@Feiteng
2015-07-11T21:40:59.000000Z
字数 6543
阅读 19406
Feiteng Email:lifeiteng0422@gmail.com
原文发在[分分钟推导神经网络],但网站不能解析Latex,转到这里。
这一节展示对矩阵、向量求导过程使用链式法则、转置、组合等技巧来快速完成求导
一个原则维数相容,这是我发明的词汇,实质是多元微分基本知识:
维数相容就是,如果
x∈Rm×n,f(x)∈R1 ,那么∂f(x)∂x∈Rm×n ;
如果x∈Rm×1,f(x)∈Rn ,那么∂f(x)∂x∈Rn×m .
举例:
J=(Xw−y)T(Xw−y)=||Xw−y||2,其中X∈Rm×n,w∈Rn×1,y∈Rm×1 ,
求∂J∂X、∂J∂w、∂J∂y
解:
step1.把所有参数当做实数来求导,
依据链式法则有
∂J∂X=2(Xw−y)w、∂J∂w=2(Xw−y)X、∂J∂y=−2(Xw−y)
可以看出除了∂J∂y=−2(Xw−y) ,∂J∂X 和∂J∂w 的求导结果连矩阵乘法都不满足
step2.根据step1的求导结果,依据维数相容原则做出调整[前后换序、转置]
依据维数相容原则
∂J∂X∈Rm×n ,但是2(Xw−y)w中(Xw−y)∈Rm×1、w∈Rn×1 ,自然得我们调整为∂J∂X=2(Xw−y)wT
同理:∂J∂w∈Rn×1 , 但是2(Xw−y)X中(Xw−y)∈Rm×1、X∈Rm×n ,那么通过换序和转置我们可以得到维数相容的结果2XT(Xw−y)
对于矩阵、向量求导:
神经网络的训练过程:反向传播求得“各层”参数W和b的导数之后,做梯度下降[一阶的GD、SGD,二阶的LBFGS、共轭梯度]。
反向传播是求导的链式法则和相容原则的完美体现,对每一层的参数求导都利用上一层的中间结果来完成。
这里的标号,参考UFLDL教程
前向传播记号(公式1):
很多DL的opensoure(caffe,Kaldi/src/nnet)实现并不是按照上面的层[
各层在BP的时候偏导数的时候,分清楚该层的输入、输出即能正确编程实现,如:
class AffineTransform
{ //a(l)=in(列向量), z(l+1)=out(列向量)
void Forward(const Vector &in, Vector *out)
{
out = W*in + b;
}
void Backward(const Vector &in, const Vector &out, const Vector &out_diff, const Vector *in_diff)
{
in_diff = W^T * out_diff; //(注)
}
void Update(const Vector &out_diff, const Vector& in)
{
W_diff = out_diff * in^T;
b_diff = out_diff;
W = W - learn_rate*W_diff;
b = b - learn_rate*b_diff;
}
private:
Matrix W;
Matrix W_diff;
Vector b;
Vector b_diff;
}
(注) out_diff
我们可以把(2)式作为一个Sigmoid层
class Sigmoid
{ //a(l)=in(列向量), z(l+1)=out(列向量)
void Forward(const Vector &in, Vector *out)
{
out = sigmoid(in);//y = 1/(1+e^-x)
}
void Backward(const Vector &in, const Vector &out, const Vector &out_diff, const Vector *in_diff)
{
in_diff = out.*(1-out).*out_diff; //dy = y(1-y)dx
}
void Update(const Vector &out_diff, const Vector& in)=0
private:
}
(注) out_diff
卷积怎么求导呢?实际上卷积可以通过矩阵乘法来实现[caffe里面是不是有image2col()],也可以使用FFT在频率域做加法,通常后者更快一些。
那么既然通过矩阵乘法,我们上面的秘密武器仍然可以运用,但卷积层求导这块还是要比MLP复杂一些,要做些累加的操作。具体怎么做还要看编程时选择什么样的策略、数据结构。
掌握了理论推导,就会觉得工程经验才是干货!