DataWhale-天池街景数字识别竞赛-task3-模型初步训练

背景

2020年5月的DW组队学习选择了天池的街景字符编码识别,在这个入门竞赛中,数据集来自Google街景图像中的门牌号数据集(The Street View House Numbers Dataset, SVHN),并根据一定方式采样得到比赛数据集。评测标准为测试集预测结果的准确率,即编码识别正确的数量测试集图片数量的比率。

组队学习的第三个任务是用PyTorch搭建一个简单的CNN模型进行训练,熟悉操作后,后续可以替换为使用预训练的Resnet18进行训练。

本章学习手册内容由 张强 编写,而本篇博客则是这章内容的笔记,在这里对作者表示感谢,受益匪浅!

初始化的几个操作

作者在一开始导入包时,插入了以下几条语句:

torch.cuda.manual_seed(0)
torch.backends.cudnn.deterministic = True
torch.backends.cudnn.benchmark = False

看到这几条语句还是挺迷惑的,后来查阅了官方文档,发现这是为了使结果可复现,而不是每次都变动(具体内容可以查看该文档)。

 In order to make computations deterministic on your specific problem on one specific platform and PyTorch release, there are a couple of steps to take.

You can use torch.manual_seed() to seed the RNG for all devices (both CPU and CUDA)

When running on the CuDNN backend, two further options must be set:

torch.backends.cudnn.deterministic = True
torch.backends.cudnn.benchmark = False

                                                                                                                                  ———— pytorch.org/docs

CNN模型简介 

卷积神经网络(CNN)是一种应用广泛的神经网络,逐渐成为计算机视觉领域的主流模型,在解决图像分类、图像检索、物体检测等问题上有很好的效果。

CNN的网络结构主要分为:

1.卷积层

在卷积层中,将会用许多尺寸较小的卷积核,在整个图像中做“卷积”运算(准确说是互相关),当完成整个图像的运算后,每个卷积核都会生成一个二维的激活图,他们可能会针对某些特征产生激活,如物体边界、颜色、轮廓等。

每个卷积层上可能会有一系列的卷积核,比如20个,那么就会形成20张二维的激活图,这些图层叠起来就形成了卷积层的输出。

2.池化层

池化层又称作下采样层(downsampling),顾名思义就是对图像进行下采样,从而减小图像的尺寸,这样可以大大降低卷积层的运算时间。池化层通常在卷积层之后加入,其中池化操作常用的算法有最大池化、平均池化等等。

3.全连接层

全连接层一般放在网络的最后,经过了一系列的卷积层和池化层之后,提取出图片的特征图,此时图像大小已经缩小到一定尺寸,此时就可以用一个全连接层完成最后的输出。为了防止过拟合,还会引入Dropout机制,又或者在进入全连接层之前使用全局平均池化

另外,激活函数可以增加模型的非线性性,所以卷积层之后通常引入一个激活函数,如ReLU

搭建CNN

首先,可以搭建一个简单的CNN模型试一下,手册提供的CNN共有两层卷积,分别包含16个和32个的3*3卷积核,激活函数选用ReLU( ),池化层窗口大小为2(即2*2),选用最大池化,最后搭配6个并联的全连接层,分别表示定长字符的6个位置。

具体代码如下:

class SVHN_Model1(nn.Module):
    def __init__(self):
        super(SVHN_Model1, self).__init__()

        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):
        fout = self.cnn(img)
        fout = fout.view(fout.shape[0], -1)
        c1 = self.fc1(fout)
        c2 = self.fc2(fout)
        c3 = self.fc3(fout)
        c4 = self.fc4(fout)
        c5 = self.fc5(fout)
        c6 = self.fc6(fout)
        return c1, c2, c3, c4, c5, c6

使用预训练模型

为了使用更加高效的模型,可以直接使用一些现成的模型,pytorch中也集成了不少出名的CNN模型,如Alexnet、Resnet等等,具体可以看官方文档。设置pretrained=True还可以下载已经训练好的参数,在此基础上做一个简单的迁移学习。

手册中使用了Resnet18,并修改了最后的全连接层,具体代码如下:

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.fc0 = nn.Linear(512, 11)
        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)
        c0 = self.fc0(feat)
        c1 = self.fc1(feat)
        c2 = self.fc2(feat)
        c3 = self.fc3(feat)
        c4 = self.fc4(feat)
        c5 = self.fc5(feat)
        return c0, c1, c2, c3, c4, c5

开始训练

设定好几个参数,以及优化算法、损失函数后,就可以开始训练了:

lr = 0.005
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr)

loss_plot, c0_plot = [], []

for epoch in range(10):
    for data in train_loader:
        if torch.cuda.is_available():
            data[0] = data[0].cuda()
            data[1] = data[1].cuda().long()
        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)

大致结果

在本机实验下,第一个模型跑一个epoch大概需要155s,第二个模型跑大概需要490s

在精度方面,经过10个epochs,第一个模型在第一个字符的准确率约为0.55,第二个模型因为运行时间太长,目前跑了两个epoch,第一个字符准确率仍在0.27左右,效果还不是很好。

耗时问题

在运行的过程中,有一个突出的问题是dataloader的预处理时间特别长,占总训练时间的80%左右,这说明在数据读取方面还需要优化。关于耗时的检测,可以参考这篇教程。主要运用了cProfile库同时搭配pstats、snakeviz查看结果。

关键代码如下:

import cProfile as cpro
# 放入你要运行的函数
cpro.run("train(model, train_loader)", filename='result.out')

import pstats
from pstats import SortKey
p = pstats.Stats('result.out')
p.strip_dirs().sort_stats(SortKey.TIME).print_stats(10)

结果如下:

关于cProfile的更多操作,可以参考这个网站

具体原因可能是由于机械硬盘读写慢,windows下不能多线程读数据集,dataloader内部构造优化不足等等造成,还需要进一步思考怎么解决这个问题。

另外,在训练中可能会出现显卡利用率低等情况,出现这些情况怎么处理,这里推荐一篇不错的博客:

深度学习PyTorch,TensorFlow中GPU利用率较低,CPU利用率很低,且模型训练速度很慢的问题总结与分析

最后

此次学习的教程由Datawhale提供,学习手册的链接为:点这里

CNN简介的部分内容参考廖星宇的《深度学习入门之PyTorch》

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
天池是一个著名的数据科学竞赛平台,而datawhale是一家致力于数据科学教育和社群建设的组织。街景字符编码识别是指通过计算机视觉技术,对街道场景中的字符进行自动识别和分类。 街景字符编码识别是一项重要的研究领域,对于提高交通安全、城市管理和智能驾驶技术都具有重要意义。街道场景中的字符包括道路标志、车牌号码、店铺招牌等。通过对这些字符进行准确的识别,可以辅助交通管理人员进行交通监管、道路规划和交通流量分析。同时,在智能驾驶领域,街景字符编码识别也是一项关键技术,可以帮助自动驾驶系统准确地识别和理解道路上的各种标志和标识,为自动驾驶提供可靠的环境感知能力。 天池datawhale联合举办街景字符编码识别竞赛,旨在吸引全球数据科学和计算机视觉领域的优秀人才,集思广益,共同推动该领域的研究和发展。通过这个竞赛,参赛选手可以使用各种机器学习和深度学习算法,基于提供的街景字符数据集,设计和训练模型,实现准确的字符编码识别。这个竞赛不仅有助于促进算法研发和技术创新,也为各参赛选手提供了一个学习、交流和展示自己技能的平台。 总之,天池datawhale街景字符编码识别是一个具有挑战性和实际应用需求的竞赛项目,旨在推动计算机视觉和智能交通领域的技术发展,同时也为数据科学爱好者提供了一个学习和展示自己能力的机会。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值