Разделение данных для машинного обучения: методы и код Python

Пройдите тест, узнайте какой профессии подходите
Сколько вам лет
0%
До 18
От 18 до 24
От 25 до 34
От 35 до 44
От 45 до 49
От 50 до 54
Больше 55

Для кого эта статья:

  • Специалисты и практики в области машинного обучения и 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 — настоящая рабочая лошадка в арсенале любого специалиста по машинному обучению. Этот метод обеспечивает быстрое и эффективное разделение данных, являясь первым шагом в подготовке модели к обучению.

Основная синтаксическая структура функции выглядит следующим образом:

Python
Скопировать код
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 выполняет разделение случайным образом (если не указано иное). Это подходит для задач, где порядок данных не имеет значения, но может создать проблемы при работе с временными рядами или последовательными данными.

Рассмотрим расширенный пример использования для задачи классификации:

Python
Скопировать код
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}')

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

Python
Скопировать код
# Отделяем тестовую выборку
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 кросс-валидация. Принцип её работы следующий:

  1. Данные разделяются на k равных частей (фолдов)
  2. Модель обучается k раз, каждый раз используя k-1 фолд для обучения и оставшийся фолд для тестирования
  3. Результаты по всем k итерациям усредняются, давая итоговую оценку модели

Базовая реализация кросс-валидации в sklearn выглядит так:

Python
Скопировать код
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 для классификационной задачи с несбалансированными классами:

Python
Скопировать код
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:

Python
Скопировать код
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): кросс-валидация предпочтительнее простого разделения

Рассмотрим полный пример рабочего процесса, включающий разделение данных, обучение модели и оценку результатов:

Python
Скопировать код
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):

Python
Скопировать код
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 для предотвращения утечки по группам:

Python
Скопировать код
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")

При разработке моделей глубокого обучения рекомендуется использовать генераторы данных для эффективной работы с большими объемами информации:

Python
Скопировать код
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 и более сложными методами вроде стратифицированной кросс-валидации должен опираться на специфику ваших данных и решаемой задачи. Помните: модель, которая блестяще работает на тренировочных данных, но проваливается на реальных примерах, не имеет практической ценности. Используйте описанные методы и код как часть вашей ежедневной практики, и ваши модели будут не просто точными на бумаге, но и надежными в реальном мире.

Читайте также

Проверь как ты усвоил материалы статьи
Пройди тест и узнай насколько ты лучше других читателей
Почему важно разделять данные на тренировочные и тестовые?
1 / 5

Загрузка...