Перевод статьи "Netflix. System Architectures for Personalization and Recommendation".
Перевод неточный, некоторые куски пропущены, так как показались мне повторением уже высказанных выше мыслей, некоторые - уточнены и дополнены примерами.
В предыдущих постах о персонализации в Netflix уже говорилось о том, что и данные, и алгоритмы их обработки одинаковы важны в задаче персонализации пользовательского опыта. Также важно вовлекать пользователя в систему рекомендаций - получать от него так больше откликов и данных. Сегодня мы поговорим о том, как создать архитектуру, которая может максимизировать пользу от собираемых данных и поддерживает возможность быстрого внедрения нововведений. Разработка архитектуры, которая
обрабатывает огромные объемы уже существующих данных,
чутко реагирует на пользовательские взаимодействия с системой
даёт возможность легко экспериментировать с новыми подходами к рекомендациям
не является тривиальной задачей. В этом посте мы поговорим о том, как решает некоторые из этих проблем Netflix.
Для начала рассмотрите общую схему архитектуры рекомендательных систем Нетфликса. Основные компоненты архитектуры содержат один или несколько алгоритмов машинного обучения.
Самое простое, что можно делать с данными - хранить их, чтобы потом спокойненько обработать в оффлайне, за эту часть на диаграмме отвечает зона “Offline jobs”. Но вычисления могут производиться не только оффлайн, но ещё и ниарлайново (nearline) и онлайново. Онлайновые вычисления лучше отвечают на недавние события и пользовательские взаимодействия (на сайт был вот только что загружен новый боевик или вы как пользователь только что лайкнули картинку с совой), но они должны отвечать на запросы в реальном времени, и это существенно ограничивает вычислительную сложность алгоритмов, использующихся в онлайне. Количество данных, которые можно обработать в онлайне не отходя от кассы, тоже ограничено требованиями по скорости. У офлайновых вычислений куда меньше подобных ограничений, так как они вычисляются батчами, и у них довольно мягкие тайминги. Тем не менее, результаты таких вычислений могут быть неактуальными для юзера, потому что самые недавние данные не были там учтены (например то, что в последние 2 часа вы резко заинтересовались уличным балетом). Это и является одной из ключевых проблем построения архитектуры персонализации - нужно совместить онлайновые и офлайновые вычисления так, чтоб они работали как одно целое - гармонично, быстро, точно. Вычисления типа nearline - это компромисс между онлайном и офлайном, здесь мы можем производить вычисления онлайн, но не требуем от них мгновенного выполнения - например, вы начали лайкать собак в ленте, а лента начала предлагать вам больше собак только через 5 минут.
Тренировка модели (Model training) - это тоже вычисления, в результате которого мы получаем обученную на имеющихся данных модель, которая позже нам и выдаёт рекомендации.
Ещё одна часть архитектуры - система распределения событий и данных (Event and Data Distribution), она описывает как события и данных разных типов обрабатываются в системе. Похожая задача - как обработать разные сигналы и модели (Signals and Models), мы подробнее поговорим о том, что это такое ниже. Ну и в конце-то концов, нам нужно из всех наших частей системы собрать какой-то усреднённый результат рекомендаций (Recommendation Result) , который для нашего пользователя будет представлять интерес.
Далее мы более детально поговорим обо всех компонентах архитектуры, а также об их взаимодействиях, для этого разобьём общую диаграмму на части и пройдёмся по каждой их них. Полезно иметь в виду, что вся инфраструктура крутится на публичном облаке AWS.
Online, nearline и offline вычисления
Онлайн-вычисления могут быстро реагировать на недавние события и использовать самые свежие данные. Например, создание галереи фильмов-боевиков, отсортированных для юзера с использованием его текущего контекста. Но онлайн-вычисления должны отвечать Соглашению об уровне доступности и времени отклика (Service Level Agreement, SLA), которое определяет максимальную задержку при ответе на запросы от клиентских приложений, иными словами время, пока наш юзер ожидает появления рекомендаций, не резиновое. Это может затруднить внедрение вычислительно сложных алгоритмов. Кроме того, при некоторых обстоятельствах онлайновые вычисления в чистом виде могут не соответствовать SLA , поэтому всегда важно продумать быстрый запасной механизм, например, возврат к предыдущему, уже вычисленному результату. Вычисления в режиме онлайн также означают, что задействованные источники данных также должны быть доступны в режиме онлайн, и это может потребовать дополнительной инфраструктуры.
С другой стороны, офлайновые вычисления позволяют использовать более сложные алгоритмы, и у них нет таких жёстких ограничений на количество обрабатываемых данных.. Простой пример — периодическое обобщение миллионов просмотров, чтоб сделать базовые общие рекомендации популярных фильмов. В офлайновые системы можно проще внедрять новые алгоритмы без необходимости тратить кучу времени на их оптимизацию. Netflix использует офлайн-вычисления для поддержки быстрого экспериментирования: если новый экспериментальный алгоритм выполняется медленнее, можно просто развернуть больше экземпляров Amazon EC2 для достижения пропускной способности, необходимой для запуска эксперимента, вместо того, чтобы тратить драгоценное время инженеров на оптимизацию алгоритма, который в итоге может оказаться не таким ценным для бизнеса. Но офлайн-алгоритм не будет быстро реагировать на изменения контекста или новые данные. "Заторможенность" офлайн рекомендаций может привести к “застою” рекомендаций, что, в свою очередь, может ухудшить пользовательский опыт. Офлайн-вычисления также требуют инфраструктуру для хранения, вычисления и доступа к большим наборам предварительно вычисленных результатов.
Nearline вычисления можно рассматривать как компромисс между двумя предыдущими типами. В этом случае вычисления выполняются точно так же, как и в онлайн подходе, но мы больше не требуем от них предоставлять результаты сразу после вычисления, а вместо этого храним их, делаем асинхронными. Это открывает возможности для потенциально более сложной обработки событий. Примером может служить обновление статуса фильма на “просмотрен” сразу после того, как юзер начинает его смотреть. Результаты могут быть сохранены в кеш или на бэкенде. Ниарлайновые вычисления также являются полем для алгоритмов инкрементного обучения.
В любом случае, мы можем не выбирать какой-то из этих подходов, ведь их можно и нужно совмещать. Есть много способов построить такую совмещённую архитектуру. Мы уже упоминали идею использования офлайн вычислений в качестве запасного аэродрома при отказе онлайн вычислений. Другой вариант - предварительно вычислить часть результата с помощью офлайн-процесса и оставить менее вычислительно сложные или более контекстно-зависимые части алгоритмов для онлайн-вычислений.
Даже моделирование может происходить в гибридном режиме оффлайн/онлайн. Обычно такой подход не используется для классификации с учителем, он применяется только в онлайне для классификации новых входных данных. Однако такие подходы, как матричная факторизация, более естественно подходят для гибридного онлайн/оффлайн моделирования: некоторые этапы могут быть предварительно вычислены в офлайн режиме, а другие могут быть обновлены в режиме реального времени. Подходы без учителя, такие как кластеризация, допускают офлайн-вычисление центров кластеров и онлайн-назначение кластеров. Эти примеры показывают, что мы можем разделять процесс обучения модели на части - “тяжёлое”, масштабное, потенциально довольно сложное обучение и более лёгкое обучение/обновление под пользователя.
Офлайн вычисления
Большая часть вычислений для рекомендаций может быть выполнена офлайн. Значит, мы можем запускать процессы периодически и в заданное нами время. Результат выполнения этих процессов не привязан к внешним запросам, так как они используют уже посчитанные данные. Существует два основных процесса, которые подпадают под описанную категорию: обучение модели и вычисление промежуточных или конечных результатов батчами. В процессе обучения модели мы собираем существующие данные и применяем алгоритм машинного обучения, который на выходе даёт набор параметров/весов модели (который мы и будем дальше называть моделью). Эта модель обычно кодируется и сохраняется в файле для последующего использования. Хотя большинство моделей обучаются в офлайн-режиме батчами, у нас также есть методы при которых инкрементное обучение выполняется онлайн. Вычисление результатов означает, что мы с помощью предобученной модели что-то посчитаем, а потом передадим либо в онлайн-обработку, либо напрямую пользователю.
Обе эти задачи требуют предобработанных и систематизированных данных, которые обычно мы получаем из запросов к базе. Они гоняют большие объёмы данных, и поэтому полезно запускать их распределенным способом. С распределением запросов отлично справляется Hadoop с помощью джобов Hive или Pig. Логично, что нам нужен механизм публикации результатов запросов, то есть уведомления всех сервисов, которым эти данные требуются для работы- речь о паттерне publisher-subscriber. У нас есть несколько требований к этому механизму: во-первых, он должен уведомлять подписчиков, когда результат запроса готов. Во-вторых, он должен поддерживать различные типы репозиториев (не только HDFS, но и S3 или Cassandra, например). И наконец, он должен прозрачно обрабатывать ошибки, обеспечивать мониторинг и оповещение. В Netflix используют внутренний инструмент под названием Hermes, который предоставляет все эти возможности и создаёт согласованную структуру публикаций и подписок. Это позволяет доставлять данные подписчикам практически в режиме реального времени. В некотором смысле по способам использования он похож на Apache Kafka, но в отличие от него Hermes это не система очередей сообщений-событий.
Signals and models. Сигналы и модели
Независимо от того, какой тип вычислений мы используем, мы должны продумать, как наша архитектура будет обрабатывать три типа данных - модели, данные, сигналы. Модели - это обычно небольшие файлы с весами/параметрами, которые получилились в результате офлайнового предобучения. Данные - это предобработанная информация, которая хранится в какой-нибудь базе данных, например метаданные фильма или рейтинги популярности. Под термином “сигнал” мы имеем в виду данные о событиях, которые приходят в систему - чей-то лайк, чей-то просмотр. Эти сигналы исходят от онлайновых сервисов, могут представлять из себя пользовательскую информацию, например, какие фильмы смотрит пользователь, или же контекстную информацию, например данные сессии, устройства, дата и время.
Event and Data Distribution. Распределение событий и данных
Наша цель - превратить данные пользовательского взаимодействия в инсайты для улучшения пользовательского опыта. Поэтому мы хотим, чтоб разные интерфейсы Нетфликса (умные ТВ, планшенты, консоли и т.п.) собирали как можно больше пользовательских данных. Это могут быть данные кликов, прокрутки, просмотров или даже содержание области видимости в момент времени. Мы их агрегируем, систематизируем и подаём в модели. Здесь можно разграничить события и данные, хотя граница довольно размыта. Будем называть событиями небольшие единицы информации, чувствительной ко времени, которые необходимо обрабатывать с наименьшей возможной задержкой - лайк на фильме, изменения поля “любимый жанр” в профиле. Эти события запускают следующее действие, например процесс обновление рекомендаций nearline. Данные - более насыщенные информационные единицы, которые, возможно, потребуется обработать и сохранить для последующего использования. Здесь задержка не так важна, как качество и количество информации. Конечно, существуют пользовательские события, которые можно рассматривать как события, так и данные и, следовательно, мы отправляем их в оба потока.
В Netflix ниарлайновый поток событий управляется с помощью внутренней платформы под названием Manhattan. Manhattan - это распределенная вычислительная система, которая занимает центральное место в архитектуре рекомендаций Нетфликса. Он чем-то похож на Storm Твиттера, но решает другие проблемы и отвечает другому набору внутренних требований. На начальных этапах обработки управление потоком данных осуществляется в основном путем протоколирования через Chukwa в Hadoop. Позже мы используем Hermes, как реализацию паттерна подписки (publish-subscribe) наших сервисов и обработчиков на сообщения о событиях и изменениях в данных.
Recommendations Results
Цель машинного обучения в системе - предоставить персонализированные рекомендации. Эти рекомендации могут быть загружены из списка, который мы рассчитали заранее или же сгенерированы на ходу онлайновыми алгоритмами. Конечно, в ход идёт и совмещение этих двух подходов, например, мы можем считать основные рекомендации оффлайн, а потом добавлять к ним что-то новенькое, обрабатывая сигналы, приходящие из риал-тайма.
В Netflix хранят офлайновые и промежуточные результаты в различных репозиториях, так они доступны, когда приходит запрос. В основном используется Cassandra, EVCache и MySQL в качестве хранилища данных. MySQL позволяет хранить структурированные реляционные данные, которые могут потребоваться для какого-либо будущего процесса. Унифицированность запросов - бесспорный плюс, но вместе с этим плюсом приходят проблемы с масштабируемостью в распределённых средах. Cassandra и EVCache имеют преимущества хранилищ типа “ключ-значение”. Cassandra - это хорошо известное и стандартное решение, когда требуется распределенное и масштабируемое хранилище без SQL. Cassandra хорошо работает в некоторых ситуациях, однако в тех случаях, когда происходят "интенсивные[" и постоянные операции записи, EVCache подходит лучше. Но вопрос всё-таки заключается не в том, где хранить данные, а том, как построить такую архитектуру, которая бы смогла совместить конфликтующие требования по сложности запроса, задержке чтения / записи и целостности транзакций.
Заключение
В заключении статьи говорится о важности построения инфраструктуры рекомендаций. Нетфликс всегда рад способным дата-инженерам и дата-саинтистам в команде. Если вы думаете, что можете внести свой вклад в улучшение систем Нетфликса, обязательно гляньте на их jobs page.