340 lines
9.9 KiB
Markdown
340 lines
9.9 KiB
Markdown
# Mario RL MVP (macOS Apple Silicon)
|
||
|
||
最小可运行工程:使用像素输入 + 传统 CNN policy(`stable-baselines3` PPO)训练 `gym-super-mario-bros / nes-py` 智能体,不使用大语言模型。
|
||
|
||
## 1. 项目结构
|
||
|
||
```text
|
||
mario-rl-mvp/
|
||
src/
|
||
env.py
|
||
train_ppo.py
|
||
eval.py
|
||
record_video.py
|
||
utils.py
|
||
artifacts/
|
||
models/
|
||
videos/
|
||
logs/
|
||
requirements.txt
|
||
README.md
|
||
```
|
||
|
||
## 2. 环境准备(macOS / Apple Silicon)
|
||
|
||
建议 Python 3.9+(本机默认 `python3` 即可)。
|
||
|
||
```bash
|
||
cd /Users/roog/Work/FNT/SpMr/mario-rl-mvp
|
||
python3 -m venv .venv
|
||
source .venv/bin/activate
|
||
python -m pip install --upgrade pip setuptools wheel
|
||
pip install -r requirements.txt
|
||
```
|
||
|
||
可选系统依赖(用于 ffmpeg 转码与潜在 SDL 兼容):
|
||
|
||
```bash
|
||
brew install ffmpeg sdl2
|
||
```
|
||
|
||
## 3. 一条命令开始训练
|
||
|
||
默认 CPU 训练(如果检测到可用且稳定的 MPS,会自动尝试启用,否则自动回退 CPU):
|
||
|
||
```bash
|
||
python -m src.train_ppo
|
||
```
|
||
|
||
常用覆盖参数:
|
||
|
||
```bash
|
||
python -m src.train_ppo \
|
||
--total-timesteps 1000000 \
|
||
--n-envs 4 \
|
||
--save-freq 50000 \
|
||
--env-id SuperMarioBros-1-1-v0 \
|
||
--movement right_only \
|
||
--seed 42
|
||
```
|
||
|
||
从已有 checkpoint 初始化后继续训练(可同时改超参数,如 `--ent-coef`):
|
||
|
||
```bash
|
||
python -m src.train_ppo \
|
||
--init-model-path artifacts/models/latest_model.zip \
|
||
--total-timesteps 500000 \
|
||
--ent-coef 0.02 \
|
||
--learning-rate 1e-4
|
||
```
|
||
|
||
如果切换了动作空间(例如 checkpoint 是 `right_only`,当前想用 `simple`),可用部分加载:
|
||
|
||
```bash
|
||
python -m src.train_ppo \
|
||
--init-model-path artifacts/models/latest_model.zip \
|
||
--allow-partial-init \
|
||
--movement simple \
|
||
--total-timesteps 300000
|
||
```
|
||
|
||
### 3.1 从已有模型继续训练(`--init-model-path`)
|
||
|
||
- 用途:加载已有 `.zip` 权重后继续训练,适合“不中断实验目标但调整探索参数”。
|
||
- 常见场景:当前策略陷入局部最优(例如 `approx_kl` 和 `policy_gradient_loss` 长期接近 0),希望从已有模型继续探索。
|
||
- 注意:这不是“热更新”,仍然需要停止当前训练进程后用新命令重启。
|
||
|
||
```bash
|
||
python -m src.train_ppo \
|
||
--init-model-path artifacts/models/latest_model.zip \
|
||
--ent-coef 0.02 \
|
||
--learning-rate 1e-4 \
|
||
--total-timesteps 300000
|
||
```
|
||
|
||
训练输出:
|
||
- stdout:PPO 训练日志
|
||
- TensorBoard:`artifacts/logs/<run_name>/tb/`
|
||
- checkpoint:`artifacts/models/<run_name>/ppo_mario_ckpt_*.zip`
|
||
- final model:`artifacts/models/<run_name>/final_model.zip`
|
||
- latest 指针:`artifacts/models/latest_model.zip` + `LATEST_MODEL.txt`
|
||
|
||
启动 TensorBoard:
|
||
|
||
```bash
|
||
tensorboard --logdir artifacts/logs --port 6006
|
||
```
|
||
|
||
## 3.2 训练日志字段速查(PPO)
|
||
|
||
训练时你会看到类似:
|
||
|
||
```text
|
||
| rollout/ep_len_mean | rollout/ep_rew_mean | ... |
|
||
| train/approx_kl | train/entropy_loss | ... |
|
||
```
|
||
|
||
下面是常用字段的含义(对应你贴出来那组):
|
||
|
||
- `rollout/ep_len_mean`:最近一批 episode 的平均步数。越大不一定越好,要结合 reward 一起看。
|
||
- `rollout/ep_rew_mean`:最近一批 episode 的平均回报。通常越高越好。
|
||
- `time/fps`:训练吞吐(每秒环境步数),只代表速度,不代表策略质量。
|
||
- `time/iterations`:第几次 rollout + update 循环。
|
||
- `time/time_elapsed`:训练已运行的秒数。
|
||
- `time/total_timesteps`:累计环境交互步数(达到你设定的 `--total-timesteps` 会停止)。
|
||
- `train/approx_kl`:新旧策略差异大小。太大说明更新过猛;接近 0 说明几乎没在更新。极小负数通常是数值误差,可当作 0。
|
||
- `train/clip_fraction`:有多少样本触发 PPO clipping。长期为 0 且 KL 也接近 0,常见于“策略基本不再更新”。
|
||
- `train/clip_range`:PPO 的 clipping 阈值(默认 0.2)。
|
||
- `train/entropy_loss`:探索强度指标。绝对值越接近 0,策略越确定、探索越少。
|
||
- `train/explained_variance`:价值网络对回报的解释度,越接近 1 越好,接近 0 说明 value 还不稳。
|
||
- `train/learning_rate`:优化器步长(参数更新幅度),不是硬件速度。
|
||
- `train/loss`:总损失(由多个部分组成),主要看趋势,不单看绝对值。
|
||
- `train/policy_gradient_loss`:策略网络的更新信号。长期接近 0 可能表示 actor 更新很弱。
|
||
- `train/value_loss`:价值网络误差。过大通常代表 critic 拟合还不稳定。
|
||
|
||
### 快速判断(实用版)
|
||
|
||
- `ep_rew_mean` / `avg_max_x_pos` 持续上升:一般在变好。
|
||
- `approx_kl≈0` + `clip_fraction=0` + `policy_gradient_loss≈0`:大概率卡住(更新几乎停了)。
|
||
- `entropy_loss` 绝对值太小且长期不变:探索不足,可尝试提高 `--ent-coef`。
|
||
|
||
## 4. 评估模型
|
||
|
||
加载最新模型,跑 N 个 episode,输出平均指标:
|
||
|
||
```bash
|
||
python -m src.eval --episodes 5 --stochastic
|
||
```
|
||
|
||
可指定模型:
|
||
|
||
```bash
|
||
python -m src.eval --model-path artifacts/models/latest_model.zip --episodes 10 --stochastic
|
||
```
|
||
|
||
注意:`eval.py` 默认 `--movement auto`,会按模型动作维度自动匹配 `right_only/simple`,避免动作空间不一致导致 `KeyError`。
|
||
|
||
输出指标包括:
|
||
- `avg_reward`
|
||
- `avg_max_x_pos`
|
||
- `clear_rate`(`flag_get=True` 的比例)
|
||
|
||
|
||
查看步数
|
||
|
||
```php
|
||
unzip -p artifacts/models/latest_model.zip data | rg '"num_timesteps"|"_total_timesteps"|"_tensorboard_log"'
|
||
```
|
||
|
||
num_timesteps = 151552
|
||
_total_timesteps = 150000
|
||
|
||
## 5. 录制回放视频(无窗口/headless)
|
||
|
||
默认录制约 10 秒 mp4 到 `artifacts/videos/`:
|
||
|
||
```bash
|
||
python -m src.record_video --duration-sec 10 --fps 30 --stochastic
|
||
```
|
||
|
||
可指定输出路径:
|
||
|
||
```bash
|
||
python -m src.record_video --output artifacts/videos/demo.mp4 --stochastic --duration-sec 10
|
||
```
|
||
|
||
注意:`record_video.py` 默认 `--movement auto`,会按模型自动匹配动作空间。
|
||
|
||
实现方式:
|
||
- 使用 `render_mode=rgb_array`,无需打开窗口
|
||
- 默认通过 `imageio + ffmpeg` 输出 mp4
|
||
- 若 mp4 写入失败,会自动降级保存帧序列(PNG),并打印 ffmpeg 转码命令
|
||
|
||
## 6. 动作空间选择说明
|
||
|
||
默认 `RIGHT_ONLY`,原因:
|
||
- 动作更少,探索空间更小,MVP 更快收敛到“向右推进”策略
|
||
- 适合先验证训练闭环
|
||
|
||
可切到 `SIMPLE_MOVEMENT`(动作更丰富):
|
||
|
||
```bash
|
||
python -m src.train_ppo --movement simple
|
||
```
|
||
|
||
## 7. 预处理与奖励
|
||
|
||
默认预处理链路:
|
||
- 跳帧:`frame_skip=4`
|
||
- 灰度:`RGB -> Gray`
|
||
- 缩放:`84x84`
|
||
- 帧堆叠:`4`
|
||
- 通道布局:`CHW`(兼容 `CnnPolicy`)
|
||
|
||
奖励:
|
||
- `--reward-mode raw`:原始奖励(默认)
|
||
- `--reward-mode clip`:裁剪奖励 `sign(reward)`(等价于旧参数 `--clip-reward`)
|
||
- `--reward-mode progress`:奖励塑形模式,额外包含:
|
||
- 前进增益奖励(`--progress-scale`)
|
||
- 死亡惩罚(`--death-penalty`)
|
||
- 通关奖励(`--flag-bonus`)
|
||
- 原地卡住惩罚(`--stall-penalty` + `--stall-steps`)
|
||
- 后退惩罚(`--backward-penalty-scale`)
|
||
|
||
使用裁剪奖励:
|
||
|
||
```bash
|
||
python -m src.train_ppo --reward-mode clip
|
||
```
|
||
|
||
针对“卡在固定位置(如 x=314 撞蘑菇)”的推荐续训命令:
|
||
|
||
```bash
|
||
python -m src.train_ppo \
|
||
--init-model-path artifacts/models/latest_model.zip \
|
||
--allow-partial-init \
|
||
--reward-mode progress \
|
||
--movement simple \
|
||
--ent-coef 0.04 \
|
||
--learning-rate 1e-4 \
|
||
--n-steps 512 \
|
||
--gamma 0.995 \
|
||
--death-penalty -120 \
|
||
--stall-penalty 0.2 \
|
||
--stall-steps 20 \
|
||
--backward-penalty-scale 0.03 \
|
||
--milestone-bonus 2.0 \
|
||
--no-progress-terminate-steps 80 \
|
||
--no-progress-terminate-penalty 30 \
|
||
--total-timesteps 150000
|
||
```
|
||
|
||
## 8. 常见问题排查
|
||
|
||
### 8.1 `pip install` 失败(nes-py/gym-super-mario-bros 编译问题)
|
||
|
||
先安装工具链并重试:
|
||
|
||
```bash
|
||
xcode-select --install
|
||
brew install cmake swig sdl2
|
||
pip install --upgrade pip setuptools wheel
|
||
pip install -r requirements.txt
|
||
```
|
||
|
||
### 8.2 MPS 不稳定或报错
|
||
|
||
强制 CPU:
|
||
|
||
```bash
|
||
python -m src.train_ppo --device cpu
|
||
```
|
||
|
||
说明:脚本会先做一次 MPS 张量 sanity check,失败自动回退 CPU。
|
||
|
||
### 8.3 视频写入失败(ffmpeg/codec)
|
||
|
||
1) 安装系统 ffmpeg:
|
||
```bash
|
||
brew install ffmpeg
|
||
```
|
||
|
||
2) 已降级保存帧序列时,手动转码:
|
||
```bash
|
||
ffmpeg -framerate 30 -i artifacts/videos/<name>_frames/frame_%06d.png -c:v libx264 -pix_fmt yuv420p artifacts/videos/<name>.mp4
|
||
```
|
||
|
||
### 8.4 图形窗口相关报错
|
||
|
||
本工程默认 `rgb_array` 录制,不依赖 GUI 窗口。
|
||
若仍遇到 SDL 问题,可显式设置:
|
||
|
||
```bash
|
||
export SDL_VIDEODRIVER=dummy
|
||
python -m src.record_video --duration-sec 10
|
||
```
|
||
|
||
### 8.5 `size mismatch for action_net`(加载旧模型时报错)
|
||
|
||
典型原因:旧 checkpoint 的动作空间与当前配置不同(如 `right_only`=5 动作,`simple`=7 动作)。
|
||
|
||
可选修复:
|
||
|
||
1) 保持和 checkpoint 一致的动作空间:
|
||
```bash
|
||
python -m src.train_ppo --init-model-path /Users/roog/Work/FNT/SpMr/mario-rl-mvp/artifacts/models/ppo_SuperMarioBros-1-1-v0_20260212_164717/ppo_mario_ckpt_150000_steps.zip --movement right_only
|
||
```
|
||
|
||
2) 若你确实要切动作空间,用部分初始化(跳过不兼容动作头):
|
||
```bash
|
||
python -m src.train_ppo --init-model-path artifacts/models/latest_model.zip --movement simple --allow-partial-init
|
||
```
|
||
|
||
3) 或者直接不加载旧模型,从头训练新动作空间。
|
||
|
||
## 9. 最小 smoke test(按顺序执行)
|
||
|
||
```bash
|
||
cd /Users/roog/Work/FNT/SpMr/mario-rl-mvp
|
||
source .venv/bin/activate
|
||
|
||
# 1) 训练 1e4 steps,并至少写出 checkpoint + final model
|
||
python -m src.train_ppo \
|
||
--total-timesteps 10000 \
|
||
--save-freq 2000 \
|
||
--n-envs 1 \
|
||
--device cpu \
|
||
--movement right_only
|
||
|
||
# 2) 快速评估
|
||
python -m src.eval --episodes 2 --max-steps 2000
|
||
|
||
# 3) 录制 10 秒视频
|
||
python -m src.record_video --duration-sec 10 --fps 30
|
||
```
|
||
|
||
验收标准:
|
||
- `artifacts/models/` 下有 `.zip` 模型
|
||
- `artifacts/logs/` 下有 TensorBoard event 文件
|
||
- `artifacts/videos/` 下有 `.mp4`(或失败时有 `_frames/` 帧序列)
|