Lecture 4: Neural Networks and Backpropagation
The problem of linear classifier
根据前几讲,对于某些特殊情况linear classifier无法正确分类。
为了解决这一问题,采用两种方式:
1.applying feasture transform.即使用图像变化,此时数据点可以进行线性分类。
以二维为例(即X只有两个元素,W也只有两个元素,本质上是一个二维问题)
所谓图像变化,本质上是坐标轴变化,变化后就可以用线性分类器进行分类,利用之前学习的KNN、SVM和softmax算法。
坐标变化主要有两种,一种是颜色直方图Color Histogram,另一种是HoG梯度方向直方图
2.采用卷积神经网络来解决,即ConvNets
Nerual Networks
本质上是在原本的线性分类的基础上将结果代入激活函数,再把结果继续进行线性分类,以此类推,建立多层的神经网络。
其中
m
a
x
(
0
,
W
1
x
)
max(0,W_1x)
max(0,W1x)可称为激活函数,常用的激活函数如下:
神经网络的结构如下图所示,其中,最后一个隐藏层和输出层也称为全连接层
神经网络每一层,每一组权重的概念可近似等效为生物上的神经元,输入的数据为电信号,从突触方向引入,从神经细胞上传递,最终得到结果。
但实际上生物神经元是十分复杂的,是多连接的模式,则说明神经网络的随机连接一样可以工作,而不是采用多层结构。
生物神经元有如下特点:
1.许多种类型
2.树突可以执行复杂的非线性运算
3.突触不是单一的权重,而是复杂的非线性动力学系统
Backpropagation
计算损失函数的梯度是十分困难的,需要大量的矩阵计算和很多张纸,对于复杂的模型是不可行的。
反向传播本质上是链式传播法则,假设权重为x,y,需要得到,
∂
L
∂
x
,
∂
L
∂
y
\frac{\partial L}{\partial x},\frac{\partial L}{\partial y}
∂x∂L,∂y∂L
网络的输出结果为z,可先求出
∂
L
∂
z
\frac{\partial L}{\partial z}
∂z∂L,从输出向输入方向传播,
∂
L
∂
z
∗
∂
z
∂
x
=
∂
L
∂
x
\frac{\partial L}{\partial z} * \frac{\partial z}{\partial x} = \frac{\partial L}{\partial x}
∂z∂L∗∂x∂z=∂x∂L,可得到结果,多层情况则以此类推。
可得,梯度值是逐层传播的,一直传播到权重层。
基础的反向传播示例:
请注意copy gate等例子,是典型的单输入多输出示例。
注意:矢量形式下,计算会更加简便
vector-valued functions
设权重为x,y,激活函数为f,结果为z,损失函数为L,则
D
x
D_x
Dx为x的矢量形式,
D
y
D_y
Dy为y的矢量形式,
D
z
D_z
Dz为z的矢量形式,值得注意的是,L为标量,其梯度与原变量具有相同的形式。
示意图如下:
当输入数据为多组时,则此时输出数据也应该是多组的,输入、权重都是矩阵,输出也是矩阵。则之前的点乘相加的方式转化为输入与输出矩阵的的乘积,而对于反向传播而言,则是将上游梯度矩阵乘以雅可比矩阵,得到下游梯度矩阵。
假设没有权重,考虑最简单的形式:
注意雅可比矩阵一般要隐式给出:
不考虑激活函数,实际形式如图所示:
代码总结
features.ipynb
核心在于进行Image Features Exercises
Extract Features
对于每一张图像,对提取的特征进行处理,比对原始像素进行处理效果更好。如The problem of linear classifer中所述,图像特征提取有几种方法,一种是定向梯度直方图HoG,另一种是颜色直方图。
这两部分算法在features.py中进行编写,用api的形式被调用。
彩色和灰度图像都可以进行梯度计算,对于彩色图像而言,转化为灰度图进行处理。
使用图像卷积来计算梯度。
在这里,使用HoG和颜色直方图的数据作为一个向量,进行处理。
卷积与梯度
图像卷积:
其形式如图所示
核心公式如下:
f
∗
w
=
∑
(
a
,
b
)
∈
w
(
x
−
a
,
y
−
b
)
∈
f
w
(
a
,
b
)
f
(
x
−
a
,
y
−
b
)
f*w=\sum_{(a,b) \in w (x - a,y - b) \in f}w(a,b)f(x - a,y - b)
f∗w=∑(a,b)∈w(x−a,y−b)∈fw(a,b)f(x−a,y−b)
梯度可以表示为
▽
f
=
[
G
x
G
y
]
=
[
∂
f
(
x
,
y
)
∂
x
∂
f
(
x
,
y
)
∂
y
]
\bigtriangledown f = \begin{bmatrix} G_x \\ G_y \end{bmatrix}=\begin{bmatrix} \frac{\partial f(x,y)}{\partial x} \\ \frac{\partial f(x,y)}{\partial y} \end{bmatrix}\quad
▽f=[GxGy]=[∂x∂f(x,y)∂y∂f(x,y)]
根据两点差分原则,进一步可以表示为
∂
f
∂
x
=
f
(
x
+
1
,
y
)
−
f
(
x
,
y
)
\frac{\partial f}{\partial x} = f(x + 1,y) - f(x,y)
∂x∂f=f(x+1,y)−f(x,y)
∂
f
∂
y
=
f
(
x
,
y
+
1
)
−
f
(
x
,
y
)
\frac{\partial f}{\partial y} = f(x,y + 1) - f(x,y)
∂y∂f=f(x,y+1)−f(x,y)
由此可以得到导数,即梯度。
幅值梯度为
g
=
G
x
2
+
G
y
2
g = \sqrt{G_x^2 + G_y^2}
g=Gx2+Gy2
方向梯度为
θ
=
a
r
c
t
a
n
(
G
y
G
x
)
\theta = arctan(\frac{G_y}{G_x})
θ=arctan(GxGy)
计算梯度有很多方法。
可通过对原图像进行卷积计算得到。
可卷积两次,第一次得到水平梯度,第二次得到垂直梯度。
1.Prewitt 算子。
[
−
1
0
1
−
1
0
1
−
1
0
1
]
\begin{bmatrix} -1 & 0 & 1 \\ -1& 0 & 1 \\ -1& 0 & 1 \end{bmatrix}\quad
⎣⎡−1−1−1000111⎦⎤
用于计算水平梯度,检测垂直边缘
[
−
1
−
1
−
1
0
0
0
1
1
1
]
\begin{bmatrix} -1 & -1 & -1 \\ 0 & 0 & 0 \\ 1& 1 & 1 \end{bmatrix}\quad
⎣⎡−101−101−101⎦⎤
用于计算垂直梯度,检测水平边缘
2.Sobel算子
[
−
1
0
1
−
2
0
2
−
1
0
1
]
\begin{bmatrix} -1 & 0 & 1 \\ -2 & 0 & 2 \\ -1& 0 & 1 \end{bmatrix}\quad
⎣⎡−1−2−1000121⎦⎤
用于计算水平梯度,检测垂直边缘
[
−
1
−
2
−
1
0
0
0
1
2
1
]
\begin{bmatrix} -1& -2 & -1 \\ 0& 0 & 0 \\ 1& 2 & 1 \end{bmatrix}\quad
⎣⎡−101−202−101⎦⎤
用于计算垂直梯度,检测水平边缘
HoG
第一步是计算整个图像的x、y方向的梯度。
可采取卷积的方式,但在这里采用两点差分的原则。
gx = np.zeros(image.shape)
gy = np.zeros(image.shape)
gx[:, :-1] = np.diff(image, n=1, axis=1) # compute gradient on x-direction
gy[:-1, :] = np.diff(image, n=1, axis=0) # compute gradient on y-direction
之后将横向和纵向的梯度平方求和,在开根号,得到幅值梯度。
值得注意的是,gx,gy的形状是相同的。
计算图像任意一点的方向梯度,由于得到的是弧度,所以先转化为角度,因为actan的大小为[-90,90],所以要加上90度,使范围达到[0,180]
grad_mag = np.sqrt(gx ** 2 + gy ** 2) # gradient magnitude
grad_ori = np.arctan2(gy, (gx + 1e-15)) * (180 / np.pi) + 90 # gradient orientation
之后将整个图像划分为若干个8 * 8的cell,将每个cell转化为长度为9的数组,为9bins,即将180°分解为9份,根据方向矩阵将,对应度数的梯度幅值加到数组中。
如图所示:
若度数为10°,则按比例加载到0°,20°中。如图所示,幅值为4,则0°加2,20°加2。
若度数为160°-180°之间,则需按比例加到0°,160°的bin中。
这就是梯度向量直方图。
可以生成n_cellsx * n_cellsy个cell
建立对应的向量
sx, sy = image.shape # image size
orientations = 9 # number of gradient bins
cx, cy = (8, 8) # pixels per cell
n_cellsx = int(np.floor(sx / cx)) # number of cells in x
n_cellsy = int(np.floor(sy / cy)) # number of cells in y
# compute orientations integral images
orientation_histogram = np.zeros((n_cellsx, n_cellsy, orientations))
遍历9个相邻的角度之间,如0-20°,20-40°等,将方向梯度矩阵所有超过角度范围内的值设置为0,据此得到标记矩阵,保留所有角度矩阵非零的幅值矩阵的值,其他值都设置为0。
在这里使用了均值滤波。
均值滤波是指在图像的一个方块区域内,中心像素的值是整个像素的平均值。以cell的形状为方块大小,遍历整个图像,得到图像各个点的像素值。
为了使取方块区域恰好在图像范围内,且方块之间互相不重叠,则规定只从cell横纵坐标的一半开始取值,每隔cell的横纵坐标的大小,开始取值。
最终得到结果,并保存在每隔cell对应的角度矩阵里。
for i in range(orientations):
# create new integral image for this orientation
# isolate orientations in this range
temp_ori = np.where(grad_ori < 180 / orientations * (i + 1), grad_ori, 0)
temp_ori = np.where(grad_ori >= 180 / orientations * i, temp_ori, 0)
# select magnitudes for those orientations
cond2 = temp_ori > 0
temp_mag = np.where(cond2, grad_mag, 0)
orientation_histogram[:, :, i] = uniform_filter(temp_mag, size=(cx, cy))[round(cx / 2) :: cx, round(cy / 2) :: cy].T
颜色直方图
根据图像的通道分为n个向量。
每个向量为该通道对应像素值的比例。
例如:
H通道,向量共有30个数,第一个数表示,像素值在[0,5]范围内的个数,第二个数就是像素在[6,11]范围内的个数,以此类推。
颜色直方图可以用现成的api调用,方法很多。
可以采用opencv,或者是matplotlib来实现。
在此,不做过多的说明。