在上一节介绍了一种最常见的降维方法PCA,本节介绍另一种降维方法LLE,本来打算对于其他降维算法一并进行一个简介,不过既然看到这里了,就对这些算法做一个相对详细的学习吧。


0.流形学习简介

在前面PCA中说到,PCA是一种无法将数据进行拉直,当直接对于曲面进行降维后,导致数据的重叠,难以区分,如下图所示:

这是因为在使用PCA降维时,PCA仅仅关注于保持降维后的方差最大,没有考虑样本的局部特征,如图所示:

利用PCA在对点①进行降维后,没有考虑点①与其他点②、③、④..的位置关系,也就是说对于点①来说,点⑤到点①的距离相较于点④到点①的距离更近,而在实际中并非如此,有句话说叫“举头见日,不见长安”。

此时就需要流形学习(Manifold Learning)来解决这个问题。所谓流形学习,就是一类基于流形的学习框架,所谓流形就是上面那样一张“S”的曲面,也可以想象成一个将地毯卷起来的样子,也就是一种不闭合的曲面,而流形学习就是对这个不闭合的去年进行降维,也就相当于把这上面那个“S”拉直,或者把地毯铺开的一个过程。

不同于PCA算法,流形学习在降维中关注保持样本的局部线性特征,为了保持局部特征,在进行降维时,不能将欧氏距离作为样本间的距离了,

此时就需要利用测地距离来保持样本间的特征,这个算法就是ISOMAP等距映射算法,该算法考虑在降维后每一个样本与其它样本的测地距离。

ISOMAP也有一个问题,就是它要考虑所有其他样本之间的测地距离,当样本的数量巨大时,算法时间较长。这时就需要对算法做进一步的改进。

1.LLE

1.1 LLE简介及基本思想

LLE(Local Linear Embedding)就是一种流形学习的算法,LLE在降维时不再考虑全部的样本来寻找全局最优解(这个在进行最终求解还是要考虑全局的,只不过对于每个样本而言,仅考虑局部),

而是保留局部的一些样本点作为局部特征进行降维,这样既保留了局部特征,又减少了计算量

LLE的基本思想比较简单,也就是只考虑“最近几个样本点”进行降维,下面具体来说说LLE的思想:

样本在原始空间中,分布如图所示:

假设现在我们对xi进行降维,将xi从原始空间降到低维空间,降维后暂且在这里称之为zi

首先第一步,找出能够“代表”xi的一些点,比如利用k-NN的方法,找到距离xi的最近的点xj.(注意:这里xj并不是一个点,而是“一些”点);

然后,定义这些能够“代表”xi的这些点与xj相连的权重为wij,那么xi就可以用这些能够“代表”它的点进行表示为:

但这些也仅仅代表而已,并不真正相等,但我们希望越能够“代表”越好,也就是期望两个值越接近越好。

这是对于一个样本点i,而对于空间中所有的点都是如此,并且希望每一个样本点与能够“代表”它的那些点的越接近越好,也就是:

我们已经通过K-NN找出这些点xj了,那对于w还是未知的,因此,需要找出一组w,使得上面的那个式子的值越小越好。这不就是一个常规的NN算法吗?直接使用梯度下降进行求解就可以求出w了。

现在假设我们已经求出了一组w,然后用这组w进行降维,同样,假设降维后的数据分布如下图:

这里zi就是xi从原始空间降维后的结果,保持wij不变,找一组zi和zj,使得:

可以看出,这个式子与上面的式子完全一样,不同的是,这里我们所要寻找的是zi和zj,而上面的式子所要寻找的是权重wij,这里同样利用梯度下降就可以进行优化求解。

上面就是LLE的基本原理,其实LLE的原理从上面来看是比较简单的,为保持局部样本的特点,将样本用局部其他样本的线性表示然后进行降维。这种方法在李宏毅老师的课程里用一句比较形象的话来概括:

1.2 LLE的数学推导

上面介绍了LLE的基本原理,说到了对于降维的过程直接用梯度下降的方法进行优化求解即可,然而同PCA一样,LLE也有一套完整的推导流程。

前面对于PCA是基于数学推导流程,求出的解析解,然后说道PCA可以看做一个NN结构,利用梯度下降进行求解。

本打算这里不再对LLE的推导进行阐述,但看到目前sklearn下对LLE用的就是解析解的方法,这里就对LLE推导和求解过程进行一个简单的学习和推导。

  • 首先,对于第一步寻找xi的最近邻样本xj这里就不再赘述,直接采用K-NN算法找出就可以了。
  • 假设原始空间中有m个样本,每个样本为n维,假设样本xi的近邻样本xj有k个

接下来,在高维空间中,需要找出xi与xj的空间线性关系,也就是找出权重系数wij。损失函数前面已经给出:

这里一般会对权重系数做一个归一化的处理,也就是:

为了便于推导,根据上面的归一化对xi做一个变形:

则有:

作进一步的变换:

这里,然后令,则有:

再对约束条件进一步变形:

这里"1k"表示k*1全为1的向量

根据损失函数L和约束条件,利用拉格朗日乘子求最小值:

对W进行求导,令其为0,则得到:

这里就得到了在高维空间中的权重系数。

这里Wi是一个k维的列向量,将所有样本的Wi串起来后,最终W则是一个m*k维的矩阵。

  • 接下来根据这些权重系数,在低维空间中找出降维后的数据,使得下面的损失函数越小越好:

这里zi和zj是新的空间中的数据,也就是说原数据为m*n,到新空间中假设降到d维,则新空间数据变为m*d

为了方便后边的推导,这里需要注意的是,这里的j不再是1~k,而是1~m,也就是说,从m个数据中拿出对应的k个样本,对于不属于近邻内的w则为0,也就是说,

W由原来的m*k维扩充到了m*m维,这里同样对于标准化数据有闲置条件:

对损失函数L(z)进一步整理得到:

令M=,则:

这里就类似于PCA中降维的求解过程类似(在PCA中是使其最大),同样的,对于上面的式子,使得L(z)最小的解为M的前d个最小的特征值所组成的特征向量所组成的Z

这里注意的是,M的最小特征值为0,此时对应的特征向量为全1,不能反映数据特征,通常选择第2到d+1小的特征值对应的特征向量得到最终降维后的数据Z

1.3 LLE的算法实现

根据上面的理论部分,按照其解析解的方式对LLE的算法进行实现,加深算法的理解。然后根据sklearn中的manifold中的LLE方法,实现LLE。

首先是LLE算法的具体实现过程:

import numpy as np

def cal_pairwise_dist(x):
# 输入矩阵x,返回两两之间的距离
"""
输入矩阵x,返回两两之间的距离
(a-b)^2 = a^2 + b^2 - 2ab
"""
# a^2 + b^2
sum_x = np.sum(np.square(x), axis=1) dist = np.add(np.add(-2 * np.dot(x, x.T), sum_x).T, sum_x)
return dist def get_n_neighbors(data, n_neighbors=10):
dist = cal_pairwise_dist(data)
dist[dist < 0] = 0
dist = dist ** 0.5 n = dist.shape[0]
N = np.zeros((n, n_neighbors)) for i in range(n):
# 计算每一个样本点,距离其最近的近邻点的索引
index_ = np.argsort(dist[i])[1:n_neighbors+1]
# 距离每一个样本最近的点的索引i
N[i] = N[i] + index_ return N.astype(np.int32) def lle(data, n_dims=2, n_neighbors=10):
# 先获取到样本点的近邻的样本索引
N = get_n_neighbors(data, n_neighbors)
# 样本数量n,维数为D
n, D = data.shape # 当原空间维度小于近邻点数量时,W不是满秩的,要进行特殊处理
if n_neighbors > D:
tol = 1e-3
else:
tol = 0 # 初始化W,W应该是n * n——neighbors维度,即n个样本有n个wi,每一个wi有n_neighbors, 这里做了转置
W = np.zeros((n_neighbors, n))
# 即1k,k维全为1的列向量
I = np.ones((n_neighbors, 1)) for i in range(n):
# 对于每一个样本点xi
# 先将xi进行伸展,形状同xj一致
Xi = np.tile(data[i], (n_neighbors, 1)).T
# xj所组成的矩阵
Ni = data[N[i]].T
# 求Yi
Yi = np.dot((Xi-Ni).T, (Xi - Ni))
# 这里是对于样本维度小于n_neighbors时做的特殊处理,MLLE算法,保持局部邻域关系的增量Hessian LLE算法
Yi = Yi + np.eye(n_neighbors) * tol * np.trace(Yi) # 求解逆矩阵
Yi_inv = np.linalg.pinv(Yi)
# 求解每一个样本的wi,并做归一化处理
wi = (np.dot(Yi_inv, I))/(np.dot(np.dot(I.T, Yi_inv), I)[0, 0])
W[:, i] = wi[:, 0] # 初始化W
W_y = np.zeros((n, n)) # 对上一步求的W做进一步扩充,之前是n*k维的,现在变成n*n维的,不是近邻的位置补0
for i in range(n):
index = N[i]
for j in range(n_neighbors):
W_y[index[j], i] = W[j, i] I_y = np.eye(n)
# 计算(I-W)(I-W).T
M = np.dot((I_y - W_y), (I_y - W_y).T)
# 求特征值
eig_val, eig_vector = np.linalg.eig(M)
# 找出前n_dim个小的特征值,忽略掉0,取第2到第n_dim+1个
index_ = np.argsort(np.abs(eig_val))[1: n_dims+1]
print("index_", index_)
# 特征值对应的特征向量就是最后降维后得到的样本
Y = eig_vector[:, index_] return Y

然后对上面的代码进行测试,首先导入一些必要的画图的工具包和数据集,数据集采用sklearn自带的“瑞士卷”数据集:

from sklearn.datasets import make_swiss_roll
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D X, color = make_swiss_roll(n_samples=5000, noise=0.1, random_state=42)
fig = plt.figure()
ax = Axes3D(fig)
ax.scatter(X[:, 0], X[:, 1], X[:, 2], c=color, cmap=plt.cm.Spectral)

同时用sklearn自带的manifold方法实现LLE,并同时用PCA对上面的数据集进行降维,对比三者得到的结果:

from sklearn import manifold
from sklearn.decomposition import PCA # LLE
data_1 = lle(X, n_neighbors=30)
# sklearn LLE
data_2 = manifold.LocallyLinearEmbedding(n_components=2, n_neighbors=30).fit_transform(X)
# PCA
pca_data = PCA(n_components=2).fit_transform(X) # 画图
plt.figure(figsize=(8, 4))
plt.subplot(131)
plt.title("LLE")
plt.scatter(data_1[:, 0], data_1[:, 1], c=color, cmap=plt.cm.Spectral) plt.subplot(132)
plt.title("sklearn_LLE")
plt.scatter(data_2[:, 0], data_2[:, 1], c=color, cmap=plt.cm.Spectral) plt.subplot(133)
plt.scatter(pca_data[:, 0], pca_data[:, 1], c=color, cmap=plt.cm.Spectral)
plt.title('PCA')

LLE算法对于所选取的局部样本点的个数较为敏感,所选取局部样本数量不同,对结果影响比较大,如下一组实验:

fig = plt.figure()
for index, k in enumerate((10, 20, 30, 40)):
plt.subplot(2, 2, index+1)
trans_data = manifold.LocallyLinearEmbedding(n_neighbors=k, n_components=2).fit_transform(X)
plt.scatter(trans_data[:, 0], trans_data[:, 1], c=color, cmap=plt.cm.Spectral)

可以看出,当n_neighbors不同取值,对于结果的影响较大,当过多或者过少时,结果则会完全坏掉。

1.4 LLE的优缺点

LLE的原理大致就是上面所介绍的,那么在最后的实验中也比较了不同的近邻数目对结果的影响,在这里总结一些LLE的优点和缺点进行比较:

优点:

    • 方法简单易于实现,相较于ISOMAP而言,计算复杂度低;
    • LLE不同于PCA的线性降维,LLE是一种非线性的降维方法,能够学习任意维的流形图形;
    • 因为其非线性的特点,LLE能够表达局部特征,从而保留原数据特征。

当然,LLE也自身存在一些缺点:

    • 对于近邻算法选取的近邻样本点和样本点的数量较为敏感
    • 仅能够学习流形图形,对于闭合的非流行图形,算法不适用
    • LLE只能够用于稠密分布均匀的样本分布,稀疏的样本使用LLE算法效果不佳(这可能也是因为LLE对于近邻样本敏感所造成的)。

2 TSNE算法

前面介绍了两种降维的方法PCA和LLE,这两种降维的方法都有一个共同的特点:在进行降维时,都强调了降维后的相似的数据要尽可能地保持相似,但并没有说对于那些不相似的数据,要有多不相似这个问题

这就导致了在进行降维时,可能导致数据的重叠问题,导致在低维空间中一样很难进行区分。

这时就需要另一种降维的方法——T-SNE。

所谓T-SNE,其就是SNE(stochastic neighbor embedding)的升级版,SNE同前面的算法一样,希望样本在高维空间中相似的数据点,到低维空间也相似,而T-SNE则是在SNE的基础上,要使得对于高维空间不相似的样本,在低维空间中也尽可能不相似

SNE则把这种距离的关系转换为一种概率。下面先介绍二者共同的部分,对于区别和改进,在后边进行对比。这里仅做简要原理介绍,不再做过多推导。

首先,定义在高维空间中两个样本xi和xj之间的相似度S(xi,xj),则:

这个概率表示,xi作为中心点,xj是其近邻点的概率,当两个点越近,则概率越大,距离较远时,则概率较小。

同样的,样本映射到低维空间后,分别为zi和zj,在低位空间中,二者的相似度定义为S'(zi,zj),则:

然后根据这两个分布,在高维空间和低维空间中,我们希望这两个分布越相似越好,而用来衡量样本相似度的指标为KL散度,因此定义损失如下:

具体的KL散度的公式如下:

然后根据这个损失函数,找一组zi和zj,使得损失越小越好。然后就是梯度下降进行求解。

上面就是SNE与TSNE所共同的过程,在高维空间中,二者都采用高斯分布来度量任意两个点相似性,即:

而在低维空间中,SNE同样采用高斯分布作为相似性度量的方式,即:

对于TSNE来说,则采用了更一般的T分布来替代高斯分布

这样可以不但拉近相似的样本的之间距离,同时,当样本不相似时,则可以使距离拉的更远。如下图所示:

对于两条曲线而言,当i与j相距较近时,二者在相似度上差不多,而当i与j相距较远时,T分布则可以进一步拉远二者之间的距离。

上面就是TSNE的基本思想,具体原理和算法这里不再进行推导,详细内容可以参考博客:TSNE-原理与实现

关于TSNE的具体实现方法,git地址https://github.com/heucoder/dimensionality_reduction_alo_codes/tree/master/codes/T-SNE。这里就利用sklearn中的tsne方法对其进行实现和比较:

from sklearn.manifold import TSNE

plt.figure()
for index, k in enumerate((5, 15, 25, 30, 40, 100)):
plt.subplot(230+index+1)
data_tsne = TSNE(n_components=2, perplexity=k).fit_transform(X)
plt.scatter(data_tsne[:, 0], data_tsne[:, 1], c=color, cmap=plt.cm.Spectral)

对于在手写数字识别上使用TSNE得到的结果,能够更加明显地看出TSNE的优点:

可以看出,TSNE解决了数据在降维之后的拥挤问题

以上就是关于LLE和TSNE的内容,这里暂时没有对TSNE进行推导,后面自己会对TSNE的源代码进行熟悉和了解加深理解,本节有关降维的方法就先到这里了。

本文参考资料:

局部线性嵌入(LLE)原理总结

LLE原理及推导过程

李宏毅《机器学习》


降维的方法其实有很多,也有同一种的不同变形,当用到时后面会再进行补充。下一更准备对比较重要的算法AutoEnder做一个复习。

【机器学习基础】无监督学习(2)——降维之LLE和TSNE的更多相关文章

  1. Spark机器学习基础-无监督学习

    0.K-means from __future__ import print_function from pyspark.ml.clustering import KMeans#硬聚类 #from p ...

  2. 2019-07-31【机器学习】无监督学习之降维PCA算法实例 (鸢尾花)

    样本 代码: import matplotlib.pyplot as plt from sklearn.decomposition import PCA from sklearn.datasets i ...

  3. 2019-07-31【机器学习】无监督学习之降维NMF算法 (人脸特征提取)

    代码 from numpy.random import RandomState #加载RandomState用于创建随机种子 import matplotlib.pyplot as plt from ...

  4. Python 机器学习实战 —— 无监督学习(上)

    前言 在上篇<Python 机器学习实战 -- 监督学习>介绍了 支持向量机.k近邻.朴素贝叶斯分类 .决策树.决策树集成等多种模型,这篇文章将为大家介绍一下无监督学习的使用.无监督学习顾 ...

  5. Python 机器学习实战 —— 无监督学习(下)

    前言 在上篇< Python 机器学习实战 -- 无监督学习(上)>介绍了数据集变换中最常见的 PCA 主成分分析.NMF 非负矩阵分解等无监督模型,举例说明使用使用非监督模型对多维度特征 ...

  6. 【机器学习】无监督学习Autoencoder和VAE

    众所周知,机器学习的训练数据之所以非常昂贵,是因为需要大量人工标注数据. autoencoder可以输入数据和输出数据维度相同,这样测试数据匹配时和训练数据的输出端直接匹配,从而实现无监督训练的效果. ...

  7. 2019-07-31【机器学习】无监督学习之聚类 K-Means算法实例 (图像分割)

    样本: 代码: import numpy as np import PIL.Image as image from sklearn.cluster import KMeans def loadData ...

  8. 2019-07-28【机器学习】无监督学习之聚类 DBSCAN方法及其应用 (在线大学生上网时间分析)

    样本: import numpy as np import sklearn.cluster as skc from sklearn import metrics import matplotlib.p ...

  9. 2019-07-25【机器学习】无监督学习之聚类 K-Means算法实例 (1999年中国居民消费城市分类)

    样本 北京,2959.19,730.79,749.41,513.34,467.87,1141.82,478.42,457.64天津,2459.77,495.47,697.33,302.87,284.1 ...

随机推荐

  1. File常用的方法

    import java.io.File; import java.io.IOException; /* 创建: createNewFile() 在指定位置创建一个空文件,成功就返回true,如果已存在 ...

  2. Shell编程之循环语句与echo的用法

    Shell编程之循环语句与echo的用法 目录 Shell编程之循环语句与echo的用法 一.echo用法 1. echo常用选项 2. 常用的转义字符 3. 特殊符号%.#的用法 二.循环语句 1. ...

  3. JavaScript中this的绑定规则

    JavaScript中this的绑定规则 前言 我们知道浏览器运行环境下在全局作用域下的this是指向window的,但是开发中却很少在全局作用域下去使用this,通常都是在函数中进行使用,而函数使用 ...

  4. 《PHP面试笔试真题库》——PHP面试的好帮手

    你好,是我琉忆. 一个文艺的PHP开发工程师. 很荣幸能够在这里带来我的第一本新书--<PHP程序员面试笔试真题库>. 一.创作过程 <PHP 程序员面试笔试真题库>是我的第三 ...

  5. 矩阵QR分解

    1 orthonormal 向量与 Orthogonal 矩阵 orthonormal 向量定义为 ,任意向量  相互垂直,且模长为1: 如果将  orthonormal 向量按列组织成矩阵,矩阵为  ...

  6. Solution -「CF 487E」Tourists

    \(\mathcal{Description}\)   Link.   维护一个 \(n\) 个点 \(m\) 条边的简单无向连通图,点有点权.\(q\) 次操作: 修改单点点权. 询问两点所有可能路 ...

  7. Solon 1.6.25 发布,轻量级应用开发框架

    关于官网 千呼万唤始出来: https://solon.noear.org .整了一个月多了...还得不断接着整! 关于 Solon Solon 是一个轻量级应用开发框架.支持 Web.Data.Jo ...

  8. python-利用faker模块生成测试数据

    Python-利用faker模块生成测试数据 1.前言: Faker模块是一个生成伪数据的第三方模块,他提供了一系列方法,使用非常方便,在做自动化测试时,注册信息,用这个模块生成测试数据就体现了它的好 ...

  9. 攻防世界之Web_unserialize3

    题目: 直接给源码,审计代码,又是一题反序列化的.传送门:反序列化漏洞 [原理] PHP反序列化漏洞:执行unserialize()时,先会调用__wakeup(). 当序列化字符串中属性值个数大于属 ...

  10. CobaltStrike逆向学习系列(11):自实现 Beacon 检测工具

    这是[信安成长计划]的第 11 篇文章 关注微信公众号[信安成长计划] 0x00 目录 0x01 检测原理 0x02 检测方案 0x03 存在的问题 0x04 解决方案 0x05 示例代码 0x06 ...