目录
0x00 PCA 主成分分析法
0x01 使用梯度上升法求解主成分分析问题
0x03 求数据的主成分PCA
0x04 求数据的前n个主成分
0x05 高维数据映射到低维数据
0x06 scikit-learn中的PCA
主成分分析法时一个非监督的机器学习算法,主要用于数据降维,通过降维,可以发现更便于人类理解的特征。
例如,下图有两种降维的方案:
扔掉特征一或者扔掉特征二:
右侧的降维方案更好,因为点和点之间的差距更大,区分度跟高,有没有更好的方案呢?
如果拟合出一个直线,将所有数据点都映射到该直线上
如何找到这个让样本间间距最大的轴?如何定义样本间间距?
使用方差:
第一步,均值归零化:
移动坐标轴,让所有的样本 在每个维度的均值归零,称为demean
这样做的原因是:均值归零化,并不会改变数据的分布,不会改变数据的方差,但是却可以将方差公式化简为:
注意:均值归零化只是为了求出主成分矩阵Wk,求出Wk之后,均值归零化的数据集我们就抛弃不用了。直接用原数据集 * Wk的转置 = 降维后的数据集。所以不必担心均值归零化的数据集如何恢复为原数据集等问题。
其中Xproject 和 Xproject拔 都是二维向量。两者相减也是一个二维向量,因为向量的平方=向量模的平方 = 一个数,所以Var(Xproject)是一个数,存在最大值
因为原来X的均值等于0,这才保证了投影之后,Xpr 的均值仍然等于0,具体数学证明如下:
以数据点在二维平面上为例,进行的证明。(数据点是n为同理)
w 是一个方向向量(单位向量)
X(i) 是二维向量 w是方向向量,点乘之后得到一个数,所以Var(Xpro)是一个数,一个因变量y。
推广到高维度
一个目标函数的最优化问题,使用梯度上升法解决
主成分分析法的本质:从一组坐标系转化到了另一组坐标系
第一主成分方差最大,第二主成分方差次之 ...
如何在求出第一主成分后,求第二主成分呢?
要求第二主成分,首选需要从原向量中去除原向量在第一主成分方向上的分量,即:
原向量 - 原向量在第一主成分方向的分量 = 原向量在第一主成分垂直方向的分量
对该新向量求第一主成分,就是原来的数据的第二主成分
如何由原向量 和 第一主成分的单位方向向量w 求出第一主成分向量呢?
原向量 点乘 第一主成分的单位方向向量w = 第一主成分向量的模
第一主成分向量的模 * 第一主成分的单位方向向量 w = 第一主成分向量(几何意义: 原向量在w方向的投影向量(蓝色))
如果用 原向量 - 第一主成分向量 = 新的向量, 几何意义便是原向量在w垂直方向的投影(绿色)
对该新的向量求第一主成分,即可得到原来数据的第二主成分。
代码实现:
假设我们一共求了k个主成分向量
每个主成分向量有n个特征
那么这些主成分向量就可以组成一个k行n列的Wk 矩阵
将样本矩阵 和 Wk矩阵的转置 相乘 就得到了 降维后的矩阵,它有k个特征。
m个样本
几何意义:每个样本在k个方向上的投影(分量),可以直观的理解为: 一个样本就是一个向量,不管原来的向量是几维的,我建立一个k维坐标系,将原来的样本向量投影到该k维坐标轴上,产生了k个分量/投影 (x1,x2,..,xk),换句话任何一个n维的向量放在一个k 维坐标系下重新表示,都是k维的。
PCA 的思想从几何角度来理解是非常朴素和简单的: 任何一个向量,放在一个二维坐标系中,就是二维的(x1,x2),放在一个三维坐标系中就是三维的 (x1,x2,x3) 。 三维向量想要想要降维为二维,只要将其放在一个二维坐标系中重新表示一下即可。 n维向量想要降维为k维,只要将其放在k维坐标系中重新表示一下就好了。
至于怎么求这个新的k维坐标系的k个方向,就是需要用梯度上升法去求样本集的前k个主成分。求出来的主成分向量就是该k维坐标的k个坐标轴。
具体来说,就是要求一个找到一个新的k维坐标系,使得原来的数据在新坐标系中的方差最大。
降维后的数据如何恢复到高维呢?
Xk 乘以 Wk 就可以重新恢复到高维,但是恢复回来的高维矩阵已经和之前不一样了
因为降维过程中丢失了一部分数据。
封装:
'''
Author: your name
Date: 2020-11-18 16:48:28
LastEditTime: 2020-11-22 11:03:15
LastEditors: Please set LastEditors
Description: In User Settings Edit
FilePath: /ML/playML/PCA.py
'''
import numpy as npclass PCA:def __init__(self, n_components):assert n_components >= 1, "n_components must be valid"self.n_components = n_components #主成分的个数 想要降到多少维self.components_ = None # Wk ,_表示通过用户传来的数据的出来的结果def __repr__(self):return "PCA(n_components=%d)" % self.n_componentsdef fit(self, X, eta=0.01, n_iters=1e4):assert self.n_components <= X.shape[1],\"只能降维不能升维"# 均值归0化def demean(X):return X-np.mean(X, axis=0)# 效用函数def f(w, X):return np.sum((X.dot(w)**2))/len(X)# 效用函数求梯度def df(w, X):return X.T.dot(X.dot(w)*2./len(X))# 方向向量 单位化def direction(w):return w/np.linalg.norm(w)# 梯度上升法 返回第一主成分的方向向量def first_component(X, initial_w, eta=0.01, n_iters=1e4, epsilon=1e-8):w = direction(initial_w) # 转化为单位向量cur_iter = 0while cur_iter < n_iters:gradient = df(w, X) # 求梯度last_w = ww = w+eta * gradient # 让w向梯度方向移动,w的值不重要,方向重要w = direction(w) # 注意1:转化为单位向量if(abs(f(w, X)-f(last_w, X)) < epsilon):# 上升幅度足够小时,说明到极值点了breakcur_iter += 1return wX_pca = demean(X)#Wk 行数 = 主成分个数 列数 = 样本的特征数self.components_ = np.empty(shape=(self.n_components,X.shape[1]))for i in range(self.n_components):#在高维坐标系中随机取一个点initial_w = np.random.random(X_pca.shape[1])w = first_component(X_pca,initial_w,eta,n_iters)# 第i主成分放在第i行的位置self.components_[i,:]=wX_pca= X_pca - X_pca.dot(w).reshape(-1,1)*wreturn selfdef transform(self,X):""" 将给定的X,映射到各个主成分分量中"""return X.dot(self.components_.T)def inverse_transform(self,X):""" 将给定的X,反向映射会原来的特征空间"""return X.dot(self.components_)
测试:
由此可见降维后再升维,丢失的信息并不能再恢复。
思路:
先降维,再升维到原来的维度,原有数据中的一部分数据会丢失,这部分丢失的数据也包括噪音
所以使用PCA先降维 再升维同样也具有对数据进行降噪的功能
下面以手写数字数据集为例:
X 特征矩阵中每一行 代表一个样本
Wk 主成分矩阵中 每一行是一个主成分向量,同时也可以看成是一个样本
在人脸识别中,X 特征矩阵中每一行 代表一个人脸样本
而Wk矩阵中每一行可以看成一个 是一个 特征脸 样本