zoukankan      html  css  js  c++  java
  • t-SNE完整笔记

    http://www.datakit.cn/blog/2017/02/05/t_sne_full.html

    t-SNE(t-distributed stochastic neighbor embedding)是用于降维的一种机器学习算法,是由 Laurens van der Maaten 和 Geoffrey Hinton在08年提出来。此外,t-SNE 是一种非线性降维算法,非常适用于高维数据降维到2维或者3维,进行可视化。

    t-SNE是由SNE(Stochastic Neighbor Embedding, SNE; Hinton and Roweis, 2002)发展而来。我们先介绍SNE的基本原理,之后再扩展到t-SNE。最后再看一下t-SNE的实现以及一些优化。

    目录

    1.SNE

    1.1基本原理

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

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

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

    1.2 SNE原理推导

    SNE是先将欧几里得距离转换为条件概率来表达点与点之间的相似度。具体来说,给定一个N个高维的数据 x1,...,xNx1,...,xN(注意N不是维度), t-SNE首先是计算概率pijpij,正比于xixi和xjxj之间的相似度(这种概率是我们自主构建的),即:

    pji=exp(∣∣xixj2/(2σ2i))kiexp(∣∣xixk2/(2σ2i))pj∣i=exp⁡(−∣∣xi−xj∣∣2/(2σi2))∑k≠iexp⁡(−∣∣xi−xk∣∣2/(2σi2))

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

    那对于低维度下的yiyi,我们可以指定高斯分布为方差为12√12,因此它们之间的相似度如下:

    qji=exp(∣∣xixj2)kiexp(∣∣xixk2)qj∣i=exp⁡(−∣∣xi−xj∣∣2)∑k≠iexp⁡(−∣∣xi−xk∣∣2)

    同样,设定qii=0qi∣i=0.

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

    C=iKL(Pi∣∣Qi)=ijpjilogpjiqjiC=∑iKL(Pi∣∣Qi)=∑i∑jpj∣ilog⁡pj∣iqj∣i

    这里的PiPi表示了给定点xixi下,其他所有数据点的条件概率分布。需要注意的是KL散度具有不对称性,在低维映射中不同的距离对应的惩罚权重是不同的,具体来说:距离较远的两个点来表达距离较近的两个点会产生更大的cost,相反,用较近的两个点来表达较远的两个点产生的cost相对较小(注意:类似于回归容易受异常值影响,但效果相反)。即用较小的 qji=0.2qj∣i=0.2 来建模较大的 pji=0.8pj∣i=0.8, cost=plog(pq)plog⁡(pq)=1.11,同样用较大的qji=0.8qj∣i=0.8来建模较大的pji=0.2pj∣i=0.2, cost=-0.277, 因此,SNE会倾向于保留数据中的局部特征

    思考:了解了基本思路之后,你会怎么选择σσ,固定初始化?

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

    Perp(Pi)=2H(Pi)Perp(Pi)=2H(Pi)

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

    H(Pi)=jpjilog2pjiH(Pi)=−∑jpj∣ilog2⁡pj∣i

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

    那么核心问题是如何求解梯度了,目标函数等价于plog(q)∑∑−plog(q)这个式子与softmax非常的类似,我们知道softmax的目标函数是ylogp∑−ylog⁡p,对应的梯度是ypy−p(注:这里的softmax中y表示label,p表示预估值)。 同样我们可以推导SNE的目标函数中的i在j下的条件概率情况的梯度是2(pijqij)(yiyj)2(pi∣j−qi∣j)(yi−yj), 同样j在i下的条件概率的梯度是2(pjiqji)(yiyj)2(pj∣i−qj∣i)(yi−yj), 最后得到完整的梯度公式如下:

    δCδyi=2j(pjiqji+pijqij)(yiyj)δCδyi=2∑j(pj∣i−qj∣i+pi∣j−qi∣j)(yi−yj)

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

    Y(t)=Y(t1)+ηδCδY+α(t)(Y(t1)Y(t2))Y(t)=Y(t−1)+ηδCδY+α(t)(Y(t−1)−Y(t−2))

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

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

    思考:SNE有哪些不足? 面对SNE的不足,你会做什么改进?

    2.t-SNE

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

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

    t-SNE在低维空间下使用更重长尾分布的t分布来避免crowding问题和优化问题。在这里,首先介绍一下对称版的SNE,之后介绍crowding问题,之后再介绍t-SNE。

    2.1 Symmetric SNE

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

    C=KL(P∣∣Q)=ijpi,jlogpijqijC=KL(P∣∣Q)=∑i∑jpi,jlog⁡pijqij

    这里的piipii,qiiqii为0,我们将这种SNE称之为symmetric SNE(对称SNE),因为他假设了对于任意i,pij=pji,qij=qjipij=pji,qij=qji,因此概率分布可以改写为:

    pij=exp(∣∣xixj2/2σ2)klexp(∣∣xkxl2/2σ2)    qij=exp(∣∣yiyj2)klexp(∣∣ykyl2)pij=exp⁡(−∣∣xi−xj∣∣2/2σ2)∑k≠lexp⁡(−∣∣xk−xl∣∣2/2σ2)    qij=exp⁡(−∣∣yi−yj∣∣2)∑k≠lexp⁡(−∣∣yk−yl∣∣2)

    这种表达方式,使得整体简洁了很多。但是会引入异常值的问题。比如xixi是异常值,那么xixj2∣∣xi−xj∣∣2会很大,对应的所有的j, pijpij都会很小(之前是仅在xixi下很小),导致低维映射下的yiyi对cost影响很小。

    思考: 对于异常值,你会做什么改进?pipi表示什么?

    为了解决这个问题,我们将联合概率分布定义修正为: pij=pij+pji2pij=pi∣j+pj∣i2, 这保证了jpij>12n∑jpij>12n, 使得每个点对于cost都会有一定的贡献。对称SNE的最大优点是梯度计算变得简单了,如下:

    δCδyi=4j(pijqij)(yiyj)δCδyi=4∑j(pij−qij)(yi−yj)

    实验中,发现对称SNE能够产生和SNE一样好的结果,有时甚至略好一点。

    2.2 Crowding问题

    拥挤问题就是说各个簇聚集在一起,无法区分。比如有一种情况,高维度数据在降维到10维下,可以有很好的表达,但是降维到两维后无法得到可信映射,比如降维如10维中有11个点之间两两等距离的,在二维下就无法得到可信的映射结果(最多3个点)。 进一步的说明,假设一个以数据点xixi为中心,半径为r的m维球(三维空间就是球),其体积是按rmrm增长的,假设数据点是在m维球中均匀分布的,我们来看看其他数据点与xixi的距离随维度增大而产生的变化。

    show png

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

    怎么解决crowding问题呢?

    Cook et al.(2007) 提出一种slight repulsion的方式,在基线概率分布(uniform background)中引入一个较小的混合因子ρρ,这样qijqij就永远不会小于2ρn(n1)2ρn(n−1) (因为一共了n(n-1)个pairs),这样在高维空间中比较远的两个点之间的qijqij总是会比pijpij大一点。这种称之为UNI-SNE,效果通常比标准的SNE要好。优化UNI-SNE的方法是先让ρρ为0,使用标准的SNE优化,之后用模拟退火的方法的时候,再慢慢增加ρρ. 直接优化UNI-SNE是不行的(即一开始ρρ不为0),因为距离较远的两个点基本是一样的qijqij(等于基线分布), 即使pijpij很大,一些距离变化很难在qijqij中产生作用。也就是说优化中刚开始距离较远的两个聚类点,后续就无法再把他们拉近了。

    2.3 t-SNE

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

    show png

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

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

    qij=(1+∣∣yiyj2)1kl(1+∣∣yiyj2)1qij=(1+∣∣yi−yj∣∣2)−1∑k≠l(1+∣∣yi−yj∣∣2)−1

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

    δCδyi=4j(pijqij)(yiyj)(1+∣∣yiyj2)1δCδyi=4∑j(pij−qij)(yi−yj)(1+∣∣yi−yj∣∣2)−1

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

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

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

    2.4 算法过程

    算法详细过程如下:

    • Data: X=x1,...,xnX=x1,...,xn
    • 计算cost function的参数:困惑度Perp
    • 优化参数: 设置迭代次数T, 学习速率ηη, 动量α(t)α(t)
    • 目标结果是低维数据表示 YT=y1,...,ynYT=y1,...,yn
    • 开始优化
      • 计算在给定Perp下的条件概率pjipj∣i(参见上面公式)
      • 令 pij=pji+pij2npij=pj∣i+pi∣j2n
      • 用 N(0,104I)N(0,10−4I) 随机初始化 Y
      • 迭代,从 t = 1 到 T, 做如下操作:
        • 计算低维度下的 qijqij(参见上面的公式)
        • 计算梯度(参见上面的公式)
        • 更新 Yt=Yt1+ηdCdY+α(t)(Yt1Yt2)Yt=Yt−1+ηdCdY+α(t)(Yt−1−Yt−2)
      • 结束
    • 结束

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

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

    优化的过程动态图如下:

    optimise

    2.5 不足

    主要不足有四个:

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

    3.变种

    后续有机会补充。

    • multiple maps of t-SNE
    • parametric t-SNE
    • Visualizing Large-scale and High-dimensional Data

    4.参考文档

    • Maaten, L., & Hinton, G. (2008). Visualizing data using t-SNE. Journal of Machine Learning Research.

    5. 代码

    文中的插图绘制:

    # coding:utf-8
    
    import numpy as np
    from numpy.linalg import norm
    from matplotlib import pyplot as plt
    plt.style.use('ggplot')
    
    def sne_crowding():
        npoints = 1000 # 抽取1000个m维球内均匀分布的点
        plt.figure(figsize=(20, 5))
        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)
            if i == 0:
                ax.set_ylabel('count')
            if i == 2:
    
                ax.set_xlabel('distance')
            ax.hist(accepts, bins=np.linspace(0., 1., 50))
            ax.set_title('m=%s' %m)
        plt.savefig("./images/sne_crowding.png")
    
        x = np.linspace(0, 4, 100)
        ta = 1 / (1 + np.square(x))
        tb = np.sum(ta) - 1
        qa = np.exp(-np.square(x))
        qb = np.sum(qa) - 1
    
    def sne_norm_t_dist_cost():
        plt.figure(figsize=(8, 5))
        plt.plot(qa/qb, c="b", label="normal-dist")
        plt.plot(ta/tb, c="g", label="t-dist")
        plt.plot((0, 20), (0.025, 0.025), 'r--')
        plt.text(10, 0.022, r'$q_{ij}$')
        plt.text(20, 0.026, r'$p_{ij}$')
    
        plt.plot((0, 55), (0.005, 0.005), 'r--')
        plt.text(36, 0.003, r'$q_{ij}$')
        plt.text(55, 0.007, r'$p_{ij}$')
    
        plt.title("probability of distance")
        plt.xlabel("distance")
        plt.ylabel("probability")
        plt.legend()
        plt.savefig("./images/sne_norm_t_dist_cost.png")
    
    if __name__ == '__main__':
        sne_crowding()
        sne_norm_t_dist_cost()
    
    

    附录一下t-sne的完整代码实现:

    # coding utf-8
    '''
    代码参考了作者Laurens van der Maaten的开放出的t-sne代码, 并没有用类进行实现,主要是优化了计算的实现
    '''
    import numpy as np
    
    
    def cal_pairwise_dist(x):
        '''计算pairwise 距离, x是matrix
        (a-b)^2 = a^w + b^2 - 2*a*b
        '''
        sum_x = np.sum(np.square(x), 1)
        dist = np.add(np.add(-2 * np.dot(x, x.T), sum_x).T, sum_x)
        return dist
    
    
    def cal_perplexity(dist, idx=0, beta=1.0):
        '''计算perplexity, D是距离向量,
        idx指dist中自己与自己距离的位置,beta是高斯分布参数
        这里的perp仅计算了熵,方便计算
        '''
        prob = np.exp(-dist * beta)
        # 设置自身prob为0
        prob[idx] = 0
        sum_prob = np.sum(prob)
        perp = np.log(sum_prob) + beta * np.sum(dist * prob) / sum_prob
        prob /= sum_prob
        return perp, prob
    
    
    def seach_prob(x, tol=1e-5, perplexity=30.0):
        '''二分搜索寻找beta,并计算pairwise的prob
        '''
    
        # 初始化参数
        print("Computing pairwise distances...")
        (n, d) = x.shape
        dist = cal_pairwise_dist(x)
        pair_prob = np.zeros((n, n))
        beta = np.ones((n, 1))
        # 取log,方便后续计算
        base_perp = np.log(perplexity)
    
        for i in range(n):
            if i % 500 == 0:
                print("Computing pair_prob for point %s of %s ..." %(i,n))
    
            betamin = -np.inf
            betamax = np.inf
            perp, this_prob = cal_perplexity(dist[i], i, beta[i])
    
            # 二分搜索,寻找最佳sigma下的prob
            perp_diff = perp - base_perp
            tries = 0
            while np.abs(perp_diff) > tol and tries < 50:
                if perp_diff > 0:
                    betamin = beta[i].copy()
                    if betamax == np.inf or betamax == -np.inf:
                        beta[i] = beta[i] * 2
                    else:
                        beta[i] = (beta[i] + betamax) / 2
                else:
                    betamax = beta[i].copy()
                    if betamin == np.inf or betamin == -np.inf:
                        beta[i] = beta[i] / 2
                    else:
                        beta[i] = (beta[i] + betamin) / 2
    
                # 更新perb,prob值
                perp, this_prob = cal_perplexity(dist[i], i, beta[i])
                perp_diff = perp - base_perp
                tries = tries + 1
            # 记录prob值
            pair_prob[i,] = this_prob
        print("Mean value of sigma: ", np.mean(np.sqrt(1 / beta)))
        return pair_prob
    
    
    def pca(x, no_dims = 50):
        ''' PCA算法
        使用PCA先进行预降维
        '''
        print("Preprocessing the data using PCA...")
        (n, d) = x.shape
        x = x - np.tile(np.mean(x, 0), (n, 1))
        l, M = np.linalg.eig(np.dot(x.T, x))
        y = np.dot(x, M[:,0:no_dims])
        return y
    
    
    def tsne(x, no_dims=2, initial_dims=50, perplexity=30.0, max_iter=1000):
        """Runs t-SNE on the dataset in the NxD array x
        to reduce its dimensionality to no_dims dimensions.
        The syntaxis of the function is Y = tsne.tsne(x, no_dims, perplexity),
        where x is an NxD NumPy array.
        """
    
        # Check inputs
        if isinstance(no_dims, float):
            print("Error: array x should have type float.")
            return -1
        if round(no_dims) != no_dims:
            print("Error: number of dimensions should be an integer.")
            return -1
    
        # 初始化参数和变量
        x = pca(x, initial_dims).real
        (n, d) = x.shape
        initial_momentum = 0.5
        final_momentum = 0.8
        eta = 500
        min_gain = 0.01
        y = np.random.randn(n, no_dims)
        dy = np.zeros((n, no_dims))
        iy = np.zeros((n, no_dims))
        gains = np.ones((n, no_dims))
    
        # 对称化
        P = seach_prob(x, 1e-5, perplexity)
        P = P + np.transpose(P)
        P = P / np.sum(P)
        # early exaggeration
        P = P * 4
        P = np.maximum(P, 1e-12)
    
        # Run iterations
        for iter in range(max_iter):
            # Compute pairwise affinities
            sum_y = np.sum(np.square(y), 1)
            num = 1 / (1 + np.add(np.add(-2 * np.dot(y, y.T), sum_y).T, sum_y))
            num[range(n), range(n)] = 0
            Q = num / np.sum(num)
            Q = np.maximum(Q, 1e-12)
    
            # Compute gradient
            PQ = P - Q
            for i in range(n):
                dy[i,:] = np.sum(np.tile(PQ[:,i] * num[:,i], (no_dims, 1)).T * (y[i,:] - y), 0)
    
            # Perform the update
            if iter < 20:
                momentum = initial_momentum
            else:
                momentum = final_momentum
            gains = (gains + 0.2) * ((dy > 0) != (iy > 0)) + (gains * 0.8) * ((dy > 0) == (iy > 0))
            gains[gains < min_gain] = min_gain
            iy = momentum * iy - eta * (gains * dy)
            y = y + iy
            y = y - np.tile(np.mean(y, 0), (n, 1))
            # Compute current value of cost function
            if (iter + 1) % 100 == 0:
                if iter > 100:
                    C = np.sum(P * np.log(P / Q))
                else:
                    C = np.sum( P/4 * np.log( P/4 / Q))
                print("Iteration ", (iter + 1), ": error is ", C)
            # Stop lying about P-values
            if iter == 100:
                P = P / 4
        print("finished training!")
        return y
    
    
    if __name__ == "__main__":
        # Run Y = tsne.tsne(X, no_dims, perplexity) to perform t-SNE on your dataset.
        X = np.loadtxt("mnist2500_X.txt")
        labels = np.loadtxt("mnist2500_labels.txt")
        Y = tsne(X, 2, 50, 20.0)
        from matplotlib import pyplot as plt
        plt.scatter(Y[:,0], Y[:,1], 20, labels)
        plt.show()
    

     

    https://yq.aliyun.com/articles/389822?utm_content=m_40767

    假设你有一个包含数百个特征(变量)的数据集,却对数据所属的领域几乎没有什么了解。 你需要去识别数据中的隐藏模式,探索和分析数据集。不仅如此,你还必须找出数据中是否存在模式--用以判定数据是有用信号还是噪音?

    这是否让你感到不知所措?当我第一次遇到这种情况,我简直全身发麻。想知道如何挖掘一个多维数据集? 这是许多数据科学家经常问的问题之一。 该篇文章中,我将带你通过一个强有力的方式来实现这一点。用PCA怎么样?

    现在,一定会有很多人心里想着“我会使用PCA来降维和可视化”。 好吧,你是对的! PCA绝对是具有大量特征的数据集的降维和可视化的不错选择。 但是,假如你能使用比PCA更先进的东西将会怎样呢?

    如果你可以很容易地找出非线性的模式呢? 在本文中,我将告诉你一个比PCA(1933)更有效、被称为t-SNE(2008)的新算法。 首先我会介绍t-SNE算法的基础知识,然后说明为什么t-SNE是非常适合的降维算法。

    你还将获得在R代码和Python语句中使用t-SNE的实践知识。

    来吧来吧!

    目录

    1.什么是t-SNE?

    2.什么是降维?

    3.t-SNE与其他降维算法

    4.t-SNE的算法细节

     4.1 算法

     4.2 时间和空间复杂性

    5.t-SNE实际上做什么?

    6.用例

    7.t-SNE与其他降维算法相比

    8.案例实践

     8.1 使用R代码

    • 超参数调试
    • 代码
    • 执行时间
    • 结果解读

     8.2 使用python语句

    • 超参数调试
    • 代码
    • 执行时间

    9.何时何地去使用

     9.1 数据科学家

     9.2 机器学习竞赛爱好者

     9.3 数据科学爱好者

    10.常见误区

    1.什么是t-SNE

     

    e8e556e681d7318d5bc3b82af54c9dfa58ac3a0c

    (t-SNE)t-分布式随机邻域嵌入是一种用于挖掘高维数据的非线性降维算法。 它将多维数据映射到适合于人类观察的两个或多个维度。 在t-SNE算法的帮助下,你下一次使用高维数据时,可能就不需要绘制很多探索性数据分析图了。

     2.什么是降维?

    为了理解t-SNE如何工作,让我们先了解什么是降维?

    简而言之,降维是在2维或3维中展现多维数据(具有多个特征的数据,且彼此具有相关性)的技术。

    有些人可能会问,当我们可以使用散点图、直方图和盒图绘制数据,并用描述性统计搞清数据模式的时候为什么还需要降低维度。

    好吧,即使你可以理解数据中的模式并将其呈现在简单的图表上,但是对于没有统计背景的人来说,仍然很难理解它。 此外,如果你有数百个特征值,你必须研究数千张图表,然后才能搞懂这些数据。

    在降维算法的帮助下,您将能够清晰地表达数据。

    3. t-SNE与其他降维算法

    现在你已经了解什么是降维,让我们看看我们如何使用t-SNE算法来降维。

    以下是几个你可以查找到的降维算法:

    1. 主成分分析(线性)
    2. t-SNE(非参数/非线性)
    3. 萨蒙映射(非线性)
    4. 等距映射(非线性)
    5. 局部线性嵌入(非线性)
    6. 规范相关分析(非线性)
    7. SNE(非线性)
    8. 最小方差无偏估计(非线性)
    9. 拉普拉斯特征图(非线性)

    好消息是,你只需要学习上述算法中的其中两种,就可以有效地在较低维度上使数据可视化 - PCA和t-SNE。

    PCA的局限性

    PCA是一种线性算法。 它不能解释特征之间的复杂多项式关系。 另一方面,t-SNE是基于在邻域图上随机游走的概率分布,可以在数据中找到其结构关系。

    线性降维算法的一个主要问题是它们集中将不相似的数据点放置在较低维度区域时,数据点相距甚远。 但是为了在低维、非线性流型上表示高维数据,我们也需要把相似的数据点靠近在一起展示,这并不是线性降维算法所能做的。

    现在,你对PCA应该有了一个简短的了解。

    局部方法寻求将流型上的附近点映射到低维表示中的附近点。 另一方面,全局方法试图保留所有尺度的几何形状,即将附近的点映射到附近的点,将远处的点映射到远处的点

    要知道,除t-SNE之外的大多数非线性技术都不能同时保留数据的局部和全局结构。

    4. t-SNE的算法细节(选读)

    该部分是为有兴趣深入理解算法的人准备的。 如果您不想了解数学上面的细节,可以放心地跳过本节。

    4.1算法

    步骤1

    随机邻近嵌入(SNE)首先通过将数据点之间的高维欧几里得距离转换为表示相似性的条件概率。数据点gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAA与数据点gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAA的相似性是条件概率gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAA——如果邻域被选择与在以gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAA为中心的正态分布的概率密度成比例,gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAA将选择gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAA作为其邻域的概率。

    ebb1c5edd82d15ed8526e870fa5d15cb9cc90352

    其中640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=是以数据点640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=为中心的正态分布的方差,如果你对数学不感兴趣,以这种方式思考它,算法开始于将点之间的最短距离(直线)转换成点的相似度的概率。 其中,点之间的相似性是: 如果在以640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=为中心的高斯(正态分布)下与邻域的概率密度成比例地选取邻域,则640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=会选择640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=作为其邻居的条件概率。

    步骤2

    对于低维数据点640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=的高维对应点640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=,可以计算类似的条件概率,其由640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=表示。

    f5757ab6c47fa29665bb6e2230bca98696d60b91

    需要注意的是,pi | i和pj | j被设置为零,因为我们只想对成对的相似性进行建模。

    简单来说,步骤1和步骤2计算一对点之间的相似性的条件概率。这对点存在于:

    1. 高维空间中
    2. 低维空间中

    为了简单起见,尝试详细了解这一点。

    让我们把3D空间映射到2D空间。 步骤1和步骤2正在做的是计算3D空间中的点的相似性的概率,并计算相应的2D空间中的点的相似性的概率。

    逻辑上,条件概率640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=必须相等,以便把具有相似性的不同维空间中的数据点进行完美表示。即,640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=之间的差必须为零,以便在高维和低维中完美复制图。

    通过该逻辑,SNE试图使条件概率的这种差异最小化。

    步骤3

    现在讲讲SNE和t-SNE算法之间的区别。

    为了测量条件概率SNE差值的总和的最小化,在全体数据点中使用梯度下降法使所有数据点的Kullback-Leibler散度总和减小到最小。 我们必须知道,K-L散度本质上是不对称的。

    换句话说,SNE代价函数重点在映射中保留数据的局部结构(为了高斯方差在高维空间的合理性,640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=)。

    除此之外,优化该代价函数是非常困难的(计算效率低)。

    因此,t-SNE也尝试最小化条件概率之差的总和值。 但它通过使用对称版本的SNE代价函数,使用简单的梯度。此外,t-SNE在低维空间中采用长尾分布,以减轻拥挤问题(参考下面译者解释)和SNE的优化问题。

    *译者注:

    拥挤问题是提出t-SNE算法的文章(Visualizing Data using t-SNE,08年发表在Journal of Machine Learning Research,大神Hinton的文章)重点讨论的问题(文章的3.2节)。译者的理解是,如果想象在一个三维的球里面有均匀分布的点,如果把这些点投影到一个二维的圆上一定会有很多点是重合的。所以在二维的圆上想尽可能表达出三维里的点的信息,把由于投影所重合的点用不同的距离(差别很小)表示,这样就会占用原来在那些距离上的点,原来那些点会被赶到更远一点的地方。t分布是长尾的,意味着距离更远的点依然能给出和高斯分布下距离小的点相同的概率值。从而达到高维空间和低维空间对应的点概率相同的目的。

    步骤4

    如果我们看到计算条件概率的方程,我们忽略了现在的讨论的方差。要选择的剩余参数是学生的t-分布的方差640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=,其中心在每个高维数据点640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=的中心。不可能存在对于数据集中的所有数据点最优的单个值640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=,因为数据的密度可能变化。在密集区域中,较小的值640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=通常与较稀疏的区域相比更合适。任何特定值640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=在所有其他数据点上诱发概率分布640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=。 这个分布有一个

    c8212f64339ad90544fc67e5ddd00ea15b00c39c

    该分布具有随着gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAA增加而增加的熵。 t-SNE对gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAA的值执行二进制搜索,产生具有由用户指定具有困惑度的gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAA2。 该困惑度定义为

    45c9d6466a1c43e50e8a23013890f80ff728c240

    其中H(640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=)是以比特字节测量的香农熵

    4909e12dfd5d0cced685be93d24bb8c26fcc2be5

    困惑度可以被解释为对邻域的有效数量的平滑测量。 SNE的性能对于茫然性的变化是相当稳固的,并且典型值在5和50之间。

    代价函数的最小化是使用梯度下降法来执行的。并且从物理上,梯度可以被解释为由图上定位点640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=和所有其他图上定位点640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=之间的一组弹簧产生的合力。所有弹簧沿着方向(640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy= - 640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=)施加力。弹簧在640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=定位点之间的排斥或吸引,取决于图中的两点之间的距离是太远还是太近 (太远和太近都不能表示两个高维数据点之间的相似性。)由弹簧在640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=之间施加的力与其长度成比例,并且还与其刚度成比例,刚度是数据的成对相似性之间的失配(pj | i-qj | i + pi | j-qi | j) 点和地图点 。

     *译者补充:

    步骤3和4都在讲述SNE 与t-SNE之间的区别,总结如下:

    区别一:将不对称的代价函数改成对称的代价函数。

    将代价函数修改为40804d1fe52bbef8eaa03c809d365d725fe148dd,其中9e13136309580387e3b636004af8730175b79451,则可避免上述不对称的代价函数所带来的问题。

    区别二:在低维空间中使用学生t-分布而不是高斯分布来计算点与点之间的相似度。

    t-SNE在低维空间中采用长尾的学生t-分布,02b033fab3fdb6fe5caf21c57f604c9cb74d92b5以减轻拥挤问题和SNE的优化问题。

    4.2 时间和空间复杂度

    现在我们已经了解了算法,是分析其性能的时候了。 正如你可能已经观察到的,该算法计算成对的条件概率,并试图最小化较高和较低维度的概率差的总值。 这涉及大量的运算和计算。 所以该算法对系统资源相当重要。

    t-SNE在数据点的数量上具有二次时间和空间复杂性。 这使得它应用于超过10,000个观察对象组成的数据集的时候特别慢和特别消耗资源。

     5. t-SNE 实际上做了什么?

    了解了 t-SNE 算法的数学描述及其工作原理之后,让我们总结一下前边学过的东西。以下便是t-SNE工作原理的简述。

    实际上很简单。 非线性降维算法t-SNE通过基于具有多个特征的数据点的相似性识别观察到的模式来找到数据中的规律。它不是一个聚类算法,而是一个降维算法。这是因为当它把高维数据映射到低维空间时,原数据中的特征值不复存在。所以不能仅基于t-SNE的输出进行任何推断。因此,本质上它主要是一种数据探索和可视化技术。

    但是t-SNE可以用于分类器和聚类中,用它来生成其他分类算法的输入特征值。

    6. 应用场景

    你可能会问, t-SNE有哪些应用场景呢?它几乎可以用于任何高维数据。不过大部分应用集中在图像处理,自然语言处理,基因数据以及语音处理。它还被用于提高心脑扫描图像的分析。以下维几个实例:

    6.1 人脸识别

    人脸识别技术已经取得巨大进展,很多诸如PCA之类的算法也已经在该领域被研究过。但是由于降维和分类的困难,人脸识别依然具有挑战性。t-SNE被用于高维度数据降维,然后用其它算法,例如 AdaBoostM2, 随机森林, 逻辑回归, 神经网络等多级分类器做表情分类。

    一个人脸识别的研究采用了日本女性脸部表情数据库和t-SNE结合AdaBoostM2的方法。其实验结果表明这种新方法效果优于诸如PCA, LDA, LLE及SNE的传统算法。

    以下为实现该方法的流程图: 

    d40cdf28684eb888fc144953fb1a5f44d6a87292

    6.2 识别肿瘤亚群(医学成像)

    质谱成像(MSI)是一种同时提供组织中数百个生物分子的空间分布的技术。 t-SNE,通过数据的非线性可视化,能够更好地解析生物分子肿瘤内异质性。

    以无偏见的方式,t-SNE可以揭示肿瘤亚群,它们与胃癌患者的存活和乳腺癌患者原发性肿瘤的转移状态具有统计相关性。 对每个t-SNE簇进行的存活分析将提供非常有用的结果。[3] 

    6.3 使用wordvec的文本比较

    词向量表示法捕获许多语言属性,如性别,时态,复数甚至语义概念,如“首都城市”。 使用降维,可以计算出使语义相似的词彼此临近的2D地图。 这种技术组合可以用于提供不同文本资料的鸟瞰图,包括文本摘要及其资料源。 这使用户能够像使用地图一样探索文本资料。[4]

     7. t-SNE与其它降维算法的对比

    下边我们将要比较t-SNE和其它算法的性能。这里的性能是基于算法所达到的准确度,而不是时间及运算资源的消耗与准确度之间的关系。

    t-SNE产生的结果优于PCA和其它线性降维模型。这是因为诸如经典缩放的线性方法不利于建模曲面的流型。 它专注于保持远离的数据点之间的距离,而不是保留临近数据点之间的距离。

    t-SNE在高维空间中采用的高斯核心函数定义了数据的局部和全局结构之间的软边界。对于高斯的标准偏差而言彼此临近的数据点对,对它们的间隔建模的重要性几乎与那些间隔的大小无关。 此外,t-SNE基于数据的局部密度(通过强制每个条件概率分布具有相同的困惑度)分别确定每个数据点的局部邻域大小[1]。 这是因为算法定义了数据的局部和全局结构之间的软边界。 与其他非线性降维算法不同,它的性能优于其它任何一个算法。 

     8. 案例实践

    让我们用MNIST手写数字数据库来实现t-SNE算法。 这是最被广泛探索的图像处理的数据集之一。

    81.使用R代码

    “Rtsne”包具有t-SNE在R语言中的实现。“Rtsne”包可以通过在R控制台中键入以下命令安装:

    
    
    
    
    • 超参数调试

    92778a3c0a11fef479bf88d754499d588e46adca

    • 代码

    MNIST数据可从MNIST网站下载,并可用少量代码转换为csv文件。对于此示例,请下载以下经过预处理的MNIST数据。

    
    
    
    
    •  执行时间
    
    
    

    可以看出,运行于相同样本规模的数据,与PCA相比t-SNE所需时间明显更长。

    • 解读结果

    这些图可用于探索性分析。 输出的x和y坐标以及成本代价值可以用作分类算法中的特征值

    988c96da554ec7785592e891a13df5af07604da1

    8.2使用Rython语句

    一个重要的事情要注意的是“pip install tsne”会产生错误。 不建议安装“tsne”包。 t-SNE算法可以从sklearn包中访问。

    • 超参数调试

    68248d6912d110f229885e0aae66c8db4c17a567

    • 代码

    以下代码引自sklearn网站的sklearn示例。

    
    
    
    
    • 执行时长
    Tsne: 13.40 s
    PCA: 0.01 s

    PCA结果图(时长0.01s)

    adf87ad6054d10442a04de9fe2410a2905fc4ef8

    t-SNE结果图

    d1c61095726880586731041ba09c8cb97a4d69f5

    9.何时何地使用t-SNE?

    9.1 数据科学家

    对于数据科学家来说,使用t-SNE的主要问题是算法的黑盒类型性质。这阻碍了基于结果提供推论和洞察的过程。此外,该算法的另一个问题是它不一定在连续运行时永远产生类似的输出。

    那么,你怎么能使用这个算法?最好的使用方法是用它进行探索性数据分析。 它会给你非常明确地展示数据内隐藏的模式。它也可以用作其他分类和聚类算法的输入参数。

    9.2机器学习竞赛爱好者

    将数据集减少到2或3个维度,并使用非线性堆栈器将其堆栈。 使用保留集进行堆叠/混合。 然后你可以使用XGboost提高t-SNE向量以得到更好的结果。

    9.3数据科学爱好者

    对于才开始接触数据科学的数据科学爱好者来说,这种算法在研究和性能增强方面提供了最好的机会。已经有一些研究论文尝试通过利用线性函数来提高算法的时间复杂度。但是尚未得到理想的解决方案。针对各种实施t-SNE算法解决自然语言处理问题和图像处理应用程序的研究论文是一个尚未开发的领域,并且有足够的空间范围。

    10.常见错误

    以下是在解释t-SNE的结果时要注意的几个点:

    1. 为了使算法正确执行,困惑度应小于数据点数。 此外,推荐的困惑度在(5至50)范围内
    2. 有时,具有相同超参数的多次运行结果可能彼此不同。
    3. 任何t-SNE图中的簇大小不得用于标准偏差,色散或任何其他诸如此类的度量。这是因为t-SNE扩展更密集的集群,并且使分散的集群收缩到均匀的集群大小。 这是它产生清晰的地块的原因之一。
    4. 簇之间的距离可以改变,因为全局几何与最佳困惑度密切相关。 在具有许多元素数量不等的簇的数据集中,同一个困惑度不能优化所有簇的距离。
    5. 模式也可以在随机噪声中找到,因此在决定数据中是否存在模式之前,必须检查具有不同的超参数组的多次运算结果。
    6. 在不同的困惑度水平可以观察到不同的簇形状。
    7. 拓扑不能基于单个t-SNE图来分析,在进行任何评估之前必须观察多个图。

    参考资料

    [1] L.J.P. van der Maaten and G.E. Hinton. Visualizing High-Dimensional Data Using  t-SNE. Journal of Machine Learning Research 9(Nov):2579-2605, 2008

    [2] Jizheng Yi et.al. Facial expression recognition Based on t-SNE and AdaBoostM2.

    IEEE International Conference on Green Computing and Communications and IEEE Internet of Things and IEEE Cyber,Physical and Social Computing (2013)

    [3]  Walid M. Abdelmoulaa et.al. Data-driven identification of prognostic tumor subpopulations using spatially mapped t-SNE of mass spectrometry imaging data.

    12244–12249 | PNAS | October 25, 2016 | vol. 113 | no. 43

    [4]  Hendrik Heuer. Text comparison using word vector representations and dimensionality reduction. 8th EUR. CONF. ON PYTHON IN SCIENCE (EUROSCIPY 2015)

  • 相关阅读:
    Google Maps Android API v2 开发笔记
    eclipse快捷键设置
    浮动div,回到顶部
    android开发环境
    Android百度地图开发之地址解析MKSearch.geocode()
    java基础(for循环)
    博客园首记
    记录有待阅读的文章——2013.2.2
    整理推荐的CSS属性书写顺序
    JavaScript——Firebug控制台详解
  • 原文地址:https://www.cnblogs.com/bnuvincent/p/9612652.html
Copyright © 2011-2022 走看看