【机器人 / 强化学习】HIL-SERL 算法篇:DQN + SAC 混合架构的实现哲学
·
【机器人 / 强化学习】HIL-SERL 算法篇:DQN + SAC 混合架构的实现哲学
引言:从单一算法到混合架构的必然性在机器人强化学习领域,传统的单一算法(如DQN或SAC)往往难以兼顾离散动作空间与连续动作空间的协同需求。例如,机器人抓取任务中,机械臂的关节角度(连续动作)与夹爪的开合(离散动作)需要联合优化。HIL-SERL(Hierarchical Implicit Learning - Soft Entropy Regularized Learning)算法通过将DQN(Deep Q-Network)与SAC(Soft Actor-Critic)有机融合,构建了一个层次化混合架构。其核心哲学在于:用DQN处理高层策略的离散决策,用SAC优化底层连续控制,通过隐式价值对齐实现两层的无缝衔接。本文将从原理层面剖析这一混合机制的数学基础,并通过可运行代码展示其实现细节。## 一、混合架构的数学原理:价值函数的双重角色HIL-SERL的核心思想是将决策过程分解为两个层次:- 高层(High-Level):使用DQN学习离散动作(如“移动至目标区域”),其Q函数记为 ( Q_h(s, a_h) ),其中 ( a_h \in {0,1,…,K-1} )。- 底层(Low-Level):使用SAC学习连续动作(如关节扭矩),其Q函数记为 ( Q_l(s, a_l) ),其中 ( a_l \in \mathbb{R}^d )。关键创新在于:底层的SAC策略 ( \pi_l(a_l | s, a_h) ) 不仅依赖状态 ( s ),还依赖高层输出的离散动作 ( a_h ) 作为条件。而DQN的奖励信号由底层SAC的隐含价值(即SAC的软Q值)生成,而非直接使用环境奖励。具体来说,对于高层动作 ( a_h ),其奖励定义为:[r_h(s, a_h) = \mathbb{E}_{a_l \sim \pi_l} \left[ Q_l(s, a_l) - \alpha \log \pi_l(a_l | s, a_h) \right]]其中 ( \alpha ) 是SAC的温度参数。这相当于用底层策略的熵正则化价值作为高层的评价标准,实现了“高层负责宏观决策,底层负责微观执行”的层次化学习。## 二、代码实现:DQN与SAC的联合训练框架以下代码展示了HIL-SERL的核心训练循环,包含高层DQN和底层SAC的交替更新。我们使用PyTorch实现一个简化版。pythonimport torchimport torch.nn as nnimport torch.optim as optimimport numpy as npfrom collections import dequeimport random# 定义环境接口(示例:机器人抓取任务)class RobotEnv: def __init__(self): self.state_dim = 10 # 状态维度(如关节角度+视觉特征) self.high_action_dim = 4 # 高层离散动作数(如左移、右移、抓取、释放) self.low_action_dim = 3 # 底层连续动作数(如关节扭矩) def step(self, high_action, low_action): # 简化:随机返回下一个状态和奖励 next_state = np.random.randn(self.state_dim) reward = np.random.rand() # 环境奖励 done = False return next_state, reward, done def reset(self): return np.random.randn(self.state_dim)# ---------- 底层SAC网络 ----------class SACPolicy(nn.Module): def __init__(self, state_dim, high_action_dim, low_action_dim, hidden_dim=128): super().__init__() # 输入:状态 + 高层动作的one-hot编码 self.net = nn.Sequential( nn.Linear(state_dim + high_action_dim, hidden_dim), nn.ReLU(), nn.Linear(hidden_dim, hidden_dim), nn.ReLU() ) self.mean = nn.Linear(hidden_dim, low_action_dim) self.log_std = nn.Linear(hidden_dim, low_action_dim) def forward(self, state, high_action_one_hot): x = torch.cat([state, high_action_one_hot], dim=-1) x = self.net(x) mean = self.mean(x) log_std = torch.clamp(self.log_std(x), -20, 2) # 限制方差范围 std = torch.exp(log_std) return mean, std# ---------- 高层DQN网络 ----------class DQNNetwork(nn.Module): def __init__(self, state_dim, high_action_dim, hidden_dim=128): super().__init__() self.net = nn.Sequential( nn.Linear(state_dim, hidden_dim), nn.ReLU(), nn.Linear(hidden_dim, hidden_dim), nn.ReLU(), nn.Linear(hidden_dim, high_action_dim) # 输出每个离散动作的Q值 ) def forward(self, state): return self.net(state)# ---------- HIL-SERL训练核心 ----------class HIL_SERL: def __init__(self, env, lr=3e-4, gamma=0.99, alpha=0.2): self.env = env self.gamma = gamma self.alpha = alpha # SAC温度参数 # 网络初始化 self.dqn = DQNNetwork(env.state_dim, env.high_action_dim) self.dqn_target = DQNNetwork(env.state_dim, env.high_action_dim) self.dqn_target.load_state_dict(self.dqn.state_dict()) self.dqn_optimizer = optim.Adam(self.dqn.parameters(), lr=lr) self.sac_policy = SACPolicy(env.state_dim, env.high_action_dim, env.low_action_dim) self.sac_q1 = nn.Sequential( nn.Linear(env.state_dim + env.low_action_dim, 128), nn.ReLU(), nn.Linear(128, 1) ) self.sac_q2 = nn.Sequential( nn.Linear(env.state_dim + env.low_action_dim, 128), nn.ReLU(), nn.Linear(128, 1) ) self.sac_optimizer = optim.Adam(list(self.sac_policy.parameters()) + list(self.sac_q1.parameters()) + list(self.sac_q2.parameters()), lr=lr) self.replay_buffer = deque(maxlen=10000) def select_high_action(self, state, epsilon=0.1): """高层动作选择:ε-贪心""" if np.random.rand() < epsilon: return np.random.randint(self.env.high_action_dim) state_tensor = torch.FloatTensor(state).unsqueeze(0) q_values = self.dqn(state_tensor) return torch.argmax(q_values).item() def select_low_action(self, state, high_action): """底层动作选择:SAC重参数化采样""" high_one_hot = torch.zeros(self.env.high_action_dim) high_one_hot[high_action] = 1.0 state_tensor = torch.FloatTensor(state).unsqueeze(0) high_one_hot = high_one_hot.unsqueeze(0) mean, std = self.sac_policy(state_tensor, high_one_hot) # 重参数化采样 noise = torch.randn_like(std) action = mean + noise * std action = torch.tanh(action) # 压缩到[-1,1] return action.squeeze(0).detach().numpy() def update(self, batch_size=64): """混合更新:同时优化DQN和SAC""" if len(self.replay_buffer) < batch_size: return batch = random.sample(self.replay_buffer, batch_size) states = torch.FloatTensor([b[0] for b in batch]) high_actions = torch.LongTensor([b[1] for b in batch]) low_actions = torch.FloatTensor([b[2] for b in batch]) rewards = torch.FloatTensor([b[3] for b in batch]) next_states = torch.FloatTensor([b[4] for b in batch]) dones = torch.FloatTensor([b[5] for b in batch]) # ---------- 更新底层SAC ---------- # 计算当前策略的Q值(软更新) with torch.no_grad(): # 为下一个状态采样底层动作(使用当前策略) high_one_hot = torch.zeros(batch_size, self.env.high_action_dim) # 这里简化:假设下一个状态的高层动作由当前DQN选择 next_high_q = self.dqn_target(next_states) next_high_action = torch.argmax(next_high_q, dim=1) high_one_hot.scatter_(1, next_high_action.unsqueeze(1), 1.0) next_mean, next_std = self.sac_policy(next_states, high_one_hot) noise = torch.randn_like(next_std) next_low_actions = next_mean + noise * next_std next_low_actions = torch.tanh(next_low_actions) # 目标Q值(双Q网络取最小) q1_next = self.sac_q1(torch.cat([next_states, next_low_actions], dim=1)) q2_next = self.sac_q2(torch.cat([next_states, next_low_actions], dim=1)) q_next = torch.min(q1_next, q2_next) # 计算软目标:Q + α * H(π) log_prob = -0.5 * (noise.pow(2) + 2 * np.log(2 * np.pi) + 2 * next_std.log()).sum(dim=1) target_q = rewards + self.gamma * (1 - dones) * (q_next.squeeze() + self.alpha * log_prob) # 当前Q值 current_q1 = self.sac_q1(torch.cat([states, low_actions], dim=1)).squeeze() current_q2 = self.sac_q2(torch.cat([states, low_actions], dim=1)).squeeze() # SAC损失:最小化Q值误差 sac_loss = nn.MSELoss()(current_q1, target_q.detach()) + nn.MSELoss()(current_q2, target_q.detach()) self.sac_optimizer.zero_grad() sac_loss.backward() self.sac_optimizer.step() # ---------- 更新高层DQN ---------- # 关键:用底层SAC的隐含价值作为高层奖励 with torch.no_grad(): # 为当前状态的高层动作计算隐含价值 high_one_hot_current = torch.zeros(batch_size, self.env.high_action_dim) high_one_hot_current.scatter_(1, high_actions.unsqueeze(1), 1.0) current_mean, current_std = self.sac_policy(states, high_one_hot_current) noise = torch.randn_like(current_std) current_low_actions = current_mean + noise * current_std current_low_actions = torch.tanh(current_low_actions) # 隐含奖励 = SAC的软Q值 q1_implicit = self.sac_q1(torch.cat([states, current_low_actions], dim=1)) q2_implicit = self.sac_q2(torch.cat([states, current_low_actions], dim=1)) implicit_reward = torch.min(q1_implicit, q2_implicit).squeeze() # DQN目标:使用隐含奖励计算贝尔曼方程 with torch.no_grad(): next_q_values = self.dqn_target(next_states).max(dim=1)[0] dqn_target = implicit_reward + self.gamma * (1 - dones) * next_q_values current_q_values = self.dqn(states).gather(1, high_actions.unsqueeze(1)).squeeze() dqn_loss = nn.MSELoss()(current_q_values, dqn_target.detach()) self.dqn_optimizer.zero_grad() dqn_loss.backward() self.dqn_optimizer.step() # 软更新目标网络 for target_param, param in zip(self.dqn_target.parameters(), self.dqn.parameters()): target_param.data.copy_(0.005 * param.data + 0.995 * target_param.data)## 三、哲学思考:为何混合架构优于单一算法?HIL-SERL的混合哲学体现在三个层面:1. 决策粒度的解耦:DQN擅长处理离散、稀疏的高层决策(如“是否抓取”),而SAC擅长连续、平滑的底层控制(如“如何调整力矩”)。将两者分离可避免单一算法在混合动作空间中的维度灾难。例如,若直接使用SAC处理离散+连续联合动作,其策略分布将变得复杂且难以优化。2. 价值传递的隐式对齐:传统层次强化学习(如Option-Critic)需要显式定义子目标,而HIL-SERL通过让DQN学习SAC的软Q值作为奖励,实现了“高层策略自动理解底层能力边界”的隐式对齐。这类似于人类决策中“知道自己的能力上限后再做计划”。3. 熵正则化的桥梁作用:SAC中的熵正则化项 ( -\alpha \log \pi_l ) 不仅鼓励底层探索,还让高层的隐含奖励更加平滑(因为底层策略的随机性被编码进价值函数)。这避免了高层策略因底层策略的确定性突变而陷入局部最优。## 四、运行与验证:一个简单的实验以下代码演示如何实例化HIL-SERL并执行一个训练步骤。python# 创建环境和算法实例env = RobotEnv()agent = HIL_SERL(env)# 执行一个完整的episodestate = env.reset()done = Falsetotal_reward = 0while not done: # 高层选择离散动作 high_action = agent.select_high_action(state, epsilon=0.2) # 底层根据高层指令生成连续动作 low_action = agent.select_low_action(state, high_action) # 环境交互 next_state, reward, done = env.step(high_action, low_action) # 存储经验(注意:这里存储的是环境奖励,但高层更新时会用隐含奖励替代) agent.replay_buffer.append((state, high_action, low_action, reward, next_state, done)) # 更新网络 agent.update(batch_size=32) state = next_state total_reward += rewardprint(f"Episode total reward: {total_reward:.2f}")## 总结HIL-SERL通过DQN与SAC的混合架构,为机器人强化学习提供了一种层次化价值对齐的解决方案。其核心哲学在于:不将混合动作空间视为一个整体,而是利用DQN的离散决策能力与SAC的连续控制优势,通过底层隐含价值作为高层奖励的桥梁,实现策略的协同进化。实际应用中,这种架构在灵巧手操作、移动机器人导航等任务中展现出显著优势——高层策略能快速收敛到合理的宏观策略,而底层策略则在微观层面保持平滑与鲁棒性。未来,该思想可进一步扩展至多智能体场景,或结合注意力机制实现更复杂的层次分解。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)