Написано Мацеком Дзюбинским и Бартошом Топольским.

tl;dr Мы разработаем четыре контролируемые модели обучения, чтобы решить, исходит ли пара поведенческих паттернов от одного и того же пользователя. Это будут следующие модели: 1) XGBoost, 2) нейронная сеть MLP, 3) простая сиамская нейронная сеть, 4) сиамская рекуррентная нейронная сеть.
Мы недавно провели семинар на конференции PL in ML на тему Идентификация пользователя на основе динамики нажатия клавиш. На семинаре была представлена проблема верификации пользователя с помощью поведенческих данных, собранных во время набора пароля на клавиатуре. Материалы воркшопа доступны здесь, но вы можете сразу запустить код благодаря этому новому горячему сервису под названием MyBinder. Идея состоит в том, что вы просматриваете записную книжку, когда читаете этот пост в блоге, так что уже сейчас имеет смысл нажать Ячейка -> Выполнить все.
Этот пост в блоге служит введением в проблему высокого уровня, обзором моделей и комментарием о том, что еще можно сделать для улучшения моделей. Но что более важно: цель семинара (и, как следствие, этой записи в блоге) состоит в том, чтобы дать вам, читателю, часть работающего кода, с которым вы можете играть, модифицировать, улучшать и бежать как можно легче.
Бесшовная система проверки пользователей значительно повысит безопасность наших электронных счетов, не жертвуя их удобством. Идея состоит в том, чтобы усилить стандартную систему проверки пользователя (обычно это пароль) системой, которая проверяет, соответствует ли поведение пользователя поведению законного владельца учетной записи.
Предположим, вы собрали данные о поведении каждого пользователя во время регистрации. Теперь через некоторое время после регистрации кто-то входит в аккаунт. Мы хотели бы иметь возможность сказать, соответствует ли модель поведения этого «кого-то» модели поведения владельца аккаунта.
Мы сосредоточимся на одном конкретном типе поведенческих данных, а именно на так называемой динамике нажатия клавиш, которая представляет собой последовательность временных меток, соответствующих событиям нажатия/отпускания, записанным во время набора текста. Мы будем обучать четыре модели бинарной классификации, которые для пары нажатий клавиш будут предсказывать, были ли они произведены одним и тем же пользователем. Используя такую модель, мы проверим, соответствует ли новый поведенческий паттерн тем, которые были собраны при регистрации, и на основании этого оценим вероятность того, что мы действительно имеем дело с владельцем аккаунта.
В общем смысле динамика нажатия клавиш относится к подробной информации о времени, описывающей, когда каждая конкретная клавиша была нажата и отпущена, когда человек печатает. Но здесь мы будем работать с менее подробной последовательностью информации о времени, а именно: мы будем иметь доступ только к информации о времени, не зная точно, какая клавиша была нажата/отпущена.
Мы будем использовать набор данных Bei Hang, но вам не нужно его скачивать, он уже включен в репозиторий мастерской в каталоге BeiHang. Есть два подмножества данных: DatasetA и DatasetB — первый был собран в среде интернет-кафе, а второй — в лабораторных условиях. Мы будем работать с DatasetA, так как он более реалистичен.
Набор данных состоит из последовательностей меток времени в следующем формате:
P1, R1, P2, R2, … , Pn, Rn
где Pi, — отметка времени нажатия клавиши iᵗʰ, а Ri — отметка времени отпускания клавиши i клавиша ᵗʰ. Для каждого пользователя у нас есть 4 или 5 таких последовательностей, собранных при регистрации, и около 20 последовательностей, записанных при попытках входа в систему. Среди этих примерно 20 последовательностей некоторые исходили от фактического владельца учетной записи, а другие были введены самозванцами.
Данные считываются в pandas.DataFrame с помощью функции produce_whole_DF (кодовая ячейка [3]), так что вы можете больше сосредоточиться на самих моделях, а не на процессе загрузки и очистка данных.
Пример динамики нажатия клавиш, собранной во время регистрации для двух пользователей (“zhuzhu” и “qianhai18”), представлен в ячейках кода [5] и [6]. . Кроме того, есть также графики для разности временных меток первого порядка (рассчитывается с помощью функции np.diff). Под разностью первого порядка я подразумеваю последовательность out, полученную из последовательности seq следующим образом: out[n] = seq[n+1] — seq[n], или за один шаг: out = np.diff(seq).
Есть две важные характеристики динамики нажатия клавиш: время задержки и время полета. Время задержки — это продолжительность нажатия клавиши, а время полета — это период времени между отпусканием клавиши и нажатием следующей клавиши.

Время пребывания и полета рассчитываются с использованием функций, определенных в ячейке кода [7], а их значения (опять же, для пользователей “zhuzhu” и “qianhai18”) отображаются в ячейках кода [8] и [9].
Теперь эти последовательности (динамика нажатия клавиш, время ожидания и полета) различаются по длине у разных пользователей, тогда как для большинства контролируемых моделей требуются векторы признаков одинаковой длины. Итак, чтобы удовлетворить эти требования, нам нужно каким-то образом агрегировать (или суммировать) эти последовательности переменной длины. Мы возьмем среднее значение и стандартное отклонение времени пребывания, времени полета, а также разности первого порядка. Эти функции вычисляются с помощью функции extract_features (кодовая ячейка [7]), что дает один вектор признаков длины = 6 для каждой динамической последовательности нажатия клавиш. Но обратите внимание, что внутри extract_features есть комментарий:# TODO(1): add more features
TODO, подобные этому, разбросаны по всему блокноту, а число (1, 2 или 3) означает сложность, а 1 означает «легко». , а 3 — «жесткий/сложный».
Легко обмануть себя, думая, что модель работает нормально, хотя на самом деле ее производительность обусловлена какой-то утечкой данных. Таким образом, важно разделить данные на обучающие и тестовые наборы в соответствии с именами пользователей. В противном случае производительность на тестовом наборе будет чрезмерно оптимистичной оценкой того, насколько хорошо модель работает с новыми, невидимыми данными (в данном случае: с новыми пользователями).
Тем не менее, набор данных Bei Hang — не самая чистая и безошибочная коллекция. Чтобы убедиться, что результаты тестового набора не подвержены дефектам данных, я вручную выбрал набор имен пользователей, test_user_names (кодовая ячейка [12] ), который мы будем использовать для определения тестового набора и набора поездов в качестве его дополнения. Это не совсем законно — я использую некоторые произвольные критерии для выбора обучающих и тестовых наборов, а не сосредотачиваюсь на том, чтобы сделать их обоих беспристрастными образцами базового распределения. Однако для целей этого сообщения в блоге давайте предположим, что динамика нажатия клавиш действительно i.i.d. для разных пользователей и что не существует искажающего фактора, вызывающего повреждение некоторых последовательностей определенных пользователей.
Но возвращаясь назад, мы упомянули в разделе Настройка, что будем обучать модели бинарной классификации. Для этого мы объединим наблюдения и пометим их: 1, если пара принадлежит одному пользователю, и 0 в противном случае. Обучение и проверка будут выполняться только для пар наблюдений, собранных при регистрации — см. переменную DF_registration_train в ячейке [12]. (Кстати, во всем коде префикс «DF_» указывает на то, что переменная — это pandas.DataFrame.)
Мы будем обучать четыре модели:
- XGBoost;
- Многослойный персептрон (MLP);
- Простая сиамская нейронная сеть;
- Сиамская рекуррентная нейронная сеть.
С точки зрения того, что модель принимает в качестве входных данных, мы имеем дело с двумя очень разными типами моделей. Первый тип (XGBoost и MLP) — это модели, которые мы будем поставлять с абсолютными различиями функций, извлеченных из последовательностей динамики нажатия клавиш:

а в коде рассчитывается просто как: abs_diff = np.abs(a — b). Метки обозначают, исходят ли две последовательности от одного и того же пользователя. Другими словами: мы будем обучать модель на матрице признаков, строки которой обозначают, как различаются две последовательности динамики нажатия клавиш.
Второй тип моделей (сиамские модели) — это те, которые принимают в качестве входных данных полную информацию, описывающую оба наблюдения:

«Простая сиамская нейронная сеть» будет принимать два связанных вектора признаков (каждый из которых описывает последовательность динамики нажатия клавиш), и это именно то, что показано на изображении выше. Однако «сиамская рекуррентная нейронная сеть» будет использовать две почти необработанные последовательности, описывающие динамику двух нажатий клавиш. «Почти» потому, что на самом деле они будут разностями первого порядка последовательностей временных меток (вспомните функцию np.diff ).
Для обучения и проверки модели мы будем использовать функцию custom_cross_val_auc_score (кодовая ячейка [15]). Что «нестандартного» в нем, так это то, что для каждой складки он разбивает данные на наборы для обучения и проверки в соответствии с именами пользователей. Таким образом, если данный пользователь использовался для обучения, он не будет использоваться для проверки.
Первый аргумент этой функции, оценщик, представляет собой объект с двумя методами: estimator.fit и estimator.predict_proba. Технически, большинство классификаторов, доступных в sklearn, реализуют эти методы (но не все имеют последний; см., например, sklearn.svm.LinearSVC), поэтому кажется, что оценщик может быть просто классификатором-голой частью. Однако объект оценки также должен иметь дополнительные атрибуты: estimator.only_feature_diffs и estimator.feature_names. Первый используется для определения того, будет ли классификатор принимать два вектора признаков, по одному на каждую последовательность динамики нажатия клавиш, или, скорее, их разницу. Последний атрибут используется для определения того, какие столбцы входного фрейма (DF_registration) используются в качестве объектов.
Но нам может потребоваться стандартизировать данные или преобразовать их перед передачей классификатору. Чтобы сохранить атрибуты, ожидаемые от объекта оценки, мы можем обернуть список шагов предварительной обработки и преобразования (вместе с шагом классификации в конце) классом sklearn.pipeline.Pipeline. Это довольно стандартная практика, и если вы раньше с ней не сталкивались — теперь у вас есть шанс. Затем мы можем добавить атрибуты estimator.only_feature_diffs и estimator.feature_names. И это именно то, что мы собираемся делать при обучении наших моделей в ячейках кода: [16], [21], [26] и [29].
xgboost.XGBClassifier — это надежная модель, поскольку она сразу дает хорошие результаты для широкого круга задач без необходимости настройки гиперпараметров. Мы не сможем вдаваться в подробности того, как XGBoost обучается в этом сообщении в блоге, но, возможно, для лучшего ознакомления с алгоритмом обязательно ознакомьтесь с этим источником.
Для наших целей достаточно знать, что XGBoost предлагает хорошую основу для наших последующих моделей на основе нейронных сетей. Если мы сможем достичь производительности XGBoost по умолчанию без особого вмешательства, мы будем считать модель «простой в обращении». Мы увидим, что если с многоуровневыми перцептронами легко обращаться, то с сиамскими архитектурами требуется гораздо больше внимания.
Кроме того, один недостаток XGBoost заключается в том, что его трудно интерпретировать, и даже с помощью таких инструментов, как LIME или ELI5, понимание того, как именно модель делает свои прогнозы, представляет собой проблему.
Модель XGBoost обучается в ячейке кода [16]. Мы наносим значения AUC, рассчитанные с помощью custom_cross_val_auc_score, в виде гистограммы. И хотя я признаю, что гистограмма из четырех значений выглядит немного глупо, она становится оправданной, если вы используете большее количество сгибов (аргумент num_folds функции custom_cross_val_auc_score). В качестве бонуса мы можем взглянуть на то, какие функции были важны для построения модели XGBoost, используя функцию xgboost.plot_importance. Конечно же, наиболее важными характеристиками были средние значения времени пребывания и времени полета, и кажется, что они весьма информативны, поскольку модель достигает высоких значений AUC около 0,90 при проверке.
На данный момент мы сохраним best_xgb_pipe и будем использовать его в самом конце этой записной книжки, чтобы увидеть, насколько хорошо он работает на тестовом наборе вместе с остальными моделями. Вы можете поиграть с аргументами xgboost.XGBClassifier и получить еще лучший результат, но мы бы посоветовали оставить эту ерунду для сиамской рекуррентной нейронной сети.
Мы представили набор данных, потратили много времени на объяснение лазеек, связанных с проверкой, и обучили нашу первую модель XGBoost.
Во второй части мы углубимся в модели на основе нейронных сетей и, наконец, оценим все модели в тестовом наборе.
Быть в курсе!
О Нетоне
Nethone создает решения для борьбы с мошенничеством и бизнес-аналитики на основе искусственного интеллекта, которые помогают предприятиям со всего мира превращать угрозы в хорошо информированные и прибыльные решения. Компания Nethone, основанная в 2016 году опытными специалистами по данным, экспертами по безопасности и руководителями предприятий, является одной из самых быстрорастущих компаний FDP в Центральной Европе.

Если вам понравился этот пост, пожалуйста, нажмите кнопку хлопка ниже 👏👏👏
Вы также можете подписаться на нас в Facebook, Twitter и LinkedIn.
Первоначально опубликовано на сайте blog.daftcode.pl 23 января 2018 г.