使用经过训练的 TensorFlow 模型时,您需要特定信息才能保存模型并配置说明。
如果您想要将 Vertex Explainable AI 与 AutoML 表格模型搭配使用,则无需执行任何配置;Vertex AI 会自动为 Vertex Explainable AI 配置模型。请跳过本文档并阅读获取说明。
本指南介绍了训练 TensorFlow 模型时需要的信息,以确保能将其与 Vertex Explainable AI 结合使用。具体而言,本指南涵盖以下主题:
在训练期间查找输入和输出张量名称,您需要在针对 Vertex Explainable AI 配置
Model
资源时指定这些名称。这包括在典型情况不适用的特殊情况下,为 Vertex Explainable AI 创建和查找适当的张量。将 TensorFlow 模型导出为与 Vertex Explainable AI 兼容的 TensorFlow SavedModel。
从已导出的 TensorFlow SavedModel 中找到输入和输出张量名称。如果您无法访问模型的训练代码,这可能会很有用。
在训练期间查找输入和输出张量名称
使用 TensorFlow 预构建容器来进行预测时,您必须知道模型的输入张量和输出张量的名称。为 Vertex Explainable AI 配置 Model
时,您需要在 ExplanationMetadata
消息中指定这些名称。
如果您的 TensorFlow 模型符合以下条件,那么您可以使用下一部分中介绍的“基本方法”在训练期间确定这些张量的名称:
- 输入不是序列化形式
- 模型的
SignatureDef
的每个输入都直接包含特征的值(可以是数值或字符串) - 输出是数值(被视为数字数据)。这不包括作为分类数据的类别 ID。
如果您的模型不符合这些条件,请参阅在特殊情况下调整训练代码并查找张量名称部分。
基本方法
在训练期间,输出模型的输入张量和输出张量的 name
属性。在以下示例中,Keras 层的 name
字段会生成 ExplanationMetadata
所需的底层张量名称:
bow_inputs = tf.keras.layers.Input(shape=(2000,))
merged_layer = tf.keras.layers.Dense(256, activation="relu")(bow_inputs)
predictions = tf.keras.layers.Dense(10, activation="sigmoid")(merged_layer)
model = tf.keras.Model(inputs=bow_inputs, outputs=predictions)
print('input_tensor_name:', bow_inputs.name)
print('output_tensor_name:', predictions.name)
运行此 Python 代码会产生以下输出:
input_tensor_name: input_1:0
output_tensor_name: dense_1/Sigmoid:0
然后,您可以在针对说明配置 Model
时,将 input_1:0
作为输入张量名称,将 dense_1/Sigmod:0
作为输出张量名称。
在特殊情况下调整训练代码并查找张量名称
在一些常见情况下,ExplanationMetadata
中的输入和输出张量不应与服务 SignatureDef
中的输入和输出张量相同:
- 输入已序列化
- 图中包含预处理操作
- 服务输出不属于概率、logits 或其他浮点类型的张量
在这些情况下,您应使用不同的方法来查找正确的输入和输出张量。总体目标是查找与您要为输入说明的特征值有关的张量,以及与 logits(激活前)、概率(激活后)或任何其他输出表示有关的张量。
输入张量的特殊情况
如果您使用序列化输入来向模型提供数据,或者图中包含预处理操作,则说明元数据中的输入与服务 SignatureDef
中的输入有所不同。
序列化输入
TensorFlow SavedModel 可以接受各种复杂的输入,包括:
- 序列化 tf.Example 消息
- JSON 字符串
- 编码的 Base64 字符串(表示图片数据)
如果您的模型接受类似这样的序列化输入,则直接使用这些张量作为说明的输入将不起作用,或者可能生成无意义的结果。相反,您需要查找输入模型内特征列的后续输入张量。
导出模型时,您可以通过在服务输入函数中调用解析函数来向 TensorFlow 图添加解析操作。tf.io 模块中列出了解析函数。这些解析函数通常会返回张量作为响应,而这些张量则更适合用于说明元数据。
例如,您可以在导出模型时使用 tf.parse_example()。它会接收序列化 tf.Example 消息,并输出提供给特征列的张量字典。您可以使用其输出来填充说明元数据。如果这些输出中的某些输出是 tf.SparseTensor(这是由 3 个张量组成的命名元组),则您应获取索引、值和 dense_shape 张量的名称,并填充元数据中的相应字段。
以下示例展示了如何在解码操作后获取输入张量的名称:
float_pixels = tf.map_fn(
lambda img_string: tf.io.decode_image(
img_string,
channels=color_depth,
dtype=tf.float32
),
features,
dtype=tf.float32,
name='input_convert'
)
print(float_pixels.name)
预处理输入
如果您的模型图包含一些预处理操作,则您可能希望在预处理步骤之后获取张量的说明。在这种情况下,您可以使用 tf.Tensor 的 name
属性来获取这些张量的名称,并将它们放入说明元数据中:
item_one_hot = tf.one_hot(item_indices, depth,
on_value=1.0, off_value=0.0,
axis=-1, name="one_hot_items:0")
print(item_one_hot.name)
解码后的张量名称会变为 input_pixels:0
。
输出张量的特殊情况
在大多数情况下,服务 SignatureDef
中的输出是概率或 logits。
如果您的模型归因于概率,而您想要说明 logit 值,则您必须查找与 logits 对应的适当输出张量名称。
如果服务 SignatureDef
的输出不是概率或 logits,则您应参考训练图中的概率操作。对于 Keras 模型,这种情况不太可能出现。如果出现这种情况,您可以使用 TensorBoard(或其他图表可视化工具)来帮助查找正确的输出张量名称。
积分梯度的特殊注意事项
如果要使用 Vertex Explainable AI 的积分梯度特征归因方法,必须确保输入可针对输出进行微分。
说明元数据在逻辑上会区分模型的特征与其输入。 将积分梯度与输入张量(该输入张量不可针对输出张量进行微分)时,您还需要提供该特征的编码(且可微分的)版本。
如果您有不可微分的输入张量,或者图中存在不可微分的操作,请使用以下方法:
- 将不可微分的输入编码为可微分的输入。
- 将
input_tensor_name
设置为不可微分的原始输入张量的名称,将encoded_tensor_name
设置为可微分的编码输入张量的名称。
包含编码的说明元数据文件
例如,假设某个模型的分类特征具有名为 zip_codes:0
的输入张量。由于输入数据包含字符串形式的邮政编码,因此输入张量 zip_codes:0
是不可微分的。如果该模型还会预处理此数据以获取邮政编码的独热编码表示,则预处理之后的输入张量是可微分的。如需将其与原始输入张量进行区分,您可以将其命名为 zip_codes_embedding:0
。
如需在说明请求中使用这两个输入张量的数据,请在针对说明配置 Model
时设置 ExplanationMetadata
:
- 将 input feature key 设置为有意义的名称,例如
zip_codes
。 - 将
input_tensor_name
设置为原始张量的名称zip_codes:0
。 - 将
encoded_tensor_name
设置为独热编码后的张量名称zip_codes_embedding:0
。 - 将
encoding
设置为COMBINED_EMBEDDING
。
{
"inputs": {
"zip_codes": {
"input_tensor_name": "zip_codes:0",
"encoded_tensor_name": "zip_codes_embedding:0",
"encoding": "COMBINED_EMBEDDING"
}
},
"outputs": {
"probabilities": {
"output_tensor_name": "dense/Softmax:0"
}
}
}
或者,您可以将 input_tensor_name
设置为可微分的编码输入张量的名称,并省略不可微分的原始张量。同时提供这两种张量的好处在于,可以对各个邮政编码值(而不是其独热编码表示)进行归因。在此示例中,您要排除原始张量 (zip_codes:0
),并将 input_tensor_name
设置为 zip_codes_embedding:0
。我们不推荐此方法,因为生成的特征归因很难说明原因。
编码
如需为 Model
启用编码,请按照以上示例所示指定编码设置。
编码特征有助于反转从编码数据到输入数据的归因过程,从而无需手动对返回的归因进行后处理。请参阅 Vertex Explainable AI 支持的编码列表。
对于 COMBINED_EMBEDDING
编码,输入张量会编码为一维数组。
例如:
- 输入:
["This", "is", "a", "test"]
- 编码的输入:
[0.1, 0.2, 0.3, 0.4]
为 Vertex Explainable AI 导出 TensorFlow SavedModel
训练 TensorFlow 模型后,请将其导出为 SavedModel。TensorFlow SavedModel 包含经过训练的 TensorFlow 模型,以及运行图所需的序列化签名、变量和其他资源。SavedModel 中的每个 SignatureDef
标识图中接受张量输入并生成张量输出的函数。
如需确保 SavedModel 与 Vertex Explainable AI 兼容,请根据您使用的是 TensorFlow 2 还是 TensorFlow 1,按照以下某一个部分中的说明操作。
TensorFlow 2
如果您使用的是 TensorFlow 2.x,请使用 tf.saved_model.save
保存您的模型。您可以在保存模型时指定输入签名。如果您只有一个输入签名,Vertex Explainable AI 会为您的说明请求使用默认服务函数。如果您有多个输入签名,则应该在保存模型时指定默认传送函数的签名:
tf.saved_model.save(m, model_dir, signatures={
'serving_default': serving_fn,
'xai_model': model_fn # Required for XAI
})
在这种情况下,Vertex Explainable AI 会为说明请求使用您通过 xai_model
键保存的模型函数签名。请为该键使用确切的字符串 xai_model
。
如果您使用预处理函数,则还需要为预处理函数和模型函数指定签名。您必须为相应键使用确切的字符串 xai_preprocess
和 xai_model
:
tf.saved_model.save(m, model_dir, signatures={
'serving_default': serving_fn,
'xai_preprocess': preprocess_fn, # Required for XAI
'xai_model': model_fn # Required for XAI
})
在这种情况下,Vertex Explainable AI 会使用您的预处理函数和模型函数处理您的说明请求。确保预处理函数的输出与模型函数所需的输入相符。
详细了解如何在 TensorFlow 中指定传送签名。
TensorFlow 1.15
如果您使用的是 TensorFlow 1.15,请不要使用 tf.saved_model.save
。Vertex Explainable AI 不支持通过此方法保存的 TensorFlow 1 模型
如果您在 Keras 中构建和训练模型,则必须将模型转换为 TensorFlow Estimator,然后将其导出到 SavedModel。本部分重点介绍如何保存模型。
在构建、编译、训练和评估 Keras 模型后,您必须执行以下操作:
- 使用
tf.keras.estimator.model_to_estimator
将 Keras 模型转换为 TensorFlow Estimator - 使用
tf.estimator.export.build_raw_serving_input_receiver_fn
提供传送输入函数 - 使用
tf.estimator.export_saved_model
将模型导出为 SavedModel。
# Build, compile, train, and evaluate your Keras model
model = tf.keras.Sequential(...)
model.compile(...)
model.fit(...)
model.predict(...)
## Convert your Keras model to an Estimator
keras_estimator = tf.keras.estimator.model_to_estimator(keras_model=model, model_dir='export')
## Define a serving input function appropriate for your model
def serving_input_receiver_fn():
...
return tf.estimator.export.ServingInputReceiver(...)
## Export the SavedModel to Cloud Storage, using your serving input function
export_path = keras_estimator.export_saved_model(
'gs://' + 'YOUR_BUCKET_NAME',
serving_input_receiver_fn
).decode('utf-8')
print("Model exported to: ", export_path)
从 SavedModel 的 SignatureDef
获取张量名称
您可以使用 TensorFlow SavedModel 的 SignatureDef
准备说明元数据,前提是它符合上一部分所述的“基本方法”的条件。如果您无法访问生成模型的训练代码,这可能会很有用。
如需检查 SavedModel 的 SignatureDef
,您可以使用 SavedModel CLI。详细了解如何使用 SavedModel CLI。
请思考以下示例 SignatureDef
:
The given SavedModel SignatureDef contains the following input(s):
inputs['my_numpy_input'] tensor_info:
dtype: DT_FLOAT
shape: (-1, 1)
name: x:0
The given SavedModel SignatureDef contains the following output(s):
outputs['probabilities'] tensor_info:
dtype: DT_FLOAT
shape: (-1, 1)
name: dense/Softmax:0
Method name is: tensorflow/serving/predict
图中有一个名为 x:0
的输入张量和一个名为 dense/Softmax:0
的输出张量。当您针对说明配置 Model
时,在 ExplanationMetadata
消息中,将 x:0
作为输入张量的名称,将 dense/Softmax:0
作为输出张量的名称。