面试小米,惨不忍睹。。。

教育   2024-11-12 16:16   北京  

哈喽,我是Johngo~

今天有一位读者拿到了小米职能部的offer。

有一个有趣的事情分享过来,就是在一系列的面试环节中,都非常的顺畅。维度,问到了核PCA,居然想不到一些核心的逻辑,回答的惨不忍睹。

在其他的环节中,还是不错的,最终还是一个非常好的结果。

今天这里也和大家聊聊「核PCA」~

每天分享干货,记得关注~


核主成分分析(Kernel Principal Component Analysis,简称核PCA)是一种基于核方法的非线性降维技术。它通过将数据映射到高维特征空间来捕捉数据的非线性结构,而传统的PCA是在线性空间中进行降维的。核PCA的核心思想是通过核技巧避免显式地进行特征空间的高维映射,只在低维空间中计算内积,从而减少计算的复杂性。

下面,咱们通过详细的推理和案例好好和大家聊聊~

1. 核PCA的基本思路

传统的PCA通过求解数据的协方差矩阵,并选择其特征值最大的特征向量来进行降维。核PCA则是将这种降维方法推广到非线性情况下。它的核心思想是通过非线性映射(使用核函数)将数据从低维空间映射到高维特征空间,在这个特征空间中进行PCA降维。

核PCA的步骤如下:

  1. 选择核函数:选择合适的核函数  来计算数据点之间的相似度。
  2. 中心化数据:数据需要在高维特征空间中进行中心化处理,这一步是核PCA的关键。
  3. 计算核矩阵:构建核矩阵 ,它是数据点之间的内积矩阵。
  4. 计算特征值和特征向量:对中心化后的核矩阵进行特征值分解,得到主成分。
  5. 选择主成分:选择前  个最大的特征值对应的特征向量,进行降维。

2. 核PCA的数学推导

假设我们有一个数据集 ,其中每个 。传统PCA的目标是通过线性变换找到一个正交基,使得数据在这些基上的方差最大。

非线性映射

我们首先考虑将数据映射到一个更高维的特征空间 ,通过一个非线性映射  将每个样本  映射到特征空间中:

在高维特征空间中,我们可以计算数据点之间的内积:

其中  是核函数。常见的核函数有:

  • 线性核
  • 高斯核
  • 多项式核

通过核函数,我们可以计算高维特征空间中的内积而不需要显式地计算 

数据中心化

PCA的一个关键步骤是数据中心化,意味着数据的均值应为零。为了在高维空间中进行类似的操作,我们需要对核矩阵进行中心化。

定义核矩阵 ,其元素为 。接着,我们对  进行中心化:

其中, 是一个全为 1 的  列向量。

特征分解

接下来,我们对中心化后的核矩阵  进行特征值分解:

其中, 是特征值, 是对应的特征向量。通过特征值分解,我们可以得到各个主成分。

降维

选择前  个最大的特征值对应的特征向量 ,作为新的基进行降维。

降维后的数据表示为:

这样,数据就从原来的低维空间降到了新的 -维空间。

核PCA 实践案例

1. 问题背景

在高维数据中,传统的PCA方法常常受限于数据的线性性质。核PCA提供了一个有效的非线性降维方法,通过使用核函数将数据映射到更高维的特征空间,在高维空间中进行PCA降维。我们将使用虚拟数据集,使用高斯核(Gaussian Kernel)进行映射,展示核PCA的效果。

2. 核PCA的目标

我们将使用核PCA方法:

  • 生成一个二维的虚拟数据集,其中数据具有非线性的分布。
  • 应用核PCA对数据进行降维处理。
  • 通过图形展示降维前后的数据分布。

3. 核PCA Python实现

数据生成

我们首先生成一个二维的非线性数据集,可以通过组合正弦和余弦函数生成一个复杂的数据集。数据点会呈现出复杂的曲线分布,适合展示核PCA的优势。

核PCA的实现

我们将使用scikit-learn库中的PCA功能,但结合核技巧来实现核PCA。核函数的选择将使用高斯核(RBF核)。然后,我们将使用matplotlibseaborn进行数据可视化。

代码实现

代码包含数据生成、模型训练以及图形绘制。

import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_moons
from sklearn.preprocessing import StandardScaler
from sklearn.metrics.pairwise import rbf_kernel

# 核PCA实现
class KernelPCA:
    def __init__(self, kernel="rbf", gamma=1.0):
        self.kernel = kernel
        self.gamma = gamma
        self.alpha_ = None
        self.eigenvalues_ = None
        self.eigenvectors_ = None
        self.X_centered_ = None
        self.K_ = None

    def fit(self, X):
        # 计算核矩阵
        if self.kernel == "rbf":
            K = rbf_kernel(X, X, gamma=self.gamma)
        else:
            raise ValueError("Unsupported kernel")

        # 数据中心化:K' = K - 1 * K - K * 1 + 1 * K * 1
        n_samples = X.shape[0]
        one_n = np.ones((n_samples, n_samples)) / n_samples
        K_centered = K - one_n.dot(K) - K.dot(one_n) + one_n.dot(K).dot(one_n)

        # 特征值分解
        eigenvalues, eigenvectors = np.linalg.eigh(K_centered)

        # 排序特征值和特征向量
        sorted_indices = np.argsort(eigenvalues)[::-1]
        self.eigenvalues_ = eigenvalues[sorted_indices]
        self.eigenvectors_ = eigenvectors[:, sorted_indices]
        
        # 保存中心化后的核矩阵
        self.K_ = K_centered

    def transform(self, X):
        # 重新计算核矩阵
        K = rbf_kernel(X, X, gamma=self.gamma)

        # 数据中心化
        n_samples = X.shape[0]
        one_n = np.ones((n_samples, n_samples)) / n_samples
        K_centered = K - one_n.dot(K) - K.dot(one_n) + one_n.dot(K).dot(one_n)

        # 投影到特征空间
        return np.dot(K_centered, self.eigenvectors_)

# 数据生成:通过make_moons生成一个非线性数据集
X, y = make_moons(n_samples=3000, noise=0.1, random_state=42)

# 标准化数据
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# 核PCA降维
kpca = KernelPCA(kernel="rbf", gamma=1.0)
kpca.fit(X_scaled)
X_kpca = kpca.transform(X_scaled)

# 绘制原始数据和降维后的数据
fig, axs = plt.subplots(12, figsize=(146))

# 绘制原始数据
axs[0].scatter(X[:, 0], X[:, 1], c=y, cmap="coolwarm", edgecolor='k', s=80)
axs[0].set_title("Original Data (Moons)")
axs[0].set_xlabel("Feature 1")
axs[0].set_ylabel("Feature 2")

# 绘制核PCA降维后的数据
axs[1].scatter(X_kpca[:, 0], X_kpca[:, 1], c=y, cmap="coolwarm", edgecolor='k', s=80)
axs[1].set_title("Data After KernelPCA")
axs[1].set_xlabel("PC 1")
axs[1].set_ylabel("PC 2")

plt.tight_layout()
plt.show()
  1. KernelPCA类:我们自定义了一个类KernelPCA,用于实现核PCA。类初始化时选择核函数(默认为RBF核)以及核函数的参数gamma。然后在fit方法中计算核矩阵,并进行中心化和特征值分解。transform方法用于将数据映射到降维后的空间。

  2. 数据生成:我们使用make_moons函数生成一个具有噪声的双月型数据集。该数据集具有明显的非线性分布,适合展示核PCA的优势。

  3. 数据标准化:在应用核PCA之前,我们对数据进行了标准化,确保每个特征的均值为0,方差为1。

  4. 降维与可视化

  • 我们将数据投影到核PCA的前两个主成分,并绘制原始数据与降维后的数据对比。
  • 使用matplotlib进行可视化,左图为原始数据,右图为降维后的数据。通过不同的颜色展示数据点的类别。

左图:原始的双月型数据。数据点分布呈现出非线性的特点。

右图:降维后的数据。通过核PCA,数据成功映射到一个新的特征空间,并且非线性关系在二维空间中得到了很好的展现。

6. 进一步的分析

通过核PCA降维后的数据,可以进一步分析数据的分布情况,观察如何通过非线性映射捕获数据的内部结构。例如,我们可以通过增加降维后的主成分数量,来查看数据在更高维空间中的表现。

降维到更高维空间

我们可以尝试将数据降到三个主成分,并绘制三维图形。

# 使用核PCA将数据降到三维
X_kpca_3d = kpca.transform(X_scaled)

# 3D绘图
from mpl_toolkits.mplot3d import Axes3D

fig = plt.figure(figsize=(108))
ax = fig.add_subplot(111, projection='3d')

# 绘制降维后的数据
scatter = ax.scatter(X_kpca_3d[:, 0], X_kpca_3d[:, 1], X_kpca_3d[:, 2], c=y, cmap='coolwarm', s=80)
ax.set_title("3D Projection After KernelPCA")
ax.set_xlabel("PC 1")
ax.set_ylabel("PC 2")
ax.set_zlabel("PC 3")

# 添加色条
fig.colorbar(scatter)
plt.show()

运行后,将会展示降维后的三维数据分布,使我们能够更好地理解核PCA在捕捉数据结构中的作用。

通过核PCA,我们成功地将复杂的非线性数据集降到二维空间,并能通过视觉化展示出数据的内在结构。非线性的数据关系在降维后的空间中变得更加可分,展示了核PCA的强大非线性映射能力。

核PCA是一种强大的非线性降维工具,能够捕捉传统PCA无法捕捉的数据结构。通过核函数将数据映射到高维空间,再通过特征分解进行降维,核PCA能够有效地在复杂的数据集中找到潜在的低维结构。通过可视化,我们能够清晰地看到降维前后数据的变化,展现了核PCA在处理非线性数据时的优势。

最后,给大家一个限时福利,超值 1 元,手慢无~

Johngo学长
机器学习算法和大数据重度研究者! 持续产出机器学习、大数据、Python、LeetCode干货~
 最新文章