- 前言
- Java代码中如何更改激活函数
- 更改前向传播forward()中卷积层激活函数。
- 更改输出层梯度计算公式
- 反向传播求激活函数微分
- 激活函数与标签值的修改
一、前言:
本文探讨在卷积神经网络结构中,如何去处理激活函数的问题。
1、如何在前向传播的时候更改卷积层的激活函数。
2、对输出层损失函数梯度求解时,因输出层激活函数选择不同,梯度会产生变化,并如何正确修改。
3、对于反向传播的时候,根据激活函数的不同,对应反向传播的梯度公式会发生变化。
图片 1
java代码为例,一个神经网络 包含一个输入层、两个卷积层,两个池化层、一个全连接层。
LayerBuilder builder = new LayerBuilder();
builder.addLayer(Layer.buildInputLayer(new Size(28,28)));
builder.addLayer(Layer.buildConvLayer(6, new Size(5, 5)));
builder.addLayer(Layer.buildSampLayer(new Size(2, 2)));
builder.addLayer(Layer.buildConvLayer(12, new Size(5, 5)));
builder.addLayer(Layer.buildSampLayer(new Size(2, 2)));
builder.addLayer(Layer.buildOutputLayer(10));//输出层;
CNN cnn = new CNN(builder, 10);
Learningrate = 0.85, batchSize = 10。采用mnist数据集测试
首先对两个卷积层设置激活函数为sigmoid激活函数
// 设置激活函数
layer.setActivation(new Sigmoid());
再设置输出层激活函数为tanh激活函数
// 设置激活函数
layer.setActivation(new Tanh());
这里注意最后一层tanh激活函数取值范围为 -1~1;(而我们之前用的是sigmoid,取值范围为0~1)
二、Java代码中如何更改激活函数
1、更改前向传播forward()中卷积层激活函数。
首先是输出层第L层。注意到输出层的W,b满足下式:
aL = σ(zL) = σ(WLaL-1 + bL)
注:σ表示当前层激活函数、W为权值、b为偏置。
``Java
final double bias = layer.getBias(j);
sum = Util.matrixOp(sum, new Operator() {
private static final long serialVersionUID = 2469461972825890810L;
@Override
public double process(double value) {
return layer.getActivation().active(value + bias);
}
});
``
这里卷积层我们采用了sigmoid激活函数,这里激活公式为:
- / (1 + Math.pow(Math.E, -x))
2、更改输出层梯度计算公式
这里我们采用的是均方差计算公式。
Lost = ||a-y||2/2;
a = σ(z);
这里均方差代价函数求导得到
F‘(x) = (a-y)σ’(z);
根据求导结果,我们只要知道激活函数的导数,我们就能求出输出层的梯度了。
而这里的梯度是我预先设定好的。
``Java
double gradient = outputLayer.getActivation().gradient(outmaps[m]);
outputLayer.setError(m, 0, 0, gradient*(target[m] - outmaps[m])); //sigmoid
``
3、反向传播求激活函数微分
只是更改前向传播是不够的。反向传播求梯度时,会因激活函数选择不同,梯度公式也会产生变化。
Output = f……(g(z));
前向传播通过复杂的函数映射计算出最后的output,而我们采用梯度下降法,去寻找合适的那个映射,所以我们根据这个复杂的映射求微分。
Gradient = F’g’(z);
而这里采用最大值池化以及sigmoid激活函数;
所以求导结果应该是
Gradient = σ‘(x)*Max’∑1nai
也就是激活函数的与最大池化导数Hadamard乘积。
``Java
outMatrix = Util.matrixOp(map, new Operator() {
private static final long serialVersionUID = 1L;
@Override
public double process(double value) {
return layer.getActivation().gradient(value);
}
});
//这里的输出结果应该除以scale(卷积核面积)
outMatrix = Util.matrixOp(outMatrix,
Util.kronecker(nextError, scale, sampPos), null, null, Util.multiply);
``
- 激活函数与标签值的修改
这里最后一层选取激活函数的时候需要注意一个点。
因为sigmoid取值范围为0~1,我们标签值也是1.所以能契合sigmoid函数。
但是当我们改为tanh激活函数的时候,tanh取值范围为-1~1.所以为了契合标签值,我将tanh函数,以及它的梯度做了个变化。 à 0.5x + 0.5;梯度变为原先的1/2,然后发现也是能正确运行的。
待解决:
Q:如果我不对激活函数但是我更改标签值的时候发现,我将标签改为2.然后我发现无法得到正确结果。