99久久全国免费观看_国产一区二区三区四区五区VM_久久www人成免费看片中文_国产高清在线a视频大全_深夜福利www_日韩一级成人av

徐土豆
認證:優質創作者
所在專題目錄 查看專題
《學習geometric deep learning筆記系列》第一篇,Non-Euclidean Structure Data之我見
《Geometric Deep Learning學習筆記》第二篇, 在Graph上定義卷積操作,圖卷積網絡
《Geometric Deep Learning學習筆記》第三篇,GCN的空間域理解,Message Passing以及其含義
Shift-GCN網絡論文筆記
Shift-GCN中Shift的實現細節筆記,通過torch.index_select實現
作者動態 更多
給定計算預算下的最佳LLM模型尺寸與預訓練數據量分配
05-19 09:33
大模型推理時的尺度擴展定律
05-18 10:32
世界多胞體與世界模型
05-13 09:42
獎勵模型中的尺度擴展定律和獎勵劫持
05-12 08:41
MeCo——給預訓練數據增加源信息,就能減少33%的訓練量并且提升效果
05-08 09:13

Shift-GCN中Shift的實現細節筆記,通過torch.index_select實現

近期在看Shift-GCN的論文[1],該網絡是基于Shift卷積算子[2]在圖結構數據上的延伸。在閱讀源代碼[3]的時候發現了其對于Non-Local Spatial Shift Graph Convolution有意思的實現方法,在這里簡要記錄一下。 本文轉載自徐飛翔的"Shift-GCN中Shift的實現細節筆記,通過torch.index_select實現"

版權聲明:本文為博主原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。

在討論代碼本身之前,簡要介紹下Non-Local Spatial Shift Graph Convolution的操作流程,具體介紹可見博文[1]。對于一個時空骨骼點序列而言,如Fig 1所示,將單幀的骨骼點圖視為是完全圖,因此任何一個節點都和其他所有節點有所連接,其shift卷積策略為:

對于一個特征圖而言,其中是骨骼點數量,是特征通道數。對于第個通道的距離為

Fig 1. 在全局空間Shift圖卷積中,將骨骼點圖視為是完全圖,其shift策略因此需要考慮本節點與其他所有節點之間的關系。

根據這種簡單的策略,如Fig 1所示,形成了類似于螺旋上升的特征圖樣。那么我們要如何用代碼描繪這個過程呢?作者公開的源代碼給予了我們一種思路,其主要應用了中的函數。先簡單介紹一下這個函數。

是一個用于索引給定張量中某一個維度中某些特定索引元素的方法,其API手冊如:

torch.index_select(input, dim, index, out=None) → Tensor
Parameters:	
input (Tensor) – 輸入張量,需要被索引的張量
dim (int) – 在某個維度被索引
index (LongTensor) – 一維張量,用于提供索引信息
out (Tensor, optional) – 輸出張量,可以不填

其作用很簡單,比如我現在的輸入張量為的尺寸大小,其中為樣本數量,為特征數目,如果我現在需要指定的某些樣本,比如第,等等樣本,我可以用一個進行索引,然后應用就可以索引了,例子如:

>>> x = torch.randn(3, 4)
>>> x
tensor([[ 0.1427,  0.0231, -0.5414, -1.0009],
         [-0.4664,  0.2647, -0.1228, -1.1068],
         [-1.1734, -0.6571,  0.7230, -0.6004]])
>>> indices = torch.tensor([0, 2])
>>> torch.index_select(x, 0, indices) # 按行索引
tensor([[ 0.1427,  0.0231, -0.5414, -1.0009],
        [-1.1734, -0.6571,  0.7230, -0.6004]])
>>> torch.index_select(x, 1, indices) # 按列索引
tensor([[ 0.1427, -0.5414],
           [-0.4664, -0.1228],
           [-1.1734,  0.7230]])

注意到有一個問題是,似乎在使用的情況下,不檢查是否會越界,因此如果你的越界了,但是報錯的地方可能不在使用的地方,而是在后續的代碼中,這個似乎就需要留意下你的了。同時,是一個,這個也是要留意的。

我們先貼出主要代碼,看看作者是怎么實現的:

class Shift_gcn(nn.Module):
    def __init__(self, in_channels, out_channels, A, coff_embedding=4, num_subset=3):
        super(Shift_gcn, self).__init__()
        self.in_channels = in_channels
        self.out_channels = out_channels
        if in_channels != out_channels:
            self.down = nn.Sequential(
                nn.Conv2d(in_channels, out_channels, 1),
                nn.BatchNorm2d(out_channels)
            )
        else:
            self.down = lambda x: x

        self.Linear_weight = nn.Parameter(torch.zeros(in_channels, out_channels, requires_grad=True), requires_grad=True)

        self.Linear_bias = nn.Parameter(torch.zeros(1,1,out_channels,requires_grad=True),requires_grad=True)

        self.Feature_Mask = nn.Parameter(torch.ones(1,25,in_channels, requires_grad=True),requires_grad=True)

        self.bn = nn.BatchNorm1d(25*out_channels)
        self.relu = nn.ReLU()
        index_array = np.empty(25*in_channels).astype(np.int)
        for i in range(25):
        for j in range(in_channels):
                index_array[i*in_channels + j] = (i*in_channels + j + j*in_channels) % (in_channels*25)
        self.shift_in = nn.Parameter(torch.from_numpy(index_array),requires_grad=False)

        index_array = np.empty(25*out_channels).astype(np.int)
        for i in range(25):
            for j in range(out_channels):
                index_array[i*out_channels + j] = (i*out_channels + j - j*out_channels) % (out_channels*25)
        self.shift_out = nn.Parameter(torch.from_numpy(index_array),requires_grad=False)
       

    def forward(self, x0):
        n, c, t, v = x0.size()
        x = x0.permute(0,2,3,1).contiguous()
        # n,t,v,c
        # shift1
        x = x.view(n*t,v*c)
        x = torch.index_select(x, 1, self.shift_in)
        x = x.view(n*t,v,c)
        x = x * (torch.tanh(self.Feature_Mask)+1)

        x = torch.einsum('nwc,cd->nwd', (x, self.Linear_weight)).contiguous() # nt,v,c
        x = x + self.Linear_bias

        # shift2
        x = x.view(n*t,-1) 
        x = torch.index_select(x, 1, self.shift_out)
        x = self.bn(x)
        x = x.view(n,t,v,self.out_channels).permute(0,3,1,2) # n,c,t,v

        x = x + self.down(x0)
        x = self.relu(x)
        # print(self.Feature_Mask.shape)
        return x

我們把forward()里面的分為三大部分,分別是:1> shift_in操作;2> 卷積操作;3> shift_out操作;其中指的shift_inshift_out只是shift圖卷積算子的不同形式而已,其主要是一致的。整個結構圖如Fig 2(c)所示。

Fig 2. Shift-Conv-Shift模組需要兩個shift操作,代碼中稱之為shift_in和shift_out。
其中的卷積操作代碼由愛因斯坦乘積[4]形式表示,其實本質上就是一種矩陣乘法,其將矩陣相乘,得到輸出張量為
 x = torch.einsum('nwc,cd->nwd', (x, self.Linear_weight)).contiguous() # nt,v,c
 x = x + self.Linear_bias
而進行的掩膜操作代碼如下所示,這代碼不需要太多仔細思考。
 x = x * (torch.tanh(self.Feature_Mask)+1)

那么我們著重考慮以下的代碼:

x = x.view(n*t,v*c)
x = torch.index_select(x, 1, self.shift_in)
x = x.view(n*t,v,c)

第一行代碼將特征圖展開,如Fig 3所示,得到了25 × C 25 \times C25×C大小的特征向量。通過torch.index_select對特征向量的不同分區進行選擇得到最終的輸出特征向量,選擇的過程如Fig 4所示。

Fig 3. 將特征圖進行拉平后得到特征向量。

那么可以知道,對于某個關節點i ii而言,給定通道j jj,當遍歷不同通道時,會存在一個周期,因此是,比如對于第0號節點的第1個通道,其需要將的值移入,如Fig 4的例子所示。而第2個通道則是需要考慮將的值移入,我們發現是以C CC為周期的。這個時候假定的是關節點都是同一個的時候,當遍歷關節點時,我們最終的索引規則是(,因為考慮到了溢出的問題,因此需要求余,有。這個對應源代碼的第23-32行,如上所示。

Fig 4. 將特征圖拉直之后的shift操作示意圖,因此需要尋找一種特殊的索引規則,以將特征圖shift問題轉化為特征向量的shift問題。

在以這個舉個代碼例子,例子如下所示:

 import numpy as np
 import torch
 array = np.arange(0,15).reshape(3,5)
 array = torch.tensor(array)
 index = np.zeros(15)
 for i in range(3):
 for j in range(5):
 index[i*5+j] = (i*5+j*5+j) % (15)
 index = torch.tensor(index).long()
 out = torch.index_select(array.view(1,-1), 1, index).view(3,5)
 print(array)
 print(out)

輸出為:

 tensor([[ 0,  1,  2,  3,  4],
            [ 5,  6,  7,  8,  9],
            [10, 11, 12, 13, 14]])
 tensor([[ 0,  6, 12,  3,  9],
            [ 5, 11,  2,  8, 14],
            [10,  1,  7, 13,  4]])

我們把這種正向移入的稱之為,反過來移入則稱之為,其索引公式有一點小變化,為:。代碼例子如下:

 import numpy as np
 import torch
 array = np.arange(0,15).reshape(3,5)
 array = torch.tensor(array)
 index = np.zeros(15)
 for i in range(3):
      for j in range(5):
        index[i*5+j] = (i*5-j*5+j) % (15)
 index = torch.tensor(index).long()
 out = torch.index_select(array.view(1,-1), 1, index).view(3,5)
 print(array)
 print(out)

輸出為:

 tensor([[ 0,  1,  2,  3,  4],
            [ 5,  6,  7,  8,  9],
            [10, 11, 12, 13, 14]])
 tensor([[ 0, 11,  7,  3, 14],
              [ 5,  1, 12,  8,  4],
              [10,  6,  2, 13,  9]])

輸入和只是因為平移方向反過來了而已。

當然,進行了特征向量的還不夠,還需要將其回一個特征矩陣,因此會有:

  x = x.view(n*t,v,c)

這樣的代碼段出現。

Reference

[1]. https://fesian.blog.csdn.net/article/details/109563113

[2]. https://fesian.blog.csdn.net/article/details/109474701

[3]. https://github.com/kchengiva/Shift-GCN

[4]. https://blog.csdn.net/LoseInVain/article/details/81143966

聲明:本內容為作者獨立觀點,不代表電子星球立場。未經允許不得轉載。授權事宜與稿件投訴,請聯系:editor@netbroad.com
覺得內容不錯的朋友,別忘了一鍵三連哦!
贊 0
收藏 1
關注 52
成為作者 賺取收益
全部留言
0/200
成為第一個和作者交流的人吧
主站蜘蛛池模板: 在线观看播放 | 国产成人久久综合一区 | 韩国无码无遮挡在线观看 | 无码国产69精品久久久孕妇 | 欧美成人高清ww | 女人爽到高潮免费视频大全 | 人妻少妇看A偷人无码精品 在教室伦流澡到高潮h麻豆 | 国产午夜精品免费一区二区三区 | 99手机国产精品 | 91tv官网精品成人亚洲 | 少妇穿牛仔裤一级av毛片 | 亚洲韩国欧洲国产日产av | 亚洲精品亚洲人成人网 | 欧美操操操 | 99视频在线精品 | 久久中文字幕精品 | 特级黄色小视频 | 精品综合久久久久久98 | 乱色国内精品视频在线 | 欧美老人巨大xxxx做受视频 | 欧美一区二区三区蜜桃 | 18禁黄久久久AAA片 | 波多野结衣成人在线观看 | 在线观看免费视频国产 | 欧美视频精品全部免费观看 | 四虎娱乐| 这里只有精品免费 | 无码一区二区三区在线观看 | 影音先锋亚洲AV少妇熟女 | 国产精品二区在线 | 丁香五月综合缴清中文 | 99激情网 | 欧美日韩在线影院 | 九九欧美 | 日韩经典在线观看 | 国产性生大片免费观看性 | 夜色亚洲 | 国产精品一区二区精品 | 久久99久| av成人免费在线看 | 国产精品久久久久久久久久免费 |