宠物狗图片分类之迁移学习代码笔记

正文共3152个字,预计阅读时间8分钟。

本文主要是总结之前零零散散抽出时间做的百度西交大狗狗图片分类竞赛题目 竞赛.目前本人已经彻底排到了50名后面,,,也没有想到什么办法去调优,并且平时也忙没时间再继续做.权且记录下来一些过程和心得。

综合说明

代码已经全部上传到github上,地址为:github code.大家下载的时候喜欢的话可以star下哈.

data目录没有上传,这里简单说下目录结构.与src同级的data目录下有train_data2和test1两个目录.分别是训练集和官方测试集.train_data2下是99个目录(原本是100种狗,后来发现有两种其实是一样的只是打标签打的不一样了,官方的回应是就这样了也不修改了,后续我测试发现将这两种合并下效果会略微好一点),每个目录下存放有图片.

训练过程我是将所有训练图片约20000张读入程序,而后分割出5%作为测试集.完成模型训练后去预测test1里图片的标签进而上传到官网评测.官方以错误率为评价指标.

目前本代码测试单模型最好约能到20.09,进行一些模型投票后最好到19.08.然后就再也升不上去了...

不保证运行本代码一定能得到上述我所说的我的最好结果.毕竟模型初始化啊,迭代轮数都会有影响.

主要是将代码撸一遍分析下为自己做笔记.

代码记录分析

main()函数

1if __name__=="__main__":
2classnum = 99
3X,Y = getdata.get('../data/train_data2/')
4X_train, X_test, Y_train, Y_test = train_test_split(X, Y,train_size=0.95, random_state=79)
5Y_train = makeonehot(Y_train,classnum)
6Y_test = makeonehot(Y_test,classnum) train(X_train,Y_train,X_test,Y_test,depoch=35,ftepoch=50,batch_size=32,classnum=classnum,out='inception.model')

首先是获取数据.使用的是getdata.py里的函数.其主要操作就是读取所有图片并将图片和标签分别存储到X,Y里去.这个没啥好说的,我代码写的比较乱,但是该有的注释还是有的,嗯。

而后使用了sklearn里的train_test_split进行了测试集合和训练集合的分割.这里取了5%作为测试集,其实是比较少的.一般来说我们做实验是至少要取20%作为测试集的,并且一般还要进行交叉验证来进行超参数选择,不过我这里都没有,原因一是我时间不多,二是跑一次程序就得一天多,很多超参数其实是我凭经验设置的,交叉验证实在是跑不起。

而后对标签进行了one_hot处理.这个keras里好像没有像tensorflow里那个sparse_crossentropy_loss的东东啊.有的话告诉我下呗。

然后就去训练了.设置了先depoch个35轮,而后ftepoch个50轮,batch_size为32张,保持的模型前缀叫inception.model等等。

train()函数

 1def train(X_train,Y_train,X_test,Y_test,depoch=50,ftepoch=201,batch_size=32,classnum=100,out='inceptionv3-ft.model'):
 2"""Use transfer learning and fine-tuning to train a network on a new dataset"""
 3nb_train_samples = len(Y_train)
 4nb_classes = classnum
 5nb_val_samples = len(Y_test)
 6batch_size = batch_size
 7# data prep
 8train_datagen =  ImageDataGenerator(
 9preprocessing_function=preprocess_input,
10rotation_range=30,#角度
11width_shift_range=0.2,#水平偏移
12height_shift_range=0.2,#高度偏移
13shear_range=0.2,#剪切强度,逆时针方向的剪切变化角度
14zoom_range=0.2,#随机缩放的幅度
15horizontal_flip=True,#进行随机水平反转
16vertical_flip=False#进行竖直反转
17)
18train_generator = train_datagen.flow(X_train, Y_train, batch_size=batch_size, seed=42)
19X_test = preprocess_input(X_test)
20# setup model
21base_model = InceptionV3(weights='imagenet', include_top=False) #include_top=False excludes final FC layer
22model = add_new_last_layer(base_model, nb_classes)
23# transfer learning
24setup_to_transfer_learn(model, base_model)
25for i in range(depoch):
26print('Epoch: ',i)
27model.fit_generator(train_generator,epochs=1,
28            steps_per_epoch =int(nb_train_samples/batch_size),
29            class_weight='auto',workers=30,max_q_size=100)
30score1, acc1 = model.evaluate(X_test, Y_test,batch_size=batch_size)
31print('epoch: ',i,'eval_acc: ',acc1)
32# fine-tuning
33setup_to_finetune(model)
34for i in range(ftepoch):
35print('Epoch: ',i)
36model.fit_generator(train_generator,epochs=1,
37            steps_per_epoch = int(nb_train_samples/batch_size),
38            class_weight='auto',workers=30,max_q_size=100)
39score1, acc1 = model.evaluate(X_test, Y_test, batch_size=batch_size)
40print('epoch: ',i,'eval_acc: ',acc1)
41if i%10 == 0 and i !=0:
42model.save(out+str(i))
43score, acc = model.evaluate(X_test, Y_test,batch_size=batch_size)
44print('now accu:',acc)
45print('ALL DONE')

首先是统计一些参数.

Keras的图片数据增强

train_datagen是训练数据生成器.这个属于是keras特有的福利了应该.我们做图像分类的时候一定要进行的一个步骤就是所谓的数据增强,也就是对原图片进行反转翻转切割放缩等变换来扩充训练数据集.而keras里则自带了这样一个增强工具.我们只需要设置好数据增强的各个参数,然后使用flow函数将原数据传入,这个生成器就会源源不断的产生从原数据增强出的数据.这样的话我们训练的时候就可以一直从这里面取出数据来作为训练集。

下面这个小坑是我自己实验总结来的,没有看keras源码,所以可能有错误,烦请大家一定提出来。

说到这里有一个小坑有必要提一下,也就是在tarin_generator=train_datagen.flow()时,传入的总数据的个数最好是能够和batch_size能够整除的,不这样做也可以,我先说说这样做的原因然后如果不这样做后续如何做大家自然就知道了。

因为datagen在生成数据时,其实是先将数据shuffle之后,挨个去取batch_size大小,直到取完这一轮或者datagen重置之后才会重新对数据shuffle而后再对数据遍历.比如说数据一共20个,batch_size设置为9,那么开始会取出2个9,在下一轮时因为只剩下2个样本而batch_size为9就会报错。

所以最好是整除关系(可以通过合理设置batch_size或者去除一部分数据使得能够整除这样的方式),那么如果不这么做呢.这里的代码其实就是一个例子.后续在使用train_generator是在这里:

1model.fit_generator(train_generator,epochs=1,
2            steps_per_epoch = int(nb_train_samples/batch_size),
3            class_weight='auto',workers=30,max_q_size=100)

这里没有报错,我个人感觉一是里面的steps_per_epoch计算的结果的作用是相当于把数据最后不够batch_size的给忽略掉不要了,二是可能fit_generator在每个epoch时会对里面的train_generator做类似重置这样的工作.

迁移学习模型导入及训练

接着说代码。

因为这里使用的迁移学习,也就是使用了预训练模型InceptionV3.InceptionV3是有自己的输入数据预处理方式的,所以这里对x_test也就是测试数据做了下预处理.其实上面的train_datagen里也要有这个预处理过程.并且IncepV3图片输入维度是299X299X3所以传入的X的大小也要匹配。

而后就是导入IncepV3的预训练模型.这里设置weights='imagenet'也就是说导入的模型有训练好的权重,并且这个权重是从imagenet里训练得到的.include_top=False指的是不包含最后的全连接输出层,这个是肯定是因为imagenet是1000分类而我们这里是99分类,我们只使用InceptionV3的前面部分而最后的全连接层我们自己来补充.也就是通过add_new_last_layer()这个函数,如下:

 1def add_new_last_layer(base_model, nb_classes):
 2"""
 3迁移学习
 4"""
 5x = base_model.output
 6x = GlobalAveragePooling2D()(x)
 7x = Dense(FC_SIZE)(x) #new FC layer, random init
 8x = BatchNormalization()(x)
 9x = Activation('relu')(x)
10x = Dropout(0.3)(x)
11predictions = Dense(nb_classes, activation='softmax')(x) #new softmax layer
12model = Model(inputs=base_model.input, outputs=predictions)
13return model

这里需要注意的是我使用了BN和dropout,并且dropout设置为0.3可以说是比较小,这个属于我自己实验调参感觉这样比较好。

而后就需要先训练我们刚才加的那几层了.这是迁移学习里的一个技巧,也就是我们会先冻结网络一部分去训练另外一部分,这里因为整个网络前面是InceptionV3的预训练权重,我们认为应该不错,而后面是我们自己加的层是随机初始化的,那么需要将训练几轮把新加的这几层权重也训练的差不多才行.这里我们冻结model的前面所有层,如下:

1def setup_to_transfer_learn(model, base_model):
2for layer in base_model.layers:
3layer.trainable = False
4model.compile(optimizer='rmsprop',loss='categorical_crossentropy', metrics=['accuracy'])

然后就是训练过程了.至于训练多少轮这个看实验结果,我这里训练20-40轮都差不多,一般来说对于复杂的问题只训练这最后两层是很难达到高的准确度的,甚至训练集的准确度也提高不了多少,这其实就是欠拟合了,因为模型可调整的参数太少了,无法拟合出这么复杂的问题.不过如果是简单的问题的话有有可能只训练后面加的这几层就行了。

finetune

那么后面就是finetune了,这也是迁移学习里最重要的一步.顾名思义就是一点一点去拟合.一般我们是要看下整个网络结构,而后从后往前开始,先放开一部分层,冻结前面的,使用数据来训练后面的这几层,好了之后再往前放开一部分层,再训练,如此这样直到达到预期的目标。

我这里只进行了一次fintune,因为我发现就这一次就已经很明显的会过拟合了(训练集准确度很高而测试集准确度不升)。

1def setup_to_finetune(model):
2for layer in model.layers[:NB_IV3_LAYERS_TO_FREEZE]:
3layer.trainable = False
4for layer in model.layers[NB_IV3_LAYERS_TO_FREEZE:]:
5layer.trainable = True
6model.compile(optimizer=SGD(lr=0.0001, momentum=0.9),loss='categorical_crossentropy', metrics=['accuracy'])

这里从参数看是冻结了前172层而后续的是放开了.至于这个172是如何得知一般我们是要看下Inception的结构,其结构一般是分层次的,每个层次包含多少多少层,通常我们倾向于以它的这个结构上的层次为划分一步一步去解冻去finetune。

然后就还是训练过程了.这里每10轮保存下模型.主要是为了后续的投票机制.

结尾

到这里基本就完了.想要提高模型效果的话就是不断的调参和上trick去测试了。

有一个技巧很多提到就是使用centerloss,这个原理的话可以参考我一篇博客centerloss.不过目前我只在tensorflow上写这个centerloss,keras好像封装的很深我目前没有太深入了解keras还真实现不出来...那篇博客可以是一个原理性的解释和说明这样的。

原文链接:https://www.jianshu.com/p/3fbc6f6a36cc

原文发布于微信公众号 - 人工智能LeadAI(atleadai)

原文发表时间:2018-05-11

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Small Code

sklearn中Logistics Regression的coef_和intercept_的具体意义

使用sklearn库可以很方便的实现各种基本的机器学习算法,例如今天说的逻辑斯谛回归(Logistic Regression),我在实现完之后,可能陷入代码太久...

41160
来自专栏磐创AI技术团队的专栏

ChatGirl 一个基于 TensorFlow Seq2Seq 模型的聊天机器人

简介 ? 还在开发中,它工作的效果还不好。但是你可以直接训练,并且运行。 包含预处理过的 twitter 英文数据集,训练,运行,工具代码,可以运行但是效果有待...

47580
来自专栏人工智能LeadAI

解析Tensorflow官方PTB模型的demo

01 seq2seq代码案例解读 RNN 模型作为一个可以学习时间序列的模型被认为是深度学习中比较重要的一类模型。在Tensorflow的官方教程中,有两个与...

52280
来自专栏专知

【专知国庆特刊-PyTorch手把手深度学习教程系列01】一文带你入门优雅的PyTorch

【导读】主题链路知识是我们专知的核心功能之一,为用户提供AI领域系统性的知识学习服务,一站式学习人工智能的知识,包含人工智能( 机器学习、自然语言处理、计算机视...

1.2K80
来自专栏PaddlePaddle

【RNN】使用RNN语言模型生成文本

导语 PaddlePaddle提供了丰富的运算单元,帮助大家以模块化的方式构建起千变万化的深度学习模型来解决不同的应用问题。这里,我们针对常见的机器学习任务,提...

57960
来自专栏Python爬虫实战

图片转字符画

字符画是一系列字符的组合,我们可以把字符看作是比较大块的像素,一个字符能表现一种颜色(暂且这么理解吧),字符的种类越多,可以表现的颜色也越多,图片也会更有层次感...

23020
来自专栏专知

【前沿】TensorFlow Pytorch Keras代码实现深度学习大神Hinton NIPS2017 Capsule论文

【导读】10月26日,深度学习元老Hinton的NIPS2017 Capsule论文《Dynamic Routing Between Capsules》终于在a...

43460
来自专栏玉树芝兰

如何用 Python 和循环神经网络(RNN)做中文文本分类?

本文为你展示,如何使用 fasttext 词嵌入预训练模型和循环神经网络(RNN), 在 Keras 深度学习框架上对中文评论信息进行情感分类。

23440
来自专栏腾讯Bugly的专栏

机器学习入门之HelloWorld(Tensorflow)

1 环境搭建 (Windows) 安装虚拟环境 Anaconda,方便python包管理和环境隔离。 Anaconda3 4.2 http://mirrors...

48880
来自专栏AI科技评论

开发 | Facebook开源 PyTorch版 fairseq,准确性最高、速度比循环神经网络快9倍

AI科技评论按:今年5月,FacebookAI研究院(FAIR)发表了他们的研究成果fairseq,在fairseq中,他们使用了一种新型的卷积神经网络来做语言...

36890

扫码关注云+社区

领取腾讯云代金券