3.1. التحقق المتبادل: تقييم أداء المقدر#

يُعد تعلم معلمات دالة التنبؤ واختبارها على نفس البيانات خطأً منهجيًا: النموذج الذي يكرر فقط تسميات العينات التي رآها للتو ستحصل على درجة مثالية لكنها ستفشل في التنبؤ بأي شيء مفيد على البيانات التي لم يتم رؤيتها بعد. تسمى هذه الحالة الإفراط في التجهيز. لتجنب ذلك، من الممارسات الشائعة عند إجراء تجربة تعلم آلي (خاضعة للإشراف) لإخفاء جزء من البيانات المتاحة كمجموعة اختبار X_test, y_test. لاحظ أن كلمة "تجربة" لا يُقصد بها للدلالة على الاستخدام الأكاديمي فقط، لأنه حتى في البيئات التجارية عادة ما يبدأ التعلم الآلي بشكل تجريبي. فيما يلي مخطط انسيابي لسير عمل التحقق المتبادل النموذجي في تدريب النموذج. يمكن تحديد أفضل المعلمات بواسطة بحث الشبكة التقنيات.

سير عمل بحث الشبكة

في scikit-learn، تقسيم عشوائي إلى مجموعات التدريب والاختبار يمكن حسابها بسرعة باستخدام دالة المساعدة train_test_split. دعنا نحمل مجموعة بيانات iris لتناسب جهاز متجه دعم خطي عليها:

>>> import numpy as np
>>> from sklearn.model_selection import train_test_split
>>> from sklearn import datasets
>>> from sklearn import svm

>>> X, y = datasets.load_iris(return_X_y=True)
>>> X.shape, y.shape
((150, 4), (150,))

يمكننا الآن أخذ عينة سريعة من مجموعة التدريب مع الاحتفاظ بـ 40٪ من البيانات لاختبار (تقييم) المصنف الخاص بنا:

>>> X_train, X_test, y_train, y_test = train_test_split(
...     X, y, test_size=0.4, random_state=0)

>>> X_train.shape, y_train.shape
((90, 4), (90,))
>>> X_test.shape, y_test.shape
((60, 4), (60,))

>>> clf = svm.SVC(kernel='linear', C=1).fit(X_train, y_train)
>>> clf.score(X_test, y_test)
0.96...

عند تقييم إعدادات مختلفة ("معلمات فائقة") للمقدرات، مثل إعداد C الذي يجب تعيينه يدويًا لـ SVM، لا يزال هناك خطر الإفراط في التجهيز على مجموعة الاختبار لأنه يمكن تعديل المعلمات حتى يعمل المقدر على النحو الأمثل. بهذه الطريقة، يمكن أن "تتسرب" المعرفة حول مجموعة الاختبار إلى النموذج ولم تعد مقاييس التقييم تُبلغ عن أداء التعميم. لحل هذه المشكلة، يمكن الاحتفاظ بجزء آخر من مجموعة البيانات كمجموعة تسمى "مجموعة التحقق من الصحة": يستمر التدريب على مجموعة التدريب، بعد ذلك يتم التقييم على مجموعة التحقق من الصحة، وعندما تبدو التجربة ناجحة، يمكن إجراء التقييم النهائي على مجموعة الاختبار.

ومع ذلك، عن طريق تقسيم البيانات المتاحة إلى ثلاث مجموعات، نحن نقلل بشكل كبير من عدد العينات التي يمكن استخدامها لتعلم النموذج، ويمكن أن تعتمد النتائج على اختيار عشوائي معين لزوج مجموعات (التدريب، التحقق من الصحة).

الحل لهذه المشكلة هو إجراء يسمى التحقق المتبادل (CV للاختصار). يجب أن تظل مجموعة الاختبار محجوزة للتقييم النهائي، لكن لم تعد هناك حاجة إلى مجموعة التحقق من الصحة عند القيام بـ CV. في النهج الأساسي، يسمى k-fold CV، يتم تقسيم مجموعة التدريب إلى k مجموعات أصغر (تم وصف مناهج أخرى أدناه، لكن بشكل عام اتبع نفس المبادئ). يتم اتباع الإجراء التالي لكل من "الطيات" k:

  • يتم تدريب نموذج باستخدام \(k-1\) من الطيات كبيانات تدريب؛

  • يتم التحقق من صحة النموذج الناتج على الجزء المتبقي من البيانات (على سبيل المثال، يتم استخدامه كمجموعة اختبار لحساب مقياس أداء مثل الدقة).

مقياس الأداء الذي أبلغ عنه التحقق المتبادل k-fold هو متوسط القيم المحسوبة في الحلقة. يمكن أن يكون هذا النهج مكلفًا من الناحية الحسابية، لكنها لا تهدر الكثير من البيانات (كما هو الحال عند إصلاح مجموعة تحقق من الصحة التعسفية)، وهي ميزة رئيسية في مشاكل مثل الاستدلال العكسي حيث يكون عدد العينات صغيرًا جدًا.

وصف للتحقق المتبادل من 5 طيات على مجموعة تدريب، مع الاحتفاظ بمجموعة اختبار.

3.1.1. حساب المقاييس المتحقق منها#

أبسط طريقة لاستخدام التحقق المتبادل هي استدعاء دالة المساعدة cross_val_score على المقدر ومجموعة البيانات.

يوضح المثال التالي كيفية تقدير دقة نواة خطية دعم آلة المتجهات على مجموعة بيانات iris عن طريق تقسيم البيانات، وملاءمة نموذج وحساب النتيجة 5 مرات متتالية (مع انقسامات مختلفة في كل مرة زمن):

>>> from sklearn.model_selection import cross_val_score
>>> clf = svm.SVC(kernel='linear', C=1, random_state=42)
>>> scores = cross_val_score(clf, X, y, cv=5)
>>> scores
array([0.96..., 1. , 0.96..., 0.96..., 1. ])

ومن ثم يتم إعطاء متوسط الدرجة والانحراف المعياري بواسطة:

>>> print("%0.2f accuracy with a standard deviation of %0.2f" % (scores.mean(), scores.std()))
0.98 دقة مع انحراف معياري قدره 0.02

افتراضيًا، يتم حساب النتيجة في كل تكرار CV هي النتيجة طريقة المقدر. من الممكن تغيير هذا باستخدام معلمة التسجيل:

>>> from sklearn import metrics
>>> scores = cross_val_score(
...     clf, X, y, cv=5, scoring='f1_macro')
>>> scores
array([0.96..., 1.  ..., 0.96..., 0.96..., 1.        ])

انظر معلمة scoring: تعريف قواعد تقييم النموذج للحصول على التفاصيل. في حالة مجموعة بيانات Iris، يتم موازنة العينات عبر الهدف فئات وبالتالي فإن الدقة ودرجة F1 متساويتان تقريبًا.

عندما تكون وسيطة cv عددًا صحيحًا، يستخدم cross_val_score استراتيجيات KFold أو StratifiedKFold افتراضيًا، والأخيرة يتم استخدامه إذا كان المقدر مشتقًا من ClassifierMixin.

من الممكن أيضًا استخدام استراتيجيات التحقق المتبادل الأخرى عن طريق تمرير تقاطع مكرر التحقق من الصحة بدلاً من ذلك، على سبيل المثال:

>>> from sklearn.model_selection import ShuffleSplit
>>> n_samples = X.shape[0]
>>> cv = ShuffleSplit(n_splits=5, test_size=0.3, random_state=0)
>>> cross_val_score(clf, X, y, cv=cv)
array([0.977..., 0.977..., 1.  ..., 0.955..., 1.        ])

خيار آخر هو استخدام تكرار ينتج عنه انقسامات (تدريب، اختبار) كمصفوفات المؤشرات، على سبيل المثال:

>>> def custom_cv_2folds(X):
...     n = X.shape[0]
...     i = 1
...     while i <= 2:
...         idx = np.arange(n * (i - 1) / 2, n * i / 2, dtype=int)
...         yield idx, idx
...         i += 1
...
>>> custom_cv = custom_cv_2folds(X)
>>> cross_val_score(clf, X, y, cv=custom_cv)
array([1.        , 0.973...])
تحويل البيانات مع البيانات المحجوزة#

تمامًا كما هو الحال من المهم اختبار متنبئ على البيانات المحجوزة من التدريب، المعالجة المسبقة (مثل التوحيد القياسي، اختيار الميزات، إلخ.) و تحويلات البيانات المماثلة بالمثل يجب يتم تعلمها من مجموعة التدريب وتطبيقها على البيانات المحجوزة للتنبؤ:

>>> from sklearn import preprocessing
>>> X_train, X_test, y_train, y_test = train_test_split(
...     X, y, test_size=0.4, random_state=0)
>>> scaler = preprocessing.StandardScaler().fit(X_train)
>>> X_train_transformed = scaler.transform(X_train)
>>> clf = svm.SVC(C=1).fit(X_train_transformed, y_train)
>>> X_test_transformed = scaler.transform(X_test)
>>> clf.score(X_test_transformed, y_test)
0.9333...

Pipeline يجعل من السهل تكوين المقدرات، وتوفير هذا السلوك تحت التحقق المتبادل:

>>> from sklearn.pipeline import make_pipeline
>>> clf = make_pipeline(preprocessing.StandardScaler(), svm.SVC(C=1))
>>> cross_val_score(clf, X, y, cv=cv)
array([0.977..., 0.933..., 0.955..., 0.933..., 0.977...])

انظر خطوط الأنابيب والمقدرات المركبة.

3.1.1.1. دالة cross_validate وتقييم المقاييس المتعددة#

تختلف دالة cross_validate عن cross_val_score في طريقتان:

  • يسمح بتحديد مقاييس متعددة للتقييم.

  • تُرجع dict تحتوي على أوقات الملاءمة، وأوقات النتيجة (واختياريًا درجات التدريب، والمقدرات المناسبة، ومؤشرات تقسيم القطار والاختبار) بالإضافة إلى درجة الاختبار.

لتقييم مقياس واحد، حيث تكون معلمة التسجيل عبارة عن سلسلة، قابل للاستدعاء أو لا شيء، ستكون المفاتيح - ['test_score', 'fit_time', 'score_time']

ولتقييم المقاييس المتعددة، تكون قيمة الإرجاع dict مع المفاتيح التالية - ['test_<scorer1_name>', 'test_<scorer2_name>', 'test_<scorer...>', 'fit_time', 'score_time']

يتم تعيين return_train_score على False افتراضيًا لتوفير وقت الحساب. لتقييم الدرجات على مجموعة التدريب أيضًا، تحتاج إلى تعيينها على True. يمكنك أيضًا الاحتفاظ بالمقدر المناسب في كل مجموعة تدريب بواسطة تعيين return_estimator=True. وبالمثل، يمكنك تعيين return_indices=True للاحتفاظ بمؤشرات التدريب والاختبار المستخدمة للتقسيم مجموعة البيانات إلى مجموعات التدريب والاختبار لكل تقسيم cv.

يمكن تحديد المقاييس المتعددة إما كقائمة أو مجموعة أو مجموعة أسماء المسجل المحددة مسبقًا:

>>> from sklearn.model_selection import cross_validate
>>> from sklearn.metrics import recall_score
>>> scoring = ['precision_macro', 'recall_macro']
>>> clf = svm.SVC(kernel='linear', C=1, random_state=0)
>>> scores = cross_validate(clf, X, y, scoring=scoring)
>>> sorted(scores.keys())
['fit_time', 'score_time', 'test_precision_macro', 'test_recall_macro']
>>> scores['test_recall_macro']
array([0.96..., 1.  ..., 0.96..., 0.96..., 1.        ])

أو كدالة dict تعيين اسم المسجل إلى دالة تسجيل محددة مسبقًا أو مخصصة:

>>> from sklearn.metrics import make_scorer
>>> scoring = {'prec_macro': 'precision_macro',
...            'rec_macro': make_scorer(recall_score, average='macro')}
>>> scores = cross_validate(clf, X, y, scoring=scoring,
...                         cv=5, return_train_score=True)
>>> sorted(scores.keys())
['fit_time', 'score_time', 'test_prec_macro', 'test_rec_macro',
 'train_prec_macro', 'train_rec_macro']
>>> scores['train_rec_macro']
array([0.97..., 0.97..., 0.99..., 0.98..., 0.98...])

فيما يلي مثال على cross_validate باستخدام مقياس واحد:

>>> scores = cross_validate(clf, X, y,
...                         scoring='precision_macro', cv=5,
...                         return_estimator=True)
>>> sorted(scores.keys())
['estimator', 'fit_time', 'score_time', 'test_score']

3.1.1.2. الحصول على التنبؤات عن طريق التحقق المتبادل#

تحتوي الدالة cross_val_predict على واجهة مشابهة لـ cross_val_score، ولكنها تُرجع، لكل عنصر في الإدخال، التنبؤ الذي تم الحصول عليه لهذا العنصر عندما كان في مجموعة الاختبار. فقط يمكن استخدام استراتيجيات التحقق المتبادل التي تعين جميع العناصر إلى مجموعة اختبار مرة واحدة بالضبط (خلاف ذلك، يتم طرح استثناء).

تحذير

ملاحظة حول الاستخدام غير المناسب لـ cross_val_predict

قد تختلف نتيجة cross_val_predict عن تلك تم الحصول عليها باستخدام cross_val_score حيث يتم تجميع العناصر في طرق مختلفة. تأخذ الدالة cross_val_score متوسطًا عبر طيات التحقق المتبادل، بينما cross_val_predict ببساطة ترجع التسميات (أو الاحتمالات) من عدة نماذج مميزة غير مميز. وبالتالي، فإن cross_val_predict ليس مقياسًا مناسبًا خطأ التعميم.

دالة cross_val_predict مناسبة لـ:
  • تصور التنبؤات التي تم الحصول عليها من نماذج مختلفة.

  • مزج النماذج: عند استخدام تنبؤات مقدر خاضع للإشراف واحد لـ تدريب مقدر آخر في طرق المجموعة.

تم تقديم مكررات التحقق المتبادل المتاحة في ما يلي القسم.

أمثلة

  • sphx_glr_auto_examples_model_selection/plot_roc_crossval.py،

  • sphx_glr_auto_examples_feature_selection/plot_rfe_with_cross_validation.py،

  • sphx_glr_auto_examples_model_selection/plot_grid_search_digits.py،

  • sphx_glr_auto_examples_model_selection/plot_grid_search_text_feature_extraction.py،

  • sphx_glr_auto_examples_model_selection/plot_cv_predict.py،

  • sphx_glr_auto_examples_model_selection/plot_nested_cross_validation_iris.py.

3.1.2. مكررات التحقق المتبادل#

تسرد الأقسام التالية الأدوات المساعدة لإنشاء مؤشرات التي يمكن استخدامها لإنشاء تقسيمات مجموعة البيانات وفقًا لاختلاف التقاطع استراتيجيات التحقق من الصحة.

3.1.2.1. مكررات التحقق المتبادل لبيانات i.i.d.#

بافتراض أن بعض البيانات مستقلة وموزعة بشكل متماثل (i.i.d.) هو الافتراض بأن جميع العينات تنبع من نفس عملية التوليد ويفترض أن عملية التوليد ليس لها ذاكرة للعينات التي تم إنشاؤها في الماضي.

يمكن استخدام أدوات التحقق المتبادل التالية في مثل هذه الحالات.

ملاحظة

بينما تعد بيانات i.i.d. افتراضًا شائعًا في نظرية التعلم الآلي، إلا أنها نادرًا ما تحدث يصمد في الممارسة. إذا كان المرء يعرف أن العينات قد تم إنشاؤها باستخدام عملية تعتمد على الوقت، فمن الآمن استخدم مخطط التحقق المتبادل للسلاسل الزمنية. وبالمثل، إذا كنا نعلم أن عملية التوليد لها بنية جماعية (عينات تم جمعها من مواضيع مختلفة، تجارب، قياس الأجهزة)، فمن الآمن استخدام التحقق المتبادل على مستوى المجموعة.

3.1.2.1.1. K-fold#

يقسم KFold جميع العينات في \(k\) مجموعات من العينات، تسمى الطيات (إذا كان \(k = n\)، فهذا يعادل Leave One Out استراتيجية)، من أحجام متساوية (إن أمكن). وظيفة التنبؤ هي تم تعلمه باستخدام \(k - 1\) طيات، ويتم استخدام الطية المتروكة للاختبار.

مثال على التحقق المتبادل من 2-fold على مجموعة بيانات تحتوي على 4 عينات:

>>> import numpy as np
>>> from sklearn.model_selection import KFold

>>> X = ["a", "b", "c", "d"]
>>> kf = KFold(n_splits=2)
>>> for train, test in kf.split(X):
...     print("%s %s" % (train, test))
[2 3] [0 1]
[0 1] [2 3]

فيما يلي تصور لسلوك التحقق المتبادل. لاحظ أن KFold لا يتأثر بالفئات أو المجموعات.

../_images/sphx_glr_plot_cv_indices_006.png

تتكون كل طية من مصفوفتين: الأولى مرتبطة بـ مجموعة التدريب، والثانية إلى مجموعة الاختبار. وبالتالي، يمكن للمرء إنشاء مجموعات التدريب / الاختبار باستخدام فهرسة numpy:

>>> X = np.array([[0., 0.], [1., 1.], [-1., -1.], [2., 2.]])
>>> y = np.array([0, 1, 0, 1])
>>> X_train, X_test, y_train, y_test = X[train], X[test], y[train], y[test]

3.1.2.1.2. Repeated K-Fold#

RepeatedKFold يكرر K-Fold n مرات. يمكن استخدامه عندما يكون المرء يتطلب تشغيل KFold n مرات، مما ينتج عنه انقسامات مختلفة في كل تكرار.

مثال على 2-fold K-Fold تم تكراره مرتين:

>>> import numpy as np
>>> from sklearn.model_selection import RepeatedKFold
>>> X = np.array([[1, 2], [3, 4], [1, 2], [3, 4]])
>>> random_state = 12883823
>>> rkf = RepeatedKFold(n_splits=2, n_repeats=2, random_state=random_state)
>>> for train, test in rkf.split(X):
...     print("%s %s" % (train, test))
...
[2 3] [0 1]
[0 1] [2 3]
[0 2] [1 3]
[1 3] [0 2]

وبالمثل، يكرر RepeatedStratifiedKFold Stratified K-Fold n مرات مع عشوائية مختلفة في كل تكرار.

3.1.2.1.3. Leave One Out (LOO)#

LeaveOneOut (أو LOO) هو تحقق متبادل بسيط. كل تعلم يتم إنشاء المجموعة عن طريق أخذ جميع العينات باستثناء واحدة، ومجموعة الاختبار هي العينة المتروكة. وبالتالي، لـ \(n\) عينات، لدينا \(n\) مختلف مجموعات التدريب و \(n\) مجموعة اختبارات مختلفة. هذا الصليب- لا يهدر إجراء التحقق من الصحة الكثير من البيانات حيث تتم إزالة عينة واحدة فقط من مجموعة التدريب:

>>> from sklearn.model_selection import LeaveOneOut

>>> X = [1, 2, 3, 4]
>>> loo = LeaveOneOut()
>>> for train, test in loo.split(X):
...     print("%s %s" % (train, test))
[1 2 3] [0]
[0 2 3] [1]
[0 1 3] [2]
[0 1 2] [3]

يجب على المستخدمين المحتملين لـ LOO لاختيار النموذج أن يزنوا بعض المحاذير المعروفة. عند مقارنتها بالتحقق المتبادل \(k\)-fold، يبني المرء \(n\) نماذج من \(n\) عينات بدلاً من \(k\) نماذج، حيث \(n > k\). علاوة على ذلك، يتم تدريب كل منها على \(n - 1\) عينات بدلاً من \((k-1) n / k\). في كلتا الحالتين، بافتراض أن \(k\) ليست كبيرة جدًا و \(k < n\)، فإن LOO أكثر تكلفة من الناحية الحسابية من التحقق المتبادل \(k\)-fold.

من حيث الدقة، غالبًا ما ينتج عن LOO تباين كبير كمقدر لـ خطأ الاختبار. بشكل حدسي، منذ \(n - 1\) من يتم استخدام \(n\) عينات لبناء كل نموذج، النماذج التي تم إنشاؤها من الطيات متطابقة تقريبًا مع بعضها البعض ومع النموذج المبني من مجموعة التدريب بأكملها.

ومع ذلك، إذا كان منحنى التعلم حادًا لحجم التدريب المعني، ثم يمكن أن يؤدي التحقق المتبادل من 5 أو 10 طيات إلى المبالغة في تقدير خطأ التعميم.

كقاعدة عامة، يقترح معظم المؤلفين، والأدلة التجريبية، أن 5 أو 10 يجب تفضيل التحقق المتبادل للطي على LOO.

المراجع#

3.1.2.1.4. Leave P Out (LPO)#

LeavePOut يشبه جدًا LeaveOneOut لأنه ينشئ كل أزواج التدريب / الاختبار الممكنة عن طريق إزالة \(p\) عينات من الكاملة جلس. لـ \(n\) عينات، ينتج عن هذا \({n \choose p}\) قطار-اختبار أزواج. على عكس LeaveOneOut و KFold، مجموعات الاختبار ستكون تتداخل لـ \(p > 1\).

مثال على Leave-2-Out على مجموعة بيانات تحتوي على 4 عينات:

>>> from sklearn.model_selection import LeavePOut

>>> X = np.ones(4)
>>> lpo = LeavePOut(p=2)
>>> for train, test in lpo.split(X):
...     print("%s %s" % (train, test))
[2 3] [0 1]
[1 3] [0 2]
[1 2] [0 3]
[0 3] [1 2]
[0 2] [1 3]
[0 1] [2 3]

3.1.2.1.5. تباديل عشوائية للتحقق المتبادل المعروف أيضًا باسم Shuffle & Split#

سيولد مكرر ShuffleSplit عددًا محددًا من قبل المستخدم انقسامات مجموعة بيانات التدريب / الاختبار المستقلة. يتم خلط العينات أولاً و ثم تقسم إلى زوج من مجموعات التدريب والاختبار.

من الممكن التحكم في العشوائية لإعادة إنتاج النتائج عن طريق زرع random_state بشكل صريح عشوائي زائف مولد الأرقام.

فيما يلي مثال على الاستخدام:

>>> from sklearn.model_selection import ShuffleSplit
>>> X = np.arange(10)
>>> ss = ShuffleSplit(n_splits=5, test_size=0.25, random_state=0)
>>> for train_index, test_index in ss.split(X):
...     print("%s %s" % (train_index, test_index))
[9 1 6 7 3 0 5] [2 8 4]
[2 9 8 0 6 7 4] [3 5 1]
[4 5 1 0 6 9 7] [2 3 8]
[2 7 5 8 0 3 4] [6 1 9]
[4 1 0 6 8 9 3] [5 2 7]

فيما يلي تصور لسلوك التحقق المتبادل. لاحظ أن ShuffleSplit لا يتأثر بالفئات أو المجموعات.

../_images/sphx_glr_plot_cv_indices_008.png

ShuffleSplit هو بالتالي بديل جيد لـ KFold التقاطع التحقق من الصحة الذي يسمح بالتحكم الدقيق في عدد التكرارات ونسبة العينات على كل جانب من جوانب تقسيم القطار / الاختبار.

3.1.2.2. مكررات التحقق المتبادل مع التقسيم الطبقي بناءً على تسميات الفئة#

يمكن أن تُظهر بعض مشكلات التصنيف خللًا كبيرًا في التوزيع من فئات الهدف: على سبيل المثال، يمكن أن يكون هناك عدة مرات سلبية عينات من العينات الإيجابية. في مثل هذه الحالات، يوصى باستخدام أخذ العينات الطبقية كما هو مطبق في StratifiedKFold و StratifiedShuffleSplit لضمان أن ترددات الفئة النسبية هي يتم الحفاظ عليها تقريبًا في كل طية تدريب والتحقق من الصحة.

3.1.2.2.1. k-fold الطبقي#

StratifiedKFold هو نوع مختلف من k-fold الذي يُرجع طبقات الطيات: تحتوي كل مجموعة على نفس النسبة المئوية تقريبًا من عينات كل منها فئة الهدف كمجموعة كاملة.

فيما يلي مثال على التحقق المتبادل الطبقي من 3 طيات على مجموعة بيانات تحتوي على 50 عينة من فئتين غير متوازنتين. نعرض عدد العينات في كل فئة ونقارنها بـ KFold.

>>> from sklearn.model_selection import StratifiedKFold, KFold
>>> import numpy as np
>>> X, y = np.ones((50, 1)), np.hstack(([0] * 45, [1] * 5))
>>> skf = StratifiedKFold(n_splits=3)
>>> for train, test in skf.split(X, y):
...     print('train -  {}   |   test -  {}'.format(
...         np.bincount(y[train]), np.bincount(y[test])))
train -  [30  3]   |   test -  [15  2]
train -  [30  3]   |   test -  [15  2]
train -  [30  4]   |   test -  [15  1]
>>> kf = KFold(n_splits=3)
>>> for train, test in kf.split(X, y):
...     print('train -  {}   |   test -  {}'.format(
...         np.bincount(y[train]), np.bincount(y[test])))
train -  [28  5]   |   test -  [17]
train -  [28  5]   |   test -  [17]
train -  [34]   |   test -  [11  5]

يمكننا أن نرى أن StratifiedKFold يحافظ على نسب الفئة (حوالي 1/10) في كل من مجموعة بيانات التدريب والاختبار.

فيما يلي تصور لسلوك التحقق المتبادل.

../_images/sphx_glr_plot_cv_indices_009.png

يمكن استخدام RepeatedStratifiedKFold لتكرار Stratified K-Fold n مرات مع عشوائية مختلفة في كل تكرار.

3.1.2.2.2. تقسيم خلط طبقي#

StratifiedShuffleSplit هو نوع مختلف من ShuffleSplit، والذي يُرجع انقسامات طبقية، على سبيل المثال التي تنشئ انقسامات عن طريق الحفاظ على نفس الشيء النسبة المئوية لكل فئة هدف كما في المجموعة الكاملة.

فيما يلي تصور لسلوك التحقق المتبادل.

../_images/sphx_glr_plot_cv_indices_012.png

3.1.2.3. تقسيمات الطيات / مجموعات التحقق من الصحة المحددة مسبقًا#

بالنسبة لبعض مجموعات البيانات، يكون التقسيم المحدد مسبقًا للبيانات إلى تدريب- وطية التحقق من الصحة أو في عدة طيات للتحقق المتبادل بالفعل موجود. باستخدام PredefinedSplit من الممكن استخدام هذه الطيات على سبيل المثال عند البحث عن المعلمات الفائقة.

على سبيل المثال، عند استخدام مجموعة التحقق من الصحة، قم بتعيين test_fold على 0 للجميع العينات التي هي جزء من مجموعة التحقق من الصحة، وإلى -1 لجميع العينات الأخرى.

3.1.2.4. مكررات التحقق المتبادل للبيانات المجمعة#

يتم كسر افتراض i.i.d. إذا كانت عملية التوليد الأساسية تنتج مجموعات من العينات التابعة.

مثل هذا التجميع للبيانات خاص بالمجال. سيكون أحد الأمثلة عندما يكون هناك البيانات الطبية التي تم جمعها من مرضى متعددين، مع أخذ عينات متعددة منكل مريض. ومن المرجح أن تعتمد هذه البيانات على المجموعة الفردية. في مثالنا، سيكون معرف المريض لكل عينة هو معرف مجموعته.

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

يمكن استخدام أدوات تقسيم التحقق المتبادل التالية للقيام بذلك. يتم تحديد معرف التجميع للعينات عبر groups معامل.

3.1.2.4.1. Group k-fold#

GroupKFold هو نوع مختلف من k-fold يضمن أن نفس المجموعة هي غير ممثلة في كل من مجموعات الاختبار والتدريب. على سبيل المثال، إذا كانت البيانات تم الحصول عليها من مواضيع مختلفة مع عدة عينات لكل موضوع وإذا النموذج مرن بما يكفي للتعلم من ميزات محددة للغاية للشخص يمكن أن تفشل في التعميم على مواضيع جديدة. GroupKFold يجعل من الممكن لاكتشاف هذا النوع من حالات الإفراط في التجهيز.

تخيل أن لديك ثلاثة مواضيع، لكل منها رقم مرتبط من 1 إلى 3:

>>> from sklearn.model_selection import GroupKFold

>>> X = [0.1, 0.2, 2.2, 2.4, 2.3, 4.55, 5.8, 8.8, 9, 10]
>>> y = ["a", "b", "b", "b", "c", "c", "c", "d", "d", "d"]
>>> groups = [1, 1, 1, 2, 2, 2, 3, 3, 3, 3]

>>> gkf = GroupKFold(n_splits=3)
>>> for train, test in gkf.split(X, y, groups=groups):
...     print("%s %s" % (train, test))
[0 1 2 3 4 5] [6 7 8 9]
[0 1 2 6 7 8 9] [3 4 5]
[3 4 5 6 7 8 9] [0 1 2]

كل موضوع في طية اختبار مختلفة، ونفس الموضوع ليس أبدًا في كل من الاختبار والتدريب. لاحظ أن الطيات ليس لها نفس الشيء بالضبط الحجم بسبب عدم التوازن في البيانات. إذا كان يجب موازنة نسب الفئة عبر الطيات، StratifiedGroupKFold هو خيار أفضل.

فيما يلي تصور لسلوك التحقق المتبادل.

../_images/sphx_glr_plot_cv_indices_007.png

على غرار KFold، ستشكل مجموعات الاختبار من GroupKFold a تقسيم كامل لجميع البيانات. على عكس KFold، GroupKFold غير عشوائي على الإطلاق، بينما يكون KFold عشوائيًا عندما shuffle=True.

3.1.2.4.2. StratifiedGroupKFold#

StratifiedGroupKFold هو مخطط تحقق متبادل يجمع بين كليهما StratifiedKFold و GroupKFold. الفكرة هي محاولة الحفاظ على توزيع الفئات في كل تقسيم مع الحفاظ على كل مجموعة ضمن تقسيم واحد. قد يكون ذلك مفيدًا عندما يكون لديك غير متوازن مجموعة بيانات بحيث يؤدي استخدام GroupKFold فقط إلى إنتاج انقسامات منحرفة.

مثال:

>>> from sklearn.model_selection import StratifiedGroupKFold
>>> X = list(range(18))
>>> y = [1] * 6 + [0] * 12
>>> groups = [1, 2, 3, 3, 4, 4, 1, 1, 2, 2, 3, 4, 5, 5, 5, 6, 6, 6]
>>> sgkf = StratifiedGroupKFold(n_splits=3)
>>> for train, test in sgkf.split(X, y, groups=groups):
...     print("%s %s" % (train, test))
[ 0  2  3  4  5  6  7 10 11 15 16 17] [ 1  8  9 12 13 14]
[ 0  1  4  5  6  7  8  9 11 12 13 14] [ 2  3 10 15 16 17]
[ 1  2  3  8  9 10 12 13 14 15 16 17] [ 0  4  5  6  7 11]
ملاحظات التنفيذ#
  • مع التنفيذ الحالي، لا يمكن إجراء خلط كامل في معظم السيناريوهات. عندما shuffle=True، يحدث ما يلي:

    1. يتم خلط جميع المجموعات.

    2. يتم فرز المجموعات حسب الانحراف المعياري للفئات باستخدام الفرز المستقر.

    3. يتم تكرار المجموعات التي تم فرزها وتعيينها إلى طيات.

    هذا يعني أنه سيتم خلط المجموعات ذات الانحراف المعياري نفسه لتوزيع الفئة فقط، مما قد يكون مفيدًا عندما تحتوي كل مجموعة على فئة واحدة فقط.

  • تقوم الخوارزمية بتعيين كل مجموعة بشكل جشع إلى إحدى مجموعات اختبار n_splits، اختيار مجموعة الاختبار التي تقلل من التباين في توزيع الفئة عبر مجموعات الاختبار. يبدأ تعيين المجموعة من المجموعات ذات أعلى إلى أدنى تباين في تكرار الفئة، أي مجموعات كبيرة بلغت ذروتها في واحد أو قليل يتم تعيين الفئات أولاً.

  • هذا التقسيم دون المستوى الأمثل بمعنى أنه قد ينتج عنه انقسامات غير متوازنة حتى لو كان التقسيم الطبقي المثالي ممكنًا. إذا كان لديك قريب نسبيًا توزيع الفئات في كل مجموعة، فإن استخدام GroupKFold أفضل.

فيما يلي تصور لسلوك التحقق المتبادل للمجموعات غير المتساوية:

../_images/sphx_glr_plot_cv_indices_005.png

3.1.2.4.3. Leave One Group Out#

LeaveOneGroupOut هو مخطط للتحقق المتبادل حيث يحتفظ كل تقسيم العينات التي تنتمي إلى مجموعة معينة. معلومات المجموعة هي يتم توفيره من خلال مصفوفة تقوم بترميز مجموعة كل عينة.

وبالتالي، تتكون كل مجموعة تدريب من جميع العينات باستثناء تلك المتعلقة بمجموعة محددة. هذا هو نفسه LeavePGroupsOut مع n_groups=1 وهو نفس GroupKFold مع n_splits يساوي عدد التسميات الفريدة التي تم تمريرها إلى معلمة groups.

على سبيل المثال، في حالات التجارب المتعددة، LeaveOneGroupOut يمكن استخدامها لإنشاء تحقق متبادل بناءً على التجارب المختلفة: نقوم بإنشاء مجموعة تدريب باستخدام عينات جميع التجارب باستثناء واحدة:

>>> from sklearn.model_selection import LeaveOneGroupOut

>>> X = [1, 5, 10, 50, 60, 70, 80]
>>> y = [0, 1, 1, 2, 2, 2, 2]
>>> groups = [1, 1, 2, 2, 3, 3, 3]
>>> logo = LeaveOneGroupOut()
>>> for train, test in logo.split(X, y, groups=groups):
...     print("%s %s" % (train, test))
[2 3 4 5 6] [0 1]
[0 1 4 5 6] [2 3]
[0 1 2 3] [4 5 6]

تطبيق شائع آخر هو استخدام معلومات الوقت: على سبيل المثال يمكن أن تكون المجموعات هي سنة جمع العينات وبالتالي تسمح للتحقق المتبادل مقابل الانقسامات القائمة على الوقت.

3.1.2.4.4. Leave P Groups Out#

LeavePGroupsOut مشابه لـ LeaveOneGroupOut، لكنه يزيل عينات متعلقة بـ \(P\) مجموعات لكل مجموعة تدريب / اختبار. كل ما هو ممكن يتم استبعاد مجموعات \(P\)، مما يعني أن مجموعات الاختبار ستتداخل لـ \(P>1\).

مثال على Leave-2-Group Out:

>>> from sklearn.model_selection import LeavePGroupsOut

>>> X = np.arange(6)
>>> y = [1, 1, 1, 2, 2, 2]
>>> groups = [1, 1, 2, 2, 3, 3]
>>> lpgo = LeavePGroupsOut(n_groups=2)
>>> for train, test in lpgo.split(X, y, groups=groups):
...     print("%s %s" % (train, test))
[4 5] [0 1 2 3]
[2 3] [0 1 4 5]
[0 1] [2 3 4 5]

3.1.2.4.5. Group Shuffle Split#

يتصرف مكرر GroupShuffleSplit كمزيج من ShuffleSplit و LeavePGroupsOut، ويولد سلسلة من الأقسام العشوائية التي تكون فيها مجموعة فرعية من المجموعات يتم الاحتفاظ بها لكل تقسيم. يتم تنفيذ كل تقسيم تدريب / اختبار بشكل مستقل بمعنى لا توجد علاقة مضمونة بين مجموعات الاختبار المتتالية.

فيما يلي مثال على الاستخدام:

>>> from sklearn.model_selection import GroupShuffleSplit

>>> X = [0.1, 0.2, 2.2, 2.4, 2.3, 4.55, 5.8, 0.001]
>>> y = ["a", "b", "b", "b", "c", "c", "c", "a"]
>>> groups = [1, 1, 2, 2, 3, 3, 4, 4]
>>> gss = GroupShuffleSplit(n_splits=4, test_size=0.5, random_state=0)
>>> for train, test in gss.split(X, y, groups=groups):
...     print("%s %s" % (train, test))
...
[0 1 2 3] [4 5 6 7]
[2 3 6 7] [0 1 4 5]
[2 3 4 5] [0 1 6 7]
[4 5 6 7] [0 1 2 3]

فيما يلي تصور لسلوك التحقق المتبادل.

../_images/sphx_glr_plot_cv_indices_011.png

هذه الفئة مفيدة عندما يكون سلوك LeavePGroupsOut هو المرغوب فيه، لكن عدد المجموعات كبير بما يكفي لتوليد كل ستكون الأقسام الممكنة مع حجب مجموعات \(P\) باهظة الثمن. في مثل هذا السيناريو، يوفر GroupShuffleSplit عينة عشوائية (مع الاستبدال) من تقسيمات القطار / الاختبار تم إنشاؤها بواسطة LeavePGroupsOut.

3.1.2.5. استخدام مكررات التحقق المتبادل لتقسيم التدريب والاختبار#

قد تكون وظائف التحقق المتبادل للمجموعة المذكورة أعلاه مفيدة أيضًا لتقسيم مجموعة بيانات إلى مجموعات فرعية للتدريب والاختبار. لاحظ أن الراحة الدالة train_test_split هي غلاف حول ShuffleSplit وبالتالي يسمح فقط بالتقسيم الطبقي (باستخدام تسميات الفئة) ولا يمكن أن تمثل المجموعات.

لإجراء تقسيم التدريب والاختبار، استخدم المؤشرات للتدريب والاختبار المجموعات الفرعية التي ينتجها المولد بواسطة طريقة split() لـ مقسم التحقق المتبادل. فمثلا:

>>> import numpy as np
>>> from sklearn.model_selection import GroupShuffleSplit

>>> X = np.array([0.1, 0.2, 2.2, 2.4, 2.3, 4.55, 5.8, 0.001])
>>> y = np.array(["a", "b", "b", "b", "c", "c", "c", "a"])
>>> groups = np.array([1, 1, 2, 2, 3, 3, 4, 4])
>>> train_indx, test_indx = next(
...     GroupShuffleSplit(random_state=7).split(X, y, groups)
... )
>>> X_train, X_test, y_train, y_test = \
...     X[train_indx], X[test_indx], y[train_indx], y[test_indx]
>>> X_train.shape, X_test.shape
((6,), (2,))
>>> np.unique(groups[train_indx]), np.unique(groups[test_indx])
(array([1, 2, 4]), array([3]))

3.1.2.6. التحقق المتبادل من بيانات السلاسل الزمنية#

تتميز بيانات السلاسل الزمنية بالارتباط بين الملاحظات التي هي قريبة في الوقت المناسب (الارتباط التلقائي). ومع ذلك، الكلاسيكية تقنيات التحقق المتبادل مثل KFold و ShuffleSplit تفترض أن العينات مستقلة و موزعة بشكل متماثل، وسيؤدي إلى ارتباط غير معقول بين حالات التدريب والاختبار (ينتج عنها تقديرات ضعيفة لـ خطأ التعميم) على بيانات السلاسل الزمنية. لذلك، من المهم جدًا لتقييم نموذجنا لبيانات السلاسل الزمنية على الملاحظات "المستقبلية" على الأقل مثل تلك التي تُستخدم لتدريب النموذج. لتحقيق هذا، واحد يتم توفير الحل بواسطة TimeSeriesSplit.

3.1.2.6.1. تقسيم السلاسل الزمنية#

TimeSeriesSplit هو نوع مختلف من k-fold الذي يعيد أول \(k\) طيات كمجموعة تدريب و \((k+1)\) طية كمجموعة اختبار. لاحظ أنه على عكس طرق التحقق المتبادل القياسية، مجموعات التدريب المتتالية هي مجموعات شاملة لتلك التي تأتي قبلها. أيضًا، يضيف جميع البيانات الفائضة إلى قسم التدريب الأول، والذي يتم استخدامه دائمًا لتدريب النموذج.

يمكن استخدام هذه الفئة للتحقق المتبادل من عينات بيانات السلاسل الزمنية التي لوحظت على فترات زمنية ثابتة.

مثال على التحقق المتبادل للسلاسل الزمنية المكونة من 3 انقسامات على مجموعة بيانات تحتوي على 6 عينات:

>>> from sklearn.model_selection import TimeSeriesSplit

>>> X = np.array([[1, 2], [3, 4], [1, 2], [3, 4], [1, 2], [3, 4]])
>>> y = np.array([1, 2, 3, 4, 5, 6])
>>> tscv = TimeSeriesSplit(n_splits=3)
>>> print(tscv)
TimeSeriesSplit(gap=0, max_train_size=None, n_splits=3, test_size=None)
>>> for train, test in tscv.split(X):
...     print("%s %s" % (train, test))
[0 1 2] [3]
[0 1 2 3] [4]
[0 1 2 3 4] [5]

فيما يلي تصور لسلوك التحقق المتبادل.

../_images/sphx_glr_plot_cv_indices_013.png

3.1.3. ملاحظة حول الخلط#

إذا لم يكن ترتيب البيانات عشوائيًا (على سبيل المثال، عينات بنفس تسمية الفئة متجاورة)، قد يكون خلطها أولاً ضروريًا للحصول على تقاطع ذي مغزى- نتيجة التحقق من الصحة. ومع ذلك، قد يكون العكس صحيحًا إذا كانت العينات ليست كذلك موزعة بشكل مستقل ومتماثل. على سبيل المثال، إذا كانت العينات تتوافق لمقالات إخبارية، ومرتبة حسب وقت نشرها، ثم خلط من المرجح أن تؤدي البيانات إلى نموذج مفرط التجهيز ودرجة تحقق من الصحة متضخمة: سيتم اختباره على عينات متشابهة بشكل مصطنع (قريبة من الوقت) لعينات التدريب.

تحتوي بعض مكررات التحقق المتبادل، مثل KFold، على خيار مدمج لخلط مؤشرات البيانات قبل تقسيمها. لاحظ أن:

  • يستهلك هذا ذاكرة أقل من خلط البيانات مباشرة.

  • افتراضيًا، لا يحدث خلط، بما في ذلك (الطبقي) K fold cross- التحقق من الصحة الذي يتم إجراؤه عن طريق تحديد cv=some_integer إلى cross_val_score، بحث الشبكة، إلخ. ضع في اعتبارك ذلك train_test_split لا يزال يُرجع تقسيمًا عشوائيًا.

  • معلمة random_state افتراضية إلى None، مما يعني أن سيكون الخلط مختلفًا في كل مرة KFold(..., shuffle=True) هو متكرر. ومع ذلك، سيستخدم GridSearchCV نفس الخلط لكل مجموعة من المعلمات التي تم التحقق من صحتها من خلال استدعاء واحد لطريقة fit.

  • للحصول على نتائج متطابقة لكل تقسيم، قم بتعيين random_state إلى عدد صحيح.

لمزيد من التفاصيل حول كيفية التحكم في عشوائية مقسمات السيرة الذاتية وتجنب المزالق الشائعة، انظر التحكم في العشوائية.

3.1.4. التحقق المتبادل واختيار النموذج#

يمكن أيضًا استخدام مكررات التحقق المتبادل لأداء النموذج مباشرةً الاختيار باستخدام Grid Search للمعلمات الفائقة المثلى لـ النموذج. هذا هو موضوع القسم التالي: ضبط المعلمات الفائقة لمُقدِّر.

3.1.5. درجة اختبار التقليب#

يقدم permutation_test_score طريقة أخرى لتقييم أداء المصنفات. يوفر التقليب القائم على القيمة الاحتمالية، والتي تمثل مدى احتمالية أداء ملحوظ لـ سيتم الحصول على المصنف عن طريق الصدفة. الفرضية الصفرية في هذا الاختبار هي أن المصنف يفشل في الاستفادة من أي اعتماد إحصائي بين الميزات والتسميات لإجراء تنبؤات صحيحة على البيانات المتروكة. permutation_test_score يولد فارغًا التوزيع عن طريق حساب n_permutations تباديل مختلفة لـ البيانات. في كل تبديل، يتم خلط التسميات بشكل عشوائي، وبالتالي إزالة أي اعتماد بين الميزات والتسميات. إخراج القيمة الاحتمالية هو جزء التباديل الذي يكون متوسط درجة التحقق المتبادل التي تم الحصول عليها بواسطة النموذج أفضل من درجة التحقق المتبادل التي تم الحصول عليها بواسطة النموذج باستخدام البيانات الأصلية. للحصول على نتائج موثوقة n_permutations يجب أن تكون عادةً أكبر من 100 و cv بين 3-10 طيات.

توفر القيمة الاحتمالية المنخفضة دليلًا على أن مجموعة البيانات تحتوي على اعتماد حقيقي بين الميزات والتسميات وكان المصنف قادرًا على استخدام هذا للحصول على نتائج جيدة. يمكن أن تكون القيمة الاحتمالية العالية بسبب نقص الاعتماد بين الميزات والتسميات (لا يوجد فرق في قيم الميزات بين الفئات) أو لأن المصنف لم يكن قادرًا على استخدام التبعية في البيانات. في الحالة الأخيرة، باستخدام مصنف أكثر ملاءمة قادر على استخدام الهيكل في البيانات، سينتج عنه أقل القيمة الاحتمالية.

يوفر التحقق المتبادل معلومات حول مدى جودة تعميم المصنف، على وجه التحديد نطاق الأخطاء المتوقعة للمصنف. ومع ذلك، لا يزال من الممكن أن يؤدي المصنف المدرب على مجموعة بيانات عالية الأبعاد بدون بنية أداء أفضل من المتوقع في التحقق المتبادل، عن طريق الصدفة. يمكن أن يحدث هذا عادةً مع مجموعات البيانات الصغيرة التي تحتوي على أقل من بضع مئات عينات. permutation_test_score يوفر معلومات حول ما إذا كان المصنف قد وجد بنية فئة حقيقية ويمكن أن يساعد في تقييم أداء المصنف.

من المهم ملاحظة أن هذا الاختبار قد ثبت أنه ينتج منخفضًا القيم الاحتمالية حتى لو كان هناك هيكل ضعيف فقط في البيانات لأن في مجموعات البيانات المتبادلة المقابلة لا يوجد هيكل على الإطلاق. هذا لذلك، فإن الاختبار قادر فقط على إظهار متى يتفوق النموذج بشكل موثوق التخمين العشوائي.

أخيرًا، يتم حساب permutation_test_score باستخدام القوة الغاشمة وملاءمة (n_permutations + 1) * n_cv نماذج داخليًا. لذلك لا يمكن تتبعه إلا مع مجموعات البيانات الصغيرة التي تناسب نموذج فردي سريع جدًا.

أمثلة

  • sphx_glr_auto_examples_model_selection/plot_permutation_tests_for_classification.py

المراجع#