首先小小的庆祝一下自己的微信公众号订阅用户已达到100人了,是小小的虚荣心也是收获也是鞭策,希望自己砥砺前行,努力进步,做到更好。

其实写文章,比较难的是对知识的沉淀,这个沉淀有多种,代码的沉淀,一件事物理解上的沉淀,甚至整合代码和文章的沉淀,要考虑他人的感受,也要在自己的能力范围内进行控制等等不一而足。

本笔记是经典的猫狗识别问题,大概25000的训练集图片加上12500的测试集图片,猫狗图片各一半,共约700多M,而之前的手写数字也不过10M多一些,对计算机处理而言都是比较耗资源的,何况这个实验的目的本来就是从小样本提升准确率。从实质上这些模型和前面的模型没有本质区别,唯一的区别是ImageDataGenerator的应用,能够把图片直接转换为浮点数向量。

关于深度学习系列笔记十二(关于猫狗判断实验)_数据

可视化训练集和验证集的损失率和准确率

可以看出随着训练轮次的增加,

训练集的准确率呈对数级上升,而验证集的准确率则在第十轮左右维持在72%的准确率

训练集的损失度呈对数级下降,而验证集的损失度则在第十轮左右最低,此后不断上升

因此本例子主要还是过度拟合导致,根本原因是样本数量不足,只有2000训练集样本

在增加dropout层以后,训练准确率较之前有所下降,但验证准确率较之前有所提升达到75%。

在利用数据增强生成器训练卷积神经网络后,训练集和验证集的准确率基本是同步的,最高上升到78%


代码示例


  1. import os, shutil

  2. def initdata():

  3. #原始数据集解压目录的两个路径

  4. original_dataset_train_dir = 'D:/Python36/Coding/PycharmProjects/ttt/dataset/train'

  5. original_dataset_test_dir = 'D:/Python36/Coding/PycharmProjects/ttt/dataset/test'

  6. #保存较小数据集的目录

  7. base_dir = 'D:/Python36/Coding/PycharmProjects/ttt/dataset/small'

  8. os.mkdir(base_dir)

  9. #分别对应划分后的训练、验证和测试的目录

  10. train_dir = os.path.join(base_dir, 'train')

  11. os.mkdir(train_dir)

  12. validation_dir = os.path.join(base_dir, 'validation')

  13. os.mkdir(validation_dir)

  14. test_dir = os.path.join(base_dir, 'test')

  15. os.mkdir(test_dir)

  16. #猫的训练图像目录

  17. train_cats_dir = os.path.join(train_dir, 'cats')

  18. os.mkdir(train_cats_dir)

  19. #狗的训练图像目录

  20. train_dogs_dir = os.path.join(train_dir, 'dogs')

  21. os.mkdir(train_dogs_dir)

  22. #猫的验证图像目录

  23. validation_cats_dir = os.path.join(validation_dir, 'cats')

  24. os.mkdir(validation_cats_dir)

  25. #狗的验证图像目录

  26. validation_dogs_dir = os.path.join(validation_dir, 'dogs')

  27. os.mkdir(validation_dogs_dir)

  28. #猫的测试图像目录

  29. test_cats_dir = os.path.join(test_dir, 'cats')

  30. os.mkdir(test_cats_dir)

  31. #狗的测试图像目录

  32. test_dogs_dir = os.path.join(test_dir, 'dogs')

  33. os.mkdir(test_dogs_dir)

  34. #将前1000 张猫的图像复制到train_cats_dir

  35. fnames = ['cat.{}.jpg'.format(i) for i in range(1000)]

  36. for fname in fnames:

  37. src = os.path.join(original_dataset_train_dir, fname)

  38. dst = os.path.join(train_cats_dir, fname)

  39. shutil.copyfile(src, dst)

  40. #将接下来500张猫的图像复制到validation_cats_dir

  41. fnames = ['cat.{}.jpg'.format(i) for i in range(1000, 1500)]

  42. for fname in fnames:

  43. src = os.path.join(original_dataset_train_dir, fname)

  44. dst = os.path.join(validation_cats_dir, fname)

  45. shutil.copyfile(src, dst)

  46. #将接下来的500 张猫的图像复制到test_cats_dir

  47. fnames = ['cat.{}.jpg'.format(i) for i in range(1500, 2000)]

  48. for fname in fnames:

  49. src = os.path.join(original_dataset_train_dir, fname)

  50. dst = os.path.join(test_cats_dir, fname)

  51. shutil.copyfile(src, dst)

  52. #将前1000 张狗的图像复制到train_dogs_dir

  53. fnames = ['dog.{}.jpg'.format(i) for i in range(1000)]

  54. for fname in fnames:

  55. src = os.path.join(original_dataset_train_dir, fname)

  56. dst = os.path.join(train_dogs_dir, fname)

  57. shutil.copyfile(src, dst)

  58. #将接下来500张狗的图像复制到validation_dogs_dir

  59. fnames = ['dog.{}.jpg'.format(i) for i in range(1000, 1500)]

  60. for fname in fnames:

  61. src = os.path.join(original_dataset_train_dir, fname)

  62. dst = os.path.join(validation_dogs_dir, fname)

  63. shutil.copyfile(src, dst)

  64. #将接下来的500 张狗的图像复制到test_dogs_dir

  65. fnames = ['dog.{}.jpg'.format(i) for i in range(1500, 2000)]

  66. for fname in fnames:

  67. src = os.path.join(original_dataset_train_dir, fname)

  68. dst = os.path.join(test_dogs_dir, fname)

  69. shutil.copyfile(src, dst)

  70. #检查一下,看看每个分组(训练/ 验证/ 测试)中分别包含多少张图像。

  71. print('total training cat images:', len(os.listdir(train_cats_dir)))

  72. # total training cat images: 1000

  73. print('total training dog images:', len(os.listdir(train_dogs_dir)))

  74. # total training dog images: 1000

  75. print('total validation cat images:', len(os.listdir(validation_cats_dir)))

  76. # total validation cat images: 500

  77. print('total validation dog images:', len(os.listdir(validation_dogs_dir)))

  78. # total validation dog images: 500

  79. print('total test cat images:', len(os.listdir(test_cats_dir)))

  80. # total test cat images: 500

  81. print('total test dog images:', len(os.listdir(test_dogs_dir)))

  82. # total test dog images: 500

  83. #即整体目录和图片数量如下

  84. # small

  85. # test

  86. # cats 500

  87. # dogs 500

  88. # train

  89. # cats 1000

  90. # dogs 1000

  91. # validation

  92. # cats 1000

  93. # dogs 1000

  94. return train_dir,validation_dir

  95. from keras.preprocessing.image import ImageDataGenerator

  96. #数据预处理

  97. def previewprocess(train_dir,validation_dir):

  98. #(1) 读取图像文件。

  99. #(2) 将JPEG 文件解码为RGB 像素网格。

  100. #(3) 将这些像素网格转换为浮点数张量。

  101. #(4) 将像素值(0~255 范围内)缩放到[0, 1] 区间(正如你所知,神经网络喜欢处理较小的输入值)。

  102. train_datagen = ImageDataGenerator(rescale=1./255) #将所有图像乘以1/255 缩放

  103. test_datagen = ImageDataGenerator(rescale=1./255) #将所有图像乘以1/255 缩放

  104. # Found 2000 images belonging to 2 classes.

  105. # Found 1000 images belonging to 2 classes.

  106. # 它生成了150×150的RGB图像[形状为(20, 150, 150, 3)]与二进制标签[形状为(20, )]组成的批量。

  107. # 每个批量中包含20个样本(批量大小)

  108. train_generator = train_datagen.flow_from_directory(

  109. train_dir,

  110. target_size=(150, 150), #将所有图像的大小调整为150×150

  111. batch_size=20,

  112. class_mode='binary') #因为使用了binary_crossentropy损失,所以需要用二进制标签

  113. validation_generator = test_datagen.flow_from_directory(

  114. validation_dir,

  115. target_size=(150, 150), #将所有图像的大小调整为150×150

  116. batch_size=20,

  117. class_mode='binary') #因为使用了binary_crossentropy损失,所以需要用二进制标签

  118. #train_generator和validation_generator数据格式如下:即4维数组和一个目标标签数组

  119. #array([[[[0.26666668, 0.27058825, 0.2509804],

  120. # [0.26666668, 0.27058825, 0.2509804],

  121. # [0.26666668, 0.27058825, 0.2509804],

  122. # ...,

  123. # [0.6745098, 0.67058825, 0.6627451],

  124. # [0.65882355, 0.654902, 0.64705884],

  125. # [0.64705884, 0.6431373, 0.63529414]]]],

  126. # dtype=float32),

  127. #array([0., 1., 0., 0., 1., 1., 1., 0., 0., 1., 1., 1., 1., 0., 1., 0., 1., 1., 1., 0.],

  128. # type=float32))

  129. #begin time= Fri May 17 14:43:46 2019

  130. for data_batch, labels_batch in train_generator:

  131. print('data batch shape:', data_batch.shape)

  132. #data batch shape: (20, 150, 150, 3)

  133. print('labels batch shape:', labels_batch.shape)

  134. break

  135. return train_generator,validation_generator

  136. def enhenceimage(train_dir,validation_dir):

  137. #利用ImageDataGenerator来设置数据增强

  138. '''

  139. datagen = ImageDataGenerator(

  140. rotation_range=40, #是角度值(在 0~180 范围内),表示图像随机旋转的角度范

  141. width_shift_range=0.2, #是图像在水平或垂直方向上平移的范围(相对于总宽度或总高度的比例

  142. height_shift_range=0.2, #是图像在水平或垂直方向上平移的范围(相对于总宽度或总高度的比例

  143. shear_range=0.2, #随机错切变换的角度

  144. zoom_range=0.2, #是图像随机缩放的范围

  145. horizontal_flip=True, #是随机将一半图像水平翻转

  146. fill_mode='nearest') #是用于填充新创建像素的方法,这些新像素可能来自于旋转或宽度/高度平移

  147. '''

  148. #利用数据增强生成器训练卷积神经网络

  149. train_datagen = ImageDataGenerator(

  150. rescale=1. / 255,

  151. rotation_range=40,

  152. width_shift_range=0.2,

  153. height_shift_range=0.2,shear_range=0.2,

  154. zoom_range=0.2,

  155. horizontal_flip=True,)

  156. test_datagen = ImageDataGenerator(rescale=1./255)

  157. train_generator = train_datagen.flow_from_directory(

  158. train_dir,

  159. target_size=(150, 150),

  160. batch_size=32,

  161. class_mode='binary')

  162. validation_generator = test_datagen.flow_from_directory(

  163. validation_dir,

  164. target_size=(150, 150),

  165. batch_size=32,

  166. class_mode='binary')

  167. return train_generator,validation_generator

  168. from keras import layers

  169. from keras import models

  170. def buildmodel():

  171. #构建模型

  172. model = models.Sequential()

  173. #4个卷积层和4个池化层以及2个全连接层

  174. model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(150, 150, 3)))

  175. model.add(layers.MaxPooling2D((2, 2)))

  176. model.add(layers.Conv2D(64, (3, 3), activation='relu'))

  177. model.add(layers.MaxPooling2D((2, 2)))

  178. model.add(layers.Conv2D(128, (3, 3), activation='relu'))

  179. model.add(layers.MaxPooling2D((2, 2)))

  180. model.add(layers.Conv2D(128, (3, 3), activation='relu'))

  181. model.add(layers.MaxPooling2D((2, 2)))

  182. model.add(layers.Flatten())

  183. model.add(layers.Dense(512, activation='relu'))

  184. model.add(layers.Dense(1, activation='sigmoid'))

  185. print(model.summary())

  186. return model

  187. def buildmodelwithdropout():

  188. #构建模型

  189. model = models.Sequential()

  190. #4个卷积层和4个池化层以及2个全连接层

  191. model.add(layers.Conv2D(32, (3, 3), activation='relu',input_shape=(150, 150, 3)))

  192. model.add(layers.MaxPooling2D((2, 2)))

  193. model.add(layers.Conv2D(64, (3, 3), activation='relu'))

  194. model.add(layers.MaxPooling2D((2, 2)))

  195. model.add(layers.Conv2D(128, (3, 3), activation='relu'))

  196. model.add(layers.MaxPooling2D((2, 2)))

  197. model.add(layers.Conv2D(128, (3, 3), activation='relu'))

  198. model.add(layers.MaxPooling2D((2, 2)))

  199. model.add(layers.Flatten())

  200. model.add(layers.Dropout(0.5))

  201. model.add(layers.Dense(512, activation='relu'))

  202. model.add(layers.Dense(1, activation='sigmoid'))

  203. print(model.summary())

  204. return model

  205. from keras import optimizers

  206. # initdata()

  207. train_generator = 'D:/Python36/Coding/PycharmProjects/ttt/dataset/small/train'

  208. validation_generator = 'D:/Python36/Coding/PycharmProjects/ttt/dataset/small/validation'

  209. train_generator,validation_generator=previewprocess(train_generator,validation_generator)

  210. model=buildmodel()

  211. modelwithdropout=buildmodelwithdropout()

  212. #Layer (type) Output Shape Param #

  213. #=================================================================

  214. #conv2d_1 (Conv2D) (None, 148, 148, 32) 896

  215. #_________________________________________________________________

  216. #max_pooling2d_1 (MaxPooling2 (None, 74, 74, 32) 0

  217. #_________________________________________________________________

  218. #conv2d_2 (Conv2D) (None, 72, 72, 64) 18496

  219. #_________________________________________________________________

  220. #max_pooling2d_2 (MaxPooling2 (None, 36, 36, 64) 0

  221. #_________________________________________________________________

  222. #conv2d_3 (Conv2D) (None, 34, 34, 128) 73856

  223. #_________________________________________________________________

  224. #max_pooling2d_3 (MaxPooling2 (None, 17, 17, 128) 0

  225. #_________________________________________________________________

  226. #conv2d_4 (Conv2D) (None, 15, 15, 128) 147584

  227. #_________________________________________________________________

  228. #max_pooling2d_4 (MaxPooling2 (None, 7, 7, 128) 0

  229. #_________________________________________________________________

  230. #flatten_1 (Flatten) (None, 6272) 0

  231. #_________________________________________________________________

  232. #dense_1 (Dense) (None, 512) 3211776

  233. #_________________________________________________________________

  234. #dense_2 (Dense) (None, 1) 513

  235. #=================================================================

  236. # 使用RMSprop 优化器。

  237. # 因为网络最后一层是单一sigmoid单元,所以我们将使用二元交叉熵作为损失函数

  238. #为模型选择正确的最后一层激活和损失函数

  239. #问题类型 最后一层激活 损失函数

  240. #二分类问题 sigmoid binary_crossentropy

  241. #多分类、单标签问题 softmax categorical_crossentropy

  242. #多分类、多标签问题 sigmoid binary_crossentropy

  243. #回归到任意值 无 mse

  244. #回归到0~1 范围内的值 sigmoid mse 或binary_crossentropy

  245. model.compile(loss='binary_crossentropy',optimizer=optimizers.RMSprop(lr=1e-4),metrics=['acc'])

  246. history = model.fit_generator(train_generator,steps_per_epoch=100,epochs=30,validation_data=validation_generator,validation_steps=50)

  247. #保存模型

  248. model.save('cats_and_dogs_small_1.h5')

  249. #编译、训练、保存带dropout模式的模型

  250. modelwithdropout.compile(loss='binary_crossentropy',optimizer=optimizers.RMSprop(lr=1e-4),metrics=['acc'])

  251. historywithdropout = modelwithdropout.fit_generator(train_generator,steps_per_epoch=100,epochs=30,validation_data=validation_generator,validation_steps=50)

  252. #保存模型

  253. modelwithdropout.save('cats_and_dogs_small_1_withdropout.h5')

  254. #和上面同样的模型,只是增强了图片样本

  255. train_generator = 'D:/Python36/Coding/PycharmProjects/ttt/dataset/small/train'

  256. validation_generator = 'D:/Python36/Coding/PycharmProjects/ttt/dataset/small/validation'

  257. train_generator,validation_generator=enhenceimage(train_generator,validation_generator)

  258. modelwithenhencedropout=buildmodelwithdropout()

  259. modelwithenhencedropout.compile(loss='binary_crossentropy',optimizer=optimizers.RMSprop(lr=1e-4),metrics=['acc'])

  260. historywithenhencedropouthistory = modelwithenhencedropout.fit_generator(train_generator,steps_per_epoch=100,epochs=30,validation_data=validation_generator,validation_steps=50)

  261. modelwithenhencedropout.save('cats_and_dogs_small_1_withehencedropout.h5')

  262. #可视化训练集和验证集的损失率和准确率

  263. # 可以看出随着训练轮次的增加,

  264. # 训练集的准确率呈对数级上升,而验证集的准确率则在第十轮左右维持在72%的准确率

  265. # 训练集的损失度呈对数级下降,而验证集的损失度则在第十轮左右最低,此后不断上升

  266. # 因此本例子主要还是过度拟合导致,根本原因是样本数量不足,只有2000训练集样本

  267. # 在增加dropout层以后,训练准确率较之前有所下降,但验证准确率较之前有所提升达到75%。

  268. # 在利用数据增强生成器训练卷积神经网络后,训练集和验证集的准确率基本是同步的,最高上升到78%

  269. #150s 2s/step - loss: 0.0340 - acc: 0.9900 - val_loss: 1.1205 - val_acc: 0.7190

  270. #153s 2s/step - loss: 0.2005 - acc: 0.9245 - val_loss: 0.6639 - val_acc: 0.7500

  271. #244s 2s/step - loss: 0.4792 - acc: 0.7687 - val_loss: 0.4896 - val_acc: 0.7494

  272. import matplotlib.pyplot as plt

  273. acc = history.history['acc']

  274. val_acc = history.history['val_acc']

  275. loss = history.history['loss']

  276. val_loss = history.history['val_loss']

  277. accwithdropout = historywithdropout.history['acc']

  278. val_accwithdropout = historywithdropout.history['val_acc']

  279. losswithdropout = historywithdropout.history['loss']

  280. val_losswithdropout = historywithdropout.history['val_loss']

  281. accwithenhencedropout = historywithenhencedropouthistory.history['acc']

  282. val_accwithenhencedropout = historywithenhencedropouthistory.history['val_acc']

  283. losswithenhencedropout = historywithenhencedropouthistory.history['loss']

  284. val_losswithenhencedropout = historywithenhencedropouthistory.history['val_loss']

  285. epochs = range(1, len(acc) + 1)

  286. plt.plot(epochs, acc, 'bo', label='Training acc')

  287. plt.plot(epochs, val_acc, 'b', label='Validation acc')

  288. plt.plot(epochs, accwithdropout, 'bo', label='Training acc withdropout',color='red')

  289. plt.plot(epochs, val_accwithdropout, 'b', label='Validation acc withdropout',color='red')

  290. plt.plot(epochs, accwithenhencedropout, 'bo', label='Training acc withenhencedropout',color='yellow')

  291. plt.plot(epochs, val_accwithenhencedropout, 'b', label='Validation acc withenhencedropout',color='yellow')

  292. plt.title('Training and validation accuracy')

  293. plt.legend()

  294. plt.figure()

  295. plt.plot(epochs, loss, 'bo', label='Training loss')

  296. plt.plot(epochs, val_loss, 'b', label='Validation loss')

  297. plt.plot(epochs, losswithdropout, 'bo', label='Training loss withdropout',color='red')

  298. plt.plot(epochs, val_losswithdropout, 'b', label='Validation loss withdropout',color='red')

  299. plt.plot(epochs, losswithenhencedropout, 'bo', label='Training loss withenhencedropout',color='yellow')

  300. plt.plot(epochs, val_losswithenhencedropout, 'b', label='Validation loss withenhencedropout',color='yellow')

  301. plt.title('Training and validation loss')

  302. plt.legend()

  303. plt.show()

关于深度学习系列笔记十二(关于猫狗判断实验)_python_02

关于深度学习系列笔记十二(关于猫狗判断实验)_python_03