Выборки. Джентльменский набор

Machine Learning

Простая случайная выборка

Вы хотите выбрать подмножество, в котором каждый член имеет равную вероятность быть выбранным. Ниже мы случайно выбираем 100 значений из набора данных:

sample_df = df.sample(100)

Название говорит само за себя. Это всё.

Стратифицированная выборка

Нам нужно оценить среднее количество голосов для каждого кандидата на выборах. В стране есть 3 города:

  • Город A с миллионом рабочих на фабрике.
  • Город B с 2 миллионами рабочих.
  • Город C с 3 миллионами пенсионеров.

Мы можем выбрать случайную выборку в 60 человек во всех городах, но есть некоторый шанс, что она окажется недостаточно сбалансированной по всем городам. Следовательно, она будет смещена, что приведет к значительной ошибке в оценке.

Если мы вместо этого сделаем случайную выборку 10, 20 и 30 человек из городов A, B и C, то можем получить меньшую ошибку в оценке для того же общего размера выборки. Сделать это в Python легко:

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y,
                                                    stratify=y, 
                                                    test_size=0.25)

Резервуарная выборка

Люблю эту задачу: у нас есть поток элементов большой и неизвестной длины, который можно прочитать только один раз. Создайте алгоритм, случайным образом выбирающий элемент из этого потока так, что каждый элемент будет выбран с равной вероятностью. Как это можно сделать? Предположим, у нас k = 5 элементов:

import randomdef generator(max):
    number = 1
    while number < max:
        number += 1
        yield number # Создаём генератор.
stream = generator(10000) # Делаем выборку.
reservoir = []
for i, element in enumerate(stream):
    if i+1<= k:
        reservoir.append(element)
    else:
        probability = k/(i+1)
        if random.random() < probability:
            # Выбираем новый элемент. Удаляем один из уже выбранных.
             reservoir[random.choice(range(0,k))] = elementprint(reservoir)
------------------------------------
[1369, 4108, 9986, 828, 5589]

Правильность алгоритма можно доказать математически. Как? Подумайте о еще меньшем количестве элементов. В математике это всегда помогает.

Допустим, в потоке всего 3 элемента, а 2 в резервуаре. Мы видим первый элемент, оставляем его в списке, так как в нашем резервуаре достаточно места. Мы видим второй элемент и поступаем так же по той же причине. Видим третий элемент. Здесь становится интересно! Выбираем третий элемент с вероятностью 2/3.

Посмотрим на вероятность выбора первого элемента:

Вероятность удаления первого элемента  —  это вероятность выбора элемента 3, умноженная на вероятность случайного выбора элемента 1 в качестве кандидата на замену из двух элементов в резервуаре, 2/3*1/2 = 1/3. Значит, вероятность его выбора: 1 – 1/3 = 2/3. То же и для второго элемента, а значит, закономерность расширяется на любой диапазон. Каждый элемент выбирается с вероятностью k/n.

Случайное удаление/добавление экземпляров

Обычно мы сталкиваемся с этим в несбалансированном наборе данных. Широко распространённый метод работы с сильно несбалансированными наборами  —  повторная выборка. Он состоит в удалении выборок из класса большинства (сужение, undersampling) и/или добавления большего количества примеров из класса меньшинства  —  дублирование элементов, oversampling.

Создадим пример несбалансированных данных:

from sklearn.datasets import make_classificationX, y = make_classification(
    n_classes=2, class_sep=1.5, weights=[0.9, 0.1],
    n_informative=3, n_redundant=1, flip_y=0,
    n_features=20, n_clusters_per_class=1,
    n_samples=100, random_state=10
)X = pd.DataFrame(X)
X['target'] = y

Сейчас мы можем сделать случайное удаление или дублирование элементов:

num_0 = len(X[X['target']==0])
num_1 = len(X[X['target']==1])
print(num_0,num_1)
# random undersample
undersampled_data = pd.concat([ X[X['target']==0].sample(num_1) , X[X['target']==1] ])
print(len(undersampled_data))
# random oversample
oversampled_data = pd.concat([ X[X['target']==0], X[X['target']==1].sample(num_0, replace=True) ])
print(len(oversampled_data))
------------------------------------------------------------
OUTPUT:
90 10
20
180

Under/oversampling в несбалансированных наборах

imblearn  —  модуль Python, созданный специально для наборов с дисбалансом. Он предоставляет методы расширения и сужения выборок.

1. Связи Томека

Один из таких методов  —  поиск связей Томека. Он состоит в поиске элементов противоположного класса на близком расстоянии. Это обеспечивает более точную классификацию.

from imblearn.under_sampling import TomekLinks

tl = TomekLinks(return_indices=True, ratio='majority')
X_tl, y_tl, id_tl = tl.fit_sample(X, y)

2. Генерация искусственных элементов

SMOTE (Synthetic Minority Oversampling Technique) искусственно создаёт примеры меньшего класса данных без дублирования.

from imblearn.over_sampling import SMOTE

smote = SMOTE(ratio='minority')
X_sm, y_sm = smote.fit_sample(X, y)

В imblearn есть и другие методы решения этих задач.

Заключение

Алгоритмы  —  это кровь науки о данных, но мы редко напоминаем себе об этом. У вас хорошая стратегия выборки  —  проект идет вверх. Плохая стратегия  —  и он провален. Будьте внимательны при выборе алгоритма.

Проект на Kaggle

Специально для сайта ITWORLD.UZ. Новость взята с сайта NOP::Nuances of programming