Раздел 42 · Аналитика данных с нуля

ML для аналитика: обучение, тест, утечка и когда модель не нужна

middle~20 мин

без входаВойди, чтобы отмечать прогресс, вести заметки и решать задачи в редакторе. войти

Отто когда-то верил этому дашборду, и агентство завоевало его, показав, где тот врёт. Теперь ставка выше: прогноз увидит каждый клиент «Гонца» в корзине, и неверная цифра станет обещанием, которое курьер не сдержит.

На дашборде Отто мы уже видели, что вечерний заказ в Уралмаше едет больше двух часов, а утренний полчаса. Отто хочет не разбирать это задним числом, а заранее говорить клиенту честный срок. Это задача для модели. Сегодня разберём, как устроено машинное обучение, и проверим модель подрядчика так, как её проверил бы опытный аналитик.

Данные на весь урок: в courier_orders.csv каждый 15-й доставленный заказ «Гонца» с 1 марта по 30 июня 2026, 2636 строк. Есть зона, час и день недели заказа, расстояние, транспорт курьера и minutes, сколько минут заказ ехал от оформления до двери. Время доставки уже переведено в местный пояс, ловушку из урока про пояса мы обошли.

Модель это правило

Начнём с правила, которое ты мог бы написать сам после урока 74:

CASE
  WHEN hour <= 18 AND distance_km <= 4.25 THEN 25.2
  WHEN hour <= 18                         THEN 39.25
  WHEN distance_km <= 5.25                THEN 45.1
  ELSE                                         71.6
END AS predicted_minutes

Днём близкий заказ едет 25 минут, далёкий 39. Вечером 45 и 72. Только это правило писал не человек. Его нашла программа: перебрала все признаки и пороги и выбрала те, что лучше всего делят заказы на быстрые и медленные. Такая модель называется деревом решений. В Python его строят так:

import pandas as pd
from sklearn.tree import DecisionTreeRegressor, export_text

orders = pd.read_csv("courier_orders.csv")
FEATURES = ["zone", "hour", "weekday", "distance_km", "transport"]
X = pd.get_dummies(orders[FEATURES], dtype=int)
y = orders["minutes"]

tree = DecisionTreeRegressor(max_depth=2, criterion="absolute_error", random_state=0)
tree.fit(X_train, y_train)   # X_train и y_train появятся в разделе про экзамен
print(export_text(tree, feature_names=list(X.columns)))

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

scikit-learn это главная библиотека классического ML в Python. У всех её моделей одинаковый интерфейс: fit(X, y) учит, predict(X) предсказывает.

Три вида задач

ЗадачаЧто угадываемПример в курсе
Регрессиячисломинуты доставки сегодня, траты клиента в 74b
Классификацияклассуйдёт ли клиент «Кубика» в 74c
Кластеризацияничего, ищем группысегменты клиентов в 74d

Регрессия и классификация называются обучением с учителем: в истории есть правильные ответы, и модель учится на них. У кластеризации ответов нет, модель сама ищет похожих клиентов, а смысл группам даёт аналитик.

Экзамен по выученным билетам

Если проверять модель на тех же заказах, на которых она училась, это экзамен по билетам, которые студент вызубрил наизусть. Отличник по таким билетам может завалить первый же новый вопрос.

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

from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=42)
len(X_train), len(X_test)   # (1977, 659)

test_size=0.25 откладывает четверть заказов. random_state=42 это сид, как в бутстрэпе: с ним коллега получит то же разбиение, заказ в заказ.

Ошибку меряем через MAE: на сколько минут в среднем промахивается прогноз. Возьмём дерево без ограничения глубины и посмотрим на обе части:

from sklearn.metrics import mean_absolute_error

deep = DecisionTreeRegressor(criterion="absolute_error", random_state=0).fit(X_train, y_train)
mean_absolute_error(y_train, deep.predict(X_train))   # 0.31
mean_absolute_error(y_test, deep.predict(X_test))     # 34.6

На обучении ошибка 0,31 минуты, почти идеал. Дерево вырастило 1928 листьев на 1977 заказов: почти у каждого заказа свой лист, модель запомнила ответы. На новых заказах она ошибается на 34,6 минуты. Это переобучение: правило выучило случайности обучающих заказов, а не закономерность.

урок из курса

Урок из курса «Аналитика данных с нуля». Курс покупается один раз и остаётся навсегда: 500 ₽.

Оплата пока не подключена: премиум и курсы открываются промокодом.