学习Keras时需要掌握的知识

学习Keras时需要掌握的知识
本文在《Deep Learning with Python》一书第七章的基础上整理。希望自己能够牢记这些代码,熟记于心,不要每次敲两行查一下OTZ。

文章目录

创建网络结构

导包

from keras import layers, Input
from keras.models import Sequential, Model

使用Sequential创建基本网络

seq_model = Sequential()
seq_model.add(layers.Dense(32, activation='relu', input_shape=(64,)))
seq_model.add(layers.Dense(32, activation='relu'))
seq_model.add(layers.Dense(10, activation='softmax'))

使用函数API

创建基本网络

input_tensor = Input(shape=(64,))
x = layers.Dense(32, activation='relu')(input_tensor)
x = layers.Dense(32, activation='relu')(x)
output_tensor = layers.Dense(10, activation='softmax')(x)
model = Model(input_tensor, output_tensor)

创建双输入网络

使用keras.layers.addkeras.layers.concatenate等,将多个输入相加、连接起来

text_vocabulary_size = 10000
question_vocabulary_size = 10000
answer_vocabulary_size = 500


# 对于多个输入,可以给不同的输入命名,也方便得到model
text_input = Input(shape=(None,), dtype='int32', name='text')
embedded_text = layers.Embedding(text_vocabulary_size, 64)(text_input)
encoded_text = layers.LSTM(32)(embedded_text)

question_input = Input(shape=(None,), dtype='int32', name='question')  
embedded_question = layers.Embedding(question_vocabulary_size, 32)(question_input)
encoded_question = layers.LSTM(16)(embedded_question)

concatenated = layers.concatenate([encoded_text, encoded_question], axis=-1)

answer = layers.Dense(answer_vocabulary_size, activation='softmax')(concatenated)

model = Model([text_input, question_input], answer)  # 注意双输入的input_tensor怎么写
model.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['acc'])

在训练部分,先随机一组虚假的数据出来

import numpy as np
num_samples = 1000
max_length = 100
text = np.random.randint(1, text_vocabulary_size, size=(num_samples, max_length))
question = np.random.randint(1, question_vocabulary_size, size=(num_samples, max_length))
answers = np.random.randint(answer_vocabulary_size, size=(num_samples))
answers = keras.utils.to_categorical(answers, answer_vocabulary_size)

在fit的时候,有两种写法。

一种是将所有输入数据以数组的形式传入:

model.fit([text, question], answers, epochs=10, batch_size=128)

另一种是好好利用在输入时对Input的命名:

model.fit({'text':text, 'question':question}, answers, epochs=10, batch_size=128)

创建多输出网络(多头网络)

vocabulary_size = 50000
num_incom_groups = 10

posts_input = Input(shape=(None,), dtype='int32', name='posts')
embedded_posts = layers.Embedding(256, vocabulary_size)(posts_input)
x = layers.Conv1D(128, 5, activation='relu')(embedded_posts)
x = layers.MaxPooling1D(5)(x)

x = layers.Conv1D(256, 5, activation='relu')(x)
x = layers.Conv1D(256, 5, activation='relu')(x)
x = layers.MaxPooling1D(5)(x)
x = layers.Conv1D(256, 5, activation='relu')(x)
x = layers.Conv1D(256, 5, activation='relu')(x)
x = layers.GlobalAvgPooling1D(x)
x = layers.Conv1D(128, activation='relu')(x)

# 输出层都需要拥有姓名
age_prediction = layers.Dense(1, name='age')(x)
income_prediction = layers.Dense(num_incom_groups, activation='softmax', name='income')(x)
gender_prediction = layers.Dense(1, activation='sigmoid', name='gender')(x)
model = Model(posts_input, [age_prediction, income_prediction, gender_prediction])

多输出网络的重点在于损失。同损失有不同的取值范围,为了平衡不同损失的贡献,对不同损失加权。
权重系数是根据先验得到的。

model.compile(optimizer='rmsprop',
              loss=['mse', 'categorical_crossentropy', 'binary_crossentropy'],
              loss_weights=[0.25, 1, 10])  # 这个加权重是根据先验加的
# 字典写法
model.compile(optimizer='rmsprop',
              loss={'age':'mse',
                    'income':'categorical_crossentropy',
                    'gender':'binary_crossentropy'},
              loss_weights={'age':0.25,
                            'income':1.,
                            'gender':10.})

输入数据也是,可以有列表写法和字典写法:

model.fit(posts, [age_targets, income_targets, ], epochs=10, batch_size=64)
model.fit(posts, {'age':age_targets,
                  'income':income_targets,
                  'gender':gender_targets},
          epochs=10, batch_size=64)

有向无环图

唯一允许的处理循环(即循环连接)是循环层的内部循环(但内部循环暂时还不了解,不予讨论)

Inception结构

事实上Keras中内置了 keras.applications.inception_v3.InceptionV3

branch_a = layers.Conv2D(128, 1, activation='relu', strides=2)(x)

branch_b = layers.Conv2D(128, 1, activation='relu')(x)
branch_b = layers.Conv2D(128, 3, activation='relu', strides=2)(branch_b)

branch_c = layers.AveragePooling2D(3, strides=2)(x)
branch_c = layers.Conv2D(128, 1, activation='relu')(branch_c)

branch_d = layers.Conv2D(128, 1, activation='relu')(x)
branch_d = layers.Conv2D(128, 3, activation='relu')(branch_d)
branch_d = layers.Conv2D(128, 3, activation='relu', strides=2)(branch_d)

output = layers.concatenate([branch_a, branch_b, branch_c, branch_d], axis=-1)

残差连接

分为恒等残差连接和线性残差连接

# 恒等残差连接
y = layers.Conv2D(128, 3, activation='relu', padding='same')(x)
y = layers.Conv2D(128, 3, activation='relu', padding='same')(y)
y = layers.Conv2D(128, 3, activation='relu', padding='same')(y)

y = layers.add([y, x])

# 线性残差连接
y = layers.Conv2D(128, 3, activation='relu', padding='same')(x)
y = layers.Conv2D(128, 3, activation='relu', padding='same')(y)
y = layers.MaxPooling2D(2, strides=2)(y)

residual = layers.Conv2D(128, 1, strides=2, padding='same')(x)

y = layers.add([y, residual])

重复使用权重/共享权重

from keras.models import Model
from keras import layers, Input

# 共享LSTM
lstm = layers.LSTM(32)  # 将一个LSTM层实例化一次

left_input = Input(shape=(None, 128))  # 长度128的向量组成的变长序列
left_output = lstm(left_input)

right_input = Input(shape=(None, 128))
right_output = lstm(right_input)  # 如果调用已有的层实例,就会重复使用它的权重

merged = layers.concatenate([left_output, right_output], axis=-1)
predictions = layers.Dense(1, activation='sigmoid')(merged)

model = Model([left_input, right_input], predictions)
model.fit([left_data, right_data], targets)  # 训练这种模型时,会基于两个输入对LSTM层的权重进行更新

将模型作为层使用

在调用模型实例时,就是在重复使用模型的权重,正如在调用层实例时,就是在重复使用层的权重。调用一个实例,无论是层实例还是模型实例,都会重复使用这个实例已经学到的表示,这很直观。

重复使用模型实例:

from keras import layers
from keras import applications
from keras import Input

# 被复用的模型:xception_base
xception_base = applications.Xception(weight=None, include_top=False)

left_input = Input(shape=(250, 250, 3))
right_input = Input(shape=(250, 250, 3))

left_features = xception_base(left_input)
right_features = xception_base(right_input)
merged_features = layers.concatenate([left_features, right_features], axis=-1)

必要知道的函数

model相关

  1. model.summary() # 输出模型结构、参数量
  2. model.compile(optimizer=’…’, loss=’…’) # 编译模型
  3. model.fit(x_train, y_train, epochs=10, batch_size=128) # 训练模型
  4. score = model.evaluate(x_train, y_train) # 评价模型
  5. keras.applications.inception_v3.InceptionV3 和 Xception(精度更高)
  6. 深度可分离卷积:layers.SeparableConv2D(64, 3, activation=‘relu’)

画图

from keras.utils import plot_model

plot_model(model, to_file='model.png')
# 想在图里显示形状信息
plot_model(model, show_shapes=True, to_file='model.png')

查看每个epoch的loss和accuracy并画图

model.compile(optimizer=optimizers.RMSprop(lr=2e-5),
              loss='binary_crossentrop',
              metrics=['acc'])
history = model.fit(train_features, train_labels,epochs=30, batch_size=20, validation_data=(validation_features, validation_labels))

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']

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

plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.legend()

plt.figure()
plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()

plt.show()

检查并监控深度学习模型

当观测到验证损失不再改善时就停止训练,可以使用 Keras回调函数来实现。

回调函数(callback)是在调用 fit 时传入模型的一个对象(即实现特定方法的类实例),它在训练过程中的不同时间点都会被模型调用。它可以访问关于模型状态与性能的所有可用数据,还可以采取行动:中断训练、保存模型、加载一组不同的权重或改变模型的状态。

内置回调函数示例

回调函数的一些用法示例如下:

  • 模型检查点(model checkpointing):在训练过程中的不同时间点保存模型的当前权重。
  • 提前终止(early stopping):如果验证损失不再改善,则中断训练(当然,同时保存在训练过程中得到的最佳模型)。
  • 在训练过程中动态调节某些参数值:比如优化器的学习率。
  • 在训练过程中记录训练指标和验证指标,或将模型学到的表示可视化(这些表示也在不断更新):你熟悉的 Keras 进度条就是一个回调函数!

keras.callbacks 模块包含许多内置的回调函数,下面列出了其中一些:

  • keras.callbacks.ModelCheckpoint
  • keras.callbacks.EarlyStopping
  • keras.callbacks.LearningRateScheduler
  • keras.callbacks.ReduceLROnPlateau
  • keras.callbacks.CSVLogger
import keras

# 通过 fit 的 callbacks 参数将回调函数传入模型中,这个参数接受一个回调函数的列表。
# 你可以传入任意个数的回调函数
callback_list = [
    keras.callbacks.EarlyStopping(  # 如果不再改善,就中断训练
        monitor='acc',  # 监控模型的验证精度
        patience=1,  # 如果精度在多于一轮的时间(即两轮)内不再改善,中断训练
    ),
    keras.callbacks.ModelCheckpoint(  # 在每轮过后保存当前权重
        filepath='my_model.h5',  # 目标模型文件的保存路径
        monitor='val_loss',
        save_best_only=True,  # 如果 val_loss 没有改善,不需要覆盖模型文件
    )
]
model.compile(optimizer='rmsprop',
              loss='binary_crossentropy',
              metrics=['acc'])
# 由于回调函数要监控验证损失和验证精度,所以在调用fit时需要传入validation_data(验证数据)
model.fit(x, y, epochs=10, batch_size=32, 
          callbacks=callback_list, 
          validation_data=(x_val, y_val))

如果验证损失不再改善,你可以使用 ReduceLROnPlateau 回调函数来降低学习率。在训练过程中如果出现了损失平台(loss plateau),那么增大或减小学习率都是跳出局部最小值的有效策略。

callback_list = [
    keras.callbacks.ReduceLROnPlateau(
        monitor='val_loss',  # 监控模型的验证损失
        factor=0.1,  # 触发时将学习率除以10
        patience=10,  # 如果验证损失在10轮内都没有改善,就触发这个回调函数
    )
]

编写你自己的回调函数

是创建 keras.callbacks.Callback 类的子类。然后你可以实现下面这些方法(从名称中即可看出这些方法的作用),它们分别在训练过程中的不同时间点被调用。

  • on_epoch_begin 在每轮开始时被调用
  • on_epoch_end 在每轮结束时被调用
  • on_batch_begin 在处理每个批量之前被调用
  • on_batch_end 在处理每个批量之后被调用
  • on_train_begin 在训练开始时被调用
  • on_train_end 在训练结束时被调用

这些方法被调用时都有一个 logs 参数,这个参数是一个字典,里面包含前一个批量、前一个轮次或前一次训练的信息,即训练指标和验证指标等。

此外,回调函数还可以访问下列属性:

  • self.model:调用回调函数的模型实例
  • self.validation_data:传入 fit 作为验证数据的值

模型集成

指将一系列不同模型的预测结果汇集到一起,从而得到更好的预测结果

想要保证集成方法有效,关键在于这组分类器的多样性(diversity)。如果所有模型的偏差都在同一个方向上,那么集成也会保留同样的偏差。如果各个模型的偏差在不同方向上,那么这些偏差会彼此抵消,集成结果会更加稳定、更加准确。

因此,集成的模型应该尽可能好,同时尽可能不同。这通常意味着使用非常不同的架构,甚至使用不同类型的机器学习方法

以分类问题为例。想要将一组分类器的预测结果汇集在一起[即分类器集成(ensemble the classifiers)],最简单的方法就是将它们的预测结果取平均值作为预测结果:

只有这组分类器中每一个的性能差不多一样好时,这种方法才奏效。

preds_a = model_a.predict(x_val)
preds_b = model_b.predict(x_val)
preds_c = model_c.predict(x_val)
preds_d = model_d.predict(x_val)

final_preds = 0.25 * (preds_a + preds_b + preds_c + preds_d)

更优化的是给它们加权重,权重值可以用随机搜索或简单的优化算法(比如Nelder-Mead方法)(暂时这一块也不了解,需要后续学习)

上一篇:tensorflow(三十九):实战——深度残差网络ResNet18


下一篇:什么是卷积