Русский
Русский
English
Статистика
Реклама

Обучение с учителем

Как самому разработать систему обнаружения компьютерных атак на основе машинного обучения

25.01.2021 20:08:49 | Автор: admin

На фото Arthur Lee Samuel, пионер машинного обучения, демонстрирует возможности искусственного интеллекта и играет в шашки с собственной программой Checkers-Playing, одной из первых самообучающихся программ в мире. 1962 год.

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

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

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

Вступление. Машинное обучение и информационная безопасность

Да, сегодня все знают про машинное обучение. Умная колонка и онлайн-кинотеатр угадывают ваше настроение и близко к идеальному предлагают в рекомендациях следующий трек/фильм. Когда вы звоните на горячую линию банка, трудно понять, кто отвечает робот или живой человек. Беспилотные автомобили уже на дорогах общего назначения.

Но как связаны машинное обучение и информационная безопасность?

Признаться, тяжело представить себе распространение технологий AI/ML в проекты и приложения информационной безопасности. Пока не пролистаешь отчет из Стэнфорда AI Index 2019 Report, 220 страниц о современном состоянии дел в области искусственного интеллекта.

Привлечение инвестиций ведущими мировыми разработчиками средств защиты информации с применением технологий искусственного интеллектаПривлечение инвестиций ведущими мировыми разработчиками средств защиты информации с применением технологий искусственного интеллекта

Выводы после прочтения отчета:

  1. Направление Сybersecurity (Network Security) это около 3% мировых частных инвестиций в стартапы, использующие технологии искусственного интеллекта. Впечатляюще.

  2. В блокноте я подсчитал объемы привлеченных инвестиций ведущими мировыми разработчиками средств защиты информации с использованием AI (таблица на рисунке выше). В топ-10 из знакомых имён только Splunk. А кто такие Sophos, Cylance, CrowdStrike, Wangsu, Opzoon? И с какими питчами они привлекают миллиарды долларов?

  3. Среди продуктов лидеров построенного рейтинга антивирусы, средства обнаружения атак, управления инцидентами, анализа защищенности, защиты от утечек, спам-фильтры, threat intelligence, почти вся номенклатура СЗИ. ML, как минимум, делает вид, что работает везде.

Итак, у меня есть желание разобраться в машинном обучении. Есть опыт в разработке средств защиты. Известны примеры успешных реализаций СЗИ c ML. Что мешает попробовать самому, с нуля разработать систему обнаружения компьютерных атак на основе машинного обучения? Вперед!

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

Последовательность шагов

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

  1. Выбор набора данных для обучения системы обнаружения компьютерных атак.

  2. Предварительная обработка данных.

  3. Сэмплирование против дисбаланса классов.

  4. Оценка значимости и отбор признаков.

  5. Сокращение признакового пространства.

  6. Выбор модели.

  7. Настройка и обучение модели.

  8. Тестирование и апробация.

Далее по шагам.

Оригинальная схема обучения с учителем от Sebastian Raschka
Типовая схема обучения с учителемТиповая схема обучения с учителем

Шаг 1. Выбор набора данных для обучения

Для обучения системы обнаружения атак среди доступных публичных наборов данных (DARPA1998, KDD1999, ISCX2012, ADFA2013 и других) я выбрал один из наиболее актуальных (на момент начала исследования) Intrusion Detection Evaluation Dataset CICIDS2017. Разработчик Canadian Institute for Cybersecurity. Набор данных CICIDS2017 подготовлен по результатам анализа сетевого трафика в изолированной среде, в которой моделировались действия 25 легальных пользователей, а также вредоносные действия нарушителей.

Набор объединяет более 50 Гб сырых данных в формате PCAP и включает 8 предобработанных файлов в формате CSV, содержащих размеченные сессии с выделенными признаками в разные дни наблюдения. Краткое описание файлов и количественный состав набора данных представлены в таблицах ниже.

Описание файлов набора данных CICIDS2017Описание файлов набора данных CICIDS2017Количественный состав набора данных CICIDS2017Количественный состав набора данных CICIDS2017Пример одной записи из набора данных CICIDS2017

Каждая запись соответствует сетевой сессии и характеризуется 85 признаками.

Flow ID, Source IP, Source Port, Destination IP, Destination Port, Protocol, Timestamp, Flow Duration, Total Fwd Packets, Total Backward Packets,Total Length of Fwd Packets, Total Length of Bwd Packets, Fwd Packet Length Max, Fwd Packet Length Min, Fwd Packet Length Mean, Fwd Packet Length Std,Bwd Packet Length Max, Bwd Packet Length Min, Bwd Packet Length Mean, Bwd Packet Length Std,Flow Bytes/s, Flow Packets/s, Flow IAT Mean, Flow IAT Std, Flow IAT Max, Flow IAT Min,Fwd IAT Total, Fwd IAT Mean, Fwd IAT Std, Fwd IAT Max, Fwd IAT Min,Bwd IAT Total, Bwd IAT Mean, Bwd IAT Std, Bwd IAT Max, Bwd IAT Min,Fwd PSH Flags, Bwd PSH Flags, Fwd URG Flags, Bwd URG Flags, Fwd Header Length, Bwd Header Length,Fwd Packets/s, Bwd Packets/s, Min Packet Length, Max Packet Length, Packet Length Mean, Packet Length Std, Packet Length Variance,FIN Flag Count, SYN Flag Count, RST Flag Count, PSH Flag Count, ACK Flag Count, URG Flag Count, CWE Flag Count, ECE Flag Count, Down/Up Ratio, Average Packet Size, Avg Fwd Segment Size, Avg Bwd Segment Size, Fwd Header Length,Fwd Avg Bytes/Bulk, Fwd Avg Packets/Bulk, Fwd Avg Bulk Rate, Bwd Avg Bytes/Bulk, Bwd Avg Packets/Bulk,Bwd Avg Bulk Rate,Subflow Fwd Packets, Subflow Fwd Bytes, Subflow Bwd Packets, Subflow Bwd Bytes, InitWinbytesforward, InitWinbytesbackward, actdatapktfwd, minsegsizeforward, Active Mean, Active Std, Active Max, Active Min, Idle Mean, Idle Std, Idle Max, Idle Min, Label

192.168.10.14-65.55.44.109-59135-443-6, 65.55.44.109, 443, 192.168.10.14, 59135, 6, 6/7/2017 9:00, 48, 1, 1, 6, 6, 6, 6, 6, 0, 6, 6, 6, 0, 250000, 41666.66667, 48, 0, 48, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 20, 20833.33333, 20833.33333, 6, 6, 6, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 9, 6, 6, 20, 0, 0, 0, 0, 0, 0, 1, 6, 1, 6, 513, 253, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, BENIGN

Хорошие данные обязательное условие для построения хорошего классификатора.

В обзорах набора данных CICIDS2017 (Intrusion2017, Panigrahi2018, Sharafaldin2018) отмечались проблемы дисбаланса классов, сложной файловой структуры, пропуска значений. Эти замечания я принял как некритичные.

Сегодня у меня есть вопросы по аккуратности разметки набора данных CICIDS2017 (связаться с авторами набора данных и задать вопросы не удалось ни по личным адресам электронной почты, ни через Canadian Institute for Cybersecurity), но именно по этой причине мне пришлось самому разработать весь pipeline начиная от сниффера и предобработки сетевых сессий, заканчивая моделью машинного обучения и тестированием в реальной сети.

Получается, больше приобрел, чем потерял.

Шаг 2. Предварительная обработка данных

Отправной точкой для проведения собственных экспериментов с датасетом CICIDS2017 послужило исследование Kahraman Kostas Anomaly Detection in Networks Using Machine Learning. При попытке воспроизведения этого исследования были обнаружены расхождения в результатах, а потом и ошибки в коде автора. Я связался в апреле 2020 года с Kahraman Kostas, мы обсудили варианты исправления ошибки, однако по состоянию на январь 2021 года код в репозитории по-прежнему некорректно оценивает значимость признаков.

Для сокращения времени вычислений в обучающей выборке был оставлен единственный класс атак веб-атаки (Brute Force, XSS, SQL Injection). Для этого я подготовил подвыборку WebAttacks на основе обработки файла Thursday-WorkingHours-Morning-WebAttacks.pcap_ISCX.csv из набора данных CICIDS2017. Набор WebAttacks включает 458968 записей, из которых 2180 относятся к веб-атакам, остальные к нормальному трафику.

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

Этапы предварительной обработки набора данных CICIDS2017 и подготовки подвыборки WebAttacks
  1. Исключение признака Fwd Header Length.1 (признаки Fwd Header Length и Fwd Header Length.1 являются идентичными).

  2. Удаление записей с null значениями идентификатора сессии Flow ID (из 458968 записей после удаления осталось 170366 записей).

  3. Замена нечисловых значений признаков Flow Bytes/s, Flow Packets/s значениями -1.

  4. Замена неопределенных значений (NaN) и бесконечных значений значениями -1.

  5. Приведение строковых значений признаков Flow ID, Source IP, Destination IP, Timestamp к числовым значениям методом label encoding.

  6. Кодирование ответов в обучающей выборке в соответствии с правилом: 0 нет атаки, 1 есть атака.

После прочтения обсуждения Should I normalize/standardize/rescale the data? понимаю, что вопросы нормирования данных это отдельное исследование.

Я храню Jupyter блокноты на Github в репозитории ml-cybersecurity, а ссылки даю на Google Colabоratory тогда код готов к запуску прямо в браузере.

Исходный код в Google Colabоratory

Матч с канадцами. Поиск и исправление ошибок в датасете

На этапе предварительной обработки данных я обнаружил погрешности в признаковом пространстве (как минимум, подозрительным показалось наличие двух разных признаков с попарно одинаковыми значениями). И решил выполнить проверку: взять сырой трафик CICIDS2017, самому выделить в нём сетевые сессии и сформировать свой датасет. Мой датасет должен был совпасть с датасетом CICIDS2017.

Обрабатываю pcap файл с записанным траффиком своим сниффером, выделяю сессии и признаки, сравниваю с датасетом Thursday-WorkingHours-Morning-WebAttacks.pcap_ISCX.csv, пытаюсь понять и исправить расхождения. Да, расхождения есть.

Подробности поиска ошибок
Выбранная сессия для анализаВыбранная сессия для анализа

Анализирую сессию Flow ID = 192.168.10.14-65.55.44.109-59135-443-6, Source IP = 65.55.44.109. У канадских исследователей два последних пакета выделены в отдельную сессию? Внимательно просматриваю исходники их сниффера и нахожу подтверждение в методе addPacket.

Что нужно учесть в моем сниффере:

  1. При появлении пакета с флагом FIN в направлении forward для воспроизведения эксперимента нужно завершить текущую сессию и создать новую. Чтобы два последних пакета FIN ACK и ACK попали во вторую сессию вместе, условие прерывания сессии нужно дополнить: количество пакетов в сессии должно быть больше 1.

  2. Завершение сессии по таймеру, 120 секунд (хотя в readme сказано про 600 секунд).

Иду дальше. Для той же сессии в прямом направлении зафиксирован один пакет (Total Fwd Packets = 1), при этом общая длина переданных пакетов в прямом направлении Total Length of Fwd Packets = 6. По данным Wireshark, длина пакета = 0. Откуда разница в 6 байт? Спускаюсь от TCP до Ethernet и обнаруживаю неучтенные 6 байт в виде дополнения (padding) фрейма Ethernet. Спорная ситуация, нужно ли включать эти 6 байт в длину TCP пакета.

Проверка длины пакета в WiresharkПроверка длины пакета в Wireshark

Проверяю остальные признаки для этой сессии, совпадают все значения, кроме Average Packet Size = 9. Как при двух пакетах по 6 байт получить значение 9, не ясно. При этом Packet Length Mean = 6, совпадает.

Начинаю проверять другие сессии, и оказывается, что часто встречаются небольшие расхождения в признаках: Packet Length Mean, Packet Length Std, Packet Length Variance , Average Packet Size, Average Fwd Segment Size, Average Bwd Segment Size. Вскрытие (восстановление исходных слагаемых по значениям средних) показывает, что при срабатывании таймаута сессии длина отбрасываемого пакета ошибочно учитывается в статистике.

Как могу, обновляю свой код, добиваться полного соответствия датасетов ( = вносить ошибки) не имеет смысла.

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

Шаг 3. Сэмплирование против дисбаланса классов

Подготовленная подвыборка WebAttacks является насбалансированной: при общем количестве записей 170366 класс нет атаки объединяет 168186 экземпляров, класс есть атака 2180 экземпляров. Для устранения дисбаланса классов подойдет метод случайного сэмплирования (субдискретизация, undersampling), заключающийся в удалении случайно выбранных экземпляров класса нет атаки. Целевое соотношение количества экземпляров классов нет атаки и есть атака я выбрал 70% / 30%.

Шаг 4. Оценка значимости и отбор признаков

Предварительно из признакового пространства были исключены признаки Flow ID, Source IP, Source Port, Destination IP, Destination Port, Protocol, Timestamp в предположении, что признаки формы (соответствующие статистикам сетевого трафика) являются более значимыми для общего случая. Кроме того, исключаемые признаки адресации могут быть относительно легко подделаны злоумышленником и не должны учитываться при обучении.

Анализ значимости признаков я выполнил с помощью встроенного механизма метода sklearn.ensemble.RandomForestClassifier (атрибут feature_importances_).

Первые результаты оценки значимости показали сильную взаимосвязь признаков Init_Win_bytes_backward, Init_Win_bytes_forward с меткой класса в обучающей выборке, что может свидетельствовать о допущенных погрешностях при формировании набора данных. Дополнительные эксперименты показали, что можно построить достаточно точный классификатор, опираясь на один единственный признак или Init_Win_bytes_backward, или Init_Win_bytes_forward. Указанные признаки были исключены из признакового пространства.

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

Результаты оценки значимости признаковРезультаты оценки значимости признаков

Шаг 5. Сокращение признакового пространства

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

Результаты корреляционного анализа двадцати наиболее значимых признаковРезультаты корреляционного анализа двадцати наиболее значимых признаков

Корреляционный анализ показал сильную зависимость между парами признаков:

  1. Average Packet Size и Packet Length Mean.

  2. Subflow Fwd Bytes и Total Length of Fwd Packets.

  3. Fwd Packet Length Mean и Avg Fwd Segment Size.

  4. Flow Duration и Fwd IAT Total.

  5. Flow Packets/s и Fwd Packets/s.

  6. Flow IAT Max и Fwd IAT Max.

По результатам корреляционного анализа из признакового пространства были исключены следующие признаки: Packet Length Mean, Subflow Fwd Bytes, Avg Fwd Segment Size, Fwd IAT Total, Fwd Packets/s, Fwd IAT Max.

После исключения признаков с наименьшей значимостью признаковое пространство было сокращено до объединения 10 признаков:

  1. Average Packet Size, средняя длина поля данных пакета TCP/IP (далее длина пакета).

  2. Flow Bytes/s, скорость потока данных.

  3. Max Packet Length, максимальная длина пакета.

  4. Fwd Packet Length Mean, средняя длина переданных в прямом направлении пакетов.

  5. Fwd IAT Min, минимальное значение межпакетного интервала (IAT, inter-arrival time) в прямом направлении.

  6. Total Length of Fwd Packets, суммарная длина переданных в прямом направлении пакетов.

  7. Fwd IAT Std, среднеквадратическое отклонение значения межпакетного интервала в прямом направлении пакетов.

  8. Flow IAT Mean, среднее значение межпакетного интервала.

  9. Fwd Packet Length Max, максимальная длина переданного в прямом направлении пакета.

  10. Fwd Header Length, суммарная длина заголовков переданных в прямом направлении пакетов.

Шаг 6. Выбор модели

На этапе выбора модели я взял 10 наиболее распространенных моделей машинного обучения и оценил их качество на подвыборке WebAttacks.

Список из 10 моделей

Для сравнения были выбраны следующие модели (алгоритмы) машинного обучения (в скобках указывается сокращенное обозначение и соответствующая реализация модели из состава пакета scikit-learn):

  1. Метод k ближайших соседей (KNN, sklearn.neighbors.KNeighborsClassifier).

  2. Метод опорных векторов (SVM, sklearn.svm.SVC).

  3. Дерево решений (CART, алгоритм обучения CART, sklearn.tree.DecisionTreeClassifier).

  4. Случайный лес (RF, sklearn.ensemble.RandomForestClassifier).

  5. Модель адаптивного бустинга над решающим деревом (AdaBoost, sklearn.ensemble.AdaBoostClassifier).

  6. Логистическая регрессия (LR, sklearn.linear_model.LogisticRegression).

  7. Байесовский классификатор (NB, sklearn.naive_bayes.GaussianNB).

  8. Линейный дискриминантный анализ (LDA, sklearn.discriminant_analysis.LinearDiscriminantAnalysis).

  9. Квадратичный дискриминантный анализ (QDA, sklearn.discriminant_analysis.QuadraticDiscriminantAnalysis).

  10. Многослойный персептрон (MLP, sklearn.neural_network.MLPClassifier).

Качество ответов классификаторов (моделей) сравнивалось с использованием следующих метрик:

  • доля правильных ответов (accuracy);

  • точность (precision, насколько можно доверять классификатору);

  • полнота (recall, как много объектов класса есть атака определяет классификатор);

  • F1-мера (F1-measure, гармоническое среднее между точностью и полнотой).

Оценка качества классификаторов производилась на сбалансированной и предобработанной подвыборке веб-атак WebAttacks набора данных CICIDS2017 (соотношение нормального и аномального трафика 70% / 30%, 20 наиболее значимых признаков). В таблице ниже приведены полученные значения метрик качества, усредненные по результатам 5 итераций кросс-валидации.

Результаты оценки качества десяти классификаторовРезультаты оценки качества десяти классификаторов

Наилучшие результаты ожидаемо продемонстрировали модели (алгоритмы) KNN, CART, RF, AdaBoost, LR. Принимая во внимание минимальное время выполнения, применение модели случайный лес (RF) для решения поставленной задачи является обоснованным выбором.

Исходный код в Google Colaboratory

Шаг 7. Настройка и обучение модели

Итак, за основу я взял модель типа случайный лес, реализация в scikit-learn RandomForestClassifier.

Среди настраиваемых гиперпараметров модели были выбраны следующие: количество деревьев в лесу (n_estimators), минимальное число объектов в одном листе дерева (min_samples_leaf), максимальная глубина дерева (max_depth), максимальное количество признаков для одного дерева (max_features).

Степень квазиоптимальности параметров модели оценивалась значением F1-меры.

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

RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',max_depth=17, max_features=10, max_leaf_nodes=None,min_impurity_decrease=0.0, min_impurity_split=None,min_samples_leaf=3, min_samples_split=2,min_weight_fraction_leaf=0.0, n_estimators=50,n_jobs=None, oob_score=False, random_state=1, verbose=0,warm_start=False)
Пример настройки

Пример результатов подбора одного гиперпараметра (max_depth) при фиксированных значениях других гиперпараметров (n_estimators, min_samples_leaf, max_features) представлен на рисунке ниже в виде зависимости метрики качества (F1-меры) от значения настраиваемого параметра (max_depth).

Зависимость F1-меры модели от параметра max_depthЗависимость F1-меры модели от параметра max_depth

Шаг 8. Тестирование и апробация

Настроенная и обученная модель RandomForestClassifier на тестовой выборке позволила получить оценку полноты (recall) 0.961 и F1-меры 0.971 (запуск 1 в протоколе эксперимента, см. таблицу ниже). Достигнутый результат свидетельствует о возможности повышения точности модели за счет квазиоптимального подбора гиперпараметров (результаты исследования Kahraman Kostas recall 0.94 и F1-мера 0.94, результаты авторов CICIDS2017 recall 0.97 и F1-мера 0.97).

Для апробации модели на реальной сетевой инфраструктуре я разработал свой сетевой анализатор сниффер (C#). Анализатор позволяет перехватить передаваемый сетевой трафик и с использованием алгоритмов реконструкции TCP сессий свободно распространяемых программных продуктов Wireshark и TCP Session Reconstruction Tool выделить отдельные сессии. Для каждой сохраненной сессии сниффер на основе алгоритма CICFlowMeter выделяет признаки и таким образом формирует набор данных.

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

Нормальный трафик соответствовал запросам легальных пользователей на подключение к консоли администратора и авторизацию. Вредоносный (аномальный) трафик моделировался программным средством OWASP ZAP и включал три типа атак: Brute Force, XSS, SQL Injection. Соотношение нормального и аномального трафика в реальном тестовом наборе данных составило 70% / 30%.

Схема стенда тестированияСхема стенда тестированияПодробно про запуск ZAP

Пример защищаемого приложения (заимствован из поста: Как за один день разработать SIEM).

Пример URL, соответствующего попытке входа в защищаемое приложение: http://192.168.121.129/buggy/admin.php?password=12345&username=admin

ZAP запускается в режиме фаззинга, при котором будут перебираться различные значения параметра password. Важно: параметр username сразу устанавливается равным admin. Будем считать, что злоумышленник знает имя администратора наверняка. В противном случае, можно для фаззера указать оба параметра, но и количество попыток возрастёт до N*N для двух параметров вместо N для одного (N количество строк в словаре).

Нагрузки (payloads, словари) для проведения атак Brute Force, XSS и SQL Injection я взял из репозитория github.com/danielmiessler/SecLists.

Как узнать, подобрал ли фаззер пароль при реализации атаки Brute Force? Я знаю, что в защищаемом веб-приложении страница авторизованного пользователя отличается от приветственной. Поэтому достаточно отсортировать результаты работы фаззера по размеру ответа столбец Size Resp. Body. И в строке с размером ответа, отличающимся от всех остальных, обнаружить пароль admin.

Успешный запуск фаззераУспешный запуск фаззера

Проведенные эксперименты на сформированном наборе данных (запуски 2, 3 в протоколе эксперимента) показали невозможность применения модели, обученной на наборе данных CICIDS2017, по следующим причинам:

  1. Анализ обучающей выборки показывает, что характер моделируемых компьютерных атак в исследовании авторов набора данных CICIDS2017 отличается от реального. Так, атаки типа Brute Force присутствуют в сессиях с максимальными скоростями до 10 Кбит/c, что не соответствует случаям применения автоматизированных средств перебора паролей.

  2. Среди десяти признаков с наибольшей значимостью четыре признака Flow Bytes/s (скорость потока данных), Fwd IAT Min (минимальное значение межпакетного интервала в прямом направлении), Flow IAT Std (среднеквадратическое отклонение значения межпакетного интервала), Flow IAT Mean (среднее значение межпакетного интервала) непосредственно зависят от физической структуры сети, в которой производится сбор сетевого трафика, а также настроек сетевого оборудования. В обучающем наборе данных сессии с признаками веб-атак записаны с низкими значениями скорости потока и высокими значениями межпакетных интервалов, что не соответствует характеристикам реальной сетевой инфраструктуры (сеть Ethernet 100 Мбит/c).

Как подготовить свой датасет

Хороший датасет должен удовлетворять определенным требованиям. У авторов CICIDS2017 есть работа, в которой перечислено 11 таких требований. Главное из этого: обеспечить разнообразие сетевого оборудования, компьютеров и операционных систем в тестовой инфраструктуре, разнообразие потоков сетевого трафика по разным направлениям, разнообразие протоколов и типов атак, разметить данные для атак и для чистого трафика.

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

План сбора датасета:

1 этап. Запись pcap файлов, очистка. При сборе грязного трафика изменяем параметры фаззера и ставим паузы во время фаззинга, чтобы разорвать сессии и увеличить их количество в датасете. При сборе чистого трафика моделируем разные действия пользователя.

2 этап. Подача pcap файлов на вход сниффера и выделение признаков. Объединение всех размеченных записей в один датасет.

Протокол экспериментаПротокол эксперимента

Важно. Запуск 2. Модель была обучена на подвыборке WebAttacks набора данных CICIDS2017 (трафик собирался в одной сети). После этого я протестировал модель на реальном трафике в другой сети, отличающейся скоростью и другими характеристиками от первой. И получил неудовлетворительное качество значение F1-меры 0.064.

Оценка вычислительной сложности производилась косвенным способом: разработанный в среде Jupyter Notebook макет системы обнаружения веб-атак запускался на персональном компьютере (процессор Intel Core i5-2300 CPU @ 2300 ГГц, ОЗУ 8 Гб) в режиме обнаружения. Тестовый набор данных содержал около 70000 записанных сессий, время обнаружения составило 0,74669 с. Таким образом, скорость обнаружения веб-атак оценивается величиной порядка 100000 сессий в секунду.

Подведение итогов

Итак, завершен эксперимент с разработкой модели случайный лес для решения задачи обнаружения компьютерных атак. Модель обучена на публичном наборе данных CICIDS2017 и протестирована в реальных условиях.

Настройка параметров выбранного классификатора RandomForestClassifier пакета scikit-learn позволила на тестовой выборке получить оценку полноты (recall) 0.961 и F1-меры 0.971 для набора данных CICIDS2017 и 0.966 и 0.882 соответственно для сформированного в исследовании набора данных.

Главный вывод эксперимента: методы машинного обучения на практике применимы для обнаружения компьютерных атак.

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

  1. Характер моделируемых компьютерных атак при сборе обучающего набора данных отличался от реального.

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

Что исправить, чтобы получилось? Идеально обучать модель на наборе данных, размеченном на основе анализа сетевого трафика в защищаемой сети. При использовании предобученной в другой сети модели (проблема transfer learning) обязательным является соответствие физической структуры защищаемой сети и сети, в которой обучалась модель, а также настроек сетевого оборудования.

Личные выводы. В начале освоения пути исследователя данных применение машинного обучения представлялось в виде 10 строчек кода из примера на scikit-learn.org. Сегодня, глядя на >500 строк кода в итоговом блокноте, я понимаю, сколько еще улучшений можно сделать и как развить решение.

Этот эксперимент занял больше двух лет, без спешки, в перерывах между чтением книг, статей, прохождением курсов, участием в соревнованиях по ML и выступлениями на конференциях. Я был нубом на старте, но смог разобраться и пройти этот увлекательный трек. Впереди новые знания и эксперименты. Но уже сегодня я уверен: современный инженер должен знать, как работает машинное обучение. Чтобы быть ближе к будущему!

Полезные ссылки

Исходный код эксперимента (блокноты, сниффер, разное): github.com/fisher85/ml-cybersecurity

Книги, которые помогли в исследовании:

  • Talabis M. Information Security Analytics.

  • Sumeet D. Data Mining and Machine Learning in Cybersecurity.

  • Lee K.-F. AI Superpowers: China, Silicon Valley, and the New World Order.

  • McAfee A. Machine, Platform, Crowd. Harnessing Our Digital Future.

  • Chio С. Machine Learning and Security: Protecting Systems with Data and Algorithms.

История прохождения курсов обучения, все бесплатные:

Цвет титульной фотографии восстановлен здесь

Подробнее..

Прокачиваем разметку мультимодальных данных меньше асессоров, больше слоёв

18.08.2020 18:15:11 | Автор: admin

Всем привет! Мы учёные лаборатории Машинное обучение ИТМО и команда Core ML ВКонтакте проводим совместные исследования. Одна из важных задач VK заключается в автоматической классификации постов: она необходима не только чтобы формировать тематические ленты, но и определять нежелательный контент. Для такой обработки записей привлекаются асессоры. При этом стоимость их работы можно значительно снизить с помощью такой парадигмы machine learning, как активное обучение.


Именно о его применении для классификации мультимодальных данных и пойдёт речь в этой статье. Мы расскажем про общие принципы и методы активного обучения, особенности их применения к задаче, а также инсайты, полученные в ходе исследования.


image


Введение


Активное обучение это раздел machine learning, где модель взаимодействует с учителем. Она запрашивает у него для тренировки лишь те данные, которые позволят обучиться лучше и, следовательно, быстрее.


Это направление интересно компаниям, которые привлекают асессоров для разметки данных (например, с помощью сервисов Amazon Mechanical Turk, Яндекс.Толока) и хотят удешевить этот процесс. Один из вариантов использовать reCAPTCHA, где пользователь должен отмечать снимки, скажем, со светофорами, и заодно получать бесплатную разметку для Google Street View. Другой способ применять активное обучение.


Некоторые уже сейчас используют его для оптимизации краудсорсинга и делятся опытом. Например, Voyage компания, которая занимается автопилотируемыми автомобилями. В статье её специалисты рассказали, как активное обучение позволило им не только сэкономить на разметке данных, но и повысить предельную точность модели. Их подход к активному обучению очень похож на тот, о котором расскажем мы.


Компания Amazon в своей работе описывает фреймворк DALC (Deep Active Learning from targeted Crowds). Он раскрывает концепцию активного обучения с точки зрения нейронных сетей, байесовского подхода и краудсорсинга. В исследовании в том числе используется техника Monte Carlo Dropout (о ней мы тоже поговорим в этой статье). Ещё авторы вводят любопытное понятие noisy annotation. Если в большинстве работ по активному обучению предполагается, что асессор говорит правду и ничего, кроме правды, то здесь допускается вероятность ошибки в силу человеческого фактора.


Про ещё одно исследование от Amazon можно прочитать здесь. В нём рассматривается концепция иерархической разметки: когда асессор вместо классической одноклассовой разметки объекта должен дать бинарный ответ о его принадлежности к определённому надклассу/классу в иерархии. При этом бинарные вопросы, как и объект для разметки, выбирает сам алгоритм. Таким образом, конечная разметка может получиться неполной: будет определён не конечный класс объекта, а его категория. Однако этого будет достаточно для обучения.


Но хватит говорить про использование парадигмы пора рассмотреть активное обучение в деталях! У него есть несколько основных подходов, или сценариев. В нашем исследовании модель взаимодействовала с учителем по сценарию pool-based sampling.


Рис. 1. Общая схема pool-based сценария активного обучения
Рис. 1. Общая схема pool-based сценария активного обучения


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


Наиболее полезные данные отправляются на разметку эксперту, чтобы затем вернуться для дообучения модели уже в активной фазе. Отправленные на разметку данные называют запросом (англ. query). Запрос может состоять как из одного объекта, так и из нескольких. Способы выбора объектов пула для запроса к эксперту (иными словами, способы оценить полезность данных) называются стратегиями активного обучения.


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


Набор данных и задача


Напомним, общая задача классификация постов ВКонтакте. Они представляют собой мультимодальные данные (изображение и текст). Предоставленный набор данных включает 250 тыс. готовых эмбеддингов постов. Здесь каждый объект (пост) размечен одним из 50 классов тематик публикаций и опционально содержит:


  1. векторное представление, или эмбеддинг (англ. embedding), картинки поста;
  2. векторное представление текста.

Стоит отметить, что набор данных сильно несбалансирован (см. рис. 2).


Рис. 2 гистограмма распределения классов
Рис. 2 гистограмма распределения классов


Модель для классификации


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


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


В проекте мы экспериментально изучили различные конфигурации глубоких нейронных сетей. Проводились эксперименты с добавлением residual соединений, highway блоков, использованием энкодеров (англ. encoder). Рассмотрели разные варианты, учитывающие мультимодальность на основе слияния (англ. fusion): метод внимания для мультимодальных данных, матричное слияние.
Но некоторые способы учёта мультимодальности данных невозможно было применить к нашей задаче например, выравнивание и обучение различным представлениям. Это связано с готовым представлением данных в виде предобученных векторов-эмбеддингов.


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


Критерием для выбора итоговой модели стала максимизация значения метрики валидационной точности. Итак, в качестве классификатора была подобрана следующая архитектура (рис. 3):


Рис. 3. Подобранная архитектура для классификации
Рис. 3. Подобранная архитектура для классификации


В данной модели производится позднее слияние модальностей. Идея в том, что эмбеддинги картинки и текста сначала обрабатываются по отдельности. Такой подход позволяет уменьшить размер нейронной сети, которая сперва извлекает из каждой модальности необходимую информацию, а потом объединяет их для финального предсказания. Кроме того, три головы модели (отдельно на текст и картинку + объединённая) дополнительно стимулируют сеть обучать веса чтобы извлекать как можно больше релевантной информации для классификации по каждой из модальностей.


Блоки, обозначенные красным и синим на рис. 3, имеют следующий вид:


Рис. 4. Описание основных блоков модели нейронной сети для классификации
Рис. 4. Описание основных блоков модели нейронной сети для классификации


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


Один из закономерных и важных вопросов, которые возникли при построении этой архитектуры: как считать функцию потерь? Варианты:


  1. простое покомпонентное суммирование элементов функции потерь с разных голов;
  2. взвешенная функция потерь с ручным перебором весов;
  3. взвешенная функция потерь с обученными весами голов.

Мы выбрали третий вариант. Ещё вдохновились статьёй из области байесовского глубокого обучения: в ней комбинации функций потерь выводятся исходя из формулы maximum likelihood и учёта неопределённости предсказаний, возникающих из-за шума в данных. В итоге мы остановились на такой функции потерь:


$L = \frac{1}{\sigma_1 ^ 2}L_1 + \frac{1}{\sigma_2 ^ 2}L_2 + \frac{1}{\sigma_3 ^ 2}L_3 + \log{\sigma_1} + \log{\sigma_2} + \log{\sigma_3}$


где $L_1, L_2, L_3$ функции потерь для разных выходов модели (в нашем случае они представляют собой категориальную кросс-энтропию), а $\sigma_1, \sigma_2, \sigma_3$ настраиваемые параметры, характеризующие дисперсию и шум в данных.


Pool-based sampling


С моделью определились теперь опишем, как будем применять и тестировать различные стратегии активного обучения. Согласно сценарию pool-based sampling мы составили пайплайн экспериментов:


  1. Берём из тренировочного набора данных какое-то количество случайных объектов.
  2. Обучаем на них модель.
  3. Выбираем новую пачку данных из оставшегося тренировочного набора, основываясь на тестируемой стратегии, и добавляем их к размеченным данным.
  4. Дообучаем модель.
  5. Считаем метрики (валидационную точность).
  6. Повторяем шаги 35 до выполнения определённого критерия (например, пока не кончится весь тренировочный набор данных).

Первые два шага соответствуют пассивной фазе обучения, шаги 36 активной.


Помимо самой стратегии, в данном пайплайне значимыми являются два параметра, а именно:


  1. Размер изначального набора данных, на котором обучается модель во время пассивной фазы. Если этот параметр окажется недостаточным, то сложно будет сравнить эффект от активного обучения и дообучения на случайных данных: точность будет стремительно расти в обоих случаях. Если же, напротив, сделать начальный размеченный набор слишком большим, то модель будет хорошо обучена уже в пассивной фазе. Тогда в активной рост точности будет слабым вне зависимости от метода обучения. В нашем случае оптимальным оказался размер изначального набора данных, равный 2000.


  2. Размер запроса к эксперту. С одной стороны, можно отправлять объекты к эксперту по одному. В этом случае первый объект в запросе будет максимизировать критерий рассматриваемой стратегии активного обучения (при сортировке объектов по убыванию соответствия критерию). И после обучения на этом объекте остальные в запросе, скорее всего, перестанут представлять интерес. Но если выбирать объекты по одному, то эксперимент затянется и всё исследование усложнится. Поэтому мы остановились на 20 объектах в запросе.
    Ещё можно варьировать количество шагов в фазе активного обучения. Очевидно, что с его увеличением точность модели может расти. Но цель нашего проекта не достичь максимальной возможной точности классификации, а исследовать эффективность активного обучения. Поэтому мы решили зафиксировать количество шагов на 100 или 200.



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


Инсайт 1: влияние выбора batch size


В качестве baseline рассмотрим, как обучается модель при случайном выборе данных (будем называть это пассивным обучением) (рис. 5).


Рис. 5. График обучения baseline-модели пассивным способом. Приведён результат пяти запусков с доверительным интервалом
Рис. 5. График обучения baseline-модели пассивным способом. Приведён результат пяти запусков с доверительным интервалом


Для достоверности этот и все последующие эксперименты запускались по пять раз с разным random state. На графиках выводится средняя точность запусков с доверительным интервалом.


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


Чтобы устранить этот эффект, нужно осознать важность параметра размера батча (англ. batch size). В нашем случае он был по дефолту выбран равным 512 из-за большого количества классов (50). Получалось, что при конечном размере размеченного набора данных и фиксированном batch size последний батч мог оказаться крайне мал. Это вносило шум в значение градиента и негативно сказывалось на обучении модели в целом. Мы опробовали следующие варианты решения этой проблемы:


  1. upsample, чтобы порции данных были одной длины;
  2. увеличение числа эпох обучения, чтобы влияние маленького батча нивелировалось последующими.

Итоговым решением стало формирование адаптивного batch size: на каждом шаге активного дообучения он вычислялся согласно формуле (1).


$current\_batch\_size =b + \Big \lfloor\frac{n \mod b}{\lfloor\frac{n}{b}\rfloor}\Big\rfloor [1]$


где $b$ изначальный batch size, а $n$ текущий размер размеченного набора данных.
Адаптивный подход помог сгладить просадки точности и получить монотонно возрастающий график (рис. 6).


Рис. 6. Сравнение использования фиксированного параметра batch size (passive на графике) и адаптивного (passive + flexible на графике)
Рис. 6. Сравнение использования фиксированного параметра batch size (passive на графике) и адаптивного (passive + flexible на графике)


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


Теперь перейдём непосредственно к исследованию методов активного обучения для нашей задачи.


Uncertainty


Первыми были реализованы наиболее простые стратегии активного обучения из обзорной статьи методы группы uncertainty sampling. Как следует из названия, стратегия основана на выборе для разметки тех объектов, в предсказании которых модель наименее уверена.


В статье приводятся три варианта подсчёта неуверенности:


1. Минимальная уверенность (англ. Least confident sampling)


В этом виде стратегии эксперту для разметки передаётся объект, наиболее вероятный предсказанный класс для которого характеризуется наименьшей уверенностью:


$x^{*}_{LC} = \underset{x}{\arg\max} \ 1 - P_{\theta }(\hat{y}|x) [2]$


где $\hat{y} = \underset{y}{\arg\max}\ P_{\theta}(y|x)$ класс с наибольшей вероятностью при классификации моделью, $y$ один из возможных классов, $x$ один из объектов набора данных, $x^{*}_{LC}$ объект, выбранный с помощью стратегии наименьшей уверенности объект.


Подробнее

Эту меру можно понимать так. Допустим, функция потерь на объекте выглядит как $1-\hat{y}$. В таком случае модель выбирает объект, на котором получит худшую оценку значения функции потерь. Она обучается на нём и тем самым уменьшает значение функции потерь.


Но у этого метода есть недостаток. Например, на одном объекте модель получила следующее распределение по трём классам: {0,5; 0,49; 0,01}, а на другом {0,49; 0,255; 0,255}. В таком случае алгоритм выберет второй объект, так как его наиболее вероятное предсказание (0,49) меньше, чем у первого объекта (0,5). Хотя интуитивно понятно, что бльшую информативность для обучения имеет первый объект: вероятности первого и второго класса в предсказании почти равны. Алгоритм стоит модифицировать, чтобы учитывать такие ситуации.


2. Минимальный отступ (англ. Margin sampling)


Согласно этому виду стратегии, алгоритм отправит на экспертизу те объекты, для которых наибольшую вероятность имеют два класса, причём эти вероятности близки:


$x^{*}_{M} = \underset{x}{\arg\min} \ P_{\theta }(\hat{y}_{1}|x) - P_{\theta }(\hat{y}_{2}|x)[3]$


где $\hat{y}_1$ наиболее вероятный класс для объекта $x$, $\hat{y}_2$ второй по вероятности класс.


Подробнее

С точки зрения информации этот метод выигрывает, так как алгоритм учитывает в два раза больше данных о распределении вероятностей классов. Однако и он не идеален, так как не рассматривает распределения по всем остальным классам. При этом, например, в MNIST (популярном наборе данных для классификации рукописных цифр) содержится десять классов соответственно, во внимание принимается лишь пятая часть информации о распределении. Преодолеть этот недостаток позволяет метод максимальной энтропии.


3. Максимальная энтропия (англ. Entropy sampling)


В этом виде стратегии для измерения неуверенности модели используется мера энтропии:


$x^{*}_{H} = \underset{x}{\arg\max} -\sum \ P_{\theta }(y_{i}|x)\log{P_{\theta }(y_{i}|x)}[4]$


где $y_{i}$ вероятность $i$-го класса для объекта $x$ при классификации данной моделью.


Подробнее

Энтропия удобна тем, что она обобщает два метода, которые мы описали выше. Она выбирает объекты обоих типов:


  • те, где наиболее вероятное предсказание имеет меньшее значение, чем предсказания для других объектов;
  • и те, для которых два наиболее вероятных класса имеют близкие значения.

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


Но практические результаты в рамках решаемой задачи показали расхождение с теорией (рис. 7).


Рис. 7. Результаты сравнения различных видов стратегии uncertainty sampling с пассивным обучением (слева с методом минимальной уверенности, по центру с методом минимального отступа, справа с методом максимальной энтропии)
Рис. 7. Результаты сравнения различных видов стратегии uncertainty sampling с пассивным обучением (слева с методом минимальной уверенности, по центру с методом минимального отступа, справа с методом максимальной энтропии)


Как можно заметить, методы least confident и entropy sampling показали себя хуже, чем пассивное обучение со случайным выбором объектов для дообучения. В то же время margin sampling оказался более эффективным.


Чтобы читатели не подозревали, что на реализацию методов могли повлиять баги, сразу отметим: все методы были протестированы также на наборе данных MNIST. И на нём, например, стратегия entropy sampling продемонстрировала результаты, не противоречащие теоретической эффективности метода. Можно сделать вывод, что практическая эффективность описанных методов неоднозначна и зависит от конкретной решаемой задачи.


Перечисленные методы просты с точки зрения реализации и обладают низкой вычислительной сложностью. Можно оценить сложность одного запроса к эксперту как $O(p\log{q})$, где $p$ размер неразмеченного набора данных, а $q$ число объектов в запросе к эксперту. Также эти методы легко применять на практике, так как они не требуют изменения используемой модели.


BALD


Следующая стратегия, о которой пойдёт речь, BALD sampling (Bayesian Active Learning by Disagreement). Это байесовский подход к измерению неуверенности комитета моделей.


Согласно классификации методов активного обучения, это представитель стратегии query-by-committee (QBC). Её основная идея в использовании предсказаний нескольких моделей с конкурирующими гипотезами. Можно брать их усреднённое предсказание за основу для uncertainty sampling. Или выбирать для разметки те объекты, в предсказаниях которых модели несогласны в большей степени. Эксперименты проводились с методом QBC на основе Monte Carlo Dropout, речь о котором пойдёт дальше.


Проблема классических байесовских методов для глубокого обучения в том, что необходимо выводить большое количество параметров, а это делает обучение моделей в два раза дороже. Поэтому авторы предложили использовать dropout в качестве способа байесовской аппроксимации. Этот подход отличается от привычного применения dropout тем, что он используется во время инференса (на стадии предсказания). Причём для каждого объекта выборки предсказание делается несколько раз одной и той же моделью, но с разными dropout-масками (рис. 8). Такой способ сэмплирования называется Monte Carlo Dropout (MC Dropout) и не требует увеличения затрат памяти при обучении модели. Так с помощью одной модели можно получить несколько предсказаний, которые могут различаться для одного и того же объекта. Несогласие моделей (где они отличаются друг от друга лишь масками dropout) считается на основе Mutual Information (MI). MI здесь представляет собой эпистемическую неопределённость, или неуверенность комитета, то есть такой вид неопределённости, которая уменьшается с добавлением новых данных. Это согласуется с концепцией активного обучения в целом.


Рис. 8. Иллюстрация MC Dropout для метода BALD
Рис. 8. Иллюстрация MC Dropout для метода BALD


Итак, для начала мы использовали усреднённое предсказание полученного QBC на основе MC Dropout комитета и применили к нему различные методы uncertainty sampling. Это не дало прироста по сравнению с соответствующими методами, использующими только одно предсказание (рис. 9).


Рис. 9. Результаты сравнения различных видов стратегии uncertainty sampling на основе QBC и без него с пассивным обучением (слева - с методом минимальной уверенности, по центру - с методом минимального отступа, справа - с методом максимальной энтропии)
Рис. 9. Результаты сравнения различных видов стратегии uncertainty sampling (на основе QBC и без него) с пассивным обучением (слева с методом минимальной уверенности, по центру с методом минимального отступа, справа с методом максимальной энтропии)


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


$a_{BALD}=\mathbb{H}(y_1,...,y_n)-\mathbb{E}[\mathbb{H}(y_1,...,y_n|\omega)] [5]$


$\mathbb{E}[\mathbb{H}(y_1,...,y_n|w)]=\frac{1}{k}\sum_{i=1}^{n}\sum_{j=1}^{k}\mathbb{H}(y_i|w_j) [6]$


где $n$ число классов, $k$ число моделей в комитете.


Первое слагаемое в формуле (5) представляет собой энтропию усреднённого предсказания комитета, второе среднюю энтропию каждой модели в отдельности. Таким образом, выбираются только те объекты, в предсказании для которых комитет менее всего согласен. Результаты применения метода BALD представлены на рис. 10.


Рис. 10. Результаты применения стратегии BALD в сравнении с пассивным способом обучения
Рис. 10. Результаты применения стратегии BALD в сравнении с пассивным способом обучения

К сожалению, пока что данный метод не дал ожидаемого результата на долгом запуске эксперимента, несмотря на прирост по сравнению с пассивным методом в начале.
Сложность алгоритмов стратегии query-by-committee в целом и BALD в частности пропорциональна числу предсказаний, сделанных для каждого объекта. В свою очередь, сложность предсказания для каждого объекта аналогична методам uncertainty sampling. Таким образом, сложность одного запроса $O(kp\log(q))$, где $p$ размер неразмеченного набора данных, $q$ число объектов в запросе к эксперту, а $k$ число предсказаний, посчитанных для одного объекта.


На практике применить метод BALD может быть нелегко при использовании фреймворка tf.keras, так как он не обладает достаточной гибкостью для работы со слоями. Поэтому в рамках данного проекта мы выбрали фреймворк PyTorch, который позволил не только с легкостью включать dropout во время инференса, но и отключать batch normalization в течение активной фазы, о чем пойдет речь далее.


Инсайт 2: отключение batch normalization в активной фазе


Подобранная модель классификации в своей структуре использует слои batch normalization. Суть подхода batch normalization в обучении параметров нормализации данных во время обучения и применении найденных параметров во время инференса, или предсказания. Идея, которую мы использовали, состоит в том, чтобы рассматривать активную фазу обучения как этап инференса, и отключать на ней обучение batch normalization. К тому же интуитивно кажется, что такой подход позволит избежать смещения модели. Насколько нам известно, данный вопрос ещё не исследовался в отношении методов активного обучения. Для экспериментов мы взяли за основу метод BALD. Рассмотрим результаты (рис. 11).


Рис. 11. Результаты отключения batch normalization для метода BALD в сравнении со стандартным методом и пассивным обучением
Рис. 11. Результаты отключения batch normalization для метода BALD в сравнении со стандартным методом и пассивным обучением


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


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


Learning loss


Теперь взглянём на проблему активного обучения с другой стороны. Предположим, что неуверенность модели относительно классов тех или иных объектов пропорциональна значению функции потерь от предсказаний для этих объектов. Но пока мы не знаем реального класса объекта, не можем посчитать значение функции потерь.


Создадим вспомогательную модель, принимающую на вход выходы промежуточных и последних слоёв модели. Задача вспомогательной модели предсказывать значение функции потерь. Мы будем выбирать для разметки те объекты, для которых это значение максимально. Этот метод называется learning loss, подробнее про него можно почитать здесь. Рассмотрим результаты первичных экспериментов, где метод применялся для базовой модели (рис. 12).


Рис. 12. Результаты применения Learning loss для базовой модели в сравнении с ее пассивным обучением
Рис. 12. Результаты применения Learning loss для базовой модели в сравнении с ее пассивным обучением

Метод learning loss не дал прироста по сравнению с пассивным обучением на случайно выбранных объектах. Логично было бы использовать его для моделей других архитектур или отказаться от него как от неэффективного для нашей задачи.
Но мы вместо этого попробуем провести следующий эксперимент. В обычном сценарии активного обучения модель не знает настоящих меток классов, а в нашей задаче они известны. Это позволяет посчитать идеальный learning loss: зная настоящую метку класса объекта, будем считать на нём значение функции потерь и добавлять в размеченный набор данных те объекты, у которых оно больше. Назовём такой подход ideal learning loss (рис. 13).


Рис. 13. Результаты применения ideal learning loss для базовой модели в сравнении с ее пассивным обучением
Рис. 13. Результаты применения ideal learning loss для базовой модели в сравнении с её пассивным обучением

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


  1. Обучаем модель на начальной выборке (2000 объектов), как для активного обучения;
  2. Выбираем из всего набора неразмеченных данных 10000 объектов (чтобы ускорить подсчёт);
  3. Для выбранных объектов неразмеченной выборки считаем значения функции потерь;
  4. Сортируем объекты по полученным значениям;
  5. Разбиваем на группы по 100 объектов;
  6. Для каждой группы параллельно обучаем на ней модель, стартуя с весов, полученных на шаге 1;
  7. Фиксируем получившиеся точности.

Далее считаем корреляцию Спирмена между точностью модели, обученной на определённой выборке, и средним значением функции потерь по каждому из объектов выборки. А также вычисляем, как средняя точность модели коррелирует со средним значением параметра отступа (из метода margin sampling).


Таблица 1. Корреляции точности и метрик активного обучения для набора данных публикаций ВКонтакте


Корреляция Спирмена p-value
Точность и loss -0,2518 0,0115
Точность и margin 0,2461 0,0136

Как видим, для метода margin sampling корреляция слабая и положительная то есть, зная отступ, можно быть уверенными, что объект полезен для обучения модели. А в случае c функцией потерь корреляция слабая отрицательная.


Возникает вопрос: а что если попробовать выбирать для разметки объекты с наименьшими значениями функции потерь?
Как ни странно, эксперименты показали, что и такой вариант тоже не работает ожидаемым образом (рис. 14).


Рис. 14. Результаты применения обратного ideal learning loss для базовой модели в сравнении с прямым ideal learning loss и пассивным обучением
Рис. 14. Результаты применения обратного ideal learning loss для базовой модели в сравнении с прямым ideal learning loss и пассивным обучением


Несмотря на плохие результаты для целевого набора данных, для набора данных MNIST были получены следующие значения корреляции:


Таблица 2. Корреляции точности и метрик активного обучения для набора данных MNIST


Корреляция Спирмена p-value
Точность и loss 0,2140 0,0326
Точность и энтропия 0,2040 0,0418

При этом сам метод ideal learning loss работает так, как ожидается (рис. 15).


Рис. 15. Активное обучение классификатора символов из набора данных MNIST стратегией ideal learning loss. Синий график ideal learning loss, оранжевый пассивное обучение
Рис. 15. Активное обучение классификатора символов из набора данных MNIST стратегией ideal learning loss. Синий график ideal learning loss, оранжевый пассивное обучение

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


Сложность метода learning loss та же, что и для методов uncertainty sampling: $O(p\log{q})$, где $p$ размер неразмеченного набора данных, а $q$ число объектов в запросе к эксперту. Но важно учесть, что при его применении обучать нужно не только основную модель, но и вспомогательную. Этот метод сложнее предыдущих в практическом применении ещё и потому, что требует проводить обучение каскада моделей.


Заключение


Не будем бесконечно раздувать статью, рассказывая обо всех применённых методах и проведённых экспериментах. Здесь мы постарались осветить основные и наиболее интересные. Любопытно, что эффективнее всех оказался самый первый и простой метод margin sampling результаты его длинного запуска можно увидеть на рис. 16.
Рис. 16. Сравнение обучения на случайно выбираемых данных (пассивное обучение) и на данных, выбираемых стратегией margin sampling
Рис. 16. Сравнение обучения на случайно выбираемых данных (пассивное обучение) и на данных, выбираемых стратегией margin sampling


График показывает: тренируя модель с помощью активного обучения (в нашем случае стратегией margin sampling), можно достичь предельной точности такой же, как у модели, обученной пассивным способом. Но при этом использовать на 25 тыс. объектов меньше. Экономия ресурсов разметки составит порядка 25% это довольно значимо.


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


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


  • выбор batch size;
  • целесообразность использования для активного обучения подходов, зарекомендовавших себя в глубоких нейронных сетях, например, batch normalization.
Подробнее..

Нейронки с нуля, или Как мы делали помощника для наших диспетчеров техподдержки

23.07.2020 12:09:17 | Автор: admin
Привет, Хабр!
Меня зовут Александр Соловьев, я программист компании DataLine.

Хочу поделиться опытом внедрения модных нынче нейронных сетей в нашей компании.
Все началось с того, что мы решили строить свой Service Desk. Зачем и почему именно свой, можно почитать моего коллегу Алексея Волкова (cface) тут.

Я же расскажу о недавнем новшестве в системе: нейросеть в помощь диспетчеру первой линии поддержки. Если интересно, добро пожаловать под кат.



Уточнение задачи

Головная боль любого диспетчера службы поддержки нужно быстро принять решение, кому назначить входящий запрос клиента. Вот какие бывают запросы:
Добрый день.
Я правильно понимаю: чтобы расшарить календарь на определенного пользователя, нужно на ПК пользователя, который хочет поделиться календарем, открыть доступ к своему календарю и вписать почту пользователя, которому он хочет дать доступ?

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

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

The sites were down again for few minutes from 2020-07-15 14:59:53 to 2020-07-15 15:12:50 (UTC time zone), now they are working fine. Could you please check and let us know why the sites are fluctuating many times.

Thanks

Бывали ситуации, когда заявка уходила не тому подразделению. Запрос брали в работу и затем переназначали на других исполнителей или отправляли обратно диспетчеру. Это увеличивало скорость решения. Время на решение запросов прописано в соглашении с клиентом (SLA), и мы несем ответственность за соблюдение сроков.

Внутри системы мы решили создать помощника для диспетчеров. Основная цель была добавить подсказки, которые помогают сотруднику принимать решение по заявке быстрее.

Больше всего не хотелось поддаться новомодной тенденции и посадить чат-бота на первой линии поддержки. Если вы хоть раз пытались написать в такую техподдержку (уже кто только этим не грешит), вы понимаете, о чем я.

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

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

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

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

Изучение теории

Оказалось, что моя задача классическая задача классификации. На входе алгоритм получает первичный текст заявки, на выходе относит его к одному из заранее известных классов то есть, подразделений компании.

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

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

Итак, я начал погружение в тему нейросетей отсюда. Изучил алгоритмы обучения нейросетей: с учителем (supervised learning), без учителя (unsupervised learning), с частичным привлечением учителя(semi-supervised learning) или с подкреплением(reinforcement learning).

В рамках моей задачи подошел метод обучения с учителем. Данных для обучения хоть отбавляй: over 100k решенных заявок.

Выбор реализации

Для реализации выбрал библиотеку Encog Machine Learning Framework. К ней идет доступная и понятная документация с примерами. К тому же реализация под Java, что мне близко.

Кратко механика работы выглядит так:
  1. Предварительно настраивается каркас нейросети: несколько слоев нейронов, соединенных связями-синапсами.

  2. В память загружается набор данных для обучения с заранее известным результатом.
  3. Данные для обучения прогоняются через каркас нейросети. В результате у каждого синапса в зависимости от данных выстраиваются веса.По этим весам нейросеть принимает то или иное решение.
  4. После прогоняем нейросеть через тестовую выборку и анализируем ошибку сети: насколько результат, выдаваемый нейросетью, соответствует известному результату из набора данных.
  5. Пункты 3 и 4 повторяются до получения минимально допустимой ошибки. При этом важно, чтобы на каждой следующей итерации ошибка уменьшалась. Если этого не происходит, значит какие-то из параметров были выбраны некорректно.

Я попробовалразличные примеры фреймворка, пришло понимание, что с цифрами на входе библиотека справляется на ура. Так, хорошо работал пример с определением класса ирисов по размеру чаши и лепестков (Ирисы Фишера).

Но у меня-то текст. Значит, буквы нужно как-то превратить в цифры. Так я перешел к первому подготовительному этапу векторизации.

Первый вариант векторизации: по буквам

Самый простой способ превратить текст в цифры это взять алфавит на первом слое нейросети. Получаются 33 буквы-нейрона: АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЬЭЮЯ.
Каждой присваивается цифра: наличие буквы в слове принимаем за единицу, а отсутствиесчитаем нулем.
Тогда слово привет в такой кодировке будет иметь вектор:


Такой вектор уже можно отдать нейросети для обучения. Ведь это число 001001000100000011010000000000000 = 1216454656

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

Второй вариант векторизации: по словарю

А если взять не буквы, а слова? Скажем, толковый словарь Даля. И считать за 1 уже наличие слова в тексте, а отсутствие за 0.

Но тут я уперся в количество слов. Вектор получится очень большой. Нейронка с 200k нейронов на входе будет считаться целую вечность и захочет много-много памяти и процессорного времени.Надо делать свой словарь. К тому же в текстах есть ИТ-специфика, которую Владимир Иванович Даль не знал.

Я снова обратился к теории. Для сокращения словаря при обработке текстов пользуются механизмами N-грамм последовательности из N элементов.

Идея в том, чтобы входной текст разбить на некоторые отрезки, составить из них словарь и в качестве 1 или 0 скормить нейросети наличие или отсутствие фразы в исходном тексте. То есть вместо буквы, как в случае с алфавитом, за 0 или 1 будет принята не просто буква, а целая фраза.

Самыми популярными являются униграммы, биграммы и триграммы. На примере фразы Добро пожаловать в ДатаЛайн расскажу о каждой из методик.
  1. Униграмма текст разбивается на слова: добро, пожаловать, в, ДатаЛайн.
  2. Биграмма разбиваем на пары слов: добро пожаловать, пожаловать в, в ДатаЛайн.
  3. Триграмма аналогично по 3 слова: добро пожаловать в, пожаловать в ДатаЛайн.
  4. N-грамма ну вы поняли. Сколько N, столько и слов подряд.
  5. Также иногда используют символьные N-граммы. Текст делится не на слова, а на последовательность букв. Например 4-символьная N-грамма выглядела бы так: добр,о по, жало, вать и т. д. Но тут получится очень большой словарь.

Я решил ограничится униграммой. Но не просто униграммой слов все равно получалось многовато.

На помощь пришел алгоритм Стеммер Портера, который использовался для унификации слов еще в 1980 году.

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

Дополнительно я убрал из словаря все цифры, знаки пунктуации, предлоги и редко встречающиеся слова, дабы не создавать шума. В итоге на 100k текстов получился словарь в 3k слов. С этим уже можно работать.

Обучение нейросети

Итак у меня уже есть:
  1. Словарь из 3k слов.
  2. Векторизованное представление словаря.
  3. Размеры входного и выходного слоев нейросети.
    По теории на первом слое (входном) предоставляется словарь, а финальный слой (выходной) количество классов-решений. У меня их 44 по количеству подразделений компании.



Для обучения нейросети осталосьвыбрать совсем немного:
  1. Метод обучения.
  2. Функцию активации.
  3. Количество скрытых слоев.

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

Итак, я взял эталонную выборку заявок в 11k и делал расчет нейросети с различными параметрами:
  1. На 10k я обучал нейронную сеть.
  2. На 1k проверял уже обученную сеть.

То есть на 10k мы строим словарь и обучаемся. А затем показываем обученной нейросети 1k неизвестных текстов. В итоге получается процент ошибки: отношение угаданных подразделений к общему числу текстов.

В итоге я добился точности на неизвестных данных около 70%.

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

Вот какие параметры получились в итоге:

Метод обучения. Encog предлагает выбрать из нескольких вариантов: Backpropagation, ManhattanPropagation, QuickPropagation, ResilientPropagation, ScaledConjugateGradient.

Это разные способы определения весов у синапсов. Какие-то из методов работают быстрее, какие-то точнее, подробнее лучше почитать в документации. Мне подошел Resilient Propagation.

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

Выбирал из 16 вариантов. Проверить все функции у меня не хватило времени. Поэтому рассматривал самые популярные: сигмоид и гиперболический тангенс в различных реализациях.

В итоге остановился на ActivationSigmoid.

Количество скрытых слоев. По теории, чем больше скрытых слоев, тем дольше и сложнее расчет.Начал с одного слоя: расчет был быстрый, но результат неточный. Я остановился на двух скрытых слоях. С тремя слоями считалось заметно дольше, а результат не сильно отличался от двухслойного.

На этом эксперименты закончил. Можно готовить инструмент к продуктиву.

В продакшен!

Дальше дело техники.
  1. Прикрутил Spark, чтобы можно было общаться с нейронкой по REST.
  2. Научил сохранять результаты расчета в файл. Не каждый же раз пересчитывать при перезапуске сервиса.
  3. Добавил возможность вычитывать актуальные данные для обучения напрямую из Service Desk. Ранее тренировался на csv-файлах.
  4. Добавил возможность пересчета нейросети, чтобы прикрутить пересчет к планировщику.
  5. Собрал все в толстый jar.
  6. Попросил у коллег сервер помощнее разрабской машины.
  7. Задеплоил и зашедулил пересчет раз в неделю.
  8. Прикрутил кнопку в нужное место Service Desk и написал коллегам, как этим чудом пользоваться.
  9. Сделал сбор статистики по тому, что выбирает нейронка и что выбрал человек (статистика ниже).


Вот так выглядит тестовая заявка:


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


Получился такой интеллектуальный помощник для диспетчера.

Для примера статистика с начала года.
<

Есть еще совсем молодая нейросеть, сделанная по тому же принципу. Но там данных еще мало, она пока набирается опыта.


Буду рад, если мой опыт поможет кому-нибудь в создании своей нейросети.
Если есть вопросы, с удовольствием отвечу.
Подробнее..

Машина опорных векторов в 30 строчек

27.02.2021 16:15:57 | Автор: admin
В этой статье я расскажу как написать свою очень простую машину опорных векторов без scikit-learn или других библиотек с готовой реализацией всего в 30 строчек на Python. Если вам хотелось разобраться в алгоритме SMO, но он показался слишком сложным, то эта статья может быть вам полезна.



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



Узнав, что на Хабре есть возможность вставлять медиаэлементы, я создал небольшое демо (если вдруг не сработает можно ещё попытать счастья с версией на гитхабе [1]). Поместите на плоскость (пространство двух фич $X$ и $Y$) несколько красных и синих точек (это наш датасет) и машина произведёт классификацию (каждая точка фона закрашивается в зависимости от того, куда был бы классифицирован соответствующий запрос). Подвигайте точки, поменяйте ядро (советую попробовать Radial Basis Functions) и твёрдость границы (константа $C$). Мои извинения за ужасный код на JS писал на нём всего несколько раз в жизни, чтобы разобраться в алгоритме используйте код на Python далее в статье.

Содержание



  • В следующем разделе я бегло опишу математическую постановку задачи обучения машины опорных векторов, к какой задаче оптимизации она сводится, а также некоторые гиперпараметры, позволяющие регулировать работу алгоритма. Этот материал лишь подводящий к нашей конечной цели и нужен чтобы вспомнить ключевые факты, если такое напоминание не требуется можете его пропустить без ущерба для понимания дальнейших частей. Если же вы ранее не сталкивались с машинами опорных векторов, то понадобиться куда более полное изложение обратите внимание на соответствующую лекцию уже ставшего классикой курса Воронцова [2] или на десятую лекцию курса [3], в которую, кстати, входит представленный ниже метод.
  • В разделе Алгоритм SMO я подробно расскажу как решить поставленную задачу минимизации упрощенным методом SMO и в чём, собственно, состоит упрощение. Будут выкладки, но их объём гораздо меньше, чем в тех подходах к SMO, что доводилось видеть мне.
  • Наконец, в разделе Реализация будет представлен код классификатора на Python и схема обучения в псевдокоде.
  • Узнать насколько алгоритм удался можно в разделе Сравнение с sklearn.svc.svm там приведено визуальное сравнение для небольших датасетов в 2D и confusion matrix для двух классов из MNIST.
  • А в Заключении что-нибудь да заключим.



Машина опорных векторов



Машина опорных векторов метод машинного обучения (обучение с учителем) для решения задач классификации, регрессии, детектирования аномалий и т.д. Мы рассмотрим ее на примере задачи бинарной классификации. Наша обучающая выборка набор векторов фич $\boldsymbol{x}_i$, отнесенных к одному из двух классов $y_i = \pm 1$. Запрос на классификацию вектор $\boldsymbol{x}$, которому мы должны приписать класс $+1$ или $-1$.

В простейшем случае классы обучающей выборки можно разделить проведя всего одну прямую как на рисунке (для большего числа фич это была бы гиперплоскость). Теперь, когда придёт запрос на классификацию некоторой точки $\boldsymbol{x}$, разумно отнести её к тому классу, на чьей стороне она окажется.

Как выбрать лучшую прямую? Интуитивно хочется, чтобы прямая проходила посередине между классами. Для этого записывают уравнение прямой как $\boldsymbol{x} \cdot \boldsymbol{w} + b = 0$ и масштабируют параметры так, чтобы ближайшие к прямой точки датасета удовлетворяли $\boldsymbol{x} \cdot \boldsymbol{w} + b = \pm 1$ (плюс или минус в зависимости от класса) эти точки и называют опорными векторами.

В таком случае расстояние между граничными точками классов равно $2/|\boldsymbol{w}|$. Очевидно, мы хотим максимизировать эту величину, чтобы как можно более качественно отделить классы. Последнее эквивалентно минимизации $\frac{1}{2} |\boldsymbol{w}|^2$, полностью задача оптимизации записывается

$ \begin{aligned} &\min \frac{1}{2} |\boldsymbol{w}|^2 \\ \text{subject to: } &y_i \left(\boldsymbol{x}_i \cdot \boldsymbol{w} + b\right) - 1 \geq 0. \end{aligned} $

Если её решить, то классификация по запросу $\boldsymbol{x}$ производится так

$ \text{class}(\boldsymbol{x}) = \text{sign}\left(\boldsymbol{x} \cdot \boldsymbol{w} + b\right). $

Это и есть простейшая машина опорных векторов.

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

Мы уже не можем решить предыдущую задачу оптимизации не существует параметров удовлетворяющих тем условиям. Тогда можно разрешить точкам нарушать границу на величину $\xi_i \geq 0$, но также желательно, чтобы таких нарушителей было как можно меньше. Этого можно достичь с помощью модификации целевой функции дополнительным слагаемым (регуляризация $L_1$):

$ \begin{aligned} &\min\left( \frac{1}{2} |\boldsymbol{w}|^2 + C \sum_i \xi_i \right)\\ \text{subject to: }&\xi_i + y_i \left(\boldsymbol{x}_i \cdot \boldsymbol{w} + b\right) - 1 \geq 0,\\ &\xi_i \geq 0, \end{aligned} $

а процедура классификации будет производиться как прежде. Здесь гиперпараметр $C$ отвечает за силу регуляризации, то есть определяет, насколько строго мы требуем от точек соблюдать границу: чем больше $C$ тем больше $\xi_i$ будет обращаться в ноль и тем меньше точек будут нарушать границу. Опорными векторами в таком случае называют точки, для которых $\xi_i > 0$.

А что если обучающая выборка напоминает логотип группы The Who и точки ни за что нельзя разделить прямой?

Здесь нам поможет остроумная техника трюк с ядром [4]. Однако, чтобы ее применить, нужно перейти к так называемой двойственной (или дуальной) задаче Лагранжа. Детальное ее описание можно посмотреть в Википедии [5] или в шестой лекции курса [3]. Переменные, в которых решается новая задача, называют дуальными или множителями Лагранжа. Дуальная задача часто проще изначальной и обладает хорошими дополнительными свойствами, например, она вогнута даже если изначальная задача невыпуклая. Хотя ее решение не всегда совпадает с решением изначальной задачи (разрыв двойственности), но есть ряд теорем, которые при определённых условиях гарантируют такое совпадение (сильная двойственность). И это как раз наш случай, так что можно смело перейти к двойственной задаче

$ \begin{aligned} &\max_{\lambda} \sum_{i=1}^n \lambda_i - \frac12 \sum_{i=1}^n \sum_{j=1}^n y_i y_j (\boldsymbol{x}_i \cdot \boldsymbol{x}_j) \lambda_i \lambda_j,\\ \text{subject to: } &0 \leq \lambda_i \leq C, \quad \mbox{ for } i=1, 2, \ldots, n,\\ &\sum_{i=1}^n y_i \lambda_i = 0, \end{aligned} $

где $\lambda_i$ дуальные переменные. После решения задачи максимизации требуется ещё посчитать параметр $b$, который не вошёл в двойственную задачу, но нужен для классификатора

$ b = \mathbb{E}_{k,\xi_k \neq 0}\left[y_k - \sum_i \lambda_i y_i (\boldsymbol{x}_i \cdot \boldsymbol{x}_k)\right]. $

Классификатор можно (и нужно) переписать в терминах дуальных переменных

$ \text{class}(\boldsymbol{x}) = \text{sign}(f(\boldsymbol{x})),\quad f(\boldsymbol{x}) = \sum_i \lambda_i y_i (\boldsymbol{x}_i \cdot \boldsymbol{x}) + b. $

В чём преимущество этой записи? Обратите внимание, что все векторы из обучающей выборки входят сюда исключительно в виде скалярных произведений $(\boldsymbol{x}_i \cdot \boldsymbol{x}_j)$. Можно сначала отобразить точки на поверхность в пространстве большей размерности, и только затем вычислить скалярное произведение образов в новом пространстве. Зачем это делать видно из рисунка.

При удачном отображении образы точек разделяются гиперплоскостью! На самом деле, всё ещё лучше: отображать-то и не нужно, ведь нас интересует только скалярное произведение, а не конкретные координаты точек. Так что всю процедуру можно эмулировать, заменив скалярное произведение функцией $K(\boldsymbol{x}_i; \boldsymbol{x}_j)$, которую называют ядром. Конечно, быть ядром может не любая функция должно хотя бы гипотетически существовать отображение $\varphi$, такое что $K(\boldsymbol{x}_i; \boldsymbol{x}_j)=(\varphi(\boldsymbol{x}_i) \cdot \varphi(\boldsymbol{x}_j))$. Необходимые условия определяет теорема Мерсера [6]. В реализации на Python будут представлены линейное ($K(\boldsymbol{x}_i; \boldsymbol{x}_j) = \boldsymbol{x}_i^T \boldsymbol{x}_j$), полиномиальное ($K(\boldsymbol{x}_i; \boldsymbol{x}_j) = (\boldsymbol{x}_i^T \boldsymbol{x}_j)^d$) ядра и ядро радиальных базисных функций ($K(\boldsymbol{x}_i; \boldsymbol{x}_j) = e^{-\gamma |\boldsymbol{x}_i - \boldsymbol{x}_j|^2}$). Как видно из примеров, ядра могут привносить свои специфические гиперпараметры в алгоритм, что тоже будет влиять на его работу.

Возможно, вы видели видео, где действие гравитации поясняют на примере натянутой резиновой пленки в форме воронки [7]. Это работает, так как движение точки по искривленной поверхности в пространстве большой размерности эквивалентно движению её образа в пространстве меньшей размерности, если снабдить его нетривиальной метрикой. Фактически, ядро искривляет пространство.


Алгоритм SMO



Итак, мы у цели, осталось решить дуальную задачу, поставленную в предыдущем разделе

$ \def\M{{\color{red}M}} \def\L{{\color{blue}L}} \begin{aligned} &\max_{\lambda} \sum_{i=1}^n \lambda_i - \frac12 \sum_{i=1}^n \sum_{j=1}^n y_i y_j K(\boldsymbol{x}_i; \boldsymbol{x}_j) \lambda_i \lambda_j,\\ \text{subject to: } &0 \leq \lambda_i \leq C, \quad \mbox{ for } i=1, 2, \ldots, n,\\ &\sum_{i=1}^n y_i \lambda_i = 0, \end{aligned} $

после чего найти параметр

$ b = \mathbb{E}_{k,\xi_k \neq 0}[y_k - \sum_i \lambda_i y_i K(\boldsymbol{x}_i; \boldsymbol{x}_k)], \tag{1} $

а классификатор примет следующий вид

$ \text{class}(\boldsymbol{x}) = \text{sign}(f(\boldsymbol{x})),\quad f(\boldsymbol{x}) = \sum_i \lambda_i y_i K(\boldsymbol{x}_i; \boldsymbol{x}) + b. \tag{2} $

Алгоритм SMO (Sequential minimal optimization, [8]) решения дуальной задачи заключается в следующем. В цикле при помощи сложной эвристики ([9]) выбирается пара дуальных переменных $\lambda_\M$ и $\lambda_\L$, а затем по ним минимизируется целевая функция, с условием постоянства суммы $inline$y_\M\lambda_\M + y_\L\lambda_\L$inline$ и ограничений $0 \leq \lambda_\M \leq C$, $0 \leq \lambda_\L \leq C$ (настройка жёсткости границы). Условие на сумму сохраняет сумму всех $y_i\lambda_i$ неизменной (ведь остальные лямбды мы не трогали). Алгоритм останавливается, когда обнаруживает достаточно хорошее соблюдение так называемых условий ККТ (Каруша-Куна-Такера [10]).

Я собираюсь сделать несколько упрощений.
  • Откажусь от сложной эвристики выбора индексов (так сделано в курсе Стэнфордского университета [11]) и буду итерировать по одному индексу, а второй выбирать случайным образом.
  • Откажусь от проверки ККТ и буду выполнять наперёд заданное число итераций.
  • В самой процедуре оптимизации, в отличии от классической работы [9] или стэнфордского подхода [11], воспользуюсь векторным уравнением прямой. Это существенно упростит выкладки (сравните объём [12] и [13]).

Теперь к деталям. Информацию из обучающей выборки можно записать в виде матрицы

$ \boldsymbol{K} = \begin{pmatrix} y_1 y_1 K(\boldsymbol{x}_1; \boldsymbol{x}_1) & y_1 y_2 K(\boldsymbol{x}_1; \boldsymbol{x}_2) & \dots & y_1 y_N K(\boldsymbol{x}_1; \boldsymbol{x}_N) \\ y_2 y_1 K(\boldsymbol{x}_2; \boldsymbol{x}_1) & y_2 y_2 K(\boldsymbol{x}_2; \boldsymbol{x}_2) & \dots & y_2 y_N K(\boldsymbol{x}_2; \boldsymbol{x}_N) \\ \cdots & \cdots & \cdots & \cdots \\ y_N y_1 K(\boldsymbol{x}_N; \boldsymbol{x}_1) & y_N y_2 K(\boldsymbol{x}_N; \boldsymbol{x}_2) & \dots & y_N y_N K(\boldsymbol{x}_N; \boldsymbol{x}_N) \\ \end{pmatrix}. \tag{3} $

В дальнейшем я буду использовать обозначение с двумя индексами ($\boldsymbol{K}_{i,j}$), чтобы обратиться к элементу матрицы и с одним индексом ($\boldsymbol{K}_{k}$) для обозначения вектора-столбца матрицы. Дуальные переменные соберём в вектор-столбец $\boldsymbol{\lambda}$. Нас интересует

$ \max_{\boldsymbol{\lambda}} \underbrace{ \sum_{i=1}^n \lambda_i - \frac12 \boldsymbol{\lambda}^T \boldsymbol{K} \boldsymbol{\lambda} }_{\mathscr{L}}. $

Допустим, на текущей итерации мы хотим максимизировать целевую функцию по индексам $\L$ и $\M$. Мы будем брать производные, поэтому удобно выделить слагаемые, содержащие индексы $\L$ и $\M$. Это просто сделать в части с суммой $\lambda_i$, а вот квадратичная форма потребует несколько преобразований.

При расчёте $\boldsymbol{\lambda}^T \boldsymbol{K} \boldsymbol{\lambda}$ суммирование производится по двум индексам, пускай $i$ и $j$. Выделим цветом пары индексов, содержащие $\L$ или $\M$.



Перепишем задачу, объединив всё, что не содержит $\lambda_\L$ или $\lambda_\M$. Чтобы было легче следить за индексами, обратите внимание на $\boldsymbol{K}$ на изображении.

$$display$$ \begin{aligned} \mathscr{L} &= \lambda_\M + \lambda_\L - \sum_{j} \lambda_\M \lambda_j K_{\M,j} - \sum_{i} \lambda_\L \lambda_i K_{\L,i} + \text{const} + \\ {\text{компенсация}\atop\text{двойного подсчета}} \rightarrow\qquad &+ \frac{1}{2}\lambda_\M^2 K_{\M,\M} + \lambda_\M \lambda_\L K_{\M,\L} + \frac{1}{2}\lambda_\L^2 K_{\L,\L} = \\ &= \lambda_\M \left(1-\sum_{j} \lambda_j K_{\M,j}\right) + \lambda_\L \left(1-\sum_{i} \lambda_i K_{\L,i}\right)+\\ &+\frac{1}{2}\left(\lambda_\M^2 K_{\M,\M} + 2 \lambda_\M \lambda_\L K_{\M,\L}+\lambda_\L^2 K_{\L,\L} \right) + \text{const} = \\ &=\boldsymbol{k}^T_0 \boldsymbol{v}_0 + \frac{1}{2}\boldsymbol{v}^{\,T}_0 \, \boldsymbol{Q} \, \boldsymbol{v}_0 + \text{const}, \end{aligned} $$display$$

где $\text{const}$ обозначает слагаемые, не зависящие от $\lambda_\L$ или $\lambda_\M$. В последней строке я использовал обозначения

$$display$$ \begin{align} \boldsymbol{v}_0 &= (\lambda_\M, \lambda_\L)^T, \tag{4a}\\ \boldsymbol{k}_0 &= \left(1 - \boldsymbol{\lambda}^T\boldsymbol{K}_{\M}, 1 - \boldsymbol{\lambda}^T\boldsymbol{K}_{\L}\right)^T, \tag{4b}\\ \boldsymbol{Q} &= \begin{pmatrix} K_{\M,\M} & K_{\M,\L} \\ K_{\L,\M} & K_{\L,\L} \\ \end{pmatrix},\tag{4c}\\ \boldsymbol{u} &= (-y_\L, y_\M)^T. \tag{4d} \end{align} $$display$$

Обратите внимание, что $\boldsymbol{k}_0 + \boldsymbol{Q} \boldsymbol{v}_0$ не зависит ни от $\lambda_\L$, ни от $\lambda_\M$

$$display$$ \boldsymbol{k}_0 = \begin{pmatrix} 1 - \lambda_\M K_{\M,\M} - \lambda_\L K_{\M,\L} - \sum_{i \neq \M,\L} \lambda_i K_{\M,i}\\ 1 - \lambda_\M K_{\L,\M} - \lambda_\L K_{\L,\L} - \sum_{i \neq \M,\L} \lambda_i K_{\L,i}\\ \end{pmatrix} = \begin{pmatrix} 1 - \sum_{i \neq \M,\L} \lambda_i K_{\M,i}\\ 1 - \sum_{i \neq \M,\L} \lambda_i K_{\L,i}\\ \end{pmatrix} - \boldsymbol{Q} \boldsymbol{v}_0. $$display$$

Ядро симметрично, поэтому $\boldsymbol{Q}^T = \boldsymbol{Q}$ и можно записать

$ \mathscr{L} = (\boldsymbol{k}_0 + \boldsymbol{Q} \boldsymbol{v}_0 - \boldsymbol{Q} \boldsymbol{v}_0)^T \boldsymbol{v}_0 + \frac{1}{2}\boldsymbol{v}^{\,T}_0 \, \boldsymbol{Q} \, \boldsymbol{v}_0 + \text{const} = (\boldsymbol{k}_0 + \boldsymbol{Q} \boldsymbol{v}_0)^T \boldsymbol{v}_0 - \frac{1}{2} \boldsymbol{v}^{\,T}_0 \, \boldsymbol{Q} \, \boldsymbol{v}_0 + \text{const} $

Мы хотим выполнить максимизацию так, чтобы $inline$y_\L\lambda_\L + y_\M\lambda_\M$inline$ осталось постоянным. Для этого новые значения должны лежать на прямой

$$display$$ (\lambda_\M^\text{new}, \lambda_\L^\text{new})^T = \boldsymbol{v}(t) = \boldsymbol{v}_0 + t \boldsymbol{u}. $$display$$

Несложно убедиться, что для любого $t$

$$display$$ y_\M\lambda_\M^\text{new} + y_\L\lambda_\L^\text{new} = y_\M \lambda_\M + y_\L \lambda_\L + t (-y_\M y_\L + y_\L y_\M) = y_\M\lambda_\M + y_\L\lambda_\L. $$display$$

В таком случае мы должны максимизировать

$ \mathscr{L}(t) = (\boldsymbol{k}_0 + \boldsymbol{Q} \boldsymbol{v}_0)^T \boldsymbol{v}(t) - \frac12 \boldsymbol{v}^{\,T}(t) \, \boldsymbol{Q} \, \boldsymbol{v}(t) + \text{const}, $

что легко сделать взяв производную

$ \begin{align} \frac{d \mathscr{L}(t)}{d t} = (\boldsymbol{k}_0 + \boldsymbol{Q} \boldsymbol{v}_0)^T \frac{d \boldsymbol{v}}{dt} &- \frac12 \left(\frac{d(\boldsymbol{v}^{\,T} \, \boldsymbol{Q} \, \boldsymbol{v})}{d \boldsymbol{v}}\right)^T \frac{d\boldsymbol{v}}{d t} =\\ &= \boldsymbol{k}_0^T \boldsymbol{u} + \underbrace{\boldsymbol{v}_0^T \boldsymbol{Q}^T \boldsymbol{u} - \boldsymbol{v}^T \boldsymbol{Q}^T \boldsymbol{u}}_{(\boldsymbol{v}_0^T - \boldsymbol{v}^T)\boldsymbol{Q} \boldsymbol{u}} = \boldsymbol{k}_0^T \boldsymbol{u} - t \boldsymbol{u}^T \boldsymbol{Q} \boldsymbol{u}. \end{align} $

Приравнивая производную к нулю, получим

$ t_* = \frac{\boldsymbol{k}^T_0 \boldsymbol{u}}{\boldsymbol{u}^{\,T} \boldsymbol{Q} \boldsymbol{u}}. \tag{5} $

И ещё одно: возможно мы заберёмся дальше чем нужно и окажемся вне квадрата как на картинке. Тогда нужно сделать шаг назад и вернуться на его границу

$$display$$ (\lambda_\M^\text{new}, \lambda_\L^\text{new}) = \boldsymbol{v}_0 + t_*^{\text{restr}} \boldsymbol{u}. $$display$$

На этом итерация завершается и выбираются новые индексы.


Реализация



Принципиальную схему обучения упрощённой машины опорных векторов можно записать как



Давайте посмотрим на код на реальном языке программирования. Если вы не любите код в статьях, можете изучить его на гитхабе [14].

Исходный код упрощённой машины опорных векторов
import numpy as npclass SVM:  def __init__(self, kernel='linear', C=10000.0, max_iter=100000, degree=3, gamma=1):    self.kernel = {'poly'  : lambda x,y: np.dot(x, y.T)**degree,         'rbf': lambda x,y: np.exp(-gamma*np.sum((y-x[:,np.newaxis])**2,axis=-1)),         'linear': lambda x,y: np.dot(x, y.T)}[kernel]    self.C = C    self.max_iter = max_iter  # ограничение параметра t, чтобы новые лямбды не покидали границ квадрата  def restrict_to_square(self, t, v0, u):     t = (np.clip(v0 + t*u, 0, self.C) - v0)[1]/u[1]    return (np.clip(v0 + t*u, 0, self.C) - v0)[0]/u[0]  def fit(self, X, y):    self.X = X.copy()    # преобразование классов 0,1 в -1,+1; для лучшей совместимости с sklearn    self.y = y * 2 - 1     self.lambdas = np.zeros_like(self.y, dtype=float)    # формула (3)    self.K = self.kernel(self.X, self.X) * self.y[:,np.newaxis] * self.y        # выполняем self.max_iter итераций    for _ in range(self.max_iter):      # проходим по всем лямбда       for idxM in range(len(self.lambdas)):                                            # idxL выбираем случайно        idxL = np.random.randint(0, len(self.lambdas))                                 # формула (4с)        Q = self.K[[[idxM, idxM], [idxL, idxL]], [[idxM, idxL], [idxM, idxL]]]         # формула (4a)        v0 = self.lambdas[[idxM, idxL]]                                                # формула (4b)        k0 = 1 - np.sum(self.lambdas * self.K[[idxM, idxL]], axis=1)                   # формула (4d)        u = np.array([-self.y[idxL], self.y[idxM]])                                    # регуляризированная формула (5), регуляризация только для idxM = idxL        t_max = np.dot(k0, u) / (np.dot(np.dot(Q, u), u) + 1E-15)         self.lambdas[[idxM, idxL]] = v0 + u * self.restrict_to_square(t_max, v0, u)        # найти индексы опорных векторов    idx, = np.nonzero(self.lambdas > 1E-15)     # формула (1)    self.b = np.mean((1.0-np.sum(self.K[idx]*self.lambdas, axis=1))*self.y[idx])     def decision_function(self, X):    return np.sum(self.kernel(X, self.X) * self.y * self.lambdas, axis=1) + self.b  def predict(self, X):     # преобразование классов -1,+1 в 0,1; для лучшей совместимости с sklearn    return (np.sign(self.decision_function(X)) + 1) // 2



При создании объекта класса SVM можно указать гиперпараметры. Обучение производится вызовом функции fit, классы должны быть указаны как $0$ и $1$ (внутри конвертируются в $-1$ и $+1$, сделано для большей совместимости с sklearn), размерность вектора фич допускается произвольной. Для классификации используется функция predict.


Сравнение с sklearn.svm.SVC



Не то, чтобы данное сравнение имело особый смысл, ведь речь идёт о крайне упрощённом алгоритме, разработанном исключительно в целях обучения студентов, но всё же. Для тестирования (и чтобы посмотреть как этим всем пользоваться) можно выполнить следующее (этот код тоже есть на гитхаб [14])

Сравнение с sklearn.svm.SVC на простом двумерном датасете
from sklearn.svm import SVCimport matplotlib.pyplot as pltimport seaborn as sns; sns.set()from sklearn.datasets import make_blobs, make_circlesfrom matplotlib.colors import ListedColormapdef test_plot(X, y, svm_model, axes, title):  plt.axes(axes)  xlim = [np.min(X[:, 0]), np.max(X[:, 0])]  ylim = [np.min(X[:, 1]), np.max(X[:, 1])]  xx, yy = np.meshgrid(np.linspace(*xlim, num=700), np.linspace(*ylim, num=700))  rgb=np.array([[210, 0, 0], [0, 0, 150]])/255.0    svm_model.fit(X, y)  z_model = svm_model.decision_function(np.c_[xx.ravel(), yy.ravel()]).reshape(xx.shape)    plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')  plt.contour(xx, yy, z_model, colors='k', levels=[-1, 0, 1], alpha=0.5, linestyles=['--', '-', '--'])  plt.contourf(xx, yy, np.sign(z_model.reshape(xx.shape)), alpha=0.3, levels=2, cmap=ListedColormap(rgb), zorder=1)  plt.title(title)X, y = make_circles(100, factor=.1, noise=.1)fig, axs = plt.subplots(nrows=1,ncols=2,figsize=(12,4))test_plot(X, y, SVM(kernel='rbf', C=10, max_iter=60, gamma=1), axs[0], 'OUR ALGORITHM')test_plot(X, y, SVC(kernel='rbf', C=10, gamma=1), axs[1], 'sklearn.svm.SVC')X, y = make_blobs(n_samples=50, centers=2, random_state=0, cluster_std=1.4)fig, axs = plt.subplots(nrows=1,ncols=2,figsize=(12,4))test_plot(X, y, SVM(kernel='linear', C=10, max_iter=60), axs[0], 'OUR ALGORITHM')test_plot(X, y, SVC(kernel='linear', C=10), axs[1], 'sklearn.svm.SVC')fig, axs = plt.subplots(nrows=1,ncols=2,figsize=(12,4))test_plot(X, y, SVM(kernel='poly', C=5, max_iter=60, degree=3), axs[0], 'OUR ALGORITHM')test_plot(X, y, SVC(kernel='poly', C=5, degree=3), axs[1], 'sklearn.svm.SVC')



После запуска будут сгенерированы картинки, но так как алгоритм рандомизированный, то они будут слегка отличаться для каждого запуска. Вот пример работы упрощённого алгоритма (слева направо: линейное, полиномиальное и rbf ядра)

А это результат работы промышленной версии машины опорных векторов.

Если размерность $2$ кажется слишком маленькой, то можно ещё протестировать на MNIST

Сравнение с sklearn.svm.SVC на 2-х классах из MNIST
from sklearn import datasets, svmfrom sklearn.model_selection import train_test_splitfrom sklearn.metrics import confusion_matriximport matplotlib.pyplot as pltimport seaborn as snsclass_A = 3class_B = 8digits = datasets.load_digits()mask = (digits.target == class_A) | (digits.target == class_B)data = digits.images.reshape((len(digits.images), -1))[mask]target = digits.target[mask] // max([class_A, class_B]) # rescale to 0,1X_train, X_test, y_train, y_test = train_test_split(data, target, test_size=0.5, shuffle=True)def plot_confusion(clf):  clf.fit(X_train, y_train)  y_fit = clf.predict(X_test)  mat = confusion_matrix(y_test, y_fit)  sns.heatmap(mat.T, square=True, annot=True, fmt='d', cbar=False, xticklabels=[class_A,class_B], yticklabels=[class_A,class_B])  plt.xlabel('true label')  plt.ylabel('predicted label');  plt.show()print('sklearn:')plot_confusion(svm.SVC(C=1.0, kernel='rbf', gamma=0.001))print('custom svm:')plot_confusion(SVM(kernel='rbf', C=1.0, max_iter=60, gamma=0.001))



Для MNIST я попробовал несколько случайных пар классов (упрощённый алгоритм поддерживает только бинарную классификацию), но разницы в работе упрощённого алгоритма и sklearn не обнаружил. Представление о качестве даёт следующая confusion matrix.




Заключение



Спасибо всем, кто дочитал до конца. В этой статье мы рассмотрели упрощённую учебную реализацию машины опорных векторов. Конечно, она не может состязаться с промышленным образцом, но благодаря крайней простоте и компактному коду на Python, а также тому, что все основные идеи SMO были сохранены, эта версия SVM вполне может занять своё место в учебной аудитории. Стоит отметить, что алгоритм проще не только весьма мудрёного алгоритма [9], но даже его упрощённой версии от Стэнфордского университета [11]. Все-таки машина опорных векторов в 30 строчках это красиво.

Список литературы



  1. https://fbeilstein.github.io/simplest_smo_ever/
  2. страница на http://www.machinelearning.ru
  3. Начала Машинного Обучения, КАУ
  4. https://en.wikipedia.org/wiki/Kernel_method
  5. https://en.wikipedia.org/wiki/Duality_(optimization)
  6. Статья на http://www.machinelearning.ru
  7. https://www.youtube.com/watch?v=MTY1Kje0yLg
  8. https://en.wikipedia.org/wiki/Sequential_minimal_optimization
  9. Platt, John. Fast Training of Support Vector Machines using Sequential Minimal Optimization, in Advances in Kernel Methods Support Vector Learning, B. Scholkopf, C. Burges, A. Smola, eds., MIT Press (1998).
  10. https://en.wikipedia.org/wiki/Karush%E2%80%93Kuhn%E2%80%93Tucker_conditions
  11. http://cs229.stanford.edu/materials/smo.pdf
  12. https://www.researchgate.net/publication/344460740_Yet_more_simple_SMO_algorithm
  13. http://fourier.eng.hmc.edu/e176/lectures/ch9/node9.html
  14. https://github.com/fbeilstein/simplest_smo_ever
Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru