资讯动态

A3C深度强化学习实战:基于KDD99的网络入侵检测系统解析

发布时间:2026/9/14 5:18:51 来源:尧图企业网站定制
简介这是一份基于深度强化学习A3C算法开发的网络入侵检测系统Python源码资源面向信息安全、计算机、人工智能等专业学生及算法爱好者可用于毕业设计、课程设计或作为强化学习与入侵检测交叉方向的入门项目。压缩包共50个文件包含约16个Python源码、20个文本说明与KDD数据集、8个data格式数据文件、4个eps训练效果曲线图及2个bat一键运行脚本整体大小19.06MB文本与data文件主要提供NSL-KDD训练/测试数据及预处理结果py文件覆盖数据预处理、环境构建、A3C智能体训练、策略监控、攻击类型统计可视化等完整模块。目前已有343人学习下载。代码在KDD数据集上经过运行验证可直接复现完整检测流程目录结构清晰、参数集中配置便于在此基础上进一步修改算法或嵌入实时流量检测场景具有较高的学习借鉴与二次开发价值。1. 为什么用A3C做网络入侵检测从KDD99到端到端决策网络入侵检测的传统路线是「先提取统计特征再交给SVM或随机森林做分类」这套流程在KDD99这类公开数据集上能拿到不错的准确率但落地时有两个硬伤一是特征工程高度依赖人工经验攻击类型一变特征分布就漂移二是检测器只能给出孤立判断无法利用同一连接会话内前后数据包之间的时序关联。这套源码用A3CAsynchronous Advantage Actor-Critic把入侵检测重新建模成序贯决策问题每个网络连接对应一个状态检测器通过与环境交互学习「放行」还是「报警」的策略不需要显式构造攻击规则。适合正在做毕业设计或课程设计的同学快速复现完整流程也适合想了解深度强化学习如何落地到安全场景的工程师。压缩包里最值得看的不是某个单文件而是my_enviroment.pyA3CtypeAD.pyworker.py这一条完整的MDP构建与异步训练链路。2. 数据预处理与KDDTrain特征工程从原始流量到强化学习状态2.1 KDD99数据集的标签结构与攻击类型映射KDD Cup 1999数据集虽然年代久远但至今仍是入侵检测论文里最常用的基准。项目里的KDDTrain.txt和KDDTest.txt是NSL-KDD版本比原始KDD99少了大量冗余记录。每条样本有41维特征最后一位是标签。标签分两类normal和具体的攻击名攻击又归为四类DoS、Probe、R2L、U2R。压缩包里单独提供了u2r_datasetdisplay.py和r2l_datasetdisplay.py说明作者在调试时重点看过这两类攻击的数据分布。攻击大类典型攻击名KDDTrain样本数特点DoSneptune, smurf, teardrop明显偏多通过洪泛耗尽目标资源Probesatan, portsweep, nmap中等端口扫描和漏洞探测R2Lguess_passwd, warezmaster较少远程非法访问U2Rbuffer_overflow, rootkit极少本地提权U2R和R2L在数据集中占比极低直接拿来做有监督分类容易过拟合到多数类。强化学习的好处是通过奖励信号调节策略而不需要人为做SMOTE过采样。你需要先理解标签在文件里的位置前41列是特征第42列是标签第43列是难度系数NSL-KDD新增。预处理脚本必须正确处理这三部分。2.2 data_preprocessing.py数值化、归一化与One-Hot编码data_preprocessing.py是整个流水线的第一环。原始数据包含三类特征连续型数值如duration、src_bytes、离散型枚举如protocol_type、service、flag、二进制标志如land、logged_in。强化学习的输入是固定维度的向量所以需要统一编码。# data_preprocessing.py 核心逻辑简化版 import pandas as pd import numpy as np DATA_PATH KDDTrain.txt COLUMN_NAMES [ duration, protocol_type, service, flag, src_bytes, dst_bytes, land, wrong_fragment, urgent, hot, # ... 共41列这里省略中间列 dst_host_srv_rerror_rate ] LABEL_COLUMN label def load_kdd_data(path): df pd.read_csv(path, headerNone, namesCOLUMN_NAMES [LABEL_COLUMN, difficulty]) # 将攻击名映射为四个大类normal单独一类共5类 attack_mapping { neptune: DoS, smurf: DoS, teardrop: DoS, satan: Probe, portsweep: Probe, nmap: Probe, guess_passwd: R2L, warezmaster: R2L, buffer_overflow: U2R, rootkit: U2R } df[attack_type] df[LABEL_COLUMN].map(attack_mapping).fillna(normal) return df def encode_features(df): # 协议类型和服务名是字符串做One-Hot编码 categorical_cols [protocol_type, service, flag] df_encoded pd.get_dummies(df, columnscategorical_cols) # 归一化只对连续数值列做最小最大缩放 numeric_cols [duration, src_bytes, dst_bytes, hot, count] for col in numeric_cols: min_val df_encoded[col].min() max_val df_encoded[col].max() df_encoded[col] (df_encoded[col] - min_val) / (max_val - min_val 1e-8) return df_encoded这段代码把字符串类别转成离散的0/1向量再把连续特征缩放到[0,1]区间。1e-8是为了避免max_val - min_val为0时产生除零错误——实际数据里有的特征如urgent几乎全是0不做这个保护就会直接报错。注意protocol_type只有tcp、udp、icmp三种One-Hot后增加3列service有70种左右flag有11种编码后特征维度会从41膨胀到120。A3C网络的输入层神经元个数必须与编码后的维度一致改数据集前先跑一遍df_encoded.shape确认。2.2.1 特征列划分与协议类型转换项目里有个细节formated_test_simple.data和formated_test_multi.data是两个预处理后的测试集版本。简单的版本只保留部分特征多版本保留全部编码特征。我一般会建议保留全特征因为A3C本身有特征提取能力没必要在入口做人工降维。但要注意data_preprocessing.py里如果直接读取KDDTrain.txt训练集和测试集的特征列顺序必须完全一致否则状态向量的含义就错位了。python data_preprocessing.py --train KDDTrain.txt --test KDDTest.txt --output_prefix formated_test常见做法是用--output_prefix同时生成多个格式的中间文件比如simple只保留与TCP连接状态直接相关的10个特征multi保留全部。项目给的formated_test_type.data和formated_test_type.data可能是在不同归一化方式下生成的调试时建议固定同一份预处理输出对比模型差异时才不会互相干扰。2.2.2 归一化方法与状态向量拼接KDD99里src_bytes的范围从0到十几万而same_srv_rate是0到1的小数。如果直接拼接强化学习的梯度会被大数值特征主导。这里用的是Min-Max归一化简单有效。但有个坑测试集里可能出现在训练集没见过的特征最大值所以归一化的缩放参数必须从训练集统计并保存下来应用到测试集。代码里如果fit在df_encoded上之后加载测试数据时需要传入同样的min_val和max_val否则归一化结果不一致。2.3 数据集划分20%训练集与KDDTest-21的用途压缩包里的KDDTrain_20Percent.txt是完整训练集的20%随机子集。强化学习训练耗时长先用小样本验证代码能跑通再切换全量数据。KDDTest-21.txt是过滤掉低难度样本后的测试集难度更高。我在复现时建议用KDDTrain_20Percent.txt做快速实验用KDDTest.txt做最终评估。注意这些数据文件的行数不同预处理脚本里不要把样本数和特征数写死用len(df)动态读取。3. 自定义环境my_enviroment.py把入侵检测建模为MDP3.1 观测空间、动作空间与奖励设计强化学习环境必须实现reset()和step(action)两个核心接口。这个项目的my_enviroment.py把每一个网络连接样本当作一个时间步的状态——也就是说这里把「一个入侵检测会话」建模成马尔可夫决策过程而不是一次分类。# my_enviroment.py 核心框架 import numpy as np class IntrusionEnv: def __init__(self, data, labels): self.data data # shape: (n_samples, n_features) self.labels labels # 0normal, 1attack self.current_step 0 self.n_samples len(data) def reset(self): self.current_step 0 return self.data[0] def step(self, action): # 当前样本的真实标签 true_label self.labels[self.current_step] # action0 放行action1 报警 if action true_label: reward 1.0 else: reward -1.0 self.current_step 1 done self.current_step self.n_samples next_state self.data[self.current_step] if not done else np.zeros_like(self.data[0]) return next_state, reward, done, {}奖励设计是这套系统的灵魂。action true_label时给正奖励否则给负奖励相当于一个即时反馈的分类器。但这样设计有个缺陷对于U2R和R2L这类极少样本模型会很快学会「永远预测normal」来获得高平均奖励。A3C在实现时通常会在奖励里加入熵正则项避免策略过早收敛到这种次优解。你会发现estimators.py里大概率有entropy_beta这个超参数就是为了控制探索。3.2 马尔可夫决策过程四个关键函数除了reset和step环境还需要提供get_action_space_size()和get_state_shape()供网络初始化使用。在my_enviroment.py中状态是一个numpy数组动作是0或1的离散整数。def get_action_space_size(self): return 2 # 0放行, 1阻断 def get_state_shape(self): return self.data.shape[1],动作空间只有2维这降低了强化学习的难度但也意味着模型无法区分具体攻击类型。如果想把系统升级为多分类入侵检测可以把动作空间改为5normal 4类攻击同时奖励函数改成只有在攻击类别匹配时才给正奖励。KDDTest.arff.txt里保留了原始攻击名可以做更细粒度的分桶。3.3 为什么用A3C而不是DQN非平稳性与多步回报DQN用经验回放池打破样本相关性但入侵检测数据是高度非平稳的——攻击模式在时间上不断变化回放池里的旧样本会干扰当前的策略学习。A3C的思路是开多个worker并行采样每个worker有自己的环境副本异步更新全局参数。这样不需要回放池也能获得多样性的样本而且N步回报N-step return能捕捉到连续几个连接之间的时序依赖。比如一次端口扫描会有多个异常连接的累积效应单步奖励看不到这个模式N步回报就能用累计奖励表达「连续多个可疑连接后的惩罚」。A3C的另一个优势是同时学习策略Actor和状态价值CriticCritic输出的价值函数可以当作异常分数。部署时如果不想让模型直接做阻断可以只看Critic的输出来排序可疑连接这也是policy_monitor.py可能承担的职责。4. A3CtypeAD.py与worker.py异步优势演员-评论家实现4.1 网络结构共享特征层与Actor-Critic双头输出A3CtypeAD.py是这个项目的核心训练脚本。网络结构一般是一个三层全连接网络输入层接预处理后的特征向量中间隐藏层用ReLU激活输出层分两个头Actor头输出动作概率分布Critic头输出状态价值。# A3CtypeAD.py 网络定义片段 import torch.nn as nn import torch.nn.functional as F class ActorCritic(nn.Module): def __init__(self, input_dim, action_dim): super().__init__() self.shared nn.Sequential( nn.Linear(input_dim, 128), nn.ReLU(), nn.Linear(128, 128), nn.ReLU() ) self.actor nn.Linear(128, action_dim) self.critic nn.Linear(128, 1) def forward(self, x): features self.shared(x) policy_logits self.actor(features) value self.critic(features) return policy_logits, value共享特征层的好处是特征提取部分可以被两个头复用。训练时既要最大化策略梯度又要回归价值函数所以总损失是策略损失 价值损失 × 系数 熵正则项。estimators.py里通常封装了这些损失函数的计算。4.2 worker.py中的rollout采样与N步回报计算A3C的每个worker独立与环境交互收集一段轨迹rollout后计算优势函数再异步更新全局网络。worker.py里核心的是N步回报计算# worker.py 中n-step回报计算的示意 def compute_n_step_return(rewards, values, gamma0.99, n_steps5): returns [] R 0.0 # 从后往前计算折扣回报 for t in reversed(range(len(rewards))): R rewards[t] gamma * R returns.insert(0, R) returns np.array(returns) # 优势 N步回报 - 当前价值估计 advantages returns - values.detach().numpy() return returns, advantagesgamma是折扣因子控制未来奖励的重要程度。入侵检测场景里如果一次攻击被正确阻断后面几个正常连接就不会被打断所以gamma设得偏高0.99可以让模型更看重长期收益。n_steps是采样步长最少设4最大可以覆盖整个句子。值设太大会让每个worker的单次更新变慢设太小又退化成单步TD误差。4.3 policy_monitor.py与estimators.py策略熵正则与性能追踪policy_monitor.py的作用是周期性记录策略的熵值。熵越大说明策略越随机探索充分熵越小说明策略趋于确定。如果训练初期熵值骤降到接近0说明模型在没学够时就锁死了选择这时候需要调高熵正则系数。estimators.py里一般会有如下计算# estimators.py 中熵正则项 def compute_entropy(policy_probs): # policy_probs: shape [batch, action_dim] return -torch.sum(policy_probs * torch.log(policy_probs 1e-10), dim-1).mean()损失函数里加上entropy_beta * entropy的负项因为我们要最大化熵鼓励探索所以梯度方向是让熵增大。训练时把熵值输出到日志就能直观看到策略是早熟了还是还在乱试。4.4 启动与超参数A3C_IDS.bat里的实际配置压缩包里的A3C_IDS.bat是Windows下的启动脚本类似echo off python A3CtypeAD.py --env my_enviroment.py --workers 4 --gamma 0.99 --entropy_beta 0.01 --lr 0.0001 --max_steps 1000000--workers是并行的进程数量建议设成CPU核心数。--lr是学习率A3C对学习率敏感0.0001到0.001之间比较安全。max_steps是所有worker总计的训练步数用20%数据集时50万步足够看到收敛趋势。5. 复现实验与效果验证A3C_test_type.eps怎么看、模型怎么调5.1 运行环境依赖与requirment.txt清单先看requirment.txt重点确认三个库torch、numpy、pandas。版本不需要最新PyTorch 1.10以上就可以跑。如果自己电脑是纯CPU环境训练一个epoch大约需要几分钟到十几分钟不等建议先用20%数据试试水。完整版KDDTrain.txt有12万条样本4个worker训练100万步CPU上大概2-3小时。提示如果torch安装失败或版本冲突先升级pip再安装。Python版本建议3.8到3.10某些依赖在3.11上会编译报错。5.2 使用预处理脚本生成formated_test_*.data运行训练前必须生成预处理后的数据。项目里直接提供了formated_test_simple.data、formated_test_multi.data等文件但为了可复现建议自己用data_preprocessing.py重新生成一份python data_preprocessing.py --train KDDTrain_20Percent.txt --test KDDTest.txt --output formated_train.data --output_test formated_test.data生成后检查一下文件大小如果只有几KB大概率是空文件或者编码出错。再用head -1 formated_test.data看第一行确认特征数量一致。5.3 从EPS图判断收敛准确率、奖励曲线与攻击类型识别训练结束后会输出A3C_test_type.eps和A3C_test_type_0.eps这是用matplotlib保存的矢量图。用支持EPS的查看器打开或者用gs命令转成PNG:gs -dNOPAUSE -dBATCH -sDEVICEpng16m -r150 -sOutputFileresult.png A3C_test_type.eps图里一般包含三类曲线平均奖励、策略熵、各类别检测准确率。重点看两个指标一是平均奖励是否在约10万步后稳定在0.85以上二是U2R那一列的准确率有没有明显波动。U2R样本极少曲线抖动大是正常的不要因为一条抖动曲线就否定模型。5.4 调参思路与常见坑第一个坑是数据泄漏。预处理阶段如果用了全量数据的均值做归一化验证集指标会虚高。代码里必须拆分fit和transform的阶段只在训练子集上拟合。第二个坑是环境里done标志处理不对导致最后几步的reward被丢掉。第三个坑是worker数量太多CPU线程切换开销超过并行收益4到8个就好。调参时优先动entropy_beta。如果训练日志里准确率一直上不去把entropy_beta从0.01降到0.001并确认gamma是0.99。如果策略熵下降太快则反方向调大entropy_beta到0.05。另外把隐藏层从128换到256通常能提升对R2L的识别率但训练时间大概会增加30%。最后如果你要在自己的数据集上复用这套代码只要保持输入是numpy.ndarray且标签为0/1环境接口完全不用改替换掉文件读取部分就行。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价