PyTorch ------VGG卷積神經網絡實現mnist手寫體識別

-接上一篇AlexNet現實mnist手寫識別

  • 使用經典模型VGGNet模型實現相同的功能
  • 先簡單介紹一下,今天的主角VGGNet,曾獲得2014年ImageNet亞軍
  • 原論文地址傳送門
  • 上圖看看VGGNet系列模型的結構:
  • 在這裏插入圖片描述
  • 下面是參數個數比較
    在這裏插入圖片描述
  • 先看看VGGNet內部的比較
  • A和A-LRN的比較,只有一個LRN的差異,在經過多次的訓練比較發現,LRN增加了複雜度,損耗內存,存在的意義不是很大
  • B與C相比較多使用了幾個1X1的卷積層,在輸入輸出維度不變的情況下,增加非線性變換,提高網絡的表達能力.
  • C和D相比較還是C的特徵提取會更好一些,雖然1X1的卷積很好很有效,但是相比較於3X3的,還是3X3的會更好一些
  • 其他的幾個相比較,模型越來越深
  • 可訓練參數的個數相差不大

VGG對於AlexNet的優勢

  • 通過重複使用簡單的Block塊來構建深度模型
  • 使用了更小的卷積核
  • 小池化核。AlexNet的3x3的池化核,VGG全部爲2x2的池化核
  • 模型深度更深,表達能力更強
  • 由於使用更小的卷積核來構建和構建更深的網絡,由多個小的卷積核堆疊來增大accept field(3個33的卷積核的堆疊accept field 和 77的accept field 一樣大小)
  • 由於使用小的卷積核,降低了可訓練參數個數,並增加了更多的非線性變化,提高了對特徵的學習能力
  • accept field 示意圖:
  • 在這裏插入圖片描述

下面是VGG11的代碼:

import time
import torch
from torch import nn, optim
import torchvision
import numpy as np
import sys
import os
import torch.nn.functional as F
device = torch.device(“cuda” if torch.cuda.is_available() else “cpu”)
class FlattenLayer(torch.nn.Module):
def init(self):
super(FlattenLayer, self).init()
def forward(self, x): # x shape: (batch, , , …)
return x.view(x.shape[0], -1)
def vgg_block(num_convs,in_channels,out_channels):
blk = []
for i in range(num_convs):
if i == 0: 、blk.append(nn.Conv2d(in_channels=in_channels,out_channels=out_channels,kernel_size=3,padding=1))
else: blk.append(nn.Conv2d(out_channels,out_channels,kernel_size=3,padding=1))
blk.append(nn.ReLU())
blk.append(nn.MaxPool2d(kernel_size=2,stride=2))
return nn.Sequential(blk)
con_arch = ((1,1,64),(1,64,128),(2,128,256),(2,256,512),(2,512,512))
fc_features = 512
7
7 #C
W*H
fc_hidden_units = 4096 # 任意修改
def vgg(conv_arch,fc_features,fc_hidden_units = 4096):
net = nn.Sequential()
for i ,(num_convs,in_channels,out_channels)in enumerate(conv_arch):
net.add_module(“vgg_block” + str(i + 1),vgg_block(num_convs,in_channels,out_channels))
net.add_module(“fc”,nn.Sequential(FlattenLayer(),
nn.Linear(fc_features,fc_hidden_units),
nn.ReLU(),
nn.Dropout(0.5),
nn.Linear(fc_hidden_units,fc_hidden_units),
nn.ReLU(),
nn.Dropout(0.5),
nn.Linear(fc_hidden_units,10)
))
return net
small_conv_arch = [(1, 1, 64), (1, 64, 128), (2, 128, 256),(2, 256, 512), (2, 512, 512)]
net = vgg(small_conv_arch, fc_features, fc_hidden_units)
#輸出模型結構
print(net)
X = torch.rand(1,1,224,224)
#模型每層的features map
for name ,blk in net.named_children():
X = blk(X)
print(name,“output shape :”,X.shape)
#下載數據 組裝好訓練數據 測試數據
def load_data_fashion_mnist(batch_size,resize = None,root = “./dataset/input/FashionMNIST2065”):
trans = []
if resize:
# 做數據增強 處理 將圖片轉化爲 規定大小 數據內容不會丟失 等比例 處理
trans.append(torchvision.transforms.Resize(size=resize))
# 將 圖片 類型 轉化爲Tensor類型
trans.append(torchvision.transforms.ToTensor())
# 將圖片 增強方式 添加到Compose 類中處理
transform = torchvision.transforms.Compose(trans)
# 讀取訓練數據
mnist_train = torchvision.datasets.FashionMNIST(root=root,train=True,download=False,transform = transform)
# 讀取 測試數據
mnist_test = torchvision.datasets.FashionMNIST(root = root,train=False,download=False,transform = transform)
# 數據加載器 在訓練 測試階段 使用多線程按批採樣數據 默認不使用多線程 num_worker 表示設置的線程數量
train_iter = torch.utils.data.DataLoader(mnist_train,batch_size = batch_size,shuffle = True,num_workers = 2)
test_iter = torch.utils.data.DataLoader(mnist_test,batch_size = batch_size,shuffle = False,num_workers = 2)
return train_iter,test_iter
batch_size = 64
#如出現“out of memory”的報錯信息,可減小batch_size或resize
train_iter,test_iter = load_data_fashion_mnist(batch_size,224)
#計算準確率
def evaluate_accuracy(data_iter,net,device = torch.device(“cpu”)):
#創建 正確率 和 總個數
acc_sum ,n = torch.tensor([0],dtype=torch.float32,device=device),0
for X,y in data_iter:
# 適配 設備
X,y = X.to(device),y.to(device)
# 設置 驗證模式
net.eval()
with torch.no_grad(): #隔離開 不要計算在計算圖內
y = y.long()#在這裏將y轉成long確實是不必要的。但是在計算交叉熵時,Pytorch強制要求y是long
acc_sum += torch.sum((torch.argmax(net(X),dim=1) == y)) # 累計預測正確的個數
n += y.shape[0] # 累計總的標籤個數
return acc_sum.item() / n
def train_fit(net,train_iter,test_iter,batch_size,optimizer,device,num_epochs):
# 將讀取的數據 拷貝到 指定的GPU上
net = net.to(device)
print("tainning on ",device)
# 設置 損失函數 交叉熵損失函數
loss = torch.nn.CrossEntropyLoss()
# 設置訓練次數
for epoch in range(num_epochs):
train_l_sum,train_acc_sum,n,batch_count,start = 0.0,0.0,0,0,time.time()
# 讀取批量數據 進行訓練
for X,y in train_iter:
X = X.to(device)
y = y.to(device)
# 訓練結果
y_hat = net(X)
# 計算 預測與標籤分佈 差異
l = loss(y_hat,y)
# 優化函數 梯度置爲零
# 1、因爲梯度可以累加
# 2、每批採樣的梯度不同,只需記錄本次樣本的梯度
optimizer.zero_grad()
# 反向求導
l.backward()
# 更新權重參數
optimizer.step()
train_l_sum += l.cpu().item()
#train_acc_sum += (torch.argmax(y_hat,dim = 1) == y).cpu().item()
# 將張量元素值累計
train_acc_sum += (y_hat.argmax(dim=1) == y).sum().cpu().item()
n += y.shape[0]
batch_count += 1
test_acc = evaluate_accuracy(test_iter,net)
print(‘epoch %d, loss %.4f, train acc %.3f, test acc %.3f, time %.1f sec’
% (epoch + 1, train_l_sum / batch_count, train_acc_sum / n, test_acc, time.time() - start))
lr , num_epochs = 0.01,5
optimizer = torch.optim.Adam(net.parameters(),lr = lr)
train_fit(net,train_iter,test_iter,batch_size,optimizer,device=device,num_epochs=num_epochs)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章