تقسيم البيانات
درس 3 من 8
3تقسيم البيانات
الصورة الكبيرةرقم تقيسه على بيانات رآها النموذج لا يقول شيئًا عن المستقبل. التقسيم الصحيح هو ما يجعل أرقامك وعدًا صادقًا، والتسريب هو ما يحوّلها إلى كذبة مقنعة.
ماذا تشاهد في الرسم: البيانات تمرّ بثلاثة أجزاء بترتيب ثابت: التدريب يتعلّم منه النموذج، والتحقّق تختار عليه، والاختبار يُفتح مرة واحدة في النهاية. إذا اخترت على الاختبار، صار رقمه متفائلًا ولم يعد وعدًا صادقًا.
خارطة هذا الجزء
المفاهيم بالترتيب، اضغط أيًّا منها للقفز إليه:
طريقان إلى نفس الفكرة
لو أعطيت الطالب أسئلة الامتحان نفسها ليتدرّب عليها، ستكون درجته عالية ولن تعرف إن كان فهم. النموذج كذلك. لهذا نقسم البيانات: جزء يتدرّب عليه، وجزء نختار به الإعدادات، وجزء نخفيه حتى النهاية ونمتحنه عليه مرة واحدة.
تقسيم train وvalidation وtest، أو التحقق المتقاطع k-fold. التقسيم الطبقي stratified يحفظ نسب الفئات، والتقسيم الزمني يمنع رؤية المستقبل. والتسريب data leakage (مثل حساب التطبيع على كل البيانات قبل التقسيم) يجعل كل رقم بعده كاذبًا.
قبل أن تبدأ
🎯خط الأساس
تعرف الدقة وخط الأساس الساذج من درس التأطير.
📏الانحدار اللوجستي
تدرّب نموذجًا بـ fit وتقيسه بـ score، كما في درس الانحدار.
الفكرة الجوهرية
الرقم الذي تقيسه على بيانات رآها النموذج لا يقول شيئًا عن المستقبل. تبني هنا التقسيم إلى ثلاثة أجزاء، ثم ترى كيف يتسرّب الجواب، ثم تقيس بأمان بالتحقّق المتقاطع، ثم تقسم بالزمن.
1ثلاثة أجزاء، لكل جزء وظيفة
التدريب لتعلّم الأوزان، والتحقّق لاختيار النموذج والإعدادات، والاختبار لرقم نهائي واحد. انظر إلى الرسم: عدد الصفوف في كل جزء من بيانات سرطان الثدي.
التدريب يتعلّم منه النموذج. التحقّق تختار عليه بين النماذج والإعدادات. الاختبار تفتحه مرة واحدة في النهاية لتعرف الرقم الذي ستعلنه. نقسم بـtrain_test_split مرتين، مع stratify حتى تبقى نسبة الفئات واحدة في الأجزاء الثلاثة.
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
X, y = load_breast_cancer(return_X_y=True)
X_rest, X_test, y_rest, y_test = train_test_split(X, y, test_size=0.2, random_state=0, stratify=y)
X_train, X_val, y_train, y_val = train_test_split(X_rest, y_rest, test_size=0.25, random_state=0, stratify=y_rest)
for name, part in [("train", y_train), ("val", y_val), ("test", y_test)]:
print(f"{name:5} rows={len(part):3} positive={part.mean():.2f}")
60% و20% و20%، ونسبة الحالات الإيجابية 0.62 إلى 0.63 في كل جزء. بدون stratify قد يقع جزء صغير بنسبة مختلفة بالصدفة، فتقارن نماذجك على بيانات لا تشبه الواقع.
2لماذا لا يكفي جزءان
الاختيار نفسه نوع من التدريب. انظر إلى الرسم: تجرّب إعدادات كثيرة وتختار الأفضل على التحقّق، ولا تصل إلى الاختبار إلا مرة واحدة.
إن جرّبت عشرين إعدادًا واخترت الأفضل على بيانات الاختبار، فقد «درّبت» اختيارك عليها. الرقم الفائز متفائل لأنك انتقيته من بين عشرين محاولة. جزء التحقّق يمتصّ هذا الانتقاء، ويبقى الاختبار نظيفًا.
3التسريب: نموذج من ضجيج
التسريب هو وصول معلومة من بيانات التقييم إلى التدريب. أخطر أشكاله معالجة تحدث قبل التقسيم.
بيانات عشوائية تمامًا: ألفا عمود من أرقام عشوائية، ومخرج عشوائي. لا يوجد أي نمط حقيقي، فأي نموذج صادق يجب أن يحصل على نحو 50%. لاحظ ماذا يحدث عندما نختار «أفضل» عشرة أعمدة قبل التقسيم.
import numpy as np
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
rng = np.random.default_rng(0)
X = rng.normal(size=(100, 2000))
y = rng.integers(0, 2, size=100)
def top_features(X, y, k=10):
corr = np.abs([np.corrcoef(X[:, j], y)[0, 1] for j in range(X.shape[1])])
return np.argsort(corr)[-k:]
best = top_features(X, y)
X_tr, X_te, y_tr, y_te = train_test_split(X[:, best], y, test_size=0.5, random_state=0)
leaky = LogisticRegression().fit(X_tr, y_tr).score(X_te, y_te)
X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.5, random_state=0)
best = top_features(X_tr, y_tr)
honest = LogisticRegression().fit(X_tr[:, best], y_tr).score(X_te[:, best], y_te)
print(f"leaky accuracy on pure noise: {leaky:.2f}")
print(f"honest accuracy on pure noise: {honest:.2f}")
74% على ضجيج خالص. السبب: اختيار الأعمدة رأى الاختبار، فاختار الأعمدة التي تصادف أنها ترتبط بمخرجات الاختبار. أما الطريقة الصادقة، التي تختار من نصف التدريب فقط، فتعطي 46%، أي الصدفة كما يجب. لو نشرت النموذج الأول لانهار فورًا.
انظر إلى الرسم مرة أخرى: الكود يعمل والرقم ممتاز، ولا شيء يقول إن هناك خطأ. لذلك تبحث عن التسريب عمدًا، والقاعدة بسيطة: قسّم أولًا، ثم افعل كل شيء آخر على التدريب وحده.
4التحقّق المتقاطع
رقم واحد من تقسيم واحد قد يكون حظًّا. التحقّق المتقاطع يقسم البيانات إلى خمسة أجزاء، ويجعل كل جزء تحقّقًا مرة.
make_pipeline يربط المعالجة بالنموذج، فيُحسب التوحيد داخل كل جزء تدريب على حدة. هذه الطريقة الآمنة الوحيدة لاستخدام التحقّق المتقاطع مع أي معالجة.
from sklearn.datasets import load_breast_cancer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_score
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
X, y = load_breast_cancer(return_X_y=True)
model = make_pipeline(StandardScaler(), LogisticRegression(max_iter=1000))
scores = cross_val_score(model, X, y, cv=5)
print("folds:", [round(float(s), 3) for s in scores])
print(f"mean={scores.mean():.3f} std={scores.std():.3f}")
خمس نتائج بين 0.974 و0.991. الانحراف 0.007 يقول إن الفرق بين نموذجين أقل من نحو 0.01 قد يكون صدفة تقسيم لا تحسّنًا حقيقيًا. اكتب المتوسط والانحراف معًا دائمًا.
5التقسيم بالزمن
إذا كان النموذج سيعمل على المستقبل، كالمبيعات أو الاحتيال، فالتقسيم العشوائي يُريه الغد ليتنبّأ بالأمس. درّب على الماضي، وقيّم على ما بعده.
import pandas as pd
days = pd.date_range("2026-01-01", periods=10, freq="D")
df = pd.DataFrame({"day": days, "sales": [12, 15, 14, 18, 20, 19, 23, 25, 24, 28]})
cut = int(len(df) * 0.7)
train, test = df.iloc[:cut], df.iloc[cut:]
print("train:", train.day.min().date(), "->", train.day.max().date())
print("test: ", test.day.min().date(), "->", test.day.max().date())
print("no future row in train:", train.day.max() < test.day.min())
رتّب حسب الزمن ثم اقطع، بلا خلط. للتحقّق المتقاطع الزمني يوفّر scikit-learn الأداة TimeSeriesSplit التي تدرّب دائمًا على الماضي وتقيّم على ما يليه.
train_test_split العادي على بيانات مرتّبة زمنيًا. هو يخلط الصفوف افتراضيًا، فتصل أيام من المستقبل إلى التدريب.كيف يعمل، خطوة بخطوة: كيف تقسم البيانات دون تسريب
افصل الاختبار أولًا، 20% بـ train_test_split مع stratify، ولا تنظر إليه بعد ذلك.
اقسم الباقي إلى تدريب وتحقّق، فتصير النسب 60% و20% و20%.
نفّذ كل معالجة، من التوحيد إلى اختيار السمات، على التدريب فقط، والأفضل داخل make_pipeline.
اختر النموذج والإعدادات على التحقّق، ثم افتح الاختبار مرة واحدة للفائز وحده.
مثال محلول
تخيّل طالبًا رأى أسئلة الاختبار قبله. علامته الكاملة لا تخبرك بشيء عن فهمه. النموذج نفس الشيء: يجب أن تحتفظ ببيانات لم يرها أبدًا لتقيسه عليها.
GroupShuffleSplit.
جرّب بلا كود
خرافة مقابل حقيقة
| الخرافة | الحقيقة |
|---|---|
| جزءان، تدريب واختبار، يكفيان. | إذا اخترت بين عشرين إعدادًا على الاختبار، صار رقم الفائز متفائلًا. التحقّق يمتصّ هذا الانتقاء ويبقى الاختبار نظيفًا. |
| التسريب يظهر كخطأ في الشيفرة. | الشيفرة تعمل والأرقام ممتازة، ثم ينهار النموذج في الإنتاج. اختيار السمات قبل التقسيم أعطى 74% على ضجيج خالص. |
| التقسيم العشوائي صالح لكل البيانات. | على بيانات مرتّبة زمنيًا يرى النموذج الغد ليتنبّأ بالأمس. درّب على الماضي وقِس على ما بعده. |
جرّب بنفسك
جرّبت 30 إعدادًا على مجموعة الاختبار واخترت أفضلها بدقة 91%. ماذا يصف هذا الرقم بدقة؟
اختيار الأفضل من ثلاثين محاولة على نفس البيانات ينتقي بعض الحظ مع الجودة. الرقم الفائز يميل للأعلى. الحل: اختر على التحقّق، ثم افتح الاختبار مرة واحدة للفائز فقط.
تبني نموذجًا يتنبّأ بالطلب على المطاعم للأسبوع القادم من بيانات سنتين. أي تقسيم هو الصحيح؟
النموذج سيُستخدم على المستقبل، فيجب أن يُقيَّم على مستقبل لم يره. التقسيم العشوائي يسمح له بتعلّم أنماط من أيام بعد يوم الاختبار، مثل عطلة أو موسم، فتبدو الأرقام أفضل مما ستكون.
بطاقات مراجعة سريعة
تحقق من فهمك
وحّدت كل البيانات بـStandardScaler ثم قسّمتها. لماذا يُعدّ هذا تسريبًا، ولماذا يكون أثره صغيرًا غالبًا رغم ذلك؟
لأن المتوسط والانحراف المستخدمين في التوحيد حُسبا من صفوف الاختبار أيضًا، فالتدريب «يعرف» شيئًا عن توزيع الاختبار. أثره صغير عادة لأن متوسط آلاف الصفوف لا يتغيّر كثيرًا بإضافة الاختبار. لكن القاعدة مهمة لأن نفس النمط مع اختيار السمات أو تعويض القيم بالهدف يصنع تسريبًا ضخمًا كما رأيت في مثال الضجيج.
لماذا نستخدم stratify عند التقسيم؟
حتى تبقى نسبة الفئات واحدة في كل جزء، كما بقيت الإيجابيات بين 0.62 و0.63 في الأجزاء الثلاثة. بدونه قد يقع جزء صغير بنسبة مختلفة بالصدفة.
مصادر هذا الدرس
مرتّبة بالنوع. ابدأ بالتوثيق الرسمي إذا تبي المرجع الدقيق، وبالمقال أو الفيديو إذا تبي الشرح أولًا.
توثيق رسمي2
خلّصت الدرس؟
سجّل الدخول ليُحفظ تقدّمك.