@anboqing
2015-06-17T22:24:35.000000Z
字数 9622
阅读 4515
PCA
降维
Richard E.Bellman(发明动态规划的美国应用数学家) 在1961年提出了这个术语:
The "Curse of dimensionality", is a term coined by Bellman to describe the problem caused by the exponential increase in volume associated with adding extra dimensions to a (mathematical) space.
一个简单的方法是:
比如我们用单特征来划分三个类的9个实例:
在一维我们会注意到类之间会有太多重叠,于是我们就决定引入第二个特征试图增加可分性.
这时,在二维空间,如果
很明显,我们用相同的bins来划分样本空间的办法是十分低效率的
在实践中,维度灾难意味着,给定一个样本大小,存在一个维数的上限,超过了这个上线,分类器的性能就会不升反降.
很多时候,在低维空间做更准确的映射比在高维空间直接映射有更好的分类性能.
定义在高维空间的函数比定义在低维空间的函数复杂的多,并且那些复杂性是难以辨明的复杂.这意味着,为了更好的学习它,越复杂的函数需要越密集的样本点 --by Friedman( famous for his friedman test)
通常,一个最优映射 y=f(x)是一个非线性函数,但是,没有一个系统和成熟的方法进行非线性变换,非线性变换的数学理论还很不成熟.(对特定特征子集的选择还要结合具体问题具体分析).因此,特征抽取常常被限定为了线性变换
* 这就是说,y 是 x 的一个线性投影
* 注意:当映射一个非线性函数时,减少后的特征空间叫做manifold(流形).参见Nonlinear dimensionality reduction
选择特征抽取映射
在线性特征提取的领域内,常用的技术有两个:
PCA的目标是在降维的同时尽可能多的保留高维空间的随机性(方差)
假设 x 是一个 N-维随机向量,表示为一组正交基向量[
假设我们只选择了基向量中的M(M<N)维来表示x,我们可以用预先选择的常量
问题现在转化成了求
现在,把
其中
现在,引入拉格朗日因子:
对每个
于是,
这样,我们就能把均方差的和表示为:
为了最小化这个式子,
把随机向量 X 投影到 其协方差矩阵
∑X 的最大特征值λi 对应的特征向量ϕi 上,我们就可以得到N维随机向量x∈RN 的一个最优(有误差的最小方差和)近似: M 个独立向量的线型组合.
注意:
- 由于PCA使用了协方差矩阵的特征向量,它能够找到单峰正态分布下数据的独立分布
- 对于非高斯分布或者多峰正态分布数据,PCA只是简单的去相关
- PCA的主要限制是它没有考虑类别的可分性,因为它没有考虑类标签
- PCA只是简单的将坐标转向了有最大方差的方向
- 而有最大方差的方向并不保证有良好的可分类特征
例如:
用PCA的话,会降维到Y轴上,因为Y轴有最大的方差.然而降维到y轴之后,样本几乎不可分,因为都混杂在一起了.
在应用PCA算法之前,我们常常要对数据进行预处理,因为PCA算法对原始数据的可靠性,一致性,以及规范性十分敏感,如果不做相应的处理,算法的效果就会大打折扣.
通常我们希望所有的特征
通常,预处理主要有2步:
假设我们有训练集:
均值规整化过程:
1. 求每个特征的均值
2. 把每个特征的值
3. 如果不同的特征之间的取值范围不一样(例如:
[U,S,V] = svd(Sigma);
//或者
[U,S,V] = eig(Sigma);
关于这里为什么要用 svd函数,因为 PCA 根据应用领域的不同,还有很多别名:
线性代数中叫 :SVD(singular value decomposition of X ) EVD(eigenvlaue decomposition ofXTX ) ,KLT(信号处理),POD(机械工程).
这样,我们得到了 [U,S,V],其中 U 就是 把特征向量按照对应的特征值大小从大到小排列所得到的一个 n x n 特征向量矩阵,这就是一个变换后的正交基
Ureduce = U(:,1:k);
这样以来,我们就可以在新基上用投影的方式表示原来的数据了:
z = Ureduce'*x;
#coding=utf-8
import numpy as np
__author__ = 'anboqing'
"""
feature scaling and mean normalization
零均值化
"""
def zeroMean(dataMat):
print("feature scaling ... ")
meanVal = np.mean(dataMat,axis=0) # axis = 0 means that calculate mean value by column(every feature)
print("mean normalization ...")
newVal = dataMat - meanVal
return newVal,meanVal
"""
求预处理之后的样本矩阵的协方差矩阵
"""
def covarianceMat(normal_data):
'''
计算协方差矩阵
:param normal_data: 零均值规范化过的数据
:return: 协方差矩阵
'''
print('calculate the covariance matrix ... ')
covMat = np.cov(normal_data,rowvar=0)
"""
numpy中的cov函数用于求协方差矩阵,
参数rowvar很重要,
若rowvar=0,说明传入的数据一行代表一个样本,
若rowvar!=0,说明一列代表一个样本
"""
return covMat
def eigenValAndVector(covMat):
"""
求协方差矩阵的特征值和特征向
:param covMat: 协方差矩阵
:return: 协方差矩阵的特征值,特征向量
"""
print('calculate eigen value and eigen vector of covariance matrix...')
eigVals,eigVects = np.linalg.eig(np.mat(covMat))
return eigVals,eigVects
def reduceDimensionality(eigVals,eigVects,dataMat,k=1):
"""
降维:保留主要成分
用前k个特征向量组成新的正交基,把原始数据映射到新的正交基上
:param eigVals: 原始数据的协方差矩阵的特征值
:param eigVects: 原始数据的协方差矩阵的特诊向量
:param dataMat: 原始数据
:param k: 要降维到多少维
:return: 降维后的数据,投影的正交基
"""
print('reduce the dimension ... ')
eigValIndices = np.argsort(eigVals) # 对特征值从小到大排序
n_eigValIndices = eigValIndices[-1:-(k+1):-1] # 最大的k个特征值的下标
data_mat_trans = eigVects[:,n_eigValIndices] # 取出前k个最大的特征向量
low_dimensional_data = dataMat * data_mat_trans # 将原始数据投影到新的向量空间的正交基上
return low_dimensional_data,data_mat_trans
def load_libsvm_data(file_path):
fin = open(file_path)
print('load data : '+file_path)
data_mat=[]
label_vec = []
for strline in fin.readlines():
line_arr = strline.strip().split()
label_vec.append(int(line_arr[0]))
line_arr = line_arr[1:]
row_arr = []
idx = 1
for pair in line_arr:
str_indice,str_val=pair.split(":")
indice = int(str_indice)
fval = float(str_val)
#print indice,fval
if indice == idx:
row_arr.append(fval)
else:
row_arr.append(0.0)
idx=idx+1
data_mat.append(row_arr)
return data_mat,label_vec
def choosePCASize(eig_vals,percentage=0.99):
'''
计算主成分个数的函数
:param eig_vals: 特征值数组
:param percentage: 要保留的方差的百分比
:return:应该降到的维数
'''
asc_eigs = np.sort(eig_vals) # 升序
desc_eigs = asc_eigs[-1::-1] # 翻转成从大到小
eig_sum = sum(desc_eigs)
tmpSum =0
num = 0
for i in desc_eigs:
tmpSum += i
num+=1
if tmpSum >= eig_sum * percentage:
return num
if __name__ == "__main__":
file_path = 'madelon'
# 读取livsvm格式的数据
data_mat,label_vec = load_libsvm_data(file_path)
# 转换成 numpy matrix 结构
dataMat = np.mat(data_mat)
# 数据零均值化
new_dat,mean_dat = zeroMean(dataMat)
# 计算协方差矩阵
cov_mat = covarianceMat(new_dat)
#print np.shape(cov_mat)
# 计算协方差矩阵的特征值和特征向量
eigen_values,eigen_vectors = eigenValAndVector(cov_mat)
# 选择要降到的维数(保存99的方差)
num_ = choosePCASize(eigen_values)
# 试着把这个数据集降维为num_维
low_dim_mat,orth_base = reduceDimensionality(eigen_values,eigen_vectors,new_dat,num_)
print np.shape(low_dim_mat)
print np.shape(orth_base)
在应用PCA的时候,对于一个1000维的数据,我们怎么知道要降维到多少维才是合理的?也就是要在保留最多信息的同时取出最多的噪声.Andrew Ng的机器学习课讲的方法是: