将 TensorFlow 与 Vertex Explainable AI 搭配使用

使用经过训练的 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 的积分梯度特征归因方法,必须确保输入可针对输出进行微分。

说明元数据在逻辑上会区分模型的特征与其输入。 将积分梯度与输入张量(该输入张量不可针对输出张量进行微分)时,您还需要提供该特征的编码(且可微分的)版本。

如果您有不可微分的输入张量,或者图中存在不可微分的操作,请使用以下方法:

  1. 将不可微分的输入编码为可微分的输入。
  2. 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_preprocessxai_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 作为输出张量的名称。

后续步骤