Pomoc chronić Wielkiej Rafy Koralowej z TensorFlow na Kaggle Dołącz Wyzwanie

Regresja podstawowa: przewidywanie zużycia paliwa

Zobacz na TensorFlow.org Uruchom w Google Colab Wyświetl źródło na GitHub Pobierz notatnik

W problemu regresji, celem jest, aby przewidzieć wyjście ciągłe wartości, jak ceny lub prawdopodobieństwem. Porównajmy to z problemem klasyfikacji, w której celem jest, aby wybrać klasę z listy klas (na przykład, gdy obraz zawiera jabłko lub pomarańczę, uznając których owocem jest na zdjęciu).

Ten poradnik wykorzystuje klasyczne Auto MPG zbiór danych i pokazuje, jak budować modele do przewidywania zużycia paliwa w latach 1970 i 1980 samochodów. W tym celu podasz modele z opisem wielu samochodów z tamtego okresu. Ten opis obejmuje takie atrybuty, jak cylindry, pojemność skokowa, moc i waga.

W tym przykładzie zastosowano interfejs API Keras. (Odwiedź Keras samouczki i przewodniki , aby dowiedzieć się więcej).

# 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-rc0

Zestaw danych Auto MPG

Zbiór danych jest dostępny z UCI Machine Learning Repository .

Pobierz dane

Najpierw pobierz i zaimportuj zestaw danych za pomocą pand:

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

Wyczyść dane

Zbiór danych zawiera kilka nieznanych wartości:

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

Upuść te wiersze, aby ten wstępny samouczek był prosty:

dataset = dataset.dropna()

"Origin" kolumna jest kategorycznie nie numeryczne. Więc następnym krokiem jest jedną gorącą zakodowania wartości w kolumnie z 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()

Podziel dane na zestawy treningowe i testowe

Teraz podziel zestaw danych na zestaw uczący i zestaw testowy. Zestaw testowy wykorzystasz w końcowej ocenie swoich modeli.

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

Sprawdź dane

Przejrzyj łączny rozkład kilku par kolumn ze zbioru uczącego.

Górny rząd sugeruje, że efektywność paliwowa (MPG) jest funkcją wszystkich pozostałych parametrów. Pozostałe wiersze wskazują, że są one funkcjami siebie nawzajem.

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

png

Sprawdźmy też ogólne statystyki. Zwróć uwagę, że każda funkcja obejmuje bardzo inny zakres:

train_dataset.describe().transpose()

Podziel funkcje z etykiet

Oddziel wartość docelową — „etykietę” — od funkcji. Ta etykieta jest wartością, której przewidywania będziesz trenował model.

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

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

Normalizacja

W tabeli statystyk łatwo zobaczyć, jak różne są zakresy poszczególnych funkcji:

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

Dobrą praktyką jest normalizowanie funkcji, które używają różnych skal i zakresów.

Jednym z powodów, dla których jest to ważne, jest to, że cechy są mnożone przez wagi modelu. Tak więc skala wyjść i skala gradientów zależy od skali wejść.

Chociaż model może zbiegać bez cech normalizacji, normalizacja czyni trening znacznie bardziej stabilne.

Warstwa normalizacji

tf.keras.layers.Normalization jest czysty i prosty sposób dodać do normalizacji funkcji danego modelu.

Pierwszym krokiem jest stworzenie warstwy:

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

Następnie dopasować stan przerób warstwy do danych poprzez wywołanie Normalization.adapt :

normalizer.adapt(np.array(train_features))

Oblicz średnią i wariancję i zapisz je w warstwie:

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

Gdy warstwa jest wywoływana, zwraca dane wejściowe, przy czym każdy element jest niezależnie znormalizowany:

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

Regresja liniowa

Przed zbudowaniem głębokiego modelu sieci neuronowej zacznij od regresji liniowej z wykorzystaniem jednej i kilku zmiennych.

Regresja liniowa z jedną zmienną

Rozpocząć z jednym zmiennej regresji liniowej do przewidzenia 'MPG' z 'Horsepower' .

Szkolenie model z tf.keras zazwyczaj rozpoczyna się od zdefiniowania modelu architektury. Używać tf.keras.Sequential model, który reprezentuje sekwencję etapów .

Model regresji liniowej z jedną zmienną składa się z dwóch etapów:

  • Normalizacji 'Horsepower' wejście wyposażony pomocą tf.keras.layers.Normalization warstwę przerób.
  • Zastosowanie transformacji liniowej (\(y = mx+b\)) w celu uzyskania sygnału wyjściowego 1 za pomocą warstwy liniowa ( tf.keras.layers.Dense ).

Liczba wejść może być albo ustawiona przez input_shape argumentu, lub automatycznie, gdy model jest uruchamiany po raz pierwszy.

Po pierwsze, należy utworzyć tablicę NumPy wykonany z 'Horsepower' funkcji. Następnie instancję tf.keras.layers.Normalization i dopasować swój stan do horsepower danych:

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

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

Zbuduj model Keras Sequential:

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
_________________________________________________________________

Model ten będzie przewidzieć 'MPG' z 'Horsepower' .

Uruchom nieprzeszkolony model na pierwszych 10 wartościach „Moc”. Wyjście nie będzie dobry, ale zauważ, że to ma oczekiwany kształt (10, 1) :

horsepower_model.predict(horsepower[:10])
array([[ 0.858],
       [ 0.484],
       [-1.583],
       [ 1.202],
       [ 1.087],
       [ 0.427],
       [ 1.288],
       [ 1.087],
       [ 0.283],
       [ 0.484]], dtype=float32)

Gdy model jest zbudowany, skonfigurować procedurę szkolenia używając Keras Model.compile metody. Najważniejszymi argumentami skompilować to loss i optimizer , ponieważ te określają, co będzie zoptymalizowany ( mean_absolute_error ) oraz w jaki sposób (za pomocą tf.keras.optimizers.Adam ).

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

Zastosowanie Keras Model.fit wykonać szkolenia dla 100 epok:

%%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 5.01 s, sys: 930 ms, total: 5.94 s
Wall time: 3.94 s

Wizualizować postęp treningowy modelki używając statystyk zapisane w history obiektu:

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

Zbierz wyniki na zestawie testowym na później:

test_results = {}

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

Ponieważ jest to regresja z pojedynczą zmienną, łatwo jest wyświetlić prognozy modelu jako funkcję danych wejściowych:

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

Regresja liniowa z wieloma danymi wejściowymi

Możesz użyć prawie identycznej konfiguracji, aby przewidywać na podstawie wielu danych wejściowych. Model ten nadal nie takie same \(y = mx+b\) wyjątkiem tego \(m\) jest macierzą i \(b\) jest wektorem.

Tworzenie dwuetapową modelu Keras sekwencyjne ponownie pierwsza warstwa, normalizer ( tf.keras.layers.Normalization(axis=-1) ), które zdefiniowano wcześniej, i przystosowany do całego zestawu danych:

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

Podczas rozmowy telefonicznej Model.predict na partii wejść, produkuje units=1 wyjścia dla każdego przykładu:

linear_model.predict(train_features[:10])
array([[ 0.461],
       [-0.335],
       [-0.567],
       [-0.721],
       [-0.516],
       [-1.158],
       [-0.568],
       [ 0.548],
       [-0.543],
       [ 0.625]], dtype=float32)

Podczas rozmowy telefonicznej modelu, jego macierze wagowe będzie wbudowany check że kernel ciężary (the \(m\) w \(y=mx+b\)) mają kształt (9, 1) :

linear_model.layers[1].kernel
<tf.Variable 'dense_1/kernel:0' shape=(9, 1) dtype=float32, numpy=
array([[-0.768],
       [ 0.107],
       [ 0.677],
       [-0.457],
       [-0.051],
       [-0.45 ],
       [ 0.309],
       [-0.349],
       [-0.409]], dtype=float32)>

Skonfigurować model z Keras Model.compile i pociąg z Model.fit do 100 epok:

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.93 s, sys: 891 ms, total: 5.83 s
Wall time: 3.83 s

Korzystanie z wszystkich wejść w tym modelu regresji osiąga znacznie niższe szkolenia i błąd walidacji niż horsepower_model , który miał jedno wejście:

plot_loss(history)

png

Zbierz wyniki na zestawie testowym na później:

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

Regresja z głęboką siecią neuronową (DNN)

W poprzedniej sekcji zaimplementowano dwa modele liniowe dla jednego i wielu danych wejściowych.

Tutaj zaimplementujesz jednowejściowe i wielowejściowe modele DNN.

Kod jest zasadniczo taki sam, z wyjątkiem tego, że model został rozszerzony o niektóre „ukryte” warstwy nieliniowe. Nazwa „ukryty” oznacza tutaj po prostu niepodłączony bezpośrednio do wejść lub wyjść.

Modele te będą zawierać o kilka więcej warstw niż model liniowy:

  • Warstwę normalizacji, jak poprzednio (w horsepower_normalizer dla modelu pojedynczego wejściowego i normalizer na wzór multiple-input).
  • Dwa ukryte, nieliniowe, Dense warstwy z Relu ( relu ) aktywacja funkcji nieliniowości.
  • Liniowy Dense warstwa jedno-wyjście.

Oba modele będą używać tej samej procedury szkoleniowej więc compile metoda jest wliczone w build_and_compile_model funkcji poniżej.

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

Regresja z użyciem DNN i pojedynczego wejścia

Stworzenie modelu DNN z tylko 'Horsepower' jako wejścia i horsepower_normalizer (zdefiniowanej wcześniej) jako warstwy normalizacji:

dnn_horsepower_model = build_and_compile_model(horsepower_normalizer)

Model ten ma znacznie więcej parametrów, które można trenować niż modele liniowe:

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
_________________________________________________________________

Trenować model z 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.01 s, sys: 900 ms, total: 5.91 s
Wall time: 3.98 s

Model ten ma nieco lepsza niż liniowej pojedynczej wejściowego horsepower_model :

plot_loss(history)

png

Jeśli działka prognoz w funkcji 'Horsepower' , należy zauważyć, jak model ten korzysta z nieliniowości dostarczonych przez ukrytych warstw:

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

png

Zbierz wyniki na zestawie testowym na później:

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

Regresja przy użyciu DNN i wielu danych wejściowych

Powtórz poprzedni proces, używając wszystkich danych wejściowych. Wydajność modelu nieznacznie poprawia się w zestawie danych walidacyjnych.

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.33 s, sys: 912 ms, total: 6.24 s
Wall time: 4.15 s
plot_loss(history)

png

Zbierz wyniki na zestawie testowym:

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

Wydajność

Ponieważ wszystkie modele zostały przeszkolone, możesz przejrzeć ich wydajność zestawu testowego:

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

Wyniki te pokrywają się z błędem walidacji zaobserwowanym podczas treningu.

Prognozować

Można teraz dokonać prognozy z dnn_model na zbiorze testowym używając Keras Model.predict i przeglądu stratę:

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

Wydaje się, że model prognozuje dość dobrze.

Teraz sprawdź rozkład błędów:

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

png

Jeśli jesteś zadowolony z modelu, zapisz ją do późniejszego użytku z Model.save :

dnn_model.save('dnn_model')
2022-01-12 02:41:23.712940: 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

Jeśli przeładujesz model, daje to identyczne dane wyjściowe:

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

Wniosek

W tym notatniku wprowadzono kilka technik radzenia sobie z problemem regresji. Oto kilka dodatkowych wskazówek, które mogą pomóc:

  • Średni kwadrat błędu (MSE) ( tf.losses.MeanSquaredError ) i średni bezwzględny błąd (MAE) ( tf.losses.MeanAbsoluteError ) są powszechne funkcje straty stosowane problemów regresji. MAE jest mniej wrażliwy na wartości odstające. Różne funkcje strat są używane do problemów klasyfikacji.
  • Podobnie metryki oceny używane do regresji różnią się od klasyfikacji.
  • Gdy numeryczne cechy danych wejściowych mają wartości w różnych zakresach, każda cecha powinna być niezależnie skalowana do tego samego zakresu.
  • Overfitting jest powszechnym problemem w modelach DNN, chociaż nie stanowił problemu w tym samouczku. Odwiedź nadmierne dopasowanie i underfit samouczek, aby uzyskać pomoc z tym.
# 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.