# 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 plot_model_max_x_trend.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 ``` ## 2.1 环境准备(WSL / Ubuntu) 如果系统 Python 缺少 `venv/pip`,推荐直接用 `uv` 创建环境并安装依赖: ```bash cd /home/roog/super-mario/mario-rl-mvp uv venv .venv -p /usr/bin/python3.10 uv pip install --python .venv/bin/python -r requirements.txt ``` 如果你更倾向用系统 `venv`,先安装: ```bash sudo apt-get update sudo apt-get install -y python3.10-venv python3-pip ``` ### RTX 50 系列(如 RTX 5080)GPU 说明 如果你看到类似: ```text ... CUDA capability sm_120 is not compatible with the current PyTorch installation ... ``` 说明当前 torch wheel 不包含 `sm_120` 内核。可直接升级到 `cu128` nightly: ```bash cd /home/roog/super-mario/mario-rl-mvp uv pip install --python .venv/bin/python --upgrade --pre torch --index-url https://download.pytorch.org/whl/nightly/cu128 ``` 验证 GPU: ```bash .venv/bin/python -c "import torch; print(torch.__version__); print(torch.version.cuda); print(torch.cuda.is_available()); print(torch.cuda.get_device_name(0) if torch.cuda.is_available() else 'N/A'); print(torch.cuda.get_device_capability(0) if torch.cuda.is_available() else 'N/A')" ``` 可选系统依赖(用于 ffmpeg 转码与潜在 SDL 兼容): ```bash brew install ffmpeg sdl2 ``` ## 3. 一条命令开始训练 默认 `--device auto` 训练(优先 CUDA,其次 MPS,最后 CPU): ```bash python -m src.train_ppo ``` 显式指定 `--device cuda` 或 `--device mps` 时,如果该设备不可用,脚本会默认报错(避免静默回退到 CPU)。 若你明确接受回退,可加: ```bash python -m src.train_ppo --device cuda --allow-device-fallback ``` 常用覆盖参数: ```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 ``` 我目前的参数 ``` python -m src.train_ppo \ --init-model-path artifacts/models/latest_model.zip \ --n-envs 16 \ --allow-partial-init \ --reward-mode progress \ --movement simple \ --ent-coef 0.001 \ --learning-rate 2e-5 \ --n-steps 2048 \ --gamma 0.99 \ --death-penalty -50 \ --stall-penalty 0.05 \ --stall-steps 40 \ --backward-penalty-scale 0.01 \ --milestone-bonus 2.0 \ --no-progress-terminate-steps 300 \ --no-progress-terminate-penalty 10 \ --time-penalty -0.01 \ --total-timesteps 1200000 ``` ### 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//tb/` - checkpoint:`artifacts/models//ppo_mario_ckpt_*.zip` - final model:`artifacts/models//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 \ --model-path artifacts/models/latest_model.zip \ --episodes 20 \ --movement simple \ --reward-mode progress \ --no-progress-terminate-steps 300 \ --no-progress-terminate-penalty 10 \ --death-penalty -50 \ --stall-penalty 0.05 \ --stall-steps 40 \ --time-penalty -0.01 \ --epsilon 0.08 ``` 可指定模型: ```bash python -m src.eval \ --model-path artifacts/models/latest_model.zip \ --episodes 20 \ --movement simple \ --reward-mode progress \ --no-progress-terminate-steps 300 \ --no-progress-terminate-penalty 10 \ --death-penalty -50 \ --stall-penalty 0.05 \ --stall-steps 40 \ --time-penalty -0.01 \ --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 \ --model-path artifacts/models/latest_model.zip \ --movement simple \ --reward-mode progress \ --no-progress-terminate-steps 300 \ --no-progress-terminate-penalty 10 \ --death-penalty -50 \ --stall-penalty 0.05 \ --stall-steps 40 \ --time-penalty -0.01 \ --epsilon 0.08 \ --duration-sec 30 ``` 或者稳定版本 ```bash python -m src.record_video \ --model-path artifacts/models/latest_model.zip \ --movement simple \ --reward-mode progress \ --no-progress-terminate-steps 300 \ --no-progress-terminate-penalty 10 \ --death-penalty -50 \ --stall-penalty 0.05 \ --stall-steps 40 \ --time-penalty -0.01 \ --epsilon 0.08 \ --epsilon-random-mode uniform \ --max-steps 6000 ``` 可选: ```bash --output artifacts/videos/mario_eps008.mp4 ``` 注意:`record_video.py` 默认 `--movement auto`,会按模型自动匹配动作空间。 实现方式: - 使用 `render_mode=rgb_array`,无需打开窗口 - 默认通过 `imageio + ffmpeg` 输出 mp4 - 若 mp4 写入失败,会自动降级保存帧序列(PNG),并打印 ffmpeg 转码命令 ## 5.1 模型趋势可视化(HTML / Markdown) 用于可视化 `artifacts/models/` 里的模型在训练过程中的关键指标趋势,输出中文 HTML 或 Markdown 报告。 默认命令: ```bash python -m src.plot_model_max_x_trend ``` 默认输出: - `artifacts/reports/model_max_x_trend.html` 输出 Markdown 报告: ```bash python -m src.plot_model_max_x_trend --format markdown ``` Markdown 默认输出: - `artifacts/reports/model_max_x_trend.md` 可选参数(自定义目录/输出): ```bash uv run python -m src.plot_model_max_x_trend \ --models-dir artifacts/models \ --logs-dir artifacts/logs \ --format markdown \ --output artifacts/reports/model_max_x_trend.md ``` 报告内容: - 主趋势:`max_x`(最大前进距离) - 多维趋势:平均回报、平均回合步数、通关率、无进展终止率、死亡终止率、超时终止率、硬卡死终止率 - 模型明细表:每个 checkpoint/final 模型对应的指标值、匹配步数、来源 TensorBoard tag - 术语解释:Run、Checkpoint、model_step、matched_step、TensorBoard Tag 等专有名词 ## 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 /home/roog/super-mario/mario-rl-mvp/artifacts/models/ppo_SuperMarioBros-1-1-v0_20260212_205220/ppo_mario_ckpt_100000_steps.zip \ --n-envs 16 \ --allow-partial-init \ --reward-mode progress \ --movement simple \ --ent-coef 0.01 \ --learning-rate 1e-4 \ --n-steps 1024 \ --gamma 0.99 \ --death-penalty -50 \ --stall-penalty 0.05 \ --stall-steps 40 \ --backward-penalty-scale 0.01 \ --milestone-bonus 2.0 \ --no-progress-terminate-steps 300 \ --no-progress-terminate-penalty 10 \ --total-timesteps 300000 ``` ## 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/_frames/frame_%06d.png -c:v libx264 -pix_fmt yuv420p artifacts/videos/.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) 或者直接不加载旧模型,从头训练新动作空间。 ### 8.6 `cudaGetDeviceCount ... Error 304`(WSL 下 CUDA 初始化失败) 如果训练启动时看到: ```text [device] cpu | CUDA unavailable, using CPU. [device_diag] ... torch.cuda.is_available()=False ... Error 304 ... ``` 说明不是 `device` 参数没传,而是 CUDA 运行时在当前环境初始化失败。 先做两步确认: ```bash nvidia-smi --query-gpu=name,driver_version,compute_cap --format=csv,noheader .venv/bin/python -c "import torch; print(torch.__version__); print(torch.version.cuda); print(torch.cuda.is_available())" ``` 常见原因是 WSL GPU 栈/驱动状态异常,而不是 PPO 代码本身。若你是临时跑通实验,可先显式 CPU: ```bash python -m src.train_ppo --device cpu ``` ## 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/` 帧序列)