作者: lukewood
创建日期: 2021/08/28
最后修改: 2021/08/28
描述: 概述如何使用 TensorFlow NumPy API 编写 Keras 模型。
NumPy 是一个非常成功的 Python 线性代数库。
TensorFlow 最近推出了 tf_numpy,这是
TensorFlow 对 NumPy API 大部分子集的实现。
得益于 tf_numpy
,你可以按照 NumPy 风格编写 Keras 层或模型!
TensorFlow NumPy API 与 TensorFlow 生态系统完全集成。 自动微分、TensorBoard、Keras 模型回调、 TPU 分布以及模型导出等功能均受支持。
让我们通过几个示例来了解一下。
import os
os.environ["KERAS_BACKEND"] = "tensorflow"
import tensorflow as tf
import tensorflow.experimental.numpy as tnp
import keras
from keras import layers
为了测试我们的模型,我们将使用波士顿房价回归数据集。
(x_train, y_train), (x_test, y_test) = keras.datasets.boston_housing.load_data(
path="boston_housing.npz", test_split=0.2, seed=113
)
input_dim = x_train.shape[1]
def evaluate_model(model: keras.Model):
loss, percent_error = model.evaluate(x_test, y_test, verbose=0)
print("训练前的平均绝对百分比误差: ", percent_error)
model.fit(x_train, y_train, epochs=200, verbose=0)
loss, percent_error = model.evaluate(x_test, y_test, verbose=0)
print("训练后的平均绝对百分比误差:", percent_error)
从 https://storage.googleapis.com/tensorflow/tf-keras-datasets/california_housing.npz 下载数据
743530/743530 ━━━━━━━━━━━━━━━━━━━━ 0s 0us/step
利用 Keras API 最灵活的方法是子类化
[keras.Model
](/api/models/model#model-class) 类。子类化模型类
使你能够完全自定义训练循环中的内容。这使得
子类化模型成为研究人员的热门选择。
在这个例子中,我们将实现一个 Model
子类,该子类使用 TNP API 对波士顿房价数据集执行回归。请注意,当使用 TNP API 与 Keras 一起时,微分和梯度下降是自动处理的。
首先,让我们定义一个简单的 TNPForwardFeedRegressionNetwork
类。
class TNPForwardFeedRegressionNetwork(keras.Model):
def __init__(self, blocks=None, **kwargs):
super().__init__(**kwargs)
if not isinstance(blocks, list):
raise ValueError(f"blocks 必须是一个列表,获取到的 blocks={blocks}")
self.blocks = blocks
self.block_weights = None
self.biases = None
def build(self, input_shape):
current_shape = input_shape[1]
self.block_weights = []
self.biases = []
for i, block in enumerate(self.blocks):
self.block_weights.append(
self.add_weight(
shape=(current_shape, block),
trainable=True,
name=f"block-{i}",
initializer="glorot_normal",
)
)
self.biases.append(
self.add_weight(
shape=(block,),
trainable=True,
name=f"bias-{i}",
initializer="zeros",
)
)
current_shape = block
self.linear_layer = self.add_weight(
shape=(current_shape, 1),
name="linear_projector",
trainable=True,
initializer="glorot_normal",
)
def call(self, inputs):
activations = inputs
for w, b in zip(self.block_weights, self.biases):
activations = tnp.matmul(activations, w) + b
# ReLu 激活函数
activations = tnp.maximum(activations, 0.0)
return tnp.matmul(activations, self.linear_layer)
就像任何其他 Keras 模型一样,我们可以利用任何支持的优化器、损失、指标或我们想要的回调。
让我们来看看模型的表现!
model = TNPForwardFeedRegressionNetwork(blocks=[3, 3])
model.compile(
optimizer="adam",
loss="mean_squared_error",
metrics=[keras.metrics.MeanAbsolutePercentageError()],
)
evaluate_model(model)
WARNING: 所有在 absl::InitializeLog() 调用之前的日志消息都写入 STDERR
I0000 00:00:1699909864.025985 48611 device_compiler.h:187] 使用 XLA 编译的集群! 此行最多在进程的生命周期内记录一次。
训练前的平均绝对百分比误差: 99.96772766113281
训练后的平均绝对百分比误差:40.94866180419922
太好了!我们的模型似乎有效地学习了手头的问题。
我们还可以使用 TNP 编写我们自己的自定义损失函数。
def tnp_mse(y_true, y_pred):
return tnp.mean(tnp.square(y_true - y_pred), axis=0)
keras.backend.clear_session()
model = TNPForwardFeedRegressionNetwork(blocks=[3, 3])
model.compile(
optimizer="adam",
loss=tnp_mse,
metrics=[keras.metrics.MeanAbsolutePercentageError()],
)
evaluate_model(model)
训练前的平均绝对百分比误差: 99.99896240234375
训练后的平均绝对百分比误差:52.533199310302734
如有需要,TNP 也可以在以层为导向的 Keras 代码结构中使用。 让我们实现相同的模型,但采用分层的方法!
def tnp_relu(x):
return tnp.maximum(x, 0)
class TNPDense(keras.layers.Layer):
def __init__(self, units, activation=None):
super().__init__()
self.units = units
self.activation = activation
def build(self, input_shape):
self.w = self.add_weight(
name="weights",
shape=(input_shape[1], self.units),
initializer="random_normal",
trainable=True,
)
self.bias = self.add_weight(
name="bias",
shape=(self.units,),
initializer="zeros",
trainable=True,
)
def call(self, inputs):
outputs = tnp.matmul(inputs, self.w) + self.bias
if self.activation:
return self.activation(outputs)
return outputs
def create_layered_tnp_model():
return keras.Sequential(
[
TNPDense(3, activation=tnp_relu),
TNPDense(3, activation=tnp_relu),
TNPDense(1),
]
)
model = create_layered_tnp_model()
model.compile(
optimizer="adam",
loss="mean_squared_error",
metrics=[keras.metrics.MeanAbsolutePercentageError()],
)
model.build((None, input_dim))
model.summary()
evaluate_model(model)
模型: "sequential"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┓ ┃ 层 (类型) ┃ 输出形状 ┃ 参数 # ┃ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━┩ │ tnp_dense (TNPDense) │ (None, 3) │ 27 │ ├─────────────────────────────────┼───────────────────────────┼────────────┤ │ tnp_dense_1 (TNPDense) │ (None, 3) │ 12 │ ├─────────────────────────────────┼───────────────────────────┼────────────┤ │ tnp_dense_2 (TNPDense) │ (None, 1) │ 4 │ └─────────────────────────────────┴───────────────────────────┴────────────┘
总参数: 43 (172.00 B)
可训练参数: 43 (172.00 B)
不可训练参数: 0 (0.00 B)
训练前的平均绝对百分比误差: 100.00006866455078
训练后的平均绝对百分比误差: 43.57806396484375
你也可以无缝切换TNP层和原生Keras层!
def create_mixed_model():
return keras.Sequential(
[
TNPDense(3, activation=tnp_relu),
# 模型将可以无缝使用普通的Dense层
layers.Dense(3, activation="relu"),
# ... 或者切换回tnp层!
TNPDense(1),
]
)
model = create_mixed_model()
model.compile(
optimizer="adam",
loss="mean_squared_error",
metrics=[keras.metrics.MeanAbsolutePercentageError()],
)
model.build((None, input_dim))
model.summary()
evaluate_model(model)
模型: "sequential_1"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┓ ┃ 层 (类型) ┃ 输出形状 ┃ 参数 # ┃ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━┩ │ tnp_dense_3 (TNPDense) │ (None, 3) │ 27 │ ├─────────────────────────────────┼───────────────────────────┼────────────┤ │ dense (Dense) │ (None, 3) │ 12 │ ├─────────────────────────────────┼───────────────────────────┼────────────┤ │ tnp_dense_4 (TNPDense) │ (None, 1) │ 4 │ └─────────────────────────────────┴───────────────────────────┴────────────┘
总参数: 43 (172.00 B)
可训练参数: 43 (172.00 B)
不可训练参数: 0 (0.00 B)
训练前的平均绝对百分比误差: 100.0
训练后的平均绝对百分比误差: 55.646610260009766
Keras API提供了多种层。将它们与NumPy代码一起使用,将在项目中节省大量时间。
TensorFlow NumPy和Keras与 TensorFlow分布策略集成。 这使得在多个GPU上或整个TPU Pod上进行分布式训练变得简单。
gpus = tf.config.list_logical_devices("GPU")
if gpus:
strategy = tf.distribute.MirroredStrategy(gpus)
else:
# 我们可以回退到一个无操作的 CPU 策略。
strategy = tf.distribute.get_strategy()
print("Running with strategy:", str(strategy.__class__.__name__))
with strategy.scope():
model = create_layered_tnp_model()
model.compile(
optimizer="adam",
loss="mean_squared_error",
metrics=[keras.metrics.MeanAbsolutePercentageError()],
)
model.build((None, input_dim))
model.summary()
evaluate_model(model)
INFO:tensorflow:使用 MirroredStrategy,设备为 ('/job:localhost/replica:0/task:0/device:GPU:0',)
使用策略:MirroredStrategy
模型: "sequential_2"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┓ ┃ 层 (类型) ┃ 输出形状 ┃ 参数 # ┃ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━┩ │ tnp_dense_5 (TNPDense) │ (None, 3) │ 27 │ ├─────────────────────────────────┼───────────────────────────┼────────────┤ │ tnp_dense_6 (TNPDense) │ (None, 3) │ 12 │ ├─────────────────────────────────┼───────────────────────────┼────────────┤ │ tnp_dense_7 (TNPDense) │ (None, 1) │ 4 │ └─────────────────────────────────┴───────────────────────────┴────────────┘
总参数: 43 (172.00 B)
可训练参数: 43 (172.00 B)
不可训练参数: 0 (0.00 B)
训练前的平均绝对百分比误差: 100.0
训练后的平均绝对百分比误差: 44.573463439941406
使用 Keras API 的许多好处之一是能够通过 TensorBoard 监控训练。 将 TensorFlow NumPy API 与 Keras 一起使用,使您可以轻松利用 TensorBoard。
keras.backend.clear_session()
要从 Jupyter notebook 加载 TensorBoard,您可以运行以下魔法命令:
%load_ext tensorboard
models = [
(
TNPForwardFeedRegressionNetwork(blocks=[3, 3]),
"TNPForwardFeedRegressionNetwork",
),
(create_layered_tnp_model(), "layered_tnp_model"),
(create_mixed_model(), "mixed_model"),
]
for model, model_name in models:
model.compile(
optimizer="adam",
loss="mean_squared_error",
metrics=[keras.metrics.MeanAbsolutePercentageError()],
)
model.fit(
x_train,
y_train,
epochs=200,
verbose=0,
callbacks=[keras.callbacks.TensorBoard(log_dir=f"logs/{model_name}")],
)
/opt/conda/envs/keras-tensorflow/lib/python3.10/site-packages/keras/src/callbacks/tensorboard.py:676: 用户警告: 模型未能序列化为 JSON。忽略... 无效的格式说明符
warnings.warn(f"Model failed to serialize as JSON. Ignoring... {exc}")
要从 Jupyter notebook 加载 TensorBoard,您可以使用 %tensorboard
魔法命令:
%tensorboard --logdir logs
TensorBoard 监控指标并检查训练曲线。
TensorBoard 还允许您探索模型使用的计算图。
检查您的模型的能力在调试过程中可以非常有价值。
将现有的NumPy代码移植到使用 tensorflow_numpy
API 的Keras模型是很简单的!
通过与Keras集成,您可以使用现有的Keras回调、指标和优化器,轻松分发您的训练并使用Tensorboard。
将更复杂的模型,如ResNet,迁移到TensorFlow NumPy API将是一个很好的后续学习练习。
有几个开源的NumPy ResNet实现可在网上找到。