import numpy as np
import tensorflow as tf
inputs = tf.keras.Input(shape=(32,)) # 数据维度为32
x = tf.keras.layers.Dense(64, activation='relu')(inputs) # 64个隐藏神经元
x = tf.keras.layers.Dense(64, activation='relu')(x) # 64个隐藏神经元
predictions = tf.keras.layers.Dense(10)(x) # 输出10分类结果
# - inputs:模型的输入
# - outputs:模型的输出
model = tf.keras.Model(inputs=inputs, outputs=predictions)
# 指定损失函数(loss) tf.keras.optimizers.RMSprop
# 优化器(optimizer) tf.keras.SparseCategoricalCrossentropy
# 指标(metrics) ['accuracy']
model.compile(optimizer=tf.keras.optimizers.RMSprop(0.001),
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=['accuracy'])
# 构造训练集、验证集、测试集
x_train = np.random.random((1000,32)) # 1000条数据,维度为32
y_train = np.random.randint(10, size=(1000,))
x_val = np.random.random((200, 32)) # 200条数据,维度为32
y_val = np.random.randint(10, size=(200,))
x_test = np.random.random((200, 32)) # 200条数据,维度为32
y_te t = np.random.randint(10, size=(200,))
tf2.0提供了许多内置的优化器,损失函数和评价指标。通常不需要从头开始创建自己的损失函数、评价指标或者优化器,因为所需的可能已经是Keras API的一部分。
构造模型后,通过将数据切成大小为"batch_size"的"批"来训练模型,并针对给定数量的"epoch"重复遍历整个数据集。
# help(model.fit) 查看model.fit()的各项功能
model.fit(x_train, y_train, batch_size=32, epochs=5, validation_data=(x_val, y_val))
上面代码中使用validation_data=(x_val, y_val)表示验证集,其中(x_val, y_val)是一个元组。参数validation_data将Numpy的元组传递(x_val, y_val)给模型,以在每个时期结束时评估验证损失和验证指标。
除了使用参数validation_data传递验证集以外,还可以使用参数validation_split,validation_split允许自动保留部分训练数据以供验证,参数值表示要保留用于验证的数据的一部分,因此应将其设置为大于0小于1的数字。例如,validation_split=0.2表示“使用20%的训练数据作为验证集”,而validation_split=0.6表示“使用60%的数据用于验证”。
验证的计算方法是在进行任何改组之前,对fit调用接收到的数组进行最后x%的采样。
注意,validation_split只能在使用Numpy进行训练时使用。
results = model.evaluate(x_test, y_test, batch_size=128)
predictions = model.predict(x_test[:3])
在模型训练的时候,除了输入数据和目标数据外,还可以在使用时将样本权重或类别权重传递给模型fit。
从Numpy数据进行训练时,通过sample_weight和class_weight参数设置样本权重或者累呗权重。
在数据集训练时,通过使数据集返回一个元组(input_batch, target_batch, sample_weight_batch)。“样本权重”数组是一个数字数组,用于指定批次中每个样本在计算总损失时应具有的权重,它通常用于不平衡的分类问题中(这种想法是为很少见的类别赋予更多的权重)。当使用的权重为1和0时,该数组可以作为损失函数的掩码(完全丢弃某些样本对总损失的贡献)。
“类别权重”字典是同一个概念的一个更具体的实例,它将类别索引映射到应该属于该类别的样本的样本权重。例如,如果在数据中类“0”的表示量比类“1”的表示量少两倍,则可以使用class_weight={0:1,1:0.5}。
下面列举一个Numpy示例,其中使用类权重或样本权重来更加重视第5类的正确分类。
构建模型
import tensorflow as tf
def get_uncompiled_model():
inputs = tf.keras.Input(shape=(32,), name='digits')
x = tf.keras.layers.Dense(64, activation='relu', name="dense_1")(inputs)
x = tf.keras.layers.Dense(64, activation='relu', name='dense_2')(x)
outputs = tf.keras.layers.Dense(10, name="predictions")(x)
model = tf.keras.Model(inputs=inputs,outputs=outputs)
return model
def get_compiled_model():
model = get_uncompiled_model()
model.compile(optimizer=tf.keras.optimizers.RMSprop(learning_rate=1e-3),
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=['sparse_categorical_accuracy'])
return model
类别加权
import numpy as np
# 构造训练集、验证集、测试集
x_train = np.random.random((1000,32)) # 1000条数据,维度为32
y_train = np.random.randint(10, size=(1000,))
x_val = np.random.random((200, 32)) # 200条数据,维度为32
y_val = np.random.randint(10, size=(200,))
x_test = np.random.random((200, 32)) # 200条数据,维度为32
y_test = np.random.randint(10, size=(200,))
# 类别5:加权
class_weight = {0:1, 1:1, 2:1, 3:1, 4:1, 5:2, 6:1, 7:1, 8:1, 9:1}
model = get_compiled_model()
model.fit(x_train, y_train, class_weight=class_weight, batch_size=64, epoch=4)
样本加权
# 样本加权
import numpy as np
# 构造训练集、验证集、测试集
x_train = np.random.random((1000,32)) # 1000条数据,维度为32
y_train = np.random.randint(10, size=(1000,))
x_val = np.random.random((200, 32)) # 200条数据,维度为32
y_val = np.random.randint(10, size=(200,))
x_test = np.random.random((200, 32)) # 200条数据,维度为32
y_test = np.random.randint(10, size=(200,))
sample_weight = np.ones(shape=(len(y_train),))
sample_weight[y_train==5] = 2
model = get_compiled_model()
model.fit(x_train, y_train, sample_weight=sample_weight, batch_size=64, epoch=4)
Keras中的回调是在训练期间(在某个时期开始,在批处理结束时,在某个时期结束时等)在不同时间点调用的对象,这些对象可用于实现以下行为:
# 早停的应用示例
model = get_compiled_model()
# callbacks相当于list
callbacks = [
tf.keras.callbacks.EarlyStopping(
# 当“val_loss”不再下降时停止训练,'val_loss'为监控的指标
monitor='val_loss',
# “不再下降”被定义为“减少不超过1e-2”
min_delta=1e-2,
# “不再改善”进一步定义为“至少2个epoch”
patience=2,
verbose=1)
]
model.fit(x_train, y_train,
epoch=20,
batch_size=64,
callbacks=callbacks,
validation_split=0.2)
在相对较大的数据集上训练模型时,至关重要的是要定期保存模型的checkpoint,最简单的方法是使用ModelCheckpoint回调。
# 模型保存checkpoint示例
model = get_compiled_model()
callbacks = [
tf.keras.callbacks.ModelCheckpoint(
filepath='mymodel_{epoch}', # 模型保存路径
save_best_only=True,
monitor='val_loss',
save_weights_only=True, # 保存模型权重
verbose=1
)
]
model.fit(x_train, y_train,
epoch=20,
batch_size=64,
callbacks=callbacks,
validation_split=0.2)
由于优化程序无法访问验证指标,因此无法使用这些计划对象来实现动态学习率计划(例如当验证损失不再改善的时候降低学习率);
但是回调确实可以访问所有指标,包括验证指标!因此可以通过使用回调函数来修改优化程序上的当前学习率,从而实现此模式。实际上,它是作为ReduceLROnPlateau回调内置的。
ReduceLROnPlateau参数:
# 动态调整学习率示例
model = get_compiled_model()
callbacks = [
tf.keras.callbacks.ModelCheckpoint(
filepath='mymodel_{epoch}', # 模型保存路径
save_best_only=True,
monitor='val_loss',
save_weights_only=True, # 保存模型权重
verbose=1
),
tf.keras.callbacks.ReduceLROnPlateau(monitor="val_sparse_categorical_accuracy",
verbose=1,
mode='max',
factor=0.5,
patience=3)
]
model.fit(x_train, y_train,
epoch=20,
batch_size=64,
callbacks=callbacks,
validation_split=0.2)
许多内置的回调函数可用:
在前面的示例中,我们考虑一个具有单个输入(shape的张量(32,))和单个输出(shape的预测张量(10,))的模型,但是具有多个输入或输出的模型呢?
考虑以下模型,该模型具有形状的图像输入(32,32,3),即(height,width,channels)和形状的时间序列输入(None,10),即(timesteps,features)。我们的模型将具有根据这些输入的组合计算出的两个输出:“得分”(形状为(1,))和五类(形状为(5,))的概率分布。
image_input = tf.keras.Input(shape=(32,32,3), name='img_input')
timeseries_input = tf.keras.Input(shape=(20,10), name='ts_input')
x1 = tf.keras.layers.Conv2D(3, 3)(image_input)
x1 = tf.keras.layers.GlobalAveragePooling2D()(x1)
x2 = tf.keras.layers.Conv1D(3, 3)(timeseries_input)
x2 = tf.keras.layers.GlobalAveragePooling1D()(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, name='class_output')(x)
model = tf.keras.Model(inputs=[image_input, timeseries_input], outputs=[score_output, class_output])
下面绘制这个模型,以便可以清楚知道我们在这里做什么(请注意,图中显示的形状是批处理形状,而不是按样本的形状)。
我们通过tf.keras.utils.plot_model
进行可视化操作,在window中直接使用该方法可能会出现一些问题,我们可以通过博客尝试解决这个问题。
在编译时,通过将损失函数作为列表传递,我们可以为不同的输出指定不同的损失:
model.compile(
optimizer=tf.keras.optimizers.RMSprop(1e-3),
loss=[tf.keras.losses.MeanSquaredError(),
tf.keras.losses.CategoricalCrossentropy(from_logits=True)]
)
如果我们仅使用单个损失函数传递给模型,则将相同的损失函数应用于每个输出,这在此处不合适。
model.compile(
optimizer=tf.keras.optimizers.RMSprop(1e-3),
loss=[tf.keras.losses.MeanSquaredError(),
tf.keras.losses.CategoricalCrossentropy(from_logits=True)],
metrics = [
[tf.keras.metrics.MeanAbsolutePercentageError(),
tf.keras.metrics.MeanAbsoluteError()],
[tf.keras.metrics.CategoricalAccuracy()]
]
)
由于我们为输出层命名,因此还可以通过dict指定每个输出的损失和指标:
model.compile(
optimizer=tf.keras.optimizers.RMSprop(1e-3),
loss={'score_output':tf.keras.losses.MeanSquaredError(),
'class_output':tf.keras.losses.CategoricalCrossentropy(from_logits=True)},
metrics={'score_output':[tf.keras.metrics.MeanAbsolutePercentageError(),
tf.keras.metrics.MeanAbsoluteError()],
'class_output':[tf.keras.metrics.CategoricalAccuracy()]})
如果有两个以上的输出,建议使用显式名称和字典。
可以使用以下参数对不同的特定于输出的损失赋予不同的权重(例如在我们的示例中,我们可能希望给某类损失函数赋予更高的权重)
model.compile(
optimizer=tf.keras.optimizers.RMSprop(1e-3),
loss={'score_output':tf.keras.losses.MeanSquaredError(),
'class_output':tf.keras.losses.CategoricalCrossentropy(from_logits=True)},
metrics={'score_output':[tf.keras.metrics.MeanAbsolutePercentageError(),
tf.keras.metrics.MeanAbsoluteError()],
'class_output':[tf.keras.metrics.CategoricalAccuracy()]},
loss_weight={'score_output':2.,'class_output':1.})
如果某些输出仅用于预测而不是训练,我们可以选择不为某些输出计算损失。
model.compile(
optimizer=tf.keras.optimizers.RMSprop(1e-3),
loss=[None, tf.keras.losses.CategoricalCrossentropy(from_logits=True)])
model.compile(
optimizer=tf.keras.optimizers.RMSprop(1e-3),
loss={'class_output':tf.keras.losses.CategoricalCrossentropy(from_logits=True)})
import tensorflow as tf
import numpy as np
image_input = tf.keras.Input(shape=(32,32,3), name='img_input')
timeseries_input = tf.keras.Input(shape=(20,10), name='ts_input')
x1 = tf.keras.layers.Conv2D(3, 3)(image_input)
x1 = tf.keras.layers.GlobalAveragePooling2D()(x1)
x2 = tf.keras.layers.Conv1D(3, 3)(timeseries_input)
x2 = tf.keras.layers.GlobalAveragePooling1D()(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, name='class_output')(x)
model = tf.keras.Model(inputs=[image_input, timeseries_input], outputs=[score_output, class_output])
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))
model.compile(
optimizer=tf.keras.optimizers.RMSprop(1e-3),
loss=[tf.keras.losses.MeanSquaredError(),
tf.keras.losses.CategoricalCrossentropy(from_logits=True)],
metrics = [
[tf.keras.metrics.MeanAbsolutePercentageError(),
tf.keras.metrics.MeanAbsoluteError()],
[tf.keras.metrics.CategoricalAccuracy()]
]
)
# 两种训练方式
model.fit([img_data, ts_data], [score_targets, class_targets], batch_size=32, epochs=3)
model.fit({'img_input':img_data, 'ts_input':ts_data},
{'score_output':score_targets, 'class_output':class_targets},
batch_size=32,
epochs=3)
tensorflow中的梯度求解利器:tf.GradientTape
。
GradientTape是eager模式下计算梯度用的,而eager模式是TensorFlow 2.0的默认模式。
举个例子,计算 y = x 2 y=x^2 y=x2在 x = 3 x=3 x=3时的导数:
import tensorflow as tf
x = tf.constant(3.0)
with tf.GradientTape() as g:
g.watch(x) # watch的作用:确保某个tensor被tape追踪
y = x * x
dy_dx = g.gradient(y,x) # gradient的作用:根据tape上面的上下文来计算某个或者某些tensor的梯度
tf.GradientTape(persistent=False,
watch_accessed_variables=True)
gradient(target,sources)
watch(tensor)
apply_gradients(grads_and_vars, name=None)
大概的使用方法如下面代码所示:
loss_object = tf.keras.losses.CategoricalCrossentropy()
optimizer = tf.keras.optimizers.Adam()
with tf.GradientTape() as tape:
predictions = model(data)
loss = loss_object(labels, predictions)
gradients = tape.gradient(loss, model.trainable_variables)
optimizer.apply_gradients(zip(gradients, model.trainable_variables))
import numpy as np
import tensorflow as tf
class MyModel(tf.keras.Model):
def __init__(self, num_classes=10):
super(MyModel, self).__init__(name='my_model')
self.num_classes = num_classes
# 定义自己需要的层
self.dense_1 = tf.keras.layers.Dense(32, activation='relu')
self.dense_2 = tf.keras.layers.Dense(self.num_classes)
def call(self, inputs):
# 定义前向传播
x = self.dense_1(inputs)
return self.dense_2(x)
data = np.random.random((1000, 32))
labels = np.random.random((1000, 10))
model = MyModel(num_classes=10)
loss_object = tf.keras.losses.CategoricalCrossentropy(from_logits=True)
optimizer = tf.keras.optimizers.Adam()
with tf.GradientTape() as tape:
predictions = model(data)
loss = loss_object(labels, predictions)
gradients = tape.gradient(loss, model.trainable_variables)
optimizer.apply_gradients(zip(gradients, model.trainable_variables))
import tensorflow as tf
class MyModel(tf.keras.Model):
def __init__(self, num_classes=10):
super(MyModel, self).__init__(name='my_model')
self.num_classes = num_classes
# 定义自己需要的层
self.dense_1 = tf.keras.layers.Dense(32, activation='relu')
self.dense_2 = tf.keras.layers.Dense(self.num_classes)
def call(self, inputs):
# 定义前向传播
x = self.dense_1(inputs)
return self.dense_2(x)
import numpy as np
data = np.random.random((1000, 32))
labels = np.random.random((1000, 10))
model = MyModel(num_classes=10)
# 优化器
optimizer = tf.keras.optimizers.SGD(learning_rate=1e-3)
# 损失函数
loss_fn = tf.keras.losses.CategoricalCrossentropy()
# 训练数据集准备(准备dataset并且打乱数据)
batch_size = 64
train_dataset = tf.data.Dataset.from_tensor_slices((data, labels))
train_dataset = train_dataset.shuffle(buffer_size=1024).batch(batch_size)
import numpy as np
import tensorflow as tf
class MyModel(tf.keras.Model):
def __init__(self, num_classes=10):
super(MyModel, self).__init__(name='my_model')
self.num_classes = num_classes
# 定义自己需要的层
self.dense_1 = tf.keras.layers.Dense(32, activation='relu')
self.dense_2 = tf.keras.layers.Dense(num_classes)
def call(self, inputs):
#定义前向传播
# 使用在 (in `__init__`)定义的层
x = self.dense_1(inputs)
return self.dense_2(x)
data = np.random.random((100, 32))
labels = np.random.random((100, 10))
model = MyModel(num_classes=10)
# Instantiate an optimizer.
optimizer = tf.keras.optimizers.SGD(learning_rate=1e-3)
# Instantiate a loss function.
loss_fn = tf.keras.losses.CategoricalCrossentropy()
# Prepare the training dataset.
batch_size = 16
train_dataset = tf.data.Dataset.from_tensor_slices((data, labels))
train_dataset = train_dataset.shuffle(buffer_size=1024).batch(batch_size)
epochs = 3
for epoch in range(epochs):
print('Start of epoch %d' % (epoch,))
# 遍历数据集的batch_size
for step, (x_batch_train, y_batch_train) in enumerate(train_dataset):
# 打开GradientTape以记录正向传递期间运行的操作,这将启用自动区分。
with tf.GradientTape() as tape:
# 运行该模型的前向传播。 模型应用于其输入的操作将记录在GradientTape上。
logits = model(x_batch_train, training=True) # 这个minibatch的预测值
# 计算这个minibatch的损失值
loss_value = loss_fn(y_batch_train, logits)
# 使用GradientTape自动获取可训练变量相对于损失的梯度。
grads = tape.gradient(loss_value, model.trainable_weights)
# 通过更新变量的值来最大程度地减少损失,从而执行梯度下降的一步。
optimizer.apply_gradients(zip(grads, model.trainable_weights))
# 每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))
让我们将metric添加到组合中。下面可以在从头开始编写的训练循环中随时使用内置指标(或编写的自定义指标)。流程如下:
class MyModel(tf.keras.Model):
def __init__(self, num_classes=10):
super(MyModel, self).__init__(name='my_model')
self.num_classes = num_classes
# 定义自己需要的层
self.dense_1 = tf.keras.layers.Dense(32, activation='relu')
self.dense_2 = tf.keras.layers.Dense(num_classes)
def call(self, inputs):
#定义前向传播
# 使用在 (in `__init__`)定义的层
x = self.dense_1(inputs)
return self.dense_2(x)
import numpy as np
x_train = np.random.random((1000, 32))
y_train = np.random.random((1000, 10))
x_val = np.random.random((200, 32))
y_val = np.random.random((200, 10))
x_test = np.random.random((200, 32))
y_test = np.random.random((200, 10))
# 优化器
optimizer = tf.keras.optimizers.SGD(learning_rate=1e-3)
# 损失函数
loss_fn = tf.keras.losses.CategoricalCrossentropy(from_logits=True)
# 准备metrics函数
train_acc_metric = tf.keras.metrics.CategoricalAccuracy()
val_acc_metric = tf.keras.metrics.CategoricalAccuracy()
# 准备训练数据集
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)
# 准备测试数据集
val_dataset = tf.data.Dataset.from_tensor_slices((x_val, y_val))
val_dataset = val_dataset.batch(64)
model = MyModel(num_classes=10)
epochs = 3
for epoch in range(epochs):
print('Start of epoch %d' % (epoch,))
# 遍历数据集的batch_size
for step, (x_batch_train, y_batch_train) in enumerate(train_dataset):
#一个batch
with tf.GradientTape() as tape:
logits = model(x_batch_train)
loss_value = loss_fn(y_batch_train, logits)
grads = tape.gradient(loss_value, model.trainable_weights)
optimizer.apply_gradients(zip(grads, model.trainable_weights))
# 更新训练集的metrics
train_acc_metric(y_batch_train, logits)
# 在每个epoch结束时显示metrics。
train_acc = train_acc_metric.result()
print('Training acc over epoch: %s' % (float(train_acc),))
# 在每个epoch结束时重置训练指标
train_acc_metric.reset_states()#!!!!!!!!!!!!!!!
# 在每个epoch结束时运行一个验证集。
for x_batch_val, y_batch_val in val_dataset:
val_logits = model(x_batch_val)
# 更新验证集merics
val_acc_metric(y_batch_val, val_logits)
val_acc = val_acc_metric.result()
print('Validation acc: %s' % (float(val_acc),))
val_acc_metric.reset_states()
#显示测试集