距离矩阵 D i j x = ∥ x i − x j ∥ 2 2 D^x_{ij}=\left\|\boldsymbol{x}_i-\boldsymbol{x}_j\right\|_2^2 Dijx=∥xi−xj∥22的计算
给定数据矩阵 X ( N × D ) \boldsymbol{X}\left(N\times D\right) X(N×D), 经常需要求解每两个样本间的距离 D i j x = ∥ x i − x j ∥ 2 2 D^x_{ij}=\left\|\boldsymbol{x}_i-\boldsymbol{x}_j\right\|_2^2 Dijx=∥xi−xj∥22.
Remark: 这个距离矩阵的用处有很多,如核函数,对比学习, etc.
目前我所知有3种编程方法可以求解 D i j x D^x_{ij} Dijx.
- For Loop
import numpy as np
x = np.array([[1,5],[3,7],[7,9]])
def distance_forloop(x):
N, _ = x.shape
d = np.zeros((N,N))
for i in range(N):
for j in range(N):
d[i,j] = np.sum((x[i,:]-x[j,:])*(x[i,:]-x[j,:]))
return d
- Elementary calculation
def distance_elementary(x):
aa = bb = np.sum(x * x, axis = 1, keepdims = True)
ab = np.matmul(x, x.T)
return aa + bb.T - 2 * ab
- Matrix Form
D x = d i a g n o a l { d i a g ( X X ⊤ ) } 1 1 ⊤ + 1 1 ⊤ d i a g n o a l { d i a g ( X X ⊤ ) } − 2 X X ⊤ \boldsymbol{D}^x=diagnoal\left\{diag\left(\boldsymbol{X}\boldsymbol{X}^\top \right)\right\}\boldsymbol{1}\boldsymbol{1}^\top+\boldsymbol{1}\boldsymbol{1}^\top diagnoal\left\{diag\left(\boldsymbol{X}\boldsymbol{X}^\top\right)\right\}-2\boldsymbol{X}\boldsymbol{X}^\top Dx=diagnoal{diag(XX⊤)}11⊤+11⊤diagnoal{diag(XX⊤)}−2XX⊤
where d i a g ( X X ⊤ ) = [ ∥ x 1 ∥ 2 2 , ⋯ , ∥ x N ∥ 2 2 ] diag\left(\boldsymbol{X}\boldsymbol{X}^\top \right) = \left[ \|\boldsymbol{x}_1\|_2^2, \cdots, \|\boldsymbol{x}_N\|_2^2\right] diag(XX⊤)=[∥x1∥22,⋯,∥xN∥22].
def distance_matrixform(x):
N, _ = x.shape
diag = np.diagonal(np.matmul(x,x.T))# 这里得到的是向量
diag = np.diag(diag)#还需要转换成对角矩阵
one_one_t = np.ones((N,N))
return np.matmul(diag,one_one_t) + np.matmul(one_one_t,diag) - 2*np.matmul(x, x.T)
Remakr: np.diagonal: (N,N)->(N,), np.diag:(N,)->(N,N)
经过检验这3种计算方式所得结果相同
d1 = distance_forloop(x)
print(d1)
d2 = distance_elementary(x)
print(d2)
d3 = distance_matrixform(x)
print(d3)
print((d1==d3)&(d1==d2))
但是方法2和方法3是类似的,计算效率都比1高,但是我个人倾向于方法3,因为它是公式化描述的。