- 机器学习
- 深度学习
【免费下载链接】dopamine
Dopamine is a research framework for fast prototyping of reinforcement learning algorithms.
导读
本文围绕 Dopamine 强化学习研究框架中dopamine.utils.bar_plotter模块展开,系统讲解BarPlotter类如何将 Agent 回调产生的数据(如 Rainbow 等分布型算法的 Return 分布)绘制为柱状图,并经 pygame 表面变换后交给AgentVisualizer拼合成可视化帧乃至视频。读完本文,你将掌握BarPlotter的数据契约、全部可配置参数、底层绘制原理,以及如何在自己的实验中复用这套可视化组件。
BarPlotter 模块定位与设计意图
BarPlotter是 Dopamine 可视化工具链(dopamine/utils)中负责"柱状图"的 Plotter 组件,官方文档对其定位只有一句话:BarPlotter used for drawing bar plots(用于绘制柱状图的 Plotter),对应源码位于 dopamine/utils/bar_plotter.py。
它在框架中的角色需要结合可视化工具链的整体结构来理解。Dopamine 的dopamine/utils目录下共提供四类 Plotter,各自负责一种可视元素:
| Plotter 组件 | 用途 |
|---|---|
AtariPlotter | 渲染 Atari 2600 游戏原始帧 |
LinePlotter | 绘制折线图(奖励曲线、Q 值曲线等) |
BarPlotter | 绘制柱状图(Return 分布、动作概率分布等) |
AgentVisualizer | 非 Plotter,负责把多个 Plotter 的输出拼合成帧并生成视频 |
BarPlotter直接继承自抽象基类 dopamine/utils/plotter.py 中的Plotter(官方类文档见 Plotter.md),并标注了@gin.configurable装饰器——这意味着它的构造参数也可以被 gin 配置系统解析覆盖。
需要特别说明文档中强调的一个副作用:实例化BarPlotter会修改全局 matplotlib 字体设置(源码第 79 行执行matplotlib.rc('font', **self.parameters['font']))。如果你的代码其他部分也依赖 matplotlib 的默认字体,这是需要留意的全局影响。
数据契约:get_bar_data_fn 回调
BarPlotter与外部世界唯一的接口是一个回调函数get_bar_data_fn。在构造函数(__init__)中,第一行断言就是:
assert 'get_bar_data_fn' in self.parameters也就是说,get_bar_data_fn是必须提供的参数,缺了它构造会直接失败。该回调的职责在源码 docstring 中有明确约定:
期望一个可调用的
get_bar_data_fn参数,它将为每个动作返回一组分布(a list of distributions for each of the actions)。通常这将是来自 Agent 的回调,返回关于其性能的有用信息。
在实际绘制时(draw()方法),返回的数据被当作二维数组解包:
bar_data = self.parameters['get_bar_data_fn']() num_actions, num_bins = bar_data.shape可见数据形状必须满足:
- 行数
num_actions:每个动作一行,即图中一组柱子; - 列数
num_bins:每行数据的长度,即柱状图的 x 轴分箱数。
框架中最典型的真实案例来自 dopamine/utils/example_viz_lib.py。其中的MyRainbowAgent通过子类化RainbowAgent暴露了一个get_probabilities()方法,把网络输出的动作概率分布直接作为柱状图数据:
def get_probabilities(self): return self._sess.run( tf.squeeze(self._net_outputs.probabilities), {self.state_ph: self.state} )然后在MyRunner.visualize()中,为 Rainbow 类型的 Agent 构造BarPlotter并绑定该回调(example_viz_lib.py第 211-216 行):
q_params['xlabel'] = 'Return' q_params['ylabel'] = 'Return probability' q_params['title'] = 'Return distribution' q_params['get_bar_data_fn'] = self._agent.get_probabilities q_plot = bar_plotter.BarPlotter(parameter_dict=q_params)这就是BarPlotter的典型应用场景:把分布型强化学习算法(如 C51、Rainbow、IQN 等)学到的 Return 分布或动作概率分布,可视化为逐帧刷新的柱状图。对比之下,DQN(Q 值)与 IQN(分位数 Q 值)在同一个visualize()方法中走的是LinePlotter分支,只有分布类算法走BarPlotter分支。
参数体系:全部默认值与取值建议
BarPlotter的全部默认参数定义在类属性_defaults中(bar_plotter.py第 42-58 行),下面是完整清单及其作用:
| 参数 | 默认值 | 含义与说明 |
|---|---|---|
x | 0 | 该 Plotter 输出在合成画面中的 x 坐标(由基类保证始终存在) |
y | 0 | 该 Plotter 输出在合成画面中的 y 坐标 |
width | 213 | 最终输出 surface 的宽度(pygame 平滑缩放目标宽度) |
height | 210 | 最终输出 surface 的高度 |
fontsize | 30 | 坐标轴刻度字号;xlabel/ylabel用fontsize - 2,title用fontsize + 2 |
bg_color | '#f8f7f2' | 绘图区(axes)背景色,浅米色 |
face_color | '#ffffff' | 整个 figure 画布背景色,白色 |
colors | ['b','g','r','c','m','y','k','w'] | 每根柱子(每个动作)的循环配色,i % num_colors索引 |
max_width | 500 | 折线图用参数,柱状图不消费(继承自共用默认模板) |
figsize | (12, 9) | matplotlib figure 尺寸(英寸),先按此渲染再缩放 |
font | {'family': 'Bitstream Vera Sans', 'weight': 'regular', 'size': 26} | 写入matplotlib.rc('font', ...)的全局字体设置 |
除_defaults外,draw()还会消费两个可选键:
legend:若存在于参数中,则作为图例标签列表传给self.plot.legend(self.parameters['legend'])。在example_viz_lib.py中它被设为['Action 0', 'Action 1', ...](按num_actions生成)。xlabel/ylabel/title:由基类_setup_plot()消费,用于设置坐标轴与标题文字。
参数合并的顺序逻辑在基类 dopamine/utils/plotter.py 第 49-51 行:
self.parameters = {'x': 0, 'y': 0} self.parameters.update(self._defaults) self.parameters.update(parameter_dict)即内置x/y默认值 → 子类_defaults→ 用户传入的parameter_dict三级覆盖,后传入的优先级最高。
绘制原理:matplotlib 渲染 + pygame 表面搬运
draw()是BarPlotter的核心方法,官方文档描述为"绘制柱状图;若parameter_dict含legend键则用作图例;返回待AgentVisualizer渲染的对象"。其内部流程可以分为三个阶段(bar_plotter.py第 81-112 行):
阶段一:matplotlib 出图。调用基类_setup_plot()完成画布清理与样式设置(清空当前坐标轴、设置 figure/axes 背景色、可选地设置 x/y 轴标签、标题、刻度及其字号),随后按num_actions逐行绘制:
self._setup_plot() num_colors = len(self.parameters['colors']) bar_data = self.parameters['get_bar_data_fn']() num_actions, num_bins = bar_data.shape for i in range(num_actions): self.plot.bar( np.arange(num_bins), bar_data[i], color=self.parameters['colors'][i % num_colors], )x 轴固定为np.arange(num_bins)(0 到 bins-1 的整数刻度),每个动作一组同色柱子;颜色按i % num_colors循环取用,因此动作数超过 8 时会复用颜色。
阶段二:把 matplotlib 画布拷贝进 pygame surface。关键代码如下:
self.fig.canvas.draw() width, height = self.fig.canvas.get_width_height() if self.plot_surface is None: self.plot_surface = pygame.Surface((width, height)) plot_buffer = np.frombuffer(self.fig.canvas.buffer_rgba(), np.uint32) surf_buffer = np.frombuffer(self.plot_surface.get_buffer(), dtype=np.int32) np.copyto(surf_buffer, plot_buffer)这里没有走 matplotlib 保存图片文件再加载的慢路径,而是直接用numpy.frombuffer把 matplotlib 画布的 RGBA 像素缓冲复制到 pygame surface 的像素缓冲中,实现内存级零拷贝搬运。
阶段三:缩放返回。用pygame.transform.smoothscale把大尺寸渲染结果缩放到配置的width×height(默认 213×210),返回供AgentVisualizer.blit的 surface 对象。
在 AgentVisualizer 中的集成与整段渲染管线
BarPlotter单独没有意义,它必须与 dopamine/utils/agent_visualizer.py 中的AgentVisualizer配合。完整渲染管线如下:
- 采样阶段:Agent 与环境交互(
step/_select_action),回调(如get_probabilities)从当前状态收集数据; - 绘制阶段:
AgentVisualizer.visualize()遍历plotters列表,逐个调用plotter.draw()得到 pygame surface,并按(plotter.x, plotter.y)坐标blit到合成画面; - 落盘阶段:
save_frame()把画面像素拆成 RGB 三通道存为 PNG 帧; - 成片阶段:
generate_video()调用ffmpeg(-r 30 -vcodec libx264 -crf 25)把帧序列合成 mp4。
example_viz_lib.py中MyRunner.visualize()展示了完整的四元素布局(第 217-233 行):
screen_width = ( atari_plot.parameters['width'] + reward_plot.parameters['width'] ) screen_height = ( atari_plot.parameters['height'] + q_plot.parameters['height'] ) # Dimensions need to be divisible by 2: if screen_width % 2 > 0: screen_width += 1 if screen_height % 2 > 0: screen_height += 1 visualizer = agent_visualizer.AgentVisualizer( record_path=record_path, plotters=[atari_plot, reward_plot, q_plot], screen_width=screen_width, screen_height=screen_height, )其中BarPlotter的坐标被设置为x = atari_plot.parameters['width'] // 2、y = atari_plot.parameters['height'],即游戏画面右上角;reward 折线图在其左侧,游戏画面位于左上,整幅画面呈现 2×2 信息布局。注意代码中会对宽高做奇偶修正,因为部分绘制路径要求尺寸可被 2 整除。
实战:复用一个自定义 BarPlotter
基于上述契约,你可以非常轻量地构造自己的BarPlotter。下面给出一个最小可运行示例(独立于框架的 Atari 环境,用随机数据模拟"每个动作的分布"):
import numpy as np from dopamine.utils.bar_plotter import BarPlotter num_actions = 4 num_bins = 51 # 对应 C51 的 51 个分位 bin def get_bar_data_fn(): # 返回 shape = (num_actions, num_bins) 的数组 data = np.random.rand(num_actions, num_bins) return data / data.sum(axis=1, keepdims=True) # 归一化为概率 plotter = BarPlotter(parameter_dict={ 'x': 160, 'y': 0, # 放在合成画面右侧 'width': 213, 'height': 210, # 输出 surface 尺寸 'fontsize': 24, 'bg_color': '#f8f7f2', 'face_color': '#ffffff', 'xlabel': 'Return', 'ylabel': 'Return probability', 'title': 'Return distribution', 'legend': [f'Action {i}' for i in range(num_actions)], 'get_bar_data_fn': get_bar_data_fn, }) surface = plotter.draw() # 返回 pygame.Surface若要在完整管线中运行官方示例,可参考 dopamine/utils/example_viz.py 的命令行入口(其功能是加载已训练 checkpoint,在 eval 模式下边回放游戏边生成可视化视频):
python example_viz.py \ --agent='rainbow' \ --game='SpaceInvaders' \ --num_steps=1000 \ --root_dir='/tmp/dopamine' \ --restore_checkpoint=/path/to/checkpoint/tf_ckpt-199其余可用 flag 包括--use_legacy_checkpoint(加载旧版 Keras 前 checkpoint 时置为True)。主入口run()(example_viz_lib.py第 278-300 行)还会动态解析一段 gin 配置,设置游戏名与WrappedReplayBuffer.replay_capacity = 300,再依次创建 Runner、执行visualize()生成图像与视频。其中 Rainbow 分支的 Return 分布图即由本文主角BarPlotter负责绘制。
使用注意事项
- matplotlib 字体副作用:构造
BarPlotter即执行matplotlib.rc('font', ...),会全局改变 matplotlib 字体。文档与源码均提示:如果代码中其他部分还要用 matplotlib,请评估影响。 - pygame 环境依赖:
AgentVisualizer构造时会设置os.environ['SDL_VIDEODRIVER'] = 'dummy'并pygame.init(),以避免无显示环境下报pygame.error: No available video device;BarPlotter本身也依赖pygame.Surface,因此运行环境需要安装 pygame。 - 数据形状约束:
get_bar_data_fn必须返回可解包为(num_actions, num_bins)的二维数组,否则draw()会因bar_data.shape解包失败而抛错。 - 颜色循环:默认仅 8 种颜色(b/g/r/c/m/y/k/w),动作数超过 8 时颜色会重复,必要时可通过
colors参数传入自定义调色板。 - 视频生成:
generate_video()内部通过subprocess.call调用ffmpeg,源码注释明确提示"仅应在可信路径下使用此功能"。
延伸阅读
- 模块级 API 文档:dopamine.utils.bar_plotter 与类文档 BarPlotter
- 基类与参数合并逻辑:dopamine/utils/plotter.py、Plotter.md
- 整条可视化管线的组合器:dopamine/utils/agent_visualizer.py、AgentVisualizer.md
- 官方示例(含 Rainbow Return 分布的可视化编排):dopamine/utils/example_viz_lib.py、dopamine/utils/example_viz.py
- 同类组件对比:dopamine/utils/line_plotter.py、dopamine/utils/atari_plotter.py
- 可视化实验入口(Jupyter Notebook 方式):dopamine/colab/agent_visualizer.ipynb
- 机器学习
- 深度学习
【免费下载链接】dopamine
Dopamine is a research framework for fast prototyping of reinforcement learning algorithms.
相关推荐
在 Apple Silicon 上运行 Omarchy 4.0.1:基于 Lume ARM64 VM、Arch Linux ARM 与 Rosetta 的兼容性移植实战
在 Apple Silicon 上运行 Omarchy 4.0.1:基于 Lume ARM64 VM、Arch Linux ARM 与 Rosetta 的兼容性
机器学习深度学习Dopamine Plotter 可视化基类解析:从抽象基类到强化学习智能体行为绘图
Dopamine Plotter 可视化基类解析:从抽象基类到强化学习智能体行为绘图 Dopamine 是面向强化学习算法快速原型设计的研究框架,在其 dopa
机器学习深度学习CKIP Transformers vs 其他中文NLP工具:性能对比与选择指南
CKIP Transformers vs 其他中文NLP工具:性能对比与选择指南 CKIP Transformers是一款基于Transformer架构的中文自
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考