Junte-se à comunidade SIG TFX-Addons e ajude a tornar o TFX ainda melhor!

O Transform TFX Pipeline Component

O componente de pipeline Transform TFX executa a engenharia de recursos em tf.Examples emitidos de um componente ExampleGen , usando um esquema de dados criado por um componente SchemaGen , e emite tanto um SavedModel quanto estatísticas em dados 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 com base em TFDV . Para fazer isso, defina um stats_options_updater_fn dentro do mesmo módulo.

Transform e TensorFlow Transform

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

  • Embedding : converter recursos esparsos (como os IDs inteiros produzidos por um vocabulário) em recursos densos, encontrando um mapeamento significativo de espaço de alta dimensão para 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 único para um número de ID.
  • Normalizando valores : transformando recursos numéricos para que todos caiam em uma faixa semelhante.
  • Bucketization : conversão de recursos de valor contínuo em recursos categóricos, atribuindo valores a intervalos discretos.
  • Recursos de texto enriquecidos: 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 fornece 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, de modo que as mesmas transformações sejam realizadas no treinamento e no momento 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 dentro do TensorFlow Transform, as transformações se tornarão parte do gráfico do TensorFlow. Essa abordagem ajuda a evitar distorção 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 podem ser definidas sem olhar para os dados.

Por outro lado, o TensorFlow Transform foi projetado para transformações que exigem uma passagem completa sobre os dados para calcular valores que não são conhecidos com antecedência. Por exemplo, a geração de vocabulário requer uma passagem completa sobre os 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 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 o 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. Esse 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 normal do TensorFlow aceita um único lote como entrada, realiza alguns cálculos apenas nesse lote e emite um lote, um analyzer realiza uma redução global (implementada no Apache Beam) em todos os lotes e retorna 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 de mean e var sob o capô, o que efetivamente gerará 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.

TensorFlow Transform preprocessing_fn

O componente TFX Transform simplifica o uso de Transform, manipulando as chamadas API relacionadas à leitura e gravação de dados e gravando a saída SavedModel no disco. Como um usuário 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 de tensores para produzir o dict de saída de tensores. 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:
    # 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

Compreender 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, portanto, para escrever o preprocessing_fn corretamente, é necessário entender como seus dados são representados como tensores. A entrada para preprocessing_fn é determinada pelo esquema. Um Schema contém uma lista de Feature e Transform os converte em uma "especificação de recurso" (às vezes chamada de "especificação de análise"), que é um dicionário cujas chaves são nomes de recursos e cujos valores são FixedLenFeature ou VarLenFeature (ou outro 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 definida resultará em um tf.FixedLenFeature com forma e default_value=None . presence.min_fraction deve ser 1 caso contrário, ocorrerá um 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 uma VarLenFeature .
  • Cada sparse_feature irá resultar em uma tf.SparseFeature cujo size e is_sorted são determinados pelo fixed_shape e is_sorted campos do SparseFeature mensagem.
  • 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 de type do feature (ou os valores do recurso de um proto sparse_feature ) e o dtype da especificação do recurso é dada pela seguinte tabela:
type dtype
schema_pb2.INT tf.int64
schema_pb2.FLOAT tf.float32
schema_pb2.BYTES tf.string

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 inteiros. Ao seguir este fluxo de trabalho, o input_fn construído no modelo produzirá a string integerizada. No entanto, 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 do input_fn para input_fn 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 de input_fn deve ser essas strings brutas, e as chaves ["cat", "dog"] precisam ser passadas para o estimador como um parâmetro (veja os 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 snippet 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 na geração de um vocabulário para a education que pode ser acessado no modelo.

O exemplo também mostra como transformar um rótulo e, em seguida, gerar um vocabulário para o rótulo transformado. Em particular, leva a education 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 por tft.vocabulary como o argumento label_vocabulary . Isso é feito lendo primeiro este vocabulário como uma lista com uma função auxiliar. Isso é mostrado no snippet 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 as estatísticas pré-transformação e pós-transformação. TFDV recebe como entrada um objeto StatsOptions opcional. Os usuários podem desejar configurar este objeto para habilitar certas estatísticas adicionais (por exemplo, estatísticas de PNL) ou para definir limites que são validados (por exemplo, frequência de token mínimo / máximo). 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 de pós-transformação geralmente se beneficiam do conhecimento do vocabulário usado para o pré-processamento de 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, para mapeamentos vocabulários criados externamente podem ser adicionados por qualquer (i) modificar directamente os vocab_paths dicionário dentro StatsOptions ou por (ii) usando tft.annotate_asset .