文章

深度学习学习笔记

张量

张量(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 的正态分布生成)

参数初始化也是需要研究的一门学科。目前深度学习框架都会自动生成,一般不考虑这些。

参考

本文由作者按照 CC BY 4.0 进行授权

© Kai. 保留部分权利。

浙ICP备20006745号-2,本站由 Jekyll 生成,采用 Chirpy 主题。