机器学习基础

Pasted%20image%2020250409171949.png
Published on
/
24 mins read
/
––– views

机器学习基础

数学基础

标量函数关于向量的导数

在机器学习中,标量函数对向量求导数的结果是一个梯度向量。梯度向量包含了函数对向量中每个分量的偏导数。

梯度的定义

梯度 bz\nabla_{\mathbf{b}} z 实际上是由所有偏导数组成的向量:

bz=zb=[zb1zb2zb3]\nabla_{\mathbf{b}} z = \frac{\partial z}{\partial \mathbf{b}} = \begin{bmatrix} \frac{\partial z}{\partial b_1} \\ \frac{\partial z}{\partial b_2} \\ \frac{\partial z}{\partial b_3} \end{bmatrix}

实例

我们展开:

z=bb=i=1nbi2z = \mathbf{b}^\top \mathbf{b} = \sum_{i=1}^n b_i^2

对每个分量 bib_i 求导:zbi=2bi\frac{\partial z}{\partial b_i} = 2b_i

所以整体梯度就是:

bz=[zb1zb2zbn]=2b\nabla_{\mathbf{b}} z = \begin{bmatrix} \frac{\partial z}{\partial b_1} \\ \frac{\partial z}{\partial b_2} \\ \vdots \\ \frac{\partial z}{\partial b_n} \end{bmatrix} = 2\mathbf{b}

b = torch.tensor([1.0, 2.0, 3.0], requires_grad=True)
print(f"向量 b = {b}")
print(f"b 的维度: {b.shape}")

# 计算 z = ||b||² = b^T b
z = torch.dot(b, b)  # 这是一个标量
print(f"z = ||b||² = {z}")
print(f"z 的维度: {z.shape} (标量)")

# 计算梯度 
z.backward()
print(f"∂z/∂b = {b.grad}")
print(f"梯度的维度: {b.grad.shape}")
# 梯度应该是 2b

ML Framework

target: looking for function

  1. function with unknown
  2. define loss
  3. optimization

具体可以分为 5 步:

  1. 读取数据:分 batch
  2. 初始化参数:给出事的权重 w 偏置 b
  3. 前向传播:计算出预测值
  4. 计算损失
  5. 反向传播,更新参数

function with unknown

Hard Sigmoid 是常见的目标 function。

  • 可以用 Sigmoid Function 逼近 Hard Sigmoid

多个 sigmoid 合成 red curve

550

最终的 function with unknown

550

loss

550

最常见的损失函数是平方误差 square error:预测值与实际值的差的平方。

optimize

不断更新 gradient

550

总的数据集会分成很多 batch:

  • 遍历所有batch称为一次epoch
  • update指更新一次batch

550

more layers

可以把模型进一步改成多层:

每一层都是一个 Sigmoid 或 ReLU 的 function,称为神经元(neuron),很多的神经元称为神经网络 (neural network)。

神经网络不是新的技术,80、90 年代就已经用过了,后来为了要重振神经网络的雄风,所以需要新的名字。每一层,称为隐藏层(hidden layer),很多的隐藏层就“深”,这套技术称为深度学习

但是层数不一定越深越好,可能会出现过拟合 overfitting 的问题:

常见的机器学习问题

  1. 监督学习
    1. 回归
    2. 分类
    3. 标记问题
    4. 搜索
    5. 推荐系统
    6. 序列学习
  2. 无监督学习
    1. 聚类 clustering :没有标签的情况下,给数据分类/聚类
    2. 主成分分析 principal component analysis:找到少量的参数来准确地捕捉数据的线性相关属性,比如,一个球的运动轨迹可以用球的速度、直径和质量来描述。
    3. 因果关系 causality 和 概率图模型 probabilistic graphical models 问题:描述观察到的许多数据的根本原因。
    4. 生成对抗性网络 generative adversarial networks :为我们提供一种合成数据的方法,甚至像图像和音频这样复杂的非结构化数据。
  3. 离线学习:与环境交互学习
  4. 强化学习

是否监督的核心区别是有无标签数据,但这背后反映的是更本质的差异。

  • 监督学习有标签,模型学的是输入→输出的映射关系
    • 目标明确,e.g. 给你一张图,告诉你是猫还是狗,模型就去学这个判断规则
  • 无监督学习没有标签,模型只有输入数据
    • 目标是自己发现数据内部的结构、规律或分布,比如聚类、降维、密度估计

Model Bias

假设模型过于简单,一个有未知参数的函数代 θ1 得到一个函数 fθ1(x),同理可得到另一个函数 fθ2(x),把所有的函数集合起来得到一个函数的集合。但是该函数的集合太小了,没有包含任何一个函数,可以让损失变低的函数不在模 型可以描述的范围内。在这种情况下,就算找出了一个 θ∗ ,虽然它是这些蓝色的函数里面最好的一个,但损失还是不够低。

Optimization

一般只会用到梯度下降进行优化,这种优化的方法很多的问题。比如(a)可能会卡在局部最小值的地方,无法找到一个真的可以让损失很低的参数。

Overfitting

过拟合的表现为:训练数据上面的损失小,测试数据上的损失大。

例子:模型过于灵活

在这 3 个蓝点(训练数据)上面,要让损失低,所以模型的这个曲线会通过这 3 个点,但是其它没有训练集做为限制的地方,因为它的灵活性很大,所以模型可以变成各式各样的函数,没有给它数据做为训练,可以产生各式各样奇怪的结果(“随意”)。

最终,表现为训练数据loss为0,测试数据loss很大。

可以用数据增强和限制模型的方法,降低 overfitting

data augmentation

根据问题的理解创造出新的数据。举个例子,在做图像识别的时候,常做的 一个招式是,假设训练集里面有某一张图片,把它左右翻转,或者是把它其中一块截出来放大 等等。

limitation

限制模型,让模型不要有过大的灵活性。

  • 减少模型参数:对于神经网络,可以减少每层的参数
  • 减少数据特征值
  • 早停(early stopping)、正则化(regularization)和丢弃法(dropout method)

Cross Validation

交叉验证 Cross Validation 是为了避免在测试集上面过拟合。

k 折交叉验证就是先把训练集切成 k 等份。在这个例子,训练集被切成 3 等份,切完以后,拿其中一份当作验证集,另外两份当训练集。

  • 这件事情要重复 3 次:第一份第 2 份当训练,第 3 份当验证;第一份第 3 份当训练,第 2 份当验证;第 1 份当验证,第 2 份第 3 份当训练。

Mismatch

不匹配,简单理解就是一种反常的情况发生了。

而且,增加数据也不能让模型做得更好,到底有没有mismatch,要看人对数据本身的理解

Local Minima and Saddle Point

  1. local minima 是局部极小值
  2. saddle point 是鞍点,不是极小,可以向其他方向 escape

这两者一般统称为 critical point 临界点。

determining the types of critical point

损失函数可由于泰勒级数近似为:

L(θ)L(θ)+(θθ)Tg+12(θθ)TH(θθ)L({\theta}) \approx L\left({\theta}'\right) + \left({\theta} - {\theta}'\right)^{\mathrm{T}} {g} + \frac{1}{2} \left({\theta} - {\theta}'\right)^{\mathrm{T}} {H} \left({\theta} - {\theta}'\right)

其中,H 里面放的是 L 的二次微分,可以通过 12(θθ)TH(θθ)\frac{1}{2} \left({\theta} - {\theta}'\right)^{\mathrm{T}} {H} \left({\theta} - {\theta}'\right) 的正负判断是否是极小值。

batch size

大的批量往往在训练的时候结果比较差,小的批量优化的结果是比较好的,这个是优化的问题。

local minima 也分好坏,如下图:

  • flat minima 是好的
  • sharp minima 是坏的

评价 local minima 好坏的根本在于:

  • train 和 test 的 loss 一般有一些差别
  • 两者的 flat minima 往往很接近
  • 但是 sharp minima 很有可能会有很大差别

批量大小对梯度下降法的影响的直观理解:

大的批量大小会让我们倾向于走到“峡谷”里面,而小的批量大小倾向于让我们走到“盆地” 里面。小的批量有很多的损失,其更新方向比较随机,其每次更新的方向都不太一样。即使 “峡谷”非常窄,它也可以跳出去,之后如果有一个非常宽的“盆地”,它才会停下来。

momentum method

  • 动量法是另一种可以对抗 local minima 和 saddle point 的方法

假设误差表面就是真正的斜坡,参数是一个球,把球从斜坡上滚下来,如果使用梯度下降,球走到或鞍点就停住了。

但是在物理的世界里,一个球如果从高处滚下来,就算滚到鞍点或局部最小值点,因为惯性的关系它还是会继续往前走。如果球的动量足够大,其甚至翻过小坡继续往前走,它并不一定会被鞍点或局部最小值点卡住,如果将其应用到梯度下降中,这就是动量。

引入动量后,可以从两个角度来理解动量法:

  • 一个角度是动量是梯度的反方向加上前一次移动的方向
  • 另外一个角度是当加上动量的时候,更新的方向不是只考虑现在的梯度,而是考虑过去所有梯度的总和

  • 其中 η 是学习率, λ 是前一个方向的权重参数,是需要调的。

Adaptive Learning Rate

不同学习率:

  • 图中误差表面的最低点在叉号处
  • 学习率太大会导致训练时参数不断震荡,如图a
  • 学习率变小可以解决震荡的问题,但是在左拐以后,BC 段的坡度已经非常平坦了(指梯度很小),这种小的学习率无法再让参数前进到目标位置,如图b

AdaGrad

AdaGrad(Adaptive Gradient)是典型的自适应学习率方法,其能够根据梯度大小自动调整学习率。AdaGrad 可以做到梯度比较大的时候,学习率就减小,梯度比较小的时候,学习率就放大。

原始的梯度下降更新某个参数 θ 的过程为(学习率固定):

θt+1iθtiηgti\boldsymbol{\theta}_{t + 1}^i \leftarrow \boldsymbol{\theta}_{t}^i - \eta \boldsymbol{g}_{t}^i

AdaGrad 的更新过程,学习率不再固定: θt+1iθtiησtigti\boldsymbol{\theta}_{t + 1}^i \leftarrow \boldsymbol{\theta}_{t}^i - \frac{\eta}{\sigma_{t}^i} \boldsymbol{g}_{t}^i

新引入的符号的说明,要点就是学习率就变得参数相关(parameter dependent):

最终自适应的效果如下:

RMSProp

RMSprop(Root Mean Squared propagation)是 Geoffrey Hinton 在 Coursera 上的深度学习的课程中提到的,没有论文,属于黑科技了...

RMSProp 第一步跟 Adagrad 的方法是相同的,即

σ0i=(g0i)2=g0i\sigma_{0}^{i} = \sqrt{\left(g_{0}^{i}\right)^{2}} = \vert \boldsymbol{g}_{0}^{i}\vert

第二步更新过程为

θ2iθ1iησ1ig1iσ1i=α(σ0i)2+(1α)(g1i)2\boldsymbol{\theta}_{2}^{i} \leftarrow \boldsymbol{\theta}_{1}^{i}-\frac{\eta}{\sigma_{1}^{i}}\boldsymbol{g}_{1}^{i} \quad \sigma_{1}^{i}=\sqrt{\alpha\left(\sigma_{0}^{i}\right)^{2}+(1 - \alpha)\left(\boldsymbol{g}_{1}^{i}\right)^{2}}
  • 这里额外引入了一个超参数 α,且 0 < α < 1。

通过 α 可以在计算算均方根的时候,调整现在这个梯度的重要性。如果 α 设很大趋近于 1,代表 g1i 比较不重要,之前算出来的梯度比较重要。

而上一节的 Adagrad 则是每一个梯度都有同等的重要性,不够灵活。

Adam

  • Adam(Adaptive moment estimation)是最常用的优化策略/优化器 optimizer

Adam 可以看作 RMSprop 加上动量,其使用动量作为参数更新方向,并且能够自适应调整学习率。PyTorch 里面已经写好了 Adam 优化器,这个优化器里面有一些超参数需 要人为决定,但是往往用 PyTorch 预设的参数就足够好了。

Learning Rate Scheduling

学习率调度(learning rate scheduling)中 η 跟时间有关,最常见的策略是学习率衰减(learning rate decay)。

还有一种策略是 warmup,即先上升一小段,到达一定值再下降。

BERT 和 Transformer 的训练都使用了 warmup,但 warmup 比较像一个 trick,没有人严谨解释为什么,一个可能的原因是:统计的结果需要足够多的数据才精准,一开始统计结果 σ 是不精准的,所以需要先上升一段时间,以收集有关 σ 的统计数据。

神经网络的两个核心

线性和非线性交替

线性和非线性处理单元的交替(称为“层”)。

神经网络的每一层通常由两种处理单元构成:

  • 线性变换:例如矩阵乘法
    y=Wx+by = Wx + b
    这一步是对输入进行线性组合,本质上相当于传统的线性模型,比如线性回归、感知机。

  • 非线性激活函数:例如 ReLU、Sigmoid、Tanh 等
    a=σ(y)a = \sigma(y)
    非线性激活函数用于打破线性限制,让网络能够逼近复杂的非线性函数。

链式法则

一句话总结,链式法则的本质是 --- 复合函数求导,逐层相乘。

链式法则(反向传播)用于一次性调整全部参数。

  • 神经网络训练的目标是最小化一个损失函数(loss function),如分类任务中的交叉熵。
  • 但网络中的参数成百上千,分布在多层中,如何计算它们对 loss 的影响?

这就用到了链式法则(Chain Rule):在复合函数中,整体导数是各层导数的乘积

例如:

Lx=Lzzyyx\frac{\partial L}{\partial x} = \frac{\partial L}{\partial z} \cdot \frac{\partial z}{\partial y} \cdot \frac{\partial y}{\partial x}

反向传播(backpropagation)正是利用链式法则:

  • 输出层开始,逐层向后传播梯度
  • 每层根据自身的输入、输出和激活函数,计算梯度
  • 所有参数的梯度在一次前向-反向传播中完成计算

NOTE

梯度为什么是向后传的 因为损失 L 是在最后一层算出来的,我们只知道"输出错了多少"。

要知道第一层的参数该怎么调,必须把这个"错误信号"一层层往回传,从数学上看,就是从后往前求损失函数对于每一层的参数的导数(梯度)。

为什么必须要回传,其实是复合函数求导法则(链式法则)可以通过函数套函数来理解,我们所说的从最后一层开始算导数,最后一层就是最外层的函数,复合函数求导要求我们从最外层开始链式求导,所以如果想知道最内层的导数(第一层),必须先求出最外层的导数,一层层求出来。

总结

原则内容作用
线性 + 非线性层交替Wx+bWx + b + 激活函数 σ\sigma提升表达能力,逼近复杂函数
链式法则 / 反向传播用链式求导法一次性计算所有参数的梯度高效训练整个网络

分类与回归

分类与回归是深度学习最常见的两种问题。

回归

最基础的单层线性回归:

from torch import nn
# Sequential = the list of layers
net = nn.Sequential(nn.Linear(2, 1))

线性回归原理

在机器学习领域,我们通常使用的是高维数据集,建模时采用线性代数表示法会比较方便。 当我们的输入包含dd个特征时,我们将预测结果y^\hat{y}表示为:

y^=w1x1+...+wdxd+b.\hat{y} = w_1 x_1 + ... + w_d x_d + b.

将所有特征放到向量xRd\mathbf{x} \in \mathbb{R}^d中,并将所有权重放到向量wRd\mathbf{w} \in \mathbb{R}^d中,我们可以用点积形式来简洁地表达模型:

y^=wx+b.\hat{y} = \mathbf{w}^\top \mathbf{x} + b.

  • 向量x\mathbf{x}对应于单个数据样本的特征。

用符号表示的矩阵XRn×d\mathbf{X} \in \mathbb{R}^{n \times d}可以很方便地引用我们整个数据集的nn个样本。其中,X\mathbf{X}的每一行是一个样本,每一列是一种特征。

对于特征集合X\mathbf{X},预测值y^Rn\hat{\mathbf{y}} \in \mathbb{R}^n可以通过矩阵-向量乘法表示为:

y^=Xw+b{\hat{\mathbf{y}}} = \mathbf{X} \mathbf{w} + b

  • 其中X\mathbf{X}是特征矩阵,w\mathbf{w}是权重向量,bb是偏置项。
  • 假如把矩阵向量乘法的式子看成线性方程组求解,那么w\mathbf{w}是需要求的解。

分类

分类的核心在于 Class as one-hot vector 。

使用 one-hot vector (独热编码)每个 vector 的距离是一样的,直接用数字 1 2 3 的话,三者之间的距离不同。

Softmax

Softmax 回归可以看作是线性回归在分类问题上的扩展。

Softmax 函数的作用是将未规范化的预测变换为非负数并且总和为1,同时让模型保持可导的性质。

公式省流:

  1. 归一化:对每个未规范化的预测求幂,这样可以确保输出非负。
  2. 保证概率和是1:每个求幂后的结果除以它们的总和。 y^=softmax(o)其中y^j=exp(oj)kexp(ok)\hat{\mathbf{y}} = \mathrm{softmax}(\mathbf{o})\quad \text{其中}\quad \hat{y}_j = \frac{\exp(o_j)}{\sum_k \exp(o_k)}

对数似然

对数似然是似然函数的对数

  • 似然可以理解为概率,区别是似然的思维是固定数据,变化参数,概率相反。

在多分类问题中,每个样本服从多项式分布,其概率质量函数为:

P(yx)=j=1q(y^j)yjP(\mathbf{y} \mid \mathbf{x}) = \prod_{j=1}^q (\hat{y}_j)^{y_j}

在softmax回归中:

  1. 似然函数:表示给定所有特征 X\mathbf{X},观测到所有标签 Y\mathbf{Y}概率 P(YX)=i=1nP(y(i)x(i))P(\mathbf{Y} \mid \mathbf{X}) = \prod_{i=1}^n P(\mathbf{y}^{(i)} \mid \mathbf{x}^{(i)})

  2. 对数似然:取似然函数的对数 logP(YX)=i=1nlogP(y(i)x(i))\log P(\mathbf{Y} \mid \mathbf{X}) = \sum_{i=1}^n \log P(\mathbf{y}^{(i)} \mid \mathbf{x}^{(i)})

logP(yx)=logj=1q(y^j)yj=j=1qylogy^j\log P(\mathbf{y} \mid \mathbf{x}) = \log \prod_{j=1}^q (\hat{y}_j)^{y_j} = \sum_{j=1}^q y \log \hat{y}_j

另外,由于概率总小于 1 ,概率的对数总是负的,所以一般 loss 都要用对数似然,这样 loss 负负得正,是一个正值,这时候就是最小化 loss 就是最小化负对数似然。

交叉熵损失

交叉熵损失是分类问题最常用的损失函数,衡量的是两个概率分布 p(真实标签)和 q(模型预测)之间的距离。

交叉熵公式:

H(p,q)=ipilogqiH(p,q)=\sum_{i}-p_{i}\log q_{i}

注意,交叉熵损失在独热编码的情况下退化为负对数似然

l(y,y^)=iyilog(yi^)=log(yy^)l(y,\hat{y}) = -\sum_{i}y_{i} \log(\hat{y_{i}}) = -\log(\hat{y_{y}})
  • 由于 y\mathbf{y} 是一个长度为 qq独热编码向量,所以除了一个值为1项以外,其他所有项都为0

梯度计算:

oil(y,y^)=softmax(o)iyi\frac{\partial}{\partial o_i} l(y, \hat{y}) = \text{softmax}(o)_i - y_i

Pytorch 的交叉熵计算函数内部使用了 LogSumExp 技巧,避免原始公式容易出现的溢出问题。

loss = nn.CrossEntropyLoss(reduction='none')

多层感知机

多层感知机由多层神经元组成, 每一层与它的上一层相连,从中接收输入; 同时每一层也与它的下一层相连,影响当前层的神经元。 当我们训练容量较大的模型时,我们面临着过拟合的风险。

全连接网络

加入隐藏层

直接的输入到输出的线性模型往往难以符合实际,所以我们需要在网络中加入隐藏层,最简单的做法是多层全连接网络:

非线性的激活函数

为了发挥多层架构的潜力,我们还需要在仿射变换之后对每个隐藏单元应用非线性的激活函数(activation function)σ\sigma

一般每个隐藏层之后都会加一层激活函数。


CNN


RNN


REF