Download code/build_hub_package.py from lin267/value-based-qlearning: direct link, hf CLI and curl.
- Browser
- Download file 13.1 kB
-
https://huggingface.co/lin267/value-based-qlearning/resolve/main/code/build_hub_package.py
- Command line
-
hf download hf://lin267/value-based-qlearning/code/build_hub_package.py
-
curl -L -o build_hub_package.py https://huggingface.co/lin267/value-based-qlearning/resolve/main/code/build_hub_package.py
13.1 kB
| # -*- coding: utf-8 -*- | |
| """ | |
| 把训练好的智能体 + 代码 + 视频 + 评估分数打包成"可以直接上传 Hub"的文件夹。 | |
| ================================================================ | |
| 对应 PPT 第 28 页学习目标第 4 条: | |
| "能够将训练好的智能体和代码上传到 Hub,并附上一个漂亮的视频回放和评估分数" | |
| 产物 hub_package/ 结构: | |
| README.md 自动填好分数 + 内嵌视频的模型卡 | |
| metrics.json 三/四个智能体的评估分数汇总 | |
| requirements.txt | |
| models/qtable_*.pkl 训练出的模型文件(Q 表 + 超参 + 指标) | |
| videos/replay_*.mp4 回放视频(含总回放 replay_value_based_all.mp4) | |
| eval_results/eval_*.json 评估分数(逐局明细) | |
| logs/<run>/train_console.log 训练日志 | |
| logs/<run>/train_curve.png 训练曲线 | |
| assets/qtable_policy_*.png 学到的策略可视化 | |
| code/ 训练 + 评估 + 可视化 + 上传脚本(可复现) | |
| run_all.ps1 一键复现脚本 | |
| 用法: | |
| D:\\study\\robot\\pyenv\\DRL\\python.exe build_hub_package.py | |
| D:\\study\\robot\\pyenv\\DRL\\python.exe build_hub_package.py --repo-id 你的用户名/value-based-qlearning | |
| """ | |
| import argparse | |
| import json | |
| import os | |
| import shutil | |
| from datetime import datetime | |
| BASE_DIR = os.path.dirname(os.path.abspath(__file__)) | |
| # 要打包的智能体(按 PPT 顺序) | |
| AGENTS = [ | |
| dict(run="frozenlake4x4", title="FrozenLake-v1 · 4x4 不滑", | |
| desc="PPT 第一部分主实验:确定性冰面,从零手写 Q 表,10,000 回合"), | |
| dict(run="frozenlake4x4_slippery", title="FrozenLake-v1 · 4x4 滑冰版", | |
| desc="对照实验:只改 is_slippery=True,冰面会打滑,20,000 回合"), | |
| dict(run="frozenlake8x8", title="FrozenLake-v1 · 8x8", | |
| desc="进阶实验:地图 8x8 + 拉长探索期,60,000 回合"), | |
| dict(run="taxi", title="Taxi-v3", | |
| desc="PPT 第二部分主实验:500 状态 × 6 动作,25,000 回合,官方 100 个评估种子"), | |
| ] | |
| CODE_FILES = [ | |
| "train_qlearning.py", | |
| "evaluate_and_record.py", | |
| "visualize_qtable.py", | |
| "make_combined_video.py", | |
| "build_hub_package.py", | |
| "upload_to_hub.py", | |
| "run_all.ps1", | |
| "01_实践3说明.md", | |
| ] | |
| REQUIREMENTS = """gymnasium>=1.0 | |
| numpy>=1.24 | |
| imageio>=2.31 | |
| imageio-ffmpeg>=0.4 | |
| Pillow>=9.0 | |
| matplotlib>=3.7 | |
| tqdm>=4.65 | |
| """ | |
| def read_json(path): | |
| if not os.path.exists(path): | |
| return None | |
| with open(path, encoding="utf-8") as f: | |
| return json.load(f) | |
| def build_readme(repo_id=None, scores=None, now=None): | |
| raw = f"https://huggingface.co/{repo_id}/resolve/main/" if repo_id else "" | |
| link = (lambda p: raw + p) if repo_id else (lambda p: p) | |
| lines = [] | |
| lines.append("# Value-based 实践 · Q-Learning(FrozenLake-v1 + Taxi-v3)") | |
| lines.append("") | |
| lines.append("> 《第3周-深度强化学习原理实践》PPT 第 27–40 页 value-based 实践产物:" | |
| "**从零手写 Q-Learning**(Q 表 + epsilon-greedy + Bellman 更新)," | |
| "训练好的智能体 + 训练代码 + 回放视频 + 评估分数。") | |
| lines.append("") | |
| lines.append("## 1. 评估分数") | |
| lines.append("") | |
| lines.append("| 智能体 | 环境 | 平均回报(100 局) | 成功率 | 平均步数 | 训练回合 |") | |
| lines.append("|---|---|---|---|---|---|") | |
| for agent in AGENTS: | |
| s = (scores or {}).get(agent["run"]) | |
| if s: | |
| lines.append( | |
| f"| {agent['title']} | `{s['env_id']}` | **{s['mean_reward']:.2f} ± " | |
| f"{s['std_reward']:.2f}** | {s['success_rate'] * 100:.0f}% | " | |
| f"{s['mean_steps']:.1f} | {s.get('n_training_episodes', '—')} |" | |
| ) | |
| else: | |
| lines.append(f"| {agent['title']} | — | 待运行 | — | — | — |") | |
| lines.append("") | |
| lines.append("> 评估方式:独立环境、贪心策略 `argmax_a Q(s,a)`、每局最多 99 步,跑 100 局;" | |
| "FrozenLake 的回报是「是否到达目标」(0/1),Taxi 的回报是" | |
| "「每步 −1,成功送达 +20」。") | |
| lines.append("") | |
| lines.append("## 2. 回放视频") | |
| lines.append("") | |
| if os.path.exists(os.path.join(BASE_DIR, "videos", "replay_value_based_all.mp4")): | |
| lines.append("**总回放(三个智能体合集)**") | |
| lines.append("") | |
| lines.append(f'<video controls width="100%" src="{link("videos/replay_value_based_all.mp4")}"></video>') | |
| lines.append("") | |
| for agent in AGENTS: | |
| video = f"videos/replay_{agent['run']}.mp4" | |
| if os.path.exists(os.path.join(BASE_DIR, video)): | |
| lines.append(f"**{agent['title']}**") | |
| lines.append("") | |
| lines.append(f'<video controls width="100%" src="{link(video)}"></video>') | |
| lines.append("") | |
| lines.append("## 3. 学到的策略(Q 表可视化)") | |
| lines.append("") | |
| for agent in AGENTS: | |
| fig = f"assets/qtable_policy_{agent['run']}.png" | |
| if os.path.exists(os.path.join(BASE_DIR, fig)): | |
| lines.append(f"![{agent['title']}]({link(fig)})") | |
| lines.append("") | |
| lines.append("## 4. 训练曲线") | |
| lines.append("") | |
| for agent in AGENTS: | |
| fig = f"logs/{agent['run']}/train_curve.png" | |
| if os.path.exists(os.path.join(BASE_DIR, fig)): | |
| lines.append(f"![{agent['title']} 训练曲线]({link(fig)})") | |
| lines.append("") | |
| lines.append("## 5. 怎么复现") | |
| lines.append("") | |
| lines.append("```powershell") | |
| lines.append("# 环境(本机已有):conda activate DRL") | |
| lines.append("conda activate DRL") | |
| lines.append("") | |
| lines.append("# 训练(每个实验 1~30 秒)") | |
| lines.append("python train_qlearning.py --env frozenlake4x4") | |
| lines.append("python train_qlearning.py --env frozenlake4x4_slippery") | |
| lines.append("python train_qlearning.py --env frozenlake8x8") | |
| lines.append("python train_qlearning.py --env taxi") | |
| lines.append("") | |
| lines.append("# 评估 + 录视频(生成 eval_results/*.json 和 videos/*.mp4)") | |
| lines.append("python evaluate_and_record.py --model models/qtable_frozenlake4x4.pkl") | |
| lines.append("python evaluate_and_record.py --model models/qtable_frozenlake4x4_slippery.pkl --video-episodes 5 --slowmo 4") | |
| lines.append("python evaluate_and_record.py --model models/qtable_frozenlake8x8.pkl") | |
| lines.append("python evaluate_and_record.py --model models/qtable_taxi.pkl") | |
| lines.append("") | |
| lines.append("# Q 表可视化 + 总回放 + 打包") | |
| lines.append("python visualize_qtable.py --all") | |
| lines.append("python make_combined_video.py") | |
| lines.append("python build_hub_package.py") | |
| lines.append("```") | |
| lines.append("") | |
| lines.append("## 6. 文件说明") | |
| lines.append("") | |
| lines.append("- `models/qtable_*.pkl`:模型文件。pickle 格式,内含 Q 表、超参数、训练种子和评估指标;") | |
| lines.append(" `evaluate_and_record.py` 可以直接加载(不需要重新训练)。") | |
| lines.append("- `videos/*.mp4`:回放,每帧标注步数/动作/奖励/Q 值条形图,片头片尾是标题卡和分数卡。") | |
| lines.append("- `eval_results/*.json`:评估分数 + 逐局明细(起点种子、回报、步数、是否成功)。") | |
| lines.append("- `logs/<run>/train_console.log`:完整训练日志(环境自检、超参数、每 N 回合进度、每次评估)。") | |
| lines.append("- `code/`:全部代码(训练 / 评估录像 / 可视化 / 打包 / 上传 / 一键脚本)。") | |
| lines.append("") | |
| lines.append("## 7. 对应 PPT 页码") | |
| lines.append("") | |
| lines.append("| PPT 页 | 内容 | 对应实现 |") | |
| lines.append("|---|---|---|") | |
| lines.append("| 29 | Q 表 / Q 函数 / 最优策略 | `train_qlearning.py` 的 `initialize_q_table` / `greedy_policy` |") | |
| lines.append("| 31–32 | FrozenLake 观测/动作/奖励 | 训练日志里的「环境自检」 |") | |
| lines.append("| 33 | Q 表初始化 + 贪心策略 | `initialize_q_table` / `greedy_policy` |") | |
| lines.append("| 34 | epsilon-greedy + 超参数 | `epsilon_greedy_policy` + `EXPERIMENTS` 配置 |") | |
| lines.append("| 35 | 训练循环(Bellman 更新) | `train_qlearning.py::train` 里的 `Q[s][a] += lr*(...)` |") | |
| lines.append("| 36–37 | 训练、Q 表可视化、评估、保存模型 | 训练日志 / `visualize_qtable.py` / `evaluate_and_record.py` / `models/*.pkl` |") | |
| lines.append("| 38–40 | Taxi-v3(500 状态 × 6 动作) | `--env taxi`(含 PPT 的 100 个官方评估种子) |") | |
| lines.append("| 28 | 上传 Hub + 视频 + 分数 | `build_hub_package.py` + `upload_to_hub.py` + README |") | |
| lines.append("") | |
| lines.append(f"- 生成时间:{now:%Y-%m-%d %H:%M}") | |
| lines.append("") | |
| return "\n".join(lines) | |
| def copy_file(src, dst): | |
| os.makedirs(os.path.dirname(dst), exist_ok=True) | |
| shutil.copy2(src, dst) | |
| def main(): | |
| parser = argparse.ArgumentParser(description="打包成可上传 Hub 的文件夹") | |
| parser.add_argument("--repo-id", type=str, default=None, | |
| help="你的 HF 用户名/仓库名,例如 zhangsan/value-based-qlearning") | |
| parser.add_argument("--out", type=str, default=os.path.join(BASE_DIR, "hub_package")) | |
| args = parser.parse_args() | |
| out = args.out | |
| if os.path.exists(out): | |
| shutil.rmtree(out) | |
| os.makedirs(out) | |
| now = datetime.now() | |
| # 1) 读取所有评估分数 | |
| scores = {} | |
| for agent in AGENTS: | |
| run = agent["run"] | |
| s = read_json(os.path.join(BASE_DIR, "eval_results", f"eval_{run}.json")) | |
| if s is None: | |
| print(f"[警告] 缺少 eval_results/eval_{run}.json," | |
| f"请先跑 evaluate_and_record.py --model models/qtable_{run}.pkl") | |
| continue | |
| scores[run] = s | |
| metrics = {} | |
| for run, s in scores.items(): | |
| metrics[run] = {k: s[k] for k in | |
| ("env_id", "n_episodes", "mean_reward", "std_reward", | |
| "success_rate", "mean_steps", "qtable_shape", | |
| "qtable_nonzero", "evaluated_at")} | |
| # 2) 拷模型 | |
| for agent in AGENTS: | |
| src = os.path.join(BASE_DIR, "models", f"qtable_{agent['run']}.pkl") | |
| if os.path.exists(src): | |
| copy_file(src, os.path.join(out, "models", os.path.basename(src))) | |
| else: | |
| print(f"[警告] 缺少模型 {src}") | |
| # 3) 拷视频(含总回放) | |
| video_dir = os.path.join(BASE_DIR, "videos") | |
| if os.path.isdir(video_dir): | |
| for name in sorted(os.listdir(video_dir)): | |
| if name.endswith(".mp4"): | |
| copy_file(os.path.join(video_dir, name), | |
| os.path.join(out, "videos", name)) | |
| # 4) 拷评估 json | |
| for agent in AGENTS: | |
| src = os.path.join(BASE_DIR, "eval_results", f"eval_{agent['run']}.json") | |
| if os.path.exists(src): | |
| copy_file(src, os.path.join(out, "eval_results", os.path.basename(src))) | |
| # 5) 拷日志 + 训练曲线 | |
| for agent in AGENTS: | |
| run_dir = os.path.join(BASE_DIR, "logs", agent["run"]) | |
| for name in ("train_console.log", "train_curve.png", "progress.csv"): | |
| src = os.path.join(run_dir, name) | |
| if os.path.exists(src): | |
| copy_file(src, os.path.join(out, "logs", agent["run"], name)) | |
| # 6) 拷 Q 表可视化 | |
| for agent in AGENTS: | |
| src = os.path.join(BASE_DIR, "assets", f"qtable_policy_{agent['run']}.png") | |
| if os.path.exists(src): | |
| copy_file(src, os.path.join(out, "assets", os.path.basename(src))) | |
| # 7) 拷代码 | |
| for name in CODE_FILES: | |
| src = os.path.join(BASE_DIR, name) | |
| if os.path.exists(src): | |
| copy_file(src, os.path.join(out, "code", name)) | |
| # 8) 写 README / metrics / requirements | |
| with open(os.path.join(out, "README.md"), "w", encoding="utf-8") as f: | |
| f.write(build_readme(repo_id=args.repo_id, scores=scores, now=now)) | |
| with open(os.path.join(out, "metrics.json"), "w", encoding="utf-8") as f: | |
| json.dump(metrics, f, ensure_ascii=False, indent=2) | |
| with open(os.path.join(out, "requirements.txt"), "w", encoding="utf-8") as f: | |
| f.write(REQUIREMENTS) | |
| n_files = sum(len(files) for _, _, files in os.walk(out)) | |
| print(f"[完成] hub_package/ 共 {n_files} 个文件") | |
| for root, dirs, files in os.walk(out): | |
| rel = os.path.relpath(root, out) | |
| for name in sorted(files): | |
| path = os.path.join(root, name) | |
| print(f" {os.path.join(rel, name) if rel != '.' else name}" | |
| f" ({os.path.getsize(path) / 1024:.0f} KB)") | |
| print("\n下一步上传:") | |
| print(" hf auth login # 粘贴你的 write token") | |
| print(" python upload_to_hub.py --repo-id 你的用户名/value-based-qlearning") | |
| if __name__ == "__main__": | |
| main() | |