新闻详情

新闻详情

首页 / 资讯中心 / 详情

ResNet异常检测模型:A→B映射如何将误报率从3.91%压到1.00%

发布时间:2026/9/30 10:21:42来源:尧图网络
ResNet异常检测模型:A→B映射如何将误报率从3.91%压到1.00%
简介这份PDF文档面向从事机器学习、数据建模与网络安全方向的研究者与工程师聚焦异常检测中自编码器易过拟合、误报率偏高的痛点提出一种基于ResNet深度神经网络的改进模型。资源包共1个文件为1.59MB的PDF论文内容涵盖模型原理、训练与测试流程及实验验证适合具备神经网络基础、希望深入理解异常检测建模思路的读者研读。文档以固定切分规则将数据分为A、B两部分训练网络学习A到B的映射并引入L2正则化与代价函数抑制过拟合同时借助ResNet残差层缓解梯度消失问题测试阶段通过输出误差与阈值划分正常与异常数据。文中还给出在KDDCup99数据集上的检测结果表明该方法能取得较好检测率与较低误报率。目前已有181人学习可作为异常检测、入侵检测方向研究者的参考材料帮助理解深度模型在数据建模中的具体落地方式与实验设计逻辑。1. 从自编码器的翻车说起这份 ResNet 异常检测模型到底解决了什么做过网络入侵检测的同行大概率都踩过同一个坑拿自编码器在 KDDCup99 上跑异常检测训练集上重构误差压得很低一上测试集误报率直接飙到 4% 以上正常流量被大批量标成攻击。这份《基于ResNet深度神经网络的异常检测模型》就是冲着这个痛点来的。作者郑非凡来自四川大学网络空间安全学院核心思路是把重构整条数据换成从数据的一部分推另一部分——固定切分规则把每条样本切成 A、B 两段训练网络学 A→B 的映射再用 ResNet 残差层压住梯度消失配合 L2 正则化抑制过拟合。在 KDDCup99 的 10% 训练集上检测率做到 94.5%误报率压到 1.00%对比多层自编码器的 93.26% / 3.91%误报率降了将近四倍。适合谁看正在做入侵检测、日志异常识别、工业设备异常预警且已经被自编码器误报折磨过的工程师也适合想把 ResNet 从图像分类迁移到结构化数据异常检测的人。2. 模型原理拆解为什么 A→B 映射比自编码器重构更抗过拟合2.1 自编码器重构的过拟合根源自编码器的训练目标是让输出尽可能等于输入也就是学一个恒等映射。问题在于当训练数据规模不够大、覆盖的正常模式不够全时网络会把训练集中那些碰巧出现的噪声也一并记下来。KDDCup99 的 10% 训练集只有 97278 条正常数据41 维特征里还有大量独热编码产生的稀疏维度自编码器很容易在这些稀疏位上过拟合。测试时遇到分布稍有偏移的正常流量重构误差就会异常升高被误判为攻击——这就是误报率居高不下的直接原因。作者在引言里点得很清楚自编码器要求训练数据能囊括所有正常数据的分布一旦测试数据与训练数据分布差异较大自编码器就无法正确重构进而把这些数据标为异常。这个观察是整篇论文的立论基础。2.2 A→B 映射的建模逻辑换个思路正常数据内部是有规律性的一部分特征能推出另一部分特征。比如网络连接记录里协议类型、服务类型、连接时长这些字段之间存在稳定的统计关联。把数据切成 A 和 B 两段训练网络学 $g(A) B$这个映射比恒等映射约束更强——网络不能偷懒直接复制输入必须真正学到 A 和 B 之间的因果关系。论文里的形式化表达是式(1)给定正常数据 $X$切成 $A$$a$ 维和 $B$$m-a$ 维目标是学一个函数 $g$ 使得 $g(A) B$。代价函数就是式(2)的 $Cost(X) |g(A) - B|$。这个设计的好处是对于和正常数据分布相似的数据A→B 的映射关系成立误差小对于异常数据这个映射关系被打破误差大。而且因为不是恒等映射L2 正则化能真正发挥作用不像自编码器那样正则化一强就欠拟合、一弱就过拟合。2.3 ResNet 残差层怎么压住梯度消失网络一深BP 反向传播的梯度在逐层相乘中衰减到接近零前层参数根本更新不动。论文用的是式(3)的残差结构$$g_{ResNet}(X) g_{fullNet}(X) X$$全连接层的输出加上原始输入梯度回传时有一条高速公路直接绕过全连接层保证即使全连接层梯度很小总梯度也不会消失。论文最终搭了 9 层网络其中 3 个普通全连接层、6 个 ResNet 层输入 41 维中间层 60 维输出 246 维再拆成 3 组 82 维的 B 候选。2.4 多输出机制与 NearCostA 和 B 之间不可避免存在一对多关系——同一个 A 可能对应几个不同的 B。如果只让网络输出一个 B遇到一对多的情况误差就会偏大。论文的解法是让网络输出 $n$ 个 B 候选测试取 $n3$代价函数取所有候选中距离最小的那个如式(4)$$Dist(X) \min_{i} |F_i(A) - B|^2$$但这样又带来新问题对异常数据来说$n$ 个候选里总有一个碰巧接近真实 B误差被稀释了。所以作者加了 NearCost式5让 $n$ 个输出互相靠拢$$NearCost(X) \sum_{i2}^{n} |F_1(A) - F_i(A)|^2$$最终代价函数是式(6)$Cost(X) Dist(X) \alpha \cdot NearCost(X) \beta \cdot |Weights|^2$论文实测 $\alpha$ 取 1e-3 到 5e-3、$\beta$ 取 1e-2 效果较好。3. 数据预处理与网络搭建从 41 维原始特征到 123 维输入3.1 KDDCup99 的字段处理KDDCup99 每条记录 41 维其中 7 个是符号型protocol_type、service、flag 等34 个是连续数值型。符号型必须数值化论文用的是独热编码。7 个符号字段展开后加上原有的连续字段最终得到 123 维、取值范围在 (0,1] 的高维数值数据。连续字段的归一化用式(7)的 min-maximport numpy as np import pandas as pd # 假设 df 是读入的 KDDCup99 数据最后一列是 label # 7 个符号型字段 cat_cols [protocol_type, service, flag, land, logged_in, is_host_login, is_guest_login] # 独热编码drop_firstFalse 保留全部类别 df_encoded pd.get_dummies(df, columnscat_cols, dtypenp.float32) # 34 个连续字段做 min-max 归一化 num_cols [c for c in df.columns if c not in cat_cols [label]] for col in num_cols: xmin, xmax df[col].min(), df[col].max() if xmax - xmin 1e-8: df_encoded[col] (df[col] - xmin) / (xmax - xmin) else: df_encoded[col] 0.0 # 最终特征维度应为 123 print(df_encoded.drop(columns[label]).shape)这段代码的逻辑先对符号字段做独热编码再对连续字段做 min-max 归一化。注意get_dummies的dtype要设成 float32否则后续转 tensor 会报类型错误。归一化时如果某列最大值等于最小值常数特征直接置 0避免除零。3.2 A/B 切分规则论文的切分不是随机的而是固定规则1-82 维作为 B标签83-123 维共 41 维作为 A输入。为什么这么切作者的解释是1-82 维里除了第 1 维是连续数值2-82 维实际上是 3 个符号字段独热编码来的大部分位是 0只有 3 位是 1实际表达能力远小于 83-123 维这些连续字段。所以让信息量大的部分做输入、信息量小的部分做标签符合输入信息量应大于标签的原则。# 按论文规则切分83-123 维索引 82:123作为输入 A # 1-82 维索引 0:82作为标签 B X_all df_encoded.drop(columns[label]).values.astype(np.float32) A X_all[:, 82:123] # 41 维输入 B X_all[:, 0:82] # 82 维标签 print(fA shape: {A.shape}, B shape: {B.shape})这里有个容易翻车的点独热编码后列的顺序会变必须确保切分前列的顺序和论文一致。建议在get_dummies之后手动 reindex 列顺序或者把切分逻辑写成基于列名的函数而不是硬编码索引。3.3 网络结构定义论文的 9 层网络参数如表 4 所示用 PyTorch 实现如下import torch import torch.nn as nn class ResNetBlock(nn.Module): 残差块全连接 跳跃连接 def __init__(self, dim): super().__init__() self.fc nn.Linear(dim, dim) self.act nn.LeakyReLU(negative_slope1e-2) def forward(self, x): return self.act(self.fc(x) x) # 式(3)的残差结构 class AnomalyNet(nn.Module): def __init__(self, in_dim41, hidden60, out_dim82, n_outputs3): super().__init__() self.n_outputs n_outputs # 层1输入全连接 41 - 60 self.fc_in nn.Linear(in_dim, hidden) # 层2-43 个 ResNet 层60 维 self.res_blocks nn.Sequential(*[ResNetBlock(hidden) for _ in range(3)]) # 层5全连接 60 - 123 self.fc_mid nn.Linear(hidden, 123) # 层6-83 个 ResNet 层123 维 self.res_blocks2 nn.Sequential(*[ResNetBlock(123) for _ in range(3)]) # 层9输出 123 - 2463 组 82 维 self.fc_out nn.Linear(123, out_dim * n_outputs) self.act nn.LeakyReLU(negative_slope1e-2) def forward(self, x): x self.act(self.fc_in(x)) x self.res_blocks(x) x self.act(self.fc_mid(x)) x self.res_blocks2(x) x self.fc_out(x) # 重塑为 (batch, n_outputs, 82) return x.view(-1, self.n_outputs, 82)激活函数选 LeakyReLU 而不是 ReLU是因为 ReLU 在负半轴梯度为 0反向传播时这部分参数永远不更新。LeakyReLU 给负半轴一个 1e-2 的斜率保证梯度能传回去。论文表 3 里对比了 sigmoid、tanh、relu、leaky_relu 的导数sigmoid 和 tanh 的导数基本小于 1训练耗时长所以选了 leaky_relu。3.4 代价函数与训练循环def cost_function(outputs, B, alpha1e-3, beta1e-2, modelNone): outputs: (batch, n_outputs, 82) B: (batch, 82) # Dist: 每个候选与真实 B 的欧式距离取最小 diff outputs - B.unsqueeze(1) # (batch, n, 82) dist_per_cand (diff ** 2).sum(dim2) # (batch, n) dist dist_per_cand.min(dim1).values # (batch,) # NearCost: n 个输出互相靠拢 near 0.0 for i in range(1, outputs.size(1)): near near ((outputs[:, 0] - outputs[:, i]) ** 2).sum(dim1) near near / outputs.size(1) # L2 正则 l2 sum(p.pow(2).sum() for p in model.parameters()) return (dist alpha * near beta * l2).mean() # 训练循环 model AnomalyNet().cuda() optimizer torch.optim.RMSprop(model.parameters(), lr1e-3) for epoch in range(150): for batch_A, batch_B in train_loader: batch_A, batch_B batch_A.cuda(), batch_B.cuda() outputs model(batch_A) loss cost_function(outputs, batch_B, modelmodel) optimizer.zero_grad() loss.backward() optimizer.step()优化器用 RMSProp论文明确写了。训练 150 轮batch size 256。注意cost_function里 L2 正则是对所有参数求平方和实际工程中如果参数太多可以只对权重矩阵做、不对 bias 做但论文没区分按原样实现即可。4. 阈值选取与检测效果均值加 1.5 倍标准差怎么定4.1 阈值公式与统计含义阈值是异常检测的命门。论文的做法很朴素但有效用训练数据过网络后的 Dist 值算均值和标准差阈值取 $\lambda mean 1.5 \times std$式10。测试时 Dist 超过 $\lambda$ 判为异常否则正常。# 用训练集计算阈值 model.eval() train_dists [] with torch.no_grad(): for batch_A, batch_B in train_loader: batch_A, batch_B batch_A.cuda(), batch_B.cuda() outputs model(batch_A) diff outputs - batch_B.unsqueeze(1) dist (diff ** 2).sum(dim2).min(dim1).values train_dists.append(dist.cpu()) train_dists torch.cat(train_dists).numpy() mean_d train_dists.mean() std_d train_dists.std() threshold mean_d 1.5 * std_d print(fmean{mean_d:.6f}, std{std_d:.6f}, threshold{threshold:.6f})1.5 这个系数是论文实测出来的。系数太小正常数据的 Dist 波动就会越过阈值误报率飙升系数太大异常数据的 Dist 也够不到阈值检测率下降。论文在 2.5 节明确说用均值加上 1.5 倍方差能获得较好的检测率与较低的误判率。4.2 检测指标计算论文用 TPR检测率、FPR误报率、F1 Score 三个指标from sklearn.metrics import confusion_matrix def evaluate(model, test_loader, threshold): model.eval() all_preds, all_labels [], [] with torch.no_grad(): for batch_A, batch_B, labels in test_loader: batch_A, batch_B batch_A.cuda(), batch_B.cuda() outputs model(batch_A) diff outputs - batch_B.unsqueeze(1) dist (diff ** 2).sum(dim2).min(dim1).values preds (dist threshold).long().cpu().numpy() all_preds.extend(preds) all_labels.extend(labels.numpy()) tn, fp, fn, tp confusion_matrix(all_labels, all_preds).ravel() tpr tp / (tp fn) # 检测率 fpr fp / (fp tn) # 误报率 f1 2 * tp / (2 * tp fp fn) return tpr, fpr, f1论文实测结果检测率 94.5%误报率 1.00%对比多层自编码器的 93.26% / 3.91%。误报率从 3.91% 降到 1.00%这是这份模型最实在的价值——在安全运营场景里误报率每降一个百分点分析师每天少看几百条告警。4.3 训练时间开销论文表 5 给了时间对比本文模型训练 807.0 秒自编码器 696.7 秒测试本文模型 4.6 秒自编码器 3.0 秒。ResNet 多出来的时间主要花在残差层的额外加法和多输出计算上。测试环境是 i5-7700 GT 730 8GB 内存这个配置放到今天算很低了实际用现代 GPU 跑会快很多。5. 避坑与排查这份模型落地时最容易翻车的五个点5.1 独热编码后列顺序错乱导致 A/B 切分失效现象训练 loss 正常下降但测试检测率只有 60% 出头跟论文差一大截。 原因pd.get_dummies生成的列顺序取决于类别出现顺序不同次运行可能不一致。如果直接按索引[:, 82:123]切分切出来的 A 和 B 可能混入了错误的字段。 解决在get_dummies之后显式 reindex 列顺序或者把切分逻辑改成基于列名的函数。我一般会先把列名列表存下来切分时按名字取不按位置取。5.2 阈值用测试集算导致数据泄漏现象检测率虚高到 98% 以上换一批数据就崩。 原因把测试集的 Dist 均值标准差拿来算阈值等于提前看了答案。 解决阈值必须只用训练集算。训练集过完网络得到 Dist 分布算 mean 和 std固定下来再去测测试集。论文 2.5 节写得很清楚是用训练数据通过神经网络后的差异来算阈值。5.3 n 值取太大导致异常数据被稀释现象误报率降了但检测率也掉得厉害漏报增多。 原因$n$ 越大异常数据越有可能在 $n$ 个候选中碰巧命中一个接近真实 B 的输出Dist 被拉低越过阈值的概率下降。 解决论文实测 $n3$ 效果较好。如果业务场景对漏报更敏感可以试 $n2$如果对误报更敏感可以试 $n4$ 但要同步调大 $\alpha$ 让 NearCost 约束更强。5.4 L2 正则系数 $\beta$ 设太大导致欠拟合现象训练 loss 降不下去检测率和误报率同时很差。 原因$\beta$ 取 1e-2 是论文在 KDDCup99 上的经验值换数据集后参数量变了同样的 $\beta$ 可能过强。 解决先把 $\beta$ 设成 0 跑一轮看模型能不能拟合能拟合后再逐步加 $\beta$从 1e-4 开始试观察验证集误报率的变化。$\alpha$ 同理从 1e-3 起步。5.5 残差块维度不匹配现象RuntimeError: The size of tensor a (60) must match the size of tensor b (123)。 原因ResNet 的跳跃连接要求输入输出维度一致。论文表 4 里层 5 是全连接 60→123层 6-8 是 123 维的 ResNet 层层 2-4 是 60 维的 ResNet 层。如果 ResNetBlock 的dim传错加法就对不上。 解决ResNetBlock 初始化时明确传入该层的维度且确保它只用在维度不变的层。维度变化的层如 60→123用普通全连接层不要套残差。6. 进阶玩法把 A/B 切分和阈值选取从主观变成可搜索论文在结束语里自己承认了两个遗留问题数据切分和阈值选取都带主观性选取好坏直接影响检测效果。这其实是留给工程落地的口子——把这两个超参做成可搜索的模型上限还能再抬一截。先说切分。论文固定用 83-123 维做 A、1-82 维做 B理由是信息量对比。但这个规则是针对 KDDCup99 的字段语义定的换到别的数据集比如 CIC-IDS2017、工业传感器数据就不一定成立。我的做法是先按字段类型分组连续数值字段归一组、独热字段归一组然后枚举几种切分比例比如 A 占 30%、40%、50%每种跑一轮训练看验证集上的 F1。切分比例不用太细粗粒度扫三四个点就能看出趋势。注意 A 的维度不能太小否则输入信息量不够网络学不到东西也不能太大否则 B 太短异常数据的 Dist 区分度不够。再说阈值。均值加 1.5 倍标准差是个固定公式但 1.5 这个系数可以搜。更工程化的做法是在验证集上画 TPR-FPR 曲线根据业务需求选工作点。安全运营场景通常要求 FPR 低于 1%那就从曲线上找 FPR1% 对应的阈值系数如果是对漏报零容忍的场景就反过来找 TPR 最高的点。下面这段代码把阈值系数搜索和指标输出串起来def search_threshold(model, val_loader, coef_range): 在验证集上搜索最优阈值系数 model.eval() dists, labels [], [] with torch.no_grad(): for batch_A, batch_B, lbl in val_loader: batch_A, batch_B batch_A.cuda(), batch_B.cuda() outputs model(batch_A) diff outputs - batch_B.unsqueeze(1) d (diff ** 2).sum(dim2).min(dim1).values dists.append(d.cpu()) labels.append(lbl) dists torch.cat(dists).numpy() labels torch.cat(labels).numpy() mean_d, std_d dists.mean(), dists.std() results [] for coef in coef_range: thr mean_d coef * std_d preds (dists thr).astype(int) tn, fp, fn, tp confusion_matrix(labels, preds).ravel() tpr tp / (tp fn) if (tp fn) 0 else 0 fpr fp / (fp tn) if (fp tn) 0 else 0 f1 2 * tp / (2 * tp fp fn) if (2 * tp fp fn) 0 else 0 results.append((coef, tpr, fpr, f1)) return results # 扫描 1.0 到 3.0步长 0.25 for coef, tpr, fpr, f1 in search_threshold(model, val_loader, [1.0 0.25 * i for i in range(9)]): print(fcoef{coef:.2f} TPR{tpr:.4f} FPR{fpr:.4f} F1{f1:.4f})跑完这张表选哪个系数就一目了然了。论文的 1.5 在 KDDCup99 上是个不错的折中但换数据集后大概率要重新扫。还有一个容易被忽略的点论文提到测试集里有 7593 条异常数据和正常数据完全一样占异常数据的 3.03%反过来有 7273 条正常数据和异常数据完全一样占正常数据的 12.00%。这些完全一样的样本从特征上根本没法区分它们直接锁死了模型精度的上限。所以如果你的数据集里也有这种标签冲突的样本别死磕模型结构先把冲突样本清掉或者重新核对标签否则调参调到天亮也突破不了那个天花板。从那以后我每次拿到新的异常检测数据集都强制先跑一遍正常与异常特征完全重合的统计确认标签冲突比例再动手建模。这个习惯帮我省过好几次无谓的调参时间。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

Spring Boot + Vue家庭维修系统:源码部署与前后端联调实战 2026/9/30 11:02:34

Spring Boot + Vue家庭维修系统:源码部署与前后端联调实战

最近在帮别人整理一套“基于Spring Boot Vue的Web家庭设备维修服务系统”,光是看标题就知道,这不是一个只能跑个登录页的玩具项目,而是包含用户下单、维修工接单、管理员派单、服务评价、维修进度跟踪等完整业务流程的企业级教学项目。很多人…

阅读更多 →
上海 PE 收缩膜源头工厂推荐:上海睿越塑料,深耕长三角多行业包装 2026/9/30 11:02:27

上海 PE 收缩膜源头工厂推荐:上海睿越塑料,深耕长三角多行业包装

长三角地区水饮、食品、家具、日化等产业密集,PE 收缩膜作为外包装刚需,采购时优先选择本地源头工厂,既能保障交付时效、降低物流成本,又能方便上门验厂、及时响应产线调试需求。在上海众多塑料包装生产企业中,上海睿越…

阅读更多 →
TVA类人智眼实操指南(10):小样本学习与现场“自我进化” 2026/9/30 11:02:26

TVA类人智眼实操指南(10):小样本学习与现场“自我进化”

前沿技术探索:TVA智能体(简称TVA)TVA智能体(亦称“AI智能体视觉”)是依托Transformer架构与“因式智能体”理论构建的新型工业视觉系统,也是当前最具代表性的具身视觉技术之一。它有机融合深度强化学习&…

阅读更多 →
TVA类人智眼实操指南(18):为什么不用几万块的显卡也能跑得飞快? 2026/9/30 11:02:26

TVA类人智眼实操指南(18):为什么不用几万块的显卡也能跑得飞快?

前沿技术探索:TVA智能体(简称TVA)TVA智能体(亦称“AI智能体视觉”)是依托Transformer架构与“因式智能体”理论构建的新型工业视觉系统,也是当前最具代表性的具身视觉技术之一。它有机融合深度强化学习&…

阅读更多 →
人永远都不够用,事永远都没人做!二三十人的公司,都开始转不动... 2026/9/30 11:02:19

人永远都不够用,事永远都没人做!二三十人的公司,都开始转不动...

你是不是也有这种体会:招聘从来没停,但总感觉缺人手。三十来个员工,人人都喊忙,新增任务根本派不下去。客户消息积压无人回应,周报反复催促才能收齐,一份报价单流转四人依旧没人拍板;新人入职三…

阅读更多 →
VMware 仅主机(Host-Only)模式:虚拟机 ↔ 物理机互通完整教程 2026/9/30 11:02:12

VMware 仅主机(Host-Only)模式:虚拟机 ↔ 物理机互通完整教程

文章目录一、前置检查(Windows宿主机)二、配置IP,保证同网段方式1:DHCP自动获取(最简单)方式2:静态IP(推荐,IP固定,适合端口映射/文件共享)三、连…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉