union和union all有什么区别_Pytorch中Linear与Conv1d(kernel=1)的区别
191214 說明:
很抱歉,突然發現圖中第三行多畫了一列叉,事實上,生成 output(0,0) 數據只用到了input[:,0] 以及 weights[0,:]。比較懶,就不再畫了,圖中第三行的第一個矩陣應該和第二行的第一個矩陣相同。
此外至于評論區中有人提到得到的結果一樣。為此我做了一個小實驗,驗證經過一步簡單優化后,模型參數之間的差異。使用的代碼如下:
import torch import torch.nn as nn from copy import deepcopySMALL = 1e-7class Model(nn.Module):def __init__(self):super().__init__()self.conv = nn.Conv1d(3, 2, 1)self.linear = nn.Linear(3, 2)params = deepcopy(self.conv.state_dict())params['weight'] = params['weight'].reshape(2, 3)self.linear.load_state_dict(params)self.optim0 = torch.optim.Adam(lr=0.5, params=self.conv.parameters())self.optim1 = torch.optim.Adam(lr=0.5, params=self.linear.parameters())def test(self, tensor):assert (self.conv.weight.flatten().eq(self.linear.weight.flatten())).all(), 'Initial weights are different'assert (self.conv.bias.flatten().eq(self.linear.bias.flatten())).all(), 'Initial biases are different'# get out tensorc_out = self.conv(tensor.transpose(1, 2)).transpose(1, 2)l_out = self.linear(tensor)# set optim targetc_target = c_out.sum(2).mean()l_target = l_out.sum(2).mean()# apply one step optimizationself.optim0.zero_grad()c_target.backward()self.optim0.step()self.optim1.zero_grad()l_target.backward()self.optim1.step()# if conv1d(kernel=1) behaves the same as linear, # their parameters should be the same after applying # one-step optimizationa = self.conv.weight.data.flatten()b = self.linear.weight.data.flatten()c = self.conv.bias.data.flatten()d = self.linear.bias.data.flatten()assert torch.add(a, -b).abs().lt(SMALL).all(), 'After a step, weights are different'assert torch.add(c, -d).abs().lt(SMALL).all(), 'After a step, biases are different'if __name__ == "__main__":gen = torch.Generator()for seed in range(1000):gen.manual_seed(seed)tensor = torch.rand((10, 3, 3), generator=gen)model = Model()model.test(tensor)代碼做的事情主要是:1)給定隨機種子,生成隨機的 tensor,2)建模并使得兩個矩陣的初始參數相等。3)用 Adam 進行一次簡單的優化,并比較優化后的參數。
這里做最后的參數比較的時候,用 torch.eq 是肯定無法通過的,經過觀察,發現最后的weights 確實很接近。經過幾次測試發現,兩者在一步優化后,在 1e-6 誤差內可以通過測試,但是無法通過 1e-7 誤差的測試。
因此,這里有兩種可能,第一,這個weights的誤差僅僅是因為計算誤差導致的,pytorch 在計算兩者的時候,本質上是一樣。第二,pytorch 在計算 conv1d 的時候確實如文檔說的使用了cross-relation operation,但是這個operation在簡單的case中,帶來的gradients和linear確實存在微小的區別,從而使得其行為不一樣。但是得說明的是,經過一個大 N 級別的優化過程,conv1d 和 linear 帶來的區別會是顯著的。因此,最起碼的,在使用 pytorch 進行計算時,不可將兩者視為等同。
通過本次實驗以及基于自己在復現 VRP-RL 的經驗,我偏向認為 conv1d 的行為和 linear 是不同的。歡迎進一步討論。
-------
最近在復現VRP下的DRL算法,當考慮C個顧客的問題,以及batch的大小為N,相應的地圖數據的shape是(N, C, 2),其中第三維分別存儲物理坐標(x,y)信息。
原文使用Conv1d with kernel_size=1來作為encoder,將原始數據映射到embedding_size=M的維度上去,得到數據形狀為(N, C, M)。
作為一個調包俠,從來都只在乎輸入和輸出的形狀,怎么方便怎么來。因為pytorch的Conv1d的API的輸入數據需要將1維和2維調換,即(N, 2, C),覺得麻煩,而且誤以為kernel=1的時候的Conv1d和Linear是完全一樣的,然后就順手用了一個Linear Layer 去做為embedding。唯一的區別僅僅在于這個encoder的選擇,結果就是和benchmark對比,花費時間更長且效果更差。
然后去Stack Overflow上面去找找看答案,發現遇到這種問題的不僅我一個(見帖子),這里就根據pytorch的API一起探索一下conv1d(kernel=1)和linear分別究竟做了什么,以及產生區別的原因。
首先我們看最簡單的Linear layer如下,
然后我們看Conv1d的API如下
有興趣的小伙伴可以推導一下公式,不難發現,假設考慮都是叉乘操作,結合軸變化,當kernel=1的時候,Conv1d和Linear的output中各元素是共源的,也就是說,對于entry(i,j),生成他們數據的原始數據來源是一樣的,結果應該沒什么區別。神級畫手只能幫到這里了(1為Linear,2為假設的Conv1d,3為實際的Conv1d):
如此這般,那問題很可能就出在集結方式了!Linear這邊,確實就是普通的叉乘操作。這時候,Conv1d中紅色的這個cross-correlation很可能就是問題的關鍵了。wiki和百度鏈接如下,具體計算公式可以在鏈接里面找到:
wiki?www.wikiwand.comInsignal processing,cross-correlationis ameasure of similarityof two series as a function of the displacement of one relative to the other. This is also known as aslidingdot productorsliding inner-product.互相關函數_百度百科?baike.baidu.com互相關函數是信號分析里的概念,表示的是兩個時間序列之間的相關程度,即描述信號 x (t),y (t) 在任意兩個不同時刻 t1,t2 的取值之間的相關程度。描述兩個不同的信號之間的相關性時,這兩個信號可以是隨機信號,也可以是確知信號。這不就是明擺著這個操作會體現兩個序列間的相關性嗎?在VRP問題中,相當于提前集結了x和y的信息,并通過學習weights與歷史信息中其他點作對比,大致估計當前點所在的位置,對于VRP問題,各個物理點x以及y之間的相關程度對于問題的求解是一個很重要的信息,當兩個位置的坐標更相近的時候,更有可能考慮這兩點相連,因此,使用Conv1d layer對問題的求解有更好的幫助。
小結,當同組數據中,各數據在每一維度的相關性比較重要的時候,Conv1d能提取這些數據并反映出來,這是普通的Linear layer做不到的。同時,做一個naive的調包俠是不可取的,仔細研究每一個API的內部運行機制才能避免寫低效甚至錯誤的代碼。
總結
以上是生活随笔為你收集整理的union和union all有什么区别_Pytorch中Linear与Conv1d(kernel=1)的区别的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 早报:电信麦芒20正式发布 realme
- 下一篇: 无极绳连续牵引绞车_可视化无极绳绞车保护