- BrainTools - https://www.braintools.ru -
Если вы видели “С этим товаром покупают…“, “Попробуйте этот урок дальше” или ощущали необычную точность автоподбора треков в музыкальных сервисах – вы сталкивались с рекомендательной системой.
Но стоит ли конкретному бизнесу вообще её строить? И если да, то с чего начать, чтобы не потратить впустую месяцы инженерного времени на “чёрный ящик”, который никто не понимает?
Эта статья написана мной РУКАМИ (не “ИИ”) и основана на моём выступлении-вебинаре для инженеров, предпринимателей и продуктовых специалистов. Я не ML исследователь и не публичный спикер – я фулстек-разработчик, который создавал и поддерживал рекомендательные системы в разнообразных коммерческих проектах.
Целевая аудитория – все, кому интересна эта тема.
Упрощённо, рекомендательная система – это компьютерная программа, которая предлагает подходящие товары/предметы (items) пользователям (users) на основе взаимодействий (interactions).
|
Компонент |
Что это |
Примеры |
|---|---|---|
|
Пользователи (users) |
Кто получает рекомендации |
Клиенты, студенты, посетители сайта |
|
Товары (items) |
Что мы рекомендуем |
Товары, уроки, видео, статьи и пр. контент |
|
Взаимодействия (interactions) |
Сигналы предпочтений |
Просмотры, лайки, покупки, написание комментариев, добавления в закладки, оценки и т.п. |
Сделаю небольшой шаг назад, чтобы взглянуть на “большую картину” – а собственно, в чём реальная ценность рекомендательных систем для бизнеса?
Бизнес – это создание товаров и услуг ради прибыли, за счёт добавленной ценности.
У машин два неоспоримых преимущества перед человеком: скорость и точность вычислений. Никакой голливудской магии тут нет.
Когда нужно за миллисекунды перебрать тысячи профилей, сопоставить сотни тегов, исключить человеческую ошибку [1] при масштабировании или мгновенно отфильтровать нерелевантный контент – эти два качества напрямую конвертируются в рост доходов и снижение издержек.
Все разговоры про “умные алгоритмы”, “нейросети, которые понимают пользователя” – маркетинговое фуфло. В классическом смысле компьютеры тупы. Они не думают, а скорее – исполняют инструкции. Написанные человеком.
Отдельная моя ремарка про цунами чатбот-ИИ-хайпа. Современные “универсальные” LLM умеют всё на поверхностном уровне, но на глубоком – почти ничего. Их работу сложно интерпретировать, тонко настроить под конкретную задачу, они ненадёжны и выдают галлюцинации с уверенным тоном. Бывают полезны в узких сценариях, но далеко не настолько, как пытаются продать. Бездумное внедрение “чёрных ящиков” без контроля ведёт к фильтр-баблам, неуместным рекомендациям и потере доверия. Топ-менеджмент часто требует воткнуть ИИ везде, не задумываясь о цене ошибки.
В отличие от этих “универсальных решений”, правильно построенная рекомендательная система даёт предсказуемую пользу: скорость, точность, управляемая интерпретируемость, гибкая настройка. Вы можете понимать, почему пользователю показали конкретный товар или урок. Вы можете отладить правило, скорректировать вес, провести A/B-тест. Вы контролируете процесс.
Классический пример, работающий десятилетиями: “покупатели, которые смотрели это, также покупали…” на Amazon. Никакой нейросетевой магии – простая коллаборативная логика [2], посчитанная быстро и точно. Этот подход до сих пор генерирует значимую долю выручки той и аналогичных платформ.
Представим, у вас есть онлайн-школа по изучению иностранного языка, например испанского. Студентам периодически назначаются домашние задания. Например: почитать текст/посмотреть видео/послушать подкаст, и потом дать обратную связь – оценить уместность задания.
Кратко, пример результатов работы рексистемы можно описать так:
Студенту John Doe назначается новое домашнее задание “Заказываем тапас в Барселоне”, потому что похожий на него студент Mark Smith, ранее уже выполнял это задание и дал позитивную обратную связь, оценив уместность задания в 5/5.
(примечание: мой изначальный вебинар был на английском, поэтому многие имена и примеры данных в коде тоже будут на английском, потому что мне влом было специально всё переделывать на русский)
Возникает логичный вопрос – а как мы (быстро, точно, без монструозных видеокарт и “облаков” из загнивающих вражеских стран) посчитали, что John Doe похож на Mark Smith?
Представим, результат нашего запроса к бд выглядит так:
Помимо обычных данных, мы получили “индекс похожести” (similarity score) в диапазоне [0,1], основанный на ряде правил. А именно – если по сравнению с John Doe у другого студента:
Одинаковый уровень владения изучаемым языком (“B1” == “B1”), добавляем 0.5
Насколько-то % совпадают теги интересов, добавляем до 0.3. Иными словами, считаем коэффициент Жаккара [3]
Совпадает родной язык (native language), добавляем 0.1
Схож возраст (в пределах 5 лет), добавляем 0.1
Фактически, это алгоритм K-ближайших соседей [4] в своей простейшей форме, эвристической и основанной на чётких рукотворных правилах.
Запрос на T-SQL выглядит так:
WITH users AS (
SELECT 'john_doe' user_id, 'John Doe' name, 'B1' skill, 'travel,food,culture' interests, 'en' native_lang, 28 age
UNION ALL SELECT 'mark_smith', 'Mark Smith', 'B1', 'travel,history,culture', 'en', 31
UNION ALL SELECT 'sofia_ivanova', 'Sofia Ivanova', 'B1', 'business,travel,food', 'ru', 29
UNION ALL SELECT 'chloe_dubois', 'Chloe Dubois', 'B2', 'food,wine,travel,culture', 'fr', 26
UNION ALL SELECT 'anna_muller', 'Anna Müller', 'A2', 'grammar,exams', 'de', 24
UNION ALL SELECT 'carlos_ruiz', 'Carlos Ruiz', 'B2', 'business,negotiations', 'pt', 35
),
john AS (SELECT * FROM users WHERE user_id = 'john_doe'),
user_tags AS (SELECT u.user_id, u.name, u.skill, u.native_lang, u.age, TRIM(value) tag FROM users u CROSS APPLY STRING_SPLIT(u.interests, ',')),
john_tags AS (SELECT TRIM(value) tag FROM john CROSS APPLY STRING_SPLIT(interests, ',')),
similarity_calc AS (
SELECT u.user_id, u.name, u.skill, u.interests, u.native_lang, u.age,
COUNT(CASE WHEN jt.tag IS NOT NULL THEN 1 END) shared_tags,
COUNT(DISTINCT ut.tag) + (SELECT COUNT(*) FROM john_tags) - COUNT(CASE WHEN jt.tag IS NOT NULL THEN 1 END) union_tags
FROM user_tags ut
LEFT JOIN john_tags jt ON ut.tag = jt.tag
JOIN users u ON u.user_id = ut.user_id
GROUP BY u.user_id, u.name, u.skill, u.interests, u.native_lang, u.age
)
SELECT
name [User], skill [Skill], interests [Interests], native_lang [Native Lang], age [Age],
CASE WHEN user_id = 'john_doe' THEN '1.00' ELSE
RIGHT('00' + CONVERT(VARCHAR(10), CAST(ROUND(
CASE WHEN skill = (SELECT skill FROM john) THEN 0.5 ELSE 0 END +
CASE WHEN union_tags > 0 THEN (shared_tags * 1.0 / union_tags) * 0.3 ELSE 0 END +
CASE WHEN native_lang = (SELECT native_lang FROM john) THEN 0.1 ELSE 0 END +
CASE WHEN ABS(age - (SELECT age FROM john)) < 5 THEN 0.1 ELSE 0 END,
2) AS NUMERIC(4,2)), 4), 4) END [Similarity to John]
FROM similarity_calc
ORDER BY CASE WHEN user_id = 'john_doe' THEN 0 ELSE 1 END, [Similarity to John] DESC;
Можете поковырять его, скопипастив себе или например сюда [5]
Пока мы поработали только с “пользователями”-студентами (users), чтобы понять, как искать похожих пользователей.
Для полноценной рек. системы нам надо учесть “товары”-домашние задания (items) и “взаимодействия”-оценки от студентов выполненным домашним заданиям.
Напомню, наша рек. система должна работать по следующей логике:
Студенту John Doe назначается новое домашнее задание HW-101 “Заказываем тапас в Барселоне”, потому что похожий на него студент Mark Smith, ранее уже выполнял это задание и дал позитивную обратную связь, оценив уместность задания в 5/5.
Запрос с учетом “товаров” и “взаимодействий” будет выглядеть так:
-- Recommendation for John Doe: profile match + peer feedback
WITH users AS (
SELECT 'john_doe' user_id, 'John Doe' name, 'B1' skill, 'travel,food,culture' interests, 'en' native_lang, 28 age UNION ALL SELECT 'mark_smith', 'Mark Smith', 'B1', 'travel,history,culture', 'en', 31 UNION ALL SELECT 'sofia_ivanova', 'Sofia Ivanova', 'B1', 'business,travel,food', 'ru', 29
),
items AS (
SELECT 'HW-101' item_id, 'Ordering Tapas in Barcelona' title, 'B1' skill, 'travel,food,culture' tags UNION ALL SELECT 'HW-102', 'Spanish Business Emails', 'B2', 'business,writing' UNION ALL SELECT 'HW-103', 'DELE A2 Grammar Drills', 'A2', 'grammar,exams'
UNION ALL SELECT 'HW-104', 'Mexican Street Food Vlog', 'A2', 'food,travel,culture' UNION ALL SELECT 'HW-105', 'Argentine History Podcast', 'B1', 'history,culture,travel'
),
feedback AS (
SELECT 'mark_smith' user_id, 'HW-101' item_id, 5 rating UNION ALL SELECT 'mark_smith', 'HW-105', 4 UNION ALL SELECT 'sofia_ivanova', 'HW-101', 5 UNION ALL SELECT 'sofia_ivanova', 'HW-102', 3 UNION ALL SELECT 'sofia_ivanova', 'HW-104', 4
),
-- Pre-aggregate peer ratings per item to avoid duplication
item_feedback AS (
SELECT item_id, AVG(rating * 1.0) AS avg_rating
FROM feedback
WHERE user_id IN ('mark_smith', 'sofia_ivanova')
GROUP BY item_id
),
john AS (
SELECT * FROM users WHERE user_id = 'john_doe'
),
recommendations AS (
SELECT
i.item_id,
i.title,
-- Personal relevance: skill + interest overlap + base weight
(CASE WHEN i.skill = j.skill THEN 0.5 ELSE 0 END +
(SELECT COUNT(*) FROM STRING_SPLIT(i.tags, ',') t
WHERE TRIM(t.value) IN (SELECT TRIM(value) FROM STRING_SPLIT(j.interests, ','))) * 0.3 / 3.0 +
0.2) AS personal_score,
ISNULL(f.avg_rating, 0) AS avg_social_rating
FROM items i
CROSS JOIN john j
LEFT JOIN item_feedback f ON f.item_id = i.item_id
)
SELECT TOP 3
item_id AS [Item ID],
title AS [Title],
ROUND(personal_score, 2) AS [Personal Match],
CAST(avg_social_rating AS DECIMAL(3,1)) AS [Avg Rating from Peers],
ROUND(personal_score + (CASE WHEN avg_social_rating >= 4 THEN 0.2 ELSE 0 END), 2) AS [Final Score]
FROM recommendations
-- Exclude items John already completed
WHERE item_id NOT IN (SELECT item_id FROM feedback WHERE user_id = 'john_doe')
-- Deterministic ordering: score first, then item ID
ORDER BY [Final Score] DESC, item_id ASC;
Опять же, можете его скопипастить и проверить здесь [5]
Данный запрос даст следующие результаты:

Вуаля, мы построили несложную рекомендательную систему!
Работает примерно так:
Скармливаем данные (datasets)
??? магия чёрная чёрного ящика ???
Получаем рекомендации

Достаточно похоже на нейросети? Концептуально – весьма.
Преимущества таких моделей в том, что они могут выявлять неочевидные паттерны и быть более эффективными на конкретных задачах. За что нередко надо расплачиваться сниженной интерпретируемостью результатов и повышенной вычислительной прожорливостью.
Однако, выбор между прозрачностью и простотой модели (как в описанной выше рек. системе) и “черноящиковостью” – это, как правило, не выбор между одним либо другим, а скорее выбор точки на диапазоне ближе к чему-то.
Условно, если двигаться от “более проразчного” к “менее прозрачному”, я бы распределил рекомендательные системы как-то так:
|
Подход |
Примеры |
Прозрачность |
Когда имеет смысл |
|---|---|---|---|
|
Жёстко прописанные правила |
“показать урок Б после урока А” |
полностью объяснимо, очевидно, захардкожено |
старт, мало данных, фигак-фигак и в продакшн |
|
Эвристики |
схожесть по тегам, демографии, эвристический kNN |
объяснимо – как в примере выше |
есть нюансы, но так же остаётся требование делать всё быстро, дёшево, понятно |
|
Матричная факторизация |
SVD, ALS [6] |
частично прозрачно, но надо приложить усилия, чтобы объяснить факторы, типа “фактор 7 связан с интересом [7] о путешествиях” |
средний масштаб (~десятки тысяч товаров), можем начинать называть себя ML-щиками! |
|
Градиентный бустинг |
можно отлаживать по фичам |
когда захотелось заморочиться похитрее после факторизации |
|
|
Эмбеддинги + поиск |
two-tower [10] сети + векторный поиск |
не особо прозрачно, интерпретировать всё не имеет смысла |
большой каталог товаров (больше чем почти у всех), и нужна скорость |
|
Сквозные нейросети |
ленты TikTok/Instagram |
полностью чёрный ящик |
огромные данные и команда, когда условно 1% улучшения в какой-нибудь метрике – это дополнительные десятки или сотни тысяч долларов прибыли каждый месяц |
Более сложные и менее прозрачные рексистемы работают не только с большим количеством данных, но и большим типом разных взаимодействий (клики, просмотры, написание комментария, оценки, потраченное время), учитывая также контекст (устройство, с которого заходили, география, день недели) и последовательность этих действий.
Помимо прочего, такие системы нередко сочетают в себе гибридный подход, например могут использовать эмбеддинги (поиск во векторам) для быстрого нахождения “товаров-кандидатов” и дальше использовать градиентный бустинг или сквозные нейросети для ранжирования/сортировки найденных кандидатов.
Разберём вариант, как конкретно выглядит “меньшая прозрачность” рек. систем на примере матричной факторизации.
Напомню, матрица – это система это по-сути табличка, или двумерный массив.
В нашем случае, следует рассмотреть матрицу взаимодействий (user-item matrix).
|
|
HW-101 |
HW-102 |
HW-103 |
HW-104 |
HW-105 |
|---|---|---|---|---|---|
|
John Doe |
? |
? |
? |
? |
? |
|
Mark Smith |
5 |
? |
? |
? |
4 |
|
Sofia Ivanova |
5 |
3 |
? |
4 |
? |
Строки = пользователи
Столбцы = товары (домашки)
Ячейки = оценки (или клики/время)
? = пропущенные значения (студент ещё не видел задание)
Цель: предсказать “?” так, чтобы предложить пользователю уроки с максимальным значением.
В отличие от предыдущего примера с эвристическим kNN, где правила настройки весов задавались вручную, подход матричной факторизации предполагает автоматический подбор значений факторов. Это уже настоящее машинное обучение [11].
Вектор – это последовательность чисел (как строка, столбец, или одномерный массив).
Для каждого студента и каждого задания мы задаём вектор из k факторов. Количество факторов k устанавливается экспериментально [12] и обычно это от 5 до 20 для относительно небольших данных (где-то до ~15 тысяч взаимодействий-interactions).
Пропущу объяснение, как проходит вычисление значений факторов (через обучение и градиентный спуск), предположим, мы уже получили векторы для пользователей и домашних заданий.
Тогда вычисление конкретного значения “?” для ячейки John Doe, HW-101 (первая ячейка в пред. таблице) будет выглядеть так:
//Обучение уже прошло и дало нам эти векторы из пяти скрытых факторов
$u = [0.42, -0.18, 0.61, 0.05, -0.33]; //вектор студента John Doe
$i = [0.55, 0.12, 0.48, 0.22, 0.10]; //вектор задания HW-101
//Базовые поправки (bias), которые модель выучила автоматически
$global = 3.85; //средняя оценка по всей школе
$u_bias = 0.30; //John склонен ставить оценки чуть выше среднего
$i_bias = 0.15; //HW-101 в целом нравится аудитории
//Считаем скалярное произведение: насколько векторы "смотрят в одну сторону"
$dot = 0;
for ($k = 0; $k < 5; $k++) {
$dot += $u[$k] * $i[$k];
}
//Прогноз
$prediction = $global + $u_bias + $i_bias + $dot;
echo "Прогноз оценки John для HW-101: " . round($prediction, 2) . "/5n";
//В итоге получим: Прогноз оценки John для HW-101: 4.78/5
Да код на PHP [13], что поделать… =)
Аналогично, мы можем посчитать и заполнить остальные ячейки в строке исходной таблицы, и найти домашние задания с максимальными предсказаниями. Отсортировав их, мы получим топ рекомендаций для конкретного студента.
Основная идея этого примера в том, что вектора для пользователя John Doe [0.42, -0.18, 0.61, 0.05, -0.33] и задания HW-101 [0.55, 0.12, 0.48, 0.22, 0.10] состоят из 5 факторов каждый, и каждый из этих факторов является скрытым, латентным.
Несмотря на то, что система автоматически их вычисляет прозрачным образом, она не даёт им никаких названий или объяснений.
Мы можем лишь предположить, что, к примеру, первый фактор для задания 0.55 как-то связан с едой (чем выше значение, тем больше тема еды характерна для задания), а третий фактор для студента 0.61 возможно связан с уровнем владения изучаемым языком.
Если вы представите, что пользователей у вас не три, а пять тысяч, заданий не пять, а десять тысяч, а факторов при этом штук 20, интерпретация рекомендаций может стать значительно сложнее по сравнению с предыдущим примером с эвристическим kNN, где расчёт рекомендации проходил по явным и понятным правилам.
И это один небольшой пример движения в сторону меньшей прозрачности – всегда можно пойти ещё дальше в “лес”, вплоть до глубоких нейронных сетей с миллионами и миллиардами весов, где практически невозможно чётко интерпретировать значение каждого отдельного “нейрона”.
Применимость рек. систем может сильно варьироваться в зависимости от конкретного коммерческого проекта.
Я бы выделил следующие взаимосвязанные условия, когда рек. система может быть реально уместной:
Желание и возможность инвестировать в рексистему
Рексистема не является волшебным инструментом, появляющимся “из воздуха”.
Любое решение, даже условно готовое, требует настройки, поддержки, погружения в тему. Данные надо причесывать, системы метрик – создавать и отслеживать.
Всё это требует времени, ресурсов – больших, чем “15 минут в день”.
Короткий цикл обратной связи
Речь идёт о взаимодействиях (interactions) и метриках. Взаимодействия – это обратная связь от пользователей.
Вы должны уметь довольно быстро понять, нравится, уместен ли рекомендуемый контент, или нет.
Метрики, измеряющие эффективность и некоторые другие показатели работы рек. системы, также должны обновляться раз в какое-то не слишком долгое время. Навскидку, это должно быть что-то в пределах одной недели.
Если между обновлениями рекомендациий, взаимодействий или метрик проходит год – у вас вряд ли будет возможность эффективно обновлять и поддерживать рек. систему.
Данные в правильном формате и в достаточном количестве
Навскидку, вам нужно не менее 100 пользователей и не менее 1000 “товаров” (items), которые вы будете рекомендовать. Больше – лучше.
Помимо прочего, данные о пользователях, товарах и контекстах должны быть оцифрованы (не быть “на бумаге” или в виде неявных знаний, устных описаний и т.п.) и иметь соответствующий формат.
Насчёт формата – вам скорее всего придётся данные всё равно “причесывать”, но есть нюансы, о некоторых из которых я напишу ниже.
Возможность проводить A/B и прочие тесты, и измерять изменения
Вытекает из первого пункта про инвестиции в рексистему, но решил обозначить отдельно.
Рексистема без метрик = непонятное нечто (мы улучшаемся или ухудшаемся со временем? в каких аспектах?).
Невозможность сравнить две модели = невозможность выбрать оптимальную.
Типа:
|
Подходит |
Не подходит |
|---|---|
|
Интернет-магазин с историей покупок |
B2B с десятком сделок в год |
|
Видеоплатформа с метриками просмотра |
Сильно зарегулированная отрасль без права на эксперименты |
|
Образовательный сервис с трекингом прогресса |
Стартап с упором на “максимизацию количества фич” и без аналитики, типа “добавим потом” |
Основная мысль – надо не только регулярно проводить достаточно измерений, но и понимать, что и как будет измеряться.
Как было показано выше, решения в области рекомендательных систем можно написать и “руками”, без библиотек, фреймворков, готовых программ и прочих сторонних/облачных продуктов. В таком случае, систему отчётов (сбора метрик) скорее всего придётся написать руками тоже.
Однако, даже если взять какое-то готовое решение, которое помимо рекомендаций будет выдавать ещё метрики, это не освобождает от необходимости иметь систему отчётов по рекоммендациям и понимать, как она работает.
А именно:
Надо понимать, как конкретно считаются метрики (подробнее об этом ниже)
Важные метрики следует дублировать на своей стороне
Имеет смысл дополнять готовые метрики своими собственными
Как бы то ни было, система метрик/отчётов должна минимально зависеть от конкретной реализации, чтобы рексистему или конкретную версию модели можно было поменять на другую, и сравнивать одно с другим через отчёты (а-ля D [14] из SOLID [15])
Теперь немного про технические метрики рексистем.
Это не исчерпывающее руководство, просто примеры для понимания:
Precision@K
Если система показала пользователю K рекомендаций, какая доля из них реально оказалась ему полезна?
Это базовая метрика точности, которая отвечает на вопрос о качестве выдачи, а не о полноте охвата.
Допустим, вы разбили данные студента на обучающую и тестовую выборку, обучили модель на восьмидесяти процентах истории, а затем попросили её порекомендовать пять товаров из оставшихся двадцати процентов. Если из пяти рекомендованных товаров студент в реальности положительно оценил только два, то Precision@5 составит сорок процентов.
Метрика не учитывает порядок внутри топа и не штрафует за пропущенные релевантные товары, которые не попали в выдачу. Она показывает исключительно чистоту того, что увидит пользователь прямо сейчас.
Coverage
Какую долю всего каталога система способна рекомендовать пользователям?
Если в вашем каталоге десять тысяч товаров, а алгоритм рекомендует только пятьсот самых популярных, покрытие составляет пять процентов.
Низкое покрытие означает игнорирование длинного хвоста, что со временем снижает маржинальность и лишает новинки шансов на продвижение. Высокое покрытие говорит о том, что модель исследует весь ассортимент, но требует контроля качества, чтобы не показывать откровенный мусор.
На практике метрику считают как отношение уникальных рекомендованных товаров к общему числу доступных товаров за выбранный период. Это индикатор здоровья системы, а не точности предсказаний.
Novelty
Насколько рекомендуемые товары неочевидны или непопулярны для конкретного пользователя?
Система может выдавать идеально релевантные товары, которые пользователь и так давно знает или с которыми постоянно положительно взаимодействует (хорошо оценивает, периодически покупает и т.п.).
Новизна штрафует такие выдачи и поощряет рекомендации, находящиеся вне прямой истории взаимодействий.
Обычно метрику рассчитывают через обратную популярность товара в логарифмической шкале, умноженную на вероятность того, что пользователь ещё не сталкивался с этим объектом.
В образовательном сервисе это может быть урок по смежной теме, который студент ещё не открывал, но который логично вписывается в его траекторию. Высокая новизна без потери релевантности удерживает внимание [16] и снижает эффект усталости от однообразия.
Diversity
Насколько сильно отличаются друг от друга товары внутри одной выдачи для одного пользователя?
Если все пять рекомендованных уроков посвящены одной грамматической теме, система имеет низкое разнообразие. Даже если каждый урок идеально подходит по уровню, пользователь быстро потеряет интерес из-за монотонности.
Разнообразие измеряют через попарное сравнение признаков товаров в выдаче, например через косинусное расстояние между их векторами или разницу категорий. В рекомендациях это работает как встроенный регулятор, который не даёт модели зацикливаться на одном кластере интересов.
Баланс между точностью и разнообразием настраивается вручную через весовые коэффициенты в финальном скоринге.
Serendipity
Способность системы рекомендовать товары, которые пользователь не искал, но неожиданно высоко оценил.
Это самая сложная для автоматизации метрика, потому что она сочетает релевантность с непредсказуемостью.
Формально её можно оценить как долю рекомендаций, которые попали в топ взаимодействий пользователя, но при этом имели низкий базовый скор популярности или не пересекались с его историей по тегам.
В реальной жизни это срабатывает, когда студент получает рекомендацию по культуре страны, а не грамматике, и вдруг понимает, что это именно то, что мотивировало его начать учить язык.
Serendipity почти невозможно вычислить идеально, но её эвристические аналоги помогают добавлять в ленту элементы исследовательского характера.
Recall@K
Какую долю всех потенциально релевантных товаров система успела захватить в свою выдачу размера K?
В отличие от Precision, которая смотрит на качество показанного, Recall оценивает полноту охвата.
Если у пользователя в тестовой выборке двадцать релевантных товаров, а модель в топ десять включила только четыре, метрика составит двадцать процентов. Этот показатель критичен в сценариях поиска или подбора, где пропуск важного элемента стоит дороже, чем показ лишнего (например: поиск вакансий, подбор курсов, медицинские рекомендации).
На практике Recall@K и Precision@K используют в паре, так как они находятся в обратной зависимости: расширяя выдачу, вы ловите больше нужного (растёт Recall), но неизбежно разбавляете ленту шумом (падает Precision).
NDCG@K
Оценка качества ранжирования, которая учитывает, что релевантные товары на верхних позициях важнее, чем на нижних.
Аббревиатура расшифровывается как Normalized Discounted Cumulative Gain (Нормализованный дисконтированный кумулятивный выигрыш). Звучит сложно, но идея простая: релевантный товар вверху списка ценится гораздо выше, чем тот же товар внизу.
Пользователь редко смотрит дальше 3–5 позиций. Поэтому попадание на 1-е место даёт системе максимум баллов, на 5-е – существенно меньше. Метрика буквально “дисконтирует” ценность товара с ростом его позиции в списке.
Главное преимущество NDCG перед Precision/Recall – она работает с градациями релевантности. Не кликнул/не кликнул, а открыл -> посмотрел 10 секунд -> добавил в корзину -> купил. Итоговое значение всегда от 0 до 1: единица означает идеально отсортированную выдачу, ноль – полную ерунду. Это отраслевой стандарт для поисковиков и персонализированных лент, где порядок выдачи решает всё.
Отдельно замечу, что у любого коммерческого проекта есть и “бизнесовые”, маркетинговые метрики, типа: CTR, LTV, конверсия, обращения в поддержку и т.п.
И хотя между вышеперечисленными техническими метриками рексистем и “бизнесовыми” метриками проекта есть взаимосвязь, она как правило не прямая и не мгновенная.
Например, высокая доля coverage (покрытие каталога в рекоммендациях) и качественный контент могут улучшать удержание пользователя и CTR, а высокий Precision@K как правило будет повышать LTV.
Но чтобы понять это, надо продолжительное время: а.) поддерживать рексистему б.) собирать и сравнивать метрики.
Опять же, не исчерпывающее руководство – просто примеры. Условно, поделю на ошибки данных (почему нужен правильный формат, правильно причесывать) и ошибки на уровне продукта/архитектуры.
Начнём с типовых ошибок на уровне данных:
Неконсистентность
Разные форматы дат, дубли сущностей, со временем – изменение неизменяемого. Модель начинает давать странные результаты не из-за алгоритма, а из-за мусора на входе.
Ошибка в самой первой картинке этой статьи – использование числового age (возраста) вместо birth_year (года рождения) для нахождения пользователей, схожих по возрасту. Age меняется со временем, а birth_year – нет, из-за чего при использовании age многие пользователи будут то схожи, то не схожи в зависимости от дня в году. И т.д.
Игнорирование негативных сигналов
Обучение только на кликах и покупках без учёта пропусков, скрытий, быстрых выходов и дизлайков. Без негативных примеров система не понимает, что показывать точно не надо.
Утечка будущего в прошлое
В обучающую выборку попадают признаки, которые стали известны только после взаимодействия. Например, модель предсказывает клик, а в фичи уже зашито время просмотра. На тестах метрики хорошие, в продакшене – бяка.
Магические пропуски
Слепая замена пустых значений нулями или средним. Для модели это создаёт искусственные закономерности. Пропуски нужно либо явно маркировать, либо обрабатывать отдельно для каждого типа признака.
Дрейф данных
Поведение [17] пользователей, популярность товаров или сезонность меняются, а веса модели остаются старыми. Метрики плавно деградируют. Решение – мониторинг распределений признаков и регулярное переобучение.
Отсутствие версионирования
Невозможно понять, какая версия датасета или фичей обучила текущую модель. При падении качества или необходимости отката приходится гадать. Нужен чёткий учёт версий данных, кода и весов.
Ошибки уровня продукта и архитектуры:
Нейросеть или “непрозрачный” продукт на старте
Сложная модель требует много данных, вычислительных ресурсов и экспертизы. На раннем этапе простая эвристика или базовая коллаборативная фильтрация часто работают лучше и быстрее. Сложность добавляют только когда простые методы уперлись в потолок.
Рекомендации только популярных товаров
Алгоритм скатывается в выдачу бестселлеров, потому что они дают стабильный CTR. Длинный хвост и новинки игнорируются. Система выглядит рабочей, но персонализации не происходит.
Игнорирование холодного старта
Новый пользователь или новый товар попадают в систему, а выдача остаётся пустой или случайной. Без отдельной стратегии (онбординг, контентные признаки, популярные категории, тематические подборки) теряется первый контакт.
Скорость против точности
Попытка запускать тяжёлую модель “на лету” для каждого запроса. Пользователь ждёт, серверы перегружаются, метрики падают. Правильный подход – оффлайн генерация кандидатов и лёгкое онлайн-ранжирование или кеширование.
Оптимизация под неверную метрику
Погоня за CTR или временем сессии без учёта качества приводит к кликбейту, выгоранию аудитории и оттоку. Техническая метрика должна коррелировать с бизнес-целью, а не подменять её.
Отсутствие фолбека при проблемах с API
Если модель упала, сеть лагает или данные не подгрузились, пользователь видит пустой экран. Нужен жёсткий запасной вариант: популярные товары, статические подборки, последний успешный кеш. Система должна работать всегда, даже с временной деградацией качества.
Рекомендательная система – не волшебная палочка и не обязательный атрибут “цифрового продукта”. Это инструмент, который работает ровно настолько хорошо, насколько хорошо вы понимаете свои данные, цели пользователей и бизнес-процессы.
За всеми разговорами про нейросети, эмбеддинги и “умный ИИ” стоит простая истина: чаще всего бизнесу нужна не самая сложная модель, а самая предсказуемая, интерпретируемая и быстрая. Начинайте с малого и двигайтесь в сторону усложнения – постепенно. Не гонитесь за “чёрными ящиками”, пока не упрётесь в потолок простых методов.
Если рекомендательная система понятна, надёжна и ведёт в правильную сторону, она окупит вложения многократно. Если же строить её на хайпе, неподготовленных таблицах и слепой вере в “автопилот” – получится дорого, сложно и бесполезно.
Прагматизм [18] важнее моды, прозрачность важнее сложности, а измерения – важнее догадок.
Спасибо за внимание.
P.s.: Текст разрешается копировать только в неизменном оригинальном виде, с указанием авторства и ссылкой на оригинал.
Автор: tkutru
Источник [19]
Сайт-источник BrainTools: https://www.braintools.ru
Путь до страницы источника: https://www.braintools.ru/article/29506
URLs in this post:
[1] ошибку: http://www.braintools.ru/article/4192
[2] логика: http://www.braintools.ru/article/7640
[3] коэффициент Жаккара: https://ru.wikipedia.org/wiki/%D0%9A%D0%BE%D1%8D%D1%84%D1%84%D0%B8%D1%86%D0%B8%D0%B5%D0%BD%D1%82_%D0%96%D0%B0%D0%BA%D0%BA%D0%B0%D1%80%D0%B0
[4] K-ближайших соседей: https://ru.wikipedia.org/wiki/%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_k_%D0%B1%D0%BB%D0%B8%D0%B6%D0%B0%D0%B9%D1%88%D0%B8%D1%85_%D1%81%D0%BE%D1%81%D0%B5%D0%B4%D0%B5%D0%B9
[5] например сюда: https://www.w3schools.com/sql/trysql.asp?filename=trysql_select_like_not
[6] SVD, ALS: https://en.wikipedia.org/wiki/Matrix_factorization_(recommender_systems)
[7] интересом: http://www.braintools.ru/article/4220
[8] LightGBM: https://en.wikipedia.org/wiki/LightGBM
[9] XGBoost: https://en.wikipedia.org/wiki/XGBoost
[10] two-tower: https://blog.reachsumit.com/posts/2023/03/two-tower-model/
[11] обучение: http://www.braintools.ru/article/5125
[12] экспериментально: https://share.google/x5nyjzrl550jEYEYI
[13] PHP: https://onlinephp.io/
[14] D: https://en.wikipedia.org/wiki/Dependency_inversion_principle
[15] SOLID: https://en.wikipedia.org/wiki/SOLID
[16] внимание: http://www.braintools.ru/article/7595
[17] Поведение: http://www.braintools.ru/article/9372
[18] Прагматизм: http://www.braintools.ru/article/7669
[19] Источник: https://habr.com/ru/articles/1019070/?utm_source=habrahabr&utm_medium=rss&utm_campaign=1019070
Нажмите здесь для печати.