Random Forest или Случайный лес — это ансамблевый алгоритм машинного обучения, объединяющий множество деревьев решений для повышения точности и устойчивости предсказаний. Алгоритм был предложен Лео Брейманом в 2001 году и с тех пор стал одним из наиболее используемых методов в задачах классификации и регрессии.
Основное преимущество Random Forest — способность снижать дисперсию модели без существенного увеличения смещения. Одиночное дерево решений склонно к переобучению: оно запоминает шум в обучающих данных и плохо обобщает на новые примеры. Ансамбль деревьев, обученных на разных подвыборках данных с разными подмножествами признаков, усредняет индивидуальные ошибки и дает более стабильный результат.
В финансовом и количественном анализе Случайный лес применяется для прогнозирования направления движения цен, оценки вероятности дефолта, скоринга и ранжирования активов. Алгоритм устойчив к выбросам, работает с разнородными признаками без нормализации и предоставляет встроенную оценку важности переменных.
Архитектура Random Forest
Ансамбль Random Forest состоит из независимо обученных деревьев решений, каждое из которых голосует за итоговое предсказание. В задачах классификации итоговый класс определяется большинством голосов, в регрессии — усреднением предсказаний всех деревьев. Независимость деревьев достигается за счет двух механизмов рандомизации: бутстрэп-выборки наблюдений и случайного отбора признаков при каждом разбиении.
Базовые деревья решений
Дерево решений — древовидная структура, где каждый внутренний узел содержит условие разбиения по одному из признаков, а листья хранят итоговые предсказания. Построение дерева происходит рекурсивно: алгоритм выбирает признак и порог, минимизирующие выбранный критерий неопределенности, и разделяет данные на две части.
Для классификации стандартные критерии разбиения:
- Gini impurity — измеряет вероятность неправильной классификации случайно выбранного элемента;
- Entropy (Information Gain) — оценивает снижение энтропии после разбиения;
- Log Loss — используется для вероятностных предсказаний.
Для регрессии применяются MSE (Mean Squared Error) и MAE (Mean Absolute Error). MSE штрафует большие отклонения сильнее, MAE более устойчив к выбросам.
В модели машинного обучения Random Forest деревья обычно выращиваются до максимальной глубины без ранней остановки. Переобучение отдельных деревьев компенсируется усреднением по ансамблю. Это отличает алгоритм Случайный лес от одиночных деревьев, где глубина и минимальное количество примеров в листе требуют тщательной настройки.
Формирование ансамбля
Ансамбль формируется из N независимых деревьев (типичные значения — от 100 до 1000). Каждое дерево обучается на своей бутстрэп-выборке и использует случайное подмножество признаков при каждом разбиении. Эти два источника случайности обеспечивают декорреляцию деревьев — ключевое условие эффективности ансамбля.
Итоговое предсказание для классификации получается по формуле:
ŷ = mode(h₁(x), h₂(x), …, hₙ(x))
где:
- ŷ — предсказанный класс;
- hᵢ(x) — предсказание i-го дерева для входа x;
- mode — наиболее частый класс среди всех деревьев;
- n — количество деревьев в ансамбле.
Для регрессии используется среднее арифметическое:
ŷ = (1/n) × Σhᵢ(x)
Дисперсия ансамбля снижается пропорционально количеству деревьев при условии их независимости. На практике деревья коррелированы (обучаются на пересекающихся данных), поэтому снижение дисперсии имеет предел. После определенного числа деревьев (обычно 200-500) качество выходит на плато.
import numpy as np
import matplotlib.pyplot as plt
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import make_classification
from sklearn.model_selection import cross_val_score
# Генерация синтетических данных
X, y = make_classification(
n_samples=1000,
n_features=20,
n_informative=10,
n_redundant=5,
random_state=42
)
# Исследование влияния количества деревьев на качество
n_estimators_range = [10, 25, 50, 100, 200, 300, 500, 750, 1000]
mean_scores = []
std_scores = []
for n_est in n_estimators_range:
rf = RandomForestClassifier(n_estimators=n_est, random_state=42, n_jobs=-1)
scores = cross_val_score(rf, X, y, cv=5, scoring='accuracy')
mean_scores.append(scores.mean())
std_scores.append(scores.std())
# Визуализация
fig, ax = plt.subplots(figsize=(10, 6))
ax.errorbar(n_estimators_range, mean_scores, yerr=std_scores,
fmt='o-', color='#1a1a1a', capsize=4, capthick=1.5,
markersize=6, linewidth=1.5)
ax.axhline(y=max(mean_scores), color='#666666', linestyle='--',
linewidth=1, alpha=0.7)
ax.set_xlabel('Количество деревьев', fontsize=11)
ax.set_ylabel('Accuracy (CV)', fontsize=11)
ax.set_title('Зависимость качества от числа деревьев в ансамбле', fontsize=12)
ax.grid(True, alpha=0.3)
ax.set_xlim(0, 1050)
plt.tight_layout()
plt.show()
print(f"Лучший результат: {max(mean_scores):.4f} при {n_estimators_range[np.argmax(mean_scores)]} деревьях")
Лучший результат: 0.9360 при 50 деревьях

Рис. 1: Зависимость верности (accuracy) от количества деревьев в Random Forest. График демонстрирует типичную картину: резкий рост качества при увеличении числа деревьев от 10 до 100, затем выход на плато. Вертикальные отрезки показывают стандартное отклонение по фолдам кросс-валидации — с ростом ансамбля оно снижается, что указывает на стабилизацию модели
Бутстрэп-агрегирование (Бэггинг, Bagging)
Bagging (Bootstrap Aggregating) — метод построения ансамбля, где каждая базовая модель обучается на бутстрэп-выборке из исходного датасета. Бутстрэп-выборка формируется случайным отбором n наблюдений с возвращением из исходных n наблюдений. В результате часть примеров попадает в выборку несколько раз, часть не попадает вовсе.
Модель Случайного леса расширяет классический бэггинг добавлением случайного отбора признаков при каждом разбиении узла. Это дополнительно декоррелирует деревья и повышает эффективность ансамбля.
Механизм формирования выборок
При формировании бутстрэп-выборки размером n из исходного датасета размером n вероятность того, что конкретное наблюдение не попадет в выборку:
P(не выбрано) = (1 — 1/n)ⁿ
где:
- n — размер исходного датасета;
- 1/n — вероятность выбора конкретного наблюдения на одном шаге.
При n → ∞ эта вероятность стремится к 1/e ≈ 0.368. На практике около 36.8% наблюдений не попадают в каждую конкретную бутстрэп-выборку. Эти наблюдения называются out-of-bag (OOB) и используются для оценки качества модели без отдельного валидационного множества.
Бутстрэп обеспечивает разнообразие обучающих множеств при ограниченном объеме данных. Каждое дерево видит немного разную версию датасета, что приводит к разным структурам деревьев и разным границам решений. Усреднение по таким деревьям сглаживает индивидуальные ошибки.

Рис. 2: Свойства бутстрэп-выборки. Левая панель показывает распределение доли OOB-наблюдений при n=1000: эмпирическое среднее близко к теоретическому значению 1/e ≈ 0.368. Правая панель демонстрирует сходимость: с ростом размера выборки средняя доля OOB приближается к теоретическому пределу
Случайный отбор признаков
В классическом бэггинге каждое дерево рассматривает все признаки при выборе разбиения. Если в данных есть сильный предиктор, большинство деревьев будут использовать его в корневом узле, что приведет к высокой корреляции между деревьями и снизит эффект усреднения.
Метод случайного леса элегантно решает эту проблему: при каждом разбиении узла алгоритм случайно выбирает подмножество из m признаков и ищет лучшее разбиение только среди них.
Рекомендуемые значения m:
- Классификация: m = √p (корень из общего числа признаков);
- Регрессия: m = p/3 (треть от общего числа признаков).
Меньшее значение m увеличивает случайность и снижает корреляцию между деревьями, однако может пропустить информативные признаки в конкретном узле. Большее m приближает поведение к обычному бэггингу. Параметр max_features в scikit-learn контролирует это значение.
Комбинация бутстрэп-выборок и случайного отбора признаков обеспечивает достаточную декорреляцию деревьев для эффективного снижения дисперсии. При p признаках и m = √p вероятность того, что два дерева выберут один и тот же признак для разбиения корня, составляет примерно m/p = 1/√p — существенно меньше единицы при большом числе признаков.
Out-of-Bag оценка
Out-of-Bag (OOB) оценка — встроенный механизм валидации Random Forest, использующий наблюдения, не попавшие в бутстрэп-выборку каждого дерева. Для каждого наблюдения предсказание формируется только теми деревьями, которые не видели это наблюдение при обучении. Это дает несмещенную оценку ошибки обобщения без необходимости выделять отдельное валидационное множество.
Принцип работы OOB
Алгоритм OOB-оценки:
- Для каждого наблюдения xᵢ определить множество деревьев Tᵢ, в чьи бутстрэп-выборки оно не попало;
- Получить предсказания от деревьев из Tᵢ;
- Агрегировать предсказания (голосование для классификации, среднее для регрессии);
- Вычислить метрику качества по всем OOB-предсказаниям.
В среднем каждое наблюдение попадает в OOB примерно для 36.8% деревьев. При 500 деревьях это около 184 деревьев на наблюдение — достаточно для стабильной агрегации.
Out-Of-Bag-оценка асимптотически эквивалентна leave-one-out кросс-валидации, но вычисляется без дополнительных затрат: все необходимые предсказания получаются в процессе обучения. Это делает OOB практичным инструментом для быстрой оценки качества и подбора гиперпараметров.
Ограничение OOB: при малом количестве деревьев (<50) оценка может быть нестабильной из-за недостаточного числа деревьев для каждого наблюдения. Рекомендуется использовать OOB при n_estimators >= 100.
Практическое применение OOB
Out-Of-Bag-оценка полезна в нескольких сценариях:
- Быстрая валидация без разбиения данных — особенно ценно при ограниченном объеме выборки;
- Мониторинг переобучения при увеличении сложности модели;
- Предварительный отбор гиперпараметров перед финальной кросс-валидацией;
- Оценка важности признаков через permutation importance на OOB-данных.
В scikit-learn OOB включается параметром oob_score=True. После обучения доступны атрибуты oob_score_ (метрика качества) и oob_decision_function_ (вероятности классов для классификации) или oob_prediction_ (предсказания для регрессии).
import numpy as np
import matplotlib.pyplot as plt
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import make_classification
from sklearn.model_selection import cross_val_score
# Генерация данных
X, y = make_classification(
n_samples=2000,
n_features=30,
n_informative=15,
n_redundant=5,
n_classes=2,
random_state=42
)
# Сравнение OOB и кросс-валидации при разном числе деревьев
n_estimators_range = [50, 100, 150, 200, 300, 500]
oob_scores = []
cv_scores_mean = []
cv_scores_std = []
for n_est in n_estimators_range:
rf = RandomForestClassifier(
n_estimators=n_est,
oob_score=True,
random_state=42,
n_jobs=-1
)
rf.fit(X, y)
oob_scores.append(rf.oob_score_)
cv_scores = cross_val_score(rf, X, y, cv=5, scoring='accuracy')
cv_scores_mean.append(cv_scores.mean())
cv_scores_std.append(cv_scores.std())
# Визуализация сравнения
fig, ax = plt.subplots(figsize=(10, 6))
ax.plot(n_estimators_range, oob_scores, 'o-', color='#1a1a1a',
linewidth=2, markersize=8, label='OOB Score')
ax.errorbar(n_estimators_range, cv_scores_mean, yerr=cv_scores_std,
fmt='s--', color='#666666', capsize=4, linewidth=2,
markersize=7, label='5-Fold CV')
ax.set_xlabel('Количество деревьев', fontsize=11)
ax.set_ylabel('Accuracy', fontsize=11)
ax.set_title('Сравнение OOB-оценки и кросс-валидации', fontsize=12)
ax.legend(fontsize=10)
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# Числовое сравнение
print("Сравнение OOB и CV:")
print(f"{'Деревья':<10} {'OOB':<10} {'CV Mean':<10} {'Разница':<10}")
for i, n_est in enumerate(n_estimators_range):
diff = abs(oob_scores[i] - cv_scores_mean[i])
print(f"{n_est:<10} {oob_scores[i]:<10.4f} {cv_scores_mean[i]:<10.4f} {diff:<10.4f}")

Рис. 3: Сравнение OOB-оценки и 5-fold кросс-валидации. OOB-оценка (сплошная линия) близка к результатам кросс-валидации (пунктир) и даже превышает ее, при этом вычисляется без дополнительного разбиения данных. Расхождение между методами обычно не превышает 0.5-1%, что делает OOB надежным инструментом для быстрой валидации
Сравнение OOB и CV:
Деревья OOB CV Mean Разница
50 0.8905 0.8995 0.0090
100 0.9100 0.9050 0.0050
150 0.9125 0.9065 0.0060
200 0.9150 0.9100 0.0050
300 0.9190 0.9100 0.0090
500 0.9210 0.9075 0.0135
Параметры настройки
Алгоритм Случайный лес имеет меньше важных параметров, чем градиентный бустинг (Gradient Boosting). Но их грамотная настройка все равно влияет на качество модели и скорость обучения.
Все параметры можно разделить на две группы:
- Параметры всего набора деревьев: количество деревьев и количество признаков;
- Параметры отдельных деревьев: глубина и минимальное число примеров в одном листе.
Ключевые гиперпараметры
Параметры ансамбля:
n_estimators — количество деревьев. Увеличение числа деревьев монотонно улучшает качество до выхода на плато и никогда не приводит к переобучению (в отличие от бустинга). Ограничение сверху — вычислительные ресурсы и время обучения. Для большинства задач 200-500 деревьев достаточно.
max_features — количество признаков для выбора при каждом разбиении. Значения по умолчанию в scikit-learn: ‘sqrt’ для классификации, 1.0 (все признаки) для регрессии. Уменьшение max_features снижает корреляцию между деревьями и уменьшает время обучения, но может потребовать увеличения n_estimators для компенсации.
Параметры деревьев:
max_depth — максимальная глубина дерева. None (без ограничения) — значение по умолчанию, деревья растут до полного разделения или до достижения min_samples_split. Ограничение глубины ускоряет обучение и может служить регуляризацией.
min_samples_split — минимальное число примеров для разбиения узла. Увеличение этого параметра приводит к более грубым деревьям и служит формой регуляризации.
min_samples_leaf — минимальное число примеров в листе. Аналогично min_samples_split, однако еще контролирует конечные узлы.
Таблица типичных значений параметров:
| Параметр | Значение по умолчанию | Диапазон для поиска | Влияние на модель |
| n_estimators | 100 | 100-1000 | Качество ↑, время ↑ |
| max_features | sqrt (классиф.) | 0.3-1.0 или sqrt, log2 | Декорреляция деревьев |
| max_depth | None | 10-50 или None | Регуляризация |
| min_samples_split | 2 | фев.20 | Регуляризация |
| min_samples_leaf | 1 | 01.окт | Сглаживание предсказаний |
Стратегии подбора параметров
Базовый подход — начать со значений по умолчанию и увеличивать n_estimators до стабилизации OOB-score. Для большинства задач этого достаточно. Модель случайного леса устойчива к переобучению, поэтому агрессивная регуляризация для нее редко требуется.
При необходимости тонкой настройки:
- Зафиксировать n_estimators=500 для стабильных оценок;
- Перебрать max_features: [0.3, 0.5, ‘sqrt’, 0.7, 1.0];
- Если есть признаки переобучения — добавить ограничения глубины или min_samples_leaf;
- После выбора структуры — увеличить n_estimators если позволяют ресурсы.
Для задач с большим числом признаков (>100) уменьшение max_features до 0.3-0.5 часто улучшает качество и ускоряет обучение. Для задач с малым числом признаков (<20) значение по умолчанию sqrt обычно оптимально.
В финансовых приложениях с высоким уровнем шума рекомендую увеличивать min_samples_leaf до 5-10 для более консервативных предсказаний и меньшей чувствительности к аномалиям в данных.
Реализация на Python
Scikit-learn предоставляет эффективную реализацию Random Forest через классы RandomForestClassifier и RandomForestRegressor. Реализация поддерживает параллельное обучение деревьев (параметр n_jobs), что ускоряет работу на многоядерных процессорах.
Базовая модель
Пример построения модели для задачи бинарной классификации с оценкой качества и анализом важности признаков:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report, roc_auc_score, roc_curve
from sklearn.datasets import make_classification
# Генерация данных с именованными признаками
X, y = make_classification(
n_samples=3000,
n_features=20,
n_informative=8,
n_redundant=4,
n_clusters_per_class=2,
random_state=42
)
feature_names = [f'feature_{i}' for i in range(X.shape[1])]
X = pd.DataFrame(X, columns=feature_names)
# Разбиение на train/test
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.25, random_state=42, stratify=y
)
# Обучение модели с OOB-оценкой
rf_model = RandomForestClassifier(
n_estimators=300,
max_features='sqrt',
oob_score=True,
random_state=42,
n_jobs=-1
)
rf_model.fit(X_train, y_train)
# Оценка качества
y_pred = rf_model.predict(X_test)
y_proba = rf_model.predict_proba(X_test)[:, 1]
print(f"OOB Score: {rf_model.oob_score_:.4f}")
print(f"Test ROC-AUC: {roc_auc_score(y_test, y_proba):.4f}")
print("\nClassification Report:")
print(classification_report(y_test, y_pred))
# Визуализация важности признаков и ROC-кривой
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# Feature Importance
importances = pd.Series(rf_model.feature_importances_, index=feature_names)
importances_sorted = importances.sort_values(ascending=True)[-10:]
importances_sorted.plot(kind='barh', ax=axes[0], color='#2a2a2a')
axes[0].set_xlabel('Importance', fontsize=11)
axes[0].set_title('Top-10 Feature Importances', fontsize=12)
# ROC Curve
fpr, tpr, _ = roc_curve(y_test, y_proba)
axes[1].plot(fpr, tpr, color='#1a1a1a', linewidth=2,
label=f'ROC-AUC = {roc_auc_score(y_test, y_proba):.3f}')
axes[1].plot([0, 1], [0, 1], 'k--', linewidth=1, alpha=0.5)
axes[1].set_xlabel('False Positive Rate', fontsize=11)
axes[1].set_ylabel('True Positive Rate', fontsize=11)
axes[1].set_title('ROC Curve', fontsize=12)
axes[1].legend(fontsize=10)
axes[1].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
OOB Score: 0.8871
Test ROC-AUC: 0.9555
Classification Report:
precision recall f1-score support
0 0.88 0.94 0.91 374
1 0.93 0.88 0.90 376
accuracy 0.91 750
macro avg 0.91 0.91 0.91 750
weighted avg 0.91 0.91 0.91 750

Рис. 4: Результаты базовой модели Random Forest. Левая панель показывает топ-10 признаков по важности (Gini importance) — информативные признаки имеют значимо более высокие значения. Правая панель — ROC-кривая на тестовой выборке, демонстрирующая разделяющую способность модели
Представленный выше код демонстрирует стандартный пайплайн работы со Случайным лесом:
- Разбиение данных;
- Обучение с OOB-оценкой;
- Расчет метрик на тесте;
- Анализ важности признаков.
Feature importance в Random Forest вычисляется как среднее снижение Gini impurity по всем узлам, где признак используется для разбиения, взвешенное на долю примеров, достигающих этого узла.
Оптимизация гиперпараметров
Для систематического поиска оптимальных параметров в обучении модели Случайного леса используется GridSearchCV или RandomizedSearchCV. При большом пространстве параметров RandomizedSearchCV эффективнее — он сэмплирует заданное число комбинаций вместо полного перебора.
import numpy as np
import matplotlib.pyplot as plt
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import RandomizedSearchCV, learning_curve
from sklearn.datasets import make_classification
# Данные
X, y = make_classification(
n_samples=1000,
n_features=20,
n_informative=10,
random_state=42
)
# Пространство поиска параметров
param_distributions = {
'n_estimators': [50, 100, 150],
'max_features': [0.3, 0.5, 0.7],
'max_depth': [10, 20, None],
'min_samples_split': [2, 5, 10],
'min_samples_leaf': [1, 2, 4]
}
# Randomized Search
rf = RandomForestClassifier(random_state=42)
random_search = RandomizedSearchCV(
rf,
param_distributions=param_distributions,
n_iter=30,
cv=3,
scoring='roc_auc',
random_state=42,
verbose=1
)
random_search.fit(X, y)
print("\nЛучшие параметры:")
for param, value in random_search.best_params_.items():
print(f" {param}: {value}")
print(f"\nЛучший CV ROC-AUC: {random_search.best_score_:.4f}")
# Learning Curve
best_rf = random_search.best_estimator_
train_sizes, train_scores, val_scores = learning_curve(
best_rf, X, y,
train_sizes=[0.2, 0.4, 0.6, 0.8, 1.0],
cv=3,
scoring='roc_auc'
)
# Визуализация
fig, ax = plt.subplots(figsize=(10, 6))
train_mean = train_scores.mean(axis=1)
train_std = train_scores.std(axis=1)
val_mean = val_scores.mean(axis=1)
val_std = val_scores.std(axis=1)
ax.fill_between(train_sizes, train_mean - train_std, train_mean + train_std,
alpha=0.2, color='#1a1a1a')
ax.fill_between(train_sizes, val_mean - val_std, val_mean + val_std,
alpha=0.2, color='#666666')
ax.plot(train_sizes, train_mean, 'o-', color='#1a1a1a', linewidth=2, label='Train')
ax.plot(train_sizes, val_mean, 's-', color='#666666', linewidth=2, label='Validation')
ax.set_xlabel('Размер обучающей выборки', fontsize=11)
ax.set_ylabel('ROC-AUC', fontsize=11)
ax.set_title('Learning Curve (оптимизированная модель)', fontsize=12)
ax.legend(fontsize=10)
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
Fitting 3 folds for each of 30 candidates, totalling 90 fits
Лучшие параметры:
n_estimators: 150
min_samples_split: 2
min_samples_leaf: 1
max_features: 0.3
max_depth: None
Лучший CV ROC-AUC: 0.9790

Рис. 5: Кривая обучения (Learning curve) для оптимизированной модели Random Forest. График показывает зависимость качества от размера обучающей выборки. Сближение кривых train и validation указывает на отсутствие переобучения. Если кривые расходятся — модель переобучена, требуется регуляризация или больше данных
Использование случайного поиска (RandomizedSearchCV) с 30 итерациями обычно достаточно, чтобы найти подходящую область значений параметров. После этого можно выполнить перебор по сетке (GridSearchCV) в узком диапазоне вокруг найденных параметров, чтобы точно настроить модель.
Кривая обучения (learning curve) помогает понять, нужны ли дополнительные данные. Если качество на проверочной выборке растет по мере увеличения объема данных, то значит, больше данных улучшит модель. Если кривая выходит на плато, текущего объема данных уже достаточно.
Заключение
Модель машинного обучения Random Forest сочетает простоту использования с высокой предсказательной силой:
- Механизм бутстрэп-агрегирования и случайного отбора признаков создает ансамбль декоррелированных деревьев, где индивидуальные ошибки взаимно компенсируются;
- OOB-оценка предоставляет встроенную валидацию без разбиения данных — ценное свойство при ограниченном объеме выборки;
- Алгоритм устойчив к выбросам, не требует нормализации признаков и редко переобучается при увеличении числа деревьев.
Эти свойства делают Random Forest надежным baseline-решением для задач классификации и регрессии.
В количественном анализе модель успешно применяется для ранжирования активов, оценки вероятностей событий и построения ансамблей с другими алгоритмами. Понимание внутренней механики — от формирования бутстрэп-выборок до влияния max_features на корреляцию деревьев — позволяет осознанно настраивать модель под специфику конкретной задачи.