数据降维
在机器学习中,数据通常都是以向量的形式输入模型以进行训练。众所周知,当数据的特征比较多,向量的维度比较大的时候,在对数据进行处理的过程中以及对模型进行训练的过程中,将会消耗极大的消耗系统的内存空间以及增加模型训练的时间成本,甚至产生维度灾难(维数灾难(Curse of Dimensionality):通常是指在涉及到向量的计算的问题中,随着维数的增加,计算量呈指数倍增长的一种现象)。因此,对数据进行降维,用低维度的向量来表示原始高维度的向量就显得尤为重要。常见的降维方法有:主成分分析,线性判别分析,等距映射,局部线性嵌入,拉普拉斯特征映射,局部保留投影等。
其中,主成分分析作为最经典的降维方法,本文将对最近对PCA降维的复习内容做一个系统的总结。
PCA 降维
顾名思义,PCA降维就是要找到数据的主成分,然后用这些主成分表示原始数据。如下图所示,所有的数据样本都是X1和X2这两个特征来表示的。
可以看出,所有的样本几乎都分布在一条直线上,因此可以将这条直线作为一个坐标轴,将所有的数据用这条坐标轴近似的表示。因此,特征也就有二维降到了一维。
在信号处理领域,通常认为信号具有较大方差,噪声具有较小方差,信号的方差和噪声方差的比值则称为信噪比。信噪比越高,则认为数据的质量就越高,反之则否。由上图可知,数据在主成分所在的轴上的投影点最为分散,投影方差也最大。因此可知,PCA降维过程,就是寻找一个轴W,使得原始数据在这个轴上的投影方差最大。
对于给定的一组数据,{v1,v2,v3,…,vn}, 去中心化后表示成{x1,x2,x3,…,xn} = {v1 - u, v2 - u, v3 - u, …, vn - u}, 其中 u=
1
n
\frac{1}{n}
n1
∑
i
=
1
n
v
i
\displaystyle\sum_{i=1}^{n}v_i
i=1∑nvi.(至于为什么要进行中心化,这里暂且不表,后面自然一目了然)。向量
x
i
x_i
xi在轴
w
w
w上的投影坐标可以表示为:
PCA的目标就是找到这样的一个
w
w
w, 使得
x
i
x_i
xi在
w
w
w上的投影坐标的投影方差最大化。投影坐标平均值可以表示为:
看到这里是否领悟到对原始数据进行中心化处理的目的了?其实就是使得数据的投影坐标的均值为0,方便后面计算最大投影方差。投影方差可以表示为:
以上公式中间部分的
就是数据样本的协方差矩阵,用 表示。另外,由于
w
w
w是一个单位向量,所以。
因此我们要求解一个最大化的问题,可以表示为:
引入拉格朗日乘子,并且对
w
w
w求导令其为零,可以求得
显然,
λ
\lambda
λ是协方差矩阵的特征值,
w
w
w则是特征值
λ
\lambda
λ所对应的特征向量。投影方差可以表示为:
投影方差就等于协方差矩阵的特征值。要求最大的投影方差只要求协方差矩阵的最大的特征值即可,最佳投影方向即对应着最大特征值所对应的特征向量。次投影方向即对应着第二大特征值岁对应的特征向量。以此类推,便可求得由协方差矩阵的前K大的特征值所对应的特征向量所构成的K维空间来表示原始数据,即将原始数据的n维降到K维。
PCA的求解过程可以归纳如下:
(1)对原始数据进行中心化处理(方便后期的投影方差的计算)
(2)求中心化处理后的样本的协方差矩阵
(3)求解协方差矩阵的特征值以及所对应的特征向量
(4)选取协方差矩阵的前d大的特征值所对应的特征向量构造d维空间,通过以下映射将n维样本映射到d维:
降维后的信息占比是:
以上便是通过最大方差理论的角度分析PCA降维理论的过程。除此之外,PCA降维也可以通过最小回归误差构造损失函数的角度来解释,具体过程详见https://www.cnblogs.com/sowhat4999/p/5824880.html
import numpy as np
class PCA:
def __init__(self, X):
self.X = X
self.mean = None
self.feature = None
def transform_data(self, n_components = 1):
n_samples, n_features = self.X.shape #样本数以及每个样本中的特征个数
self.mean = np.array([np.mean(self.X[:, i]) for i in range(n_features)]) #每个特征的平均值
norm_X = self.X - self.mean #对原始数据进行中心化处理
scatter_X = np.dot(norm_X.T, norm_X) #数据样本的协方差矩阵
eig_val, eig_vec = np.linalg.eig(scatter_X) #求解协方差差矩阵的特征值以及每个特征值所对应的特征向量
eig_pairs = [(np.abs(eig_val[i]), eig_vec[:, i]) for i in range(len(eig_val))]
eig_pairs.sort(reverse = True) #将特征值按从大到小的顺序排序
self.feature = np.array([ele[1] for ele in eig_pairs[:n_components]) #取前n_components个特征向量
new_data = np.dot(norm_X, self.feature.T) #降维过后的数据
return new_data
def inverse_transform(self, new_data):
return np.dot(new_data, self.feature) + self.mean
def load_dataset(filepath):
return np.loadtxt(filepath, dtype = np.float)
if __name__ == "__main__":
X = load_datatxt('D:/机器之心/当前学习/PCA/pca-master/data/testPCA4.txt') #导入数据
myPCA = PCA(X = X)
new_data = myPCA.transform(n_components = 1)
origin_data = myPCA.inverse_transform(new_data = new_data)
print("将数据降到{}后, 数据前后方差比是{}".format(new_data.shape[1], np.var(new_data)/np.var(origin_data)
当然,如果我们不想自己造轮子的话,也可以直接调用PCA的API.
from sklearn.decomposition import PCA
pca = PCA(n_components = 1)
X = load_datatxt('D:/机器之心/当前学习/PCA/pca-master/data/testPCA4.txt')
new_data = pca.fit_transform(X)
origin_data = pca.inverse_data(new_data)
print("将数据降到{}后, 数据前后方差比是{}".format(new_data.shape[1], np.var(new_data)/np.var(origin_data)
以上便是个人对于降维算法中最经典的PCA的算法的一点拙见,不足之处望批评指正!