上一節我們學習了Pytorch優化網絡的基本方法,本節我們將以MNIST數據集為例,通過搭建一個完整的神經網絡,來加深對Pytorch的理解。
一、數據集
MNIST是一個非常經典的數據集,下載鏈接:http://yann.lecun.com/exdb/mnist/
下載下來的文件如下:
該手寫數字數據庫具有60,000個示例的訓練集和10,000個示例的測試集。它是NIST提供的更大集合的子集。數字已經過尺寸標准化,並以固定尺寸的圖像為中心。
手寫數字識別是一個比較簡單的任務,它是一個10分類問題,(0-9),之所以選這個數據集,是因為識別難度低,計算量小,數據容易獲得。
二、模型搭建
1、網絡節點的確定
對於不同的目的,網絡的選擇也是不一樣的。一般來說,網絡容量和數據集大小是對應的。一個小型數據集也只需要一個小型的網絡。
這里有一個經驗值:
1)model_size=sqrt(in_size*out_size)
2)model_size=log(in_size)
3) model_size=sqrt(in_size*out_size)
model_size:網絡的節點量
in_size:輸入的節點量
out_size輸出的節點量
2、導入pytorch包
import torch import torchvision import trochvision import datasets import trochvision import transforms from torch.autograd import Variable
3、獲取訓練集和測試集
#root用於指定數據集下載后的存放路徑
#transform用於指定導入數據集需要對數據進行變換操作
#train指定在數據集下載后需要載入哪部分數據,true為訓練集,false為測試集
data_train=datasets.MNIST(root="./data/",transform=transform,train=True,download=True) data_test=datasets.MNIST(root='./data/',transform=transform,train=False)
4、數據預覽和裝載
#數據裝載,可以理解為對圖片的處理 #處理完成后,將圖片送給模型訓練,裝載就是打包的過程 #dataset 用於指定載入的數據集名稱 #batch_size設置了每個包的圖片數據數據個數 #shuffle 裝載過程將數據隨機打亂並打包 data_loader_train=torch.utils.data.DataLoader(dataset=data_train,batch_size=64,shuffle=True) data_loader_test=torch.utils.data.DataLoader(dataset=data_test,batch_size=64,shuffle=True)
裝載后,可以選取其中一個批次數據進行預覽
images,labels=next(iter(data_loader_train)) img=torchvision.utils.make_grid(images) #batch_size,channel,height,weight img=img.numpy().transpose(1,2,0) #height,weight,channel std=[0.5,0.5,0.5] mean=[0.5,0.5,0.5] img=img*std+mean print(labels) cv2.show('win',img) key_pressed=cv2.waitKey(0)
5、模型搭建和參數優化
#卷積神經網絡CNN結構一般包含幾層: 輸入層:數據輸入 卷積層:卷積核進行特征提取和特征映射 torch.nn.Conv2d 激勵層:卷積也是線性,所以要增加非線性映射 torch.nn.ReLU 池化層:進行下采樣,對特征圖稀疏處理,去除噪聲 torch.nn.MaxPool2d 全連接層:torch.nn.Linear 輸出層:輸出結果 import torch.nn as nn class Model(nn.Module): def __init__(self): self.conv1=nn.Sequential( nn.Conv2d(1,64,kernel_size=3,stride=1,padding=1), nn.ReLU(), nn.Conv2d(64,128,kernel_size=3,stride=1,padding=1), nn.ReLU(), nn.Maxpool2d(stride=2,kernel_size=2)) self.dense=nn.Sequential( nn.Linear(14*14*128,1024), nn.ReLU(), nn.Dropout(p=0.5), nn.Linear(1024,10)) def forward(self,x): net=self.conv1(x) net=net.view(-1,14*14*128) net=self.dense(net) return net
由於MNIST分類任務較簡單,所以,網絡結構只使用了兩個卷積層,一個最大池化層和兩個全連接。
在前向傳播forward函數中,首先,經過self.conv1卷積處理,然后使用view(-1,14*14*128)對參數實現扁平化,來滿足緊接的全連接。
搭建完模型后,就可以開始進行模型訓練和對參數進行優化了。
#模型實例化 #定義損失函數和優化函數 #損失函數使用交叉熵 #優化函數使用自適應優化算法 model=Model() cost=torch.nn.CrossEntropyLoss() optimizer=torch.optim.Adam(model.parameters())
可以打印搭建好的網絡結構
print(model) 顯示如下: Model( (conv1): Sequential( (0): Conv2d(1, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (1): ReLU() (2): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (3): ReLU() (4): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) ) (dense): Sequential( (0): Linear(in_features=25088, out_features=1024, bias=True) (1): ReLU() (2): Dropout(p=0.5) (3): Linear(in_features=1024, out_features=10, bias=True) ) )
n_epochs = 10
for epoch in range(n_epochs): total_loss = 0.0 total_corrrect = 0 print("Epoch {}/{}".format(epoch, n_epochs)) print("-"*10) for data in data_loader_train: X_train , y_train = data X_train,y_train = Variable(X_train),Variable(y_train) outputs = model(X_train) _,pred = torch.max(outputs.data,1) optimizer.zero_grad() loss = cost(outputs,y_train) loss.backward() optimizer.step() total_loss += loss.data[0] total_corrrect += torch.sum(pred == y_train.data) testing_correct = 0 for data in data_loader_train: X_test,y_test = data X_test,y_test = Variable(X_test),Variable(y_test) outputs = model(X_test) _,pred = torch.max(X_test) testing_correct += torch.sum(pred == y_test.data) print("Loss is {:.4f}, Train Accuracy is:{:.4f}%,Test Accuracy is:{:.4f}" .format(total_loss/len(data_train),100*total_corrrect/len(data_train), 100*testing_correct/len(data_test)))
總的訓練周期為10次,使用print顯示訓練過程細節。且在每輪訓練完成后,使用測試集驗證模型泛化能力,並計算准確率。
6、完整訓練代碼
import torch import torch.nn as nn import torchvision.datasets as normal_datasets import torchvision.transforms as transforms from torch.autograd import Variable num_epochs = 5 batch_size = 100 learning_rate = 0.001 # 將數據處理成Variable, 如果有GPU, 可以轉成cuda形式 def get_variable(x): x = Variable(x) return x.cuda() if torch.cuda.is_available() else x # 從torchvision.datasets中加載一些常用數據集 train_dataset = normal_datasets.MNIST( root='./mnist/', # 數據集保存路徑 train=True, # 是否作為訓練集 transform=transforms.ToTensor(), # 數據如何處理, 可以自己自定義 download=True) # 路徑下沒有的話, 可以下載 # 見數據加載器和batch test_dataset = normal_datasets.MNIST(root='./mnist/', train=False, transform=transforms.ToTensor()) train_loader = torch.utils.data.DataLoader(dataset=train_dataset, batch_size=batch_size, shuffle=True) test_loader = torch.utils.data.DataLoader(dataset=test_dataset, batch_size=batch_size, shuffle=False) # 兩層卷積 class CNN(nn.Module): def __init__(self): super(CNN, self).__init__() # 使用序列工具快速構建 self.conv1 = nn.Sequential( nn.Conv2d(1, 16, kernel_size=5, padding=2), nn.BatchNorm2d(16), nn.ReLU(), nn.MaxPool2d(2)) self.conv2 = nn.Sequential( nn.Conv2d(16, 32, kernel_size=5, padding=2), nn.BatchNorm2d(32), nn.ReLU(), nn.MaxPool2d(2)) self.fc = nn.Linear(7 * 7 * 32, 10) def forward(self, x): out = self.conv1(x) out = self.conv2(out) out = out.view(out.size(0), -1) # reshape out = self.fc(out) return out cnn = CNN() if torch.cuda.is_available(): cnn = cnn.cuda() # 選擇損失函數和優化方法 loss_func = nn.CrossEntropyLoss() optimizer = torch.optim.Adam(cnn.parameters(), lr=learning_rate) for epoch in range(num_epochs): for i, (images, labels) in enumerate(train_loader): images = get_variable(images) labels = get_variable(labels) outputs = cnn(images) loss = loss_func(outputs, labels) optimizer.zero_grad() loss.backward() optimizer.step() if (i + 1) % 100 == 0: print('Epoch [%d/%d], Iter [%d/%d] Loss: %.4f' % (epoch + 1, num_epochs, i + 1, len(train_dataset) // batch_size, loss.item())) # Save the Trained Model torch.save(cnn.state_dict(), 'cnn.pkl')
7、模型驗證
為了驗證訓練好的模型效果,我們選取一部分測試集中的圖片,看看與真實值的偏差,並對結果可視化。
import matplotlib.pyplot as plt import cv2 import torch import torchvision from torchvision import datasets from torchvision import transforms from torch.autograd import Variable transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize(mean=[0.5,0.5,0.5],std=[0.5,0.5,0.5])]) data_test = datasets.MNIST(root="./data/", transform = transform, train = False) data_loader_test = torch.utils.data.DataLoader(dataset =data_test, batch_size = 4, shuffle = True) #模型搭建和參數優化 # 在順利完成數據裝載后,我們可以開始編寫卷積神經網絡模型的搭建和參數優化的代碼 #卷積層使用torch.nn.Conv2d類來搭建 # 激活層使用torch.nn.ReLU 類方法來搭建 # 池化層使用torch.nn.MaxPool2d類方法來搭建 # 全連接層使用 torch.nn.Linear 類方法來搭建 class Model(torch.nn.Module): def __init__(self): super(Model,self).__init__() self.conv1 = torch.nn.Sequential( torch.nn.Conv2d(1,64,kernel_size=3,stride=1,padding=1), torch.nn.ReLU(), torch.nn.Conv2d(64,128,kernel_size=3,stride=1,padding=1), torch.nn.ReLU(), torch.nn.MaxPool2d(stride=2,kernel_size=2)) self.dense = torch.nn.Sequential( torch.nn.Linear(14*14*128,1024), torch.nn.ReLU(), torch.nn.Dropout(p = 0.5), torch.nn.Linear(1024,10) ) # 我們通過繼承torch.nn.Modeule來構造網絡,因為手寫數字 # 識別比較簡單,我們只是用了兩個卷積層,一個最大池化層,兩個全連接層。 # 在向前傳播過程中進行x.view(-1, 14 * 14 * 128) # 對參數實現扁平化。最后通過自己self.dense定義的全連接層進行最后的分類 def forward(self, x): x = self.conv1(x) x = x.view(-1,14*14*128) x = self.dense(x) return x # 在編寫完搭建卷積神經網絡模型的代碼后,我們可以對模型進行訓練和參數進行優化了 # 首先 定義在訓練之前使用哪種損失函數和優化函數 # 下面定義了計算損失值的損失函數使用的是交叉熵 # 優化函數使用的額是Adam自適應優化算法 model = Model() # 將所有的模型參數移動到GPU上 if torch.cuda.is_available(): model.cuda() cost = torch.nn.CrossEntropyLoss() optimizer = torch.optim.Adam(model.parameters()) # print(model) # 卷積神經網絡模型進行模型訓練和參數優化的代碼 n_epochs = 5 X_test,y_test = next(iter(data_loader_test)) inputs = Variable(X_test) pred = model(inputs) _,pred = torch.max(pred,1) print("Predict Label is:",(i for i in pred)) print("Real Label is :",[i for i in y_test]) img = torchvision.utils.make_grid(X_test) img = img.numpy().transpose(1,2,0) std = [0.5,0.5,0.5] mean = [0.5,0.5,0.5] img = img*std +mean cv2.imshow('win',img) key_pressed=cv2.waitKey(0)
附原文完整代碼:
#_*_coding:utf-8_*_ import matplotlib.pyplot as plt import numpy as np import cv2 import time import torch # torchvision包的主要功能是實現數據的處理,導入和預覽等 import torchvision from torchvision import datasets from torchvision import transforms from torch.autograd import Variable start_time = time.time() # 對數據進行載入及有相應變換,將Compose看成一種容器,他能對多種數據變換進行組合 # 傳入的參數是一個列表,列表中的元素就是對載入的數據進行的各種變換操作 transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize(mean=[0.5,0.5,0.5],std=[0.5,0.5,0.5])]) # 首先獲取手寫數字的訓練集和測試集 # root 用於指定數據集在下載之后的存放路徑 # transform 用於指定導入數據集需要對數據進行那種變化操作 # train是指定在數據集下載完成后需要載入那部分數據, # 如果設置為True 則說明載入的是該數據集的訓練集部分 # 如果設置為FALSE 則說明載入的是該數據集的測試集部分 data_train = datasets.MNIST(root="./data/", transform = transform, train = True, download = True) data_test = datasets.MNIST(root="./data/", transform = transform, train = False) #數據預覽和數據裝載 # 下面對數據進行裝載,我們可以將數據的載入理解為對圖片的處理, # 在處理完成后,我們就需要將這些圖片打包好送給我們的模型進行訓練 了 而裝載就是這個打包的過程 # dataset 參數用於指定我們載入的數據集名稱 # batch_size參數設置了每個包中的圖片數據個數 # 在裝載的過程會將數據隨機打亂順序並進打包 data_loader_train = torch.utils.data.DataLoader(dataset =data_train, batch_size = 64, shuffle = True) data_loader_test = torch.utils.data.DataLoader(dataset =data_test, batch_size = 64, shuffle = True) # 在裝載完成后,我們可以選取其中一個批次的數據進行預覽 images,labels = next(iter(data_loader_train)) img = torchvision.utils.make_grid(images) img = img.numpy().transpose(1,2,0) std = [0.5,0.5,0.5] mean = [0.5,0.5,0.5] img = img*std +mean # print(labels) print([labels[i] for i in range(64)]) # 由於matplotlab中的展示圖片無法顯示,所以現在使用OpenCV中顯示圖片 # plt.imshow(img) # cv2.imshow('win',img) # key_pressed=cv2.waitKey(0) #模型搭建和參數優化 # 在順利完成數據裝載后,我們可以開始編寫卷積神經網絡模型的搭建和參數優化的代碼 #卷積層使用torch.nn.Conv2d類來搭建 # 激活層使用torch.nn.ReLU 類方法來搭建 # 池化層使用torch.nn.MaxPool2d類方法來搭建 # 全連接層使用 torch.nn.Linear 類方法來搭建 class Model(torch.nn.Module): def __init__(self): super(Model,self).__init__() self.conv1 = torch.nn.Sequential( torch.nn.Conv2d(1,64,kernel_size=3,stride=1,padding=1), torch.nn.ReLU(), torch.nn.Conv2d(64,128,kernel_size=3,stride=1,padding=1), torch.nn.ReLU(), torch.nn.MaxPool2d(stride=2,kernel_size=2)) self.dense = torch.nn.Sequential( torch.nn.Linear(14*14*128,1024), torch.nn.ReLU(), torch.nn.Dropout(p = 0.5), torch.nn.Linear(1024,10) ) # 我們通過繼承torch.nn.Modeule來構造網絡,因為手寫數字 # 識別比較簡單,我們只是用了兩個卷積層,一個最大池化層,兩個全連接層。 # 在向前傳播過程中進行x.view(-1, 14 * 14 * 128) # 對參數實現扁平化。最后通過自己self.dense定義的全連接層進行最后的分類 def forward(self, x): x = self.conv1(x) x = x.view(-1,14*14*128) x = self.dense(x) return x # 在編寫完搭建卷積神經網絡模型的代碼后,我們可以對模型進行訓練和參數進行優化了 # 首先 定義在訓練之前使用哪種損失函數和優化函數 # 下面定義了計算損失值的損失函數使用的是交叉熵 # 優化函數使用的額是Adam自適應優化算法 model = Model() # 將所有的模型參數移動到GPU上 if torch.cuda.is_available(): model.cuda() cost = torch.nn.CrossEntropyLoss() optimizer = torch.optim.Adam(model.parameters()) # print(model) # 卷積神經網絡模型進行模型訓練和參數優化的代碼 n_epochs = 5 for epoch in range(n_epochs): running_loss = 0.0 running_correct = 0 print("Epoch {}/{}".format(epoch, n_epochs)) print("-"*10) for data in data_loader_train: X_train , y_train = data # 有GPU加下面這行,沒有不用加 # X_train, y_train = X_train.cuda(), y_train.cuda() X_train , y_train = Variable(X_train),Variable(y_train) # print(y_train) outputs = model(X_train) # print(outputs) _,pred = torch.max(outputs.data,1) optimizer.zero_grad() loss = cost(outputs,y_train) loss.backward() optimizer.step() # running_loss += loss.data[0] running_loss += loss.item() running_correct += torch.sum(pred == y_train.data) # print("ok") # print("**************%s"%running_corrrect) print("train ok ") testing_correct = 0 for data in data_loader_test: X_test,y_test = data # 有GPU加下面這行,沒有不用加 # X_test, y_test = X_test.cuda(), y_test.cuda() X_test,y_test = Variable(X_test),Variable(y_test) outputs = model(X_test) _, pred = torch.max(outputs,1) testing_correct += torch.sum(pred == y_test.data) # print(testing_correct) print( "Loss is :{:.4f},Train Accuracy is:{:.4f}%,Test Accuracy is:{:.4f}".format( running_loss / len(data_train),100 * running_correct / len(data_train), 100 * testing_correct / len(data_test))) stop_time = time.time() print("time is %s" %(stop_time-start_time))
上一篇:
下一篇:
未完待續。。。