实现具有交叉熵损失的 Softmax 输出层

问题描述 投票:0回答:1

我正在使用这个仓库(https://github.com/SnailWalkerYC/LeNet-5_Speed_Up)并尝试学习神经网络细节。该仓库用 C 和 CUDA 实现了 LeNet5。我现在关注的是 CPU 部分及其在 seq/ 中的代码。我迷失的一个特别的地方是 seq/lenet.c 中的这个函数

static inline void softmax(double input[OUTPUT], double loss[OUTPUT], int label, int count){
      double inner = 0; 
      for (int i = 0; i < count; ++i){
           double res = 0;
           for (int j = 0; j < count; ++j){
              res += exp(input[j] - input[i]);
           }
           loss[i] = 1. / res;
           inner -= loss[i] * loss[i];
       }
       inner += loss[label];
       for (int i = 0; i < count; ++i){
          loss[i] *= (i == label) - loss[i] - inner;
       }
}

因为没有注释,所以我花了一些时间来了解这个功能。最后我发现它正在计算 MSE 损失函数相对于 softmax 层输入的导数。

然后我尝试将交叉熵损失函数与 softmax 一起使用,所以我用以下函数来替换上面的函数。

static inline void softmax(double input[OUTPUT], double loss[OUTPUT], int label, int count)
{
    double inner = 0;
    double max_input = -INFINITY;

    // Find the maximum input value to prevent numerical instability
    for (int i = 0; i < count; ++i)
    {
        if (input[i] > max_input)
            max_input = input[i];
    }

    // Compute softmax and cross-entropy loss
    double sum_exp = 0;
    for (int i = 0; i < count; ++i)
    {
        double exp_val = exp(input[i] - max_input);
        sum_exp += exp_val;
        loss[i] = exp_val;
    }

    double softmax_output[OUTPUT];
    for (int i = 0; i < count; ++i)
    {
        loss[i] /= sum_exp;
        softmax_output[i] = loss[i];
    }

    // Compute cross-entropy loss and derivatives
    inner = -log(softmax_output[label]);
    for (int i = 0; i < count; ++i)
    {
        
    loss[i] = softmax_output[i] - (i == label);
    }
}

但是,使用我的 softmax() 函数版本,MNIST 识别不起作用。原始版本的准确率>96%。我的交叉熵损失代码有什么问题?

c deep-learning neural-network conv-neural-network softmax
1个回答
0
投票

好吧,我会回答我自己的问题。

我成功地使交叉熵损失与 softmax 一起工作。有两个地方需要调整:

  1. 交叉熵 w.r.t. 的导数Softmax 的输入具有(预测 - 标签)的形式,如许多地方所见。但是,这个特定存储库中的代码必须是
loss[i] = (i == label) - softmax_output[i]; // opposite of the common form

而不是

loss[i] = softmax_output[i] - (i == label); // conforms to the common form

这是因为这个 CNN 更新权重和偏差的方式是违反直觉的:

double k = ALPHA / batchSize;
FOREACH(i, GETCOUNT(LeNet5))
    ((double *)lenet)[i] += k * buffer[i];

因此,要么使用损失导数的相反形式,要么将权重更新更改为

   ((double *)lenet)[i] -= k * buffer[i];
  1. 即使进行了上述修复,这个 MNIST CNN 仍然无法工作。有时训练期间的损失并没有下降。我注意到的另一个奇怪的事情是学习率(代码中的 ALPHA)。此代码将学习率硬编码为
    0.5
    。我见过的大多数地方都使用过
    0.1

通过上述 1) 和 2),模型现在一致地预测 >%96 水平。

嗯,在这个过程中我学到了很多东西。

© www.soinside.com 2019 - 2024. All rights reserved.