本文是基于tensorflow2.2.0版本,介绍了模型的训练与评估。主要介绍了tf.keras的内置训练过程,包括compile、fit,其中compile中包含优化器、loss与metrics的使用,内置api中还包含了很多辅助工具,在Callback中进行介绍;除了简单的单输入单输出模型之外,本文还介绍了多输入、多输出模型的训练过程。本文中涉及的内容都是内置api,相关内容大都可以进行自定义,自定义相关内容会陆续在后续文章里介绍。
实战系列篇章中主要会分享,解决实际问题时的过程、遇到的问题或者使用的工具等等。如问题分解、bug排查、模型部署等等。相关代码实现开源在:https://github.com/wellinxu/nlp_store ,更多内容关注知乎专栏(或微信公众号):NLP杂货铺。
- 简单文本分类模型示例
- 训练与评估流程
- compile
- fit
- Callback
- 多输入、多输出模型
- compile
- fit
- 参考
简单文本分类模型示例
如下面代码所示,根据【NLP实战篇之tensorflow2.0快速入门】获取一个完整的文本分类示例,其中包含数据获取、数据简单预处理、模型构建、训练与评估。
import tensorflow as tf
# 下载IMDB数据
vocab_size = 10000 # 保留词的个数
imdb = tf.keras.datasets.imdb
(train_data, train_labels), (test_data, test_labels) = imdb.load_data(num_words=vocab_size)
# 一个将单词映射到整数索引的词典
word_index = imdb.get_word_index() # 索引从1开始
word_index = {k:(v+3) for k,v in word_index.items()}
word_index[""] = 0
word_index[""] = 1
word_index[""] = 2 # unknown
word_index[""] = 3
# 统一文本序列长度
train_data = tf.keras.preprocessing.sequence.pad_sequences(train_data, value=word_index[""], padding="post", truncating="post", maxlen=256)
test_data = tf.keras.preprocessing.sequence.pad_sequences(test_data, value=word_index[""], padding="post", truncating="post", maxlen=256)
# 模型构建
model = tf.keras.Sequential([
tf.keras.layers.Embedding(vocab_size, 16), # [batch_size, seq_len, 16]
tf.keras.layers.GlobalAveragePooling1D(), # [batch_size, 16]
tf.keras.layers.Dense(16, activation='relu'), # [batch_size, 16]
tf.keras.layers.Dense(1, activation='sigmoid') # [batch_size, 1]
])
# 配置模型训练参数
# model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
model.compile(optimizer=tf.keras.optimizers.Adam(), loss=tf.keras.losses.BinaryCrossentropy(), metrics=[tf.keras.metrics.BinaryAccuracy()])
# 训练模型
history = model.fit(train_data, train_labels, epochs=40, batch_size=512)
# 评估测试集
model.evaluate(test_data, test_labels, verbose=2)
训练与评估流程
compile
如上面代码所示,要使用fit进行训练模型,需要制定优化器、损失函数和评价指标,通过compile方法传递给模型。除了这三个参数,compile还包含了其他参数:
- optimizer: 字符串(优化器名称)或者优化器实例,具体可看
tf.keras.optimizers
- loss: 字符串(损失函数名称)、损失函数或损失实例,具体可看
tf.keras.losses.Loss
与tf.keras.losses
- metrics: 评价方法列表,列表中每个值可以是字符串(度量函数名称)、度量函数或者度量实例,具体可看
tf.keras.metrics.Metric
与tf.keras.metrics
- loss_weights: 列表或者字典格式,模型不同输出的loss权重,用于多输出模型
- sample_weight_mode:
- weighted_metrics: 通过样本权重或者类别权重计算的度量列表
- **kwargs: 其他关键字参数
其中优化器、loss和度量方法都有很多内置的api,如下表所示。除此之外,还支持自定义相关函数,此部分内容在后续文章中介绍。
- optimizer优化器:Adadelta、Adagrad、Adam、Adamax、Ftrl、SGD、Nadam、RMSprop等
- loss损失:BinaryCrossentropy、CategoricalCrossentropy、CategoricalHinge、CosineSimilarity、Hinge、Huber、KLDivergence、LogCosh、MeanAbsoluteError、MeanAbsolutePercentageError、MeanSquaredError、MeanSquaredLogarithmicError、Poisson、SparseCategoricalCrossentropy、SquaredHinge、MAE、MAPE、MSE、MSLE等等
- metrics度量:AUC、Accuracy、BinaryAccuracy、BinaryCrossentropy、CategoricalAccuracy、CosineSimilarity、FalseNegatives、FalsePositives、MeanIoU、Mean、MeanAbsoluteError、MeanSquaredError、Precision、Recall、TopKCategoricalAccuracy等等
在训练神经网络的时候,有个比较重要的超参数:学习率,这个参数的大小或者变化,都严重影响着最终模型的效果。通过优化器中learning_rate参数可以设置学习率的大小与变化。learning_rate可以设置为静态的,比如2e-5,或者设置为动态的,tf.keras.optimizers.schedules
中已经提供了部分学习率衰减方法,如:ExponentialDecay、InverseTimeDecay、LearningRateSchedule、PiecewiseConstantDecay、PolynomialDecay等等。
fit
fit方法是tf.keras中内置的训练方法,其除了包含必要的输入数据与训练轮次之外,还有包含很多其他参数,如下所示:
- batch_size: Integer或
None
,每次进行梯度更新的样本数量,默认32- epochs: Integer. 模型训练的轮次Number of epochs to train the model.
- verbose: 0, 1, or 2. 信息显示模式0 = silent, 1 = progress bar, 2 = one line per epoch
- callbacks:
keras.callbacks.Callback
示例列表- validation_split: 0-1之间的浮点数,将多少比例的训练数据用作验证数据
- validation_data: 评估数据
- shuffle: Boolean (每一轮训练之前是否扰乱训练数据) or str (for 'batch')
- class_weight: 字典,类别权重,类别索引为key,权重为值
- sample_weight: 样本权重
- initial_epoch: Integer,从第几轮次开始训练
- steps_per_epoch: Integer or
None
,每轮次训练多少步- validation_steps:
- validation_batch_size:
- validation_freq:
- max_queue_size: Integer. 只用于
keras.utils.Sequence
输入- workers: Integer. 只用于
keras.utils.Sequence
输入- use_multiprocessing: Boolean. 只用于
keras.utils.Sequence
输入
其中validation_split参数可以自动分离训练集留作验证数据,class_weight作为类权重,可以缓解类别不平衡的问题,样本权重sample_weight可以起到类似的作用,其控制程度更细致,能够进一步提高难样本的权重,或者降低简单/无效等样本。
Callback
Callback是训练或评估期间,在不同时间点(某个周期开始时、某个批次结束时、某个周期结束时)调用的对象,这些对象可以实现以下行为:
- 定义验证模型
- 定期或者触发条件进行模型保存
- 训练停滞时改变学习率
- 训练停滞时微调结构
- 训练结束或者触发条件时发送电子邮件或即时消息等
具体使用方式,如下所示:
callbacks = [
# 提前终止训练
tf.keras.callbacks.EarlyStopping(monitor="val_loss", min_delta=1e-2, patience=2, verbose=1),
# 保存中间模型
tf.keras.callbacks.ModelCheckpoint(filepath="mymodel_{epoch}", save_best_only=True, monitor="val_loss", verbose=1),
# 可视化化损失与指标
tf.keras.callbacks.TensorBoard(log_dir="/full_path_to_your_logs", histogram_freq=0, embeddings_freq=0, update_freq="epoch")
]
model.fit(train_data, train_labels, epochs=40, batch_size=512, callbacks=callbacks)
tf.keras.callbacks
中提供了一些内置的callback,我们也可以进行自定义,自定义相关内容后续文章介绍。下面展示了keras中内置的callback:
- BaseLogger:累积每轮训练的平均指标,这个Callback会被keras模型默认调用
- CSVLogger:将每轮的损失与度量结果数据流写入csv文件中
- EarlyStopping:当指定的度量指标停止改进时,停止训练
- History:将事件记录到
History
对象中,这个Callback会被keras模型默认调用- LearningRateScheduler:修改学习率
- ModelCheckpoint:以某种频率保存模型或权重
- ProgbarLogger:向stdout输出度量指标
- ReduceLROnPlateau:当某个指标停止改进时降低学习率
- RemoteMonitor
- TensorBoard:可视化工具
- TerminateOnNaN:当遇到NaN损失时终止训练
多输入、多输出模型
前面的示例中,模型都是单个输入与单个输出,但有很多模型是多个输入或输出,例如上图模型结构所示,我们用以下方法构建模型:
image_input = tf.keras.Input(shape=(32, 32, 3), name="img_input")
timeseries_input = tf.keras.Input(shape=(None, 10), name="ts_input")
x1 = tf.keras.layers.Conv2D(3, 3)(image_input)
x1 = tf.keras.layers.GlobalMaxPooling2D()(x1)
x2 = tf.keras.layers.Conv1D(3, 3)(timeseries_input)
x2 = tf.keras.layers.GlobalMaxPooling1D()(x2)
x = tf.keras.layers.concatenate([x1, x2])
score_output = tf.keras.layers.Dense(1, name="score_output")(x)
class_output = tf.keras.layers.Dense(5, activation="softmax", name="class_output")(x)
model = tf.keras.Model(
inputs=[image_input, timeseries_input], outputs=[score_output, class_output]
)
compile
如果loss或者metrics参数只有单个传递给模型,则每一个输出都用一个loss或者metrics,但在很多情况下不同的输出需要不同的loss或者metrics,我们就需要对应每个输出给出不同的值,如下面所示:
model.compile(
optimizer=tf.keras.optimizers.RMSprop(1e-3),
loss=[tf.keras.losses.MeanSquaredError(), tf.keras.losses.CategoricalCrossentropy()],
metrics=[
[
tf.keras.metrics.MeanAbsolutePercentageError(),
tf.keras.metrics.MeanAbsoluteError(),
],
[tf.keras.metrics.CategoricalAccuracy()],
],
)
loss与metrics都是list的形式,按照output的顺序对应,而我们上面的模型中,已经给输出层进行了命名,则可以通过字典来制定loss与metrics,当输出超过2个的时候,尤其推荐字典的方式。同时,我们还可以使用loss_weights参数来给不同的输出指定权重,具体使用方法如下:
model.compile(
optimizer=tf.keras.optimizers.RMSprop(1e-3),
loss={
"score_output": tf.keras.losses.MeanSquaredError(),
"class_output": tf.keras.losses.CategoricalCrossentropy(),
},
metrics={
"score_output": [
tf.keras.metrics.MeanAbsolutePercentageError(),
tf.keras.metrics.MeanAbsoluteError(),
],
"class_output": [tf.keras.metrics.CategoricalAccuracy()],
},
loss_weights={"score_output": 2.0, "class_output": 1.0},
)
除此之外,如果某些输出不为训练,只用来预测,则可以写成这样:
# list的形式
model.compile(
optimizer=tf.keras.optimizers.RMSprop(1e-3),
loss=[None, tf.keras.losses.CategoricalCrossentropy()],
)
# 或dict的形式
model.compile(
optimizer=tf.keras.optimizers.RMSprop(1e-3),
loss={"class_output": tf.keras.losses.CategoricalCrossentropy()},
)
fit
fit在接受多输入的时候,跟上面loss类似,可以使用numpy数组的list或者dict形式,如下所示:
# 随机生成NumPy数据
img_data = np.random.random_sample(size=(100, 32, 32, 3))
ts_data = np.random.random_sample(size=(100, 20, 10))
score_targets = np.random.random_sample(size=(100, 1))
class_targets = np.random.random_sample(size=(100, 5))
# list形式
model.fit([img_data, ts_data], [score_targets, class_targets], batch_size=32, epochs=1)
# 或者dict形式
model.fit(
{"img_input": img_data, "ts_input": ts_data},
{"score_output": score_targets, "class_output": class_targets},
batch_size=32,
epochs=1,
)
当然可以将数据转换成Dataset格式,然后传给fit,如下面代码所示:
# dataset格式
train_dataset = tf.data.Dataset.from_tensor_slices(
(
{"img_input": img_data, "ts_input": ts_data},
{"score_output": score_targets, "class_output": class_targets},
)
)
train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64)
model.fit(train_dataset, epochs=1)
参考
https://www.tensorflow.org/guide/keras/train_and_evaluate