EduFlow

الوحدة 1 · ما هو تعلّم الآلة

النماذج في scikit-learn: fit وpredict وscore

18 دقراءة 4 دتمرين Python

الدرس في لمحة

استخدم أي نموذج في scikit-learn عبر الاستدعاءات الثلاثة نفسها (fit وpredict وscore)، واقرأ ما تعلّمه النموذج بعد التدريب، واكتشف الحفظ بمقارنة نتيجتي التدريب والاختبار.

  1. 1ثلاثة استدعاءات
  2. 2ما الذي يعرفه النموذج بعد التدريب
  3. 3نتيجة التدريب تكذب
  4. 4العقد نفسه، والافتراضات مختلفة

لا فيديو هنا، بل الشيء الحقيقي: درس مكتوب وأمثلة قابلة للتشغيل واختبار وتدريب.

يأتي scikit-learn بعشرات النماذج، من الخطوط المستقيمة إلى غابات الأشجار، وتستخدمها جميعًا بالطريقة نفسها. تعلّم ثلاثة استدعاءات وستستطيع تجربة أيّ منها على بيانات الانقطاع في بضعة أسطر. هذا الاتساق هو السبب الرئيسي في كون scikit-learn المكتبة الافتراضية لتعلّم الآلة الكلاسيكي.

ثلاثة استدعاءات

النموذج في scikit-learn هو estimator (مقدِّر): كائن Python تنشئه بإعداداته، وتدرّبه بـ fit، وتستخدمه بـ predict وscore.

Python
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier

churn = pd.read_csv("churn.csv")
features = ["tenure_days", "orders", "total_spent", "avg_order_value",
            "days_since_last_order", "used_coupon", "support_tickets", "mobile_share"]
X, y = churn[features], churn["churned"]
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.25, random_state=42, stratify=y)

model = DecisionTreeClassifier(max_depth=3, random_state=42)  # 1. create
model.fit(X_train, y_train)                                    # 2. learn
print(model.predict(X_test)[:10])                              # 3. use
print("train accuracy:", round(model.score(X_train, y_train), 3))
print("test accuracy: ", round(model.score(X_test, y_test), 3))

ما يفعله كل استدعاء:

  • الـ constructor يأخذ الـ hyperparameters: خيارات تحدّدها قبل التدريب. max_depth=3 تعني أن الشجرة لا تطرح أكثر من ثلاثة أسئلة متتالية. وrandom_state=42 تجعل كسر التعادلات قابلًا للتكرار.
  • fit(X_train, y_train) تنظر في الأمثلة وتتعلّم معاملات (parameters) النموذج. في الشجرة، هذه المعاملات هي الأسئلة والعتبات (“هل days_since_last_order أكبر من 236.5؟”). وتُعيد النموذج نفسه، لذا يعمل أيضًا model = DecisionTreeClassifier().fit(X, y).
  • predict(X) تُعيد تنبؤًا واحدًا لكل صف: وهنا مصفوفة من الأصفار والآحاد.
  • score(X, y) تتنبأ ثم تقارن بالإجابات الحقيقية. في المصنِّفات تُعيد الـ accuracy (الدقة)، أي نسبة التنبؤات الصحيحة. وفي نماذج الانحدار تُعيد R²، الذي ستتعرّف عليه في القسم 2.
يُنشأ الـ estimator بالـ hyperparameters، ثم يُدرَّب على بيانات التدريب ليتعلّم معاملات تُخزَّن في سمات تنتهي بشرطة سفلية، ثم يُستخدم للتنبؤ على بيانات جديدة أو لحساب النتيجة مقابل إجابات معروفة. Model(max_depth=3) hyperparameters fit نموذج مدرَّب classes_, tree_ … predict(X) score(X, y)
دورة حياة الـ estimator

ما الذي يعرفه النموذج بعد التدريب

كل ما يتعلّمه النموذج أثناء fit يُخزَّن في سمات (attributes) تنتهي أسماؤها بشرطة سفلية. وهي غير موجودة قبل fit؛ فطلب إحداها يرفع AttributeError، واستدعاء predict على نموذج غير مدرَّب يرفع NotFittedError.

Python
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier

churn = pd.read_csv("churn.csv")
features = ["tenure_days", "orders", "total_spent", "avg_order_value",
            "days_since_last_order", "used_coupon", "support_tickets", "mobile_share"]
X_train, X_test, y_train, y_test = train_test_split(
    churn[features], churn["churned"], test_size=0.25, random_state=42,
    stratify=churn["churned"])
model = DecisionTreeClassifier(max_depth=3, random_state=42).fit(X_train, y_train)

print(model.classes_)            # the labels it saw: [0 1]
print(model.n_features_in_)      # 8
print(model.feature_names_in_[:3])

new_customer = pd.DataFrame([{
    "tenure_days": 400, "orders": 2, "total_spent": 180.0, "avg_order_value": 90.0,
    "days_since_last_order": 150, "used_coupon": 0, "support_tickets": 1,
    "mobile_share": 0.5}])
print(model.predict(new_customer))        # [1]: likely to churn
print(model.predict_proba(new_customer))  # [[0.36 0.64]]

تعطيك predict_proba الاحتمال الذي يقدّره النموذج لكل فئة، بترتيب classes_. هذا العميل يحصل على 64% للانقطاع. ستعتمد على الاحتمالات كثيرًا في القسم 3، لأنها تتيح لك أن تختار مقدار الحذر الذي تريده.

نتيجة التدريب تكذب

أزل الآن حدّ العمق وراقب ما يحدث. الشجرة غير المحدودة تواصل طرح الأسئلة حتى يجلس كل عميل من عملاء التدريب في ورقة لا تحوي إلا فئته.

Python
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
from sklearn.neighbors import KNeighborsClassifier

churn = pd.read_csv("churn.csv")
features = ["tenure_days", "orders", "total_spent", "avg_order_value",
            "days_since_last_order", "used_coupon", "support_tickets", "mobile_share"]
X_train, X_test, y_train, y_test = train_test_split(
    churn[features], churn["churned"], test_size=0.25, random_state=42,
    stratify=churn["churned"])

for model in [DecisionTreeClassifier(max_depth=3, random_state=42),
              DecisionTreeClassifier(random_state=42),
              KNeighborsClassifier(n_neighbors=5)]:
    model.fit(X_train, y_train)
    print(f"{model!r:55} train {model.score(X_train, y_train):.2f}  test {model.score(X_test, y_test):.2f}")

الشجرة غير المحدودة مثالية على العملاء الـ 298 الذين درستهم، وأسوأ من الشجرة الضحلة على المئة الذين لم ترَهم. لقد حفظت خصوصيات العملاء الأفراد. هذه الفجوة بين أداء التدريب وأداء الاختبار هي الـ overfitting (الإفراط في التوافق)، وستقضي جزءًا كبيرًا من القسم 2 في تعلّم كيفية التحكم فيها.

لاحظ أيضًا النموذج الثالث. خوارزمية أقرب k جيران (k-nearest neighbours) لا تشبه الشجرة في شيء (فهي تتنبأ بالنظر إلى أكثر خمسة عملاء تدريب تشابهًا)، ومع ذلك الكود متطابق. تبديل النموذج يكلّفك سطرًا واحدًا.

العقد نفسه، والافتراضات مختلفة

الواجهة المشتركة تُخفي أفكارًا مختلفة جدًا عن العالم. فالشجرة تفترض أن الانقطاع يمكن وصفه بعدد قليل من أسئلة نعم/لا ذات حدود فاصلة حادة. وأقرب k جيران تفترض أن العملاء المتشابهين يتصرّفون بالطريقة نفسها، حيث “التشابه” يعني القرب حين يُعامَل كل عمود كمسافة. لا أحد الافتراضين صحيح بإطلاق؛ كلٌّ منهما مفيد في مواقف وخاطئ في أخرى. ولهذا تجرّب عدة نماذج على التقسيم نفسه بدل أن تتجادل حول أيّها يجب أن يفوز.

نماذج الانحدار تلتزم العقد نفسه. DecisionTreeRegressor وLinearRegression تأخذان y رقميًا، وتُعيد predict أرقامًا بدل الفئات، وتُعيد score قيمة R² بدل الـ accuracy. كل ما تتعلّمه عن estimator واحد ينتقل إلى البقية.

تفصيلتان توفّران عليك الارتباك لاحقًا. استدعاء fit مرة ثانية يمحو ما تعلّمه النموذج من قبل ويبدأ من الصفر؛ ولا يضيف إلى معرفته القديمة. والإعدادات التي مرّرتها إلى الـ constructor تبقى مقروءة كسمات عادية (model.max_depth)، فيمكنك دائمًا التحقق مما طُلب من نموذج مدرَّب أن يفعله.

لديك الآن نموذج بدقة 73% على بيانات الاختبار. هل هذا جيد؟ لا يمكنك الحكم بعد، لأنك لم تسأل كم سيحقق نموذج بلا أي ذكاء على الإطلاق. هذا هو موضوع الدرس القادم.

أهم النقاط

  • كل estimator في scikit-learn يُنشأ بإعداداته، ويتعلّم في fit(X, y)، ويُستخدم عبر predict(X) وscore(X, y).
  • الـ hyperparameters مثل max_depth تحدّدها أنت قبل التدريب؛ أما المعاملات (parameters) مثل عتبات التقسيم فتُتعلَّم أثناء fit.
  • سمات (attributes) النموذج المدرَّب تنتهي بشرطة سفلية (classes_ وfeature_names_in_) ولا توجد إلا بعد fit.
  • تُعيد score قيمة الـ accuracy للمصنِّفات وقيمة R² لنماذج الانحدار.
  • الفجوة الكبيرة بين نتيجتي التدريب والاختبار تعني أن النموذج حفظ صفوف تدريبه.

قراءات إضافية

تحقّق من فهمك

4 أسئلة

السؤال 1 من 4

ماذا يطبع هذا الكود؟

Python
from sklearn.tree import DecisionTreeClassifier
model = DecisionTreeClassifier(max_depth=3)
print(model.classes_)
السؤال 2 من 4

شجرة تحقق 1.00 على بيانات التدريب و0.72 على بيانات الاختبار. وشجرة بعمق 3 تحقق 0.78 و0.73. أيّهما النموذج الأفضل لـ Cartwheel؟

السؤال 3 من 4

أيّ هذه القيم hyperparameter وليست معاملًا متعلَّمًا؟

السؤال 4 من 4

درّبت نموذجًا على DataFrame أعمدته بترتيب معيّن، ثم استدعيت predict على DataFrame فيه الأعمدة نفسها بترتيب مختلف. ماذا يحدث؟

تدريب

تمرين Python

درّب شجرتين وقارن بينهما

درّب شجرتي قرار على بيانات تدريب الانقطاع وشاهد الحفظ بالأرقام.

  1. درّب shallow (max_depth=3) وdeep (بلا حدّ للعمق)، وكلتاهما بـ random_state=42.
  2. اجعل shallow_test دقة الشجرة الضحلة على بيانات الاختبار، وdeep_train / deep_test دقة الشجرة العميقة على بيانات التدريب والاختبار.
  3. اجعل new_pred تنبؤ الشجرة الضحلة (0 أو 1) للعميل new_customer.

ملفات البيانات: churn.csv

import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier

churn = pd.read_csv("churn.csv")
features = ["tenure_days", "orders", "total_spent", "avg_order_value",
            "days_since_last_order", "used_coupon", "support_tickets", "mobile_share"]
X_train, X_test, y_train, y_test = train_test_split(
    churn[features], churn["churned"], test_size=0.25, random_state=42,
    stratify=churn["churned"])

new_customer = pd.DataFrame([{
    "tenure_days": 120, "orders": 6, "total_spent": 940.0, "avg_order_value": 156.7,
    "days_since_last_order": 12, "used_coupon": 1, "support_tickets": 0,
    "mobile_share": 0.8}])

# 1. A tree limited to depth 3, and a tree with no depth limit (both random_state=42)
shallow = DecisionTreeClassifier(max_depth=3, random_state=42)
deep = DecisionTreeClassifier(random_state=42)

# 2. Fit both on the training data, then fill in the scores
shallow_test = None
deep_train = None
deep_test = None

# 3. The shallow tree's prediction (0 or 1) for new_customer
new_pred = None
جارٍ تحميل المحرّر…

Ctrl/⌘ + Enter للتشغيل · يعمل في بيئة معزولة داخل متصفّحك. لا شيء يغادر جهازك.

(لا مخرجات)

الفحوص

  • الشجرتان مدرَّبتان بإعدادات العمق الصحيحة
  • shallow_test هي دقة الشجرة الضحلة على بيانات الاختبار
  • deep_train وdeep_test هما نتيجتا الشجرة العميقة
  • new_pred هو تنبؤ الشجرة الضحلة للعميل new_customer