个性化阅读
专注于IT技术分析

降维技术:t-SNE简介

本文概述

在本教程中, 你将了解最近发现的降维技术, 即t-分布随机邻居嵌入(t-SNE)。更具体地说, 你将:

  • 了解降维及其类型。
  • 了解主成分分析(PCA)及其在python中的用法。
  • 了解t分布随机邻居嵌入(t-SNE)及其在python中的用法。
  • 可视化这两种算法的结果。
  • 指出两种算法之间的区别。

降维

如果你以前使用过具有很多功能的数据集, 则可以了解理解或探索功能之间的关系有多么困难。这不仅使EDA过程变得困难, 而且还影响了机器学习模型的性能, 因为你可能会过度拟合模型或违反算法的某些假设, 例如线性回归中的特征独立性。这就是降维的地方。在机器学习中, 降维是通过获取一组主变量来减少所考虑的随机变量数量的过程。通过减小要素空间的维数, 可以减少要考虑的要素之间的关系, 可以轻松地对其进行探索和可视化, 并且过拟合的可能性也较小。

降维可以通过以下方式实现:

  • 消除特征:通过消除特征来减少特征空间。但是, 这样做有一个缺点, 因为你无法从已删除的那些功能中获得任何信息。
  • 功能选择:你可以应用一些统计测试, 以便根据其重要性对它们进行排名, 然后为你的工作选择功能的子集。这又遭受信息丢失的困扰, 并且不稳定, 因为不同的测试对要素的重要性得分不同。你可以在此处查看更多信息。
  • 特征提取:创建新的独立特征, 其中每个新的独立特征是每个旧的独立特征的组合。这些技术可以进一步分为线性和非线性降维技术。

主成分分析(PCA)

主成分分析或PCA是一种线性特征提取技术。它执行数据到低维空间的线性映射, 以使低维表示中的数据差异最大化。它通过从协方差矩阵计算特征向量来实现。对应于最大特征值(主成分)的特征向量用于重构原始数据方差的很大一部分。

用更简单的术语来说, PCA以特定的方式组合你的输入功能, 你可以删除最不重要的功能, 同时仍保留所有功能中最有价值的部分。另外一项好处是, PCA之后创建的每个新功能或组件都彼此独立。

t分布随机邻居嵌入(t-SNE)

t分布随机邻居嵌入(t-SNE)是一种用于降维的非线性技术, 特别适合于高维数据集的可视化。它广泛应用于图像处理, NLP, 基因组数据和语音处理。为简单起见, 这是t-SNE工作的简要概述:

  • 该算法首先计算高维空间中点的相似性概率, 然后计算对应的低维空间中点的相似性概率。点的相似度计算为如果在以A为中心的高斯(正态分布)下, 按与它们的概率密度成比例的比例选择了邻居, 则A将选择B作为邻居的条件概率。
  • 然后, 它试图最小化高维和低维空间中这些条件概率(或相似性)之间的差异, 以实现低维空间中数据点的完美表示。
  • 为了测量条件概率差之和的最小值, t-SNE使用梯度下降方法将总数据点的Kullback-Leibler散度之和最小化。

注意Kullback-Leibler散度或KL散度是衡量一个概率分布与第二个预期概率分布之间的差异的量度。

有兴趣了解算法详细工作的人士可以参考本研究论文。

用更简单的术语来说, t分布随机邻居嵌入(t-SNE)使两个分布之间的差异最小化:一个分布用于测量输入对象的成对相似性, 另一个分布用于测量嵌入中相应的低维点的成对相似性。

以此方式, t-SNE将多维数据映射到较低维空间, 并通过基于具有多个特征的数据点的相似性来识别观察到的聚类, 从而尝试在数据中查找模式。但是, 在此过程之后, 输入特征将不再可识别, 并且你不能仅基于t-SNE的输出进行任何推断。因此, 它主要是一种数据探索和可视化技术。

在Python中使用t-SNE

现在, 你将t-SNE应用于开源数据集, 并尝试可视化结果。此外, 你还将可视化同一数据集上PCA的输出, 以将其与t-SNE的输出进行比较。

你将使用的数据集是Fashion-MNIST数据集, 可在此处找到(不要忘记签出!)。 Fashion-MNIST数据集是来自10个类别的70, 000种时尚产品的28×28灰度图像, 每个类别7, 000张图像。训练集有60, 000张图像, 测试集有10, 000张图像。 Fashion-MNIST替代了原始MNIST数据集以产生更好的结果, 图像尺寸, 训练和测试分割与原始MNIST数据集相似。与MNIST相似, Fashion-MNIST也包含10个标签, 但是你没有10个不同的时尚配饰标签, 例如凉鞋, 衬衫, 裤子等, 而不是手写数字。

每个培训和测试示例都分配有以下标签之一:

  • 0 T恤/上衣
  • 1条裤子
  • 2套头衫
  • 3礼服
  • 4外套
  • 5凉鞋
  • 6衬衫
  • 7运动鞋
  • 8袋
  • 9踝靴

你可以在Github存储库的utils文件夹中找到mnist_reader.py文件。该文件具有专门用于读取.gz文件的函数load_mnist(), 或者你可以按以下方式定义该函数:

# Fashion MNIST reader

def load_mnist(path, kind='train'):
    import os
    import gzip
    import numpy as np

    """Load MNIST data from `path`"""
    labels_path = os.path.join(path, '%s-labels-idx1-ubyte.gz'
                               % kind)
    images_path = os.path.join(path, '%s-images-idx3-ubyte.gz'
                               % kind)

    with gzip.open(labels_path, 'rb') as lbpath:
        labels = np.frombuffer(lbpath.read(), dtype=np.uint8, offset=8)

    with gzip.open(images_path, 'rb') as imgpath:
        images = np.frombuffer(imgpath.read(), dtype=np.uint8, offset=16).reshape(len(labels), 784)

    return images, labels

你将在训练集数据上应用算法。使用你刚定义的load_mnist()函数, 将第一个参数作为数据文件的位置传递, 将第二个参数种类作为”训练”传递以读取训练集数据。

X_train, y_train = load_mnist('Downloads/datasets/mnist/fashion_mnist', kind='train')

你可以使用shape属性检查数据的尺寸。你会注意到训练集具有60000个采样点和784个特征。

X_train.shape
(60000, 784)

变量y_train包含每个样本的标签, 标记为0到9之间的整数。

y_train
array([9, 0, 0, ..., 3, 0, 5], dtype=uint8)

接下来, 你将导入在整个教程中将使用的必要库。为了保持可重复性, 你将定义一个随机状态变量RS并将其设置为123。

import time
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patheffects as PathEffects
%matplotlib inline

import seaborn as sns
sns.set_style('darkgrid')
sns.set_palette('muted')
sns.set_context("notebook", font_scale=1.5, rc={"lines.linewidth": 2.5})
RS = 123

为了可视化这两种算法的结果, 你将创建一个fashion_scatter()函数, 该函数带有两个参数:1. x, 它是包含算法输出的二维numpy数组, 而2. colors, 是1 -D numpy数组, 包含数据集的标签。该函数将使用与可变颜色中的类数一样多的唯一颜色来绘制散点图。

# Utility function to visualize the outputs of PCA and t-SNE

def fashion_scatter(x, colors):
    # choose a color palette with seaborn.
    num_classes = len(np.unique(colors))
    palette = np.array(sns.color_palette("hls", num_classes))

    # create a scatter plot.
    f = plt.figure(figsize=(8, 8))
    ax = plt.subplot(aspect='equal')
    sc = ax.scatter(x[:, 0], x[:, 1], lw=0, s=40, c=palette[colors.astype(np.int)])
    plt.xlim(-25, 25)
    plt.ylim(-25, 25)
    ax.axis('off')
    ax.axis('tight')

    # add the labels for each digit corresponding to the label
    txts = []

    for i in range(num_classes):

        # Position of each label at median of data points.

        xtext, ytext = np.median(x[colors == i, :], axis=0)
        txt = ax.text(xtext, ytext, str(i), fontsize=24)
        txt.set_path_effects([
            PathEffects.Stroke(linewidth=5, foreground="w"), PathEffects.Normal()])
        txts.append(txt)

    return f, ax, sc, txts

为确保你不会在计算机上浪费内存和时间, 你将仅使用前20, 000个样本来运行算法。另外, 不要忘记检查前20000个样本是否涵盖所有10个类别的样本。

# Subset first 20k data points to visualize
x_subset = X_train[0:20000]
y_subset = y_train[0:20000]

print np.unique(y_subset)
[0 1 2 3 4 5 6 7 8 9]

现在, 你将对子集数据使用PCA并可视化其输出。你将使用sklearn的PCA, 并且n_components(要保留的主要组件数)等于4。sklearn中有许多可用的参数可以调整, 但是现在, 你将使用默认值。如果你想进一步了解这些参数, 请查阅sklearn的PCA文档。

from sklearn.decomposition import PCA

time_start = time.time()

pca = PCA(n_components=4)
pca_result = pca.fit_transform(x_subset)

print 'PCA done! Time elapsed: {} seconds'.format(time.time()-time_start)
PCA done! Time elapsed: 1.55137515068 seconds

注意PCA在具有20000个样本的x_subset上运行所花费的时间。是不是很快?

现在, 你将所有四个主要组成部分存储在新的DataFrame pca_df中, 并检查由这四个组成部分解释的数据差异量。

pca_df = pd.DataFrame(columns = ['pca1', 'pca2', 'pca3', 'pca4'])

pca_df['pca1'] = pca_result[:, 0]
pca_df['pca2'] = pca_result[:, 1]
pca_df['pca3'] = pca_result[:, 2]
pca_df['pca4'] = pca_result[:, 3]

print 'Variance explained per principal component: {}'.format(pca.explained_variance_ratio_)
Variance explained per principal component: [0.29021329 0.1778743  0.06015076 0.04975864]

注意, 第一和第二主成分解释了数据x_subset中几乎48%的方差。你将通过将这两个组件传递给fashion_scatter()函数来进行可视化。

top_two_comp = pca_df[['pca1', 'pca2']] # taking first and second principal component

fashion_scatter(top_two_comp.values, y_subset) # Visualizing the PCA output
(<matplotlib.figure.Figure at 0x7f380ee716d0>, <matplotlib.axes._subplots.AxesSubplot at 0x7f380d56b710>, <matplotlib.collections.PathCollection at 0x7f380eedec90>, [Text(774.45, -689.695, u'0'), Text(42.8638, -1429.36, u'1'), Text(1098.28, 376.975, u'2'), Text(329.272, -1111.49, u'3'), Text(1266.97, 295.466, u'4'), Text(-1589.56, 61.8677, u'5'), Text(713.473, -65.687, u'6'), Text(-1521.41, 335.666, u'7'), Text(63.8177, 1003.33, u'8'), Text(-645.691, 1168.24, u'9')])
t-SNE简介1

如你所见, PCA尝试分离不同点并形成相似点的聚类组。同样, 该图可用于探索性分析。主成分(pca1, pca2等)可用作分类或聚类算法中的功能。

现在, 你将使用t-SNE算法进行相同的练习。 Scikit-learn提供了t-SNE的实现, 你可以在此处查看其文档。它为t-SNE提供了各种各样的调整参数, 最值得注意的是:

  • n_components(默认值:2):嵌入空间的尺寸。
  • 困惑度(默认值:30):困惑度与其他流形学习算法中使用的最近邻居的数量有关。考虑选择一个介于5到50之间的值。
  • early_exaggeration(默认值:12.0):控制原始空间中的自然簇在嵌入式空间中的紧度以及它们之间有多少空间。
  • learning_rate(默认值:200.0):t-SNE的学习率通常在(10.0, 1000.0)范围内。
  • n_iter(默认值:1000):优化的最大迭代次数。至少应为250。
  • 方法(默认值:” barnes_hut”):Barnes-Hut逼近以O(NlogN)时间运行。 method =” exact”将在O(N ^ 2)时间内以较慢但精确的算法运行。

你将使用默认参数在x_subset上运行t-SNE。

from sklearn.manifold import TSNE
import time
time_start = time.time()

fashion_tsne = TSNE(random_state=RS).fit_transform(x_subset)

print 't-SNE done! Time elapsed: {} seconds'.format(time.time()-time_start)
t-SNE done! Time elapsed: 1172.31058598 seconds

可以看出, 与PCA相比, 在相同样本大小的数据上执行t-SNE需要花费更长的时间。

使用fashion_scatter()函数可视化t-SNE的输出:

fashion_scatter(fashion_tsne, y_subset)
(<matplotlib.figure.Figure at 0x7f380ee71410>, <matplotlib.axes._subplots.AxesSubplot at 0x7f380ee92190>, <matplotlib.collections.PathCollection at 0x7f384c0530d0>, [Text(41.5947, 6.30529, u'0'), Text(16.6429, 61.3673, u'1'), Text(13.9577, -39.045, u'2'), Text(23.7094, 24.126, u'3'), Text(31.9231, -32.8377, u'4'), Text(-36.6758, 5.25523, u'5'), Text(16.933, -17.417, u'6'), Text(-59.4418, -11.159, u'7'), Text(-22.5197, -37.1971, u'8'), Text(-34.8161, 39.4658, u'9')])
t-SNE简介2

你会注意到, 与你之前创建的PCA可视化相比, 这已经是一项重大改进。你会看到数字非常清楚地聚集在自己的小组中。如果现在使用聚类算法来挑选单独的聚类, 则可能可以相当准确地将新点分配给标签。

Scikit-learn的t-SNE文档明确指出:

如果要素数量非常多, 强烈建议使用另一种降维方法(例如, 对于密集数据使用PCA或对于稀疏数据使用TruncatedSVD)将尺寸数量减少到合理的数量(例如50个)。这将抑制一些噪声并加快样本之间成对距离的计算。

现在, 你将牢记此建议, 实际上, 在将数据输入t-SNE算法之前, 请减少维度数。为此, 你将再次使用PCA。你将首先创建一个新数据集, 其中包含由PCA约简算法生成的五十个维度, 然后使用该数据集执行t-SNE。

time_start = time.time()

pca_50 = PCA(n_components=50)
pca_result_50 = pca_50.fit_transform(x_subset)

print 'PCA with 50 components done! Time elapsed: {} seconds'.format(time.time()-time_start)

print 'Cumulative variance explained by 50 principal components: {}'.format(np.sum(pca_50.explained_variance_ratio_))
PCA with 50 components done! Time elapsed: 2.34411096573 seconds
Cumulative variance explained by 50 principal components: 0.862512386067

现在, 你将在PCA的输出pca_result_50上应用t-SNE。

import time
time_start = time.time()


fashion_pca_tsne = TSNE(random_state=RS).fit_transform(pca_result_50)

print 't-SNE done! Time elapsed: {} seconds'.format(time.time()-time_start)
t-SNE done! Time elapsed: 491.321714163 seconds

请注意, 如何大幅减少运行t-SNE所需的时间。

fashion_scatter(fashion_pca_tsne, y_subset)
(<matplotlib.figure.Figure at 0x7f380d56be90>, <matplotlib.axes._subplots.AxesSubplot at 0x7f380d580e10>, <matplotlib.collections.PathCollection at 0x7f380ee207d0>, [Text(6.34246, 19.2471, u'0'), Text(26.9357, 58.5841, u'1'), Text(18.1853, -41.8484, u'2'), Text(50.7503, 17.6256, u'3'), Text(33.4893, -35.3055, u'4'), Text(-36.2952, 5.40452, u'5'), Text(13.7695, -19.4419, u'6'), Text(-61.3563, -12.7764, u'7'), Text(-20.3385, -34.2562, u'8'), Text(-38.9556, 44.1898, u'9')])
t-SNE简介3

你可能会注意到该绘图与上一个绘图大致相似, 但有一个不同。请注意, 与之前的绘图相比, 与标签0相对应的数据点(或与T恤/上装对应的图像)现在更紧密地聚集在一起。同样, 运行t-SNE所需的时间也减少了。

PCA与t-SNE

尽管PCA和t-SNE都有各自的优点和缺点, 但PCA和t-SNE之间的一些关键区别可以指出如下:

  • t-SNE在计算上很昂贵, 并且在数百万个样本数据集中可能花费数小时, 而PCA将在几秒钟或几分钟内完成。
  • PCA是一种数学技术, 但t-SNE是一种概率技术。
  • 像PCA一样, 线性降维算法专注于在低维表示中将相异的数据点分开放置。但是, 为了在低维, 非线性流形上表示高维数据, 必须将相似的数据点靠近在一起表示, 这是必不可少的, 这是t-SNE并非PCA。
  • 有时在t-SNE中, 具有相同超参数的不同运行可能会产生不同的结果, 因此在使用t-SNE进行任何评估之前必须观察多个图, 而对于PCA则不是这样。
  • 由于PCA是一种线性算法, 因此无法通过t-SNE来准确捕获特征之间的复杂多项式关系。

总结

恭喜!!你已经完成了本教程的结尾。你从学习降维开始, 并探索了其最受欢迎的两种技术:主成分分析和t分布随机邻居嵌入。你还将这两种算法应用于同一数据集, 并通过创建一些漂亮的图来比较了它们的结果。但是, 你只是从头开始, 因为在t-SNE中可以探索很多内容。希望本教程为你使用t-SNE的下一个旅程提供一个良好的开端。

如果你想了解有关Python的更多信息, 请参加srcmini的”使用Python进行数据可视化入门”课程。

赞(0)
未经允许不得转载:srcmini » 降维技术:t-SNE简介

评论 抢沙发

评论前必须登录!