Costruisci un modello lineare con Estimators

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

Panoramica

Questa procedura dettagliata end-to-end addestra un modello di regressione logistica utilizzando l'API tf.estimator . Il modello viene spesso utilizzato come base per altri algoritmi più complessi.

Impostare

pip install sklearn
import os
import sys

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from IPython.display import clear_output
from six.moves import urllib

Carica il set di dati titanic

Utilizzerai il set di dati Titanic con l'obiettivo (piuttosto morboso) di prevedere la sopravvivenza dei passeggeri, date caratteristiche come sesso, età, classe, ecc.

import tensorflow.compat.v2.feature_column as fc

import tensorflow as tf
# Load dataset.
dftrain = pd.read_csv('https://storage.googleapis.com/tf-datasets/titanic/train.csv')
dfeval = pd.read_csv('https://storage.googleapis.com/tf-datasets/titanic/eval.csv')
y_train = dftrain.pop('survived')
y_eval = dfeval.pop('survived')

Esplora i dati

Il set di dati contiene le seguenti funzionalità

dftrain.head()
dftrain.describe()

Ci sono rispettivamente 627 e 264 esempi nei set di formazione e valutazione.

dftrain.shape[0], dfeval.shape[0]
(627, 264)

La maggior parte dei passeggeri ha tra i 20 ei 30 anni.

dftrain.age.hist(bins=20)
<AxesSubplot:>

png

Ci sono circa il doppio dei passeggeri maschi rispetto alle femmine a bordo.

dftrain.sex.value_counts().plot(kind='barh')
<AxesSubplot:>

png

La maggior parte dei passeggeri era nella "terza" classe.

dftrain['class'].value_counts().plot(kind='barh')
<AxesSubplot:>

png

Le femmine hanno una probabilità molto più alta di sopravvivere rispetto ai maschi. Questa è chiaramente una caratteristica predittiva per il modello.

pd.concat([dftrain, y_train], axis=1).groupby('sex').survived.mean().plot(kind='barh').set_xlabel('% survive')
Text(0.5, 0, '% survive')

png

Ingegneria delle funzionalità per il modello

Gli estimatori utilizzano un sistema chiamato colonne delle caratteristiche per descrivere come il modello dovrebbe interpretare ciascuna delle caratteristiche dell'input grezzo. Uno stimatore si aspetta un vettore di input numerici e le colonne delle caratteristiche descrivono come il modello dovrebbe convertire ciascuna caratteristica.

La selezione e la creazione del giusto set di colonne di funzionalità è la chiave per apprendere un modello efficace. Una colonna di funzionalità può essere uno degli input grezzi nel dict delle funzionalità originali (una colonna di funzionalità di base ) o qualsiasi nuova colonna creata utilizzando trasformazioni definite su una o più colonne di base (una colonna di funzionalità derivata ).

Lo stimatore lineare utilizza caratteristiche sia numeriche che categoriali. Le colonne delle caratteristiche funzionano con tutti gli stimatori TensorFlow e il loro scopo è definire le caratteristiche utilizzate per la modellazione. Inoltre, forniscono alcune funzionalità di ingegneria delle funzionalità come la codifica one-hot, la normalizzazione e la bucketizzazione.

Colonne delle caratteristiche di base

CATEGORICAL_COLUMNS = ['sex', 'n_siblings_spouses', 'parch', 'class', 'deck',
                       'embark_town', 'alone']
NUMERIC_COLUMNS = ['age', 'fare']

feature_columns = []
for feature_name in CATEGORICAL_COLUMNS:
  vocabulary = dftrain[feature_name].unique()
  feature_columns.append(tf.feature_column.categorical_column_with_vocabulary_list(feature_name, vocabulary))

for feature_name in NUMERIC_COLUMNS:
  feature_columns.append(tf.feature_column.numeric_column(feature_name, dtype=tf.float32))

La input_function come i dati vengono convertiti in un tf.data.Dataset che alimenta la pipeline di input in modalità streaming. tf.data.Dataset può contenere più origini come un dataframe, un file in formato CSV e altro.

def make_input_fn(data_df, label_df, num_epochs=10, shuffle=True, batch_size=32):
  def input_function():
    ds = tf.data.Dataset.from_tensor_slices((dict(data_df), label_df))
    if shuffle:
      ds = ds.shuffle(1000)
    ds = ds.batch(batch_size).repeat(num_epochs)
    return ds
  return input_function

train_input_fn = make_input_fn(dftrain, y_train)
eval_input_fn = make_input_fn(dfeval, y_eval, num_epochs=1, shuffle=False)

È possibile esaminare il set di dati:

ds = make_input_fn(dftrain, y_train, batch_size=10)()
for feature_batch, label_batch in ds.take(1):
  print('Some feature keys:', list(feature_batch.keys()))
  print()
  print('A batch of class:', feature_batch['class'].numpy())
  print()
  print('A batch of Labels:', label_batch.numpy())
Some feature keys: ['sex', 'age', 'n_siblings_spouses', 'parch', 'fare', 'class', 'deck', 'embark_town', 'alone']

A batch of class: [b'Third' b'Third' b'Third' b'Third' b'Third' b'First' b'Second' b'First'
 b'First' b'Third']

A batch of Labels: [0 1 1 0 0 1 0 1 1 0]

Puoi anche ispezionare il risultato di una colonna caratteristica specifica usando il livello tf.keras.layers.DenseFeatures :

age_column = feature_columns[7]
tf.keras.layers.DenseFeatures([age_column])(feature_batch).numpy()
array([[35.],
       [14.],
       [28.],
       [19.],
       [28.],
       [35.],
       [60.],
       [63.],
       [45.],
       [21.]], dtype=float32)

DenseFeatures accetta solo tensori densi, per ispezionare una colonna categoriale è necessario prima trasformarla in una colonna indicatore:

gender_column = feature_columns[0]
tf.keras.layers.DenseFeatures([tf.feature_column.indicator_column(gender_column)])(feature_batch).numpy()
array([[1., 0.],
       [0., 1.],
       [0., 1.],
       [1., 0.],
       [1., 0.],
       [1., 0.],
       [1., 0.],
       [0., 1.],
       [0., 1.],
       [0., 1.]], dtype=float32)

Dopo aver aggiunto tutte le caratteristiche di base al modello, addestriamo il modello. L'addestramento di un modello è solo un singolo comando che utilizza l'API tf.estimator :

linear_est = tf.estimator.LinearClassifier(feature_columns=feature_columns)
linear_est.train(train_input_fn)
result = linear_est.evaluate(eval_input_fn)

clear_output()
print(result)
{'accuracy': 0.7537879, 'accuracy_baseline': 0.625, 'auc': 0.8060607, 'auc_precision_recall': 0.7480768, 'average_loss': 0.5639972, 'label/mean': 0.375, 'loss': 0.5542658, 'precision': 0.7741935, 'prediction/mean': 0.25232768, 'recall': 0.4848485, 'global_step': 200}

Colonne di funzioni derivate

Ora hai raggiunto una precisione del 75%. L'uso separato di ciascuna colonna delle caratteristiche di base potrebbe non essere sufficiente per spiegare i dati. Ad esempio, la correlazione tra l'età e l'etichetta può essere diversa a seconda del sesso. Pertanto, se impari un solo modello di peso per gender="Male" e gender="Female" , non catturerai tutte le combinazioni di età-genere (ad esempio distinguendo tra gender="Male" AND age="30" AND gender="Male" AND age="40" ).

Per conoscere le differenze tra le diverse combinazioni di funzionalità, puoi aggiungere colonne di funzionalità incrociate al modello (puoi anche inserire in un bucket la colonna dell'età prima della colonna incrociata):

age_x_gender = tf.feature_column.crossed_column(['age', 'sex'], hash_bucket_size=100)

Dopo aver aggiunto la funzione di combinazione al modello, addestriamo nuovamente il modello:

derived_feature_columns = [age_x_gender]
linear_est = tf.estimator.LinearClassifier(feature_columns=feature_columns+derived_feature_columns)
linear_est.train(train_input_fn)
result = linear_est.evaluate(eval_input_fn)

clear_output()
print(result)
{'accuracy': 0.7462121, 'accuracy_baseline': 0.625, 'auc': 0.845577, 'auc_precision_recall': 0.7873878, 'average_loss': 0.47313985, 'label/mean': 0.375, 'loss': 0.46722567, 'precision': 0.6509434, 'prediction/mean': 0.41550797, 'recall': 0.6969697, 'global_step': 200}

Ora raggiunge una precisione del 77,6%, che è leggermente migliore rispetto alle sole funzioni di base addestrate. Puoi provare a utilizzare più funzioni e trasformazioni per vedere se puoi fare di meglio!

Ora puoi utilizzare il modello del treno per fare previsioni su un passeggero dal set di valutazione. I modelli TensorFlow sono ottimizzati per fare previsioni su un batch o una raccolta di esempi contemporaneamente. In precedenza, eval_input_fn era definito utilizzando l'intero set di valutazione.

pred_dicts = list(linear_est.predict(eval_input_fn))
probs = pd.Series([pred['probabilities'][1] for pred in pred_dicts])

probs.plot(kind='hist', bins=20, title='predicted probabilities')
INFO:tensorflow:Calling model_fn.
INFO:tensorflow:Done calling model_fn.
INFO:tensorflow:Graph was finalized.
INFO:tensorflow:Restoring parameters from /tmp/tmpe5vngw46/model.ckpt-200
INFO:tensorflow:Running local_init_op.
INFO:tensorflow:Done running local_init_op.
<AxesSubplot:title={'center':'predicted probabilities'}, ylabel='Frequency'>

png

Infine, guarda la caratteristica operativa del ricevitore (ROC) dei risultati, che ci darà un'idea migliore del compromesso tra il tasso di vero positivo e il tasso di falso positivo.

from sklearn.metrics import roc_curve
from matplotlib import pyplot as plt

fpr, tpr, _ = roc_curve(y_eval, probs)
plt.plot(fpr, tpr)
plt.title('ROC curve')
plt.xlabel('false positive rate')
plt.ylabel('true positive rate')
plt.xlim(0,)
plt.ylim(0,)
(0.0, 1.05)

png