前面在14节中忘了可以再看看,引用了卷积基,但是发现一个问题就是训练的时候特别特别的慢,那么为啥慢,因为虽然不用训练卷积基的权重等参数(只需要训练全连接输出层),但是每一张图片都要经过它,反复提取特征,其实这是重复的操作。
那么就考虑只让卷积基提取一次图片特征就好(就是每个图片只过一次卷积基),只反复训练输出层(全连接网络)。
那么运用卷积基提取图片特征代码如何实现?
def extract_features(data_generator, sample_count):
i = 0
features = np.zeros(shape=(shape_count,6,6,512)) #建立一个0向量,形状(6,6,512)是来自于卷积基的最后一层的池化层输出形状
labels = np.zeros(shape=(sample_count))
for inputs_batch, labels_batch in data_generator:
features_batch = covn_base.predict(inputs_batch) #一个batch一个batch的预测图片特征
features[i*batch_size :(i+ 1) * batch_size] = features_batch #将特征放入前面的0向量存起来
labels[i*batch_size :(i+ 1) * batch_size] = labels_batch
i +=1
if i * batch_size >= sample_count: #当图片数量超过输入的,就跳出循环
break
return features, labels
提取训练集,测试集特征
train_features,train_labels = extract_features(train_generator,2000)
test_features, test_labels = extract_features(test_generator,1000)
建立模型
model = keras.Sequential()
model.add(layers.GlobalAveragePooling2D(input_shape=(6,6,512)).) # 数据扁平化和FLATTEN的效果一样但是更高效
model.add(layers.Dense(512,activation='relu'))
model.add(layers.Dropout(0.5))
model.add(layers.Dense(1,activation='sigmoid'))
编译
model.compile(optimizer='adam', #用实际数据的时候,可能需要调参,那么就实例化 optimizer=keras.optimizers.Adam(lr=0.0001)
loss='binary_crossentropy'
metrics=['acc']
)
训练模型
history = model.fit(train_features, train_labels,
epochs=30,
batch_size = 50,
validation_data=(test_features, test_labels))
注意:相应的训练也改成了train_features, train_labels。
预训练网络模型–微调
什么是微调?
微调就是冻结模型库的底部卷积层,共同训练新添加的分类器和顶部部分卷积层。
下面来解释一下,为什么要冻结底部卷积层,因为底部卷积层是提取一些通用特征,图片纹理,轮廓,细小的一些规则。而顶部的卷积层是经过一层一层的神经网络视野扩大形成的抽象的特征,比如猫,狗(即与特定的任务相关)比如是分猫狗,那么顶部卷积层提取的特征就是猫和狗的特征。
所以,这允许我们微调基础模型中的高阶特征表示,使他们与特定的任务更相关。(所以微调简单来说就是让预训练网络更加切合我们的自己的特定实际任务,从而提高精度)
注意:只有在分类器已经训练好了,才能微调卷积基的顶部卷积层。
如果不这样的话,(由于初始化的权重是随机的,刚开始的训练误差,微调之前,这些误差反向传播回去会破坏掉卷积层学到的特征。)
所以微调的步骤:
- 在预训练卷积基上添加自定义层
- 冻结卷积基所有层
- 训练添加的分类层
- 解冻卷积基的一部分层
- 联合训练解冻的卷积层和添加的自定义层
代码实现:
#1-3步
model = keras.Sequential()
model.add(covn_base)
model.add(layers.GlobalAveragePooling2D().) # 数据扁平化和FLATTEN的效果一样但是更高效
model.add(layers.Dense(512,activation='relu'))
model.add(layers.Dense(1,activation='sigmoid'))
covn_base.trainable = False
#然后训练,训练好后(准确率够高),解冻卷积基的一部分层
#第4步解冻卷积基的一部分
covn_base.trainable = True #解冻所有层
len(covn_base.layers) #看一下有多少层
fine_tune_at = -3 # 只训练卷积基的最后三层,即顶部卷积层
for layer in covn_base.layers[:fine_tune_at]:
layer.trainable = False #除最后三层的卷积基的卷积层全部冻结
model.conpile(loss='binary_crossentropy',
optimizer = keras.optimizers.Adam(lr=0.0005/10),
metrics = ['accuracy'])
#训练模型
initial_epochs = 12 #前面已经训练了12个epochs了,所以现在从13个epochs 开始训练
fine_tune_epochs = 10 #顶部卷积层要训练多少个epochs 10个
total_epochs = initial_epochs + fine_tune_epochs #一共训练的epoches
history = model.fit(
train_image_ds,
steps_per_epoch=trian_count//BATCH_SIZE,# 训练集的图片数量除以图片生成器BATCH_SIZE
epochs=total_epochs,
initial_epoch = initial_epochs,
validation_data= test_image_ds,
validation_step = test_count//BATCH_SIZE)# 测试集的图片数量除以图片生成器BATCH_SIZE
#接下来的训练就会接着上面训练分类器的后接续训练