Вопрос или проблема
Я работал над проектом по обнаружению фишинга в качестве учебного упражнения. После очистки данных, создания новых признаков, масштабирования не бинарных и обучения модели случайного леса, я достиг F1-метрики 0.999 на тестовом наборе. Учитывая высокий показатель, я обеспокоен тем, что моя модель может переобучаться, хотя я предпринял шаги для снижения переобучения, такие как удаление сильно коррелирующих признаков (например, я оставил только один из URLLength и NoOfLettersInURL из-за коэффициента корреляции Персона > 0.9) и балансировка набора данных (57% помечено как 0, 43% помечено как 1).
В соответствии с требованиями упражнения я исключил конкретные признаки (URLSimilarityIndex, CharContinuationRate, URLTitleMatchScore, URLCharProb и TLDLegitimateProb) и использовал StandardScaler для не бинарных колонок. Я также выполнил дополнительное создание признаков, включая расчет соотношений, таких как self_reference_ratio и энтропия для URL.
Вот мой главный вопрос:
Существует ли способ подтвердить, отражает ли высокий F1 показатель фактическую производительность или потенциальное переобучение, какие решения вы предлагаете (Внесите изменения в алгоритм? В набор данных?)?
Спасибо!
Вот ссылка на набор данных: https://archive.ics.uci.edu/dataset/967/phiusiil+phishing+url+dataset
Вот код:
import pandas as pd
import math
from collections import Counter
import numpy as np
import re
from scipy.stats import pointbiserialr
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import f1_score, accuracy_score
from sklearn.utils import shuffle
from xgboost import XGBClassifier
data = pd.read_csv('PhiUSIIL_Phishing_URL_Dataset.csv')
data.drop(columns=['URLSimilarityIndex', 'CharContinuationRate', 'URLTitleMatchScore', 'URLCharProb', 'TLDLegitimateProb'], inplace = True)
data1 = data.copy()
def self_reference_ratio(row):
total_refs = row['NoOfSelfRef'] + row['NoOfExternalRef']
return row['NoOfSelfRef'] / total_refs if total_refs != 0 else 0
def external_reference_ratio(row):
total_refs = row['NoOfSelfRef'] + row['NoOfExternalRef']
return row['NoOfExternalRef'] / total_refs if total_refs != 0 else 0
def image_to_resource_ratio(row):
total_resources = row['NoOfImage'] + row['NoOfCSS'] + row['NoOfJS']
return row['NoOfImage'] / total_resources if total_resources != 0 else 0
def calculate_entropy(url):
url_part = url.split('://')[-1]
char_count = Counter(url_part)
length = len(url_part)
entropy = -sum((freq / length) * math.log2(freq / length) for freq in char_count.values())
return entropy
def calculate_js_usage_ratio(row):
return row['NoOfJS'] / row['LineOfCode'] if row['LineOfCode'] != 0 else 0
def detect_non_standard_port(url):
match = re.search(r":(\d+)", url)
if match:
port = int(match.group(1))
return 1 if port not in [80, 443] else 0
return 0
def detect_prefix_suffix(domain):
return 1 if '-' in domain else 0
data1['non_std_port'] = data1['URL'].apply(detect_non_standard_port)
data1['prefix_suffix'] = data1['Domain'].apply(detect_prefix_suffix)
data1[['non_std_port', 'prefix_suffix']].head()
data1['self_reference_ratio'] = data1.apply(self_reference_ratio, axis=1)
data1['external_reference_ratio'] = data1.apply(external_reference_ratio, axis=1)
data1['image_to_resource_ratio'] = data1.apply(image_to_resource_ratio, axis=1)
data1['entropy'] = data1['URL'].apply(calculate_entropy)
data1['js_usage_ratio'] = data1.apply(calculate_js_usage_ratio, axis=1)
data1.drop(columns = ['FILENAME','URL','Domain','TLD','Title'], inplace = True)
binary_columns = [col for col in data1.columns if data1[col].nunique() == 2]
non_binary_columns = [col for col in data1.columns if col not in binary_columns + ['label']]
scaler = StandardScaler()
data1[non_binary_columns] = scaler.fit_transform(data1[non_binary_columns])
data1 = shuffle(data1, random_state=42)
pd.set_option('display.max_columns', None)
X = data1.drop(columns=['label'])
y = data1['label']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
model = RandomForestClassifier(random_state=42)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
f1 = f1_score(y_test, y_pred)
print("F1 Score:", f1)
Ответ или решение
Достижение очень высокого значения F1 (0.999) после обучения модели на основе случайного леса (Random Forest) может действительно вызывать опасения по поводу переобучения модели. Давайте разберемся, что это может означать, и какие шаги можно предпринять для подтверждения или опровержения подозрений о переобучении.
1. Понимание F1-метрики и ее значения
F1-метрика является композитной метрикой, которая объединяет точность (precision) и полноту (recall). При высоком значении F1-метрики полезно удостовериться, что модель действительно адекватно распознает положительные классы, а не просто демонстрирует высокие значения из-за несбалансированных классов или других причин. В вашем случае, распределение классов между 57% и 43% достаточно близкое, что уменьшает риск значительного искажения метрики.
2. Причины, по которым может возникнуть переобучение
Заподозрить переобучение стоит в следующих случаях:
- Слишком высокие значения показателя на тестовом наборе, особенно если во время обучения была использована значительная часть исходного набора данных.
- Различие в производительности модели на валидационном наборе и тестовом наборе.
- Использование незащищённых (непрерывных) признаков, которые могут привести к загрублению.
3. Проверка факта переобучения
Чтобы подтвердить или опровергнуть наличие переобучения, рекомендуется:
-
Разделение данных на обучающий, валидационный и тестовый наборы. Использовать валидационный набор (например, через кросс-валидацию) для проверки, как модель ведет себя на незнакомых данных, что поможет избежать случайных случайностей, связанных с разделением на тренировочную и тестовую выборки.
-
Изменить гиперпараметры модели. Попробуйте уменьшить количество деревьев в случайном лесу, увеличить минимальное количество образцов, необходимых для разделения узла и прочие параметры, которые могут помочь в борьбе с переобучением.
-
Визуализация ошибок классификатора. Посмотреть, примерные ошибки на тестовом наборе, чтобы определить, есть ли у модели систематические ошибки.
4. Способы предотвращения переобучения
Существует несколько методов для уменьшения риска переобучения:
-
Регуляризация: Методы, такие как обрезка деревьев (tree pruning), использование ограничения глубины деревьев или минимального количества листьев, могут помочь лучше обобщать данные.
-
Увеличение объема данных: Применение техник аугментации данных, таких как добавление шума, может повысить обобщающую способность модели.
-
Снижение размерности: Устранение многовариантных признаков или применение методов отбора признаков, таких как LASSO, может сократить переобучение.
-
Применение ансамблевых методов: Использование методов бустинга (например, XGBoost) может дать преимущества по сравнению с использованием случайного леса, благодаря их способности улучшать точность модели.
5. Заключение
Достичь F1-метрики 0.999 действительно впечатляюще, однако важно удостовериться, что это значение является результатом эффективной работы модели, а не следствием переобучения. Применение предложенных методов проверки и коррекции поможет объективно оценить модель и улучшить ее надежность для использования в реальных условиях.