詳解vmamba中的矩陣分塊重排方案

點(diǎn)擊藍(lán)字,關(guān)注我們 本周在深入研究vmamba的代碼時(shí),發(fā)現(xiàn)PatchMerging2D類與PatchExpand2D類實(shí)現(xiàn)的很巧妙,其中還涉及到張量重排的技巧,這里做一記錄。 首先總結(jié)如下,給定輸入A(b,c,h,w),h與w均為偶數(shù)。PatchMerging2D 將A的空間維度以 2X2 的不重疊鄰域特征堆疊到通道維,從而得到B(b,4*c,h//2,w//2)。PatchExpand2D將B通過(guò)張量重排轉(zhuǎn)換成C(b,c,h,w)。實(shí)際上在PatchMerging2D與PatchExpand2D中間還有很多步驟,這里為突出重點(diǎn)做出適當(dāng)簡(jiǎn)化。

PatchMerging2D
如下代碼所示,通過(guò)這種操作實(shí)現(xiàn)鄰域特征提取。這里需要注意,代碼只提取2*2鄰域,因此PatchExpand2D中也是對(duì)應(yīng)的這一情況。如果提取3*3鄰域,則PatchExpand2D也將相應(yīng)改變。
        x0 = x[:, 0::2, 0::2, :]  # B H/2 W/2 C        x1 = x[:, 1::2, 0::2, :]  # B H/2 W/2 C        x2 = x[:, 0::2, 1::2, :]  # B H/2 W/2 C        x3 = x[:, 1::2, 1::2, :]  # B H/2 W/2 C                x = torch.cat([x0, x1, x2, x3], -1)  # B H/2 W/2 4*C
如果要提取3*3鄰域,按照上面代碼的實(shí)現(xiàn)邏輯,則應(yīng)該是
        x0 = x[:, 0::3, 0::3, :]  # B H/3 W/3 C        x1 = x[:, 1::3, 0::3, :]  # B H/3 W/3 C        x2 = x[:, 0::3, 1::3, :]  # B H/3 W/3 C        x3 = x[:, 1::3, 1::3, :]  # B H/3 W/3 C        ........        x7 = x[:, 2::3, 3::3, :]        x8 = x[:, 3::3, 2::3, :]                x = torch.cat([x0, x1, x2, x3,...,x8], -1)  # B H/2 W/2 9*C
PatchExpand2D
PatchExpand2D類中調(diào)用了einops.rearrange,其實(shí)現(xiàn)的功能為劃分子塊重排。 首先給出rearrange的示例,給大家一個(gè)直觀感受

x = rearrange(x, 'b h w (p1 p2 c)-> b (h p1) (w p2) c', p1=2, p2=2, c=1)
rearrange函數(shù)的輸入包括三部分,x是輸入張量,'b?h?w?(p1?p2?c)->?b?(h?p1)?(w?p2)?c'是張量重排規(guī)則,p1=self.dim_scale,?p2=self.dim_scale,?c=C//self.dim_scale是重排規(guī)則中的相關(guān)變量賦值。這個(gè)格式其實(shí)與之前我們講過(guò)的torch.einsum很相似,詳情見(jiàn)torch.einsum解析,但區(qū)別在于,torch.einsum實(shí)現(xiàn)的是愛(ài)因斯坦求和,是包含加法和乘法的。rearrange只是張量的重排,并不涉及到加法和乘法。有人會(huì)說(shuō),那torch.einsum是不是都得有兩個(gè)或以上的輸入張量?。窟€真不一定,比如求對(duì)角線平方和就也只需要一個(gè)輸入,因此這不能作為torch.einsum和rearrange的區(qū)別。
diagonal_elements = torch.einsum('ii->i', A)
下面我們重新看rearrange示例的重排規(guī)則部分:
'b h w (p1 p2 c)-> b (h p1) (w p2) c'
輸入為4維張量,分別為b, h, w, 2*2*1。然后將其重排為形狀b, h*2, w*2, 1。我們先一句話總結(jié)其作用:將(h, w)形狀的張量重排成(h*2,w*2),也就是每個(gè)位置擴(kuò)展成2*2的子區(qū)域,該區(qū)域用張量的通道維度填充,也就是這里的第四個(gè)維度2*2*1。 起初我對(duì)這一操作十分不解,核心問(wèn)題是,我可以通過(guò)通過(guò)線性代數(shù)中的子矩陣概念來(lái)理解這一操作。但是我無(wú)法將張量重排操作前后的映射關(guān)系用數(shù)學(xué)公式表示出來(lái)。 例如(1,2,2,4)的張量,我們可以理解為(2,2)個(gè)向量,每個(gè)向量包含4個(gè)元素。將其通道維度展開(kāi)以將原來(lái)的空間維度(2,2)擴(kuò)展成(4,4)可以這樣理解,原來(lái)(2,2)的每一個(gè)位置從(1,1)變成(2,2)形狀,區(qū)域還是那個(gè)區(qū)域。或者反過(guò)來(lái),(4,4)分成4份,每一份就是(2,2)。 用一張純手圖對(duì)上述文字作說(shuō)明(原諒我整了個(gè)手繪就放上來(lái)了)
接下來(lái)進(jìn)入解密時(shí)刻,我們還是以(1,2,2,4)->(1,4,4,1)為例。因?yàn)閎=1,我們省略。拆分后的通道維數(shù)為1,也省略。初始位置(b, h, w, (p1 * p2 * c)),新位置坐標(biāo)為(b, h * p1 + p1_index, w * p2 + p2_index, c) 。其中,(p1_index, p2_index)為拆分后通道維元素的相對(duì)坐標(biāo)。 初始位置(0,0)包含4個(gè)元素,以第2個(gè)元素為例,其原始坐標(biāo)為(0,0,1),在拆分后的坐標(biāo)為 (0,1),對(duì)應(yīng)于新位置中的(p1_index, p2_index)。這里實(shí)際上就還是按照逐行填充的原則,因?yàn)樾碌男螤顬?2,2),所以其坐標(biāo)只有{(0,0),(0,1),(1,0),(1,1)}這四種情況。這樣就把映射關(guān)系的公式講清楚了。

元素

初始位置 (b, h, w, (p1 * p2 * c))

拆分后的 (p1, p2, c)

新位置(b, h * p1 + p1_index, w * p2 + p2_index, c)?

1

(0, 0, 0, 0)

(0, 0, 0)

(0, 0 * 2 + 0, 0 * 2 + 0, 0)

2

(0, 0, 0, 1)

(0, 1, 0)

(0, 0 * 2 + 0, 0 * 2 + 1, 0)

3

(0, 0, 0, 2)

(1, 0, 0)

(0, 0 * 2 + 1, 0 * 2 + 0, 0)

4

(0, 0, 0, 3)

(1, 1, 0)

(0, 0 * 2 + 1, 0 * 2 + 1, 0)

5

(0, 0, 1, 0)

(0, 0, 0)

(0, 0 * 2 + 0, 1 * 2 + 0, 0)

6

(0, 0, 1, 1)

(0, 1, 0)

(0, 0 * 2 + 0, 1 * 2 + 1, 0)

7

(0, 0, 1, 2)

(1, 0, 0)

(0, 0 * 2 + 1, 1 * 2 + 0, 0)

8

(0, 0, 1, 3)

(1, 1, 0)

(0, 0 * 2 + 1, 1 * 2 + 1, 0)

9

(0, 1, 0, 0)

(0, 0, 0)

(0, 1 * 2 + 0, 0 * 2 + 0, 0)

10

(0, 1, 0, 1)

(0, 1, 0)

(0, 1 * 2 + 0, 0 * 2 + 1, 0)

11

(0, 1, 0, 2)

(1, 0, 0)

(0, 1 * 2 + 1, 0 * 2 + 0, 0)

12

(0, 1, 0, 3)

(1, 1, 0)

(0, 1 * 2 + 1, 0 * 2 + 1, 0)

13

(0, 1, 1, 0)

(0, 0, 0)

(0, 1 * 2 + 0, 1 * 2 + 0, 0)

14

(0, 1, 1, 1)

(0, 1, 0)

(0, 1 * 2 + 0, 1 * 2 + 1, 0)

15

(0, 1, 1, 2)

(1, 0, 0)

(0, 1 * 2 + 1, 1 * 2 + 0, 0)

16

(0, 1, 1, 3)

(1, 1, 0)

(0, 1 * 2 + 1, 1 * 2 + 1, 0)

其實(shí)這里有一個(gè)有趣的地方,如果?'b h w (p1 p2 c)-> b (h p1) (w p2) c'?被改成了?'b h w (p1 p2 c)-> b (h p2) (w p1) c'?會(huì)發(fā)生甚么事情?我們舉個(gè)最簡(jiǎn)單的例子作為結(jié)束。

原來(lái)的結(jié)果如果是?

修改后的結(jié)果是

也就是說(shuō),本來(lái)是逐行填充,現(xiàn)在變成了逐列填充!
尾聲

實(shí)際上,我在和師兄探討這個(gè)映射關(guān)系具體是什么的時(shí)候,師兄的意思是不求甚解,原因是這只是一個(gè)工具,我們知道怎么用即可,不必深究。其實(shí)從科研效率的角度上來(lái)講,師兄說(shuō)得完全正確。人的時(shí)間和精力是有限的,需要投入到最緊要的事情上。但我認(rèn)為在rearrange這個(gè)上花費(fèi)的時(shí)間是有意義的。理由如下: 其一,二維圖像的分塊與重排的確非常重要,例如卷積操作的滑動(dòng)窗口其實(shí)本質(zhì)上就是一種分塊,而由于圖像內(nèi)目標(biāo)的大小、形狀等存在差異,實(shí)際上不同的分塊策略會(huì)對(duì)模型性能有影響,了解張量重排的映射關(guān)系對(duì)于深入理解卷積神經(jīng)網(wǎng)絡(luò)是有益處的。 其二,目前深度學(xué)習(xí)已經(jīng)發(fā)展了很多年,以發(fā)論文為例,審稿人的口味越來(lái)越刁鉆,簡(jiǎn)單的增刪改模塊已經(jīng)難以發(fā)表好論文。我認(rèn)為想要有所收獲,一方面是深度,需要對(duì)原理有更深入的理解,從而有所改良;另一方面是廣度,需要旁征博引,他山之石可以攻玉。

綜上所述,科研本就是需要不斷學(xué)習(xí),你也不知道今天學(xué)的東西能否用上,這個(gè)確實(shí)是玄學(xué)。但是如果有興趣有精力有時(shí)間,還是要深入探討一下,否則以后不管是進(jìn)入業(yè)界做項(xiàng)目還是進(jìn)入高校教書(shū)育人,對(duì)本領(lǐng)域的知識(shí)還是得學(xué)學(xué)許昕,“還是太全面了” 終歸是好詞。

END 你 你 遲日江山麗 春風(fēng)花草香

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容