简介:一份针对软件定义网络(SDN)流量工程问题的学术论文PDF,题为《一种基于深度强化学习的SDN路由算法》,刊于《上海师范大学学报(自然科学版)》2021年第1期。论文面向网络工程、深度学习、数据分析与数据研究领域的读者,适合作为参考文献或专业指导;其核心是DRL-Routing算法,使用较全面的网络信息构造状态,采用一对多网络配置完成路由选择,以奖励函数调节往返路径吞吐量,改善传统OSPF、LL路由在动态流量下难以寻优的不足。压缩包内为1个PDF文件,大小1.36MB,内容完整,已有651人浏览/学习。读者可获取论文全文,内容包括强化学习基本模型、DRL-Routing总体架构(含OpenFlow网络发现、网络监控模块、动作转换器)、仿真实验及与传统路由算法的对比结果;既可用于深度强化学习与SDN路由优化的学习和调试,也可作为课题研究、课程设计和论文写作的参考文献。
1. 把深度强化学习用到SDN路由:从论文到工程实现之间缺什么
一篇《基于深度强化学习的SDN路由算法》读起来总是很顺:状态空间、动作空间、奖励函数三段式整齐,实验里时延比传统算法低百分之二三十。可一旦自己动手复现,就会撞上论文里不写的细节——训练不收敛、控制器下发跟不上决策频率、仿真流量模型和真实网络偏差太大。这篇文章想说的是:深度强化学习确实能用在SDN路由上,但它不是把Dijkstra替换掉那么简单,而是一套牵涉网络拓扑建模、控制器改造、训练稳定性和评估公平性的完整工程。适合刚读完这类论文、打算用Mininet加Ryu或者自有控制器跑通最小Demo的工程师。下面是按我自己的复现路径整理的完整记录,从算法设计讲到训练、踩坑和评估。
2. DRL路由的状态空间、动作空间与奖励函数:三个设计决策决定算法上限
2.1 从“算路径”到“学路径”:DRL到底在拟合什么
传统路由算法把网络抽象成带权图,用Dijkstra或OSPF算最短路径。链路权重来自周期广播,网络状态变化越快,广播周期就越尴尬——短了消耗带宽,长了路径更新滞后。SDN把控制平面集中到控制器之后,理论上可以在每个时间片拿到全网的实时状态,但“给定一组实时状态求一组最优路径”这件事本身是NP难的,尤其面对多约束QoS路由,主流做法只能靠启发式搜索。
深度强化学习换了思路:不再显式求解每条流的路径,而是拟合一个策略函数,输入是网络状态,输出是路由决策。深度神经网络负责从高维状态里提取模式,强化学习负责通过奖励信号让策略往“低时延、低丢包、高吞吐”的方向迭代。这个东西本质上是把在线优化问题变成了序贯决策问题——每到一个决策时刻,观察网络状态,选一个动作,等网络反馈,再更新策略。
这里要提醒一句:DRL路由不是要在所有场景上打败Dijkstra。静态拓扑、低负载、路由变化不频繁时,传统算法又稳又快。DRL的价值集中在流量突发、拓扑局部故障、多目标约束这类传统算法不擅长的动态场景。如果你要复现的论文实验里基线对比是静态场景,先怀疑实验设计是否有问题。
2.2 状态空间怎么构建:全网队列长度与链路利用率的取舍
状态空间的构建直接决定训练难度。最朴素的做法是把每台交换机的实时队列长度、每个端口的收发包计数、链路利用率拼成一维向量,外加一个流表项数量或平均排队时延。这个方案的问题在于维度膨胀——一个中等规模拓扑动辄几十个观测维度,其中很多维度长期不变,网络噪声又会在训练初期把有效信号淹没。
我常用的做法是把观测分成两部分:静态拓扑特征(节点邻居关系、链路带宽)和动态状态特征(端口速率、队列深度、丢包计数),动态特征做归一化后拼接。对于SDN场景,控制器通过OFPPortStatsRequest周期性拉取端口统计就能拿到这些数据,不需要额外埋点。归一化边界也很关键,链路利用率按当前带宽归一化到0到1,队列深度按硬件队列上限归一化,丢包计数用滑动窗口内的增量而不是累积值,否则重启计数器后会突然跳变。
一个容易忽略的细节是:状态向量里不要放原始计数器,要放“变化量”。比如端口接收字节数是一个持续增长的计数器,直接喂给网络会让网络误以为“数值越大状态越异常”,正确做法是保留前一次采样值,用当前值减前一次值除以采样间隔得到速率。
2.3 离散动作还是连续动作:逐跳决策与权值偏置方案
路由决策有两种最常见动作映射方式。
第一种是逐跳决策:agent针对每个交换机的每个目的节点输出下一跳端口,动作空间是所有可能下一跳的集合。这种方案和OpenFlow的流表自然契合,但动作空间随拓扑规模线性增长——14节点拓扑加一台边缘交换机,动作数量就可能突破32,DQN的最后一个全连接层会变得非常宽,探索效率骤降。
第二种是权值偏置方案:agent输出的不是具体下一跳,而是对某个核心节点(比如汇聚层出口)的流量百分比或链路权值偏置量。控制器收到偏置后,在拓扑图上给链路权重加一个偏移量再跑一遍K最短路径。这个方案我用得最多,因为动作空间可以压缩到个位数,而且天然保证路径连通性——DRL只负责“调整网络的权衡”,底层合法性交给确定性算法兜底。
权值偏置方案适合连续动作,通常用DDPG或TD3实现。如果坚持离散动作,可以把偏置量量化成“不做调整、轻度偏向、强烈偏向”三档,动作空间直接砍到个位级别。量化损失在仿真里不大明显,但在真实网络里能显著降低冲突概率。
2.4 奖励函数的三项加权:时延、丢包率与负载均衡
奖励函数是DRL路由里最“玄学”的部分。很多入门复现只给平均时延做负奖励,训练出来模型会疯狂把流量挤向某条低时延链路,导致局部过载,整体吞吐反而下降。正确的奖励函数至少要覆盖三个维度:平均端到端时延、丢包率、负载均衡度。
我一般这样设计:
def compute_reward(stats: dict) -> float: # stats 包含本轮统计:delay_ms, loss_rate, link_utilization alpha, beta, gamma = 0.5, 0.3, 0.2 delay_penalty = stats["avg_delay_ms"] / 50.0 # 期望时延 50ms 为基准 loss_penalty = stats["loss_rate"] / 0.01 # 期望丢包率 1% 为基准 util_list = stats["link_utilization"] utilization_std = np.std(util_list) # 负载均衡度 balance_penalty = utilization_std / 0.5 # 期望标准差低于0.5 reward = -(alpha * delay_penalty + beta * loss_penalty + gamma * balance_penalty) return float(reward)三个系数按场景调整。业务对时延敏感就把alpha加大;对吞吐敏感就把beta加大;对网络稳定性敏感就把gamma加大。这个公式里有个隐藏要点:三个子项都用归一化后的相对量,避免量纲不一致导致某一项主导梯度。
需要特别注意的是,奖励值不要在设计上过早饱和。如果网络状态一直很好,奖励长期接近0,agent的探索动力会大幅下降。可以在网络状态优于基线(比如时延低于静态路由时)时额外给一个小的正奖励,让agent明确知道自己“做对了”。
2.5 DQN、DDPG还是PPO:路由场景下的框架选择
选型没有标准答案,但有清晰边界。
DQN适合离散动作、状态维度不高、拓扑规模固定的场景。实现简单、调参直觉性强,适合第一个最小闭环。缺点是动作空间大时Q值估计不可靠,而且传统DQN的过估计问题需要靠Double DQN或Dueling DQN缓解。
DDPG/TD3适合连续动作,和权值偏置方案是绝配。TD3在DDPG基础上加了裁剪双Q学习,超参数敏感度低很多,是我在真实流量仿真里的主力。缺点是对奖励尺度和探索噪声很敏感,训练初期容易崩。
PPO的优势是稳定,on-policy性质在部分场景下探索更充分,但样本效率低,每轮更新都要新采一轮数据。仿真网络里跑一次交互很便宜,PPO也能接受;但如果你后面要接真实网络,样本效率会变成硬伤。
我给一个保守的推荐路径:先做DQN跑通链路,再把动作换成连续偏置、算法换成TD3,最后用PPO做基线对比。这样每一步都能定位问题出在算法还是环境接口。
3. 用Mininet+Ryu+Stable-Baselines3跑通最小闭环:环境搭建与训练主循环
3.1 环境版本选择:CPU也能跑,关键是别用太新的库
仿真环境不需要GPU。DRL路由的状态维度通常是几十维,决策网络两三层MLP就够,CPU训练完全跑得动。我按下面这套版本组合跑过多次,兼容性最省心:
| 组件 | 推荐版本 | 说明 |
|---|---|---|
| Ubuntu | 20.04 / 22.04 | 系统版本影响不大 |
| Mininet | 2.3.0 | 自带的mn命令即可 |
| Ryu | 4.34 | 纯Python控制器,改起来方便 |
| Python | 3.8 / 3.9 | Stable-Baselines3对3.10以下支持最好 |
| PyTorch | 1.13 CPU版 | SB3依赖它做网络计算 |
| Stable-Baselines3 | 1.8.0 | DQN/TD3/PPO都有现成实现 |
一个版本陷阱:Stable-Baselines3从2.0开始要求Python高于3.8,且依赖的gymnasium和gym接口有破坏性变化。如果你按旧教程写的from gym import spaces,在SB3 2.x下会报环境注册错误。我建议锁死版本,用pip install stable-baselines3==1.8.0 gym==0.21,别追新。
3.2 搭一个6节点SDN拓扑:Mininet脚本与流量注入
拓扑规模不要一开始就上14节点。6节点两层拓扑足够验证算法收敛性,训练一轮也快。下面这个脚本创建2台核心交换机加4台边缘交换机,每台边缘交换机挂一台主机,链路带宽100Mbps,添加固定时延:
#!/usr/bin/env python3 from mininet.topo import Topo from mininet.net import Mininet from mininet.link import TCLink from mininet.node import RemoteController from mininet.cli import CLI class DRLTopo(Topo): def build(self): cores = [self.addSwitch('c%d' % i) for i in range(2)] edges = [self.addSwitch('e%d' % i) for i in range(4)] # 核心层和边缘层全互联 for c in cores: for e in edges: self.addLink(c, e, bw=100, delay='1ms', loss=0) # 每台边缘交换机挂一台 host for i, e in enumerate(edges): host = self.addHost('h%d' % i, cpu=0.5) self.addLink(host, e, bw=100, delay='0.5ms', loss=0) topo = DRLTopo() net = Mininet(topo=topo, link=TCLink, controller=RemoteController) net.addController('c0', controller=RemoteController, ip='127.0.0.1', port=6633) net.start() CLI(net) net.stop()脚本里最关键的是TCLink,它让链路真正带上带宽和时延参数,而RemoteController把控制平面指向本机Ryu监听的6633端口。loss=0一开始先不注入丢包,方便验证训练逻辑,后面加随机丢包再看算法表现。
流量注入我用iperf的UDP流模拟突发。均匀随机选择源和目的主机,每个决策周期内启动一条持续5秒的UDP流:
# 在h1上启动 UDP server 监听5001 iperf -s -u -p 5001 & # 在h3上向h1的IP注20Mbps UDP流,持续60秒 iperf -c 10.0.0.1 -u -p 5001 -b 20M -t 60 &注意Mininet自动分配的IP是从10.0.0.1开始的,不同拓扑里host编号和IP对应关系要先在CLI里查清楚再写死进训练脚本。
3.3 改造Ryu控制器:开放统计查询接口并把决策变成flow_mod
Ryu控制器的核心任务有两个:周期收集端口统计给agent当观测,拿到agent动作后生成对应flow_mod下发。下面是一个精简版实现:
from ryu.base import app_manager from ryu.controller import ofp_event from ryu.controller.handler import MAIN_DISPATCHER, set_ev_cls from ryu.ofproto import ofproto_v1_3 from ryu.lib import hub import threading class DRLController(app_manager.RyuApp): OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.datapaths = {} self.latest_stats = {} # 端口统计快照 self.agent = None # 训练进程注入的接口 self.monitor_thread = hub.spawn(self._monitor_loop) @set_ev_cls(ofp_event.EventOFPSwitchFeatures, MAIN_DISPATCHER) def switch_features(self, ev): datapath = ev.msg.datapath self.datapaths[datapath.id] = datapath self.logger.info('switch %s connected', datapath.id) def _monitor_loop(self): while True: for dp in self.datapaths.values(): self._request_port_stats(dp) hub.sleep(2) # 每2秒拉一次统计 def _request_port_stats(self, datapath): parser = datapath.ofproto_parser req = parser.OFPPortStatsRequest(datapath, 0, datapath.ofproto.OFPP_ANY) datapath.send_msg(req) @set_ev_cls(ofp_event.EventOFPPortStatsReply, MAIN_DISPATCHER) def port_stats_reply(self, ev): dp = ev.msg.datapath stats = {p.port_no: (p.rx_bytes, p.tx_bytes, p.rx_dropped) for p in ev.msg.body} self.latest_stats[dp.id] = stats这个控制器的统计收集频率是2秒一次。决策周期如果比统计周期短,agent看到的状态总是旧数据,训练会很不稳。我一般把决策频率和统计频率做成同一个值,比如都是2秒,这样每个决策周期的观察和反馈是一一对应的。
动作下发部分更直接,根据agent给出的端口号构造OFPActionOutput,然后下发优先级别较高的精确流表项。对于还没有流表项的新流,控制器依然保持默认的PacketIn上报,避免新流因为没有流表而被丢弃。
3.4 训练主循环:DQN参数设置、经验回放与模型保存
训练进程要写一个自定义gym环境,把SDN网络包成一个标准接口。环境里必须包含reset和step两个方法:
import time import numpy as np import gym from gym import spaces from stable_baselines3 import DQN class SDNRoutingEnv(gym.Env): def __init__(self, net, controller): super().__init__() # 动作空间:4条可用路径的编号 self.action_space = spaces.Discrete(4) # 观测:8个核心端口的归一化利用率 + 4条主机队列深度 self.observation_space = spaces.Box(low=0, high=1, shape=(12,), dtype=np.float32) self.net = net self.controller = controller self.stats = {} def _collect_obs(self): stats = self.controller.latest_stats obs = [] # 这里按固定顺序取出链路利用率,顺序不能乱 for switch_id in sorted(stats.keys()): for port_no in [1, 2, 3]: rx, tx, drop = stats[switch_id][port_no] rate = (rx + tx) / (2 * 100 * 1024 * 1024) # 归一化到100Mbps obs.append(min(rate, 1.0)) return np.clip(np.array(obs, dtype=np.float32), 0, 1) def step(self, action): self.controller.set_route(action) # 下发对应流表 time.sleep(2) # 等待网络状态更新 obs = self._collect_obs() reward = compute_reward(self.controller.latest_stats) done = False return obs, reward, done, {} def reset(self): # 清空旧流表并重新注入基准流量 self.controller.clear_routes() time.sleep(1) return self._collect_obs()这里有个设计细节:time.sleep(2)是同步阻塞方式,训练进程等网络更新时会浪费算力。更高效的做法是启动一个独立的统计线程不断刷新self.stats,step里只做动作下发,然后立刻从最新统计里取数据。同步版本胜在逻辑简单,适合先验证收敛性,后面调优再改成异步。
训练入口如下:
env = SDNRoutingEnv(net, controller) model = DQN( "MlpPolicy", env, learning_rate=1e-3, buffer_size=100000, learning_starts=1000, # 前1000步只探索不学习 batch_size=64, gamma=0.99, target_update_interval=1000, exploration_fraction=0.3, train_freq=4, verbose=1, ) model.learn(total_timesteps=50000) model.save("drl_routing_dqn.zip")learning_starts一定要设够大。网络环境动作反馈有延迟,前几百步采到的样本质量差,太早学习会把噪声当信号。exploration_fraction设为0.3表示前30%的训练步数里保持高探索率,后面慢慢退化为贪心策略,这是DQN收敛的标准配置。
4. DRL路由训练最常踩的五个坑:现象、原因与解法
4.1 训练曲线一条直线:奖励值完全不动
现象:训练跑了几千步,奖励值稳定在一个值附近,几乎没有波动。
原因分两种。一种是状态没有归一化,网络的初始输出不稳定,agent发现无论怎么选动作奖励都差不多,梯度方向互相抵消;另一种是动作根本没有效果——比如set_route下发流表时匹配条件写错,实际流量依然走默认路径,agent收集到的状态不会因为动作改变。
解决:先单独调_collect_obs,打印几个状态值确认它们跟随流量注入在变化。然后临时把奖励函数改为-1固定值,跑100步看loss是否正常下降。如果loss在动而奖励不动,问题在环境状态和奖励函数不匹配,优先检查状态是否包含了流量变化的信息。
4.2 时延数据离谱:Mininet的仿真误差来源
现象:仿真里统计的平均时延忽高忽低,同一流量矩阵下多次实验差异巨大,某些跳数少的路径时延反而更高。
原因:Mininet的host是共享物理机CPU的,UDP流量在host上产生时会争抢CPU;iperf自身的报告周期和Ryu的统计周期不对齐,也会引入噪声。更大的坑是delay参数只在TCLink下生效,如果你用了Link而不是TCLink,带宽限制和时延完全无效。
解决:统一使用TCLink,并把iperf报告周期调成和控制器统计周期一致(比如都用2秒)。如果时延仍不稳,考虑换用socket直连式流量脚本而不是iperf,减少iperf本身调度带来的抖动。复现论文数据前先跑一组固定流量的重复实验,把系统本身的方差量化出来。
4.3 Ryu的REST API被调崩:训练器与控制器通信别走HTTP
现象:训练跑到中途,控制器进程报socket connection reset,或者响应延迟逐渐拉高。
原因:很多人图省事,训练进程通过Ryu的REST API每步调一次接口获取统计和下发规则。但REST API是同步阻塞的,统计请求和动作下发全在一个线程里排队,网络交互一多整个控制器就假死。
解决:把训练器和控制器放在同一个Python进程里,通过内嵌接口直接调用内部方法。上面的示例代码就是这种模式——训练进程持有控制器对象引用,直接调set_route和读取latest_stats,完全绕开网络通信。如果一定要跨进程,用Unix domain socket或者gRPC,每个请求单独开线程处理,不要让控制器主循环被阻塞。
4.4 决策跟不上流量突发:动作频率与事件频率不匹配
现象:流量从10Mbps突增到80Mbps后,agent连续好几个决策周期都没有反应,时延先冲高再缓慢回落到正常。
原因:动作频率等于统计拉取频率,而这个频率远远慢于流量变化。网络里的流建立是事件级的,一秒可能发生几十次,而agent每2秒才决策一次。决策期间新来的流全按默认路径转发,等于白等一个周期。
解决:把控制器的事件上报和agent决策解耦——新流到达时先按最短路径转发,同时把事件缓存起来,agent每个决策周期统一重新优化全部流的路由。这样突发期间的即时流量不会被延迟,agent优化的是“下一段时间的路由布局”,而不是试图实时响应每一条流。这也更贴近实际网络里控制器的行为。
4.5 换拓扑就不行:训练分布与测试分布不一致
现象:同样的模型在训练拓扑上表现很好,换到节点数量不同的拓扑后,时延甚至比Dijkstra还差。
原因:DRL策略是从状态到动作的映射,模型会隐含地记住训练拓扑的特征。节点数变了,状态向量的维度变了,即使维度不变(用固定大小填充),拓扑特征分布也变了,模型没有泛化能力。
解决:训练时做域随机化——把拓扑在合理范围内随机生成,每次episode换一个拓扑。比如核心交换机数量在2到4之间变,边缘交换机数量在4到8之间变,链路带宽在50M到100M之间变。这样策略学到的是“对不同拓扑通用”的决策规则,而不是记住某张具体的图。如果只是想复现论文里的单拓扑效果,就没必要做域随机化,但要明确标注模型只服务于该拓扑。
5. 评估DRL路由算法:和Dijkstra、ECMP对比实验的评判标准
5.1 评测指标到底看哪几个:平均时延、吞吐、丢包和负载均衡度
对比算法时只报一个平均时延是远远不够的。网络路由评估至少要覆盖四个维度:
平均端到端时延是所有流从发送到接收的时延均值,反映用户体验;吞吐量是网络每秒能成功送达的数据总量,反映容量利用率;丢包率直接反映网络过载程度,业务类型不同容忍度差很多;负载均衡度用链路利用率的标准差表示,标准差越大说明流量越集中。
这四个指标经常互相矛盾。DRL模型压低时延的同时可能升高丢包率;ECMP天然均匀但时延可能偏高。所以评估报告必须四项都列,而且说明优化目标。如果你的场景是视频会议,时延和丢包率权重最高;如果是文件传输,吞吐量和大文件完成时间是核心。
5.2 对比实验的公平性:同一流量矩阵、同一网络状态采样
做对比实验最大的坑是流量矩阵不一致。DRL训练时用了某一种流量模式,测试时也用同一种,那是“开卷考试”;Dijkstra或ECMP没有见过这个分布,直接被降维打击。
正确做法是准备多套流量矩阵,每套矩阵里包含静态负载、突发流、短连接混合三种模式。每个算法都在这三套矩阵上各跑5次,每次使用不同的随机种子。最后报每个算法在每个矩阵下的均值加减标准差。没有置信区间的对比数据,基本不具备参考价值。
另一个公平性细节是预热时间。DRL模型需要网络状态统计周期来更新观察,跑到稳态后才能出真实成绩。传统算法没有任何学习过程,从一开始就按固定策略转发。所以对DRL来说,前两个统计周期(比如4秒)应该从结果中剔除,避免“还没开始就压着打”的尴尬。
5.3 用CDF曲线呈现结果:不要只给一张均值表
平均时延相同的情况下,时延分布可能完全不同。一个算法可能在95%的情况下时延都在40ms以下,只有5%的情况飙到200ms;另一个算法则均匀分布在80到120ms之间。平均值可能都是80ms,但前者对时延敏感业务显然更友好。
所以我习惯把所有测试流量的时延点记录下来,画成累积分布函数曲线。下面这段代码可以直接用在训练产出的日志上:
import json import numpy as np import matplotlib.pyplot as plt # log.json 里存了每条流的完成时延 with open("log.json") as f: delays = json.load(f)["delay_ms"] delays_sorted = np.sort(delays) cdf = np.arange(1, len(delays_sorted) + 1) / len(delays_sorted) plt.plot(delays_sorted, cdf, label="DRL") plt.xlabel("End-to-end delay (ms)") plt.ylabel("CDF") plt.grid(True) plt.legend() plt.savefig("delay_cdf.png", dpi=150)输出曲线会让论文里的结论更扎实。对比时把DRL、Dijkstra、ECMP三条CDF画在同一张图上,能直观看出DRL是在哪个分位点改善的——是普遍改善还是只改善尾延迟,这两种情况对应的适用场景完全不同。
6. 从仿真到真实网络:迁移学习、在线安全与我的工程习惯
6.1 从6节点到实际拓扑:迁移学习与域随机化
仿真环境里收敛的策略直接搬进真实网络,大概率翻车。真实网络的链路带宽是动态的,交换机队列深度受硬件限制,控制器的流表下发时延也远高于仿真。迁移学习是这条路上最实用的手段:先在仿真里预训练,加载到真实网络后用真实流量微调前几层网络参数,冻结后面的层,这样既保留仿真里学到的通用路由知识,又让模型适应真实网络的状态分布。
微调时学习率要降一到两个数量级,比如从1e-3降到1e-5,否则容易直接把预训练权重大洗牌。域随机化在仿真训练时也要用,把链路时延、带宽、丢包率都加上随机分布,让模型学会在“不确定环境”下找最优解,而不是记住某一组固定参数。
6.2 在线部署的降级开关:用策略熵判断是否接管路由
真实网络不允许拿业务流量做无限制试错。我给在线部署加了一道保险:模型输出动作的同时输出该动作的策略熵。熵高说明模型对当前状态没把握,这时不触发DRL路由,流量继续走传统路由算法;只有熵低到阈值以下才接管路由。阈值靠仿真阶段的校准数据确定——在仿真里跑一组训练集外的拓扑,统计正确决策时熵的分布,取95分位作为阈值。
这个降级开关看似保守,实际省去了线上跑A/B测试的大半风险。路由决策出错影响的是全网业务,不是单个进程,宁可策略少生效几次,不能让错误决策上线。我还会保留一个手动切换开关,一旦监控发现端到端时延对比基线有明显劣化,立即切回Dijkstra。
6.3 我坚持的几条工程习惯
我做这类项目有一个固定习惯:每次训练开始前固定随机种子,并用文本记录当前版本的环境、算法、超参数和拓扑文件。没有种子控制的训练结果对比都不算数——同一个模型同一次训练,两次启动波动可能超过算法之间的差异。
第二个习惯是每次只改一个变量。今天调奖励权重就只调奖励权重,明天调拓扑就只调拓扑。DRL训练的黑匣子属性太强,一次调三个参数翻车了你根本定位不了是哪个改动导致不收敛。第三个习惯是保留每一版模型的中间快照,每隔5000步存一次zip。训练跑崩了可以从最近的快照续跑,不用全部重来。这个操作救过我很多次,堪称“后悔药”。
从复现论文到把算法真正接进SDN网络,中间隔着的不是代码量,而是对状态、动作、奖励三者一致性的理解。想办法让自己的模型从仿真里走出来,哪怕只是在一台物理交换机上做一小时闭环测试,你对这套算法的理解都会比读十篇论文深刻得多。希望帮到你。
本文还有配套的精品资源,点击获取