Lenet5 論文學習,特點,代碼實現

目錄

 

LeNet5 論文

I Introduction

II 卷積神經網絡在文字識別中的應用

A 卷積神經網絡

B LeNet-5

III 實驗結果及與其他分類方法 比較

​    

二、特點

三、代碼實現


LeNet5 論文

論文:Gradient-Based Learning Applied to Document Recognition

          Lecun Y, Bottou L. Gradient-based learning applied to document recognition[J]. Proceedings of the                    IEEE,1998,86(11):2278-2324.

I Introduction

     在過去的幾年中, 機器學習技術, 特別是在神經網絡中的應用, 在模式識別系統的設計中發揮着越來越重要的作用。事實上, 可以說, 學習技術的可用性是近年來模式識別應用 (如連續語音識別和手寫識別) 成功的關鍵因素。

     以文檔理解爲個案研究, 我們展示了通過手工集成單獨設計的模塊來構建識別系統的傳統方法, 可以用統一的、原則性好的設計範式代替, 稱爲圖形轉換器網絡, 允許訓練所有模塊以優化全局性能標準。

     在本研究中, 我們考慮了手寫體字符識別的任務(第一和第二部分), 並將幾種學習技術在基準數據集上的性能與手寫數字識別(第三部分)進行了比較。雖然更多的自動學習是有益的, 沒有學習技術可以成功, 而沒有少量的先驗知識的任務。在多層神經網絡的情況下, 融合知識的一個好方法是將其結構調整爲任務。第二部分中介紹的卷積神經網絡是專門的神經網絡體系結構的一個例子, 它通過使用局部連接模式和對權重施加約束, 將2D 形狀的不變性知識結合起來。第三部分對幾種獨立手寫體數字識別方法進行了比較。從對個別字符的識別到對文檔中單詞和句子的識別。第四五六部分提出了幾種問題的解決方法,現在已經不被使用。

II 卷積神經網絡在文字識別中的應用

      使用梯度下降法的多層網絡可以從大量的數據中學習複雜的,高緯,非線性的映射,這使得他們成爲圖像識別任務的首選。

      對於字符識別,可以將圖像作爲行向量作爲輸入輸入到網絡中。雖然這些任務(比如字符識別)可以使用傳統的前向全連接網絡完成。但是還存在一些問題:

  1. 沒有結構的網絡的主要缺點是,多於圖像或者音頻這些應用來說,不具備平移,形變扭曲的不變性。在輸入到固定大小輸入的網絡前,字符圖像的大小必須歸一化,並且放在輸入的中間,不幸的是,沒有哪種預處理能夠達到如此完美。在下面描述的卷積神經網絡中,位移不變性(shift invariance)可以通過權值共享實現。
  2. 全連接的網絡的另一個缺點就是完全忽略了輸入的拓撲結構。在不影響訓練的結果的情況下,輸入圖像可以是任意的順序。然而,圖像具有很強的二維局部結構:空間相鄰的像素具有高度相關性。局部相關性對於提取局部特徵來說具有巨大優勢,因爲相鄰像素的權值可以分成幾類。CNN通過將隱藏結點的感受野限制在局部來提取特徵。

A 卷積神經網絡

      CNN通過局部感受野(local receptive fields),權值共享(shared weights),下采樣(sub-sampling)實現位移,縮放,和形變的不變性(shift,scale,distortion invariance)。

       每一層的每個神經元(each unit)接受上一層中一組局部領域的神經元的輸入(就是局部感受野)。利用圖像的局部相關性

       我們可以將局部感受野位於圖像不同位置的一組神經元設置爲相同的權值(這就是權值共享)。減少了參數數量

       在特徵圖中降低特徵位置的精度的方式是降低特徵圖的空間分辨率,這個可以通過下采樣層達到,下采樣層通過求局部平均降低特徵圖的分辨率,並且降低了輸出對平移和形變的敏感度。卷積層和下采樣層是交替出現的,這種形式形成一個金字塔:每一層,特徵圖的分辨率逐漸減低,而特徵圖的數量逐漸增加。降低特徵位置的精度

B LeNet-5

    

          LeNet-5共有7層,不包含輸入,輸入圖像爲32*32大小的灰度圖像

          C1卷積層,這一層的輸入就是原始的圖像,輸入層接受圖片的輸入大小爲32*32*1。卷積層的核(過濾器)尺寸爲5*5,深度爲6,不使用0進行填充,步長爲1。卷積層總共的參數有5*5*1*6+6 =156個參數

          S2池化層。本層的輸入是C1層的輸出,它接受一個28*28*6的節點矩陣。所使用的核大小爲2*2,長和寬的步長都是2,6*2 = 12個參數。

           C3卷積層,輸入是一個14*14*6的矩陣,C3層一共有16卷積核,每一個卷積核的大小爲5*5,輸出是10*10*16。

           S4池化層, S4層的池化方式與S2層相同,輸入是10*10*16,輸出是5*5*16。

           F6全連接層,使用Sigmoid函數

           輸出層,是由歐式徑向基函數(RBF)組成。每一個輸出對應一個RBF函數,每一個RBF函數都有84維的輸入向量,RBF的函數公式如下。每一個RBF函數都會有一個輸出,最後輸出層會輸出一個10維的向量。

III 實驗結果及與其他分類方法 比較

   

二、特點

LeNet5特徵能夠總結爲如下幾點:
    1)卷積神經網絡使用三個層作爲一個系列: 卷積,池化,非線性
    2)使用卷積提取空間特徵
    3)使用映射到空間均值下采樣(subsample) 
    4)雙曲線(tanh)或S型(sigmoid)形式的非線性
    6)層與層之間的稀疏連接矩陣避免大的計算成本

三、代碼實現

Lenet5 的pytorch代碼實現如下:

import torch
import os
import time
from torch.utils.data import DataLoader
from torchvision.datasets import MNIST
from torchvision import transforms
from torch import optim
import torch.nn as nn
from torchvision.datasets import ImageFolder
from torchvision import transforms as T
from com.product.selftraining.zk import ZkUtil
from torchsummary import summary
from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
import json;
import cv2;
from PIL import Image;

# MINIST數據集
def loadMNIST():
    data_train = MNIST('./data/mnist',
                       download=True,
                       transform=transforms.Compose([
                           transforms.Resize((32, 32)),
                           transforms.ToTensor()]))
    # print(data_train.classes);
    size= len(data_train.class_to_idx);
    # print(data_train.class_to_idx);
    data_test = MNIST('./data/mnist',
                      download=True,
                      train=False,
                      transform=transforms.Compose([
                          # transforms.Resize((32, 32)),
                          transforms.ToTensor()]))
    # 並行處理數據
    data_train_loader = DataLoader(data_train, batch_size=256, shuffle=True, num_workers=8)
    data_test_loader = DataLoader(data_train, batch_size=1024, num_workers=8)
    return data_train, data_train, data_train_loader, data_test_loader

# Load data,從ImageFolder加載數據
def loadImage(batch_size, trainSetPath, testSetPath):
    transform = transforms.Compose([
        transforms.Resize((32, 32)),
        transforms.Grayscale(),
        transforms.ToTensor(),
    ]);

    trainset = ImageFolder(trainSetPath, transform=transform)
    testset = ImageFolder(testSetPath, transform=transform)
    size = len(trainset.class_to_idx);
    # device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
    trainloader = DataLoader(trainset, batch_size=4, shuffle=True, num_workers=10)
    testloader = DataLoader(testset, batch_size=4, shuffle=False, num_workers=10)
    return trainset, testset, trainloader, testloader


# build network
class LeNet(nn.Module):
    size = 0;
    def __init__(self,size):
        super(LeNet, self).__init__()
        self.size = size;
        self.conv = nn.Sequential(
            # stride 步長  padding填充
            # (n+2*padding-f)/stride+1 向下取整
            # 輸入32*32*1
            # 卷積層,輸入32*32*1,輸出28*28*6
            nn.Conv2d(1, 6, kernel_size=(5,5), stride=1),
            # 激活函數
            nn.ReLU(),
            # 池化層,輸入28*28*6,輸出14*14*6
            nn.MaxPool2d(kernel_size=(2, 2),stride=2),
            # 卷積層,輸入14 * 14 * 6,輸出10*10*16
            nn.Conv2d(6, 16, kernel_size=(5,5), stride=1),
            # 激活函數
            nn.ReLU(),
            # 池化層,輸入10*10*16,輸出5*5*16
            nn.MaxPool2d(kernel_size=(2, 2),stride=2),
            # 卷積層,輸入5*5*16,輸出1*1*120
            nn.Conv2d(16, 120, kernel_size=(5,5), stride=1),
            nn.ReLU()
        )
        self.fc = nn.Sequential(
            # 卷積層,輸入120,輸出84
            nn.Linear(120, 84),
            nn.ReLU(),
            # 卷積層,輸入84,輸出2
            nn.Linear(84, self.size),
            nn.Sigmoid(),
        )
    def forward(self, x):
        # 卷積運算
        out = self.conv(x)
        # Convert multidimensional data to 2D data
        out = out.view(out.size(0), -1)
        # 全連接運算
        out = self.fc(out)
        return out


def LeNetTrain():
    learning_rate = 1e-2
    batch_size = 199
    epoches = 50
    try:
    # trainset, testset, trainloader, testloader = loadMNIST(batch_size,trainSetPath,testSetPath)  # data
        start = time.time();
        trainset, testset, trainloader, testloader = loadMNIST();
        lenet = LeNet(len(trainset.class_to_idx))  # network
        print(summary(lenet,(1,32,32)));
        criterian = nn.CrossEntropyLoss(reduction='sum')
        for i in range(epoches):
            optimizer = optim.SGD(lenet.parameters(), lr=learning_rate)
            learning_rate= learning_rate*0.95
            running_loss = 0.
            running_acc = 0.
            for (img, label) in trainloader:
                optimizer.zero_grad()  #
                output = lenet(img)
                loss = criterian(output, label)
                loss.backward()
                optimizer.step()
                running_loss += loss.item()
                valu, predict = torch.max(output, 1)
                correct_num = (predict == label).sum()
                running_acc += correct_num.item()
            running_loss /= len(trainset)
            running_acc /= len(trainset)
            print("[%d/%d] Loss: %.5f, Acc: %.2f" % (i + 1, epoches, running_loss,
                                                     100 * running_acc));
        end = time.time();
        print(end-start);
        modelUrl = path + "/" + realName + ".pth";
        # only save the parameter of model
        torch.save(lenet, modelUrl);
    except(BaseException):
        print(BaseException);


if __name__ == '__main__':
     LeNetTrain()

 實現了兩種加載訓練集的方式,MINIST數據集下載較慢,可以自行下載數據集,之後更改配置文件加載本地數據集:

本地加載mnist數據集的方法

ImageFolder使用:ImageFolder

 

 

 

 

 

 

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