神经网络通俗理解学习笔记(1)
- 神经网络原理
- 激活函数
- 前向传播和反向传播
- 多层感知机代码实现
- 加载数据
- 网络结构
- 损失函数
- 优化器
- 训练
- 测试
- 保存
- 回归问题
- 一元线性回归
- 多元线性回归
- 多项式回归
- 线性回归代码实现
- 数据生成
- 设置超参数
- 初始化参数
- 可视化
- Pytorch模型实现
- 分类问题
- 数学表示
- 损失函数
- 多分类问题代码实现
- 加载MINIST数据集
- 数据加载器
- 构建网络
- 损失函数和优化器
- 模型评估
- 模型训练
- 常见问题及对策
- 训练常见问题
- 模型架构设计
- 万能近似定理
- 宽度 or 深度
- 过拟合问题
- 欠拟合问题
- 过拟合欠拟合应对策略
- 数据集大小选择
- 数据增强
- 使用验证集
- 模型选择
- K折交叉验证
- 提前终止
- 过拟合欠拟合代码示例
- 深度学习中的 正则化
- Dropout方法
- Dropout代码实现
- 梯度消失和爆炸
- 梯度裁剪
- ReLU激活函数
- Batch Normalization
- 残差结构
- 模型文件的读写
- 方式1:模型参数的保存
- 方式2:整个模型的保存
- 方式3:checkpoint
- 梯度下降及变体
- 最优化与深度学习
- 损失误差
- 损失函数性质
- 梯度下降
- 随机梯度下降(SGD)
- 小批量梯度下降法
- 动量法
- AdaGrad算法
- RMSProp算法
- AdaDelta算法
- Adam算法
- 梯度下降代码实现
- 学习率调节器
神经网络原理
汇聚n个线性变换,最后做一个非线性变换。
神经网络本质上是很多个线性模型的模块化组合
输入层是样本本身,维度就是样本维度
输出层是样本类别标签
隐藏层是最重要的神经元,并不是层数越多维度越大泛化能力越好,会出现过拟合问题。
隐藏层层数和维度多了容易过拟合,少了模型比较弱,隐藏层层数和维度2个指标需要综合考量,都是重要的超参数。
激活函数
用于非线性变换,必须是可导的
为什么引入非线性?
线性组合无法表示复杂的非线性数据。
神经网络模型的本质其实是如何用线性模型去解决非线性问题。
怎么引入非线性?
通过激活函数。(就可以对空间进行扭曲变换)
线性空间的非线性,在高维非线性空间却线性可分。
sigmoid函数
输出值压缩到(0,1),常用于二分类问题,其他一般不用,容易导致梯度消失问题
Tanh函数
是sigmoid函数的改进版,输出值压缩到(-1,1),输出以0为中心,更快的收敛速度,适用于多分类,输入值较大的时候也容易导致梯度消失
Relu函数
多数情况下的第一选择,解决梯度消失问题,计算速度快,但当输入为负数时某些权重无法更新,有很多改进版。
softmax函数
输入值映射到概率分布上
主要用在多分类问题
使得输出具有可解释性
激活函数用于给线性的矩阵运算非线性化
softmax作用是将输出转化为概率值,相当于归一化
损失衡量当前模型的好坏
反向传播是优化调整参数(权重w、截距b)
不同网路模型区别体现在:
网络模型结构、损失函数、动态求解损失函数过程、对问题(过拟合等)的解决方式
前向传播和反向传播
损失函数
均方误差
前向传播(本质传递数据和信息):
不断往前传计算损失
深度前馈网络
前馈神经网络
反向传播(本质传递误差、梯度/偏导数):
通过训练参数(w和b)使损失函数的值越来越小
损失倒查分解(偏导数的链式法则)
计算每层参数梯度
目的是找到这些参数的偏导数,然后以此为依据更新模型参数。
多层感知机代码实现
举初级阶段便于理解的神经网络代码实现的例子。
加载数据
先加载数据
由于直接全部拿去训练内存吃不消,所以批量进行训练,训练集需要设置shuffle为true,消除顺序影响,提升泛化能力。
网络结构
损失函数
根据分类问题或是回归问题确定
这里是分类问题,所以用交叉熵损失函数
优化器
优化器Adam
训练
在图片任务中,一般采用小批量策略读取数据和训练
外层循环表示训练10次,里面的循环表示从dataloader中循环读取数据,每次读取batchsize大小的批数据
测试
保存
回归问题
一元线性回归
用于对数据进行预测
找最优的拟合直线 转化为 最优化问题,反复迭代参数使得目标函数值最小
多元线性回归
X 变成矩阵的形式
多项式回归
把非线性问题转化为线性问题去求解
这个X可能是高维的
线性回归代码实现
本质上是一个神经元的神经网络
数据生成
tensor是张量的意思,一种多维数组,在机器学习领域,tensor通常用来表示训练数据、模型参数、输入数据等。
维度可以是一维、二维、n维
pytorch中的基本数据结构,具备良好的计算性能,可以使得GPU加速,大大提高计算效率。
设置超参数
学习率和最大迭代次数
初始化参数
用pytorch的randn函数初始化参数w
用zeros函数初始化参数b
randn函数会生成均值为0标准差为1的随机张量
zeros函数会生成全部元素都为0的张量
grad参数设为true 表示在反向传播时计算梯度
初始化方法:
1、常数 zeors
2、随机数 randn
3、预训练好的参数
具体使用哪种视问题而定
可视化
Pytorch模型实现
设置超参数以及前面的生成数据一样
为什么要清空梯度?
- 避免梯度累加:在神经网络训练过程中,每次迭代都会计算梯度。如果不清空梯度,那么在下一次迭代时,新的梯度会与之前的梯度累加。这会导致梯度值非常大,从而使得模型参数更新过大,影响模型的收敛。
- 确保每次迭代独立:清空梯度可以确保每次迭代都是独立的,每次更新模型参数都是基于当前批次的损失函数计算出的梯度,而不是之前批次的累积梯度。
- 防止梯度爆炸:在某些情况下,如果梯度没有被清空,梯度可能会随着迭代次数的增加而指数级增长,导致所谓的梯度爆炸问题,这会使得模型参数更新变得不稳定。
分类问题
输出是离散的类别标签,而不是连续的值
把一个问题用数学模型表示,然后找到目标函数,用优化求解办法获得模型参数
数学表示
softmax可以使所有类别的概率和等于1
可以将输入的特征值转化为概率值,方便决策
神经网络中一般用概率这种表示
损失函数
均方误差适合衡量回归预测结果与真实值的差距,但不能很好衡量分类结果与真实值之前的差距
在二分类中,当y为1时 后面项为0;当y为0时,前面项为0
对数运算的好处:
- 单调性质
- 结合性质 将多个乘转化为和的形式
- 缩放性 可以把一个大数范围进行压缩
m样本数,n类别数, Yij 表示样本i的类别标号是j,p(Xij)表示预测的样本i属于类别j的概率
二分类问题上本质上和对数损失函数等价
多分类表示上略有区别,更适合神经网络模型
通常二分类用对数损失函数,多分类用交叉熵损失函数
多分类问题代码实现
在二分类中 可以用sigmoid函数
在多分类中 通常用softmax函数
神经网络的最后一层称为softmax层,这一层的输出是概率分布,表示输入数据属于每个类别的概率,为了计算这个概率,我们使用softmax函数
在训练神经网络时,通常使用交叉熵损失函数度量预测值和真实值的差距,对于多分类问题,交叉熵损失函数可以计算为
y是预测值,p是预测值
通过最小化交叉熵损失函数,可以训练模型参数。训练完之后,可以使用他进行多分类,为了做出预测,需要将输入数据输入到神经网络中,并根据输出的概率分布决定它属于哪个类别
使用softmax函数和交叉熵损失函数是一种多分类的常见方法。
加载MINIST数据集
可以使用torchvision中的数据加载器轻松访问这个数据集
数据加载器
pytorch中加载和预处理数据的工具
可以将数据分成若干批,每次送入一个批次的数据到神经网络中,可以减少内存的使用,使得训练更快
cmap显示灰度图
构建网络
定义模型
定义一个线性层
实例化模型
损失函数和优化器
模型评估
model.eval 设置模型为评估模式不会更新权重
torch.no_gard 上下文管理器 适用于不需要求梯度的情况
x.view(-1,input_size) -1 是pytorch中特殊符号表示这个数据维度由数据其他维度决定
实际上是将数据拉伸成一个二维矩阵,每一行对应一个样本,每一列对应特征
使输入数据的形状满足模型的要求
模型训练
常见问题及对策
训练常见问题
模型架构设计
网络结构、节点数量、网络层数、不同类型的层、层间链接关系等
万能近似定理
一个具有足够多的隐藏节点的多层前馈神经网络,可以逼近任意连续的函数。
必须包含一种 有挤压性质的激活函数(例如sigmoid函数),让他能够进行非线性空间变换
宽度 or 深度
增加深度更有助提高泛化能力
实践证明,在宽度不变的情况下,增加深度,每一层更有助于捕获新的特征,更有助于提高泛化能力
过拟合问题
Overfitting;模型在训练数据上表现良好,在测试数据上不佳
泛化能力:训练后的模型应用到新的、未知的数据上的能力
产生原因:通常是由模型复杂度过高导致的
欠拟合问题
Underfitting:学习能力不足,无法学习到数据集中的“一般规律
产生原因:模型学习能力较弱,而数据复杂度较高的情况
解决办法:增加模型层数或者神经元数量或者更复杂的网络模型结构
模型复杂度越高,其表征能力越强
实际中,我们想要泛化误差尽可能小,着重解决过拟合问题
过拟合欠拟合应对策略
本质原因:数据和模型匹配问题
- 数据复杂度
- 模型复杂度
- 训练策略
数据集大小选择
数据角度
数据集较小,很容易出现过拟合
数据集过大可能导致训练效率降低
数据增强
数据角度
对训练数据进行变换,圳增加数据数量和多样性
有效解决过拟合问题,提高模型在新数据上的泛化能力
使用验证集
数据角度
验证集(validation set) : 训练中评估模型性能,调整超参数
在训练中就可以对模型进行评估
模型选择
奥卡姆剃刀法则:选择简单合适的模型解决复杂的问题
deeper is better
K折交叉验证
训练策略角度
1.将训练数据划分为K份
2.对于每份数据作为验证集,剩余的 K-1 份数据作为训练集,进行训练和验证。
3.计算 K 次验证的平均值。
提前终止
训练策略角度
Early stopping:模型对训练数据集迭代收敛之前停止迭代来防止过拟合
如果在验证集上发现测试误差上升,则停止训练
过拟合欠拟合代码示例
数据生成
数据划分
DataLoader 封装数据加载器
shuffle打乱顺序,提高泛化能力
模型定义
nn.Linear(a,b) 分别是输入输出维度
辅助函数
注意测试模型的时候 不用计算梯度
可视化
深度学习中的 正则化
对学习算法的修改目的是减少泛化误差,而不是训练误差
没有一种算法或者模型能够在所有的场景中都表现良好
正则化是一种权衡过拟合和欠拟合的手段
L2正则化
通过给模型的损失函数添加一个模型参数的平方和的惩罚项来实现正则化
空间解释
L1正则化
通过在损失函数中加入对模型参数权值矩阵中各元素绝对值之和的惩罚项,来限制模型参数的值
·L1正则化更倾向于产生稀疏解)适于特征选择
·L2正则化更倾向于小的非零权值,更适用于优化问题
范数惩罚
将L1和L2正则化扩展到一般情况
权重衰减
Dropout方法
数据增广是从数据角度
提前终止、权重衰减都是从训练过程角度优化
Dropout是从调整模型结构角度
工作原理
- 在训练过程中随机删除即将其权重设为零)一些神经元
- 只用在训练期间,不用在测试期间!
使模型不能完全依赖某些特定特征,防止神经对网络对某些训练集过于依赖,而使模型泛化能力更强
主要步骤
- 指定一个保留比例p
- 每层每个神经元,以p的概率保留,以1-p的概率将权重设为零
- 训练中使用保留的神经元进行前向、反向传播
- 测试过程,将所有权重乘以p
直观理解
- 相当于把一个网络拆分
- 由多个网络构成集成学习
神经网络中的使用
在训练网络的每个单元都要添加概率计算
为什么能减少过拟合
- 本质是Bagging集成学习,平均化作用(不同网络产生不同过拟合无法避免,但是取平均,可以抵消一部分)
- 减少神经元之间复杂的关系(2个神经元不一定每次都碰面, 权值更新不依赖本身连接关系,迫使其学习更鲁棒、显著的数据特征)
- 类似性别在生物进化中的角色
优点:
- 可以有效地减少过拟合
- 简单方便
- 实用有效
缺点:
- 降低训练效率
- 损失函数不够明确
本质上把一个大的网络拆成很多个小的网络,然后通过共享参数减轻过拟合
训练过程中随机把一些权重参数置为0
当前dropout已经被大量运用于全连接网络,一般设为0.5/0.3,效果非常好
Dropout代码实现
数据生成
模型定义
模型训练
可视化
梯度消失和爆炸
梯度的重要性
深度神经网络就是非线性多元函数
优化模型就是找到合适权重,最小化损失函数
本质反向传播的内在问题导致
链式求导法则本身就是激活函数的偏导数连乘
如果偏导数都是大于1 ,当层数增多,就会以指数形式爆炸,发生梯度爆炸
如果小于1 ,就会指数形式衰减,发生梯度消失
梯度消失(比梯度爆炸更容易出现,需要着重解决)
激活函数的导数小于1容易发生梯度消失
梯度爆炸
- 梯度可在更新中累积, 变成非常大,导致网络不稳定
- 原因之一:深层网络
- 原因之二:初始化权重的值过大
梯度爆炸信号:
训练过程中损失出现显著不稳定现象,或者溢出
解决方法
- 预训练加微调(比较老,不太用了)
- 梯度剪切
- 正则
- ReLU激活函数
- Batchnorm
- 残差结构
梯度裁剪
超过则将其强制限制在这个范围之内设置一个梯度剪切阈值
ReLU激活函数
导数在正数部分恒等于1,无梯度消失
Batch Normalization
简称BN,被广泛应用,具有加速收敛速度,提升网络训练稳定性的效果
将输出信号规范化到均值为0,方差为1保证网络的稳定性
将数据规整到统一区间,减少数据发散程度 ,减少网络训练的难度。
防止梯度弥散
残差结构
跨层连接结构解决梯度消失问题
可以轻松构建几百层上千层网络,不需要担心梯度消失问题
模型文件的读写
一方面保存模型,便于到新的环境使用
另一方面当模型训练需要很长时间时,定期保存,可以避免训练意外中断带来的损失
张量的保存和加载
深度学习中模型的参数一般都是张量形式
方式1:模型参数的保存
只保存参数,需要实例化模型
方式2:整个模型的保存
会记录模型定义路径,当模型定义路径更改,加载可能出错
方式3:checkpoint
以字典形式保存,运行中如果异常中止,可以让我们 很方便接着上次继续训练
梯度下降及变体
最优化与深度学习
二者密切联系
特定的优化问题: 寻找神经网络上的一组参数,能显著的降低损失函数
主要差异
- 目标不同:最优化有明确度量,深度学习只能用代理损失函数
- 数据不同:最优化只关心现有数据,深度学习关心泛化
- 实现方式:最优化只是数学算法,深度学习关心实现细节
- 研究内容:前者只关心最优解算法,后者专注于多层神经网络
训练误差和泛化误差
训练误差(Training Error):指模型在训练数据上的误差
泛化误差(Generalization Error):指模型在新数据上的误差
经验风险
把机器学习问题转化为一个优化问题的最简单方法是最小化训练集上的期望损失
优化中的挑战
- 病态(ill-conditioned problem) 数据敏感,数据上微妙变化会对求解造成剧烈变化 ,过拟合一定是病态
- 局部极小值
- 鞍点
- 悬崖
- 长期依赖(梯度消失和爆炸)
局部最小值
在某个局部区域内,目标函数的值小于周围所有点的值梯度下降可能在某个点处停止减小
解决:可以使用随机梯度法、也可以使用训练数据的不同子集
鞍点
鞍点(saddle point)模型训练过程中,损失函数在某一点取得最小值或最大值,但不是全局最优解。
特殊局部最小值
常在模型复杂度较高,训练样本少时出现
解决:可以使用不同的优化算法/减少模型 复杂度/使用更多数据/拆分不同子集/随机初始化/使用更多超参数搜索/
从数据、模型、搜索三个角度考虑问题
悬崖
梯度更新会很大程度上改变参数值,使得参数弹射的非常远
解决:梯度截断/
长期依赖问题
深度结构使模型丧失了学习到先前信息的能力,让优化变得困难
损失误差
损失函数的起源
损失函数(loss function):衡量预测值和真实值之间差异的函数
损失函数的起源可以追溯到统计学和最小二乘回归
MSE
MSE其实就是最大似然估计 的一种特殊情况
回归问题大量使用均方误差MSE
分类问题经常使用交叉熵损失函数
贝叶斯估计大量使用在概率图模型中
损失函数性质
可导性和可微性
可微性(differentiability):函数在任意一点处都有一个导数
可导性(continuity):函数有连续的导函数
使用梯度下降法的前提,进而能够寻找损失函数的最小值
凸性
凸函数保证损失函数有全局最小值,可以用较简单优化算法
凹函数则需要使用更复杂的优化算法找最小值
保证找到函数最小值的理论基础
如何判断函数凸性
凸性定理:如果函数的二阶导数大于 0,则是凸函数;
如果小于 0,则是凹函数;等于0,是平函数
凸约束和凸优化
凸优化:使用凸优化算法来最小化凸函数的优化问题
凸约束:在优化问题中,约束条件是凸函数
找到非凸问题中凸的地方进行解决
实际中,很多都是非凸问题,可以通过凸约束把非凸问题转化为凸问题
Jensen不等式
凸函数的定义就是Jensen不等式的两点形式
Jensen不等式:期望的函数小于等于凸函数的期望间
梯度下降
1847年提出
在深度学习中损失函数中如果有sigmoid函数 对数指数函数,这个时候函数叫超越方程,没有解析解,只能借助最优化中的 算法通过搜索逼近来求解。
如何逼近求解?
搜索逼近策略
梯度 Gradient
梯度就是函数曲面的陡度,偏导数是某个具体方向上的陡度
梯度就等于所有方向上偏导数的向量和
偏导数链式法则
对于高维使用这个
学习率
损失函数就像这座山,梯度就是找到最陡的方向, 学习率就是控制油门
三要素:初始点、梯度、学习率
随机梯度下降(SGD)
(Stochastic Gradient Descent, SGD)
1951年提出
梯度下降法的问题
- 不能保证被优化函数达到全局最优解
- 全部训练数据上最小化损失,计算时间太长
- 如果函数形态复杂,可能会在局部最小值附近来回震荡
- 对于初始值的选择非常敏感
随机梯度下降解决:
- 每次迭代中仅使用一个样本来计算梯度
- 根据梯度来调整参数的值
右边式子
优点:
计算速度快
相对不易陷入局部最优
缺点:
受噪声影响大·
有方差大无法收敛情况
动态学习率
使用动态学习率帮助模型更快地收敛
小批量梯度下降法
Mini-Batch Stochastic Gradient Descent
1994年提出
基本思想
每一次迭代中,使用一小部分的随机样本来计算梯度
优点
训练速度快
较少的震荡
更多的控制
缺点
需要调整参数变多
学习率敏感
批量大小通常在32-256之间
决定批量大小的因素
过大的批量虽然使得梯度估计更精确,但回报小
太小的批量难以充分利用多核架构
并行处理下,内存消耗和批量大小成正比
2的幂次方在使用GPU时可以提高效率,故取值32-256之间
注意:随机抽取
代码实现
收敛效果挺好
速度提高,收敛性也不错
通常来说SGD 通常就指小批量梯度下降
动量法
1993年提出
Momentum
物理学中的动量
动量指的是这个物体在它运动方向上保持运动的趋势
深度学习中的动量
一阶动量:过去各个时刻梯度的线性组合
二阶动量:过去各个时刻梯度的平方的线性组合
基本思想
将当前的梯度与上一步的梯度加权平均来减少梯度的震荡
惯性动量
优点:
加速收敛,可以跳过局部最小值
减少学习率过大导致的振荡风险
减轻初始值权重的敏感度
缺点:
需要维护动量项,速度变慢
AdaGrad算法
2011 年提出
二阶动量
基本思想
根据二阶动量动态调整学习率
稀疏特征(AdaGrad算法 主要针对这种情况优化)
指的是在很多样本中只有少数出现过的特征
训练模型时,稀疏特征可能很少更新,导致训练不出理想结果
优点:
有效地处理稀疏特征
能够自动调整学习率
缺点
训练后期收敛速度变慢
学习率调整方式固定
代码实现
RMSProp算法
改变二阶动量计算方法的策略:不累积全部历史梯度,而只关注过去一段时间窗口的下降梯度
优缺点:
自动调整学习率,模型收敛更快
实现上较为简单
需要调整超参数,如衰减率和学习
率
对稀疏特征效果不是很好
AdaDelta算法
避免使用手动调整学习率的方法来控制训练过程,而是自动调整学习率,使得训练过程更加顺畅
AdaDelta优缺点
自动调整学习率,省去调参时间
收敛比较慢,无法显示调整学习率
需要维护梯度和权重更新量的积分
两种对二阶动量进行优化的方法
基本思想:时序累加修改二阶动量,动态调整学习率
Adam算法
基本思想
把一阶动量和二阶动量都用起来,Adaptive+Momentum
梯度下降法及其变体关系
原理框架流程
核心差异
区别在于下降方向
最佳选择策略讨论
- 不想做精细的调优,那么Adam
- 更加自如地控制优化迭代的各类参数,那么SGD
- 先用Adam快速下降,再用SGD调优
- 算法美好,数据王道!
经验建议
选择熟悉的算法·
根据需求来选择·
先用小数据实验
数据集充分打散·
考虑不同的组合
监控好训练数据
梯度下降代码实现
学习率调节器
需要考虑的因素
模型复杂度
训练数据规模
任务复杂度
优化器类型
损失函数变化情况
Batch size
什么是学习率调节器
学习率是各类优化算法中的最关键的参数之一
学习率调节器能够在训练过程中动态调整学习率
一般刚开始大,后面小
学习率衰减
每训练一定次数就将学习率降低一定比例
指数衰减
每次迭代时将学习率乘上一个衰减率,从而使学习率逐渐降低
余弦学习率调节
根据余弦函数来调节学习率
预热
学习率从较小值逐渐提升到较大值的过程