Construire un modèle linéaire avec des estimateurs

Voir sur TensorFlow.org Exécuter dans Google Colab Voir la source sur GitHub Télécharger le cahier

Aperçu

Cette procédure pas à pas de bout en bout forme un modèle de régression logistique à l'aide de l'API tf.estimator . Le modèle est souvent utilisé comme référence pour d'autres algorithmes plus complexes.

Installer

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

Charger le jeu de données titanesque

Vous utiliserez l'ensemble de données Titanic dans le but (plutôt morbide) de prédire la survie des passagers, compte tenu de caractéristiques telles que le sexe, l'âge, la 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')

Explorer les données

Le jeu de données contient les caractéristiques suivantes

dftrain.head()
dftrain.describe()

Il y a respectivement 627 et 264 exemples dans les ensembles de formation et d'évaluation.

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

La majorité des passagers ont entre 20 et 30 ans.

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

png

Il y a environ deux fois plus de passagers masculins que de passagers féminins à bord.

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

png

La majorité des passagers étaient dans la "troisième" classe.

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

png

Les femmes ont beaucoup plus de chances de survivre que les hommes. Il s'agit clairement d'une caractéristique prédictive du modèle.

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

png

Ingénierie des fonctionnalités pour le modèle

Les estimateurs utilisent un système appelé colonnes de caractéristiques pour décrire comment le modèle doit interpréter chacune des caractéristiques d'entrée brutes. Un estimateur attend un vecteur d'entrées numériques et les colonnes de caractéristiques décrivent comment le modèle doit convertir chaque caractéristique.

La sélection et la création du bon ensemble de colonnes de fonctionnalités sont essentielles pour apprendre un modèle efficace. Une colonne de caractéristiques peut être soit l'une des entrées brutes dans le dict de caractéristiques d'origine (une colonne de caractéristiques de base ), soit toute nouvelle colonne créée à l'aide de transformations définies sur une ou plusieurs colonnes de base (une colonne de caractéristiques dérivées ).

L'estimateur linéaire utilise à la fois des caractéristiques numériques et catégorielles. Les colonnes de caractéristiques fonctionnent avec tous les estimateurs TensorFlow et leur objectif est de définir les caractéristiques utilisées pour la modélisation. De plus, ils fournissent certaines fonctionnalités d'ingénierie de fonctionnalités telles que l'encodage à chaud, la normalisation et la compartimentation.

Colonnes de fonction de 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 spécifie comment les données sont converties en un tf.data.Dataset qui alimente le pipeline d'entrée en continu. tf.data.Dataset peut prendre plusieurs sources telles qu'une trame de données, un fichier au format csv, etc.

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)

Vous pouvez inspecter l'ensemble de données :

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]

Vous pouvez également inspecter le résultat d'une colonne de caractéristiques spécifique à l'aide de la couche 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 n'accepte que les tenseurs denses, pour inspecter une colonne catégorique, vous devez d'abord la transformer en colonne indicatrice :

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)

Après avoir ajouté toutes les fonctionnalités de base au modèle, entraînons le modèle. La formation d'un modèle est une simple commande à l'aide de 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}

Colonnes de caractéristiques dérivées

Vous avez maintenant atteint une précision de 75 %. L'utilisation de chaque colonne de caractéristiques de base séparément peut ne pas suffire à expliquer les données. Par exemple, la corrélation entre l'âge et l'étiquette peut être différente selon le sexe. Par conséquent, si vous n'apprenez qu'un seul poids de modèle pour gender="Male" et gender="Female" , vous ne capturerez pas toutes les combinaisons âge-sexe (par exemple, en distinguant gender="Male" AND age="30" AND gender="Male" ET age="40" ).

Pour connaître les différences entre les différentes combinaisons de fonctionnalités, vous pouvez ajouter des colonnes de fonctionnalités croisées au modèle (vous pouvez également segmenter la colonne d'âge avant la colonne croisée) :

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

Après avoir ajouté la fonctionnalité de combinaison au modèle, entraînons à nouveau le modèle :

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}

Il atteint maintenant une précision de 77,6 %, ce qui est légèrement mieux qu'uniquement entraîné dans les fonctionnalités de base. Vous pouvez essayer d'utiliser plus de fonctionnalités et de transformations pour voir si vous pouvez faire mieux !

Vous pouvez maintenant utiliser le modèle de train pour faire des prédictions sur un passager à partir de l'ensemble d'évaluation. Les modèles TensorFlow sont optimisés pour effectuer des prédictions sur un lot ou une collection d'exemples à la fois. Auparavant, eval_input_fn était défini à l'aide de l'ensemble d'évaluation complet.

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

Enfin, regardez la caractéristique de fonctionnement du récepteur (ROC) des résultats, ce qui nous donnera une meilleure idée du compromis entre le taux de vrais positifs et le taux de faux positifs.

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