LeNet-5——CNN經典網絡模型詳解(pytorch實現)

這是一個最簡單也是最基礎的CNN模型,大家可以慢慢看,有不懂地方評論區見~

一、LeNet-5

這個是n多年前就有的一個CNN的經典結構,主要是用於手寫字體的識別,也是剛入門需要學習熟悉的一個網絡。 原論文地址

LeNet-5——CNN經典網絡模型詳解(pytorch實現)

輸入:32*32的手寫字體圖片,這些手寫字體包含0~9數字,也就是相當於10個類別的圖片

輸出:分類結果,0~9之間的一個數

LeNet-5——CNN經典網絡模型詳解(pytorch實現)

因此我們可以知道,這是一個多分類問題,總共有十個類,因此神經網絡的最後輸出層必然是SoftMax問題,然後神經元的個數是10個。LeNet-5結構:

  • 輸入層:32*32的圖片,也就是相當於1024個神經元
  • C1層:paper作者,選擇6個特徵卷積核,然後卷積核大小選擇55,這樣我們可以得到6個特徵圖,然後每個特徵圖的大小為32-5+1=28,也就是神經元的個數為628*28=784。

參考公式

LeNet-5——CNN經典網絡模型詳解(pytorch實現)

  • S2層:這就是下采樣層,也就是使用最大池化進行下采樣,池化的size,選擇(2,2),也就是相當於對C1層2828的圖片,進行分塊,每個塊的大小為22,這樣我們可以得到1414個塊,然後我們統計每個塊中,最大的值作為下采樣的新像素,因此我們可以得到S1結果為:1414大小的圖片,共有6個這樣的圖片。
  • C3層:卷積層,這一層我們選擇卷積核的大小依舊為55,據此我們可以得到新的圖片大小為14-5+1=10,然後我們希望可以得到16張特徵圖。那麼問題來了?這一層是最難理解的,我們知道S2包含:6張14 * 14大小的圖片,我們希望這一層得到的結果是:16張1010的圖片。這16張圖片的每一張,是通過S2的6張圖片進行加權組合得到的,具體是怎麼組合的呢?問題如下圖所示: 為了解釋這個問題,我們先從簡單的開始,我現在假設輸入6特徵圖的大小是55的,分別用6個55的卷積核進行卷積,得到6個卷積結果圖片大小為11,如下圖所示: 為了簡便起見,我這裡先做一些標號的定義:我們假設輸入第i個特徵圖的各個像素值為x1i,x2i……x25i,因為每個特徵圖有25個像素。因此第I個特徵圖經過55的圖片卷積後,得到的卷積結果圖片的像素值Pi可以表示成: 這個是卷積公式因此對於上面的P1 ~ P6的計算方法,這個就是直接根據公式。然後我們把P1~P6相加起來,也就是: P=P1+P2+……P6

把上面的Pi的計算公式,代入上式,那麼我們可以得到:

<code>	P=WX
/<code>

其中X就是輸入的那6張55特徵圖片的各個像素點值,而W就是我們需要學習的參數,也就相當於6個55的卷積核,當然它包含著6*(5*5)個參數。因此我們的輸出特徵圖就是:

<code>Out=f(P+b)
/<code>

這個就是從S2到C3的計算方法,其中b表示偏置項,f為激活函數。

我們迴歸到原來的問題:有6張輸入1414的特徵圖片,我們希望用55的卷積核,然後最後我們希望得到一張10*10的輸出特徵圖片?

根據上面的過程,也就是其實我們用55的卷積核去卷積每一張輸入的特徵圖,當然每張特徵圖的卷積核參數是不一樣的,也就是不共享,因此我們就相當於需要6(55)個參數。對每一張輸入特徵圖進行卷積後,我們得到6張1010,新圖片,這個時候,我們把這6張圖片相加在一起,然後加一個偏置項b,然後用激活函數進行映射,就可以得到一張10*10的輸出特徵圖了。

總之,C3層每個圖片是通過S2圖片進行卷積後,然後相加,並且加上偏置b,最後在進行激活函數映射得到的結果。

  • S4層:下采樣層,比較簡單,也是知己對C3的16張1010的圖片進行最大池化,池化塊的大小為22。因此最後S4層為16張大小為55的圖片。至此我們的神經元個數已經減少為:165*5=400。
  • C5層:我們繼續用5*5的卷積核進行卷積,然後我們希望得到120個特徵圖。這樣C5層圖片的大小為5-5+1=1,也就是相當於1個神經元,120個特徵圖,因此最後只剩下120個神經元了。這個時候,神經元的個數已經夠少的了,後面我們就可以直接利用全連接神經網絡,進行這120個神經元的後續處理,

上面的結構,只是一種參考,在現實使用中,每一層特徵圖需要多少個,卷積核大小選擇,還有池化的時候採樣率要多少,等這些都是變化的,這就是所謂的CNN調參,我們需要學會靈活多變。

代碼部分

採用pytorch官網的數據集

<code>#model.py

import torch.nn as nn
import torch.nn.functional as F

class LeNet(nn.Module):
    def __init__(self):
        super(LeNet,self).__init__()
        self.covn1 = nn.Conv2d(3,16,5)
        self.pool1 = nn.MaxPool2d(2,2)
        self.conv2 = nn.Conv2d(16,32,5)
        self.pool2 = nn.MaxPool2d(2,2)
        self.fc1 = nn.Linear(32*5*5,120)
        self.fc2 = nn.Linear(120,84)
        self.fc3 = nn.Linear(84,10)
    
    def forward(self, x):
        x = F.relu(self.conv1(x))#input(3,32,32) output(16,28,28)
        x = self.pool1(x) #output(16,14,14)
        x = F.relu(self.conv2(x)) #output(32,10.10)
        x = self.pool2(x) #output(32,5,5)
        x = x.view(-1,32*5*5) #output(5*5*32)
        x = F.relu(self.fc1(x)) #output(120)
        x = F.relu(self.fc2(x)) #output(84)
        x = self.fc3(x) #output(10)
        return x

# #model調試
# import torch

# #定義shape
# input1 = torch.rand([32,3,32,32])
# model = LeNet()#實例化
# print(model)
# #輸入網絡中
# output = model(input1)/<code>
LeNet-5——CNN經典網絡模型詳解(pytorch實現)

在這裡插入圖片描述

首先下載數據集

<code>#train.py

import torch
import torchvision
import torch.nn as nn
#from model import LeNet
import torch.optim as optim
import torchvision.transforms as transforms

#預處理函數
transform = transforms.Compose(
    [transforms.ToTensor(),
     transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

# 50000張訓練圖片
train_set = torchvision.datasets.CIFAR10(root='./data', train=True,
                                        download=True, transform=transform))
/<code>

或者在網盤下載 鏈接:https://pan.baidu.com/s/1NBHp0SxEOJ5EIyYUsDHm_g 提取碼:qp3k

先小部分測試一下

<code>#train.py

import torch
import torchvision
import torch.nn as nn
from model import LeNet
import torch.optim as optim
import torchvision.transforms as transforms
from torchvision import transforms, datasets, utils
import matplotlib.pyplot as plt
import numpy as np


#device : GPU or CPU
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print(device)


transform = transforms.Compose(
    [transforms.ToTensor(),
     transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

# 50000張訓練圖片
train_set = torchvision.datasets.CIFAR10(root='./data', train=True,
                                         download=False, transform=transform)
train_loader = torch.utils.data.DataLoader(train_set, batch_size=36,
                                           shuffle=False, num_workers=0)

# 10000張驗證圖片
val_set = torchvision.datasets.CIFAR10(root='./data', train=False,
                                       download=False, transform=transform)
val_loader = torch.utils.data.DataLoader(val_set, batch_size=4,
                                         shuffle=False, num_workers=0)
val_data_iter = iter(val_loader)
val_image, val_label = val_data_iter.next()
print(val_image.size())
print(train_set.class_to_idx)
classes = ('plane', 'car', 'bird', 'cat',
          'deer', 'dog', 'frog', 'horse', 'ship', 'truck')


#顯示圖像,之前需把validate_loader中batch_size改為4
aaa = train_set.class_to_idx
cla_dict = dict((val, key) for key, val in aaa.items())
def imshow(img):
    img = img / 2 + 0.5  # unnormalize
    npimg = img.numpy()
    plt.imshow(np.transpose(npimg, (1, 2, 0)))
    plt.show()

print(' '.join('%5s' % cla_dict[val_label[j].item()] for j in range(4)))
imshow(utils.make_grid(val_image))

/<code>
LeNet-5——CNN經典網絡模型詳解(pytorch實現)

正式測試

<code>#train.py

import torch
import torchvision
import torch.nn as nn
from model import LeNet
import torch.optim as optim
import torchvision.transforms as transforms
from torchvision import transforms, datasets, utils
import matplotlib.pyplot as plt
import numpy as np


#device : GPU or CPU
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print(device)


transform = transforms.Compose(
    [transforms.ToTensor(),
     transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

# 50000張訓練圖片
train_set = torchvision.datasets.CIFAR10(root='./data', train=True,
                                         download=False, transform=transform)
train_loader = torch.utils.data.DataLoader(train_set, batch_size=36,
                                           shuffle=False, num_workers=0)

# 10000張驗證圖片
val_set = torchvision.datasets.CIFAR10(root='./data', train=False,
                                       download=False, transform=transform)
val_loader = torch.utils.data.DataLoader(val_set, batch_size=5000,
                                         shuffle=False, num_workers=0)
val_data_iter = iter(val_loader)
val_image, val_label = val_data_iter.next()
print(val_image.size())
# print(train_set.class_to_idx)
# classes = ('plane', 'car', 'bird', 'cat',
#           'deer', 'dog', 'frog', 'horse', 'ship', 'truck')
#
#
# #顯示圖像,之前需把validate_loader中batch_size改為4
# aaa = train_set.class_to_idx
# cla_dict = dict((val, key) for key, val in aaa.items())
# def imshow(img):
#     img = img / 2 + 0.5  # unnormalize
#     npimg = img.numpy()
#     plt.imshow(np.transpose(npimg, (1, 2, 0)))
#     plt.show()
#
# print(' '.join('%5s' % cla_dict[val_label[j].item()] for j in range(4)))
# imshow(utils.make_grid(val_image))




net = LeNet()
net.to(device)

loss_function = nn.CrossEntropyLoss()
#定義優化器
optimizer = optim.Adam(net.parameters(), lr=0.001)

#訓練過程
for epoch in range(10):  # loop over the dataset multiple times

    running_loss = 0.0 #累加損失
    for step, data in enumerate(train_loader, start=0):
        # get the inputs; data is a list of [inputs, labels]
        inputs, labels = data
        #print(inputs.size(), labels.size())
        # zero the parameter gradients
        optimizer.zero_grad()#如果不清除歷史梯度,就會對計算的歷史梯度進行累加
        # forward + backward + optimize
        outputs = net(inputs.to(device))
        loss = loss_function(outputs, labels.to(device))
        loss.backward()
        optimizer.step()

        # print statistics
        running_loss += loss.item()
        if step % 500 == 499:    # print every 500 mini-batches
            with torch.no_grad():#上下文管理器
                outputs = net(val_image.to(device))  # [batch, 10]
                predict_y = torch.max(outputs, dim=1)[1]
                accuracy = (predict_y == val_label.to(device)).sum().item() / val_label.size(0)

                print('[%d, %5d] train_loss: %.3f  test_accuracy: %.3f' %
                      (epoch + 1, step + 1, running_loss / 500, accuracy))
                running_loss = 0.0

print('Finished Training')

save_path = './Lenet.pth'
torch.save(net.state_dict(), save_path)

/<code>
LeNet-5——CNN經典網絡模型詳解(pytorch實現)

進行預測

<code>#predict.py

import torch
import torchvision.transforms as transforms
from PIL import Image
from model import LeNet
import matplotlib.pyplot as plt
import numpy as np

#預處理
transform = transforms.Compose(
    [transforms.Resize((32, 32)),
     transforms.ToTensor(),
     transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

classes = ('plane', 'car', 'bird', 'cat',
           'deer', 'dog', 'frog', 'horse', 'ship', 'truck')

net = LeNet()
net.load_state_dict(torch.load('Lenet.pth'))

im = Image.open('飛機.jpg')      #測試飛機
#im = Image.open('貓.jpg')         #測試貓
#im = Image.open('輪船.jpg')       #測試狗
#im = Image.open('狗。jpg’)       #測試貓,可能出現錯誤
im1 = transform(im)  # [C, H, W]
im = torch.unsqueeze(im1, dim=0)  # [N, C, H, W]

#顯示圖像
def imshow(img):
    img = img / 2 + 0.5  # unnormalize
    npimg = img.numpy()
    plt.imshow(np.transpose(npimg, (1, 2, 0)))
    plt.show()
imshow(im1)


with torch.no_grad():
    outputs = net(im)
    predict = torch.max(outputs, dim=1)[1].data.numpy()
    predict1 = torch.softmax(outputs, dim=1)
    print(predict1)
    print(classes[int(predict)])

/<code> 
LeNet-5——CNN經典網絡模型詳解(pytorch實現)


分享到:


相關文章: