赞
踩
接着上篇完成之后,已经过了2天时间了,终于,在今天将代码全部写完,并通过测试。太不容易了,主要是遇到一个坑,浪费好多时间。废话不多说,直接进入正题吧。
特征提取一般的都是使用主成分分析法,即PCA。
PCA的一般处理步骤分为:
这里的话,就不介绍协方差矩阵相关的一些东西了,不懂的同学可以自行百度。
首先,我们来了解一下第三方库sklearn中的PCA的使用方法,一看你会觉得so easy的。
- # sklearn PCA 调用
- import numpy as np
- import sklearn.decomposition as dp
- from matplotlib import pyplot as plt
- from sklearn.datasets import load_iris
- #'''
- x,y=load_iris(return_X_y=True)
- print(x.shape) # 4维(150,4)
- pca=dp.PCA(n_components=2)
-
- # pca.fit(x)
- # a=pca.transform(x)
- # print(a.shape)
-
- # fit和transform一块计算
- reduced_x=pca.fit_transform(x)
- print(len(reduced_x))
- print(reduced_x[:10])
- print('特征值:')
- print(pca.n_features_)
- print(pca.n_samples_)
- print('特征向量:')
- print(pca.components_)
- print(type(reduced_x))
- print(reduced_x.shape)
- print(pca.explained_variance_ratio_)
可以直接忽略print相关代码,核心代码其实就是fit_transform函数,可以直接得到降维后的数据。
为了熟悉以上所说的步骤,我们通过numpy库来自实现一下这个步骤,
这里说明一下numpy.lianlg.eig方法可用于计算协方差矩阵的特征值和特征向量,返回值即为特征值和特征向量,另外,需要知道协方差矩阵的一个性质。
如此以来,可以对照着看一下如下代码:
- '''使用Numpy来实现CPA,只具体到相应步骤'''
- import numpy as np
- from sklearn.datasets import load_iris
- from sklearn.utils.extmath import svd_flip
-
-
- class PCA:
- def __init__(self,n_components):
- self.n_components=n_components
-
- def fit_transform(self,x):
- self.feature_count=x.shape[1]
- #中心化
- x=x-x.mean(axis=0)
- #协方差矩阵
- self.convariance=np.dot(x.T,x)/x.shape[0]
- # #求特征值和特征向量
- # eig_vals,eig_vectors=np.linalg.eig(self.convariance)
- # # 排序获得前n个特征
- # idx=np.argsort(-eig_vals)
- # # 得到由特征向量组成的降维矩阵
- # self.components_=eig_vectors[:,idx[:self.n_components]]
- # print('特征值:')
- # print(eig_vals)
- # print('特征向量:')
- # print(self.components_)
- # # 对x进行降维处理
- # return np.dot(x,self.components_)
-
- U,S,V=np.linalg.svd(x,full_matrices=False)
- U,V=svd_flip(U,V)
- print(V)
- # 得到由特征向量组成的降维矩阵
- self.components_=V.T[:,:self.n_components]
- # print('特征值:')
- # print(eig_vals)
- print('特征向量:')
- print(self.components_)
- # 对x进行降维处理
- return np.dot(x,self.components_)
-
-
- pca=PCA(n_components=2)
- x,y=load_iris(True)
- x_components=pca.fit_transform(x)
- print(x_components[:10])
-
相信眼尖的同学已经看到了,我处理函数中有一部分注释代码,是使用的eig方法,而再用的是svd函数,其实这里即是我要说的坑。
我一开始使用eig函数实现后,将对应的特征值打印出来后发现,第二维的数据与sklearn得到的数据刚好为相反数,这就很奇怪了,按道理的话,要么一致要么都相反才对,然后我怀疑我代码写的有问题,各种分析最后打开百度,一瞬间我就明了了,因为好多人都提了类似的问题,那区别究竟为何呢?查看sklearn的PCA方法可以看到起定义中使用的正是svd方法(奇异值分解法),这个方法只需要将中心化后的矩阵做为参数传入即可,但需要在处理结束后进行svd_flip操作,其实这才是第二维数据互为相反数的原因。其实就是使用的操作方法不一致,而导致的结果。
这里也警醒我,不是万不得已,不要放着现成的库不用,而使用自己写的方法,用自己的方法也并没有那么炫酷,而且容易掉坑里。
方法的详细实现,贵在了解其步骤,熟悉其内在原理,并不是要写出一个可以投入使用的方法。
那么到这里是不是就结束了呢?
其实我觉得,到上一步结束后,大部分都能了解PCA的处理流程了,但是,其实还可以再进一步的加深巩固一下,这部分留给有精力的同学喽,其实就是将中心化等能自己实现的都用np自己实现以下,这里也不多说什么,直接是那个代码就是了
- '''PCA算法细节自实现'''
- import numpy as np
- from sklearn.datasets import load_iris
-
-
- class PCA:
- def __init__(self,n_components):
- self.n_components=n_components
-
- def fit_transform(self,X):
- self.X=X
- self._centralizaed()
- self._conv()
- self._eig()
- # 这里一定要注意,是使用中心化后的矩阵和降维矩阵做乘
- result=np.dot(self.centerX,self.components)
- return result
-
- # 中心化
- def _centralizaed(self):
- mean=np.array([np.mean(col) for col in self.X.T]) #等同于 X.mean(axis=0)
- self.centerX=self.X-mean
-
- # 求协方差矩阵
- def _conv(self):
- self.convariance=np.dot(self.centerX.T,self.centerX)/self.X.shape[0]
-
- def _eig(self):
- eig_values,eig_vectors= np.linalg.eig(self.convariance)
- idx=np.argsort(-eig_values)
- self.components=eig_vectors[:,idx[:self.n_components]]
- print('特征值:')
- print(eig_values)
- print('特征向量:')
- print(self.components[:10])
-
-
- pca=PCA(n_components=2)
- x,y=load_iris(return_X_y=True)
- x_components=pca.fit_transform(x)
- # print(pca.components.shape)
- # print(pca.components[:10])
- print(x_components.shape)
- print(x_components[:10])
其实我认为,对于原理的掌握并不一定非要完全理解了,再去敲代码加深理解,在掌握原理的基础上,可以试着先跟着代码的思路其实现以下,运行了解一下相关的步骤结果,这样更有利于对原理的掌握。
对于python小白的我来说,今天这里还学到了,np.ndarray对矩阵的操作方法,其实都是numpy的一些用法,相对于list的基础操作来说,numpy可以支持更多简便的操作,因此我们在ML的过程中,一般都已numpy为运算库,因此需要多多学习numpy的用法了。
-
- b=[[1,2,3],[4,5,6]]
- b[:,a[:2]]
- Traceback (most recent call last):
- File "<input>", line 1, in <module>
- TypeError: list indices must be integers or slices, not tuple
- type(b)
- <class 'list'>
-
-
- import numpy as np
- c=np.array([[1,2,3],[4,5,6]])
- type(c)
- <class 'numpy.ndarray'>
- c[:,a[:2]]
- array([[2, 3],
- [5, 6]])
- c[:,[1,2]]
- array([[2, 3],
- [5, 6]])
- c[:,[0,2]]
- array([[1, 3],
- [4, 6]])
- b
- [[1, 2, 3], [4, 5, 6]]
- b.mean(axis=0)
- Traceback (most recent call last):
- File "<input>", line 1, in <module>
- AttributeError: 'list' object has no attribute 'mean'
-
-
- c.mean(axis=0)
- array([2.5, 3.5, 4.5])
- c.mean(axis=1)
- array([2., 5.])
- c.T
- array([[1, 4],
- [2, 5],
- [3, 6]])
- c.T[0]
- array([1, 4])
- c
-
-
- np.transpose(c)
- array([[1, 4],
- [2, 5],
- [3, 6]])
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。