La journée communautaire ML est le 9 novembre ! Rejoignez - nous pour les mises à jour de tensorflow, JAX et plus En savoir plus

Régression de base : prédire l'efficacité énergétique

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

Dans un problème de régression, le but est de prédire la sortie d'une valeur constante, comme un prix ou une probabilité. Cela contraste avec un problème de classification, dont le but est de sélectionner une classe à partir d' une liste des classes (par exemple, où une image contient une pomme ou une orange, reconnaissant que fruit est dans l'image).

Ce tutoriel utilise le classique Auto MPG jeu de données et montre comment construire des modèles pour prédire l'efficacité énergétique de la fin des années 1970 et au début des années 1980 les automobiles. Pour ce faire, vous fournirez aux modèles une description de nombreuses automobiles de cette période. Cette description comprend des attributs tels que les cylindres, la cylindrée, la puissance et le poids.

Cet exemple utilise l'API Keras. (Visitez les KERAS didacticiels et guides pour en savoir plus.)

# Use seaborn for pairplot.
pip install -q seaborn
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns

# Make NumPy printouts easier to read.
np.set_printoptions(precision=3, suppress=True)
import tensorflow as tf

from tensorflow import keras
from tensorflow.keras import layers

print(tf.__version__)
2.6.0

Le jeu de données Auto MPG

L'ensemble des données est disponible à partir de l' apprentissage automatique UCI Repository .

Obtenez les données

Commencez par télécharger et importer l'ensemble de données à l'aide de pandas :

url = 'http://archive.ics.uci.edu/ml/machine-learning-databases/auto-mpg/auto-mpg.data'
column_names = ['MPG', 'Cylinders', 'Displacement', 'Horsepower', 'Weight',
                'Acceleration', 'Model Year', 'Origin']

raw_dataset = pd.read_csv(url, names=column_names,
                          na_values='?', comment='\t',
                          sep=' ', skipinitialspace=True)
dataset = raw_dataset.copy()
dataset.tail()

Nettoyer les données

L'ensemble de données contient quelques valeurs inconnues :

dataset.isna().sum()
MPG             0
Cylinders       0
Displacement    0
Horsepower      6
Weight          0
Acceleration    0
Model Year      0
Origin          0
dtype: int64

Supprimez ces lignes pour que ce didacticiel initial reste simple :

dataset = dataset.dropna()

La "Origin" colonne est catégorique, et non numérique. Ainsi , l'étape suivante consiste à coder un chaud les valeurs de la colonne avec pd.get_dummies .

dataset['Origin'] = dataset['Origin'].map({1: 'USA', 2: 'Europe', 3: 'Japan'})
dataset = pd.get_dummies(dataset, columns=['Origin'], prefix='', prefix_sep='')
dataset.tail()

Divisez les données en ensembles d'entraînement et de test

Maintenant, divisez l'ensemble de données en un ensemble d'apprentissage et un ensemble de test. Vous utiliserez l'ensemble de test dans l'évaluation finale de vos modèles.

train_dataset = dataset.sample(frac=0.8, random_state=0)
test_dataset = dataset.drop(train_dataset.index)

Inspecter les données

Examinez la distribution conjointe de quelques paires de colonnes de l'ensemble d'apprentissage.

La rangée du haut suggère que le rendement énergétique (MPG) est fonction de tous les autres paramètres. Les autres rangées indiquent qu'elles sont fonction les unes des autres.

sns.pairplot(train_dataset[['MPG', 'Cylinders', 'Displacement', 'Weight']], diag_kind='kde')
<seaborn.axisgrid.PairGrid at 0x7fb1a5e66bd0>

png

Vérifions également les statistiques globales. Notez que chaque fonctionnalité couvre une gamme très différente :

train_dataset.describe().transpose()

Diviser des entités à partir d'étiquettes

Séparez la valeur cible (le « libellé ») des caractéristiques. Cette étiquette est la valeur que vous entraînerez le modèle à prédire.

train_features = train_dataset.copy()
test_features = test_dataset.copy()

train_labels = train_features.pop('MPG')
test_labels = test_features.pop('MPG')

Normalisation

Dans le tableau des statistiques, il est facile de voir à quel point les plages de chaque fonctionnalité sont différentes :

train_dataset.describe().transpose()[['mean', 'std']]

Il est recommandé de normaliser les fonctionnalités qui utilisent des échelles et des plages différentes.

L'une des raisons pour lesquelles cela est important est que les caractéristiques sont multipliées par les poids du modèle. Ainsi, l'échelle des sorties et l'échelle des gradients sont affectées par l'échelle des entrées.

Bien qu'un modèle pourrait converger sans normalisation des fonctionnalités, la normalisation rend la formation beaucoup plus stable.

La couche de normalisation

Le tf.keras.layers.Normalization est une façon propre et simple d'ajouter la normalisation des fonctionnalités dans votre modèle.

La première étape consiste à créer le calque :

normalizer = tf.keras.layers.Normalization(axis=-1)

Ensuite, adapter l'état de la couche pré - traitement aux données en appelant Normalization.adapt :

normalizer.adapt(np.array(train_features))

Calculez la moyenne et la variance, et stockez-les dans la couche :

print(normalizer.mean.numpy())
[[   5.478  195.318  104.869 2990.252   15.559   75.898    0.178    0.197
     0.624]]

Lorsque la couche est appelée, elle renvoie les données d'entrée, chaque entité étant normalisée indépendamment :

first = np.array(train_features[:1])

with np.printoptions(precision=2, suppress=True):
  print('First example:', first)
  print()
  print('Normalized:', normalizer(first).numpy())
First example: [[   4.    90.    75.  2125.    14.5   74.     0.     0.     1. ]]

Normalized: [[-0.87 -1.01 -0.79 -1.03 -0.38 -0.52 -0.47 -0.5   0.78]]

Régression linéaire

Avant de construire un modèle de réseau de neurones profonds, commencez par une régression linéaire en utilisant une ou plusieurs variables.

Régression linéaire avec une variable

Commencez par une régression linéaire simple variable à prédire 'MPG' de 'Horsepower' .

La formation d' un modèle avec tf.keras commence généralement en définissant l'architecture du modèle. Utilisez un tf.keras.Sequential modèle, ce qui représente une séquence d'étapes .

Votre modèle de régression linéaire à variable unique comporte deux étapes :

  • Normaliser la 'Horsepower' entrée en utilisant la présente tf.keras.layers.Normalization couche pré - traitement.
  • Appliquer une transformation linéaire ($ y = mx + b $) pour produire une sortie à l' aide d' une couche linéaire ( tf.keras.layers.Dense ).

Le nombre d'entrées peut être soit fixée par le input_shape argument ou automatiquement lorsque le modèle est exécuté pour la première fois.

Tout d' abord, créer un tableau NumPy fait des 'Horsepower' - 'Horsepower' caractéristiques. Ensuite, instancier le tf.keras.layers.Normalization et adapter son état aux horsepower données:

horsepower = np.array(train_features['Horsepower'])

horsepower_normalizer = layers.Normalization(input_shape=[1,], axis=None)
horsepower_normalizer.adapt(horsepower)

Construisez le modèle Keras séquentiel :

horsepower_model = tf.keras.Sequential([
    horsepower_normalizer,
    layers.Dense(units=1)
])

horsepower_model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
normalization_1 (Normalizati (None, 1)                 3         
_________________________________________________________________
dense (Dense)                (None, 1)                 2         
=================================================================
Total params: 5
Trainable params: 2
Non-trainable params: 3
_________________________________________________________________

Ce modèle prédira 'MPG' de 'Horsepower' .

Exécutez le modèle non entraîné sur les 10 premières valeurs 'Horsepower'. La sortie ne sera pas bon, mais avis qu'il a la forme attendue de (10, 1) :

horsepower_model.predict(horsepower[:10])
array([[ 1.208],
       [ 0.682],
       [-2.229],
       [ 1.693],
       [ 1.531],
       [ 0.601],
       [ 1.814],
       [ 1.531],
       [ 0.399],
       [ 0.682]], dtype=float32)

Une fois que le modèle est construit, configurer la procédure de formation à l' aide du Keras Model.compile méthode. Les plus importants arguments de compilation sont la loss et l' optimizer , puisque ceux - ci définissent ce qui sera optimisé ( mean_absolute_error ) et comment ( en utilisant le tf.keras.optimizers.Adam ).

horsepower_model.compile(
    optimizer=tf.optimizers.Adam(learning_rate=0.1),
    loss='mean_absolute_error')

Après avoir configuré la formation configurée, Keras Model.fit pour exécuter la formation pour 100 époques:

%%time
history = horsepower_model.fit(
    train_features['Horsepower'],
    train_labels,
    epochs=100,
    # Suppress logging.
    verbose=0,
    # Calculate validation results on 20% of the training data.
    validation_split = 0.2)
CPU times: user 3.97 s, sys: 765 ms, total: 4.74 s
Wall time: 3.02 s

Visualisez la progression de la formation du modèle en utilisant les statistiques stockées dans l' history objet:

hist = pd.DataFrame(history.history)
hist['epoch'] = history.epoch
hist.tail()
def plot_loss(history):
  plt.plot(history.history['loss'], label='loss')
  plt.plot(history.history['val_loss'], label='val_loss')
  plt.ylim([0, 10])
  plt.xlabel('Epoch')
  plt.ylabel('Error [MPG]')
  plt.legend()
  plt.grid(True)
plot_loss(history)

png

Recueillez les résultats sur l'ensemble de test pour plus tard :

test_results = {}

test_results['horsepower_model'] = horsepower_model.evaluate(
    test_features['Horsepower'],
    test_labels, verbose=0)

Comme il s'agit d'une régression à variable unique, il est facile de visualiser les prédictions du modèle en fonction de l'entrée :

x = tf.linspace(0.0, 250, 251)
y = horsepower_model.predict(x)
def plot_horsepower(x, y):
  plt.scatter(train_features['Horsepower'], train_labels, label='Data')
  plt.plot(x, y, color='k', label='Predictions')
  plt.xlabel('Horsepower')
  plt.ylabel('MPG')
  plt.legend()
plot_horsepower(x,y)

png

Régression linéaire avec plusieurs entrées

Vous pouvez utiliser une configuration presque identique pour faire des prédictions basées sur plusieurs entrées. Ce modèle fait toujours la même chose $y = mx+b$ sauf que $m$ est une matrice et $b$ est un vecteur.

Créer un nouveau modèle Keras en deux étapes en série avec la première couche étant normalizer ( tf.keras.layers.Normalization(axis=-1) ) vous avez défini précédemment et adaptés à l'ensemble de jeu de données:

linear_model = tf.keras.Sequential([
    normalizer,
    layers.Dense(units=1)
])

Lorsque vous appelez Model.predict sur un lot d'entrées, il produit des units=1 sorties pour chaque exemple:

linear_model.predict(train_features[:10])
array([[-0.026],
       [ 1.078],
       [-0.645],
       [ 0.771],
       [ 1.044],
       [ 1.031],
       [ 1.247],
       [ 1.5  ],
       [ 0.614],
       [ 0.496]], dtype=float32)

Lorsque vous appelez le modèle, ses matrices de poids seront-retour intégré que les kernel poids (le $ m $ en $ y = mx + b $) ont une forme de (9, 1) :

linear_model.layers[1].kernel
<tf.Variable 'dense_1/kernel:0' shape=(9, 1) dtype=float32, numpy=
array([[ 0.426],
       [-0.703],
       [-0.434],
       [ 0.546],
       [ 0.643],
       [ 0.328],
       [-0.515],
       [-0.215],
       [-0.101]], dtype=float32)>

Configurez le modèle avec Keras Model.compile et train avec Model.fit pour 100 époques:

linear_model.compile(
    optimizer=tf.optimizers.Adam(learning_rate=0.1),
    loss='mean_absolute_error')
%%time
history = linear_model.fit(
    train_features,
    train_labels,
    epochs=100,
    # Suppress logging.
    verbose=0,
    # Calculate validation results on 20% of the training data.
    validation_split = 0.2)
CPU times: user 3.97 s, sys: 641 ms, total: 4.61 s
Wall time: 2.92 s

En utilisant toutes les entrées dans ce modèle de régression atteint une formation beaucoup plus faible et une erreur de validation que le horsepower_model , qui avait une entrée:

plot_loss(history)

png

Recueillez les résultats sur l'ensemble de test pour plus tard :

test_results['linear_model'] = linear_model.evaluate(
    test_features, test_labels, verbose=0)

Régression avec un réseau de neurones profonds (DNN)

Dans la section précédente, vous avez implémenté deux modèles linéaires pour des entrées simples et multiples.

Ici, vous implémenterez des modèles DNN à entrée unique et à entrées multiples.

Le code est fondamentalement le même, sauf que le modèle est étendu pour inclure des couches non linéaires "cachées". Le nom "caché" ici signifie simplement pas directement connecté aux entrées ou sorties.

Ces modèles contiendront quelques couches de plus que le modèle linéaire :

  • La couche de normalisation, comme précédemment (avec horsepower_normalizer pour un modèle à entrée unique et normalizer pour un modèle à entrées multiples).
  • Deux cachées, non linéaires, Dense couches avec la RELU ( relu fonction d'activation) non - linéarité.
  • Une linéaire Dense couche de sortie unique.

Les deux modèles utilisent la même procédure de formation de sorte que la compile méthode est inclus dans la build_and_compile_model fonction ci - dessous.

def build_and_compile_model(norm):
  model = keras.Sequential([
      norm,
      layers.Dense(64, activation='relu'),
      layers.Dense(64, activation='relu'),
      layers.Dense(1)
  ])

  model.compile(loss='mean_absolute_error',
                optimizer=tf.keras.optimizers.Adam(0.001))
  return model

Régression utilisant un DNN et une seule entrée

Créer un modèle DNN avec seulement 'Horsepower' en entrée et horsepower_normalizer (définie plus haut) en tant que couche de normalisation:

dnn_horsepower_model = build_and_compile_model(horsepower_normalizer)

Ce modèle a un peu plus de paramètres entraînables que les modèles linéaires :

dnn_horsepower_model.summary()
Model: "sequential_2"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
normalization_1 (Normalizati (None, 1)                 3         
_________________________________________________________________
dense_2 (Dense)              (None, 64)                128       
_________________________________________________________________
dense_3 (Dense)              (None, 64)                4160      
_________________________________________________________________
dense_4 (Dense)              (None, 1)                 65        
=================================================================
Total params: 4,356
Trainable params: 4,353
Non-trainable params: 3
_________________________________________________________________

Former le modèle avec Keras Model.fit :

%%time
history = dnn_horsepower_model.fit(
    train_features['Horsepower'],
    train_labels,
    validation_split=0.2,
    verbose=0, epochs=100)
CPU times: user 4.38 s, sys: 734 ms, total: 5.12 s
Wall time: 3.37 s

Ce modèle fait un peu mieux que la seule entrée linéaire horsepower_model :

plot_loss(history)

png

Si vous indiquerez les prévisions en fonction de la 'Horsepower' , vous devriez remarquer comment ce modèle profite de la non - linéarité fourni par les couches cachées:

x = tf.linspace(0.0, 250, 251)
y = dnn_horsepower_model.predict(x)
plot_horsepower(x, y)

png

Recueillez les résultats sur l'ensemble de test pour plus tard :

test_results['dnn_horsepower_model'] = dnn_horsepower_model.evaluate(
    test_features['Horsepower'], test_labels,
    verbose=0)

Régression à l'aide d'un DNN et de plusieurs entrées

Répétez le processus précédent en utilisant toutes les entrées. Les performances du modèle s'améliorent légèrement sur l'ensemble de données de validation.

dnn_model = build_and_compile_model(normalizer)
dnn_model.summary()
Model: "sequential_3"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
normalization (Normalization (None, 9)                 19        
_________________________________________________________________
dense_5 (Dense)              (None, 64)                640       
_________________________________________________________________
dense_6 (Dense)              (None, 64)                4160      
_________________________________________________________________
dense_7 (Dense)              (None, 1)                 65        
=================================================================
Total params: 4,884
Trainable params: 4,865
Non-trainable params: 19
_________________________________________________________________
%%time
history = dnn_model.fit(
    train_features,
    train_labels,
    validation_split=0.2,
    verbose=0, epochs=100)
CPU times: user 4.15 s, sys: 711 ms, total: 4.86 s
Wall time: 3.16 s
plot_loss(history)

png

Recueillez les résultats sur l'ensemble de test :

test_results['dnn_model'] = dnn_model.evaluate(test_features, test_labels, verbose=0)

Performance

Étant donné que tous les modèles ont été entraînés, vous pouvez examiner les performances de leur ensemble de tests :

pd.DataFrame(test_results, index=['Mean absolute error [MPG]']).T

Ces résultats correspondent à l'erreur de validation observée lors de l'apprentissage.

Faire des prédictions

Vous pouvez maintenant faire des prédictions avec le dnn_model sur l'ensemble de test à l' aide Keras Model.predict et examiner la perte:

test_predictions = dnn_model.predict(test_features).flatten()

a = plt.axes(aspect='equal')
plt.scatter(test_labels, test_predictions)
plt.xlabel('True Values [MPG]')
plt.ylabel('Predictions [MPG]')
lims = [0, 50]
plt.xlim(lims)
plt.ylim(lims)
_ = plt.plot(lims, lims)

png

Il semble que le modèle prédit assez bien.

Maintenant, vérifiez la distribution des erreurs :

error = test_predictions - test_labels
plt.hist(error, bins=25)
plt.xlabel('Prediction Error [MPG]')
_ = plt.ylabel('Count')

png

Si vous êtes satisfait du modèle, l' enregistrer pour une utilisation ultérieure avec Model.save :

dnn_model.save('dnn_model')
2021-10-13 01:25:59.246092: W tensorflow/python/util/util.cc:348] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them.
INFO:tensorflow:Assets written to: dnn_model/assets

Si vous rechargez le modèle, il donne une sortie identique :

reloaded = tf.keras.models.load_model('dnn_model')

test_results['reloaded'] = reloaded.evaluate(
    test_features, test_labels, verbose=0)
pd.DataFrame(test_results, index=['Mean absolute error [MPG]']).T

Conclusion

Ce cahier a introduit quelques techniques pour gérer un problème de régression. Voici quelques conseils supplémentaires qui peuvent vous aider :

  • Erreur quadratique moyenne (MSE) ( tf.losses.MeanMeanSquaredError ) et l' erreur absolue moyenne (MAE) ( tf.losses.MeanAbsoluteError ) sont des fonctions de perte couramment utilisés pour les problèmes de régression. MAE est moins sensible aux valeurs aberrantes. Différentes fonctions de perte sont utilisées pour les problèmes de classification.
  • De même, les mesures d'évaluation utilisées pour la régression diffèrent de la classification.
  • Lorsque les entités de données d'entrée numériques ont des valeurs avec des plages différentes, chaque entité doit être mise à l'échelle indépendamment à la même plage.
  • Le surapprentissage est un problème courant pour les modèles DNN, même si ce n'était pas un problème pour ce didacticiel. Visitez le surajustement et underfit tutoriel pour plus aider.
# MIT License
#
# Copyright (c) 2017 François Chollet
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.