SVC и predict_proba: обучаем калибровку вероятностей

SVC успешно предсказывает классы, но у обычного экземпляра нет predict_proba. Расстояние до разделяющей поверхности не является вероятностью. Для вероятностного выхода потребуется отдельная обучаемая процедура.

Выбрать интерфейс до сохранения модели

SVC возвращает метку через predict и числовое расстояние через decision_function. Потребитель, который ожидает вероятность от нуля до единицы, не должен незаметно получать расстояние. Сначала согласуйте смысл выхода, затем обучайте соответствующий составной estimator.

В scikit-learn 1.9 параметр SVC probability объявлен устаревающим. Для нового кода используйте CalibratedClassifierCV вокруг SVC; это рекомендуемый путь вместо включения probability=True. Если поддерживаете более раннюю версию с этим параметром, он также должен быть выбран до fit: изменение настройки после обучения не рассчитывает недостающую калибровку.

Обучить модель вместе с вероятностным выходом

CalibratedClassifierCV с ensemble=False получает предсказания по разбиениям для обучения калибровки, а базовый estimator затем обучается на всех переданных данных. Полученный объект включает и классификатор, и обученное преобразование его выхода. Сохранять и применять нужно этот объект целиком.

Число разбиений должно соответствовать доступному количеству объектов каждого класса. В примере у каждого класса три строки, поэтому явно задаём cv=3. Для рабочего набора способ разбиения выбирают по структуре задачи. Автоматический обычный CV не учитывает временной порядок или связанные группы объектов.

Клонируем исходный SVC, чтобы настройки сохранились, а предыдущий результат fit не оказался скрытым состоянием новой процедуры. Базовый labels_only остаётся отдельным классификатором. Обёртка сама управляет новыми обучениями.

import math
from sklearn.base import clone
from sklearn.svm import SVC
from sklearn.calibration import CalibratedClassifierCV

X = [[-3], [-2], [-1], [1], [2], [3]]
y = ["left", "left", "left", "right", "right", "right"]
labels_only = SVC(kernel="linear").fit(X, y)
assert not hasattr(labels_only, "predict_proba")
with_probabilities = CalibratedClassifierCV(
    clone(labels_only), cv=3, ensemble=False).fit(X, y)
probabilities = with_probabilities.predict_proba([[-2], [2]])
assert probabilities.shape == (2, 2)
assert with_probabilities.classes_.tolist() == ["left", "right"]
for row in probabilities.tolist():
    assert all(math.isfinite(value) and 0 <= value <= 1 for value in row)
    assert abs(sum(row) - 1) < 1e-9
print(probabilities.tolist())

Проверить договорённость потребителя

Результат имеет две строки и два столбца, соответствующих classes_. Проверяем конечность, диапазон и сумму каждой строки. Эти проверки подтверждают форму вероятностного выхода на конкретном запуске. Они не оценивают, насколько вероятности соответствуют реальным частотам события на будущих данных: это отдельная проверка применения модели.

Если потребитель раньше использовал decision_function, его числовой порог нельзя автоматически перенести на predict_proba. Изменилась шкала. Обновите описание результата и правила принятия решения вместе с артефактом модели. Для многоклассовой задачи также не требуйте, чтобы любой путь получения класса всегда совпадал: способ построения вероятностей имеет собственную процедуру.

Дополнительная калибровка увеличивает объём обучения. Если задача требует только ранжирования по расстоянию, сначала выясните, нужны ли вероятности вообще. Если интерфейс явно обещает вероятность, соответствующая обученная часть должна присутствовать до публикации модели.

Начать с классификатора и его выходов

В открытом уроке LogisticRegression вы разберёте fit, predict и classes_. Этот базовый цикл помогает различать метки и дополнительные числовые выходы классификаторов. Откройте программу курса, чтобы связать обучение с использованием сохранённого результата.

Продолжить на практике