O Dia da Comunidade de ML é dia 9 de novembro! Junte-nos para atualização de TensorFlow, JAX, e mais Saiba mais

Classifique dados estruturados usando camadas de pré-processamento Keras

Ver no TensorFlow.org Executar no Google Colab Ver fonte no GitHub Baixar caderno

Este tutorial demonstra como classificar dados estruturados (por exemplo, dados tabulares em um CSV). Você vai usar Keras para definir o modelo e camadas de pré-processamento como uma ponte para mapear a partir de colunas em um CSV de recursos usados para treinar o modelo. Este tutorial contém o código completo para:

  • Carregar um arquivo CSV usando Pandas .
  • Construir um gasoduto de entrada para lote e embaralhar as linhas usando tf.data .
  • Mapeie a partir de colunas no CSV para recursos usados ​​para treinar o modelo usando camadas de pré-processamento Keras.
  • Construa, treine e avalie um modelo usando Keras.

O conjunto de dados

Você vai usar uma versão simplificada do Petfinder conjunto de dados . Existem vários milhares de linhas no CSV. Cada linha descreve um animal de estimação e cada coluna descreve um atributo. Você usará essas informações para prever se o animal será adotado.

A seguir está uma descrição deste conjunto de dados. Observe que há colunas numéricas e categóricas. Há uma coluna de texto livre que você não usará neste tutorial.

Coluna Descrição Tipo de recurso Tipo de dados
Modelo Tipo de animal (cachorro, gato) Categórico fragmento
Era Idade do animal de estimação Numérico inteiro
Raça 1 Raça primária do animal de estimação Categórico fragmento
Color1 Cor 1 do animal de estimação Categórico fragmento
Cor 2 Cor 2 do animal de estimação Categórico fragmento
MaturitySize Tamanho na maturidade Categórico fragmento
FurLength Comprimento do pelo Categórico fragmento
Vacinado Animal de estimação foi vacinado Categórico fragmento
Esterilizado Animal de estimação foi esterilizado Categórico fragmento
Saúde Condição de saúde Categórico fragmento
Taxa Taxa de adoção Numérico inteiro
Descrição Escrita de perfil para este animal de estimação Texto fragmento
PhotoAmt Total de fotos enviadas para este animal de estimação Numérico inteiro
AdoptionSpeed Velocidade de adoção Classificação inteiro

Importar TensorFlow e outras bibliotecas

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

from sklearn.model_selection import train_test_split
from tensorflow.keras import layers
from tensorflow.keras.layers.experimental import preprocessing
tf.__version__
'2.5.0'

Use o Pandas para criar um dataframe

Pandas é uma biblioteca Python com muitas utilidades votos para carregar e trabalhar com dados estruturados. Você usará o Pandas para baixar o conjunto de dados de um URL e carregá-lo em um dataframe.

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()

Criar variável de destino

A tarefa na competição Kaggle é prever a velocidade com que um animal de estimação será adotado (por exemplo, na primeira semana, no primeiro mês, nos primeiros três meses e assim por diante). Vamos simplificar isso para nosso tutorial. Aqui, você vai transformar isso em um problema de classificação binária e simplesmente prever se o animal foi adotado ou não.

Depois de modificar a coluna do rótulo, 0 indicará que o animal de estimação não foi adotado e 1 indicará que foi.

# 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 o dataframe em treinamento, validação e teste

O conjunto de dados que você baixou era um único arquivo CSV. Você vai dividir isso em conjuntos de treinamento, validação e teste.

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

Crie um pipeline de entrada usando tf.data

Em seguida, você irá envolver os dataframes com tf.data , a fim de embaralhar e lote os dados. Se você estivesse trabalhando com um arquivo CSV muito grande (tão grande que não cabe na memória), você usaria tf.data para lê-lo diretamente do disco. Isso não é abordado neste 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)
  ds = ds.prefetch(batch_size)
  return ds

Agora que você criou o pipeline de entrada, vamos chamá-lo para ver o formato dos dados que ele retorna. Você usou um tamanho de lote pequeno para manter a saída legível.

batch_size = 5
train_ds = df_to_dataset(train, batch_size=batch_size)
[(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']
A batch of ages: tf.Tensor([7 3 1 4 2], shape=(5,), dtype=int64)
A batch of targets: tf.Tensor([0 0 1 0 1], shape=(5,), dtype=int64)

Você pode ver que o conjunto de dados retorna um dicionário de nomes de coluna (do dataframe) que mapeia para valores de coluna de linhas no dataframe.

Demonstrar o uso de camadas de pré-processamento.

A API de camadas de pré-processamento Keras permite que você crie pipelines de processamento de entrada nativos Keras. Você usará 3 camadas de pré-processamento para demonstrar o código de pré-processamento do recurso.

Você pode encontrar uma lista de camadas de pré-processamento disponíveis aqui .

Colunas numéricas

Para cada recurso Numérico, você usará uma camada de Normalização () para garantir que a média de cada recurso seja 0 e seu desvio padrão seja 1.

get_normalization_layer função devolve uma camada a qual se aplica a normalização featurewise características numéricas.

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

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

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

  return normalizer
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.8334968],
       [-0.8334968],
       [ 1.0716735],
       [-0.8334968],
       [-0.8334968]], dtype=float32)>

Colunas categóricas

Neste conjunto de dados, o tipo é representado como uma string (por exemplo, 'Cachorro' ou 'Gato'). Você não pode alimentar strings diretamente para um modelo. A camada de pré-processamento se encarrega de representar as strings como um vetor one-hot.

get_category_encoding_layer função devolve uma camada que mapeia os valores a partir de um vocabulário para índices inteiros e codifica um-quentes as características.

def get_category_encoding_layer(name, dataset, dtype, max_tokens=None):
  # Create a StringLookup layer which will turn strings into integer indices
  if dtype == 'string':
    index = preprocessing.StringLookup(max_tokens=max_tokens)
  else:
    index = preprocessing.IntegerLookup(max_tokens=max_tokens)

  # Prepare a Dataset that only yields our 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)

  # Create a Discretization for our integer indices.
  encoder = preprocessing.CategoryEncoding(num_tokens=index.vocabulary_size())

  # Apply one-hot encoding to our indices. The lambda function captures the
  # layer so we can use them, or include them in the functional model later.
  return lambda feature: encoder(index(feature))
type_col = train_features['Type']
layer = get_category_encoding_layer('Type', train_ds, 'string')
layer(type_col)
<tf.Tensor: shape=(5, 4), dtype=float32, numpy=
array([[0., 0., 1., 0.],
       [0., 0., 0., 1.],
       [0., 0., 0., 1.],
       [0., 0., 1., 0.],
       [0., 0., 1., 0.]], dtype=float32)>

Freqüentemente, você não deseja alimentar um número diretamente no modelo, mas, em vez disso, usar uma codificação one-hot dessas entradas. Considere os dados brutos que representam a idade de um animal de estimação.

type_col = train_features['Age']
category_encoding_layer = get_category_encoding_layer('Age', train_ds,
                                                      'int64', 5)
category_encoding_layer(type_col)
<tf.Tensor: shape=(5, 5), dtype=float32, numpy=
array([[0., 1., 0., 0., 0.],
       [0., 0., 0., 1., 0.],
       [0., 0., 0., 0., 1.],
       [0., 1., 0., 0., 0.],
       [0., 0., 1., 0., 0.]], dtype=float32)>

Escolha quais colunas usar

Você viu como usar vários tipos de camadas de pré-processamento. Agora você vai usá-los para treinar um modelo. Você estará usando API Keras-funcional para construir o modelo. A API funcional Keras é uma maneira de criar modelos que são mais flexíveis do que o tf.keras.Sequential API.

O objetivo deste tutorial é mostrar o código completo (por exemplo, a mecânica) necessária para trabalhar com camadas de pré-processamento. Algumas colunas foram selecionadas arbitrariamente para treinar nosso modelo.

Anteriormente, você usou um pequeno tamanho de lote para demonstrar o pipeline de entrada. Vamos agora criar um novo pipeline de entrada com um tamanho de lote maior.

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)
all_inputs = []
encoded_features = []

# Numeric 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)
# Categorical features encoded as integers.
age_col = tf.keras.Input(shape=(1,), name='Age', dtype='int64')
encoding_layer = get_category_encoding_layer('Age', 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)
# Categorical features encoded as string.
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(header, 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)

Crie, compile e treine o modelo

Agora você pode criar nosso modelo de ponta a ponta.

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)
model.compile(optimizer='adam',
              loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
              metrics=["accuracy"])

Vamos visualizar nosso gráfico de conectividade:

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

png

Treine o modelo

model.fit(train_ds, epochs=10, validation_data=val_ds)
Epoch 1/10
29/29 [==============================] - 2s 19ms/step - loss: 0.6824 - accuracy: 0.4887 - val_loss: 0.5852 - val_accuracy: 0.7134
Epoch 2/10
29/29 [==============================] - 0s 7ms/step - loss: 0.5998 - accuracy: 0.6500 - val_loss: 0.5615 - val_accuracy: 0.7340
Epoch 3/10
29/29 [==============================] - 0s 7ms/step - loss: 0.5775 - accuracy: 0.6676 - val_loss: 0.5489 - val_accuracy: 0.7243
Epoch 4/10
29/29 [==============================] - 0s 7ms/step - loss: 0.5553 - accuracy: 0.6928 - val_loss: 0.5403 - val_accuracy: 0.7264
Epoch 5/10
29/29 [==============================] - 0s 7ms/step - loss: 0.5553 - accuracy: 0.7045 - val_loss: 0.5338 - val_accuracy: 0.7248
Epoch 6/10
29/29 [==============================] - 0s 7ms/step - loss: 0.5417 - accuracy: 0.7058 - val_loss: 0.5290 - val_accuracy: 0.7281
Epoch 7/10
29/29 [==============================] - 0s 7ms/step - loss: 0.5369 - accuracy: 0.7093 - val_loss: 0.5251 - val_accuracy: 0.7302
Epoch 8/10
29/29 [==============================] - 0s 7ms/step - loss: 0.5364 - accuracy: 0.7135 - val_loss: 0.5222 - val_accuracy: 0.7291
Epoch 9/10
29/29 [==============================] - 0s 7ms/step - loss: 0.5296 - accuracy: 0.7154 - val_loss: 0.5198 - val_accuracy: 0.7362
Epoch 10/10
29/29 [==============================] - 0s 7ms/step - loss: 0.5288 - accuracy: 0.7222 - val_loss: 0.5175 - val_accuracy: 0.7313
<tensorflow.python.keras.callbacks.History at 0x7f51e056a510>
loss, accuracy = model.evaluate(test_ds)
print("Accuracy", accuracy)
10/10 [==============================] - 0s 5ms/step - loss: 0.5209 - accuracy: 0.7331
Accuracy 0.7331022620201111

Inferência sobre novos dados

Agora você pode salvar e recarregar o modelo Keras. Siga o tutorial aqui para mais informações sobre modelos TensorFlow.

model.save('my_pet_classifier')
reloaded_model = tf.keras.models.load_model('my_pet_classifier')
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 se ter uma previsão para uma nova amostra, você pode simplesmente chamar model.predict() . Existem apenas duas coisas que você precisa fazer:

  1. Envolva os escalares em uma lista para ter uma dimensão de lote (os modelos processam apenas lotes de dados, não amostras únicas)
  2. Chamada convert_to_tensor em cada recurso
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 86.6 percent probability of getting adopted.

Próximos passos

A melhor maneira de aprender mais sobre como classificar dados estruturados é experimentando você mesmo. Você pode querer encontrar outro conjunto de dados para trabalhar e treinar um modelo para classificá-lo usando um código semelhante ao anterior. Para melhorar a precisão, pense cuidadosamente sobre quais recursos incluir em seu modelo e como eles devem ser representados.