Diese Seite wurde von der Cloud Translation API übersetzt.
Switch to English

Erste Schritte mit TensorFlow Transform

In diesem Handbuch werden die Grundkonzepte von tf.Transform und deren Verwendung vorgestellt. Es wird:

  • Definieren Sie eine Vorverarbeitungsfunktion , eine logische Beschreibung der Pipeline, die die Rohdaten in Daten umwandelt, die zum Trainieren eines maschinellen Lernmodells verwendet werden.
  • Zeigen Sie die Apache Beam- Implementierung, die zum Transformieren von Daten verwendet wird, indem Sie die Vorverarbeitungsfunktion in eine Beam-Pipeline konvertieren.
  • Zusätzliche Verwendungsbeispiele anzeigen.

Definieren Sie eine Vorverarbeitungsfunktion

Die Vorverarbeitungsfunktion ist das wichtigste Konzept von tf.Transform . Die Vorverarbeitungsfunktion ist eine logische Beschreibung einer Transformation des Datensatzes. Die Vorverarbeitungsfunktion akzeptiert ein SparseTensor und gibt es zurück, wobei ein Tensor Tensor oder 2D SparseTensor . Es gibt zwei Arten von Funktionen, die zum Definieren der Vorverarbeitungsfunktion verwendet werden:

  1. Jede Funktion, die Tensoren akzeptiert und zurückgibt. Diese fügen dem Diagramm TensorFlow-Operationen hinzu, die Rohdaten in transformierte Daten umwandeln.
  2. Jede der Analysatoren , bereitgestellt durch tf.Transform . Analysatoren akzeptieren auch Tensoren und geben sie zurück. Im Gegensatz zu TensorFlow-Funktionen fügen sie dem Diagramm jedoch keine Operationen hinzu. Stattdessen veranlassen Analysatoren, dass tf.Transform eine tf.Transform außerhalb von TensorFlow berechnet. Sie verwenden die Eingabetensorwerte über den gesamten Datensatz, um einen konstanten Tensor zu generieren, der als Ausgabe zurückgegeben wird. Beispielsweise berechnet tft.min das Minimum eines Tensors über dem Datensatz. tf.Transform bietet einen festen Satz von Analysatoren, der jedoch in zukünftigen Versionen erweitert wird.

Beispiel für eine Vorverarbeitungsfunktion

Durch die Kombination von Analysatoren und regulären TensorFlow-Funktionen können Benutzer flexible Pipelines zum Transformieren von Daten erstellen. Die folgende Vorverarbeitungsfunktion transformiert jedes der drei Merkmale auf unterschiedliche Weise und kombiniert zwei der Merkmale:

import tensorflow as tf
import tensorflow_transform as tft
import tensorflow_transform.beam as tft_beam

def preprocessing_fn(inputs):
  x = inputs['x']
  y = inputs['y']
  s = inputs['s']
  x_centered = x - tft.mean(x)
  y_normalized = tft.scale_to_0_1(y)
  s_integerized = tft.compute_and_apply_vocabulary(s)
  x_centered_times_y_normalized = x_centered * y_normalized
  return {
      'x_centered': x_centered,
      'y_normalized': y_normalized,
      'x_centered_times_y_normalized': x_centered_times_y_normalized,
      's_integerized': s_integerized
  }

Hier sind x , y und s Tensor , die Eingabemerkmale darstellen. Der erste neue Tensor, der erstellt wird, x_centered , wird erstellt, indem tft.mean auf x tft.mean und von x subtrahiert wird. tft.mean(x) gibt einen Tensor zurück, der den Mittelwert des Tensors x . x_centered ist der Tensor x mit dem subtrahierten Mittelwert.

Der zweite neue Tensor, y_normalized , wird auf ähnliche Weise erstellt, jedoch unter Verwendung der Convenience-Methode tft.scale_to_0_1 . Diese Methode funktioniert ähnlich wie x_centered , nämlich ein Maximum und ein Minimum zu berechnen und diese zur Skalierung von y .

Der Tensor s_integerized zeigt ein Beispiel für die Manipulation von Zeichenfolgen. In diesem Fall nehmen wir eine Zeichenfolge und ordnen sie einer Ganzzahl zu. Dies verwendet die Komfortfunktion tft.compute_and_apply_vocabulary . Diese Funktion berechnet mithilfe eines Analysators die eindeutigen Werte, die von den Eingabezeichenfolgen verwendet werden, und konvertiert die Eingabezeichenfolgen mithilfe von TensorFlow-Operationen in Indizes in der Tabelle der eindeutigen Werte.

Die letzte Spalte zeigt, dass es möglich ist, TensorFlow-Operationen zu verwenden, um neue Features durch Kombinieren von Tensoren zu erstellen.

Die Vorverarbeitungsfunktion definiert eine Pipeline von Operationen für ein Dataset. Um die Pipeline anzuwenden, setzen wir auf eine konkrete Implementierung der tf.Transform API. Die Apache Beam-Implementierung bietet PTransform der die Vorverarbeitungsfunktion eines Benutzers auf Daten PTransform . Der typische Workflow eines tf.Transform Benutzers erstellt eine Vorverarbeitungsfunktion und integriert diese dann in eine größere Beam-Pipeline, um die Daten für das Training zu erstellen.

Batching

Die Stapelverarbeitung ist ein wichtiger Bestandteil von TensorFlow. Da eines der Ziele von tf.Transform besteht, ein TensorFlow-Diagramm für die Vorverarbeitung bereitzustellen, das in das Serving-Diagramm (und optional in das Trainingsdiagramm) integriert werden kann, ist die tf.Transform auch ein wichtiges Konzept in tf.Transform .

Während dies im obigen Beispiel nicht offensichtlich ist, werden der benutzerdefinierten Vorverarbeitungsfunktion Tensoren übergeben, die Stapel und nicht einzelne Instanzen darstellen, wie dies während des Trainings und des Servierens mit TensorFlow der Fall ist. Auf der anderen Seite führen Analysatoren eine Berechnung über den gesamten Datensatz durch, die einen einzelnen Wert und keinen Stapel von Werten zurückgibt. x ist ein Tensor mit der Form (batch_size,) , während tft.mean(x) ein Tensor mit der Form () . Die Subtraktion x - tft.mean(x) sendet, wobei der Wert von tft.mean(x) von jedem Element des durch x tft.mean(x) subtrahiert wird.

Apache Beam-Implementierung

Während die Vorverarbeitungsfunktion als logische Beschreibung einer Vorverarbeitungspipeline gedacht ist, die in mehreren Datenverarbeitungsframeworks implementiert ist, bietet tf.Transform eine kanonische Implementierung, die in Apache Beam verwendet wird. Diese Implementierung demonstriert die Funktionalität, die für eine Implementierung erforderlich ist. Es gibt keine formale API für diese Funktionalität, daher kann jede Implementierung eine API verwenden, die für das jeweilige Datenverarbeitungsframework idiomatisch ist.

Die Apache Beam-Implementierung bietet zwei PTransform , mit denen Daten für eine Vorverarbeitungsfunktion verarbeitet werden. Im Folgenden wird die Verwendung für das zusammengesetzte PTransform AnalyzeAndTransformDataset :

raw_data = [
    {'x': 1, 'y': 1, 's': 'hello'},
    {'x': 2, 'y': 2, 's': 'world'},
    {'x': 3, 'y': 3, 's': 'hello'}
]

raw_data_metadata = ...
transformed_dataset, transform_fn = (
    (raw_data, raw_data_metadata) | tft_beam.AnalyzeAndTransformDataset(
        preprocessing_fn))
transformed_data, transformed_metadata = transformed_dataset

Der Inhalt von transformed_data wird unten angezeigt und enthält die transformierten Spalten im gleichen Format wie die Rohdaten. Insbesondere sind die Werte von s_integerized [0, 1, 0] - diese Werte hängen davon ab, wie die Wörter hello und world auf Ganzzahlen abgebildet wurden, was deterministisch ist. Für die Spalte x_centered haben wir den Mittelwert subtrahiert, sodass die Werte der Spalte x , die [1.0, 2.0, 3.0] , [-1.0, 0.0, 1.0] . In ähnlicher Weise stimmen die restlichen Spalten mit den erwarteten Werten überein.

[{u's_integerized': 0,
  u'x_centered': -1.0,
  u'x_centered_times_y_normalized': -0.0,
  u'y_normalized': 0.0},
 {u's_integerized': 1,
  u'x_centered': 0.0,
  u'x_centered_times_y_normalized': 0.0,
  u'y_normalized': 0.5},
 {u's_integerized': 0,
  u'x_centered': 1.0,
  u'x_centered_times_y_normalized': 1.0,
  u'y_normalized': 1.0}]

Sowohl raw_data als auch transformed_data sind Datensätze. Die nächsten beiden Abschnitte zeigen, wie die Beam-Implementierung Datensätze darstellt und wie Daten auf die Festplatte gelesen und geschrieben werden. Der andere Rückgabewert, transform_fn , stellt die Transformation dar, die auf die Daten angewendet wird und im Folgenden ausführlich behandelt wird.

Das AnalyzeAndTransformDataset ist die Zusammensetzung der beiden grundlegenden Transformationen, die von der Implementierung AnalyzeDataset und TransformDataset bereitgestellt werden. Die folgenden zwei Codefragmente sind also äquivalent:

transformed_data, transform_fn = (
    my_data | tft_beam.AnalyzeAndTransformDataset(preprocessing_fn))
transform_fn = my_data | tft_beam.AnalyzeDataset(preprocessing_fn)
transformed_data = (my_data, transform_fn) | tft_beam.TransformDataset()

transform_fn ist eine reine Funktion, die eine Operation darstellt, die auf jede Zeile des Datasets angewendet wird. Insbesondere werden die Analysatorwerte bereits berechnet und als Konstanten behandelt. Im Beispiel enthält transform_fn als Konstanten den Mittelwert der Spalte x , das Minimum und das Maximum der Spalte y sowie das Vokabular, mit dem die Zeichenfolgen Ganzzahlen zugeordnet werden.

Ein wichtiges Merkmal von tf.Transform ist, dass transform_fn eine Zuordnung über Zeilen darstellt - es ist eine reine Funktion, die auf jede Zeile separat angewendet wird. Die gesamte Berechnung zum Aggregieren von Zeilen erfolgt in AnalyzeDataset . Darüber hinaus wird transform_fn als TensorFlow- Graph dargestellt, das in das Serving-Diagramm eingebettet werden kann.

In diesem speziellen Fall wird AnalyzeAndTransformDataset für Optimierungen bereitgestellt. Dies ist das gleiche Muster, das beim Scikit-Lernen verwendet wird und die Methoden fit , transform und fit_transform .

Datenformate und Schema

In den vorherigen Codebeispielen wurde der Code, der raw_data_metadata definiert, weggelassen. Die Metadaten enthalten das Schema, das das Layout der Daten definiert, sodass sie aus verschiedenen Formaten gelesen und in diese geschrieben werden. Selbst das im letzten Abschnitt gezeigte In-Memory-Format ist nicht selbstbeschreibend und erfordert, dass das Schema als Tensoren interpretiert wird.

Hier ist die Definition des Schemas für die Beispieldaten:

from tensorflow_transform.tf_metadata import dataset_metadata
from tensorflow_transform.tf_metadata import schema_utils

raw_data_metadata = dataset_metadata.DatasetMetadata(
      schema_utils.schema_from_feature_spec({
        's': tf.io.FixedLenFeature([], tf.string),
        'y': tf.io.FixedLenFeature([], tf.float32),
        'x': tf.io.FixedLenFeature([], tf.float32),
    }))

Die Klasse dataset_schema.Schema enthält die Informationen, die zum Parsen der Daten von ihrem On-Disk- oder In-Memory-Format in Tensoren erforderlich sind. Es wird normalerweise durch Aufrufen von schema_utils.schema_from_feature_spec mit einem Diktat-Mapping-Funktionsschlüssel für die Werte tf.io.FixedLenFeature , tf.io.VarLenFeature und tf.io.SparseFeature . Weitere Informationen finden Sie in der Dokumentation zu tf.parse_example .

Oben verwenden wir tf.io.FixedLenFeature um anzugeben, dass jedes Feature eine feste Anzahl von Werten enthält, in diesem Fall einen einzelnen Skalarwert. Da tf.Transform , hat der tatsächliche Tensor der das Feature darstellt, die Form (None,) wobei die unbekannte Dimension die Stapeldimension ist.

Eingabe und Ausgabe mit Apache Beam

Bisher verwendete das Datenformat für die Beispiele Listen von Wörterbüchern. Dies ist eine Vereinfachung, die auf der Fähigkeit von Apache Beam beruht, mit Listen zu arbeiten, sowie auf der Hauptdarstellung von Daten, der PCollection . Eine PCollection ist eine Datendarstellung, die Teil einer Beam-Pipeline ist. Eine Beam-Pipeline wird gebildet, indem verschiedene PTransform , einschließlich AnalyzeDataset und TransformDataset , AnalyzeDataset und die Pipeline ausgeführt werden. Eine PCollection wird nicht im Speicher der PCollection erstellt, sondern unter den PCollection verteilt (obwohl in diesem Abschnitt der speicherinterne Ausführungsmodus verwendet wird).

Das folgende Beispiel erfordert sowohl das Lesen als auch das Schreiben von Daten auf der Festplatte und das Darstellen von Daten als PCollection (keine Liste), siehe: census_example.py . Im Folgenden zeigen wir, wie Sie die Daten herunterladen und dieses Beispiel ausführen. Der Datensatz "Census Income" wird vom UCI Machine Learning Repository bereitgestellt. Dieser Datensatz enthält sowohl kategoriale als auch numerische Daten.

Die Daten sind im CSV-Format, hier sind die ersten beiden Zeilen:

39, State-gov, 77516, Bachelors, 13, Never-married, Adm-clerical, Not-in-family, White, Male, 2174, 0, 40, United-States, <=50K
50, Self-emp-not-inc, 83311, Bachelors, 13, Married-civ-spouse, Exec-managerial, Husband, White, Male, 0, 0, 13, United-States, <=50K

Die Spalten des Datensatzes sind entweder kategorisch oder numerisch. Da es viele Spalten gibt, wird ein Schema (ähnlich wie im vorherigen Beispiel) generiert, indem alle Spalten jedes Typs durchlaufen werden. Dieser Datensatz beschreibt ein Klassifizierungsproblem: Vorhersage der letzten Spalte, in der die Person mehr oder weniger als 50.000 pro Jahr verdient. Aus der Sicht von tf.Transform ist diese Bezeichnung jedoch nur eine weitere kategoriale Spalte.

Verwenden Sie dieses Schema, um die Daten aus der CSV-Datei zu lesen. Die Konstante ordered_columns enthält die Liste aller Spalten in der Reihenfolge, in der sie in der CSV-Datei ordered_columns werden. ordered_columns erforderlich, da das Schema diese Informationen nicht enthält. Einige zusätzliche Beam-Transformationen werden entfernt, da sie bereits beim Lesen aus der CSV-Datei ausgeführt wurden. Jede CSV-Zeile wird in eine Instanz im In-Memory-Format konvertiert.

In diesem Beispiel lassen wir die Funktion " education-num fehlen. Dies bedeutet, dass es als tf.io.VarLenFeature in der feature_spec und als tf.SparseTensor in der Vorverarbeitung_fn dargestellt wird. Um den möglicherweise fehlenden Feature-Wert zu behandeln, füllen wir fehlende Instanzen mit einem Standardwert aus, in diesem Fall 0.

converter = tft.coders.CsvCoder(ordered_columns, raw_data_schema)

raw_data = (
    p
    | 'ReadTrainData' >> textio.ReadFromText(train_data_file)
    | ...
    | 'DecodeTrainData' >> beam.Map(converter.decode))

Die Vorverarbeitung ähnelt dem vorherigen Beispiel, außer dass die Vorverarbeitungsfunktion programmgesteuert generiert wird, anstatt jede Spalte manuell anzugeben. In der folgenden Vorverarbeitungsfunktion sind NUMERICAL_COLUMNS und CATEGORICAL_COLUMNS Listen, die die Namen der numerischen und kategorialen Spalten enthalten:

def preprocessing_fn(inputs):
  """Preprocess input columns into transformed columns."""
  # Since we are modifying some features and leaving others unchanged, we
  # start by setting `outputs` to a copy of `inputs.
  outputs = inputs.copy()

  # Scale numeric columns to have range [0, 1].
  for key in NUMERIC_FEATURE_KEYS:
    outputs[key] = tft.scale_to_0_1(outputs[key])

  for key in OPTIONAL_NUMERIC_FEATURE_KEYS:
    # This is a SparseTensor because it is optional. Here we fill in a default
    # value when it is missing.
      sparse = tf.sparse.SparseTensor(outputs[key].indices, outputs[key].values,
                                      [outputs[key].dense_shape[0], 1])
      dense = tf.sparse.to_dense(sp_input=sparse, default_value=0.)
    # Reshaping from a batch of vectors of size 1 to a batch to scalars.
    dense = tf.squeeze(dense, axis=1)
    outputs[key] = tft.scale_to_0_1(dense)

  # For all categorical columns except the label column, we generate a
  # vocabulary but do not modify the feature.  This vocabulary is instead
  # used in the trainer, by means of a feature column, to convert the feature
  # from a string to an integer id.
  for key in CATEGORICAL_FEATURE_KEYS:
    tft.vocabulary(inputs[key], vocab_filename=key)

  # For the label column we provide the mapping from string to index.
  initializer = tf.lookup.KeyValueTensorInitializer(
      keys=['>50K', '<=50K'],
      values=tf.cast(tf.range(2), tf.int64),
      key_dtype=tf.string,
      value_dtype=tf.int64)
  table = tf.lookup.StaticHashTable(initializer, default_value=-1)

  outputs[LABEL_KEY] = table.lookup(outputs[LABEL_KEY])

  return outputs

Ein Unterschied zum vorherigen Beispiel besteht darin, dass die Beschriftungsspalte die Zuordnung von der Zeichenfolge zu einem Index manuell angibt. '>50' wird also auf 0 und '<=50K' auf 1 abgebildet, da es nützlich ist zu wissen, welcher Index im trainierten Modell welchem ​​Label entspricht.

Die Variable raw_data stellt eine PCollection , die Daten im gleichen Format wie die Liste raw_data (aus dem vorherigen Beispiel) enthält, wobei dieselbe AnalyzeAndTransformDataset Transformation verwendet wird. Das Schema wird an zwei Stellen verwendet: Lesen der Daten aus der CSV-Datei und als Eingabe für AnalyzeAndTransformDataset . Sowohl das CSV-Format als auch das In-Memory-Format müssen mit einem Schema gepaart werden, um sie als Tensoren zu interpretieren.

Die letzte Phase besteht darin, die transformierten Daten auf die Festplatte zu schreiben und hat eine ähnliche Form wie das Lesen der Rohdaten. Das dazu verwendete Schema ist Teil der Ausgabe von AnalyzeAndTransformDataset der ein Schema für die Ausgabedaten abgeleitet wird. Der Code zum Schreiben auf die Festplatte ist unten dargestellt. Das Schema ist Teil der Metadaten, verwendet die beiden jedoch austauschbar in der tf.Transform API (dh tf.Transform die Metadaten an den ExampleProtoCoder ). Beachten Sie, dass dies in ein anderes Format schreibt. textio.WriteToText anstelle von textio.WriteToText die integrierte Unterstützung von Beam für das TFRecord Format und verwenden Sie einen Codierer, um die Daten als Example zu codieren. Dies ist ein besseres Format für das Training, wie im nächsten Abschnitt gezeigt. transformed_eval_data_base liefert den Basisdateinamen für die einzelnen Shards, die geschrieben werden.

transformed_data | "WriteTrainData" >> tfrecordio.WriteToTFRecord(
    transformed_eval_data_base,
    coder=tft.coders.ExampleProtoCoder(transformed_metadata))

Zusätzlich zu den Trainingsdaten wird transform_fn auch mit den Metadaten ausgeschrieben:

_ = (
    transform_fn
    | 'WriteTransformFn' >> tft_beam.WriteTransformFn(working_dir))
transformed_metadata | 'WriteMetadata' >> tft_beam.WriteMetadata(
    transformed_metadata_file, pipeline=p)

Führen Sie die gesamte Beam-Pipeline mit p.run().wait_until_finish() . Bis zu diesem Punkt stellt die Beam-Pipeline eine verzögerte, verteilte Berechnung dar. Es enthält Anweisungen, was getan werden soll, aber die Anweisungen wurden nicht ausgeführt. Dieser letzte Aufruf führt die angegebene Pipeline aus.

Laden Sie den Volkszählungsdatensatz herunter

Laden Sie den Volkszählungsdatensatz mit den folgenden Shell-Befehlen herunter:

  wget https://archive.ics.uci.edu/ml/machine-learning-databases/adult/adult.data
  wget https://archive.ics.uci.edu/ml/machine-learning-databases/adult/adult.test

census_example.py beim Ausführen des Skripts census_example.py das Verzeichnis mit diesen Daten als erstes Argument. Das Skript erstellt ein temporäres Unterverzeichnis, um die vorverarbeiteten Daten hinzuzufügen.

Integration in TensorFlow Training

Der letzte Abschnitt von census_example.py zeigt, wie die vorverarbeiteten Daten zum Trainieren eines Modells verwendet werden. Weitere Informationen finden Sie in der Dokumentation zu Estimators . Der erste Schritt besteht darin, einen Estimator zu Estimator der eine Beschreibung der vorverarbeiteten Spalten erfordert. Jede numerische Spalte wird als beschrieben real_valued_column , die mit einer festen Größe (ein Wrapper um einen dichten Vektor 1 in diesem Beispiel). Jede kategorische Spalte wird aus der Zeichenfolge auf ganze Zahlen abgebildet und dann wird in weitergegeben indicator_column . tft.TFTransformOutput wird verwendet, um den Pfad der tft.TFTransformOutput für jedes kategoriale Feature zu ermitteln.

real_valued_columns = [feature_column.real_valued_column(key)
                       for key in NUMERIC_FEATURE_KEYS]

one_hot_columns = [
    tf.feature_column.indicator_column(
        tf.feature_column.categorical_column_with_vocabulary_file(
            key=key,
            vocabulary_file=tf_transform_output.vocabulary_file_by_name(
                vocab_filename=key)))
    for key in CATEGORICAL_FEATURE_KEYS]

estimator = tf.estimator.LinearClassifier(real_valued_columns + one_hot_columns)

Der nächste Schritt besteht darin, einen Builder zu erstellen, um die Eingabefunktion für Training und Evaluierung zu generieren. tf.Learn unterscheidet sich von dem von tf.Learn verwendeten tf.Learn da zum tf.Learn der transformierten Daten keine Feature-Spezifikation erforderlich ist. Verwenden Sie stattdessen die Metadaten für die transformierten Daten, um eine Feature-Spezifikation zu generieren.

def _make_training_input_fn(tf_transform_output, transformed_examples,
                            batch_size):
  ...
  def input_fn():
    """Input function for training and eval."""
    dataset = tf.data.experimental.make_batched_features_dataset(
        ..., tf_transform_output.transformed_feature_spec(), ...)

    transformed_features = tf.compat.v1.data.make_one_shot_iterator(
        dataset).get_next()
    ...

  return input_fn

Der verbleibende Code entspricht der Verwendung der Estimator Klasse. Das Beispiel enthält auch Code zum Exportieren des Modells im SavedModel Format. Das exportierte Modell kann von Tensorflow Serving oder der Cloud ML Engine verwendet werden .