PyTorch——CrossEntopyLoss和NLLLoss的区别


NLLLoss

  • 使用

    1
    torch.nn.NLLLoss()
  • 具体操作

    • 返回负对数似然损失(The negative log likelihood loss)
    • 虽然命名是负对数自然损失, 但是实际上本函数不含有log操作,本函数假设log操作在输入前已经完成了
  • 常用于分类问题的损失函数

  • 一般适用于网络最后一层为log_softmax的时候

计算公式

  • 单个样本的计算公式:
    • 普通样本计算公式:
      $$loss(x, class) = -x[class]$$
    • 带有权重的单个样本计算公式:
      $$loss(x, class) = -weights[class] * x[class]$$
    • 因为多类别分类中,类别中只有一个维度是1, 其他维度都是0, 所以在计算时只考虑为1的维度就行, 为0的维度与当前类别值相乘为0
      • (在这里我们存储的不是向量,而是该为1的维度的索引,所以使用-x[class]即可直接取出该样本对应的对数似然损失,其中,取对数的操作在输入前已经完成了)
  • 批量样本的计算公式:
    • size_average=True(default):
      $$all\_loss = \frac{1}{mini\_batch\_size}\sum_i loss(x_i, class_i)$$
    • size_average=False:
      $$all\_loss = \sum_i loss(x_i, class_i)$$

CrossEntropyLoss

  • 使用

    1
    torch.nn.CrossEntropyLoss()
  • 具体操作

    • 等价于 log_softmax + torch.nn.NLLLoss()
    • 先对网络输出做对数似然, 然后再
  • softmax的定义
    $$softmax(x_{i}) = \frac{e^{x_{i}}}{\sum_{j=1}x_{j}}$$

  • log_softmax的定义
    $$log(softmax(x_{i}))$$

    • 注意: 这里的log是以e为底的对数

为什么是这种实现方式?

  • 为什么是log_softmax + torch.nn.NLLLoss()的方式而不是普通的计算方式
    • 普通的计算方式是直接对每个概率求出log值, 然后相加, 本质上是一样的
    • CrossEntropyLoss中多了个softmax是为了保证输入都是概率值
  • log(softmax(x))的优化
    • 实际上使用的是log_softmax(x)
    • log_softmax(x)的运算速度比单独计算softmax + log的速度快
    • 同时二者的运算结果相同
    • 文档中没有提到, 但是一种可能的优化方法是
      $$
      \begin{align}
      log_sofmax(x) &= log \frac{e^{x_{i}}}{\sum_{j=1}x_{j}} \\
      &= log e^{x_i} - log \sum_{j=1}x_{j} \\
      &= x_i - log \sum_{j=1}x_{j}
      \end{align}
      $$
    • 上面的式子中,只需要计算一次 \(log \sum_{j=1}x_{j}\)即可(且不同维度可重用该值), 其他的都是加减法运算

相关损失函数 BCELoss

  • 使用

    1
    torch.nn.BCELoss()
  • 具体操作

    • 就是实现了书上定义的二分类交叉熵定义

计算公式:

  • 单个样本的计算公式:
    • 普通样本计算公式:
      $$ loss(o,t)=-\frac{1}{n}\sum_i(t[i] log(o[i])+(1-t[i]) log(1-o[i])) $$
    • 带有权重的单个样本计算公式:
      $$ loss(o,t)=-\frac{1}{n}\sum_iweights[i]\ (t[i]log(o[i])+(1-t[i])* log(1-o[i])) $$
    • 因为多类别分类中,类别中只有一个维度是1, 其他维度都是0, 所以在计算时只考虑为1的维度就行, 为0的维度与当前类别值相乘为0
      • (在这里我们存储的不是向量,而是该为1的维度的索引,所以使用-x[class]即可直接取出该样本对应的对数似然损失,其中,取对数的操作在输入前已经完成了)
  • 批量样本的计算公式:
    • size_average=True(default):
      $$all\_loss = \frac{1}{mini\_batch\_size}\sum_i loss(o_i, t_i)$$
    • size_average=False:
      $$all\_loss = \sum_i loss(o_i, t_i)$$

BCELoss vs CrossEntropyLoss

  • BCELoss对应的网络只有一个输出值
  • CrossEntropyLoss对应的网络有两个输出值
  • 可以证明, 二分类时BCELossCrossEntropyLoss等价
    • 证明时, 将每个CrossEntropyLoss的计算公式中的 softmax 函数分子分母同时除以shift, 即可得到为下面的定义, 进一步可得到BCELoss的计算公式
      $$f_i(x) = \frac{e^{(x_i - shift)}} { \sum^j e^{(x_j - shift)}},shift = max (x_i)$$

相关损失函数 MultiLabelMarginLoss

  • 使用

    1
    torch.nn.MultiLabelMarginLoss()
  • 用于多标签分类的损失函数


总结

  • 一般来说直接使用CrossEntropyLoss即可
    • 二分类时还可以使用nn.BCELoss
    • 二分类时使用nn.BCELoss的话,输入的inputtarget维度都为n * 1的维度
    • 二分类时使用CrossEntropyLoss则输入的inputn * 2的维度
  • 如果使用NLLLoss则一定记得在输出层最后加一层log_softmax
  • 注意,log指的是以e为底的对数函数,而不是以10为底的
    • Mac自带的计算器中log是以10为底的,ln才是以e为底的