מודל לינארי הכולל משתנים קטגוריים לחיזוי מחירי בתים

מחבר:
בתאריך:

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

לחץ כאן כדי להוריד קובץ ה-csv של מסד הנתונים

לחץ כאן כדי להוריד את קובץ קוד המלא שנפתח במדריך

 

השלבים המקדימים

את השלבים המקדימים עשיתי במדריך הקודם. לכן אדלג עליהם. להורדת הקוד המלא הכולל את השלבים המקדימים

 

עיבוד המידע וקידוד משתנים קטגוריים

שימוש במשתנה קטגורי (שמי) מציב בעיה מפני שמחשבים צריכים מספרים כדי לעבוד איתם. גישה אחת לפתרון הבעיה היא למפות כל אחת מהקטגוריות למספר. הבעיה עם הגישה הזו שמודלים של למידת מכונה מניחים שיש למספרים ערך כמותי. לדוגמה, אם אנחנו נקודד את השכונות למספרים. שכונה א תקבל את הערך 1, שכונה ב את הערך 2 ושכונה ג את הערך 3. המודל עלול להסיק ששכונה א + ב שוות לשכונה ג. כדי למנוע את הבעיה משתמשים בקידוד one hot encoding שהופכת את המשתנה השמי למערך של 0 ו-1 שבו כל המשתנים מקבלים 0 לבד מאחד שמקבל 1. כל קטגוריה מקבלת 1 במקום שונה במערך.

בוא נראה איך עושים את זה, ובסוף הדוגמה תוכלו לראות בעצמכם איך נראים משתנים קטגוריים אחרי קידוד בשיטת one hot encoding.

נעזר ב-numpy כדי למצות את סוגי הדירות.

type_names = np.unique(np.array(fd.type))
type_names
array(['Condo', 'Multi-Family', 'Residential'], dtype=object)

3 שמות ל-3 סוגים.

נשתמש ב-pandas כדי לקודד בשיטת one hot encoding, המתאימה לקידוד נתונים קטגוריים.

t_dummies = pd.get_dummies(fd.type)

נציץ במספר דוגמאות אקראיות כדי לראות את הקידוד בפעולה:

t_dummies.sample(5)
Condo Multi-Family Residential
979 0 0 1
297 0 0 1
344 1 0 0
  • דוגמאות 979 ו-297 שייכות לקטגוריה Residential
  • דוגמה 344 שייכת לסוג Condo.

נאחד את העמודות שהוספנו כתוצאה מהקידוד עם יתר מסד הנתונים לאורך ציר ה-y:

merged = pd.concat([fd, t_dummies], axis=1)
merged.head()
beds baths sq__ft type price Condo Multi-Family Residential
0 2 1 836 Residential 59222 0 0 1
1 3 1 1167 Residential 68212 0 0 1
2 2 1 796 Residential 68880 0 0 1
3 2 1 852 Residential 69307 0 0 1
4 2 1 797 Residential 81900 0 0 1

collinearity של מודלים לינאריים היא כאשר ניתן להסיק את אחת מעמודות הנתונים מהעמודות האחרות. זו בעיה שנוצרת כשעובדים עם קידוד one hot encoding כי ניתן להסיק תמיד את אחת העמודות משילוב של האחרות. במקרה שלנו, יש לנו שלוש עמודות ואם שתי העמודות הראשונות הם 0 אז נסיק בוודאות שהעמודה השלישית היא 1.

פתרון הבעיה של collinearity הוא באמצעות השמטת אחת העמודות שנוצרו באמצעות one hot encoding. במקרה שלנו נוריד את העמודה Condo:

X = merged.drop(['Condo'],axis=1)

כדי לפתח את המודל אנחנו צריכים להגדיר את ציר ה-x (משתנים בלתי תלויים) ואת ציר ה-y (המשתנה התלוי).

המשתנה התלוי שלנו הוא המחיר:

y = fd['price'].values

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

X = merged.drop(['price','type','Condo'],axis=1).values

נשארנו עם העמודות הבאות:

X.columns
Index(['beds', 'baths', 'sq__ft', 'Multi-Family', 'Residential'], dtype='object')

סה"כ 5 סדרות של נתונים שאותם נזין לתוך המודל.

 

הפרדת הנתונים לסט אימון ומבחן

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

# Split the data to test and train groups
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y,
        test_size=0.33, random_state=42)

 

אימון המודל

אימון המודל נעשה באמצעות מודל Keras ושכבה אחת של נוירונים:

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Activation
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.callbacks import EarlyStopping

המודל הוא מודל ליניארי פשוט, מפני שניסיתי מספר מודלים מורכבים מרובי שכבות וכולם היו פחות טובים מאשר המודל הלינארי הפשוט אשר שבו אני משתמש במדריך.

model = Sequential()
# The model maps the 5 inputs (according to the number of features) to a single output (price)
# The model receives 1 input and outputs a single value
model.add(Dense(1, input_shape=(5,)))

# We expect the model to be linear
model.add(Activation('linear'))

המבנה של המודל מתבסס 5 קלטים (inputs) כנגד חמישה פיצ'רים בציר ה-x ופלט 1 בודד שהוא המחיר.

נסכם:

model.summary()
Model: "sequential"
__________________________________________
Layer (type)            Output Shape Param
==========================================
dense (Dense)           (None, 1)    6
__________________________________________
activation (Activation) (None, 1)    0
==========================================
Total params: 6
Trainable params: 6
Non-trainable params: 0

נקמפל את המודל:

# Compile the model
# Adam optimizer for the learning rate
# In preliminary trials I found the learning rate of 10 to be the best for 
# the task at hand
model.compile(Adam(lr=10), 'mean_squared_error')

נגדיר את התנאים להפסקת תהליך הלמידה:

# The EarlyStopping callback stops the learning process if the loss doesn't improve
es = EarlyStopping(monitor='val_loss',
          min_delta=0,
          patience=10,
          verbose=1,
          mode='auto')

ניתן למכונה ללמוד:

model.fit(X_train,
      y_train,
      batch_size=32,
      epochs=1000,
      validation_data=(X_test,y_test),
      callbacks=[es])

 

הערכת המודל

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

פיתחנו מודל ליניארי מה שאומר שיהיו לנו שיפועים ונקודת חיתוך אחת עם ציר ה-Y. כמה שיפועים? 5 שיפועים כמספר הפיצ'רים. 

נמצה את הקבועים (5 השיפועים ונקודת החיתוך עם ציר ה-y) שמצא המודל:

# extract the coefficients from the model
# 5 slopes for 5 features
# B is the Y-intercept
(W1, W2, W3, W4, W5), B = model.get_weights()

 

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

# Let's pick a single record
X_test.loc[273]
beds 4
baths 2
sq__ft 2169

המחיר ששולם על הדירה בפועל הוא:

# Actual price
y_test[273]
292000

האם המודל שלנו יצליח לחזות את מחיר הדירה?

נחשב את המחיר הצפוי באמצעות הערכים שמצאנו:

# Predicted price
y_pred273 = 4 * W1 + 2 * W2 + 2169 * W3 + 0 * W4 + 1 * W5 + B
y_pred273[0]
316144

קרוב למדי.

 

הערכת המודל על כל הדוגמאות בקבוצת המבחן

נמצא את התחזיות עבור כל סט נתוני המבחן.

y_pred = model.predict(X_test)

מה מידת הקשר בין המחירים בקבוצת המבחן לבין התוצאות אותם חזה המודל? הקוד הבא משווה את מחירי הבתים בפועל למחירים אותם חוזה המודל.

from scipy import stats
slope, intercept, r_value, p_value, std_err = stats.linregress(y_test.values.reshape(269), y_pred.reshape(269))
idx = ['slope', 'intercept', 'r_value', 'p_value', 'std_err']

data = np.array([slope, intercept, r_value, p_value, std_err])
print(pd.DataFrame(data = data, index = idx , columns = ['values']))
          values
slope     5.110700e-01
intercept 1.132419e+05
r_value   6.720274e-01
p_value   1.062323e-36
std_err   3.446500e-02
  • ערך r של 0.67 אומר לנו שהמודל מצליח להסביר 67% מהשונות בתוצאות. 
  • ערך p שואף לאפס, 1.062323e-36 מלמד שהקשר אותו מתאר המודל הוא מובהק ביותר, ואינו מקרי.

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

הפונקציה הבאה מתארת את משוואת הישר ע"פ הנתונים לעיל: 

predict_line = lambda S: 5.110700e-01 * y_test + 1.132419e+05

כל מה שאנחנו צריכים זה רק 2 נקודות כדי לשרטט את הישר המתאר את תחזית המחירים :

# There exists only one straight line passing through two distinct points
two_pts_for_the_line = np.array([y_test.min(), y_test.max()])
predicted_line = predict_line(two_pts_for_the_line)

נשרטט את תחזית המחירים (באדום) כנגד המחירים בפועל (בכחול):

plt.plot(y_test, y_pred, 'b+')
plt.plot(y_test, predicted_line, c='RED')
plt.xlabel("Prices")
plt.ylabel("Predicted prices")
plt.title("Actual Vs. Predicted prices")
plt.tight_layout()

באדום המחירים שחזה המודל ובכחול המחירים בפועל

נעריך את מידת הסטייה בין התחזית למחירים בפועל:

from sklearn import metrics
print('MAE: %.2f' % metrics.mean_absolute_error(y_test, y_pred))
print('RMSE: %.2f' % np.sqrt(metrics.mean_squared_error(y_test, y_pred)))
MAE: 56353.71
RMSE: 83444.98

ככל שמדדי השגיאה MAE ו- RMSE נמוכים יותר כך ההתאמה טובה יותר. RMSE מודד את השגיאה באותן יחידות כמו הערכים שהמודל מנפק. במקרה שלנו, המודל נותן תחזיות שנמצאות בטווח של +/- 83,444$.

בקובץ המצורף למדריך ניתן למצוא את כל הקוד וגם השוואה לניתוח באמצעות ספריית sklearn

 

סיכום

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

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

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

 

אהבתם? לא אהבתם? דרגו!

0 הצבעות, ממוצע 0 מתוך 5 כוכבים