用Python示例介绍t-SNE


在这篇文章中,我将对t-SNE算法进行高级概述。我还将分享一些示例python代码,我将在Digits和MNIST数据集上使用t-SNE。

什么是t-SNE?

t-分布随机邻域嵌入(t-SNE)是一种无监督的非线性技术,主要用于数据探索和可视化高维数据。简单来说,t-SNE让您对数据如何在高维空间中排列有一种感觉或直觉。它由Laurens van der Maatens和Geoffrey Hinton于2008年开发。

t-SNE与PCA

如果您熟悉主成分分析(PCA),那么像我一样,您可能想知道PCA和t-SNE之间的区别。首先要注意的是PCA是在1933年开发的,而t-SNE是在2008年开发的。自1933年以来,数据科学领域的变化主要发生在计算和数据大小领域。其次,PCA是一种线性降维技术,旨在最大化方差并保留大的成对距离。换句话说,不同的东西最终相差甚远。这可能导致可视性差,尤其是在处理非线性流形结构时。将多种结构视为任何几何形状,如:圆柱体,球体,曲线等。

t-SNE与PCA的不同之处仅在于保留了小的成对距离或局部相似性,而PCA则关注保留大的成对距离以最大化方差。Laurens使用图1中的Swiss Roll数据集很好地说明了PCA和t-SNE方法。您可以看到,由于此数据集(流形)的非线性并且保留较大距离,PCA将错误地保留数据结构。

用Python示例介绍t-SNE

图1 - Swiss Roll数据集。使用t-SNE(实线)与最大化方差PCA保持小距离

t-SNE如何运作

既然我们知道为什么要用t-SNE而不是PCA,让我们来讨论t-SNE是如何工作的。t-SNE算法在高维空间和低维空间中计算实例对之间的相似性度量。然后尝试使用成本函数来优化这两个相似度度量。让我们把它分成三个基本步骤。

1、第一步, 测量高维空间中点之间的相似性。考虑一堆分散在二维空间中的数据点(图2)。对于每个数据点(xi),我们将在该点上以高斯分布为中心。然后我们测量所有点(xj)在高斯分布下的密度。然后对所有点重新命名。这就给出了所有点的一组概率(Pij)。这些概率与相似性成正比。这意味着,如果数据点x1和x2在这个高斯圆下有相等的值那么它们的比例和相似性是相等的因此在这个高维空间的结构中有局部相似性。高斯分布或圆可以使用所谓的perple来操作,它影响分布的方差(圆形大小)和最近邻的数量。困惑的正常范围是5到50。

用Python示例介绍t-SNE

图2 - 测量高维空间中的成对相似性

2.第二步 类似于步骤1,但不使用高斯分布,而是使用具有一个自由度的Student t分布,这也称为Cauchy分布(图3)。这为我们提供了低维空间中的第二组概率(Qij)。正如您所看到的,Student t分布的尾部比正态分布更重。重尾可以更好地模拟远距离。

用Python示例介绍t-SNE

图3 - Normal 与Student t分布

3.最后一步 是,我们希望这些低维空间(Qij)的概率集尽可能地反映高维空间(Pij)的概率集。我们希望这两个映射结构是相似的。我们使用Kullback-Liebler散度(KL)测量二维空间的概率分布之间的差异。最后,我们使用梯度下降法来最小化KL成本函数。

用于t-SNE的用例

现在您已经了解了t-SNE的工作原理,让我们快速谈谈它的使用场景。Laurens van der Maaten在他的视频演示中展示了很多例子。他提到在气候研究,计算机安全,生物信息学,癌症研究等领域使用t-SNE。t-SNE可用于高维数据,然后这些维度的输出成为其他分类模型的输入。

同样,t-SNE也可以用于调查、学习或评估分割。通常情况下,我们在建模之前选择段的数量,或者在结果之后迭代。t-SNE常常在数据中显示明显的分离。这可以在使用分段模型选择一个聚类数或评估您的分段实际上是否成立之后使用。然而,t-SNE并不是一种聚类方法,因为它不保留PCA这样的输入,而且在运行之间值常常会发生变化,所以它纯粹是用于探索。

Python代码示例

下面是一些python代码,您可以在其中看到Digits和MNIST数据集上PCA和t-SNE之间的视觉比较。由于维度差异以及结果的差异,我选择了这两个数据集。我还在代码中展示了一种技术,您可以在运行t-SNE之前运行PCA。这样做可以减少计算,通常可以减少到~30维,然后运行t-SNE。

我使用python运行它并调用SAS库(http://go.documentation.sas.com/?cdcId=pgmcdc&cdcVersion=8.11&docsetId=allprodsactions&docsetTarget=actionSetsByName.htm&locale=en)。它可能看起来与您使用的略有不同,您可以在下面的图像中看到它。我使用Seaborn作为我认为很棒的视觉效果,但是使用t-SNE你可能会得到非常紧凑的clusters 并且需要放大。如果你需要放大或操纵你的绘图对象,另一个可视化工具,如Plotly,可能会更好。此外,%matplotlib notebook在绘制之前进行简单的调用也可以使用Matplotlib。

第1步 - 加载Python库。

创建与SAS服务器的连接(称为“CAS”,这是一个分布式内存引擎)。加载CAS操作集(将这些设置视为库)。读入数据并查看shape。

# Load Python Libraries

import swat

import pandas as pd

import numpy as np

import matplotlib.pyplot as plt

import seaborn as sns

from time import time

%matplotlib inline

# Create CAS Connection# Creat

conn = swat.CAS(host, portnum, protocol='http')

conn.sessionProp.setSessOpt(messageLevel='NONE'); # Suppress CAS Messages

# Load CAS Action Sets

conn.loadactionset('pca')

conn.loadactionset('tsne');

# Read in Data# Read

digits = pd.read_csv('/Users/anviol/Desktop/Content/Datasets/Digits/digits.csv')

mnist = pd.read_csv('/Users/anviol/Desktop/Content/Datasets/mnist/small_mnist.csv')

display(digits.shape)

display(mnist.shape)

(1083, 66)

(5921, 786)

第2步 - 到目前为止,我仍在使用本地计算机。

我要将这些数据加载到我提到的CAS服务器中。这有助于我利用分布式环境并提高性能效率。然后我对Digits和MNIST数据进行PCA分析。

# Upload Digits and MNIST Data to CAS Server

digits_cas = conn.upload_frame(digits, casout=dict(name='digits_df', replace=True))

mnist_cas = conn.upload_frame(mnist, casout=dict(name='mnist_df', replace=True));

# Perform PCA: Digits

pca_digits = conn.eig(table='digits_df',

n=30,

inputs=list(digits.drop(['ID', 'Label'], axis=1)),

output={'casOut':{'name':'pca_digits','replace':'TRUE'},

'copyVars':['ID','Label']})

# Perform PCA: MNIST

pca_mnist = conn.eig(table='mnist_df',

n=30,

output={'casOut':{'name':'pca_mnist','replace':'TRUE'},

'copyVars':['ID','Label']})

第3步 - 显示Digits和MNIST的PCA结果

# Plot Digits PCA

# Set style of scatterplot

sns.set_context("notebook", font_scale=1.1)

sns.set_style("ticks")

# Create scatterplot of dataframe

sns.lmplot(x='Score1',

y='Score2',

data=pca_digits_out,

fit_reg=False,

legend=True,

size=9,

hue='Label',

scatter_kws={"s":200, "alpha":0.3})

plt.title('PCA Results: Digits', weight='bold').set_fontsize('14')

plt.xlabel('Prin Comp 1', weight='bold').set_fontsize('10')

plt.ylabel('Prin Comp 2', weight='bold').set_fontsize('10')

用Python示例介绍t-SNE

PCA实际上在Digits数据集和查找结构上做得不错

# Plot MNIST PCA

sns.set_context("notebook", font_scale=1.1)

sns.set_style("ticks")

sns.lmplot(x='Score1',

y='Score2',

data=pca_mnist_out,

fit_reg=False,

legend=True,

size=9,

hue='Label',

scatter_kws={"s":200, "alpha":0.3})

plt.title('PCA Results: MNIST', weight='bold').set_fontsize('14')

plt.xlabel('Prin Comp 1', weight='bold').set_fontsize('10')

plt.ylabel('Prin Comp 2', weight='bold').set_fontsize('10')

用Python示例介绍t-SNE

正如你在MNIST数据集上看到的,PCA有一个“拥挤”的问题

第4步 - 现在让我们尝试与上面相同的步骤,但使用t-SNE算法

# Perform t-SNE: Digits

tsne_digits = conn.tSne(table='digits_df',

inputs = list(digits.drop(['ID', 'Label'], axis=1)),

nDimensions = 2,

maxIters = 1000,

perplexity = 100,

learningRate = 1000,

seed = 24680,

output= {'casout':{'name':'tsne_digits','replace':'TRUE'},

'copyVars':['ID','Label']})

# Move Scored Dataframes to Pandas Dataframes for Plotting

tsne_digits_out = conn.fetch(table='tsne_digits', maxrows=len(mnist_cas), to=len(mnist_cas))

tsne_digits_out = pd.DataFrame(tsne_digits_out['Fetch'])

tsne_digits_out['Label'] = tsne_digits_out['Label'].astype(int)

# Plot Digits t-SNE

sns.set_context("notebook", font_scale=1.1)

sns.set_style("ticks")

sns.lmplot(x='_DIM_1_',

y='_DIM_2_',

data=tsne_digits_out,

fit_reg=False,

legend=True,

size=9,

hue='Label',

scatter_kws={"s":200, "alpha":0.3})

plt.title('t-SNE Results: Digits', weight='bold').set_fontsize('14')

plt.xlabel('Dimension 1', weight='bold').set_fontsize('10')

plt.ylabel('Dimension 2', weight='bold').set_fontsize('10')

用Python示例介绍t-SNE

现在对于MNIST数据集......

# Perform t-SNE: MNIST

tsne_MNIST = conn.tSne(table='mnist_df',

inputs = list(mnist.drop(['ID', 'Label'], axis=1)),

nDimensions = 2,

maxIters = 1000,

perplexity = 100,

learningRate = 1000,

seed = 24680,

output= {'casout':{'name':'tsne_MNIST','replace':'TRUE'},

'copyVars':['ID','Label']})

# Pandas Plotting Dataframe

tsne_MNIST_out = conn.fetch(table='tsne_MNIST', maxrows=len(mnist_cas), to=len(mnist_cas))

tsne_MNIST_out = pd.DataFrame(tsne_MNIST_out['Fetch'])

tsne_MNIST_out['Label'] = tsne_MNIST_out['Label'].astype(int)

# Plot MNIST t-SNE

sns.set_context("notebook", font_scale=1.1)

sns.set_style("ticks")

sns.lmplot(x='_DIM_1_',

y='_DIM_2_',

data=tsne_MNIST_out,

fit_reg=False,

legend=True,

size=9,

hue='Label',

scatter_kws={"s":200, "alpha":0.3})

plt.title('t-SNE Results: MNIST', weight='bold').set_fontsize('14')

plt.xlabel('Dimension 1', weight='bold').set_fontsize('10')

plt.ylabel('Dimension 2', weight='bold').set_fontsize('10')

用Python示例介绍t-SNE

查看GitHub中的完整笔记本:https://github.com/aviolante/sas-python-work/blob/master/tSneExampleBlogPost.ipynb