Соревнование Data Fusion Contest 2023 в этом году состоялось во второй раз и собрало сильнейшие индустриальные команды и отдельных любителей моделей алгоритмов машинного обучения. Кто‑то участвовал впервые, а кто‑то, уже умудрённый прошлым опытом был явно настроен только на победу.  В этот раз мы решили принципиально изменить задание и придумали новый формат. Что произойдет, если столкнуть лицом к лицу участников, мотивированных атаковать модели машинного обучения, с другими участниками, мотивированными свои модели защищать? Кто победит, каким окажется тот стек моделей и подходов, который приведет к победе? Что важнее, знания и опыт, или гибкость ума или нестандартные подходы?  Мы задали себе все эти вопросы и решили найти ответы на практике, подготовив для участников Data Fusion Contest 2023 очень нестандартное и по теме и по формату соревнование по Adversarial ML с атаками на модели машинного обучения, а также с их защитой.  Давайте разбираться, что из этого получилось по факту, и какие решения предложили участники, чтобы оказаться в рядах победителей!  В качестве данных участникам предстояло работать с синтетически сгенерированным датасетом на основе реальных транзакций. Отправной точкой для соревнования была реальная банковская задача на транзакционных данных: предсказание вероятности дефолта по кредиту для клиента. То есть, это задача бинарной классификации на данных транзакций, с которой активным участникам соревнований уже привычно работать. А раз основа соревнования понятна, то можно сконцентрироваться на самом интересном: новой для всех части про атаки и защиту ML моделей.  Чтобы история про Adversarial ML была полностью раскрыта, мы подготовили для участников 2 задачи:  Задача Атака, где нужно было создавать атаки на входную последовательность транзакций. Атаки были строго регламентированы по своему «бюджету» на возможные изменения. Задача Защита, где нужно было строить модели, способные и защищаться от атак, и не сильно проседать по качеству (а лучше, повышать его).  Мы умышленно не передавали участникам слишком много данных для обучения, чтобы и постановка, и ход решений были больше похожи на то, как если бы история с атаками моделей происходила в реальности.  На помощь участникам, помимо бейзлайнов, мы также предоставили дополнительную нейросетевую модель.  Это была та модель, которую предстояло атаковать в задаче Атака, и по успешному пробитию которой строился лидерборд этой задачи. Сама модель умышленно была не очень сильной. Однако, она была специально обучена на куда большем количестве данных, никак не пересекающихся ни с тренировочными, ни с тестовыми. Некоторые победители соревнования смогли этим воспользоваться в обеих задачах, но об этом чуть позже. Задача Защита проходила в контейнерном формате, где данные были защищены от подглядывания в тестовое множество, а сами решения можно было легко перезапустить на новых тестовых данных. Интересно то, что задача Атака, выглядела для участников как классическая, но «под капотом» тоже была контейнерной. Участникам нужно было отправить свои исправленные (атакованные) входные данные для опубликованной нейросети как табличный файл. Но чтобы проверить успешность атак, модель все равно требуется на этих данных запустить. Также, чтобы поощрить обмен знаниями, идеями и наработками, в обеих задачах была объявлена специальная номинация Companion с денежными призами за лучшее публичное решение.  Регистрация была открыта с 7 февраля по 3 апреля, а само соревнование проходило в 2 этапа, по месяцу каждый. Сперва в течение месяца участники отправляли свои решения на платформу. В конце этапа лучшие 10 команд участников каждой из задач (Атака и Защита) отбирались для участия в турнире.  Турниры запускались в режиме полного перебора «стенка на стенку» между 20 решениями задачи Атака (по 2 решения от каждого из топ-10), и 20 решениями задачи Защита в течение недели после отбора. Именно благодаря турнирам мы и смогли узнать, насколько страшны атаки на ML модели в ситуации, когда от атак будут активно защищаться. Именно из‑за этой логики все призовые в соревновании присуждались по результатам турниров. По сути, по ходу соревнования лидерборды обеих задач использовались только как отборочная квалификация на турниры.  Турниры редко встречаются в соревнованиях по машинному обучению, возможно, из‑за того, что их очень сложно организовать. Нам было очень важно провести промежуточный турнир в середине соревнования, так как на нем участники получили возможность оценить, что такое турнир и что их ждёт в финале. А нам, как организаторам, открывалась возможность обкатать этот формат и по необходимости внести доработки.  Например, после первого турнира мы столкнулись с эффектом «самострела», когда участники, попавшие в топ-10 со стороны обеих задач, столкнулись сами с собой. На промежуточном турнире одна команда смогла таким образом разменять повышение своего места в одной задаче за счет снижения в другой: сильно пробив свою собственную защиту, получили бонус к атаке и штраф к защите. К финальному турниру мы внесли поправку, что «самострелы» не будут учитываться в результатах. Также, к финальному турниру мы разрешили всем участникам выбирать свои турнирные решения, чтобы атакующие и защищающиеся были в более равных условиях. Больше про организацию наших турниров можно прочитать здесь. Чтобы помочь участникам влиться в задачи, мы проводили тематические митапы и воркшопы в начале каждого из этапов соревнования. На встречах мы рассказывали про задачу с ее подходами и бейзлайнами, вместе с участниками разбирали публичные решения участников, делились идеями, а на втором митапе подвели итоги первого этапа соревнования. Финальные итоги и награждения проходили уже на конференции Data Fusion 2023. Материалы с митапов также доступны всем здесь, а трансляции с конференции у нас в ВК. Мы собрали описания лучших решений с их подходами, интересными находками, и общим ходом мысли от тех, кто занял топ-3 в каждой из задач на финальном турнире. Далее, со слов самих победителей: Решение представляет из себя вариацию атаки «Sampling Fool«. Для каждого клиента генерируются 10 000 случайных замен транзакций, которые оцениваются на ансамбле моделей. Ансамбль представляет из себя 100 простых catboost моделей обученных на разных подмножествах данных. Для обучения моделей использовались только фичи связанные с mcc‑кодами и суммой транзакций, то есть только те фичи, на которые может влиять атакующий. Например, часть моделей обучалась только на фичах связанных с количеством подряд идущих одинаковых mcc‑кодов, а другая часть моделей — на фичах связанных с общей суммой транзакций по каждому mcc‑коду. По отдельности, каждая из обученных моделей сильно уступала baseline‑у, но для проведения атаки, качество атакуемых моделей не является значимым. Перед началом атаки, необходимо было для каждого клиента определить направление в какую сторону должен меняться его скор. Для первоначального ранжирования клиентов использовалась baseline‑модель. У 30% клиентов с наивысшим скором, атакующий алгоритм пытался уменьшить скор. У 40% клиентов с наименьшим скором, атакующий алгоритм, наоборот, пытался повысить скор. Оставшиеся 30% клиентов не подвергались атаке и их транзакции оставались без изменений. Алгоритм атаки работает итеративно. На каждой итерации генерируется 1000 новых случайных замен транзакций, которые оцениваются на ансамбле моделей. По результатам оценки выбирается лучшая замена, а ещё 100 наиболее хороших замен переходят в следующую итерацию. По завершению работы алгоритма для каждого клиента формируется 10 замен, которые наибольшим образом меняют скор ансамбля моделей. При генерации замены транзакции, случайно выбирается mcc‑код из набора допустимых, а сумма транзакции выбирается максимально похожей на уже существующие значения по данному mcc‑коду. Это необходимо, чтобы не сильно искажать статистические метрики по mcc‑кодам. Для получения финального submission‑а для небольшого числа пользователей (около 20%) были использованы замены, полученные при атаке на baseline модель вместо ансамбля из catboost моделей. Наше решение можно рассматривать как паззл из трех кусочков. Прежде чем перейти к первому из них, быстро вспомним как выглядело бейзлайн‑решение: Последовательность транзакций для каждого из клиентов перемешивается несколько раз. Для каждой из «перемешек» предсказывался скор предобученной нейронкой от организаторов. Итоговый скор — усреднение предсказаний по всем перемешкам. Оказывается, даже полное перемешивание последовательностей не ухудшает качество предсказаний, при этом исправляя особую уязвимость крайних транзакций для RNN‑сети. Однако, радикально улучшить данную схему сложно — данных для дообучения нейронки очень мало, и ее все также можно легко атаковать сильными градиентными атаками. Бейзлайн показывает нам, что сама последовательная природа данных не сильно важна — можно самостоятельно сделать различные фичи‑агрегаты и обучить на них бустинг, который однозначно потребует меньше данных и будет существенно более стойким к атакам. Однако, полностью выкидывать нейронку тоже не хочется — она была предобучена на намного большем наборе данных, и фактически является дополнительным источником информации. Первым кусочком паззла является обучение (дистилляция) бустингов нейронкой. Вместо того, чтобы использовать скор нейронки в качестве фичи, будем использовать его как целевую переменную, обучаясь под него. Это позволяет нам использовать для обучения неразмеченные данные из атаки и качественно валидироваться на полном наборе размеченных данных. После определенного момента, улучшение качества на исходных данных становится очень сложным — учась имитировать нейронку, бустинги стараются повторять ее во всем, включая ее качество, и не пытаются из фичей вытащить больше, чем нужно. Как заставить бустинг смотреть и выжимать информацию из *разных* фичей? Тут выходит на сцену второй кусочек паззла — разделение всех фичей на малые подмножества и обучение разных бустингов на них. Последующее ансамблирование этих бустингов дает существенные приросты в качестве, так как эти модели содержат в себе очень различную информацию. Для защиты от особенно сильных атак используется третий кусочек паззла — система очистки транзакций, определяющая подмененные и атакованные значения и убирающая их. Система обучалась на наших собственных атаках, сделанных за период соревнования. Это краткое описание нашего решения — на самом деле, за каждым из пунктов скрываются различные нюансы, особенно с учетом новизны задач в соревновании. У нас есть телеграм‑канал Нескучный Data Science (https://t.me/not_boring_ds), в котором есть много разного контента, в том числе про соревнования. Мы скоро напишем полный разбор решения, и выложим его в том числе и там, так что остаемся на связи! В нашем распоряжении имеется банковская RNN модель бинарной классификации, предсказывающая дефолт клиента. Также есть небольшая выборка клиентов. При этом модель банка при обучении не видела доступные нам данные, что хорошо, ведь мы можем использовать её предсказание как признак и построить более сильный алгоритм для классификации. Теперь нужно сделать признаки на основе имеющейся информации: Для клиента банка по каждому mcc коду считаем число уникальных дней (и нормализуем), сумму и среднее среди операций в рублях.  Добавим в таблицу сколько дней/месяцев прошло с минимальной даты. Посчитаем количество уникальных, минимальное и максимальное значение. Важным показателем оказалось отношение моментов, когда была совершена транзакция, к общему количеству между первым и последним.  Аналогично посчитаем по каждому типу транзакции (оплата и поступление) отдельно. Количество по каждой валюте. Количество и число уникальных дней по каждому часу, дню недели, году, месяцу. Дополнительно сумму за каждый час. Среди признаков есть как те, которые меняются при атаках, так и неуязвимые. На валидации качество поднялось с 0.69 до 0.73 Публичное решение на основе примера наивного способа атаки от организаторов сработало неожиданно хорошо 👍. Нужно придумать, как его улучшить. На операциях для дообучения провёл эксперимент. При разделении пользователей на основе их правильных классов удалось уронить модель банка с 0.68 до 0.13, когда без этого метрика падала до 0.42, поэтому будем разделять пользователей, применяя свой классификатор (он лучше). Сильно влияла граница для деления, во всех случаях брал среднее значение (медиана это ошибка). Дополнительные эвристики. Добавим банковские операции для дообучения к тем, что атакуем. Тогда мы сможем выбирать, что подставлять из большего набора. Ещё для них мы знаем правильные метки, так что будем делить их используя. Имеется достаточно много времени на работу решения, нужно пользоваться им по максимуму. Для защиты пользователей от мошенников банки умеют детектировать подозрительные операции. Сделаем такой алгоритм. На одном наборе данных обучим ранкер, его предсказания и ранг будем передавать для обучения классификатора на другом наборе, валидировать будем на том, что для обучения. В роли признаков возьмём характеристики транзакций, на основе времени, общие для пользователя и mcc кода. Метрики получились высокими. Может показаться, что такой алгоритм умеет хорошо определять только мои же атаки, но это не так. Вероятности будем подавать далее в качестве весов. В примере решения предлагается несколько раз перемешивать порядок транзакций, брать часть из них и усреднять предсказания.  Рассмотрим, как улучшить такой подход. Наблюдение, если делать мало повторов, то точность может быть выше, чем при большом количестве. Получается, происходит везение на одних данных, которое не факт, что произойдёт на других. Будем делать 100 повторений, тогда точность начинает сходиться. Каждый раз будем фиксировать разный random seed, у всех пользователей брать равное число (по 280), отключим дублирование, будем использовать полученные ранее веса. В качестве итогового классификатора будем использовать CatBoost. С предыдущего шага будем подавать минимальное, максимальное, их разницу, среднее и медиану. Добавим сгенерированные признаки. Обучать будем и на обычных, и на атакованных данных. Валидировал на 5 группах, выборка стратифицированная. Можно увидеть большой разброс на разных частях. Применим идею с защиты RNN модели и тоже будем несколько раз делать предсказание на части данных (по 275), используя веса. Интересно, что в обоих случаях, хоть мы и рассматриваем не все транзакции, но качество не то, что падает, оно даже увеличивается. Чтобы сделать решение более стабильным и повысить качество, обучим модели с разными случайными сидами и усредним их предсказания. В качестве защитной модели была выбрана базовая модель организаторов на треке Защита. Первым делом прогоняем защиту на исходных транзакциях и сохраняем результаты. Затем для каждого пользователя подбирались разные комбинации атаки транзакций с помощью генетического алгоритма. ГА пытался максимизировать и минимизировать вывод из нейросети защиты. Все результаты этих комбинаций сохранялись. Далее мы имеем для каждого пользователя множество комбинаций и полученных результатов после прогонки защиты, а также результаты на чистых данных. В идеале мы должны инвертировать скор, для этого достаточно чтобы сортировка результатов была обратной сортировке исходных результатов. Этого добиваемся с помощью ГА.   Решение для отбора на финальный турнир и на сам турнир было использовано одно и тоже как основное, которое было лучше на привате и на турнире. Использовался стандартный пайплайн. Генерация признаков на основе сумм транзакций относительно mcc_code, часов, положительных и отрицательных транзакций (min,max,std,mean,median,sum), а также времени между транзакциями (max,std,mean,median) по каждому юзеру. Отсутствующие суммы заполняются нулями. Использовались все mcc_code никакой из них не выбрасывался. Валюта не использовалась. Обучение моделей. Для борьбы с дисбалансом классов использовался параметр «scale_pos_weight». Через GridsearchCV был подобран оптимальный вес положительного класса для Catboost. Чтобы придать больший вес классу меньшинства в градиенте. Другие гиперпараметры не использовались. Ансамбль. Обучены и сохранены 7 моделей Catboost параметры кросс‑валидации (StratifiedKFold) для усредненного предсказания.   Турнир. Стратегия на турнир набрать ROC‑AUC больше 0.7 на чистых данных и не сильно в среднем просесть на атакованных. Учитывая, что данное решение имело уязвимости (mcc_code). Делая более защищенную модель можно было потерять в качестве и потом не добрать на турнире. Прошедшее соревнование по Adversarial ML стало уникальным опытом и для участников, и для набирающей внимание новой темы атак на модели машинного обучения в целом.  Основной результат соревнования можно описать так, что если у участников в распоряжении есть полный доступ к модели и никто ее не защищает, ее можно искажать вплоть до «переворачивания» (1 — ROC‑AUC). А если есть доступ к дополнительным размеченным данным, которые модель не видела, то усугубить ее результаты еще сильнее. Однако, в ситуации турнира, когда атакующие сталкиваются с желающими защищаться, вместо просадки по ROC‑AUC на 0.4+, результат ковровых атак по всем клиентам опускается до 0.04–0.06.  Отметим, что выше — далеко не все решения участников, которыми с нами поделились. Мы постараемся их систематизировать для научной статьи, но это требует времени.  Благодарим всех за активное участие, общение в чате, присутствие на онлайн‑митапах и желание развивать и развиваться в новых для нас всех темах машинного обучения! Мы постараемся и дальше радовать участников интересными задачами. Результаты самих турниров, доступны на страницах задач соревнования (раз и два).  Будем вместе делать data science лучше!