مقدمة
أهلا بكم في الجزء الثاني من سلسلة فهم لتعلم الآلة. اليوم سوف نتحدث عن النوع الثاني من أنواع التعلم الخاضع للإشراف ألا وهو التصنيف والمعروف بالـ Classification، إن الأمثلة على تطبيقات التصنيف متعددة، على سبيل المثال لا الحصر
- تصنيف هل الشخص محتال أم لا (مجال البنوك)
- تصنيف الخلية إذا ما كانت خلية سرطانية أم لا (المجال الطبي)
- تصنيف نوع العطل في محرك السيارة (مجال الصناعة)
- تصنيف هل العميل سيترك الشركة أم سيستمر (شركات الاتصالات، البنوك، مزودي الخدمات)
وتكمن أهمية خوارزميات التصنيف في أمرين أساسيين، أولا تصنيف الأشياء و ثانيا تحديد العوامل التي أدت إلى هذا التصنيف. وفي درس اليوم، سوف نتعلم كيفية استخدام خوارزميات التصنيف لتوقع أي من عملاء شركة الاتصال سيترك الشركة وأي منهم سيستمر في الاشتراك بخدماتها.
ماذا نريد أن نفعل؟
في هذا المشروع، أنت عالم بيانات لشركة اتصالات تقدم خدمات الاتصال و الإنترنت للمواطنين. وبما إن المنافسة بين شركات الاتصال عالية، طُلب منك عمل نموذج تعلم آلة يستطيع توقع مَن مِن العملاء قد يستغني عن خدمات الشركة ويذهب إلى شركة أخرى بناء على الذي حصل في الماضي.
ما المقصود بالماضي؟
حسنا، سبق وأن تحدثنا أن تعلم الآلة مبني على التعلم من البيانات المجمعة (أي الخبرات الماضية)، حيث أن المرحلة الأولى في سير عمل نماذج تعلم الآلة هو (التذكر)
ومن أجل ذلك، تقوم شركات الإتصال بالعادة بتسجيل بيانات العملاء مثل (العمر، عدد أفراد العائلة، متزوج أم لا، ذكر أم أنثى) بالإضافة إلى تسجيل الخدمات المشتركين فيها مثل (نوع الإنترنت، عدد الخطوط المستعملة، قيمة الاشتراك، مدة العقد) ثم يقومون بملاحظة من ترك الشركة ومن استمر فيها. ومن خلال هذه الملاحظات، سوف نقارن بيانات العملاء الحالين ببيانات العملاء الذين تركوا الشركة، فإن كان هناك تشابه في تصرفاتهم وبياناتهم، فبالغالب سوف يتركون الشركة، ومن أجل حل هذه المشكلة، نقوم بتوجيه عروض مخصصة لهم من أجل المحافظة عليهم.
نموذج البيانات
Data Modelمن أجل بناء نموذج تعلم الآلة لابد أولا من بناء نموذج البيانات (جدول البيانات) المناسب للتدريب. حيث أن جودة البيانات هي العامل الأساسي في جودة ودقة نماذج تعلم الآلة. إذًا كيف نبني نموذج البيانات المناسب؟
إنه يوجد في كل شركة قاعدة بيانات تحتوي على العديد من الجداول ، ومهمتنا الأولى هي استخراج البيانات المناسبة من هذه الجداول لبناء النموذج وجمعها في جدول واحد عن طريق لغة الـــ SQL. يمكنك زيارة الدرس التالي إن كنت مهتم في كيفية استخراج البيانات من قواعد البيانات.
اختيار الأعمدة المناسبة لنموذج البيانات
المقصود بالأعمدة هي الصفات مثل جنس العميل، مدة اشتراكه، إلخ. حيث أن هناك صفات تأثر أكثر من الأخرى على مدى استمرارية العميل. حسنا، كيف أختار الأعمدة التي تساهم فعلا في توقع مَن مِن العملاء سوف يترك الشركة؟ إن الإجابة على هذا السؤال تطلب العديد من التجارب والخبرات، حيث أن الخبرة في المجال (Business Knowledge) الذي يعمل فيه عالم البيانات أمر مهم. ولتسريع عملية البحث، تستطيع الاجتماع مع الخبراء في الشركة لمساعدتك في تحديد الأعمدة التي تساهم في التوقع بشكل صحيح.
نموذج البيانات المستخدم (البيانات المستخدمة)
إن نموذج البيانات المستخدم في هذا الدرس جاهز. وهو موجود على موقع Kaggle.
هنا نجد المصدر الأصلي للبيانات
ولقد قمنا ببعض التعديلات الطفيفة على البيانات لتتناسب مع موضوع اليوم. ولذلك نرجو تحميل البيانات من هنا لتتبع خطوات الدرس بشكل صحيح.
الخطوة الأولى: استكشاف البيانات المستخدمة
Step One: Explore Dataعلينا الآن استكشاف نموذج البيانات المستخدم، ومعرفة ماهي الأعمدة (الصفات) التي سوف تستخدم في توقع من من العملاء سوف يترك الشركة
import pandas as pd
df = pd.read_csv('https://github.com/KAFSALAH/Fihm_Lessons/raw/master/ML_Series2_Classification/customer_churn.csv')
# General info about the data
print('——————————————————————————')
print('First 3 rows')
display(df.head(3))
—————————————————————————— First 3 rows
| customerID | gender | SeniorCitizen | Married | Dependents | tenure | PhoneService | MultipleLines | InternetService | OnlineSecurity | OnlineBackup | DeviceProtection | TechSupport | Contract | PaperlessBilling | PaymentMethod | MonthlyCharges | TotalCharges | Churn |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 7590-VHVEG | Female | No | Yes | No | 1 | No | No phone service | DSL | No | Yes | No | No | Month-to-month | Yes | Electronic check | 29.85 | 29.85 | No |
1 | 5575-GNVDE | Male | No | No | No | 34 | Yes | No | DSL | Yes | No | Yes | No | One year | No | Mailed check | 56.95 | 1889.5 | No |
2 | 3668-QPYBK | Male | No | No | No | 2 | Yes | No | DSL | Yes | Yes | No | No | Month-to-month | Yes | Mailed check | 53.85 | 108.15 | Yes |
Number of rows and columns
(7043, 19)
—————————————————————————— Types of columns RangeIndex: 7043 entries, 0 to 7042 Data columns (total 19 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 customerID 7043 non-null object 1 gender 7043 non-null object 2 SeniorCitizen 7043 non-null object 3 Married 7043 non-null object 4 Dependents 7043 non-null object 5 tenure 7043 non-null int64 6 PhoneService 7043 non-null object 7 MultipleLines 7043 non-null object 8 InternetService 7043 non-null object 9 OnlineSecurity 7043 non-null object 10 OnlineBackup 7043 non-null object 11 DeviceProtection 7043 non-null object 12 TechSupport 7043 non-null object 13 Contract 7043 non-null object 14 PaperlessBilling 7043 non-null object 15 PaymentMethod 7043 non-null object 16 MonthlyCharges 7043 non-null float64 17 TotalCharges 7043 non-null object 18 Churn 7043 non-null object dtypes: float64(1), int64(1), object(17) memory usage: 1.0+ MB
None
الآن، وبعد الاطلاع على الصفات المستخدمة، فلنقم بتلخيص تعريف كل صفة كالتالي
رقم مميز لكل عميل
جنس العميل
هل العميل كبير بالسن؟
هل العميل متزوج؟
هل هناك تابعين؟
مدة اشتراك العميل بالأشهر
هل العميل مشترك بخدمة الاتصال؟
هل العميل يستخدم أكثر من خط اتصال؟
نوع خدمة الإنترنت
هل لدى العميل خدمات حماية أون لاين؟
هل لدى العميل نسخة احتياطية أون لاين؟
هل لدى العميل ضمان؟
هل لدى العميل خدمة دعم فني؟
رقم مميز لكل عميل
نوع عقد العميل
هل الفواتير إلكترونية؟
طريقة سداد الفواتير
قيمة الفواتير الشهرية
مجموع قيمة فواتير العميل
الخطوة الثانية: معالجة البيانات
Step Two: Preprocess Dataأ- التحقق من القيم الناقصة أو المتكررة
في مرحلة معالجة البيانات، دعونا نتحقق أولا من عدم وجود قيم ناقصة أو صفوف متكررة (وجود صف متكرر يعني أن هناك عميل تكررت بياناته).
print("Number of duplicated values is ", df.duplicated().sum())
print("Number of missing values is", df.isnull().values.sum())
Number of duplicated values is 0 Number of missing values is 0
بعد أن تحققنا أنه لا يوجد صفوف متكررة، نستطيع التخلص من صفة customerID حيث أن هذه الميزة لا تأثر على التوقع.
df.drop('customerID', axis = 1, inplace = True) # 'Inplace' implies that data is modified in place
# Axis = 1 because we are dropping a column (0 in case we are dropping a row)
ب- ترميز الأعمدة الكتابية
فلنقم أيضا من التحقق من الأعمدة التي تحتوي على كتابات وتحويلها إلى أرقام من أجل بناء النموذج
# To identify object columns
print('Object columns are as follows')
object_cols = df.loc[:,df.dtypes==object].columns.tolist()
object_cols
Object columns are as follows
['gender', 'SeniorCitizen', 'Married', 'Dependents', 'PhoneService', 'MultipleLines', 'InternetService', 'OnlineSecurity', 'OnlineBackup', 'DeviceProtection', 'TechSupport', 'Contract', 'PaperlessBilling', 'PaymentMethod', 'TotalCharges', 'Churn']
نلاحظ أمرا غريبا. إن عمود TotalCharges مصنف على أنه عمود كتابي لا رقمي. وهذا أمر خاطئ حيث عند التحقق نجد أن العمود يحتوي على أرقام.
df.TotalCharges
0 29.85 1 1889.5 2 108.15 3 1840.75 4 151.65 ... 7038 1990.5 7039 7362.9 7040 346.45 7041 306.6 7042 6844.5 Name: TotalCharges, Length: 7043, dtype: object
هذه الأخطاء تحصل بالعادة عند عملية إدخال البيانات في قاعدة البيانات أو حين القراءة منها. نستطيع استخدام الكود الذي بالأسفل لتحويله إلى عمود رقمي.
df['TotalCharges'] = pd.to_numeric(df['TotalCharges'], errors='coerce')
# Check again if 'TotalCharges' is still considered as an object or not
print('Object columns are as follows')
object_cols = df.loc[:,df.dtypes==object].columns.tolist()
object_cols
Object columns are as follows
['gender', 'SeniorCitizen', 'Married', 'Dependents', 'PhoneService', 'MultipleLines', 'InternetService', 'OnlineSecurity', 'OnlineBackup', 'DeviceProtection', 'TechSupport', 'Contract', 'PaperlessBilling', 'PaymentMethod', 'Churn']
قمنا بتحويل نوع عمود TotalCharges من كتابي إلى رقمي بنجاح، ولكن هذه الخطوة قد ينتج بسببها بعض القيم المفقودة. حيث أن البرنامج سوف يقوم بترجمة الكتابة إلى رقم، فإن لم يستطع، سوف يترك الخلية فارغة. للتحقق من عدد الخلايا الفارغة التي نتجت نستطيع استخدام الكود التالي
df['TotalCharges'].isna().sum()
11
يوجد لدينا ١١ خلية فارغة. أي أننا خسرنا بيانات ١١ عميل في صفة التكلفة الإجمالية TotalCharges نستطيع تحديد ماهي الصفوف التي تحتوي على خلية فارغة في التكلفة الإجمالية وتعبئتها يدويا بناء على البيانات الموجودة قبل التحويل. ولكن لحفظ الوقت، نستطيع ملء الخلايا الفارغة بقيمة الوسيط لعمود التكلفة الإجمالية كحلٍ وسط يجمع ما بين الدقة والسرعة
# Replace missing values with the median
df.TotalCharges.fillna(df.TotalCharges.median(), inplace = True)
# Check again for missing values
df['TotalCharges'].isna().sum()
0
الآن، لترميز البيانات، سوف نقوم باستكشاف العناصر المميزة في كل عمود كتابي. وتذكر أن هناك نوعين في الأعمدة الكتابية
# Identify the number of unique elements in each object columns
for i in object_cols:
print('Number of unique elements in' , i, 'is', len(df.loc[:,i].unique()))
print(df.loc[:,i].unique())
Number of unique elements in gender is 2 ['Female' 'Male'] Number of unique elements in SeniorCitizen is 2 ['No' 'Yes'] Number of unique elements in Married is 2 ['Yes' 'No'] Number of unique elements in Dependents is 2 ['No' 'Yes'] Number of unique elements in PhoneService is 2 ['No' 'Yes'] Number of unique elements in MultipleLines is 3 ['No phone service' 'No' 'Yes'] Number of unique elements in InternetService is 3 ['DSL' 'Fiber optic' 'No'] Number of unique elements in OnlineSecurity is 3 ['No' 'Yes' 'No internet service'] Number of unique elements in OnlineBackup is 3 ['Yes' 'No' 'No internet service'] Number of unique elements in DeviceProtection is 3 ['No' 'Yes' 'No internet service'] Number of unique elements in TechSupport is 3 ['No' 'Yes' 'No internet service'] Number of unique elements in Contract is 3 ['Month-to-month' 'One year' 'Two year'] Number of unique elements in PaperlessBilling is 2 ['Yes' 'No'] Number of unique elements in PaymentMethod is 4 ['Electronic check' 'Mailed check' 'Bank transfer (automatic)' 'Credit card (automatic)'] Number of unique elements in Churn is 2 ['No' 'Yes']
والآن سوف نقوم بالترميز عن طريق ثلاثة طرق
ترميز الأعمدة المرتبة
“Mapping” for ordinal columns
ترميز الأعمدة المرتبة يبدأ بصفر لأقل قيمة. ثم يرتفع، على سبيل المثال عمود مشتركي خدمة الإنترنت. إن لم يكن مشترك نعطيه قيمة 0. إن كان مشترك في خدمة (الدي إس إل) نعطيه 1. وإن كان مشترك في الألياف البصرية نعطيه 2.“LabelEncoder” for binary nominal columns
ترميز الأعمدة الثنائية يكون عن طريق الـ 0، 1. الأعمدة الثنائية مثل جنس العميل وهل متزوج أم لا، لا يوجد فيها أهمية كبيرة للترتيب أيهم يأخذ قيمة ال0 وأيهم ال1.“get_dummies” for non-binary nominal columns
ترميز الأعمدة الغير المرتبة الغير ثناية عن طريق صناعة عمود خاص لكل عنصر سوف نقوم باستخدام هذه الطريقة لترميز عمود PaymentMethod حيث أنه عمود غير ثنائي ولا يحتوي على ترتيب أيضا.
# Firstly, we map ordinal columns
df.Churn = df.Churn.map({'No':0, 'Yes':1}) # We will annotate churned customers with 1
#---
df.InternetService = df.InternetService.map({'No':0, 'DSL':1, 'Fiber optic':2})
df.MultipleLines = df.MultipleLines.map({'No phone service':0, 'No':1, 'Yes':2})
df.Contract = df.Contract.map({'Month-to-month':0,'One year':1,'Two year':2})
#---
## For similar ordinal columns, we can map them using a for loop
similar_ordinal_cols = ['OnlineSecurity','OnlineBackup','DeviceProtection','TechSupport']
for i in similar_ordinal_cols:
df[i] = df[i].map({'No internet service':0, 'No' :1, 'Yes': 2})
الآن، فلنقم بالتحقق من الأعمدة المتبقية والتي تمثل الأعمدة الكتابية الغير مرتبة
print('Object columns are as follows')
object_cols = df.loc[:,df.dtypes==object].columns.tolist()
object_cols
Object columns are as follows
['gender', 'SeniorCitizen', 'Married', 'Dependents', 'PhoneService', 'PaperlessBilling', 'PaymentMethod']
جميع هذه الصفات هي صفات ثنائية (نعم أو لا) ما عدا PaymentMethod لذلك سوف نقوم بترميز PaymentMethod عن طريق get_dummies والأعمدة الثنائية عن طريق LabelEncoder
ترميز الأعمدة الثنائية
# Now, we LabelEncode binary nominal columns
object_cols.remove('PaymentMethod') # Remove it to later encode it using get_dummies
from sklearn.preprocessing import LabelEncoder
labelencoder = LabelEncoder()
for i in object_cols:
df[i] = labelencoder.fit_transform(df[i])
ترميز الأعمدة الغير مرتبة الغير ثنائية
# Lastly, we encode 'PaymentMethod' column using get_dummies
encoded_df = pd.get_dummies(df, drop_first = True) # drop_first to avoid data redundancy
فلنتحقق الآن من شكل البيانات الحالي بعد عملية الترميز
encoded_df.head()
| gender | SeniorCitizen | Married | Dependents | tenure | PhoneService | MultipleLines | InternetService | OnlineSecurity | OnlineBackup | DeviceProtection | TechSupport | Contract | PaperlessBilling | MonthlyCharges | TotalCharges | Churn | PaymentMethod_Credit card (automatic) | PaymentMethod_Electronic check | PaymentMethod_Mailed check |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 1 | 1 | 2 | 1 | 1 | 0 | 1 | 29.85 | 29.85 | 0 | 0 | 1 | 0 |
1 | 1 | 0 | 0 | 0 | 34 | 1 | 1 | 1 | 2 | 1 | 2 | 1 | 1 | 0 | 56.95 | 1889.5 | 0 | 0 | 0 | 1 |
2 | 1 | 0 | 0 | 0 | 2 | 1 | 1 | 1 | 2 | 2 | 1 | 1 | 0 | 1 | 53.85 | 108.15 | 1 | 0 | 0 | 1 |
3 | 1 | 0 | 0 | 0 | 45 | 0 | 0 | 1 | 2 | 1 | 2 | 2 | 1 | 0 | 42.3 | 1840.75 | 0 | 0 | 0 | 0 |
4 | 0 | 0 | 0 | 0 | 2 | 1 | 1 | 2 | 1 | 1 | 1 | 1 | 0 | 1 | 70.7 | 151.65 | 1 | 0 | 1 | 0 |
نلاحظ أن العمود المستهدف Churn أصبح في منتصف الجدول، فلنقم بتحريكه إلى أقصى يمين العمود من أجل المحافظة على شكل الجدول بشكل مناسب حيث أن العرف أن العمود المستهدف يكون في أقصى اليمين
last_column = encoded_df.pop('Churn')
encoded_df.insert(df.shape[1]+1, 'Churn', last_column)
encoded_df.head(3)
| gender | SeniorCitizen | Married | Dependents | tenure | PhoneService | MultipleLines | InternetService | OnlineSecurity | OnlineBackup | DeviceProtection | TechSupport | Contract | PaperlessBilling | MonthlyCharges | TotalCharges | PaymentMethod_Credit card (automatic) | PaymentMethod_Electronic check | PaymentMethod_Mailed check | Churn |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 1 | 1 | 2 | 1 | 1 | 0 | 1 | 29.85 | 29.85 | 0 | 1 | 0 | 0 |
1 | 1 | 0 | 0 | 0 | 34 | 1 | 1 | 1 | 2 | 1 | 2 | 1 | 1 | 0 | 56.95 | 1889.5 | 0 | 0 | 1 | 0 |
2 | 1 | 0 | 0 | 0 | 2 | 1 | 1 | 1 | 2 | 2 | 1 | 1 | 0 | 1 | 53.85 | 108.15 | 0 | 0 | 1 | 1 |
الخطوة الثالثة: تحضير البيانات من أجل تدريب النماذج
Step Three: Prepare Data for Machine Learning Trainingأ- فصل البيانات
كما أشرنا سابقا في الجزء الأول، نحن نقوم بفصل البيانات إلى جزئين من أجل اختبار دقة النموذج. فلننظر إلى كيفية توزيع العملاء في عمود الـ Churn كما يلي
print('Customer Churn distribution')
display(encoded_df.Churn.value_counts())
encoded_df.Churn.value_counts(normalize = False).plot(kind= 'bar', color = 'darkblue');
Customer Churn distribution
0 5174 1 1869 Name: Churn, dtype: int64
print('Customer Churn Percentages')
display(encoded_df.Churn.value_counts(normalize = True))
Customer Churn Percentages
0 0.73463 1 0.26537 Name: Churn, dtype: float64
نلاحظ أنه يوجد 5,174 عميل ما زال مستمرا مع الشركة، ومن الناحية الأخرى يوجد لدينا معلومات 1,869 عميل قد قام بترك الشركة. وحين النظر إلى النسب، نجد أن نسبة الذين تركوا الشركة هي 26.5%. ومن أجل اختبار دقة النموذج بشكل فعال، نقوم بالمحافظة على هذه النسب في كلا الجدولين (جدول التدريب وجدول الاختبار) ومن أجل القيام بذلك نقوم باستخدام وظيفة StratifiedShuffleSplit والتي تقوم بالحفاظ على نسبة توزيع العمود بالمستهدف
feature_cols = encoded_df.columns[:-1] # select feature columns (Independent columns)
from sklearn.model_selection import StratifiedShuffleSplit
# Get the split indexes
strat_shuf_split = StratifiedShuffleSplit(n_splits=1,
test_size=0.25,
random_state=123) # Taking 25% of the dataset as the test set
# We use 'next' to get the arrays from the generator object.
train_idx, test_idx = next(strat_shuf_split.split(encoded_df[feature_cols], encoded_df.Churn))
# Create the splits
X_train = encoded_df.loc[train_idx, feature_cols] # loc[index,column]
y_train = encoded_df.loc[train_idx, 'Churn']
X_test = encoded_df.loc[test_idx, feature_cols]
y_test = encoded_df.loc[test_idx, 'Churn']
كما في الدرس الماضي، أصبح لدنيا أربعة متغيرات كالتالي
- متغيرات التدريب هي X_train, y_train
- متغيرات الاختبار هي x_test, y_test
للتحقق من أن توزيع العمود المستهدف في كلا من مجموعتي التدريب والاختبار متساوي
y_train.value_counts(normalize=True)
0 0.73457 1 0.26543 Name: Churn, dtype: float64
y_test.value_counts(normalize=True)
0 0.73481 1 0.26519 Name: Churn, dtype: float64
ب- إعادة وزن قيم الأعمدة
الآن سوف نقوم بالخطوة الأخيرة قبل تدريب النماذج وهي إعادة وزن الأعمدة. وذلك مثل ما ذكرنا في الجزء الأول من أجل جعل الأعمدة ذات أثر متساوي في بناء النموذج
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)
الخطوة الرابعة: تدريب النماذج وتقييم أداؤها
Step Four: Train and Evaluate the ML Modelsسوف نقوم بتدريب ثلاثة نماذج وتقييم أداؤها، هذه النماذج سوف تكون باستخدام الخوارزميات التالية
خوارزمية تتوقع التصنيف بناء على تصنيف العملاء القريبين (الجيران)
خوارزمية تتوقع التصنيف بناء على فصل المجموعات
خوارزمية تتوقع احتمالية التصنيف
أيضا، كما أنه هنالك معاير لتقييم أداء نماذج الانحدار الخطي Linear Regression، هناك أيضا معاير لتقييم الأداء في نماذج التصنيف Classification، والمعاير المستخدمة في هذا المشروع هي Accuracy Precision Recall f1-score.
ونحن ننصح بالاطلاع على معنى كل معيار والغرض منه من خلال زيارة هذا الثريد المميز الذي قام به أحد أعضاء فريق فهم "محمد عثمان".
خلال هذا الثريد سأشرح أحد أهم مفاهيم قياس الأداء في نماذج التصنيف classification في #التعلم_الآلي ML مفهوم مصفوفة الالتباس(الحيرة) Confusion Matrix
— محمد عثمان | Data-AI 👨🏻💻📊 (@Science10S) February 15, 2022
1/18 pic.twitter.com/nbgJDwK7DJ
from sklearn.neighbors import KNeighborsClassifier
knn = KNeighborsClassifier(n_neighbors=7) # We will use 7 neighbors, feel free to try other values
knn.fit(X_train, y_train)
y_preds = knn.predict(X_test)
from sklearn.metrics import accuracy_score, classification_report
print(classification_report(y_test, y_preds))
print('-------------')
print('Accuracy score: ', round(accuracy_score(y_test, y_preds), 2))
precision recall f1-score support 0 0.83 0.85 0.84 1294 1 0.56 0.52 0.54 467 accuracy 0.76 1761 macro avg 0.70 0.69 0.69 1761 weighted avg 0.76 0.76 0.76 1761 ------------- Accuracy score: 0.76
from sklearn.svm import SVC
SVM_Model = SVC(kernel='rbf',gamma=1)
SVM_Model.fit(X_train, y_train)
y_preds = SVM_Model.predict(X_test)
print(classification_report(y_test, y_preds))
print('-------------')
print('Accuracy score: ', round(accuracy_score(y_test, y_preds), 2))
precision recall f1-score support 0 0.79 0.93 0.86 1294 1 0.63 0.33 0.43 467 accuracy 0.77 1761 macro avg 0.71 0.63 0.64 1761 weighted avg 0.75 0.77 0.74 1761 ------------- Accuracy score: 0.77
للتنويه
على الرغم من تشابه اسم Logistic Regression مع Linear Regression إلا أن الـ Logistic Regression يُستعمل فقط في التصنيف وليس في توقع القيم المتصلة على غرار الانحدار الخطي.
from sklearn.linear_model import LogisticRegression
lr = LogisticRegression(solver='liblinear')
lr.fit(X_train, y_train)
lr_preds = lr.predict(X_test)
lr_prob = lr.predict_proba(X_test).max(axis=1)
Logistic_Results = (pd.DataFrame([lr_preds,lr_prob])
.T.rename(columns={0:'Prediction',1:'Probability'}))
Logistic_Results.head(10)
| Prediction | Probability |
---|---|---|
0 | 1 | 0.743765 |
1 | 0 | 0.958825 |
2 | 0 | 0.996193 |
3 | 0 | 0.834216 |
4 | 0 | 0.953707 |
5 | 1 | 0.506502 |
6 | 0 | 0.77516 |
7 | 0 | 0.692961 |
8 | 0 | 0.722585 |
9 | 0 | 0.993586 |
print(classification_report(y_test, lr_preds))
print('-------------')
print('Accuracy score: ', round(accuracy_score(y_test, lr_preds), 2))
precision recall f1-score support 0 0.84 0.90 0.87 1294 1 0.65 0.52 0.58 467 accuracy 0.80 1761 macro avg 0.75 0.71 0.72 1761 weighted avg 0.79 0.80 0.79 1761 ------------- Accuracy score: 0.8
نلاحظ أن أحد مزايا خوارزمية Logistic Regression هي القدرة على إعطاء التوقع مع احتمالية حصوله. فمثلا، لقد توقع النموذج أن العميل الأول في مجموعة الاختبار سوف يترك الشركة واحتمالية حصول هذا الأمر هي 0.74
خطوة إضافية: تحسين معدل الاسترجاع
Bonus Step: Improve Recall Scoreنلاحظ أن نموذج الـ Logistic Regression قد أحرز أعلى نتيجة Accuracy = 80%. إلا أن نماذجنا الحالية ما زالت تعاني عند توقع الـ Churn بشكل دقيق بسبب أن الـ Recall منخفض وللتوضيح أكثر، إن معيار recall يساوي 0.52 عند توقع الـ1، أي حين توقع مَن مِن العملاء سوف يترك الشركة [Churn Prediction] مما يعني أن هناك 52% من الذين تركوا الشركة توقعناهم بشكل صحيح أنهم تركوا الشركة. وهناك 48% من الذين تركوا الشركة لم نستطع توقع تركهم. إحد أسباب هذه المشكلة هي وجود صفوف قليلة لمن ترك الشركة مقارنة بصفوف الموجودين حيث أن نسبة صفوف الذين تركوا الشركة هي 26.5% فقط. وهذه مشكلة معروفة بـ Imbalanced target feature.
إحدى حلول هذه المشكلة هي توليد صفوف صناعية تكون مشابهة لصفوف العملاء الذين تركوا الشركة وذلك من أجل موازنة توزيع قيم العمود المستهدف كالتالي
from imblearn.over_sampling import SMOTE
Smote = SMOTE(random_state=123)
X_Smote, y_Smote = Smote.fit_resample(X_train, y_train)
lr = LogisticRegression(solver='liblinear')
lr.fit(X_Smote, y_Smote)
lr_preds = lr.predict(X_test)
print(classification_report(y_test, lr_preds))
print('-------------')
print('Accuracy score: ', round(accuracy_score(y_test, lr_preds), 2))
precision recall f1-score support 0 0.91 0.75 0.82 1294 1 0.53 0.78 0.63 467 accuracy 0.76 1761 macro avg 0.72 0.77 0.72 1761 weighted avg 0.81 0.76 0.77 1761 ------------- Accuracy score: 0.76
نلاحظ أن دقة النموذج الكلية قد انخفضت إلى 76% ولكن دقة الـ Recall قد ارتفعت إلى 78% وهو مؤشر جيد؛ لأن حين توقع مَن مِن العملاء سوف يترك الشركة، معيار الـ Recall يعتبر هو ذو الأهمية الكبرى.
نصيحة فهم للتعلم
في هذا الدرس تحدثنا عن النوع الثاني من أنواع تعلم الآلة الخاضع للإشراف ألا وهو التصنيف. نصيحة فهم هي التعرف على نقاط القوة والضعف للخوارزميات المستعملة في هذا الدرس. بالإضافة إلى التعرف إلى معايير تقييم جودة نماذج التصنيف مثل الدقة والاسترجاع. يمكنكم أيضا زيارة موقع Kaggle لتجربة حلول مشاريع أخرى باستخدام خوارزميات التصنيف لتثبيت ما تم تعلمه.
ماذا تعلمنا؟
- خوارزميات التصنيف تستعمل بكثرة في مجالات متعددة مثل الطب والصناعة
- اكتشفنا مرة أخرى أن معالجة البيانات بشكل مناسب هي الجزء الأهم لضمان جودة النماذج
- بعض خوارزميات التصنيف تعطي التوقع مع احتمالية حدوث التوقع مثل خوارزمية Logistic Regression
- حين فصل البيانات من أجل التدريب والاختبار. يستحسن المحافظة على توزيع القيم في العمود المستهدف من خلال StratifiedShuffleSplit
- معايير تقييم أداء نماذج التصنيف متعددة، وتختلف أهمية المعيار بحسب التطبيق
- عند توقع من من العملاء سوف يترك الشركة، يكون معيار الاسترجاع هو أهم معيار Recall Metric