1、概述
1.1 维数灾难
维数灾难: 通常是指在涉及到向量的计算的问题中,随着维数的增加,计算量呈指数倍增长的一种现象。
有的时候,维度太大也会导致机器学习性能的下降,并不是特征维度
越大越好,模型的性能会随着特征的增加先上升后下降。
1.2 降维
(1)降维是将训练数据中的样本从高维空间转换到低维空间,要注意,不存在完全无损的降维。
(2)降维的主要作用:
-
减少冗余特征,降低数据维度
-
数据可视化
(3)降维的优缺点
降维的优点:
-
通过减少特征的维数,数据集存储所需的空间也相应减少,减少了特征维数所需的计算训练时间;
-
数据集特征的降维有助于快速可视化数据;
-
通过处理多重共线性消除冗余特征。
降维的缺点:
-
由于降维可能会丢失一些数据;
-
在主成分分析(PCA)降维技术中,有时需要考虑多少主成分是难以确定的,往往使用经验法则。
2、SVD(奇异值分解)
2.1 基本定义
定义矩阵 A 的 SVD 为
A
=
U
Σ
V
T
A=U \Sigma V^{T}
A=UΣVT
符号定义
A
=
U
Σ
V
T
=
u
1
σ
1
v
1
T
+
⋯
+
u
r
σ
r
v
r
T
=
σ
1
u
1
v
1
T
+
σ
2
u
2
v
2
T
+
⋯
+
σ
r
u
r
v
r
T
\begin{aligned} A=U \Sigma V^{T}=u_{1} \sigma_{1} v_{1}^{T}+\cdots+u_{r} \sigma_{r} v_{r}^{T}\\ =\sigma_{1} u_{1} v_{1}^{T}+\sigma_{2} u_{2} v_{2}^{T}+\cdots+\sigma_{r} u_{r} v_{r}^{T} \end{aligned}
A=UΣVT=u1σ1v1T+⋯+urσrvrT=σ1u1v1T+σ2u2v2T+⋯+σrurvrT
其中,
-
U 是一个 m × m m\times m m×m的正交矩阵,每个特征向量 u i u_{i} ui叫做 A 的左奇异向量;
-
Σ \Sigma Σ是一个 m × n m\times n m×n的矩阵,除了主对角线上的元素以外全为 0,主对角线上的每个元素都称为奇异值 σ \sigma σ;
-
V 是一个 n × n n\times n n×n的正交矩阵,每个特征向量 v i v_{i} vi叫做 A 的右奇异向量;
-
U 和 V 都是正交矩阵,即满足 U T U = E , V T V = E U^{T} U=E, V^{T} V=E UTU=E,VTV=E;
-
r 为 A 的秩;
-
注意:对任意 m × n m\times n m×n矩阵 A , A T A 和 A A T A^{T} A 和 A A^{T} ATA和AAT都是对称矩阵,且有相同的特征值。
2.2 SVD求解
(1)U 矩阵求解
方阵 A T A A^{T}A ATA是一个 m × m m\times m m×m的矩阵,对其进行特征分解,得到 m 个特征值 λ i \lambda_{i} λi和与其对应的 m 个特征向量 u i u_{i} ui;所有特征向量组成的 m × m m\times m m×m矩阵即为所求的 U。
(2)V 矩阵求解
同上,对 A A T AA^{T} AAT特征分解,得到 n × n n\times n n×n矩阵 V。
(3) Σ \Sigma Σ矩阵求解
特征值矩阵等于奇异值矩阵的平方,也就是说特征值和奇异值满足如下关系:
σ
i
=
λ
i
\sigma_{i}=\sqrt{\lambda_{i}}
σi=λi
又由于奇异值矩阵
Σ
\Sigma
Σ除了对角线上是奇异值,而其他位置都是0,那我们只需要求出每个奇异值 𝜎就可以了。
2.3 SVD降维的原理
SVD分解可以将一个矩阵进行分解,对角矩阵对角线上的特征值递减存放,而且奇异值的减少特别的快,在很多情况下,前10%甚至 1%的奇异值的和就占了全部的奇异值之和的 99%以上的比例。
也就是说,对于奇异值,它跟我们特征分解中的特征值类似,我们也可以用最大的 k 个的奇异值和对应的左右奇异向量来近似描述矩阵。
也就是说:
A
m
×
n
=
U
m
×
m
Σ
m
×
n
V
n
×
n
T
≈
U
m
×
k
Σ
k
×
k
V
k
×
n
T
A_{m \times n}=U_{m \times m} \Sigma_{m \times n} V_{n \times n}^{T} \approx U_{m \times k} \Sigma_{k \times k} V_{k \times n}^{T}
Am×n=Um×mΣm×nVn×nT≈Um×kΣk×kVk×nT
将矩阵 A 进行奇异值分解后,我们选择其中最大的前 k 个奇异值与其相对应的左右奇异向量,便可以将原矩阵 A 进行近似表述。这便实现了降维。
3、主成分分析
主成分分析(PCA)是一种降维方法,通过将一个大的特征集转换成一个较小的特征集,这个特征集仍然包含了原始数据中的大部分信息,从而降低了原始数据的维数。
PCA的思想很简单——减少数据集的特征数量,同时尽可能地保留信息。
PCA算法的两种实现方法:
-
基于SVD分解协方差矩阵实现PCA算法
-
基于特征值分解协方差矩阵实现PCA算法
3.1 基于SVD分解协方差矩阵实现PCA算法
PCA 减少 n 维到 k 维:
设有 m 条 n 维数据,将原始数据按列组成n行m列矩阵 X
(1)均值归一化。我们需要计算出所有特征的均值,然后令
x
j
=
x
j
−
μ
j
x_{j}=x_{j}-\mu_{j}
xj=xj−μj(
u
j
u_{j}
uj是均值)。如
果特征是在不同的数量级上,我们还需要将其除以标准差
σ
\sigma
σ;
(2)算协方差矩阵 ∑ = 1 m ∑ i = 1 n ( x ( i ) ) ( x ( i ) ) T \sum=\frac{1}{m} \sum_{i=1}^{n}\left(x^{(i)}\right)\left(x^{(i)}\right)^{T} ∑=m1∑i=1n(x(i))(x(i))T
注意:如果上面 X 矩阵是 m × n m\times n m×n,则 ∑ = 1 m ∑ i = 1 n ( x ( i ) ) T ( x ( i ) ) \sum=\frac{1}{m} \sum_{i=1}^{n}\left(x^{(i)}\right)^{T}\left(x^{(i)}\right) ∑=m1∑i=1n(x(i))T(x(i))
(3)计算协方差矩阵 Σ \Sigma Σ的特征向量,可以利用奇异值分解(SVD)来求解。即[U, S, V]= svd(sigma)
(4)如果我们希望将数据从 n 维降至 k 维,我们只需要从U 中选取前 k 个向量,获得一个 n × k n\times k n×k维度的矩阵,我们用 U r e d u c e U_{reduce} Ureduce表示,然后通过如下计算获得要求的新特征向量 z i z^{i} zi: Z = U reduce T X Z=U_{\text {reduce }}^{T} X Z=Ureduce TX
(5)计算时一定要考虑维度是否匹配。
3.2 基于特征值分解协方差矩阵实现PCA算法
(1)、(2)同上
(3)用特征值分解方法计算协方差矩阵 Σ \Sigma Σ的特征值和特征向量;
(4)对特征值从大到小排序,选择其中最大的 k 个。然后将其对应的 k 个特征向量分别作为行向量组成特征向量矩阵 P。
(5)将数据转换到 k 个特征向量构建的新空间中,即 𝑌 = 𝑃𝑋。
3.3 重建的压缩表示
与降维过程相反,还原后的数据为
X
appox
=
U
reduce
Z
X_{\text {appox }}=U_{\text {reduce }} Z
Xappox =Ureduce Z
显然,这必然会损失一部分信息。
3.4 PCA算法优缺点
(1)PCA算法优点
-
仅仅需要以方差衡量信息量,不受数据集以外的因素影响
-
各主成分之间正交,可消除原始数据成分间的相互影响的因素
-
计算方法简单,主要运算时特征值分解,易于实现
-
它是无监督学习,完全无参数限制的
(2)PCA算法缺点
-
主成分各个特征维度的含义具有一定的模糊性,不如原始样本特征的解释性强
-
方差小的非主成分也可能含有对样本差异的重要信息,因降维丢弃可能对后续数据处理有影响
4、课后习题
# @Time : 2021/11/1 10:54
# @Author : xiao cong
# @Function : 主成分分析
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy.io import loadmat
data = loadmat("ex7data1.mat")
# print(data)
X = data["X"]
print("X.shape = ", X.shape)
plt.scatter(X[:, 0], X[:, 1], s=30, c='b') # 画出散点图
plt.title("Original Data Points")
plt.xlabel("X1")
plt.ylabel("X2")
plt.show()
def feature_normalize(X): # 数据标准化
means = X.mean(axis=0)
stds = X.std(axis=0, ddof=1)
X_norm = (X - means) / stds
return means, stds, X_norm
def pca(X):
m = X.shape[0]
sigma = np.dot(X.T, X) / m
U, S, V = np.linalg.svd(sigma) # 奇异值分解
return U, S, V
means, stds, X_norm = feature_normalize(X)
U, S, V = pca(X_norm)
print("[U, S, V] = ", U, S, V)
# 进行降维(降到一维)
def project_data(X, U, k):
U_reduce = U[:, :k]
return np.dot(X, U_reduce)
Z = project_data(X_norm, U, 1)
# print("Z = ", Z)
# 恢复数据
def recover_data(Z, U, k):
U_reduce = U[:, :k]
return np.dot(Z, U_reduce.T)
X_recovered = recover_data(Z, U, 1)
# 画出重建前后的散点图
plt.figure(figsize=(7, 5))
plt.scatter(X_norm[:, 0], X_norm[:, 1], s=30, c='b', label="Original Data Points")
plt.scatter(X_recovered[:, 0], X_recovered[:, 1], s=30, c='r', label="PCA Reduced Data Points")
for i in range(X_norm.shape[0]):
plt.plot([X_norm[i, 0], X_recovered[i, 0]], [X_norm[i, 1], X_recovered[i, 1]], 'k--')
plt.title("Example Dataset: Reduced Dimension Points Shown")
plt.xlabel("X1[Feature Normalized]")
plt.ylabel("X2[Feature Normalized]")
plt.legend()
plt.axis("equal") # 等间距
plt.grid()
plt.show()
# ********************************************************************************************
# 在人脸图像上运行PCA,看看如何在实践中使用它来减少维度。
faceData = loadmat("ex7faces.mat")
# print(faceData)
X = faceData["X"]
print(X.shape)
def display_data(X, row, column, dim):
fig, ax = plt.subplots(row, column, figsize=(row, column))
for r in range(row):
for c in range(column):
ax[r][c].imshow(X[r * 10 + c].reshape(dim, dim).T, cmap='Greys_r')
ax[r][c].set_xticks([])
ax[r][c].set_yticks([]) # 去除坐标轴
plt.show()
display_data(X, row=10, column=10, dim=32)
# PCA 降维
means, stds, X_norm = feature_normalize(X)
U, S, V = pca(X_norm)
Z = project_data(X_norm, U, k=100)
display_data(Z, row=10, column=10, dim=10)
# 重建恢复
X_rec = recover_data(Z, U, k=100)
display_data(X_rec, row=10, column=10, dim=32)