Data Science ML Статистика

Сэмплирование данных в ML

Сэмплирование (sampling) – это процесс выбора подмножества данных из полной их совокупности.

Необходимость в сэмплировании может встречаться на разных этапах жизненного цикла проекта.

Когда нужно сэмплирование?

  • На этапе прототипирования, когда нужно быстро построить и оценить намеченную модель.
  • Для балансировки классов – с помощью сэмплирования можно выровнять количество примеров для каждого класса в данных с их неравномерным содержанием.
  • Для визуализации и быстрого исследования данных.
  • Для разделения на тренировочную, тестовую и валидационную выборки.

Варианты сэмплирования и их особенности

Неслучайное сэмплирование (не рекомендуется)

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

Примеры неслучайного сэмплирования:

  • на основании доступности данных – берется то, что доступно
  • метод снежного кома – берутся данные, связанные с уже имеющимися данными (например, данные аккаунтов соцсети, связанные с имеющимися в распоряжении – друзья, лайкнутые и т.д.).
  • на основе экспертных оценок – выборка делается на основе оценок экспертов.
  • по квоте – отбор нужного числа наблюдений для каждого класса без какой-либо рандомизации.

Случайное сэмплирование

Случайный выбор из всей совокупности. Например, выбрать 10% из всех данных.

Минус этого подхода – редко встречающиеся классы могут вовсе не попасть в выборку.

Сэмплирование с возвратом

Случайный выбор элементов из совокупности, допускающий повторение одного и того же элемента. То есть в результирующей выборке один и тот же элемент может встретиться 2 и более раз. А некоторые элементы могут вовсе не попасть в выборку. Новая выборка может быть равна по размеру или быть даже больше исходной.

Этот вариант сэмплирования лежит в основе bootstrap – метода статистической оценки данных. При использовании бутстрэпа из всей совокупности делаются многократные выборки с возвратом.

Сэмплирование с возвратом используется в подходе bagging в алгоритме случайного леса.

Сэмплирование по времени

Выбор подмножеств по временным меткам. Используется в задачах прогнозирования для временных рядов.

Стратифицированное сэмплирование

Сначала данные разделяются на классы (страты), затем из каждого класса берется нужный процент данных (например, 10%). Это обеспечивает сохранение всех классов, включая малочисленные.

Этот метод сложно использовать в случае мультиметочной классификации (когда одно наблюдение может принадлежать нескольким классам, имеет несколько меток классов).

Undersampling и Oversampling

Методы изменения количества примеров в данных с целью балансировки по классам.

  • Undersampling – удаление части примеров из мажоритарного класса.
  • Oversampling – создание дополнительных примеров для миноритарных классов (SMOTE – один из подходов).

Кластерное сэмплирование

Это метод сэмплирования, при котором сначала производится разбиение на кластеры (группы), а затем случайным образом берется несколько кластеров. В дальнейшем анализе используются все элементы из выбранных кластеров.

Например, необходимо провести опрос среди всех преподавателей вузов. Сначала выполняется кластеризация – кластерами являются университеты. Затем случайным образом выбирается несколько университетов, и выполняется опрос всех преподавателей выбранных университетов.

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

Кластерное сэмплирование удобно, когда все данные уже естественным образом разбиваются на кластеры: города, университеты, больницы, отделы, временные диапазоны и т.д.

Взвешенное сэмплирование

Каждому наблюдению может быть придан вес (вероятность попасть в выборку).

Это удобно в ситуациях, когда ясно, что не все данные имеют одинаковую ценность.

Например, если известно, что более поздние данные имеют бОльшую ценность, чем ранние, поздним данным необходимо придать бОльший вес при создании выборки.

Еще один пример – если данные поступают из разных источников в неравном количестве. Но при этом известно, что в реальности они встречаются с одинаковой вероятностью. Необходимо ввести веса для выравнивания количества данных из каждого источника.

Резервуарное сэмплирование

Резервуарное сэмплирование (англ. Reservoir Sampling) — это алгоритм, который позволяет случайным образом выбрать k элементов из потока данных неизвестной или очень большой длины, не храня весь поток в памяти.

Это особенно важно, когда:

  • данные приходят в виде потока (например, лог-файлы, телеметрия);
  • неизвестно заранее, сколько будет данных;
  • невозможно хранить все данные одновременно.

Резервуарное сэмплирование обеспечивает для каждого элемента равную вероятность попасть в выборку.

Созданная таким образом выборка фиксируется и далее используется как обычная выборка данных. Она как бы являет собой срез из потока данных на текущий момент.

Сэмплирование по важности

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

Примеры использования сэмплирования в реальных задачах

СценарийСэмплированиеЧто дает?
Классификация с дисбалансомStratified + OversamplingПовышение recall для редких классов
Потоковые данныеReservoir SamplingОграниченные ресурсы, невозможно хранить всё
Прототипирование модели на больших данныхRandom SamplingБыстрое обучение без переобучения
Time seriesTime-based split + rolling samplingСохранение причинности и трендов
A/B тестыStratified Sampling по географии или пользователямИзбежание перекоса в группах

Примеры кода сэмплирования

Резервуарное сэмплирование

import random

def reservoir_sampling_k(stream, k):
    reservoir = []  # Наша выборка из k случайных элементов

    for i, item in enumerate(stream):
        if i < k:
            # Первые k элементов — просто добавляем в резервуар
            reservoir.append(item)
        else:
            # Для следующих элементов:
            # Выбираем случайное число от 0 до i
            j = random.randint(0, i)
            if j < k:
                # С вероятностью k/i заменяем один из текущих элементов
                reservoir[j] = item

    return reservoir


# Поток из 10000 элементов
stream = (f"line_{x}" for x in range(10000))
print(reservoir_sampling_k(stream, 5))

Вывод
---------------------------------
['line_6341', 'line_8433', 'line_2542', 'line_2668', 'line_3236']

Кластерное сэмплирование

import random

# Допустим, у нас есть 4 кластера (города), в каждом — по 3 человека
population = {
    "CityA": ["A1", "A2", "A3"],
    "CityB": ["B1", "B2", "B3"],
    "CityC": ["C1", "C2", "C3"],
    "CityD": ["D1", "D2", "D3"],
}

# Случайно выбираем 2 города
sampled_clusters = random.sample(list(population.keys()), 2)

# Собираем всех людей из этих городов
cluster_sample = []
for cluster in sampled_clusters:
    cluster_sample.extend(population[cluster])

print("Выбранные кластеры:", sampled_clusters)
print("Выборка:", cluster_sample)

Вывод
----------------------
Выбранные кластеры: ['CityD', 'CityA']
Выборка: ['D1', 'D2', 'D3', 'A1', 'A2', 'A3']

Взвешенное сэмплирование

import random

"""Необходимо сделать выборку двух чисел из набора с учетом веса каждого элемента.
Числа 100 и 1000 имеют вдвое меньшую вероятность выбора."""

data = random.choices(
    population=[1, 2, 3, 4, 100, 1000],
    weights=[0.2, 0.2, 0.2, 0.2, 0.1, 0.1],
    k=2
)
# This expression is equivalent to the next
data_1 = random.choices(
    population=[1, 1, 2, 2, 3, 3, 4, 4, 100, 1000],
    k=2
)

print(data)
print(data_1)

Вывод
---------------------------------
[2, 2]
[3, 4]

Вставить формулу как
Блок
Строка
Дополнительные настройки
Цвет формулы
Цвет текста
#333333
Используйте LaTeX для набора формулы
Предпросмотр
\({}\)
Формула не набрана
Вставить