解开复杂性:噪声注入的流形学习的新方法

揭秘复杂问题:噪声注入流形学习的创新方法

在数据科学的世界中,高维数据既是一个挑战,也是一个机遇。虽然它提供了一个丰富的关系和模式宝库,可以被塑造和转化,但如果不仔细清理和选择,分析和得出结论就会变得无法承受和困惑:“维度灾难”。虽然直觉上你可能会倾向于使用主成分分析将数据嵌入到一个较小的子空间中,但你可能会增加数据问题的难度,非线性嵌入技术可能会是更合适的选择。然而,在选择正确的非线性技术时需要小心,因为选择错误可能会导致过拟合或根本不适用的嵌入。在本文中,我将借此机会讨论一种新方法,以便我们作为数据科学家可以根据复杂数据的底层结构做出明智的定量决策。

我将首先介绍流形学习是什么,并概述四种常见线性和非线性嵌入技术的高级但信息丰富的摘要。通过这些,我们将更深入地了解每种情况下所做的假设及其对有效嵌入的影响。我还将介绍一些使用噪声注入分析方法评估流形和可以得出的推论的Python示例。本文结束时,您将全面了解不同的流形学习技术以及在数据中真正理解底层结构的步骤。

流形学习

在介绍这些流形学习技术之前,重要的是明确什么是流形?在我们的背景下,流形是我们高维空间结构的近似表示,它可能与附近的其他数据点存在局部和/或全局关系。但我们真的不知道我们N维空间中的真实结构,并且在嵌入数据时常常被迫对数据点之间的关系做出隐含的假设。与在数学中的流形学习(黎曼几何学)不同,在那里可以找到从一个空间到另一个空间的显式映射。

机器学习模型的成功,无论是在性能还是数据驱动的洞察力方面,都基本上取决于我们传递给它的数据。传递更多信息可以使这些算法找到更多复杂的关系和模式,但它也会导致一系列问题,这些问题通常被概括为维度灾难:

  • 过拟合模型:随着数据的维度增加,后续的机器学习模型可能无法概括数据中的真实关系,因为它们对噪声和异常值过拟合。
  • 点间关系扩展:在大的复杂特征空间中,某些区域变得非常稀疏,很难建模,或者变得非常集中,关键信息被模糊了。
  • 增加计算复杂性:大多数机器学习算法在特征数增加时无法很好地扩展,从而导致培训模型时增加计算时间或内存需求。

为了克服这一问题,我们必须要么减少我们考虑的特征数,要么将数据映射到较低维空间,同时尽可能多地保留关键信息。在下一节中,我们总结和探讨不同的技术(线性和非线性)。

主成分分析

主成分分析(PCA)可以说是嵌入或降低数据集维度的最著名方法,这很可能源于其统计方法所推断出的可解释性。在线上可以找到很多其他更深入讨论这个算法的文章,但为了本文的目的,我以下面列出了主要步骤。

关键的观点是,PCA试图通过假设一个线性流形,并将数据映射到N个正交主成分(它们是原始特征的线性组合)来保持所有数据点之间的关系。它首先对数据进行标准化,以平均值为中心并相应地进行缩放,以使所有变量的方差保持一致:

其中 Xⱼ 是所有特征 j 的原始特征空间 X,μ 和 σ 分别是 Xⱼ 的均值和标准差。然后,该算法计算标准化数据的协方差矩阵 S:

表达每个变量与其他变量的相关性。PCA然后执行协方差矩阵的特征值分解,以确定特征值λᵢ和特征向量vᵢ

矩阵W由这些特征向量定义,按照特征值降序排序。转换后的数据的最终投影Y只是Z和W的乘积。

总之,PCA提供了一种揭示数据内部结构的方式,以最佳方式保留和解释方差(即在最少维度间最大化信息)。每个特征值与方差的部分成比例,因此我们的矩阵W强制要求第一个投影的主成分包含最多方差,而每个后续的正交分量包含较少的一部分方差。

局部线性嵌入

在进入更高级的非线性方法之前,让我们从可能最简单理解的方法开始,即局部线性嵌入(LLE)。实质上,LLE假定给定数据点及其邻居可以近似表示并映射到流形上的线性切平面,使它们彼此之间成为线性组合。映射邻域群集到平面的权重调整以最小化转换的误差(见下文),并将这个过程对每个数据点重复。因此,虽然在数据点的邻域中存在局部线性性,但非线性性将被全局捕捉。

作为数据科学家,您需要定义最近邻的数量k,这需要仔细调整。有了这个值后,首先要解决的优化问题是权重数组Wᵢⱼ,它将每个数据点Xᵢ映射到切平面作为其邻居的线性组合:

对于每个i的主题,约束条件如下:

确保权重总和为1,以保持局部几何形状。从这里,我们的嵌入Yᵢ只是这些权重和原始空间Xᵢ的乘积,并确保最小化以下嵌入成本函数。

上述确保较低维度表示最佳地保留了原始空间中的局部权重。虽然这是一种优雅捕捉非线性关系的解决方案,但是如果我们为k选择的值不合适,或者我们的N维空间中有稀疏的部分,可能会导致嵌入数据的损坏。接下来,我们将探讨其他使用局部和全局关系结合形成最终嵌入的非线性嵌入技术,这在大多数情况下使它们更加稳健。

谱嵌入

谱嵌入(SE),也称为拉普拉斯特征映射嵌入,形成连接所有数据点的相似性图,然后根据点之间的谱相似性进行加权。通过这样做,SE不仅保留了局部关系(与LLE相同),而且通过连接的图确保了全局关系也被考虑进去。对图拉普拉斯特性的依赖使得该算法能够发现其他技术可能无法识别的复杂非线性结构。

该算法的第一步是构建图:

其中Wᵢⱼ是节点i和j之间的边的宽度,σ是一个可自定义的参数,用于控制邻域的宽度。从此,图拉普拉斯矩阵L被定义为L=D-W,其中W是图的邻接矩阵,D是具有下面条目的对角度矩阵:

通过对最终嵌入施加正交性和中心约束,该算法对拉普拉斯矩阵执行特征值分解,以相应地嵌入数据。

等距特征映射

最后一个非线性流形技术是等距特征映射(ISOMAP),它是一种强大的非线性嵌入方法,比前面提到的方法略具更多定制性。算法的方法假设您可以通过一个连接的邻域图来表示高维空间,其中节点之间的距离是测地距离。然后,该方法应用多维缩放(MDS)来找到数据的低维表示,使得节点之间的成对距离尽可能保持不变。

在构建图形时,您可以选择对最近邻点的数量和数据点与其邻居之间的相对欧几里德距离设置限制。这些约束需要适当调整,例如,不能太大,否则会形成快捷路径边缘(缺少重要结构信息),但也不能太小,以至于我们无法创建连接图形。如果满足这些邻域条件,则建立两个节点之间的边缘。使用图形G,算法使用最短路径算法f(例如Dijkstra’s或Floyd’s)计算所有点对之间的测地距离Dᵢⱼ:

最后一步是将数据映射到子空间,涉及将MDS应用于Dᵢⱼ。如果我们回想一下之前对PCA的概述,我们在进行特征值分解之前会评估协方差矩阵。MDS稍有不同,它通过计算相对于中心矩阵H的格拉姆矩阵B来计算:

其中e是一个全为1的向量,n是数据点的数量。最终嵌入是由与最大特征值对应的d个特征向量派生的:

噪音注入

到目前为止,我们已经介绍了一些线性和非线性的方法,用于通过对潜在流形进行某些假设来将数据嵌入到较低维空间中,但当我们处理无法可视化的高维数据时,我们如何知道哪种方法能捕捉到有用的信息。从定量角度来评估任何嵌入技术性能的一种方法是使用我所称之为噪音注入的方法。通过这种方法,我们在原始空间中应用不同程度的噪音,并监视其对我们的嵌入结果的影响。基本原理是,随着原始空间中噪音(扭曲)的增加,任何流形学习算法都将无法捕捉到真实的潜在结构。通过观察嵌入在不同程度的噪音下的响应,可以轻松判断每种技术在对数据集中的潜在结构进行建模方面的能力。下面是一个逐步摘要的摘要,介绍了如何进行这种分析,并提供了两个Python示例以帮助理解:

  1. 使用高斯噪音生成原始数据集的替代数据集,其中噪音的方差会按比例增加。
  2. 使用一组流形学习技术嵌入这些数据集。
  3. 对于每种技术,使用procrustes分析将注入噪音的嵌入结果与原始空间(没有人工添加噪音)的嵌入结果进行比较。procrustes分析是一种常用的统计方法,用于比较两个形状。该评估方法会将一个空间上的点旋转、缩放和平移到另一个空间上,其目标是最小化每个数据点之间的平方差之和。这种差异是一个相似度的度量,我们将对其进行分析,以观察每种嵌入方法在受到噪音影响时的表现。
  4. 最后一步是绘制与合成附加噪音的规模相关的procrustes距离的变化,从而可以得出关于每种技术性能的结论。

让我们将上述步骤应用于经典的S曲线数据集:

import matplotlib.pyplot as pltfrom sklearn import manifold, datasetsimport numpy as npfrom scipy.spatial import procrustesfrom sklearn.decomposition import PCAdef compute_procrustes_distances(data, embedding_technique, max_noise, noise_step=0.05):    """    计算一系列高斯噪声水平的Procrustes距离。    参数:        data(np.array):原始数据集。        embedding_technique(object):流形学习技术的实例。        max_noise(float):要添加的最大噪声级别。        noise_step(float):噪声添加的增量步长。    返回:        list:每个噪声级别的Procrustes距离的列表。    """    base_embedding = embedding_technique.fit_transform(data)    noise_levels = np.arange(0, max_noise, noise_step)    distances = []    for noise in noise_levels:        noisy_data = data + np.random.normal(-noise, noise, data.shape)        noisy_embedding = embedding_technique.fit_transform(noisy_data)        _, _, disparity = procrustes(base_embedding, noisy_embedding)        distances.append(disparity)        return distancesdef plot_data(X, colour):    """    在3D中绘制数据集。    参数:        X(np.array):数据点。        colour(np.array):数据点的颜色映射。    """    fig = plt.figure(figsize=(30, 10))    ax = fig.add_subplot(111, projection='3d')    ax.scatter(X[:, 0], X[:, 1], X[:, 2], c=colour, cmap=plt.cm.Spectral)    ax.view_init(4, -72)    plt.show()def plot_procrustes_distances(noise_range, distances, labels):    """    绘制不同嵌入技术的Procrustes距离。    参数:        noise_range(np.array):噪声级别范围。        distances(dict):每种嵌入技术的距离字典。        labels(list):每种嵌入技术的标签列表。    """    plt.figure(figsize=(10, 6))    for label in labels:        plt.plot(noise_range, distances[label], label=label)    plt.xlabel('噪声范围')    plt.ylabel('Procrustes距离')    plt.title('嵌入技术比较')    plt.legend()    plt.show()# 生成并绘制S曲线数据集X,colour = datasets.make_s_curve(1000, random_state=0)plot_data(X, colour)# 计算不同嵌入技术的Procrustes距离max_noise = 2noise_range = np.arange(0, max_noise, 0.05)embedding_techniques = {    "PCA": PCA(2),    "ISOMAP": manifold.Isomap(n_components=2),    "LLE": manifold.LocallyLinearEmbedding(n_neighbors=10, n_components=2),    "SE": manifold.SpectralEmbedding(n_components=2, n_neighbors=10)}distances = {label: compute_procrustes_distances(X, technique, max_noise) for label, technique in embedding_techniques.items()}# 绘制计算得到的Procrustes距离plot_procrustes_distances(noise_range, distances, embedding_techniques.keys())

当然,在实际应用中,我们不会了解到真实的结构,就像这个虚假的例子一样,但现在让我们假设我们不知道真实的结构。从上图中我们可以得到什么?基本上,一个良好的趋势应该类似于一个Sigmoid曲线,最初我们可以看到对少量噪声有弹性,与原始空间非常相似的嵌入,然而随着噪声破坏了基本结构,会出现一个临界点。此时,我们期望Procrustes距离急剧增加,并且后续的嵌入只能捕捉到噪声,没有什么有意义的信息。考虑到这一点,以及上图,我们可以总结如下:

PCA:虽然Procrustes距离随噪声增加而增加,但趋势是相当线性的,因此可能无法捕捉到足够的真实结构信息。在不考虑其他趋势的情况下,这一点单独就足以表明需要非线性嵌入。

LLE:我们发现即使是微小的噪声,也几乎没有弹性,这可能是由于局部线性性的关键假设被违反所致。增加k,即最近邻居的数量,可能会减少该嵌入的脆弱性,但可能会以在生成子空间中丢失细节(信息)为代价。

ISOMAP:最初该嵌入技术的性能看起来还可以,但随着引入更多的噪声,可以明显地看出没有捕捉到足够的信息(噪声水平为0.25时存在线性趋势)。

SE:在探索的所有方法中,SE的性能最佳,尽管需要调整以获得最佳拟合。

总的来说,显然我们的数据结构存在于非线性流形上,并且用SE和ISOMAP似乎能够相当好地捕捉到非线性。考虑到这一点,以及LLE的糟糕表现,我们可以推断在原始空间中流形存在明显的曲率。让我们用另一个例子来探索这一点:

#生成并绘制S曲线数据集X,color = datasets.make_swiss_roll(1000, random_state=0)plot_data(X, color)#为不同的嵌入技术计算和绘制Procrustes距离max_noise = 4noise_range = np.arange(0, max_noise, 0.05)embedding_techniques = {    "PCA": PCA(2),    "ISOMAP":manifold.Isomap(n_components=2),    "LLE": manifold.LocallyLinearEmbedding(n_neighbors=10, n_components=2),    "SE": manifold.SpectralEmbedding(n_components=2, n_neighbors=10)}distances = {label: compute_procrustes_distances(X, technique, max_noise) for label, technique in embedding_techniques.items()}#绘制计算得到的Procrustes距离plot_procrustes_distances(noise_range, distances, embedding_techniques.keys())

使用前面概述的相同函数,我们可以获得每个嵌入技术的上述Procrustes距离趋势。很明显,数据中的结构是非常非线性的,因为我们观察到PCA无法捕捉到潜在结构。原始空间中的持续非线性特征也通过LLE的性能差来进一步强调,这可能是由于将邻域映射到切平面的不一致性。同样,SE和ISOMAP表现良好,后者在结构从噪声中变得破碎后比前者更快地趋向于Procrustes距离为1。可以合理地推测SE在所有嵌入中捕捉到了一些噪声,这可能可以通过调整参数来纠正。调整这些算法可以改善嵌入数据的泛化和拟合性能,下面是对上述ISOMAP技术进行调整的示例:

import numpy as npfrom scipy.spatial import procrustesimport matplotlib.pyplot as pltfrom sklearn import manifold, datasetsdef return_procrustes_distance(data, embedding_technique, max_noise, noise_step=0.05):    """    计算不同噪声水平下的Procrustes距离。    参数:数据(array_like):要嵌入的原始数据。    embedding_technique(对象):嵌入技术(例如PCA,SpectralEmbedding)。    max_noise(浮点数):要添加的最大噪声水平。    noise_step(浮点数):噪声水平的增量步长。    返回值:列表:每个噪声水平的Procrustes距离列表。    """    embeddings = []    distances = []    noise_range = np.arange(0, max_noise, noise_step)    for noise_level in noise_range:        noisy_data = data + np.random.normal(0, noise_level, data.shape)        embedded_data = embedding_technique.fit_transform(noisy_data)        if not embeddings:  # 如果嵌入列表为空            embeddings.append(embedded_data)        _, _, disparity = procrustes(embeddings[0], embedded_data)        distances.append(disparity)    return distances# 生成S曲线数据集X,_ = datasets.make_swiss_roll(1000, random_state=0)# 参数max_noise = 2k_values = [5, 7, 9]  # ISOMAP的不同k值# 计算并绘制每个k值下的Procrustes距离noise_levels = np.arange(0, max_noise, 0.05)plt.figure(figsize=(10, 6))for k in k_values:    embedding = manifold.Isomap(n_components=2, n_neighbors=k)    procrustes_distances = return_procrustes_distance(X, embedding, max_noise)    plt.plot(noise_levels, procrustes_distances, label=f'ISOMAP (k={k})')plt.xlabel('噪声水平')plt.ylabel('Procrustes距离')plt.title('不同k值下ISOMAP的噪声水平的Procrustes距离')plt.legend()plt.show()

上面是调整的一个非常通用的例子,你肯定会想探索其他的参数,但原则就是通过简单调整 k 并观察噪声的影响,我们可以开始看到算法更好地泛化。

结论

在本文中,我们探讨了各种流形学习技术,并展示了如何通过噪声注入来更好地理解高维数据中的潜在结构。我们通过利用对每个算法如何工作的理解,每个算法的基本假设以及分析噪声对嵌入的影响来实现这一目标。有了这些洞察力,我们可以更明智地决策如何在传递给后续机器学习流程之前预处理或处理数据。这种方法还丰富了我们对所得嵌入的泛化以及其如何响应噪声或潜在未来数据漂移的理解。

无论您计划将嵌入技术作为机器学习解决方案的一部分部署,还是希望扩展您进行探索性数据分析的过程,上述方法都将帮助您更深入地了解任何高维数据集中隐藏的结构。

除非另有说明,所有图片均由作者提供。