Обнаружение вторжений с применением технологий машинного обучения. Часть 2

e982a41ac4c547492a6fe8700019f8e3.png

Привет Хабр! Меня зовут Татьяна Ошуркова, я главный аналитик департамента ИТ корпоративного, инвестиционного и депозитарного бизнеса Росбанка и автор телеграм-канала IT Talks. В первой части статьи я рассказала некоторые теоретические основы про системы обнаружения вторжений и использование машинного обучения при решении задач информационной безопасности. Также рассмотрела данные, которые будут использоваться, их анализ и предварительную подготовку.

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

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

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

  • TP (True Positive). Данное число показывает количество случаев, когда классификатор верно отнёс объект к рассматриваемому классу.

  • TN (True Negative). Данное число показывает количество случаев, когда классификатор верно утверждает, что объект не принадлежит к рассматриваемому классу.

  • FP (False Positive). Данное число показывает количество случаев, когда классификатор неверно отнёс объект к рассматриваемому классу.

  • FN (False Negative). Данное число показывает количество случаев, когда классификатор неверно утверждает, что объект не принадлежит к рассматриваемому классу.

Далее рассмотрим метрики модели и поговорим про три простые оценки. Мы будем использовать аccuracy, recall и precision.

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

Acc=(TP + TNT)/(TP + TN + FP + FN)

Recall — (полнота, sensitivity, TPR (true positive rate)) показывает отношение верно классифицированных объектов класса к общему числу элементов этого класса.

Recall=TP/(TP+FN)

Precision — (точность, перевод совпадает с accuracy)показывает долю верно классифицированных объектов среди всех объектов, которые к этому классу отнес классификатор.

Precision=TP/(TP+FP)

Кроме метрик для оценки эффективности мы будем использовать матрицу ошибок. Матрица ошибок (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

Рисунок 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

Рисунок 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

Рисунок 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

Рисунок 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

Рисунок 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

Рисунок 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.

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

© Habrahabr.ru