Entwicklung von scikit-learn-Estimators#
Ob Sie einen Estimator zur Aufnahme in scikit-learn vorschlagen, ein separates Paket entwickeln, das mit scikit-learn kompatibel ist, oder benutzerdefinierte Komponenten für Ihre eigenen Projekte implementieren, dieses Kapitel beschreibt, wie Objekte entwickelt werden, die sicher mit scikit-learn-Pipelines und Modellselektionswerkzeugen interagieren.
Dieser Abschnitt beschreibt die öffentliche API, die Sie für einen scikit-learn-kompatiblen Estimator verwenden und implementieren sollten. Innerhalb von scikit-learn selbst experimentieren wir und verwenden einige private Tools, und unser Ziel ist es immer, sie öffentlich zu machen, sobald sie stabil genug sind, damit Sie sie auch in Ihren eigenen Projekten verwenden können.
APIs von scikit-learn-Objekten#
Es gibt zwei Haupttypen von Estimators. Die erste Gruppe kann als einfache Estimators betrachtet werden, zu der die meisten Estimators wie LogisticRegression oder RandomForestClassifier gehören. Die zweite Gruppe sind Meta-Estimators, die andere Estimators umschließen. Pipeline und GridSearchCV sind zwei Beispiele für Meta-Estimators.
Hier beginnen wir mit einigen Begriffen und illustrieren dann, wie Sie Ihre eigenen Estimators implementieren können.
Elemente der scikit-learn API werden im Glossar der gebräuchlichen Begriffe und API-Elemente detaillierter beschrieben.
Unterschiedliche Objekte#
Die Hauptobjekte in scikit-learn sind (eine Klasse kann mehrere Schnittstellen implementieren)
- Estimator:
Das Basiselement, implementiert eine
fitMethode, um aus Daten zu lernen, entwederestimator = estimator.fit(data, targets)
oder
estimator = estimator.fit(data)
- Predictor:
Für überwachtes Lernen oder einige unüberwachte Probleme, implementiert
prediction = predictor.predict(data)
Klassifikationsalgorithmen bieten normalerweise auch eine Möglichkeit, die Sicherheit einer Vorhersage zu quantifizieren, entweder unter Verwendung von
decision_functionoderpredict_probaprobability = predictor.predict_proba(data)
- Transformer:
Zum Modifizieren von Daten auf überwachte oder unüberwachte Weise (z. B. durch Hinzufügen, Ändern oder Entfernen von Spalten, aber nicht durch Hinzufügen oder Entfernen von Zeilen). Implementiert
new_data = transformer.transform(data)
Wenn Anpassen und Transformieren effizienter zusammen als getrennt durchgeführt werden kann, implementiert
new_data = transformer.fit_transform(data)
- Modell:
Ein Modell, das ein Maß für die Güte der Anpassung oder eine Wahrscheinlichkeit für unbekannte Daten liefern kann, implementiert (höher ist besser)
score = model.score(data)
Estimators#
Die API hat ein vorherrschendes Objekt: den Estimator. Ein Estimator ist ein Objekt, das ein Modell auf Basis von Trainingsdaten anpasst und bestimmte Eigenschaften für neue Daten ableiten kann. Es kann zum Beispiel ein Klassifikator oder ein Regressor sein. Alle Estimators implementieren die fit-Methode
estimator.fit(X, y)
Von allen Methoden, die ein Estimator implementiert, ist fit normalerweise diejenige, die Sie selbst implementieren möchten. Andere Methoden wie set_params, get_params usw. sind in BaseEstimator implementiert, von dem Sie erben sollten. Möglicherweise müssen Sie von weiteren Mixins erben, die wir später erklären werden.
Instanziierung#
Dies betrifft die Erstellung eines Objekts. Die __init__ Methode des Objekts kann Konstanten als Argumente akzeptieren, die das Verhalten des Estimators bestimmen (wie die alpha Konstante in SGDClassifier). Sie sollte jedoch nicht die tatsächlichen Trainingsdaten als Argument erhalten, da dies der fit() Methode überlassen bleibt.
clf2 = SGDClassifier(alpha=2.3)
clf3 = SGDClassifier([[1, 2], [2, 3]], [-1, 1]) # WRONG!
Idealerweise sollten alle von __init__ akzeptierten Argumente Schlüsselwortargumente mit einem Standardwert sein. Mit anderen Worten, ein Benutzer sollte einen Estimator instanziieren können, ohne ihm Argumente zu übergeben. In einigen Fällen, in denen es keine sinnvollen Standardwerte für ein Argument gibt, können diese ohne Standardwert belassen werden. In scikit-learn selbst gibt es nur wenige Stellen, nur bei einigen Meta-Estimators, bei denen das Unter-Estimator-Argument ein erforderliches Argument ist.
Die meisten Argumente entsprechen Hyperparametern, die das Modell oder das Optimierungsproblem beschreiben, das der Estimator zu lösen versucht. Andere Parameter können definieren, wie der Estimator sich verhält, z. B. die Position eines Caches zur Speicherung einiger Daten. Diese anfänglichen Argumente (oder Parameter) werden vom Estimator immer gespeichert. Beachten Sie auch, dass sie nicht im Abschnitt "Attribute", sondern im Abschnitt "Parameter" für diesen Estimator dokumentiert werden sollten.
Darüber hinaus **sollte jedes Schlüsselwortargument, das von** __init__ **akzeptiert wird, einem Attribut der Instanz entsprechen**. Scikit-learn verwendet dies, um die relevanten Attribute zu finden, die einem Estimator beim Modell-Selection zugewiesen werden sollen.
Zusammenfassend sollte ein __init__ so aussehen:
def __init__(self, param1=1, param2=2):
self.param1 = param1
self.param2 = param2
Es sollte keine Logik geben, nicht einmal Eingabevalidierung, und die Parameter sollten nicht geändert werden; was auch bedeutet, dass sie idealerweise keine veränderlichen Objekte wie Listen oder Wörterbücher sein sollten. Wenn sie veränderlich sind, sollten sie kopiert werden, bevor sie geändert werden. Die entsprechende Logik sollte dort platziert werden, wo die Parameter verwendet werden, typischerweise in fit. Das Folgende ist falsch:
def __init__(self, param1=1, param2=2, param3=3):
# WRONG: parameters should not be modified
if param1 > 1:
param2 += 1
self.param1 = param1
# WRONG: the object's attributes should have exactly the name of
# the argument in the constructor
self.param3 = param2
Der Grund für die Verschiebung der Validierung ist, dass, wenn __init__ eine Eingabevalidierung enthält, dieselbe Validierung in set_params durchgeführt werden müsste, das in Algorithmen wie GridSearchCV verwendet wird.
Es wird auch erwartet, dass Parameter mit nachgestelltem _ **nicht innerhalb der** __init__ **Methode gesetzt werden**. Mehr Details zu Attributen, die keine Init-Argumente sind, folgen in Kürze.
Fitting#
Das Nächste, was Sie wahrscheinlich tun möchten, ist, einige Parameter im Modell zu schätzen. Dies wird in der fit() Methode implementiert, und hier findet das Training statt. Zum Beispiel ist dies der Ort, an dem die Berechnung zum Erlernen oder Schätzen von Koeffizienten für ein lineares Modell stattfindet.
Die fit() Methode nimmt die Trainingsdaten als Argumente entgegen, die ein Array im Falle von unüberwachtem Lernen oder zwei Arrays im Falle von überwachtem Lernen sein können. Andere Metadaten, die mit den Trainingsdaten geliefert werden, wie z. B. sample_weight, können ebenfalls als Schlüsselwortargumente an fit übergeben werden.
Beachten Sie, dass das Modell mit X und y angepasst wird, aber das Objekt keine Referenz auf X und y speichert. Es gibt jedoch einige Ausnahmen, wie im Fall von vorab berechneten Kernels, bei denen diese Daten für die Verwendung durch die predict-Methode gespeichert werden müssen.
Parameter |
|
|---|---|
X |
array-ähnlich der Form (n_samples, n_features) |
y |
array-ähnlich der Form (n_samples,) |
kwargs |
optionale datenabhängige Parameter |
Die Anzahl der Samples, d. h. X.shape[0], sollte mit y.shape[0] übereinstimmen. Wenn diese Anforderung nicht erfüllt ist, sollte eine Ausnahme vom Typ ValueError ausgelöst werden.
y kann im Falle von unüberwachtem Lernen ignoriert werden. Um jedoch die Verwendung des Estimators als Teil einer Pipeline zu ermöglichen, die sowohl überwachte als auch unüberwachte Transformer mischen kann, müssen auch unüberwachte Estimators ein y=None Schlüsselwortargument an zweiter Stelle akzeptieren, das vom Estimator einfach ignoriert wird. Aus demselben Grund müssen die Methoden fit_predict, fit_transform, score und partial_fit ein y Argument an zweiter Stelle akzeptieren, wenn sie implementiert sind.
Die Methode sollte das Objekt (self) zurückgeben. Dieses Muster ist nützlich, um schnelle Einzeiler in einer IPython-Sitzung zu implementieren, wie z. B.:
y_predicted = SGDClassifier(alpha=10).fit(X_train, y_train).predict(X_test)
Abhängig von der Art des Algorithmus kann fit manchmal zusätzliche Schlüsselwortargumente akzeptieren. Jedoch sollte jeder Parameter, dem ein Wert zugewiesen werden kann, bevor auf die Daten zugegriffen werden kann, ein __init__ Schlüsselwortargument sein. Idealerweise sollten **Fit-Parameter auf direkt datenabhängige Variablen beschränkt sein**. Zum Beispiel sind eine Gram-Matrix oder eine Affinitätsmatrix, die aus der Datenmatrix X vorab berechnet werden, datenabhängig. Ein Toleranz-Stoppkriterium tol ist nicht direkt datenabhängig (obwohl der optimale Wert gemäß einer Bewertungsfunktion wahrscheinlich dies ist).
Wenn fit aufgerufen wird, sollte jeder vorherige Aufruf von fit ignoriert werden. Im Allgemeinen sollte der Aufruf von estimator.fit(X1) und dann estimator.fit(X2) dasselbe sein wie nur der Aufruf von estimator.fit(X2). Dies ist jedoch in der Praxis möglicherweise nicht der Fall, wenn fit von einem Zufallsprozess abhängt, siehe random_state. Eine weitere Ausnahme von dieser Regel ist, wenn der Hyperparameter warm_start für unterstützte Estimators auf True gesetzt ist. warm_start=True bedeutet, dass der vorherige Zustand der trainierbaren Parameter des Estimators wiederverwendet wird, anstatt die Standard-Initialisierungsstrategie zu verwenden.
Geschätzte Attribute#
Gemäß den Konventionen von scikit-learn müssen Attribute, die Sie Ihren Benutzern als öffentliche Attribute zugänglich machen möchten und die aus den Daten geschätzt oder gelernt wurden, immer einen Namen haben, der mit einem nachgestellten Unterstrich endet. Zum Beispiel würden die Koeffizienten eines Regressions-Estimators nach Aufruf von fit in einem coef_ Attribut gespeichert. Ebenso sollten Attribute, die Sie im Prozess lernen und die Sie speichern, aber dem Benutzer nicht zugänglich machen möchten, mit einem führenden Unterstrich beginnen, z. B. _intermediate_coefs. Die erste Gruppe (mit nachgestelltem Unterstrich) müssen Sie als "Attribute" dokumentieren, die zweite Gruppe (mit führendem Unterstrich) nicht.
Die geschätzten Attribute werden erwartet, überschrieben zu werden, wenn Sie fit ein zweites Mal aufrufen.
Universelle Attribute#
Estimators, die tabellarische Eingaben erwarten, sollten zur Zeit von fit ein n_features_in_ Attribut setzen, um die Anzahl der Features anzugeben, die der Estimator für nachfolgende Aufrufe von predict oder transform erwartet. Weitere Details finden Sie in SLEP010.
Ebenso sollten Estimators, denen DataFrames wie Pandas oder Polars übergeben werden, ein feature_names_in_ Attribut setzen, um die Feature-Namen der Eingabedaten anzugeben, wie in SLEP007 beschrieben. Die Verwendung von validate_data setzt diese Attribute automatisch für Sie.
Einen eigenen Estimator erstellen#
Wenn Sie einen neuen scikit-learn-kompatiblen Estimator implementieren möchten, gibt es neben der oben beschriebenen scikit-learn API einige interne Aspekte von scikit-learn, die Sie kennen sollten. Sie können überprüfen, ob Ihr Estimator der scikit-learn-Schnittstelle und den Standards entspricht, indem Sie check_estimator auf einer Instanz ausführen. Der pytest-Decorator parametrize_with_checks kann ebenfalls verwendet werden (siehe seine Docstring für Details und mögliche Interaktionen mit pytest).
>>> from sklearn.utils.estimator_checks import check_estimator
>>> from sklearn.tree import DecisionTreeClassifier
>>> check_estimator(DecisionTreeClassifier()) # passes
[...]
Die Hauptmotivation, eine Klasse mit der scikit-learn Estimator-Schnittstelle kompatibel zu machen, könnte sein, dass Sie sie mit Werkzeugen zur Modellevaluierung und -auswahl wie GridSearchCV und Pipeline verwenden möchten.
Bevor die erforderliche Schnittstelle unten detailliert beschrieben wird, beschreiben wir zwei Möglichkeiten, die richtige Schnittstelle einfacher zu erreichen.
Und Sie können überprüfen, ob der obige Estimator alle gängigen Checks besteht:
>>> from sklearn.utils.estimator_checks import check_estimator
>>> check_estimator(TemplateClassifier()) # passes
get_params und set_params#
Alle scikit-learn Estimators verfügen über die Funktionen get_params und set_params.
Die Funktion get_params nimmt keine Argumente entgegen und gibt ein Wörterbuch der __init__ Parameter des Estimators zusammen mit ihren Werten zurück.
Sie nimmt ein Schlüsselwortargument, deep, entgegen, das einen booleschen Wert erhält, der bestimmt, ob die Methode die Parameter von Sub-Estimators zurückgeben soll (nur relevant für Meta-Estimators). Der Standardwert für deep ist True. Zum Beispiel, wenn man den folgenden Estimator betrachtet:
>>> 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
Der Parameter deep steuert, ob die Parameter des subestimator gemeldet werden oder nicht. Wenn also deep=True ist, wird die Ausgabe sein:
>>> 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 -> 0.0
subestimator__max_iter -> 100
subestimator__n_jobs -> None
subestimator__penalty -> deprecated
subestimator__random_state -> None
subestimator__solver -> lbfgs
subestimator__tol -> 0.0001
subestimator__verbose -> 0
subestimator__warm_start -> False
subestimator -> LogisticRegression()
Wenn der Meta-Estimator mehrere Sub-Estimators verwendet, haben diese oft Namen (wie z. B. benannte Schritte in einem Pipeline Objekt), in diesem Fall wird der Schlüssel <name>__C, <name>__class_weight usw. sein.
Wenn deep=False ist, wird die Ausgabe sein:
>>> for param, value in my_estimator.get_params(deep=False).items():
... print(f"{param} -> {value}")
my_extra_param -> random
subestimator -> LogisticRegression()
Andererseits nimmt set_params die Parameter von __init__ als Schlüsselwortargumente entgegen, entpackt sie in ein Wörterbuch der Form 'parameter': value und setzt die Parameter des Estimators mit diesem Wörterbuch. Es gibt den Estimator selbst zurück.
Die Funktion set_params wird verwendet, um beispielsweise Parameter während der Gitterauswahl zu setzen.
Klonen#
Wie bereits erwähnt, sollten veränderliche Konstruktorargumente kopiert werden, bevor sie geändert werden. Dies gilt auch für Konstruktorargumente, die Estimators sind. Deshalb erstellen Meta-Estimators wie GridSearchCV eine Kopie des gegebenen Estimators, bevor sie ihn ändern.
In scikit-learn erhalten wir jedoch beim Kopieren eines Estimators einen nicht angepassten Estimator, bei dem nur die Konstruktorargumente kopiert werden (mit einigen Ausnahmen, z. B. Attribute, die sich auf interne Maschinerie wie das Metadaten-Routing beziehen).
Die Funktion, die für dieses Verhalten verantwortlich ist, ist clone.
Estimators können das Verhalten von base.clone anpassen, indem sie die Methode base.BaseEstimator.__sklearn_clone__ überschreiben. __sklearn_clone__ muss eine Instanz des Estimators zurückgeben. __sklearn_clone__ ist nützlich, wenn ein Estimator einen Zustand beibehalten muss, wenn base.clone für den Estimator aufgerufen wird. Zum Beispiel verwendet FrozenEstimator dies.
Estimator-Typen#
Unter den einfachen Estimators (im Gegensatz zu Meta-Estimators) sind die häufigsten Typen Transformer, Klassifikatoren, Regressoren und Clustering-Algorithmen.
Transformer erben von TransformerMixin und implementieren eine transform Methode. Dies sind Estimators, die die Eingabe nehmen und sie auf irgendeine Weise transformieren. Beachten Sie, dass sie niemals die Anzahl der Eingabe-Samples ändern sollten und die Ausgabe von transform den Eingabe-Samples in der gleichen Reihenfolge entsprechen sollte.
Regressoren erben von RegressorMixin und implementieren eine predict Methode. Sie sollten numerische y in ihrer fit Methode akzeptieren. Regressoren verwenden r2_score standardmäßig in ihrer score Methode.
Klassifikatoren erben von ClassifierMixin. Wenn zutreffend, können Klassifikatoren decision_function implementieren, um rohe Entscheidungswerte zurückzugeben, basierend auf denen predict seine Entscheidung treffen kann. Wenn die Berechnung von Wahrscheinlichkeiten unterstützt wird, können Klassifikatoren auch predict_proba und predict_log_proba implementieren.
Klassifikatoren sollten y (Ziel)-Argumente an fit akzeptieren, die Sequenzen (Listen, Arrays) von entweder Zeichenketten oder Ganzzahlen sind. Sie sollten nicht davon ausgehen, dass die Klassenbezeichnungen ein kontinuierlicher Bereich von Ganzzahlen sind; stattdessen sollten sie eine Liste von Klassen in einem classes_ Attribut oder einer Eigenschaft speichern. Die Reihenfolge der Klassenbezeichnungen in diesem Attribut sollte mit der Reihenfolge übereinstimmen, in der predict_proba, predict_log_proba und decision_function ihre Werte zurückgeben. Der einfachste Weg, dies zu erreichen, ist das Platzieren von
self.classes_, y = np.unique(y, return_inverse=True)
in fit. Dies gibt ein neues y zurück, das Klassenindizes anstelle von Bezeichnungen im Bereich [0, n_classes) enthält.
Die predict Methode eines Klassifikators sollte Arrays mit Klassenbezeichnungen aus classes_ zurückgeben. In einem Klassifikator, der decision_function implementiert, kann dies mit
def predict(self, X):
D = self.decision_function(X)
return self.classes_[np.argmax(D, axis=1)]
Das Modul multiclass enthält nützliche Funktionen für die Arbeit mit Mehrklassen- und Mehrfachlabel-Problemen.
Clustering-Algorithmen erben von ClusterMixin. Idealerweise sollten sie einen y Parameter in ihrer fit Methode akzeptieren, dieser sollte jedoch ignoriert werden. Clustering-Algorithmen sollten ein labels_ Attribut setzen, das die jedem Sample zugewiesenen Labels speichert. Falls zutreffend, können sie auch eine predict Methode implementieren, die die neu gegebenen Samples zugewiesenen Labels zurückgibt.
Wenn der Typ eines gegebenen Estimators überprüft werden muss, z. B. in einem Meta-Estimator, kann man prüfen, ob das gegebene Objekt eine transform Methode für Transformer implementiert, und ansonsten Hilfsfunktionen wie is_classifier oder is_regressor verwenden.
Entwickler-API für set_output#
Mit SLEP018 führt scikit-learn die set_output API ein, um Transformer so zu konfigurieren, dass sie pandas DataFrames ausgeben. Die set_output API wird automatisch definiert, wenn der Transformer get_feature_names_out definiert und base.TransformerMixin untererbt. get_feature_names_out wird verwendet, um die Spaltennamen der Pandas-Ausgabe zu erhalten.
base.OneToOneFeatureMixin und base.ClassNamePrefixFeaturesOutMixin sind hilfreiche Mixins zur Definition von get_feature_names_out. base.OneToOneFeatureMixin ist nützlich, wenn der Transformer eine Eins-zu-Eins-Entsprechung zwischen Eingabefeatures und Ausgabefeatures hat, wie z. B. StandardScaler. base.ClassNamePrefixFeaturesOutMixin ist nützlich, wenn der Transformer eigene Feature-Namen generieren muss, wie z. B. PCA.
Sie können sich von der set_output API abmelden, indem Sie auto_wrap_output_keys=None setzen, wenn Sie eine benutzerdefinierte Unterklasse definieren.
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):
...
Der Standardwert für auto_wrap_output_keys ist ("transform",), was automatisch fit_transform und transform umschließt. Die TransformerMixin verwendet den Mechanismus von __init_subclass__, um auto_wrap_output_keys zu verarbeiten und alle anderen Schlüsselwortargumente an seine Oberklasse weiterzugeben. Die __init_subclass__ der Oberklassen sollten sich nicht auf auto_wrap_output_keys verlassen.
Bei Transformern, die mehrere Arrays in transform zurückgeben, umschließt das automatische Umschließen nur das erste Array und verändert die anderen Arrays nicht.
Siehe Einführung der set_output API für ein Beispiel zur Verwendung der API.
Entwickler-API für check_is_fitted#
Standardmäßig prüft check_is_fitted, ob im Instanzattribut ein nachgestellter Unterstrich vorhanden ist, z. B. coef_. Ein Estimator kann das Verhalten ändern, indem er eine Methode __sklearn_is_fitted__ implementiert, die keine Argumente entgegennimmt und einen booleschen Wert zurückgibt. Wenn diese Methode existiert, gibt check_is_fitted einfach deren Ausgabe zurück.
Siehe __sklearn_is_fitted__ als Entwickler-API für ein Beispiel zur Verwendung der API.
Entwickler-API für HTML-Darstellung#
Warnung
Die API für die HTML-Darstellung ist experimentell und die API kann sich ändern.
Estimators, die von BaseEstimator erben, zeigen eine HTML-Darstellung von sich selbst in interaktiven Programmierumgebungen wie Jupyter Notebooks. Zum Beispiel können wir dieses HTML-Diagramm anzeigen
from sklearn.base import BaseEstimator
BaseEstimator()
Die rohe HTML-Darstellung wird durch Aufrufen der Funktion estimator_html_repr auf einer Estimator-Instanz erhalten.
Um die URL, die auf die Dokumentation eines Estimators verweist (d. h. wenn auf das "?"-Symbol geklickt wird), anzupassen, überschreiben Sie die Attribute _doc_link_module und _doc_link_template. Zusätzlich können Sie eine Methode _doc_link_url_param_generator bereitstellen. Setzen Sie _doc_link_module auf den Namen des (obersten) Moduls, das Ihren Estimator enthält. Wenn der Wert nicht mit dem Namen des obersten Moduls übereinstimmt, enthält die HTML-Darstellung keinen Link zur Dokumentation. Für scikit-learn-Estimators ist dies auf "sklearn" gesetzt.
Die _doc_link_template wird verwendet, um die endgültige URL zu erstellen. Standardmäßig kann sie zwei Variablen enthalten: estimator_module (der vollständige Name des Moduls, das den Estimator enthält) und estimator_name (der Klassenname des Estimators). Wenn Sie mehr Variablen benötigen, sollten Sie die Methode _doc_link_url_param_generator implementieren, die ein Wörterbuch der Variablen und ihrer Werte zurückgeben sollte. Dieses Wörterbuch wird verwendet, um die _doc_link_template zu rendern.
Codierungsrichtlinien#
Im Folgenden finden Sie einige Richtlinien, wie neuer Code für die Aufnahme in scikit-learn geschrieben werden sollte und die möglicherweise für externe Projekte übernommen werden können. Natürlich gibt es Sonderfälle und Ausnahmen von diesen Regeln. Die Befolgung dieser Regeln bei der Einreichung von neuem Code erleichtert jedoch die Überprüfung, sodass neuer Code in kürzerer Zeit integriert werden kann.
Einheitlich formatierter Code erleichtert die gemeinsame Verantwortung für den Code. Das scikit-learn-Projekt versucht, die offiziellen Python-Richtlinien in PEP8 genau zu befolgen, die beschreiben, wie Code formatiert und eingerückt werden sollte. Bitte lesen Sie diese und befolgen Sie sie.
Darüber hinaus fügen wir die folgenden Richtlinien hinzu
Verwenden Sie Unterstriche zur Trennung von Wörtern in Nicht-Klassennamen:
n_samplesanstelle vonnsamples.Vermeiden Sie mehrere Anweisungen in einer Zeile. Bevorzugen Sie einen Zeilenumbruch nach einer Kontrollflussanweisung (
if/for).Verwenden Sie absolute Importe
Unit-Tests sollten Imports verwenden, die exakt denen des Client-Codes entsprechen. Wenn
sklearn.fooeine Klasse oder Funktion exportiert, die insklearn.foo.bar.bazimplementiert ist, sollte der Test sie vonsklearn.fooimportieren.Bitte verwenden Sie unter keinen Umständen
import *. Dies wird von den offiziellen Python-Empfehlungen als schädlich angesehen. Es macht den Code schwerer lesbar, da der Ursprung der Symbole nicht mehr explizit referenziert wird, aber vor allem verhindert es die Verwendung eines statischen Analysewerkzeugs wie pyflakes, um automatisch Fehler in scikit-learn zu finden.Verwenden Sie den NumPy Docstring Standard in allen Ihren Docstrings.
Ein gutes Beispiel für Code, den wir mögen, finden Sie hier.
Eingabevalidierung#
Das Modul sklearn.utils enthält verschiedene Funktionen zur Eingabevalidierung und -konvertierung. Manchmal reicht np.asarray für die Validierung aus; verwenden Sie nicht np.asanyarray oder np.atleast_2d, da diese NumPy's np.matrix durchlassen, das eine andere API hat (z. B. bedeutet * bei np.matrix das Skalarprodukt, bei np.ndarray jedoch das Hadamard-Produkt).
In anderen Fällen sollten Sie unbedingt check_array für jedes Array-ähnliche Argument aufrufen, das an eine scikit-learn API-Funktion übergeben wird. Die genauen zu verwendenden Parameter hängen hauptsächlich davon ab, ob und welche scipy.sparse Matrizen akzeptiert werden müssen.
Weitere Informationen finden Sie auf der Seite Hilfsprogramme für Entwickler.
Zufallszahlen#
Wenn Ihr Code von einem Zufallszahlengenerator abhängt, verwenden Sie nicht numpy.random.random() oder ähnliche Routinen. Um die Wiederholbarkeit bei der Fehlerprüfung zu gewährleisten, sollte die Routine ein Schlüsselwort random_state akzeptieren und dies zur Erstellung eines numpy.random.RandomState Objekts verwenden. Siehe sklearn.utils.check_random_state in Hilfsprogramme für Entwickler.
Hier ist ein einfaches Beispiel für Code, der einige der oben genannten Richtlinien verwendet.
from sklearn.utils import check_array, check_random_state
def choose_random_sample(X, random_state=0):
"""Choose a random point from X.
Parameters
----------
X : array-like of shape (n_samples, n_features)
An array representing the data.
random_state : int or RandomState instance, default=0
The seed of the pseudo random number generator that selects a
random sample. Pass an int for reproducible output across multiple
function calls.
See :term:`Glossary <random_state>`.
Returns
-------
x : ndarray of shape (n_features,)
A random point selected from X.
"""
X = check_array(X)
random_state = check_random_state(random_state)
i = random_state.randint(X.shape[0])
return X[i]
Wenn Sie Zufälligkeit in einem Estimator anstelle einer eigenständigen Funktion verwenden, gelten zusätzliche Richtlinien.
Zunächst sollte der Estimator ein random_state-Argument für seinen __init__ mit einem Standardwert von None annehmen. Er sollte den Wert dieses Arguments unverändert in einem Attribut random_state speichern. fit kann check_random_state auf dieses Attribut anwenden, um einen tatsächlichen Zufallszahlengenerator zu erhalten. Wenn aus irgendeinem Grund Zufälligkeit nach fit benötigt wird, sollte der RNG in einem Attribut random_state_ gespeichert werden. Das folgende Beispiel sollte dies verdeutlichen.
class GaussianNoise(BaseEstimator, TransformerMixin):
"""This estimator ignores its input and returns random Gaussian noise.
It also does not adhere to all scikit-learn conventions,
but showcases how to handle randomness.
"""
def __init__(self, n_components=100, random_state=None):
self.random_state = random_state
self.n_components = n_components
# the arguments are ignored anyway, so we make them optional
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)
Der Grund für diese Einrichtung ist die Reproduzierbarkeit: Wenn ein Estimator zweimal auf dieselben Daten fit wird, sollte er beide Male ein identisches Modell erzeugen, daher die Validierung in fit, nicht in __init__.
Numerische Prüfungen in Tests#
Beim Prüfen der Quasi-Gleichheit von Arrays mit kontinuierlichen Werten sollten Sie sklearn.utils._testing.assert_allclose verwenden.
Die relative Toleranz wird automatisch aus den Datentypen der bereitgestellten Arrays abgeleitet (insbesondere für float32 und float64 Datentypen), kann aber über rtol überschrieben werden.
Beim Vergleichen von Arrays mit Nullelementen stellen Sie bitte einen Nicht-Null-Wert für die absolute Toleranz über atol bereit.
Weitere Informationen finden Sie im Docstring von sklearn.utils._testing.assert_allclose.