Se usó la API de Cloud Translation para traducir esta página.
Switch to English

Clasificar datos estructurados con columnas de características

Ver en TensorFlow.org Ejecutar en Google Colab Ver código fuente en GitHub Descargar cuaderno

Este tutorial muestra cómo clasificar datos estructurados (por ejemplo, datos tabulares en un CSV). Usaremos Keras para definir el modelo, y presentaremos columnas como un puente para mapear desde columnas en un CSV a características utilizadas para entrenar el modelo. Este tutorial contiene código completo para:

  • Cargue un archivo CSV usando Pandas .
  • Construya una tubería de entrada para agrupar y mezclar las filas usando tf.data .
  • Mapa de columnas en el CSV a características utilizadas para entrenar el modelo utilizando columnas de características.
  • Construye, entrena y evalúa un modelo usando Keras.

El conjunto de datos

Utilizaremos una versión simplificada del conjunto de datos PetFinder. Hay varios miles de filas en el CSV. Cada fila describe una mascota y cada columna describe un atributo. Utilizaremos esta información para predecir la velocidad a la que se adoptará la mascota.

A continuación se incluye una descripción de este conjunto de datos. Observe que hay columnas numéricas y categóricas. Hay una columna de texto libre que no utilizaremos en este tutorial.

Columna Descripción Tipo de Característica Tipo de datos
Tipo Tipo de animal (perro, gato) Categórico cuerda
Años Edad de la mascota Numérico entero
Raza1 Raza primaria de la mascota Categórico cuerda
Color1 Color 1 de mascota Categórico cuerda
Color2 Color 2 de mascota Categórico cuerda
Madurez Tamaño Talla al vencimiento Categórico cuerda
Longitud de pelaje Longitud de la piel Categórico cuerda
Vacunados La mascota ha sido vacunada Categórico cuerda
Esterilizado Mascota ha sido esterilizada Categórico cuerda
Salud Estado de salud Categórico cuerda
Cuota Tarifa de Adopción Numérico entero
Descripción Reseña de perfil para esta mascota Texto cuerda
PhotoAmt Total de fotos cargadas para esta mascota Numérico entero
Adopción Velocidad Velocidad de adopción Clasificación entero

Importar TensorFlow y otras bibliotecas

pip install -q sklearn
 import numpy as np
import pandas as pd

import tensorflow as tf

from tensorflow import feature_column
from tensorflow.keras import layers
from sklearn.model_selection import train_test_split
 

Use pandas para crear un marco de datos

Pandas es una biblioteca de Python con muchas utilidades útiles para cargar y trabajar con datos estructurados. Usaremos Pandas para descargar el conjunto de datos desde una URL y cargarlo en un marco de datos.

 import pathlib

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

 dataframe.head()
 

Crear variable objetivo

La tarea en el conjunto de datos original es predecir la velocidad a la que se adoptará una mascota (por ejemplo, en la primera semana, el primer mes, los primeros tres meses, etc.). Simplifiquemos esto para nuestro tutorial. Aquí, transformaremos esto en un problema de clasificación binaria, y simplemente predeciremos si la mascota fue adoptada o no.

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

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

# Drop un-used columns.
dataframe = dataframe.drop(columns=['AdoptionSpeed', 'Description'])
 

Divida el marco de datos en tren, validación y prueba

El conjunto de datos que descargamos era un solo archivo CSV. Dividiremos esto en trenes, validación y conjuntos de pruebas.

 train, test = train_test_split(dataframe, test_size=0.2)
train, val = train_test_split(train, test_size=0.2)
print(len(train), 'train examples')
print(len(val), 'validation examples')
print(len(test), 'test examples')
 
7383 train examples
1846 validation examples
2308 test examples

Crear una tubería de entrada usando tf.data

A continuación, envolveremos los marcos de datos con tf.data . Esto nos permitirá usar columnas de características como un puente para mapear desde las columnas en el marco de datos de Pandas a las características utilizadas para entrenar el modelo. Si estuviéramos trabajando con un archivo CSV muy grande (tan grande que no cabe en la memoria), usaríamos tf.data para leerlo directamente desde el disco. Eso no está cubierto en este tutorial.

 # A utility method to create a tf.data dataset from a Pandas Dataframe
def df_to_dataset(dataframe, shuffle=True, batch_size=32):
  dataframe = dataframe.copy()
  labels = dataframe.pop('target')
  ds = tf.data.Dataset.from_tensor_slices((dict(dataframe), labels))
  if shuffle:
    ds = ds.shuffle(buffer_size=len(dataframe))
  ds = ds.batch(batch_size)
  return ds
 
 batch_size = 5 # A small batch sized is used for demonstration purposes
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)
 

Comprender la tubería de entrada

Ahora que hemos creado la canalización de entrada, llamemos para ver el formato de los datos que devuelve. Hemos utilizado un pequeño tamaño de lote para mantener la salida legible.

 for feature_batch, label_batch in train_ds.take(1):
  print('Every feature:', list(feature_batch.keys()))
  print('A batch of ages:', feature_batch['Age'])
  print('A batch of targets:', label_batch )
 
Every feature: ['Type', 'Age', 'Breed1', 'Gender', 'Color1', 'Color2', 'MaturitySize', 'FurLength', 'Vaccinated', 'Sterilized', 'Health', 'Fee', 'PhotoAmt']
A batch of ages: tf.Tensor([ 2  4 48 36 15], shape=(5,), dtype=int64)
A batch of targets: tf.Tensor([0 1 0 1 1], shape=(5,), dtype=int64)

Podemos ver que el conjunto de datos devuelve un diccionario de nombres de columna (del marco de datos) que se asignan a los valores de columna de las filas del marco de datos.

Demostrar varios tipos de columnas de características

TensorFlow proporciona muchos tipos de columnas de características. En esta sección, crearemos varios tipos de columnas de características y demostraremos cómo transforman una columna desde el marco de datos.

 # We will use this batch to demonstrate several types of feature columns
example_batch = next(iter(train_ds))[0]
 
 # A utility method to create a feature column
# and to transform a batch of data
def demo(feature_column):
  feature_layer = layers.DenseFeatures(feature_column)
  print(feature_layer(example_batch).numpy())
 

Columnas numéricas

La salida de una columna de características se convierte en la entrada al modelo (usando la función de demostración definida anteriormente, podremos ver exactamente cómo se transforma cada columna del marco de datos). Una columna numérica es el tipo más simple de columna. Se utiliza para representar características reales valoradas. Al usar esta columna, su modelo recibirá el valor de la columna del marco de datos sin cambios.

 photo_count = feature_column.numeric_column('PhotoAmt')
demo(photo_count)
 
[[5.]
 [1.]
 [8.]
 [1.]
 [4.]]

En el conjunto de datos PetFinder, la mayoría de las columnas del marco de datos son categóricas.

Columnas cubicadas

A menudo, no desea alimentar un número directamente en el modelo, sino dividir su valor en diferentes categorías basadas en rangos numéricos. Considere los datos sin procesar que representan la edad de una persona. En lugar de representar la edad como una columna numérica, podríamos dividir la edad en varios segmentos usando una columna dividida en segmentos . Observe que los valores únicos de abajo describen con qué rango de edad coincide cada fila.

 age = feature_column.numeric_column('Age')
age_buckets = feature_column.bucketized_column(age, boundaries=[1, 3, 5])
demo(age_buckets)
 
[[0. 0. 1. 0.]
 [0. 1. 0. 0.]
 [0. 1. 0. 0.]
 [0. 0. 0. 1.]
 [0. 0. 1. 0.]]

Columnas categóricas

En este conjunto de datos, Tipo se representa como una cadena (por ejemplo, 'Perro' o 'Gato'). No podemos alimentar cadenas directamente a un modelo. En su lugar, primero debemos asignarlos a valores numéricos. Las columnas de vocabulario categórico proporcionan una forma de representar cadenas como un vector único (al igual que ha visto anteriormente con los cubos de edad). El vocabulario puede pasarse como una lista usando categorical_column_with_vocabulary_list , o cargarse desde un archivo usando categorical_column_with_vocabulary_file .

 animal_type = feature_column.categorical_column_with_vocabulary_list(
      'Type', ['Cat', 'Dog'])

animal_type_one_hot = feature_column.indicator_column(animal_type)
demo(animal_type_one_hot)
 
[[1. 0.]
 [0. 1.]
 [0. 1.]
 [0. 1.]
 [0. 1.]]

Incrustar columnas

Supongamos que, en lugar de tener solo algunas cadenas posibles, tenemos miles (o más) valores por categoría. Por una serie de razones, a medida que aumenta el número de categorías, se vuelve inviable entrenar una red neuronal usando codificaciones de uno en caliente. Podemos usar una columna de incrustación para superar esta limitación. En lugar de representar los datos como un vector único de muchas dimensiones, una columna de incrustación representa esos datos como un vector denso de menor dimensión en el que cada celda puede contener cualquier número, no solo 0 o 1. El tamaño de la incrustación ( 8, en el ejemplo a continuación) es un parámetro que debe ajustarse.

 # Notice the input to the embedding column is the categorical column
# we previously created
breed1 = feature_column.categorical_column_with_vocabulary_list(
      'Breed1', dataframe.Breed1.unique())
breed1_embedding = feature_column.embedding_column(breed1, dimension=8)
demo(breed1_embedding)
 
[[-0.02612482 -0.03677908 -0.23290709  0.09945397  0.08281118  0.36647838
  -0.17653757 -0.19444326]
 [ 0.30964506 -0.47443122 -0.41711554 -0.64884776 -0.21938232  0.4189953
  -0.44518548 -0.03063301]
 [ 0.30964506 -0.47443122 -0.41711554 -0.64884776 -0.21938232  0.4189953
  -0.44518548 -0.03063301]
 [-0.13033295  0.13335079 -0.25702646 -0.30840352  0.15335475  0.28056872
   0.13844194  0.3651677 ]
 [ 0.30964506 -0.47443122 -0.41711554 -0.64884776 -0.21938232  0.4189953
  -0.44518548 -0.03063301]]

Columnas de características hash

Otra forma de representar una columna categórica con un gran número de valores es usar una columna_categoría_con_categoría_hash . Esta columna de características calcula un valor hash de la entrada, luego selecciona uno de los cubos hash_bucket_size para codificar una cadena. Al usar esta columna, no necesita proporcionar el vocabulario, y puede elegir hacer que la cantidad de hash_buckets sea significativamente menor que la cantidad de categorías reales para ahorrar espacio.

 breed1_hashed = feature_column.categorical_column_with_hash_bucket(
      'Breed1', hash_bucket_size=10)
demo(feature_column.indicator_column(breed1_hashed))
 
[[0. 0. 0. 0. 0. 0. 0. 1. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 1. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 1. 0. 0.]
 [0. 0. 0. 0. 1. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 1. 0. 0.]]

Columnas de entidades cruzadas

La combinación de características en una sola característica, mejor conocida como cruces de características , permite que un modelo aprenda pesos separados para cada combinación de características. Aquí, crearemos una nueva característica que es la combinación de Age y Type. Tenga en cuenta que crossed_column no crea la tabla completa de todas las combinaciones posibles (que podrían ser muy grandes). En cambio, está respaldado por una hashed_column , por lo que puede elegir qué tan grande es la tabla.

 crossed_feature = feature_column.crossed_column([age_buckets, animal_type], hash_bucket_size=10)
demo(feature_column.indicator_column(crossed_feature))
 
[[0. 1. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 1. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 1. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 1. 0.]
 [0. 0. 0. 0. 0. 0. 1. 0. 0. 0.]]

Elija qué columnas usar

Hemos visto cómo usar varios tipos de columnas de características. Ahora los usaremos para entrenar a un modelo. El objetivo de este tutorial es mostrarle el código completo (por ejemplo, la mecánica) necesario para trabajar con columnas de características. Hemos seleccionado algunas columnas para entrenar nuestro modelo a continuación de manera arbitraria.

 feature_columns = []

# numeric cols
for header in ['PhotoAmt', 'Fee', 'Age']:
  feature_columns.append(feature_column.numeric_column(header))
 
 # bucketized cols
age = feature_column.numeric_column('Age')
age_buckets = feature_column.bucketized_column(age, boundaries=[1, 2, 3, 4, 5])
feature_columns.append(age_buckets)
 
 # indicator_columns
indicator_column_names = ['Type', 'Color1', 'Color2', 'Gender', 'MaturitySize',
                          'FurLength', 'Vaccinated', 'Sterilized', 'Health']
for col_name in indicator_column_names:
  categorical_column = feature_column.categorical_column_with_vocabulary_list(
      col_name, dataframe[col_name].unique())
  indicator_column = feature_column.indicator_column(categorical_column)
  feature_columns.append(indicator_column)
 
 # embedding columns
breed1 = feature_column.categorical_column_with_vocabulary_list(
      'Breed1', dataframe.Breed1.unique())
breed1_embedding = feature_column.embedding_column(breed1, dimension=8)
feature_columns.append(breed1_embedding)
 
 # crossed columns
age_type_feature = feature_column.crossed_column([age_buckets, animal_type], hash_bucket_size=100)
feature_columns.append(feature_column.indicator_column(age_type_feature))
 

Crear una capa de entidades

Ahora que hemos definido nuestras columnas de características, utilizaremos una capa DenseFeatures para ingresarlas en nuestro modelo Keras.

 feature_layer = tf.keras.layers.DenseFeatures(feature_columns)
 

Anteriormente, utilizamos un pequeño tamaño de lote para demostrar cómo funcionaban las columnas de características. Creamos una nueva tubería de entrada con un tamaño de lote más grande.

 batch_size = 32
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)
 

Crea, compila y entrena el modelo

 model = tf.keras.Sequential([
  feature_layer,
  layers.Dense(128, activation='relu'),
  layers.Dense(128, activation='relu'),
  layers.Dropout(.1),
  layers.Dense(1)
])

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

model.fit(train_ds,
          validation_data=val_ds,
          epochs=10)
 
Epoch 1/10
231/231 [==============================] - 2s 9ms/step - loss: 0.6442 - accuracy: 0.6783 - val_loss: 0.5172 - val_accuracy: 0.6907
Epoch 2/10
231/231 [==============================] - 2s 7ms/step - loss: 0.5379 - accuracy: 0.7169 - val_loss: 0.5099 - val_accuracy: 0.7178
Epoch 3/10
231/231 [==============================] - 2s 7ms/step - loss: 0.5166 - accuracy: 0.7259 - val_loss: 0.4996 - val_accuracy: 0.7400
Epoch 4/10
231/231 [==============================] - 2s 7ms/step - loss: 0.5019 - accuracy: 0.7321 - val_loss: 0.5063 - val_accuracy: 0.6874
Epoch 5/10
231/231 [==============================] - 2s 7ms/step - loss: 0.4930 - accuracy: 0.7408 - val_loss: 0.4937 - val_accuracy: 0.7324
Epoch 6/10
231/231 [==============================] - 2s 7ms/step - loss: 0.4915 - accuracy: 0.7422 - val_loss: 0.4951 - val_accuracy: 0.7367
Epoch 7/10
231/231 [==============================] - 2s 7ms/step - loss: 0.4841 - accuracy: 0.7520 - val_loss: 0.4984 - val_accuracy: 0.7129
Epoch 8/10
231/231 [==============================] - 2s 7ms/step - loss: 0.4788 - accuracy: 0.7534 - val_loss: 0.4919 - val_accuracy: 0.7291
Epoch 9/10
231/231 [==============================] - 2s 7ms/step - loss: 0.4751 - accuracy: 0.7475 - val_loss: 0.4955 - val_accuracy: 0.7254
Epoch 10/10
231/231 [==============================] - 2s 7ms/step - loss: 0.4699 - accuracy: 0.7565 - val_loss: 0.5118 - val_accuracy: 0.6896

<tensorflow.python.keras.callbacks.History at 0x7f31b3a185f8>
 loss, accuracy = model.evaluate(test_ds)
print("Accuracy", accuracy)
 
73/73 [==============================] - 0s 5ms/step - loss: 0.5202 - accuracy: 0.7002
Accuracy 0.7001733183860779

Próximos pasos

La mejor manera de aprender más sobre la clasificación de datos estructurados es probarlo usted mismo. Sugerimos encontrar otro conjunto de datos para trabajar y entrenar un modelo para clasificarlo usando un código similar al anterior. Para mejorar la precisión, piense detenidamente qué características incluir en su modelo y cómo se deben representar.