Clasifique datos estructurados utilizando capas de preprocesamiento de Keras

Ver en TensorFlow.org Ejecutar en Google Colab Ver fuente en GitHub Descargar libreta

Este tutorial demuestra cómo clasificar datos estructurados, como datos tabulares, utilizando una versión simplificada del conjunto de datos de PetFinder de una competencia de Kaggle almacenada en un archivo CSV.

Utilizará Keras para definir el modelo y las capas de preprocesamiento de Keras como un puente para mapear desde las columnas en un archivo CSV a las características utilizadas para entrenar el modelo. El objetivo es predecir si una mascota será adoptada.

Este tutorial contiene código completo para:

  • Cargando un archivo CSV en un DataFrame usando pandas .
  • Creación de una canalización de entrada para procesar por lotes y mezclar las filas mediante tf.data . (Visite tf.data: Build TensorFlow input pipelines para obtener más detalles).
  • Mapeo de columnas en el archivo CSV a funciones utilizadas para entrenar el modelo con las capas de preprocesamiento de Keras.
  • Construir, entrenar y evaluar un modelo utilizando los métodos incorporados de Keras.

El miniconjunto de datos de PetFinder.my

Hay varios miles de filas en el archivo de conjunto de datos CSV de PetFinder.my mini, donde cada fila describe una mascota (un perro o un gato) y cada columna describe un atributo, como edad, raza, color, etc.

En el resumen del conjunto de datos a continuación, observe que hay principalmente columnas numéricas y categóricas. En este tutorial, solo tratará con esos dos tipos de funciones, eliminando Description (una función de texto libre) y AdoptionSpeed (una función de clasificación) durante el preprocesamiento de datos.

Columna Descripción de la mascota tipo de característica Tipo de datos
Type Tipo de animal ( Dog , Cat ) Categórico Cuerda
Age Envejecer Numérico Entero
Breed1 raza primaria Categórico Cuerda
Color1 Color 1 Categórico Cuerda
Color2 Color 2 Categórico Cuerda
MaturitySize Tamaño en la madurez Categórico Cuerda
FurLength Longitud de la piel Categórico Cuerda
Vaccinated La mascota ha sido vacunada. Categórico Cuerda
Sterilized La mascota ha sido esterilizada. Categórico Cuerda
Health Estado de salud Categórico Cuerda
Fee Tasa de adopción Numérico Entero
Description Redacción de perfil Texto Cuerda
PhotoAmt Total de fotos subidas Numérico Entero
AdoptionSpeed Velocidad categórica de adopción Clasificación Entero

Importar TensorFlow y otras bibliotecas

import numpy as np
import pandas as pd
import tensorflow as tf

from tensorflow.keras import layers
tf.__version__
'2.8.0-rc1'

Cargue el conjunto de datos y léalo en un DataFrame de pandas

pandas es una biblioteca de Python con muchas utilidades útiles para cargar y trabajar con datos estructurados. Use tf.keras.utils.get_file para descargar y extraer el archivo CSV con el mini conjunto de datos PetFinder.my y cárguelo en un DataFrame con pandas.read_csv :

dataset_url = 'http://storage.googleapis.com/download.tensorflow.org/data/petfinder-mini.zip'
csv_file = 'datasets/petfinder-mini/petfinder-mini.csv'

tf.keras.utils.get_file('petfinder_mini.zip', dataset_url,
                        extract=True, cache_dir='.')
dataframe = pd.read_csv(csv_file)
Downloading data from http://storage.googleapis.com/download.tensorflow.org/data/petfinder-mini.zip
1671168/1668792 [==============================] - 0s 0us/step
1679360/1668792 [==============================] - 0s 0us/step

Inspeccione el conjunto de datos comprobando las primeras cinco filas del DataFrame:

dataframe.head()

Crear una variable objetivo

La tarea original en la competencia Predicción de adopción PetFinder.my de Kaggle era predecir la velocidad a la que se adoptará una mascota (por ejemplo, en la primera semana, el primer mes, los primeros tres meses, etc.).

En este tutorial, simplificarás la tarea transformándola en un problema de clasificación binaria, donde simplemente debes predecir si una mascota fue adoptada o no.

Después de modificar la columna AdoptionSpeed , 0 indicará que la mascota no fue adoptada y 1 indicará que sí.

# In the original dataset, `'AdoptionSpeed'` of `4` indicates
# a pet was not adopted.
dataframe['target'] = np.where(dataframe['AdoptionSpeed']==4, 0, 1)

# Drop unused features.
dataframe = dataframe.drop(columns=['AdoptionSpeed', 'Description'])

Dividir el DataFrame en conjuntos de entrenamiento, validación y prueba

El conjunto de datos está en un solo marco de datos de pandas. Divídalo en conjuntos de entrenamiento, validación y prueba utilizando, por ejemplo, una proporción de 80:10:10, respectivamente:

train, val, test = np.split(dataframe.sample(frac=1), [int(0.8*len(dataframe)), int(0.9*len(dataframe))])
print(len(train), 'training examples')
print(len(val), 'validation examples')
print(len(test), 'test examples')
9229 training examples
1154 validation examples
1154 test examples

Cree una tubería de entrada usando tf.data

A continuación, cree una función de utilidad que convierta cada DataFrame de conjunto de entrenamiento, validación y prueba en un tf.data.Dataset , luego mezcle y agrupe los datos.

def df_to_dataset(dataframe, shuffle=True, batch_size=32):
  df = dataframe.copy()
  labels = df.pop('target')
  df = {key: value[:,tf.newaxis] for key, value in dataframe.items()}
  ds = tf.data.Dataset.from_tensor_slices((dict(df), labels))
  if shuffle:
    ds = ds.shuffle(buffer_size=len(dataframe))
  ds = ds.batch(batch_size)
  ds = ds.prefetch(batch_size)
  return ds

Ahora, use la función recién creada ( df_to_dataset ) para verificar el formato de los datos que devuelve la función auxiliar de canalización de entrada invocándola en los datos de entrenamiento, y use un tamaño de lote pequeño para mantener la salida legible:

batch_size = 5
train_ds = df_to_dataset(train, batch_size=batch_size)
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel_launcher.py:4: FutureWarning: Support for multi-dimensional indexing (e.g. `obj[:, None]`) is deprecated and will be removed in a future version.  Convert to a numpy array before indexing instead.
  after removing the cwd from sys.path.
[(train_features, label_batch)] = train_ds.take(1)
print('Every feature:', list(train_features.keys()))
print('A batch of ages:', train_features['Age'])
print('A batch of targets:', label_batch )
Every feature: ['Type', 'Age', 'Breed1', 'Gender', 'Color1', 'Color2', 'MaturitySize', 'FurLength', 'Vaccinated', 'Sterilized', 'Health', 'Fee', 'PhotoAmt', 'target']
A batch of ages: tf.Tensor(
[[84]
 [ 1]
 [ 5]
 [ 1]
 [12]], shape=(5, 1), dtype=int64)
A batch of targets: tf.Tensor([1 1 0 1 0], shape=(5,), dtype=int64)

Como demuestra el resultado, el conjunto de entrenamiento devuelve un diccionario de nombres de columna (del DataFrame) que se asignan a valores de columna de filas.

Aplicar las capas de preprocesamiento de Keras

Las capas de preprocesamiento de Keras le permiten crear canalizaciones de procesamiento de entrada nativas de Keras, que se pueden usar como código de preprocesamiento independiente en flujos de trabajo que no son de Keras, combinados directamente con modelos de Keras y exportados como parte de un modelo guardado de Keras.

En este tutorial, utilizará las siguientes cuatro capas de preprocesamiento para demostrar cómo realizar el preprocesamiento, la codificación de datos estructurados y la ingeniería de características:

Puede obtener más información sobre las capas disponibles en la guía Trabajar con capas de preprocesamiento .

  • Para las características numéricas del mini conjunto de datos PetFinder.my, utilizará una capa tf.keras.layers.Normalization para estandarizar la distribución de los datos.
  • Para las características categóricas , como los Type de mascotas (cadenas de Dog y Cat ), las transformará en tensores codificados en caliente con tf.keras.layers.CategoryEncoding .

Columnas numéricas

Para cada característica numérica en el mini conjunto de datos PetFinder.my, utilizará una capa tf.keras.layers.Normalization para estandarizar la distribución de los datos.

Defina una nueva función de utilidad que devuelva una capa que aplique la normalización por características a las características numéricas utilizando esa capa de preprocesamiento de Keras:

def get_normalization_layer(name, dataset):
  # Create a Normalization layer for the feature.
  normalizer = layers.Normalization(axis=None)

  # Prepare a Dataset that only yields the feature.
  feature_ds = dataset.map(lambda x, y: x[name])

  # Learn the statistics of the data.
  normalizer.adapt(feature_ds)

  return normalizer

A continuación, pruebe la nueva función llamándola en el total de funciones de fotos de mascotas cargadas para normalizar 'PhotoAmt' :

photo_count_col = train_features['PhotoAmt']
layer = get_normalization_layer('PhotoAmt', train_ds)
layer(photo_count_col)
<tf.Tensor: shape=(5, 1), dtype=float32, numpy=
array([[-0.8272058 ],
       [-0.19125296],
       [ 1.3986291 ],
       [-0.19125296],
       [-0.50922936]], dtype=float32)>

Columnas categóricas

Type de mascotas en el conjunto de datos se representan como cadenas ( Dog y Cat ) que deben codificarse en caliente antes de ingresar al modelo. La función de Age

Defina otra nueva función de utilidad que devuelva una capa que asigne valores de un vocabulario a índices enteros y codifique en caliente las características mediante el tf.keras.layers.StringLookup , tf.keras.layers.IntegerLookup y tf.keras.CategoryEncoding capas:

def get_category_encoding_layer(name, dataset, dtype, max_tokens=None):
  # Create a layer that turns strings into integer indices.
  if dtype == 'string':
    index = layers.StringLookup(max_tokens=max_tokens)
  # Otherwise, create a layer that turns integer values into integer indices.
  else:
    index = layers.IntegerLookup(max_tokens=max_tokens)

  # Prepare a `tf.data.Dataset` that only yields the feature.
  feature_ds = dataset.map(lambda x, y: x[name])

  # Learn the set of possible values and assign them a fixed integer index.
  index.adapt(feature_ds)

  # Encode the integer indices.
  encoder = layers.CategoryEncoding(num_tokens=index.vocabulary_size())

  # Apply multi-hot encoding to the indices. The lambda function captures the
  # layer, so you can use them, or include them in the Keras Functional model later.
  return lambda feature: encoder(index(feature))

Pruebe la función get_category_encoding_layer llamándola a las funciones de 'Type' de mascotas para convertirlas en tensores codificados multi-calientes:

test_type_col = train_features['Type']
test_type_layer = get_category_encoding_layer(name='Type',
                                              dataset=train_ds,
                                              dtype='string')
test_type_layer(test_type_col)
<tf.Tensor: shape=(5, 3), dtype=float32, numpy=
array([[0., 1., 0.],
       [0., 1., 0.],
       [0., 1., 0.],
       [0., 1., 0.],
       [0., 1., 0.]], dtype=float32)>

Repita el proceso en las características 'Age' de la mascota:

test_age_col = train_features['Age']
test_age_layer = get_category_encoding_layer(name='Age',
                                             dataset=train_ds,
                                             dtype='int64',
                                             max_tokens=5)
test_age_layer(test_age_col)
<tf.Tensor: shape=(5, 5), dtype=float32, numpy=
array([[1., 0., 0., 0., 0.],
       [0., 0., 0., 1., 0.],
       [1., 0., 0., 0., 0.],
       [0., 0., 0., 1., 0.],
       [1., 0., 0., 0., 0.]], dtype=float32)>

Preprocesar las funciones seleccionadas para entrenar el modelo

Ha aprendido a utilizar varios tipos de capas de preprocesamiento de Keras. A continuación, usted:

  • Aplique las funciones de utilidad de preprocesamiento definidas anteriormente en 13 características numéricas y categóricas del mini conjunto de datos PetFinder.my.
  • Agregue todas las entradas de funciones a una lista.

Como se mencionó al principio, para entrenar el modelo, utilizará el mini conjunto de datos de PetFinder.my numérico ( 'PhotoAmt' , 'Fee' ) y categórico ( 'Age' , 'Type' , 'Color1' , 'Color2' , 'Gender' , 'MaturitySize' 'FurLength' , 'Longitud del pelaje' , 'Vaccinated' , 'Sterilized' , 'Health' , 'Breed1' ).

Anteriormente, usó un tamaño de lote pequeño para demostrar la canalización de entrada. Ahora vamos a crear una nueva tubería de entrada con un tamaño de lote más grande de 256:

batch_size = 256
train_ds = df_to_dataset(train, batch_size=batch_size)
val_ds = df_to_dataset(val, shuffle=False, batch_size=batch_size)
test_ds = df_to_dataset(test, shuffle=False, batch_size=batch_size)
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel_launcher.py:4: FutureWarning: Support for multi-dimensional indexing (e.g. `obj[:, None]`) is deprecated and will be removed in a future version.  Convert to a numpy array before indexing instead.
  after removing the cwd from sys.path.

Normalice las características numéricas (la cantidad de fotos de mascotas y la tarifa de adopción) y agréguelas a una lista de entradas llamada encoded_features :

all_inputs = []
encoded_features = []

# Numerical features.
for header in ['PhotoAmt', 'Fee']:
  numeric_col = tf.keras.Input(shape=(1,), name=header)
  normalization_layer = get_normalization_layer(header, train_ds)
  encoded_numeric_col = normalization_layer(numeric_col)
  all_inputs.append(numeric_col)
  encoded_features.append(encoded_numeric_col)

Convierta los valores categóricos enteros del conjunto de datos (la edad de la mascota) en índices enteros, realice una codificación múltiple y agregue las entradas de características resultantes a encoded_features :

age_col = tf.keras.Input(shape=(1,), name='Age', dtype='int64')

encoding_layer = get_category_encoding_layer(name='Age',
                                             dataset=train_ds,
                                             dtype='int64',
                                             max_tokens=5)
encoded_age_col = encoding_layer(age_col)
all_inputs.append(age_col)
encoded_features.append(encoded_age_col)

Repita el mismo paso para los valores categóricos de cadena:

categorical_cols = ['Type', 'Color1', 'Color2', 'Gender', 'MaturitySize',
                    'FurLength', 'Vaccinated', 'Sterilized', 'Health', 'Breed1']

for header in categorical_cols:
  categorical_col = tf.keras.Input(shape=(1,), name=header, dtype='string')
  encoding_layer = get_category_encoding_layer(name=header,
                                               dataset=train_ds,
                                               dtype='string',
                                               max_tokens=5)
  encoded_categorical_col = encoding_layer(categorical_col)
  all_inputs.append(categorical_col)
  encoded_features.append(encoded_categorical_col)

Crear, compilar y entrenar el modelo.

El siguiente paso es crear un modelo utilizando la API funcional de Keras . Para la primera capa de su modelo, fusione la lista de entradas de características ( encoded_features ) en un vector a través de la concatenación con tf.keras.layers.concatenate .

all_features = tf.keras.layers.concatenate(encoded_features)
x = tf.keras.layers.Dense(32, activation="relu")(all_features)
x = tf.keras.layers.Dropout(0.5)(x)
output = tf.keras.layers.Dense(1)(x)

model = tf.keras.Model(all_inputs, output)

Configure el modelo con Keras Model.compile :

model.compile(optimizer='adam',
              loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
              metrics=["accuracy"])

Visualicemos el gráfico de conectividad:

# Use `rankdir='LR'` to make the graph horizontal.
tf.keras.utils.plot_model(model, show_shapes=True, rankdir="LR")

png

A continuación, entrene y pruebe el modelo:

model.fit(train_ds, epochs=10, validation_data=val_ds)
Epoch 1/10
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/keras/engine/functional.py:559: UserWarning: Input dict contained keys ['target'] which did not match any model input. They will be ignored by the model.
  inputs = self._flatten_to_reference_inputs(inputs)
37/37 [==============================] - 2s 19ms/step - loss: 0.6524 - accuracy: 0.5034 - val_loss: 0.5887 - val_accuracy: 0.6941
Epoch 2/10
37/37 [==============================] - 0s 8ms/step - loss: 0.5906 - accuracy: 0.6648 - val_loss: 0.5627 - val_accuracy: 0.7218
Epoch 3/10
37/37 [==============================] - 0s 8ms/step - loss: 0.5697 - accuracy: 0.6924 - val_loss: 0.5463 - val_accuracy: 0.7504
Epoch 4/10
37/37 [==============================] - 0s 8ms/step - loss: 0.5558 - accuracy: 0.6978 - val_loss: 0.5346 - val_accuracy: 0.7504
Epoch 5/10
37/37 [==============================] - 0s 8ms/step - loss: 0.5502 - accuracy: 0.7105 - val_loss: 0.5272 - val_accuracy: 0.7487
Epoch 6/10
37/37 [==============================] - 0s 8ms/step - loss: 0.5415 - accuracy: 0.7123 - val_loss: 0.5210 - val_accuracy: 0.7608
Epoch 7/10
37/37 [==============================] - 0s 8ms/step - loss: 0.5354 - accuracy: 0.7171 - val_loss: 0.5152 - val_accuracy: 0.7435
Epoch 8/10
37/37 [==============================] - 0s 8ms/step - loss: 0.5301 - accuracy: 0.7214 - val_loss: 0.5113 - val_accuracy: 0.7513
Epoch 9/10
37/37 [==============================] - 0s 8ms/step - loss: 0.5286 - accuracy: 0.7189 - val_loss: 0.5087 - val_accuracy: 0.7574
Epoch 10/10
37/37 [==============================] - 0s 8ms/step - loss: 0.5252 - accuracy: 0.7260 - val_loss: 0.5058 - val_accuracy: 0.7539
<keras.callbacks.History at 0x7f5f9fa91c50>
loss, accuracy = model.evaluate(test_ds)
print("Accuracy", accuracy)
5/5 [==============================] - 0s 6ms/step - loss: 0.5012 - accuracy: 0.7626
Accuracy 0.762565016746521

Realizar inferencia

El modelo que ha desarrollado ahora puede clasificar una fila de un archivo CSV directamente después de haber incluido las capas de preprocesamiento dentro del propio modelo.

Ahora puede guardar y volver a cargar el modelo de Keras con Model.save y Model.load_model antes de realizar la inferencia en nuevos datos:

model.save('my_pet_classifier')
reloaded_model = tf.keras.models.load_model('my_pet_classifier')
2022-01-26 06:20:08.013613: W tensorflow/python/util/util.cc:368] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them.
WARNING:absl:Function `_wrapped_model` contains input name(s) PhotoAmt, Fee, Age, Type, Color1, Color2, Gender, MaturitySize, FurLength, Vaccinated, Sterilized, Health, Breed1 with unsupported characters which will be renamed to photoamt, fee, age, type, color1, color2, gender, maturitysize, furlength, vaccinated, sterilized, health, breed1 in the SavedModel.
INFO:tensorflow:Assets written to: my_pet_classifier/assets
INFO:tensorflow:Assets written to: my_pet_classifier/assets

Para obtener una predicción para una nueva muestra, simplemente puede llamar al método Keras Model.predict . Solo hay dos cosas que debes hacer:

  1. Envuelva los escalares en una lista para tener una dimensión de lote (los Model solo procesan lotes de datos, no muestras individuales).
  2. Llame a tf.convert_to_tensor en cada función.
sample = {
    'Type': 'Cat',
    'Age': 3,
    'Breed1': 'Tabby',
    'Gender': 'Male',
    'Color1': 'Black',
    'Color2': 'White',
    'MaturitySize': 'Small',
    'FurLength': 'Short',
    'Vaccinated': 'No',
    'Sterilized': 'No',
    'Health': 'Healthy',
    'Fee': 100,
    'PhotoAmt': 2,
}

input_dict = {name: tf.convert_to_tensor([value]) for name, value in sample.items()}
predictions = reloaded_model.predict(input_dict)
prob = tf.nn.sigmoid(predictions[0])

print(
    "This particular pet had a %.1f percent probability "
    "of getting adopted." % (100 * prob)
)
This particular pet had a 77.7 percent probability of getting adopted.

Próximos pasos

Para obtener más información sobre la clasificación de datos estructurados, intente trabajar con otros conjuntos de datos. Para mejorar la precisión durante el entrenamiento y la prueba de sus modelos, piense detenidamente qué características incluir en su modelo y cómo deben representarse.

A continuación se presentan algunas sugerencias para conjuntos de datos:

  • Conjuntos de datos de TensorFlow: MovieLens : un conjunto de clasificaciones de películas de un servicio de recomendación de películas.
  • Conjuntos de datos de TensorFlow: calidad del vino : dos conjuntos de datos relacionados con las variantes rojas y blancas del vino portugués "Vinho Verde". También puede encontrar el conjunto de datos de calidad del vino tinto en Kaggle .
  • Kaggle: conjunto de datos arXiv : un corpus de 1,7 millones de artículos académicos de arXiv, que abarcan física, informática, matemáticas, estadística, ingeniería eléctrica, biología cuantitativa y economía.