摘要
本周重點跟著網課學習了pytorch框架下張量的各種常用操作API,為后面跑模型做準備,因為看的視頻比較偏向原理,現在對張量有了一個新的認識。其次在時序的研究上,最近我在看圖神經網絡跟時序結合的方向,所以本周學習了GNN模型以及跟時間序列結合的方式有哪些。
This week’s focus was on learning various common tensor manipulation APIs under the PyTorch framework through online courses, in preparation for running models in the future. Since the videos I watched were more theoretical, I now have a new understanding of tensors. In addition, in my study of time series, I have recently been exploring the combination of graph neural networks (GNN) with time series data, so this week I learned about GNN models and different ways to combine them with time series.
時序知識學習
GNN與時序
GNN解決什么問題?
上學期學習的CNN、RNN之類的模型對于圖像,文字之類的歐幾里得數據可以進行很好的處理。**歐幾里得數據:**存在于N維歐氏空間的東西,比如2維表格里面的數據,再比如CNN經常做的RGB圖像數據是3維數據(h*w*c)。
然而我們的現實世界更多的是非歐幾里得數據,比如交通網、社交網絡、污染物擴散,這種難以排序,沒有坐標參考點,難以用矩陣或張量表示的數據就無法用CNN、RNN之類模型,而GNN這種模型特點就是用來處理這種非歐幾里得數據,它以圖為輸入,輸出各種下游任務的預測結果,比如:
-
節點分類:預測某一節點的類型
-
邊的預測:另一種使用GNN的方法是找到可以為圖形增加價值的新邊。以社交網絡為例,GNN可以找到在嵌入空間中與某人關系密切但還不是朋友的用戶(節點)(也就是沒有將他彼此聯系起來的邊),然后可以將這些用戶作為朋友推薦介紹給他。
-
聚類:識別密集連接的節點形成的簇。
圖
圖這部分在數據結構中已經學習過了所以沒有詳細看,這部分我主要看圖是以什么形式進行輸入。在數據結構中,圖的表示方式有兩種,一種是領接矩陣一種是領接列表,在數據結構中,圖用領接矩陣來表示的話,內存占用特別大O(n的平方),而且領接矩陣表示的圖通常很稀疏,領接列表的話可以大幅減少空間的消耗,在某些場景中可能比較有用,一般還是用領接矩陣多。理由如下:
-
領接矩陣是方陣:可以通過矩陣運算對節點進行聚合、信息傳遞等操作,這使得 GNN 能夠有效地處理大規模的圖數據。
-
稀疏性:對于大多數實際的圖數據來說,它們的領接矩陣通常是稀疏的,即只有很少的邊連接了大量的節點。這種稀疏性可以通過優化算法來利用,從而提高計算效率。
-
與深度學習框架的兼容性:許多深度學習框架中都已經內置了對矩陣運算的支持,這使得將圖表示為領接矩陣的形式更加方便與這些框架進行集成。
-
通用性:領接矩陣可以表示各種類型的圖,包括有向圖、無向圖、加權圖、多重圖等。
GNN與傳統NN作比較
CNN 的本質是將一個像素和其周圍的像素值通過(局部的)卷積核進行匯聚,經過組合多層(深度)卷積,生成一個(高層的)特征向量,該向量包含了圖像的多個特征,是各項下游任務(分類,聚類,排名等)的基礎。CNN大部分都在做特征提取的任務,這點在上周學習LSTM代碼的時候看到過用一維CNN提取特征作為LSTM輸入的例子。
而CNN的三個思想:局部、匯聚、組合,也是GNN的核心思想。
-
局部性:GNN中的局部性是指節點的表示是通過聚合其鄰居節點的表示來計算得到的。這種局部性的機制使得GNN能夠捕捉到節點在其局部鄰域內的信息,并利用這些信息來更新節點的表示。
-
匯聚:GNN的匯聚體現在一個節點從周圍節點收集信息。對信息進行匯聚,創造出一條新的信息,類似于CNN的卷積。左圖是CNN的卷積,右圖是GNN的匯聚
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-TLxz2Xyp-1681741471136)(周報+ab14e843-4446-43b7-ad38-28f6bd08ba0c/圖片.png)]
- 組合:GNN的多輪信息傳遞,相當于CNN的多層卷積,在CNN中網絡末端層的一個元素可由輸入層的多個元素匯聚而成,在GNN中,一個節點的更新不僅與跟它直接相連的節點有關,因為在第二輪傳遞的時候,信息就到達了節點的領接節點,經過多輪后任意節點所收到的消息都可能融合了很多其他節點的消息,這跟CNN的第N層卷積層的上的值由前面很多層神經元相關很類似。
首先是節點的不同:
非歐數據比歐幾里得數據的結構復雜,節點位置可以移動,但圖還是之前那個圖,所以想用傳統CNN那種用一個卷積核在圖上卷積是不可行的,并且節點的數量可能發生變化,比如新增或刪除節點,這些操作會導致與深度學習模型的輸入維度不匹配。
這個特征簡單來說就是CNN具有平移不變性,旋轉不變性。而圖的節點沒有順序,同一個圖可以有多個順序,要求的是置換不變性。比如一個圖,他的兩種順序就會出現兩種領接矩陣,直接卷積的話就會出現不同的結果。
定義: 置換不變**,考慮我們要學習一個函數 f ,將圖G(A,X)映射到空間里 ,使得f(A1,X1)=f(A2,X2) ,其中A ,X分別表示鄰接矩陣和節點特征矩陣,1和2分別對應上面的兩種順序。如果對于任意的順序 ,上面的式子都成立,就說f 是個置換不變**的函數。
其次是圖中有邊:
CNN跟RNN不是顯式地表達節點之間的依存關系,而是通過不同的節點特征來間接表達節點之間的關系,這些相關只是作為節點的特征。而GNN用邊來表示節點之間的依存關系。
如何與時序結合起來?
GNN在處理動態圖其實就是涉及到時序的問題了。我查閱了資料了解了一下幾種GNN處理時序問題的方法:
-
一種是GNN+ 時序模型的方法:
比如GNN-LSTM,即先將時間序列經過圖神經網絡進行空間上的卷積,然后再將結果輸入到LSTM中進行時間上的卷積;或者LSTM-GNN,即先利用LSTM提取時序關系,然后再輸入到GNN中進行空間上的卷積。當然,還可以分別利用LSTM和GNN直接對原始時間序列進行操作,然后再將二者結果進行組合。
-
第二種是時間切片法:
今天看到這里想起了師兄跟我提到的“每個時刻都是一個圖,根據歷史的多張圖預測未來圖的變化”應該就是這種方法,把動態圖分解為多個時間步驟,每個時間步驟都是一個靜態圖。然后,將每個時間步驟作為輸入,使用靜態圖的GNN進行處理。
-
基于注意力機制的方法:
這種方法通過引入一個注意力機制來處理時序信息。具體來說,每個節點都有一個自適應的時間權重,表示該節點在當前時間步驟中的重要性。然后,GNN會根據節點的時間權重來更新節點的表示。
-
ST-GCN
這是一種基于時空圖卷積網絡的方法,用于處理時序動作識別問題。該方法將時序數據轉換為時空圖,并使用時空圖卷積網絡來進行處理。
Pytorch框架學習
創建張量
-
先建個列表或元組c,c再轉張量a
a = torch.tensor(c)
這個時候如果列表是float,張量會是float
type(a) 可以看a的數據類型
-
從另一個張量中初始化另一個張量
zeros_like()ones_like()
把某個張量變為全0,全1或rand_like()
變為某個隨機張量,新張量的shape跟原張量是一樣的 -
根據形狀隨機生成張量
torch.rand((h, c))
張量常用函數
.shape
看形狀 .device
看張量在哪個設備上
張量常用API
numel
方法,torch.numel,返回張量的元素總數,當然也可以輸出張量的shape,然后乘一下也可以表示。
創建類API
-
zeros
方法,torch.zeros(),返回一個為全為0的張量,shape之類的由zeros(參數)里面的參數自己設置 -
arange
和range
,生成一維張量。張量的元素由參數設置,參數關鍵在于start和end以及step,也就是開始和結束數。 arange跟range的不同在于 size大1。arange不包括end,而range包含end。size的大小在下面的圖中可以看到
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-6tfE19R0-1681741471136)(周報+ab14e843-4446-43b7-ad38-28f6bd08ba0c/圖片 1.png)]
-
eye
創建一個對角線為1的張量,參數要設置矩陣的大小,最少設置一個h,此時為方陣。 -
full
創建一個值全為value,自定義size的張量。因此參數最主要就是value和size,調用方法用torch.full((h, c), value)
torch.full((2, 3), 3.141592)
輸出為
tensor([[ 3.1416, 3.1416, 3.1416],
[ 3.1416, 3.1416, 3.1416]])
- `full_like(張量輸入a, value)` ,根據輸出的張量a,返回一個shape一樣,但值為value的張量### 索引、切片API- `cat`連接。torch.cat(*tensors*, *dim=0*, ***, *out=None*), 第一個參數表示需要連接的tensor,dim表示在哪個維度進行連接,維度從0開始算,也就是行。```Python
import torch
import numpyb = torch.full((2, 2), 1)
print(b)
c= torch.full((2, 3), 2)
print(c)
d = torch.cat((b, c), dim=1)
print(d)
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-ONuCPD2a-1681741471138)(周報+ab14e843-4446-43b7-ad38-28f6bd08ba0c/圖片 2.png)]
比如這里的b跟c,只能在列上加,所以dim為1。
stack
torch.stack(tensors, dim=0, ***, out=None)。stack跟cat雖然都是合并,但有很大的不同,stack會有維度上的疊加,而cat是在dim維度里面加上第二個張量的內容,最明顯就是看中括號的個數。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-mfXFx2tH-1681741471138)(周報+ab14e843-4446-43b7-ad38-28f6bd08ba0c/圖片 3.png)]
可以看中括號個數確定維度的變化,cat連接后還是二維,而stack堆疊后成了三維。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-JuWBWJHI-1681741471138)(周報+ab14e843-4446-43b7-ad38-28f6bd08ba0c/圖片 4.png)]
-
chunks
切片。torch.chunk(input, chunks, dim=0),input是張量輸入,chunks是分成幾塊,dim是在哪個維度進行分割。默認為0,也就是在行進行切割。 -
split
分塊。跟chunks的區別在于chunks是均分,而split可以不均分,按自己的想法分。torch.split(tensor, split_size_or_sections, dim=0)。三個參數中,dim不設置的時候默認為0,也就是行分塊。最需要解釋的是 split_size_or_sections,它表示每個張量塊的大小,為整數,所以輸入張量的大小/每個塊大小不能整除的話,最后一個塊可能會比較小。split_size_or_sections也可以為一個列表[1,2,3,4,…n],把輸入分為n個張量,每個張量的塊大小為對應位置的數字大小。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-vq3QQVZr-1681741471138)(周報+ab14e843-4446-43b7-ad38-28f6bd08ba0c/圖片 5.png)]
gather
取變量。torch.gather(input, dim, index, ***, sparse_grad=False, out=None),前三個參數必須設置。gather沿著某個維度取變量。這個比較難。首先輸出的形狀跟Index一致,其次 ****主要是看dim和Index. dim=0,則要去找的行為index上的值,列不用看index的值。dim=1,則列為index上對應的值,列不看。比如下圖:
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-WDggemU0-1681741471138)(周報+ab14e843-4446-43b7-ad38-28f6bd08ba0c/圖片 6.png)]
這個時候的列看什么? 不看索引表的值,直接看當前確定的元素是第幾列就為第幾列。
reshape
重塑形狀,元素數和相對順序不變。torch.reshape(input, shape)。參數就兩個,輸入,形狀,這里的形狀(h, c)要保證h*c=元素總數。想變成一維的,也就是拉直(在不知道總長度的時候),a[-1]在Python中表示最后一個元素,那么shape可以用[-1]或者(-1,)來表示一維拉長。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-uxo4g9zQ-1681741471138)(周報+ab14e843-4446-43b7-ad38-28f6bd08ba0c/圖片 7.png)]
scatter_
.有下劃_的一般是直接在該位操作。選定某些位置并改變他們的值。.scatter_(dim, index, src, reduce=None)。在張量上選位置的時候跟gather的索引一樣,需要注意的是取src覆蓋的時候,是在src上直接取index相同位置的值。如下圖:
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-xyS2z3u0-1681741471138)(周報+ab14e843-4446-43b7-ad38-28f6bd08ba0c/圖片 8.png)]
至于填的位置,就跟gather一樣了,看dim和index.
-
scatter_add_
,跟普通的不同在于他這里是+src的值。 -
squeeze
torch.squeeze(input, dim=None)。這個API的作用是消除多余的維度,dim可以指定消除某個維度。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-kF8B6oeY-1681741471139)(周報+ab14e843-4446-43b7-ad38-28f6bd08ba0c/圖片 9.png)]
take
torch.take(input, index)。根據索引的下標在一維的input中找到。意思先把input拉成一維,再根據index查找。
總結
這周的成果主要就是跟著師兄給的課程學了一些Pytorch框架的基本操作,然后看了不少關于GNN和時序相結合的帖子,下周除了繼續學習pytorch課程外,我打算找篇GNN和時序結合的論文來看,然后等pytorch框架掌握差不多了,就去跟師兄要數據集跑試試。