![]() | ![]() | ![]() | ![]() |
בבעיית רגרסיה , המטרה היא לחזות תפוקה של ערך רציף, כמו מחיר או הסתברות. לעומת זאת עם בעיית סיווג , כאשר המטרה היא לבחור מחלקה מרשימת הכיתות (למשל, כאשר תמונה מכילה תפוח או תפוז, תוך זיהוי של איזה פרי בתמונה).
מחברת זו משתמשת במערך הנתונים הקלאסי 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>
בדוק גם את הסטטיסטיקה הכוללת, שים לב כיצד כל תכונה מכסה טווח שונה מאוד:
0974 אאדאף 0פיצול תכונות מתוויות
הפרד בין ערך היעד, "התווית", לתכונות. תווית זו היא הערך שתאמן את המודל לחזות.
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)
אסוף את התוצאות על ערכת הבדיקה, להמשך:
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)
מספר כניסות
אתה יכול להשתמש בהתקנה כמעט זהה כדי לחזות על סמך מספר תשומות. מודל זה עדיין עושה את אותו $ 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)
אסוף את התוצאות על ערכת הבדיקה, להמשך:
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)
אם תשרטט את התחזיות כפונקציה של Horsepower
, תראה כיצד מודל זה מנצל את האי-ליניאריות שמספקות השכבות הנסתרות:
x = tf.linspace(0.0, 250, 251)
y = dnn_horsepower_model.predict(x)
plot_horsepower(x, y)
אסוף את התוצאות על ערכת הבדיקה, להמשך:
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)
אסוף את התוצאות על ערכת הבדיקה:
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)
זה נראה כאילו המודל חוזה בצורה סבירה.
עכשיו תסתכל על התפלגות השגיאות:
error = test_predictions - test_labels
plt.hist(error, bins=25)
plt.xlabel('Prediction Error [MPG]')
_ = plt.ylabel('Count')
אם אתה מרוצה מהדגם שמור אותו לשימוש מאוחר יותר:
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.