深度学习学习笔记
张量
张量(Tensor)表示一个由数值组成的数组,这个数组可能有多个维度:
- 具有一个轴的张量对应数学上的向量(vector);
- 具有两个轴的张量对应数学上的矩阵(matrix);
- 具有两个轴以上的张量没有特殊的数学名称。
运算符
不改变形状:
- 加、减、乘($\odot$)、除、求幂(二元)(每个元素独立计算)
- 求自然对数幂(一元)
- 逻辑运算,生成新的 bool 类型张量
改变形状:
- 向量点积($\mathbf{x}^\top \mathbf{y} = \sum_{i=1}^{d} x_i y_i$)
- 矩阵乘法
- 转置($\mathbf{x}^\top$)
- 连结(concatenate):3x4 和 3x5 按照列轴首尾相连,变为 3x9。
- 整体求和,变为单元素
- 广播(扩展长度为 1 的轴)
索引
和 python 数组操作差不多,表示范围时左闭右开,用:
分隔。例如,[0:2, :]
访问第 1 行和第 2 行,其中“:”代表沿轴 1(列)的所有元素。
数据预处理
pandas
线性代数
标量
只有一个元素的张量(0 维),相当于一个点
向量
只有一个轴的张量
维度
张量的轴数量
降维
- 求和,降为标量
- 沿一个轴降维求和,减小一维
- 不降维求和,对应的轴保留维度,即使只剩一个元素
矩阵
只有两个轴的张量
$\mathbf{A} \in \mathbb{R}^{m \times n}$表示(m,n)的矩阵
转置
交换矩阵的行和列
矩阵乘法
可以将第二个矩阵看成是列向量的组合,矩阵和列向量的乘法就是每行向量和列向量的“点积”,然后将结果“连结”
所以要求第一个矩阵的列数必须和第二个矩阵的行数相同。
范数
表示向量的大小
L1 范数:
\[\|\mathbf{x}\|_1 = \sum_{i=1}^n \left|x_i \right|.\]元素绝对值之和
L2 范数:
\[\|\mathbf{x}\|_2 = \sqrt{\sum_{i=1}^n x_i^2},\]元素平方和的平方根,类似于三角不等式求边长和
扩展为 Lp 范数:
\[\|\mathbf{x}\|_p = \left(\sum_{i=1}^n \left|x_i \right|^p \right)^{1/p}.\]矩阵也有类似于向量的范数表示大小的方式
微积分
微分
- 导数:\(f'(x) = \lim_{h \rightarrow 0} \frac{f(x+h) - f(x)}{h}.\)
- 微分运算符:$\frac{d}{dx}$和$D$
- 常见表达式求微分,如幂律
- 法则
偏导数
在多元函数中只对一个变量求导(其余变量视为常数):\(\frac{\partial f}{\partial x_i} = \lim_{h \rightarrow 0} \frac{f(x_1, \ldots, x_{i-1}, x_i+h, x_{i+1}, \ldots, x_n) - f(x_1, \ldots, x_i, \ldots, x_n)}{h}.\)
注意偏导的符号和导数不同
梯度
表示多元函数在某一点上沿每个方向上升或下降的速度(偏导数)。表示为一个向量,每个元素代表一个方向。
函数$f(\mathbf{x})$相对于$\mathbf{x}$($\mathbf{x}=[x_1,x_2,\ldots,x_n]^\top$,转置表示从行向量变为列向量,可以理解为就是函数的参数组)的梯度是一个包含$n$个偏导数的向量:
\[\nabla_{\mathbf{x}} f(\mathbf{x}) = \bigg[\frac{\partial f(\mathbf{x})}{\partial x_1}, \frac{\partial f(\mathbf{x})}{\partial x_2}, \ldots, \frac{\partial f(\mathbf{x})}{\partial x_n}\bigg]^\top,\]梯度也是列向量
关于书中微分多元函数的一些规则,需要参考线性代数中的矩阵-向量积。对于$(m,n)$形状的矩阵 A,乘$x$(列向量,长度为 n),得到的积的表达式为$\nabla_{\mathbf{x}} \mathbf{A} \mathbf{x}$。同理,$\nabla_{\mathbf{x}} \mathbf{x}^\top \mathbf{A}$,表示 x 的转置(变为行向量,长度还是 n)乘矩阵 A 得到的积。
双竖线表示 L2 范数:
$|x|^2$表示$f(x)=x_1^2+x_2^2+…+x_n^2$
梯度为:
$\nabla f(x)=[2x_1+2x_2+…+2x_n]^\top=2[x_1,…,x_n]^\top=2x$
$|\mathbf{x}|_p$表示矩阵的 Frobenius 范数和向量的 L2 范数类似。
链式法则
用于微分复合函数,示例见https://www.cnblogs.com/bigmonkey/p/8350943.html
链式法则可以用于反向传播计算梯度。
自动微分
- 符号微分:直接转换,如 sin(x) 的导数是 cos(x),但也只有特定的表达式能这样转换。
- 数值微分:直接使用微分的方式计算$\frac{dy}{dx}=\frac{f(x+2^{-32})-f(x)}{2^{-32}}$(float32 精度下最小值为$2^{-32}$)。精度越大(如 float64),计算越慢,且占用的内存也越大,但结果也更准确。但这种方式的精度还是无法满足要求,所以在深度学习领域一般只是用来验证梯度计算是否正确。
- 自动微分
梯度保存
pytorch 的 requires_grad 参数用于指定张量是否需要计算梯度(为创建的 tensor 对象预留存储梯度的固定内存区域),而不是每次都分配内存保存计算结果。
梯度下降
在某一点上找下降最快的方向(偏导数最大的方向的反方向)。
反向传播
计算梯度的算法
使用 backward()函数可以对特定的点求其梯度。(注意并不是像我们做题一样推导出导数表达式,而是只对一个点求偏导数)
非标量反向传播
y 也是向量时,不能直接使用 backward()。一般将 y 转为向量的总和,再求梯度
概率
基本概率论
样本(specimen)是观测或调查的一部分个体,总体是研究对象的全部。
选取样本的过程叫做抽样
1
2
3
4
5
# 定义一个向量,长度为6,每个元素表示概率
fair_probs = torch.ones([6]) / 6
# 采样10次,得到采样结果(10个值,样本空间:{0,1,2,3,4,5}),记录索引i在采样结果中出现的次数。比如采样10次,输出结果中的索引0处就是0出现的次数
multinomial.Multinomial(10, fair_probs).sample()
# tensor([0., 1., 2., 4., 2., 1.])
联合概率
多个条件同时发生的概率
贝叶斯定理
A 和 B 同时发生的概率等于 A 发生的概率乘以 A 已经发生时 B 发生的条件概率:
\[P(A, B) = P(B \mid A) P(A)\]同理:
\[P(A, B) = P(A \mid B) P(B)\]得到贝叶斯定理:
\[P(A \mid B) = \frac{P(B \mid A) P(A)}{P(B)}.\]期望
类似加权平均
方差
即平方误差,表示随机变量与其期望的偏差程度。标准差是方差的平方根
线性回归
回归用于预测数值,和分类不同。
目标是在线性模型中找到一组权重向量 w 和偏置 b,让预测值和实际值的偏差尽可能小(损失函数最小)。
损失函数
评估模型的计算值与训练样本中的实际值的差异,训练样本中的实际值是可靠的,所以以它为参考目标,调整模型参数,让模型拟合于训练样本。(前提是训练样本本身确实是可以用模型表示,如果训练样本是完全随机毫无规律的,那也拟合不了)
解析解
见https://zhuanlan.zhihu.com/p/95814925
多元线性回归模型(其中假设 $y$ 为实际向量,$\hat y$ 为预测向量,$\mathbf{X}$为矩阵):
\[\mathbf{\hat y}=\mathbf{X}\mathbf{w}+b\]损失函数 $v$ 可以表示为:
\[v=\|\mathbf{\hat y}-\mathbf{y}\|^2=\|\mathbf{X}\mathbf{w}+b-\mathbf{y}\|^2\]尝试将 b 和 w 合并为 s。对于测试集(一个 y 向量和一个 X 矩阵)中的其中一个结果相关的预测量$\hat y^{(i)}$以及对应参变量$\mathbf{x}$向量($i$为列号):
\[\hat y^{(i)}=w_1x_1^{(i)}+...+w_nx_n^{(i)}+b=s_1x_1^{(i)}+...+s_nx_n^{(i)}+s_{n+1}\]相当于我们将 x 向量视为新的 X 向量,相当于扩充了测试集中的参变量,多加了一列值为 1 的列:
\[\mathbf{X^{(i)}}=[x_1^{(i)},...,x_n^{(i)},1]\]将 w 向量视为新的 s 向量,多加了一个值为 1 的元素:
\[\mathbf{s}=[w_1,...,w_n,b]^\top\]损失函数变为:
\[v=\|\mathbf{\hat y}-\mathbf{y}\|^2=\|\mathbf{X}\mathbf{s}-\mathbf{y}\|^2\]要求损失最小,就是求 $v$ 最小的情况,当然 $v = 0$ 就是最小的,但较难求解,所以通过求导的方式,去求谷底(导数为0),求$\frac{dv}{ds}=0$时的 s 向量:
\[\mathbf{s} = (\mathbf X^\top \mathbf X)^{-1}\mathbf X^\top \mathbf{y}.\]随机梯度下降
要让损失函数变小,直接方式就是算损失函数关于 w 和 b 的导数(梯度),调整变量(损失函数的变量是 w 权重和 b,而不是 x,y)往偏导数最大的反方向(负梯度)移动。
线性模型比较简单,对其损失函数可以直接推导解析解,甚至不需要训练数据。现在大多数模型都不是线性的(反而还要去线性化),所以梯度下降是更为普遍的做法。
每一个可以移动的方向都由模型参数以及训练样本中的每组 x(训练样本的变量)和 y(训练样本的结果)向量确定,但计算所有方向太慢了,一般每次抽取训练样本中的一部分的 x 和 y,称为小批量随机梯度下降。
极大似然
一般来说线性模型过于理想化,实际的线性模型可能也不是完全线性的,会有一些误差,我们以正态分布误差为例:
\[y = \mathbf{w}^\top \mathbf{x} + b + \epsilon\]可以视为:
\[y = \epsilon\] \[\epsilon \sim \mathcal{N}(\mathbf{w}^\top \mathbf{x} + b, \sigma^2)\]对特定 x 时 y 的似然:
\[P(y \mid \mathbf{x}) = \frac{1}{\sqrt{2 \pi \sigma^2}} \exp\left(-\frac{1}{2 \sigma^2} (y - \mathbf{w}^\top \mathbf{x} - b)^2\right).\]这个表达式中$\mu$就是$\mathbf{w}^\top \mathbf{x} + b$
y 向量的整体匹配概率可以由每个 y 似然连乘得到
极大似然估计和最小化均方误差得到的结果是相同的,都能找到损失最小点。
softmax 回归
softmax 回归有多个输出。在分类问题中,每个输出都表示对应类别与输入的相似度。
如:
- 鸡:0.9
- 狗:0.03
- 鸭:0.3
softmax 就是将相似度转化为分类概率(区别就是分布概率的总和总是为1):
- 鸡:0.9/(0.9+0.03+0.3)=0.73
是鸡的概率为 73%
由于有多个输出,在全连接的情况下,参数个数为单节点输入数(w 个数)x 总节点数(p)
对数似然
对 softmax 求损失最小的方法也是求最大似然。
损失函数称为交叉熵损失
信息论
softmax 输出信息的熵(熵的意思是包含的信息量)可以用H[P]
(香农熵)表示。比如上面鸡狗鸭的 softmax 输出概率。
一般来说这个信息的熵就是所有概率相加,就是 1,但是香农熵增加了一个$-\log{P(j)}$,认为概率较低的事件可能让人更惊异(更多信息量)。
所以使用最大似然从信息论的角度看并不完善,我们还要让香农熵中的惊异程度也变小,也就是让香农熵最小。
多层感知机
隐藏层
输入为$\mathbf{X} \in \mathbb{R}^{n \times d}$,表示 n 个样本,每个样本 d 个特征。一层有 h 个输入单元。每个单元权重参数量为 d,与样本特征相同,也就是一层共有$d \times h$个权重参数(偏置 b 也别忘了)。假设单个单元输出的特征个数为 q,该层的输出为$h \times q$。对于分类问题最后要通过输出层(g 个单元)将输出限制为 g 个标量,再通过 softmax 转为每个类别的概率。
注意不管隐藏层有多少,都会退化成一个线性模型,所以必须引入去线性化(激活函数)
过拟合与欠拟合
将数据集分为三份:训练集、验证集、测试集
训练集用于调整模型参数,验证集用于训练过程中识别模型是否过拟合(或欠拟合),测试集用于测试模型的最终效果。
过拟合:在验证集上表现较差,模型泛化能力不足,原因是自由度过高,比如参数量过大、权重取值范围过大,还有样本数量过少。
欠拟合:损失函数无法收敛,模型表达能力不足,可以考虑增加参数量提升复杂度
正则化
为了限制权重取值范围,防止过拟合,使用正则化让所有权重更趋向于 0。当然趋向于任何确定的值都行,比如 1,只是 0 会比较简单。因为 0 正好分割了正数和负数,计算距离时直接使用绝对值,比较简单。
为什么权重取值范围过大会导致过拟合?如果某几个权重很大,远大于其他权重,那么这些权重对应的特征会大幅影响输出结果,导致整个模型偏向于这些权重,可能会偏离正确结果。
正则化就是将参数与 0 的距离也加入损失函数,作为惩罚机制,尽可能使参数趋向于 0。
使用 L2 正则而不是 L1 正则,是为了让通过平方来放大对较大的权重的惩罚,比如[0.1,0.01,3],L1 范数平方为 3.11,L2 范数平方为 9.010001,放大了 3 这个异常值的影响,迫使第三个权重需要再降低,然后第一和第二权重可以适当提高。所以 L2 正则让各个权重更平均,也就是让模型更平滑,单个特征对整个模型的输出影响较小。而 L1 正则让权重整体更趋于 0,但并不一定平滑。
暂退法(Dropout)
用于减少过拟合现象。
在每轮训练中随机抛弃部分中间节点,让结果不过度依赖任何一个节点。注意删除中间节点的同时需要提高其他节点的特征,保证输出结果的数值大小和原来的相似。因为抛弃节点后下一层的输入会少几个特征,需要填补。
比如某一层抛弃 1/2 的节点(输入特征置为 0),则其他节点的输入特征都要乘 2。
前向、反向传播
前向传播
也可以称为推理过程,就是每层的各单元根据输入和自身权重偏置计算结果,并进行一些转换如激活函数、softmax 函数等。得到结果作为下一层的输入,直到得到最终结果。
反向传播
可见文章深度学习-反向传播详解
假设 Z 为输出结果和期望结果的差值(误差),Y 为倒数第一层参数,X 为倒数第二层参数。
我们希望知道 X 对于结果 Z 的影响,也就是倒数第二层的影响,这样可以针对性的调整 X 来得到更正确的结果,方法就是求 Z 对于 X 的偏导数。这个过程就是反向传播,也就是所谓的训练。
根据链式法则,Z 对 X 的偏导数可以由 Z 对 Y 的偏导数推得:
\[\frac{\partial \mathsf{Z}}{\partial \mathsf{X}} = \frac{\partial \mathsf{Z}}{\partial \mathsf{Y}}\frac{\partial \mathsf{Y}}{\partial \mathsf{X}}\]可以看出倒数第二层的偏导数的计算可以利用倒数第一层的偏导数,同理推出计算倒数第三层的梯度时也可以利用这个结果,直到计算完所有层,这就是反向
的意思。
一般训练时,每次得到结果后都会计算所有层的参数,并应用梯度下降算法更新所有参数。
反向传播优化
在前向传播过程中,Z 对 Y 以及 Y 对 X 的导数都已经计算过了,可以选择保留下来。这样在反向传播计算 Z 对 X 导数时可以直接使用。
这就导致相比与单纯推理,训练会更耗费内存。
稳定性
梯度爆炸
梯度过大,参数更新过快,导致难以收敛
梯度消失
梯度过小,参数更新过慢,收敛速度慢。
sigmoid 函数是导致梯度消失的原因之一。在输入很大或很小时导数几乎变 0。结合之前的反向传播链式法则,反向传播过程中如果一个梯度变 0,后续计算的和它相关的梯度就全变 0 了。
而 ReLU 的导数恒为 1,比较稳定。(输入小于 0 时,导数为 0,但输出也为 0,也就是该节点已经失效了,后续输出和该节点没关系,就算梯度消失影响也不大。)
(所以本书之前的例子对权重的初始化都用了均值为 0,方差为 0.01 的正态分布生成)
参数初始化也是需要研究的一门学科。目前深度学习框架都会自动生成,一般不考虑这些。