Примечание
Перейти в конец, чтобы скачать полный код примера или запустить этот пример в браузере с помощью JupyterLite или Binder.
Визуализация вероятностных предсказаний VotingClassifier
Построение графика предсказанных вероятностей классов на игрушечном наборе данных: их предсказывают три разных классификатора, а затем усредняет VotingClassifier.
Сначала инициализируются три линейных классификатора. Два из них — сплайновые модели с членами взаимодействия: одна использует постоянную экстраполяцию, другая — периодическую. Третий классификатор — это Nystroem с ядром «rbf» по умолчанию.
В первой части примера эти три классификатора используются для демонстрации мягкого голосования с помощью VotingClassifier с взвешенным средним. Задаём weights=[2, 1, 3]: предсказания сплайновой модели с постоянной экстраполяцией получают вдвое больший вес, чем предсказания модели с периодической экстраполяцией, а предсказания модели Nystroem — втрое больший вес, чем предсказания периодического сплайна.
Во второй части показано, как мягкие предсказания можно преобразовать в жёсткие.
# Authors: The scikit-learn developers # SPDX-License-Identifier: BSD-3-Clause
Сначала мы создаём зашумлённый набор данных XOR, предназначенный для задачи бинарной классификации.
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from matplotlib.colors import ListedColormap
n_samples = 500
rng = np.random.default_rng(0)
feature_names = ["Feature #0", "Feature #1"]
common_scatter_plot_params = dict(
cmap=ListedColormap(["tab:red", "tab:blue"]),
edgecolor="white",
linewidth=1,
)
xor = pd.DataFrame(
np.random.RandomState(0).uniform(low=-1, high=1, size=(n_samples, 2)),
columns=feature_names,
)
noise = rng.normal(loc=0, scale=0.1, size=(n_samples, 2))
target_xor = np.logical_xor(
xor["Feature #0"] + noise[:, 0] > 0, xor["Feature #1"] + noise[:, 1] > 0
)
X = xor[feature_names]
y = target_xor.astype(np.int32)
fig, ax = plt.subplots()
ax.scatter(X["Feature #0"], X["Feature #1"], c=y, **common_scatter_plot_params)
ax.set_title("The XOR dataset")
plt.show()

Из-за присущей набору данных XOR нелинейной разделимости часто предпочтение отдаётся моделям на основе деревьев. Однако правильно подобранная инженерия признаков в сочетании с линейной моделью может дать эффективные результаты, а также обеспечить лучше откалиброванные вероятности для образцов, расположенных в переходных областях, на которые влияет шум.
Определим модели и обучим их на всём наборе данных.
from sklearn.ensemble import VotingClassifier
from sklearn.kernel_approximation import Nystroem
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import PolynomialFeatures, SplineTransformer, StandardScaler
clf1 = make_pipeline(
SplineTransformer(degree=2, n_knots=2),
PolynomialFeatures(interaction_only=True),
LogisticRegression(C=10),
)
clf2 = make_pipeline(
SplineTransformer(
degree=2,
n_knots=4,
extrapolation="periodic",
include_bias=True,
),
PolynomialFeatures(interaction_only=True),
LogisticRegression(C=10),
)
clf3 = make_pipeline(
StandardScaler(),
Nystroem(gamma=2, random_state=0),
LogisticRegression(C=10),
)
weights = [2, 1, 3]
eclf = VotingClassifier(
estimators=[
("constant splines model", clf1),
("periodic splines model", clf2),
("nystroem model", clf3),
],
voting="soft",
weights=weights,
)
clf1.fit(X, y)
clf2.fit(X, y)
clf3.fit(X, y)
eclf.fit(X, y)
VotingClassifier(estimators=[('constant splines model',
Pipeline(steps=[('splinetransformer',
SplineTransformer(degree=2,
n_knots=2)),
('polynomialfeatures',
PolynomialFeatures(interaction_only=True)),
('logisticregression',
LogisticRegression(C=10))])),
('periodic splines model',
Pipeline(steps=[('splinetransformer',
SplineTransformer(degree=2,
extrapolation='periodic',
n_knots=4)),
('polynomialfeatures',
PolynomialFeatures(interaction_only=True)),
('logisticregression',
LogisticRegression(C=10))])),
('nystroem model',
Pipeline(steps=[('standardscaler',
StandardScaler()),
('nystroem',
Nystroem(gamma=2,
random_state=0)),
('logisticregression',
LogisticRegression(C=10))]))],
voting='soft', weights=[2, 1, 3])
В среде Jupyter повторно выполните эту ячейку, чтобы отобразить HTML-представление, или доверьте этому блокноту. На GitHub HTML-представление отобразить не удаётся. Попробуйте открыть эту страницу с помощью nbviewer.org.
Параметры
Обученные атрибуты
Параметры
Обученные атрибуты
6 признаков
| Feature #0_sp_0 |
| Feature #0_sp_1 |
| Feature #0_sp_2 |
| Feature #1_sp_0 |
| Feature #1_sp_1 |
| Feature #1_sp_2 |
Параметры
Обученные атрибуты
| Имя | Тип | Значение |
|---|---|---|
| n_features_in_ n_features_in_: int Количество признаков, обнаруженных во время :term:`fit`. .. versionadded:: 0.24 | int | 6 |
| n_output_features_ n_output_features_: int Общее количество выходных полиномиальных признаков. Количество выходных признаков вычисляется путём перебора всех комбинаций входных признаков подходящего размера. | int | 22 |
| powers_ powers_: ndarray of shape (`n_output_features_`, `n_features_in_`) `powers_[i, j]` — показатель степени j-го входного признака в i-м выходном признаке. | ndarray[int64](22, 6) | [[0,0,0,0,0,0], [1,0,0,0,0,0], [0,1,0,0,0,0], ..., [0,0,0,1,1,0], [0,0,0,1,0,1], [0,0,0,0,1,1]] |
22 признака
| 1 |
| x0 |
| x1 |
| x2 |
| x3 |
| x4 |
| x5 |
| x0 x1 |
| x0 x2 |
| x0 x3 |
| x0 x4 |
| x0 x5 |
| x1 x2 |
| x1 x3 |
| x1 x4 |
| x1 x5 |
| x2 x3 |
| x2 x4 |
| x2 x5 |
| x3 x4 |
| x3 x5 |
| x4 x5 |
Параметры
Атрибуты обученной модели
Параметры
Атрибуты обученной модели
6 признаков
| Feature #0_sp_0 |
| Feature #0_sp_1 |
| Feature #0_sp_2 |
| Feature #1_sp_0 |
| Feature #1_sp_1 |
| Feature #1_sp_2 |
Параметры
Обученные атрибуты
| Имя | Тип | Значение |
|---|---|---|
| n_features_in_ n_features_in_: int Количество признаков, обнаруженных во время :term:`fit`. .. versionadded:: 0.24 | int | 6 |
| n_output_features_ n_output_features_: int Общее количество выходных полиномиальных признаков. Количество выходных признаков вычисляется путём перебора всех комбинаций входных признаков подходящего размера. | int | 22 |
| powers_ powers_: ndarray формы (`n_output_features_`, `n_features_in_`) `powers_[i, j]` — показатель степени j-го входного признака в i-м выходном признаке. | ndarray[int64](22, 6) | [[0,0,0,0,0,0], [1,0,0,0,0,0], [0,1,0,0,0,0], ..., [0,0,0,1,1,0], [0,0,0,1,0,1], [0,0,0,0,1,1]] |
22 признака
| 1 |
| x0 |
| x1 |
| x2 |
| x3 |
| x4 |
| x5 |
| x0 x1 |
| x0 x2 |
| x0 x3 |
| x0 x4 |
| x0 x5 |
| x1 x2 |
| x1 x3 |
| x1 x4 |
| x1 x5 |
| x2 x3 |
| x2 x4 |
| x2 x5 |
| x3 x4 |
| x3 x5 |
| x4 x5 |
Параметры
Обученные атрибуты
Параметры
Обученные атрибуты
2 признака
| Признак #0 |
| Признак #1 |
Параметры
Обученные атрибуты
100 признаков
| nystroem0 |
| nystroem1 |
| nystroem2 |
| nystroem3 |
| nystroem4 |
| nystroem5 |
| nystroem6 |
| nystroem7 |
| nystroem8 |
| nystroem9 |
| nystroem10 |
| nystroem11 |
| nystroem12 |
| nystroem13 |
| nystroem14 |
| nystroem15 |
| nystroem16 |
| nystroem17 |
| nystroem18 |
| nystroem19 |
| nystroem20 |
| nystroem21 |
| nystroem22 |
| nystroem23 |
| nystroem24 |
| nystroem25 |
| nystroem26 |
| nystroem27 |
| nystroem28 |
| nystroem29 |
| nystroem30 |
| nystroem31 |
| nystroem32 |
| nystroem33 |
| nystroem34 |
| nystroem35 |
| nystroem36 |
| nystroem37 |
| nystroem38 |
| nystroem39 |
| nystroem40 |
| nystroem41 |
| nystroem42 |
| nystroem43 |
| nystroem44 |
| nystroem45 |
| nystroem46 |
| nystroem47 |
| nystroem48 |
| nystroem49 |
| nystroem50 |
| nystroem51 |
| nystroem52 |
| nystroem53 |
| nystroem54 |
| nystroem55 |
| nystroem56 |
| nystroem57 |
| nystroem58 |
| nystroem59 |
| nystroem60 |
| nystroem61 |
| nystroem62 |
| nystroem63 |
| nystroem64 |
| nystroem65 |
| nystroem66 |
| nystroem67 |
| nystroem68 |
| nystroem69 |
| nystroem70 |
| nystroem71 |
| nystroem72 |
| nystroem73 |
| nystroem74 |
| nystroem75 |
| nystroem76 |
| nystroem77 |
| nystroem78 |
| nystroem79 |
| nystroem80 |
| nystroem81 |
| nystroem82 |
| nystroem83 |
| nystroem84 |
| nystroem85 |
| nystroem86 |
| nystroem87 |
| nystroem88 |
| nystroem89 |
| nystroem90 |
| nystroem91 |
| nystroem92 |
| nystroem93 |
| nystroem94 |
| nystroem95 |
| nystroem96 |
| nystroem97 |
| nystroem98 |
| nystroem99 |
Параметры
Обученные атрибуты
6 признаков
| votingclassifier_constant splines model0 |
| votingclassifier_constant splines model1 |
| votingclassifier_periodic splines model0 |
| votingclassifier_periodic splines model1 |
| votingclassifier_nystroem model0 |
| votingclassifier_nystroem model1 |
Наконец, используем DecisionBoundaryDisplay, чтобы построить график предсказанных вероятностей. Используя расходящуюся цветовую карту (например, "RdBu"), мы можем убедиться, что более темные цвета соответствуют predict_proba, близким к 0 или 1, а белый цвет — predict_proba, равным 0.5.
from itertools import product
from sklearn.inspection import DecisionBoundaryDisplay
fig, axarr = plt.subplots(2, 2, sharex="col", sharey="row", figsize=(10, 8))
for idx, clf, title in zip(
product([0, 1], [0, 1]),
[clf1, clf2, clf3, eclf],
[
"Splines with\nconstant extrapolation",
"Splines with\nperiodic extrapolation",
"RBF Nystroem",
"Soft Voting",
],
):
disp = DecisionBoundaryDisplay.from_estimator(
clf,
X,
response_method="predict_proba",
plot_method="pcolormesh",
cmap="RdBu",
alpha=0.8,
ax=axarr[idx[0], idx[1]],
)
axarr[idx[0], idx[1]].scatter(
X["Feature #0"],
X["Feature #1"],
c=y,
**common_scatter_plot_params,
)
axarr[idx[0], idx[1]].set_title(title)
fig.colorbar(disp.surface_, ax=axarr[idx[0], idx[1]], label="Probability estimate")
plt.show()

Для проверки можно убедиться, что для заданного образца вероятность, предсказанная VotingClassifier, действительно является взвешенным средним мягких предсказаний отдельных классификаторов.
В случае бинарной классификации, как в данном примере, массивы predict_proba содержат вероятность принадлежности к классу 0 (здесь обозначена красным) в качестве первого элемента и вероятность принадлежности к классу 1 (здесь обозначена синим) в качестве второго элемента.
test_sample = pd.DataFrame({"Feature #0": [-0.5], "Feature #1": [1.5]})
predict_probas = [est.predict_proba(test_sample).ravel() for est in eclf.estimators_]
for (est_name, _), est_probas in zip(eclf.estimators, predict_probas):
print(f"{est_name}'s predicted probabilities: {est_probas}")
constant splines model's predicted probabilities: [0.11272662 0.88727338] periodic splines model's predicted probabilities: [0.99726573 0.00273427] nystroem model's predicted probabilities: [0.3185838 0.6814162]
print(
"Weighted average of soft-predictions: "
f"{np.dot(weights, predict_probas) / np.sum(weights)}"
)
Weighted average of soft-predictions: [0.3630784 0.6369216]
Как видно, приведенный выше ручной расчет предсказанных вероятностей эквивалентен результату VotingClassifier:
print(
"Predicted probability of VotingClassifier: "
f"{eclf.predict_proba(test_sample).ravel()}"
)
Predicted probability of VotingClassifier: [0.3630784 0.6369216]
Чтобы преобразовать мягкие предсказания в жесткие при наличии весов, для каждого класса вычисляется взвешенное среднее предсказанных вероятностей. Затем итоговая метка класса определяется по метке класса с наибольшей средней вероятностью, что соответствует порогу по умолчанию predict_proba=0.5 в случае бинарной классификации.
print(
"Class with the highest weighted average of soft-predictions: "
f"{np.argmax(np.dot(weights, predict_probas) / np.sum(weights))}"
)
Class with the highest weighted average of soft-predictions: 1
Это эквивалентно результату метода predict объекта VotingClassifier:
print(f"Predicted class of VotingClassifier: {eclf.predict(test_sample).ravel()}")
Predicted class of VotingClassifier: [1]
Мягкие голоса можно порогово преобразовывать, как и результаты любого другого вероятностного классификатора. Это позволяет задать порог вероятности, при превышении которого будет предсказываться положительный класс, вместо того чтобы просто выбирать класс с наибольшей предсказанной вероятностью.
from sklearn.model_selection import FixedThresholdClassifier
eclf_other_threshold = FixedThresholdClassifier(
eclf, threshold=0.7, response_method="predict_proba"
).fit(X, y)
print(
"Predicted class of thresholded VotingClassifier: "
f"{eclf_other_threshold.predict(test_sample)}"
)
Predicted class of thresholded VotingClassifier: [0]
Общее время выполнения скрипта: (0 минут 0.713 секунды)
Связанные примеры
© 2007–2026 The scikit-learn developers
Licensed under the 3-clause BSD License.
https://scikit-learn.org/1.9/auto_examples/ensemble/plot_voting_decision_regions.html