斯坦福CS231n笔记

Gu Wei 2022年7月

看的是b站的视频,是2017年的版本。CS231n的全称是CS231n: Convolutional Neural Networks for Visual Recognition,即面向视觉识别的卷积神经网络。笔记前面很多内容参考了知乎

然而从第五章开始我就看密西根大学的EECS 498-007/598-005的2019版本了,个人觉得讲的更加清晰一点。

个人觉得作为初学者来说,本课程还是有一定难度的,无论是来自于话题的不熟悉、Justin Johnson的语速,还是numpy、pytorch的陌生。但是课程质量还是很高的,其中三个assignment相当不错的(虽然大部分我都不是自己写的)。

作为初学者,笔记纰漏难免。


 

一、简介

对于一张图片,人看到的是图片,计算机看到的是一个三维向量,每个数据范围在0~255。这就是所谓人与计算机之间的semantic gap。注意到我们用RGB格式,且每个颜色有8位,所以数据范围在0~255之间;像素构成两维,RGB构成第三维。下面是一个两行五列的色块:

注意到这些多维数组,我们称之为张量(tensor)

计算机识别图像遇到困难和挑战有:

我们必须实现一个具有较好鲁棒性的算法来应对以上问题。

那如何写一个图像分类的算法呢?我们采用数据驱动方法——给计算机很多数据,然后实现学习算法。也就是先手动给数据分好类、打好标签,做出训练集;然后实现训练函数,来训练分类器(train the classifier)或者说学习一个模型;最后实现预测函数,利用训练好的分类器来预测它未曾见过的图像的分类标签。所谓的分类器就是:在标记好类别的训练数据基础上,判断一个新的观察样本所属的类别。


二、Nearest Neighbor以及k-NN分类器

1. Nearest Neighbor分类器

思路:训练时记下所有图像,且不做更多处理;预测则通过遍历所有图像后,找出距离最近的那幅图像,并返回该图像的标签。

时间复杂度:训练O(1);预测O(n)(这里意指要遍历n张图像)

应用情况:几乎不使用,预测时间过长,预测精度差

距离:距离有两种——L1距离(曼哈顿距离)L2距离(欧式距离)。前者是矩阵中所有数据做差后加和,后者是矩阵所有数据做差后做平方和后开根。L1距离随坐标的选取而不同,L2距离和坐标无关。因此,若坐标有特定含义,可以优先考虑L1距离。

数据集CIFAR-10。这个数据集包含了60000张32X32的图像。每张图像都有10种分类标签中的一种。其中50000张为训练集,10000张为测试集。

python代码

1)加载数据。在下面的代码中,Xtr(大小是50000x32x32x3,50000个三维向量)存有训练集中所有的图像;Ytr是对应的长度为50000的1维数组,存有图像对应的分类标签(从0到9);tr意为train训练数据,te意为test测试数据;Xtr.shape[0] = 50000, Xte.shape[0] = 10000;Xtr、Ytr、Xte、Yte应该都是numpy中的array数据类型。

2)训练和评分。在下面代码中,Yte_predict == Yte返回的是一个布尔数组,np.mean返回数组里所有元素的平均值。

3)上述代码中NearestNeighbor类为:

2. k-NN分类器

k-NN是k-Nearest Neighbor的缩写,当k等于一时,就是Nearest Neighbor分类器。

思路:找距离最近的k张图片,选取这k张照片出现次数最多的标签作为预测结果。

相对优势:如下图所示,更高的k值可以让分类的效果更平滑,使得分类器对于异常值更有抵抗力。白色区域应该是近邻标签的最高票数相同导致的(比如:2个邻居是红色,2个邻居是蓝色,还有1个是绿色),此时可以任意选择其中一个标签作为预测结果。

应用情况:几乎不使用,预测时间过长,预测精度差

img

如何选择k

3. 总评

眼想想就很一般——存储训练数据占用空间大、预测函数计算资源耗时高、预测准确度低。

具体说来,这些图片的排布更像是一种颜色分布函数,或者说是基于背景的,而不是图片的语义主体。比如,狗的图片可能和青蛙的图片非常接近,这是因为两张图片都是白色背景。从理想效果上来说,我们肯定是希望同类的图片能够聚集在一起,而不被背景或其他不相关因素干扰。为了达到这个目的,我们不能止步于原始像素比较,得继续前进。


三、线性分类器

1. 简介

考虑这样一个函数,输入一张图像,输出其对应每个标签的评分,选取最高的评分,便是预测结果。这就是评分函数(score function)。那么如何评价评分结果的好坏、如何量化预测分类标签和真实标签的一致性,我们需要损失函数(loss function)。于是,我们再通过一些手段不断改进评分函数中的参数,让损失函数值最小,这便是最优化问题。我们本节的思路就将围绕这三点展开。

此外,损失函数也被称为代价函数(cost function)目标函数(objective)

2. 线性评分函数

考虑这样一个线性映射:f(xi,W,b)=Wxi+b. 式中W和b称为函数的参数(parameter),W还被称为权重(weight),b被称为偏差向量(bias vector)xi是测试集中第i个数据,函数值便是最后的评分。注意到所有的变量都是矩阵或向量。

举例:仍以CIFAR-10为例。xi为3072*1的列向量,W是10*3072的矩阵,b是10*1的列向量,函数值是10*1的列向量。函数每一行对应着不同标签的评分。如果我们把W中每一行取出来,可以生成十张图片,称之为标签的模板或者原型,如下图所示:

img

此外还有两个技巧

3. 损失函数

这里先介绍两种损失函数,第一个是SVM(support vector machine, 支持向量机)分类器采用的合页损失(hinge loss),第二个是Softmax分类器采用的交叉熵损失(cross-entropy loss)。然后介绍正则化(regularization)。事实上,目前我并没有理清楚这些奇怪的命名,以及前应后果的联系。可能要学完机器学习才能理清思路吧。不过缺少背景知识不妨碍我去学习这两种损失函数,这里希望建立一种感性认识。

此外,我对这部分的内容进行了顺序调整,尽可能理清楚思路吧。

目前是这么理解SVM的:SVM利用决策边界划分两类数据。数据是n维的话,那么决策边界是n-1维。若决策边界大于三维的话,这个决策边界可以称为超平面,划分也称为超平面划分。我们只考虑线性SVM,对于那些不是线性可分的数据,可以通过核技巧(kernel trick)来转换为线性可分的情况。而找到这样的核函数很难,有时就放宽对样本的要求,允许少量的样本分类错误,这就是所谓的软间隔。在软间隔中,有一个损失函数也就是合页损失函数,来评价样本背离约束的情况。现在我们要分割超过两类的数据,SVM要推广到多类SVM(multiclass SVM)。SVM深入学习需要凸优化的背景知识。

3.1 损失函数

损失函数的最终表达式如下:

L=1NiLidata loss+λR(W)regularization loss

上述公式由两个部分组成:数据损失(data loss),即所有样例的的平均损失[公式],以及正则化损失(regularization loss)。下面先介绍两种Li,并进行一点对比;再介绍正则化损失。

3.2 SVM分类器

将第i个数据记作xi(一个列向量),对应正确标签记作yi(一个数字),评分函数f(xi,W)=Wxi计算出来的结果记作s(一个列向量),sj是第j个类别的得分sj=f(xi,W)j 。评分函数中没有b可能是用了bias trick。针对第i个数据的多类SVM的损失函数定义如下(不唯一,这里只介绍一种):

Li=jyimax(0,sjsyi+Δ)=jyi{0,if sj+Δsyisjsyi+Δ,otherwise

其中max(0,sjsyi+Δ)称为合页损失(hinge loss,铰链损失),由于函数图像像合页。

sj=wjxi,其中wj是权重W的第j行,故Li=jyimax(0,wjxiwyixi+Δ)

例子:假设有3个分类,评分s=[13,7,11];其中第一个类别是正确类别,即yi=0;同时假设Δ是10。上面的公式是将所有不正确分类(jyi)加起来,可得:

Li=max(0,713+10)+max(0,1113+10)=8

即此时数据损失为8

解释:只有当不正确标签的分数比正确类别分数高出Δ,才会计算损失值。

此外,还有平方合页损失函数(也就是(sjsyi+Δ)2),将更强烈(平方地而不是线性地)地惩罚过界的边界值。如何选择损失函数取决于你关心何种错误。

一些课堂提问

python代码

3.3 Softmax 分类器

Softmax分类器其实是logistic回归分类器面对多个分类的一般化归纳(我显然看不懂这句话)。损失函数就是所谓的交叉熵损失cross-entropy loss)(交叉熵的背景知识这里也不再研究)。公式如下:

Li=lg(esyijesj)

上式中将第i个数据记作xi,对应正确标签记作yi,评分函数f(xi,W)=Wxi计算出来的结果记作ssj是第j个类别的得分sj=f(xi,W)j 。其中函数fj(z)=ezjkezk被称作softmax 函数,是一个压缩数值的函数。

例子s=[3.2,5.1,1.7]yi=0,则esyi=e3.2=24.5jesj=e3.2+e5.1+e1.7=24.5+164.0+0.18=188.68,于是Li=lg(0.13)=0.89

一些课堂提问

3.4 Softmax vs. SVM

img

3.5 正则化损失

若存在一个权重W能够正确地分类每个数据,即对于所有的i都有Li=0,那么对于任何的λ>1,有λW也能够正确地分类每个数据。可见W并不唯一。

为了避免训练时过拟合,我们向损失函数增加一个正则化惩罚(regularization penalty)R(W),来鼓励模型选择更简单的W,而不是一味地去拟合训练数据。λ是一个超参数。(注意到这里的正则化和正则表达式中的正则没有任何关系)

L=1NiLidata loss+λR(W)regularization loss

常用正则化

式中wk,l是权重W的k行l列元素

例子:假设输入向量x=[1,1,1,1],两个权重向量w1=[1,0,0,0],w2=[0.25,0.25,0.25,0.25]。那么w1Tx=w2Tx=1,两个权重向量都得到同样的内积,但是w1的L2惩罚是12+02+02+02=1,而[公式]的L2惩罚是0.252×4=0.25。因此,根据L2惩罚来看,[公式]更好,因为它的正则化损失更小。

L1和L2的感性区别:L1更倾向于稀疏解,它倾向于让W大部分元素接近0;而L2更多考虑W整体分布,倾向于更小更分散的权重向量,这就会鼓励分类器最终将所有维度上的特征都用起来,而不是强烈依赖其中少数几个维度。

λ的感性理解λ越大,对训练数据的拟合越差,但是对测试数据的表现会从差变好再变差

3.6 实际考虑

如何设置Δ

你可能注意到上面的内容对超参数Δ及其设置是一笔带过,那么它应该被设置成什么值?需要通过交叉验证来求得吗?现在看来,该超参数在绝大多数情况下设为Δ=1.0都是安全的。超参数Δλ看起来是两个不同的超参数,但实际上他们一起控制同一个权衡:即损失函数中的数据损失和正则化损失之间的权衡。理解这一点的关键是要知道,权重W的大小对于分类分值有直接影响(当然对他们的差异也有直接影响):当我们将W中值缩小,分类分值之间的差异也变小,反之亦然。因此,不同分类分值之间的边界的具体值(比如Δ=1.0Δ=100)从某些角度来看是没意义的,因为权重自己就可以控制差异变大和缩小。也就是说,真正的权衡是我们允许权重能够变大到何种程度(通过正则化强度λ来控制)。

4. 最优化

最优化的目的是寻找参数W,它使得损失函数值最小。

优化的方法很多,但是我们直接进入梯度下降

4.1 梯度计算

有两种梯度:一是数值梯度(numerical gradient),缓慢的近似方法,但实现相对简单。另一个是解析梯度(analytic gradient),计算迅速、结果精确,但是实现时容易出错,且需要使用微分。现在对两种方法进行介绍:

建议:利用解析梯度计算,利用数据梯度检查。说人话:先手动求出梯度解析式,然后带入特殊值检验一下。

数值梯度的python实现

解析梯度的函数原型

4.2 梯度下降

计算出梯度,就需要选取步长(step size,也叫做学习率learning rate),还要注意到梯度是指向函数增加的方向,所以最后是减去梯度乘以步长。步长也是一个超参数,而且相当重要,可能是最先需要确定的超参数。小步长下降稳定但进度慢,大步长进展快但是风险更大。采取大步长可能导致错过最优点,让损失值上升。

梯度下降有以下几种版本:

4.3 SGD的问题

4.4 SGD + Momentum

常见且推荐的方法。就是之前的速度会影响之后的。下图中ρ相当于一个速度的衰减因子,通常设置为0.9或0.99,当ρ=0时变回普通的SGD。下面两个实现是一致的(当初始速度v0=0且初始位置x0相同时,会给出相同的x序列)。

image-20220417221736728

显然的,引入动量可以一定程度上解决4.3节SGD具有的三个问题。

4.5 Nesterov Momentum

另一种动量形式,计算f(xt+ρvt)处的梯度,而不是f(xt)处的。即:

{vt+1=ρvtαf(xt+ρvt)xt+1=xt+vt+1

为了更好贴合梯度计算api,有以下等价形式:

image-20220418094309805

三种方法对比如下:(可以看到引入动量后权重的更新会overshoot——跑过头再回来)

image-20220418095033447

4.6 AdaGrad

AdaGrad(adaptive gradient)出发点在于想让历史上大梯度的权重更新慢一点、历史上小梯度的权重更新快一点。代码如下:

4.7 RMSProp

当AdaGrad运行时间较长后,grad_squared会变得很大,导致梯度不再更新了。针对这个问题,只需让grad_squared更新为之前值和新加入的dw*dw的加权,称之为RMSProp(root mean square propagation)。代码如下:

RMSProp相较于动量,更新权重时不容易overshoot。

4.8 Adam

很常见且推荐使用的一个方法,综合利用了RMSProp和Momentum两个想法,代码如下:

通常设置beta1=0.9、beta2=0.999、learning_rate=1e-3,5e-4,1e-4。之所以要bias correction是出于以下考虑:在开始训练时,moment2可能是一个很小的值,导致权重的更新过大。几种方法模拟如下:

image-20220418135411785

4.9 Second-Order Optimization

之前我们讨论的梯度下降法都只用了一阶导,称为first-order optimization。还有利用一阶和二阶导来更新的,似乎用了向量函数的泰勒展开,引入Hessian矩阵。但是由于它在时间和空间上的糟糕表现,并不是一个可行的方案。这里也不再展开了。


四、反向传播

反向传播(backpropagation)是一种求梯度的方法。其背后的数学背景就是链式法则(chain rule)。下面通过几个例子来进行说明。

需要申明一个不严格但是常用的术语,常常用“x上的梯度”或者“x的梯度”来表示“函数对于x的偏导”。

1. 例一

本例希望建立反向传播的基本概念。

例一:求f(x,y,z)=(x+y)z的在(-2,5,-4)的梯度f=(fx,fy,fz)

步骤一:将函数f可视化为计算图(computational graph),(先忽略掉绿色和红色的数字)

img

步骤二:前向传播计算出每个节点的数值,如上图绿色数字所示

步骤三:反向传播计算每个节点上的梯度,如上图红色数字所示。下面对步骤三进一步阐释。

  1. 最右侧ff=1
  2. f=qz得:fz=q=3fq=z=4 可见乘法相当于一个交换器。
  3. q=x+y得:qx=1,qy=1 但是我们要求的是fxfy,需要利用链式法则fx=fqqx得,fx=1×4=4。同样fy=4。可见加法相当于一个分配器。

步骤四:计算图中最左侧(-4,-4,3)即为所求。

注意到:

python代码

2. 例二

本例希望建立起多个门组合成一个门的想法。

对于[公式]有计算图:

img

略作说明,比如exp节点。已知输入值x=-1,输出q=ex=0.37,q上的梯度是-0.53,求x上的梯度。解:fx=fqqx=0.53×e1=0.20

f(w,x)中有一个基本的函数sigmoid函数[公式]。sigmoid对x求导有:

σ(x)=11+ex
dσ(x)dx=(1σ(x))σ(x)

于是计算图中最后四个门可以简化为一个门,且局部梯度为(1-0.73)*0.73=0.20。这样的处理可以简化运算。

python代码

注意到上面已经有矩阵和向量的影子了。例三将进一步阐明这种情况。

3. 例三

之前考虑的内容都是多元函数的情况,本例希望将其推广到矩阵和向量操作。这里只给出最难的矩阵相乘的例子。

例三:假设我们已经知道输入矩阵W(大小为5*10)、输入矩阵X(大小为10*3)、输出矩阵D=WX(大小为5*3)、以及D的梯度dD(大小为5*3),现在想要计算W和X的梯度。

先上python代码

而问题的关键在于矩阵的求导运算。

这里记录一下我目前的计算方法,可能并不是最简单的,但是至少我这个方法是本质的、通用的。也就是用多元函数的链式求导法则,进行计算。

问题

已知Y=WX,Y的梯度dY(即损失函数l对Y的偏导dY),求W的梯度dW和X的梯度dX。

结论

dW=dYXT,dX=WTdY

证明

已知:

WX=(w11w12w13w21w22w23)(x11x21x31)=Y=(y11y21)dY=(dy11dy21)=(ly11ly21)

求:

dW=(lw11lw12lw13lw21lw22lw23)dX=(lx11lx21lx31)

由多元函数链式法则:

lx11=ly11y11x11+ly21y21x11=dy11w11+dy21w21

同理可以算出其他值,整理即得结论


五、神经网络

接下来是关于神经网络(neural network)的学习。本章体量较大,大体思路为:神经网络的建模与结构,数据的预处理、正则化和损失函数,神经网络的动态部分(即神经网络学习参数和搜索最优超参数的过程),以及卷积神经网络。

1. 神经网络的建模与结构

1.1 与神经元的类比

先介绍神经元(neuron or unit)模型。在这个模型中,神经元接受到来自n个其他神经元传递过来的输入信号xi,这些输入信号通过带权重wi的连接进行传递,神经元接收到的总输入iwixi将减去阈值b,然后通过激活函数(activation function)f处理以产生输出f(iwixi+b)

这如同:树突将信号传递到细胞体,信号在细胞体中相加,如果最终之和高于某个阈值,那么神经元将会激活,向其轴突输出一个峰值信号。当然这只是一厢情愿的类比,真实的神经元复杂多了。

img

1.2 激活函数

img

总结:使用ReLU,设置好学习率,或许可以监控你的网络中死亡的神经元占的比例。如果单元死亡问题困扰你,就试试Leaky ReLU或者Maxout,不要再用sigmoid和tanh了。ReLU的变种其实表现和ReLU差不多的,提升1%的正确率已经了不起了,甚至还可能表现更糟糕一点。

1.3 结构

对于普通神经网络,最普通的层的类型是全连接层(fully-connected layer)。全连接层中的神经元与其前后两层的神经元是完全成对连接的,但是在同一个全连接层内的神经元之间没有连接。只有隐层需要激活函数,输入输出层不需要。下面是两个神经网络的图例,都使用的全连接层:

img

命名规则:当我们说N层神经网络的时候,我们没有把输入层算入。所以上图左边是一个两层神经网络,上图右是一个三层的神经网络。输入层、隐层、输出层。

确定网络尺寸。用来度量神经网络的尺寸的标准主要有两个:一个是神经元的个数,另一个是参数的个数,用上面图示的两个网络举例:

python代码

全连接层的前向传播一般就是先进行一个矩阵乘法,然后加上偏置并运用激活函数。

1.4 表达能力

这里不加证明地给出:具有一个隐层的神经网络就可以任意精度地近似一个连续函数。既然一个隐层就能近似任何函数,那为什么还要构建更多层来将网络做得更深?答案是:虽然一个2层网络在数学理论上能完美地近似所有连续函数,但在实际操作中效果相对较差。

另外,在实践中3层的神经网络会比2层的表现好,然而继续加深(做到4,5,6层)很少有太大帮助。卷积神经网络的情况却不同,在卷积神经网络中,对于一个良好的识别系统来说,深度是一个极端重要的因素(比如几十个可学习的层)。对于该现象的一种解释观点是:因为图像拥有层次化结构(比如脸是由眼睛等组成,眼睛又是由边缘组成),所以多层处理对于这种数据就有直观意义。

设置层的数量和尺寸

首先,要知道当我们增加层的数量和尺寸时,网络的容量上升了。即神经元们可以合作表达许多复杂函数,所以表达函数的空间增加。例如,如果有一个在二维平面上的二分类问题。我们可以训练3个不同的神经网络,每个网络都只有一个隐层,但是每层的神经元数目不同:

img

在上图中,可以看见有更多神经元的神经网络可以表达更复杂的函数。然而这既是优势也是不足,优势是可以分类更复杂的数据,不足是可能造成对训练数据的过拟合(overfitting)。而有3个神经元的模型的表达能力只能用比较宽泛的方式去分类数据。它将数据看做是两个大块,并把个别在绿色区域内的红色点看做噪声。在实际中,这样可以在测试数据中获得更好的泛化(generalization)能力。

基于上面的讨论,看起来如果数据不是足够复杂,则似乎小一点的网络更好,因为可以防止过拟合。然而并非如此,防止神经网络的过拟合有很多方法(L2正则化,dropout和输入噪音等),后面会详细讨论。在实践中,使用这些方法来控制过拟合比减少网络神经元数目要好得多。下图中,每个神经网络都有20个隐层神经元,但是随着正则化强度增加,它的决策边界变得更加平滑。

img

不要减少网络神经元数目的主要原因在于小网络更难使用梯度下降等局部方法来进行训练:虽然小型网络的损失函数的局部极小值更少,也比较容易收敛到这些局部极小值,但是这些极小值一般都很差,损失值很高。相反,大网络拥有更多的局部极小值,但就实际损失值来看,这些局部极小值表现更好,损失更小。

总之,不应该因为害怕出现过拟合而使用小网络。相反,应该进尽可能使用大网络,然后使用正则化技巧来控制过拟合。

2. 数据预处理、正则化和损失函数

2.1 数据预处理

均值减法(Mean subtraction):就是做零中心化(zero-centered)。把数据中每个独立特征减去平均值,从几何上可以理解为在每个维度上都将数据云的中心都迁移到原点,X-=np.mean(X,axis=0)。对于图像来说,我们可以对所有像素都减去一个值,X-=np.mean(X);也可以在3个颜色通道上,分别减去各自的均值。

归一化(Normalization):先对数据做零中心化处理,然后每个维度都除以其标准差,X /= np.std(X, axis=0)。做归一化能使得数据每个维度范围和贡献差不多。由于图像处理中像素的数值范围几乎是一致的(都在0-255之间),所以进行这个额外的预处理步骤并不是很必要。

img

之所以要做零中心化或归一化,课程中这样两个说法。1)输入值有正有负,避免本章1.2节讨论的z字形下降;2)考虑一条过原点的直线划分红蓝两类点(如下图),不中心化的点使得损失值对于直线的抖动更敏感、难以优化。类似的,神经网络某一层的输入均值不为0或者方差不为1,该层网络权重的微小扰动可能会导致该层输出的巨大扰动,从而导致学习困难。

image-20220710184507062

在图像数据预处理时,我们通常只做零中心化处理,有时还会再除以标准差。从训练集中求图片平均值,然后各个集(训练/验证/测试集)中的图像再减去这个平均值。下面是AlexNet、VGG和ResNet如何处理CIFAR-10数据的例子:

此外,为了介绍的完整性,常见的预处理还有PCA和白化。PCA(principal component analysis,主成分分析)先对数据进行零中心化处理,然后计算协方差矩阵,再对数据协方差矩阵进行奇异值分解,接着将零中心化后的数据投影到特征基准上(去除数据相关性),最后留下了数据中包含最大方差的多个维度,达到降维,从而节省训练时间和存储器空间。白化(whitening)把投影到特征基准上的数据除以该维度的特征值,来对数值范围进行归一化。这里不再深入探究,具体数学细节应该不简单。下图从左到右依次是原数据、去相关性数据和白化后的数据。

img

2.2 权重初始化

预处理完数据后,我们还需要初始化所有权重的值。核心目标是:通过合理的初始化权重,让网络在最初的训练过程中能合理地更新权重,而不是直接不学习。下面是几个想法。

全零初始化(错误)。这是一个错误的做法。因为如果网络中的每个神经元都计算出同样的输出,然后它们就会在反向传播中计算出同样的梯度,从而进行同样的参数更新。换句话说,如果权重被初始化为同样的值,神经元之间就失去了不对称性的源头。话是这么说,但是具体举个例子就很麻烦了。自己画了个简单的两层神经网络体会了一下,输入层为x1x2,隐藏层h1h2,输出层y1y2,会发现h1=h2、wx1->h2=wx1->h1、wx2->h1=wx1->h2、wh1->y1=wh2->y1、wh1->y2=wh2->y2(可能有误),反正具有对称性了,显然不是我们想要的。事实上,如果权重只要被初始化为同样的值,不一定要全零,就会产生这样的问题。

小随机数初始化。比如生成一个均值为0,标准差为0.01的正态分布:W = 0.01 * np.random.randn(Din, Dout)。对于浅层网络表现尚可,但是对于较深的网络有问题。下面是六层的神经网络,每层都有4096个神经元,都按照上述正态分布初始化权重,采用tanh激活函数,每层的输出如图:

image-20220411105035830

可见随着深度加深,输出将接近全零的情况,这也会导致权重的梯度接近全零(回忆到反向传播求梯度的过程中,有一项wixi求导得到xi=0,会使得梯度为0),网络也就不学习了。

若初始化分布改成标准差为0.05的正态分布:W = 0.05 * np.random.randn(Din, Dout)。会有以下结果:

image-20220411110209520

可见由于权重初始值太大,大部分激活函数都饱和了。这也使得局部梯度为0,权重梯度为0,网络也不再学习了。

Xavier初始化。使用1/sqrt(n)校准方差,将权重初始化为W = np.random.randn(Din, Dout) / np.sqrt(Din),也就是均值为0、标准差为1/sqrt(Din)的分布,会有以下结果:

image-20220411110648006

这样做的出发点在于想要输出的方差等于输出的方差。考虑y=iDinwixi推导如下:

D(yi)=Din×D(wixi) //w,x=Din×[E(xi2)E(wi2)E(xi)2E(wi)2] //w,x=Din×D(xi)D(wi) //xw

D(yi)=D(xi)D(wi)=1/Din,得证。然而注意到yi还要通过非线性的激活函数才能得到最终的输出,所以最终的输出的方差肯定不是输入的方差。特别地,如果采用非零中心的激活函数如ReLU,使用1/sqrt(n)校准方差不尽人意,每层结果如下:

image-20220411131624491

由上图可见,随着深度加深,ReLU的输出将越来越集中在0,同样阻碍了学习。对于ReLU,我们可以通过 sqrt(2 / Din)来校准标准差(Kaiming / MSRA initialization),也就是将权重初始化为:W = np.random.randn(Din, Dout) * np.sqrt(2 / Din),得到的每层结果如下:

image-20220411132139924

ResNet的权重初始化。对于卷积层,Din=kernel_size2×input_channels。Residual block的输入为x,通过两个卷积层输出为f(x),再加上x得到Residual block的输出f(x)+x。若对于两个卷积层的filter都进行MSRA初始化,那么有D(f(x)+x)>D(x)方差随着block增多而增大。可以仅对第一个卷积层进行MSRA初始化,第二个直接设置为0,这样Residual Block的输出就是x,能保证输出输入方差一致。

2.3 正则化

正则化被用来避免网络过拟合训练数据。之前提及的L1和L2正则化,以及批量归一化(第六章第五节)都是正则化的手段。这里主要是谈论dropout(随机失活)

dropout的核心想法是在前向传播时,随机设置一些神经元为0,通常可以设置失活概率为0.5。为什么dropout可行,课程中给出两种解释。一是减少神经元之间的共适应关系(co-adaptation),使得在丢失某些特定信息的情况下依然可以从其他信息中学到一些模式,增强鲁棒性。二是通过dropout我们训练了一些共享参数的网络全体。有用就行,我反正不明觉厉。

img

在预测的时候,为了保障对同一个输入的预测结果是确定的,肯定不能进行随即失活。但是我们想要预测时神经元的输出与训练时的预期输出是一致的,否则预测时就不是当初训练的神经网络了。以p=0.5为例,在预测时神经元必须把它们的输出减半,这是因为在训练的时候它们的输出的数学期望只有一半。就是说假设神经元的输出a=w1x+w2y,失活率为0.5,可以算出输出的数学期望E(a)=0.5(w1x+w2y),也就是在前面乘以失活率。于是有如下代码:

上述做法有一点小问题,就是在预测时还需要额外增加乘以p的操作,会造成一点性能浪费。不如在训练时除以p,这样预测时就不用乘以p了。有如下代码,称之为反向随机失活(inverted dropout)

AlexNet和VGG只在fc6和fc7(两个巨大的全连接层)做了dropout操作,而使用了global average pooling的GoogLeNet和ResNet等根本就不用dropout了。像ResNet只用了BN层和L2正则化。

此外,数据增强(data augmentation)也是很常见的做法。譬如将测试的图片翻转、随机裁剪(random crop)、改变颜色等等做法。对于小数据集,还常见cutout(把照片的一些部分用0代替)、mixup(把两张照片重叠,然后说这是40%的猫,60%的狗)。

正则化的方法很多,这里不一一列举了。

3. 动态部分和训练之后

3.1 学习率策略

下图中给出了学习率大小对损失值更新的直观理解。我们可以从较大的学习率开始学习,并逐渐降低学习率。接下来讨论几种降低学习率的策略。

image-20220418185219969

Learning Rate Decay:Step

在训练一定epoch(代)后(譬如每30代),降低学习率(譬如乘以0.1)。训练多少代、降低多少学习率也是超参数,需要一些尝试与调整。不过也可以采用启发式(heuristic)方案,即观察损失值平滑(plateau)后,降低学习率。

image-20220418185759971

Learning Rate Decay:Cosine

常见的方法。设置学习率αt为:

αt=12α0(1+cos(tπ/T))

其中α0为初始学习率,T为希望训练的epoch数。并没有引入更多的超参数,初始学习率原来就要设置,希望训练的epoch数一般越大越好,取决于你想要等待的时间。

Learning Rate Decay:Linear

常见的方法。设置学习率αt为:

αt=α0(1t/T)

Learning Rate Decay:Inverse Sqrt

并不那么常见。设置学习率αt为:

αt=α0/t

其潜在风险在于并没有保持一段时间的高学习率,而是迅速下降了。

当我们设置α0=1T=100时,cosine、linear和inverse sqrt函数图分别如下:

image-20220418193118122

Learning Rate Decay:constant

最常见的学习率设定了,也是推荐在你训练网络一开始采用的,可以避免一些不必要的麻烦。

3.2 训练多久?

观察损失值(下左)和正确率(下右)随着训练的变化。右图随着不断的训练会产生过拟合现象,需要在验证集正确率最高的点停止训练。实际操作中,可以设定一个最大epoch大小,然后每一定epoch数后设置检查点,记录下所有参数,最后看哪个检查点具有最高的验证集正确率。

image-20220418195215126

3.3 选取超参数

现在有好多超参数需要设置,有两个直接的方法:

random search可能比grid search更好,出于以下考虑:如果两个超参数一个影响大(下图绿线)、另一个影响小(下图橙线),我们更希望让影响大的超参数取到让绿线最高点的值,而不那么关心影响小的超参数。显然random search更容易找到使得绿线最高的重要超参数的值。

image-20220418203433902

3.4 选取超参数(无大量GPU加持)

3.3节介绍的方法其实在枚举各种超参数组合,耗时耗力。一些基本技巧可以帮助我们选取超参数。

  1. Check initial loss: Turn off weight decay (应该就是令正则化项为0), sanity check loss at initialization. e.g. log(C) for softmax with C classes

  2. Overfit a small sample: Try to train to 100% training accuracy on a small sample of training data (~5-10 minibatches); fiddle with architecture (譬如每层多少神经元), learning rate, weight initialization. Turn off regularization.

  3. Find LR that makes loss go down: Use the architecture from the previous step, use all training data, turn on small weight decay, find a learning rate that makes the loss drop significantly within ~100 iterations. 可以尝试1e-1, 1e-2, 1e-3, 1e-4的学习率。

  4. Coarse grid, train for ~1-5 epochs: Choose a few values of learning rate and weight decay around what worked from Step3, train a few models for ~1-5 epochs. 可以尝试的权重衰减(应该就是正则化项前的λ)为1e-4, 1e-5, 0。

  5. Refine grid, train longer: Pick best models from Step4, train them for longer (~10-20 epochs) without learning rate decay

  6. Look at learning curves: 也就是观察损失值和正确率(训练集和验证集)随着训练的变化

    • 一开始损失值不下降,后来突然开始下降——初始化做的不好
    • 损失值在较高的时候就平滑了——考虑使用learning rate decay
    • 损失值还在下降呢,突然陡然下降一点后就平滑了——过早引入learning rate decay
    • 训练集和验证集的正确率都还在不断上升——继续训练!
    • 训练集正确率上升且验证集正确率平滑或下降、两者之间的间隔越来越大——过拟合了,考虑增强正则化项、以及获取更多的测试数据
    • 训练和验证集中间几乎没有间隔——欠拟合了,考虑训练更久、更大的模型
  7. GOTO step5

最后还有两个建议

3.5 Model Ensemble

3.6 迁移学习

很常见的一个方法。迁移学习(transfer learning)简单来说就是拿来一些在大规模数据集上已经预训练(pre-train)好的模型,然后我把输出层外其他层需要学习的参数复制下来,输出层随机初始化,最后做一些微调(fine-tune)工作——可以设置底层的学习率相对较低甚至为零,高层的学习率相对较高,从而对网络进行学习。这样设置学习率是出于越接近底层的泛化能力越高。

微调可以加速收敛,并(有时)能提高正确率(因为预训练模型是在一个更大规模数据集上训练的)。

image-20220419091315921

Distributed Training和Large-Batch Training未整理,反正我们也没有几百个GPU


六、卷积神经网络

事实上课程中卷积神经网络是放在第五章第二节数据预处理、正则化和损失函数之前讲的,但是我的整理遵循了课堂笔记的顺序。于是将卷积神经网络部分放在了如何训练神经网络之后。

本章主要分为两个部分,首先探讨用来构建卷积神经网络的各种层(convolutional layer, pooling layer and normalization layer, fully connected layer),然后探讨现有的卷积神经网络架构(AlexNet, ZFNet, VGG, GoogLeNet, ResNet)。

1. 概述

常规神经网络如第五章1.3节所示,我们先把数据展开成向量,经过几个全连接层后,得到最终的评分。这个方案存在问题:将数据展开成向量会破坏原有的空间结构;需要学习的参数的数量会随着输入尺寸的增加而爆炸(显然的,想想权重的数量)。

卷积神经网络(convolutional neural network)是由层组成的。有以下四种可以构建卷积神经网络的层:

此外卷积层和全连接层对输入执行变换操作的时候,最后会用到激活函数,通常是ReLU。一个简易的卷积神经网络便可由[Conv ReLU Pool]反复叠加而成。下面将讨论卷积层、池化层和归一化层。默认情况下,讨论的例子还是CIFAR-10的数据集。

2. 卷积层

2.1 引入

image-20220405144255944

上图是一个3*5*5的filter,取照片中一块3*5*5的一块数据,与filter(也称作kernel)做没有对核翻转的离散卷积运算(也就是对应位置相乘后再求和,后文多称之为multiply-add操作)得到一个数值,得到的数值还需要再加上偏置b(一个filter一个偏置,是一个数)。易见filter划过整张照片会得到一个1*28*28的输出结果,称之为激活图(activation map)。之所以大小是1*28*28,是因为四周各有两个像素没法算出结果。

例子如下图:(输入3*4的数据,核大小为2*2,输出2*3,输出的每一个元素经历4次multiply-add运算)

image-20220407213307416

2.2 多个filter

进一步,我们采用多个不同的filter,一个输入可以得到更多的激活图;再若一批(batch)里面有多个输入,那么输出会进一步增加:(下图中,C: channel, H: height, W: width, K: kernel)

image-20220405150935393

2.3 加入ReLU

由于卷积层做的是线性运算,两个卷积层如果直接拼在一起,那么我们只会得到另一个卷积层。因此,一般情况下要在卷积层之间加上非线性的激活函数(通常就是ReLU):

image-20220405153319868

2.4 卷积层的filter学到了什么呢?

下面是AlexNet的一个例子:(AlexNet第一个卷积层中用了64个filter,每个filter大小是3*11*11)

image-20220405154026297

可见,filter常常学到oriented edges and opposing colors.

2.5 进一步讨论输出的大小

输出的channel大小等于filter的个数。不妨假设输入和filter都是正方形,输入边长为W,filter边长为K,那么输出的边长为WK+1.

为了避免输出尺寸缩小,需要在输入中增加padding,通常在输入数据的四周增加P圈0,称之为zero padding。此时输出的边长为WK+1+2P。通常设置P=(K1)/2,这使得输出尺寸等于输入,称之为same padding。

之前默认了filter在输入数据划动的步长(stride)是1,我们可以设置步长为S。那么输出的边长为(WK+2P)/S+1(去尾法舍入)

2.6 接受域(感受野)

接受域(receptive field)指影响到输出单元的所有输入单元的集合。有的时候接受域只指一个卷积层,输入数据的哪部分影响到输出的一个单元;有的时候指最初的输入照片的哪部分影响到这个输出值了,譬如:

image-20220405162707113

记L是卷积层的层数,K是filter的边长,那么接受域边长是1+L(K1)

两层的3 * 3filter的卷积层接受域与一层5 * 5的相同,1+2(31)=1+1(51)

Problem: For large images we need many layers for each output to "see" the whole image. Solution: Downsample inside the network. 上面提到的步长和之后要讨论的池化都是downsample的方法。

2.7 Summary

image-20220405164658952

注意到上面说kernel size是KH×KW忽略了通道数,默认是Cin

2.8 一道题目

image-20220405165754668

每个filter需要学习的参数个数是3*5*5+1(for bias) = 76, 有10个filter,所以总共要学习参数个数为760.

multiply-add操作就是之前所说的内积。

2.9 其他类型的卷积

目前我们讨论的是2D卷积。此外还有1D和3D的卷积。所谓维度应该可能是指输入数据除去通道维度后剩下的维度数。这里不再展开,只做了解。

3. 池化层

池化层(pooling layer)的作用是降低数据体的空间尺寸(downsample),从而减少网络中参数的数量,使得计算资源耗费变少,也能有效控制过拟合。下面是max pooling的例子:(在2*2的网格内取最大元素,网格移动的步长为2)

image-20220406203923160

有两点值得指出:

除了max pooling外,常见的还有average pooling。

总结如下:

image-20220406204818088

4. 案例LeNet-5

采取了经典的架构:[Conv,ReLU,Pool] x N, flatten, [FC, ReLU] x M, FC. (flatten是将张量展开成向量,FC是全连接层)。下面是LeNet-5的设计(1998年为解决文字识别而提出的卷积神经网络架构):

image-20220406205333847

(图中Weight Size最前面两个就是filter的大小,后面两个就是全连接层的权重矩阵大小)

表格其实已经体现出所有信息了,要看懂!

5. 归一化层

深网络很难训练,不容易收敛。而归一化层(normalization layer)则能帮助神经网络更快收敛,其中最常见的当属批量归一化(batch normalization,BN)。之所以可行,据说是帮助减少了“internal covariate shift”。在训练过程中,因为各层参数不停在变化,所以每个隐层都会面临covariate shift的问题,也就是在训练过程中,隐层的输入分布老是变来变去,这就是所谓的“Internal Covariate Shift”。于是,我们通过一定规范化手段,把每层输入值的分布强行拉回到均值为0方差为1的标准正态分布,就可以避免“internal covariate shift”了。事实上这还停留在经验层面,没有很多理论上的支持。

先来看看全连接层的批量归一化。

image-20220406214334594

进行一波上图的解释:输入数据有D个维度,一批有N个数据,所以输入是N*D的;xi,j是第i行第j列,所以μj就是第j个channel的平均值;同样σj2就是第j个channel的方差;x^i,j就是归一化后的xi,j,之所以分母还有个ϵ是避免分母为0;将输入硬生生拉到标准正态分配可能限制太强了,我们通常还会引入β,γ,特别的当γ=σ,β=μ时,yi,j=xi,j,这意味着训练得到这个BN层没有用。

再来看看卷积神经网络的批量归一化。

卷积神经网络每个激活图有一个γ,σ,β,μ。下图对比了全连接层和卷积网络的BN层区别:

image-20220406223445587

其实就是把每个channel(也就是每个激活图)的所有数值去做运算。

此外,需要强调的是训练时和测试时的BN层表现是不一样的。训练时μ,σ都是我通过这一批数据算出来的,而测试时μ,σ是给定的常数(取所有训练时的batch的平均值),这是为了避免测试的结果会受同一批其余数据影响。由于测试时,BN层不存在训练的参数了,变成了简单的线性运算,所以可以与先前的全连接层或者卷积层合并。

通常我们把BN层插入到全连接层和卷积层之后,在非线性运算前(譬如tanh激活函数)。

除了批量归一化,我们还有一些其他的归一化手段。如Layer NormalizationInstance Normalization(训练和测试表现一致,不再受同一批其余数据影响):

image-20220406225031910

image-20220406225322580

6. AlexNet

image-20220407204557774

输入227*227;一共八层;5层卷积层;max pooling;3层全连接层;ReLU激活函数;使用"local response normalization",但是目前不用了,也不做介绍;在两个GTX580上训练,由于GTX580只有3GB内存,所以模型要分到两个GPU上训练;激活函数还是采用了ReLU;损失函数使用softmax。具体如下(虽然我怎么感觉下表和上图有点对不上,但是问题不大,关键在于分析过程):

image-20220407205132268

选表格中几个数据分析(memory是指输出的大小,参数是指该层需要学习的参数,flop是指从输入到输出要做多少次floating point operations):

关于怎么提出AlexNet这样的架构的,可能是通过大量反复试错(trial and error)得到的。

分析每一层的存储大小、参数个数和flop运算,我们可以画出如下柱状图(具体结论也在图中显示了):

image-20220408091624774

7. ZFNet

其实就是更大的AlexNet,也是采用了5层卷积层,3层全连接层,除了:

计算量和存储量都大于AlexNet。

8. VGG

VGG和AlexNet对比如下(VGG应该是画错了,分别少画了一个和两个卷积层。VGG16有13层卷积层和3层全连接层;VGG19有15层卷积层和3层全连接层):

image-20220408150154851

VGG16每层大小具体为(max pooling层的通道数应该是上面标的数字的一半,其实看图片的宽度可以看出来):

一文读懂VGG网络

VGG的设计遵循以下设计规则:

下面对以上设计规则进行分析

1)All conv are 3X3 stride 1 pad 1

VGG所有卷积层的filter大小都是3*3的,而在AlexNet不同层有不同的filter大小——11 * 11、5 * 5和3 * 3。由本章2.6节可知,两层3 * 3的卷积层和一层5 * 5的卷积层接受域相同,而两层3 * 3只需要更少的需要学习的参数,并且在两层之中加入激活函数,给网络更多非线性元素以及更深的深度。

关于更少的参数。考虑Conv(5x5, C->C)和Conv(3x3, C->C)+Conv(3x3, C->C),其中C->C意味着输入数据是C通道的,输出还是C通道。前者需要25C2个参数、25C2HW次flop运算,而后者一共需要18C2个参数、18C2HW次flop运算。

这样kernel size不再是一个超参数,而是规定好的常数。

2)All max pool are 2X2 stride 2. After pool, double channels

考虑池化前后的卷积层:

image-20220408140918889

可见两者需要的flop次数相同。

关于如何在pool后double channels,应该是通过下一个卷积层实现的,不是通过池化层实现的,这样也会导致有些卷积层的输入和输出通道并不相同。

最后,关于AlexNet和VGG所需要的内存、参数和flop数有下面直观的对比。

image-20220408141549264

最后的最后,由于批量归一化尚未提出,为了使网络收敛,VGG训练时先是训练比较浅的版本,然后再在训练好的层之间插入新的层,继续训练。

9. GoogLeNet

主要讨论几个重要设计思想,核心在于节约且高效。

  1. stem network

    image-20220408145748326

    通过一些操作先把输入的长宽降到28,注意到VGG也将长宽降到28,而GoogLeNet的所需计算量更小。

  2. Inception Module

    image-20220408151310802

    要点:不同于VGG将kernel大小设置为3 * 3,GoogLeNet计算了四种不同的kernel size组合;inception module在GoogLeNet中反复出现;采用了1*1卷积层,来减小通道大小。最后这个filter concatenation应该就是简单地把多个激活图按照channel维度拼起来。

  3. Global Average Pooling

    image-20220408153056100

    回忆到,之前的模型大部分需要学习的参数在全连接层,于是GoogLeNet先用了kernel size等于last conv layer‘s spatial size的average pooling来得到一组神经元,再用一层全连接层得到输出层。

  4. Auxiliary Classifiers

    image-20220408154159394

    GoogLeNet提出时还没有批量归一化,让网络收敛很困难。于是GoogLeNet在网络两个地方分别增添了一个auxiliary classifier,也是做global average pooling来得到阶段性的评分。在训练时,权重的梯度将根据三个分类器综合得到。

10. ResNet

ResNet(残差神经网络)拥有了BN层的加持,可以训练更深(百层)的网络。但是当网络加深后,会发现深网络在训练和测试的表现可能均不佳,如下图:

image-20220408170204873

这是因为深网络存在underfitting的问题(注意到不是overfitting,否则训练的表现会更好)。按理说深网络有更多表现能力,从而表现得应该至少和浅网络一样好。退一步讲,把浅网络直接复制过来并在最后加上几个恒等层(identity,指该层的输入输出一致),从而构建出和浅网络一样性能的深网络。从这一点出发,我们能不能修改一下网络结构,能帮助网络学习到恒等层,这就是ResNet的一个重要设计思想。

譬如,在两个卷积层的输出结果F(x)上再加上x,当作最终的输出。当两个卷积层的核都设置为0,那么F(x)结果必然是0,那最终结果就是输入,也就是学到了恒等层。这样的一个组件称为residual block,是ResNet的基本组件。

image-20220408171113777

ResNet的基本设计如下图。首先用stem network来减小输入数据的尺寸,然后通过多个stage。每个stage由通道数区分,每个stage又由多个residual block组成,所有residual block的卷积核都是3 * 3大小的。最后通过global average pooling和一层全连接层得到输出层。

image-20220408171855604

在进一步加深ResNet的深度时,研究者又将"basic" residual block全替换为"bottleneck" residual block。设计如下图所示,可以看出flops会略少一点,此外网络会更深一点(每个block有3层):

image-20220408172905732

最后给出五种ResNet设计,以及计算量和ImageNet表现。

image-20220408173530003

关于每个block的ReLU层和BN层有以下两种设计。"pre-activation" ResNet block在测试中有百分之零点几的正确率提升。

image-20220408173915200

11. 其他

在ResNet之后又有更多的架构被提出,如ResNeXt、grouped convolution、squeeze-and-excitation network、densely connected neural network、tiny network(针对移动设备),以及对生成最优网络架构的网络研究(neural architecture search)。

12. 结论

目前不要自己设计神经网络。用ResNet-50或者ResNet-101就行了。如果还想要在移动设备上运行的话,可以看看MobileNets和ShuffleNets。


七、循环神经网络

循环神经网络RNN(recurrent neural network)能更好地处理序列的信息,即前面的输入和后面的输入是有关系的。细节因具体实现不同而不同,这里只是讲讲我理解的内容。先看一下其简单的结构。

1. 基本结构

image-20220626200510811

其中红色为输入层,绿色为隐藏层,蓝色为输出层。之前讨论的神经网络都是one to one的结构。one to many如image captioning: image->sequence of words; many to one如video classification: sequence of images->label; many to many (特别的有seq2seq结构)如machine translation: sequence of words->sequence of words、per-frame video classification: sequence of words->sequence of labels.

2. vanilla RNN

我们来看一下最简单的RNN实现。

image-20220626201528129

RNN有内部状态,在t时间记为ht(hidden state),且有关系式ht=fW(ht1,xt),其中xt是输入层的一个值,WWhh,Wxh,Why对所有的映射f保持一致。映射f就选择了tanh(可能出于历史原因)。应该还有bias项,但是式子里没有写出。

于是一个many to many的RNN就长这样:

image-20220626202440059

类似也可以得到one to many, many to one和seq2seq结构。特别的seq2seq结构其实是many to one + one to many:

image-20220626202618182

例子

我们来看一个image captioning的具体例子。

image-20220709144046921

首先将照片经过训练过的CNN网络得到特征向量。特征向量通过一个全连接层得到h0待用。我们用一个词汇表记录每个单词对应一个非负整数,其中还包含有<START>, <END>两个特殊token,分别标记着开始和结束。我的x输入是非负整数向量(因为每个单词对应一个非负整数),但是这样包含的信息就太少了,我们先把x通过一个映射变成浮点数矩阵再进行输入(一个整数对应一个浮点向量,上图没有体现)。

训练时:我们有图像captioning的ground truth值,将GT值作为输入x[t](整数向量),经过映射得到了对应的浮点矩阵,扔入隐层ht=tanh(Whhht1+Whxxt),再通过一个全连接层得到评分y,y再与GT做softmax loss。最后梯度下降更新权重矩阵。

测试时:将h0和<START>token输入,得到的yi包括了所有单词的得分,找最大的那个作为最终的单词。将单词作为xi+1继续输入,如此往复直到<END>token出现。

vanilla RNN 梯度流问题

image-20220626203310361

之前已经提到,W对所有的tanh都是一样的。这就导致了在反向传播时,乘以大量W,如果W的奇异值大于1,那么导致梯度爆炸,如果奇异值小于1,导致梯度消失。

3. LSTM

LSTM即Long Short Term Memory,可以解决vanilla RNN的梯度问题。

image-20220626204101410

上图中就是Hadamard product,也就是element-wise product,就是矩阵对应元素相乘。

LSTM内部状态在ht外新增了ct,也就是cell state。它有四个门,分别是:

在反向传播时,不再会产生多次W相乘所带来的梯度问题。

RNN还有很多变种,譬如Gated Recurrent Unit (GRU)。具体细节不再展开。

4. 注意力机制

4.1 Attention

先复习一下seq2seq模型。

image-20220627132017365

上图中编码器的输入序列为x1,...,x4,通过等式ht=fW(xt,ht1)可以计算出h1,...,h4。通过h4可以得到s0和c。解码器使用等式st=gU(yt1,st1,c)计算出s1,...,s4。特别的,y0是start token,y4是stop token。

问题在于,当输入序列很长时,最后的context vector c难以记住所有内容。针对此我们提出了attention机制。

image-20220627133354299

同样的,编码器的输入序列为x1,...,x4,通过等式ht=fW(xt,ht1)可以计算出h1,...,h4,通过h4可以得到s0。但是不同于之前的seq2seq,加入attention后,我们先通过等式et,i=fatt(st1,hi)计算出e11,...,e14。然后再把这些et,i扔到softmax,得到a11,...,a14。最后通过ct=iat,ihi计算出c1,而不是单纯使用c=hT。解码器通过等式st=gU(yt1,st1,ct)计算出s1。接着我们再次使用等式et,i=fatt(st1,hi)计算出e21,...,e24,扔到softmax得到a21,...,a24。再通过ct=iat,ihi计算出c2。如下图。如法炮制可以得到最后的输出序列y。

image-20220627134655827

对于上述操作有直观理解。 Context vector attends to the relevant part of the input sequence “comiendo” = “eating” so maybe a21=a24=0.05, a22=0.1, a23=0.8。

可以看到The decoder doesn’t use the fact that hi form an ordered sequence – it just treats them as an unordered set hi。于是,we can use similar architecture given any set of input hidden vectors hi。下面是一个用一句话描述图片的例子。可以看到我们利用CNN将图片生成了h1,1,...,h3,3,就可以使用上述模型了。

image-20220627135836951

4.2 Attention Layer

设计是看得懂,但是怎么想出来的暂时搞不懂。

image-20220705163015598

4.3 self-attention

self-attention layer和attention layer几乎一样,除了自注意力层的query输入也是由input vector生成

image-20220705163315162

可以发现虽然Yi是所有输入的函数,但是自注意力机制并没有记录位置信息,Yi就对应Xi。那处理序列肯定是不够的。于是我们还需要引入位置编码(position encoding)。位置编码将位置信息注入到输入里,也就是输入X已经包含了位置信息。具体位置编码这里不展开。

CNN with Self-Attention

image-20220705164702654

4.4 Three Ways of Processing Sequences

如图:

image-20220705164817355

重点:RNN并行度很差;卷积需要很多层才能看到长序列;自注意力一层就可以看到所有序列,但是对于长序列计算复杂度很高(不过可以高度并行)。

4.5 Transformer

transformer由多个transformer block组成,如图

image-20220705165609008

transformer在NLP中表现出色!但是计算量爆炸!


八、Visualizing and Understanding

本章试图探讨CNN不同层到底学到了什么。6.2.4我们已经给出了AlexNet中第一个卷积层filter表示的照片,但更高层的filter都不是三通道了,自然不能表示一个RGB照片。我们可以用灰度表示,但是结果不令人感兴趣。

大部分内容只是讲讲想法,不是很清楚。

有几个想法

最后,介绍了deep dream和neural style transfer. 挺有意思的两个东西。

image-20220629182612321


九、Detection、Segmentation

之前讨论的计算机视觉任务只是给一张图片做分类,但是计算机视觉任务远不止于此。

image-20220702152154642

1. Object Detection

1.1 Task Definition

1.2 Challenges

1.3 Detecting a Single Object

如果照片只有一个对象,我们有如下方案:

image-20220702152917485

即:拿来一个已经预训练好的网络,在原来的损失函数后再增加一个关于Box Coordinates的损失值,继续训练即可。这种多个损失值加权求和得到最终的损失值,我们称为multitask loss. 但是显然一张照片可能不止一个对象,这个简单的方案并不可行。

1.4 Detecting a Multiple Objects

Sliding Window

我们来看一个检测多个对象的想法:Sliding Window。固定一个大小为h*w的窗口,一个一个像素地划过大小为H*W的图片,每次判断这个窗口的内容是狗、猫还是背景。很显然有窗口数目太大的问题。并不可行

image-20220702153628125

Region Proposals

通过一些启发式的手段,在图像上找到可能的区域。其中Selective Search方法可以利用CPU在几秒内找到2000多个区域。具体细节不做了解。

1.5 R-CNN: Region-Based CNN

利用region proposals,我们可以有如下直观做法:

image-20220702155544496

  1. 原图跑region proposals得到很多区域
  2. 将这些区域缩放为相同大小
  3. 扔到CNN,得到分类分数和Bbox。所谓的Bbox就是指对region proposals得到的bounding box的修正(图中有一个修正方案)。
  4. 在测试时,可以用分类分数来扔掉一些候选区域

这些CNN共享权重。训练的时候,我们通过GT boxes将bounding boxes分成了positive、neutral和negative,然后根据这个分类进行训练。每个positive box又和一个GT box配对,这样就可以训练bounding box transformation了,最后用multitask loss得到损失值。

image-20220702191440683

1.6 Comparing Boxes: Intersection over Union (IoU)

如何评价预测的区域和ground truth之间的相近程度呢?我们有如下的办法:

image-20220702160546889

1.7 Overlapping Boxes: Non-Max Suppression (NMS)

Problem: Object detectors often output many overlapping detections.

image-20220702162313730

上图中,Object detectors找到了四个区域,它们是狗勾的可能性如图所示。可以看到同一只狗勾都有两个区域。如何排除关于同一个物体的多个区域呢,我们有如下Non-Max Suppression算法(一种贪心算法):

  1. Select next highest-scoring box
  2. Eliminate lower-scoring boxes with IoU > threshold (e.g. 0.7)
  3. If any boxes remain, GOTO 1

就是留下不怎么重叠且得分高的框,把与这些框重叠多的框去掉。

然而NMS算法存在一个小问题,考虑下面的图片:

image-20220702162958110

如果一个图片高度重叠,如何做object detection呢。目前没有非常好的方案。

1.8 Evaluating Object Detectors: Mean Average Precision (mAP)

知道如何评价预测的区域和ground truth之间的相近程度后,那我们如何评价一个Object Detector呢?我们采用mean average precision,具体如下:

  1. Run object detector on all test images (with NMS)

  2. For each category, compute Average Precision (AP) = area under Precision vs Recall Curve:

    1. For each detection (highest score to lowest score):

      1. If it matches some GT (groundtruth) box with IoU > 0.5, mark it as positive and eliminate the GT
      2. Otherwise mark it as negative
      3. Plot a point on PR Curve
    2. Average Precision (AP) = area under PR curve

  3. Mean Average Precision (mAP) = average of AP for each category

  4. For “COCO mAP”: Compute mAP@thresh for each IoU threshold (0.5, 0.55, 0.6,…, 0.95) and take average

image-20220702165936065

上图中,0.99、0.95和0.10三个区域分别和三个GT box匹配,0.9和0.5两个区域没有匹配。precision依次为1/1,2/2,2/3,2/4和3/5. recall依次为1/3,2/3,2/3,2/3和3/3.

1.9 Fast R-CNN

再看看1.5节介绍的R-CNN,它的速度相当慢。一张图片就有2000左右个区域,这些区域都要通过CNN。由于这些区域其实高度重叠,导致了太多冗余计算。

fast R-CNN先运行CNN,再进行warp(图像缩放)。如下图所示:

image-20220702172313097

fast R-CNN下大部分的计算都在backbone network里了,per-region network相对轻量。使得高度重叠的区域不会造成过多冗余计算。

下面着重了解一下如何crop(裁剪) features,也就是Region of Interest (Rols) Pool的具体内容。

image-20220702174046756

原图上的候选区域对应特征图(就是原图经过CNN生成的特征图)一块区域。将特征图划分成网格,将得到的对应区域snap(对齐)到网格。再进行max-pool得到固定大小的区域。但是这种对齐手段会:在特征图比原始图片尺寸小的情况下,一点点的精度损失映射到原始图片上就存在很大的像素点差别。于是有进阶版本ROI Align就是取消了snapping的操作,使用双线性内插的方法获得坐标未浮点数的像素点上的图像数值。具体细节不再展开(不过这门课的作业好像需要ROI Align哦,但是cs231n的作业没有R-CNN)。

1.10 Faster R-CNN

Fast R-CNN大部分时间花在了region proposal上。Faster R-CNN使用Region Proposal Network (RPN)来得到候选区域。Faster R-CNN的设计图如下:

image-20220702184449258

  1. 提取特征:输入固定大小的图片,进过卷积层提取特征图feature maps
  2. 生成region proposals: 然后经过Region Proposal Networks(RPN)生成region proposals。该层通过softmax判断anchors属于foreground或者background,再利用bounding box 回归修正anchors获得精确的proposals(候选区域)。
  3. ROI Pooling: 该层的输入是feature maps和proposals,综合这些信息后提取proposal feature maps
  4. Classification: 将Roi pooling生成的proposal feature maps分别传入softmax分类和bounding box regression获得检测物体类别和检测框最终的精确位置。

重点在于RPN的设计。特征图上设置几个anchor,训练一个小的神经网络判断anchor box是背景还是物体,这个神经网络还要预测box transform来改变anchor box的尺寸。使用K个不同的anchor box大小。。反正很不清楚的我觉得。

上述的方案称做two-stage object detector:

与之对应的还有one-stage object detector,差不多就是在判断anchor box是背景还是物体时,直接判断它是什么具体物体,不需要second stage了。

反正,目前one-stage的表现在逐步赶上two-stage,更深的backbone network表现更好。。。很复杂的一个话题,目前了解大概吧。

2. Semantic Segmentation

2.1 Task Definition

semantic segmentation(语义分割)即:给照片的每个像素打上标签,不区分实例,只关心像素点。譬如下图两头牛不区分哪些像素属于哪头的。

image-20220703131448485

2.2 Sliding Window

用一个滑动窗口遍历整个图片,每次给窗口的中心像素分类(通过CNN)。很直接的方法,但是十分低效。

2.3 Fully Convolutional Network

使用多个卷积层,将原始3*H*W大小的图片,变成C*H*W大小的scores,其中C代表分类的个数,也就是每个像素对每个分类有一个得分,总共有C个得分。最后取最大的那个得分作为该像素的分类。

image-20220703135046983

然而我们知道卷积层往往会做downsample的工作,也就是输出的长宽会比输入小。而我们上面的方案要求最终的scores矩阵的长宽和最初的输入图像相同。我们确实可以使用padding来让卷积层的输出长宽大小不变,但是一直保持长宽大小不变使得计算成本上升。这里我们就需要引入upsample的概念,让输出的长宽比输入大。FCN如下图:

image-20220703141135319

downsample有pooling,那么upsample就有unpooling。unpooling的几种方法:

image-20220703141404160

image-20220703141700509

上述unpooling方法都没有需要学习的参数。

downsample有卷积,那么upsample就有transposed convolution(转置卷积)。如下图所示:

image-20220703142247267

然而为什么上述称为转置卷积呢?和转置的关系是什么?下面试图解释。

考虑一维卷积的例子。输入向量a=(a,b,c,d),卷积核(x,y,z),步长为1,padding为1. 于是卷积操作可以用矩阵表示为下图左:

image-20220703145452073

如果我们把矩阵X转置后乘a,就有上图右的结果。这实际上就是转置卷积的结果。个人认为应该是现有上面转置卷积的方法,然后再有这个名字的。

3. Instance Segmentation

3.1 Task Defination

在明确定义前,需要明确things和stuff的概念。

之前讲的object detection就是只给出things,而语义分割则给出things和stuff。

于是实例分割就是指:Detect all objects in the image and identify the pixels that belong to each object (only things!)

image-20220703150806668

3.2 Mask R-CNN

想法也很直接,我们先做object detection,然后再predict a segmentation mask for each object。所谓的mask,我目前认为就是下面这种黑黑的东西:

image-20220703151625244

整个网络只用在R-CNN最后加上一个小CNN预测mask就好了。Mask R-CNN结构如下图:

image-20220703151441118

4. Beyond Instance Segmentation

实例分割又可以引申出好多计算机视觉任务。这里做一个简单的介绍。


十、3D Vision

这里只介绍两个3D视觉任务:

1. 3D Shape Representation

1.1 Depth Map

For each pixel, depth map gives distance from the camera to the object in the world at that pixel. RGB image + Depth image = RGB-D image (2.5D). 之所以称为伪3D,是因为Depth map并不可以给出物体被遮挡部分的数据。获取depth map比较简单,只需要通过一些特殊的3D传感器就行了。

image-20220704202508229

Predicting Depth Maps

即如何从原图预测depth maps,可以通过Fully Convolutional network。损失函数采用L2 Distance比较预测图片和ground truth图片每个像素差距,这是不可行的。这是因为通过一张图片,我们无法分辨远处的大物体和近处的小物体,它们在照片上可能是一样大的。正确的做法是采用Scale invariant loss function(如果所有预测的深度和ground truth有一个倍数差,那么loss认为0),具体函数如下:

image-20220704204910952

Surface Normals

For each pixel, surface normals give a vector giving the normal vector (法向量) to the object in the world for that pixel. 下图中不同颜色表示了不同方向的法向量。

image-20220704205054889

同样用FCN进行预测,损失函数用预测法向量和ground truth法向量的夹角就可。同样我们也是没法给出物体被遮挡部分的数据。

1.2 Voxel Grid

Voxel(体素)就是3D空间的像素。Voxel Grid即: represent a shape with a V*V*V grid of occupancies. 优势:简单。劣势:如果想要精致地捕捉图像,需要更多的小voxel。

image-20220705083447955

Voxel Grid -> Label

先用3D卷积层,再flatten,最后用全连接层得到class scores。此时Voxel Grid表示为一个四维张量,其中第四维为1(即1*V*V*V),我们可以通过元素值为1或者0来表示该位置有没有方块。之所以要第四维,是因为3D卷积的要求。

Input image -> Voxel Grid

Scaling Voxels

注意到,Voxel Grid所占的内存空间是巨大的,譬如一个10243大小、数据类型为float32的Voxel Grid就要4GB内存。有一些小技巧来缩小voxel grid所用的内存空间。

1.3 Implicit Surface

似乎是用一个隐函数来表示物体表面。这个隐函数就是神经网络。我输入一个三维坐标,这个神经网络告诉我这个坐标在物体里还是物体外。更多细节不讨论。

1.4 Pointcloud

image-20220705093600021

优点:Can represent fine structures without huge numbers of points

缺点:点是无限小的东西,所以pointcloud并没有显式地表现出物体。如果我们想要表示出物体的话,需要将这些点放大。

Pointcloud -> Label

image-20220705094215191

Input image -> Pointcloud(课堂上skip了)

image-20220705094605560

Loss Function

Chamfer distance

对于所有的蓝色点,找到其最近的橙色点,计算它们的距离,并求和。对于所有的橙色点,找到其最近的蓝色点,计算它们的距离,并求和。两个和相加即为最终的损失值。

image-20220705095021347

1.5 Mesh

即:用一组三角形来表示3D shape。如下图:

image-20220705095632644

优点:可以表示多种图形;在节点添加数据,可以给表面上色、增加材质等等。

缺点:Nontrivial to process with neural nets

Pixel2Mesh

即:输入照片,输出mesh。如下图:

image-20220705101706901

下面介绍四个key ideas

2. Metrics

就是如何比较预测值和ground truth之间的接近程度。

image-20220705105640579

3. Camera Systems

就是输入的物品视角是正视的(canonical coordinate)还是随意的(view coordinate)。反正view coordinate泛化更好,测试正确性更高;canonical coordinate数据更容易导入,训练时容易过拟合。

4. Datasets

最后讲了讲mesh R-CNN,差不多就是在mask R-CNN的基础上,将物品生成mesh。它似乎不再是通过Ellipsoid Mesh(Pixel2Mesh采用)生成最终的mesh,而是先做voxel grid,再生成mesh。


十一、Videos

视频可以被表示为一个四维张量:T*3*H*W (or 3*T*H*W). 其中T是时间维度。视频分类的主要任务是识别动作,譬如跑、跳、吃等动作。视频往往大小过大,于是在训练时我们使用帧数低的片段,在测试时使用原始视频的片段。如下图:

image-20220705134435550

下面给出几种神经网络方案。

1. Single-Frame CNN

对每帧分别扔到CNN中,最后将每帧的分类分数平均一下,得到最终的分类结果。虽然这种方案丢失了每帧之间联系的信息,但是却表现尚可,可以作为视频分类的基准。

image-20220705135100459

2. Late Fusion

有两种方案

之所以称为late fusion是因为每帧图片先各自通过CNN,再扔到一起。虽然有一点学习每帧之间联系的味道,但是味道不浓,目测还是难以捕捉到low-level motion between frames。

3. Early Fusion

把时间维度并到通道维度里,也就是把T*3*H*W的张量reshape成3T*H*W. 然后扔到2D CNN中。看上去可以较好学习每帧之间的联系。但是把时间维度并到通道维度挺aggressive的。进一步说这种操作是No temporal shift-invariance的,因为filters extend over the entire length in time, then if we want to detect changes in color at different times, we need to learn separate filters. (2时刻从蓝色转变为橙色,3时刻从蓝色转变为橙色,就需要两个不同的filter,而3D CNN只需要一个filter就行了,因为这个filter本身就会在时间轴上遍历一遍)

image-20220705140517164

4. 3D CNN (Slow Fusion)

也就是直接用3D CNN就行了。

image-20220705140942423

之所以称为slow fusion是因为receptive field的时间维度是随着时间慢慢增加的。

image-20220705143012574

C3D: The VGG of 3D CNNs

采用VGG的设计理念,把其中的2D CNN改成3D CNN就可以了。表现不错,但是计算量爆炸。

5. Two-Stream Networks

two-stream network使用了像素的运动信息。

Measuring Motion: Optical Flow

光流的具体计算我们不关心,只需只要光流给出像素的运动信息。

image-20220705151446769

Separating Motion and Appearance: Two-Stream Networks

two-stream分别指spatial stream和temporal stream。temporal stream采用了early fusion的方法。表现比之前的方法更好。

image-20220705151646609

6. Modeling long-term temporal structure

So far all our temporal CNNs only model local motion between frames in very short clips of ~2-5 seconds. What about long-term structure?

CNN+RNN

可以预先训练好CNN网络,把CNN当作特征提取器,也就是反向传播时不再更改CNN的值。

image-20220705153614246

Recurrent Convolutional Network

就是把之前的RNN中的矩阵乘法改成卷积

image-20220705153925627

Spatio-Temporal Self-Attention (Nonlocal Block)

即:把第七章的4.3节的2D卷积改成3D卷积。

image-20220705170651344

最后把这些nonlocal block通过3D卷积层连起来就好了

Inflating 2D Networks to 3D (I3D)

想法是把现有的2D CNN架构,修改成3D的。稍微具体来说:


十二、生成模型

1. Supervised vs Unsupervised Learning

之前我们讨论的所有内容都归于监督学习(supervised learning)。监督学习和无监督学习(unsupervised learning)的区别如下:

image-20220706203029034

2. Discriminative vs Generative Models

可以通过学习到的概率分布的不同,将模型分成判别模型(discriminative model)生成模型(generative model)

3. 分类

通过模型是否能计算p(x)、计算值是不是精确值可以将生成模型分类。我们本章只讨论三个生成模型。

image-20220706210230752

4. Autoregressive Models

Goal: Write down an explicit function for p(x)=f(x,W)。可见x的概率p(x)是输入x和权重矩阵W的函数。

Input: 输入样本x(1),x(2),...,x(N)

Loss Function: 采用最大似然估计(maximum likelihood estimation)即最优的权重矩阵W满足:

image-20220706212730694

(回忆最大似然估计,存在即合理,想让采样的样本呈现的概率最大)

Model

通过贝叶斯公式可以将p(x)写成:(x是一张照片的话,那么xi就可以是一个像素)

image-20220706213103384

可以看到p(x)的展开式和RNN的结构十分相似。

PixelRNN

image-20220706213443871

可以看到所有的点都和左边和上边的点相关联。softmax over [0,1,...,255]似乎是指先把所有像素做softmax(注意到softmax范围是0~1),再乘以255.

PixelCNN

image-20220706215156801

PixelCNN比PixelRNN快一点,因为它不是一个像素一个像素算的。但是两个网络都是要顺序执行,都很慢。

它们生成的图片远处看看像是张图片,仔细看看什么都不是。

5. (Regular, non-variational) Autoencoders

在看变分自编码器(variational autoencoder, VAE)之前,我们先看非变分自编码器,也就是常规的自编码器(autoencoder, AE)。自编码器是一种无监督学习模型,希望从输入数据x中提取潜在的特征zlatent features)。在训练时,我们还需要引入一个decoder(解码器),将编码器学习到的特征z变成x^,损失函数就是x^x之间的L2距离。可见整个系统要学习的其实就是一个恒等函数。

image-20220707085046950

训练完后,把decoder扔掉,encoder可以用来初始化一些监督学习模型。

不过自编码器只是提取特征,我们也不是很清楚这些特征意味着什么,也没法生成新的图片。在实际操作中,自编码器表现的并不是那么好。

6. Variational Autoencoders

并不能很好的理解这节内容,下面的整理可能有所纰漏

变分自编码器引入了概率,我们一方面可以从原始数据x中学习潜在的特征z,另一方面可以根据特征z生成新的数据。在训练后,decoder生成新数据的方法类似于:

z是特征值,满足概率分布pθ(z)θ是概率分布中需要学习的参数,*号意味着ground truth值。输入z后,通过神经网络得到x在z下的条件概率分布pθ(x|z(i)),并计算出像素的均值μx|z和和协方差矩阵x|z,这个协方差矩阵是对角阵(除了主对角线上的元素外,其他元素都是0,主对角线上元素就是方差),也就是意味着像素和像素之间是互相独立的(注意到这是一个人为假设)。最后根据均值μx|z和和协方差矩阵x|z,按照高斯分布得到最终的数据x。

image-20220707093218643

如何训练这个模型呢?基本思想是想要最大化训练数据的可能性(最大似然)。于是我们需要计算出输入数据x的概率分布p(x)。一个想法是通过如下积分计算:

pθ(x)=pθ(x,z)dz=pθ(x|z)pθ(z)dz

pθ(x|z)可以通过decoder网络算出,pθ(z)我们假设它是个高斯分布。但是真正麻烦的是这里对z的积分,这是一个不知道多少维度的积分,不具备可行性。

另一个想法是通过贝叶斯公式:

pθ(x)=pθ(x|z)pθ(z)pθ(z|x)

同样我们可以计算pθ(x|z)pθ(z),但是没法计算pθ(z|x). 但是我们可以设法通过一个encoder网络计算出pθ(z|x)的近似值qϕ(z|x)。也就是我们要同时训练一个decoder网络和一个encoder网络,也就是:

image-20220707104620213

其实我没理解qϕ(z|x)怎么就成为pθ(z|x)的近似值了。不如直接看下面的数学推导,qϕ(z|x)直接是通过分子分母同乘而引入的

更具体来说:

首先用贝叶斯公式有:

image-20220707111740320

两边同时对z求条件数学期望(z满足qϕ(z|x)的概率分布)有:

image-20220707111950363

注意到上面计算用到了一个数学事实:

Ezqϕ(z|x)[logpθ(x)]=logpθ(x)p(z)dz=logpθ(x)

此外还用到了KL散度的数学定义。

上述结果中有pθ(z|x)项,我们没法计算,但是我们知道KL散度一定非负。于是有logpθ(x)的下界:

image-20220707112627167

其实我们上述的做法就是所谓的变分推断(variational inference),求的下界称为变分下界(variational lower bound)目前有点不明觉厉。反正我们现在要训练encoder和decoder两个网络,使得这个变分下界最大。

那这两个网络具体长什么样呢?这里给出一个例子:

image-20220707131141272

具体训练过程为:

image-20220707135830810

先把输入数据x扔到encoder里得到latent code z在x下的条件分布qϕ(z|x)。KL散度要求这个条件分布尽可能和z的先验概率p(z)相近。我们最初认为z的先验概率p(z)就是标准正态分布。得到latent code z后,将它扔到decoder里,得到x在z下的条件分布pθ(x|z)Ezqϕ(z|x)[logpθ(x|z)]希望decoder输出的x和encoder输入的x类似(?)。

变分下界中,蓝色框希望latent code包含足够多的信息来重建data,绿色框则希望latent code不复杂,尽可能接近标准正态。

应用

反正上述工作生成的图片比较模糊,不过速度挺快的。

7. GAN

GANgenerative adversarial networks(生成对抗网络)。GAN由辨别器(discriminator,D)和生成器(generator,G)组成。生成器G输入latent变量z,生成一张图片。辨别器D则判断输入的照片是真实的还是生成的。生成器的目标是尽可能愚弄辨别器,辨别器的目标是尽可能分辨照片的真假。当然我们的最终目标是得到一个以假乱真的生成器,它生成的图像满足的概率分布pG收敛于真实的图像分布pdata

我们共同训练生成器G和辨别器D,目标函数是:

minGmaxD(Expdata[logD(x)]+Ezp(z)[log(1D(G(z)))])

辨别器希望对所有真实数据输出1、对所有伪造数据输出0;生成器希望完全愚弄辨别器,也就是对于伪造数据,辨别器也输出1。这些都在目标函数的minmax体现了。

将目标函数记作V(G,D),那么有如下梯度更新策略:

image-20220707150732076

看上去训练挺简单,但事实上最初版本的GAN相当难训练,我们很难知道什么时候训练结束。打个比方,现在有一个造假币的和一个警察。如果警察侦察技术太高,造假币的还没能更新技术就被抓走了;如果造假币的技术太高,警察都不会发现有假币,造假币的也就会不思进取;唯有两者相辅相成,才能互相促进,我们才会有完美的造假技术。

接下来,我们想证明当pG=pdata时,目标函数达到最优。

image-20220707153650604

第一个等号是做了还原,第二个等号是把期望写成定义式且把max放到积分号里面。通过令导数为0,得到辨别器DG(x)的最优解,记为DG(x).

image-20220707154011616

DG(x)带入,再把积分写成期望的形式,并做了一点数学操作。

image-20220707154229174

先后使用KL散度的定义和JS散度的定义,将等式改写。再根据JS散度的性质得证。

当下基于GAN的网络生成图像的表现非常非常好,关于GAN的论文也成千上万。


十三、强化学习

我们已经讨论过了监督学习和无监督学习,这章我们讨论强化学习(reinforcement learning)的一些基本内容。强化学习的简单定义和目标如下:

image-20220707164514253

1. What is reinforcement learning

image-20220707181146898

在t时刻,agent看到了state st,st可能是噪音或者不完整的;当agent得到状态后,执行action at;最后environment再通过reward rt告诉agent它表现的怎么样。action会导致environment的变化,agent也会根据它看到的state和收到的reward来不断学习。

举两个例子

image-20220707182015333

image-20220707220422531

Reinforcement Learning vs Supervised Learning

如果把state看作input、action看作prediction、reward看作loss,似乎强化学习和监督学习就一样了。但事实上两者有以下本质上的不同:

2. Q Learning

马尔可夫决策过程(MDP)强化学习中常见的一种数学形式。考虑一个tuple (S,A,R,P,γ)

之所以称这个tuple是MDP,是因为它满足马尔可夫性质(Markov property):The current state completely characterizes the state of the world. Rewards and next states depend only on current state, not history. 现实中常见的例子如走迷宫,我们在一个分叉路口的决策(朝哪个方向走)会导致环境的变化,反应为位置的更新。但新的位置只取决于当前位置和当前走的方向,与之前几步毫无关联(即如何来到当前位置不影响下一步的环境)。

Goal

agent根据policy π和状态,来给出action的分布。它的目标是找到最优的π是的累计discounted reward tγtrt最大。其中γ是[0,1]范围内的衰减因子。可以看出这个奖励其实是当前实时奖励和未来可能奖励的和,只不过未来可能的奖励有一个衰减因子。

具体有:

image-20220707201020542

Finding Optimal Policies

模型具有太多随机性(譬如最初状态,状态转变的可能性,奖励等),所以我们需要找的π是使tγtrt的数学期望最大的方案。即:

image-20220707202005530

Value Function and Q Function

image-20220707202139137

Q函数就是初始状态为s、初始动作为a、采用π policy的奖励累计和的期望。

Bellman Equation

image-20220707202410525

Optimal Q-function是采用了最佳的策略π,使得奖励累计和期望最大。Bellman等式(由定义易得)给出了一个递推关系即:Q*等于当前能给你的立即奖励加上未来奖励的衰减。

下面不加证明的给出两个事实:

image-20220707214253139

于是就有Deep Q-Learning(用深度神经网络来模拟Q函数)

image-20220707214642176

譬如之前的第一节的Atari games,我们有如下的神经网络:(4个action对应着上下左右四个动作)

image-20220707220543863

3. Policy Gradients

已经在数学中迷失了自我

image-20220707215107904

image-20220707215253062

image-20220707215504344

image-20220707215715973


最后一堂课是总结与展望,无整理