10.3.5 推理
经过前面的这些编码工作,我们现在可以使用这个基线模型进行车辆轨迹的预测工作。
(1)下面代码用于配置测试集的数据加载器和数据集。首先加载测试数据集的配置信息,并构建相应的Rasterizer。然后,通过ChunkedDataset加载测试数据,同时使用预定义的掩码(mask)对部分车辆进行过滤,最终创建AgentDataset对象。通过DataLoader设置测试数据加载器,以便后续进行推理测试,并输出测试数据集的信息。
test_cfg = cfg["test_data_loader"]
# Rasterizer
rasterizer = build_rasterizer(cfg, dm)
# Test dataset/dataloader
test_zarr = ChunkedDataset(dm.require(test_cfg["key"])).open()
test_mask = np.load(f"{PATH_TO_DATA}/scenes/mask.npz")["arr_0"]
test_dataset = AgentDataset(cfg, test_zarr, rasterizer, agents_mask=test_mask)
test_dataloader = DataLoader(test_dataset,
shuffle=test_cfg["shuffle"],
batch_size=test_cfg["batch_size"],
num_workers=test_cfg["num_workers"])
print(test_dataset)
执行后会输出:
+------------+------------+------------+-----------------+----------------------+----------------------+----------------------+---------------------+
| Num Scenes | Num Frames | Num Agents | Total Time (hr) | Avg Frames per Scene | Avg Agents per Frame | Avg Scene Time (sec) | Avg Frame frequency |
+------------+------------+------------+-----------------+----------------------+----------------------+----------------------+---------------------+
| 11314 | 1131400 | 88594921 | 31.43 | 100.00 | 78.31 | 10.00 | 10.00 |
+------------+------------+------------+-----------------+----------------------+----------------------+----------------------+---------------------+
(2)定义运行预测的函数run_prediction,接收一个训练好的模型(predictor)和测试数据加载器(data_loader)。在预测过程中,模型对测试数据进行前向传播,得到目标坐标和置信度。如果配置中启用了图像坐标(image_coords),则进行相应的坐标转换。预测结果被存储在各自的列表中,并最终以NumPy数组的形式返回时间戳、轨迹ID、坐标和置信度。函数使用tqdm库显示预测进度。
def run_prediction(predictor, data_loader):
predictor.eval()
pred_coords_list = []
confidences_list = []
timestamps_list = []
track_id_list = []
with torch.no_grad():
dataiter = tqdm(data_loader)
for data in dataiter:
image = data["image"].to(device)
inputs = data["image"].to(device)
target_availabilities = data["target_availabilities"].to(device)
targets = data["target_positions"].to(device)
matrix = data["world_to_image"].to(device)
centroid = data["centroid"].to(device)[:,None,:].to(torch.float)
pred, confidences = predictor(image)
if cfg['test_params']['image_coords']:
matrix_inv = torch.inverse(matrix)
pred = pred + bias[:,None,:,:]
pred = torch.cat([pred,torch.ones((bs,3,tl,1)).to(device)], dim=3)
pred = torch.stack([torch.matmul(matrix_inv.to(torch.float), pred[:,i].transpose(1,2))
for i in range(3)], dim=1)
pred = pred.transpose(2,3)[:,:,:,:2]
pred = pred - centroid[:,None,:,:]
pred_coords_list.append(pred.cpu().numpy().copy())
confidences_list.append(confidences.cpu().numpy().copy())
timestamps_list.append(data["timestamp"].numpy().copy())
track_id_list.append(data["track_id"].numpy().copy())
timestamps = np.concatenate(timestamps_list)
track_ids = np.concatenate(track_id_list)
coords = np.concatenate(pred_coords_list)
confs = np.concatenate(confidences_list)
return timestamps, track_ids, coords, confs
(3)下面的代码片段用于根据LOAD_MODEL标志选择是否加载预训练模型。如果LOAD_MODEL为True,它会创建一个新的LyftModel实例,加载之前保存的模型权重,并将模型移动到可用的计算设备(GPU或CPU)。用户需要设置saved_model_path变量,指定保存的模型路径。
LOAD_MODEL = False
if LOAD_MODEL:
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
predictor = LyftModel(cfg)
saved_model_path = ""
predictor.load_state_dict(torch.load(saved_model_path))
predictor.to(device)
(4)根据INFER标志选择是否执行推理。如果INFER为True,它调用run_prediction函数获取测试集上的模型预测结果,然后使用write_pred_csv将时间戳、轨迹ID、坐标和置信度保存为CSV文件(submission.csv)。
from l5kit.evaluation import write_pred_csv
INFER = False
if INFER:
timestamps, track_ids, coords, confs = run_prediction(predictor, test_dataloader)
write_pred_csv('submission.csv',timestamps=timestamps,
track_ids=track_ids, coords=coords, confs=confs)
6. 训练参数探索
(1)首先看看在配置文件中哪些参数与训练相关,具体实现代码如下所示。
train_dataset[0].keys()
执行后会输出:
dict_keys(['image', 'target_positions', 'target_yaws', 'target_availabilities', 'history_positions', 'history_yaws', 'history_availabilities', 'world_to_image', 'track_id', 'timestamp', 'centroid', 'yaw', 'extent'])
(2)定义函数sample_dataset,功能是使用配置文件和本地数据管理器构建Rasterizer,然后创建了一个代理数据集(AgentDataset)对象。最后,返回数据集中第100个样本的信息,包括图像、目标位置等。
def sample_dataset():
rasterizer = build_rasterizer(cfg, dm)
train_dataset = AgentDataset(cfg, train_zarr, rasterizer)
return train_dataset[100]
(3)绘制了六幅图像,每幅图像的Raster尺寸不同。通过循环遍历不同的Raster尺寸(sizes列表),调用sample_dataset函数获取对应Raster尺寸的图像样本,并在图中显示,如图10-8所示。最后,将Raster尺寸重置为默认值[224, 224]。
sizes = [[200, 200], [224, 224], [250, 250], [350, 350], [450, 450], [500, 500]]
f, ax = plt.subplots(2, 3, figsize=(20, 12))
ax = ax.flatten()
for i in range(6):
cfg['raster_params']['raster_size'] = sizes[i]
sample = sample_dataset()
ax[i].imshow(sample['image'][-3:].transpose(1, 2, 0))
ax[i].get_xaxis().set_visible(False)
ax[i].get_yaxis().set_visible(False)
ax[i].set_title(f"Raster size: {sizes[i]}")
#reset to default
cfg['raster_params']['raster_size'] = [224, 224]
图10-8 绘制的六幅图像
(4)下面的代码绘制了六幅图像,每幅图像的像素尺寸(pixel size)不同,如图10-9所示。通过循环遍历不同的像素尺寸(sizes列表),调用sample_dataset函数获取对应像素尺寸的图像样本,并在图中显示。最后,将像素尺寸重置为默认值[0.5, 0.5]。
sizes = [[.2, .2,], [.3, .3], [.4, .4], [.5, .5], [.6, .6], [.7, .7]]
f, ax = plt.subplots(2, 3, figsize=(20, 12))
ax = ax.flatten()
for i in range(6):
cfg['raster_params']['pixel_size'] = sizes[i]
sample = sample_dataset()
ax[i].imshow(sample['image'][-3:].transpose(1, 2, 0))
ax[i].get_xaxis().set_visible(False)
ax[i].get_yaxis().set_visible(False)
ax[i].set_title(f"Pixel size: {sizes[i]}")
#reset to default
cfg['raster_params']['pixel_size'] = [0.5, 0.5]
图10-9 重设像素后的六幅图像
如果pixel_size = [.5, .5],这意味着最多能够捕捉大小不小于半米的物体:如果物体小于半米,则不会被检测为像素,因此要查看这些物体需要更高的分辨率(较低的 pixel_size)。会发现由 pixel_size 定义的栅格化过程会存在一些固有的不准确性,每个历史位置、车道和其他代理都被编码为像素,如果栅格的 pixel_size = .5,应该期望每个预测位置的每个方向有 .5/4 的平均误差,这可以通过使用较小的 pixel_size 来解决这个问题。例如下面的代码绘制了四幅图像,每幅图像的Raster尺寸和像素尺寸均不同,如图10-10所示。通过循环遍历不同的Raster尺寸和像素尺寸组合(sizes列表),调用sample_dataset函数获取对应的图像样本,并在图中显示。最后,将Raster尺寸和像素尺寸重置为默认值[224, 224]和[0.5, 0.5]。
sizes = [[[200, 200],[.5, .5]], [[250, 250],[.4, .4]], [[350, 350],[.3, .3]], [[500, 500],[.2, .2]]]
f, ax = plt.subplots(1, 4, figsize=(20, 12))
ax = ax.flatten()
for i in range(4):
cfg['raster_params']['pixel_size'] = sizes[i][1]
cfg['raster_params']['raster_size'] = sizes[i][0]
sample = sample_dataset()
ax[i].imshow(sample['image'][-3:].transpose(1, 2, 0))
ax[i].get_xaxis().set_visible(False)
ax[i].get_yaxis().set_visible(False)
ax[i].set_title(f"Raster/Pixel size: {sizes[i]}")
#reset to default
cfg['raster_params']['raster_size'] = [224, 224]
图10-10 4幅图像