17 KiB
חיזוי סדרות זמן עם Support Vector Regressor
בשיעור הקודם, למדת כיצד להשתמש במודל ARIMA כדי לבצע תחזיות של סדרות זמן. עכשיו תכיר את מודל Support Vector Regressor, שהוא מודל רגרסיה המשמש לחיזוי נתונים רציפים.
מבחן מקדים
מבוא
בשיעור זה, תגלו דרך ספציפית לבנות מודלים עם SVM: Support Vector Machine עבור רגרסיה, או SVR: Support Vector Regressor.
SVR בהקשר של סדרות זמן 1
לפני שנבין את החשיבות של SVR בחיזוי סדרות זמן, הנה כמה מושגים חשובים שעליכם להכיר:
- רגרסיה: טכניקת למידה מונחית לחיזוי ערכים רציפים מתוך קבוצת נתונים נתונה. הרעיון הוא להתאים עקומה (או קו) במרחב התכונות שיש לה את המספר המרבי של נקודות נתונים. לחצו כאן למידע נוסף.
- Support Vector Machine (SVM): סוג של מודל למידת מכונה מונחית המשמש לסיווג, רגרסיה וזיהוי חריגות. המודל הוא היפר-מישור במרחב התכונות, שבמקרה של סיווג משמש כגבול, ובמקרה של רגרסיה משמש כקו ההתאמה הטוב ביותר. ב-SVM, פונקציית Kernel משמשת בדרך כלל כדי להפוך את קבוצת הנתונים למרחב בעל מספר ממדים גבוה יותר, כך שניתן להפריד אותם בקלות. לחצו כאן למידע נוסף על SVMs.
- Support Vector Regressor (SVR): סוג של SVM, שמטרתו למצוא את קו ההתאמה הטוב ביותר (שבמקרה של SVM הוא היפר-מישור) שיש לו את המספר המרבי של נקודות נתונים.
למה SVR? 1
בשיעור הקודם למדתם על ARIMA, שהוא שיטה סטטיסטית ליניארית מוצלחת לחיזוי נתוני סדרות זמן. עם זאת, במקרים רבים, נתוני סדרות זמן מכילים אי-ליניאריות, שלא ניתן למפות באמצעות מודלים ליניאריים. במקרים כאלה, היכולת של SVM להתחשב באי-ליניאריות בנתונים עבור משימות רגרסיה הופכת את SVR למוצלח בחיזוי סדרות זמן.
תרגיל - בניית מודל SVR
השלבים הראשונים להכנת הנתונים זהים לאלה של השיעור הקודם על ARIMA.
פתחו את /working בתיקייה של שיעור זה ומצאו את הקובץ notebook.ipynb.2
-
הריצו את המחברת וייבאו את הספריות הנדרשות: 2
import sys sys.path.append('../../')
import os import warnings import matplotlib.pyplot as plt import numpy as np import pandas as pd import datetime as dt import math from sklearn.svm import SVR from sklearn.preprocessing import MinMaxScaler from common.utils import load_data, mape
-
טענו את הנתונים מתוך הקובץ
/data/energy.csv
לתוך DataFrame של Pandas והסתכלו עליהם: 2energy = load_data('../../data')[['load']]
-
צרו גרף של כל נתוני האנרגיה הזמינים מינואר 2012 עד דצמבר 2014: 2
energy.plot(y='load', subplots=True, figsize=(15, 8), fontsize=12) plt.xlabel('timestamp', fontsize=12) plt.ylabel('load', fontsize=12) plt.show()
עכשיו, בואו נבנה את מודל ה-SVR שלנו.
יצירת קבוצות אימון ובדיקה
עכשיו הנתונים שלכם טעונים, כך שתוכלו להפריד אותם לקבוצות אימון ובדיקה. לאחר מכן תעצבו מחדש את הנתונים כדי ליצור קבוצת נתונים מבוססת צעדי זמן, שתידרש עבור SVR. תאמנו את המודל שלכם על קבוצת האימון. לאחר שהמודל סיים את האימון, תעריכו את דיוקו על קבוצת האימון, קבוצת הבדיקה ולאחר מכן על כל קבוצת הנתונים כדי לראות את הביצועים הכוללים. עליכם לוודא שקבוצת הבדיקה מכסה תקופה מאוחרת יותר בזמן מקבוצת האימון כדי להבטיח שהמודל לא יקבל מידע מתקופות זמן עתידיות 2 (מצב המכונה Overfitting).
-
הקצו תקופה של חודשיים מה-1 בספטמבר עד ה-31 באוקטובר 2014 לקבוצת האימון. קבוצת הבדיקה תכלול את התקופה של חודשיים מה-1 בנובמבר עד ה-31 בדצמבר 2014: 2
train_start_dt = '2014-11-01 00:00:00' test_start_dt = '2014-12-30 00:00:00'
-
הציגו את ההבדלים: 2
energy[(energy.index < test_start_dt) & (energy.index >= train_start_dt)][['load']].rename(columns={'load':'train'}) \ .join(energy[test_start_dt:][['load']].rename(columns={'load':'test'}), how='outer') \ .plot(y=['train', 'test'], figsize=(15, 8), fontsize=12) plt.xlabel('timestamp', fontsize=12) plt.ylabel('load', fontsize=12) plt.show()
הכנת הנתונים לאימון
עכשיו, עליכם להכין את הנתונים לאימון על ידי ביצוע סינון וסקיילינג של הנתונים שלכם. סננו את קבוצת הנתונים כך שתכלול רק את התקופות והעמודות הדרושות, וסקיילינג כדי להבטיח שהנתונים יוקרנו בטווח 0,1.
-
סננו את קבוצת הנתונים המקורית כך שתכלול רק את התקופות שהוזכרו לכל קבוצה ותכלול רק את העמודה הדרושה 'load' בנוסף לתאריך: 2
train = energy.copy()[(energy.index >= train_start_dt) & (energy.index < test_start_dt)][['load']] test = energy.copy()[energy.index >= test_start_dt][['load']] print('Training data shape: ', train.shape) print('Test data shape: ', test.shape)
Training data shape: (1416, 1) Test data shape: (48, 1)
-
בצעו סקיילינג לנתוני האימון כך שיהיו בטווח (0, 1): 2
scaler = MinMaxScaler() train['load'] = scaler.fit_transform(train)
-
עכשיו, בצעו סקיילינג לנתוני הבדיקה: 2
test['load'] = scaler.transform(test)
יצירת נתונים עם צעדי זמן 1
עבור SVR, אתם ממירים את נתוני הקלט לצורה [batch, timesteps]
. לכן, תעצבו מחדש את train_data
ו-test_data
כך שתהיה ממד חדש שמתייחס לצעדי הזמן.
# Converting to numpy arrays
train_data = train.values
test_data = test.values
לדוגמה זו, ניקח timesteps = 5
. כך שהקלטים למודל הם הנתונים עבור 4 צעדי הזמן הראשונים, והפלט יהיה הנתונים עבור צעד הזמן החמישי.
timesteps=5
המרת נתוני האימון לטנסור דו-ממדי באמצעות list comprehension מקונן:
train_data_timesteps=np.array([[j for j in train_data[i:i+timesteps]] for i in range(0,len(train_data)-timesteps+1)])[:,:,0]
train_data_timesteps.shape
(1412, 5)
המרת נתוני הבדיקה לטנסור דו-ממדי:
test_data_timesteps=np.array([[j for j in test_data[i:i+timesteps]] for i in range(0,len(test_data)-timesteps+1)])[:,:,0]
test_data_timesteps.shape
(44, 5)
בחירת קלטים ופלטים מנתוני האימון והבדיקה:
x_train, y_train = train_data_timesteps[:,:timesteps-1],train_data_timesteps[:,[timesteps-1]]
x_test, y_test = test_data_timesteps[:,:timesteps-1],test_data_timesteps[:,[timesteps-1]]
print(x_train.shape, y_train.shape)
print(x_test.shape, y_test.shape)
(1412, 4) (1412, 1)
(44, 4) (44, 1)
יישום SVR 1
עכשיו, הגיע הזמן ליישם SVR. לקריאה נוספת על יישום זה, תוכלו לעיין ב-תיעוד הזה. עבור היישום שלנו, נבצע את השלבים הבאים:
- הגדירו את המודל על ידי קריאה ל-
SVR()
והעברת היפר-פרמטרים של המודל: kernel, gamma, c ו-epsilon - הכינו את המודל לנתוני האימון על ידי קריאה לפונקציה
fit()
- בצעו תחזיות על ידי קריאה לפונקציה
predict()
עכשיו ניצור מודל SVR. כאן נשתמש ב-RBF kernel, ונגדיר את היפר-פרמטרים gamma, C ו-epsilon כ-0.5, 10 ו-0.05 בהתאמה.
model = SVR(kernel='rbf',gamma=0.5, C=10, epsilon = 0.05)
התאמת המודל לנתוני האימון 1
model.fit(x_train, y_train[:,0])
SVR(C=10, cache_size=200, coef0=0.0, degree=3, epsilon=0.05, gamma=0.5,
kernel='rbf', max_iter=-1, shrinking=True, tol=0.001, verbose=False)
ביצוע תחזיות עם המודל 1
y_train_pred = model.predict(x_train).reshape(-1,1)
y_test_pred = model.predict(x_test).reshape(-1,1)
print(y_train_pred.shape, y_test_pred.shape)
(1412, 1) (44, 1)
יצרתם את ה-SVR שלכם! עכשיו נצטרך להעריך אותו.
הערכת המודל שלכם 1
להערכה, קודם כל נחזיר את הנתונים לסקייל המקורי שלנו. לאחר מכן, כדי לבדוק את הביצועים, ניצור גרף של סדרת הזמן המקורית והתחזית, ונדפיס גם את תוצאת ה-MAPE.
החזירו את הפלטים המנובאים והמקוריים לסקייל המקורי:
# Scaling the predictions
y_train_pred = scaler.inverse_transform(y_train_pred)
y_test_pred = scaler.inverse_transform(y_test_pred)
print(len(y_train_pred), len(y_test_pred))
# Scaling the original values
y_train = scaler.inverse_transform(y_train)
y_test = scaler.inverse_transform(y_test)
print(len(y_train), len(y_test))
בדיקת ביצועי המודל על נתוני האימון והבדיקה 1
נחלץ את חותמות הזמן מקבוצת הנתונים כדי להציג בציר ה-x של הגרף שלנו. שימו לב שאנחנו משתמשים ב-timesteps-1
הערכים הראשונים כקלט עבור הפלט הראשון, כך שחותמות הזמן עבור הפלט יתחילו לאחר מכן.
train_timestamps = energy[(energy.index < test_start_dt) & (energy.index >= train_start_dt)].index[timesteps-1:]
test_timestamps = energy[test_start_dt:].index[timesteps-1:]
print(len(train_timestamps), len(test_timestamps))
1412 44
צרו גרף של התחזיות עבור נתוני האימון:
plt.figure(figsize=(25,6))
plt.plot(train_timestamps, y_train, color = 'red', linewidth=2.0, alpha = 0.6)
plt.plot(train_timestamps, y_train_pred, color = 'blue', linewidth=0.8)
plt.legend(['Actual','Predicted'])
plt.xlabel('Timestamp')
plt.title("Training data prediction")
plt.show()
הדפיסו את MAPE עבור נתוני האימון
print('MAPE for training data: ', mape(y_train_pred, y_train)*100, '%')
MAPE for training data: 1.7195710200875551 %
צרו גרף של התחזיות עבור נתוני הבדיקה
plt.figure(figsize=(10,3))
plt.plot(test_timestamps, y_test, color = 'red', linewidth=2.0, alpha = 0.6)
plt.plot(test_timestamps, y_test_pred, color = 'blue', linewidth=0.8)
plt.legend(['Actual','Predicted'])
plt.xlabel('Timestamp')
plt.show()
הדפיסו את MAPE עבור נתוני הבדיקה
print('MAPE for testing data: ', mape(y_test_pred, y_test)*100, '%')
MAPE for testing data: 1.2623790187854018 %
🏆 קיבלתם תוצאה טובה מאוד על קבוצת הבדיקה!
בדיקת ביצועי המודל על כל קבוצת הנתונים 1
# Extracting load values as numpy array
data = energy.copy().values
# Scaling
data = scaler.transform(data)
# Transforming to 2D tensor as per model input requirement
data_timesteps=np.array([[j for j in data[i:i+timesteps]] for i in range(0,len(data)-timesteps+1)])[:,:,0]
print("Tensor shape: ", data_timesteps.shape)
# Selecting inputs and outputs from data
X, Y = data_timesteps[:,:timesteps-1],data_timesteps[:,[timesteps-1]]
print("X shape: ", X.shape,"\nY shape: ", Y.shape)
Tensor shape: (26300, 5)
X shape: (26300, 4)
Y shape: (26300, 1)
# Make model predictions
Y_pred = model.predict(X).reshape(-1,1)
# Inverse scale and reshape
Y_pred = scaler.inverse_transform(Y_pred)
Y = scaler.inverse_transform(Y)
plt.figure(figsize=(30,8))
plt.plot(Y, color = 'red', linewidth=2.0, alpha = 0.6)
plt.plot(Y_pred, color = 'blue', linewidth=0.8)
plt.legend(['Actual','Predicted'])
plt.xlabel('Timestamp')
plt.show()
print('MAPE: ', mape(Y_pred, Y)*100, '%')
MAPE: 2.0572089029888656 %
🏆 גרפים מרשימים מאוד, שמראים מודל עם דיוק טוב. כל הכבוד!
🚀אתגר
- נסו לשנות את היפר-פרמטרים (gamma, C, epsilon) בזמן יצירת המודל והעריכו את הנתונים כדי לראות איזה סט של היפר-פרמטרים נותן את התוצאות הטובות ביותר על נתוני הבדיקה. למידע נוסף על היפר-פרמטרים אלה, תוכלו לעיין בתיעוד כאן.
- נסו להשתמש בפונקציות kernel שונות עבור המודל ונתחו את ביצועיהן על קבוצת הנתונים. מסמך מועיל ניתן למצוא כאן.
- נסו להשתמש בערכים שונים עבור
timesteps
כדי שהמודל יוכל להסתכל אחורה ולבצע תחזית.
מבחן מסכם
סקירה ולימוד עצמי
שיעור זה נועד להציג את השימוש ב-SVR לחיזוי סדרות זמן. לקריאה נוספת על SVR, תוכלו לעיין ב-בלוג הזה. תיעוד זה ב-scikit-learn מספק הסבר מקיף יותר על SVMs באופן כללי, SVRs וגם פרטי יישום אחרים כמו פונקציות kernel שונות שניתן להשתמש בהן, והפרמטרים שלהן.
משימה
קרדיטים
כתב ויתור:
מסמך זה תורגם באמצעות שירות תרגום מבוסס בינה מלאכותית Co-op Translator. בעוד שאנו שואפים לדיוק, יש לקחת בחשבון שתרגומים אוטומטיים עשויים להכיל שגיאות או אי דיוקים. המסמך המקורי בשפתו המקורית צריך להיחשב כמקור סמכותי. עבור מידע קריטי, מומלץ להשתמש בתרגום מקצועי על ידי אדם. איננו נושאים באחריות לאי הבנות או לפרשנויות שגויות הנובעות משימוש בתרגום זה.
-
הטקסט, הקוד והתוצאות בסעיף זה נתרמו על ידי @AnirbanMukherjeeXD ↩︎