目录
3.7 softmax回归的简洁实现
默认的内置类型字典中的元素时无序的,这个ordereddict类实现了对字典的排序,当调用这个类生成类对象时,生成的字典就是有顺序的,
代码里就是生成了有序字典,使用(k,v)对的形式存取值。
整个过程梳理:
1.从零开始实现过程
- 导入需要的模块
- 读取数据
- 初始化模型参数,定义跟踪参数的梯度变化
- 编写函数实现softmax的计算过程,得到矩阵
- 定义网络模型,写具体计算过程
- 定义损失函数,也是自己编的交叉熵损失函数,是一个向量,还没有加起来求平均
- 计算准确率,也是封装成函数
- 训练模型,定义函数,然后调用函数来训练,函数中,输入网络(自己定义的),输入训练和测试数据(最前边读取的数据),自己写的损失函数,进行求和运算,这里明确那个损失函数计算出来是正的,因为0到1之间的对数是负的,然后是自己编写的优化器sgd函数优化参数。
注意: 没有定义优化器时就在计算梯度后,用自己写的函数进行反向传播,更新梯度,定义了优化器之后,直接调用优化器,如下图
2.简洁实现过程
- 导入包
- 读取数据
- 定义模型和初始化模型(都是基于pytorch工具进行定义和初始化的)
- 交叉熵损失函数(直接用的pytorch中提供的,输出是一个向量,需要在训练函数中求和)
- 优化算法(也是使用的框架里的,输入模型参数,设置学习率)
- 设置循环次数,直接调用训练函数
3.8 多层感知机MLP
全连接层只是对数据做仿射变换
,也就是线性变换
,多个仿射变换的叠加仍然是一个仿射变换,所以要引入非线性变换即激活函数。
多层感知机定义是在单层神经网络的基础上引入一个到多个隐藏层。每个隐藏层的输出通过激活函数进行变换,多层感知机的层数和各隐藏层中单元个数都是超参数。
几种常用的激活函数:
detach()函数,tensor.detach()函数,返回一个新的张量,从当前计算图中分离下来,仍指向原变量的存放位置,但是不再计算其梯度。
ReLU函数:
只保留正数元素,负数元素清零。
sigmod函数,将元素的值变换到0和1之间,
tanh函数,将元素的值变换到-1和1之间
多层感知机的表示
在分类问题
中,对输出O做softmax运算,输出也应该是矩阵,一个是节点数,一个是批量样本数,然后交叉熵得到的是向量,分别是每个元素的交叉熵值。
在回归问题
中,将输出层的输出个数设为1,然后使用平方损失函数。
3.9 多层感知机的从零开始实现
①为什么后边使用框架里交叉熵损失函数的,loss值变得很小。
因为在原书中框架下,使用交叉熵损失函数,输出值相当于在batch维度上求和了,然后再累加所有的损失,除以样本总数,得到一个epoch后的损失值。在pytorch中,交叉熵损失函数默认在一个batch上求平均得到损失函数的值。所以输出的就已经是平均值, 就是只有一个值,再求和也是一个值,后边再累加求最终损失其实与真实的相差了256倍。
②明确最后输出的loss和计算过程中的loss,即求梯度的loss不是一个。
③损失l的值和他的梯度值是成比例的,有关系。
④在使用框架时,得到的损失l是均值,是一个样本的,求出来的梯度也是一个样本的,那么在进行参数更新时,不用除以256,但是自己编的sgd还是除以了,所以在学习率上补偿了,在使用框架里的SGD时,默认是均值,就不会再除以256了,学习率正常即可。
3.10 多层感知机的简洁实现
3.11 模型选择 欠拟合 过拟合
训练误差
和泛化误差
,就是训练集误差和测试集误差,无法从训练误差估计泛化误差,降低训练误差,泛化误差也不一定降低,机器学习模型应该关注降低泛化误差。
- 模型选择:评估若干候选模型,这些候选模型有着不同的超参数或者网络参数,然后从中选择模型。
- 使用验证数据集来选择模型,可以预留一部分在训练数据集和测试数据集以外的数据来进行模型选择,一般可以在训练集中选择百分之二十作为验证集,剩下的作为训练集。
本书中所使用的测试集应该为验证集。
K折交叉验证
,当训练数据不够时就不能留出大量的数据作为验证,就可以将训练集分成K份,对于同一个模型,使用一小份作为验证,剩下的训练,轮流使用每个小份作为验证,其余的训练,然后对于验证的结果求均值,得到结果。作为一个模型的验证集评估指标。
过拟合
(训练误差远小于测试误差)欠拟合
(无法得到较低的训练误差)
两个影响模型过拟合和欠拟合的因素:模型复杂度(选择合适的模型复杂度)训练数据集大小。
3.12 权重衰减
应对过拟合问题的常用方法:权重衰减
权重衰减和L2范数正则化是等价的,正则化
是通过为模型损失函数添加惩罚项,使得模型学习到的参数值较小,从而降低模型复杂度,进而减小过拟合。
加上惩罚项以后原来的损失函数变为
在小批量随机梯度下降的过程中,权重的迭代方式变为
也就是权重值就变小了,两个权重值先乘以了一个小于1的数,然后减去梯度,这就是权重衰减
,为需要学习的模型增加了限制,可能对过拟合有效。
简洁实现,可以在构建优化器实例时,通过weight_decay参数指定权重衰减超参数,默认情况下会对权重和偏差同时衰减,也可以分别构建优化器实例,分别进行权重衰减。
然后调用函数,先设置衰减超参数为0,有过拟合现象,且w的范数值较大。再设置衰减超参数为3,减轻过拟合现象,且w的范数值明显减小。
分析:明确优化器设置就是设置一些模型参数更新策略,传入网络模型的参数,再根据策略更新,比如学习率的设置,衰减超参数的设置。
3.13 丢弃法
另一种应对过拟合的方法,是丢弃法dropout
,本例指的是倒置丢弃法。
丢弃法
就是在隐藏层中丢弃部分神经元,在反向传播时丢弃的神经元相关的权重的梯度为0,相对应的参数不更新,不会朝着拟合的走势改变,神经元的丢弃是随机的,从而使得输出层的运算无法过度依赖某个神经元,从而起到正则化的作用。(其实和正则化是一样的,正则化是通过减小权重值,从而减弱神经元的作用,这里直接丢弃神经元)
就是在搭建网络时,添加丢弃超参数,这样正常训练,每一次迭代丢弃的神经元是随机的。最终使得训练出来的网络参数泛化能力更强。
从零开始实现,编写丢弃函数,
如果保持节点概率是0,就把所有节点值都设为0,否则,按照输入的大小,生成随机均匀分布的值,然后和保留概率值比较,然后使用float()将条件判别式的结果转化为浮点数,满足条件的是1,不满足条件的是0,然后这个掩膜乘以原来的输入,得到留下来的数,再除以保留概率值,使得输入的期望值不变。
在训练模型的时候使用丢弃法,使得网络输出不依赖于某个节点,增加泛化性,评估模型时不进行丢弃。
简洁实现过程
直接在全连接层,激活函数层后加上Dropout层,指定丢弃概率
,这个层就以指定概率值随机丢弃上一层的输出元素。
附:
这一块代码中,isinstance函数表示判断一个对象是否是一个已知的类型,这里如果是,就进入评估模式,关闭dropout,计算准确率,然后再改回训练模式。
3.14 正向传播 反向传播和计算图
正向传播
是指对神经网络沿着从输入层到输出层的顺序,依次计算并存储模型的中间变量和输出。
反向传播
指的是计算神经网络梯度的方法,依据微积分中的链式法则,沿着输出层到输入层的顺序,依次计算并存储目标函数有关的神经网络各层的中间变量以及参数的梯度。
正向传播和反向传播相互依赖。
3.15 数值稳定性和模型初始化
数值稳定性,典型问题是衰减
和爆炸
。
当神经网络的层数较多时,模型的数值稳定性容易变差,比如多层感知机的第l层输出。
当层数较大时,输出就可能衰减或者爆炸,比如当权重参数为0.2时,许多层相乘就是很小的数,当参数为5,许多层相乘就是很大的数。
随机初始化模型参数
原因:
如果一个隐藏层里的隐藏单元参数都初始化为相等的值,在前向传播时,每个隐藏单元将相同的输入计算出相同的值,再反向传播时,每个隐藏层的参数梯度值也相等,迭代之后参数还是相等,本质上,这一层只有一个隐藏单元在发挥作用。所以要对网络的模型参数进行随机初始化。
创建张量的函数
pytorch的默认随机初始化:比如使用torch.nn.init.normal_()使权重参数采用正态分布的随机初始化方式。
在nn.module模块中不同类型的层都采取了较为合理的初始化策略。
常用的随机初始化方法Xavier随机初始化,假设全连接层输入个数是a,输出个数是b,这种随机初始化方式将使该层权重参数的每个元素都随机采样于均匀分布。
梯度消失 梯度爆炸问题总结及解决办法
神经网络在更新参数的过程中,使用反向传播即BP算法求各层网络的梯度,就是使用神经网络的链式法则,这个反向传播过程就会导致梯度消失和梯度爆炸。
梯度消失的原因
:深层网络;使用了sigmoid激活函数。
梯度爆炸的原因
:深层网络;参数的初始值过大。
举例推导:
一个神经网络,由链式法则求损失函数对参数的梯度。
可以看到,梯度的求解公式,是激活函数的梯度乘以参数w,连乘。
再看sigmoid函数,其导数的变化趋势是先增加再减小,最大值是四分之一,然后由于神经网络的参数初始化值往往小于1,w的绝对值小于1,所以那个梯度值会随着网络深度的增加变得很小,最终
梯度消失
,前几层网络参数的梯度值接近0.
如果网络的初始化参数比较大,就是w的绝对值比较大,使得激活函数的导数与w乘积的绝对值大于1了,随着网络深度的增加,梯度值就会很大,前几层网络参数的梯度就很大,就是
梯度爆炸
。
其他的几种激活函数,tanh函数,导数值也小于1,比sigmoid略好,但是也会梯度消失。RelU函数,其导数在正值部分恒为1,不会因此梯度消失和爆炸。
解决方案:
1.加BN层,批标准化,Batch Normalization,其使用在网络每一层输出之后,激活函数之前,这个过程是将每一层的输出标准化为正态分布,然后再使用缩放和平移对标准化之后的数据分布进行调整,这样就使得输出数据避免集中在梯度饱和区,容易造成梯度消失,因为饱和区梯度趋于0,这样就增大了梯度值,缓解了梯度消失问题,也使得网络非线性增强,提高网络性能。
2.使用其他的激活函数
3.使用梯度剪切预防梯度爆炸
4.使用正则化,预防梯度爆炸,这里就是通过在训练中限制参数的大小,在预防过拟合的同时还能预防梯度爆炸。
5.使用残差网络ResNet,可轻松搭建多层神经网络,不用担心梯度消失问题。
原因:
梯度消失的本质原因是激活函数小于1和权重参数的值较小,使用relu激活函数,解决了导数小于1的问题,但是还是要经过多个权重层,残差网络加入直连,解决了权重参数累加使得梯度减小的问题。
6.LSTM网络