MLrandom forestансамбликлассификация

Случайный лес (random forest): как работает и где подводит

2026-07-12 11 мин

Случайный лес — это ансамбль из сотен решающих деревьев, где каждое дерево обучено на своей случайной подвыборке строк и признаков, а финальный ответ получается усреднением. В классификации деревья голосуют за класс, в регрессии их прогнозы усредняют. Именно за счёт этого усреднения лес почти всегда точнее и заметно стабильнее одного дерева: одиночное дерево легко цепляется за шум в данных, но когда деревьев много и они непохожи друг на друга, их индивидуальные ошибки взаимно гасятся.

Что такое случайный лес и почему это «лес»?

Одно решающее дерево — это последовательность вопросов к данным. «Пользователь сделал больше 3 заказов? Средний чек выше 2000? Заходил за последние 7 дней?» На каждом шаге дерево делит выборку по признаку так, чтобы внутри получившихся групп целевая переменная стала однороднее. Растёт оно жадно, пока в листе не останется несколько объектов. Проблема в том, что глубокое дерево запоминает обучающую выборку почти дословно: подвинь пару строк — и структура разъедется, прогноз поменяется. Такую модель называют неустойчивой: маленькое изменение входа даёт большое изменение выхода.

Лес решает это грубой силой. Берём не одно дерево, а много — обычно от сотни до тысячи. Каждое обучаем на слегка искажённой копии данных. Поодиночке эти деревья такие же нестабильные, но их ошибки разные и случайные, поэтому в среднем они компенсируют друг друга. «Лес» — потому что деревьев много, «случайный» — потому что случайность зашита в двух местах, о которых дальше.

Откуда в лесу берётся случайность

Первый источник — bagging (bootstrap aggregating). Перед обучением каждого дерева мы формируем bootstrap-выборку: берём столько же строк, сколько в исходных данных, но с возвращением. Одни строки попадают в выборку по два-три раза, другие не попадают вовсе — таких «забытых» примерно треть. В итоге каждое дерево видит свой чуть перекошенный срез реальности.

Второй источник — случайные признаки на каждом сплите. Когда дерево выбирает, по какому признаку делить узел, ему разрешают смотреть не на все колонки, а только на случайное подмножество (параметр max_features, для классификации по умолчанию — корень из числа признаков). Это ключевой момент. Без него все деревья цеплялись бы за один-два самых сильных признака и получались бы почти одинаковыми. А одинаковые деревья усреднять бесполезно: их ошибки коррелируют, и усреднение ничего не сглаживает. Ограничивая признаки, мы заставляем деревья быть разными.

Побочный бонус bagging — те самые «забытые» строки (out-of-bag). Каждую строку можно проверить на деревьях, которые её при обучении не видели, и получить честную оценку качества без отдельной валидационной выборки. В sklearn это включается флагом oob_score=True.

Почему лес переобучается меньше, чем одно дерево?

Ошибку модели удобно раскладывать на смещение (bias) и разброс (variance). Глубокое дерево — это низкое смещение и огромный разброс: оно хорошо ложится на любые данные, но результат сильно зависит от конкретной выборки. Усреднение независимых прогнозов не трогает смещение, зато снижает разброс. Будь деревья полностью независимы, разброс падал бы пропорционально их числу. Полной независимости нет — они учатся на одних и тех же данных, — но случайные строки и признаки делают деревья достаточно непохожими, чтобы разброс упал в разы.

Отсюда следствие, которое путает новичков: от увеличения числа деревьев лес не переобучается. Добавили с 300 до 800 деревьев — стало чуть точнее и медленнее, но не хуже. Переобучение регулируется глубиной деревьев и размером листа, а не их количеством. Поэтому n_estimators обычно ставят «сколько не жалко по времени», а тюнят совсем другое.

Можно ли доверять feature importance?

Feature importance — первое, за чем аналитики лезут в лес: «какие признаки влияют на отток?». В sklearn по умолчанию считается mean decrease in impurity (MDI): насколько каждый признак в среднем снижал загрязнённость узлов при сплитах. Считается мгновенно, но у неё есть три неприятных свойства.

Во-первых, MDI смещена в сторону признаков с большим числом уникальных значений. Непрерывная колонка вроде «сумма заказов до копейки» или почти уникальный user_id получат высокую важность просто потому, что по ним много вариантов деления — даже если предсказательной ценности в них ноль. Во-вторых, важность считается на обучающих данных, поэтому частично отражает переобучение, а не реальный сигнал. В-третьих, коррелированные признаки делят важность между собой: если orders_count и payments_count почти дублируют друг друга, лес размажет важность на оба, и каждый по отдельности покажется слабее, чем он есть.

Что делать. Использовать permutation importance на отложенной выборке: перемешиваем значения одного признака и смотрим, насколько просела метрика. Это честнее — меряется на данных, которых модель не видела, и напрямую в терминах качества. С коррелированными признаками помогает оценивать их группой, а не поодиночке. И главное: feature importance отвечает на вопрос «что использует модель», а не «что является причиной оттока». Это не про причинно-следственную связь, и подменять одно другим в отчёте для бизнеса — прямой путь к неправильному решению.

Чем случайный лес отличается от бустинга?

Случайный лес и градиентный бустинг (XGBoost, LightGBM, CatBoost) — оба ансамбли деревьев, но собираются они противоположным образом.

Лес строит деревья параллельно и независимо. Каждое дерево глубокое, сильное и обучается на своём куске данных, ничего не зная про остальные. Итог — усреднение. Главная цель — снизить разброс. Деревья можно считать хоть в сто потоков одновременно.

Бустинг строит деревья последовательно. Первое даёт грубый прогноз, второе учится исправлять его ошибки (точнее, градиент функции потерь), третье — ошибки первых двух, и так по цепочке. Деревья маленькие, каждое по отдельности слабое, но цепочка постепенно уменьшает смещение. Порядок здесь принципиален — параллелить обучение деревьев между собой нельзя.

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

Какие гиперпараметры реально важны?

Их немного, и это плюс. По убыванию влияния:

Тюнить всё сразу не нужно. В большинстве случаев хватает подобрать max_depth и min_samples_leaf, а n_estimators просто поставить побольше.

Как обучить случайный лес в sklearn

Соберём модель оттока: одна строка — один пользователь, таргет churned (ушёл за месяц или нет).

import pandas as pd
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_auc_score

features = ['orders_count', 'avg_check', 'days_since_last_order',
            'sessions_30d', 'support_tickets', 'discount_share']
X = df[features]
y = df['churned']            # 1 — ушёл, 0 — остался

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.25, stratify=y, random_state=42)

model = RandomForestClassifier(
    n_estimators=500,
    max_depth=None,
    max_features='sqrt',
    min_samples_leaf=20,
    class_weight='balanced',
    oob_score=True,
    n_jobs=-1,
    random_state=42,
)
model.fit(X_train, y_train)

proba = model.predict_proba(X_test)[:, 1]
print('OOB score:', round(model.oob_score_, 3))
print('ROC-AUC :', round(roc_auc_score(y_test, proba), 3))
n_jobs=-1 — обучать деревья на всех ядрах, лес это позволяет по своей природе. predict_proba даёт вероятность оттока — для скоринга она полезнее жёсткого predict, потому что порог отсечения можно двигать под задачу: удерживать топ-5% группы риска или всех, у кого вероятность выше 0.3.

Честную важность признаков берём через permutation, а не через .feature_importances_:

from sklearn.inspection import permutation_importance

result = permutation_importance(
    model, X_test, y_test,
    scoring='roc_auc', n_repeats=10, random_state=42)

importance = (pd.Series(result.importances_mean, index=features)
                .sort_values(ascending=False))
print(importance)

Если days_since_last_order уверенно наверху — логично: давно не заходивший пользователь ближе к оттоку. Но дальше этот сигнал уходит уже в продуктовые гипотезы и A/B-тесты, а не в вывод «давность визита вызывает отток».

Когда брать случайный лес, а когда нет?

Брать стоит, когда данные табличные, со смешанными признаками (числа плюс категории после кодирования), нужен быстрый крепкий baseline, важна устойчивость к выбросам и не хочется возиться с масштабированием — деревьям оно не нужно. Лес работает «из коробки» и редко подводит.

Не брать, когда данные — это текст, картинки, звук или длинные последовательности: там уместнее нейросети. Когда нужна максимальная точность на таблице и есть время настраивать — берут бустинг. Когда важна интерпретируемость на уровне «одна понятная формула» для бизнеса или регулятора — честнее логистическая регрессия или одиночное неглубокое дерево, потому что лес из 500 деревьев одним предложением не объяснишь. И когда данных совсем мало — десятки строк, — ансамблю просто не на чем разгуляться.

Что спрашивают про случайный лес на собеседовании?

Набор вопросов предсказуем, и заготовленные формулировки экономят нервы:

Прогнать эти ответы вслух и получить обратную связь помогает AI мок-собес, а типовые вопросы по ML и статистике собраны в разделе с вопросами с собеседований. Отработать сам код можно в Python-тренажёре, синтаксис pandas и sklearn держать под рукой — в справочнике, а разбор бизнес-задач целиком — в практических кейсах. Умение довести ML-модель до продуктового вывода отделяет джуна от миддла и заметно двигает зарплатную вилку, особенно на собесах в продуктовых компаниях.

Случайный лес стоит довести до автоматизма: его спрашивают на собесе и применяют в работе почти каждую неделю. Потренируйтесь на реальных данных, а если хочется системно закрыть ML вместе с SQL и статистикой, загляните в план подготовки к собеседованию. Первые задачи в каждом разделе открыты бесплатно, а Pro снимает лимиты и добавляет безлимитный AI-собес.

Связанная тема — Дерево решений (decision tree): как работает для аналитика.

Закрепи на практике
Python-задачи через pandas/numpy/scipy — попробуй бесплатно.
Python-тренажёр →