0 引言
- 李沐老师的《动手学深度学习》课程笔记
- 跳转至课程网站
1 课程安排
- 内容
- 深度学习基础:线性神经网络,多路感知机
- 卷积神经网络:LeNet,AlexNet,VGG,Inception,ResNet
- 循环神经网络: RNN, GRU, LSTM, seq2seq
- 注意力机制: Attention, Transformer
- 优化算法: SGD, Momentum, Adam
- 高性能计算: 并行, 多GPU, 分布式
- 计算机视觉: 目标检测, 语义分割
- 自然语言处理: 词嵌入, BERT
3 安装
1 | sudo apt install build-essential # 安装编译环境 |
4 数据操作+数据预处理
4.1 数据操作
- N维数组是机器学习和神经网络的主要数据结构
- 0-d标量:一个类别
- 1-d向量:一个特征向量
- 2-d矩阵:一个样本-特征矩阵
- 3-d: RGB图片(*宽*高*通道)
- 4-d:一个RGB图像批量(批量大小*宽*高*通道)
- 5-d: 一个视频批量(批量大小*时间*宽*高*通道)
- python中 : 代表的区间为前开后闭的区间
- id()类似C++中指针
1 | [1:3, 1:] # 访问子区域,表示访问1-2行,1到最后一列 |
4.2 数据预处理
1 | # 创建一个人工数据集,并存储在csv文件 |
4.3 数据操作
1 | # reshape 和 view的 区别 |
- tensor为张量,为数学上的定义,array为数学上的数组的定义
5 线性代数
1 | # 标量 |
- 对指定维度求和 sum:对哪个维度求和,得到的维度为丢掉该维度,若keepdims则将丢掉的维度设置为 1
6 矩阵计算

7 自动求导
7.1 自动求导
- 自动求导计算一个函数在指定值上的导数
- 计算图
- 将代码分解成操作子
- 将计算表示成一个无环图

- 反向累计总结
- 构造计算图
- 前向: 执行图,存储中间结果
- 反向: 从相反方向执行图,去除不需要的枝
- 复杂度
- 计算复杂度:
- 反向:,n是操作子个数,正向和反向的代价类似
- 正向:,计算复杂度用来计算一个变量的梯度
- 内存复杂度:
- 反向:,需要存储正向的所有中间结果,消耗GPU资源
- 正向:
- 计算复杂度:
7.2 自动求导实现
- 实现对函数求导
1 | import torch |
- 非标量变量的反向传播
- 当
y不是标量时,向量y关于向量x的导数的最自然解释是一个矩阵 - 对于高阶和高维的
y和x,求导的结果可以是一个高阶张量 - 然而,虽然这些更奇特的对象确实出现在高级机器学习中(包括深度学习中),但当调用向量的反向计算时,通常会试图计算一批训练样本中每个组成部分的损失函数的导数(我们的目的不是计算微分矩阵,而是单独计算批量中每个样本的偏导数之和。)
- 当
1 | # 深度学习中,目的不是计算微分矩阵,而是批量中每个样本单独计算的偏微分之和 |
- 分离计算:将某些计算移动到记录的计算图之外]
- 例如,假设
y是作为x的函数计算的,而z则是作为y和x的函数计算的。需要计算z关于x的梯度,但由于某种原因希望将y视为一个常数,并且只考虑到x在y被计算后发挥的作用 - 可以分离
y来返回一个新变量u,该变量与y具有相同的值,但丢弃计算图中如何计算y的任何信息- 换句话说,梯度不会向后流经
u到x,后面的反向传播函数计算z=u*x关于x的偏导数,同时将u作为常数处理,而不是z=x*x*x关于x的偏导数
- 换句话说,梯度不会向后流经
- 例如,假设
1 | # 将某些计算移动到记录的计算图之外 |
- pytorch为隐式构造,对控制流的计算较好,但计算速度较显示构造慢
- 多个损失函数分别反向时需要累计梯度,因此pytorch默认为累计梯度的
8 线性回归+基础优化算法
- 线性总结
- 线性回归是对n维输入的加权,外加偏差
- 使用平方损失来衡量预测值和真实值的差异
- 线性回归有显示解
- 线性回归可以看作单层神经网络
- 梯度下降
- 挑选一个初始值
- 重复迭代参数
- 沿梯度方向将增加损失函数值
- 学习率:步长的超参数
- 小批量随机梯度下降
- 在整个训练集上算梯度太贵,一个深度神经网络模型可能需要数分钟至数小时
- 可以随机采样b个样本来近似损失,
- b是批量大小,另一个重要的超参数
- 批量太小,计算量太小不适合并行来最大利用计算资源
- 批量太大内存消耗增加,浪费计算
- b是批量大小,另一个重要的超参数
- 总结
- 梯度下降通过不断沿着繁体都方向更新参数求解
- 小批量随机梯度下降是深度学习默认的求解算法
- 两个重要的超参数是批量大小和学习率
1 | # ---------------------- |
1 | # ---------------- |
- 为什么使用平方损失而不是绝对差值
- 平方损失和绝对差值的差别不大,绝对差值是一个不可导的函数,在0点时绝对差值的导数不好求解
- 损失为什么取平均
- 本质上没有太大差别,取平均之后梯度数值较小,便于计算。不使用损失取平均,使用学习率除以n也可以
- GD和SGD的差别
- GD在每次迭代都需要用到全部训练数据
- SGD每次迭代可以只用一个训练数据来更新参数
- 使用生成器生成数据的优势
- 相比return,需要一个batch运行一遍,并不需要完全设置好
9 Softmax 回归 + 损失函数 + 图片分类数据集
9.1 Softmax 回归
- 回归 VS 分类
- 回归估计一个连续值
- 单连续数值输出,输出为自然区间, 跟真实值的区别作为损失
- 分类预测一个离散类别
- 通常多个输出,输出i是预测为第i类的置信度
- 回归估计一个连续值
- 从回归到多类分类——均方损失
- 对类别进行1为有效编码
- 使用均方损失训练,
- 无校验比例: 需要更置信的识别正确类(大余类),
- 校验比例
- 输出匹配概率(非负,和为1):
- 概率 和 的区别作为损失
- 输出匹配概率(非负,和为1):
- softmax和交叉熵损失
- 交叉熵常用来衡量两个概率的区别
- 损失函数
- 梯度是真实概率和预测概率的区别
- 总结
- softmax是一个多分类模型
- 使用softmax操作子得到每个类的预测置信度
- 使用交叉熵来衡量预测和标号的区别

9.2 损失函数
- 均方损失L2 Loss:
- 绝对值损失函数L1 Loss :
- 当预测值和真值相距较远时,梯度仍然较大,参数更新速度不变,但 0 点处不可导,不平滑,优化末期时会不稳定
- Huber损失函数:
- 综合上述两个损失函数的特点,当预测值和真值相差较大时参数更新速度不变,在优化末期,梯度更新速度会越来越小,优化较为平滑

9.3 图片分类数据集
1 | # MNIST数据集是图像分类中广泛使用的数据集之一,但作为基准数据集过于简单 |
9.4 softmax回归从零开始实现
1 | # softmax回归的从零开始实现 |
9.5 softmax的简洁实现
1 | # 通过深度学习框架的高级API能够很容易的实现softmax回归 |
9.6 QA

-
- hard label 更容易标注,但会丢失类内,类间的关联并且引入噪声
- softlabel 给模型带来更返回能力,携带更多信息,对噪声鲁棒
-
- softlabel训练策略
- 在hard label监督下,由于softmax的作用,one-hot的最大值位置无限往1优化,但永远不可能等于1,达到一定优化程度后达到饱和区,可以保证优化过程处于优化效率最高的中间区域,避免进入饱和区
-
- 使用one-hot编码会使不正确的类变为0, 因此损失函数不计不正确的类,实际上是关心不正确的类
-
- 最小化损失等价于最大化似然函数,统计学中的概念
-
- 在计算精度时使用eval()将模型设置为评估模式可以避免在评估模式时计算梯度
10 多层感知机+代码实现
10.1 感知机
- 给定输入, 权重, 和偏移b, 感知机输出
- 二分类:-1或1
- 回归输出实数,Softmax输出概率
- 二分类:-1或1
- 训练感知机
- 等价于使用批量大小为1的梯度下降,并使用损失函数
- 当分类正确时,相当于小于0,则损失函数为0,不进行更新
- 等价于使用批量大小为1的梯度下降,并使用损失函数

- 收敛定理
- 数据在半径内
- 余量
- 感知机保证在步后收敛
10.2 多层感知机

- 上述问题需要实现异或操作,一个分类器无法实现,则使用多个分类器组合

- 隐藏层大小是超参数
- 单隐藏层——单分类
- 输入:
- 隐藏层:
- 输出层:
-是按元素的激活函数,激活函数为非线性函数
-
- 激活函数
- sigmoid函数:将输入投影到(0, 1)
- tanh激活函数: 将输入投影到(-1, 1)
- ReLu函数: ReLu = max(x, 0)
- 多类分类:
- 与softmax回归的唯一区别,添加了隐藏层
- 输入:
- 隐藏层:
- 输出层:
-
-
- - 可以使用多隐藏层:
-
-
-
- - 超参数
- 隐藏层数
- 每层隐藏层的大小
- 多隐藏层时,隐藏层的大小越往后越压缩,最好是慢慢做压缩,不断对信息做提炼,在一开始可以比输入层稍大一点,扩充数据,(但在CNN中会有先压缩再扩张的模型)
10.3 多层感知机的实现
1 | # 1 多层感知机的从零开始实现 |
- 多层感知机的精度和softmax回归的精度差别不大,损失减小
- 使用MLP(多层感知机)而不是SVM的原因:MLP方便之后效果不好时调整模型,代码方便实现
1 | # 2 多层感知机的简洁实现 |
10.4 QA
- 神经网络中一般使用增加隐藏层的层数,而不是神经元个数
- 因为一层中神经元个数太多,网络较难训练
- 不同任务下的激活函数本质上区别不大,该超参数的调节意义较小
- 一般使用ReLu即可
11 模型选择 + 过拟合和欠拟合
11.1 模型选择
- 训练误差和泛化误差
- 训练误差:模型在训练数据上的误差
- 泛化误差:模型在新数据上的误差
- 验证数据集和测试数据集
- 验证数据集:一个用来评估模型好坏的数据集
- 经常称为test_data,但是是错误的
- 测试数据集:只用一次的数据集
- 验证数据集:一个用来评估模型好坏的数据集
- 解决数据集数量不足的问题:K-折交叉验证
- 在没有足够多数据时使用
- 算法
- 将驯良数据分割成K块
- For i=1,…,K
- 使用第i块作为验证数据集,其余的作为训练数据集
- 报告K个验证集误差的平均
- 常用K=5,或K=10
11.2 过拟合和欠拟合

- 模型容量
- 拟合各种函数的能力
- 低容量的模型难以拟合训练数据
- 高容量的模型可以记住所有的训练数据

- 在模型足够大的前提下,控制模型容量是泛化误差向下降,泛化误差和训练误差的gap变小,可以容忍一定程度的过拟合
- 模型容量的两个主要因素:参数的个数和参数值的选择范围
- VC维
- 对于一个分类模型,VC等于一个最大的数据集的大小,不管如何给定标号,都存在一个模型来对它进行完美分类
- 二维输入的感知机,VC维=3
- 能够分类任何3个点,而不是4个(xor)
- 支持N维输入的感知机的VC维是N+1
- 一些多层感知机的VC维是
- 深度学习中很少使用,衡量不是很准确,且计算较难
- 数据复杂度
- 多个重要因素:样本个数,每个样本的元素个数,时间和空间结构,多样性
11.3 代码
- 使用多项式来生成训练和测试数据的标签:
1 | # 1 模型选择,欠拟合和过拟合 |
12 权重衰退
12.1 权重衰退
- 使用均方范数作为硬性限制
- 权重衰退,通过限制参数值的选择范围来控制模型容量:
- 通常不限制偏移 b (限制不限制都差不多)
- 小的 意味着更强的正则项
- 权重衰退,通过限制参数值的选择范围来控制模型容量:
- 使用均方单数作为柔性限制
- 对于每个 ,都可以找到 使之前的目标函数等价于 ,(可通过拉格朗日乘子来证明)
- 超参数 控制了正则项的重要程度
-:无作用
-
- 参数更新法则
- 计算梯度:
- 时间t更新参数:
- 通常,在深度学习中通常叫做权重衰退
- 权重衰退用来可解决欠拟合问题
12.2 代码实现
- 生成数据的公式:
1 | # 1 权重衰退的从零开始实现 |
13.1 丢弃法(dropout)
- 动机
- 一个好的模型需要对输入数据的扰动鲁棒
- 使用有噪声的数据等价于Tikhonov正则
- 丢弃法:在层之间加入噪音
- 一个好的模型需要对输入数据的扰动鲁棒
- 无偏差的加入噪音
- 对 加入噪音得到 ,但希望
- 丢弃法对每个元素进行扰动:,不改变数据的期望
- dropout参数p为丢弃的节点的概率
- 使用丢弃法:通常将丢弃法作用在隐藏全连接层的输出上
-
-
-- y = softmax(o)

- 推理中的丢弃法
- 正则项只在训练中使用:影响模型参数的更新
- 在推理过程中,丢弃法直接返回输入:
- 保证确定性的输出
- 总结
- 丢弃法将一些输出项随机置0来控制模型复杂度
- 常作用在多层感知机的隐藏层输出上
- 丢弃概率是控制模型复杂度的超参数
13.2 代码实现
1 | # 1 Dropout的从零开始实现 |
13.3 QA
- dropout随机置0的地方梯度也为0,dropout随机置0的地方的权重在该轮不被更新
- BN为卷积层使用的,dropout为全连接层使用的
- BN层,可以加快网络的训练和收敛的速度,控制梯度爆炸防止梯度消失,防止过拟合
- dropout改变的为隐藏层的输出
- 也可以改变标签实现正则化(CV中会使用)
- dropout和权重衰减为两种解决过拟合的方法
- 权重衰减weight_weak更为常用可以在CNN等中使用
- dropout更容易调参
- 训练一个单隐藏层的全连接MLP,隐藏层不使用dropout为64,而使用dropout=0.5隐藏层为128,效果可能较好,需要让模型更强而使用正则使模型学偏
14 数值稳定性 + 模型初始化和激活函数
14.1 数值稳定性
- 神经网络的梯度
- 考虑有d层的神经网络: h^t=f_t(h^{t-1}) \space and \space y=l_d_d_...*f_1(x)
- 计算损失关于参数的梯度:
- 上述表达式中间计算了次矩阵乘法
- 太多矩阵乘法会带来梯度爆炸和梯度消失的问题
- 梯度爆炸的问题
- 值超出值域(对float16尤为严重,GPU对float16运算速度较快)
- 对学习率敏感
- 学习率太大->大参数值->更大的梯度
- 学习率太小->训练无进展
- 可能需要在训练过程不断调整学习率

- 梯度消失的问题
- 梯度值变成0(对float16尤为严重)
- 训练没有进展,不管如何选择学习率
- 对于底部层尤为严重,仅仅顶部层训练的较好,无法让神经网络更深
14.2 模型初始化和激活函数
- 让训练更加稳定
- 目标:让梯度值在合理的范围内
- 让乘法变成加法: ResNet,LSTM
- 归一化: 梯度归一化,梯度裁剪
- 合理的权重初始和激活函数
- 将神经网络设计的每层设计为如下形式可以使训练更加稳定
- 让每层的方差都是一个常数
- 让每层的输出和梯度都看作随机变量
- 让均值和方差都保持一致
-,a和b都是常数
- 让每层的方差都是一个常数
- 具体做法
- 权重初始化:在合理区间里随机初始参数
- 训练开始的时候更容易有数值不稳定
- 远离最优解的地方损失函数表面可能很复杂
- 最优解附近表面比较平
- 使用来初始可能对小网络没问题,但不能保证深度神经网络
- 例子MLP
- 假设
- 是 i.i.d, 那么E
- 独立于 - 假设没有激活函数 , 这里
- - 正向方差:
- 假设输入方差和输出方差一致,则
- 反向均值和方差
-
-
- - Xavier初始化(权重初始化的方法,设置权重的方差)
- 难以满足
- Xavier 使得
- 正态分布
- 均匀分布
- 分布 和方差是
- 适配权重形状更新,特别是
- 假设
- 激活函数的选取
- 假设线性的激活函数 and
- 正向
-
- - 反向 and
-
-
- 正向
- 常用的激活函数,使用泰勒展开
-- sigmoid的一次项不满足要求,则需要调整sigmoid为即可满足要求
- 假设线性的激活函数 and

14.3 QA
- nan和inf的产生原因和解决办法
- nan的产生原因:除以0出现,一般梯度过小会出现
- inf的产生原因:learning rate过大,或权重初始时值太大出现
- 解决办法:学习率选择不太大(优先调整学习率使不产生nan和inf),合理初始化权重,激活函数的选择
- 在训练过程中,如果网络层的输出中间层特征元素的值突然变成nan,一般是发生了梯度爆炸问题
- 梯度消失的原因可能由sigmoid激活函数引起,但不仅仅是由该原因引起
15 实战:Kaggle房价预测 + 课程竞赛:加州2020年房价预测
1 | # 实现几个函数来方便下载数据 |
16 PyTorch 神经网络基础
16.1 模型构造
1 | # 层和块 |
16.2 参数管理
1 | # 参数管理 |
16.3 自定义层
1 | # 构造一个没有任何参数的自定义层 |
16.4 读写文件
1 | # 加载和保存张量 |
16.5 QA
- 将特征字符串变成tensor,使用one-hot方法进行转换,内存不足时的解决办法
- 方法一:使用稀疏矩阵进行解决
- 方法二:使用其他方法进行解决,使用summary
- net(x)实现的原理
- 其继承的nn.module实现了__call__方法,类似于在类中重载 () 运算符,使得类实例对象可以像调用普通函数那样,以“对象名()”的形式使用
17 使用和购买 GPU
1 | # 计算设备 |
18 预测房价竞赛总结
- 使用的方法:
- 跳转至 autogluon
- 跳转至 h2o
- 跳转至 随机森林the-th-place-approach-random-forest
- 在该竞赛中,排名靠前的都是用了集成学习
- 集成学习为刷榜的常用方法
- 特征预处理和超参数是取得好成绩的基础
- 房价数据集的难点
- 数值较大
- 有文本特征(地址,介绍)
- 训练数据是前6个月,公榜是后3个月,私榜是再后3个月
- 关于automl
- 数据科学家80%时间在处理数据,20%调模型
- automl目前节省10%时间
19 卷积层
19.1 从全连接到卷积
- 重新考察全连接层
- 将输入和输出变形为矩阵(宽度,高度)
- 将权重变形为4-D张量到:
- V是W的重新索引
- 原则#1-平移不变性
- x的平移导致h的平移
- v不应该依赖于
- 解决方案:,
- 这就是二维卷积(实际为二维交叉相关)
- 原则#2-局部性
-- 当评估时,不应该用远离的参数
- 解决方案:当时,使得
-
- 卷积层是特殊的全连接层,其使用了平移不变性和局部性原理
19.2 卷积层

- 二维卷积层
- 输入X:
- 核W:
- 偏差
- 输出Y:
- - 核矩阵 W 和 偏移 b是可学习的参数, 核矩阵的大小是超参数
- 卷积层实际上计算的是交叉相关
- 交叉相关和卷积由于对称性,实际在使用中没有区别
- 二维交叉相关:
- 卷积:
- 一维交叉相关:文本,语言,时序序列
- 三维:视频,医学图像,气象图片
19.3 代码
1 | # 互相关运算 |
20.1 填充和步幅
- 填充
- 给定32*32输入图像
- 应用5*5大小的卷积核
- 第1层得到输出大小28*28
- 第7层得到输出大小4*4
- 更大的卷积核可以更快地减小输出大小
- 形状从 n_k_n_w 减小到
- 填充:在输入周围添加额外的行/列
- 填充行和列,输出形状为
- 通常取
- 当为奇数:在上下两侧填充
- 当为偶数:在上侧填充, 在下侧填充
- 步幅
- 填充减小的输出大小与层数线性相关
- 给定输入大小224*224,在使用5*5卷积核的情况下,需要44层将输出降低到4*4
- 需要大量计算才能得到较小输出
- 步幅是指行/列的滑动步长
- 给定高度 和宽度 的步幅,输出形状是
- 如果,
- 如果输入高度和宽度都可被步幅整除:
- 填充减小的输出大小与层数线性相关
- 总结
- 填充和步幅是卷积层的超参数
- 填充在输入周围添加额外的行/列,来控制输出形状的减小量
- 步幅是每次滑动核窗口时的行/列的步长,可以成倍的减少输出形状
20.2 代码实现
1 | # 填充和步幅 |
20.3 QA
- 超参数:核大小,填充,步幅
- 一般而言,填充保证输入和输出的大小相同,填充为核大小减一,为默认值
- 一般而言,步幅,步幅为 1 最好,当计算量太大时,步幅较大,取 2
- 一般而言,核大小为最关键的
- 填充,步幅和通道数一般是网络架构的一部分,一般不改变
- 卷积核的边长一般取奇数,一般选择 3*3
- 使用自动机学习,NAS方法可以让超参数一起训练
21 卷积层里的多输入多输出通道
- 多个输入通道
- 彩色图片可能有RGB三个通道,转换为灰度会丢失信息
- 每个通道都有一个卷积核,结果是所有通道卷积结果的和
- 输入X:c_i_n_h_n_w
- 核W:c_i_k_h_k_w
- 输出Y:,表达式为
- 多个输出通道
- 无论多少个输入通道目前只用到单输出通道
- 可以有多个三维卷积核,每个核生成一个输出通道
- 输入X:c_i_n_h_n_w
- 核W:c_o_c_i_k_h*k_w
- 输出Y:c_o_m_h_m_w,表达式为
- 多个输入和输出通道
- 每个输出通道可以识别特定模式
- 输入通道核识别并组合输入中的模式
- 1*1卷积核
-是一个受欢迎的选择,不识别空间模式,只是融合通道- 相当于输入形状为n_hn_w_c_i,权重为c_o_c_i的全连接层

- 二维卷积层
- 输入X:c_i_n_h_n_w
- 核W:c_o_c_i_k_h*k_w
- 偏差B:
- 输出Y:c_o_m_h_m_w,表达式为
- 计算复杂度(浮点计算书FLOP)
- 每个输入通道有独立的二维卷积核,所有通道结果相加得到一个输出通道结果;每个输出通道有独立的三维卷积核
1 | # 实现以下多输入通道互相关运算 |
- QA
- 通常情况下,输入和输出大小减半时,输出的通道数会加一倍(空间信息压缩使用更多通道存储信息)
- Padding 0很多不会影响卷积神经网络
- 每个通道的卷积核不一样,同一层不同通道的卷积核大小一样,为了计算方便,若不一样需要进行两个卷积分开操作,计算效率更高
- 在计算卷积时,偏差的作用性越来越低,尤其是在batch normalization时
- MobileNet:使用3*3*3和1*1*n的卷积层叠加,来分别进行空间信息的检测和信息融合以及输出通道的调整
22 池化层
- 池化层
- 积对位置敏感
- 需要一定程度的平移不变性,物体稍微改动,输出不变,因此需要一个池化层
- 池化层的目的为:降低卷积层对位置的敏感性,同时降低对空间降采样表示的敏感性
- 二维最大池化层
- 返回滑动窗口中的最大值
- 填充,步幅,和多个通道
- 池化层与卷积层类似,都具有填充、步幅和窗口大小作为超参数
- 没有可学习的参数
- 在每个输入通道应用池化层以获得相应的输出通道,不会像卷积层将多个通道融合
- 输出通道数=输入通道数
- 平均池化层
- 最大池化层:每个窗口中最强的模式信号
- 平均池化层:将最大池化层中最大操作替换成平均
1 | # 实现池化层的正向传播 |
- QA
- 池化层一般放在卷积层之后,降低卷积对位置的敏感性
- 池化时,窗口重叠与没有重叠几乎没有影响
- 目前池化层的使用越来越少,通过其他操作实现
23 经典卷积神经网络 LeNet

- LeNet其中使用5*5的卷积核和2*2的平均池化层
- 先用卷积层来学习图片空间信息然后使用全连接层来转换到类别空间
1 | # LeNet由两个部分组成:卷积编码器和全连接层密集块 |
-
卷积神经网络设计思想:
- 使用不同的卷积层获取空间信息并不断进行压缩,将压缩后的信息增加到不同通道中,不断减小高和宽,最后通过感知机实现
-
QA
- 常见做法:高宽减半,通道数翻倍(使通道能匹配的模式更多)
24 深度卷积神经网络 AlexNet

- AlexNet
- AlexNet赢了2012年ImageNet竞赛
- 更深更大的LeNet
- 主要改进
- 丢弃法:方便做更大的模型
- ReLu:让Sigmoid激活函数梯度可以更大,支持更深的模型
- MaxPooling:使得输出值比较大,梯度值相应更大,使得训练更加容易
- 计算机视觉方法论的改变
- 对比
- 传统机器学习方法:输入图片->人工特征提取->SVM
- 深度学习方法:输入图片->通过CNN学习特征->Softmax回归
- 构造CNN相对简单, Softmax更加高效
- 转换为端到端的学习



- AlexNet相较于LeNet的架构变化
- 更大的核窗口和步长,因为图片更大了
- 使用3*3的池化窗口,2*2运行一个像素往一边移动一下不影响输出,而3*3允许往左右各移一点不影响输出
- 输出通道变为256,用更大的输出通道,识别更多的模式
- 新加了3层卷积层和一个池化层
- 从120增加到了4096,输出1000类
- 窗口更大,增加了三个卷积层,最后的全连接层更大
- 激活函数从sigmoid变到了ReLu(减缓梯度消失)
- 隐藏全连接层后加入了丢弃层(两个4096全连接层增加dropout丢弃)
- 数据增强

1 | # AlexNet深度神经网络 |
- QA
- 卷积神经网络卷积层提取的信息不够充足,需要两个全连接Dense(4096)层,若只是用一个,效果会变差
- 网络要求输入size固定,对于不同的图片需要保持长宽比,将短边resize到指定的尺寸再进行裁剪
25 使用块的网络 VGG


- AlexNet最大问题是结构不够规则,结构不够清晰,设计更深的网络需要更好的设计思想,将框架设计的更加规则
- 使用更多的全连接层(太贵)
- 更多的卷积层
- 将卷积层组合成块(VGG的重要思想)

- VGG块
- 同样的计算开销的情况下,深但窄效果更好
- 3*3卷积(填充1),n层,m通道
- 2*2最大池化层(步幅2)
- VGG结构
- 多个VGG块后接全连接层
- 不同次数的重复块得到不同的架构,VGG-16,VGG-19,…
- VGG对AlexNet最大的贡献:将AlexNet中的卷积和池化层抽离成VGG块

- 总结
- VGG 使用可重复使用的卷积块来构建深度卷积神经网络
- 不同的卷积块个数和超参数可以得到不同复杂度的变种
1 | # VGG块 |
26 网络中的网络 NiN
- 网络中的网络NiN目前使用较少,但提出很多思想
- 全连接层的问题
- 卷积层需要较少的参数 c_i_c_o_k^2
- 但卷积层后的第一个全连接层的参数
- LeNet 16_5_5*120=48k
- AlexNet 255_5_5*4096=26M
- VGG 512_7_7*4096=102M,占用空间较大,空间的大部分占用都在卷积层后的第一个全连接层
- 为了解决VGG中参数较多,占用较大的问题,提出NiN
- NiN的思想:完全不要全连接层
- NiN块
- 一个卷积层后跟两个全连接层
- 步幅1,无填充,输出形状跟卷积层输出一样
- 1*1卷积层起到了全连接层的作用
- NiN块类似最简单的卷积神经网络
- 一个卷积层后跟两个全连接层

- NiN架构
- 无全连接层
- 交替使用NiN块和步幅为2的最大池化层
- 逐步减小高宽和增大通道数,步幅为2的最大池化层为高宽减半
- 最后使用全局平均池化层(高宽等于输入的高宽)得到输出
- 其输入通道数是类别数

- 总结
- NiN块使用卷积层加两个1*1卷积层
- 后者对每个像素增加了非线性性(两个卷积层相当于做了两个隐含层的MLP,MLP的ReLu函数增加了线性性)
- NiN使用全局平均池化层来替代VGG和AlexNet中的全连接层
- 不容易过拟合,更少的参数个数
- NiN块使用卷积层加两个1*1卷积层
1 | # NiN块 |
- QA
- 使用pytorch的部署到C++生产环境中,可以使用torchScript或者onnx部署
- 分类使用softmax,softmax函数写在training函数的loss函数中,没有写在神经网络中
- 加入全局池化层,将输入降低,且没有可学习的参数,使学习变得简单,最大的好处是使模型复杂度降低,提升泛化性,缺点是使收敛变慢
27 含并行连结的网络 GoogLeNet / Inception
- googleNet

- 最好的卷积层超参数


- Inception块:4个路径从不同层面抽取信息,然后再输出通道维合并
- 结构
- 第一条路径使用1*1的卷积层
- 第二条路先使用1*1的卷积层对通道做变换,然后使用3*3的卷积层
- 第三条路先使用1*1的卷积层对通道做变换,然后使用5*5的卷积层
- 第四条路先使用3*3的池化层,然后使用1*1的卷积层
- 最后使用concatenation做合并,跟输入等同高宽
- 跟单3*3或5*5卷积层比,Inception块由更少的参数个数和计算复杂度
- 结构

上图中白色的框用来变换通道数,其他蓝色框用来抽取空间信息
- GoogLeNet
- 5段,9个Inception块
- stage:高宽减半为一个stage
- GoogLeNet stage1 为 7*7卷积核3*3MaxPool
- GoogLeNet stage2 为 1*1卷积 3*3卷积核 3*3MaxPool
- GoogLeNet stage3 为 2 个Inception block(不改变高宽只改变通道数) 3*3MaxPool
- GoogLeNet stage4 为 5 个Inception block(不改变高宽只改变通道数) 3*3MaxPool
- GoogLeNet stage5 为 2 个Inception block(不改变高宽只改变通道数) 3*3MaxPool
- 最后使用全局池化层和全连接层
- GoogLeNet大量使用1*1卷积层增加通道数,并且使用全局池化层

- 段1 & 段2:更小的窗口,更多的通道
- 快速减小图像尺寸并迅速增大通道数,使用更小的卷积层高宽保留更流

- 段3: 2个Inception block的通道数的分配不同

- 段4 & 段5: 5个Inception block

- Inception由各种后续变种
- Inception-BN(V2):使用batch normalization
- Inception-V3:修改了Inception块
- 替换5*5为多个3*3卷积层
- 替换5*5为1*7和7*1卷积层
- 替换3*3为1*3和3*1卷积层
- 更深
- Inception-V4:使用残差连接



上图右侧为InceptionV1的版本,左图为InceptionV3的版本
- 总结
- Inception块由4条由不同超参数的卷积层和池化层的路来抽取不同的信息
- 一个主要优点就是模型参数小,计算复杂度低
- GoogLeNet使用了9个Inception块,是第一个达到上百层的网络
- 后续有一系列改进
- Inception块由4条由不同超参数的卷积层和池化层的路来抽取不同的信息
1 | # Inception块 |
- QA
- 1*1的卷积层用来降低通道数,减小计算量
- 在神经网络搭建中,最好不要修改经典的模型,或者将通道数整体减少
- 通道数通常设置为,主要是为了方便GPU计算
- 目前主要使用GoogleNetV3
28 批量归一化(Batch norm)
- 批量归一化
- 问题
- 损失出现在最后,后面的层训练较快
- 数据在最底部
- 底部的层训练较慢
- 底部层一变化,所有都得跟着变
- 最后的那些层需要重新学习多次
- 导致收敛变慢
- 批量归一化可以在学习底部层的时候避免变化顶部层
- 原理
- 固定小批量里面的均值和方差: and
- 然后再做额外的调整(可学习的参数):
- 批量归一化层
- 可学习的参数
- 作用在
- 全连接层和卷积层输出上,激励函数前
- 全连接层和卷积层输入上
- 对全连接层,作用在特征维
- 二维输入,每一行为样本,每一列为特征
- 对卷积层,作用在通道维
- 在每个批量里,一个像素是一个样本,与像素(样本)对应的通道维为就是特征维
- 批量归一化在做什么
- 最初论文是向用它来减少内部协变量转移
- 后续有论文指出它可能就是通过在每个小批量里加入噪声来控制模型变量:为随机偏移和随机缩放,为噪声,然后通过一个学习到的方差和均值进行归一化
- 没必要和dropout丢弃法混合使用
- 问题
- 批量归一化固定小批量中的均值和方差,然后学习出适合的偏移和缩放
- 可以加速收敛速度,但一般不改变模型精度
1 | # batch norm的从零开始实现 |
-
代码实现中:
momentum=0.9, eps=1e-5, 1e-6, 1e-7都可以,不同的eps对输出有一定影响- pytorch中默认
momentum=0.1
- pytorch中默认
-
QA
- xavier保证在参数初始化时初始化正常避免梯度爆炸和消失;BN保证在训练过程中均值和方差,可以保证较大的梯度,保证可使用较大的学习率,保证训练速度
29 残差网络 ResNet
- ResNet是实际使用中较好用的一个神经网络
- 对于非嵌套函数类,较复杂(由较大区域表示)的函数类不能保证更接近“真”函数( ),在嵌套函数类中不会发生,只有当较复杂的函数类包含较小的函数类时才能确保提高它们的性能
- 对于深度神经网络,如果我们能将新添加的层训练成_恒等映射_,新模型和原模型将同样有效;同时,由于新模型可能得出更优的解来拟合训练数据集,因此添加层似乎更容易降低训练误差
- 对于非嵌套函数类(图左边),较复杂的函数类并不总是向“真”函数靠拢(复杂度由向递增),虽然比更接近,但却离的更远了
- 相反对于图右侧的嵌套函数类,可以避免上述问题
- 对于深度神经网络,如果我们能将新添加的层训练成_恒等映射_,新模型和原模型将同样有效;同时,由于新模型可能得出更优的解来拟合训练数据集,因此添加层似乎更容易降低训练误差

-
残差网络的核心思想是:每个附加层都应该更容易地包含原始函数作为其元素之一
-
残差块
- 串联一个层改变函数类,希望能扩大函数类
- 残差块加入快速通道(右边)来得到 的结构
- 左边的ResNet的高宽不变,右边的ResNet块高宽减半,靠近输入的3*3卷积核和1*1卷积核的步幅为2

- ResNet块是从VGG过来的

- 不同的残差块

- ResNet网络:类似VGG和GoogLeNet的总体架构,有5个stage,最后有一个全局池化层,但替换成了ResNet块

1 | # 从零开始实现 |
31 深度学习硬件:CPU 和 GPU
-
提升CPU利用率
- 在计算a+b之前,需要准备数据
- 主内存->L3->L2->L1->寄存器
- 提升空间和时间的内存本地性
- 时间:重用数据使得保持它们在缓存
- 空间:按序读写数据使得可以预读取
- 如果一个矩阵是按列存储,访问一行会比访问一列要快
- CPU会一次读取64字节(缓存线)
- CPU会提前读取下一个(缓存线)
- 并行来利用所有核:超线程不一定提升性能,因为它们共享寄存器
- 在计算a+b之前,需要准备数据
-
提升GPU利用率
- 并行:使用数千个线程
- 内存本地性:缓存更小,架构更加简单
- 少用控制语句:支持有限,同步开销很大
-
CPU/GPU带宽
- 带宽受限,并且需要同步开销
- 不要频繁在CPU和GPU之间传数据
-
CPU/GPU高性能计算编程
- CPU:C++或任何高性能语言
- GPU:
- Nvidia:Cuda,编译器和驱动成熟
- OpenCL:质量取决于硬件厂商
-
QA
- 全连接层较为耗费内存和性能,且模型占用空间更大,模型的计算复杂度和模型的占用空间不成正比
w-=lr*w.grad计算前后的w的地址相同,而w=w-lr*w.grad计算后右式赋值给一个新的变量,然后赋值给变量w,地址会发生变化,不推荐使用
32 深度学习硬件:TPU和其他
- DSP:数字信号处理
- 为数字信号处理算法设计:点积,卷积,FFT
- 低功耗,高性能,比移动GPU快5X,功耗更低
- VLIW:一条指令加u四年上百次乘累加
- 编程和调试困难,编译器质量良莠不齐
- 可编程阵列(FPGA)
- 有大量可编程逻辑单元和可配置的连接
- 可配置成计算复杂函数:VHDL,Verilog
- 通常比通用硬件更高效
- 工具链质量良莠不齐
- 一次编译需要数小时
- AI ASIC
- google TPU是标志性芯片
- 能够媲美Nvidia GPU性能
- 核心是systolic array
- 计算单元(PE)阵列
- 特别适合做矩阵乘法
- 设计和制造相对简单
- google TPU是标志性芯片
33 单机多卡并行
- 单机多卡并行
- 一台机器可以安装多个GPU
- 在训练和预测时,将小批量计算切分到多个GPU上来达到加速目的
- 常用切分方案
- 数据并行:将小批量分成n块,每个GPU拿到完整参数计算一块数据的梯度(通常性能更好)
- 模型并行:将模型分成n块,每个GPU拿到一块模型计算它的前向和方向结果(通常用于模型大到单GPU放不下)
- 通道并行(数据+模型并行)
- 数据并行
- 读一个数据块
- 拿回参数
- 计算梯度
- 发出梯度
- 更新梯度
34 多GPU训练实习
34.1 从零开始实现
1 | # 多GPu训练 |
- 多GPU训练不变快的原因,GPU增加但batch_size不增加,每一次计算不能高效的使用GPU的线程,最好保证每个GPU能拿到与相同相同的batch_size,但收敛速度很有可能会变慢,精度会降低,需要提高学习率
34.2 简洁实现
1 | import torch |
- QA
- learning rate可能会导致nan问题,导致准确率震荡
35 分布式训练
- 分布式计算
- 数据放在分布式文件系统上->通过网络读取数据->多个worker<->多个参数服务器
- 其中的带宽主要受网络带宽的限制
- 计算一个小批量
- 每个计算服务器读取小批量中的一部分
- 进一步将数据切分到每个GPU上
- 每个worker从参数服务器获取模型参数
- 复制参数到每个GPU上
- 每个GPU计算梯度
- 将所有GPU上的梯度求和
- 梯度传回服务器
- 每个服务器对梯度求和并更新参数




- 模型需要有好的计算(FLOP)通信(model size)比:Inception>ResNet>AlexNet
- Inception 比 ResNet更好做并行计算
36 数据增广
- 数据增强:增加一个已有数据集,使得有更多的多样性
- 在语言里面加入各种不同的背景噪音
- 改变图片的颜色和形状
- 在线生成增强后的数据,随机生成,随后使用增强数据进行训练,可认为一种正则项
- 常用方法:
- 左右翻转
- 上下翻转(不一定都可以)
- 切割(从图像中切割一块,然后变形到固定形状,使用随机高宽比,随机大小,随机位置)
- 颜色(改变色调,饱和度,明亮度)
- github上图片增强的各种方法
1 | # 图片增广 |
37 微调(迁移学习之一)
- 微调是CV中深度学习最重要的技术,可以使用迁移学习
- 网络架构
- 一个神经网络一般可以分成两块
- 特征抽取将原始像素变成容易线性分割的特征
- 线性分类器来做分类
- 一个神经网络一般可以分成两块

- 微调
- 网络架构中,使用源数据集训练好的模型,特征抽取部分可能仍然对目标数据集做特征抽取,线性分类器部分由于标号可能变了故该部分不能使用
- 微调中的权重初始化:在目标数据集模型初始化中,特征抽取部分的权重的初始化使用源数据集模型的权重,而线性分类器使用随机初始化

- 训练:是一个目标数据集上的正常训练任务,但使用更强的正则化
- 使用更小的学习率
- 使用更小的数据迭代
- 源数据集远复杂于目标数据集,通常微调效果更好
- 重用分类器权重
- 源数据集可能也有目标数据中的部分标号
- 可以使用预训练好模型分类器中对应标号对应的向量来做初始化
- 固定一些层
- 神经网络通常学习有层次地特征表示
- 低层次的特征更加通用
- 高层次的特征则更跟数据集相关
- 可以固定底部一些层的参数,不参与更新:更强的正则
- 神经网络通常学习有层次地特征表示
1 | # 微调 |
- 最好都是从微调(fine tuning)开始进行训练,而不是从零开始训练
39 实战 Kaggle 比赛:图像分类(CIFAR-10)
1 | import collections |
40 实战 Kaggle 比赛:狗的品种识别
1 | import os |
41 物体检测和数据集
- 目标检测中:边缘框
- 一个边缘框可以通过4个数字定义:
- (左上x, 左上y, 右下x, 右下y)
- (左上x, 左上y, 宽, 高)
- 图像处理从左上到右下遍历图片
- 一个边缘框可以通过4个数字定义:
- 目标检测数据集
- 每行表示一个物体:图片文件名,物体类别,边缘框
- COCO
- 80个物体,330K图片,1.5M物体
1 | # 目标检测和边界框 |
- 数据集
1 | # 目标检测数据集 |
42 锚框

- 锚框
- 一类目标检测算法是基于锚框(目前仍为主流算法)
- 提出多个被称为锚框的区域(边缘框)
- 预测每个锚框里是否含有有关注的物体
- 如果是,预测从这个锚框到真实边缘框的偏移
- 一类目标检测算法是基于锚框(目前仍为主流算法)
- IoU交并比:用来计算两个框之间的相似度
- 0表示无重叠,1表示重合
- 是Jacquard指数的一个特殊情况
- 给定两个集合A和B:

- 赋予锚框标号(一种常用算法)
- 每个锚框是一个训练样本
- 将每个锚框,要么标注成背景,要么关联上一个真实边缘框
- 可能会产生大量的锚框,这样会导致大量的负类样本
- 流程:
- 首先选取锚框值IoU中间的最大值,随后将其对应的边缘框给锚框,并将锚框所在的行和列的元素删除,重复上述操作,选出所有边缘框对应的锚框

- 使用非极大抑制(NMS)输出
- 每个锚框预测一个边缘框
- NMS可以合并相似的预测
- 选中非背景类的最大预测值
- 去掉所有其它和它的IoU值大于 的预测
- 重复上述过程知道所有预测要么被选中,要么被去掉

- 总结
- 一类目标检测算法基于锚框来预测
- 首先生成大量锚框,并赋予标号,每个锚框作为一个样本进行训练
- 在预测时,使用NMS来去掉冗余的预测
- 一类目标检测算法基于锚框来预测
1 | %matplotlib inline |
43 树叶分类竞赛技术总结
- 相比于课程介绍的代码,主要做了下面的加强
- 数据增强,在测试时多次使用稍弱的增强然后取平均
- 使用多个模型预测,最后结果加权平均
- 训练算法和学习率
- 清理数据
- 数据方面
- 有重复图片,可以手动去除
- 图片背景较多,而且树叶没有方向性,可以做更多增强
- 跨图片增强
- Mixup:随机叠加两张图片
- CutMix:随机组合来自不同图片的块
- 模型方面
- 模型多为ResNet变种
- DenseNet,ResNeXt,ResNest,…
- EfficientNet
- 优化算法多为Adam或其变种
- 学习率一般使Cosine或者训练不同时往下调(存在相关代码)
- 模型多为ResNet变种
- AutoGluon
- 15行代码,安装加训练化花时100分钟.跳转
- 精度96%
- 可以通过定制化提升精度
- 下一版本将搜索更多的模型超参数
- AG目前仍是关注工业界上,非比赛
- 总结
- 提升精度思路:根据数据挑选增强,使用新模型,新优化算法,多个模型融合,测试时使用增强
- 在工业界应用中:
- 少使用模型融合和测试时增强,计算代价过高
- 通常固定模型超参数,而将精力主要花在提升数据质量
44 物体检测算法:R-CNN,SSD,YOLO
- R-CNN
- 使用启发式搜索算法来选择锚框
- 使用与训练模型来对每个锚框抽取特征
- 训练一个SVM来对类别分类
- 训练一个线性回归模型来预测边缘框偏移

- 兴趣区域(RoI)池化层
- 给定一个锚框,均匀分割成块,输出每块里的最大值
- 不管锚框多大,总是输出 个值

- Fast RCNN
- 上述模型每次一张图片需要抽取特征,一张图片需要抽取特征变成大量的小图片,而Fast RCNN在一整张图片上抽取特征给定锚框,在锚框内使用RoI池化,最后输出一个矩阵
- 不对每个锚框进行CNN抽取特征,而是对整张图片抽取特征
- 使用CNN对图片抽取特征
- 使用RoI池化层对每个锚框生成固定长度特征

- Faster R-CNN:使用一个区域提议网络(RPN)来替代启发式搜索来获得更好的锚框
- 使用一个神经网络来代替上述的选择性搜索算法,相当于进行一次粗糙的目标检测

- Mask R-CNN
- 如果有像素级别的标号,使用FCN来利用这些信息
- RoI池化层改为RoI align 在像素级别预测时,切分为等分,像素值为加权平均数

-
总结
- Fast/Faster RCNN持续提升性能
- Fast/Faster RCNN和Mask RCNN是在追求高精度场景下的常用算法
-
单发多框检测(SSD)
- 生成锚框
- 对每个像素,生成多个以它为中心的锚框
- 给定n个大小和个高宽比,那么生成个锚框,其大小和高宽比分别为

- SSD模型
- 一个基础网络来抽取特征,然后多个卷积层块来减半高宽
- 在每段都生成锚框:底部段来拟合小物体,顶部段来拟合大物体
- 对每个锚框预测类别和边缘框
- 总结
- SSD通过单神经网络来检测模型
- 以每个像素为中心的产生多个锚框
- 在多个段的输出上进行多尺度的检测

- YOLO模型
- SSD中锚框大量重叠,因此浪费了很多计算
- YOLO将图片均匀分成个锚框
- 每个锚框预测B个边缘框
45 SSD实现
45.1 多尺度锚框
1 | # 多尺度目标检测 |
45.2 SSD
1 | %matplotlib inline |
46 语义分割和数据集
- 语义分割将图片中的每个像素分类到对应的类别
- 应用:背景虚化,路面分割
- 实例分割:区别每一个实例,目标检测的进化版本
- 最重要的语义分割数据集之一时Pascal VOC2012
1 | %matplotlib inline |
47 转置卷积
47.1 转置卷积

- 转置卷积(在语义分割中常用)
- 卷积不会增大输入的高宽,通常要么不变,要么减半
- 转置卷积则可以用来增大输入高宽
- 为什么称为转置
- 对于卷积,可以对W构造一个V使得卷积等价于对应的向量版本
- 转置卷积则等价于
- 如果卷积将输入从变成了,同样超参数下它将变成
47.2 转置卷积也是一种卷积
- 转置卷积是一种卷积
- 将输入和核进行了重新排列
- 同卷积一般是做下采样不同,通常用作上采样
- 如果卷积将输入从变成了,同样超参数下它将变成
- 重新排列输入和核
- 当填充为0步幅为1时
- 将输入填充k-1(k是核窗口)
- 将核矩阵上下,左右翻转
- 然后正常卷积(填充0,步幅1)
- 当填充为p步幅为1时
- 将输入填充k-p-1(k是核窗口)
- 将核矩阵上下,左右翻转
- 然后正常卷积(填充0,步幅1)
- 当填充为p步幅为s时
- 在行和列之间插入s-1行或列
- 将输入填充k-p-1(k是核窗口)
- 将核矩阵上下,左右翻转
- 然后正常卷积(填充0,步幅1)
- 当填充为0步幅为1时

- 形状换算
- 输入高(宽)为n,核k,填充p,步幅s
- 转置卷积:
- 卷积:
- 当可以整除,则 与转置卷积公式互为逆公式,当卷积在不能整除时,转置卷积时的为能整除的最小的
- 如果让高宽成倍增加,那么
- 同反卷积的关系
- 数学上的反卷积是卷积的逆运算:若则
- 反卷积与转置卷积并相同,反卷积很少用在深度学习中,反卷积神经网络指用了转置卷积的神经网络
1 | # 转置卷积的实现 |
- 转置卷积不是进行上采样,可以对卷积核的参数进行训练
48 全连接卷积神经网络 FCN

- 全连接神经网络(FCN)
- 是用深度神经网络来做语义分割的奠基性工作
- 用转置卷积层替换CNN最后的全连接层,从而可以实现每个像素的预测
- 将CNN中的最后的全连接层和全局池化层去除
- 1*1卷积层合并通道,降低通道数,减小计算量
- 转置卷积层,将图片扩大

1 | %matplotlib inline |
49 样式迁移
- 样式迁移:将样式图片中的样式迁移到内容图片上,得到合成图片(类似滤镜)
- 基于CNN的样式迁移
- 内容图片计算其CNN的特征的内容损失
- 样式图片计算其CNN的特征的样式损失
- 训练的生成的图片的CNN的特征的内容损失和样式损失,同时计算图像上的噪点
- 训练内容为图片,不是损失函数

1 | %matplotlib inline |
- VGG系列对抽取特征效果较好
51 序列模型
- 序列数据:实际中很多数据是有时序结构
- 统计工具
- 在时间观察到,那么得到T个不独立的随机变量
- 使用条件概率展开:

- 序列模型
-- 对条件概率建模:
- 对见过的数据建模,也称自回归模型
- 对条件概率建模:
- 方案A——马尔可夫假设
- 假设当前数据只跟个数据点相关:
- 方案B——潜变量模型
- 引入潜变量来表示过去信息:

1 | # 使用正弦函数和一些可加性噪声来生成序列数据 |
52 文本预处理
1 | # 文本预处理 |
53 语言模型
- 语言模型
- 给定文本序列,语言模型的目的是估计联合概率
- 应用:
- 做与训练模型(BERT,GPT-3)
- 生成文本,给定前面几个词,不断地生成后续文本
- 判断多个序列中哪个更常见
- 使用计数来建模
- 假定序列长度为2,预测
- n是总词数,是单个单词和连续单词对的出现次数
- 很容易扩展到长为3的情况
- 假定序列长度为2,预测
- N元语法
- 当序列很长时,因为文本量不够大,很可能
- 使用马尔可夫假设缓解这个问题
- 一元语法:
- 二元语法:
- 三元语法:
1 | import random |
54 循环神经网络 RNN
- 潜变量自回归模型:使用潜变量总结过去信息

- 循环神经网络
- 更新隐藏状态:
- 输出


- 困惑度
- 衡量一个语言模型的好坏可以用平均交叉熵:,p时语言模型预测概率,是真实词
- 历史原因NLP使用困惑度来衡量,是平均每次可能选项:1表示完美,无穷大是最差情况
- 梯度裁剪
- 迭代中计算这T个时间步上的梯度,在反向传播过程中产生长度为的矩阵乘法链,导致数值不稳定
- 梯度剪裁能有效预防梯度爆炸,如果梯度长度超过,那拖影回长度
55 循环神经网络 RNN 的实现
55.1 从零开始实现
1 | %matplotlib inline |
55.2 简洁实现
1 | import torch |
56 门控循环单元(GRU)
- 关注一个序列
- 不是每个观察值都是同等重要
- 想只记住相关的观察需要
- 能关注的机制(更新门)
- 能遗忘的机制(重置门)

- 候选隐藏状态:

- 隐状态:


1 | import torch |
57 长短期记忆网络(LSTM)
- 长短期记忆网络
- 忘记门:将值朝0减少,\boldsymbol{F}_t &=\sigma\left(\boldsymbol{X}_t \boldsymbol{W}_{x f}+\boldsymbol{H}_{t-1} \boldsymbol{W}_{h f}+\boldsymbol{b}_f\right)
- 输入门:决定不是忽略掉输入数据,\begin{aligned} \boldsymbol{I}_t &=\sigma\left(\boldsymbol{X}_t \boldsymbol{W}_{x i}+\boldsymbol{H}_{t-1} \boldsymbol{W}_{h i}+\boldsymbol{b}_i\right)
- 输出门:决定是不是使用隐状态,\boldsymbol{O}_t &=\sigma\left(\boldsymbol{X}_t \boldsymbol{W}_{x \jmath}+\boldsymbol{H}_{t-1} \boldsymbol{W}_{h o}+\boldsymbol{b}_o\right) \end{aligned}

- 候选记忆单元:

- 记忆单元:

- 隐藏状态:


1 | import torch |
58 深层循环神经网络
- 更深
- 浅RNN:输入,隐层,输出
- 深RNN:输入,隐层,隐层,…,输出
-,,,,

1 | # 简洁实现 |
59 双向循环神经网络
- 双向RNN
- 一个前向RNN隐层
- 一个方向RNN隐层
- 合并两个隐状态得到输出
-

- 总结
- 双向循环神经网络通过反向更新的隐藏层来利用方向时间信息
- 通常用来对序列抽取特征,填空,而不是预测未来
1 | # 双向神经网络的错误应用 |
60 机器翻译数据集
1 | import os |
61 编码器-解码器架构
- 重新考察CNN
- 编码器:将输入编程成中间表达形式(特征)
- 将文本表示成向量
- 解码器:将中间表示解码成输出
- 向量表示成输出
- 编码器:将输入编程成中间表达形式(特征)
- 编码器-解码器架构
- 一个模型被分为两块:编码器处理输出,解码器生成输出



1 | from torch import nn |
62 序列到序列学习(seq2seq)
- Seq2Seq
- 编码器是一个RNN,读取输入句子:可以是双向的
- 双向RNN经常用在解码器中
- 解码器使用另一个RNN来输出
- 上一个时刻的翻译输出传递给下一时刻,同时结合此时刻的输入进行输出
- 编码器是一个RNN,读取输入句子:可以是双向的

- 编码器-解码器细节
- 编码器是没有输出的RNN
- 编码器最后时间步的隐状态用作解码器的初始隐状态
- 编码器将最后一层的RNN,最后时间步的隐状态结合解码器的Embedding用作解码器的初始隐状态

- 训练:训练时解码器使用目标句子作为输入
- 推理时使用上一时刻的输出作为输入

- 衡量生成序列的好坏
-是预测中所有n-gram的精度- 标签序列 A B C D E F 和预测序列 A B B C D ,有
- BLEU定义:,该值越大越好
-\frac{\operatorname{len}_{\text {label }}为惩罚过短的预测,长匹配有高权重
1 | import collections |
63 束搜索
- 贪心搜索
- Seq2Seq中使用了贪心搜索来预测序列:将当时时刻预测概率最大的词输出
- 但贪心很可能不是最优的:当前选取的在全局时间上可能不是最优的

- 穷举搜索
- 最优算法:对所有可能的序列,计算它的概率,然后选取最好的那个
- 如果输出字典大小为n, 序列最长为T,那么需要考虑个序列:计算上不可行
- 束搜索
- 做法
- 保存最好的k个候选
- 在每个时刻,对每个候选新加一项(n种可能),在个选项中选出最好的k个
- 时间复杂度
- 每个候选的最终分数:
- 通常:
- 做法

64 注意力机制
- 注意力机制
- 卷积,全连接,池化层都只考虑不随意线索
- 注意力机制则显示的考虑随意线索
- 随意线索被称之为查询(query)
- 每个输入是一个值(value)和不随意线索(key)的对
- 通过注意力池化层来有偏向性的选项选择某些输入
- 非参注意力池化层
- 给定数据
- 平均池化是最简单的方案:
- 更好的方案是Nadaraya-Watson核回归:
- K是核为衡量距离的量,为value,为key
- Nadaraya-Watson核回归
- 使用高斯核:
- 则:
- 参数化的注意力机制
- 在之前基础上引入可学习的 w :
1 | # 注意力汇集Nadaraya-Watson核回归 |
65 注意力分数
- 回顾:为注意力权重,为注意力分数

- 拓展到高维度
- 假设query, m对key-value
- 注意力池化层:
-
-- 此处a为注意力分数
- Additive Attention(加性注意力)
- 可学参数:
- 等价于将query和key合并起来后放入到一个隐藏大小为h输出大小为1的单隐藏层MLP
- 好处:key和value可以为任意长度
- scaled dot-product attention
- 如果query和key都是同样的长度,那么可以
- 向量化版本
-- 注意力分数:
- 注意力池化:
- 总结
- 注意力分数是query和key的相似度,注意力权重是分数的softmax的结果
- 两种常见的分数计算
- 将query和key合并起来放入一个单输出单隐层的MLP
- 直接将query和key做内积
1 | import math |
66 使用注意力机制的seq2seq
67 自注意力
68 Transformer
69 BERT预训练
70 BERT微调
71 优化算法
- 动量法:使用平滑过的梯度对权重更新
-
-- 梯度平滑:
-常见取值 - 最简单的算法中都实现了该方法,
pytorch中SGD中具有momentum选项即为动量法
- 梯度平滑:
- Adam
- 记录通常
- 展开
- 因为, 所以权重和为 1
- 由于, 且, 修正
- 类似记录, 通常, 且修正
- 计算重新调整后的梯度
- 最后更新