机器学习降维算法MDS、ISPOMAP

1、介绍
首先先看一些定义
 流形,是局部具有欧几里得空间性质的空间,能用欧氏距离来进行距离计算。
 同胚,在拓扑学中,两个流形,如果可以通过弯曲、延展、剪切(只要最终完全沿着当初剪开的缝隙再重新粘贴起来)等操作把其中一个变为另一个,则认为两者是同胚的。故也可以称流形是在局部与欧式空间同胚的空间。
 测地线距离,空间中两点的局域最短或最长路径
在这里插入图片描述

如图:蓝色实线表示测地距离,蓝色虚线是传统上我们所认识的欧氏距离
用流形在局部上与欧氏空间同胚这个性质,对每个点基于欧氏距离找出其近邻点,然后就能建立一个近邻连接图,图中近邻点之间存在连接,而非近邻点之间不存在连接,于是,计算两点之间测地线距离的问题就转变为计算近邻连接图上两点之间的最短路径问题.
在近邻连接图上计算两点间的最短路径,可采用著名的Dikstra算法或Floyd 算法,在得到任意两点的距离之后,就可通过 MDS 方法来获得样本点在低维空间中的坐标
ISOMAP,使用“测地距离”使高维空间中任意点对之间的测地距离关系在低维空间中保持不变,这也是它最主要的优点,而不是使用原始的欧几里得距离,这样可以更好的控制数据信息的流失,能够在低维空间中更加全面的将高维空间的数据表现出来
Isomap算法是在MDS算法的基础上衍生出的一种算法,MDS算法是保持降维后的样本间距离不变,而ISOMAP的实现过程还需要MDS算法的加入来实现
其算法描述过程如下:
在这里插入图片描述

2、算法实现
2.1数据的选择
先说一下数据吧,这里我的数据使用的是随机生成的
为什么没有选择doc2vec处理文档后的数据呢?
因为那样生成的确实是高维度的数据,没有使用并不是说ISOMAP不能处理,而是因为我讲解ISOMAP模型的目的是想展现这个算法它的实现过程以及他的优势,讲解过程中想要借助图像来更方便的展现实现效果,鉴于高维空间的构成是我们难以想象的,所以我需要一个三维可展现的空间来降维到二维,让大家更加便于理解其中的过程
2、算法实现
2.1数据的选择
先说一下数据吧,这里我的数据使用的是随机生成的
为什么没有选择doc2vec处理文档后的数据呢?
因为那样生成的确实是高维度的数据,没有使用并不是说ISOMAP不能处理,而是因为我讲解ISOMAP模型的目的是想展现这个算法它的实现过程以及他的优势,讲解过程中想要借助图像来更方便的展现实现效果,鉴于高维空间的构成是我们难以想象的,所以我需要一个三维可展现的空间来降维到二维,让大家更加便于理解其中的过程
2.2主要实现过程
 生成随机数据
借助于scikit-learn 的 datasets 模块我们专门用make_s_curve生成流行数据,凸显ISOMAP优势

X, Y = make_s_curve(n_samples = 500, #主要数据是X,Y只是用来生成不同的颜色  
                           noise = 0.1,  
                           random_state = 42) 

这里X生成的是一个500*3的数据,每一列分别作为三维空间的坐标,相当于现在我们准备的500个点。y其实只是我们用来展现图形不同颜色的代表数,这里我们为了展示最初的效果,绘制三维空间图像展示数据分布

def 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)  

结果如下:
在这里插入图片描述
 生成距离矩阵
上面我们生成了500个点,现在我们先计算两两之间的欧氏距离

1.	def cal_pairwise_dist(x):    
2.	    N,D = np.shape(x)    
3.	    dist = np.zeros([N,N])    
4.	    for i in range(N):    
5.	        for j in range(N):    
6.	            # dist[i,j] = np.dot((x[i]-x[j]),(x[i]-x[j]).T)    
7.	            dist[i,j] = np.sqrt(np.dot((x[i]-x[j]),(x[i]-x[j]).T))  
8.	            # dist[i,j] = np.sum(np.abs(x[i]-x[j]))    
9.	        
10.	    #返回任意两个点之间距离    
11.	    return dist  

得到距离矩阵
在这里插入图片描述
 MDS降维实现
为了凸显ISOMAP的效果,我们这里先实现一下MDS降维处理后的效果,我们可以对比结果
使用MDS算法将数据降维到二维空间

1.	def my_MDS(dist, n_dims):    
2.	    # dist (n_samples, n_samples)    
3.	    dist = dist**2    
4.	    n = dist.shape[0]    
5.	    T1 = np.ones((n,n))*np.sum(dist)/n**2    
6.	    T2 = np.sum(dist, axis = 1)/n     
7.	    T3 = np.sum(dist, axis = 0)/n    
8.	    
9.	    B = -(T1 - T2 - T3 + dist)/2    
10.	    
11.	    eig_val, eig_vector = np.linalg.eig(B)    
12.	    index_ = np.argsort(-eig_val)[:n_dims]    
13.	    picked_eig_val = eig_val[index_].real    
14.	    picked_eig_vector = eig_vector[:, index_]    
15.	    
16.	return picked_eig_vector*picked_eig_val**(0.5)  

在这里插入图片描述
 ISOMAP的实现
我们这里的主要部分就是构建最短路径矩阵,思想上面也说了,我们需要规定临界点的个数k,保留下每个点的k个临界点的欧式距离值,剩余其他的所有距离看作是无穷大,利用相邻点欧氏距离的累加,逐步寻找跨越的点的距离,这个就是测地距离

# 构建最短路径图  
def floyd(D,n_neighbors=15):  
    Max = np.max(D)*1000  
    n1,n2 = D.shape  
    k = n_neighbors  
    D1 = np.ones((n1,n1))*Max  
    D_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 D1 

计算完距离之后我们得到一个测地距离矩阵
在这里插入图片描述
接下来执行我们上面算法描述中的第5步使用MDS对测地距离矩阵进行降维,依旧是降维到二维空间,绘制图像得到我们ISOMAP处理后的结果
在这里插入图片描述

其实,sklearn中由关于ISOMAP算法的包,我们可以直接调用,这样比较方便

1.	#直接调包  
2.	data_ISOMAP2 = Isomap(n_neighbors = 10, n_components = 2).fit_transform(X)  

这样的结果是
在这里插入图片描述
ISOMAP处理后的展现的图像,保留了原来数据的相对位置,他其实是将三维空间的图像展开类似于展开一样展现在二维中,可以看到我门调包处理后的结果与我们自己写的算法实现结果大致相同,说明我们自己手动实现的效果也不错。
当然ISOMAP的作用不仅仅限于这些,我们可以用ISOMAP处理更高维的数据而且保持测地数据不变。

全部代码:

# -*- coding: utf-8 -*-
"""
Created on Fri Nov 12 17:52:03 2021

@author: Yangz
"""
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
from mpl_toolkits.mplot3d import Axes3D

# 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)) #欧氏距离的计算 相当于 sqrt((x1-x2)^2+(y1-y2)^2+(z1-z2)^2)                                           
            # dist[i,j] = np.sum(np.abs(x[i]-x[j]))
    
    #返回任意两个点之间距离
    return dist
    
    
    
# 构建最短路径图
def floyd(D,n_neighbors):#=15
    Max = np.max(D)*1000  
    n1,n2 = D.shape
    k = n_neighbors
    D1 = np.ones((n1,n1))*Max
    D_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 D1


def my_mds(dist, n_dims):
    # dist (n_samples, n_samples)
    dist = dist**2  #矩阵所有元素平方
    n = dist.shape[0]
    T1 = np.ones((n,n))*np.sum(dist)/n**2   # 计算dist(ij)^2
    T2 = np.sum(dist, axis = 1)/n  # 计算dist(i.)^2
    T3 = np.sum(dist, axis = 0)/n   # 计算dist(.j)^2

    B = -(T1 - T2 - T3 + dist)/2  #B = Z^T * Z
    #对矩阵B做特征分解
    eig_val, eig_vector = np.linalg.eig(B)  #eig_val矩阵B的特征值,eig_vector归一化的特征向量
    index_ = np.argsort(-eig_val)[: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 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



def 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, #主要数据是X,Y只是用来生成不同的颜色
                           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()
  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值