学习总结
(1)本次图像多分类中的最后一层网络不需要加激活,因为在最后的Torch.nn.CrossEntropyLoss已经包括了激活函数softmax。这里注意softmax的dim参数问题,如下面这个是(3,2)的一个变量,dim = 0 实际上是对第一维的3个变量进行对数化,而dim = 1是对第二维进行操作。
a = torch.Tensor([[1,1],[2,2],[3,3]]) a.size() Out[89]: torch.Size([3, 2]) b = torch.nn.Softmax(dim=0)(a) b Out[91]: tensor([[0.0900, 0.0900], [0.2447, 0.2447], [0.6652, 0.6652]]) b = torch.nn.Softmax(dim=1)(a) b Out[93]: tensor([[0.5000, 0.5000], [0.5000, 0.5000], [0.5000, 0.5000]])
(2)本次pytorch在读图像时,将PIL图像(现在都用Pillow了)转为Tensor,神经网络一般希望input比较小,最好在-1到1之间,最好符合正态分布。三种主流图像处理库的比较:
一、多分类问题
注意验证/测试的流程基本与训练过程大体一致,不同点在于:
需要预先设置torch.no_grad,以及将model调至eval模式
不需要将优化器的梯度置零
不需要将loss反向回传到网络
不需要更新optimizer
二、分布和API
法一:把每一个类别的确定看作是一个二分类问题。利用交叉熵。
为了解决抑制问题,就不要输出每个类别的概率,且满足每个概率大于0和概率之和为1的条件。(二分类我们输出的是分布,求出一个然后用1减去即可,多分类虽然也可以这样,但是最后1减去其他所有概率的计算,还需要构建计算图有点麻烦)。
之前二分类中的交叉熵的两项中只能有一项为0.
(1)NLLLoss函数计算如下红色框:
(2)可以直接使用torch.nn.CrossEntropyLoss(将下列红框计算纳入)。注意右侧是由类别生成独热编码向量。
交叉熵,最后一层网络不需要激活,因为在最后的Torch.nn.CrossEntropyLoss已经包括了激活函数softmax。
(1)交叉熵手写版本
import numpy as np y = np.array([1, 0, 0]) z = np.array([0.2, 0.1, -0.1]) y_predict = np.exp(z) / np.exp(z).sum() loss = (- y * np.log(y_predict)).sum() print(loss) # 0.9729189131256584
(2)交叉熵pytorch栗子
交叉熵损失和NLL损失的区别(读文档):
https://pytorch.org/doc s/stable/nn.html#crossentropyloss
https://pytorch.org/docs/stable/nn.html#nllloss
搞懂为啥:CrossEntropyLoss <==> LogSoftmax + NLLLoss
三、交叉熵代码实践
前面是sigmoid,后面是softmax(使得概率大于0,概率值和为1)。 两点注意。
# -*- coding: utf-8 -*- """ Created on Mon Oct 18 22:48:55 2021 @author: 86493 """ # 用CrossEntropyLoss计算交叉熵 import torch # 注意1:设定的第0个分类 y = torch.LongTensor([0]) z = torch.Tensor([[0.2, 0.1, -0.1]]) # 注意2:CrossEntropyLoss criterion = torch.nn.CrossEntropyLoss() loss = criterion(z, y) print(loss.item()) # 0.9729189276695251 # 举栗子:mini-batch:batch_size = 3 import torch criterion = torch.nn.CrossEntropyLoss() # 三个样本 Y = torch.LongTensor([2, 0, 1]) # 第一个样本比较吻合,loss会较小 Y_pred1 = torch.Tensor([[0.1, 0.2, 0.9], # 2 该层为原始的线性层的输出 [1.1, 0.1, 0.2], # 0 [0.2, 2.1, 0.1]])# 1 # 第二个样本差的比较远,loss较大 Y_pred2 = torch.Tensor([[0.8, 0.2, 0.3], # 2 [0.2, 0.3, 0.5], # 0 [0.2, 0.2, 0.5]])# 1 l1 = criterion(Y_pred1, Y) l2 = criterion(Y_pred2, Y) print("Batch Loss1 = ", l1.data, "\nBatch Loss2 =", l2.data) # Batch Loss1 = tensor(0.4966) # Batch Loss2 = tensor(1.2389)
四、代码实践
(1)pytorch在读图像时,将PIL图像(现在都用Pillow了)转为Tensor,神经网络希望input比较小,最好在-1到1之间,最好符合正态分布。
三种主流图像处理库的比较:
(2)transforms.Normalize
,转为标准正态分布即是一种映射到(0, 1)函数。
(3)激活层改用relu,最后一层不用加,因为交叉熵损失函数里面已经包括了。
(4)_, predicted = torch.max(outputs.data, dim = 1)
求出每一行(样本)的最大值的下标,dim = 1即行的维度;返回最大值和最大值所在的下标。
# -*- coding: utf-8 -*- """ Created on Mon Oct 18 19:25:46 2021 @author: 86493 """ import torch from torchvision import transforms from torchvision import datasets from torch.utils.data import DataLoader # 使用relu激活函数 import torch.nn.functional as F import torch.optim as optim import torch.nn as nn import matplotlib.pyplot as plt losslst = [] batch_size = 64 transform = transforms.Compose([ # 将PIL图片转为Tensor transforms.ToTensor(), # 归一化,分别为均值和标准差 transforms.Normalize((0.1307, ), (0.3081, )) ]) # 训练集数据 train_dataset = datasets.MNIST(root = '../dataset/mnist/', train = True, download = True, transform = transform) train_loader = DataLoader(train_dataset, shuffle = True, batch_size = batch_size) # 测试集数据 test_dataset = datasets.MNIST(root = '../dataset/mnist/', train = False, download = True, transform = transform) test_loader = DataLoader(test_dataset, shuffle = False, batch_size = batch_size) # 模型 class Net(nn.Module): def __init__(self): super(Net, self).__init__() self.l1 = nn.Linear(784, 512) self.l2 = nn.Linear(512, 256) self.l3 = nn.Linear(256, 128) self.l4 = nn.Linear(128, 64) self.l5 = nn.Linear(64, 10) def forward(self, x): # -1位置会自动算出N,即变成(N, 784)矩阵 x = x.view(-1, 784) x = F.relu(self.l1(x)) x = F.relu(self.l2(x)) x = F.relu(self.l3(x)) x = F.relu(self.l4(x)) # 注意最后一层不用加上relu,因为交叉熵已经含有softmax return self.l5(x) model = Net() # 交叉熵作为loss criterion = torch.nn.CrossEntropyLoss() # 优化器SGD加上冲量以优化训练过程 optimizer = optim.SGD(model.parameters(), lr = 0.01, momentum = 0.5) def train(epoch): running_loss = 0.0 for batch_idx, data in enumerate(train_loader, 0): # 1.准备数据 inputs, labels = data # data为元组 # 2.向前传递 outputs = model(inputs) loss = criterion(outputs, labels) losslst.append(loss) # print(losslst,"+++++++++++++") # 3.反向传播 optimizer.zero_grad() loss.backward() # 4.更新参数 optimizer.step() # 记得取loss值用item(),否则会构建计算图 running_loss += loss.item() losslst.append(loss.item()) # 每300个batch打印一次 if batch_idx % 300 == 299: print('[%d, %5d] loss: %.3f' % (epoch + 1, batch_idx + 1, running_loss / 300)) running_loss = 0.0 def test(): correct = 0 total = 0 # 加上with这句后面就不会产生计算图 with torch.no_grad(): for data in test_loader: images, labels = data outputs = model(images) # 求出每一行(样本)的最大值的下标,dim = 1即行的维度 # 返回最大值和最大值所在的下标 _, predicted = torch.max(outputs.data, dim = 1) # label矩阵为N × 1 total += labels.size(0) # 猜对的数量,后面算准确率 correct += (predicted == labels).sum().item() print('Accuracy on test set: %d %%' % (100 * correct / total)) # print("losslst的长度为:", len(losslst)) if __name__ == '__main__': for epoch in range(10): # if epoch % 10 == 9: # 每10个输出一次 train(epoch) test()
[1, 300] loss: 2.216 [1, 600] loss: 0.872 [1, 900] loss: 0.423 Accuracy on test set: 89 % [2, 300] loss: 0.333 [2, 600] loss: 0.272 [2, 900] loss: 0.232 Accuracy on test set: 93 % [3, 300] loss: 0.189 [3, 600] loss: 0.176 [3, 900] loss: 0.160 Accuracy on test set: 95 % [4, 300] loss: 0.135 [4, 600] loss: 0.127 [4, 900] loss: 0.121 Accuracy on test set: 95 % [5, 300] loss: 0.104 [5, 600] loss: 0.096 [5, 900] loss: 0.097 Accuracy on test set: 96 % [6, 300] loss: 0.080 [6, 600] loss: 0.071 [6, 900] loss: 0.083 Accuracy on test set: 97 % [7, 300] loss: 0.062 [7, 600] loss: 0.062 [7, 900] loss: 0.062 Accuracy on test set: 97 % [8, 300] loss: 0.050 [8, 600] loss: 0.052 [8, 900] loss: 0.053 Accuracy on test set: 97 % [9, 300] loss: 0.034 [9, 600] loss: 0.045 [9, 900] loss: 0.043 Accuracy on test set: 97 % [10, 300] loss: 0.032 [10, 600] loss: 0.032 [10, 900] loss: 0.036 Accuracy on test set: 97 %