Le composant du pipeline Transform TFX

Le composant de pipeline Transform TFX effectue l'ingénierie des fonctionnalités sur tf.Examples émis à partir d'un composant SampleGen , à 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. Une fois exécuté, le SavedModel acceptera les tf.Examples émis à partir d'un composant SampleGen et émettra les données de fonctionnalité transformées.

  • Consomme : tf.Examples d'un composant SampleGen 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 votre preprocessing_fn écrit, il doit être défini dans un module python qui est ensuite fourni au composant Transform en 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 au sein du même module.

Transformation et transformation 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 de caractéristiques 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 clairsemées (comme les identifiants entiers produits par un vocabulaire) en caractéristiques denses en trouvant une cartographie significative d'un espace de haute dimension à un espace de basse dimension. Consultez l’ unité Embeddings dans le cours intensif d’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 entiers en créant un vocabulaire qui mappe chaque valeur unique à un numéro d'identification.
  • Normaliser les valeurs : transformer les caractéristiques numériques afin qu'elles se situent toutes dans une plage similaire.
  • Bucketisation : conversion de fonctionnalités à valeur continue en fonctionnalités catégorielles en attribuant des valeurs à des compartiments discrets.
  • Enrichissement des fonctionnalités de texte : production de fonctionnalités à partir de données brutes telles que des jetons, des n-grammes, des entités, des sentiments, etc., pour enrichir l'ensemble des fonctionnalités.

TensorFlow Transform prend en charge ces types de transformations, ainsi que 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 examiner les données.

En revanche, TensorFlow Transform est conçu pour les transformations qui nécessitent une transmission complète des données pour calculer des valeurs qui ne sont pas connues à l'avance. Par exemple, la génération de vocabulaire nécessite une transmission complète des 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 d'entraînement. Par exemple, lors de la normalisation de caractéristiques, la fonction tft.scale_to_z_score calculera la moyenne et l'écart type d'une caractéristique, 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 de biais 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 pure carte qui accepte des lots d'entrées et renvoie des lots de sorties. 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 complètes telles que le calcul des quantiles, TensorFlow Transform fournit des fonctions spéciales appelées analyzers qui ressemblent aux 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 sous forme de calcul. constante. Alors qu'une opération TensorFlow ordinaire prendra un seul lot en entrée, effectuera des calculs uniquement 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 1 . Pour ce faire, il appelle les analyseurs mean et var sous le capot, qui généreront 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 calculate_and_apply_vocabulary l' API TensorFlow Transform ou utiliser les fonctions TensorFlow standard 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, SparseTensor s ou RaggedTensor s). Afin de définir correctement le preprocessing_fn il est nécessaire de comprendre comment les données sont représentées sous forme de tenseurs. L'entrée dans preprocessing_fn est déterminée par le schéma. Un prototype Schema est finalement converti en une « spécification de fonctionnalité » (parfois appelée « spécification d'analyse ») qui est utilisée pour l'analyse des données. Pour plus de détails sur la logique de conversion, cliquez ici .

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

Habituellement, on souhaite utiliser TensorFlow Transform à la fois pour générer un vocabulaire et appliquer ce vocabulaire pour convertir des chaînes en entiers. En suivant ce workflow, 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 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 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 en 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 fonctionnalité 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. Cela aboutit à la génération d’un vocabulaire pour education accessible 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 brute des étiquettes et convertit toutes les étiquettes sauf les 5 premières (par fréquence) en UNKNOWN , sans convertir l'étiquette en nombre 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 discutée ci-dessus, mais nous montrons ici 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 avant et après la 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 validés (par exemple la 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 de post-transformation bénéficient souvent de la connaissance du vocabulaire utilisé pour le prétraitement d'une fonctionnalité. Le nom du vocabulaire et le mappage du chemin sont fournis à StatsOptions (et donc TFDV) pour chaque vocabulaire généré par TFT. De plus, des mappages pour les vocabulaires créés en externe peuvent être ajoutés soit en (i) modifiant directement le dictionnaire vocab_paths dans StatsOptions, soit en (ii) utilisant tft.annotate_asset .