「导语」模型的训练与评估是整个机器学习工作流程的外围环节。只有把握了正确的训练与评估办法,并灵便应用,能力使咱们更加疾速地进行试验剖析与验证,从而对模型有更加粗浅的了解。
前言
在上一篇 Keras
模型构建的文章中,咱们介绍了在 TensorFlow 2.x
版本中应用 Keras
构建模型的三种办法,那么本篇将在上一篇的根底上着重介绍应用 Keras
模型进行本地训练、评估以及预测的流程和办法。Keras
模型有两种训练评估的形式,一种形式是应用模型内置 API
,如 model.fit()
,model.evaluate()
和 model.predict()
等别离执行不同的操作;另一种形式是利用即时执行策略 (eager execution
) 以及 GradientTape
对象自定义训练和评估流程。对所有 Keras
模型来说这两种形式都是依照雷同的原理来工作的,没有实质上的区别。在个别状况下,咱们更违心应用第一种训练评估形式,因为它更为简略,更易于应用,而在一些非凡的状况下,咱们可能会思考应用自定义的形式来实现训练与评估。
内置 API 进行训练评估
端到端残缺示例
上面介绍应用模型内置 API
实现的一个端到端的训练评估示例,能够认为要应用该模型去解决一个多分类问题。这里应用了 函数式 API
来构建 Keras
模型,当然也能够应用 Sequential
形式以及子类化形式去定义模型。示例代码如下所示:
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
import numpy as np
# Train and Test data from numpy array.
x_train, y_train = (np.random.random((60000, 784)),
np.random.randint(10, size=(60000, 1)),
)
x_test, y_test = (np.random.random((10000, 784)),
np.random.randint(10, size=(10000, 1)),
)
# Reserve 10,000 samples for validation.
x_val = x_train[-10000:]
y_val = y_train[-10000:]
x_train = x_train[:-10000]
y_train = y_train[:-10000]
# Model Create
inputs = keras.Input(shape=(784,), name='digits')
x = layers.Dense(64, activation='relu', name='dense_1')(inputs)
x = layers.Dense(64, activation='relu', name='dense_2')(x)
outputs = layers.Dense(10, name='predictions')(x)
model = keras.Model(inputs=inputs, outputs=outputs)
# Model Compile.
model.compile(
# Optimizer
optimizer=keras.optimizers.RMSprop(),
# Loss function to minimize
loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True),
# List of metrics to monitor
metrics=['sparse_categorical_accuracy'],
)
# Model Training.
print('# Fit model on training data')
history = model.fit(
x_train,
y_train,
batch_size=64,
epochs=3,
# We pass some validation for monitoring validation loss and metrics
# at the end of each epoch
validation_data=(x_val, y_val),
)
print('\nhistory dict:', history.history)
# Model Evaluate.
print('\n# Evaluate on test data')
results = model.evaluate(x_test, y_test, batch_size=128)
print('test loss, test acc:', results)
# Generate predictions (probabilities -- the output of the last layer)
# Model Predict.
print('\n# Generate predictions for 3 samples')
predictions = model.predict(x_test[:3])
print('predictions shape:', predictions.shape)
从代码中能够看到,要实现模型的训练与评估的整体流程,首先要构建好模型;而后要对模型进行编译 (compile
),目标是指定模型训练过程中须要用到的优化器 (optimizer
),损失函数 (losses
) 以及评估指标 (metrics
);接着开始进行模型的训练与穿插验证 (fit
),此步骤须要提前指定好训练数据和验证数据,并设置好一些参数如 epochs
等能力持续,穿插验证操作会在每轮 (epoch
) 训练完结后主动触发;最初是模型评估 (evaluate
) 与预测 (predict
),咱们会依据评估与预测后果来判断模型的好坏。这样一个残缺的模型训练与评估流程就实现了,上面来对示例里的一些实现细节进行开展解说。
模型编译 (compile)
- 在模型训练之前首先要进行模型编译,因为只有晓得了要优化什么指标,如何进行优化以及要关注什么指标,模型能力被正确的训练与调整。
compile
办法蕴含三个主要参数,一个是待优化的损失 (loss
),它指明了要优化的指标,一个是优化器 (optimizer
),它指明了指标优化的方向,还有一个可选的指标项 (metrics
),它指明了训练过程中要关注的模型指标。Keras API
中曾经蕴含了许多内置的损失函数,优化器以及指标,能够拿来即用,可能满足大多数的训练须要。 - 损失函数类次要在
tf.keras.losses
模块下,其中蕴含了多种预约义的损失,比方咱们罕用的二分类损失BinaryCrossentropy
,多分类损失CategoricalCrossentropy
以及均方根损失MeanSquaredError
等。传递给compile
的参数既能够是一个字符串如binary_crossentropy
也能够是对应的losses
实例如tf.keras.losses.BinaryCrossentropy()
,当咱们须要设置损失函数的一些参数时(比方上例中from_logits=True
),则须要应用实例参数。 - 优化器类次要在
tf.keras.optimizers
模块下,一些罕用的优化器如SGD
,Adam
以及RMSprop
等均蕴含在内。同样它也能够通过字符串或者实例的形式传给compile
办法,个别咱们须要设置的优化器参数次要为学习率 (learning rate
),其余的参数能够参考每个优化器的具体实现来动静设置,或者间接应用其默认值即可。 - 指标类次要在
tf.keras.metrics
模块下,二分类里罕用的AUC
指标以及lookalike
里罕用的召回率 (Recall
) 指标等均有蕴含。同理,它也能够以字符串或者实例的模式传递给compile
办法,留神compile
办法接管的是一个metric
列表,所以能够传递多个指标信息。 -
当然如果
losses
模块下的损失或metrics
模块下的指标不满足你的需要,也能够自定义它们的实现。-
对于自定义损失,有两种形式,一种是定义一个损失函数,它接管两个输出参数
y_true
和y_pred
,而后在函数外部计算损失并返回。代码如下:def basic_loss_function(y_true, y_pred): return tf.math.reduce_mean(tf.abs(y_true - y_pred)) model.compile(optimizer=keras.optimizers.Adam(), loss=basic_loss_function)
-
如果你须要的损失函数不仅仅蕴含上述两个参数,则能够采纳另外一种子类化的形式来实现。定义一个类继承自
tf.keras.losses.Loss
类,并实现其__init__(self)
和call(self, y_true, y_pred)
办法,这种实现形式与子类化层和模型比拟类似。比方要实现一个加权的二分类穿插熵损失,其代码如下:class WeightedBinaryCrossEntropy(keras.losses.Loss): """ Args: pos_weight: Scalar to affect the positive labels of the loss function. weight: Scalar to affect the entirety of the loss function. from_logits: Whether to compute loss from logits or the probability. reduction: Type of tf.keras.losses.Reduction to apply to loss. name: Name of the loss function. """ def __init__(self, pos_weight, weight, from_logits=False, reduction=keras.losses.Reduction.AUTO, name='weighted_binary_crossentropy'): super().__init__(reduction=reduction, name=name) self.pos_weight = pos_weight self.weight = weight self.from_logits = from_logits def call(self, y_true, y_pred): ce = tf.losses.binary_crossentropy( y_true, y_pred, from_logits=self.from_logits, )[:, None] ce = self.weight * (ce * (1 - y_true) + self.pos_weight * ce * y_true) return ce model.compile(optimizer=keras.optimizers.Adam(), loss=WeightedBinaryCrossEntropy( pos_weight=0.5, weight=2, from_logits=True, ), )
-
对于自定义指标,也能够通过子类化的形式来实现,首先定义一个指标类继承自
tf.keras.metrics.Metric
类并实现其四个办法,别离是__init__(self)
办法,用来创立状态变量,update_state(self, y_true, y_pred, sample_weight=None)
办法,用来更新状态变量,result(self)
办法,用来返回状态变量的最终后果,以及reset_states(self)
办法,用来从新初始化状态变量。比方要实现一个多分类中真正例 (True Positives) 数量
的统计指标,其代码如下:class CategoricalTruePositives(keras.metrics.Metric): def __init__(self, name='categorical_true_positives', **kwargs): super().__init__(name=name, **kwargs) self.true_positives = self.add_weight(name='tp', initializer='zeros') def update_state(self, y_true, y_pred, sample_weight=None): y_pred = tf.reshape(tf.argmax(y_pred, axis=1), shape=(-1, 1)) values = tf.cast(y_true, 'int32') == tf.cast(y_pred, 'int32') values = tf.cast(values, 'float32') if sample_weight is not None: sample_weight = tf.cast(sample_weight, 'float32') values = tf.multiply(values, sample_weight) self.true_positives.assign_add(tf.reduce_sum(values)) def result(self): return self.true_positives def reset_states(self): # The state of the metric will be reset at the start of each epoch. self.true_positives.assign(0.) model.compile(optimizer=keras.optimizers.RMSprop(learning_rate=1e-3), loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True), metrics=[CategoricalTruePositives()], )
- 对于一些在层 (
layers
) 外部定义的损失,能够通过在自定义层的call
办法里调用self.add_loss()
来实现,而且在模型训练时,它会主动增加到整体的损失中,不必人为干涉。通过比照退出自定义损失前后模型训练输入的loss
值的变动来确认这部分损失是否被退出到了整体的损失中。还能够在build
模型后,打印model.losses
来查看该模型的所有损失。留神正则化损失是内置在Keras
的所有层中的,只须要在调用层时退出相应正则化参数即可,无需在call
办法中add_loss()
。 - 对于指标信息来说,能够在自定义层的
call
办法里调用self.add_metric()
来新增指标,同样的,它也会主动呈现在整体的指标中,无需人为干涉。 -
函数式 API
实现的模型,能够通过调用model.add_loss()
和model.add_metric()
来实现与自定义模型同样的成果。示例代码如下:import tensorflow as tf from tensorflow import keras from tensorflow.keras import layers inputs = keras.Input(shape=(784,), name='digits') x1 = layers.Dense(64, activation='relu', name='dense_1')(inputs) x2 = layers.Dense(64, activation='relu', name='dense_2')(x1) outputs = layers.Dense(10, name='predictions')(x2) model = keras.Model(inputs=inputs, outputs=outputs) model.add_loss(tf.reduce_sum(x1) * 0.1) model.add_metric(keras.backend.std(x1), name='std_of_activation', aggregation='mean', ) model.compile(optimizer=keras.optimizers.RMSprop(1e-3), loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True), ) model.fit(x_train, y_train, batch_size=64, epochs=1)
-
- 如果要编译的是多输出多输入模型,则能够为每一个输入指定不同的损失函数以及不同的指标,前面会具体介绍。
模型训练与验证 (fit)
- 模型的训练通过调用
model.fit()
办法来实现,fit
办法包含训练数据与验证数据参数,它们能够是numpy
类型数据,也能够是tf.data
模块下dataset
类型的数据。另外fit
办法还包含epochs
,batch_size
以及steps_per_epoch
等管制训练流程的参数,并且还能够通过callbacks
参数管制模型在训练过程中执行一些其它的操作,如Tensorboard
日志记录等。 -
模型的训练和验证数据能够是
numpy
类型数据,最开始的端到端示例即是采纳numpy
数组作为输出。个别在数据量较小且内存能容下的状况下采纳numpy
数据作为训练和评估的数据输出。- 对于
numpy
类型数据来说,如果指定了epochs
参数,则训练数据的总量为原始样本数量 * epochs
。 - 默认状况下一轮训练 (
epoch
) 所有的原始样本都会被训练一遍,下一轮训练还会应用这些样本数据进行训练,每一轮执行的步数 (steps
) 为原始样本数量 /batch_size
,如果batch_size
不指定,默认为32
。穿插验证在每一轮训练完结后
触发,并且也会在所有验证样本上执行一遍,能够指定validation_batch_size
来管制验证数据的batch
大小,如果不指定默认同batch_size
。 - 对于
numpy
类型数据来说,如果设置了steps_per_epoch
参数,示意一轮要训练指定的步数,下一轮会在上轮根底上应用下一个batch
的数据持续进行训练,直到所有的epochs
完结或者训练数据的总量
被耗尽。要想训练流程不因数据耗尽而完结,则须要保障数据的总量
要大于steps_per_epoch * epochs * batch_size
。同理也能够设置validation_steps
,示意穿插验证所需步数,此时要留神验证集的数据总量要大于validation_steps * validation_batch_size
。 fit
办法还提供了另外一个参数validation_split
来主动从训练数据集中保留肯定比例的数据作为验证,该参数取值为0-1
之间,比方0.2
代表20%
的训练集用来做验证,fit
办法会默认保留numpy
数组最初面20%
的样本作为验证集。
- 对于
-
TensorFlow 2.0
之后,更为举荐的是应用tf.data
模块下dataset
类型的数据作为训练和验证的数据输出,它能以更加疾速以及可扩大的形式加载和预处理数据。-
应用
dataset
进行训练的代码如下:train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train)) # Shuffle and slice the dataset. train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64) # Prepare the validation dataset val_dataset = tf.data.Dataset.from_tensor_slices((x_val, y_val)) val_dataset = val_dataset.batch(64) # Now we get a test dataset. test_dataset = tf.data.Dataset.from_tensor_slices((x_test, y_test)) test_dataset = test_dataset.batch(64) # Since the dataset already takes care of batching, # we don't pass a `batch_size` argument. model.fit(train_dataset, epochs=3, validation_data=val_dataset) result = model.evaluate(test_dataset)
dataset
个别是一个二元组,第一个元素为模型的输出特色,如果为多输出就是多个特色的字典 (dict
) 或元组 (tuple
),第二个元素是实在的数据标签 (label
),即ground truth
。- 应用
from_tensor_slices
办法能够从nunpy
数组间接生成dataset
类型数据,是一种比拟方便快捷的生成形式,个别在测试时应用。其它较为罕用的生成形式,比方从TFRecord
文件或文本文件 (TextLine
) 中生成dataset
,能够参考tf.data
模块下的相干类的具体实现。 dataset
能够调用内置办法提前对数据进行预处理,比方数据打乱 (shuffle
),batch
以及repeat
等操作。shuffle
操作是为了减小模型过拟合的几率,它仅为小范畴打乱,须要借助于一个缓存区,先将数据填满,而后在每次训练时从缓存区里随机抽取batch_size
条数据,产生的空缺用前面的数据填充,从而实现了部分打乱的成果。batch
是对数据进行分批次,罕用于管制和调节模型的训练速度以及训练成果,因为在dataset
中曾经batch
过,所以fit
办法中的batch_size
就无需再提供了。repeat
用来对数据进行复制,以解决数据量有余的问题,如果指定了其参数count
,则示意整个数据集要复制count
次,不指定就会有限次复制
,此时必须要设置steps_per_epoch
参数,不然训练无奈终止。- 上述例子中,
train dataset
的全副数据在每一轮都会被训练到,因为一轮训练完结后,dataset
会重置,而后被用来从新训练。然而当指定了steps_per_epoch
之后,dataset
在每轮训练后不会被重置,始终到所有epochs
完结或所有的训练数据被耗费完之后终止,要想训练失常完结,须保障提供的训练数据总量要大于steps_per_epoch * epochs * batch_size
。同理也能够指定validation_steps
,此时数据验证会执行指定的步数,在下次验证开始时,validation dataset
会被重置,以保障每次穿插验证应用的都是雷同的数据。validation_split
参数不适用于dataset
类型数据,因为它须要晓得每个数据样本的索引,这在dataset API
下很难实现。 - 当不指定
steps_per_epoch
参数时,numpy
类型数据与dataset
类型数据的解决流程完全一致。但当指定之后,要留神它们之间在解决上的差别。对于numpy
类型数据而言,在解决时,它会被转为dataset
类型数据,只不过这个dataset
被repeat
了epochs
次,而且每轮训练完结后,这个dataset
不会被重置,会在上次的batch
之后持续训练。假如原始数据量为n
,指定steps_per_epoch
参数之后,两者的差别次要体现在实在的训练数据量上,numpy
为n * epochs
,dataset
为n
。具体细节能够参考源码实现。 dataset
还有map
与prefetch
办法比拟实用。map
办法接管一个函数
作为参数,用来对dataset
中的每一条数据进行解决并返回一个新的dataset
,比方咱们在应用TextLineDataset
读取文本文件后生成了一个dataset
,而咱们要抽取输出数据中的某些列作为特色 (features
),某些列作为标签 (labels
),此时就会用到map
办法。prefetch
办法事后从dataset
中筹备好下次训练所需的数据并放于内存中,这样能够缩小每轮训练之间的提早等待时间。
-
-
除了训练数据和验证数据外,还能够向
fit
办法传递样本权重 (sample_weight
) 以及类别权重 (class_weight
) 参数。这两个参数通常被用于解决分类不均衡问题,通过给类别少的样本赋予更高的权重,使得各个类别对整体损失的奉献趋于统一。-
对于
numpy
类型的输出数据,能够应用上述两个参数,以下面的多分类问题为例,如果要给分类5
一个更高的权重,能够应用如下代码来实现:import numpy as np # Here's the same example using `class_weight` class_weight = {0: 1., 1: 1., 2: 1., 3: 1., 4: 1., # Set weight "2" for class "5", # making this class 2x more important 5: 2., 6: 1., 7: 1., 8: 1., 9: 1.} print('Fit with class weight') model.fit(x_train, y_train, class_weight=class_weight, batch_size=64, epochs=4) # Here's the same example using `sample_weight` instead: sample_weight = np.ones(shape=(len(y_train), )) sample_weight[y_train == 5] = 2. print('\nFit with sample weight') model.fit( x_train, y_train, sample_weight=sample_weight, batch_size=64, epochs=4, )
-
而对于
dataset
类型的输出数据来说,不能间接应用上述两个参数,须要在构建dataset
时将sample_weight
退出其中,返回一个三元组的dataset
,格局为(input_batch, target_batch, sample_weight_batch)
。示例代码如下所示:sample_weight = np.ones(shape=(len(y_train), )) sample_weight[y_train == 5] = 2. # Create a Dataset that includes sample weights # (3rd element in the return tuple). train_dataset = tf.data.Dataset.from_tensor_slices(( x_train, y_train, sample_weight, )) # Shuffle and slice the dataset. train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64) model.fit(train_dataset, epochs=3)
-
-
在模型的训练过程中有一些非凡工夫点,比方在一个
batch
完结或者一个epoch
完结时,个别都会做一些额定的解决操作来辅助咱们进行训练,下面介绍过的模型穿插验证就是其中之一。还有一些其它的操作,比方当模型训练停滞不前时 (loss
值在某一值左近一直稳定),主动减小其学习速率 (learning rate
) 以使损失持续降落,从而失去更好的收敛成果;在训练过程中保留模型的权重信息,以备重启模型时能够在已有权重的根底上持续训练,从而缩小训练工夫;还有在每轮的训练完结后记录模型的损失 (loss
) 和指标 (metrics
) 信息,以供Tensorboard
剖析应用等等,这些操作都是模型训练过程中不可或缺的局部。它们都能够通过回调函数 (callbacks
) 的形式来实现,这些回调函数都在tf.keras.callbacks
模块下,能够将它们作为列表参数传递给fit
办法以达到不同的操作目标。-
上面以
EarlyStopping
为例阐明callbacks
的应用形式。本例中,当穿插验证损失val_loss
至多在2
轮 (epochs
) 训练中的缩小值都低于1e-2
时,咱们会提前进行训练。其示例代码如下所示:callbacks = [ keras.callbacks.EarlyStopping( # Stop training when `val_loss` is no longer improving monitor='val_loss', # "no longer improving" being defined as "no better than 1e-2 less" min_delta=1e-2, # "no longer improving" being further defined as "for at least 2 epochs" patience=2, verbose=1, ) ] model.fit( x_train, y_train, epochs=20, batch_size=64, callbacks=callbacks, validation_split=0.2, )
- 一些比拟罕用的
callbacks
须要理解并把握,如ModelCheckpoint
用来保留模型权重信息,TensorBoard
用来记录一些指标信息,ReduceLROnPlateau
用来在模型停滞时减小学习率。更多的callbacks
函数能够参考tf.keras.callbacks
模块下的实现。 -
当然也能够自定义
callbacks
类,该子类须要继承自tf.keras.callbacks.Callback
类,并按需实现其内置的办法,比方如果须要在每个batch
训练完结后记录loss
的值,则能够应用如下代码实现:class LossHistory(keras.callbacks.Callback): def on_train_begin(self, logs): self.losses = [] def on_batch_end(self, batch, logs): self.losses.append(logs.get('loss'))
- 在
TensorFlow 2.0
之前,ModelCheckpoint
内容和TensorBoard
内容是同时记录的,保留在雷同的文件夹下,而在2.0
之后的keras API
中它们能够通过不同的回调函数离开指定。记录的日志文件中,含有checkpoint
关键字的文件个别为检查点文件,含有events.out.tfevents
关键字的文件个别为Tensorboard
相干文件。
-
多输入输出模型
-
思考如图所示的多输出多输入模型,该模型包含两个输出和两个输入,
score_output
输入示意分值,class_output
输入示意分类,其示例代码如下:from tensorflow import keras from tensorflow.keras import layers image_input = keras.Input(shape=(32, 32, 3), name='img_input') timeseries_input = keras.Input(shape=(None, 10), name='ts_input') x1 = layers.Conv2D(3, 3)(image_input) x1 = layers.GlobalMaxPooling2D()(x1) x2 = layers.Conv1D(3, 3)(timeseries_input) x2 = layers.GlobalMaxPooling1D()(x2) x = layers.concatenate([x1, x2]) score_output = layers.Dense(1, name='score_output')(x) class_output = layers.Dense(5, name='class_output')(x) model = keras.Model(inputs=[image_input, timeseries_input], outputs=[score_output, class_output], )
-
在进行模型编译时,如果只指定一个
loss
显著不能满足不同输入的损失计算形式,所以此时能够指定loss
为一个列表 (list
),其中每个元素别离对应于不同的输入。示例代码如下:model.compile(optimizer=keras.optimizers.RMSprop(1e-3), loss=[keras.losses.MeanSquaredError(), keras.losses.CategoricalCrossentropy(from_logits=True) ], loss_weights=[1, 1], )
此时模型的优化指标为所有单个损失值的总和,如果想要为不同的损失指定不同的权重,能够设置
loss_weights
参数,该参数接管一个标量系数列表 (list
),用以对模型不同输入的损失值进行加权。如果仅为模型指定一个loss
,则该loss
会利用到每一个输入,在模型的多个输入损失计算形式雷同时,能够采纳这种形式。 -
同样的对于模型的指标 (
metrics
),也能够指定为多个,留神因为metrics
参数自身即为一个列表,所以为多个输入指定metrics
应该应用二维列表。示例代码如下:model.compile(optimizer=keras.optimizers.RMSprop(1e-3), loss=[keras.losses.MeanSquaredError(), keras.losses.CategoricalCrossentropy(from_logits=True), ], metrics=[ [keras.metrics.MeanAbsolutePercentageError(), keras.metrics.MeanAbsoluteError()], [keras.metrics.CategoricalAccuracy()], ], )
-
对于有明确名称的输入,能够通过字典的形式来设置其
loss
和metrics
。示例代码如下:model.compile(optimizer=keras.optimizers.RMSprop(1e-3), loss={'score_output': keras.losses.MeanSquaredError(), 'class_output': keras.losses.CategoricalCrossentropy(from_logits=True), }, metrics={ 'score_output': [keras.metrics.MeanAbsolutePercentageError(), keras.metrics.MeanAbsoluteError()], 'class_output': [keras.metrics.CategoricalAccuracy(), ] }, )
-
对于仅被用来预测的输入,也能够不指定其
loss
。示例代码如下:model.compile(optimizer=keras.optimizers.RMSprop(1e-3), loss=[ None, keras.losses.CategoricalCrossentropy(from_logits=True), ], ) # Or dict loss version model.compile(optimizer=keras.optimizers.RMSprop(1e-3), loss={'class_output': keras.losses.CategoricalCrossentropy(from_logits=True), }, )
-
对于多输入输出模型的训练来说,也能够采纳和其
compile
办法雷同的形式来提供数据输出,也就是说既能够应用列表的形式,也能够应用字典的形式来指定多个输出。-
numpy
类型数据示例代码如下:# Generate dummy Numpy data 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)) # Fit on lists model.fit(x=[img_data, ts_data], y=[score_targets, class_targets], batch_size=32, epochs=3, ) # Alternatively, fit on dicts model.fit( x={ 'img_input': img_data, 'ts_input': ts_data, }, y={ 'score_output': score_targets, 'class_output': class_targets, }, batch_size=32, epochs=3, )
-
dataset
类型数据示例代码如下:# Generate dummy dataset data from numpy train_dataset = tf.data.Dataset.from_tensor_slices(((img_data, ts_data), (score_targets, class_targets), )) # Alternatively generate with dict 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=3)
-
自定义训练流程
- 如果你不想应用
model
内置提供的fit
和evaluate
办法,而想应用低阶API
自定义模型的训练和评估的流程,则能够借助于GradientTape
来实现。深度神经网络在后向流传过程中须要计算损失 (loss
) 对于权重矩阵的导数(也称为梯度),以更新权重矩阵并取得最优解,而GradientTape
能主动提供求导帮忙,无需咱们手动求导,它实质上是一个求导记录器
,可能记录前项流传的过程,并据此计算导数。 -
模型的构建过程与之前相比没有什么不同,次要体现在训练的局部,示例代码如下:
import tensorflow as tf from tensorflow import keras from tensorflow.keras import layers import numpy as np # Get the model. inputs = keras.Input(shape=(784,), name='digits') x = layers.Dense(64, activation='relu', name='dense_1')(inputs) x = layers.Dense(64, activation='relu', name='dense_2')(x) outputs = layers.Dense(10, name='predictions')(x) model = keras.Model(inputs=inputs, outputs=outputs) # Instantiate an optimizer. optimizer = keras.optimizers.SGD(learning_rate=1e-3) # Instantiate a loss function. loss_fn = keras.losses.SparseCategoricalCrossentropy(from_logits=True) # Prepare the metrics. train_acc_metric = keras.metrics.SparseCategoricalAccuracy() val_acc_metric = keras.metrics.SparseCategoricalAccuracy() # Prepare the training dataset. batch_size = 64 train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train)) train_dataset = train_dataset.shuffle(buffer_size=1024).batch(batch_size) # Prepare the validation dataset. val_dataset = tf.data.Dataset.from_tensor_slices((x_val, y_val)) val_dataset = val_dataset.batch(64) epochs = 3 for epoch in range(epochs): print('Start of epoch %d' % (epoch,)) # Iterate over the batches of the dataset. for step, (x_batch_train, y_batch_train) in enumerate(train_dataset): # Open a GradientTape to record the operations run # during the forward pass, which enables autodifferentiation. with tf.GradientTape() as tape: # Run the forward pass of the layer. # The operations that the layer applies # to its inputs are going to be recorded # on the GradientTape. logits = model(x_batch_train, training=True) # Logits for this minibatch # Compute the loss value for this minibatch. loss_value = loss_fn(y_batch_train, logits) # Use the gradient tape to automatically retrieve # the gradients of the trainable variables with respect to the loss. grads = tape.gradient(loss_value, model.trainable_weights) # Run one step of gradient descent by updating # the value of the variables to minimize the loss. optimizer.apply_gradients(zip(grads, model.trainable_weights)) # Update training metric. train_acc_metric(y_batch_train, logits) # Log every 200 batches. if step % 200 == 0: print('Training loss (for one batch) at step %s: %s' % (step, float(loss_value))) print('Seen so far: %s samples' % ((step + 1) * 64)) # Display metrics at the end of each epoch. train_acc = train_acc_metric.result() print('Training acc over epoch: %s' % (float(train_acc), )) # Reset training metrics at the end of each epoch train_acc_metric.reset_states() # Run a validation loop at the end of each epoch. for x_batch_val, y_batch_val in val_dataset: val_logits = model(x_batch_val) # Update val metrics val_acc_metric(y_batch_val, val_logits) val_acc = val_acc_metric.result() val_acc_metric.reset_states() print('Validation acc: %s' % (float(val_acc), ))
- 留神
with tf.GradientTape() as tape
局部的实现,它记录了前向流传的过程,而后应用tape.gradient
办法计算出loss
对于模型所有权重矩阵 (model.trainable_weights
) 的导数(也称作梯度),接着利用优化器 (optimizer
) 去更新所有的权重矩阵。 - 在上述训练流程中,模型的训练指标在每个
batch
的训练中进行更新操作 (update_state()
),在一个epoch
训练完结后打印指标的后果 (result()
),而后重置该指标 (reset_states()
) 并进行下一轮的指标记录,穿插验证的指标也是同样的操作。 -
留神与应用模型内置
API
进行训练不同,在自定义训练中,模型中定义的损失,比方正则化损失以及通过add_loss
增加的损失,是不会主动累加在loss_fn
之内的。如果要蕴含这部分损失,则须要批改自定义训练的流程,通过调用model.losses
来将模型的全副损失退出到要优化的损失中去。示例代码如下所示:with tf.GradientTape() as tape: logits = model(x_batch_train) loss_value = loss_fn(y_batch_train, logits) # Add extra losses created during this forward pass: loss_value += sum(model.losses) grads = tape.gradient(loss_value, model.trainable_weights) optimizer.apply_gradients(zip(grads, model.trainable_weights))
参考资料
- Keras 模型训练与评估
- Keras 模型 fit 办法
- tf.data.Dataset