虽然吴恩达推荐在第一次实现神经网络的时候用
for
循环挨个挨个传入样本正向传播反向传播,每次累加误差与隐藏层的成绩来计算梯度,但我不满意呀,妥妥能向量化的东西,怎么能用
for
循环呢?我直接进行一个向量化神经网络传播的推导。
向量化终极技巧
当你不知道矩阵该怎么运算的时候,直接把线性式按照遵循矩阵运算规则的形式替换一下,一般就是对的。
神经网络架构
这里使用的是吴恩达机器学习ex4里的架构:
分为三层,输入层、隐藏层与输出层。输入层为400个单元分别对应手写数字图片中的400个像素,隐藏层有25个单元,输出层有10个单元对应10个数字。
输入矩阵
X
X
X为5000×400的矩阵,包含5000张手写数字图片,每张图片有20×20=400个像素。
X
=
[
−
(
x
(
1
)
)
T
−
−
(
x
(
2
)
)
T
−
⋮
−
(
x
(
m
)
)
T
−
]
X=\left[\begin{matrix} -(x^{(1)})^T-\\ -(x^{(2)})^T-\\ \vdots\\ -(x^{(m)})^T-\\ \end{matrix}\right]
X=⎣⎢⎢⎢⎡−(x(1))T−−(x(2))T−⋮−(x(m))T−⎦⎥⎥⎥⎤
对于答案矩阵
y
y
y,我们需要处理一下,
y
y
y本来是m×1的列向量,
y
(
i
)
y^{(i)}
y(i)为第
i
i
i张图片上的数字。现在我们要把它转化成与我们神经网络的输出相同的格式,即若第
i
i
i张图片上是4,则我们期望的神经网络输出是
a
(
3
)
=
[
0
0
0
1
0
0
0
0
0
0
]
a^{(3)}= \left[\begin{matrix} 0\\ 0\\ 0\\ 1\\ 0\\ 0\\ 0\\ 0\\ 0\\ 0\\ \end{matrix}\right]
a(3)=⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎡0001000000⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎤
由此,我们把列向量
y
y
y转换成
m
×
s
L
m\times s_L
m×sL的矩阵,包含m个样本我们期望的神经网络输出。
参数矩阵遵循原定义, Θ ( l ) = s l + 1 × ( s l + 1 ) \Theta^{(l)}=s_{l+1}\times(s_l+1) Θ(l)=sl+1×(sl+1),为 l + 1 l+1 l+1层从 l l l层转移的权值。
正向传播
向前传播比较简单,因为每一层的激励矩阵都是
m
×
s
l
m\times s_l
m×sl的,我们首先在左侧为矩阵加上偏置(一列1),矩阵变为
m
×
(
s
l
+
1
)
m\times(s_l+1)
m×(sl+1)的,要传播到下一层,就乘以参数矩阵的转置再通过逻辑函数,即
a
(
l
+
1
)
=
g
(
[
1
a
(
l
)
]
Θ
(
l
)
)
a^{(l+1)}=g([1\quad a^{(l)}]\Theta^{(l)})
a(l+1)=g([1a(l)]Θ(l))
因为只有一个隐藏层,所以传播两次就到了输出层,得到一个
m
×
s
L
m\times s_L
m×sL的矩阵。
反向传播
代价函数
正好我们的目标矩阵
y
y
y也是
m
×
s
L
m\times s_L
m×sL的,两个矩阵之间每行元素都对应一个样本,所以我们套用代价函数公式:
J
(
Θ
)
=
−
1
m
∑
i
=
1
m
∑
k
=
1
K
[
y
k
(
i
)
log
(
(
h
Θ
(
x
(
i
)
)
)
k
)
+
(
1
−
y
k
(
i
)
)
log
(
1
−
(
h
Θ
(
x
(
i
)
)
)
k
)
]
+
λ
2
m
∑
l
=
1
L
−
1
∑
i
=
1
s
l
∑
j
=
1
s
l
+
1
(
Θ
j
i
(
l
)
)
2
J(\Theta)=-\frac{1}{m}\sum_{i=1}^m\sum_{k=1}^K[y_k^{(i)}\log((h_\Theta(x^{(i)}))_k)+(1-y_k^{(i)})\log(1-(h_\Theta(x^{(i)}))_k)]+\frac{\lambda}{2m}\sum_{l=1}^{L-1}\sum_{i=1}^{s_l}\sum_{j=1}^{s_{l+1}}(\Theta_{ji}^{(l)})^2
J(Θ)=−m1i=1∑mk=1∑K[yk(i)log((hΘ(x(i)))k)+(1−yk(i))log(1−(hΘ(x(i)))k)]+2mλl=1∑L−1i=1∑slj=1∑sl+1(Θji(l))2
这里
y
k
(
i
)
y_k^{(i)}
yk(i)和
h
Θ
(
x
(
i
)
)
k
h_\Theta(x^{(i)})_k
hΘ(x(i))k都是对应相乘的,很显然我们这里要改成矩阵形式的话就应该使用点乘,求和用sum
函数,所以有
J
(
Θ
)
=
−
1
m
[
sum
(
y
.
∗
log
(
a
(
3
)
)
)
+
sum
(
(
1
−
y
)
.
∗
(
log
(
1
−
a
(
3
)
)
)
)
]
+
λ
2
m
∑
l
=
1
L
−
1
∑
i
=
1
s
l
∑
j
=
1
s
l
+
1
(
Θ
j
i
(
l
)
)
2
J(\Theta)=-\frac{1}{m}[\text{sum}(y.*\log(a^{(3)}))+\text{sum}((1-y).*(\log(1-a^{(3)})))]+\frac{\lambda}{2m}\sum_{l=1}^{L-1}\sum_{i=1}^{s_l}\sum_{j=1}^{s_{l+1}}(\Theta_{ji}^{(l)})^2
J(Θ)=−m1[sum(y.∗log(a(3)))+sum((1−y).∗(log(1−a(3))))]+2mλl=1∑L−1i=1∑slj=1∑sl+1(Θji(l))2
后面的正则项直接按项平方后求和即可,注意偏置项不参与正则化,如果用矩阵整个运算的话,要记得把第一列减掉。
误差传播
为了计算梯度,我们先算误差。输出层的误差很好求,直接
a
(
3
)
a^{(3)}
a(3)与
y
y
y相减再转置一下:
δ
(
3
)
=
(
a
(
3
)
−
y
)
T
\delta^{(3)}=(a^{(3)}-y)^T
δ(3)=(a(3)−y)T
得到一个
s
L
×
m
s_L\times m
sL×m的矩阵。之所以要转置,是因为之前在推单个样本的反向传播时,
δ
\delta
δ都是
s
L
×
1
s_L\times 1
sL×1的列向量,为了更好的套用公式,所以转置一下让每个样本的
δ
\delta
δ仍为列向量。
误差传播公式为
δ
(
l
)
=
(
(
Θ
(
l
)
)
T
δ
(
l
+
1
)
)
.
∗
a
(
l
)
.
∗
(
1
−
a
(
l
)
)
\delta^{(l)}=((\Theta^{(l)})^T\delta^{(l+1)}).*a^{(l)}.*(1-a^{(l)})
δ(l)=((Θ(l))Tδ(l+1)).∗a(l).∗(1−a(l))
而
Θ
(
l
)
\Theta^{(l)}
Θ(l)为
s
l
+
1
×
(
s
l
+
1
)
s_{l+1}\times(s_l+1)
sl+1×(sl+1)的矩阵,所以完美符合上面的公式,我们把
Θ
(
l
)
\Theta^{(l)}
Θ(l)转置一下跟
δ
(
l
+
1
)
\delta^{(l+1)}
δ(l+1)相乘,得到一个
(
s
l
+
1
)
×
m
(s_l+1)\times m
(sl+1)×m的矩阵。至于后面的点乘部分,加上了偏置项的
a
(
l
)
a^{(l)}
a(l)是
m
×
(
s
l
+
1
)
m\times (s_l+1)
m×(sl+1)的,我们转置一下再按位相乘即可。这样我们就完成了一次误差的反向传播!
但是,还有一件很重要的事情,我们是不计算偏置项的误差的,因为偏置项都恒为 + 1 +1 +1,改变的是系数矩阵,所以我们要去掉除 δ ( L ) \delta^{(L)} δ(L)外所有误差矩阵的第一行,最后 δ ( l ) \delta^{(l)} δ(l)是 s l × m s_l\times m sl×m的。
计算梯度
要计算梯度,先计算
Δ
(
l
)
\Delta^{(l)}
Δ(l),单个样本的计算式为
Δ
(
l
)
:
=
Δ
(
l
)
+
δ
(
l
+
1
)
(
a
(
l
)
)
T
\Delta^{(l)}:=\Delta^{(l)}+\delta^{(l+1)}(a^{(l)})^T
Δ(l):=Δ(l)+δ(l+1)(a(l))T
这里
δ
(
l
+
1
)
\delta^{(l+1)}
δ(l+1)和
a
(
l
)
a^{(l)}
a(l)都是列向量,所以需要转置一下
a
(
l
)
a^{(l)}
a(l),但在上面的计算中
a
(
l
)
a^{(l)}
a(l)是
m
×
(
s
l
+
1
)
m\times (s_l+1)
m×(sl+1)的矩阵,
δ
(
l
+
1
)
\delta^{(l+1)}
δ(l+1)是
s
l
+
1
×
m
s_{l+1}\times m
sl+1×m的矩阵,正好每个样本的
δ
(
l
+
1
)
\delta^{(l+1)}
δ(l+1)竖着放,
a
(
l
)
a^{(l)}
a(l)横着放,所以我们不需要转置,直接相乘即可,还顺便完成了求和工作,即
Δ
(
l
)
=
δ
(
l
+
1
)
a
(
l
)
\Delta^{(l)}=\delta^{(l+1)}a^{(l)}
Δ(l)=δ(l+1)a(l)
非常的简洁。
有了
Δ
\Delta
Δ,剩下的工作就很简单了,由
∂
∂
Θ
i
j
(
l
)
J
(
Θ
)
=
D
i
j
(
l
)
=
{
1
m
(
Δ
i
j
(
l
)
+
λ
Θ
i
j
(
l
)
)
j
≠
0
1
m
Δ
i
j
(
l
)
j
=
0
\frac{\partial}{\partial\Theta_{ij}^{(l)}}J(\Theta)=D_{ij}^{(l)}=\left\{ \begin{aligned} &\frac{1}{m}(\Delta_{ij}^{(l)}+\lambda\Theta_{ij}^{(l)})&&j\not=0\\ &\frac{1}{m}\Delta_{ij}^{(l)}&&j=0\\ \end{aligned}\right.
∂Θij(l)∂J(Θ)=Dij(l)=⎩⎪⎨⎪⎧m1(Δij(l)+λΘij(l))m1Δij(l)j=0j=0
我们将
Δ
\Delta
Δ矩阵除以
m
m
m,再给需要正则化的参数加上正则项,就得到了梯度。