主成分分析法(PCA,principal components analysis)
- 试图找到一个低维平面对数据进行投影,最小化投影误差的平方,即最小化数据与投影后的对应点之间的距离的平方值
- 在进行PCA之前需要对数据进行均值化和特征缩放
PCA不是线性回归
-
线性回归是使真实值y与预测值y之间的误差最小(垂直距离最小)
-
PCA是使点到低维平面上的投影的距离平方最小(投影距离平方最小)
将n维降到k维的PCA步骤
-
对训练数据 x ( i ) , x ( 2 ) , … , x ( m ) x^{(i)},x^{(2)},\dots,x^{(m)} x(i),x(2),…,x(m)进行均值归一化/特征规约:
均值化(必须有):
μ j = 1 m ∑ i = 1 m x j ( i ) \mu_j=\frac{1}{m}\sum_{i=1}^{m}{x_j^{(i)}} μj=m1i=1∑mxj(i)x j ( i ) : = x j ( i ) − μ j x_j^{(i)}:=x_j^{(i)}-\mu_j xj(i):=xj(i)−μj
特征缩放(可有可无):如果特征之间的数据差异很大( 如一个特征是 房屋面积大小 (0到2000),一个是卧室数(1到5),显然第二个的方差比第一个小 ),则需要在均值化的基础上进行特征缩放
x j ( i ) : = x j ( i ) s j 其 中 s j 是 标 准 差 x_j^{(i)}:=\frac{x_j^{(i)}}{s_j} \quad 其中s_j是标准差 xj(i):=sjxj(i)其中sj是标准差 -
计算协方差矩阵 Σ \Sigma Σ 是一个 n × n n\times n n×n的矩阵
Σ = 1 m ∑ i = 1 m ( x ( i ) ) ( x ( i ) ) T 其 中 x ( i ) 是 n × 1 \Sigma=\frac{1}{m}\sum_{i=1}^{m}{(x^{(i)})(x^{(i)})^T} \quad 其中x^{(i)}是n \times 1 Σ=m1i=1∑m(x(i))(x(i))T其中x(i)是n×1 -
对协方差矩阵进行特征值分解(一般用奇异值分解SVD来代替特征值分解),返回U S V三个变量
U是一个 n × n n\times n n×n的矩阵,获取前K列 u ( 1 ) , . . . , u ( k ) u^{(1)},...,u^{(k)} u(1),...,u(k)的向量,即想投影数据的k个方向,然后获取降到k维后的数据Z
主成分数量选择
- 计算SVD,得到分解后的U S V三个变量,其中 Σ = U ∗ S ∗ V \Sigma=U*S*V Σ=U∗S∗V
- 选择最小k,使其满足以下条件 (0.99:表示保留了数据99%的差异性)
∑ i = 1 k S i i ∑ i = 1 n S i i ≥ 0.99 \frac{\sum_{i=1}^{k}{S_{ii}}}{\sum_{i=1}^{n}{S_{ii}}}\geq 0.99 ∑i=1nSii∑i=1kSii≥0.99
其中,S是一个对角矩阵,对角线的元素为 S 11 , S 22 . . . S_{11},S_{22}... S11,S22... ,而矩阵的其余元素都是0。
PCA建议与误区
测试集和验证集应使用和训练集一样的 U r e d u c e U_{reduce} Ureduce
步骤如下:
- 用PCA将训练数据压缩至 K个特征,得到 U r e d u c e U_{reduce} Ureduce
- 用 U r e d u c e U_{reduce} Ureduce,将测试集和验证集的数据 x 转换成z
误区
-
PCA是为了提高学习算法的速度,而不是为了减少过拟合。过拟合问题还是用正则化来解决比较好
-
不建议一开始就用PCA。一开始用原始数据处理,如果处理效果达不到你的要求,再使用PCA。
PCA应用
-
压缩
减少存储数据所需的存储器或硬盘空间
加速学习算法
-
可视化
通过选择k=2或k=3,可以可视化到二维或三维数据集
python实现
按原理进行实现
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
matplotlib.rcParams['font.sans-serif'] = [u'SimHei']
matplotlib.rcParams['axes.unicode_minus'] = False
def pca(data_mat, top_k_feat=9999999):
"""
利用PCA对数据进行降维,获取降维后的数据和重构后的数据
:param data_mat: 原始数据,m*n的矩阵
:param top_k_feat: 需要降到的维度数
:return:
"""
mean_vals = np.mean(data_mat, axis=0)
mean_removed = (data_mat - mean_vals) # 去均值化
# 数据缩放:对特征之间数量级存在较大差异的,去量钢化
# stddev_vals = np.std(data_mat, axis=0)
# mean_removed = mean_removed / stddev_vals
# mean_removed = np.mat(pd.DataFrame(mean_removed).replace(np.nan, 0))
cov_mat = np.cov(mean_removed, rowvar=0) # 计算协方差矩阵 n*n
# 通常用奇异值分解SVD 代替 特征值分解eig
U, S, V = np.linalg.svd(cov_mat) # 获得SVD后的 U(n*n)、S(n*n)、V(n*n),特征值S已降序排列
red_vects = U[:, :top_k_feat] # 取前top_k_feat列的特征向量
red_data_mat = mean_removed * red_vects # 将原始数据转换到降维后的空间上
recon_mat = red_data_mat * red_vects.T + mean_vals # 重构原始数据
# recon_mat = np.mat(
# (red_data_mat * red_vects.T).A * (stddev_vals.A)) + mean_vals # 重构原始数据
return red_data_mat, recon_mat
def get_top_k_feat(eig_values,variance_ratio=0.99):
"""
根据variance_ratio确定保留的特征数
:param eig_values: 特征值,从大到小排序
:param variance_ratio: 主成分的方差和所占的最小比例阈值
:return:
"""
sum_S = float(np.sum(eig_values))
curr_S = 0
for i in range(len(eig_values)):
curr_S += float(eig_values[i])
if curr_S / sum_S >= variance_ratio:
return i + 1
def plot_picture(dataMat, reconMat):
plt.scatter(dataMat[:, 0].flatten().A[0], dataMat[:, 1].flatten().A[0],
c='b', marker='o', s=50)
plt.scatter(reconMat[:, 0].flatten().A[0], reconMat[:, 1].flatten().A[0],
c='r', marker='^', s=30)
plt.title("原始数据和重构之后的数据分布")
plt.show()
if __name__ == '__main__':
# 获取数据
data_mat = random_data() # 随机生成的数据
# 获取降维后的数据和重构后的数据
red_data_mat, recon_mat = pca(data_mat, 1)
# 绘图
plot_picture(data_mat, recon_mat)
将二维数据降到一维数据后,运行结果如下
调用sklearn里的PCA包实现
from sklearn.decomposition import PCA
from sklearn.datasets.samples_generator import make_blobs
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
matplotlib.rcParams['font.sans-serif'] = [u'SimHei']
matplotlib.rcParams['axes.unicode_minus'] = False
def make_blobs_data():
# X为样本数据,Y为簇类别, 共1000个样本,每个样本3个特征,共4个簇
X, y = make_blobs(n_samples=10000, n_features=3,
centers=[[3, 3, 3], [4, 4, 4], [5, 5, 5], [6, 6, 6]],
cluster_std=[0.1, 0.2, 0.1, 0.2],
random_state=3)
fig = plt.figure()
ax = Axes3D(fig, rect=[0, 0, 1, 1], elev=30, azim=20)
plt.scatter(X[:, 0], X[:, 1], X[:, 2], marker='o')
plt.show()
return X
if __name__ == '__main__':
# 获取数据
data_mat = make_blobs_data() # 随机生成的数据
# PCA只适用于密集型数据,不适合大尺寸的数据
# n_components值是大于等于1的整数,则表示降维后的特征数
# n_components值在(0,1]之间,则表示主成分的方差和所占的最小比例阈值
# n_components='mle':自动确定保留的特征数
pca = PCA(n_components=2).fit(data_mat)
# explained_variance_ratio_返回降维后的各主成分的方差值占总方差值的比例,即单个变量方差贡献率,这个比例越大,则越是重要的主成分
print(pca.explained_variance_ratio_)
# 降维后的数据
reduced_X = pca.transform(data_mat)
plt.scatter(reduced_X[:, 0], reduced_X[:, 1], marker='o')
plt.title("降维后的数据")
plt.show()
运行结果
调用sklearn里的KPCA包实现
from sklearn.decomposition import KernelPCA #核PCA模块
from sklearn.datasets import make_circles
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import matplotlib
matplotlib.rcParams['font.sans-serif'] = [u'SimHei']
matplotlib.rcParams['axes.unicode_minus'] = False
def make_circles_data():
"""生成圆形的二维数据"""
# factor表示里圈和外圈的距离之比.每圈共有n_samples/2个点
x, y = make_circles(n_samples=600, factor=0.2, noise=0.02) # factor代表维度
return x,y
def plot_kpca_picture(x,y,x_kpca):
plt.subplot(121)
plt.title('原始数据空间')
plt.scatter(x[:, 0], x[:, 1], c=y)
plt.xlabel('$x_1$')
plt.ylabel('$x_2$')
plt.subplot(122)
plt.title('KPCA')
plt.scatter(x_kpca[:, 0], x_kpca[:, 1], c=y)
plt.xlabel('$x_1$')
plt.ylabel('$x_2$')
plt.show()
if __name__ == '__main__':
x, y = make_circles_data()
# 使用kernal PCA
# 这里调用的核是径向基函数(Radial Basis Function, RBF)
# gamma是一个核参数(用于处理非线性)
kpca = KernelPCA(kernel='rbf', gamma=10)
x_kpca = kpca.fit_transform(x)
# 绘图
plot_kpca_picture(x, y, x_kpca)
运行结果