KDD99数据上的一维CNN入侵检测实战:从预处理到API部署

发布时间:2026/10/12 20:57:42
KDD99数据上的一维CNN入侵检测实战:从预处理到API部署 简介本资源是一套基于CNN卷积神经网络实现的网络入侵检测完整项目专为计算机相关专业本科生毕业设计与深度学习实战练习者打造聚焦KDD99数据集上的二分类与多分类入侵识别任务兼顾理论可解释性与工程可运行性。压缩包共16个文件含4个核心Python训练/测试脚本如main_cnn.py、cnn_main.py、2个gzip格式的KDD99原始数据集kddcup.data_10_percent.gz等、3个TensorFlow事件日志文件.zjx-24000635、4个XML配置与IDE项目文件以及README.md说明文档和.bak备份文件整体大小17.51MB结构清晰、模块分离明确。已有120人下载学习适合零基础入门深度学习安全应用的学生快速上手。用户可直接运行调试全部源码获取从数据预处理、CNN模型构建、训练可视化到检测结果评估的全流程实践材料并附有导师审定通过的高分98分设计文档涵盖算法原理、实验对比与部署建议切实支撑毕设答辩与能力验证。1. 这不是“调个Keras模型跑个准确率”的玩具项目它用真实KDD99数据一维CNN结构完整训练/测试/日志闭环跑出98分毕业答辩结果——适合正在赶毕设 deadline 的本科生、想补网络流量建模实战的转行者、以及需要可复现IDS baseline 的安全方向初学者你可能已经试过网上搜“CNN 入侵检测”点开十个项目九个是 Jupyter Notebook 里几行model.add(Conv1D(...)) KDD99 标签打乱后 train_test_split最后贴张 92% 准确率截图就收工。但真正卡住你的从来不是“怎么写 CNN 层”而是原始 KDD99 的 41 维特征怎么归一化才不丢攻击语义label 编码时 normal 和 probe 类别权重怎么拉平test 阶段 batch_size1 和 batch_size64 推理结果为什么差 3.7%tfevents 日志里 loss 曲线突然抖动到底是数据 pipeline 漏了 shuffle 还是 multi_logs 目录下某个 .log 文件被截断这份资源不是教学 Demo它是某 211 高校计算机学院网络空间安全方向学生经导师三轮代码审查、两轮数据重采样、一次全量 retrain 后最终答辩得分 98 的完整工程包——含可直接python main.py启动的入口、带注释的handle2.py数据预处理链、cnn_main.py中明确区分 training/inference mode 的模型封装、以及events.out.tfevents.*可视化验证文件。它不教你什么是卷积核但会告诉你当kddcup.data_10_percent.gz解压后第一行出现 0,tcp,http,SF,181,545,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,......## 1. 这不是“调个Keras模型跑个准确率”的玩具项目它用真实KDD99数据一维CNN结构完整训练/测试/日志闭环跑出98分毕业答辩结果——适合正在赶毕设 deadline 的本科生、想补网络流量建模实战的转行者、以及需要可复现IDS baseline 的安全方向初学者你可能已经试过网上搜“CNN 入侵检测”点开十个项目九个是 Jupyter Notebook 里几行model.add(Conv1D(...)) KDD99 标签打乱后 train_test_split最后贴张 92% 准确率截图就收工。但真正卡住你的从来不是“怎么写 CNN 层”而是原始 KDD99 的 41 维特征怎么归一化才不丢攻击语义label 编码时 normal 和 probe 类别权重怎么拉平test 阶段 batch_size1 和 batch_size64 推理结果为什么差 3.7%tfevents 日志里 loss 曲线突然抖动到底是数据 pipeline 漏了 shuffle 还是 multi_logs 目录下某个 .log 文件被截断这份资源不是教学 Demo它是某 211 高校计算机学院网络空间安全方向学生经导师三轮代码审查、两轮数据重采样、一次全量 retrain 后最终答辩得分 98 的完整工程包——含可直接python main.py启动的入口、带注释的handle2.py数据预处理链、cnn_main.py中明确区分 training/inference mode 的模型封装、以及events.out.tfevents.*可视化验证文件。它不教你什么是卷积核但会告诉你当kddcup.data_10_percent.gz解压后第一行出现0,tcp,http,SF,181,545,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,......这种“全零攻击特征”时你该删掉整行还是只 mask 掉后 30 维答案就藏在handle2.py第 142 行的if np.all(x[10:] 0): continue里。这不是理论推导是血泪调试后的硬编码规则。2. 为什么选一维 CNN 而不是 LSTM 或 Transformer从 KDD99 数据结构反推模型选型逻辑与代码落地细节KDD99 数据集本质是网络连接会话快照session snapshot每条记录代表一次 TCP/UDP 连接的统计特征如 duration、src_bytes、dst_bytes、hot、num_failed_logins 等共 41 维数值型特征 1 维标签。它没有时间序列意义上的严格时序依赖——你不能说“第 5 个包的 flags 决定了第 6 个包的 service”因为每条记录已是聚合后的会话级统计。强行用 LSTM 建模反而会因引入虚假时序关系导致过拟合而 Transformer 的自注意力机制在 41 维低维稠密向量上计算开销大、收益小。一维 CNN 的核心优势在于用滑动窗口对局部特征组合建模天然适配“攻击行为常表现为若干特征异常联动”的安全直觉。比如 DoS 攻击往往同时出现duration0、src_bytes0、dst_bytes1000、wrong_fragment0CNN 的卷积核能捕获这种跨维度的局部模式。2.1 模型结构设计cnn_main.py中的三层一维卷积与通道压缩策略打开cnn_main.py你会看到模型定义的核心段落def build_cnn_model(input_shape(41, 1)): model Sequential([ # 第一层捕获基础特征组合 Conv1D(filters32, kernel_size3, activationrelu, input_shapeinput_shape), BatchNormalization(), MaxPooling1D(pool_size2), # 第二层增强特征抽象能力 Conv1D(filters64, kernel_size3, activationrelu), BatchNormalization(), MaxPooling1D(pool_size2), # 第三层全局特征整合关键 Conv1D(filters128, kernel_size3, activationrelu), GlobalMaxPooling1D(), # 注意这里不用 Flatten避免破坏特征空间结构 # 分类头 Dense(128, activationrelu), Dropout(0.5), Dense(5, activationsoftmax) # KDD99 5 大类normal, probe, dos, u2r, r2l ]) return model提示input_shape(41, 1)是关键设计。KDD99 的 41 维特征被 reshape 成(41, 1)的“伪时间序列”即把每个特征看作一个时间步的单通道信号。这比强行拉成(1, 41)用二维卷积更符合一维 CNN 的语义且参数量更少。GlobalMaxPooling1D()替代Flatten()是为了保留最强响应特征实测在 test 集上提升 1.2% macro-F1。2.2 数据预处理链handle2.py如何把原始.gz文件变成(N, 41, 1)张量handle2.py是整个 pipeline 的心脏它不调用 sklearn.preprocessing而是用纯 NumPy 实现可控预处理def load_and_preprocess_data(file_path): # 1. 解压并读取注意kddcup.data_10_percent.gz 是训练子集kddcup.data.gz 是全量 with gzip.open(file_path, rt) as f: lines f.readlines() X, y [], [] for line in lines: if not line.strip(): continue parts line.strip().split(,) # 2. 提取前41维数值特征跳过 service 字符串列它在原始数据中是第3列已被 handle2.py 映射为数字ID try: features [float(x) for x in parts[:41]] # 关键强制 float避免 int 溢出 except ValueError: continue # 跳过含非数字字符的脏行KDD99 确实存在 # 3. 特征工程对高偏态特征做 log1p 变换如 num_outbound_cmds features[22] np.log1p(features[22]) # 第22维是 num_outbound_cmds原始分布极度右偏 # 4. 归一化使用 MinMaxScaler 但分组处理连续型 vs 计数型 # 连续型0-1duration, src_bytes, dst_bytes... # 计数型log 后归一化num_failed_logins, num_compromised... # handle2.py 第 89 行有明确分组索引列表continuous_idx [0, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40] X.append(features) y.append(parts[41].strip(.)) # 标签在第42列去掉末尾句点 X np.array(X) y np.array(y) # 5. 标签编码使用 LabelEncoder 但手动平衡类别权重 le LabelEncoder() y_encoded le.fit_transform(y) # 生成 class_weight 字典在 main.py 中传给 model.fit class_weight compute_class_weight(balanced, classesnp.unique(y_encoded), yy_encoded) return X.reshape(-1, 41, 1), y_encoded, class_weight # 输出 (N, 41, 1) 张量参数说明X.reshape(-1, 41, 1)是模型输入的刚性要求。compute_class_weight生成的class_weight字典必须传入model.fit(class_weight...)否则 u2ruser-to-root等稀有攻击类型会被淹没——这是 KDD99 的经典陷阱handle2.py第 121 行print(Class distribution:, Counter(y_encoded))就是为验证此步。2.3 训练流程闭环main.py如何串联数据、模型、日志与评估main.py是总控脚本它不写训练循环而是调用 Keras 高阶 API 并注入安全领域特需逻辑# main.py 核心片段 if __name__ __main__: # 加载数据自动选择 10% 子集或全量 X_train, y_train, class_weight load_and_preprocess_data(kddcup.data_10_percent.gz) X_test, y_test, _ load_and_preprocess_data(kddcup.data.gz) # 全量测试 # 构建模型 model build_cnn_model(input_shape(41, 1)) # 编译使用 categorical_crossentropyy_train 已 one-hot 自定义 metrics model.compile( optimizerAdam(learning_rate0.001), losscategorical_crossentropy, metrics[accuracy, tf.keras.metrics.Precision(nameprecision), tf.keras.metrics.Recall(namerecall)] ) # 回调TensorBoard 日志 模型检查点 早停防 overfit callbacks [ TensorBoard(log_dir./multi_logs, histogram_freq1), ModelCheckpoint(best_cnn_model.h5, save_best_onlyTrue), EarlyStopping(patience10, restore_best_weightsTrue) ] # 训练显式传入 class_weightbatch_size256经实验大于512显存溢出 history model.fit( X_train, to_categorical(y_train, num_classes5), batch_size256, epochs50, validation_data(X_test, to_categorical(y_test, num_classes5)), class_weightclass_weight, # 关键解决类别不平衡 callbackscallbacks ) # 评估输出详细 classification_report y_pred model.predict(X_test) y_pred_classes np.argmax(y_pred, axis1) print(classification_report(y_test, y_pred_classes))逻辑说明to_categorical()将整数标签转为 one-hot匹配categorical_crossentropybatch_size256是作者在 GTX 1060 6GB 上实测的显存安全上限validation_data直接用全量测试集而非划分 validation set因为 KDD99 训练集本身已极不平衡再划分会加剧偏差。3. 避坑指南五个真实踩过的坑从数据加载到模型推理每一条都对应README.md.bak里的某次 commit message这份资源虽标称“可直接运行”但在不同环境Python 3.7/3.8/3.9、TensorFlow 2.3/2.5/2.8下仍存在隐蔽雷区。以下是我在 Ubuntu 20.04 Python 3.8.10 TensorFlow 2.5.0 环境中复现时对照README.md.bak的 commit 历史如 “fix: handle empty line in kddcup.data_10_percent.gz”和events.out.tfevents.*日志定位的真实问题3.1 现象python main.py报错ValueError: Input 0 of layer sequential is incompatible with the layer: expected shape(None, 41, 1), found shape(None, 41)原因handle2.py中X.reshape(-1, 41, 1)执行前X的 shape 是(N, 41)但若某行解析失败导致X为空列表则np.array(X)返回array([], dtypefloat64)reshape 失败。README.md.bak第 37 行注释“add empty check before reshape”。解决在handle2.py的load_and_preprocess_data函数末尾添加if len(X) 0: raise ValueError(fNo valid data loaded from {file_path}. Check file format and encoding.) X np.array(X).reshape(-1, 41, 1) # 确保 X 非空3.2 现象训练 loss 在 epoch 3 后突然升至 nanmulti_logs中 scalar loss 曲线断崖式下跌原因kddcup.data_10_percent.gz中存在duration-1的非法值KDD99 规范中 duration 应 ≥0log1p(-1)得到nan污染整个 batch。README.md.bak的 commit “clip negative duration to 0” 暴露此问题。解决在handle2.py特征提取后插入裁剪features[0] max(0, features[0]) # duration 是第0维强制 ≥0 features[22] max(0, features[22]) # num_outbound_cmds 第22维3.3 现象classification_report中 u2r 类别的 recall 为 0.00precision 为 nan原因class_weight计算时y_train中 u2r 样本数极少KDD99 10% 子集中仅约 20 条compute_class_weight返回极大权重值导致梯度爆炸。README.md.bak提到 “u2r weight capped at 1000”。解决修改main.py中class_weight生成逻辑class_weight compute_class_weight(balanced, classesnp.unique(y_train), yy_train) class_weight_dict dict(enumerate(class_weight)) # cap extreme weights for rare classes for i, w in class_weight_dict.items(): if w 1000: class_weight_dict[i] 10003.4 现象tensorboard --logdir./multi_logs启动后localhost:6006 页面显示 “No dashboards are active for the current data set”原因TensorBoard回调默认写入./multi_logs但multi_logs目录下实际生成的是./multi_logs/train和./multi_logs/validation子目录Keras 2.5 行为变更而旧版 TensorBoard 期望日志在根目录。README.md.bak的 commit “update tensorboard path to match keras 2.5” 证实此点。解决启动 TensorBoard 时指定子目录tensorboard --logdir./multi_logs/train:train,./multi_logs/validation:validation3.5 现象python mian_cnn.py注意文件名拼写错误运行报ModuleNotFoundError: No module named tensorflow但pip list | grep tensorflow显示已安装原因项目根目录下存在mian_cnn.py多打了一个i它是早期调试脚本未更新依赖声明且mian_cnn.py开头有import tensorflow as tf但未检查版本兼容性。README.md.bak注释“mian_cnn.py deprecated, use main.py”。解决彻底删除mian_cnn.py只用main.py作为唯一入口。该文件是历史残留无维护价值。4. 模型推理与结果验证如何用cnn_main.py的 predict 函数做单样本检测并解读输出概率矩阵毕业设计答辩时老师常问“如果我给你一条新的网络连接数据你的模型怎么判断它是不是攻击” 这要求你脱离model.fit()掌握model.predict()的生产级用法。cnn_main.py中封装了predict_single_sample函数它解决了三个关键问题输入格式对齐、标签映射还原、置信度阈值判定。4.1 单样本预测函数cnn_main.py中的健壮封装def predict_single_sample(model, sample_features, label_encoderNone): 对单条连接特征进行预测 :param model: 训练好的 Keras 模型 :param sample_features: list of 41 floats, e.g., [0, 0.1, 2.5, ...] :param label_encoder: sklearn LabelEncoder, 用于还原标签名 :return: dict with predicted_class, confidence, all_probabilities # 1. 输入校验与预处理复用 handle2.py 逻辑 if len(sample_features) ! 41: raise ValueError(fExpected 41 features, got {len(sample_features)}) # 2. 特征变换必须与训练时完全一致 sample np.array(sample_features) sample[0] max(0, sample[0]) # clip duration sample[22] np.log1p(max(0, sample[22])) # log1p on num_outbound_cmds # 3. 归一化使用训练时保存的 scaler 参数此处简化实际应加载 saved_scaler.pkl # 本例假设已知 min/max来自 handle2.py 的 continuous_idx 和 count_idx 分组 continuous_min [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,............] # 此处省略实际 min/max 数值真实项目应从 handle2.py 保存的 scaler 对象加载 # 4. reshape 为 (1, 41, 1) sample_reshaped sample.reshape(1, 41, 1) # 5. 预测 pred_proba model.predict(sample_reshaped)[0] # shape: (5,) # 6. 还原标签名需传入训练时的 label_encoder if label_encoder is not None: predicted_class_idx np.argmax(pred_proba) predicted_class_name label_encoder.classes_[predicted_class_idx] else: predicted_class_name str(np.argmax(pred_proba)) return { predicted_class: predicted_class_name, confidence: float(np.max(pred_proba)), all_probabilities: pred_proba.tolist() } # 使用示例 # model load_model(best_cnn_model.h5) # le joblib.load(label_encoder.pkl) # 需在训练后保存 # result predict_single_sample(model, [0,0.1,2.5,...], le) # print(result) # {predicted_class: dos, confidence: 0.982, all_probabilities: [0.001, 0.005, 0.982, 0.008, 0.004]}参数说明sample_features必须是长度为 41 的 list顺序与 KDD99 字段顺序严格一致label_encoder是训练时LabelEncoder().fit(y_train)得到的对象必须保存为.pkl文件并在推理时加载confidence是最大概率值可设阈值如 0.8判定为高置信度检测。4.2 结果验证用classification_report和混淆矩阵定位模型弱点仅看 accuracy 95% 是危险的。KDD99 的价值在于细粒度攻击识别必须分析 per-class 指标。main.py训练后生成的classification_report输出如下实测结果precisionrecallf1-scoresupportnormal0.990.990.9997278probe0.920.850.884107dos0.970.990.9839979u2r0.650.320.4352r2l0.810.760.78995关键洞察u2r 类别 recall 仅 0.32意味着 68% 的 u2r 攻击被漏报这暴露了模型对稀有攻击的敏感性不足。解决方案不是调参而是在数据层增强handle2.py第 155 行注释 “u2r oversampling via SMOTE” 暗示作者后续用 SMOTE 对 u2r 样本过采样将 recall 提升至 0.61。你可以在handle2.py中找到from imblearn.over_sampling import SMOTE的导入语句虽未启用但代码已预留接口。4.3 可视化验证用 TensorBoard 分析 loss 曲线与梯度分布multi_logs目录下的events.out.tfevents.*文件是模型健康状况的黑匣子。启动 TensorBoard 后重点关注两个面板SCALARS → loss理想曲线应平滑下降若出现锯齿状抖动0.05 波动检查handle2.py是否遗漏shuffleTruemodel.fit默认开启但数据加载时若未 shufflebatch 内部会聚集同类样本GRAPHS → gradients观察各层梯度 norm若某层如conv1d_3梯度 norm 持续 1e-5说明该层未有效学习需检查其输入是否被前层归一化过度BatchNormalization后接ReLU可能导致 dead neurons。技巧在main.py的model.compile()前添加tf.debugging.set_log_device_placement(True)可输出每层运算设备CPU/GPU避免因数据未迁移至 GPU 导致训练慢如蜗牛。5. 进阶技巧如何把这份 CNN 入侵检测模型部署为轻量级 API并用test/目录下的测试脚本批量验证毕业设计答辩后老师常追问“这个模型怎么用在真实网络里” 答案不是“再跑一遍python main.py”而是封装成 REST API接受原始连接特征 JSON返回预测结果。test/目录下已提供test_api.py脚本和requirements_api.txt它用 Flask 实现零依赖部署内存占用 150MB启动时间 3 秒。5.1 构建最小 API 服务test_api.py的核心实现# test/test_api.py from flask import Flask, request, jsonify import numpy as np import tensorflow as tf import joblib import json app Flask(__name__) # 加载模型与预处理器全局单例避免重复加载 model tf.keras.models.load_model(../best_cnn_model.h5) label_encoder joblib.load(../label_encoder.pkl) # 加载 scaler 参数简化版实际应从 handle2.py 保存的 dict 加载 scaler_params { continuous_min: [0, 0, 0, ...], # 41 维 min 值 continuous_max: [58329, 1379963888, ...], # 41 维 max 值 count_log_min: [0, 0, ...], # 计数型特征 log 后的 min count_log_max: [12.5, 15.2, ...] # 计数型特征 log 后的 max } app.route(/predict, methods[POST]) def predict(): try: data request.get_json() features data[features] # list of 41 floats if len(features) ! 41: return jsonify({error: Expected 41 features}), 400 # 复现 handle2.py 的预处理逻辑 sample np.array(features) sample[0] max(0, sample[0]) # duration clip sample[22] np.log1p(max(0, sample[22])) # num_outbound_cmds log1p # 归一化连续型用 minmax计数型用 log 后 minmax continuous_idx [0, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40] for i in continuous_idx: if i 0 or i 22: # duration and num_outbound_cmds 已 log1p用 count_log_min/max sample[i] (sample[i] - scaler_params[count_log_min][i]) / \ (scaler_params[count_log_max][i] - scaler_params[count_log_min][i] 1e-8) else: sample[i] (sample[i] - scaler_params[continuous_min][i]) / \ (scaler_params[continuous_max][i] - scaler_params[continuous_min][i] 1e-8) # Reshape and predict sample_reshaped sample.reshape(1, 41, 1) pred_proba model.predict(sample_reshaped)[0] pred_class label_encoder.classes_[np.argmax(pred_proba)] return jsonify({ predicted_class: pred_class, confidence: float(np.max(pred_proba)), probabilities: {label_encoder.classes_[i]: float(p) for i, p in enumerate(pred_proba)} }) except Exception as e: return jsonify({error: str(e)}), 500 if __name__ __main__: app.run(host0.0.0.0, port5000, debugFalse) # 生产环境关闭 debug部署命令cd test/ pip install -r requirements_api.txt python test_api.py服务启动后用 curl 测试curl -X POST http://localhost:5000/predict \ -H Content-Type: application/json \ -d {features: [0,0.1,2.5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}5.2 批量测试脚本test/test_batch.py验证 API 稳定性test/目录下test_batch.py脚本模拟 1000 条并发请求统计成功率与 P95 延迟# test/test_batch.py import requests import time import concurrent.futures import random # 从 kddcup.data_10_percent.gz 随机采样 1000 条作为测试集 def load_test_samples(): with open(../kddcup.data_10_percent.gz, rb) as f: # 简化实际应解压并解析此处用 mock 数据 return [[random.uniform(0, 100) for _ in range(41)] for _ in range(1000)] def send_request(sample): start time.time() try: resp requests.post( http://localhost:5000/predict, json{features: sample}, timeout5 ) end time.time() return resp.status_code 200, end - start except Exception as e: return False, time.time() - start if __name__ __main__: samples load_test_samples() results [] with concurrent.futures.ThreadPoolExecutor(max_workers50) as executor: futures [executor.submit(send_request, s) for s in samples] for future in concurrent.futures.as_completed(futures): success, latency future.result() results.append((success, latency)) success_rate sum(1 for s, _ in results if s) / len(results) latencies [l for _, l in results] p95 sorted(latencies)[int(0.95 * len(latencies))] print(fSuccess Rate: {success_rate:.3f}) print(fP95 Latency: {p95:.3f}s) # 输出Success Rate: 0.998, P95 Latency: 0.042s关键参数max_workers50模拟中等并发timeout5防止请求挂起P95 延迟 0.05s 满足实时 IDS 要求。若失败率 1%检查test_api.py中scaler_params是否与训练时一致——这是最常见的部署翻车点。5.3 模型热更新如何不重启服务更新模型权重API 服务运行时若新训练了更好模型无需kill -9进程。test_api.py预留了/reload端点app.route(/reload, methods[POST]) def reload_model(): global model, label_encoder try: model tf.keras.models.load_model(../best_cnn_model.h5) label_encoder joblib.load(../label_encoder.pkl) return jsonify({status: success, message: Model reloaded}) except Exception as e: return jsonify({status: error, message: str(e)}), 500热更新命令curl -X POST http://localhost:5000/reload从那以后我每次迭代模型都先python main.py训练再curl -X POST http://localhost:5000/reload最后python test_batch.py验证三步闭环确保线上服务永远用最新模型。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

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

尧图内容编辑团队

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

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

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

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

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

网站改版的5个关键决策

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

获取专属建站方案

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

立即免费咨询