在机器学习中,数据预处理极为重要,好的数据预处理往往比模型更为关键,数据预处理中降维是相当重要的一个环节。常见的降维方法有很多种,如:SVD奇异值分解法,PCA主成分分析法等,这里主要针对PCA谈谈自己的看法。
PCA概念
PCA的思想是寻找一个一维基,使得所有数据变换为这个基上的坐标表示后,方差值最大。二维中用协方差表示数据分散程度,设原始数据矩阵 XXX 对应的协方差矩阵为CCC,而 PPP 是一组基按行组成的矩阵,设Y=PXY=PXY=PX,则YYY为XXX对PPP做基变换后的数据。设YYY的协方差矩阵为DDD,我们推导一下DDD与CCC的关系:
D=1n−1YYT=1n−1(PX)(PX)T=1n−1(PXXTPT)=P(1n−1XXT)PT=PCPT
\begin{aligned}
D
&= \frac{1}{n-1}YY^{T} \\
&= \frac{1}{n-1}(PX)(PX)^{T} \\
&= \frac{1}{n-1}(PXX^{T}P^{T}) \\
&= P(\frac{1}{n-1}XX^{T})P^{T} \\
&= PCP^{T}
\end{aligned}
D=n−11YYT=n−11(PX)(PX)T=n−11(PXXTPT)=P(n−11XXT)PT=PCPT
这样我们就看清楚了,我们要找的 P 是能让原始协方差矩阵对角化的 P。换句话说,优化目标变成了寻找一个矩阵 P,满足PCPTPCP^{T}PCPT是一个对角矩阵,并且对角元素按从大到小依次排列,那么 P 的前 K 行就是要寻找的基,用 P 的前 K 行组成的矩阵乘以 X 就使得 X 从 N 维降到了 K 维并满足上述优化条件。
PCA原理
PCA算法流程如下:
- 中心化,每一个特征都减去各自的平均值(主要是方便计算);
- 计算协方差矩阵(二维的是计算方差,多维的计算协方差);
- 计算协方差矩阵的特征值和特征向量;
- 将特征值从大到小排序;
- 保留最大的k个特征向量;
- 将数据转换到k个特征向量构成的新坐标系中。
PCA计算步骤
假设x=(x1,x2,...,xn)Tx=(x_1, x_2, ..., x_n)^Tx=(x1,x2,...,xn)T为nnn维的随机矢量,我们想要降维指k维(0<k<n)0<k<n)0<k<n)则PCA具体计算步骤如下:
- 对每个样本进行中心化处理,xi=xi−1n∑i=1nxix_i = x_i - \frac{1}{n} \sum_{i=1}^n x_ixi=xi−n1∑i=1nxi;
- 计算协方差矩阵
cov(xi,xj)=E[(xi−E(xi))(xj−E(xj))]=1n−1(xi−x‾)(xj−x‾)T \begin{aligned} cov(x_i, x_j) &= E[(x_i - E(x_i))(x_j - E(x_j))] \\ &=\frac{1}{n-1}(x_i - \overline x)(x_j - \overline x)^T \end{aligned} cov(xi,xj)=E[(xi−E(xi))(xj−E(xj))]=n−11(xi−x)(xj−x)T - 计算特征值和特征向量
这里首先说明一下特征值与特征向量的概念:假设AAA为nnn阶方阵,如果存在一个数λ\lambdaλ和非零nnn维向量,使得 Ax=λxAx = \lambda xAx=λx成立,则称 mmm为矩阵AAA的一个特征值,xxx则称为AAA的对应于特征值λ\lambdaλ的特征向量。
利用 Ax=λxAx = \lambda xAx=λx变换为: ∣A−λE∣x=0|A - \lambda E| x = 0∣A−λE∣x=0,EEE为nnn维的单位向量,解出该方程就可以得到特征值 λ\lambdaλ,将λ\lambdaλ带入,则可求出对应的特征向量。
- 将特征值进行排序,选出前k个对应的特征向量,即为我们需要的数据。
PCA代码实现
import numpy as np
def func_pca(input, k):
"""
这里的input默认都是预处理过的,都是数值类型,维度为 m * n
"""
m, n = input.shape
if k > n:
assert "k 必须小于特征维度!"
# 中心化
average = np.mean(input, 0)
input_average = input - average
# 计算协方差矩阵
cov_vec = np.cov(input_average.T)
# 计算协方差也可以按照公式计算
# cov_vec = 1/(m-1+1e-6) * np.dot(input_average.T, input_average)
# 求解特征值和特征向量
feature_val, feature_vec = np.linalg.eig(cov_vec)
# 获取前 k 个特征向量
index_val = np.argsort(-feature_val)
selected_vec = feature_vec[:, index_val[:k]]
# 获取data
data = np.dot(input_average, selected_vec)
return data
这里顺便说一下协方差的意义:
- cov(X, Y) > 0时,表示 X 与 Y正相关;
- cov(X, Y) < 0时,表示 X 与 Y负相关;
- cov(X, Y) = 0时,表示 X 与 Y不相关;
这里的相关指的是线性相关,并不能得到他们之间的非线性关系。