当前位置:   article > 正文

代码实现usb管控方法_六种常见数据降维方法简介及代码实现

for i in range(len(data1)):

事实上,在高维情形下 现的数据样本稀疏、 距离计算困 难等问是所有机器学习方法共同面 的严重障碍, 被称为" 维数灾难" (curse of dimensionality) . 缓解维数灾难的一个重要途径是降维(dimension reduction) 亦称" 维数约简 “ ,即通过某种数学变换将原始高维属 性空间转变为 一个低维"子空间" (subspace) ,在这 子空 中样本密 大幅提高 计算 变得更为容易。为什么进行降维?这是因为在很多时候, 人们观测或收集到的数据样本虽是高维的,但与学习任务密切相关的也许仅是某个低维分布,即高 空间中个低维"嵌入" (embedding) 如图给出直观的例子,原始高空间中的样本点,在这个低维嵌入子空间中更容易进行学习。

f69604728d73238710a30e9811fcded0.png

常见的数据降维方法有:PCA、LDA、MDS、ISOMAP、SNE、T-SNE、AutoEncoder等

一、MDS:MultiDimensional Scaling 多维尺度变换

MDS算法要求原始空间中样本之间的距离在低维空间中得以保持。但是为了有效降维,我们往往只需要降维后的距离与原始空间距离尽可能接近即可。

07e74e3085c3acdd911dffff2d499500.png

7fb449300ddbf953db4e765e097b932e.png

f9b6306d55a2546a63ddb7ebf8e5629d.png

1387ca3b298624245eae0eb47e4575cc.png

7cce67b05d27e319a038f6b1bc966c45.png
  1. def calculate_distance(x, y):
  2. d = np.sqrt(np.sum((x - y) ** 2))
  3. return d
  4. # 计算矩阵各行之间的欧式距离;x矩阵的第i行与y矩阵的第0-j行继续欧式距离计算,构成新矩阵第i行[i0、i1...ij]
  5. def calculate_distance_matrix(x, y):
  6. d = metrics.pairwise_distances(x, y)
  7. return d
  8. def cal_B(D):
  9. (n1, n2) = D.shape
  10. DD = np.square(D) # 矩阵D 所有元素平方
  11. Di = np.sum(DD, axis=1) / n1 # 计算dist(i.)^2
  12. Dj = np.sum(DD, axis=0) / n1 # 计算dist(.j)^2
  13. Dij = np.sum(DD) / (n1 ** 2) # 计算dist(ij)^2
  14. B = np.zeros((n1, n1))
  15. for i in range(n1):
  16. for j in range(n2):
  17. B[i, j] = (Dij + DD[i, j] - Di[i] - Dj[j]) / (-2) # 计算b(ij)
  18. return B
  19. def MDS(data, n=2):
  20. D = calculate_distance_matrix(data, data)
  21. # print(D)
  22. B = cal_B(D)
  23. Be, Bv = np.linalg.eigh(B) # Be矩阵B的特征值,Bv归一化的特征向量
  24. # print numpy.sum(B-numpy.dot(numpy.dot(Bv,numpy.diag(Be)),Bv.T))
  25. Be_sort = np.argsort(-Be)
  26. Be = Be[Be_sort] # 特征值从大到小排序
  27. Bv = Bv[:, Be_sort] # 归一化特征向量
  28. Bez = np.diag(Be[0:n]) # 前n个特征值对角矩阵
  29. # print Bez
  30. Bvz = Bv[:, 0:n] # 前n个归一化特征向量
  31. Z = np.dot(np.sqrt(Bez), Bvz.T).T
  32. # print(Z)
  33. return Z

95fc0a82ae6f792b02e91b138ad992f6.png

二、ISOMAP: Isometric Mapping 等距特征映射

对于流形(Manifold,局部具有欧式空间性质的空间),两点之间的距离并非欧氏距离。而是采用“局部具有欧式空间性质”的原因,让两点之间的距离近似等于依次多个临近点的连线的长度之和。通过这个方式,将多维空间“展开”到低维空间。

Isomap算法是在MDS算法的基础上衍生出的一种算法,MDS算法是保持降维后的样本间距离不变,Isomap算法引进了邻域图,样本只与其相邻的样本连接,他们之间的距离可直接计算,较远的点可通过最小路径算出距离,在此基础上进行降维保距。

计算流程如下:

  1. 设定邻域点个数,计算邻接距离矩阵,不在邻域之外的距离设为无穷大;
  2. 求每对点之间的最小路径,将邻接矩阵矩阵转为最小路径矩阵;
  3. 输入MDS算法,得出结果,即为Isomap算法的结果。

6162537f24797bf0b8ca79032374e3e0.png
  1. def calculate_distance(x, y):
  2. d = np.sqrt(np.sum((x - y) ** 2))
  3. return d
  4. # 计算矩阵各行之间的欧式距离;x矩阵的第i行与y矩阵的第0-j行继续欧式距离计算,构成新矩阵第i行[i0、i1...ij]
  5. def calculate_distance_matrix(x, y):
  6. d = metrics.pairwise_distances(x, y)
  7. return d
  8. def cal_B(D):
  9. (n1, n2) = D.shape
  10. DD = np.square(D) # 矩阵D 所有元素平方
  11. Di = np.sum(DD, axis=1) / n1 # 计算dist(i.)^2
  12. Dj = np.sum(DD, axis=0) / n1 # 计算dist(.j)^2
  13. Dij = np.sum(DD) / (n1 ** 2) # 计算dist(ij)^2
  14. B = np.zeros((n1, n1))
  15. for i in range(n1):
  16. for j in range(n2):
  17. B[i, j] = (Dij + DD[i, j] - Di[i] - Dj[j]) / (-2) # 计算b(ij)
  18. return B
  19. def MDS(data, n=2):
  20. D = calculate_distance_matrix(data, data)
  21. # print(D)
  22. B = cal_B(D)
  23. Be, Bv = np.linalg.eigh(B) # Be矩阵B的特征值,Bv归一化的特征向量
  24. # print numpy.sum(B-numpy.dot(numpy.dot(Bv,numpy.diag(Be)),Bv.T))
  25. Be_sort = np.argsort(-Be)
  26. Be = Be[Be_sort] # 特征值从大到小排序
  27. Bv = Bv[:, Be_sort] # 归一化特征向量
  28. Bez = np.diag(Be[0:n]) # 前n个特征值对角矩阵
  29. # print Bez
  30. Bvz = Bv[:, 0:n] # 前n个归一化特征向量
  31. Z = np.dot(np.sqrt(Bez), Bvz.T).T
  32. # print(Z)
  33. return Z

49a23e8cf49f4a3adaba6815249be1c1.png

参考

三、PCA:Principle component analysis 主成分分析

它是一个线性变换。这个变换把数据变换到一个新的坐标系统中,使得任何数据投影的第一大方差在第一个坐标(称为第一主成分)上,第二大方差在第二个坐标(第二主成分)上,依次类推。主成分分析经常用于减少数据集的维数,同时保持数据集的对方差贡献最大的特征。

9104f7a90b5ab6b2f479146b5ce6a68e.png

74ee06aec4dfdd76e9546b92a07c9038.png

7a898c19e8b105a7a2b9b54fd5425ff6.png

fc39a00d3e79bcf1837ba78b3e258b35.png
  1. def pca(data, n):
  2. data = np.array(data)
  3. # 均值
  4. mean_vector = np.mean(data, axis=0)
  5. # 协方差
  6. cov_mat = np.cov(data - mean_vector, rowvar=0)
  7. # 特征值 特征向量
  8. fvalue, fvector = np.linalg.eig(cov_mat)
  9. # 排序
  10. fvaluesort = np.argsort(-fvalue)
  11. # 取前几大的序号
  12. fValueTopN = fvaluesort[:n]
  13. # 保留前几大的数值
  14. newdata = fvector[:, fValueTopN]
  15. new = np.dot(data, newdata)
  16. return new

95fc0a82ae6f792b02e91b138ad992f6.png

四、LDA:Linear Discriminant Analysis 线性判别分析

与PCA一样,是一种线性降维算法。不同于PCA只会选择数据变化最大的方向,由于LDA是有监督的(分类标签),所以LDA会主要以类别为思考因素,使得投影后的样本尽可能可分。它通过在k维空间选择一个投影超平面,使得不同类别在该超平面上的投影之间的距离尽可能近,同时不同类别的投影之间的距离尽可能远。从而试图明确地模拟数据类之间的差异。

LDA方法的考虑是,对于一个多类别的分类问题,想要把它们映射到一个低维空间,如一维空间从而达到降维的目的,我们希望映射之后的数据间,两个类别之间“离得越远”,且类别内的数据点之间“离得越近”,这样两个类别就越好区分。因此LDA方法分别计算“within-class”的分散程度Sw和“between-class”的分散程度Sb,而我们希望的是Sb/Sw越大越好,从而找到最合适的映射向量w。

假设我们的数据集$D={(x_1,y_1), (x_2,y_2), ...,((x_m,y_m))}$,其中任意样本xixi为n维向量,yi∈{0,1}。我们定义Nj(j=0,1)为第j类样本的个数,Xj(j=0,1)为第j类样本的集合,而μj(j=0,1)为第j类样本的均值向量,定义Σj(j=0,1)为第j类样本的协方差矩阵(严格说是缺少分母部分的协方差矩阵)。

μj的表达式为:

2873b7155ba00268e20c53efddc59b3d.png

Σj的表达式为:

2cb779ccc3e44581c6277d654ea4ff33.png

c09e6eeec6432b2b55f1db5658ea33ff.png
  1. import numpy as np
  2. import csv
  3. import matplotlib.pyplot as plt
  4. def loadDataset(filename):
  5. data1 ,data2 = [], []
  6. with open(filename, 'r') as f:
  7. lines = csv.reader(f)
  8. data = list(lines)
  9. for i in range(len(data)):
  10. del(data[i][0])
  11. for j in range(len(data[i])):
  12. data[i][j] = float(data[i][j])
  13. if data[i][-1]:
  14. del(data[i][-1])
  15. data1.append(data[i])
  16. else:
  17. del(data[i][-1])
  18. data2.append(data[i])
  19. return np.array(data1), np.array(data2)
  20. def lda_num2(data1, data2, n=2):
  21. mu0 = data2.mean(0)
  22. mu1 = data1.mean(0)
  23. print(mu0)
  24. print(mu1)
  25. sum0 = np.zeros((mu0.shape[0], mu0.shape[0]))
  26. for i in range(len(data2)):
  27. sum0 += np.dot((data2[i] - mu0).T, (data2[i] - mu0))
  28. sum1 = np.zeros(mu1.shape[0])
  29. for i in range(len(data1)):
  30. sum1 += np.dot((data1[i] - mu1).T, (data1[i] - mu1))
  31. s_w = sum0 + sum1
  32. print(s_w)
  33. w = np.linalg.pinv(s_w) * (mu0 - mu1)
  34. new_w = w[:n].T
  35. new_data1 = np.dot(data1, new_w)
  36. new_data2 = np.dot(data2, new_w)
  37. return new_data1, new_data2
  38. def view(data):
  39. x = [i[0] for i in data]
  40. y = [i[1] for i in data]
  41. plt.figure()
  42. plt.scatter(x, y)
  43. plt.show()
  44. if __name__ == '__main__':
  45. data1, data2 = loadDataset("Pima.csv")
  46. newdata1, newdata2 = lda_num2(data1, data2, 2)
  47. print(newdata1)
  48. print(newdata2)
  49. view(np.concatenate((newdata1, newdata2))*10**7)
  50. view(newdata1 * 10 ** 7)
  51. view(newdata2 * 10 ** 7)

db2a7f34b57d7e0ca045a7d9ae5473c1.png

db2a7f34b57d7e0ca045a7d9ae5473c1.png

959b07ea54eb61b5efe761748a34b761.png

五、SNE:Stochastic Neighbor Embedding

SNE是通过仿射(affinitie)变换将数据点映射到概率分布上,主要包括两个步骤:

  • SNE构建一个高维对象之间的概率分布,使得相似的对象有更高的概率被选择,而不相似的对象有较低的概率被选择。
  • SNE在低维空间里在构建这些点的概率分布,使得这两个概率分布之间尽可能的相似。

我们看到SNE模型是非监督的降维,他跟kmeans等不同,他不能通过训练得到一些东西之后再用于其它数据(比如kmeans可以通过训练得到k个点,再用于其它数据集,而t-SNE只能单独的对数据做操作,也就是说他只有fit_transform,而没有fit操作)

SNE是先将欧几里得距离转换为条件概率来表达点与点之间的相似度。具体来说,给定一个N个高维的数据 x1,...,xN(注意N不是维度), SNE首先是计算概率pij,正比于xi和xj之间的相似度(这种概率是我们自主构建的),即以x_i自己为中心,以高斯分布选择x_j作为近邻点的条件概率::

c6d3df41f9b9de7aa41ce3646bc41874.png

这里的有一个参数是σi,对于不同的点xi取值不一样,后续会讨论如何设置。此外设置px∣x=0因为我们关注的是两两之间的相似度。

那对于低维度下的yi,我们可以指定高斯分布为方差为根号二分之一,因此它们之间的相似度如下:

0337068ad509b2242c718cc1e0e8c5c7.png

同样,设定qi∣i=0

如果降维的效果比较好,局部特征保留完整,那么 pi∣j=qi∣j, 因此我们优化两个分布之间的距离-KL散度(Kullback-Leibler divergences),那么目标函数(cost function)如下:

de065efe56f6c2e1b4290210c08c7381.png

c4ded52b3f6cf89c71049576c9e72b26.png

首先不同的点具有不同的σi,Pi的熵(entropy)会随着σi的增加而增加。SNE使用困惑度(perplexity)的概念,用二分搜索的方式来寻找一个最佳的σ。其中困惑度指:

7d43d51d86477b2c5b3a95323f25df22.png

这里的H(Pi)是Pi的熵,即:

dcf75ff42286a7529db4e483e64f7948.png

困惑度可以解释为一个点附近的有效近邻点个数。SNE对困惑度的调整比较有鲁棒性,通常选择5-50之间,给定之后,使用二分搜索的方式寻找合适的σ

c65f37a8ee560a3367c3e28c3a188d21.png

dce7e5f8e95ab2ed375b41634cc431a9.png

在初始化中,可以用较小的σ下的高斯分布来进行初始化。为了加速优化过程和避免陷入局部最优解,梯度中需要使用一个相对较大的动量(momentum)。即参数更新中除了当前的梯度,还要引入之前的梯度累加的指数衰减项,如下:

4af2217e0bbbfb98959a223de91a3bf7.png

这里的Y(t)表示迭代t次的解,η表示学习速率,α(t)表示迭代t次的动量。

此外,在初始优化的阶段,每次迭代中可以引入一些高斯噪声,之后像模拟退火一样逐渐减小该噪声,可以用来避免陷入局部最优解。因此,SNE在选择高斯噪声,以及学习速率,什么时候开始衰减,动量选择等等超参数上,需要跑多次优化才可以。

六、t-SNE:t-distributed stochastic neighbor embedding

尽管SNE提供了很好的可视化方法,但是他很难优化,而且存在”crowding problem”(拥挤问题)。后续中,Hinton等人又提出了t-SNE的方法。与SNE不同,主要如下:

  • 使用对称版的SNE,简化梯度公式
  • 低维空间下,使用t分布替代高斯分布表达两点之间的相似度

t-SNE在低维空间下使用更重长尾分布的t分布来避免crowding问题和优化问题。

8b697c1565a5bc3a362a233768a39bf9.png

我们对比一下高斯分布和t分布(如上图,code见probability/distribution.md), t分布受异常值影响更小,拟合结果更为合理,较好的捕获了数据的整体特征。

使用了t分布之后的q变化,如下:

4fd8abbed61ea43c5895c4af3a8c855d.png

其中y~i~=x~i~/2σ

此外,t分布是无限多个高斯分布的叠加,计算上不是指数的,会方便很多。优化的梯度如下:

877e5a43312f705a1096be93df138683.png

fae1882319ff0e20d8168c6b369e9ecb.png

t-sne的有效性,也可以从上图中看到:横轴表示距离,纵轴表示相似度, 可以看到,对于较大相似度的点,t分布在低维空间中的距离需要稍小一点;而对于低相似度的点,t分布在低维空间中的距离需要更远。这恰好满足了我们的需求,即同一簇内的点(距离较近)聚合的更紧密,不同簇之间的点(距离较远)更加疏远。

总结一下,t-SNE的梯度更新有两大优势:

  • 对于不相似的点,用一个较小的距离会产生较大的梯度来让这些点排斥开来。
  • 这种排斥又不会无限大(梯度中分母),避免不相似的点距离太远。

f333580fca59f502c2ffd6c1e356d52f.png

资料来源

  1. import numpy as np
  2. def cal_pairwise_dist(x):
  3. '''计算pairwise 距离, x是matrix
  4. (a-b)^2 = a^w + b^2 - 2*a*b
  5. '''
  6. sum_x = np.sum(np.square(x), 1)
  7. dist = np.add(np.add(-2 * np.dot(x, x.T), sum_x).T, sum_x)
  8. return dist
  9. def cal_perplexity(dist, idx=0, beta=1.0):
  10. '''计算perplexity, D是距离向量,
  11. idx指dist中自己与自己距离的位置,beta是高斯分布参数
  12. 这里的perp仅计算了熵,方便计算
  13. '''
  14. prob = np.exp(-dist * beta)
  15. # 设置自身prob为0
  16. prob[idx] = 0
  17. sum_prob = np.sum(prob)
  18. perp = np.log(sum_prob) + beta * np.sum(dist * prob) / sum_prob
  19. prob /= sum_prob
  20. return perp, prob
  21. def seach_prob(x, tol=1e-5, perplexity=30.0):
  22. '''二分搜索寻找beta,并计算pairwise的prob
  23. '''
  24. # 初始化参数
  25. print("Computing pairwise distances...")
  26. (n, d) = x.shape
  27. dist = cal_pairwise_dist(x)
  28. pair_prob = np.zeros((n, n))
  29. beta = np.ones((n, 1))
  30. # 取log,方便后续计算
  31. base_perp = np.log(perplexity)
  32. for i in range(n):
  33. if i % 500 == 0:
  34. print("Computing pair_prob for point %s of %s ..." % (i, n))
  35. betamin = -np.inf
  36. betamax = np.inf
  37. perp, this_prob = cal_perplexity(dist[i], i, beta[i])
  38. # 二分搜索,寻找最佳sigma下的prob
  39. perp_diff = perp - base_perp
  40. tries = 0
  41. while np.abs(perp_diff) > tol and tries < 50:
  42. if perp_diff > 0:
  43. betamin = beta[i].copy()
  44. if betamax == np.inf or betamax == -np.inf:
  45. beta[i] = beta[i] * 2
  46. else:
  47. beta[i] = (beta[i] + betamax) / 2
  48. else:
  49. betamax = beta[i].copy()
  50. if betamin == np.inf or betamin == -np.inf:
  51. beta[i] = beta[i] / 2
  52. else:
  53. beta[i] = (beta[i] + betamin) / 2
  54. # 更新perb,prob值
  55. perp, this_prob = cal_perplexity(dist[i], i, beta[i])
  56. perp_diff = perp - base_perp
  57. tries = tries + 1
  58. # 记录prob值
  59. pair_prob[i,] = this_prob
  60. print("Mean value of sigma: ", np.mean(np.sqrt(1 / beta)))
  61. return pair_prob
  62. def pca(x, no_dims=50):
  63. ''' PCA算法
  64. 使用PCA先进行预降维
  65. '''
  66. print("Preprocessing the data using PCA...")
  67. (n, d) = x.shape
  68. x = x - np.tile(np.mean(x, 0), (n, 1))
  69. l, M = np.linalg.eig(np.dot(x.T, x))
  70. y = np.dot(x, M[:, 0:no_dims])
  71. return y
  72. def tsne(x, no_dims=2, initial_dims=50, perplexity=30.0, max_iter=1000):
  73. """Runs t-SNE on the dataset in the NxD array x
  74. to reduce its dimensionality to no_dims dimensions.
  75. The syntaxis of the function is Y = tsne.tsne(x, no_dims, perplexity),
  76. where x is an NxD NumPy array.
  77. """
  78. # Check inputs
  79. if isinstance(no_dims, float):
  80. print("Error: array x should have type float.")
  81. return -1
  82. if round(no_dims) != no_dims:
  83. print("Error: number of dimensions should be an integer.")
  84. return -1
  85. # 初始化参数和变量
  86. x = pca(x, initial_dims).real
  87. (n, d) = x.shape
  88. initial_momentum = 0.5
  89. final_momentum = 0.8
  90. eta = 500
  91. min_gain = 0.01
  92. y = np.random.randn(n, no_dims)
  93. dy = np.zeros((n, no_dims))
  94. iy = np.zeros((n, no_dims))
  95. gains = np.ones((n, no_dims))
  96. # 对称化
  97. P = seach_prob(x, 1e-5, perplexity)
  98. P = P + np.transpose(P)
  99. P = P / np.sum(P)
  100. # early exaggeration
  101. P = P * 4
  102. P = np.maximum(P, 1e-12)
  103. # Run iterations
  104. for iter in range(max_iter):
  105. # Compute pairwise affinities
  106. sum_y = np.sum(np.square(y), 1)
  107. num = 1 / (1 + np.add(np.add(-2 * np.dot(y, y.T), sum_y).T, sum_y))
  108. num[range(n), range(n)] = 0
  109. Q = num / np.sum(num)
  110. Q = np.maximum(Q, 1e-12)
  111. # Compute gradient
  112. PQ = P - Q
  113. for i in range(n):
  114. dy[i, :] = np.sum(np.tile(PQ[:, i] * num[:, i], (no_dims, 1)).T * (y[i, :] - y), 0)
  115. # Perform the update
  116. if iter < 20:
  117. momentum = initial_momentum
  118. else:
  119. momentum = final_momentum
  120. gains = (gains + 0.2) * ((dy > 0) != (iy > 0)) + (gains * 0.8) * ((dy > 0) == (iy > 0))
  121. gains[gains < min_gain] = min_gain
  122. iy = momentum * iy - eta * (gains * dy)
  123. y = y + iy
  124. y = y - np.tile(np.mean(y, 0), (n, 1))
  125. # Compute current value of cost function
  126. if (iter + 1) % 100 == 0:
  127. if iter > 100:
  128. C = np.sum(P * np.log(P / Q))
  129. else:
  130. C = np.sum(P / 4 * np.log(P / 4 / Q))
  131. print("Iteration ", (iter + 1), ": error is ", C)
  132. # Stop lying about P-values
  133. if iter == 100:
  134. P = P / 4
  135. print("finished training!")
  136. return y
  137. if __name__ == "__main__":
  138. # Run Y = tsne.tsne(X, no_dims, perplexity) to perform t-SNE on your dataset.
  139. X = np.loadtxt("mnist2500_X.txt")
  140. labels = np.loadtxt("mnist2500_labels.txt")
  141. Y = tsne(X, 2, 50, 20.0)
  142. from matplotlib import pyplot as plt
  143. plt.scatter(Y[:, 0], Y[:, 1], 20, labels)
  144. plt.show()

c82a157575b2ef93ca96da2d08f8defa.png
声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号