LogisticRegression: fit, predict и classes_
Содержание курса
clf.fit, атрибут classes_ и контракт обучения
Вызов обучения выглядит точно так же, как у LinearRegression:
clf.fit(X_train, y_train)
Контракт тот же: первый аргумент — матрица признаков, второй — вектор целевых значений. До этого вызова классификатор — пустой объект; после — он знает всё, что нужно для предсказания.
Сразу после fit у clf появляется атрибут classes_:
print(clf.classes_) # array([0, 1])
classes_ — это NumPy-массив уникальных меток из y_train, отсортированных по возрастанию. Если бы y содержал метки {1, 0} в любом порядке, classes_ всё равно был бы array([0, 1]). Сортировка гарантированная и не зависит от того, как метки встречались в данных.
Почему это важно? Во-первых, classes_ позволяет убедиться, что модель действительно увидела оба класса. Во-вторых, порядок в classes_ фиксирует соответствие между индексами и метками — это пригодится в следующем уроке, когда речь пойдёт о вероятностях.
До вызова fit обращение к classes_ вызовет AttributeError: атрибут просто не существует на необученном объекте. Это нормальное поведение sklearn — атрибуты, которые зависят от данных, создаются только во время обучения.
fit требует обоих классов. LogisticRegression не умеет обучаться, если в y_train присутствует только одна метка — вызов завершится ValueError. Поэтому перед обучением стоит проверить состав обоих наборов:
print(y_train.value_counts())
print(y_test.value_counts())
Если один из классов отсутствует в y_train, нужно сначала выяснить причину. Первый шаг — проверить исходный y до разбиения:
print(y.value_counts())
Дальнейшие действия зависят от того, что вы увидите.
Оба класса есть в исходном y, но один не попал в y_train — скорее всего, это неудачное случайное распределение при конкретном random_state. Попробуйте параметр stratify=y в train_test_split: он стремится сохранить пропорции классов в каждой части.
# 1. Заменяем разбиение
X_train_raw, X_test_raw, y_train, y_test = train_test_split(
X_raw, y, test_size=0.2, random_state=42, stratify=y
)
# 2. Заново обучаем preprocessor на новом X_train_raw
X_train = preprocessor.fit_transform(X_train_raw)
X_test = preprocessor.transform(X_test_raw)
# 3. Только теперь обучаем классификатор
clf.fit(X_train, y_train)
Но stratify — не универсальное решение. Для его корректной работы каждый класс должен встречаться в y хотя бы в двух экземплярах, иначе train_test_split завершится ValueError. Даже при достаточном числе экземпляров округление при разбиении может оставить редкий класс без представителей в одной из частей. Поэтому после применения stratify всегда проверяйте результат явно:
print(y_train.value_counts())
print(y_test.value_counts())
Если редкого класса по-прежнему не хватает — это сигнал, что примеров этого класса в данных действительно мало. Менять random_state вручную, перебирая варианты, не стоит: это не увеличивает число примеров и даёт лишь случайный результат без каких-либо гарантий.
Исходный y содержит только один класс — проблема не в разбиении, а в самих данных: объектов второго класса просто нет. Ни stratify, ни смена параметров разбиения здесь не помогут. Нужны реальные примеры пропущенного класса.
Когда примеров редкого класса действительно мало, стоит рассмотреть сбор дополнительных данных или пересмотр схемы оценки. Например, кросс-валидация позволяет эффективнее использовать ограниченный набор, но не решает проблему автоматически: нужно убедиться, что оба класса представлены в обучающей части каждого фолда. Это выходит за рамки текущего урока, но важно понимать: никакой инструмент не заменяет достаточного числа примеров обоих классов.
