专栏名称: 算法与数学之美
从生活中挖掘数学之美,在实践中体验算法之奇,魅力旅程,从此开始!
目录
相关文章推荐
九章算法  ·  「九点热评」Meta新员工都是裁员刀下鬼! ·  2 天前  
九章算法  ·  湾区码二代,已经是next level了 ·  2 天前  
九章算法  ·  「九点热评」亚麻员工狂怼CEO被解雇! ·  3 天前  
算法爱好者  ·  普通人如何抓住 DeepSeek ... ·  2 天前  
51好读  ›  专栏  ›  算法与数学之美

手把手教你如何在Kaggle猫狗大战冲到Top2%

算法与数学之美  · 公众号  · 算法  · 2017-03-24 20:12

正文


本文会通过 Keras 搭建一个深度卷积神经网络来识别一张图片是猫还是狗,在验证集上的准确率可以达到99.6%,建议使用显卡来运行该项目。本项目使用的 Keras 版本是1.2.2。

猫狗大战

数据集来自 kaggle 上的一个竞赛: Dogs vs. Cats ,训练集有25000张,猫狗各占一半。测试集12500张,没有标定是猫还是狗。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

➜  猫狗大战 ls train | head

cat.0.jpg

cat.1.jpg

cat.10.jpg

cat.100.jpg

cat.1000.jpg

cat.10000.jpg

cat.10001.jpg

cat.10002.jpg

cat.10003.jpg

cat.10004.jpg

➜  猫狗大战 ls test | head

1.jpg

10.jpg

100.jpg

1000.jpg

10000.jpg

10001.jpg

10002.jpg

10003.jpg

10004.jpg

10005.jpg


下面是训练集的一部分例子:


数据预处理

由于我们的数据集的文件名是以 type.num.jpg 这样的方式命名的,比如 cat.0.jpg ,但是使用 Keras 的 ImageDataGenerator 需要将不同种类的图片分在不同的文件夹中,因此我们需要对数据集进行预处理。这里我们采取的思路是创建符号链接(symbol link),这样的好处是不用复制一遍图片,占用不必要的空间。

我们可以从下面看到文件夹的结构,train2里面有两个文件夹,分别是猫和狗,每个文件夹里是12500张图。

1

2

3

4

5

6

7

8

9

├── test [12500 images]

├── test.zip

├── test2

│   └── test -> ../test/

├── train [25000 images]

├── train.zip

└── train2

├── cat [12500 images]

└── dog [12500 images]


导出特征向量

对于这个题目来说,使用预训练的网络是最好不过的了,经过前期的测试,我们测试了 ResNet50 等不同的网络,但是排名都不高,现在看来只有一两百名的样子,所以我们需要提高我们的模型表现。那么一种有效的方法是综合各个不同的模型,从而得到不错的效果,兼听则明。如果是直接在一个巨大的网络后面加我们的全连接,那么训练10代就需要跑十次巨大的网络,而且我们的卷积层都是不可训练的,那么这个计算就是浪费的。所以我们可以将多个不同的网络输出的特征向量先保存下来,以便后续的训练,这样做的好处是我们一旦保存了特征向量,即使是在普通笔记本上也能轻松训练。


为了复用代码,我觉得写一个函数是非常有必要的,那么我们的函数就需要输入模型,输入图片的大小,以及 预处理函数 ,因为 Xception 和 Inception V3 都需要将数据限定在 (-1, 1) 的范围内,然后我们利用 GlobalAveragePooling2D 将卷积层输出的每个激活图直接求平均值,不然输出的文件会非常大,且容易过拟合。然后我们定义了两个 generator,利用 model.predict_generator 函数来导出特征向量,最后我们选择了 ResNet50, Xception, Inception V3 这三个模型(如果有兴趣也可以导出 VGG 的特征向量)。每个模型导出的时间都挺长的,在 aws p2.xlarge 上大概需要用 十分钟到二十分钟 。 这三个模型都是在 ImageNet 上面预训练过的,所以每一个模型都可以说是身经百战,通过这三个老司机导出的特征向量,可以高度概括一张图片有哪些内容。

最后导出的 h5 文件包括三个 numpy 数组:

  • train (25000, 2048)

  • test (12500, 2048)

  • label (25000,)


如果你不想自己计算特征向量,可以直接在这里下载导出的文件 GitHub releases (https://github.com/ypwhs/dogs_vs_cats/releases/tag/gap) 百度云 (https://pan.baidu.com/s/1pK7psxX#list/path=%2Fdataset%2FDogs%20vs%20Cats)

参考资料:

  • ResNet 15.12 (https://arxiv.org/abs/1512.03385)

  • Inception v3 15.12 (https://arxiv.org/abs/1512.00567)

  • Xception 16.10 (https://arxiv.org/abs/1610.02357)


载入特征向量

经过上面的代码以后,我们获得了三个特征向量文件,分别是:

我们需要载入这些特征向量,并且将它们合成一条特征向量,然后记得把 X 和 y 打乱,不然之后我们设置 validation_split 的时候会出问题。这里设置了 numpy 的随机数种子为2017,这样可以确保每个人跑这个代码,输出都能是一样的结果。

import h5py

import numpy as np

from sklearn.utils import shuffle

np.random.seed(2017)

X_train = []

X_test = []

for filename in ["gap_ResNet50.h5", "gap_Xception.h5", "gap_InceptionV3.h5"]:

 with h5py.File(filename, 'r') as h:

X_train.append(np.array(h['train']))

X_test.append(np.array(h['test']))

y_train = np.array(h['label'])

X_train = np.concatenate(X_train, axis=1)

X_test = np.concatenate(X_test, axis=1)

X_train, y_train = shuffle(X_train, y_train)



构建模型

模型的构建很简单,直接 dropout 然后分类就好了。


我们还可以对模型进行可视化:

1

2

3

4

5

6

7

8

9

10

11

12

13

digraph G{

node [shape=record]

a[label="ResNet50|{input:|output:}|{(224, 224, 3)|(2048)}"]

b[label="InceptionV3|{input:|output:}|{(299, 299, 3)|(2048)}"]

c[label="Xception|{input:|output:}|{(299, 299, 3)|(2048)}"]

Merge[label="Merge|{input:|output:}|{(3, 2048)|(6144)}"]

Dropout[label="Dropout|Rate:|0.5"]

Output[label="Output|{input:|output:}|{(6144)|(1)}"]

Image -> a -> Merge

Image -> b -> Merge

Image -> c -> Merge

Merge -> Dropout -> Output

}



训练模型

模型构件好了以后,我们就可以进行训练了,这里我们设置验证集大小为 20% ,也就是说训练集是20000张图,验证集是5000张图。

1 model.fit(X_train, y_train, batch_size= 128 , nb_epoch= 8 , validation_split= 0.2 )

Train on 20000 samples, validate on 5000 samples

Epoch 1/8

20000/20000 [==============================] - 1s - loss: 0.1193 - acc: 0.9591 - val_loss: 0.0283 - val_acc: 0.9936

Epoch 2/8

20000/20000 [==============================] - 0s - loss: 0.0319 - acc: 0.9898 - val_loss: 0.0181 - val_acc: 0.9952

Epoch 3/8

20000/20000 [==============================] - 0s - loss: 0.0252 - acc: 0.9916 - val_loss: 0.0172 - val_acc: 0.9934

Epoch 4/8

20000/20000 [==============================] - 0s - loss: 0.0214 - acc: 0.9936 - val_loss: 0.0140 - val_acc: 0.9956

Epoch 5/8

20000/20000 [==============================] - 0s - loss: 0.0200 - acc: 0.9926 - val_loss: 0.0139 - val_acc: 0.9954

Epoch 6/8

20000/20000 [==============================] - 0s - loss: 0.0189 - acc: 0.9933 - val_loss: 0.0129 - val_acc: 0.9956







请到「今天看啥」查看全文