3.3. Кросс-валидация
Автор: Елистратова Евгения
Введение
Кросс-валидация — это процедура для оценки качества работы модели, которая широко применяется в машинном обучении. Она помогает сравнить между собой различные модели и выбрать наилучшую для конкретной задачи.
В этом разделе мы рассмотрим наиболее распространённые методы кросс-валидации, а также обсудим возможные проблемы, которые могут возникнуть в процессе их применения.
Методы кросс-валидации
Hold-out
Метод hold-out представляет из себя простое разделение набора данных на тренировочное множество (трейн, от англ. train) и множество для оценки качества модели (тест, от англ. test).
Такое разделение очень легко реализовать с помощью библиотеки sklearn:
import numpy as np
from sklearn.model_selection import train_test_split
X, y = np.arange(1000).reshape((500, 2)), np.arange(500)
X_train, X_test, y_train, y_test = train_test_split(
X, y,
test_size=0.2,
random_state=42
)
Чтобы оценить модель, вы обучаете её на тренировочном множестве, а результаты измеряете на тестовом. У sklearn по дефолту выставлен параметр shuffle=True, то есть перед разделением на тренировочное и тестовое множества происходит перемешивание семплов (и для воспроизводимости такого разбиения нужно фиксировать random_state).
А что будет, если не перемешать данные?
Если обучение модели не зависит от порядка подачи в неё примеров (что верно, например, для KNN или решающего дерева), то перемешивание данных влияет только на то, что в итоге окажется в трейне, а что — в тесте. Если данные шли какими-то группами (например, сначала 800 000 картинок с кошками, а за ними — 200 000 картинок с собаками), а train_test_split был совершён в пропорции 0,8, то модель просто не увидит собак в трейне.
А в случае, когда модель обучается с помощью градиентного спуска или его вариации (про различные модификации SGD подробно расскажем в статье о нейросетях), отсутствие перемешивания данных может влиять более интересным образом.
Вот пример из практики Yandex Research. Как вы думаете, что не так с графиком обучения этой модели?
Ответ (не открывайте сразу, сначала подумайте сами!)
На графике видна периодичность по числу итераций! По большим пикам можно вычислить места, где проход по данным начался заново. Кроме того, график в конце ползёт вниз. Это означает, что модель уже начала переобучаться, выучив последовательность данных на трейне и используя эту информацию больше, чем сами данные.
Если данные перемешать, то график обучения станет таким:
Источник: курс Лены Войты по NLP
Можно привести даже более простой пример, когда отсутствие перемешивания данных может вас сильно подвести.
Вспомним обозначенный ранее датасет из миллиона картинок кошек и собак. Пусть изначальный порядок тренировочных данных такой: сначала подряд идёт полмиллиона картинок с кошками, а затем так же подряд идут картинки с собаками. Тогда модель на первой половине обучения выучит, что на картинке всегда кошка, а за вторую забудет, что учила на первой, и будет всегда предсказывать собак. При этом на сами данные при предсказании она опираться не будет вообще.
Продолжим. Если у вас достаточно данных, лучше всегда предусматривать также валидационное множество. Это третье множество данных, которое позволяет безопасно оценить качество модели и предотвратить её переобучение.
Разбиение данных на тренировочное, тестовое и валидационное множество в библиотеке sklearn:
import numpy as np
from sklearn.model_selection import train_test_split
X, y = np.arange(1000).reshape((500, 2)), np.arange(500)
X_train, X_test, y_train, y_test = train_test_split(
X, y,
test_size=0.2,
random_state=42
)
X_train, X_val, y_train, y_val = train_test_split(
X_train, y_train,
test_size=0.1,
random_state=42
)
Если вы перебираете какие-то модели для вашей задачи, то оптимизировать их качество стоит на валидационном множестве, а окончательно сравнивать модели на тестовом множестве.
Оптимизация модели может включать:
- подбор гиперпараметров;
- подбор архитектуры (если речь идёт о нейросетях);
- подбор оптимального трешхолда для максимизации значений целевой метрики — например, вы делаете двуклассовую классификацию, а модель выдаёт непрерывные значения от 0 до 1, которые нужно бинаризовать так, чтобы получить максимальный скор по F1.
Если же оптимизировать модели и сравнивать их на одном и том же множестве, то можно неявно заложить в модели информацию о тестовом множестве и получить результаты на новых данных хуже ожидаемых.
Немного прервёмся на пример: к чему может привести неявное использование моделью тестового множества.
Пример
Представьте, что вы хотите обучить модель одномерной линейной регрессии для предсказания ваших данных:
где и — искомые параметры вашей модели.
Однако представьте, что параметр вам кто-то запретил обучать на тренировочном множестве и для вас у этой модели всего один параметр. Пусть на первой итерации у вас задано фиксированное значение . Используя это значение, вы подобрали на трейне лучшее при данном и замерили качество получившейся модели на тестовом множестве.
На следующей итерации вы взяли значение , повторили с ним предыдущий шаг. И так далее.
Теперь пришло время выбирать модель, и из всех них вы выбрали ту, которая показала лучший результат на тестовом множестве. Вам может показаться, что ваша модель с одним параметром обучена на трейне и всё хорошо, но на самом деле вы использовали оба множества, чтобы обучить модель с двумя параметрами, и теперь ваша тестовая оценка качества модели завышена.
Этот пример может показаться искусственным, но на деле он легко переносится на модели любой сложности. Просто представьте себе, что часть обучаемых весов вашей сложной модели вам запретили обучать на трейне и вы начинаете так же, как и выше, оценивать их на тесте, то есть по факту учить на тесте.
А чем такая ситуация отличается от подбора гиперпараметров модели (которые вы уже действительно не можете обучить на трейне) сразу на тестовом множестве? По сути, ничем.
Продолжим. Перед внедрением найденную лучшую модель можно обучить на всех имеющихся данных. Правда, вы не сможете оценить качество получившейся модели, так как у вас уже не будет тестового множества. Чтобы примерно оценить, как будет вести себя модель при добавлении новых данных, вы можете построить кривые обучения: зависимость качества модели на трейне и на тесте от числа поданных семплов на вход.
Кривые обучения могут выглядеть следующим образом (код для отрисовки таких кривых можно найти в документации библиотеки sklearn):
Если графики подсказывают, что качество модели по валидационным метрикам продолжает расти, имеет смысл добавить новые данные.
На картинке выше приведены кривые обучения двух моделей на одном и том же датасете. Модель слева показала итоговые результаты явно хуже, чем у модели справа. К тому же график качества на валидации у модели слева близок к плато, хотя и продолжает расти, — а качество модели справа могло бы ещё вырасти при добавлении дополнительных семплов (качество на трейне константно высокое, а на валидации возрастает).
Стратификация (stratification)
При простом случайном разделении на тренировочное и тестовое множества (как в примерах выше) может случиться так, что их распределения окажутся не такими, как у всего исходного множества. Проиллюстрируем такую ситуацию на примере случайного разбиения датасета с информацией по трём видам ирисов Iris на трейн и тест.
Распределение классов в данном датасете равномерное:
Случайное разбиение, в котором две трети цветов (100) отправились в трейн, а оставшаяся треть (50) отправилась в тест, может выглядеть, например, так:
- трейн: (распределение );
- тест: (распределение ).
Если распределение цветов в исходном датасете отражает то, что в природе они встречаются одинаково часто, то мы только что получили два новых датасета, в которых распределение цветов не соответствует природному. Распределения обоих датасетов вышли не только несбалансированными, но ещё и разными: самый частый класс в трейне соответствует наименее частому классу в тесте.
На помощь в такой ситуации может прийти стратификация — разбиение на трейн и тест, сохраняющее исходное соотношение классов. В библиотеке sklearn такое разбиение можно получить с помощью параметра stratify:
import numpy as np
from sklearn.model_selection import train_test_split
X, y = np.arange(1000).reshape((500, 2)), np.random.choice(4, size=500, p=[0.1, 0.2, 0.3, 0.4])
X_train, X_test, y_train, y_test = train_test_split(
X, y,
test_size=0.2,
random_state=42,
stratify=y
)
На достаточно больших датасетах (порядка хотя бы 10 тысяч семплов) со сбалансированными классами можно не сильно беспокоиться об описанной выше проблеме и использовать обычное случайное разбиение.
Но если у вас очень несбалансированные данные, в которых один класс встречается намного чаще другого (как, например, в задачах фильтрации спама или сегментации осадков на спутниковых снимках), стратификация может существенно помочь.
k-Fold
Метод k-Fold чаще всего имеют в виду, когда говорят о кросс-валидации. Он является обобщением метода hold-out и представляет из себя следующий алгоритм:
- Фиксируется некоторое целое число (обычно от 5 до 10), меньше числа семплов в датасете.
- Датасет разбивается на одинаковых частей (в последней части может быть меньше семплов, чем в остальных). Эти части называются фолдами.
- Далее происходит итераций, во время каждой из которых один фолд выступает в роли тестового множества, а объединение остальных — в роли тренировочного. Модель учится на фолде и тестируется на оставшемся.
- Финальный скор модели либо получается усреднением получившихся тестовых результатов, либо измеряется на отложенном тестовом множестве, не участвовавшем в кросс-валидации.
Этот метод есть в sklearn:
import numpy as np
from sklearn.model_selection import KFold
X = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]])
y = np.array([1, 2, 3, 4])
kf = KFold(n_splits=2)
for train_index, test_index in kf.split(X):
print("TRAIN:", train_index, "TEST:", test_index)
X_train, X_test = X[train_index], X[test_index]
y_train, y_test = y[train_index], y[test_index]
'''
result:
TRAIN: [2 3] TEST: [0 1]
TRAIN: [0 1] TEST: [2 3]
'''
В коде выше получилось два фолда: в первый вошли объекты с индексами 2 и 3, во второй — объекты с индексами 0 и 1. На первой итерации алгоритма тренировочным будет фолд с индексами 2 и 3, а на второй — фолд с индексами 0 и 1. В sklearn есть также метод cross_val_score, который принимает на вход классификатор, данные и способ разбиения данных (либо число фолдов) и возвращает результаты кросс-валидации:
from sklearn.model_selection import cross_val_score
clf = svm.SVC(kernel='linear', C=1, random_state=42)
scores = cross_val_score(clf, X, y, cv=5)
print(scores)
'''
result:
array([0.96..., 1. , 0.96..., 0.96..., 1. ])
'''
Возникает вопрос: какую модель брать для сравнения с остальными на отложенном тестовом множестве (если оно у вас есть) либо для итогового использования в задаче? После применения метода k-Fold для одной модели у вас на руках останется экземпляров (инстансов) этой модели, обученных на разных подмножествах трейна. Возможные варианты:
- делать предсказание с помощью усреднения предсказаний этих инстансов;
- из этих инстансов выбрать тот, который набрал лучший скор на своём тестовом фолде, и применять дальше его;
- заново обучить модель уже на всех фолдах и делать предсказания уже этой моделью.
Выбирать, какой способ лучше, нужно в зависимости от конкретной задачи и вычислительных возможностей.
Метод k-Fold даёт более надёжную оценку качества модели, чем hold-out, так как обучение и тест модели происходят на разных подмножествах исходного датасета. Однако проведение итераций обучения и теста может быть вычислительно затратным, и поэтому метод обычно применяют либо когда данных мало, либо когда вычислительных ресурсов достаточно, чтобы проводить все итераций параллельно.
В реальных задачах данных зачастую достаточно много для того, чтобы hold-out давал хорошую оценку качества модели, поэтому k-Fold в больших задачах применяется не очень часто.
Leave-one-out
Метод leave-one-out (LOO) — частный случай метода k-Fold: в нём каждый фолд состоит ровно из одного семпла. LOO тоже есть в библиотеке sklearn:
import numpy as np
from sklearn.model_selection import LeaveOneOut
X = np.array([[1, 2], [3, 4], [5, 6]])
y = np.array([1, 2, 3])
loo = LeaveOneOut()
for train_index, test_index in loo.split(X):
print("TRAIN:", train_index, "TEST:", test_index)
X_train, X_test = X[train_index], X[test_index]
y_train, y_test = y[train_index], y[test_index]
'''
result:
TRAIN: [1 2] TEST: [0]
TRAIN: [0 2] TEST: [1]
TRAIN: [0 1] TEST: [2]
'''
Этот метод может понадобиться в случае, если у вас очень мало данных (например, в задаче сегментации клеток на изображениях с оптического микроскопа) — и вы хотите использовать максимальное их количество для обучения модели.
Для валидации на каждой итерации методу требуется всего один семпл, однако и итераций будет столько, сколько семплов в данных, поэтому метод неприменим для средних и больших задач.
Stratified k-Fold
Метод stratified k-Fold — это метод k-Fold, использующий стратификацию при разбиении на фолды: каждый фолд содержит примерно такое же соотношение классов, как и всё исходное множество. Такой подход может потребоваться в случае, например, очень несбалансированного соотношения классов, когда при обычном случайном разбиении некоторые фолды могут либо вообще не содержать семплов каких-то классов, либо содержать их слишком мало. Этот метод также представлен в sklearn:
import numpy as np
from sklearn.model_selection import StratifiedKFold
X = np.array([[1, 2], [3, 4], [1, 2], [3, 4]])
y = np.array([0, 0, 1, 1])
skf = StratifiedKFold(n_splits=2)
for train_index, test_index in skf.split(X, y):
print("TRAIN:", train_index, "TEST:", test_index)
X_train, X_test = X[train_index], X[test_index]
y_train, y_test = y[train_index], y[test_index]
'''
result:
TRAIN: [1 3] TEST: [0 2]
TRAIN: [0 2] TEST: [1 3]
'''
Полезные ссылки
- Гайд от Google о приёмах работы с несбалансированными наборами данных.
- Отличный блог-пост от Neptune про различные методы кросс-валидации.
- Большая статья-обзор про методы сравнения моделей и оценки их качества.
- Секция Model Selection от разработчиков библиотеки&
sklearn.
Когда стоит заподозрить, что оценка качества модели завышена
Ваша модель показала очень высокое качество на тестовых данных, вы радостно откидываетесь на спинку кресла и достаёте шампанское… Или пока рано? Перед тем как сообщить коллегам о своих высоких результатах, проверьте, что вы не допустили какую-то из этих ошибок:
- Не перемешали данные (вспоминаем пример выше с тензорбордом курильщика).
- Подбирали гиперпараметры на тестовом множестве и на нём же оценивали качество модели.
- У вас в данных есть фича, которая в некотором смысле является «прокси» к таргету (англ. proxy for the target). Это такая фича, которая почти равна таргету, хотя формально им не является, и так же, как и таргет, не будет доступна на момент реального применения модели.
- Провели feature engineering на всём датасете, а не только на трейне. Например, вы строили TF-IDF фичи или bag-of-words на всех данных, а не только на трейне, тем самым заложив в свои тренировочные данные информацию о тестовых данных.
- Применили стандартизацию данных на всём датасете, а не только на трейне. Например, в случае StandardScaler тестовое множество повлияет на используемые этим методом оценки среднего и стандартного отклонения.
- Смешали трейн с тестом. Этот пункт может звучать очень банально, но на практике часто оказывается, что правильно разделить данные на тренировочные и тестовые — это сложно, даже с учётом всех описанных выше техник.
Пример «прокси» к таргету
Предположим, вы хотите предсказывать, сколько будут зарабатывать выпускники разных вузов с разных факультетов через 10 лет после выпуска.
Допустим, у вас есть разнообразные исторические данные о прошлых выпускниках (какие вуз и школу оканчивали, какие факультеты, в каком городе и так далее). В датасете много колонок, и есть искушение особенно не вглядываться в каждую из них, а просто разбить данные на трейн и тест и отправить в модель.
Но потом вдруг обнаруживается, что у вас всё это время имелась колонка «Доход через пять лет после выпуска», которая явно скоррелирована с таргетом и является важной для вашей модели, но на момент реального применения модели этой информации у вас не будет.
Соответственно, наличием этой колонки во многом и объяснялся высокий скор вашей модели. Мораль: всегда внимательно изучайте свои данные перед обучением моделей.
Примеры подмешивания тестовых данных в тренировочные
- Ваши данные зависят от времени, а вы при разбиении на трейн и тест этого не учли. Например, вы применили обычный random split при работе с временными рядами, передав тем самым вашей модели информацию из будущего. Или вы предсказываете погоду на несколько часов вперёд, а у вас данные одного и того же дня находятся и в трейне, и в тесте.
- У вас есть датасет с картинками, и вы решили увеличить количество семплов в нём с помощью аугментаций (примерами аугментаций могут служить симметричные отражения, повороты, растяжения). При этом вы взяли весь датасет, применили к нему аугментации и только после этого разделили на трейн и тест. В таком случае преобразования какой-то одной картинки могут попасть в оба множества и вы получите пересечение трейна и теста.
- Вы решаете задачу рекомендации статей или постов пользователям на основе их комментариев и прочтений, при этом в трейне и тесте у вас одни и те же пользователи.
- Вы решаете какую-то задачу, где происходит работа с видеоданными. Например, распознаёте движение по видео или предсказываете фамилию актёра, попавшего в кадр. При этом в трейн и тест у вас попадают различные кадры из одного и того же видео.
- У вас есть спутниковые снимки, и вы хотите по ним предсказывать рельеф местности. При этом у вас в трейне и тесте есть кропы снимков над одними и теми же географическими координатами (хоть и в разное время).
- Вы обучаете голосового ассистента в звуковом потоке распознавать момент, когда к нему обращаются (например, «Слушай, Алиса», «Ok, Google»). При этом у вас в трейне и тесте одни и те же люди. Это, на первый взгляд, не очень страшная проблема, но на самом деле достаточно большая нейросеть может запомнить интонации и манеру речи конкретного человека и будет использовать эти сведения для тестовых записей с этим человеком. При этом с новыми людьми распознавание будет работать намного хуже.
- Вы хотите расширить тренировочный датасет дополнительными данными из другого датасета, но при этом оказывается, что другой датасет содержит в себе часть тестового множества вашего исходного датасета. Например, есть два публичных датасета: ImageNet LSVRC 2015, в котором 1000 классов и чуть больше миллиона изображений, и ImageNet, в котором 21 тысяча классов и чуть больше 14 миллионов изображений. При этом первый полностью содержится во втором, поэтому использование ImageNet для расширения обучающей выборки из ImageNet LSVRC 2015 может закончиться тем, что в трейне окажутся примеры из тестового множества, сформированного из ImageNet LSVRC 2015.
Подумайте над задачами
Задача 1. Допустим, вы должны обучить модель, которая определяет тему новостной статьи по её тексту. Если отсортировать статьи по дате их публикации, то ваши данные могут выглядеть, например, так:
Здесь цвет фигуры соответствуют новости, которой посвящена статья. Почему случайное разбиение данных на трейн и тест может привести к проблемам в этой задаче?
Ответ (не открывайте сразу, сначала подумайте сами!)
На самом деле, новостные статьи с одной и той же тематикой появляются кластерами во времени, так как статьи о новом событии выходят, как правило, порциями в то же время, когда произошло событие. Если разбить данные случайно, то тренировочное и тестовое множества с большой вероятностью будут содержать объекты из близких по времени кластеров:
Оценка качества модели по такому тестовому множеству может оказаться завышенной, потому что модель при обучении видела статьи, очень похожие на те, что были в тесте. Например, если 01.05.2021 проходил футбольный матч и в трейне оказалась статья об этом, то модели будет легче правильно выставить тему «футбол» на тестовой статье, если тестовая статья была взята из того же дня.
Поэтому при разбиении на трейн и тест важно проследить, чтобы статьи были из разных временных промежутков, но при этом набор тематик в трейне и тесте совпадал, ведь модели нужно предсказать тему из определённого набора.
Простым решением будет при разбиении на трейн и тест учитывать время, когда была опубликована статья:
Тут нужно учитывать, что в реальности кластеры историй по времени выражены не столь чётко и могут пересекаться. Поэтому если трейн и тест расположены слишком близко друг к другу по времени, то они могут пересечься. Это не так плохо с учётом того, что новости о каких-то событиях могут продолжать выходить в течение растянутого промежутка времени. Но если хочется избежать такой ситуации, то можно оставить между трейном и тестом некоторый временной зазор: тренироваться, например, на апрельских публикациях, а тестироваться на второй неделе мая, оставив таким образом недельный промежуток между двумя множествами.
Задача 2. Существует такая задача, как прогнозирование временных рядов. Временной ряд — любая численная последовательность, у которой индекс — это время. На практике задача прогнозирования часто возникает в форме «Что будет с показателями нашего продукта в ближайший день, месяц или год?». При этом имеются какие-то исторические данные этих показателей за предыдущее время, которые можно визуализировать в виде графика по времени:
Как бы вы проводили кросс-валидацию для такой задачи?
Ответ (не открывайте сразу, сначала подумайте сами!)
Кросс-валидация моделей для такой задачи осложняется тем, что данные не должны пересекаться по времени: тренировочные данные должны идти до валидационных, а валидационные — до тестовых. С учётом этих особенностей фолды в кросс-валидации для временных рядов располагаются вдоль временной оси так, как показано на картинке:
В sklearn реализована такая схема кросс-валидации:
import numpy as np
from sklearn.model_selection import TimeSeriesSplit
X = np.array([[1, 2], [3, 4], [1, 2], [3, 4], [1, 2], [3, 4]])
y = np.array([1, 2, 3, 4, 5, 6])
tscv = TimeSeriesSplit()
print(tscv)
for train_index, test_index in tscv.split(X):
print("TRAIN:", train_index, "TEST:", test_index)
X_train, X_test = X[train_index], X[test_index]
y_train, y_test = y[train_index], y[test_index]
'''
result:
TRAIN: [0] TEST: [1]
TRAIN: [0 1] TEST: [2]
TRAIN: [0 1 2] TEST: [3]
TRAIN: [0 1 2 3] TEST: [4]
TRAIN: [0 1 2 3 4] TEST: [5]
'''
Полезные ссылки
- Классный пример, когда случайное разбиение данных может испортить ML-модель.
- Блог-пост про различные «умные» способы получить завышенные оценки качества моделей.
- Статья про методики разбиения данных в рекомендательных системах.
- Статья про временные ряды из курса «Открытый курс машинного обучения» от ODS.
- Библиотека Prophet от Facebook для прогнозирования временных рядов, у которой есть своя имплементация кросс-валидации с дополнительными фичами (таблицы с результатами кросс-валидации, красивые графики).
- Статья с теоретическим обоснованием метода Prophet.
Заключение
Мы рассмотрели методы оценки качества моделей машинного обучения, уделив особое внимание предотвращению ошибок при разделении данных и выборе стратегий кросс-валидации.
Основные методы кросс-валидации:
- Hold-out: простое разделение на тренировочное, тестовое и валидационное подмножества. Критически важно перемешивать данные, чтобы предотвратить зависимость качества обучения от порядка данных. Валидационное множество используется для подбора гиперпараметров, а финальная оценка — на тесте.
- k-Fold: датасет делится на частей, модель обучается и тестируется раз. Даёт более надёжную оценку, чем hold-out, но требует больше ресурсов. Подходит для небольших данных.
— Stratified k-Fold: сохраняет распределение классов в фолдах, важно для несбалансированных данных (например, спам-фильтрация).
— Leave-one-out (LOO): частный случай k-Fold, где каждый фолд — один образец. Применяется при очень малом количестве данных.
Ключевые ошибки, которые приводят к завышенной оценке качества:
- Смешивание****трейна и теста: использование тестовых данных при обучении, например неправильная работа с временной структурой. Тогда случайное разбиение временных рядов или новостных данных приведёт к утечке информации из будущего.
- Feature engineering на всём датасете: например, TF-IDF на всех данных, а не только на трейне, нормализация данных с помощью статистик, посчитанных до разделения на трейн и тест.
- Подбор гиперпараметров на тесте: таким образом тестовые данные неявно участвуют в обучении.
- «Прокси»-фичи: наличие признаков, коррелирующих с таргетом (например, данные о доходе через пять лет для прогноза дохода через 10 лет).
Рекомендации:
- Внимательно анализируйте структуру данных при разделении на****трейн и тест. Для временных рядов разделяйте прошлое и будущее (например, используя реализацию TimeSeriesSplit из библиотеки
sklearn), для геоданных — исключайте пересечение локаций, в рекомендательных системах убедитесь, что одни пользователи или объекты не дублируются в разных множествах. - Создавайте несколько тестовых наборов под специфические бизнес-сценарии. Например, в маркетплейсе: если обычно вы фокусируетесь на общих рекомендациях, то перед Новым годом вам может понадобиться отдельный тест для оценки точности предсказания сезонного (новогоднего) ассортимента. Другой пример — синтез речи. Для аудиокниг требуются эмоциональные и разнообразные интонации, а для голосового ассистента — ровные и предсказуемые. В этом случае, чтобы адекватно оценить качество в разных сферах, также потребуются разные тестовые сеты.
- Для несбалансированных данных применяйте стратификацию (stratified k-Fold).
- Проверяйте утечки данных: feature engineering и нормализация должны проводиться только на трейне.
- Перед внедрением модели обучайте её на всех данных, но предварительно оценивайте качество через кросс-валидацию.
Итог:
Для надёжной оценки требуется строгое разделение данных, учёт их структуры (временных и пространственных зависимостей) и исключение неявного использования тестовых данных на любом этапе разработки модели. Кросс-валидация помогает минимизировать риски, но должна применяться с пониманием специфики задачи. Для критичных бизнес-кейсов рекомендуется создавать специализированные тестовые наборы данных.
В следующей статье мы подробнее остановимся на методах подбора гиперпараметров. До сих пор мы упоминали гиперпараметры только в контексте оценки качества моделей и как один из случаев, когда полезно применять кросс-валидацию. Там же вас ждёт объёмная практическая работа.