你好,我是kk~
今儿看到很多同学在讨论T-SNE,参与感太强。
咱们今天也来和大家聊聊~
T-SNE,是一种用于高维数据可视化的降维技术,尤其在将数据嵌入到二维或三维空间时非常有效。
T-SNE 的核心思想
T-SNE 的核心目标是保持数据高维空间中的邻近关系。它会尝试在低维空间中还原高维数据点之间的局部邻近关系,这样在可视化结果中,原始数据中的相似点会聚集在一起,不相似的点则远离。
T-SNE 包含两步:
在高维空间中定义点对之间的相似性:用条件概率来表示两个点是“邻居”的可能性。 在低维空间中定义点对之间的相似性:通过优化使低维空间中的点对相似性与高维空间中的相似性尽可能匹配。
高维空间的相似性定义
在高维空间中,T-SNE 采用条件概率来衡量数据点之间的相似性。具体而言,给定点 和 ,定义在高维空间中点 是点 的邻居的概率为:
其中, 是针对点 的高斯分布的标准差,反映了点的局部邻域范围。通过这种方式,局部相似性被量化为一种条件概率。
为了使这个相似性是对称的,T-SNE 使用如下对称相似性度量:
其中, 是数据点的总数。
低维空间的相似性定义
在低维空间中,T-SNE 使用学生 t 分布 (t-distribution) 来定义点对之间的相似性。这是 T-SNE 的一个关键创新,学生 t 分布(自由度为 1)有较长的尾部,能够更好地处理拥挤区域中的点对。在低维空间中,点 和 之间的相似性被定义为:
目标函数(Kullback-Leibler 散度)
T-SNE 通过最小化高维空间与低维空间中分布之间的差异来优化嵌入。衡量分布差异的方法是 Kullback-Leibler 散度 (KL 散度),其目标是最小化高维分布 和低维分布 之间的 KL 散度:
其中, 是目标函数(成本函数),当 和 更加接近时,KL 散度的值越小,说明两种分布越相似。
梯度下降优化
为了最小化成本函数,T-SNE 使用梯度下降法来更新低维空间中的点位置 。成本函数的梯度为:
该公式通过每一步的梯度更新来调整低维空间中的点 和 之间的距离,从而使得低维空间中的相似性 尽可能接近高维空间中的相似性 。
T-SNE 中的关键步骤
总结 T-SNE 的执行步骤如下:
**计算高维相似性 **:通过高斯分布来衡量高维空间中数据点之间的相似性。 **初始化低维点位置 **:随机初始化或使用 PCA 初始化低维嵌入。 **计算低维相似性 **:通过学生 t 分布来衡量低维空间中的点对相似性。 最小化 KL 散度:使用梯度下降法最小化高维空间与低维空间相似性分布之间的 KL 散度。 重复迭代:不断更新低维空间中的点位置,直至收敛。
算法优化与参数调节
Perplexity(困惑度):困惑度控制着每个点的局部邻域大小,较大的困惑度会使得算法考虑更多的点作为邻居。 早期夸大(early exaggeration):T-SNE 在最初的几次迭代中会夸大高维相似性 ,这有助于更快地形成低维结构。
T-SNE 的限制
计算复杂度高:T-SNE 的时间复杂度较高,尤其在大规模数据集上表现不佳。 非凸优化:由于 KL 散度优化是非凸的,T-SNE 的结果可能会依赖于初始点位置,因此不同运行可能产生不同的结果。
下面,给大家带来一个案例~
案例概述
我们生成一个多类别的高维数据集,并使用 T-SNE 将其降维到二维。为了展示 T-SNE 的效果,我们将绘制两类图形:
T-SNE 不同 perplexity 参数下的效果对比:展示 perplexity 参数如何影响降维结果。 T-SNE 结果的密度分布图:直观展示数据类别在降维后的二维空间中的分布情况。
使用 Python 的 sklearn
库生成数据,并使用 matplotlib
和 seaborn
进行数据可视化。
1. 导入必要的库
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.datasets import make_blobs
from sklearn.manifold import TSNE
from mpl_toolkits.mplot3d import Axes3D
2. 生成虚拟数据集
我们生成一个包含 5 个类别的 3D 数据集,每个类别的点分布在高维空间内。这个数据集将帮助我们展示 T-SNE 降维的效果。
# 设置随机种子以确保结果可重复
np.random.seed(42)
# 生成一个高维数据集,包含 5 类,每类 1000 个样本,3D 空间中的数据
X, y = make_blobs(n_samples=5000, n_features=3, centers=5, cluster_std=2.0)
# 查看数据的基本信息
print("数据集的形状:", X.shape)
print("标签的形状:", y.shape)
生成的数据集包含 5000 个样本,每个样本有 3 个特征(3D 空间),并被分成 5 个类别。
3. 使用 T-SNE 进行降维
接下来,我们使用 T-SNE 将 3D 数据降维到 2D。为了比较不同的 perplexity 参数对结果的影响,我们将使用多个 perplexity 值。
# 定义 T-SNE perplexity 值列表
perplexities = [5, 30, 50, 100]
# 创建图形窗口
plt.figure(figsize=(14, 10))
# 对不同 perplexity 值执行 T-SNE
for i, perplexity in enumerate(perplexities):
tsne = TSNE(n_components=2, perplexity=perplexity, n_iter=1000, random_state=42)
X_tsne = tsne.fit_transform(X)
# 创建子图
plt.subplot(2, 2, i+1)
plt.scatter(X_tsne[:, 0], X_tsne[:, 1], c=y, cmap='viridis', s=10)
plt.title(f"Perplexity = {perplexity}")
plt.xlabel('T-SNE Dimension 1')
plt.ylabel('T-SNE Dimension 2')
# 显示图形
plt.tight_layout()
plt.show()
4. 可视化 perplexity 对 T-SNE 结果的影响
此步骤绘制了 4 个子图,分别展示不同 perplexity 参数下 T-SNE 的降维效果。perplexity 参数决定了 T-SNE 中每个点的邻居数,因此会影响到局部结构的保持情况。
5. 创建密度分布图
为了进一步展示 T-SNE 的结果,我们将绘制 T-SNE 结果的密度分布图。这样可以更清楚地看到不同类别在低维空间中的分布。
# 选择一个合适的 perplexity 值(例如 30)
tsne = TSNE(n_components=2, perplexity=30, n_iter=1000, random_state=42)
X_tsne = tsne.fit_transform(X)
# 创建图形
plt.figure(figsize=(10, 8))
# 使用 seaborn 绘制密度图
sns.kdeplot(x=X_tsne[:, 0], y=X_tsne[:, 1], hue=y, palette="bright", fill=True, thresh=0.05)
plt.title('T-SNE Density Plot')
plt.xlabel('T-SNE Dimension 1')
plt.ylabel('T-SNE Dimension 2')
# 显示图形
plt.show()
6. 解释图形
T-SNE 不同 perplexity 下的效果对比:通过比较不同 perplexity 的图形,我们可以看到,当 perplexity 较小时,T-SNE 更关注局部邻域结构,点群相对更加分散;而随着 perplexity 增大,T-SNE 更倾向于在低维空间中捕捉到数据的全局结构。 T-SNE 密度分布图:这个图形通过颜色的密度变化展示了每个类别的集中度和分布形态,可以帮助我们更好地理解降维后的类内和类间关系。
7. 完整代码
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.datasets import make_blobs
from sklearn.manifold import TSNE
from mpl_toolkits.mplot3d import Axes3D
# 设置随机种子以确保结果可重复
np.random.seed(42)
# 生成一个高维数据集,包含 5 类,每类 500 个样本,3D 空间中的数据
X, y = make_blobs(n_samples=2500, n_features=3, centers=5, cluster_std=2.0)
# 定义 T-SNE perplexity 值列表
perplexities = [5, 30, 50, 100]
# 创建图形窗口
plt.figure(figsize=(14, 10))
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.manifold import TSNE
# 对不同 perplexity 值执行 T-SNE
for i, perplexity in enumerate(perplexities):
tsne = TSNE(n_components=2, perplexity=perplexity, n_iter=1000, random_state=42)
X_tsne = tsne.fit_transform(X)
# 创建子图
plt.subplot(2, 2, i+1)
plt.scatter(X_tsne[:, 0], X_tsne[:, 1], c=y, cmap='viridis', s=10)
plt.title(f"Perplexity = {perplexity}")
plt.xlabel('T-SNE Dimension 1')
plt.ylabel('T-SNE Dimension 2')
# 显示图形
plt.tight_layout()
plt.show()
# 选择一个合适的 perplexity 值(例如 30)
tsne = TSNE(n_components=2, perplexity=30, n_iter=1000, random_state=42)
X_tsne = tsne.fit_transform(X)
# 创建图形
plt.figure(figsize=(10, 8))
# 使用 seaborn 绘制密度图
sns.kdeplot(x=X_tsne[:, 0], y=X_tsne[:, 1], hue=y, palette="bright", fill=True, thresh=0.05)
plt.title('T-SNE Density Plot')
plt.xlabel('T-SNE Dimension 1')
plt.ylabel('T-SNE Dimension 2')
# 显示图形
plt.show()
案例中,通过调整 perplexity 参数,我们可以观察到 T-SNE 如何平衡局部结构与全局结构。同时,密度分布图也帮助我们更好地理解了不同类别在低维空间中的分布情况。