当前位置:   article > 正文

特征提取PCA实现及避坑指南_pca.fit

pca.fit

接着上篇完成之后,已经过了2天时间了,终于,在今天将代码全部写完,并通过测试。太不容易了,主要是遇到一个坑,浪费好多时间。废话不多说,直接进入正题吧。

特征提取一般的都是使用主成分分析法,即PCA。

PCA的一般处理步骤分为:

  1. 中心化
  2. 求协方差矩阵
  3. 对协方差矩阵求特征值和特征向量,从而得到降维矩阵
  4. 通过中心化后的矩阵和降维矩阵的乘积即可得到最终的结果。

这里的话,就不介绍协方差矩阵相关的一些东西了,不懂的同学可以自行百度。

首先,我们来了解一下第三方库sklearn中的PCA的使用方法,一看你会觉得so easy的。

  1. # sklearn PCA 调用
  2. import numpy as np
  3. import sklearn.decomposition as dp
  4. from matplotlib import pyplot as plt
  5. from sklearn.datasets import load_iris
  6. #'''
  7. x,y=load_iris(return_X_y=True)
  8. print(x.shape) # 4维(150,4)
  9. pca=dp.PCA(n_components=2)
  10. # pca.fit(x)
  11. # a=pca.transform(x)
  12. # print(a.shape)
  13. # fit和transform一块计算
  14. reduced_x=pca.fit_transform(x)
  15. print(len(reduced_x))
  16. print(reduced_x[:10])
  17. print('特征值:')
  18. print(pca.n_features_)
  19. print(pca.n_samples_)
  20. print('特征向量:')
  21. print(pca.components_)
  22. print(type(reduced_x))
  23. print(reduced_x.shape)
  24. print(pca.explained_variance_ratio_)

可以直接忽略print相关代码,核心代码其实就是fit_transform函数,可以直接得到降维后的数据。


为了熟悉以上所说的步骤,我们通过numpy库来自实现一下这个步骤,

这里说明一下numpy.lianlg.eig方法可用于计算协方差矩阵的特征值和特征向量,返回值即为特征值和特征向量,另外,需要知道协方差矩阵的一个性质。

如此以来,可以对照着看一下如下代码:

  1. '''使用Numpy来实现CPA,只具体到相应步骤'''
  2. import numpy as np
  3. from sklearn.datasets import load_iris
  4. from sklearn.utils.extmath import svd_flip
  5. class PCA:
  6. def __init__(self,n_components):
  7. self.n_components=n_components
  8. def fit_transform(self,x):
  9. self.feature_count=x.shape[1]
  10. #中心化
  11. x=x-x.mean(axis=0)
  12. #协方差矩阵
  13. self.convariance=np.dot(x.T,x)/x.shape[0]
  14. # #求特征值和特征向量
  15. # eig_vals,eig_vectors=np.linalg.eig(self.convariance)
  16. # # 排序获得前n个特征
  17. # idx=np.argsort(-eig_vals)
  18. # # 得到由特征向量组成的降维矩阵
  19. # self.components_=eig_vectors[:,idx[:self.n_components]]
  20. # print('特征值:')
  21. # print(eig_vals)
  22. # print('特征向量:')
  23. # print(self.components_)
  24. # # 对x进行降维处理
  25. # return np.dot(x,self.components_)
  26. U,S,V=np.linalg.svd(x,full_matrices=False)
  27. U,V=svd_flip(U,V)
  28. print(V)
  29. # 得到由特征向量组成的降维矩阵
  30. self.components_=V.T[:,:self.n_components]
  31. # print('特征值:')
  32. # print(eig_vals)
  33. print('特征向量:')
  34. print(self.components_)
  35. # 对x进行降维处理
  36. return np.dot(x,self.components_)
  37. pca=PCA(n_components=2)
  38. x,y=load_iris(True)
  39. x_components=pca.fit_transform(x)
  40. print(x_components[:10])

 相信眼尖的同学已经看到了,我处理函数中有一部分注释代码,是使用的eig方法,而再用的是svd函数,其实这里即是我要说的坑。

我一开始使用eig函数实现后,将对应的特征值打印出来后发现,第二维的数据与sklearn得到的数据刚好为相反数,这就很奇怪了,按道理的话,要么一致要么都相反才对,然后我怀疑我代码写的有问题,各种分析最后打开百度,一瞬间我就明了了,因为好多人都提了类似的问题,那区别究竟为何呢?查看sklearn的PCA方法可以看到起定义中使用的正是svd方法(奇异值分解法),这个方法只需要将中心化后的矩阵做为参数传入即可,但需要在处理结束后进行svd_flip操作,其实这才是第二维数据互为相反数的原因。其实就是使用的操作方法不一致,而导致的结果。

这里也警醒我,不是万不得已,不要放着现成的库不用,而使用自己写的方法,用自己的方法也并没有那么炫酷,而且容易掉坑里。

方法的详细实现,贵在了解其步骤,熟悉其内在原理,并不是要写出一个可以投入使用的方法。

那么到这里是不是就结束了呢?


其实我觉得,到上一步结束后,大部分都能了解PCA的处理流程了,但是,其实还可以再进一步的加深巩固一下,这部分留给有精力的同学喽,其实就是将中心化等能自己实现的都用np自己实现以下,这里也不多说什么,直接是那个代码就是了

  1. '''PCA算法细节自实现'''
  2. import numpy as np
  3. from sklearn.datasets import load_iris
  4. class PCA:
  5. def __init__(self,n_components):
  6. self.n_components=n_components
  7. def fit_transform(self,X):
  8. self.X=X
  9. self._centralizaed()
  10. self._conv()
  11. self._eig()
  12. # 这里一定要注意,是使用中心化后的矩阵和降维矩阵做乘
  13. result=np.dot(self.centerX,self.components)
  14. return result
  15. # 中心化
  16. def _centralizaed(self):
  17. mean=np.array([np.mean(col) for col in self.X.T]) #等同于 X.mean(axis=0)
  18. self.centerX=self.X-mean
  19. # 求协方差矩阵
  20. def _conv(self):
  21. self.convariance=np.dot(self.centerX.T,self.centerX)/self.X.shape[0]
  22. def _eig(self):
  23. eig_values,eig_vectors= np.linalg.eig(self.convariance)
  24. idx=np.argsort(-eig_values)
  25. self.components=eig_vectors[:,idx[:self.n_components]]
  26. print('特征值:')
  27. print(eig_values)
  28. print('特征向量:')
  29. print(self.components[:10])
  30. pca=PCA(n_components=2)
  31. x,y=load_iris(return_X_y=True)
  32. x_components=pca.fit_transform(x)
  33. # print(pca.components.shape)
  34. # print(pca.components[:10])
  35. print(x_components.shape)
  36. print(x_components[:10])

其实我认为,对于原理的掌握并不一定非要完全理解了,再去敲代码加深理解,在掌握原理的基础上,可以试着先跟着代码的思路其实现以下,运行了解一下相关的步骤结果,这样更有利于对原理的掌握。


对于python小白的我来说,今天这里还学到了,np.ndarray对矩阵的操作方法,其实都是numpy的一些用法,相对于list的基础操作来说,numpy可以支持更多简便的操作,因此我们在ML的过程中,一般都已numpy为运算库,因此需要多多学习numpy的用法了。

  1. b=[[1,2,3],[4,5,6]]
  2. b[:,a[:2]]
  3. Traceback (most recent call last):
  4. File "<input>", line 1, in <module>
  5. TypeError: list indices must be integers or slices, not tuple
  6. type(b)
  7. <class 'list'>
  8. import numpy as np
  9. c=np.array([[1,2,3],[4,5,6]])
  10. type(c)
  11. <class 'numpy.ndarray'>
  12. c[:,a[:2]]
  13. array([[2, 3],
  14. [5, 6]])
  15. c[:,[1,2]]
  16. array([[2, 3],
  17. [5, 6]])
  18. c[:,[0,2]]
  19. array([[1, 3],
  20. [4, 6]])
  21. b
  22. [[1, 2, 3], [4, 5, 6]]
  23. b.mean(axis=0)
  24. Traceback (most recent call last):
  25. File "<input>", line 1, in <module>
  26. AttributeError: 'list' object has no attribute 'mean'
  27. c.mean(axis=0)
  28. array([2.5, 3.5, 4.5])
  29. c.mean(axis=1)
  30. array([2., 5.])
  31. c.T
  32. array([[1, 4],
  33. [2, 5],
  34. [3, 6]])
  35. c.T[0]
  36. array([1, 4])
  37. c
  38. np.transpose(c)
  39. array([[1, 4],
  40. [2, 5],
  41. [3, 6]])

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号