在强化学习任务中,当智能体的观测包含图像、高度图或其他空间结构数据时,全连接网络难以有效捕获局部空间特征与平移不变性。rsl_rl 通过 ActorCriticCNN 模块为 Actor-Critic 架构引入卷积神经网络(CNN)编码器,支持混合观测输入——即同时处理一维向量观测与二维空间观测。CNN 分支将二维观测压缩为紧凑的特征向量,再与一维观测拼接后送入 MLP 策略头与价值头,从而在保持策略网络表达能力的同时,高效利用空间信息。本章聚焦于 CNN 编码器的内部结构、混合观测的数据流,以及配置与集成方式。
Sources: modules/actor_critic_cnn.py, networks/cnn.py
CNN 网络结构与设计哲学
CNN 类位于 networks/cnn.py,继承自 nn.Sequential,其核心设计目标是通过声明式配置堆叠多层卷积运算,自动处理张量维度推演,最终输出可用于 MLP 的扁平特征向量。网络支持逐层独立配置卷积核、步长、膨胀率、归一化、激活与池化,并在末尾提供可选的全局池化与展平操作。这种设计允许用户通过配置文件定义从原始图像到特征向量的完整变换,而无需手动推导每一层的张量形状。
每一层的构建遵循固定的运算顺序:卷积 → 归一化(可选)→ 激活 → 最大池化(可选)。在层间推进过程中,模块会调用 _compute_padding 自动计算保持空间对齐所需的填充量,并调用 _compute_output_dim 追踪输出高宽,确保后续层接收到正确的输入尺寸。若启用全局池化(max 或 avg),输出将被进一步压缩为 (B, C, 1, 1) 的形状;若启用 flatten,则最终输出为 (B, C×H×W) 的二维张量,可直接与一维观测拼接。
Sources: networks/cnn.py, networks/cnn.py
CNN 配置参数详解
下表汇总了 CNN 构造函数的全部参数及其作用。其中 kernel_size、stride、dilation、norm、max_pool 既可以是单个标量/布尔值(表示所有层共享同一配置),也可以是列表/元组(表示逐层独立配置)。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
input_dim |
tuple[int, int] |
— | 输入张量的高与宽 (H, W) |
input_channels |
int |
— | 输入通道数,如 RGB 图像为 3 |
output_channels |
tuple/list[int] |
— | 每一层卷积的输出通道数,长度决定层数 |
kernel_size |
int / tuple/list |
— | 卷积核尺寸,可为逐层列表 |
stride |
int / tuple/list |
1 |
卷积步长 |
dilation |
int / tuple/list |
1 |
空洞卷积的膨胀率 |
padding |
str |
"none" |
填充类型:none、zeros、reflect、replicate、circular |
norm |
str / tuple/list |
"none" |
归一化类型:none、batch、layer |
activation |
str |
"elu" |
激活函数名称,由 resolve_nn_activation 解析 |
max_pool |
bool / tuple/list |
False |
每层后是否接 MaxPool2d(kernel=3, stride=2, padding=1) |
global_pool |
str |
"none" |
末端全局池化:none、max、avg |
flatten |
bool |
True |
是否将最终特征图展平为向量 |
padding 设为 none 时不使用填充;设为其他模式时,_compute_padding 会依据 PyTorch 的 Conv2d 文档公式计算对称填充量,使输出尺寸保持合理的对齐关系。ActorCriticCNN 要求 CNN 输出必须为展平状态(flatten=True),否则会在初始化时抛出异常,因为 MLP 头无法接受保留空间维度的张量。
Sources: networks/cnn.py, networks/cnn.py
权重初始化策略
CNN 类提供 init_weights 方法,对所有 nn.Conv2d 层执行 Kaiming Normal 初始化(kaiming_normal_),并将偏置项置零。这种初始化方式与 ReLU 及其变体(如 ELU)配合良好,有助于缓解深层卷积网络的前向传播方差爆炸问题。需要注意的是,当前 ActorCriticCNN 并未显式调用 CNN 的 init_weights,因此卷积层默认使用 PyTorch 的标准初始化;若用户在训练中发现收敛困难,可在模型构建后手动调用该接口。
Sources: networks/cnn.py
ActorCriticCNN 混合观测架构
ActorCriticCNN 继承自基础的 ActorCritic,并非重写整个策略网络,而是在基类能力之上扩展了二维观测分支。它通过 obs_groups 配置区分策略网络(policy)与价值网络(critic)所使用的观测集合,并在每个集合内部进一步识别哪些观测是二维的(形状为 B, C, H, W),哪些是一维的(形状为 B, C)。这种分层解析使得同一套代码能够同时适应纯图像输入、纯向量输入,以及两者混合的复杂场景。
在初始化阶段,ActorCriticCNN 会遍历 obs_groups["policy"] 和 obs_groups["critic"],分别构建 actor_obs_groups_2d / actor_obs_groups_1d 与对应的 critic 分组。对于每一个二维观测组,模块会实例化一个独立的 CNN 编码器,所有编码器的扁平输出拼接后,与一维观测的拼接向量组合,形成最终进入 MLP 的联合特征表示。
Sources: modules/actor_critic_cnn.py
数据流架构图
以下 Mermaid 图展示了 ActorCriticCNN 的前向传播数据流。以策略网络为例,价值网络遵循完全对称的逻辑,但使用 critic_cnns 与 critic MLP。
flowchart LR
subgraph ObsInput[观测输入 TensorDict]
O1[1D观测组<br/>B x C]
O2[2D观测组A<br/>B x C x H x W]
O3[2D观测组B<br/>B x C x H x W]
end
subgraph ActorEncoder[Actor编码分支]
C1[CNN_A<br/>flatten输出]
C2[CNN_B<br/>flatten输出]
CAT1[Concat<br/>1D + CNN特征]
MLP[Actor MLP<br/>hidden_dims]
end
O1 --> CAT1
O2 --> C1 --> CAT1
O3 --> C2 --> CAT1
CAT1 --> MLP --> Mean[动作均值]
在 act 与 act_inference 阶段,_update_distribution 首先调用 get_actor_obs 分离出一维与二维观测,随后用 actor_cnns 对二维观测进行编码,将编码结果与一维观测在特征维度(dim=-1)上拼接,最后调用基类的 _update_distribution 计算动作分布的均值与标准差。evaluate 方法对 critic 侧执行同样的拼接逻辑,只是目标变为状态价值估计。
Sources: modules/actor_critic_cnn.py
与 ActorCritic 基类的关键差异
| 特性 | ActorCritic(基类) | ActorCriticCNN |
|---|---|---|
| 观测维度支持 | 仅 B x C 一维观测 |
一维 B x C 与二维 B x C x H x W 混合 |
| 观测获取方式 | get_actor_obs 返回单一张量 |
get_actor_obs 返回 (1d_tensor, 2d_dict) 元组 |
| 归一化范围 | 对全部 actor/critic 观测归一化 | 仅对一维观测执行 EmpiricalNormalization |
| CNN 配置 | 无 | 通过 actor_cnn_cfg / critic_cnn_cfg 独立配置 |
| 多 2D 观测组 | 不支持 | 每个 2D 组拥有独立 CNN,输出拼接 |
基类 ActorCritic 在 get_actor_obs 中直接按 obs_groups["policy"] 顺序拼接所有观测,并通过 actor_obs_normalizer 对整个拼接向量进行经验归一化。而 ActorCriticCNN 的重写版本将归一化限制在一维观测子集上,原因在于二维图像数据通常已通过环境侧预处理(如除以 255 或直方图归一化)达到合理数值范围,额外的逐元素经验归一化可能破坏空间结构的相对关系。
Sources: modules/actor_critic.py, modules/actor_critic_cnn.py, modules/actor_critic_cnn.py
观测分组与 CNN 配置解析
ActorCriticCNN 的灵活性建立在 obs_groups 与 CNN 配置字典的协同解析之上。obs_groups 定义了策略网络与价值网络各自关注哪些观测组,例如:
obs_groups = {
"policy": ["proprioception", "depth_camera", "height_scan"],
"critic": ["proprioception", "depth_camera", "height_scan", "command"]
}
在初始化时,ActorCriticCNN 检查每个观测组的张量形状:若 len(shape) == 4,则归类为二维观测;若 len(shape) == 2,则归类为一维观测。如果策略侧或价值侧不存在任何二维观测,模块会断言失败并提示用户改用 ActorCritic,这避免了不必要的 CNN 计算开销。
CNN 配置 actor_cnn_cfg 和 critic_cnn_cfg 支持两种写法。第一种是为每个 2D 观测组提供独立的配置字典:
actor_cnn_cfg = {
"depth_camera": {
"output_channels": [32, 64, 128],
"kernel_size": [8, 4, 3],
"stride": [4, 2, 1],
"activation": "elu"
},
"height_scan": {
"output_channels": [16, 32],
"kernel_size": 3,
"stride": 2
}
}
第二种是当所有 2D 观测组共享相同结构时,直接传入单个字典,ActorCriticCNN 会自动将其复制到每个组:
actor_cnn_cfg = {
"output_channels": [32, 64],
"kernel_size": 3,
"stride": 2
}
# 内部自动转换为 {"depth_camera": {...}, "height_scan": {...}}
Sources: modules/actor_critic_cnn.py, modules/actor_critic_cnn.py
配置示例与训练集成
在实际训练流程中,OnPolicyRunner 通过配置文件中的 class_name 字段解析策略类。若将 policy.class_name 设为 ActorCriticCNN,OnPolicyRunner._construct_algorithm 会在构建算法时把观测样本 obs、解析后的 obs_groups 以及所有 policy_cfg 参数传入构造函数。以下为一个完整的策略配置示例:
policy:
class_name: ActorCriticCNN
actor_hidden_dims: [256, 256, 256]
critic_hidden_dims: [256, 256, 256]
activation: elu
actor_obs_normalization: true
critic_obs_normalization: true
actor_cnn_cfg:
output_channels: [32, 64, 32]
kernel_size: [8, 4, 3]
stride: [4, 2, 1]
norm: none
activation: elu
flatten: true
critic_cnn_cfg:
output_channels: [32, 64, 32]
kernel_size: [8, 4, 3]
stride: [4, 2, 1]
norm: none
activation: elu
flatten: true
需要注意的是,actor_cnn_cfg 与 critic_cnn_cfg 中的参数直接对应 CNN.__init__ 的命名,但无需传入 input_dim 和 input_channels,因为这两个值由环境返回的观测张量形状自动推导。若策略侧和价值侧的二维观测组不同(例如 critic 额外接收一张全局地图),则必须为两侧分别提供完整的分组配置字典,且字典的键必须与对应 obs_groups 中的组名完全匹配。
Sources: runners/on_policy_runner.py, modules/actor_critic_cnn.py
与系统其他模块的交互
ActorCriticCNN 并非孤立组件,它与训练系统的交互关系如下:
- 算法层:
PPO和PPO_AMP通过统一的act/evaluate接口调用策略网络,无需感知底层是否存在 CNN 编码器。若需深入了解算法逻辑,请参阅 PPO 算法实现与训练流程。 - 存储层:
RolloutStorage直接存储环境返回的原始TensorDict观测,因此二维观测以完整张量形式保存在回放缓冲区中。关于数据存储格式与 Transition 结构,请参阅 Rollout 数据存储与 Transition。 - 基础架构:
ActorCriticCNN继承自ActorCritic,两者共享动作噪声建模、分布更新与状态字典加载逻辑。若尚未熟悉基础策略架构,建议先阅读 Actor-Critic 基础架构设计。 - 其他变体:rsl_rl 还提供基于循环网络与注意力编码器的策略变体,分别对应 循环与注意力策略变体。CNN 版本与这些变体互不冲突,但在当前实现中并未提供 CNN + RNN 的复合模块,需要时可自行组合。