协议预测实现中LSTM模型准确率问题及优化建议问询
提升不平衡网络协议数据集的LSTM小类别预测准确率方案
问题背景
- 数据集:140万行Wireshark捕获导出的CSV,仅使用Source、Destination、Protocol三个字段
- 任务:基于LSTM学习前序流量的Source/Destination/Protocol序列关联,预测下一个数据包的Protocol
- 现状:
- 类别极度不平衡:TCP约100万条,UDP约20万条,其余协议样本量极少
- 模型整体准确率0.8-0.85,但仅能准确预测TCP类别,小类别完全失效
- 尝试过调整序列长度、epochs、batch size、缩小数据集、类别权重平衡,但小类别准确率仅0.3-0.4,整体准确率暴跌至0.2
核心问题分析
- 数据泄露:当前代码将当前步的Protocol独热编码加入输入特征,模型可直接利用该信息预测下一步,完全偏离任务目标(学习Source/Dest与Protocol的关联)
- 评估指标不合理:用整体Accuracy评估不平衡数据集,无法反映小类别的真实性能
- 类别不平衡处理不当:简单的类别权重设置可能过度压制多数类,导致整体性能暴跌,且模型梯度被多数类淹没
- 特征工程不足:IP地址的哈希方式未能充分保留语义信息
针对性优化建议与代码修改
1. 修复数据泄露问题
移除输入特征中的当前Protocol字段,仅保留Source和Destination的特征,确保模型学习的是Source/Dest序列与Protocol的关联,而非直接利用已知信息。
2. 更换评估指标
使用分类报告(Precision/Recall/F1)和混淆矩阵评估,聚焦小类别性能,替代无意义的整体Accuracy。
3. 改进类别不平衡处理
- 使用
class_weight='balanced'自动计算合理的类别权重,避免手动设置的偏差 - 改用Focal Loss替代交叉熵损失,降低易分类样本(TCP)的权重,强制模型关注小类别
4. 优化特征工程
将IP地址转换为32位整数作为数值特征,比哈希更具区分度,保留IP的完整语义。
5. 模型与训练策略调整
- 加入Dropout层防止过拟合
- 使用自定义早停机制监控小类别F1分数,避免过拟合
- 调整学习率,配合更多epochs平稳训练
修改后的示例代码
import pandas as pd import numpy as np from sklearn.model_selection import TimeSeriesSplit from sklearn.preprocessing import LabelEncoder from sklearn.metrics import classification_report, confusion_matrix from keras.models import Sequential from keras.layers import LSTM, Dense, Dropout, Bidirectional from keras.utils import to_categorical from keras.callbacks import EarlyStopping from keras.losses import CategoricalFocalCrossentropy # IP转32位整数函数 def ip_to_int(ip_str): try: octets = list(map(int, ip_str.split('.'))) return (octets[0] << 24) + (octets[1] << 16) + (octets[2] << 8) + octets[3] except: return 0 # 加载并预处理数据 data = pd.read_csv('paketa.csv') data = data.dropna() data = data[(data['Protocol'] != 'IPv6') & (data['Protocol'] != 'MPTCP')] unique_classes = data['Protocol'].unique() print("Unique Protocols:", unique_classes) # 特征工程:IP转整数并归一化 data['Source_int'] = data['Source'].apply(ip_to_int) data['Dest_int'] = data['Destination'].apply(ip_to_int) data['Source_int'] = (data['Source_int'] - data['Source_int'].min()) / (data['Source_int'].max() - data['Source_int'].min()) data['Dest_int'] = (data['Dest_int'] - data['Dest_int'].min()) / (data['Dest_int'].max() - data['Dest_int'].min()) # 构建序列:仅用Source和Dest特征,预测下一个Protocol sequence_length = 10 sequences = [] targets = [] feature_cols = ['Source_int', 'Dest_int'] feature_data = data[feature_cols].values for i in range(len(feature_data) - sequence_length): seq = feature_data[i:i+sequence_length] target = data.iloc[i+sequence_length]['Protocol'] sequences.append(seq) targets.append(target) X = np.array(sequences) y = np.array(targets) # 标签编码与独热编码 label_encoder = LabelEncoder() label_encoder.fit(unique_classes) y_indices = label_encoder.transform(y) y_onehot = to_categorical(y_indices, num_classes=len(unique_classes)) # 构建LSTM模型 num_classes = len(unique_classes) num_features = len(feature_cols) model = Sequential() model.add(Bidirectional(LSTM(128, return_sequences=True), input_shape=(sequence_length, num_features))) model.add(Dropout(0.3)) model.add(LSTM(64)) model.add(Dropout(0.3)) model.add(Dense(num_classes, activation='softmax')) # 使用Focal Loss处理类别不平衡 model.compile( loss=CategoricalFocalCrossentropy(gamma=2.0), # gamma越大,小类别权重越高 optimizer='adam', metrics=['accuracy'] ) # 自定义早停:监控小类别F1分数 class F1EarlyStopping(EarlyStopping): def __init__(self, val_f1_threshold=0.5, **kwargs): super().__init__(**kwargs) self.val_f1_threshold = val_f1_threshold def on_epoch_end(self, epoch, logs=None): val_pred = np.argmax(model.predict(self.validation_data[0], verbose=0), axis=1) val_true = np.argmax(self.validation_data[1], axis=1) report = classification_report(val_true, val_pred, output_dict=True) # 取样本量小于5万的小类别平均F1 small_class_f1 = np.mean([report[str(i)]['f1-score'] for i in range(num_classes) if report[str(i)]['support'] < 50000]) if small_class_f1 >= self.val_f1_threshold: self.model.stop_training = True super().on_epoch_end(epoch, logs) # 时间序列拆分 tscv = TimeSeriesSplit(n_splits=2) for train_index, test_index in tscv.split(X): X_train, X_test = X[train_index], X[test_index] y_train, y_test = y_onehot[train_index], y_onehot[test_index] # 训练模型 early_stop = F1EarlyStopping( monitor='val_loss', patience=5, restore_best_weights=True, val_f1_threshold=0.6 ) history = model.fit( X_train, y_train, epochs=20, batch_size=64, class_weight='balanced', validation_split=0.1, callbacks=[early_stop], verbose=1 ) # 评估模型 y_pred = np.argmax(model.predict(X_test, verbose=0), axis=1) y_true = np.argmax(y_test, axis=1) print("\n分类报告:") print(classification_report(y_true, y_pred, target_names=label_encoder.classes_)) print("\n混淆矩阵:") print(confusion_matrix(y_true, y_pred))
内容的提问来源于stack exchange,提问作者GENERALE
相关产品推荐
相关产品推荐

