HDPlanner 代码阅读
这个是DRLE的改进版本。只关注了不同部分的代码
状态空间不同
维护了信标节点
网络结构不同
Policy Net
视点编码器一样,信标解码器的查询是目标特征,key 是信标特征,最后是一个单头注意力,输出选择的一个信标,作为导航子目标或者探索目标。
然后是动作解码器,查询由机器人位置 当前节点特征和选择的信标特征构成,key 就是邻居特征,最后是一个单头注意力,输出选择一个邻居(动作)
信标可以类比与自己的探索目标,使用这个网络结构可以达到选择探索目标的目的
Q Net
与上面类似,只不过这里不同于DRLE 的是计算了两个Q value(选择信标的 和 选择动作的)
训练过程和DRLE 一样只不过因为是有两个Q value,
所以说针对一个Q net 需要分别构建关于信标q 动作q 带来的loss function
然后两个 loss function 加起来
总之与DRLE 不同指出就是多了一个q value(信标的)
增加了对比学习
对比学习
对比学习的基本理念
对比学习是一种 无监督或自监督的表示学习方法,核心目标是:
-
拉近相似样本(positive pairs)在特征空间的距离
-
推远不相似样本(negative pairs)在特征空间的距离
换句话说,CL 想让模型学会把“应该相似的东西”映射到特征空间里很靠近,把“不应该相似的东西”映射得远一些。
对比学习的目标是 提高特征区分度,辅助 RL 网络更快收敛,尤其是在探索动作空间稀疏的情况下。
总结
对比学习在你的训练中起到了 辅助策略网络学习更清晰的特征表示 的作用:
-
强化 RL 学习信号 → 动作选择更准确
-
增加特征空间可分性 → 正例和负例更容易区分
-
与 RL 损失结合 → triplet loss + policy loss 一起训练
对比学习的概念?
对比学习的过程,要素?
作用?
https://blog.csdn.net/2503_90237586/article/details/148469494?ops_request_misc=elastic_search_misc&request_id=1f17a7befb18a03a9daa6f6ba5b71aa6&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_click~default-3-148469494-null-null.142^v102^pc_search_result_base5&utm_term=%E5%AF%B9%E6%AF%94%E5%AD%A6%E4%B9%A0&spm=1018.2226.3001.4187
https://blog.csdn.net/exploer_try/article/details/116502372?ops_request_misc=elastic_search_misc&request_id=1f17a7befb18a03a9daa6f6ba5b71aa6&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~ElasticSearch~search_v2-21-116502372-null-null.142^v102^pc_search_result_base5&utm_term=%E5%AF%B9%E6%AF%94%E5%AD%A6%E4%B9%A0&spm=1018.2226.3001.4187
https://blog.csdn.net/zlyzjiabjw547479/article/details/147091672?ops_request_misc=elastic_search_misc&request_id=1f17a7befb18a03a9daa6f6ba5b71aa6&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-147091672-null-null.142^v102^pc_search_result_base5&utm_term=%E5%AF%B9%E6%AF%94%E5%AD%A6%E4%B9%A0&spm=1018.2226.3001.4187
对比学习对强化学习的作用?
🧩 一、直观理解:对比学习在帮 RL “看得更清楚”
强化学习的核心是:
学习一个策略 π(a|s),让智能体在状态空间中选择最优动作。
但 RL 的瓶颈在于:
-
状态表示(state representation)不稳定:同样的环境变化很大,模型难以泛化。
-
奖励稀疏:智能体要靠很少的奖励信号学会复杂策略。
-
探索效率低:很多状态相似但不完全一样,模型无法识别哪些“相似”。
而对比学习的目标是:
让模型学会“哪些状态(或节点)相似,哪些不同”,从而在没有奖励的地方也能形成有意义的表征。
所以,CL 相当于在 RL 前面加了一个“视觉+认知的预训练层”,帮助模型更容易分辨好状态、坏状态。
⚙️ 二、机制层面:CL 为 RL 提供了“结构化特征空间”
1️⃣ 在状态表示学习中
传统 RL(如 DQN, PPO)直接从原始输入(图像、节点特征等)学 Q 值或策略。
这种方式容易过拟合环境表面特征,导致泛化差。
CL 通过对比损失(如 Triplet loss 或 InfoNCE):
-
拉近语义相似的状态/节点(positive pair)
-
推远语义不同的状态/节点(negative pair)
从而学得一个结构化的嵌入空间,不同状态之间的相似度在几何上有意义。
对应到你的场景:
相似状态 = “相邻、连通、效用接近的地图节点”
不同状态 = “被障碍分隔或效用差异大的节点”
模型学到的 embedding 会把可通行区域映射得近、障碍区映射得远。
2️⃣ 在策略学习中
在你代码中(例如 graph_encoder_and_center_decoder):
-
RL 网络输出中心节点(action)的概率分布
-
对比学习模块再通过 Triplet loss 优化特征空间
-
anchor = 当前策略选择的中心节点
-
positive = 最优中心节点(比如 A* 代价最小的节点)
-
negative = 其他随机节点
-
其作用是让策略网络的输出特征空间满足:
∥f(aanchor)−f(apositive)∥<∥f(aanchor)−f(anegative)∥−margin\|f(a_{anchor}) - f(a_{positive})\| < \|f(a_{anchor}) - f(a_{negative})\| - margin∥f(aanchor)−f(apositive)∥<∥f(aanchor)−f(anegative)∥−margin
这使得策略网络:
-
更容易区分“好动作”和“坏动作”;
-
在新环境中仍能识别出“形状相似但从未见过”的好节点。
🧠 三、效果层面:CL 帮 RL 提升三大核心能力
| 能力 | 普通 RL | 加入对比学习后的 RL |
|---|---|---|
| 状态表示能力 | 依赖奖励信号,容易陷入局部最优 | 自监督预训练 + 相似性约束,表示更稳定 |
| 泛化能力 | 新环境表现差 | 可以迁移到未见过的状态空间 |
| 样本效率 | 训练需要大量交互 | 无需额外奖励即可辅助学习特征 |
🚀 四、结合你的多机器人探索任务
在你的场景中:
-
Anchor:当前策略选出的中心信标节点
-
Positive:真实最优或近似最优中心(由代价函数或 A* 求得)
-
Negative:其他非最优节点
对比学习的结果是:
-
所有“真正重要的中心区域”在特征空间中被聚得很近;
-
无效区域(如被障碍阻隔、远离目标的地方)被推远;
-
策略网络在遇到新地图时,也能识别出“形状上类似”的中心结构。
这在多机器人探索中极有价值,因为:
探索任务的奖励信号(比如地图覆盖率)非常稀疏,
CL 提供了一个“结构性指导”,让每个机器人更快收敛到有效的分工策略。
🧩 五、总结一句话:
对比学习是强化学习的“表示催化剂”
它让 RL 的特征空间更有几何结构,从而:
-
改善状态识别;
-
提升泛化;
-
加速收敛;
-
增强稳定性。
总之,他可以将状态空间中在特征空间上相似的状态拉近,在特征空间上不相似的状态推远,这样可以识别出好状态 坏状态,选择出好动作,也有利于提升泛化性,见到没有见过的相似的状态也能识别出来
# contrastive learning for center and actioncenter_logp, action_logp, \selected_center_index, selected_action_index, \center_node_features, neighboring_features, selected_center_feature, selected_action_feature = global_policy_net(*observation)epsilon = 0.5if torch.rand(1) < epsilon:cl_center_q_values, _, cl_action_q_values, _ = dp_q_net1(*q_observation)else:cl_center_q_values, _, cl_action_q_values, _ = dp_target_q_net1(*q_observation)center_index = torch.argmax(cl_center_q_values, dim=1)action_index = torch.argmax(cl_action_q_values, dim=1)center_positive_node_features, center_negative_node_features = get_contrastive_pairs(center_logp, center_node_features, selected_center_index, center_index)action_positive_node_features, action_negative_node_features = get_contrastive_pairs(action_logp, neighboring_features, selected_action_index, action_index)triplet_loss_center = get_triplet_loss(selected_center_feature, center_positive_node_features, center_negative_node_features)triplet_loss_action = get_triplet_loss(selected_action_feature, action_positive_node_features, action_negative_node_features)triplet_loss = triplet_loss_center + triplet_loss_actionglobal_policy_optimizer.zero_grad()triplet_loss.backward()policy_contrastive_grad_norm = torch.nn.utils.clip_grad_norm_(global_policy_net.parameters(), max_norm=10000, norm_type=2)global_policy_optimizer.step()def get_contrastive_pairs(action_logp, center_node_features, center_index, index):valid_indices = torch.nonzero(action_logp > -1e7, as_tuple=False)has_valid_values = valid_indices[:, 0].unique()selected_indices = []for row_index in has_valid_values:row_valid_indices = valid_indices[valid_indices[:, 0] == row_index, 1]selected_index = torch.randint(0, row_valid_indices.size(0), (1,))selected_indices.append(row_valid_indices[selected_index].unsqueeze(0))center_index = torch.cat(selected_indices, dim=0)# print("Center Index:", center_index.size()) # [batch_size, 1]negative_node_features = center_node_features[torch.arange(center_node_features.size(0)), center_index.squeeze(1), :].unsqueeze(1)negative_node_features = negative_node_features.view(negative_node_features.size(0), -1)# print("Negative Node Features:", negative_node_features.size()) # [batch_size, 128]positive_node_features = center_node_features[torch.arange(center_node_features.size(0)), index.squeeze(1), :].unsqueeze(1)positive_node_features = positive_node_features.view(positive_node_features.size(0), -1)# print("Positive Node Features:", positive_node_features.size()) # [batch_size, 128]return positive_node_features, negative_node_featuresdef get_triplet_loss(anchor, positive, negative, margin=0.5):distance_positive = F.pairwise_distance(anchor, positive)distance_negative = F.pairwise_distance(anchor, negative)loss = F.relu(distance_positive - distance_negative + margin)loss = loss.mean()# print("Triplet Loss:", loss.item())return loss
selected_center_index = center_index[torch.arange(center_index.size(0)), center_logp_index, :] 细致的介绍每一个语法
1️⃣ center_index
-
假设形状
[B, n_center, 1]-
B = batch size
-
n_center = 每个样本的候选中心节点数量
-
最后一个维度是单列(存放全局节点索引)
-
示例:
center_index = [[[3], [5], [7]], # 第 0 个样本的候选中心
[[2], [4], [6]]] # 第 1 个样本的候选中心
# shape = [2, 3, 1]
2️⃣ torch.arange(center_index.size(0))
-
center_index.size(0)= batch size = B -
torch.arange(B)会生成[0, 1, 2, ..., B-1] -
用作 批量索引,对应每个样本的行索引
B = 2
torch.arange(B) # tensor([0, 1])
3️⃣ center_logp_index
-
形状
[B] -
每个元素表示 在候选中心列表中的位置,也就是
center_logp概率最大的索引
示例:
center_logp_index = [2, 0]
# 表示第 0 个样本选择第 2 个候选中心,第 1 个样本选择第 0 个候选中心
4️⃣ 索引方式
center_index[torch.arange(B), center_logp_index, :]
-
Python / PyTorch 高级索引:
-
第一个维度:行(样本)索引 →
[0, 1, 2, ...] -
第二个维度:列(候选中心位置)索引 →
center_logp_index -
第三个维度:冒号
:→ 选取完整列(全局节点索引)
-
逻辑:对每个样本,从候选中心列表中选出概率最高的中心节点对应的全局索引
5️⃣ 输出
-
形状
[B, 1] -
内容就是每个样本选择的中心节点在全局节点图中的索引
