UNSW-NB15入侵检测实战:CNN-LSTM与特征工程全流程

发布时间:2026/10/7 13:39:13
UNSW-NB15入侵检测实战:CNN-LSTM与特征工程全流程 简介面向计算机相关专业毕业设计、课程设计与期末大作业场景这份基于机器学习的入侵检测系统项目包含完整可运行的Python源码与配套技术文档。项目曾获导师认可与99分评审高分覆盖数据预处理、特征提取、模型训练与预测等环节并分别提供CNN、LSTM及CNN-PyTorch实现除训练与预测外预处理部分也覆盖相关性分析、不平衡样本处理、归一化及标签编码等常用步骤适合需要实战参考的本科生与入门学习者。压缩包共31个文件以14个Python脚本为核心辅以CSV数据集、HDF5模型权重、Markdown/TXT说明文档、技术方案Word文档和结构图整体约26.58MB目录按CNN、LSTM、ML、data等模块划分便于对照研读。资源内包含UNSW_NB15数据集处理脚本及打包数据可直接复现二分类入侵检测流程。目前已有68人学习下载对希望短时间搭建同类型系统或快速理解检测思路的读者有较高参考价值。1. 把入侵检测当分类问题UNSW-NB15 与 CNN/LSTM 的落地组合入侵检测系统听起来像个黑匣子但把这套源码拆开之后你会发现它的核心就是一句话给一条网络流量记录判断它是正常还是攻击。UNSW-NB15 提供了 49 维特征和九类攻击行为CNN 负责提局部模式LSTM 负责建模顺序关系两者都是可以直接训练的分类器。这套基于机器学习的入侵检测系统 Python 源码把数据处理、模型训练、预测验证串成了一条完整链路数据、脚本、文档、结果图片都齐适合做机器学习课设、期末大作业或者毕设起步。想快速跑通全流程的从数据管道往下走就行。2. 数据管道先行特征提取、编码归一化与类别不平衡处理数据处理是整个项目里最容易翻车也最花时间的环节。机器学习里的数据处理做不好后面模型调参全是白费。这套源码把这一步拆成了四个脚本Get_corr.py、get_feature.py、Lable_encoder.py、Min-max.py加上一个 Process_Imbanlce.py 处理样本不平衡顺序对上了模型训练才能顺。2.1 特征工程Get_corr.py 与 get_feature.py 在筛什么UNSW-NB15 解压之后你会看到一个列数很多的 CSV。原始数据集有 49 个特征包含像 srcip、dstip 这样的 IP 字符串列还有 sport、dsport 端口号以及协议类型 proto。这些列里IP 地址是纯类别信息模型直接读会当成巨大的数值端口号则会被 80、443 这种大数字干扰特征尺度。所以拿到原始表第一步不是训练而是先把特征筛一遍。项目里 Get_corr.py 做的是特征相关性分析输出各特征与标签的相关性排序用来判断哪些列对是否攻击有区分度。get_feature.py 则把筛选结果落地生成后续真正喂给模型的特征矩阵。我一般会在这一步把 IP 段、端口这类高基数类别列先剔除再看相关性。# 特征筛选剔除字符串列后按相关系数排序 import pandas as pd df pd.read_csv(../data/UNSW_NB15.csv, encodingutf-8) drop_cols [id, srcip, dstip, sport, dsport] df df.drop(columns[c for c in drop_cols if c in df.columns]) # 相关性排序二分类标签列通常叫 label corr df.corr()[label].abs().sort_values(ascendingFalse) print(corr.head(20))逻辑说明corr() 只能处理数值列所以先把字符串列剔除干净否则会抛异常或产生一堆 NaN。相关系数取绝对值正负只代表方向绝对值越大说明该特征与标签的线性关系越强。排序后你通常能看到 service、ct_state_ttl、ct_srv_dst 这类连接状态特征排在最前面这和网络安全领域的经验判断是一致的。参数说明相关性阈值取多少没有定论。特征多的时候我习惯先按 0.05 卡一条线把低于这个值的弱相关特征丢掉再在训练时对比丢之前的精度。如果算出来大部分特征相关性都很低说明原始数据里脏列太多先回头检查有没有没剔除的字符串列。2.2 编码与归一化Lable_encoder.py 和 Min-max.py 的配合顺序数据里 attack_cat 列是字符串形式的攻击类型比如 Normal、Fuzzers、Generic。模型要的是数值所以 Lable_encoder.py 会用 LabelEncoder 或 pandas 的 factorize 把它映射成 0、1、2 这样的整数。二分类场景下更常见的做法是只保留 0 和 1所有攻击类别合并成 1正常为 0多分类场景才保留 0 到 9 的十类编码。归一化这块Min-max.py 对应 sklearn 的 MinMaxScaler。为什么选 MinMax 而不是 StandardScalerCNN 和 LSTM 的激活函数对输入尺度敏感MinMax 把特征压到 0~1 区间保留原始分布形状StandardScaler 会把特征变成均值 0 方差 1 的分布产生负值ReLU 对负输入直接截断梯度信号就丢了。不过在特征存在明显长尾分布的情况下MinMax 会让大多数样本挤在 0 附近这时候 StandardScaler 反而更好——但这个项目里先按 Min-max.py 跑通。# 标签编码 MinMax 归一化的完整顺序 import pandas as pd from sklearn.preprocessing import LabelEncoder, MinMaxScaler df pd.read_csv(../data/UNSW_NB15.csv, encodingutf-8) # 1) 先对攻击类别做标签编码 le LabelEncoder() df[attack_cat] le.fit_transform(df[attack_cat].astype(str)) # 2) 二分类标签所有攻击类合并为 1正常为 0 df[label] (df[attack_cat] ! le.transform([Normal])[0]).astype(int) # 3) 数值特征列统一做 MinMax 归一化 feature_cols [c for c in df.columns if c not in [attack_cat, label, id]] scaler MinMaxScaler() df[feature_cols] scaler.fit_transform(df[feature_cols]) df.to_csv(../data/2_feture.csv, indexFalse)逻辑说明先编码再归一化这个顺序不能换。LabelEncoder 要求输入全是字符串归一化要求输入全是数值。如果先把整个 DataFrame 丢给 MinMaxScaler字符串列会直接报错如果先归一化再编码标签列混在特征矩阵里归一化会把标签也压一遍等于提前泄露了目标信息。处理完的 2_feture.csv 就是后续所有模型共用的特征文件。参数说明MinMaxScaler 的 feature_range 默认是 (0,1)。如果训练时发现模型收敛慢可以改成 (-1,1)但配套的激活函数和输入维度要重新核对。注意 fit 只能用训练集测试集只做 transform否则测试集的信息会提前混进训练流程。2.3 类别不平衡Process_Imbanlce.py 处理的是分布不是样本量UNSW-NB15 的类别分布很不均匀。Generic 和 Exploits 样本很多Shellcode 和 Worms 很少有的类别可能只有几百条而正常流量有几万条。直接拿原始分布训练模型会学成无脑预测大类整体准确率看着很高但小类攻击几乎全漏。这就是 Process_Imbanlce.py 存在的意义——它处理的不是样本数量本身而是类别分布。常见做法是下采样或者过采样。下采样把大类抽稀训练速度快但会丢信息过采样用 SMOTE 合成少类样本信息保留好但可能产生噪声。Process_Imbanlce.py 从命名看走的是后一条路用 imbalanced-learn 库做重采样。# 用 SMOTE 做类别不平衡处理先切分再重采样 from imblearn.over_sampling import SMOTE from sklearn.model_selection import train_test_split X df[feature_cols].values y df[label].values X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42, stratifyy ) smote SMOTE(random_state42) X_train_res, y_train_res smote.fit_resample(X_train, y_train) print(pd.Series(y_train_res).value_counts().to_dict())逻辑说明先切分再重采样是硬性要求。如果先对整个数据集 SMOTE 再切训练测试集合成样本可能同时出现在两边造成数据泄露测试指标虚高得离谱。stratifyy 保证切分后训练集和测试集里的类别比例跟原始数据一致这一步在类别不平衡时尤其重要。参数说明SMOTE 的 k_neighbors 默认 5对样本只有几百条的少数类建议调低到 3否则合成样本会和大量邻居重叠。重采样后模型训练时长会明显增加尤其是 LSTM。如果机器性能吃紧可以考虑先下采样大类到两倍于少类再做 SMOTE。2.4 数据切分train.csv 与 test.csv 的划分思路data 目录里已经有 train.csv 和 test.csv省了手动划分的步骤。但你需要知道它是怎么切的这直接影响你对外汇报评估结果的底气。入侵检测领域的惯例是按时间切分原始记录按时间排序前 80% 时间段做训练后 20% 做测试。这样模拟的是用过去的流量发现未来的攻击比随机切分更接近真实部署场景。如果作者用的是随机切分那评估结果会偏乐观一点因为训练集和测试集来自同一分布。不管哪种方式至少确认测试集里每条攻击类别都出现了别让 Shellcode 这种少类在测试集里一条都见不到。# 按时间戳切分避免随机切分带来的乐观偏差 df pd.read_csv(../data/UNSW_NB15.csv, parse_dates[time]) df df.sort_values(time) split_idx int(len(df) * 0.8) train_df df.iloc[:split_idx].copy() test_df df.iloc[split_idx:].copy() print(ftrain: {len(train_df)}, test: {len(test_df)})逻辑说明parse_dates 会把 time 列解析成时间类型sort_values 按时间升序排列。取前 80% 做训练、后 20% 做测试保证测试集在时间上严格晚于训练集。如果后续发现测试集效果比随机切分差这是正常现象按时间切分的评估结果通常更保守。3. 双模型落地CNN 与 LSTM 在 UNSW-NB15 上的训练路径数据管道跑通之后进入模型环节。这套资源给了两条路cnn 目录下的 CNN 方案lstm 目录下的 LSTM 方案。两者读取的是同一份 2_feture.csv差异在于怎么理解一条网络流记录——CNN 把它当成局部特征组合LSTM 把它当成特征顺序序列。3.1 CNN 的训练入口与模型结构cnn_pytorch.py 与 cnn_train.py 怎么配合cnn 目录下有两个关键脚本cnn_pytorch.py 负责模型定义cnn_train.py 负责训练流程。从 PyTorch 的实现来看这种表格数据用的是一维卷积 Conv1d。每条样本是一个特征向量可以理解成单通道的一维信号卷积核沿着特征维度滑动提取相邻特征之间的局部模式。比如端口号和协议类型之间的组合关系可能就藏在这种局部结构里。# 基于 PyTorch 的 1D-CNN 模型核心结构对应 cnn_pytorch.py import torch import torch.nn as nn class IDSCNN(nn.Module): def __init__(self, n_features, num_classes2): super().__init__() self.conv_block nn.Sequential( nn.Conv1d(1, 64, kernel_size3, padding1), nn.ReLU(), nn.MaxPool1d(2), nn.Conv1d(64, 128, kernel_size3, padding1), nn.ReLU(), nn.MaxPool1d(2), ) self.classifier nn.Sequential( nn.Flatten(), nn.Linear(128 * (n_features // 4), 128), nn.ReLU(), nn.Dropout(0.5), nn.Linear(128, num_classes), ) def forward(self, x): x x.unsqueeze(1) # (batch, n_features) - (batch, 1, n_features) x self.conv_block(x) return self.classifier(x)逻辑说明Conv1d 的输入形状是 (batch, channels, length)特征向量先 unsqueeze(1) 变成单通道一维信号。kernel_size3 表示卷积核每次看相邻 3 个特征padding1 保持卷积后的长度不变。MaxPool1d(2) 把特征长度减半两次池化后长度变为原来的四分之一所以全连接层第一个 Linear 的输入维度要用 n_features 除以 4。如果特征数不是 4 的整数倍这里维度就对不上会直接报错。训练脚本 cnn_train.py 的流程是标准的 PyTorch 训练循环读 train.csv转成 Tensor 和 DataLoader每个 epoch 里算 loss、反向传播、更新权重并定时在验证集上评估。二分类场景建议用 CrossEntropyLoss配合最后一层输出的 logits数值上比 BCELoss 加 Sigmoid 稳定。优化器用 Adam学习率先给 1e-3如果 loss 震荡再降到 1e-4。# cnn_train.py 训练主循环的简化结构 from torch.utils.data import DataLoader, TensorDataset dataset TensorDataset(torch.tensor(X_train, dtypetorch.float32), torch.tensor(y_train, dtypetorch.long)) loader DataLoader(dataset, batch_size64, shuffleTrue) model IDSCNN(n_featuresfeature_cols长度, num_classes2) optimizer torch.optim.Adam(model.parameters(), lr1e-3) criterion nn.CrossEntropyLoss() for epoch in range(epochs): for X_batch, y_batch in loader: optimizer.zero_grad() out model(X_batch) loss criterion(out, y_batch) loss.backward() optimizer.step()逻辑说明batch_size64 是表格数据常见起点数据量不大时 32 也行。shuffleTrue 打乱样本顺序避免模型学到样本排布规律。每个 batch 里 optimizer.zero_grad() 必须放在 forward 之前否则梯度会累加。训练完之后模型权重会存到 cnn/res 目录下供 cnn_predict.py 调用。3.2 LSTM 的序列建模与参数lstm_train.py 在建模什么LSTM 在入侵检测里的作用有两种理解。一种是把同一源 IP 的多条流量按时间组织成真实序列另一种是把单条流量的特征向量按特征顺序逐位读入每个特征值当作一个时间步。lstm_train.py 走的是后一种思路代码上更简单一条样本是一个 49 维向量LSTM 按顺序读完 49 个特征最后输出分类结果。# LSTM 模型核心结构对应 lstm_train.py 的实现思路 import torch.nn as nn class IDSLSTM(nn.Module): def __init__(self, n_features, hidden_size64, num_layers2, num_classes2): super().__init__() self.lstm nn.LSTM( input_size1, hidden_sizehidden_size, num_layersnum_layers, batch_firstTrue, bidirectionalTrue, ) self.fc nn.Linear(hidden_size * 2, num_classes) def forward(self, x): x x.unsqueeze(-1) # (batch, n_features, 1) out, _ self.lstm(x) out out[:, -1, :] # 取最后一个时间步的输出 return self.fc(out)逻辑说明input_size1 表示每个时间步只读一个特征值n_features 个特征就是 n_features 个时间步。bidirectionalTrue 让 LSTM 从两个方向读特征序列最后一个时间步的输出包含了整个序列的信息。hidden_size*2 是因为双向 LSTM 会把正向和反向的隐藏状态拼接起来。参数说明hidden_size64 是起步值数据集规模大时可以调到 128。num_layers2 增加网络深度但小数据集上超过两层容易过拟合。如果训练太慢把 num_layers 降到 1或者 hidden_size 降到 32先跑通再调大。LSTM 比 CNN 慢不少CPU 机器上要有心理准备这也是为什么很多入门项目先用 CNN 出基线结果。3.3 预测与评估cnn_predict.py 与 lstm_predict.py 的结果怎么解读预测脚本读测试集加载训练好的模型权重文件输出每条样本的预测类别。cnn_predict.py 和 lstm_predict.py 的逻辑几乎一样只是加载的模型类不同。输出除了预测标签一般还会附带混淆矩阵和分类报告。# 预测脚本核心逻辑对应 cnn_predict.py import torch from sklearn.metrics import classification_report, confusion_matrix model.load_state_dict(torch.load(cnn/res/best_model.pt)) model.eval() preds, labels [], [] with torch.no_grad(): for X_batch, y_batch in test_loader: out model(X_batch) pred out.argmax(dim1) preds.extend(pred.numpy()) labels.extend(y_batch.numpy()) print(confusion_matrix(labels, preds)) print(classification_report(labels, preds, digits4))逻辑说明model.eval() 会关闭 Dropout 和 BatchNorm 的训练行为这一步忘了的话预测结果不稳定。argmax(dim1) 取每个样本概率最高的类别作为输出。classification_report 给出精确率、召回率、F1 和每个类别的样本数这些比整体 accuracy 更值得关注因为入侵检测场景下漏报攻击的代价远高于误报正常流量。如果预测脚本报找不到权重文件先回训练目录确认训练真的跑完并且保存了 .pt 文件。root 目录下的 cnn_pytorch_acc.jpg 是训练过程的准确率曲线截图对照它就能判断自己训练出的模型有没有达到预期水平——如果曲线明显没收敛说明数据管道或超参有问题往下看避坑章节。4. 避坑实录数据格式、依赖版本与运行环境的五个典型问题这套源码我在复现的时候踩过不少坑。下面这五条基本覆盖了新手运行时会碰到的绝大多数问题按现象、原因、解决的顺序写可以直接对照排查。4.1 现象一ModuleNotFoundError: No module named torch原因虚拟环境没建或者 requirements.txt 没安装。很多拿源码直接跑的人用的是系统 Python里面只装了 pip 和几个基础库。CNN 和 LSTM 方案都依赖 PyTorch数据处理依赖 scikit-learn 和 imbalanced-learn少一个都跑不起来。解决先建虚拟环境再执行 pip install -r requirements.txt。Windows 下安装 PyTorch 如果从默认 PyPI 拉包很慢可以加国内镜像源。装完后进入 Python 验证一下 import torch 和 import imblearn 是否正常两个都通过再跑训练脚本。4.2 现象二训练 loss 不降accuracy 一直在 50% 附近震荡原因输入数据没有归一化或者标签没编码干净。CNN 和 LSTM 对输入量级非常敏感源端口、包长度这种大数值列会把梯度推向错误方向。另一个隐蔽原因是训练集和测试集用了不同的特征列顺序比如从 dict 构建 DataFrame 时列顺序不稳定模型在训练时看到的特征排列和测试时不一致。解决回到 Min-max.py 把特征压到 0~1确认 attack_cat 列已经被编码成 0/1 而不是保留原始字符串。特征列顺序统一用同一份 feature_cols 列表来取不要靠记忆写列名。检查方式是打印训练集和测试集的列名列表逐列比对。4.3 现象三准确率 95% 以上但预测结果全是正常类原因这是类别不平衡最典型的翻车现场。UNSW-NB15 里正常流量占比大模型学到全部预测正常这种偷懒策略就能拿到很高准确率。如果 Process_Imbanlce.py 没跑或者跑完重采样之后没有 stratify 切分测试集里攻击样本太少评估结果就更具有欺骗性。解决训练前用 SMOTE 重采样训练集但测试集保持原始分布不要对测试集做任何重采样。评估时看 classification_report 里每个攻击类别的召回率而不是只看整体 acc。如果召回率还是上不去把 SMOTE 的采样策略改成 0.5 比例减少合成样本的噪声干扰。4.4 现象四Windows 下路径带中文或反斜杠报错原因pandas 的 read_csv 在 Windows 上遇到中文目录或反斜杠转义会出问题或者文件编码不是 UTF-8。UNSW-NB15 原始 CSV 某些字段里带有特殊字符用 gbk 编码读取会崩或者产生乱码列。解决全程用正斜杠路径比如 ../data/UNSW_NB15.csv文件统一放到英文目录下。读取时指定 encodingutf-8实在不行加 enginepython 兜底。data 目录里的 UNSW_NB15.zip 解压后建议重命名为 UNSW_NB15.csv 再读避免文件名里带中文。4.5 现象五预测脚本报找不到模型权重文件原因训练脚本和预测脚本的路径约定不一致。比如训练时把权重存到了 cnn/res/ 下预测脚本却默认去根目录找或者训练中断过权重文件根本没生成。还有一种情况是脚本里写了 .cuda()但本地机器没有 GPU代码没有判断 torch.cuda.is_available() 就直接崩了。解决先检查训练目录下有没有生成 .pt 文件确认训练真的跑完。再看预测脚本里的路径是否和保存路径一致不一致就改成相对路径。GPU 相关的代码改成条件判断device torch.device(cuda if torch.cuda.is_available() else cpu)然后模型和数据都 to(device)这样 CPU 和 GPU 都能跑。5. 从二分类 demo 到自有多分类检测器最后这一步怎么改如果你已经跑通了 demo_2分类.py下一步最值得做的事就是把二分类改成 UNSW-NB15 完整的多分类。原始数据集有十个类别一个 Normal 加上 Fuzzers、Analysis、Backdoors、DoS、Exploits、Generic、Reconnaissance、Shellcode、Worms 九种攻击。多分类能让你看清楚哪些攻击容易互相混淆这在答辩里比单个准确率更有说服力。改成多分类只需要动三个地方。第一标签编码保留 0 到 9 的完整映射不要把所有攻击合并成 1第二模型输出层的 num_classes 从 2 改成 10第三评估部分用多分类版本的 classification_report。具体改动如下# 二分类合并所有攻击为 1 df[label] (df[attack_cat] ! Normal).astype(int) # 多分类保留完整类别映射 le LabelEncoder() df[label] le.fit_transform(df[attack_cat]) # 此时 label 范围为 0~9对应原始十个类别这两行代码的差别就是整个改造的核心。改完之后把 CNN 和 LSTM 的输出层都调成 10其余结构不用动。观察训练时的 loss 曲线如果前几个 epoch 下降缓慢说明类别间区分度比二分类低很多这是正常的——Generic 和 Exploits 的特征重叠度本来就高。验证技巧也很关键多分类之后不要只看整体 acc把混淆矩阵打印出来逐行看。Shellcode 和 Worms 样本少如果被大面积误判成其他攻击说明少数类的训练样本仍然不够可以考虑对这两个类单独加大 SMOTE 的采样比例。我习惯把混淆矩阵存成图片答辩时直接展示哪类攻击被谁混淆了比一句准确率 98%有说服力得多。我刚拿到这套源码时在 4.3 那个坑里栽过跟头——第一次跑出 96% 的准确率兴奋了半天点开混淆矩阵才发现攻击样本漏了一半。从那以后我每次复现流程都强制走一遍先看数据均衡情况再训练最后先看混淆矩阵再看 acc。做二分类 demo 先跑通完整链路再改多分类这条路花的时间最少。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询