O componente Transform TFX Pipeline

O componente de pipeline Transform TFX executa engenharia de recursos em tf.Examples emitidos de um componente ExampleGen , usando um esquema de dados criado por um componente SchemaGen e emite um SavedModel, bem como estatísticas sobre dados pré-transformação e pós-transformação. Quando executado, o SavedModel aceitará tf.Examples emitido de um componente ExampleGen e emitirá os dados do recurso transformado.

  • Consome: tf.Examples de um componente ExampleGen e um esquema de dados de um componente SchemaGen.
  • Emite: Um SavedModel para um componente Trainer, estatísticas de pré-transformação e pós-transformação.

Configurando um componente de transformação

Depois que seu preprocessing_fn for escrito, ele precisará ser definido em um módulo python que será fornecido ao componente Transform como uma entrada. Este módulo será carregado por transform e a função chamada preprocessing_fn será encontrada e usada por Transform para construir o pipeline de pré-processamento.

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

Além disso, você pode fornecer opções para o cálculo de estatísticas pré-transformadas ou pós-transformadas baseadas em TFDV . Para fazer isso, defina stats_options_updater_fn dentro do mesmo módulo.

Transformação e transformação do TensorFlow

Transform faz uso extensivo do TensorFlow Transform para realizar engenharia de recursos em seu conjunto de dados. O TensorFlow Transform é uma ótima ferramenta para transformar dados de recursos antes de irem para o seu modelo e como parte do processo de treinamento. As transformações de recursos comuns incluem:

  • Incorporação : conversão de recursos esparsos (como os IDs inteiros produzidos por um vocabulário) em recursos densos, encontrando um mapeamento significativo do espaço de alta dimensão para o espaço de baixa dimensão. Consulte a unidade Embeddings no Curso intensivo de aprendizado de máquina para obter uma introdução aos embeddings.
  • Geração de vocabulário : conversão de strings ou outros recursos não numéricos em inteiros, criando um vocabulário que mapeia cada valor exclusivo para um número de identificação.
  • Normalizando valores : transformando recursos numéricos para que todos caiam em um intervalo semelhante.
  • Bucketização : conversão de recursos de valor contínuo em recursos categóricos, atribuindo valores a intervalos discretos.
  • Enriquecimento de recursos de texto : produção de recursos a partir de dados brutos, como tokens, n-gramas, entidades, sentimento, etc., para enriquecer o conjunto de recursos.

O TensorFlow Transform oferece suporte para estes e muitos outros tipos de transformações:

  • Gere automaticamente um vocabulário a partir de seus dados mais recentes.

  • Execute transformações arbitrárias nos seus dados antes de enviá-los ao seu modelo. O TensorFlow Transform cria transformações no gráfico do TensorFlow para seu modelo para que as mesmas transformações sejam realizadas no momento do treinamento e da inferência. Você pode definir transformações que se referem a propriedades globais dos dados, como o valor máximo de um recurso em todas as instâncias de treinamento.

Você pode transformar seus dados como quiser antes de executar o TFX. Mas se você fizer isso no TensorFlow Transform, as transformações se tornarão parte do gráfico do TensorFlow. Essa abordagem ajuda a evitar distorções de treinamento/serviço.

As transformações dentro do seu código de modelagem usam FeatureColumns. Usando FeatureColumns, você pode definir bucketizações, inteirações que usam vocabulários predefinidos ou quaisquer outras transformações que possam ser definidas sem examinar os dados.

Por outro lado, o TensorFlow Transform foi projetado para transformações que exigem uma passagem completa dos dados para calcular valores que não são conhecidos antecipadamente. Por exemplo, a geração de vocabulário requer uma passagem completa dos dados.

Além de calcular valores usando o Apache Beam, o TensorFlow Transform permite que os usuários incorporem esses valores em um gráfico do TensorFlow, que pode então ser carregado no gráfico de treinamento. Por exemplo, ao normalizar recursos, a função tft.scale_to_z_score calculará a média e o desvio padrão de um recurso e também uma representação, em um gráfico do TensorFlow, da função que subtrai a média e divide pelo desvio padrão. Ao emitir um gráfico do TensorFlow, e não apenas estatísticas, o TensorFlow Transform simplifica o processo de criação do pipeline de pré-processamento.

Como o pré-processamento é expresso em um gráfico, ele pode acontecer no servidor e tem garantia de consistência entre o treinamento e o serviço. Essa consistência elimina uma fonte de distorção de treinamento/serviço.

O TensorFlow Transform permite que os usuários especifiquem seu pipeline de pré-processamento usando o código TensorFlow. Isso significa que um pipeline é construído da mesma maneira que um gráfico do TensorFlow. Se apenas as operações do TensorFlow fossem usadas neste gráfico, o pipeline seria um mapa puro que aceita lotes de entrada e retorna lotes de saída. Tal pipeline seria equivalente a colocar este gráfico dentro de seu input_fn ao usar a API tf.Estimator . Para especificar operações de passagem completa, como quantis de computação, o TensorFlow Transform fornece funções especiais chamadas analyzers que aparecem como operações do TensorFlow, mas na verdade especificam um cálculo diferido que será feito pelo Apache Beam e a saída inserida no gráfico como um constante. Embora uma operação comum do TensorFlow receba um único lote como entrada, execute alguns cálculos apenas nesse lote e emita um lote, um analyzer realizará uma redução global (implementada no Apache Beam) em todos os lotes e retornará o resultado.

Ao combinar operações comuns do TensorFlow e analisadores TensorFlow Transform, os usuários podem criar pipelines complexos para pré-processar seus dados. Por exemplo, a função tft.scale_to_z_score pega um tensor de entrada e retorna esse tensor normalizado para ter média 0 e variância 1 . Ele faz isso chamando os analisadores mean e var nos bastidores, o que irá efetivamente gerar constantes no gráfico iguais à média e à variância do tensor de entrada. Em seguida, ele usará operações do TensorFlow para subtrair a média e dividir pelo desvio padrão.

O preprocessing_fn da transformação do TensorFlow

O componente TFX Transform simplifica o uso do Transform manipulando as chamadas de API relacionadas à leitura e gravação de dados e gravando a saída SavedModel no disco. Como usuário do TFX, você só precisa definir uma única função chamada preprocessing_fn . Em preprocessing_fn você define uma série de funções que manipulam o ditado de tensores de entrada para produzir o ditado de tensores de saída. Você pode encontrar funções auxiliares como scale_to_0_1 e compute_and_apply_vocabulary na API TensorFlow Transform ou usar funções regulares do TensorFlow conforme mostrado abaixo.

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

Compreendendo as entradas para preprocessing_fn

O preprocessing_fn descreve uma série de operações em tensores (ou seja, Tensor s, SparseTensor s ou RaggedTensor s). Para definir corretamente o preprocessing_fn é necessário entender como os dados são representados como tensores. A entrada para preprocessing_fn é determinada pelo esquema. Um proto Schema é eventualmente convertido em uma "especificação de recurso" (às vezes chamada de "especificação de análise") que é usada para análise de dados. Veja mais detalhes sobre a lógica de conversão aqui .

Usando o TensorFlow Transform para lidar com rótulos de string

Normalmente, deseja-se usar o TensorFlow Transform para gerar um vocabulário e aplicar esse vocabulário para converter strings em números inteiros. Ao seguir este fluxo de trabalho, o input_fn construído no modelo produzirá a string inteirada. No entanto, os rótulos são uma exceção, porque para que o modelo seja capaz de mapear os rótulos de saída (inteiros) de volta para strings, o modelo precisa que input_fn produza um rótulo de string, junto com uma lista de valores possíveis do rótulo. Por exemplo, se os rótulos forem cat e dog , então a saída de input_fn deve ser essas strings brutas, e as chaves ["cat", "dog"] precisam ser passadas para o estimador como um parâmetro (veja detalhes abaixo).

Para lidar com o mapeamento de rótulos de string para números inteiros, você deve usar o TensorFlow Transform para gerar um vocabulário. Demonstramos isso no trecho de código abaixo:

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)

  ...

A função de pré-processamento acima pega o recurso de entrada bruto (que também será retornado como parte da saída da função de pré-processamento) e chama tft.vocabulary nele. Isso resulta na geração de um vocabulário para education que pode ser acessado no modelo.

O exemplo também mostra como transformar um rótulo e depois gerar um vocabulário para o rótulo transformado. Em particular, ele pega o education bruto e converte todos os 5 rótulos principais (por frequência) em UNKNOWN , sem converter o rótulo em um número inteiro.

No código do modelo, o classificador deve receber o vocabulário gerado por tft.vocabulary como argumento label_vocabulary . Isso é feito primeiro lendo este vocabulário como uma lista com uma função auxiliar. Isso é mostrado no trecho abaixo. Observe que o código de exemplo usa o rótulo transformado discutido acima, mas aqui mostramos o código para usar o rótulo bruto.

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,
      ...)

Configurando estatísticas de pré-transformação e pós-transformação

Conforme mencionado acima, o componente Transform invoca TFDV para calcular estatísticas pré-transformadas e pós-transformadas. TFDV recebe como entrada um objeto StatsOptions opcional. Os usuários podem desejar configurar este objeto para permitir certas estatísticas adicionais (por exemplo, estatísticas de PNL) ou para definir limites que sejam validados (por exemplo, frequência mínima/máxima do token). Para fazer isso, defina stats_options_updater_fn no arquivo do módulo.

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

As estatísticas pós-transformação geralmente se beneficiam do conhecimento do vocabulário usado para pré-processar um recurso. O nome do vocabulário para mapeamento de caminho é fornecido ao StatsOptions (e, portanto, ao TFDV) para cada vocabulário gerado pelo TFT. Além disso, mapeamentos para vocabulários criados externamente podem ser adicionados (i) modificando diretamente o dicionário vocab_paths em StatsOptions ou (ii) usando tft.annotate_asset .