PCA 是一种常见的基于线性变换的数据降维方法,能够将原始数据变换为一组各维度线性无关的表示。
# @param X: matrix 要求内容为可计算的数字
# @param k: 降维后矩阵的尺寸(小于X的列数)
pca <- function(X,k){
# 行0均值化
for(i in 1:nrow(X)) X[i,] <- X[i,]-mean(X[i,])
# 按特征值大小对行排序后的协方差矩阵的特征向量矩阵(data.frame),返回前n列与X的积
with(eigen((X%*%t(X))/ncol(X)),vectors[order(values),])[1:k,]%*%X
}
数据的行和列分别代表什么含义?
# 随机产生两组数据各100个
A <- matrix(runif(300)*20,nrow=3)
B <- matrix(runif(300)*20+15,nrow=3)
使用随机数产生两组使用矩阵表示的数据。这两个矩阵各有100列,每列表示一条记录(Record),每行表示记录中的一个字段(Attribute)同时也代表数据的维度。3行就代表三个维度。以下是部分数据的样例。
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
[1,] 18.960956 1.748467 6.2212104 18.322197 9.317954 2.825602 11.268678 3.739493
[2,] 2.551108 15.809941 0.6862373 6.144451 9.101383 16.783569 0.281998 1.353761
[3,] 15.463714 14.475244 10.3760060 7.748462 11.792671 5.040896 3.564827 15.413125
使用plot3d函数可以直观的看到数据的分布状态
plot3d(c(A[1,],B[1,]),c(A[2,],B[2,]),c(A[3,],B[3,]),col = rep(c("red","blue"),each=100),xlab="x",ylab="y",zlab="z")
为什么要求降维后数据的方差最大?
如果一组数据之间离得很近,代表这组数据的相似度很高,也就是说明了这组数据所包含的信息量更少。在机器学习中,数据中的信息量越大,学习效果也就相应的越好。那么应当如何去衡量一组数据的相似度呢?方差就是一种简单有效的方式。计算每个字段方差的函数如下:
V a r ( A ) = 1 m ∑ i = 1 m ( a i − A ˉ ) 2 Var(A)=\frac1m\sum_{i=1}^m(a_i-\bar{A})^2 Var(A)=m1i=1∑m(ai−Aˉ)2
然而这个式子里有一个均值 A ˉ \bar{A} Aˉ的存在,导致计算复杂度提升,那么有没有什么方法能够去除均值呢?
均值归零的意义是什么?
如果我们能够让每个字段的均值为0,那么字段方差函数可以简化为如下形式:
V a r ( A ) = 1 m ∑ i = 1 m a i 2 Var(A)=\frac1m\sum_{i=1}^ma_i^2 Var(A)=m1i=1∑mai2
均值归零的方法表示如下:
a i ′ = a i − A ˉ , i = 1 , 2 , . . . , m a_i'=a_i-\bar{A},i=1,2,...,m ai′=ai−Aˉ,i=1,2,...,m
为什么要求协方差为0?
当我们仅使用方差最大的标准来降维时,可能会出现相同字段间数据分散,但不同字段间数据聚合的现象,也就是说不同字段间会存在线性相关性。那么字段间就会存在信息重复的情况。我们可以使用协方差函数计算两组数据之间相关性系数:
C o v ( A , B ) = 1 m ∑ i = 1 m ( a i − A ˉ ) ( b i − B ˉ ) Cov(A,B)=\frac1m\sum_{i=1}^m(a_i-\bar{A})(b_i-\bar{B}) Cov(A,B)=m1i=1∑m(ai−Aˉ)(bi−Bˉ)
当然在行均值归零化之后,协方差函数简化为下面的形式:
C o v ( A , B ) = 1 m ∑ i = 1 m a i b i Cov(A,B)=\frac1m\sum_{i=1}^ma_ib_i Cov(A,B)=m1i=1∑maibi
相关性系数的绝对值越小,说明两组数据的相关程度越低。
如何使方差最大同时让协方差为0?
假设我们有矩阵 Y Y Y:
Y = [ a 1 a 2 . . . a m b 1 b 2 . . . b m ] Y= \begin{bmatrix} a_1 & a_2 & ... & a_m \\ b_1 & b_2 & ... & b_m \\ \end{bmatrix} Y=[a1b1a2b2......ambm]
那么
D = 1 m Y Y T = ( 1 m ∑ i = 1 m a i 2 1 m ∑ i = 1 m a i b i 1 m ∑ i = 1 m a i b i 1 m ∑ i = 1 m b i 2 ) = ( V a r ( A ) C o v ( A , B ) C o v ( A , B ) V a r ( B ) ) D= \frac1mYY^T= \begin{pmatrix} \frac1m\sum_{i=1}^ma_i^2 & \frac1m\sum_{i=1}^ma_ib_i \\ \frac1m\sum_{i=1}^ma_ib_i & \frac1m\sum_{i=1}^mb_i^2 \\ \end{pmatrix}=\begin{pmatrix} Var(A) & Cov(A,B) \\ Cov(A,B) & Var(B) \\ \end{pmatrix} D=m1YYT=(m1∑i=1mai2m1∑i=1maibim1∑i=1maibim1∑i=1mbi2)=(Var(A)Cov(A,B)Cov(A,B)Var(B))
Y矩阵是什么?
若D矩阵除主对角线外的元素全为0,就表示矩阵Y各字段的协方差均为0。显然,Y矩阵就是我们期望X降维后的目标矩阵。 Y = f ( X ) Y=f(X) Y=f(X)
如何变换X才能得到Y?
我们假设Y和X之间存在一种线性变换关系f,那么
Y = f ( X ) = P X Y=f(X)=PX Y=f(X)=PX
其中P是一个未知的矩阵。于是Y的协方差矩阵就被转换为了
D = 1 m Y Y T = 1 m ( P X ) ( P X ) T = 1 m P X X T P T = P ( 1 m X X T ) P T = P C P T D=\frac1mYY^T\\ =\frac1m(PX)(PX)^T\\ =\frac1mPXX^TP^T\\ =P(\frac1mXX^T)P^T\\ =PCP^T D=m1YYT=m1(PX)(PX)T=m1PXXTPT=P(m1XXT)PT=PCPT
其中C是X的协方差矩阵,显然C是已知矩阵。那么问题就从“如何转换X到Y”变成了“对矩阵C对角化”。对角化过程请参照“线性代数”相关书籍。
假设我们得到了n组特征值与特征向量,我们将特征值按大小顺序取前k个对应的特征向量组成k*m的矩阵 P = [ ξ 1 , ξ 2 , . . . , ξ k ] T P=[\xi_1,\xi_2,...,\xi_k]^T P=[ξ1,ξ2,...,ξk]T,那么
P C P T = Λ = ( λ 1 λ 2 ⋱ λ k ) PCP^T=\Lambda=\begin{pmatrix} \lambda_1 & & & \\ & \lambda_2 & & \\ & & \ddots & \\ & & & \lambda_k \\ \end{pmatrix} PCPT=Λ=⎝⎜⎜⎛λ1λ2⋱λk⎠⎟⎟⎞
得到矩阵P之后P左乘X就得到了Y。
以下是全部示例代码
# ----------------------PCA函数--------------------------
# @param X: matrix 要求内容为可计算的数字
# @param k: 降维后矩阵的尺寸(小于X的列数)
pca <- function(X,k){
# 行0均值化
for(i in 1:nrow(X)) X[i,] <- X[i,]-mean(X[i,])
# 按特征值大小对行排序后的协方差矩阵的特征向量矩阵(data.frame),返回前n列与X的积
with(eigen((X%*%t(X))/ncol(X)),vectors[order(values),])[1:k,]%*%X
}
--------------------模拟数据--------------------
# 随机产生两组数据各100个
A <- matrix(runif(300)*20,nrow=3)
B <- matrix(runif(300)*20+15,nrow=3)
# 绘制出可拖动的3D立体分布图
if(!require(rgl)){
install.packages("rgl")
library(rgl)
}
plot3d(c(A[1,],B[1,]),c(A[2,],B[2,]),c(A[3,],B[3,]),col = rep(c("red","blue"),each=100),xlab="x",ylab="y",zlab="z")
# -----------------对三维数据进行降维到二维--------------
# two_dim <- pca(cbind(A,B),2)
# plot(two_dim[1,],two_dim[2,], pch = 16,col = rep(c('red','blue'),each = 100),xlab="x", ylab="y")
# ---------------三维降至一维---------------------
# plot(pca(cbind(A,B),1),rep.int(0,200), pch = 16,col = rep(c('red','blue'),each = 100),xlab="x", ylab="y")