手写数字的流形学习

Beginner

This tutorial is from open-source community. Access the source code

简介

在本实验中,我们将探索数字数据集上的各种流形嵌入技术。我们将使用不同的技术来嵌入数字数据集,绘制原始数据在每个嵌入上的投影,并比较不同嵌入方法得到的结果。

虚拟机使用提示

虚拟机启动完成后,点击左上角切换到“笔记本”标签,以访问 Jupyter Notebook 进行练习。

有时,你可能需要等待几秒钟让 Jupyter Notebook 完成加载。由于 Jupyter Notebook 的限制,操作的验证无法自动化。

如果你在学习过程中遇到问题,可以随时向 Labby 提问。课程结束后提供反馈,我们会及时为你解决问题。

加载数字数据集

我们将加载数字数据集,并且只使用十个可用类别中的六个。我们还将绘制该数据集中的前一百个数字。

## 加载数字数据集
from sklearn.datasets import load_digits

digits = load_digits(n_class=6)
X, y = digits.data, digits.target
n_samples, n_features = X.shape
n_neighbors = 30

## 绘制前一百个数字
import matplotlib.pyplot as plt

fig, axs = plt.subplots(nrows=10, ncols=10, figsize=(6, 6))
for idx, ax in enumerate(axs.ravel()):
    ax.imshow(X[idx].reshape((8, 8)), cmap=plt.cm.binary)
    ax.axis("off")
_ = fig.suptitle("A selection from the 64-dimensional digits dataset", fontsize=16)

绘制嵌入函数

我们将定义一个辅助函数来绘制嵌入图。该函数将嵌入数据和绘图标题作为输入。函数会在嵌入图上绘制每个数字,并为一组数字显示一个注释框。

import numpy as np
from matplotlib import offsetbox
from sklearn.preprocessing import MinMaxScaler

def plot_embedding(X, title):
    _, ax = plt.subplots()
    X = MinMaxScaler().fit_transform(X)

    for digit in digits.target_names:
        ax.scatter(
            *X[y == digit].T,
            marker=f"${digit}$",
            s=60,
            color=plt.cm.Dark2(digit),
            alpha=0.425,
            zorder=2,
        )
    shown_images = np.array([[1.0, 1.0]])  ## just something big
    for i in range(X.shape[0]):
        ## plot every digit on the embedding
        ## show an annotation box for a group of digits
        dist = np.sum((X[i] - shown_images) ** 2, 1)
        if np.min(dist) < 4e-3:
            ## don't show points that are too close
            continue
        shown_images = np.concatenate([shown_images, [X[i]]], axis=0)
        imagebox = offsetbox.AnnotationBbox(
            offsetbox.OffsetImage(digits.images[i], cmap=plt.cm.gray_r), X[i]
        )
        imagebox.set(zorder=1)
        ax.add_artist(imagebox)

    ax.set_title(title)
    ax.axis("off")

比较嵌入技术

我们将使用不同的方法比较不同的嵌入技术。我们将存储投影后的数据以及执行每次投影所需的计算时间。

from sklearn.decomposition import TruncatedSVD
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
from sklearn.ensemble import RandomTreesEmbedding
from sklearn.manifold import (
    Isomap,
    LocallyLinearEmbedding,
    MDS,
    SpectralEmbedding,
    TSNE,
)
from sklearn.neighbors import NeighborhoodComponentsAnalysis
from sklearn.pipeline import make_pipeline
from sklearn.random_projection import SparseRandomProjection

embeddings = {
    "随机投影嵌入": SparseRandomProjection(
        n_components=2, random_state=42
    ),
    "截断奇异值分解嵌入": TruncatedSVD(n_components=2),
    "线性判别分析嵌入": LinearDiscriminantAnalysis(
        n_components=2
    ),
    "等距映射嵌入": Isomap(n_neighbors=n_neighbors, n_components=2),
    "标准局部线性嵌入": LocallyLinearEmbedding(
        n_neighbors=n_neighbors, n_components=2, method="standard"
    ),
    "修正局部线性嵌入": LocallyLinearEmbedding(
        n_neighbors=n_neighbors, n_components=2, method="modified"
    ),
    "海森局部线性嵌入": LocallyLinearEmbedding(
        n_neighbors=n_neighbors, n_components=2, method="hessian"
    ),
    "局部切空间对准局部线性嵌入": LocallyLinearEmbedding(
        n_neighbors=n_neighbors, n_components=2, method="ltsa"
    ),
    "多维缩放嵌入": MDS(
        n_components=2, n_init=1, max_iter=120, n_jobs=2, normalized_stress="auto"
    ),
    "随机树嵌入": make_pipeline(
        RandomTreesEmbedding(n_estimators=200, max_depth=5, random_state=0),
        TruncatedSVD(n_components=2),
    ),
    "谱嵌入": SpectralEmbedding(
        n_components=2, random_state=0, eigen_solver="arpack"
    ),
    "t-SNE 嵌入": TSNE(
        n_components=2,
        n_iter=500,
        n_iter_without_progress=150,
        n_jobs=2,
        random_state=0
    ),
    "邻域成分分析嵌入": NeighborhoodComponentsAnalysis(
        n_components=2, init="pca", random_state=0
    ),
}

projections, timing = {}, {}
for name, transformer in embeddings.items():
    if name.startswith("线性判别分析"):
        data = X.copy()
        data.flat[:: X.shape[1] + 1] += 0.01  ## 使 X 可逆
    else:
        data = X

    print(f"计算 {name}...")
    start_time = time()
    projections[name] = transformer.fit_transform(data, y)
    timing[name] = time() - start_time

绘制结果

我们将绘制每种方法给出的最终投影。

for name in timing:
    title = f"{name} (时间 {timing[name]:.3f}秒)"
    plot_embedding(projections[name], title)

plt.show()

总结

在本实验中,我们在数字数据集上探索了各种流形嵌入技术。我们使用不同的技术对数字数据集进行嵌入,绘制了原始数据在每个嵌入上的投影,并比较了从不同嵌入方法获得的结果。这些结果有助于深入了解每种嵌入方法在嵌入空间中将相似数字分组在一起的有效性。