文章目录
数据流向
- 输入: 从
nuscenes_vad_dataset.py
接收输入数据:多视角图像、3D标注、地图标注、ego信息 - 特征提取:
ResNet backbone
+FPN
提取多尺度特征 - BEV编码: 提取历史和当前BEV特征
- 感知预测:
检测物体
和HD地图
元素 - 规划预测: 基于感知结果和BEV特征进行
轨迹规划
- Latent预测: 预测
未来场景
状态 - 输出: 检测框、地图元素、规划轨迹、场景预测结果
1、从
dataset
中准备训练数据(nuscenes_vad_dataset.py
)与预处理(datasets/pipelines/transform_3d.py
);
2、SSR/projects/mmdet3d_plugin/SSR/SSR.py,模型前向传播阶段;
3、SSR/projects/mmdet3d_plugin/SSR/SSR_head.py
完整流程:
调用 build_dataset → 创建数据集 nuscenes_vad_dataset.py:VADCustomNuScenesDataset 数据
调用 build_model → 创建模型 SSR.py:SSR 模型
调用 custom_train_model → 启动训练(projects/mmdet3d_plugin/SSR/apis/train.py) 训练
1、输入
dataset
([VADCustomNuScenesDataset])和model
(SSR)直接传递给 projects/mmdet3d_plugin/SSR/apis/train.py
(custom_train_detector)
2、从
custom_train_detector
→ projects/mmdet3d_plugin/SSR/apis/mmdet_train.py
【/apis/mmdet_train.py
其中包括:数据加载器构建data_loaders
、模型并行化distributed
、优化器build_optimizer
和build_runner
构建以及训练循环启动runner.run
】
2.1 **数据加载器构建:输入:
dataset[0]
(VADCustomNuScenesDataset)经过 函数:build_dataloader
(projects/mmdet3d_plugin/datasets/builder.py)输出:data_loaders[0]
,一个 PyTorch DataLoader 对象,生成批次数据
2.2 模型并行化 : 将模型放到 GPU 上,支持分布式或单机多卡训练
2.3 优化器和 Runner 构建: 优化器根据 cfg.optimizer(如 AdamW)构建;
Runner
: 默认EpochBasedRunner
(mmcv.runner),但可能被 cfg.runner 覆盖为EpochBasedRunner_video
2.4 训练循环启动: 输入:
data_loaders
和cfg.workflow
(通常为 [(‘train’, 1)])开始按 epoch 迭代训练
3、
mmdet_train.py
(runner.run(data_loaders, cfg.workflow)) → SSR/projects/mmdet3d_plugin/SSR/runner/epoch_based_runner.py
(model.train_step) → SSR.py (SSR.forward(return_loss=True, **data_list[-1])
)
self.model
是 SSR 的实例,train_step
是训练的核心步骤。
【按理来说,train_step 应该在 SSR.py 中,但是 SSR.py 却没有定义 。原因如下:SSR
继承自MVXTwoStageDetector
(mmdet3d/models/detectors/mvx_two_stage.py)。
MVXTwoStageDetector 又继承自Base3DDetector
(mmdet3d/models/detectors/base.py)。
在Base3DDetector
中,定义了一个通用的train_step
方法。】
train_step
调用了 self(data),即SSR.forward(return_loss=True, **data)
。
SSR
中重写了 forward 方法**,根据return_loss=True
调用forward_train
一、数据预处理
1. 数据加载入口
# 从 /tools/train.py 开始:
datasets = [build_dataset(cfg.data.train)]
2. 数据集构建
# mmdetection3d/mmdet3d/datasets/builder.py
# 调用builder.py中注册的数据集构建器
def build_dataset(cfg, default_args=None):
# 根据配置构建数据集
dataset = build_from_cfg(cfg, DATASETS, default_args) # 通过注册器找到VADCustomNuScenesDataset
return dataset
3、数据集类实例化
# 通过注册器创建 VADCustomNuScenesDataset 实例
@DATASETS.register_module()
class VADCustomNuScenesDataset(NuScenesDataset):
def __init__(self, queue_length=4, bev_size=(200, 200), ...):
# 初始化数据集参数
4、数据加载器构建
# SSR/projects/mmdet3d_plugin/datasets/builder.py
def build_dataloader(dataset,
samples_per_gpu,
workers_per_gpu,
num_gpus=1,
dist=True,
shuffle=True,
**kwargs):
# 构建数据加载器
# 1. 构建采样器
sampler = build_sampler(...)
# 2. 创建DataLoader
data_loader = DataLoader(
dataset,
batch_size=batch_size,
sampler=sampler,
num_workers=num_workers,
collate_fn=partial(collate, samples_per_gpu=samples_per_gpu),
**kwargs)
数据加载过程中,会调用
VADCustomNuScenesDataset
的prepare_train_data
方法
5、数据预处理流水线
在 /projects/mmdet3d_plugin/datasets/pipelines/
下的处理步骤
# 图像加载和处理 (transform_3d.py)
@PIPELINES.register_module()
class PadMultiViewImage:
"""填充多视角图像"""
def _pad_img(self, results):
# 根据size或size_divisor填充图像
@PIPELINES.register_module()
class NormalizeMultiviewImage:
"""归一化多视角图像"""
def __call__(self, results):
# 对每张图像进行归一化处理
@PIPELINES.register_module()
class PhotoMetricDistortionMultiViewImage:
"""图像光度扭曲增强"""
# 格式化处理 (formating.py)
@PIPELINES.register_module()
class CustomDefaultFormatBundle3D:
"""格式化各种数据为模型可用的张量格式"""
def __call__(self, results):
# 处理ego信息
if self.with_ego:
results['ego_his_trajs'] = DC(to_tensor(...))
results['ego_fut_trajs'] = DC(to_tensor(...))
results['ego_fut_cmd'] = DC(to_tensor(...))
将
numpy
数组转换为PyTorch
张量
使用DataContainer
封装数据,便于分布式训练
统一数据格式,确保模型输入的一致性
# 点云处理 (loading.py)
@PIPELINES.register_module()
class CustomLoadPointsFromFile:
"""加载点云数据"""
@PIPELINES.register_module()
class CustomLoadPointsFromMultiSweeps:
"""加载多帧点云数据"""
加载原始点云数据
处理多帧点云数据的融合
进行点云预处理(如降采样、坐标转换等)
# 数据收集
@PIPELINES.register_module()
class CustomCollect3D:
"""收集并组织最终的训练数据"""
def __call__(self, results):
data = {}
# 1. 收集图像元信息
for key in self.meta_keys:
data['img_metas'] = DC(img_metas, cpu_only=True)
# 2. 收集训练所需的所有数据
for key in self.keys:
data[key] = results[key]
整合所有预处理后的数据
按照模型需要的格式组织数据
区分CPU和GPU数据
最终的数据
完整调用链路:
/tools/train.py
调用mmdetection3d
的build_dataset
mmdetection3d
的build_dataset
通过注册器找到
VADCustomNuScenesDataset
- 创建
VADCustomNuScenesDataset
实例 - 调用
SSR
中的build_dataloader
构建数据加载器 - 在数据加载过程中,会调用
VADCustomNuScenesDataset
的prepare_train_data
方法 prepare_train_data
中会执行预处理pipeline
prepare_train_data
中执行以下步骤:
调用get_data_info
获取原始数据
使用pre_pipeline
进行预处理
执行pipeline
处理流程处理图像等数据
调用vectormap_pipeline
处理地图数据
处理历史帧数据并合并
{
# 1. 图像元信息
"img_metas": DataContainer {
"filename": [6个相机图像路径], # 6个相机视角
"ori_shape": [360, 640, 3], # 原始图像尺寸
"img_shape": [384, 640, 3], # 处理后图像尺寸
"lidar2img": [6个4x4矩阵], # LiDAR到图像的转换矩阵
"pad_shape": [384, 640, 3], # padding后尺寸
"scale_factor": 1.0, # 缩放因子
"box_mode_3d": 0, # 3D框模式
"img_norm_cfg": { # 图像归一化配置
"mean": [123.675, 116.28, 103.53],
"std": [58.395, 57.12, 57.375],
"to_rgb": true
}
},
# 2. 3D检测框标注
"gt_bboxes_3d": DataContainer {
LiDARInstance3DBoxes, # LiDAR坐标系下的3D框
box_dim: 9 # 每个框9个参数(x,y,z,w,l,h,θ,vx,vy)
},
# 3. 标签
"gt_labels_3d": DataContainer { # 3D目标类别标签
torch.Tensor # 类别索引
},
# 4. 图像数据
"img": DataContainer { # 多视角图像数据
torch.Tensor # shape: [B,6,3,384,640]
},
# 5. Ego车辆信息
"ego_his_trajs": DataContainer, # 历史轨迹
"ego_fut_trajs": DataContainer, # 未来轨迹
"ego_fut_masks": DataContainer, # 未来轨迹mask
"ego_fut_cmd": DataContainer, # 导航指令
"ego_lcf_feat": DataContainer, # 局部特征
# 6. 地图信息
"map_gt_labels_3d": DataContainer, # 地图元素标签
"map_gt_bboxes_3d": DataContainer { # 地图元素框
LiDARInstanceLines { # 实例化的线段
"patch_size": [60.0, 30.0], # 区域大小
"max_x": 15.0, # x轴最大范围
"max_y": 30.0, # y轴最大范围
"instance_list": [ # 线段列表
"LINESTRING (...)", # 每条线段的坐标序列
...
]
}
}
}
多模态数据: 6个相机视角的图像、LiDAR点云信息、地图矢量信息、车辆状态信息
时序信息: 历史轨迹、未来轨迹、轨迹mask
坐标系转换: LiDAR到图像的变换矩阵、不同坐标系下的3D框表示
地图表示: 矢量化的地图元素(车道线、路口等)、实例化的线段表示
数据增强配置: 图像归一化参数、数据padding和缩放信息
训练流程
tools/train.py
->SSR/apis/train.py
->SSR/apis/mmdet_train.py
->epoch_based_runner.py
(model.train_step) ->SSR.py
(注册在 @DETECTORS.register_module() 中)
训练主流程(mmdet_train.py
)
def custom_train_detector(model, dataset, cfg, distributed=False, ...):
# 1. 构建数据加载器
data_loaders = [
build_dataloader(
ds,
cfg.data.samples_per_gpu,
cfg.data.workers_per_gpu,
len(cfg.gpu_ids),
dist=distributed,
...
) for ds in dataset
]
# 2. 模型放到GPU上
if distributed:
model = MMDistributedDataParallel(
model.cuda(),
device_ids=[torch.cuda.current_device()],
...
)
# 3. 构建优化器
optimizer = build_optimizer(model, cfg.optimizer)
# 4. 构建runner控制训练流程
runner = build_runner(
cfg.runner, # 这里cfg.runner中指定了runner类型为EpochBasedRunner
default_args=dict(
model=model, # 传入SSR模型实例
optimizer=optimizer,
work_dir=cfg.work_dir,
logger=logger,
meta=meta
))
# 5. 注册训练相关的hooks
runner.register_training_hooks(
cfg.lr_config, # 学习率配置
optimizer_config, # 优化器配置
cfg.checkpoint_config, # 检查点配置
cfg.log_config, # 日志配置
...
)
配置文件中:
runner = dict(type='EpochBasedRunner', max_epochs=total_epochs)
通过RUNNERS
注册器找到我们的自定义Runner
数据经过SSR模型(SSR.py
)
SSR.forward_train
->SSR.obtain_history_bev
->SSR.obtain_next_bev
->SSR.extract_feat
->SSR.forward_pts_train
->SSRHead.forward
->SSRHead.loss
SSR.forward_train
:
forward_train
├── obtain_history_bev (获取历史BEV特征)
│ ├── extract_feat (提取图像特征)
│ └── pts_bbox_head (生成BEV特征) # 实际上是在调用 SSRHead 类的 forward 函数
│
├── obtain_next_bev (获取未来BEV特征)
│ ├── extract_feat
│ └── pts_bbox_head
│
├── extract_feat (提取当前帧特征)
│ ├──extract_img_feat
│
└── forward_pts_train (计算损失)
└── pts_bbox_head (处理当前帧) # 实际上是在调用 SSRHead 类的 forward 函数
SSR_head.py
:
pts_bbox_head (SSRHead)
├── transformer.get_bev_features # (生成BEV特征)
├── tokenlearner # (提取关键tokens)
├── latent_decoder # (场景理解)
├── way_decoder # (轨迹规划)
└── loss # (损失计算)
SSR_transformer.py
:
get_bev_features
├── encoder # (特征编码)
│ └── spatial_cross_attention
├── decoder # (特征解码)
│ └── temporal_self_attention
└── positional_encoding # (位置编码)
tokenlearner.py
:
TokenLearnerV11
├── MlpBlock # (特征变换)
└── TokenFuser # (特征融合)
训练日志
这里加载配置文件以及训练信息
配置文件 : 涵盖了数据集、数据处理流程、模型结构、优化器等信息。
全局参数:点云范围和类别
point_cloud_range = [-15.0, -30.0, -2.0, 15.0, 30.0, 2.0]
class_names = [
'car', 'truck', 'construction_vehicle', 'bus', 'trailer', 'barrier',
'motorcycle', 'bicycle', 'pedestrian', 'traffic_cone'
]
点云范围 :
point_cloud_range
定义了 3D 空间的有效范围(单位:米),格式为[x_min, y_min, z_min, x_max, y_max, z_max]
。这里是X: [-15, 15], Y: [-30, 30], Z: [-2, 2]
,表示模型关注的空间范围。
数据处理流程(Pipelines)
训练流程(train_pipeline
)
train_pipeline = [
dict(type='LoadMultiViewImageFromFiles', to_float32=True),
dict(type='PhotoMetricDistortionMultiViewImage'),
dict(
type='LoadAnnotations3D',
with_bbox_3d=True,
with_label_3d=True,
with_attr_label=True),
dict(
type='CustomObjectRangeFilter',
point_cloud_range=[-15.0, -30.0, -2.0, 15.0, 30.0, 2.0]),
dict(
type='CustomObjectNameFilter',
classes=[
'car', 'truck', 'construction_vehicle', 'bus', 'trailer',
'barrier', 'motorcycle', 'bicycle', 'pedestrian', 'traffic_cone'
]),
dict(
type='NormalizeMultiviewImage',
mean=[123.675, 116.28, 103.53],
std=[58.395, 57.12, 57.375],
to_rgb=True),
dict(type='RandomScaleImageMultiViewImage', scales=[0.4]),
dict(type='PadMultiViewImage', size_divisor=32),
dict(
type='CustomDefaultFormatBundle3D',
class_names=[
'car', 'truck', 'construction_vehicle', 'bus', 'trailer',
'barrier', 'motorcycle', 'bicycle', 'pedestrian', 'traffic_cone'
],
with_ego=True),
dict(
type='CustomCollect3D',
keys=[
'gt_bboxes_3d', 'gt_labels_3d', 'img', 'ego_his_trajs',
'ego_fut_trajs', 'ego_fut_masks', 'ego_fut_cmd', 'ego_lcf_feat',
'gt_attr_labels'
])
]
加载多视角图像:
LoadMultiViewImageFromFiles
从文件中加载多视角相机图像,转换为 32 位浮点数。
图像增强(光度畸变):PhotoMetricDistortionMultiViewImage
应用数据增强
(如亮度、对比度调整)。
加载 3D 标注:LoadAnnotations3D
加载 3D 边界框、标签和属性。
范围过滤(基于点云范围):CustomObjectRangeFilter
过滤超出指定范围的目标。
类别过滤(基于类别范围):CustomObjectNameFilter
保留指定类别的目标。
图像归一化:NormalizeMultiviewImage
按均值和标准差归一化图像(RGB 通道)。
随机缩放被(:RandomScaleImageMultiViewImage
随机缩放图像,比例为0.4
。
填充(保证尺寸是32的倍数):PadMultiViewImage
填充图像,使尺寸能被 32 整除
。
格式化:CustomDefaultFormatBundle3D
将数据格式化为模型输入,包含自车(ego)信息
。
收集数据:CustomCollect3D
收集指定**键(key)**的数据(如3D 边界框、图像、自车轨迹
等)。
测试流程(test_pipeline
)
测试流程增加了点云加载和多尺度增强,适用于推理阶段:
test_pipeline = [
dict(type='LoadMultiViewImageFromFiles', to_float32=True),
dict(type='LoadPointsFromFile', coord_type='LIDAR', ...),
...
dict(type='MultiScaleFlipAug3D', img_scale=(1600, 900), ...)
]
加载点云:
LoadPointsFromFile
加载激光雷达点云数据
(但是use_lidar=False ??)。
多尺度增强:MultiScaleFlipAug3D
在测试时使用多尺度输入(图像尺寸 1600x900)。
评估流程(eval_pipeline
)
eval_pipeline = [
dict(type='LoadPointsFromFile', ...),
dict(type='LoadPointsFromMultiSweeps', sweeps_num=10, ...),
...
]
多帧点云:
LoadPointsFromMultiSweeps
加载10 帧
的点云数据,用于评估。
数据配置(data)
data = dict(
samples_per_gpu=1,
workers_per_gpu=4,
train=dict(...),
val=dict(...),
test=dict(...),
shuffler_sampler=dict(type='DistributedGroupSampler'),
nonshuffler_sampler=dict(type='DistributedSampler'))
批大小:
samples_per_gpu=1
表示每个 GPU 处理 1 个样本。
工作线程:workers_per_gpu=4
表示每个 GPU 使用 4 个线程加载数据。
训练/验证/测试集: 分别定义了train
、val
和test
数据集的参数,包括路径、管道、模态等。
采样器:DistributedGroupSampler
和DistributedSampler
用于分布式训练的数据采样。
小结
数据处理流程(
train_pipeline
、test_pipeline
和eval_pipeline
),Pipelines
是全局模板,而data
是具体实例化。
data
是具体任务的“实例化”,它不仅指定了数据处理流程(通过引用 pipeline),还包括数据集的具体信息:数据集类型(type='VADCustomNuScenesDataset')
,数据根目录(data_root)
,标注文件路径(ann_file)
,其他参数(如 classes、modality、test_mode)
。
data字典中的pipeline会覆盖之前定义的pipeline
中间配置参数(evaluation、checkpoint_config、log_config等)
评估配置(evaluation)
evaluation = dict(
interval=24,
pipeline=[...]
)
评估间隔:
interval=24
表示每24 个
epoch
进行一次评估。
评估流程:pipeline
定义了评估时的数据处理流程,主要加载点云数据(LoadPointsFromFile
和LoadPointsFromMultiSweeps
),格式化后收集点云(points
)。这与eval_pipeline
一致,用于计算指标
。
检查点配置(checkpoint_config)
checkpoint_config = dict(interval=1, max_keep_ckpts=12)
保存间隔:
interval=1
表示每1
个epoch
保存一次模型检查点。
最大保留数量:max_keep_ckpts=12
表示最多保留 12 个检查点,旧的会被覆盖。
日志配置(log_config)
log_config = dict(
interval=100,
hooks=[dict(type='TextLoggerHook'), dict(type='TensorboardLoggerHook')]
)
日志间隔:
interval=100
表示每 100 次
迭代记录一次日志。
日志钩子:
TextLoggerHook
: 将日志输出到文本文件。
TensorboardLoggerHook
: 将日志输出到TensorBoard
可视化工具。
分布式参数(dist_params)
dist_params = dict(backend='nccl')
分布式后端:
backend='nccl'
表示使用NVIDIA
的NCCL 库
进行分布式通信,适用于多 GPU 训练。
日志级别和工作目录
log_level = 'INFO'
work_dir = './save/outputs/'
日志级别:
INFO
表示记录信息级别的日志。
工作目录:work_dir
指定了输出文件(日志、检查点等)的保存路径。
加载和恢复
load_from = None
resume_from = None
加载预训练模型:
load_from=None
表示不从预训练模型加载。
恢复训练:resume_from=None
表示不从已有检查点恢复训练。
工作流程(workflow)
workflow = [('train', 1)]
训练流程: 表示只执行训练阶段
(train)
,循环1 次
。通常可以包括 val 或 test,但这里仅训练。
插件(plugin)和体素大小
plugin = True
plugin_dir = 'projects/mmdet3d_plugin/'
voxel_size = [0.15, 0.15, 4]
插件支持:
plugin=True
表示启用自定义插件,路径为plugin_dir
。
体素大小:voxel_size
定义了点云体素化的分辨率(X: 0.15m, Y: 0.15m, Z: 4m)
。
图像归一化和类别
img_norm_cfg = dict(mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
num_classes = 10
map_classes = ['divider', 'ped_crossing', 'boundary']
图像归一化: 定义了 RGB 图像的均值和标准差,用于标准化。
目标类别数:num_classes=10
对应 10 个检测类别。
地图类别:map_classes
定义了 3 个地图元素类别。
其他参数
map_num_vec = 100
map_fixed_ptsnum_per_gt_line = 20
map_fixed_ptsnum_per_pred_line = 20
map_eval_use_same_gt_sample_num_flag = True
map_num_classes = 3
_dim_ = 256
_pos_dim_ = 128
_ffn_dim_ = 512
_num_levels_ = 1
bev_h_ = 100
bev_w_ = 100
queue_length = 3
total_epochs = 12
地图参数: 定义了
map_num_vec = 100
(地图向量的数量,例如,检测 100 条车道线或路沿)、map_fixed_ptsnum_per_gt_line = 20
(每个地图向量(如一条车道线)用 20 个点来表示)、评估设置、map_num_classes=3
: 地图元素的类别数量(例如,车道线、分隔带、路沿)。等。
模型维度:_dim_=256
(嵌入维度)、_pos_dim_=128
(位置编码维度)、_ffn_dim_=512
(前馈网络维度)。
BEV 和队列:bev_h_
和bev_w_
定义了BEV分辨率(100x100)
,queue_length=3
表示时间队列长度。
总 epoch 数:total_epochs=12
表示训练 12 个 epoch。
模型配置(model)
整体流程:
(多摄像头图像 + 车辆状态
(can_bus)
) ---->
[ResNet + FPN]
提取各视角图像特征 ---->
[BEVFormer Encoder]
融合成统一的BEV
特征图 (包含时空信息) ---->
[SSR Head (含 Decoder 和任务头)]
使用query向量
在BEV 图
上进行 “查找” 和 “解码”,并行地进行 3D 物体检测、未来轨迹预测、高清地图元素(如车道线)的矢量化构建 ---->
同时输出 {(物体检测结果 + 轨迹), (地图元素向量), (规划路径)
}
模型采用多种损失函数(
分类、回归、IoU、点损失、方向损失、规划约束损失
等)进行端到端的训练,并使用匈牙利算法进行预测与 GT 之间的匹配。配置中还包括了数据增强、预训练权重加载、特定模块(如 BEV 特征、潜在世界模型)的监督等细节
解码器和任务头:
查询向量
与BEV 特征图
进行交互(通过 Transformer 解码器层)。查询向量会“关注”BEV 特征图中的相关区域,并逐渐吸收信息,变得特定化。例如,某个obejct query会逐渐学会代表场景中的一辆特定汽车。
经过解码器精炼后的查询向量
,会被送入各自的、小的预测网络
(这就是任务头,通常是几层全连接层)
图像特征提取
img_backbone=dict(
type='ResNet',
depth=50,
num_stages=4,
out_indices=(3, ),
frozen_stages=1,
...)
img_neck=dict(
type='FPN',
in_channels=[2048],
out_channels=256,
...)
输出:只输出一个尺度的特征图
[B, N, 256, H/16, W/16]
【只处理 conv4 的输出,并通过额外卷积(add_extra_convs=‘on_output’)将通道数降至 256】
核心处理头:pts_bbox_head
【处理转换后的 BEV 特征并输出最终结果(如物体框、轨迹、地图元素)的核心模块】
pts_bbox_head=dict(
type='SSRHead',
map_thresh=0.5,
dis_thresh=0.2,
pe_normalization=True,
tot_epoch=12,
use_traj_lr_warmup=False,
query_thresh=0.0,
query_use_fix_pad=False,
ego_his_encoder=None,
ego_lcf_feat_idx=None,
valid_fut_ts=6,
latent_decoder=dict(...), # MultiheadAttention
way_decoder=dict(...), # cross_attn 用于路径或轨迹的生成/解码
use_pe=True, # 使用位置编码
bev_h=100,
bev_w=100,
num_query=300, # 用于物体检测/预测的查询向量数量。
num_classes=10, # 物体检测任务需要区分的类别数量(例如,汽车、行人、自行车等)
in_channels=256,
sync_cls_avg_factor=True, # 在使用多 GPU 进行分布式训练时,同步分类损失的归一化因子,确保不同 GPU 上的损失计算一致。
with_box_refine=True, # 在 Transformer 解码器的不同层之间迭代地优化预测的边界框
as_two_stage=False,
map_num_vec=100, # 用于地图构建的查询向量数量(例如,检测 100 条车道线或路沿)
map_num_classes=3, # 地图元素的类别数量(例如,车道线、分隔带、路沿)
map_num_pts_per_vec=20, # 每个地图向量(如一条车道线)用 20 个点来表示
map_num_pts_per_gt_vec=20, # 真实标注(Ground Truth)的地图向量也用 20 个点表示
map_query_embed_type='instance_pts', # 地图查询向量的嵌入方式,基于实例的点。
map_transform_method='minmax', # 地图点坐标的归一化方法
map_gt_shift_pts_pattern='v2', # 对 GT 地图点进行扰动的方式(用于数据增强或匹配)
map_dir_interval=1, # 计算地图向量方向时使用的点间隔
map_code_size=2, # 地图向量编码的大小(可能指每个点的 x, y 坐标)
map_code_weights=[1.0, 1.0, 1.0, 1.0],
transformer=dict(...),
bbox_coder=dict(...),
map_bbox_coder=dict(...),
positional_encoding=dict(...),
loss_all=dict(...) # 各类损失【】
...)
SSRHead
是自定义检测头,负责 3D 目标检测和地图预测。
阈值:map_thresh=0.5
地图预测的分类阈值。dis_thresh=0.2
距离阈值,可能用于匹配。
位置编码:pe_normalization=True
表示对位置编码进行归一化。
训练参数:tot_epoch=12
与总epoch
一致,use_traj_lr_warmup=False
表示不使用轨迹学习率预热。
查询参数:query_thresh=0.0
和query_use_fix_pad=False
控制查询生成。
自车特征 :ego_his_encoder=None
和ego_lcf_feat_idx=None
表示不使用特定的自车历史编码器。
未来时间步**:valid_fut_ts=6
表示预测未来 6 帧
。
BEVFormer编码器
transformer=dict(
type='SSRPerceptionTransformer',
map_num_vec=100,
map_num_pts_per_vec=20,
rotate_prev_bev=False,
use_shift=True,
use_can_bus=True,
embed_dims=256,
encoder=dict(
type='BEVFormerEncoder',
num_layers=3,
pc_range=[-15.0, -30.0, -2.0, 15.0, 30.0, 2.0],
num_points_in_pillar=4,
return_intermediate=False,
transformerlayers=dict(...)),
...)
3层
编码器,将多视角图像特征转换为BEV表示
map_bbox_coder (地图边界框编码/解码器)
map_bbox_coder=dict(
type='MapNMSFreeCoder',
post_center_range=[-20, -35, -20, -35, 20, 35, 20, 35],
pc_range=[-15.0, -30.0, -2.0, 15.0, 30.0, 2.0],
max_num=50,
voxel_size=[0.15, 0.15, 4],
num_classes=3),
类似
bbox_coder
,但用于处理地图元素的输出。参数类似,但post_center_range
和max_num
不同,适配地图元素的特性。
type='MapNMSFreeCoder'
: 针对地图元素的无 NMS 解码器。
positional_encoding (位置编码)
positional_encoding=dict(
type='LearnedPositionalEncoding',
num_feats=128, # 位置编码的特征维度的一半(总维度是 256,因为通常 x 和 y 方向分开编码再拼接)
row_num_embed=100,
col_num_embed=100), # BEV 特征图的高度和宽度,用于生成对应的可学习编码
Loss Functions (损失函数): 定义了模型训练时使用的各种损失函数及其权重
loss_cls=dict(
type='FocalLoss',
use_sigmoid=True,
gamma=2.0,
alpha=0.25,
loss_weight=2.0),
loss_bbox=dict(type='L1Loss', loss_weight=0.25),
loss_traj=dict(type='L1Loss', loss_weight=0.2),
loss_traj_cls=dict(
type='FocalLoss',
use_sigmoid=True,
gamma=2.0,
alpha=0.25,
loss_weight=0.2),
loss_iou=dict(type='GIoULoss', loss_weight=0.0),
loss_map_cls=dict(
type='FocalLoss',
use_sigmoid=True,
gamma=2.0,
alpha=0.25,
loss_weight=2.0),
loss_map_bbox=dict(type='L1Loss', loss_weight=0.0),
loss_map_iou=dict(type='GIoULoss', loss_weight=0.0),
loss_map_pts=dict(type='PtsL1Loss', loss_weight=1.0),
loss_map_dir=dict(type='PtsDirCosLoss', loss_weight=0.005),
loss_plan_reg=dict(type='L1Loss', loss_weight=1.0),
loss_plan_bound=dict(
type='PlanMapBoundLoss', loss_weight=1.0, dis_thresh=1.0),
loss_plan_col=dict(type='PlanCollisionLoss', loss_weight=1.0),
loss_plan_dir=dict(type='PlanMapDirectionLoss', loss_weight=0.5)),
loss_cls: 物体分类损失,使用
Focal Loss
(解决类别不平衡问题)。权重为 2.0。
loss_bbox: 物体边界框回归损失
,使用L1 Loss
(预测框和 GT 框的 L1 距离)。权重为 0.25。
loss_traj: 物体未来轨迹点回归损失
,使用L1 Loss
。权重为 0.2。
loss_traj_cls: 物体未来轨迹点的分类损失
(可能用于预测轨迹是否存在或轨迹点的有效性),使用 Focal Loss。权重为 0.2。
loss_iou: 物体边界框的IoU 损失
,使用GIoU Loss
。权重为 0.0,表示未使用。
loss_map_cls:地图元素分类损失
,使用Focal Loss
。权重为 2.0。
loss_map_bbox:地图元素边界框回归损失
(如果地图元素用框表示),使用 L1 Loss。权重为 0.0,表示未使用。
loss_map_iou:地图元素 IoU 损失
,使用GIoU Loss
。权重为 0.0,表示未使用。
loss_map_pts: 地图元素上的点坐标回归损失
,使用PtsL1Loss
(可能是 L1 Loss 的变种)。权重为 1.0。
loss_map_dir:地图元素方向损失
,使用PtsDirCosLoss
(基于点计算方向向量,然后用余弦相似度计算损失)。权重为 0.005。
loss_plan_reg: 规划轨迹点回归损失
,使用L1 Loss
。权重为 1.0。
loss_plan_bound: 规划轨迹边界约束损失
(PlanMapBoundLoss),惩罚轨迹超出地图边界(如车道线)。权重为 1.0,dis_thresh=1.0 是距离阈值。
loss_plan_col: 规划轨迹碰撞损失
(PlanCollisionLoss),惩罚规划轨迹与检测到的物体发生碰撞。权重为 1.0。
loss_plan_dir: 规划轨迹方向损失
(PlanMapDirectionLoss),鼓励规划轨迹与地图方向(如车道线方向)保持一致。权重为 0.5。
latent_world_model (潜在世界模型)
latent_world_model=dict(
type='CustomTransformerDecoder',
num_layers=2,
return_intermediate=False,
transformerlayers=dict(
type='BaseTransformerLayer',
attn_cfgs=[
dict(type='MultiheadAttention', embed_dims=256, num_heads=8)
],
feedforward_channels=512,
operation_order=('self_attn', 'norm', 'ffn', 'norm'))),
接收当前的潜在状态(如 BEV 特征或查询向量)并预测未来的潜在状态
loss_bev (BEV 损失)
loss_bev=dict(type='MSELoss', loss_weight=1.0),
使用均方误差损失 (
MSELoss
),权重为 1.0。一种辅助损失,用于监督中间生成的 BEV 特征图
,让它更接近某个目标(例如,通过某种方式生成的 GT BEV 表示),以帮助模型学习更好的 BEV 表征。
train_cfg (训练配置)
train_cfg=dict(
pts=dict( # 再次定义了 BEV 空间和体素化参数,用于训练时的 GT 处理和匹配。
grid_size=[512, 512, 1],
voxel_size=[0.15, 0.15, 4],
point_cloud_range=[-15.0, -30.0, -2.0, 15.0, 30.0, 2.0],
out_size_factor=4, # 输出特征图相对于输入图像的尺寸缩小因子
assigner=dict( # 预测物体
type='HungarianAssigner3D', # 使用匈牙利算法进行最优匹配,将预测的物体框(来自 num_query 个查询)与 GT 物体框进行一对一匹配
cls_cost=dict(type='FocalLossCost', weight=2.0), # 分类匹配成本,使用 Focal Loss 计算。权重 2.0。
reg_cost=dict(type='BBox3DL1Cost', weight=0.25), # 边界框回归匹配成本,使用 3D BBox L1 Cost 计算。权重 0.25
iou_cost=dict(type='IoUCost', weight=0.0), # IoU 匹配成本,使用 IoU Cost 计算。权重 0.0 (未使用)
pc_range=[-15.0, -30.0, -2.0, 15.0, 30.0, 2.0]), # 匹配时使用的坐标范围
map_assigner=dict( # 预测地图
type='MapHungarianAssigner3D',
cls_cost=dict(type='FocalLossCost', weight=2.0),
reg_cost=dict(
type='BBoxL1Cost', weight=0.0, box_format='xywh'),
iou_cost=dict(type='IoUCost', iou_mode='giou', weight=0.0),
pts_cost=dict(type='OrderedPtsL1Cost', weight=1.0),
pc_range=[-15.0, -30.0, -2.0, 15.0, 30.0, 2.0]))))
这部分包含了只在训练阶段使用的参数,特别是关于如何将模型预测与真实标签 GT 进行匹配以计算损失(即
Assigners
)
训练策略(优化器和学习率)
optimizer = dict(
type='AdamW',
lr=5e-05,
paramwise_cfg=dict(custom_keys=dict(img_backbone=dict(lr_mult=0.1))),
weight_decay=0.01)
optimizer_config = dict(grad_clip=dict(max_norm=35, norm_type=2))
# 学习率在训练过程中动态调整(余弦退火 (Cosine Annealing))
lr_config = dict(
policy='CosineAnnealing',
warmup='linear',
warmup_iters=500, # 预热阶段持续 500 次迭代 (iterations/steps)
warmup_ratio=0.3333333333333333, # 预热阶段的起始学习率是目标基础学习率 (optimizer['lr']) 的 1/3
min_lr_ratio=0.001) # 当余弦退火结束时(即训练结束时),学习率达到的最小值是基础学习率的 0.001 倍。这确保学习率不会完全降到 0。
# 训练循环的类型和总时长
runner = dict(type='EpochBasedRunner', max_epochs=12)
find_unused_parameters = True # 查找未使用的参数---PyTorch 的 DDP (分布式训练) 功能会在反向传播(计算梯度)时检查模型中是否存在某些参数在前向传播中没有被用到(因此没有梯度)
'''
钩子 (Hook) 是一种机制,允许在训练/评估过程中的特定时间点(如每个 Epoch 开始/结束,每次迭代之前/之后)插入并执行自定义的操作
'''
custom_hooks = [
dict(type='CustomSetEpochInfoHook'),
dict(type='MEGVIIEMAHook', init_updates=10560, priority='NORMAL') # 指定在训练开始的前 10560 次参数更新 (updates/iterations) 内不计算 EMA,之后才开始
]
gpu_ids = range(0, 4)
paramwise_cfg=dict(custom_keys=dict(img_backbone=dict(lr_mult=0.1)))
: 参数化配置。这允许为模型的不同部分设置不同的学习率。custom_keys
: 定义自定义规则。
img_backbone=dict(lr_mult=0.1)
: 这条规则表示,对于模型中名字包含 “img_backbone
” 的参数,其实际学习率将是基础学习率 lr 乘以lr_mult (0.1)
。也就是图像主干网络的学习率只有0.00005 * 0.1 = 0.000005
。这么做的原因是图像主干网络加载了预训练权重,通常只需要进行微调 (fine-tuning),使用较小的学习率可以避免破坏已经学到的知识。
weight_decay=0.01
: 设置权重衰减系数为 0.01。权重衰减是一种正则化技术,通过在损失函数中添加一个惩罚项来限制模型权重的大小,有助于防止模型过拟合。
整体架构流程
流程总结
- 输入: [B, N, 3, H, W] → img_backbone →
[B, N, 2048, H/32, W/32]
. - 特征融合: img_neck →
[B, N, 256, H/32, W/32]
. - BEV 转换: transformer.encoder →
[B, 256, 100, 100]
. - 查询生成: latent_decoder 和 way_decoder →
[B, 300, 256](目标)+ [B, 100, 256](地图)
。 - 预测: 分支输出分类和回归结果。
- 损失: 使用
FocalLoss
、L1Loss
等计算误差。
顶层结构:SSR
SSR(
(pts_bbox_head): SSRHead(...),
(img_backbone): ResNet(...),
(img_neck): FPN(...),
(grid_mask): GridMask(), # 数据增强模块
(tokenfuser): TokenFuser(...), # Token融合模块
(latent_world_model): CustomTransformerDecoder(...),
(loss_bev): MSELoss() # BEV 特征的均方误差损失
)
latent_decoder 查询生成与解码
(latent_decoder): CustomTransformerDecoder(
(layers): ModuleList(
(0-2): BaseTransformerLayer(
(attentions): MultiheadAttention(...),
(ffns): FFN(...),
(norms): LayerNorm(...)
)
)
),
(way_decoder): CustomTransformerDecoder(
(layers): ModuleList(
(0): BaseTransformerLayer(...)
)
)
输入: BEV 特征
[B, 256, 100, 100]
+ 查询嵌入(query_embedding
,map_instance_embedding
)。
输出:
目标查询特征[B, 300, 256]
。
地图查询特征[B, 100, 256]
。
latent_decoder
: 3 层Transformer
,生成 300 个目标查询(num_query=300
)。
way_decoder
: 1 层Transformer
,生成 100 个地图查询(map_num_vec=100
)。
预测分支
(cls_branches): ModuleList(
(0): Sequential(
(0-5): Linear → LayerNorm → ReLU,
(6): Linear(in_features=256, out_features=10)
)
),
(reg_branches): ModuleList(
(0): Sequential(
(0-3): Linear → ReLU,
(4): Linear(in_features=256, out_features=10)
)
),
(map_cls_branches): ModuleList(...out_features=3),
(map_reg_branches): ModuleList(...out_features=2)
输入: 查询特征
[B, 300, 256]
(物体)和[B, 100, 256]
(地图)。
输出:
目标预测:[B, 300, 10]
(分类)+[B, 300, 10]
(回归)。
地图预测:[B, 100, 3]
(分类)+[B, 100, 20, 2]
(回归,每向量 20 个点)。
cls_branches:
目标分类,输出 10 类 logits(num_classes=10)。
reg_branches
: 目标回归,输出10 维边界框参数
(x, y, z, w, l, h, yaw
等)。
map_cls_branches
: 地图分类,输出 3 类 logits(map_num_classes
=3)。
map_reg_branches
: 地图回归,输出 2 维坐标(x, y)。
技术名词解释
提示:这里可以添加技术名词解释
例如:
- Bert
- GPT 初代
- GPT-2
- GPT-3
- ChatGPT
技术细节
提示:这里可以添加技术细节
例如:
- API
- 支持模型类型
小结
提示:这里可以添加总结
例如:
提供先进的推理,复杂的指令,更多的创造力。