学习自 bilibili - 李宏毅2021春机器学习课程(只包括 BERT 以前部分、AWD,不包括作业及选修)。

Introduction

机器学习就是代替人类繁冗的思考,对当前训练资料寻找一个固定的黑盒函数,将已知的输入转化为(预测)期望得到的未知输出。

简单的分类:

Regression

常见的 Regression 训练方法:

  1. 建立含有未知参数的预测函数 $f$(Function with Unknown Parameters)
  2. 定义基于训练集的损失函数(Define Loss from Training Data)
  3. 优化Optimization

Function

从当前数据如何去预测未来,这个预测函数如何建立,往往基于 Domain Knowledge,也即对此问题本质上的了解。

一般而言:首先写出带有未知参数(Parameter)的函数,如 $y=b+\sum w_i*x_i$:

这之后去修改,或者调参优化这个预测函数。

对函数的修改

做完一轮训练,如果认为与真实情况相比,呈现的对比结果从根本上就很奇怪(Based on Domain Knowledge),那可以修改函数本身以期更接近真实情况。

Models

Linear Model

譬如上文提到的,预测值和输入值线性相关的,就是一个 Linear Model。

这样的模型显然有很大的限制(源于其本身的 Model Bias),因为种种原因,因变量与 feature 之间很难有一直呈比例的变化,甚至可能会出现很奇怪的曲线。而线性模型的弊端就在这里出现了,譬如它无法构造出如下图一般的折线:

linear_model_bias

我们如果要构造一种普适的曲线,这是很困难的。但我们可以在一条曲线上取很多个点,用折线连接起来,再用很多分段线性(Piecewise Linear)的曲线去构造。

piecewise_linear

但如何来表示这些连续曲线呢?这就引出了接下来的模型:(常见的激活函数: Activation FunctionSigmoid, ReLU

Sigmoid

sigmoid

用一条顺滑的曲线去近似表示之,其中:

sigmoid2

用之就能尽可能地组合出复杂的曲线,当然分多少段,这也是个 hyperparameter,总之,我们能用这种方式去构建一个 y - {x_i} 的二维复杂曲线:

sigmoid3

Rectified Linear Unit (ReLU)

那么 ReLU 又是什么呢?也即 $y=c·max(0, b+wx)$,其实就如下图所示:

ReLU

表现在函数中是这样:

ReLU2

可以看到,一个 Hard Sigmoid 需要两个 ReLU 去代替,但在实际操作中正常常用 ReLU 作激活函数,其原因是?是什么?—-

Matrixize

我自己编的词语—-

我们将以上的函数都化作线性代数的形式,以 sigmoid 为例,有: \(y=b+\sum\limits_i c_i·sigmoid( b_i + \sum\limits_j w_{ij}x_j )\\ r_i:=b_i+\sum\limits_j w_{ij}x_j\\\) matrix

设 sigmoid: $\textbf{a} = \sigma(\textbf{r})$,则有:

matrix2

整理一下得到: \(y=b+\textbf{c}^T\sigma(\textbf{b}+\textbf{W}\textbf{x})\) matrix_parameters

为了方便表示,我们将所有未知的参数统称为一个向量 $\theta$,同时称 Loss function 为 $L(\theta)$。

Loss

定义

用来衡量当前这个函数 $f$ 的预测结果和真实结果的差距

###常用的计算方法

不同的数据分布常常定义不同的 Loss,但总归的原则是:Loss 越大,则当前参数越差;反之越优。

Error Surface (等高线图)

对不同参数的组合,我们可以画出不同的等高线图,用颜色去表示 Loss 的分布差异,以期更直观地进行参数调整。

例(暖色系表示 Loss 大,效果差;冷色系表示 Loss 小,效果较好):

error_surface

Optimization

显然我们不可能定义一个函数就能精准预测未来,故我们需要对我们定义的函数进行优化。

那怎么优化呢?对已选定的函数来说,一个简单的想法就是调整参数去降低 Loss。

在很多参数的时候,显然我们不可能去暴力搜索,于是乎提出了一些贪心/优化的方法:

梯度下降法(Gradient Descent)

其实总感觉这个方法最终还是只能找到局部最优欸

有一点极大似然估计的感觉

Gradient_Descent

这里以一维度的参数为例。

简单来讲,在选取初始参数 $w_0$ 的基础上,对该点进行微分 ${\partial L\over \partial w}\mid_{w=w_0}$,由于我们要向 Loss 降低的方向更新参数,则 $w_1 \leftarrow w_0 - \eta {\partial L\over \partial w}\mid_{w=w_0}$。

其中 $\eta$ 称为学习率(Learning Rate,hyperparameter),而梯度 $g=\triangledown L(w_0)={\partial L\over \partial w}\mid_{w=w_0}$。

如此反复调整,直到:达到人工预定的更新上限次数;或是微分极小或为 0,则不再更新。

如果是多个参数($\theta$),显然不断地分别微分更新即可。

Critical Point Problem

提出

Q:这样岂不是容易找到很多”局部最优”?(local minima

A:其实不然。当然会有这样的情况(微分为 0),其统称为 critical point(包括 local maxima, local minima, saddle point)。

其中 saddle point 为下图这种情况:

saddle_point

当维度较高(参数很多)时,当然可能有很大几率卡在微分很小的地方,但也有大概率会有至少一个维度是呈 Loss 下降态的:试验也证明了这个假说。

判别及解决

Q:那么如何判断:用梯度下降法优化不动的时候,是处于什么样的情况呢?如果处于鞍点,又该往哪个方向调整参数呢?

A1:简单地容易想到二阶微分,相关的数学推导知识此处暂且按下不表,可详参视频的第 11 P。

A2:Momentum

Gradient Descent + Momentum (动量)

我们借助物理世界的概念去对抗 critical point prob,设想梯度下降法的点如同小球,如果滚到梯度为 0 的点,却具有惯性,则可能继续前行。

加上动量概念以后,我们每次更新参数,都结合于 前一步移动的惯性 与 当前参数的 Gradient 去调整参数。

Momentum

其实到了这里我们就容易发现一个很大的问题:Learning Rate 等参数过于固定,导致每一步都很僵硬,这也正是梯度下降法的一个问题。于是引入自适应的学习率这一说法。

Adapted Learning Rate

和动量结合着看, 实质上是一个决定力的方向, 一个决定力的大小.

其实很多时候压根走不到 critical point 就卡住了,如下图这样的情况:

learning_rate

如果我们只是单纯地把比率设小一些,当到一定小的梯度时,两者乘积更小了,可此时离最优点其实还有不小的优化空间,训练更新参数的速度却远远难以抵达;设过大了,则容易出现上图反复横跳之类的情况。

所以我们可以贪心地去动态调整参数,比方说,在梯度极大处,我们降低学习速度,以期不错过可能出现的临界点;反之增加,以期快速步过这个缓慢的阶段

总结

GD

我们最终得到的版本,大抵就是这样。

Speed Up (Backpropagation)

我们知道一个神经网络中有很多参数,更新耗费的时间就久了起来,甚至很难想象怎么能做到更新的。所以这里介绍了一个叫做 backpropagation 的方法得以在 DNN 中用 GD 去更新参数。

(此处以朴素梯度下降法(Vanilla Gradient Descent)为例)

链式法则

$s=f(x,y),x=g(t),y=h(t)\rightarrow{\partial s\over \partial t} = {\partial s\over \partial x}{\partial x\over \partial t} + {\partial s\over \partial y}{\partial y\over \partial t}$

Backpropagation

递推

我们每次更新参考的是 $L_(\theta) = \sum\limits_{i=1}^Ne_{i}(\theta)$,那么更新参数的偏微分也就是 ${\partial L(\theta)\over \partial w}={1\over N}\sum\limits_{i=1}^N{\partial e_i(\theta)\over \partial w}$,这样一来每次对单个参数重复计算,复杂度就会很高。能不能提出一种复杂度很低的算法呢?

BackPropagation

(上图的 C 就是我文字中的 e)

我们可以把对 Loss 求微分的过程一一拆分开来,求取每个差异函数对每个参数的微分。

对于 DNN,由上图我们可以知道,${\partial e_i\over \partial w}={\partial z\over \partial w}{\partial e_i\over \partial z}$。我们把前面这部分叫做 Forward pass,后面这部分叫做 Backward pass。显然我们知道 $z(x)$ 的函数式,那求对 $w$ 的偏微分没有难度;而对于后面这部分,我们往后看,又可以分为两个部分:

BackPropagation2

又因为 ${\partial a\over \partial z}=\sigma’$,当 $z$ 确定时,也很好计算。则问题最终扩散到 $e_i$ 对后面这层 layer 的各个参数的偏微分。

这里显然有一个递归:

  1. 后面一层就是 output layer 了,直接求一次就完事;
  2. 否则,每个参数仍然取决于后一层 layer 的各个偏微分。

不妨从后往前推,一切迎刃而解:

BackwordPass

这样一来,GD 更新参数的速度就可以接受了。

BackPropagation3

Batch

含义

我们把所有资料按 B 个分为一组,称之为 Batch,B 为 Batchsize;每次用一个 Batch 去更新参数 $\theta_i=\theta_{i-1}-\eta \textbf{g}$,我们称第 i 个 Batch 算出来的 Loss 为 Li。

每更新一次参数叫一次 Update,更新一轮叫 Epoch,在每次 Epoch 之前要做一次 Shuffle,避免极端情况的出现。

原因

为什么要用 Batch?

如果我们一味地用所有训练资料去更新参数,虽然总的来说效果比较稳定,但时间消耗太久了。而分成小块分阶段地更新,每次更新的间隔就大大缩短了(但显然效果不太稳定,Noisy)。

(但事实上可以利用并行去大大减少前者的时间开销)

好处

比方说使用梯度下降法时,尽管每个 Batch 分布可以看成一致的,但由于数据的差异性,Loss 曲线在相同参数时的取值很可能不一样,例如下图:

batch

这样一来,减少了卡在 critical point 的情况,那么也许训练出的模型在普适性上的表现就比较良好了(注意我们始终关注的是模型在当前分布下的普遍预测情况)。

Normalization

考虑维度过高,Error Surface 过于复杂,除了优化寻找更低 Loss 点的方法,我们期望直接简化这个平面。

在深度学习中,以 Fully Connected Feedforward Network 为例,我们知道每个中间层变量的值每个参数都取决于前面一层所有的变量,尽管有参数来对应调节,但对应维度的规模相差很大的时候,不同维度的变化不同,产生的差异自然是巨大的:

normalization

如果我们能够归一化所有的变量范围,那 Loss 收敛速度可能会更快一些,训练可能会更顺利。

####Standardization

简单地,我们可以对一组 Batch (也可以对整体做,不过数据量太大了)里面所有 feature 向量某个维度 $i$ 的参数做一个标准化:$\tilde{x}{ij}\leftarrow {x{ij} - \mu_i\over \sigma_i}$,这样一来分布都会在 0 左右,而方差在 1 左右。

但对 DL 来说,我们发现每个标准化后的向量在一层 layer 后,每个输出结果的分布很可能又天差地远了,又因为此时的向量可能每个维度间存在某种关系,故不能如前直接依次标准化了,这样一来就有对所有向量标准化的想法,这里介绍的方法如下:

standardization

此处对向量 $\sigma$ 的公式有些误导,事实上它的意思是对向量中的每个元素单独进行这样的操作;之后标准化的时候的运算也是对每个元素单独进行运算。

这也让 $z_i$ 这些中间层变量彼此关联起来,整个网络都相关起来了。

又有人说,中间层的变量 [分布都会在 0 左右,而方差在 1 左右],这样可能会有 restrict,故可以对之设定又一些参数去调整(如 $\hat{z}_i=\gamma\tilde{z}+\beta$)

在线调整

但实际情况中很多时候不可能到某个 BatchSize 才做一次 test,或者这样说: test 时根本没有 Batch。这怎么办呢?

故常有读入一笔就处理一笔的想法,比方说,每取一个 Batch,就算一个 $\mu_i$,最后算一个 moving average:$\bar{\mu}\leftarrow p\bar{\mu}+(1-p)\mu^t$,在 test 时代替其参数进行 normalization。

Loss Guide

根据 Loss 的表现,简单地提出了几种情况去分类优化:

GuideToLoss

训练集 Loss 过大

简单地分成两个可能性:

  1. Function 不够复杂(受 Model BIas 制约);
  2. Optimization 做得较差(比方说限制于梯度下降法的劣势)。

如何判断呢?

举个例子:显然,相同的 neuron network,既然 n 层能做到一定的 Loss 效果,那前 n 层相同的(更加复杂、弹性更大的) n + k 层 NN 完全没有理由做不到更好的 Loss。那么这种情况就是第二种情况: Optimization 做得不够好——那么这样我们就可以先做一些层数小的训练,得到较好的结果再做较大的进行对比分析。

如果当前层数本身就很小,Loss 依然很大,也许正得归结于 Function 的局限性了。

####测试集 Loss 过大(训练集 Loss 小)

这种情况下也能简单分为两个可能性:

  1. Overfit
  2. Mismatch

—-其实我觉得还有可能 Loss 函数本身选择得不够好(对于衡量精确程度)

—-能不能说,我用训练集的 y 去按预测 function 倒推中间层结果进行一个中间层的,类似于 MITM 的优化?

过拟合(Overfitting

训练集上 Loss 越来越小,而测试集上 Loss 不变甚而变大了这样的情况,就叫做过拟合

产生这种情况可能是训练集数据太少,以至于训练出的函数只是 simply 去贴合训练资料,不够复杂,或者不够简单,总之就是普适性太弱

Mismatch

实际情况中,训练集和测试集的分布可能由于各种原因(突发状况、时间段不同导致结果根本不同之类)不同。

—-如何判断、解决这样的问题

Cross Validation

限制较多也会影响(model bias),限制太少也会影响(overfit…),那么我们怎么能够选择出一个比较漂亮的 model 呢?于是乎提出了 Cross Validation 的方法去帮助选择 model。

比方说把训练资料分为两部分,一部分称为训练集,一部分称为验证集Validation Set):用前者训练,然后到后者去本地对比训练结果,去根据上面的情况调整自己的训练方案、训练模型。

DeepLearning (DNN)

Introduction

看了前面的介绍,我们顺理成章地可以来介绍深度学习了。深度学习其实就是神经网络,只是换了个名字罢了,一般而言,基于 Neural Network 方法,我们都称之为 DNN。

详见下图:

DNN

我们发现,其实可以把这个拟合多做几遍,相当于是增加了所谓的”深度”,来让参数更多,从而更接近平滑的曲线预测更加真实的情况。

我们发现,当同一层参数够多的时候,他也能逼近任何连续的函数呀,那么为什么还要提出 DNN,为什么要变”深”而不是变”宽”呢?

—-是不是应该找paper看看

其实深度学习也大同小异,训练过程分为 3 个步骤:

  1. 定义一系列函数(也称 Neural Network
  2. 判断函数优劣
  3. 选择最佳函数

NN (Neuron Network)

每个逻辑回归函数称为一个神经元(Neuron),把它们按不同的方式(自己设计)连接起来(Structure),就形成了不同的 NN

常见的连接方式

Fully Connected Feedforward Network (more flexible)

把 neuron 按输入向量的长度 k 排成一层,每一层之间又形成一个完全二部图即可。这样一来,显然有每层之间的输入与输出均为一个 k 维的向量,如图所示: NN

DNN2

CNN 卷积神经网络 (less flexible)

图像分类,输入图片可以看作一个 3D Tensor,则 input 数量为 channels (3 if RGB) x width x height 个(Full Conn),但这样的参数数量过多,故进行简化:

  1. 图像分割

    我们往往是通过图片上一些小块的特征决定对这张图片的认知,所以可以将图片划分为许多个 Receptive Field,如此,不同的 neuron 对应图片划分出的不同部分(可重叠)。经典设置如下:

    CNNsimplification1

  2. 参数共享

    我们知道一个特征通常和出现在图片中的具体区域位置是无关的,故常常采用 [参数共享] 的方式去进一步减少 NN 中所需参数的个数:将一个 neuron 称为一个 filter,每个 filter 具有相同的参数。

    用 Filter 遍历一遍图片,这个过程称为 Convolution。

    CNNsimplification2

  3. (Pooling (Max Pooling))

    一张鸟的图片缩小,仍然看出是一张鸟的图片,故有 Pooling 的应用。

    Filter 得到 Convolution,以 k x k 的格子划分区块,每个区块取最大的一个。

    CNN

Flatten: 把输出用一个向量表示。

Classification

专为影像设计

有时可以看作是一种另类的 Regression,多分类过程中通过 Softmax 作为 output layer 将输出映射到 (0, 1) 以内,所有输出值累加之和为 1,最后以概率最大的 $y_i$ 作为答案。

NN

而对这样的概率答案,我们常用 Cross-Entropy 作为 Loss Func:$e=-\sum\limits_i \widehat{y_i}lny_i’$(已知分布情况下,这样拟合出的参数类似于最大似然估计)

Self-attention

此前谈论的输入都可以表示为一个向量,但很多时候(如句子处理、语音识别)输入会有长度不同的多组向量表示。根据输出又可大致分为以下三种情况:

Sequence Labeling (len(input) == len(output)),这里以词性分析为例:假设有 n 个词 (input),需要输出对应 n 个词的词性 (output),由于对每个词的分析需要基于整个句子的语境,最好的方式是囊括整个句子,但长句会导致参数过多,用固定长度的窗口去遍历也显然不太适合,同时也并没有让网络集中某个词组,则此时需要用到 Self-attention。

过程

  1. 计算当前词与其他词的关联度 $\alpha$:

    • 点乘:self-attention-alpha
    • 累加:self-attention-alpha2
  2. 通过如 Softmax 去转化一下 $\alpha$ 得到分数:

    self-attention-matrix0

  3. 通过之去算出一个总分数,谁的 $a_{i,j}^{‘}$ 最大,总分数就越接近谁:

self-attention-matrix01

  1. 最后得到 ${b_i}$,然后做 FC,最后得到最终结果:

self-attention

矩阵加速

self-attention-matrix

self-attention-matrix2

self-attention-matrix3

总的来讲,就是这样:

self-attention-matrixf

此外,${b_i}$ 可以不止有一个,称为 multi-head self-attention。

Positional Encoding

我们发现在自注意力机制中,没有提及当前词在句中相对位置的信息,于是加入向量 $e_i$ 以表示。

这样的向量尚未有一种确定方式,除了提前设定,不同学习方法学习出来的向量甚至也不同。

BTW,CNN 是 self-attention 的一种特殊情况。

Transformer

Seq2Seq

Input -> Encoder -> Decoder -> Output

Encoder

self-attention-matrixf

self-attention-matrixf

residual connection: 在自注意力输出的向量后加上输入向量

Decoder – Autoregressive (AT)

self-attention-matrixf

self-attention-matrixf

流程如上,其中 masked 意即多头注意力生成第 k 个向量时,用前 k 个输入(正如 decoder 输出的过程),其中结束程序的标志为:生成一个 END 符号标志。

Decoder – Non-autoregressive (NAT)

self-attention-matrixf

优点:

缺点:

GAN (生成对抗网络)

把网络当作一个 generator 使用:

NN Gen1 -> Discriminator v1 -> v2 -> v2 -> v3 -> v3

对抗网络:生成网络 -> 训练识别网络 -> 训练生成网络 (此时只调整 Gen 的参数,且 Gradient ascent,使得识别出来的分数越高越好) -> 训练识别网络 -> …

self-attention-matrixf

self-attention-matrixf

细节:

我们有 $P_G,P_{data}$,前者是 Generator 从(正态分布)样本生成的,后者是由真实的目标数据生成的。

Generator Optimizing: 使得 $G^*=arg \min\limits_G Div(P_G, P_{data} )$ (Divergence) 减小;

尽管不知道 $P_G, P_{data}$ 的分布情况,无法计算其散度,但我们可以拿到很多组样本,再通过 Discriminator,就能 Optimize 之。

Discriminator Optimizing: 对真实图像打分高、生成图打分低,则一般可以 Maximize 如下的一个 Objective Function,其中 D 就是 Discriminator(等价于一个 Cross-entropy):

self-attention-matrixf

前面的散度不好表示,但在这里我们可以用识别器的优化函数替换(直观想一想易知):

self-attention-matrixf

WGAN

大多数情况下,PG PDATA 就像 dim-2 上的两条曲线,重合率很小,如果用 JS Divergence 去二分类,总能得到 100% 正确率的结果,loss 就无意义。Wasserstein Distance 把 P 变成 Q 所需的(所有方案可以得到的)最小的平均距离。

self-attention-matrixf

其中,1-Lipschitz 简单来说就是要求 D 函数足够平滑,不会出现打分趋近无穷的情况(与 JS Div 无异)。这样的函数挺难找的,有一个想法 Spectral Normalization 一类。

Conditional GAN

就是我们常常最喜欢的,给定一些约束条件,和随机分布 $z$,输出一个符合条件的答案。

trick 在于训练时也应加入 条件和输出不对应的数据,以求 Discriminator 考虑全面;Supervised + GAN 使用效果较好。

Cycle GAN

当无成对数据的时候,我们将输入、输出看作满足 Domain $x,y$,期望对在 Domain $x$ 中的数据输出一个 Domain $y$ 中的对应数据。

为了保证输入输出有一定的对应关系,而不是仅仅满足 Domain 的需求,我们提出了 Cycle GAN

self-attention-matrixf

经过两次转换后,输入、最终输出应该尽量接近(consistency);可以同时做两个方向的训练。

Self-Supervised Learning

是无监督学习的一种

BERT: Transformer 的 Encoder(用于 NLP)

训练

Masking Input

假设我们只有无标签的文章,则进行一个 Masking Input,随机盖住一些输入文字(特殊符号替换;随机换字),然后在 BERT 中找到盖住部分对应的输出,将其做一线性变换得到分类答案,并与正确字符进行 Cross-Entropy Optimization。

Next Sentence Prediction

在两句间添加一特殊标记、开头添加一特殊标记,现在做一 2-classification,预测两个句子是否有顺序关系。

但这太简单了,故后提出 Sentence order prediction,判断 句1、句2 的前后关系。

Pre-training

训练 BERT 进行填空(以上两种方式),然后得到预训练好的参数模型。

Downstream Tasks

BERT 在预训练后,可以进行分化,Fine-tune,以完成某项具体的任务。

使用BERT的整个过程是连续应用Pre-Train和Fine-Tune,它可以被视为一种半监督方法。

Examples

Pre-training a Seq2Seq model

MASS/BART,正如以上的方法一样,在破坏了输入的句子之后,通过 Seq2Seq 模型来恢复它以达到预训练目的。

Why work

最常见的解释是,当输入一串文本时,每个文本都有一个对应的向量。对于这个向量,我们称之为 embedding,它代表着输入词的含义。而意思相近的词语向量接近。但是,BERT 考虑上下文,同一个词语的 embedding 也会有很大不同(例如苹果、苹果手机)。

但这只是一种解释。当我们使用 BERT 对无意义句子进行分类时,它仍然可以很好地对句子进行分类。

Multi-lingual BERT

由多语言训练的 BERT。

神奇之处在于,用 英文 Q&A 进行训练,仍可在高概率的情况下(78%)回答中文 Q&A。

使用 Mean Reciprocal Rank(MRR)进行对比(MRR的值越高,不同embedding之间的Alignment就越好),知道,具有相同含义但来自不同语言的词将被转化为更接近的向量。

但是当训练多语言的 BERT 时,如果给它英语,它可以用英语填空,如果给它中文,它可以用中文填空而不会混在一起。后来发现,取所有英文单词/中文单词的 embedding 均值,其差值即为英汉语言之间的差距。

BERT P3_GPT3

自监督模型下任务不同。

How to use

给出范例,进行题目的一个做,有点像人类做题。

In-context Learning

给出少量例子(“Few-shot” Learning)进行训练,但是并未做梯度下降,故专称为 In-context Learning

“One-shot” Learning “Zero-shot” Learning

给一个例子/给出一个叙述,而进行翻译。(正确率较低)

Adversarial Attack

White Box

知道模型及其参数

在原(单张图像)向量上增加扰动向量,对其按原参数模型进行训练。根据攻击类型,将损失函数设为 $L(x)=-e(y,\hat{y})$,或 $L(x)=-e(y,\hat{y})+e(y,y^{target})$。意即距离正确目标越远(、离 Fake Target 越近),则效果越好。

WhiteBoxAttack

Black Box

实际上,我们很多时候拿不到模型参数,但仍然面临攻击的可能。

我们可以自训练一个本地网络,用这个网络去研究攻击添加的扰动向量。直观上,由于各个模型在同一个任务上的识别有很大的共通性,故这样的攻击成功率较高。

Defense

Passive Defense

Proactive Defense

对一系列训练集进行训练,并自寻找到它们的攻击向量 $\widetilde{x}^n$,控制正确的 tag 并进行训练。