零基础入门CV之街道字符识别(三)
卷积神经网络
卷积神经网络是一种具有局部连接,权值共享等特性的深层前馈神经网络。
用全连接前馈网络来处理图像时,会存在参数太多,导致训练效率低和局部不变性特征等问题。卷积神经网络是怎样解决这些问题的?
局部连接。每个神经元不再和上一层的所有神经元相连,而只和一小部分的神经元相连,这样就减小了很多参数。
权值共享。一组连接可以共享同一个权重,而不是每个连接有一个不同的权重,这样极大的减小了参数数量。
下采样 使用Pooling来减少每层的样本数,进一步减少参数数量,同时还可以提高模型的鲁棒性(鲁棒性指一个系统或组织有抵御或克服不利条件的能力)。
卷积神经网络是受生物学上感受野机制的启发提出的。感受野主要是指神经元只接受其所支配的刺激区域内的信号。一个神经元的感受野是指视网膜上的特定区域,只有这个区域内的刺激才能够激活该神经元。
目前的卷积神经网络一般是由卷积层、汇聚层和全连接层交叉堆叠而成的前馈神经网络,使用反向传播算法进行训练。
卷积神经网络有三个结构上的特性:局部连接、权重共享以及汇聚。这些特性使得卷积神经网络具有一定程度上的平移、缩放和旋转不变性。和前馈神经网络相比,卷积神经网络的参数更少。对于图像识别任务来说,卷积神经网络通过尽可能保留重要的参数,去掉大量不重要的参数,来达到更好的学习效果。
卷积神经网络常用的架构模式是N个卷积层叠加,然后叠加一个Pooling层,重复这个结构M次,最后叠加K个全连接层。
通过多次卷积和池化,CNN的最后一层将输入的图像像素映射为具体的输出。如在分类任务中会转换为不同类别的概率输出,然后计算真实标签与CNN模型的预测结果的差异,并通过反向传播更新每层的参数,并在更新完成后再次前向传播,如此反复直到训练完成 。
与传统机器学习模型相比,CNN具有一种端到端(End to End)的思路。在CNN训练的过程中是直接从图像像素到最终的输出,并不涉及到具体的特征提取和构建模型的过程,也不需要人工的参与。
Pytorch构建CNN模型
在上一章节我们讲解了如何使用Pytorch来读取赛题数据集,本节我们使用本章学习到的知识构件一个简单的CNN模型,完成字符识别功能。
在Pytorch中构建CNN模型非常简单,只需要定义好模型的参数和正向传播即可,Pytorch会根据正向传播自动计算反向传播。
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数据集上的预训练模型,具体方法如下:
class SVHN_Model2(nn.Module): def __init__(self): super(SVHN_Model1, self).__init__() model_conv = models.resnet18(pretrained=True) model_conv.avgpool = nn.AdaptiveAvgPool2d(1) model_conv = nn.Sequential(*list(model_conv.children())[:-1]) self.cnn = model_conv self.fc1 = nn.Linear(512, 11) self.fc2 = nn.Linear(512, 11) self.fc3 = nn.Linear(512, 11) self.fc4 = nn.Linear(512, 11) self.fc5 = nn.Linear(512, 11) def forward(self, img): feat = self.cnn(img) # print(feat.shape) 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) return c1, c2, c3, c4, c5
- 零基础入门CV之街道字符识别(四)
- java语言基础入门——使用键盘输入字符功能
- 手写字符识别入门学习记录(1)
- linux学习入门 基础部分(6)【文件属性,目录属性,更改组,权限识别,默认权限,特殊权限,权限列表】
- 零基础入门机器学习:如何识别一只猫?
- Keras入门-搭建BP网络识别手写字符
- OpenCV+Python车牌字符分割和识别入门
- Linux驱动入门(一)字符设备驱动基础
- MySQ基础入门系列之——字符与日期数据处理
- tesseract_ocr 字符识别基础及训练字库、合并字库
- MatConvNet使用入门之车牌数字字符识别
- 0基础入门语音与音频识别
- 模式识别零基础入门
- tesseract_ocr 字符识别基础及训练字库、合并字库
- 关于字符数组定义和越界使用的基础入门小贴士
- (转)零基础入门--中文命名实体识别
- tesseract_ocr 字符识别基础及训练字库、合并字库
- 学习笔记(13):Python零基础轻松从入门到实战-字符和字符串-3
- Java入门:基础算法之从字符串中找到重复的字符
- 【新手入门】课程4-深度学习入门CV-手写数字识别