AlexNet網絡模型搭建
環境準備
首先在某個盤符下創建一個文件夾,就叫AlexNet吧,用來存放源代碼。
然后新建一個python文件,就叫plot.py
吧,往里面寫入以下代碼,用于下載數據集:
# FashionMNIST里面包含了許多數據集
from click.core import batch
from spacy.cli.train import train
from torchvision.datasets import FashionMNIST
from torchvision import transforms # 處理數據集,歸一化
import torch.utils.data as Data
import numpy as np
import matplotlib.pyplot as plt# 下載FashionMMIST數據集
train_data = FashionMNIST(root="./data", # 指定數據集要下載的路徑train=True,# 要訓練集# 將數據進行歸一化操作transform=transforms.Compose([transforms.Resize(size=224), # 調整數據的大小transforms.ToTensor() # 將數據轉換為tensor]),download=True # 開啟下載
)# 加載數據集集
train_loader = Data.DataLoader(dataset=train_data,# 要加載的數據集batch_size=64 ,# 批量數據大小shuffle=True, # 打亂數據順序num_workers=0, # 加載數據線程數量
)# 繪制出訓練集
for step,(b_x,b_y) in enumerate(train_loader):if step > 0:breakbatch_x = b_x.squeeze().numpy() # 將四維張量移除第一維,將數據轉換為numpy格式batch_y = b_y.numpy() # 將張量數據轉成numpy格式class_label = train_data.classes # 訓練集標簽
print("class_label,",class_label)# 繪圖
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=10)plt.axis('off')plt.subplots_adjust(wspace=0.05)plt.show()
執行上述代碼后,就會開始下載所需要的數據集文件,只不過下載的速度比較慢,然后下載完成,項目的根目錄會多出data文件夾,以下是data的目錄結構:
--data--FashionMNIST--raw # 該文件夾下就存放數據集文件
搭建網絡模型
創建model.py
文件,用于構建模型代碼。
import torch
from torch import nn # nn層
from torchsummary import summary # 查看網絡模型參數
import torch.nn.functional as F # 用于dropout# 搭建模型
class AlexNet(nn.Module):def __init__(self):# 初始化網絡層super(AlexNet, self).__init__()self.ReLU = nn.ReLU() # ReLU 激活函數# kernel_size=11 : 卷積核大小 11*11self.c1 = nn.Conv2d(in_channels=1, out_channels=96, kernel_size=11, stride=4) # 第一層:卷積self.s2 = nn.MaxPool2d(kernel_size=3, stride=2) # 第二層:最大池化層self.c3 = nn.Conv2d(in_channels=96, out_channels=256, kernel_size=5, padding=2) # 第三層:卷積層self.s4 = nn.MaxPool2d(kernel_size=3, stride=2) # 第四層:最大池化層self.c5 = nn.Conv2d(in_channels=256, out_channels=384, kernel_size=3, padding=1) # 第五層:卷積層self.c6 = nn.Conv2d(in_channels=384, out_channels=384, kernel_size=3, padding=1) # 第六層:卷積層self.c7 = nn.Conv2d(in_channels=384, out_channels=256, kernel_size=3, padding=1) # 第七層:卷積層self.s8 = nn.MaxPool2d(kernel_size=3, stride=2) # 第八層:最大池化層self.flatten = nn.Flatten() # 第九層:平展層self.f1 = nn.Linear(6 * 6 * 256, 4096) # 第十層:全連接層self.f2 = nn.Linear(4096, 4096) # 第十一層:全連接層self.f3 = nn.Linear(4096, 10) # 第十二層:全連接層# 前向傳播def forward(self, x):# 卷積完后,然后進行激活函數x = self.ReLU(self.c1(x))x = self.s2(x)x = self.ReLU(self.c3(x))x = self.s4(x)x = self.ReLU(self.c5(x))x = self.ReLU(self.c6(x))x = self.ReLU(self.c7(x))x = self.s8(x)x = self.flatten(x) # 平展層x = self.ReLU(self.f1(x)) # 線性全連接層x = F.dropout(x, 0.5)x = self.ReLU(self.f2(x))x = F.dropout(x, 0.5)x = self.f3(x) # 輸出層return x# 測試模型的搭建有沒有成功(僅測試)
if __name__ == "__main__":device = torch.device("cuda" if torch.cuda.is_available() else "cpu")model = AlexNet().to(device)print(summary(model, (1, 227, 227)))
模型訓練
創建一個model_train.py
文件
import copy
import timeimport torch
from torchvision.datasets import FashionMNIST
from torchvision import transforms
import torch.nn as nn
import torch.utils.data as Data
import numpy as np
import pandas as pd
import matplotlib.pyplot as pltfrom model import AlexNet# 處理訓練集核數據集
def train_val_data_process():# 加載數據集train_data = FashionMNIST(root="./data",# 數據集所在路徑train=True, # 要訓練集# 將數據進行歸一化操作transform=transforms.Compose([transforms.Resize(size=227), # 修改數據的大小transforms.ToTensor() # 將數據轉成Tensor格式]),download=True # 開啟加載)# 隨機 劃分訓練集 和 驗證集train_data,val_data = Data.random_split(train_data, # 要劃分的數據集[round(0.8*len(train_data)), # 劃分80%給訓練集round(0.2*len(train_data)) # 劃分20%給驗證集])# 加載訓練集數據train_dataloader = Data.DataLoader(dataset=train_data,# 要加載的訓練集batch_size=32,# 每輪的訓練批次數shuffle=True,# 打亂數據順序num_workers=2,# 加載數據線程數量)# 加載驗證集數據val_dataloader = Data.DataLoader(dataset=val_data,# 要加載的驗證集batch_size=32,# 每輪的訓練批數shuffle=True,# 打亂數據順序num_workers=2,# 加載數據集的線程數量)return train_dataloader,val_dataloader# 模型訓練
def train_model_process(model,train_dataloader,val_dataloader,num_epochs):# model:需要訓練的模型,train_dataloader:訓練集數據,val_dataloader:驗證集數據,num_epochs:訓練輪數# 指定設備device = torch.device("cuda" if torch.cuda.is_available() else "cpu")# 定義優化器optimizer = torch.optim.Adam(model.parameters(),lr=0.001)# 定義交叉熵損失函數criterion = nn.CrossEntropyLoss()# 將模型放入到設備中進行訓練model.to(device)# 復制當前模型的參數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)# 初始化參數,記錄本輪的模型的損失之和精度train_loss = 0.0 # 訓練的損失train_corrects = 0 # 訓練的準確度val_loss = 0.0 # 驗證集的損失val_corrents = 0 # 驗證集的準確度train_num = 0 # 本輪訓練集的數量val_num = 0 # 本輪驗證集的數量# 取出每輪中的數據集進行訓練for step,(b_x,b_y) in enumerate(train_dataloader):b_x = b_x.to(device) # 將訓練集數據放入到設備當中b_y = b_y.to(device) # 將標簽數據放入到設備當中model.train() # 開啟模型訓練模式# 將每批次中的標簽數據放入到模型中,進行前向傳播output = model(b_x)# 查找每一行中最大值對應的行標,即預測值pre_lab = torch.argmax(output,dim=1)# 計算當前批次的損失值(模型的輸出,標簽)loss = criterion(output,b_y)# 每批次訓練完后,將梯度初始化成0optimizer.zero_grad()# 反向傳播計算loss.backward()# 更新參數optimizer.step()# 本批次損失值的累加train_loss += loss.item() * b_x.size(0)# 如果模型預測的結果正確,本批次的準確度+1train_corrects += torch.sum(pre_lab == b_y.data)# 本此次的訓練數據累加train_num += b_y.size(0)# 取出每輪中的數據進行驗證for step,(b_x,b_y) in enumerate(val_dataloader):# 將數據和標簽分別放入到設備中b_x = b_x.to(device)b_y = b_y.to(device)model.eval() # 設置模型為評估模式# 前向傳播,輸入一個批次,輸出該批次的對應的預測值output = model(b_x)# 查找每一行中最大值對應的行標,即預測值pre_lab = torch.argmax(output,dim=1)# 計算本此次的損失函數loss = criterion(output,b_y)# 本批次的損失函數累加val_loss += loss.item() * b_x.size(0)# 如果預測正確,那就本批次的精度度累加val_corrents += torch.sum(pre_lab==b_y.data)# 當前用于驗證的樣本數累加val_num += b_x.size(0)# 計算每輪次的損失值和準確率train_loss_all.append(train_loss / train_num) # 本輪訓練集的loss值train_acc_all.append(train_corrects.double().item() / train_num) # 本輪訓練集的準確率val_loss_all.append(val_loss / val_num) # 本輪驗證集的loss值val_acc_all.append(val_corrents.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]))# 尋找最高準確度 和 模型的權重參數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() - sinceprint("訓練耗費的時間:{:.0f}m{:.0f}s".format(time_use//60,time_use%60))# 將最佳的模型參數保存torch.save(best_model_wts,"best_model.pth") # .pth是權重文件的后綴# 保存訓練好的模型參數train_process = pd.DataFrame(data={"epoch":range(num_epochs),"train_loss_all":train_loss_all,"train_acc_all":train_acc_all,"val_loss_all":val_loss_all,"val_acc_all":val_acc_all})return train_process# 定義繪圖的函數,繪制loss和準確度
def matplot_acc_loss(train_process):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_acc_all,"bs-",label="val loss")plt.legend() # 圖例plt.xlabel("epoch") # x軸標簽plt.ylabel("loss") # y軸標簽# 繪制訓練集和驗證集的準確度圖像plt.subplot(1,2,2) # 一行兩列,第二列plt.plot(train_process['epoch'],train_process.val_loss_all,"ro-",label="train acc")plt.plot(train_process['epoch'],train_process.val_acc_all,"bs-",label="va acc")if __name__ == '__main__':# 實例化自定義模型類model = AlexNet()# 加載數據集train_dataloader,val_dataloader = train_val_data_process()# 訓練模型train_process = train_model_process(model,train_dataloader,val_dataloader,20)# 繪制圖像matplot_acc_loss(train_process)
模型測試
創建一個model_test.py
文件,用于模型的測試
import torch
import torch.utils.data as Data
from numpy.random import shuffle
from torchvision import transforms
from torchvision.datasets import FashionMNISTfrom model import AlexNet# 加載要訓練的數據
def test_data_process():# 加載測試集數據test_data = FashionMNIST(root="./data",# 指定數據集要下載的路徑train=False,# 不要訓練集數據# 數據歸一化操作transform=transforms.Compose([transforms.Resize(size=227), # 將數據轉成227*227大小transforms.ToTensor(),# 將數據轉成Tensor格式]),download=True # 加載數據)# 通過DataLoader加載器 來加載數據test_dataloader = Data.DataLoader(dataset=test_data,# 要加載的數據batch_size=1, # 每輪訓練的批次數shuffle=True, # 打亂數據集num_workers=0, # 加載數據集的線程數量)return test_dataloader# 測試模型
def test_model_process(model,test_dataloader):# 指定設備device = torch.device("cuda" if torch.cuda.is_available() else "cpu")# 將模型放入設備中model.to(device)# 初始化模型訓練的每輪參數test_correct = 0.0 # 準確度test_num = 0 # 測試樣本數量# 只進行前向傳播with torch.no_grad(): # 將梯度設置為0for test_data_x,test_data_y in enumerate(test_dataloader): # 遍歷每輪次# 由于上面設置批次為1,所以這里就不需要循環批次了test_data_x = test_data_x.to(device) # 將測試數據放入到設備中test_data_y = test_data_y.to(device) # 將標簽數據放入到設備中# 模型切換成評估模式model.eval()# 前向傳播 將測試數據放入到模型中output = model(test_data_x)# 查找每一行中最大值的行標pre_lab = torch.argmax(output,dim=1)# 模型預測的結果 將pre_lab 與 標簽數據 進行比較# 如果預測正確,則加1test_correct += torch.sum(pre_lab==test_data_y.data)# 測試樣本數量累加test_num += test_data_y.size(0)# 計算最終測試的準確率 每輪的準確度 / 總樣本數量test_acc = test_correct.double().item() / test_numprint("測試模型的準確率為:",test_acc)if __name__ == '__main__':# 加載模型model = AlexNet()# 加載訓練好的模型最佳參數model.load_state_dict(torch.load('best_model.pth'))# 加載測試的數據集test_dataloader = test_data_process()# 開始訓練# test_model_process(model, test_dataloader)# 模型具體的訓練過程device = torch.device("cuda" if torch.cuda.is_available() else "cpu")# 將模型放入到設備當中model = model.to(device)# 數據的類別classes = ('T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat','Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot')# 梯度設置為0with torch.no_grad():# 遍歷測試集中的 測試數據 和 標簽for b_x,b_y in test_dataloader:# 將模型設置為評估模式model.eval()# 將數據放入到模型中,得出預測結果output = model(b_x)# 獲取最大值的行標pre_lab = torch.argmax(output,dim=1)# 取出張量中的下標result = pre_lab.item()label = b_y.item()print("預測結果為:",classes[result],"標簽為:",classes[label])