Le composant Transform TFX Pipeline

Le composant de pipeline Transform TFX effectue l'ingénierie des fonctionnalités sur tf.Examples émis à partir d'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 pré-transformées et post-transformées. Lorsqu'il est exécuté, le SavedModel acceptera les tf.Examples émis par un composant ExampleGen et émettra les données de caractéristiques transformées.

  • Consomme : tf.Examples d'un composant ExampleGen et un schéma de données d'un composant SchemaGen.
  • Émet : un composant 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ées sur TFDV . Pour ce faire, définissez un stats_options_updater_fn dans le même module.

Transformer et transformer TensorFlow

Transform utilise largement TensorFlow Transform pour effectuer l'ingénierie des fonctionnalités sur votre ensemble 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 de caractéristiques éparses (comme les identifiants entiers produits par un vocabulaire) en caractéristiques denses en trouvant une correspondance significative de l'espace de grande dimension vers l'espace de faible dimension. Consultez l' unité Embeddings dans le cours d'initiation à l'apprentissage automatique pour une introduction aux embeddings.
  • 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 associe chaque valeur unique à un numéro d'identification.
  • Normalisation des valeurs : transformation des caractéristiques numériques afin qu'elles se situent toutes dans une plage similaire.
  • Bucketisation : conversion de caractéristiques à valeur continue en caractéristiques catégorielles en attribuant des valeurs à des compartiments 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 bien d'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'apprentissage 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é sur toutes les instances d'entraînement.

Vous pouvez transformer vos données comme bon vous semble 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 l'asymétrie formation/service.

Les transformations à l'intérieur de votre code de modélisation utilisent FeatureColumns. À l'aide de FeatureColumns, vous pouvez définir des bucketisations, des entierisations qui utilisent des vocabulaires prédéfinis ou toute autre transformation pouvant être définie sans consulter 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.

En plus de calculer des valeurs à l'aide d'Apache Beam, TensorFlow Transform permet aux utilisateurs d'intégrer ces valeurs dans un graphique TensorFlow, qui peut ensuite être chargé dans le graphique de formation. Par exemple, lors de la normalisation d'entités, la fonction tft.scale_to_z_score calcule la moyenne et l'écart type d'une entité, ainsi qu'une représentation, dans un graphique TensorFlow, de la fonction qui soustrait la moyenne et divise par l'écart type. En émettant un graphique TensorFlow, et 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 sa cohérence est garantie entre l'entraînement et le service. Cette cohérence élimine une source d'asymétrie 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 graphique 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 de passage complet telles que le calcul des quantiles, TensorFlow Transform fournit des fonctions spéciales appelées analyzers qui ressemblent à 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 des calculs 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 renverra le résultat.

En combinant des opérations TensorFlow ordinaires et des analyseurs TensorFlow Transform, 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 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 dans l' API de transformation TensorFlow ou utiliser les fonctions TensorFlow habituelles, 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:
    # If sparse make it dense, setting nan's to 0 or '', and apply zscore.
    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 les tenseurs (c'est-à-dire Tensor s ou SparseTensor s) et donc pour écrire le preprocessing_fn correctement, il est nécessaire de comprendre comment vos données sont représentées sous forme de tenseurs. L'entrée de preprocessing_fn est déterminée par le schéma. Un proto Schema contient une liste de Feature s 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és et dont les valeurs sont FixedLenFeature ou VarLenFeature (ou autre options non utilisées par TensorFlow Transform).

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

  • Chaque tf.FixedLenFeature shape feature et default_value=None . presence.min_fraction doit être 1 sinon et une erreur se produira, car lorsqu'il n'y a pas de valeur par défaut, un tf.FixedLenFeature nécessite que la fonctionnalité soit toujours présente.
  • Chaque feature dont la shape n'est pas définie entraînera un VarLenFeature .
  • Chaque sparse_feature se traduira par 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 entités 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 champ type de la feature (ou la feature values ​​d'un proto sparse_feature ) et le dtype de la feature spec 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 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 produira la chaîne entière. Cependant, les étiquettes sont une exception, car pour que le modèle puisse mapper les étiquettes de sortie (entiers) sur des chaînes, le modèle a besoin de input_fn pour générer une étiquette de chaîne, ainsi qu'une liste des valeurs possibles de l'étiquette. Par exemple, si les étiquettes sont cat et dog , la sortie de input_fn doit être ces chaînes brutes, et les clés ["cat", "dog"] doivent être transmises à l'estimateur en tant que paramètre (voir les détails ci-dessous).

Afin de gérer le mappage des étiquettes de chaîne sur des nombres entiers, vous devez utiliser TensorFlow Transform pour générer un vocabulaire. Nous le démontrons 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 caractéristique 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 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 l' 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 illustré 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 invoque TFDV pour calculer les statistiques de pré-transformation et de post-transformation. TFDV prend en entrée un objet StatsOptions facultatif. Les utilisateurs peuvent souhaiter paramétrer cet objet pour activer certaines statistiques supplémentaires (ex : statistiques NLP) ou pour fixer des seuils validés (ex : fréquence min/max des jetons). 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 caractéristique. Le nom du vocabulaire au mappage de chemin est fourni à StatsOptions (et donc à TFDV) pour chaque vocabulaire généré par TFT. De plus, des mappages pour des vocabulaires créés en externe peuvent être ajoutés en (i) modifiant directement le dictionnaire vocab_paths dans StatsOptions ou en (ii) en utilisant tft.annotate_asset .