函数式API
之前的模型都使用Sequential
模型,然后不断地add
新的层,但它的局限性很大,比如要包含多个输入和输出,或者分叉神经网络的时候,用Sequential
是实现不了的,所以可以使用另一种更灵活的方式: 函数式API。
函数式API是每次把层当做函数来用,把上一层作为下一层的函数,之后使用keras.models.Model
,提供输入和输出来建立模型(可以有多个输入和多个输出)。
下面使用针对MNIST数据集的函数式API来作为例子:
from keras import Input, layers, models
#首先是输入层
input_tensor = Input(shape=(28,28, 1))
#接下来是卷积层,Flatten,以及全连接层
cnn = layers.Conv2D(32, (3, 3))(input_tensor)
bn = layers.BatchNormalization()(cnn)
flatten = layers.Flatten()(bn)
#可以看出,每一层都是拿上一层作为参数
dense = layers.Dense(64, activation='relu')(flatten)
output_tensor = layers.Dense(10, activation='softmax')(dense)
#最后,使用models.Model(输入层,输出层)建立模型
model = models.Model(input_tensor, output_tensor)
model.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy'])
看一下结构吧
model.summary()
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
input_14 (InputLayer) (None, 28, 28, 1) 0
_________________________________________________________________
conv2d_8 (Conv2D) (None, 26, 26, 32) 320
_________________________________________________________________
batch_normalization_7 (Batch (None, 26, 26, 32) 128
_________________________________________________________________
flatten_6 (Flatten) (None, 21632) 0
_________________________________________________________________
dense_22 (Dense) (None, 64) 1384512
_________________________________________________________________
dense_23 (Dense) (None, 10) 650
=================================================================
Total params: 1,385,610
Trainable params: 1,385,546
Non-trainable params: 64
_________________________________________________________________
如果是多输入模型,可以使用下面的方法组合及训练,根据name
确定输入数据集:
input_1 = Input(xxx, name='input_A')
input_1 = Input(xxx, name='input_B')
....
output = Dense(...)(...)
model = models.Model([input_1, input_2], output)
model.fit({'input_A':dataset1, 'input_B':dataset2}, labels)
如果是多输出模型,则是下面这种方式分别设置损失函数:
out_1 = Dense(xxx, name='out1')(...)
out_2 = Dense(..., name='out2')(...)
model = models.Model(input_layer, [out_1, out_2])
model.compile(optimizer='rmsprop',
loss={'out1':'mse',
'out2':'categorical_crossentropy'})
函数式编程就是这么简单。
迁移学习
迁移学习的概念非常火,比如,VGG16、ResNet、Inception等等都是很有名的计算机视觉模型。通常可以使用这些在ImageNet上预训练好的来做特征提取,再加上自己的全连接层。
Keras中可以很方便地进行迁移学习,现在还是用之前的dogs-vs-cats比赛数据集为例。
首先从keras.applications
导入一个模型,常见的模型有:
- VGG16, VGG19
- Inception
- Xception
- InceptionResNet
- MobileNet
- DenseNet
- NasNet
我在这里使用的是最基础的VGG16:
weights
:为预训练的权重,如果设置为None,则为从头开始训练include_top
:一般设置为False,也就是只包括卷积层,而不包括后面的全连接层,因为我们要自己训练全连接层input_shape
:输入尺寸
from keras.applications import VGG16
from keras import layers, models, optimizers
base_model =VGG16(weights='imagenet', include_top=False,
input_shape=(150, 150, 3))
通常情况下,可以考虑冻结部分卷积网络的权重,因为这些权重都是训练好的,能够提取很多抽象特征,如果再训练的话,与重新训练一个神经网络就没什么两样了,这里我为了方便,把所有预训练过的卷积层的网络全部冻结。
base_model.trainable = False
现在,就可以在后面加我们自己的全连接层了。
x0 = base_model.output
x1 = layers.Flatten()(x0)
x2 = layers.Dense(512)(x1)
x3 = layers.Dropout(0.5)(x2)
predictions = layers.Dense(1, activation='sigmoid')(x2)
# this is the model we will train
model = models.Model(inputs=base_model.input, outputs=predictions)
model.compile(optimizer=optimizers.rmsprop(1e-4),
loss='binary_crossentropy',
metrics=['accuracy'])
加好之后,直接训练吧(毕竟卷积网络是预训练好的,所以一般不需要训练太多轮,这也是迁移学习的好处)
history = model.fit_generator(
train_generator,
steps_per_epoch=400,
epochs=15,
validation_data=val_generator,
validation_steps=50)
看一下训练效果如何:
import matplotlib.pyplot as plt
acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']
plt.plot(acc[:20], 'g', label='training acc')
plt.plot(val_acc[:20], 'r', label='validation acc')
plt.title("Training and Validation accuracy")
plt.legend()
plt.show()
plt.figure()
plt.plot(loss, 'g', label='training loss')
plt.plot(val_loss, 'r', label='validation loss')
plt.title("Training and Validation loss")
plt.legend()
看来效果还是不错的。