本关任务:补充 python 代码,完成 PCA (主成分分析)函数,实现降维功能。
相关知识
为了完成本关任务,你需要掌握:
降维的方法有很多,而最为常用的就是PCA(主成分分析)。 PCA 是将数据从原来的坐标系转换到新的坐标系,新的坐标系的选择是由数据本身决定的。第一个新坐标轴选择的是原始数据中方差最大的方向,第二个新坐标轴的选择和第一个坐标轴正交且方差最大的方向。然后该过程一直重复,重复次数为原始数据中的特征数量。最后会发现大部分方差都包含在最前面几个新坐标轴中,因此可以忽略剩下的坐标轴,从而达到降维的目的。
PCA 在降维时,需要指定将维度降至多少维,假设降至 k 维,则 PCA 的算法流程如下:
其中demean ,协方差矩阵,特征值与特征向量的相关知识如下:
demean 又称为零均值化,意思是将数据中每个维度上的均值变成 0。那为什么要这样做呢? PCA 实质上是找方差最大的方向,而方差的公式如下(其中μ为均值):
如果将均值变成0,那么方差计算起来就更加方便,如下:
在 numpy 中想要 demean 很简单,代码如下:
import numpy as np
#计算样本各个维度的均值
u = np.mean(data, axis=0)
#demean
after_demean = data - u
协方差描述的是两个特征之间的相关性,当协方差为正时,两个特征呈正相关关系(同增同减);当协方差为负时,两个特征呈负相关关系(一增一减);当协方差为0时,两个特征之间没有任何相关关系。
协方差的数学定义如下(假设样本有 x 和 y 两种特征,而 X 就是包含所有样本的 x 特征的集合, Y 就是包含所有样本的 y 特征的集合):
如果在算协方差之前做了 demean 操作,那么公式则为:
假设样本只有 X 和 Y 这两个特征,现在把 X 与 X, X 与 Y, Y 与 X, Y 与 Y 的协方差组成矩阵,那么就构成了协方差矩阵。而协方差矩阵反应的就是特征与特征之间的相关关系。
NumPy 提供了计算协方差矩阵的函数 cov,示例代码如下:
import numpy as np
# 计算after_demean的协方差矩阵
# after_demean的行数为样本个数,列数为特征个数
# 由于cov函数的输入希望是行代表特征,列代表数据的矩阵,所以要转置
cov = np.cov(after_demean.T)
特征值与特征向量的数学定义:如果向量v 与矩阵 A 满足 Av=λv ,则称向量 v 是矩阵A的一个特征向量, λ 是相应的特征值。
因为协方差矩阵为方阵,所以我们可以计算协方差矩阵的特征向量和特征值。其实这里的特征值从某种意义上来说体现了方差的大小,特征值越大方差就越大。而特征值所对应的特征向量就代表将原始数据进行坐标轴转换之后的数据。
numpy 为我们提供了计算特征值与特征向量的接口 eig,示例代码如下:
import numpy as np
#eig函数为计算特征值与特征向量的函数
#cov为矩阵,value为特征值,vector为特征向量
value, vector = np.linalg.eig(cov)
因此,PCA 算法伪代码如下:
#假设数据集为D,PCA后的特征数量为k
def pca(D, k):
after_demean=demean(D)
计算after_demean的协方差矩阵cov
value, vector = eig(cov)
根据特征值value将特征向量vector降序排序
筛选出前k个特征向量组成映射矩阵P
after_demean和P做矩阵乘法得到result
return result
在 begin-end 之间填写pca(data, k)函数,实现 PCA 算法,要求返回降维后的数据。其中:
注意:为了顺利评测,计算协方差矩阵时请使用 NumPy 提供的 cov 函数。
只需完成 pca 函数即可,程序内部会调用您所完成的 pca 函数来进行验证。以下为其中一个测试用例(其中 data 部分表示原始样本数据,k 表示需要降维至 k 维):
测试输入:
{‘data’:[[1, 2.2, 3.1, 4.3, 0.1, -9.8, 10], [1.8, -2.2, 13.1, 41.3, 10.1, -89.8, 100]],‘k’:3}
预期输出:
[[-6.34212110e+01 6.32827124e-15 1.90819582e-17]
[ 6.34212110e+01 -6.32827124e-15 2.02962647e-16]]
import numpy as np
def pca(data, k):
'''
对data进行PCA,并将结果返回
:param data:数据集,类型为ndarray
:param k:想要降成几维,类型为int
:return: 降维后的数据,类型为ndarray
'''
#********* Begin *********#
u = np.mean(data,axis=0)
after_demean = data-u
cov = np.cov(after_demean.T)
value,vector = np.linalg.eig(cov)
vals=np.array(value)
vecs=np.array(vector)
sorted_indices = np.argsort(vals)
P = vecs[:,sorted_indices[:-k-1:-1]]
result = np.dot(after_demean,P)
return result
#********* End *********#