Pytorch教程——卷積神經(jīng)網(wǎng)絡識別Fashion-MNIST

? ? ? ?針對使用卷積神經(jīng)網(wǎng)絡進行圖像分類的問題,下面會使用Pytorch搭建一個類似LeNet-5的網(wǎng)絡結構,用于Fashion-MNIST數(shù)據(jù)集的圖像分類。針對該問題的分析可以分為數(shù)據(jù)準備、模型建立以及使用訓練集進行訓練與使用測試集測試模型的效果。針對卷積網(wǎng)絡的建立,將會分別建立常用的卷積神經(jīng)網(wǎng)絡與基于空洞卷積的卷積神經(jīng)網(wǎng)絡。首先導入所需要的庫及相關模塊。
import pandas as pd
from sklearn.metrics import accuracy_score,confusion_matrix,classification_report
import matplotlib.pyplot as plt
import seaborn as sns
import copy
import numpy as np
import time
import torch
import torch.nn as nn
from torch.optim import Adam
import torch.utils.data as Data?
from torchvision import transforms?
from torchvision.datasets import FashionMNIST
1、圖像數(shù)據(jù)準備
? ? ? ? 首先準備FashionMNIST數(shù)據(jù)集,該數(shù)據(jù)集可以直接使用torchvision庫中datasets模塊的FashionMNIST()的API函數(shù)讀取,如果指定的工作文件夾中沒有當前的數(shù)據(jù),可以從網(wǎng)絡上自動下載該數(shù)據(jù)集,數(shù)據(jù)的準備程序如下所示:
# 使用 FashionMNIST 數(shù)據(jù),準備訓練數(shù)據(jù)集
train_data = FashionMNIST(
? ? root = '...data/FashionMNIST',? ?#數(shù)據(jù)集路徑
? ? train = True,? ?#只使用訓練數(shù)據(jù)集
? ? transform = transforms.ToTensor(),
? ? download = False
)
# 定義一個數(shù)據(jù)加載器
train_loader = Data.DataLoader(
? ? dataset = train_data,? ?# 使用的數(shù)據(jù)集
? ? batch_size = 64,? ? # 批量處理樣本大小
? ? shuffle = False,? ?# 每次迭代前不打亂數(shù)據(jù)
? ? num_workers = 2,? ? # 使用兩個進程
)
# 計算train_loader有多少個batch
print("train_loader的batch數(shù)量為:",len(train_loader))
Out[1]:train_loader的batch數(shù)量為: 938
? ? ? ?上面的程序導入了訓練數(shù)據(jù)集,然后使用Data.DataLoader()函數(shù)將其定義為數(shù)據(jù)加載器,每個batch中會包含64個樣本,通過len()函數(shù)可以計算數(shù)據(jù)加載器中包含的batch數(shù)量,輸出顯示train_loader中包含938個batch。需要注意的是參數(shù)shuffle = False,表示加載器中每個batch使用的樣本都是固定的,這樣有利于在訓練模型時根據(jù)迭代的次數(shù)將其切分為訓練集和驗證集。
? ? ? ?為了觀察數(shù)據(jù)集中每個圖像的內容,可以獲取一個batch的圖像,然后將其可視化,以觀察數(shù)據(jù)。獲取數(shù)據(jù)并可視化的程序如下所示:
# 獲得一個batch的數(shù)據(jù)
for step, (b_x, b_y) in enumerate(train_loader):
? ? if step > 0:
? ? ? ? break
# 可視化一個 batch 的圖像
batch_x = b_x.squeeze().numpy()
batch_y = b_y.numpy()
class_label = train_data.classes
class_label[0] = 'T-shirt'
plt.figure(figsize = (12, 5))
for ii in np.arange(len(batch_y)):
? ? plt.subplot(4, 16, ii + 1)
? ? plt.imshow(batch_x[ii, :, :], cmap = plt.cm.gray)
? ? plt.title(class_label[batch_y[ii]], size = 9)
? ? plt.axis('off')
? ? plt.subplots_adjust(wspace = 0.05)
? ? ? ?在上面的程序中,使用for循環(huán)獲取一個batch的數(shù)據(jù)b_x和b_y,并使用XX.numpy()方法將張量數(shù)據(jù)XX轉化為Numpy數(shù)組的形式,通過train_data.classes可以獲取10類數(shù)據(jù)的標簽,利用for循環(huán)和plt.subplot()、plt.imshow()等繪圖函數(shù),可以將64張圖像進行可視化,得到的可視化圖像如圖1所示:

? ? ? ?在對訓練集進行處理后,下面對測試數(shù)據(jù)集進行處理。導入測試數(shù)據(jù)集后,將所有的樣本處理為一個整體,看作一個batch用于測試,可使用如下程序:
# 對測試集進行處理
test_data = FashionMNIST(
? ? root = '...data/FashionMNIST',? #數(shù)據(jù)的路徑
? ? train = False, #不使用訓練數(shù)據(jù)集
? ? download = True, #數(shù)據(jù)未下載,所以這里要下載
)
# 為數(shù)據(jù)添加一個通道維度,并且取值范圍歸一化,即數(shù)據(jù)范圍縮放到0 ~ 1之間
test_data_x = test_data.data.type(torch.FloatTensor) / 255.0
test_data_x = torch.unsqueeze(test_data_x, dim = 1)
test_data_y = test_data.targets? # 測試集標簽
print("test_data_x.shape:",test_data_x.shape)
print("test_data_y.shape:",test_data_y.shape)
Out[2]:test_data_x.shape: torch.Size([10000, 1, 28, 28])?
? ? ? ? ? ? ?test_data_y.shape: torch.Size([10000])
? ? ? ?上面的程序同樣使用FashionMNIST()函數(shù)導入測試數(shù)據(jù)集,并且將其處理為一個整體,從輸出結果可發(fā)現(xiàn)測試集有10000張28 X?28的圖像。
2、卷積神經(jīng)網(wǎng)絡的搭建
? ? ? ?在數(shù)據(jù)準備完畢后,可以搭建一個卷積神經(jīng)網(wǎng)絡,并且使用訓練數(shù)據(jù)對網(wǎng)絡進行訓練,使用測試集驗證所搭建的網(wǎng)絡的識別精度。針對搭建的卷積神經(jīng)網(wǎng)絡,可以使用如圖2所示的網(wǎng)絡結構。

? ? ? ? 圖2搭建的卷積神經(jīng)網(wǎng)絡有2個卷積層,分別包含16個和32個3 X 3卷積核,并且卷積后使用ReLU激活函數(shù)進行激活,兩個池化層均為平均值池化,而兩個全連接層分別有256和128個神經(jīng)元,最后的分類器則包含了10個神經(jīng)元。
? ? ? ? 針對上述網(wǎng)絡結構,可以使用下面的程序代碼對卷積神經(jīng)網(wǎng)絡進行定義。
#卷積神經(jīng)網(wǎng)絡的搭建
class MyConvNet(nn.Module):
? ? def __init__(self):
? ? ? ? super(MyConvNet,self).__init__()
? ? ? ? # 定義第一層卷積
? ? ? ? self.conv1 = nn.Sequential(
? ? ? ? ? ? nn.Conv2d(
? ? ? ? ? ? ? ? in_channels = 1,? ? # 輸入的feature map
? ? ? ? ? ? ? ? out_channels = 16,? ?# 輸出的feature map
? ? ? ? ? ? ? ? kernel_size = 3,? ? # 卷積核大小
? ? ? ? ? ? ? ? stride = 1,? ? ?# 卷積核步長為1
? ? ? ? ? ? ? ? padding = 1,? ? # 邊緣填充1
? ? ? ? ? ? ),
? ? ? ? ? ? nn.ReLU(),? # 激活函數(shù)
? ? ? ? ? ? nn.AvgPool2d(
? ? ? ? ? ? ? ? kernel_size = 2,? ? # 平均值池化,2*2
? ? ? ? ? ? ? ? stride = 2,? ? ?# 池化步長2
? ? ? ? ? ? ),? #池化后:(16*28*28)->(16*14*14)
? ? ? ? )
#定義第二個卷積層
? ? ? ? self.conv2 = nn.Sequential(
? ? ? ? ? ? nn.Conv2d(16, 32, 3, 1, 0),? #卷積操作(16*14*14)->(32*12*12)
? ? ? ? ? ? nn.ReLU(),
? ? ? ? ? ? nn.AvgPool2d(2, 2),? #最大值池化(32*12*12)->(32*6*6)
? ? ? ? )
? ? ? ? self.classifier = nn.Sequential(
? ? ? ? ? ? nn.Linear(32 * 6 * 6, 256),
? ? ? ? ? ? nn.ReLU(),
? ? ? ? ? ? nn.Linear(256, 128),
? ? ? ? ? ? nn.ReLU(),
? ? ? ? ? ? nn.Linear(128, 10),
? ? ? ? )
? ? def forward(self,x):
? ? ? ? # 定義前向傳播路徑
? ? ? ? x = self.conv1(x)
? ? ? ? x = self.conv2(x)
? ? ? ? x = x.view(x.size(0), -1)? ?# 展平多維的卷積圖層
? ? ? ? output = self.classifier(x)
? ? ? ? return output
#輸出網(wǎng)絡結構
myconvnet = MyConvNet()
myconvnet
? ? ? ? 上面程序中的類MyConvNet()通過nn.Sequential()、nn.Conv2d()、nn.ReLU()、nn.AvgPool2d()、nn.Linear()等層,定義了一個擁有兩個卷積層和三個全連接層的卷積神經(jīng)網(wǎng)絡分類器,并且在forward()函數(shù)中定義了數(shù)據(jù)在網(wǎng)絡中的前向傳播過程,然后使用myconvnet = MyConvNet()得到可用于學習的網(wǎng)絡myconvnet,其網(wǎng)絡結構輸出如下所示:
Out[3]:MyConvNet( ?
? ? ? ? ? ? ? ? ? ?(conv1): Sequential( ? ?
? ? ? ? ? ? ? ? ? ? ? ?(0): Conv2d(1, 16, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) ??
? ? ? ? ? ? ? ? ? ? ?? (1): ReLU() ??
? ? ? ? ? ? ? ? ? ? ? ?(2): AvgPool2d(kernel_size=2, stride=2, padding=0) ?
? ? ? ? ? ? ? ? ? ? )
? ? ? ? ? ? ? ? ? ? (conv2): Sequential( ? ?
? ? ? ? ? ? ? ? ? ? ? ? (0): Conv2d(16, 32, kernel_size=(3, 3), stride=(1, 1)) ??
? ? ? ? ? ? ? ? ? ? ? ??(1): ReLU() ?
? ? ? ? ? ? ? ? ? ? ? ? (2): AvgPool2d(kernel_size=2, stride=2, padding=0) ?
? ? ? ? ? ? ? ? ? ? ?)
? ? ? ? ? ? ? ? ? ? ?(classifier): Sequential( ??
? ? ? ? ? ? ? ? ? ? ? ??(0): Linear(in_features=1152, out_features=256, bias=True) ??
? ? ? ? ? ? ? ? ? ? ? ? (1): ReLU() ? ?
? ? ? ? ? ? ? ? ? ? ? ? (2): Linear(in_features=256, out_features=128, bias=True) ??
? ? ? ? ? ? ? ? ? ? ? ? (3): ReLU() ??
? ? ? ? ? ? ? ? ? ? ? ? (4): Linear(in_features=128, out_features=10, bias=True) ?
? ? ? ? ? ? ? ? ? ? ? )
? ? ? ? ? ? ? ? ?)
3、卷積神經(jīng)網(wǎng)絡訓練與預測
? ? ? ?為了訓練定義好的網(wǎng)絡結構myconvnet。還需要定義一個train_model()函數(shù),該函數(shù)可以用訓練數(shù)據(jù)集來訓練myconvnet。訓練數(shù)據(jù)整體包含60000張圖像,938個batch,可以使用80%的batch用于模型的訓練,20%的batch用于模型的驗證,所以在定義train_model()函數(shù)時,應該包含模型的訓練和驗證兩個過程。train_model()函數(shù)的程序如下所示:
# 定義網(wǎng)絡的訓練過程函數(shù)
def train_model(model, traindataloader, train_rate, criterion, optimizer, num_epochs = 25):
? ? '''
? ? model:網(wǎng)絡模型;traindataloader:訓練數(shù)據(jù)集,會切分為訓練集和驗證集;
? ? train_rate:訓練集batchsize百分比;criterion:損失函數(shù);optimizer:優(yōu)化方法;num_epochs:訓練的輪數(shù)
? ? '''
? ? # 計算訓練使用的 batch 數(shù)量
? ? batch_num = len(traindataloader)
? ? train_batch_num = round(batch_num * train_rate)
? ? # 復制模型參數(shù)
? ? best_model_wts = copy.deepcopy(model.state_dict())
? ? best_acc = 0.0
? ? train_loss_all = []
? ? train_acc_all = []
? ? val_loss_all =[]
? ? val_acc_all = []
? ? since = time.time()
? ? # 訓練框架
? ? for epoch in range(num_epochs):
? ? ? ? print('Epoch {}/{}'.format(epoch, num_epochs - 1))
? ? ? ? print('-' * 10)
? ? ? ? #每個epoch有兩個訓練階段
? ? ? ? train_loss = 0.0?
? ? ? ? train_corrects = 0
? ? ? ? train_num = 0
? ? ? ? val_loss = 0.0
? ? ? ? val_corrects = 0
? ? ? ? val_num = 0
? ? ? ? for step, (b_x, b_y) in enumerate(traindataloader):
? ? ? ? ? ? if step < train_batch_num:
? ? ? ? ? ? ? ? model.train()? ?# 設置模型為訓練模式
? ? ? ? ? ? ? ? output = model(b_x)
? ? ? ? ? ? ? ? pre_lab = torch.argmax(output, 1)
? ? ? ? ? ? ? ? loss = criterion(output, b_y)? ?# 計算誤差損失
? ? ? ? ? ? ? ? optimizer.zero_grad()? ?# 清空過往梯度
? ? ? ? ? ? ? ? loss.backward() # 誤差反向傳播
? ? ? ? ? ? ? ? optimizer.step()? ? # 根據(jù)誤差更新參數(shù)
? ? ? ? ? ? ? ? train_loss += loss.item() * b_x.size(0)
? ? ? ? ? ? ? ? train_corrects += torch.sum(pre_lab == b_y.data)
? ? ? ? ? ? ? ? train_num += b_x.size(0)
? ? ? ? ? ? else:
? ? ? ? ? ? ? ? model.eval()? ? # 設置為驗證模式
? ? ? ? ? ? ? ? output = model(b_x)
? ? ? ? ? ? ? ? pre_lab = torch.argmax(output, 1)
? ? ? ? ? ? ? ? loss = criterion(output, b_y)
? ? ? ? ? ? ? ? val_loss += loss.item() * b_x.size(0)
? ? ? ? ? ? ? ? val_corrects += torch.sum(pre_lab == b_y.data)
? ? ? ? ? ? ? ? val_num += b_x.size(0)
? ? ? ? # ======================小循環(huán)結束========================
? ? ? ? # 計算一個epoch在訓練集和驗證集上的損失和精度
? ? ? ? train_loss_all.append(train_loss / train_num)
? ? ? ? train_acc_all.append(train_corrects.double().item() / train_num)
? ? ? ? val_loss_all.append(val_loss / val_num)
? ? ? ? val_acc_all.append(val_corrects.double().item() / val_num)
? ? ? ? print('{} Train Loss: {:.4f} Train Acc: {:.4f}'.format(epoch, train_loss_all[-1], train_acc_all[-1]))
? ? ? ? print('{} Val Loss: {:.4f} Val Acc: {:.4f}'.format(epoch, val_loss_all[-1], val_acc_all[-1]))
? ? ? ? # 拷貝模型最高精度下的參數(shù)
? ? ? ? if val_acc_all[-1] > best_acc:
? ? ? ? ? ? best_acc = val_acc_all[-1]
? ? ? ? ? ? best_model_wts = copy.deepcopy(model.state_dict())
? ? ? ? time_use = time.time() - since
? ? ? ? print('Train and Val complete in {:.0f}m {:.0f}s'.format(time_use // 60, time_use % 60))
? ? # ===========================大循環(huán)結束===========================
? ? # 使用最好模型的參數(shù)
? ? model.load_state_dict(best_model_wts)
? ? train_process = pd.DataFrame(
? ? ? ? data = {"epoch": range(num_epochs),
? ? ? ? ? ? ? ? "train_loss_all": train_loss_all,
? ? ? ? ? ? ? ? "val_loss_all": val_loss_all,
? ? ? ? ? ? ? ? "train_acc_all": train_acc_all,
? ? ? ? ? ? ? ? "val_acc_all": val_acc_all})
? ? return model, train_process
? ? ? ?在上面的train_model()函數(shù)通過train_batch_num確定用于訓練的batch數(shù)量,并且在每輪的迭代中,如果step<train_batch_num,則進入訓練模式,否則進入驗證模式。在模型的訓練和驗證過程中,分別輸出當前的損失函數(shù)大小和對應的識別精度,并將它們保存在列表匯總中,最后組成數(shù)據(jù)表格train_process輸出。為了保存模型最高精度下的訓練參數(shù),使用copy.deepcopy()函數(shù)將模型最優(yōu)的參數(shù)保存在best_model_wts中,最終將所有的訓練結果使用 model.load_state_dict(best_model_wts)將最優(yōu)的參數(shù)賦值給最終的模型。
? ? ? 下面使用train_model()函數(shù),對指定的模型和優(yōu)化器進行訓練。
# 對模型進行訓練
optimizer = torch.optim.Adam(myconvnet.parameters(), lr = 0.0003)? ?# Adam優(yōu)化器
criterion = nn.CrossEntropyLoss()? ?# 損失函數(shù)
myconvnet, train_process = train_model(myconvnet, train_loader, 0.8, criterion, optimizer, num_epochs = 25)
? ? ? ? myconvnet分類器使用了Adam優(yōu)化器,損失函數(shù)為交叉熵損失函數(shù)。train_model()將訓練集train_loader的80%用于訓練,20%用于測試,共訓練25輪,訓練過程中的輸出如下所示:
Out[4]:?Epoch 0/24?
????????? ? ? ?----------?
???????????????0 Train Loss: 0.7745 Train Acc: 0.7146?
???????????????0 Val Loss: 0.5506 Val Acc: 0.7888?
???????????????Train and Val complete in 0m 12s?
???????????????Epoch 1/24
???????????????......
???????????????24 Train Loss: 0.1780 Train Acc: 0.9336?
???????????????24 Val Loss: 0.2592 Val Acc: 0.9128?
???????????????Train and Val complete in 4m 56s
? ? ? ?在模型訓練結束后使用折線圖將模型訓練過程中的精度和損失函數(shù)進行可視化,得到的圖像如圖3所示:
# 可視化訓練過程
plt.figure(figsize = (12, 4))
plt.subplot(1, 2, 1)
plt.plot(train_process.epoch, train_process.train_loss_all, 'ro-', label = 'Train loss')
plt.plot(train_process.epoch, train_process.val_loss_all, 'bs-', label = 'Val loss')
plt.legend()
plt.xlabel('epoch')
plt.ylabel('Loss')
plt.subplot(1, 2, 2)
plt.plot(train_process.epoch, train_process.train_acc_all, 'ro-', label = 'Train acc')
plt.plot(train_process.epoch, train_process.val_acc_all, 'bs-', label = 'Val acc')
plt.xlabel('epoch')
plt.ylabel('Acc')
plt.legend()
plt.show()

? ? ? ?從圖3可以發(fā)現(xiàn)模型在訓練過程中,損失函數(shù)在訓練集上迅速減小,在驗證集上先減小然后逐漸收斂到一個很小的區(qū)間,說明模型已經(jīng)穩(wěn)定。在訓練集上的精度一直在增大,而在驗證集上的精度收斂到一個小區(qū)間內。
? ? ? ?為了得到計算模型的泛化能力,使用輸出的模型在測試集上進行預測。
# 測試集預測,并可視化預測效果
myconvnet.eval()
output = myconvnet(test_data_x)
pre_lab = torch.argmax(output, 1)
acc = accuracy_score(test_data_y, pre_lab)
print(test_data_y)
print(pre_lab)
print("測試集上的預測精度為:", acc)
Out[5]:tensor([9, 2, 1, ?..., 8, 1, 5])?
? ? ? ? ? ? ?tensor([9, 2, 1, ?..., 8, 1, 5])?
? ? ? ? ? ? ?測試集上的預測精度為: 0.9073
? ? ? ?從輸出結果可以發(fā)現(xiàn),模型在測試集上的預測精度為90.73%。針對測試樣本的預測結果,使用混淆矩陣表示,并將其可視化,觀察其在每類數(shù)據(jù)上的預測情況。
# 計算測試集上的混淆矩陣并可視化
conf_mat = confusion_matrix(test_data_y, pre_lab)
df_cm = pd.DataFrame(conf_mat, index = class_label, columns = class_label)
heatmap = sns.heatmap(df_cm, annot = True, fmt = 'd', cmap = 'YlGnBu')
heatmap.yaxis.set_ticklabels(heatmap.yaxis.get_ticklabels(), rotation = 0, ha = 'right')
heatmap.xaxis.set_ticklabels(heatmap.xaxis.get_ticklabels(), rotation = 45, ha = 'right')
plt.ylabel('True label')
plt.xlabel('Predicted label')
plt.show()
? ? ? ?上述代碼輸出如圖4所示的混淆矩陣。

? ? ? ?從圖4中可以發(fā)現(xiàn),最容易預測發(fā)生錯誤的是T-shirt和Shirt,相互預測的樣本量超過了100個。
4、空洞卷積神經(jīng)網(wǎng)絡的搭建
? ? ? ?在Pytorch庫中使用nn.Conv2d()函數(shù),通過調節(jié)參數(shù)dilation的取值,進行不同大小卷積核的空洞卷積運算。針對搭建的空洞卷積神經(jīng)網(wǎng)絡,可以使用圖5所示的網(wǎng)絡結構。
? ? ? ?圖5 搭建的卷積神經(jīng)網(wǎng)路含有兩個空洞卷積層,兩個池化層以及兩個全連接層,并且分類器包含10個神經(jīng)元。該網(wǎng)絡結構除了卷積方式的差異外,與圖2所示的卷積結構完全相同。

? ? ? ?下面搭建圖5所表示的空洞卷積,程序如下:
class MyConvdilaNet(nn.Module):
? ? def __init__(self):
? ? ? ? super(MyConvdilaNet, self).__init__()
? ? ? ? # 定義第一層卷積
? ? ? ? self.conv1 = nn.Sequential(
? ? ? ? ? ? nn.Conv2d(
? ? ? ? ? ? ? ? in_channels = 1,? ? # 輸入圖像通道數(shù)
? ? ? ? ? ? ? ? out_channels = 16,? ?# 輸出特征數(shù)(卷積核個數(shù))
? ? ? ? ? ? ? ? kernel_size = 3,? ? # 卷積核大小
? ? ? ? ? ? ? ? stride = 1,? ? ?# 卷積核步長1
? ? ? ? ? ? ? ? padding = 1,? ? # 邊緣填充1
? ? ? ? ? ? ? ? dilation = 2,
? ? ? ? ? ? ),
? ? ? ? ? ? nn.ReLU(),? # 激活函數(shù)
? ? ? ? ? ? nn.AvgPool2d(
? ? ? ? ? ? ? ? kernel_size = 2,? ? # 平均值池化,2*2
? ? ? ? ? ? ? ? stride = 2,? ? ?# 池化步長2
? ? ? ? ? ? ),
? ? ? ? )
? ? ? ? self.conv2 = nn.Sequential(
? ? ? ? ? ? nn.Conv2d(16, 32, 3, 1, 0, dilation = 2),
? ? ? ? ? ? nn.ReLU(),
? ? ? ? ? ? nn.AvgPool2d(2, 2),
? ? ? ? )
? ? ? ? self.classifier = nn.Sequential(
? ? ? ? ? ? nn.Linear(32 * 4 * 4, 256),
? ? ? ? ? ? nn.ReLU(),
? ? ? ? ? ? nn.Linear(256, 128),
? ? ? ? ? ? nn.ReLU(),
? ? ? ? ? ? nn.Linear(128, 10),
? ? ? ? )
? ? def forward(self, x):
? ? ? ? # 定義前向傳播路徑
? ? ? ? x = self.conv1(x)
? ? ? ? x = self.conv2(x)
? ? ? ? x = x.view(x.size(0), -1)? ?# 展平多維的卷積圖層
? ? ? ? output = self.classifier(x)
? ? ? ? return output
#輸出網(wǎng)絡結構?
myconvdilation = MyConvdilaNet()
? ? ? ?上面的程序在類MyConvdilaNet()中,同樣通過nn.Sequential(),nn.Conv2d(),nn.ReLU(),nn.AvgPool2d(),nn.Linear()等層定義了一個擁有兩個空洞卷積層和三個全連接層的卷積神經(jīng)網(wǎng)絡的分類器,其中在空洞卷積中使用參數(shù)dilation = 2來實現(xiàn),最后在forward()函數(shù)中定義了數(shù)據(jù)在網(wǎng)絡中的前向傳播過程,并使用myconvdilation =?MyConvdilaNet()得到可用于學習的網(wǎng)絡myconvnet。
5、空洞卷積神經(jīng)網(wǎng)絡訓練與預測
? ? ? ?下面使用定義好的train_model()函數(shù)對網(wǎng)絡myconvdilation進行訓練,程序代碼如下:
# 對模型進行訓練
optimizer = torch.optim.Adam(myconvdilanet.parameters(), lr = 0.0003)
criterion = nn.CrossEntropyLoss()
myconvdilanet, train_process = train_model(myconvdilanet, train_loader, 0.8, criterion, optimizer, num_epochs = 25)
Out[6]:Epoch 0/24?
? ? ? ? ? ? ? ----------?
? ? ? ? ? ? ? 0 Train Loss: 0.8922 Train Acc: 0.6718?
? ? ? ? ? ? ? 0 Val Loss: 0.6322 Val Acc: 0.7498?
? ? ? ? ? ? ? Train and Val complete in 1m 2s
? ? ? ? ? ? ? ...
????????????? 24 Train Loss: 0.2531 Train Acc: 0.9062?
? ? ? ? ? ? ? 24 Val Loss: 0.2907 Val Acc: 0.8942?
? ? ? ? ? ? ? Train and Val complete in 24m 58s
? ? ? ? 使用折線圖將模型訓練過程中的精度和損失函數(shù)進行可視化,程序代碼如下:
# 可視化訓練過程
plt.figure(figsize = (12, 4))
plt.subplot(1, 2, 1)
plt.plot(train_process.epoch, train_process.train_loss_all, 'ro-', label = 'Train loss')
plt.plot(train_process.epoch, train_process.val_loss_all, 'bs-', label = 'Val loss')
plt.legend()
plt.xlabel('epoch')
plt.ylabel('Loss')
plt.subplot(1, 2, 2)
plt.plot(train_process.epoch, train_process.train_acc_all, 'ro-', label = 'Train acc')
plt.plot(train_process.epoch, train_process.val_acc_all, 'bs-', label = 'Val acc')
plt.legend()
plt.xlabel('epoch')
plt.ylabel('acc')
plt.show()

? ? ? ?從模型訓練過程中可以看出,損失函數(shù)在訓練集上迅速縮小,在驗證集上先減小然后逐漸收斂到一個很小的區(qū)間,說明模型已經(jīng)穩(wěn)定。在訓練集上的精度在一直增大,而在驗證集上的精度收斂到一個小區(qū)間內。
? ? ? ?下面使用輸出的模型在測試集上進行預測,以計算模型的泛化能力,可使用如下所示的程序:
# 對測試集進行預測,并可視化預測效果
myconvdilanet.eval()
output = myconvdilanet(test_data_x)
pre_lab = torch.argmax(output, 1)
acc = accuracy_score(test_data_y, pre_lab)
print(''測試集上的預測精度為:'', acc)
Out[7]:?測試集上的預測精度為:0.8841
? ? ? ?從輸出中可以發(fā)現(xiàn)模型在測試集上的預測精度為88.41%,識別精度略低于卷積神經(jīng)網(wǎng)路,針對測試樣本的預測結果,同樣可以使用混淆矩陣表示,并將其可視化,程序如下:
# 計算混淆矩陣并可視化
conf_mat = confusion_matrix(test_data_y, pre_lab)
df_cm = pd.DataFrame(conf_mat, index = label, columns = label)
heatmap = sns.heatmap(df_cm, annot = True, fmt = 'd', cmap = 'YlGnBu')
heatmap.yaxis.set_ticklabels(heatmap.yaxis.get_ticklabels(), rotation = 0, ha = 'right')
heatmap.xaxis.set_ticklabels(heatmap.xaxis.get_ticklabels(), rotation = 45, ha = 'right')
plt.ylabel('True label')
plt.xlabel('Predicted label')
plt.show()
? ? ? 如圖7所示:
