这一节主要开始构建一个字符识别模型,基于赛题理解,本章将构建一个定长多字符分类模型
工欲善其事必先利其器,首先来了解下pytorch与TensorFlow。(本比赛主要用到pytorch框架用于解决这个问题)
PyTorch基本结构
pytorch主要分为以下几个模块来训练模型:
- tensor:tensor为基本结构,可以直接创建,从list创建以及由numpy数组得到,torch还提供一套运算以及shape变换的方法
- Variable:自动求导机制,利用Variable包括tensor后,便可以使用其求导的功能了,有点像装饰器
- nn:nn模块是整个pytorch的核心,自己设计的NEt(),继承nn.Model后可以提取模型参数,进行前向forward()运算(自己设计的),以及后向运算(自动),nn提供基本网络结构单元,例如nn.Linear(),nn.Conv2d()等,还提供了基本损失函数nn.CrossEntropyLoss等
- torch.optim:该模块提供自动求导更新参数等功能,用它来封装模型参数nn.parameter()后,loss求导后,可以用.step来更新整个参数。
- torch.utils.data.DataSet:该模块提供加载数据初始化的方式,需要实现完善__getitem__()和__len__()的接口,便可以使用DataLoader多进程加载数据。
参考:
Tensorflow 处理结构及基本应用
处理结构
Tensorflow的设计理念主要体现在两个方面
- 将图定义和图运算完全分开
- TensorFlow 中涉及的运算都要放在图中,而图的运行只发生在会话(session)中。开启会话后,就可以用数据去填充节点,进行运算;关闭会话后,就不能进行计算了。因此,会话提供了操作运行和 Tensor 求值的环境。
Tensorflow 首先要定义神经网络的结构,然后再把数据放入结构种运算和training
基本应用
- 使用图(graph)来计算任务
- 在被称为会话(Session)的上下文(context)执行图
- 使用tensor表示数据
- 通过变量(Variable)维护状态
- 使用feed和fetch可以为任意的操作(arbitrary operation)赋值或从其中获取数据
参考:https://www.jianshu.com/p/2b06b5630852,TensorFlow入门教程
CNN
卷积神经网络(CNN)每一层由众多的卷积核组成,每个卷积核对输入的像素进行卷积操作,得到下一次的输入。随着网络层的增加卷积核会逐渐扩大感受野,并缩减图像的尺寸。
输入图片经过卷积后所得特征图大小的计算公式
输入图片大小:W x W
Filter大小:F x F
步长:S
padding的像素数:P
输出图片大小N x N ,计算公式:
N = (W-F + 2P)/S + 1
参考:
CNN 是一种层次模型,输入的是原始的图像的像素数据。CNN通过卷积(converlution)、池化(pooling)、非线性激活函数(non-linear activation function)和全连接层(fully connected layer)构成。
如下所示,是非常经典的字符识别模型--LeNet网络结构。它包括两个卷积层,两个池化层,两个全连接层。卷积核都为5 x 5,stride= 1,池化层使用最大池化。
通过多次卷积和池化,CNN的最后一层将输入的图像像素映射为具体的输出。如在分类任务中会转换成不同类别的概率输出,然后计算真实标签与CNN模型的预测结果的差异,并通过反向传播更新每层的参数,并在更新完成后再次前向全波,如此反复直到训练完成。
比较典型的网络结构
LeNet--5(1998)
AlexNet(2012)
VGG-16(2014)
Inception-v1(2014)
ResNet-50(2015)
PyTorch构建CNN模型
在pytorch中构建CNN模型只需要定义好模型的参数和正向传播,pytorch会根据正向传播自动计算反向传播
本模型包括两层卷积层,最后并联6个全连接层用于分类。
导入模块
import os,sys,glob,shutil,json
import cv2
from PIL import Image
import numpy as np
from tqdm import tqdm,tqdm_notebook
import torch
import torchvision.datasets as datasets
from torchvision import transforms
import torchvision.models as models
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.nn = 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.nn(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()
对super()的理解:(参考:https://www.runoob.com/python/python-func-super.html)
super()函数用于调用父类(超类)的一个方法,它用于解决多重继承的问题。python3.x和python2.x的一个区别就是:python3可以直接使用super().xxx 代替super(class,self).xxx举例:
torch.nn.Sequential()(参考:https://pytorch-cn.readthedocs.io/zh/latest/package_references/torch-nn/#torchnn)
torch.nn.Sequential(*args):是一个时序容器,Models会以传入的顺序被添加到容器中。举例:
关于卷积后图像尺寸大小的计算,可参考前面的公式,以及举例链接:https://www.jianshu.com/p/45a26d278473
模型的训练
#损失函数
criterion = nn.CrossEntropyLoss()
#优化器
optimizer = optim.Adam(model.parameters(),0.005)
loss_plot,c0_plot = [],[]
#迭代10次
for epoch in range(10):
for input,target in train_loader:
c0,c1,c2,c3,c4,c5 = model(data[0])
loss = criterion(c0,data[1].long()[:,0])+\
criterion(c1,data[1].long()[:,1])+\
criterion(c2,data[1].long()[:,2])+\
criterion(c3,data[1].long()[:,3])+\
criterion(c4,data[1].long()[:,4])
# criterion(c5,data[1].long()[:,5])
loss /=6
optimizer.zero_grad()
loss.backward()
optimizer.step()
loss_plot.append(loss.item())
c0_plot.append((c0.argmax(1) == data[1][:,1]).sum().item()*1.0/c0.shape[0])
print(epoch)
plot 画的的loss以及c0_plot不知道是训练次数少的缘故,似乎得到的结果有点不对劲
也可以使用pytorch提供的模型(在ImageNet数据集上的预训练模型)
class SVHN_Model2(nn.Module):
def __init__(self):
super(SVHN_Model2,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)
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
对children()的理解
返回当前模型 子模块的迭代器