【PyTorch基础教程12】图像多分类问题(学不会来打我啊)

举报
野猪佩奇996 发表于 2022/01/22 23:30:31 2022/01/22
【摘要】 学习总结 (1)本次图像多分类中的最后一层网络不需要加激活,因为在最后的Torch.nn.CrossEntropyLoss已经包括了激活函数softmax。这里注意softmax的dim参数问题,如下面...

学习总结

(1)本次图像多分类中的最后一层网络不需要加激活,因为在最后的Torch.nn.CrossEntropyLoss已经包括了激活函数softmax。这里注意softmaxdim参数问题,如下面这个是(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]])

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

(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

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

(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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

四、代码实践

(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()

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129

代码打印的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 %

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

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()

文章来源: andyguo.blog.csdn.net,作者:山顶夕景,版权归原作者所有,如需转载,请联系作者。

原文链接:andyguo.blog.csdn.net/article/details/120834714

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。