PyTorch ------NIN(NetworkInNetwork)卷積神經網絡實現mnist手寫體識別

  • 接上一篇 VGG實現mnist手寫識別

  • 使用NIN經典模型實現相同的功能

  • 今天介紹的是NIN(NetworkInNetwork)

  • 原文地址傳送門

  • 正在考慮找個時間把論文翻一下,有同樣想法的小夥伴嗎,一起搞事情

  • 這個模型有兩個創新點:

    • 1、使用MLP Convolution layers
      • 經典的神經網絡模型是堆疊卷積層和池化層,由卷積層線性生成features map 然後通過激活函數完成非線性映射.
      • 公式和示意圖如下:
      • 在這裏插入圖片描述
      • 解釋一下上圖公式中各個變量的含義(i,j)是features map 的像素索引,X(i,j)表示輸入像素單元的索引的中心,k表示features map 的索引
      • 當潛在特徵線性可分時,這種線性卷積足可以抽象,但是想要得到更好的抽象,需要用更加通用的函數來提取特徵,這樣可以儘可能的逼近潛在特徵的表現形式.
      • 在傳統的CNN中可以通過一套完整的濾波器來涵蓋同一特徵的所有變體,但是同一特徵使用太多的濾波器,將會給下一層帶來額外的負擔
      • 使用一個通用函數逼近器做局部特徵的特徵提取,因爲它能逼近潛在特徵的更多抽象表示.
      • 在這裏有兩個選擇一個是徑向基(Radial basis network)和多層感知器(multilayer perceptron),使用多層感知機有兩個原因:首先是因爲多層感知機與神經網絡結構兼容,可以使用反向傳播進行訓練,第二是多層感知機可以是很深的模型
      • 使用MLP代替了GLM,形成MLPConv層
      • 下圖是MLPConv計算公式和示意圖:
      • 在這裏插入圖片描述
  • 在這裏插入圖片描述

    • 當時作者使用1X1卷積核,具有里程碑式的意義,之後的模型都開始關注1X1的卷積核.
  • 2 、(GAP)Global Average Pooling

    • 傳統卷積神經網絡在網絡的淺層進行卷積提取特徵,將由最後一個卷積層得到的features map 輸入到 全連接層,然後由全連接層完成分類任務
    • 但是全連接層參數過多,容易產生過擬合,從而阻礙了模型的泛化能力,作者提出了一種叫做全局平均池化層,由他代替CNN中的全連接層.
    • GAP相比較全連接層的優點:
      • 在於通過增強特徵圖與類比間對應關係使卷積結構保留的更好,
      • 使特徵圖分類是可信的,更具有解釋性
      • GAP沒有優化的參數
      • 可以將GAP視作一種結構化正則化
  • NIN模型結構:

  • 在這裏插入圖片描述

  • 下面看一下NIN在各個數據集的性能

  • 在這裏插入圖片描述
    -在這裏插入圖片描述

  • 在這裏插入圖片描述

  • 下面上代碼:

    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 nin_block(in_channels,out_channels,kernel_size,stride,padding):
    blk = nn.Sequential(
    nn.Conv2d(in_channels,out_channels,kernel_size,stride,padding),
    nn.ReLU(),
    nn.Conv2d(out_channels,out_channels,kernel_size = 1),# 模擬全連接的多層感知機
    nn.ReLU(),
    nn.Conv2d(out_channels,out_channels,kernel_size = 1),
    nn.ReLU()
    )
    return blk
    class GlobalAvgPool2d(nn.Module):
    def init(self):
    super(GlobalAvgPool2d,self).init()
    def forward(self,x):
    return F.avg_pool2d(x,kernel_size = x.size()[2:])
    net = nn.Sequential(
    nin_block(1,96,kernel_size=11,stride=4,padding=0),
    nn.MaxPool2d(kernel_size=3,stride=2),
    nin_block(96,256,kernel_size=5,stride=1,padding=2),
    nn.MaxPool2d(kernel_size=3,stride=2),
    nin_block(256,384,kernel_size=3,stride=1,padding=1),
    nn.MaxPool2d(kernel_size=3,stride=2),
    nn.Dropout(0.5),
    nin_block(384,10,kernel_size=3,stride=1,padding=1),
    GlobalAvgPool2d(),
    FlattenLayer()
    )
    X = torch.rand(1,1,224,224)
    for name,blk in net.named_children():
    X = blk(X)
    print(name,“out shape:”,X.shape)
    print(net)
    #下載數據 組裝好訓練數據 測試數據
    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.002,5
    optimizer = torch.optim.Adam(net.parameters(),lr = lr)
    train_fit(net,train_iter,test_iter,batch_size,optimizer,device,num_epochs)

  • 代碼到此結束

  • 下面在上一個VGG 和NIN的結構對比,增加印象:

  • 先上VGG的網絡結構:

  • 在這裏插入圖片描述
    -下面是NIN結構-
    在這裏插入圖片描述
    -這次真的結束了

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