PCA介绍------降维
主成分分析(Principal Component Analysis),是一种用于探索高维数据的技术。PCA通常用于高维数据集的探索与可视化。还可以用于数据压缩,数据预处理等。PCA可以把可能具有线性相关性的高维变量合成为线性无关的低维变量,称为主成分(principal components),新的低维数据集会尽可能的保留原始数据的变量,可以将高维数据集映射到低维空间的同时,尽可能的保留更多变量。
注意:降维就意味着信息的丢失,这一点一定要明确,如果用原始数据在模型上没有效果,期望通过降维来进行改善这是不现实的,不过鉴于实际数据本身常常存在的相关性,我们可以想办法在降维的同时将信息的损失尽量降低。当你在原数据上跑了一个比较好的结果,又嫌它太慢模型太复杂时候才可以采取PCA降维。
PCA数学理论
基
二维空间默认(1,0)和(0,1)为一组基。 其实任何两个线性无关的二维向量都可以成为一组基。因为正交基有较好的性质,所以一般使用的基都是正交的。
例如:只有一个(3,2)本身是不能够精确表示一个向量的。这里的3实际表示的是向量在x轴上的投影值是3,在y轴上的投影值是2。也就是说我们其实隐式引入了一个定义:以x轴和y轴上正方向长度为1的向量为标准,默认选择(1,0)和(0,1)为基。
内积
向量A和B的内积公式为:
A⋅B=|A||B|cos(a)A⋅B=|A||B|cos(a)
我们希望基的模是1,因为从内积的意义可以看到,如果基的模是1,那么就可以方便的用向量点乘基而直接获得其在新基上的坐标了,即 向量在基上的投影=向量与基的内积=坐标。
基变换的矩阵表示
将一组向量的基变换表示为矩阵的相乘。一般的,如果我们有M个N维向量,想将其变换为由R个N维向量(R个基)表示的新空间中,那么首先将R个基按行组成矩阵P,然后将待变换向量按列组成矩阵X,那么两矩阵的乘积就是变换结果。R可以小于N,而R决定了变换后数据的维数。也就是说,我们可以将一N维数据变换到更低维度的空间中去,变换后的维度取决于基的数量。 因此这种矩阵相乘可以表示降维变换:
YR×M=PR×N×XN×MYR×M=PR×N×XN×M
两个矩阵相乘的意义:将右边矩阵中的每一列列向量变换到左边矩阵中每一行行向量为基所表示的空间中。
优化目标
如何选择基才是最优的。或者说,如果我们有一组N维向量,现在要将其降到R维,那么我们应该如何选择R个基才能最大程度保留原有的信息?
对于一个二维空间:要在二维平面中选择一个方向,将所有数据都投影到这个方向所在直线上,用投影值表示原始记录。这是一个实际的二维降到一维的问题。那么如何选择这个方向才能尽量保留最多的原始信息呢?一种直观的看法是:希望投影后的投影值尽可能分散,而这种分散程度,可以用数学上的方差来表述。
对于上面二维降成一维的问题来说,找到那个使得方差最大的方向就可以了。不过对于更高维,还有一个问题需要解决。考虑三维降到二维问题,与之前相同,首先我们希望找到一个方向使得投影后方差最大,这样就完成了第一个方向的选择,继而我们选择第二个投影方向。如果我们还是单纯只选择方差最大的方向,很明显,这个方向与第一个方向应该是“几乎重合在一起”,显然这样的维度是没有用的,因此,应该有其他约束条件。从直观上说,让两个不同维度尽可能表示更多的原始信息,我们是不希望它们之间存在(线性)相关性的,因为相关性意味着两个维度不是完全线性独立,必然存在重复表示的信息。
数学上用协方差表示两个维度的相关性,当协方差为0时,表示两个维度完全独立。为了让协方差为0,我们选择第二个基时只能在与第一个基正交的方向上选择,因此最终选择的两个方向一定是正交的。
降维问题的优化目标:将一组N维向量降为R维,其目标是选择R个单位正交基,使得原始数据变换到这组基上后,各维度两两间的协方差为0,而每个维度的方差则尽可能大(在正交的约束下,取最大的R个方差)。
协方差矩阵
上面推导出优化目标,那么具体该怎么实现呢,下面就用到了协方差矩阵。回顾一下,协方差矩阵的每个元素是各个向量元素之间的协方差,特殊的,矩阵对角线上的元素分别是各个向量的方差。
设原始矩阵为X(N×M),表示M个N维向量,其协方差矩阵为C(N×N);P(R×N)为变换矩阵;Y(R×M)为目标矩阵, 其协方差矩阵为D。我们要求降维后的矩阵Y的每一维包含的数据足够分散,也就是每一行(维)方差足够大,而且要求行之间的元素线性无关,也就是要求行之间的协方差全部为0,这就要求协方差矩阵D的对角线元素足够大,除对角线外元素都为0。 相当于对C进行协方差矩阵对角化。
具体推导如下:
D=1MYY′=1MPXX′P′=PCP′D=1MYY′=1MPXX′P′=PCP′
C是X的协方差矩阵,是实对称矩阵,整个PCA降维过程其实就是一个实对称矩阵对角化的过程。
PCA具体算法步骤
设有M个N维数据:
- 将原始数据按列组成N行M列矩阵X
- 将X的每一行进行零均值化,即减去每一行的均值
- 求出X的协方差矩阵C
- 求出协方差矩阵C的特征值及对应的特征向量,C的特征值就是Y的每维元素的方差,也是D的对角线元素,从大到小沿对角线排列构成D。
- 将特征向量按对应特征值大小从上到下按行排列成矩阵,根据实际业务场景,取前R行组成矩阵P
- Y=PX即为降到R维后的目标矩阵
Scikit-learn PCA实例分析
Scikit-learn是Python下著名的机器学习库,关于它我在这里就不多做介绍了,反正很好很强大。
首先数据选用经典的手写字符数据。
from sklearn import datasets digits = datasets.load_digits() x = digits.data #输入数据 y = digits.target #输出数据
PCA的调用也很简单
from sklearn import decomposition pca = decomposition.PCA() pca.fit(x)
可视化,matplotlib是Python下的绘图库,功能也是十分强大。
import matplotlib.pyplot as plt plt.figure() plt.plot(pca.explained_variance_, 'k', linewidth=2) plt.xlabel('n_components', fontsize=16) plt.ylabel('explained_variance_', fontsize=16) plt.show()
pca.explained_variance_ 就是上面协方差矩阵D的对角线元素,如下图所示:
至于到底降到多少维度,主要取决于方差,具体的方法可以采用交叉验证。
实现PCA算法
# -*- coding: utf-8 -*-
"""
作者:lds
利用Numpy,Pandas和Matplotlib实现PCA,并可视化结果
"""
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn import datasets
class DimensionValueError(ValueError):
"""定义异常类"""
pass
class PCA(object):
"""定义PCA类"""
def __init__(self, x, n_components=None):
"""x的数据结构应为ndarray"""
self.x = x
self.dimension = x.shape[1]
if n_components and n_components >= self.dimension:
raise DimensionValueError("n_components error")
self.n_components = n_components
def cov(self):
"""求x的协方差矩阵"""
x_T = np.transpose(self.x) #矩阵转秩
x_cov = np.cov(x_T) #协方差矩阵
return x_cov
def get_feature(self):
"""求协方差矩阵C的特征值和特征向量"""
x_cov = self.cov()
a, b = np.linalg.eig(x_cov)
m = a.shape[0]
c = np.hstack((a.reshape((m,1)), b))
c_df = pd.DataFrame(c)
c_df_sort = c_df.sort(columns=0, ascending=False) #按照特征值大小降序排列特征向量
return c_df_sort
def explained_varience_(self):
c_df_sort = self.get_feature()
return c_df_sort.values[:, 0]
def paint_varience_(self):
explained_variance_ = self.explained_varience_()
plt.figure()
plt.plot(explained_variance_, 'k')
plt.xlabel('n_components', fontsize=16)
plt.ylabel('explained_variance_', fontsize=16)
plt.show()
def reduce_dimension(self):
"""指定维度降维和根据方差贡献率自动降维"""
c_df_sort = self.get_feature()
varience = self.explained_varience_()
if self.n_components: #指定降维维度
p = c_df_sort.values[0:self.n_components, 1:]
y = np.dot(p, np.transpose(self.x)) #矩阵叉乘
return np.transpose(y)
varience_sum = sum(varience) #利用方差贡献度自动选择降维维度
varience_radio = varience / varience_sum
varience_contribution = 0
for R in xrange(self.dimension):
varience_contribution += varience_radio[R] #前R个方差贡献度之和
if varience_contribution >= 0.99:
break
p = c_df_sort.values[0:R+1, 1:] #取前R个特征向量
y = np.dot(p, np.transpose(self.x)) #矩阵叉乘
return np.transpose(y)
digits = datasets.load_digits()
x = digits.data
y = digits.target
if __name__ == '__main__':
pca = PCA(x)
y = pca.reduce_dimension()
注意:
根据上面对PCA的数学原理的解释,我们可以了解到一些PCA的能力和限制。PCA本质上是将方差最大的方向作为主要特征,并且在各个正交方向上将数据“离相关”,也就是让它们在不同正交方向上没有相关性。
因此,PCA也存在一些限制,例如它可以很好的解除线性相关,但是对于高阶相关性就没有办法了,对于存在高阶相关性的数据,可以考虑Kernel PCA,通过Kernel函数将非线性相关转为线性相关,关于这点就不展开讨论了。另外,PCA假设数据各主特征是分布在正交方向上,如果在非正交方向上存在几个方差较大的方向,PCA的效果就大打折扣了。
最后需要说明的是,PCA是一种无参数技术,也就是说面对同样的数据,如果不考虑清洗,谁来做结果都一样,没有主观参数的介入,所以PCA便于通用实现,但是本身无法个性化的优化。
希望这篇文章能帮助朋友们了解PCA的数学理论基础和实现原理,借此了解PCA的适用场景和限制,从而更好的使用这个算法。
线性判别分析Linear Discriminant Analysis---LDA
PCA追求的是在降维之后能够最大化保持数据的内在信息,并通过衡量在投影方向上的数据方差的大小来衡量该方向的重要性。 PCA优缺点: 优点:
1.最小误差
2.提取了主要信息 缺点:
PCA将所有的样本(特征向量集合)作为一个整体对待,去寻找一个均方误差最小意义下的最优线性映射投影,而忽略了类别属性,而它所忽略的投影方向有可能刚好包含了重要的可分性信息。
注意区别:
LDA所追求的目标和PCA不同,不是希望保持数据最多的信息,而是希望数据在降维后能够很容易地被区分开来。
LDA一种有监督的线性降维方法,,也就是说它的数据集的每个样本是有类别输出的。这点和PCA不同。PCA是不考虑样本类别输出的无监督降维技术。
核心思想:往线性判别超平面的法向量上投影,使的区分度最大(高内聚,低耦合)。LDA是为了使得降维后的数据点尽可能地容易被区分! 用一句话概括,就是“投影后类内方差最小,类间方差最大”。
什么意思呢? 我们要将数据在低维度上进行投影, 投影后希望每一种类别数据的投影点尽可能的接近, 而不同类别的数据的类别中心之间的距离尽可能的大。
LDA算法计算步骤:
对d维数据进行标准化处理(d为特征数量) 对每一类别,计算d维的均值向量 构造类间的散步矩阵和类内的散步矩阵 计算矩阵的特征值和对应的特征向量 选取前k个特征值对应的特征向量,构造一个d x k维的转换矩阵W,特征向量以列的形式排列 使用转换矩阵W将样本映射到新的特征子空间上
代码:
import numpy as np
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
from sklearn.datasets import load_iris
import matplotlib.pyplot as plt
def lda(data, target, n_dim):
'''
:param data: (n_samples, n_features)
:param target: data class
:param n_dim: target dimension
:return: (n_samples, n_dims)
'''
clusters = np.unique(target)
if n_dim > len(clusters)-1:
print("K is too much")
print("please input again")
exit(0)
#within_class scatter matrix
Sw = np.zeros((data.shape[1],data.shape[1]))
for i in clusters:
datai = data[target == i]
datai = datai-datai.mean(0)
Swi = np.mat(datai).T*np.mat(datai)
Sw += Swi
#between_class scatter matrix
SB = np.zeros((data.shape[1],data.shape[1]))
u = data.mean(0) #所有样本的平均值
for i in clusters:
Ni = data[target == i].shape[0]
ui = data[target == i].mean(0) #某个类别的平均值
SBi = Ni*np.mat(ui - u).T*np.mat(ui - u)
SB += SBi
S = np.linalg.inv(Sw)*SB
eigVals,eigVects = np.linalg.eig(S) #求特征值,特征向量
eigValInd = np.argsort(eigVals)
eigValInd = eigValInd[:(-n_dim-1):-1]
w = eigVects[:,eigValInd]
data_ndim = np.dot(data, w)
return data_ndim
if __name__ == '__main__':
iris = load_iris()
X = iris.data
Y = iris.target
data_1 = lda(X, Y, 2)
data_2 = LinearDiscriminantAnalysis(n_components=2).fit_transform(X, Y)
plt.figure(figsize=(8,4))
plt.subplot(121)
plt.title("LDA")
plt.scatter(data_1[:, 0], data_1[:, 1], c = Y)
plt.subplot(122)
plt.title("sklearn_LDA")
plt.scatter(data_2[:, 0], data_2[:, 1], c = Y)
plt.savefig("LDA.png",dpi=600)
plt.show()
使用了自己编程实现的LDA和调用sklearn自带的LDA对iris数据进行LDA,效果图如上。
PCA和LDA的比较: 相同点: 1)两者均可以对数据进行降维。 2)两者在降维时均使用了矩阵特征分解的思想。 3)两者都假设数据符合高斯分布。
不同点: 1)LDA是有监督的降维方法,而PCA是无监督的降维方法 2)LDA降维最多降到类别数k-1的维数,而PCA没有这个限制。 3)LDA除了可以用于降维,还可以用于分类。 4)LDA选择分类性能最好的投影方向,而PCA选择样本点投影具有最大方差的方向。
LDA算法总结: LDA算法既可以用来降维,又可以用来分类,但是目前来说,主要还是用于降维。在我们进行图像识别图像识别相关的数据分析时,LDA是一个有力的工具。
LDA算法的主要优点有: 1)在降维过程中可以使用类别的先验知识经验,而像PCA这样的无监督学习则无法使用类别先验知识。 2)LDA在样本分类信息依赖均值而不是方差的时候,比PCA之类的算法较优。
LDA算法的主要缺点有: 1)LDA不适合对非高斯分布样本进行降维,PCA也有这个问题。 2)LDA降维最多降到类别数k-1的维数,如果我们降维的维度大于k-1,则不能使用LDA。当然目前有一些LDA的进化版算法可以绕过这个问题。 3)LDA在样本分类信息依赖方差而不是均值的时候,降维效果不好。 4)LDA可能过度拟合数据。