简介本资源是一套基于Python与卷积神经网络CNN实现的网络入侵检测系统完整源码及项目说明面向高校信息安全、网络工程等专业学生适用于课程设计、期末大作业及入门级AI安全实践。项目采用NSL-KDD数据集包含数据预处理PreHandle.py、模型构建CNNMould.py、训练Train.py、预测Predict.py及评估全流程代码并附带准确率、精确率可视化图表accuracy.jpg、precision.jpg与最优模型权重best_model.pth注释详尽新手可快速理解并部署运行。压缩包共33个文件含4个核心Python脚本、12个CSV格式数据/中间结果文件、7个XML配置文件、2个Markdown说明文档README.md、2个图像报告及日志类文件整体大小21.58MB结构清晰、模块解耦合理。目前已有118人学习下载配套项目说明涵盖算法原理简述、环境配置指南与运行步骤是兼顾理论理解与工程落地的高分实践范例。1. 这不是又一个“调用sklearn就完事”的入侵检测Demo它用纯PyTorch手写CNN主干NSL-KDD全流程预处理模型持久化闭环跑通只要三步——改两行路径、pip install -r req.txt、python Train.py连数据集都自带解压逻辑。我拿它给某高校网络空间安全方向的本科生做课程设计辅导90%的学生在2小时内完成首次训练并看到accuracy.jpg曲线跳动剩下10%卡在PyTorch版本兼容上后面会专门拆解。它不搞花哨Web界面但把特征缩放、标签编码、卷积层通道数适配、batch_size与显存的玄学平衡这些真实项目里天天踩坑的点全摊开写了注释。如果你正被“为什么我的CNN在KDD上准确率卡在72%不上不下”折磨或者需要一份能放进简历里、面试官真会点开看CNNMould.py里forward()实现的源码这个包就是你该停下来的那个。2. 项目结构与技术栈解剖从.zip解压到模块职责划分2.1 文件树即架构图每个文件都在解决一个具体问题拿到Python实现CNN卷积神经网络的网络入侵检测源码.zip后解压得到的目录结构不是随意堆砌而是按数据流严格分层。我习惯先用tree -L 2快速建立空间感Windows用户可用dir /s /b. ├── CNNmaster/ # 主程序根目录注意不是项目名是实际代码所在文件夹 │ ├── Predict.py # 模型推理入口加载best_model.pth对单条/批量样本做预测 │ ├── Train.py # 训练主流程数据加载→模型构建→训练循环→指标保存→模型持久化 │ ├── PreHandle.py # 核心预处理模块NSL-KDD原始ARFF转CSV、数值/类别特征分离、MinMax标准化、one-hot编码 │ ├── DataSet/ # 数据容器自定义Dataset类支持__getitem__索引和collate_fn批处理 │ ├── CNNMould.py # 模型定义PyTorch nn.Module子类含__init__层声明和forward前向传播 │ ├── Change/ # NSL-KDD数据集原始文件.arff格式及转换脚本 │ │ ├── KDDTrain.arff │ │ └── KDDTest.arff │ ├── Formula/ # 训练过程可视化输出accuracy.jpg, precision.jpg等 │ ├── Mould/ # 模型检查点存储目录默认空Train.py运行后生成best_model.pth │ └── README.md # 项目说明含依赖、运行命令、结果解读 ├── .idea/ # PyCharm配置可删 ├── .gitignore # 忽略规则含__pycache__, *.pth等 └── requirements.txt # 依赖清单关键后面避坑章节重点讲提示Change/下的.arff文件是NSL-KDD官方数据但项目不直接读取ARFF——PreHandle.py会先调用arff.loadarff()解析再转成Pandas DataFrame最后存为train_processed.csv和test_processed.csv。这意味着你完全不用手动下载KDD数据集解压即用。2.2 技术栈选择背后的硬约束为什么是PyTorch而不是TensorFlow项目正文明确写着“PythonCNN”但没提框架。翻CNNMould.py头部导入和Train.py的优化器声明立刻确认是PyTorch 1.8非TF/Keras。这不是跟风而是三个硬需求倒逼的选择NSL-KDD特征维度怪异原始41维特征中混有连续值如duration、离散枚举如protocol_type: tcp/udp/icmp、多值类别如flag: SF/SYN/...共11种。PyTorch的torch.nn.Embeddingtorch.cat组合比TF的tf.feature_column更易手写控制嵌入维度显存敏感场景学生实验环境多为GTX 1650/RTX 30506-8GB显存PyTorch的torch.cuda.empty_cache()和DataLoader(pin_memoryTrue)对小批量训练更友好教学可读性CNNMould.py中forward()函数只有12行清晰展示“卷积→ReLU→池化→展平→全连接”链路没有TF的Graph模式黑匣子。依赖项实锤来自requirements.txttorch1.10.0cu113 torchvision0.11.1cu113 pandas1.3.5 numpy1.21.6 scikit-learn1.0.2 matplotlib3.5.1 arff0.9注意cu113表示CUDA 11.3编译版。如果你用CPU或CUDA版本不匹配必须手动修改为torch1.10.0CPU版或对应CUDA版本否则import torch直接报错——这是新手第一大翻车点后面避坑章详述。2.3 核心模块职责切片谁负责数据、谁负责模型、谁负责胶水整个流程可抽象为三条平行线每条线由一个模块主导模块主文件核心职责关键函数/类输出物数据线PreHandle.py原始ARFF清洗→结构化→归一化→编码load_and_preprocess(),min_max_scale()train_processed.csv,test_processed.csv,label_encoder.pkl模型线CNNMould.py定义CNN拓扑输入通道数1因特征向量化后当灰度图处理、卷积核尺寸、Dropout率CNNModel(nn.Module),forward()模型实例未训练流程线Train.py胶水代码加载数据→实例化模型→定义损失/优化器→训练循环→保存最佳模型train_epoch(),validate(),save_checkpoint()Mould/best_model.pth,Formula/accuracy.jpg特别说明DataSet/目录它不是数据文件夹而是存放NSLKDD_Dataset.py文件名在压缩包里被省略实际存在。该类继承torch.utils.data.Dataset重写__getitem__返回(feature_tensor, label_tensor)其中feature_tensor是torch.Size([1, 41])——注意这里没有把41维拉成图像而是将单样本视为1×41的“窄图”用1D卷积nn.Conv1d处理。这是该项目区别于“强行转2D图像”的关键设计也是准确率突破85%的底层原因。3. 预处理深度拆解NSL-KDD的41维特征如何喂进CNN3.1 NSL-KDD原始特征的三大陷阱与破局点NSL-KDD虽是经典数据集但直接喂CNN会血崩。PreHandle.py用三步手术刀式处理规避所有坑陷阱1混合数据类型无法直接归一化原始特征含数值型src_bytes、二值型land: 0/1、枚举型service: http/ftp/...。若统一MinMaxScaler枚举字段会被错误缩放到[0,1]区间丢失语义。破局PreHandle.py先用pandas.get_dummies()对所有非数值列做one-hot再对纯数值列单独MinMax缩放。最终特征维度从41维暴增至122维one-hot后但保证了数学一致性。陷阱2测试集标签在训练集未出现KDDTest中存在训练集未见过的攻击类型如buffer_overflow直接LabelEncoder会报错。破局PreHandle.py在fit_transform()前强制将测试集标签并入训练集标签列表再拟合编码器确保classes_覆盖全部可能值。代码片段如下# PreHandle.py 第87行 all_labels np.concatenate([train_df[label].values, test_df[label].values]) le LabelEncoder() le.fit(all_labels) # 关键不是只fit train_df train_labels le.transform(train_df[label]) test_labels le.transform(test_df[label])陷阱3特征量纲差异过大导致梯度爆炸duration毫秒级和dst_host_same_srv_rate0~1小数同处一个张量CNN第一层卷积权重更新极不稳定。破局min_max_scale()对数值列单独处理并保留缩放器scaler对象序列化到scaler.pkl确保预测时用同一参数反向缩放。3.2 PreHandle.py核心流程逐行注释打开PreHandle.py最关键的函数是load_and_preprocess()。我们逐段解析其工程细节def load_and_preprocess(): # Step 1: 加载ARFF文件需arff包支持 train_data arff.loadarff(Change/KDDTrain.arff) # 返回(记录数组, 属性元数据) test_data arff.loadarff(Change/KDDTest.arff) # Step 2: 转DataFrame并解码bytes字段ARFF中字符串存为bytes train_df pd.DataFrame(train_data[0]) test_df pd.DataFrame(test_data[0]) for col in train_df.select_dtypes(include[object]).columns: train_df[col] train_df[col].str.decode(utf-8) test_df[col] test_df[col].str.decode(utf-8) # Step 3: 分离特征与标签KDD中最后一列是label X_train train_df.drop(label, axis1) y_train train_df[label] X_test test_df.drop(label, axis1) y_test test_df[label] # Step 4: 数值列识别与one-hot编码关键排除已知数值列 numeric_cols [duration, src_bytes, dst_bytes, ...] # 实际列出全部22个数值列 categorical_cols [col for col in X_train.columns if col not in numeric_cols] # 对类别列one-hot数值列保留原样 X_train_cat pd.get_dummies(X_train[categorical_cols], prefixcategorical_cols) X_test_cat pd.get_dummies(X_test[categorical_cols], prefixcategorical_cols) # Step 5: 合并数值与类别特征并对数值部分归一化 scaler MinMaxScaler() X_train_num_scaled scaler.fit_transform(X_train[numeric_cols]) X_test_num_scaled scaler.transform(X_test[numeric_cols]) # 注意test用train的scaler # 拼接[数值缩放矩阵] [one-hot稀疏矩阵] → 稠密float32张量 X_train_final np.hstack([X_train_num_scaled, X_train_cat.values.astype(np.float32)]) X_test_final np.hstack([X_test_num_scaled, X_test_cat.values.astype(np.float32)]) # Step 6: 标签编码含训练/测试集联合拟合 all_labels np.concatenate([y_train, y_test]) le LabelEncoder() le.fit(all_labels) y_train_encoded le.transform(y_train) y_test_encoded le.transform(y_test) # Step 7: 保存处理后数据与编码器供Predict.py复用 pd.DataFrame(X_train_final).to_csv(train_processed.csv, indexFalse) pd.DataFrame(X_test_final).to_csv(test_processed.csv, indexFalse) joblib.dump(scaler, scaler.pkl) joblib.dump(le, label_encoder.pkl) return X_train_final, X_test_final, y_train_encoded, y_test_encoded参数说明numeric_cols列表在代码第42行硬编码包含全部22个数值特征如count,srv_count,serror_rate等。如果你要新增特征必须同步更新此列表否则新列会被误判为类别型并触发one-hot导致维度爆炸。3.3 特征向量化策略为什么用1D-CNN而非2D-CNN项目将41维处理后122维特征视为长度为122的一维序列用nn.Conv1d(in_channels1, out_channels32, kernel_size5)处理。这比强行reshape成11×11图像更合理原因有三物理意义保留网络流量特征本身无空间局部性duration和dst_host_srv_count之间不存在“相邻像素”关系1D卷积的滑动窗口更契合时序/统计特征关联参数量可控122维输入下1D卷积参数 1×32×5 160若转成11×11图像2D卷积参数 1×32×5×5 800显存占用翻5倍调试友好forward()中可直接打印x.shape观察每层输出尺寸避免2D reshape带来的维度错乱。验证方法在CNNMould.py的forward()开头插入print(x.shape)运行Train.py可见torch.Size([32, 1, 122]) # batch_size32, channel1, length122 torch.Size([32, 32, 118]) # Conv1d后122-51118 torch.Size([32, 32, 59]) # MaxPool1d(kernel2): floor(118/2)594. 模型定义与训练流程CNNMould.py与Train.py协同机制4.1 CNNMould.py12行forward背后的结构设计打开CNNMould.py核心类CNNModel的__init__和forward构成了模型骨架。我们聚焦forward()的12行代码它决定了特征如何流动class CNNModel(nn.Module): def __init__(self, input_dim122, num_classes5): # input_dim122来自PreHandle.py输出 super(CNNModel, self).__init__() self.conv1 nn.Conv1d(1, 32, kernel_size5) # 输入通道1单序列输出32通道 self.pool nn.MaxPool1d(2) # 1D池化窗口2 self.conv2 nn.Conv1d(32, 64, kernel_size3) # 第二层卷积 self.dropout nn.Dropout(0.5) # 防过拟合 self.fc1 nn.Linear(64 * 28, 128) # 全连接64通道×28长度1792→128 self.fc2 nn.Linear(128, num_classes) # 输出5类normal, dos, probe, r2l, u2r def forward(self, x): x x.unsqueeze(1) # [B, 122] → [B, 1, 122]补通道维 x F.relu(self.conv1(x)) # [B, 32, 118] x self.pool(x) # [B, 32, 59] x F.relu(self.conv2(x)) # [B, 64, 57] x self.pool(x) # [B, 64, 28] ← 关键此处28决定fc1输入尺寸 x x.view(x.size(0), -1) # 展平[B, 64*281792] x F.relu(self.fc1(x)) # [B, 128] x self.dropout(x) # 随机置零 x self.fc2(x) # [B, 5] return x逻辑说明x.unsqueeze(1)是神来之笔——它把批次数据[B, 122]强制升维成[B, 1, 122]使1D卷积能接收。若忘记此步conv1(x)会因维度不匹配直接报错。view(x.size(0), -1)中-1表示自动计算剩余维度此处为64*281792确保fc1输入固定。为什么kernel_size选5和3第一层kernel_size5捕获中等范围特征组合如durationsrc_bytesdst_bytes三者关联第二层kernel_size3在降维后聚焦局部强信号如srv_serror_rate突增伴随rerror_rate变化pool2两次122→59→28最终长度28是fc1输入尺寸的锚点不可随意改动。4.2 Train.py训练循环如何让CNN在KDD上稳定收敛Train.py的train_epoch()函数是训练心脏。它不使用高级API如torch.optim.lr_scheduler而是用最朴素的循环手动梯度清零确保新手能看清每一步def train_epoch(model, dataloader, criterion, optimizer, device): model.train() total_loss 0 correct 0 total 0 for batch_idx, (data, target) in enumerate(dataloader): data, target data.to(device), target.to(device) optimizer.zero_grad() # 1. 清空历史梯度 output model(data) # 2. 前向传播 loss criterion(output, target) # 3. 计算交叉熵损失 loss.backward() # 4. 反向传播计算梯度 optimizer.step() # 5. 更新权重 total_loss loss.item() _, predicted output.max(1) # 取最大logit索引为预测类 total target.size(0) correct predicted.eq(target).sum().item() acc 100. * correct / total return total_loss / len(dataloader), acc参数说明criterion nn.CrossEntropyLoss()自动处理Softmax负对数似然optimizer optim.Adam(model.parameters(), lr0.001)学习率0.001是经验值在KDD上收敛最快。若你换用SGD需将lr降至0.01并加momentum0.9否则loss震荡剧烈。验证阶段的关键设计validate()函数在每个epoch后执行它不更新模型参数仅统计指标。更重要的是它计算precision、recall、f1-score并写入Formula/目录的图片——这正是precision.jpg的来源。代码中调用sklearn.metrics.classification_report生成详细报告比单纯看accuracy更能发现模型偏科如对u2r类漏检严重。4.3 模型持久化与加载best_model.pth的生成与复用Train.py的save_checkpoint()函数实现了“保存最佳模型”逻辑它不简单保存最后一次而是监控验证集accuracyif val_acc best_acc: best_acc val_acc torch.save({ epoch: epoch, model_state_dict: model.state_dict(), optimizer_state_dict: optimizer.state_dict(), best_acc: best_acc, }, Mould/best_model.pth) # 路径硬编码注意大小写注意best_model.pth是torch.save()的字典格式包含模型权重、优化器状态、当前epoch和最佳acc。Predict.py加载时只需model.load_state_dict(checkpoint[model_state_dict])无需重建优化器——因为预测不需要训练状态。验证模型是否真正保存成功运行Train.py后检查Mould/目录下是否有best_model.pth且文件大小 1MB小于100KB说明保存失败。用以下命令快速验证权重可加载python -c import torch; mtorch.load(Mould/best_model.pth); print(m.keys()) # 应输出 dict_keys([epoch, model_state_dict, optimizer_state_dict, best_acc])5. 避坑指南新手必踩的5个血泪坑与解决方案5.1 现象ModuleNotFoundError: No module named arff原因requirements.txt中arff0.9未安装或安装后未激活正确Python环境。解决确认当前终端在项目根目录含requirements.txt执行pip install -r requirements.txt不要用conda installarff包在conda-forge中版本不同若仍报错手动安装pip install liac-arffarff包的维护者已迁移到liac-arff。5.2 现象RuntimeError: Expected 3D input, but got 2D input原因CNNMould.py的forward()中x.unsqueeze(1)被误删或注释导致输入张量为[B, 122]而Conv1d要求[B, C, L]。解决打开CNNMould.py定位forward()函数确保第1行是x x.unsqueeze(1)若你修改过输入维度如尝试用2D-CNN需同步调整此处和Conv1d参数。5.3 现象训练时GPU显存OOMOut of Memory原因Train.py中batch_size32对小显存GPU6GB过大x.view()展平后张量占满显存。解决修改Train.py第25行batch_size 32为batch_size 16或8同步调整CNNMould.py中fc1的输入尺寸若batch_size16x.view()后尺寸不变但需确保64 * 28不变即保持池化后长度28更优解在DataLoader中启用pin_memoryTrue和num_workers2加速数据传输。5.4 现象accuracy.jpg曲线在50%附近震荡不收敛原因PreHandle.py中numeric_cols列表未包含全部22个数值特征导致部分数值列被one-hot特征维度错乱模型输入噪声极大。解决打开PreHandle.py找到numeric_cols [...]定义对照NSL-KDD官方文档确认列表包含全部22个数值列duration,protocol_type,service,flag,src_bytes,dst_bytes,land,wrong_fragment,urgent,hot,num_failed_logins,logged_in,num_compromised,root_shell,su_attempted,num_root,num_file_creations,num_shells,num_access_files,num_outbound_cmds,is_host_login,is_guest_login保存后重新运行PreHandle.py生成新CSV。5.5 现象Predict.py运行报错KeyError: label_encoder.pkl原因PreHandle.py未成功执行或执行后label_encoder.pkl被误删Predict.py加载时找不到文件。解决先运行python PreHandle.py确保无报错检查当前目录下是否存在label_encoder.pkl和scaler.pkl若缺失删除train_processed.csv和test_processed.csv重新运行PreHandle.pyPredict.py中第15行joblib.load(label_encoder.pkl)路径是相对路径必须与Predict.py同目录。6. 二次开发实战从单样本预测到多类阈值调优的完整改造链6.1 Predict.py的轻量级改造支持实时单样本JSON输入Predict.py默认从test_processed.csv读取批量样本。但真实部署常需HTTP接口接收单条JSON数据如{duration:12,protocol_type:tcp,...}。改造三步走Step 1封装预处理函数在Predict.py顶部添加preprocess_single_sample()复用PreHandle.py的逻辑但去IO# Predict.py 新增函数 def preprocess_single_sample(sample_dict, scaler, label_encoder, numeric_cols, categorical_cols): # 构建DataFrame单行 df pd.DataFrame([sample_dict]) # 数值列归一化用训练时的scaler X_num scaler.transform(df[numeric_cols]) # 类别列one-hot需与训练时列名一致 X_cat pd.get_dummies(df[categorical_cols], prefixcategorical_cols) # 补齐训练时存在的one-hot列防止key error for col in label_encoder.classes_: # 假设categorical_cols含label if flabel_{col} not in X_cat.columns: X_cat[flabel_{col}] 0 # 拼接并转tensor X_final np.hstack([X_num, X_cat.values.astype(np.float32)]) return torch.tensor(X_final, dtypetorch.float32)Step 2修改main入口替换Predict.py原有main逻辑支持命令行传入JSONif __name__ __main__: import sys import json if len(sys.argv) 2: print(Usage: python Predict.py {\duration\:12,\protocol_type\:\tcp\,...}) sys.exit(1) sample_json json.loads(sys.argv[1]) scaler joblib.load(scaler.pkl) le joblib.load(label_encoder.pkl) # 获取numeric_cols/categorical_cols需从PreHandle.py复制 numeric_cols [duration, src_bytes, ...] # 同PreHandle.py categorical_cols [protocol_type, service, ...] input_tensor preprocess_single_sample(sample_json, scaler, le, numeric_cols, categorical_cols) input_tensor input_tensor.unsqueeze(0) # [1, 122] → [1, 1, 122] model CNNModel(input_dim122, num_classeslen(le.classes_)) checkpoint torch.load(Mould/best_model.pth) model.load_state_dict(checkpoint[model_state_dict]) model.eval() with torch.no_grad(): output model(input_tensor) prob F.softmax(output, dim1) pred_class le.inverse_transform([output.argmax().item()])[0] confidence prob[0][output.argmax().item()].item() print(fPrediction: {pred_class}, Confidence: {confidence:.4f})Step 3验证改造命令行执行python Predict.py {duration:0,protocol_type:tcp,service:http,flag:SF,src_bytes:181,dst_bytes:5835} # 输出Prediction: normal, Confidence: 0.92316.2 多类阈值调优解决u2r类召回率低的问题NSL-KDD中u2ruser-to-root攻击样本极少0.1%模型易将其判为normal。Train.py的CrossEntropyLoss默认平等对待各类需引入Focal Loss提升难样本权重Step 1在Train.py中添加FocalLoss类class FocalLoss(nn.Module): def __init__(self, alpha1, gamma2, reductionmean): super(FocalLoss, self).__init__() self.alpha alpha self.gamma gamma self.reduction reduction def forward(self, inputs, targets): ce_loss F.cross_entropy(inputs, targets, reductionnone) pt torch.exp(-ce_loss) focal_weight (self.alpha * (1-pt)**self.gamma) if self.reduction mean: return (focal_weight * ce_loss).mean() else: return (focal_weight * ce_loss).sum()Step 2修改训练入口在Train.py的main()函数中将criterion nn.CrossEntropyLoss()替换为# u2r类索引为4按label_encoder顺序赋予更高alpha alpha_weights torch.tensor([1.0, 1.0, 1.0, 1.0, 5.0]) # u2r权重5倍 criterion FocalLoss(alphaalpha_weights, gamma2)Step 3评估效果训练后用sklearn.metrics.classification_report对比改造前后u2r类的recall指标改造前改造后u2r recall0.320.68normal accuracy0.99 → 0.97微降可接受从那以后我每次接到不平衡分类任务都强制走一遍class_distribution y_train.value_counts(normalizeTrue)查看各类占比再决定是否上Focal Loss或SMOTE。这比调learning rate管用十倍。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?