Как оптимально разделить набор данных на обучающую, валидационную и тестовую выборки?
https://t.me/data_analysis_mlКак оптимально разделить набор данных на обучающую, валидационную и тестовую выборки?
У каждого подмножества данных есть цель, от создания модели до обеспечения её производительности:
Обучающий набор: это подмножество данных, которые я буду использовать для обучения модели.
Валидационная выборка: используется для контроля процесса обучения. Она поможет предотвратить переобучение и обеспечит более точную настройку входных параметров.
Тестовый набор: подмножество данных для оценки производительности модели.
Размер и соотношение данных для обучения.
Чтобы принять решение о размере каждого подмножества, я часто вижу стандартные правила и соотношения. Сталкиваюсь с разделением по правилу 80-20 (80 % для тренировочного сплита, 20% для тестового сплита) или по правилу 70-20-10 (70% для обучения, 20% для проверки, 10% для тестирования) и т.д.
Было несколько дискуссий о том, каким может быть оптимальное разделение, но в целом отсутствие достаточного количества данных как в обучающем, так и в проверочном наборе приведет к тому, что модель будет трудно изучить/обучить или определить, действительно ли эта модель работает хорошо или нет.
В BigQuery, как правило, можно создать только подмножество для обучения и тестирования, потому что проверка может быть выполнена при создании модели с DATA_SPLIT опцией.
Прежде чем разделить набор данных, необходимо знать о некоторых распространённых ловушках:
Низкокачественные данные: если входные данные, использованные для создания вашей модели, имеют выбросы, это скорее всего негативно повлияет на качество модели.
Переобучение: когда входных данных недостаточно, но построенная модель хорошо объясняет параметры из обучающей выборки, то считается, что любой выброс или колебания приводят к недостоверным прогнозам.
Несбалансированный набор данных: представьте, что вы хотите обучить модель, которая предсказывает покупку клиента, но у вас есть 500 тысяч потенциальных покупателей и 300 покупателей. Для начала нужно создать более равный набор данных (используя технику повторной выборки). Исследовательский анализ данных (EDA) поможет выявить несбалансированные данные.
Утечка данных: означает, что входные данные были изменены (масштабированы, преобразованы) перед созданием обучающего набора данных или во время их разделения.
Разделение набора данных с помощью SQL
Для эксперимента у меня есть следующий набор данных, состоящий из 11 тыс. строк и трёх полей идентификатора заказа, даты и связанного значения заказа.
Разделю эту таблицу на обучающую и тестовую подгруппы.
Выбор правильного столбца для разделения
Чтобы сделать правильное разделение, используйте один столбец, который отличает каждую строку вашей базовой таблицы. В противном случае может быть трудно создать равномерно распределённое разделение.
Чтобы добиться уникальности, есть несколько вариантов в практических примерах:
- Создать новое поле с уникальными значениями (например, функция генератора случайных чисел, такая как RAND()или UUID())
- Создать хеш одного уже уникального поля или хеш комбинации полей, которая создает уникальный идентификатор строки
Для второго варианта наиболее распространенной хеш-функцией, является FARM_FINGERPRINT().
Почему именно этот, а не MD5() или SHA ()?
Это обусловлено двумя особенностями. Во-первых, он всегда даёт одни и те же результаты для одних и тех же входных данных, а во-вторых, он возвращает INT64 значение (число, а не комбинацию чисел и символов), которым можно управлять с помощью других математических функций, например, MOD () для получения коэффициента разделения.
Создайте уникальный идентификатор строки
Чтобы создать уникальный идентификатор строки, использую FARM_FINGERPRINT() функцию:
WITH base_table AS ( SELECT * FROM `datastic.train_test_split.base_table`) -- Main Query SELECT *, FARM_FINGERPRINT(order_id) AS hash_value_order_id, TO_JSON_STRING(bt) AS full_json_row, FARM_FINGERPRINT(TO_JSON_STRING(bt)) AS hash_value_full_row, MOD(FARM_FINGERPRINT(order_id), 10) AS hash_mod, ABS(MOD(FARM_FINGERPRINT(order_id), 10)) AS hash_abs_mod FROM base_table bt
То, что я буду продолжать разбивать в нашем наборе данных, это последний столбец (hash_abs_mod).
Следующая таблица иллюстрирует каждый шаг процесса хеширования слева направо.
Влияние хеш-функции и функции по модулю на нашу базовую таблицу.
В базовой таблице уже есть уникальный идентификатор значения для каждой строки, это order_id поле. В этом случае мы можем просто хешировать его, а использовать ABS(MOD(x,10)) функции для классификации каждой строки в сегментах от 0 до 9.
Чтобы создать хеш всей строки, то есть всех столбцов, обозначим её bt, затем делаем JSON, используя TO_JSON_STRING (bt).
Затем хешировать? Цель этого метода избежать повторяющихся строк, которые всегда будут попадать в одно и то же разделение.
Создание неповторяемого разделения
Чтобы случайным образом разделить набор данных, можно использовать RAND () функцию. Это вернёт для каждой строки псевдослучайное десятичное число в интервале 0–1, включая 0 и исключая 1.
Эта функция обеспечивает равномерное распределение, что означает, что можно отфильтровать, например, все значения ниже 0,8-80% моих данных.
Но каждый раз, когда запускаем запрос, функция будет возвращать разные числа, поэтому наборы для обучения и тестирования будут разными.
Запущу случайное разделение для 80% обучения и 20% тестирования:
WITH base_table AS ( SELECT * FROM `datastic.train_test_split.base_table`) -- Main Query SELECT *, RAND() AS pseudo_random, IF (RAND() < 0.8, 'train', 'test') AS split_set, FROM base_table view raw
Вы можете увидеть в результатах, что есть число со многими десятичными знаками, сгенерированное RAND () функцией, и метка, заданная IF () оператором.
Но, не сможете воспроизвести точно такое же разделение, если не сохраните результаты как есть.
Разделить на обучение и тестирование с помощью RAND.
Создание повторяемого разделения
Чтобы создать разделение, которое можно использовать повторно, лучше всего использовать комбинацию функций MOD()и FARM_FINGERPRINT(), потому что выходные данные останутся одинаковыми для одних и тех же входных данных.
Использую уникальный ключ (order_id поле). Можно использовать оператор CASE WHEN со следующим диапазоном, чтобы сделать повторяемое разделение 80/10/10:
WITH base_table AS ( SELECT * FROM `datastic.train_test_split.base_table`) -- Main Query SELECT *, CASE WHEN ABS(MOD(FARM_FINGERPRINT(order_id), 10)) < 8 THEN 'train' WHEN ABS(MOD(FARM_FINGERPRINT(order_id), 10)) = 8 THEN 'validation' WHEN ABS(MOD(FARM_FINGERPRINT(order_id), 10)) = 9 THEN 'test' END AS split_set FROM base_table bt
Разделение на 3 подмножества (обучение, проверка и тестирование).
Результат будет таким же, как и для предыдущего неповторяемого разделения, за исключением того, что этот запрос всегда будет производить одно и то же разделение.
Разделить на обучение, проверку и тестирование с помощью MOD () и FARM_FINGERPRINT ().
Вывод:
Теперь вы знаете, как лучше разделить набор данных на подмножества, с возможностью повторного использования. Этот метод дает 2 преимущества:
1) Одни и те же результаты для входных данных.
2) Возвращает значение в виде числа, а не комбинацию чисел и символов, которым можно легко управлять, что позволяет сократить время обработки. Это означает, что необходимо проверить, какой временной диапазон покрывает 80% (в случае разделения 80/20) наших базовых данных.
Кроме того, строки с одной и той же датой имеют тенденцию быть коррелированными, поэтому эти строки должны оставаться в одном разбиении.
https://t.me/ai_machinelearning_big_data