news 2026/9/24 16:00:49

Dopamine BarPlotter 可视化指南:用 matplotlib + pygame 绘制强化学习 Agent 行为分布柱状图

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Dopamine BarPlotter 可视化指南:用 matplotlib + pygame 绘制强化学习 Agent 行为分布柱状图
  • 机器学习
  • 深度学习

【免费下载链接】dopamine

Dopamine is a research framework for fast prototyping of reinforcement learning algorithms.

项目地址:https://gitcode.com/gh_mirrors/do/dopamine
点击查看免费下载

导读

本文围绕 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 行),下面是完整清单及其作用:

参数默认值含义与说明
x0该 Plotter 输出在合成画面中的 x 坐标(由基类保证始终存在)
y0该 Plotter 输出在合成画面中的 y 坐标
width213最终输出 surface 的宽度(pygame 平滑缩放目标宽度)
height210最终输出 surface 的高度
fontsize30坐标轴刻度字号;xlabel/ylabelfontsize - 2titlefontsize + 2
bg_color'#f8f7f2'绘图区(axes)背景色,浅米色
face_color'#ffffff'整个 figure 画布背景色,白色
colors['b','g','r','c','m','y','k','w']每根柱子(每个动作)的循环配色,i % num_colors索引
max_width500折线图用参数,柱状图不消费(继承自共用默认模板)
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_dictlegend键则用作图例;返回待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配合。完整渲染管线如下:

  1. 采样阶段:Agent 与环境交互(step/_select_action),回调(如get_probabilities)从当前状态收集数据;
  2. 绘制阶段AgentVisualizer.visualize()遍历plotters列表,逐个调用plotter.draw()得到 pygame surface,并按(plotter.x, plotter.y)坐标blit到合成画面;
  3. 落盘阶段save_frame()把画面像素拆成 RGB 三通道存为 PNG 帧;
  4. 成片阶段generate_video()调用ffmpeg-r 30 -vcodec libx264 -crf 25)把帧序列合成 mp4。

example_viz_lib.pyMyRunner.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'] // 2y = 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负责绘制。

使用注意事项

  1. matplotlib 字体副作用:构造BarPlotter即执行matplotlib.rc('font', ...),会全局改变 matplotlib 字体。文档与源码均提示:如果代码中其他部分还要用 matplotlib,请评估影响。
  2. pygame 环境依赖AgentVisualizer构造时会设置os.environ['SDL_VIDEODRIVER'] = 'dummy'pygame.init(),以避免无显示环境下报pygame.error: No available video deviceBarPlotter本身也依赖pygame.Surface,因此运行环境需要安装 pygame。
  3. 数据形状约束get_bar_data_fn必须返回可解包为(num_actions, num_bins)的二维数组,否则draw()会因bar_data.shape解包失败而抛错。
  4. 颜色循环:默认仅 8 种颜色(b/g/r/c/m/y/k/w),动作数超过 8 时颜色会重复,必要时可通过colors参数传入自定义调色板。
  5. 视频生成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.

项目地址:https://gitcode.com/gh_mirrors/do/dopamine
点击查看免费下载
上一篇:大麦自动抢票脚本实操指南:3 条命令跑通从搜索到下单的全流程
下一篇:OpenProject 11.3.0 发布解读:GitHub 集成、API v3 扩展与新成员邀请流程

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/24 16:00:06

DRF 3.x Format Suffixes 格式后缀使用示例和配置方法

在现代Web开发中,API已经成为了核心架构的一部分。Django Rest Framework(简称DRF)是Python中一个强大且广泛使用的库,帮助开发者快速构建高效且灵活的API。在API开发中,如何支持客户端使用不同的响应格式是一项重要的需求。为了满足这一需求,DRF引入了格式后缀机制,通过…

作者头像 李华
网站建设 2026/9/24 15:52:34

PRQL 的 from 数据源:指定关系、别名与特殊标识符的完整指南

PRQL 的 from 数据源:指定关系、别名与特殊标识符的完整指南 【免费下载链接】prql PRQL is a modern language for transforming data — a simple, powerful, pipelined SQL replacement 项目地址: https://gitcode.com/gh_mirrors/pr/prql from 是 PRQL 管…

作者头像 李华