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

Какие принципы machine learning использовал при реализации с нуля?

3.0 Senior🔥 81 комментариев
#Другое

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

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

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

Принципы Machine Learning при реализации с нуля

При разработке ML-систем с нуля нужно следовать проверенным принципам, которые обеспечивают надёжность, масштабируемость и воспроизводимость результатов.

1. Разделение данных: Train/Validation/Test

Основной принцип — никогда не обучайте и не оценивайте на одних и тех же данных:

from sklearn.model_selection import train_test_split
import numpy as np

# Загружаем данные
X = load_data()  # Features
y = load_labels()  # Labels

# Шаг 1: Отделяем тестовый набор (20% данных)
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)

# Шаг 2: Отделяем validation набор из обучающих данных (25% = 20% от всех)
X_train, X_val, y_train, y_val = train_test_split(
    X_train, y_train, test_size=0.25, random_state=42
)

print(f'Train: {len(X_train)}, Validation: {len(X_val)}, Test: {len(X_test)}')

Правило:

  • Train set — обучение модели (60-70%)
  • Validation set — выбор гиперпараметров, ранняя остановка (15-20%)
  • Test set — ФИНАЛЬНАЯ оценка, используется только один раз (15-20%)

2. Нормализация и масштабирование данных

Большинство алгоритмов требуют одинаковый масштаб признаков:

from sklearn.preprocessing import StandardScaler, MinMaxScaler
from sklearn.pipeline import Pipeline

# StandardScaler: (x - mean) / std — для нормального распределения
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)  # Fit только на train!
X_val_scaled = scaler.transform(X_val)         # Transform используя train stats
X_test_scaled = scaler.transform(X_test)       # То же для test

# MinMaxScaler: (x - min) / (max - min) — для [0, 1] диапазона
scaler = MinMaxScaler(feature_range=(0, 1))

# Правильный способ с Pipeline
pipeline = Pipeline([
    ('scaler', StandardScaler()),
    ('model', RandomForestClassifier())
])

pipeline.fit(X_train, y_train)
score = pipeline.score(X_test, y_test)

ВАЖНО: Масштабируем только на train данных, потом применяем к val/test!

3. Классовый баланс и стратификация

Для несбалансированных данных используем стратификацию:

from sklearn.model_selection import StratifiedKFold
from imblearn.over_sampling import SMOTE

# Стратификация: сохраняем распределение классов во всех наборах
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 распределение: {np.bincount(y_train)}')
print(f'Test распределение: {np.bincount(y_test)}')

# Для сильно несбалансированных данных используем SMOTE
from imblearn.over_sampling import SMOTE

smote = SMOTE(random_state=42)
X_train_balanced, y_train_balanced = smote.fit_resample(X_train, y_train)

print(f'Balanced: {np.bincount(y_train_balanced)}')

# Или используем class_weight в модели
from sklearn.ensemble import RandomForestClassifier

model = RandomForestClassifier(
    class_weight='balanced',  # Автоматическая балансировка весов
    random_state=42
)

4. Кросс-валидация

Не полагайтесь на один сплит данных:

from sklearn.model_selection import cross_val_score, KFold, StratifiedKFold

# K-Fold кросс-валидация
kfold = KFold(n_splits=5, shuffle=True, random_state=42)
scores = cross_val_score(
    RandomForestClassifier(),
    X_train, y_train,
    cv=kfold,
    scoring='accuracy'
)

print(f'CV Scores: {scores}')
print(f'Mean: {scores.mean():.4f} +/- {scores.std():.4f}')

# Стратифицированная кросс-валидация для несбалансированных данных
stratified_kfold = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
scores = cross_val_score(
    RandomForestClassifier(),
    X_train, y_train,
    cv=stratified_kfold,
    scoring='f1_weighted'
)

5. Правильный выбор метрик

Не всегда accuracy — правильная метрика:

from sklearn.metrics import (
    accuracy_score, precision_score, recall_score, f1_score,
    roc_auc_score, confusion_matrix, classification_report
)

y_pred = model.predict(X_test)
y_pred_proba = model.predict_proba(X_test)[:, 1]

# Классификация
print('=== Классификация ===')
print(f'Accuracy:  {accuracy_score(y_test, y_pred):.4f}')
print(f'Precision: {precision_score(y_test, y_pred):.4f}')  # Из положительных, сколько правильных
print(f'Recall:    {recall_score(y_test, y_pred):.4f}')     # Из реальных позитивов, сколько нашли
print(f'F1 Score:  {f1_score(y_test, y_pred):.4f}')         # Гармоническое среднее
print(f'ROC-AUC:   {roc_auc_score(y_test, y_pred_proba):.4f}')

print('\n=== Матрица ошибок ===')
print(confusion_matrix(y_test, y_pred))
print(classification_report(y_test, y_pred))

# Регрессия
from sklearn.metrics import mean_squared_error, r2_score, mean_absolute_error

print('=== Регрессия ===')
print(f'MAE:  {mean_absolute_error(y_test, y_pred):.4f}')
print(f'RMSE: {np.sqrt(mean_squared_error(y_test, y_pred)):.4f}')
print(f'R2:   {r2_score(y_test, y_pred):.4f}')

6. Hyperparameter Tuning

Найдите оптимальные параметры модели:

from sklearn.model_selection import GridSearchCV, RandomizedSearchCV

# GridSearchCV — перебирает все комбинации
param_grid = {
    'n_estimators': [10, 50, 100, 200],
    'max_depth': [5, 10, None],
    'min_samples_split': [2, 5, 10],
    'learning_rate': [0.01, 0.1, 0.2]
}

grid_search = GridSearchCV(
    RandomForestClassifier(random_state=42),
    param_grid,
    cv=5,
    scoring='f1_weighted',
    n_jobs=-1,  # Параллельно
    verbose=1
)

grid_search.fit(X_train, y_train)

print(f'Best params: {grid_search.best_params_}')
print(f'Best CV score: {grid_search.best_score_:.4f}')

# Используем лучшую модель
best_model = grid_search.best_estimator_
test_score = best_model.score(X_test, y_test)
print(f'Test score: {test_score:.4f}')

# RandomizedSearchCV — случайно выбирает комбинации (быстрее для больших пространств)
random_search = RandomizedSearchCV(
    RandomForestClassifier(random_state=42),
    param_distributions=param_grid,
    n_iter=20,
    cv=5,
    n_jobs=-1,
    random_state=42
)

7. Feature Engineering и Selection

Правильные признаки — залог успеха:

from sklearn.preprocessing import PolynomialFeatures
from sklearn.feature_selection import SelectKBest, f_classif, RFE

# Полиномиальные признаки
poly = PolynomialFeatures(degree=2, include_bias=False)
X_poly = poly.fit_transform(X_train)

# SelectKBest — выбираем K лучших признаков
selector = SelectKBest(score_func=f_classif, k=10)
X_selected = selector.fit_transform(X_train, y_train)
support = selector.get_support()
feature_names = np.array(feature_names)[support]
print(f'Selected features: {feature_names}')

# RFE — рекурсивное исключение признаков
from sklearn.ensemble import RandomForestClassifier

rfe = RFE(RandomForestClassifier(random_state=42), n_features_to_select=10)
X_rfe = rfe.fit_transform(X_train, y_train)

# Feature importance
model = RandomForestClassifier(random_state=42)
model.fit(X_train, y_train)
importances = model.feature_importances_
top_indices = np.argsort(importances)[-10:]
print(f'Top 10 features: {np.array(feature_names)[top_indices]}')

8. Воспроизводимость результатов

Устанавливайте random_state везде:

import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier

# Воспроизводимость
RANDOM_STATE = 42

np.random.seed(RANDOM_STATE)

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=RANDOM_STATE, stratify=y
)

model = RandomForestClassifier(
    n_estimators=100,
    random_state=RANDOM_STATE
)

model.fit(X_train, y_train)

9. Мониторинг переобучения

Отслеживайте gap между train и validation:

from sklearn.model_selection import learning_curve
import matplotlib.pyplot as plt

train_sizes, train_scores, val_scores = learning_curve(
    RandomForestClassifier(random_state=42),
    X_train, y_train,
    cv=5,
    train_sizes=np.linspace(0.1, 1.0, 10),
    scoring='accuracy'
)

train_mean = np.mean(train_scores, axis=1)
val_mean = np.mean(val_scores, axis=1)

plt.plot(train_sizes, train_mean, label='Train')
plt.plot(train_sizes, val_mean, label='Validation')
plt.legend()
plt.show()

# Если lines расходятся — переобучение

10. Сохранение и воспроизведение модели

import pickle
import joblib

# Сохранение
with open('model.pkl', 'wb') as f:
    pickle.dump(model, f)

# Или более быстро
joblib.dump(model, 'model.pkl')

# Загрузка
model = joblib.load('model.pkl')

# Сохранение версии
model_version = {
    'model': model,
    'scaler': scaler,
    'feature_names': feature_names,
    'hyperparameters': grid_search.best_params_,
    'metrics': {'accuracy': 0.95, 'f1': 0.93},
    'timestamp': datetime.now().isoformat()
}

joblib.dump(model_version, 'model_v1.0.pkl')

11. Лучшие практики

  • Начните просто — baseline модель до усложнения
  • Разделяйте данные — train/val/test отдельно
  • Нормализуйте только на train — потом применяйте к val/test
  • Проверяйте баланс классов — используйте стратификацию
  • Кросс-валидируйте — не полагайтесь на один сплит
  • Выбирайте правильные метрики — не только accuracy
  • Тюните гиперпараметры — на validation, не на test
  • Отслеживайте переобучение — смотрите gap между train и val
  • Документируйте всё — версию модели, параметры, метрики
  • Воспроизводимость — используйте random_state везде

Эти принципы критичны для разработки надёжных ML-систем в продакшене.