
Всем привет! С вами Артемий Лямин (@lyaminartemiy) и Иван Тренёв (@123-39). Мы работаем специалистами по разработке нейронных сетей в команде автоматизации машинного обучения Альфа-Банка. Тут мы будем рассказывать как провели масштабную модернизацию нашей внутренней AutoDL-библиотеки, пересмотрев её с самых основ.
Мы покажем, какие ограничения были у предыдущей версии и почему постоянные точечные улучшения в какой-то момент перестали работать. Подробно разберём архитектурные решения, которые легли в основу новой системы: обсудим переход к модульной мультимодальной трансформерной архитектуре, погрузимся в автоматический поиск архитектур, переосмыслим пайплайн работы с данными и многое другое. Отдельно остановимся на инфраструктурной части — удалённых запусках и оркестрации экспериментов.
Данной статьей мы хотим показать как превратили набор разрозненных моделей в единую воспроизводимую и расширяемую AutoDL-платформу. Предлагаем пройти этот путь вместе с нами.
Предыстория
В Альфа-Банке у нас давно живет ANNA — внутренний фреймворк для обучения нейросетевых моделей на банковских данных. Первая версия была заточена под рекуррентные архитектуры (LSTM/GRU) и хорошо справлялась с поставленными задачами.
Как выглядит процесс взаимодействия пользователя с фреймворком?
Пользователь передаёт конфигурацию запуска со всей необходимой информацией о задаче:
-
Тип задачи — классификация, регрессия или ранжирование.
-
Путь до обучающей выборки, которая лежит на Hadoop.
-
Метрика, наиболее соответствующая решаемой задаче.
Пользователь также готовит тренировочную выборку: список клиентов, даты и целевое событие (таргет).
Разработчик запускает скрипт обучения или инференса. В процессе работы пайплайна задействуются несколько базовых моделей на различных доменах неструктурированных данных:
-
Модель на карточных транзакциях.
-
Модель на данных БКИ (бюро кредитных историй).
-
Модель на транзакциях расчётных счетов.
Далее выходы из этих моделей подаются в табличную/миксер-модель, которая учится на эмбеддингах базовых моделей и выдаёт финальное предсказание.
По мере роста числа кейсов и доменов начали проявляться системные ограничения:
-
Жёсткая привязка к структуре витрин. При любом изменении данных приходилось вносить правки в ядро, менять логику и пересобирать пайплайн — это сильно снижало гибкость и масштабируемость.
-
Высокий порог входа. Новым DS/ML-специалистам требовалось значительное время, чтобы разобраться во фреймворке: он был перегружен внутренними соглашениями и неочевидными зависимостями, из-за чего time-to-value для бизнеса увеличивался.
-
Архитектурные ограничения. Добавлять новые архитектуры, гибкие пайплайны и кастомные фичи становилось всё сложнее — система просто не была для этого спроектирована.
-
Деплой. ANNA не была библиотекой в привычном смысле: её нельзя было просто установить через pip. Вместо этого приходилось поднимать отдельную копию репозитория на GPU-ноде.
-
Скорость. Для больших датасетов (10+ млн строк) время работы пайплайна доходило до нескольких дней.
Цели рефакторинга

Мы определили несколько ключевых направлений развития:
-
Отказаться от разрозненных скриптов в пользу целостной модульной архитектуры из независимых компонентов.
-
Перейти от устаревших рекуррентных моделей к мультимодальной трансформерной архитектуре. Эпоха RNN давно закончилась: они плохо работают с длинными последовательностями и почти не масштабируются.
-
Вместо множества отдельных моделей под каждый тип модальности создать единую модель, в которой признаки разной природы непосредственно влияют друг на друга и обрабатываются параллельно.
-
Сократить время и стоимость обучения как минимум без потери качества.
-
Создать полноценный AutoDL с подбором архитектуры и гиперпараметров индивидуально под каждую задачу.
-
Более рационально использовать ресурсы за счёт фильтрации данных, не вносящих значимый вклад в финальное предсказание.
Бенчмарки для оценки качества
Прежде чем проводить эксперименты, мы отобрали несколько задач в качестве бенчмарков, которые внутри решают разработчики моделей с помощью нашего классического AutoML. Данные бенчмарки представляют собой несколько важных склонностных моделей бинарной классификации, а также модель оттока.
Ниже представлена таблица с кратким описанием сэмплированных выборок этих моделей:
|
Модель |
Тип выборки |
Размер выборки |
Процентное соотношение таргетов (1:0) |
|---|---|---|---|
|
Склонность 1 |
train |
257 590 |
24.45 |
|
val |
85 011 |
23.25 |
|
|
oot |
500 000 |
0.09 |
|
|
Склонность 2 |
train |
266 141 |
25.0 |
|
val |
88 714 |
25.0 |
|
|
oot |
500 000 |
0.54 |
|
|
Склонность 3 |
train |
1 496 929 |
24.82 |
|
val |
496 939 |
24.48 |
|
|
oot |
500 000 |
0.39 |
|
|
Склонность 4 |
train |
1 498 725 |
24.98 |
|
val |
497 780 |
24.72 |
|
|
oot |
500 000 |
5.13 |
|
|
Отток клиентов |
train |
1 500 000 |
25.0 |
|
val |
500 000 |
25.0 |
|
|
oot |
500 000 |
35.57 |
AutoDL для табличных и транзакционных данных: краткий обзор
Прежде чем перейти к описанию нашего решения, стоит оглянуться на ландшафт существующих подходов. За последние несколько лет область автоматизированного глубокого обучения для табличных и транзакционных данных прошла стремительную эволюцию — от первых экспериментов с механизмами внимания на структурированных признаках до полноценных систем автоматического поиска архитектур. Ниже приведены три ключевые работы, сформировавшие контекст для наших решений.

FT-Transformer (2021)
Работа FT-Transformer (Gorishniy et al., NeurIPS 2021) стала переломной для применения трансформеров к табличным данным. Авторы предложили модуль Feature Tokenizer, который преобразует все признаки — как категориальные, так и числовые — в токены единой размерности, после чего подаёт их в стандартный Transformer-энкодер. FT-Transformer показал лучший средний ранг среди глубоких моделей на 11 бенчмарках и существенно сократил разрыв с градиентным бустингом. Эта работа де-факто установила стандарт для последующих табличных трансформеров и задала направление, которому мы во многом следовали при проектировании ANNA 2.0.
TabBERT (2020)
Статические табличные трансформеры обрабатывают каждую строку независимо, но банковские данные по своей природе последовательны. TabBERT (Padhi et al., IBM Research, ICASSP 2021) предложил двухуровневую иерархическую архитектуру: Field Transformer кодирует отдельные транзакции, формируя вектор каждой строки, а Sequence Transformer обрабатывает упорядоченную последовательность таких векторов для захвата временных зависимостей. Модель обучалась в стиле BERT через маскирование полей транзакций. Именно эта идея иерархического кодирования — сначала отдельные события, затем их последовательность — оказала косвенное влияние на архитектуру ANNA 2.0.
MUFASA (2021)
Ближе всего к нашей задаче стоит MUFASA (Xu et al., AAAI 2021) — первая работа, в которой автоматический поиск архитектуры был применён к задаче мультимодального слияния на структурированных данных. MUFASA одновременно оптимизирует архитектуры модальностно-специфичных энкодеров и стратегии их объединения с помощью эволюционного поиска. MUFASA подтвердила, что автоматический подбор стратегии слияния модальностей может превосходить ручное проектирование, и стала одним из ориентиров при разработке нашего fusion-модуля.
Таким образом, к моменту начала работы над ANNA 2.0 в индустрии сложились четыре ключевых направления: трансформеры для табличных признаков, иерархические архитектуры для транзакционных последовательностей, дифференцируемый поиск архитектур и автоматизация мультимодального слияния. Наша задача состояла в том, чтобы объединить все эти идеи в единой библиотеке, адаптированной под специфику банковских данных.
Data-Centric подход

Когда мы начали переосмыслять подход к данным, стало очевидно: значительная часть ограничений старой системы связана не с моделью, а с тем, как формируются входные признаки. В начале пути нашего рефакторинга мы сместили фокус с усложнения архитектуры на контроль качества данных на каждом этапе пайплайна.
В предыдущей версии системы обработка табличных признаков строилась по статичной схеме. Все числовые и категориальные фичи проходили этап бинаризации: для каждой фичи в каждом источнике данных заранее определялось разбиение на бины, после чего значения заменялись индексами этих бинов. Далее полученные индексы подавались в nn.Embedding, где каждая фича фактически имела собственную embedding-таблицу.
Такой подход оказался неустойчивым в условиях реальных данных. Распределения признаков со временем меняются, особенно в банковском домене, и статическая дискретизация перестаёт адекватно отражать структуру данных.
В новой версии пайплайна мы перешли к динамическому биннингу, встроенному в data pipeline. Для этого используется QuantileDiscretizer из PySpark, который строит разбиения на основе квантилей текущего распределения признака. Бины теперь не фиксированы: они пересчитываются с заданной периодичностью на актуальных данных, что позволяет автоматически учитывать сдвиги в данных (data drift) без ручного вмешательства.
Источники данных

Источников данных в ANNA 2.0 может быть сколько угодно, но на данный момент мы рассматривали пять: транзакции расчётного счёта, карточные транзакции, данные о кредитах, сжатый Feature Store, а также история кликов пользователя. Когда Data Scientist запускает эксперимент, библиотека подхватывает необходимые эмбеддинги по клиенту на нужную дату и собирает последовательности.
Мы исследовали различные методы получения self-supervised эмбеддингов, включая классические автокодировщики и их современные модификации. В частности, мы исследовали как реконструкционные подходы, так и методы контрастивного обучения, что привело нас к экспериментам со следующей архитектурой.
Вначале мы использовали SCARF (Self-supervised Contrastive Learning for Tabular Data) — подход к созданию эмбеддингов для табличных данных на основе контрастивного обучения. SCARF создаёт две различные замаскированные версии одного и того же сэмпла и затем учится так, чтобы полученные эмбеддинги для обеих версий были близки друг к другу, одновременно отталкивая эмбеддинги других сэмплов.
Выяснилось, что SCARF нередко даёт недостаточно информативные эмбеддинги: модель фокусируется только на контрастивной цели и не учится восстанавливать признаки. Мы доработали подход — добавили комбинированную функцию ошибки. Итоговый метод объединяет реконструкцию (как в Masked AutoEncoder) и контрастивное обучение (как в SCARF): модель учится не просто различать сэмплы, но и предсказывать скрытую информацию. В качестве контрастивного лосса использовался InfoNCE.
Полученная архитектура для извлечения эмбеддингов транзакций состоит из четырёх ключевых компонентов:
-
Слой эмбеддингов: каждая фича представляется собственной таблицей эмбеддингов.
-
Блок энкодера: вся закодированная информация пропускается через трансформер, на выходе получается обогащённое представление.
-
Блок декодера: получает замаскированные данные и пытается их восстановить.
-
Комбинированный лосс: состоит из двух частей: реконструкции замаскированных токенов кросс-энтропией и симметричного контрастивного лосса InfoNCE по двум аугментациям одной строки.
Feature Store
Следующий вопрос был довольно прямой: а что, если сжать Feature Store и получить эмбеддинги клиента на конкретную дату?
Объединив порядка 40 источников, мы получили около 5000 столбцов и миллиард строк. Пробовали разные DL-модели для табличных данных.
Лучше всего в standalone-режиме сработал ванильный автокодировщик с нормализацией каждого признака. Помимо простоты, этот подход имеет преимущества с точки зрения использования ресурсов для обучения.
Эмбеддинги Feature Store вошли в финальную архитектуру ANNA 2.0 как отдельная модальность. ETL-процессы для этого источника пока в разработке, однако в экспериментах добавление FS-эмбеддингов уже дало положительный эффект.
Ниже представлены результаты экспериментов с добавлением Feature Store (ROC-AUC):
|
Модель |
Тип выборки |
ANNA 1.2 |
ANNA с FS |
CatBoost |
LogReg |
|---|---|---|---|---|---|
|
Склонность 1 |
val |
0.77 |
0.7953 |
0.7175 |
0.4129 |
|
oot |
0.75 |
0.7811 |
0.6053 |
0.3115 |
|
|
Склонность 2 |
val |
0.77 |
0.7678 |
0.6228 |
0.5116 |
|
oot |
0.76 |
0.759 |
0.6258 |
0.4616 |
|
|
Склонность 3 |
val |
0.87 |
0.8593 |
0.7922 |
0.4437 |
|
oot |
0.90 |
0.8817 |
0.8395 |
0.4119 |
|
|
Склонность 4 |
val |
0.94 |
0.9388 |
0.8947 |
0.5697 |
|
oot |
0.90 |
0.9548 |
0.9285 |
0.4730 |
|
|
Отток клиентов |
val |
0.93 |
0.9131 |
0.8390 |
0.7801 |
|
oot |
0.92 |
0.8224 |
0.8190 |
0.7670 |
Vertica
Также мы добавили информацию о действиях клиентов на сайте и в приложении Альфа-Банка. Нам известны все клики и просмотры каждой страницы, что даёт полезную информацию о предпочтениях клиента.
Пайплайн обработки данных Vertica состоит из четырёх этапов:
-
Построение последовательностей.
-
Обработка последовательностей.
-
Обучение unsupervised FastText на корпусе токенов.
-
Инференс и извлечение эмбеддингов.
Этап 1. Сырые данные из Vertica преобразуются в унифицированные текстовые «предложения», где каждое слово — комбинация категорий и атрибутов события. Разделяются составные имена событий, отфильтровываются технические события, удаляются числовые и хеш-подобные токены.
Этап 2. События каждого клиента объединяются в упорядоченную последовательность, отражающую его поведение за определённый период до целевой даты.
Этап 3. Последовательности готовятся к векторизации. Все последовательности приводятся к одной длине: короткие дополняются токеном <PAD>, длинные обрезаются. Для получения семантических векторных представлений используется модель FastText в режиме skipgram (размерность эмбеддингов — 64, количество эпох — 20).
Этап 4. Строятся векторные представления всех клиентских последовательностей.
Как и в случае с Feature Store, эмбеддинги Vertica вошли в архитектуру ANNA 2.0 в качестве отдельной модальности. Предварительные эксперименты подтвердили, что данные о поведении клиентов на сайте и в приложении несут дополнительный информативный сигнал.
В таблице ниже представлены результаты экспериментов с добавлением Vertica (ROC-AUC):
|
Модель |
Тип выборки |
ROC-AUC |
F1 |
||
|---|---|---|---|---|---|
|
ANNA 1.2 |
Vertica FastText |
ANNA 1.2 |
Vertica FastText |
||
|
Склонность 1 |
train |
0.769 |
0.718 |
0.492 |
0.461 |
|
val |
0.757 |
0.685 |
0.480 |
0.449 |
|
|
oot |
0.738 |
0.602 |
0.002 |
0.007 |
|
|
Склонность 2 |
train |
0.790 |
0.620 |
0.500 |
0.370 |
|
val |
0.770 |
0.570 |
0.480 |
0.350 |
|
|
oot |
0.750 |
0.540 |
0.570 |
0.460 |
|
|
Склонность 3 |
train |
0.890 |
0.790 |
0.630 |
0.510 |
|
val |
0.870 |
0.740 |
0.610 |
0.470 |
|
|
oot |
0.900 |
0.760 |
0.150 |
0.040 |
|
|
Склонность 4 |
train |
0.940 |
0.820 |
0.780 |
0.550 |
|
val |
0.940 |
0.770 |
0.770 |
0.530 |
|
|
oot |
0.850 |
0.730 |
0.400 |
0.310 |
|
|
Отток клиентов |
train |
0.930 |
0.790 |
0.730 |
0.510 |
|
val |
0.930 |
0.770 |
0.720 |
0.490 |
|
|
oot |
0.920 |
0.740 |
0.080 |
0.040 |
|
Теперь рассмотрим революцию архитектуры модели.
Зачем переходим к трансформерам?

В первой версии библиотеки использовался классический подход: отдельная RNN для каждого источника данных с последующей «склейкой» эмбеддингов и дообучением финальной модели. Такая архитектура была работоспособна, но обладала рядом критических ограничений при росте объёмов данных и количества источников. В новой версии мы сделали ставку на трансформеры.
Масштабируемость и эффективность вычислений
Рекуррентные сети (RNN) страдают от последовательной природы вычислений: они плохо параллелизуются и испытывают проблемы с долгосрочными зависимостями. Трансформеры обрабатывают последовательность целиком, что позволяет эффективно задействовать GPU-ускорение. Стоит отметить, что сложность self-attention растёт квадратично с длиной последовательности, однако на практике это компенсируется массивным параллелизмом. Трансформеры значительно лучше справляются с длинными последовательностями за счёт прямого доступа к любому элементу через механизм внимания.

Универсальность и простота расширения
В старой версии добавление нового источника данных означало проектирование новой модели, её обучение и изменение этапа конкатенации. Трансформер изначально задуман как универсальный агрегатор: добавить новую модальность — значит просто подать на вход ещё одну последовательность эмбеддингов. Механизм внимания сам определит, как её сопоставить с остальными. Это существенно упрощает архитектуру пайплайна и снижает объём технического долга.
Удобство автоматического поиска архитектуры
Универсальность трансформеров делает их особенно удобными для автоматического подбора архитектуры. Когда всё построено вокруг единого шаблона, значительно проще динамически изменять глубину, ширину, механизмы внимания и другие параметры модели. Это хорошо сочетается с методами вроде дифференцируемого поиска архитектуры.

От ансамбля разрозненных моделей к единому end-to-end
Старая схема (RNN₁ → эмбеддинг₁, RNN₂ → эмбеддинг₂, затем конкатенация → ещё одна модель) — это по сути гетерогенный ансамбль с ручной склейкой. Такая композиция не позволяет ошибкам одной модели корректироваться другими модулями на этапе обучения. Новая версия использует единую архитектуру с fusion-модулем, который одновременно видит все модальности и выявляет перекрёстные зависимости между разными источниками данных в одном графе вычислений.
Скорость обработки данных
RNN требуют последовательного прохода по временной оси, тогда как трансформеры обеспечивают более высокую скорость обработки за счёт параллелизма и оптимизированных вычислений на современных аппаратных ускорителях (GPU/TPU). В сочетании с возможностью фильтрации избыточных модальностей прямо в процессе обучения новая версия работает в разы быстрее, особенно на больших объёмах данных.

Итог прост: переход на трансформеры меняет не архитектуру, а сам подход к вычислениям. На смену слабо связанной последовательности RNN-моделей приходит единый, автоматически настраиваемый параллельный процесс.
Как пришли к финальной архитектуре? Полный roadmap улучшений
Весь путь от первых экспериментов до финальной архитектуры ANNA 2.0 выстроен из последовательных решений. Каждое из них отвечало на один из трёх вопросов: где происходит потеря качества, где тратится лишнее время и как сделать систему одновременно мощнее и эффективнее.

1. Фильтрация разреженных последовательностей
Анализ обучающей выборки показал, что значительная часть клиентов имеет крайне мало эмбеддингов на заданном временном интервале — по сути, пустые или почти пустые последовательности. Было решено удалять из обучающей выборки клиентов, у которых число эмбеддингов на заданном окне оказывается ниже порогового значения. Валидационная и отложенная выборки остаются нетронутыми.
Эффект оказался ощутимым даже при фильтрации всего 10% выборки: сборка данных ускорилась на 10%, обучение — на 20–25%, при этом качество модели не просело.
2. Отказ от наивного усреднения: новые механизмы пулинга
В базовой реализации трансформерный блок возвращал тензор (batch_size, seq_len, emb_dim), который «схлопывался» до (batch_size, emb_dim) с помощью masked mean pooling. Мы протестировали два альтернативных механизма агрегации:
Masked Attention Pooling (MAP) — одноголовый механизм внимания со скалярной оценкой важности для каждого токена. Модель сама учится выделять наиболее информативные временные шаги.
MultiHead Seed Pooling (MSHP) — вместо единственного вектора запроса обучается набор из k «seed»-векторов, которые через multi-head attention «опрашивают» токены последовательности.
На практике MAP продемонстрировал лучший результат — прирост качества составил порядка 2% относительно базового пулинга.
3. Поиск оптимального механизма слияния модальностей
Получив качественный пулинг для каждой отдельной модальности, мы сосредоточились на задаче их объединения. Было протестировано несколько подходов с нарастающей сложностью:
WeightMean Fusion (WMF) — взвешенное усреднение пул-эмбеддингов всех модальностей с обучаемыми скалярными весами. Просто, интерпретируемо, но не гибко.
Gated Fusion (GF) — важность каждой модальности определяется не фиксированными весами, а небольшим MLP, который адаптируется под конкретный пример.
Context-Based Multimodal Fusion (CBMF) — каждая модальность представлена не только эмбеддингом, но и специфическим контекстным вектором.
Настоящим прорывом стало решение объединить лучшее из перечисленных подходов в рамках единого архитектурного модуля.
Multimodal Bottleneck Transformer Fusion (MBTF) — финальный и наиболее мощный механизм. Его ключевая идея заимствована из концепции attention bottlenecks: между модальностями вводится небольшая группа специальных латентных токенов — «узких мест». Каждый такой токен собирает и конденсирует наиболее важную информацию из своей последовательности, а затем передаёт её соседним модальностям через кросс-модальное внимание.
Помимо стабильного превосходства по метрикам (прирост качества в среднем на 5% на базовых бенчмарках), MBTF принёс архитектурный бонус: поскольку модуль работает непосредственно с полными последовательностями, предварительный пулинг стал не нужен — MBTF взял на себя и агрегацию, и слияние одновременно.
4. Выбор оптимальной стратегии маскирования
После выбора MBTF возник вопрос: как правильно обращаться с сэмплами, у которых все последовательности эмбеддингов пусты? Мы протестировали четыре стратегии:
-
Маскирование сквозь всю архитектуру вплоть до последнего слоя.
-
Полное отсутствие маскирования.
-
Маскирование до момента объединения последовательностей разных модальностей.
-
Маскирование до последнего результирующего слоя внимания в fusion-модуле.
Победил вариант с маскированием до момента объединения модальностей: он обеспечил наилучший результат по большинству метрик (в среднем качество выросло на 4% относительно других кандидатов).
5. Улучшение оптимизатора: переход на Muon
Параллельно с архитектурными экспериментами мы проверили замену стандартного Adam на Muon — современный оптимизатор, ориентированный на матричные параметры трансформерных архитектур. Результат: значимой просадки в качестве не наблюдалось, а скорость одного цикла обучения выросла примерно на 5%.
6. Динамическая фильтрация модальностей в процессе обучения
По мере роста числа источников данных возникает закономерный вопрос: все ли модальности одинаково полезны? Был реализован механизм автоматической фильтрации модальностей прямо в процессе обучения. Вклад каждого источника данных оценивается по двум критериям:
-
Attention gate fusion — усреднённые веса multi-head attention внутри fusion-блока отражают, насколько активно модальность «запрашивается» другими источниками.
-
Importance gate — обучаемый gate-модуль внутри bottleneck-слоёв явно учится оценивать значимость каждой модальности и подавлять несущественные.
Модальности, не прошедшие порог значимости по обоим критериям, исключаются из дальнейшего обучения и инференса. Это позволяет сокращать время обучения, снижать потребление памяти и ускорять сборку продакшн-выборки.
7. DARTS: автоматический поиск оптимальной архитектуры
Чтобы называться полноценным AutoDL-инструментом, недостаточно создать единую архитектуру под все случаи жизни. Ответом стало внедрение модуля нейропоиска на основе DARTS (Differentiable Architecture Search). Именно это можно назвать визитной карточкой всей библиотеки.
Ключевая идея DARTS состоит в том, чтобы превратить дискретный и комбинаторно сложный выбор архитектуры в непрерывную оптимизационную задачу, решаемую обычным градиентным спуском. Строится единая суперсеть — суперпозиция всех рассматриваемых вариантов. Операции взвешиваются архитектурными параметрами через softmax, и вся система оптимизируется двухуровнево:
-
Внутренний уровень — веса сети обновляются по обучающей выборке.
-
Внешний уровень — архитектурные параметры обновляются по валидационной выборке.
За несколько эпох такого совместного обучения DARTS выявляет наиболее перспективную конфигурацию, которая затем фиксируется для финального обучения. Это делает ANNA 2.0 полноценной AutoDL-системой.
8. Hyperopt: тонкая настройка найденной архитектуры
Финальным штрихом стало внедрение Hyperopt — байесовской оптимизации гиперпараметров. Hyperopt запускается после DARTS, когда структура сети уже зафиксирована. На этом этапе настраиваются числовые гиперпараметры, не подпадающие под определение архитектурных (learning rate, batch size и другие).
Такая двухэтапная схема — сначала поиск архитектуры через DARTS, затем её настройка через Hyperopt — позволяет получить по-настоящему оптимальную модель без ручного вмешательства, замыкая полный цикл AutoDL.

Итог: от идеи к системе
Каждый шаг этого пути решал конкретную проблему и открывал новые возможности. Фильтрация разреженных данных дала первый прирост в скорости; MAP вернул модели способность различать важные и неважные моменты времени; MBTF радикально переосмыслил подход к слиянию модальностей; правильная стратегия маскирования и замена оптимизатора добавили стабильности; динамическая фильтрация сделала систему стройнее; а DARTS с Hyperopt превратили её в полноценный автоматический конвейер.
Где нам было больно? Сложности и ограничения, с которыми столкнулись
1. Проблемы с памятью

При склейке последовательностей от разных источников в одну таблицу объём данных растёт мультипликативно: каждая запись содержит бины от всех модальностей, причём длины последовательностей могут различаться. В итоге промежуточные датасеты на Hadoop-кластере раздувались, вызывая переполнение дисков и ошибки нехватки памяти на этапе shuffle.
Кроме того, трансформер требует фиксированной размерности входного тензора. Наивный паддинг приводит к тому, что модель тратит память и вычисления на обработку огромного количества пустых pad-символов. При автоподборе архитектуры потребление памяти на GPU выросло в 5–6 раз по сравнению с обычным обучением.
Мы внедрили комплекс мер:
-
Для Hadoop переписали объединение через map-side join и range join с предварительной фильтрацией и бродкастом коротких последовательностей. Это позволило ускорить этап shuffle в 2–3 раза и устранить ошибки нехватки памяти.
-
Начали собирать последовательности с динамической длиной, что уменьшило нагрузку на Spark. При формировании батча вместо обычных тензоров стали использовать nested tensors, позволяющие формировать батч из последовательностей различной длины. Это уменьшило потребление памяти GPU и ускорило процесс обучения на 20%.
-
Реализовали кастомный downsampler, который кратно уменьшает длины последовательностей, сохраняя при этом значимую часть информации. Это сократило потребление памяти в 3–4 раза и увеличило скорость обучения трансформерной части в 2–3 раза.
В результате время обучения сократилось в среднем на 40% по сравнению с «наивным» подходом.
2. Новизна модуля дифференцируемого подбора архитектуры

При внедрении DARTS мы столкнулись с неожиданной проблемой: несмотря на известность метода, публикаций и детальных примеров кода для его применения к многомодальным трансформерам с fusion-модулями практически не было. При этом:
-
Отсутствовали рекомендации по выбору операций (виды внимания, размеры FFN, способы объединения) в дифференцируемом пространстве.
-
Не было готовых решений для стабилизации обучения при одновременном поиске архитектуры и гиперпараметров — эти два процесса могут конфликтовать.
Мы пошли по пути инженерного исследования:
-
Создали собственный фреймворк поверх PyTorch, который анализирует каркасную структуру разрабатываемой модели и строит класс-обёртку для подбора параметров, используя структурный анализ каждого составляющего модуля.
-
Для синхронизации применили прогрессивное замораживание: сначала идёт поиск архитектуры, затем фиксируется лучшая конфигурация, и уже поверх неё донастраиваются гиперпараметры.
3. Масштаб архитектуры и проблема затухания градиентов

Современный трансформер с fusion-модулем для нескольких модальностей — это десятки подслоёв. При такой глубине и ветвлении стандартные градиенты начинали затухать: сигнал ошибки не доходил до ранних слоёв, особенно до проекций для редких модальностей.
Для решения мы применили комбинацию методов:
-
Pre-normalization вместо post-normalization. Как в трансформерах последних поколений (GPT, ViT), мы перенесли LayerNorm внутрь остаточных блоков — это улучшило поток градиентов.
-
Дифференцируемые residual scaling. Для каждого fusion-блока ввели обучаемый коэффициент, автоматически регулирующий вклад модуля в общий выход.
-
Gradient clipping с адаптивным порогом. Вместо фиксированного значения использовали динамический порог на основе перцентиля нормы градиента — это позволило сохранить обучаемость глубоких ветвей.
Нерешённые проблемы:
-
Мы не нашли универсального решения для совместного использования DARTS и Hyperopt и реализовали их последовательную оптимизацию.
-
Не достигли существенного увеличения скорости сборки и обработки данных на Spark из-за большого объёма данных при объединении в единую таблицу.
Что под капотом?
Чтобы понять масштаб изменений, стоит взглянуть на новую архитектуру сервиса. Весь каркас построен на пяти основных модулях:
-
Сбор данных и формирование выборок.
-
Модельная часть.
-
Фильтрация модальностей.
-
Подбор архитектуры (DARTS).
-
Подбор гиперпараметров (Hyperopt).

Модуль сбора данных и формирования выборок
Прежде чем модель увидит хоть один пример, данные проходят длинный путь преобразований. Это модуль подготовки данных — комбинированный Spark-пайплайн, который работает с двумя типами входных сигналов: классическими числовыми фичами, упакованными в бины, и готовыми векторными представлениями — эмбеддингами. На выходе формируется унифицированная таблица:
|
Поле |
Тип данных |
Описание |
|---|---|---|
|
|
|
Идентификатор клиента |
|
|
|
Момент целевого действия |
|
|
|
Целевая переменная |
|
|
|
Последовательность бинов или эмбеддингов i-й модальности |
|
|
|
Временные метки элементов последовательности i-й модальности |
Этап 1. Формирование последовательностей бинов
Первый этап наследует логику, разработанную в ANNA 1.2, и обрабатывает три категории сырых фичей:
-
Бюро кредитных историй (БКИ) — история кредитных обязательств, просрочек и запросов.
-
Карточные транзакции (КТ) — операции по дебетовым и кредитным картам.
-
Транзакции расчётного счёта (РС) — движения по счетам юридических и физических лиц.
Для каждого источника события клиента упорядочиваются по времени, и из них формируется последовательность бинов. Такое представление приводит разнородные типы фичей к единому формату и делает данные устойчивыми к выбросам.

Этап 2. Формирование последовательностей эмбеддингов
Параллельно с первым этапом пайплайн обрабатывает таблицы с готовыми векторными представлениями — эмбеддингами, посчитанными заранее в отдельных ETL-процессах.
-
Нормализация входных таблиц. Эмбеддинги могут поступать из разных источников с разными схемами данных. Первым делом все таблицы приводятся к единому стандарту: унифицируются имена столбцов, выполняется приведение типов, проверяется корректность векторных представлений.
-
Range join с таблицей таргетов. Каждому обучающему сэмплу должна соответствовать история действий клиента, предшествовавших целевому событию. Для этого выполняется range join: таблица эмбеддингов соединяется с таблицей таргетов по условию попадания временной метки действия в заданное ретроспективное окно.
-
Динамическое определение оптимальной длины последовательности. Вместо ручной настройки этого параметра модуль вычисляет оптимальную длину динамически — анализируя реальное распределение числа событий по выборке на заданном временном окне. Такой подход позволяет автоматически адаптироваться к специфике каждой задачи и набора данных.
-
Сборка упорядоченных последовательностей. Для каждого сэмпла отобранные эмбеддинги сортируются по времени и упаковываются в две параллельные структуры: последовательность векторов seq_modality_i и соответствующую ей последовательность временных меток seq_modality_ts_i. Временные метки используются моделью для учёта относительного расстояния между событиями.
-
Фильтрация разреженных последовательностей. Заключительный шаг — удаление из обучающей выборки сэмплов, у которых длина последовательности оказалась меньше заданного порога. Для валидационной и отложенной выборок отсутствующие модальности записываются как None и не участвуют в формировании результирующего эмбеддинга на этапе инференса.
Этап 3. Объединение модальностей в результирующую таблицу
Финальный этап — горизонтальное объединение всех модальностей по ключу (client, dt). Именно эта структура позволяет обучать одну модель сразу по всем источникам данных — без промежуточных отдельных моделей на каждую модальность.
Модельный модуль
Модельная часть — трансформерная архитектура для одновременной обработки последовательностей бинов и эмбеддингов различных модальностей с объединением через fusion-модуль.

1. Блок обучаемых эмбеддингов
Поскольку фреймворк должен одновременно обрабатывать как последовательности эмбеддингов, так и последовательности бинов, последние необходимо также перевести в эмбеддинги. Для этого используется набор обучаемых модулей, формирующих последовательности эмбеддингов для конкретного таргета.
class Bins2Embeddings(nn.Module):
def __init__(self, bin_dims: int, embedd_dim: int) -> None:
super().__init__()
self.embeddings = nn.Embedding(bin_dims, embedd_dim)
self.dropouts = nn.Sequential(
# Starting with batch len feat emb
nn.Dropout2d(0.1), # Timestep dropout
Rearrange("batch len feat emb -> batch feat len emb"),
nn.Dropout2d(0.1), # Feature dropout
Rearrange("batch feat len emb -> batch emb len feat"),
nn.Dropout2d(0.1), # All (by emb)
Rearrange("batch emb len feat -> batch len feat emb"),
)
def forward(self, x: torch.Tensor) -> torch.Tensor:
res = self.embeddings(x)
res = self.dropouts(res)
res = rearrange(res, "batch len feat emb -> batch len (feat emb)")
return res
2. Блок позиционного кодирования
Для учёта расстояния от событий до целевого действия применяется блок позиционного кодирования. В качестве реализации использовано относительное синусоидальное кодирование, которое позволяет учитывать расстояние от целевого действия до каждого эмбеддинга в последовательности.
class PositionalEncoding(nn.Module):
def __init__(self, max_time: float, dropout: float):
super().__init__()
self.dropout = nn.Dropout(p=dropout)
self.max_time = max_time
def forward(
self,
x: torch.Tensor,
time_deltas: torch.Tensor
) -> torch.Tensor:
batch_size, seq_len, d_embedd = x.size()
device = x.device
time_deltas = time_deltas.to(dtype=x.dtype)
div_term = torch.exp(
torch.arange(0, d_embedd, 2, device=device)
* (-math.log(self.max_time) / d_embedd)
)
pe = torch.zeros(batch_size, seq_len, d_embedd, device=device)
pe[..., 0::2] = torch.sin(time_deltas * div_term)
pe[..., 1::2] = torch.cos(time_deltas * div_term)
x = x + pe
return self.dropout(x)
3. Проекционный блок
Вспомогательный модуль, в котором реализована нормализация и приведение всех эмбеддингов к единой размерности, чтобы стандартизировать модальности для подачи в единый трансформерный модуль.
class SequenceProjection(nn.Module):
def __init__(self, init_size: int, d_model: int, p_dropout: float):
super().__init__()
self.norm = nn.LayerNorm(init_size)
self.head = nn.Sequential(
nn.Linear(init_size, d_model), nn.ReLU(), nn.Dropout(p_dropout)
)
def forward(self, x: torch.Tensor) -> torch.Tensor:
x = self.norm(x)
out = self.head(x)
return out
4. Downsampling-блок
Для оптимизации использования памяти и ускорения обучения применяется модуль уменьшения длин последовательностей. Он реализован через комбинацию одномерных свёрток, что позволяет сохранить информацию и ускорить последующий расчёт внимания трансформера. В зависимости от требуемой выходной длины последовательности используется набор из N ≥ 1 таких свёрток.
class DownsampleBlock(nn.Module):
def __init__(self, seq_len: int, new_seq_len: int, emb_size: int):
super().__init__()
if seq_len <= new_seq_len:
iter_number = 1
else:
iter_number = math.ceil(math.log2(seq_len / new_seq_len))
self.downsaple_blocks = nn.ModuleList(
[
nn.Sequential(
nn.Conv1d(
in_channels=emb_size,
out_channels=emb_size,
kernel_size=3,
stride=2,
padding=1,
),
nn.GELU(),
)
for _ in range(iter_number)
]
)
self.norm = nn.BatchNorm1d(emb_size)
self.act = nn.GELU()
def forward(self, x: torch.Tensor) -> torch.Tensor:
x = x.transpose(1, 2)
for downsaple_block in self.downsaple_blocks:
x = downsaple_block(x)
x = self.norm(x)
x = self.act(x)
return x.transpose(1, 2)
5. Transformer-блок
Стандартный трансформерный энкодер — это стек из L идентичных блоков, состоящих из двух подслоёв: Multi-Head Attention (MHA) и Feed-Forward Network (FFN), — между которыми расположены остаточные соединения и нормализация.
В ANNA 2.0 принципиально важно то, что через один и тот же энкодер с общими весами пропускаются последовательности всех модальностей. Это вынуждает модель строить универсальное пространство признаков. Одновременно за счёт многоголовости подслоя MHA для каждой последовательности могут вычисляться собственные матрицы запросов, ключей и значений.
6. Fusion-блок
После того как энкодер обработал все модальности независимо, перед моделью встаёт принципиально иная задача: как объединить несколько последовательностей разной природы в единое согласованное представление?
Самый наивный путь — раннее слияние (early fusion) — имеет серьёзный изъян: сложность self-attention растёт квадратично с длиной последовательности. Противоположный крайний случай — позднее слияние (late fusion) — теряет почти все промежуточные кросс-модальные сигналы.
Multimodal Bottleneck Transformer Fusion (MBT), предложенный командой Google Research на NeurIPS 2021, нашёл элегантный компромисс. Вместо полного кросс-модального внимания модель ограничивает поток межмодальной информации через небольшое число специальных bottleneck-токенов, которые вынуждают её собирать и конденсировать самое важное из каждой модальности.
Важное архитектурное следствие: MBTF работает непосредственно с полными последовательностями токенов, не требуя предварительного «схлопывания» в один вектор. Это позволяет отказаться от отдельного этапа пулинга.
Модуль фильтрации модальностей

Важным этапом обучения является фильтрация (отбрасывание) модальностей, которые вносят минимальный вклад в финальное предсказание. Вклад модальности определяется по двум критериям:
-
Attention gate fusion — усреднённые веса multi-head attention внутри fusion-блока.
-
Importance gate — обучаемый gate-модуль внутри bottleneck-слоёв.
Алгоритм отбора модальностей:
-
Вычисляется важность модальности на основе описанных критериев.
-
Для каждой важности считается экспоненциальное скользящее среднее (ema) и экспоненциально взвешенная скользящая дисперсия (ewv).
-
По этим значениям берётся окно из последних 10% актуальных значений и вычисляется среднее.
-
Находится максимальная важность (на основе ema).
-
Начиная от наименее значимой модальности проверяются условия на удаление: ema модальности меньше наиболее информативной модальности на 10+%; ewv модальности меньше 10% ema.
-
Если оба условия выполняются — модальность удаляется.
Во время обучения, процесс отбора модальностей выглядит следующим образом:
Модуль подбора архитектуры (DARTS)
Технически DARTS реализован через собственный фреймворк поверх PyTorch. Он анализирует каркасную структуру модели и автоматически строит класс-обёртку (суперсеть), в которой каждый настраиваемый элемент (тип внимания, размер FFN, способ объединения модальностей) представлен как взвешенная сумма возможных вариантов. Архитектурные параметры оптимизируются на валидационной выборке, тогда как веса самой сети — на обучающей.
Модуль подбора гиперпараметров (Hyperopt)
После того как DARTS определил оптимальную архитектуру, запускается байесовская оптимизация числовых гиперпараметров (learning rate, batch size и других) с помощью Hyperopt. Двухэтапная схема — сначала архитектура, затем гиперпараметры — необходима, поскольку одновременная оптимизация обоих пространств приводит к конфликтам и нестабильному обучению.
Практическое использование фреймворка
Мы разделили систему на независимые изолированные компоненты, которые взаимодействуют друг с другом. Ниже представлена общая схема алгоритма работы нашего AutoDL-фреймворка.
Для запуска пайплайна обучения или инференса пользователю необходимо выполнить два обязательных действия:
№1. Подготовить выборку в требуемом формате. Требуются поля: клиент, целевая переменная, целевая дата и поле, определяющее принадлежность сэмпла к выборке.
№2. Создать конфигурационный файл со всеми необходимыми параметрами. Самый простой вариант — воспользоваться готовой функцией библиотеки для генерации конфигурации. Достаточно задать только обязательные параметры, остальные подтянутся автоматически.
import anna
model_name = "anna_test"
path_to_dataset = "dataset.parquet"
generate_config = anna.generate_config(
model_name=model_name,
sample_path=path_to_dataset,
hub_name="automatization",
algorytm="regression",
architecture_oprimization_complexity=3,
jira_task="AUTOML-777",
)
После этого запускается пайплайн обучения, в котором архитектура модели подбирается автоматически.

Модульная структура и интеграция с AutoML

Новая версия фреймворка проектировалась с опорой на принципы модульной архитектуры и строгого разделения ответственности. Основная задача — превратить разрозненные скрипты в целостную систему с предсказуемым поведением и понятным интерфейсом.
Архитектура кода
С архитектурной точки зрения мы пришли к комбинации паттернов Factory Method и Facade. Фабричный слой отвечает за создание ключевых компонентов пайплайна — валидаторов, сборщиков данных и менеджеров моделей — в зависимости от конфигурации задачи. Поверх этого добавлен фасадный слой, который скрывает внутреннюю сложность системы и предоставляет пользователю минималистичный API.
В результате пользователь взаимодействует не с множеством низкоуровневых модулей, а с тремя основными сущностями: Validator, Collector и Manager.
import anna
feature_collector: anna.BaseFeatureCollector = (
anna.FeatureCollectorFactory.create_collector(
config=anna.ProjectConfig(),
task_mode="train",
)
)
feature_collector.collect()
Распространение и деплой
Ключевым изменением стало то, что теперь фреймворк существует не как изолированный репозиторий, а как полноценная pip-библиотека. Она собирается и публикуется во внутренний артифактори, откуда может быть установлена любым специалистом через стандартные инструменты управления зависимостями. Это радикально снижает порог входа.
import anna
config = anna.ProjectConfig("config.yaml")
tml = anna.TrainModelLauncher(config, spark)
tml.launch()
Дополнительно мы реализовали механизм удалённых запусков обучения и инференса: пользователь инициирует выполнение через вызовы anna.remote.train и anna.remote.inference, после чего соответствующий пайплайн автоматически разворачивается на GPU-кластере. Оркестрация выполняется через Argo Workflows, что позволяет эффективно управлять ресурсами, масштабировать вычисления и стандартизировать процесс запуска экспериментов.
Интеграция с AutoML через MIXER

Отдельным компонентом экосистемы стал инструмент MIXER, разработанный на базе внутреннего AutoML-фреймворка ARTEML. Его основная задача — объединение предсказаний различных моделей и систем в единый мета-скор. MIXER позволяет эффективно комбинировать результаты классического AutoML (ARTEML), нейросетевого AutoDL (ANNA), а также любые внешние скоринговые источники.
С технической точки зрения MIXER реализован поверх AutoGluon, что обеспечивает автоматический подбор оптимальной ансамблевой стратегии и устойчивость к различным типам входных моделей. Такой подход позволяет не противопоставлять разные классы моделей, а использовать их сильные стороны совместно.
Бенчмарки и результаты
Сравнение проводится с предыдущей стабильной версией библиотеки ANNA 1.2. Бенчмарки сравниваются по метрикам ROC-AUC и F1. Также измеряется скорость выполнения всего пайплайна.
Метрики
В таблице сравнения метрик ANNA 1.2 и ANNA 2.0 ниже жирным шрифтом выделено наилучшее значение метрики для каждой пары (модель, тип выборки) из двух систем.
|
Модель |
Тип выборки |
ROC-AUC |
F1 |
||
|---|---|---|---|---|---|
|
ANNA 1.2 |
ANNA 2.0 |
ANNA 1.2 |
ANNA 2.0 |
||
|
Склонность 1 |
train |
0.771 |
0.822 |
0.495 |
0.561 |
|
val |
0.763 |
0.786 |
0.486 |
0.507 |
|
|
oot |
0.754 |
0.830 |
0.031 |
0.032 |
|
|
Склонность 2 |
train |
0.937 |
0.943 |
0.736 |
0.749 |
|
val |
0.930 |
0.916 |
0.722 |
0.695 |
|
|
oot |
0.924 |
0.907 |
0.141 |
0.121 |
|
|
Склонность 3 |
train |
0.892 |
0.927 |
0.641 |
0.713 |
|
val |
0.888 |
0.908 |
0.633 |
0.676 |
|
|
oot |
0.914 |
0.924 |
0.168 |
0.233 |
|
|
Склонность 4 |
train |
0.946 |
0.961 |
0.785 |
0.819 |
|
val |
0.945 |
0.949 |
0.784 |
0.790 |
|
|
oot |
0.913 |
0.952 |
0.519 |
0.570 |
|
|
Отток клиентов |
train |
0.805 |
0.831 |
0.512 |
0.539 |
|
val |
0.803 |
0.779 |
0.510 |
0.484 |
|
|
oot |
0.812 |
0.793 |
0.579 |
0.581 |
|
На ряде задач (Склонность 2, Отток клиентов) ANNA 1.2 показала более высокий ROC-AUC на oot-выборке. Мы связываем это со спецификой данных конкретных задач: для них характерно значительное расхождение между распределениями обучающей и отложенной выборок (что видно по разрыву процентного соотношения таргетов), при котором более простая архитектура RNN может оказаться менее склонной к переобучению. Эти кейсы являются предметом дальнейшего исследования.
Скорость
Скорость работы измерялась на модели склонности 2. В качестве фичей рассматривались бины неструктурированных данных: карточные транзакции, данные БКИ и транзакции расчётных счетов. Количество эпох обучения — 20.
|
Этап |
ANNA 2.0 |
ANNA 1.2 |
|---|---|---|
|
Сборка данных |
63 мин. |
63 мин. |
|
Оптимизация архитектуры (DARTS) |
15 мин. |
— |
|
Подбор гиперпараметров (Hyperopt) |
30 мин. |
— |
|
Цикл обучения |
33 мин. |
240 мин. |
Обучение проходило на графическом ускорителе NVIDIA H100 80 GB. Ниже приведена визуализация терминальной команды nvtop, которая демонстрирует потребление памяти графического ускорителя во время самого ресурсозатратного этапа – оптимизации архитектуры (DARTS).
Итоги
По результатам экспериментов обновлённая версия библиотеки не потеряла в качестве предсказаний: на трёх из пяти бенчмарков (Склонность 1, 3 и 4) показала значимый прирост ROC-AUC на oot-выборке, на остальных двух — сопоставимые результаты.
При этом цикл обучения кратно ускорился, а с учётом дополнительных этапов автоподбора архитектуры и гиперпараметров суммарное время обучения сократилось более чем в 3 раза. Не менее важно, что ANNA 2.0 подбирает уникальную архитектуру модели под каждую задачу индивидуально.
Выводы и взгляд на будущее

ANNA 2.0 — это результат глубокой переработки AutoDL-библиотеки Альфа-Банка, затронувшей все уровни: от формирования данных до подбора архитектуры и деплоя.
Архитектура: мы перешли от ансамбля разрозненных RNN к единой мультимодальной трансформерной модели с fusion-модулем (MBTF), способной одновременно работать с множеством источников данных. Автоматический подбор архитектуры через DARTS и гиперпараметров через Hyperopt превратил фреймворк в полноценную AutoDL-систему без ручной настройки.
Скорость: цикл обучения ускорился: суммарное время пайплайна сократилось более чем в 3 раза. Потребление GPU-памяти снизилось в 3–4 раза (сравнительно с начальным подходом) за счёт downsampling-блока и nested tensors.
Качество: на трёх из пяти бенчмарков ANNA 2.0 показала значимый прирост ROC-AUC на отложенной выборке (до +7.6 п.п. на Склонности 1), на остальных двух — сопоставимые результаты.
Инженерия: фреймворк превратился из набора скриптов в полноценную pip-библиотеку с минималистичным API, удалёнными запусками через Argo Workflows и интеграцией с AutoML-платформой через MIXER на базе AutoGluon.
В ближайших планах — завершение ETL-процессов для источников Feature Store и Vertica, добавление новых источников данных, продолжение оптимизации отдельных модулей и экспериментов с новыми архитектурами. Также мы планируем выход в open source после прохождения всех тестов и финализации улучшений.
Отдельно, мы хотели бы сказать огромное спасибо нашим коллегам: Диме Тырину и Андрею Шемчуку. Они помогали нам развивать и улучшать получившийся продукт на каждом этапе его жизненного цикла!
Читайте также:
Автор: 123-39


