Как получилось, что оценка точности для 10-кратной перекрестной проверки хуже, чем для 90-10 train_test_split с использованием sklearn?

#python #machine-learning #scikit-learn #k-fold

Вопрос:

Задача состоит в двоичной классификации с помощью нейронной сети. Данные представлены в словаре, который содержит составные имена (в качестве ключа) каждой записи и метки (0 или 1, как третий элемент в векторном значении). Первый и второй элементы-это две части составного имени, которые позже используются для извлечения соответствующих функций.

В обоих случаях словарь преобразуется в два массива с целью выполнения сбалансированной неполной выборки класса большинства (которая присутствует в 66% данных):

 data_for_sampling = np.asarray([key for key in list(data.keys())])
labels_for_sampling = [element[2] for element in list(data.values())]

sampler = RandomUnderSampler(sampling_strategy = 'majority')
data_sampled, label_sampled = sampler.fit_resample(data_for_sampling.reshape(-1, 1), labels_for_sampling)
 

Затем пересчитанные массивы имен и меток используются для создания обучающих и тестовых наборов с помощью метода Kfold:

 kfolder = KFold(n_splits = 10, shuffle = True)
kfolder.get_n_splits(data_sampled)

for train_index, test_index in kfolder.split(data_sampled):

        data_train, data_test = data_sampled[train_index], data_sampled[test_index]
 

Или метод train_test_split:

 data_train, data_test, label_train, label_test = train_test_split(data_sampled, label_sampled, test_size = 0.1, shuffle = True)
 

Наконец, имена из data_train и data_test используются для повторного извлечения соответствующих записей (по ключу) из исходного словаря, который затем используется для сбора характеристик этих записей. Насколько я понимаю, однократное разделение 10-кратных наборов должно обеспечивать такое же распределение данных для тестирования на тренировках, как и 90-10 train_test_split, и это, по-видимому, верно во время обучения, когда оба обучающих набора дают точность ~0,82 только после одной эпохи, выполняемой отдельно с помощью model.fit(). Однако, когда соответствующие модели оцениваются с помощью model.evaluate() на тестовых наборах после указанной эпохи, набор из train_test_split дает оценку ~0,86, в то время как набор из Kfold составляет ~0,72. Я провел множество тестов, чтобы убедиться, что это просто плохое случайное семя, которое не ограничено, но результаты остались прежними. Наборы также имеют правильно сбалансированное распределение меток и, казалось бы, хорошо перемешанные записи.

Ответ №1:

В зависимости от количества шума в данных и от размера набора данных можно ожидать, что при таком поведении оценки по выборке данных будут отклоняться на эту величину. Одно разделение не гарантируется таким же, как и любое другое разделение, поэтому у вас сначала 10, а затем среднее значение по всем результатам.

То, что вы должны считать наиболее обобщаемым, — это не какое-либо одно данное разделение (независимо от того, происходит ли оно из одного из 10 сгибов или train_test_split() ), но гораздо более надежным является средний результат по всем N сгибам.

Более глубокое изучение данных может выявить, есть ли какая-то причина, по которой одно или несколько расщеплений так сильно отличаются друг от друга. Например, возможно, в ваших данных есть какая-то особенность (например, «дата сбора образца» и методология сбора менялись от месяца к месяцу), из-за которой данные отличаются друг от друга предвзятым образом. Если это так, вам следует использовать стратифицированное разделение тестов (также в вашем резюме) (см. Документацию по этому вопросу в scikit-learn), чтобы вы могли получить более объективную группировку своих данных.

Ответ №2:

Как оказалось, проблема возникает из-за сочетания источников:

В то время как shuffle = True в методе train_test_split() сначала правильно перемешивает предоставленные данные, а затем разбивает их на нужные части, значение shuffle = True в методе Kfold приводит только к случайным образом построенным складкам, однако данные в складках остаются упорядоченными.

Это то, на что указывает документация, благодаря этому сообщению: https://github.com/scikit-learn/scikit-learn/issues/16068

Теперь, во время обучения, моя пользовательская функция поезда снова применяет перемешивание к данным поезда, просто для уверенности, однако она не перемешивает тестовые данные. Более того, model.evaluate() по умолчанию принимает значение batch_size = 32, если параметр не задан, что в сочетании с упорядоченными тестовыми данными привело к расхождению в точности проверки. Тестовые данные действительно ошибочны в том смысле, что они содержат большую часть «труднопрогнозируемых» записей, которые были сгруппированы вместе благодаря упорядочению и, похоже, снизили среднюю точность результатов. Учитывая завершенный прогон по всем N складкам, как указал ТК Арлен, в конце концов, возможно, действительно была дана более точная оценка, но я ожидал более близких результатов только после одного сгиба, что привело к обнаружению этой проблемы.