当前位置: 首页 > news >正文

机械臂模仿学习2.1:行为克隆

核心定义与本质

行为克隆(BC)是一种模仿学习方法,模仿人的动作,学出一个策略网络

本质是学习从状态到动作的映射函数,将模仿转化为监督学习(分类或者回归)

实现过程

1.数据收集

数据集由(状态,动作)二元组构成,记作

  • sj​ 为原始状态
  • aj​为专家动作

2.模型选择

选择策略模型 πθ,以状态 s 为输入,输出动作 a 的概率分布或具体值

连续动作:线性模型线性层,输出连续值

离散动作:分类模型Softmax,输出动作概率分布

3.损失函数

连续动作:均方误差(MSE)Huber 损失

离散动作:交叉熵

4.监督训练

使用梯度下降等算法最小化损失,调整参数 θ,使模型输出逼近专家动作

5.策略部署

训练完成后,模型 πθ 可直接用于新状态输入,独立预测并执行动作。

优化目标函数

痛点

分布偏移:模型仅在专家访问过的状态分布上训练。实际部署时,微小误差会导致模型进入数据集从未去过的“新状态”,失去参考依据。

复合误差:在“新状态”下,模型缺乏经验可能做出更错误的动作,导致状态进一步偏离。误差像滚雪球一样累积,最终导致任务彻底失败。

代码实践

1.训练PPO智能体

train_expert.py

# train_expert_ppo.py import paddle import paddle.nn.functional as F import gymnasium as gym import numpy as np from tqdm import tqdm class PolicyNet(paddle.nn.Layer): def __init__(self, state_dim, hidden_dim, action_dim): super().__init__() self.fc1 = paddle.nn.Linear(state_dim, hidden_dim) self.fc2 = paddle.nn.Linear(hidden_dim, action_dim) def forward(self, x): x = F.relu(self.fc1(x)) return F.softmax(self.fc2(x), axis=-1) # 输出概率 class ValueNet(paddle.nn.Layer): def __init__(self, state_dim, hidden_dim): super().__init__() self.fc1 = paddle.nn.Linear(state_dim, hidden_dim) self.fc2 = paddle.nn.Linear(hidden_dim, 1) def forward(self, x): x = F.relu(self.fc1(x)) return self.fc2(x) def compute_advantage(rewards, values, gamma=0.99, lam=0.95): """计算 GAE 优势函数""" advantages = [] gae = 0 values = values + [0] # 最后加一个0作为下一个状态的值 for t in reversed(range(len(rewards))): delta = rewards[t] + gamma * values[t+1] - values[t] gae = delta + gamma * lam * gae advantages.insert(0, gae) return advantages def ppo_update(policy, value, optimizer_p, optimizer_v, states, actions, old_log_probs, advantages, returns, clip_eps=0.2, epochs=10): for _ in range(epochs): # 计算新策略的对数概率 probs = policy(states) dist = paddle.distribution.Categorical(probs) # 调整 actions 形状以匹配 log_prob 要求 actions = actions.reshape([-1, 1]) # [batch_size, 1] new_log_probs = dist.log_prob(actions) # 结果应为 [batch_size, 1] new_log_probs = new_log_probs.squeeze(-1) # 压缩为 [batch_size] 以便与 old_log_probs 一致 # 计算比率 ratio = paddle.exp(new_log_probs - old_log_probs) # old_log_probs 是 [batch_size] surr1 = ratio * advantages surr2 = paddle.clip(ratio, 1 - clip_eps, 1 + clip_eps) * advantages policy_loss = -paddle.mean(paddle.minimum(surr1, surr2)) # 价值网络损失 value_pred = value(states).squeeze() # [batch_size] value_loss = F.mse_loss(value_pred, returns) # 更新 optimizer_p.clear_grad() policy_loss.backward() optimizer_p.step() optimizer_v.clear_grad() value_loss.backward() optimizer_v.step() def collect_trajectory(env, policy, value, max_steps=500): states, actions, rewards, log_probs, values = [], [], [], [], [] state = env.reset()[0] if isinstance(env.reset(), tuple) else env.reset() done = False while not done: state_t = paddle.to_tensor(state, dtype='float32').unsqueeze(0) probs = policy(state_t) v = value(state_t).item() dist = paddle.distribution.Categorical(probs) action = dist.sample([1]).item() log_prob = dist.log_prob(paddle.to_tensor([action])).item() step_result = env.step(action) if len(step_result) == 5: next_state, reward, terminated, truncated, _ = step_result done = terminated or truncated else: next_state, reward, done, _ = step_result states.append(state) actions.append(action) rewards.append(reward) log_probs.append(log_prob) values.append(v) state = next_state return states, actions, rewards, log_probs, values def train_ppo(env, policy, value, optimizer_p, optimizer_v, episodes=1000): return_list = [] for episode in tqdm(range(episodes), desc="PPO训练"): # 收集一条轨迹 states, actions, rewards, log_probs, values = collect_trajectory(env, policy, value) episode_return = sum(rewards) return_list.append(episode_return) # 计算优势与回报 advantages = compute_advantage(rewards, values) returns = [adv + v for adv, v in zip(advantages, values)] # 简化:return = advantage + value advantages = paddle.to_tensor(advantages, dtype='float32') returns = paddle.to_tensor(returns, dtype='float32') states = paddle.to_tensor(states, dtype='float32') actions = paddle.to_tensor(actions, dtype='int64') old_log_probs = paddle.to_tensor(log_probs, dtype='float32') # 归一化优势 advantages = (advantages - advantages.mean()) / (advantages.std() + 1e-8) # PPO更新 ppo_update(policy, value, optimizer_p, optimizer_v, states, actions, old_log_probs, advantages, returns) if (episode+1) % 100 == 0: avg = np.mean(return_list[-100:]) print(f"Episode {episode+1}, 平均回报: {avg:.2f}") return return_list # 主程序 env = gym.make('CartPole-v1') state_dim = env.observation_space.shape[0] action_dim = env.action_space.n hidden_dim = 128 policy = PolicyNet(state_dim, hidden_dim, action_dim) value = ValueNet(state_dim, hidden_dim) optimizer_p = paddle.optimizer.Adam(learning_rate=3e-4, parameters=policy.parameters()) optimizer_v = paddle.optimizer.Adam(learning_rate=1e-3, parameters=value.parameters()) print("开始PPO训练...") returns = train_ppo(env, policy, value, optimizer_p, optimizer_v, episodes=500) # PPO通常收敛更快 paddle.save(policy.state_dict(), "net_ppo.pdparams") print("专家模型已保存为 net_ppo.pdparams")

2.行为克隆

bc_cartpole.py

import paddle import paddle.nn.functional as F import gymnasium as gym import numpy as np import matplotlib.pyplot as plt from tqdm import tqdm # -------------------- 网络定义 -------------------- class PolicyNet(paddle.nn.Layer): def __init__(self, state_dim, hidden_dim, action_dim): super(PolicyNet, self).__init__() self.fc1 = paddle.nn.Linear(state_dim, hidden_dim) self.fc2 = paddle.nn.Linear(hidden_dim, action_dim) def forward(self, x): x = F.relu(self.fc1(x)) # 注意:这里返回 logits(不加 softmax),以便使用交叉熵 return self.fc2(x) # -------------------- 专家数据采样 -------------------- def sample_expert_data(n_episode, env, model): states = [] actions = [] for episode in range(n_episode): reset_result = env.reset() if isinstance(reset_result, tuple): state = reset_result[0] else: state = reset_result done = False while not done: state_tensor = paddle.to_tensor(state, dtype='float32').unsqueeze(0) logits = model(state_tensor) # 注意:模型返回 logits probs = F.softmax(logits, axis=-1) # 转换为概率以便取动作 action = paddle.argmax(probs, axis=-1).item() states.append(state) actions.append(action) step_result = env.step(action) if len(step_result) == 5: next_state, reward, terminated, truncated, _ = step_result done = terminated or truncated else: next_state, reward, done, _ = step_result state = next_state return np.array(states), np.array(actions) # -------------------- 行为克隆智能体 -------------------- class BehaviorClone: def __init__(self, state_dim, hidden_dim, action_dim, lr): self.policy = PolicyNet(state_dim, hidden_dim, action_dim) self.optimizer = paddle.optimizer.Adam(parameters=self.policy.parameters(), learning_rate=lr) def learn(self, states, actions): states = paddle.to_tensor(states, dtype="float32") actions = paddle.to_tensor(actions, dtype="int64") # 一维标签 logits = self.policy(states) # [batch, action_dim] loss = F.cross_entropy(logits, actions) # 交叉熵损失 self.optimizer.clear_grad() loss.backward() self.optimizer.step() return loss def take_action(self, state, deterministic=True): state = paddle.to_tensor([state], dtype="float32") logits = self.policy(state) probs = F.softmax(logits, axis=-1) if deterministic: return paddle.argmax(probs, axis=-1).item() else: return paddle.distribution.Categorical(probs).sample([1]).item() # -------------------- 测试函数 -------------------- def test_agent(agent, env, n_episode, deterministic=True): return_list = [] for _ in range(n_episode): reset_result = env.reset() state = reset_result[0] if isinstance(reset_result, tuple) else reset_result episode_return = 0 done = False while not done: action = agent.take_action(state, deterministic=deterministic) step_result = env.step(action) if len(step_result) == 5: next_state, reward, terminated, truncated, _ = step_result done = terminated or truncated else: next_state, reward, done, _ = step_result state = next_state episode_return += reward return_list.append(episode_return) return np.mean(return_list) def test_expert(env, model, n_episodes=10): returns = [] for _ in range(n_episodes): reset_result = env.reset() state = reset_result[0] if isinstance(reset_result, tuple) else reset_result episode_return = 0 done = False while not done: state_tensor = paddle.to_tensor(state, dtype='float32').unsqueeze(0) logits = model(state_tensor) probs = F.softmax(logits, axis=-1) action = paddle.argmax(probs, axis=-1).item() step_result = env.step(action) if len(step_result) == 5: state, reward, terminated, truncated, _ = step_result done = terminated or truncated else: state, reward, done, _ = step_result episode_return += reward returns.append(episode_return) return np.mean(returns) # -------------------- 主程序 -------------------- env_name = 'CartPole-v1' env = gym.make(env_name) state_dim = env.observation_space.shape[0] action_dim = env.action_space.n hidden_dim = 128 # 加载专家模型 actor = PolicyNet(state_dim, hidden_dim, action_dim) try: layer_state_dict = paddle.load("net_ppo.pdparams") actor.set_state_dict(layer_state_dict) print("专家模型加载成功") except Exception as e: print("加载失败,请检查文件路径:", e) exit() # 测试专家模型得分 expert_score = test_expert(env, actor) print(f"专家模型平均回报: {expert_score:.2f}") # 如果专家得分太低,建议重新训练专家(见后文) if expert_score < 400: print("警告:专家模型得分偏低,行为克隆效果可能不佳。建议先重新训练专家。") # 采样专家数据 n_episode = 100 expert_s, expert_a = sample_expert_data(n_episode, env, actor) print("专家数据量:", expert_s.shape[0]) # 训练行为克隆 lr = 1e-4 bc_agent = BehaviorClone(state_dim, hidden_dim, action_dim, lr) n_iterations = 3000 batch_size = 64 test_returns = [] with tqdm(total=n_iterations, desc="训练进度") as pbar: for i in range(n_iterations): sample_indices = np.random.randint(0, expert_s.shape[0], size=batch_size) bc_agent.learn(expert_s[sample_indices], expert_a[sample_indices]) if (i + 1) % 10 == 0: current_return = test_agent(bc_agent, env, 5, deterministic=True) test_returns.append(current_return) pbar.set_postfix({'return': '%.3f' % current_return}) pbar.update(1) # 绘制结果 plt.plot(range(len(test_returns)), test_returns) plt.xlabel('Iterations (x10)') plt.ylabel('Returns') plt.title('BC on {} (Expert Score: {:.1f})'.format(env_name, expert_score)) plt.show()
绘制行为克隆的奖励曲线
iteration_list = list(range(len(test_returns))) plt.plot(iteration_list, test_returns) plt.xlabel('Iterations') plt.ylabel('Returns') plt.title('BC on {}'.format(env_name)) plt.show()

http://www.cnnetsun.cn/news/1256312.html

相关文章:

  • Android tinyalsa深度解析之mixer_wait_event调用流程与实战(一百五十八)
  • 【工具开发自用】FVTracker基于Python的基金估值跟踪工具1.22更新发布
  • LLM Weekly(2026.2.23-2026.3.1)
  • Android功耗系列专题理论之十二:待机功耗问题关键分析点
  • 微信小程序开发项目搭建(保姆教程)
  • 基于上camera WIFI最小系统设计探索
  • 第一周单片机学习笔记及心得
  • 计算机毕业设计 java 新能源汽车运力管理系统 Java 智能新能源汽车运力管理平台 SpringBoot+MySQL 新能源汽车运力管理系统
  • Firefly ITX-RK3588 实战:MIPI CSI摄像头采集,FFmedia本地HDMI预览与GStreamer网络推流全链路解析
  • Linux Mint远程开发环境搭建:SSH配置与root权限管理避坑指南
  • Feign服务调用超时全解析:从Eureka注册中心到网络配置的深度排查
  • Linux网络性能实战:一键公网测速脚本在边缘设备与云服务器上的部署与应用
  • 5分钟搞定:用Docker快速部署OpenWRT镜像(附Zabbix监控配置)
  • 零基础学网络安全的难度如何?
  • LCD时序参数配置实战:从TFT-RGB接口到HSYNC/VSYNC信号调试技巧
  • Chatbot智能问诊系统架构设计与实现:从技术选型到生产环境部署
  • DeOldify开源模型对比分析:与其它图像上色项目的效果与技术差异
  • 【书生·浦语】internlm2-chat-1.8b入门指南:Ollama界面操作+提问技巧详解
  • Anything V5商业应用探索:电商配图、游戏立绘一键生成
  • 丹青幻境效果展示:水墨晕染、工笔细描、写意泼墨三种风格生成对比
  • Qwen2.5-VL-7B-Instruct从入门到精通:图文混合提问全流程演示
  • 手把手教你玩转NXP i.MX 8M Plus开发板:多媒体接口与工业通信全解析
  • Python FFmpeg 实战指南:从安装到视频处理
  • VMAF实战:从原理到调优,构建精准视频质量评估体系
  • WeKnora企业级部署:基于Docker Swarm的高可用架构
  • Vue组件间通信方式大全:从Props到Vuex的10种方法
  • 开源GEO系统源码获取方法详解,附完整下载与配置教程
  • PRIMARK突袭验厂如何应对
  • 从零到万亿:Kimi-K2的MuonClip优化器如何驯服MoE大模型训练
  • Qwen-Image-2512-SDNQ效果展示:广告创意AI生成作品集