PCA

1.原始维度空间中按方差最大化找一组低维正交基。先找第一个方向,原始数据投影到这个方向后,方差最大,然后找第二个方向,第二个方向与第一个方向正交 and 原始数据投影到这个方向后,方差最大,接下来,是第三个方向,第三个方向与第一、二个方向都正交,且原始数据投影到上面后方差最大,以此类推

2.算法过程:
有几种描述
第一种:
原始数据 -> 协方差矩阵 -> 特征值分解,得到特征值和特征向量 -> 按特征值逆序特征向量 -> 保留特征向量矩阵的前k个column得到转换矩阵 ->  转换矩阵右乘原始数据

第二种:
中心化原始数据 -> 协方差矩阵 -> 特征值分解,得到特征值和特征向量 -> 按特征值逆序特征向量 -> 保留特征向量矩阵的前k个column得到转换矩阵 ->  转换矩阵右乘中心化原始数据
上面两种PCA的算法步骤描述,原始数据无论是否进行中心化,得到的转换矩阵都是一样的,中心化只是一种数据预处理,也可以做标准化。对原始数据中心化与否,得到的转换矩阵都是一样,最终得到的降维矩阵每个column是相差一个固定的数,这个固定的数是均值和转换矩阵作用的结果,而且相同column的方差也是不变的。举个例子,原始数据作中心化得到的PCA降维结果与不进行中心化得到的PCA降维结果作差,第一个column都差80,第二个column都差50,两个结果相比,第一个column标准差都是30,第二个column标准差都是20。原始数据如若进行的是标准化,和不进行预处理的 降维结果比较或者转换矩阵比较,并没有看出什么规律。

第三种:
数据集X -> X'X/n(n是sample size) -> 特征值分解,得到特征值和特征向量 -> 按特征值逆序特征向量 -> 保留特征向量矩阵的前k个column得到转换矩阵 ->  转换矩阵右乘X
这种描述的话,X中心化和不中心化差异就很大了,如果中心化,X'X/n就是协方差矩阵,就等价于第二种流程,如果不进行中心化,得不到方差最大的方向
上面左图是X不进行中心化最后得到的方向,这里可以把方向理解成要拟合一条直线,这条直线要过原点,右图是X中心化后得到的方向。人们常说的PCA中心化,应该就是表达方式3下的中心化。

第四种:
数据集X -> SVD奇异值分解,得到奇异值和特征向量 -> 按奇异值排序特征向量 -> 保留特征向量矩阵的前k个column得到转换矩阵 ->  转换矩阵右乘X 
在实际应用中,PCA可以直接对X进行SVD奇异值分解,方便快捷地得到特征向量,从而得到转换矩阵,这个时候X是否中心化就和方式3有相同影响。

方式二、三、四得到转换矩阵后,也可以右乘原始数据,而不是中心化后的原始数据,PCA过程中的中心化是求转换矩阵所需要的,原始数据的中心化是一种预处理,有些步骤/算法包直接合二为一了,是右乘哪一个,看应用是否需要中心化,原始数据和中心化原始数据的差异参考方式二 下的解释。


3、一些现成工具采取的PCA方法
sklearn中from sklearn.decomposition import PCA
pca = PCA(n_components = 2)
pca.fit_transform(X)
sklearn的PCA会将原始数据中心化,然后用SVD求解,最后transform得到的结果也是转换矩阵乘上中心化原始数据,但是sklearn的PCA出来的结果某些column和手写的PCA会存在符号的差异,结果矩阵 的 某一个维度符号相反,没有影响,细节的原因是可参考https://www.cnblogs.com/lochan/p/7001907.html,但我看了解释后有疑问:ui*σi*vi=(-ui)*σi*(-vi) ,就是说vi可能全部反号(vi转置就是特征向量矩阵),但是我发现的差异是结果中某个column符号恰好相反,不是全部column都相反,这说不通。

4、手工实现PCA
def pca(X,no_dims = 2):
    X_temp = X - np.mean(X,0)
    l, M = np.linalg.eig(np.dot(X_temp.T, X_temp))
    idx = np.argsort(l)[::-1]
    M = M[:,idx]
    print(M[:,:no_dims])
    return np.dot(X,M[:,:no_dims])

def pca1(X,no_dims = 2):
    X_temp = X - np.mean(X,0)
    l, M = np.linalg.eig(np.dot(X_temp.T, X_temp)*1.0/X.shape[0])
    idx = np.argsort(l)[::-1]
    M = M[:,idx]
    print(M[:,:no_dims])
    return np.dot(X,M[:,:no_dims])

def pca2(X,no_dims = 2):
    X = X - np.mean(X,0)
    l, M = np.linalg.eig(np.dot(X.T, X)*1.0)
    idx = np.argsort(l)[::-1]
    M = M[:,idx]
    print(M[:,:no_dims])
    return np.dot(X,M[:,:no_dims])


def pca3(X,no_dims = 2):
    C = np.cov(X,rowvar=False)
    l, M = np.linalg.eig(C)
    idx = np.argsort(l)[::-1]
    M = M[:,idx]
    print(M[:,:no_dims])
    return np.dot(X,M[:,:no_dims])

pca、pca1、pca3转换矩阵都是右乘原始数据,pca2是右乘中心化后的原始数据。
pca和pca1比较,差异是np.dot(X_temp.T, X_temp)和np.dot(X_temp.T, X_temp)*1.0/X.shape[0],除不除样本数,最后算出来转换矩阵是一样的,但前者特征值是后者的X.shape[0]倍。协方差的特征值就是降维后对应维的方差。

#pca用svd求解
from numpy import linalg

def pca4(X,no_dims = 2):
    U, s, Vt = linalg.svd(X-np.mean(X,0),full_matrices = False)
    return X.dot(Vt.T[:,:no_dims])

linalg.svd分解出来s是奇异值,已经desc排序,Vt.T也是按奇异值desc排序,直接取前k个向量就行。奇异值s和特征值的关系是 λi = si^2/n(n是sample size),如果特征值计算用的是np.dot(X_temp.T, X_temp),λi = si^2。



留言

熱門文章