Classifica i dati strutturati utilizzando i livelli di preelaborazione Keras

Visualizza su TensorFlow.org Esegui in Google Colab Visualizza l'origine su GitHub Scarica quaderno

Questo tutorial mostra come classificare i dati strutturati, come i dati tabulari, utilizzando una versione semplificata del set di dati PetFinder da una competizione Kaggle archiviata in un file CSV.

Utilizzerai Keras per definire il modello e i livelli di preelaborazione Keras come ponte per mappare dalle colonne in un file CSV alle funzioni utilizzate per addestrare il modello. L'obiettivo è prevedere se un animale domestico verrà adottato.

Questo tutorial contiene il codice completo per:

  • Caricamento di un file CSV in un DataFrame utilizzando Pandas .
  • Creazione di una pipeline di input per eseguire in batch e mescolare le righe utilizzando tf.data . (Visita tf.data: Build pipeline di input TensorFlow per maggiori dettagli.)
  • Mappatura dalle colonne nel file CSV alle funzionalità utilizzate per addestrare il modello con i livelli di preelaborazione Keras.
  • Costruire, addestrare e valutare un modello utilizzando i metodi integrati di Keras.

Il mini set di dati PetFinder.my

Ci sono diverse migliaia di righe nel file del set di dati CSV di PetFinder.my mini, in cui ogni riga descrive un animale domestico (un cane o un gatto) e ogni colonna descrive un attributo, come età, razza, colore e così via.

Nel riepilogo del set di dati riportato di seguito, si noti che sono presenti principalmente colonne numeriche e categoriali. In questo tutorial, ti occuperai solo di questi due tipi di funzionalità, rilasciando Description (una funzionalità di testo libero) e AdoptionSpeed (una funzionalità di classificazione) durante la preelaborazione dei dati.

Colonna Descrizione dell'animale domestico Tipo di caratteristica Tipo di dati
Type Tipo di animale ( Dog , Cat ) Categorico Corda
Age Età Numerico Numero intero
Breed1 Razza primaria Categorico Corda
Color1 Colore 1 Categorico Corda
Color2 Colore 2 Categorico Corda
MaturitySize Taglia a maturità Categorico Corda
FurLength Lunghezza della pelliccia Categorico Corda
Vaccinated L'animale è stato vaccinato Categorico Corda
Sterilized L'animale è stato sterilizzato Categorico Corda
Health Condizione di salute Categorico Corda
Fee Tassa di adozione Numerico Numero intero
Description Redazione del profilo Testo Corda
PhotoAmt Totale foto caricate Numerico Numero intero
AdoptionSpeed Velocità categoriale di adozione Classificazione Numero intero

Importa TensorFlow e altre librerie

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

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

Carica il set di dati e leggilo in un DataFrame panda

pandas è una libreria Python con molte utili utilità per caricare e lavorare con dati strutturati. Usa tf.keras.utils.get_file per scaricare ed estrarre il file CSV con il mini set di dati PetFinder.my e caricarlo in 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

Ispeziona il set di dati controllando le prime cinque righe di DataFrame:

dataframe.head()

Crea una variabile di destinazione

Il compito originale del concorso PetFinder.my Adoption Prediction di Kaggle era di prevedere la velocità con cui un animale domestico verrà adottato (ad es. nella prima settimana, nel primo mese, nei primi tre mesi e così via).

In questo tutorial, semplificherai il compito trasformandolo in un problema di classificazione binaria, in cui devi semplicemente prevedere se un animale domestico è stato adottato o meno.

Dopo aver modificato la colonna AdoptionSpeed , 0 indicherà che l'animale non è stato adottato e 1 indicherà che lo era.

# 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'])

Suddividi il DataFrame in set di training, convalida e test

Il set di dati è in un singolo DataFrame panda. Suddividilo in set di addestramento, convalida e test utilizzando, ad esempio, un rapporto 80:10:10, rispettivamente:

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

Crea una pipeline di input usando tf.data

Quindi, crea una funzione di utilità che converte ogni set di dati di training, convalida e test DataFrame in un tf.data.Dataset , quindi mescola e raggruppa i dati in batch.

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

Ora, usa la funzione appena creata ( df_to_dataset ) per controllare il formato dei dati che la funzione di supporto della pipeline di input restituisce chiamandola sui dati di addestramento e usa una piccola dimensione batch per mantenere leggibile l'output:

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)

Come dimostra l'output, il set di addestramento restituisce un dizionario di nomi di colonna (da DataFrame) che esegue il mapping ai valori di colonna dalle righe.

Applicare i livelli di preelaborazione Keras

I livelli di preelaborazione Keras consentono di creare pipeline di elaborazione dell'input Keras native, che possono essere utilizzate come codice di preelaborazione indipendente nei flussi di lavoro non Keras, combinate direttamente con i modelli Keras ed esportate come parte di un Keras SavedModel.

In questo tutorial, utilizzerai i seguenti quattro livelli di preelaborazione per dimostrare come eseguire la preelaborazione, la codifica dei dati strutturati e l'ingegneria delle funzionalità:

Puoi saperne di più sui livelli disponibili nella guida Lavorare con i livelli di preelaborazione .

  • Per le funzionalità numeriche del mini set di dati PetFinder.my, utilizzerai un livello tf.keras.layers.Normalization per standardizzare la distribuzione dei dati.
  • Per le caratteristiche categoriali , come pet Type s (stringhe Dog e Cat ), le trasformerai in tensori codificati multi-hot con tf.keras.layers.CategoryEncoding .

Colonne numeriche

Per ogni caratteristica numerica nel mini set di dati PetFinder.my, utilizzerai un livello tf.keras.layers.Normalization per standardizzare la distribuzione dei dati.

Definire una nuova funzione di utilità che restituisce un livello che applica la normalizzazione in base alle caratteristiche alle caratteristiche numeriche utilizzando quel livello di preelaborazione 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

Quindi, testa la nuova funzione chiamandola sulle funzionalità totali delle foto degli animali caricate per normalizzare '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)>

Colonne categoriali

Type di animali domestici nel set di dati sono rappresentati come stringhe, Dog s e Cat s, che devono essere codificate multi-hot prima di essere inserite nel modello. La funzione Age

Definire un'altra nuova funzione di utilità che restituisce un livello che mappa i valori da un vocabolario a indici interi e codifica multi-hot le funzionalità utilizzando la tf.keras.layers.StringLookup , tf.keras.layers.IntegerLookup e tf.keras.CategoryEncoding strati:

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

Testare la funzione get_category_encoding_layer chiamandola sulle funzionalità pet 'Type' per trasformarle in tensori codificati multi-hot:

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

Ripeti il ​​processo sulle caratteristiche 'Age' dell'animale domestico:

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

Preelabora le funzioni selezionate su cui addestrare il modello

Hai imparato come utilizzare diversi tipi di livelli di preelaborazione Keras. Successivamente, dovrai:

  • Applicare le funzioni di utilità di preelaborazione definite in precedenza su 13 funzioni numeriche e categoriali dal mini set di dati PetFinder.my.
  • Aggiungi tutti gli input di funzionalità a un elenco.

Come accennato all'inizio, per addestrare il modello, utilizzerai il mini set di dati PetFinder.my numerico ( 'PhotoAmt' , 'Fee' ) e categoriale ( 'Age' , 'Type' , 'Color1' , 'Color2' , 'Gender' Caratteristiche di 'Gender' , 'MaturitySize' , 'FurLength' , 'Vaccinated' , 'Sterilized' , 'Health' , 'Breed1' ).

In precedenza, hai usato una piccola dimensione batch per dimostrare la pipeline di input. Creiamo ora una nuova pipeline di input con una dimensione batch maggiore di 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.

Normalizza le caratteristiche numeriche (il numero di foto degli animali domestici e la quota di adozione) e aggiungile a un elenco di input chiamato 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)

Trasforma i valori categoriali interi dal set di dati (l'età degli animali domestici) in indici interi, esegui la codifica multi-hot e aggiungi gli input delle funzionalità risultanti 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)

Ripetere lo stesso passaggio per i valori categoriali della stringa:

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)

Crea, compila e addestra il modello

Il passaggio successivo consiste nel creare un modello utilizzando l' API funzionale Keras . Per il primo livello nel tuo modello, unisci l'elenco di input di funzionalità - encoded_features - in un vettore tramite concatenazione 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)

Configura il modello con Keras Model.compile :

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

Visualizziamo il grafico della connettività:

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

png

Quindi, addestra e testa il modello:

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

Eseguire l'inferenza

Il modello che hai sviluppato ora può classificare una riga da un file CSV direttamente dopo aver incluso i livelli di preelaborazione all'interno del modello stesso.

Ora puoi salvare e ricaricare il modello Keras con Model.save e Model.load_model prima di eseguire l'inferenza sui nuovi dati:

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

Per ottenere una previsione per un nuovo campione, puoi semplicemente chiamare il metodo Keras Model.predict . Ci sono solo due cose che devi fare:

  1. Avvolgere gli scalari in un elenco in modo da avere una dimensione batch (il Model elabora solo batch di dati, non singoli campioni).
  2. Chiama tf.convert_to_tensor su ciascuna funzione.
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.

Prossimi passi

Per ulteriori informazioni sulla classificazione dei dati strutturati, prova a lavorare con altri set di dati. Per migliorare la precisione durante l'addestramento e il test dei tuoi modelli, pensa attentamente a quali funzionalità includere nel tuo modello e come dovrebbero essere rappresentate.

Di seguito sono riportati alcuni suggerimenti per i set di dati:

  • Set di dati TensorFlow: MovieLens : un insieme di valutazioni di film da un servizio di consigli sui film.
  • Set di dati TensorFlow: Qualità del vino : due set di dati relativi alle varianti rosse e bianche del vino portoghese "Vinho Verde". Puoi anche trovare il set di dati sulla qualità del vino rosso su Kaggle .
  • Kaggle: arXiv Dataset : un corpus di 1,7 milioni di articoli accademici di arXiv, che coprono fisica, informatica, matematica, statistica, ingegneria elettrica, biologia quantitativa ed economia.