算法流程很简单,但要究其原理,着实费了不少功夫。原因还是在于对矩阵的知识忘得太多了。在参考各篇博文的时候,突然就冒出很多矩阵名词,没办法,想理解算法原貌,只得重新一个个取查含义,重新学习了这些基础知识点后,理解原理相对来说还是简单的,这里推荐这篇博文https://blog.csdn.net/shizhixin/article/details/51181379
看了许多,这篇讲的更容易理解些,排版清晰,公式推导严谨。但纵观那么多文章,却没有提及怎么选择 维度“K”的,吴恩达的机器学习课程,提到用平方误差和 与 训练集的方差 的比例,来衡量选取k维后,对原数据的保留程度,但没有详细说明怎么做。后来还是在《机器学习实战》上找到了方法,就是用 t=选取的特征值的和 / 所有特征值的和 ,来表示降维后,信息的保留程度。之所以能这样做,是因为在推导过程中发现,特征值等于原数据在对应特征向量降维后的方差,而方差反应了信息量的大小。
吴恩达的推荐使用matlab的SVD(奇异值分解)求解。 numpy上也有对应的函数可以调用,两种方法可以得到同样的结果。
import numpy as np
class PCA():
'''
PCA降维算法
'''
def pca(self, dataSet, dimenK):
'''
使用主成分分析算法,把n维数据集降为k维数据
:param dataSet: n*m数据集,n为维度,m为样本数量,np.array
:param dimenK: 需要降到的维度
:return: 降维后的数据集 k*m,和信息量占比t
'''
meanRemoved = dataSet - np.mean(dataSet, axis=0) # 去均值化
covMat = np.cov(meanRemoved) # 计算协方差矩阵
eigVals, eigVects = np.linalg.eig(covMat) # 计算协方差矩阵的特征值和特征向量
eigValsInd = np.argsort(eigVals) # 返回从小到大排序后,元素的索引
eigValsInd = eigValsInd[: : -1][: dimenK] # 获取前k个最大的特征值的索引
redEigVects = eigVects[:, eigValsInd] # 根据索引获取对应的特征向量
lowDDataMat = np.dot(redEigVects.T, meanRemoved) # 特征向量*去均值化的原数据=降维后的数据
t = np.sum(eigVals[eigValsInd]) / np.sum(eigVals) # 在PCA,特征值等于对应特征向量*原数据后的方差,这里用方差代表信息量,该值衡量降维后保留的原数据多少的信息量
# 以下使用SVD(奇异值分解)求解
# u, s, vt = np.linalg.svd(covMat) # s存储奇异值,u存储对应的特征向量
# eigVals = s[: dimenK] # 选取前K个奇异值,对应上面的特征值
# redEigVects = u[:, : dimenK] # 选取对应的特征向量, n*k
# lowDDataMat = np.dot(redEigVects.T, meanRemoved) # 通过左乘基向量矩阵,降维
# t = np.sum(eigVals) / np.sum(s) # 计算信息保留度
return lowDDataMat, t
# 以下是测试数据
dataSet = np.random.rand(8, 10) * 10
pca = PCA()
print(pca.pca(dataSet, 5)[1])