1.pca简介
PCA通过线性变换将高维数据转换为低维数据,同时保留最大的数据方差。这样做的目的是减少数据集的维度,尽可能保留原始数据的信息。
2.算法流程
1.数据中心化
对原始数据进行中心化处理,即将每个特征的值减去该特征的均值,以保证数据的均值为零。
pj = np.mean(X, axis=0)
X_pj = X - pj
2.计算协方差矩阵
找到一个轴,使得样本空间的所有点映射到这个轴的协方差最大。
公式:cov(X,Y)=1/n−1∑ni=1(xi−x¯)(yi−y¯)
n = X.shape[0]
cov = np.dot(X_pj.T,X_pj) / (n - 1)
因为事先已经对他进行了数据中心化,所以得到的协方差就可以不用求解平均值,而直接np.dot(X_pj.T,X_pj)就得到第i个特征和第j个特征的协方差。除以(n-1)是为了得到无偏估计,这样求解准确度会更高。
3.协方差矩阵分解计算特征值和特征向量
对矩阵A进行特征值分解就是将方阵分解为其特征值和特征向量的过程。
公式:A=VΛV−1
原理:
上三角形是对角线下方的值全部为零,上三角形的对角线就是他的特征值。我们通过不断把A进行相似矩阵转化,他的特征值是不会变的,通过迭代多次最后吧矩阵A转化为上三角形,就可以直接得到他的特征值了,特征向量就是变换过程中Q的累乘。
def qr_algorithm(A, num=1000, tol=1e-6):
n = A.shape[0]
tzxl = np.eye(n)
for i in range(num):
Q, R = np.linalg.qr(A)
A = np.dot(R, Q)
tzxl = np.dot(tzxl, Q)
t = np.sqrt(np.sum(np.square(A) - np.sum(np.square(np.diag(A)))))
if t < tol:
break
tzz = np.diag(A)
return tzz, tzxl
使用QR分解的迭代过程逐步将矩阵A转换成上三角矩阵,同时累积了所有的正交变换矩阵,从而得到矩阵A的特征值和特征向量的近似解。
4.选择主成分
t1 = np.argsort(-tzz)
tzxl = tzxl[:, t1]
cnt = 2
zcf = tzxl[:, :cnt]
X1 = np.dot(X_pj, zcf)
5.构建投影矩阵
6.数据投影
7.解释主成分
3.代码展示
1.打印降维度后数据
label_encoder = LabelEncoder()
y_encoded = label_encoder.fit_transform(y)
print(X1)
plt.rcParams['font.sans-serif'] = ['SimHei']
rcParams['axes.unicode_minus'] = False
plt.scatter(X1[:, 0], X1[:, 1], c=y_encoded, cmap='viridis')
plt.xlabel('主成分一')
plt.ylabel('主成分二')
plt.title('PCA')
plt.show()
将y的标签映射为数值。并且需要加上plt.rcParams['font.sans-serif'] = ['SimHei']和rcParams['axes.unicode_minus'] = False来保证中文和负号在图像中正常显示
四、实验中遇到的问题
因为这个实验的流程相对比较简短,主要的问题就是在特征值分解的理解上,刚开始因为直接计算特征值和特征向量的难度太大,没想到用相似矩阵来求解。后来使用QR分解法迭代求解特征值和特征向量就比较简便。
五.pca的优缺点
优点
1.能够降低模型的复杂度
2.有助于消除特征之间的相关性,减少多重共线性对模型的影响
3.有助于提取保留需要的多个特征
4.pca可以降低到三维或者二维有助于可视化显示
缺点
1.不一定需要,而且可能丢失有用信息
2.对数据线性假设,当数据非线性的时候效果不好
3.计算复杂度较高
4.降维后的特征通常难以解释
六.实验总代码
import numpy as np
import pandas as pd
from sklearn.datasets import load_iris
from matplotlib import rcParams
import matplotlib.pyplot as plt
from sklearn.preprocessing import LabelEncoder
train_data = pd.read_csv("C:\Users\李烨\Desktop\新建文件夹\6\iris.txt", sep='\s+')
假设数据集的最后一列是目标标签
X = train_data.iloc[:, :-1].values # 特征
y = train_data.iloc[:, -1].values # 标签
pj = np.mean(X, axis=0)
X_pj = X - pj
n = X.shape[0]
cov = np.dot(X_pj.T, X_pj) / (n - 1)
def QR(A, num=1000, tol=1e-6):
n = A.shape[0]
tzxl = np.eye(n)
for i in range(num):
Q, R = np.linalg.qr(A)
A = np.dot(R, Q)
tzxl = np.dot(tzxl, Q)
t = np.sqrt(np.sum(np.square(A) - np.sum(np.square(np.diag(A)))))
if t < tol:
break
tzz = np.diag(A)
return tzz, tzxl
tzz, tzxl = QR(cov)
t1 = np.argsort(-tzz)
tzxl = tzxl[:, t1]
cnt = 2
zcf = tzxl[:, :cnt]
X1 = np.dot(X_pj, zcf)
label_encoder = LabelEncoder()
y_encoded = label_encoder.fit_transform(y)
print(X1)
plt.rcParams['font.sans-serif'] = ['SimHei']
rcParams['axes.unicode_minus'] = False
plt.scatter(X1[:, 0], X1[:, 1], c=y_encoded, cmap='viridis')
plt.xlabel('主成分一')
plt.ylabel('主成分二')
plt.title('PCA')
plt.show()