large-scale-DRL-exploration 代码阅读 总结

训练篇

流程

策略网络;

两个Q_net , 解决高估问题;

温度调节参数alpha,可学习参数;

两个目标Q_net;

策略网络优化器;

两个Q_net 优化器;

alpha优化器;

目标熵;

定义并启动 远程 actor RLRunner

神经网络数据并行化,将输入数据划分到多个GPU,即在多个GPU 训练神经网络

远程 do job

初始化环境,智能体,groudTruth节点管理器

开始运行智能体,在环境搜集experience;

更新规划状态,得到观测,真值观测;

开始:保存观测和真值观测;

根据策略网络选择动作,执行动作,保存动作;

更新规划状态

保存奖励

再保存st+1的观测,真值观测

循环直到完成任务或者达到步数上限

等到完成job,获取job 结果:

将job result 存储到经验回放buffer,直至experience 达到一定的数量开始训练:

将experience 转化为张量准备喂给相应的神经网络;

SAC

计算Q,计算logp, (forward) 优化策略网络loss function (backward)

计算目标Q值,优化Q_net, 两个Q_net 分开优化

计算动作熵,优化alpha

保存 奖励 loss function 值 梯度 动作商 alpha 探索表现评估 等数据

执行篇

流程

初始化参数

加载训练后的智能体 robot agent

订阅地图和定位

robot agent 初始化节点管理器

run:

是否进入save 模式:

距离目标点还很远,保持going

选择可行的较远但是不是特别远的无碰撞路点,作为导航目标 return

没有的选 退出save mode

振荡模式解决 retuen

直接获取已经规划的下一个路点作为目标

开始新规划

移除非法节点,为规划做准备

找到机器人归属的即最近的节点定位

update_planning_state # 更新地图 机器人在图上的节点定位 要更新的地图 边界点

以及更新图: 更新图上的节点,增加删除,以及每一个节点的效用,需要维护的更新的

边,更新了邻居节点,删除与原点不连接的节点,

维护了哪些节点是新加的 刚删除的,返回机器人位置对应的节点坐标

注:图是由一个基于四叉树的节点管理器维护的

更新稀疏图:找到效用非零节点;当前机器人位置为第一个keynode 且作为聚类中心;

找到聚类中心周围一定范围的节点

遍历,找到节点中效用非零的节点,计算与参考点之间的路径;

找一些路径上的节点加入key node

1.不是ref node 的邻居且不是太远且下一个路径点就碰撞了的时

候,此节点(与机器人连接)和下一个路径上的节点作为key node

2.效用大于0的节点作为 key node 且与机器人连接

3.其他的路径点也作为key node 但是没有和ref node 连接(邻居)

再聚类其他聚类中心附近的节点

维护聚类中心,需要删除的删除,并维护在聚类中心聚类范围符合条件的

节点,防止被重复聚类

寻找其他的非零节点且还没有被聚类过的节点作为新的聚类中心

计算机器人位置到每一个聚类中心的路径

找一些路径上的节点加入key node

1.不是ref node 的邻居且不是太远且下一个路径点就碰撞了的

时候,此节点(与机器人连接)和下一个路径上的节点作为

key node

2.效用大于0的节点作为 key node 且与机器人连接

3.其他的路径点也作为key node 但是没有和ref node 连接(邻

检查遗漏的效用非零节点作为关键点

将本是在node_dict 是邻居的节点,现在又都是关键节点了,重新恢复邻

居关系

✅ 总结

稀疏图 = 机器人位置节点 + 所有非零效用节点 + 机器人到聚类中心 / 高效用节点路径上

的节点

update_key_node_observation() # 构建的稀疏图作为观测

如果还没探索完成就 get observation() # 得到根据稀疏图构建的观测,这是输给神经网络的

torch.load(model_file, map_location=self.device)

加载一个模型(或张量)文件,这个文件一般是模型的参数字典,还包括优化器状态等训练状态。由torch.save() 保存,指定加载到GPU 还是CPU

t1, t2 = np.meshgrid(x, y)

candidate_starts = np.vstack([t1.T.ravel(), t2.T.ravel()]).T

x = [x_min, x_max]

y = [y_min, y_max]

t1, t2 = np.meshgrid(x, y)

t1 =

\[x_min, x_max\], \[x_min, x_max\]

t2 =

\[y_min, y_min\], \[y_max, y_max\]

🔹 第一步:t1.Tt2.T

.T 表示矩阵转置。

由于 t1t2 都是 2×2 的矩阵,转置之后其实仍然是 2×2:

t1.T =

\[x_min, x_min\], \[x_max, x_max\]

t2.T =

\[y_min, y_max\], \[y_min, y_max\]

🔹 第二步:ravel()

ravel() 会把二维矩阵"摊平"为一维数组。

t1.T.ravel() → [x_min, x_min, x_max, x_max]

t2.T.ravel() → [y_min, y_max, y_min, y_max]

🔹 第三步:np.vstack([...]).T

np.vstack 把两个一维数组按"行"堆叠成二维数组:

np.vstack([t1.T.ravel(), t2.T.ravel()])

\[x_min, x_min, x_max, x_max\], \[y_min, y_max, y_min, y_max\]

然后再 .T 转置成每一行是一个 (x, y) 坐标对:

candidate_starts =

\[x_min, y_min\], \[x_min, y_max\], \[x_max, y_min\], \[x_max, y_max

]

最终结果

candidate_starts 是一个形状为 (4, 2) 的数组,

存放当前节点块的 四个顶点坐标 (x, y)

角点 坐标说明
[x_min, y_min] 左下角
[x_min, y_max] 左上角
[x_max, y_min] 右下角
[x_max, y_max] 右上角

save_mode 触发条件

save_mode 会在以下情况下被触发:

  1. RL 规划出现 waypoint loop(机器人在两个或几个 waypoint 之间反复来回)

    • 例如历史 waypoint 序列出现 ABAB 或 ABCABC 形式。
  2. RL agent 推出的下一步 waypoint 的 utility 为 0

    • 即到达该节点无法带来额外探索信息。
  3. 用户显式设置参数 ENABLE_SAVE_MODE = True

思考

为什么训练的时候节点维度需要padding , 执行的时候就不需要了?

训练的时候需要进行Batch训练,每一个Batch中节点的数量需要一样多,这样才可以并行处理,合成一个大矩阵 张量 输给神经网络

执行的时候不需要多个batch , 另外transformer本身是可以处理变序列长度的,即节点数量可以变化

1. 张量在 GPU 上是矩阵/多维数组

  • 神经网络在 GPU 上进行前向传播和反向传播时,整个 batch 的数据被当作一个大张量处理

  • 举例:节点特征 node_inputs 的形状 [batch_size, max_nodes, feature_dim]

  • GPU 计算是按块并行矩阵运算的,如果 batch 内每个样本的节点数不同,就无法组成一个规则矩阵。


2. 为什么不能直接用变长序列?

  • 假设 batch 内有两个图:

    • 图 A: 5 个节点

    • 图 B: 8 个节点

  • 如果不 padding,它们的 node_inputs 就是 [5, feature_dim][8, feature_dim]

  • 这种不规则形状无法堆叠成 [2, ?, feature_dim] 的张量 → GPU 不能直接并行计算。


3. 解决办法:padding

  • 将每个图的节点数 padding 到固定长度 N_max(即 batch 内最长的节点数或预设最大值)。

  • 对于不存在的节点,特征填充为 0,mask 标记为 0(或者 1,取决于实现)。

  • 网络在 attention、消息传递时会用 mask 忽略 padding 节点。


4. 部署阶段 batch=1

  • 推理时通常 一次只处理一个图 (batch size=1),此时张量形状 [1, N, feature_dim],N 可以动态变化。

  • GPU 不需要在同一 batch 内对齐多个样本,因此 不需要 padding,只要 mask 正确处理即可。


总结

  • 训练:batch>1 → 同一 batch 内节点数必须统一 → padding + mask

  • 部署/推理:batch=1 → 单图可变长 → 不需要 padding

为什么训练和部署的时候 邻居节点都需要padding?

🧩 一、训练阶段:为什么邻居节点必须 padding?

背景

在训练时,你是 批量并行训练多个样本(batch)

每个样本(一个状态)都有不同数量的邻居节点。

但是你的网络里 pointer 层(SingleHeadAttention) 最终要执行这样的操作:

U = self.norm_factor * torch.matmul(Q, K.transpose(1, 2))

attention = torch.log_softmax(U, dim=-1)

其中:

  • Q → 当前节点的嵌入 [batch, 1, dim]

  • K → 邻居节点嵌入 [batch, num_neighbors, dim]

  • 所以 U 的形状是 [batch, 1, num_neighbors]

问题:num_neighbors 不同怎么办?

举例:

样本 邻居数量
样本 1 4
样本 2 7
样本 3 5

Transformer 的矩阵乘法要求每个 batch 的张量形状一致:

  • 不能同时处理 [1, 4, 128][1, 7, 128][1, 5, 128]

  • 所以必须 pad 到统一的 max_num_neighbors = 7

💡 因此在训练阶段:

  • 你对邻居节点做 padding(补成一样长)

  • edge_padding_mask 把 pad 掉的节点屏蔽掉:

U = U.masked_fill(mask == 1, -1e8)

Softmax 后那些无效节点的概率≈0,对学习无影响。

📘 所以:训练时必须 padding 邻居节点 ,是因为 要在 batch 内进行并行计算

🚀 二、部署(推理)阶段:为什么 有时看起来 也要 padding?

按理说------你完全正确:

我每次输入一个状态,一个节点的邻居数量是固定的(比如 5 个),我直接喂进去 [1, 5, dim] 不就行了吗?

确实如此。
在推理时(batch=1)理论上不需要 padding。

✅ 三、结论总结

阶段 是否必须 padding 邻居节点 原因
训练 ✅ 必须 batch 内样本的邻居数量不同,必须统一形状进行并行矩阵乘法
部署(推理) ❌ 理论上不需要 batch=1,可变长度序列可直接输入
但实践中有时仍 padding ⚙️ 为了复用训练数据管线、避免 shape 不一致错误或保持接口统一

🎯 最核心的一句话:

Padding 本身不是为 Transformer 逻辑服务的,而是为 GPU 并行(batch)服务的。

在推理阶段,只有一个样本时,padding 纯属可选(除非你的代码强制要求固定形状)。

训练的时候,仿真是选择了at , 接下来机器人的位置就直接更新到 st+1 了吗,直接跳过去?

那么执行的时候机器人选择at对应的st+1距离机器人很远,不可能跳过去,这和sim是不是有gap?执行的时候一直一定频率重规划,机器人的实时位置作为st, 而不是上次计算的at要到达的st+1作为此时的st,这样合理吗?

非常好的问题 👏 ------你提到了强化学习在**仿真训练(simulation) 真实执行(deployment)**之间最关键的"sim-to-real gap ",而你说的这个点------"机器人无法瞬移到下一个节点,而是必须靠连续路径前进"------正是这个 gap 的核心。

下面我们系统地分析这件事。


🧩 一、在仿真训练(Sim)中

强化学习通常是在离散拓扑层级上训练

  • 状态 sts_tst​:当前关键节点(如REIM图上的节点坐标)

  • 动作 ata_tat​:选择下一个关键节点(邻居)

  • 环境转移 st+1=f(st,at)s_{t+1} = f(s_t, a_t)st+1​=f(st​,at​):机器人"跳转"到下一个关键节点

  • 奖励 rtr_trt​:例如新探索面积、路径代价惩罚等

👉 换句话说,训练时的环境是离散的、抽象的、无物理运动过程的

机器人从节点 A 到 B 不考虑路径长度或导航误差,只统计逻辑上的探索效果。


🚀 二、在真实执行(Deploy)中

执行阶段则变成:

  • 策略网络仍然在关键节点层面决策(比如"下一个目标是节点 B");

  • 但机器人实际是连续地导航过去:

    • 调用路径规划(如A*、Dijkstra);

    • 发送目标点到局部控制器;

    • 在过程中会因为障碍物、漂移、定位误差等发生偏差。


⚖️ 三、所以确实存在 gap:

方面 仿真训练 实际执行
状态表示 离散节点 实时物理位置
状态转移 瞬间跳转 按路径连续前进
时间步 决策步(节点级) 控制步(持续时间)
动作执行 无误差 可能失败/偏离

🧠 四、你的做法是合理的(而且是业界常见方案)

你说的这种:

"执行的时候机器人以固定频率重规划,用实时位置作为 sts_tst​,而不是上次计划的 st+1s_{t+1}st+1​"

✅ 是完全正确、合理的。

这是**分层执行策略(hierarchical execution)**的典型做法:

上层(策略层)

  • 每次根据当前真实位置(最近关键节点或聚类中心)调用策略;

  • 输出一个离散目标节点(或区域)。

下层(导航层)

  • 用传统路径规划(如A*, DWA、TEB)规划到该目标;

  • 实际执行一段时间;

  • 再次更新真实位置,重新获取状态输入,做下一次决策。

这样可以:

  • 避免由于连续运动和环境变化造成的偏移;

  • 保持策略在执行中对"现实状态"有反馈;

  • 提高鲁棒性。


🔄 五、总结一句话:

在训练中机器人"跳过去"是为了让策略学到节点级的全局决策逻辑

在执行中用"实时位置重规划"是为了在连续物理空间中安全执行

这是强化学习与传统规划模块的典型分层协作方式。

我在训练阶段,学习到了一个策略网络,根据当前状态,这个策略网络能够输出一个最优动作。但是在执行阶段,我换了机器人需要探索的环境,这个环境没有出现在训练阶段,那么策略网络还能够根据当前状态推理出最优动作吗?

非常关键的问题------你现在问到的其实是强化学习在泛化(generalization) 迁移(transferability)上的核心挑战。
你的理解完全正确:训练学到的策略确实是
环境相关的函数
,如果执行环境不同,策略不一定还能输出最优动作。

我们来分层拆解这个问题。


🧩 一、首先明确:策略网络到底学到了什么?

训练好的策略网络 πθ(a∣s)\pi_\theta(a|s)πθ​(a∣s) 实际上是学到了一个从状态到动作的映射函数,这个函数的"最优性"是基于你在训练环境中定义的:

  • 状态特征分布(地图拓扑、可通行性、前沿分布)

  • 动作空间(邻居节点连接方式)

  • 奖励结构(探索面积、距离代价等)

所以本质上,策略网络只在它见过的状态分布范围 内保证性能最优。

换句话说,它的"智能"来自于训练环境的统计规律


🚫 二、如果部署环境不同,会发生什么?

当你把同一个策略放到一个新环境(例如不同布局的建筑或障碍分布)时,

即使状态输入的形式相同(如REIM节点特征、邻居关系),它也可能遇到:

问题类型 举例 后果
分布外状态 (Out-of-distribution) 之前从未见过的拓扑结构 策略输出不稳定、动作随机
不同奖励结构 探索代价与奖励的平衡不同 策略不再"最优"
不同邻居连接规律 图结构更稀疏或更密集 注意力层失效、邻居mask失衡
不同传感器噪声/误差 输入特征失真 策略误判、动作异常

所以,原始策略通常无法直接泛化到一个完全陌生的环境中。


✅ 三、但是------某些设计可以显著提升泛化能力

这也是为什么你的系统中使用了基于图结构(key node graph)+ Transformer 这样的设计:

它们天然比 CNN 或固定维度输入的网络更具泛化能力。

1️⃣ 拓扑不变性

Transformer 处理变长节点序列 + 图结构特征,可以应对不同地图大小、形状的差异。

2️⃣ 基于局部特征决策

如果策略只依赖于局部邻居信息(而非全局坐标),它能迁移到不同环境,因为局部探索规律相似。

3️⃣ 归一化和mask机制

通过padding mask统一输入结构,能让模型在训练时学会"忽略无效邻居",从而在执行时适应不同节点数量。

4️⃣ domain randomization(领域随机化)

在训练阶段使用多种地图、随机初始位置、不同探索目标,可以让策略"习惯"变化,显著提高泛化性能。


⚙️ 四、实际执行时的可行方案

方法 说明 优缺点
(1) 直接零样本部署 直接用训练好的策略执行新环境 快速但不稳定,需训练时广泛domain randomization
(2) 在线微调(fine-tuning) 在新环境中继续训练几轮 稳定但耗时,需要安全探索机制
(3) 分层控制 策略输出高层目标,底层靠经典规划(A*/TEB)实现 实际最常用,策略泛化问题被显著缓解
(4) 模型蒸馏或模仿学习 用RL策略生成数据,训练一个泛化性更强的策略 可迁移,但需要数据量大

🧠 五、总结一句话:

策略网络在新的环境中不一定输出最优动作

但如果它基于拓扑结构、局部邻居关系domain-randomized训练

就有机会在未见过的环境中表现出近似最优的行为。

总之,你的训练样本很多很随机分布很广,可以表征出完整的所有可能的状态,就不需要泛化性,因为你执行的时候就会遇到和训练一样的状态,但是这不可能的事。

但是一般情况下,训练出来的模型具有一定的泛化性,换句话说遇到差不多的状态,也能作出一定的不错的决策来。除非遇到差距很大的状态,模型表现可能会实效。

所以说提升泛化性的首要是样本分布广表征足够多的状态,其次是起点随机,另外可以学习判断这个是不是在强化训练的状态分布中的,不是就切换专家系统执行

相关推荐
王哈哈^_^2 小时前
YOLOv11视觉检测实战:安全距离测算全解析
人工智能·数码相机·算法·yolo·计算机视觉·目标跟踪·视觉检测
Q_Q19632884752 小时前
python+django/flask基于Echarts+Python的图书零售监测系统设计与实现(带大屏)
spring boot·python·django·flask·node.js·php
..Cherry..2 小时前
Etcd详解(raft算法保证强一致性)
数据库·算法·etcd
深度学习lover3 小时前
<数据集>yolo航拍交通目标识别数据集<目标检测>
人工智能·python·yolo·目标检测·计算机视觉·航拍交通目标识别
商汤万象开发者3 小时前
LazyLLM教程 | 第13讲:RAG+多模态:图片、表格通吃的问答系统
人工智能·科技·算法·开源·多模态
程序猿20233 小时前
Python每日一练---第二天:合并两个有序数组
开发语言·python
权泽谦3 小时前
用 Flask + OpenAI API 打造一个智能聊天机器人(附完整源码与部署教程)
python·机器人·flask
Lee_yayayayaya3 小时前
本原多项式产生m序列的原理
算法
njxiejing3 小时前
Numpy一维、二维、三维数组切片实例
开发语言·python·numpy