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

У онлайн-обучения есть несколько преимуществ, два наиболее важных из которых связаны с пространственной сложностью и скоростью. Обучение модели онлайн означает, что вам не нужно хранить в памяти все данные обучения, что является отличной новостью, если вы имеете дело с огромными наборами данных. Инкрементальный характер также означает, что ваша модель может быстро реагировать на изменения в распределении поступающих данных при условии, что используемый алгоритм правильно настроен.

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

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

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

Выбор алгоритма обучения

Один из наиболее важных вариантов при создании системы онлайн-обучения - это выбор алгоритма обучения. Алгоритмы, которые используют (стохастический) градиент для изучения оптимальных параметров, естественно подходят для инкрементального обучения¹. Многие алгоритмы поддерживают использование SGD для оптимизации, но почти для всех ситуаций мы рекомендуем использовать линейный обучающийся, такой как линейная или логистическая регрессия. На это есть несколько причин:

  • Линейные учащиеся в основном хорошо понимают
  • Эффективные реализации существуют практически для любого широко используемого языка программирования. Для более экзотических языков вы можете легко развернуть собственную реализацию SGD; правило обновления является простым для наиболее распространенных потерь (например, потеря журнала, Huber и MSE)
  • Вы можете делать прогнозы, используя только изученные параметры / веса, потому что прогноз представляет собой простую линейную комбинацию (позже мы увидим, почему это важно)
  • Объяснить, как линейный ученик приходит к предсказанию, легко по сравнению с моделями черного ящика.

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

Управление потоком обучающих данных

После того, как мы выбрали наш алгоритм обучения, нам нужно управлять тем, как данные обучения вводятся в него. Очевидный выбор - это какая-то очередь. SGD предполагает, что данные вашего обучения поступают случайным образом; если это действительно так, достаточно простой структуры очереди FIFO. В случаях, когда это предположение не выполняется, вы можете либо буферизовать и перемешивать данные перед их загрузкой в ​​очередь FIFO, либо, альтернативно, реализовать перемешивание через очередь с приоритетом. В большинстве случаев мы предпочитаем использовать примитивы очереди (LPUSH, RPOP и т. Д.) Хранилища в памяти, такого как Redis, вместо специальной системы брокера сообщений. Причина этого двоякая: этого достаточно для наших нужд, и он многоцелевой, поскольку мы можем использовать ту же систему для хранения других вещей (подробнее об этом чуть позже).

Управление версиями модели для прогнозов

До сих пор мы рассматривали только пошаговое обучение модели, но мы также хотим предоставлять прогнозы в реальном времени. Здесь вступает в силу наша рекомендация использовать линейного ученика: чтобы сделать прогноз, нам нужен только текущий контекст и параметры, которые мы изучили, и ничего больше. Если бы мы использовали, например, нейронную сеть, нам нужно было бы знать параметры, архитектуру сети (включая выбор функций активации), а также множество другой информации. Для линейных учеников нам нужно намного меньше. Если у нас есть линейная модель для прогнозирования роста на основе веса (наш контекст), у нас есть height = ‹некоторый изученный параметр› * weight + ‹некоторый изученный параметр перехвата›. Учитывая контекст, эти два параметра - все, что нам нужно для прогнозирования. На практике линейные модели могут иметь тысячи параметров, но по большому счету это тоже немного.

В нашей системе примеров, когда наш линейный ученик учится на примерах через очередь Redis FIFO, он периодически сохраняет параметры модели обратно в Redis. Как часто это происходит, можно настроить: для некоторых приложений может быть даже целесообразно делать это каждый раз, когда изучается один обучающий пример. Здесь важно отметить, что сохранение (и действительно получение) параметров должно быть атомарной операцией. В Redis это гарантировано, если вы используете команды SET / GET (ядро Redis однопоточное). Для более сложных взаимодействий можно использовать механизм блокировки. Ключевым требованием является то, что все изменения параметров модели обновляются за один раз; если мы попадаем в ситуацию, когда одни параметры обновились, а другие нет, мы получаем неверные прогнозы.

Служение вашим предсказаниям

Разумно разделить логику обучения и логики вывода. В нашей настройке, поскольку параметры модели обновляются в Redis атомарно, обслуживание прогнозов происходит следующим образом:

  • Предварительно обработайте контекст, чтобы сделать его пригодным для вывода (пока мы пропустим проблему поиска контекста)
  • Получение параметров модели атомарно из Redis (GET)
  • Вычислить линейную комбинацию весов модели и контекстных характеристик.
  • Вернуть результат

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

Если вы хотите обслуживать свои прогнозы через REST API, у вас есть несколько вариантов: вы можете реализовать логику прогнозирования и развернуть ее непосредственно на логических серверах, вы можете использовать контейнеры или можете выбрать бессерверное решение. Ключевым требованием является то, что ваш API прогнозирования должен масштабироваться по горизонтали: по мере увеличения нагрузки вы можете наращивать больше оборудования и балансировать между экземплярами. В идеале он также должен замедляться при уменьшении нагрузки, что позволяет сэкономить на расходах. Мы рекомендуем использовать бессерверное решение (Lambda на AWS, функции Azure в Azure и т. Д.) Или подход на основе контейнеров. Как правило, горизонтальное масштабирование выполняется за вас, и, поскольку вы не занимаетесь интенсивными вычислениями при обслуживании линейных прогнозов, объем ресурсов для вывода довольно невелик.

Регистрация ваших данных

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

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

Хранение функций (необязательно)

Мы еще не рассмотрели, как обрабатывать контекст, необходимый для прогнозирования. Если вы создаете механизм рекомендаций и рекомендуете разные элементы в зависимости от возраста, эту информацию нужно откуда-то извлекать. Если это возможно, самое простое решение - отправить его как часть полезной нагрузки запроса на прогноз. В тех случаях, когда это невозможно (например, в приложении с выходом в Интернет, где данные недоступны и / или конфиденциальны), вам понадобится быстрое хранилище функций. Redis также можно использовать для этой цели. В таком сценарии наша логика обслуживания выглядит следующим образом:

  • Получить контекст из Redis (GET)
  • Предварительно обработайте контекст, чтобы сделать его пригодным для вывода
  • Получение параметров модели атомарно из Redis (GET)
  • Вычислить линейную комбинацию весов модели и контекстных характеристик.
  • Вернуть результат

Разное хранилище состояний (необязательно)

Еще раз ударил по барабану в памяти: это идеальное место для хранения таких вещей, как гиперпараметры эксплуатации / исследования (для обучения с подкреплением), сегменты A / B-тестирования или любой другой тип небольшой дополнительной информации, которая может понадобиться при обслуживании запросов.

Другие примечания по масштабируемости и сложности

На высоком уровне наша примерная архитектура состоит из трех логических компонентов: один для инкрементального обучения, один для вывода и один для управления состоянием.

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

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

Для очереди FIFO и хранилища значений ключей в Redis большой пул памяти гарантирует, что у вас будет достаточно большая очередь и место для хранилища функций. С точки зрения операций, большинство из них, которые вы будете использовать, имеют постоянную сложность: push / pop очереди, GET и SET - все O (1).

Пример архитектуры

Пример архитектуры, представленный ниже, предназначен для стека AWS (ECS для обучения; ElastiCache для организации очередей, параметров модели и хранилища функций; Lambda для вывода; RDS для ведения журнала), но может быть легко адаптирован для использования на платформах других поставщиков облачных вычислений или в -домашняя фурнитура. Он разработан специально для использования с алгоритмами линейного обучения; нелинейные решения требуют другой настройки из-за относительно высоких затрат на вычисления, связанных как с обучением, так и с логическим выводом. Некоторые ключевые соображения, например управление доступом и шлюзы API опущены для краткости.

Пример сценария: механизм рекомендаций на основе онлайн-обучения с подкреплением

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

С точки зрения конечных пользователей, когда они просматривают интернет-магазин, REST API обслуживает прогнозы, выбирая параметры модели (и, необязательно, необходимые контекстные функции) из хранилища данных в памяти и вычисляя лучшие рекомендации с использованием линейной комбинации функций. и параметры. Поскольку поиски по существу выполняются за O (1), API остается работоспособным (при условии правильной конфигурации) и быстро отвечает, независимо от количества обучающих примеров, передаваемых обучающемуся.

Когда пользователи совершают покупки (или не делают), данные регистрируются и помещаются в нашу очередь Redis. Из Redis наш линейный ученик потребляет и тренируется на примерах так быстро, как только может, и переводит полученные параметры модели обратно в Redis (либо после каждого примера, либо после достижения настраиваемого временного горизонта). Когда скорость поступления обучающих примеров остается ниже, чем способность обучающихся обучаться на них, параметры модели в Redis обновляются практически в реальном времени. Когда скорость приема данных превышает пропускную способность обучения, возраст хранимых параметров линейно растет. В этом ключевая причина, по которой наша примерная архитектура отделяет вертикально масштабируемые компоненты от горизонтально масштабируемых: во время сильных всплесков трафика базовой модели разрешается немного отставать, если это необходимо. Однако REST API остается производительным: конечные пользователи быстро получают свои прогнозы, а взаимодействие с пользователем остается неизменным. При нормальной нагрузке обучение и вывод происходят практически в реальном времени.

Заключительные мысли

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

Особая благодарность партнеру по машинному обучению Ярно Картеле и партнеру по архитектуре решений Лилли Неванлинне за их общие отзывы и технические знания.

[1] другие процедуры оптимизации также способны к постепенному обучению, но из-за их популярности в этой статье мы сосредоточимся на SGD.