PyTorch基础教程12图像多分类问题(学不会来打我啊)
Posted 山顶夕景
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PyTorch基础教程12图像多分类问题(学不会来打我啊)相关的知识,希望对你有一定的参考价值。
学习总结
(1)本次图像多分类中的最后一层网络不需要加激活,因为在最后的Torch.nn.CrossEntropyLoss
已经包括了激活函数softmax。
(2)本次pytorch在读图像时,将PIL图像(现在都用Pillow了)转为Tensor,神经网络一般希望input比较小,最好在-1到1之间,最好符合正态分布。三种主流图像处理库的比较:
库 | 函数/方法 | 返回值 | 图像像素格式 | 像素值范围 | 图像矩阵表示 |
---|---|---|---|---|---|
skimage | io.imread(xxx) | numpy.ndarray | RGB | [0, 255] | (H X W X C) |
cv2 | cv2.imread(xxx) | numpy.ndarray | BGR | [0, 255] | (H X W X C) |
Pillow(PIL) | Image.open(xxx) | PIL.Image.Image对象 | 根据图像格式,一般为RGB | [0, 255] | — |
一、多分类问题
注意验证/测试的流程基本与训练过程大体一致,不同点在于:
- 需要预先设置
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之间,最好符合正态分布。
三种主流图像处理库的比较:
库 | 函数/方法 | 返回值 | 图像像素格式 | 像素值范围 | 图像矩阵表示 |
---|---|---|---|---|---|
skimage | io.imread(xxx) | numpy.ndarray | RGB | [0, 255] | (H X W X C) |
cv2 | cv2.imread(xxx) | numpy.ndarray | BGR | [0, 255] | (H X W X C) |
Pillow(PIL) | Image.open(xxx) | PIL.Image.Image对象 | 根据图像格式,一般为RGB | [0, 255] | — |
(2)transforms.Normalize
,转为标准正态分布即是一种映射到(0, 1)函数。
Pixel
norm
=
Pixel
origin
−
mean
std
\\text { Pixel }_{\\text {norm }}=\\frac{\\text { Pixel }_{\\text {origin }}-\\text { mean }}{\\text { std }}
Pixel norm = std Pixel origin − mean
(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()
代码打印的loss值:
[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 %
Reference
(1)pytorch中with torch.no_grad()
(2)https://www.bilibili.com/video/BV1Y7411d7Ys?p=9
(3)pytorch学习(五)—图像的加载/读取方式
(4)解决Python报错:RuntimeError: Can’t call numpy() on Variable that requires grad. Use var.detach().numpy()
以上是关于PyTorch基础教程12图像多分类问题(学不会来打我啊)的主要内容,如果未能解决你的问题,请参考以下文章
从零开始学PyTorch:一文学会线性回归逻辑回归及图像分类