O componente Transform TFX Pipeline

Mantenha tudo organizado com as coleções Salve e categorize o conteúdo com base nas suas preferências.

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 e estatísticas em dados de pré-transformação e pós-transformação. Quando executado, o SavedModel aceitará tf.Examples emitidos 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

Uma vez que seu preprocessing_fn é escrito, ele precisa ser definido em um módulo python que é então 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 desejar fornecer opções para o cálculo de estatísticas de pré-transformação ou pós-transformação baseado em TFDV . Para fazer isso, defina um stats_options_updater_fn dentro do mesmo módulo.

Transformar e Transformar TensorFlow

O 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 : convertendo características esparsas (como os IDs inteiros produzidos por um vocabulário) em características densas, 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 dentro de um intervalo semelhante.
  • Bucketization : convertendo recursos de valor contínuo em recursos categóricos, atribuindo valores a buckets discretos.
  • Enriquecendo recursos de texto : produzindo recursos a partir de dados brutos, como tokens, n-grams, entidades, sentimento, etc., para enriquecer o conjunto de recursos.

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

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

  • Execute transformações arbitrárias em 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 bucketizations, integerizations 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 pelos 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 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, não apenas estatísticas, o TensorFlow Transform simplifica o processo de criação do pipeline de pré-processamento.

Como o pré-processamento é expresso como um gráfico, ele pode acontecer no servidor e é garantido que seja consistente entre o treinamento e a veiculaçã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 do 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 adiado que será feito pelo Apache Beam e a saída inserida no gráfico como um constante. Enquanto uma operação comum do TensorFlow pega um único lote como entrada, executa algum cálculo apenas nesse lote e emite 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 do TensorFlow Transform, os usuários podem criar pipelines complexos para pré-processar seus dados. Por exemplo, a função tft.scale_to_z_score recebe um tensor de entrada e retorna esse tensor normalizado para ter média 0 e variância 1 . Ele faz isso chamando os analisadores de mean e var sob o capô, que efetivamente gerarão constantes no gráfico iguais à média e variância do tensor de entrada. Em seguida, ele usará as operações do TensorFlow para subtrair a média e dividir pelo desvio padrão.

A transformação do TensorFlow preprocessing_fn

O componente TFX Transform simplifica o uso de Transform manipulando as chamadas de API relacionadas à leitura e gravação de dados e gravando o SavedModel de saída 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 dict de entrada dos tensores para produzir o dict de saída dos tensores. Você pode encontrar funções auxiliares como scale_to_0_1 e compute_and_apply_vocabulary na API de transformação do TensorFlow 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

Entendendo as entradas para o preprocessing_fn

O preprocessing_fn descreve uma série de operações em tensores (ou seja, Tensor s ou SparseTensor s) e para escrever o preprocessing_fn corretamente é necessário entender como seus dados são representados como tensores. A entrada para o preprocessing_fn é determinada pelo esquema. Um proto Schema contém uma lista de Feature s, e Transform converte-os em uma "especificação de recurso" (às vezes chamada de "especificação de análise") que é um dict cujas chaves são nomes de recursos e cujos valores são FixedLenFeature ou VarLenFeature (ou outros opções não usadas pelo TensorFlow Transform).

As regras para inferir uma especificação de recurso do Schema são

  • Cada feature com shape definido resultará em um tf.FixedLenFeature com shape e default_value=None . presence.min_fraction deve ser 1 caso contrário e resultará em erro, pois quando não há valor padrão, um tf.FixedLenFeature requer que o recurso esteja sempre presente.
  • Cada feature com shape não definida resultará em um VarLenFeature .
  • Cada sparse_feature resultará em um tf.SparseFeature cujo size e is_sorted são determinados pelos campos fixed_shape e is_sorted da mensagem SparseFeature .
  • Os recursos usados ​​como index_feature ou value_feature de um sparse_feature não terão sua própria entrada gerada na especificação do recurso.
  • A correspondência entre o campo type do feature (ou o recurso de valores de um proto sparse_feature ) e o dtype da especificação do recurso é fornecida pela tabela a seguir:
type dtype
schema_pb2.INT tf.int64
schema_pb2.FLOAT tf.float32
schema_pb2.BYTES tf.string

Como usar 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 possa mapear os rótulos de saída (inteiros) de volta para as strings, o modelo precisa do input_fn para gerar um rótulo de string, junto com uma lista de valores possíveis do rótulo. Por exemplo, se os rótulos são cat e dog , a saída do 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 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 bruta (que também será retornado como parte da saída da função de pré-processamento) e chama tft.vocabulary nele. Isso resulta em um vocabulário sendo gerado para education que pode ser acessado no modelo.

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

No código do modelo, o classificador deve receber o vocabulário gerado pelo 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

Como mencionado acima, o componente Transform invoca o TFDV para calcular estatísticas de pré-transformação e pós-transformação. O TFDV recebe como entrada um objeto StatsOptions opcional. Os usuários podem querer configurar este objeto para habilitar certas estatísticas adicionais (por exemplo, estatísticas de NLP) ou para definir limites que são validados (por exemplo, frequência mínima / máxima do token). Para fazer isso, defina um 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 para StatsOptions (e, portanto, TFDV) para cada vocabulário gerado por 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 .