Ayuda a proteger la Gran Barrera de Coral con TensorFlow en Kaggle Únete Challenge

Empiece a utilizar TensorFlow Transform

Esta guía presenta los conceptos básicos de tf.Transform y cómo utilizarlos. Va a:

  • Definir una función de pre-procesamiento, una descripción lógica de la tubería que transforma los datos en bruto en los datos utilizados para entrenar un modelo de aprendizaje automático.
  • Mostrar el Apache Beam aplicación utilizada para transformar los datos mediante la conversión de la función de procesamiento previo en una tubería de haz.
  • Muestre ejemplos de uso adicionales.

Definir una función de preprocesamiento

La función de pre-procesamiento es el concepto más importante de tf.Transform . La función de preprocesamiento es una descripción lógica de una transformación del conjunto de datos. La función de pre-procesamiento acepta y devuelve un diccionario de los tensores, donde significa un tensor Tensor o SparseTensor . Hay dos tipos de funciones que se utilizan para definir la función de preprocesamiento:

  1. Cualquier función que acepte y devuelva tensores. Estos agregan operaciones de TensorFlow al gráfico que transforman los datos sin procesar en datos transformados.
  2. Cualquiera de los analizadores proporcionadas por tf.Transform . Analizadores también aceptan y tensores regresan, pero a diferencia de las funciones TensorFlow, que no añaden operaciones a la gráfica. En su lugar, analizadores causa tf.Transform para calcular una operación fuera de paso llena de TensorFlow. Usan los valores del tensor de entrada en todo el conjunto de datos para generar un tensor constante que se devuelve como salida. Por ejemplo, tft.min calcula el mínimo de un tensor sobre el conjunto de datos. tf.Transform proporciona un conjunto fijo de analizadores, pero esto se extenderá en futuras versiones.

Ejemplo de función de preprocesamiento

Al combinar analizadores y funciones regulares de TensorFlow, los usuarios pueden crear canalizaciones flexibles para transformar datos. La siguiente función de preprocesamiento transforma cada una de las tres características de diferentes formas y combina dos de las características:

import tensorflow as tf
import tensorflow_transform as tft
import tensorflow_transform.beam as tft_beam

def preprocessing_fn(inputs):
  x = inputs['x']
  y = inputs['y']
  s = inputs['s']
  x_centered = x - tft.mean(x)
  y_normalized = tft.scale_to_0_1(y)
  s_integerized = tft.compute_and_apply_vocabulary(s)
  x_centered_times_y_normalized = x_centered * y_normalized
  return {
      'x_centered': x_centered,
      'y_normalized': y_normalized,
      'x_centered_times_y_normalized': x_centered_times_y_normalized,
      's_integerized': s_integerized
  }

Aquí, x , y y s son Tensor s que representan las características de entrada. El primer tensor de nuevo que se crea, x_centered , se construye mediante la aplicación de tft.mean a x y restando esto desde x . tft.mean(x) devuelve un tensor que representa la media de la tensor x . x_centered es el tensor x con la media resta.

El segundo nuevo tensor, y_normalized , se crea de una manera similar pero usando el método de conveniencia tft.scale_to_0_1 . Este método hace algo similar a la informática x_centered , es decir, el cálculo de un máximo y mínimo y el uso de éstas a escala y .

El tensor s_integerized muestra un ejemplo de manipulación de cadenas. En este caso, tomamos una cadena y la asignamos a un número entero. Este sistema utiliza la función de conveniencia tft.compute_and_apply_vocabulary . Esta función usa un analizador para calcular los valores únicos tomados por las cadenas de entrada y luego usa las operaciones de TensorFlow para convertir las cadenas de entrada en índices en la tabla de valores únicos.

La columna final muestra que es posible usar operaciones de TensorFlow para crear nuevas funciones combinando tensores.

La función de preprocesamiento define una canalización de operaciones en un conjunto de datos. Con el fin de aplicar la tubería, nos basamos en una aplicación concreta de la tf.Transform API. La implementación Apache Beam proporciona PTransform que se aplica la función de procesamiento previo de un usuario a los datos. El flujo de trabajo típico de un tf.Transform usuario construir una función de procesamiento previo, a continuación, incorporar esta en una tubería más grande de la viga, la creación de los datos de entrenamiento.

Por lotes

El procesamiento por lotes es una parte importante de TensorFlow. Dado que uno de los objetivos de tf.Transform es proporcionar un gráfico TensorFlow para el preprocesamiento que se pueden incorporar en la gráfica de la porción (y, opcionalmente, el gráfico de la formación), preparación de lotes es también un concepto importante en tf.Transform .

Aunque no es evidente en el ejemplo anterior, el usuario define preprocesamiento función se pasa tensores que representan lotes de y no casos individuales, como ocurre durante el entrenamiento y la porción con TensorFlow. Por otro lado, los analizadores realizan un cálculo sobre todo el conjunto de datos que devuelve un valor único y no un lote de valores. x es un Tensor con una forma de (batch_size,) , mientras que tft.mean(x) es un Tensor con una forma de () . La resta x - tft.mean(x) transmisiones donde el valor de tft.mean(x) se resta de cada elemento del lote representado por x .

Implementación de Apache Beam

Mientras que la función de procesamiento previo pretende ser una descripción lógica de una tubería de procesamiento previo aplicado sobre los marcos de procesamiento de datos múltiples, tf.Transform proporciona una implementación canónica utilizado en Apache Beam. Esta implementación demuestra la funcionalidad requerida de una implementación. No existe una API formal para esta funcionalidad, por lo que cada implementación puede utilizar una API que sea idiomática para su marco de procesamiento de datos en particular.

La implementación Apache Beam proporciona dos PTransform s utilizados para tratar datos para una función de pre-procesamiento. Los siguientes muestra el uso para el material compuesto PTransform AnalyzeAndTransformDataset :

raw_data = [
    {'x': 1, 'y': 1, 's': 'hello'},
    {'x': 2, 'y': 2, 's': 'world'},
    {'x': 3, 'y': 3, 's': 'hello'}
]

raw_data_metadata = ...
transformed_dataset, transform_fn = (
    (raw_data, raw_data_metadata) | tft_beam.AnalyzeAndTransformDataset(
        preprocessing_fn))
transformed_data, transformed_metadata = transformed_dataset

El transformed_data contenido se muestra a continuación y contiene las columnas transformadas en el mismo formato que los datos en bruto. En particular, los valores de s_integerized son [0, 1, 0] valores -estos dependen de cómo las palabras hello y world fueron asignadas a números enteros, que es determinista. Para la columna x_centered , restamos la media por lo que los valores de la columna x , que eran [1.0, 2.0, 3.0] , se convirtió [-1.0, 0.0, 1.0] . Del mismo modo, el resto de las columnas coinciden con sus valores esperados.

[{u's_integerized': 0,
  u'x_centered': -1.0,
  u'x_centered_times_y_normalized': -0.0,
  u'y_normalized': 0.0},
 {u's_integerized': 1,
  u'x_centered': 0.0,
  u'x_centered_times_y_normalized': 0.0,
  u'y_normalized': 0.5},
 {u's_integerized': 0,
  u'x_centered': 1.0,
  u'x_centered_times_y_normalized': 1.0,
  u'y_normalized': 1.0}]

Ambos raw_data y transformed_data son conjuntos de datos. Las siguientes dos secciones muestran cómo la implementación de Beam representa conjuntos de datos y cómo leer y escribir datos en el disco. El otro valor de retorno, transform_fn , representa la transformación aplicada a los datos, cubiertos en detalle a continuación.

El AnalyzeAndTransformDataset es la composición de las dos transformadas fundamentales previstas por la aplicación AnalyzeDataset y TransformDataset . Entonces, los siguientes dos fragmentos de código son equivalentes:

transformed_data, transform_fn = (
    my_data | tft_beam.AnalyzeAndTransformDataset(preprocessing_fn))
transform_fn = my_data | tft_beam.AnalyzeDataset(preprocessing_fn)
transformed_data = (my_data, transform_fn) | tft_beam.TransformDataset()

transform_fn es una pura función que representa una operación que se aplica a cada fila del conjunto de datos. En particular, los valores del analizador ya están calculados y tratados como constantes. En el ejemplo, la transform_fn contiene como constantes de la media de la columna x , el mínimo y máximo de la columna y , y el vocabulario utilizado para mapear las cadenas a números enteros.

Una característica importante de tf.Transform es que transform_fn representa un mapa sobre las filas -es una función pura aplicada a cada fila por separado. Todo el cálculo para la agregación de filas se hace en AnalyzeDataset . Además, la transform_fn se representa como un TensorFlow Graph que puede ser embebido en la gráfica de servir.

AnalyzeAndTransformDataset se proporciona optimizaciones en este caso especial. Este es el mismo patrón utilizado en scikit-learn , proporcionando el fit , transform , y fit_transform métodos.

Formatos de datos y esquema

La implementación de TFT Beam acepta dos formatos de datos de entrada diferentes. El formato "instancia dict" (como se ve en el ejemplo anterior y en simple_example.py ) es un formato intuitivo y es adecuado para pequeños conjuntos de datos mientras que el TFXIO ( Apache Flecha formato) proporciona un rendimiento mejorado y es suitble para grandes conjuntos de datos.

La implementación de Beam indica en qué formato estaría la PCollection de entrada mediante los "metadatos" que acompañan a la PCollection:

(raw_data, raw_data_metadata) | tft.AnalyzeDataset(...)
  • Si raw_data_metadata es un dataset_metadata.DatasetMetadata (véase más adelante, "La 'ejemplo dict' formato" sección), a continuación, raw_data se espera que sea en el formato de "instancia dict".
  • Si raw_data_metadata es un tfxio.TensorAdapterConfig (véase más adelante, "El formato TFXIO" sección), a continuación, raw_data se espera que sea en el formato TFXIO.

El formato "dictado de instancia"

En los ejemplos de código anteriores, el código que define raw_data_metadata se omite. Los metadatos contienen el esquema que define el diseño de los datos para que se lean y escriban en varios formatos. Incluso el formato en memoria que se muestra en la última sección no es autodescriptivo y requiere el esquema para ser interpretado como tensores.

Aquí está la definición del esquema para los datos de ejemplo:

from tensorflow_transform.tf_metadata import dataset_metadata
from tensorflow_transform.tf_metadata import schema_utils

raw_data_metadata = dataset_metadata.DatasetMetadata(
      schema_utils.schema_from_feature_spec({
        's': tf.io.FixedLenFeature([], tf.string),
        'y': tf.io.FixedLenFeature([], tf.float32),
        'x': tf.io.FixedLenFeature([], tf.float32),
    }))

El Schema proto contiene la información necesaria para analizar los datos de su formato en disco o en memoria, en los tensores. Por lo general se construye llamando schema_utils.schema_from_feature_spec con una asignación de teclas de función dict a tf.io.FixedLenFeature , tf.io.VarLenFeature y tf.io.SparseFeature valores. Consulte la documentación de tf.parse_example para más detalles.

Por encima usamos tf.io.FixedLenFeature para indicar que cada función contiene un número fijo de valores, en este caso un solo valor escalar. Debido tf.Transform casos lotes, el actual Tensor que representa la característica tendrá forma (None,) donde la dimensión desconocida es la dimensión del lote.

El formato TFXIO

Con este formato, se espera que los datos que deben figurar en un pyarrow.RecordBatch . Para los datos tabulares, nuestra implementación Apache haz acepta Flecha RecordBatch es que consisten en columnas de los siguientes tipos:

  • pa.list_(<primitive>) , donde <primitive> es pa.int64() , pa.float32() pa.binary() o pa.large_binary() .

  • pa.large_list(<primitive>)

El conjunto de datos de entrada de juguete que se ha usado anteriormente, cuando se representa como un RecordBatch , tiene el siguiente aspecto:

raw_data = [
    pa.record_batch([
        pa.array([[1], [2], [3]], pa.list_(pa.float32())),
        pa.array([[1], [2], [3]], pa.list_(pa.float32())),
        pa.array([['hello'], ['world'], ['hello']], pa.list_(pa.binary())),
    ], ['x', 'y', 's'])
]

Al igual que en DatasetMetadata siendo necesarias para acompañar el formato "instancia dict", un tfxio.TensorAdapterConfig es necesaria para acompañar a los RecordBatch es. Consiste en el esquema Flecha de los RecordBatch ES, y TensorRepresentations a únicamente determinar cómo columnas en RecordBatch ES pueden ser interpretados como TensorFlow Tensores (incluyendo pero no limitados a tf.Tensor, tf.SparseTensor).

TensorRepresentations es un Dict[Text, TensorRepresentation] que establece la relación entre un tensor que preprocessing_fn acepta y columnas en las RecordBatch es. Por ejemplo:

tensor_representation = {
    'x': text_format.Parse(
        """dense_tensor { column_name: "col1" shape { dim { size: 2 } } }"""
        schema_pb2.TensorRepresentation())
}

Medios que inputs['x'] en preprocessing_fn debe ser una densa tf.Tensor, cuyos valores provienen de una columna de nombre 'col1' en la entrada RecordBatch es, y su forma (por lotes) debe ser [batch_size, 2] .

TensorRepresentation es un Protobuf definido en TensorFlow metadatos .

Compatibilidad con TensorFlow

tf.Transform proporciona soporte para la exportación de la transform_fn anteriormente, ya sea como un 1.x TF o un SavedModel TF 2.x. El comportamiento predeterminado antes de la 0.30 de liberación exportó un SavedModel TF 1.x. A partir de la 0.30 de liberación, el comportamiento predeterminado es exportar un SavedModel TF 2.x menos comportamientos TF 2.x son desactivado explícitamente (llamando tf.compat.v1.disable_v2_behavior() por ejemplo).

Si el uso de conceptos tales como TF 1.x Estimators y Sessions , puede conservar el comportamiento anterior al pasar force_tf_compat_v1=True a tft_beam.Context si se utiliza tf.Transform como una biblioteca independiente o al transformar los componentes de TFX.

Al exportar la transform_fn como SavedModel TF 2.x, el preprocessing_fn se espera que sea trazable usando tf.function . Además, si se ejecuta la canalización de forma remota (por ejemplo, con la DataflowRunner ), asegurarse de que el preprocessing_fn y las dependencias están empaquetados adecuadamente como se describe aquí .

Problemas conocidos con el uso de tf.Transform para exportar un 2.x TF SavedModel están documentados aquí .

Entrada y salida con Apache Beam

Hasta ahora, hemos visto los datos de entrada y salida en las listas de Python (de RecordBatch es o diccionarios de instancia). Esta es una simplificación que se basa en la capacidad del Apache Beam para el trabajo con las listas, así como su principal representación de datos, el PCollection .

Un PCollection es una representación de datos que forma parte de una tubería de haz. Una tubería de haz se forma mediante la aplicación de diversos PTransform s, incluyendo AnalyzeDataset y TransformDataset , y en funcionamiento la tubería. Un PCollection no se crea en la memoria del binario principal, sino que se distribuye entre los trabajadores (aunque esta sección se utiliza el modo de ejecución en memoria).

Pre enlatados- PCollection fuentes ( TFXIO )

El RecordBatch formato que acepta nuestra aplicación es un formato común que otras bibliotecas TFX aceptan. Por lo tanto TFX ofertas convenientes "fuentes" (también conocido como TFXIO ) que leen los archivos de varios formatos de disco y productos RecordBatch ES y también pueden dar TensorAdapterConfig , incluyendo inferidos TensorRepresentations .

Aquellos TFXIO s se puede encontrar en el paquete tfx_bsl ( tfx_bsl.public.tfxio ).

Ejemplo: conjunto de datos "Ingresos del censo"

En el siguiente ejemplo se requiere tanto la lectura como la escritura de datos en el disco y representar datos como PCollection (no una lista), véase: census_example.py . A continuación, mostramos cómo descargar los datos y ejecutar este ejemplo. El conjunto de datos "Ingresos de censo" se proporciona por la UCI Machine Learning Repositorio . Este conjunto de datos contiene datos tanto categóricos como numéricos.

Los datos están en formato CSV, aquí están las dos primeras líneas:

39, State-gov, 77516, Bachelors, 13, Never-married, Adm-clerical, Not-in-family, White, Male, 2174, 0, 40, United-States, <=50K
50, Self-emp-not-inc, 83311, Bachelors, 13, Married-civ-spouse, Exec-managerial, Husband, White, Male, 0, 0, 13, United-States, <=50K

Las columnas del conjunto de datos son categóricas o numéricas. Este conjunto de datos describe un problema de clasificación: predecir la última columna donde el individuo gana más o menos de 50 mil por año. Sin embargo, desde la perspectiva de tf.Transform , esta etiqueta es simplemente otra columna categórica.

Utilizamos una lata Pre- TFXIO , BeamRecordCsvTFXIO a traducir las líneas CSV en RecordBatches . TFXIO requiere dos piezas de información importante:

  • un TensorFlow metadatos de esquema que contiene el tipo y la forma de información acerca de cada columna CSV. TensorRepresentation s son una parte opcional del esquema; si no se proporcionan (que es el caso en este ejemplo), se deducirán de la información de tipo y forma. Se puede obtener el esquema, ya sea mediante el uso de una función de ayuda que proporcionamos a traducir del análisis TF especificaciones (que se muestran en este ejemplo), o ejecutando TensorFlow validación de datos .

  • una lista de nombres de columnas, en el orden en que aparecen en el archivo CSV. Tenga en cuenta que esos nombres deben coincidir con los nombres de las funciones en el esquema.

En este ejemplo permitimos que la education-num característica que se encuentra. Esto significa que se representa como un tf.io.VarLenFeature en el feature_spec, y como tf.SparseTensor en el preprocessing_fn . Otras características resultarán tf.Tensor s del mismo nombre en el preprocessing_fn .

csv_tfxio = tfxio.BeamRecordCsvTFXIO(
    physical_format='text', column_names=ordered_columns, schema=SCHEMA)

record_batches = (
    p
    | 'ReadTrainData' >> textio.ReadFromText(train_data_file)
    | ...  # fix up csv lines
    | 'ToRecordBatches' >> csv_tfxio.BeamSource())

tensor_adapter_config = csv_tfxio.TensorAdapterConfig()

. Tenga en cuenta que tuvimos que hacer algunos fix-ups adicionales después de las líneas CSV se leen en caso contrario, podríamos confiar en la CsvTFXIO para manejar tanto la lectura de los archivos y su traducción a RecordBatch es:

csv_tfxio = tfxio.CsvTFXIO(train_data_file, column_name=ordered_columns,
                           schema=SCHEMA)
record_batches = p | 'TFXIORead' >> csv_tfxio.BeamSource()
tensor_adapter_config = csv_tfxio.TensorAdapterConfig()

El preprocesamiento es similar al ejemplo anterior, excepto que la función de preprocesamiento se genera mediante programación en lugar de especificar manualmente cada columna. En la función de pre-procesamiento a continuación, NUMERICAL_COLUMNS y CATEGORICAL_COLUMNS son listas que contienen los nombres de los botones numéricos y categóricos columnas:

def preprocessing_fn(inputs):
  """Preprocess input columns into transformed columns."""
  # Since we are modifying some features and leaving others unchanged, we
  # start by setting `outputs` to a copy of `inputs.
  outputs = inputs.copy()

  # Scale numeric columns to have range [0, 1].
  for key in NUMERIC_FEATURE_KEYS:
    outputs[key] = tft.scale_to_0_1(outputs[key])

  for key in OPTIONAL_NUMERIC_FEATURE_KEYS:
    # This is a SparseTensor because it is optional. Here we fill in a default
    # value when it is missing.
      sparse = tf.sparse.SparseTensor(outputs[key].indices, outputs[key].values,
                                      [outputs[key].dense_shape[0], 1])
      dense = tf.sparse.to_dense(sp_input=sparse, default_value=0.)
    # Reshaping from a batch of vectors of size 1 to a batch to scalars.
    dense = tf.squeeze(dense, axis=1)
    outputs[key] = tft.scale_to_0_1(dense)

  # For all categorical columns except the label column, we generate a
  # vocabulary but do not modify the feature.  This vocabulary is instead
  # used in the trainer, by means of a feature column, to convert the feature
  # from a string to an integer id.
  for key in CATEGORICAL_FEATURE_KEYS:
    tft.vocabulary(inputs[key], vocab_filename=key)

  # For the label column we provide the mapping from string to index.
  initializer = tf.lookup.KeyValueTensorInitializer(
      keys=['>50K', '<=50K'],
      values=tf.cast(tf.range(2), tf.int64),
      key_dtype=tf.string,
      value_dtype=tf.int64)
  table = tf.lookup.StaticHashTable(initializer, default_value=-1)

  outputs[LABEL_KEY] = table.lookup(outputs[LABEL_KEY])

  return outputs

Una diferencia con el ejemplo anterior es que la columna de etiqueta especifica manualmente la asignación de la cadena a un índice. Así '>50' se asigna a 0 y '<=50K' se asigna a 1 porque es útil saber qué índice en los corresponde modelo entrenado para que etiqueta.

El record_batches variable representa un PCollection de pyarrow.RecordBatch es. El tensor_adapter_config está dada por csv_tfxio , que se infiere de SCHEMA (y en última instancia, en este ejemplo, a partir de las especificaciones TF de análisis sintáctico).

La etapa final es escribir los datos transformados en el disco y tiene una forma similar a la de leer los datos sin procesar. El esquema utilizado para hacer esto es parte de la salida de AnalyzeAndTransformDataset la que infiere un esquema para los datos de salida. El código para escribir en el disco se muestra a continuación. El esquema es una parte de los metadatos, pero utiliza los dos indistintamente en el tf.Transform API (es decir, pasar los metadatos para el ExampleProtoCoder ). Tenga en cuenta que esto escribe en un formato diferente. En lugar de textio.WriteToText , utilice la viga de soporte incorporado para la TFRecord formato y utilizar un codificador para codificar los datos mientras que el Example Protos. Este es un formato mejor para usar en la capacitación, como se muestra en la siguiente sección. transformed_eval_data_base proporciona el nombre de archivo base para los fragmentos individuales que se escriben.

transformed_data | "WriteTrainData" >> tfrecordio.WriteToTFRecord(
    transformed_eval_data_base,
    coder=tft.coders.ExampleProtoCoder(transformed_metadata))

Además de los datos de entrenamiento, transform_fn también se escribe con los metadatos:

_ = (
    transform_fn
    | 'WriteTransformFn' >> tft_beam.WriteTransformFn(working_dir))
transformed_metadata | 'WriteMetadata' >> tft_beam.WriteMetadata(
    transformed_metadata_file, pipeline=p)

Ejecutar todo el canal de la viga con p.run().wait_until_finish() . Hasta este punto, la canalización de Beam representa un cálculo distribuido diferido. Proporciona instrucciones sobre lo que se hará, pero las instrucciones no se han ejecutado. Esta última llamada ejecuta la canalización especificada.

Descarga el conjunto de datos del censo

Descargue el conjunto de datos del censo con los siguientes comandos de shell:

  wget https://archive.ics.uci.edu/ml/machine-learning-databases/adult/adult.data
  wget https://archive.ics.uci.edu/ml/machine-learning-databases/adult/adult.test

Cuando se ejecuta el census_example.py guión, pasar al directorio que contiene estos datos como el primer argumento. El script crea un subdirectorio temporal para agregar los datos preprocesados.

Integrar con TensorFlow Training

La sección final del census_example.py muestran cómo se usan los datos preprocesado para entrenar un modelo. Consulte la documentación de los estimadores para más detalles. El primer paso es la construcción de un Estimator que requiere una descripción de las columnas preprocesados. Cada columna numérica se describe como un real_valued_column que es una envoltura alrededor de un vector densa con un tamaño fijo ( 1 en este ejemplo). Cada columna categórica es mapeado de cadena a números enteros y luego se la pasó a indicator_column . tft.TFTransformOutput se utiliza para encontrar la ruta del archivo de vocabulario para cada característica categórica.

real_valued_columns = [feature_column.real_valued_column(key)
                       for key in NUMERIC_FEATURE_KEYS]

one_hot_columns = [
    tf.feature_column.indicator_column(
        tf.feature_column.categorical_column_with_vocabulary_file(
            key=key,
            vocabulary_file=tf_transform_output.vocabulary_file_by_name(
                vocab_filename=key)))
    for key in CATEGORICAL_FEATURE_KEYS]

estimator = tf.estimator.LinearClassifier(real_valued_columns + one_hot_columns)

El siguiente paso es crear un constructor para generar la función de entrada para entrenamiento y evaluación. Los difiere de la utilizada por la formación tf.Learn desde una especificación de función no es necesaria para analizar los datos transformados. En su lugar, utilice los metadatos de los datos transformados para generar una especificación de característica.

def _make_training_input_fn(tf_transform_output, transformed_examples,
                            batch_size):
  ...
  def input_fn():
    """Input function for training and eval."""
    dataset = tf.data.experimental.make_batched_features_dataset(
        ..., tf_transform_output.transformed_feature_spec(), ...)

    transformed_features = tf.compat.v1.data.make_one_shot_iterator(
        dataset).get_next()
    ...

  return input_fn

El código restante es lo mismo que usar el Estimator clase. El ejemplo también contiene código para exportar el modelo en el SavedModel formato. El modelo exportado puede ser utilizado por Tensorflow Porción o la nube ML motor .