Разделение данных для машинного обучения: методы и код Python
Для кого эта статья:
- Специалисты и практики в области машинного обучения и Data Science
- Студенты и обучающиеся, желающие углубить свои знания в области Python-разработки и анализа данных
Разработчики, стремящиеся улучшить качество своих моделей и их производительность в реальных условиях
Если вы когда-нибудь обучали модель машинного обучения, которая прекрасно работала на тренировочных данных, но терпела фиаско на реальных примерах, то вы знакомы с болезненным опытом переобучения. Этой критической проблемы можно избежать, правильно разделяя данные на обучающие и тестовые наборы. Разделение данных — фундаментальный этап подготовки данных для обучения искусственного интеллекта, без которого невозможно создать действительно надежные и обобщающие модели. Давайте разберёмся, как это делать эффективно 🔍 и каким кодом реализовать.
Если вы хотите глубоко понять не только разделение данных, но и все аспекты Python-разработки для анализа данных и создания искусственного интеллекта, обратите внимание на курс Обучение Python-разработке от Skypro. Этот комплексный курс позволит вам освоить все этапы работы с данными — от их подготовки до построения и оптимизации моделей. Студенты курса уже через 3 месяца создают полнофункциональные проекты машинного обучения и Data Science с правильной валидацией результатов!
Почему разделение данных критично для качественного ИИ
Представьте, что вы готовитесь к важному экзамену. Вы можете либо изучать разнообразные материалы по предмету, либо просто заучить ответы на конкретные билеты. Первый подход даст вам настоящее понимание предмета, второй — лишь иллюзию знаний. То же самое происходит с моделями машинного обучения.
При обучении моделей без правильного разделения данных возникают три фундаментальные проблемы:
- Переобучение (overfitting) — модель "заучивает" тренировочные данные, вместо того чтобы выявлять общие закономерности
- Необъективная оценка — без отдельного тестового набора невозможно честно оценить производительность модели
- Ложная уверенность — высокая точность на тренировочных данных создает иллюзию готовой к внедрению модели
Алексей Морозов, ведущий специалист по машинному обучению
Однажды мы создавали модель прогнозирования оттока клиентов для крупного банка. Первая версия модели показывала фантастическую точность в 98% на наших данных. Я был в восторге, пока не внедрил решение в боевую среду, где точность упала до 61%. Оказалось, мы допустили классическую ошибку — не разделили данные должным образом и не учли временную структуру данных. Модель просто "запомнила" исторические паттерны вместо выявления признаков оттока. После правильного разделения с учётом временного аспекта и применения кросс-валидации, мы получили модель с точностью 82% как на тестовых, так и на реальных данных. Этот случай навсегда изменил мой подход к валидации моделей.
Статистические данные подтверждают значимость проблемы: согласно исследованию Kaggle, 78% практикующих специалистов по данным считают ошибки в разделении выборок одной из главных причин провала моделей на практике 📊.
| Проблема | Последствия | Частота возникновения |
|---|---|---|
| Переобучение | Плохая обобщающая способность модели | 68% проектов |
| Утечка данных | Завышенные метрики, ложные корреляции | 43% проектов |
| Несбалансированное разделение | Модель работает только с определёнными типами данных | 37% проектов |
Разделение данных — это не просто технический этап подготовки данных для обучения искусственного интеллекта, а стратегическое решение, определяющее успех всего проекта. Выбор метода разделения зависит от типа данных, размера датасета и специфики задачи.

Основные методы сплита данных в машинном обучении
Существует несколько подходов к разделению данных, каждый из которых имеет свои преимущества и области применения. Выбор метода зависит от размера вашего датасета, типа данных и конкретной задачи машинного обучения.
Рассмотрим основные методы разделения данных, которые используются в современных проектах Data Science:
- Простое разделение (Simple Split): базовый метод, при котором данные делятся в определенной пропорции (обычно 70-80% на обучение и 20-30% на тестирование)
- Стратифицированное разделение (Stratified Split): сохраняет пропорцию классов или распределение целевой переменной в обеих выборках
- Кросс-валидация (Cross-Validation): разбиение данных на k частей, с поочередным использованием каждой части в качестве тестовой выборки
- Временное разделение (Time-Based Split): учитывает временную структуру данных, используя более ранние данные для обучения и более поздние для тестирования
- Групповое разделение (Group-Based Split): разделение, учитывающее групповую структуру данных (например, все транзакции одного клиента должны попасть либо в тренировочную, либо в тестовую выборку)
Каждый метод имеет свои особенности и ограничения, которые важно учитывать при выборе 🔄:
| Метод разделения | Когда использовать | Преимущества | Недостатки |
|---|---|---|---|
| Простое разделение | Большие датасеты с равномерным распределением классов | Простота, низкие вычислительные затраты | Риск несбалансированного распределения классов |
| Стратифицированное разделение | Несбалансированные данные, малые выборки | Сохранение распределения целевой переменной | Сложнее реализовать для многомерных задач |
| K-fold кросс-валидация | Малые и средние датасеты, проверка устойчивости модели | Надежная оценка обобщающей способности | Высокие вычислительные затраты |
| Временное разделение | Временные ряды, последовательные данные | Реалистичная оценка предсказательной способности | Требует учета сезонности и трендов |
В практических задачах часто используется комбинация этих методов. Например, стратифицированная кросс-валидация объединяет преимущества сохранения распределения классов и многократной проверки на разных подвыборках.
Мария Соколова, руководитель направления Data Science
Работая над проектом прогнозирования спроса на товары в розничной сети, мы столкнулись с необходимостью учитывать сезонность. Первоначально мы использовали стандартное разделение traintestsplit, и модель показывала хорошие результаты на тестовой выборке, но полностью проваливалась в реальных прогнозах. Проблема была в том, что случайное разделение смешивало сезоны. После перехода на временное разделение, где для обучения использовались данные за первые три квартала, а для тестирования — за последний, качество прогнозов драматически улучшилось. Более того, мы дополнительно внедрили стратифицированный подход по категориям товаров, чтобы убедиться, что и редкие категории корректно представлены в обеих выборках. Эта комбинация методов разделения данных увеличила точность прогнозов на 34% и сэкономила компании миллионы на управлении складскими запасами.
Train
Функция train_test_split из библиотеки sklearn — настоящая рабочая лошадка в арсенале любого специалиста по машинному обучению. Этот метод обеспечивает быстрое и эффективное разделение данных, являясь первым шагом в подготовке модели к обучению.
Основная синтаксическая структура функции выглядит следующим образом:
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=42, stratify=y
)
Этот простой на вид код скрывает за собой несколько важных параметров, которые могут кардинально повлиять на качество модели:
- test_size: определяет долю данных для тестовой выборки (обычно от 0.2 до 0.3)
- random_state: гарантирует воспроизводимость результатов при повторных запусках
- stratify: обеспечивает сохранение пропорции классов в обеих выборках
- shuffle: контролирует перемешивание данных перед разделением (по умолчанию True)
Параметр stratify особенно важен при работе с несбалансированными данными. Представьте датасет медицинской диагностики, где только у 5% пациентов обнаружено заболевание. Если просто случайно разделить данные, можно получить тестовую выборку с совершенно иным распределением, что приведет к искаженной оценке модели 🩺.
Важно понимать, что train_test_split выполняет разделение случайным образом (если не указано иное). Это подходит для задач, где порядок данных не имеет значения, но может создать проблемы при работе с временными рядами или последовательными данными.
Рассмотрим расширенный пример использования для задачи классификации:
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score
# Загрузка данных
data = pd.read_csv('customer_data.csv')
X = data.drop('churn', axis=1)
y = data['churn']
# Базовое разделение
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.25, random_state=42
)
# Обучение модели
model = RandomForestClassifier(n_estimators=100)
model.fit(X_train, y_train)
# Оценка на тестовой выборке
predictions = model.predict(X_test)
accuracy = accuracy_score(y_test, predictions)
print(f'Точность модели: {accuracy:.4f}')
Для продвинутых случаев можно создать несколько уровней разделения. Например, вы можете сначала отделить валидационную выборку от обучающей, чтобы настроить гиперпараметры модели:
# Отделяем тестовую выборку
X_temp, X_test, y_temp, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
# Разделяем оставшиеся данные на обучающую и валидационную выборки
X_train, X_val, y_train, y_val = train_test_split(
X_temp, y_temp, test_size=0.25, random_state=42, stratify=y_temp
)
# Теперь у нас есть три выборки: X_train/y_train, X_val/y_val, X_test/y_test
При работе с большими датасетами обратите внимание на параметр shuffle=True, который может потребовать значительных ресурсов памяти. В таких случаях стоит рассмотреть поэтапную обработку данных или специализированные инструменты для работы с большими объемами информации.
Кросс-валидация: повышаем надежность модели
Кросс-валидация — это мощный инструмент, который выводит оценку качества моделей на новый уровень, особенно при работе с ограниченными объемами данных. В отличие от простого разделения train_test_split, кросс-валидация позволяет использовать все доступные данные как для обучения, так и для тестирования, что значительно повышает надежность оценки производительности модели.
Наиболее распространенный вариант — k-fold кросс-валидация. Принцип её работы следующий:
- Данные разделяются на k равных частей (фолдов)
- Модель обучается k раз, каждый раз используя k-1 фолд для обучения и оставшийся фолд для тестирования
- Результаты по всем k итерациям усредняются, давая итоговую оценку модели
Базовая реализация кросс-валидации в sklearn выглядит так:
from sklearn.model_selection import cross_val_score
from sklearn.ensemble import RandomForestClassifier
model = RandomForestClassifier(n_estimators=100)
scores = cross_val_score(model, X, y, cv=5, scoring='accuracy')
print(f"Средняя точность: {scores.mean():.4f}")
print(f"Стандартное отклонение: {scores.std():.4f}")
Для более продвинутых сценариев sklearn предлагает несколько специализированных классов кросс-валидации:
- KFold: классическая k-fold кросс-валидация
- StratifiedKFold: сохраняет распределение классов в каждом фолде
- GroupKFold: учитывает групповую структуру данных
- TimeSeriesSplit: специально разработан для временных рядов
- RepeatedKFold: многократно повторяет KFold с разными разбиениями
Рассмотрим пример использования StratifiedKFold для классификационной задачи с несбалансированными классами:
from sklearn.model_selection import StratifiedKFold
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score, f1_score
import numpy as np
# Инициализируем кросс-валидацию
skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
# Подготовим списки для хранения результатов
accuracy_scores = []
f1_scores = []
# Проходим по всем фолдам
for train_index, test_index in skf.split(X, y):
X_train, X_test = X.iloc[train_index], X.iloc[test_index]
y_train, y_test = y.iloc[train_index], y.iloc[test_index]
# Обучаем модель
model = RandomForestClassifier(n_estimators=100)
model.fit(X_train, y_train)
# Предсказываем и оцениваем
y_pred = model.predict(X_test)
accuracy_scores.append(accuracy_score(y_test, y_pred))
f1_scores.append(f1_score(y_test, y_pred, average='weighted'))
print(f"Средняя точность: {np.mean(accuracy_scores):.4f}")
print(f"Среднее F1: {np.mean(f1_scores):.4f}")
При работе со временными рядами важно учитывать их последовательный характер, используя TimeSeriesSplit:
from sklearn.model_selection import TimeSeriesSplit
import matplotlib.pyplot as plt
tscv = TimeSeriesSplit(n_splits=5)
# Визуализация разбиения временного ряда
fig, ax = plt.subplots(figsize=(10, 8))
for i, (train_index, test_index) in enumerate(tscv.split(X)):
ax.scatter(test_index, [i] * len(test_index), c='red', marker='_', s=60, label='Тест' if i == 0 else "")
ax.scatter(train_index, [i] * len(train_index), c='blue', marker='_', s=60, label='Обучение' if i == 0 else "")
ax.set_title('TimeSeriesSplit')
ax.legend()
plt.show()
| Метод кросс-валидации | Преимущества | Недостатки | Лучшие сценарии использования |
|---|---|---|---|
| KFold | Простота, универсальность | Не учитывает структуру данных | Сбалансированные датасеты среднего размера |
| StratifiedKFold | Сохраняет распределение классов | Применим только для классификации | Несбалансированные данные с редкими классами |
| TimeSeriesSplit | Учитывает временную структуру | Меньше данных для первых итераций обучения | Финансовые прогнозы, временные ряды |
| GroupKFold | Предотвращает "утечку" информации по группам | Требует дополнительного параметра группировки | Медицинские исследования, данные по клиентам |
Важно помнить, что кросс-валидация требует больше вычислительных ресурсов, чем простое разделение, поскольку модель обучается k раз. Однако этот дополнительный расход с лихвой компенсируется более надежной оценкой качества модели и снижением риска переобучения 📈.
Практический код и оптимальные пропорции разделения
Эффективное разделение данных — это не просто применение правильного алгоритма, но и выбор оптимальных пропорций и учет специфики конкретной задачи. Рассмотрим практические рекомендации и готовые решения для разных сценариев.
Оптимальные пропорции разделения зависят от размера датасета и типа задачи:
- Для больших датасетов (>100k записей): 80% на обучение, 10% на валидацию, 10% на тестирование
- Для средних датасетов (10k-100k): 70% на обучение, 15% на валидацию, 15% на тестирование
- Для малых датасетов (<10k): кросс-валидация предпочтительнее простого разделения
Рассмотрим полный пример рабочего процесса, включающий разделение данных, обучение модели и оценку результатов:
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split, cross_val_score, StratifiedKFold
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report, confusion_matrix
import matplotlib.pyplot as plt
import seaborn as sns
# Загрузка данных
data = pd.read_csv('customer_data.csv')
X = data.drop('target', axis=1)
y = data['target']
# Шаг 1: Разделение на тренировочную и тестовую выборки с учетом баланса классов
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
# Шаг 2: Предобработка данных (важно применять те же преобразования к тестовым данным)
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test) # Используем параметры, полученные на обучающей выборке
# Шаг 3: Кросс-валидация для оценки модели и подбора гиперпараметров
skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
model = RandomForestClassifier(n_estimators=100, random_state=42)
# Проводим кросс-валидацию
cv_scores = cross_val_score(model, X_train_scaled, y_train, cv=skf, scoring='accuracy')
print(f"Результаты кросс-валидации: {cv_scores}")
print(f"Средняя точность: {cv_scores.mean():.4f} ± {cv_scores.std():.4f}")
# Шаг 4: Обучение финальной модели на всех тренировочных данных
model.fit(X_train_scaled, y_train)
# Шаг 5: Оценка на тестовой выборке
y_pred = model.predict(X_test_scaled)
print("\nОтчет по классификации:")
print(classification_report(y_test, y_pred))
# Шаг 6: Визуализация матрицы ошибок
cm = confusion_matrix(y_test, y_pred)
plt.figure(figsize=(8, 6))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues')
plt.xlabel('Предсказанные классы')
plt.ylabel('Истинные классы')
plt.title('Матрица ошибок')
plt.show()
Для работы с временными рядами рекомендуется использовать скользящее окно (rolling window) или расширяющееся окно (expanding window):
from sklearn.model_selection import TimeSeriesSplit
import numpy as np
# Создаем временной ряд (например, ежедневные данные)
X = np.array(range(1000)).reshape(-1, 1)
y = np.sin(X / 50) + np.random.normal(0, 0.1, size=(1000, 1))
# Инициализируем TimeSeriesSplit с 5 фолдами
tscv = TimeSeriesSplit(n_splits=5)
# Проходим по фолдам и обучаем модель
for train_index, test_index in tscv.split(X):
X_train, X_test = X[train_index], X[test_index]
y_train, y_test = y[train_index], y[test_index]
print(f"Обучение: {len(train_index)}, Тест: {len(test_index)}")
# Здесь вы бы обучали и оценивали вашу модель
При работе со сложными данными важно учитывать не только разделение, но и возможность утечки данных (data leakage). Рассмотрим практику с использованием GroupKFold для предотвращения утечки по группам:
from sklearn.model_selection import GroupKFold
# Предположим, у нас есть группы (например, ID пациентов)
groups = np.array([1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4])
X = np.random.rand(len(groups), 5)
y = np.random.randint(0, 2, size=len(groups))
# Создаем GroupKFold
gkf = GroupKFold(n_splits=3)
# Проходим по фолдам
for train_index, test_index in gkf.split(X, y, groups=groups):
X_train, X_test = X[train_index], X[test_index]
y_train, y_test = y[train_index], y[test_index]
groups_train, groups_test = groups[train_index], groups[test_index]
# Проверяем, что группы не пересекаются
print(f"Группы в обучении: {set(groups_train)}")
print(f"Группы в тесте: {set(groups_test)}")
print(f"Пересечение групп: {set(groups_train) & set(groups_test)}\n")
При разработке моделей глубокого обучения рекомендуется использовать генераторы данных для эффективной работы с большими объемами информации:
import numpy as np
from tensorflow.keras.utils import Sequence
class DataGenerator(Sequence):
def __init__(self, x_data, y_data, batch_size=32, shuffle=True):
self.x_data = x_data
self.y_data = y_data
self.batch_size = batch_size
self.shuffle = shuffle
self.indexes = np.arange(len(x_data))
if self.shuffle:
np.random.shuffle(self.indexes)
def __len__(self):
return int(np.ceil(len(self.x_data) / self.batch_size))
def __getitem__(self, index):
batch_indexes = self.indexes[index * self.batch_size:(index + 1) * self.batch_size]
X = self.x_data[batch_indexes]
y = self.y_data[batch_indexes]
return X, y
def on_epoch_end(self):
if self.shuffle:
np.random.shuffle(self.indexes)
# Использование генератора с разделением данных
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, stratify=y)
train_generator = DataGenerator(X_train, y_train, batch_size=32)
test_generator = DataGenerator(X_test, y_test, batch_size=32, shuffle=False)
# Теперь вы можете использовать эти генераторы с model.fit()
Важно помнить, что идеальное разделение данных — это баланс между статистической надежностью и практической реализуемостью. Экспериментируйте с разными подходами, но всегда оценивайте модель на независимых тестовых данных, чтобы получить объективное представление о её производительности в реальных условиях 🧪.
Правильное разделение данных — это не просто техническая формальность, а фундаментальный элемент, определяющий успех или провал вашей модели машинного обучения. Выбор между простым
train_test_splitи более сложными методами вроде стратифицированной кросс-валидации должен опираться на специфику ваших данных и решаемой задачи. Помните: модель, которая блестяще работает на тренировочных данных, но проваливается на реальных примерах, не имеет практической ценности. Используйте описанные методы и код как часть вашей ежедневной практики, и ваши модели будут не просто точными на бумаге, но и надежными в реальном мире.
Читайте также
- Нейронные сети: от теории к практике – руководство для начинающих
- Революция финансов: как ИИ трансформирует банкинг и инвестиции
- Топ-10 инструментов для разработки ИИ: что выбрать для проекта
- Нормализация и очистка данных: ключ к точным ML-моделям
- Этические принципы ИИ: проблемы выбора в цифровую эпоху
- Машинное обучение: типы алгоритмов и их применение в аналитике
- Тест Тьюринга: как определение машинного мышления изменило ИИ
- Тест Тьюринга: как отличить искусственный интеллект от человека
- Тест Тьюринга устарел: почему современные ИИ требуют новых методов оценки
- ИИ-революция: как алгоритмы меняют общество и рынок труда


