參考:Convolutional Neural Networks Tutorial in PyTorch - Adventures in Machine Learning

網路結構大致為:

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader
import torchvision.transforms as transforms
import torchvision.datasets
import numpy as np

# PART 1:載入數據,設置超參

#超參設置
num_epochs = 5
num_classes = 10
batch_size = 100
learning_rate = 0.001

#pytorch 將會下載MNIST數據保存到DATA_PATH
#也會將訓練好的模型保存到MODEL_STORE_PATH
DATA_PATH = "E:/DL/CNN/dataset"
MODEL_STORE_PATH = "E:/DL/CNN/model"

# transforms to apply to the data
# transforms.Compose函數來自於torchvision包
# 用Compose可以將各種transforms有序組合到一個list中
# 首先指定一個轉換transforms.ToTensor(),將數據轉換為pytorch tensor
# pytorch tensor是pytorch中特殊的數據類型,用於網路中數據和權重的操作,本質上是多維矩陣
# 接下來用transforms.Normalize對數據進行歸一化,參數為數據的平均數和標準差
# MNIST數據是單通道的,多通道就需要提供每個通道的均值和方差
trans = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))])

# MNIST dataset,這裡創建了train_dataset和test_dataset對象
# root:train.pt和test.pt數據文件位置;train:指定獲取train.pt或者test.pt數據
# tranform:對創建的數據進行操作transform操作;download:從線上下載MNIST數據
train_dataset = torchvision.datasets.MNIST(root=DATA_PATH, train=True, transform=trans, download=True)
test_dataset = torchvision.datasets.MNIST(root=DATA_PATH, train=False, transform=trans)

# pytorch中的DataLoader對象,可以對數據洗牌,批處理數據,多處理來並行載入數據
train_loader = DataLoader(dataset=train_dataset, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(dataset=test_dataset, batch_size=batch_size, shuffle=False)

# PART 2:創建CNN類

# 神經網路的結構:
# 輸入圖片為 28*28 單通道
# 第一次卷積:32 channels of 5 x 5 convolutional filters,a ReLU activation
# followed by 2 x 2 max pooling(stride = 2,this gives a 14 x 14 output)
# 第二次卷積:64 channels of 5 x 5 convolutional filters
# 2 x 2 max pooling (stride = 2,produce a 7 x 7 output)
# 展開需要節點:7 x 7 x 64 = 3164 個,接上全連接層(含1000個節點)
# 最後對10個輸出節點進行softmax操作,產生分類概率

class ConvNet(nn.Module):
# 初始化定義網路的結構:也就是定義網路的層
def __init__(self):
super(ConvNet,self).__init__()

# Sequential方法使我們有序的創建網路層
# Conv2d nn.Module的方法,該方法創建一組卷積濾波器,
# 第一個參數為輸入的channel數,第二個參數為輸出的channel數
# kernel_size:卷積濾波器的尺寸,這裡卷積濾波器為5*5,所以參數設置為5
# 如果卷積濾波器為 x*y,參數就是一個元組(x,y)
self.layer1 = nn.Sequential(
# 卷積操作&池化操作的維度變化公式: width_of_output = (width_of_input - filter_size + 2*padding)/stride + 1
# 卷積操作時維度變化:28-5+2*2+1 =28,我希望卷積的輸出和輸出維度一樣,所以加了2 padding
nn.Conv2d(1,32,kernel_size=5,stride=1,padding=2),
# 激活函數
nn.ReLU(),
# kernel_size:pooling size,stride:down-sample
nn.MaxPool2d(kernel_size=2,stride=2))

self.layer2 = nn.Sequential(
nn.Conv2d(32,64,kernel_size=5,stride=1,padding=2),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2,stride=2))

self.drop_out = nn.Dropout()
# 後面兩個全連接層,分別有1000個節點,10個節點對應10種類別
# 接全連接層的意義是:將神經網路輸出的豐富信息加到標準分類器中
self.fc1 = nn.Linear(7*7*64,1000)
self.fc2 = nn.Linear(1000,10)

# 定義網路的前向傳播,該函數會覆蓋 nn.Module 里的forward函數
# 輸入x,經過網路的層層結構,輸出為out
def forward(self,x):
out = self.layer1(x)
out = self.layer2(out)
# flattens the data dimensions from 7 x 7 x 64 into 3164 x 1
# 左行右列,-1在哪邊哪邊固定只有一列
out = out.reshape(out.size(0),-1)
# 以一定概率丟掉一些神經單元,防止過擬合
out = self.drop_out(out)
out = self.fc1(out)
out = self.fc2(out)
return out

# PART 3:創建一個CNN實例
model = ConvNet()
# 該函數包含了 SoftMax activation 和 cross entorpy,所以在神經網路結構定義的時候不需要定義softmax activation
criterion = nn.CrossEntropyLoss()
# 第一個參數:我們想要訓練的參數。
# 在nn.Module類中,方法 nn.parameters()可以讓pytorch追蹤所有CNN中需要訓練的模型參數,讓他知道要優化的參數是哪些
optimizer = optim.Adam(model.parameters(),lr = learning_rate)

# PART 4:訓練模型

#訓練數據集長度
total_step = len(train_loader)
loss_list = []
acc_list = []
for epoch in range(num_epochs):
# 遍歷訓練數據(images,label)
for i,(images,labels) in enumerate(train_loader):
# 向網路中輸入images,得到output,在這一步的時候模型會自動調用model.forward(images)函數
outputs = model(images)
# 計算這損失
loss = criterion(outputs,labels)
loss_list.append(loss.item())

# 反向傳播,Adam優化訓練
# 先清空所有參數的梯度緩存,否則會在上面累加
optimizer.zero_grad()
# 計算反向傳播
loss.backward()
# 更新梯度
optimizer.step()

# 記錄精度
total = labels.size(0)
# torch.max(x,1) 按行取最大值
# output每一行的最大值存在_中,每一行最大值的索引存在predicted中
# output的每一行的每個元素的值表示是這一類的概率,取最大概率所對應的類作為分類結果
# 也就是找到最大概率的索引
_,predicted = torch.max(outputs.data,1)
# .sum()計算出predicted和label相同的元素有多少個,返回的是一個張量,.item()得到這個張量的數值(int型)
correct = (predicted == labels).sum().item()
acc_list.append(correct/total)

if (i+1) % 100 == 0:
print(Epoch[{}/{}],Step[{},{}],Loss:{:.4f},Accuracy:{:.2f}%
.format(epoch+1,num_epochs,i+1,total_step,loss.item(),(correct/total)*100))

# PART 5:測試模型

#將模型設為評估模式,在模型中禁用dropout或者batch normalization層
model.eval()
# 在模型中禁用autograd功能,加快計算
with torch.no_grad():
correct = 0
total = 0
for images,labels in test_loader:
outputs = model(images)
_,predicted = torch.max(outputs.data,1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
print(Test Accuracy of the model on the 1w test images:{} %.format((correct / total) * 100))

# save the model
torch.save(model.state_dict(),MODEL_STORE_PATH + conv_net_model.ckpt)

推薦閱讀:

相关文章