تطوير مقدرات scikit-learn#

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

واجهات برمجة تطبيقات كائنات scikit-learn#

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

يتم وصف عناصر واجهة برمجة تطبيقات scikit-learn بشكل أكثر تحديدًا في مسرد المصطلحات الشائعة وعناصر واجهة برمجة التطبيقات.

كائنات مختلفة#

الكائنات الرئيسية في scikit-learn هي (يمكن لفئة واحدة تنفيذ واجهات متعددة):

Estimator:

الكائن الأساسي، ينفذ أسلوب fit للتعلم من البيانات، إما:

estimator = estimator.fit(data, targets)

أو:

estimator = estimator.fit(data)
Predictor:

للتعلم الخاضع للإشراف، أو بعض المشكلات غير الخاضعة للإشراف، ينفذ:

prediction = predictor.predict(data)

عادةً ما تقدم خوارزميات التصنيف أيضًا طريقة لتحديد اليقين للتنبؤ، إما باستخدام decision_function أو predict_proba:

probability = predictor.predict_proba(data)
Transformer:

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

new_data = transformer.transform(data)

عندما يمكن إجراء التوفيق والتحويل معًا بكفاءة أكبر من إجراءهما بشكل منفصل، ينفذ:

new_data = transformer.fit_transform(data)
Model:

نموذج يمكنه إعطاء مقياس جودة التوفيق أو احتمالية بيانات غير مرئية، ينفذ (الأعلى أفضل):

score = model.score(data)

المقدرات#

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

estimator.fit(X, y)

تحتوي جميع المقدرات المضمنة أيضًا على أسلوب set_params، والذي يعيِّن معلمات مستقلة عن البيانات (يتجاوز قيم المعلمات السابقة التي تم تمريرها إلى __init__).

يجب أن ترث جميع المقدرات في قاعدة كود scikit-learn الرئيسية من sklearn.base.BaseEstimator.

الاعداد#

يتعلق هذا بإنشاء كائن. قد يقبل أسلوب __init__ الخاص بالكائن ثوابت كوسيطات تحدد سلوك المقدر (مثل الثابت C في SVMs). ومع ذلك، لا ينبغي أن يأخذ بيانات التدريب الفعلية كوسيطة، حيث يتم ترك هذا لأسلوب fit():

clf2 = SVC(C=2.3)
clf3 = SVC([[1, 2], [2, 3]], [-1, 1]) # خطأ!

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

بالإضافة إلى ذلك، يجب أن تتوافق كل وسيطة ذات كلمة رئيسية يقبلها __init__ مع سمة في النموذج. يعتمد Scikit-learn على هذا لـ العثور على السمات ذات الصلة لتعيينها على مقدر عند إجراء اختيار النموذج.

لتلخيص، يجب أن يبدو __init__ كما يلي:

def __init__(self, param1=1, param2=2):
    self.param1 = param1
    self.param2 = param2

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

def __init__(self, param1=1, param2=2, param3=3):
    # خطأ: لا ينبغي تعديل المعلمات
    if param1 > 1:
        param2 += 1
    self.param1 = param1
    # خطأ: يجب أن تحتوي سمات الكائن على اسم
    # الوسيطة في المُنشئ تمامًا
    self.param3 = param2

سبب تأجيل التحقق من الصحة هو أنه يجب إجراء نفس التحقق من الصحة في set_params، والذي يُستخدم في خوارزميات مثل GridSearchCV.

الملائمة#

الشيء التالي الذي قد ترغب في القيام به هو تقدير بعض المعلمات في النموذج. يتم تنفيذ ذلك في أسلوب fit().

يأخذ أسلوب fit() بيانات التدريب كوسيطات، والتي يمكن أن تكون مصفوفة واحدة في حالة التعلم غير الخاضع للإشراف، أو مصفوفتين في حالة التعلم الخاضع للإشراف.

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

المعلمات

X

مصفوفة من الشكل (n_samples, n_features)

y

مصفوفة من الشكل (n_samples,)

kwargs

معلمات اختيارية تعتمد على البيانات

يجب أن يكون X.shape[0] هو نفسه y.shape[0]. إذا لم يتم استيفاء هذا الشرط الأساسي، فيجب طرح استثناء من النوع ValueError.

قد يتم تجاهل y في حالة التعلم غير الخاضع للإشراف. ومع ذلك، لـ جعل من الممكن استخدام المقدر كجزء من خط أنابيب يمكنه خلط كل من المحولات الخاضعة للإشراف وغير الخاضعة للإشراف، حتى المقدرات غير الخاضعة للإشراف تحتاج إلى قبول وسيطة ذات كلمة رئيسية y=None في الموضع الثاني يتم تجاهلها بواسطة المقدر. للسبب نفسه، تحتاج أساليب fit_predict و fit_transform و score و partial_fit إلى قبول وسيطة y في المكان الثاني إذا تم تنفيذها.

يجب أن يُعيد الأسلوب الكائن (self). هذا النمط مفيد لتكون قادرًا على تنفيذ بطانات سريعة في جلسة IPython مثل:

y_predicted = SVC(C=100).fit(X_train, y_train).predict(X_test)

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

عندما يتم استدعاء fit، يجب تجاهل أي استدعاء سابق لـ fit. بشكل عام، استدعاء estimator.fit(X1) ثم estimator.fit(X2) يجب أن يكون هو نفسه استدعاء estimator.fit(X2) فقط. ومع ذلك، قد لا يكون هذا صحيحًا من الناحية العملية عندما يعتمد fit على بعض العمليات العشوائية، انظر random_state. استثناء آخر لهذه القاعدة هو عندما يتم تعيين المعلمة الفائقة warm_start إلى True للمقدرات التي تدعمها. warm_start=True تعني أنه تمت إعادة استخدام الحالة السابقة لـ معلمات المقدر القابلة للتدريب بدلاً من استخدام استراتيجية التهيئة الافتراضية.

السمات المقدرة#

يجب أن تحتوي السمات التي تم تقديرها من البيانات دائمًا على اسم ينتهي بشرطة سفلية زائدة، على سبيل المثال، سيتم تخزين معاملات بعض مقدرات الانحدار في سمة coef_ بعد استدعاء fit.

من المتوقع تجاوز السمات المقدرة عند استدعاء fit للمرة الثانية.

السمات الاختيارية#

في الخوارزميات التكرارية، يجب تحديد عدد التكرارات بواسطة عدد صحيح يسمى n_iter.

السمات العامة#

يجب على المقدرات التي تتوقع إدخالًا جدوليًا تعيين سمة n_features_in_ في وقت fit للإشارة إلى عدد الميزات التي يتوقعها المقدر للاستدعاءات اللاحقة لـ predict أو transform. انظر SLEP010 للتفاصيل.

بناء المقدر الخاص بك#

إذا كنت ترغب في تنفيذ مقدر جديد متوافق مع scikit-learn، سواء كان ذلك لك فقط أو للمساهمة به في scikit-learn، فهناك العديد من العناصر الداخلية لـ scikit-learn التي يجب أن تكون على دراية بها بالإضافة إلى واجهة برمجة تطبيقات scikit-learn الموضحة أعلاه. يمكنك التحقق مما إذا كان المقدر الخاص بك يلتزم بواجهة ومعايير scikit-learn عن طريق تشغيل check_estimator على نموذج. يمكن أيضًا استخدام مُزَيِّن pytest parametrize_with_checks (انظر سلسلة الوثائق الخاصة به للحصول على التفاصيل والتفاعلات الممكنة مع pytest):

>>> from sklearn.utils.estimator_checks import check_estimator
>>> from sklearn.svm import LinearSVC
>>> check_estimator(LinearSVC())  # يمر

الدافع الرئيسي لجعل الفئة متوافقة مع واجهة مقدر scikit-learn قد يكون أنك تريد استخدامها مع أدوات اختيار النموذج وتقييمه مثل model_selection.GridSearchCV و pipeline.Pipeline.

قبل تفصيل الواجهة المطلوبة أدناه، نصف طريقتين لتحقيق الواجهة الصحيحة بسهولة أكبر.

get_params و set_params#

تحتوي جميع مقدرات scikit-learn على دوال get_params و set_params. لا تأخذ دالة get_params أي وسيطات وتُعيد قاموسًا لـ معلمات __init__ للمقدر، جنبًا إلى جنب مع قيمها.

يجب أن تأخذ وسيطة واحدة ذات كلمة رئيسية، deep، والتي تتلقى قيمة منطقية تحدد ما إذا كان يجب على الأسلوب إرجاع معلمات المقدرات الفرعية (بالنسبة لمعظم المقدرات، يمكن تجاهل ذلك). القيمة الافتراضية لـ deep يجب أن تكون True. على سبيل المثال، بالنظر إلى المقدر التالي:

>>> from sklearn.base import BaseEstimator
>>> from sklearn.linear_model import LogisticRegression
>>> class MyEstimator(BaseEstimator):
...     def __init__(self, subestimator=None, my_extra_param="random"):
...         self.subestimator = subestimator
...         self.my_extra_param = my_extra_param

ستتحكم المعلمة deep فيما إذا كان ينبغي الإبلاغ عن معلمات subestimator أم لا. وبالتالي، عندما deep=True، سيكون الناتج:

>>> my_estimator = MyEstimator(subestimator=LogisticRegression())
>>> for param, value in my_estimator.get_params(deep=True).items():
...     print(f"{param} -> {value}")
my_extra_param -> random
subestimator__C -> 1.0
subestimator__class_weight -> None
subestimator__dual -> False
subestimator__fit_intercept -> True
subestimator__intercept_scaling -> 1
subestimator__l1_ratio -> None
subestimator__max_iter -> 100
subestimator__multi_class -> deprecated
subestimator__n_jobs -> None
subestimator__penalty -> l2
subestimator__random_state -> None
subestimator__solver -> lbfgs
subestimator__tol -> 0.0001
subestimator__verbose -> 0
subestimator__warm_start -> False
subestimator -> LogisticRegression()

غالبًا ما يكون لـ subestimator اسم (مثل الخطوات المسماة في كائن Pipeline)، وفي هذه الحالة يجب أن يصبح المفتاح <name>__C و <name>__class_weight وما إلى ذلك.

بينما عندما deep=False، سيكون الناتج:

>>> for param, value in my_estimator.get_params(deep=False).items():
...     print(f"{param} -> {value}")
my_extra_param -> random
subestimator -> LogisticRegression()

من ناحية أخرى، يأخذ set_params معلمات __init__ كوسيطات ذات كلمات رئيسية، ويفك حزمها في قاموس بالشكل 'parameter': value ويعيِّن معلمات المقدر باستخدام هذا القاموس. يجب أن تكون القيمة المعادة هي المقدر نفسه.

على الرغم من أن آلية get_params ليست ضرورية (انظر الاستنساخ أدناه)، فإن دالة set_params ضرورية لأنها تُستخدم لتعيين المعلمات أثناء عمليات البحث الشبكية.

أسهل طريقة لتنفيذ هذه الدوال، والحصول على أسلوب __repr__ معقول، هي الوراثة من sklearn.base.BaseEstimator. إذا كنت لا ترغب في جعل التعليمات البرمجية الخاصة بك تعتمد على scikit-learn، فإن أسهل طريقة لـ تنفيذ الواجهة هي:

def get_params(self, deep=True):
    # لنفترض أن هذا المقدر يحتوي على المعلمتين "alpha" و "recursive"
    return {"alpha": self.alpha, "recursive": self.recursive}

def set_params(self, **parameters):
    for parameter, value in parameters.items():
        setattr(self, parameter, value)
    return self

المعلمات و init#

بما أن model_selection.GridSearchCV يستخدم set_params لتطبيق إعداد المعلمات على المقدرات، فمن الضروري أن يكون لاستدعاء set_params نفس التأثير مثل تعيين المعلمات باستخدام أسلوب __init__. أسهل طريقة موصى بها لتحقيق ذلك هي عدم إجراء أي تحقق من صحة المعلمات في __init__. يجب القيام بكل المنطق وراء معلمات المقدر، مثل ترجمة وسيطات السلسلة إلى دوال، في fit.

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

الاستنساخ#

لاستخدامه مع وحدة model_selection، يجب أن يدعم المقدر دالة base.clone لتكرار مقدر. يمكن القيام بذلك عن طريق توفير أسلوب get_params. إذا كان get_params موجودًا، فسيكون clone(estimator) نموذجًا لـ type(estimator) الذي تم استدعاء set_params عليه مع مستنسخات نتيجة estimator.get_params().

سيتم نسخ الكائنات التي لا توفر هذا الأسلوب بعمق (باستخدام دالة Python القياسية copy.deepcopy) إذا تم تمرير safe=False إلى clone.

يمكن للمقدرات تخصيص سلوك base.clone عن طريق تعريف أسلوب __sklearn_clone__. يجب أن يُعيد __sklearn_clone__ نموذجًا لـ المقدر. __sklearn_clone__ مفيد عندما يحتاج المقدر إلى التمسك ببعض الحالة عند استدعاء base.clone على المقدر. على سبيل المثال، يمكن تعريف مقدر تعريف ثابت للمحولات على النحو التالي:

class FrozenTransformer(BaseEstimator):
    def __init__(self, fitted_transformer):
        self.fitted_transformer = fitted_transformer

    def __getattr__(self, name):
        # سمات `fitted_transformer` الآن قابلة للوصول
        return getattr(self.fitted_transformer, name)

    def __sklearn_clone__(self):
        return self

    def fit(self, X, y):
        # لا يغير التوفيق حالة المقدر
        return self

    def fit_transform(self, X, y=None):
        # fit_transform يحول البيانات فقط
        return self.fitted_transformer.transform(X, y)

توافق خط الأنابيب#

لكي يكون المقدر قابلاً للاستخدام مع pipeline.Pipeline في أي خطوة باستثناء الخطوة الأخيرة، فإنه يحتاج إلى توفير دالة fit أو fit_transform. لكي تتمكن من تقييم خط الأنابيب على أي بيانات باستثناء مجموعة التدريب، فإنه يحتاج أيضًا إلى توفير دالة transform. لا توجد متطلبات خاصة للخطوة الأخيرة في خط الأنابيب، باستثناء أن لديها دالة fit. يجب أن تأخذ جميع دوال fit و fit_transform الوسيطات X, y، حتى لو لم يتم استخدام y. وبالمثل، لكي يكون score قابلًا للاستخدام، تحتاج الخطوة الأخيرة من خط الأنابيب إلى دالة score تقبل y اختياريًا.

أنواع المقدرات#

تعتمد بعض الوظائف الشائعة على نوع المقدر الذي تم تمريره. على سبيل المثال، يكون التحقق المتبادل في model_selection.GridSearchCV و model_selection.cross_val_score افتراضيًا طبقيًا عند استخدامه على مصنف، ولكن ليس بخلاف ذلك. وبالمثل، تحتاج أدوات التسجيل لمتوسط ​​الدقة التي تأخذ تنبؤًا مستمرًا إلى استدعاء decision_function للمصنفات، ولكن predict لمقدرات الانحدار. يتم تنفيذ هذا التمييز بين المصنفات ومقدرات الانحدار باستخدام سمة _estimator_type، التي تأخذ قيمة سلسلة. يجب أن تكون "classifier" للمصنفات و "regressor" لمقدرات الانحدار و "clusterer" لأساليب التجميع، لتعمل كما هو متوقع. ستعيِّن الوراثة من ClassifierMixin أو RegressorMixin أو ClusterMixin السمة تلقائيًا. عندما يحتاج مقدر التعريف التلوي إلى التمييز بين أنواع المقدرات، بدلاً من التحقق من _estimator_type مباشرةً، يجب استخدام مساعدين مثل base.is_classifier.

نماذج محددة#

يجب أن تقبل المصنفات وسيطات y (الهدف) إلى fit وهي متواليات (قوائم، مصفوفات) من السلاسل أو الأعداد الصحيحة. لا ينبغي لهم افتراض أن تسميات الفئات هي نطاق متجاور من الأعداد الصحيحة؛ بدلاً من ذلك، يجب عليهم تخزين قائمة بالفئات في سمة أو خاصية classes_. يجب أن يتطابق ترتيب تسميات الفئات في هذه السمة مع الترتيب الذي تُعيد به predict_proba و predict_log_proba و decision_function قيمها. أسهل طريقة لتحقيق ذلك هي وضع:

self.classes_, y = np.unique(y, return_inverse=True)

في fit. يُعيد هذا y جديدًا يحتوي على فهارس الفئات، بدلاً من التسميات، في النطاق [0، n_classes).

يجب أن يُعيد أسلوب predict الخاص بالمصنف مصفوفات تحتوي على تسميات فئات من classes_. في مصنف ينفذ decision_function، يمكن تحقيق ذلك باستخدام:

def predict(self, X):
    D = self.decision_function(X)
    return self.classes_[np.argmax(D, axis=1)]

في النماذج الخطية، يتم تخزين المعاملات في مصفوفة تسمى coef_، و يتم تخزين المصطلح المستقل في intercept_. يحتوي sklearn.linear_model._base على عدد قليل من الفئات الأساسية و mixins التي تنفذ أنماط النموذج الخطي الشائعة.

تحتوي الوحدة multiclass على دوال مفيدة للعمل مع مشكلات متعددة الفئات ومتعددة التسميات.

علامات المقدر#

تحذير

علامات المقدر تجريبية وتخضع واجهة برمجة التطبيقات للتغيير.

قدّم Scikit-learn علامات المقدر في الإصدار 0.21. هذه تعليقات توضيحية للمقدرات تسمح بفحص إمكانياتها برمجيًا، مثل دعم المصفوفة المتفرقة وأنواع المخرجات المدعومة والأساليب المدعومة. علامات المقدر هي قاموس يُعيده أسلوب _get_tags(). يتم استخدام هذه العلامات في الفحوصات الشائعة التي تُجريها دالة check_estimator ومُزَيِّن parametrize_with_checks. تحدد العلامات الفحوصات التي يجب تشغيلها وبيانات الإدخال المناسبة. يمكن أن تعتمد العلامات على معلمات المقدر أو حتى بنية النظام ويمكن بشكل عام تحديدها فقط في وقت التشغيل.

مجموعة علامات المقدر الحالية هي:

allow_nan (افتراضيًا = خطأ)

ما إذا كان المقدر يدعم البيانات ذات القيم المفقودة المشفرة على أنها np.nan

array_api_support (افتراضيًا = خطأ)

ما إذا كان المقدر يدعم المدخلات المتوافقة مع واجهة برمجة تطبيقات Array.

binary_only (افتراضيًا = خطأ)

ما إذا كان المقدر يدعم التصنيف الثنائي ولكنه يفتقر إلى دعم التصنيف متعدد الفئات.

multilabel (افتراضيًا = خطأ)

ما إذا كان المقدر يدعم المخرجات متعددة التسميات

multioutput (افتراضيًا = خطأ)

ما إذا كان مقدر الانحدار يدعم مخرجات متعددة الأهداف أو يدعم المصنف مخرجات متعددة الفئات ومتعددة المخرجات.

multioutput_only (افتراضيًا = خطأ)

ما إذا كان المقدر يدعم فقط التصنيف أو الانحدار متعدد المخرجات.

no_validation (افتراضيًا = خطأ)

ما إذا كان المقدر يتخطى التحقق من صحة الإدخال. هذا مخصص فقط لـ المحولات عديمة الحالة والوهمية!

non_deterministic (افتراضيًا = خطأ)

ما إذا كان المقدر غير حتمي نظرًا لـ random_state ثابت

pairwise (افتراضيًا = خطأ)

تشير سمة منطقية هذه إلى ما إذا كانت البيانات (X) fit وأمثالها من الأساليب تتكون من مقاييس زوجية للعينات بدلاً من تمثيل ميزة لكل عينة. عادةً ما تكون True حيث يحتوي المقدر على معلمة metric أو affinity أو kernel بقيمة "precomputed". الغرض الأساسي منها هو دعم meta_estimator أو إجراء التحقق المتبادل الذي يستخرج عينة فرعية من البيانات المخصصة لمقدر زوجي، حيث يجب فهرسة البيانات على كلا المحورين. على وجه التحديد، يتم استخدام هذه العلامة بواسطة sklearn.utils.metaestimators._safe_split لتقطيع الصفوف و الأعمدة.

preserves_dtype (افتراضيًا = [np.float64])

ينطبق فقط على المحولات. يتوافق مع أنواع البيانات التي سيتم الحفاظ عليها بحيث يكون X_trans.dtype هو نفسه X.dtype بعد استدعاء transformer.transform(X). إذا كانت هذه القائمة فارغة، فلا يتوقع أن يحافظ المحول على نوع البيانات. تعتبر القيمة الأولى في القائمة نوع البيانات الافتراضي، المطابق لـ نوع بيانات الإخراج عندما لا يتم الحفاظ على نوع بيانات الإدخال.

poor_score (افتراضيًا = خطأ)

ما إذا كان المقدر يفشل في توفير درجة "معقولة" لمجموعة الاختبار، والتي هي حاليًا للانحدار R2 من 0.5 على make_regression(n_samples=200, n_features=10, n_informative=1, bias=5.0, noise=20, random_state=42)، و للتصنيف دقة 0.83 على make_blobs(n_samples=300, random_state=0). تستند مجموعات البيانات والقيم هذه إلى المقدرات الحالية في sklearn وقد يتم استبدالها بشيء أكثر منهجية.

requires_fit (افتراضيًا = صحيح)

ما إذا كان المقدر يتطلب التوفيق قبل استدعاء أحد transform أو predict أو predict_proba أو decision_function.

requires_positive_X (افتراضيًا = خطأ)

ما إذا كان المقدر يتطلب X موجبًا.

requires_y (افتراضيًا = خطأ)

ما إذا كان المقدر يتطلب تمرير y إلى أساليب fit أو fit_predict أو fit_transform. تكون العلامة صحيحة للمقدرات التي ترث من ~sklearn.base.RegressorMixin و ~sklearn.base.ClassifierMixin.

requires_positive_y (افتراضيًا = خطأ)

ما إذا كان المقدر يتطلب y موجبًا (ينطبق فقط على الانحدار).

_skip_test (افتراضيًا = خطأ)

ما إذا كان سيتم تخطي الاختبارات الشائعة تمامًا. لا تستخدم هذا إلا إذا كان لديك سبب وجيه جدًا.

_xfail_checks (افتراضيًا = خطأ)

قاموس {check_name: reason} من الفحوصات الشائعة التي سيتم تمييزها على أنها XFAIL لـ pytest، عند استخدام parametrize_with_checks. سيتم ببساطة تجاهل هذه الفحوصات ولن يتم تشغيلها بواسطة check_estimator، ولكن سيتم طرح SkipTestWarning. لا تستخدم هذا إلا إذا كان هناك سبب وجيه جدًا لعدم اجتياز المقدر الخاص بك للفحص. لاحظ أيضًا أن استخدام هذه العلامة يخضع للتغيير بدرجة كبيرة لأننا نحاول جعلها أكثر مرونة: كن مستعدًا للتغييرات الجذرية في المستقبل.

stateless (افتراضيًا = خطأ)

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

X_types (افتراضيًا = ["2darray"])

أنواع الإدخال المدعومة لـ X كقائمة من السلاسل. يتم تشغيل الاختبارات حاليًا فقط إذا كانت "2darray" موجودة في القائمة، مما يدل على أن المقدر يأخذ مصفوفات numpy ثنائية الأبعاد مستمرة كمدخلات. القيمة الافتراضية هي ["2darray"]. الأنواع الأخرى المحتملة هي 'string' و 'sparse' و 'categorical' و dict و '1dlabels' و '2dlabels'. الهدف هو أنه في المستقبل سيحدد نوع الإدخال المدعوم البيانات المستخدمة أثناء الاختبار، خاصةً للبيانات 'string' و 'sparse' و 'categorical'. في الوقت الحالي، لا تستخدم الاختبارات الخاصة بالبيانات المتفرقة علامة 'sparse'.

من غير المحتمل أن تناسب القيم الافتراضية لكل علامة احتياجات المقدر الخاص بك المحدد. يمكن إنشاء علامات إضافية أو يمكن تجاوز علامات افتراضية عن طريق تعريف أسلوب _more_tags () الذي يُعيد قاموسًا بالعلامات المُلغاة المطلوبة أو العلامات الجديدة. فمثلا:

class MyMultiOutputEstimator(BaseEstimator):

    def _more_tags(self):
        return {'multioutput_only': True,
                'non_deterministic': True}

أي علامة ليست في _more_tags () ستعود إلى القيم الافتراضية الموثقة أعلاه.

حتى لو لم يكن ذلك مُوصىً به، فمن الممكن تجاوز أسلوب _get_tags (). لاحظ مع ذلك أنه يجب أن تكون جميع العلامات موجودة في القاموس. إذا لم تكن أي من المفاتيح الموثقة أعلاه موجودة في ناتج _get_tags ()، فسيحدث خطأ.

بالإضافة إلى العلامات، تحتاج المقدرات أيضًا إلى الإعلان عن أي معلمات غير اختيارية إلى __init__ في سمة الفئة _required_parameters، وهي قائمة أو tuple. إذا كانت _required_parameters هي ["estimator"] أو ["base_estimator"] فقط، فسيتم تهيئة المقدر باستخدام نموذج من LogisticRegression (أو RidgeRegression إذا كان المقدر مقدر انحدار) في الاختبارات. اختيار هذين النموذجين غريب نوعًا ما، لكن كلاهما يجب أن يوفر حلولًا قوية مغلقة الشكل.

واجهة برمجة تطبيقات المطور لـ set_output#

مع SLEP018، يُقدِّم scikit-learn واجهة برمجة تطبيقات set_output لتكوين المحولات لإخراج إطارات بيانات pandas. يتم تعريف واجهة برمجة تطبيقات set_output تلقائيًا إذا عرّف المحول get_feature_names_out وفئات فرعية base.TransformerMixin. يُستخدم get_feature_names_out للحصول على أسماء أعمدة ناتج pandas.

base.OneToOneFeatureMixin و base.ClassNamePrefixFeaturesOutMixin هما mixins مفيدان لتعريف get_feature_names_out. base.OneToOneFeatureMixin مفيد عندما يكون للمحول تطابق واحد لواحد بين ميزات الإدخال وميزات الإخراج، مثل StandardScaler. base.ClassNamePrefixFeaturesOutMixin مفيد عندما يحتاج المحول إلى إنشاء أسماء ميزات خاصة به، مثل PCA.

يمكنك إلغاء الاشتراك في واجهة برمجة تطبيقات set_output عن طريق تعيين auto_wrap_output_keys = None عند تعريف فئة فرعية مخصصة:

class MyTransformer(TransformerMixin, BaseEstimator, auto_wrap_output_keys=None):

    def fit(self, X, y=None):
        return self
    def transform(self, X, y=None):
        return X
    def get_feature_names_out(self, input_features=None):
        ...

القيمة الافتراضية لـ auto_wrap_output_keys هي ("transform",)، والتي تقوم تلقائيًا بالتفاف fit_transform و transform. يستخدم TransformerMixin آلية __init_subclass__ لاستهلاك auto_wrap_output_keys وتمرير جميع وسيطات الكلمات الرئيسية الأخرى إلى فئة super الخاصة به. لا ينبغي أن تعتمد __init_subclass__ لفئات Super على auto_wrap_output_keys.

بالنسبة للمحولات التي تُعيد مصفوفات متعددة في transform، سيقوم التفاف السيارات بالتفاف المصفوفة الأولى فقط ولن يغير المصفوفات الأخرى.

انظر تقديم واجهة برمجة التطبيقات set_output لمثال حول كيفية استخدام واجهة برمجة التطبيقات.

واجهة برمجة تطبيقات المطور لـ check_is_fitted#

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

انظر __sklearn_is_fitted__ كـ API للمطورين لمثال حول كيفية استخدام واجهة برمجة التطبيقات.

واجهة برمجة تطبيقات المطور لتمثيل HTML#

تحذير

واجهة برمجة تطبيقات تمثيل HTML تجريبية وتخضع واجهة برمجة التطبيقات للتغيير.

تعرض المقدرات التي ترث من BaseEstimator تمثيل HTML لأنفسهم في بيئات البرمجة التفاعلية مثل دفاتر ملاحظات Jupyter. على سبيل المثال، يمكننا عرض مخطط HTML هذا:

from sklearn.base import BaseEstimator

BaseEstimator()

يتم الحصول على تمثيل HTML الأولي عن طريق استدعاء دالة estimator_html_repr على نموذج مقدر.

لتخصيص عنوان URL المرتبط بوثائق المقدر (أي عند النقر فوق رمز "?" )، قم بتجاوز سمات _doc_link_module و _doc_link_template. بالإضافة إلى ذلك، يمكنك توفير أسلوب _doc_link_url_param_generator. عيِّن _doc_link_module إلى اسم الوحدة (المستوى الأعلى) التي تحتوي على المقدر الخاص بك. إذا لم تتطابق القيمة مع اسم وحدة المستوى الأعلى، فلن يحتوي تمثيل HTML على رابط إلى الوثائق. بالنسبة لمقدرات scikit-learn، يتم تعيين هذا إلى "sklearn".

يُستخدم _doc_link_template لإنشاء عنوان URL النهائي. افتراضيًا، يمكن أن يحتوي على متغيرين: estimator_module (الاسم الكامل للوحدة التي تحتوي على المقدر) و estimator_name (اسم فئة المقدر). إذا كنت بحاجة إلى المزيد من المتغيرات، فيجب عليك تنفيذ أسلوب _doc_link_url_param_generator الذي يجب أن يُعيد قاموسًا بالمتغيرات وقيمها. سيتم استخدام هذا القاموس لعرض _doc_link_template.

إرشادات الترميز#

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

تسهّل التعليمات البرمجية المنسقة بشكل موحد مشاركة ملكية التعليمات البرمجية. يحاول مشروع scikit-learn اتباع إرشادات Python الرسمية المفصّلة في PEP8 التي توضح كيفية تنسيق التعليمات البرمجية ومسافة بادئة لها. يرجى قراءتها و اتباعها.

بالإضافة إلى ذلك، نضيف الإرشادات التالية:

  • استخدم الشرطات السفلية لفصل الكلمات في الأسماء غير الفئوية: n_samples بدلاً من nsamples.

  • تجنب استخدام عبارات متعددة في سطر واحد. فضّل سطر إرجاع بعد عبارة تدفق التحكم (if/for).

  • استخدم عمليات الاستيراد النسبية للمراجع داخل scikit-learn.

  • اختبارات الوحدة هي استثناء للقاعدة السابقة؛ يجب أن تستخدم عمليات الاستيراد المطلقة، تمامًا كما تفعل تعليمات العميل البرمجية. والنتيجة الطبيعية هي أنه إذا صدّرت sklearn.foo فئة أو دالة تم تنفيذها في sklearn.foo.bar.baz، فيجب على الاختبار استيرادها من sklearn.foo.

  • يرجى عدم استخدام import * في أي حالة. يُعتبر ضارًا من قِبَل توصيات Python الرسمية. يجعل الشفرة أكثر صعوبة في القراءة لأن أصل الرموز لم يعد مرجعًا إليه صراحةً، ولكن الأهم من ذلك، أنه يمنع استخدام أداة تحليل ثابتة مثل pyflakes للعثور تلقائيًا على الأخطاء في scikit-learn.

  • استخدم معيار سلسلة وثائق numpy في جميع سلاسل الوثائق الخاصة بك.

يمكن العثور على مثال جيد للتعليمات البرمجية التي نحبها هنا.

التحقق من صحة الإدخال#

تحتوي الوحدة sklearn.utils على دوال مختلفة للتحقق من صحة الإدخال وتحويله. في بعض الأحيان، يكفي np.asarray للتحقق من الصحة؛ لا تستخدم np.asanyarray أو np.atleast_2d، حيث إنها تسمح بمرور np.matrix الخاص بـ NumPy، والذي يحتوي على واجهة برمجة تطبيقات مختلفة (على سبيل المثال، * تعني حاصل الضرب النقطي على np.matrix، ولكن حاصل الضرب Hadamard على np.ndarray).

في حالات أخرى، تأكد من استدعاء check_array على أي وسيطة تشبه المصفوفة يتم تمريرها إلى دالة واجهة برمجة تطبيقات scikit-learn. تعتمد المعلمات الدقيقة التي يجب استخدامها بشكل أساسي على ما إذا كان يجب قبول مصفوفات scipy.sparse وأيها.

لمزيد من المعلومات، راجع صفحة أدوات مساعدة للمطورين.

الأرقام العشوائية#

إذا كانت التعليمات البرمجية الخاصة بك تعتمد على مُولِّد أرقام عشوائية، فلا تستخدم numpy.random.random () أو إجراءات مماثلة. لضمان إمكانية التكرار في فحص الأخطاء، يجب أن يقبل الإجراء كلمة رئيسية random_state ويستخدم هذا لإنشاء كائن numpy.random.RandomState. انظر sklearn.utils.check_random_state في أدوات مساعدة للمطورين.

فيما يلي مثال بسيط للتعليمات البرمجية باستخدام بعض الإرشادات المذكورة أعلاه:

from sklearn.utils import check_array, check_random_state

def choose_random_sample(X, random_state=0):
    """اختر نقطة عشوائية من X.

    المعلمات
    ----------
    X : مصفوفة من الشكل (n_samples, n_features)
        مصفوفة تمثل البيانات.
    random_state : int أو نموذج RandomState، افتراضيًا = 0
        بذرة مُولِّد الأرقام العشوائية الزائفة الذي يختار
        عينة عشوائية. مرّر int للحصول على ناتج قابل للتكرار عبر استدعاءات دوال
        متعددة.
        انظر :term:`المُصطلحات <random_state>`.

    المُخرجات
    -------
    x : ndarray من الشكل (n_features,)
        نقطة عشوائية محددة من X.
    """
    X = check_array(X)
    random_state = check_random_state(random_state)
    i = random_state.randint(X.shape[0])
    return X[i]

إذا كنت تستخدم العشوائية في مقدر بدلاً من دالة قائمة بذاتها، فتنطبق بعض الإرشادات الإضافية.

أولاً، يجب أن يأخذ المقدر وسيطة random_state إلى __init__ الخاص به بقيمة افتراضية None. يجب أن يخزن قيمة هذه الوسيطة، دون تعديل، في سمة random_state. يمكن لـ fit استدعاء check_random_state على تلك السمة للحصول على مُولِّد أرقام عشوائية فعلي. إذا كانت هناك حاجة للعشوائية لسبب ما بعد fit، فيجب تخزين RNG في سمة random_state_. يجب أن يوضح المثال التالي هذا:

class GaussianNoise(BaseEstimator, TransformerMixin):
    """يتجاهل هذا المقدر مدخلاته ويُعيد ضوضاء غاوسية عشوائية.

    كما أنه لا يلتزم بجميع اتفاقيات scikit-learn،
    ولكنه يعرض كيفية التعامل مع العشوائية.
    """

    def __init__(self, n_components=100, random_state=None):
        self.random_state = random_state
        self.n_components = n_components

    # يتم تجاهل الوسيطات على أي حال، لذلك نجعلها اختيارية
    def fit(self, X=None, y=None):
        self.random_state_ = check_random_state(self.random_state)

    def transform(self, X):
        n_samples = X.shape[0]
        return self.random_state_.randn(n_samples, self.n_components)

سبب هذا الإعداد هو إمكانية التكرار: عندما يكون المقدر fit مرتين لنفس البيانات، يجب أن ينتج نموذجًا متطابقًا في كلتا المرتين، ومن ثم التحقق من الصحة في fit، وليس __init__.

التأكيدات العددية في الاختبارات#

عند التأكيد على شبه مساواة مصفوفات القيم المستمرة، استخدم sklearn.utils._testing.assert_allclose.

يتم استنتاج التسامح النسبي تلقائيًا من dtypes المصفوفات المقدمة (لأنواع بيانات float32 و float64 على وجه الخصوص) ولكن يمكنك التجاوز عبر rtol.

عند مقارنة مصفوفات العناصر الصفرية، يرجى تقديم قيمة غير صفرية لـ التسامح المطلق عبر atol.

لمزيد من المعلومات، يرجى الرجوع إلى سلسلة الوثائق الخاصة بـ sklearn.utils._testing.assert_allclose.