• pytorch 多分类中的损失函数


    前言

    pytorch  中的损失函数:

    1. CrossEntropyLoss
    2. LogSoftmax
    3. NLLLoss

    Softmax

    在多分类的时候,我们希望输出是符合概率分布的,所以利用Softmax做了归一化的处理。

    这个过程非常好理解,将所有的项相加得到分母,各项在作为分子,只不过这里加了一个e为底的指数函数,确保值都大于0。

    多分类的神经网络的最后一层,一般就会用到Softmax,所以最后一层一般不用激活(详见最后的数字分类的代码),因为Softmax就相当于做了激活(将数据映射到0~1)。最终Softmax输出每个类别的概率值。

    CrossEntropyLoss <==> LogSoftmax + NLLLoss

    有了概率值之后,就开始构造损失函数了,这里还是用到交叉熵。

    最大似然估计,散度,交叉熵_code bean的博客-CSDN博客

     回忆一下二分类的交叉熵:当时我们的函数时BCE

    criterion = torch.nn.BCELoss(size_average=True)  # 二分类交叉熵损失函数

     这是式子是上面的展开,p=y   q=(1-y)  而Y只有两种选择0和1,所以当Y等于1的时候,后面那一项就没了。所以当到了多分类其实也一样,Y只有两种选择0和1。当某一类为1时那么其他的类都是0.(这里分类是互斥的,就会有这个特性,你是猫就不会是狗的这种分类)

    交叉熵公式,最终保存下的也只有一项。

     右侧的独热码,就是人判断的标签,也是人给的概率。互斥的这种多分类交叉熵最终就只有只有一项:

    LogSoftmax 

     那LogSoftmax的含义就是对softmax的结果取了一个log

    1. m = nn.LogSoftmax()
    2. input = torch.randn(2, 3)
    3. output = m(input)

     那为啥输出的好好的概率,又加个log干什么呢?

    有种说法是,因为输出的概率是0~1,从log函数看出,如果概率越接近1,那么对应Y的绝对值越小。这种表示确定性越大,信息量越小,反之信息量越大。

    那我觉得还有另外一个原因,就是LogSoftmax一般是和NLLLoss结合使用的。

    NLLLoss

    NLLLoss完成的就是交叉熵的部分:

     

    而且 NLLLoss要求的输入值就是概率取对数的结果,那LogSoftmax和NLLLoss就可以无缝的链接了:

    1. m = nn.LogSoftmax(dim=1)
    2. loss = nn.NLLLoss()
    3. # input is of size N x C = 3 x 5
    4. input = torch.randn(3, 5, requires_grad=True)
    5. # each element in target has to have 0 <= value < C
    6. target = torch.tensor([1, 0, 4])
    7. output = loss(m(input), target)
    8. output.backward()

    CrossEntropyLoss

    那说了这么多,CrossEntropyLoss把几个人的活全部干了:

    1. import torch
    2. y = torch.LongTensor([0])
    3. z = torch.Tensor([[0.2, 0.1, -0.1]])
    4. criterion = torch.nn.CrossEntropyLoss()
    5. loss = criterion(z, y)
    6. print(loss)

     一个数字识别的多分类的例子

     最后在一个详细的例子里看一下,具体的用法

    1. import torch
    2. from torchvision import transforms
    3. from torchvision import datasets
    4. from torch.utils.data import DataLoader
    5. import torch.optim as optim
    6. import torch.nn.functional as F
    7. # 准备数据集
    8. batch_size = 64
    9. transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))])
    10. train_dataset = datasets.MNIST(root='./dataset/mnist/', train=True, download=True, transform=transform)
    11. train_loader = DataLoader(train_dataset, shuffle=True, batch_size=batch_size)
    12. test_dataset = datasets.MNIST(root='./dataset/mnist/', train=False, download=True, transform=transform)
    13. test_loader = DataLoader(test_dataset, shuffle=False, batch_size=batch_size)
    14. # 构造网络模型
    15. class Net(torch.nn.Module):
    16. def __init__(self):
    17. super(Net, self).__init__()
    18. self.l1 = torch.nn.Linear(784, 512)
    19. self.l2 = torch.nn.Linear(512, 256)
    20. self.l3 = torch.nn.Linear(256, 128)
    21. self.l4 = torch.nn.Linear(128, 64)
    22. self.l5 = torch.nn.Linear(64, 10)
    23. def forward(self, x):
    24. # 将C*W*H三维张量变为二维张量,用于深度深度学习处理
    25. x = x.view(-1, 784)
    26. x = F.relu(self.l1(x))
    27. x = F.relu(self.l2(x))
    28. x = F.relu(self.l3(x))
    29. x = F.relu(self.l4(x))
    30. # 最后一层不进行激活,不做非线性变换
    31. return self.l5(x)
    32. model = Net()
    33. # 构造损失函数和优化器
    34. criterion = torch.nn.CrossEntropyLoss() # 此函数,需要一个未激活的输入,它将 交叉熵 和 softmax 的计算进行融合。(这样计算更快更稳定!)
    35. optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.5) # momentum:冲量
    36. def train(epoch):
    37. running_loss = 0
    38. for batch_idx, data in enumerate(train_loader, 0):
    39. # 获得一个批次的输入与标签
    40. inputs, target = data
    41. # 开始训练
    42. optimizer.zero_grad()
    43. # 正向传播
    44. y_pred = model(inputs)
    45. # 计算损失
    46. loss = criterion(y_pred, target)
    47. # 反向传播
    48. loss.backward()
    49. # 更新梯度
    50. optimizer.step()
    51. running_loss = running_loss + loss
    52. if batch_idx % 300 == 299:
    53. print('[%d, %5d] loss: %.3f' % (epoch + 1, batch_idx + 1, running_loss / 300))
    54. running_loss = 0.0
    55. def test():
    56. correct = 0
    57. total = 0
    58. # 不计算梯度
    59. with torch.no_grad():
    60. for data in test_loader:
    61. inputs, labels = data
    62. prec = model(inputs)
    63. '''
    64. torch.max(input, dim) 函数
    65. 输入:
    66. input是softmax函数输出的一个tensor
    67. dim是max函数索引的维度0/1,0是每列的最大值,1是每行的最大值
    68. 输出:
    69. 函数会返回两个tensor,第一个tensor是每行的最大值,softmax的输出中最大的是1,
    70. 所以第一个tensor是全1的tensor;第二个tensor是每行最大值的索引,这个索引的值正好和预测的数字相等。
    71. '''
    72. _, predicted = torch.max(prec.data, dim=1) # predicated为维度(784,1)的张量
    73. total += labels.size(0)
    74. # 张量之间的比较运算
    75. correct += (predicted == labels).sum().item()
    76. print('accuracy on test set: %d %% ' % (100 * correct / total))
    77. if __name__ == "__main__":
    78. for epoch in range(10): # 每轮训练之后,都预测一次
    79. train(epoch)
    80. test()

     输出结果:

    1. [1, 300] loss: 2.166
    2. [1, 600] loss: 0.820
    3. [1, 900] loss: 0.422
    4. accuracy on test set: 89 %
    5. [2, 300] loss: 0.306
    6. [2, 600] loss: 0.269
    7. [2, 900] loss: 0.231
    8. accuracy on test set: 94 %
    9. [3, 300] loss: 0.185
    10. [3, 600] loss: 0.172
    11. [3, 900] loss: 0.152
    12. accuracy on test set: 95 %
    13. [4, 300] loss: 0.129
    14. [4, 600] loss: 0.124
    15. [4, 900] loss: 0.118
    16. accuracy on test set: 96 %
    17. [5, 300] loss: 0.103
    18. [5, 600] loss: 0.094
    19. [5, 900] loss: 0.095
    20. accuracy on test set: 96 %
    21. [6, 300] loss: 0.080
    22. [6, 600] loss: 0.076
    23. [6, 900] loss: 0.077
    24. accuracy on test set: 97 %
    25. [7, 300] loss: 0.062
    26. [7, 600] loss: 0.067
    27. [7, 900] loss: 0.059
    28. accuracy on test set: 97 %
    29. [8, 300] loss: 0.052
    30. [8, 600] loss: 0.050
    31. [8, 900] loss: 0.051
    32. accuracy on test set: 97 %
    33. [9, 300] loss: 0.036
    34. [9, 600] loss: 0.045
    35. [9, 900] loss: 0.042
    36. accuracy on test set: 97 %
    37. [10, 300] loss: 0.031
    38. [10, 600] loss: 0.034
    39. [10, 900] loss: 0.032
    40. accuracy on test set: 97 %

    参考资料:

    《PyTorch深度学习实践》完结合集_哔哩哔哩_bilibili

    softmax交叉熵为什么要取-log_LEILEI18A的博客-CSDN博客_交叉熵的log

  • 相关阅读:
    001.第一个C语言项目
    [ 笔记 ] 计算机网络安全_5_防火墙原理与设计
    力扣大厂热门面试算法题 - 动态规划
    业聚医疗第三次冲刺港交所上市,钱永勋、刘桂祯夫妇为实控人
    2023第六届山东国际青少年眼睛健康产业展会,视力矫正仪展②
    C++——list
    基于Python的世界各个国家的幸福度的公开数据集的数据挖掘
    Redis_03_Redis发布订阅(Pub/Sub)
    MES系统核心价值之如何确保生产过程有序?
    12/4总结报告
  • 原文地址:https://blog.csdn.net/songhuangong123/article/details/125502262