Rejoignez la communauté SIG TFX-Addons et contribuez à rendre TFX encore meilleur ! Rejoignez SIG TFX-Addons

Le composant Transform TFX Pipeline

Le composant de pipeline Transform TFX effectue l'ingénierie des fonctionnalités sur tf.Exemples émis par un composant ExampleGen , à l'aide d'un schéma de données créé par un composant SchemaGen , et émet à la fois un SavedModel ainsi que des statistiques sur les données avant et après transformation. Lorsqu'il est exécuté, le SavedModel acceptera tf.Examples émis par un composant ExampleGen et émettra les données de fonction transformées.

  • Consomme: tf.Examples d'un composant ExampleGen et un schéma de données d'un composant SchemaGen.
  • Émet: un SavedModel vers un composant Trainer, des statistiques de pré-transformation et de post-transformation.

Configuration d'un composant de transformation

Une fois que votre preprocessing_fn est écrit, il doit être défini dans un module python qui est ensuite fourni au composant Transform en tant qu'entrée. Ce module sera chargé par transform et la fonction nommée preprocessing_fn sera trouvée et utilisée par Transform pour construire le pipeline de prétraitement.

transform = Transform(
    examples=example_gen.outputs['examples'],
    schema=schema_gen.outputs['schema'],
    module_file=os.path.abspath(_taxi_transform_module_file))

De plus, vous souhaiterez peut-être fournir des options pour le calcul des statistiques de pré-transformation ou de post-transformation basé sur TFDV . Pour ce faire, définissez un stats_options_updater_fn dans le même module.

Transformer et transformer TensorFlow

Transform fait un usage intensif de TensorFlow Transform pour effectuer l'ingénierie d' entités sur votre jeu de données. TensorFlow Transform est un excellent outil pour transformer les données d'entités avant qu'elles ne soient transmises à votre modèle et dans le cadre du processus de formation. Les transformations de fonctionnalités courantes incluent:

  • Incorporation : conversion d'entités clairsemées (comme les ID entiers produits par un vocabulaire) en entités denses en trouvant une cartographie significative d'un espace de grande dimension à un espace de faible dimension. Reportez-vous à l' unité Embeddings dans le cours d'initiation à l' apprentissage automatique pour une introduction aux incorporations.
  • Génération de vocabulaire : conversion de chaînes ou d'autres caractéristiques non numériques en nombres entiers en créant un vocabulaire qui mappe chaque valeur unique à un numéro d'identification.
  • Normalisation des valeurs : transformation des entités numériques afin qu'elles tombent toutes dans une plage similaire.
  • Bucketisation : conversion d'entités à valeur continue en entités catégorielles en attribuant des valeurs à des buckets discrets.
  • Enrichir les fonctionnalités de texte : produire des fonctionnalités à partir de données brutes telles que des jetons, des n-grammes, des entités, des sentiments, etc., pour enrichir l'ensemble de fonctionnalités.

TensorFlow Transform prend en charge ces types de transformations et de nombreux autres:

  • Générez automatiquement un vocabulaire à partir de vos dernières données.

  • Effectuez des transformations arbitraires sur vos données avant de les envoyer à votre modèle. TensorFlow Transform crée des transformations dans le graphique TensorFlow pour votre modèle afin que les mêmes transformations soient effectuées au moment de l'entraînement et de l'inférence. Vous pouvez définir des transformations qui font référence aux propriétés globales des données, comme la valeur maximale d'une fonctionnalité dans toutes les instances d'entraînement.

Vous pouvez transformer vos données comme vous le souhaitez avant d'exécuter TFX. Mais si vous le faites dans TensorFlow Transform, les transformations font partie du graphique TensorFlow. Cette approche permet d'éviter le biais de formation / service.

Les transformations à l'intérieur de votre code de modélisation utilisent FeatureColumns. À l'aide de FeatureColumns, vous pouvez définir des compartiments, des intégrations qui utilisent des vocabulaires prédéfinis ou toute autre transformation pouvant être définie sans regarder les données.

En revanche, TensorFlow Transform est conçu pour les transformations qui nécessitent un passage complet sur les données pour calculer des valeurs qui ne sont pas connues à l'avance. Par exemple, la génération de vocabulaire nécessite un passage complet sur les données.

Outre le calcul des valeurs à l'aide d'Apache Beam, TensorFlow Transform permet aux utilisateurs d'incorporer ces valeurs dans un graphe TensorFlow, qui peut ensuite être chargé dans le graphe d'entraînement. Par exemple, lors de la normalisation d'entités, la fonction tft.scale_to_z_score calculera la moyenne et l'écart type d'une tft.scale_to_z_score , ainsi qu'une représentation, dans un graphe TensorFlow, de la fonction qui soustrait la moyenne et divise par l'écart type. En émettant un graphique TensorFlow, pas seulement des statistiques, TensorFlow Transform simplifie le processus de création de votre pipeline de prétraitement.

Étant donné que le prétraitement est exprimé sous forme de graphique, il peut se produire sur le serveur et il est garanti d'être cohérent entre la formation et la diffusion. Cette cohérence élimine une source de biais de formation / service.

TensorFlow Transform permet aux utilisateurs de spécifier leur pipeline de prétraitement à l'aide du code TensorFlow. Cela signifie qu'un pipeline est construit de la même manière qu'un graphe TensorFlow. Si seules les opérations TensorFlow étaient utilisées dans ce graphique, le pipeline serait une carte pure qui accepte des lots d'entrée et renvoie des lots de sortie. Un tel pipeline équivaudrait à placer ce graphique dans votre input_fn lors de l'utilisation de l'API tf.Estimator . Afin de spécifier des opérations passe-partout telles que le calcul des quantiles, TensorFlow Transform fournit des fonctions spéciales appelées analyzers qui apparaissent comme des opérations TensorFlow, mais spécifient en fait un calcul différé qui sera effectué par Apache Beam, et la sortie insérée dans le graphique en tant que constant. Alors qu'une opération TensorFlow ordinaire prendra un seul lot en entrée, effectuera un calcul sur ce lot et émettra un lot, un analyzer effectuera une réduction globale (implémentée dans Apache Beam) sur tous les lots et retournera le résultat.

En combinant des opérations TensorFlow ordinaires et des analyseurs de transformation TensorFlow, les utilisateurs peuvent créer des pipelines complexes pour prétraiter leurs données. Par exemple, la fonction tft.scale_to_z_score prend un tenseur d'entrée et renvoie ce tenseur normalisé pour avoir une moyenne de 0 et une variance de 1 . Pour ce faire, il appelle les analyseurs de mean et de var sous le capot, ce qui générera effectivement des constantes dans le graphique égales à la moyenne et à la variance du tenseur d'entrée. Il utilisera ensuite les opérations TensorFlow pour soustraire la moyenne et la diviser par l'écart type.

La transformation TensorFlow preprocessing_fn

Le composant TFX Transform simplifie l'utilisation de Transform en gérant les appels d'API liés à la lecture et à l'écriture de données, et en écrivant la sortie SavedModel sur le disque. En tant qu'utilisateur TFX, vous n'avez qu'à définir une seule fonction appelée preprocessing_fn . Dans preprocessing_fn vous définissez une série de fonctions qui manipulent le dict d'entrée des tenseurs pour produire le dict de sortie des tenseurs. Vous pouvez trouver des fonctions d'assistance telles que scale_to_0_1 et compute_and_apply_vocabulary l' API TensorFlow Transform ou utiliser des fonctions TensorFlow normales comme indiqué ci-dessous.

def preprocessing_fn(inputs):
  """tf.transform's callback function for preprocessing inputs.

  Args:
    inputs: map from feature keys to raw not-yet-transformed features.

  Returns:
    Map from string feature key to transformed feature operations.
  """
  outputs = {}
  for key in _DENSE_FLOAT_FEATURE_KEYS:
    # Preserve this feature as a dense float, setting nan's to the mean.
    outputs[_transformed_name(key)] = transform.scale_to_z_score(
        _fill_in_missing(inputs[key]))

  for key in _VOCAB_FEATURE_KEYS:
    # Build a vocabulary for this feature.
    outputs[_transformed_name(
        key)] = transform.compute_and_apply_vocabulary(
            _fill_in_missing(inputs[key]),
            top_k=_VOCAB_SIZE,
            num_oov_buckets=_OOV_SIZE)

  for key in _BUCKET_FEATURE_KEYS:
    outputs[_transformed_name(key)] = transform.bucketize(
        _fill_in_missing(inputs[key]), _FEATURE_BUCKET_COUNT)

  for key in _CATEGORICAL_FEATURE_KEYS:
    outputs[_transformed_name(key)] = _fill_in_missing(inputs[key])

  # Was this passenger a big tipper?
  taxi_fare = _fill_in_missing(inputs[_FARE_KEY])
  tips = _fill_in_missing(inputs[_LABEL_KEY])
  outputs[_transformed_name(_LABEL_KEY)] = tf.where(
      tf.is_nan(taxi_fare),
      tf.cast(tf.zeros_like(taxi_fare), tf.int64),
      # Test if the tip was > 20% of the fare.
      tf.cast(
          tf.greater(tips, tf.multiply(taxi_fare, tf.constant(0.2))), tf.int64))

  return outputs

Comprendre les entrées du preprocessing_fn

Le preprocessing_fn décrit une série d'opérations sur des tenseurs (c'est-à-dire, Tensor s ou SparseTensor s) et donc pour écrire correctement le preprocessing_fn il est nécessaire de comprendre comment vos données sont représentées sous forme de tenseurs. L'entrée du preprocessing_fn est déterminée par le schéma. Un proto de Schema contient une liste de Feature , et Transform les convertit en une «spécification de fonctionnalité» (parfois appelée «spécification d'analyse») qui est un dict dont les clés sont des noms de fonctionnalité et dont les valeurs sont l'une de FixedLenFeature ou VarLenFeature (ou autre options non utilisées par TensorFlow Transform).

Les règles pour déduire une spécification d'entité à partir du Schema sont

  • Chaque feature avec un jeu de shape se traduira par un tf.FixedLenFeature avec shape et default_value=None . presence.min_fraction doit être 1 sinon et une erreur en résultera, car quand il n'y a pas de valeur par défaut, un tf.FixedLenFeature exige que la fonction soit toujours présente.
  • Chaque feature dont la shape n'est pas définie entraînera une VarLenFeature .
  • Chaque sparse_feature aboutira à un tf.SparseFeature dont la size et is_sorted sont déterminés par les champs fixed_shape et is_sorted du message SparseFeature .
  • Les index_feature utilisées comme index_feature ou value_feature d'une sparse_feature n'auront pas leur propre entrée générée dans la spécification d'entité.
  • La correspondance entre le type domaine de la feature (ou la valeur caractéristique d'un sparse_feature proto) et le dtype de la spécification de caractéristique est donnée par le tableau suivant:
type dtype
schema_pb2.INT tf.int64
schema_pb2.FLOAT tf.float32
schema_pb2.BYTES tf.string

Utilisation de TensorFlow Transform pour gérer les étiquettes de chaîne

Habituellement, on veut utiliser TensorFlow Transform pour à la fois générer un vocabulaire et appliquer ce vocabulaire pour convertir des chaînes en nombres entiers. Lorsque vous suivez ce flux de travail, le input_fn construit dans le modèle sortira la chaîne integerized. Cependant, les étiquettes sont une exception, car pour que le modèle puisse mapper les étiquettes de sortie (entier) sur des chaînes, le modèle a besoin de input_fn pour générer une étiquette de chaîne, avec une liste de valeurs possibles de l'étiquette. Par exemple, si les étiquettes sont cat et dog alors la sortie de input_fn doit être ces chaînes brutes, et les clés ["cat", "dog"] doivent être passées dans l'estimateur en tant que paramètre (voir les détails ci-dessous).

Afin de gérer le mappage des étiquettes de chaîne en entiers, vous devez utiliser TensorFlow Transform pour générer un vocabulaire. Nous démontrons cela dans l'extrait de code ci-dessous:

def _preprocessing_fn(inputs):
  """Preprocess input features into transformed features."""

  ...


  education = inputs[features.RAW_LABEL_KEY]
  _ = tft.vocabulary(education, vocab_filename=features.RAW_LABEL_KEY)

  ...

La fonction de prétraitement ci-dessus prend la fonction d'entrée brute (qui sera également renvoyée dans le cadre de la sortie de la fonction de prétraitement) et appelle tft.vocabulary dessus. Il en résulte un vocabulaire généré pour l' education qui peut être consulté dans le modèle.

L'exemple montre également comment transformer une étiquette, puis générer un vocabulaire pour l'étiquette transformée. En particulier, il prend la education étiquette brute et convertit toutes les étiquettes sauf les 5 premières (par fréquence) en UNKNOWN , sans convertir l'étiquette en entier.

Dans le code du modèle, le classificateur doit recevoir le vocabulaire généré par tft.vocabulary comme argument label_vocabulary . Cela se fait en lisant d'abord ce vocabulaire sous forme de liste avec une fonction d'assistance. Ceci est montré dans l'extrait ci-dessous. Notez que l'exemple de code utilise l'étiquette transformée décrite ci-dessus, mais ici nous montrons le code pour utiliser l'étiquette brute.

def create_estimator(pipeline_inputs, hparams):

  ...

  tf_transform_output = trainer_util.TFTransformOutput(
      pipeline_inputs.transform_dir)

  # vocabulary_by_name() returns a Python list.
  label_vocabulary = tf_transform_output.vocabulary_by_name(
      features.RAW_LABEL_KEY)

  return tf.contrib.learn.DNNLinearCombinedClassifier(
      ...
      n_classes=len(label_vocab),
      label_vocabulary=label_vocab,
      ...)

Configuration des statistiques de pré-transformation et de post-transformation

Comme mentionné ci-dessus, le composant Transform appelle TFDV pour calculer les statistiques avant et après transformation. TFDV prend en entrée un objet StatsOptions facultatif. Les utilisateurs peuvent souhaiter configurer cet objet pour activer certaines statistiques supplémentaires (par exemple les statistiques NLP) ou pour définir des seuils qui sont validés (par exemple, la fréquence de jeton min / max). Pour ce faire, définissez un stats_options_updater_fn dans le fichier du module.

def stats_options_updater_fn(stats_type, stats_options):
  ...
  if stats_type == stats_options_util.StatsType.PRE_TRANSFORM:
    # Update stats_options to modify pre-transform statistics computation.
    # Most constraints are specified in the schema which can be accessed
    # via stats_options.schema.
  if stats_type == stats_options_util.StatsType.POST_TRANSFORM
    # Update stats_options to modify post-transform statistics computation.
    # Most constraints are specified in the schema which can be accessed
    # via stats_options.schema.
  return stats_options

Les statistiques post-transformées bénéficient souvent de la connaissance du vocabulaire utilisé pour le prétraitement d'une entité. Le nom de vocabulaire au mappage de chemin est fourni à StatsOptions (et donc à TFDV) pour chaque vocabulaire généré par TFT. En outre, pour les applications à l' extérieur vocabulaires créés peuvent être ajoutés soit par (i) modifier directement les vocab_paths dictionnaire de l' intérieur StatsOptions ou par (ii) en utilisant tft.annotate_asset .