На самом деле продемонстрировать не сложно, почему использование всего набора данных (т. Е. Перед разделением на обучение / тестирование) для выбора объектов может сбить вас с толку.Вот одна из таких демонстраций с использованием случайных фиктивных данных с Python и scikit-learn:
import numpy as np
from sklearn.feature_selection import SelectKBest
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
# random data:
X = np.random.randn(500, 10000)
y = np.random.choice(2, size=500)
Поскольку наши данные X
являются случайными (500 выборок, 10 000 объектов), а наши метки y
являются двоичными,мы ожидаем, что мы никогда не сможем превысить базовую точность для такой настройки, то есть ~ 0,5, или около 50%.Давайте посмотрим, что происходит, когда мы применяем неправильную процедуру использования всего набора данных для выбора объектов, прежде чем разделить:
selector = SelectKBest(k=25)
# first select features
X_selected = selector.fit_transform(X,y)
# then split
X_selected_train, X_selected_test, y_train, y_test = train_test_split(X_selected, y, test_size=0.25, random_state=42)
# fit a simple logistic regression
lr = LogisticRegression()
lr.fit(X_selected_train,y_train)
# predict on the test set and get the test accuracy:
y_pred = lr.predict(X_selected_test)
accuracy_score(y_test, y_pred)
# 0.76000000000000001
Вау!Мы получаем 76% тест точность по бинарной проблеме, где, согласно самым основным законам статистики, мы должны получить что-то очень близкое к 50%!
Правда в том, чтонам удалось получить такую точность теста просто потому, что мы совершили очень простую ошибку: мы по ошибке думаем , что наши тестовые данные не видны, но на самом деле тестовые данные уже были обнаружены в процессе построения моделиво время выбора функции, в частности, здесь:
X_selected = selector.fit_transform(X,y)
Насколько плохо выключен мы можем быть в реальности?Что ж, опять же нетрудно увидеть: предположим, что после того, как мы закончили с нашей моделью и у нас развернуто ее (ожидая что-то похожее на 76% точности на практике с новыми невидимыми данными), мы получим некоторые действительно новые данные:
X_new = np.random.randn(500, 10000)
, где, конечно, нет никаких качественных изменений, то есть новых тенденций или чего-то еще - эти новые данные генерируются с помощью той же самой базовой процедуры.Предположим также, что мы знаем истинные метки y
, сгенерированные, как указано выше:
y_new = np.random.choice(2, size=500)
Как наша модель будет работать здесь, когда столкнется с этими действительно невидимыми данными?Нетрудно проверить:
# select the same features in the new data
X_new_selected = selector.transform(X_new)
# predict and get the accuracy:
y_new_pred = lr.predict(X_new_selected)
accuracy_score(y_new, y_new_pred)
# 0.45200000000000001
Ну, это правда: мы отправили нашу модель в бой, думая, что она способна с точностью ~ 76%, но на самом деле она работает как случайное предположение...
Итак, давайте теперь посмотрим правильную процедуру (т. Е. Сначала разделим, и выберем функции, основываясь только на наборе training ):
# split first
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=42)
# then select features using the training set only
selector = SelectKBest(k=25)
X_train_selected = selector.fit_transform(X_train,y_train)
# fit again a simple logistic regression
lr.fit(X_train_selected,y_train)
# select the same features on the test set, predict, and get the test accuracy:
X_test_selected = selector.transform(X_test)
y_pred = lr.predict(X_test_selected)
accuracy_score(y_test, y_pred)
# 0.52800000000000002
Там, где точность теста 0f 0,528 достаточно близка к теоретически предсказанному одному из 0,5 в таком случае (т. Е. Фактически случайному угадыванию).
Престижность Якобу Шрайберу за предоставление простой идеи(отметьте все темы , они содержат другие полезные примеры), хотя и в несколько ином контексте, чем тот, о котором вы спрашиваете здесь (перекрестная проверка):