NetVLAD
題目:NetVLAD: CNN architecture for weakly supervised place recognition
這是一篇場景識別的論文,場景識別可以看作是影象檢索的一種。影象檢索是給定query image
,然後透過特徵提取器
得到一個固定維度的特徵向量
,透過計算
和資料庫中已提前計算的特徵向量之間的距離(例如歐式距離),從而判斷
所屬的類別。
NetVLAD是VLAD的改進版,NeXtVLAD又是NetVLAD的改進版,關於NeXTVLAD的介紹可以參考
如果在閱讀本文的過程中有不理解的地方,可以直接跳到第四部分,對照著NetVLAD的實現再看公式,可能更容易理解NetVLAD是怎麼做的。
1 介紹
論文中提出了一個用於場景識別的可端到端訓練的CNN架構,架構的主要元件是NetVLAD。
下面的這個圖,簡單描述了NetVLAD的聚類思路,如果暫時不理解,沒關係,可以先往下看。
2 回顧VLAD
通常,透過傳統方法(例如SIFT)會獲得一張影象的多個區域性特徵。Vector of Locally Aggregated Descriptors (VLAD)是其中一種將若干區域性特徵壓縮為一個特定大小全域性特徵的方法,透過聚類,實現了將特徵降維。VLAD在影象檢索任務上得到廣泛使用。
個
維的區域性特徵
作為輸入,
個聚類中心
作為VLAD的引數,輸出的特徵
是
維的,這裡為了方便表示把
表示成矩陣的形式,實際上VLAD的輸出
會被轉換成向量,然後再進行規範化(normalization)操作,最後這個特徵作為影象的全域性特徵。
的計算公式如下:
其中
表示第
個區域性特徵第
維的值,
表示第
個聚類中心第
維的值。
是指示函式,表示第
個區域性特徵是否屬於第
類,如果
與第
個聚類中心最近,則
的值為1,則與其他聚類中心(
)的值為0。更具體點,
計算的是屬於某一類的所有區域性特徵與對應聚類中心的殘差和。
對特徵
在
這一維上執行
操作,再轉換成向量,最後執行
得到最終的全域性特徵向量。
3 NetVLAD
然而,直接在可訓練的CNN架構中嵌入VLAD層是不可行的,因為VLAD中的
是不連續的,因此論文作者對
做了修改,提出了NetVLAD。
把VLAD中的指示函式
換成可導的
約掉分子分母中的
,再令
和
,即可得到
的最終表示式
表示把
分配給聚類中心
的權重,取值範圍為0到1之間。
越大表示
與
的距離越近。
是一個常數,當
時,
的取值就無限接近於1或0了。
最後得到
的表示式
其中
均為可學習的引數。
至此,NetVLAD的推導過程就介紹完了。
4 NetVLAD實現
下面是NetVLAD層的PyTorch實現,來源於
https://
github。com/lyakaap/NetV
LAD-pytorch/blob/master/netvlad。py
import
torch
import
torch。nn
as
nn
import
torch。nn。functional
as
F
class
NetVLAD
(
nn
。
Module
):
“”“NetVLAD layer implementation
Args:
num_clusters : int
The number of clusters
dim : int
Dimension of descriptors
alpha : float
Parameter of initialization。 Larger value is harder assignment。
normalize_input : bool
If true, descriptor-wise L2 normalization is applied to input。
”“”
def
__init__
(
self
,
num_clusters
=
64
,
dim
=
128
,
alpha
=
100。0
,
normalize_input
=
True
):
super
()
。
__init__
()
self
。
num_clusters
=
num_clusters
self
。
dim
=
dim
self
。
alpha
=
alpha
self
。
normalize_input
=
normalize_input
self
。
conv
=
nn
。
Conv2d
(
dim
,
num_clusters
,
kernel_size
=
(
1
,
1
),
bias
=
True
)
self
。
centroids
=
nn
。
Parameter
(
torch
。
rand
(
num_clusters
,
dim
))
def
forward
(
self
,
x
):
# x: (N, C, H, W), H * W對應論文中的N,表示區域性特徵的數目,C對應論文中的D,表示特徵的維度
N
,
C
=
x
。
shape
[:
2
]
if
self
。
normalize_input
:
x
=
F
。
normalize
(
x
,
p
=
2
,
dim
=
1
)
# across descriptor dim
# soft-assignment
soft_assign
=
self
。
conv
(
x
)
。
view
(
N
,
self
。
num_clusters
,
-
1
)
# (N, C, H, W) -> (N, num_clusters, H, W) -> (N, num_clusters, H * W)
soft_assign
=
F
。
softmax
(
soft_assign
,
dim
=
1
)
# (N, num_clusters, H * W)
x_flatten
=
x
。
view
(
N
,
C
,
-
1
)
# (N, C, H, W) -> (N, C, H * W)
# calculate residuals to each clusters
# 減號前面前記為a,後面記為b, residual = a - b
# a: (N, C, H * W) -> (num_clusters, N, C, H * W) -> (N, num_clusters, C, H * W)
# b: (num_clusters, C) -> (H * W, num_clusters, C) -> (num_clusters, C, H * W)
# residual: (N, num_clusters, C, H * W)
residual
=
x_flatten
。
expand
(
self
。
num_clusters
,
-
1
,
-
1
,
-
1
)
。
permute
(
1
,
0
,
2
,
3
)
-
\
self
。
centroids
。
expand
(
x_flatten
。
size
(
-
1
),
-
1
,
-
1
)
。
permute
(
1
,
2
,
0
)
。
unsqueeze
(
0
)
# soft_assign: (N, num_clusters, H * W) -> (N, num_clusters, 1, H * W)
# (N, num_clusters, C, H * W) * (N, num_clusters, 1, H * W)
residual
*=
soft_assign
。
unsqueeze
(
2
)
vlad
=
residual
。
sum
(
dim
=-
1
)
# (N, num_clusters, C, H * W) -> (N, num_clusters, C)
vlad
=
F
。
normalize
(
vlad
,
p
=
2
,
dim
=
2
)
# intra-normalization
vlad
=
vlad
。
view
(
x
。
size
(
0
),
-
1
)
# flatten vald: (N, num_clusters, C) -> (N, num_clusters * C)
vlad
=
F
。
normalize
(
vlad
,
p
=
2
,
dim
=
1
)
# L2 normalize
return
vlad