ViT-vision transformer
介紹
Transformer最早是在NLP領(lǐng)域提出的,受此啟發(fā),Google將其用于圖像,并對分類流程作盡量少的修改。
起源:從機器翻譯的角度來看,一個句子想要翻譯好,必須考慮上下文的信息!
如:The animal didn’t cross the street because it was too tired將其翻譯成中文,這里面就涉及了it這個詞的翻譯,具體it是指代animal還是street就需要根據(jù)上下文來確定,所以現(xiàn)在問題就變成,如何讓機器學(xué)習(xí)上下文?
例如有兩個特征,分別為性別和收入,二者做交互特征(簡單的說即兩個特征相乘),可以得到如:此數(shù)據(jù)為男人的狀態(tài)下收入為多少的特征,則可以利用這個特征去分析性別對收入的影響,相對于同時考慮了性別和收入的關(guān)系。那么借鑒這個思想,相對于引入一個相乘的交互關(guān)系就可以去表示上下文信息了。而Attention在本質(zhì)上用一句話概括就是:帶權(quán)重的相乘求和。
在Attention中,假如我們要翻譯it這個詞,這時候it這個詞稱為query(Q)待查詢。查詢什么呢,查詢句子中的其他單詞包括自己(這里其他的單詞包括自己稱為(keys(K)),這里的查詢操作相對于上文說的相乘,而在Attention中用的是點乘操作。如果還記得Attention的輸入是Patch embedding的結(jié)果,即是一個個N維空間的向量,即Q和K代表的內(nèi)容都為N維空間的向量,那么點乘即可以表示這兩個向量的相似程度——Q*K = |Q||K|cosθ
Q和K相乘后可以得到一個代表詞和詞之間相似度的概念,這里記為S。如果我們對這個S取softmax,是不是相對于就得到了當(dāng)前要查詢的Q,到底對應(yīng)哪個詞的概率比較大的概率,這里記為P。
而Attention就是對P做權(quán)重加和的結(jié)果,而為什么還要對P做權(quán)重(這個權(quán)重也是可學(xué)習(xí)的)加和呢,其實我覺得這才是Attention的精髓,因為每個權(quán)重即代表了網(wǎng)絡(luò)對于哪個概率對應(yīng)下的內(nèi)容更加注意,對于哪些內(nèi)容不需要注意,使網(wǎng)絡(luò)可以更加關(guān)注與需要注意的東西,其他無關(guān)的東西,通過這個權(quán)重,相對于舍棄了。而我們記這個權(quán)重為V。
思路:ViT算法中,首先將整幅圖像拆分成若干個patch,然后把這些patch的線性嵌入序列作為Transformer的輸入送入網(wǎng)絡(luò),然后使用監(jiān)督學(xué)習(xí)的方式進行圖像分類的訓(xùn)練。
具體流程:
- 將圖像拆分成若干個patch
- 將patches通過一個線性映射層,得到若干個token embedding
- 將多個token embedding concat一個cls_token(可學(xué)習(xí)參數(shù))
- 每個參數(shù)均加上position embedding位置編碼,防止無法找到原來的位置
- 將token embedding、cls_token和position embedding一同傳入encoder模塊
- encoder模塊(L個block)
- Layer Norm:標(biāo)準(zhǔn)歸一化(便于收斂)
- MSA/MHA:多頭子注意力機制
- 輸入輸出作殘差鏈接
- Layer Norm:標(biāo)準(zhǔn)歸一化(便于收斂)
- MLP:全連接層(Linear+…)
- encoder的輸出通過MLP Head作分類任務(wù)
優(yōu)點:模型簡單且效果好,較好的擴展性,模型越大效果越好。
與CNN結(jié)構(gòu)對比
- Transformer的平移不變性和局部感知性較差,在數(shù)據(jù)量不充分時,效果較差
- 但是對于大量的訓(xùn)練數(shù)據(jù),Transformer的效果更佳
- 無需像CNN構(gòu)造復(fù)雜的網(wǎng)絡(luò)結(jié)構(gòu),CNN往往是不斷加深網(wǎng)絡(luò),才能對刷新某任務(wù)的SOTA
模型結(jié)構(gòu)
圖像分塊嵌入Patch Embedding
具體步驟:
-
將 H ? W ? C H * W * C H?W?C的圖像,變成一個 N ? ( P 2 ? C ) N * (P^2*C) N?(P2?C)的序列,這個序列由一系列展平的圖像塊構(gòu)成,即把圖像切分成小塊后再展平,其中, N = H W / P 2 N=HW/P^2 N=HW/P2個圖像塊,每個圖像塊的維度為 P 2 ? C P^2*C P2?C, P P P表示圖像塊大小, C C C表示通道數(shù)量。
-
將每個圖像塊的維度由 P 2 ? C P^2*C P2?C變換為 D D D,在此進行embedding,只需對每個 P 2 ? C P^2*C P2?C圖像塊做一個線性變換,將維度壓縮至 D D D。
-
將 ( N + 1 ) ? D (N+1)*D (N+1)?D的序列作為encoder的輸入。
為啥是N+1呢?因為要多加上一個維度才能關(guān)聯(lián)到全局的信息,這個恰好是class token
class PatchEmbed(nn.Module):
"""
2D Image to Patch Embedding
"""
def __init__(self, img_size=224, patch_size=16, in_c=3, embed_dim=768, norm_layer=None):
super().__init__()
img_size = (img_size, img_size)
patch_size = (patch_size, patch_size)
self.img_size = img_size
self.patch_size = patch_size
self.grid_size = (img_size[0] // patch_size[0], img_size[1] // patch_size[1])
self.num_patches = self.grid_size[0] * self.grid_size[1]
self.proj = nn.Conv2d(in_c, embed_dim, kernel_size=patch_size, stride=patch_size)
self.norm = norm_layer(embed_dim) if norm_layer else nn.Identity()
def forward(self, x):
B, C, H, W = x.shape
assert H == self.img_size[0] and W == self.img_size[1], \
f"Input image size ({H}*{W}) doesn't match model ({self.img_size[0]}*{self.img_size[1]})."
# flatten: [B, C, H, W] -> [B, C, HW]
# transpose: [B, C, HW] -> [B, HW, C]
x = self.proj(x).flatten(2).transpose(1, 2)
x = self.norm(x)
return x
多頭自注意力機制Multi-head Self-attention
多頭較于單頭的優(yōu)勢是增強了網(wǎng)絡(luò)的穩(wěn)定性和魯棒性
將 ( N + 1 ) ? D (N+1)*D (N+1)?D的序列輸入至encoder進行特征提取,其最重要的結(jié)構(gòu)是多頭自注意力機制,2 head的multi-head attention結(jié)構(gòu)如下所示,具體步驟如下:
- 輸入 a i a^i ai經(jīng)過轉(zhuǎn)移矩陣 W W W,得到 q i , k i , v i q^i,k^i,v^i qi,ki,vi,再分別切分成 q i , 1 , q i , 2 , k i , 1 , k i , 2 , v i , 1 , v i , 2 , q i , 1 . . . q^{i,1},q^{i,2},k^{i,1},k^{i,2},v^{i,1},v^{i,2},q^{i,1}... qi,1,qi,2,ki,1,ki,2,vi,1,vi,2,qi,1...
- 接著 q i , j 與 k i , j q^{i,j}與k^{i,j} qi,j與ki,j做attention,得到權(quán)重向量 α α α,將 α α α與 v i , j v^{i,j} vi,j進行加權(quán)求和,最終得到 b i , j b^{i,j} bi,j
- 將 b i , j b^{i,j} bi,j拼接起來,通過一個線性層進行處理,得到最終的結(jié)果。
具體說說其中的attention,
q
i
,
j
,
k
i
,
j
與
v
i
,
j
q^{i,j},k^{i,j}與v^{i,j}
qi,j,ki,j與vi,j計算
b
i
,
j
b^{i,j}
bi,j的方法是縮放點積注意力 (Scaled Dot-Product Attention),加權(quán)內(nèi)積得到
α
α
α:
α
1
,
i
=
q
1
?
k
i
d
α_{1,i}=\frac{q^1*k^i}{\sqrtn5n3t3z}
α1,i?=d?q1?ki?
其中,d是q和k的維度大小,除以一個
d
\sqrtn5n3t3z
d?可以達到歸一化的效果。
接著,將 α 1 , i α_{1,i} α1,i?取softmax操作,并與 v i , j v^{i,j} vi,j相乘得到最后結(jié)果。
class Attention(nn.Module):
def __init__(self,
dim, # 輸入token的dim
num_heads=8,
qkv_bias=False,
qk_scale=None,
attn_drop_ratio=0.,
proj_drop_ratio=0.):
super(Attention, self).__init__()
self.num_heads = num_heads
head_dim = dim // num_heads
self.scale = qk_scale or head_dim ** -0.5
self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias)
self.attn_drop = nn.Dropout(attn_drop_ratio)
self.proj = nn.Linear(dim, dim)
self.proj_drop = nn.Dropout(proj_drop_ratio)
def forward(self, x):
# [batch_size, num_patches + 1, total_embed_dim]
B, N, C = x.shape
# qkv(): -> [batch_size, num_patches + 1, 3 * total_embed_dim]
# reshape: -> [batch_size, num_patches + 1, 3, num_heads, embed_dim_per_head]
# permute: -> [3, batch_size, num_heads, num_patches + 1, embed_dim_per_head]
qkv = self.qkv(x).reshape(B, N, 3, self.num_heads, C // self.num_heads).permute(2, 0, 3, 1, 4)
# [batch_size, num_heads, num_patches + 1, embed_dim_per_head]
q, k, v = qkv[0], qkv[1], qkv[2] # make torchscript happy (cannot use tensor as tuple)
# transpose: -> [batch_size, num_heads, embed_dim_per_head, num_patches + 1]
# @: multiply -> [batch_size, num_heads, num_patches + 1, num_patches + 1]
attn = (q @ k.transpose(-2, -1)) * self.scale
attn = attn.softmax(dim=-1)
attn = self.attn_drop(attn)
# @: multiply -> [batch_size, num_heads, num_patches + 1, embed_dim_per_head]
# transpose: -> [batch_size, num_patches + 1, num_heads, embed_dim_per_head]
# reshape: -> [batch_size, num_patches + 1, total_embed_dim]
x = (attn @ v).transpose(1, 2).reshape(B, N, C)
x = self.proj(x)
x = self.proj_drop(x)
return x
多層感知機Multilayer Perceptron
class Mlp(nn.Module):
"""
MLP as used in Vision Transformer, MLP-Mixer and related networks
"""
def __init__(self, in_features, hidden_features=None, out_features=None, act_layer=nn.GELU, drop=0.):
super().__init__()
out_features = out_features or in_features
hidden_features = hidden_features or in_features
self.fc1 = nn.Linear(in_features, hidden_features)
self.act = act_layer()
self.fc2 = nn.Linear(hidden_features, out_features)
self.drop = nn.Dropout(drop)
def forward(self, x):
x = self.fc1(x)
x = self.act(x)
x = self.drop(x)
x = self.fc2(x)
x = self.drop(x)
return x
DropPath
一種特殊的 Dropout,用來替代傳統(tǒng)的Dropout結(jié)構(gòu)。作用是:若x為輸入的張量,其通道為[B,C,H,W],那么drop_path的含義為在一個Batch_size中,隨機有drop_prob的樣本,不經(jīng)過主干,而直接由分支進行恒等映射。
def drop_path(x, drop_prob: float = 0., training: bool = False):
if drop_prob == 0. or not training:
return x
keep_prob = 1 - drop_prob
shape = (x.shape[0],) + (1,) * (x.ndim - 1) # work with diff dim tensors, not just 2D ConvNets
random_tensor = keep_prob + torch.rand(shape, dtype=x.dtype, device=x.device)
random_tensor.floor_() # binarize
output = x.div(keep_prob) * random_tensor
return output
class DropPath(nn.Module):
"""
Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks).
"""
def __init__(self, drop_prob=None):
super(DropPath, self).__init__()
self.drop_prob = drop_prob
def forward(self, x):
return drop_path(x, self.drop_prob, self.training)
Class Token
假設(shè)我們將原始圖像切分成 3 × 3 = 9個小圖像塊,最終的輸入序列長度卻是10,也就是說我們這里人為的增加了一個向量進行輸入,我們通常將人為增加的這個向量稱為 Class Token。
若沒有這個向量,也就是將 N = 9 個向量輸入 Transformer 結(jié)構(gòu)中進行編碼,我們最終會得到9個編碼向量,可對于圖像分類任務(wù)而言,我們應(yīng)該選擇哪個輸出向量進行后續(xù)分類呢?兩個方案可以實現(xiàn):
- ViT算法提出了一個可學(xué)習(xí)的嵌入向量 Class Token,將它與9個向量一起輸入到 Transformer 結(jié)構(gòu)中,輸出10個編碼向量,然后用這個 Class Token 進行分類預(yù)測即可。
- 取除了cls_token之外的所有token的均值作為類別特征表示,即編碼中的x[:,self.num_tokens:].mean(dim=1)
Positional Encoding
在self-attention中,輸入是一整排的tokens,我們很容易知道tokens的位置信息,但是模型是無法分辨的,因為self-attention的運算是無向的,因此才使用positional encoding把位置信息告訴模型。
按照 Transformer 結(jié)構(gòu)中的位置編碼習(xí)慣,這個工作也使用了位置編碼。不同的是,ViT 中的位置編碼沒有采用原版 Transformer 中的 sin/cos 編碼,而是直接設(shè)置為可學(xué)習(xí)的 Positional Encoding。
MLP Head
得到輸出后,ViT中使用了 MLP Head對輸出進行分類處理,這里的 MLP Head 由 LayerNorm 和兩層全連接層組成,并且采用了 GELU 激活函數(shù)。文章來源:http://www.zghlxwxcb.cn/news/detail-615406.html
參考鏈接:文章來源地址http://www.zghlxwxcb.cn/news/detail-615406.html
- https://blog.csdn.net/qq_42735631/article/details/126709656?ops_request_misc=&request_id=&biz_id=102&utm_term=vision%20transformer%E6%A8%A1%E5%9E%8B&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-0-126709656.nonecase&spm=1018.2226.3001.4187
- https://blog.csdn.net/aixiaomi123/article/details/128025584?ops_request_misc=&request_id=&biz_id=102&utm_term=vision%20transformer%E6%A8%A1%E5%9E%8B&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-1-128025584.nonecase&spm=1018.2226.3001.4187
- https://github.com/google-research/vision_transformer/tree/main
- https://blog.csdn.net/lzzzzzzm/article/details/122963640?ops_request_misc=&request_id=&biz_id=102&utm_term=vit%20transformer%E4%B8%AD%E7%9A%84%E5%A4%9A%E5%A4%B4%E8%87%AA%E6%B3%A8%E6%84%8F%E5%8A%9B%E6%9C%BA%E5%88%B6&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-1-122963640.nonecase&spm=1018.2226.3001.4187
02&utm_term=vit%20transformer%E4%B8%AD%E7%9A%84%E5%A4%9A%E5%A4%B4%E8%87%AA%E6%B3%A8%E6%84%8F%E5%8A%9B%E6%9C%BA%E5%88%B6&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-1-122963640.nonecase&spm=1018.2226.3001.4187- https://blog.csdn.net/weixin_41803874/article/details/125729668
到了這里,關(guān)于ViT-vision transformer的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!