Softmax层概述
Softmax是将神经网络得到的多个值,进行归一化处理,使得到的值在[0,1]之间,让结果变得可解释。Softmax可以将结果看作是概率,某个类别概率越大,将样本归为该类别的可能性也就越高,因此通常用于多分类。
Softmax输出计算
下图参考李宏毅老师课件中的Softmax的计算图。图中是神经元的输出也可以作为第个类别预测的概率结果,是第个类别的真实值,只能取0或者1,且。
Softmax反向传播
在神经网络后面添加Softmax,真实的标签(或者是类别)就相当于真实的分布,经过Softmax计算出的值就是预测的结果,因此可以使用交叉熵函数来作为损失函数。Softmax的损失函数为
反向传播的过程其实就是计算 的过程,根据导数的链式法则:
根据前向计算过程得出:
当的时候
当的时候
根据求导得出:
综合两项偏导数,推出:
注:因为如果给定一个样本,那么他对应的真实标签只有一个值为,其余为,故。
代码实现
前向计算实现
先调用softmax_cpu实现前向计算,再调用softmax_x_ent_cpu计算整个网络的损失函数值和当前层的sensitivity即l.delta的值。
void forward_softmax_layer(const softmax_layer l, network_state net)
{
if(l.softmax_tree){
int i;
int count = 0;
for (i = 0; i < l.softmax_tree->groups; ++i) {
int group_size = l.softmax_tree->group_size[i];
softmax_cpu(net.input + count, group_size, l.batch, l.inputs, 1, 0, 1, l.temperature, l.output + count);
count += group_size;
}
} else {
softmax_cpu(net.input, l.inputs/l.groups, l.batch, l.inputs, l.groups, l.inputs/l.groups, 1, l.temperature, l.output);
}
if(net.truth && !l.noloss){
softmax_x_ent_cpu(l.batch*l.inputs, l.output, net.truth, l.delta, l.loss);//计算softmax的sensitivity
l.cost[0] = sum_array(l.loss, l.batch*l.inputs);//计算网络的总损失
}
}
这个实现很简单,就是一步步按照前面的公式,计算指数值,再求和,最后归一化。
void softmax(float *input, int n, float temp, float *output, int stride)
{
int i;
float sum = 0;
// 赋初始最大值为float中的最小值-FLT_MAX(定义在float.h中)
float largest = -FLT_MAX;
// 寻找输入中的最大值,至于为什么要找出最大值,是为了数值计算上的稳定,详细请戳:http://freemind.pluskid.org/machine-learning/softmax-vs-softmax-loss-numerical-stability/
// 这篇博客写的不错,博客在接近尾声的时候,提到了为什么要减去输入中的最大值。
for(i = 0; i < n; ++i){
if(input[i*stride] > largest) largest = input[i*stride];
}
for(i = 0; i < n; ++i){
// 在进行指数运算之间,如上面博客所说,首先减去最大值(当然温度参数也要除)
float e = exp(input[i*stride]/temp - largest/temp);
sum += e;//求和
output[i*stride] = e;// 并将每一个输入的结果保存在相应的输出中
}
// 最后一步:归一化转换为概率(就是softmax函数的原型~),最后的输出结果保存在output中
for(i = 0; i < n; ++i){
output[i*stride] /= sum;
}
}
下面的实现计算delta[i]的值的时候是真实值减去预测值,而上面推导的结果是预测值减去真实值。为什么也可以???
void softmax_x_ent_cpu(int n, float *pred, float *truth, float *delta, float *error)
{
int i;
for (i = 0; i < n; ++i) {//n相当于类别数
float t = truth[i];//真值
float p = pred[i];//预测值
error[i] = (t) ? -log(p) : 0;//计算交叉熵损失
delta[i] = t - p;//根据公式计算softmax层的梯度
}
}
反向传播实现
softmax的敏感梯度图在softmax前向inference的时候已经计算完成,所以在反向的时候,这里只要调用axpy_cpu将对应位置的值传回去就行了。
void backward_softmax_layer(const softmax_layer l, network_state net)
{
axpy_cpu(l.inputs*l.batch, 1, l.delta, 1, net.delta, 1);
}
参考资料
【1】http://freemind.pluskid.org/machine-learning/softmax-vs-softmax-loss-numerical-stability/
【2】图示Softmax及交叉熵损失函数
【3】Softmax函数与交叉熵