Предвзятые данные и как с ними бороться
История недостаточной выборки
Мой студент только что закончил диплом с отличием в области науки о данных, связанный с классификацией - или, по крайней мере, он думал, что закончил. Он получил смесь из нескольких тысяч твитов, опубликованных ~ 2000 студентов и ~ 700 предпринимателями из района Бостона. Ему пришлось обучить модель машинного обучения (RandomForestClassifier), которая предсказывала, был ли твит опубликован студентом или предпринимателем. Звучит как легкая задача. Он произвольно выбрал 70% твитов для обучения, а остальные для тестирования. Модель вроде бы работала и имела точность 83% - совсем неплохо для курсового проекта.
К сожалению, модель очень хорошо распознавала твиты студентов (точность 95%), но довольно плохо распознавала твиты предпринимателей (точность 55%). Почему? Потому что в наборе обучающих данных было ~ 1400 студентов и только ~ 500 предпринимателей. Другими словами, он был предвзято относился к студентам и научился лучше распознавать их твиты.
Один из способов исправить набор обучающих данных - предоставить равное или несколько равное количество выборок для каждого результата. Если у нас есть только ~ 500 предпринимателей в данных по обучению, тогда мы должны также включить только ~ 500 студентов, если наша цель не состоит в том, чтобы исказить учебный процесс. Scikit-Learn, самый популярный пакет анализа данных в Python, не имеет функции разделения на тестовые и обучающие программы без смещения. Но мы можем построить его сами.
Пусть X будет фреймом данных Pandas, который содержит функции (предикторы) и результаты для нашего полного набора данных. Характеристики находятся в столбцах x1..xN, а результаты - в столбце y. Начните с вычисления количества образцов, подтверждающих каждый из результатов:
from numpy.random import binomial np.random.seed(0) # Generate synthetic dataset with three outcomes samples = 100 X = pd.DataFrame({"x1": binomial(1, 0.3, samples), "x2": binomial(1, 0.6, samples), "y": binomial(2, 0.5, samples)}) grouper = X.groupby("y") sizes = grouper.size() #0 26 <- the smallest #1 46 #2 28
Предположим, мы все еще хотим выбрать 70% из наименьшей группы выборок для обучения и соответствующее количество выборок из более крупных групп. Мы можем рассчитать процент образцов из более крупных групп, чтобы соответствовать 70% наименьшей группы. Эти проценты, кстати, являются вероятностями выбора образца:
fracs = (sizes.min() * 0.7) / sizes #0 0.700000 <- the smallest #1 0.395652 #2 0.650000
Теперь, когда мы знаем вероятности, мы можем использовать биномиальное распределение (np.random.binomial
) и его логическое дополнение, чтобы сделать случайный выбор обучающей и тестовой выборок соответственно. Мы будем накапливать образцы в двух изначально пустых DataFrames по мере прохождения групп идентичных результатов:
test = pd.DataFrame() train = pd.DataFrame() for grp, data in grouper: # reuse the grouper! mask = binomial(1, fracs.loc[grp], data.shape[0]).astype(bool) train = train.append(data[mask]) test = test.append(data[~mask])
Количество строк в наборе обучающих данных теперь примерно одинаково для каждого результата. Набор обучающих данных сбалансирован и не способствует какому-либо конкретному результату:
train.groupby("y").size() #y #0 21 #1 20 #2 23
Модель, обученная на этом наборе данных, увидит примерно одинаковое количество выборок, принадлежащих трем классам, и не будет смещена.
Должны ли мы также уравнять набор данных тестирования, который не сбалансирован? Скорее всего, нет: вы используете его только для тестирования, и его предвзятость не должна вызывать беспокойства.