了解PCA
PCA的概念:主成分分析(Principal Component Analysis,PCA),是一种统计方法。通过正交变换将一组可能存在相关性的变量转换为一组线性不相关的变量,转换后的这组变量叫主成分。
PCA的主要目标是将特征维度变小,同时尽量减少信息损失。就是对一个样本矩阵,一是换特征,找一组新的特征来重新表示;二是减少特征,新特征的数目要远小于原特征的数目。
通过PCA将n维原始特征映射到k维(k<n)上,称这k维特征为主成分。需要强调的是,不是简单地从n维特征中去除其余n- k维特征,而是重新构造出全新的k维正交特征,且新生成的k维数据尽可能多地包含原来n维数据的信息。例如,使用PCA将20个相关的特征转化为5个无关的新特征,并且尽可能保留原始数据集的信息。
怎么找到新的维度呢?实质是数据间的方差够大,通俗地说,就是能够使数据到了新的维度基变换下,坐标点足够分散,数据间各有区分。
上图所示的左图中有5个离散点,降低维度,就是需要把点映射成一条线。将其映射到右图中黑色虚线上则样本变化最大,且坐标点更分散,这条黑色虚线就是第一主成分的投影方向。
PCA是一种线性降维方法,即通过某个投影矩阵将高维空间中的原始样本点线性投影到低维空间,以达到降维的目的,线性投影就是通过矩阵变换的方式把数据映射到最合适的方向。
使用梯度上升法求解第一主成分
公式:
- 向量映射
- 求某向量在X轴上的映射(求a到b的映射)
- 主成分分析法
- 主成分分析
步骤:
- 生成数据
X = np.empty(shape=(100, 2))
X[:,0] = np.random.uniform(0,100,size=100)
X[:,1] = 0.75*X[:,0] + 3 + np.random.normal(0,10,size=100)
- 先做一个demean操作,均值归零的操作,这样的好处在于可以少计算平均值
def demean(X): # 传入一个矩阵使得各个维度上的均值都为0
return X-np.mean(X, axis=0)
# axis = 0 : 在横的方向上取值
- 求方差最大的效用函数
def f(X,w): # 求方差最大的效用函数
return np.sum(X.dot(w)**2)/len(X)
- 求梯度
def df(X,w): # 求梯度
return X.T.dot(X.dot(w))*2./len(X)
- 求单位方向。因为w的模是1,那么说明w的每个维度都非常小,因为w+步长后w的模发生改变,但是我们不希望它的模发生变化,因为w作为方向向量,我们希望它的值为一,所以每次得到w之后我们都要进行处理,使它的模为1。
def direction(w): # 每次求一个单位方向
return w/np.linalg.norm(w)
- 用梯度上升法求第一主成分。w的模是1,那么说明w的每个维度都非常小,所以不能选择太大值。
def gradient_ascent(X, initial_w, eta=0.00001, n_iters=1e4, espilon=1e-8):
w = direction(initial_w)
i_iter = 1
while i_iter<=n_iters:
last_w = w
gradient = df(X,w)
w = w + gradient*eta
w = direction(w)
if abs(f(X,w)-f(X,last_w)) < espilon:
break
i_iter += 1
return w
- 绘制第一主成分
因为demean操作后会得到零值坐标轴,所以红线在中间部位,将值放大,即可清楚的看到第一主成分。对于PCA问题,不能用数据标准化来处理数据,因为在数据标准化中就把方差变为0,也就不存在方差最大值了。
使用梯度上升法求解第二主成分
如何寻找第二主成分?
找到第一主成分之后,每一个样本都去掉第一主成分上的分量,对于这个结果,继续去求第一主成分,得到的就是第二主成分。
举个简单的例子:
如下图所示,比如求y轴为它的第一主成分,那么把y轴的分量去掉,也就是去掉各点在第一主成分上的分量,即得到第二主成分。
比如说a想去掉b上的主成分,获得在c上的主成分,
步骤:
- 去掉第一主成分分量(其中的w为求第一主成分中的w)
X2 = X_demean-X_demean.dot(w).reshape(-1,1)*w # 去掉第一主成分分量以后的样本
- 求第二主成分(其中的initial_w为求第一主成分中的initial_w)
w2 = gradient_ascent(X2, initial_w)
- 将第二主成分画在图上
求数据前n个主成分
根据以上内容,编写相应的程序:
def first_n_components(n_conponents,X,eta=0.01):
def demean(X): # 传入一个矩阵使得各个维度上均值都维0
return X-np.mean(X,axis=0)
def f(X,w): # 求方差最大的效用函数
return np.sum(X.dot(w)**2)/len(X)
def df(X,w): # 求梯度
return X.T.dot(X.dot(w)) * 2. /len(X)
def direction(w): # 每次求一个单位方向
return w/np.linalg.norm(w)
def first_components(X,initial_w,eta,n_iters=1e4,epsilon=1e-8): # eta取值比较小
w = direction(initial_w)
i_iter = 1
while i_iter<=n_iters:
last_w = w
gradient = df(X,w)
w = w+ gradient*eta
w = direction(w)
if abs(f(X,w)-f(X,last_w))<epsilon:
break
i_iter+=1
return w
X_p =X.copy()
initial_w = np.random.random(X.shape[1]) # 这里不能取0
X_p = demean(X_p)
res = np.empty(shape=(n_conponents,X.shape[1]))
for i in range(n_conponents):
res[i] = first_components(X_p,initial_w,eta)
X_p = X_p-X_p.dot(res[i]).reshape(-1,1)*res[i]
return res
使用sklearn中封装的PCA
示例1:
使用真实数据集
-
解释方差的比例,如下图所示,这两个数据表示这两个维度占了多少的方差比重,如果把64个维度算作100%,则这两个维度占了约29%。(主成分分解是从方差最大的往方差最小的求)
-
根据方差比例,求主成分
-
降维的意义:方便做数据可视化