大数跨境

彻底讲透!维度灾难 !

彻底讲透!维度灾难 ! 机器学习和人工智能AI
2026-09-11
5

哈喽,大家好~

很常见的一个问题:特征是不是越多越好?模型怎么训练集效果不错,一到测试集就不行了?

很多同学刚接触机器学习时,会下意识觉得:多给模型一些特征,信息肯定更丰富。可实际情况经常相反,特征一多,模型反而更难学。这就是机器学习里非常经典的概念:维度灾难

维度灾难

可以先想象一个一维空间。

假设我们要在数轴上找数据点,区间长度是 1,平均放 10 个样本,点与点之间并不算稀疏。

现在换成二维平面。如果还想保持同样密度,就不能只要 10 个样本了,而是大约需要   个样本。

继续升到 10 维呢?需要的样本数量会变成:

这里   表示每个维度上希望划分的数量,  表示特征维度。

假设每个维度都分成 10 份,10 维空间就需要:

也就是 100 亿个样本。

一句话概括:维度升高后,空间体积会指数级膨胀,原本看起来不少的数据,瞬间就变稀疏了。

而机器学习模型,尤其是 KNN、聚类、核方法这类依赖“距离”的模型,就会开始迷路。

为什么距离会失效?

维度灾难有一个很关键的现象,叫作距离集中

在低维空间里,最近邻和最远邻的距离差别很明显。你找最近的样本,确实能找到“和我很像”的人。

但在高维空间中,最近点和最远点的距离会越来越接近:

其中,  是最远样本距离,  是最近样本距离。

这意味着什么?

原来 KNN 可以说:“离我最近的几个点,大概率和我属于一类。”

可高维时它会发现:“大家离我都差不多远,那我到底该信谁?”

所以,维度灾难并不是说高维数据一定不能用,而是告诉我们:维度增加后,数据量、特征质量和模型选择都要跟着升级。

一个通俗例子

两个有用特征,加上一堆噪声,我们构造一个二分类问题。

数据真正有价值的只有两个特征,分别记作     。它们能把两类样本分开。然后我们不断加入随机噪声特征,看看 KNN 的效果会怎样。

同时,我们生成两张分析图:

  • 第一张图同时观察维度升高后的分类准确率、距离集中程度,以及 PCA 降维后的改善效果。
  • 第二张图展示原始高维 KNN 和 PCA 降维后 KNN 的分类边界。
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_moons
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.decomposition import PCA
from sklearn.pipeline import Pipeline
from sklearn.metrics import accuracy_score

np.random.seed(42)

# 两个月牙形状的数据:前两个特征是真正有效的信息
X_signal, y = make_moons(n_samples=1200, noise=0.22, random_state=42)

dims = [25102050100]
raw_scores, pca_scores, distance_ratios = [], [], []

for dim in dims:
    noise = np.random.normal(01, size=(len(X_signal), dim - 2))
    X = np.hstack([X_signal, noise])

    X_train, X_test, y_train, y_test = train_test_split(
        X, y, test_size=0.3, stratify=y, random_state=42
    )

    # 直接在高维空间训练 KNN
    knn = KNeighborsClassifier(n_neighbors=15)
    knn.fit(X_train, y_train)
    raw_scores.append(accuracy_score(y_test, knn.predict(X_test)))

    # PCA 保留 2 个主成分后,再训练 KNN
    pca_knn = Pipeline([
        ("pca", PCA(n_components=2, random_state=42)),
        ("knn", KNeighborsClassifier(n_neighbors=15))
    ])
    pca_knn.fit(X_train, y_train)
    pca_scores.append(accuracy_score(y_test, pca_knn.predict(X_test)))

    # 计算测试集中每个样本到最近点、最远点的距离差异
    sample = X_test[:200]
    dist = np.sqrt(((sample[:, None, :] - sample[None, :, :]) ** 2).sum(axis=2))
    dist[dist == 0] = np.nan
    d_min = np.nanmin(dist, axis=1).mean()
    d_max = np.nanmax(dist, axis=1).mean()
    distance_ratios.append((d_max - d_min) / d_min)

# 图 1:维度、准确率、距离集中现象
fig, axes = plt.subplots(12, figsize=(145))

axes[0].plot(dims, raw_scores, "o-", lw=3, ms=8,
             color="#ff3b30", label="原始高维 KNN")
axes[0].plot(dims, pca_scores, "s-", lw=3, ms=8,
             color="#007aff", label="PCA(2维) + KNN")
axes[0].set_xlabel("特征维度")
axes[0].set_ylabel("测试集准确率")
axes[0].set_title("维度升高后,噪声如何影响分类效果")
axes[0].legend()

axes[1].plot(dims, distance_ratios, "D-", lw=3, ms=8, color="#af52de")
axes[1].set_xlabel("特征维度")
axes[1].set_ylabel("(最远距离 - 最近距离) / 最近距离")
axes[1].set_title("距离集中:高维下远近差异不断缩小")

plt.tight_layout()
plt.show()

# 图 2:50维时,观察原始模型与降维模型的分类边界
noise = np.random.normal(01, size=(len(X_signal), 48))
X_50 = np.hstack([X_signal, noise])

raw_model = KNeighborsClassifier(n_neighbors=15).fit(X_50, y)
pca_model = Pipeline([
    ("pca", PCA(n_components=2, random_state=42)),
    ("knn", KNeighborsClassifier(n_neighbors=15))
]).fit(X_50, y)

xx, yy = np.meshgrid(
    np.linspace(-23250),
    np.linspace(-1.52250)
)
grid_signal = np.c_[xx.ravel(), yy.ravel()]
grid_noise = np.zeros((len(grid_signal), 48))
grid_50 = np.hstack([grid_signal, grid_noise])

fig, axes = plt.subplots(12, figsize=(145))

for ax, model, title in zip(
    axes,
    [raw_model, pca_model],
    ["原始 50 维 KNN""PCA 降维后 KNN"]
):
    Z = model.predict(grid_50).reshape(xx.shape)
    ax.contourf(xx, yy, Z, alpha=0.35, cmap="coolwarm")
    ax.scatter(X_signal[:, 0], X_signal[:, 1], c=y,
               cmap="coolwarm", edgecolor="white", s=28)
    ax.set_title(title)
    ax.set_xlabel("有效特征 $x_1$")
    ax.set_ylabel("有效特征 $x_2$")

plt.tight_layout()
plt.show()

第一张图里,随着维度从 2 增加到 100,原始 KNN 的测试准确率会明显下降。

原因很简单:后面加入的特征全是噪声,但 KNN 计算欧氏距离时,会把这些噪声也算进去。原来真正有区分度的  ,反而被淹没了。

右侧曲线则展示了距离集中现象。数值越低,说明最近邻和最远邻之间的差距越小。高维空间里,“近”这个概念越来越不可靠。

第二张图更直观。左图的原始高维 KNN 边界会变得混乱,模型被噪声带偏。

右图通过 PCA 把主要信息压缩到低维后,边界重新贴合了两个月牙形状。

总之就是说:降维不只是为了画图,它还能过滤噪声、减少计算量,并让部分模型重新找到有效距离。

面对维度灾难,我们这里一般从三个方向入手。

第一,做特征选择。不要把所有字段都塞进模型,优先保留和目标变量真正相关的特征。

第二,做降维。PCA、LDA、UMAP 这些方法都很常见。PCA 适合先快速压缩冗余信息,LDA 更适合带标签的分类任务。

第三,换模型。树模型对无关特征通常比 KNN 更稳一些;正则化模型,例如 Lasso、Ridge,也能抑制无用特征的影响。

总结

维度灾难的核心,不是“高维不好”,而是高维空间需要更多数据,也更容易让距离和噪声主导模型判断。

我们今天用 KNN 看到了两个现象:维度升高会让测试效果下降,最近邻和最远邻会越来越难区分。实验中,大家可以继续尝试用 PCA 对比不同保留维度,也可以把 KNN 换成随机森林、XGBoost,观察不同模型对高维噪声的敏感程度。

【声明】内容源于网络
0
0
机器学习和人工智能AI
让我们一起期待 AI 带给我们的每一场变革!推送最新行业内最新最前沿人工智能技术!
内容 384
粉丝 0
机器学习和人工智能AI 让我们一起期待 AI 带给我们的每一场变革!推送最新行业内最新最前沿人工智能技术!
总阅读5.5k
粉丝0
内容384