В предыдущих сериях... (предыстория)

Читатель, впервые столкнувшийся с творчеством Лаборатории машинного обучения на Хабре, задается справедливым вопросом: «Да кто такие эти ваши нэйросэти в банковских задачах?». Тем временем в Альфа-Банке нейросети применяются уже несколько лет и приносят банку сотни миллионов рублей ежегодно. Подробно изучить наш опыт можно из цикла статей про построение нейросетевых моделей на временных последовательных данных: карточных транзакциях, транзакциях расчетного счета и данных из БКИ. В этой главе дам лишь небольшое резюме.

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

Первым процессом, в который нам удалось внедрить нейросети, стал кредитный скоринг. Быстро стало ясно, что подходы легко масштабируются и на «соседние» задачи – предсказание склонности клиентов к банковским продуктам и оттока.

Заинтересовало и хотите изучить подробнее?

Присоединяйтесь к бесплатному курсу DL in Finance на Stepik.org. Вы узнаете, как при помощи нейронных сетей можно значительно улучшить метрики в классических банковских задачах: кредитный скоринг, склонность к продуктам и предсказание оттока. 

Многократное повторение упражнения по обучению модели под некоторый таргет позволило:

  • с одной стороны, выработать довольно универсальные архитектуры и техники обучения для каждого домена данных (текущий стандарт модели на последовательных данных мы описали в статье про триединую нейросетевую модель);

  • а с другой, досыта наесться повторением одних и тех же шагов.

Однако, заботливых рук нейросетевых моделей ждут еще множество других бизнес-процессов банка…

Десять джунов?..

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

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

? Определить тип задачи
? Собрать выборку с целевой переменной
⛏️ Собрать данные из N доменов
? Обучить модель на каждом домене
⚖️ Обучить метамодель
? Вывести N+1 в промышленную среду

При этом и крайний пункт тоже довольно трудоемок:

?‍? Написать код модели по шаблону
? Пройти код-ревью
?️ Пройти функциональное тестирование
?️‍♂️ Пройти нагрузочное тестирование
? Собрать Docker-образ с кодом модели
? Выкатить образ в промышленную среду исполнения

Перечисленные шаги нужно пройти для каждого из N доменов данных.

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

С одной стороны дата сайентисты не пишут никакой новый код, а по тысяче раз переписывают одни и те же архитектуры и оттачивают мастерство вывода барплота в matplotlib.

Написание кода дата сайентистом: ожидание vs. реальность
Написание кода дата сайентистом: ожидание vs. реальность

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

Исследование новых подходов дата сайентистом: ожидание vs. реальность
Исследование новых подходов дата сайентистом: ожидание vs. реальность

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

...Или один сервис?

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

  1. Делать это быстро — чтобы приблизить то светлое будущее, когда мы будем принимать большинство решений на основе данных.

  2. Минимизируя использование ресурса дата сайентистов и команд сопровождения.

  3. Избавив DS’ов от рутинной механической работы.

Мы разработали такой сервис в этом году и дали ему кодовое имя ANNA – Auto Neural Networks Analytics. Есть несколько каламбурных версий, почему сервис назвали именно так.

Почему сервис назвали ANNA?
Шутка номер 01

В Лаборатории машинного обучения Альфа-Банка работает 23 парня и ни одной девушки. Нам очень хотелось, чтобы в нашем мужском коллективе девушка все-таки появилась, пусть и пока только роботическая…

Шутка номер 10

Аня — Жена нашего тимлида Димы — на их свадьбе пожаловалась, что он очень много работает и времени на совместный досуг совсем не остается. Команда пошла на хитрость и назвала сервис ANNA. Так Дима продолжил посвящать себя рабочим задачам, но его нельзя было упрекнуть, что он проводит с Аней мало времени… Почему-то Дима все равно предпочел проводить время с женой, а не с ♂️крепким мужским коллективом♂️, но название за сервисом закрепилось.

Как было на самом деле

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

  • ANNA – Auto Neural Network Analytics

  • HANNAH – H… Auto Neural Network Analytics Helper

  • NNASTYA – Neural network auto service to your analytics

  • ANNSWER ("Хотите добавить нейросеть в свой процесс? У нас есть ОТВЕТ”):

    • Auto Neural Network Service With Extra Reward

    • Auto Neural Network Service With Easy Reward

    • Auto Neural Network Service With Exciting Reward

    • Auto Neural Network Service With Extra Research

Остановились на ANNA как наиболее лаконичном.

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

Схема взаимодействия пользователя с сервисом
Схема взаимодействия пользователя с сервисом

От пользователя сервис получает всю основную информацию по задаче:

  1. Тип задачи — реализованы типы, которые покрывают 95% банковских задачек.

  2. Метрика, которая наиболее соответствует решаемой задаче.

  3. Выборка обучения. Здесь мы задали стандарт, фактически состоящий из 3-х полей: идентификатор клиента, дата сбора данных и целевая переменная.

Информация, получаемая сервисом от пользователя
Информация, получаемая сервисом от пользователя

Что под капотом?

Поговорим теперь про основные компоненты сервиса. Всего их четыре:

  1. Обновляемые витрины данных.

  2. Пайплайны обучения, инференса и ретро-инференса.

  3. Бэкенд.

  4. Пользовательский веб-интерфейс.

И начнем, конечно, с данных, потому что для автоматического обучения модели нам нужно топливо – данные, на которых эти модели будут обучаться.

Источники данных

Наиболее проработанными источниками данных являются:

  • карточные транзакции,

  • транзакции расчетного счета,

  • кредитные истории.

Они же — наиболее ценные для банковских задач. 

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

Витрина карточных транзакций
Витрина карточных транзакций

Модельная часть

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

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

  1. Категориальные признаки мы отображаем в вектор с помощью подхода Entity Embeddings.

  2. Объединенные векторные представления элементов последовательности затем подаются на вход какому-нибудь кодировщику — это может быть как рекуррентный слой, так и трансформер.

  3. Поверх выходов кодировщика обучается полносвязная «голова» – несколько полносвязных слоев. Чтобы получить векторное представление.

  4. Из полносвязной «головы» мы вытаскиваем числовой вектор, который можно считать эмбеддингом клиента по данному домену данных.

  5. Поверх эмбеддингов для разных доменов мы обучаем ещё одну модель, которая возвращает один-единственный выход.

До появления сервиса в коде этих моделей был полный бардак творческий беспорядок. Он содержал большое количество антипаттернов, каждый DS считал долгом переписать тот код, который был написан до него. Поэтому весь этот код стандартизировали, обернули в библиотеку, и для обучения новых моделей теперь нам достаточно просто передавать в неё небольшой набор конфигов. Технически это реализовано в виде веток и тегов в гите:

  • при создании каждой новой задачи мы бранчуемся от мастера и создаём коммит, в котором передаём параметры задачи — тип и метрику;

  • внутри этой ветки для каждого нового запуска создаём новый коммит с параметрами этого запуска, который помечается соответствующим тегом.

Бэкенд – «мозг» Анны

К сожалению, таблички на Hadoop и код в гите не умеют самостоятельно работать и никакой ценности для бизнеса не приносят. Нужно как-то заставить эти модели работать, поэтому следующая компонента, о которой поговорим — это инфраструктура. Давайте вместе порассуждаем, как можно организовать инфраструктуру для автоматического обучения и инференса моделей.

Схема взаимодействия с инфраструктурой
Схема взаимодействия с инфраструктурой

Где запускать код? Давайте для каждой модели поднимать под в Кубере, динамически выделять для этого пода ресурсы, забирать образ модели из реестра и запускать этот образ внутри поднятого пода.

Откуда брать данные? На этот вопрос я уже дал ответ выше — наши данные хранятся в виде витрин на Hadoop и нам нужно просто обеспечить из Кубера доступ к Hadoop и права на чтение соответствующих витрин.

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

Как собирать образы моделей? На одном из предыдущих шагов мы решили, что будем забирать образы из некоторого реестра, вот только как они там появятся? Для сборки образов мы будем использовать Jenkins. Он будет забирать код модели из гита, собирать образ, сохранять его в реестре, создавать и запускать соответствующий DAG в Airflow.

Где хранить артефакты? Согласитесь, если мы обучили модель и не сохранили её веса, то мы зря потратили время. Поэтому для сохранения весов и логирования прочих артефактов по модели будем использовать MLFlow.

Как объединить все части в едином порыве? Во всём этом процессе роль мозгового центра будет выполнять бэкенд. Он реализован на FastAPI с базой на Postgres и делает следующее:

  • хранит информацию о задачах, выборках и моделях,

  • параметризует пайплайны моделей,

  • запускает пайплайны Jenkins,

  • мониторит статусы ранов в Airflow,

  • получает аналитику по моделям из MLFlow.

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

Результат разработки сервиса
Результат разработки сервиса

Представим, что сервис станет очень популярен, все захотят использовать силу нейросетей в своих задачах и начнут отправлять в этот сервис свои запросы: «обучи модель», «проинференси модель» … 

По нашей задумке для каждого такого запроса мы будем поднимать под в Кубере: поднимем один под, второй, третий, четвёртый… ресурсы Кубера начнут заканчиваться… поднимем пятый под, ресурсы закончились окончательно… поднимаем шестой под, ресурсов нет… Кубер некоторое время сопротивляется, но через какое время наш сервис становится недоступен. Точнее сервис-то остаётся доступен, только никакие модели мы в нём обучать не можем. И дело в том, что мы не решаем такую важную проблему, что сервис не контролирует потребление ресурсов Кубера и Hadoop.

Решение этой проблемы достаточно прозаично (спойлер: это не взять и отказаться от сервиса). Давайте добавим в наш сервис очередь, например, мы используем брокер Rabbit, которая будет запускать не более N ранов одновременно. Предположим нам пришло 6 одновременных запросов, размер нашей очереди – 3. Тогда мы для 3-х запросов поднимем поды в Кубере, а оставшиеся запросы будут ожидать освобождение ресурсов. Когда какой-то под завершится, ресурсы ресурсы Кубера освободятся, мы заберем из очереди следующий запрос и запустим его на Кубере. Таким образом, мы сможем обеспечить какое-никакое отказоустойчивое функционирование сервиса на ограниченных ресурсах.

Очередь помогает контролировать нагрузку на ресурсы
Очередь помогает контролировать нагрузку на ресурсы

No-code пользовательский интерфейс

Последняя компонента сервиса — это пользовательский интерфейс. Никто не станет пользоваться сервисом, если у него не будет красивенькой «мордашки». Мы реализовали такую мордашку на джаваскриптовом Vue и Nginx. Приглашаем эксепертов оценить таланты веб-дизайна наших DS'ов в секцию комментариев ?

Пользовательский интерфейс сервиса
Пользовательский интерфейс сервиса

Развязка

Поговорим о результатах — какие двери перед нами открывает такого рода автоматизация. Попробуем ответить на этот вопрос в формате было vs. стало.

Было:

Много ручных рутинных процессов, мало времени на исследования и улучшение качества моделей ⇒ DS'ы делают однотипные задачи.

Стало:

  1. Автоматизированный процесс с околонулевым участием DS'ов.

  2. Масштабируемая и отказоустойчивая (prove me wrong ?) инфраструктура для разработки моделей. Если одним утром мы захотим разрабатывать в два раза больше моделей одновременно, нам достаточно добавить железа, переконфигурировать Кубер и расширить очередь.

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

больше времени на исследования, новый уровень развития DS.

А подробно о том, как трансформируется работа DS'ов в прикладных индустриях, когда они расправятся с рутиной, читайте в статье Побеждаем рутину в Data Science.

To be continued

Напоследок остановимся на идеях по развитию сервиса ANNA.

  1. В первую очередь, хочется внедрить в сервис NAS – Neural Architecture Search, чтобы уйти от прибитых гвоздями архитектур в пользу более гибкого поиска и подбора архитектуры под конкретную задачу.

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

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

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

Нашими новостями продолжим делиться здесь, а также в телеграмм-канале «Нескучный Data Science». Подписывайтесь на нас и до новых встреч!

Комментарии (1)


  1. ciphronaut
    02.04.2024 05:56
    +2

    Отличная шутка! В последнее время Альфа-Банк удивляет своей изобретательностью. То МЛМ придумает, то игру с шариками. А вот сегодня очень изобретательно пошутили. С 1Апреля!