BCELoss、CrossEntropyLoss

PyTorch 提供了两种损失函数的使用方法:函数形式、模块形式,函数形式的损失函数定义在 torch.nn.functional 库中,使用时传入神经网络的预测值和目标值来计算损失,模型形式是通过构建一个模块的实例对象,然后通过模块的 forward 方法来计算损失,损失函数模型在 torch.nn 库。

在分类问题中,常用的损失函数有: BCELoss、BCEWithLogitsLoss、CrossEntropyLoss。

下面为损失函数使用代码示例:

import torch.nn as nn
import torch.nn.functional as F
import torch


# 1. 函数形式
def test01():

    # 固定随机数种子
    torch.manual_seed(0)

    inputs = torch.randint(0, 10, size=[10, 1]).float()
    target = torch.randint(0, 10, size=[10, 1]).float()

    print(inputs)
    print(target)

    # 第一个参数为网络的预测值
    # 第二个参数为目标值
    loss = F.mse_loss(inputs, target)
    print('loss: %.2f' % loss.item())


# 2. 模块形式:
def test02():

    # 固定随机数种子
    torch.manual_seed(0)

    inputs = torch.randint(0, 10, size=[10, 1]).float()
    target = torch.randint(0, 10, size=[10, 1]).float()

    # 实例化模块对象
    criterion = nn.MSELoss()
    # 计算损失
    loss = criterion(inputs, target)
    print('loss: %.2f' % loss.item())


if __name__ == '__main__':
    test01()
    test02()

程序输出结果:

tensor([[4.],
        [9.],
        [3.],
        [0.],
        [3.],
        [9.],
        [7.],
        [3.],
        [7.],
        [3.]])
tensor([[1.],
        [6.],
        [6.],
        [9.],
        [8.],
        [6.],
        [6.],
        [8.],
        [4.],
        [3.]])
loss: 17.70
loss: 17.70

2. BCELoss

BCELoss 用于单标签、多标签的二分类问题,输出和目标值的维度为:(B, C),B 表示样本数量,C 表示类别数量,每一个 C 值表示属于某个标签的概率。

单标签
  1. N 表示样本数量;
  2. y 表示样本的真实标签,0 或者 1;
  3. x 表示样本的预测为正确类别的概率;
  4. 如果样本真实标签为 1,则希望 x 的值概率越大越好。如果样本的真实标签为 0,则希望 x 的值越小越好.
多标签
  1. N 表示样本数量;
  2. M 表示每个样本的真实标签数量;
  3. y 表示某个样本属于某个标签,标签值为 0 或者 1;
  4. x 表示某个样本属于某个标签的概率;

下面为单标签和多标签计算损失的示例代码:

import torch
import torch.nn as nn
import torch.nn.functional as F


# 1. 单标签
def test01():

    # 固定随机数种子
    torch.manual_seed(0)

    # 模块方式初始化损失函数
    criterion = nn.BCELoss()

    # 构建目标标签和预测概率值
    y_true = torch.randint(0, 2, size=[10, 1]).float()
    y_pred = torch.sigmoid(torch.randn(10, 1)).float()

    print(y_true)
    print(y_pred)

    # 输入形状: (batch_size, pred_proba)
    loss = criterion(y_pred, y_true)
    print('loss: %.2f' % loss.item())


# 2. 多标签
def test02():

    # 固定随机数种子
    torch.manual_seed(0)

    # 模块方式初始化损失函数
    criterion = nn.BCELoss()

    # 构建目标标签和预测概率值
    y_true = torch.randint(0, 2, size=[10, 2]).float()
    y_pred = torch.sigmoid(torch.randn(10, 2)).float()

    print(y_true)
    print(y_pred)

    # 输入形状: (batch_size, pred_proba)
    loss = criterion(y_pred, y_true)
    print('loss: %.2f' % loss.item())


if __name__ == '__main__':
    test01()
    print('-' * 30)
    test02()

程序输出结果:

tensor([[0.],
        [1.],
        [1.],
        [0.],
        [1.],
        [1.],
        [1.],
        [1.],
        [1.],
        [1.]])
tensor([[0.5400],
        [0.3529],
        [0.3137],
        [0.7431],
        [0.4350],
        [0.7440],
        [0.6025],
        [0.6984],
        [0.3044],
        [0.3111]])
loss: 0.87
------------------------------
tensor([[0., 1.],
        [1., 0.],
        [1., 1.],
        [1., 1.],
        [1., 1.],
        [1., 0.],
        [0., 1.],
        [0., 0.],
        [0., 0.],
        [0., 1.]])
tensor([[0.6454, 0.1744],
        [0.4155, 0.8645],
        [0.4462, 0.3224],
        [0.6371, 0.5645],
        [0.4566, 0.3365],
        [0.7187, 0.6198],
        [0.7691, 0.5211],
        [0.2315, 0.4988],
        [0.3733, 0.4239],
        [0.1707, 0.8464]])
loss: 0.81

2. BCEWithLogitsLoss

BCEWithLogitsLoss 和 BCELoss 一样用于单标签、或者多标签的二分类问题,它相当于 Sigmoid 和 BCELoss 的结合,对网络的输出结果先进行 Sigmoid 计算将预测值的映射到 (0, 1) 之间,再对其使用 BCELoss 计算损失。

由此可见,当我们的神经网络最后一层使用的是 Sigmoid 时,则直接使用 BCELoss 计算损失,否则使用 BCEWithLogitsLoss 损失函数。

单标签
多标签

BCEWithLogitsLoss 的计算公式中只是比 BCELoss 多个一个 sigmoid 函数计算。

下面的示例代码仅仅把 sigmoid 函数去除,其他并没有改变:

import torch
import torch.nn as nn
import torch.nn.functional as F


# 1. 单标签
def test01():

    # 固定随机数种子
    torch.manual_seed(0)

    # 模块方式初始化损失函数
    criterion = nn.BCEWithLogitsLoss()

    # 构建目标标签和预测概率值
    y_true = torch.randint(0, 2, size=[10, 1]).float()
    y_pred = torch.randn(10, 1).float()

    print(y_true)
    print(y_pred)

    # 输入形状: (batch_size, pred_proba)
    loss = criterion(y_pred, y_true)
    print('loss: %.2f' % loss.item())


# 2. 多标签
def test02():

    # 固定随机数种子
    torch.manual_seed(0)

    # 模块方式初始化损失函数
    criterion = nn.BCEWithLogitsLoss()

    # 构建目标标签和预测概率值
    y_true = torch.randint(0, 2, size=[10, 2]).float()
    y_pred = torch.randn(10, 2)

    print(y_true)
    print(y_pred)

    # 输入形状: (batch_size, pred_proba)
    loss = criterion(y_pred, y_true)
    print('loss: %.2f' % loss.item())


if __name__ == '__main__':
    test01()
    print('-' * 30)
    test02()

程序输出结果:

tensor([[0.],
        [1.],
        [1.],
        [0.],
        [1.],
        [1.],
        [1.],
        [1.],
        [1.],
        [1.]])
tensor([[ 0.1604],
        [-0.6065],
        [-0.7831],
        [ 1.0622],
        [-0.2613],
        [ 1.0667],
        [ 0.4159],
        [ 0.8396],
        [-0.8265],
        [-0.7949]])
loss: 0.87
------------------------------
tensor([[0., 1.],
        [1., 0.],
        [1., 1.],
        [1., 1.],
        [1., 1.],
        [1., 0.],
        [0., 1.],
        [0., 0.],
        [0., 0.],
        [0., 1.]])
tensor([[ 0.5988, -1.5551],
        [-0.3414,  1.8530],
        [-0.2159, -0.7425],
        [ 0.5627,  0.2596],
        [-0.1740, -0.6787],
        [ 0.9383,  0.4889],
        [ 1.2032,  0.0845],
        [-1.2001, -0.0048],
        [-0.5181, -0.3067],
        [-1.5810,  1.7066]])
loss: 0.81

3. CrossEntropyLoss

CrossEntropyLoss 主要用于多分类问题,输出和目标的维度是 (batch, C) ,batch 表示样本数量,C 表示类别数量。假设:某个样本预测的类别有 10 个,神经网络对 10 个类别都得到了 logits,CrossEntropyLoss 会对这 10 个 logits 计算 softmax,即: 属于某个类别的概率,然后找到最大概率的索引作为预测类别.

下列为使用示例代码:

import torch
import torch.nn as nn
import torch.nn.functional as F


# 1. 单标签
def test01():

    # 固定随机数种子
    torch.manual_seed(0)

    # 模块方式初始化损失函数
    criterion = nn.CrossEntropyLoss()

    # 程序表示10个样本的标签为 0-7
    y_true = torch.randint(0, 4, size=[10,])
    # 程序表示10个样本,每个样本都预测为8个标签的logits
    y_pred = torch.randn(10, 4).float()

    print(y_true)
    print(y_pred)

    # 输入形状: (batch_size, pred_proba)
    loss = criterion(y_pred, y_true)
    print('loss: %.2f' % loss.item())


# 2. 多标签
def test02():

    # 固定随机数种子
    torch.manual_seed(0)

    # 模块方式初始化损失函数
    criterion = nn.CrossEntropyLoss()

    # 程序表示10个样本,每个样本有2个目标标签
    y_true = torch.randint(0, 4, size=[5, 2])
    # 程序表示10个样本,每个样本预测8个类别,每个类别预测2个标签的logits
    y_pred = torch.randn(5, 4, 2).float()

    print(y_true)
    print(y_pred)

    # 输入形状: (batch_size, pred_proba)
    loss = criterion(y_pred, y_true)
    print('loss: %.2f' % loss.item())

if __name__ == '__main__':
    test01()
    print('-' * 30)
    test02()

程序的输出结果:

tensor([0, 3, 1, 0, 3, 3, 3, 3, 1, 3])
tensor([[ 0.4913, -0.2041, -0.0885,  0.5239],
        [-0.6659,  0.8504, -1.3527, -1.6959],
        [ 0.7854,  0.9928, -0.1932, -0.3090],
        [ 0.5026, -0.8594,  0.7502, -0.5855],
        [ 1.4437,  0.2660,  0.1665,  0.8744],
        [-0.1435, -0.1116, -0.6136,  0.0316],
        [ 1.0554,  0.1778, -0.2303, -0.3918],
        [ 0.5433, -0.3952,  0.2055, -0.4503],
        [ 1.5210,  3.4105, -1.5312, -1.2341],
        [ 1.8197, -0.5515, -1.3253,  0.1886]])
loss: 1.45
------------------------------
tensor([[0, 3],
        [1, 0],
        [3, 3],
        [3, 3],
        [1, 3]])
tensor([[[ 0.4913, -0.2041],
         [-0.0885,  0.5239],
         [-0.6659,  0.8504],
         [-1.3527, -1.6959]],

        [[ 0.7854,  0.9928],
         [-0.1932, -0.3090],
         [ 0.5026, -0.8594],
         [ 0.7502, -0.5855]],

        [[ 1.4437,  0.2660],
         [ 0.1665,  0.8744],
         [-0.1435, -0.1116],
         [-0.6136,  0.0316]],

        [[ 1.0554,  0.1778],
         [-0.2303, -0.3918],
         [ 0.5433, -0.3952],
         [ 0.2055, -0.4503]],

        [[ 1.5210,  3.4105],
         [-1.5312, -1.2341],
         [ 1.8197, -0.5515],
         [-1.3253,  0.1886]]])
loss: 2.14

CrossEntropyLoss 等价于使用 LogSoftmax 和 NLLLoss 的组合。

未经允许不得转载:一亩三分地 » BCELoss、CrossEntropyLoss