Basisregression: Kraftstoffeffizienz vorhersagen

Auf TensorFlow.org ansehen In Google Colab ausführen Quelle auf GitHub anzeigen Notizbuch herunterladen

In einem Regressionsproblem ist das Ziel , die Ausgabe eines kontinuierlichen Wert, wie ein Preis oder einer Wahrscheinlichkeit vorherzusagen. Vergleichen Sie dies mit einem Klassifikationsproblem, wo das Ziel ist, eine Klasse aus einer Liste von Klassen zu wählen ist (zum Beispiel, wo ein Bild einen Apfel oder eine Orange enthält, zu erkennen , welche Früchte im Bild).

Dieses Notebook nutzt das klassische Auto MPG Dataset und baut ein Modell , um die Kraftstoffeffizienz von Ende der 1970er Jahre und Anfang der 1980er Jahre Automobile zu prognostizieren. Versehen Sie das Modell dazu mit einer Beschreibung vieler Automobile aus dieser Zeit. Diese Beschreibung enthält Attribute wie: Zylinder, Hubraum, PS und Gewicht.

Dieses Beispiel verwendet die tf.keras API finden Sie dieses Handbuch für Details.

# 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
from tensorflow.keras.layers.experimental import preprocessing

print(tf.__version__)
2.5.0

Der Auto MPG-Datensatz

Der Datensatz wird aus dem verfügbaren UCI Machine Learning Repository .

Holen Sie sich die Daten

Laden Sie zuerst den Datensatz mit Pandas herunter und importieren Sie ihn:

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

Bereinigen Sie die Daten

Der Datensatz enthält einige unbekannte Werte.

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

Löschen Sie diese Zeilen, um dieses anfängliche Tutorial einfach zu halten.

dataset = dataset.dropna()

Die "Origin" Spalte ist wirklich kategorisch, nicht numerisch. So konvertiert , die zu einem one-hot mit 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()

Teilen Sie die Daten in Train and Test auf

Teilen Sie nun den Datensatz in einen Trainingssatz und einen Testsatz auf.

Nutzen Sie das Test-Set bei der abschließenden Bewertung Ihrer Modelle.

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

Überprüfen Sie die Daten

Schauen Sie sich kurz die gemeinsame Verteilung einiger Säulenpaare aus dem Trainingsset an.

Betrachtet man die obere Reihe, sollte klar sein, dass die Kraftstoffeffizienz (MPG) eine Funktion aller anderen Parameter ist. Wenn man sich die anderen Zeilen ansieht, sollte klar sein, dass sie Funktionen voneinander sind.

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

png

Sehen Sie sich auch die Gesamtstatistiken an und beachten Sie, dass jede Funktion einen sehr unterschiedlichen Bereich abdeckt:

train_dataset.describe().transpose()

Trennen von Features von Labels

Trennen Sie den Zielwert, das "Label", von den Merkmalen. Dieses Label ist der Wert, den Sie dem Modell beibringen, um es vorherzusagen.

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

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

Normalisierung

In der Statistiktabelle ist leicht zu erkennen, wie unterschiedlich die Bereiche jedes Merkmals sind.

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

Es empfiehlt sich, Features zu normalisieren, die unterschiedliche Maßstäbe und Bereiche verwenden.

Dies ist unter anderem deshalb wichtig, weil die Features mit den Modellgewichtungen multipliziert werden. Die Skalierung der Ausgaben und die Skalierung der Gradienten werden also durch die Skalierung der Eingaben beeinflusst.

Obwohl ein Modell ohne Funktion Normalisierung konvergieren könnte, macht das Training Normalisierung wesentlich stabiler.

Die Normalisierungsebene

Die preprocessing.Normalization Schicht ist eine saubere und einfache Art und Weise zu bauen , dass in Ihr Modell Vorverarbeitung.

Der erste Schritt besteht darin, die Ebene zu erstellen:

normalizer = preprocessing.Normalization(axis=-1)

Dann .adapt() es auf die Daten:

normalizer.adapt(np.array(train_features))

Dadurch werden Mittelwert und Varianz berechnet und im Layer gespeichert.

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

Wenn der Layer aufgerufen wird, gibt er die Eingabedaten zurück, wobei jedes Feature unabhängig normalisiert wird:

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

Lineare Regression

Bevor Sie ein DNN-Modell erstellen, beginnen Sie mit einer linearen Regression.

Eine Variable

Beginnen Sie mit einer einzigen variablen linearen Regression zur Vorhersage MPG von Horsepower .

Ein Modell mit dem Training tf.keras beginnt in der Regel durch die Modellarchitektur definieren.

In diesem Fall verwenden Sie ein keras.Sequential Modell. Dieses Modell stellt eine Abfolge von Schritten dar. In diesem Fall gibt es zwei Schritte:

  • Normalisieren der horsepower .
  • Anwenden einer linearen Transformation ($ y = mx + b $) , um 1 - Ausgang mit layers.Dense .

Die Anzahl der Eingänge kann entweder durch das eingestellt wird input_shape Argument, oder automatisch , wenn das Modell zum ersten Mal ausgeführt wird.

Erstellen Sie zunächst die Leistung Normalization

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

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

Erstellen Sie das sequentielle Modell:

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
_________________________________________________________________

Dieses Modell wird vorhersagen MPG von Horsepower .

Führen Sie das ungeübte Modell mit den ersten 10 PS-Werten aus. Der Ausgang wird nicht gut sein, aber Sie werden sehen , dass es die erwartete Form hat, (10,1) :

horsepower_model.predict(horsepower[:10])
array([[-0.489],
       [-0.276],
       [ 0.902],
       [-0.685],
       [-0.62 ],
       [-0.243],
       [-0.734],
       [-0.62 ],
       [-0.162],
       [-0.276]], dtype=float32)

Sobald das Modell gebaut wird, konfigurieren Sie die Trainingsprozedur die Verwendung Model.compile() Methode. Die wichtigsten Argumente der Kompilierung sind die loss und der optimizer , da diese zu definieren , was (optimiert werden mean_absolute_error ) und wie (die Verwendung von optimizers.Adam ).

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

Sobald die Ausbildung so konfiguriert ist, verwenden Sie Model.fit() die Ausbildung auszuführen:

%%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.51 s, sys: 682 ms, total: 4.2 s
Wall time: 2.66 s

Visualisieren Sie die Trainingsfortschritt des Modells unter Verwendung der in der gespeicherten Statistiken history Objekt.

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

Sammeln Sie die Ergebnisse auf dem Testset für später:

test_results = {}

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

Da es sich um eine Regression mit einer einzelnen Variablen handelt, ist es einfach, die Vorhersagen des Modells als Funktion der Eingabe zu betrachten:

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

Mehrere Eingänge

Sie können ein fast identisches Setup verwenden, um Vorhersagen basierend auf mehreren Eingaben zu treffen. Dieses Modell macht immer noch das gleiche $y = mx+b$, außer dass $m$ eine Matrix und $b$ ein Vektor ist.

Dieses Mal verwenden , um die Normalization die auf den gesamten Datensatz angepasst wurden.

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

Wenn Sie dieses Modell auf einer Charge von Eingaben aufrufen, erzeugt es units=1 Ausgänge für jedes Beispiel.

linear_model.predict(train_features[:10])
array([[-0.354],
       [ 0.183],
       [-4.128],
       [ 0.718],
       [ 3.96 ],
       [-0.498],
       [ 4.393],
       [ 4.577],
       [-1.01 ],
       [ 3.099]], dtype=float32)

Wenn Sie das Modell aufrufen, werden seine Gewichtsmatrizen erstellt. Jetzt sehen Sie , dass der kernel (der $ m $ in $ y = mx + b $) eine Form von (9,1) .

linear_model.layers[1].kernel
<tf.Variable 'dense_1/kernel:0' shape=(9, 1) dtype=float32, numpy=
array([[ 0.055],
       [-0.419],
       [-0.369],
       [-0.55 ],
       [ 0.758],
       [ 0.441],
       [ 0.721],
       [ 0.551],
       [-0.591]], dtype=float32)>

Verwenden Sie die gleiche compile und fit Anrufe wie für die einzelnen Eingangs horsepower Modell:

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.53 s, sys: 610 ms, total: 4.14 s
Wall time: 2.62 s

Mit allen Eingaben erreicht eine viel geringere Ausbildung und Validierungsfehler als das horsepower - Modell:

plot_loss(history)

png

Sammeln Sie die Ergebnisse auf dem Testset für später:

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

Eine DNN-Regression

Im vorherigen Abschnitt wurden lineare Modelle für einzelne und mehrere Eingaben implementiert.

Dieser Abschnitt implementiert Single-Input- und Multi-Input-DNN-Modelle. Der Code ist im Grunde der gleiche, außer dass das Modell um einige "versteckte" nichtlineare Schichten erweitert wurde. Der Name "versteckt" bedeutet hier nur nicht direkt mit den Ein- oder Ausgängen verbunden.

Diese Modelle enthalten einige Ebenen mehr als das lineare Modell:

  • Die Normalisierungsschicht.
  • Zwei versteckte, nicht - lineare, Dense Schichten mit der relu Nicht - Linearität.
  • Eine lineare Single-Output-Schicht.

Beide werden die gleiche Trainingsprozedur verwenden , um die compile - Methode in der enthalten ist build_and_compile_model Funktion unten.

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

Eine Variable

Beginnen Sie mit einem DNN-Modell für eine einzelne Eingabe: "Horsepower"

dnn_horsepower_model = build_and_compile_model(horsepower_normalizer)

Dieses Modell hat einige mehr trainierbare Parameter als die linearen Modelle.

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
_________________________________________________________________

Trainieren Sie das Modell:

%%time
history = dnn_horsepower_model.fit(
    train_features['Horsepower'], train_labels,
    validation_split=0.2,
    verbose=0, epochs=100)
CPU times: user 3.77 s, sys: 643 ms, total: 4.42 s
Wall time: 2.86 s

Dieses Modell schneidet etwas besser ab als das lineare PS-Modell.

plot_loss(history)

png

Wenn Sie die Vorhersagen als Funktion der plotten Horsepower , Horsepower , werden Sie sehen , wie dieses Modell Vorteil der durch die verborgenen Schichten versehen Nicht - Linearität nimmt:

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

png

Sammeln Sie die Ergebnisse auf dem Testset für später:

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

Vollständiges Modell

Wenn Sie diesen Vorgang mit allen Eingaben wiederholen, wird die Leistung des Validierungs-Datasets geringfügig verbessert.

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 3.67 s, sys: 727 ms, total: 4.39 s
Wall time: 2.85 s
plot_loss(history)

png

Sammeln Sie die Ergebnisse auf dem Testset:

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

Leistung

Nachdem nun alle Modelle trainiert sind, überprüfen Sie die Leistung des Testsatzes und sehen Sie, wie sie abgeschnitten haben:

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

Diese Ergebnisse stimmen mit dem während des Trainings aufgetretenen Validierungsfehler überein.

Voraussagen machen

Sehen Sie sich zum Schluss die Fehler an, die das Modell bei Vorhersagen für die Testreihe gemacht hat:

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

Es sieht so aus, als ob das Modell ziemlich gut vorhersagt.

Schauen Sie sich nun die Fehlerverteilung an:

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

png

Wenn Sie mit dem Modell zufrieden sind, speichern Sie es für die spätere Verwendung:

dnn_model.save('dnn_model')
INFO:tensorflow:Assets written to: dnn_model/assets

Wenn Sie das Modell neu laden, gibt es eine identische Ausgabe:

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

Abschluss

In diesem Notebook wurden einige Techniken zur Behandlung eines Regressionsproblems vorgestellt. Hier sind noch ein paar Tipps, die helfen können:

  • Mittlere quadratische Fehler (MSE) und mittlere absolute Fehler (MAE) sind häufig Verlustfunktionen für Regressionsprobleme. Der mittlere absolute Fehler reagiert weniger empfindlich auf Ausreißer. Für Klassifikationsprobleme werden verschiedene Verlustfunktionen verwendet.
  • Ebenso unterscheiden sich Bewertungsmetriken, die für die Regression verwendet werden, von der Klassifizierung.
  • Wenn numerische Eingabedaten-Features Werte mit unterschiedlichen Bereichen aufweisen, sollte jedes Feature unabhängig auf denselben Bereich skaliert werden.
  • Overfitting ist ein häufiges Problem für DNN-Modelle, es war kein Problem für dieses Tutorial. Siehe die Überanpassung und Unterbau Tutorial für weitere Hilfe mit diesem.
# 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.