拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

深度强化学习入侵检测实战:DQN状态动作奖励设计、调参与测试集排坑指南

深度强化学习入侵检测实战:DQN状态动作奖励设计、调参与测试集排坑指南

简介:基于深度强化学习(A3C算法)的网络入侵检测系统Python源码,附带KDD Cup数据集,面向计算机、信息安全、人工智能等专业在校学生与开发者,适合作为毕业设计、期末大作业或初期项目立项演示。资源打包为zip格式,共50个文件,涵盖16个py源码模块,包括数据预处理、自定义环境构建、A3C训练与测试、策略监控等核心环节;20个txt文件提供数据集及说明文档;8个data文件为处理后的样本数据;另有4个eps实验结果图和2个bat批处理脚本,包体大小约19.06MB。项目代码结构清晰,可直接运行测试,能够完整呈现深度强化学习在网络入侵检测任务中的建模与训练流程,帮助读者理解A3C算法、状态空间设计及攻击类型识别思路,具备较高的学习借鉴价值。目前已有343人学习下载,适合需要快速上手强化学习与网络安全交叉方向的读者使用。

1. 基于深度强化学习的网络入侵检测系统:这份源码到底值不值得吃透

做安全方向的人对入侵检测应该都不陌生:规则引擎堆了一堆特征,误报还是压不下去;传统机器学习模型跑得挺快,遇到没见过攻击变种又容易抓瞎。这份基于深度强化学习开发的网络入侵检测系统Python源码,把检测逻辑训练成一个能根据环境反馈不断调整策略的智能体,随包带数据集,解压后就能跑通从训练到评估的完整链路。它适合两类人:一类是拿深度强化学习算法做毕设或练手、但不想只跑老掉牙CartPole的Python工程师;另一类是正在做异常流量识别、苦于没有安全工作台可参考的安全研发或运维。核心价值在于把“怎么检测攻击”从写规则变成训决策,后面所有改代码的动作,都围绕这一点展开。

2. 把入侵检测建模成强化学习问题:状态、动作、奖励三个核心设计

拿到这份源码,第一件该做的事不是急着跑训练,而是看懂作者(或者你自己要延续的)建模方式。强化学习和普通监督学习最大的区别是:它没有静态的“标签-预测”对,只有“状态-动作-奖励-下一状态”的交互闭环。入侵检测要怎么放进这个闭环,决定了后面所有代码怎么改。

2.1 状态表示:网络流特征与原始报文两种喂法

把网络流量喂给强化学习智能体,最常见的路线是特征级表示。直接拿入侵检测数据集里每条连接记录的特征做状态。NSL-KDD这条路线最有代表性:每条记录41个字段,其中protocol_type、service、flag这类枚举特征需要做one-hot展开,数值特征做归一化。这个方案的优点是状态维度小、训练快、可解释性强;缺点是特征工程做得好不好,直接决定智能体的上限。这类基于深度强化学习开发的网络入侵检测系统,默认走的就是这条路——压缩包里的数据集解压后就是这种“一行样本代表一条网络连接记录”的格式。

第二条路是报文级表示。不提取特征,直接把原始流量的字节序列、包长序列或会话前N个包丢给CNN或LSTM做状态编码。这个做法上限更高,因为特征由模型自己学,但训练成本和数据量要求都上去了,单机跑起来非常吃力。我一般建议新人先把特征级流程跑通,再考虑报文级。状态向量就是一行样本特征,智能体每走一步看到的是一个维度固定的数组,这一步的action、reward和下一步的state构成一条经验记录。

这里有一个直接影响训练效果的点:state的数值尺度。protocol_type被one-hot成0/1,但duration可能到几百,src_bytes可能到几十万。这种尺度差异会让DQN的Q值更新非常不稳定,所以代码里统一用MinMaxScaler把所有列压到[0,1]区间。这是新手最容易跳过的步骤之一,也是第五章排查单里最高发的黑匣子之一。

2.2 动作空间:二分类、多分类与特征选择型动作

动作空间决定智能体能做什么,它的设计比网络结构更影响成败。常见设定有三种。

第一种是二分类:动作0=正常,动作1=攻击。最简单,训练最稳,适合只关心“有没有问题”的监控场景。第二种是多分类:按数据集标注粒度分出DoS、Probe、R2L、U2R等攻击族,每个族一个动作。检出的同时能拿到攻击类型,但类不平衡问题立刻变严重,训练曲线也抖得厉害。第三种是特征选择型动作:智能体每次从特征全集里挑一部分“看”,再决定是否报警。这种设计适合解决特征多、冗余高的场景,但训练复杂度高,不建议作为第一个版本。

包里常见的实现是二分类或四分类。四分类对应NSL-KDD里最常见的normal/DoS/Probe/R2L+U2R合并,因为R2L和U2R样本量极少,拆太细根本训不起来。我自己做类似项目时也沿用这个取舍:把样本量少的攻击族合并成一个大类,先保证检出率,再做家族细分。

动作空间与网络输出的对应关系是:DQN网络最后一层神经元个数等于动作个数,输出每个动作的Q值,训练时取最大Q值对应的动作作为决策。修改动作空间只需要改action_dim这一个参数,以及y标签的映射关系,网络结构和奖励函数都不用动,这对想拿这份代码去跑自己数据的人来说很友好。

2.3 奖励函数:检测率、误报率与生存时长的权衡

强化学习里一切都由奖励驱动,所以奖励函数是整份源码里最该仔细读的地方。最朴素的写法是:判别正确给+1,判别错误给-1。一步一奖励,智能体很快能学到“跟着多数类走”——测试集里Normal占比高,全部猜Normal也有很高的准确率,但攻击检出率趋近于0。这就是奖励设计失衡的典型翻车现场。

稍微好一点的写法是引入不对称奖励:攻击被检出给+1,正常流量判对给+0.1,误报给-2,漏报给-3。目的是让智能体把“漏报攻击”当成代价最大的错误,因为入侵检测里漏报的代价远高于误报。

# 常用不对称奖励函数,直接放在训练循环里 if action == label: reward = 1.0 if label == 1 else 0.1 else: reward = -2.0 if action == 1 else -3.0

这段代码的思路是:只有攻击类别的正确判定有高奖励,正常流量的正确判定给一个小奖励维持温和信号;误报和漏报都扣分,但漏报扣得更狠。参数怎么调取决于业务场景——如果你的场景里误报会淹没日志系统,就把误报的-2改到-5;如果攻击漏掉会直接造成财产损失,就把漏报的-3继续加大。

还有一个细节:奖励的绝对数值会影响DQN的训练稳定性。奖励值如果超过±10,Q值更新很容易震荡。把这四条奖励控制在[-5, 1]区间内,是我做这个方向多年踩坑换来的经验。你在自己的环境里调奖励时,宁可先小幅度调,也别直接从几百的惩罚开始试。

3. 用Python跑通最小DRL-NIDS:从数据加载到训练

这一章直接给你一套可复现的最小实现。全程用深度强化学习算法里的DQN(Deep Q-Network)做载体,因为它在NIDS这类离散动作场景下最稳、最容易调试。代码按三个小节拼起来,就是一个能跑通的完整训练脚本。

3.1 环境准备与数据集加载

先建独立环境,避免把系统Python弄脏。这一步相当于Python安装教程里的常规操作,但对这种带PyTorch依赖的项目很关键。

cd 项目目录 python -m venv drl-nids-env source drl-nids-env/bin/activate # Windows用 drl-nids-env\Scripts\activate pip install torch pandas numpy scikit-learn matplotlib

然后加载数据集。NSL-KDD打开后是KDDTrain+.txt这种格式,每行41个特征加一个标签。

import pandas as pd import numpy as np from sklearn.preprocessing import MinMaxScaler from sklearn.model_selection import train_test_split # NSL-KDD 的41个字段名,按官方顺序排列 col_names = ['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', 'count', 'srv_count', 'serror_rate', 'srv_serror_rate', 'rerror_rate', 'srv_rerror_rate', 'same_srv_rate', 'diff_srv_rate', 'srv_diff_host_rate', 'dst_host_count', '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'] df = pd.read_csv('data/KDDTrain+.txt', header=None, names=col_names) # 标签形如 normal. / neptune. / warezclient.,去掉点号后统一 df['label'] = df['label'].str.rstrip('.') # 二分类映射:normal为0,其余攻击族统一合并为1 attack_map = {'normal': 0} df['label'] = df['label'].apply(lambda x: attack_map.get(x, 1)) # 枚举特征one-hot展开,数值特征交给scaler df = pd.get_dummies(df, columns=['protocol_type', 'service', 'flag']) X = df.drop('label', axis=1).values.astype(np.float32) y = df['label'].values.astype(np.int64) # 关键:先切分,再fit scaler,防止数据泄漏 X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42, stratify=y) scaler = MinMaxScaler() X_train = scaler.fit_transform(X_train) X_test = scaler.transform(X_test) # 复用同一个scaler,不能重新fit

上面这段有两个容易忽略的参数。train_test_split里的stratify=y是按标签比例分层切分,保证训练集和测试集里攻击样本占比一致,否则随机切分可能把攻击样本全切到一边,训练直接废掉。random_state=42固定切分结果,保证每次跑代码拿到的训练集测试集完全一样,后面调参才有可比性。

3.2 DQN智能体结构:三层MLP就够

NIDS的特征级状态维度不高,一般几十到一百多,用三层MLP做Q网络足够。不用上来就堆Transformer,那只会让训练变慢,效果未必更好。

import torch import torch.nn as nn import random from collections import deque class DQN(nn.Module): def __init__(self, state_dim, action_dim, hidden=64): super().__init__() self.net = nn.Sequential( nn.Linear(state_dim, hidden), nn.ReLU(), nn.Linear(hidden, hidden), nn.ReLU(), nn.Linear(hidden, action_dim) # 输出每个动作的Q值 ) def forward(self, x): return self.net(x) class ReplayBuffer: def __init__(self, capacity=20000): self.buf = deque(maxlen=capacity) def push(self, s, a, r, s2, done): self.buf.append((s, a, r, s2, done)) def sample(self, batch_size): batch = random.sample(self.buf, batch_size) s_b = torch.FloatTensor([e[0] for e in batch]) a_b = torch.LongTensor([e[1] for e in batch]).unsqueeze(1) r_b = torch.FloatTensor([e[2] for e in batch]).unsqueeze(1) s2_b = torch.FloatTensor([e[3] for e in batch]) d_b = torch.FloatTensor([e[4] for e in batch]).unsqueeze(1) return s_b, a_b, r_b, s2_b, d_b def __len__(self): return len(self.buf)

hidden=64是我在41维特征输入下的默认值。特征one-hot展开后维度会涨到一百上下,64个隐藏神经元足够拟合Q值。如果你的数据集特征更多,可以调到128,但不要一上来就256以上,过拟合会让测试集效果反而变差。ReplayBuffer用deque(maxlen=capacity)实现,容量满了自动丢弃最旧样本,这是DQN经验回放的标准做法。

3.3 训练循环:经验回放、目标网络与epsilon衰减

这是整份源码的核心骨架。训练循环要同时做四件事:按epsilon-greedy策略选动作、算奖励、存经验、从经验里采样更新网络。我按episode组织训练,每个episode从训练集随机起点连续走256步,这样训练成本可控,也符合“一段会话过程”的直觉。

action_dim = 2 # 正常/攻击 state_dim = X_train.shape[1] device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') dqn = DQN(state_dim, action_dim, hidden=64).to(device) target_net = DQN(state_dim, action_dim, hidden=64).to(device) target_net.load_state_dict(dqn.state_dict()) # 初始权重同步 optimizer = torch.optim.Adam(dqn.parameters(), lr=1e-3) loss_fn = nn.MSELoss() replay = ReplayBuffer(capacity=20000) batch_size = 64 gamma = 0.99 # 折扣因子,衡量未来奖励的重要程度 epsilon = 1.0 # 初始探索率 epsilon_min = 0.05 # 最低探索率 epsilon_decay = 0.995 # 每个episode结束后衰减 step_per_episode = 256 target_update_freq = 10 num_episodes = 300 for episode in range(num_episodes): start = np.random.randint(0, len(X_train) - step_per_episode) state = X_train[start] for t in range(start, start + step_per_episode): # epsilon-greedy:探索期随机选动作,后期选当前Q值最大的动作 if np.random.rand() < epsilon: action = np.random.randint(action_dim) else: with torch.no_grad(): q = dqn(torch.FloatTensor(state).unsqueeze(0).to(device)) action = q.argmax().item() # 不对称奖励:攻击判对+1,正常判对+0.1,误报-2,漏报-3 label = y_train[t] if action == label: reward = 1.0 if label == 1 else 0.1 else: reward = -2.0 if action == 1 else -3.0 done = (t == start + step_per_episode - 1) # 本episode最后一步视为终止 next_state = X_train[t + 1] if not done else state replay.push(state, action, reward, next_state, done) state = next_state # 经验足够后开始训练 if len(replay) >= batch_size: s_b, a_b, r_b, s2_b, d_b = replay.sample(batch_size) s_b, a_b, r_b, s2_b, d_b = (s_b.to(device), a_b.to(device), r_b.to(device), s2_b.to(device), d_b.to(device)) q_pred = dqn(s_b).gather(1, a_b) # 当前Q值 with torch.no_grad(): q_next = target_net(s2_b).max(1, keepdim=True)[0] q_target = r_b + (1 - d_b) * gamma * q_next # 目标Q值 loss = loss_fn(q_pred, q_target) optimizer.zero_grad() loss.backward() optimizer.step() # 每个episode结束后衰减探索率,定期同步目标网络 epsilon = max(epsilon_min, epsilon * epsilon_decay) if episode % target_update_freq == 0: target_net.load_state_dict(dqn.state_dict()) if episode % 20 == 0: print(f'episode {episode} | epsilon {epsilon:.3f} | loss {loss.item():.4f}')

这段代码里有三个设计要说明。第一,目标Q值里(1 - d_b) * gamma * q_next,终止状态直接拿奖励做目标,不再往后看未来Q值,这是DQN终止状态的标准处理。第二,目标网络每10个episode同步一次,目的是让训练目标不要每步都变,Q值更新才稳定。如果每步都用最新网络算目标,Q值容易发散成一条乱跳的曲线。第三,done标志只在episode最后一步置True,中间步骤next_state取下一行样本,等于把数据集的连接记录当作一个时间序列在看,这是简化的处理方式;真要在生产环境用,应该按会话ID和时间戳重组顺序,而不是用数据集里的原始行序。

4. 让模型训得稳的四个参调动作:学习率、epsilon、回放容量与奖励尺度

很多人拿到源码第一件事是跑默认参数,损失能降,但测试集指标一般。问题不在代码,在于DQN的默认参数是为游戏环境调的,入侵检测的数据分布和奖励密度都不同,必须做针对性调整。这一章我把最影响结果也最容易踩坑的四个参数讲透。

4.1 学习率和epsilon衰减必须配合调

学习率决定Q网络参数每次更新的步长。1e-3在CartPole这类简单环境里没问题,但在NIDS这种奖励稀疏、样本不平衡的场景下,1e-3经常导致训练中期Q值震荡,loss曲线哗哗抖。我一般先把学习率降到5e-4,如果发现loss下降明显变缓再提回1e-3。

epsilon衰减决定智能体从“满世界探索”到“专心利用所学策略”的速度。衰减太快,模型还来不及见全攻击样本就已经进入纯利用模式,很多攻击族一局都没探索到;衰减太慢,后期还在大量随机乱试,训练不收敛。经验值是:初始1.0,每个episode乘以0.995,300个episode后大约降到0.05,这个节奏配合学习率5e-4,在NSL-KDD上通常能稳定收敛。

这两个参数必须配合调的原因是它们互相牵制:学习率大但探索率高,网络会被连续的高方差样本带偏;探索率低但学习率小,模型收敛速度慢,训练时间翻倍还不见得好。我的习惯是固定epsilon_decay,先试三个学习率,画出训练曲线选抖动最小的那组,再回过来微调衰减系数。

4.2 经验回放容量不是越大越好

经验回放的直观理解是“让模型从历史经验里反复学习”。容量越大,模型越能抵抗样本间相关性,但代价是旧经验在缓冲区里停留时间过长。NIDS的流量模式在变,攻击样本分布也在变,如果缓冲区塞满了三个月前的旧经验,新出现的攻击模式会被旧经验淹没,模型对当前流量形态的反应变慢。

源码里ReplayBuffer默认20000,这个数值对NSL-KDD这种万级样本足够。换到CICIDS2017这种百万级数据集时,我建议把容量调大到50000左右,但不要超过100000,否则每次采样都从海量历史里挑,训练速度明显变慢,收益却不明显。判断标准很简单:如果训练后期loss仍然波动剧烈,先把容量调小而不是调大,多数情况能更快稳住。

4.3 奖励裁剪:防止Q值膨胀

DQN一个出名的问题是Q值高估。NIDS场景下特别容易发生:攻击样本少,奖励稀疏,部分状态的Q值被网络记忆扭曲,越训越高。奖励裁剪是最直接的抑制手段,把每步奖励限制在固定区间,防止异常大奖励把整个Q值分布带飞。

实践中我坚持三条线:攻击检出最高+1,漏报最低-3,单步奖励绝对值不超过3,同时把所有中间取值也压在这个范围。不要用+10、-50这种惩罚,那会让Q值loss在个别样本上暴涨,训练被迫靠降低学习率来“硬扛”,最终两头不讨好。加上奖励裁剪后,如果Q值仍然膨胀,检查是否忘了把目标网络每N步同步回来。

4.4 特征缩放与数据切分方式

最后是数据层面最容易出问题的地方。特征缩放用MinMax还是Standard,对DQN影响不大,但一定要在train_test_split之后再fit,否则scaler在训练阶段看到了测试集的统计量,属于数据泄漏,测试指标会虚高。一旦你带着这个虚高指标去上线,真实环境效果直接打折。

数据切分方式上,普通随机切分即可,但需要stratify=y保证攻击样本在两边比例一致。如果要模拟真实场景的时间泛化能力,就要按时间顺序切分:拿前80%时间段数据训练,后20%时间段数据测试。这种做法对NIDS更重要,因为攻击模式会随时间变化,随机切分会让测试集混入同一时期的相关样本,指标好看但没有实际参考价值。这组参数建议整理成表:

参数默认值推荐范围调参方向
learning_rate1e-33e-4 ~ 1e-3训练震荡就降低
epsilon_decay0.9950.98 ~ 0.999收敛慢就调大
replay容量2000010000 ~ 50000数据集大就调大
batch_size6432 ~ 128训练不稳定就调大
奖励区间[-3, 1]绝对值不超过5收敛差就缩小
target更新频率105 ~ 20Q值不稳就调小

5. DRL入侵检测为何总在测试集翻车:五个常见问题与排查

这一章写的是我这个方向反复踩过的坑,每条都按“现象 → 原因 → 解决”的顺序展开。如果你跑这份源码发现测试集指标不对劲,先对照下面五条排查一遍。

5.1 训练Loss下降但F1低——智能体学到了“怎么做都输”

现象:训练过程loss从几百降到个位数,看起来“收敛了”,但测试集F1不到0.3,甚至不如随机猜。

原因:loss下降只代表Q值预测稳定,不代表策略判别能力提高。类不平衡下,攻击样本占比低,智能体把每条流量都判成normal也能拿很高的累积奖励,所以Q网络学到了“最优策略是全部判normal”。这是DRL-NIDS最常见的黑匣子,不是代码bug,是目标函数和数据分布错位。

解决:改用recall和F1作为主指标,不要盯loss;奖励函数按2.3节改成不对称设计;训练前对训练集做攻击样本加权采样,让智能体每个episode都能遇到足够的攻击样本。

5.2 类不平衡导致智能体躺平

现象:模型到了训练后期epsilon降到最低,输出几乎全是normal,攻击检出率等于零。

原因:攻击样本在NSL-KDD里占比不高,随机探索时命中攻击样本的次数太少,Q网络学到的“normal优势”越来越大,最后彻底压过攻击动作的Q值。

解决:给每个episode的起点加类别加权——按标签分布采样起点,提高攻击样本进入状态序列的概率。代码里把start = np.random.randint(0, len(X_train) - step_per_episode)改成先按类别概率选类别、再在类别内随机选起点。这样每个episode至少能看到一段攻击样本序列,智能体对攻击状态才会有真实的Q值估计。

5.3 数据泄漏:scaler的fit被测试集复用

现象:训练指标正常,测试指标高得离谱(AUC 0.99以上),但一换真实环境就崩。

原因:对全量X做fit_transform后才切分训练集测试集,scaler在训练阶段看到了测试集的统计量,测试集信息提前泄露给模型。更隐蔽的另一种情况:有些人先切分,但误把测试集也单独fit_transform了一遍,等于测试集用了两套不同尺度,模型根本没法泛化。

解决办法就是第3.1节的顺序:先train_test_split,再X_train = scaler.fit_transform(X_train),最后X_test = scaler.transform(X_test)。测试集scaler只做transform,不做第二次fit。这是所有NIDS深度实验的底线,踩一次就知道它多阴险。

5.4 奖励函数让智能体走捷径

现象:智能体学会固定输出攻击,所有流量都被报警,实际是检测系统整个废掉。

原因:奖励函数里攻击检出给+1,误报只扣-1,智能体很容易算出一笔账:把所有样本判为攻击,获得的期望奖励比认真区分高得多。奖励设计不权衡,智能体必然钻空子,这不是模型笨,是目标函数本身有漏洞。

解决:把误报惩罚加大到比正常判对奖励高一个量级,比如误报-2,正常判对+0.1。再观察策略是否还躺平,如果仍然全判攻击,说明漏报惩罚相对误报又偏重了,继续调平衡。我推荐直接把奖励映射到F1的变化量上,比拍脑袋设计多档奖励更稳。

5.5 seed不固定导致训练差异巨大

现象:同一份代码,换个机器或换一次运行,测试集F1差5到10个百分点。

原因:DQN回放采样的随机性、PyTorch参数初始化随机性、数据集切分随机性,三处随机源叠加,结果差异被放大。Python的random、numpy、torch各自持有全局随机状态,只固定其中一个没用。

解决:训练脚本开头一次性固定三处seed,再固定数据集切分的random_state。

import random, numpy as np, torch random.seed(42) np.random.seed(42) torch.manual_seed(42)

固定seed之后,同环境下可以复现同一套结果。但要注意,CPU和GPU的浮点运算顺序仍有微小差异,F1有1-2个点的波动属于正常范围,超过5个点就要怀疑代码里还有未固定的随机源,比如ReplayBuffer的random.sample有没有用全局随机状态。这个问题的玄学程度很高,我见过有人调了一周参数,最后发现是忘了固定numpy的seed,白费功夫。

6. 验证与进阶:换数据集、补基线、留好复现配置

训练脚本跑通之后,下一步是确认这套方案是不是真的比传统方法强。只在一个数据集上出指标说明不了问题,至少要做两件事:换数据集验证,补传统基线对比。

6.1 换数据集验证泛化

把训练脚本里的数据加载换成UNSW-NB15或CICIDS2017,改动点集中在三处:列名映射换成新数据集的字段、枚举特征的one-hot列名要重新对齐、标签映射按攻击族重新合并。其余DQN网络和训练循环完全不用动,这就是特征级DRL-NIDS的迁移优势。

from sklearn.metrics import classification_report # 测试集评估,训练结束后单独跑 y_pred = [] with torch.no_grad(): for i in range(len(X_test)): q = dqn(torch.FloatTensor(X_test[i]).unsqueeze(0).to(device)) y_pred.append(q.argmax().item()) print(classification_report(y_test, y_pred, target_names=['normal', 'attack']))

如果换到UNSW-NB15后F1掉得不多,说明智能体学到的是通用的攻击判别逻辑,而不是记住了NSL-KDD的样本形态。这一步是对这份源码价值最直接的检验。

6.2 基线对比怎么写

对比实验至少要有两个基线:随机森林或XGBoost,以及一个简单的单步监督学习模型。随机森林在同样的训练集上很快就能出一个F1,如果DRL模型连随机森林都打不过,就得回头审视奖励设计或状态构建,而不是继续堆训练轮数。基线代码用sklearn即可,几十行搞定,这里不展开,但对比时注意和DRL模型共用同一个切分后的数据,保证公平。

6.3 留一个训练配置记录习惯

我做这类项目最大的后悔药,是每次训练把关键参数和结果一并落盘。用json记录learning_rate、epsilon_decay、replay容量、reward四档数值、seed、F1和recall,文件名带时间戳。否则调试两周后回看结果,根本不知道哪组参数跑出哪个指标,所有实验全部作废。血泪经验说明,模型能复现和模型效果好同样重要。

现在再回头看这份基于深度强化学习开发的网络入侵检测系统Python源码,你要做的不是追求某个花哨的算法变体,而是把状态、动作、奖励三个闭环设计清楚,再按第四、五章的调参与排查把训练稳下来。跑通它只是第一步,能换数据集、补基线、留好配置,你才算真正吃透了这套检测方案。希望帮到你。

本文还有配套的精品资源,点击获取

返回列表