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

Обновление 27.09.2019: многие люди спрашивали, существуют ли какие-либо специализированные библиотеки для инкрементного обучения. Да! Vowpal Wabbit чрезвычайно мощный и существует уже довольно давно. Для тех, кто предпочитает API, вдохновленные scikit, обратите внимание на creme.

Наш ‹вставьте название продукта AI› становится лучше, чем больше вы его используете!

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

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

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

Но как насчет новых данных? Если мы просто обучим модель один раз и больше никогда к ней не прикоснемся, мы упустим информацию, которую могли бы предоставить нам другие данные. Это особенно важно в среде, где поведение быстро меняется. Интернет-магазины - одна из таких сред: продукт, популярный сегодня, может быть почти забыт завтра.

Чтобы реагировать на новые данные и создать ИИ, который учится со временем, специалисты по машинному обучению обычно делают одно из двух:

  1. Они вручную обучаются на новых данных и развертывают получившуюся модель, как только будут довольны ее производительностью.
  2. Они планируют обучение на новых данных, например, раз в неделю, и автоматически развертывают полученную модель.

В 99,99% случаев, когда кто-то заявляет, что «наш ИИ становится лучше, чем больше вы его используете», на самом деле они имеют в виду, что они выбрали подход 2), планируя обучение новых моделей. В самом простом виде это может быть буквально одна строка в файле crontab.

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

Мне нравится использовать scikit-learn, чтобы поиграть с машинным обучением. Все различные алгоритмы в scikit-learn реализуют один и тот же простой API, что упрощает запуск и запуск. Для задач регрессии я обычно начинаю с класса SGDRegressor. Вот как обучить простую модель на фиктивных данных (взятых прямо из документации scikit):

import numpy as np
from sklearn import linear_model
n_samples, n_features = 10, 5
y = np.random.randn(n_samples)
X = np.random.randn(n_samples, n_features)
clf = linear_model.SGDRegressor()
clf.fit(X, y)

Метод fit() выполняет всю обучающую магию, в результате чего получается модель, которую мы можем использовать для прогнозирования (в данном случае прогнозирования на одном примере):

clf.predict(np.random.randn(1, n_features))

В дополнение к fit()method, SGDRegressor также предоставляет partial_fit() метод, так что вы можете постепенно обучать небольшие пакеты данных. Фактически, все алгоритмы обучения, совместимые со стандартными алгоритмами оптимизации, такими как (стохастический) градиентный приличный, adam, RMSprop и т. Д., Имеют такую ​​возможность.

Из любопытства давайте посмотрим, сколько времени потребуется на обучение на одном примере, используя partial_fit():

import numpy as np
from sklearn import linear_model
n_samples, n_features = 1, 500
y = np.random.randn(n_samples)
X = np.random.randn(n_samples, n_features)
clf = linear_model.SGDRegressor()
import time
start_time = time.time()
clf.partial_fit(X, y)
elapsed_time = time.time() - start_time
print(elapsed_time)
>>> 0.0008502006530761719

0,0009 секунд на моем компьютере . Это довольно быстро. Фактически, если бы мы поместили наш SGDRegressor за REST API и тренировались на примере каждый раз, когда был сделан HTTP-запрос, с учетом, скажем, 10 мс для обработки запроса, мы могли бы обрабатывать около 520 запросов в секунду, или около 45 миллионов запросов в день.

Это вызывает интересный вопрос: с учетом этих цифр, можно ли будет учиться на новых примерах в чем-то близком к реальному времени? В этом и заключается потенциал онлайн-обучения: как только мы видим новый пример, давайте извлечем уроки из него как можно быстрее. Чем быстрее, тем лучше. Фактически, поскольку скорость превосходит все остальное в онлайн-обучении, мы обычно используем простые алгоритмы обучения вместо сложных, таких как нейронные сети. Мы стремимся к обучению на уровне миллисекунд; все остальное на втором месте.

Пуристы в области машинного обучения могут посмеяться над идеей онлайн-алгоритмов для обучения в реальном времени. Обучение модели может пойти не так, как надо: сам алгоритм может не подходить, модель может не хорошо обобщать, скорость обучения может быть неправильной, регуляризация может быть слишком низкой или слишком высокой… список можно продолжить. Зачем нам вообще пытаться узнать немедленно, если нет никаких гарантий относительно того, что может случиться?

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

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

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

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

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

В этом сила онлайн-обучения: при правильном выполнении оно может среагировать за минуты или даже секунды. При этом не бывает «вчерашних новостей».

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

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

Немедленное обучение также требует быстрого доступа к новым данным. Если вам посчастливилось получить все данные, необходимые для одного обучающего примера, в рамках вызова API, все готово. Но если что-то недоступно на стороне клиента, вам нужно иметь возможность получить эти данные откуда-то за миллисекунды. Обычно это означает использование хранилища в памяти, такого как Redis. Фреймворки обработки «больших данных» не очень полезны. Если вы хотите заниматься как пакетным, так и онлайн-обучением, Spark недостаточно. Если вы занимаетесь только онлайн-обучением, Spark бесполезен.

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

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

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

Особая благодарность Data Scientist Ярно Кивиахо, которого я считаю одним из ведущих специалистов Финляндии по этой теме.

Если вам нравится поиграть с (по общему признанию простым) кодом в этом посте, он доступен в виде GitHub gist: https://gist.github.com/maxpagels/b9c9001f7e5b28a5742b81b02f7704e2

[1]: Теоретически у вас может быть функция, указывающая на такое серьезное событие, но невозможно учесть все.