ARTICLE DETAIL

资讯详情

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

混合动作空间强化学习:P-DQN与MPDQN算法核心机制全解析

混合动作空间强化学习:P-DQN与MPDQN算法核心机制全解析 做强化学习的人可能都遇到过这种让人发疯的情况环境里的动作要么是离散的左转、右转、抓取、放下要么是连续的方向盘转角、关节力矩、功率大小但真实世界从来不按教科书出牌。机械臂要“抓取”一个杯子得同时决定选“抓取”这个离散动作以及“用多大的力、以什么角度接近”这些连续参数。自动驾驶要在“跟车”“变道”“刹车”之间做离散决策又要把转向角、加速度这些连续量控制好。这类问题就是今天想聊的核心混合动作空间。这类问题让DQN、DDPG、PPO这些主流算法集体失灵——不是哪个方法不好而是它们的动作建模方式天然就不兼容混合空间。近几年有两篇文章把这条路打通了一篇是NeurIPS 2019上的P-DQNParametrized Deep Q-Networks另一篇是对P-DQN做重要改进的MPDQNMulti-Pass Q-Networks。这篇博客我会从问题定义讲起把这两个算法的核心机制、数学原理、实现细节和踩坑经验一次说透。无论你是刚入门强化学习的新手还是正在发愁动作空间建模的研究者、工程师这篇文章都值得你花二十分钟认真读一遍。1. 混合动作空间为什么标准强化学习算法在这里集体失灵在聊算法之前得先把“混合动作空间”这个东西钉死。因为很多人在环境建模阶段就搞错了后面全在硬调bug。1.1 从“点餐”看动作空间离散、连续、参数化动作空间这个概念说人话就是“智能体在一个状态下可以做哪些事”。我们可以用点餐来打比方离散动作空间相当于餐厅菜单上固定搭配好的套餐。你只能选套餐A、套餐B、套餐C不能改里面的任何东西。对应的算法就是DQN那一系输出一个概率分布或Q值表选一个动作。这类问题的特点是动作是枚举的有限且不连续。连续动作空间相当于自助餐你可以自由决定夹多少肉、舀多少汤动作取值是一个连续区间。对应的算法是DDPG、SAC、TD3这些输出一个连续向量。这类问题的特点是动作可以是任意实数没法一个个枚举。参数化动作空间也就是本文的主角相当于餐厅给你定制套餐你先选一个套餐离散动作再自定义里面的配料和用量连续参数。形式上可以写成$A {(d, x_d) \mid d \in [K], x_d \in X_d}$意思是总共有K个离散动作选择第d个动作的同时给它附上一个连续的参数向量x_d。注意不同离散动作可能对应不同维度的参数。比如“抓取”需要“力度、角度”两个参数而“滑动”只需要“距离”一个参数。所以参数维度可以是动作相关的。这种结构在现实任务里极其常见。游戏AI里英雄要选择放哪个技能离散然后又决定释放方向、力度连续。推荐系统里策略要先决定推荐哪个商品品类离散再决定给多少折扣连续。资源调度里要决定调度哪台机器离散再决定分配多少带宽或算力连续。可以说凡是“先做决定、再调细节”的决策层级本质上都是参数化动作空间。1.2 混合动作空间的真实场景机械臂、资源调度、游戏AI、自动驾驶再看看真实场景这类任务真的不是少数。机械臂控制可能是最典型的。机械臂做一个抓取任务动作空间是移动到目标附近连续关节角度选择抓取或放置离散决定抓取力度连续。我见过很多团队一开始用DDPG把离散动作也编码成连续值去逼近效果非常差。因为离散动作本质上是不可微的你让一个连续策略去逼近一个“跳变”的决策边界它很容易在一个动作边界来回震荡。资源调度是最近两三年工业界特别关注的方向。比如云数据中心的服务器管理控制器要决定当前应该扩容还是缩容离散决策然后决定具体要调整多少个容器实例、调整多少CPU配额连续参数。传统的启发式方法完全没法处理这种组合爆炸式的策略空间而纯强化学习算法又很难同时处理好决策和参数两部分的协同。游戏AI里更不用说了。MOBA游戏里智能体要选择攻击哪个目标离散放哪个技能离散然后决定技能释放方向、距离连续。这类任务如果只用离散动作建模需要把连续方向离散化成几十个挡位动作维度瞬间爆炸训练难度成千上万倍上升。自动驾驶虽然说到底是连续控制为主但在决策层面同样面临混合建模的问题变道、超车、靠边停车是离散决策而方向转角、加速度、刹车力度是连续参数。很多论文里把决策层和控制层分开处理但在端到端学习的框架里混合动作空间的问题始终绕不过去。1.3 你踩过的坑DQN、DDPG、PPO都试过了为什么效果不好说了这么多场景到底为什么标准算法搞不定我挨个说DQN面对连续参数最常见的做法是把连续参数离散化。比如力度分成10档角度分成36档组合起来就是360个动作。这种做法有两个致命问题一是维度爆炸参数每增加一个维度就指数级膨胀二是精度丢失离散化粒度不够策略永远做不出精细控制。你可以想象一个机械臂只有36个角度、10个力度可选抓杯子的成功率能有多低。DDPG面对离散动作只能把离散动作也当成连续值输出再取整或者取argmax。这相当于把一个本来就是分类的问题硬生生当成回归问题处理。最直接的后果是策略在动作边界上不稳定训练出的策略经常出现“明明要抓取却输出一个模棱两可的中间值”的诡异行为。PPO这类方法相对灵活一些可以设计一个混合策略网络一部分输出离散动作的概率分布一部分输出连续参数的高斯分布。但这样做的最大问题是离散动作和连续参数的梯度更新是割裂的两者的协调完全靠reward信号隐式引导训练效率很低。而且连续参数摊到每个离散动作上时如果某些动作很少被采样到对应参数的训练就会被饿死。我自己最开始的尝试是走PPO路线结果发现一个残酷的事实在混合动作空间里随机策略的探索效率低得惊人。因为策略不仅要探索“用哪个动作”还要探索“这个动作对应的参数是什么”探索空间是两者的笛卡尔积大多数随机探索都在无效区域里浪费掉了。所以混合动作空间真正需要的是一个能同时处理离散选择和连续参数并且能够让两者共享决策信息、协同更新的算法框架。这便是P-DQN和MPDQN登场的原因。2. P-DQN算法机制解析把连续参数“挂”在离散动作上P-DQN的核心思想并不复杂但对当时2019年的强化学习社区来说确实是一个很有创意的角度。它的思路可以总结为一句话离散动作的Q值被建模成连续参数的函数。2.1 P-DQN的核心思想动作由“离散选项连续参数”构成我们先把目标梳理清楚。假设有K个离散动作可选每个离散动作d都对应一个连续参数向量x_d。智能体的完整动作就是选一个离散动作d再加上相应的参数x_d。如果沿用DDPG的思路我们可以构造一个策略网络记作x μ(s, d)输入状态s和离散动作d输出连续参数。这个过程可以理解为给定当前局面针对每一个离散动作智能体都在心里想好了“如果选这个动作那么参数应该怎么设”。然后我们再用一个Q网络Q(s, d, x_d)在选定了d和x_d之后评估这个完整动作的价值。决策的时候智能体只需要对每个离散动作d计算$Q(s, d, \mu(s, d))$然后选值最大的那个d作为最终决策并使用对应的μ(s, d)作为参数。这里其实是做了一个关键转化原来需要同时优化离散和连续现在变成了先由Actor网络对每个离散动作给出参数再用Q网络对这些“动作-参数对”打分选出最优秀的那个离散动作。这个过程很像选房子中介Actor先给每个小区离散动作提供一套具体房型方案连续参数然后评委Q网络给每套方案打分最后选分数最高的小区。这里的重点在于每个小区都要考虑一遍而不是直接把所有小区混在一起选。2.2 网络结构与更新流程评估网络、参数生成网络P-DQN的网络结构可以分为两大块第一块是Q网络也就是评估网络。它不再像DQN那样接收离散动作的one-hot编码而是接收状态s和连续参数x_d输出该状态下采取动作d并配上参数x_d的Q值。注意这里有K个离散动作意味着Q网络最好有K个输出头每个头对应一个离散动作的Q值输入则是相应动作的参数向量。第二块是Actor网络也就是参数生成网络。它的任务是根据状态s生成所有离散动作对应的参数μ(s, d)。注意这里有一个非常容易忽略的细节对于每一个离散动作dActor都要单独生成一套参数哪怕最终决策只会选一个动作。这确实增加了计算量但是保证了Q网络在评估每个离散动作时都有对应的参数可用。网络结构实现上我建议把Q网络设计成这种结构共享一个状态编码层然后每个离散动作单独分流出一个多头感知机输入该动作的参数向量输出对应的Q值。Actor网络则相对简单输入状态s输出一个拼接向量d维动作参数全部拼在一起输出使用时再按动作维度切分。训练流程可以简单分成两步交替进行第一步更新Q网络。采样一个转移样本(s, a_d, x_d, r, s)计算目标值。这里的难点在于s状态下的Q值如何估计因为我们需要知道s下选择哪个离散动作、参数是什么才能得到最大的Q值。P-DQN的方案是用当前的Actor网络μ(s, d)为每个离散动作生成参数然后计算max_d Q(s, d, μ(s, d))其中Q是目标网络。然后套用标准的TD目标公式。第二步更新Actor网络。因为最终决策是选Q值最大的离散动作那么Actor的优化目标就是让“对应那个最优离散动作的参数”产生的Q值尽可能高。但问题在于当前最优离散动作会随着训练不断变化所以P-DQN的实现里通常对所有离散动作都做梯度上升目标是提升Q(s, d, μ(s, d))的平均值或总和。这样无论最后选哪个离散动作其对应的参数都已经经过了优化。2.3 P-DQN的数学原理损失函数与梯度更新推导P-DQN的数学推导其实并不复杂但要搞明白里面的近似假设。我们定义策略π的形式是“先选离散动作再输出连续参数”于是整体目标可以写作$J \mathbb{E}{s}\left[\max{d} Q(s, d, \mu(s, d))\right]$这里我们隐含地使用了贪心策略即每次选择Q值最大的d。Q网络的损失函数就是一个标准的时间差分误差。给定一个transition(s, d, x_d, r, s)目标值可以写成$y r \gamma \max_{d} Q_{\theta^-}\left(s, d, \mu_{\phi^-}(s, d)\right)$其中θ^-和φ^-分别是Q网络和Actor网络的目标网络参数。然后Q网络的损失就是均方误差$L_Q \mathbb{E}\left[\left(y - Q_\theta(s, d, x_d)\right)^2\right]$这个式子里有一个微妙的问题理论上目标值应该对所有可能的d和x_d求max但P-DQN用Actor网络μ给每个d提供一个x_d然后只在离散维度d上求max。这意味着连续参数部分不是全局最优的只是“Actor当前给出的最优点”。这是P-DQN的一个关键近似也是后续几个改进版本的切入点。接下来是Actor的更新。我们要让Q(s, d, μ(s, d))最大化。用确定性策略梯度的思路对Actor参数φ求梯度$\nabla_\phi J \approx \mathbb{E}\left[\sum_{d} \nabla_\phi Q_\theta\left(s, d, \mu_\phi(s, d)\right)\right]$展开后是$\nabla_\phi Q_\theta \nabla_{x_d} Q_\theta(s, d, x)\big|{x\mu\phi(s, d)} \cdot \nabla_\phi \mu_\phi(s, d)$这就是标准的链式法则。需要注意的是P-DQN在更新Actor时是对所有K个离散动作的Q值求和或取均值做梯度上升而不仅仅是当前选中的那个动作。这样做的好处是让所有离散动作的参数生成能力都得到训练坏处是可能会让Actor把精力分散到那些“本来就不该选”的动作上。这个设计在有些场景下是稳定训练的功臣在另一些场景下则成了拖后腿的元素后面讲MPDQN时会再展开。2.4 关于P-DQN的收敛性思考与局限P-DQN的收敛性并不能直接由DQN的收敛性保证因为这里的目标值计算依赖一个同时更新的Actor网络。也就是说Q网络的训练目标本身会随着Actor的更新而移动这让训练过程更容易出现震荡。我自己的实验经验是P-DQN在动作空间维度较低比如离散动作不超过10个参数维度不超过3个的任务里表现还行但一旦动作组合变复杂训练就容易出现两个典型问题一是Q值高估导致Actor被误导到一套“看起来Q值很高、实际收益很低”的参数上二是参数更新时梯度方向忽东忽西因为Q网络对不同离散动作的梯度方向可能完全相反Actor在更新参数时相当于在对抗一个“分裂”的优化目标。此外P-DQN在高维状态空间下还有样本效率的问题。因为Actor要为每个离散动作都生成一套参数如果离散动作数量很大这个输出维度会相当可观训练起来会非常吃力。这也是MPDQN出现的重要动机。3. MPDQN关键改进参数共享与混合梯度传播MPDQN的全称是Multi-Pass Q-Networks我更喜欢把它理解为“对P-DQN的‘缝合手术’”。它的出发点非常具体P-DQN里那个Actor要同时优化所有离散动作对应的参数这导致了梯度冲突和训练不稳定。MPDQN正是从这个痛点下手。3.1 P-DQN的一个明显毛病Full-α联合更新带来的不稳定在P-DQN中Actor网络的目标函数是所有离散动作的Q值之和这个设计被称为Full-α联合更新。你可以想象一下演员同时排练了十个剧本每个剧本的导演都对表演提出了完全不同的要求一会儿让他演英雄一会儿让他演反派一会儿让他演喜剧人物。最后演员被逼成了一个各方面都平庸、哪方面都不突出的“折中形态”。在具体任务里这意味着什么假设机械臂任务里有“抓取”“滑动”“按压”三个离散动作它们的参数空间完全不同。抓取需要学习力度和角度滑动需要学习距离按压需要学力的大小。如果用P-DQN的Full-α更新Actor网络需要在同一次梯度更新里同时提升这三个动作对应的参数质量。但问题是这三个动作对应的Q值梯度大概率是不同甚至冲突的于是Actor参数更新的方向会被拉成一个“四不像”最终结果是每个动作的参数都学不精。MPDQN说白了就是把“演员被不同导演拉扯”的问题解决掉。它的核心思路是不再让Actor通过一个输出头去生成所有动作的所有参数而是让Q网络自己掌握“参数如何影响价值”的信息再通过一种混合梯度的方式把连续参数的学习从离散动作的Q值评估中解耦出来。3.2 MPDQN的核心机制把Q值当作参数的函数来优化MPDQN的关键改进在于对Q值的理解。在P-DQN里我们先把参数x_d给Actor网络生成然后塞进Q网络算Q值。这里x_d只是Q网络的一个“外部输入”Q网络对x_d的依赖完全来自输入层。MPDQN换了一个视角它把Q网络重新设计成Q(s, d, x_d)的形式但同时在网络结构上实现了“Q值对参数x_d的显式依赖”。具体来说MPDQN在Q网络内部会先将状态s编码成一个特征向量然后对不同离散动作的参数分别计算价值最后再聚合起来。这里的“多路传递”Multi-Pass指的正是在Q网络内部同一个状态特征会分别“通过”不同离散动作各自的参数分支每个分支输出该离散动作的Q值。这样做的好处是参数x_d不再是外部单纯塞进来的数值而是真正进入了Q值计算的“内部通道”。这就像以前点外卖的时候你只把订单丢给店里完全不管厨房怎么处理现在你能看到厨房的内部流程并且可以精确指导“这份订单里加多少辣、多少盐”更合适。Q网络学会了参数对价值的边际影响于是梯度可以更准确地回传到参数生成网络。实现上的一个重点是MPDQN让Actor网络的更新不仅仅依赖Q值对参数的梯度∂Q/∂x_d而是综合考虑了Q网络内部的中间特征。这就等于在反向传播时让参数的梯度和状态-动作价值评估之间建立了更直接的高速通道减少了过去那种“梯度传到一半就消散或者扭曲”的情况。3.3 混合梯度传播的具体操作与实现细节MPDQN的具体实现可以选择两种方式我这里介绍最直观的一种在Q网络中为每个离散动作分支都计算Q(s, d, x_d)但Actor的输出μ_φ(s)只输出“当前状态s下所有动作的参数向量”然后对每个离散动作分支都计算Q值。训练时关键的区别在于Actor的梯度计算。MPDQN中Actor损失函数虽然是类似的形式$L_{actor} -\sum_{d} Q_\theta\left(s, d, \mu_\phi(s, d)\right)$但它对总Q值的评估是直接对Q网络内部做了一次关于x_d的“全微分”同时考量了Q网络内部的状态特征和动作参数的耦合方式。这带来的结果是理论推导中的梯度计算会更稳定因为参数更新不再只是通过Q网络的“输入层”来传播而是通过Q网络的“中间层特征”来传播梯度路径明显短了不少。另外还有一个工程实现上的小细节MPDQN在更新Q网络时目标值计算用了和P-DQN类似的方式即用目标Actor网络给s下的每个离散动作生成目标参数再计算max_d Q_target(s, d, μ_target(s, d))。如果Q网络没有参数化的内部结构这种目标值的计算很容易出现高估偏差。而MPDQN由于Q值对参数的建模更精确高估问题会得到一定的缓解。3.4 MPDQN与其他变体的横向对比PDQN、Hybrid-PPO、Parameterised Action DDPGMPDQN不是唯一解决混合动作空间的方案但它在很多实验中的确表现得更加稳定和高效。我把几个常见方案放在一起做个对比方法动作建模方式更新机制主要优势主要短板P-DQN离散选择连续参数生成Q网络TD更新 Actor确定性策略梯度结构清晰容易实现Full-α更新导致梯度冲突训练不稳定MPDQN离散选择参数化Q值Q网络内部混合梯度传播参数学习更稳定样本效率更高网络结构稍复杂需要更多工程细节调优Hybird-PPO离散策略连续高斯策略混合一个策略网络输出混合分布用PPO更新理论上更通用适合随机策略探索效率低训练慢实现复杂度高Parameterised Action DDPG类似P-DQN但用确定性策略梯度类似DDPG对连续参数和离散决策联合更新更接近DDPG的直觉容易忽略离散动作之间的独立差异导致更新不精确表格里每一项都值得展开说。Hybrid-PPO这类方法最大的问题我在前面提过探索空间太大了。它把离散选择和连续参数当成两个独立分布的联合采样随机初始化的策略几乎就是在随机游走reward信号非常稀疏训练效率感人。Parameterised Action DDPG的问题在于它把离散动作也当成可微的连续变量来处理会模糊离散动作的本质特征容易出现我在第一节里说到的“中间值困境”。MPDQN为什么在对比里胜出我觉得核心就是它同时尊重了两个事实第一离散动作的选择本质上是一个不可微的argmax问题所以选择那一步用Q值评估第二连续参数对价值的影响是可微的所以参数优化走梯度路径。两种机制各司其职用网络结构把各自的优势发挥出来这是它相比其他方法最聪明的地方。4. 动手实现从搭建网络到跑通一个混合动作环境这一节我分享一套可以直接上手的实现思路。我不打算贴完整代码因为代码仓库里已经有很多成熟实现我更想讲清楚实现时容易踩的坑以及什么样的设计决策会让你的训练效率天差地别。4.1 网络结构设计建议离散头、连续头和共享编码层先说Q网络。我的建议是采用“共享编码层离散动作独立分支”的结构。具体来说状态s先经过一个两层的MLP编码成一个特征向量h。然后对每个离散动作d有一个独立的小型MLP接收的特征是[h, x_d]的拼接输出该动作的Q值。这样的好处是不同离散动作的参数维度可以不同每个分支可以独立处理自己维度的输入同时共享编码层让状态特征在不同动作之间复用节省参数数量。这里有一个不容易注意到的坑如果不同离散动作的参数维度差异很大比如动作1的参数是3维动作2的参数是10维那么拼接[h, x_d]之后各分支的输入维度不一样。这在PyTorch里实现很简单每个分支定义不同的Linear层就行。但要注意做好维度管理和参数初始化的对齐否则训练初期某些分支的Q值会特别大把共享编码层的梯度带偏。Actor网络的输出是一个向量它将所有离散动作的参数拼接在一起输出。假设动作d的参数维度为dim_d那么Actor输出维度就是Σ dim_d。输出的每个分段就是对应动作的参数训练时直接切片丢给Q网络对应分支即可。激活函数方面连续动作参数如果有边界一定要在输出层加上tanh再缩放到边界范围内。这一点极其重要因为强化学习里的动作参数通常在环境交互时要求有物理意义比如角度只能在-180°到180°之间如果Actor输出越界环境会直接给一个奇怪的reward甚至崩溃。4.2 训练循环的伪代码与关键细节我写一段核心训练循环的伪代码这个结构基本可以复用# 初始化Q网络 Q_theta、Actor网络 mu_phi以及对应的target网络 # 初始化经验回放池 replay_buffer for episode in range(max_episodes): state env.reset() episode_reward 0 while not done: # 1. 用Actor给所有离散动作生成参数 params_all mu_phi(state) # shape: [sum(dim_d)] params_by_action split(params_all) # 按动作切分 # 2. 计算每个离散动作的Q值选最优离散动作 q_values [] for d in range(K): q_values.append(Q_theta(state, d, params_by_action[d])) discrete_action argmax(q_values) # 3. 执行动作得到转移样本 action (discrete_action, params_by_action[discrete_action]) next_state, reward, done, info env.step(action) # 4. 存储transition replay_buffer.add((state, discrete_action, params_by_action[discrete_action], reward, next_state, done)) # 5. 从回放池采样一个batch更新网络 batch replay_buffer.sample(batch_size) update_networks(batch) state next_state episode_reward reward def update_networks(batch): state, action_d, action_x, reward, next_state, done batch # 用target Actor给next_state生成参数 next_params_all mu_phi_target(next_state) next_params_by_action split(next_params_all) # 计算target Q值 target_q_values [] for d in range(K): target_q_values.append(Q_theta_target(next_state, d, next_params_by_action[d])) max_target_q max(target_q_values) # 对离散维度取max y reward gamma * (1 - done) * max_target_q # 更新Q网络注意只更新当前选择的离散动作对应的分支 q_pred Q_theta(state, action_d, action_x) loss_q MSELoss(q_pred, y) optimizer_q.zero_grad() loss_q.backward() optimizer_q.step() # 更新Actor网络 # 先让Actor生成当前state下的所有参数 params_all mu_phi(state) params_by_action split(params_all) # Actor的损失是所有离散动作Q值的负均值 q_vals [] for d in range(K): q_vals.append(Q_theta(state, d, params_by_action[d])) loss_actor -mean(q_vals) optimizer_actor.zero_grad() loss_actor.backward() # 注意只更新Actor参数不更新Q网络参数 # 关键需要把Q网络的梯度冻结 loss_actor.backward(retain_graphTrue) # 或者使用detach等技巧 optimizer_actor.step()这段伪代码里有几个容易踩坑的细节我特别提醒第一Q网络更新的时候只更新当前采样到的离散动作对应的分支。其他分支的Q值预测不做更新。原因是你只对实际执行的那个动作有真实的reward反馈没有执行的动作没有ground truth强行更新反而会把Q值搞乱。第二Actor更新时需要对Q网络的所有分支都算一次梯度。这里有个技术问题如果Q网络的不同分支共享了编码层那么一次backward就会同时更新Q网络和Actor网络这会导致训练不稳定。标准做法是在Actor的backward之前把Q网络的参数暂时固定例如在PyTorch里用with torch.no_grad():包住Q网络前向传播或者调用Q_theta.requires_grad_(False)。第三目标网络用软更新比较好也就是target_params tau * current_params (1 - tau) * target_paramstau取0.005左右。硬更新定期复制在P-DQN这类算法里很容易因为Q值突变导致训练崩溃我吃过一次大亏。4.3 超参数选择与调参经验P-DQN/MPDQN的超参数选择和普通DQN、DDPG有一些不太一样的地方我列一张表超参数建议取值注意事项学习率Q网络1e-3 ~ 3e-4太大会导致Q值高估太小则收敛太慢学习率Actor网络1e-4 ~ 1e-3Actor学习率建议比Q网络低因为Actor的梯度来自Q网络的间接传播回放池大小100k ~ 1M混合动作空间的探索样本质量参差大回放池有助于稳定batch size128 ~ 256采样量太小的话某些离散动作可能一整个batch都没被采到目标网络更新系数tau0.005软更新比硬更新稳定得多探索噪声参数空间加高斯噪声不要只在Q值选择上加ε-greedy否则连续参数部分探索不足reward normalization建议做不同离散动作的reward尺度差异很大不做归一化会让梯度偏置关于探索噪声我要多讲一句。在混合动作空间里探索需要同时考虑离散选择和连续参数两部分。我试过最有效的方案是在Actor输出的参数上叠加一个均值为零的高斯噪声噪声方差随训练进度逐渐衰减。同时在离散动作选择上保持一个较小的ε-greedy探索概率比如0.1到0.3但核心探索动力应该放在连续参数上。原因是离散选择一旦确定对结果的影响是决定性的频繁乱试反而会让Q网络学到错误的价值信号。4.4 简单实验验证与结果观察如果你第一次跑通P-DQN或MPDQN我建议先用一个简单环境验证比如带参数版本的多臂老虎机或者OpenAI Gym里稍作修改的CartPole——把动作改成“向左推/向右推”两个离散动作参数是推力大小。这种环境收敛快、可解释性强适合验证算法实现的正确性。观察训练曲线时我建议重点看两个指标episode reward曲线和Q值曲线。如果Q值在训练早期迅速飙升但episode reward没有同步上升说明出现了Q值高估。这时候你要先检查目标网络更新是否太频繁再检查Actor更新时是否把Q网络也给更新了。如果Q值曲线很正常但episode reward长期没有起色问题大概率出在探索参数上——试试加大参数噪声方差或者增加ε-greedy概率。我自己的经验里遇到最多的问题是Actor更新时不小心把Q网络参数也更新了导致整个训练像“左脚踩右脚”一样螺旋升天。这个bug非常隐蔽因为loss值看起来在正常下降但策略效果越来越差。排查方法很简单训练过程中打印Q网络第一层权重的范数如果一直在变化说明你的梯度隔离没有做好。5. 应用场景与行业落地从仿真到现实算法不能只活在论文里。这一节我聊聊混合动作空间在几个真实场景中的应用趋势以及MDPQN这类算法在落地时需要留意的现实工程问题。5.1 机械臂控制抓取规划的“动作-参数”解耦机械臂抓取可能是混合动作空间最直接的应用场景。一个机械臂的完整控制策略包括目标定位连续位置、轨迹规划连续路径、末端执行器动作离散抓取、释放、切换工具、执行力度控制连续。我以前和一个做机器人分拣的朋友聊过他们团队的早期方案是把“抓取”和“释放”建模成两个离散动作用DQN来学力度则固定不变。效果还行但一旦遇到易碎品或者不同重量的物体固定力度完全不够用。后来他们换成混合动作空间建模力度作为连续参数“抓取”“释放”作为离散动作训练效率和抓取成功率都有了大幅度提升。这里面有一个非常关键的工程问题仿真环境与真实环境的差距。在仿真环境里你可以在几十个episode内让策略探索各种极端参数然后收到反馈。但在真实机械臂上一次错误的抓取力度可能直接损坏工件甚至机械臂。所以落地的时候一定要先在高保真仿真环境里把策略训到足够鲁棒再迁移到真实世界并且迁移时对Actor输出做物理边界约束。5.2 推荐系统与资源调度离散决策连续分配推荐系统里有一个很经典的问题给用户推荐什么内容离散选择以及以什么频率、什么渠道发送连续参数。这个场景最近两年引起了不少团队的关注尤其是推送时机和展示方式的优化本质上是一个混合动作空间问题。资源调度就更典型了。云平台上要决定给某个任务分配哪台服务器离散选择以及分配多少CPU、多少内存、多少带宽连续参数。这个场景的特点是状态空间巨大、动作组合复杂而且环境是动态变化的。有意思的是这类问题最近也出现了与运筹优化结合的尝试用混合整数线性规划MILP做离线求解再用强化学习做在线决策混合动作空间正好是两者的中间层。在这些场景落地时最需要注意的是reward函数的设计。资源调度的reward通常包含多个目标任务完成时间、资源利用率、能耗等。如果reward设计成简单的线性加权策略很容易学到“钻空子”的行为。我建议在reward里加入约束惩罚项比如超过资源上限时给予大的负惩罚这比事后调整策略要高效得多。5.3 游戏AI与自动驾驶决策层与执行层分层协同在游戏AI里混合动作空间的建模方式尤其自然。像《星际争霸》《Dota 2》这类游戏智能体既要选择使用哪个技能离散又要决定释放方向和距离连续同时还要决定移动目标连续位置。2023年以来业界在游戏AI方面出现了一个趋势不再从头训练一个完整的大模型而是用分层强化学习高层策略输出离散意图低层策略输出连续动作序列。混合动作空间在这种分层架构里正好充当了“上层决策和下层参数”的桥梁。P-DQN在这一层有天然的优势因为它的离散动作选择本身就是基于Q值评估的天然适合做“意图筛选”。自动驾驶方向虽然暂时还不是混合动作空间的主战场因为其控制层面更倾向于纯连续控制但在决策层面已经有了混合建模的尝试换道离散加上换道轨迹连续参数通过混合策略来处理交互决策。我个人的判断是未来端到端自动驾驶如果要在决策层引入可解释性混合动作空间会成为一个重要的建模选项。6. 常见问题与避坑实录最后这一节我整理一下自己实际操作中遇到的典型问题按症状排查基本能覆盖大部分新手用户的困境。6.1 训练不稳定的典型表现与对策训练不稳定的表现很多最常见的是reward曲线剧烈震荡、Q值发散、策略在训练后期完全退化。我遇到过的一个典型案例是训练前期reward稳步上升到了中期突然崩盘一切回到原点。排查后发现问题出在Actor的学习率太高导致参数更新迈的步子太大Q网络来不及“纠正”Actor的方向两者互相拉扯最后把策略拉进了坑里。对策如下把Actor学习率降低到Q网络的五分之一甚至十分之一给Q网络多一点“权威”目标网络软更新的tau调小一点比如0.001让目标值的更新更平滑对网络参数做梯度裁剪clip范围控制在1.0以内防止梯度爆炸。6.2 探索策略设计ε-greedy的变形在混合动作空间里简单的ε-greedy并不够用。因为即使你以一定概率随机选了离散动作随机选择的参数也可能是完全不合理的。如果你在离散动作上做随机探索比如有K个动作就有1/K的概率选到某个动作但参数却是随机噪声——这个噪声样本对Q网络的训练几乎是有害的。我的经验是采用分层探索策略离散动作选择用ε-greedyε从0.3线性衰减到0.05连续参数则在Actor输出的基础上叠加高斯噪声。高斯噪声的初始标准差设为参数范围的10%左右然后逐渐衰减到2%左右。这个方法比单纯的ε-greedy或者单纯的参数噪声都更稳定。6.3 辅助损失与奖励塑形的实战经验在混合动作空间任务里reward信号往往非常稀疏。比如机械臂抓取只有成功抓取才有1的reward其他时刻都是0。稀疏reward会让Actor不知道“哪个阶段的参数是好的”训练速度极慢。我在实践里用过两种有效的做法第一种是奖励塑形。在稀疏reward之外给每一步加上一个辅助奖励比如“物体靠近目标的距离变化”。奖励塑形的时候要注意不要喧宾夺主。如果辅助奖励的值域远大于主奖励策略可能会去优化“靠近目标”而不是“抓取成功”。我通常会设置辅助奖励为“前进多少距离”的增量并且把系数压到很小比如0.1让主奖励始终主导学习方向。第二种是HERHindsight Experience Replay式的目标重标注。对于机械臂这类任务可以把“成功抓取”的状态修改为“虽然没有抓到目标物体但成功抓到了另一个物体”以此构造正样本。这个方法在处理混合动作空间时尤其有用因为它让Q网络能在稀疏reward下也学到“动作-参数”与结果之间的关系。6.4 常见问题速查表报错、发散、维度不匹配现象可能原因解决方法训练刚开始Q值就爆炸学习率过高、Q值未做归一化降低学习率检查reward尺度reward长期没有提升探索不足、reward太稀疏增大参数噪声、加入reward shaping不同离散动作的Q值差距悬殊采样不均衡某些动作太少被执行增加ε-greedy概率或者对每个动作单独设置采样权重Actor输出越界导致环境崩溃输出层激活函数使用不当使用tanh激活函数然后缩放到动作边界动作参数维度不匹配参数切分逻辑错误打印每个动作分支的输入输出维度逐个核对训练中期Q值突然飙升目标网络更新过快调低tau或者改用软更新同一个任务跑多次结果差异极大随机种子、网络初始化差异固定随机种子或者做多次重复实验取均值这张表是我最想让大家收藏的因为我发现80%的实现问题都能在这里找到对应答案。尤其是维度不匹配的问题混合动作空间实现里非常容易出现因为每个离散动作的参数维度不同一旦在拼接、切分、索引的过程中差了一个维度代码不会报错但训练效果会莫名其妙地变差。遇到这种情况我的排查方法是在训练的最开始手动构造一个假batch走一遍完整的前向和反向传播把每一步的张量形状打印出来仔细核对。另一个很容易被忽略的问题是离散动作参数的索引映射。在P-DQN的实现里Actor输出的拼接向量按动作顺序排列Q网络的不同分支按同样的顺序接收。这个顺序一旦在代码里写错比如切分时把动作0的参数切给了动作1策略就会学到一套完全错乱的映射。由于网络本身具备拟合任意映射的能力这种错误不会导致明显的报错但会让训练收敛到奇奇怪怪的局部最优。我的建议是在最开始就把动作索引映射封装成一个函数加上单元测试确保任何地方调用都不会错位。再说一个我在多进程训练环境里踩过的坑。混合动作空间的任务通常需要多个环境并行采集数据以提升采样效率。但如果不同进程的随机种子设置不当可能导致某个离散动作在某个进程里永远不会被采样到造成样本分布严重偏移。建议为每个进程设置独立的随机种子并在采集数据时记录每个离散动作被执行的比例如果某个动作的比例长期为0需要主动干预采样策略。写在最后但都是硬货P-DQN和MPDQN给我的最大感受是它们解决的不是“能不能用强化学习”的问题而是“如何让强化学习的动作建模更贴合真实世界”的问题。真实世界的决策从来不是纯离散或纯连续的而是两者的精细嵌套。如果你的任务正好是这种结构这两个算法值得你认真研究。最后分享一个我踩过很多次坑之后总结出的调参小技巧如果你在两个算法之间犹豫不决可以先跑P-DQN做快速原型验证因为它的实现更简单、调试更容易等确认整个pipeline没有问题之后再切换到MPDQN做精调。MPDQN在大多数场景下效果更好但它的网络结构和训练流程多了一些细节直接上手调试容易分不清是算法的问题还是代码的bug。先简单后复杂一步一步迁移你会省下大量的调试时间。
返回列表