你好,我是kk~
字节的算法专家,今天和我们在聊了一些算法的核心思想。
趁着这个机会,咱们今天把Kernel PCA分享给大家。
核主成分分析(Kernel Principal Component Analysis, Kernel PCA) 是主成分分析(PCA)的非线性扩展,利用核技巧将数据从原始空间映射到高维特征空间,在该特征空间中进行线性PCA以捕获数据的非线性特征。核PCA非常适合处理非线性的数据结构。
核PCA核心思想
PCA的核心在于通过线性变换寻找数据分布的主轴。然而,PCA是一种线性方法,如果数据在原空间中的分布是非线性的,PCA的效果就不理想。核PCA通过核技巧(Kernel Trick)来解决这个问题,它将数据映射到高维空间,在高维空间中找到数据的线性结构,从而捕获原始数据的非线性结构。
具体步骤如下:
1. 数据映射到高维空间:通过非线性映射函数 ,将输入数据 从原空间映射到高维空间。
2. 在高维空间进行PCA:计算映射后数据的协方差矩阵,并进行特征值分解,找到特征向量和特征值。
3. 利用核函数避免显式映射:通过核函数 来直接计算点积,从而避免直接计算高维特征空间中的映射 。
核PCA数学推导
假设我们有 个数据点 ,目标是找到主成分并降维。PCA通过以下步骤实现:
1. 中心化数据:对数据进行均值中心化处理:
2. 计算协方差矩阵:
3. 求协方差矩阵的特征值和特征向量:通过对协方差矩阵 进行特征值分解,找到对应的特征向量作为主成分方向。
核PCA的高维映射
现在我们考虑核PCA。假设有一个非线性映射函数 ,将输入数据从原空间映射到高维空间:
其中 是一个高维甚至无限维的特征空间。
目标:在这个高维空间中做PCA。我们需要计算映射后数据的协方差矩阵,并进行特征值分解。
1. 中心化数据:数据映射后,我们首先对数据进行均值中心化:
2. 协方差矩阵:在高维空间中,协方差矩阵为:
我们的目标是对这个协方差矩阵进行特征值分解,找到特征值和特征向量。
核技巧(Kernel Trick)
由于 是高维甚至无限维的空间,显式计算 的点积会非常昂贵。核技巧允许我们通过核函数 来直接计算特征空间中点积 ,避免显式映射。
核函数 定义为:
常用的核函数包括:
线性核:$ 多项式核:$ 高斯RBF核:$
特征值分解
由于核技巧,我们实际上只需要处理核矩阵 ,其中:
此时我们需要对核矩阵 进行特征值分解。
设核矩阵的特征值分解为:
其中 是特征向量矩阵, 是特征值矩阵。
在PCA中,我们希望在高维空间中找到主成分的方向,即特征向量 ,满足:
结合核技巧和特征值分解的结果,特征向量 可以表示为数据在高维空间的线性组合:
通过将这一表达式代入到特征方程中,并利用核矩阵的性质,可以推导出以下结果:
其中 是特征向量的系数向量。
核PCA的具体步骤
总结核PCA的步骤如下:
**1. 构建核矩阵 **:
计算核矩阵 。 对核矩阵进行中心化:令 为全为 的 矩阵,有:
2. 特征值分解:
对中心化后的核矩阵 进行特征值分解,得到特征值和特征向量。
3. 选择主成分:
根据特征值的大小选择前 个最大的特征值对应的特征向量。 核PCA降维后的新表示为:
其中 是特征值分解得到的特征向量的分量。
核PCA的本质是通过核技巧将原始数据映射到一个高维特征空间,在该空间中进行线性PCA以捕捉数据的非线性结构。通过利用核函数避免了直接计算高维空间中的点积,从而使得计算变得更加高效。核PCA的推导建立在特征空间中的PCA基础上,通过核函数 和特征值分解来获得主成分方向和降维后的数据表示。
案例
大致步骤:
1. 生成虚拟数据:我们将生成一些具有复杂非线性分布的数据集,以展示标准线性PCA的局限性,以及核PCA如何帮助我们捕捉非线性特征。
2. 应用核主成分分析:使用Python的sklearn
库进行核PCA,并展示如何将数据投影到低维空间。
3. 生成复杂且鲜艳的图形:绘制多个图形,包括原始数据、PCA和核PCA降维后的数据分布,分析结果。
import numpy as np
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA, KernelPCA
from sklearn.datasets import make_moons, make_circles
from sklearn.preprocessing import StandardScaler
# 生成两个非线性数据集:半月形和同心圆数据集
def generate_datasets():
# 半月形数据集
X_moons, y_moons = make_moons(n_samples=2000, noise=0.1, random_state=42)
# 同心圆数据集
X_circles, y_circles = make_circles(n_samples=1000, noise=0.05, factor=0.4, random_state=42)
return (X_moons, y_moons), (X_circles, y_circles)
# 标准化数据
def standardize_data(X):
scaler = StandardScaler()
return scaler.fit_transform(X)
# 绘制原始数据集
def plot_original_data(X_moons, y_moons, X_circles, y_circles):
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 6))
# 绘制半月形数据集
ax1.scatter(X_moons[y_moons == 0][:, 0], X_moons[y_moons == 0][:, 1], color='red', s=30, label='Class 0')
ax1.scatter(X_moons[y_moons == 1][:, 0], X_moons[y_moons == 1][:, 1], color='blue', s=30, label='Class 1')
ax1.set_title('Original Moons Dataset', fontsize=15)
ax1.legend()
ax1.grid(True)
# 绘制同心圆数据集
ax2.scatter(X_circles[y_circles == 0][:, 0], X_circles[y_circles == 0][:, 1], color='green', s=30, label='Class 0')
ax2.scatter(X_circles[y_circles == 1][:, 0], X_circles[y_circles == 1][:, 1], color='purple', s=30, label='Class 1')
ax2.set_title('Original Circles Dataset', fontsize=15)
ax2.legend()
ax2.grid(True)
plt.show()
# PCA和Kernel PCA降维后的数据可视化
def plot_pca_kpca(X_moons, y_moons, X_circles, y_circles):
# 标准化数据
X_moons_std = standardize_data(X_moons)
X_circles_std = standardize_data(X_circles)
# 定义PCA和Kernel PCA
pca = PCA(n_components=2)
kpca_rbf = KernelPCA(n_components=2, kernel='rbf', gamma=15)
kpca_poly = KernelPCA(n_components=2, kernel='poly', degree=3, gamma=1, coef0=1)
# 计算PCA降维结果
X_moons_pca = pca.fit_transform(X_moons_std)
X_circles_pca = pca.fit_transform(X_circles_std)
# 计算核PCA降维结果 (RBF核和多项式核)
X_moons_kpca_rbf = kpca_rbf.fit_transform(X_moons_std)
X_moons_kpca_poly = kpca_poly.fit_transform(X_moons_std)
X_circles_kpca_rbf = kpca_rbf.fit_transform(X_circles_std)
X_circles_kpca_poly = kpca_poly.fit_transform(X_circles_std)
# 绘制PCA和Kernel PCA降维的可视化结果
fig, axes = plt.subplots(3, 2, figsize=(14, 18))
# 半月形数据集PCA
axes[0, 0].scatter(X_moons_pca[y_moons == 0][:, 0], X_moons_pca[y_moons == 0][:, 1], color='red', s=30,
label='Class 0')
axes[0, 0].scatter(X_moons_pca[y_moons == 1][:, 0], X_moons_pca[y_moons == 1][:, 1], color='blue', s=30,
label='Class 1')
axes[0, 0].set_title('Moons PCA', fontsize=15)
axes[0, 0].legend()
axes[0, 0].grid(True)
# 同心圆数据集PCA
axes[0, 1].scatter(X_circles_pca[y_circles == 0][:, 0], X_circles_pca[y_circles == 0][:, 1], color='green', s=30,
label='Class 0')
axes[0, 1].scatter(X_circles_pca[y_circles == 1][:, 0], X_circles_pca[y_circles == 1][:, 1], color='purple', s=30,
label='Class 1')
axes[0, 1].set_title('Circles PCA', fontsize=15)
axes[0, 1].legend()
axes[0, 1].grid(True)
# 半月形数据集RBF核PCA
axes[1, 0].scatter(X_moons_kpca_rbf[y_moons == 0][:, 0], X_moons_kpca_rbf[y_moons == 0][:, 1], color='red', s=30,
label='Class 0')
axes[1, 0].scatter(X_moons_kpca_rbf[y_moons == 1][:, 0], X_moons_kpca_rbf[y_moons == 1][:, 1], color='blue', s=30,
label='Class 1')
axes[1, 0].set_title('Moons Kernel PCA (RBF)', fontsize=15)
axes[1, 0].legend()
axes[1, 0].grid(True)
# 同心圆数据集RBF核PCA
axes[1, 1].scatter(X_circles_kpca_rbf[y_circles == 0][:, 0], X_circles_kpca_rbf[y_circles == 0][:, 1],
color='green', s=30, label='Class 0')
axes[1, 1].scatter(X_circles_kpca_rbf[y_circles == 1][:, 0], X_circles_kpca_rbf[y_circles == 1][:, 1],
color='purple', s=30, label='Class 1')
axes[1, 1].set_title('Circles Kernel PCA (RBF)', fontsize=15)
axes[1, 1].legend()
axes[1, 1].grid(True)
# 半月形数据集多项式核PCA
axes[2, 0].scatter(X_moons_kpca_poly[y_moons == 0][:, 0], X_moons_kpca_poly[y_moons == 0][:, 1], color='red', s=30,
label='Class 0')
axes[2, 0].scatter(X_moons_kpca_poly[y_moons == 1][:, 0], X_moons_kpca_poly[y_moons == 1][:, 1], color='blue', s=30,
label='Class 1')
axes[2, 0].set_title('Moons Kernel PCA (Polynomial)', fontsize=15)
axes[2, 0].legend()
axes[2, 0].grid(True)
# 同心圆数据集多项式核PCA
axes[2, 1].scatter(X_circles_kpca_poly[y_circles == 0][:, 0], X_circles_kpca_poly[y_circles == 0][:, 1],
color='green', s=30, label='Class 0')
axes[2, 1].scatter(X_circles_kpca_poly[y_circles == 1][:, 0], X_circles_kpca_poly[y_circles == 1][:, 1], color='purple',
s=30, label='Class 1')
axes[2, 1].set_title('Circles Kernel PCA (Polynomial)', fontsize=15)
axes[2, 1].legend()
axes[2, 1].grid(True)
plt.show()
# 主函数
def main():
# 生成数据
(X_moons, y_moons), (X_circles, y_circles) = generate_datasets()
# 绘制原始数据
plot_original_data(X_moons, y_moons, X_circles, y_circles)
# 应用PCA和Kernel PCA并绘图
plot_pca_kpca(X_moons, y_moons, X_circles, y_circles)
if __name__ == "__main__":
main()
1. 数据生成:使用make_moons
和make_circles
函数生成两组具有非线性分布的数据,分别是半月形数据集和同心圆数据集。
2. 数据标准化:我们使用StandardScaler
对数据进行标准化处理,以确保不同维度的数据具有相同的尺度,这对于PCA和核PCA是重要的预处理步骤。
3. PCA和核PCA降维:我们分别应用PCA和两种不同的核PCA(RBF核和多项式核)来对数据进行降维。RBF核是一种常用的核函数,能够捕捉复杂的非线性模式,而多项式核可以帮助我们探索多项式特征之间的关系。
原始数据的可视化:绘制了半月形和同心圆数据集的原始二维表示。 PCA降维结果:展示了标准PCA在非线性数据上的效果,通常无法很好地分离数据。
核PCA降维结果:通过RBF和多项式核PCA,我们能够更好地捕捉数据的非线性特征,并在低维空间中将类进行更好的分离。
通过整个的理论和案例,大家清楚地看到核PCA的强大之处。与标准PCA不同,核PCA可以通过非线性映射,捕捉复杂的非线性特征,特别适合非线性分布的数据集。