Construir um modelo linear com estimadores

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

Visão geral

Este passo a passo completo treina um modelo de regressão logística usando a API tf.estimator . O modelo é frequentemente usado como base para outros algoritmos mais complexos.

Configurar

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

Carregar o conjunto de dados do Titanic

Você usará o conjunto de dados do Titanic com o objetivo (bastante mórbido) de prever a sobrevivência dos passageiros, dadas características como sexo, idade, classe etc.

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

Explorar os dados

O conjunto de dados contém os seguintes recursos

dftrain.head()
dftrain.describe()

Existem 627 e 264 exemplos nos conjuntos de treinamento e avaliação, respectivamente.

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

A maioria dos passageiros tem entre 20 e 30 anos.

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

png

Há aproximadamente o dobro de passageiros do sexo masculino do que do sexo feminino a bordo.

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

png

A maioria dos passageiros estava na "terceira" classe.

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

png

As fêmeas têm uma chance muito maior de sobreviver em relação aos machos. Este é claramente um recurso preditivo para o modelo.

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

png

Engenharia de recursos para o modelo

Os estimadores usam um sistema chamado colunas de recursos para descrever como o modelo deve interpretar cada um dos recursos brutos de entrada. Um estimador espera um vetor de entradas numéricas e as colunas de recursos descrevem como o modelo deve converter cada recurso.

Selecionar e criar o conjunto certo de colunas de recursos é fundamental para aprender um modelo eficaz. Uma coluna de recurso pode ser uma das entradas brutas no dict de recursos original (uma coluna de recurso base ) ou qualquer nova coluna criada usando transformações definidas em uma ou várias colunas de base (colunas de recurso derivadas ).

O estimador linear usa recursos numéricos e categóricos. As colunas de recursos funcionam com todos os estimadores do TensorFlow e sua finalidade é definir os recursos usados ​​para modelagem. Além disso, eles fornecem alguns recursos de engenharia de recursos, como codificação one-hot, normalização e bucketização.

Colunas de recursos básicos

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

A input_function especifica como os dados são convertidos em um tf.data.Dataset que alimenta o pipeline de entrada em um modo de streaming. tf.data.Dataset pode receber várias fontes, como um dataframe, um arquivo formatado em csv e muito mais.

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)

Você pode inspecionar o conjunto de dados:

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]

Você também pode inspecionar o resultado de uma coluna de feição específica usando a camada 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 aceita apenas tensores densos, para inspecionar uma coluna categórica você precisa primeiro transformá-la em uma coluna indicadora:

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)

Depois de adicionar todos os recursos básicos ao modelo, vamos treinar o modelo. Treinar um modelo é apenas um único comando usando a 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}

Colunas de recursos derivados

Agora você atingiu uma precisão de 75%. Usar cada coluna de recurso base separadamente pode não ser suficiente para explicar os dados. Por exemplo, a correlação entre a idade e o rótulo pode ser diferente para diferentes sexos. Portanto, se você aprender apenas um único peso de modelo para gender="Male" e gender="Female" , não capturará todas as combinações de idade e gênero (por exemplo, distinguir entre gender="Male" AND age="30" AND gender="Male" E age="40" ).

Para aprender as diferenças entre diferentes combinações de recursos, você pode adicionar colunas de recursos cruzados ao modelo (você também pode agrupar a coluna de idade antes da coluna cruzada):

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

Depois de adicionar o recurso de combinação ao modelo, vamos treinar o modelo novamente:

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}

Ele agora atinge uma precisão de 77,6%, que é um pouco melhor do que apenas treinado em recursos básicos. Você pode tentar usar mais recursos e transformações para ver se pode fazer melhor!

Agora você pode usar o modelo de trem para fazer previsões sobre um passageiro do conjunto de avaliação. Os modelos do TensorFlow são otimizados para fazer previsões em um lote ou coleção de exemplos de uma só vez. Anteriormente, o eval_input_fn era definido usando todo o conjunto de avaliação.

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

Finalmente, observe a característica operacional do receptor (ROC) dos resultados, o que nos dará uma ideia melhor da compensação entre a taxa de verdadeiros positivos e a taxa de falsos positivos.

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