街景字符编码识别-Task3:字符识别模型
3.1 学习目标
- 学习CNN基础和原理
- 使用Pytorch框架构建CNN模型,并完成训练
3.2 CNN简介
-
卷积神经网络(简称CNN)是一类特殊的人工神经网络,是深度学习中重要的一个分支。CNN在很多领域都表现优异,精度和速度比传统计算学习算法高很多。特别是在计算机视觉领域,CNN是解决图像分类、图像检索、物体检测和语义分割的主流模型。
-
CNN每一层由众多的卷积核组成,每个卷积核对输入的像素进行卷积操作,得到下一次的输入。随着网络层的增加卷积核会逐渐扩大感受野,并缩减图像的尺寸。
-
CNN是一种层次模型,输入的是原始的像素数据。CNN通过卷积(convolution)、池化(pooling)、非线性激活函数(non-linear activation function)和全连接层(fully connected layer)构成。
-
经典的LeNet模型结构如下:
3.4 Pytorch构建CNN模型
本次重点学习如何用Pytorch搭建一个简单的CNN网络
- 在Pytorch中构建CNN模型非常简单,只需要定义好模型的参数和正向传播即可,Pytorch会根据正向传播自动计算反向传播。
- 下面将讲解如何搭建一个多输出的CNN网络
- 注:卷积计算公式如下 N = (W − F + 2P )/S+1 (下取整)
- w为原边长 F为卷积核大小 P为填充大小 S为步长 N为输出大小
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__()
# torch.nn.Sequential是一个Sequential容器,模块将按照构造函数中传递的顺序添加到模块中。优点是不用挨个给每一层起变量名,并且写forward也是很简单的事情,不用一层一层地去运算了。
#也可以构造一个空容器然后向里面添加网络
#self.layer1 = torch.nn.Sequential()
#self.layer1.add_module('conv1', torch.nn.Conv2d(3, 32, 3, 1, padding=1))//输入3维,输出32维,输出大小32*32
#size(n,3,64,128) 输入
self.cnn = nn.Sequential(
nn.Conv2d(3, 16, kernel_size=(3, 3), stride=(2, 2)),#size(n,16,31,63)
nn.ReLU(),
nn.MaxPool2d(2),#size(n,16,15,31)
nn.Conv2d(16, 32, kernel_size=(3, 3), stride=(2, 2)),#size(n,32,7,15)
nn.ReLU(),
nn.MaxPool2d(2),
)#szie(n,32,3,7)
#构建6个并列的全连接层,用于多输出
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)
# 如果有保存的参数模型,则加载模型,并在其基础上继续训练
#log_dir = 'epoch10.pth'
#if os.path.exists(log_dir):
# checkpoint = torch.load(log_dir)
# model.load_state_dict(checkpoint['model'])
# optimizer.load_state_dict(checkpoint['optimizer'])
# start_epoch = checkpoint['epoch']
# print('加载 epoch {} 成功!'.format(start_epoch))
#可视化训练损失
def show_curve(ys, title):
x = np.array(range(len(ys)))
y = np.array(ys)
plt.plot(x, y, c='b')
plt.axis()
plt.title('{} curve'.format(title))
plt.xlabel('epoch')
plt.ylabel('{}'.format(title))
plt.show()
# 迭代10个Epoch
for epoch in range(1, 11):
print(epoch)
total_loss = 0
for i, (images, targets) in enumerate(train_loader):
# targets = torch.LongTensor(targets)
c0, c1, c2, c3, c4, c5 = model(images)
targets = targets.long()
loss = criterion(c0, targets[:, 0]) + \
criterion(c1, targets[:, 1]) + \
criterion(c2, targets[:, 2]) + \
criterion(c3, targets[:, 3]) + \
criterion(c4, targets[:, 4]) + \
criterion(c5, targets[:, 5])
loss /= 6
optimizer.zero_grad()
loss.backward()#向后传播
optimizer.step()
total_loss += loss.item()
# loss_plot.append(loss.item())
if (i + 1) % 100 == 0:
print("Step [{}/{}] Train Loss: {:.4f}"
.format(i + 1, len(train_loader), loss.item()))
print('loss':total_loss/len(train_loader))
total_list.append(total_loss/len(train_loader))
#模型参数的保存
state = {'model': model.state_dict(), 'optimizer': optimizer.state_dict(), 'epoch': epoch}
torch.save(state, './epoch{}.pth'.format(epoch))
show_curve(total_list, 'train loss')
用Task2中封装好的数据训练10轮,结果如下: