李沐机器学习系列2--- mlp

news2024/6/2 11:45:37

1 Introduction

LP中有一个很强的假设,输入和输出是线性关系,这一般是不符合事实的。
通过几何的方式去对信息进行理解和压缩是比较高效的,MLP可以表示成下面的形式。
在这里插入图片描述

1.1 从线性到非线性

X ∈ R n × d X \in R^{n \times d} XRn×d表示输入层,有n个样本,d个特征。
H ∈ R n × h H \in R^{n\times h} HRn×h表述隐藏层的输出,有h个输出;
W ( 1 ) ∈ R d × h W^{(1)} \in R^{d\times h} W(1)Rd×h 表示隐藏层的权重, b ( 1 ) ∈ R 1 × h b^{(1)} \in R^{1 \times h} b(1)R1×h表示隐藏层的偏置
W ( 2 ) ∈ R h × q W^{(2)} \in R^{h\times q} W(2)Rh×q b ( 2 ) ∈ R 1 × q b^{(2)} \in R^{1\times q} b(2)R1×q分别是输出层的权重和偏置
H = X ∗ W ( 1 ) + b ( 1 ) O = H ∗ W ( 2 ) + b ( 2 ) \begin{aligned} H &= X * W^{(1)} + b^{(1)} \\ O & = H * W^{(2)}+b^{(2)} \end{aligned} HO=XW(1)+b(1)=HW(2)+b(2)
如果只是这样处理没有任何作用
O = X ∗ W 1 ∗ W 2 + b 1 ∗ W 2 + b 2 O = X*W_1*W_2+b_1*W_2+b_2 O=XW1W2+b1W2+b2
关键还是需要一个非线性的激活函数,有了激活函数多层感知机就不会退化了。
H = σ ( X ∗ W ( 1 ) + b ( 1 ) ) O = H ∗ W ( 2 ) + b ( 2 ) \begin{aligned} H &= \sigma (X * W^{(1)} + b^{(1)}) \\ O & = H * W^{(2)}+b^{(2)} \end{aligned} HO=σ(XW(1)+b(1))=HW(2)+b(2)

1.2 激活函数

  • RELU
    R E L U ( x ) = m a x ( 0 , x ) RELU(x) = max(0,x) RELU(x)=max(0,x)
y.backward(torch.ones_like(x), retain_graph=True)
d2l.plot(x.detach(), x.grad, 'x', 'grad of relu', figsize=(5, 2.5))

在这里插入图片描述
y是一个向量,所以要指定torch.ones_like(x), 因为y是一个向量,所以需要retain_graph,防止计算图被销毁。

y.backward(torch.ones_like(x), retain_graph=True) # 保留计算图
plt.plot(x.detach(), x.grad)  

在这里插入图片描述

  • sigmoid
    s i g m o i d ( x ) = 1 1 + e x p ( − x ) sigmoid(x)=\frac{1}{1+exp(-x)} sigmoid(x)=1+exp(x)1

在这里插入图片描述
导数是
在这里插入图片描述

  • tanh
    t a n h ( x ) = 1 − e x p ( − 2 x ) 1 + e x p ( − 2 x ) tanh(x)=\frac{1-exp(-2x)}{1+exp(-2x)} tanh(x)=1+exp(2x)1exp(2x)
    在0附近接近线性,
    在这里插入图片描述
    在这里插入图片描述

1.2 多层感知机

如果从零实现的话,需要自己定义个网络customNet

import torch
import torch.nn as nn
import torch.optim as optim
import matplotlib.pyplot as plt

import torchvision.transforms as transforms
from torch.utils.data import DataLoader

from torch import nn
from d2l import torch as d2l
batch_size = 256
transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.5,), (0.5,))
])

train_iter = torchvision.datasets.FashionMNIST(
    root='./data',
    train=True,
    download=True,
    transform=transform)
test_iter = torchvision.datasets.FashionMNIST(
    root='./data',
    train=False,
    download=True,
    transform=transform)
train_loader = DataLoader(train_iter, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(test_iter, batch_size=batch_size, shuffle=False)


# Neural Network Class
class CustomNet(nn.Module):
    def __init__(self, num_inputs, num_hiddens, num_outputs):
        super(CustomNet, self).__init__()
        self.linear1 = nn.Linear(num_inputs, num_hiddens)
        self.linear2 = nn.Linear(num_hiddens, num_outputs)

    def forward(self, x):
        x = x.reshape((-1, num_inputs))
        H = torch.relu(self.linear1(x))
        return self.linear2(H)

# Network Parameters
num_inputs, num_hiddens, num_outputs = 784, 256, 10
net = CustomNet(num_inputs, num_hiddens, num_outputs)

# Loss Function and Optimizer
loss = nn.CrossEntropyLoss(reduction='mean')
updater = optim.SGD(net.parameters(), lr=0.1)

# Training Loop
num_epochs = 10
for epoch in range(num_epochs):
    total_loss = 0
    for x, y in train_loader:
        output = net(x)
        l = loss(output, y)
        updater.zero_grad()
        l.backward()
        updater.step()
        total_loss += l.item()
    print(f'Epoch {epoch + 1}, Average Loss: {total_loss / len(train_loader)}')

# Test the model
def test_model(net, test_loader):
    net.eval()  # Set the model to evaluation mode
    test_correct = 0
    total = 0
    with torch.no_grad():
        for x, y in test_loader:
            output = net(x)
            _, predicted = torch.max(output, 1)
            total += y.size(0)
            test_correct += (predicted == y).sum().item()
    return test_correct / total

accuracy = test_model(net, test_loader)
print(f'Test Accuracy: {accuracy:.2f}')

# Visualize one example
test_images, test_labels = next(iter(test_loader))
image, label = test_images[0], test_labels[0]

net.eval()
with torch.no_grad():
    output = net(image.unsqueeze(0))
    _, prediction = torch.max(output, 1)
    prediction = prediction.item()

# Display the image
plt.imshow(image.squeeze(), cmap='gray')
plt.title(f'Predicted: {prediction}, Actual: {label}')
plt.show()    

使用pytorch自带的网络定义工具

# Network Parameters
num_inputs, num_hiddens, num_outputs = 784, 256, 10

# Define the network using nn.Sequential
net = nn.Sequential(
    nn.Flatten(),
    nn.Linear(num_inputs, num_hiddens),
    nn.ReLU(),
    nn.Linear(num_hiddens, num_outputs)
)

2 模型选择、欠拟合和过拟合

2.1 训练误差和泛化误差

2.1.1 统计学习理论

同名定理,训练误差收敛到泛化误差的速率。
独立同步分布假设,训练数据和测试数据都是从相同分布中独立提取出来。
有些假设符合独立同部分假设,人脸识别、语音识别和语言翻译任务;
有些假设不符合独立同分布,用大学生的人脸数据来检测养老院的老人。

2.1.2 模型复杂性

简单的模型和大量的数据,期待泛化误差和训练误差接近。需要更多训练迭代的模型比较复杂,较小训练迭代周期的不那么复杂。
影响泛化的因素:
1)可调参数的数量,模型往往更容易过拟合;
2)参数采用的值,权重的取值范围较大,更容易过拟合;
3)训练样本的数量;

2.1.3 验证集

需要通过验证集来进行模型筛选
训练和验证误差之间的泛化误差很小,欠拟合;
在这里插入图片描述

import math
import numpy as np
import torch
from torch import nn
from d2l import torch as d2l

max_degree = 20
n_train, n_test = 100, 100
true_w = np.zeros(max_degree)
true_w[0:4] = np.array([5, 1.2, -3.4, 5.6])

features = np.random.normal(size=(n_train + n_test, 1))
# print(features)
np.random.shuffle(features)
print(np.arange(max_degree).reshape(1, -1))
poly_features = np.power(features, np.arange(max_degree).reshape(1, -1))
print(poly_features)
for i in range(max_degree):
    poly_features[:, i] /= math.gamma(i + 1)

labels = np.dot(poly_features, true_w)
labels += np.random.normal(scale=0.1, size=labels.shape)

# 将pnumy array 转换成tensor
true_w, features, poly_features, labels = [torch.tensor(x, dtype=torch.float32)
                                          for x in [true_w, features, poly_features, labels]]
features[:2], poly_features[:2, :], labels[:2]

# 对模型进行训练
def evaluate_loss(net, data_iter, loss):
    metric = d2l.Accumulator(2)
    for x, y in data_iter:
        out = net(x)
        y = y.reshape(out.shape)
        l = loss(out, y)
        metric.add(l.sum(), l.numel())
    return metric[0]/metric[1]

def train(train_features, test_features, train_labels, test_labels,
          num_epochs=400):
    loss = nn.MSELoss(reduction='none')
    input_shape = train_features.shape[-1]
    net = nn.Sequential(nn.Linear(input_shape, 1, bias=False))
    batch_size = min(10, train_labels.shape[0])
    train_iter = d2l.load_array((train_features, train_labels.reshape(-1, 1)),
                               batch_size)
    test_iter = d2l.load_array((test_features, test_labels.reshape(-1, 1)),
                              batch_size, is_train=False)
    trainer = torch.optim.SGD(net.parameters(), lr=0.01)
    animator = d2l.Animator(xlabel='epoch', ylabel='loss', yscale='log',
                           xlim=[1, num_epochs], ylim=[1e-3,1e2],
                           legend=['train', 'test'])
    for epoch in range(num_epochs):
        d2l.train_epoch_ch3(net, train_iter, loss, trainer)
        if epoch == 0  or (epoch + 1) % 20 == 0:
            animator.add(epoch + 1, (evaluate_loss(net, train_iter, loss),
                                    evaluate_loss(net, test_iter, loss)))
    
    print('weight:', net[0].weight.data.numpy())

train(poly_features[:n_train, :4], poly_features[n_train:, :4],
     labels[:n_train], labels[n_train:])

3 权重衰减

在基础loss的基础上,增加对大权重的惩罚。
在这里插入图片描述

只要修改一下loss function,

def l2_penalty(w):
    return torch.sum(w.pow(2)) / 2
# 对模型进行训练
def train(lambd):
    w, b = init_params()
    net, loss = lambda X: d2l.linreg(X, w, b), d2l.squared_loss
    num_epochs, lr = 100, 0.003
    animator = d2l.Animator(xlabel='epochs', ylabel='loss', yscale='log',
                            xlim=[5, num_epochs], legend=['train', 'test'])
    for epoch in range(num_epochs):
        for X, y in train_iter:
            # 增加了L2范数惩罚项,
            # 广播机制使l2_penalty(w)成为一个长度为batch_size的向量
            l = loss(net(X), y) + lambd * l2_penalty(w)
            l.sum().backward()
            d2l.sgd([w, b], lr, batch_size)
        if (epoch + 1) % 5 == 0:
            animator.add(epoch + 1, (d2l.evaluate_loss(net, train_iter, loss),
                                     d2l.evaluate_loss(net, test_iter, loss)))
    print('w的L2范数是:', torch.norm(w).item())

在pytorch上可以同步衰减权重和参数

def train_concise(wd):
    net = nn.Sequential(nn.Linear(num_inputs, 1))
    for param in net.parameters():
        param.data.normal_()
    loss = nn.MSELoss(reduction='none')
    num_epochs, lr = 100, 0.003
    # 偏置参数没有衰减
    trainer = torch.optim.SGD([
        {"params":net[0].weight,'weight_decay': wd},
        {"params":net[0].bias}], lr=lr)
    animator = d2l.Animator(xlabel='epochs', ylabel='loss', yscale='log',
                            xlim=[5, num_epochs], legend=['train', 'test'])
    for epoch in range(num_epochs):
        for X, y in train_iter:
            trainer.zero_grad()
            l = loss(net(X), y)
            l.mean().backward()
            trainer.step()
        if (epoch + 1) % 5 == 0:
            animator.add(epoch + 1,
                         (d2l.evaluate_loss(net, train_iter, loss),
                          d2l.evaluate_loss(net, test_iter, loss)))
    print('w的L2范数:', net[0].weight.norm().item())

4 dropout

![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/90703e065d4d4184b4ee1db336a369ae.png

随机去掉一些节点,
在这里插入图片描述
标准暂退法
在这里插入图片描述
这里因为dropout层的参数被mask了,输出为0,在反向传播的时候,更新的数值很小

def dropout_layer(X, dropout):
    assert 0 <= dropout <= 1
    # 在本情况中,所有元素都被丢弃
    if dropout == 1:
        return torch.zeros_like(X)
    # 在本情况中,所有元素都被保留
    if dropout == 0:
        return X
    mask = (torch.rand(X.shape) > dropout).float()
    return mask * X / (1.0 - dropout)
    
dropout1, dropout2 = 0.2, 0.5

class Net(nn.Module):
    def __init__(self, num_inputs, num_outputs, num_hiddens1, num_hiddens2,
                 is_training = True):
        super(Net, self).__init__()
        self.num_inputs = num_inputs
        self.training = is_training
        self.lin1 = nn.Linear(num_inputs, num_hiddens1)
        self.lin2 = nn.Linear(num_hiddens1, num_hiddens2)
        self.lin3 = nn.Linear(num_hiddens2, num_outputs)
        self.relu = nn.ReLU()

    def forward(self, X):
        H1 = self.relu(self.lin1(X.reshape((-1, self.num_inputs))))
        # 只有在训练模型时才使用dropout
        if self.training == True:
            # 在第一个全连接层之后添加一个dropout层
            H1 = dropout_layer(H1, dropout1)
        H2 = self.relu(self.lin2(H1))
        if self.training == True:
            # 在第二个全连接层之后添加一个dropout层
            H2 = dropout_layer(H2, dropout2)
        out = self.lin3(H2)
        return out


net = Net(num_inputs, num_outputs, num_hiddens1, num_hiddens2)

在pytorch后面,添加一个mask的dropout层就可以实现

net = nn.Sequential(nn.Flatten(),
        nn.Linear(784, 256),
        nn.ReLU(),
        # 在第一个全连接层之后添加一个dropout层
        nn.Dropout(dropout1),
        nn.Linear(256, 256),
        nn.ReLU(),
        # 在第二个全连接层之后添加一个dropout层
        nn.Dropout(dropout2),
        nn.Linear(256, 10))

def init_weights(m):
    if type(m) == nn.Linear:
        nn.init.normal_(m.weight, std=0.01)

net.apply(init_weights);

5 参数初始化

5.1 梯度爆炸

如果模型有L层,那么每层之间的参数进行更新的时候,梯度满足下面的关系。因为参数的grad有乘积关系,容易出现梯度爆炸和梯度消失。
在这里插入图片描述
sigmoid函数的梯度消失
当参数很大或者很小,gradient都很小,经过多次传递之后,直接梯度消失了
在这里插入图片描述

5.2 xavier 参数初始化

保证梯度的方差不变
在这里插入图片描述

在这里插入图片描述

6 分布和偏移

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1353187.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2023-12-26分割回文串和子集以及子集II

131. 分割回文串 思想&#xff1a;回溯三步骤&#xff01;① 传入参数 ② 回溯结束条件 ③ 单层搜索逻辑&#xff01;抽象成回溯树&#xff0c;树枝上是每次从头部穷举切分出的子串&#xff0c;节点上是待切分的剩余字符串【从头开始每次往后加一】 class Solution:def partiti…

基于混合蛙跳算法优化的Elman神经网络数据预测 - 附代码

基于混合蛙跳算法优化的Elman神经网络数据预测 - 附代码 文章目录 基于混合蛙跳算法优化的Elman神经网络数据预测 - 附代码1.Elman 神经网络结构2.Elman 神经用络学习过程3.电力负荷预测概述3.1 模型建立 4.基于混合蛙跳优化的Elman网络5.测试结果6.参考文献7.Matlab代码 摘要&…

ChatGPT持续火热,OpenAI年收入突破16亿美元

著名科技媒体The Information消息&#xff0c;由于OpenAI的ChatGPT等产品实现强劲增长&#xff0c;其年收入将突破16亿美元。 这主要得益于OpenAI在2023年发布了一系列重磅产品&#xff0c;包括GPT-4、ChatGPT企业版、GPT-4 Turbo、DALLE 3、苹果/安卓应用、自定义GPTs、Assis…

测试人必看,看完必会的fiddler抓包,抓包抓的好........

&#x1f4e2;专注于分享软件测试干货内容&#xff0c;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;交流讨论&#xff1a;欢迎加入我们一起学习&#xff01;&#x1f4e2;资源分享&#xff1a;耗时200小时精选的「软件测试」资…

ELF文件信息一览

准备开个专栏&#xff0c;记录《从零开始实现链接器》的学习过程&#xff0c;先占个坑。 之前一直想把自己的学习过程记录在个人博客网站上&#xff0c;但这个要自己维护&#xff0c;上传图片什么的比较麻烦。关键是没有人互动&#xff0c;自己也没有怎么去看&#xff0c;慢慢的…

麒麟云增加计算节点

一、安装基座系统并配置好各项设置 追加的计算节点服务器&#xff0c;安装好系统&#xff0c;把主机名、网络网线&#xff08;网线要和其他网线插的位置一样&#xff09;、hosts这些配置好&#xff0c;在所有节点的/etc/hosts里面添加信息 在控制节点添加/kylincloud/multinod…

解决Redis序列化乱码问题

如果我们使用原生的JDK序列化&#xff0c;那么当我们将数据存储到Redis中就会出现乱码的情况 为了解决这个问题我们需要重写RedisTemplate从而解决序列化乱码问题 首先在Maven中引入相应的依赖 <dependency> <groupId>com.fasterxml.jackson.core</group…

交换机01_以太网

1、交换机工作原理 交换机是数据链路层的设备&#xff0c;数据链路层传输的是数据帧&#xff0c;所以封装的是MAC头部&#xff08;主要有源MAC地址和目的MAC地址&#xff09; 2、数据链路层的功能&#xff1a; 建立逻辑连接&#xff0c;进行物理地址寻址&#xff0c;差错校验…

CMake入门教程【核心篇】安装(install)

&#x1f608;「CSDN主页」&#xff1a;传送门 &#x1f608;「Bilibil首页」&#xff1a;传送门 &#x1f608;「本文的内容」&#xff1a;CMake入门教程 &#x1f608;「动动你的小手」&#xff1a;点赞&#x1f44d;收藏⭐️评论&#x1f4dd; 文章目录 1. 概述2. 使用方法2…

基于头脑风暴算法优化的Elman神经网络数据预测 - 附代码

基于头脑风暴算法优化的Elman神经网络数据预测 - 附代码 文章目录 基于头脑风暴算法优化的Elman神经网络数据预测 - 附代码1.Elman 神经网络结构2.Elman 神经用络学习过程3.电力负荷预测概述3.1 模型建立 4.基于头脑风暴优化的Elman网络5.测试结果6.参考文献7.Matlab代码 摘要&…

软碟通UltraISO制作U盘安装Ubuntu

清华大学开源软件镜像站https://mirrors.tuna.tsinghua.edu.cn/ 从里面下载ubuntu-22.04-desktop-amd64.iso UltraISO是一款非常不错的U盘启动盘制作工具&#xff0c;一直被许多网友们所喜欢&#xff0c;使用简单、方便。 UltraISO官方下载地址&#xff1a;https://cn.ultrais…

Halcon顶帽运算与底帽运算的应用

Halcon顶帽运算与底帽运算的应用 文章目录 Halcon顶帽运算与底帽运算的应用1. 提取小的物件2. 校正非均匀光照 正如上文所说的&#xff0c;顶帽运算返回的像素部分是尺寸比结构元素小的&#xff0c;并且比较亮的局部小区域&#xff1b;底帽运算返回的像素部分是尺寸比结构元素小…

【EI会议征稿通知】第三届艺术设计与数字化技术国际学术会议( ADDT 2024)

第三届艺术设计与数字化技术国际学术会议( ADDT 2024&#xff09; 2024 3rd International Conference on Art Design and Digital Technology 所谓艺术设计&#xff0c;就是将艺术的审美感应用到与日常生活密切相关的设计中&#xff0c;使其不仅具有审美功能&#xff0c;而且…

Arduino定时器和定时器中断

目录 一、定时器中断库函数方式说明 1、定时器中断编号和引脚说明 2、库文件安装 3、MsTimer2库文件使用 4、TimerOne库文件使用 5、注意事项 二、定时器的寄存器配置说明 1、定时器寄存器列表说明 2、Timer0寄存器说明 3、预分频系数与比较匹配器 4、定时器模式 …

天津大数据培训机构 大数据时代已到来!

大数据时代已经来临&#xff0c;越来越多的人开始关注大数据&#xff0c;并且准备转行大数据。但是&#xff0c;对于一个外行人或者小白来说&#xff0c;大数据是什么&#xff1f;大数据需要学什么&#xff1f;什么样的大数据培训机构是靠谱的&#xff1f;这几个简单的问题就足…

xpath定位--切换frame/窗口

在web自动化中&#xff0c;有时候我们界面上明明定位到了该元素&#xff0c;但是就是点击不到&#xff0c;怎么回事&#xff1f; --可能是没有切换到对应的frame或者没有切换到对应窗口&#xff01;&#xff01;&#xff01; 切换frame用于在同一个窗口中切换到frame上下文&a…

SpringBoot之多环境开发配置

1 多环境开发配置 问题导入 在实际开发中&#xff0c;项目的开发环境、测试环境、生产环境的配置信息是否会一致&#xff1f;如何快速切换&#xff1f; 1.1 多环境启动配置 yaml文件多环境启动 不同环境使用—隔开 示例代码&#xff1a; spring:profiles:active: dev#生产…

Spring Bean的生命周期(钩子函数)

借鉴&#xff1a;https://www.cnblogs.com/liweimingbk/p/17843970.html https://blog.csdn.net/lxz352907839/article/details/128634404 一、Spring Bean生命周期 如果Spring配置文件中所定义的Bean类实现了ApplicationContextAware 接口&#xff0c;那么在加载Spring配置文…

C++多态性——(3)动态联编的实现——虚函数

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd; 成功的秘诀就在于多努力一次&#xff…

后端杂七杂八系列篇一

后端杂七杂八系列篇一 ① MySQL选择合适的数据类型① Char与Varchar② Text与Blob ② EqualsAndHashCode(callSuper true)的作用③ mybatis-plus 相关① 主键生成策略② 使用Model实现CRUD③ Wrapper的用法① Wrapper的继承关系② 项目中最常用的warpper [LambdaQueryWrapper]…