当前位置: 首页 > news >正文

可视化 | 数据可视化降维算法梳理

文章目录

  • 📚数据描述
    • 🐇iris
    • 🐇MNIST
  • 📚PCA
    • 🐇算法流程
    • 🐇图像描述
  • 📚Kernel-PCA
    • 🐇算法流程
    • 🐇图像描述
  • 📚MDS
    • 🐇算法流程
    • 🐇图像描述
  • 📚ISOMAP
    • 🐇算法流程
    • 🐇图像描述
  • 📚LLE
    • 🐇算法流程
    • 🐇图像描述
  • 📚LPP
    • 🐇算法流程
    • 🐇图像描述
  • 📚tSNE
    • 🐇算法流程
    • 🐇图像描述
  • 📚UMAP
    • 🐇算法流程
    • 🐇图像描述

本篇博客整理资源来源及代码来源,本篇主要是基于该资源,针对各种数据可视化降维算法流程梳理及可视化实践感知。

📚数据描述

🐇iris

  • 鸢尾花数据集收集了3种不同品种的鸢尾花(山鸢尾、变色鸢尾和维吉尼亚鸢尾)的特征数据。
  • 每个样本包含了四个特征:萼片长度(sepal length)、萼片宽度(sepal width)、花瓣长度(petal length)以及花瓣宽度(petal width)。
  • 数据集中的每个样本都被标记为相应的类别,即鸢尾花的品种。一共有150个样本,其中每个类别有50个样本。

🐇MNIST

MNIST数据集是一个非常流行的手写数字图像数据库,包含了大量的手写数字图像样本。该数据集由60,000个用于训练的样本和10,000个用于测试的样本组成,每个样本都是一个28x28像素的灰度图像,并且标有相应的真实数字标签。

  • mnist2500_labels.txt是一个文本文件,每一行包含一个样本的标签。标签的范围是0到9,分别对应数字0到9。
  • mnist2500_X.txt也是一个文本文件,每一行包含一个样本的特征向量。特征向量是将28x28的图像展平为一个长度为784的向量。
    • 每个元素表示图像中对应像素的亮度值。像素的亮度值通常用一个范围在0到1之间的数字表示,表示像素的灰度级别。
    • 在这个示例中,大部分像素的值为0,表示黑色。某些像素可能具有非零值,表示该位置的像素较亮。
    • 这样的一行数据包含了784个元素,分别对应原始图像中的每个像素点。读取这行数据,可以恢复出原始的28x28像素图像。每行数据都对应着MNIST数据集中的一个手写数字样本的特征向量,用于进行降维算法的练习和处理。

📚PCA

  • PCA(Principal Component Analysis) 主成分分析
  • PCA是一种使用最广泛的数据降维算法。PCA的主要思想是将n维特征映射到k维上,这k维是全新的正交特征也被称为主成分,是在原有n维特征的基础上重新构造出来的k维特征。
  • PCA的任务:寻找一组正交基 v i v_i vi ,使所有样本沿着 v i v_i vi进行投影后,方差最大(信息量最大)

在这里插入图片描述
在这里插入图片描述

🐇算法流程

  1. 加载数据集iris:从文件中加载数据集,将特征数据和标签数据分开存储。
  2. 数据预处理:对特征数据进行中心化处理,即减去每个特征的平均值,使数据的均值为零。
  3. 计算协方差矩阵:将中心化后的特征数据计算协方差矩阵,用来描述特征之间的相关性。
  4. 计算特征值和特征向量:对协方差矩阵进行特征值分解,得到特征值和对应的特征向量。
  5. 选择特征维度:根据特征值的大小进行排序,并选取前n个最大的特征值对应的特征向量作为新的特征维度。
  6. 数据投影:将中心化后的特征数据投影到选取的特征维度上,得到降维后的新的数据。
  7. 可视化:将降维后的数据进行可视化,用散点图展示不同类别的数据在新的特征空间中的分布。

import numpy as np
import matplotlib.pyplot as plt# 使用PCA算法进行数据降维
def pca(data, n_dim):#N:样本数#D:维度数N, D = np.shape(data)# 数据中心化处理,即将每个特征减去该特征的平均值data = data - np.mean(data, axis=0, keepdims=True)# 计算协方差矩阵C = np.dot(data.T, data) / (N - 1)  # [D,D]# 计算协方差矩阵的特征值和特征向量eig_values, eig_vector = np.linalg.eig(C)# 对特征值进行排序,选择前n_dim个维度# indexs_:特征值排序后的索引indexs_ = np.argsort(-eig_values)[:n_dim]# 选择的特征向量picked_eig_vector = eig_vector[:, indexs_]  # [D,n_dim]# 投影数据到选取的维度上data_ndim = np.dot(data, picked_eig_vector)return data_ndim, picked_eig_vector# 绘制降维后的数据图像
def draw_pic(datas, labs):plt.cla()# 获取唯一的标签值unque_labs = np.unique(labs)# 生成一系列颜色colors = [plt.cm.Spectral(each) for each in np.linspace(0, 1, len(unque_labs))]# 散点图对象p = []legends = []for i in range(len(unque_labs)):# 找到对应标签值的数据索引index = np.where(labs == unque_labs[i])# 绘制散点图pi = plt.scatter(datas[index, 0], datas[index, 1], c=[colors[i]])p.append(pi)legends.append(unque_labs[i])# 添加图例plt.legend(p, legends)plt.show()'''
加载鸢尾花数据集,数据集以逗号分隔,最后一列是标签。
使用PCA算法对特征进行降维,将数据降为2维。
将降维后的数据和标签传递给 draw_pic函数进行可视化。
draw_pic函数根据标签值生成不同颜色的散点图,并添加图例。
'''
if __name__ == "__main__":# 加载鸢尾花数据集data = np.loadtxt("iris.data", dtype="str", delimiter=',')# 从data中提取的特征数据feas = data[:, :-1]feas = np.float32(feas)# 从data中提取的标签数据labs = data[:, -1]# 使用PCA算法将数据降低为2维data_2d, picked_eig_vector = pca(feas, 2)# 绘制降维后的数据图像draw_pic(data_2d, labs)

🐇图像描述

在这里插入图片描述

  • 该散点图显示了鸢尾花数据集在降维后的两个主成分上的分布。
  • 每个样本在散点图上表示为一个点,不同类别的样本用不同颜色的散点表示
  • 通过降维,我们将原始数据的多个特征降低到了两个主成分上。图中每个点的横坐标和纵坐标分别对应了数据在降维后的第一和第二个主成分上的值
  • 通过观察散点图,我们可以看到不同品种的鸢尾花在降维后的空间中形成了不同的聚类分布。这样的可视化效果有助于我们直观地了解数据集的结构和样本之间的关系

📚Kernel-PCA

PCA降维是一种线性变换,在高维空间的样本点如果线性不可分的话,到了低维空间依旧线性不可分。为了能够让样本在低维空间线性可分引入了KPCA(Kernel PCA)。

在这里插入图片描述
在这里插入图片描述


在这里插入图片描述

🐇算法流程

输入: 数据矩阵data,降维后的维数n_dims,核函数kernel

输出: 降维后的数据矩阵data_n


  1. 计算数据矩阵的样本个数N和特征个数D。初始化核矩阵K为一个全零矩阵。

  2. 对于每一个样本对(i, j),计算核函数值K[i, j] = kernel(data[i], data[j])

  3. 将核矩阵K进行中心化操作:

    • 构造一个全1矩阵one_n,其元素值均为1/N

    • 中心化操作:K = K - one_n.dot(K) - K.dot(one_n) + one_n.dot(K).dot(one_n)

  4. 对核矩阵K进行特征值分解,得到特征值eig_values和特征向量eig_vector

  5. 对特征值进行从大到小排序,并选择前n_dims个索引值:

    • idx = np.argsort(-eig_values)[:n_dims]
  6. 提取较大的特征值和对应的特征向量:

    • eigval = eig_values[idx]

    • eigvector = eig_vector[:, idx]

  7. 对特征值进行正则化处理,即将特征值开方:

    • eigval = eigval ** (1 / 2)
  8. 构建投影矩阵u,将特征向量除以正则化后的特征值:

    • u = eigvector / eigval.reshape(-1, n_dims)
  9. 将中心化后的核矩阵K与投影矩阵u相乘,得到降维后的数据矩阵data_ndata_n = np.dot(K, u)

  10. 返回降维后的数据矩阵data_n


import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris# a越大,输出结果的斜率越陡,非线性程度越高;r越大,输出结果偏移的程度越大。
# 定义sigmoid核函数
def sigmoid(x1, x2, a=0.25, r=1):x = np.dot(x1, x2)return np.tanh(a * x + r)# 定义线性核函数
# 参数a和d控制了输入向量之间的点积运算结果的缩放和幂指数。a越大,输出结果的线性增长率越高;d越大,输出结果的幂指数增加,非线性程度越高。
# 参数c控制了输出结果的平移。增大c会使输出结果整体平移。
def linear(x1, x2, a=1, c=0, d=1):x = np.dot(x1, x2)x = np.power((a * x + c), d)return x# 定义径向基函数(RBF)核函数
# gamma参数控制了数据在降维过程中的映射范围
# 当gamma较小时,高维空间中的样本点彼此之间的相似度较高,即样本点之间的距离较远时,核函数的取值接近于0。
# 当gamma较大时,样本点之间的差异性被放大,即样本点之间的距离大的时候,核函数的取值接近于1。
def rbf(x1, x2, gamma=7):x = np.dot((x1 - x2), (x1 - x2))x = np.exp(-gamma * x)return xdef kpca(data, n_dims=2, kernel=rbf):N, D = np.shape(data)K = np.zeros([N, N])# 利用核函数计算Kfor i in range(N):for j in range(N):K[i, j] = kernel(data[i], data[j])# 对K进行中心化one_n = np.ones((N, N)) / NK = K - one_n.dot(K) - K.dot(one_n) + one_n.dot(K).dot(one_n)# 计算特征值和特征向量eig_values, eig_vector = np.linalg.eig(K)idx = np.argsort(-eig_values)[:n_dims]  # 从大到小排序# 选取较大的特征值eigval = eig_values[idx]eigvector = eig_vector[:, idx]  # [N,d]# 进行正则化eigval = eigval ** (1 / 2)u = eigvector / eigval.reshape(-1, n_dims)  # u [N,d]# 进行降维data_n = np.dot(K, u)return data_ndef draw_pic(datas, labs):plt.cla()unque_labs = np.unique(labs)colors = [plt.cm.Spectral(each)for each in np.linspace(0, 1, len(unque_labs))]p = []legends = []for i in range(len(unque_labs)):index = np.where(labs == unque_labs[i])pi = plt.scatter(datas[index, 0], datas[index, 1], c=[colors[i]])p.append(pi)legends.append(unque_labs[i])plt.legend(p, legends)plt.show()if __name__ == "__main__":iris = load_iris()data = iris.datalabs = iris.targetdata_2d = kpca(data, n_dims=2, kernel=rbf)draw_pic(data_2d, labs)

🐇图像描述

在这里插入图片描述

  • 使用 kpca函数对数据进行降维操作,将数据降维为2维。这里使用了RBF(径向基函数)作为核函数。 调用 draw_pic函数,将降维后的数据绘制出来。同样地,不同标签的样本用不同颜色表示。

📚MDS

MDS(multidimensional scaling)多维标度分析

在这里插入图片描述

在这里插入图片描述

🐇算法流程

  1. 计算样本点之间的两两距离cal_pairwise_dist
    • 获取样本数量N和特征维度D,然后初始化一个N×N的全零矩阵dist来存储样本点之间的距离。
    • 使用双重循环遍历所有样本点,并使用np.abs函数计算两个样本点特征向量的绝对差值,然后使用np.sum函数对差值求和,得到两个样本点之间的距离,最后将距离值存入dist矩阵中。
    • 最后,返回任意两个点之间的距离矩阵dist
  2. MDS降维my_mds
    • 获取样本数量n,然后对距离矩阵进行处理,将小于0的距离值设为0,然后将距离值平方

    • 根据公式计算T1,T2,T3和B矩阵。
      在这里插入图片描述

      • 距离矩阵中所有距离值之和除以样本数量的平方,T1 = np.ones((n, n)) * np.sum(dist) / n ** 2
      • 对应样本与其他所有样本之间的距离均值,T2 = np.sum(dist, axis=1, keepdims=True) / n
      • 其他所有样本与对应样本之间的距离均值,T3 = np.sum(dist, axis=0, keepdims=True) / n
      • B = -(T1 - T2 - T3 + dist) / 2
    • 使用np.linalg.eig函数求解B矩阵的特征值和特征向量。然后对特征值进行降序排序,并选取前n_dims个特征值和对应的特征向量。将选取的特征向量乘以特征值的平方根得到降维后的数据。

  3. 绘制降维后的数据可视化图像draw_pic
    • 获取唯一的标签值,并为每个标签值生成一个颜色。
    • 遍历每个标签值,在降维后的数据中找到对应标签的索引,将这些点按照对应的颜色绘制在图上,同时记录下这些点的句柄和标签值。
    • 使用plt.legend函数设置图例,并调用plt.show函数显示图像。

import numpy as np
import matplotlib.pyplot as plt# 计算两两样本点之间的距离
# x: 样本特征向量 [N,D]
def cal_pairwise_dist(x):N, D = np.shape(x)# 初始化距离矩阵dist = np.zeros([N, N])for i in range(N):for j in range(N):dist[i, j] = np.sum(np.abs(x[i] - x[j]))  # 计算两个样本点之间的距离,使用绝对值和求和# 返回任意两个点之间的距离矩阵return dist# 执行MDS算法,实现降维
# dist: 距离矩阵,N*N
# n_dims: 降维后的维度
# 返回降维后的数据
def my_mds(dist, n_dims):n, n = np.shape(dist)dist[dist < 0] = 0dist = dist ** 2T1 = np.ones((n, n)) * np.sum(dist) / n ** 2  # 构造T1矩阵T2 = np.sum(dist, axis=1, keepdims=True) / n  # 计算T2矩阵T3 = np.sum(dist, axis=0, keepdims=True) / n  # 计算T3矩阵B = -(T1 - T2 - T3 + dist) / 2  # 计算B矩阵eig_val, eig_vector = np.linalg.eig(B)  # 求解B矩阵的特征值和特征向量index_ = np.argsort(-eig_val)[:n_dims]  # 对特征值进行降序排序,并选取前n_dims个特征值的索引picked_eig_val = eig_val[index_].real  # 选取特征值,并转为实数picked_eig_vector = eig_vector[:, index_]  # 选取特征向量return picked_eig_vector * picked_eig_val ** (0.5)  # 返回降维后的数据# 绘制降维后的数据可视化图像
def draw_pic(datas, labs):plt.cla()unque_labs = np.unique(labs)  # 获取唯一的标签值colors = [plt.cm.Spectral(each)for each in np.linspace(0, 1, len(unque_labs))]  # 生成颜色数组,用于不同的标签类型p = []legends = []for i in range(len(unque_labs)):index = np.where(labs == unque_labs[i])  # 找到对应标签的索引pi = plt.scatter(datas[index, 0], datas[index, 1], c=[colors[i]])  # 根据索引绘制散点图,并使用对应的颜色p.append(pi)  # 记录绘制的散点图句柄legends.append(unque_labs[i])  # 记录标签plt.legend(p, legends)  # 设置图例plt.show()  # 显示图像if __name__ == "__main__":# 加载数据data = np.loadtxt("iris.data", dtype="str", delimiter=',')feas = data[:, :-1]  # 特征部分feas = np.float32(feas)  # 将特征部分转换为浮点型labs = data[:, -1]  # 标签部分# 计算两两样本之间的距离矩阵dist = cal_pairwise_dist(feas)# 进行降维data_2d = my_mds(dist, 2)  # 降到2维# 绘制可视化图像draw_pic(data_2d, labs)

🐇图像描述

在这里插入图片描述

  • 散点图的每个点表示一个样本,其中x轴和y轴分别表示降维后的第一维和第二维。不同种类的鸢尾花样本被用不同的颜色进行标记。
  • 不同种类的鸢尾花样本在二维图像中可能会呈现出明显的聚类分布。
  • 不同种类的鸢尾花样本在二维图像中可能会有一定的重叠区域。虽然MDS算法降维后尽可能保留了样本间的距离关系,但是仍然存在维度丢失的情况,因此不同种类的鸢尾花样本可能在某些区域发生重叠。
  • 样本点之间可能存在一些异常值或离群点。这些点可能是由于数据的噪声或采样误差引起的。它们在降维后的图像中可能位于孤立的位置,远离其他样本点。

📚ISOMAP

ISOMAP等距特征映射

在这里插入图片描述
在这里插入图片描述


在这里插入图片描述

🐇算法流程

  1. 在 main 函数中,首先使用 make_s_curve 函数生成一个 S 曲线形状的数据集 X 和对应的标签 Y,并调用 scatter_3d 函数将其可视化。

  2. 调用 cal_pairwise_dist 函数计算样本之间的距离矩阵 dist。

  3. 调用 my_mds 函数对距离矩阵 dist 进行多维尺度变换,将高维的数据降到2维,并使用散点图绘制降维后的结果。

  4. 使用 my_Isomap 函数对距离矩阵 dist 进行 ISOMAP 算法,将数据降到2维,并使用散点图绘制降维后的结果。

    def my_Isomap(D, n=2, n_neighbors=30):D_floyd = floyd(D, n_neighbors)data_n = my_mds(D_floyd, n_dims=n)return data_n
    
  5. 使用 sklearn 中的 Isomap 函数对数据进行 ISOMAP 降维,同样绘制降维后的结果。

  6. 展示绘制的三张图。


import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_s_curve
from sklearn.manifold import Isomap
from tqdm import tqdm# x 维度 [N,D]
def cal_pairwise_dist(x):N, D = np.shape(x)dist = np.zeros([N, N])for i in range(N):for j in range(N):# dist[i,j] = np.dot((x[i]-x[j]),(x[i]-x[j]).T)dist[i, j] = np.sqrt(np.dot((x[i] - x[j]), (x[i] - x[j]).T))# dist[i,j] = np.sum(np.abs(x[i]-x[j]))# 返回任意两个点之间距离return dist# 构建最短路径图
def floyd(D, n_neighbors=15):Max = np.max(D) * 1000n1, n2 = D.shapek = n_neighborsD1 = np.ones((n1, n1)) * MaxD_arg = np.argsort(D, axis=1)for i in range(n1):D1[i, D_arg[i, 0:k + 1]] = D[i, D_arg[i, 0:k + 1]]for k in tqdm(range(n1)):for i in range(n1):for j in range(n1):if D1[i, k] + D1[k, j] < D1[i, j]:D1[i, j] = D1[i, k] + D1[k, j]return D1def my_mds(dist, n_dims):# dist (n_samples, n_samples)dist = dist ** 2n = dist.shape[0]T1 = np.ones((n, n)) * np.sum(dist) / n ** 2T2 = np.sum(dist, axis=1) / nT3 = np.sum(dist, axis=0) / nB = -(T1 - T2 - T3 + dist) / 2eig_val, eig_vector = np.linalg.eig(B)index_ = np.argsort(-eig_val)[:n_dims]picked_eig_val = eig_val[index_].realpicked_eig_vector = eig_vector[:, index_]return picked_eig_vector * picked_eig_val ** (0.5)def my_Isomap(D, n=2, n_neighbors=30):D_floyd = floyd(D, n_neighbors)data_n = my_mds(D_floyd, n_dims=n)return data_ndef scatter_3d(X, y):fig = plt.figure()ax = plt.axes(projection='3d')ax.scatter(X[:, 0], X[:, 1], X[:, 2], c=y, cmap=plt.cm.hot)ax.view_init(10, -70)ax.set_xlabel("$x_1$", fontsize=18)ax.set_ylabel("$x_2$", fontsize=18)ax.set_zlabel("$x_3$", fontsize=18)plt.show(block=False)if __name__ == "__main__":X, Y = make_s_curve(n_samples=500,noise=0.1,random_state=42)scatter_3d(X, Y)# 计算距离dist = cal_pairwise_dist(X)# MDS 降维data_MDS = my_mds(dist, 2)plt.figure()plt.title("my_MSD")plt.scatter(data_MDS[:, 0], data_MDS[:, 1], c=Y)plt.show(block=False)# ISOMAP 降维data_ISOMAP = my_Isomap(dist, 2, 10)plt.figure()plt.title("my_Isomap")plt.scatter(data_ISOMAP[:, 0], data_ISOMAP[:, 1], c=Y)plt.show(block=False)data_ISOMAP2 = Isomap(n_neighbors=10, n_components=2).fit_transform(X)plt.figure()plt.title("sk_Isomap")plt.scatter(data_ISOMAP2[:, 0], data_ISOMAP2[:, 1], c=Y)plt.show(block=False)plt.show()

🐇图像描述

  1. 三维散点图:展示了原始数据集 X 的三个维度的分布情况,不同类别的样本使用不同的颜色进行表示。
    在这里插入图片描述

  2. my_MSD:使用 MDS 算法将数据降到2维后的结果,可以看到数据在2维空间中的分布情况。
    在这里插入图片描述

  3. my_Isomap:使用 ISOMAP 算法将数据降到2维后的结果,同样展示了数据在2维空间中的分布情况。与 my_MSD 图进行对比可以看出两种降维方法的差异。
    在这里插入图片描述

  4. sk_Isomap:使用 sklearn 中的 Isomap 函数对数据进行降维后的结果,同样展示了数据在2维空间中的分布情况,与 my_Isomap 进行对比可以看出两种实现的一致性。
    在这里插入图片描述

📚LLE

LLE(Locally Linear Embedding,局部线性嵌入)

在这里插入图片描述
在这里插入图片描述


在这里插入图片描述

🐇算法流程

  1. 在 main 函数中,首先使用 make_swiss_roll 函数生成一个瑞士卷形状的数据集 X 和对应的标签 Y,并调用 scatter_3d 函数将其可视化。
  2. 使用LLE函数对数据集 X 进行局部线性嵌入降维,将高维的数据降到2维,并使用散点图绘制降维后的结果。
    • 接收输入参数
      • data: 高维数据,格式为 [N, D],其中 N 是样本数量,D 是特征维度。
      • n_dims: 降维后的目标维度。
      • n_neighbors: K 近邻的数目。
    • 根据输入数据计算临近点索引
      • 调用 get_n_neighbors 函数,传入数据 data 和 n_neighbors,获取每个样本点的 n_neighbors 个临近点的位置索引。
    • 计算重构权重 w
      • 初始化一个全零矩阵 w,形状为 [N, n_neighbors]。
      • 遍历样本集中的每个样本 i:
        • 获取样本 i 的临近点数据 X_k,形状为 [n_neighbors, D],和样本 i 自身数据 X_i,形状为 [1, D]。
        • 构建矩阵 I,形状为 [n_neighbors, 1],元素都为 1。
        • 计算重构误差矩阵 S i = ( I ∗ X i − X k ) ∗ ( I ∗ X i − X k ) . T Si = (I * X_i - X_k) * (I * X_i - X_k).T Si=(IXiXk)(IXiXk).T
        • 为了避免对角线元素过小,加上一个非零的扰动项 t o l ∗ n p . t r a c e ( S i ) tol * np.trace(Si) tolnp.trace(Si)
        • 使用伪逆函数 np.linalg.pinv 计算 Si 的伪逆矩阵 Si_inv。
        • 计算重构权重 w [ i ] = ( I . T ∗ S i i n v ) / ( I . T ∗ S i i n v ∗ I ) w[i] = (I.T * Si_inv) / (I.T * Si_inv * I) w[i]=(I.TSiinv)/(I.TSiinvI)
    • 构建权重矩阵 W
      • 初始化一个全零矩阵 W,形状为 [N, N]。
      • 遍历样本集中的每个样本 i,将 w[i] 中的权重值赋给 W[i] 对应的临近点索引。
    • 构建对称矩阵 C
      • 计算矩阵 I_N = np.eye(N),形状为 [N, N]。
      • 计算差异矩阵 C = ( I N − W ) . T ∗ ( I N − W ) C = (I_N - W).T * (I_N - W) C=(INW).T(INW)
    • 进行特征值分解
      • 使用 np.linalg.eig 函数计算差异矩阵 C 的特征值 eig_val 和特征向量 eig_vector。
    • 选择特征值和特征向量
      • 对特征值 eig_val 进行排序,得到升序排列的索引 index_。
      • 选择索引为 1 到 n_dims+1 的特征向量 eig_vector[:, index_],作为降维后的数据。
    • 返回降维后的数据 y
  3. 使用 sklearn 中的LLE函数对数据进行 LLE 降维,同样绘制降维后的结果。
  4. 可视化。

import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_swiss_roll
from sklearn.manifold import LocallyLinearEmbedding# x 维度 [N,D]
def cal_pairwise_dist(x):N, D = np.shape(x)dist = np.zeros([N, N])for i in range(N):for j in range(N):dist[i, j] = np.sqrt(np.dot((x[i] - x[j]), (x[i] - x[j]).T))# 返回任意两个点之间距离return dist# 获取每个样本点的 n_neighbors个临近点的位置
def get_n_neighbors(data, n_neighbors=10):dist = cal_pairwise_dist(data)dist[dist < 0] = 0N = dist.shape[0]Index = np.argsort(dist, axis=1)[:, 1:n_neighbors + 1]return Index# data : N,D
def lle(data, n_dims=2, n_neighbors=10):N, D = np.shape(data)if n_neighbors > D:tol = 1e-3else:tol = 0# 获取 n_neighbors个临界点的位置Index_NN = get_n_neighbors(data, n_neighbors)# 计算重构权重w = np.zeros([N, n_neighbors])for i in range(N):X_k = data[Index_NN[i]]  # [k,D]X_i = [data[i]]  # [1,D]I = np.ones([n_neighbors, 1])Si = np.dot((np.dot(I, X_i) - X_k), (np.dot(I, X_i) - X_k).T)# 为防止对角线元素过小Si = Si + np.eye(n_neighbors) * tol * np.trace(Si)Si_inv = np.linalg.pinv(Si)w[i] = np.dot(I.T, Si_inv) / (np.dot(np.dot(I.T, Si_inv), I))# 计算 WW = np.zeros([N, N])for i in range(N):W[i, Index_NN[i]] = w[i]I_N = np.eye(N)C = np.dot((I_N - W).T, (I_N - W))# 进行特征值的分解eig_val, eig_vector = np.linalg.eig(C)index_ = np.argsort(eig_val)[1:n_dims + 1]y = eig_vector[:, index_]return ydef scatter_3d(X, y):fig = plt.figure()ax = plt.axes(projection='3d')ax.scatter(X[:, 0], X[:, 1], X[:, 2], c=y, cmap=plt.cm.hot)ax.view_init(10, -70)ax.set_xlabel("$x_1$", fontsize=18)ax.set_ylabel("$x_2$", fontsize=18)ax.set_zlabel("$x_3$", fontsize=18)plt.show(block=False)if __name__ == "__main__":X, Y = make_swiss_roll(n_samples=500)scatter_3d(X, Y)data_2d = lle(X, n_dims=2, n_neighbors=12)print(data_2d.shape)plt.figure()plt.title("my_LLE")plt.scatter(data_2d[:, 0], data_2d[:, 1], c=Y, cmap=plt.cm.hot)plt.show(block=False)data_2d_sk = LocallyLinearEmbedding(n_components=2, n_neighbors=12).fit_transform(X)plt.figure()plt.title("my_LLE_sk")plt.scatter(data_2d[:, 0], data_2d[:, 1], c=Y, cmap=plt.cm.hot)plt.show()

🐇图像描述

  1. 三维散点图:展示了原始数据集 X 的三个维度的分布情况,不同类别的样本使用不同的颜色进行表示。
    在这里插入图片描述

  2. my_LLE:使用自定义的 LLE 算法将数据降到2维后的结果,可以看到数据在2维空间中的分布情况。
    在这里插入图片描述

  3. my_LLE_sk:使用 sklearn 中的 LLE 函数对数据进行降维后的结果,同样展示了数据在2维空间中的分布情况。与 my_LLE 图进行对比可以看出两种实现的一致性。
    在这里插入图片描述

📚LPP

  • LPP(Locality Preserving Projections)局部保留投影算法
  • LPP 可以被看做是PCA(Principal component analysis)的替代,同时与LE (Laplacian eigenmaps)及 LLE(Locally Linear Embedding)有着非常相近的性质。
  • 计算流程与PCA类似,PCA只考虑数据本身的分布,LPP还考虑了样本点间的相对位置关系

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


在这里插入图片描述

🐇算法流程

  1. 调用 cal_pairwise_dist 函数计算样本之间的欧氏距离矩阵 dist。
  2. 计算 dist 的最大值 max_dist
  3. 分别调用LLP函数和 PCA 函数对数据 X 进行降维。LLP:
    • 接收输入参数
      • X: 高维数据,格式为 [N, D],其中 N 是样本数量,D 是特征维度。
      • n_dims: 降维后的目标维度。
      • n_neighbors: K 近邻的数目。
      • t: 权重计算的参数。
    • 计算 RBF 距离矩阵
      • 调用 cal_rbf_dist 函数,传入数据 X、n_neighbors 和 t,计算 RBF 距离矩阵 W。
      • 根据欧氏距离计算出的样本间距离矩阵 dist,通过 RBF 核函数计算得到 W。
    • 构建度矩阵 D
      • 初始化一个全零矩阵 D,形状与 W 相同。
      • 对于每个样本 i,计算 W[i] 中非零元素的和,并将结果保存在对应的 D[i, i] 位置上。
    • 构建拉普拉斯矩阵 L
      • 初始化一个全零矩阵 L,形状与 W 相同。
      • 遍历样本集中的每个样本 i,计算$ L[i, j] = D[i, i] - W[i, j]$,其中 j 是样本 i 的 K 近邻。
    • 计算矩阵乘积 XDXT 和 XLXT
      • X . T X.T X.T 是 X 的转置矩阵。
      • X D X T = X . T ∗ D ∗ X XDXT = X.T * D * X XDXT=X.TDX,计算出的结果形状为 [D, D]。
      • X L X T = X . T ∗ L ∗ X XLXT = X.T * L * X XLXT=X.TLX,计算出的结果形状为 [D, D]。
    • 对矩阵 XLXT 进行特征值分解
      • 使用 np.linalg.pinv 函数计算矩阵 XDXT 的伪逆。
      • 将 XDXT 的伪逆与 XLXT 相乘,并利用 np.linalg.eig 函数计算得到特征值 eig_val 和特征向量 eig_vec。
    • 对特征值排序和选择
      • 对特征值 eig_val 进行绝对值排序,得到排序后的索引 sort_index_。
      • 根据排序后的索引 sort_index_,选择前 n_dims 个非零特征值,即从下标 j 开始取值。
    • 获取降维后的数据 Y
      • 根据所选的特征值索引 sort_index_,获取相应的特征向量 eig_vec。
      • 将特征向量 eig_vec 与原始数据 X 相乘,得到降维后的数据 Y,其形状为 [N, n_dims],其中 N 是样本数量。
  4. 使用不同的子图显示 LPP 降维结果和 PCA 降维结果的散点图,并根据原始的目标变量 Y 进行着色。

import numpy as np
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
from sklearn.datasets import load_iris# x 维度 [N,D]
def cal_pairwise_dist(X):N, D = np.shape(X)tile_xi = np.tile(np.expand_dims(X, 1), [1, N, 1])tile_xj = np.tile(np.expand_dims(X, axis=0), [N, 1, 1])dist = np.sum((tile_xi - tile_xj) ** 2, axis=-1)# 返回任意两个点之间距离return distdef rbf(dist, t=1.0):'''rbf kernel function'''return np.exp(-(dist / t))def cal_rbf_dist(data, n_neighbors=10, t=1):dist = cal_pairwise_dist(data)dist[dist < 0] = 0N = dist.shape[0]rbf_dist = rbf(dist, t)W = np.zeros([N, N])for i in range(N):index_ = np.argsort(dist[i])[1:1 + n_neighbors]W[i, index_] = rbf_dist[i, index_]W[index_, i] = rbf_dist[index_, i]return W# X 输入高维数据 格式 [N,D]
# n_neighbors K近邻的数目
# t 权重计算的参数
def lpp(X, n_dims=2, n_neighbors=30, t=1.0):N = X.shape[0]W = cal_rbf_dist(X, n_neighbors, t)D = np.zeros_like(W)for i in range(N):D[i, i] = np.sum(W[i])L = D - WXDXT = np.dot(np.dot(X.T, D), X)XLXT = np.dot(np.dot(X.T, L), X)eig_val, eig_vec = np.linalg.eig(np.dot(np.linalg.pinv(XDXT), XLXT))sort_index_ = np.argsort(np.abs(eig_val))eig_val = eig_val[sort_index_]print("eig_val[:10]", eig_val[:10])j = 0while eig_val[j] < 1e-6:j += 1print("j: ", j)sort_index_ = sort_index_[j:j + n_dims]eig_val_picked = eig_val[j:j + n_dims]print(eig_val_picked)A = eig_vec[:, sort_index_]Y = np.dot(X, A)return Ydef scatter_3d(X, y):fig = plt.figure()ax = plt.axes(projection='3d')ax.scatter(X[:, 0], X[:, 1], X[:, 2], c=y, cmap=plt.cm.hot)ax.view_init(10, -70)ax.set_xlabel("$x_1$", fontsize=18)ax.set_ylabel("$x_2$", fontsize=18)ax.set_zlabel("$x_3$", fontsize=18)plt.show(block=False)if __name__ == '__main__':X = load_iris().dataY = load_iris().targetn_neighbors = 5dist = cal_pairwise_dist(X)max_dist = np.max(dist)data_2d_LPP = lpp(X, n_neighbors=n_neighbors, t=0.01 * max_dist)data_2d_PCA = PCA(n_components=2).fit_transform(X)plt.figure(figsize=(12, 6))plt.subplot(121)plt.title("LPP")plt.scatter(data_2d_LPP[:, 0], data_2d_LPP[:, 1], c=Y)plt.subplot(122)plt.title("PCA")plt.scatter(data_2d_PCA[:, 0], data_2d_PCA[:, 1], c=Y)plt.show()

🐇图像描述

LPP 降维:使用 LPP 算法将数据降到2维后的结果,散点图展示了数据在2维空间中的分布情况。

在这里插入图片描述

📚tSNE

  • t-SNE(t-Distributed Stochastic Neighbor Embedding)
  • t-sne是一种非常常用的数据降维数据可视化)基本原理:
    • 高维空间构建一个概率分布拟合高维样本点间的相对位置关系
    • 低维空间,也构建一个概率分布,拟合低维样本点之间的位置关系
    • 通过学习,调整低维数据点,令两个分布接近

在这里插入图片描述


在这里插入图片描述
在这里插入图片描述


在这里插入图片描述


在这里插入图片描述

🐇算法流程

  1. 计算数据集中任意两点之间的距离cal_pairwise_dist

    • 输入是一个 NxD 维度的矩阵,其中 N 是数据点的数量,D 是每个数据点的维度。
    • 它首先对 X 计算平方和,然后根据欧式距离公式计算距离。
  2. 计算联合概率 P i j P_{ij} Pij及其对应的熵calc_P_and_entropy

    • 根据给定的 beta 值和距离矩阵 D 来计算联合概率矩阵 P 和对应的熵
    • sigma (可以由beta导出)是高斯核宽度,用于模糊相似度图。
    • 函数返回归一化后的 P 矩阵和对数熵。
  3. 二分搜索的方法寻找最优的σbinary_search

    • 对每个数据点执行二分搜索以找到最优的 beta 值。
    • 目标是找到最优的 beta 值,使得给定点的高斯相似度的对数熵接近预设 perplexity 的对数值。
  4. 计算高维数据的联合概率p_joint

    • 计算高维数据的联合概率矩阵 P。
    • 首先计算所有点对的距离,然后对每个点运用二分搜索找到最优的 sigma,最后计算联合概率。
  5. 计算低维数据的联合概率q_tsne

    • 根据嵌入在低维空间的点计算低维的联合概率矩阵 Q 和相应的元素num。
  6. **t-SNE算法的主体部分。**首先计算高维数据的联合概率,然后进行迭代训练,更新低维数据,最后返回低维数据:estimate_tsen

    • 计算高维联合概率 P,然后通过迭代优化降维嵌入 Y(低维数据 [N,d])。
    • 在每一轮,它根据当前的Y 计算低维联合概率 Q,
    • 然后用基于梯度下降的方法更新 Y。
  7. 可视化draw_pic


import numpy as np
import matplotlib.pyplot as plt
from PCA import pca# 计算任意两点之间的欧式距离 ||x_i-x_j||^2
# X 维度 [N,D],其中 N 是数据点的数量,D 是每个数据点的维度
# 首先对 X 计算平方和,然后根据欧式距离公式计算距离。
def cal_pairwise_dist(X):sum_X = np.sum(np.square(X), 1)D = np.add(np.add(-2 * np.dot(X, X.T), sum_X).T, sum_X)# 返回任意两个点之间距离return D# 计算P_ij 以及 log松弛度
def calc_P_and_entropy(D, beta=1.0):P = np.exp(-D.copy() * beta)sumP = np.sum(P)# 计算熵log_entropy = np.log(sumP) + beta * np.sum(D * P) / sumPP = P / sumPreturn P, log_entropy# 二值搜索寻找最优的 sigma
def binary_search(D, init_beta, logU, tol=1e-5, max_iter=50):beta_max = np.infbeta_min = -np.infbeta = init_betaP, log_entropy = calc_P_and_entropy(D, beta)diff_log_entropy = log_entropy - logUm_iter = 0while np.abs(diff_log_entropy) > tol and m_iter < max_iter:# 交叉熵比期望值大,增大betaif diff_log_entropy > 0:beta_min = betaif beta_max == np.inf or beta_max == -np.inf:beta = beta * 2else:beta = (beta + beta_max) / 2.# 交叉熵比期望值小, 减少betaelse:beta_max = betaif beta_min == -np.inf or beta_min == -np.inf:beta = beta / 2else:beta = (beta + beta_min) / 2.# 重新计算P, log_entropy = calc_P_and_entropy(D, beta)diff_log_entropy = log_entropy - logUm_iter = m_iter + 1# 返回最优的 beta 以及所对应的 Preturn P, beta# 给定一组数据 datas :[N,D]
# 计算联合概率 P_ij : [N,N]
def p_joint(datas, target_perplexity):N, D = np.shape(datas)# 计算两两之间的距离distances = cal_pairwise_dist(datas)# beta = 1/(2*sigma^2)beta = np.ones([N, 1])  logU = np.log(target_perplexity)p_conditional = np.zeros([N, N])# 对每个样本点搜索最优的sigma(beta) 并计算对应的Pfor i in range(N):if i % 500 == 0:print("Compute joint P for %d points" % (i))# 删除 i -i 点Di = np.delete(distances[i, :], i)# 进行二值搜索,寻找 beta# 使 log_entropy 最接近 logUP, beta[i] = binary_search(Di, beta[i], logU)# 在ii的位置插0p_conditional[i] = np.insert(P, i, 0)# 计算联合概率P_join = p_conditional + p_conditional.TP_join = P_join / np.sum(P_join)print("Mean value of sigma: %f" % np.mean(np.sqrt(1 / beta)))return P_join# Y : 低维数据 [N,d]
# 根据Y,计算低维的联合概率 q_ij
def q_tsne(Y):N = np.shape(Y)[0]sum_Y = np.sum(np.square(Y), 1)num = -2. * np.dot(Y, Y.T)num = 1. / (1. + np.add(np.add(num, sum_Y).T, sum_Y))num[range(N), range(N)] = 0.Q = num / np.sum(num)Q = np.maximum(Q, 1e-12)return Q, num# datas 输入高维数据 [N,D]
# labs 高维数据的标签[N,1]
# dim 降维的维度 d
# plot 绘图
def estimate_tsen(datas, labs, dim, target_perplexity, plot=False):N, D = np.shape(datas)# 随机初始化低维数据YY = np.random.randn(N, dim)# 计算高维数据的联合概率print("Compute P_joint")P = p_joint(datas, target_perplexity)# 开始若干轮对 P 进行放大P = P * 4.P = np.maximum(P, 1e-12)# 开始进行迭代训练# 训练相关参数max_iter = 1500initial_momentum = 0.5final_momentum = 0.8eta = 500  # 学习率,通常较大min_gain = 0.01dY = np.zeros([N, dim])  # 梯度iY = np.zeros([N, dim])  # Y的变化gains = np.ones([N, dim])for m_iter in range(max_iter):# 计算 QQ, num = q_tsne(Y)# 计算梯度PQ = P - Qfor i in range(N):dY[i, :] = np.sum(np.tile(PQ[:, i] * num[:, i], (dim, 1)).T * (Y[i, :] - Y), 0)# Perform the updateif m_iter < 20:momentum = initial_momentumelse:momentum = final_momentumgains = (gains + 0.2) * ((dY > 0.) != (iY > 0.)) + \(gains * 0.8) * ((dY > 0.) == (iY > 0.))gains[gains < min_gain] = min_gainiY = momentum * iY - eta * (gains * dY)Y = Y + iY# Y 去中心化,点的偏移对点和点之间的距离无影响Y = Y - np.tile(np.mean(Y, 0), (N, 1))# Compute current value of cost functionif (m_iter + 1) % 10 == 0:C = np.sum(P * np.log(P / Q))print("Iteration %d: loss is %f" % (m_iter + 1, C))# 停止放大Pif m_iter == 100:P = P / 4.if plot and m_iter % 100 == 0:print("Draw Map")draw_pic(Y, labs, name="%d.jpg" % (m_iter))return Ydef draw_pic(datas, labs, name='1.jpg'):plt.cla()unque_labs = np.unique(labs)colors = [plt.cm.Spectral(each)for each in np.linspace(0, 1, len(unque_labs))]p = []legends = []for i in range(len(unque_labs)):index = np.where(labs == unque_labs[i])pi = plt.scatter(datas[index, 0], datas[index, 1], c=[colors[i]])p.append(pi)legends.append(unque_labs[i])plt.legend(p, legends)plt.savefig(name)if __name__ == "__main__":mnist_datas = np.loadtxt("mnist2500_X.txt")mnist_labs = np.loadtxt("mnist2500_labels.txt")print("first reduce by PCA")datas, _ = pca(mnist_datas, 30)X = datas.realY = estimate_tsen(X, mnist_labs, 2, 30, plot=True)draw_pic(Y, mnist_labs, name="final.jpg")

🐇图像描述

迭代过程

在这里插入图片描述

在这里插入图片描述

  • 最后生成的 “final.jpg” 图像中,横纵坐标是原始高维度数据通过 t-SNE 算法降维到二维之后的坐标值,也就是每个数据点在二维空间中的位置。这两个坐标值是通过 t-SNE 算法根据数据点在高维空间中的相互布局关系计算得出的。
  • 至于散点的颜色,每种颜色代表了一个特定的类别。

📚UMAP

  • UMAP(Uniform Manifold Approximation and Projection)均匀流形逼近与投影
  • UMAP 是新近提出的一种新型的数据降维(数据可视化)方法。其算法原理以及降维效果与t-sne类似,但有以下改进:
    • 比t-sne 可以得到更好的数据聚拢效果,能能够表达更好的局部结构。
    • 运算效率以及运算速度比t-sne好的多,可以适用于大规模数据降维。
    • 可以实现任意维度的降维。

在这里插入图片描述


在这里插入图片描述

在这里插入图片描述

在这里插入图片描述


在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

🐇算法流程

  • 相关函数:

    • 计算样本点两两之间距离的函数cal_pairwise_dist()
    • 获取每个样本点n_neighbors个临近点位置和距离的函数get_n_neighbors()
    • 计算样本点参数 sigmas 和 rhos 的函数compute_sigmas_and_rhos()
    • 计算样本点之间连接强度的函数compute_membership_strengths()
    • 获取图输入的函数get_graph_Inputs()
    • 使用谱分析法进行初始化的函数init_embedding_spectral()
    • 计算每条边多少个 epoch 更新一次的函数make_epochs_per_sample()
    • 梯度裁剪函数clip()
    • 训练一轮的函数train_one_epoch()
    • 通过训练获取 embedding 的函数train_embedding()
    • 获取 embedding 的函数get_embedding()
    • 找到参数 a 和 b 的函数find_ab_params()
    • UMAP 算法的主要实现函数UAMP(),依次调用上述定义的函数。
    • 绘制结果图像的函数draw_pic()
  • 整个程序中,UMAP 算法的主要思路如下:

    • 从高维数据中计算样本点之间的距离和连接关系。

    • 计算 sigmas 和 rhos 参数。

    • 通过谱分析方法初始化低维嵌入。

    • 训练一轮微调嵌入,包括更新正样本和负样本。

    • 通过多轮训练,利用梯度下降法优化降维结果。

    • 最后用 matplotlib 库绘制降维结果并展示。

  • main函数

    • 首先读取手写数字识别数据集MNIST的样本数据(“mnist2500_X.txt”)和标签数据(“mnist2500_labels.txt”),并打印出样本数据的形状。
      • (2500, 784)
    • 之后,程序调用UMAP函数处理样本数据,通过设置降维后的维数为2,最小距离为0.3,最大距离(spread)为2,以及邻近节点数为30,对MNIST数据进行降维处理,并将降维后的数据存储在变量embedding中,并将降维数据的形状打印出来。这一步主要是进行降维计算。
      • (2500, 2)
    • 调用draw_pic()函数将降维后的数据进行可视化展示,并将可视化结果保存为"final-d0.01n-30_new.jpg"文件。这一步主要是为了展示降维的效果
    • 最后,使用plt.show()命令显示出图像。

import numpy as np
from scipy.optimize import curve_fit
from tqdm import tqdm
import scipy.sparse
# 利用numba 提高运行速度
import numba
import matplotlib.pyplot as plt
from warnings import warn# x 维度 [N,D]
def cal_pairwise_dist(x):print("compute distance")N,D = np.shape(x)dist = np.zeros([N,N])for i in tqdm(range(N)):for j in range(N):dist[i,j] = np.sqrt(np.dot((x[i]-x[j]),(x[i]-x[j]).T))#返回任意两个点之间距离return dist# 获取每个样本点的 n_neighbors个临近点的位置以及距离
def get_n_neighbors(data, n_neighbors = 15):dist = cal_pairwise_dist(data)dist[dist < 0] = 0N = dist.shape[0]NN_index = np.argsort(dist,axis=1)[:,0:n_neighbors]NN_dist = np.sort(dist,axis=1)[:,0:n_neighbors]return NN_index,NN_dist# 计算每个样本点的参数 sigmas 以及 rhos
def compute_sigmas_and_rhos(distances,k,local_connectivity=1,n_iter=64,tol = 1.0e-5,min_k_dis_scale=1e-3):print("computing sigmas and rhos")# 获取样本数目N= distances.shape[0]# 定义变量存储每个样本的 sigma 和 rhorhos = np.zeros(N, dtype=np.float32)sigmas = np.zeros(N, dtype=np.float32)mean_distances = np.mean(distances)target = np.log2(k)for i in tqdm(range(N)):lo = 0.0hi = np.infmid = 1.0# rho_i 为距离第i个样本最近的第local_connectivity个距离ith_distances = distances[i]non_zero_dists = ith_distances[ith_distances > 0.0]rhos[i] = non_zero_dists[local_connectivity - 1]# 通过2值搜索的方法计算sigma_ifor n in range(n_iter):psum = 0.0for j in range(1, distances.shape[1]):d = distances[i, j] - rhos[i]if d > 0:psum += np.exp(-(d / mid))else:psum += 1.0if np.fabs(psum - target) < tol:breakif psum > target:hi = midmid = (lo + hi) / 2.0else:lo = midif hi == np.inf:mid *= 2else:mid = (lo + hi) / 2.0sigmas[i] = mid# 进一步处理 防止 sigma_i 过小if rhos[i] > 0.0:mean_ith_distances = np.mean(ith_distances)if sigmas[i] < min_k_dis_scale * mean_ith_distances:sigmas[i] = min_k_dis_scale * mean_ith_distances# rhos[i]<=0 N个近邻点距离过近else:if sigmas[i] < min_k_dis_scale * mean_distances:sigmas[i] = min_k_dis_scale * mean_distancesreturn sigmas,rhos# 计算两点间的连接强度
def compute_membership_strengths(NN_index,NN_dists,sigmas,rhos):print("compute membership strengths")n_samples, n_neighbors =  np.shape(NN_index)rows = np.zeros(n_samples*n_neighbors, dtype=np.int32)cols = np.zeros(n_samples*n_neighbors, dtype=np.int32)vals = np.zeros(n_samples*n_neighbors, dtype=np.float32)for i in tqdm(range(n_samples)):for j in range(n_neighbors):if NN_index[i, j] == i:val = 0.0elif NN_dists[i, j] - rhos[i] <= 0.0 or sigmas[i] == 0.0:val = 1.0else:val = np.exp(-((NN_dists[i, j] - rhos[i]) / (sigmas[i])))rows[i * n_neighbors + j] = icols[i * n_neighbors + j] = NN_index[i, j]vals[i * n_neighbors + j] = valreturn rows, cols, valsdef get_graph_Inputs(X,n_neighbors,local_connectivity=1):n_samples = X.shape[0]# 计算每个样本点的N个临近点的位置和距离NN_index, NN_dists = get_n_neighbors(X,n_neighbors)# 计算每个样本的 sigm 与 rho 为后边的图计算提供参数sigmas,rhos = compute_sigmas_and_rhos(NN_dists,n_neighbors,local_connectivity)# 计算两点间的 连接强度 即计算条件概率 Pj|irows, cols, vals = compute_membership_strengths(NN_index,NN_dists,sigmas,rhos)# 构造稀疏矩阵result = scipy.sparse.coo_matrix((vals, (rows, cols)), shape=(X.shape[0], X.shape[0]))result.eliminate_zeros() # 去掉0# 计算联合概率 Pijtranspose = result.transpose()prod_matrix = result.multiply(transpose)# Pij  =  Pj|i + Pi|j - Pj|i* Pi|jresult = result + transpose - prod_matrixreturn result# 谱分析法进行初始化
def init_embedding_spectral(graph,dim):n_samples = graph.shape[0]k = dimdiag_data = np.asarray(graph.sum(axis=0))# Normalized LaplacianI = scipy.sparse.identity(graph.shape[0], dtype=np.float64)D = scipy.sparse.spdiags(1.0 / np.sqrt(diag_data), 0, graph.shape[0], graph.shape[0])L = I - D * graph * Dnum_lanczos_vectors = max(2 * k + 1, int(np.sqrt(graph.shape[0])))try:if L.shape[0] < 2000000:eigenvalues, eigenvectors = scipy.sparse.linalg.eigsh(L,k+1,which="SM",ncv=num_lanczos_vectors,tol=1e-4,v0=np.ones(L.shape[0]),maxiter=graph.shape[0] * 5,)else:print("---------------eigenvalues-------------------")eigenvalues, eigenvectors = scipy.sparse.linalg.lobpcg(L, np.random.normal(size=(L.shape[0], k+1)), largest=False, tol=1e-8)order = np.argsort(eigenvalues)[1:k+1]return eigenvectors[:, order]except scipy.sparse.linalg.ArpackError:warn("WARNING: spectral initialisation failed! The eigenvector solver\n""failed. This is likely due to too small an eigengap. Consider\n""adding some noise or jitter to your data.\n\n""Falling back to random initialisation!")return np.random.uniform(low=-10.0, high=10.0, size=(graph.shape[0], dim))def make_epochs_per_sample(weights, n_epochs):result = -1.0 * np.ones(weights.shape[0], dtype=np.float64)# 边的权重越大在整个训练过程中更新的次数越多,更新间隔越小n_samples = n_epochs * (weights / weights.max())result[n_samples > 0] = float(n_epochs) / n_samples[n_samples > 0] # 更新间隔return result# 梯度裁剪 -4,+4之间
@numba.njit()
def clip(val):if val > 4.0:return 4.0elif val < -4.0:return -4.0else:return valdef train_one_epoch(head_embedding,tail_embedding,head,tail,n_vertices,epochs_per_sample,epochs_per_negative_sample,epoch_of_next_sample,epoch_of_next_negative_sample,a,b,alpha,n,dim):for i in range(epochs_per_sample.shape[0]):#对正样本进行采样if epoch_of_next_sample[i] <= n:j = head[i]k = tail[i]current = head_embedding[j]other = tail_embedding[k]# 计算两点间距离dist_squared = np.dot((current-other),(current-other))# 计算正样本梯度if dist_squared > 0.0:grad_coeff = -2.0 * a * b * pow(dist_squared, b - 1.0)grad_coeff /= a * pow(dist_squared, b) + 1.0else:grad_coeff = 0.0# 进行更新for d in range(dim):# 梯度裁剪grad_d = clip(grad_coeff * (current[d] - other[d]))# 梯度current[d] += grad_d * alpha# 下次更新的轮次epoch_of_next_sample[i] += epochs_per_sample[i]# 计算负样本的数目n_neg_samples = int((n - epoch_of_next_negative_sample[i]) / epochs_per_negative_sample[i])# 进行负样本采样for p in range(n_neg_samples):k = np.random.randint(n_vertices)other = tail_embedding[k]dist_squared = np.dot((current-other),(current-other))if dist_squared > 0.0:grad_coeff = 2.0 * bgrad_coeff /= (0.001 + dist_squared) * (a * pow(dist_squared, b) + 1)elif j == k:continueelse:grad_coeff = 0.0for d in range(dim):if grad_coeff > 0.0:grad_d = clip(grad_coeff * (current[d] - other[d]))else:grad_d = 4.0current[d] += grad_d * alpha# 计算下次负样本更新轮次epoch_of_next_negative_sample[i] += (n_neg_samples * epochs_per_negative_sample[i])# 通过训练获得embedding
def train_embedding(head_embedding, #头结点 向量tail_embedding, #尾结点 向量head, # 头结点 编号tail, # 尾结点 编号epochs_per_sample, # 正样本采样控制epochs_per_negative_sample, # 负样本采样控制a,b, #initial_alpha, #  初始化学习率n_epochs, # 训练轮次n_vertices # 顶点数目):dim = head_embedding.shape[1]alpha = initial_alphaepoch_of_next_negative_sample = epochs_per_negative_sample.copy()epoch_of_next_sample = epochs_per_sample.copy()optimize_fn = numba.njit(train_one_epoch, fastmath=True, parallel=False)for n in tqdm(range(n_epochs)):# 进行1轮更新optimize_fn(head_embedding,tail_embedding,head,tail,n_vertices,epochs_per_sample,epochs_per_negative_sample,epoch_of_next_sample,epoch_of_next_negative_sample,a,b,alpha,n,dim)# 更新学习率alpha = initial_alpha * (1.0 - (float(n) / float(n_epochs)))return head_embeddingdef get_embedding(graph,dim,a,b,negative_sample_rate,n_epochs=None,initial_alpha=1.0):# 行列交换graph = graph.tocoo()graph.sum_duplicates()# 顶点数目n_vertices = graph.shape[1]# 计算迭代轮次 数据越少迭代轮次越多if n_epochs is None:if graph.shape[0] <= 10000:n_epochs = 500else:n_epochs = 200# 边的权重过低,无法采样,将权重设置为0if n_epochs > 10:graph.data[graph.data < (graph.data.max() / float(n_epochs))] = 0.0graph.eliminate_zeros()# 利用谱分析的方法,借助graph,对低维数据进行初始化initialisation = init_embedding_spectral(graph,dim)# 加入一些随机数据增加随机性expansion = 10.0 / np.abs(initialisation).max()embedding = (initialisation * expansion).astype(np.float32) + np.random.normal(scale=0.0001, size=[graph.shape[0], dim]).astype(np.float32)# 计算图中每条边,每隔多少个epoch 更新一次epochs_per_sample = make_epochs_per_sample(graph.data, n_epochs)# 负样本,每隔多少个epoch 更新一次epochs_per_negative_sample = epochs_per_sample/negative_sample_rate# 开始进行训练,获取 embeddinghead = graph.rowtail = graph.col# 训练获取降维数据embedding =train_embedding(embedding,embedding,head,tail,epochs_per_sample,epochs_per_negative_sample,a,b,initial_alpha,n_epochs,n_vertices)return embeddingdef find_ab_params(min_dist,spread):def curve(x, a, b):return 1.0 / (1.0 + a * x ** (2 * b))xv = np.linspace(0, spread * 3, 300)yv = np.zeros(xv.shape)yv[xv < min_dist] = 1.0yv[xv >= min_dist] = np.exp(-(xv[xv >= min_dist] - min_dist) / spread)params, covar = curve_fit(curve, xv, yv)return params[0], params[1]def UAMP(X,dim=2, # 降维后的维度n_neighbors=15, # N近邻min_dist = 0.1, # 控制投影后,相似点的聚拢程度spread = 1,negative_sample_rate=5, # 负样本采样是正样本采样的多少倍n_epochs=None, # 训练轮次initial_alpha= 1.0 # 初始化学习率):# 估算参数 a,ba,b = find_ab_params(min_dist,spread)# 根据高维数据 计算点与点之间的连接关系graph = get_graph_Inputs(X,n_neighbors,local_connectivity=1)print(graph)#embedding = get_embedding(graph,dim,a,b,negative_sample_rate,n_epochs,initial_alpha)return embeddingdef draw_pic(datas,labs,name = '1.jpg'):plt.cla()unque_labs = np.unique(labs)colors = [plt.cm.Spectral(each)for each in np.linspace(0, 1,len(unque_labs))]p=[]legends = []for i in range(len(unque_labs)):index = np.where(labs==unque_labs[i])pi = plt.scatter(datas[index, 0], datas[index, 1], c =[colors[i]] )p.append(pi)legends.append(unque_labs[i])plt.legend(p, legends)plt.savefig(name)if __name__ == "__main__":mnist_datas = np.loadtxt("mnist2500_X.txt")mnist_labs = np.loadtxt("mnist2500_labels.txt")print(mnist_datas.shape)embedding = UAMP(mnist_datas,dim=2,min_dist=0.3,spread=2,n_neighbors=30)print(embedding.shape)draw_pic(embedding,mnist_labs,name = "final-d0.01n-30_new.jpg")plt.show()

🐇图像描述

  • 参数设置:dim=2,min_dist=0.01,spread=1

在这里插入图片描述

  • 参数设置:dim=2,min_dist=0.3,spread=2

在这里插入图片描述

相关文章:

可视化 | 数据可视化降维算法梳理

文章目录 &#x1f4da;数据描述&#x1f407;iris&#x1f407;MNIST &#x1f4da;PCA&#x1f407;算法流程&#x1f407;图像描述 &#x1f4da;Kernel-PCA&#x1f407;算法流程&#x1f407;图像描述 &#x1f4da;MDS&#x1f407;算法流程&#x1f407;图像描述 &#…...

分布式:一文吃透分布式事务和seata事务

目录 一、事务基础概念二、分布式事务概念什么是分布式事务分布式事务场景CAP定理CAP理论理解CAPCAP的应用 BASE定理强一致性和最终一致性BASE理论 分布式事务分类刚性事务柔性事务 三、分布式事务解决方案方案汇总XA规范方案1&#xff1a;2PC第一阶段&#xff1a;准备阶段第二…...

Java架构师前沿技术

目录 1 导学2 信息物理系统2.1CPS的体系架构2.2 CPS的技术体系3 人工智能4 机器人5 边缘计算6 数字李生体7 云计算7.1 云计算的部署模式8 大数据想学习架构师构建流程请跳转:Java架构师系统架构设计 1 导学 2 信息物理系统 信息物理系统(CPS)是控制系统、嵌入式系统的扩展与…...

OpenCV ycrcb颜色空间

Opencv中有一个Ycrcb的选项&#xff0c;这个选项其实是Yuv444packet. 下面代码从文件中获取到一个yuv444planar的文件&#xff0c;通过手动转换&#xff0c;将其转为YcrCb&#xff0c;然后进行颜色空间csc. 所以可以确定这是一个packet的存储格式 def yuv444p_2_bgr8_opencv(…...

SPSS两独立样本t检验

前言&#xff1a; 本专栏参考教材为《SPSS22.0从入门到精通》&#xff0c;由于软件版本原因&#xff0c;部分内容有所改变&#xff0c;为适应软件版本的变化&#xff0c;特此创作此专栏便于大家学习。本专栏使用软件为&#xff1a;SPSS25.0 本专栏所有的数据文件请点击此链接下…...

视频格式高效转换:MP4视频批量转MKV格式的方法

随着数字媒体技术的不断发展&#xff0c;视频格式转换已经成为了我们日常工作中不可或缺的一部分。不同的视频格式适用于不同的场景和设备&#xff0c;因此将视频从一种格式转换为另一种格式往往是我们必须完成的任务。在本文中&#xff0c;我们将重点介绍如何运用云炫AI智剪高…...

0028Java程序设计-智能农场监控报警系统设计与实现

文章目录 摘要目 录系统设计开发环境 摘要 我国是一个以农业为主的国家&#xff0c;在当今社会信息化迅速发展的背景下&#xff0c;将信息技术与农业相融合是必然的趋势。现代信息技术在农业生产中的运用&#xff0c;主要体现在两个领域&#xff1a;一是传感器技术&#xff1b…...

数据结构和算法——用C语言实现所有图状结构及相关算法

文章目录 前言图的基本概念图的存储方式邻接矩阵邻接表十字链表临界多重表 图的遍历最小生成树普里姆算法&#xff08;Prim&#xff09;克鲁斯卡尔算法&#xff08;Kruskal&#xff09; 最短路径BFS求最短路径迪杰斯特拉算法&#xff08;Dijkstra&#xff09;弗洛伊德算法&…...

JavaScript一些数据类型介绍

JavaScript一些数据类型介绍 1&#xff09;数字类型&#xff08;Number&#xff09;&#xff1a;可以表示整数和浮点数&#xff0c;例如&#xff1a;42、3.14159。 var x 42; // x 的类型是 Number var y 3.14159; // y 的类型是 Number2&#xff09;字符串类型&#xff08…...

正向代理和反向代理与负载均衡

自存用 什么是反向代理&#xff0c;反向代理与正向代理的区别 一文帮你梳理清楚「正向代理和反向代理的区别与联系」 什么是反向代理服务器 正向代理为用户服务&#xff0c;给用户换个ip使其能访问其他网站 反向代理为服务器服务&#xff0c;使用户访问特定网站服务器。反向代…...

制造执行系统(MES)的核心功能是什么?

“一般来讲&#xff0c;制造执行系统&#xff08;MES&#xff09;的功能模块包括过程监控&#xff0c;质量管理&#xff0c;设备监控&#xff0c;计划执行等功能模块。” 为了深入探讨MES的核心功能&#xff0c;本文将从以下3个方面展开说明&#xff1a; 首先&#xff0c;从概…...

uniapp如何使用mumu模拟器

模拟器安装 下载地址&#xff1a;MuMu模拟器 模拟器相关设置 1.在设置-显示中选中手机版&#xff0c;设置手机分辨率 2.设置-关于手机-版本号快速点击&#xff0c;将其设置为开发者模式 3.选择多开器 4.打开hbuilderx&#xff0c;找到adb设置 5.配置adb路径及端口号&#x…...

【MATLAB源码-第64期】matlab基于DWA算法的机器人局部路径规划包含动态障碍物和静态障碍物。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 动态窗口法&#xff08;Dynamic Window Approach&#xff0c;DWA&#xff09;是一种局部路径规划算法&#xff0c;常用于移动机器人的导航和避障。这种方法能够考虑机器人的动态约束&#xff0c;帮助机器人在复杂环境中安全、…...

阿里云国际版和国内版的区别是什么,为什么很多人喜欢选择国际版?

阿里云国际版和国内版区别如下&#xff1a; 谈到区别&#xff0c;我们不妨先来对比下相同点与不同点&#xff0c;才能清晰明确的知道二者区别 下面先介绍不同点&#xff1a; 面向市场更广泛 阿里云国际版主要是面向国际&#xff08;全球&#xff09;客户的&#xff0c;而国内…...

监听redis过期业务处理

配置类&#xff1a; package com.testimport org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.cache.annotation.EnableCaching; import org.springframework.c…...

计算机网络与技术——数据链路层

&#x1f60a;计算机网络与技术——数据链路层 &#x1f680;前言☃️基本概念&#x1f94f;封装成帧&#x1f94f;透明传输&#x1f94f;差错检测 ☃️点对点协议PPP&#x1f94f;PPP协议的特点&#x1f94f;PPP协议的帧格式&#x1f50d;PPP异步传输时透明传输&#xff08;字…...

UE5 Android下载zip文件并解压缩到指定位置

一、下载是使用市场的免费插件 二、解压缩是使用市场的免费插件 三、Android路径问题 windows平台下使用该插件没有问题&#xff0c;只是在Android平台下&#xff0c;只有使用绝对路径才能进行解压缩&#xff0c;所以如何获得Android下的绝对路径&#xff1f;增加C文件获得And…...

CSS3盒模型

CSS3盒模型规定了网页元素的显示方式&#xff0c;包括大小、边框、边界和补白等概念。2015年4月&#xff0c;W3C的CSS工作组发布了CSS3基本用户接口模块&#xff0c;该模块负责控制与用户接口界面相关效果的呈现方式。 1、盒模型基础 在网页设计中&#xff0c;经常会听到内容…...

VINS-Mono-VIO初始化 (五:视觉惯性对齐求解)

整体思想就是根据预积分的公式&#xff0c;把已知量和未知量各放到一边&#xff0c;因为前面的数据都是变换到 c 0 c_{0} c0​下的&#xff0c;不是真正意义上和重力对齐的世界坐标&#xff0c;然后位移和速度的预积分中会用到加速度计获取的重力加速度g&#xff0c;但是这个重…...

详解Vue——的双向数据绑定是如何实现的?

引言 在现代的Web开发中&#xff0c;数据绑定是一个非常重要的概念。Vue.js是一种流行的JavaScript框架&#xff0c;它提供了一种简单而强大的方式来实现双向数据绑定。本文将介绍Vue的双向数据绑定原理&#xff0c;并提供相关代码示例。 什么是双向数据绑定&#xff1f; 双向…...

ESP32读取DHT11温湿度数据

芯片&#xff1a;ESP32 环境&#xff1a;Arduino 一、安装DHT11传感器库 红框的库&#xff0c;别安装错了 二、代码 注意&#xff0c;DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...

让AI看见世界:MCP协议与服务器的工作原理

让AI看见世界&#xff1a;MCP协议与服务器的工作原理 MCP&#xff08;Model Context Protocol&#xff09;是一种创新的通信协议&#xff0c;旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天&#xff0c;MCP正成为连接AI与现实世界的重要桥梁。…...

QT: `long long` 类型转换为 `QString` 2025.6.5

在 Qt 中&#xff0c;将 long long 类型转换为 QString 可以通过以下两种常用方法实现&#xff1a; 方法 1&#xff1a;使用 QString::number() 直接调用 QString 的静态方法 number()&#xff0c;将数值转换为字符串&#xff1a; long long value 1234567890123456789LL; …...

Spring数据访问模块设计

前面我们已经完成了IoC和web模块的设计&#xff0c;聪明的码友立马就知道了&#xff0c;该到数据访问模块了&#xff0c;要不就这俩玩个6啊&#xff0c;查库势在必行&#xff0c;至此&#xff0c;它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据&#xff08;数据库、No…...

Java线上CPU飙高问题排查全指南

一、引言 在Java应用的线上运行环境中&#xff0c;CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时&#xff0c;通常会导致应用响应缓慢&#xff0c;甚至服务不可用&#xff0c;严重影响用户体验和业务运行。因此&#xff0c;掌握一套科学有效的CPU飙高问题排查方法&…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

比较数据迁移后MySQL数据库和OceanBase数据仓库中的表

设计一个MySQL数据库和OceanBase数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...

【从零开始学习JVM | 第四篇】类加载器和双亲委派机制(高频面试题)

前言&#xff1a; 双亲委派机制对于面试这块来说非常重要&#xff0c;在实际开发中也是经常遇见需要打破双亲委派的需求&#xff0c;今天我们一起来探索一下什么是双亲委派机制&#xff0c;在此之前我们先介绍一下类的加载器。 目录 ​编辑 前言&#xff1a; 类加载器 1. …...

文件上传漏洞防御全攻略

要全面防范文件上传漏洞&#xff0c;需构建多层防御体系&#xff0c;结合技术验证、存储隔离与权限控制&#xff1a; &#x1f512; 一、基础防护层 前端校验&#xff08;仅辅助&#xff09; 通过JavaScript限制文件后缀名&#xff08;白名单&#xff09;和大小&#xff0c;提…...

SQL注入篇-sqlmap的配置和使用

在之前的皮卡丘靶场第五期SQL注入的内容中我们谈到了sqlmap&#xff0c;但是由于很多朋友看不了解命令行格式&#xff0c;所以是纯手动获取数据库信息的 接下来我们就用sqlmap来进行皮卡丘靶场的sql注入学习&#xff0c;链接&#xff1a;https://wwhc.lanzoue.com/ifJY32ybh6vc…...