目录
一、相关背景
在许多领域的研究与应用中,通常需要对含有多个变量的数据进行观测,收集大量数据后进行分析寻找规律。多变量大数据集无疑会为研究和应用提供丰富的信息,但是也在一定程度上增加了数据采集的工作量。更重要的是在很多情形下,许多变量之间可能存在相关性,从而增加了问题分析的复杂性。如果分别对每个指标进行分析,分析往往是孤立的,不能完全利用数据中的信息,因此盲目减少指标会损失很多有用的信息,从而产生错误的结论。
因此需要找到一种合理的方法,在减少需要分析的指标同时,尽量减少原指标包含信息的损失,以达到对所收集数据进行全面分析的目的。由于各变量之间存在一定的相关关系,因此可以考虑将关系紧密的变量变成尽可能少的新变量,使这些新变量是两两不相关的,那么就可以用较少的综合指标分别代表存在于各个变量中的各类信息。
降维具有如下一些优点:
-
1) 使得数据集更易使用。
-
2) 降低算法的计算开销。
-
3) 去除噪声。
-
4) 使得结果容易理解。
降维的算法有很多,比如奇异值分解(SVD)、主成分分析(PCA)、因子分析(FA)、独立成分分析(ICA)
二、降维方法
1. PCA降维
PCA(Principal Component Analysis),即主成分分析方法,是一种使用最广泛的数据降维算法。PCA的主要思想是将n维特征映射到k维上,这k维是全新的正交特征也被称为主成分,是在原有n维特征的基础上重新构造出来的k维特征。PCA的工作就是从原始的空间中顺序地找一组相互正交的坐标轴,新的坐标轴的选择与数据本身是密切相关的。其中,第一个新坐标轴选择是原始数据中方差最大的方向,第二个新坐标轴选取是与第一个坐标轴正交的平面中使得方差最大的,第三个轴是与第1,2个轴正交的平面中方差最大的。依次类推,可以得到n个这样的坐标轴。通过这种方式获得的新的坐标轴,我们发现,大部分方差都包含在前面k个坐标轴中,后面的坐标轴所含的方差几乎为0。于是,我们可以忽略余下的坐标轴,只保留前面k个含有绝大部分方差的坐标轴。事实上,这相当于只保留包含绝大部分方差的维度特征,而忽略包含方差几乎为0的特征维度,实现对数据特征的降维处理。
事实上,通过计算数据矩阵的协方差矩阵,然后得到协方差矩阵的特征值特征向量,选择特征值最大(即方差最大)的k个特征所对应的特征向量组成的矩阵。这样就可以将数据矩阵转换到新的空间当中,实现数据特征的降维。
由于得到协方差矩阵的特征值特征向量有两种方法:特征值分解协方差矩阵、奇异值分解协方差矩阵,所以PCA算法有两种实现方法:基于特征值分解协方差矩阵实现PCA算法、基于SVD分解协方差矩阵实现PCA算法。
1.1 PCA实例
对于m个n维数据A,即数据矩阵维度为m*n,找到一个经过这m个数据中心点C的n-1维超平面,将这m个数据映射到该超平面上。
实现方法:假设特征维度n=2,数据个数m=10,有如下数据:
x1 | x2 |
0.9 | 1 |
2.4 | 2.6 |
1.2 | 1.7 |
0.5 | 0.7 |
0.3 | 0.7 |
1.8 | 1.4 |
0.5 | 0.6 |
0.3 | 0.6 |
2.5 | 2.6 |
1.3 | 1.1 |
计算各自特征的平均值,并对每个数据减去该平均值:
X1 | X2 |
---|---|
0.9 - 1.17 = -0.27 | 1 - 1.3 = -0.3 |
2.4 - 1.17 = 1.23 | 2.6 - 1.3 = 1.3 |
1.2 - 1.17 = 0.03 | 1.7 - 1.3 = 0.4 |
0.5 - 1.17 = -0.67 | -0.7 - 1.3 = 0.6 |
0.3 - 1.17 = -0.87 | -0.7 - 1.3 = 0.6 |
1.8 - 1.17 = 0.63 | 1.4 - 1.3 = 0.1 |
0.5 - 1.17 = -0.67 | 0.6 - 1.3 = -0.7 |
0.3 - 1.17 = -0.87 | 0.6 - 1.3 = -0.7 |
2.5 - 1.17 = 1.33 | 2.6 - 1.3 = 1.3 |
1.3 - 1.17 = 0.13 | 1.1 - 1.3 = -0.2 |
协方差公式:
计算各特征之间的协方差矩阵M(n*n):
计算该协方差矩阵M的特征值和特征向量:pass
此时的特征向量矩阵形状为2*2,即原数据的维度是2,若要降至1维,则只需取特征向量矩阵的第一列(1*2)
将数据A(2*10)与特征向量矩阵的第一列(1*2)相乘,得到降维后的数据矩阵(1*10)
1.2 代码实现(python+numpy)
import numpy as np
x1 = [0.9, 2.4, 1.2, 0.5, 0.3, 1.8, 0.5, 0.3, 2.5, 1.3]
x2 = [1, 2.6, 1.7, 0.7, 0.7, 1.4, 0.6, 0.6, 2.6, 1.1]
A = np.array([x1, x2])
# 求平均值
avg_A = np.mean(A, axis=1)
print(avg_A)
#### print result ####
[1.17 1.3 ]
####
# 数据归一化
A[0] -= avg_A[0]
A[1] -= avg_A[1]
cov_matrix = np.cov(A)
print(cov_matrix)
#### print result ####
[[0.68677778 0.60666667]
[0.60666667 0.59777778]]
####
# 求特征值,特征向量
eigenvalue, featurevector = np.linalg.eig(cov_matrix)
print(eigenvalue)
print(featurevector)
#### print result ####
[1.25057433 0.03398123]
[[ 0.73251454 -0.68075138]
[ 0.68075138 0.73251454]]
####
# 求降维后的数据矩阵A
print(np.matmul(featurevector.T[0], A))
#### print result ####
[-0.40200434 1.78596968 0.29427599 -0.89923557 -1.04573848 0.5295593
-0.96731071 -1.11381362 1.85922114 -0.04092339]
####
1.3 具体PCA代码
import numpy as np
import matplotlib.pyplot as plt
from scipy.io import loadmat
def normalize(data):
m, n = data.shape
mean = np.mean(data, axis=0)
std = np.std(data, axis=0)
data = (data - mean)/std
#归一化
return data
def PCA(data, k):
m, n = data.shape
data = normalize(data)
Coef = data.T * data/m
U, S, V = np.linalg.svd(Coef)
#奇异值分解
UReduce = U[:, 0:k]
#取前k维值
Z = data * UReduce
return Z
if __name__ == '__main__':
data = np.mat([[1,2,3,4,5],[6,7,8,9,10]])
data_pca = PCA(data, 1)
print data, data_pca
1.4 应用实例
import numpy as np
import matplotlib.pyplot as plt
def normalize(data):
m, n = data.shape
mean = np.mean(data, axis=0)
std = np.std(data, axis=0)
data = (data - mean)/std
return data
def PCA(data, k):
m, n = data.shape
data = normalize(data)
Coef = data.T * data/m
U, S, V = np.linalg.svd(Coef)
UReduce = U[:, 0:k]
Z = data * UReduce
return XNorm, Z, U, UReduce, S, V
#数据恢复
def recover(UReduce, Z):
return Z * UReduce.T
import pca
import numpy as np
import matplotlib.pyplot as plt
from scipy.io import loadmat
#处理图片
def display(images, width, height):
m, n = images.shape
rows = int(np.floor(np.sqrt(m)))
cols = int(np.ceil(m / rows))
#图像拼接
dstImage = images.copy()
dstImage = np.zeros((rows * height, cols * width))
for i in range(rows):
for j in range(cols):
idx = cols * i + j
image = images[idx].reshape(height, width)
dstImage[i * height:i * height + height,
j * width: j * width + width] = image
plt.imshow(dstImage.T, cmap='gray')
plt.axis('off')
plt.show()
if __name__ == '__main__':
data = loadmat('ex7faces.mat')
X = np.mat(data['X'],dtype=np.float32)
m, n = X.shape
#展示原图
display(X[0:100, :], 32, 32)
XNorm, Z, U, UReduce, S, V = PCA(X, k=100)
XRec = recover(UReduce, Z)
#显示修复后的图,可以看出,PCA 损失了一部分细节
display(XRec[0:100, :], 32, 32)
本节reference:
https://www.jianshu.com/p/9e2e5778dd6d
http://blog.codinglabs.org/articles/pca-tutorial.html
https://blog.csdn.net/program_developer/article/details/80632779
2. SVD分解
对于一个实对称矩阵,可以做特征值分解,而对于一个m*n的矩阵,要想得到相似的分解结果,需要用到SVD分解。
SVD分解原理,对于m*n的矩阵,能够得到实对称矩阵以及,依次计算这两个实对称矩阵的特征值与特征向量,
虽然和不相等,但计算出来的特征值应该是相同的,只不过顺序不同,得到相同的特征值矩阵各自的特征向量矩阵和,因此,其中的形状为m*m,的形状为m*n,的形状为n*n。
通过保留前k个特征值,即被裁减为m*k,被裁减为k*k,被裁减为k*n,裁剪后相乘的矩阵形状仍为m*n。
即:
那么如何体现降维这一思想呢?
原矩阵的值的总个数为m*n,过滤后的矩阵值的总个数为m*k+k*k+k*n,当保留的k远远小于n的时候(事实上,SVD分解只需10%甚至1%的特征值便能够大致保留原矩阵的信息),就有
因此实现数据量的减少压缩。
举个例子:假设原图大小100x100,k为5,则原图要10000点存储,而拆分后100*5+5+100*5=1005,图像大小变为原来的1/10。(为什么5*5变成了5,因为特征值矩阵是对角矩阵,可以进一步简化为一个一维的向量~~)
SVD通过保留最大特征的特征值来保证原矩阵信息的完整性,通过舍弃那些特征权重较低的特征值来减少数据量,实现数据降维。
同样的,舍弃了特征权重较低的特征值,相当于舍弃了一些噪音点,因此可以用于给图像降噪。
本节reference:
https://www.zhihu.com/question/34143886?sort=created
http://www.imooc.com/article/287629
https://www.cnblogs.com/endlesscoding/p/10033527.html
3. LDA分解
1. 左边是 PCA,属于无监督方法,当数据没有标签时可以用它。
右边是 LDA,属于监督方法。考虑了数据的分类信息,这样数据在低维空间上就可以分类了,减少了很多的运算量。
2. PCA 主要是从特征的协方差角度考虑,追求的是在降维之后能够最大化保持数据的内在信息。
它不考虑分类信息,因此,降低维度后,信息损失降到最低,但分类上可能会变得更加困难。
LDA 追求的是降维后的数据点尽可能容易被区分。
降维后的样本数据在新的维度空间有最大的类间距离和最小的类内方差,数据在低维空间有最佳的可分离性。
3. PCA 后的维度数目是和数据维度相关的,原始数据是 n 维,那么 PCA 后维度为 1、2~n 维。
LDA 后的维度数目是和类别的个数相关的,原始数据是 n 维,一共有 C 个类别,那么 LDA 后维度为 1、2~C-1 维。
4. PCA 投影的坐标系都是正交的。
LDA 关注分类能力,不保证投影到的坐标系是正交的。
本节reference: