Привет, меня зовут Михаил Ройзнер. Недавно я выступил перед студентами Малого Шада Яндекса с лекцией о том, что такое рекомендательные системы и какие методы там бывают. На основе лекции я подготовил этот пост. План лекции: Начнем с простого: что вообще такое рекомендательные системы, и какими они бывают. Наверное, все уже сталкивались с ними в интернете. Первый пример — рекомендательные системы фильмов.   На сайте imdb.com пользователи могут оценивать фильмы по десятибалльной шкале. Оценки агрегируются, получается средний рейтинг фильма. На этом же сайте есть блок с рекомендациями для конкретного пользователя. Если я зашел на сайт и оценил несколько фильмов, imdb сможет порекомендовать мне еще какие-нибудь фильмы. Похожий блок есть и на фейсбуке.  Нечто сходное, но только для музыки, делает last.fm. Он рекомендует мне исполнителей, альбомы, мероприятия, на которые мне стоит сходить. Сервис Pandora в России почти неизвестен, т.к. у нас он не работает, однако в Америке он очень популярен. Это такое персональное радио, которое постепенно подстраивается под пользователя на основе его оценок, и в итоге играет только те треки, которые ему нравятся.   Еще одна известная область — рекомендация товаров. На картинке ниже у нас, конечно же, Amazon. Если вы купили что-то на амазоне, за вами будут охотиться с дополнительными предложениями: похожими товарами или аксессуарами. Это хорошо и для пользователей (им не нужно искать эти товары самостоятельно), и конечно, это хорошо для самого магазина.   Мы перечислили три категории, но на самом деле их гораздо больше: заведения на карте, новости, статьи, сайты, концерты, театры, выставки, видео, книги, приложения, игры, путешествия, социальные связи и многое другое. Можно выделить два основных типа рекомендательных систем. Их, конечно же больше, но мы сегодня будем рассматривать именно эти и в особенности коллаборативную фильтрацию. Рекомендательные системы появились в интернете достаточно давно, около 20 лет назад. Однако настоящий подъем в этой области случился примерно 5-10 лет назад, когда произошло соревнование Netflix Prize. Компания Netflix тогда давала в прокат не цифровые копии, а рассылала VHS-кассеты и DVD. Для них было очень важно повысить качество рекомендаций. Чем лучше Netflix рекомендует своим пользователям фильмы, тем больше фильмов они берут в прокат. Соответственно, растет и прибыль компании. В 2006 году они запустили соревнование Netflix Prize. Они выложили в открытый доступ собранные данные: около 100 миллионов оценок по пятибалльной шкале с указанием ID проставивших их пользователей. Участники соревнования должны были как можно лучше предугадывать, какую оценку поставит определенному фильму тот или иной пользователь. Качество предсказания измерялось при помощи метрики RMSE (средне-квадратичное отклонение). У Netflix уже был алгоритм, который предсказывал оценки пользователей с качеством 0.9514 по метрике RMSE. Задача была улучшить предсказание хотя бы на 10% — до 0.8563. Победителю был обещан приз в $ 1 000 000. Соревнование длилось примерно три года. За первый год качество улучшили на 7%, дальше все немного замедлилось. Но в конце две команды с разницей в 20 минут прислали свои решения, каждое из которых проходило порог в 10%, качество у них было одинаковое с точностью до четвертого знака. В задаче, над которой множество команд билось три года, все решили каких-то двадцать минут. Опоздавшая команда (как и многие другие, участвовавшие в конкурсе) остались ни с чем, однако сам конкурс очень сильно подстегнул развитие в этой области. Для начала формализуем нашу задачу. Что мы имеем? У нас есть множество пользователей &#119906 ∈ &#119880, множество объектов &#119894 ∈ &#119868 (фильмы, треки, товары и т.п.) и множество событий (&#119903&#119906&#119894, &#119906, &#119894, ...) ∈ &#119967 (действия, которые пользователи совершают с объектами). Каждое событие задается пользователем &#119906, объектом &#119894, своим результатом &#119903&#119906&#119894 и, возможно, еще какими-то характеристиками. От нас требуется: Допустим, нам дана таблица с оценками пользователей:  На нужно как можно лучше предсказать, какие оценки должны быть в ячейках со знаками вопроса:  Напомним, что основная идея коллаборативной фильтрации — похожим пользователям обычно нравятся похожие объекты. Начнем с самого простого метода.   У этого алгоритма есть несколько проблем:   Однако у этого алгоритма также есть свои проблемы:   Проблемы: Для этого алгоритма нам потребуется несколько понятий из линейной алгебры: векторы, матрицы и операции с ними. Я не буду здесь приводить все определения, если вам нужно освежить эти знания, то все объяснения есть в видеозаписи лекции примерно с 33 минуты.  SVD (Singular Value Decomposition), переводится как сингулярное разложение матрицы. В теореме о сингулярном разложении утверждается, что у любой матрицы &#119808 размера &#119899×&#119898 существует разложение в произведение трех матриц: &#119880, &#425 &#1080 &#119881&#119879:   Матрицы &#119880 и &#119881 ортогональные, а &#425 — диагональная (хотя и не квадратная).   Причем лямбды в матрице &#425 будут упорядочены по невозрастанию. Сейчас мы эту теорему не будем доказывать не будем, просто воспользуемся самим разложением. Но что нам с того, что мы можем какую-то матрицу в произведение трех еще более непонятных матриц. Для нас тут будет интересно следующее. Помимо обычного разложения бывает еще усеченное, когда из лямбд, остаются только первые &#119889 чисел, а остальные мы полагаем равными нулю.   Так вот, оказывается, что полученная матрица &#119808′ хорошо приближает исходную матрицу &#119808 и, более того, является наилучшим низкоранговым приближением с точки зрения средне-квадратичного отклонения.  Как же использовать все это для рекомендаций? У нас была матрица, мы разложили ее в произведение трех матриц. При чем разложили не точно, а приблизительно. Упростим все немного, обозначив произведение первых двух матриц за одну матрицу:    Теперь отвлечемся немного от всех этих матриц и сконцентрируемся на получившемся алгоритме: чтобы предсказать оценку пользователя &#119880 для фильма &#119868, мы берем некоторый вектор &#119901&#119906 (набор параметров) для данного пользователя и вектор для данного фильма &#119902&#119894. Их скалярное произведение и будет нужным нам предсказанием: &#119903&#770&#119906&#119894 = &#10216&#119901&#119906,&#119902&#119894&#10217.  Алгоритм достаточно простой, но дает удивительные результаты. Он не просто позволяет нам предсказывать оценки. С его помощью мы можем по истории пользователей выявлять скрытые признаки объектов и интересы пользователей. Например, может так получиться, что на первой координате вектора у каждого пользователя будет стоять число, показывающее, похож ли пользователь больше на мальчика или на девочку, на второй координате — число, отражающее примерный возраст пользователя. У фильма же первая координата будет показывать, интересен ли он больше мальчикам или девочкам, а вторая — какой возрастной группе пользователей он интересен. Но не все так просто. Есть несколько проблем. Во-первых, матрица оценок &#119929 нам полностью не известна, поэтому мы не можем просто взять ее SVD-разложение. Во-вторых, SVD-разложение не единственное: (&#119880Ω)&#425(&#119881Ω)&#119879=&#119880&#425&#119881&#119879, поэтому даже если мы найдем хоть какое-то разложение, вряд ли именно в нем первая координата будет соответствовать полу пользователя, а вторая — возрасту.

 Попробуем разобраться с первой проблемой. Тут нам понадобится машинное обучение. Итак, мы не можем найти SVD-разложение матрицы, т.к. мы не знаем саму матрицу. Но мы хотим воспользоваться этой идеей и придумать модель предсказания, которая будет работать сходным с SVD образом. Наша модель будет зависеть от многих параметров — векторов пользователей и фильмов. Для заданных параметров, чтобы предсказать оценку, мы возьмем вектор пользователя, вектор фильма и получим их скалярное произведение:   Но так как векторов мы не знаем, их еще нужно получить. Идея заключается в том, что у нас есть оценки пользователей, при помощи которых мы можем найти такие оптимальные параметры, при которых наша модель предсказывала бы эти оценки как можно лучше:   Итак, мы хотим найти такие параметры θ, чтобы квадрат ошибки был как можно меньше. Но тут есть парадокс: мы хотим меньше ошибаться в будущем, но мы не знаем, какие оценки у нас будут спрашивать. Соответственно и оптимизировать это мы не можем. Но нам известны уже проставленные пользователями оценки. Попробуем подобрать параметры так, чтобы на тех оценках, которые у нас уже есть, ошибка была как можно меньше. Кроме того, добавим еще одно слагаемое — регуляризатор.   Регуляризация нужна для борьбы с переобучением — явлением, когда построенная модель хорошо объясняет примеры из обучающей выборки, но достаточно плохо работает на примерах, не участвовавших в обучении. Вообще, методов борьбы с переобучением существует несколько, хочется отметить два из них. Во-первых, нужно выбирать простые модели. Чем проще модель, тем лучше она обобщается на будущие данные (это похоже на известный принцип бритвы Оккама). А второй метод — это как раз регуляризация. Когда мы настраиваем модель для обучающей выборки, мы оптимизируем ошибку. Регуляризация заключается том, что мы оптимизируем не просто ошибку, а ошибку плюс некоторая функция от параметров (например, норма вектора параметров). Это позволяет ограничить размер параметров в решении, уменьшает степень свободы модели. Как же нам найти оптимальные параметры? Нам нужно оптимизировать вот такой функционал:   Параметров много: для каждого пользователя, для каждого объекта у нас есть свой вектор, который мы хотим оптимизировать. У нас есть функция, зависящая от большого количества переменных. Как найти ее минимум? Тут нам потребуется градиент — вектор из частных производных по каждому параметру.    Градиент очень удобно представлять себе визуально. На иллюстрации у нас изображена поверхность: функция от двух переменных. Например, высота над уровнем моря. Тогда градиент в какой-нибудь конкретной точке — это такой вектор, направленный в ту сторону, куда больше всего растет наша функция. А если пустить воду из этой точки, она потечет по направлению противоположному градиенту.   Самый известный метод оптимизации функций — градиентный спуск. Допустим у нас есть функция от многих переменных, мы хотим ее оптимизировать. Мы берем какое-нибудь изначальное значение, а потом смотрим, куда можно двигаться, чтобы минимизировать это значение. Метод градиентного спуска — это итеративный алгоритм: он многократно берет параметры определенной точки, смотрит на градиент и шагает против его направления:     Проблемы этого метода заключаются в том, что он, во-первых, в нашем случае работает очень медленно и, во-вторых, находит локальные, а не глобальные минимумы. Вторая проблема для нас не так страшна, т.к. в нашем случае значение функционала в локальных минимумах оказывается близким к глобальному оптимуму. Однако метод градиентного спуска применять нужно не всегда. Например, если нам нужно посчитать минимум для параболы, действовать этим методом необходимости нет, мы точно знаем, где находится ее минимум. Оказывается, что функционал, который мы пытаемся оптимизировать — сумма квадратов ошибок плюс сумма квадратов всех параметров — это тоже квадратичный функционал, он очень похож на параболу. Для каждого конкретного параметра, если мы зафиксируем все остальные, это будет как раз параболой. Т.е. минимум по одной координате мы можем точно определить. На этом соображении и основан метод Alternating Least Squares. Я не буду на нем подробно останавливаться. Скажу лишь, что в нем мы попеременно точно находим минимумы то по одним координатам, то по другим:   Мы фиксируем все параметры объектов, оптимизируем точно параметры пользователей, дальше фиксируем параметры пользователей и оптимизируем параметры объектов. Действуем итеративно:   Работает все это достаточно быстро, при этом каждый шаг можно распараллелить. Если мы хотим улучшить качество рекомендаций, нам нужно научиться его измерять. Для этого алгоритм, обученный на одной выборке — обучающей, проверяется на другой — тестовой. Netflix предложил измерять качество рекомендаций по метрике RMSE:   Сегодня это стандартная метрика для предсказания оценки. Однако у нее есть свои недостатки: Существуют и другие метрики — метрики ранжирования, например, основанные на полноте и точности. Пусть &#119929 — множество рекомендованных объектов, &#119927 — множество объектов, которые на самом деле пользователю понравятся.   Тут тоже есть проблемы: Похожесть объектов не такая уж очевидная вещь. К этой задаче могут быть разные подходы:  Разработчик-исследователь