前言

今天再来做一个神经网络的分类任务,完整代码见 https://github.com/zong4/AILearning。

代码

来看看比上次多了什么。

数据增强

首先是考虑到图像比较少,所以通过旋转缩放来生成新的图像供 AI 训练。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
train_image_generator = ImageDataGenerator(rescale=1./255,
rotation_range=45,
width_shift_range=.15,
height_shift_range=.15,
horizontal_flip=True,
zoom_range=0.5) # Generator for our training data

train_data_gen = train_image_generator.flow_from_directory(batch_size=batch_size,
directory=train_dir,
target_size=(IMG_HEIGHT, IMG_WIDTH),
class_mode='binary')

augmented_images = [train_data_gen[0][0][0] for i in range(5)]

plotImages(augmented_images)

效果如下。

模型结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
model = Sequential([
Conv2D(16, 3, padding='same', activation='relu', input_shape=(IMG_HEIGHT, IMG_WIDTH ,3)),
MaxPooling2D(),
Conv2D(32, 3, padding='same', activation='relu'),
MaxPooling2D(),
Conv2D(64, 3, padding='same', activation='relu'),
MaxPooling2D(),
Flatten(),
Dense(512, activation='relu'),
tf.keras.layers.Dropout(0.5),
Dense(1)
])

model.summary()

可以通过 model.summary() 来查看模型的结构。

结果

这是没有 Dropout 层的结果。

1
accuracy: 0.6516 - loss: 0.6097 - val_accuracy: 0.6931 - val_loss: 0.5868

加上 Dropout 层后,正确率变低了,说明之前的模型过拟合了。

1
accuracy: 0.6129 - loss: 0.6371 - val_accuracy: 0.6618 - val_loss: 0.6068

优化

又加了一层卷积,还是不够。

1
accuracy: 0.6103 - loss: 0.6333 - val_accuracy: 0.6808 - val_loss: 0.5937

提了一下 epoch,正确率提高了。

1
accuracy: 0.7266 - loss: 0.5674 - val_accuracy: 0.7199 - val_loss: 0.5659

提供了更丰富的数据增强。

1
2
3
4
5
6
7
8
9
10
11
train_datagen = ImageDataGenerator(
rescale=1./255,
rotation_range=45, # 增大旋转范围
width_shift_range=0.3, # 增大水平平移范围
height_shift_range=0.3, # 增大垂直平移范围
shear_range=0.3, # 增大错切范围
zoom_range=[0.7, 1.3], # 更宽的缩放范围
horizontal_flip=True,
vertical_flip=True, # 增加垂直翻转
fill_mode='nearest'
)

直接给训练集的正确率干飞掉了,过拟合无疑了。

引入批量归一化层(Batch Normalization)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
model = Sequential([
Conv2D(16, 3, padding='same', input_shape=(IMG_HEIGHT, IMG_WIDTH ,3)),
BatchNormalization(),
Activation('relu'),
MaxPooling2D(),
Conv2D(32, 3, padding='same'),
BatchNormalization(),
Activation('relu'),
MaxPooling2D(),
Conv2D(64, 3, padding='same'),
BatchNormalization(),
Activation('relu'),
MaxPooling2D(),
Conv2D(128, 3, padding='same'),
BatchNormalization(),
Activation('relu'),
MaxPooling2D(),
Flatten(),
Dense(512, activation='relu'),
tf.keras.layers.Dropout(0.5),
Dense(1)
])

正确率直接烂掉了,应该是数据集太小了。

所以干脆和上一步合一起来训练,好像没什么变化,那我就没头绪了。

修改学习率调度器为余弦退火学习率调度器。

1
2
3
def cosine_annealing_lr(epoch, lr, T_max=100):
return 0.001 * 0.5 * (1 + np.cos(np.pi * epoch / T_max))
lr_scheduler = LearningRateScheduler(cosine_annealing_lr)

看上去,epoch 不够,还在跳,不过貌似效果还可以。

后记

总之,现在对好多调参手段还没法说出个所以然,只能靠试,所以还是得进行更深入的理论学习,期待以后的文章吧。