函数式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()

看来效果还是不错的。

最后修改:2021 年 06 月 01 日 02 : 16 PM
如果觉得我的文章对你有用,请随意赞赏