
Stable Baselines3 Monitor Wrapper 完全指南训练数据采集、monitor.csv 日志格式与结果加载【免费下载链接】stable-baselines3PyTorch version of Stable Baselines, reliable implementations of reinforcement learning algorithms.项目地址: https://gitcode.com/GitHub_Trending/st/stable-baselines3本指南以 Stable Baselines3SB3官方 API 文档docs/common/monitor.md为核心围绕stable_baselines3.common.monitor模块系统讲解Monitor环境包装器的完整使用方式从单环境到向量化环境的接入、monitor.csv日志的精确格式与自定义字段扩展到用load_results加载数据并绘制学习曲线的实战流程。读完本文你将能够在自己训练的任何 Gymnasium 环境中无缝采集逐 episode 的回报reward、长度length与耗时time并直接复用 SB3 生态的分析与可视化能力。Monitor 是什么一个为记录训练过程而生的环境包装器在强化学习训练中我们关心的是每个 episode 究竟取得了多少总回报、运行了多少步、耗时多久而不是原始的 step 级 reward 流。Stable Baselines3 在 stable_baselines3/common/monitor.py 中实现了Monitor类它是一个标准的 GymnasiumWrapper职责是在每个 episode 结束时把累计回报r、episode 步数l、结束时刻的时间戳t写入日志把上述信息注入step()返回的info[episode]字典供上层回调与算法读取可选地把日志持久化到 CSV 文件后缀固定为monitor.csv供离线分析与绘图使用。Monitor在 SB3 的整个训练链路中无处不在BaseAlgorithm._wrap_env在检测到普通非向量化环境尚未被Monitor包裹且monitor_wrapperTrue默认开启时会自动执行env Monitor(env)见 stable_baselines3/common/base_class.py。也就是说即使你不显式包装SB3 算法默认也会在内部套上Monitor只是此时不写日志文件只提供info[episode]等信息。官方将Monitor作为训练数据采集的标准入口这也是本文聚焦它的原因。快速上手三步接入 MonitorMonitor的构造函数签名如下见 monitor.pyMonitor( env: gym.Env, filename: str | None None, allow_early_resets: bool True, reset_keywords: tuple[str, ...] (), info_keywords: tuple[str, ...] (), override_existing: bool True, )最简用法在创建算法之前把 Gymnasium 环境包一层即可import gymnasium as gym from stable_baselines3.common.monitor import Monitor env gym.make(CartPole-v1) env Monitor(env) # 不传 filename仅收集数据不落盘训练并查看每个 episode 的数据——Monitor.step()会在 episode 结束时把{r: ..., l: ..., t: ...}写入返回的info[episode]obs, info env.reset() episode_rewards [] for _ in range(2000): obs, reward, terminated, truncated, info env.step(env.action_space.sample()) if terminated or truncated: if episode in info: print(fEpisode done. reward{info[episode][r]:.2f}, flength{info[episode][l]}, time{info[episode][t]:.2f}s) episode_rewards.append(info[episode][r]) obs, _ env.reset() print(fMean episode reward: {sum(episode_rewards) / len(episode_rewards):.2f})如果希望把日志写入文件只需传入filename。文件会以monitor.csv结尾详见下文ResultsWriter的文件命名规则env Monitor(gym.make(CartPole-v1), filename./logs/cartpole.monitor.csv)官方测试 tests/test_monitor.py 展示了这一典型使用闭环创建Monitor(env, monitor_file)、循环step采样直至 episode 结束、close()收尾并断言get_total_steps()、get_episode_rewards()、get_episode_lengths()等统计与手工累计一致。这也说明Monitor的统计访问器是可信赖的。核心 API 逐项解析Monitor提供的统计访问器定义于 monitor.py方法返回值含义get_total_steps()int自包装以来累计的 step 总数get_episode_rewards()list[float]每个已结束 episode 的累计回报get_episode_lengths()list[int]每个已结束 episode 的步数get_episode_times()list[float]每个 episode 结束时距t_start的墙钟秒数内部实现上step()在terminated or truncated时累计sum(self.rewards)、len(self.rewards)并写入ep_info见 monitor.py。t字段使用的是round(time.time() - self.t_start, 6)即相对t_start的秒数而非绝对时间戳——这是 monitor.csv 格式的一个重要细节读取时需要还原见下节。参数语义与边界行为filename为None时不落盘否则交给ResultsWriter管理文件句柄。Monitor.__init__还会把env.spec.id如CartPole-v1写入文件头作为元数据见 monitor.py。allow_early_resets默认为True允许在 episode 尚未结束时调用reset()训练回调中常见。若设为False在未结束时提前 reset 会抛出RuntimeError: Tried to reset an environment before done...见 monitor.py测试 test_monitor_error_cases 覆盖了该路径。reset_keywords声明哪些reset()的关键字参数会被记录进当前 episode 的信息如options。若声明了某个 key 而reset()未传入会抛出ValueError: Expected you to pass keyword argument ... into reset见 monitor.py。info_keywords声明要从env.step()返回的info字典中额外抽取的字段随r/l/t一起写入 CSV 与info[episode]。典型用途是记录自定义的每个 episode 度量例如成功率。override_existing默认为True覆盖已存在的同名文件设为False则以追加模式append写入便于多次训练结果并入同一文件——官方测试 test_monitor_load_results 验证了追加模式下load_results的行数会正确累加。与 SB3 训练主流程的自动集成通过make_vec_env创建向量化环境时Monitor会被自动套在每个子环境上monitor_dir指定日志目录每个子环境的文件名是其在向量化环境中的 rankmonitor_kwargs可透传给Monitor构造函数见 stable_baselines3/common/env_util.pyfrom stable_baselines3.common.env_util import make_vec_env vec_env make_vec_env( LunarLander-v3, n_envs4, monitor_dir./logs, # None 则不落盘但仍会包裹 Monitor monitor_kwargs{info_keywords: (success,)}, # 额外记录 info[success] )如果使用SubprocVecEnv等原生向量化环境例如 Procgen 系列可直接使用VecMonitorstable_baselines3/common/vec_env/vec_monitor.py在向量层面完成同样的r/l/t记录若环境已被Monitor包裹又套上VecMonitor会发出UserWarning提示统计会被后者覆盖见 vec_monitor.py。monitor.csv 日志格式逐字节看懂训练数据当传入filename后ResultsWritermonitor.py负责落盘。文件命名规则若filename不以monitor.csv结尾则——若是目录就拼成目录/monitor.csv否则追加.monitor.csv后缀。写入前会自动os.makedirs创建缺失目录monitor.py官方测试也专门验证了这一点tests/test_monitor.py。文件头两行固定为 JSON 元数据与 CSV 表头之后每行对应一个已结束的 episode#{t_start: 1771532779.9940808, env_id: Pendulum-v1} r,l,t -1463.466035,200,1.622209这是官方测试 tests/test_monitor.py 中定义的标准样例字段含义r该 episode 累计回报四舍五入到 6 位小数round(ep_rew, 6)l该 episode 的 step 数tepisode 结束时相对t_start的墙钟秒数6 位小数若指定了info_keywords/reset_keywords其对应字段会作为额外的 CSV 列追加在r,l,t之后ResultsWriter用csv.DictWriter以(r, l, t, *extra_keys)为表头见 monitor.py。细节说明打开文件时显式指定newline\n避免 Windows 平台上出现\r\r\n换行问题见 monitor.py相关历史见 docs/misc/changelog.md每写完一行都会立即flush()保证进程崩溃时已完成的 episode 数据不丢失monitor.pyoverride_existingFalse时只写数据行、不再重复写 JSON 头与 CSV 表头monitor.py。加载与分析load_results 与配套工具load_results把 monitor.csv 变成 DataFrameload_results(path)会扫描目录下所有匹配*monitor.csv的文件逐个读取 JSON 头与 CSV 数据把所有 DataFrame 拼接后按时间排序并把t字段从相对各自文件t_start的秒数还原为相对最早t_start的秒数见 monitor.pyfrom stable_baselines3.common.monitor import load_results df load_results(./logs) print(df.head()) # 列index, r, l, t以及自定义的 extra 列注意事项均有源码/测试佐证依赖pandas未安装时会抛出带安装指引的ImportError提示pip install stable-baselines3[extra]monitor.py目录下没有任何monitor.csv时抛出LoadMonitorResultsErrormonitor.py测试 test_monitor_load_results 覆盖空文件只有表头无数据行会返回空 DataFrame且与有数据的文件混合加载时不会产生警告——这是针对 GH#2213 的专门回归修复见 tests/test_monitor.py 与 docs/misc/changelog.md。配套工具get_monitor_files 与绘图get_monitor_files(path)用 glob 匹配*monitor.csv返回日志文件路径列表monitor.py。在此基础上SB3 的results_plotter模块stable_baselines3/common/results_plotter.py提供了一键绘图能力plot_results(dirs, num_timesteps, x_axis, task_name)对多个日志目录调用load_results并通过ts2xy将 DataFrame 拆成 x/y 坐标。支持的三种 x 轴模式定义于 results_plotter.py常量值x 轴含义X_TIMESTEPStimesteps累计步数np.cumsum(df.l)反映样本效率X_EPISODESepisodesepisode 序号X_WALLTIMEwalltime_hrs墙钟时间秒数 ÷ 3600单位小时官方绘图指南 docs/guide/plotting.md 给出了完整闭环示例——训练后直接出图import os import gymnasium as gym import matplotlib.pyplot as plt from stable_baselines3 import PPO from stable_baselines3.common.monitor import Monitor from stable_baselines3.common.results_plotter import plot_results from stable_baselines3.common import results_plotter log_dir tmp/ os.makedirs(log_dir, exist_okTrue) env gym.make(CartPole-v1) env Monitor(env, log_dir) # 训练数据写入 tmp/monitor.csv model PPO(MlpPolicy, env, verbose1) model.learn(total_timesteps20_000) plot_results([log_dir], 20_000, results_plotter.X_TIMESTEPS, PPO CartPole) plt.show()更精细的手工处理可借助ts2xy与window_func滑动窗口平滑示例同样见 docs/guide/plotting.mdfrom stable_baselines3.common.monitor import load_results from stable_baselines3.common.results_plotter import ts2xy, window_func import numpy as np df load_results(log_dir) x, y ts2xy(df, timesteps) # x累计步数, yepisode 回报 x_smooth, y_smooth window_func(x, y, 50, np.mean) # 50 个 episode 的滑动均值实战案例用 Monitor 回调实现按最优训练回报自动保存模型官方示例 docs/guide/examples.md 展示了Monitor与自定义BaseCallback的经典组合回调在每隔check_freq步时调用load_results(self.log_dir)读取 Monitor 数据用ts2xy(..., timesteps)提取回报序列当最近 100 个 episode 的平均回报创下新高时保存模型import os import numpy as np from stable_baselines3 import TD3 from stable_baselines3.common.monitor import Monitor from stable_baselines3.common.results_plotter import ts2xy, load_results from stable_baselines3.common.callbacks import BaseCallback class SaveOnBestTrainingRewardCallback(BaseCallback): def __init__(self, check_freq: int, log_dir: str, verbose: int 1): super().__init__(verbose) self.check_freq check_freq self.log_dir log_dir self.save_path os.path.join(log_dir, best_model) self.best_mean_reward -np.inf def _on_step(self) - bool: if self.n_calls % self.check_freq 0: x, y ts2xy(load_results(self.log_dir), timesteps) if len(x) 0: mean_reward np.mean(y[-100:]) # 最近 100 个 episode 平均回报 if mean_reward self.best_mean_reward: self.best_mean_reward mean_reward self.model.save(self.save_path) return True log_dir tmp/ os.makedirs(log_dir, exist_okTrue) env gym.make(LunarLanderContinuous-v3) env Monitor(env, log_dir) # 关键日志目录必须包含 Monitor 产生的文件 model TD3(MlpPolicy, env, verbose0) model.learn(total_timesteps100_000, callbackSaveOnBestTrainingRewardCallback(1000, log_dir))该案例再次印证了 Monitor 的定位它是所有训练数据分析绘图、回调、TensorBoard 之外的自定义监控的事实数据源。常见问题与排查清单RuntimeError: Tried to reset an environment before done在allow_early_resetsFalse下提前 reset 触发。要么显式传allow_early_resetsTrue要么确保在terminated/truncated后再 reset。ValueError: Expected you to pass keyword argument ... into resetreset_keywords中声明的参数未在reset()传入。LoadMonitorResultsError: No monitor files ... foundload_results的目标目录下没有*monitor.csv确认Monitor(filename...)确实写入了文件。ImportError: pandas is required ...load_results/绘图依赖pandas用pip install stable-baselines3[extra]安装额外依赖。CSV 中t字段为何不是绝对时间戳monitor.csv 记录的是相对t_start的秒数load_results会在加载时按全局最早t_start统一归零直接手写解析器时需注意。环境已用Monitor包裹却又被VecMonitor包裹会触发警告VecMonitor的统计会覆盖Monitor的统计避免双重包裹。延伸阅读模块源码stable_baselines3/common/monitor.pyMonitor / ResultsWriter / get_monitor_files / load_results / LoadMonitorResultsError官方测试tests/test_monitor.py数据一致性、追加模式、空文件回归、错误路径全覆盖向量化版包装器stable_baselines3/common/vec_env/vec_monitor.pyVecMonitor训练绘图指南docs/guide/plotting.md回调 监控示例docs/guide/examples.md从 SB2 迁移说明bench/monitor.py→common/monitor.pydocs/guide/migration.md【免费下载链接】stable-baselines3PyTorch version of Stable Baselines, reliable implementations of reinforcement learning algorithms.项目地址: https://gitcode.com/GitHub_Trending/st/stable-baselines3创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考