בחירת התכונות (feature selection) עבור מודל למידת מכונה

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

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

יש גישות רבות לבחירת התכונות feature selection ללמידת מכונה. במדריך זה נסביר כיצד לשפר תוצאות של מודלים מסוג XGBoost תוך הסתמכות על המדד feature importance שנותן ניקוד לתכונות לפי מידת התרומה שלהם לתוצאות, ושימוש רק באותם התכונות שקיבלו ניקוד גבוה.

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

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

בחירת התכונות (feature selection) עבור מודל למידת מכונה

המדריך מתייחס לכל הנקודות העקרוניות אבל אלוהים נמצא בפרטים אז אני ממליץ בחום להוריד את המחברת ולעיין בה: xgboost_feature_selection.ipynb.

 

יבוא הספריות

נייבא את הספריות:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import xgboost as xgb
  • numpy ו-pandas בשביל החישובים ועבודה עם מערכי נתונים
  • matplotlib בשביל התרשימים
  • XGBoost הוא מודל למידת מכונה

 

מסד הנתונים

מסד הנתונים מספק מידע על הנוסעים על סיפון הטיטאניק דוגמת: גיל, מין, מצב כלכלי, או מיקום התא בספינה. הורדתי אותו מהקישור: titanic data set

וייבאתי ל-dataframe:

df = pd.read_csv('titanic.csv')

כמה עמודות ודוגמאות במסד הנתונים?

# How many rows and columns
df.shape
(891, 12)
  • 12 עמודות השייכות ל-891 דוגמאות.

נסקור את העמודות:

df.info()
#   Column       Non-Null Count  Dtype  
---  ------       --------------  -----  
 0   PassengerId  891 non-null    int64  
 1   Survived     891 non-null    int64  
 2   Pclass       891 non-null    int64  
 3   Name         891 non-null    object 
 4   Sex          891 non-null    object 
 5   Age          714 non-null    float64
 6   SibSp        891 non-null    int64  
 7   Parch        891 non-null    int64  
 8   Ticket       891 non-null    object 
 9   Fare         891 non-null    float64
 10  Cabin        204 non-null    object 
 11  Embarked     889 non-null    object
  • חלק מהעמודות מספריות, אחרות קטגוריות.
  • חלק מהנתונים חסרים. לדוגמה, 77% מנתוני העמודה Cabin.

את סקירת הנתונים המלאה ניתן למצוא במדריך "סיווג באמצעות מודל למידת מכונה XGBoost".

 

הכנת הנתונים ללמידת מכונה

כיוון שמטרת המדריך היא להסביר על בחירת הנתונים נשים לב לתכונות (=עמודות) אותם ניתן להסיר.

מועמד ראשון במעלה להסרה אילו עמודות המתאפיינות בשיעור שונות גבוה. לדוגמה, עמודת האינדקס (PassgengerId). כיוון שלכל נוסע יש id ייחודי המודל לא יכול ללמוד מזה שום דבר.

נשתמש בפונקציה unique() כדי למצוא את הערכים הייחודיים בכל עמודה:

לדוגמה, מה שיעור הערכים הייחודיים בעמודה Survived? (כאשר מספר הדוגמאות הכולל הוא 891)

percentage = len(df[col].unique())/891*100
print(col, "{:.1f}".format(percentage))
Survived 0.2
  • 0.2% הוא שיעור הייחודיים בעמודה Survived.

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

for col in df.columns:
    percentage = len(df[col].unique())/891*100
    print(col, "{:.1f}".format(percentage))
PassengerId 100.0
Survived 0.2
Pclass 0.3
Name 100.0
Sex 0.2
Age 10.0
SibSp 0.8
Parch 0.8
Ticket 76.4
Fare 27.8
Embarked 0.4

העמודות בהם שיעור הנתונים הייחודיים הוא גבוה במיוחד הם: Ticket (מזהה כרטיס הנסיעה) עם 76.4% ערכים ייחודיים. 100% משמות הנוסעים (העמודה Name) וה-PassengerId הינם ייחודיים. עמודות בהם השונות גבוהה במיוחד לא תורמות, ואף עלולות להזיק כי הם מכניסות רעש שמפריע למודל ללמוד. נסיר אותם:

# remove high variance features
df_clean = df.drop(['PassengerId','Name','Ticket'], axis=1)

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

# remove columns with lots of missing values
df_clean = df_clean.drop(['Cabin'], axis=1)

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

כמעט 20% מנתוני העמודה Age חסרים. אפשר למלא את החסר באמצעות מדד אמצע, דוגמת ממוצע וחציון. הדרך המומלצת על ידי XGBoost היא למלא את החסר באפסים:

# XGBoost requires no missing cells
# we can replace the missing values with the median
# med = df_clean.Age.median()
# the XGBoost way is replacing the missing values with 0
df_clean.Age = df_clean.Age.fillna(0)

נסיר דוגמאות בהם חסרים נתונים:

# drop rows with missing values
df_clean = df_clean.dropna(axis=0)

את הנתונים הקטגוריים נקודד בשיטת one-hot encode:

# we have to one hot encode categorical columns before using XGBoost
categorical_columns = ['Sex', 'Embarked', 'Pclass', 'Parch']
for cname in categorical_columns:
    dummies = pd.get_dummies(df_clean[cname], prefix=cname)
    df_clean = pd.concat([df_clean, dummies], axis=1)
    df_clean.drop([cname], axis=1, inplace=True)

נפריד את הנתונים למשתנה מטרה (y) ומשתנים בלתי תלויים (x):

# separate into dependent and independent variables
y = df_clean.pop('Survived') 
x = df_clean

נפצל את מסד הנתונים לשני סטים, אימון ומבחן (train and test):

x_train, x_test, y_train, y_test = train_test_split(x, y,
   train_size=.7,
   random_state=42,
   stratify=y)

 

מודל הבסיס

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

אפשר להשתמש ב-XGBoost למשימות רגרסיה או סיווג. כדי לחזות מי שרד את טביעת הטיטאניק נשתמש בו כמסווג באמצעות הפונקציה xgboost.XGBClassifier:

def classify(x_train, x_test):
    model = xgb.XGBClassifier(objective='binary:logistic', missing=1, seed=42)
    model = model.fit(x_train,
                    y_train,
                    verbose=True,
                    early_stopping_rounds=10,
                    eval_metric='aucpr',
                    eval_set=[(x_test,y_test)])
    return model
  • את האלגוריתם שמתי בתוך הפונקציה classify כדי שנוכל להשתמש בה עם הרכבים שונים של סט הנתונים הבלתי תלויים (x_train, x_test) שהפונקציה מקבלת בתור פרמטר.

המדד בו נשתמש להערכת ביצועי המודל הוא f1 כי הוא פחות מוטה ממדד accuracy בפרט במסד נתונים שאינו מאוזן. הפונקציה evaluate תשמש אותנו לביצוע ההערכה:

def evaluate(model, x_test):
    y_pred = model.predict(x_test)
    return classification_report(y_test, y_pred)

הפונקציה מקבלת בתור פרמטרים את המודל של XGBoost והרכב שונה של סט הנתונים הבלתי תלויים של קבוצת המבחן x_test.

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

model = classify(x_train, x_test)
print(evaluate(model, x_test))

התוצאות:

                   f1-score   

           0       0.83      
           1       0.70   
  • מודל הבסיס מציג מדד f1 של 83% עבור קטגוריה 1 ו-70% עבור קטגוריה 0.

נשתמש בפונקציה הבאה כדי למצוא את מידת התרומה היחסית של כל אחת מהתכונות (עמודות) ליכולת הסיווג של המודל:

# make a dataframe of the feature importance
def get_feature_importance(model):
    features_list = list(model.get_booster().get_fscore().items())
    features_df = pd.DataFrame(features_list, columns=['feature','importance']).sort_values('importance', ascending=False)
    return features_df

importance = get_feature_importance(model)

נתאר את התוצאות בגרף:

# plot feature importance
from xgboost import plot_importance
from matplotlib import pyplot

plot_importance(model)
pyplot.show()

feature importance with xgboost depicted in a plot

  • הגרף מלמד שיש תכונות שהם יותר חשובות ליכולת הסיווג של המודל, דוגמת: Fare ו-Age, ותכונות שתרומתם קטנה בהרבה, דוגמת: Embarked ו-Pclass.

 

בחינת ההשפעה של בחירת תכונות על ביצועי המודל

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

לדוגמה, נסיר את התכונות אשר תרומתם היחסית נמוכה מ-3.5:

# filter out features below the threshold of 3.5
threshold = 3.5
selected_features = []
for i,r in importance.iterrows():
    if r.importance >= threshold:
        selected_features.append(r.feature)
  • דרך אחרת להגיד את זה היא שרמת הסף היא 3.5 ומה שמתחתיה מסונן החוצה.

אילו תכונות נשארו לאחר הסינון?

# which features remained
selected_features
['Age',
 'Fare',
 'SibSp',
 'Sex_female',
 'Embarked_S',
 'Pclass_3',
 'Embarked_C',
 'Parch_0',
 'Pclass_1',
 'Parch_2']

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

# use only the filtered features for the independent sets
x_train = x_train[selected_features]
x_test = x_test[selected_features]

data set of the selected features above the threshold

נסנן עם שלוש רמות סף שונות, 3, 6 ו-12 ונבחן את השפעת הסינון על התוצאות:

# classify then select with the following thresholds
thresholds = [3, 6, 12]

for threshold in thresholds:
    selected_features = []
    for i,r in importance.iterrows():
        if r.importance > threshold:
            selected_features.append(r.feature)
            
    model = classify(x_train[selected_features], x_test[selected_features])
    print(evaluate(model, x_test[selected_features]))

את התוצאות סיכמתי בטבלה הבאה המציגה את ערכי f1 עבור רמות סף שונות של סינון:

f1 scores for the threshold 3, 6, 12

  • הסרה של עמודות שדירוג ה-feature importance שלהם נמוך או שווה ל-3 גרם לשיפור בביצועי המודל הנמדדים באמצעות f1.
  • כשמשתמשים ברמת סף של 6 (ומסירים 4 עמודות מתוך 12) מדד f1 כלל לא נפגע.
  • גם ברמת סף 12 (שמשאירה אותנו עם 4 עמודות בלבד) מדד f1 נפגע רק מעט.

 

סיכום

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

 

גם זה יעניין אותך

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

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

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

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

 

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

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

 

 

הוסף תגובה חדשה

 

= 3 + 5