1.1 概述
助教老师讲了讲课程的重要性和整体内容。
1.2 历史背景
1.2.1 history of cv
5.4亿年前化石表明动物已经具备眼睛,物种大爆炸。
现在的视觉称为了最重要的感知能力。大脑中50%的神经细胞在处理视觉信息。
16世纪,第一台针孔理论相机。目前相机是最受欢迎的传感器之一。
人类开始研究动物视觉:电生理学研究,猫实验,看看是啥能刺激视觉中枢。
1966,麻省理工学院,视觉论文《THE SUMMER VISION PROJECT》,视觉信息被简化成简单的形状。
70年代,David Marr的书《VISION》,原始草图重建。
70年代,出现了广义圆柱体和图形结构,将复杂的视觉信息简单化。
80年代,基于简单图形进行识别和重建任务。
直接识别任务太难,先做分割任务。
2006,富士相机实时面部检测。
新世纪初,SIFT被提出。
机器学习出现,暴露问题:过拟合、模型维度高,不好泛化。建立大型数据集ImageNet。2009开始ImageNet组织目标检测竞赛,统一量化基准。2012卷积神经网络出现,获得冠军。
1.3 课程介绍
聚焦于图像分类问题,基于ImageNet挑战赛。拓展到目标检测、图片摘要
卷积神经网络CNN已经成为大赛的主流。2012年AlexNet出现,是一个7层的网络。2014VGG、GoogleNet。实际上CNN的雏形1998年就已经出现,LeCun大佬。后来用的多了,取决于:
-
算力提升
-
数据多了
现在视觉任务貌似偏向了目标检测,这并不只是视觉的主要内容,实际上还有分割、分类、3D理解等。举例:
-
feifei博士期间,实验:人看图半秒即可描述一段话;
-
人看到图像上的东西会有笑的反应。
实际上是借助了已有知识去理解了图像。说明到这种程度cv还有很长路要走。
老师说他相信:CV Can Better Our Lives.
本课的老师:feifei, Justin, Serena, &so on
2 图像分类
2.1 数据驱动方法
需要的知识:Python+NumPy, Google Cloud
问题与挑战
图像分类是cv的core task,计算机给图像分配一个标签。但是计算机只能看到一堆像素数值,面临的问题就是“语义鸿沟”。比如给目标换一个角度拍摄、换一个同类的目不同的背景信息......像素数值会发生巨大变化,人眼识别是鲁棒的,机器也要做到这样。
我们的任务
希望训练时间长,检测时间短
传统方法
比如边缘检测
数据驱动的方法
-
收集数据与标签
-
训练(机器学习的过程) train
-
在新的图上评估/预测 predict
数据集举例
-
CIFAR10
10类别,5w训练图像,1w测试图像。32x32 px图像
用于比较图像相似性的损失函数
-
L1:曼哈顿距离
-
L2:欧氏距离
最近邻的问题
不好处理数据中的噪音,所以出现了K近邻,使用多数投票,分类边界变得平滑
2.2 KNN
KNN原理:
通过重构距离函数可以把KNN泛化到不同的数据类型上
L1依赖于坐标,向量的每个值有确定意义时,适合使用L1。否则可以两种都尝试一下,找到最好的。
超参设置
-
idea1:所有数据选择同一个参数,K越小在训练集上性能越好,但是到测试集上需要调大K。这样就会出现过拟合,过分的贴合训练数据,降低了泛化能力。
-
idea2:split data into train and test,选择不同参数,导致测试集很好,训练集不行了。
-
idea3:split data into train, val and test,train用一个超参,val和test用一个超参,选择val上最好的参数用于test
-
idea4:交叉验证,传统机器学习常见,DL不咋用
学生提问1:val和test区别?
答:val是有标签的,通过在val上测试,与标签比较,修正算法的正确率,而test是没有标签的,用于观察预测结果或者实际应用。
提问2:test不能代表现实中的样本,咋办?
答:数据集是独立同分布的,现实不是,所以创建数据集的时候应该考虑随机性。
所以,KNN在图像分类上很少使用,原因:
-
test时间长
-
距离很难判断像素差异(不好找损失函数)
-
维度灾难,难度指数增长
提问:绿点和蓝点表示啥?
答:代表不同类数据。维度越高,需要越多的数据点来填满空间,所以是指数倍增长。
2.3 线性分类
比如:输入图像经过模块f,输出十个数中的一个,代表十个类别中的一个。
深度学习的主要工作就是构建高性能的f。
线性分类器:
线性分类的模型简单,但是泛化能力差,只允许学习一个类型的一个模板。比如分类任务中,学习车的前脸,无法从车的侧面图判断是否是一辆车。
一个二维图像,经过特征提取后映射为高维空间中的一个点,只需在高维空间将点线性分类。而在低维空间中,两类是无法线性分开的。
3 损失函数和优化器
损失函数
来衡量我们对结果的不满意程度,当评分函数输出结果与真实结果之间差异越大,损失函数输出越大,反之越小。
我们对于预测训练集数据分类标签的情况总有一些不满意的,而损失函数就能将这些不满意的程度量化。
多类SVM
SVM的损失函数想要SVM在正确分类上的得分始终比不正确分类上的得分高出一个边界值Δ。
多类SVM“想要”正确类别的分类分数比其他不正确分类类别的分数要高,而且至少高出delta的边界值。如果其他分类分数进入了红色的区域,甚至更高,那么就开始计算损失。如果没有这些情况,损失值为0。我们的目标是找到一些权重,它们既能够让训练集中的数据样例满足这些限制,也能让总的损失值尽可能地低。
正则化
我们希望能向某些特定的权重W添加一些偏好,对其他权重则不添加,以此来消除模糊性。这一点是能够实现的,方法是向损失函数增加一个正则化惩罚(regularization penalty)R(W)部分。
减轻模型复杂度,防止过拟合(over-fitting)。
Softmax
在Softmax分类器中,函数映射保持不变,但将这些评分值视为每个分类的未归一化的对数概率,并且将折叶损失(hinge loss)替换为交叉熵损失(cross-entropy loss)。公式如下:
SVM和Softmax的比较
Softmax分类器为每个分类提供了“可能性”:SVM的计算是无标定的,而且难以针对所有分类的评分值给出直观解释。Softmax分类器则不同,它允许我们计算出对于所有分类标签的可能性。举个例子,针对给出的图像,SVM分类器可能给你的是一个[12.5, 0.6, -23.0]对应分类“猫”,“狗”,“船”。而softmax分类器可以计算出这三个标签的”可能性“是[0.9, 0.09, 0.01],这就让你能看出对于不同分类准确性的把握。
优化器
最优化
损失函数可以量化某个具体权重集W的质量。而最优化的目标就是找到能够最小化损失函数值的W的过程 。
参数更新需要有技巧地设置步长。也叫学习率。如果步长太小,进度稳定但是缓慢,如果步长太大,进度快但是可能有风险。
-
随机搜索:Acc=15.5%
随机尝试很多不同的权重,然后看其中哪个最好。每走一步都尝试几个随机方向,如果某个方向是向山下的,就向该方向走一步。
# 假设X_train的每一列都是一个数据样本(比如3073 x 50000) # 假设Y_train是数据样本的类别标签(比如一个长50000的一维数组) # 假设函数L对损失函数进行评价 bestloss = float("inf") # Python assigns the highest possible float value for num in xrange(1000): W = np.random.randn(10, 3073) * 0.0001 # generate random parameters loss = L(X_train, Y_train, W) # get the loss over the entire training set if loss < bestloss: # keep track of the best solution bestloss = loss bestW = W print 'in attempt %d the loss was %f, best %f' % (num, loss, bestloss)
-
随即本地搜索:Acc=21.4%
从一个随机W开始,然后生成一个随机的扰动δW ,只有当W+δW的损失值变低,我们才会更新。这个过程的具体代码如下:
W = np.random.randn(10, 3073) * 0.001 # 生成随机初始W bestloss = float("inf") for i in xrange(1000): step_size = 0.0001 Wtry = W + np.random.randn(10, 3073) * step_size loss = L(Xtr_cols, Ytr, Wtry) if loss < bestloss: W = Wtry bestloss = loss print 'iter %d loss is %f' % (i, bestloss)
-
跟随梯度
前两个策略中,我们是尝试在权重空间中找到一个方向,沿着该方向能降低损失函数的损失值。其实不需要随机寻找方向,因为可以直接计算出最好的方向,这就是从数学上计算出最陡峭的方向。这个方向就是损失函数的梯度(gradient)。在蒙眼徒步者的比喻中,这个方法就好比是感受我们脚下山体的倾斜程度,然后向着最陡峭的下降方向下山。
梯度下降
现在可以计算损失函数的梯度了,程序重复地计算梯度然后对参数进行更新,这一过程称为梯度下降,他的普通版本是这样的:
# 普通的梯度下降
while True:
weights_grad = evaluate_gradient(loss_fun, data, weights)
weights += - step_size * weights_grad # 进行梯度更新
随机梯度下降:
随机采样一部分训练集样本,计算梯度并实现下降过程。
4 反向传播与神经网络
反向传播
反向传播是利用链式法则递归计算表达式的梯度的方法。
导数与梯度
牢记这些导数的意义:函数变量在某个点周围的极小区域内变化,而导数就是变量变化导致的函数在该方向上的变化率。
梯度∇f是偏导数的向量,所以有
链式法则
复合函数求导
# 设置输入值
x = -2; y = 5; z = -4
# 进行前向传播
q = x + y # q becomes 3
f = q * z # f becomes -12
# 进行反向传播:
# 首先回传到 f = q * z
dfdz = q # df/dz = q, 所以关于z的梯度是3
dfdq = z # df/dq = z, 所以关于q的梯度是-4
# 现在回传到q = x + y
dfdx = 1.0 * dfdq # dq/dx = 1. 这里的乘法是因为链式法则
dfdy = 1.0 * dfdq # dq/dy = 1
前向传播从输入计算到输出(绿色),反向传播从尾部开始,根据链式法则递归地向前计算梯度(显示为红色),一直到网络的输入端。可以认为,梯度是从计算链路中回流。
sigmoid函数:
一个计算实例
x = 3 # 例子数值
y = -4
# 前向传播
sigy = 1.0 / (1 + math.exp(-y)) # 分子中的sigmoi #(1)
num = x + sigy # 分子 #(2)
sigx = 1.0 / (1 + math.exp(-x)) # 分母中的sigmoid #(3)
xpy = x + y #(4)
xpysqr = xpy**2 #(5)
den = sigx + xpysqr # 分母 #(6)
invden = 1.0 / den #(7)
f = num * invden # 搞定! #(8)
# 回传 f = num * invden
dnum = invden # 分子的梯度 #(8)
dinvden = num #(8)
# 回传 invden = 1.0 / den
dden = (-1.0 / (den**2)) * dinvden #(7)
# 回传 den = sigx + xpysqr
dsigx = (1) * dden #(6)
dxpysqr = (1) * dden #(6)
# 回传 xpysqr = xpy**2
dxpy = (2 * xpy) * dxpysqr #(5)
# 回传 xpy = x + y
dx = (1) * dxpy #(4)
dy = (1) * dxpy #(4)
# 回传 sigx = 1.0 / (1 + math.exp(-x))
dx += ((1 - sigx) * sigx) * dsigx # Notice += !! See notes below #(3)
# 回传 num = x + sigy
dx += (1) * dnum #(2)
dsigy = (1) * dnum #(2)
# 回传 sigy = 1.0 / (1 + math.exp(-y))
dy += ((1 - sigy) * sigy) * dsigy #(1)
# 完成! 嗷~~
用向量化操作计算梯度
# 前向传播
W = np.random.randn(5, 10)
X = np.random.randn(10, 3)
D = W.dot(X)
# 假设我们得到了D的梯度
dD = np.random.randn(*D.shape) # 和D一样的尺寸
dW = dD.dot(X.T) #.T就是对矩阵进行转置
dX = W.T.dot(dD)
神经网络简介
神经网络是将一些简单的函数以分层的方式堆叠起来,以组成更复杂的非线性函数。
一个三层的神经网络可以类比地看做,其中W1,W2,W3是需要进行学习的参数。
神经网络的结构
一些神经元的输出是另一些神经元的输入,通常神经网络模型中神经元是分层的。
神经元通过全连接层连接,层间神经元两两相连,但是层内神经元不连接;
分层的结构能够让神经网络高效地进行矩阵乘法和激活函数运算
讨论了更大网络总是更好的这一事实。然而更大容量的模型一定要和更强的正则化(比如更高的权重衰减)配合,否则它们就会过拟合。在后续章节中我们讲学习更多正则化的方法,尤其是dropout。
5 卷积神经网络
NN的结构是一系列的层将输入数据变换为输出数据。常规NN隐藏层都为全连接层,构建大网络时效率低下,容易过拟合。而CNN调整结构为卷积层、池化层、全连接层。
层
卷积层 (conv)
负责提取特征图片,卷积核为n*n矩阵,通常自己生成然后通过反向传播修正。
-
卷积核:卷积层的参数是有一些可学习的小卷积核集合构成的,卷积核尺寸为(num是前一层的深度),前向传播时每个卷积核都与输入数据在宽高深三个维度做离散卷积运算。
-
特征图:又称为激活图,一个卷积核与输入数据卷积会生成一个二维激活图,每个卷积层上有多个卷积核也就有多个激活图,将激活图在深度方向上层叠起来就生成了输出数据。
-
局部连接:每个神经元只与输入数据的一个局部区域连接,该连接的空间大小叫做神经元的感受野(receptive field)。
-
参数
-
输入数据体的尺寸为
-
4个超参数:滤波器的数量K;滤波器的空间尺寸F;步长S;零填充数量P
-
输出数据体的尺寸为,其中:
-
由于参数共享,每个滤波器包含个权重,卷积层一共有个权重和K个偏置。
-
在输出数据体中,第d个特征图(空间尺寸是),用第d个滤波器和输入数据进行有效卷积运算的结果(使用步长S),最后在加上第d个偏差。
-
对这些超参数,常见的设置是F=3,S=1,P=1。
-
激活层
将卷积层的结果做非线性映射。
池化层
负责降低特征图片维度,从而降低运算量,压缩数据和参数的量,用于减少过拟合。常用的有最大池化、均匀池化。
反向传播:回顾一下反向传播的内容,其中max(x,y)函数的反向传播可以简单理解为将梯度只沿最大的数回传。因此,在向前传播经过汇聚层的时候,通常会把池中最大元素的索引记录下来(有时这个也叫作道岔(switches)),这样在反向传播的时候梯度的路由就很高效。
全连接层
连接整个输入向量。在全连接层中,神经元对于前一层中的所有激活数据是全部连接的,这个常规神经网络中一样。
-
任何全连接层都可以被转化为卷积层,最大的局部等于全局。比如,一个K=4096的全连接层,输入数据体的尺寸是,这个全连接层可以被等效地看做一个F=7,P=0,S=1,K=4096的卷积层。换句话说,就是将滤波器的尺寸设置为和输入数据体的尺寸一致,这样输出就变成,本质上和全连接层的输出是一样的。
结构
卷积神经网络通常是由三种层构成:卷积层,汇聚层(除非特别说明,一般就是最大值汇聚)和全连接层(简称FC)
指的是一个可选的汇聚层,通常0<=N<=3,M>=0,K>=0,通常K<3。
6 训练神经网络1
激活函数
建议:用ReLU非线性函数。注意设置好学习率,监控网络中死亡的神经元占的比例。如果单元死亡问题困扰你,就试试Leaky ReLU或者Maxout。
数据预处理
零中心化:它对数据中每个独立特征减去平均值,从几何上可以理解为在每个维度上都将数据云的中心都迁移到原点。而对于图像,更常用的是减去对应图像的均值而不是每个维度的均值,也可以在3个颜色通道上分别操作。理解:初始化时参数为0均值,线性分类器经过原点,能更快速地分类零中心的数据。
归一化(即标准化):数据在每一维度的数值范围都近似相等,通常有两种方式。第一种是先对数据做零中心化处理,然后每个维度都除以其标准差,相当于对每个维度上的数据做标准化。第二种方法是对每个维度都做归一化,使得每个维度的最大和最小值是1和-1。在图像处理中像素的数值范围几乎是一致的,归一化不是很必要。
PCA:过线性变换将原始数据变换为一组各维度线性无关的表示,可用于提取数据的主要特征分量,常用于高维数据的降维, 通常使用PCA降维过的数据训练线性分类器和神经网络会达到非常好的性能效果,同时还能节省时间和存储器空间。
白化:白化操作的输入是特征基准上的数据,然后对每个维度除以其特征值来对数值范围进行归一化。该变换的几何解释是:如果数据服从多变量的高斯分布,那么经过白化后,数据的分布将会是一个均值为零,且协方差相等的矩阵。
注意:任何预处理策略(比如数据均值)都只能在训练集数据上进行计算,算法训练完毕后再应用到验证集或者测试集上。在实际操作中,只会做零中心化处理。
参数初始化
不能将所有参数设为0, 因为如果网络中的每个神经元都计算出同样的输出,然后它们就会在反向传播中计算出同样的梯度,从而进行同样的参数更新,神经元之间就失去了不对称性的源头;从代价函数的角度来说, 参数初始化又不能太大,因此权重初始值要非常接近0又不能等于0。
小随机数初始化
过小的数值使得传播的数据分布在0处,那么在反向传播的时候就会计算出非常小的梯度, 出现梯度消失问题;过大的数值使得传播的数据分布在饱和区,同样出现梯度消失问题。
使用校准方差
随着输入数据量的增长, 随机初始化的神经元的输出数据的分布中的方差也在增大。可以推算出:,根据, 只要在权重前面乘上系数就可以。而对于ReLU神经元,一半的数据分布被消减导致方差减半,应改为乘上系数。
稀疏初始化(Sparse initialization)
将所有权重矩阵设为0,但是为了打破对称性,每个神经元都同下一层固定数目的神经元随机连接,其权重数值由一个小的高斯分布生成。
偏置(biases)的初始化
通常将偏置初始化为0,这是因为随机小数值权重矩阵已经打破了对称性。对于ReLU非线性激活函数,用0.01这样的小数值常量作为所有偏置的初始值能让所有的ReLU单元一开始就激活,就能保存并传播一些梯度, 但是这样做是不是总是能提高算法性能并不清楚。
推荐是使用ReLU激活函数,并且使用w = np.random.randn(n) * np.sqrt(2.0/n)
(Xavier init)来进行权重初始化
批量标准化BN
在网络的每一层之前都做预处理, 使得数据服从标准正态分布,将全连接层或卷积层与激活函数之间添加一个BatchNorm层, 使用了批量归一化的网络对于不好的初始值有更强的鲁棒性
由于BN操作也就是标准化是一个简单可求导的操作, 所以对于整个网络依然可以利用梯度下降法进行迭代优化。
批量归一化(Batch Normalization)
批量归一化可以理解为在网络的每一层之前都做预处理,只是这种操作以另一种方式与网络集成在了一起。搞定!
损失
数据损失是一个有监督学习问题,用于衡量分类算法的预测结果(即分类评分)和真实标签结果之间的一致性。公式为:中,N是训练集数据的样本数,相当于对所有样本的数据损失求平均。
分类问题
每个样本数据具有唯一的真实标签
SVM分类器的折叶损失:
有些学者的论文中指出平方折叶损失效果更好
Softmax分类器的交叉熵损失:
当类别的数目非常庞大的时候, 就需要使用 Hierarchical Softmax , Hierarchical Softmax将标签分解成一个树, 每个标签都表示成这个树上的一个路径,这个树的每个节点处都训练一个Softmax分类器来决策左树还是右树, 树的结构对于算法的最终结果影响很大,而且一般需要具体问题具体分析
属性分类
当是一个二值向量,每个样本具有一个或多个属性,而且属性之间并不相互排斥。
1、对每个属性创建一个独立的二分类的分类器
在上式中,
-
表示第j个类别的第i个属性的真实值, 的值为1或者-1
-
当该类别被正确预测并展示的时候,分值向量fjfj为正,其余情况为负
-
当一个正样本的得分小于+1,或者一个负样本得分大于-1的时候就会累计损失值
2、对每种属性训练一个独立的逻辑回归分类器
回归问题
通常是计算预测值和真实值之间的损失, 然后用L2平方范式或L1范式度量差异。如果有多个数量被预测了,要对预测的所有维度的预测求和。
注意:L2损失要比Softmax损失优化起来困难很多, 因为L2要预测一个真实的确切值, 而Softmax是一种概率意义上的预测, 还有一点就是L2对于异常值来说会导致很大的局部梯度, 所以在回归问题中, 我们依然优先考虑可否转化为分类问题去解决, 比如如果对一个产品的星级进行预测,使用5个独立的分类器来对1-5星进行打分的效果一般比使用一个回归损失要好很多。分类还有一个额外优点,就是能给出关于回归的输出的分布,而不是一个简单的毫无把握的输出值。如果确信分类不适用,那么使用L2损失吧,但是一定要谨慎。
结构化预测
结构化损失是指标签可以是任意的结构,例如图表、树或者其他复杂物体的情况。通常这种情况还会假设结构空间非常巨大,不容易进行遍历。结构化SVM背后的基本思想就是在正确的结构和得分最高的非正确结构之间画出一个边界。解决这类问题,并不是像解决一个简单无限制的最优化问题那样使用梯度下降就可以了,而是需要设计一些特殊的解决方案,这样可以有效利用对于结构空间的特殊简化假设。
7 训练神经网络2
Fancier optimization优化方法
训练神经网络的核心问题是对损失函数的优化问题。优化方法有:随机梯度下降法(Stochastic Gradient Descent,SGD),带动量的SGD,AdaGrad,RMSProp,Adam等等。
随机梯度下降的问题:
损失在一个方向很敏感,另一个方向的改变很小! 锯齿形状
参考:
正则化
通过控制神经网络的容量来防止其过拟合
迁移学习
迁移学习(Transfer learning) 顾名思义就是把已训练好的模型参数迁移到新的模型来帮助新模型训练。
深度学习的模型可以划分为 训练 和 预测 两个阶段。
-
训练 分为两种策略:一种是白手起家从头搭建模型进行训练,一种是通过预训练模型进行训练。
-
预测 相对简单,直接用已经训练好的模型对进行预测即可。
迁移学习有几种方式
1)Transfer Learning :冻结预训练模型的全部卷积层,只训练自己定制的。
2)Extract Feature Vector :先计算出预训练模型的卷积层对所有训练和测试数据的特征向量,然后抛开预训练模型,只训练自己定制的简配版全连接网络。
3)Fine-tune :冻结预训练模型的部分卷积层(通常是靠近输入的多数卷积层),训练剩下的卷积层(通常是靠近输出的部分卷积层)和全连接层。
第一种和第二种训练得到的模型本质上并没有什么区别,拿到新数据集,想要用预训练模型处理的时候,通常都会先用上面方法一或者方法二来看看预训练模型在新数据上的表现怎么样,摸个底。如果表现不错,还想看看能不能进一步提升,就可以试试Fine-tune,进一步解锁卷积层以继续训练模型。
8 深度学习软件
CPU&GPU
(我看网上说最新的cs231n已经介绍TPU了)
深度学习用的GPU是Nvidia的。
框架
-
TF
-
PyTorch
最近也在跟着小土堆二刷Pytorch入门,就先用这个框架吧,之前做毕业也在环境搭建上踩过坑了。
9 CNN案例
AlexNet
VGG
深网络,小卷积核,定期池化
创新点:
-
使用了更多的卷积层,更多地使用3x3卷积核代替5x5卷积核(本质上是更大、更深的AlexNet)
-
采用了模块化设计:将多个卷积层组合成块,多个VGG块后连接全连接层,不同次数的重复块即可得到不同的架构VGG-16、11、19等,网络实现与管理更为方便简洁,有效减少代码量,奠定了今天的网络模型大多分为五个stage的雏形。
GoogLeNet
提升网络性能最直接的办法就是增加网络深度和宽度,这也就意味着巨量的参数。但是,巨量参数容易产生过拟合,也会大大增加计算量,因此GoogleNet设想在不提高参数的情况下,增加网络深度
创新:
-
设计出Inception块,使得在不增加参数数量的情况下,将网络变得更深
-
同NiN一样使用全局平均汇聚层,在传入全连接层之前,将每个通道的高和宽变成1,大大减少了展平后的参数数量。
-
训练时增加了两个辅助分类器(预测时去掉),使用网络得以更稳定的训练(现在有了更好的训练方法,这个特性或许不是必要的)
ResNet
学习输入、输出之间的残差,即H ( x ) − x H(x)-xH(x)−x。X -> (H(X) - X) + X。其中X这一部分为直接的identity mapping,而H(X) - X则为有参网络层要学习的输入输出间残差。 增加网络层次时,减少损失函数的衰减、损失。
10 循环神经网络
RNN具有记忆功能,其输出与之前的状态和当前的输入有关,适合处理基于序列的数据。
网络传播机制
正向传播
网络在t时刻接收到输入之后,隐藏层的值是,输出值是。关键一点是,的值不仅仅取决于,还取决于。可以看出,循环神经网络的输出值,是受前面历次输入值、、、、...影响的,这就是为什么循环神经网络可以往前看任意多个输入值的原因。
反向传播
同样利用链式法则,梯度沿两个方向传播,一个方向是其传递到上一层网络,另一个是方向是将其沿时间线传递到初始时刻,相比CNN过程更加复杂。
梯度爆炸和消失
RNN在训练中很容易发生梯度爆炸和梯度消失,这导致训练时梯度不能在较长序列中一直传递下去,从而使RNN无法捕捉到长距离的影响。
通常来说,梯度爆炸更容易处理一些。因为梯度爆炸的时候,我们的程序会收到NaN错误;也可以设置一个梯度阈值,当梯度超过这个阈值的时候可以直接截取。
梯度消失更难检测,而且也更难处理一些。总的来说,我们有三种方法应对梯度消失问题:
-
合理的初始化权重值。初始化权重,使每个神经元尽可能不要取极大或极小值,以躲开梯度消失的区域。
-
使用relu代替sigmoid和tanh作为激活函数。
-
使用其他结构的RNNs,比如长短时记忆网络(LTSM)和Gated Recurrent Unit(GRU)
LSTM
为解决原始RNN无法处理长距离依赖的问题,长短时记忆网络(LSTM)采用两种状态:h(对短期的输入敏感)、c(保存长期的状态),如下图:
在t时刻,LSTM的输入有三个:当前时刻网络的输入值、上一时刻LSTM的输出值、以及上一时刻的单元状态;LSTM的输出有两个:当前时刻LSTM输出值、和当前时刻的单元状态。注意x、h、c都是向量。
11 应用:分割、定位、检测