网上看到关于数据降维的文章不少,介绍MDS的却极少,遂决定写一写。
考虑一个这样的问题。我们有n个样本,每个样本维度为m。我们的目标是用不同的新的k维向量(k<
举个栗子:原来有3个4维样本(1,0,0,3),(8,0,0,5),(2,0,0,4),显然我们可以用三个新的二维样本(1,3),(8,5),(2,4)来保持维度变小并相互之间距离不变。
那么问题来了,如果不是这么明显的数据该如何来处理?降维后的距离一定会相等吗?
MDS算法给出了在给定k值条件下的最优解决方案。
首先我们计算所有原空间中样本相互之间的距离平方矩阵Dist[][],显然这是一非负对称实数矩阵。至此,其实我们要维护的就是Dist不变,与原样本已经无关了。
接下来我们要根据Dist推算出目标降维后内积矩阵B,B[i][j]就是降维后第i,j个向量的内积。关于推导过程可以看相关书籍,这里给出一个优美的结论。
B[i][j]=-0.5(Dist[i][j] - avg(Disti[i]) - avg(Distj[j]) + avg_Dist)
有了B,只需要对B分解成B=U*UT的形式就达到我们的目标了。
对B做特征分解(奇异分解也一样),B=V*diag*VT。
我们可以取最大的k个特征值及其对应的特征向量构成diagk和Vk。
此时U=Vk*diagk0,5就是我们降维后的n个行向量组成的矩阵了。
如果还有疑惑,下面的代码运行试试就明白了。
召唤算法君:
import numpy as np # run this to get a test matrix # A = np.random.randint(1,100,(5,20)) # np.save('mat.npy', A) # exit() A = np.load('mat.npy') n,m = A.shape Dist = np.zeros((n,n)) B = np.zeros((n,n)) for i in range(n): for j in range(n): Dist[i][j] = sum((ix-jx)**2 for ix,jx in zip(A[i], A[j])) disti2 = np.array([0]*n) distj2 = np.array([0]*n) for x in range(n): disti2[x] = np.mean([Dist[x][j] for j in range(n)]) distj2[x] = np.mean([Dist[i][x] for i in range(n)]) distij2 = np.mean([Dist[i][j] for i in range(n) for j in range(n)]) for i in range(n): for j in range(n): B[i][j] = -0.5*(Dist[i][j] - disti2[i] - distj2[j] + distij2) w,v = np.linalg.eig(B) v=v.transpose() U = [{'eVal':w[i], 'eVec':v[i]} for i in range(n)] U.sort(key = lambda obj:obj.get('eVal'), reverse = True) k=4 w=np.array([0]*k) v=np.zeros((k,n)) for i in range(k): w[i] = U[i].get('eVal')**0.5 v[i] = U[i].get('eVec') ans = np.dot(v.transpose(), np.diag(w)) ans_dist = np.zeros((n,n)) for i in range(n): ans_str="" for j in range(n): ans_dist[i][j] = sum((ix-jx)**2 for ix,jx in zip(ans[i], ans[j])) print("Orign dis[][] is :") print Dist print("MDS dis[][] is :") print(ans_dist)