← Назад к вопросам

Что такое система сплитования?

2.0 Middle🔥 141 комментариев
#A/B-тестирование

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Система сплитования (Splitting): разделение данных в ML

Что такое сплитование (Splitting)

Сплитование — это разделение датасета на несколько частей для обучения и валидации моделей. Это критично для объективной оценки качества модели.

Главная проблема без сплитования

❌ НЕПРАВИЛЬНО: обучать и тестировать на одних данных
model.fit(data, labels)
score = model.score(data, labels)  # 98% accuracy!

Проблема: модель просто запомнила данные (overfitting)
В реальности: на новых данных работает плохо (20% accuracy)

1. Train-Test Split — базовое разделение

from sklearn.model_selection import train_test_split

# Данные
X = [[1,2], [2,3], [3,4], [4,5], [5,6], [6,7]]
y = [0, 0, 1, 1, 1, 0]

# Разделение: 80% train, 20% test
X_train, X_test, y_train, y_test = train_test_split(
    X, y, 
    test_size=0.2,  # 20% на тестирование
    random_state=42  # для воспроизводимости
)

print(f"Train size: {len(X_train)}")  # 4-5 примеров
print(f"Test size: {len(X_test)}")    # 1-2 примера

# Обучение
model = LogisticRegression()
model.fit(X_train, y_train)

# Тестирование (на несвязанных данных)
score = model.score(X_test, y_test)
print(f"Accuracy: {score:.2f}")

Пропорции:

  • 80/20: классический подход
  • 70/30: для больших датасетов
  • 90/10: для очень маленьких данных

2. Train-Validation-Test Split — трёхсторонний

Проблема single split: мы выбираем параметры модели смотря на test set → test set становится "загрязненным"

Решение: три части

from sklearn.model_selection import train_test_split

# Первое разделение
X_train, X_temp, y_train, y_temp = train_test_split(
    X, y,
    test_size=0.4,  # 40% на validation + test
    random_state=42
)

# Второе разделение
X_val, X_test, y_val, y_test = train_test_split(
    X_temp, y_temp,
    test_size=0.5,  # 50% from 40% = 20% final test
    random_state=42
)

print(f"Train: {len(X_train)} ({len(X_train)/len(X):.0%})")
print(f"Validation: {len(X_val)} ({len(X_val)/len(X):.0%})")
print(f"Test: {len(X_test)} ({len(X_test)/len(X):.0%})")

# Train на train
model.fit(X_train, y_train)

# Выбираем параметры по validation
val_score = model.score(X_val, y_val)

# ФИНАЛЬНАЯ оценка по test (только один раз)
test_score = model.score(X_test, y_test)

Пропорции:

  • 60/20/20: классическое
  • 70/15/15: часто
  • 80/10/10: для больших данных

3. K-Fold Cross-Validation — самый надежный

Идея: разделить на K частей, обучать K раз на разных фолдах

from sklearn.model_selection import KFold, cross_val_score

# Создать 5 фолдов
kf = KFold(n_splits=5, shuffle=True, random_state=42)

# Модель
model = LogisticRegression()

# Cross-validation
scores = cross_val_score(model, X, y, cv=kf, scoring='accuracy')

print(f"Scores per fold: {scores}")
# [0.85, 0.90, 0.88, 0.87, 0.89]

print(f"Mean accuracy: {scores.mean():.2f} (+/- {scores.std():.2f})")
# Mean accuracy: 0.88 (+/- 0.02)

Визуализация K-Fold:

Датасет: [A B C D E F G H I J]

Fold 1: Train [B C D E F G H I J], Test [A]
Fold 2: Train [A C D E F G H I J], Test [B]
Fold 3: Train [A B D E F G H I J], Test [C]
Fold 4: Train [A B C E F G H I J], Test [D]
Fold 5: Train [A B C D F G H I J], Test [E]

(Схемой не совсем корректна, но идея верна)

4. Stratified K-Fold — для несбалансированных данных

Проблема обычного K-Fold: в каком-то фолде может быть только класс 1, в другом только класс 0

Решение: сохранить пропорцию классов

from sklearn.model_selection import StratifiedKFold

# Несбалансированные данные: 95% класс 0, 5% класс 1
y = [0]*95 + [1]*5

# Обычный KFold (может быть несбалансированный)
kf = KFold(n_splits=5)
for train_idx, test_idx in kf.split(X):
    print(f"Train: {sum(y[i] for i in train_idx)} ones")
    # Некоторые фолды могут быть 2, другие 3, 1

# Stratified KFold (сохраняет пропорцию 95/5)
skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
for train_idx, test_idx in skf.split(X, y):
    print(f"Train: {sum(y[i] for i in train_idx)} ones")
    # Все фолды будут с примерно одинаковыми пропорциями

5. Time Series Split — для временных рядов

Проблема обычного split: нарушение временной последовательности

❌ НЕПРАВИЛЬНО для временного ряда
Данные: Jan, Feb, Mar, Apr, May, Jun
Тренд split: Train [Jan, Mar, May], Test [Feb, Apr, Jun]
↓
Тестируем на ПРОШЛОМ, обучаемся на БУДУЩЕМ!

✅ ПРАВИЛЬНО
Fold 1: Train [Jan], Test [Feb]
Fold 2: Train [Jan, Feb], Test [Mar]
Fold 3: Train [Jan, Feb, Mar], Test [Apr]
Fold 4: Train [Jan, Feb, Mar, Apr], Test [May]
(Всегда обучаемся на ПРОШЛОМ, тестируем на БУДУЩЕМ)
from sklearn.model_selection import TimeSeriesSplit

tscv = TimeSeriesSplit(n_splits=5)

for train_idx, test_idx in tscv.split(X):
    X_train, X_test = X[train_idx], X[test_idx]
    y_train, y_test = y[train_idx], y[test_idx]
    
    print(f"Train: {train_idx.min()}-{train_idx.max()}, Test: {test_idx.min()}-{test_idx.max()}")
    # 0-0, 1-1 | 0-1, 2-2 | 0-2, 3-3 | 0-3, 4-4

6. Stratified Train-Test Split

Для несбалансированных данных с одним разделением

from sklearn.model_selection import train_test_split

# Несбалансированные
y = [0]*95 + [1]*5  # 95% и 5%

# Обычный split может не сохранить пропорцию
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)

print(f"Train: {sum(y_train)} ones из {len(y_train)}")  # может быть 4 из 80
print(f"Test: {sum(y_test)} ones из {len(y_test)}")      # может быть 1 из 20

# Stratified (сохранит 95/5)
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, stratify=y, random_state=42
)

print(f"Train: {sum(y_train)/len(y_train):.1%} ones")  # ~5%
print(f"Test: {sum(y_test)/len(y_test):.1%} ones")      # ~5%

Сравнение методов

МетодИспользованиеПлюсыМинусы
Train-TestБыстрая проверкаПростоНестабильно на маленьких данных
Train-Val-TestВыбор hyperparametersПравильноТребует больше данных
K-Fold CVОценка качестваСтабильноМедленнее
Stratified K-FoldНесбалансированные данныеСохраняет пропорцииМедленнее
Time Series SplitВременные рядыРеалистичноТолько для временных данных

Практический пример: полный pipeline

from sklearn.model_selection import StratifiedKFold, cross_validate
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score

# Данные
X, y = load_data()  # загружаем

# 1. First split: отделяем test set (заморозим до конца)
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, stratify=y, random_state=42
)

# 2. K-Fold на training data
skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)

# 3. Pipeline со всеми шагами
from sklearn.pipeline import Pipeline
pipeline = Pipeline([
    ('scaler', StandardScaler()),
    ('model', LogisticRegression())
])

# 4. Cross-validation на training data
scores = cross_validate(
    pipeline, X_train, y_train, 
    cv=skf,
    scoring=['accuracy', 'precision', 'recall', 'f1']
)

print(f"CV Accuracy: {scores['test_accuracy'].mean():.3f}")
print(f"CV Precision: {scores['test_precision'].mean():.3f}")
print(f"CV Recall: {scores['test_recall'].mean():.3f}")

# 5. ФИНАЛЬНОЕ обучение на всех training данных
pipeline.fit(X_train, y_train)

# 6. ФИНАЛЬНАЯ оценка на test (один раз!)
y_pred = pipeline.predict(X_test)
final_accuracy = accuracy_score(y_test, y_pred)
final_f1 = f1_score(y_test, y_pred)

print(f"\nFinal Test Accuracy: {final_accuracy:.3f}")
print(f"Final Test F1: {final_f1:.3f}")

Типичные ошибки

Ошибка 1: Обучаться и тестировать на одних данных

model.fit(X, y)
model.score(X, y)  # НЕПРАВИЛЬНО!

Ошибка 2: Использовать test set для выбора параметров

for lr in [0.01, 0.1, 1]:
    model = LogisticRegression(lr=lr)
    model.fit(X_train, y_train)
    score = model.score(X_test, y_test)  # test set загрязнен!

Ошибка 3: Нормализовать ВСЕ данные перед split

X = scaler.fit_transform(X)  # используем test информацию!
X_train, X_test = train_test_split(X, test_size=0.2)

Правильно: split ПЕРВЫЙ, потом нормализовать

X_train, X_test = train_test_split(X, test_size=0.2)
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)  # учим на train
X_test = scaler.transform(X_test)  # применяем на test

Памятка для собеседования

Когда использовать:

  • 📊 Маленький датасет (< 1000): K-Fold CV
  • 📊 Большой датасет: Train-Test Split (80/20)
  • 📊 Несбалансированные данные: Stratified методы
  • 📊 Временные ряды: Time Series Split
  • 📊 Выбор гиперпараметров: Train-Val-Test

Основной принцип: "Never peek at the test set" — тестовые данные должны оставаться чистыми до финальной оценки.