首页 / 资讯中心 / 文章详情

Social GAN多模态行人轨迹预测:社会池化与复现实战

Social GAN多模态行人轨迹预测:社会池化与复现实战 ★ FEATURED ARTICLE
行人轨迹预测这个方向我这两年陆陆续续跟了不少工作从 Social LSTM 一路读到 Social GAN中间踩过的坑和绕过的弯不算少。Social GAN 是我认为把“多模态”这件事真正讲清楚的一篇代表工作标题里的 “Socially Acceptable Trajectories” 四个字其实已经把它的核心诉求说透了——预测出来的轨迹不光要准还得在社会规范上说得过去不能一头扎进人群里。本文围绕这篇论文讲的三件事展开一是为什么要用生成对抗网络来做轨迹预测二是它的生成器、判别器和社会池化模块到底是怎么搭起来的三是照着论文复现时最容易翻车的地方在哪、怎么绕过去。整篇内容偏工程视角适合做行人/车辆轨迹预测、机器人局部导航、自动驾驶感知下游模块的同学参考刚入门的朋友也能顺着读下来因为我会把损失函数、社会池化、模式崩塌这些容易绕晕的东西拆到能上手为止。1. 为什么行人轨迹预测需要 Social GAN1.1 从 Social LSTM 看 L2 损失的“平均化”困境只要你看过 Social LSTM就会明白为什么后续那么多工作都在“多模态”这个词上做文章。Social LSTM 的核心思路是在每个行人的 LSTM 之间加一个社会池化层用它把周围邻居的隐状态聚合进来从而让每个人的预测都“看得见”别人。这个设计在当年是相当漂亮的但它有一个结构上的隐病训练目标用的是 L2 损失也就是让预测位置和真实位置在欧氏距离上尽量接近。问题就在这里。真实场景中一个人走向路口接下来可能直行、可能左转、可能停下等红灯这三条路径在空间上差得很远但每条都是合理的。L2 损失面对这种一对多的情况会倾向于输出一个“折中解”——既不完全直行也不完全左转而是在中间那条实际根本没人走的方向上画一条线。用大白话说就是你把这些可能轨迹当成考试选项L2 损失让网络学会的是填一个“平均答案”而不是真正选一个合理选项。放到画面上看预测出来的轨迹往往虚虚地扫过人群既不真实也不安全。原论文在 ETH 数据集上的可视化对比里就放了这个现象Social LSTM 的预测明显像是“融化”在了几个真实未来之间。这个问题的本质不是网络容量不够而是目标函数和任务本身不匹配。轨迹预测天然是一个多模态分布预测问题用单峰的高斯假设去拟合多峰分布无论网络多深都会产生这条路。Social GAN 的第一层动机就在这里要打破 L2 损失带来的平均化倾向就得换一种能让网络“大胆选一条道”的建模方式。1.2 生成式建模的引入GAN 在轨迹预测上的动机把 GAN 引入轨迹预测乍一听有点“杀鸡用牛刀”毕竟 GAN 最早是做图像生成的。但仔细想图像生成和轨迹预测在“多模态”这件事上其实是同一类问题给定一张人脸可以生成无数张风格不同的脸这在数学上就是 p(x|y) 是一个多峰分布给一段历史轨迹可以外推出多条合理未来同样是多峰分布。GAN 的生成器本身就是从一个潜在噪声里采样天然能覆盖不同模式判别器则负责把那些“一看就不像人走的路”筛掉。这套机制搬到轨迹预测上逻辑上非常自洽。具体到 Social GAN生成器接收历史轨迹的编码和一份随机噪声 z输出一条未来轨迹判别器接收完整的“历史 未来”序列判断这条未来是真实的还是生成器编的。判别器在这个过程中实际上在学一个“合理性度量”这条轨迹符不符合人类行走的习惯、会不会和周围的邻居发生碰撞、会不会突然急拐弯。这样生成器就被逼着不去输出那些“平均但无意义”的轨迹而是尽量输出判别器认为像人类走出来的那类路径。选择 GAN 而不是 VAE 或者 diffusion这篇论文的时间点上 diffusion 还没流行我认为主要是两个考虑。一是当时 VAE 的采样结果偏保守容易退化成“模糊的平均”虽然比 L2 好一点但多模态性依旧有限二是 VAE 的 ELBO 里 KL 项对多模态模式覆盖不够敏感容易一个模式都没学扎实。GAN 虽然训练难但生成样本的锐度更高、模式区分更明显这在后续各种行人轨迹预测实验里也反复被验证。论文里那句 “socially acceptable” 其实就在强调这一点与其输出一条看似准确、实际谁都不会那么走的轨迹不如输出几条可能性不同但每条都合理的路径。2. 整体架构拆解生成器、判别器与社会池化2.1 Generator 的编解码结构与社会池化的插入位置Generator 是标准的 Encoder-Decoder LSTM 结构输入端吃的是历史轨迹序列输出端吐的是未来轨迹序列。每个行人的历史轨迹用一维位置序列表示坐标一般以他自身最后观测到的位置为参考原点做偏移再做相同的位移归一化这样所有人的轨迹长度和位置尺度就拉到一个量级上了。编码器在每个时间步吞进这个人的坐标产出一个隐状态解码器从这个隐状态出发逐步往外推未来坐标。社会池化在这个编解码结构里处于枢纽位置。论文的做法是在每个时间步对每个行人把他周围一定邻域内的其他人也纳入计算——具体来说把邻居相对于该行人的位置做一个空间网格化的聚合网格大小通常设定为 4×4 或者 8×8每个格子里取落入的邻居隐状态做 max pooling最后把聚合后的结果和行人自己的隐状态拼接喂给下一层 MLP 或者直接送进解码器。这个设计和 Social LSTM 的社会池化很像但有个关键差别Social LSTM 里把聚合后的状态当作下一帧编码器输入的附加信息而 Social GAN 里社会池化的结果主要用于解码阶段的初始隐状态构造。换个角度理解Social GAN 让编码器专注于“理解自己”而把“理解周围”这件事主要交给池化后与解码器衔接的那个环节。这个细微的改动让网络结构更清爽也减少了池化长距离传播时的信息稀释。2.2 社会池化的具体参数与邻域设计社会池化里最容易被忽略、但影响最大的参数是邻域半径和网格分辨率。论文沿用了 Social LSTM 的经验设置邻域半径一般在 3 到 4 米之间网格分辨率 4×4 或 8×8。半径设小了远的邻居被忽略网络在拥挤场景里会漏掉潜在的碰撞风险半径设大了池化后的向量容易把远处一些无关的人也搅进来让行人之间的区分度下降。4×4 的网格每个网格尺寸大约对应 1 米上下这个尺度和行人身体的“社会距离”是匹配的——人跟人之间保持 0.5 到 1 米左右的身体避让距离建模时把邻域切成这个粒度的网格网络能学到“近到贴脸”和“还有余地”这种区分。这里有个实用小细节值得单独提池化时到底是“只有落在邻域内的邻居参与聚合”还是“所有邻居都参与、只是位置不同”论文采用的是前者落在邻域外的行人权重为零。这点和后来很多工程实现里的“软注意力池化”思路不同——那个做法会让所有邻居都有非零权重但权重按距离衰减。论文的这个硬邻域做法更贴近他们当时的 GPU 显存条件和训练效率要求实现上也更简单池化结果只受周围有限几个人影响。但如果你希望在拥挤场景里让网络学会“主动挑选重要邻居”软注意力池化是值得一提的升级路径后文我还会再展开。2.3 Discriminator 的对称结构与 GAN 博弈设计判别器在论文里的结构和生成器的编码器基本对称也是用 LSTM 编码整条“历史 未来”轨迹序列每个时间步都做社会池化最后把 LSTM 最后一个隐状态送入一个 MLP 输出真假概率。这个设计挺巧的它让判别器看轨迹的时候同样“有社会意识”——判断“这条未来合不合理”的时候不只看这个人自己的轨迹形态还看他和周围人的交互是否自然。生成器和判别器之间的博弈本质是在两条轨迹空间的分布之间做匹配。真实数据分布是 p_data人在真实人行道上走出来的轨迹生成器在学一个 p_g网络自己编的轨迹判别器要判断一个样本来自哪边。训练的损失是标准的 GAN minimax 形式只不过判别器输入是变长序列需要 LSTM 这种序列模型来编码。论文提到一个实现细节训练判别器时用的是单独的一次更新训练生成器时判别器的参数冻结只更新生成器。这就避免了经典 GAN 里常见的“判别器训得太狠导致生成器梯度消失”那个坑。还有一点很关键但容易被读者忽略判别器不是判断“这条轨迹准不准”而是判断“这条轨迹像不像真的人类轨迹”。准确度是 ADE/FDE 这类指标的事而“像不像”是判别器的事。这就构成了 Social GAN 的双重目标既要生成器骗过判别器又要它在 L2 意义上不要偏离真实太远。所以最终生成器的损失里除了对抗损失还有一个监督项升级版是 Variety Loss后面细讲。这个监督项就是为了防止生成器为了骗过判别器而彻底放飞输出一堆判别器欣赏、但和真实未来差很远的轨迹。3. Variety Loss 与多模态生成的核心机制3.1 为什么需要 Variety Loss从单样本到多采样如果只按标准 GAN 损失训练生成器会倾向于把所有噪声输入都往同一个“最容易骗过判别器”的位置送这叫模式崩塌。在图像 GAN 上表现为生成器只会画同一种猫在轨迹预测里表现为不管输入什么噪声喷出来的都是同一条轨迹。这显然违背了“多模态”的初衷。论文的解决办法是 Variety Loss也常被叫做 Best-of-K Loss。训练时对每个样本生成 K 条候选轨迹论文里 K20然后只对其中和真实轨迹最接近的那条算 L2 损失把其他 K-1 条“往真实位置做平均”的诱导切断。这样做的直觉是生成器可以自由地把每条噪声映射到不同的模式上只要其中至少有一条落在了真实轨迹附近就行。判别器的对抗信号再叠加进来把那些“看起来不像人走”的候选给压掉。合起来生成器就学会了既有多样性、又每条都比较合理。我一开始以为 K 越大越好实测下来并不是。K 太小时样本覆盖不够容易有几个模式学不到K 太大时每条候选被更新得不够充分训练早期收敛慢而且显存会被撑爆。论文选的 K20 其实是在效果和代价之间比较折中的一个数。我在自己的复现里用过 K10 和 K20 两套设置K20 通常在 ADE 上能换来大约 5% 到 8% 的改善但训练速度慢了差不多一半。如果是做研究想追指标K20 合理如果只是跑通一个 demoK8 或 K10 也能看到多模态效果。3.2 训练策略与常用参数表Social GAN 的训练流程看起来不复杂实际操作起来对超参比较敏感。我把论文给出的配置和我复现时用的配置整理成下面这张表两边确实有出入但都在合理范围内。参数项论文配置复现实测常用说明历史帧数8 帧8 帧每帧 0.4 秒覆盖约 3.2 秒预测帧数12 帧12 帧预测 4.8 秒是 ETH/UCY 的标准设置隐层维度3232再大容易过拟合对效果提升不明显嵌入维度坐标1616第一层 MLP 输出社会池化网格4×44×4 或 8×8场景越拥挤网格建议越大邻域半径3 米2.5 到 3 米可结合场景密度微调优化器AdamAdam判别器和生成器都用 Adam学习率1e-35e-4 到 1e-3判别器可稍低Batch size6432 到 64取决于显卡显存采样候选数 K208 到 20研究追指标用 20梯度裁剪未明确通常 1.0对训练稳定性帮助大有一个论文没明说但我反复验证过的点训练刚开始时不要一上来就对抗让生成器先单独用 Variety Loss 跑几个 epoch等它至少能产出像样的轨迹之后再引入判别器交替训练。这个 warm-up 步骤几乎在所有 GAN 复现里都是标配Social GAN 也不例外。如果省掉这一步判别器一开始就能轻松分辨真假生成器的梯度几乎为零很容易直接学不动。3.3 从实验指标看 Social GAN 的收益论文在 ETH 和 UCY 两个数据集上做了大量比较指标是 ADE 和 FDE。ADE 是所有未来时间步的位移误差平均FDE 是最后一帧的位移误差。两者都是越小越好单位是米。Social GAN 在 ETH 上把 FDE 从约 0.87 降到约 0.70在 UCY 的 HOTEL 场景上从约 0.48 降到约 0.30ZARA 场景上也有类似幅度的下降。这些数字单看可能不直观翻译一下就是预测 4.8 秒后的位置平均误差从快 1 米缩到 0.7 米以内对机器人避障来说这个差距足以影响一次是否能安全通过狭窄通道的决策。不过我更关心的不是绝对数值而是不同 K 下的指标变化趋势。论文里报告了 K1、K5、K10、K20 时的 ADE/FDE可以看到从 K1 到 K5 的改善最显著从 K10 到 K20 就进入收益递减。这也是我前面说 K8 到 10 在很多场景里已经够用的原因。如果你要把 Social GAN 塞进一个实时性要求比较高的系统里K 取小一点、结合模型剪枝或者蒸馏整体速度能压下来不少精度损失也可以接受。4. 实操过程与核心环节实现4.1 数据预处理坐标归一化与滑动窗口切分ETH/UCY 这两个数据集有个特点不同场景的轨迹文件格式不完全一样有的是每帧列出所有人坐标有的是按行人 ID 列出整条轨迹。读取之后的第一步是统一成“按行人分组的轨迹”格式之后再做坐标归一化。论文用的归一化比较简单以每个行人历史轨迹的最后一个观测位置为原点对所有历史帧和未来帧的坐标做平移。这一步的目的是把不同场景、不同绝对位置的数据拉到同一个量级上避免网络被绝对坐标带偏。归一化之后再用滑动窗口切分样本。每个样本是一个窗口包含 8 帧历史加 12 帧未来。切分时要注意丢弃那些“轨迹不完整”的窗口比如某人在第 5 帧才出现在画面里、第 15 帧就被遮挡了这种样本强行切出来会把大量缺失值带进训练。我的做法是强制要求窗口内每个人的 20 帧轨迹都完整不完整的直接跳过。这个筛选会损失一部分样本但训练稳定性提升明显。再补一个细节ETH/UCY 里不同场景的帧率是一致的都是 0.4 秒一帧但场景尺度差别挺大。HOTEL 大厅里的人走得很慢UNIV 校园里有些学生走得很快网络如果一视同仁地训练容易出现“慢场景预测偏慢、快场景预测偏快”的偏移。我一般会在样本切分之后统计每个场景的平均速度在训练时把速度作为一个辅助特征如果网络支持或者在不同场景上分开评估避免用总量指标掩盖了场景之间的差异。4.2 社会池化模块的可复现实现社会池化是这篇论文里代码量最大、最容易写错的部分。下面给出一份可以直接参考的 PyTorch 实现逻辑和论文一致但做了必要的简化处理import torch import torch.nn as nn class SocialPooling(nn.Module): def __init__(self, hidden_dim32, grid_size4, neighborhood3.0): super().__init__() self.grid_size grid_size self.neighborhood neighborhood # 用一层 MLP 对每个网格内的聚合结果做映射 self.mlp nn.Sequential( nn.Linear(hidden_dim, hidden_dim), nn.ReLU(), nn.Linear(hidden_dim, hidden_dim) ) def forward(self, hidden_states, positions): # hidden_states: [B, N, H] # positions: [B, N, 2] 按人归一化后的坐标 B, N, H hidden_states.shape device hidden_states.device # 先把所有位置拉到原点附近 rel positions.unsqueeze(2) - positions.unsqueeze(1) # [B, N, N, 2] dist torch.norm(rel, dim-1) # [B, N, N] mask (dist self.neighborhood) (dist 1e-6) # 去掉自身 # 把相对位置映射到网格下标 gs self.grid_size cell (rel / (self.neighborhood / gs) gs // 2).long() cell cell.clamp(0, gs - 1) cell_idx cell[..., 0] * gs cell[..., 1] # [B, N, N] # 对每个行人把邻域内的隐状态按网格分组做 max pooling pooled torch.zeros(B, N, gs * gs, H, devicedevice) for b in range(B): for i in range(N): valid mask[b, i] if valid.sum() 0: continue idx cell_idx[b, i][valid] hs hidden_states[b, valid] for c in range(gs * gs): sel hs[idx c] if sel.size(0) 0: pooled[b, i, c] sel.max(dim0).values pooled pooled.view(B, N, gs * gs * H) # 过一层 MLP 融合网格特征 return self.mlp(pooled.view(B * N, gs * gs * H)).view(B, N, H)这份实现为了可读性做了一些妥协真实训练时上面那两层 for 循环必须向量化或者用 scatter 操作重写否则速度会慢到无法接受。我踩过的坑是一开始直接用循环实现单 epoch 训练要两个小时向量化之后压到二十来分钟。向量化的思路是用scatter_或者index_add按 (batch, person, cell) 三个维度做聚合网上很多 Social LSTM 的开源实现里都有类似的散点操作可以照着改。还有一个细节dist 1e-6这个过滤是为了排除行人自己。如果坐标是浮点数严格等于 0 的判断容易失效用一个小阈值更稳。另外neighborhood / gs算的是每个网格在物理世界里的边长这个值随着邻域半径和网格数变化网格越细这个边长越小最后池化出来的结果也越“锐”。这个联动关系在调参时很容易搞混建议固定住邻域半径再单独调网格分辨率。4.3 训练循环与损失权重配置训练循环里最关键的其实是损失函数的拼装。生成器总损失 监督损失Variety Loss 的 Best-of-K λ × 对抗损失。这个 λ 论文没给明确值我复现时试了 0.1、0.5、1.0 三档结果 λ0.1 时对抗信号太弱多模态性出不来λ1.0 时训练很不稳定容易在 50 个 epoch 之后开始发散λ0.5 相对稳。所以下面这段代码里我用了 0.5 这个值实践里效果比较可控。import torch import torch.nn as nn def variety_loss(pred_traj, gt_traj): pred_traj: [B, K, T, 2] K 条候选轨迹 gt_traj: [B, T, 2] 真实未来 diff pred_traj - gt_traj.unsqueeze(1) # [B, K, T, 2] l2 torch.norm(diff, dim-1).mean(dim-1) # [B, K] best, _ torch.min(l2, dim1) # [B] return best.mean() def train_step(generator, discriminator, history, future, positions_hist, opt_g, opt_d, lambda_adv0.5, K20): B history.shape[0] # ---- 生成器前向 ---- pred, logits_fake generator(history, positions_hist, KK) sup_loss variety_loss(pred, future) # 用判别器判断生成的轨迹 adv_loss nn.functional.binary_cross_entropy_with_logits( logits_fake, torch.ones_like(logits_fake) ) g_loss sup_loss lambda_adv * adv_loss opt_g.zero_grad() g_loss.backward() nn.utils.clip_grad_norm_(generator.parameters(), max_norm1.0) opt_g.step() # ---- 判别器前向 ---- logits_real discriminator(history, future, positions_hist) _, logits_fake generator(history, positions_hist, K1) d_loss nn.functional.binary_cross_entropy_with_logits( logits_real, torch.ones_like(logits_real) ) nn.functional.binary_cross_entropy_with_logits( logits_fake, torch.zeros_like(logits_fake) ) opt_d.zero_grad() d_loss.backward() nn.utils.clip_grad_norm_(discriminator.parameters(), max_norm1.0) opt_d.step() return sup_loss.item(), adv_loss.item(), d_loss.item()这段代码里有个容易忽略的设置梯度裁剪的max_norm1.0。论文没提这个但在我的复现里没有它几乎训不动尤其是判别器学习率稍高的时候容易爆炸。这属于典型的“论文里没写、但一线操作必备”的经验。5. 常见问题排查与避坑技巧实录5.1 模式崩塌与多样性不足在 Social GAN 复现里模式崩塌是最常见的失败模式。具体表现是训练到某个阶段之后无论输入什么噪声生成的十几条候选轨迹都会挤到同一个方向上去看可视化就像同一根线抖了十几下。判别器那边的 loss 始终在 0.69 附近也就是 ln2 附近说明判别器已经学不出真假了生成器的对抗 loss 也很低但 ADE/FDE 指标反而变差。排查方向有这么几个。先看 K 是不是设小了K1 肯定不行K5 以上才有多样性的空间再看监督损失和对抗损失的权重λ 太大比如 1.0 以上容易把生成器逼到只去骗判别器而不看真实轨迹然后看判别器是不是太强判别器如果 3 到 5 个 epoch 内就把真假分得干干净净生成器基本学不动这时应该调低判别器学习率比如生成器 1e-3、判别器 5e-4或者给判别器加 dropout。我的土办法是给判别器和生成器各设一个比较小的学习率让互相博弈的过程慢一点反而比快速对抗更稳。还有一个不太显眼但很常见的原因Batch 内的样本分布过于单一。如果训练数据按场景排序之后直接切 batch可能出现一个 batch 里全是 HOTEL 场景的样本判别器很容易记住“HOTEL 大厅里的轨迹应该长这样”从而对生成器所在的模式范围做出误判。解决办法是在构建 DataLoader 时一定要 shuffle并且尽量让每个 batch 里的场景尽量分散。5.2 社会池化的邻域参数怎么调邻域半径这个参数在论文里是一笔带过的“3 米”但在实际场景中并不一定合适。3 米这个数对应的是相对稀疏的人行道场景如果换到地铁站台、演唱会散场这种高密度场景3 米半径内可能塞了十几个人池化信息会被严重稀释每个人得到的社会池化结果几乎一样等于没池化。反过来如果场景很稀疏3 米半径里一个人都没有池化输出全零社会池化的意义也就没了。比较稳妥的做法是先统计场景里的平均最近邻距离再根据这个分布来设半径。比如平均最近邻距离是 0.8 米那么半径设 2.5 到 3 倍平均距离基本合适也就是 2 到 2.5 米。如果任务场景里大多数人贴得很近把半径缩到 2 米以内、网格分辨率调细到 6×6让网络能分辨出“近邻”和“稍远邻”的差别会比直接用论文默认值好。另外一个隐藏参数是每条轨迹与邻居轨迹的对齐方式。论文用的是“以自己当前位置为参考原点”的相对坐标这当然是最直接的。但如果场景里有大量同向行走的人直接用相对坐标会让网络把“同向但在身后”和“反向但在身前”这两种完全不同的社会关系混在一起。一个值得试的做法是同时输入相对位置和相对速度让网络有机会学到方向和意图的差异。这个改动不在论文里是我在自己的实验里觉得有效的方向可以视具体任务权衡。5.3 训练过程不稳定与异常排查下面这张表是我自己遇到的、以及和同行交流时听来的典型异常按“现象—可能原因—处理办法”整理成速查表方便对照排查。现象可能原因处理办法判别器 loss 快速降到 0 附近判别器学得太快生成器还没进入状态降低判别器学习率或判别器每 2 到 3 步更新一次生成器 loss 全程震荡不下降学习率过高或 λ 过大学习率降到 5e-4λ 从 0.5 往下降训练里 ADE 反复涨落没有收敛趋势Batch 内分布不均未做 shuffle检查 DataLoader强制打乱可视化里候选轨迹全部重合模式崩塌提高 K检查 λ降低判别器强度预测轨迹在历史末端出现“断崖”归一化原点在有遮挡的样本上不稳定强制只保留完整轨迹窗口剔除被遮挡样本推理速度过慢池化用循环实现未向量化检查算子实现必要时用 scatter 重写再补充一个不太好归类、但很值得说的经验在训练早期就去看可视化不要只盯 loss。GAN 的 loss 本身解释性很差判别器 0.69 附近可能代表双方均衡也可能代表判别器崩了生成器 sup_loss 在降但不代表轨迹质量在升因为 L2 在轨迹预测里对“方向正确但细节粗糙”的轨迹相当宽容。看可视化是最直接的诊断手段尤其是找几个 le 的场景比如两人迎面走过、一群人向同一方向走把历史、真实未来、预测未来画在同一张图上能立刻看出是“多模态没了”还是“偏离得离谱”。6. 从论文到落地应用场景与延伸思考6.1 在机器人导航与自动驾驶下游模块里的角色Social GAN 最直接的应用场景是移动机器人的局部路径规划。传统规划器用恒速度假设外推行人轨迹结果经常把机器人送到“别人马上要走的位置”上走一步就被迫停下来让路。把 Social GAN 的输出接到规划器里规划器可以拿到 K 条可能未来做“风险最小”的决策既可以选择最有把握通过的那条路径也可以在几个可能未来里取一个“最坏情况别撞”的保守策略。实际用下来接上多模态预测之后机器人的平均等待次数能下降不少因为它能提前绕开别人要走的方向而不是被动等对方先走。在自动驾驶的感知下游Social GAN 这类行人轨迹预测模块更多是作为“行为预测”的一个子任务存在。需要提醒的是这篇论文的设定是纯视觉轨迹输入只有位置序列没有用地图信息、没有用人体朝向、没有用场景语义人行横道、路沿。所以如果直接拿去做车规级应用光靠 Social GAN 的输出是不够的。更合理的用法是把它作为行为预测子模块的一个基准再结合地图约束、车道线、交通信号这些先验做后处理。我见过不少团队把 Social GAN 的输出当作“行人意图候选集”再用规则或者树搜索做剪枝这个思路实际效果不错而且比端到端预测可控。6.2 后续工作与可能的改进方向Social GAN 之后这个方向上的主要改进集中在几个方向。第一是多模态表达更细致比如把轨迹分解成“意图”和“路径”两层用一个离散的意图变量去控制宏观方向再用连续变量细化微观路径第二是把场景先验加进来比如把地图栅格化之后作为额外输入通道让网络学到“人不会穿墙走”第三是把社会交互从简单池化升级成图注意力用 Transformer 或者 GNN 去建模行人之间的关系这在 ZARA 这种极端拥挤的场景下提升比较明显。还有一类改进值得关注是把预测和规划放到一起联合优化。Social GAN 的输出只是预测不涉及“我预测了之后要怎么办”。如果下游有机器人或者自动驾驶车辆很多工作会把“预测损失”和“规划损失”放在一个框架里联合训练让预测模块更了解“规划器关心的是什么”。这个方向在学术上还比较新工程上落地的不多但思路是对的——预测的目的本来就是服务决策而不是追求指标好看。另外一个实际部署中绕不开的是实时性。Social GAN 在论文里的配置是 K20、隐层 32 维、在 GPU 上跑推理不算很重但要是放到嵌入式平台或者车载芯片上就需要做模型压缩。我试过的路线是先对判别器做剪枝判别器更多是训练期的辅助推理时原本就不需要再对生成器的 LSTM 换成更轻的 GRUK 从 20 降到 5 到 8整体速度能提升到原来的 3 到 4 倍ADE 指标下降大约 5% 到 10%在很多工业场景里这个 trade-off 是可接受的。6.3 阅读这本论文时值得留心的几个思维锚点最后分享几个我读这篇论文时反复琢磨的点对理解后续工作也有帮助。Social GAN 把生成式建模引入轨迹预测实际上建立了一个“评估多模态预测”的方法论用 ADE/FDE 评价“最接近的那条”用轨迹集合的分散度评价“多模态丰富性”这个二元评价后来成了标配。理解这一点读后续基于 diffusion 的轨迹预测工作时就不会迷路因为它们做的其实是在同样的问题设定下换更稳的生成模型。第二社会池化的本质是局部邻域信息聚合。这个思路后来被 Transformer 的自注意力、GAT 的邻接矩阵聚合取代但没有变的是原理——把周围人的状态以某种加权方式合并进来。所以真正的关键不是“用池化还是用注意力”而是“怎么设计一个合理的邻域和权重”。这也是为什么我在复现时特别强调先把邻域半径和网格参数调好再考虑换更复杂的聚合结构。第三也是一条我自己的体会不要把 Social GAN 当成“更准的预测器”而要当成“更像人行走方式的测试床”。论文里那些 ADE/FDE 数字旁边的可视化才是真正值得细品的部分因为数字会掩盖很多具体问题而“这个预测合理不合理”这件事看图比看表更直接。最近我在做场景落地时把可视化脚本单独抽出来做成了一套工具链每次训练完先看一组典型场景的图再决定要不要继续调参这个习惯帮我少走了不少回头路。
阅读完成 · 觉得有帮助?
咨询建站