Transformers開始在視頻識別領域的“豬突猛進”,各種改進和魔改層出不窮,由此作者將開啟Video Transformer系列的講解,本篇主要介紹了FBAI團隊的TimeSformer,這也是第一篇使用純Transformer結構在視頻識別上的文章,
如果覺得有用,就請點贊、收藏、關注!

paper: https://arxiv.org/abs/2102.05095
code(offical): https://github.com/facebookresearch/TimeSformer
accept: ICML2021
author: Facebook AI
一、前言
Transformers(VIT)在影像識別領域大展拳腳,超越了很多基于Convolution的方法,視頻識別領域的Transformers也開始’豬突猛進’,各種改進和魔改也是層出不窮,本篇博客講解一下FBAI團隊的TimeSformer,這也是第一篇使用純Transformer結構在視頻識別上的文章,
二、出發點
Video vs Image
- Video是具有時序資訊的,多個幀來表達行為或者動作,相比于Image直接理解pixel的內容而言,Video需要理解temporal的資訊,
Transformer vs CNNs
-
相比于Convolution,Transformer沒有很強的歸納偏置,可以更好的適合大規模的資料集,
-
Convolution的kernel被用來設計獲取區域特征的,所以不能對超出’感受野’的特征資訊進行建模,無法更好的感知全域特征,而Transformer的
self-attention機制不僅可以獲取區域特征同時本身就具備全域特征感知能力, -
Transformer具備更快的訓練和推理的速度, 可以在與CNNS在相同的計算下構建具有更大學習能力的模型,(這個來自于VIT)
-
可以把video視作為來自于各個獨立幀的patch集合的序列,所以可以直接適用于VIT結構,
Transfomrer自身問題
self-attention的計算復雜程度跟token的數量直接相關,對于video來說,相比于影像會有更多的token(有N幀), 計算量會更大,
三、演算法設計
Transformers有這么多的優點,所以既要保留純粹的Transformer結構,同時要修改self-attention使其計算量降低并且可以構建Temporal特征,
構建VideoTransformer
我們先梳理一下Video怎么輸入到Transformer中: 對于Video來說,輸入為, 表示為F幀采樣的尺寸為的RGB影像,Transformer需要patch構建sequence進行輸入,所以有,這里表示的是patchsize大小,表示的是每幀有多少個patch,展開后,可以表示為向量,
對輸入做Embeeding處理,, 這里表示為一個可學習的矩陣,表示一個可學習空間位置編碼,相比于Image的cls-token,Video的cls-token表示為 ,
Transformer整體包含L層encoding blocks, 每個block的query,key,value表達如下:
這里,表示attention heads數量,表示的是每個head的維度,
相比于Image的self-attention, Video的self-attention需要計算temporal維度,公式表達為:
Note: 公式里把cls-token單獨提出來了,這樣方便表達空間和時序維度的attention,
合并每個heads的attention后,進行一個線性投影,送入MLP中,同時進行一個殘差連接和Image的Transformer沒有區別,公式表達如下:
最后就是分類層了,取cls-token用于最終的分類,
這樣,我們就可以得到一個從輸入到輸出的VideoTransformer的完整表示,知道了怎么輸入輸出,接下來討論怎么改進更好的獲取temporal特征資訊,
Self-Attention范式
為了解決時序的問題,文中提出了幾種構建范式,如下圖所示:

TransformerBlock
-
**SpaceAttention(S)**這種就是標準的Transformer結構了,不計算Temporal的資訊,只計算空間資訊,公式可以表達為:
-
**Joint Space-Time Attention(ST)**這種就是把temporal和空間的token拉伸在一起,計算量會變得很大( -> ),公式表達為:
-
**Divided Space-Time Attention(T+S)**相比于前兩種,這個變種的attention計算分成了兩步,第一步計算Temporal-self-attention,第二步計算Spatial-self-attention,復雜度則會變為( -> ),每一次計算都會有
cls-token參與,所以需要+2,公式表達如下:兩步獨立計算且意義不同,所以Q,K,V需要來自不同的weights,不能共享權重,簡單的定義為:
-
**Sparse Local Global Attention (L+G)**這個attention文章只做了簡單的描述,沒有給出相關代碼實作,這里參考了Generating Long Sequences with Sparse Transformers(https://arxiv.org/pdf/1904.10509.pdf)文章,做一個簡單的解釋,
先引入幾個概念和圖示
Self-Attention, 左邊是self-attention矩陣,右邊是對應的相乘關系,復雜度為,

transformer
Atrous Self-Attention,為了減少計算復雜度,參考空洞概念,類似于空洞卷積,只計算與之相關的k個元素計算,這樣就會存在距離不滿足k的倍數的注意力為0,相當于加了一個k的stride的滑窗,如下圖中的白色位置,這樣復雜度可以從降低到,

Atrous Self Attention
Local Self-Attention, 標準self-attention是用來計算
Non-Local的,那也可以引入區域關聯來計算local的,很簡單,約束每個元素與自己k個鄰域元素有關即可,如下圖,復雜度為, 也就是, 計算復雜度直接從平方降低到了線性,也損失了標準self-attention的長距離相關性,
Local Self Attention
Sparse Self-Attention, 所以有了OpenAI的Sparse self-attention,直接合并Local和Atrous,除了相對距離不超過k的,相對距離為k的倍數的注意力都為0,這樣Attention就有了"區域緊密相關和遠程稀疏相關"的特性,

Sparse Self Attention
回到本文,local-attention只考慮的patches,也就是每個patch只關注1/4影像區域近鄰的patchs,其他的patchs忽略,global-attention則采用2的stride來在Temporal維度和HW維度上進行patches的滑窗計算,與Sparser self-attention不同點在于,Sparse Local Global Attention先計算local后再進行計算global,
-
Axial Attention(T+W+H), 已經有很多的影像分類的paper講過解耦attention,也就是用H或者W方向的attention單獨計算,例如cswin-transformers里面的簡單圖示如下:

w self-attention
與之不同的是,Video不僅分行和列,還要分時序維度來進行計算,對應Q,K,V的weighis也各不相同,先計算Temporal-attention,然后Width-attention,最后Height-attention,行和列可以互換,不影響結果,

不同attention可視化
為了說明問題,用藍色表示query patch,非藍色的顏色表示在每種不同范式下與藍色patch的自我注意力計算,不同顏色表示不同的維度來計算attention,
四、代碼分析
論文中只給出了前三種attention的實作,所以我們就只分析前三種attention的code
PatchEmbed
Video的輸入前面有介紹,是(B,C,T,H,W), 如果我們使用2d卷積的話,是沒辦法輸入5個維度的,所以要合并F和B成一個維度,有(B,C,T,H,W)->((B,T),C,H,W),和VIT一樣,采用Conv2d做embeeding,代碼如下,最侄訓傳一個維度為((B,T), (H//P*W//P), D)的embeeding.
class PatchEmbed(nn.Module):
""" Image to Patch Embedding
"""
def __init__(self, img_size=224, patch_size=16, in_chans=3, embed_dim=768):
super().__init__()
img_size = to_2tuple(img_size)
patch_size = to_2tuple(patch_size)
num_patches = (img_size[1] // patch_size[1]) * (img_size[0] // patch_size[0])
self.img_size = img_size
self.patch_size = patch_size
self.num_patches = num_patches
self.proj = nn.Conv2d(in_chans, embed_dim, kernel_size=patch_size, stride=patch_size)
def forward(self, x):
B, C, T, H, W = x.shape
x = rearrange(x, 'b c t h w -> (b t) c h w')
x = self.proj(x) # ((bt), dim, h//p, w//p)
W = x.size(-1)
x = x.flatten(2).transpose(1, 2) # ((b, t), )
return x, T, W # ((b, t), h//p * w//p, dims)
從patchEmbed得到的((B,T), nums_patches, dim),需要concat上一個clstoken用于最后的分類,所以有:
B = x.shape[0]
x, T, W = self.patch_embed(x)
cls_tokens = self.cls_token.expand(x.size(0), -1, -1) # ((bs, T), 1, dims)
x = torch.cat((cls_tokens, x), dim=1) # ((bs, T), (nums+1), dims)
Space Attention
Space Attention已經介紹過了,只計算空間維度的atttention, 所以得到的embeeding直接送入到VIT的block里面,由于,T是合并到了BatchSize維度的,所以計算完attention后需要transpose回來,然后多幀取平均,最后送入MLP來做分類,代碼如下:
## Attention blocks
def blocks(x):
x = x + self.drop_path(self.attn(self.norm1(x)))
x = x + self.drop_path(self.mlp(self.norm2(x)))
return x
for blk in self.blocks:
x = blk(x, B, T, W)
### Predictions for space-only baseline
if self.attention_type == 'space_only':
x = rearrange(x, '(b t) n m -> b t n m',b=B,t=T)
x = torch.mean(x, 1) # averaging predictions for every frame
Joint Space-Time Attention
Joint Space-Time Attention 需要引入TimeEmbeeding, 這個Embeeidng和PosEmbeeding類似,是可學習的,定義如下:
self.time_embed = nn.Parameter(torch.zeros(1, num_frames, embed_dim))
計算attention之前,需要引入TimeEmbeeding的資訊到PatchEmbeeding,所以有:
cls_tokens = x[:B, 0, :].unsqueeze(1) # (bs, 1, dims)
x = x[:,1:] # ((bs, t), nums_patchs, dims)
x = rearrange(x, '(b t) n m -> (b n) t m',b=B,t=T) # ((bs, nums_patches), t, dims)
x = x + self.time_embed # ((bs, nums_patches), t, dims)
# 為了加上timeembeeding
x = self.time_drop(x)
x = rearrange(x, '(b n) t m -> b (n t) m',b=B,t=T) # (bs, (nums_patches, t), dims)
x = torch.cat((cls_tokens, x), dim=1) # (bs, (nums_patches, t) + 1, dims)
由于已經合并了time和space的token計算,所以直接取cls-token進行分類即可,
for blk in self.blocks:
x = blk(x, B, T, W)
Divided Space-Time Attention
Divided Space-Time Attention相對復雜一些,涉及比較多的shape轉換,和Joint一樣,也需要引入TimeEmbeeding,和上面一致,這里就不重復了,先把維度transpose為((B, nums_patches), T, Dims)進行時序的attention計算,并加上殘差, 有:
## Temporal
xt = x[:,1:,:] # (bs, (nums_pathces, T), dims)
xt = rearrange(xt, 'b (h w t) m -> (b h w) t m',b=B,h=H,w=W,t=T) # ((bs, nums_pathces), T, dims)
res_temporal = self.drop_path(self.temporal_attn(self.temporal_norm1(xt))) # ((bs, nums_pathces), T, dims)
# 漸進式學習時間特征
res_temporal = self.temporal_fc(res_temporal) # (bs, (nums_patches, T), dims)
xt = x[:,1:,:] + res_temporal # (bs, (nums_patches, T), dims)
這里有個特殊的層temporal_fc,文章中并沒有提到過,但是作者在github的issue有回答,temporal_fc層首先以零權重初始化,因此在最初的訓練迭代中,模型只利用空間資訊,隨著訓練的進行,該模型會逐漸學會納入時間資訊,實驗表明,這是一種訓練TimeSformer的有效方法,(Note: 訓練trick,沒有的話可能會掉點)
temporal_fc = nn.Linear(dim, dim)
nn.init.constant_(temporal_fc.weight, 0)
nn.init.constant_(temporal_fc.bias, 0)
然后計算空間attention,這里要注意的是需要repeat和transpose cls-token的shape,原始的cls-token只表達spatial的所有資訊,現在需要把temporal的資訊融合進來,代碼如下:
## Spatial
init_cls_token = x[:,0,:].unsqueeze(1) # (bs, 1, dims)
cls_token = init_cls_token.repeat(1, T, 1) # (bs, T, dims)
cls_token = rearrange(cls_token, 'b t m -> (b t) m', b=B, t=T).unsqueeze(1) # ((bs, T), 1, dims)
xs = xt
xs = rearrange(xs, 'b (h w t) m -> (b t) (h w) m',b=B,h=H,w=W,t=T) # ((bs, T), num_patches, dims)
xs = torch.cat((cls_token, xs), 1) # ((bs, T), (num_patches + 1), dims)
res_spatial = self.drop_path(self.attn(self.norm1(xs))) # ((bs, T), (num_patches + 1), dims)
cls-token這里有兩個作用,一個是保留原始特征資訊并參與空間特征計算,另一個是融合時序特征,
### Taking care of CLS token
cls_token = res_spatial[:,0,:] # ((bs, T), dims)
cls_token = rearrange(cls_token, '(b t) m -> b t m',b=B,t=T) # (bs, T, dims)
cls_token = torch.mean(cls_token,1,True) ## averaging for every frame # (bs, 1, dims)
res_spatial = res_spatial[:,1:,:] # ((bs, T), num_patches, dims)
res_spatial = rearrange(res_spatial, '(b t) (h w) m -> b (h w t) m',b=B,h=H,w=W,t=T) # (bs, (num_patches, T), dims)
res = res_spatial
x = xt
第一部分就是帶有原始cls-token的時序殘差特征,第二部分就是融合時序特征的空間cls-token和spatial-attention,兩部分相加,最后送入MLP,完成整個attention的計算,
# res
x = torch.cat((init_cls_token, x), 1) + torch.cat((cls_token, res), 1) # (bs, (num_patches, T), dims)
## Mlp
x = x + self.drop_path(self.mlp(self.norm2(x)))
五、實驗結果
Analysis of Self-Attention Schemes

Attention實驗結論很明顯,K400和SSV2,Divided Space-Time效果是最好的, 比較有趣的是Space在K400的表現并不差,但是在SSV2上效果很差,說明SSV2資料集更加趨向于動作,K400更加趨向于內容,(NOTE: Joint Space-Time attention實際上使用了TimeEmbeeding的,實際引數量應該比Space多一點點,不過量級很少,所以這里沒有標示,)
compare the computational cost

做了一下極限crop和frames的實驗,可以看到Divided Space-time可以跑更大的解析度且更多的幀,也就意味著可以刷更高的指標,
Comparison to 3D CNNs

雖然TimeSformer的引數很大,但是推理開銷更少,訓練成本也更低,反之I3D,SlowFast這種3D CNNs需要更長的優化周期才能達到不錯的性能,
The Importance of Pretraining

實驗說明了一個問題,更NB的pretrain會帶來更高的收益,TimeSformer表示的是8x224x224video片段輸入,TimeSformer-HR表示的是16x448x448video片段輸入,TimeSformer-L表示的是96x224x224video片段輸入,
The Impact of Video-Data Scale

分開討論,對于理解性的視頻資料集,K400,TimeSfomer可以在少量資料集的情況下也超過I3D和SlowFast,對于時序性的資料,TimeSformer需要更多的資料集才能達到不錯的效果,
The Importance of Positional Embeddings

空間和時序的pos embeeding很重要,尤其是SSV2資料集上表現很明顯,
Varying the Number of Tokens

增加解析度可以提升性能,增加視頻采樣幀數可以帶來持續收益,最高可以達到96幀(GPU顯存限制),已經遠超cnn base的8-32幀,
Comparison to the State-of-the-Art


K400, TimeSformer采用的是3spatial crops(left,center,right)就可以達到80.7%的SOTA,K600,TimeSformer達到了82.2%的SOTA,
The effect of using multiple temporal clips

采用了{1,3,5,10}不同的clips數量,可以看到TimeSfomer-L的性能保持不變,TimeSfomer在3clips的時候性能保持穩定,X3D,SlowFast還會隨著clips的增加(>=5)而提升性能,對于略短的視頻片段來說,TimeSfomer可以用更少的推理開銷達到很高的性能,
Something-Something-V2 & Diving-48

SSV2上的性能只比SlowFast高,甚至低于TSM,Diviing-48比SlowFast高了很多,
Long-Term Video Modeling

相比于SlowFast在長視頻的表現,TimeSformer高出10個點左右,這個表里的資料是先用k400做pretrain后訓練howto100得到的,使用imagenet21k做pretrain,最高可以達到62.1%,說明TimeSformer可以有效的訓練長視頻,不需要額外的pretrian資料,
Additional Ablations
-
Smaller&Larger Transformers Vit Large, k400和SSV2都降了1個點 相比vit base Vit Small, k400和SSV2都降了5個點 相比vit base
-
Larger Patch Size patchsize 從16調整為32,降低了3個點
-
The Order of Space and Time Self-Attention 調整空間attention在前,時序attention在后,降低了0.5個點 嘗試了并行時序空間attention,降低了0.4個點
Visualizing Learned Space-Time Attention

TimeSformer可以學會關注視頻中的空間和時序相關部分,以便進行時空理解,
Visualizing Learned Feature Embeddings.

t-SNE顯示,可以看到Divided Space-Time Attention的特征區分程度更強
六、結論
-
提出了基于Transformer的video模型范式,設計了divide sapce-time attention,
-
在K400,K600上取得了SOTA的效果,
-
相比于3D CNNs,訓練和推理的成本低,
-
可以應用于超過一分鐘的視頻片段,具備長視頻建模能力,
參考
https://blog.csdn.net/m0_37531129/article/details/108125010
https://arxiv.org/pdf/1904.10509.pdf
如果覺得有用,就請點贊、收藏、關注!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/392274.html
標籤:其他
