168 lines
5.9 KiB
Python
168 lines
5.9 KiB
Python
import gymnasium as gym
|
|
import torch
|
|
import os
|
|
import pickle
|
|
from agents.dpac import DPACAgent
|
|
from agents.ddpg import DDPGAgent
|
|
from utils import plot_comparison
|
|
|
|
|
|
RESULTS_DIR = 'training_results'
|
|
|
|
|
|
# ---------- 路径工具 ----------
|
|
def _path(agent_class, env_name, num_episodes, suffix):
|
|
os.makedirs(RESULTS_DIR, exist_ok=True)
|
|
return os.path.join(RESULTS_DIR, f'{agent_class.__name__}_{env_name}_{num_episodes}{suffix}')
|
|
|
|
|
|
def result_path(agent_class, env_name, num_episodes):
|
|
return _path(agent_class, env_name, num_episodes, '.pkl')
|
|
|
|
|
|
def model_path(agent_class, env_name, num_episodes):
|
|
return _path(agent_class, env_name, num_episodes, '_model.pt')
|
|
|
|
|
|
# ---------- 训练 & 保存 ----------
|
|
def train(env_name, agent_class, device, num_episodes):
|
|
rp = result_path(agent_class, env_name, num_episodes)
|
|
mp = model_path(agent_class, env_name, num_episodes)
|
|
|
|
if os.path.exists(rp) and os.path.exists(mp):
|
|
with open(rp, 'rb') as f:
|
|
rewards = pickle.load(f)
|
|
print(f"[{agent_class.__name__}] 已加载训练结果: {rp}")
|
|
return rewards
|
|
|
|
print(f"[{agent_class.__name__}] 开始训练 ...")
|
|
if agent_class.__name__ == 'DDPGAgent':
|
|
rewards, agent = _train_ddpg(env_name, agent_class, device, num_episodes)
|
|
else:
|
|
rewards, agent = _train_dpac(env_name, agent_class, device, num_episodes)
|
|
|
|
with open(rp, 'wb') as f:
|
|
pickle.dump(rewards, f)
|
|
agent.save(mp)
|
|
print(f"[{agent_class.__name__}] 训练完成 | 曲线: {rp} | 模型: {mp}")
|
|
return rewards
|
|
|
|
|
|
def _train_dpac(env_name, agent_class, device, num_episodes):
|
|
env = gym.make(env_name)
|
|
state_dim = env.observation_space.shape[0] # type: ignore
|
|
action_dim = env.action_space.shape[0] # type: ignore
|
|
max_action = float(env.action_space.high[0]) # type: ignore
|
|
|
|
agent = agent_class(state_dim, action_dim, max_action, device)
|
|
rewards = []
|
|
|
|
for ep in range(num_episodes):
|
|
state, _ = env.reset()
|
|
episode_reward = 0
|
|
while True:
|
|
action = agent.select_action(state)
|
|
ns, reward, terminated, truncated, _ = env.step(action)
|
|
agent.update(state, action, reward, ns, None, terminated or truncated)
|
|
state = ns
|
|
episode_reward += reward # type: ignore
|
|
if terminated or truncated:
|
|
break
|
|
rewards.append(episode_reward)
|
|
if (ep + 1) % 50 == 0:
|
|
avg = sum(rewards[-50:]) / 50
|
|
print(f" 回合 {ep+1}/{num_episodes}, 近50回合均分: {avg:.2f}")
|
|
|
|
env.close()
|
|
return rewards, agent
|
|
|
|
|
|
def _train_ddpg(env_name, agent_class, device, num_episodes, batch_size=64, start_steps=2000):
|
|
env = gym.make(env_name)
|
|
state_dim = env.observation_space.shape[0] # type: ignore
|
|
action_dim = env.action_space.shape[0] # type: ignore
|
|
max_action = float(env.action_space.high[0]) # type: ignore
|
|
|
|
agent = agent_class(state_dim, action_dim, max_action, device)
|
|
rewards = []
|
|
total_steps = 0
|
|
|
|
for ep in range(num_episodes):
|
|
state, _ = env.reset()
|
|
episode_reward = 0
|
|
while True:
|
|
action = agent.select_action(state)
|
|
ns, reward, terminated, truncated, _ = env.step(action)
|
|
agent.replay_buffer.add(state, action, reward, ns, terminated or truncated)
|
|
total_steps += 1
|
|
state = ns
|
|
episode_reward += reward # type: ignore
|
|
if total_steps > start_steps:
|
|
agent.update(batch_size)
|
|
if terminated or truncated:
|
|
break
|
|
rewards.append(episode_reward)
|
|
if (ep + 1) % 50 == 0:
|
|
avg = sum(rewards[-50:]) / 50
|
|
print(f" 回合 {ep+1}/{num_episodes}, 近50回合均分: {avg:.2f}")
|
|
|
|
env.close()
|
|
return rewards, agent
|
|
|
|
|
|
# ---------- 视频演示 ----------
|
|
def demo(env_name, agent_class, device, num_demo=5, num_episodes=600):
|
|
mp = model_path(agent_class, env_name, num_episodes)
|
|
if not os.path.exists(mp):
|
|
print(f"[{agent_class.__name__}] 未找到模型权重 {mp},跳过演示。")
|
|
return
|
|
|
|
video_dir = os.path.join(RESULTS_DIR, 'videos')
|
|
os.makedirs(video_dir, exist_ok=True)
|
|
|
|
base_env = gym.make(env_name, render_mode='rgb_array')
|
|
env = gym.wrappers.RecordVideo(base_env, video_dir, name_prefix=f'{agent_class.__name__}_demo')
|
|
state_dim = env.env.observation_space.shape[0] # type: ignore
|
|
action_dim = env.env.action_space.shape[0] # type: ignore
|
|
max_action = float(env.env.action_space.high[0]) # type: ignore
|
|
|
|
agent = agent_class(state_dim, action_dim, max_action, device)
|
|
agent.load(mp)
|
|
print(f"[{agent_class.__name__}] 加载模型: {mp},录制 {num_demo} 局演示 ...")
|
|
|
|
for ep in range(num_demo):
|
|
state, _ = env.reset()
|
|
total_reward = 0.0
|
|
while True:
|
|
action = agent.select_action(state)
|
|
state, reward, terminated, truncated, _ = env.step(action)
|
|
total_reward += float(reward)
|
|
if terminated or truncated:
|
|
break
|
|
print(f" 回合 {ep+1}/{num_demo}, 总奖励: {total_reward:.2f}")
|
|
|
|
env.close()
|
|
base_env.close()
|
|
print(f"[{agent_class.__name__}] 视频已保存至: {video_dir}")
|
|
|
|
|
|
# ---------- 主程序 ----------
|
|
if __name__ == "__main__":
|
|
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
|
print(f"计算设备: {device}\n")
|
|
|
|
ENV_NAME = 'Pendulum-v1'
|
|
EPISODES = 600
|
|
|
|
dpac_rewards = train(ENV_NAME, DPACAgent, device, EPISODES)
|
|
ddpg_rewards = train(ENV_NAME, DDPGAgent, device, EPISODES)
|
|
|
|
results = {
|
|
'DPAC': dpac_rewards,
|
|
'DDPG': ddpg_rewards,
|
|
}
|
|
plot_comparison(results, window=50, save_path='compare_dpac_vs_ddpg.png')
|
|
|
|
demo(ENV_NAME, DPACAgent, device)
|
|
demo(ENV_NAME, DDPGAgent, device)
|