Utiliser TensorFlow pour les explications

Lorsque vous travaillez avec des modèles TensorFlow personnalisés, vous devez disposer d'informations spécifiques pour enregistrer votre modèle et configurer des explications.

Si vous souhaitez utiliser Vertex Explainable AI avec un modèle tabulaire AutoML, aucune configuration n'est requise. Vertex AI configure automatiquement le modèle pour Vertex Explainable AI. Ignorez ce document et consultez la page Obtenir des explications.

Ce guide fournit les informations dont vous avez besoin pour entraîner un modèle TensorFlow afin de vous assurer que vous pouvez l'utiliser avec Vertex Explainable AI. Il couvre en particulier les rubriques suivantes :

  • Trouver les noms des Tensors d'entrée et de sortie lors de l'entraînement que vous devez spécifier lorsque vous configurez une ressource Model pour Vertex Explainable AI. Cela implique de créer et de rechercher les Tensors appropriés pour Vertex Explainable AI dans les cas spéciaux lorsque ceux-ci ne fonctionnent pas.

  • Exporter votre modèle TensorFlow en tant que modèle SavedModel TensorFlow compatible avec Vertex Explainable AI

  • Rechercher les noms de Tensor d'entrée et de sortie à partir d'un modèle SavedModel TensorFlow qui a déjà été exporté Cela peut être utile si vous n'avez pas accès au code d'entraînement du modèle.

Rechercher des noms de Tensor d'entrée et de sortie lors de l'entraînement

Lorsque vous utilisez un conteneur prédéfini TensorFlow pour diffuser les prédictions, vous devez connaître les noms des Tensors d'entrée et du Tensor de sortie de votre modèle. Vous spécifiez ces noms dans un message ExplanationMetadata lorsque vous configurez un Model pour Vertex Explainable AI.

Si votre modèle TensorFlow répond aux critères suivants, vous pouvez utiliser la "méthode de base" décrite dans la section suivante pour déterminer ces noms de Tensor pendant l'entraînement :

  • vos entrées ne sont pas sérialisées ;
  • Chaque entrée de la SignatureDef du modèle contient directement la valeur de la caractéristique (il peut s'agir de valeurs numériques ou de chaînes).
  • les sorties sont des valeurs numériques, traitées comme des données numériques. Cela exclut les identifiants de classe, qui sont considérés comme des données catégorielles.

Si votre modèle ne répond pas à ces critères, lisez la section Ajuster le code d'entraînement et rechercher les noms de Tensor dans les cas particuliers.

Méthode de base

Pendant l'entraînement, écrivez l'attribut name des Tensors d'entrée de votre modèle et de ses Tensors de sortie. Dans l'exemple suivant, le champ name de la couche Keras produit le nom du Tensor sous-jacent dont vous avez besoin pour votre 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)

L'exécution de ce code Python affiche le résultat suivant:

input_tensor_name: input_1:0
output_tensor_name: dense_1/Sigmoid:0

Vous pouvez ensuite utiliser input_1:0 comme nom de Tensor d'entrée et dense_1/Sigmod:0 comme nom de Tensor de sortie lorsque vous configurez votre Model pour les explications.

Ajuster le code d'entraînement et rechercher des noms de Tensor dans des cas particuliers

Dans certains cas courants, les Tensors d'entrée et de sortie de ExplanationMetadata ne doivent pas être identiques à ceux de votre SignatureDef de diffusion:

  • vous avez des entrées sérialisées ;
  • votre graphe inclut des opérations de prétraitement ;
  • vos résultats d'inférence ne sont pas des probabilités, des fonctions logit ou d'autres types de Tensors à virgule flottante.

Dans ces cas, vous devez utiliser différentes approches pour trouver les Tensors d'entrée et de sortie corrects. L'objectif général est de trouver les Tensors correspondant aux valeurs de caractéristique que vous souhaitez expliquer pour les entrées, ainsi que les Tensors correspondant aux fonctions logit (préactivation), aux probabilités (postactivation) ou à toute autre représentation des résultats.

Cas particuliers pour les Tensors d'entrée

Les entrées de vos métadonnées d'explications diffèrent de celles de votre inférence SignatureDef si vous utilisez une entrée sérialisée pour alimenter le modèle, ou si votre graphe inclut des opérations de prétraitement.

Entrées sérialisées

Les modèles TensorFlow SavedModel acceptent diverses entrées complexes, y compris les suivantes :

  • Messages tf.Example sérialisés
  • Chaînes JSON
  • Chaînes encodées en base64 (pour représenter les données d'image)

Si votre modèle accepte des entrées sérialisées comme celles-ci, l'utilisation directe de ces Tensors pour vos explications ne fonctionnera pas ou produira des résultats absurdes. Vous devez plutôt rechercher les Tensors d'entrée suivants qui alimentent les colonnes de caractéristiques de votre modèle.

Lorsque vous exportez votre modèle, vous pouvez ajouter une opération d'analyse à votre graphe TensorFlow en appelant une fonction d'analyse dans la fonction d'entrée d'inférence. Les fonctions d'analyse sont répertoriées dans le module tf.io. Ces fonctions d'analyse renvoient généralement des Tensors en tant que réponse. Ces Tensors constituent les meilleurs choix pour vos métadonnées d'explications.

Par exemple, vous pouvez utiliser la fonction tf.parse_example() lors de l'exportation de votre modèle. La fonction utilise un message tf.Example sérialisé et génère un dictionnaire de Tensors qui alimentent les colonnes de caractéristiques. Vous pouvez utiliser la sortie pour renseigner vos métadonnées d'explications. Si certaines de ces sorties sont tf.SparseTensor, qui est un tuple nommé composé de trois Tensors, vous devez obtenir les noms des indices, des valeurs et des Tensors dense_shape, ainsi que renseigner les champs correspondants dans les métadonnées.

L'exemple suivant montre comment obtenir le nom du Tensor d'entrée après une opération de décodage :

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)
Prétraiter les entrées

Si le graphe de votre modèle contient des opérations de prétraitement, vous pouvez obtenir des explications sur les Tensors après l'étape de prétraitement. Dans ce cas, les noms de ces Tensors sont obtenus en utilisant la propriété name de tf.Tensor et en les insérant dans les métadonnées d'explications :

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)

Le nom du Tensor décodé devient input_pixels:0.

Cas particuliers pour les Tensors de sortie

Dans la plupart des cas, les sorties de votre inférence SignatureDef sont des probabilités ou des fonctions logit.

Si votre modèle attribue des probabilités, mais que vous souhaitez plutôt expliquer les valeurs logit, vous devez trouver les noms de Tensor de sortie appropriés qui correspondent aux valeurs logit.

Si votre inférence SignatureDef comporte des résultats qui ne sont pas des probabilités ni des valeurs logit, vous devez vous référer à l'opération de probabilités dans le graphe d'entraînement. Ce scénario est peu probable pour les modèles Keras. Si tel est le cas, vous pouvez utiliser TensorBoard (ou d'autres outils de visualisation graphique) pour localiser les noms de Tensor de sortie corrects.

Remarques spécifiques aux gradients intégrés

Si vous souhaitez utiliser la méthode d'attribution de caractéristiques des gradients intégrés de Vertex Explainable AI, vous devez vous assurer que vos entrées sont différenciées par rapport à la sortie.

Les métadonnées d'explications séparent logiquement les caractéristiques d'un modèle de ses entrées. Lorsque vous utilisez des gradients intégrés avec un Tensor d'entrée non différenciable par rapport au Tensor de sortie, vous devez également fournir la version encodée (et différenciable) de la caractéristique concernée.

Utilisez l'approche suivante si vous avez des Tensors d'entrée non différenciables, ou si des opérations non différenciables sont présentes dans votre graphe :

  1. Encodez les entrées non différenciables comme entrées différenciables.
  2. Définissez la valeur input_tensor_name sur le nom du Tensor non différenciable d'origine, et définissez la valeur encoded_tensor_name sur le nom de sa version encodable et différenciable.

Fichier de métadonnées d'explications avec encodage

Prenons l'exemple d'un modèle qui possède une caractéristique de catégorie avec un Tensor d'entrée nommé zip_codes:0. Comme les données d'entrée incluent des codes postaux en tant que chaînes, le Tensor d'entrée zip_codes:0 n'est pas différenciable. Si le modèle prétraite également ces données pour obtenir une représentation des codes postaux par encodage one-hot, le Tensor d'entrée après les opérations de prétraitement est différenciable. Pour le distinguer du Tensor d'entrée d'origine, vous pouvez le nommer zip_codes_embedding:0.

Pour utiliser les données des deux Tensors d'entrée dans votre requête d'explications, définissez ExplanationMetadata comme suit lorsque vous configurez votre Model pour les explications:

  • Attribuez un nom explicite à la clé de la caractéristique d'entrée, par exemple zip_codes.
  • Définissez input_tensor_name sur le nom du Tensor d'origine, zip_codes:0.
  • Définissez encoded_tensor_name sur le nom du Tensor après encodage one-hot, zip_codes_embedding:0.
  • Définissez encoding sur 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"
      }
    }
}

Vous pouvez également définir input_tensor_name sur le nom du Tensor d'entrée encodé et différenciable, et omettre le Tensor non différenciable d'origine. Spécifier deux Tensors permet d'effectuer les attributions sur chaque valeur de code postal plutôt que sur leur représentation après encodage one-hot. Dans cet exemple, vous devez exclure le Tensor d'origine (zip_codes:0) et définir input_tensor_name sur zip_codes_embedding:0. Cette approche n'est pas recommandée, car les attributions de caractéristiques qui en résultent sont difficiles à interpréter.

Encodage

Pour activer l'encodage pour votre Model, spécifiez les paramètres d'encodage comme indiqué dans l'exemple précédent.

La fonctionnalité d'encodage permet d'inverser le processus des données encodées vers les données d'entrée pour les attributions, ce qui évite les opérations manuelles de post-traitement des attributions renvoyées. Consultez la liste des encodages compatibles avec Vertex Explainable AI.

Pour l'encodage COMBINED_EMBEDDING, le Tensor d'entrée est encodé dans un tableau 1D.

Exemple :

  • Entrée : ["This", "is", "a", "test"]
  • Entrée encodée: [0.1, 0.2, 0.3, 0.4]

Exporter des modèles SavedModel TensorFlow pour Vertex Explainable AI

Après avoir entraîné un modèle TensorFlow, exportez-le en tant que modèle SavedModel. Le modèle TensorFlow SavedModel contient votre modèle TensorFlow entraîné, ainsi que des signatures sérialisées, des variables et d'autres éléments nécessaires à l'exécution du graphe. Chaque SignatureDef du SavedModel identifie une fonction de votre graphe qui accepte les entrées de Tensor et génère des sorties de Tensor.

Pour vous assurer que votre modèle SavedModel est compatible avec Vertex Explainable AI, suivez les instructions de l'une des sections suivantes, selon que vous utilisez TensorFlow 2 ou TensorFlow 1.

TensorFlow 2

Si vous utilisez TensorFlow 2.x, enregistrez votre modèle à l'aide de tf.saved_model.save. Vous pouvez spécifier des signatures d'entrée lorsque vous enregistrez votre modèle. Si vous disposez d'une signature d'entrée, Vertex Explainable AI utilise la fonction de diffusion par défaut pour vos requêtes d'explications. Si vous disposez de plusieurs signatures d'entrée, vous devez spécifier la signature de votre fonction de diffusion par défaut lorsque vous enregistrez votre modèle :

tf.saved_model.save(m, model_dir, signatures={
    'serving_default': serving_fn,
    'xai_model': model_fn # Required for XAI
    })

Dans ce cas, Vertex Explainable AI utilise la signature de fonction de modèle que vous avez enregistrée avec la clé xai_model pour votre requête d'explications. Utilisez la chaîne exacte xai_model pour la clé.

Si vous utilisez une fonction de prétraitement, vous devez également spécifier les signatures pour votre fonction de prétraitement et votre fonction de modèle. Vous devez utiliser les chaînes exactes xai_preprocess et xai_model comme clés :

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
    })

Dans ce cas, Vertex Explainable AI utilise votre fonction de prétraitement et votre fonction de modèle pour vos requêtes d'explications. Assurez-vous que la sortie de votre fonction de prétraitement correspond à l'entrée attendue par votre fonction de modèle.

Apprenez-en plus sur la spécification de signatures de diffusion dans TensorFlow.

TensorFlow 1.15

Si vous utilisez TensorFlow 1.15, n'utilisez pas tf.saved_model.save. Vertex Explainable AI n'est pas compatible avec les modèles TensorFlow 1 enregistrés avec cette méthode.

Si vous créez et entraînez votre modèle dans Keras, vous devez le convertir en estimateur TensorFlow, puis l'exporter vers un modèle SavedModel. Cette section porte sur l'enregistrement d'un modèle.

Après avoir créé, compilé, entraîné et évalué votre modèle Keras, vous devez :

  • convertir le modèle Keras en un estimateur TensorFlow, à l'aide de tf.keras.estimator.model_to_estimator ;
  • spécifier une fonction d'entrée de diffusion, à l'aide de tf.estimator.export.build_raw_serving_input_receiver_fn ;
  • Exporter le modèle en tant que modèle SavedModel à l'aide de tf.estimator.export_saved_model.
# 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)

Obtenir des noms de Tensor à partir de la SignatureDef d'un modèle SavedModel

Vous pouvez utiliser la SignatureDef d'un modèle SavedModel TensorFlow pour préparer vos métadonnées d'explications, à condition qu'elles répondent aux critères de la "méthode de base" décrite dans une section précédente. Cela peut être utile si vous n'avez pas accès au code d'entraînement qui a généré le modèle.

Pour inspecter le paramètre SignatureDef de votre modèle SavedModel, vous pouvez utiliser la CLI SavedModel. Découvrez en détail comment utiliser la CLI SavedModel.

Prenons l'exemple SignatureDef suivant :

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

Le graphe présente un Tensor d'entrée nommé x:0 et un Tensor de sortie nommé dense/Softmax:0. Lorsque vous configurez votre Model pour les explications, utilisez x:0 comme nom de Tensor d'entrée et dense/Softmax:0 comme nom de Tensor de sortie dans le message ExplanationMetadata.

Étape suivante