Treten Sie der SIG TFX-Addons-Community bei und helfen Sie, TFX noch besser zu machen!

Besseres ML-Engineering mit ML-Metadaten

Auf TensorFlow.org ansehen In Google Colab ausführen Quelle auf GitHub anzeigen Notizbuch herunterladen

Angenommen, Sie richten eine Produktions-ML-Pipeline ein, um Pinguine zu klassifizieren. Die Pipeline nimmt Ihre Trainingsdaten auf, trainiert und bewertet ein Modell und überträgt es in die Produktion.

Wenn Sie jedoch später versuchen, dieses Modell mit einem größeren Datensatz zu verwenden, der verschiedene Pinguinarten enthält, stellen Sie fest, dass sich Ihr Modell nicht wie erwartet verhält und die Arten falsch klassifiziert.

An dieser Stelle interessiert Sie Folgendes:

  • Was ist die effizienteste Methode zum Debuggen des Modells, wenn das einzige verfügbare Artefakt das Modell in der Produktion ist?
  • Mit welchem ​​Trainingsdatensatz wurde das Modell trainiert?
  • Welcher Trainingslauf führte zu diesem fehlerhaften Modell?
  • Wo sind die Ergebnisse der Modellbewertung?
  • Wo mit dem Debuggen beginnen?

ML Metadata (MLMD) ist eine Bibliothek, die die mit ML-Modellen verknüpften Metadaten nutzt, um Ihnen bei der Beantwortung dieser und weiterer Fragen zu helfen. Eine hilfreiche Analogie ist, sich diese Metadaten als Äquivalent zum Anmelden in der Softwareentwicklung vorzustellen. Mit MLMD können Sie die mit den verschiedenen Komponenten Ihrer ML-Pipeline verbundenen Artefakte und Herkunft zuverlässig verfolgen.

In diesem Tutorial richten Sie eine TFX-Pipeline ein, um ein Modell zu erstellen, das Pinguine basierend auf der Körpermasse und der Länge und Tiefe ihrer Halme und der Länge ihrer Flossen in drei Arten einteilt. Anschließend verwenden Sie MLMD, um die Herkunft von Pipelinekomponenten zu verfolgen.

TFX-Pipelines in Colab

Colab ist eine leichtgewichtige Entwicklungsumgebung, die sich deutlich von einer Produktionsumgebung unterscheidet. In der Produktion können Sie verschiedene Pipeline-Komponenten wie Datenaufnahme, Transformation, Modelltraining, Ausführungshistorien usw. über mehrere verteilte Systeme hinweg verwenden. Für dieses Tutorial sollten Sie sich bewusst sein, dass es erhebliche Unterschiede bei der Orchestrierung und der Metadatenspeicherung gibt – alles wird lokal in Colab gehandhabt. Erfahren Sie mehr über TFX in Colab hier .

Einrichten

Zuerst installieren und importieren wir die erforderlichen Pakete, richten Pfade ein und laden Daten herunter.

Upgrade-Pip

Um zu vermeiden, dass Pip in einem System aktualisiert wird, wenn es lokal ausgeführt wird, stellen Sie sicher, dass wir in Colab ausgeführt werden. Lokale Systeme können natürlich separat nachgerüstet werden.

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

TFX installieren und importieren

pip install -q -U tfx

Pakete importieren

Hast du die Laufzeit neu gestartet?

Wenn Sie Google Colab verwenden und die obige Zelle zum ersten Mal ausführen, müssen Sie die Laufzeit neu starten, indem Sie oben auf die Schaltfläche "LAUFZEIT NEU STARTEN" klicken oder das Menü "Laufzeit > Laufzeit neu starten ..." verwenden. Dies liegt an der Art und Weise, wie Colab Pakete lädt.

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

Überprüfen Sie die TFX- und MLMD-Versionen.

from tfx import v1 as tfx
print('TFX version: {}'.format(tfx.__version__))
import ml_metadata as mlmd
print('MLMD version: {}'.format(mlmd.__version__))
WARNING:absl:RuntimeParameter is only supported on Cloud-based DAG runner currently.
TFX version: 0.30.0
MLMD version: 0.30.0

Laden Sie den Datensatz herunter

In dieser Kollaboration verwenden wir den Palmer Penguins-Datensatz, der auf Github zu finden ist. Wir haben den Datensatz verarbeitet, indem wir alle unvollständigen Datensätze int32 island und sex int32 und Labels in int32 konvertiert int32 . Der Datensatz enthält 334 Aufzeichnungen über die Körpermasse und die Länge und Tiefe der culmens von Pinguinen und die Länge ihrer Flossen. Sie verwenden diese Daten, um Pinguine in eine von drei Arten einzuordnen.

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-datay2haa9jo/penguins_processed.csv',
 <http.client.HTTPMessage at 0x7efdb511f810>)

Erstellen Sie einen InteractiveContext

Um TFX-Komponenten interaktiv in diesem Notebook auszuführen, erstellen Sie einen InteractiveContext . Der InteractiveContext verwendet ein temporäres Verzeichnis mit einer kurzlebigen MLMD-Datenbankinstanz. Beachten Sie, dass Aufrufe von InteractiveContext außerhalb der Colab-Umgebung keine Operationen sind.

Im Allgemeinen empfiehlt es sich, ähnliche Pipelineausführungen unter einem Context zu gruppieren.

interactive_context = InteractiveContext()
WARNING:absl:InteractiveContext pipeline_root argument not provided: using temporary directory /tmp/tfx-interactive-2021-06-02T09_09_36.757584-87ovf4p_ as root for pipeline outputs.
WARNING:absl:InteractiveContext metadata_connection_config not provided: using SQLite ML Metadata database at /tmp/tfx-interactive-2021-06-02T09_09_36.757584-87ovf4p_/metadata.sqlite.

Konstruieren Sie die TFX-Pipeline

Eine TFX-Pipeline besteht aus mehreren Komponenten, die verschiedene Aspekte des ML-Workflows ausführen. In diesem Notebook erstellen und führen Sie die ExampleGen , StatisticsGen , SchemaGen und Trainer und verwenden die Evaluator und Pusher Komponente, um das trainierte Modell auszuwerten und zu pushen.

Weitere Informationen zu TFX-Pipelinekomponenten finden Sie im Komponenten-Lernprogramm .

Instanziieren und Ausführen der ExampleGen-Komponente

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:apache_beam.io.tfrecordio:Couldn't find python-snappy so the implementation of _TFRecordUtil._masked_crc32c is not as fast as it could be.

Instanziieren und Ausführen der StatisticsGen-Komponente

statistics_gen = tfx.components.StatisticsGen(
    examples=example_gen.outputs['examples'])
interactive_context.run(statistics_gen)

Instanziieren und Ausführen der SchemaGen-Komponente

infer_schema = tfx.components.SchemaGen(
    statistics=statistics_gen.outputs['statistics'], infer_feature_shape=True)
interactive_context.run(infer_schema)

Instanziieren und Ausführen der Trainerkomponente

# 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

Führen Sie die Trainer Komponente aus.

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)
ERROR:absl:udf_utils.get_fn {'train_args': '{\n  "num_steps": 100\n}', 'eval_args': '{\n  "num_steps": 50\n}', 'module_file': None, 'run_fn': None, 'trainer_fn': None, 'custom_config': 'null', 'module_path': 'penguin_trainer@/tmp/tfx-interactive-2021-06-02T09_09_36.757584-87ovf4p_/_wheels/tfx_user_code_Trainer-0.0+fef7c4ed90dc336ca26daee59d65660cf8da5fa988b2ca0c89df2f558fda10f4-py3-none-any.whl'} 'run_fn'
Epoch 1/5
20/20 [==============================] - 1s 42ms/step - loss: 1.2349 - sparse_categorical_accuracy: 0.3166 - val_loss: 1.0058 - val_sparse_categorical_accuracy: 0.3200
Epoch 2/5
20/20 [==============================] - 0s 7ms/step - loss: 0.9357 - sparse_categorical_accuracy: 0.4969 - val_loss: 0.8231 - val_sparse_categorical_accuracy: 0.7800
Epoch 3/5
20/20 [==============================] - 0s 6ms/step - loss: 0.7468 - sparse_categorical_accuracy: 0.8277 - val_loss: 0.6455 - val_sparse_categorical_accuracy: 0.7800
Epoch 4/5
20/20 [==============================] - 0s 6ms/step - loss: 0.5482 - sparse_categorical_accuracy: 0.8105 - val_loss: 0.5178 - val_sparse_categorical_accuracy: 0.7800
Epoch 5/5
20/20 [==============================] - 0s 6ms/step - loss: 0.4635 - sparse_categorical_accuracy: 0.7674 - val_loss: 0.4715 - val_sparse_categorical_accuracy: 0.7800
INFO:tensorflow:Assets written to: /tmp/tfx-interactive-2021-06-02T09_09_36.757584-87ovf4p_/Trainer/model/4/Format-Serving/assets
INFO:tensorflow:Assets written to: /tmp/tfx-interactive-2021-06-02T09_09_36.757584-87ovf4p_/Trainer/model/4/Format-Serving/assets

Bewerten und pushen Sie das Modell

Verwenden Sie die Evaluator Komponente, um das Modell zu bewerten und zu „segnen“, bevor Sie die Pusher Komponente verwenden, um das Modell in ein Serving-Verzeichnis zu pushen.

_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)
ERROR:absl:udf_utils.get_fn {'eval_config': '{\n  "metrics_specs": [\n    {\n      "metrics": [\n        {\n          "class_name": "SparseCategoricalAccuracy",\n          "threshold": {\n            "value_threshold": {\n              "lower_bound": 0.6\n            }\n          }\n        }\n      ]\n    }\n  ],\n  "model_specs": [\n    {\n      "label_key": "species",\n      "signature_name": "serving_default"\n    }\n  ],\n  "slicing_specs": [\n    {}\n  ]\n}', 'feature_slicing_spec': None, 'fairness_indicator_thresholds': None, 'example_splits': 'null', 'module_file': None, 'module_path': None} 'custom_eval_shared_model'
ERROR:absl:udf_utils.get_fn {'eval_config': '{\n  "metrics_specs": [\n    {\n      "metrics": [\n        {\n          "class_name": "SparseCategoricalAccuracy",\n          "threshold": {\n            "value_threshold": {\n              "lower_bound": 0.6\n            }\n          }\n        }\n      ]\n    }\n  ],\n  "model_specs": [\n    {\n      "label_key": "species",\n      "signature_name": "serving_default"\n    }\n  ],\n  "slicing_specs": [\n    {}\n  ]\n}', 'feature_slicing_spec': None, 'fairness_indicator_thresholds': None, 'example_splits': 'null', 'module_file': None, 'module_path': None} 'custom_extractors'
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow_model_analysis/writers/metrics_plots_and_validations_writer.py:113: 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:113: 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)

Das Ausführen der TFX-Pipeline füllt die MLMD-Datenbank. Im nächsten Abschnitt verwenden Sie die MLMD-API, um diese Datenbank nach Metadateninformationen abzufragen.

Abfrage der MLMD-Datenbank

Die MLMD-Datenbank speichert drei Arten von Metadaten:

  • Metadaten zur Pipeline und Herkunftsinformationen, die den Pipelinekomponenten zugeordnet sind
  • Metadaten zu Artefakten, die während der Pipelineausführung generiert wurden
  • Metadaten zu den Ausführungen der Pipeline

Eine typische Produktionsumgebungspipeline bedient mehrere Modelle, wenn neue Daten eintreffen. Wenn Sie in bereitgestellten Modellen auf fehlerhafte Ergebnisse stoßen, können Sie die MLMD-Datenbank abfragen, um die fehlerhaften Modelle zu isolieren. Sie können dann die Herkunft der Pipelinekomponenten verfolgen, die diesen Modellen entsprechen, um Ihre Modelle zu debuggen

Richten Sie den Metadatenspeicher (MD) mit dem zuvor definierten InteractiveContext , um die MLMD-Datenbank abzufragen.

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]

Erstellen Sie einige Hilfsfunktionen, um die Daten aus dem MD-Speicher anzuzeigen.

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)

Fragen Sie zuerst den MD-Speicher nach einer Liste aller seiner gespeicherten ArtifactTypes .

display_types(store.get_artifact_types())

PushedModel Nächstes alle PushedModel Artefakte ab.

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

Fragen Sie den MD-Speicher nach dem neuesten Push-Modell ab. Dieses Tutorial hat nur ein gepushtes Modell.

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

Einer der ersten Schritte beim Debuggen eines gepushten Modells besteht darin, zu prüfen, welches trainierte Modell gepusht wird und mit welchen Trainingsdaten dieses Modell trainiert wird.

MLMD bietet Traversal-APIs zum Durchlaufen des Provenienzdiagramms, mit denen Sie die Modellherkunft analysieren können.

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

Fragen Sie die übergeordneten Artefakte für das übertragene Modell ab.

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

Fragen Sie die Eigenschaften für das Modell ab.

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

Fragen Sie die Upstream-Artefakte für das Modell ab.

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

Rufen Sie die Trainingsdaten ab, mit denen das Modell trainiert wurde.

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

Nachdem Sie nun über die Trainingsdaten verfügen, mit denen das Modell trainiert hat, fragen Sie die Datenbank erneut ab, um den Trainingsschritt (Ausführung) zu finden. Fragen Sie den MD-Speicher nach einer Liste der registrierten Ausführungsarten ab.

display_types(store.get_execution_types())

Der Trainingsschritt ist der ExecutionType namens tfx.components.trainer.component.Trainer . Durchqueren Sie den MD-Laden, um den Trainerlauf zu erhalten, der dem gepushten Modell entspricht.

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)

Zusammenfassung

In diesem Tutorial haben Sie erfahren, wie Sie MLMD nutzen können, um die Herkunft Ihrer TFX-Pipelinekomponenten zu verfolgen und Probleme zu beheben.

Weitere Informationen zur Verwendung von MLMD finden Sie in diesen zusätzlichen Ressourcen: