目录
5.3 RL + IL 混合方法:利用示范数据加速 RL 训练并学习更自然的动作
5.3.2 代码实现(PyTorch + Stable-Baselines3)
5.4 敏捷技能学习:跑、跳、后空翻等高动态动作的建模与训练
5.5 实验:少量专家数据 → 行为克隆初始化 → 强化学习微调
5.3 RL + IL 混合方法:利用示范数据加速 RL 训练并学习更自然的动作
目标 – 在典型的连续控制环境(如 Humanoid、Walker2D 等)中,结合强化学习 (RL) 与行为克隆 (IL) 的思路,让智能体在少量示范数据下快速收敛,并获得更加平滑、符合物理约束的运动轨迹。
核心思想 – 先用演示数据做预训练(Behavior Cloning, BC),再通过带有“行为克隆奖励”或“KL 限制”的策略梯度方法继续优化,最终得到既高效又自然的控制策略。
5.3.1 设计思路
| 步骤 | 说明 |
|---|---|
| 1. 数据收集 | 使用运动捕捉、MotionBuilder 或者手工动画生成演示轨迹。每条轨迹为 state → action 对序列,长度可根据动作复杂度调整(如跑步 5s→250帧)。 |
| 2. BC 预训练 | 用交叉熵或均方误差 (MSE) 损失把策略网络参数初始化为能够模仿演示。 |
| 3. RL 细化 | 采用 PPO / SAC 等连续控制算法,加入 行为克隆奖励 或 KL 限制,保证在优化过程中不偏离演示轨迹太远。 |
| 4. 终端策略 | 在训练完成后,可进一步做微调或使用多任务学习(不同动作共享网络)。 |
为什么要这样做?
- RL 的探索往往导致极端、不可预期的姿态,尤其在高维关节空间。
- 直接从随机初始化开始收敛耗时长且不稳定。
- 演示数据提供了“先验”,使智能体在起始阶段就能产生符合物理与美学的动作,从而减少梯度噪声、加速学习。
5.3.2 代码实现(PyTorch + Stable-Baselines3)
下面给出完整可跑的示例,演示如何在 Humanoid-v4 环境中使用 PPO 与 Behavior Cloning 混合训练。我们将:
- 用 BC 学习一个初始策略。
- 在 PPO 的 loss 里加入 KL 惩罚(或行为克隆奖励)。
注意:为了演示,示例演示数据会用随机生成的“近似跑步”轨迹代替真实 mocap 数据;实际使用时请自行准备
demo_states.npy与demo_actions.npy。
# ====================== 1. 环境与依赖 ======================
import gym, numpy as np, torch, os, pathlib
from stable_baselines3 import PPO
from stable_baselines3.common.vec_env import DummyVecEnv, VecNormalize
from stable_baselines3.common.policies import ActorCriticPolicy
from stable_baselines3.common.callbacks import BaseCallback
# 让 SB3 能够使用自定义 loss(见后面)
class CustomActorCriticPolicy(ActorCriticPolicy):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# ====================== 2. 数据准备 ======================
def load_demo_data(path="demo"):
"""
加载演示数据。这里用随机生成的示例,实际请替换为 mocap 数据。
"""
demo_states = np.load(os.path.join(path, "demo_states.npy"))
demo_actions = np.load(os.path.join(path, "demo_actions.npy"))
return demo_states, demo_actions
# ====================== 3. BC 预训练 ======================
def bc_pretrain(policy, states, actions, lr=1e-4, epochs=5):
"""
用均方误差(MSE)做行为克隆。
- policy: SB3 的 ActorCriticPolicy 实例
- states, actions: numpy arrays shape (N, state_dim) / (N, act_dim)
"""
optimizer = torch.optim.Adam(policy.parameters(), lr=lr)
for epoch in range(epochs):
perm = np.random.permutation(len(states))
states_shuf = torch.tensor(states[perm], dtype=torch.float32).to(policy.device)
actions_shuf = torch.tensor(actions[perm], dtype=torch.float32).to(policy.device)
# 前向
with torch.no_grad():
mean, log_std = policy.forward(states_shuf)
std = (log_std.exp()).clamp(min=1e-3) # 防止除零
# 计算 MSE 损失
loss = ((mean - actions_shuf)**2).mean()
optimizer.zero_grad()
loss.backward()
optimizer.step()
if epoch % 1 == 0:
print(f"[BC] Epoch {epoch+1}/{epochs} | Loss: {loss.item():.4f}")
# ====================== 4. PPO + KL 限制 ======================
class KLLossCallback(BaseCallback):
"""
每一步计算 KL 损失并写回到 policy 的属性 `kl_loss`,让 PPO 使用。
"""
def __init__(self, kl_coeff=1e-3, verbose=0):
super().__init__(verbose)
self.kl_coeff = kl_coeff
def _on_step(self) -> bool:
# 计算当前策略与上一策略的 KL
pi_old_logp = self.model.policy.log_prob(self.model._last_obs)
pi_new_logp = self.model.policy.log_prob(self.training_env.get_attr('observation'))
kl = (pi_old_logp - pi_new_logp).mean()
self.model.policy.kl_loss = kl * self.kl_coeff
return True
# ====================== 5. 主训练流程 ======================
def main():
env_id = "Humanoid-v4"
vec_env = DummyVecEnv([lambda: gym.make(env_id)])
vec_env = VecNormalize(vec_env, norm_obs=True, norm_reward=False)
# 1) 初始化策略网络
policy_kwargs = dict(activation_fn=torch.nn.Tanh,
net_arch=[dict(pi=[256, 128], vf=[256, 128])])
model = PPO(CustomActorCriticPolicy, vec_env,
learning_rate=2.5e-4,
n_steps=2048,
batch_size=64,
gamma=0.99,
gae_lambda=0.95,
clip_range=0.2,
policy_kwargs=policy_kwargs,
verbose=1)
# 2) 加载演示数据并预训练
demo_states, demo_actions = load_demo_data()
bc_pretrain(model.policy, demo_states, demo_actions, lr=3e-4, epochs=10)
# 3) 开始 PPO + KL 限制训练
kl_cb = KLLossCallback(kl_coeff=1e-3)
model.learn(total_timesteps=int(5e6), callback=[kl_cb])
# 4) 保存模型
os.makedirs("trained_models", exist_ok=True)
model.save("trained_models/ppo_humanoid_bc_kl")
if __name__ == "__main__":
main()
关键实现细节
| 模块 | 说明 |
|---|---|
| BC 损失 | 采用 MSE 而不是交叉熵,原因是动作连续。可根据需要改为 GaussianPolicy 的负对数似然 (NLL)。 |
| KL 限制 | 在 KLLossCallback 中,每一步计算旧策略与当前策略的 KL,然后乘以系数写回到 policy 里;PPO 在内部会把这个值加到 loss。这样可以在探索时保持对演示轨迹的“温和”偏离。 |
| VecNormalize | 对观测进行归一化,提升训练稳定性。注意 RL 与 IL 的数据分布差异,最好先用 BC 训练后再开启 normalize。 |
| 网络结构 | net_arch=[dict(pi=[256,128], vf=[256,128])] 是典型的两层全连接。可以进一步改为 LSTM 或 Transformer,以捕捉时序依赖。 |
常见挑战与解决方案
-
演示数据质量不佳
- 症状:BC 训练后策略抖动、收敛慢。
- 对策:使用数据增强(噪声注入、时间拉伸)或在 BC 损失中加入 KL 正则化,限制离演示的距离。
-
KL 过大导致探索受限
- 症状:收敛速度慢,无法超越演示表现。
- 对策:动态调节
kl_coeff(如根据当前 KL 与目标值做比例),或采用 Adaptive KL 机制。
-
奖励与行为克隆冲突
- 症状:在某些状态下,RL 想要做非演示动作导致奖励下降。
- 对策:把行为克隆奖励设为负的 KL 或加权到总 reward,或使用 Replay Buffer 将 BC 数据混入 RL 经验。
-
高维观测导致过拟合
- 症状:BC 在训练集上表现好,但在环境中泛化差。
- 对策:加入 Dropout、L2 正则,或采用 Curriculum Learning 从简单到复杂的演示。
5.4 敏捷技能学习:跑、跳、后空翻等高动态动作的建模与训练
在真实机器人或游戏角色中,高动态动作(如跑步、跳跃、后空翻)需要时序一致性、冲击容忍 与 能量效率 的平衡。以下从建模、奖励设计、网络结构与训练策略四个维度给出实践指南。

最低0.47元/天 解锁文章
122

被折叠的 条评论
为什么被折叠?



