Python实现t-分布随机邻域嵌入(t-SNE)降维算法

devtools/2024/11/19 8:43:32/

目录

      • Python实现t-分布随机邻域嵌入(t-SNE)降维算法的博客
        • 引言
        • t-SNE算法原理
        • t-SNE的优势与局限
        • Python实现t-SNE算法
          • 1. 创建t-SNE类
          • 2. 示例场景:MNIST手写数字数据集
          • 3. 结果分析
        • 结论
        • 运行结果

Python实现t-分布随机邻域嵌入(t-SNE)降维算法的博客

引言

在数据科学和机器学习中,降维技术是用于降低数据维度并且保留重要特征的关键方法。当我们处理高维数据时,常规的线性降维方法如PCA(主成分分析)可能不足以捕捉数据的非线性结构。为此,t-分布随机邻域嵌入(t-SNE)算法作为一种强大的非线性降维工具,被广泛用于高维数据的可视化和聚类分析。本文将详细介绍t-SNE算法的原理,并使用Python进行实现。我们将通过一个具体的场景来展示如何使用t-SNE算法实现降维

t-SNE算法原理

t-SNE(t-Distributed Stochastic Neighbor Embedding)是一种非线性降维算法,旨在将高维数据映射到低维空间(通常是二维或三维),以便进行数据可视化。t-SNE通过保留高维空间中数据点的局部邻域结构,使得降维后的数据点在低维空间中保持相似的局部关系。t-SNE算法的核心思想可以分为以下几步:

  1. 高维空间中的相似度计算
    t-SNE首先在高维空间中计算数据点之间的相似度。具体而言,给定两个数据点 x i x_i xi x j x_j xj,其在高维空间中的相似度由条件概率 p j ∣ i p_{j|i} pji表示。这个概率反映了在高维空间中选择点 x j x_j xj作为点 x i x_i xi邻居的概率。t-SNE假设高维数据分布遵循高斯分布,并通过高斯核函数计算条件概率:
    p j ∣ i = exp ⁡ ( − ∥ x i − x j ∥ 2 / 2 σ i 2 ) ∑ k ≠ i exp ⁡ ( − ∥ x i − x k ∥ 2 / 2 σ i 2 ) p_{j|i} = \frac{\exp(-\|x_i - x_j\|^2 / 2\sigma_i^2)}{\sum_{k \neq i} \exp(-\|x_i - x_k\|^2 / 2\sigma_i^2)} pji=k=iexp(xixk2/2σi2)exp(xixj2/2σi2)
    其中, σ i \sigma_i σi是根据点 x i x_i xi 的局部密度自适应调整的参数。

  2. 低维空间中的相似度计算
    在低维空间中,t-SNE使用t-分布而非高斯分布来计算相似度。t-分布在低维空间中具有较长的尾部,这使得t-SNE能够更好地处理数据点间的较大距离。给定两个低维数据点 y i y_i yi y j y_j yj,其相似度由条件概率 q j ∣ i q_{j|i} qji 表示:
    q i j = ( 1 + ∥ y i − y j ∥ 2 ) − 1 ∑ k ≠ l ( 1 + ∥ y k − y l ∥ 2 ) − 1 q_{ij} = \frac{(1 + \|y_i - y_j\|^2)^{-1}}{\sum_{k \neq l} (1 + \|y_k - y_l\|^2)^{-1}} qij=k=l(1+ykyl2)1(1+yiyj2)1
    t-分布通过其较长的尾部,更容易将远距离的数据点推远,从而使得局部结构更加清晰。

  3. 最小化KL散度
    t-SNE通过最小化高维空间中的条件概率分布 P P P 和低维空间中的条件概率分布 Q Q Q 之间的Kullback-Leibler散度(KL散度)来优化低维嵌入:
    K L ( P ∥ Q ) = ∑ i ∑ j p j ∣ i log ⁡ p j ∣ i q j ∣ i KL(P \| Q) = \sum_i \sum_j p_{j|i} \log \frac{p_{j|i}}{q_{j|i}} KL(PQ)=ijpjilogqjipji
    通过最小化KL散度,t-SNE确保在低维空间中,具有相似关系的数据点保持紧密,而不相似的数据点分离开。

  4. 梯度下降优化
    为了最小化KL散度,t-SNE使用梯度下降法进行优化。算法通过逐步调整低维空间中的数据点位置,来减少KL散度的值,从而得到低维空间中的嵌入。

t-SNE的优势与局限

t-SNE的最大优势在于它能够很好地保留数据的局部结构,非常适合高维数据的可视化。它常用于揭示数据中的聚类或流形结构,尤其适用于处理复杂的非线性数据。

然而,t-SNE也存在一些局限性。首先,它的计算复杂度较高,尤其是在处理大规模数据时。其次,t-SNE对参数(如学习率和邻居数)的选择较为敏感,参数设置不当可能导致结果不理想。此外,t-SNE在降维时可能会丢失全局结构信息,这意味着它更适合用于局部结构的探索,而非全局结构的分析。

Python实现t-SNE算法

接下来,我们将使用Python实现t-SNE算法,并将其封装到一个面向对象的类中。

1. 创建t-SNE类
python">import numpy as np
from scipy.spatial.distance import pdist, squareformclass TSNE:def __init__(self, n_components=2, perplexity=30.0, learning_rate=200.0, n_iter=1000):self.n_components = n_componentsself.perplexity = perplexityself.learning_rate = learning_rateself.n_iter = n_iterself.Y = None  # Embeddings in low-dimensional spacedef _h_beta(self, D, beta):P = np.exp(-D * beta)sumP = np.sum(P)if sumP == 0:sumP = 1e-10  # Avoid division by zeroH = np.log(sumP) + beta * np.sum(D * P) / sumPP = P / sumPreturn H, Pdef _binary_search(self, D, target, tol=1e-5, max_iter=50):beta_min = -np.infbeta_max = np.infbeta = 1.0H, P = self._h_beta(D, beta)Hdiff = H - np.log(target)iter_count = 0while np.abs(Hdiff) > tol and iter_count < max_iter:if Hdiff > 0:beta_min = betaif beta_max == np.inf:beta *= 2else:beta = (beta + beta_max) / 2else:beta_max = betaif beta_min == -np.inf:beta /= 2else:beta = (beta + beta_min) / 2H, P = self._h_beta(D, beta)Hdiff = H - np.log(target)iter_count += 1return Pdef fit(self, X):n_samples = X.shape[0]target_perplexity = np.log(self.perplexity)P = np.zeros((n_samples, n_samples), dtype=np.float64)for i in range(n_samples):Di = np.square(X[i] - X).sum(axis=1).astype(np.float64)Di[i] = np.inf  # Exclude self-distanceP[i] = self._binary_search(Di, target_perplexity)P = (P + P.T) / (2 * n_samples)P = np.maximum(P, 1e-12)  # Prevent log(0)self.Y = np.random.randn(n_samples, self.n_components)for iter in range(self.n_iter):D_Y = squareform(pdist(self.Y, 'sqeuclidean'))Q = np.exp(-D_Y / 2)Q[np.diag_indices_from(Q)] = 0  # Zero out diagonal (self-similarity)Q = np.maximum(Q, 1e-12)  # Prevent log(0)Q /= Q.sum()PQ = P - Qfor i in range(n_samples):gradient = np.sum((PQ[:, i][:, np.newaxis] * (self.Y[i] - self.Y)), axis=0)self.Y[i] += self.learning_rate * gradientif iter % 100 == 0:cost = np.sum(P * np.log(P / Q))print(f"Iteration {iter}: cost = {cost}")def transform(self):"""返回降维后的数据:return: 降维后的数据"""return self.Y
2. 示例场景:MNIST手写数字数据集

为了演示如何使用t-SNE算法,我们将使用经典的MNIST手写数字数据集。该数据集包含10个类别的手写数字,每个样本是一个28x28的灰度图像。我们将使用t-SNE将784维的图像数据降维到2维空间,并进行可视化。

python">from sklearn.datasets import fetch_openml
import matplotlib.pyplot as plt# 加载MNIST数据集
mnist = fetch_openml('mnist_784', version=1)
X, y = mnist["data"], mnist["target"]# 随机抽取1000个样本进行降维
np.random.seed(42)
indices = np.random.choice(X.shape[0], 1000, replace=False)
X_subset, y_subset = X.iloc[indices], y.iloc[indices]# 使用TSNE类进行降维
tsne = TSNE(n_components=2, perplexity=30.0, learning_rate=200.0, n_iter=1000)
tsne.fit(X_subset.values)
Y = tsne.transform()# 可视化
plt.scatter(Y[:, 0], Y[:, 1], c=y_subset.astype(int), cmap='tab10', s=50, alpha=0.8)
plt.colorbar()
plt.title("t-SNE visualization of MNIST")
plt.show()
3. 结果分析

通过上述代码,我们可以将高维的MNIST数据集降维到2维,并对不同的数字类别进行可视化。我们可以观察到,t-SNE算法能够有效地将不同类别的数据点分离开来,形成不同的簇。每个簇代表一个手写数字类别,从而使我们能够直观地识别数据的聚类结构。

结论

t-SNE是一种强大的非线性降维技术,特别适用于高维数据的可视化和聚类分析。在本篇博客中,我们详细介绍了t-SNE算法的原理,并使用Python实现了该算法。通过对MNIST手写数字数据集的降维和可视化,我们展示了t-SNE在揭示数据结构方面的优势。然而,t-SNE在大规模数据集上的计算开销较高,对参数设置敏感,因此在实际应用中需要仔细调优和考虑其局限性。希望本文对您理解t-SNE算法有所帮助,并能在实际项目中应用这一技术。

运行结果

在这里插入图片描述


http://www.ppmy.cn/devtools/104817.html

相关文章

CSS 的超级好用的object-fit属性

object-fit 是 CSS 中的一个非常有用的属性&#xff0c;它决定了替换元素&#xff08;如 <img>、<video>、<canvas> 等&#xff09;的内容应该如何适应其使用的高度和宽度。这个属性解决了在不同布局和屏幕尺寸下&#xff0c;如何优雅地控制元素内容显示的问…

氢气传感器的工作寿命一般是多久?

​ ​氢气传感器的工作寿命因多种因素而存在较大差异&#xff0c;这些因素包括传感器的类型、品牌、使用环境以及日常维护情况等。 ​ ​催化燃烧式氢气传感器通常具有相对较长的工作寿命&#xff0c;大约在 2 至 5 年。在其工作过程中&#xff0c;催化剂会随着时间的推移…

(四)Kafka离线安装 - Kafka下载及安装

Kafka官方下载地址&#xff1a;Apache Kafka 这时候下载安装版本。 我这里的安装目录在 /usr/local/ cd /usr/local/# 创建目录 mkdir kafka cd kafka mkdir kafka_log 把下载的压缩包&#xff0c;放入到/usr/local/kafka/目录下&#xff0c;解压。 # 解压 tar -zxvf kafka…

Webpack打包常见问题及优化策略

聚沙成塔每天进步一点点 本文回顾 ⭐ 专栏简介Webpack打包常见问题及优化策略1. 引言2. Webpack打包常见问题2.1 打包时间过长问题描述主要原因 2.2 打包体积过大问题描述主要原因 2.3 依赖包版本冲突问题描述主要原因 2.4 动态导入和代码拆分问题问题描述主要原因 2.5 文件路径…

基于卷积神经网络的磨削平板类零件擦伤检测

基于卷积神经网络的磨削平板类零件擦伤检测 前言正文 前言 还记得读研那会儿刚学习完了卷积神经网络&#xff0c;初步学会了最基础的分类问题&#xff0c;当时也有点python基础&#xff0c;同时对TensorFlow也有点入门了。正好我的课题中有一类缺陷比较难以用传统方法识别判断&…

docker 介绍以及常用命令

文章目录 Docker 概述docker 概念安装 Docker核心概念Docker 镜像Docker 容器Docker 仓库 docker 与虚拟机比较 Docker 命令docker 进程相关命令镜像相关命令查看本地镜像搜索镜像拉取/推送镜像删除镜像 容器相关命令创建容器查看容器启动/终止/删除容器新建并启动容器进入容器…

CSS3中display显示属性

显示属性 把块级元素改为行内元素&#xff1a; p { display:inline } 把行内元素改为块级元素&#xff0c;会填满父元素&#xff1a; a { display:block } 设为none&#xff0c;全都消失&#xff0c;位置也不占&#xff0c;visibility为hidden消失了&#xff0c;但是还占位…

90分钟实现一门编程语言——极简解释器教程

关键字 解释器, C#, Scheme, 函数式编程 关于 本文介绍了如何使用C#实现一个简化但全功能的Scheme方言——iScheme及其解释器&#xff0c;通过从零开始逐步构建&#xff0c;展示了编程语言/解释器的工作原理。 作者 Lucida a.k.a Luc 如果你是通过移动设备阅读本教程&…