histogram loss筆記

histogram loss[1] 用來做 embedding learning,思路是最小化 negative pair 相似性比 positive pair 還大的可能性/概率,其中相似性用 embeddings 的內積表示:sij=<xi,xj>s_{ij}=<x_i,x_j>,按 positive/negative 分成 s+s^+ss^- 兩撥,對應文中 S+S^+SS^- 兩個集合。將 embeddings 做 L2 normalization,相似性範圍就是 [1,1][-1,1]
上述的那個概率就是文中的公式 3:
preverse=11p(x)[1xp+(y)dy]dx(a)p_{reverse}=\int_{-1}^1p^-(x)\left[\int_{-1}^xp^+(y)dy\right]dx \tag{a}
其中 p()p^-(\cdot)ss^- 的分佈, p+()p^+(\cdot)s+s^+ 的分佈,需要估計。

Density Estimation

密度估計的思路見 [3],這裏符號儘量沿用 [1],省去 +/- 上標因爲兩者都是這麼估計。先將密度函數的極限定義式寫出來:
p(tr)=limΔ0Φ(tr+1)Φ(tr1)2Δp(t_r)=\lim_{\Delta\rightarrow0}\frac{\Phi(t_{r+1})-\Phi(t_{r-1})}{2\Delta}
文中在 [1,1][-1,1] 均勻插了 R 個點,Δ=2R1\Delta=\frac{2}{R-1} 是組距,trt_r 是其中一個插的點,r{1,,R}r\in\{1,\dots,R\}Φ()\Phi(\cdot)p()p(\cdot) 對應的累積分佈函數。於是離散地估計 (a) 式:
Φ(tr+1)Φ(tr1)ϕr+1ϕr1=s[tr1,tr+1]1S\Phi(t_{r+1})-\Phi(t_{r-1})\approx\phi_{r+1}-\phi_{r-1}=\sum_{s\in[t_{r-1},t_{r+1}]}\frac{1}{|S|}
此處 ϕr\phi_r 是離散的累積分佈函數,
p(tr)hrΔ=#s[tr1,tr+1]2S1Δp(t_r)\approx\frac{h_r}{\Delta}=\frac{\#s\in[t_{r-1},t_{r+1}]}{2|S|}\frac{1}{\Delta}
這可以看成直方圖:第 r 個矩形,Δ\Delta 爲組距,分子統計落入 trt_r 鄰域的點數,第一項是區間的概率和(分母的 2 是因爲對於 sij[tx,tx+1]s_{ij}\in[t_x,t_{x+1}],會分別在考慮 p(tx)p(t_x)p(tx+1)p(t_{x+1}) 被考慮,相當於被算了兩次),除以組距得到密度。
由 [3] 可以看到,這種估計可以改寫成核函數的形式:
hr=1S(i,j)121(sijtrΔ1)(b)h_r=\frac{1}{|S|}\sum_{(i,j)}\frac{1}{2}\cdot1(\frac{|s_{ij}-t_r|}{\Delta}\leq1)\tag{b}
其中所用的核可以看成:KΔ(xtr)=121(xtrΔ1)K_{\Delta}(x-t_r)=\frac{1}{2}\cdot1(\frac{|x-t_r|}{\Delta}\leq1)。於是一種自然的擴展就是:用別的核函數。文中用了 triangular kernel[8],公式 (2) 可以改寫成:
δi,j,r=(1sijtrΔ)1(sijtrΔ1)(c)\delta_{i,j,r}=(1-\frac{|s_{ij}-t_r|}{\Delta})\cdot1(\frac{|s_{ij}-t_r|}{\Delta}\leq1)\tag{c}
於是公式 (1) 就是:
hr=1S(i,j)(1sijtrΔ)1(sijtrΔ1)(d)h_r=\frac{1}{|S|}\sum_{(i,j)}(1-\frac{|s_{ij}-t_r|}{\Delta})\cdot1(\frac{|s_{ij}-t_r|}{\Delta}\leq1)\tag{d}
和 (b) 式對比着看,換這個核使得 hrh_r 裏帶有 sijs_{ij},從而可以回傳梯度。
可以驗證這個 {hr}\{h_r\} 序列是一個合法的概率分佈:易知 0hr<10\leq h_r<1,而要計算 r=1Rhr\sum_{r=1}^Rh_r,考慮到對於 sij[tk,tk+1)s_{ij}\in[t_k,t_{k+1}),它會分別在 hkh_k 時貢獻 tk+1sijΔ\frac{t_{k+1}-s_{ij}}{\Delta}、在 hk+1h_{k+1} 時貢獻 sijtkΔ\frac{s_{ij}-t_k}{\Delta},所以:
r=1Rhr=1S(i,j)[sijtkΔ+tk+1sijΔ]=(i,j)1S=1\begin{aligned}\sum_{r=1}^Rh_r&=\frac{1}{|S|}\sum_{(i,j)}[\frac{s_{ij}-t_k}{\Delta}+\frac{t_{k+1}-s_{ij}}{\Delta}] \\ &=\sum_{(i,j)}\frac{1}{|S|}=1 \end{aligned}

Histogram Loss

最終對 (a) 式的估計就寫成文中公式 (4),即 histogram loss:
L=r=1R(hrq=1rhq+)L=\sum_{r=1}^R(h_r^-\sum_{q=1}^rh_q^+)

Code

  • tensorflow 1.12
#import tensorflow as tf
def cos(X, Y=None):
	"""C(i,j) = cos(Xi, Yj)"""
    X_n = tf.math.l2_normalize(X, axis=1)
    if (Y is None) or (X is Y):
        return tf.matmul(X_n, tf.transpose(X_n))
    Y_n = tf.math.l2_normalize(Y, axis=1)
    return tf.matmul(X_n, tf.transpose(Y_n))


def sim_mat(label, label2=None):
    """S[i][j] = 1 <=> i- & j-th share at lease 1 label"""
    if label2 is None:
    	label2 = label
    return tf.cast(tf.matmul(label, tf.transpose(label2)) > 0, "float32")


def histogram_loss(X, L, R=151):
    """histogram loss
    X: [n, d], feature WITHOUT L2 norm
    L: [n, c], label
    R: scalar, num of estimating point, same as the paper
    """
    delta = 2. / (R - 1)  # step
    # t = (t_1, ..., t_R)
    t = tf.lin_space(-1., 1., R)[:, None]  # [R, 1]
    # gound-truth similarity matrix
    M = sim_mat(L)  # [n, n]
    # cosine similarity, in [-1, 1]
    S = cos(X)  # [n, n]

    # get indices of upper triangular (without diag)
    S_hat = S + 2  # shift value to [1, 3] to ensure triu > 0
    S_triu = tf.linalg.band_part(S_hat, 0, -1) * (1 - tf.eye(tf.shape(S)[0]))
    triu_id = tf.where(S_triu > 0)

    # extract triu -> vector of [n(n - 1) / 2]
    S = tf.gather_nd(S, triu_id)[None, :]  # [1, n(n-1)/2]
    M_pos = tf.gather_nd(M, triu_id)[None, :]
    M_neg = 1 - M_pos

    scaled_abs_diff = tf.math.abs(S - t) / delta  # [R, n(n-1)/2]
    # mask_near = tf.cast(scaled_abs_diff <= 1, "float32")
    # delta_ijr = (1 - scaled_abs_diff) * mask_near
    delta_ijr = tf.maximum(0, 1 - scaled_abs_diff)

    def histogram(mask):
        """h = (h_1, ..., h_R)"""
        sum_delta = tf.reduce_sum(delta_ijr * mask, 1)  # [R]
        return sum_delta / tf.maximum(1, tf.reduce_sum(mask))

    h_pos = histogram(M_pos)[None, :]  # [1, R]
    h_neg = histogram(M_neg)  # [R]
    # all 1 in lower triangular (with diag)
    mask_cdf = tf.linalg.band_part(tf.ones([R, R]), -1, 0)
    cdf_pos = tf.reduce_sum(mask_cdf * h_pos, 1)  # [R]

    loss = tf.reduce_sum(h_neg * cdf_pos)
    return loss

References

  1. (paper)Learning Deep Embeddings with Histogram Loss
  2. (code)madkn/HistogramLoss
  3. 什麼是核密度估計?如何感性認識?
  4. 2.8. 概率密度估計(Density Estimation)
  5. 核密度估計
  6. 非參數方法——核密度估計(Kernel Density Estimation)
  7. 核密度估計Kernel Density Estimation(KDE)概述 密度估計的問題
  8. Kernel (statistics)
  9. 核函數
  10. 《Learning Deep Embeddings with Histogram Loss》筆記
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章