前言
最近学了Google的深度学习库,TensorFlow
,这个库是真的强大,当然还有更高的封装,keras
之类的,但是既然一直用着TensorFlow,就想去找个好玩的东西试试,然后看到lintcode
上有一个猫狗的分类比赛,就去试试吧。做了一个周末加几天,大概做出来了,因为做深度学习需要大量的计算资源,电脑是真的跑不起来(CPU版TensorFlow),这里是我的代码实现VGG16-tranfer,所以我用了Google的免费GPU资源进行计算Colaboratory,Colaboratory 是一个 Google 研究项目,旨在帮助传播机器学习培训和研究成果。它是一个 Jupyter 笔记本环境,不需要进行任何设置就可以使用,并且完全在云端运行。然后数据集和模型通过和Google Drive进行数据交互得到,别的不说,确实用GPU算的很快,无论是训练的时候还是验证的时候。
Colaboratory
分配的计算资源
据说可以免费的使用Tesla k80 GPU加速,除了一些简单的debug在本地测试一下,我都想全部丢在上面跑了。。。
初步思路
之前只做过mnist数据集的训练(神经网络级别的hello world),mnist数据集的宽高是28x28x1,1代表了这个图是一通道的,所以计算量很小,我们要用什么网络实现呢,我一开始选的LetNet
,并且重头开始训练(现在看来真是天真的不行)
LetNet网络图示
因为数据集是一个有20000张带label的图片,一旦输入数据处理的不对,对内存和显存都有着很大的压力,直接使用PIL
把图像转成矩阵或者使用OpenCV(但是我觉得前期处理可以)转化应该是不行的了,然后接触到了TFRecord这种TensorFlow
特有的数据集格式,我会另外写一篇来介绍Tfrecord,第一次转化的时候就被坑死了。。
一开始考虑到训练的难度,没有打算直接输入3通道图片进去训练,因为有着之前一个写好的一个LetNet
,在单矩阵上做卷积,一开始的图片处理打算是全部压缩成灰度图,然后输入CNN里面进行训练,之后进行训练的时候(本机CPU训练),差点弄死我了,内存算上压缩的高达60GB+(Liunx会有swap区),我已经听到我的SSD在尖叫了。。。
TensorFlow
压缩灰度图操作
1 | # img_data读取的图片数据 |
初次实验改良搭的图,TensorBoard显示
在图上就可以看出,FC层非常硕大,这是因为全连接的神经网络参数众多,训练起来非常庞大,卷积神经网络CNN诞生的其中一个目标就是缩减参数,但是在一开始的时候我只搭了大概3层卷积层(后来使用VGG16甚至有着13层卷积层),然后在FC层诞生了一个几十万参数的矩阵进行乘法的操作。。。然后内存就爆了(那时候还在用CPU),通过性能可视化可以看到内存变化,tf.RunMetadata()
,然后又加了两层卷积层,然后训练还是很难,然后就打算重构了。
这个代码可以在Github
的LetNet_error
找到
Tfrecord首坑
在打包train数据集的时候,写错了几行代码,导致跑了一个晚自习,后来发现一台卡死的电脑和一个高达80G的tfrecord文件
(´-﹏-`;)
本来正常操作应该是这样的(伪代码)
1 | def _bytes_feature(value): |
但是我读取之后使用了jpeg解码。。。把解码后的数据读进去了,然后几何倍数般的文件大小增长。
1 | img_data = tf.image.decode_jpeg(image_raw_data) |
VGG16 迁移学习
最后一直在思考如何使用高正确率的网络(之前的网络能跑是能跑,但是正确率有点低,训练集都这样了,拿到测试集不完蛋了),后来想到了之前的CTPN
提取feature map
时候使用的是VGG16,然后就去了解了VGG16,顺便了解了迁移学习,经过一段时间的了解,明白了迁移学习在TensorFlow
里的主要实现机制,在TensorFlow
里进行梯度下降之类的优化操作(反向传播BP)是使用优化器(optimizer),而这个优化器优化的都是TensorFlow
里的变量tf.Variable
,我们把需要优化的参数使用变量,固定的参数使用常量之类的就好了,这样就很简单了。
经过很多层卷积层和池化层之后,参数到FC分类层的时候确实变得很少了,而且迁移学习仅仅是训练后面几个层而已,一开始是仅仅训练最后的FC层,在数据集上获得了大概70的正确率。
大概图(VGG主体)(tensorboard):
理论图:
之后为了提高正确率,采用了数据提升的手段,这样我们的模型将看不到任何两张完全相同的图片,这有利于我们抑制过拟合,使得模型的泛化能力更好。(来自keras文档),在这里我使用了图片翻转、调整图片色彩(亮度、对比度之类的)。
1 | # 随机调整图像的色彩 |
L2正则化、dropout这些实现就不赘述了。
验证test
我们训练网络之后,选择一个正确率较高的模型固化成ckpt
格式,然后在转化成pb
格式的模型以供使用,pb
模型提供输入就可以得到输出了,比ckpt
好用,在验证的时候也有诸多问题,比如机器配置不够。。。到最后测试集都是分开两次进行验证的,一次性读入进行验证对内存显存的占用的太可怕了,到了最后也只能转为TFrecord
文件来读取,而且还分了两次,因为一次性无法全部验证,就在遍历TFrecord
文件上跳了坑。。。
TFrecord验证集神坑
在使用测试集的tfrecord
的时候,读取数据使用了队列+tf.train.batch
,就跟训练的时候一样。我想如果我一次batch读取50个,遍历一次应该用100次就能遍历完了(测试集5000张图),但是。。这个读取方法会有重复数据读取,而不会抛出tf.errors.OutOfRangeError
错误,在后来使用pandas
对结果排序的时候,发现在100次里面是随机读取的数据,读取的数据重复了3次,只能去找不重复的遍历方法,后来在一篇文章发现了将TFrecord
的数据转成迭代器使用的方法数据集的使用方法,主要实现:
1 | def parse(record): |
这样就可以遍历整个TFrecord
了,这个东西真的坑死我了。
后记
这次比赛是真的认识了很多东西,主要都是TensorFlow
的实现,然后就是完全熟练的使用Colaboratory
,这个东西确实对于没有计算资源的学习者有很大的帮助啊。深深认识到没有计算资源基本玩不了深度学习。。。。