
简介这套Python实现的基于CNN网络入侵检测项目源码与文档说明适合深度学习入门、网络安全方向课设或期末大作业的本科生及自学者。项目难度适中代码经过本地编译验证完整覆盖NSL-KDD数据集预处理、CNN模型定义、训练与预测流程并附有README说明与实操文档可帮助理解入侵检测任务的建模思路与代码组织方式。压缩包共33个文件约21.58MB主要包含4个Python源码文件、12个cvs数据文件、7个xml工程配置文件、1个pth模型权重文件以及准确率曲线图、混淆矩阵图表等辅助材料源码按数据预处理、模型构建、训练、预测模块拆分注释清晰、易读易改。数据预处理脚本和模型训练代码均可直接运行支持自行调节学习率、卷积核等参数复用于其他二分类或多分类任务也可作为期末答辩时的完整演示项目。目前已有355人学习下载适合希望快速参考完整实现或作为高分期末作业模板的读者。1. 用 Python 和 CNN 做网络入侵检测这门课设为什么值得认真做网络入侵检测系统NIDS一直是网络安全领域的热门方向而用 Python 实现基于 CNN卷积神经网络的入侵检测模型正是许多高校网络工程、信息安全专业期末大作业的高频选题。这类项目源码加上文档说明被打包成 zip 发放本质上是在考察学生能否把深度学习理论落地到真实流量数据上而不是停留在调库层面。打开这个压缩包你大概率会看到 KDD Cup 1999 或 NSL-KDD 数据集的预处理脚本、CNN 模型定义文件、训练与评估代码以及一份说明文档。这个方向能解决的问题很明确传统入侵检测系统依赖专家规则和特征匹配对未知攻击的泛化能力差而 CNN 能自动从流量特征中提取空间相关性把分类任务交给网络自己学习。适合的人群包括正在做课程设计的学生、想入门深度学习在安全领域应用的开发者以及需要快速搭建一个可演示 IDS 原型的工程师。接下来的内容我会从数据集处理讲到模型调参再讲踩过的坑尽量让你拿到源码后能直接跑通、能讲清楚、能应付答辩。2. 入侵检测与 CNN 的契合点为什么流量数据适合用卷积网络2.1 从特征工程到自动特征提取的转变传统入侵检测的做法是先定义大量规则比如某个端口短时间内连接次数超过阈值就判定为扫描攻击。这种方式依赖人工经验规则覆盖不全时漏报率很高。后来发展到机器学习方法用 SVM、随机森林等算法对特征向量分类但特征仍需要人工挑选。CNN 的思路完全不同它把原始特征当作一种空间结构来处理通过卷积核在特征维度上滑动自动学习局部组合特征。网络流量数据经过预处理后每条记录通常是几十个数值特征协议类型、源字节数、目的字节数、连接状态等。这些特征看似是平铺的一维向量但相邻特征之间存在某种关联——比如同一个 TCP 连接中的包长、标志位、持续时长在空间上相邻这种局部模式恰好是 CNN 擅长捕捉的。把一维特征向量 reshape 成合适的形状喂给卷积层卷积核就能提取出特征之间的短程依赖关系这比全连接网络直接学习所有特征组合要高效得多。2.2 CNN 处理一维序列输入的常见结构对于 NIDS 这种输入是一维特征向量的任务常见做法是使用一维卷积Conv1D而不是图像领域常用的 Conv2D。一个典型的网络结构包括输入层接受 shape 为 (seq_len, feature_dim) 或 (seq_len,) 的数据经过几个 Conv1D 层提取特征每层后接 BatchNormalization 和 MaxPooling1D 做归一化和降采样最后接全局池化或展平层再通过全连接层输出分类概率。代码骨架大概是这样的from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Conv1D, MaxPooling1D, Flatten, Dense, Dropout, BatchNormalization def build_cnn_model(input_shape, num_classes): model Sequential([ Conv1D(filters64, kernel_size3, activationrelu, input_shapeinput_shape), BatchNormalization(), MaxPooling1D(pool_size2), Conv1D(filters128, kernel_size3, activationrelu), BatchNormalization(), MaxPooling1D(pool_size2), Flatten(), Dense(128, activationrelu), Dropout(0.5), Dense(num_classes, activationsoftmax) ]) model.compile(optimizeradam, losscategorical_crossentropy, metrics[accuracy]) return model这段代码里input_shape 需要根据特征维度确定。如果每条样本有 41 个特征NSL-KDD 标准可以 reshape 成 (41, 1)即把每个特征当作一个时间步Conv1D 在特征序列上滑动。filters 表示卷积核数量决定提取多少种局部模式kernel_size 表示卷积核覆盖的相邻特征个数一般取 3 或 5太小感受野不足太大容易过拟合。BatchNormalization 加速收敛并稳定训练Dropout 防止过拟合这些做课设答辩时都是加分点。2.3 二分类还是多分类取决于数据集标签定义KDD Cup 1999 和 NSL-KDD 的标签分为正常Normal和四类攻击DoS、Probe、R2L、U2R。做课设时先确认文档里要求的是二分类正常 vs 异常还是五分类正常 四类攻击。二分类简单准确率容易做高但答辩时老师常问各类攻击的检出率分别是多少这时只看总体准确率是不够的五分类任务中 R2L 和 U2R 样本量极少是典型的类别不平衡问题需要额外处理。选择哪种分类粒度直接决定模型输出层神经元数量和损失函数。多分类用 softmax 和 categorical_crossentropy二分类用 sigmoid 和 binary_crossentropy。建议文档说明里明确写清楚实验设定把混淆矩阵和每类别的精确率、召回率都打出来这比只给一个准确率数字有说服力得多。3. 数据预处理与特征编码决定模型上限的关键一步3.1 NSL-KDD 数据集的加载与清洗NSL-KDD 是 KDD Cup 1999 的改进版本去掉了冗余记录训练集和测试集的分布更合理。拿到源码包后第一步通常是加载 CSV 格式的 KDDTrain.csv 和 KDDTest.csv。每条记录有 41 个特征和 1 个标签特征分为三类连续数值型如 duration、src_bytes、离散符号型如 protocol_type、service、flag、以及二进制型。加载代码通常长这样import pandas as pd column_names [ duration, protocol_type, service, flag, src_bytes, dst_bytes, land, wrong_fragment, urgent, hot, # ... 省略部分特征名 dst_host_srv_count, dst_host_same_srv_rate, dst_host_diff_srv_rate, dst_host_same_src_port_rate, dst_host_srv_diff_host_rate, dst_host_serror_rate, dst_host_srv_serror_rate, dst_host_rerror_rate, dst_host_srv_rerror_rate, label ] train_df pd.read_csv(KDDTrain.csv, namescolumn_names) test_df pd.read_csv(KDDTest.csv, namescolumn_names) print(f训练集形状: {train_df.shape}, 测试集形状: {test_df.shape}) print(train_df[label].value_counts())这里需要注意KDD 数据集的原始 CSV 没有表头必须手动指定列名。如果列名数量和 CSV 实际列数不匹配pandas 会报错常见的翻车点在最后有一列difficulty_levelNSL-KDD 特有读数据时要么把这一列也加进列名要么用usecols忽略它。建议先打印train_df.shape确认列数不要想当然。3.2 符号特征编码LabelEncoder 还是 OneHotEncoderprotocol_type、service、flag 这三个特征是离散字符串不能直接喂给 CNN。常见的处理方式有两种如果每个符号特征的取值种类少如 protocol_type 只有 3 种取值用 OneHot 编码展开成多列如果取值很多service 有 70 种左右用 LabelEncoder 转成整数再归一化。但这里有个坑LabelEncoder 给类别赋予的整数大小是随机的比如 tcp0、udp1、icmp2这个大小关系没有实际意义CNN 的卷积核会把它当作真实数值关系来学习效果往往不如 OneHot。我一般会这样做from sklearn.preprocessing import LabelEncoder # 方法一对取值少的特征做 OneHot train_df pd.get_dummies(train_df, columns[protocol_type, flag]) # 方法二对取值多的特征做 LabelEncoder 并归一化 le LabelEncoder() for col in [service]: train_df[col] le.fit_transform(train_df[col]) test_df[col] le.transform(test_df[col])但这里有个严重的问题训练集和测试集的类别取值可能不一致。比如训练集里 service 有 70 种测试集里可能出现训练集没见过的 service 值。如果直接用训练集拟合的 LabelEncoder 去 transform 测试集sklearn 会直接抛异常。解决方法是先合并两个数据集做编码或者对测试集中出现的新类别统一映射到一个未知类再在文档里说明这种处理的理由。这个细节在答辩时很加分因为它体现了对真实场景中分布漂移问题的理解。3.3 数值特征归一化与训练集测试集泄漏41 个特征里数值型特征的量纲差异巨大src_bytes 可能是 0 到几百万而 land 只有 0 或 1。如果不归一化卷积层的梯度更新会被大数值特征主导模型很难收敛。标准做法是用 StandardScaler 或 MinMaxScaler但必须只在训练集上拟合 scaler然后同时 transform 训练集和测试集。如果先对全量数据做归一化再划分训练集测试集会造成数据泄漏测试集的信息被模型间接看到了评估结果虚高。from sklearn.preprocessing import StandardScaler scaler StandardScaler() train_numeric_cols train_df.select_dtypes(include[float64, int64]).columns train_df[train_numeric_cols] scaler.fit_transform(train_df[train_numeric_cols]) test_df[train_numeric_cols] scaler.transform(test_df[train_numeric_cols])这段代码里scaler 只在 train_df 上 fittest_df 只做 transform。注意select_dtypes会把标签列也选进去如果标签是字符串没问题如果是整数编码就需要先剔除 label 列。处理完特征后把标签单独拆出来做 one-hot 编码用于多分类模型训练。最后要把数据 reshape 成 CNN 输入格式通常是(样本数, 特征维度, 1)。4. 模型训练与评估把准确率做上去的完整流程4.1 数据集划分不要只留一个测试集拿到源码后常见的做法是直接把 KDDTrain 当训练集、KDDTest 当测试集然后开始训练。但这样做有一个问题你无法在训练过程中验证模型效果超参数调优没有依据。更好的流程是从训练集里再切出 20% 作为验证集训练时监控验证集准确率和损失防止过拟合。from sklearn.model_selection import train_test_split X_train, X_val, y_train, y_val train_test_split( X_train_raw, y_train_raw, test_size0.2, random_state42, stratifyy_train_raw ) X_train X_train.reshape(X_train.shape[0], X_train.shape[1], 1) X_val X_val.reshape(X_val.shape[0], X_val.shape[1], 1)stratifyy_train_raw是按类别比例分层抽样保证验证集里各个攻击类型的占比和训练集一致。这对 NSL-KDD 尤其重要因为 R2L 和 U2R 样本极少如果不分层抽样验证集里可能一个 U2R 样本都分不到验证指标完全失真。4.2 训练循环与 EarlyStoppingCNN 在 NSL-KDD 上训练比较容易过拟合因为训练集规模只有几万条且类别不平衡严重。常见做法是设置早停当验证集损失连续多个 epoch 不再下降时停止训练并用回调保存最佳模型。from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint callbacks [ EarlyStopping(monitorval_loss, patience10, restore_best_weightsTrue), ModelCheckpoint(best_model.h5, monitorval_accuracy, save_best_onlyTrue) ] history model.fit( X_train, y_train, validation_data(X_val, y_val), epochs100, batch_size256, callbackscallbacks, verbose1 )patience10表示连续 10 个 epoch 验证损失没改善就停。restore_best_weightsTrue会把模型权重回滚到验证集表现最好的那一轮而不是停在最后一个 epoch。batch_size 取 256 在大多数 GPU 或 CPU 上都能接受太大容易收敛慢太小则梯度噪声大、训练振荡。如果训练时发现验证损失震荡剧烈优先减小学习率或增大 batch_size。4.3 评估指标准确率之外必须看混淆矩阵课设报告里如果只写准确率 99.2%答辩老师大概率会追问对 U2R 攻击的检出率是多少NSL-KDD 测试集中 U2R 样本极少很多模型对其检出率为 0但总体准确率依然很高这就是准确率指标的欺骗性。必须输出混淆矩阵、各类别的 precision、recall、F1-score。from sklearn.metrics import classification_report, confusion_matrix import numpy as np y_pred model.predict(X_test) y_pred_class np.argmax(y_pred, axis1) y_true_class np.argmax(y_test, axis1) print(classification_report(y_true_class, y_pred_class, target_nameslabel_names)) print(confusion_matrix(y_true_class, y_pred_class))看报告时重点关注 R2L 和 U2R 两行的 recall。如果这两行数字很低说明模型基本没学会少数类攻击模式这时候需要回头处理类别不平衡。一个简单的改进是class_weight让少数类在 loss 计算中拥有更高权重。另一个是过采样少数类样本但要注意别在测试集上做任何采样。4.4 类别不平衡的两种处理方案对比处理 NSL-KDD 类别不平衡课设中最常用两种做法class_weight 和少数类过采样。class_weight 不改数据只在训练时加大少数类的 loss 占比实现简单。from sklearn.utils.class_weight import compute_class_weight class_weights compute_class_weight( balanced, classesnp.unique(y_train_raw), yy_train_raw ) class_weight_dict dict(enumerate(class_weights))过采样则用 imbalanced-learn 库的 SMOTE 生成少数类合成样本但 SMOTE 是基于特征空间插值的对表格型数据效果尚可对类别型特征多的数据可能生成矛盾的样本。做课设时用 class_weight 更安全答辩时讲清楚原理就够如果追求更好的少数类检出率再尝试 SMOTE 并对比两组实验结果。5. 常见坑与避坑指南课设翻车现场复盘5.1 特征维度不匹配导致模型无法编译现象输入模型时报错Input 0 of layer conv1d is incompatible提示期望三维输入但实际给了两维。原因Conv1D 要求输入是(batch_size, steps, channels)而很多人在预处理后直接拿二维数组(样本数, 特征数)去喂模型忘记 reshape。解决在 fit 之前执行X_train X_train.reshape(-1, n_features, 1)第三维设为 1 表示单通道。这个错误在第一次跑代码时几乎必现源码包里的脚本如果没处理手动补上即可。5.2 训练集和测试集类别不一致导致 transform 崩溃现象执行测试集 LabelEncoder transform 时报unknown class错误。原因测试集中某个 service 协议在训练集中从未出现编码器不认识。解决合并训练集测试集后统一 fit或对测试集中的新类别做未知类映射。课设报告里如果提到这个处理说明考虑了真实流量的开放性问题是加分项。另一个相关坑是 OneHot 编码后训练集和测试集列数不同原因同样是新类别产生了新列用pd.concat后统一编码能根治。5.3 训练准确率不升反降或直接 NaN现象loss 在几个 epoch 后变成 NaN或准确率一直徘徊在基线水平。原因学习率过大导致梯度爆炸或数值特征没有归一化或激活函数选择不当。解决先把学习率降到 0.0001 试试确认归一化已执行检查数据里是否有无穷大或空值。加 BatchNormalization 层能缓解梯度问题。另外要注意NSL-KDD 中有少量特征的取值极端分布如 src_bytes 的右偏只用 StandardScaler 有时不如取 log 变换稳定这属于进阶调参技巧。5.4 准确率高但攻击检出率极低现象总准确率 99%但 U2R 和 R2L 的 recall 接近于 0。原因类别极度不平衡模型把所有样本都预测为多数类就是最优策略。解决使用 class_weight 加重少数类权重或在训练时对少数类做重复采样。评估时不要只看准确率输出 macro-F1 或加权 F1这几个指标对不平衡更敏感。如果答辩时老师问为什么 U2R 检不出回答思路是U2R 样本只有几十条特征空间被正常流量淹没模型没有足够的判别信息这是数据层面的问题不是模型结构的问题。5.5 结果无法复现现象同一个脚本多次运行准确率波动明显。原因没有固定随机种子模型初始化随机数据划分随机。解决在代码开头固定 numpy、tensorflow 和 Python 内置 random 的种子。import random import numpy as np import tensorflow as tf random.seed(42) np.random.seed(42) tf.random.set_seed(42)课设报告里截图的结果必须可复现不然答辩时老师让你现场跑一遍结果和报告不一样就很尴尬。固定种子后模型训练速度和结果基本一致这是所有深度学习项目中必须养成的习惯。6. 进阶玩法与验证技巧让课设从能跑变成能讲6.1 试试用训练好的模型做离线流量检测演示模型训练完可以写一个小函数接收一条 CSV 格式的流量记录输出预测类型做成命令行工具。def predict_single_record(model, record_df, scaler, encoder): record record_df.copy() for col in [protocol_type, flag]: record pd.get_dummies(record, columns[col]) record record.reindex(columnsfeature_columns, fill_value0) record[numeric_cols] scaler.transform(record[numeric_cols]) record_array record.values.reshape(1, -1, 1) pred model.predict(record_array) return label_names[np.argmax(pred)]这段代码里reindex是核心单挑记录做 OneHot 后列数肯定和训练时不一样用训练时的完整特征列列表对齐缺失列填 0。这样模型才能接受输入。演示时拿测试集里几条已知类型的数据跑一遍输出预测结果比光讲一堆训练曲线更有说服力。6.2 画训练曲线时注意原始记录而不是平滑曲线课设报告里通常会放训练准确率和损失曲线图。画图时用 matplotlib 记录 history 中的原始数据就好关键是把训练集和验证集两条线放一起对比观察过拟合发生的 epoch 位置。如果只画训练集曲线没法展示早停策略的效果。import matplotlib.pyplot as plt plt.plot(history.history[accuracy], labeltrain_acc) plt.plot(history.history[val_accuracy], labelval_acc) plt.xlabel(Epoch) plt.ylabel(Accuracy) plt.legend() plt.savefig(training_history.png, dpi150)图片保存分辨率要够不要截窗口模糊图。文档里左图右图对应说明比如第 20 轮后验证集准确率开始下降说明过拟合出现触发早停回滚到第 18 轮权重。6.3 把你的模型和传统机器学习做横向对比答辩时老师最常问的问题之一为什么用 CNNCNN 比随机森林好在哪建议在文档里补一个对比实验跑一个随机森林或逻辑回归作为基线比较二者在 NSL-KDD 测试集上的准确率和 F1。CNN 在这种表格数据上优势并不明显但特征相关性提取能力比手工特征略强特别是在原始特征不做太多人工构造的情况下。如果不做对比实验老师容易怀疑为什么不用随机森林。实现方式很简单用 sklearn 的 RandomForestClassifier 跑同样的数据然后对比两到三个指标。对比结果不一定要 CNN 全面碾压哪怕 CNN 在 U2R 上多召回几个样本或者总体 F1 高一点都有交代。如果随机森林反而更好那也可以说明传统方法在小样本攻击上仍有优势未来可以考虑 CNN 树模型的集成方案这比硬吹 CNN 合理得多。6.4 文档说明怎么写才打高分我见过很多课设源码里带的文档说明长篇大论贴代码却唯独不写实验结果分析。高分文档至少要有这几块问题定义和数据集描述、数据预处理流程图、模型结构图和每层参数表、超参数设置表、训练曲线图、混淆矩阵与各类别指标表、对比实验表以及遇到的三个问题和解决办法章节。每个表格配一句解释比如从表 4 可以看出U2R 的召回率仅为 0.12主要原因是训练集中该类别只有 52 条样本。这种文档老师没理由扣分。整个项目做下来我最深的感受是CNN 在网络入侵检测上的表现很大程度上取决于数据预处理而不是网络深度。我自己的课设就是从乱吞 Shape 错误开始一步步把代码跑通又因为类别不平衡反复调整最后才把改进过程写成文档。这条路你大概率也要走一遍希望这些踩坑经验能帮你少走几步弯路。希望帮到你。本文还有配套的精品资源点击获取