CausalTAD: Causal Implicit Generative Model for Debiased Online Trajectory Anomaly Detection

GraphL發表於2024-12-02

資料格式

異常資料的生成

在你提供的程式碼中,異常資料生成策略(如繞行和切換)並沒有直接出現。這些策略似乎是在資料預處理或資料集構建過程中實現的,但在程式碼片段中沒有涉及到具體如何透過 Dijkstra 演算法生成繞行軌跡,或者如何切換軌跡中的某一段。

異常資料的生成通常是透過對原始資料進行修改或者引入異常行為來模擬。你提到的兩種策略:

  1. 繞行(Detour):透過刪除一段路並重新計算最短路徑來替換原有路段。這需要在軌跡資料中找到特定的路段,刪除或替換它們,可能還需要呼叫 Dijkstra 演算法進行最短路徑計算。
  2. 切換(Switch):將某一段軌跡替換為其他軌跡中的相似部分。這可能意味著找到兩條軌跡的相似部分,然後交換它們的某個子軌跡。

若要在程式碼中實現這些策略,你可以透過以下方式進行擴充套件:

  1. 為軌跡資料新增異常生成方法:在 TrajectoryLoader 或資料預處理部分,定義函式來模擬這些異常軌跡,例如使用 Dijkstra 演算法生成繞行軌跡,或者對軌跡進行切換。
  2. 修改軌跡:在軌跡資料載入後,根據設定的比例或策略,對部分軌跡進行修改(如繞行或切換),生成異常資料。
  3. 整合資料集:將生成的異常資料與正常資料結合,作為最終的測試資料集。

如果你需要在程式碼中實現這些異常資料生成方法,可以參考類似的虛擬碼:

def generate_detour_trajectory(original_traj, road_network):
    # 選擇一個路段,刪除並計算繞行軌跡
    start_node, end_node = choose_detour_segment(original_traj)
    detour_path = dijkstra_algorithm(start_node, end_node, road_network)
    return detour_path

def generate_switch_trajectory(original_traj, other_traj):
    # 在兩個軌跡中找到相似部分,進行切換
    switch_point = find_similar_part(original_traj, other_traj)
    switched_trajectory = replace_segment(original_traj, switch_point, other_traj)
    return switched_trajectory

你可以根據資料的結構,將這些方法嵌入到資料載入過程中,自動生成異常軌跡並與正常軌跡一起處理。

是的,基於你提供的資訊,這些資料集一開始是透過 異常軌跡生成方法(如繞行和切換)來人為地建立的,這些方法將正常軌跡轉換為異常軌跡,並賦予了標籤。因此,異常資料集是有標籤的,通常使用如下方式:

  1. 標籤定義:正常軌跡和異常軌跡(透過繞行和切換生成的軌跡)會被標註為不同的標籤。例如,正常軌跡的標籤可能為 0,而繞行軌跡和切換軌跡的標籤可能分別為 12,或者是其他你設定的標籤。

  2. 資料的標註:這些軌跡的標籤是透過人工規則(繞行或切換)決定的,通常在生成異常軌跡時就已經指定好了標籤。因此,這些異常軌跡在整個資料集中會有明確的標籤標識,並且用於模型訓練或評估時作為 Ground Truth。

關於整段軌跡的問題與子軌跡的考慮:

從你提供的程式碼中來看,似乎 異常檢測模型 在處理軌跡資料時,關注的是 整段軌跡,即整個軌跡序列的輸入和輸出。這表明,模型在做預測或訓練時,主要是將整個軌跡作為一個單位來進行處理。

  1. 整段軌跡問題

    • 你提到的 繞行切換 都是針對整段軌跡的修改。例如,繞行軌跡是透過從起點到終點的最短路徑替換原有的某段路,切換是將一條軌跡的某段替換成另一條軌跡的部分。
    • 這些操作在模型的輸入端可能不會進行細粒度(即子軌跡)的處理,而是直接考慮整個軌跡序列。
  2. 子軌跡的考慮

    • 如果你希望模型能夠識別 子軌跡 的異常(而不僅僅是整段軌跡),則模型需要在細粒度上進行處理,例如使用滑動視窗或子圖的方式,從軌跡中提取較小的部分作為子軌跡進行異常檢測。
    • 當前的程式碼(尤其是 TrajectoryLoader 和後續的 VAE 模型等)處理的是 整個軌跡,並沒有顯式地把軌跡分成子軌跡進行訓練。因此,模型的設計是基於 整個軌跡的輸入和輸出,而不是單個子軌跡。

總結:

  • 標籤:這些異常軌跡資料集確實有標籤,是透過生成異常軌跡時就已經設定的。
  • 整段軌跡 vs 子軌跡:當前的模型和資料處理似乎關注的是 整段軌跡的異常檢測,沒有顯式地考慮子軌跡。如果你希望在模型中引入 子軌跡 的處理,可以考慮在資料載入時,將軌跡分割為多個子軌跡或子序列,作為新的輸入進行處理。這可能需要對現有的模型架構進行一定的修改,比如使用滑動視窗、圖結構建模等方法。

所以它這裡用的是路段,而沒有使用座標?

是的,這裡使用的是路段(road segments)而不是直接使用座標(coordinates)。具體來說,實驗中的軌跡資料是透過將每個軌跡與實際的路網進行匹配後,得到的每條軌跡由多個路段組成。每個路段代表了車輛行駛路徑中的一個具體部分,而不是原始的GPS座標點。

在這項研究中,軌跡資料經過預處理和過濾,所有的軌跡都已被匹配到路網,並且每個軌跡的路段是按照實際的道路網路進行表示。這樣,軌跡不僅是由GPS座標點組成的,更多的是與道路網路中的具體路段相關聯的。

例如,在生成異常軌跡時(如Detour和Switch),操作是基於路段的選擇和修改,而非座標點本身。這種基於路段的表示方式幫助模型更好地理解和處理與實際路網相關的異常行為。

這裡有提及軌跡資料的長度麼

在這段內容中提到的軌跡資料的長度有一些說明,但並沒有提供詳細的每條軌跡的長度。具體來說:

  • 過濾條件:在資料預處理階段,作者過濾掉了 長度小於30的軌跡,也就是說,每條軌跡至少有30個路段(segments)以上。

  • 軌跡內容:每條軌跡由多個路段(road segments)組成,並且這些軌跡已經與路網匹配。

雖然沒有詳細列出每條軌跡的具體長度(如每條軌跡有多少個路段),但可以推測每條軌跡的長度應該大於等於30個路段,符合實驗的過濾條件。

因此,軌跡的長度並不是非常具體的數值,而是透過這一過濾條件間接給出了軌跡的最小長度。

也就是50條是訓練的,50是條是同分布的測試。還有ood的?

是的,您的理解是正確的。根據文中的描述,實驗中每個城市選擇了100個源-目的地(SD)對,每對至少有100條軌跡。

  • 訓練集:從每個SD對中隨機選擇50條軌跡作為訓練資料。
  • ID測試集:剩下的50條軌跡作為測試資料,這些資料與訓練資料來自相同的分佈,即同分布資料
  • OOD測試集:這部分資料並不來自訓練資料的同一分佈,通常是透過選取其他SD對的軌跡,或者透過某些策略生成的與訓練資料分佈不同的資料,用來測試模型在分佈外資料上的表現。

所以,每個城市的100個SD對中,會有一半的軌跡用於訓練,另一半用於ID測試集,而OOD測試集則是從其他資料來源中選取的。

程式碼處理與文字是否一致

從您提供的程式碼來看,資料處理和前面文字中的描述大致一致。以下是幾個關鍵點對比:

  1. 路段表示:程式碼中的 TrajectoryLoaderGraphLoader 類實現了軌跡資料的載入和處理,資料以路段(而非原始座標)為單位。每個軌跡被對映到具體的路段(透過 node2id),並且在預處理時進行了填充(bos_eos_pad)和批處理(batch_preprocess),符合文中提到的基於路段的軌跡表示。

  2. 軌跡長度:程式碼並未特別強調軌跡長度的過濾條件(例如小於30個路段的軌跡),但透過批處理部分來看,軌跡的長度是存在的,並且在資料預處理過程中進行了填充操作。可以推測,程式碼中也間接遵循了類似的過濾規則,確保每個批次的軌跡都有一定的最小長度。

  3. 資料集劃分

    • 訓練集ID測試集 的劃分上,程式碼透過 TrajectoryLoader 類載入了訓練資料,並且進行了批處理。雖然程式碼沒有明確顯示 50/50 的比例劃分,但從 train_epoch 方法和資料集的使用上可以看出,模型會在這些資料上進行訓練和評估。
    • OOD測試集:在 test 方法中,程式碼顯式地使用了 TrajectoryLoader 載入了正常資料集、繞行資料集(detour)、切換資料集(switch)、以及 OOD 資料集(ood_dataset)。這與文中提到的使用與訓練資料分佈不同的資料來評估模型在 OOD 資料上的表現一致。

總結來說,程式碼實現與文中的實驗設計是匹配的,尤其是在資料集劃分和軌跡的表示方式(路段)上,程式碼中對資料集的載入、劃分和處理邏輯與文中的描述一致。

前兩個是資料,後兩個是長度

好的,下面是一個表格,展示了您提到的幾個變數及其含義:

變數 維度 說明
src_data_batchs torch.Size([32, N]) 輸入資料的批次,每個批次包含 32 條軌跡資料,每條資料的長度為 N(即路段數)。
trg_data_batchs torch.Size([32, N+2]) 目標資料的批次,每個批次包含 32 條軌跡資料,每條資料的長度為 N+2(包括 boseos 標記)。
src_length_batchs torch.Size([32]) 每條輸入軌跡的真實長度,即有效的路段數(不包括填充)。
trg_length_batchs torch.Size([32]) 每條目標軌跡的真實長度,即有效的元素數(不包括填充的 boseos 標記)。

示例

假設某個批次的維度如下:

  • src_data_batchs: torch.Size([32, 135])

    • 表示當前批次的 32 條輸入資料,每條資料包含 135 個路段。
  • trg_data_batchs: torch.Size([32, 137])

    • 表示當前批次的 32 條目標資料,每條資料包含 137 個元素,其中 135 個來自輸入資料(路段),另外 2 個是 boseos 標記。
  • src_length_batchs: torch.Size([32])

    • 表示每條輸入資料的有效長度,例如所有 32 條資料的真實長度為 135(即每條資料的實際路段數)。
  • trg_length_batchs: torch.Size([32])

    • 表示每條目標資料的有效長度,例如所有 32 條資料的目標長度為 137(即每條資料的有效元素數,包括 boseos 標記)。

這個表格總結了資料的結構,幫助您理解各個變數的作用。如果有其他具體問題或需要進一步擴充套件的部分,請隨時告知!

資料的輸入和處理,一個批次32條資料。資料是否來自同一個SD?似乎不一定來自同一個SD

是的,TrajectoryLoader 中的資料處理方式確認了每個批次中資料是分開處理的,並分別送入模型進行訓練。在程式碼中,資料是透過以下幾個步驟組織的:

1. 資料載入 (load_data 方法)

  • load_data 方法從指定的路徑載入軌跡資料(trajectory_path)。資料集是一個字典(dataset),每個字典項對應一個軌跡(traj)。軌跡中的每個節點被對映為一個ID(使用 node2id 字典),並儲存到 self.data 列表中。
  • 資料在載入時會根據 self.shuffle 引數決定是否打亂順序。

2. 處理 BOS、EOS 和 Padding

  • BOS (Beginning of Sequence) 和 EOS (End of Sequence) 是用於序列開始和結束的特殊標記。self.bosself.eos 分別被設定為 self.label_num-3self.label_num-2
  • Padding (self.pad) 用於填充較短的軌跡,使它們具有相同的長度,以便能夠批處理。

3. 批次預處理 (batch_preprocess 方法)

  • 該方法按批次處理資料。每個批次包含 batch_size 條軌跡資料(如果資料不足一個完整批次,則包含剩餘的資料)。每條軌跡資料被劃分為源資料和目標資料:
    • 源資料 (src_data_batchs):從軌跡中直接提取的資料(沒有 BOS 和 EOS 標記)。
    • 目標資料 (trg_data_batchs):在源資料的基礎上,加上 BOS 和 EOS 標記。
  • 每個批次中的軌跡會被填充(padding),使所有軌跡長度一致:
    • 源資料會填充到最大長度 max_length,目標資料會填充到 max_length + 2(為了容納額外的 BOS 和 EOS 標記)。

4. 批次資料和長度資訊

  • src_data_batchstrg_data_batchs 分別儲存源資料和目標資料(每個批次是一個 PyTorch Tensor)。
  • src_length_batchstrg_length_batchs 儲存每條軌跡的實際長度(在填充後,實際長度可能小於填充後的最大長度)。

5. 批次資料列印

  • batch_preprocess 方法中,每次生成一個批次後,會列印該批次的源資料、目標資料及其長度的維度,以便除錯和檢視資料是否正確組織。

總結:

在每個批次中,資料是分別處理並輸入到模型中的:

  • src_data_batchs 儲存源軌跡資料。
  • trg_data_batchs 儲存目標軌跡資料(加上了 BOS 和 EOS)。
  • 每條軌跡都會被填充以確保每個批次中的軌跡具有相同的長度,便於批次處理。
  • 這些資料(源資料和目標資料)是按批次處理的,但它們不一定來自同一個源-目標對。

這種資料處理方式是常見的序列建模(如 RNN 或 Transformer)訓練中的方法,它將源序列和目標序列(通常是帶有特殊標記的)作為輸入傳遞給模型。

應該是第二種

從您的描述來看,src_data_batchstrg_data_batchs 是每批次輸入到模型的資料,通常每個批次(batch)包含32條軌跡資料。對於如何組織這些資料(是否同一個SD對的軌跡一起輸入,或者來自不同的SD對),有兩種常見的處理方式:

1. 每個批次包含相同SD對的軌跡(SD內批次)

  • 同一SD對的軌跡輸入一起:在這種方式下,每個批次中的32條軌跡通常來源於相同的源-目的地(SD)對,也就是說,每個批次中的32條軌跡的源資料和目標資料都來源於同一個SD對。例如,假設你有100個SD對資料,每個SD對都有100條軌跡,經過劃分後,每批次會從同一個SD對中選擇32條軌跡資料作為源和目標資料輸入模型。

  • 源資料和目標資料配對:每批次中的src_data_batchstrg_data_batchs的軌跡是一一對應的,它們通常是從同一個SD對中的源軌跡和目標軌跡中選取的。這種方式保證了模型在訓練時每次處理的都是真實的源到目標的對映關係,模型學習如何從源資料(如輸入軌跡)對映到目標資料(如輸出軌跡)。

2. 不同SD對的軌跡混合輸入(跨SD批次)

  • 不同SD對的軌跡混合輸入:另一種常見方式是,在一個批次中,源資料和目標資料可能來自不同的源-目的地(SD)對。這樣,每個批次的32條軌跡可能包含多個SD對的資料。這種方式可以增加訓練資料的多樣性,有助於模型泛化能力的提升。

  • 資料打亂與混合:為了保證不同SD對的軌跡資料能被混合並輸入到模型中,通常會對軌跡資料進行隨機打亂(shuffling)。這種方法常用於神經網路訓練中,幫助模型更好地從多樣的資料中學習,而不會僅限於同一SD對的樣本。

3. 模型輸入的處理方式

無論是哪種方式,資料的輸入過程都會遵循以下幾個步驟:

  • 資料預處理:將每條軌跡轉換為數字化表示,可能包括詞彙表的查詢、編碼等。
  • 填充與對齊:由於每條軌跡的長度可能不同,通常會進行填充操作,使得批次中的所有軌跡具有相同的長度(例如,填充到最大軌跡長度)。這就是為什麼src_length_batchstrg_length_batchs是必需的,它們記錄了去除填充的實際軌跡長度。
  • 輸入到模型:填充後的軌跡資料會被輸入到模型的編碼器部分,目標資料會輸入到解碼器部分。模型會透過源資料預測目標資料的相應部分,尤其是在生成任務中,模型會學習如何從源資料的表示中生成目標資料。

綜上所述:

  • 是否來自同一個SD對:如果採用的是SD內批次(同一個SD對的資料一起輸入),那麼每個批次中的32條軌跡都會來自同一個SD對。而如果採用跨SD批次(不同SD對的資料一起輸入),那麼一個批次中的32條軌跡可能來自多個不同的SD對。
  • 訓練策略:不同的訓練策略會影響這種資料的組織方式。在很多情況下,尤其是針對大規模資料集時,採用跨SD批次可以提高訓練效率,而對於需要保持源和目標一致性較高的任務(如序列到序列的生成任務),可能會使用SD內批次

根據您的具體任務和模型設計,這兩種方法都可以合理地進行選擇。如果需要進一步確認是哪種方式,您可以檢視資料載入部分的程式碼,看看批次資料是如何組織的。

模型總體架構

這個模型是一個包含多種元件的神經網路模型,主要用於序列資料的學習和預測。它結合了圖卷積神經網路(GNN)、變分自編碼器(VAE)、自信度計算模組、損失計算模組等。以下是模型的詳細結構和工作原理:

1. 模型初始化 (__init__ 方法)

  • 輸入引數
    • input_size:輸入資料的維度(如每個節點的特徵維度)。
    • hidden_size:隱藏層的大小。
    • device:裝置型別(如 'cuda''cpu')。
    • layer_rnn:RNN層的數量(用於VAE中)。
    • label_num:標籤數量(輸出維度)。
    • edge_num:圖中邊的數量。
  • 元件
    • Confidence:自信度模組,負責計算每個預測的自信度。
    • VAE:變分自編碼器模組,處理序列資料的重構和潛在空間的編碼/解碼。
    • road_embedding:透過嵌入層對路網中的每個節點進行編碼,對映到 hidden_size 維度。
    • projection_head:一個引數矩陣,用於將潛在空間的表示對映到標籤空間。
    • sd_projection_head:另一個投影矩陣,可能用於處理與“SD”相關的任務。
    • gnn:圖卷積網路(SPGNN),用於在圖結構資料上進行學習。
    • sd_loss:計算“SD”相關損失的負對數似然損失函式。
    • log_soft:LogSoftmax層,用於將模型輸出轉化為機率分佈。

2. 損失函式 (loss_fn 方法)

  • loss_fn 是一個自定義的損失函式,計算給定目標序列(target)和模型輸出(p_x)之間的損失,考慮了掩碼(mask)來忽略特定的無效或填充部分。
  • 步驟
    1. p_x 轉換為機率分佈(透過 torch.exp(p_x))。
    2. 使用掩碼 maskp_x 進行加權,忽略無效部分。
    3. 計算負對數似然(NLL)損失:nll = -torch.log(p_x)

3. 掩碼生成 (get_mask 方法)

  • get_mask 方法生成一個掩碼,指定哪些節點/標籤在當前批次中是有效的。這是透過遍歷邊列表並與標籤進行匹配來實現的,確保每個標籤都有相應的掩碼。
  • 步驟
    1. 根據 edge_listlabel 生成一個掩碼,標識哪些標籤是有效的。
    2. 構造掩碼矩陣,使得每個有效的標籤位置為 1,無效的位置為 0。

4. 前向傳播 (forward 方法)

  • forward 方法執行前向傳播過程,計算損失並返回多個輸出(如 nll_losskl_lossconfidencesd_nll_loss)。
  • 輸入
    • src:源序列(輸入序列)。
    • trg:目標序列(輸出序列)。
    • edge_list:邊列表,表示當前子圖的結構。
    • src_lengths:源序列的長度。
    • trg_lengths:目標序列的長度。
  • 步驟
    1. 自信度計算:透過 self.confidence(src) 計算輸入序列的自信度。
    2. 條件輸入構造:從源序列中提取特定位置的元素,並與目標序列合併形成條件輸入(srctrg 之間的關係)。
    3. 序列嵌入:透過 road_embedding 將源序列和目標序列對映到 hidden_size 維度的空間。
    4. 變分自編碼器(VAE):使用 VAE 計算 kl_loss(Kullback-Leibler散度損失),以及透過 VAE 解碼得到的預測(p_xsd_p_x)。
    5. 標籤對映與損失計算
      • p_x 投影到標籤空間,計算 nll_loss(負對數似然損失)。
      • 透過 sd_p_x 計算“SD”相關的損失(sd_nll_loss)。
    6. 輸出
      • nll_loss:負對數似然損失。
      • kl_loss:KL散度損失。
      • confidence:每個預測的自信度。
      • sd_nll_loss:與“SD”相關的損失。

5. 整體流程

  • 圖嵌入與預測:首先,源和目標序列透過嵌入層對映到隱藏空間,隨後透過變分自編碼器(VAE)進行潛在空間的學習和序列生成。自信度模組計算每個預測的置信度,圖卷積網路(GNN)模組則用於捕捉圖結構中的關係。
  • 損失計算:模型透過對數似然損失、KL散度損失和“SD”損失進行組合,進行訓練和最佳化。

總結:

這個模型結合了圖卷積(GNN)、變分自編碼器(VAE)和自信度計算,適用於處理圖資料和序列資料的任務。它透過多個元件的結合,處理複雜的輸入資料並計算相應的損失,最終輸出包括預測損失、KL損失、自信度和“SD”損失等多個資訊。

圖卷積模組

在這個模型中,圖神經網路(GNN)透過 SPGNNLayersSPGNN 類實現,並且主要利用圖卷積操作對資料進行處理。下面是對軌跡資料的編碼過程的分析。

1. SPGNN 層(SPGNNLayers

  • 輸入

    • x: 輸入特徵,通常是節點的嵌入(如圖中每個節點的特徵表示)。這裡是 (hidden_size, label_num) 的矩陣,表示每個節點在圖中的嵌入。
    • edge_list: 形狀為 (2, edge_num) 的張量,表示圖中邊的索引,通常是一個二元組 (source_node, target_node),表示從源節點到目標節點的邊。
    • edge2id: 是一個索引張量,表示樣本邊的索引。它通常是一個一維張量,記錄了哪些邊被取樣。
  • 過程

    • 邊權重計算:首先,透過 sp_softmax 函式計算每個邊的權重,這裡採用的是稀疏的 softmax 函式。透過邊列表和邊權重(edge_weight)來計算每條邊的權重。這個過程的目的是根據鄰接節點間的關係對節點特徵進行加權。
    • 圖卷積:然後,透過 sp_matmul 函式執行圖卷積操作。圖卷積的核心思想是將每個節點的特徵與其鄰接節點的特徵加權相加,從而聚合資訊。
  • 輸出

    • 輸出的 x 是更新後的節點嵌入,這些嵌入經過圖卷積層的處理後,可以包含節點之間的資訊傳遞和關係。

2. SPGNN(SPGNN

  • 這個類封裝了 SPGNNLayers,並且透過 forward 方法傳遞資料:
    • 輸入
      • projection_head: 這是節點的特徵,應該是輸入到 SPGNN 模組的資料,它通常是經過某種嵌入或投影后的節點特徵。
      • sub_edge: 這是取樣到的邊,決定了哪些邊用於圖卷積操作。
      • edge2index: 這是邊的索引,幫助我們標識哪些邊被選中參與計算。
    • 處理SPGNN 透過呼叫 SPGNNLayers 中的 forward 方法來執行圖卷積操作,將輸入特徵和邊的關係進行處理。
    • 輸出:返回更新後的節點特徵(projection_head)。

3. 圖卷積操作(sp_softmaxsp_matmul

  • sp_softmax:計算每條邊的權重,採用的是一種稀疏的 softmax 操作。首先根據邊的目標節點計算邊的權重,再將這些權重應用於圖卷積中。
    • 輸入:indices 表示邊的目標節點,values 是邊的權重,N 是圖中節點的數量。
    • 透過 scatter_add_ 對邊權重進行歸一化,得到一個稀疏的 softmax 分佈。
  • sp_matmul:稀疏矩陣乘法,執行圖卷積操作。它根據邊的關係將節點特徵進行加權求和,輸出更新後的節點特徵。
    • 輸入:indices 表示邊的目標節點,values 是邊的權重,mat 是節點特徵矩陣。
    • 輸出:加權後的節點特徵。

4. 軌跡資料的編碼

  • 軌跡資料處理流程
    • 在這個模型中,軌跡資料通常表示為一系列的節點序列,其中每個節點表示一個位置或狀態。軌跡資料首先透過 TrajectoryLoader 轉換成節點索引的形式,便於處理。
    • 然後,在 Model 中,軌跡的輸入(如源序列 src 和目標序列 trg)經過嵌入層(road_embedding),將每個節點的索引對映到一個固定維度的特徵空間中。
    • 接著,嵌入後的節點特徵會作為輸入傳遞給圖神經網路(SPGNN)進行處理。圖神經網路透過圖卷積操作在圖結構中傳播資訊,聚合鄰居節點的特徵,進一步豐富每個節點的表示。

5. 結合軌跡資料與圖卷積

  • 軌跡資料在圖卷積網路中編碼的過程是透過將軌跡資料轉化為圖的節點特徵,並透過圖卷積操作聚合節點之間的資訊。
  • 在訓練過程中,模型將軌跡資料對映到圖結構中,利用圖神經網路來處理節點之間的相互影響,使得每個節點的表示不僅包含原始特徵,還融合了鄰居節點的資訊。這種方式能夠捕捉到軌跡資料中的時序和空間關係,以及節點之間的結構依賴。

總結:

在這個網路中,軌跡資料透過嵌入層被轉化為節點的特徵表示,並透過圖卷積網路(GNN)進行編碼。GNN 利用軌跡中各節點之間的空間關係來更新每個節點的表示,從而有效捕捉到軌跡資料中的時序和空間依賴性。

VAE 模組

這個 VAE 模組實現了一個變分自編碼器(Variational Autoencoder, VAE)結構,結合了編碼器(EncoderRNN)和解碼器(DecoderRNN),並且透過 DecoderSD 進行額外的處理。我們將逐步分析它是如何編碼輸入資料的。

1. VAE 模組(VAE)的總體結構

  • 編碼器EncoderRNN

    • 輸入:源序列(src)的特徵。
    • 輸出:對輸入的潛在表示(latent variable)的機率分佈(正態分佈),以及該分佈的均值(mu)和標準差(sigma)。
  • 解碼器DecoderRNN

    • 輸入:從編碼器得到的潛在變數 z,目標序列(trg)。
    • 輸出:透過解碼器生成的目標序列的預測機率分佈。
  • 潛在變數的生成:潛在變數 z 是從編碼器的輸出分佈 q(z|x) 中抽樣得到的。該潛在變數被輸入到解碼器中以生成輸出序列。

  • 額外的解碼器DecoderSD 是一個輔助解碼器,進一步處理潛在變數並生成與之相關的隱藏表示。

2. 編碼過程:如何將輸入編碼成潛在空間

編碼器(EncoderRNN)的任務是將輸入序列編碼成潛在空間的分佈,併為 VAE 提供潛在變數的隨機取樣。

EncoderRNN 的編碼過程:

  1. 輸入

    • 輸入序列(src)的特徵表示,形狀為 (batch_size, seq_len, hidden_size)。這些輸入可能是透過某種嵌入層或預處理獲得的。
    • 輸入序列的長度(src_lengths)表示每個序列的實際長度,用於處理變長輸入。
  2. LSTM 層

    • 編碼器的核心是一個雙向(bidirectional=True)的 LSTM(self.lstm)。LSTM 將輸入序列的特徵對映到一個隱層空間,透過 LSTM 對序列進行處理,產生隱藏狀態。
    • 隱藏狀態被線性變換為潛在空間的均值(mu)和對數標準差(log_sigma)。
      • mu = self.enc_mu(hidden):表示潛在空間的均值。
      • log_sigma = self.enc_log_sigma(hidden):表示潛在空間的標準差,之後透過 torch.exp(log_sigma) 得到實際的標準差。
  3. 潛在變數

    • 透過 重引數化技巧(reparameterization trick)從均值和標準差生成潛在變數 z
      • z = q_z.rsample():從正態分佈 q_z 中取樣得到潛在變數 zq_z 是由編碼器產生的正態分佈 Normal(mu, sigma)
  4. 輸出

    • 編碼器的輸出是:
      • q_z: 潛在變數的分佈(Normal(mu, sigma))。
      • mu: 均值。
      • sigma: 標準差。

3. 潛在空間到輸出的對映:解碼過程

解碼器(DecoderRNN)使用從編碼器得到的潛在變數 z 生成目標序列。

DecoderRNN 的解碼過程:

  1. 輸入

    • 潛在變數 z 被輸入到解碼器,它透過 self.hidden_linear(z) 對映到合適的隱藏空間。
    • 目標序列(trg)的前 seq_len-1 個時間步用於訓練過程中目標的監督。
    • lengths 用於指示目標序列的實際長度,幫助處理變長的目標序列。
  2. LSTM 層

    • 解碼器透過 LSTM(self.lstm)生成目標序列的預測。LSTM 在每個時間步根據當前輸入和上一時刻的隱狀態來計算新的隱狀態,並生成預測的輸出。
  3. 輸出

    • 解碼器的輸出 p_x 是目標序列的預測,它是由潛在變數 z 生成的潛在目標序列的機率分佈。
  4. 訓練時

    • 如果是訓練模式(train=True),解碼器會根據輸入的目標序列生成預測輸出,並根據真實的目標序列進行訓練。
  5. 推理時

    • 在推理模式(train=False)下,解碼器逐步生成每個時間步的輸出,透過之前的輸出作為下一時間步的輸入。

4. KL 散度和潛在變數的正則化

在訓練過程中,VAE 引入了 KL 散度(KL divergence)作為損失函式的一部分,用於約束潛在空間的分佈接近標準正態分佈 N(0, 1)。具體來說:

  • kl_loss = torch.distributions.kl_divergence(q_z, torch.distributions.Normal(0, 1.)) 計算了潛在空間分佈 q_z(由編碼器生成的分佈)與標準正態分佈之間的 KL 散度。

KL 散度作為正則化項,促使模型學習到較為平滑的潛在空間,從而提高生成能力。

5. DecoderSD:潛在變數的額外解碼

DecoderSD 是一個輔助的解碼器,它接收潛在變數 z,並生成與之相關的隱藏表示。該模組可能用於進一步的任務或正則化,具體實現是透過 hidden_linear 將潛在變數 z 對映到一個隱藏表示(hidden)。

總結:如何進行編碼

VAE 模組中,編碼過程透過以下步驟實現:

  1. 輸入序列透過編碼器(EncoderRNN:將輸入序列(src)編碼成潛在空間的分佈(Normal(mu, sigma))。
  2. 潛在變數抽樣:從該分佈中取樣得到潛在變數 z,並將其輸入到解碼器。
  3. 解碼器生成輸出:解碼器(DecoderRNN)利用潛在變數 z 和目標序列(trg)生成目標序列的預測。
  4. KL 散度:計算潛在分佈的 KL 散度,並作為正則化項最佳化模型。

這整個過程實現了將輸入序列透過編碼器轉換為潛在空間的編碼,並透過解碼器生成目標序列,形成了一個典型的變分自編碼器結構。

那就是說你一條軌跡100多個時間步,每個時間步都會有一個標籤和ground truth對比?那具體怎麼計算這個loss

是的,一條軌跡中每個時間步都會有一個標籤,並且每個時間步的標籤和其對應的 ground truth 進行對比。具體來說,損失計算是透過對每個時間步的預測值和真實值之間的差異進行比較,計算出每個時間步的損失,然後對所有時間步的損失進行彙總。

損失計算流程

在這個模型中,具體的損失計算流程如下:

  1. 輸入與輸出

    • src:輸入軌跡序列,形狀為 (batch_size, seq_len)
    • trg:目標軌跡序列,形狀為 (batch_size, seq_len)
    • p_x:透過VAE解碼器生成的預測軌跡,形狀為 (batch_size, seq_len, label_num),表示每個時間步的預測標籤分佈。
  2. NLL損失(Negative Log-Likelihood Loss)

    • 計算模型的預測(p_x)和真實標籤(trg)之間的對數似然損失。

具體的步驟

1. 計算預測的機率分佈 (p_x):

在VAE模組中,p_x表示模型對目標軌跡(trg)的預測,通常是透過log-softmax將輸出轉化為對數機率分佈,表示每個時間步的標籤的預測機率分佈。

p_x = self.logsoftmax(p_x)

這個 p_x 的形狀是 (batch_size, seq_len, label_num),即對於每條軌跡中的每個時間步,模型都會輸出一個長度為 label_num 的機率分佈。

2. 計算每個時間步的負對數似然損失(NLL Loss)

每個時間步的損失是根據模型預測的機率分佈和真實標籤之間的對數差異來計算的。

p_x = p_x.view(batch_size * seq_len, -1)  # (batch_size * seq_len, label_num)
target = trg.reshape(-1)  # (batch_size * seq_len)
nll = -torch.log(p_x[torch.arange(target.size(0)).to(target.device), target])
nll = nll.view(batch_size, seq_len)  # (batch_size, seq_len)

在這裡:

  • p_x[torch.arange(target.size(0)), target] 透過索引 target 對應的標籤的預測機率。
  • 然後使用 torch.log 計算每個標籤的對數機率,並用負號轉換為負對數似然(NLL)。
  • nll 儲存了每個時間步的 NLL 損失。

3. 計算KL散度損失(KL Divergence Loss):

這是VAE特有的損失部分,它約束了潛在變數 z 的分佈使其接近標準正態分佈。KL散度損失不是針對每個時間步計算的,而是針對整個潛在空間(所有樣本)的分佈進行計算。

kl_loss = torch.distributions.kl_divergence(q_z, torch.distributions.Normal(0, 1.)).sum(dim=-1)

q_z 是由編碼器生成的潛在變數的分佈,KL散度損失會將它們與標準正態分佈進行對比。這個損失有助於使潛在空間的分佈更具正則性,但它不影響每個時間步的具體標籤預測。

4. 計算最終的損失

損失是透過對 NLL 損失KL 損失 進行加權組合計算得到的。模型的目標是最小化這兩個損失的加權和:

return nll_loss, kl_loss
  • nll_loss 是基於每個時間步的預測結果和真實標籤之間的負對數似然損失。
  • kl_loss 是潛在空間的正則化損失,確保潛在變數的分佈接近標準正態分佈。

總結

  • 逐時間步計算損失:每個時間步的損失是透過比較預測的標籤分佈(p_x)和真實標籤(trg)來計算的,具體是透過 NLL Loss 實現。
  • NLL Loss:計算每個時間步的負對數似然損失,表示預測值與真實標籤之間的差異。
  • KL Divergence Loss:用於正則化潛在空間的分佈,使得潛在變數 z 的分佈接近標準正態分佈。

最終的損失是這兩部分的加權和,模型會在訓練過程中透過最小化這個總損失來學習軌跡的生成過程。

這裡的KL loss是對編碼重構部分進行構建吧?使得編碼的值趨於0,1分佈?

是的,KL損失在這裡主要是對編碼重構部分進行構建的。具體來說,它用於將潛在空間的分佈 q_z(即透過編碼器得到的潛在變數的分佈)與標準正態分佈(標準正態分佈 N(0, 1))進行對比,從而確保潛在變數的分佈在訓練過程中逐漸逼近標準正態分佈。

KL損失的作用

  • 潛在空間的正則化:VAE的核心思想是透過編碼器將輸入資料對映到潛在空間,再透過解碼器重構輸入。KL損失鼓勵潛在變數(透過編碼器得到的分佈)接近標準正態分佈 N(0, 1)。這有助於生成模型的穩定性,並使得潛在空間具有良好的結構,便於後續的取樣和解碼。

  • 具體的計算

    • 透過編碼器,得到潛在變數的均值 mu 和標準差 sigma

    • 然後透過 torch.distributions.Normal(mu, sigma) 建立一個正態分佈,表示編碼後的潛在空間分佈。

    • 最後,KL散度計算公式如下:

      \( \text{KL}(q_z \| p_z) = \mathbb{E}_{q_z} \left[\log\frac{q_z(z)}{p_z(z)}\right] \)

      其中,q_z(z) 是編碼器得到的潛在變數的分佈(通常是高斯分佈),而 p_z(z) 是標準正態分佈 N(0,1)。KL散度的計算目標是讓 q_z(z) 儘可能接近 p_z(z),即使得編碼器的輸出分佈接近標準正態分佈。

  • 最佳化目標

    • 透過KL損失,模型會懲罰那些偏離標準正態分佈的潛在變數分佈,使得潛在變數的分佈趨近於標準正態分佈,從而促進了潛在空間的規範化。
    • 換句話說,KL損失是對潛在空間的正則化項,它確保了潛在變數不會分佈過於分散或者不規則,進而幫助解碼器更好地重構輸入資料。

KL Loss的計算

在程式碼中,KL散度損失是透過以下方式計算的:

kl_loss = torch.distributions.kl_divergence(q_z, torch.distributions.Normal(0, 1.)).sum(dim=-1)
  • q_z 是由編碼器輸出的潛在變數分佈(musigma 透過編碼器計算得到)。
  • torch.distributions.Normal(0, 1.) 是標準正態分佈,即期望潛在變數分佈接近它。
  • 透過 kl_divergence 計算 q_z 與標準正態分佈之間的KL散度,sum(dim=-1) 是對所有維度進行求和。

總結

  • KL損失的主要作用是使潛在變數的分佈接近標準正態分佈 N(0, 1),從而正則化潛在空間,促使編碼器學習到穩定且可生成的潛在空間表示。
  • 透過最小化KL散度,編碼器將學到一個可控且結構化的潛在空間,有利於後續的解碼器生成更加逼近真實資料的輸出。

1(NLL損失)和3(SD損失)的區別

在你提到的損失函式中,1(NLL損失)和3(SD損失)確實有一些相似之處,但它們的目標和計算方式存在重要區別。下面是對這兩者的詳細對比:

1. NLL損失(負對數似然損失)

NLL損失是用來衡量解碼器生成的軌跡分佈(即p_x)與真實目標軌跡(即target)之間的差距。NLL的目標是最大化目標軌跡在解碼器生成的機率分佈中的機率,這意味著模型試圖學習如何生成與真實軌跡接近的軌跡。

  • 計算過程

    • p_x 是解碼器生成的機率分佈,表示給定潛在變數後對每個時間步的預測。
    • 使用mask來過濾掉無效部分(如padding)。這個mask保證只有有效部分的軌跡(即實際存在的部分)被考慮在內。
    • 透過p_x = torch.exp(p_x)p_x應用exp,然後透過mask進行篩選。
    • 計算負對數似然 -torch.log(p_x),表示真實標籤(target)在生成的分佈中的對數機率。
  • 目標:最大化真實軌跡與生成軌跡之間的相似度,使得生成的軌跡儘可能接近目標軌跡。

3. SD損失(Soft Decoder損失)

SD損失是透過對潛在空間表示(z)的解碼計算,並與真實標籤進行對比來最佳化的。與NLL損失不同的是,SD損失主要集中在潛在空間(latent space)上,並且它有額外的操作來處理潛在變數的解碼結果。

  • 計算過程

    • 首先,sd_p_x 是潛在變數z經過解碼器後的輸出。經過線性變換(sd_p_x = sd_p_x.mm(self.sd_projection_head))後,得到解碼後的分佈。
    • 然後應用softmax(self.log_soft(sd_p_x))將解碼結果轉換為機率分佈。
    • 計算與真實標籤(sd)的負對數似然損失,並加上一個權重(0.1)來調節其重要性。
  • 目標:透過這種解碼的方式,SD損失試圖在潛在空間的表示上找到一個合理的機率分佈,使得解碼後的潛在空間與真實標籤之間的差異最小化。

主要區別

  1. 目標不同

    • NLL損失 關注的是生成的軌跡分佈與真實軌跡之間的對比,目的是最大化生成的軌跡的似然,主要用於重構過程中的解碼器輸出。
    • SD損失 關注的是潛在空間的解碼結果與標籤之間的對比,目的是確保潛在變數透過解碼器得到的表示與真實標籤有一致性。它是一種輔助損失,通常在潛在空間(latent space)上進行最佳化。
  2. 計算方式不同

    • NLL損失 是基於序列生成的逐步對比,計算每個時間步的生成機率。
    • SD損失 是基於潛在空間的解碼結果,處理的是潛在變數z的解碼後的分佈,並與標籤進行對比。它通常是在潛在空間中做一個類似於分類的任務。
  3. 作用不同

    • NLL損失 是主要的生成損失,直接影響到模型生成結果的準確性。
    • SD損失 是輔助損失,它透過確保潛在空間的表示與標籤的相似性,來幫助潛在空間學習更好的表示。

總結

  • NLL損失 主要用於訓練解碼器,使得它生成的軌跡與真實軌跡一致。
  • SD損失 主要用於潛在空間的最佳化,確保潛在變數的解碼結果與真實標籤一致。它是對潛在空間的正則化,有助於改善潛在空間表示的質量。

兩者雖然都使用了負對數似然(NLL)計算,但它們關注的目標不同:一個是在生成過程中的預測(NLL損失),另一個是在潛在空間中對潛在變數進行分類(SD損失)。

我可以這麼理解麼?一個是對編碼之前的結果計算loss。一個是對解碼後的結果計算loss

是的,你的理解是對的!我們可以這樣總結:

  • NLL損失:關注的是對 解碼後的結果(生成的軌跡分佈)與真實標籤之間的對比。透過計算每個時間步生成標籤的負對數似然來評估模型的生成能力,確保模型生成的軌跡儘可能接近真實軌跡。

  • SD損失:關注的是對 潛在空間的解碼結果(潛在變數z的解碼結果)與真實標籤之間的對比。它透過潛在空間的表示來引導解碼器生成與標籤一致的軌跡,確保模型在潛在空間中學到的表示能夠正確地生成與標籤一致的軌跡。

簡單來說:

  • NLL損失是對 生成的軌跡(解碼後的結果)進行評估。
  • SD損失是對 潛在變數的解碼結果(透過潛在空間生成的軌跡)進行評估。

這兩者之間的主要區別在於:

  • NLL損失直接作用於生成的標籤,屬於傳統的序列預測任務。
  • SD損失則作用於潛在空間(latent space),確保潛在表示能夠正確地生成符合標籤的結果。

這兩個損失函式一起工作,幫助模型在學習過程中同時最佳化生成軌跡的質量和潛在空間的表示。

你理解得非常準確!

好的,我明白你的意思。你希望透過例子來理解 NLL損失SD損失 在軌跡資料中的計算和作用。讓我們從一條軌跡出發,逐步講解它們的計算。

假設條件

假設我們有一條軌跡,它由一系列的路段組成,包含100個時間步(例如,100個路段)。每個時間步會有一個標籤(比如說,它代表某個特定的道路或位置)。我們將透過這條軌跡的embedding來解釋損失計算。

1. NLL損失(負對數似然損失)

NLL損失的目的是對比生成的軌跡的分佈與真實軌跡之間的差異。具體來說,在軌跡生成任務中,NLL損失會計算每個時間步的生成機率與真實標籤之間的差異,目標是使得生成的軌跡儘可能符合真實軌跡。

假設資料:

  • 軌跡的標籤:[t_1, t_2, ..., t_100],每個t_i表示一個特定的標籤(比如,第i個時間步對應的道路段)。
  • 模型透過VAE解碼生成的機率分佈:p_x(t),表示在時間步i,模型預測每個可能標籤(比如道路)出現的機率。

步驟:

  1. 在每個時間步i,模型會生成一個標籤分佈 p_x(t_i),該分佈表示每個可能標籤的機率。假設有n個可能標籤,那麼p_x(t_i)是一個長度為n的向量,其中每個元素代表標籤t_i的預測機率。
  2. 真實標籤t_i和預測分佈p_x(t_i)之間的差距透過NLL損失計算。公式如下:
    \( \text{NLL loss} = -\log(p_x(t_i)) \quad \text{for each time step } i \)
    這裡,p_x(t_i)表示在第i步時模型生成標籤t_i的機率。

具體例子:
假設時間步i=3,真實標籤t_3 = 2,而模型生成的標籤分佈p_x(t_3)[0.1, 0.7, 0.2],表示標籤0的機率是0.1,標籤1的機率是0.7,標籤2的機率是0.2。

NLL損失計算如下:
\( \text{NLL loss for step 3} = -\log(p_x(t_3 = 2)) = -\log(0.2) \approx 1.609 \)

對於整條軌跡,我們會對每個時間步的NLL損失進行累加,最終得到整條軌跡的損失:
\( \text{Total NLL loss} = \sum_{i=1}^{100} -\log(p_x(t_i)) \)

NLL損失的目標: 最大化每個時間步生成的標籤的機率,使得整個軌跡的生成機率儘可能接近真實軌跡的標籤。


2. SD損失(Soft Decoder Loss)

SD損失關注的是潛在空間的解碼結果與真實標籤之間的差異。它透過一個輔助解碼器對潛在變數z進行解碼,目標是確保潛在空間中學到的表示能生成與真實標籤一致的結果。

假設資料:

  • 潛在空間變數 z:模型透過變分自編碼器(VAE)從輸入資料中學習到的潛在表示,它可以是一個長度為h的向量。
  • 解碼器:解碼器dec將潛在變數z轉換為標籤分佈。假設解碼器生成了標籤p_x,它與NLL損失中的p_x是類似的,只不過這裡的p_x是從潛在空間表示z解碼出來的。

步驟:

  1. 潛在變數 z(來自VAE的編碼器)會被輸入到解碼器中,透過解碼器生成軌跡的標籤分佈 p_x。這是一個基於潛在空間的生成過程。
  2. SD損失計算的是潛在變數z的解碼輸出與真實標籤之間的差異,通常也是透過負對數似然損失來衡量。
  3. 公式如下:
    \( \text{SD loss} = -\log(p_x(t)) \quad \text{for each time step} \)
    與NLL損失類似,SD損失關注的是潛在空間表示的解碼結果和真實標籤之間的對比。

具體例子:
假設潛在變數z = [0.5, -0.3, 0.2],解碼器將z轉換為標籤分佈p_x = [0.2, 0.3, 0.5],真實標籤t = 2。那麼SD損失為:
\( \text{SD loss} = -\log(p_x(t = 2)) = -\log(0.5) \approx 0.693 \)

對於整條軌跡,SD損失會計算每個時間步的損失,並對它們進行累加:
\( \text{Total SD loss} = \sum_{i=1}^{100} -\log(p_x(t_i)) \)

SD損失的目標: 透過潛在空間中的表示z,解碼器應該能夠生成一個接近真實標籤的輸出。SD損失確保了模型在潛在空間中學習到的表示是與標籤一致的。


總結:

  1. NLL損失:關注的是生成軌跡的每個時間步的標籤預測分佈與真實標籤之間的差異,目標是最大化生成軌跡的似然,使得生成的軌跡儘可能與真實軌跡一致。

  2. SD損失:關注的是潛在空間中的解碼結果與真實標籤之間的差異,目標是透過潛在空間學習到的表示來生成與真實標籤一致的軌跡。它通常作為輔助損失來最佳化潛在空間的表示。

舉例:

假設軌跡的標籤是 [t_1, t_2, ..., t_100]z是從VAE編碼器得到的潛在表示。如果你使用NLL損失計算每個時間步的損失,目標是最小化每個時間步生成標籤與真實標籤之間的差異。而使用SD損失時,目標是確保潛在變數z透過解碼器產生的軌跡標籤與真實標籤匹配。

希望這個例子能幫助你更清楚地理解NLL損失SD損失的區別!

confidence參與了哪些計算?

在你提供的模型中,Confidence模組參與了模型的多個計算過程,具體如下:

1. 輸入資料和模型架構

Confidence模組接收輸入為一個序列資料 data(通常是軌跡資料或路徑資料)。它的主要作用是透過編碼和解碼操作,計算潛在變數(latent variable)的分佈,並透過這些潛在變數生成預測,同時還參與了損失的計算。

具體來說,Confidence模組的功能可以分為以下幾個部分:

2. Confidence模組的作用和流程

2.1 潛在變數分佈(潛在空間的學習)

  • Confidence模組首先對輸入序列進行嵌入(embedding),透過nn.Embedding將每個標籤對映到一個連續的空間中,得到每個時間步的潛在表示。

    x = self.embedding(data)
    
  • 然後,Confidence模組透過兩個全連線層enc_muenc_log_sigma)分別計算潛在變數的均值(mu)和對數標準差(log_sigma)。透過log_sigma計算標準差 sigma

    mu = self.enc_mu(x)
    log_sigma = self.enc_log_sigma(x)
    sigma = torch.exp(log_sigma)
    
  • 這些引數用於構建一個正態分佈q_z),這是透過torch.distributions.Normal(mu, sigma)實現的。這為潛在空間的取樣提供了機率分佈。

    q_z = torch.distributions.Normal(mu, sigma)
    
  • 然後從這個正態分佈中重引數化取樣rsample),得到潛在變數 z。這個潛在變數 z 作為後續解碼器的輸入,用於生成模型的輸出。

    z = q_z.rsample()
    

2.2 解碼器部分

  • 隨後,Confidence模組將潛在變數 z 輸入到解碼器 dec,經過一系列的全連線層後得到預測結果 p_x。這裡,p_x 是模型根據潛在空間表示生成的軌跡或路徑的機率分佈。

    p_x = self.dec(z)
    
  • p_x 經過另一個全連線層(self.predict)後,輸出的是每個時間步的標籤機率(通常是每個標籤的類別機率)。然後應用logsoftmax來對這些機率進行歸一化處理:

    p_x = self.predict(p_x)
    p_x = self.logsoftmax(p_x)
    

2.3 計算損失

  • Confidence模組計算與真實標籤之間的損失。首先,透過負對數似然損失(NLL)來衡量模型生成的標籤分佈 p_x 與真實標籤之間的差距:

    nll = self.nll(p_x, data)
    
  • nll 被調整為適應批次資料的形狀(reshape),然後返回給訓練過程:

    nll = nll.reshape(batch_size, seq_len)
    

2.4 KL散度(KL Divergence)

  • 除了NLL損失,Confidence模組還計算潛在分佈 q_z 和標準正態分佈 Normal(0, 1) 之間的KL散度。KL散度用於正則化潛在空間,使其學習一個接近標準正態分佈的表示,從而幫助生成更平滑、更具有泛化能力的軌跡。

    divergence = torch.distributions.kl_divergence(q_z, torch.distributions.Normal(0, 1))
    
  • 最後,Confidence模組將NLL損失與KL散度的和返回:

    return nll + divergence.sum(dim=-1)
    

3. Confidence模組的作用總結

Confidence模組的主要作用是在模型的潛在空間中進行編碼和解碼,同時計算生成的軌跡與真實軌跡之間的差距。具體而言,Confidence

  1. 編碼輸入資料:透過嵌入和神經網路計算潛在空間的分佈引數(均值和標準差),並生成潛在變數 z
  2. 生成軌跡:將潛在變數 z 輸入到解碼器中,生成軌跡或路徑的預測分佈。
  3. 計算損失:透過負對數似然損失(NLL)和KL散度來最佳化潛在空間的表示,並確保生成的軌跡與真實軌跡之間的一致性。

4. Confidence在整體模型中的作用

Confidence模組在模型的訓練中發揮了以下幾個作用:

  • 它透過潛在變數 z 的取樣和解碼,參與了整個模型的生成過程,直接影響到生成的軌跡。
  • 它計算了KL散度,幫助潛在空間的分佈收斂到標準正態分佈,從而增強模型的泛化能力。
  • 它計算了NLL損失,確保生成的軌跡與真實軌跡相匹配,從而最佳化模型的重構能力。

總的來說,Confidence模組是一個融合了變分自編碼器(VAE)和監督學習的元件,既用於生成軌跡,也透過正則化潛在空間來提升模型的表現。

雙通道的學習

是的,你的理解是對的!在這段程式碼中,模型的前向過程確實涉及了 confidenceVAE 的聯合使用,具體步驟如下:

1. Confidence模組

  • 作用confidence 模組用於對輸入的 src(即輸入序列)進行編碼,計算潛在空間的分佈和潛在變數的取樣,生成潛在變數的解碼結果。
  • confidence = self.confidence(src):呼叫 Confidence 模組,生成一個潛在空間分佈的輸出。confidence 的作用是提取輸入序列的潛在空間分佈,併為後續的生成和損失計算提供依據。

2. 處理輸入序列

  • 接下來,模型根據 src_lengthssrc 做進一步處理,提取了起始和目標序列的條件資訊(cond_srccond_trg)。
  • cond_src 取了輸入序列的第一個時間步,cond_trg 則取了輸入序列在 src_lengths 指定位置的時間步資料。然後將這兩者拼接成新的輸入序列 src

3. VAE模組的Forward計算

  • 然後,src 會被送入 VAE 模組進行前向傳播。VAE的作用是學習輸入的潛在空間表示,並透過編碼器-解碼器結構生成新的軌跡。
  • kl_loss, p_x, sd_p_x = self.vae.forward(src, trg, src_lengths, trg_lengths):這一步是對 srctrg 進行聯合訓練,VAE 會輸出:
    • kl_loss:KL 散度損失,用於約束潛在空間的分佈接近標準正態分佈。
    • p_x:基於潛在空間 z 生成的目標序列的機率分佈。
    • sd_p_x:潛在空間的解碼結果,用於後續計算SD損失。

4. 總結

  • confidence 用來為模型提供潛在空間的分佈資訊(即編碼的分佈),並幫助生成軌跡。
  • 然後,VAE 透過進一步的編碼-解碼過程,生成目標軌跡並計算損失。

透過結合 confidenceVAE,模型可以從潛在空間中學習到軌跡生成的規律,同時保持對潛在空間的良好控制,以便在生成軌跡時既能提高精度,又能提高魯棒性。

因此,首先是透過 confidence 計算潛在空間的分佈和生成過程,再結合 VAE 進行目標軌跡的生成和解碼。

雙VAE的程式碼對比

在你提供的程式碼中,模型利用了兩個不同的 VAE(TG-VAE 和 RP-VAE)來處理軌跡資料和路段資料。讓我們分開解釋並分析每個部分的實現,特別是如何使用 VAE 和損失計算。

1. TG-VAE 部分

在模型中,TG-VAE 部分主要負責建模軌跡對 (T, C) 的模式,並計算軌跡的重構損失和 KL 散度損失。這個部分的實現大致可以歸納為以下幾個步驟:

TG-VAE 作用:

  • 輸入:軌跡資料對 (T, C)。
  • 編碼:透過編碼器對軌跡對進行編碼,得到潛在變數 z
  • 解碼:解碼器生成與軌跡資料相關的機率分佈,重構軌跡。
  • 損失計算:透過負對數似然損失(NLLLoss)和 KL 散度損失進行訓練。

在程式碼中,vae.forward() 實際上呼叫的是 TG-VAE 的編碼和解碼過程。

kl_loss, p_x, sd_p_x = self.vae.forward(src, trg, src_lengths, trg_lengths)
  • srctrg 是輸入和目標軌跡。
  • kl_loss 是透過計算潛在空間的 KL 散度得到的損失。
  • p_x 是解碼器生成的軌跡的機率分佈。
  • sd_p_x 是解碼器生成的潛在空間的輸出,後續用於 SD損失

2. RP-VAE 部分

RP-VAE 部分負責處理每個路段的偏差因子,透過變分推斷計算潛在的路段偏差因子。具體實現步驟如下:

RP-VAE 作用:

  • 輸入:路段資訊(edge_list)。
  • 編碼:對每個路段的偏差因子進行編碼,計算路段的潛在空間。
  • 解碼:解碼器根據潛在空間資訊生成去偏的軌跡。
  • 損失計算:計算路段偏差因子的 KL 散度損失和生成軌跡的重構損失。

3. 程式碼中的損失計算部分

在你的程式碼中,loss_fnget_mask 是用來計算 NLL損失 的,而 KL散度損失 計算是透過 vae.forward() 進行的。詳細程式碼如下:

NLL損失計算

def loss_fn(self, p_x, target, mask):
    """
    Input:
    p_x (batch_size*seq_len, hidden_size): P(target|z)
    target (batch_size*seq_len) : the target sequences
    mask (batch_size*seq_len, vocab_size): the mask according to the road network
    """
    p_x = torch.exp(p_x)  # 對p_x進行exp計算
    p_x = p_x * mask.float()  # 應用mask以忽略不相關部分
    masked_sums = p_x.sum(dim=-1, keepdim=True) + 1e-6  # 對每一行進行求和以歸一化
    p_x = p_x / masked_sums  # 歸一化
    p_x[:, self.label_num-1] = 1  # 設定最後一個標籤的機率為1(通常是padding標記)
    
    p_x = p_x[torch.arange(target.size(0)).to(target.device), target]
    nll = -torch.log(p_x)  # 計算NLL損失
    return nll

KL散度損失計算

KL 散度損失的計算是透過 vae.forward() 方法得到的。vae.forward() 呼叫了變分推斷來計算潛在空間的 KL 散度,目標是將潛在空間的分佈趨近於標準正態分佈。

kl_loss, p_x, sd_p_x = self.vae.forward(src, trg, src_lengths, trg_lengths)

SD損失計算

SD損失是透過解碼後的潛在變數 sd_p_x 計算的,它使用了 NLLLoss 來計算潛在空間表示的損失:

sd_p_x = sd_p_x.view(batch_size*2, -1)
sd_p_x = sd_p_x.mm(self.sd_projection_head)
sd_p_x = self.log_soft(sd_p_x)  # 對 sd_p_x 應用 softmax
sd = sd.view(-1)
sd_nll_loss = 0.1 * self.sd_loss(sd_p_x, sd)  # 計算SD損失

4. 總結

  • TG-VAE 主要負責編碼和解碼軌跡對 (T, C),並計算 NLL 和 KL 散度損失。
  • RP-VAE 主要負責處理路段偏差因子的編碼和解碼,並計算 KL 散度損失。
  • confidence 參與了編碼部分,生成的潛在表示影響後續的模型訓練。

總結表格

VAE模組 輸入資料 主要計算 輸出 損失計算
TG-VAE 軌跡資料 (T, C) 編碼軌跡資料,得到潛在空間表示,解碼後生成軌跡 kl_loss(KL 散度),p_x(重構軌跡) KL 散度損失 + NLL 損失(生成軌跡與真實軌跡對比)
RP-VAE 路段資料 編碼路段的偏差因子,解碼後生成去偏的軌跡 sd_p_x(潛在表示的解碼結果) KL 散度損失 + NLL 損失(潛在表示與標準正態分佈對比)

程式碼實現過程:

  1. 編碼:VAE 編碼器將輸入資料(軌跡或路段)對映到潛在空間。
  2. 解碼:VAE 解碼器生成與輸入資料相匹配的軌跡分佈。
  3. 損失計算:根據生成的軌跡與真實軌跡的差異計算 NLL 損失,並根據潛在分佈與標準正態分佈的差異計算 KL 散度損失

這個過程描述了你程式碼中 VAE 結構的核心,並明確瞭如何透過兩個不同的 VAE 處理軌跡資料和路段資料,最佳化潛在空間。

兩個VAE輸入資料的不同

是的,程式碼中的輸入資料確實不同,主要是針對兩個 VAE 模組 (TG-VAE 和 RP-VAE) 的不同輸入。為了幫助你理解,我們可以透過一個簡單的例子來解釋這兩個部分的區別和它們的輸入資料。

1. TG-VAE(Trajectory Generation VAE)

輸入資料

  • src(輸入序列):這是軌跡的輸入資料,表示為時間步(比如說一條路徑的路段)。
  • trg(目標序列):這是目標軌跡資料,通常是 src 的一個變體,比如經過擾動或修改後的軌跡。

舉例:

假設你有一個城市的軌跡資料集,軌跡代表的是某個車輛的路徑。每個時間步是車輛的位置。

時間步 座標(位置)
1 (x1, y1)
2 (x2, y2)
3 (x3, y3)
4 (x4, y4)

假設 src 是車輛的實際軌跡(輸入),而 trg 是目標軌跡,可能是擾動過後的軌跡或者是預測的軌跡(例如,軌跡預測模型生成的路徑)。

  • src = [ (x1, y1), (x2, y2), (x3, y3), (x4, y4) ] -> 這是你輸入的實際軌跡資料。
  • trg = [ (x1, y1), (x2, y2), (x3, y3), (x4, y4) ] -> 這是你想要模型重構的目標軌跡資料。

TG-VAE 中,srctrg 作為輸入,用於訓練模型的編碼器和解碼器。目標是透過潛在空間 z 來學習軌跡的生成模式。

計算的損失:

  1. NLL 損失:透過模型生成的軌跡分佈和實際軌跡之間的對比。
  2. KL 散度損失:透過潛在空間分佈(q_z)和標準正態分佈(N(0, 1))的差異來最佳化模型。

2. RP-VAE(Road Segment VAE)

輸入資料

  • edge_list(子圖的邊資訊):這不是軌跡資料,而是表示道路網路的邊資訊。例如,城市的道路圖中的路段和連線的交叉口。
  • label(目標標籤):與每個路段相關的標籤(可能是路段的偏差因子等資訊)。

舉例:

假設你有一個城市的道路圖。圖的節點代表交叉口,邊代表道路連線。

節點對 路段長度
A-B 100米
B-C 200米
C-D 150米
D-E 120米

edge_list 可以表示這些道路連線的節點對:

  • edge_list = [(A, B), (B, C), (C, D), (D, E)]

label 可以表示每個道路的某種偏差因子,比如某個道路的交通流量偏差、路況、或者車輛的行駛行為。

  • label = [0.5, 0.2, 0.3, 0.4] -> 這些是每個路段的偏差因子。

RP-VAE 中,輸入的 edge_listlabel 用於編碼路段的偏差因子,並透過變分推斷最佳化這些因子。

計算的損失:

  1. NLL 損失:透過解碼後的潛在空間表示 sd_p_x 和標籤 sd 之間的對比來計算。
  2. KL 散度損失:透過潛在空間的分佈和標準正態分佈的差異來最佳化模型。

總結

模組 輸入資料 解釋 損失計算
TG-VAE src(軌跡資料),trg(目標軌跡資料) 軌跡資料對,模型學習軌跡的生成和重構。 NLL 損失 + KL 散度損失(最佳化軌跡的生成模式和潛在空間分佈)
RP-VAE edge_list(路段資訊),label(路段標籤) 路段資訊對,模型學習路段的偏差因子。 NLL 損失 + KL 散度損失(最佳化路段的偏差因子和潛在空間分佈)

整體流程舉例:

假設你有一輛車的軌跡資料,它經過了不同的道路(這些道路可以在一個圖中表示)。透過 TG-VAE,你可以學習這條軌跡的生成過程,即透過模型對軌跡進行編碼並重構它。而透過 RP-VAE,你可以為這些道路(路段)計算偏差因子,並最佳化它們,以便生成符合實際道路特徵的軌跡。

你輸入的 srctrg 主要是軌跡資料,而 edge_listlabel 主要是與道路相關的拓撲資訊和偏差因子。在這個過程中,TG-VAERP-VAE 分別對軌跡資料和路段資料進行建模,計算相應的損失,最終最佳化整個模型。

相關文章