Meilleure ingénierie ML avec les métadonnées ML

Voir sur TensorFlow.org Exécuter dans Google Colab Voir la source sur GitHub Télécharger le cahier

Supposons un scénario dans lequel vous configurez un pipeline de ML de production pour classer les pingouins. Le pipeline ingère vos données d'entraînement, entraîne et évalue un modèle et le pousse vers la production.

Cependant, lorsque vous essayez plus tard d'utiliser ce modèle avec un ensemble de données plus important contenant différents types de manchots, vous constatez que votre modèle ne se comporte pas comme prévu et commence à classer les espèces de manière incorrecte.

À ce stade, vous êtes intéressé à savoir :

  • Quel est le moyen le plus efficace de déboguer le modèle lorsque le seul artefact disponible est le modèle en production ?
  • Quel jeu de données d'entraînement a été utilisé pour entraîner le modèle ?
  • Quel parcours d'entraînement a conduit à ce modèle erroné ?
  • Où sont les résultats de l'évaluation du modèle ?
  • Par où commencer le débogage ?

ML métadonnées (MLMD) est une bibliothèque qui met à profit les métadonnées associées aux modèles ML pour vous aider à répondre à ces questions et plus encore. Une analogie utile consiste à considérer ces métadonnées comme l'équivalent de la connexion au développement de logiciels. MLMD vous permet de suivre de manière fiable les artefacts et la lignée associés aux différents composants de votre pipeline de ML.

Dans ce didacticiel, vous configurez un pipeline TFX pour créer un modèle qui classe les manchots en trois espèces en fonction de la masse corporelle, de la longueur et de la profondeur de leurs culmens, ainsi que de la longueur de leurs nageoires. Vous utilisez ensuite MLMD pour suivre la lignée des composants du pipeline.

Pipelines TFX dans Colab

Colab est un environnement de développement léger qui diffère considérablement d'un environnement de production. En production, vous pouvez avoir divers composants de pipeline tels que l'ingestion de données, la transformation, la formation de modèles, les historiques d'exécution, etc. sur plusieurs systèmes distribués. Pour ce didacticiel, vous devez savoir qu'il existe des différences importantes dans l'orchestration et le stockage des métadonnées : tout est géré localement dans Colab. En savoir plus sur TFX à Colab ici .

Installer

Tout d'abord, nous installons et importons les packages nécessaires, configurons les chemins et téléchargeons les données.

Pip de mise à niveau

Pour éviter de mettre à niveau Pip dans un système lors de l'exécution locale, assurez-vous que nous exécutons dans Colab. Les systèmes locaux peuvent bien sûr être mis à niveau séparément.

try:
  import colab
  !pip install --upgrade pip
except:
  pass

Installer et importer TFX

pip install -q -U tfx

Importer des packages

As-tu redémarré le runtime ?

Si vous utilisez Google Colab, la première fois que vous exécutez la cellule ci-dessus, vous devez redémarrer le runtime en cliquant au-dessus du bouton "RESTART RUNTIME" ou en utilisant le menu "Runtime> Restart runtime ...". Cela est dû à la façon dont Colab charge les packages.

import os
import tempfile
import urllib
import pandas as pd

import tensorflow_model_analysis as tfma
from tfx.orchestration.experimental.interactive.interactive_context import InteractiveContext

Vérifiez les versions TFX et MLMD.

from tfx import v1 as tfx
print('TFX version: {}'.format(tfx.__version__))
import ml_metadata as mlmd
print('MLMD version: {}'.format(mlmd.__version__))
TFX version: 1.4.0
MLMD version: 1.4.0

Télécharger le jeu de données

Dans ce colab, nous utilisons l' ensemble de données Penguins Palmer qui se trouve sur Github . Nous avons traité l'ensemble de données en omettant des dossiers incomplets, et laisse tomber l' island et le sex colonnes et converti étiquettes à int32 . L'ensemble de données contient 334 enregistrements de la masse corporelle, de la longueur et de la profondeur des culmens des manchots, ainsi que de la longueur de leurs nageoires. Vous utilisez ces données pour classer les manchots dans l'une des trois espèces.

DATA_PATH = 'https://raw.githubusercontent.com/tensorflow/tfx/master/tfx/examples/penguin/data/labelled/penguins_processed.csv'
_data_root = tempfile.mkdtemp(prefix='tfx-data')
_data_filepath = os.path.join(_data_root, "penguins_processed.csv")
urllib.request.urlretrieve(DATA_PATH, _data_filepath)
('/tmp/tfx-datal9104odr/penguins_processed.csv',
 <http.client.HTTPMessage at 0x7f9c6d8d2290>)

Créer un Contexte Interactif

Pour exécuter des composants TFX de manière interactive dans ce bloc - notes, créez un InteractiveContext . Le InteractiveContext utilise un répertoire temporaire avec une instance de base de données MLMD éphémère. Notez que les appels à InteractiveContext sont pas d'habitation en dehors de l'environnement Colab.

En général, il est une bonne pratique de groupe fonctionne de pipelines similaires dans un Context .

interactive_context = InteractiveContext()
WARNING:absl:InteractiveContext pipeline_root argument not provided: using temporary directory /tmp/tfx-interactive-2021-12-05T11_15_56.285625-5hcexlo8 as root for pipeline outputs.
WARNING:absl:InteractiveContext metadata_connection_config not provided: using SQLite ML Metadata database at /tmp/tfx-interactive-2021-12-05T11_15_56.285625-5hcexlo8/metadata.sqlite.

Construire le pipeline TFX

Un pipeline TFX se compose de plusieurs composants qui exécutent différents aspects du flux de travail ML. Dans ce bloc - notes, vous créez et exécutez les ExampleGen , StatisticsGen , SchemaGen et Trainer de composants et d' utiliser l' Evaluator et Pusher composant pour évaluer et pousser le modèle formé.

Reportez - vous au didacticiel de composants pour plus d' informations sur les composants de pipeline TFX.

Instanciez et exécutez le composant ExampleGen

example_gen = tfx.components.CsvExampleGen(input_base=_data_root)
interactive_context.run(example_gen)
WARNING:apache_beam.runners.interactive.interactive_environment:Dependencies required for Interactive Beam PCollection visualization are not available, please use: `pip install apache-beam[interactive]` to install necessary dependencies to enable all data visualization features.
WARNING:root:Make sure that locally built Python SDK docker image has Python 3.7 interpreter.
WARNING:apache_beam.io.tfrecordio:Couldn't find python-snappy so the implementation of _TFRecordUtil._masked_crc32c is not as fast as it could be.

Instanciez et exécutez le composant StatisticsGen

statistics_gen = tfx.components.StatisticsGen(
    examples=example_gen.outputs['examples'])
interactive_context.run(statistics_gen)
WARNING:root:Make sure that locally built Python SDK docker image has Python 3.7 interpreter.

Instanciez et exécutez le composant SchemaGen

infer_schema = tfx.components.SchemaGen(
    statistics=statistics_gen.outputs['statistics'], infer_feature_shape=True)
interactive_context.run(infer_schema)
WARNING: Logging before InitGoogleLogging() is written to STDERR
I1205 11:16:00.941947  6108 rdbms_metadata_access_object.cc:686] No property is defined for the Type

Instanciez et exécutez le composant Trainer

# Define the module file for the Trainer component
trainer_module_file = 'penguin_trainer.py'
%%writefile {trainer_module_file}

# Define the training algorithm for the Trainer module file
import os
from typing import List, Text

import tensorflow as tf
from tensorflow import keras

from tfx import v1 as tfx
from tfx_bsl.public import tfxio

from tensorflow_metadata.proto.v0 import schema_pb2

# Features used for classification - culmen length and depth, flipper length,
# body mass, and species.

_LABEL_KEY = 'species'

_FEATURE_KEYS = [
    'culmen_length_mm', 'culmen_depth_mm', 'flipper_length_mm', 'body_mass_g'
]


def _input_fn(file_pattern: List[Text],
              data_accessor: tfx.components.DataAccessor,
              schema: schema_pb2.Schema, batch_size: int) -> tf.data.Dataset:
  return data_accessor.tf_dataset_factory(
      file_pattern,
      tfxio.TensorFlowDatasetOptions(
          batch_size=batch_size, label_key=_LABEL_KEY), schema).repeat()


def _build_keras_model():
  inputs = [keras.layers.Input(shape=(1,), name=f) for f in _FEATURE_KEYS]
  d = keras.layers.concatenate(inputs)
  d = keras.layers.Dense(8, activation='relu')(d)
  d = keras.layers.Dense(8, activation='relu')(d)
  outputs = keras.layers.Dense(3)(d)
  model = keras.Model(inputs=inputs, outputs=outputs)
  model.compile(
      optimizer=keras.optimizers.Adam(1e-2),
      loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True),
      metrics=[keras.metrics.SparseCategoricalAccuracy()])
  return model


def run_fn(fn_args: tfx.components.FnArgs):
  schema = schema_pb2.Schema()
  tfx.utils.parse_pbtxt_file(fn_args.schema_path, schema)
  train_dataset = _input_fn(
      fn_args.train_files, fn_args.data_accessor, schema, batch_size=10)
  eval_dataset = _input_fn(
      fn_args.eval_files, fn_args.data_accessor, schema, batch_size=10)
  model = _build_keras_model()
  model.fit(
      train_dataset,
      epochs=int(fn_args.train_steps / 20),
      steps_per_epoch=20,
      validation_data=eval_dataset,
      validation_steps=fn_args.eval_steps)
  model.save(fn_args.serving_model_dir, save_format='tf')
Writing penguin_trainer.py

Exécutez le Trainer composant.

trainer = tfx.components.Trainer(
    module_file=os.path.abspath(trainer_module_file),
    examples=example_gen.outputs['examples'],
    schema=infer_schema.outputs['schema'],
    train_args=tfx.proto.TrainArgs(num_steps=100),
    eval_args=tfx.proto.EvalArgs(num_steps=50))
interactive_context.run(trainer)
running bdist_wheel
running build
running build_py
creating build
creating build/lib
copying penguin_trainer.py -> build/lib
installing to /tmp/tmpum1crtxy
running install
running install_lib
copying build/lib/penguin_trainer.py -> /tmp/tmpum1crtxy
running install_egg_info
running egg_info
creating tfx_user_code_Trainer.egg-info
writing tfx_user_code_Trainer.egg-info/PKG-INFO
writing dependency_links to tfx_user_code_Trainer.egg-info/dependency_links.txt
writing top-level names to tfx_user_code_Trainer.egg-info/top_level.txt
writing manifest file 'tfx_user_code_Trainer.egg-info/SOURCES.txt'
reading manifest file 'tfx_user_code_Trainer.egg-info/SOURCES.txt'
writing manifest file 'tfx_user_code_Trainer.egg-info/SOURCES.txt'
Copying tfx_user_code_Trainer.egg-info to /tmp/tmpum1crtxy/tfx_user_code_Trainer-0.0+fef7c4ed90dc336ca26daee59d65660cf8da5fa988b2ca0c89df2f558fda10f4-py3.7.egg-info
running install_scripts
creating /tmp/tmpum1crtxy/tfx_user_code_Trainer-0.0+fef7c4ed90dc336ca26daee59d65660cf8da5fa988b2ca0c89df2f558fda10f4.dist-info/WHEEL
creating '/tmp/tmpo87nn6ey/tfx_user_code_Trainer-0.0+fef7c4ed90dc336ca26daee59d65660cf8da5fa988b2ca0c89df2f558fda10f4-py3-none-any.whl' and adding '/tmp/tmpum1crtxy' to it
adding 'penguin_trainer.py'
adding 'tfx_user_code_Trainer-0.0+fef7c4ed90dc336ca26daee59d65660cf8da5fa988b2ca0c89df2f558fda10f4.dist-info/METADATA'
adding 'tfx_user_code_Trainer-0.0+fef7c4ed90dc336ca26daee59d65660cf8da5fa988b2ca0c89df2f558fda10f4.dist-info/WHEEL'
adding 'tfx_user_code_Trainer-0.0+fef7c4ed90dc336ca26daee59d65660cf8da5fa988b2ca0c89df2f558fda10f4.dist-info/top_level.txt'
adding 'tfx_user_code_Trainer-0.0+fef7c4ed90dc336ca26daee59d65660cf8da5fa988b2ca0c89df2f558fda10f4.dist-info/RECORD'
removing /tmp/tmpum1crtxy
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/setuptools/command/install.py:37: SetuptoolsDeprecationWarning: setup.py install is deprecated. Use build and pip and other standards-based tools.
  setuptools.SetuptoolsDeprecationWarning,
listing git files failed - pretending there aren't any
I1205 11:16:01.389324  6108 rdbms_metadata_access_object.cc:686] No property is defined for the Type
I1205 11:16:01.392832  6108 rdbms_metadata_access_object.cc:686] No property is defined for the Type
Processing /tmp/tfx-interactive-2021-12-05T11_15_56.285625-5hcexlo8/_wheels/tfx_user_code_Trainer-0.0+fef7c4ed90dc336ca26daee59d65660cf8da5fa988b2ca0c89df2f558fda10f4-py3-none-any.whl
Installing collected packages: tfx-user-code-Trainer
Successfully installed tfx-user-code-Trainer-0.0+fef7c4ed90dc336ca26daee59d65660cf8da5fa988b2ca0c89df2f558fda10f4
Epoch 1/5
20/20 [==============================] - 1s 11ms/step - loss: 0.9891 - sparse_categorical_accuracy: 0.4300 - val_loss: 0.9594 - val_sparse_categorical_accuracy: 0.4800
Epoch 2/5
20/20 [==============================] - 0s 6ms/step - loss: 0.8369 - sparse_categorical_accuracy: 0.6350 - val_loss: 0.7484 - val_sparse_categorical_accuracy: 0.8200
Epoch 3/5
20/20 [==============================] - 0s 6ms/step - loss: 0.5289 - sparse_categorical_accuracy: 0.8350 - val_loss: 0.5068 - val_sparse_categorical_accuracy: 0.7800
Epoch 4/5
20/20 [==============================] - 0s 6ms/step - loss: 0.4481 - sparse_categorical_accuracy: 0.7800 - val_loss: 0.4125 - val_sparse_categorical_accuracy: 0.8600
Epoch 5/5
20/20 [==============================] - 0s 6ms/step - loss: 0.3068 - sparse_categorical_accuracy: 0.8650 - val_loss: 0.3279 - val_sparse_categorical_accuracy: 0.8300
2021-12-05 11:16:06.493168: W tensorflow/python/util/util.cc:348] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them.
INFO:tensorflow:Assets written to: /tmp/tfx-interactive-2021-12-05T11_15_56.285625-5hcexlo8/Trainer/model/4/Format-Serving/assets
INFO:tensorflow:Assets written to: /tmp/tfx-interactive-2021-12-05T11_15_56.285625-5hcexlo8/Trainer/model/4/Format-Serving/assets

Évaluer et pousser le modèle

Utilisez le Evaluator composant pour évaluer et « bénir » le modèle avant d' utiliser le Pusher composant pour pousser le modèle dans un répertoire de service.

_serving_model_dir = os.path.join(tempfile.mkdtemp(),
                                  'serving_model/penguins_classification')
eval_config = tfma.EvalConfig(
    model_specs=[
        tfma.ModelSpec(label_key='species', signature_name='serving_default')
    ],
    metrics_specs=[
        tfma.MetricsSpec(metrics=[
            tfma.MetricConfig(
                class_name='SparseCategoricalAccuracy',
                threshold=tfma.MetricThreshold(
                    value_threshold=tfma.GenericValueThreshold(
                        lower_bound={'value': 0.6})))
        ])
    ],
    slicing_specs=[tfma.SlicingSpec()])
evaluator = tfx.components.Evaluator(
    examples=example_gen.outputs['examples'],
    model=trainer.outputs['model'],
    schema=infer_schema.outputs['schema'],
    eval_config=eval_config)
interactive_context.run(evaluator)
I1205 11:16:07.075275  6108 rdbms_metadata_access_object.cc:686] No property is defined for the Type
I1205 11:16:07.078761  6108 rdbms_metadata_access_object.cc:686] No property is defined for the Type
WARNING:root:Make sure that locally built Python SDK docker image has Python 3.7 interpreter.
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow_model_analysis/writers/metrics_plots_and_validations_writer.py:114: tf_record_iterator (from tensorflow.python.lib.io.tf_record) is deprecated and will be removed in a future version.
Instructions for updating:
Use eager execution and: 
`tf.data.TFRecordDataset(path)`
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow_model_analysis/writers/metrics_plots_and_validations_writer.py:114: tf_record_iterator (from tensorflow.python.lib.io.tf_record) is deprecated and will be removed in a future version.
Instructions for updating:
Use eager execution and: 
`tf.data.TFRecordDataset(path)`
pusher = tfx.components.Pusher(
    model=trainer.outputs['model'],
    model_blessing=evaluator.outputs['blessing'],
    push_destination=tfx.proto.PushDestination(
        filesystem=tfx.proto.PushDestination.Filesystem(
            base_directory=_serving_model_dir)))
interactive_context.run(pusher)
I1205 11:16:11.935312  6108 rdbms_metadata_access_object.cc:686] No property is defined for the Type

L'exécution du pipeline TFX remplit la base de données MLMD. Dans la section suivante, vous utilisez l'API MLMD pour interroger cette base de données pour obtenir des informations sur les métadonnées.

Interroger la base de données MLMD

La base de données MLMD stocke trois types de métadonnées :

  • Métadonnées sur le pipeline et les informations de lignée associées aux composants du pipeline
  • Métadonnées sur les artefacts générés lors de l'exécution du pipeline
  • Métadonnées sur les exécutions du pipeline

Un pipeline d'environnement de production typique sert plusieurs modèles à mesure que de nouvelles données arrivent. Lorsque vous rencontrez des résultats erronés dans les modèles servis, vous pouvez interroger la base de données MLMD pour isoler les modèles erronés. Vous pouvez ensuite tracer la lignée des composants du pipeline qui correspondent à ces modèles pour déboguer vos modèles

Mettre en place le magasin de métadonnées (MD) avec le InteractiveContext défini précédemment pour interroger la base de données MLMD.

connection_config = interactive_context.metadata_connection_config
store = mlmd.MetadataStore(connection_config)

# All TFX artifacts are stored in the base directory
base_dir = connection_config.sqlite.filename_uri.split('metadata.sqlite')[0]

Créez des fonctions d'assistance pour afficher les données du magasin MD.

def display_types(types):
  # Helper function to render dataframes for the artifact and execution types
  table = {'id': [], 'name': []}
  for a_type in types:
    table['id'].append(a_type.id)
    table['name'].append(a_type.name)
  return pd.DataFrame(data=table)
def display_artifacts(store, artifacts):
  # Helper function to render dataframes for the input artifacts
  table = {'artifact id': [], 'type': [], 'uri': []}
  for a in artifacts:
    table['artifact id'].append(a.id)
    artifact_type = store.get_artifact_types_by_id([a.type_id])[0]
    table['type'].append(artifact_type.name)
    table['uri'].append(a.uri.replace(base_dir, './'))
  return pd.DataFrame(data=table)
def display_properties(store, node):
  # Helper function to render dataframes for artifact and execution properties
  table = {'property': [], 'value': []}
  for k, v in node.properties.items():
    table['property'].append(k)
    table['value'].append(
        v.string_value if v.HasField('string_value') else v.int_value)
  for k, v in node.custom_properties.items():
    table['property'].append(k)
    table['value'].append(
        v.string_value if v.HasField('string_value') else v.int_value)
  return pd.DataFrame(data=table)

Tout d' abord, le magasin de requête MD pour une liste de tous ses stockés ArtifactTypes .

display_types(store.get_artifact_types())

Ensuite, interroger tous les PushedModel artefacts.

pushed_models = store.get_artifacts_by_type("PushedModel")
display_artifacts(store, pushed_models)

Interrogez le magasin MD pour le dernier modèle poussé. Ce tutoriel n'a qu'un seul modèle poussé.

pushed_model = pushed_models[-1]
display_properties(store, pushed_model)

L'une des premières étapes du débogage d'un modèle poussé consiste à examiner quel modèle entraîné est poussé et quelles données d'entraînement sont utilisées pour entraîner ce modèle.

MLMD fournit des API de parcours pour parcourir le graphique de provenance, que vous pouvez utiliser pour analyser la provenance du modèle.

def get_one_hop_parent_artifacts(store, artifacts):
  # Get a list of artifacts within a 1-hop of the artifacts of interest
  artifact_ids = [artifact.id for artifact in artifacts]
  executions_ids = set(
      event.execution_id
      for event in store.get_events_by_artifact_ids(artifact_ids)
      if event.type == mlmd.proto.Event.OUTPUT)
  artifacts_ids = set(
      event.artifact_id
      for event in store.get_events_by_execution_ids(executions_ids)
      if event.type == mlmd.proto.Event.INPUT)
  return [artifact for artifact in store.get_artifacts_by_id(artifacts_ids)]

Interrogez les artefacts parents pour le modèle poussé.

parent_artifacts = get_one_hop_parent_artifacts(store, [pushed_model])
display_artifacts(store, parent_artifacts)

Interrogez les propriétés du modèle.

exported_model = parent_artifacts[0]
display_properties(store, exported_model)

Interrogez les artefacts en amont pour le modèle.

model_parents = get_one_hop_parent_artifacts(store, [exported_model])
display_artifacts(store, model_parents)

Obtenez les données d'entraînement avec lesquelles le modèle a été entraîné.

used_data = model_parents[0]
display_properties(store, used_data)

Maintenant que vous disposez des données d'entraînement avec lesquelles le modèle a été entraîné, interrogez à nouveau la base de données pour trouver l'étape d'entraînement (exécution). Interrogez le magasin MD pour obtenir une liste des types d'exécution enregistrés.

display_types(store.get_execution_types())

L'étape de formation est le ExecutionType nommé tfx.components.trainer.component.Trainer . Parcourez le magasin MD pour faire courir le trainer qui correspond au modèle poussé.

def find_producer_execution(store, artifact):
  executions_ids = set(
      event.execution_id
      for event in store.get_events_by_artifact_ids([artifact.id])
      if event.type == mlmd.proto.Event.OUTPUT)
  return store.get_executions_by_id(executions_ids)[0]

trainer = find_producer_execution(store, exported_model)
display_properties(store, trainer)

Résumé

Dans ce didacticiel, vous avez appris comment tirer parti de MLMD pour tracer la lignée de vos composants de pipeline TFX et résoudre les problèmes.

Pour en savoir plus sur l'utilisation de MLMD, consultez ces ressources supplémentaires :