Алгоритми контрольованого навчання

tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Вивчайте та практикуйте Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Підтримайте HackTricks

Основна інформація

Контрольоване навчання використовує мічені дані для навчання моделей, які можуть робити прогнози на нових, невідомих входах. У кібербезпеці контрольоване машинне навчання широко застосовується для завдань, таких як виявлення вторгнень (класифікація мережевого трафіку як нормальний або атака), виявлення шкідливого ПЗ (відрізнення шкідливого програмного забезпечення від доброзичливого), виявлення фішингу (ідентифікація шахрайських веб-сайтів або електронних листів) та фільтрація спаму, серед інших. Кожен алгоритм має свої переваги і підходить для різних типів проблем (класифікація або регресія). Нижче ми розглянемо ключові алгоритми контрольованого навчання, пояснимо, як вони працюють, і продемонструємо їх використання на реальних наборах даних з кібербезпеки. Ми також обговоримо, як поєднання моделей (ансамблеве навчання) може часто покращити прогностичну ефективність.

Алгоритми

  • Лінійна регресія: Основний алгоритм регресії для прогнозування числових результатів шляхом підгонки лінійного рівняння до даних.

  • Логістична регресія: Алгоритм класифікації (незважаючи на свою назву), який використовує логістичну функцію для моделювання ймовірності бінарного результату.

  • Деревоподібні моделі: Моделі з деревоподібною структурою, які розділяють дані за ознаками для здійснення прогнозів; часто використовуються через їх зрозумілість.

  • Випадкові ліси: Ансамбль дерев рішень (через бэггінг), який покращує точність і зменшує перенавчання.

  • Методи опорних векторів (SVM): Класифікатори з максимальним відступом, які знаходять оптимальну роздільну гіперплощину; можуть використовувати ядра для нелінійних даних.

  • Наївний Байєс: Ймовірнісний класифікатор, заснований на теоремі Байєса з припущенням про незалежність ознак, відомий своєю популярністю у фільтрації спаму.

  • k-найближчих сусідів (k-NN): Простий класифікатор "на основі екземплярів", який позначає зразок на основі більшості класу його найближчих сусідів.

  • Градієнтний бустинг: Ансамблеві моделі (наприклад, XGBoost, LightGBM), які створюють потужний предиктор, послідовно додаючи слабші навчальні моделі (зазвичай дерева рішень).

Кожен розділ нижче надає покращений опис алгоритму та приклад коду на Python з використанням бібліотек, таких як pandas і scikit-learn (та PyTorch для прикладу з нейронною мережею). Приклади використовують публічно доступні набори даних з кібербезпеки (такі як NSL-KDD для виявлення вторгнень та набір даних про фішингові веб-сайти) і дотримуються послідовної структури:

  1. Завантажити набір даних (завантажити через URL, якщо доступно).

  2. Попередня обробка даних (наприклад, кодування категоріальних ознак, масштабування значень, розподіл на навчальні/тестові набори).

  3. Навчити модель на навчальних даних.

  4. Оцінити на тестовому наборі, використовуючи метрики: точність, точність, відгук, F1-оцінка та ROC AUC для класифікації (і середньоквадратична помилка для регресії).

Давайте заглибимося в кожен алгоритм:

Лінійна регресія

Лінійна регресія є регресійним алгоритмом, що використовується для прогнозування безперервних числових значень. Вона припускає лінійний зв'язок між вхідними ознаками (незалежними змінними) і виходом (залежною змінною). Модель намагається підлаштувати пряму (або гіперплощину у вищих вимірах), яка найкраще описує зв'язок між ознаками та цільовою змінною. Це зазвичай робиться шляхом мінімізації суми квадратів помилок між прогнозованими та фактичними значеннями (метод найменших квадратів).

Найпростіший спосіб представити лінійну регресію - це пряма:

plaintext
y = mx + b

Де:

  • y - це прогнозоване значення (вихід)
  • m - це нахил лінії (коефіцієнт)
  • x - це вхідна ознака
  • b - це y-перетин

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

plaintext
y = w1*x1 + w2*x2 + ... + wn*xn + b

tip

Випадки використання в кібербезпеці: Лінійна регресія сама по собі менш поширена для основних завдань безпеки (які часто є класифікацією), але її можна застосувати для прогнозування числових результатів. Наприклад, можна використовувати лінійну регресію для прогнозування обсягу мережевого трафіку або оцінки кількості атак за певний період часу на основі історичних даних. Вона також може прогнозувати ризиковий бал або очікуваний час до виявлення атаки, враховуючи певні системні метрики. На практиці алгоритми класифікації (як-от логістична регресія або дерева) частіше використовуються для виявлення вторгнень або шкідливого ПЗ, але лінійна регресія слугує основою і є корисною для аналізу, орієнтованого на регресію.

Ключові характеристики лінійної регресії:

  • Тип проблеми: Регресія (прогнозування безперервних значень). Не підходить для прямої класифікації, якщо не застосувати поріг до виходу.

  • Інтерпретованість: Висока -- коефіцієнти легко інтерпретувати, показуючи лінійний вплив кожної ознаки.

  • Переваги: Простота і швидкість; хороша базова лінія для завдань регресії; добре працює, коли істинний зв'язок приблизно лінійний.

  • Обмеження: Не може захоплювати складні або нелінійні зв'язки (без ручного інженерії ознак); схильна до недообучення, якщо зв'язки нелінійні; чутлива до викидів, які можуть спотворити результати.

  • Знаходження найкращого наближення: Щоб знайти найкращу лінію, яка розділяє можливі категорії, ми використовуємо метод, званий Звичайні найменші квадрати (OLS). Цей метод мінімізує суму квадратів різниць між спостережуваними значеннями та значеннями, передбаченими лінійною моделлю.

Приклад -- Прогнозування тривалості з'єднання (регресія) в наборі даних про вторгнення Нижче ми демонструємо лінійну регресію, використовуючи набір даних з кібербезпеки NSL-KDD. Ми розглянемо це як задачу регресії, прогнозуючи `тривалість` мережевих з'єднань на основі інших ознак. (Насправді, `тривалість` є однією з ознак NSL-KDD; ми використовуємо її тут лише для ілюстрації регресії.) Ми завантажуємо набір даних, попередньо обробляємо його (кодуємо категоріальні ознаки), навчаємо модель лінійної регресії та оцінюємо середню квадратичну помилку (MSE) та R² бал на тестовому наборі.
python
import pandas as pd
from sklearn.preprocessing import LabelEncoder
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error, r2_score

# ── 1. Column names taken from the NSL‑KDD documentation ──────────────
col_names = [
"duration","protocol_type","service","flag","src_bytes","dst_bytes","land",
"wrong_fragment","urgent","hot","num_failed_logins","logged_in",
"num_compromised","root_shell","su_attempted","num_root",
"num_file_creations","num_shells","num_access_files","num_outbound_cmds",
"is_host_login","is_guest_login","count","srv_count","serror_rate",
"srv_serror_rate","rerror_rate","srv_rerror_rate","same_srv_rate",
"diff_srv_rate","srv_diff_host_rate","dst_host_count",
"dst_host_srv_count","dst_host_same_srv_rate","dst_host_diff_srv_rate",
"dst_host_same_src_port_rate","dst_host_srv_diff_host_rate",
"dst_host_serror_rate","dst_host_srv_serror_rate","dst_host_rerror_rate",
"dst_host_srv_rerror_rate","class","difficulty_level"
]

# ── 2. Load data *without* header row ─────────────────────────────────
train_url = "https://raw.githubusercontent.com/Mamcose/NSL-KDD-Network-Intrusion-Detection/master/NSL_KDD_Train.csv"
test_url  = "https://raw.githubusercontent.com/Mamcose/NSL-KDD-Network-Intrusion-Detection/master/NSL_KDD_Test.csv"

df_train = pd.read_csv(train_url, header=None, names=col_names)
df_test  = pd.read_csv(test_url,  header=None, names=col_names)

# ── 3. Encode the 3 nominal features ─────────────────────────────────
for col in ['protocol_type', 'service', 'flag']:
le = LabelEncoder()
le.fit(pd.concat([df_train[col], df_test[col]], axis=0))
df_train[col] = le.transform(df_train[col])
df_test[col]  = le.transform(df_test[col])

# ── 4. Prepare features / target ─────────────────────────────────────
X_train = df_train.drop(columns=['class', 'difficulty_level', 'duration'])
y_train = df_train['duration']

X_test  = df_test.drop(columns=['class', 'difficulty_level', 'duration'])
y_test  = df_test['duration']

# ── 5. Train & evaluate simple Linear Regression ─────────────────────
model = LinearRegression().fit(X_train, y_train)
y_pred = model.predict(X_test)

print(f"Test MSE: {mean_squared_error(y_test, y_pred):.2f}")
print(f"Test R² : {r2_score(y_test, y_pred):.3f}")

"""
Test MSE: 3021333.56
Test R² : -0.526
"""

У цьому прикладі модель лінійної регресії намагається передбачити duration з інших мережевих характеристик. Ми вимірюємо продуктивність за допомогою середньоквадратичної помилки (MSE) та R². R², близький до 1.0, вказує на те, що модель пояснює більшість варіацій у duration, тоді як низький або негативний R² вказує на погане відповідність. (Не дивуйтеся, якщо R² тут низький -- передбачити duration може бути важко з наданих характеристик, і лінійна регресія може не вловити шаблони, якщо вони складні.)

Логістична регресія

Логістична регресія є алгоритмом класифікації, який моделює ймовірність того, що екземпляр належить до певного класу (зазвичай "позитивного" класу). Незважаючи на свою назву, логістична регресія використовується для дискретних результатів (на відміну від лінійної регресії, яка призначена для безперервних результатів). Вона особливо використовується для бінарної класифікації (два класи, наприклад, шкідливий проти доброзичливого), але може бути розширена для багатокласових проблем (використовуючи підходи softmax або один-проти-інших).

Логістична регресія використовує логістичну функцію (також відому як сигмоїдальна функція) для перетворення прогнозованих значень у ймовірності. Зверніть увагу, що сигмоїдальна функція є функцією зі значеннями між 0 і 1, яка зростає у S-подібній кривій відповідно до потреб класифікації, що корисно для завдань бінарної класифікації. Тому кожна характеристика кожного входу множиться на своє призначене вагове значення, а результат передається через сигмоїдальну функцію для отримання ймовірності:

plaintext
p(y=1|x) = 1 / (1 + e^(-z))

Де:

  • p(y=1|x) - це ймовірність того, що вихід y дорівнює 1, враховуючи вхід x
  • e - основа натурального логарифма
  • z - це лінійна комбінація вхідних ознак, зазвичай представлена як z = w1*x1 + w2*x2 + ... + wn*xn + b. Зверніть увагу, що в найпростішій формі це пряма лінія, але в більш складних випадках це стає гіперплощиною з кількома вимірами (по одному на кожну ознаку).

tip

Випадки використання в кібербезпеці: Оскільки багато проблем безпеки є по суті рішеннями так/ні, логістична регресія широко використовується. Наприклад, система виявлення вторгнень може використовувати логістичну регресію, щоб вирішити, чи є мережеве з'єднання атакою на основі ознак цього з'єднання. У виявленні фішингу логістична регресія може поєднувати ознаки веб-сайту (довжина URL, наявність символу "@" тощо) в ймовірність того, що це фішинг. Вона використовувалася в ранніх фільтрах спаму і залишається сильною базовою моделлю для багатьох завдань класифікації.

Логістична регресія для некласичної класифікації

Логістична регресія розроблена для бінарної класифікації, але її можна розширити для вирішення багатокласових проблем, використовуючи такі техніки, як один-проти-інших (OvR) або softmax регресія. У OvR для кожного класу навчається окрема модель логістичної регресії, розглядаючи її як позитивний клас проти всіх інших. Клас з найвищою прогнозованою ймовірністю обирається як остаточне передбачення. Softmax регресія узагальнює логістичну регресію для кількох класів, застосовуючи функцію softmax до вихідного шару, що виробляє розподіл ймовірностей для всіх класів.

Ключові характеристики логістичної регресії:

  • Тип проблеми: Класифікація (зазвичай бінарна). Вона прогнозує ймовірність позитивного класу.

  • Інтерпретованість: Висока -- як і в лінійній регресії, коефіцієнти ознак можуть вказувати, як кожна ознака впливає на логарифмічні шанси результату. Ця прозорість часто цінується в безпеці для розуміння, які фактори сприяють сповіщенню.

  • Переваги: Проста і швидка в навчанні; добре працює, коли зв'язок між ознаками та логарифмічними шансами результату є лінійним. Виводить ймовірності, що дозволяє оцінювати ризики. З відповідною регуляризацією вона добре узагальнює і може краще справлятися з мультиколінеарністю, ніж проста лінійна регресія.

  • Обмеження: Припускає лінійний межу прийняття рішень у просторі ознак (не справляється, якщо справжня межа є складною/нелінійною). Вона може показувати погані результати в проблемах, де взаємодії або нелінійні ефекти є критичними, якщо ви не додасте поліноміальні або взаємодійні ознаки вручну. Також логістична регресія менш ефективна, якщо класи не можуть бути легко розділені лінійною комбінацією ознак.

Приклад -- Виявлення фішингових веб-сайтів за допомогою логістичної регресії:

Ми використаємо Набір даних фішингових веб-сайтів (з репозиторію UCI), який містить витягнуті ознаки веб-сайтів (наприклад, чи має URL IP-адресу, вік домену, наявність підозрілих елементів у HTML тощо) та мітку, що вказує, чи є сайт фішинговим або легітимним. Ми навчаємо модель логістичної регресії для класифікації веб-сайтів, а потім оцінюємо її точність, точність, відгук, F1-оцінку та ROC AUC на тестовій вибірці.

python
import pandas as pd
from sklearn.datasets import fetch_openml
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, roc_auc_score

# 1. Load dataset
data = fetch_openml(data_id=4534, as_frame=True)  # PhishingWebsites
df   = data.frame
print(df.head())

# 2. Target mapping ─ legitimate (1) → 0, everything else → 1
df['Result'] = df['Result'].astype(int)
y = (df['Result'] != 1).astype(int)

# 3. Features
X = df.drop(columns=['Result'])

# 4. Train/test split with stratify
## Stratify ensures balanced classes in train/test sets
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.20, random_state=42, stratify=y)

# 5. Scale
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test  = scaler.transform(X_test)

# 6. Logistic Regression
## L‑BFGS is a modern, memory‑efficient “quasi‑Newton” algorithm that works well for medium/large datasets and supports multiclass natively.
## Upper bound on how many optimization steps the solver may take before it gives up.	Not all steps are guaranteed to be taken, but would be the maximum before a "failed to converge" error.
clf = LogisticRegression(max_iter=1000, solver='lbfgs', random_state=42)
clf.fit(X_train, y_train)

# 7. Evaluation
y_pred = clf.predict(X_test)
y_prob = clf.predict_proba(X_test)[:, 1]

print(f"Accuracy : {accuracy_score(y_test, y_pred):.3f}")
print(f"Precision: {precision_score(y_test, y_pred):.3f}")
print(f"Recall   : {recall_score(y_test, y_pred):.3f}")
print(f"F1-score : {f1_score(y_test, y_pred):.3f}")
print(f"ROC AUC  : {roc_auc_score(y_test, y_prob):.3f}")

"""
Accuracy : 0.928
Precision: 0.934
Recall   : 0.901
F1-score : 0.917
ROC AUC  : 0.979
"""

У цьому прикладі виявлення фішингу логістична регресія генерує ймовірність для кожного веб-сайту бути фішинговим. Оцінюючи точність, точність (precision), відзив (recall) та F1, ми отримуємо уявлення про продуктивність моделі. Наприклад, високий відзив означає, що вона виявляє більшість фішингових сайтів (важливо для безпеки, щоб мінімізувати пропущені атаки), тоді як висока точність означає, що має мало хибних спрацьовувань (важливо, щоб уникнути втоми аналітиків). ROC AUC (Площа під кривою ROC) надає незалежну від порогу міру продуктивності (1.0 є ідеальним, 0.5 не краще, ніж випадковість). Логістична регресія часто добре справляється з такими завданнями, але якщо межа прийняття рішень між фішинговими та легітимними сайтами є складною, можуть знадобитися більш потужні нелінійні моделі.

Дерев'яні рішення

Дерево рішень є універсальним алгоритмом навчання з учителем, який можна використовувати як для класифікації, так і для регресії. Воно навчається на основі ієрархічної моделі рішень, схожої на дерево, на основі ознак даних. Кожен внутрішній вузол дерева представляє тест на певну ознаку, кожна гілка представляє результат цього тесту, а кожен листовий вузол представляє прогнозований клас (для класифікації) або значення (для регресії).

Для побудови дерева алгоритми, такі як CART (Classification and Regression Tree), використовують такі міри, як нечистота Джині або приріст інформації (ентропія), щоб вибрати найкращу ознаку та поріг для розподілу даних на кожному кроці. Мета на кожному розподілі полягає в тому, щоб розділити дані, щоб збільшити однорідність цільової змінної в отриманих підмножинах (для класифікації кожен вузол прагне бути якомога чистішим, містячи переважно один клас).

Дерева рішень є високоінтерпретованими — можна слідувати шляху від кореня до листа, щоб зрозуміти логіку за прогнозом (наприклад, "ЯКЩО service = telnet І src_bytes > 1000 І failed_logins > 3 ТО класифікувати як атаку"). Це цінно в кібербезпеці для пояснення, чому було піднято певне сповіщення. Дерева можуть природно обробляти як числові, так і категоріальні дані та вимагають мало попередньої обробки (наприклад, масштабування ознак не потрібне).

Однак одне дерево рішень може легко перенавчитися на навчальних даних, особливо якщо його виростити глибоким (багато розподілів). Техніки, такі як обрізка (обмеження глибини дерева або вимога мінімальної кількості зразків на лист), часто використовуються для запобігання перенавчанню.

Є 3 основні компоненти дерева рішень:

  • Кореневий вузол: Верхній вузол дерева, що представляє весь набір даних.
  • Внутрішні вузли: Вузли, які представляють ознаки та рішення на основі цих ознак.
  • Листові вузли: Вузли, які представляють остаточний результат або прогноз.

Дерево може виглядати ось так:

plaintext
[Root Node]
/   \
[Node A]  [Node B]
/   \      /   \
[Leaf 1] [Leaf 2] [Leaf 3] [Leaf 4]

tip

Випадки використання в кібербезпеці: Деревоподібні моделі використовувалися в системах виявлення вторгнень для отримання правил для ідентифікації атак. Наприклад, ранні IDS, такі як системи на основі ID3/C4.5, генерували правила, зрозумілі людині, для розрізнення нормального та шкідливого трафіку. Вони також використовуються в аналізі шкідливого ПЗ для визначення, чи є файл шкідливим на основі його атрибутів (розмір файлу, ентропія секцій, виклики API тощо). Чіткість дерев рішень робить їх корисними, коли потрібна прозорість — аналітик може перевірити дерево, щоб підтвердити логіку виявлення.

Ключові характеристики дерев рішень:

  • Тип проблеми: Як класифікація, так і регресія. Зазвичай використовуються для класифікації атак проти нормального трафіку тощо.

  • Інтерпретованість: Дуже висока — рішення моделі можна візуалізувати та зрозуміти як набір правил if-then. Це велика перевага в безпеці для довіри та перевірки поведінки моделі.

  • Переваги: Можуть захоплювати нелінійні зв'язки та взаємодії між ознаками (кожен розподіл можна розглядати як взаємодію). Немає потреби масштабувати ознаки або кодувати категоріальні змінні — дерева обробляють це нативно. Швидке виведення (прогнозування — це просто слідування шляху в дереві).

  • Обмеження: Схильні до перенавчання, якщо не контролювати (глибоке дерево може запам'ятати навчальний набір). Вони можуть бути нестабільними — незначні зміни в даних можуть призвести до іншої структури дерева. Як окремі моделі, їхня точність може не відповідати більш просунутим методам (ансамблі, такі як Random Forests, зазвичай працюють краще, зменшуючи дисперсію).

  • Знаходження найкращого розподілу:

  • Нечистота Джині: Вимірює нечистоту вузла. Нижча нечистота Джині вказує на кращий розподіл. Формула:

plaintext
Gini = 1 - Σ(p_i^2)

Де p_i — це пропорція екземплярів у класі i.

  • Ентропія: Вимірює невизначеність у наборі даних. Нижча ентропія вказує на кращий розподіл. Формула:
plaintext
Entropy = -Σ(p_i * log2(p_i))

Де p_i — це пропорція екземплярів у класі i.

  • Приріст інформації: Зменшення ентропії або нечистоти Джині після розподілу. Чим вищий приріст інформації, тим кращий розподіл. Він обчислюється як:
plaintext
Information Gain = Entropy(parent) - (Weighted Average of Entropy(children))

Крім того, дерево закінчується, коли:

  • Всі екземпляри в вузлі належать до одного класу. Це може призвести до перенавчання.
  • Досягнуто максимальну глибину (задано в коді) дерева. Це спосіб запобігти перенавчанню.
  • Кількість екземплярів у вузлі нижча за певний поріг. Це також спосіб запобігти перенавчанню.
  • Приріст інформації від подальших розподілів нижчий за певний поріг. Це також спосіб запобігти перенавчанню.
Приклад — Дерево рішень для виявлення вторгнень: Ми навчимо дерево рішень на наборі даних NSL-KDD для класифікації мережевих з'єднань як *нормальних*, так і *атак*. NSL-KDD є покращеною версією класичного набору даних KDD Cup 1999, з такими ознаками, як тип протоколу, служба, тривалість, кількість невдалих входів тощо, та міткою, що вказує на тип атаки або "нормальний". Ми відобразимо всі типи атак на клас "аномалія" (бінарна класифікація: нормальний проти аномалії). Після навчання ми оцінюємо продуктивність дерева на тестовому наборі.
python
import pandas as pd
from sklearn.tree import DecisionTreeClassifier
from sklearn.preprocessing import LabelEncoder
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, roc_auc_score

# 1️⃣  NSL‑KDD column names (41 features + class + difficulty)
col_names = [
"duration","protocol_type","service","flag","src_bytes","dst_bytes","land",
"wrong_fragment","urgent","hot","num_failed_logins","logged_in","num_compromised",
"root_shell","su_attempted","num_root","num_file_creations","num_shells",
"num_access_files","num_outbound_cmds","is_host_login","is_guest_login","count",
"srv_count","serror_rate","srv_serror_rate","rerror_rate","srv_rerror_rate",
"same_srv_rate","diff_srv_rate","srv_diff_host_rate","dst_host_count",
"dst_host_srv_count","dst_host_same_srv_rate","dst_host_diff_srv_rate",
"dst_host_same_src_port_rate","dst_host_srv_diff_host_rate","dst_host_serror_rate",
"dst_host_srv_serror_rate","dst_host_rerror_rate","dst_host_srv_rerror_rate",
"class","difficulty_level"
]

# 2️⃣  Load data ➜ *headerless* CSV
train_url = "https://raw.githubusercontent.com/Mamcose/NSL-KDD-Network-Intrusion-Detection/master/NSL_KDD_Train.csv"
test_url  = "https://raw.githubusercontent.com/Mamcose/NSL-KDD-Network-Intrusion-Detection/master/NSL_KDD_Test.csv"

df_train = pd.read_csv(train_url, header=None, names=col_names)
df_test  = pd.read_csv(test_url,  header=None, names=col_names)

# 3️⃣  Encode the 3 nominal features
for col in ['protocol_type', 'service', 'flag']:
le = LabelEncoder().fit(pd.concat([df_train[col], df_test[col]]))
df_train[col] = le.transform(df_train[col])
df_test[col]  = le.transform(df_test[col])

# 4️⃣  Prepare X / y   (binary: 0 = normal, 1 = attack)
X_train = df_train.drop(columns=['class', 'difficulty_level'])
y_train = (df_train['class'].str.lower() != 'normal').astype(int)

X_test  = df_test.drop(columns=['class', 'difficulty_level'])
y_test  = (df_test['class'].str.lower() != 'normal').astype(int)

# 5️⃣  Train Decision‑Tree
clf = DecisionTreeClassifier(max_depth=10, random_state=42)
clf.fit(X_train, y_train)

# 6️⃣  Evaluate
y_pred = clf.predict(X_test)
y_prob = clf.predict_proba(X_test)[:, 1]

print(f"Accuracy : {accuracy_score(y_test, y_pred):.3f}")
print(f"Precision: {precision_score(y_test, y_pred):.3f}")
print(f"Recall   : {recall_score(y_test, y_pred):.3f}")
print(f"F1‑score : {f1_score(y_test, y_pred):.3f}")
print(f"ROC AUC  : {roc_auc_score(y_test, y_prob):.3f}")


"""
Accuracy : 0.772
Precision: 0.967
Recall   : 0.621
F1‑score : 0.756
ROC AUC  : 0.758
"""

У цьому прикладі дерева рішень ми обмежили глибину дерева до 10, щоб уникнути екстремального перенавчання (параметр max_depth=10). Метрики показують, наскільки добре дерево розрізняє нормальний та атакуючий трафік. Високий відгук означає, що воно ловить більшість атак (важливо для IDS), тоді як висока точність означає, що мало хибних тривог. Дерева рішень часто досягають пристойної точності на структурованих даних, але одне дерево може не досягти найкращої можливої продуктивності. Тим не менш, інтерпретованість моделі є великою перевагою — ми могли б дослідити розподіли дерева, щоб побачити, наприклад, які ознаки (наприклад, service, src_bytes тощо) є найвпливовішими для позначення з'єднання як шкідливого.

Випадкові ліси

Випадковий ліс — це метод ансамблевого навчання, який базується на деревах рішень для покращення продуктивності. Випадковий ліс навчає кілька дерев рішень (отже, "ліс") і комбінує їхні результати для отримання остаточного прогнозу (для класифікації, зазвичай шляхом голосування більшості). Дві основні ідеї у випадковому лісі — це беггінг (bootstrap aggregating) та випадковість ознак:

  • Беггінг: Кожне дерево навчається на випадковій вибірці з навчальних даних (вибірка з поверненням). Це вводить різноманітність серед дерев.

  • Випадковість ознак: На кожному розподілі в дереві розглядається випадкова підмножина ознак для розподілу (замість усіх ознак). Це ще більше декорелює дерева.

Середнє значення результатів багатьох дерев зменшує дисперсію, яку може мати одне дерево рішень. Простими словами, окремі дерева можуть перенавчатися або бути шумними, але велика кількість різноманітних дерев, що голосують разом, згладжує ці помилки. Результат часто є моделлю з вищою точністю та кращою узагальненістю, ніж одне дерево рішень. Крім того, випадкові ліси можуть надати оцінку важливості ознак (дивлячись на те, наскільки кожен розподіл ознаки зменшує нечистоту в середньому).

Випадкові ліси стали робочою конячкою в кібербезпеці для завдань, таких як виявлення вторгнень, класифікація шкідливого ПЗ та виявлення спаму. Вони часто добре працюють з мінімальним налаштуванням і можуть обробляти великі набори ознак. Наприклад, у виявленні вторгнень випадковий ліс може перевершити окреме дерево рішень, виявляючи більш тонкі патерни атак з меншими хибними позитивами. Дослідження показали, що випадкові ліси демонструють позитивні результати в порівнянні з іншими алгоритмами при класифікації атак у наборах даних, таких як NSL-KDD та UNSW-NB15.

Ключові характеристики випадкових лісів:

  • Тип проблеми: Переважно класифікація (також використовується для регресії). Дуже добре підходить для високорозмірних структурованих даних, поширених у журналах безпеки.

  • Інтерпретованість: Нижча, ніж у одного дерева рішень — ви не можете легко візуалізувати або пояснити сотні дерев одночасно. Однак оцінки важливості ознак надають певне уявлення про те, які атрибути є найвпливовішими.

  • Переваги: Загалом вища точність, ніж у моделей з одним деревом завдяки ансамблевому ефекту. Стійкість до перенавчання — навіть якщо окремі дерева перенавчаються, ансамбль узагальнює краще. Обробляє як числові, так і категоріальні ознаки та може управляти відсутніми даними до певної міри. Також відносно стійкий до викидів.

  • Обмеження: Розмір моделі може бути великим (багато дерев, кожне потенційно глибоке). Прогнози повільніші, ніж у одного дерева (оскільки потрібно агрегувати результати багатьох дерев). Менш інтерпретовані — хоча ви знаєте важливі ознаки, точну логіку не легко відстежити як просте правило. Якщо набір даних є надзвичайно високорозмірним і розрідженим, навчання дуже великого лісу може бути обчислювально важким.

  • Процес навчання:

  1. Вибірка Bootstrap: Випадковим чином вибирайте навчальні дані з поверненням, щоб створити кілька підмножин (вибірки bootstrap).
  2. Конструкція дерева: Для кожної вибірки bootstrap побудуйте дерево рішень, використовуючи випадкову підмножину ознак на кожному розподілі. Це вводить різноманітність серед дерев.
  3. Агрегація: Для завдань класифікації остаточний прогноз робиться шляхом голосування більшості серед прогнозів усіх дерев. Для завдань регресії остаточний прогноз — це середнє значення прогнозів з усіх дерев.
Приклад — Випадковий ліс для виявлення вторгнень (NSL-KDD): Ми використаємо той же набір даних NSL-KDD (бінарно позначений як нормальний проти аномалії) і навчимо класифікатор випадкового лісу. Ми очікуємо, що випадковий ліс працюватиме так само добре або краще, ніж одне дерево рішень, завдяки середньому значенню ансамблю, що зменшує дисперсію. Ми оцінимо його за тими ж метриками.
python
import pandas as pd
from sklearn.preprocessing import LabelEncoder
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import (accuracy_score, precision_score,
recall_score, f1_score, roc_auc_score)

# ──────────────────────────────────────────────
# 1. LOAD DATA  ➜  files have **no header row**, so we
#                 pass `header=None` and give our own column names.
# ──────────────────────────────────────────────
col_names = [                       # 41 features + 2 targets
"duration","protocol_type","service","flag","src_bytes","dst_bytes","land",
"wrong_fragment","urgent","hot","num_failed_logins","logged_in",
"num_compromised","root_shell","su_attempted","num_root","num_file_creations",
"num_shells","num_access_files","num_outbound_cmds","is_host_login",
"is_guest_login","count","srv_count","serror_rate","srv_serror_rate",
"rerror_rate","srv_rerror_rate","same_srv_rate","diff_srv_rate",
"srv_diff_host_rate","dst_host_count","dst_host_srv_count",
"dst_host_same_srv_rate","dst_host_diff_srv_rate",
"dst_host_same_src_port_rate","dst_host_srv_diff_host_rate",
"dst_host_serror_rate","dst_host_srv_serror_rate","dst_host_rerror_rate",
"dst_host_srv_rerror_rate","class","difficulty_level"
]

train_url = "https://raw.githubusercontent.com/Mamcose/NSL-KDD-Network-Intrusion-Detection/master/NSL_KDD_Train.csv"
test_url  = "https://raw.githubusercontent.com/Mamcose/NSL-KDD-Network-Intrusion-Detection/master/NSL_KDD_Test.csv"

df_train = pd.read_csv(train_url, header=None, names=col_names)
df_test  = pd.read_csv(test_url,  header=None, names=col_names)

# ──────────────────────────────────────────────
# 2. PRE‑PROCESSING
# ──────────────────────────────────────────────
# 2‑a) Encode the three categorical columns so that the model
#      receives integers instead of strings.
#      LabelEncoder gives an int to each unique value in the column: {'icmp':0, 'tcp':1, 'udp':2}
for col in ['protocol_type', 'service', 'flag']:
le = LabelEncoder().fit(pd.concat([df_train[col], df_test[col]]))
df_train[col] = le.transform(df_train[col])
df_test[col]  = le.transform(df_test[col])

# 2‑b) Build feature matrix X  (drop target & difficulty)
X_train = df_train.drop(columns=['class', 'difficulty_level'])
X_test  = df_test.drop(columns=['class', 'difficulty_level'])

# 2‑c) Convert multi‑class labels to binary
#      label 0 → 'normal' traffic, label 1 → any attack
y_train = (df_train['class'].str.lower() != 'normal').astype(int)
y_test  = (df_test['class'].str.lower() != 'normal').astype(int)

# ──────────────────────────────────────────────
# 3. MODEL: RANDOM FOREST
# ──────────────────────────────────────────────
# • n_estimators = 100 ➜ build 100 different decision‑trees.
# • max_depth=None  ➜ let each tree grow until pure leaves
#                    (or until it hits other stopping criteria).
# • random_state=42 ➜ reproducible randomness.
model = RandomForestClassifier(
n_estimators=100,
max_depth=None,
random_state=42,
bootstrap=True          # default: each tree is trained on a
# bootstrap sample the same size as
# the original training set.
# max_samples           # ← you can set this (float or int) to
#     use a smaller % of samples per tree.
)

model.fit(X_train, y_train)

# ──────────────────────────────────────────────
# 4. EVALUATION
# ──────────────────────────────────────────────
y_pred = model.predict(X_test)
y_prob = model.predict_proba(X_test)[:, 1]

print(f"Accuracy : {accuracy_score(y_test, y_pred):.3f}")
print(f"Precision: {precision_score(y_test, y_pred):.3f}")
print(f"Recall   : {recall_score(y_test, y_pred):.3f}")
print(f"F1‑score : {f1_score(y_test, y_pred):.3f}")
print(f"ROC AUC  : {roc_auc_score(y_test, y_prob):.3f}")

"""
Accuracy:  0.770
Precision: 0.966
Recall:    0.618
F1-score:  0.754
ROC AUC:   0.962
"""

Випадковий ліс зазвичай досягає сильних результатів у цій задачі виявлення вторгнень. Ми можемо спостерігати покращення в метриках, таких як F1 або AUC, порівняно з єдиним деревом рішень, особливо в recall або precision, залежно від даних. Це узгоджується з розумінням, що "Random Forest (RF) є ансамблевим класифікатором і добре працює в порівнянні з іншими традиційними класифікаторами для ефективної класифікації атак.". У контексті операцій безпеки модель випадкового лісу може надійніше виявляти атаки, зменшуючи кількість хибних сповіщень, завдяки усередненню багатьох правил рішень. Важливість ознак з лісу може показати, які мережеві ознаки є найбільш показовими для атак (наприклад, певні мережеві сервіси або незвичайні кількості пакетів).

Support Vector Machines (SVM)

Support Vector Machines - це потужні моделі навчання з учителем, які використовуються в основному для класифікації (а також регресії як SVR). SVM намагається знайти оптимальну розділову гіперплощину, яка максимізує відстань між двома класами. Лише підмножина навчальних точок (так звані "опорні вектори", найближчі до межі) визначає положення цієї гіперплощини. Максимізуючи відстань (відстань між опорними векторами та гіперплощиною), SVM зазвичай досягає хорошого узагальнення.

Ключем до сили SVM є можливість використовувати ядерні функції для обробки нелінійних зв'язків. Дані можуть бути неявно перетворені в простір ознак вищої вимірності, де може існувати лінійний роздільник. Загальні ядра включають поліноміальне, радіальну базисну функцію (RBF) та сигмоїдальне. Наприклад, якщо класи мережевого трафіку не є лінійно роздільними в сирому просторі ознак, RBF-ядро може відобразити їх у вищу вимірність, де SVM знаходить лінійний розподіл (що відповідає нелінійній межі в оригінальному просторі). Гнучкість вибору ядер дозволяє SVM вирішувати різноманітні проблеми.

SVM відомі своєю ефективністю в ситуаціях з високовимірними просторами ознак (як-от текстові дані або послідовності опкодів шкідливого ПЗ) і в випадках, коли кількість ознак велика відносно кількості зразків. Вони були популярні в багатьох ранніх застосуваннях кібербезпеки, таких як класифікація шкідливого ПЗ та виявлення вторгнень на основі аномалій у 2000-х роках, часто демонструючи високу точність.

Однак SVM не легко масштабуються на дуже великі набори даних (складність навчання є суперлінійною від кількості зразків, а використання пам'яті може бути високим, оскільки може знадобитися зберігати багато опорних векторів). На практиці, для завдань, таких як виявлення вторгнень у мережі з мільйонами записів, SVM може бути занадто повільним без ретельного підбору підвибірки або використання апроксимаційних методів.

Ключові характеристики SVM:

  • Тип проблеми: Класифікація (бінарна або багатокласова через один-проти-одного/один-проти-решти) та варіанти регресії. Часто використовуються в бінарній класифікації з чітким розділенням межі.

  • Інтерпретованість: Середня -- SVM не є такими ж зрозумілими, як дерева рішень або логістична регресія. Хоча ви можете визначити, які точки даних є опорними векторами, і отримати уявлення про те, які ознаки можуть бути впливовими (через ваги в лінійному випадку з ядром), на практиці SVM (особливо з нелінійними ядрами) розглядаються як чорні ящики.

  • Переваги: Ефективні в високовимірних просторах; можуть моделювати складні межі рішень за допомогою ядерного трюку; стійкі до перенавчання, якщо відстань максимізована (особливо з правильним параметром регуляризації C); добре працюють навіть коли класи не розділені великою відстанню (знаходять найкращу компромісну межу).

  • Обмеження: Витратні з обчислювальної точки зору для великих наборів даних (як навчання, так і прогнозування погано масштабується з ростом даних). Вимагає ретельного налаштування параметрів ядра та регуляризації (C, тип ядра, гамма для RBF тощо). Не надає безпосередньо ймовірнісних виходів (хоча можна використовувати масштабування Платта для отримання ймовірностей). Також SVM можуть бути чутливими до вибору параметрів ядра --- поганий вибір може призвести до недонавчання або перенавчання.

Випадки використання в кібербезпеці: SVM використовувалися в виявленні шкідливого ПЗ (наприклад, класифікація файлів на основі витягнутих ознак або послідовностей опкодів), виявленні аномалій у мережі (класифікація трафіку як нормального або шкідливого) та виявленні фішингу (використовуючи ознаки URL). Наприклад, SVM може взяти ознаки електронного листа (кількість певних ключових слів, бали репутації відправника тощо) і класифікувати його як фішинг або легітимний. Вони також були застосовані до виявлення вторгнень на наборах ознак, таких як KDD, часто досягаючи високої точності за рахунок обчислень.

Приклад -- SVM для класифікації шкідливого ПЗ: Ми знову використаємо набір даних про фішингові вебсайти, цього разу з SVM. Оскільки SVM можуть бути повільними, ми використаємо підмножину даних для навчання, якщо це необхідно (набір даних містить близько 11 тис. екземплярів, що SVM може обробити розумно). Ми використаємо RBF-ядро, яке є поширеним вибором для нелінійних даних, і ми ввімкнемо оцінки ймовірності для розрахунку ROC AUC.
python
import pandas as pd
from sklearn.datasets import fetch_openml
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC
from sklearn.metrics import (accuracy_score, precision_score,
recall_score, f1_score, roc_auc_score)

# ─────────────────────────────────────────────────────────────
# 1️⃣  LOAD DATASET   (OpenML id 4534: “PhishingWebsites”)
#     • as_frame=True  ➜  returns a pandas DataFrame
# ─────────────────────────────────────────────────────────────
data = fetch_openml(data_id=4534, as_frame=True)   # or data_name="PhishingWebsites"
df   = data.frame
print(df.head())          # quick sanity‑check

# ─────────────────────────────────────────────────────────────
# 2️⃣  TARGET: 0 = legitimate, 1 = phishing
#     The raw column has values {1, 0, -1}:
#       1  → legitimate   → 0
#       0  &  -1          → phishing    → 1
# ─────────────────────────────────────────────────────────────
y = (df["Result"].astype(int) != 1).astype(int)
X = df.drop(columns=["Result"])

# Train / test split  (stratified keeps class proportions)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.20, random_state=42, stratify=y)

# ─────────────────────────────────────────────────────────────
# 3️⃣  PRE‑PROCESS: Standardize features (mean‑0 / std‑1)
# ─────────────────────────────────────────────────────────────
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test  = scaler.transform(X_test)

# ─────────────────────────────────────────────────────────────
# 4️⃣  MODEL: RBF‑kernel SVM
#     • C=1.0         (regularization strength)
#     • gamma='scale' (1 / [n_features × var(X)])
#     • probability=True  → enable predict_proba for ROC‑AUC
# ─────────────────────────────────────────────────────────────
clf = SVC(kernel="rbf", C=1.0, gamma="scale",
probability=True, random_state=42)
clf.fit(X_train, y_train)

# ─────────────────────────────────────────────────────────────
# 5️⃣  EVALUATION
# ─────────────────────────────────────────────────────────────
y_pred = clf.predict(X_test)
y_prob = clf.predict_proba(X_test)[:, 1]   # P(class 1)

print(f"Accuracy : {accuracy_score(y_test, y_pred):.3f}")
print(f"Precision: {precision_score(y_test, y_pred):.3f}")
print(f"Recall   : {recall_score(y_test, y_pred):.3f}")
print(f"F1‑score : {f1_score(y_test, y_pred):.3f}")
print(f"ROC AUC  : {roc_auc_score(y_test, y_prob):.3f}")

"""
Accuracy : 0.956
Precision: 0.963
Recall   : 0.937
F1‑score : 0.950
ROC AUC  : 0.989
"""

Модель SVM виведе метрики, які ми можемо порівняти з логістичною регресією для тієї ж задачі. Ми можемо виявити, що SVM досягає високої точності та AUC, якщо дані добре розділені за ознаками. З іншого боку, якщо набір даних містить багато шуму або перекриваючих класів, SVM може не значно перевершувати логістичну регресію. На практиці SVM може дати приріст, коли існують складні, нелінійні зв'язки між ознаками та класом — RBF-ядро може захоплювати вигнуті межі рішень, які логістична регресія пропустить. Як і з усіма моделями, необхідна ретельна настройка параметрів C (регуляризація) та ядра (наприклад, gamma для RBF), щоб збалансувати зміщення та дисперсію.

Різниця між логістичною регресією та SVM

АспектЛогістична регресіяМетоди опорних векторів
Функція метиМінімізує лог-ущерб (крос-ентропія).Максимізує маржу, мінімізуючи hinge-ущерб.
Межа рішенняЗнаходить найкращу гіперплощину, яка моделює P(y|x).Знаходить гіперплощину з максимальною маржею (найбільший розрив до найближчих точок).
ВихідЙмовірнісний – надає калібровані ймовірності класів через σ(w·x + b).Детермінований – повертає мітки класів; ймовірності потребують додаткової роботи (наприклад, масштабування Платта).
РегуляризаціяL2 (за замовчуванням) або L1, безпосередньо балансує недо- та переобучення.Параметр C компенсує ширину маржі проти помилок класифікації; параметри ядра додають складності.
Ядра / НелінійністьРідна форма є лінійною; нелінійність додається через інженерію ознак.Вбудований трик ядра (RBF, поліноміальне тощо) дозволяє моделювати складні межі у високих вимірах.
МасштабованістьРозв'язує опуклу оптимізацію за O(nd); добре справляється з дуже великим n.Навчання може бути O(n²–n³) за пам'яттю/часом без спеціалізованих рішень; менш дружнє до величезного n.
ІнтерпретованістьВисока – ваги показують вплив ознак; відношення шансів інтуїтивно зрозуміле.Низька для нелінійних ядер; опорні вектори розріджені, але не легко пояснити.
Чутливість до викидівВикористовує гладкий лог-ущерб → менш чутливий.Hinge-ущерб з жорсткою маржею може бути чутливим; м'яка маржа (C) пом'якшує.
Типові випадки використанняКредитне оцінювання, медичний ризик, A/B тестування – де ймовірності та пояснюваність мають значення.Класифікація зображень/тексту, біоінформатика – де важливі складні межі та високовимірні дані.
  • Якщо вам потрібні калібровані ймовірності, інтерпретованість або ви працюєте з величезними наборами даних — обирайте логістичну регресію.
  • Якщо вам потрібна гнучка модель, яка може захоплювати нелінійні зв'язки без ручної інженерії ознак — обирайте SVM (з ядрами).
  • Обидві оптимізують опуклі цілі, тому глобальні мінімуми гарантовані, але ядра SVM додають гіперпараметри та обчислювальні витрати.

Наївний Байєс

Наївний Байєс — це сімейство ймовірнісних класифікаторів, заснованих на застосуванні теореми Байєса з сильною незалежністю між ознаками. Незважаючи на це "наївне" припущення, Наївний Байєс часто працює дивовижно добре для певних застосувань, особливо тих, що стосуються тексту або категоричних даних, таких як виявлення спаму.

Теорема Байєса

Теорема Байєса є основою класифікаторів Наївного Байєса. Вона пов'язує умовні та маргінальні ймовірності випадкових подій. Формула:

plaintext
P(A|B) = (P(B|A) * P(A)) / P(B)

Де:

  • P(A|B) є апостеріорною ймовірністю класу A за умови ознаки B.
  • P(B|A) є ймовірністю ознаки B за умови класу A.
  • P(A) є апріорною ймовірністю класу A.
  • P(B) є апріорною ймовірністю ознаки B.

Наприклад, якщо ми хочемо класифікувати, чи текст написаний дитиною чи дорослим, ми можемо використовувати слова в тексті як ознаки. На основі деяких початкових даних, класифікатор Наївного Байєса попередньо обчислить ймовірності кожного слова бути в кожному потенційному класі (дитина або дорослий). Коли надається новий текст, він обчислить ймовірність кожного потенційного класу, враховуючи слова в тексті, і вибере клас з найвищою ймовірністю.

Як ви можете бачити в цьому прикладі, класифікатор Наївного Байєса є дуже простим і швидким, але він припускає, що ознаки незалежні, що не завжди є правдою в реальних даних.

Типи класифікаторів Наївного Байєса

Існує кілька типів класифікаторів Наївного Байєса, залежно від типу даних і розподілу ознак:

  • Гаусів Наївний Байєс: Припускає, що ознаки слідують гаусівському (нормальному) розподілу. Підходить для безперервних даних.
  • Мультиноміальний Наївний Байєс: Припускає, що ознаки слідують мультиноміальному розподілу. Підходить для дискретних даних, таких як кількість слів у класифікації тексту.
  • Бернуллі Наївний Байєс: Припускає, що ознаки є бінарними (0 або 1). Підходить для бінарних даних, таких як наявність або відсутність слів у класифікації тексту.
  • Категоричний Наївний Байєс: Припускає, що ознаки є категоріальними змінними. Підходить для категоріальних даних, таких як класифікація фруктів за їх кольором і формою.

#### Ключові характеристики Наївного Байєса:

  • Тип проблеми: Класифікація (бінарна або багатокласова). Зазвичай використовується для завдань класифікації тексту в кібербезпеці (спам, фішинг тощо).

  • Інтерпретованість: Середня -- він не так безпосередньо інтерпретується, як дерево рішень, але можна перевірити вивчені ймовірності (наприклад, які слова найімовірніше в спам-пошті в порівнянні з "хамом"). Форму моделі (ймовірності для кожної ознаки за умови класу) можна зрозуміти, якщо це потрібно.

  • Переваги: Дуже швидке навчання та прогнозування, навіть на великих наборах даних (лінійно в кількості екземплярів * кількість ознак). Вимагає відносно невелику кількість даних для надійної оцінки ймовірностей, особливо з належним згладжуванням. Часто є дивно точним як базовий варіант, особливо коли ознаки незалежно вносять свідчення до класу. Добре працює з високорозмірними даними (наприклад, тисячі ознак з тексту). Не вимагає складного налаштування, окрім встановлення параметра згладжування.

  • Обмеження: Припущення незалежності може обмежити точність, якщо ознаки сильно корельовані. Наприклад, у мережевих даних ознаки, такі як src_bytes і dst_bytes, можуть бути корельовані; Наївний Байєс не зможе зафіксувати цю взаємодію. Коли розмір даних стає дуже великим, більш виразні моделі (такі як ансамблі або нейронні мережі) можуть перевершити Наївного Байєса, навчаючись залежностям ознак. Також, якщо для ідентифікації атаки потрібна певна комбінація ознак (а не лише окремі ознаки незалежно), Наївний Байєс матиме труднощі.

tip

Випадки використання в кібербезпеці: Класичне використання -- виявлення спаму -- Наївний Байєс був основою ранніх фільтрів спаму, використовуючи частоти певних токенів (слів, фраз, IP-адрес) для обчислення ймовірності того, що електронний лист є спамом. Він також використовується в виявленні фішингових електронних листів та класифікації URL, де наявність певних ключових слів або характеристик (як "login.php" в URL, або @ в шляху URL) сприяє ймовірності фішингу. У аналізі шкідливого ПЗ можна уявити класифікатор Наївного Байєса, який використовує наявність певних викликів API або дозволів у програмному забезпеченні, щоб передбачити, чи є це шкідливим ПЗ. Хоча більш просунуті алгоритми часто працюють краще, Наївний Байєс залишається хорошим базовим варіантом завдяки своїй швидкості та простоті.

Приклад -- Наївний Байєс для виявлення фішингу: Щоб продемонструвати Наївного Байєса, ми використаємо Гаусів Наївний Байєс на наборі даних про вторгнення NSL-KDD (з бінарними мітками). Гаусів НБ буде розглядати кожну ознаку як таку, що слідує нормальному розподілу для кожного класу. Це грубий вибір, оскільки багато мережевих ознак є дискретними або сильно перекошеними, але це показує, як можна застосувати НБ до даних безперервних ознак. Ми також могли б вибрати Бернуллі НБ на наборі даних бінарних ознак (як набір спрацьованих сповіщень), але ми залишимося з NSL-KDD тут для узгодженості.
python
import pandas as pd
from sklearn.naive_bayes import GaussianNB
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, roc_auc_score

# 1. Load NSL-KDD data
col_names = [                       # 41 features + 2 targets
"duration","protocol_type","service","flag","src_bytes","dst_bytes","land",
"wrong_fragment","urgent","hot","num_failed_logins","logged_in",
"num_compromised","root_shell","su_attempted","num_root","num_file_creations",
"num_shells","num_access_files","num_outbound_cmds","is_host_login",
"is_guest_login","count","srv_count","serror_rate","srv_serror_rate",
"rerror_rate","srv_rerror_rate","same_srv_rate","diff_srv_rate",
"srv_diff_host_rate","dst_host_count","dst_host_srv_count",
"dst_host_same_srv_rate","dst_host_diff_srv_rate",
"dst_host_same_src_port_rate","dst_host_srv_diff_host_rate",
"dst_host_serror_rate","dst_host_srv_serror_rate","dst_host_rerror_rate",
"dst_host_srv_rerror_rate","class","difficulty_level"
]

train_url = "https://raw.githubusercontent.com/Mamcose/NSL-KDD-Network-Intrusion-Detection/master/NSL_KDD_Train.csv"
test_url  = "https://raw.githubusercontent.com/Mamcose/NSL-KDD-Network-Intrusion-Detection/master/NSL_KDD_Test.csv"

df_train = pd.read_csv(train_url, header=None, names=col_names)
df_test  = pd.read_csv(test_url,  header=None, names=col_names)

# 2. Preprocess (encode categorical features, prepare binary labels)
from sklearn.preprocessing import LabelEncoder
for col in ['protocol_type', 'service', 'flag']:
le = LabelEncoder()
le.fit(pd.concat([df_train[col], df_test[col]], axis=0))
df_train[col] = le.transform(df_train[col])
df_test[col]  = le.transform(df_test[col])
X_train = df_train.drop(columns=['class', 'difficulty_level'], errors='ignore')
y_train = df_train['class'].apply(lambda x: 0 if x.strip().lower() == 'normal' else 1)
X_test  = df_test.drop(columns=['class', 'difficulty_level'], errors='ignore')
y_test  = df_test['class'].apply(lambda x: 0 if x.strip().lower() == 'normal' else 1)

# 3. Train Gaussian Naive Bayes
model = GaussianNB()
model.fit(X_train, y_train)

# 4. Evaluate on test set
y_pred = model.predict(X_test)
# For ROC AUC, need probability of class 1:
y_prob = model.predict_proba(X_test)[:, 1] if hasattr(model, "predict_proba") else y_pred
print(f"Accuracy:  {accuracy_score(y_test, y_pred):.3f}")
print(f"Precision: {precision_score(y_test, y_pred):.3f}")
print(f"Recall:    {recall_score(y_test, y_pred):.3f}")
print(f"F1-score:  {f1_score(y_test, y_pred):.3f}")
print(f"ROC AUC:   {roc_auc_score(y_test, y_prob):.3f}")

"""
Accuracy:  0.450
Precision: 0.937
Recall:    0.037
F1-score:  0.071
ROC AUC:   0.867
"""

Цей код навчає класифікатор Naive Bayes для виявлення атак. Naive Bayes обчислює такі речі, як P(service=http | Attack) та P(Service=http | Normal) на основі навчальних даних, припускаючи незалежність між ознаками. Потім він використовує ці ймовірності для класифікації нових з'єднань як нормальних або атакуючих на основі спостережуваних ознак. Продуктивність NB на NSL-KDD може бути не такою високою, як у більш просунутих моделей (оскільки незалежність ознак порушується), але вона часто є прийнятною і має перевагу в надзвичайній швидкості. У таких сценаріях, як фільтрація електронної пошти в реальному часі або початкова тріаж URL-адрес, модель Naive Bayes може швидко виявляти очевидно шкідливі випадки з низьким використанням ресурсів.

k-Найближчі сусіди (k-NN)

k-Найближчі сусіди є одним з найпростіших алгоритмів машинного навчання. Це непараметричний, заснований на екземплярах метод, який робить прогнози на основі схожості з прикладами в навчальному наборі. Ідея для класифікації полягає в тому, щоб класифікувати нову точку даних, знайти k найближчих точок у навчальних даних (її "найближчі сусіди") і призначити клас більшості серед цих сусідів. "Близькість" визначається за допомогою метрики відстані, зазвичай евклідової відстані для числових даних (інші відстані можуть використовуватися для різних типів ознак або проблем).

K-NN не вимагає явного навчання -- фаза "навчання" полягає лише в зберіганні набору даних. Уся робота відбувається під час запиту (прогнозування): алгоритм повинен обчислити відстані від точки запиту до всіх навчальних точок, щоб знайти найближчі. Це робить час прогнозування лінійним щодо кількості навчальних зразків, що може бути витратним для великих наборів даних. Через це k-NN найкраще підходить для менших наборів даних або сценаріїв, де ви можете обміняти пам'ять і швидкість на простоту.

Незважаючи на свою простоту, k-NN може моделювати дуже складні межі рішень (оскільки фактично межа рішень може мати будь-яку форму, визначену розподілом прикладів). Він, як правило, добре працює, коли межа рішень дуже нерегулярна і у вас багато даних -- по суті, дозволяючи даним "говорити за себе". Однак у високих вимірах метрики відстані можуть ставати менш значущими (прокляття вимірності), і метод може мати труднощі, якщо у вас немає великої кількості зразків.

Використання в кібербезпеці: k-NN був застосований для виявлення аномалій -- наприклад, система виявлення вторгнень може позначити мережеву подію як шкідливу, якщо більшість її найближчих сусідів (попередні події) були шкідливими. Якщо нормальний трафік формує кластери, а атаки є викидами, підхід K-NN (з k=1 або малим k) фактично виконує виявлення аномалій найближчих сусідів. K-NN також використовувався для класифікації сімей шкідливих програм за допомогою бінарних векторів ознак: новий файл може бути класифікований як певна сім'я шкідливих програм, якщо він дуже близький (в просторі ознак) до відомих екземплярів цієї сім'ї. На практиці k-NN не є таким поширеним, як більш масштабовані алгоритми, але він концептуально простий і іноді використовується як базовий або для маломасштабних проблем.

Ключові характеристики k-NN:

  • Тип проблеми: Класифікація (існують варіанти регресії). Це ліниве навчання -- без явного підбору моделі.

  • Інтерпретованість: Низька до середньої -- немає глобальної моделі або стислого пояснення, але можна інтерпретувати результати, дивлячись на найближчих сусідів, які вплинули на рішення (наприклад, "цей мережевий потік був класифікований як шкідливий, оскільки він схожий на ці 3 відомі шкідливі потоки"). Отже, пояснення можуть бути засновані на прикладах.

  • Переваги: Дуже просто реалізувати і зрозуміти. Не робить припущень щодо розподілу даних (непараметричний). Може природно обробляти багатокласові проблеми. Це адаптивно в тому сенсі, що межі рішень можуть бути дуже складними, сформованими розподілом даних.

  • Обмеження: Прогнозування може бути повільним для великих наборів даних (необхідно обчислити багато відстаней). Витратний за пам'яттю -- зберігає всі навчальні дані. Продуктивність погіршується в просторах ознак з високою вимірністю, оскільки всі точки, як правило, стають майже рівновіддаленими (що робить концепцію "найближчого" менш значущою). Потрібно правильно вибрати k (кількість сусідів) -- занадто мале k може бути шумним, занадто велике k може включати нерелевантні точки з інших класів. Також ознаки повинні бути масштабовані відповідно, оскільки обчислення відстані чутливі до масштабу.

Приклад -- k-NN для виявлення фішингу:

Ми знову використаємо NSL-KDD (бінарна класифікація). Оскільки k-NN є обчислювально важким, ми використаємо підмножину навчальних даних, щоб зберегти його керованим у цій демонстрації. Ми виберемо, скажімо, 20,000 навчальних зразків з повних 125k і використаємо k=5 сусідів. Після навчання (насправді просто зберігання даних) ми оцінимо на тестовому наборі. Ми також масштабуватимемо ознаки для обчислення відстані, щоб забезпечити, що жодна окрема ознака не домінує через масштаб.

python
import pandas as pd
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, roc_auc_score

# 1. Load NSL-KDD and preprocess similarly
col_names = [                       # 41 features + 2 targets
"duration","protocol_type","service","flag","src_bytes","dst_bytes","land",
"wrong_fragment","urgent","hot","num_failed_logins","logged_in",
"num_compromised","root_shell","su_attempted","num_root","num_file_creations",
"num_shells","num_access_files","num_outbound_cmds","is_host_login",
"is_guest_login","count","srv_count","serror_rate","srv_serror_rate",
"rerror_rate","srv_rerror_rate","same_srv_rate","diff_srv_rate",
"srv_diff_host_rate","dst_host_count","dst_host_srv_count",
"dst_host_same_srv_rate","dst_host_diff_srv_rate",
"dst_host_same_src_port_rate","dst_host_srv_diff_host_rate",
"dst_host_serror_rate","dst_host_srv_serror_rate","dst_host_rerror_rate",
"dst_host_srv_rerror_rate","class","difficulty_level"
]

train_url = "https://raw.githubusercontent.com/Mamcose/NSL-KDD-Network-Intrusion-Detection/master/NSL_KDD_Train.csv"
test_url  = "https://raw.githubusercontent.com/Mamcose/NSL-KDD-Network-Intrusion-Detection/master/NSL_KDD_Test.csv"

df_train = pd.read_csv(train_url, header=None, names=col_names)
df_test  = pd.read_csv(test_url,  header=None, names=col_names)

from sklearn.preprocessing import LabelEncoder
for col in ['protocol_type', 'service', 'flag']:
le = LabelEncoder()
le.fit(pd.concat([df_train[col], df_test[col]], axis=0))
df_train[col] = le.transform(df_train[col])
df_test[col]  = le.transform(df_test[col])
X = df_train.drop(columns=['class', 'difficulty_level'], errors='ignore')
y = df_train['class'].apply(lambda x: 0 if x.strip().lower() == 'normal' else 1)
# Use a random subset of the training data for K-NN (to reduce computation)
X_train = X.sample(n=20000, random_state=42)
y_train = y[X_train.index]
# Use the full test set for evaluation
X_test = df_test.drop(columns=['class', 'difficulty_level'], errors='ignore')
y_test = df_test['class'].apply(lambda x: 0 if x.strip().lower() == 'normal' else 1)

# 2. Feature scaling for distance-based model
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test  = scaler.transform(X_test)

# 3. Train k-NN classifier (store data)
model = KNeighborsClassifier(n_neighbors=5, n_jobs=-1)
model.fit(X_train, y_train)

# 4. Evaluate on test set
y_pred = model.predict(X_test)
y_prob = model.predict_proba(X_test)[:, 1]
print(f"Accuracy:  {accuracy_score(y_test, y_pred):.3f}")
print(f"Precision: {precision_score(y_test, y_pred):.3f}")
print(f"Recall:    {recall_score(y_test, y_pred):.3f}")
print(f"F1-score:  {f1_score(y_test, y_pred):.3f}")
print(f"ROC AUC:   {roc_auc_score(y_test, y_prob):.3f}")

"""
Accuracy:  0.780
Precision: 0.972
Recall:    0.632
F1-score:  0.766
ROC AUC:   0.837
"""

Модель k-NN класифікує з'єднання, аналізуючи 5 найближчих з'єднань у підмножині навчального набору. Якщо, наприклад, 4 з цих сусідів є атаками (аномаліями), а 1 - нормальним, нове з'єднання буде класифіковане як атака. Продуктивність може бути розумною, хоча часто не такою високою, як у добре налаштованого Random Forest або SVM на тих же даних. Однак k-NN іноді може проявити себе, коли розподіли класів дуже нерегулярні та складні — ефективно використовуючи пам'яті для пошуку. У кібербезпеці k-NN (з k=1 або малим k) може бути використаний для виявлення відомих патернів атак за прикладом або як компонент у більш складних системах (наприклад, для кластеризації, а потім класифікації на основі членства в кластері).

Градієнтні підсилювальні машини (наприклад, XGBoost)

Градієнтні підсилювальні машини є одними з найпотужніших алгоритмів для структурованих даних. Градієнтне підсилення відноситься до техніки побудови ансамблю слабких навчальних моделей (часто дерев рішень) послідовно, де кожна нова модель виправляє помилки попереднього ансамблю. На відміну від бэггінгу (Random Forest), який будує дерева паралельно та усереднює їх, підсилення будує дерева одне за одним, кожне зосереджуючись більше на випадках, які попередні дерева неправильно передбачили.

Найпопулярніші реалізації в останні роки — це XGBoost, LightGBM та CatBoost, всі з яких є бібліотеками градієнтного підсилення дерев рішень (GBDT). Вони мали надзвичайний успіх у змаганнях з машинного навчання та застосуваннях, часто досягаючи найсучаснішої продуктивності на табличних наборах даних. У кібербезпеці дослідники та практики використовували градієнтно підсилені дерева для завдань, таких як виявлення шкідливого ПЗ (використовуючи ознаки, витягнуті з файлів або поведінки під час виконання) та виявлення мережевих вторгнень. Наприклад, модель градієнтного підсилення може поєднувати багато слабких правил (дерев), таких як "якщо багато SYN-пакетів і незвичайний порт -> ймовірно, сканування", в потужний композитний детектор, який враховує багато тонких патернів.

Чому підсилені дерева такі ефективні? Кожне дерево в послідовності навчається на залишкових помилках (градієнтах) прогнозів поточного ансамблю. Таким чином, модель поступово "підсилює" області, де вона слабка. Використання дерев рішень як базових навчальних моделей означає, що фінальна модель може захоплювати складні взаємодії та нелінійні зв'язки. Крім того, підсилення в основному має форму вбудованої регуляризації: додаючи багато малих дерев (і використовуючи швидкість навчання для масштабування їх внесків), воно часто добре узагальнює без великого перенавчання, за умови, що вибрані правильні параметри.

Ключові характеристики градієнтного підсилення:

  • Тип проблеми: Переважно класифікація та регресія. У безпеці зазвичай класифікація (наприклад, бінарна класифікація з'єднання або файлу). Він обробляє бінарні, багатокласові (з відповідною втратою) та навіть ранжування.

  • Інтерпретованість: Низька до середньої. Хоча одне підсилене дерево є малим, повна модель може мати сотні дерев, що не є зрозумілим для людини в цілому. Однак, як і Random Forest, вона може надавати оцінки важливості ознак, а інструменти, такі як SHAP (SHapley Additive exPlanations), можуть бути використані для інтерпретації окремих прогнозів до певної міри.

  • Переваги: Часто є найкращим алгоритмом для структурованих/табличних даних. Може виявляти складні патерни та взаємодії. Має багато параметрів налаштування (кількість дерев, глибина дерев, швидкість навчання, терміни регуляризації), щоб налаштувати складність моделі та запобігти перенавчанню. Сучасні реалізації оптимізовані для швидкості (наприклад, XGBoost використовує інформацію другого порядку градієнта та ефективні структури даних). Як правило, краще обробляє незбалансовані дані, коли поєднується з відповідними функціями втрат або шляхом коригування ваг зразків.

  • Обмеження: Складніше налаштувати, ніж простіші моделі; навчання може бути повільним, якщо дерева глибокі або кількість дерев велика (хоча все ще зазвичай швидше, ніж навчання порівнянної глибокої нейронної мережі на тих же даних). Модель може перенавчитися, якщо не налаштована (наприклад, занадто багато глибоких дерев з недостатньою регуляризацією). Через велику кількість гіперпараметрів ефективне використання градієнтного підсилення може вимагати більше експертизи або експериментів. Також, як і методи на основі дерев, він не обробляє дуже розріджені високорозмірні дані так ефективно, як лінійні моделі або Naive Bayes (хоча його все ще можна застосовувати, наприклад, у класифікації тексту, але може не бути першим вибором без інженерії ознак).

tip

Випадки використання в кібербезпеці: Практично скрізь, де можна використовувати дерево рішень або випадковий ліс, модель градієнтного підсилення може досягти кращої точності. Наприклад, змагання з виявлення шкідливого ПЗ від Microsoft активно використовували XGBoost на інженерних ознаках з бінарних файлів. Дослідження виявлення мережевих вторгнень часто повідомляють про найкращі результати з GBDT (наприклад, XGBoost на наборах даних CIC-IDS2017 або UNSW-NB15). Ці моделі можуть приймати широкий спектр ознак (типи протоколів, частота певних подій, статистичні ознаки трафіку тощо) і поєднувати їх для виявлення загроз. У виявленні фішингу градієнтне підсилення може поєднувати лексичні ознаки URL, ознаки репутації доменів та ознаки вмісту сторінок для досягнення дуже високої точності. Ансамблевий підхід допомагає охопити багато крайніх випадків і тонкощів у даних.

Приклад -- XGBoost для виявлення фішингу: Ми використаємо класифікатор градієнтного підсилення на наборі даних про фішинг. Щоб зберегти все простим і самодостатнім, ми використаємо `sklearn.ensemble.GradientBoostingClassifier` (який є повільнішою, але простішою реалізацією). Зазвичай можна використовувати бібліотеки `xgboost` або `lightgbm` для кращої продуктивності та додаткових функцій. Ми навчимо модель і оцінимо її подібно до попереднього разу.
python
import pandas as pd
from sklearn.datasets import fetch_openml
from sklearn.model_selection import train_test_split
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, roc_auc_score

# 1️⃣ Load the “Phishing Websites” data directly from OpenML
data = fetch_openml(data_id=4534, as_frame=True)   # or data_name="PhishingWebsites"
df   = data.frame

# 2️⃣ Separate features/target & make sure everything is numeric
X = df.drop(columns=["Result"])
y = df["Result"].astype(int).apply(lambda v: 1 if v == 1 else 0)  # map {-1,1} → {0,1}

# (If any column is still object‑typed, coerce it to numeric.)
X = X.apply(pd.to_numeric, errors="coerce").fillna(0)

# 3️⃣ Train/test split
X_train, X_test, y_train, y_test = train_test_split(
X.values, y, test_size=0.20, random_state=42
)

# 4️⃣ Gradient Boosting model
model = GradientBoostingClassifier(
n_estimators=100, learning_rate=0.1, max_depth=3, random_state=42
)
model.fit(X_train, y_train)

# 5️⃣ Evaluation
y_pred = model.predict(X_test)
y_prob = model.predict_proba(X_test)[:, 1]

print(f"Accuracy:  {accuracy_score(y_test, y_pred):.3f}")
print(f"Precision: {precision_score(y_test, y_pred):.3f}")
print(f"Recall:    {recall_score(y_test, y_pred):.3f}")
print(f"F1‑score:  {f1_score(y_test, y_pred):.3f}")
print(f"ROC AUC:   {roc_auc_score(y_test, y_prob):.3f}")

"""
Accuracy:  0.951
Precision: 0.949
Recall:    0.965
F1‑score:  0.957
ROC AUC:   0.990
"""

Модель градієнтного підсилення, ймовірно, досягне дуже високої точності та AUC на цьому наборі даних про фішинг (часто ці моделі можуть перевищувати 95% точності при належному налаштуванні на таких даних, як показано в літературі. Це демонструє, чому GBDT вважаються "найсучаснішою моделлю для табличних наборів даних" -- вони часто перевершують простіші алгоритми, захоплюючи складні патерни. У контексті кібербезпеки це може означати виявлення більшої кількості фішингових сайтів або атак з меншими пропусками. Звичайно, потрібно бути обережним щодо перенавчання -- зазвичай ми використовуємо такі техніки, як крос-валідація, і контролюємо продуктивність на валідаційному наборі при розробці такої моделі для впровадження.

Комбінування моделей: ансамблеве навчання та стекінг

Ансамблеве навчання є стратегією комбінування кількох моделей для покращення загальної продуктивності. Ми вже бачили конкретні методи ансамблів: Random Forest (ансамбль дерев через бэггінг) та Gradient Boosting (ансамбль дерев через послідовне підсилення). Але ансамблі можна створювати й іншими способами, такими як голосуючі ансамблі або стекінг (stacking). Основна ідея полягає в тому, що різні моделі можуть захоплювати різні патерни або мати різні слабкості; комбінуючи їх, ми можемо компенсувати помилки кожної моделі силами іншої.

  • Голосуючий ансамбль: У простому голосуючому класифікаторі ми навчаємо кілька різноманітних моделей (скажімо, логістичну регресію, дерево рішень і SVM) і змушуємо їх голосувати за фінальне передбачення (більшість голосів для класифікації). Якщо ми зважуємо голоси (наприклад, надаємо вищу вагу більш точним моделям), це зважена схема голосування. Це зазвичай покращує продуктивність, коли окремі моделі є досить хорошими та незалежними -- ансамбль зменшує ризик помилки окремої моделі, оскільки інші можуть її виправити. Це як мати панель експертів, а не одну думку.

  • Стекінг (Stacked Ensemble): Стекінг йде ще далі. Замість простого голосування він навчає мета-модель вчитися, як найкраще комбінувати прогнози базових моделей. Наприклад, ви навчаєте 3 різні класифікатори (базові навчальні моделі), а потім передаєте їх виходи (або ймовірності) як ознаки в мета-класифікатор (часто просту модель, таку як логістична регресія), яка вчиться оптимальному способу їх змішування. Мета-модель навчається на валідаційному наборі або через крос-валідацію, щоб уникнути перенавчання. Стекінг часто може перевершити просте голосування, вивчаючи які моделі більше довіряти в яких обставинах. У кібербезпеці одна модель може бути кращою в виявленні мережевих сканувань, тоді як інша краще виявляє шкідливе програмне забезпечення; модель стекінгу може навчитися покладатися на кожну відповідно.

Ансамблі, чи то через голосування, чи через стекінг, зазвичай підвищують точність і надійність. Недоліком є підвищена складність і іноді знижена інтерпретованість (хоча деякі підходи ансамблів, такі як середнє значення дерев рішень, все ще можуть надати певне уявлення, наприклад, важливість ознак). На практиці, якщо операційні обмеження дозволяють, використання ансамблю може призвести до вищих показників виявлення. Багато виграшних рішень у викликах з кібербезпеки (і в конкурсах Kaggle загалом) використовують ансамблеві техніки, щоб витиснути останню частину продуктивності.

Приклад -- Голосуючий ансамбль для виявлення фішингу: Щоб проілюструвати стекінг моделей, давайте об'єднаємо кілька моделей, які ми обговорювали на наборі даних про фішинг. Ми використаємо логістичну регресію, дерево рішень і k-NN як базові навчальні моделі, а Random Forest як мета-навчальника для агрегування їхніх прогнозів. Мета-навчальник буде навчатися на виходах базових навчальних моделей (використовуючи крос-валідацію на навчальному наборі). Ми очікуємо, що стекінгова модель буде працювати так само добре або трохи краще, ніж окремі моделі.
python
import pandas as pd
from sklearn.datasets import fetch_openml
from sklearn.model_selection import train_test_split
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.ensemble import StackingClassifier, RandomForestClassifier
from sklearn.metrics import (accuracy_score, precision_score,
recall_score, f1_score, roc_auc_score)

# ──────────────────────────────────────────────
# 1️⃣  LOAD DATASET (OpenML id 4534)
# ──────────────────────────────────────────────
data = fetch_openml(data_id=4534, as_frame=True)     # “PhishingWebsites”
df   = data.frame

# Target mapping:  1 → legitimate (0),   0/‑1 → phishing (1)
y = (df["Result"].astype(int) != 1).astype(int)
X = df.drop(columns=["Result"])

# Train / test split (stratified to keep class balance)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.20, random_state=42, stratify=y)

# ──────────────────────────────────────────────
# 2️⃣  DEFINE BASE LEARNERS
#     • LogisticRegression and k‑NN need scaling ➜ wrap them
#       in a Pipeline(StandardScaler → model) so that scaling
#       happens inside each CV fold of StackingClassifier.
# ──────────────────────────────────────────────
base_learners = [
('lr',  make_pipeline(StandardScaler(),
LogisticRegression(max_iter=1000,
solver='lbfgs',
random_state=42))),
('dt',  DecisionTreeClassifier(max_depth=5, random_state=42)),
('knn', make_pipeline(StandardScaler(),
KNeighborsClassifier(n_neighbors=5)))
]

# Meta‑learner (level‑2 model)
meta_learner = RandomForestClassifier(n_estimators=50, random_state=42)

stack_model = StackingClassifier(
estimators      = base_learners,
final_estimator = meta_learner,
cv              = 5,        # 5‑fold CV to create meta‑features
passthrough     = False     # only base learners’ predictions go to meta‑learner
)

# ──────────────────────────────────────────────
# 3️⃣  TRAIN ENSEMBLE
# ──────────────────────────────────────────────
stack_model.fit(X_train, y_train)

# ──────────────────────────────────────────────
# 4️⃣  EVALUATE
# ──────────────────────────────────────────────
y_pred = stack_model.predict(X_test)
y_prob = stack_model.predict_proba(X_test)[:, 1]   # P(phishing)

print(f"Accuracy : {accuracy_score(y_test, y_pred):.3f}")
print(f"Precision: {precision_score(y_test, y_pred):.3f}")
print(f"Recall   : {recall_score(y_test, y_pred):.3f}")
print(f"F1‑score : {f1_score(y_test, y_pred):.3f}")
print(f"ROC AUC  : {roc_auc_score(y_test, y_prob):.3f}")

"""
Accuracy : 0.954
Precision: 0.951
Recall   : 0.946
F1‑score : 0.948
ROC AUC  : 0.992
"""

Складений ансамбль використовує комплементарні сильні сторони базових моделей. Наприклад, логістична регресія може обробляти лінійні аспекти даних, дерево рішень може захоплювати специфічні взаємодії у вигляді правил, а k-NN може досягати успіху в локальних сусідствах простору ознак. Мета-модель (в даному випадку випадковий ліс) може навчитися, як зважувати ці вхідні дані. Отримані метрики часто показують покращення (навіть якщо незначне) в порівнянні з метриками будь-якої окремої моделі. У нашому прикладі фішингу, якщо логістична регресія сама мала F1, скажімо, 0.95, а дерево 0.94, то ансамбль може досягти 0.96, виправляючи помилки кожної моделі.

Методи ансамблів, такі як цей, демонструють принцип, що "поєднання кількох моделей зазвичай призводить до кращої узагальненості". У кібербезпеці це можна реалізувати, маючи кілька двигунів виявлення (один може бути на основі правил, один - на основі машинного навчання, один - на основі аномалій), а потім шар, який агрегує їхні сповіщення - фактично форма ансамблю - для прийняття остаточного рішення з більшою впевненістю. При розгортанні таких систем слід враховувати додаткову складність і забезпечити, щоб ансамбль не став занадто важким для управління або пояснення. Але з точки зору точності, ансамблі та стекінг є потужними інструментами для покращення продуктивності моделі.

References

tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Вивчайте та практикуйте Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Підтримайте HackTricks