← к модулям

Content-based (TF-IDF)

Каждый фильм описывается числовым вектором из жанров и тегов; похожесть — косинус между векторами. Мнения других пользователей не нужны.

    Теория простым языком

    Content-based рекомендует «похожее на похожее»: если тебе зашёл фильм, найдём другие фильмы, похожие на него по описанию — по жанрам и тегам. Мнения других людей здесь вообще не нужны, смотрим только на сам фильм.

    Главная мысль

    Чтобы находить «похожие» фильмы, надо сначала научиться измерять похожесть. А чтобы её измерить — описать каждый фильм числами. Разберём это по шагам, вводя слова по дороге.

    Признак (фича)свойство айтема, которым мы его описываем. У фильма это, например, жанры («комедия», «боевик») и теги, которые ставят зрители («атмосферный», «основано на реальных событиях»).

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

    Почему появился content-based подход
    БылоPopularity показывает одно и то же всем.
    ПроблемаХочется учитывать вкус конкретного человека, но поведения других о нужном айтеме может быть мало или совсем нет (новый айтем).
    ИдеяРекомендовать айтемы, похожие по содержанию на то, что пользователь уже любил.
    Стало лучшеПерсонализация, работает для холодного айтема (есть признаки), рекомендации легко объяснить («похоже по жанру»).
    Осталось слабымПузырь фильтров (только «ещё такое же»), зависимость от качества метаданных, нет коллективной мудрости.
    ДальшеCollaborative filtering — использовать поведение многих пользователей, а не только признаки айтема.

    Шаг 1. Превращаем фильм в числа

    Компьютер не понимает слова «комедия», ему нужны числа. Поэтому каждому фильму сопоставляют длинный список чисел — по одному на каждое возможное слово (жанр/тег). Если слово есть у фильма — число больше нуля, если нет — ноль.

    Векторпросто список чисел фиксированной длины. «Вектор фильма» — это его описание, переведённое в числа.

    Но не все слова одинаково полезны. Жанр «драма» есть у тысяч фильмов и почти ничего не говорит, а тег «киберпанк» — редкий и очень информативный. Чтобы редкие слова весили больше, используют приём:

    TF-IDFспособ расставить веса словам: часто встречающееся у фильма слово получает вес побольше (TF), но если это слово есть почти у всех фильмов — вес снова снижается (IDF). Итог: редкие меткие слова важнее банальных.

    Шаг 2. Меряем похожесть

    Теперь у каждого фильма есть вектор. Похожесть двух фильмов — это насколько их векторы «смотрят в одну сторону».

    Косинусное сходствонасколько «в одну сторону» направлены два вектора, независимо от их длины. В общем случае это число от −1 до 1, но у нас все TF-IDF-веса неотрицательные, поэтому косинус не может стать отрицательным и лежит в диапазоне 0…1: 1 — фильмы очень похожи по описанию, 0 — ничего общего.
    Пример. У «Истории игрушек» вектор — это «мультфильм + комедия + приключения». У «Истории игрушек 2» почти такой же набор → косинус близко к 1 → их и порекомендуем как похожие.

    Шаг 3. Рекомендации лично тебе

    «Похожие на один фильм» — это полдела. Чтобы советовать персонально, строят твой портрет из того, что тебе уже понравилось (оценки от 4 и выше) — низкие оценки в профиль не идут, иначе мы бы учили модель на том, что тебе как раз не зашло.

    Профиль пользователяусреднённый вектор всех фильмов, которые человек высоко оценил. Грубо говоря, «средний фильм твоей мечты» в числах. Дальше ищем реальные фильмы, чьи векторы ближе всего к этому профилю.
    Псевдокод
    # офлайн: один раз превращаем фильмы в векторы
    для каждого фильма i:
        v[i] = tf-idf(жанры и теги фильма i)
    
    recommend(пользователь u, K):
        liked = фильмы, которым u поставил оценку ≥ 4
        profile = среднее из нормированных v[i] по liked
        для каждого фильма i, не виденного u:
            score[i] = cosine(profile, v[i])
        вернуть K фильмов с наибольшим score
    Формулы: TF-IDF, косинус, профиль

    Вес слова tt в фильме dd — это TF-IDF:

    wt,d=tft,dlogNdftw_{t,d} = \mathrm{tf}_{t,d}\cdot \log\frac{N}{\mathrm{df}_t}

    где tft,d\mathrm{tf}_{t,d} — как часто слово встречается у фильма, dft\mathrm{df}_t — у скольких фильмов оно есть вообще, NN — всего фильмов. Редкое слово (маленький dft\mathrm{df}_t) → большой log\log → больший вес.

    Косинусное сходство двух векторов:

    cos(a,b)=abab=tatbttat2tbt2\cos(a,b) = \frac{a\cdot b}{\lVert a\rVert\,\lVert b\rVert} = \frac{\sum_t a_t b_t}{\sqrt{\sum_t a_t^2}\,\sqrt{\sum_t b_t^2}}

    Деление на длины a,b\lVert a\rVert,\lVert b\rVert и делает меру независимой от длины описания. При неотрицательных весах числитель ≥ 0, поэтому cos[0,1]\cos\in[0,1].

    Профиль пользователя uu — среднее нормированных векторов понравившихся фильмов Lu={i:rui4}L_u=\{i: r_{ui}\ge 4\}:

    pu=1LuiLuvivip_u = \frac{1}{|L_u|}\sum_{i\in L_u}\frac{v_i}{\lVert v_i\rVert}

    Сильная сторона: холодный старт айтема

    Холодный стартситуация, когда о ком-то/чём-то ещё нет данных. Холодный старт айтема — новый фильм, который никто не успел оценить.

    Content-based с этим справляется: даже у только что вышедшего фильма есть жанры и описание, а значит — вектор. Его можно рекомендовать сразу, не дожидаясь ни одной оценки. Это его главный козырь.

    Сильные стороны

    • Решает холодный старт айтема: новому фильму хватает его описания.
    • Объяснимость: «похоже по жанрам/тегам» — понятная причина рекомендации.
    • Не зависит от других пользователей — работает даже с одним человеком.

    Слабые стороны

    • Замыкает в «пузыре»: советует только похожее на уже виденное, без сюрпризов.
    • Качество ограничено качеством описаний: по одним жанрам легко промахнуться.
    • Слабая точность персональных топ-списков (это видно по метрикам на этой странице).

    ⚠️ Что может пойти не так

    • Качество = качество признаков: если у фильмов только жанры (как часто бывает), векторы грубые и похожесть смазана.
    • «Пузырь фильтров»: метрики разнообразия (diversity) и новизны (novelty) у content-based обычно низкие — он крутит одно и то же по одному признаку.
    • Профиль усредняет всё подряд: у человека с очень разными вкусами «средний» вектор оказывается в пустоте между кластерами и плохо описывает любой из них.
    • Низкие оценки нельзя кидать в профиль наравне с высокими — иначе нелюбимое тянет рекомендации к себе.

    🧠 Проверь себя: Почему новый фильм content-based может рекомендовать сразу, а коллаборативная фильтрация — нет?