Обнаружение вторжений с применением технологий машинного обучения. Часть 2
Привет Хабр! Меня зовут Татьяна Ошуркова, я главный аналитик департамента ИТ корпоративного, инвестиционного и депозитарного бизнеса Росбанка и автор телеграм-канала IT Talks. В первой части статьи я рассказала некоторые теоретические основы про системы обнаружения вторжений и использование машинного обучения при решении задач информационной безопасности. Также рассмотрела данные, которые будут использоваться, их анализ и предварительную подготовку.
Во второй части я продолжу рассказывать о реализации системы обнаружения вторжений с применением машинного обучения и подробно рассмотрю обучение моделей, а также анализ их работы и выводы, исходя из полученных результатов.
Важно отметить, что пример, разобранный в данной статьи, носит обучающий характер и предназначен для демонстрации принципов работы. Применение данного примера в реальных проектах требует дополнительных настроек и адаптации к конкретным условиям.
Первым шагом затронем теорию и поговорим об оценке эффективности модели. После обучения мы будем оценивать работу модели с помощью метрик. Для того, чтобы рассчитать метрики, нам необходимо воспользоваться некоторыми общими понятиями. Есть несколько терминов, обозначающие количественные показатели, которые относятся к следующим значениям:
TP (True Positive). Данное число показывает количество случаев, когда классификатор верно отнёс объект к рассматриваемому классу.
TN (True Negative). Данное число показывает количество случаев, когда классификатор верно утверждает, что объект не принадлежит к рассматриваемому классу.
FP (False Positive). Данное число показывает количество случаев, когда классификатор неверно отнёс объект к рассматриваемому классу.
FN (False Negative). Данное число показывает количество случаев, когда классификатор неверно утверждает, что объект не принадлежит к рассматриваемому классу.
Далее рассмотрим метрики модели и поговорим про три простые оценки. Мы будем использовать аccuracy, recall и precision.
Accuracy — (точность) показывает долю правильных классификаций. Несмотря на очевидность и простоту, является одной из самых малоинформативных оценок классификаторов.
Recall — (полнота, sensitivity, TPR (true positive rate)) показывает отношение верно классифицированных объектов класса к общему числу элементов этого класса.
Precision — (точность, перевод совпадает с accuracy)показывает долю верно классифицированных объектов среди всех объектов, которые к этому классу отнес классификатор.
Кроме метрик для оценки эффективности мы будем использовать матрицу ошибок. Матрица ошибок (confusion matrix) — это таблица, используемая для оценки производительности алгоритмов классификации. Она отображает количество верных и ошибочных предсказаний, сделанных моделью, и позволяет визуализировать, где и какие ошибки происходят. Матрица делится на четыре части, каждая из которых соответствует одному из четырех чисел, рассмотренных выше и расположенных на матрице следующим образом:
y=1 | y=0 | |
a (x)=1 | Истинно-положительный (True Positive — TP) | Ложно-положительный (False Positive — FP) |
a (x)=0 | Ложно-отрицательный (False Negative — FN) | Истинно-отрицательный (True Negative — TN) |
Для построения матрицы ошибок необходимо сохранить фактические метки целевой переменной тестового набора данных. Далее выполнить предсказание меток целевой переменной для тестового набора данных с использованием модели. После этого вычисляется матрица ошибок на основе фактических и предсказанных меток целевой переменной, создается объект для визуализации матрицы ошибок с заданными метками классов и выполняется построение графика. Функция с кодом для оценки и построения матрицы ошибок представлена ниже:
def evaluation(model, name, X_train, X_test, y_train, y_test):
start_time = time.time()
model.predict(X_test)
end_time = time.time()
execution_time = end_time - start_time
print("Time " + str(name) + " : %.3f sec" % execution_time)
train_accuracy = metrics.accuracy_score(y_train, model.predict(X_train))
test_accuracy = metrics.accuracy_score(y_test, model.predict(X_test))
train_precision = metrics.precision_score(y_train, model.predict(X_train))
test_precision = metrics.precision_score(y_test, model.predict(X_test))
train_recall = metrics.recall_score(y_train, model.predict(X_train))
test_recall = metrics.recall_score(y_test, model.predict(X_test))
kernal_evals[str(name)] = [train_accuracy, test_accuracy, train_precision, test_precision, train_recall, test_recall]
print("Training Accuracy " + str(name) + " {} Test Accuracy ".format(train_accuracy*100) + str(name) + " {}".format(test_accuracy*100))
print("Training Precision " + str(name) + " {} Test Precision ".format(train_precision*100) + str(name) + " {}".format(test_precision*100))
print("Training Recall " + str(name) + " {} Test Recall ".format(train_recall*100) + str(name) + " {}".format(test_recall*100))
actual = y_test
predicted = model.predict(X_test)
confusion_matrix = metrics.confusion_matrix(actual, predicted)
cm_display = metrics.ConfusionMatrixDisplay( \
confusion_matrix = \
confusion_matrix, \
display_labels = ['normal', 'attack'])
fig, ax = plt.subplots(figsize=(10,10))
ax.grid(False)
ax.set_title(name, fontsize=15)
cm_display.plot(ax=ax)
plt.show()
Перейдем к обучению моделей. Мы рассмотрим логистическую регрессию, метод k-ближайших соседей, наивный байесовский классификатора Гаусса, градиентный бустинг, метод случайного леса и нейронную сеть.
Логистическая регрессия (англ. logistic regression) — это метод машинного обучения, который используется для решения задачи бинарной классификации, разделения данных на два класса. Он основан на логистической функции, которая преобразует линейную комбинацию признаков в вероятность принадлежности к одному из классов. Суть метода заключается в нахождении оптимальные веса для каждого признака таким образом, чтобы модель максимально точно предсказывала классы новых данных.
lr = LogisticRegression().fit(x_train, y_train)
evaluation(lr, "Logistic Regression", x_train, x_test, y_train, y_test)
В результате получены следующие оценки:
Training Accuracy Logistic Regression 87.59986106286905
Test Accuracy Logistic Regression 88.80730303631672
Training Precision Logistic Regression 83.48096953178235
Test Precision Logistic Regression 84.8625629113434
Training Recall Logistic Regression 91.44806995094903
Test Recall Logistic Regression 92.68498942917547
По оценкам можно сказать, что модель логистической регрессии показывает высокую производительность с точностью 87.60% на обучающем и 88.81% на тестовом наборах данных, что указывает на хорошую обобщающую способность и отсутствие переобучения. Высокие значения точности (83.48% на обучающем и 84.86% на тестовом) и полноты (91.45% на обучающем и 92.68% на тестовом) свидетельствуют о качественных предсказаниях и эффективном захвате положительных случаев, подтверждая сбалансированность модели и низкий уровень ошибок. На рисунке 1 представлена матрица ошибок, по ней также можно сделать выводы о том, показатели по модели очень хорошие, но не идеальные, как и числа на побочной диагонали.
Рисунок 1
Далее рассмотрим метод k-ближайших соседей. К-ближайших соседей (K-Nearest Neighbors или просто KNN) — это алгоритм классификации и регрессии, основанный на гипотезе компактности, которая предполагает, что расположенные близко друг к другу объекты в пространстве признаков имеют схожие значения целевой переменной или принадлежат к одному классу.
Суть метода заключается в классификации или предсказании значения для нового объекта, основываясь на его ближайших соседях из обучающего набора данных.
knn = KNeighborsClassifier(n_neighbors=20).fit(x_train, y_train)
evaluation(knn, "KNeighborsClassifier", x_train, x_test, y_train, y_test)
В результате получены следующие оценки:
Training Accuracy KNeighborsClassifier 98.0747283282886
Test Accuracy KNeighborsClassifier 98.0551696765231
Training Precision KNeighborsClassifier 98.38536060279871
Test Precision KNeighborsClassifier 98.25457641549595
Training Recall KNeighborsClassifier 97.46214544679036
Test Recall KNeighborsClassifier 97.58985200845666
Метод k ближайших соседей демонстрирует высокую производительность с точностью 98.07% на обучающем и 98.06% на тестовом наборах данных, что свидетельствует о его эффективности в классификации. Высокие значения точности (98.39% на обучающем и 98.25% на тестовом) и полноты (97.46% на обучающем и 97.59% на тестовом) указывают на хорошую способность предсказания и эффективное захватывание положительных случаев. Эти оценки говорят о высокой надежности и сбалансированности метода. На рисунке 2 представлена матрица ошибок, по ней также можно сделать выводы о том, показатели по модели очень высокие, на побочной диагонали числа крайне малы, что также говорит о хорошем качестве работы модели.
Рисунок 2
Далее рассмотрим использование наивного байесовского классификатора Гаусса. Naive Bayes — это простой вероятностный классификатор, основанный на применении теоремы Байеса с предположением о наивности (независимости) признаков. Суть метода: предсказать принадлежность объекта к определенному классу, используя вероятностные модели для каждого класса.
nb = GaussianNB().fit(x_train, y_train)
evaluation(gnb, "GaussianNB", x_train, x_test, y_train, y_test)
В результате получены следующие оценки:
Training Accuracy GaussianNB 92.62640797896094
Test Accuracy GaussianNB 93.84798571145069
Training Precision GaussianNB 92.63180639585133
Test Precision GaussianNB 94.23159707275074
Training Recall GaussianNB 91.42674344209853
Test Recall GaussianNB 92.55813953488372
Наивный байесовский классификатор Гаусса демонстрирует умеренную, но стабильную производительность. С точностью 92.63% на обучающем и 93.85% на тестовом наборах данных, модель эффективно определяет классы объектов. Высокие значения точности (92.63% на обучающем и 94.23% на тестовом) и полноты (91.43% на обучающем и 92.56% на тестовом) свидетельствуют о её надежности.
На рисунке 3 представлена матрица ошибок, по ней можно сделать выводы о том, показатели достаточно неплохо уровня, но числа на побочной диагонали уже больше, чем у предыдущей модели.
Рисунок 3
Далее рассмотрим градиентный бустинг. Градиентный бустинг — это техника машинного обучения для задач классификации и регрессии, которая строит модель предсказания в форме ансамбля слабых предсказывающих моделей, обычно деревьев решений.
В коде будем использовать реализацию XGBoost (Extreme Gradient Boosting, экстремальный градиентный бустинг) — это реализация алгоритма градиентного бустинга. Суть метода заключается в обучении ансамбля слабых моделей (обычно деревьев решений) последовательно, где каждая новая модель исправляет ошибки предыдущих.
xgb_classifier = xgb.XGBClassifier(objective='binary:logistic', n_estimators=100, random_state=42)
xgb_classifier.fit(x_train, y_train)
evaluation(xgb_classifier, "XGBoost", x_train, x_test, y_train, y_test)
В реализации решается задача бинарной классификации с применением логистической регрессии с применением 100 базовых моделей.
В результате получены следующие оценки:
Training Accuracy XGBoost 99.9909347152389
Test Accuracy XGBoost 98.8809287557055
Training Precision XGBoost 99.91762638918734
Test Precision XGBoost 98.91536182818452
Training Recall XGBoost 99.99817263451826
Test Recall XGBoost 98.830866807611
Градиентный бустинг, представленный моделью XGBoost, проявляет впечатляющую производительность как на обучающем, так и на тестовом наборах данных. С точностью 99.99% на обучающем и 98.88% на тестовом, модель успешно классифицирует объекты. Высокие значения прецизионности (99.92% на обучающем и 98.92% на тестовом) и полноты (99.99% на обучающем и 98.83% на тестовом) подтверждают надежность и эффективность модели в предсказании положительных случаев. Но нужно помнить, что очень высокие оценки производительности модели XGBoost могут указывать на возможные проблемы, такие как переобучение, дисбаланс классов или наличие шума в данных. Хотя текущие результаты показывают отличное качество классификации, важно учитывать эти факторы для правильной интерпретации и дальнейшего улучшения модели.
На рисунке 4 представлена матрица ошибок, по ней можно также сделать выводы об высоких показателях работы модели, о чем говорят крайние малые значения близкие к нулю на побочной диагонали.
Рисунок 4
Перейдем к методу случайного леса. Метод случайного леса (англ. random forest) — это алгоритм машинного обучения, который использует комбинацию нескольких деревьев решений для решения задач классификации или регрессии. Суть метода заключается в том, что в каждом дереве решений строится независимо от других, их обучение происходит на разных подмножествах данных и признаков. Затем для классификации или регрессии результаты всех деревьев объединяются.
rf = RandomForestClassifier().fit(x_train, y_train)
evaluation(rf, "RandomForestClassifier", x_train, x_test, y_train, y_test)
В результате получены следующие оценки:
Training Accuracy RandomForestClassifier 99.99167281423711
Test Accuracy RandomForestClassifier 98.78170271879341
Training Precision RandomForestClassifier 99.9914571898426
Test Precision RandomForestClassifier 98.83065198983911
Training Recall RandomForestClassifier 99.99572841246449
Test Recall RandomForestClassifier 98.70401691331924
Можно сделать вывод, что модель случайного леса (RandomForestClassifier) демонстрирует высокую производительность с точностью 99.99% на обучающем и 98.78% на тестовом наборах данных, что свидетельствует о её способности эффективно классифицировать объекты. Высокие значения прецизионности (99.99% на обучающем и 98.83% на тестовом) и полноты (99.99% на обучающем и 98.70% на тестовом) указывают на точные и надежные предсказания. Однако, как и в предыдущем случае нужно понимать, что такие высокие оценки могут указывать на переобучение, когда модель хорошо запоминает обучающие данные, но может быть менее эффективной на новых данных. Это требует внимательного анализа и, возможно, дополнительных шагов по регулированию модели для обеспечения её устойчивости и обобщающей способности.
На рисунке 5 представлена матрица ошибок, по ней можно также сказать, что показатели работы модели близки к идеалу.
Рисунок 5
В коде, ссылка на который будет в конце материала, дополнительно рассматривается метод случайного леса с применением метода главных компонент при подготовке данных. Я не буду останавливаться в данной статье на этом более подробно, так как использование данного метода не улучшило показатели модели и является не совсем эффективным вариантом для подготовки данных в моем случае, но в качестве практики, вы можете поэкспериментировать с подготовкой данных и попробовать использовать другой метод уменьшения размерности для подготовки.
Перейдем к нейронным сетям. Для начала поговорим о том, какие есть виды нейронных сетей и как каждый из видов используется для обнаружения вторжений.
Многослойные перцептроны могут использоваться для обнаружения аномалий и сигнатур атак.
Рекуррентные нейронные сети могут использоваться для анализа сетевого трафика и обнаружения аномалий на основе последовательных шаблонов.
Глубокие нейронные сети могут использоваться для обработки сложных данных и выявления необычных или аномальных шаблонов в сетевом трафике.
Генеративные состязательные сети (GAN). Могут использоваться для создания реалистичных синтетических данных и обучения модели IDS на более разнообразных образцах атак.
Для решение задачи я буду использовать первый вид, последовательную модель (Sequential model) и линейный стек слоев, где каждый слой имеет ровно один вход и один выход. В качестве алгоритма оптимизации будет использован Adam.
Если говорить про архитектуру, то нейронная сеть будет состоять из следующих слоев:
Полносвязанные слои с активацией ReLU. Используются для извлечения и преобразования признаков из входных данных.
Dropout слои. Помогает предотвратить переобучение модели путем случайного отключения нейронов во время обучения.
Выходной слой с активацией Sigmoid. Выдает значения в интервале от 0 до 1, что интерпретируется как вероятность принадлежности к положительному классу.
Реализация в коде представлена ниже:
model = tf.keras.Sequential([
tf.keras.layers.Dense(units=64, activation='relu', input_shape=(x_train.shape[1:]),
kernel_regularizer=regularizers.l2(0.01),
bias_regularizer=regularizers.l2(0.01)),
tf.keras.layers.Dropout(0.5),
tf.keras.layers.Dense(units=32, activation='relu',
kernel_regularizer=regularizers.l2(0.01),
bias_regularizer=regularizers.l2(0.01)),
tf.keras.layers.Dropout(0.5),
tf.keras.layers.Dense(units=1, activation='sigmoid')
])
Посмотрим на summary table на рисунке 6, где в первом столбце указан тип слоя, во втором форма и размерность выходных данных, в третьем количество параметров в слое, которое подлежит обучению. Dense — полносвязанные слои между которыми распределены параметры. Dropout слои не содержат нейронов, потому что их основная функция заключается в регуляризации сети, а не в вычислениях. Они работают путем случайного «выключения» некоторого процента нейронов в предыдущих слоях на каждом этапе обучения, чтобы предотвратить переобучение и улучшить обобщающую способность модели.
Рисунок 6
Нейронная сеть обучалась на 100 эпохах, метрики по первым и последним эпохам представлены ниже:
Эпоха | Точность (%) | Потери | Валидационная точность (%) |
1 | 78.35 | 11.7777 | 94.94 |
2 | 91.72 | 4.4146 | 95.30 |
3 | 92.78 | 1.1391 | 96.13 |
4 | 93.41 | 0.8637 | 96.19 |
5 | 93.77 | 0.7069 | 96.29 |
… | … | … | … |
96 | 96.33 | 0.1778 | 97.30 |
97 | 96.34 | 0.2730 | 97.30 |
98 | 96.27 | 0.2695 | 97.18 |
99 | 96.08 | 0.2383 | 97.26 |
Далее давайте сделаем выводы по работе нейронной сети, исходя из метрик по эпохам:
Точность (Accuracy). В начале обучения точность составляла около 78.35%, а к концу достигла 96.29% на обучающем наборе данных и 96.98% на валидационном.
Потери (Loss). В начале обучения потери были высокими (11.78), но по мере обучения сети они уменьшались и к концу составили 0.1871 на обучающем наборе и 0.1404 на валидационном.
Валидационная точность (Validation Accuracy). В начале обучения валидационная точность была ниже, чем обучающая, но по мере обучения она приблизилась к обучающей точности.
Валидационные потери (Validation Loss). Аналогично точности, в начале обучения валидационные потери были высокими, но по мере обучения они снижались, что указывает на улучшение обобщающей способности модели.
Ниже представлен фрагмент кода:
model.compile(optimizer='adam',
loss='binary_crossentropy',
metrics=['accuracy'])
model.summary()
history = model.fit(x_train, y_train, validation_data=(x_test, y_test), epochs=100, verbose=1)
Далее давайте подведем итоги и посмотрим на сводную таблицу, где представлены метрики и время работы каждой модели. Важно уточнить, что свои наработки я тестировала на устройстве Устройство: Intel® Core™ i7–8565U CPU @ 1.80GHz 1.99 GHz. Безусловно, в промышленных масштабах используется совершенно другое железо.
Модель | Accuracy (Train) | Precision | Recall | Accuracy (Test) | Precision | Recall | Время © |
Лог. регрессия | 87.60 | 88.81 | 83.48 | 84.86 | 91.45 | 92.68 | 0.040 |
Ближайшие соседи | 98.07 | 98.06 | 98.39 | 98.25 | 97.46 | 97.59 | 0.899 |
Наивный байесовский классификатор | 92.63 | 93.85 | 92.63 | 94.23 | 91.43 | 92.56 | 0.055 |
XGBoost | 99.99 | 98.88 | 99.92 | 98.92 | 100.00 | 98.83 | 0.063 |
Случайный лес | 99.99 | 98.78 | 99.99 | 98.83 | 100.00 | 98.70 | 0.095 |
Нейронная сеть | 97.00 | 97.00 | 99.00 | 99.00 | 94.00 | 95.00 | 0.258 |
По итоговым данным можно сказать, что наиболее высокие метрики у градиентного бустинга и случайного леса. Далее идет метод ближайших соседей и нейронная сеть, хотя в большинстве случаев нейронная сеть дает самый высокие показатели. Многое зависит от качества подготовки данных, я тестировала данную реализацию на другом датасете и там показатели были совершенно другие, исходя из чего можно сказать, что датасет, который я брала в данном случае, требует более детального анализа и предварительной подготовки. Кроме подготовки на многое влияет объем данных, а также параметры моделей и построение нейронной сети.
Несмотря на то, что в данном случае нет большого разрыва во времени, на это обязательно нужно обращаться внимание, так как системы обнаружения вторжений в большинстве случаев находятся на 3 и 4 слоях модель OSI, а значит кроме качества работы важны следующие факторы:
Обнаружение в реальном времени.
Снижение задержек.
Эффективное использование ресурсов сети.
Способность к масштабированию.
Эффективное реагирование на угрозы.
Кроме того, необходимо помнить, что в зависимости от модели может потребоваться большое количество ресурсов, особенно для рабоыт нейронной сети. Поэтому для выбора решения стоит оценивать все показатели эффективности в совокупности, не забываю ни про железе, ни про время.
Машинное обучение в сфере кибербезопасности можно использовать не только для работы в онлайн-режиме, а также для постанализа данных, что делает требования к эффективности работы и потребности ресурсов не столь строгими.
Ссылка на решение, рассмотренное в данной серии статей: https://github.com/Oshurkova/IDSNetwork. Решение, которое я упоминала выше, схожее с текущим, с использованием другого датасета и немного другой реализацией: https://github.com/Oshurkova/ids_model.
Желаю удачи в изучении машинного обучения для сферы кибербезопасности, систем обнаружения вторжений и не только.