字符識別模型搭建專題 - 定長多字符分類模型
我們的基礎網絡會常用CNN(即卷積神經網絡)來實現
所以我們先來了解一下CNN的原理
簡介:卷積神經網絡(簡稱CNN)是一類特殊的人工神經網絡,是深度學習中重要的一個分支。CNN在很多領域都表現優異,精度和速度比傳統計算學習算法高很多。特別是在計算機視覺領域,CNN是解決圖像分類、圖像檢索、物體檢測和語義分割的主流模型。
學習手冊裏提到了四個深度學習中比較核心的概念,分別是卷積和池化、非線性激活函數和全連接,下面會展開介紹。
CNN作爲一個典型的層次模型,爲後面大部分的計算機視覺方向的模型奠定了堅實的基礎。
- 卷積
相關的概念
- 感受野:感受野(Receptive Field)的定義是卷積神經網絡每一層輸出的特徵圖(feature map)上的像素點在輸入圖片上映射的區域大小。再通俗點的解釋是,特徵圖上的一個點對應輸入圖上的區域。
- 步長:即我們卷積核在輸入特徵圖上每一步移動的大小。
- padding:由於我們通過卷積會使得輸出的圖片相對於原始的圖片變小(1*1卷積除外),所以我們爲了保持與原始圖像大小一致一般會選擇使用padding的方法,即在圖像的周圍進行擴展。
有圖的解釋:感受野,stride和padding
在日常使用中我們會使用不同大小的卷積核來使得改變一次卷積過程中感受野的大小
或許會採用一些比較獨特的卷積方式諸如:空洞卷積
2. 池化
池化(Pooling)是卷積神經網絡中另一個重要的概念,它實際上是一種形式的降採樣。
一般常見的池化方法有最大值池化和平均值池化
直覺上,這種機制能夠有效地原因在於,在發現一個特徵之後,它的精確位置遠不及它和其他特徵的相對位置的關係重要。池化層會不斷地減小數據的空間大小,因此參數的數量和計算量也會下降,這在一定程度上也控制了過擬合
3.非線性激活函數
爲了提高整個網絡的非線性能力而出現,現在常見的非線性激活函數有三類
一般認爲是sigmod、tanh和RELU
4.全連接層
即當前層的每一個節點都和上一層節點之間有連接。
示例給了一個最早的用在手寫字符識別的minist數據集上的LeNet的結構
然後枚舉了後續的發展歷程
第一次在ImageNet挑戰賽上大放異彩,引領計算機視覺新一代浪潮的AlexNet以及由劍橋大學提出的VGG
以及大名鼎鼎的InceptionV3和ResNet等
有時間我會在自己的csdn博客更新相關模型的解讀
一般來說,現有的深度學習架構都會提供一個自動求導的功能,方便了我們模型搭建的過程
不用去手動計算反向傳播的過程,這裏我們選擇使用pytorch來進行
相關的框架還有如Amazon的mxnet還有Google的TensorFlow
我們構建一個非常簡單的CNN,然後進行訓練。這個CNN模型包括兩個卷積層,最後並聯6個全連接層進行分類
import torch
torch.manual_seed(0)
torch.backends.cudnn.deterministic = False
torch.backends.cudnn.benchmark = True
import torchvision.models as models
import torchvision.transforms as transforms
import torchvision.datasets as datasets
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.autograd import Variable
from torch.utils.data.dataset import Dataset
# 定義模型
class SVHN_Model1(nn.Module):
def __init__(self):
super(SVHN_Model1, self).__init__()
# CNN提取特徵模塊
self.cnn = nn.Sequential(
nn.Conv2d(3, 16, kernel_size=(3, 3), stride=(2, 2)),
nn.ReLU(),
nn.MaxPool2d(2),
nn.Conv2d(16, 32, kernel_size=(3, 3), stride=(2, 2)),
nn.ReLU(),
nn.MaxPool2d(2),
)
#
self.fc1 = nn.Linear(32*3*7, 11)
self.fc2 = nn.Linear(32*3*7, 11)
self.fc3 = nn.Linear(32*3*7, 11)
self.fc4 = nn.Linear(32*3*7, 11)
self.fc5 = nn.Linear(32*3*7, 11)
self.fc6 = nn.Linear(32*3*7, 11)
def forward(self, img):
feat = self.cnn(img)
feat = feat.view(feat.shape[0], -1)
c1 = self.fc1(feat)
c2 = self.fc2(feat)
c3 = self.fc3(feat)
c4 = self.fc4(feat)
c5 = self.fc5(feat)
c6 = self.fc6(feat)
return c1, c2, c3, c4, c5, c6
model = SVHN_Model1()
# 以下爲訓練代碼
# 損失函數
criterion = nn.CrossEntropyLoss()
# 優化器
optimizer = torch.optim.Adam(model.parameters(), 0.005)
loss_plot, c0_plot = [], []
# 迭代10個Epoch
for epoch in range(10):
for data in train_loader:
c0, c1, c2, c3, c4, c5 = model(data[0])
loss = criterion(c0, data[1][:, 0]) + \
criterion(c1, data[1][:, 1]) + \
criterion(c2, data[1][:, 2]) + \
criterion(c3, data[1][:, 3]) + \
criterion(c4, data[1][:, 4]) + \
criterion(c5, data[1][:, 5])
loss /= 6
optimizer.zero_grad()
loss.backward()
optimizer.step()
loss_plot.append(loss.item())
c0_plot.append((c0.argmax(1) == data[1][:, 0]).sum().item()*1.0 / c0.shape[0])
print(epoch)
指導書也提供了在ImageNet上來進行嘗試的代碼,後續繼續學習