1. 元數據路由#

注意

元數據路由 API 仍在實驗階段,尚未在所有估計器中實作。請參考支援和不支援的模型列表以取得更多資訊。它可能會在沒有通常的棄用週期下變更。預設情況下,此功能未啟用。您可以將 enable_metadata_routing 標誌設定為 True 來啟用它

>>> import sklearn
>>> sklearn.set_config(enable_metadata_routing=True)

請注意,本文件中介紹的方法和要求僅在您想將元數據(例如 sample_weight)傳遞給方法時才相關。如果您只傳遞 Xy,並且沒有將其他參數/元數據傳遞給諸如 fittransform 等方法,那麼您無需設定任何內容。

本指南示範了如何在 scikit-learn 中的物件之間路由和傳遞元數據。如果您正在開發與 scikit-learn 相容的估計器或元估計器,您可以查看我們相關的開發人員指南:元數據路由

元數據是估計器、評分器或 CV 分割器在使用者明確將其作為參數傳遞時會考慮的數據。例如,KMeans 接受其 fit() 方法中的 sample_weight,並將其用於計算其質心。classes 由某些分類器使用,而 groups 在某些分割器中使用,但除了 X 和 y 之外,傳遞到物件方法中的任何數據都可以視為元數據。在 scikit-learn 1.3 版之前,如果這些物件與其他物件一起使用,例如在 GridSearchCV 中接受 sample_weight 的評分器,則沒有單一 API 可以傳遞類似的元數據。

使用元數據路由 API,我們可以通過元估計器(例如 PipelineGridSearchCV)或諸如 cross_validate 之類的函數,將元數據傳輸到估計器、評分器和 CV 分割器,這些函數會將數據路由到其他物件。為了將元數據傳遞給諸如 fitscore 之類的方法,使用元數據的物件必須請求它。這是通過 set_{method}_request() 方法完成的,其中 {method} 由請求元數據的方法名稱取代。例如,在其 fit() 方法中使用元數據的估計器將使用 set_fit_request(),而評分器將使用 set_score_request()。這些方法允許我們指定要請求的元數據,例如 set_fit_request(sample_weight=True)

對於諸如 GroupKFold 之類的分組分割器,預設會請求 groups 參數。這最好通過以下範例來說明。

1.1. 使用範例#

在這裡,我們提供一些範例來展示一些常見的使用案例。我們的目標是通過 cross_validate 傳遞 sample_weightgroups,該函數將元數據路由到 LogisticRegressionCV 和使用 make_scorer 製作的自訂評分器,兩者都可以在其方法中使用元數據。在這些範例中,我們希望個別設定是否在不同的使用者中使用元數據。

本節中的範例需要以下匯入和數據

>>> import numpy as np
>>> from sklearn.metrics import make_scorer, accuracy_score
>>> from sklearn.linear_model import LogisticRegressionCV, LogisticRegression
>>> from sklearn.model_selection import cross_validate, GridSearchCV, GroupKFold
>>> from sklearn.feature_selection import SelectKBest
>>> from sklearn.pipeline import make_pipeline
>>> 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)

1.1.1. 加權評分和擬合#

LogisticRegressionCV 內部使用的分割器 GroupKFold 預設會請求 groups。但是,我們需要通過在 LogisticRegressionCV`的 `set_fit_request() 方法和 make_scorer`的 `set_score_request 方法中指定 sample_weight=True 來明確請求它和我們的自訂評分器。兩個使用者都知道如何在他們的 fit()score() 方法中使用 sample_weight。然後,我們可以在 cross_validate 中傳遞元數據,它會將其路由到任何活動的使用者

>>> weighted_acc = make_scorer(accuracy_score).set_score_request(sample_weight=True)
>>> lr = LogisticRegressionCV(
...     cv=GroupKFold(),
...     scoring=weighted_acc
... ).set_fit_request(sample_weight=True)
>>> cv_results = cross_validate(
...     lr,
...     X,
...     y,
...     params={"sample_weight": my_weights, "groups": my_groups},
...     cv=GroupKFold(),
...     scoring=weighted_acc,
... )

請注意,在本範例中,cross_validatemy_weights 路由到評分器和 LogisticRegressionCV

如果我們在 cross_validate 的參數中傳遞 sample_weight,但沒有設定任何物件來請求它,則會引發 UnsetMetadataPassedError,提示我們需要明確設定將其路由到何處。如果傳遞了 params={"sample_weights": my_weights, ...}(請注意拼寫錯誤,即 weights 而不是 weight),情況也是如此,因為沒有任何底層物件請求 sample_weights

1.1.2. 加權評分與非加權擬合#

當將諸如 sample_weight 之類的元數據傳遞到 路由器元估計器 或路由函式)時,所有 sample_weight 消費者 都需要明確請求權重或明確不請求權重(即 TrueFalse)。因此,要執行非加權擬合,我們需要將 LogisticRegressionCV 設定為不請求樣本權重,以便 cross_validate 不會傳遞權重。

>>> weighted_acc = make_scorer(accuracy_score).set_score_request(sample_weight=True)
>>> lr = LogisticRegressionCV(
...     cv=GroupKFold(), scoring=weighted_acc,
... ).set_fit_request(sample_weight=False)
>>> cv_results = cross_validate(
...     lr,
...     X,
...     y,
...     cv=GroupKFold(),
...     params={"sample_weight": my_weights, "groups": my_groups},
...     scoring=weighted_acc,
... )

如果沒有呼叫 linear_model.LogisticRegressionCV.set_fit_requestcross_validate 將會引發錯誤,因為傳遞了 sample_weight,但 LogisticRegressionCV 並未明確配置為識別權重。

1.1.3. 非加權特徵選擇#

只有當物件的方法知道如何使用元數據時,才能路由元數據,這在大多數情況下表示它們將元數據作為明確的參數。只有這樣,我們才能使用 set_fit_request(sample_weight=True) 等設定元數據的請求值。這會使物件成為 消費者

LogisticRegressionCV 不同,SelectKBest 無法消耗權重,因此其執行個體上沒有設定 sample_weight 的請求值,並且 sample_weight 不會路由到它。

>>> weighted_acc = make_scorer(accuracy_score).set_score_request(sample_weight=True)
>>> lr = LogisticRegressionCV(
...     cv=GroupKFold(), scoring=weighted_acc,
... ).set_fit_request(sample_weight=True)
>>> sel = SelectKBest(k=2)
>>> pipe = make_pipeline(sel, lr)
>>> cv_results = cross_validate(
...     pipe,
...     X,
...     y,
...     cv=GroupKFold(),
...     params={"sample_weight": my_weights, "groups": my_groups},
...     scoring=weighted_acc,
... )

1.1.4. 不同的評分和擬合權重#

儘管 make_scorerLogisticRegressionCV 都期望使用鍵 sample_weight,但我們可以使用別名將不同的權重傳遞給不同的消費者。在此範例中,我們將 scoring_weight 傳遞給評分器,並將 fitting_weight 傳遞給 LogisticRegressionCV

>>> weighted_acc = make_scorer(accuracy_score).set_score_request(
...    sample_weight="scoring_weight"
... )
>>> lr = LogisticRegressionCV(
...     cv=GroupKFold(), scoring=weighted_acc,
... ).set_fit_request(sample_weight="fitting_weight")
>>> cv_results = cross_validate(
...     lr,
...     X,
...     y,
...     cv=GroupKFold(),
...     params={
...         "scoring_weight": my_weights,
...         "fitting_weight": my_other_weights,
...         "groups": my_groups,
...     },
...     scoring=weighted_acc,
... )

1.2. API 介面#

消費者 是一個物件(估計器、元估計器、評分器、分割器),它在其至少一個方法中接受並使用一些 元數據(例如 fitpredictinverse_transformtransformscoresplit)。僅將元數據轉發到其他物件(子估計器、評分器或分割器)而不使用元數據本身的元估計器不是消費者。將元數據路由到其他物件的(元)估計器是 路由器。(元)估計器可以同時是 消費者路由器。(元)估計器和分割器會為每個至少接受一個元數據的方法公開一個 set_{method}_request 方法。例如,如果估計器在 fitscore 中支援 sample_weight,則它會公開 estimator.set_fit_request(sample_weight=value)estimator.set_score_request(sample_weight=value)。這裡 value 可以是

  • True:方法請求 sample_weight。這表示如果提供了元數據,將會使用它,否則不會引發錯誤。

  • False:方法不請求 sample_weight

  • None:如果傳遞了 sample_weight,路由器將會引發錯誤。在大多數情況下,這是物件實例化時的預設值,並確保使用者在傳遞元數據時明確設定元數據請求。唯一的例外是 Group*Fold 分割器。

  • "param_name":如果我們想將不同的權重傳遞給不同的消費者,則為 sample_weight 的別名。如果使用了別名,則元估計器不應將 "param_name" 轉發給消費者,而應轉發 sample_weight,因為消費者會預期有一個名為 sample_weight 的參數。這表示物件要求的元數據(例如 sample_weight)與使用者提供的變數名稱(例如 my_weights)之間的映射是在路由器層級完成的,而不是由消耗物件本身完成的。

使用 set_score_request 以相同的方式請求評分器的元數據。

如果使用者傳遞了元數據(例如 sample_weight),則應由使用者設定所有可能消耗 sample_weight 的物件的元數據請求,否則路由器物件會引發錯誤。例如,以下程式碼會引發錯誤,因為尚未明確指定是否應將 sample_weight 傳遞給估計器的評分器。

>>> param_grid = {"C": [0.1, 1]}
>>> lr = LogisticRegression().set_fit_request(sample_weight=True)
>>> try:
...     GridSearchCV(
...         estimator=lr, param_grid=param_grid
...     ).fit(X, y, sample_weight=my_weights)
... except ValueError as e:
...     print(e)
[sample_weight] are passed but are not explicitly set as requested or not
requested for LogisticRegression.score, which is used within GridSearchCV.fit.
Call `LogisticRegression.set_score_request({metadata}=True/False)` for each metadata
you want to request/ignore.

可以透過明確設定請求值來修正此問題。

>>> lr = LogisticRegression().set_fit_request(
...     sample_weight=True
... ).set_score_request(sample_weight=False)

在 **用法範例** 章節的末尾,我們會停用元數據路由的組態旗標。

>>> sklearn.set_config(enable_metadata_routing=False)

1.3. 元數據路由支援狀態#

所有消費者(即僅消耗元數據而不路由它們的簡單估計器)都支援元數據路由,這表示它們可以在支援元數據路由的元估計器中使用。然而,正在進行對元估計器支援元數據路由的開發,以下是支援和尚不支援元數據路由的元估計器和工具的清單。

支援元數據路由的元估計器和函式

不支援元數據路由的中繼估算器與工具