Social GAN代碼要點記錄


近日在閱讀Social GAN文獻的實驗代碼,加深對模型的理解,發現源代碼的工程化很強,也比較適合構建實驗模型的學習,故細致閱讀。下文是筆者閱讀中一些要點總結,有關於pytorch,也有關於模型自身的。

GPU -> CPU

SGAN的實驗代碼在工程化方面考慮比較充分,考慮到了在CPU和GPU兩種平台上模型的運行。原生平台是GPU,若要切換為CPU,需要做如下改動(目前只改動了訓練過程所需的,測試評估還未進行,但估計類似):

  1. args.use_gpu需要置為0,以保證int_dtypefloat_dtype不是cuda。
  2. 檢索cuda(),可以發現在model.py還有些殘缺未考慮的cuda定義,使用torch.cuda.is_available()判斷是否GPU可使用,只有可行采用cuda()定義:
x = xxx()
if torch.cuda.is_available():
	x = x.cuda() 

池化層實現細節

Social GAN相較於Social LSTM提出了新的池化模型以滿足不同行人軌跡間信息共享與相互作用,具體有以下幾個方面的變動:

  1. Social GAN的池化頻率為一次,只在利用已知軌跡編碼后進行一次池化。(代碼中一個額外選項是在預測的每一步都進行池化)
  2. 池化范圍為全局而不是固定的范圍區間,代碼使用max pooling的手段使得在場景人數不確定的情況下可以保持數據維度固定。
  3. 池化輸入數據由兩方面組成:LSTMs的隱藏狀態+最后位置的相對信息

而在代碼實現時,計算相對位置信息時顯得比較巧妙,例如在同場景的行人位置信息,代碼通過兩次不同的repeat策略將原有N個人的位置信息重復N次,從而形成了[P0, P0, P0, ...] [P1, P1, P1, ...] ... 和 [P0, P1, P2, ...] [P0, P1, P2, ...] ..兩個矩陣,通過矩陣相減即可得到一個N*N行的矩陣,第\(i\)行是第\(i \% N\)個人相對於第\(i / N\)個人的相對位置。

	curr_hidden = h_states.view(-1, self.h_dim)[start:end]
    curr_end_pos = end_pos[start:end]

    # Repeat -> [H1, H2, H3, ...][H1, H2, H3, ...]...
    curr_hidden_1 = curr_hidden.repeat(num_ped, 1)
    # Repeat position -> [P1, P2, P3, ...][P1, P2, P3, ...]...
    curr_end_pos_1 = curr_end_pos.repeat(num_ped, 1)
    # Repeat position -> [P1, P1, P1, ...][P2, P2, P2, ...]...
    curr_end_pos_2 = self.repeat(curr_end_pos, num_ped)
    # 得到行人的end_pos間的相對關系,並交給感知機去具體處理。
    # 每個行人與其他行人的相對位置關系由num_ped項,合計有num_ped**2項。
    curr_rel_pos = curr_end_pos_1 - curr_end_pos_2
    curr_rel_embedding = self.spatial_embedding(curr_rel_pos)

    # 拼接H_i和處理過的pos,放入多層感知機,最后經過maxPooling。
    mlp_h_input = torch.cat([curr_rel_embedding, curr_hidden_1], dim=1)
    curr_pool_h = self.mlp_pre_pool(mlp_h_input)

DataLoader相關

安利一個知乎,上面對使用Pytorch實現dataLoader解釋得很細致

https://zhuanlan.zhihu.com/p/30385675

dataLoader迭代器的數據格式

Dataset繼承而來的TrajectoryDataSet__get_item__進行了重寫,以方便dataLoader使用並整合,每次函數返回的是一個列表:

out = [
    self.obs_traj[start:end, :], self.pred_traj[start:end, :],
    self.obs_traj_rel[start:end, :], self.pred_traj_rel[start:end, :],
    self.non_linear_ped[start:end], self.loss_mask[start:end, :]
]
return out

列表中有6個元素,以obs_traj為例,其大小為[N][2][seq_len],但是在使用dataLoader進行迭代時出現了這種形式,不僅一個batch中解壓得到的變為7個,而且obs_traj的大小變為[seq_len][batch][2],,順序發生了變化.

batch = [tensor.cuda() for tensor in batch]
(obs_traj, pred_traj_gt, obs_traj_rel, pred_traj_gt_rel, non_linear_ped,loss_mask, seq_start_end) = batch

Solution

  1. 問題主要是忽視了DataLoadercollate_fn函數的作用,這個函數是在trajectory.py中自定義的函數,主要作用時當dataLoader收集到batch_sizeitem后形成一個列表,而后交由自定義的collate_fn做預處理,處理后的數據就會被輸出為batch
  2. seq_collate解答了數據格式的兩個疑問,包括使用permutecat函數。

從dataLoader獲取的batch數據的概念辨析

Solution

  1. batch != batch_size

    1. 模型注釋中有多處使用batch來表示張量格式,一個batch的數據常常有batch_size行,但在該模型中不成立。
    2. 嚴格來說,一個batch中有batch_sizeitem,但一個item可以用多行表示,這就是該模型的數據特點,其在一個batch中額外新增了seq_start_end列表(len(seq_start_end) == batch_size),使用該列表即可抽取出一個item

    \[batch = \Sigma_{i=0}^{batch\_size-1}N_i (N_i \ge min\_peds) \]

    \(N_i\)表示一個場景下的行人個數。

  2. 一個batch中有多場景的行人軌跡數據

    1. LSTM編碼和譯碼:每個軌跡都是獨立的,此時可以整個batch一起處理
    2. 池化:設計同一場景下各行人序列數據交互,需要使用seq_start_end划分場景分別計算。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM