Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Система сплитования (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" — тестовые данные должны оставаться чистыми до финальной оценки.