- 机器学习
- 深度学习
【免费下载链接】dopamine
Dopamine is a research framework for fast prototyping of reinforcement learning algorithms.
导读
在强化学习研究中,对比不同算法、不同游戏、不同超参数下的训练曲线是评估工作的核心环节。Dopamine 在dopamine/colab/utils.py中提供了一套从实验日志到 Pandas DataFrame 的数据读取工具,其中read_experiment是连接"磁盘上散落的实验目录"与"可供 seaborn / matplotlib 直接绘图的汇总数据"的关键入口。本文将以 read_experiment 官方 API 文档 为骨架,结合 utils.py 源码、Logger 日志模块 与 load_statistics.ipynb 实战示例,完整讲解该函数的设计动机、参数语义、路径构造规则、内部实现原理,并给出可直接运行的读取与绘图方案,帮助你快速搭建自己的实验分析流水线。
一、数据从哪来:Dopamine 的日志落盘机制
在理解read_experiment之前,需要先知道它读取的对象是如何产生的。Dopamine 使用 dopamine/discrete_domains/logger.py 中的Logger类将训练/评估的统计量落盘:
- 训练过程中,代理会把每个迭代(iteration)的统计数据(如
train_episode_returns、eval_episode_returns)存入 Logger 内部的字典; - 通过
log_to_file(filename_prefix, iteration_number)将该字典用pickle序列化,写到${logging_dir}/${filename_prefix}_${iteration_number}; - 默认的
logs_duration=4表示最多保留最近 4 个版本的日志文件,更旧的会被自动清理(见 logger.py 与 logger.py)。
在 colab/utils.py 中定义了与 Logger 协作的两个常量:
FILE_PREFIX = 'log' ITERATION_PREFIX = 'iteration_'也就是说,一次实验在磁盘上的典型布局是:
experiment_dir/ └── logs/ ├── log_0 ├── log_1 ├── log_2 └── ...其中每个log_<n>是包含iteration_0、iteration_1… 等键的 pickle 字典。而read_experiment的职责,就是把多条这样的实验目录按参数网格批量读入、汇总并合并成一个可用于分析的 DataFrame。
二、read_experiment:签名与参数语义
read_experiment的完整签名(见 read_experiment 文档 与 utils.py 源码)为:
dopamine.colab.utils.read_experiment( log_path, parameter_set=None, job_descriptor='', iteration_number=None, summary_keys=('train_episode_returns', 'eval_episode_returns'), verbose=False )| 参数 | 类型 | 含义 | 默认值 |
|---|---|---|---|
log_path | string | 存放所有实验结果的基础路径 | 必填 |
parameter_set | collections.OrderedDict | 实验参数名到允许取值的映射;同时定义参数在job_descriptor中的出现顺序 | None |
job_descriptor | string | 用于为每个 trial 构造完整路径的格式化字符串 | '' |
iteration_number | int 或None | 若不为None,则固定读取该迭代号的数据;否则读取最新迭代 | None |
summary_keys | Iterable[str] | 需要做逐迭代汇总的统计量名称 | ('train_episode_returns', 'eval_episode_returns') |
verbose | bool | 为True时打印额外信息(如正在读取的文件路径) | False |
| 返回 | Pandas DataFrame | 包含全部实验参数、迭代号与汇总统计量的结果表 | — |
该方法的官方说明(read_experiment 文档)明确了两个核心约定:
parameter_set是一个有序字典,它一方面定义本次实验的参数,另一方面决定这些参数在job_descriptor中出现的顺序;- 方法会读取所有形如
${log_path}/${job_descriptor}.format(params)/logs的实验目录,其中params由parameter_set各元素取值的笛卡尔积构造。
三、路径构造机制:参数网格 → 实验目录
原文档给出了最直观的例子(read_experiment 文档):
import collections parameter_set = collections.OrderedDict([ ('game', ['Asterix', 'Pong']), ('epsilon', ['0', '0.1']) ]) read_experiment('/tmp/logs', parameter_set, job_descriptor='{}_{}')该调用会尝试依次读取以下 4 个目录中的logs子目录:
/tmp/logs/Asterix_0/logs /tmp/logs/Asterix_0.1/logs /tmp/logs/Pong_0/logs /tmp/logs/Pong_0.1/logs从源码看,其实现过程是(utils.py):
keys = [] if parameter_set is None else list(parameter_set.keys()) ordered_values = [parameter_set[key] for key in keys] for parameter_tuple in itertools.product(*ordered_values): if job_descriptor is not None: name = job_descriptor.format(*parameter_tuple) else: # 回退命名:game_Asterix-epsilon_0 name = '-'.join( [keys[i] + '_' + str(parameter_tuple[i]) for i in range(len(keys))] ) experiment_path = '{}/{}/logs'.format(log_path, name) raw_data, last_iteration = load_statistics( experiment_path, iteration_number=iteration_number, verbose=verbose )需要注意的细节:
itertools.product按parameter_set中参数的声明顺序生成元组,因此job_descriptor中的每个{}占位符依次对应parameter_set的一个键;- 若
job_descriptor=None,则会自动退化为key_value-key_value形式的目录名(如game_Asterix-epsilon_0),适合日志目录没有统一模板时的读取; - 每个实验路径末尾统一追加
/logs,这与第一节中 Logger 输出的目录结构一一对应。
四、返回结果:DataFrame 的结构与内部实现
read_experiment返回一个 Pandas DataFrame,其列由三部分组成(utils.py):
column_names = keys + ['iteration'] + list(summary_keys)即:参数网格列 + 迭代号列 + 各汇总统计量列。以前述示例为例,返回表的列将是game、epsilon、iteration、train_episode_returns、eval_episode_returns,行数为"参数组合数 × 实际迭代数"。
源码中的实现要点(utils.py):
- 预分配容量:
expected_num_iterations = 200,先按参数组合数 × 200预建 DataFrame 索引,再逐行填充; - 逐参数组合、逐迭代填行:每个参数元组调用
load_statistics取回原始字典与最新迭代号,再交给summarize_data生成逐迭代汇总,然后为0..last_iteration的每个迭代号写入一行(参数值 + 迭代号 + 各统计量); - 数值类型统一:将所有能转换为数值的列
astype(np.float64),规避后续merge时"object 列与数值列合并报 ValueError"的坑(utils.py); - 裁剪多余行:最后用
drop(np.arange(row_index, expected_num_rows))丢弃预分配后未被填充的行,保证返回表紧凑干净。
五、协同工作的配套函数
read_experiment并非孤立存在,它依赖colab.utils模块中另外几个工具函数(模块文档),理解它们有助于排查读取问题。
5.1 load_statistics:单目录读取
签名与文档见 load_statistics 文档:
load_statistics(log_path, iteration_number=None, verbose=True)- 若
iteration_number=None,自动通过get_latest_iteration找到最新的日志文件; - 日志文件名固定为
log_<iteration>(FILE_PREFIX = 'log'); - 用
pickle.load读取后返回(data, iteration)二元组(utils.py)。
5.2 summarize_data:逐迭代汇总
签名与文档见 summarize_data 文档:
summarize_data(data, summary_keys)- 输入是
load_statistics返回的字典(键为iteration_0、iteration_1…); - 对每个 key 计算每个迭代内所有 episode 的均值(
np.mean); - 数据缺失时沿用上一个迭代的值(源码注释明确说明:"We allow reporting the same value multiple times when data is missing",utils.py),保证曲线连续不断档。
5.3 get_latest_iteration / get_latest_file
get_latest_iteration(path):用 glob 匹配log_[0-9]*,解析出最大迭代号;无日志时抛ValueError(文档、utils.py);get_latest_file(path):返回最新日志的完整路径;找不到时返回None(文档、utils.py)。
5.4 load_baselines:官方基线数据加载
load_baselines(base_dir, verbose=False)用于读取 Dopamine 官方发布的基线数据(utils.py):
- 遍历
ALL_GAMES(60 款 Atari 游戏)与['dqn', 'c51', 'rainbow', 'iqn']四种代理; - 期望文件布局为
${base_dir}/${agent}/${game}.pkl,读取后统一转为 float64 并按游戏merge合并; - 仓库 baselines/atari/data 下即存放此类基线 JSON(
*.json/*.vg.json),而 Atari 与 MuJoCo 的基线可视化页面对应 baselines/atari/plots.html 与 baselines/mujoco/plots.html。
六、实战:在 Colab 中读取实验并绘制训练曲线
仓库中的 dopamine/colab/load_statistics.ipynb 演示了read_experiment的两种典型用法,可直接作为模板。
示例 1:批量读取样本实验并与基线合并绘图
假设样本日志位于/content/samples/rainbow/<GAME>_v4/logs,其中<GAME>是游戏名列表GAMES中的元素。此时各参数在目录名中出现的顺序是"agent在前、game在后",因此:
import collections parameter_set = collections.OrderedDict([ ('agent', ['rainbow']), ('game', GAMES) ]) sample_data = colab_utils.read_experiment( '/content/samples', parameter_set=parameter_set, job_descriptor='{}/{}_v4', summary_keys=['train_episode_returns']) sample_data['agent'] = 'Sample Rainbow' sample_data['run_number'] = 1 for game in GAMES: experimental_data[game] = experimental_data[game].merge( sample_data[sample_data.game == game], how='outer')之后即可用 seaborn 按agent分组绘制各游戏的训练曲线:
import seaborn as sns import matplotlib.pyplot as plt for game in GAMES: fig, ax = plt.subplots(figsize=(16, 8)) sns.lineplot(x='iteration', y='train_episode_returns', hue='agent', data=experimental_data[game], ax=ax) plt.title(game) plt.show()这里read_experiment返回的iteration与train_episode_returns两列,正好直接作为折线图的 x、y 轴数据。
示例 2:组合 load_statistics 与 summarize_data 读取单个实验
当只需要处理一条实验目录、并希望保留原始数据而非网格化汇总时,可以绕过read_experiment,手动组合两个底层函数:
import matplotlib.pyplot as plt for game in GAMES: raw_data, _ = colab_utils.load_statistics( '/content/samples/rainbow/{}_v4/logs'.format(game), verbose=False) summarized_data = colab_utils.summarize_data( raw_data, ['train_episode_returns']) plt.plot(summarized_data['train_episode_returns'], label='episode returns') plt.title('Rainbow training - {}'.format(game)) plt.xlabel('Iteration') plt.ylabel('Return') plt.legend() plt.show()更多用例
- dopamine/colab/agents.ipynb 中使用
read_experiment读取论文基线数据(如random_dqn_data = colab_utils.read_experiment(...)),用于复现算法对比图; - dopamine/colab/cartpole.ipynb 中对 CartPole 环境同样通过
colab_utils.read_experiment(DQN_PATH, verbose=True, ...)读取训练结果; - 各 notebook 的完整运行方式与数据下载说明见 dopamine/colab/README.md。
七、使用建议与常见问题
- 目录名必须与
job_descriptor严格一致:路径拼接是纯字符串格式化,job_descriptor中的占位符数量与顺序必须与parameter_set的键一一对应,否则会因找不到目录而读取失败。 iteration_number用于对齐多次实验的迭代窗口:不同 trial 的训练长度可能不同,通过指定同一iteration_number可以强制在所有目录读取相同迭代号的日志,便于公平对比;不指定时则各自取最新迭代。verbose=True有助于调试:开启后load_statistics会打印"Reading statistics from: ",便于确认实际命中的文件路径。- 缺失数据会被"前向填充":
summarize_data在某个迭代缺少数据时沿用上一个迭代的均值,绘图时曲线不会出现空洞,但解读时要留意这一点。 - 依赖说明:
utils.py依赖numpy、pandas与tensorflow,其中文件 I/O 通过tf.io.gfile完成(utils.py),因此log_path不仅可以是本地路径,也可指向 GCS 等gfile支持的存储位置,方便直接读取云端训练产物。 - 读取失败时检查日志文件是否仍存在:Logger 默认只保留最近 4 个版本(
logs_duration=4,logger.py),如果实验早已结束且日志被清理,get_latest_iteration会抛出ValueError: No log data found,此时需检查训练时的logging_dir配置或延长日志保留周期。
结语
read_experiment将"参数网格 × 迭代日志"这一常见的实验组织方式抽象为一次调用:通过parameter_set声明参数空间、用job_descriptor描述目录命名模板,即可在几行代码内完成多实验数据的批量读取、逐迭代汇总与 DataFrame 化,为后续的曲线对比、表格统计与论文图表生成提供统一、干净的数据入口。结合load_statistics、summarize_data、get_latest_iteration等底层函数,你可以灵活组合出适配自己实验目录结构的分析管线。
- 机器学习
- 深度学习
【免费下载链接】dopamine
Dopamine is a research framework for fast prototyping of reinforcement learning algorithms.
相关推荐
Dopamine 实验数据读取与统计分析:dopamine.colab.utils 模块完全指南
Dopamine 实验数据读取与统计分析:dopamine.colab.utils 模块完全指南 本指南以 Dopamine 仓库中的 colab/utils.
机器学习深度学习加载 Dopamine 基线实验数据:dopamine.colab.utils.load_baselines 函数解析与实战指南
加载 Dopamine 基线实验数据:dopamine.colab.utils.load_baselines 函数解析与实战指南 load_baselines
强化学习机器学习深度学习gs-quant 价差套利完整实践指南:用卡尔曼滤波动态追踪公平价差
gs quant 价差套利完整实践指南:用卡尔曼滤波动态追踪公平价差 一次失败的交易复盘:螺纹钢 铁矿石价差,以60日滚动均值作为回归中枢,2023年3月不到三
强化学习机器学习深度学习
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考