iscc2026---网络安全事件智能分类赛题writeup
📋 任务背景
随着数字化转型的不断深入,企业网络规模不断扩大,各类业务系统、物联网设备以及云服务大量接入网络环境,使得网络流量规模呈指数级增长。在复杂的网络环境中,大量正常业务流量与潜在的恶意行为混合在一起,例如扫描行为、暴力破解、僵尸网络通信以及拒绝服务攻击等。不同攻击行为之间往往存在一定的特征重叠,同时网络环境也会随着时间发生变化。因此,如何从复杂的网络流量数据中准确识别不同类型的安全事件,是网络安全领域中的重要问题。
竞赛聚焦于基于网络安全事件智能分类,参赛者需要利用机器学习或深度学习方法,对网络流量样本进行分析,并准确识别其所属的安全事件类别。竞赛旨在推动数据驱动的网络安全分析技术发展,探索更加高效、智能的网络安全事件检测方法。竞赛数据通过对原始流量数据进行特征提取与匿名化处理,形成网络流量统计特征数据。
任务目标
基于网络流量统计特征数据,构建多分类模型,准确识别12类网络安全事件(class_0 ~ class_11)。
数据规模
| 数据集 | 样本数 | 特征维度 |
|---|---|---|
| 训练集 | 53,477 | 50维匿名化流量特征 |
| 测试集 | 19,440 | 50维匿名化流量特征 |
评估指标
- 宏平均F1分数(Macro F1):对所有类别平等对待,要求模型在每个类别上都有良好表现
核心挑战
- 50维匿名化特征,无法利用领域知识
- 训练集与测试集存在显著分布偏移(对抗验证AUC≈0.87)
- 类别不完全平衡(样本数从2,665到5,576不等)
🧠 整体技术路线
本方案采用 “海量特征工程 + 高置信度伪标签 + 多模型集成 + 无泄漏阈值优化” 的四阶段策略。核心思想是:先通过特征工程极大地扩充信息量,再利用伪标签让模型间接学习测试集分布,最后通过多模型融合与无泄漏的阈值优化最大化宏平均F1。
原始特征 (50维)
↓ 统计特征 + 无监督特征 + 交互特征
增强特征 (500+维)
↓ 互信息特征选择
精选特征 (80维)
↓ 第一阶段:5折CV + 极高置信度伪标签
扩充训练集 (原始 + 伪标签)
↓ 第二阶段:5种模型融合
OOF + 测试集预测
↓ 无泄漏阈值优化(仅用原始训练集OOF)
最终提交
🧬 特征工程:从50维到500+维的蜕变
特征工程是本次方案最核心的驱动力,分为三个层次。
1. 统计特征(7维)
对每行样本计算跨特征的统计量,捕获样本的整体分布特性。
| 特征 | 含义 |
|---|---|
sum_features |
所有特征值的总和 |
mean_features |
所有特征值的均值 |
std_features |
所有特征值的标准差 |
max_features |
所有特征值的最大值 |
min_features |
所有特征值的最小值 |
skew_features |
特征间的偏度 |
kurt_features |
特征间的峰度 |
df_out['sum_features'] = df.sum(axis=1)
df_out['mean_features'] = df.mean(axis=1)
df_out['std_features'] = df.std(axis=1)
df_out['skew_features'] = df.skew(axis=1)
df_out['kurt_features'] = df.kurtosis(axis=1)
2. 无监督特征(20维)
通过聚类和降维方法,从全局视角挖掘数据的内在结构。
- PCA降维(5维):提取全局主成分,捕获线性结构
- KMeans聚类距离(15维):将样本映射到15个聚类中心,计算到每个中心的距离作为新特征
pca = PCA(n_components=5, random_state=42)
kmeans = KMeans(n_clusters=15, random_state=42, n_init=10)
3. 高阶交互特征(420维)
基于互信息分析选出的15个最关键特征,两两之间生成四则运算特征:
- 加法特征 (
A + B):捕获协同关系 - 减法特征 (
A - B):捕获相对差异 - 乘法特征 (
A × B):捕获放大效应 - 除法特征 (
A ÷ B):捕获比例关系
组合数=C152×4=105×4=420组合数 = C_{15}^2 \times 4 = 105 \times 4 = 420组合数=C152×4=105×4=420
X[f'{col1}_plus_{col2}'] = X[col1] + X[col2]
X[f'{col1}_minus_{col2}'] = X[col1] - X[col2]
X[f'{col1}_mult_{col2}'] = X[col1] * X[col2]
X[f'{col1}_div_{col2}'] = X[col1] / (X[col2] + 1e-5)
🎯 特征选择
面对500+维的庞大数据集,使用**互信息(Mutual Information)**评估每个特征与标签的依赖关系,精选Top80特征入模。
选择互信息的优势:
- 能捕获非线性依赖关系
- 不依赖模型假设
- 比皮尔逊相关系数更适合复杂匿名数据
selector = SelectKBest(mutual_info_classif, k=80)
X_selected = selector.fit_transform(X_scaled, y_encoded)
🏗️ 模型训练与优化策略
LightGBM 参数配置
lgb_params = {
'objective': 'multiclass',
'num_class': 12,
'metric': 'multi_logloss',
'boosting_type': 'gbdt',
'num_leaves': 63,
'max_depth': 8,
'learning_rate': 0.05,
'min_child_samples': 50,
'subsample': 0.8,
'colsample_bytree': 0.8,
'reg_alpha': 0.15,
'reg_lambda': 0.15,
'is_unbalance': True
}
第一阶段:高置信度伪标签
采用5折交叉验证对测试集进行预测,取平均概率。仅选择预测概率 > 0.99的样本作为“高置信度伪标签”加入训练集。
CONFIDENCE_THRESHOLD = 0.99
max_probs = np.max(test_preds_stage1, axis=1)
pseudo_indices = np.where(max_probs > CONFIDENCE_THRESHOLD)[0]
设计考量:
- 极高阈值(0.99):确保伪标签的准确性,防止噪声
- 领域自适应:让模型间接学习测试集分布,缓解分布偏移
第二阶段:5-Seed Model Blending
使用5个不同随机种子(42, 77, 888, 2026, 9999)训练模型,将预测结果平均融合。
seeds = [42, 77, 888, 2026, 9999]
blended_test_preds = sum(各种子预测) / len(seeds)
集成优势:
- 不同种子 → 不同采样顺序 → 不同树结构
- 平均多个模型预测 → 降低方差,提升泛化性
⚖️ 无泄漏阈值优化(关键优化点)
问题识别
如果将伪标签数据也用于阈值优化,会导致严重的分数虚高(本地分数虚高,线上无效)。
解决方案
阈值优化过程严格隔离,仅使用原始真实标签训练集的OOF(Out-of-Fold)预测进行搜索。
def optimize_thresholds(y_true, y_pred_proba):
def f1_opt(weights):
weighted_preds = y_pred_proba * weights
pred_labels = np.argmax(weighted_preds, axis=1)
return -f1_score(y_true, pred_labels, average='macro')
init_weights = [1.0] * 12
res = minimize(f1_opt, init_weights, method='Nelder-Mead')
return np.abs(res.x)
# 严格截取:只用原始训练集部分
pure_train_oof = blended_oof_preds[:ORIGINAL_TRAIN_LEN]
best_weights = optimize_thresholds(y_encoded, pure_train_oof)
优化后,每个类别的预测概率乘以最优权重,再取argmax作为最终标签。
📊 实验结果
本地交叉验证性能
| 类别 | Precision | Recall | F1-score |
|---|---|---|---|
| class_0 | 0.87 | 0.91 | 0.89 |
| class_1 | 0.98 | 0.94 | 0.96 |
| class_2 | 0.77 | 0.80 | 0.78 |
| class_3 | 0.83 | 0.73 | 0.78 |
| class_4 | 0.92 | 0.96 | 0.94 |
| class_5 | 0.97 | 0.99 | 0.98 |
| class_6 | 0.98 | 0.98 | 0.98 |
| class_7 | 0.94 | 0.93 | 0.94 |
| class_8 | 0.95 | 0.99 | 0.97 |
| class_9 | 0.95 | 0.92 | 0.93 |
| class_10 | 0.94 | 0.94 | 0.94 |
| class_11 | 1.00 | 0.98 | 0.99 |
- 宏平均F1:0.92
- 加权平均F1:0.93
线上得分
| 方案 | 线上 Macro F1 |
|---|---|
| 最终方案 | 0.71703 |
完整的python代码
import pandas as pd
import numpy as np
import lightgbm as lgb
from sklearn.model_selection import StratifiedKFold
from sklearn.preprocessing import RobustScaler, LabelEncoder
from sklearn.feature_selection import SelectKBest, mutual_info_classif
from sklearn.decomposition import PCA
from sklearn.cluster import KMeans
from sklearn.metrics import f1_score
from scipy.optimize import minimize
import warnings
warnings.filterwarnings('ignore')
print("========== 1-4. 数据加载与神级特征重组 ==========")
train = pd.read_csv('train_data.csv')
test = pd.read_csv('test_data.csv')
feature_cols = [col for col in train.columns if col not in ['id', 'label']]
X = train[feature_cols].astype(float)
y = train['label']
X_test = test[feature_cols].astype(float)
le = LabelEncoder()
y_encoded = le.fit_transform(y)
# 基础统计
def add_stat_features(df):
df_out = df.copy()
df_out['sum_features'] = df.sum(axis=1)
df_out['mean_features'] = df.mean(axis=1)
df_out['std_features'] = df.std(axis=1)
df_out['max_features'] = df.max(axis=1)
df_out['min_features'] = df.min(axis=1)
df_out['skew_features'] = df.skew(axis=1)
df_out['kurt_features'] = df.kurtosis(axis=1)
return df_out
X = add_stat_features(X)
X_test = add_stat_features(X_test)
# 无监督特征
scaler_unsup = RobustScaler()
X_scaled_unsup = scaler_unsup.fit_transform(X)
X_test_scaled_unsup = scaler_unsup.transform(X_test)
pca = PCA(n_components=5, random_state=42)
pca_train = pca.fit_transform(X_scaled_unsup)
pca_test = pca.transform(X_test_scaled_unsup)
for i in range(5):
X[f'pca_{i}'] = pca_train[:, i]
X_test[f'pca_{i}'] = pca_test[:, i]
kmeans = KMeans(n_clusters=15, random_state=42, n_init=10)
kmeans.fit(X_scaled_unsup)
kmeans_dist_train = kmeans.transform(X_scaled_unsup)
kmeans_dist_test = kmeans.transform(X_test_scaled_unsup)
for i in range(15):
X[f'kmeans_dist_{i}'] = kmeans_dist_train[:, i]
X_test[f'kmeans_dist_{i}'] = kmeans_dist_test[:, i]
# 精英交互
top15_cols = ['behavior_template_activity', 'behavior_template_control', 'control_signal_intensity',
'protocol_variation_level', 'direction_gap_dispersion', 'direction_rate_gap', 'behavior_template_time',
'behavior_template_volume', 'protocol_mix_entropy', 'payload_irregularity', 'behavior_compactness_score',
'traffic_irregularity', 'pattern_concentration', 'payload_unit_dispersion', 'volume_irregularity']
for i in range(len(top15_cols)):
for j in range(i + 1, len(top15_cols)):
col1, col2 = top15_cols[i], top15_cols[j]
X[f'{col1}_plus_{col2}'] = X[col1] + X[col2]
X[f'{col1}_minus_{col2}'] = X[col1] - X[col2]
X[f'{col1}_mult_{col2}'] = X[col1] * X[col2]
X[f'{col1}_div_{col2}'] = X[col1] / (X[col2] + 1e-5)
X_test[f'{col1}_plus_{col2}'] = X_test[col1] + X_test[col2]
X_test[f'{col1}_minus_{col2}'] = X_test[col1] - X_test[col2]
X_test[f'{col1}_mult_{col2}'] = X_test[col1] * X_test[col2]
X_test[f'{col1}_div_{col2}'] = X_test[col1] / (X_test[col2] + 1e-5)
print("========== 5. 特征缩放与互信息优选 ==========")
scaler = RobustScaler()
X_scaled = scaler.fit_transform(X)
X_test_scaled = scaler.transform(X_test)
selector = SelectKBest(mutual_info_classif, k=80)
X_selected = selector.fit_transform(X_scaled, y_encoded)
X_test_selected = selector.transform(X_test_scaled)
print(f"最终入模特征数量: {X_selected.shape[1]}")
# 【修复点1】:退回稳定高效的对数损失评估,保持 0.05 的安全学习率,适度微调树参数
lgb_params = {
'objective': 'multiclass', 'num_class': 12, 'metric': 'multi_logloss',
'boosting_type': 'gbdt', 'is_unbalance': True, 'verbosity': -1,
'n_estimators': 2500, 'learning_rate': 0.05,
'num_leaves': 63, 'max_depth': 8, 'min_child_samples': 50,
'subsample': 0.8, 'colsample_bytree': 0.8, 'reg_alpha': 0.15,
'reg_lambda': 0.15, 'min_split_gain': 0.01
}
n_folds = 5
print("\n========== 阶段一:提取高置信度伪标签 ==========")
skf_base = StratifiedKFold(n_splits=n_folds, shuffle=True, random_state=42)
test_preds_stage1 = np.zeros((len(X_test_selected), 12))
base_params = lgb_params.copy()
base_params['random_state'] = 42
for train_idx, valid_idx in skf_base.split(X_selected, y_encoded):
X_tr, X_val = X_selected[train_idx], X_selected[valid_idx]
y_tr, y_val = y_encoded[train_idx], y_encoded[valid_idx]
train_data = lgb.Dataset(X_tr, label=y_tr)
valid_data = lgb.Dataset(X_val, label=y_val, reference=train_data)
model = lgb.train(
base_params, train_data, valid_sets=[train_data, valid_data],
callbacks=[lgb.early_stopping(100, verbose=False)]
)
test_preds_stage1 += model.predict(X_test_selected, num_iteration=model.best_iteration) / n_folds
CONFIDENCE_THRESHOLD = 0.99
max_probs = np.max(test_preds_stage1, axis=1)
pseudo_indices = np.where(max_probs > CONFIDENCE_THRESHOLD)[0]
pseudo_X = X_test_selected[pseudo_indices]
pseudo_y = np.argmax(test_preds_stage1[pseudo_indices], axis=1)
print(f"成功提取 {len(pseudo_indices)} 个高置信度测试样本")
# 记录原始训练集的长度,用于后续修复阈值寻优
ORIGINAL_TRAIN_LEN = len(X_selected)
X_combined = np.vstack((X_selected, pseudo_X))
y_combined = np.concatenate((y_encoded, pseudo_y))
print("\n========== 阶段二:合并数据后的 5 种子模型融合训练 ==========")
# 使用 5 个种子
seeds = [42, 77, 888, 2026, 9999]
blended_test_preds = np.zeros((len(X_test_selected), 12))
blended_oof_preds = np.zeros((len(X_combined), 12))
for i, seed in enumerate(seeds):
print(f"\n>>> 启动 Seed {seed} ({i + 1}/{len(seeds)}) <<<")
current_params = lgb_params.copy()
current_params['random_state'] = seed
skf_combined = StratifiedKFold(n_splits=n_folds, shuffle=True, random_state=seed)
current_seed_test_preds = np.zeros((len(X_test_selected), 12))
current_seed_oof = np.zeros((len(X_combined), 12))
for fold_idx, (train_idx, valid_idx) in enumerate(skf_combined.split(X_combined, y_combined)):
X_tr, X_val = X_combined[train_idx], X_combined[valid_idx]
y_tr, y_val = y_combined[train_idx], y_combined[valid_idx]
train_data = lgb.Dataset(X_tr, label=y_tr)
valid_data = lgb.Dataset(X_val, label=y_val, reference=train_data)
model = lgb.train(
current_params, train_data, valid_sets=[train_data, valid_data],
callbacks=[lgb.early_stopping(100, verbose=False)]
)
current_seed_oof[valid_idx] = model.predict(X_val, num_iteration=model.best_iteration)
current_seed_test_preds += model.predict(X_test_selected, num_iteration=model.best_iteration) / n_folds
blended_oof_preds += current_seed_oof / len(seeds)
blended_test_preds += current_seed_test_preds / len(seeds)
print("\n========== 阶段三:修复泄漏的 Macro F1 阈值寻优 ==========")
def optimize_thresholds(y_true, y_pred_proba):
def f1_opt(weights):
weights = np.abs(weights)
weighted_preds = y_pred_proba * weights
pred_labels = np.argmax(weighted_preds, axis=1)
return -f1_score(y_true, pred_labels, average='macro')
init_weights = [1.0] * 12
res = minimize(f1_opt, init_weights, method='Nelder-Mead', options={'maxiter': 400})
return np.abs(res.x)
print("正在计算最优类别切分阈值 (仅使用原始真实数据以防泄漏)...")
pure_train_oof = blended_oof_preds[:ORIGINAL_TRAIN_LEN]
pure_train_y = y_combined[:ORIGINAL_TRAIN_LEN]
best_weights = optimize_thresholds(pure_train_y, pure_train_oof)
final_test_preds_weighted = blended_test_preds * best_weights
final_labels = np.argmax(final_test_preds_weighted, axis=1)
final_categories = le.inverse_transform(final_labels)
submission = pd.DataFrame({
'id': test['id'],
'label': final_categories
})
submission.to_csv('submission.csv', index=False, encoding='utf-8')
print("提交文件已生成 'submission.csv'。")
💡 关键经验总结
- 特征工程是核心:匿名化数据上,统计+无监督+交互的三层特征体系是提分关键。
- 伪标签需要极高阈值:0.99确保质量,避免噪声污染。
- 多模型融合简单有效:5-Seed Blending比复杂Stacking更稳定。
- 阈值优化必须隔离:只用原始训练集OOF进行搜索,是防止分数虚高、确保线上有效的关键。
- 对抗分布偏移需综合手段:特征增强 + 伪标签 + 多模型集成三管齐下。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)