矩阵展开与复原
展开
当我们不想自己手写梯度下降或者别的什么算法,而是想直接调用已有函数的话,都要考虑兼容性问题。比如matlab自带的fminunc
函数,它可以比梯度下降更快的找到使函数值最小的参数,但它要求使用者提供代价-梯度函数,且返回的代价、梯度和作为自变量的参数都要是向量形式。但在神经网络原理部分,参数、梯度都是矩阵形式,这个时候就需要我们对矩阵进行展开,使其变成向量。
对于单个矩阵A
,要将它变成列向量,直接使用A=A(:)
即可。若想把多个矩阵合并为一个列向量,将它们分别展开为列向量后再沿列组合即可:
A=[A1(:);A2(:);A3(:)];
复原
用列向量形式求出了优化值,但要在神经网络中运算的话,还是矩阵形式更方便,于是我们还需要把矩阵从列向量中复原回来。我们利用reshape
函数,把列向量中对应的部分提取出来,按照给定的参数构成矩阵:
A1=reshape(A(k+1:k+n*m),n,m);
梯度检验
反向传播计算梯度是一个效率很高、很精妙的算法,但精妙意味着容易写错,有时候我们想确认我们写出来的反向传播算法是否有错误,我们可以写一个虽然效率低但更容易实现的算法计算梯度,将两个算法的结果放在一起比较,若每个元素都基本一致,就可以放心的使用了。
这个低效率的算法就是利用定义去计算梯度,对于函数
J
(
Θ
)
J(\Theta)
J(Θ),它的偏导数为
∂
∂
Θ
j
J
(
Θ
)
≈
J
(
Θ
1
,
⋯
,
Θ
j
+
ε
,
⋯
,
Θ
n
)
−
J
(
Θ
1
,
⋯
,
Θ
j
−
ε
,
⋯
,
Θ
n
)
2
ε
\frac{\partial}{\partial \Theta_j}J(\Theta)\approx\frac{J(\Theta_1,\cdots,\Theta_j+\varepsilon,\cdots,\Theta_n)-J(\Theta_1,\cdots,\Theta_j-\varepsilon,\cdots,\Theta_n)}{2\varepsilon}
∂Θj∂J(Θ)≈2εJ(Θ1,⋯,Θj+ε,⋯,Θn)−J(Θ1,⋯,Θj−ε,⋯,Θn)
为了使这个值比较精确,
ε
\varepsilon
ε一般取
1
0
−
4
10^{-4}
10−4,我们对每个参数都执行一遍该过程,就可以得到梯度矩阵。
可以发现,这个方法的确很慢,每计算一个导数就要得到两个代价函数值,意味着要做两次向前传播,而反向传播计算所有单元的偏差值只需要反向传播一次,高下立判。
随机初始化
在前面的算法中,我们一般都将参数初始化为全0,但这在神经网络中是不可行的。
考虑上图所示的神经网络,若参数都为0,那么输入值和参数矩阵相乘后还是0,则隐藏层的值都为
g
(
0
)
=
0.5
g(0)=0.5
g(0)=0.5,以此类推,所有的隐藏层单元都会是
0.5
0.5
0.5。相应地,在计算偏差的时候它们也会保持一致,之后计算出的梯度也会一致,这样隐藏层的多个单元都没有意义,因为它们更新之后都是一样的值。
所以,在初始化的时候,我们会采用随机初始化的办法对参数进行赋值,用rand(n,m)
就可以生成一个
n
×
m
n\times m
n×m的随机矩阵,元素的值都在
[
0
,
1
]
[0,1]
[0,1]之间。若想要改变取值范围为
[
−
a
,
a
]
[-a,a]
[−a,a],使用
A=rand(n,m)*(2*a)-a;
即可。