天天看點

用Pytorch搭建LeNet-5模型且基于CIFAR-10資料集訓練

本文中的執行個體是Pytorch官方的一個demo,這裡我結合最近正在看的書《Python深度學習 基于Pytorch》P125 Pytorch實作CIFAR-10多分類這節,進行了梳理與個人的總結,以加深自己的了解。

目的:

①了解LeNet-5模型

②加深Pytorch的學習

③從頭至尾介紹搭建網絡并進行訓練預測的全過程

目錄

    • 簡單介紹
      • CIFAR-10資料集
      • LeNet-5模型
      • 查找Pytorch中API的具體内容
    • 執行個體
      • 1.建構神經網絡
      • 2.下載下傳資料集并檢視部分資料
      • 3.訓練模型
      • 4.在測試集中随機預測四張圖看看效果
      • 5.測試模型在測試集上的準确率及10個類别的準确率
      • 6.存儲訓練好的權重檔案進行預測
    • 整體源代碼

簡單介紹

CIFAR-10資料集

CIFAR-10資料集由10個類的60000個32x32彩色圖像組成,以下是十個類别,每個類有6000個圖像。有50000個訓練圖像和10000個測試圖像。

classes = (‘plane’, ‘car’, ‘bird’, ‘cat’,‘deer’, ‘dog’, ‘frog’, ‘horse’, ‘ship’, ‘truck’)

用Pytorch搭建LeNet-5模型且基于CIFAR-10資料集訓練

資料集分為五個訓練批次和一個測試批次,每個批次有10000個圖像。測試批次包含來自每個類的恰好1000個随機選擇的圖像。

訓練批次包含随機順序的圖像,但一些訓練批次可能包含來自一個種類的圖像比另一個類更多。

總的訓練批次包含來自每個類的正好5000張圖像。

因為是彩色圖像,3個通道,是以下面模型的輸入為3×32×32

LeNet-5模型

用Pytorch搭建LeNet-5模型且基于CIFAR-10資料集訓練

1.模型架構

LeNet-5模型結構為輸入層–卷積層–池化層–卷積層–池化層–全連接配接層–全連接配接層–輸出,為串聯模式,如上圖所示

2.模型特點

a.每個卷積層包含3個部分:卷積、池化和非線性激活函數。

b.使用卷積提前空間特征。

c.采用降采樣(Subsample)的平均池化層(Average Pooling)。

d.使用雙曲正切(Tanh)的激活函數。

e.最後用MLP作為分類器。

3.模型的網絡介紹

輸入:

灰階圖像,通道為1,尺寸為1×32×32

第一層:卷積層

LeNet-5模型接受的輸入層大小是1×32x32。卷積層的過濾器的尺寸是5x5,深度(卷積核個數)為6,不使用全0填充,步長為1。則這一層的輸出的尺寸為32-5+1=28,深度為6。本層的輸出矩陣大小為6×28×28。

第二層:池化層

這一層的輸入是第一層的輸出,是一個6×28x28=4704的節點矩陣。本層采用的過濾器為2x2的大小,長和寬的步長均為2,是以本層的輸出矩陣大小為6×14x14。

第三層:卷積層

本層的輸入矩陣大小為6×14x14,使用的過濾器大小為5x5,深度為16。本層不使用全0填充,步長為1。本層的輸出矩陣大小為16×10x10。

第四層:池化層

本層的輸入矩陣大小是16×10x10,采用的過濾器大小是2x2,步長為2,本層的輸出矩陣大小為16×5x5。

第五層:全連接配接層

本層的輸入矩陣大小為16×5x5。将此矩陣中的節點拉成一個向量,那麼這就和全連接配接層的輸入一樣了,本層的輸出節點個數為120。

第六層:全連接配接層

本層的輸入節點個數為120個,輸出節點個數為84個。

第七層:全連接配接層

本層的輸入節點為84個,輸出節點個數為10個。

下面的神經網絡模型是以LeNet-5為基準,其中就卷積核的個數不同,我看了書上和網上的模型,這裡的參數都不同,猜測卷積核的個數可以自己定,但要保證各層之間次元的統一。

查找Pytorch中API的具體内容

方法一:在Pycharm中将滑鼠移至函數名上,按住ctrl+滑鼠左鍵

方法二:進入Pytorch官網——Docs——Search Docs中輸入需要查找的API名稱

用Pytorch搭建LeNet-5模型且基于CIFAR-10資料集訓練

執行個體

Pytorch中Tensor的通道排序:[batch,channel,height,width]

1.建構神經網絡

定義一個LeNet()類,繼承于nn.Module的父類,這個類中包括兩個函數:初始化函數和前向傳播函數。初始化函數中:模型使用到的網絡層結構;前向傳播函數:實作整個前向傳播的過程。

class LeNet(nn.Module):
    def __init__(self):
        super(LeNet,self).__init__()
        # 卷積層1:輸入圖像深度=3,輸出圖像深度=16,卷積核大小=5*5,卷積步長=1;16表示輸出次元,也表示卷積核個數
        self.conv1 = nn.Conv2d(in_channels=3,out_channels=16,kernel_size=5,stride=1)
        # 池化層1:采用最大池化,區域集大小=2*2.池化步長=2
        self.pool1 = nn.MaxPool2d(kernel_size=2,stride=2)
        # 卷積層2
        self.conv2 = nn.Conv2d(in_channels=16,out_channels=32,kernel_size=5,stride=1)
        # 池化層2
        self.pool2 = nn.MaxPool2d(kernel_size=2, stride=2)
        # 全連接配接層1:輸入大小=32*5*5,輸出大小=120
        self.fc1 = nn.Linear(32*5*5,120)
        # 全連接配接層2
        self.fc2 = nn.Linear(120,84)
        # 全連接配接層3
        self.fc3 = nn.Linear(84,10)

    def forward(self,x):
        x = F.relu(self.conv1(x))  # input(3, 32, 32) output(16, 28, 28)
        x = self.pool1(x)  # output(16, 14, 14)
        x = F.relu(self.conv2(x))  # output(32, 10, 10)
        x = self.pool2(x)  # output(32, 5, 5)
        x = x.view(-1, 32 * 5 * 5)  # output(32*5*5)
        x = F.relu(self.fc1(x))  # output(120)
        x = F.relu(self.fc2(x))  # output(84)
        x = self.fc3(x)  # output(10)
        return x
           

2.下載下傳資料集并檢視部分資料

import torch
import torchvision
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
import numpy as np
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim # 優化器

'''下載下傳資料集'''
# transforms.Compose()函數将兩個函數拼接起來。
# (ToTensor():把一個PIL.Image轉換成Tensor,Normalize():标準化,即減均值,除以标準差)
transform = transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
# 訓練集:下載下傳CIFAR10資料集,如果沒有事先下載下傳該資料集,則将download參數改為True
trainset = torchvision.datasets.CIFAR10(root='./data', train=True,download=False, transform=transform)
# 用DataLoader得到生成器,其中shuffle:是否将資料打亂;
# num_workers表示使用多程序加載的程序數,0代表不使用多程序
trainloader = torch.utils.data.DataLoader(trainset, batch_size=4,shuffle=True, num_workers=0)

# 測試集資料下載下傳
testset = torchvision.datasets.CIFAR10(root='./data', train=False,download=False, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=4,shuffle=False, num_workers=0)

classes = ('plane', 'car', 'bird', 'cat','deer', 'dog', 'frog', 'horse', 'ship', 'truck')


# 顯示圖像
def imshow(img):
    # 因為标準化normalize是:output = (input-0.5)/0.5
    # 則反标準化unnormalize是:input = output*0.5 + 0.5
    img = img / 2 + 0.5     # unnormalize
    npimg = img.numpy()
    # transpose()會更改多元數組的軸的順序
    # Pytorch中是[channel,height,width],這裡改為圖像的[height,width,channel]
    plt.imshow(np.transpose(npimg, (1, 2, 0)))
    plt.show()

# 随機擷取部分訓練資料
dataiter = iter(trainloader)
images, labels = dataiter.next()
# 顯示圖像
# torchvision.utils.make_grid()将多張圖檔拼接在一張圖中
imshow(torchvision.utils.make_grid(images))
# 列印标簽
# str.join(sequence):用于将序列中的元素以指定的字元str連接配接成一個新的字元串。這裡的str是' ',空格
# %5s:表示輸出字元串至少5個字元,不夠5個的話,左側用空格補
print(' '.join('%5s' % classes[labels[j]] for j in range(4)))
           

輸出:

用Pytorch搭建LeNet-5模型且基于CIFAR-10資料集訓練
用Pytorch搭建LeNet-5模型且基于CIFAR-10資料集訓練

3.訓練模型

'''訓練模型'''
# 有GPU就用GPU跑,沒有就用CPU
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

net = LeNet()
net=net.to(device)
# print("該網絡共有 {} 個參數".format(sum(x.numel() for x in net.parameters())))
# # 該網絡共有 121182 個參數

# 對于多分類問題,應該使用Softmax函數,這裡CIFAR10資料集屬于多分類,卻使用了交叉熵損失函數,
# 是因為進入CrossEntropyLoss()函數内部就會發現其中包含了Softmax函數
loss_function = nn.CrossEntropyLoss() # 使用交叉熵損失函數
# 優化器選擇Adam,學習率設為0.001
optimizer = optim.Adam(net.parameters(), lr=0.001)
# 列印檢視神經網絡的結構
# print(net)
# # LeNet(
# #   (conv1): Conv2d(3, 16, kernel_size=(5, 5), stride=(1, 1))
# #   (pool1): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
# #   (conv2): Conv2d(16, 32, kernel_size=(5, 5), stride=(1, 1))
# #   (pool2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
# #   (fc1): Linear(in_features=800, out_features=120, bias=True)
# #   (fc2): Linear(in_features=120, out_features=84, bias=True)
# #   (fc3): Linear(in_features=84, out_features=10, bias=True)
# # )

for epoch in range(10): # 整個疊代10輪
    running_loss = 0.0 # 初始化損失函數值loss=0
    for i, data in enumerate(trainloader, start=0):
        # 擷取訓練資料
        inputs, labels = data
        inputs, labels = inputs.to(device), labels.to(device) # 将資料及标簽傳入GPU/CPU

        # 權重參數梯度清零
        optimizer.zero_grad()

        # 正向及反向傳播
        outputs = net(inputs)
        loss = loss_function(outputs, labels)
        loss.backward()
        optimizer.step()

        # 顯示損失值
        running_loss += loss.item()
        if i % 2000 == 1999:    # print every 2000 mini-batches
            print('[%d, %5d] loss: %.3f' %(epoch + 1, i + 1, running_loss / 2000))
            running_loss = 0.0

print('Finished Training')
           

輸出:

[1, 2000] loss: 1.884

[1, 4000] loss: 1.602

[1, 6000] loss: 1.506

[1, 8000] loss: 1.445

[1, 10000] loss: 1.405

[1, 12000] loss: 1.377

[2, 2000] loss: 1.274

[2, 4000] loss: 1.248

[2, 6000] loss: 1.239

[2, 8000] loss: 1.226

[2, 10000] loss: 1.213

[2, 12000] loss: 1.185

[3, 2000] loss: 1.106

[3, 4000] loss: 1.124

[3, 6000] loss: 1.137

[3, 8000] loss: 1.109

[3, 10000] loss: 1.096

[3, 12000] loss: 1.089

[4, 2000] loss: 1.013

[4, 4000] loss: 1.015

[4, 6000] loss: 1.028

[4, 8000] loss: 1.037

[4, 10000] loss: 1.056

[4, 12000] loss: 1.009

[5, 2000] loss: 0.935

[5, 4000] loss: 0.964

[5, 6000] loss: 0.980

[5, 8000] loss: 0.962

[5, 10000] loss: 0.975

[5, 12000] loss: 0.993

[6, 2000] loss: 0.901

[6, 4000] loss: 0.908

[6, 6000] loss: 0.920

[6, 8000] loss: 0.924

[6, 10000] loss: 0.905

[6, 12000] loss: 0.944

[7, 2000] loss: 0.855

[7, 4000] loss: 0.864

[7, 6000] loss: 0.904

[7, 8000] loss: 0.896

[7, 10000] loss: 0.898

[7, 12000] loss: 0.890

[8, 2000] loss: 0.819

[8, 4000] loss: 0.828

[8, 6000] loss: 0.844

[8, 8000] loss: 0.880

[8, 10000] loss: 0.854

[8, 12000] loss: 0.863

[9, 2000] loss: 0.802

[9, 4000] loss: 0.795

[9, 6000] loss: 0.833

[9, 8000] loss: 0.848

[9, 10000] loss: 0.824

[9, 12000] loss: 0.834

[10, 2000] loss: 0.775

[10, 4000] loss: 0.758

[10, 6000] loss: 0.781

[10, 8000] loss: 0.806

[10, 10000] loss: 0.806

[10, 12000] loss: 0.829

Finished Training

為什麼每計算一個batch,就需要調用一次optimizer.zero_grad() 将權重參數梯度清零?

是為了進行梯度累加。一定條件下,batchsize越大訓練效果越好,梯度累加則實作了batchsize的變相擴大,如果accumulation_steps為8,則batchsize ‘變相’ 擴大了8倍。

通過這個特性能夠變相實作一個很大batch數值的訓練。

4.在測試集中随機預測四張圖看看效果

'''預測四張圖看看'''
dataiter = iter(testloader)
images, labels = dataiter.next()

imshow(torchvision.utils.make_grid(images))
print('GroundTruth: ', ' '.join('%5s' % classes[labels[j]] for j in range(4)))

images, labels = images.to(device), labels.to(device)
outputs = net(images)
_, predicted = torch.max(outputs, 1)

print('Predicted: ', ' '.join('%5s' % classes[predicted[j]]for j in range(4)))
           

輸出:

用Pytorch搭建LeNet-5模型且基于CIFAR-10資料集訓練
用Pytorch搭建LeNet-5模型且基于CIFAR-10資料集訓練

可以發現四張圖隻錯了一張

5.測試模型在測試集上的準确率及10個類别的準确率

'''測試模型'''
correct = 0
total = 0
# with是一個上下文管理器
# with torch.no_grad()表示其包括的内容不需要計算梯度,也不會進行反向傳播,節省記憶體
with torch.no_grad():
    for data in testloader:
        images, labels = data
        images, labels = images.to(device), labels.to(device)
        outputs = net(images)
        # torch.max(outputs.data, 1)傳回outputs每一行中最大值的那個元素,且傳回其索引
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()

print('Accuracy of the network on the 10000 test images: %d %%' % (100 * correct / total))

# 列印10個分類的準确率
class_correct = list(0. for i in range(10)) #class_correct=[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
class_total = list(0. for i in range(10)) #class_total=[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
with torch.no_grad():
    for data in testloader:
        images, labels = data
        images, labels = images.to(device), labels.to(device)
        outputs = net(images) # outputs的次元是:4*10
        # torch.max(outputs.data, 1)傳回outputs每一行中最大值的那個元素,且傳回其索引
        # 此時predicted的次元是:4*1
        _, predicted = torch.max(outputs, 1)
        # 此時c的次元:4将預測值與實際标簽進行比較,且進行降維
        c = (predicted == labels).squeeze()
        for i in range(4):
            label = labels[i]
            class_correct[label] += c[i].item()
            class_total[label] += 1
for i in range(10):
    print('Accuracy of %5s : %2d %%' % (classes[i], 100 * class_correct[i] / class_total[i]))
           

輸出:

用Pytorch搭建LeNet-5模型且基于CIFAR-10資料集訓練

6.存儲訓練好的權重檔案進行預測

torch.nn.Module子產品中的state_dict變量存放訓練過程中需要學習的權重和偏執系數,state_dict作為python的字典對象将每一層的參數映射成tensor張量

參考部落格:state_dict詳解

Python中.pth檔案的作用

save_path = './Lenet.pth'
torch.save(net.state_dict(), save_path)
           
用Pytorch搭建LeNet-5模型且基于CIFAR-10資料集訓練

上面是已經訓練好了的權重檔案,下面随便用一張自己找的圖檔預測下

用Pytorch搭建LeNet-5模型且基于CIFAR-10資料集訓練

重建立一個py檔案,讀取訓練好了的權重并對目标圖像進行預測:

import torch
import torchvision.transforms as transforms
from PIL import Image
from model import LeNet

transform = transforms.Compose([transforms.Resize((32, 32)),transforms.ToTensor(),transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

classes = ('plane', 'car', 'bird', 'cat','deer', 'dog', 'frog', 'horse', 'ship', 'truck')

net = LeNet()
net.load_state_dict(torch.load('Lenet.pth'))

im = Image.open('1.jpg')
im = transform(im)  # [C, H, W]
# 輸入pytorch網絡中要求的格式是[batch,channel,height,width],是以這裡增加一個次元
im = torch.unsqueeze(im, dim=0)  # [N, C, H, W]

with torch.no_grad():
    outputs = net(im)
    predict = torch.max(outputs, dim=1)[1].data.numpy() # 索引即classed中的類别
print(classes[int(predict)])

# 直接列印張量的預測結果
with torch.no_grad():
    outputs = net(im)
    predict = torch.softmax(outputs,dim=1) # [batch,channel,height,width],這裡因為對batch不需要處理
print(predict)
           

輸出:

用Pytorch搭建LeNet-5模型且基于CIFAR-10資料集訓練

整體源代碼

main.py

import torch
import torchvision
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
import numpy as np
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim # 優化器


'''1.建構神經網絡'''
class LeNet(nn.Module):
    def __init__(self):
        super(LeNet,self).__init__()
        # 卷積層1:輸入圖像深度=3,輸出圖像深度=16,卷積核大小=5*5,卷積步長=1;16表示輸出次元,也表示卷積核個數
        self.conv1 = nn.Conv2d(in_channels=3,out_channels=16,kernel_size=5,stride=1)
        # 池化層1:采用最大池化,區域集大小=2*2.池化步長=2
        self.pool1 = nn.MaxPool2d(kernel_size=2,stride=2)
        # 卷積層2
        self.conv2 = nn.Conv2d(in_channels=16,out_channels=32,kernel_size=5,stride=1)
        # 池化層2
        self.pool2 = nn.MaxPool2d(kernel_size=2, stride=2)
        # 全連接配接層1:輸入大小=32*5*5,輸出大小=120
        self.fc1 = nn.Linear(32*5*5,120)
        # 全連接配接層2
        self.fc2 = nn.Linear(120,84)
        # 全連接配接層3
        self.fc3 = nn.Linear(84,10)

    def forward(self,x):
        x = F.relu(self.conv1(x))  # input(3, 32, 32) output(16, 28, 28)
        x = self.pool1(x)  # output(16, 14, 14)
        x = F.relu(self.conv2(x))  # output(32, 10, 10)
        x = self.pool2(x)  # output(32, 5, 5)
        x = x.view(-1, 32 * 5 * 5)  # output(32*5*5)
        x = F.relu(self.fc1(x))  # output(120)
        x = F.relu(self.fc2(x))  # output(84)
        x = self.fc3(x)  # output(10)
        return x


'''2.下載下傳資料集并檢視部分資料'''
# transforms.Compose()函數将兩個函數拼接起來。
# (ToTensor():把一個PIL.Image轉換成Tensor,Normalize():标準化,即減均值,除以标準差)
transform = transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
# 訓練集:下載下傳CIFAR10資料集,如果沒有事先下載下傳該資料集,則将download參數改為True
trainset = torchvision.datasets.CIFAR10(root='./data', train=True,download=False, transform=transform)
# 用DataLoader得到生成器,其中shuffle:是否将資料打亂;
# num_workers表示使用多程序加載的程序數,0代表不使用多程序
trainloader = torch.utils.data.DataLoader(trainset, batch_size=4,shuffle=True, num_workers=0)

# 測試集資料下載下傳
testset = torchvision.datasets.CIFAR10(root='./data', train=False,download=False, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=4,shuffle=False, num_workers=0)

classes = ('plane', 'car', 'bird', 'cat','deer', 'dog', 'frog', 'horse', 'ship', 'truck')


# 顯示圖像
def imshow(img):
    # 因為标準化normalize是:output = (input-0.5)/0.5
    # 則反标準化unnormalize是:input = output*0.5 + 0.5
    img = img / 2 + 0.5     # unnormalize
    npimg = img.numpy()
    # transpose()會更改多元數組的軸的順序
    # Pytorch中是[channel,height,width],這裡改為圖像的[height,width,channel]
    plt.imshow(np.transpose(npimg, (1, 2, 0)))
    plt.show()

# 随機擷取部分訓練資料
dataiter = iter(trainloader)
images, labels = dataiter.next()
# 顯示圖像
# torchvision.utils.make_grid()将多張圖檔拼接在一張圖中
imshow(torchvision.utils.make_grid(images))
# 列印标簽
# str.join(sequence):用于将序列中的元素以指定的字元str連接配接成一個新的字元串。這裡的str是' ',空格
# %5s:表示輸出字元串至少5個字元,不夠5個的話,左側用空格補
print(' '.join('%5s' % classes[labels[j]] for j in range(4)))


'''3.訓練模型'''
# 有GPU就用GPU跑,沒有就用CPU
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

net = LeNet()
net=net.to(device)
# print("該網絡共有 {} 個參數".format(sum(x.numel() for x in net.parameters())))
# # 該網絡共有 121182 個參數

# 對于多分類問題,應該使用Softmax函數,這裡CIFAR10資料集屬于多分類,卻使用了交叉熵損失函數,
# 是因為進入CrossEntropyLoss()函數内部就會發現其中包含了Softmax函數
loss_function = nn.CrossEntropyLoss() # 使用交叉熵損失函數
# 優化器選擇Adam,學習率設為0.001
optimizer = optim.Adam(net.parameters(), lr=0.001)
# 列印檢視神經網絡的結構
# print(net)
# # LeNet(
# #   (conv1): Conv2d(3, 16, kernel_size=(5, 5), stride=(1, 1))
# #   (pool1): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
# #   (conv2): Conv2d(16, 32, kernel_size=(5, 5), stride=(1, 1))
# #   (pool2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
# #   (fc1): Linear(in_features=800, out_features=120, bias=True)
# #   (fc2): Linear(in_features=120, out_features=84, bias=True)
# #   (fc3): Linear(in_features=84, out_features=10, bias=True)
# # )

for epoch in range(10): # 整個疊代10輪
    running_loss = 0.0 # 初始化損失函數值loss=0
    for i, data in enumerate(trainloader, start=0):
        # 擷取訓練資料
        inputs, labels = data
        inputs, labels = inputs.to(device), labels.to(device) # 将資料及标簽傳入GPU/CPU

        # 權重參數梯度清零
        optimizer.zero_grad()

        # 正向及反向傳播
        outputs = net(inputs)
        loss = loss_function(outputs, labels)
        loss.backward()
        optimizer.step()

        # 顯示損失值
        running_loss += loss.item()
        if i % 2000 == 1999:    # print every 2000 mini-batches
            print('[%d, %5d] loss: %.3f' %(epoch + 1, i + 1, running_loss / 2000))
            running_loss = 0.0

print('Finished Training')


'''4.在測試集中随機預測四張圖看看效果'''
dataiter = iter(testloader)
images, labels = dataiter.next()

imshow(torchvision.utils.make_grid(images))
print('GroundTruth: ', ' '.join('%5s' % classes[labels[j]] for j in range(4)))

images, labels = images.to(device), labels.to(device)
outputs = net(images)
_, predicted = torch.max(outputs, 1)

print('Predicted: ', ' '.join('%5s' % classes[predicted[j]]for j in range(4)))


'''5.測試模型在測試集上的準确率及10個類别的準确率'''
correct = 0
total = 0
# with是一個上下文管理器
# with torch.no_grad()表示其包括的内容不需要計算梯度,也不會進行反向傳播,節省記憶體
with torch.no_grad():
    for data in testloader:
        images, labels = data
        images, labels = images.to(device), labels.to(device)
        outputs = net(images)
        # torch.max(outputs.data, 1)傳回outputs每一行中最大值的那個元素,且傳回其索引
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()

print('Accuracy of the network on the 10000 test images: %d %%' % (100 * correct / total))

# 列印10個分類的準确率
class_correct = list(0. for i in range(10)) #class_correct=[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
class_total = list(0. for i in range(10)) #class_total=[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
with torch.no_grad():
    for data in testloader:
        images, labels = data
        images, labels = images.to(device), labels.to(device)
        outputs = net(images) # outputs的次元是:4*10
        # torch.max(outputs.data, 1)傳回outputs每一行中最大值的那個元素,且傳回其索引
        # 此時predicted的次元是:4*1
        _, predicted = torch.max(outputs, 1)
        # 此時c的次元:4将預測值與實際标簽進行比較,且進行降維
        c = (predicted == labels).squeeze()
        for i in range(4):
            label = labels[i]
            class_correct[label] += c[i].item()
            class_total[label] += 1
for i in range(10):
    print('Accuracy of %5s : %2d %%' % (classes[i], 100 * class_correct[i] / class_total[i]))


'''6.存儲訓練好的權重檔案'''
save_path = './Lenet.pth'
torch.save(net.state_dict(), save_path)
           

predict.py

import torch
import torchvision.transforms as transforms
from PIL import Image
from model import LeNet

transform = transforms.Compose([transforms.Resize((32, 32)),transforms.ToTensor(),transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

classes = ('plane', 'car', 'bird', 'cat','deer', 'dog', 'frog', 'horse', 'ship', 'truck')

net = LeNet()
net.load_state_dict(torch.load('Lenet.pth'))

im = Image.open('1.jpg')
im = transform(im)  # [C, H, W]
# 輸入pytorch網絡中要求的格式是[batch,channel,height,width],是以這裡增加一個次元
im = torch.unsqueeze(im, dim=0)  # [N, C, H, W]

with torch.no_grad():
    outputs = net(im)
    predict = torch.max(outputs, dim=1)[1].data.numpy() # 索引即classed中的類别
print(classes[int(predict)])

# 直接列印張量的預測結果
with torch.no_grad():
    outputs = net(im)
    predict = torch.softmax(outputs,dim=1) # [batch,channel,height,width],這裡因為對batch不需要處理
print(predict)
           

繼續閱讀