تحدثنا في المقال السابق عن معظم المشاكل التي قد تواجهنا أثنا تعاملنا مع المتغيرات الفئوئية ، في هذا المقال سنقوم ببناء نموذج لتصنيف مجموعة البيانات cat-in-the-dat II ونحاول تحسين أدائه بطريقة تدريجية. قبل القيام بناء النماذج ، من الضروري الاهتمام بالتحقق المتقاطع.
التحقق المتقاطع
لقد رأينا بالفعل توزيع المسميات / الهدف ، ونعلم أنها مشكلة تصنيف ثنائية مع أهداف منحرفة. وبالتالي ، سوف نستخدم StratifiedKFold لتقسيم البيانات هنا.
import pandas as pd from sklearn import model_selection if __name__ == '__main__': # قراءة ملفات التدريب df = pd.read_csv("../input/cat_train.csv") # -1 و نمله بـ kfold ننشأ عامود جديد نسميه df["kfold"] = -1 # الخطوة التاليه هي عشوائية صفوف البيانات df = df.sample(frac=1).reset_index(drop=True) # الحصول على المسيات y = df.target.values # model_selection من kfold تهيئة كلاس kf = model_selection.StratifiedKFold(n_splits=5) # kfold ملء عامود for f , (t_, v_) in enumerate(kf.split(X=df, y=y)): df.loc[v_, 'kfold'] = f # حفظ ملف الجديد مع عامود الطيات df.to_csv("../input/cat_train_folds.csv", index=False)
يمكننا الآن التحقق من طيات csv الجديدة لمعرفة عدد العينات في كل طية:
In [X]: import pandas as pd In [X]: df = pd.read_csv("../input/cat_train_folds.csv") In [X]: df.kfold.value_counts() Out[X]: 4 120000 3 120000 2 120000 1 120000 0 120000 Name: kfold, dtype: int64
تحتوي جميع الطيات على 120000 عينة. هذا متوقع لأن بيانات التدريب تحتوي على 600000 عينة ، وقمنا بعمل خمسة طيات . حتى الان جيد جدا.
الآن ، يمكننا أيضًا التحقق من التوزيع المستهدف لكل طيه.
In [X]: df[df.kfold==0].target.value_counts() Out[X]: 0 97536 1 22464 Name: target, dtype: int64 In [X]: df[df.kfold==1].target.value_counts() Out[X]: 0 97536 1 22464 Name: target, dtype: int64 In [X]: df[df.kfold==2].target.value_counts() Out[X]: 0 97535 1 22465 Name: target, dtype: int64 In [X]: df[df.kfold==3].target.value_counts() Out[X]: 0 97535 1 22465 Name: target, dtype: int64 In [X]: df[df.kfold==4].target.value_counts() Out[X]: 0 97535 1 22465 Name: target, dtype: int64
نرى أن توزيع الأهداف هو نفسه في كل جزء. هذا هو ما نحتاج إليه. يمكن أيضًا أن تكون متشابهة ولا يلزم أن تكون هي نفسها طوال الوقت. الآن ، عندما نبني نماذجنا ، سيكون لدينا نفس توزيع الأهداف في كل مكان.
بناء النماذج
خط الترميز الأحادي و الإنحدار اللوجستي
أحد أبسط النماذج التي يمكننا بناءها هو بإستخدام خط الترميز الأحادي لجميع البيانات واستخدام الانحدار اللوجستي (logistic regression)
import pandas as pd from sklearn import linear_model from sklearn import metrics from sklearn import preprocessing def run(fold): # تحميل بيانات التدريب مع الطيات df = pd.read_csv("../input/cat_train_folds.csv") # target , id, kfold جميع الأعمدة هي سمات فيما عدا أعمدة features = [ f for f in df.columns if f not in ("id" , "target", "kfold") ] # NONE بـ NaN تعبئة جميع قيم # strings سنحول جميع القيم إلى # لن يؤثر هذا لأن بياناتنا فئويه for col in features: df.loc[:, col] = df[col].astype(str).fillna("NONE") # الحصول على بيانات التدريب بإستخدام الطيات df_train = df[df.kfold != fold].reset_index(drop = True) # الحصول على بيانات التحقق بإستحدام الطيات df_valid = df[df.kfold == fold].reset_index(drop = True) # تهيئة خط الترميز الأحادي ohe = preprocessing.OneHotEncoder() # مناسبت خط الترميز الأحادي على سمات التدريب و التحقق full_data = pd.concat( [df_train[features], df_valid[features]],axis=0 ) ohe.fit (full_data[features]) # تحويل بيانات التدريب x_train = ohe.transform(df_train[features]) # تحويل بيانات التحقق x_valid = ohe.transform(df_valid[features]) # تهئية النموذج الإنحدار اللولجيستي model = linear_model.LogisticRegression() # تناسب النموذج على بيانات التدريب model.fit(x_train, df_train.target.values) # التنبو على بيانات التحقق # AUC سنحتاج إلى قيم الإحتمالات لأننا نحسب valid_preds = model.predict_proba(x_valid)[:, 1] # AUC الحصول على نتيجة auc=metrics.roc_auc_score(df_valid.target.values, valid_preds) # auc طباعة print(auc) if __name__ == "__main__": run(0)
بالتالي ماذا حدث؟ أنشأ دالة تقسم البيانات إلى تدريب والتحقق ، بالنسبة لعدد الطيات المعطى ، وتعالج قيم NaN ، وتطبق خط الترميو الأحادي على جميع البيانات وتدرب نموذج الانحدار اللوجستي البسيط.
عندما نقوم بتشغيل هذا الجزء الكود ، فإنه ينتج مخرجات مثل هذا:
C:\Users\.....\anaconda3\lib\site-packages\sklearn\linear_model\_logistic.py:762: ConvergenceWarning: lbfgs failed to converge (status=1): STOP: TOTAL NO. of ITERATIONS REACHED LIMIT. Increase the number of iterations (max_iter) or scale the data as shown in: https://scikit-learn.org/stable/modules/preprocessing.html Please also refer to the documentation for alternative solver options: https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression n_iter_i = _check_optimize_result( 0.7864687530013026
هناك بعض التحذيرات. يبدو أن الانحدار اللوجستي لم يتقارب مع الحد الأقصى لعدد التكرارات. لم نغير في المعايير ، لذلك لا بأس بذلك. نرى أن AUC هو ~ 0.786.
دعنا نطبع هذا لجميع الطيات الآن بتغيير بسيط في الكود.
import pandas as pd from sklearn import linear_model from sklearn import metrics from sklearn import preprocessing def run(fold): # تحميل بيانات التدريب مع الطيات df = pd.read_csv("../input/cat_train_folds.csv") # target , id, kfold جميع الأعمدة هي سمات فيما عدا أعمدة features = [ f for f in df.columns if f not in ("id" , "target", "kfold") ] # NONE بـ NaN تعبئة جميع قيم # strings سنحول جميع القيم إلى # لن يؤثر هذا لأن بياناتنا فئويه for col in features: df.loc[:, col] = df[col].astype(str).fillna("NONE") # الحصول على بيانات التدريب بإستخدام الطيات df_train = df[df.kfold != fold].reset_index(drop = True) # الحصول على بيانات التحقق بإستحدام الطيات df_valid = df[df.kfold == fold].reset_index(drop = True) # تهيئة خط الترميز الأحادي ohe = preprocessing.OneHotEncoder() # مناسبت خط الترميز الأحادي على سمات التدريب و التحقق full_data = pd.concat( [df_train[features], df_valid[features]],axis=0 ) ohe.fit (full_data[features]) # تحويل بيانات التدريب x_train = ohe.transform(df_train[features]) # تحويل بيانات التحقق x_valid = ohe.transform(df_valid[features]) # تهئية النموذج الإنحدار اللولجيستي model = linear_model.LogisticRegression() # تناسب النموذج على بيانات التدريب model.fit(x_train, df_train.target.values) # التنبو على بيانات التحقق # AUC سنحتاج إلى قيم الإحتمالات لأننا نحسب valid_preds = model.predict_proba(x_valid)[:, 1] # AUC الحصول على نتيجة auc=metrics.roc_auc_score(df_valid.target.values, valid_preds) # auc طباعة print(f"Fold = {fold}, AUC = {auc}") if __name__ == "__main__": for fold_ in range(5): run(fold_)
يرجى ملاحظة أننا لا نجري الكثير من التغييرات ، و هذا يعطي:
❯ python -W ignore ohe_logres.py Fold = 0, AUC = 0.7847865042255127 Fold = 1, AUC = 0.7853553605899214 Fold = 2, AUC = 0.7879321942914885 Fold = 3, AUC = 0.7870315929550808 Fold = 4, AUC = 0.7864668243125608
لاحظ أنني أستخدم “-W ignore” لتجاهل جميع التحذيرات. نرى أن AUC مستقرة تمامًا في جميع الجوانب. متوسط AUC هو 0.78631449527. جيد جدًا لنوذجنا الأول!
نموذج الغابة العشوائية
لتطبيق نموذج الغابة العشوائية في مجموعة البيانات هذه ، بدلاً من خط الترميز الأحادي ، يمكننا استخدام ترميز التسمية وتحويل كل سمة في كل عمود إلى عدد صحيح كما تمت مناقشته سابقًا. لا يختلف الكود كثيرًا عن خط الترميز الأحادي . لنلقي نظرة.
import pandas as pd from sklearn import ensemble from sklearn import metrics from sklearn import preprocessing def run(fold): # تحميل بيانات التدريب مع الطيات df = pd.read_csv("../input/cat_train_folds.csv") # target , id, kfold جميع الأعمدة هي سمات فيما عدا أعمدة features = [ f for f in df.columns if f not in ("id" , "target", "kfold") ] # NONE بـ NaN تعبئة جميع قيم # strings سنحول جميع القيم إلى # لن يؤثر هذا لأن بياناتنا فئويه for col in features: df.loc[:, col] = df[col].astype(str).fillna("NONE") # سنقوم اللأن بترميز تسميات السمات for col in features: # لكل عامود سمة LabelEncoder تهيئة lbl = preprocessing.LabelEncoder() #مناسبة ترميز السميات مع كل البيانات lbl.fit(df[col]) #تحويل جميع البيانات df.loc[:, col] = lbl.transform(df[col]) # الحصول على بيانات التدريب بإستحدام الطيات df_train = df[df.kfold != fold].reset_index(drop = True) # الحصول على بيانات التحقق بإستحدام الطيات df_valid = df[df.kfold == fold].reset_index(drop = True) # الحصول على بيانات التددريب x_train = df_train[features].values # الحصول على بيانات التحقق x_valid = df_valid[features].values # تهيئة نموذج الغابة العشوائية model = ensemble.RandomForestClassifier(n_jobs=-1) # مناسبة النموذج على بيانات التدريب model.fit(x_train, df_train.target.values) # التنبو على بيانات التحقق # AUC سنحتاج إلى قيم الإحتمالات لأننا نحسب valid_preds = model.predict_proba(x_valid)[:, 1] # AUC الحصول على نتيجة auc=metrics.roc_auc_score(df_valid.target.values, valid_preds) # auc طباعة print(f"Fold = {fold}, AUC = {auc}") if __name__ == "__main__": for fold_ in range(5): run(fold_)
نحن نستخدم Random Forest من scikit-Learn وقمنا بالتخلص من خط الترميز الأحادي. و إستخدمنا ترميز التسمية. النتائج هي كما يلي:
❯ python lbl_rf.py Fold = 0, AUC = 0.7167390828113697 Fold = 1, AUC = 0.7165459672958506 Fold = 2, AUC = 0.7159709909587376 Fold = 3, AUC = 0.7161589664189556 Fold = 4, AUC = 0.7156020216155978
فرق كبير! نموذج الغابة العشوائية ، بدون أي ضبط للمعايير ، يؤدي أداءً أسوأ بكثير من الانحدار اللوجستي البسيط.
وهذا سبب يجعلنا نبدأ دائمًا بالنماذج البسيطة أولاً. سيبدأ أحد محبي الغابة العشوائية بها هنا وسيتجاهل نموذج الانحدار اللوجستي معتقدًا أنه نموذج بسيط للغاية لا يمكنه تحقيق أي قيمة أفضل من الغابة العشوائية. هذا النوع من الأشخاص سيرتكب خطأ فادحًا.
في تنفيذنا للغابة العشوائية ، تستغرق الطيات وقتًا أطول بكثير لإكمالها مقارنةً بالإنحدار اللوجستي. لذا ، فنحن لا نخسر فقط AUCولكننا نأخذ وقتًا أطول لإكمال التدريب. يرجى ملاحظة أن الاستدلال يستغرق أيضًا وقتًا طويلاً في الغابة العشوائية كما أنه يأخذ مساحة أكبر بكثير.
إذا أردنا ، يمكننا أيضًا محاولة إستخدام الغابة العشوائية على بيانات متناثره مرمزه بإستخدام خط التنرميز الأحادي ، ولكن هذا سيستغرق الكثير من الوقت. يمكننا أيضًا محاولة تقليل المصفوفات المرمزه لخط الترميز الأحادي باستخدام تحلل القيمة المفرد (singular value decomposition) . هذه طريقة شائعة جدًا لاستخراج الموضوعات في معالجة اللغة الطبيعية.
import pandas as pd from scipy import sparse from sklearn import decomposition from sklearn import ensemble from sklearn import metrics from sklearn import preprocessing def run(fold): # تحميل بيانات التدريب مع الطيات df = pd.read_csv("../input/cat_train_folds.csv") # target , id, kfold جميع الأعمدة هي سمات فيما عدا أعمدة features = [ f for f in df.columns if f not in ("id" , "target", "kfold") ] # NONE بـ NaN تعبئة جميع قيم # strings سنحول جميع القيم إلى # لن يؤثر هذا لأن بياناتنا فئويه for col in features: df.loc[:, col] = df[col].astype(str).fillna("NONE") # الحصول على بيانات التدريب بإستحدام الطيات df_train = df[df.kfold != fold].reset_index(drop = True) # الحصول على بيانات التحقق بإستحدام الطيات df_valid = df[df.kfold == fold].reset_index(drop = True) # تهيئة خط الترميز الأحادي ohe = preprocessing.OneHotEncoder() # مناسبت خط الترميز الأحادي على سمات التدريب و التحقق full_data = pd.concat( [df_train[features], df_valid[features]],axis=0 ) ohe.fit (full_data[features]) # تحويل بيانات التدريب x_train = ohe.transform(df_train[features]) # تحويل بيانات التحقق x_valid = ohe.transform(df_valid[features]) # SVD تهيئة # نقوم بتقليل حجم البيانات إلى 120 عنصر svd = decomposition.TruncatedSVD(n_components=120) #مع مجموعة البيانات المتناثرة SVD مناسبة full_sparse = sparse.vstack((x_train, x_valid)) svd.fit(full_sparse) # تحويل بيانات التدريب المتناثرة x_train = svd.transform(x_train) #تحويل بيانات التحقق المتناثرة x_valid = svd.transform(x_valid) # تهيئة نموذج الغابة العشوائية model = ensemble.RandomForestClassifier(n_jobs=-1) # مناسبة النموذج على بيانات التدريب model.fit(x_train, df_train.target.values) # التنبو على بيانات التحقق # AUC سنحتاج إلى قيم الإحتمالات لأننا نحسب valid_preds = model.predict_proba(x_valid)[:, 1] # AUC الحصول على نتيجة auc=metrics.roc_auc_score(df_valid.target.values, valid_preds) # auc طباعة print(f"Fold = {fold}, AUC = {auc}") if __name__ == "__main__": for fold_ in range(5): run(fold_)
نقوم بترميز البيانات الكاملة بإستخدم خط الترميز الأحادي ثم نلائم TruncatedSVD من scikit-Learn على مصفوفة المتناثرة مع بيانات التدريب + التحقق. بهذه الطريقة ، نقوم بتقليل المصفوفة المتناثرة عالية الأبعاد إلى 120 سمة ثم نلائم مصنف الغابة العشوائي.
وهذه هي ناتج هذا النموذج:
❯ python ohe_svd_rf.py Fold = 0, AUC = 0.7064863038754249 Fold = 1, AUC = 0.706050102937374 Fold = 2, AUC = 0.7086069243167242 Fold = 3, AUC = 0.7066819080085971 Fold = 4, AUC = 0.7058154015055585
نرى أن الأمر أسوأ. يبدو أن أفضل طريقة لهذه المشكلة هي بإستخدم خط الترميز الأحادي مع الانحدار اللوجستي. يبدو أن الغابة العشوائية تستغرق الكثير من الوقت. ربما يمكننا تجربة XGBoost.
نموذج XGBoost
في حالة عدم معرفتك بـ XGBoost ، فهي واحدة من أشهر خوارزميات تعزيز الإشتقاق (gradient boosting ) . نظرًا لأنها خوارزمية مبنية على الشجرة ، فسنستخدم ترميز التسمية على البيانات.
import pandas as pd import xgboost as xgb from sklearn import metrics from sklearn import preprocessing def run(fold): # تحميل بيانات التدريب مع الطيات df = pd.read_csv("../input/cat_train_folds.csv") # target , id, kfold جميع الأعمدة هي سمات فيما عدا أعمدة features = [ f for f in df.columns if f not in ("id" , "target", "kfold") ] # NONE بـ NaN تعبئة جميع قيم # strings سنحول جميع القيم إلى # لن يؤثر هذا لأن بياناتنا فئويه for col in features: df.loc[:, col] = df[col].astype(str).fillna("NONE") # سنقوم اللأن بترميز تسميات السمات for col in features: # لكل عامود سمة LabelEncoder تهيئة lbl = preprocessing.LabelEncoder() #مناسبة ترميز السميات مع كل البيانات lbl.fit(df[col]) #تحويل جميع البيانات df.loc[:, col] = lbl.transform(df[col]) # الحصول على بيانات التدريب بإستحدام الطيات df_train = df[df.kfold != fold].reset_index(drop = True) # الحصول على بيانات التحقق بإستحدام الطيات df_valid = df[df.kfold == fold].reset_index(drop = True) # الحصول على بيانات التددريب x_train = df_train[features].values # الحصول على بيانات التحقق x_valid = df_valid[features].values # xgboost تهيئة نموذج model = xgb.XGBClassifier( n_jobs=-1, max_depth=7, n_estimators=200 ) # مناسبة النموذج على بيانات التدريب model.fit(x_train, df_train.target.values) # التنبو على بيانات التحقق # AUC سنحتاج إلى قيم الإحتمالات لأننا نحسب valid_preds = model.predict_proba(x_valid)[:, 1] # AUC الحصول على نتيجة auc=metrics.roc_auc_score(df_valid.target.values, valid_preds) # auc طباعة print(f"Fold = {fold}, AUC = {auc}") if __name__ == "__main__": for fold_ in range(5): run(fold_)
وتجدر الإشارة إلى أنه في هذا الكود ، قمت بتعديل عدة معايير لـ xgboost. إفتراضياً max_depth لـ xgboost هي 3 ، وقمت بتغييرها إلى 7 ، وقمت أيضًا بتغيير عدد المقدرات (n_estimators) من 100 إلى 200.
الطيات الخمسة من هذا النموذج هي كما يلي:
python lbl_xgb.py Fold = 0, AUC = 0.7656768851999011 Fold = 1, AUC = 0.7633006564148015 Fold = 2, AUC = 0.7654277821434345 Fold = 3, AUC = 0.7663609758878182 Fold = 4, AUC = 0.764914671468069
نرى أن لدينا نتائج أفضل بكثير من الغابة العشوائية العادية دون أي ضبط ، وربما يمكننا تحسين ذلك أكثر من خلال ضبط المزيد من المعايير . يمكنك أيضًا تجربة بعض هندسة السمات، وإسقاط أعمدة معينة لا تضيف أي قيمة إلى النموذج ، وما إلى ذلك. ولكن يبدو أنه لا يوجد الكثير يمكننا القيام به هنا
مجموعة بيانات جديدة
لإثبات التحسينات في النموذج. دعنا نغير مجموعة البيانات إلى مجموعة بيانات أخرى بها الكثير من المتغيرات الفئوية. مجموعة البيانات الأكثر شهرة هي بيانات تعداد البالغين في الولايات المتحدة. تحتوي مجموعة البيانات على بعض السمات، وتتمثل مهمتك في توقع الراتب. دعونا نلقي نظرة على مجموعة البيانات هذه . توضح الصورة أدناه بعض الأعمدة من مجموعة البيانات هذه.
تحتوي مجموعة البيانات هذه على الأعمدة التالية:
• age
• workclass
• fnlwgt
• education
• education.num
• marital.status
• occupation
• relationship
• race
• sex
• capital.gain
• capital.loss
• hours.per.week
• native.country
• income
فلنبدأ ببناء النموذج نرى أن عمود الدخل عبارة عن سلسلة. فلنقم بحساب قيمة في هذا العمود.
In [X]: import pandas as pd In [X]: df = pd.read_csv("../input/adult.csv") In [X]: df.income.value_counts() Out[X]: <=50K 24720 >50K 7841
نرى أن هناك 7841 حالة بدخل أكبر من 50 ألف دولار أمريكي. يمثل هذا حوالي 24٪ من إجمالي عدد العينات. وبالتالي ، سنستخدم نفس التقييم المستخدم مع مجموعة بيانات cat-in-the-dat ، أي AUC.
قبل أن نبدأ في النمذجة ، من أجل التبسيط ، سنقوم بإسقاط بعض الأعمدة ، وهي عددية ، وهي:
• fnlwgt
• age
• capital.gain
• capital.loss
• hours.per.week
نموذج الانحدار اللوجستي
سنقوم بإستخدام خط الترميز الأحادي مع الانحدار اللوجستي ونرى ما سيحدث. تتمثل الخطوة الأولى دائمًا في إجراء تحقق متقاطع. لن أعرض هذا الجزء من الكود هنا. لأننا سبق و قمنا بشئ مماثل .
import pandas as pd from sklearn import linear_model from sklearn import metrics from sklearn import preprocessing def run(fold): # تحميل بيانات التدريب مع الطيات df = pd.read_csv("../input/adult_folds.csv") # قائمة بالأعمدة العددية num_cols = [ "fnlwgt", "age", "capital.gain", "capital.loss", "hours.per.week" ] # إسقاط الأعمدة العددية df = df.drop(num_cols,axis=1) # تعيين الأهداف إلى 0 و 1 target_mapping = { "<=50k": 0, ">50k" : 1 } df.loc[:, "income"] = df.income.map(target_mapping) # income , kfold جميع الأعمدة هي سمات فيما عدا أعمدة features = [ f for f in df.columns if f not in ("income" , "kfold") ] # NONE بـ NaN تعبئة جميع قيم # strings سنحول جميع القيم إلى # لن يؤثر هذا لأن بياناتنا فئويه for col in features: df.loc[:, col] = df[col].astype(str).fillna("NONE") # الحصول على بيانات التدريب بإستخدام الطيات df_train = df[df.kfold != fold].reset_index(drop = True) # الحصول على بيانات التحقق بإستحدام الطيات df_valid = df[df.kfold == fold].reset_index(drop = True) # تهيئة خط الترميز الأحادي ohe = preprocessing.OneHotEncoder() # مناسبت خط الترميز الأحادي على سمات التدريب و التحقق full_data = pd.concat( [df_train[features], df_valid[features]],axis=0 ) ohe.fit (full_data[features]) # تحويل بيانات التدريب x_train = ohe.transform(df_train[features]) # تحويل بيانات التحقق x_valid = ohe.transform(df_valid[features]) # تهئية النموذج الإنحدار اللولجيستي model = linear_model.LogisticRegression() # تناسب النموذج على بيانات التدريب model.fit(x_train, df_train.income.values) # التنبو على بيانات التحقق # AUC سنحتاج إلى قيم الإحتمالات لأننا نحسب valid_preds = model.predict_proba(x_valid)[:, 1] # AUC الحصول على نتيجة auc=metrics.roc_auc_score(df_valid.income.values, valid_preds) # auc طباعة print(f"Fold = {fold}, AUC = {auc}") if __name__ == "__main__": for fold_ in range(5): run(fold_)
وعندما نقوم بتشغيل هذا الكود ، نحصل على:
❯ python -W ignore ohe_logres.py Fold = 0, AUC = 0.8794809708119079 Fold = 1, AUC = 0.8875785068274882 Fold = 2, AUC = 0.8852609687685753 Fold = 3, AUC = 0.8681236223251438 Fold = 4, AUC = 0.8728581541840037
هذا AUC جيد جدًا بالنسبة لنموذج بهذه البساطة!
نموذج xgboost
لنجرب إستخدام ترميز التسمية مع xgboost بدون ضبط أي من المعايير.
import pandas as pd import xgboost as xgb from sklearn import metrics from sklearn import preprocessing def run(fold): # تحميل بيانات التدريب مع الطيات df = pd.read_csv("../input/adult_folds.csv") # قائمة بالأعمدة العددية num_cols = [ "fnlwgt", "age", "capital.gain", "capital.loss", "hours.per.week" ] # إسقاط الأعمدة العددية df = df.drop(num_cols, axis=1) # تعيين الأهداف إلى 0 و 1 target_mapping = { "<=50K": 0, ">50K" : 1 } df.loc[:, "income"] = df.income.map(target_mapping) # income , kfold جميع الأعمدة هي سمات فيما عدا أعمدة features = [ f for f in df.columns if f not in ("income" , "kfold") ] # NONE بـ NaN تعبئة جميع قيم # strings سنحول جميع القيم إلى # لن يؤثر هذا لأن بياناتنا فئويه for col in features: df.loc[:, col] = df[col].astype(str).fillna("NONE") # سنقوم اللأن بترميز تسميات السمات for col in features: # لكل عامود سمة LabelEncoder تهيئة lbl = preprocessing.LabelEncoder() #مناسبة ترميز السميات مع كل البيانات lbl.fit(df[col]) #تحويل جميع البيانات df.loc[:, col] = lbl.transform(df[col]) # الحصول على بيانات التدريب بإستحدام الطيات df_train = df[df.kfold != fold].reset_index(drop = True) # الحصول على بيانات التحقق بإستحدام الطيات df_valid = df[df.kfold == fold].reset_index(drop = True) # الحصول على بيانات التددريب x_train = df_train[features].values # الحصول على بيانات التحقق x_valid = df_valid[features].values # xgboost تهيئة نموذج model = xgb.XGBClassifier( n_jobs=-1, ) # مناسبة النموذج على بيانات التدريب model.fit(x_train, df_train.income.values) # التنبو على بيانات التحقق # AUC سنحتاج إلى قيم الإحتمالات لأننا نحسب valid_preds = model.predict_proba(x_valid)[:, 1] # AUC الحصول على نتيجة auc=metrics.roc_auc_score(df_valid.income.values, valid_preds) # auc طباعة print(f"Fold = {fold}, AUC = {auc}") if __name__ == "__main__": for fold_ in range(5): run(fold_)
و عند تشغيل هذا
❯ python lbl_xgb.py Fold = 0, AUC = 0.8800810634234078 Fold = 1, AUC = 0.886811884948154 Fold = 2, AUC = 0.8854421433318472 Fold = 3, AUC = 0.8676319549361007 Fold = 4, AUC = 0.8714450054900602
هذا يبدو جيدا بالفعل دعونا نرى النتائج عندما نزيد max_depth إلى 7 و n_estimators إلى 200.
❯ python lbl_xgb.py Fold = 0, AUC = 0.8764108944332032 Fold = 1, AUC = 0.8840708537662638 Fold = 2, AUC = 0.8816601162613102 Fold = 3, AUC = 0.8662335762581732 Fold = 4, AUC = 0.8698983461709926
يبدو أنه لا يتحسن. يوضح هذا أن المعايير من مجموعة بيانات واحدة لا يمكن نقلها إلى مجموعة بيانات أخرى. يجب أن نحاول ضبط المعايير مرة أخرى . الآن ، دعونا نحاول تضمين السمات العددية في نموذج xgboost بدون ضبط المعايير .
import pandas as pd import xgboost as xgb from sklearn import metrics from sklearn import preprocessing def run(fold): # تحميل بيانات التدريب مع الطيات df = pd.read_csv("../input/adult_folds.csv") # قائمة بالأعمدة العددية num_cols = [ "fnlwgt", "age", "capital.gain", "capital.loss", "hours.per.week" ] # تعيين الأهداف إلى 0 و 1 target_mapping = { "<=50K": 0, ">50K" : 1 } df.loc[:, "income"] = df.income.map(target_mapping) # income , kfold جميع الأعمدة هي سمات فيما عدا أعمدة features = [ f for f in df.columns if f not in ("income" , "kfold") ] # NONE بـ NaN تعبئة جميع قيم # strings سنحول جميع القيم إلى # لن يؤثر هذا لأن بياناتنا فئويه for col in features: # لا نقوم بترميز الأعمدة العددية if col not in num_cols: df.loc[:, col] = df[col].astype(str).fillna("NONE") # سنقوم اللأن بترميز تسميات السمات for col in features: # لكل عامود سمة LabelEncoder تهيئة lbl = preprocessing.LabelEncoder() #مناسبة ترميز السميات مع كل البيانات lbl.fit(df[col]) #تحويل جميع البيانات df.loc[:, col] = lbl.transform(df[col]) # الحصول على بيانات التدريب بإستحدام الطيات df_train = df[df.kfold != fold].reset_index(drop = True) # الحصول على بيانات التحقق بإستحدام الطيات df_valid = df[df.kfold == fold].reset_index(drop = True) # الحصول على بيانات التددريب x_train = df_train[features].values # الحصول على بيانات التحقق x_valid = df_valid[features].values # xgboost تهيئة نموذج model = xgb.XGBClassifier( n_jobs=-1, ) # مناسبة النموذج على بيانات التدريب model.fit(x_train, df_train.income.values) # التنبو على بيانات التحقق # AUC سنحتاج إلى قيم الإحتمالات لأننا نحسب valid_preds = model.predict_proba(x_valid)[:, 1] # AUC الحصول على نتيجة auc=metrics.roc_auc_score(df_valid.income.values, valid_preds) # auc طباعة print(f"Fold = {fold}, AUC = {auc}") if __name__ == "__main__": for fold_ in range(5): run(fold_)
لذلك ، نحتفظ بالأعمدة العددية ؛ لكننا لا نقوم من دون ترميز التسميه. لذلك ، تتكون مصفوفة السمات النهائية لدينا من أعمدة عددية (كما هي) وأعمدة فئوية مشفرة. يمكن لأي خوارزمية قائمة على الأشجار التعامل مع هذا المزيج بسهولة.
يرجى ملاحظة أننا لا نحتاج إلى تسوية البيانات عندما نستخدم النماذج القائمة على الأشجار. ومع ذلك ، يعد هذا أمرًا ضروريا يجب القيام به عندما نستخدم نماذج خطية مثل الانحدار اللوجستي.
دعونا نشغل هذا الكود !
python lbl_xgb_num.py Fold = 0, AUC = 0.9209790185449889 Fold = 1, AUC = 0.9247157449144706 Fold = 2, AUC = 0.9269329887598243 Fold = 3, AUC = 0.9119349082169275 Fold = 4, AUC = 0.9166408030141667
هذه نتيجة ممتازة!
الآن ، يمكننا محاولة إضافة بعض السمات. سنأخذ جميع الأعمدة الفئوية وننشئ مجموعات من الدرجة الثانية. ألق نظرة على feature_engineering في المقتطف أدناه لمعرفة كيفية القيام بذلك.
import itertools import pandas as pd import xgboost as xgb from sklearn import metrics from sklearn import preprocessing def feature_enginnering(df, cat_cols): """ تُستخدم هذه الوظيفة في هندسة السمات : param df: إطار بيانات الباندا مع بيانات التدريب / الاختبار : param cat_cols: قائمة الأعمدة الفئوية : return: إطار البيانات مع سمات جديدة """ # سيؤدي هذا إلى إنشاء مجموعتين من القيم # في هذه القائمة # فمثلا: # سترجع list(itertools.combinations([1,2,3], 2)) # [(1, 2), (1, 3), (2, 3)] combi = list(itertools.combinations(cat_cols,2)) return for c1, c2 in combi: df.loc[ :, c1 + "_" + c2 ] = df[c1].astype(str) + "_" + df[c2].astype(str) return df def run(fold): # تحميل بيانات التدريب مع الطيات df = pd.read_csv("../input/adult_folds.csv") # قائمة بالأعمدة العددية num_cols = [ "fnlwgt", "age", "capital.gain", "capital.loss", "hours.per.week" ] # تعيين الأهداف إلى 0 و 1 target_mapping = { "<=50K": 0, ">50K" : 1 } df.loc[:, "income"] = df.income.map(target_mapping) # قائمة بالإعمدة الفئوية في هندسة السمات cat_cols = [ c for c in df.columns if c not in num_cols and c not in ("kfold", "income") ] # income , kfold جميع الأعمدة هي سمات فيما عدا أعمدة features = [ f for f in df.columns if f not in ("income" , "kfold") ] # NONE بـ NaN تعبئة جميع قيم # strings سنحول جميع القيم إلى # لن يؤثر هذا لأن بياناتنا فئويه for col in features: # لا نقوم بترميز الأعمدة العددية if col not in num_cols: df.loc[:, col] = df[col].astype(str).fillna("NONE") # سنقوم اللأن بترميز تسميات السمات for col in features: if col not in num_cols: # لكل عامود سمة LabelEncoder تهيئة lbl = preprocessing.LabelEncoder() #مناسبة ترميز السميات مع كل البيانات lbl.fit(df[col]) #تحويل جميع البيانات df.loc[:, col] = lbl.transform(df[col]) # الحصول على بيانات التدريب بإستحدام الطيات df_train = df[df.kfold != fold].reset_index(drop = True) # الحصول على بيانات التحقق بإستحدام الطيات df_valid = df[df.kfold == fold].reset_index(drop = True) # الحصول على بيانات التددريب x_train = df_train[features].values # الحصول على بيانات التحقق x_valid = df_valid[features].values # xgboost تهيئة نموذج model = xgb.XGBClassifier( n_jobs=-1, ) # مناسبة النموذج على بيانات التدريب model.fit(x_train, df_train.income.values) # التنبو على بيانات التحقق # AUC سنحتاج إلى قيم الإحتمالات لأننا نحسب valid_preds = model.predict_proba(x_valid)[:, 1] # AUC الحصول على نتيجة auc=metrics.roc_auc_score(df_valid.income.values, valid_preds) # auc طباعة print(f"Fold = {fold}, AUC = {auc}") if __name__ == "__main__": for fold_ in range(5): run(fold_)
هذه طريقة بليده جدًا لإنشاء سمات من أعمدة فئوية. ينبغي للمرء أن يلقي نظرة على البيانات ومعرفة أي خليط أكثر منطقية. إذا كنت تستخدم هذه الطريقة ، فقد ينتهي بك الأمر إلى إنشاء الكثير من السمات ، وفي هذه الحالة ، ستحتاج إلى استخدام نوع من تحديد السمات لتحديد أفضل السمات .
دعونا نرى النتائج الآن.
python lbl_xgb_num_feat.py Fold = 0, AUC = 0.9211483465031423 Fold = 1, AUC = 0.9251499446866125 Fold = 2, AUC = 0.9262344766486692 Fold = 3, AUC = 0.9114264068794995 Fold = 4, AUC = 0.9177914453099201
يبدو أنه حتى بدون تغيير أي معايير وفقط عن طريق إضافة مجموعة من السمات ، يمكننا تحسين نتائجنا قليلاً. دعونا نرى ما إذا كانت زيادة max_depth إلى 7 مفيدة.
❯ python lbl_xgb_num_feat.py Fold = 0, AUC = 0.9286668430204137 Fold = 1, AUC = 0.9329340656165378 Fold = 2, AUC = 0.9319817543218744 Fold = 3, AUC = 0.919046187194538 Fold = 4, AUC = 0.9245692057162671
ومرة أخرى ، تمكنا من تحسين نموذجنا. لاحظ أننا لم نستخدم بعد قيمًا نادرة ، أو سمات ثنائية ، أومجموعة من السمات المرمزه بإستخدام خط الترميز الأحادي أو ترميز المسميات ….إلخ.
ترميز الهدف (target encoding)
هناك طريقة أخرى لهندسة السمات من السمات الفئوية وهي استخدام ترميز الهدف (target encoding) . ومع ذلك ، يجب أن تكون حذرًا جدًا هنا لأن هذا قد يودي إلى فرط تخصيص نموذجك .
ترميز الهدف هو أسلوب تقوم فيه بتعيين كل فئة في سمة معين إلى متوسط القيمة المستهدفة الخاصة بها ، ولكن يجب أن يتم ذلك دائمًا بطريقة التحقق المتقاطع.
هذا يعني أن أول شيء تفعله هو إنشاء الطيات ، ثم استخدام تلك الطيات لإنشاء سمات ترميز مستهدفة للأعمدة المختلفة في البيانات بنفس الطريقة التي تناسب فيها (fit) توقع النموذج مع الطيات.
لذلك ، إذا قمت بإنشاء 5 طيات ، يجب عليك إنشاء ترميز مستهدف 5 مرات بحيث يكون لديك في النهاية ترميز للمتغيرات في كل طية غير مشتقة من نفس الطية. وبعد ذلك عندما تناسب النموذج الخاص بك ، يجب عليك استخدام نفس الطيات مرة أخرى.
يمكن اشتقاق الترميز المستهدف لبيانات الاختبار الي لم يتدرب عليها النموذج من بيانات التدريب الكاملة أو يمكن أن يكون متوسط جميع الطيات الخمسة.
دعونا نرى كيف يمكننا استخدام ترميز الهدف على نفس مجموعة البيانات الخاصة بالبالغين حتى نتمكن من المقارنة.
import copy import pandas as pd from sklearn import metrics from sklearn import preprocessing import xgboost as xgb def mean_target_encoding(data): # عمل نسخة من إطار البيانات df = copy.deepcopy(data) # قائمة بالأعمدة العددية num_cols = [ "fnlwgt", "age", "capital.gain", "capital.loss", "hours.per.week" ] # تعيين الأهداف إلى 0 و 1 target_mapping = { "<=50K": 0, ">50K" : 1 } df.loc[:, "income"] = df.income.map(target_mapping) # income , kfold جميع الأعمدة هي سمات فيما عدا أعمدة features = [ f for f in df.columns if f not in num_cols and f not in ("kfold", "income") ] # income , kfold جميع الأعمدة هي سمات فيما عدا أعمدة features = [ f for f in df.columns if f not in ("income" , "kfold") ] # NONE بـ NaN تعبئة جميع قيم # strings سنحول جميع القيم إلى # لن يؤثر هذا لأن بياناتنا فئويه for col in features: # لا نقوم بترميز الأعمدة العددية if col not in num_cols: df.loc[:, col] = df[col].astype(str).fillna("NONE") # سنقوم اللأن بترميز تسميات السمات for col in features: if col not in num_cols: # لكل عامود سمة LabelEncoder تهيئة lbl = preprocessing.LabelEncoder() #مناسبة ترميز السميات مع كل البيانات lbl.fit(df[col]) #تحويل جميع البيانات df.loc[:, col] = lbl.transform(df[col]) encoded_dfs = [] #إنشاء حلقة على كل الطيات for fold in range(5): # الحصول على بيانات التدريب و التحقق df_train = df[df.kfold != fold].reset_index(drop = True) df_valid = df[df.kfold == fold].reset_index(drop = True) # لكل أعمدة السمات for column in features : # إنشاء قاموس mapping_dict = dict( df_train.groupby(column)["income"].mean() ) # هو العمود الجديد الذي لدينا مع متوسط الترميز column_enc df_valid.loc[ :, column + "_enc" ] = df_valid[column].map(mapping_dict) # إضافة إلى القائمة إطار بيانات التحقق المرمبز encoded_dfs.append(df_valid) # إنشاء إطار بيانات كامل encoded_dfs = pd.concat(encoded_dfs, axis=0) return encoded_dfs def run(fold): # الحصول على البيانات في الطيات df_train = df[df.kfold != fold].reset_index(drop=True) # الحصول على بيانات التحقق بإستحدام الطيات df_valid = df[df.kfold == fold].reset_index(drop = True) # income , kfold جميع الأعمدة هي سمات فيما عدا أعمدة features = [ f for f in df.columns if f not in ("income" , "kfold") ] # الحصول على بيانات التددريب x_train = df_train[features].values # الحصول على بيانات التحقق x_valid = df_valid[features].values # xgboost تهيئة نموذج model = xgb.XGBClassifier( n_jobs=-1, max_depth=7 ) # مناسبة النموذج على بيانات التدريب model.fit(x_train, df_train.income.values) # التنبو على بيانات التحقق # AUC سنحتاج إلى قيم الإحتمالات لأننا نحسب valid_preds = model.predict_proba(x_valid)[:, 1] # AUC الحصول على نتيجة auc=metrics.roc_auc_score(df_valid.income.values, valid_preds) # auc طباعة print(f"Fold = {fold}, AUC = {auc}") if __name__ == "__main__": # قراءة البيانات df = pd.read_csv("../input/adult_folds.csv") # إنشاء متوسط ترميز الهدف للفئات df = mean_target_encoding(df) # بدأ التدريب و التحقق في 5 طيات for fold_ in range(5): run(fold_)
وتجدر الإشارة إلى أنه في المقتطف أعلاه ، لم أتخلص من الأعمدة الفئوية عندما أجرينا ترميز التهدف. احتفظت بجميع السمات وأضفت سمات ترميز الهدف فوقها. أيضا إستخدمت المتوسط (mean). يمكنك استخدام الانحراف المعياري أو المتوسط أو أي وظيفة أخرى للأهداف.
دعونا نرى النتائج.
Fold = 0, AUC = 0.9332240662017529 Fold = 1, AUC = 0.9363551625140347 Fold = 2, AUC = 0.9375013544556173 Fold = 3, AUC = 0.92237621307625 Fold = 4, AUC = 0.9292131180445478
جميل ! لدينا تحسن طفيف. ومع ذلك ، يجب أن تكون حذرًا للغاية عند استخدام ترميز الهدف لأنه عرضة جدًا لفرط التخصيص (overfitting) . عندما نستخدم ترميز الهدف ، فمن الأفضل استخدام نوع من التنعيم أو إضافة ضوضاء في القيم المشفرة.
لدى Scikit-Learn ترميز الهدف مع التنعيم ، أو يمكنك إنشاء التنعيم الخاص بك. يقدم التنعيم نوعًا من التنظيم الذي يساعد في عدم فرط تخصيص النموذج.
يعتبر التعامل مع السمات الفئوية مهمة معقدة. هناك الكثير من المعلومات المتداولة في العديد من الموارد. و الهدف من هذه المقالة هو مساعدتك على البدء في أي مشكلة تتعلق بالمتغيرات الفئوية.
ومع ذلك ، بالنسبة لمعظم المشاكل ، لن تحتاج إلى أي شيء أكثر من خط الترميز الأحادي و ترميز التسمية. و لتحسين النماذج بشكل أكبر ، قد تحتاج إلى المزيد!
الشبكات العصبية
لا يمكننا إنهاء هذا المقالة دون استخدام شبكة عصبية على هذه البيانات. لذلك ، دعنا نلقي نظرة على تقنية تُعرف باسم تضمين الكيانات (entity embeddings) . يتم تمثيل الفئات كمتجهات . نحن نمثل الفئات بواسطة المتجهات في كل من أساليب الترميز الثنائي و خط الترميز الأحادي. ولكن ماذا لو كان لدينا عشرات الآلاف من الفئات. سيؤدي ذلك إلى إنشاء مصفوفات ضخمة وسيستغرق وقتًا طويلاً بالنسبة لنا لتدريب النماذج المعقدة. وبالتالي يمكننا تمثيلها بالمتجهات ذات القيم العائمة بدلاً من ذلك.
الفكرة بسيطة للغاية. لديك طبقة تضمين لكل سمة فئويه. لذلك ، يمكن الآن تعيين كل فئة في عمود إلى تضمين (مثل تعيين الكلمات لتضمينات في معالجة اللغة الطبيعية). ثم تقوم بإعادة تشكيل هذه التضمينات إلى أبعادها لجعلها مسطحة ثم تربط جميع المدخلات بالتضمينات . ثم أضف مجموعة من الطبقات الكثيفة وطبقة الإخراج ، وقد انتهيت.
هذا سهل جدًا استخدام TF / Keras لتطبيق ذلك .
import copy import pandas as pd from sklearn import metrics from sklearn import preprocessing import xgboost as xgb def mean_target_encoding(data): # عمل نسخة من إطار البيانات df = copy.deepcopy(data) # قائمة بالأعمدة العددية num_cols = [ "fnlwgt", "age", "capital.gain", "capital.loss", "hours.per.week" ] # تعيين الأهداف إلى 0 و 1 target_mapping = { "<=50K": 0, ">50K" : 1 } df.loc[:, "income"] = df.income.map(target_mapping) # income , kfold جميع الأعمدة هي سمات فيما عدا أعمدة features = [ f for f in df.columns if f not in num_cols and f not in ("kfold", "income") ] # income , kfold جميع الأعمدة هي سمات فيما عدا أعمدة features = [ f for f in df.columns if f not in ("income" , "kfold") ] # NONE بـ NaN تعبئة جميع قيم # strings سنحول جميع القيم إلى # لن يؤثر هذا لأن بياناتنا فئويه for col in features: # لا نقوم بترميز الأعمدة العددية if col not in num_cols: df.loc[:, col] = df[col].astype(str).fillna("NONE") # سنقوم اللأن بترميز تسميات السمات for col in features: if col not in num_cols: # لكل عامود سمة LabelEncoder تهيئة lbl = preprocessing.LabelEncoder() #مناسبة ترميز السميات مع كل البيانات lbl.fit(df[col]) #تحويل جميع البيانات df.loc[:, col] = lbl.transform(df[col]) encoded_dfs = [] #إنشاء حلقة على كل الطيات for fold in range(5): # الحصول على بيانات التدريب و التحقق df_train = df[df.kfold != fold].reset_index(drop = True) df_valid = df[df.kfold == fold].reset_index(drop = True) # لكل أعمدة السمات for column in features : # إنشاء قاموس mapping_dict = dict( df_train.groupby(column)["income"].mean() ) # هو العمود الجديد الذي لدينا مع متوسط الترميز column_enc df_valid.loc[ :, column + "_enc" ] = df_valid[column].map(mapping_dict) # إضافة إلى القائمة إطار بيانات التحقق المرمبز encoded_dfs.append(df_valid) # إنشاء إطار بيانات كامل encoded_dfs = pd.concat(encoded_dfs, axis=0) return encoded_dfs def run(fold): # الحصول على البيانات في الطيات df_train = df[df.kfold != fold].reset_index(drop=True) # الحصول على بيانات التحقق بإستحدام الطيات df_valid = df[df.kfold == fold].reset_index(drop = True) # income , kfold جميع الأعمدة هي سمات فيما عدا أعمدة features = [ f for f in df.columns if f not in ("income" , "kfold") ] # الحصول على بيانات التددريب x_train = df_train[features].values # الحصول على بيانات التحقق x_valid = df_valid[features].values # xgboost تهيئة نموذج model = xgb.XGBClassifier( n_jobs=-1, max_depth=7 ) # مناسبة النموذج على بيانات التدريب model.fit(x_train, df_train.income.values) # التنبو على بيانات التحقق # AUC سنحتاج إلى قيم الإحتمالات لأننا نحسب valid_preds = model.predict_proba(x_valid)[:, 1] # AUC الحصول على نتيجة auc=metrics.roc_auc_score(df_valid.income.values, valid_preds) # auc طباعة print(f"Fold = {fold}, AUC = {auc}") if __name__ == "__main__": # قراءة البيانات df = pd.read_csv("../input/adult_folds.csv") # إنشاء متوسط ترميز الهدف للفئات df = mean_target_encoding(df) # بدأ التدريب و التحقق في 5 طيات for fold_ in range(5): run(fold_)
ستلاحظ أن هذا النهج يعطي أفضل النتائج وهو أيضًا سريع للغاية إذا كان لديك GPU! يمكن أيضًا تحسين هذا بشكل أكبر ، ولا داعي للقلق بشأن هندسة السمات حيث تتعامل معها الشبكة العصبية من تلقاء نفسها. هذا بالتأكيد يستحق المحاولة عند التعامل مع مجموعة بيانات كبيرة من السمات الفئوية. عندما يكون حجم التضمين هو نفسه عدد الفئات الفريدة ، يكون لدينا خط ترميز أحادي.
إضافة تعليق