t-SNE算法

t-SNE 算法

前言

  t-SNE(t-distributed stochastic neighbor embedding) 是用于降维的一种机器学习算法,由 Laurens van der Maaten 和 Geoffrey Hinton在 08 年提出。t-SNE 作为一种非线性降维算法,非常适用于高维数据降维到 2 维或者 3 维,便于进行可视化。在实际应用中,t-SNE 很少用于降维,主要用于可视化,可能的原因有以下几方面:

  • 当发现数据需要降维时,一般是特征间存在高度的线性相关性,此时一般使用线性降维算法,比如 PCA。即使是特征之间存在非线性相关,也不会先使用非线性降维算法降维之后再搭配一个线性的模型,而是直接使用非线性模型;
  • 一般 t-SNE 都将数据降到 2 维或者 3 维进行可视化,但是数据降维降的维度一般会大一些,比如需要降到 20 维,t-SNE 算法使用自由度为 1 的 t 分布很难做到好的效果,而关于如何选择自由度的问题,目前没有研究;
  • t-SNE 算法的计算复杂度很高,另外它的目标函数非凸,可能会得到局部最优解;

  在可视化的应用中,t-SNE 的效果要好于 PCA,下面是对手写数字可视化的一个结果对比。对可视化的效果衡量,无非是两方面:相似的数据是不是离得近,不相似的数据是不是离得远。从这两方面来讲,t-SNE 的效果要明显优于 PCA。
      


SNE

基本思想

  t-SNE 算法由 SNE 改进而来,所以先来介绍 $\mathrm{SNE}_{\circ}$ 给定 $ \mathrm{n}$ 个高维数据 $ x_{1}, x_{2}, \ldots, x_{n} $,若将其降维至 2 维,SNE 的基本思想是若两个数据在高维空间中是相似的,那么降维至 2 维空间时它们应该离得很近。

相似性计算

  SNE 使用条件概率来描述两个数据之间的相似性,假设 $ x_{i}, x_{j}$ 是高维空间中的两个点,那么以点 $ x_{i}$ 为中心构建方差为 $ \sigma_{i}$ 的高斯分布,使用 $ p_{j \mid i}$ 表示 $ x_{j}$ 是 $ x_{i}$ 邻域的概率,如果 $ x_{j}$ 离 $ x_{i}$ 很近,那么 $ p_{j \mid i}$ 很大,反之, $ p_{j \mid i}$ 很小,$ p_{j \mid i}$ 定义如下:

    $p_{j \mid i}=\frac{\exp \left(-\left\|x_{i}-x_{j}\right\|^{2} /\left(2 \sigma_{i}^{2}\right)\right)}{\sum_{k \neq i} \exp \left(-\left\|x_{i}-x_{k}\right\|^{2} /\left(2 \sigma_{i}^{2}\right)\right)}$

  由于只关心不同点对之间的相似度,所以设定 $p_{i \mid i}=0$ 。
  当把数据映射到低维空间后,高维数据点之间的相似性也应该在低维空间的数据点上体现出来。这里同样用条件概率的形式描述,假设 $ x_{i}, x_{j}$ 映射到低维空间后对应 $ y_{i}, y_{j}$,$y_{j}$ 是 $ y_{i}$ 邻域的条件概率为 $ q_{j \mid i}$ :

    $q_{j \mid i}=\frac{\exp \left(-\left\|y_{i}-y_{j}\right\|^{2}\right)}{\sum \limits _{k \neq i} \exp \left(-\left\|y_{i}-y_{k}\right\|^{2}\right)}$

  低维空间中的方差直接设置为  $ \sigma_{i}=\frac{1}{\sqrt{2}}  $ ,方便计算,同样   $q_{i \mid i}=0$  。 

目标函数

  此时就很明朗了,若  $y_{i}$  和  $y_{j}$  真实反映了高维数据点  $x_{i}$  和  $x_{j}$  之间的关系,那么条件概率  $p_{j \mid i}$  与  $q_{j \mid} $ 应该完全相等。这里我们只考虑了  $x_{i}$  与  $x_{j}$ 之间的条件概率,若考虑  $x_{i}$  与其他所有点之间的条件概率,则可构成一个条件概率分布  $P_{i}$  , 同理在低维空间存在一个条件概率分布  $Q_{i}$  且应该与  $P_{i}$  一致。如何衡量两个分布之间的相似性? 当然是用经典的 KL 距离(Kullback-Leibler Divergence),SNE 最终目标就是对所有数据点最小化这个 KL 距 离,我们可以使用梯度下降算法最小化如下代价函数:

    $C=\sum\limits_{i} K L\left(P_{i}|| Q_{i}\right)=\sum \limits _{i} \sum \limits_{j} p_{j \mid i} \log \frac{p_{j \mid i}}{q_{j \mid i}}$

  似乎到这里问题就漂亮的解决了,代价函数都写出来了,剩下的事情就是利用梯度下降算法进行训练。但事情远没有那么简单,因为 KL 距离是一个非对称的度量。最小化代价函数的目的是让  $p_{j \mid i}$  和  $q_{j \mid i}$ 的值尽可能的接近,即低维空间中点的相似性应当与高维空间中点的相似性一致。但是从代价函数的形式就可以看出,当  $p_{j \mid i}$  较大,$q_{j \mid i}$  较小时,代价较高;而  $p_{j \mid i}$ 较小,$ q_{j \mid i}$ 较大时,代价较低。什么意思呢? 很显然,高维空间中两个数据点距离较近时,若映射到低维空间后距离较远,那么将得到一个很高的惩罚,这当然没问题。反之,高维空间中两个数据点距离较远时,若映射到低维空间距离较近,将得到一个很低的惩罚值,这就有问题了,理应得到一个较高的惩罚才对。换句话说,SNE的代价函数更关注局部结构,而忽视了全局结构。

SNE缺点

  通过以上的介绍,总结一下SNE的缺点:

  • 不对称导致梯度计算复杂,对目标函数计算梯度如下,由于条件概率  $p_{j \mid i}$  不等于  $p_{i \mid j}$,$q_{j \mid i}$  不等于  $q_{i \mid j} $,因此梯度计算中需要的计算量较大。

    $\frac{\delta C}{\delta y_{i}}=2 \sum \limits _{j}\left(p_{j \mid i}-q_{j \mid i}+p_{i \mid j}-q_{i \mid j}\right)\left(y_{i}-y_{j}\right)$

  这个梯度还有一定的物理意义,我们可以用分子之间的引力和斥力进行解释。低维空间中点 $y_{i}$ 的位置是由其他所 有点对其作用力的合力所决定的。其中某个点 $y_{j}$ 对其作用力是沿着 $y_{i}-y_{j}$ 方向的,具体是引力还是斥力占主导就 取决于 $y_{j}$ 与 $y_{i}$ 之间的距离了,其实就与 $\left(p_{j \mid i}-q_{j \mid i}+p_{i \mid j}-q_{i \mid j}\right)$ 这一项有关。

  • Crowing 问题。就是不同类别的簇挤在一起,无法区分开来。拥挤问题的出现与某个特定算法无关,而是由于高维空间距离分布和低维空间距离分布的差异造成的。比如有一种情况,高维度数据在降维到10维下,可以有很好的表达,但是降维到两维后无法得到可信映射,比如降维如 10 维中有 11 个点之间两两等距离的,在二维下就无法得到可信的映射结果(最多3个点)。进一步的说明,假设一个以数据点 $x_i$ 为中心,半径为 $r$ 的 $m$ 维球(三维空间就是球),其体积是按 $r^m$ 增长的,假设数据点是在m维球中均匀分布的,我们来看看其他数据点与 $x_i$ 的距离随维度增大而产生的变化。

      

  从上图可以看到,随着维度的增大,大部分数据点都聚集在 $m$ 维球的表面附近,与点 $x_i$ 的距离分布极不均衡。如果直接将这种距离关系保留到低维,就会出现拥挤问题。

import matplotlib.pyplot as plt
import numpy as np
from numpy.linalg import norm
npoints = 1000 # 抽取1000个m维球内均匀分布的点
plt.figure(figsize=(20, 4))
for i, m in enumerate((2, 3, 5, 8)):
    # 这里模拟m维球中的均匀分布用到了拒绝采样,即先生成m维立方中的均匀分布,再剔除m维球外部的点
    accepts = []
    while len(accepts) < 1000:
        points = np.random.rand(500, m)
        accepts.extend([d for d in norm(points, axis=1) if d <= 1.0]) # 拒绝采样
    accepts = accepts[:npoints]
    ax = plt.subplot(1, 4, i+1)
    ax.set_xlabel('distance') # x轴表示点到圆心的距离
    if i == 0:
        ax.set_ylabel('count') # y轴表示点的数量
    ax.hist(accepts, bins=np.linspace(0., 1., 50), color='green')
    ax.set_title('m={0}'.format(str(m)), loc='left')
plt.show()

View Code


t-SNE

  SNE算法的思路是不错的,但是它的可视化效果大家也看到了,存在很大改进空间。如何改进它呢?

对称 SNE

  原始 SNE 中,在高维空间中条件概率 $p_{j \mid i}$ 不等于 $p_{i \mid j}$ , 低维空间中 $q_{j \mid i}$ 不等于 $q_{i \mid j} $,于是提出对称 $\mathrm{SNE} $,采用更加通用的联合概率分布代替原始的条件概率,使得 $p_{i j}=p_{j i}, \quad q_{i j}=q_{j i} $

  优化  $p_{i} \mid j$  和  $q_{i} \mid j$ 的 KL 散度的一种替换思路是,使用联合概率分布来替换条件概率分布,即 $P$ 是高维空间里各个点的联合概率分布, $Q$ 是低维 空间下的,目标函数为:

    $C=K L(P \| Q)=\sum \limits_{i} \sum \limits _{j} p_{i, j} \log \frac{p_{i j}}{q_{i j}}$

  简单来讲,在低维空间中定义 $ q_{i j} $ :

    $q_{i j}=\frac{\exp \left(-\left\|y_{i}-y_{j}\right\|^{2}\right)}{\sum_{k \neq l} \exp \left(-\left\|y_{k}-y_{l}\right\|^{2}\right)}$

  当然,在高维空间我们也可以定义 $p_{i j} $:

    $p_{i j}=\frac{\exp \left(-\left\|x_{i}-x_{j}\right\|^{2} / 2 \sigma^{2}\right)}{\sum \limits_ {k \neq l} \exp \left(-\left\|x_{k}-x_{l}\right\|^{2} / 2 \sigma^{2}\right)}$

   但是在高维空间中这样的定义会带来异常值的问题,怎么理解呢? 假设点  $x_{i}$  是一个噪声点,那么  $\left\|x_{i}-x_{j}\right\|$  的平方会很大,那么对于所有的  $\mathrm{j}, p_{i j}$  的值都会很小,导致在低维映射下的  $y_{i}$  对整个损失函数的影响很小,但对于异常值,我们显然需要得到一个更大的惩罚,于是对高维空间中的联合概率修正为:

    $p_{i j}=\frac{p_{i l}+p_{p_{j i}}}{2}$

  这样就避免了异常值的问题,此时的梯度变为:

    $\frac{\delta C}{\delta y_{i}}=4 \sum_{j}\left(p_{i j}-q_{i j}\right)\left(y_{i}-y_{j}\right)$

  相比于原始 SNE,对称 SNE 的梯度更加简化,计算效率更高。但对称SNE的效果只是略微优于原始SNE的效果。

引入 t 分布

  $t$-分布(t-distribution)用于根据小样本来估计呈正态分布且方差未知的总体的均值。如果总体方差已知(例如在样本数量足够多时),则应该用正态分布来估计总体均值。

   $t$分布曲线形态与 $n$(确切地说与自由度df)大小有关。与标准正态分布曲线相比,自由度 df 越小,$t$ 分布曲线愈平坦,曲线中间愈低,曲线双侧尾部翘得愈高;自由度 df 愈大,$t$ 分布曲线愈接近正态分布曲线,当自由度 $df=∞$ 时,$t$分布曲线为标准正态分布曲线。

  假设 $X$ 服从标准正态分布 $N (0,1)$, $Y$ 服从 $ \chi^{2}(n) $分布, 那么  $Z=\frac{X}{\sqrt{Y / n}} $ 的分布称为自由度为  $n$  的分布,记为  $Z \sim t(n) $ 。 

  分布密度函数 $f_{Z}(x)=\frac{\operatorname{Gam}\left(\frac{n+1}{2}\right)}{\sqrt{n \pi} \operatorname{Gam}\left(\frac{n}{2}\right)}\left(1+\frac{x^{2}}{n}\right)^{-\frac{n+1}{2}} $

  其中,$ \operatorname{Gam}(\mathrm{x}) $ 为伽马函数。

  对称SNE实际上在高维度下 另外一种减轻”拥挤问题”的方法:在高维空间下,在高维空间下我们使用高斯分布将距离转换为概率分布,在低维空间下,我们使用更加偏重长尾分布的方式来将距离转换为概率分布,使得高维度下中低等的距离在映射后能够有一个较大的距离。

      

  t 分布是一种长尾分布,从图中可以看到,在没有异常点时,t 分布与高斯分布的拟合结果基本一致。而在第二张图中,出现了部分异常点,由于高斯分布的尾部较低,对异常点比较敏感,为了照顾这些异常点,高斯分布的拟合结果偏离了大多数样本所在位置,方差也较大。相比之下,t  分布的尾部较高,对异常点不敏感,保证了其鲁棒性,因此其拟合结果更为合理,较好的捕获了数据的整体特征。
  那么如何利用 t 分布的长尾性来改进 SNE 呢?看下图,注意这个图并不准确,主要是为了说明 t 分布是如何发挥作用的。

  图中有高斯分布和  $\mathrm{t}$  分布两条曲线,表示点之间的相似性与距离的关系,高斯分布对应高维空间,$\mathrm{t}$  分布对应低维空间。那么对于高维空间中相距较近的点,为了满足  $p_{i j}=q_{i j}$ , 低维空间中的距离需要稍小一点; 而对于高维空间中相距较远的点,为了满足  $p_{i j}=q_{i j} $, 低维空 间中的距离需要更远。这恰好满足了我们的需求,即同一簇内的点(距离较近)聚合的更紧密,不同簇之间的点(距离较远)更加疏远。

      

  引入 t 分布之后,在低维空间中,用自由度为 1 的 t 分布重新定义 :

     $q_{i j}=\frac{\left(1+\left\|y_{i}-y_{j}\right\|^{2}\right)^{-1}}{\sum \limits _{k \neq l}\left(1+\left\|y_{i}-y_{j}\right\|^{2}\right)^{-1}}$

  然后与原始 SNE 一样,我们使用 K-L 散度定义目标函数进行优化,从而求解。至此,关于 t-SNE 算法的原理部分,我们就介绍完了。

t-SNE 算法过程

  • $Data: X=x_{1}, \ldots, x_{n}$
  • 计算 cost function 的参数: 困惑度 Perp
  • 优化参数:设置迭代次数 $ \mathrm{T}$ , 学习速率$ \eta$ ,动量 $ \alpha(t)$
  • 目标结果是低维数据表示 $ Y^{T}=y_{1}, \ldots, y_{n}$
  • 开始优化 结束结束
    • 计算在给定 Perp下的条件概率 $ p_{j \mid i}$ (参见上面公式)
    • 令 $ p_{i j}=\frac{p_{j\left|i+p_{i}\right| j}}{2 n}$
    • 用 $ N\left(0,10^{-4} I\right)$ 随机初始化 $ \mathrm{Y}$
    • 迭代,从 $ \mathrm{t}=1$ 到 $ \mathrm{T}$, 做如下操作:
      • 计算低维度下的 $ q_{i j}$ (参见上面的公式)
      • 计算梯度(参见上面的公式)
      • 更新 $ Y^{t}=Y^{t-1}+\eta \frac{d C}{d Y}+\alpha(t)\left(Y^{t-1}-Y^{t-2}\right)$
    • 结束

  优化过程中可以尝试的两个 trick:

  • 提前压缩 (ear1y compression) : 开始初始化的时候,各个点要离得近一点。这样小的距离,方便各个聚类中心的移动。可以通过引入 $L2$ 正则项 (距离的平方和) 来实现。
  • 提前夸大(ear1y exaggeration):在开始优化阶段, $ p_{i j}$ 乘以一个大于 1 的数进行扩大, 来避免因为 $ q_{i j}$ 太小导致优化太慢的问题。比如前 50 次迭代, $ p_{i j} $ 乘以 4。

   优化的过程动态图如下:

      

不足

  主要不足有四个:

  • 主要用于可视化,很难用于其他目的。比如测试集合降维,因为他没有显式的预估部分,不能在测试集合直接降维;比如降维到10维,因为 t 分布偏重长尾,1个自由度的t分布很难保存好局部特征,可能需要设置成更高的自由度。
  • t-SNE 倾向于保存局部特征,对于本征维数(intrinsic dimensionality)本身就很高的数据集,是不可能完整的映射到 2-3 维的空间
  • t-SNE 没有唯一最优解,且没有预估部分。如果想要做预估,可以考虑降维之后,再构建一个回归方程之类的模型去做。但是要注意,t-SNE中距离本身是没有意义,都是概率分布问题。
  • 训练太慢。有很多基于树的算法在 t-SNE 上做一些改进