在这篇文章中,我将介绍机器学习中的一种无监督学习算法——PCA,因为它主要有两种用途,即降维与特征提取,所以我将将围绕这两种用途来介绍它,包括基本概念,应用与代码实践。
一、 PCA
1.1 概念
PCA(principal component analysis)中文名叫主成分分析,是一种旋转数据集的方法,也是一种广泛使用的统计方法,用于探索数据内部的结构,尤其是当变量间存在相关性的时候。它在旋转数据集后,通常是根据新特征对数据的重要性来选择它的一个子集。
1.2 PCA步骤
PCA整体有五个步骤,分别是:1.标准化数据,2.计算协方差矩阵,3.计算特征值与特征向量,4.排序并选择主成分,5.数据投影。详细些说,PCA因为对于量纲敏感,所以需要统一数据的量纲,进行标准化处理,而使之有相同的尺度;而计算协方差则是因为PCA是为了寻找数据的最大方差方向,而协方差则正好反应了特征间的相关性;而特征值的计算是因为特征值的大小反映了该特征方向上方差的大小;在选择主成分时,会按照特征值从大到小排序,选取前k个特征向量作为主成分,这k个特征向量构成一个新的基,用于投影原始数据;最后就是将原始数据投影到刚才得到的那个新的基上。
1.3 PCA的优缺点
优点:PCA可以降低程序的复杂度,并为数据提供一种可视化的方法,而且也可以在一定程度上减少噪声,因为PCA倾向于保留最重要的信息。
缺点:PCA假设数据的主要信息可以通过方差来得到,但会存在信息存在于低方差方向的现象,而且PCA是种线性的方法,如果数据中存在非线性的结构,那么PCA可能就无法很好捕捉这些结构。
二、降维
1.1 概念
降维(Dimensionality Reduction)是数据科学和机器学习中一个重要的概念和技术,它的目的是通过减少数据的特征数量来简化数据集,同时尽可能保留数据的关键信息。降维技术广泛应用于数据预处理阶段,以提高后续分析或建模的效率和效果。
1.2 降维的方法
关于降维的方法,主要分为两种,一种是线性方法,一种是非线性方法。其中,关于线性方法常见的有三种,分别是:主成分分析(PCA)、线性判别法(LDA)、因子分析(FA);而非线性分析主要有四种,分别为:t-分布邻域嵌入(t-SNE)、多维尺度分析(MDS)、局部线性嵌入(LLE)、自组织映射(SOM)。
具体关于这七中降维方法我会在之后单独再写一篇关于降维的文章来介绍,所以在此不会多言。
1.3 降维的应用
降维可以应用在各个方面,比如如下的这个数据可视化上,此外,在NLP、生物基因等方面也有着不同的应用。
1.4 python代码
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_digits
from sklearn.decomposition import PCA
from sklearn.neural_network import MLPClassifier
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import accuracy_score
# 数据加载及预处理
digits = load_digits()
X = digits.data
y = digits.target
scaler = StandardScaler()
X_scaler = scaler.fit_transform(X)
# 降维
pca = PCA(n_components=50)
X_pca = pca.fit_transform(X_scaler)
# 数据划分
X_train, X_test, y_train, y_test = train_test_split(X_pca,y,random_state=42)
# mlp实例化
mlp = MLPClassifier(hidden_layer_sizes=(100,), max_iter=1000)
mlp.fit(X_train,y_train)
# 预测
y_pre = mlp.predict(X_test)
accuracy = accuracy_score(y_test, y_pre)
print("The accuracy score is: {}".format(accuracy))
# 可视化
pca_vis = PCA(n_components=2)
X_vis = pca_vis.fit_transform(X_scaler)
plt.figure(figsize=(8, 6))
for digit in range(10):
plt.scatter(X_vis[y == digit, 0], X_vis[y == digit, 1], label=f"Digit {digit}", alpha=0.7)
plt.xlabel("First Principal Component")
plt.ylabel("Second Principal Component")
plt.legend()
plt.title("PCA of Digits Dataset")
plt.grid(True)
# 热图绘制
plt.matshow(pca_vis.components_, cmap='viridis')
plt.xticks([0,1],["First component", "Second component"])
plt.colorbar()
plt.xticks(range(len(digits.feature_names)),
digits.feature_names,rotation=60,ha='left')
plt.xlabel("Feature")
plt.ylabel("principal components")
plt.show()
在这个代码中,是对于一个手写数字用多层感知机来进行识别,期间分别对于这个手写数字的数据集进行了两次的pca降维操作,但它们的目的不同,在第一次中,降维是为了让数据集更适于mlp去训练,这样做可以减少代码的计算复杂度、去除噪声并提高性能;第二次降维则是为了数据的可视化,它将数据从原有的维数降到二维,以此来让其能够展示在平面图表上,并有助于查看这些数据的分布情况。
关于代码的输出如下:
我们需要知道,每次进行运算得到的结构其实都有可能不相同,但极为近似,就像我上一次的结果就是0.98,而之所以这样是因为神经网络的权重随机初始化的,而且在训练过程中关于正则化等技术都有可能导致结果的不一样。
然后是这个代码给出的图像:
三、 特征提取
1.1 概念
特征提取(Feature Extraction)是数据科学和机器学习中的一个重要环节,它涉及从原始数据中提取有用的特征或信息,以供后续的分析或建模使用。特征提取的目标是在保留数据的关键信息的同时,减少数据的复杂性和维度,从而提高模型的性能和解释性。
1.2 特征提取的方法
关于特征提取,可以根据其是否依赖标签而将方法分为两种:有监督特征提取和无监督特征提取。关于有监督特征提取的常见方法有:线性判别分析(LDA)、特征选择、特征构造、决策树特征重要性。关于无监督特征提取有:主成分分析(PCA)、非负举证分解(NMF)、自编码器、t-分布邻域嵌入(t-SNE)、多维尺度分析。
1.3 特征提取的应用
关于特征提取,它可以用于多个方面,下面的这个人脸识别就是基于PCA的一个特征提取应用,此外,图像分类、基因表达数据分析、股票市场分析等都会应用到它。
1.4 python代码
如下这个代码是关于人脸识别的,它会将判别是同一个人的所有图像一起显示出来。本来关于人脸照片的数据集可以在sklearn上直接获取,通过这个导包的方式:
from sklearn.datasets import fetch_lfw_people
但在实践中发现会出现网络连接上的报错,像这样:raise HTTPError(req.full_url, code, msg, hdrs, fp) urllib.error.HTTPError: HTTP Error 403: Forbidden
那么,为了解决这个问题,我便在kaggle上找到了一个人脸识别的数据集,其url在这里:
face-recog-dataset (kaggle.com)
那么,具体的代码如下:
from sklearn.metrics import classification_report
from sklearn.decomposition import PCA
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
import numpy as np
import os
import glob
from PIL import Image
import matplotlib.pyplot as plt
import warnings
# 指定图像文件夹的路径
all_path = r"C:\Users\20349\Desktop\ArtificialIntelligence\ML\kaggle\人脸识别数据集"
# 获取所有jpg图像的路径列表
image_path = glob.glob(os.path.join(all_path, '*.jpg'))
# 设定一个统一的大小
target_size = (128, 128)
# 指定颜色模式
convert_mode = 'RGB'
# 读取并处理所有图像
image_size = []
labels = [] # 假设你已经有了对应的标签列表
for idx, path in enumerate(image_path):
img = Image.open(path).convert(convert_mode).resize(target_size)
image_size.append(np.array(img))
labels.append(idx) # 这里假设每个图像对应一个唯一的标签
# 将图像列表转换为NumPy数组
image_array = np.array(image_size)
# 数据预处理:归一化
image_array = image_array.astype('float32') / 255.0
# 将图像数组展平为一维向量
flattened_images = image_array.reshape(image_array.shape[0], -1)
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(flattened_images, labels, test_size=0.2, random_state=42)
# PCA特征提取
pca = PCA(n_components=150)
X_train_pca = pca.fit_transform(X_train)
X_test_pca = pca.transform(X_test)
# KNN分类器
clf = KNeighborsClassifier(n_neighbors=3)
clf.fit(X_train_pca, y_train)
# 预测
y_pred = clf.predict(X_test_pca)
# 打印分类报告
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=UserWarning)
print(classification_report(y_test, y_pred, zero_division=1))
# 显示部分图像及其预测标签
show = min(10, len(X_test))
fig, axes = plt.subplots(2, (show + 1) // 2, figsize=(15, 8))
axes = axes.ravel()
for i in range(show):
# 重塑为原始的三维数组 (128, 128, 3)
img = X_test[i].reshape(target_size[0], target_size[1], 3)
axes[i].imshow(img)
axes[i].set_title(f'Predicted: {y_pred[i]}, Actual: {y_test[i]}')
axes[i].set_xticks([])
axes[i].set_yticks([])
plt.tight_layout()
plt.show()
最后我们会从众多的.jpg的文件中识别出同一人的图像,而如下则是随机读取的某一段图像:
然后这是预测结果:
不过我们观察这个结果,会发现有一定的预测错误,这可能是PCA降维不足导致的,那么我们可以考虑增加主成分的数量或是采用其他特征提取方法来改善。
此上