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

Сегодня мы впервые представляем нашу новую рекомендательную технологию TinyML, которая работает локально, на обычном смартфоне. Теперь при потере доступа в интернет Моя волна не просто бесшовно продолжит воспроизведение музыки, но и будет подстраиваться под действия пользователя и формировать рекомендации, исходя из его предпочтений в текущий момент.

Как это работает и что нам пришлось для этого создать — расскажем под катом.

Идея полноценного офлайн-режима для Моей волны появилась не сразу. Мы, конечно, давно хотели сделать так, чтобы пользователи могли слушать музыку и без доступа в сеть. Обычно рекомендательные системы в музыкальных стримингах работают только онлайн: если сети нет — пользователь остаётся с набором скачанных треков, а рекомендации перестают адаптироваться к его действиям. В Яндекс Музыке тоже была возможность слушать скачанные треки, находясь без интернета. Для этого был офлайн-режим, который нужно включать отдельно. 

По сути, офлайн-режим — это такой «авиарежим» для Яндекс Музыки: включаешь в конкретные периоды отсутствия связи, чтобы слушать то, что скачалось на устройство. Проблема в том, что связь может пропадать спонтанно, на произвольные промежутки времени. Например, в лифте. У меня есть ещё один пример — личный и очень показательный. Однажды мы ездили в Карелию. Там между глэмпингами есть длинные участки дороги без интернета. И мы были вынуждены либо мириться с полным отсутствием Моей волны в пути, либо регулярно вручную переключаться между рекомендациями и офлайн-режимом. Неудобно.

Обновление Моей волны решает эту проблему. Переключение между онлайном и офлайном происходит незаметно для пользователя. Даже без сети в рекомендациях могут появляться незнакомые треки. Их можно лайкать или скипать — и Моя волна будет перестраиваться на основе этих сигналов. В большинстве случаев пользователь даже не заметит, что в какой-то момент потерял соединение с сетью.

Как это работает? Этот вопрос логично разделить на два других: откуда берётся музыка без интернета и как мы её ранжируем на устройстве. Начнём с более простого.

Откуда берутся треки для Моей волны в офлайне

Приложение Яндекс Музыка использует три источника треков на устройстве:

Кэш Моей волны. Треки, которые недавно играли в онлайне, сохраняются в кэше и могут использоваться офлайн.

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

Проактивная загрузка. Это новый механизм. Теперь приложение сообщает серверу Яндекс Музыки, какие треки уже есть на устройстве, и запрашивает дополнительные «на случай отключения интернета». Наша серверная рекомендательная технология анализирует список треков и присылает список дополнительных композиций, которые соответствуют предпочтениям пользователя и могут пригодиться при отсутствии сети.

Ну а дальше приложение просто скачивает эти треки в фоне. Чтобы не расходовать мобильный интернет, треки скачиваются только при подключении к Wi-Fi. Скачивание происходит ночью, когда устройство стоит на зарядке (или достаточно заряжено). Более того, пользователи могут управлять в настройках объёмом памяти, который Моя волна использует для офлайн-работы. Но отключать полностью не рекомендуем: без этой опции офлайн-рекомендации будут куда менее разнообразными и в потоке точно не будет новых, незнакомых треков.

Как устроена технология локальных рекомендаций

В приложении Яндекс Музыки появилась не только проактивная загрузка треков, но и рекомендательная технология TinyML. Вопреки ожиданиям, это не нейросетевая модель, а куда более неожиданное решение. По сути, это очень маленькая виртуальная машина, встроенная в приложение Яндекс Музыки и выполняющая роль интерпретатора для специального байт-кода.

Байт-код — это скомпилированный код на языке C, который формирует офлайн-рекомендации на устройстве. В нём закодированы операции: вычисление косинусов между векторами, матричные умножения, применение преобразующих функций — все те базовые операции, которые производят рекомендации и на серверах. Важно, что код компилируется под конкретный набор треков на устройстве, — он «знает», с какими именно треками будет работать. Можно сказать, что мы собираем персональную «модель» рекомендаций под каждого пользователя с учётом только тех треков, которые ей доступны. Это позволяет радикально сэкономить потребление ресурсов смартфона: байт-код очень компактный, буквально десятки килобайт!

Помимо байт-кода, с сервера скачиваются и данные о загруженных треках. Не сами треки, а их векторные представления и другая полезная для рекомендаций техническая информация. Опять же, так мы экономим вычислительные ресурсы устройства, потому что вычисление векторов для треков происходит на сервере, а не локально. Благодаря этому офлайн-версия Моей волны доступна на любом смартфоне, где есть Яндекс Музыка.

Байт-код и данные о треках загружаются с сервера по API раз в три дня или при существенном изменении набора кэшированных треков. Общий объём данных с учётом сжатия обычно меньше 100 КБ, в худшем случае — меньше мегабайта. Система может работать с лимитом до 10 000 треков, но чаще всего это сотни треков.

На этом месте читатели с опытом в мобильной разработке могут спросить: а зачем вы создавали свою TinyML, а не воспользовались уже существующими решениями? Ведь есть проект ONNX, есть нативные ML-библиотеки. С одной стороны, это упростило бы нам работу, не пришлось бы писать свой серверный компилятор. С другой — нам бы пришлось выносить логику работы рекомендаций из байт-кода на клиенты, писать её под каждую платформу. А ещё клиентский код не так-то просто обновить без обновления всего приложения, в отличие от загрузки простого байт-кода по API.

Кто-то может вспомнить ещё и проект WebAssembly, который уже «из коробки» позволяет работать с ML и писать код низкого уровня. Но в этом случае нам пришлось бы встроить его библиотеку в само приложение и увеличить его размер на десятки мегабайт. Столь радикально раздувать размер ради одной единственной функции мы не считаем правильным. TinyML уложился в 30 килобайт.

В общем, альтернативные решения, конечно, есть, но они усложняют клиентский код и недостаточно гибкие для нас. Поэтому мы вынесли все сложности на бэкенд, а в приложение Музыки добавили один единственный класс на 500 строк кода.

Как Моя волна в офлайне работает на практике

Когда интернет пропадает, приложение запускает TinyML. Виртуальная машина начинает интерпретировать байт-код и обрабатывать данные о доступных треках. 

Помимо байт-кода для локального формирования рекомендаций, сервер также отдаёт предпросчитанную очередь — список треков «что поставить, если интернет отключится прямо сейчас». Это несколько десятков треков, рассчитанных тяжёлыми серверными нейросетями. Эта очередь — палочка-выручалочка для коротких перебоев с сетью вроде поездки на лифте. TinyML может использовать её как основу, но если пользователь начинает пропускать треки, система отбросит предпросчитанную очередь и будет строить рекомендации на основе свежей обратной связи. 

Например, у пользователя в кэше есть и джаз, и металл. Он начинает пропускать металл — TinyML понимает, что настроение изменилось, и переключается на джаз. Всё это происходит локально, без интернета. 

Для длительного офлайн-прослушивания (те самые несколько часов в Карелии) основную работу делает локальный код TinyML, а не предпросчитанная очередь.


Когда мы начинали этот проект, то хотели дать пользователям возможность непрерывно слушать Мою волну, влиять на неё, открывать для себя что-то новое — без оглядки на то, что происходит с интернетом в этот момент. Хочется верить, что у нас получилось.

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