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 continue, comme un prix ou une probabilité. Comparez cela avec un problème de classification , où le but est de sélectionner une classe dans une liste de classes (par exemple, lorsqu'une image contient une pomme ou une orange, en reconnaissant quel fruit est dans l'image).
Ce didacticiel utilise l'ensemble de données Auto MPG classique et montre comment créer des modèles pour prédire l'efficacité énergétique des automobiles de la fin des années 1970 et du début des années 1980. Pour ce faire, vous fournirez aux modèles une description de nombreuses automobiles de cette période. Cette description inclut des attributs tels que les cylindres, la cylindrée, la puissance et le poids.
Cet exemple utilise l'API Keras. (Visitez les didacticiels et guides Keras 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.8.0-rc1
Le jeu de données Auto MPG
L'ensemble de données est disponible dans le référentiel d'apprentissage automatique de l'UCI .
Obtenir 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 colonne "Origin"
est catégorique et non numérique. L'étape suivante consiste donc à encoder à 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()
Diviser les données en ensembles d'apprentissage 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)
Inspectez 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 l'efficacité énergétique (MPG) est fonction de tous les autres paramètres. Les autres rangées indiquent qu'elles sont fonction l'une de l'autre.
sns.pairplot(train_dataset[['MPG', 'Cylinders', 'Displacement', 'Weight']], diag_kind='kde')
<seaborn.axisgrid.PairGrid at 0x7f6bfdae9850>
Vérifions également les statistiques globales. Notez que chaque fonctionnalité couvre une plage très différente :
train_dataset.describe().transpose()
Séparer les entités des é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 entités qui utilisent différentes échelles et plages.
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 puisse converger sans normalisation des caractéristiques, la normalisation rend la formation beaucoup plus stable.
La couche de normalisation
Le tf.keras.layers.Normalization
est un moyen 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, adaptez l'état de la couche de 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 neuronal profond, commencez par une régression linéaire en utilisant une et plusieurs variables.
Régression linéaire à une variable
Commencez par une régression linéaire à variable unique pour prédire 'MPG'
à partir de 'Horsepower'
.
La formation d'un modèle avec tf.keras
commence généralement par la définition de l'architecture du modèle. Utilisez un modèle tf.keras.Sequential
, qui représente une séquence d'étapes .
Il y a deux étapes dans votre modèle de régression linéaire à variable unique :
- Normalisez les caractéristiques d'entrée
'Horsepower'
à l'aide de la couche de prétraitementtf.keras.layers.Normalization
. - Appliquez une transformation linéaire (\(y = mx+b\)) pour produire 1 sortie à l'aide d'un calque linéaire (
tf.keras.layers.Dense
).
Le nombre d' entrées peut être défini soit par l'argument input_shape
, soit automatiquement lorsque le modèle est exécuté pour la première fois.
Tout d'abord, créez un tableau NumPy composé des fonctionnalités 'Horsepower'
. Ensuite, instanciez tf.keras.layers.Normalization
et adaptez son état aux données de horsepower
:
horsepower = np.array(train_features['Horsepower'])
horsepower_normalizer = layers.Normalization(input_shape=[1,], axis=None)
horsepower_normalizer.adapt(horsepower)
Construisez le modèle séquentiel Keras :
horsepower_model = tf.keras.Sequential([
horsepower_normalizer,
layers.Dense(units=1)
])
horsepower_model.summary()
Model: "sequential" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= normalization_1 (Normalizat (None, 1) 3 ion) dense (Dense) (None, 1) 2 ================================================================= Total params: 5 Trainable params: 2 Non-trainable params: 3 _________________________________________________________________
Ce modèle prédira 'MPG'
à partir de 'Horsepower'
.
Exécutez le modèle non formé sur les 10 premières valeurs de "puissance". La sortie ne sera pas bonne, mais notez qu'elle a la forme attendue de (10, 1)
:
horsepower_model.predict(horsepower[:10])
array([[-1.186], [-0.67 ], [ 2.189], [-1.662], [-1.504], [-0.59 ], [-1.782], [-1.504], [-0.392], [-0.67 ]], dtype=float32)
Une fois le modèle construit, configurez la procédure d'entraînement à l'aide de la méthode Keras Model.compile
. Les arguments les plus importants à compiler sont le loss
et l' optimizer
, car 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')
Utilisez Keras Model.fit
pour exécuter la formation pendant 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 4.79 s, sys: 797 ms, total: 5.59 s Wall time: 3.8 s
Visualisez la progression de l'entraînement du modèle à l'aide des statistiques stockées dans l'objet d' history
:
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)
Collectez les résultats sur le jeu de test pour plus tard :
test_results = {}
test_results['horsepower_model'] = horsepower_model.evaluate(
test_features['Horsepower'],
test_labels, verbose=0)
Puisqu'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)
Régression linéaire avec plusieurs entrées
Vous pouvez utiliser une configuration presque identique pour effectuer des prédictions basées sur plusieurs entrées. Ce modèle fait toujours le même \(y = mx+b\) sauf que \(m\) est une matrice et \(b\) est un vecteur.
Créez à nouveau un modèle séquentiel Keras en deux étapes avec la première couche étant le normalizer
( tf.keras.layers.Normalization(axis=-1)
) que vous avez défini précédemment et adapté à l'ensemble 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 sorties units=1
pour chaque exemple :
linear_model.predict(train_features[:10])
array([[ 0.441], [ 1.522], [ 0.188], [ 1.169], [ 0.058], [ 0.965], [ 0.034], [-0.674], [ 0.437], [-0.37 ]], dtype=float32)
Lorsque vous appelez le modèle, ses matrices de poids seront construites - vérifiez que les poids du kernel
(le \(m\) dans \(y=mx+b\)) ont la forme (9, 1)
:
linear_model.layers[1].kernel
<tf.Variable 'dense_1/kernel:0' shape=(9, 1) dtype=float32, numpy= array([[-0.702], [ 0.307], [ 0.114], [ 0.233], [ 0.244], [ 0.322], [-0.725], [-0.151], [ 0.407]], dtype=float32)>
Configurez le modèle avec Keras Model.compile
et entraînez-vous 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 4.89 s, sys: 740 ms, total: 5.63 s Wall time: 3.75 s
L'utilisation de toutes les entrées de ce modèle de régression permet d'obtenir une erreur de formation et de validation beaucoup plus faible que le modèle horsepower_model
, qui avait une entrée :
plot_loss(history)
Collectez les résultats sur le jeu 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 profond (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 qu'il n'est pas directement connecté aux entrées ou aux sorties.
Ces modèles contiendront quelques couches de plus que le modèle linéaire :
- La couche de normalisation, comme avant (avec
horsepower_normalizer
pour un modèle à entrée unique etnormalizer
pour un modèle à entrées multiples). - Deux couches cachées, non linéaires et
Dense
avec la non-linéarité de la fonction d'activation ReLU (relu
). - Une couche
Dense
linéaire à sortie unique.
Les deux modèles utiliseront la même procédure de formation, de sorte que la méthode de compile
est incluse dans la fonction build_and_compile_model
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éez un modèle DNN avec uniquement 'Horsepower'
comme entrée et horsepower_normalizer
(défini précédemment) comme 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 (Normalizat (None, 1) 3 ion) 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 _________________________________________________________________
Entraînez 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 5.07 s, sys: 691 ms, total: 5.76 s Wall time: 3.92 s
Ce modèle fait légèrement mieux que le modèle linéaire à entrée unique horsepower_model
:
plot_loss(history)
Si vous tracez les prédictions en fonction de 'Horsepower'
, vous devriez remarquer comment ce modèle tire parti de la non-linéarité fournie par les couches cachées :
x = tf.linspace(0.0, 250, 251)
y = dnn_horsepower_model.predict(x)
plot_horsepower(x, y)
Collectez les résultats sur le jeu de test pour plus tard :
test_results['dnn_horsepower_model'] = dnn_horsepower_model.evaluate(
test_features['Horsepower'], test_labels,
verbose=0)
Régression utilisant un DNN et 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 (Normalizatio (None, 9) 19 n) 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 5.08 s, sys: 725 ms, total: 5.8 s Wall time: 3.94 s
plot_loss(history)
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 leurs ensembles de test :
pd.DataFrame(test_results, index=['Mean absolute error [MPG]']).T
Ces résultats correspondent à l'erreur de validation observée lors de la formation.
Faire des prédictions
Vous pouvez maintenant faire des prédictions avec le dnn_model
sur l'ensemble de test à l'aide de 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)
Il semble que le modèle prédit raisonnablement 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')
Si vous êtes satisfait du modèle, enregistrez-le pour une utilisation ultérieure avec Model.save
:
dnn_model.save('dnn_model')
2022-01-26 07:26:13.372245: 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. 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 :
- L'erreur quadratique moyenne (MSE) (
tf.losses.MeanSquaredError
) et l'erreur absolue moyenne (MAE) (tf.losses.MeanAbsoluteError
) sont des fonctions de perte courantes utilisées 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 dans la même plage.
- Le surajustement est un problème courant pour les modèles DNN, même si ce n'était pas un problème pour ce didacticiel. Consultez le didacticiel de surajustement et de sous-ajustement pour obtenir de l'aide à ce sujet.
# 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.