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

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

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

Нам нужна функция user_chargeback_count_last_30_days .
Пакетное задание запускается в конце дня, незадолго до полуночи, и вычисляет количество возвратов платежей за последние 30 дней. Для пользователя U123 это значение равно 1. По состоянию на полночь это фактически верно.

Теперь взгляните на итоговый объединенный обучающий набор данных.
Утренние транзакции в 9:10 и 11:45 уже имеют счетчик возвратов платежей, равный 1. На момент совершения этих платежей возврат платежа еще не был зарегистрирован. Но обучающие данные этого не знают. Время сглажено.
Вот где модель жульничает.

С точки зрения модели, рискованные транзакции уже сопровождаются подтвержденными признаками мошенничества. Восприятие информации в офлайн-режиме значительно улучшается. На данном этапе ничего подозрительного не наблюдается.
Но в процессе производства предполагается, что модель никогда не будет заглядывать в будущее.
При развертывании на ранних этапах транзакций еще не отображается количество возвратов платежей. Сигнал исчезает, и производительность резко падает.
Это не ошибка моделирования. Это утечка предположений.
Скрытое предположение состоит в том, что функция ежедневной пакетной обработки данных действительна для всех событий этого дня. Это не так. Функция действительна только в том случае, если она могла существовать в тот самый момент, когда был сделан прогноз.
Каждая функция должна отвечать на один вопрос:
«Могло ли это значение существовать именно в тот момент, когда было сделано предсказание?»
Если ответ не является уверенным «да», функция недействительна.
Настройки по умолчанию, которые становятся сигналами
После путешествий во времени это очень распространенная причина сбоев, с которыми я сталкивался в производственных системах. В отличие от утечек, эта причина не зависит от будущего. Она зависит от молчания.
Большинство инженеров относятся к отсутствующим значениям как к гигиенической проблеме. Заполняют их средним значением, медианой или каким-либо другим методом восполнения, а затем переходят к следующему шагу.
Эти значения по умолчанию кажутся безобидными. Достаточно безопасными, чтобы модель могла продолжать работу.
Оказывается, такое предположение дорого обходится.
В реальных системах отсутствие данных редко означает случайность. Часто отсутствие данных означает что-то новое, неизвестное, еще не наблюдавшееся или еще не вызывающее доверия. Когда мы сводим все это к одному значению по умолчанию, модель не видит пробела. Она видит закономерность.
Позвольте мне изложить это конкретно.
Впервые я столкнулся с этим в системе обработки мошеннических операций в реальном времени, где мы использовали функцию под названием avg_transaction_amount_last_7_days . Для активных пользователей это значение работало корректно. Для новых или неактивных пользователей конвейер обработки функций возвращал значение по умолчанию, равное нулю.

Чтобы проиллюстрировать, как значение по умолчанию стало надежным индикатором статуса пользователя, я вычислил наблюдаемый уровень мошенничества, сгруппированный по значению функции:
data.groupby(«avg_txn_amount_last_7_days»)[«is_fraud»].mean()
Как показано, у пользователей со значением, равным нулю, наблюдается значительно более низкий уровень мошенничества — не потому, что нулевые траты по своей природе безопасны, а потому, что это неявно обозначает «нового или неактивного пользователя».
Все пользователи со средней суммой транзакций, равной нулю, не являются мошенниками. Не потому, что ноль сам по себе безопасен, а потому, что эти пользователи новые/неактивные. Модель не учится принципу «низкие расходы — это безопасно». Она учится принципу «отсутствие истории транзакций означает безопасность».
По умолчанию это стало сигналом.
В процессе обучения все выглядит хорошо, поскольку точность повышается. Затем происходит изменение производственного трафика.
В часы пик у нижестоящего сервиса начинают возникать проблемы со временем ожидания. Внезапно активные пользователи временно теряют доступ к истории транзакций. Значение avg_transaction_amount_last_7_days обнуляется. Модель уверенно помечает их как пользователей с низким риском.
Опытные команды подходят к этому иначе. Они отделяют отсутствие от ценности, четко отслеживают доступность функций. И самое главное, они никогда не позволяют молчанию выдаваться за информацию.
Сдвиг численности населения без сдвига распределения
Мне потребовалось гораздо больше времени, чтобы распознать эту неисправность, в основном потому, что все обычные сигналы тревоги оставались незамеченными.
Когда говорят о дрейфе данных, обычно подразумевают сдвиг распределения. Гистограммы признаков смещаются. Процентили меняются. Тесты Колмогорова-Смирнова загораются на панелях мониторинга. Всем понятно, что делать дальше. Исследовать исходные данные, переобучить, перекалибровать.
Изменение численности популяции без изменения распределения — это совсем другое дело. В этом случае распределение признаков остается стабильным. Сводные статистические данные практически не меняются. Панели мониторинга выглядят обнадеживающе. И все же поведение модели неуклонно ухудшается.
Впервые я столкнулся с этим в крупномасштабной системе управления рисками платежей, работающей с несколькими сегментами пользователей. Модель учитывала такие характеристики транзакций, как сумма, время суток, сигналы устройства, счетчики скорости и коды категорий продавцов. Все эти характеристики тщательно отслеживались. Их распределение практически не менялось из месяца в месяц.
Тем не менее, уровень мошенничества начал постепенно расти в очень специфическом сегменте трафика. Изменились не данные, а то, кого эти данные представляют.
Со временем продукт расширился и охватил новые группы пользователей. Новые географические регионы с различными платежными привычками. Новые категории продавцов с непривычными моделями транзакций. Рекламные кампании, которые привлекли пользователей, ведущих себя иначе, но все же попадающих в те же числовые диапазоны. С точки зрения распределения, ничего необычного не наблюдалось. Но основная масса пользователей изменилась.
Модель обучалась в основном на опытных пользователях с длительной историей поведения. По мере роста пользовательской базы, всё большая доля трафика приходилась на новых пользователей, чьё поведение статистически выглядело схожим, но семантически отличалось. Сумма транзакции в 2000 означала совершенно разное для давнего пользователя и для новичка. Модель этого не знала, потому что мы её этому не научили.

См. рисунок выше. Он показывает, почему этот тип сбоя трудно обнаружить на практике. Первые два графика показывают распределение объема транзакций и краткосрочной скорости обращения для опытных и новых пользователей. С точки зрения мониторинга, эти характеристики кажутся стабильными, с учетом перекрытия. Если бы это был единственный доступный сигнал, большинство команд пришли бы к выводу, что конвейер обработки данных и входные данные модели остаются работоспособными.
Третий график выявляет реальную проблему. Несмотря на то, что распределения признаков практически идентичны, уровень мошенничества существенно различается в разных группах населения. Модель применяет одни и те же границы принятия решений к обеим группам, поскольку входные данные выглядят знакомыми, но лежащий в их основе риск не одинаков. Изменились не сами данные, а то, кого эти данные представляют.
По мере изменения состава трафика в результате роста или расширения эти предположения перестают быть верными, даже несмотря на то, что данные по-прежнему выглядят статистически нормальными. Без явного моделирования контекста популяции или оценки производительности по группам эти недостатки остаются незаметными до тех пор, пока бизнес-показатели не начнут ухудшаться.
Прежде чем уйти
Ни одна из неудач, описанных в этой статье, не была вызвана некачественными моделями.
Архитектура была разумной. Функционал был продуман до мелочей. Недостатком стала система, лежащая в основе модели, а именно сделанные нами предположения о времени, отсутствии и о том, кого представляют данные.
Время — это не статичный индекс. Метки поступают с задержкой. Признаки развиваются неравномерно. Границы пакетов редко совпадают с моментами принятия решений. Когда мы это игнорируем, модели обучаются на информации, которую они больше никогда не увидят.
Главный вывод таков: высокие показатели в офлайн-режиме не являются доказательством правильности модели. Они доказывают, что модель соответствует заданным вами предположениям. Настоящая работа машинного обучения начинается тогда, когда эти предположения сталкиваются с реальностью.
Проектируйте с учетом конкретного момента.
Список литературы и дополнительные материалы
[1] ROC-кривые и AUC (Краткий курс Google по машинному обучению)
https://developers.google.com/machine-learning/crash-course/classification/roc-and-auc
[2] Тест Колмогорова – Смирнова (Википедия).
https://en.wikipedia.org/wiki/Kolmogorov%E2%80%93Smirnov_test[3] Сдвиги распределения данных и мониторинг (чип Хюен)
https://huyenchip.com/2022/02/07/data-distribution-shifts-and-monitoring.html
Источник: towardsdatascience.com



























