参数化(神经网络)嵌入

UMAP 包含两个步骤:首先,计算代表您数据的图;其次,学习该图的嵌入

_images/umap-only.png

参数化 UMAP 取代了第二个步骤,最小化与 UMAP(在此我们称之为非参数化 UMAP)相同的目标函数,但使用神经网络学习数据和嵌入之间的关系,而不是直接学习嵌入本身

_images/pumap-only.png

参数化 UMAP 只是 UMAP 的一个子类,因此可以像非参数化 UMAP 一样使用,只需将 umap.UMAP 替换为 parametric_umap.ParametricUMAP。参数化 UMAP 的最基本用法就是简单地在代码中将 UMAP 替换为 ParametricUMAP

from umap.parametric_umap import ParametricUMAP
embedder = ParametricUMAP()
embedding = embedder.fit_transform(my_data)

在此实现中,我们使用 Keras 和 Tensorflow 作为后端来训练该神经网络。学习嵌入的额外复杂性带来了许多额外的可配置设置,这些设置在非参数化 UMAP 中没有。在 GitHub 仓库上有一系列 Jupyter Notebook,可以指导您了解这些参数

定义自己的网络

默认情况下,参数化 UMAP 使用 3 层 100 个神经元的全连接神经网络。要扩展参数化 UMAP 以使用更复杂的架构,例如卷积神经网络,我们只需要定义网络并将其作为参数传递给 ParametricUMAP。使用 tf.keras.Sequential 可以轻松完成此操作。以下是 MNIST 的示例

# define the network
import tensorflow as tf
dims = (28, 28, 1)
n_components = 2
encoder = tf.keras.Sequential([
    tf.keras.layers.InputLayer(input_shape=dims),
    tf.keras.layers.Conv2D(
        filters=32, kernel_size=3, strides=(2, 2), activation="relu", padding="same"
    ),
    tf.keras.layers.Conv2D(
        filters=64, kernel_size=3, strides=(2, 2), activation="relu", padding="same"
    ),
    tf.keras.layers.Flatten(),
    tf.keras.layers.Dense(units=256, activation="relu"),
    tf.keras.layers.Dense(units=256, activation="relu"),
    tf.keras.layers.Dense(units=n_components),
])
encoder.summary()

要将数据加载到 ParametricUMAP 中,我们首先需要将其从 28x28x1 的图像展平为 784 维向量。

from tensorflow.keras.datasets import mnist
(train_images, Y_train), (test_images, Y_test) = mnist.load_data()
train_images = train_images.reshape((train_images.shape[0], -1))/255.
test_images = test_images.reshape((test_images.shape[0], -1))/255.

然后我们可以将网络传递给 ParametricUMAP 并进行训练

# pass encoder network to ParametricUMAP
embedder = ParametricUMAP(encoder=encoder, dims=dims)
embedding = embedder.fit_transform(train_images)

如果您不熟悉 Tensorflow/Keras 并想训练自己的模型,我们建议您查看 Tensorflow 文档

保存和加载模型

与非参数化 UMAP 不同,参数化 UMAP 不能仅仅通过 pickle UMAP 对象来保存,因为它包含 Keras 网络。为了保存参数化 UMAP,有一个内置函数

embedder.save('/your/path/here')

然后您可以在其他地方加载参数化 UMAP

from umap.parametric_umap import load_ParametricUMAP
embedder = load_ParametricUMAP('/your/path/here')

这将加载 UMAP 对象及其包含的参数化网络。

绘制损失曲线

参数化 UMAP 在训练期间使用 Keras 监控损失。每次训练 epoch 后会打印该损失。此损失保存在 embedder._history 中,并可以进行绘制

print(embedder._history)
fig, ax = plt.subplots()
ax.plot(embedder._history['loss'])
ax.set_ylabel('Cross Entropy')
ax.set_xlabel('Epoch')
_images/umap-loss.png

与其他 Keras 模型类似,如果您通过模型的 fit 方法继续训练模型,embedder._history 将随着进一步的训练 epoch 损失进行更新。

参数化逆变换(重构)

要使用第二个神经网络学习数据和嵌入之间的逆映射,我们只需将 parametric_reconstruction= True 传递给 ParametricUMAP。

与编码器类似,也可以将自定义解码器传递给 ParametricUMAP,例如

decoder = tf.keras.Sequential([
    tf.keras.layers.InputLayer(input_shape=(n_components)),
    tf.keras.layers.Dense(units=256, activation="relu"),
    tf.keras.layers.Dense(units=7 * 7 * 256, activation="relu"),
    tf.keras.layers.Reshape(target_shape=(7, 7, 256)),
    tf.keras.layers.UpSampling2D((2)),
    tf.keras.layers.Conv2D(
        filters=64, kernel_size=3, padding="same", activation="relu"
    ),
    tf.keras.layers.UpSampling2D((2)),
    tf.keras.layers.Conv2D(
        filters=32, kernel_size=3, padding="same", activation="relu"
    ),

])

此外,可以使用验证数据来测试数据集外样本的重构损失

validation_images = test_images.reshape((test_images.shape[0], -1))/255.

最后,我们可以将验证数据和网络传递给 ParametricUMAP 并进行训练

embedder = ParametricUMAP(
    encoder=encoder,
    decoder=decoder,
    dims=dims,
    parametric_reconstruction= True,
    reconstruction_validation=validation_images,
    verbose=True,
)
embedding = embedder.fit_transform(train_images)

自编码 UMAP

在上面的示例中,编码器被训练来最小化 UMAP 损失,解码器被训练来最小化重构损失。要同时在 UMAP 损失和重构损失上联合训练编码器,请将 autoencoder_loss = True 传递到 ParametricUMAP 中。

embedder = ParametricUMAP(
    encoder=encoder,
    decoder=decoder,
    dims=dims,
    parametric_reconstruction= True,
    reconstruction_validation=validation_images,
    autoencoder_loss = True,
    verbose=True,
)

提前停止和 Keras 回调

有时,将嵌入器训练到训练损失达到某个平台期会很有用。在深度学习中,提前停止是一种实现方法。Keras 提供了自定义的 回调,允许您在训练期间实现检查,例如提前停止。我们可以使用回调(例如提前停止)与 ParametricUMAP 一起,根据预定义的训练阈值提前停止训练,使用 keras_fit_kwargs 参数

keras_fit_kwargs = {"callbacks": [
    tf.keras.callbacks.EarlyStopping(
        monitor='loss',
        min_delta=10**-2,
        patience=10,
        verbose=1,
    )
]}

embedder = ParametricUMAP(
    verbose=True,
    keras_fit_kwargs = keras_fit_kwargs,
    n_training_epochs=20
)

我们还传入了 n_training_epochs = 20,允许提前停止在达到 20 个 epoch 之前结束训练。

更多重要参数

  • batch_size:参数化 UMAP 通过从 UMAP 图中随机采样的边批量进行训练,然后通过梯度下降进行训练。参数化 UMAP 默认的批量大小为 1000 条边,但可以调整为更适合您的 GPU 或 CPU 的值。

  • loss_report_frequency:如果设置为 1,则 Keras 嵌入中的一个 epoch 指的是在 UMAP 中计算的图上的一次迭代。将 loss_report_frequency 设置为 10,会将该 epoch 分割成 10 个独立的 epoch,以便更频繁地报告。

  • n_training_epochs:在 UMAP 图上训练的 epoch 数(与 loss_report_frequency 无关)。训练网络多个 epoch 将产生更好的嵌入,但耗时更长。此参数与基础 UMAP 类中的 n_epochs 不同,后者对应于单个参数化 UMAP epoch 中训练边的最大次数。

  • optimizer:用于训练神经网络的优化器。默认使用 Adam (tf.keras.optimizers.Adam(1e-3))。您可能通过使用不同的优化器来加速或改进训练。

  • parametric_embedding:如果设置为 false,将学习非参数化嵌入,使用与参数化嵌入相同的代码,这可以作为使用相同优化器对参数化和非参数化嵌入进行直接比较。参数化嵌入是同时在整个数据集上执行的。

  • global_correlation_loss_weight:是否额外在全局成对关系的相关性上进行训练(多维尺度分析)

  • landmark_loss_fn:在对有标志的数据进行再训练时使用的损失函数,您已将嵌入空间中的期望位置提供给模型的 fit 方法。默认使用欧几里得损失。有关再训练、标志以及为何使用它们的更多信息,请参见 使用参数化 UMAP 转换新数据

  • landmark_loss_weight:相对于 umap 损失,如何加权标志损失,默认为 1.0。

扩展模型

您可能希望在本文档实现的功能之外定制参数化 UMAP。为了尽可能方便您对参数化 UMAP 进行修改,我们制作了一些 Jupyter Notebook,展示了如何将参数化 UMAP 扩展到您自己的用例。

引用我们的工作

如果您在工作中使用参数化 UMAP,请引用我们的论文

 @article{sainburg2021parametric,
  title={Parametric UMAP Embeddings for Representation and Semisupervised Learning},
  author={Sainburg, Tim and McInnes, Leland and Gentner, Timothy Q},
  journal={Neural Computation},
  volume={33},
  number={11},
  pages={2881--2907},
  year={2021},
  publisher={MIT Press One Rogers Street, Cambridge, MA 02142-1209, USA journals-info~…}
}