
简介面向计算机相关专业毕业设计、课程设计与期末大作业等场景资源提供一套基于机器学习的入侵检测系统 Python 源码与完整文档说明覆盖 CNN 与 LSTM 模型训练预测、数据预处理、特征筛选、标签编码、样本不平衡处理等环节可直接用于 UNSW-NB15 等网络流量数据集上的二分类实践。压缩包共 31 个文件包含 14 个 Python 脚本、5 个 TXT 说明、3 个 CSV 数据集、3 个 Markdown 文档、2 个 HDF5 模型文件以及 1 份 DOC 技术方案整体约 26.58MB目录按 IDS、CNN、LSTM、ML 等模块组织并配有 requirements.txt 与运行结果记录便于按步骤复现与调试。项目源自研三大作业经导师指导后评审分为 99 分代码完整可运行适合初学者作为实战项目练习也适合正在准备毕设或需要完整项目参考的学生快速搭建入侵检测实验。目前已有 166 人学习下载资源内附技术方案和模型效果图可帮助理解从数据处理到模型评估的完整流程。1. 机器学习落地入侵检测从规则引擎到可运行的三套模型源码这套基于机器学习的入侵检测系统源码包把三类主流方案——随机森林、CNN、LSTM——全部跑通并配上了 UNSW-NB15 数据集的处理链路和说明文档。规则引擎比如 Snort 的特征库只能抓已知攻击面对壳变、混淆和未知 payload 基本失灵机器学习检测的思路是把流量抽象成特征向量让模型自己找规律。对正在做毕设或课程设计的人来说它正好补上理论课和真实工程之间的缺口数据清洗、特征降维、不平衡处理、模型对比每个环节都有现成脚本。文档能直接撑起设计报告的结构代码跑通后再按自己的数据集改。资源里甚至预置了一份极简二分类 demo五分钟就能验证环境是否正常。2. 先啃数据UNSW-NB15 的预处理链路与四个关键动作2.1 解压与目录认知train.csv、test.csv、2_feture.csv 分别该什么时候用打开压缩包之后会发现数据部分分了好几层UNSW_NB15.zip 是原始公开数据集Process_data.zip 是处理过的中间产物根目录下的 train.csv、test.csv 是整理好的实验集。很多新手上来直接读 UNSW_NB15.zip 里的原始文件结果被字段格式和各种异常值搞得怀疑人生。我的习惯是优先看 Process_data.zip 和 train.csv因为它们已经做了一部分清洗字段名能对上脚本。2_feture.csv 看名字就知道是只保留两个核心特征的精简版这类文件最适合在第一次接触项目时用来跑通流程不要在还没理解整套逻辑的情况下就上全量数据。先做两件事解压数据、用轻量方式看一眼训练集长什么样。unzip UNSW_NB15.zip unzip Process_data.zip python - EOF import pandas as pd df pd.read_csv(train.csv, nrows5, low_memoryFalse) print(df.shape) print(df.columns.tolist()[:10]) if label in df.columns: print(df[label].value_counts()) EOF这里nrows5表示只读前 5 行目的是快速确认列名和解压路径是否正确避免直接把几 GB 的 CSV 整个塞进内存。low_memoryFalse是为了阻止 pandas 在读取混合类型字段时产生类型推断警告实际项目中这个参数经常被忽略但忽略之后dtpye不稳定后面训练脚本容易报 KeyError。如果你发现label列不在 train.csv 里多半是文件解压位置不对或者读错了原始数据包的路径。2.2 类别编码与数值归一化Label_encoder.py 和 Min-max.py 到底改了什么流量数据里有不少非数值列最常见的是proto协议类型、service服务类型、state连接状态。随机森林和深度学习模型都不认识字符串所以要先编码。Label_encoder.py 干的就是这件事把每个字符串取值映射成一个整数编号。这里有一个取舍类别取值很少的时候也可以考虑 OneHot但这个项目脚本用的是整数编码因为对树模型来说整数编码代价更小而且 UNSW-NB15 里这几列的取值区间有限整数编码不会带来误导。数值列则要做 Min-Max 归一化规则是缩放到[0,1]区间。CNN 和 LSTM 这类基于梯度的模型对输入尺度非常敏感原始数值范围从几到几十万都有不缩放的后果就是 loss 不降或者直接 NaN。随机森林虽然不在乎尺度但统一流程之后三套模型可以共用同一份预处理结果省掉重复踩坑。import pandas as pd from sklearn.preprocessing import LabelEncoder, MinMaxScaler df pd.read_csv(train.csv, low_memoryFalse) # 类别列UNSW-NB15 里最常见的三个字符串列 for col in [proto, service, state]: if col in df.columns: le LabelEncoder() df[col] le.fit_transform(df[col].astype(str)) # 数值列排除标签列之后再做缩放 num_cols df.select_dtypes(include[int64, float64]).columns.difference([label]) scaler MinMaxScaler(feature_range(0, 1)) df[num_cols] scaler.fit_transform(df[num_cols]) # 保存为 parquet读写比 csv 快精度不丢失 df.to_parquet(train_processed.parquet)这里astype(str)很关键因为 CSV 里某些类别列可能混入空值pandas 读进来会变成float或NaN直接 fit 会报错先转字符串可以兜底。select_dtypes负责挑出所有数值列再用difference([label])把标签排除这样不会把label也缩放掉。保存成 parquet 是我一直推荐的习惯训练集大起来之后 CSV 的读写速度会拖慢整个实验循环。2.3 特征筛选与不平衡处理Get_corr.py 和 Process_Imbanlce.py 的顺序不要搞反全量特征全带上不是不行但很多特征之间高度相关冗余特征会拖慢训练还可能放大噪声。Get_corr.py 做的就是相关性过滤计算两两特征之间的相关系数把超过阈值的列删掉一部分。这一步建议放在归一化之后、不平衡处理之前因为相关系数不受缩放影响但顺序固定之后出问题更容易排查。import pandas as pd df pd.read_parquet(train_processed.parquet) feat_cols [c for c in df.columns if c ! label] corr df[feat_cols].corr().abs() drop_cols set() for i in range(len(corr.columns)): for j in range(i 1, len(corr.columns)): if corr.iloc[i, j] 0.8: drop_cols.add(corr.columns[j]) keep_cols [c for c in feat_cols if c not in drop_cols] print(f原始特征数 {len(feat_cols)}删除 {len(drop_cols)} 个保留 {len(keep_cols)} 个)0.8是经验阈值可以按数据量调特征多就压到0.95特征少就放宽到0.7。注意这个脚本删的是“后出现的那一列”保留顺序靠前的列所以列顺序会影响最终保留结果真要做严格筛选还需要结合特征重要性一起看。最后一环是 Process_Imbanlce.py。UNSW-NB15 里 Normal 和 Generic 样本数量很大Shellcode、Worms 很少是典型的不平衡数据。如果不处理模型会倾向把所有样本判成多数类整体准确率看着很高小类攻击全被漏掉。Process_Imbanlce.py 做的事情一般是抽样或合成少数类样本跑完之后强烈建议打印一下各类样本数确认没有把少数类直接清掉。python - EOF import pandas as pd df pd.read_csv(Process_data/balanced_train.csv) print(df[label].value_counts()) EOF如果某个攻击类别的样本数为 0说明处理脚本写死了输出类别而你手里的标签集合跟它不一致这种情况要先统一标签映射再谈训练。3. 随机森林路线用 RFP.py 建立第一条可解释的检测基线3.1 为什么第一站必须选随机森林对表格数据的天然优势与特征重要性一上来就碰深度学习不是一个好习惯。随机森林在表格数据上往往能和深度模型打得不分上下成本却低一个数量级。它基于 Bagging 思想对训练集做有放回抽样训练多棵决策树最终投票决定结果。每棵树只见过一部分样本和一部分特征单棵会过拟合但多棵树平均之后方差显著下降。对入侵检测这种特征维度高、噪声多的场景这种集成机制天然稳。另一个现实理由是特征重要性。RFP.py 跑完之后可以直接输出feature_importances_告诉你哪些流量特征对检测起了主要作用。写论文或设计报告时这张图比任何模型结构图都有说服力。我一般会把 top10 特征列出来再跟手工特征工程对比解释为什么某些特征比如连接时长、源字节数在统计上更显著。这套源码里把 ML 部分排在深度学习前面不是偶然它承担着“基线”的作用——后续 CNN 和 LSTM 的效果如果连随机森林都干不过问题多半出在数据而不是模型。3.2 RFP.py 核心参数解析300 棵树、分层采样与类别权重先运行python RFP.py然后打开脚本看核心参数。下面这一段是随机森林最常见的配置模板from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import train_test_split from sklearn.metrics import classification_report X df.drop(columns[label]).values y df[label].values X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.3, stratifyy, random_state42 ) rf RandomForestClassifier( n_estimators300, max_depth18, min_samples_leaf2, n_jobs-1, class_weightbalanced_subsample, random_state42 ) rf.fit(X_train, y_train) print(classification_report(y_test, rf.predict(X_test), digits4))stratifyy保证训练集和测试集的类别比例与全量数据一致这是处理不平衡数据的第一步不加的话模型评估结果会随机波动。n_estimators300是收敛与训练速度的平衡点继续加到 500 精度提升很有限但训练时间线性增长。max_depth18限制每棵树的最大深度防止单棵树把训练集背下来。min_samples_leaf2要求叶子节点至少 2 个样本配合深度限制一起抑制过拟合。class_weightbalanced_subsample是随机森林处理不平衡的最好方式之一每次 bootstrap 采样后根据该子集的类别分布重新计算权重比全局class_weightbalanced更灵活。n_jobs-1表示用满所有 CPU 核心训练 250 万条数据也不至于等到天荒地老。跑完后不要只看 accuracy重点看classification_report里 macro avg 和 weighted avg 两行前者把每个类别的指标平等平均后者按样本量加权。两者差距过大说明模型在少数类上表现很差。3.3 二分类与多分类的评估差异demo_2分类.py 里的指标陷阱这套资源里专门放了一个 demo_2分类.py把问题简化成正常/攻击二分类。二分类场景下 Accuracy 更有参考价值因为正负样本相对接近。但 UNSW-NB15 原本有 10 类标签Normal 加 9 种攻击多分类时小类攻击样本很少单纯看 Accuracy 会出现“Shellcode 全部漏掉acc 依然 95%”的假象。所以建议用宏平均 F1 和加权 F1 两个数配合看再打印混淆矩阵。场景常用指标原因二分类Accuracy、Recall正负样本接近Accuracy 能反映总体好坏Recall 看漏报多分类macro F1、weighted F1、Confusion Matrix少数类样本稀少加权平均更能反映小类检测能力我第一次跑这个项目时二分类 demo 的 Accuracy 轻松上 90%满心以为大功告成结果多分类一跑Worms 这个类别的 F1 不到 0.1。后面才反应过来是评估方式没对上小类样本全被淹没在多数类里。这也是为什么建议先跑 demo_2分类.py 建立感性认识再上多分类看详细报告。4. CNN 与 LSTM把特征矩阵和流量窗口喂给深度模型4.1 深度模型在 IDS 里的价值自动特征提取与序列依赖随机森林再强也依赖人工特征工程而深度学习可以直接从原始特征里做组合。CNN 擅长捕捉局部关联把每条连接的几十个特征看成一维序列卷积核在特征维度上滑动自动学习相邻特征的组合模式比如“目标端口 连接状态 字节数”三者之间的协同关系。LSTM 则更进一步它天然适合处理时序攻击行为往往不是单条连接能刻画的扫描、爆破、慢速 DDoS 都会在时间维度上留下规律。如果只把每条连接当作独立样本这类时序信息就全丢了。资源里既有cnn_pytorch.py这种完整独立的 PyTorch 实现也有拆成cnn_train.py/cnn_predict.py的训练预测组合。PyTorch 版本的好处是模型结构透明断点好查适合课程设计里要解释每一层作用的情况。如果你只想快速出结果直接用cnn_train.py跑默认配置即可。4.2 cnn_train.py 的模型结构一维卷积处理特征向量的维度推演不管是哪个脚本核心 CNN 结构都长差不多。下面是一个典型的一维卷积分类网络import torch import torch.nn as nn class IDS_CNN(nn.Module): def __init__(self, n_features49, num_classes10): super().__init__() self.conv1 nn.Conv1d(1, 64, kernel_size3, padding1) self.conv2 nn.Conv1d(64, 128, kernel_size3, padding1) self.pool nn.MaxPool1d(2) self.fc1 nn.Linear(128 * (n_features // 4), 256) self.fc2 nn.Linear(256, num_classes) def forward(self, x): # 输入形状 (batch, n_features) - (batch, 1, n_features) x x.view(x.size(0), 1, -1) x torch.relu(self.conv1(x)) x torch.relu(self.conv2(x)) x self.pool(x) x x.view(x.size(0), -1) x torch.relu(self.fc1(x)) return self.fc2(x)第一行x.view(x.size(0), 1, -1)把每条连接的特征向量 reshape 成(batch, 1, n_features)1是 Conv1d 要求的输入通道数。两个卷积层都用了kernel_size3, padding1所以特征长度经过卷积后保持不变nn.MaxPool1d(2)会把长度减半所以 flatten 后是128 * (n_features // 2)我代码里写n_features // 4是因为两轮下采样之后的长度。如果n_features是奇数// 4会算出误差这是我实际踩过的坑——后来改成用nn.AdaptiveAvgPool1d(1)直接压成 1省掉所有手算维度的麻烦。损失函数用nn.CrossEntropyLoss()优化器用 Adam学习率我习惯设1e-3batch_size 从 128 开始调。4.3 lstm_train.py 的序列构造滑动窗口、步长与标签对齐LSTM 的输入要求是(batch, seq_len, n_features)也就是每个样本是一段连续连接序列。问题在于原始 CSV 里每一行是一条独立连接没有天然的“会话”概念。常见做法是滑动窗口把连续 N 条连接拼成一个窗口标签取窗口最后一条连接的标签代表“用过去 N 条预测当前这一条的状态”。import numpy as np def build_sequences(df, seq_len8, feat_colsNone): feat_cols feat_cols or [c for c in df.columns if c ! label] vals df[feat_cols].values labels df[label].values.reshape(-1) seqs, seq_labels [], [] for i in range(len(vals) - seq_len 1): seqs.append(vals[i:i seq_len]) seq_labels.append(labels[i seq_len - 1]) return np.array(seqs), np.array(seq_labels) X_seq, y_seq build_sequences(df, seq_len8)seq_len8是经验值跟数据采样密度有关如果流量是按秒级别采集的8 条连接大约对应几秒钟的行为窗口足够覆盖大多数扫描型攻击的短时特征。如果窗口太长训练样本数会骤减因为总长度减去窗口后才是一个样本窗口太短又学不到时序规律。这个参数我一般会尝试 4、8、16 三档看验证集 F1 再定。LSTM 网络本身可以简单一些两到三层堆叠每层 64 或 128 个隐藏单元层之间加 Dropout(0.3)最后接全连接输出类别。训练时特别注意 LSTM 比 CNN 慢很多第一次实验先用原始数据的一小部分比如 5 万条把流程跑通确认 loss 在下降之后再上全量。许多人一上来就全量训练 LSTM跑了一晚上发现 learning rate 设置不合理白白浪费时间。5. 避坑手册这条源码工程最容易翻车的五个运行与评估问题5.1 环境与数据链路三个“跑不起来”的典型现场问题一python cnn_train.py报 FileNotFoundError。现象脚本启动后直接提示找不到train.csv或Process_data目录。原因最常见是先解压了源码包但没有解压数据压缩包另一个高频原因是 Windows 下路径里带中文pandas 读取时变成乱码路径。解决把整个工程放到纯英文目录下先unzip Process_data.zip再运行如果还报错打开脚本看它 read_csv 的路径是相对路径还是绝对路径把它改成你实际解压后的相对路径。问题二numpy.core.multiarray failed to import之类的报错。现象requirements.txt 按说明装了但一导入 sklearn 或者 torch 就崩。原因本地环境原来装过高版本 numpypip 检查依赖时认为“已满足要求”没按 requirements.txt 重新安装导致 sklearn 或 torch 与 numpy 的 ABI 不兼容。解决老老实实建一个干净的虚拟环境在虚拟环境里执行pip install -r requirements.txt。如果你用的 PyCharm直接把解释器切到新建的 venv不要用全局环境硬跑。问题三数据读进来全是 NaN或者类别列全是空。现象打印df.info()数值列明明有数据却显示 object 类型label列大量 NaN。原因CSV 里有空行或异常分隔符有些行的字段数跟表头不一致pandas 强行读进来会自动把整列转成 object。解决读文件时加上enginepython和on_bad_linesskip参数先跳过异常行或者直接用Process_data.zip里已经清洗好的版本不要拿原始大 CSV 硬刚。5.2 训练与评估两个“看起来没问题”的隐性翻车问题四loss 不下降或者训练几步就变 NaN。现象CNN/LSTM 的 loss 曲线在 0.7 附近横着走甚至直接变 inf。原因大概率没跑 Min-max.py 就把数据喂给网络了。某些特征字段比如流量字节数原始范围在几十万神经网络第一层权重更新时梯度直接爆炸另一可能是 batch_size 设太大内存不够导致部分样本被丢弃。解决先跑一遍归一化流程再训练batch_size 从 128 降到 64 或 32学习率从默认1e-3降到3e-4。这三个动作按顺序做90% 的 loss 问题都能解决剩下的再用 TensorBoard 看梯度分布。问题五Accuracy 很高但分类报告里少数类 F1 惨不忍睹甚至 LSTM 每次跑结果都不一样。现象classification_report里加权 F1 和 Accuracy 差距超过 20 个百分点或者同一次代码跑两次测试集结果波动超过 5%。原因Accuracy 被多数类带偏少数类样本太少导致模型“选择性失明”可复现性问题则是随机种子没固定数据切分没分层PyTorch 初始化本身带随机性。解决评估一定打印classification_report重点看 macro F1训练前固定三个种子——random.seed(42)、np.random.seed(42)、torch.manual_seed(42)切分数据统一用stratifyy。如果少数类还是太少回到 Process_Imbanlce.py 把平衡流程跑一遍再看结果。6. 把模型用起来predict 脚本、结果解读与陌生数据集上的迁移习惯6.1 先跑通最小 demo再启动全量训练拿到这个工程后我每次的第一步都是先跑demo_2分类.py。它把问题简化成二分类数据量小、训练快专门用来验证环境、验证预处理链路、验证预测接口。demo 跑通说明依赖版本和数据路径没问题再上RFP.py跑随机森林基准最后才碰cnn_train.py/lstm_train.py这类全量训练。很多人在 demo 都没跑通的情况下直接开全量 LSTM浪费半天时间才想起来环境不对。资源里已经准备好了cnn_predict.py、lstm_predict.py和classify.py训练完直接对test.csv做推理。预测脚本最怕遇到一个问题训练时用的特征列和预测时不一致。特别是预处理阶段做过特征筛选后keep_cols 列表要同步保存否则新数据进来列数对不上模型根本没法前向传播。我一般会把筛选后的列名用joblib.dump存成feature_cols.pkl预测时直接加载。6.2 从预测脚本到可解释结果混淆矩阵、漏报率与逐类 F1训练结束后结果解读要有三层。第一层打印混淆矩阵看模型具体把哪些类别互相认错第二层看逐类 F1重点关注少数攻击类别有没有被“清零”第三层才是整体 Accuracy。对入侵检测来说漏报比误报更致命所以我会单独计算每个类别的 recallrecall 过低说明这类攻击基本被漏掉而混淆矩阵里那一行就是重灾区。from sklearn.metrics import confusion_matrix, classification_report y_pred model.predict(X_test) print(confusion_matrix(y_test, y_pred)) print(classification_report(y_test, y_pred, digits4))如果要把模型迁移到自己的数据集上处理流程必须原样走一遍解压 → 列名对齐 → 类别编码 → 归一化 → 特征筛选 → 训练切分时stratify。只要跳过任何一个环节模型的预测结果基本就等于随机猜。从那以后我拿到任何机器学习源码工程都强制自己先跑一遍最小 demo再看数据分布最后才碰全量训练这个顺序帮我省了大量排错时间。希望今天这一篇帮到你。本文还有配套的精品资源点击获取