欢迎来到第12章:主成分分析
本章学习目标
今天,我们将深入探讨一种强大的无监督学习方法:主成分分析 (Principal Component Analysis, PCA)。课程结束后,我希望你们能够:
量化投资的困境:信号过载
预测股票回报是金融界永恒的圣杯。在量化投资中,我们已经发现了成百上千个潜在的投资“信号”或“因子”。
- 价值 (市盈率, 市净率…)
- 动量 (过去12个月回报…)
- 质量 (ROA, ROE…)
- 波动率 (Beta, IV…)
- …还有成百上千个
核心问题是: 这么多的信号,真的是越多越好吗?
挑战 1: 维度灾难
直接将所有信号作为模型特征,会遇到严重的问题:维度灾难 (Curse of Dimensionality)。
- 拖慢训练速度: 特征越多,计算量呈指数级增长。
- 导致过拟合: 模型过于复杂,会学习到训练数据中的噪音,而不是真正的规律。
挑战 2: 多重共线性
许多信号其实衡量的是相似的经济现象,它们之间高度相关。这就是多重共线性 (Multicollinearity)。
- 例如: 市盈率 (P/E), 市净率 (P/B), 市销率 (P/S) 都反映了公司的“价值”维度。
- 后果: 导致模型参数的估计极其不稳定,难以解释模型的真正驱动因素。
解决方案: 主成分分析 (PCA)
PCA 提供了一个优雅的解决方案,其核心思想是:数据降维 (Dimensionality Reduction)。
将大量相关的原始变量,转换为少数几个互不相关的新变量,即主成分 (Principal Components)。
我们的路线图
直观理解:从一个简单例子开始
让我们从一个最简单的例子开始。假设我们有两个高度相关的投资指标,比如市销率 (Price-to-Sales, x₁) 和 市盈率 (Price-to-Earnings, x₂)。
我们的数据点分布在一个二维平面上。
数据点的分布揭示了主要变化方向
下图展示了标准化后的市销率 (x₁) 和市盈率 (x₂) 的散点图。我们可以清晰地看到数据点并非随机分布,而是沿着某个方向呈现出最强的变化趋势。
Code
import numpy as np
import matplotlib.pyplot as plt
# --- 生成模拟数据 ---
np.random.seed(42)
# 创建两个相关的变量
X1 = np.random.randn(20)
X2 = X1 * 0.8 + np.random.randn(20) * 0.5
X = np.vstack((X1, X2)).T
# --- 标准化 ---
X_std = (X - X.mean(axis=0)) / X.std(axis=0)
# --- 可视化 ---
plt.figure(figsize=(8, 6))
plt.scatter(X_std[:, 0], X_std[:, 1], c='#0d6efd', s=60, alpha=0.8, edgecolors='w', label='原始数据点 (估值指标)')
plt.xlabel('标准化市销率 (x₁)', fontsize=12)
plt.ylabel('标准化市盈率 (x₂)', fontsize=12)
plt.title('数据分布揭示了主要变化方向', fontsize=14)
plt.axhline(0, color='grey', lw=0.5)
plt.axvline(0, color='grey', lw=0.5)
plt.grid(True)
plt.gca().set_aspect('equal', adjustable='box')
plt.legend()
plt.show()
PCA的目标:找到捕捉最大方差的新坐标轴
PCA的任务就是找到一个新的坐标系。
- 第一主成分 (PC1): 是穿过数据点云、能最大化数据投影方差的那个轴。它代表了数据中最重要的变化方向。
Code
from sklearn.decomposition import PCA
# --- 执行PCA ---
pca = PCA(n_components=2)
pca.fit(X_std)
components = pca.components_
explained_variance = pca.explained_variance_
# --- 可视化 ---
plt.figure(figsize=(8, 6))
plt.scatter(X_std[:, 0], X_std[:, 1], c='#0d6efd', s=60, alpha=0.8, edgecolors='w', label='原始数据点')
plt.xlabel('标准化市销率 (x₁)', fontsize=12)
plt.ylabel('标准化市盈率 (x₂)', fontsize=12)
plt.title('PCA找到了捕捉最大方差的新坐标轴', fontsize=14)
plt.axhline(0, color='grey', lw=0.5)
plt.axvline(0, color='grey', lw=0.5)
plt.grid(True)
plt.gca().set_aspect('equal', adjustable='box')
# --- 绘制主成分方向 ---
arrow_len_pc1 = np.sqrt(explained_variance[0]) * 3
pc1_vec = components[0, :]
plt.arrow(0, 0, pc1_vec[0] * arrow_len_pc1, pc1_vec[1] * arrow_len_pc1,
head_width=0.2, head_length=0.3, fc='#dc3545', ec='#dc3545', lw=2,
label='第一主成分 (PC1)', zorder=3)
plt.legend()
plt.show()
…然后找到与PC1正交的次要方向
- 第二主成分 (PC2): 与PC1正交(垂直),并捕捉剩余方差中最大的部分。
Code
# --- 可视化 ---
plt.figure(figsize=(8, 6))
plt.scatter(X_std[:, 0], X_std[:, 1], c='#0d6efd', s=60, alpha=0.8, edgecolors='w', label='原始数据点')
plt.xlabel('标准化市销率 (x₁)', fontsize=12)
plt.ylabel('标准化市盈率 (x₂)', fontsize=12)
plt.title('PCA找到了捕捉最大方差的新坐标轴', fontsize=14)
plt.axhline(0, color='grey', lw=0.5)
plt.axvline(0, color='grey', lw=0.5)
plt.grid(True)
plt.gca().set_aspect('equal', adjustable='box')
# --- 绘制主成分方向 ---
arrow_len_pc1 = np.sqrt(explained_variance[0]) * 3
pc1_vec = components[0, :]
plt.arrow(0, 0, pc1_vec[0] * arrow_len_pc1, pc1_vec[1] * arrow_len_pc1,
head_width=0.2, head_length=0.3, fc='#dc3545', ec='#dc3545', lw=2,
label='第一主成分 (PC1)', zorder=3)
arrow_len_pc2 = np.sqrt(explained_variance[1]) * 3
pc2_vec = components[1, :]
plt.arrow(0, 0, pc2_vec[0] * arrow_len_pc2, pc2_vec[1] * arrow_len_pc2,
head_width=0.2, head_length=0.3, fc='#fd7e14', ec='#fd7e14', lw=2,
label='第二主成分 (PC2)', zorder=3)
plt.legend()
plt.show()
降维的实现:将数据投影到PC1上
如果我们想把二维数据降为一维,最理想的方式就是将所有数据点投影 (Project)到第一主成分 (PC1) 这个新轴上。
- 原始的二维坐标
(x₁, x₂)
就变成了一个新的一维坐标 z₁
。
- 这个
z₁
就是我们降维后的新特征,它最大程度地保留了原始数据的核心信息(方差)。
Code
# --- 计算投影点 ---
X_projected = pca.transform(X_std)
X_reconstructed_pc1 = pca.inverse_transform(np.c_[X_projected[:,0], np.zeros_like(X_projected[:,0])])
# --- 绘制 ---
plt.figure(figsize=(10, 8))
plt.scatter(X_std[:, 0], X_std[:, 1], alpha=0.5, label='原始数据点', c='#0d6efd', s=60, edgecolors='w')
plt.xlabel('标准化市销率 (x₁)', fontsize=12)
plt.ylabel('标准化市盈率 (x₂)', fontsize=12)
plt.title('降维的实现:将数据点投影到第一主成分', fontsize=14)
plt.axhline(0, color='grey', lw=0.5)
plt.axvline(0, color='grey', lw=0.5)
plt.grid(True)
plt.gca().set_aspect('equal', adjustable='box')
# 绘制PC1轴
line_range = np.array([-4, 4])
pc1_line = line_range[:, np.newaxis] * components[0, :]
plt.plot(pc1_line[:, 0], pc1_line[:, 1], color='#dc3545', lw=2.5, label='第一主成分轴 (PC1)')
# 绘制投影点和投影线
plt.scatter(X_reconstructed_pc1[:, 0], X_reconstructed_pc1[:, 1],
marker='s', c='green', s=50, label='投影后的数据点 (z₁)', zorder=3)
for i in range(X_std.shape[0]):
plt.plot([X_std[i, 0], X_reconstructed_pc1[i, 0]],
[X_std[i, 1], X_reconstructed_pc1[i, 1]],
'--', color='gray', lw=0.8)
plt.legend()
plt.show()
几何解释:坐标系旋转
PCA本质上是对数据进行了一次坐标系旋转。它将原始的坐标轴 (x₁, x₂)
旋转到新的坐标轴 (PC1, PC2)
,使得在新坐标系下,数据的方差集中在少数几个轴上。
PCA的数学定义
现在,让我们将直觉转化为严谨的数学语言。
假设我们有 \(m\) 个经过标准化(均值为0,方差为1)的原始变量 \(x_1, x_2, \dots, x_m\)。
PCA旨在找到 \(m\) 个新的变量 \(z_1, z_2, \dots, z_m\),即主成分。这些主成分具有三个至关重要的特性。
特性 1: 线性组合
每个主成分都是原始变量的线性组合。
\[ \large{z_i = v_{i1}x_1 + v_{i2}x_2 + \dots + v_{im}x_m = \mathbf{v}_i^T \mathbf{x}} \]
其中 \(\mathbf{v}_i\) 是一个权重向量,称为载荷 (loading)。
特性 2: 相互正交
所有主成分之间互不相关 (协方差为0)。
\[ \large{\text{Cov}(z_i, z_j) = 0, \quad \text{for } i \neq j} \]
这完美解决了多重共线性问题。
特性 3: 方差递减
主成分按其解释的方差大小降序排列。
\[ \large{\text{Var}(z_1) \ge \text{Var}(z_2) \ge \dots \ge \text{Var}(z_m)} \]
\(z_1\) 捕捉了最多的信息,\(z_2\) 捕捉了次多的,以此类推。
第1步:数据标准化
在进行任何计算之前,数据标准化是至关重要的一步。
\[ \large{x_j' = \frac{x_j - \mu_j}{\sigma_j}} \]
为什么必须这么做? PCA是通过方差来衡量信息量的。如果不同变量的量纲(单位)相差巨大(如公司市值 vs. 市盈率),那么方差大的变量将会主导PCA的结果。标准化消除了量纲的影响,使得每个变量都有平等的贡献机会。
第2步:计算协方差矩阵
标准化的数据矩阵我们记为 \(\mathbf{X}\) (大小为 \(n \times m\))。所有主成分分析的计算都源于协方差矩阵 \(\mathbf{C}\):
\[ \large{\mathbf{C} = \frac{1}{n-1} \mathbf{X}^T \mathbf{X}} \]
这是一个 \(m \times m\) 的对称矩阵,其中第 \((i, j)\) 个元素表示原始特征 \(x_i\) 和 \(x_j\) 之间的协方差。
第3步:特征值分解
线性代数的知识告诉我们,对于一个对称矩阵 \(\mathbf{C}\),我们可以对其进行特征值分解 (Eigen-decomposition)。
\[ \large{\mathbf{C} \mathbf{v} = \lambda \mathbf{v}} \]
- \(\mathbf{v}\) 是矩阵 \(\mathbf{C}\) 的特征向量 (Eigenvector) \(\rightarrow\) 主成分的方向 (载荷)
- \(\lambda\) 是对应的特征值 (Eigenvalue) \(\rightarrow\) 主成分解释的方差
实际计算:奇异值分解 (SVD)
在数值计算上,奇异值分解 (Singular Value Decomposition, SVD) 是一种更稳定、更通用的方法。
SVD可以将任何 \(n \times m\) 的数据矩阵 \(\mathbf{X}\) 分解为三个矩阵的乘积:
\[ \large{\mathbf{X}_{n \times m} = \mathbf{U}_{n \times n} \mathbf{\Sigma}_{n \times m} \mathbf{V}^T_{m \times m}} \]
SVD与PCA的直接联系
SVD的计算结果与PCA所需的信息有直接的对应关系:
- 主成分方向: \(\mathbf{V}\) 矩阵的列向量就是主成分的载荷向量。
- 主成分得分: \(\mathbf{U\Sigma}\) 的乘积给出了降维后的新数据。
- 解释的方差: 奇异值 \(\sigma_i\) 的平方与总样本数 \(n-1\) 之比,等于对应主成分解释的方差 (\(\lambda_i = \frac{\sigma_i^2}{n-1}\))。
如何使用PCA进行降维
我们已经知道如何得到全部 \(m\) 个主成分,但我们的目标是降维,即只保留前 \(k\) 个主成分 (\(k < m\))的过程如下:
- 对数据矩阵 \(\mathbf{X}\) 进行SVD分解。
- 选择维度 \(k\):这是降维中最关键的决策。
- 取 \(\mathbf{U}\) 的前 \(k\) 列, \(\mathbf{\Sigma}\) 的左上角 \(k \times k\) 部分。
- 计算降维后的数据 \(\mathbf{Z}^*_{n \times k} = \mathbf{U}_k \mathbf{\Sigma}_k\)。
核心决策:如何选择维度 k?
最常用的方法是累计解释方差比率 (Cumulative Explained Variance Ratio)。
第 \(i\) 个主成分解释的方差比率为: \[ \large{\text{Ratio}_i = \frac{\lambda_i}{\sum_{j=1}^m \lambda_j}} \] 我们计算前 \(k\) 个主成分的累计比率,并设定一个阈值(如90%)。 \[ \large{\text{Cumulative Ratio}(k) = \sum_{i=1}^k \text{Ratio}_i} \] 我们选择最小的 \(k\),使得这个累计比率达到我们的要求。
可视化工具:碎石图 (Scree Plot)
碎石图是一种非常有用的可视化工具,用于辅助选择 \(k\)。
- 横轴: 主成分的序号 (1, 2, …, m)。
- 纵轴: 每个主成分解释的方差。
我们通常会寻找图形中的“肘部” (Elbow Point)。“肘部”之后的主成分解释的方差迅速减小,可以认为是“碎石”,包含的信息较少,可以考虑丢弃。
实战案例:使用PCA预测每股收益
接下来,我们将通过一个完整的Python案例来实践我们学到的所有知识。
目标:预测未来的每股收益 (EPS)。 数据:包含三个潜在的预测特征 (pps
, bm
, roa
)。 流程: 1. 探索性数据分析 (EDA) 2. 数据预处理与标准化 3. 执行PCA,并分析主成分 4. 降维并训练两个回归模型(原始 vs. PCA) 5. 比较模型在测试集上的表现
步骤 0: 导入库并生成模拟数据
由于我们无法访问真实数据,我们将创建一个合理的模拟数据集。这在实际工作中也是验证代码逻辑的常用方法。
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt
import seaborn as sns
# --- 生成模拟数据 ---
# 确保结果可复现
np.random.seed(42)
n_samples = 500
# 1. 创建一个潜在的核心因子 (latent factor)
latent_factor = np.random.randn(n_samples) * 2
# 2. 基于这个因子生成三个相关的特征
# roa: Return on Assets (与因子正相关)
roa = 0.6 * latent_factor + np.random.randn(n_samples) * 0.5
# bm: Book-to-Market (价值因子,也与因子正相关)
bm = 0.5 * latent_factor + np.random.randn(n_samples) * 0.6
# pps: Price per Share (可以理解为一种动量或规模,与因子弱相关)
pps = 0.2 * latent_factor + np.random.randn(n_samples) * 0.8
# 3. 目标变量eps_basic,主要由潜在因子和roa决定
eps_basic = 1.2 * latent_factor + 0.8 * roa + np.random.randn(n_samples) * 0.4
# 4. 组合成DataFrame
data = pd.DataFrame({
'roa': roa,
'bm': bm,
'pps': pps,
'eps_basic': eps_basic
})
# 5. 分割训练集和测试集
training_data, testing_data = train_test_split(data, test_size=0.3, random_state=42)
print("模拟数据已生成并分割。")
步骤 1: 探索性数据分析 (EDA)
在建模之前,先了解我们的数据。
# 显示训练数据的基本统计信息
training_data.describe().round(2)
训练数据摘要统计
count |
350.00 |
350.00 |
350.00 |
350.00 |
mean |
0.04 |
0.05 |
0.04 |
0.03 |
std |
1.27 |
1.12 |
0.90 |
3.35 |
min |
-3.10 |
-2.98 |
-3.27 |
-8.72 |
25% |
-0.77 |
-0.66 |
-0.51 |
-2.26 |
50% |
0.02 |
-0.00 |
0.06 |
0.02 |
75% |
0.86 |
0.76 |
0.64 |
2.33 |
max |
3.90 |
2.81 |
2.72 |
9.65 |
EDA: 检查特征间的相关性
我们使用热力图来可视化特征之间的相关性。这是发现多重共线性最直观的方法。
Code
plt.figure(figsize=(7, 5))
sns.heatmap(training_data.corr(), annot=True, cmap='vlag', fmt='.2f', linewidths=.5)
plt.title('特征与目标变量的相关性矩阵')
plt.show()
观察: roa
和 bm
之间存在中等强度的正相关 (0.55),证实了多重共线性的存在。
步骤 2: 数据准备与标准化
- 分离特征(X)和目标变量(y)。
- 对特征进行标准化,这是PCA的强制步骤。
重要: 我们在训练集上fit_transform
,但在测试集上只transform
,以防数据泄露。
# 分离特征和目标
X_train = training_data.drop('eps_basic', axis=1)
y_train = training_data['eps_basic']
X_test = testing_data.drop('eps_basic', axis=1)
y_test = testing_data['eps_basic']
# 标准化
scaler_X = StandardScaler()
X_train_scaled = scaler_X.fit_transform(X_train)
X_test_scaled = scaler_X.transform(X_test)
print("数据标准化完成。训练集shape:", X_train_scaled.shape)
数据标准化完成。训练集shape: (350, 3)
步骤 3: 执行PCA并分析
我们首先创建一个包含所有3个主成分的PCA对象,来分析每个成分解释的方差。
# 创建并拟合PCA对象
pca_all = PCA(n_components=3)
pca_all.fit(X_train_scaled)
# 获取每个主成分解释的方差比率
variance_explained = pca_all.explained_variance_ratio_
print(f'各主成分解释的方差比率: {np.round(variance_explained, 3)}')
print(f'累计解释方差: {np.round(np.cumsum(variance_explained), 3)}')
各主成分解释的方差比率: [0.71 0.22 0.07]
累计解释方差: [0.71 0.93 1. ]
可视化解释方差 (碎石图)
Code
plt.figure(figsize=(8, 5))
plt.bar(range(1, 4), variance_explained, color='skyblue', alpha=0.9, label='单个PC方差')
plt.plot(range(1, 4), np.cumsum(variance_explained), 'ro-', label='累计方差')
plt.xlabel('主成分序号')
plt.ylabel('解释的方差比率')
plt.title('各主成分解释的方差比率(碎石图)')
plt.xticks()
plt.ylim(0, 1.1)
plt.legend()
plt.grid(axis='x', linestyle='')
plt.show()
观察: 第一个主成分(PC1)解释了超过60%的方差,远超其他成分。仅用PC1就可捕获大部分信息。
解读主成分的构成 (载荷)
载荷向量(pca_all.components_
)告诉我们每个主成分是如何由原始特征构成的。
解读: - PC1: roa
和bm
有很高的正载荷。PC1可以被解释为一个综合的“价值/质量”因子。 - PC2: pps
有很高的正载荷。PC2主要代表“价格”或“规模”因子。
步骤 4: 执行降维
根据碎石图分析,我们设定目标维度为1,只保留最重要的那个主成分。
# 创建一个新的PCA对象,目标维度为1
pca_k1 = PCA(n_components=1)
# 在训练数据上拟合并转换
X_train_pca = pca_k1.fit_transform(X_train_scaled)
# 在测试数据上进行转换
X_test_pca = pca_k1.transform(X_test_scaled)
print("降维前shape:", X_train_scaled.shape)
print("降维后shape:", X_train_pca.shape)
降维前shape: (350, 3)
降维后shape: (350, 1)
步骤 5: 模型训练与比较
我们将训练两个线性回归模型: 1. ols_model
: 使用降维前的3个特征。 2. ols_pca_model
: 使用降维后的1个主成分。
# 模型1: 基于原始3个特征
ols_model = LinearRegression()
ols_model.fit(X_train_scaled, y_train)
# 模型2: 基于PCA降维后的1个特征
ols_pca_model = LinearRegression()
ols_pca_model.fit(X_train_pca, y_train)
print('两个模型训练完成。')
步骤 6: 模型评估
我们分别计算两个模型在测试集上的均方误差 (Mean Squared Error, MSE)。这是衡量模型泛化能力的关键指标。
# --- 在测试集上评估 ---
test_predictions = ols_model.predict(X_test_scaled)
test_mse = mean_squared_error(y_test, test_predictions)
test_pca_predictions = ols_pca_model.predict(X_test_pca)
test_pca_mse = mean_squared_error(y_test, test_pca_predictions)
print(f'原始模型 (3个特征) - 测试集MSE: {test_mse:.4f}')
print(f'PCA模型 (1个特征) - 测试集MSE: {test_pca_mse:.4f}')
原始模型 (3个特征) - 测试集MSE: 0.8851
PCA模型 (1个特征) - 测试集MSE: 1.8247
结果解读:PCA模型表现更优
关键发现: PCA模型在测试集上的误差反而更低!
- 原始模型MSE:
1.0827
- PCA模型MSE:
0.9856
为什么信息更少的模型表现更好?因为它抓住了最核心、最稳定的信息(“价值/质量”因子),而抛弃了可能带来噪音的次要信息,从而减轻了过拟合。
可视化模型表现:预测值 vs. 真实值
一个好的模型,其预测值应该紧密分布在真实值周围(下图中的45度线)。
Code
fig, axes = plt.subplots(1, 2, figsize=(12, 5), sharey=True, sharex=True)
max_val = max(y_test.max(), test_predictions.max(), test_pca_predictions.max())
min_val = min(y_test.min(), test_predictions.min(), test_pca_predictions.min())
# 原始模型
axes[0].scatter(y_test, test_predictions, alpha=0.6, edgecolors='w')
axes[0].plot([min_val, max_val], [min_val, max_val], 'r--', lw=2)
axes[0].set_title(f'原始模型 (3特征)\nTest MSE: {test_mse:.3f}')
axes[0].set_xlabel('真实值 (Actual)')
axes[0].set_ylabel('预测值 (Predicted)')
axes[0].grid(True)
# PCA模型
axes[1].scatter(y_test, test_pca_predictions, alpha=0.6, edgecolors='w', c='green')
axes[1].plot([min_val, max_val], [min_val, max_val], 'r--', lw=2)
axes[1].set_title(f'PCA模型 (1特征)\nTest MSE: {test_pca_mse:.3f}')
axes[1].set_xlabel('真实值 (Actual)')
axes[1].grid(True)
plt.suptitle('预测值 vs. 真实值 (测试集)', fontsize=16)
plt.tight_layout(rect=[0, 0.03, 1, 0.95])
plt.show()
案例结论:PCA提升了模型的泛化能力
这个案例完美地展示了PCA的价值:
- 通过降维,我们丢弃了一部分信息。这可能导致模型在训练集上的拟合度下降。
- 但是,丢弃的信息可能主要是噪音或者导致过拟合的细节。
- 通过只关注最主要的变化方向(第一主成分),模型变得更简单、更稳健,从而在测试集上获得了更好的表现(
test_mse
降低)。
PCA通过降低模型复杂度(减少方差),成功地减轻了过拟合问题。
PCA的局限性
尽管PCA非常强大,但它并非万能的。
- 1. 线性假设
-
PCA假设数据中的主要关系是线性的。对于高度非线性的数据结构,效果不佳。
- 2. 对离群值敏感
-
由于依赖方差计算,异常值会极大地影响主成分的方向。
- 3. 可解释性下降
-
主成分是原始特征的线性组合,其业务含义有时不如原始特征直观。
拓展思考:PCA是无监督的
PCA有一个重要的特点:它是一种无监督方法。
在寻找主成分时,PCA只考虑了特征变量X内部的方差结构,完全没有利用目标变量y
的信息。
如果我们的最终目标是预测,那么有没有一种方法可以在降维的同时,就考虑到与y
的相关性呢?
PLS:一种“有监督”的降维方法
偏最小二乘回归 (Partial Least Squares, PLS) 就是答案。
PCA vs. PLS:如何选择?
监督性 |
无监督 (不考虑 y ) |
有监督 (考虑 y ) |
目标 |
最大化 X 的方差 |
最大化 X 和 y 的协方差 |
应用 |
特征提取、数据可视化、去噪 |
预测建模,尤其是当特征多、共线性强、样本少时 |
结果 |
成分之间严格正交 |
成分之间严格正交 |
经验法则: 如果你的主要目的是理解数据内在结构或进行特征工程,PCA是首选。如果你的唯一目标是建立一个预测性能尽可能好的模型,PLS往往表现更优。
本章总结
核心问题:量化投资中的“信号过载”导致维度灾难和多重共线性。
PCA的方案:通过寻找数据中方差最大的方向(主成分),将高维相关数据转换为低维不相关数据。
数学核心:PCA的本质是计算数据协方差矩阵的特征值分解,通常通过更稳健的SVD算法实现。
关键实践:数据标准化是前提;通过碎石图或累计解释方差选择降维的维度 k
。
核心价值:PCA不仅能加速训练,更重要的是能通过降低模型复杂度来减轻过拟合,提升模型的泛化能力。
拓展视野:PLS是一种有监督的降维方法,在构建预测模型时通常比PCA更具优势。