欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

实战深度学习:跟着李沐理解多层感知机

最编程 2024-01-19 10:33:18
...

多层感知机的从零开始实现

import torch
from matplotlib import pyplot as plt
from torch import nn
from d2l import torch as d2l

batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)

# 初始化模型参数
num_inputs, num_outputs, num_hiddens = 784, 10, 256
W1 = nn.Parameter(torch.randn(num_inputs, num_hiddens, requires_grad=True) * 0.01)
b1 = nn.Parameter(torch.zeros(num_hiddens, requires_grad=True))
W2 = nn.Parameter(torch.randn(num_hiddens, num_outputs, requires_grad=True) * 0.01)
b2 = nn.Parameter(torch.zeros(num_outputs, requires_grad=True))
params = [W1, b1, W2, b2]


def relu(X):
    a = torch.zeros_like(X)
    return torch.max(X, a)


def net(X):
    X = X.reshape((-1, num_inputs))
    H = relu(X @ W1 + b1)  # @代表矩阵乘法的简写
    return H @ W2 + b2


loss = nn.CrossEntropyLoss(reduction='none')

num_epochs, lr = 10, 0.1
updater = torch.optim.SGD(params, lr=lr)
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, updater)
plt.show()
d2l.predict_ch3(net,test_iter)
plt.show()

多层感知机的简洁实现

import torch
from matplotlib import pyplot as plt
from torch import nn
from d2l import torch as d2l

net = nn.Sequential(nn.Flatten(), nn.Linear(784, 256), nn.ReLU(), 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)


batch_size, lr, num_epochs = 256, 0.01, 10
loss = nn.CrossEntropyLoss(reduction='none')


train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)

optimer = torch.optim.SGD(net.parameters(), lr=lr)
d2l.train_ch3(net, train_iter,test_iter, loss, num_epochs, optimer)


plt.show()

权重衰减

简洁实现

net = nn.Sequential(nn.Linear(num_inputs, 1))
for param in net.parameters():
	param.data.normal_()
optimer = torch.optim.SGD([ {"params":net[0].weight,'weight_decay':wd},
{"params":net[0].bias}],lr=lr)

上面设置“weight_decay”为wd就是设置其使用权重衰减。

Dropout

一个好的模型需要对输入数据的扰动鲁棒,也就是不能够受噪声的影响。那么如果使用带有噪声的数据来学习的话,如果能够使得其不学习到噪声的那部分内容,那么也相当于是正则化。因此丢弃法(Dropout)就是在层之间加入噪音。

那么从定义方向出发,它就是无偏差的加入噪音,即对原本输入x\pmb{x}加入噪音得到x\pmb{x}^{\prime},希望其均值不变,即:

E[x]=xE[\pmb{x}^{\prime}]=\pmb{x}

那么丢弃法具体的做法是对每个元素执行如下扰动

xi={0with probablity pxi1potherwisex^{\prime}_i=\begin{cases} 0\quad with~probablity~p\\ \frac{x_i}{1-p}\quad otherwise \end{cases}

那么这样可以保证期望不变:

E[xi]=p×0+(1p)×xi1p=xiE[x^{\prime}_i]=p\times 0 + (1-p)\times \frac{x_i}{1-p}=x_i

那么这个丢弃概率就是控制模型复杂度的超参数

具体是通常将丢弃法作用在多层感知机的隐藏层的输出上,即:

在这里插入图片描述

这是在训练过程中使用,它将会影响模型参数的更新,而在测试的时候并不会进行dorpout操作,这样能够保证确定性的输出。从实验上来说,它和正则化能够达到类似的效果。

那么Dropout放在隐藏层的输出,会将那些被置为0的神经元的权重在本次不进行更新,那么就可以认为是每一次Dropout都是从所有的隐藏层神经元中挑选出一部分来进行更新。

具体的实现直接调用nn.Dropout()层即可。

数值稳定性

在计算梯度时:

在这里插入图片描述

因为向量对向量的求导是矩阵,因此这么多次矩阵的运算可能会遇见梯度爆炸或者梯度消失的问题。

假设矩阵中的梯度大部分都是比1大一点的数,那么经过这么多次梯度计算就可能出现梯度过大而爆炸;那么梯度如果稍微小于1也就会经过这么多次迭代之后接近于0。

在这里插入图片描述

那么梯度爆炸就会带来如下的问题:

  • 值超过了数值类型可以表示的范围
  • 对学习率更加敏感
    • 当学习率比较大,乘上较大的梯度就更新程度比较大,难以稳定
    • 当学习率太小,那么可能导致在除开梯度爆炸的那些权重外的正常权重无法正常更新

而对于梯度消失,例如采用sigmoid函数:

在这里插入图片描述

这么小的梯度在多个叠加之后就可能会出现梯度消失的问题。它的主要问题是:

  • 也是超过表示范围,直接就使大部分梯度值为0,无法更新
  • 训练因为梯度值为0,无法正常更新
  • 对于底部层尤为严重,因为梯度是从输出层反向传播计算得到的,越到底部层,叠加的层数越多,梯度越可能消失,那就使得只有顶部层能够正常训练更新

那么如何使训练更加稳定的首要目标,就是让梯度值在合理的范围内,例如在某些算法中它们将梯度的乘法转换成加法,或者是对梯度进行归一化、裁剪等。但还有一种重要的方法就是合理的进行权重初始化,以及选择适合的激活函数

具体来说,结论就是在对权重进行初始化的时候,让权重是从一个均值为0,方差为γt=2nt1+nt\gamma_t=\frac{2}{n_{t-1}+n_t}中采样得到的。其中nt1ntn_{t-1}、n_{t}代表该权重所连接的两个层的神经元的数目。因此需要根据层的形状来选择权重所服从分布的方差

而激活函数经过推导,可以认为tanh(x)和ReLU(x)这两个激活函数能够具有较好的特性,而sigmoid(x)需要调整为4×sigmoid(x)24\times sigmoid(x)-2才能够达到与前两个相同的效果。

环境和分布偏移

1、分布偏移的类型

主要有以下几种偏移类型:

  • 协变量偏移:指的是数据的分布p(x)p(x)发生了变化,例如在训练的时候用到的训练数据集分布p1(x)p_1(x)和测试的时候用到的测试集分布p2(x)p_2(x)不同,那么这就很难使得模型在测试数据集上表现好。不过这种变化还有一个架设计就是虽然输入的分布可能随时间发生变化,但是标签函数(即条件分布P(yx)P(y\mid x))不会改变。例如在训练的时候我们用真实的猫和狗来让机器学会分类,但是在测试的时候我们用的是卡通的猫和狗,这就是训练和测试两部分的数据集不相同,但是它们的标签函数是相同的,可以正确地对猫和狗进行标注。
  • 标签偏移:指的是和协变量偏移相反的问题,因为这里假设标签边缘概率P(y)P(y)可以改变,但是类别条件分布P(xy)P(x\mid y)在不同的领域之间保持不变。这里可以举一个例子就是预测患者的疾病,症状就是x,而所患的疾病就是标签y,那么疾病的相对流行率,或者说各种疾病之间的比例可能发生变化(即P(y)P(y))可能发生变化,而对于某种特定疾病所对应的症状(P(xy))P(x\mid y))不会发生变化。
  • 概念偏移:指的是标签的定义出现了变化。举个例子就是我们对于美貌的定义,可能会随着时间的变化而发生变化,那么这个“美貌”的标签的概念就发生了变化。

2、分布偏移纠正

首先需要了解什么是经验风险与实际风险:在训练时我们通常是最小化损失函数(不考虑正则化项),即:

minf1Numi=1Numloss(f(xi),yi)\min_{f} \frac{1}{Num}\sum_{i=1}^{Num} loss(f(x_i),y_i)

这一项在训练数据集上的损失称为经验风险。那么经验风险就是为了来近似真实风险的,也就是数据的真实分布下的损失。然而在实际中我们无法获得真实数据的分布。因此一般认为最小化经验风险可以近似于最小化真实风险

协变量偏移纠正

对于目前已有的数据集(x,y),我们要评估P(yx)P(y\mid x),但是当前的数据xix_i是来源于某些源分布q(x)q(x)(可以认为是训练数据集的分布),而不是来源于目标分布p(x)p(x)(可以认为是真实数据的分布,或者认为是测试数据的分布)。但存在协变量偏移的假设即p(yx)=q(yx)p(y\mid x)=q(y\mid x)。因此:

loss(f(x),y)p(x)dxdy = loss(f(x),y)q(yx)q(x)p(x)q(x)dxdy\iint loss(f(x),y)p(x)dxdy~=~ \iint loss(f(x),y)q(y\mid x)q(x)\frac{p(x)}{q(x)}dxdy

因此当前我们需要计算数据来自于目标分布和来自于源分布之间的比例,来重新衡量每个样本的权重,即:

βi=p(xi)q(xi)\beta_i=\frac{p(x_i)}{q(x_i)}

那么将该权重代入到每个数据样本中,就可以使用加权经验风险最小化来训练模型:

minf1Numi=1Numβiloss(f(xi),y)\min_f \frac{1}{Num}\sum_{i=1}^{Num}\beta_i loss(f(x_i),y)