卷积神经网络学习笔记

卷积神经网络学习笔记

参考资料

卷积神经网络基本概念

吴恩达深度学习课程

吴恩达深度学习相关资料

Table of Contents generated with DocToc

概述

首先 ,卷积神经网络CNN能做什么呢?

一个简单的例子 它能将一个二维像素点组成的图形进行识别 判断是”x” or “ o”

当然被判断的二维像素点图像不是非常规整的 可能是经过旋转,平移,或需要从更复杂的情况下辨别出 到底是 “x” 还是 “o”。

除了CNN卷积神经网络还有其他的深度学习神经网络

那么CNN是如何将一副原始像素点图转化成可处理对象 并判断它的?

下面的流程图展示了CNN大致的训练流程

根据这张流程图 下面将详细展开每一块的具体操作

反向传播过程

反向传播实际上就是通过梯度下降来反馈调节向前传播的参数达到学习目的

逻辑回归:用于拟合离散函数并根据输入预测相应的离散函数值

梯度下降:通过对代价函数的分析运算寻找到更合适的$w,d$ 的反馈学习过程

向前传播是不断迭代逻辑回归的过程,而反向传播是使用梯度下降更新参数$w,b$的过程

key words: 逻辑回归, 激活函数, 损失函数, 代价函数, 梯度下降法

逻辑回归(Logistic Regression)

线性回归是根据给定关系推测出一个连续函数,如给出一组房价-面积关系推测出一个连续函数拟合数据,

而逻辑回归通常用于拟合离散函数并根据输入预测相应的离散函数值或者说分类问题, 例如判断邮件是否为垃圾邮件。

$w$是一个$nx$维向量(因为$w$实际上是特征权重,维度与特征向量相同)

比如 上一层特征向量大小为(nx,m)那么$w^T$的大小(r,nx)

$b$是一个实数(表示偏差)

而$x$就是输入的训练数据集/上一层特征向量

我们想让$yhat$ 通过计算得到的预测值 它表示[0,1]的概率

在二分分类中$yhat=P(y=1|x)$ 表示在给出样本$x$的前提下 通过DNN判断出$y=1$的概率

比如对于正确值(lable)$y=1$的样本$P(y=1|x)$应该取向于1

而对于(lable)$y=0$的样本$P(y=1|x)$应该取向于0

所以需要一个sigmoid函数做映射转换

逻辑回归的代价函数

为了训练逻辑回归模型的参数参数$w$和参数$b$我们,需要一个代价函数,通过训练代价 函数来得到参数$w$和参数。$b$为了让模型通过学习调整参数,你需要给予一个$m$样本的训练集,这会让你在训练集上 找到参数$w$和参数$b$,来得到你的输出 。

我们在二分类中用到的损失函数是:$𝐿(𝑦hat, 𝑦) = −𝑦log(𝑦hat) − (1 − 𝑦)log(1 − 𝑦hat)$

(没注明log的底数默认为以e为底) 这个公式实际上叫做交叉熵函数。

当目标值(正确值)𝑦 = 1时损失函数$𝐿 = −log(𝑦hat)$,如果想要损失函数𝐿尽可能得小,

那么𝑦hat就要尽可能大,因为 sigmoid 函数取值[0,1],所以𝑦hat会无限接近于 1。

当目标值(正确值)𝑦 = 0时损失函数$𝐿 = −log(1 − 𝑦hat)$,如果想要损失函数𝐿尽可能得小,

那么𝑦hat就要尽可能小,因为 sigmoid 函数取值[0,1],所以$𝑦hat$会无限接近于 0。

一般我们用预测值和实际值的平方差或者它们平方差的一半,但是通常在逻辑回归中我们不这么

做,因为当我们在学习逻辑回归参数的时候,会发现我们的优化目标不是凸优化,只能找到多个局

部 最优值,梯度下降法很可能找不到全局最优值,虽然平方差是一个不错的损失函数,但是我 们在

逻辑回归模型中会定义另外一个损失函数。

梯度下降法

梯度下降法可以做什么?

在测试集上,通过最小化代价函数(成本函数)𝐽(𝑤, 𝑏)来更新参数𝑤和𝑏,

𝐽(𝑤, 𝑏)是一对每组训练数据的损失函数Loss function 的均值

在逻辑回归问题中Loss function使用$𝐿(𝑦hat, 𝑦) = −𝑦log(𝑦hat) − (1 − 𝑦)log(1 − 𝑦hat)$

在实践中,𝑤可以是更高的维度,但是为了更好地绘图,我们定义𝑤和𝑏,都是单一实数。

代价函数(成本函数)𝐽(𝑤, 𝑏)是在水平轴𝑤和 𝑏上的曲面,因此曲面的高度就是𝐽(𝑤, 𝑏)在某一点的函数值。

我们所做的就是找到使得代价函数(成本函数)𝐽(𝑤, 𝑏)函数值最小值的参数𝑤和𝑏。

通过梯度下降逐步找到最低点对应的参数𝑤和𝑏。理想情况下, 无论在哪里初始化, 成本函数将收敛至同一个最低点。

但通常𝐽(𝑤, 𝑏)函数构成的梯度图像存在多个局部最低点,这导致不同的初始化参数设置会使参数𝑤和𝑏最终收敛的位置存在差异。

简化模型固定d的值讨论𝐽(𝑤) 和𝑤, 梯度下降实际上就是不断迭代𝑤使得它接近最低点。

其中α表示学习率 具体的体现在每次迭代更新的”步长” 之后会介绍如何给定一个合适的学习率。

虽然学习率α通常固定, 但是由于𝐽(𝑤)逐渐靠近最低点, 其偏导数(斜率)也逐渐趋近于0, 使得代价函数逐渐收敛至最低点。

一般习惯上在代码实现中将𝐽(𝑤, 𝑏)对𝑤的偏导(偏导将另一个无关变量视为定值参数)写作dw

同理将𝐽(𝑤, 𝑏)对𝑏的偏导写作db

逻辑回归与梯度下降

回想一下逻辑回归的公式定义如下: $𝑦hat = 𝑎 = 𝜎(𝑧) $其中$𝑧 = 𝑤^𝑇𝑥 + 𝑏$ , $𝜎(𝑧) = 1/(1+𝑒^{−𝑧})$

  • 损失函数有许多种可以参考此处

损失函数: $𝐿(𝑦hat, 𝑦) = −𝑦log(𝑦hat) − (1 − 𝑦)log(1 − 𝑦hat)$

代价函数:$ 𝐽(𝑤, 𝑏) = 1/𝑚 ∑ 𝐿(𝑦hat^𝑖 , 𝑦 ^i ) $

梯度下降法中𝑤和𝑏的修正量可以表达如下 :

$𝑤: = 𝑤 − 𝑎×𝜕𝐽(𝑤,𝑏)/𝜕𝑤 $,$𝑏: = 𝑏 − 𝑎×𝜕𝐽(𝑤,𝑏)/𝜕𝑏 $

假设现在只考虑单个样本的情况,单个样本的代价函数定义如下:

$𝐿(𝑦hat, 𝑦) = −𝑦log(𝑦hat) − (1 − 𝑦)log(1 − 𝑦hat) $

$yhat=a$ 是逻辑回归的输出(预测值)$y$ 是标签值(正确值)

这个计算图中假设一组训练数据$x$中是有两个值{x1,x2}的矩阵 同时$w$也是一个有两个元素{w1,w2}的矩阵

现在模拟求解一组样本的梯度下降如何计算 ,已知输入(或初始化)x1,w1,x2,w2,b

目标是求$w1,w2,b$的梯度下降, 即更新$w1=w1-a×dL/dw1$,$w2=w2-a×dL/dw2$,$b=b-a×dL/db$

具体过程:

1.求$d𝐿(𝑎, 𝑦)/d𝑎$,在编写代码时变量名写作$𝑑a$

已知 $𝐿(𝑦hat, 𝑦) = −𝑦log(𝑦hat) − (1 − 𝑦)log(1 − 𝑦hat) $ and $yhat=a$ ($log$表示以e为底即$ln$)

通过微积分得到: $𝑑𝐿(𝑎,𝑦)/𝑑𝑎 = −𝑦/𝑎 + (1 − 𝑦)/(1 − 𝑎)$

2.求$d𝐿(𝑎, 𝑦)/dz$ 写作$dz$

通过链式法则得到$d𝐿(𝑎, 𝑦)/dz=dL(a,y) / da × da / dz$

其中 $da/dz$,又有$a=𝜎(z)$

所以就是求对sigmoid 函数求导, 已知$𝜎(𝑧) = 1/(1+𝑒^{−𝑧})$ ,sigmoid函数简写做$𝜎$

那么$d𝜎/dz=𝜎(z)*(1-𝜎(z))$ , 具体求导过程

已知 $dL(a,y) / da$ 那么经过化简得$d𝐿(𝑎, 𝑦)/dz=a-y$

3.求$d𝐿(𝑎, 𝑦)/dw1$(写作dw1),$d𝐿(𝑎, 𝑦)/dw2$(写作dw2),$d𝐿(𝑎, 𝑦)/db$(写作db)

注意w1,w2是矩阵$w$中的两个元素

根据链式法则$dL(a,y)/dw1=dz/dw1× dL(a,y)/dz$

$dz/dw1=x1$ 且通过第2步已经求得$d𝐿(𝑎, 𝑦)/dz$(写作”$dz$”)为$(a-y)$

那么”$dw1$”=$x1×dL(a,y)/dz$ 同理可求得”$dw2$”,”$db$”

$dwi=xi×(a-y)$ ,$db=(a-y)$

4.更新$w1=w1-a×dL/dw1$,$w2=w2-a×dL/dw2$,$b=b-a×dL/db$

以上是一个样本时的情况

下面在m个样本时的情况

首先全局代价函数:$ 𝐽(𝑤, 𝑏) = 1/𝑚 ∑ 𝐿(𝑦hat^𝑖 , 𝑦 ^i ) $,实际上时对每个样本损失量求平均

那么一个样本时求$dL(a,y)/dw1$ 在m个样本时就相当于求$dJ(w,b)/dw1$

对于$w$中的一个元素$w1$ ,全局代价函数对$𝑤1$的微分,也同样是各项损失对$𝑤1$微分的平均。

所以在m个样本中求$dwi$ 以及$db$ 就是求各项样本中的损失对它们微分的平均

伪代码实现

1
2
3
4
5
6
7
8
9
10
11
12
J=0;dw1=0;dw2=0;db=0;
for i = 1 to m #枚举样本
z[i] = wx[i]+b;
a[i] = sigmoid(z[i]);
J += -(y[i]log(a[i])+(1-y[i]*log(1-a[i]));
dz[i] = a[i]-y[i];
for indx = 1 to nx #枚举这个样本中每个特征值
dw[indx] += x[indx][i]*dz[i]; #x[indx][i]表示第i个样本的第indx元素或特征值
db += dz(i);
J/= m;db/= m;
for indx = 1 to nx
dw[indx]/= m;

然后更新$w1$,$w2$,….$wn$ (矩阵w) 和$b$

$wi=wi-a×dwi$

$b=b-a×db$

向量化

在向前传播过程中计算$Z[layer]=W[layer]^TX[layer]+b[layer]$

$X$是由多个大小为(1,nx)的单样本$x$ 组成 下图直观的表示了向量化(矩阵计算)

$W$的维度(r,nx) r可以变化在输出层中r=1(二分类问题)那么$W[layer]^TX[Layer]+b[layer]$ 就等于一个实数

如何优化for循环使得逻辑回归中的矩阵运算加速

在python的numpy库 可以直接进行向量运算

1
2
3
4
5
6
7
8
z=0
///for循环模拟矩阵运算
for i in range(n_x)
z+=w[i]*x[i] ///假设为(1,nx)的w矩阵和(nx,1)的x矩阵相乘
z+=b

///多线程并行模拟矩阵运算
z=np.dot(w,x)+b

这个方法加速矩阵运算的原理是让CPU/GPU运行SIMD指令 让它们能够并行运算

或者说同时计算矩阵中对应元素相乘 并得到结果,而非逐个遍历相乘再相加

上面展示了用numpy.dot()计算逻辑回归

$w$是一个(r,nx),(这里假设r=1)的列向量 $X$是由多个$x^i$ 组成的训练集大小为 (nx,m)

在使用numpy库中的函数Z=numpy.dot(w.T,X) + b , $Z$也是一个(1,m)大小的矩阵

原本b是一个实数当他与向量相加时它被自动转化成对应大小的矩阵

使用向量化计算逻辑回归&梯度下降

已知矩阵$𝑎 = 𝜎(𝑧)$和真实值构成的矩阵$y$ ,通过求导化简得到的公式$dz=a-y$ 求得(1,m)大小的$dz$矩阵

而之前用for循环累加模拟矩阵运算$dw$ 实际上是 (nx,m)的矩阵$X$ 乘 (m,1) 的矩阵$dz^T$ (T表示转置)

for循环实现和.dot函数实现对比

省略掉代价函数$J$的计算 因为原来计算代价函数是为了求导$dw$等其他需要的参数,

后来通过求导化简发现可以忽略掉代价函数(与它无关)的计算直接求得$dw$ 等其他需要的参数所以就省略了

损失函数的解释

回顾$yhat=P(y=1|x)$ 所以$P(y|x)$ (这个概率表示给定样本x时正确预测出为y的概率)可以如上表示

如果要省去if表达式则可以用指数函数表示 $P(𝑦|𝑥) = 𝑦hat^𝑦 × (1 − 𝑦hat)^{(1−𝑦)} $

我们想衡量这个$P(𝑦|𝑥)$ 概率的大小 对它取log

则有$logP(𝑦|𝑥)=ylog(yhat)+(1-y)log(1-yhat)$

发现这就是损失函数$Loss function$ 取负数

因为这个值$logP(y|x)$的大小表示 预测值与正确值是否贴近 预测值与正确值差的越大

需要修正的量越大,对$w,b$的调整越大相差越小 需要调整的越小 对$w,b$的调整越小 ,

所以$-logP(y|x)$来表示损失函数 。

对于m个样本的情况时,所有样本预测值都与正确值相同的概率是

同样的取log后变为

对这个求和取平均得到的就是代价函数$J$ 。

在pytorch中的交叉熵损失函数nn.CrossEntropyLoss(类) 或 F.cross_entropy(函数)

实际上是log_softmax + nll_loss(negtive log likelihood)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#理解多分类中的nll.loss函数 (nll = negative log likelihood)
#nll_loss实际没有求log,它默认输入已经使用过log_softmax
predic = torch.randn(6,3) #假设最后输出6个样本,每个样本对应三种分类的概率
label = tensor([0,1,2,1,0,0]) #假设这6个样本对应的正确的标签为label
nll_loss = F.nll_loss(predic,label,reduction='none') #reduction = mean 求平均,sum 求和
same_as_nll_loss = -predic[range(6),label]#注意取负数
print(nll_loss)
print(same_as_nll_loss)


# F.log_softmax?
#dim=1对每一行进行softmax,一行表示一个样本对应三个分类的可能性
predic_after_log_softmax = F.log_softmax(predic,dim=1)
nll_loss2 = F.nll_loss(predic_after_log_softmax,label,reduction='none')
predic_after_log_softmax2 = torch.log(torch.softmax(predic,1))
nll_loss3 = F.nll_loss(predic_after_log_softmax2,label,reduction='none')
# print(predic)
# print(torch.softmax(predic,1))
print(nll_loss2)
print(nll_loss3) #F.log_softmax = torch.log(torch.softmax)
# log_softmax + nll_loss = nn.CrossEntropyLoss(类) = F.cross_entropy(函数)
'''
import torch.nn as nn
import torch.nn.functional as F
'''
loss_func = nn.CrossEntropyLoss(reduction='none')
print(loss_func(predic, label))
print(F.cross_entropy(predic, label, reduction='none'))

''' #输出
tensor([-0.5667, -0.8479, -0.4085, 0.3947, -0.1787, 1.1803])
tensor([-0.5667, -0.8479, -0.4085, 0.3947, -0.1787, 1.1803])
tensor([0.4737, 1.4688, 1.0265, 2.4169, 0.9445, 3.0860])
tensor([0.4737, 1.4688, 1.0265, 2.4169, 0.9445, 3.0860])
tensor([0.4737, 1.4688, 1.0265, 2.4169, 0.9445, 3.0860])
tensor([0.4737, 1.4688, 1.0265, 2.4169, 0.9445, 3.0860])
'''

多分类中使用softmax激活函数作为最后的输出值(之后会详细介绍):

具体的如$softmax(a)=e^a/(e^a+e^b+e^c)$

pytorch中的nll_loss(负对数似然值)并没有取对数的操作,设计者默认已经使用了log_softmax。

因为在softmax中同时进行log运算更快。

label mat 标签矩阵可以认为是一个和预测矩阵同维度的0/1矩阵。

具体的如上图中的label mat:[0, 1, … , 2] = [[1, 0, 0], [0, 1, 0], …. [0, 0, 1]]。

那么nll_loss 可以视作:

$predic mat(after log softmax) × label mat$ 此处’×’表示对应位值相乘。

激活函数

在下文中对未确定激活函数的神经元使用$g()$ 代替表示

sigmoid函数 $𝜎(z)= 1/(1+𝑒^{-z}) $

它的导数 $𝜎‘(z)=𝜎×(1-𝜎) $

tanh函数 $tanh(z)=(𝑒^𝑧 − 𝑒^{−𝑧})/ (𝑒^𝑧+ 𝑒^{-z})$

它的导数 $tanh‘(z)=1-tanh^2(z) $

实验表明多数情况下果在隐藏层上使用tanh激活函数 效果总是优于 sigmoid 函数 其均值是更接近0的

但有一个例外:在二分类的问题中,对于输出层,因为𝑦的值是 0 或 1,所以想让𝑦hat的数 值介于 0 和 1 之间,

而不是在-1 和+1 之间。所以需要在输出层使用 sigmoid 激活函数,然后其它的所有单 元都选择 Relu 函数。

Relu函数 $Relu(z)=max(0,z)$

它的导数在z<=0是接近0,大于0时为1

如果在隐藏层上不确定使用哪个激活函数,那么通常会 使用 Relu 激活函数。

有时,也会使用 tanh 激活函数,但 Relu 的一个优点是:当𝑧是负值的 时候,导数等于 0。

但从实际上来说,当使用𝑧的导数时,𝑧=0 的导数是没有定义的

因此引入了Leaky Relu 函数(它整体不是线性的,因为不是一条直线 )

为什么要引入非线性激活函数 (如sigmoid tanh)

因为非线性激活函数实际上是在模仿人的神经元 只有刺激到达一定的剧烈程度才会产生电信号,

而剧烈程度反映在激活函数中就是其导数 而线性函数的导数是定值所以没有意义。

(摘自弹幕)

假设激活函数为$g(z)=z$ ,那么会导致无论网络有多深,

最终输入与输出值之间的映射相当于一个单纯的线性映射。

Softmax回归

softmax回归对应logistic 回归 用于分类问题 而不仅限于二分分类 成为C类问题

比如从多个事物中辨别指定事物就需要用到softmax回归

而softmax回归具体实现就是运用激活函数

$softmax(x)=e^x/ ∑e^x$ 其中x是一个向量(1,nx) 这也是它与其他激活函数不同的地方

对于这个公式的理解就是对向量中所有元素进行归一化变为一个和=1的概率值

其中向量中每个元素代表判断为某具体事物的概率P(cat|x) 等等

softmax和hardmax对应,hardmax就是实际值对于一个确定答案的分类问题

它的label=[0,0,0,0,1] 而softmax所得到的预测label=[0.01,0.01,0.01,0.01,0.95]

是所有概率中取得最大的值作为判断结果

对于softmax输出矩阵(1,n)中第i个元素对输入矩阵中第j个元素的求导 (推导过程)

设输入矩阵为$z$,那么有$D_jS_i=∂ softmax(z_i)/∂ z_j$其中 $S_i=softmax(z_i)$

设输入矩阵z ,$softmax(z)=yhat$, $Loss(yhat,y)=-ylogyhat$

(连接博客中的Loss函数求和表示单个样本向量中所有元素求值相加此处直接表示对矩阵每个元素运算)

现在要求$dL/dz=dL/dyhat × dyhat/dz$

其中$pi=yhati$ ,$xi=z[i]$

在代码中变量命名为”dz”=$dL/dz=yhat-y$

注意此处得到这样的dz ,前提是使用,$softmax(z)$, $Loss(yhat,y)=-ylogyhat$

发现这个结果与之前在求sigmoid作为激活函数的二分分类问题中dz完全一样

因为当C类问题所要判断的对象只有一个那么它就退化成二分分类问题

对于softmax输出矩阵(1,n)中第i个元素对输入矩阵中第j个元素的求导 就只有i=j的情况了

与sigmoid函数求导相同

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
'''
这里总结一下上面提及的激活函数及其导数
的python代码实现
'''

#激活函数

def Relu(x):
return np.where(x>0,x,0.0001)

#激活函数
def tanh(x):
return np.tanh(x)
#激活函数
def softmax(x):
exp=np.exp(x-x.max())
return exp/exp.sum()

#激活函数的导数

def d_Relu(data):
return np.where(x>0,1,0.0001)

def d_softmax(data):
temp=softmax(data)
return np.diag(temp)-np.outer(temp,temp)

def d_tanh(data):
#return np.diag(1/(np.cosh(data))**2)
return 1/(np.cosh(data)**2)
#加速运算优化不用对角矩阵
#对应的若不用对角矩阵反向传播过程中的内积改为矩阵内元素逐个相乘

反向传播的理解

反向传播实际上是不断按照链式法则求导的过程

下图中某些×1/m是在m个样例的情况下取平均

np.sum()函数中的axis=1表示水平求和 keepdims=true保证输出的矩阵仍然是(n[2],1)的矩阵

这里只讨论一个样本的情况所以用小写z,a表示一个样本得到的值括号中表示层级[layer]

一个样本的代价函数$J$就是损失函数$L$

注释:以下$g[layer]’(x)$表示第layer层的激活函数对x求导

对于”dz[1]”(即dL/dz[1])如何得到 通过链式法则得$dz[1]=dL/dz[2]×dz[2]/da[1]×da[1]/dz[1]$

其中$dL/dz[2]=a[2]-Y$ , $dz[2]/da[1]=W[2]$ , $da[1]/ dz[1]=g[1]’(z[1])$

所以推得 $“dz[1]”=dL / dz[1]=W[2]^T(a[2]-Y)×g[1]’(z[1])$

  • 插入语

    这里×号表示矩阵内元素逐个相乘而不是矩阵乘法 原因如下:

    本来推导公式求导得到两个矩阵,按照规则点乘 没有问题

    但由于其中某个矩阵是求导计算中产生的对角矩阵

    比如输入z为(1,nx)矩阵 g(z)=a 为(1,nx)矩阵 求da/dz时实际上是a[j]对z[i]求导

    当i ! = j 时da[j]/dz[i]=0 否则存在值 因此da/dz实际上是一个对角矩阵

    所以干脆把对角矩阵压成(1,nx)大小的矩阵 逐个乘(1,nx)的矩阵

    就相当于(nx,nx)的对角矩阵点乘(1,nx)的矩阵

    而对于softmax函数求导就比较特殊由于da[j]/dz[i]中i !=j 也有值

    所以不能优化为逐个相乘,老老实实遵守规则点乘

    另外在 大野喵渣的简单神经网络实战中在反向传播过程推导出现了 outer运算

    对于(1,n) outer (1,m) 就相当于 (n,1) dot (1,m)

    outer对于高维矩阵(n,m) 会flatten至(1,n*m)

    1
    2
    3
    4
    5
    a=array([[1,2],[3,4],[5,6]])  ###此时a是一个array对象
    print(a)
    #array([[1,2],[3,4],[5,6]])
    print((a.flatten())
    #array([1,2,3,4,5,6])

    两者含义和计算结果相同 使用outer感觉徒增理解难度

在$“dz[1]”=dL / dz[1]=W[2]^Tdz[2]×g[1]’(z[1])$ 中,

前两项相乘维度为(n[1],n[2])×(n[2],1)=(n[1],1) 后一项为(n[1],1)两者相乘要保证最后输出仍然为(n[1],1);

注意:这里的矩阵:𝑊[2]的维度是:(𝑛[2], 𝑛[1])。𝑧[2] , 𝑑𝑧[2]的维度都是:(𝑛[2], 1),

若从上一层输入m个样本时dZ[2]则为(n[2],m)如果是二分类,那维度就是(1,1)。对于任何变量dz和z的维度一样

总结一下 左边是逐个样本计算右边是将m个样本向量化

反向传播需要计算

$dZ[l]=dA[l]×g[l]’(Z[l])$ 此处×号表示矩阵逐个元素相乘

$dW[l]=1/m× dZ[l]A[l-1]^T$

$db[l]=1/m × np.sum(dZ[l],axis=1,keepdims=True)$

$dA[l-1]=W[l]^TdZ[l]$

对于向前传播时需要对x初始化,同样反向传播需要对$da[l]$初始化 $da[l]=-y/a+(1-y)/(1-a)$

在二分分类传播中最后a[l]为一个实数A[l]的大小为(1,m)

扩展到更深层的神经网络

对于向前传播

输入 A[l-1] 计算Z[l], A[l] 输出A[l] 缓存Z[l];

对于反向传播

输入dA[l],Z[l] 计算 dW[l],db[l],dA[l-1] 输出dA[l-1]

缓存Z[l]是因为向前传播计算A[l]=g(Z[l])

以及反向传播时计算$dZ[l]=dA[l]×g[l]’(Z[l])$ 时会用到 (此处的×表示逐个元素相乘)

  • Tips

检查每一层参数维度是否正确的小技巧

$w^{[l]}:(n^{[l]},n^{[l-1]})$

当训练集中只有一个样本时

$z^{[l]},a^{[l]},b^{[l]}:(n^{[l]},1)$

当训练集中有m个样本时

$Z^{[l]},A^{[l]},b{[l]}:(n^{[l]},m)$

向前传播过程

卷积和池化主要是在模拟生物脑 提取事物特征 抽象化具体特征的过程 (压缩图片)

当取得图片的具体特征后经过全连接层×权重并求和判断结果

比如识别一个汉字 取得了它有’横’ ‘竖’ ‘撇’ 等特征

每个特征对预测结果有权重即是哪个特征对最后判断影响更大某些则比较小(乘权重的含义)

哪几个特征表明是某个字的可能性比较大 (求和的含义)

  • 以上均为个人理解

在向前传播过程中具体包括:

卷积层 → 池化层 → …(Repeat)… → 全连接层 → …(Repeat)…

convolution → pooling → …(Repeat)… → full connected …

全连接层中逻辑回归和激活函数的内容在上文中已经学习了

key words: 卷积, 池化, 全连接

卷积层

卷积有两组输入:特征图和卷积核,卷积需要依据滑动 步长(stride)、填充长度(padding)、卷积核窗口大(filtersize)、分组数(groups)、扩张系数(dilation rate) 来决定如何计算。

卷积核(filter) 也叫过滤器 一般的选择的卷积核的大小为n×n 且n为奇数

每次卷积和都会和特征图中的一块与他大小相同的区域进行运算:

比如特征图 为RGB三通道 6×6的 三维矩阵

每层对应的Xi×Yi 最后求和(三层中所有点求和) 得到Fi

步长即卷积核每次平移的单位 每次平移后再次进行运算 得到新的 Fi

由于处于特征图边缘的值对卷积结果影响较小(参与运算次数少) 而靠进中心的则被多次运算 对结果影响大

所以引入了padding 即给原特征图周围加上几层值为0(一般)的像素点 使得原图边缘的值的影响因子更大

有两种较为常用的pading策略

Same pading 输出结果的大小与处理前一致

valid pading 输出的结果的大小比处理前小

所以整个卷积过程可以表示为

最后得到一次卷积后的结果为

Nc(卷积核的个数而非特征图或卷积核的通道数) ×n[i]×n[i] 的矩阵

其中$n[i]= floor( (n[i-1]+2×padding - filtersize)/stride+1) $

实际上一个卷积核表示某种特征

比如对于垂直边缘卷积核 是要提取图片中垂直分界区域 也就是垂直特征

水平边缘卷积核 是要提取图片中水平垂直分界区域

也就是 水平特征 卷积核越多 代表所提取的特征因素越多

而经过卷积的图片被逐渐降维则代表抽象化程度更高, 即从具体到抽象的特征提取

具体表现在卷积后Fi一个数值来表示上一层某块区域的值

池化层

降低数据的维度,缩减模型大小,可以避免过拟合,提高所提取特征的健壮性

避免过度拟合的意思是 比如说训练一个识别面部的神经网络

你希望能辨别出所有人的脸 而不仅仅是你的脸

池化操作也有对应的超级参数(固定值不受反馈学习影响) 大小(size)以及步长(stride),通常没有pading

具体的以上图为例 pooling 就是提取对应色块的Max value (还有一种取平均值的池化操作)

池化操作后新的特征图size:$n[i]= floor( (n[i-1] - filtersize)/stride+1)$

对于多通道的池化操作 每层对应的区域取Max value 池化后输出的通道数Nc与原特征图相同

全连接层

全连接层有什么用?

参考博客深入理解CNN卷积层、池化层、全连接层

这是一个神经元 从上一层神经元输入了三个量x1,x2,x3 通过一个线性函数+激活函数函数$𝜎(w^Tx+b)$

(此处激活函数为sigmoid函数)

由多个神经元组成多层神经网络

上一层输入特征矩阵$A[0]=X=[x1,x2,x3]$ 进入到下一层的四个神经元中通过上图的线性函数+激活函数

每个神经元计算得到$a$组成矩阵$A[1]$输入到下一层

用向量化表示该过程就是 $A[layer+1]=g^{[layer]}(W[layer]X[layer]+b[layer])$

每层的特征权重 $W$ 偏差值$b$ 和激活函数$g^{[layer]}()$ 都不一样

特别的需要注意矩阵的维度是否正确 (4个神经元输出的a矩阵组成一个A矩阵大小为(4,4)的矩阵)

$W[layer]$大小为(r,nx) 它的r直接决定了下一层矩阵A的行数(神经元个数) 而nx表示这一层的A行数(样本数量)

下图中$x$为一个样本其中包含{x1,x2,x3}三个值的列向量

下图展示了每个神经元输出的矩阵a如何整合成A 以及向前传播的伪代码

从水平上看,矩阵𝐴代表了各个训练样本。从竖直上看,矩阵𝐴的不同的索引对应于不 同的隐藏单元(神经元)。

在垂直方向,这个垂直索引对应于神经网络中的不同节点。例如,这个节点,该值位于 矩阵的最左上角对应于激

活单元,它是位于第一个训练样本上的第一个隐藏单元。它的下一 个值对应于第一个训练样本上的第二个隐藏单

元的激活值。以及第一个训练示例 中第三个隐藏单元,等等。

当水平扫描,将从第一个训练示例中从第一个 隐藏的单元到第二个训练样本,第三个训练样本……直到节点对应于

第一个隐藏单元的激活 值,且这个隐藏单元是位于这𝑚个训练样本中的最终训练样本。

全连接层与卷积层的关系

全连接层可以是做一种特殊的卷积,

回忆全连接层间的传递:

前一层所有神经元组成的矩阵$x$通过 $a=g(w^Tx+b)$ 运算变成下一层网络中的一个神经元。

这类似于使用卷积操作转化到下一层。

如上图所示, 之前的全连接层使用$a=g(w^Tx+b)$ 可以看作

对1×1×4 的矩阵分别用m个等维度的矩阵(过滤器)进行对应值相乘再求和的卷积操作,

最终得到一个1×1×m 的全新矩阵。

但实际上卷积和全连接的传递过程还是存在差别,

考虑下面两种情况:

  1. 特征图和全连接层相连,AlexNet经过五次池化后得到7×7×512的特征图,下一层全连接连向4096个神经元,这个过程可以看做有4096个7×7×512的卷积核和7×7×512的特征图进行卷积操作,最终得到1×1×4096的特征图,等价与全连接得到4096个神经元。
  2. 全连接层和全连接层相连,AlexNet的再下一层依然是4096个神经元,即4096个神经元和4096个神经元全连接,由(1)我们得到了1×1×4096的特征图,本次全连接过程可以看做存在4096个1×1×4096个卷积核,依次和1×1×4096的特征图进行卷积操作,等价与全连接操作。

引用自理解为什么要将全连接层转化为卷积层

这里涉及到一种利用Conv卷积替代FC全连接层的技巧

这个技巧将被用于Detection中的滑动窗口卷积操作

优化技术

主要涉及如何解决模型的欠拟合/过拟合问题。

以及如何更高效的训练模型。

输入输出

此处以二分分类问题为例子

讨论输入(训练数据集)输出(预测值yhat)

输入中也包括一些参数和超参数

参数是要在不断学习中逐渐被修正趋于最优 比如逻辑回归中的$w,b$

超参数则是最初给定不会改变的量比如在逻辑回归中选择的激活函数,梯度下降中的学习率α

对于一个二分类问题 比如判断图片中是否存在猫

输入一个特征向量$x$ 由训练集经过卷积和池化后获得的特征图得到

对于上面这个例子来说 $x$ 是一个 $nx × 1$ 的向量(矩阵) 其中$nx=n×n×nc$ ,而 $y=0,1$

对于m个图形成的训练集 Mtest ={$(x^1, y^1), (x^2, y^2), (x^3, y^3) … (x^m, y^m)$}

(此处参考资料整理作者 黄海广 在参考栏第三个链接)

所有训练数据集的输入值和输出集合以如下方式组成矩阵$X$,$Y$ 一般$X$的每一列是$x^i$

对参数的随机初始化

在输入训练集的之前要给$w,b$预设初始值 让他们能在之后的学习中有效的达到最优解

虽然初始化为0可行但是所有的w赋值为0会使梯度下降法失效

因为每个神经元上计算得到的结果将会完全一样 之后无论如何梯度下降所有神经元的函数任然相同

通常会随机生成一个矩阵 w=np.random.randn((r,c))*0.01

这里乘上0.01让$z=w^Tx+b$中w尽量小而不为0 所得到的z处于激活函数导数最大处

能让梯度下降更快提高学习效率

另外对于初始化超参数如学习率$α$,以及后面提到的梯度下降优化算法中的$β$

它们的变化对神经网络的性能影响往往需要达到数量级的变化才能有效体现。

所以我们通常在指数尺度上对它们进行随机初始化。

假如使用0.0001~0.1的均匀随机分别采样 那么90%的$α$会大于0.1只有10%在0.0001到0.1之间。

而在对数尺度上采样 不同数量级的$α$就能被均匀的采样到了,

这样就能在较大尺度上选取合适的区间中筛选合适的超参数。

1
2
3
4
r = np.random.rand()*4
a = 1**r
#对于beta一般取0.9~0.9999
beta = 1 - 1**r

归一化

为了更快的进行梯度下降找到代价函数的最优解

通常对输入数据采取归一化处理

归一化或者说标准化实际就是将原来的数据集通过映射

变换到一个新的集合内作为网络的直接输入

对于训练集中的样本值分布范围比较大的情况,

训练一个网络模型得到代价函数$J$的最优解需要耗费更多的算力

一般会对样本输入值进行归一化处理。

如上图,左侧是没有进行归一化输入的情况,

代价函数$J(w,b)$ 呈现出一个狭长的值域,

而获得最优解的过程将会像图中的蓝线那样寻找梯度下降

最快的方向移动也就是垂直于这个椭圆的切线方向,

同时由于这个狭长的值域我们需要设置一个较小的学习率$α$

以便它有效下降到最优解,但这样会导致训练效率低下

若将样本输入值进行归一化处理 得到的代价函数则如右图所示

它将更趋向于一个圆状,这样就有利于更快的找到相对最优解

归一化对于输入样本值的调整可以视为两步:

设数据集$X$包含m个样本记为$X_{(i)}=(x_1,x_2)$

第一步零均值化: 第二步归一化方差:

$u=1/m∑_iX_{(i)}^2$ $σ^2=1/m∑_iX_{(i)}^2$

$X:=X-u$ $X:=X/σ$

最后样本的输入值将会贴近原点且处在一个较小的区间内

  • tips:

    作为中间值的$u$ 和$σ^2$ 无论在输入训练集还是测试集都要一致

    实际使用时统一用训练集中获得的$u$和 $σ^2$ 对$X$进行调整

Batch Normalizing 批归一化

在上文讲到对输入数据进行归一化处理利于网络的梯度下降,

由此可以想到对每一层的输入都进行归一化处理,

直觉上是有益于优化网络学习能力的。

因为对于网络中每一层而言前一层的输入数据都可以视为一个特殊的生数据raw input

它们(输入数据)拥有相同的”地位”,

自然而然就会想到将输入的归一化应用到每一层输入。

对每一层的输入矩阵进行归一化处理成为Batch Normalizing。

(回忆一下几个相关符号之间的关系$a_{[layer]} = g(Z_{[layer]})$而$Z_{[layer]}=w·a_{[layer-1]}+b$)

后者较为常用,Ng Andrew在课堂中主要讲述后者的具体实现。

类似的,我们需要输出矩阵的均值$μ$和方差$σ^2$。

由于BatchNorm对$z$进行了零均值化,因此原来线性处理中$+bias$ 就失去了意义。

因为无论是否加入偏移量最后经过BatchNorm的$z^{norm}$ 最终都是$μ=0$

所以索性就在使用BatchNorm时将$Z_{[layer]}=w·a_{[layer-1]}+b$中的$b$省略

假设线性处理后的输出矩阵为$Z=[z_{(1)},z_{(2)},…,z_{(i)} ]$

$μ=1/m∑_iz_{(i)}$ $σ^2=1/m∑i(z{(i)}-μ)^2$

$z_{(i)}^{norm} =(z_{(i)}-μ)/sqrt(σ^2+ε)$其中$ε$是一个趋近于0的数为了避免产生除0错误

在获得归一化矩阵$Z^{norm}$之后我们还需要再进行一次类似线性处理的操作。

$Z^{hat} = γ·Z^{norm}+β$ 。$Z^{hat}$将替代原来的$Z$作为激活函数的真正输入值,

这里的参数$γ,β$ 是为了调整$Z$的均值以及方差而设计的。

同样的它们也会通过后来的梯度下降方法进行更新。

这幅图直观的说明我们调整$γ,β$能调整$z_{(i)}$的整体分布。

从而能够更好的利用非线性激活函数再神经元中起到的作用。

最后梳理一下如何将BatchNormilzing拟合进神经网络:
$$
a_{[l-1]}→^{w,b}→Z_{[l]} → ^{μ,σ^2}→Z_{[l]}^{norm}→^{γ,β}→Z{[l]}^{hat}→^{g()}→a_{[l]}
$$
BatchNorm通常与Mini-Batch一起应用到网络学习中,以达到更好的优化网络的学习能力

之后将要讲述的一系列优化梯度下降法也同样可以和BatchNorm一起使用。

Batch Normlizing的理解

对于神经网络当它学习完特定目标如识别黑猫后再对它进行下一步训练让它能够识别所有猫。

此时对于网络它的输入数据的特征分布矩阵就发生较大的便宜,成为covariate shift。

直观的理解是因为网络已经对原来的数据集 (黑猫)拟合了二分类的函数,

对于新的数据集由于与原来的数据集有较大偏差,网络相当于重新学习一个新任务。

同样的对于隐藏层而言前一层传来的输入值就如同模型的输入层读取的生数据。

对于某一层而言前方发生了什么它一概不知,只看得到前一层的输出。

由于在学习过程中权值和偏移量的微小改变经过深度网络的放大,

到某一个隐藏层时它每次所学习的”数据样本”(即中间层输入,”地位”等同于生输入)会有较大差异。

从而难以让之后的网络在训练之后能让参数达到一个稳定状态。

而将中间层输出进行BatchNorm的好处就是能让”数据样本”相对的稳定。

这样网络在学习时每一层都不会受到前方扰动导致的输入偏差较大而难以收敛,

因为输入值都会被归一化至一个较小且与过去输入值相近的区域内。

这也让网络的每一层(Layer)都不会过于依赖前层(相对独立),

在输入mini-batch时每个独立的batch有各自的”样本数据”均值与方差,

均值与方差的轻微差异对隐藏层起到了类似dropout随机失活的正则化效果

这种操作就像在级联放大电路中加入了电压跟随器(BatchNorm),

使得前后两个级联放大器(Layer)相对独立。

优化梯度下降

MiniBatch梯度下降法

之前讲到使用向量化方法将多个训练样本整合成一个矩阵作为输入

然而实际训练时不会这么做,而是会将样本分批输入

Mini-Batch或者平时说的Batch(批)实际就是一次”投喂”给系统的样本量。

假设原来拥有m个样本将他分成5000份Mini-Batch。

然后将这5000份样本依次输入。所有样本被迭代一次成为1个epoch。

这么做似乎和原来一个矩阵所有样本一次性输入似乎对于每个独立样本而言没有什么差别,

它们都只被使用一次。但是对于网络而言一次性读入所有样本只能进行一次梯度下降。

而Mini-Batch分批投喂却能使用等量的样本进行多次梯度下降。

相比一次性投喂,分批投喂能在较少次数的迭代(epoch)内让网络找到最优解。

代码实现上也是将每个Batch向量化后输入,通常会进行多次迭代(多个epoch)。

那么如何选择合适的Mini-Batch呢?

可以将所有样本视为一整个Batch,也可以将每个独立的样本分别当作一个Batch。

而恰当的Batch大小需要取两者之间。

如图所示,假设要通过多次梯度下降达到最优解(中心)。

Batch size = 1的情况下网络很难找到正确下降的方向,(这种情况下的梯度下降称为SGD)

因为一次投喂的样本量太少,特殊性太强而普遍性太弱,网络会提取出只符合这个样本的特征。

Batch size = m的情况下网络虽然能找到正确的方向稳步下降,但每次下降的步幅可能就会小。

因为样本的数量太多,网络需要从中一点点找到普遍特征以符合所有独立个体。

动量梯度下降法

为了减小损失函数值在下降时向无益于减小损失值的方向移动,引入了动量梯度下降法

本质上是利用前几次迭代的dw(db)的指数加权平均替代原来的dw(db)来更新w(b)

假设损失函数的梯度下降过程如下图描述,

可以发现它会波动性的向无益于减小损失值的方向移动

这显然降低了梯度下降的效率, 传统的若想让梯度下降更快,可以使用更大的学习率,

而更大的学习率带来的问题是会导致想错误的方向移动更远,

甚至偏离出可以正确下降到最优解的椭圆形区域(如紫色折线)

这时候动量梯度下降法可以有效的减少错误方向上的”运动”分量。

在实际操作时就是将原来的w以及b的更新稍作修改。

$V_{dw} = βV_{dw} + (1-β)dw$

$V_{db} = βV_{db} + (1-β)db$

$w = w - αV_{dw}$ , $b = b - αV_{db}$

这里就涉及到了指数加权平均数的概念

  • 指数加权平均数

    假设要绘制日期与气温的关系图,得到了一系列蓝色的离散坐标点。

    当$β=0.9$时,以$V_t = βV_{t-1} + (1-β)T_t$替代原来的温度$T_t$可以得到红色折线。

    当$β=0.98$时,以$V_t = βV_{t-1} + (1-β)T_t$替代原来的温度$T_t$可以得到绿色折线。

    其中$V_t$就相当于前$1/(1-β)$ 的指数加权平均温度,

    (当$β=0.9$时约等于前十天,$β=0.98$时约等于前50天,下文称为时间窗)

    当$β=0.9$时对递推式进行展开:$V_t = 0.1T_t+0.1×0.9T_{t-1}+0.1×(0.9)^2T_{t-2}….. $

    发现当前时刻的温度对$V_t$的影响最大, 距离当前时刻越远的温度对$V_t$的影响就越小。

    这种效果呈现在图中就类似于一个滤波器, 过滤了”高频的毛刺信号”

    通常的若展开式某项其$β^n$接近$1/e$时认为它是时间窗的边界,(虽然不知道为什么)

    可以利用$lim_{x→0}(1-x)^{1/x}=1/e$ 对边界的位置进行估计,其中$x=1-β$

    比如$β=0.9$时 代入 求得$0.9^{10}≈1/e$ 即$n=10$

    另外由于$V_0$初始化为0 递推公式中最初的几个$V_t$值会非常小,

    通常会使用$V_t/(1-β^t)$来替代原来的$V_t$ 随着t的增大分母趋向于1对$V_t$影响越小。

    但通常在算法实现时不会采用这种策略。

    采用指数加权平均进行梯度下降的优势在于:计算某区间的均值时,

    不需要将这个区间内的信息全部保留只需要保留上一次更新的$V_{dw}.V_{db}$

均方根传递(RMSprop)

类似于动量梯度下降法, RMSprop对参数的更新方式进行了如下修改:

$S_{dw} = βS_{dw} + (1-β)|dw|^2$

$S_{db} = βS_{db} + (1-β)|db|^2$

$w = w - α×dw/sqrt(S_{dw}+ε)$, $b = b - α×db/sqrt(S_{db}+ε)$

其中$ε$ 是个趋近于0的极小值为了避免产生除0的情况。

对于动量梯度下降法取的是前几次下降的梯度(向量)的指数加权平均$V$ 作为替代。

而对于均方根传递取的是前几次下降的梯度模值(标量)的指数加权平均$S$作为替代。

Ng Andrew举了个例子, 为了简化问题假设$J(w,b)$ 是如下图的凸函数:

(将向量w,b都当作一维,实际上$b$和$w$ 都是多维向量)

当梯度下降时$J(w,b)$往b轴正方向移动更多时(即$dJ/db$ 比 $dJ/dw$更大时),

$S_{db}$ 也会比$S_{dw}$更大(实际它们是包含前几次梯度下降影响的积累值)

因此这时$b$的变化将会比$w$的变化更小,体现在上图就是$J(w,b)$下次将向$w$移动更多(绿色折线)

Adam梯度下降

Adam梯度下降汲取了前两者的特点,在实际应用中获得了较好的效果。

$V_{dw} = β_1V_{dw} + (1-β1)dw$ $S{dw} = β_2S_{dw} + (1-β_2)|dw|^2$

$V_{db} = β_1V_{db} + (1-β1)db$ $S{db} = β_2S_{db} + (1-β_2)|db|^2$

对$V_{dw},V_{db}$进行修正: $V^{correct} = V/(1-β_1^t)$其中t指第t项

对$S_{dw},S_{db}$进行修正: $S^{correct} = S/(1-β_2^t)$其中t指第t项

最后更新参数

$w = w - α×V_{dw}/sqrt(S_{dw}+ε)$

$b = b - α×db/sqrt(S_{db}+ε)$

学习率衰减

固定学习率的梯度下降会存在一个问题。

当$J(w,b)$靠近最优解附近时,由于学习率α(或者说更新步长)过大导致多次迭代后,

损失函数一直在靠近最优解附近的区域上反复横跳而无法收敛。

然而取小学习率会导致初期梯度下降过于缓慢。

一个较好的方法是学习率能随着下降的过程而逐渐减小。

这样既保证初期能进行快速地梯度下降,同时又能在靠近最优解时能进行”微调”。

通常有以下几种方案对学习率进行自适应更新:

$α=α_0/(1+DecayRate×epoch)$ 一个epoch表示完整的迭代所有样本一次

$α=0.95^{epoch}α_0$或者 $α=Kα_0/sqrt(t)$其中t表示第几个mini-batch。

梯度消失和梯度爆炸

  • 梯度消失:指梯度下降过程中出现梯数呈指数级减小趋于0
  • 梯度爆炸:指梯度下降过程中出现梯数呈指数级增大趋于∞
  • 解决方案:使用ReLu激活函数或使用残差网络(下文提及)以及权重初始化方法

产生原因

以一个简单模型为例子:

假设这个神经网络模型中激活函数$g(z)=z$且$b[l]=0$

那么预测值$yhat=w^{[l]}×w^{[l-1]}×…w^{[2]}×w^{[1]}×x$

假设特征向量中的元素值<1那么在经过L层网络后w将趋近于0

假设特红向量中的元素值>1那么在经过L层网络后w将变得很大

以前文sigmoid函数作为激活函数时的反向传播为例:

$dJ/dw^{[i]}=x^{[i]}×(a^{[i]}-y)$

(x是上一层的输出这一层的输入,a是这一层经过激活函数后的输出,y是正确值)

由于神经网络隐藏层的深度不断加深 $w$的累乘效果越加明显

导致 $x^{[i]}→∞/0$ 导致梯度爆炸/消失

对于特征向量的更新$w:=w-α×dJ/dw$ 也会失去控制/基本不变

权重初始化

以单个神经元为例:

我们不希望这一层的$z$过大或过小 而是接近1

这样即使加大深度也能尽量避免梯度消失或爆炸

设前一层有$n^{[l-1]}$ 层神经元 那么每个特征向量的值初始化为$sqrt(1/n^{[l-1]})*w$

相当于给原来的特征向量×权重

实际上是对于所有的特征向量$w_i$ 保证他们的方差为$1/n$

假如使用的是ReLu激活函数那么设置$w_i$方差为2/n 更合理, 则$w_i=w_i×sqrt(2/n^{[l-1]})$

对于tanh激活函数则使用:

过拟合与正则化

所谓过拟合 通俗来讲 就是缺乏普遍性只能处理特殊值

过拟合有两种原因:

1.训练集和测试集特征分布不一致(黑白马)

2.模型太复杂而样本不足

为了减少过度拟合引入了正则化的概念

正则化是针对参数的约束,而归一化是对输入的调整。

通常正则化的方法有:L1正则化,L2正则化,Dropout正则 (此处参考外部博客)

对于二分分类问题 将是A与非A区分开来重要的是划分”决策边界”

简单的线性模型准确率较低,

而将所有的样本A从当前的数据集划分出来会导致新加入的样本不能正确判断。

原因是没有正确提取抽象特征 称之为“过拟合“。

L1正则化与L2正则化

  • 如何正则化

L1/L2正则化实际上是在损失函数$J(w,b)$ 后加入一个“惩罚项”

而这个“惩罚项”实际就与Lp 范数 ($L_p norm$)有关

$L_p norm$ 的定义为:$||W||_p=(∑_i|w_i|^p)^{1/p}$

L1正则化: $J_1(w,d)=1/m × ∑L(yhat,y) + λ/2m × ||W||_1$

其中$||W||_1=∑_i|w_i|$即所有特征系数绝对值的和

L1正则化能使得一部分特征向量趋于0 让特征矩阵变得稀疏,

起到特征选择的作用 达到简化模型的目的。

L2正则化: $J_2(w,d)=1/m × ∑L(yhat,y) + λ/2m × ||W||_2^2$

其中$||W||_2^2=∑_i|w_i|^2=w^Tw$即所有特征系数的平方和

L2正则化能使得特征向量有比例的缩放防止过拟合,而不会起到特征选择的作用

在两种正则化中都存在系数λ/2m。

其中λ是可调超级参数,而m是样本数量, 除2是为了消掉求导时的平方项产生的2 。

  • 那么如何处理正则化后的梯度下降(更新特征向量w)?

为了方便理解将w1作为一个数,对于L1正则化而言有:

其中$dJ/dw_1=“dw_1”$是对于未加入正则项的代价函数$J(w,d)$的求导,

另外$sign(x)=|x|/dx = -1_{x<=0} or 1_{x>0}$

对于L2正则化而言有:

在L2正则化中对于梯度下降过程中的特征参数更新应该替换为$w:=w-αdw’$

其中$dw’$为加入正则项后的$dJ_2/dw$ 即: $dw’=dw+λ/m×w$

而$dw$是不包含正则项时$dJ/dw$

  • 正则化问什么能防止过拟合

调整正则项系数λ可以使得特征向量w趋于0

而逻辑回归函数$z_{[l]}=w_{[l]}a_{[l-1]}+b_{[l]}$ 的结果$z$ 也会相应减小

那么激活函数$g(z)$ 就会接近线性

这样就会避免“决策边界”成为一个过于复杂的高阶多元曲线函数

(虽然不太明白什么原理但是感觉很有道理)

Inverted Dropout正则化

Inverted Dropout 也成为随机失活 一般运用在训练网络模型 而不再测试模型时使用

原理就是将网络中某些点(特征)省略 从而简化网络模型

  • 向前传播过程中如何加入inverted dropout 正则化

对于每个点 它是否失活(省略)的概率 被设置为 keep-prob

假设某一层经过激活函数输出后得到 $a_{[l]}=g(z_{[l]})$

在$a_{[l]}$传递到下一层网络成为$x_{[l+1]}$前

1
2
3
4
keep_prob=0.8
dl=np.random.rand(al.shape[0],al.shape[1])<keep_prob
al=np.multiply(al,bl) #al=al*dl 逐个相乘
al/=keep_prob

对所有特征量 进行随机失活 有0.8的概率保留原值 否则置为0

但这样操作会导致特征值中20%的值失效 为了不影响$z_{[l+1]}=w_{[l+1]}x_{[l+1]}+b_{[l+1]}$ 的期望值

应此最后需要将al/=keep_prob

  • 为什么dropout正则化可以避免过拟合

第二个直观认识是,我们从单个神经元入手,如图,这个单元的工作就是输入并生成一些有意义的输出。通过dropout,该单元的输入几乎被消除,有时这两个单元会被删除,有时会删除其它单元,就是说,紫色圈起来的这个单元,它不能依靠任何特征,因为特征都有可能被随机清除,或者说该单元的输入也都可能被随机清除。我不愿意把所有赌注都放在一个节点上,不愿意给任何一个输入加上太多权重,因为它可能会被删除,因此该单元将通过这种方式积极地传播开,并为单元的四个输入增加一点权重,通过传播所有权重,dropout将产生收缩权重的平方范数的效果,和L2正则化类似,实施dropout的结果是它会压缩权重,并完成一些预防过拟合的外层正则化。

”(引用自Ng Andrew 字幕)

迁移学习与数据增强

为了提高模型训练效率 可以采用迁移学习的方法

将开源的深度学习模型移植到自己的模型中

为了在有限的训练集中 增加训练集样本提高模型准确率

可以采用一些手段增强数据

迁移学习

可以从开源网站中获得搭建好的网络模型 以及训练好的参数 作为初始化值

(不知道原先的分类对象与想要的分类对象不一致是否影响)

仅仅修改softmax部分(或后半部分网络) 以达到较少量的训练集就能达到较高的正确率

一般开源模型中会有一个开关”freeze” 将前几层网络中的参数冻结,

这样在训练过程中就只会修改自己搭建的几层网络中的参数

数据增强

主要有3种方式

  • 镜像对称: 即将原有的样本图片镜像对称得到新的样本
  • 随机截取: 即随机截取原有样本图片的某个部分获得新的样本
  • 色域调整: 即随机调整原有样本图片的RGB值生成新的图片

经典神经网络模型

介绍几类经典神经网络模型

LeNet-5AlexNetVGG

残差网络: ResNet

Inception

LeNet-5

针对灰度图像训练

使用simgiod函数以及逻辑回归

论文来源:[LeCun et al., 1998. Gradient-based learning applied to document recongintion]

AlexNet

论文来源:[Krizhevsky et al., 2012. ImageNet classification with deep convolutional neural networks]

使用了Relu激活函数 涉及到将图分组放入两个互相关联的gpu进行训练

有一种独有的类型层 “局部响应归一化层” 对局部神经元的活动创建竞争机制,使得其中响应比较大的值变得相对更大,并抑制其他反馈较小的神经元,增强了模型的泛化能力 (吴恩达老师说 后来被发现用处不大。。)

一般的“归一化” 是指将一个[L,R]区间内浮动的变量集 映射到[l,r]一个更小的区间变量集

具体的归一化有具体的转换公式

VGG

论文来源:[Simonyan & Zisserman 2015. Very deep convolutional network for large-scale image recognition]

VGG-16模型图解 (same表示使用same pading)

ResNet

论文来源:[He et al. 2015.Deep residual net works for image recognition]

此处涉及到残差网络(Residual block)

普通方法:

$z^{[l+1]} = W^{[l+1]}a^{[l]}+b^{[l+1]}$ , $a^{[l+1]}=g(z^{[l+1]})$

$z^{[l+2]} = W^{[l+2]}a^{[l+1]}+b^{[l+2]}$ , $a^{[l+2]}=g(z^{[l+2]})$

残差网络方法:

$z^{[l+1]} = W^{[l+1]}a^{[l]}+b^{[l+1]}$ , $a^{[l+1]}=g(z^{[l+1]})$

$z^{[l+2]} = W^{[l+2]}a^{[l+1]}+b^{[l+2]}$ , $a^{[l+2]}=g(z^{[l+2]}+a^{[l]})$

这种设计可以避免梯度消失 ,当模型层数上升普通方法下 模型的精度下降

而采用了残差网络则可以避免这种现象发生。

下图中每两层组成一个残差网络

在残差网络采用了same pading

L和L+2层拥有相同维度才能保证$z^{[l+2]}+a^{[l]}$能相加

否则可以引入$w_s$ 通过 $w_s × a^{[l]}$ 使得两者维度一至可以相加

而$w_s$不需要初始化 它的值将会通过后天学习自动更新

Inception

论文来源: [Szegedy et al.,2014, Going Deeper with Convolutions]

在这之前先介绍一下 1×1卷积

当遇到需要压缩样本的信道,即不改变长度与宽度的前提下减少深度 可以采用1×1卷积

如上图 将深度为192的样本变为32

可以用32个1×1×192的过滤器 进行卷积

对于其中一个过滤器对应位相乘再求和 后用relu激活得到对应的值

这一步能将深度为192的样本压缩成1维

当存在32个过滤器时将32个1维结果叠加 则生成28×28×32的结果

1×1卷积核在inception的实现中能大大减少卷积池化过程的计算量(通过减少信道数) 但会损失一定信息

在googleNet的模型中采用以信道分组卷积再合并的方式

最后将几组的信道组合成一个整体作为下一层

重复这样卷积处理搭建出一个googleNet的模型

其中有三个分支 用softmax预测出结果 以防止过度拟合出错

计算机视觉中的三类问题

计算及视觉主要有两类问题

分类问题(classification):判断给出图片中的对象是A还是B

定位问题(localization):找出给出图片中所需要检索物体的位置

而Detection是针对多种不同物体的定位与判断

特征点提取

Detection的定位与判断需要依赖被识别物体的特征点坐标集合作为神经网络的输出单元

以人体姿态识别为例 Detection神经网络会输出 {是否存在人体躯干0/1,特征点坐标1(x,y), … , 特征点坐标n(x,y)}

目标检测

  • 滑动窗口检测

滑动窗口检测的实现方法很简单,就是以固定的窗口大小与滑动步长,对图片中的区域逐个进行识别。

若当前窗口存在则这个区域的输出结果为1,否则为0。然后逐渐增大窗口再次遍历图片中的所有位置。

这个做法十分朴素,但是显然的缺点是计算量非常大。

若为了减少计算量采用较大的窗口与较大的步长则会导致目标定位不准确

  • FCN(全卷积神经网络)实现滑动窗口

实际操作时我们并不会对所有区域单独进行卷积-池化-全连接的神经网络估计

而是将整个图片作为一个整体输入。这里利用了卷积操作替代全连接操作的技巧

假设存在一个16×16的图片 用一个大小为14×14的窗口滑动,则存在4个区域需要进行单独识别。

利用卷积操作替代全连接操作则简化了这个步骤。

论文参考:[Sermanet et al.,2014,OverFeat: Intergrated recongnition, localization and detection using convolutional network.]

可以看到利用卷积代替全连接操作后图片信息压缩后各个区域的相对位置不变。

这个性质避免了滑动窗口重复计算的劣势。

此处Ng Andrew 没有详细描述最初的滑动窗口和最终”压缩”处理后得到的输出之间的关系。

28×28的原图经过 5×5卷积(步长1) —— 2×2池化(步长2) —— 5×5卷积(步长1),

得到一个8×8的矩阵 那么每一个矩阵元素对应原来14×14窗口在原图上每间隔2个像素的一次采样。

这里有两个问题:

1.为了得到14×14窗口每间隔2个像素的一次采样最终结果必须经过 固定size的卷积池化操作吗?

2.最终得到的8×8的矩阵是否可以解释为其他大小的滑动窗口采样得到的结果?

如果利用卷积压缩公式:$n[i]= floor( (n[i-1]+2×padding - filtersize)/stride+1) $

发现原图经过5×5卷积(步长1) —— 2×2池化(步长2) —— 5×5卷积(步长1),得到的矩阵大小$x$

与原图像大小28×28,滑动窗口大小14×14以2为步长直接卷积得到的最终矩阵大小$y$ 相同。

即$x=y=8$ 。

个人想法: 卷积和池化代替全连接操作图片信息压缩后各个区域的相对位置不变。

先确定好最终要将图片分成多少份区域进行识别,

只要调整卷积和池化的窗口大小和步长就能得到预想的最终矩阵。

比如100×100的原图变为3×3矩阵:

30×30卷积(步长2) 得到36×36矩阵 —— 3×3池化(步长3)得到12×12矩阵

—— 4×4 卷积(步长2) 得到3×3矩阵。

  • Bounding Box预测

滑动窗口卷积存在一个明显的问题,由于规定的检测区域是离散的分布在原图中的几个固定位置。

若被检测物体不能独立的存在于任何一个区域中内时,被检测物体的边界就不能被准确定位。

Bounding Box预测则是规定了几个参数来辅助确定被检测物体边界Bounding。

以YOLO算法为例。

对输出值标签增加$b_x b_y b_h b_w$ 分别表示物体中心坐标(x,y)以及物体的高度宽度。

$P_c$ 表示当前Bounding box中存在被检测物体的概率, $C_x$表示这个物体属于某个类的概率

这样预先将图片分割成3×3的区域然后利用FCN网络得到各个区域的Label输出值。

预测框的选择

当运行算法时,同一个目标可能被检测多次而产生多个预测框,

这时候就需要从预测狂中选择一个最优的预测框作为最终结果。

  • 非极大值抑制 (Non-Max Suppression)

    非极大值抑制就是只保留检测到同一个目标物体的概率最大的预测框。

    具体的实现过程如下:

    1
    2
    3
    4
    5
    6
    7
    Discard all Boxes with p_c <= threshold value.
    While there are any remaining boxes unmarked:
    Pick the Box with the largest p_c as a prediction Box_A
    If there is any remaining box with IoU >= threshold value with Box_A
    Discard those box.
    Else Box_A is the only one remaining box of this target.
    Mark the Box_A to avoid picked at next loop.

    如果存在多个不同分类(Class)的目标需要被检测,则需要对每种分类分别运行非极大值抑制操作。

    上述伪代码中出现了参数IoU即交并比(Intersection over union)。

    这是评估两个框的重叠程度的指标。

  • 交并比(Intersection over union)

    交并比对于两个预测框A与B而言 就是相交面积/合并面积。$IoU=A∩B/A∪B$

    通过人为设定阈值,判断检测框之间的关系。

    IoU >= Threshold 则认为两个检测框捕获了同一个物体。需要根据极大值抑制保留概率大的Box

    IoU < Threshold 则认为两个检测框捕获了不同的物体。

    实际运用中交并比也可以作为目标跟踪的依据。

    通过计算相邻两帧图片中Box的IoU。

    若大于某一阈值 则判定为同一个物体移动导致的位移,

    则它们的ID或者识别码保持不变。

Anchor Boxes锚定框

在实际使用中不同物体的中心点可能会落在同一个预先划分的Bounding Box中,

然而传统方法中一个格子(BoundingBox)只能检测到最多一个目标物体。

而锚定框可以很好的改善这种情况。

图中汽车的中心点和人的中心点同时出现在同一个Bounding Box中,

可以采用锚定框策略,预设一个AnchorBox1形状接近于一个站立的人(竖着的矩形)。

同样的,预设另一个AnchorBox2形状接近于车辆的侧面(横着的矩形)。

这时对应的标签y也需要更改,它需要包括两个对应AnchorBox的参数。

假如包裹检测目标的矩形框与预设的某个AnchorBox具有最大的IOU值。

那么这个检测目标将会被归类为对应标签的物体如:人/车。

  • 此处疑问:这里吴恩达所说的$p_c$似乎是包含有分类$c$的概念。

若$p_c$包含有检测目标是否为对应目标的含义那为什么还需要$c_1,c_2,c_3$来进行分类。

我个人认为$p_c$的定义应该是检测区域内出现目标的概率,具体是什么物体由$c_x$判断。

AnchorBox能较好的解决两个对象中心出现在同一个BoundingBox的情况但是,

但是不能很好地解决3个及以上的物体出现在同一个boundingBox中的情况。

通常地,我们会采用预先分出更多个BoundingBox,

这样两个物体被同一个Box捕获的概率就会小很多。

持续缓慢更新ing。。。