Notes
SSDH[1]做的是 hashing 式的图像检索,用图像的 VGG-F fc-7 layer feature 计算各样本间的距离,并基于此距离构造相似矩阵指导 hash code 的学习。
其构造相似矩阵的思路是:两个样本之间,距离分成三类,
- 特别近,认为它们相似, S i j = 1 S_{ij}=1 Sij=1
- 特别远,认为不相似, S i j = − 1 S_{ij}=-1 Sij=−1
- 不远不近,不确定, S i j = 0 S_{ij}=0 Sij=0
作者先将样本间的距离(文中用了 cosine 距离)算出来,观察这些距离值的分布的概率密度函数(文中 Figure 1(b)),发现有点像两个半边的正态分布(文中 Figure 1 (c)、(d))拼在一起的样子。
于是在最高点处劈开两边(即将数据分成两部分),一边各是一个半边的正态分布,分别拟合这两个正态分布,得到它们的均值和标准差:
m
l
,
σ
l
m_l, \sigma_l
ml,σl 和
m
r
,
σ
r
m_r, \sigma_r
mr,σr,再利用这两组
m
,
σ
m,\sigma
m,σ 构造两个距离阈值:
d
s
=
m
l
−
α
σ
l
d_s=m_l-\alpha\sigma_l
ds=ml−ασl 和
d
d
=
m
r
+
β
σ
r
d_d=m_r+\beta\sigma_r
dd=mr+βσr(其中
α
,
β
\alpha,\beta
α,β 是超参)。然后定义相似矩阵:
S
i
j
=
{
1
,
d
(
i
,
j
)
≤
d
s
0
,
d
s
<
d
(
i
,
j
)
<
d
d
−
1
,
d
(
i
,
j
)
≥
d
d
S_{ij}=\begin{cases} 1, & d(i,j)\le d_s \\ 0, & d_s<d(i,j)<d_d \\ -1, & d(i,j)\ge d_d \end{cases}
Sij=⎩⎪⎨⎪⎧1,0,−1,d(i,j)≤dsds<d(i,j)<ddd(i,j)≥dd
其中 d(i,j) 表示样本 i 和样本 j 之间的距离。
拟合正态分布获得均值和标准差的过程可以直接调 scipy.stats.norm.fit()
[3] 完成,不用自己写。
Code
这是作者释出代码中构造相似矩阵的部分,详见 [2]。
# 数据距离
euc_ = pdist(train_features, 'cosine')
euc_dis = squareform(euc_)
orig_euc_dis = euc_dis
# 试探分界点:距离分布密度的最高点所在
start = -0.00000001
margin = 1.0/100 # 组距(dx)
num = np.zeros(100)
max_num = 0.0
max_value = 0.0
for i in range(100):
# (start, end) 定义了一个组距
end = start + margin
# 统计直方图中这个组距对应的那条柱
temp_matrix = (euc_dis > start) & (euc_dis < end)
num[i] = np.sum(temp_matrix) # 频数
if num[i] > max_num:
max_num = num[i] # 最大频数 -> 对应最大密度
max_value = start
start = end
# 数据分界:left-Gaussian & right-Gaussian
euc_dis = euc_dis.reshape(-1, 1)
left = []
right = []
for i in range(euc_dis.shape[0]):
if euc_dis[i] <= max_value:
left.append(euc_dis[i])
else:
right.append(euc_dis[i])
left = np.array(left)
right = np.array(right)
# 补全为完整的 Gaussian 分布数据(补上另一半)
fake_right = 2 * max_value - left
fake_left = 2 * max_value - right
left_all = np.concatenate([left, fake_right])
right_all = np.concatenate([fake_left, right])
# 拟合高斯分布 <- 直接掉 scipy 的包
l_mean, l_std = norm.fit(left_all)
r_mean, r_std = norm.fit(right_all)
# 得相似矩阵
S1 = ((orig_euc_dis < l_mean-2*l_std))*1.0 # 明显小的为 similar
S2 = ((orig_euc_dis > r_mean+2*r_std))*(-1.0) # 明显大的为 disimilar
S = S1 + S2