Примечание
Перейти в конец чтобы скачать полный пример кода или запустить этот пример в браузере через JupyterLite или Binder.
Маршрутизация метаданных#
Этот документ показывает, как вы можете использовать механизм маршрутизации метаданных в scikit-learn для маршрутизации метаданных к оценивателям, оценщикам и разделителям CV, которые их потребляют.
Чтобы лучше понять следующий документ, нам нужно ввести два понятия: маршрутизаторы и потребители. Маршрутизатор — это объект, который пересылает некоторые данные и метаданные другим объектам. В большинстве случаев маршрутизатор является мета-оценщик,
т.е. оценщик, который принимает другой оценщик в качестве параметра. Функция, такая как sklearn.model_selection.cross_validate который принимает оценщик как параметр и передает данные и метаданные, также является маршрутизатором.
Потребитель, с другой стороны, это объект, который принимает и использует некоторые заданные
метаданные. Например, оценщик, учитывающий sample_weight в
его fit метод является потребителем sample_weight.
Объект может быть одновременно маршрутизатором и потребителем. Например,
мета-оценщик может учитывать sample_weight в некоторых вычислениях, но также может направлять его к базовому оценщику.
Сначала несколько импортов и некоторые случайные данные для остальной части скрипта.
# Authors: The scikit-learn developers
# SPDX-License-Identifier: BSD-3-Clause
import warnings
from pprint import pprint
import numpy as np
from sklearn import set_config
from sklearn.base import (
BaseEstimator,
ClassifierMixin,
MetaEstimatorMixin,
RegressorMixin,
TransformerMixin,
clone,
)
from sklearn.linear_model import LinearRegression
from sklearn.utils import metadata_routing
from sklearn.utils.metadata_routing import (
MetadataRouter,
MethodMapping,
get_routing_for_object,
process_routing,
)
from sklearn.utils.validation import check_is_fitted
n_samples, n_features = 100, 4
rng = np.random.RandomState(42)
X = rng.rand(n_samples, n_features)
y = rng.randint(0, 2, size=n_samples)
my_groups = rng.randint(0, 10, size=n_samples)
my_weights = rng.rand(n_samples)
my_other_weights = rng.rand(n_samples)
Маршрутизация метаданных доступна только при явном включении:
set_config(enable_metadata_routing=True)
Эта служебная функция является заглушкой для проверки передачи метаданных:
def check_metadata(obj, **kwargs):
for key, value in kwargs.items():
if value is not None:
print(
f"Received {key} of length = {len(value)} in {obj.__class__.__name__}."
)
else:
print(f"{key} is None in {obj.__class__.__name__}.")
Вспомогательная функция для красивого вывода информации о маршрутизации объекта:
def print_routing(obj):
pprint(obj.get_metadata_routing()._serialize())
Потребляющий оценщик#
Здесь мы демонстрируем, как оцениватель может предоставить необходимый API для поддержки маршрутизации метаданных в качестве потребителя. Представьте простой классификатор, принимающий
sample_weight как метаданные на его fit и groups в его
predict method:
class ExampleClassifier(ClassifierMixin, BaseEstimator):
def fit(self, X, y, sample_weight=None):
check_metadata(self, sample_weight=sample_weight)
# all classifiers need to expose a classes_ attribute once they're fit.
self.classes_ = np.array([0, 1])
return self
def predict(self, X, groups=None):
check_metadata(self, groups=groups)
# return a constant value of 1, not a very smart classifier!
return np.ones(len(X))
Теперь у вышеуказанного оценщика есть всё необходимое для потребления метаданных. Это достигается с помощью некоторой магии, выполненной в BaseEstimator. Теперь
существует три метода, предоставляемых вышеуказанным классом: set_fit_request,
set_predict_request, и get_metadata_routing. Также существует
set_score_request для sample_weight которая присутствует с
ClassifierMixin реализует score метод, принимающий
sample_weight. То же самое относится к регрессорам, которые наследуются от
RegressorMixin.
По умолчанию метаданные не запрашиваются, что мы можем видеть как:
print_routing(ExampleClassifier())
{'fit': {'sample_weight': None},
'predict': {'groups': None},
'score': {'sample_weight': None}}
Вышеуказанный вывод означает, что sample_weight и groups не запрашиваются ExampleClassifier, и если маршрутизатору переданы эти метаданные, он должен вызвать ошибку, поскольку пользователь явно не указал, являются ли они обязательными или нет. То же самое верно для sample_weight в score
, который наследуется от ClassifierMixin. Чтобы явно установить значения запроса для этих метаданных, мы можем использовать эти методы:
est = (
ExampleClassifier()
.set_fit_request(sample_weight=False)
.set_predict_request(groups=True)
.set_score_request(sample_weight=False)
)
print_routing(est)
{'fit': {'sample_weight': False},
'predict': {'groups': True},
'score': {'sample_weight': False}}
Примечание
Обратите внимание, что пока указанный выше оценщик не используется в мета-оценщике, пользователю не нужно устанавливать запросы для метаданных, а установленные значения игнорируются, поскольку потребитель не проверяет и не направляет предоставленные метаданные. Простое использование указанного выше оценщика будет работать как ожидалось.
est = ExampleClassifier()
est.fit(X, y, sample_weight=my_weights)
est.predict(X[:3, :], groups=my_groups)
Received sample_weight of length = 100 in ExampleClassifier.
Received groups of length = 100 in ExampleClassifier.
array([1., 1., 1.])
Маршрутизирующий мета-оцениватель#
Теперь мы покажем, как спроектировать мета-оцениватель в качестве маршрутизатора. В качестве упрощённого примера, вот мета-оцениватель, который не делает ничего, кроме маршрутизации метаданных.
class MetaClassifier(MetaEstimatorMixin, ClassifierMixin, BaseEstimator):
def __init__(self, estimator):
self.estimator = estimator
def get_metadata_routing(self):
# This method defines the routing for this meta-estimator.
# In order to do so, a `MetadataRouter` instance is created, and the
# routing is added to it. More explanations follow below.
router = MetadataRouter(owner=self).add(
estimator=self.estimator,
method_mapping=MethodMapping()
.add(caller="fit", callee="fit")
.add(caller="predict", callee="predict")
.add(caller="score", callee="score"),
)
return router
def fit(self, X, y, **fit_params):
# `get_routing_for_object` returns a copy of the `MetadataRouter`
# constructed by the above `get_metadata_routing` method, that is
# internally called.
request_router = get_routing_for_object(self)
# Meta-estimators are responsible for validating the given metadata.
# `method` refers to the parent's method, i.e. `fit` in this example.
request_router.validate_metadata(params=fit_params, method="fit")
# `MetadataRouter.route_params` maps the given metadata to the metadata
# required by the underlying estimator based on the routing information
# defined by the MetadataRouter. The output of type `Bunch` has a key
# for each consuming object and those hold keys for their consuming
# methods, which then contain key for the metadata which should be
# routed to them.
routed_params = request_router.route_params(params=fit_params, caller="fit")
# A sub-estimator is fitted and its classes are attributed to the
# meta-estimator.
self.estimator_ = clone(self.estimator).fit(X, y, **routed_params.estimator.fit)
self.classes_ = self.estimator_.classes_
return self
def predict(self, X, **predict_params):
check_is_fitted(self)
# As in `fit`, we get a copy of the object's MetadataRouter,
request_router = get_routing_for_object(self)
# then we validate the given metadata,
request_router.validate_metadata(params=predict_params, method="predict")
# and then prepare the input to the underlying `predict` method.
routed_params = request_router.route_params(
params=predict_params, caller="predict"
)
return self.estimator_.predict(X, **routed_params.estimator.predict)
Давайте разберем различные части приведенного выше кода.
Во-первых, get_routing_for_object берет наш мета-оценщик (self) и возвращает
MetadataRouter или
MetadataRequest если объект является потребителем, на основе вывода оценщика get_metadata_routing метод.
Затем в каждом методе мы используем route_params метод для построения словаря вида {"object_name": {"method_name": {"metadata":
value}}} для передачи в метод базового оценщика. Параметр object_name
(estimator в вышеуказанном routed_params.estimator.fit пример) такой же,
как добавленный в get_metadata_routing. validate_metadata
гарантирует, что все указанные метаданные запрашиваются, чтобы избежать скрытых ошибок.
Далее мы проиллюстрируем различные поведения и, в частности, типы ошибок, которые возникают.
meta_est = MetaClassifier(
estimator=ExampleClassifier().set_fit_request(sample_weight=True)
)
meta_est.fit(X, y, sample_weight=my_weights)
Received sample_weight of length = 100 in ExampleClassifier.
Обратите внимание, что приведённый выше пример вызывает нашу служебную функцию
check_metadata() через ExampleClassifier. Он проверяет, что
sample_weight корректно передается ему. Если это не так, как в следующем примере, он напечатает, что sample_weight является None:
meta_est.fit(X, y)
sample_weight is None in ExampleClassifier.
Если мы передаем неизвестные метаданные, возникает ошибка:
try:
meta_est.fit(X, y, test=my_weights)
except TypeError as e:
print(e)
MetaClassifier.fit got unexpected argument(s) {'test'}, which are not routed to any object.
И если мы передаем метаданные, которые не запрашивались явно:
try:
meta_est.fit(X, y, sample_weight=my_weights).predict(X, groups=my_groups)
except ValueError as e:
print(e)
Received sample_weight of length = 100 in ExampleClassifier.
[groups] are passed but are not explicitly set as requested or not requested for ExampleClassifier.predict, which is used within MetaClassifier.predict. Call `ExampleClassifier.set_predict_request({metadata}=True/False)` for each metadata you want to request/ignore. See the Metadata Routing User guide for more information.
Также, если мы явно установим его как не запрошенный, но он предоставлен:
meta_est = MetaClassifier(
estimator=ExampleClassifier()
.set_fit_request(sample_weight=True)
.set_predict_request(groups=False)
)
try:
meta_est.fit(X, y, sample_weight=my_weights).predict(X[:3, :], groups=my_groups)
except TypeError as e:
print(e)
Received sample_weight of length = 100 in ExampleClassifier.
MetaClassifier.predict got unexpected argument(s) {'groups'}, which are not routed to any object.
Ещё одно понятие для введения — алиасированные метаданные. Это происходит, когда оценщик запрашивает метаданные с другим именем переменной, чем имя по умолчанию. Например, в настройке, где есть два оценщика в конвейере, один может запросить sample_weight1 и другой
sample_weight2. Обратите внимание, что это не меняет то, что ожидает оценщик, это только сообщает мета-оценщику, как сопоставить предоставленные метаданные с требуемыми. Вот пример, где мы передаем aliased_sample_weight в
мета-оценщик, но мета-оценщик понимает, что
aliased_sample_weight является псевдонимом для sample_weight, и передаёт его как
sample_weight к базовому оценщику:
meta_est = MetaClassifier(
estimator=ExampleClassifier().set_fit_request(sample_weight="aliased_sample_weight")
)
meta_est.fit(X, y, aliased_sample_weight=my_weights)
Received sample_weight of length = 100 in ExampleClassifier.
Передача sample_weight здесь не сработает, так как запрашивается с псевдонимом и sample_weight с таким именем не запрашивается:
try:
meta_est.fit(X, y, sample_weight=my_weights)
except TypeError as e:
print(e)
MetaClassifier.fit got unexpected argument(s) {'sample_weight'}, which are not routed to any object.
Это приводит нас к get_metadata_routing. Способ работы маршрутизации в scikit-learn заключается в том, что потребители запрашивают то, что им нужно, а маршрутизаторы передают это дальше. Кроме того, маршрутизатор раскрывает то, что ему требуется самому, чтобы его можно было использовать внутри другого маршрутизатора, например, конвейер внутри объекта поиска по сетке.
Выход get_metadata_routing который является словарным представлением MetadataRouterвключает полное дерево запрошенных метаданных всеми вложенными объектами и их соответствующими маршрутами методов, т.е. какой метод под-оценщика используется в каком методе мета-оценщика:
print_routing(meta_est)
{'estimator': {'mapping': [{'callee': 'fit', 'caller': 'fit'},
{'callee': 'predict', 'caller': 'predict'},
{'callee': 'score', 'caller': 'score'}],
'router': {'fit': {'sample_weight': 'aliased_sample_weight'},
'predict': {'groups': None},
'score': {'sample_weight': None}}}}
Как видите, единственные метаданные, запрошенные для метода fit является
"sample_weight" с "aliased_sample_weight" как псевдоним.
~utils.metadata_routing.MetadataRouter класс позволяет нам легко создавать
объект маршрутизации, который создаст вывод, необходимый для нашего
get_metadata_routing.
Чтобы понять, как работают псевдонимы в мета-оценщиках, представьте наш мета-оценщик внутри другого:
meta_meta_est = MetaClassifier(estimator=meta_est).fit(
X, y, aliased_sample_weight=my_weights
)
Received sample_weight of length = 100 in ExampleClassifier.
В приведенном выше примере вот как fit метод meta_meta_est
будет вызывать подоценщик своего fit методы:
# user feeds `my_weights` as `aliased_sample_weight` into `meta_meta_est`:
meta_meta_est.fit(X, y, aliased_sample_weight=my_weights):
...
# the first sub-estimator (`meta_est`) expects `aliased_sample_weight`
self.estimator_.fit(X, y, aliased_sample_weight=aliased_sample_weight):
...
# the second sub-estimator (`est`) expects `sample_weight`
self.estimator_.fit(X, y, sample_weight=aliased_sample_weight):
...
Использование и маршрутизация Мета-Оценщика#
Для немного более сложного примера рассмотрим мета-оценщик, который направляет метаданные к базовому оценщику, как и раньше, но также использует некоторые метаданные в своих собственных методах. Этот мета-оценщик является одновременно потребителем и маршрутизатором. Реализация такого оценщика очень похожа на то, что было раньше, но с несколькими изменениями.
class RouterConsumerClassifier(MetaEstimatorMixin, ClassifierMixin, BaseEstimator):
def __init__(self, estimator):
self.estimator = estimator
def get_metadata_routing(self):
router = (
MetadataRouter(owner=self)
# defining metadata routing request values for usage in the meta-estimator
.add_self_request(self)
# defining metadata routing request values for usage in the sub-estimator
.add(
estimator=self.estimator,
method_mapping=MethodMapping()
.add(caller="fit", callee="fit")
.add(caller="predict", callee="predict")
.add(caller="score", callee="score"),
)
)
return router
# Since `sample_weight` is used and consumed here, it should be defined as
# an explicit argument in the method's signature. All other metadata which
# are only routed, will be passed as `**fit_params`:
def fit(self, X, y, sample_weight, **fit_params):
if self.estimator is None:
raise ValueError("estimator cannot be None!")
check_metadata(self, sample_weight=sample_weight)
# We add `sample_weight` to the `fit_params` dictionary.
if sample_weight is not None:
fit_params["sample_weight"] = sample_weight
request_router = get_routing_for_object(self)
request_router.validate_metadata(params=fit_params, method="fit")
routed_params = request_router.route_params(params=fit_params, caller="fit")
self.estimator_ = clone(self.estimator).fit(X, y, **routed_params.estimator.fit)
self.classes_ = self.estimator_.classes_
return self
def predict(self, X, **predict_params):
check_is_fitted(self)
# As in `fit`, we get a copy of the object's MetadataRouter,
request_router = get_routing_for_object(self)
# we validate the given metadata,
request_router.validate_metadata(params=predict_params, method="predict")
# and then prepare the input to the underlying ``predict`` method.
routed_params = request_router.route_params(
params=predict_params, caller="predict"
)
return self.estimator_.predict(X, **routed_params.estimator.predict)
Ключевые части, в которых данный мета-оценщик отличается от нашего предыдущего
мета-оценщика, заключаются в принятии sample_weight явно в fit и включая его в fit_params. Поскольку sample_weight является явным аргументом, мы можем быть уверены, что set_fit_request(sample_weight=...) присутствует
для этого метода. Мета-оценщик является как потребителем, так и
маршрутизатором sample_weight.
В get_metadata_routing, мы добавляем self к маршрутизации с использованием
add_self_request чтобы указать, что этот оценщик потребляет
sample_weight а также является маршрутизатором; что также добавляет
$self_request ключ к информации о маршрутизации, как показано ниже. Теперь рассмотрим несколько примеров:
Метаданные не запрашиваются
meta_est = RouterConsumerClassifier(estimator=ExampleClassifier())
print_routing(meta_est)
{'$self_request': {'fit': {'sample_weight': None},
'score': {'sample_weight': None}},
'estimator': {'mapping': [{'callee': 'fit', 'caller': 'fit'},
{'callee': 'predict', 'caller': 'predict'},
{'callee': 'score', 'caller': 'score'}],
'router': {'fit': {'sample_weight': None},
'predict': {'groups': None},
'score': {'sample_weight': None}}}}
sample_weightзапрошенный суб-оценщиком
meta_est = RouterConsumerClassifier(
estimator=ExampleClassifier().set_fit_request(sample_weight=True)
)
print_routing(meta_est)
{'$self_request': {'fit': {'sample_weight': None},
'score': {'sample_weight': None}},
'estimator': {'mapping': [{'callee': 'fit', 'caller': 'fit'},
{'callee': 'predict', 'caller': 'predict'},
{'callee': 'score', 'caller': 'score'}],
'router': {'fit': {'sample_weight': True},
'predict': {'groups': None},
'score': {'sample_weight': None}}}}
sample_weightзапрошенный мета-оценщиком
meta_est = RouterConsumerClassifier(estimator=ExampleClassifier()).set_fit_request(
sample_weight=True
)
print_routing(meta_est)
{'$self_request': {'fit': {'sample_weight': True},
'score': {'sample_weight': None}},
'estimator': {'mapping': [{'callee': 'fit', 'caller': 'fit'},
{'callee': 'predict', 'caller': 'predict'},
{'callee': 'score', 'caller': 'score'}],
'router': {'fit': {'sample_weight': None},
'predict': {'groups': None},
'score': {'sample_weight': None}}}}
Обратите внимание на разницу в запрошенных представлениях метаданных выше.
Мы также можем использовать псевдонимы метаданных для передачи различных значений методам fit мета- и суб-оценщиков:
meta_est = RouterConsumerClassifier(
estimator=ExampleClassifier().set_fit_request(sample_weight="clf_sample_weight"),
).set_fit_request(sample_weight="meta_clf_sample_weight")
print_routing(meta_est)
{'$self_request': {'fit': {'sample_weight': 'meta_clf_sample_weight'},
'score': {'sample_weight': None}},
'estimator': {'mapping': [{'callee': 'fit', 'caller': 'fit'},
{'callee': 'predict', 'caller': 'predict'},
{'callee': 'score', 'caller': 'score'}],
'router': {'fit': {'sample_weight': 'clf_sample_weight'},
'predict': {'groups': None},
'score': {'sample_weight': None}}}}
Однако, fit мета-оценщика требуется только псевдоним для подоценщика и обрабатывает собственные веса выборок как sample_weight, поскольку он не проверяет и не направляет свои собственные необходимые метаданные:
meta_est.fit(X, y, sample_weight=my_weights, clf_sample_weight=my_other_weights)
Received sample_weight of length = 100 in RouterConsumerClassifier.
Received sample_weight of length = 100 in ExampleClassifier.
Псевдоним только для суб-оценщика:
Это полезно, когда мы не хотим, чтобы мета-оценщик использовал метаданные, но подоценщик должен.
meta_est = RouterConsumerClassifier(
estimator=ExampleClassifier().set_fit_request(sample_weight="aliased_sample_weight")
)
print_routing(meta_est)
{'$self_request': {'fit': {'sample_weight': None},
'score': {'sample_weight': None}},
'estimator': {'mapping': [{'callee': 'fit', 'caller': 'fit'},
{'callee': 'predict', 'caller': 'predict'},
{'callee': 'score', 'caller': 'score'}],
'router': {'fit': {'sample_weight': 'aliased_sample_weight'},
'predict': {'groups': None},
'score': {'sample_weight': None}}}}
Мета-оценщик не может использовать aliased_sample_weight, потому что ожидает, что он передан как sample_weight. Это применится даже если
set_fit_request(sample_weight=True) был установлен на нем.
Простой конвейер#
Немного более сложный случай использования — мета-оцениватель, напоминающий
Pipeline. Вот мета-оценщик, который принимает преобразователь и классификатор. При вызове его fit метод, он применяет
преобразователь fit и transform перед запуском классификатора на преобразованных данных. При predict, он применяет transform
перед прогнозированием с помощью классификатора predict метод на преобразованных новых данных.
class SimplePipeline(ClassifierMixin, BaseEstimator):
def __init__(self, transformer, classifier):
self.transformer = transformer
self.classifier = classifier
def get_metadata_routing(self):
router = (
MetadataRouter(owner=self)
# We add the routing for the transformer.
.add(
transformer=self.transformer,
method_mapping=MethodMapping()
# The metadata is routed such that it retraces how
# `SimplePipeline` internally calls the transformer's `fit` and
# `transform` methods in its own methods (`fit` and `predict`).
.add(caller="fit", callee="fit")
.add(caller="fit", callee="transform")
.add(caller="predict", callee="transform"),
)
# We add the routing for the classifier.
.add(
classifier=self.classifier,
method_mapping=MethodMapping()
.add(caller="fit", callee="fit")
.add(caller="predict", callee="predict"),
)
)
return router
def fit(self, X, y, **fit_params):
routed_params = process_routing(self, "fit", **fit_params)
self.transformer_ = clone(self.transformer).fit(
X, y, **routed_params.transformer.fit
)
X_transformed = self.transformer_.transform(
X, **routed_params.transformer.transform
)
self.classifier_ = clone(self.classifier).fit(
X_transformed, y, **routed_params.classifier.fit
)
return self
def predict(self, X, **predict_params):
routed_params = process_routing(self, "predict", **predict_params)
X_transformed = self.transformer_.transform(
X, **routed_params.transformer.transform
)
return self.classifier_.predict(
X_transformed, **routed_params.classifier.predict
)
Обратите внимание на использование MethodMapping для
объявления, какие методы дочернего оценщика (вызываемого) используются в каких
методах мета-оценщика (вызывающего). Как видите, SimplePipeline использует transformer's transform и fit методы в fit, и его
transform метод в predict, и это то, что вы видите реализованным в
структуре маршрутизации класса pipeline.
Ещё одно отличие в приведённом выше примере от предыдущих – использование process_routing, который обрабатывает входные
параметры, выполняет необходимую проверку и возвращает routed_params
который мы создали в предыдущих примерах. Это сокращает шаблонный код,
который разработчик должен писать в каждом методе мета-оценщика. Разработчикам
настоятельно рекомендуется использовать эту функцию, если нет веской причины
против этого.
Чтобы протестировать вышеуказанный pipeline, добавим пример трансформера.
class ExampleTransformer(TransformerMixin, BaseEstimator):
def fit(self, X, y, sample_weight=None):
check_metadata(self, sample_weight=sample_weight)
return self
def transform(self, X, groups=None):
check_metadata(self, groups=groups)
return X
def fit_transform(self, X, y, sample_weight=None, groups=None):
return self.fit(X, y, sample_weight).transform(X, groups)
Обратите внимание, что в приведенном выше примере мы реализовали fit_transform который
вызывает fit и transform с соответствующими метаданными. Это требуется только
если transform принимает метаданные, поскольку по умолчанию fit_transform
реализация в TransformerMixin не передает метаданные в
transform.
Теперь мы можем протестировать наш пайплайн и проверить, правильно ли передаются метаданные. Этот пример использует наш SimplePipeline, наш ExampleTransformer, и наш
RouterConsumerClassifier который использует наш ExampleClassifier.
pipe = SimplePipeline(
transformer=ExampleTransformer()
# we set transformer's fit to receive sample_weight
.set_fit_request(sample_weight=True)
# we set transformer's transform to receive groups
.set_transform_request(groups=True),
classifier=RouterConsumerClassifier(
estimator=ExampleClassifier()
# we want this sub-estimator to receive sample_weight in fit
.set_fit_request(sample_weight=True)
# but not groups in predict
.set_predict_request(groups=False),
)
# and we want the meta-estimator to receive sample_weight as well
.set_fit_request(sample_weight=True),
)
pipe.fit(X, y, sample_weight=my_weights, groups=my_groups).predict(
X[:3], groups=my_groups
)
Received sample_weight of length = 100 in ExampleTransformer.
Received groups of length = 100 in ExampleTransformer.
Received sample_weight of length = 100 in RouterConsumerClassifier.
Received sample_weight of length = 100 in ExampleClassifier.
Received groups of length = 100 in ExampleTransformer.
groups is None in ExampleClassifier.
array([1., 1., 1.])
Устаревание / Изменение значения по умолчанию#
В этом разделе мы показываем, как следует обрабатывать случай, когда маршрутизатор также становится потребителем, особенно когда он потребляет те же метаданные, что и его под-оценщик, или потребитель начинает потреблять метаданные, которые он не потреблял в более старой версии. В этом случае должно выдаваться предупреждение в течение некоторого времени, чтобы пользователи знали, что поведение изменилось по сравнению с предыдущими версиями.
class MetaRegressor(MetaEstimatorMixin, RegressorMixin, BaseEstimator):
def __init__(self, estimator):
self.estimator = estimator
def fit(self, X, y, **fit_params):
routed_params = process_routing(self, "fit", **fit_params)
self.estimator_ = clone(self.estimator).fit(X, y, **routed_params.estimator.fit)
def get_metadata_routing(self):
router = MetadataRouter(owner=self).add(
estimator=self.estimator,
method_mapping=MethodMapping().add(caller="fit", callee="fit"),
)
return router
Как объяснено выше, это допустимое использование, если my_weights не должны передаваться как sample_weight to MetaRegressor:
reg = MetaRegressor(estimator=LinearRegression().set_fit_request(sample_weight=True))
reg.fit(X, y, sample_weight=my_weights)
Теперь представьте, что мы дальше развиваем MetaRegressor и теперь также потребляет
sample_weight:
class WeightedMetaRegressor(MetaEstimatorMixin, RegressorMixin, BaseEstimator):
# show warning to remind user to explicitly set the value with
# `.set_{method}_request(sample_weight={boolean})`
__metadata_request__fit = {"sample_weight": metadata_routing.WARN}
def __init__(self, estimator):
self.estimator = estimator
def fit(self, X, y, sample_weight=None, **fit_params):
routed_params = process_routing(
self, "fit", sample_weight=sample_weight, **fit_params
)
check_metadata(self, sample_weight=sample_weight)
self.estimator_ = clone(self.estimator).fit(X, y, **routed_params.estimator.fit)
def get_metadata_routing(self):
router = (
MetadataRouter(owner=self)
.add_self_request(self)
.add(
estimator=self.estimator,
method_mapping=MethodMapping().add(caller="fit", callee="fit"),
)
)
return router
Вышеуказанная реализация почти такая же, как MetaRegressor, и
из-за значения запроса по умолчанию, определенного в __metadata_request__fit
при обучении выдается предупреждение.
with warnings.catch_warnings(record=True) as record:
WeightedMetaRegressor(
estimator=LinearRegression().set_fit_request(sample_weight=False)
).fit(X, y, sample_weight=my_weights)
for w in record:
print(w.message)
Received sample_weight of length = 100 in WeightedMetaRegressor.
Support for sample_weight has recently been added to WeightedMetaRegressor(estimator=LinearRegression()) class. To maintain backward compatibility, it is ignored now. Using `set_fit_request(sample_weight={True, False})` on this method of the class, you can set the request value to False to silence this warning, or to True to consume and use the metadata.
Когда оценщик потребляет метаданные, которые он раньше не потреблял, можно использовать следующий шаблон, чтобы предупредить пользователей об этом.
class ExampleRegressor(RegressorMixin, BaseEstimator):
__metadata_request__fit = {"sample_weight": metadata_routing.WARN}
def fit(self, X, y, sample_weight=None):
check_metadata(self, sample_weight=sample_weight)
return self
def predict(self, X):
return np.zeros(shape=(len(X)))
with warnings.catch_warnings(record=True) as record:
MetaRegressor(estimator=ExampleRegressor()).fit(X, y, sample_weight=my_weights)
for w in record:
print(w.message)
sample_weight is None in ExampleRegressor.
Support for sample_weight has recently been added to ExampleRegressor() class. To maintain backward compatibility, it is ignored now. Using `set_fit_request(sample_weight={True, False})` on this method of the class, you can set the request value to False to silence this warning, or to True to consume and use the metadata.
В конце мы отключаем флаг конфигурации для маршрутизации метаданных:
set_config(enable_metadata_routing=False)
Разработка сторонних разработчиков и зависимость scikit-learn#
Как показано выше, информация передаётся между классами с помощью
MetadataRequest и
MetadataRouterМетоды ядра для проецирования данных в альтернативные размерные пространства
ваш оценщик наследуется от
BaseEstimatorпараметры, используемые методами вашего оценщика, например.
fit, явно определены в сигнатуре метода, в отличие от*argsили*kwargs.ваш оценщик не передает никакие метаданные базовым объектам, т.е. это не маршрутизатор.
Общее время выполнения скрипта: (0 минут 0.050 секунд)
Связанные примеры
Обучение Elastic Net с предвычисленной матрицей Грама и взвешенными выборками