疫情三年,发明出来很多词:原地静默、静态管理、非必要不出门、就地过节...一系列闻所未闻、见所未见的新词映射的是疫情的“严峻事态”。有网友评价说:如果连下雨的权利都掌握在了少数人手中,那普通人连打伞的资格都将被剥夺。
为什么罕见的说这些呢,你可以想象说被封控八个月是有多难受。你很难相信八个月里一直有人“无症状”感染,那只不过及时下的雨而已。
相应的代码链接:
MG-GAN-mainhttps://github.com/selflein/mg-gan
一、论文简介
MG-GAN是继用GAN做行人轨迹预测的文章。那MG-GAN要解决一个什么问题呢?它要解决的是样本OOD(out-of-distribution)的问题,那什么是样本OOD的问题呢?举例来说,在论文的图一中,(a)代表的含义是:黑色的实线代表的是观测到的行人轨迹,其中红色区域代表该行人的destination,黄色实线代表该行人到达某个destination的真实轨迹;(b)中蓝色区域是SOTA的GAN产生的潜在分布,可以看到说SOTA的GAN无法解决“不连续的模式(disconnected modes)”的问题,用人话说就是SOTA的GAN无法对行人可能存在的destinations的分布做到独立预测,那黄色和红色的实线代表的都是预测的样本,只不过红色实线表示说产生了out-of-distribution的问题,;在(c)中可以看到:多generator的GAN分别对行人可能存在的destination做出了独立的预测分布,并且预测的样本没有产生OOD的问题。

二、数据处理
那数据处理这一部分其实还蛮直觉的,你可以想象说在原先对观测轨迹数据的处理上,多加了对场景图片的处理。在代码里是先对场景图片进行处理,处理的手段包括旋转、剪裁,这一部分看过代码的读者应该有所了解。值得一提的是代码将场景图片的大小剪裁到了三个规格,分别是:旋转过后的img、在img的基础上再进行resize的small_image、在img的基础上再进行resize的tiny_image,它们统称为scene_image。这里值得注意的是,若你对图片进行了旋转,相应的行人轨迹坐标也要进行旋转。对轨迹的处理就比较直接,类似于我在Social-STGCNN里写的“数据是怎样被处理的”,那这里就不再赘述。
在得到mini-batch数据后,注意:这里的坐标都是旋转过后的坐标,我们就可以送入模型中进行训练了吗?正如我在Social GAN中叙述的一样,在单一轨迹预测时,我们把一个行人或说一条轨迹当做一个样本是完全OK的,但在Social GAN中,为了研究轨迹之间的交互,且研究联合轨迹预测模型(就是同时为多条轨迹预测),最好的方式是让一个场景片段中所有的轨迹在一个batch中被处理。在MG-GAN中设置batchsize=2,所以我们每次对两个mini-batch中的数据同时进行处理。
那这个处理的过程并不是一步到位的,举例来说,你还需要对选择的规格的图片做进一步操作,比如我们用的是small_image()规格的场景图片,那它还需要以某个mini-batch下(如:(3,8,2))的单独行人的最后一帧所处的位置(坐标)为中心,调整small_image的大小,进一步进行剪裁,使该行人位于剪裁过后图片(
,用斜体以区别上述的img)的中心;除此之外,R、G、B三通道还要加上一个A通道,表示透明度(这里我也不敢百分百确定,不过大概率是这个意思)。在做这样一件事之前,我们还需要先将
图像像素归一化到
之间,这样做是为了加快训练网络的收敛性。紧接着,我们就可以concat一个0矩阵,经
函数调整维度后,就可以变成我们期望的维度要求[b, c ,h ,w](1,4,33,33);那mini-batch中剩余的两个人重复上述过程,经concat后就得到一个mini-batch下所有行人的场景特征(3,4,33,33);那你可以推理得到说,另一个mini-batch也是同样重复上述过程,得到该mini-batch下所有行人的场景特征(3,4,33,33);那这个batch下所有行人的场景特征的大小就是(6,4,33,33)。那我们就把该batch喂给我们的模型,其余的batch也是重复相同的操作。
三、MG-GAN算法
在理清MG-GAN算法之前,我们需要先看两篇博客及一篇论文(MGAN),后面的叙述才不会让你感到困惑:
2.EM算法
3.MGAN: TRAINING GENERATIVE ADVERSARIAL NETS WITH MULTIPLE GENERATORS
好,那我们开始吧。MG-GAN的网络架构长什么样呢?你肯定会说这不是废话吗,论文图二有给出啊。那为什么我要多此一举让读者感觉我好像不是很聪明的样子呢?这是因为论文给的结构图还蛮容易让人产生文章很简单的幻觉。你可以看到说,无非就是在输入判别器前多了一些交互的处理,然后多了几个generator而已,通过PM Network对generators加权,然后选择概率大的generator产生的轨迹送给discriminator进行判别real/fake,并没有什么特别的啊。但事实上,文章并不是这么好懂,看过代码的朋友应该有感觉说代码与论文给的网络架构有很大的出入,并且很多地方并不是那么的直觉,相反,一些操作甚至会让人觉得:这样做应该不work吧...

好,那为了能够叙述方便,先将GAN的部分搁置,看看交互工作是怎么做的吧。行人轨迹预测要提取的信息无非就两种:一是行人与行人间的交互,这里面包含行人间的时空信息,也称社会尺度信息;二是行人与场景间的交互,研究的是行人与周围场景的关联关系(交互),这里的场景指的是,人是站在路边,还是草坪上,或者是靠近一片水池、还是靠近一幢办公楼…换句话说就是研究行人更愿意出现在场景中的哪些位置。交互网络的上分支是对场景信息进行编码,可以看到说,就是代理人
所处的场景。那上述提到的batch下所有行人的场景特征[6,4,33,33]首先会被送给CNN进行特征提取,这里CNN由两个不同的卷积层(block 1、block 2)组成,且输出维度大小对于D、G来说有所差别:判别器阶段由[6,4,33,33]→[6,8,8,8];生成器阶段由[6,4,33,33]→[6,16,8,8]。以判别器阶段为例,提取过后的特征先进行纬度调整变为[6,64,8](b,hw,c),再被送入Physical Attention模块。Attention的流程大体上可以分为两步:
①首先是要计算注意力系数(attention coefficient),学习节点
之间的相关性,也就是
,其中
;
②其次是根据计算好的注意力系数把特征加权求和:
Pysical Attention模块模块实际上是由两层线性层组成,负责将维度从8维映射到32再映射回8维,得到维度大小为[6,64,8]的attention_scores矩阵,这一步实际上就对应了上面的,再通过softmax计算注意力系数
[6,64,8],并将其之前的场景特征进行加权,输出维度大小为[6,64,8]的attention_out矩阵,该矩阵包含行人对所处场景中的哪些场景特征是较为关注的。那为了与Social attention的特征进行concat拼接,对attention_out矩阵的最后一维进行sum操作,使其大小变为[6,64],称作secen_encoding
在生成器阶段也是重复上述操作,不同的是提取过后的特征经维度调整后变为[6,64,16],再被送入Physical Attention模块,最终得到的attention_out矩阵大小为[6,64,8],经sum[-1]后变为[6,64]。
交互网络的下分支是对行人交互信息进行编码。这一部分其实还蛮直觉的,但是又不是以往那么直接说单纯的把agent与其他周遭行人做交互而已。那这一部分难理解在哪里呢?难在我该怎么叙述才不会让读者感到困惑。举例来说,上分支场景信息的编码对于discriminator或者generator来说输入都是一样的,都是一个batch下所有行人的场景特征。所以上面的叙述可以只介绍discriminator,然后另一个同理可得就足够使读者感到清晰。但是接下来对行人交互信息编码,由于D和G的输入不同,所以希望读者在看博客之前已经对代码有所了解,这样才不会让你感到困惑。
从下图我们可以看到说代理行人与周遭行人
前八帧的轨迹分别送给了LSTM,然后将得到的hidden_state分别送给Social Attention模块做行人交互。

但具体的实现并不是这样子,举例来说,在初始化D或训练D阶段,行人前八帧的观测轨迹in_xy [8,6,2],前八帧的相对坐标in_dxdy [8,6,2],以及后十二帧的预测轨迹pred_obs [12,1,6,2](seq_len, samples, peds_num,dim)后十二帧的预测相对坐标pred_rel [12,1,6,2]在处理后会被送入Social Attention模块中,需要注意的是,pred_obs以及pred_rel的维度大小来自两个方面:在初始化D阶段,pred_obs、pred_rel等于gt_xy、gt_dxdy,意味着pred_obs、pred_rel是真实的后十二帧的轨迹或相对坐标;在训练D阶段,pred_obs、pred_rel的维度大小来自generator的输出,这是一件非常直觉的事,因为我们的generator输出的就是预测轨迹。好,那in_xy [8,6,2]、in_dxdy [8,6,2]、pred_obs [12,1,6,2]、pred_rel [12,1,6,2]会被怎样处理呢?首先,相对坐标in_dxdy通过embedding嵌入到64维,随即送入LSTM中得到hidden_state [6,64],将hidden_state通过一个全连接层,得到矩阵in_enc [6,32]。 pred_rel经维度调整后通过一个全连接层,得到维度为[6,32](或[120,32])。将in_enc与pred_enc进行concat拼接后,得到一个大小为[6,64](或[120,64])的矩阵。其次,取观测坐标in_xy、相对坐标in_dxdy的最后一帧,将其拼接,得到一个大小为[6,1,4](或[120,1,4])的矩阵,该矩阵包含行人在最后一帧的绝对坐标以及相对坐标,利用该矩阵计算行人间的l2 distance、bearing angle、dcas_matrix,得到行人间的social feat [6,6,3](或[120,120,3])。那这里具体是怎么算出这三个数值的,还有待讨论。得到的social feat通过一个全连接层将维度映射到64,得到矩阵emb_social_feats[6,6,64](或[120,120,64])。将
与emb_social_feats做Attention就得到了行人间的交互,我们把做Attention后得到的矩阵叫做
[6,64](或[120,64]),它代表的物理含义是agent对他周遭的哪些行人较为在意。那到这里,我们在D阶段的行人交互就结束了。

在训练G阶段,有两处不同:一是我们的输入只有in_xy [8,6,2]、in_dxdy [8,6,2];二是维度不同。举例来说,首先,相对坐标in_dxdy通过embedding嵌入到16维,随即送入LSTM中得到hidden_state [6,32],这里不需要再通过一个全连接层了,得到的hidden_state就是我们的,因为没有pred_rel的输入也不需要什么concat拼接;其次,取观测坐标in_xy、相对坐标in_dxdy的最后一帧,将其拼接,得到一个大小为[6,1,4]的矩阵,该矩阵包含行人在最后一帧的绝对坐标以及相对坐标,利用该矩阵计算行人间的l2 distance、bearing angle、dcas_matrix,得到行人间的social feat [6,6,3]。那这里具体是怎么算出这三个数值的,还有待讨论。得到的social feat通过一个全连接层将维度映射到32,得到矩阵emb_social_feats[6,6,32]。将
与emb_social_feats做Attention就得到了行人间的交互,我们把做Attention后得到的矩阵叫做
[6,32]),它代表的物理含义也是agent对他周遭的哪些行人较为在意。那到这里,我们在G阶段的行人交互就结束了。

在交互模块结束后,我们把上述得到的拼接在一起,得到
([6,192] / [120,192] for D,[6,128] for G)。对判别器D来说,
中的每一行包含了该行人20帧的轨迹信息(观测8帧+预测/GT12帧)、他更愿意出现在场景中的哪些位置,以及他对周遭哪些行人比较在意。那这样一个综合了多方面的信息的矩阵,丢给我们的判别器D,就能得到预测行人轨迹的真假;或者将其丢给classifier
,就能得到预测的轨迹信息来自哪一个generator。对生成器G来说,
中的每一行包含了该行人前八帧的观测信息、他更愿意出现在场景中的哪些位置,以及他对周遭哪些行人比较在意。那这样一个综合了多方面信息的矩阵加上噪声,丢给我们的生成器G,就能够输出行人轨迹的预测结果;或者丢给论文架构中的PM Network,就能得到每个generator的加权概率。