לשמור את התאריך! קלט / פלט של Google חוזר 18-20 במאי הירשם עכשיו
דף זה תורגם על ידי Cloud Translation API.
Switch to English

רגרסיה בסיסית: ניבוי יעילות הדלק

צפה ב- TensorFlow.org הפעל בגוגל קולאב צפה במקור ב- GitHub הורד מחברת

בבעיית רגרסיה , המטרה היא לחזות תפוקה של ערך רציף, כמו מחיר או הסתברות. לעומת זאת עם בעיית סיווג , כאשר המטרה היא לבחור מחלקה מרשימת הכיתות (למשל, כאשר תמונה מכילה תפוח או תפוז, תוך זיהוי איזה פרי נמצא בתמונה).

מחברת זו משתמשת במערך הנתונים Auto MPG הקלאסי ובונה מודל לחיזוי יעילות הדלק של מכוניות בסוף שנות השבעים ותחילת שנות השמונים. לשם כך, ספק לדגם תיאור של מכוניות רבות מאותה תקופת זמן. תיאור זה כולל תכונות כמו: צילינדרים, תזוזה, כוחות סוס ומשקל.

דוגמה זו משתמשת בממשק ה- API של tf.keras , עיין במדריך זה לפרטים.

# 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.4.1

מערך ה- MPG האוטומטי

מערך הנתונים זמין ממאגר למידה למידה של UCI .

קבל את הנתונים

הורד וייבא תחילה את מערך הנתונים באמצעות פנדות:

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

נקה את הנתונים

מערך הנתונים מכיל כמה ערכים לא ידועים.

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

זרוק את השורות האלה כדי להשאיר את המדריך הראשוני הזה פשוט.

dataset = dataset.dropna()

העמודה "Origin" היא באמת קטגורית, ולא מספרית. אז המירו את זה לחם חם עם 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()

פצל את הנתונים לרכבת ולבדוק

כעת פצל את מערך הנתונים למערך אימונים וערכת מבחנים.

השתמש בערכת המבחנים בהערכה הסופית של המודלים שלנו.

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

בדוק את הנתונים

הסתכלו במהירות על ההפצה המשותפת של כמה זוגות עמודים מתוך מערך האימונים.

כשמסתכלים על השורה העליונה צריך להיות ברור שיעילות הדלק (MPG) היא פונקציה של כל שאר הפרמטרים. כשמסתכלים על השורות האחרות צריך להיות ברור שהן פונקציות זו של זו.

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

png

בדוק גם את הסטטיסטיקה הכוללת, שים לב כיצד כל תכונה מכסה טווח שונה מאוד:

train_dataset.describe().transpose()

פיצול תכונות מתוויות

הפרד בין ערך היעד, "התווית", לתכונות. תווית זו היא הערך שתאמן את המודל לחזות.

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

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

נוֹרמָלִיזָצִיָה

בטבלת הסטטיסטיקה קל לראות עד כמה הטווחים של כל תכונה שונים.

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

זה נוהג טוב לנרמל תכונות המשתמשות בסולם ובטווחים שונים.

אחת הסיבות לכך חשובה היא מכיוון שהתכונות מוכפלות במשקלי הדגם. כך שקנה ​​המידה של התפוקות וקנה המידה של הדרגתיות מושפעים מקנה המידה של הקלטים.

למרות שמודל עשוי להתכנס ללא נורמליזציה של תכונות, נורמליזציה הופכת את האימונים ליציבים הרבה יותר.

שכבת הנורמליזציה

שכבת preprocessing.Normalization היא דרך נקייה ופשוטה לבנות את העיבוד המוקדם הזה למודל שלך.

הצעד הראשון הוא ליצור את השכבה:

normalizer = preprocessing.Normalization()

ואז .adapt() זה לנתונים:

normalizer.adapt(np.array(train_features))

פעולה זו מחשבת את הממוצע והשונות ומאחסנת אותם בשכבה.

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

כאשר קוראים לשכבה היא מחזירה את נתוני הקלט, כאשר כל תכונה מנורמלת באופן עצמאי:

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

רגרסיה לינארית

לפני בניית מודל DNN, התחל ברגרסיה ליניארית.

משתנה אחד

התחל עם רגרסיה ליניארית משתנה אחת, כדי לחזות MPG מכוח Horsepower .

אימון מודל עם tf.keras מתחיל בדרך כלל בהגדרת ארכיטקטורת המודל.

במקרה זה השתמש במודל keras.Sequential . מודל זה מייצג רצף של צעדים. במקרה זה ישנם שני שלבים:

  • נרמל את horsepower הקלט.
  • החל טרנספורמציה לינארית ($ y = mx + b $) כדי לייצר פלט אחד באמצעות layers.Dense .

מספר כניסות יכולות להיות מוגדר על ידי input_shape הטיעון, או באופן אוטומטי כאשר המודל מנוהל בפעם הראשונה.

ראשית צור את שכבת Normalization כוח סוס:

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

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

בנה את המודל הרציף:

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
_________________________________________________________________

מודל זה ינבא MPG מכוח Horsepower .

הפעל את המודל הלא מאומן על 10 ערכי כוח הסוס הראשונים. הפלט לא יהיה טוב, אבל תראה שיש לו את הצורה הצפויה, (10,1) :

horsepower_model.predict(horsepower[:10])
array([[ 0.283],
       [ 0.16 ],
       [-0.523],
       [ 0.397],
       [ 0.359],
       [ 0.141],
       [ 0.426],
       [ 0.359],
       [ 0.094],
       [ 0.16 ]], dtype=float32)

לאחר בניית המודל, הגדר את הליך האימון בשיטת Model.compile() . הטיעונים החשובים ביותר כדי ללקט הם loss ואת optimizer מאז אלה מגדירים מה יהיה מותאם ( mean_absolute_error ) ואיך (באמצעות optimizers.Adam ).

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

לאחר הגדרת האימון, השתמש ב- Model.fit() לביצוע האימון:

%%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 6.89 s, sys: 859 ms, total: 7.75 s
Wall time: 6.1 s

דמיין את התקדמות האימון של המודל באמצעות הסטטיסטיקה המאוחסנת באובייקט 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)

png

אסוף את התוצאות על ערכת הבדיקה, להמשך:

test_results = {}

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

מכיוון שמדובר ברגרסיה משתנה אחת, קל להסתכל על תחזיות המודל כפונקציה של הקלט:

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

מספר כניסות

אתה יכול להשתמש בהתקנה כמעט זהה כדי לחזות על סמך מספר תשומות. מודל זה עדיין עושה את אותו $ y = mx + b $ אלא ש $ m $ הוא מטריצה ​​ו- $ b $ הוא וקטור.

הפעם השתמש בשכבת Normalization שהותאמה לכל מערך הנתונים.

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

כאשר אתה קורא למודל זה על קבוצה של תשומות, הוא מייצר units=1 יציאות עבור כל דוגמה.

linear_model.predict(train_features[:10])
array([[-0.757],
       [-0.652],
       [ 2.807],
       [-0.595],
       [-1.475],
       [ 0.226],
       [-1.833],
       [-3.532],
       [ 0.067],
       [-1.706]], dtype=float32)

כשאתה קורא למודל, ייבנו מטריצות משקל. עכשיו אתה יכול לראות kernel ($ m $ ב- $ y = mx + b $) יש צורה של (9,1) .

linear_model.layers[1].kernel
<tf.Variable 'dense_1/kernel:0' shape=(9, 1) dtype=float32, numpy=
array([[ 0.097],
       [ 0.728],
       [ 0.241],
       [ 0.281],
       [-0.693],
       [ 0.084],
       [-0.536],
       [ 0.168],
       [ 0.2  ]], dtype=float32)>

השתמש באותן שיחות compile fit כמו למודל horsepower קלט יחיד:

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 6.78 s, sys: 731 ms, total: 7.51 s
Wall time: 5.88 s

השימוש בכל הקלטים משיג שגיאת אימון ואימות נמוכה בהרבה ממודל horsepower :

plot_loss(history)

png

אסוף את התוצאות על ערכת הבדיקה, להמשך:

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

רגרסיה של DNN

החלק הקודם הטמיע מודלים לינאריים עבור כניסות בודדות ומרובות.

סעיף זה מיישם מודלים DNN עם קלט יחיד ומרוב קלט. הקוד הוא בעצם זהה למעט שהמודל מורחב כדי לכלול כמה שכבות "סמויות" לא ליניאריות. פירוש השם "מוסתר" כאן אינו מחובר ישירות לכניסות או ליציאות.

מודלים אלה יכילו כמה שכבות יותר מהמודל הליניארי:

  • שכבת הנורמליזציה.
  • שתי שכבות נסתרות, לא לינאריות, Dense תוך שימוש relu ליניאריות relu .
  • שכבת תפוקה אחת ליניארית.

שניהם ישתמשו באותו הליך הכשרה כך compile השיטה נכללת build_and_compile_model הפונקציה להלן.

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

משתנה אחד

התחל עם מודל DNN עבור קלט יחיד: "כוח סוס"

dnn_horsepower_model = build_and_compile_model(horsepower_normalizer)

למודל זה יש לא מעט פרמטרים ניתנים להכשרה מאשר המודלים הליניאריים.

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
_________________________________________________________________

לאמן את הדגם:

%%time
history = dnn_horsepower_model.fit(
    train_features['Horsepower'], train_labels,
    validation_split=0.2,
    verbose=0, epochs=100)
CPU times: user 7.01 s, sys: 790 ms, total: 7.8 s
Wall time: 6.12 s

דגם זה עושה מעט טוב יותר מדגם הספק ליניארי.

plot_loss(history)

png

אם תשרטט את התחזיות כפונקציה של Horsepower , תראה כיצד מודל זה מנצל את האי-ליניאריות שמספקות השכבות הנסתרות:

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

png

אסוף את התוצאות על ערכת הבדיקה, להמשך:

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

דגם מלא

אם אתה חוזר על תהליך זה באמצעות כל הקלטים זה משפר מעט את הביצועים במערך האימות.

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 7.39 s, sys: 624 ms, total: 8.01 s
Wall time: 6.31 s
plot_loss(history)

png

אסוף את התוצאות על ערכת הבדיקה:

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

ביצועים

כעת, כאשר כל הדגמים מאומנים, בדקו את ביצועי ערכת הבדיקה וראו כיצד הם הצליחו:

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

תוצאות אלו תואמות את שגיאת האימות שנראתה במהלך האימון.

לעשות תחזיות

לסיום, חזהו הסתכלו על השגיאות שנעשו על ידי המודל בעת חיזוי על מערך הבדיקה:

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

זה נראה כאילו המודל חוזה בצורה סבירה.

עכשיו תסתכל על התפלגות השגיאות:

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

png

אם אתה מרוצה מהדגם שמור אותו לשימוש מאוחר יותר:

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

אם אתה טוען מחדש את הדגם, זה נותן פלט זהה:

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

סיכום

מחברת זו הציגה כמה טכניקות לטיפול בבעיית רגרסיה. להלן מספר טיפים נוספים העשויים לעזור:

  • שגיאת ריבוע ממוצעת (MSE) ו- Mean Absolute Error (MAE) הן פונקציות אובדן נפוצות המשמשות לבעיות רגרסיה. שגיאה מוחלטת ממוצעת פחות רגישה לחריגים. פונקציות אובדן שונות משמשות לבעיות סיווג.
  • באופן דומה, מדדי הערכה המשמשים לרגרסיה שונים מהסיווג.
  • כאשר לתכונות נתוני קלט מספריים יש ערכים עם טווחים שונים, יש לשנות את כל התכונות באופן עצמאי לאותו טווח.
  • התאמת יתר היא בעיה נפוצה עבור דגמי DNN, זו לא הייתה בעיה עבור מדריך זה. עיין במדריך האוברפיט והתלבושות לעזרה נוספת בנושא זה.
# 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.