Driving SDK负载均衡特性
自动驾驶场景中,涉及多种传感器数据和不同的道路交通环境,导致自动驾驶模型易出现计算负载不均衡的现象,造成性能瓶颈。
Driving SDK实现了自动驾驶模型负载均衡特性,通过负载均衡策略,缓解计算负载不均衡瓶颈,提升模型性能。
背景
自动驾驶模型的数据集通常为多传感器数据,包含点云、雷达数据、照相机图像等。
根据模型任务的差异,不同道路场景下的数据量差别较大,例如,QCNet模型是一种用于轨迹预测的神经网络架构,使用Argoverse2数据集学习轨迹特征。数据集采用Agents表达道路场景中的交通参与目标,不同道路上的Agents数量差异较大,在分布式训练中造成了节点负载不均衡的现象,为了等待负载最大的节点,其他节点常常进入通信等待阶段,增加了额外的训练开销。
具有类似负载不均衡的模型还有Deformable DETR、CenterPoint3D等,因此,通过对模型进行动态的负载均衡处理,能够让分布式训练的各节点具有相近的负载,避免过多通信等待开销,提升模型性能。
特性介绍
1. Dynamic Dataset
Driving SDK负载均衡特性提供DynamicDataset抽象类,该类包含sorting和bucketing两个抽象方法。
-
sorting抽象类方法 根据模型中造成负载不均衡瓶颈的主要元素,对元素进行排序。 -
bucketing抽象类方法 根据sorting方法结果,对数据集样本进行分桶。
用户可以自行继承该抽象类,实现抽象类方法,同时,Driving SDK提供两个继承DynamicDataset的子类,均已实现bucketing类方法。
-
UniformBucketingDynamicDataset该类需传入num_bucket参数,作为分桶总量,数据集样本将按照sorting方法结果,均匀分布到所有桶内。 -
CapacityBucketingDynamicDataset该类需传入bucket_capacity参数,作为桶容量,数据集样本将按照sorting方法结果,每个桶中分配bucket_capacity个样本。
2. Dynamic Sampler
Driving SDK负载均衡特性提供DynamicSampler抽象类,该类包含bucket_arange抽象方法。
bucket_arange抽象类方法 根据DynamicDataset类bucketing方法结果,对分桶结果进行随机化。
用户可以自行继承该抽象类,实现抽象类方法,同时,Driving SDK提供两个继承DynamicSampler的子类,均已实现bucket_arange类方法。
-
DynamicDistributedSampler该类进行两次随机化处理: 1)对样本桶进行随机化 2)在样本桶内,对桶内数据进行随机化 适用于UniformBucketingDynamicDataset的分桶结果随机化。 -
ReplicasDistributedSampler该类进行三次随机化处理: 1)对样本桶进行随机化 2)在样本桶内,对桶内数据进行随机化 3)根据分布式训练的节点数,将桶内数据均匀分布到每个节点上,并再次进行随机化 适用于CapacityBucketingDynamicDataset的分桶结果随机化。
3. Dynamic Transforms
包含负载均衡预处理的函数。
使用方法
以CenterPoint3D模型为例,介绍Driving SDK负载均衡特性的使用方法。
1. 确认引入负载不均衡瓶颈的元素
CenterPoint3D的数据集为点云,在模型训练过程中,首先会将点云转换为voxel张量,模型对voxel张量的尺寸敏感,当voxel较大时,模型计算耗时长,反之则耗时短。因此,确认引入负载不均衡瓶颈的元素为点云转换获得的voxel张量尺寸。
2. 按照voxel尺寸进行排序(sorting)
按照model_examples/CenterPoint/README.md中准备数据集一节生成数据集所需的序列化文件。在生成代码中,以下代码用于获取数据集样本的voxel张量尺寸。
from mx_driving.dataset.utils import get_voxel_number_from_mean_vfe
voxel_num = get_voxel_number_from_mean_vfe(data_path, ref_sd_rec['filename'], sweeps, max_sweeps)
info["voxel_num"] = voxel_num
其中,get_voxel_number_from_mean_vfe函数位于dataset/utils/dynamic_transform.py中,用户无需再对模型进行过多侵入式修改,即可获取voxel张量尺寸。
对模型文件pcdet/datasets/nuscenes/nuscenes_dataset.py进行修改,原代码为:
class NuScenesDataset(DatasetTemplate):
def __init__(self, dataset_cfg, class_names, training=True, root_path=None, logger=None):
root_path = (root_path if root_path is not None else Path(dataset_cfg.DATA_PATH)) / dataset_cfg.VERSION
super().__init__(
dataset_cfg=dataset_cfg, class_names=class_names, training=training, root_path=root_path, logger=logger)
...
if self.training and self.dataset_cfg.get('BALANCED_RESAMPLING', False):
self.infos = self.balanced_infos_resampling(self.infos)
修改后为:
from mx_driving.dataset import PointCloudDynamicDataset
class NuScenesDataset(PointCloudDynamicDataset, DatasetTemplate):
def __init__(self, dataset_cfg, class_names, training=True, root_path=None, logger=None):
root_path = (root_path if root_path is not None else Path(dataset_cfg.DATA_PATH)) / dataset_cfg.VERSION
DatasetTemplate.__init__(
self, dataset_cfg=dataset_cfg, class_names=class_names, training=training, root_path=root_path, logger=logger
)
...
if self.training and self.dataset_cfg.get('BALANCED_RESAMPLING', False):
self.infos = self.balanced_infos_resampling(self.infos)
if training:
self.sorting()
self.buckets = self.bucketing(bucket_capacity=8)
else:
self.buckets = None
NuScenesDataset在继承原有父类的基础上,还继承了PointCloudDynamicDataset类,因此可以直接调用类中的sorting和bucketing函数。
3. 对bucketing结果进行随机化
对模型文件pcdet/datasets/__init__.py进行修改,原代码为:
sampler = torch.utils.data.distributed.DistributedSampler(dataset)
修改后为:
from mx_driving.dataset import ReplicasDistributedSampler
sampler = ReplicasDistributedSampler(dataset)
完成修改后,模型即可正常训练,基于负载均衡特性的训练性能比未使用负载均衡特性的性能提升18%。