Утечка цели и неправильное разделение
Содержание курса
Удаление признака-нарушителя до разделения данных
Обнаружив утечку, нужно убрать признак из X — и сделать это до вызова train_test_split. Порядок важен.
import pandas as pd
from sklearn.model_selection import train_test_split
df = pd.DataFrame({
'hours_studied': [10, 5, 8, 3, 7],
'attendance_pct': [90, 60, 80, 40, 75],
'final_score': [88, 55, 78, 42, 72],
'passed': [1, 0, 1, 0, 1]
})
# Шаг 1: убираем цель и признак-нарушитель
X = df.drop(columns=['passed', 'final_score']) # утечка удалена
y = df['passed']
# Шаг 2: только после этого делим
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
print(list(X.columns)) # ['hours_studied', 'attendance_pct']
Почему именно до train_test_split? Разберём, что пошло бы не так, если бы мы оставили final_score в X и попытались исправить ситуацию уже после разделения — только для обучающей части.
Представим, что X сформирован с final_score и уже разбит на X_train и X_test. Тогда попытка удалить столбец только из train и обучить модель выглядела бы так:
# Плохой сценарий — только для иллюстрации, не повторять
X_bad = df.drop(columns=['passed']) # final_score оставлен
X_train_bad, X_test_bad, y_train, y_test = train_test_split(
X_bad, y, test_size=0.2, random_state=42
)
X_train_fixed = X_train_bad.drop(columns=['final_score']) # убрали только из train
from sklearn.tree import DecisionTreeClassifier
model = DecisionTreeClassifier()
model.fit(X_train_fixed, y_train) # fit видел 2 признака
model.predict(X_test_bad) # X_test_bad содержит 3 признака — несовпадение схем
Scikit-learn запоминает имена и количество столбцов при fit. Когда predict получает набор с другими столбцами, вызов завершается исключением — модель не может сделать ни одного предсказания. Это не тихое искажение оценки, а полная блокировка работы.
Можно, конечно, удалить final_score из обеих частей после разделения — технической ошибки тогда не будет. Но логика становится запутанной: нужно не забыть обработать каждую часть отдельно, и риск пропустить одну из них вполне реален. Гораздо надёжнее сформировать корректный X один раз, до любого деления.
Ещё опаснее сценарий, когда final_score остаётся в X целиком и попадает в fit. Насколько сильно просядет ошибка, зависит от модели и от того, насколько точно признак кодирует цель. Но даже если ошибка снизится лишь умеренно, оценка становится недостоверной: в реальном запросе final_score будет недоступен, и модель окажется бесполезной с первого же предсказания. Утечку обнаруживают не по величине ошибки, а задав вопрос о доступности признака в момент прогноза — именно этот вопрос позволяет отделить допустимые признаки от недопустимых.
Правило простое: X должен содержать только те столбцы, которые будут доступны в момент настоящего прогноза. Всё остальное — вон до split.
