主成分分析(PCA,Principal Component Analysis)是一项在高维数据中,寻找最重要特征的降维技术,大大减少数据的维度,而不显著损失信息量。本文将通过基于飞桨框架的实际代码示例,来展示所提供的高效、灵活的线性代数 API,如何简化机器学习和深度学习中的数据处理和分析工作,为高维数据集的处理和分析提供了有效工具。主成分分析在人脸识别项目中完整代码及数据集已上传至飞桨星河社区:
https://aistudio.baidu.com/projectdetail/8055072_
在现代计算框架中,为了高效地处理和存储大规模的数据集,尤其是在这些数据集中存在大量零值的情况下,采用稀疏数据结构变得尤为重要。飞桨是一个领先的深度学习平台,提供了强大的稀疏计算能力,支持从基本的稀疏张量操作到构建复杂的稀疏神经网络。这些工具主要通过 paddle.sparse 命名空间来实现,使得开发者能够高效处理大量包含零值的数据集,从而优化内存使用和计算速度。
01 数据集
本文使用的是 ORL 官方数据集,下载网址:
http://www.cl.cam.ac.uk/Research/DTG/attarchive/pub/data/att_faces.tar.Z
该数据集包含40个人的人脸图像,其中每一个人有10张人脸图像。相应的 PGM 文件为说明。
02 安装库
安装 cv2的库:
pip install opencv-pytho
安装飞桨框架的库:(cpu 版本的即可)
pip install paddle
03 基于飞桨框架实现代码和相关函数
3.1 图片矢量化
在进行主成分分析之前,我们需要将所有训练图片矢量化为向量。首先定义一个函数,将人脸图像矢量化为一个向量,向量大小与图片的像素有关。
import cv2
import paddle
# 图片矢量化
def img2vector(image):
img = cv2.imread(image, 0) # 读取图片
imgVector = paddle.reshape(paddle.to_tensor(img, dtype='float32'), [1, -1]) # 重塑为1行多列
return imgVector
3.2 标读取数据并进行矢量化
接下来定义一个函数用于读取训练图片,并对每张图片进行矢量化处理:
import os
import numpy as np
class ORLDataset:
def __init__(self, data_path, k, train=True):
self.data_path = data_path
self.k = k
self.train = train
def load_orl(self):
train_images = []
train_labels = []
test_images = []
test_labels = []
sample = np.random.permutation(10) + 1 # 生成随机序列
for i in range(40): # 共有40个人
people_num = i + 1
for j in range(10): # 每人10张照片
image_path = os.path.join(self.data_path, 's' + str(people_num), str(sample[j]) + '.pgm')
img = img2vector(image_path) # 读取图片并进行矢量化
if j < self.k: # 构成训练集
train_images.append(img)
train_labels.append(people_num)
else: # 构成测试集
test_images.append(img)
test_labels.append(people_num)
if self.train:
return paddle.concat(train_images, axis=0), paddle.to_tensor(train_labels, dtype='int64')
else:
return paddle.concat(test_images, axis=0), paddle.to_tensor(test_labels, dtype='int64')
3.3 PCA降纬操作
PCA(主成分分析)的主要目标是通过找到数据集的主成分来实现降维,主成分是数据集中方差最大的方向。接下来,我们将详细介绍 PCA 降维操作的实现过程。
■ 实现步骤
(1) 数据类型转换和形状获取:首先,将输入的数据类型转换为浮点数,并获取数据的行数和列数。
data = paddle.cast(data, 'float32')
rows, cols = data.shape
(2)计算均值并进行数据中心化:计算数据的均值,并将数据进行中心化处理。数据中心化是指将数据的每一维都减去该维的均值,使得数据的均值为0。
data_mean = paddle.mean(data, axis=0) # 计算均值
A = data - paddle.tile(data_mean, repeat_times=[rows, 1]) # 数据中心化
数据中心化的公式为:
$$A = X - \bar{X}$$
其中, X X X 为原始数据矩阵, b a r X \\bar{X} barX 为数据的均值。
(3)计算协方差矩阵:计算中心化后的数据矩阵 A A A 的协方差矩阵 C C C。
C = paddle.matmul(A, A, transpose_y=True)
协方差矩阵的公式为:
$$C = AA^T$$
(4) 计算特征值和特征向量:使用协方差矩阵 C C C 计算特征值 l a m b d a \\lambda lambda 和特征向量 v e c v \\vec{v} vecv。
eig_vals, eig_vects = paddle.linalg.eigh(C)
特征值和特征向量的公式为:
$$C\vec{v} = \lambda\vec{v}$$
(5)选取前 r 个特征向量:选取特征值最大的前 r r r个 特征向量,并将这些特征向量构成矩阵 V r V_r Vr。
eig_vects = paddle.matmul(A.T, eig_vects[:, :r])
特征向量矩阵的构成公式为:
$$Vr = [\vec{v}1, \vec{v}2, \ldots, \vec{v}r]$$
(6)特征向量标准化:对选取的每个特征向量进行标准化,使其长度为1。
for i in range(r):
eig_vects[:, i] = eig_vects[:, i] / paddle.norm(eig_vects[:, i])
特征向量标准化的公式为:
$$\hat{\vec{v}}i = \frac{\vec{v}i}{|\vec{v}_i|}$$
(7)计算降维后的数据:将中心化的数据矩阵 A A A 投影到特征向量矩阵 V r V_r Vr上,得到降维后的数据矩阵 Y Y Y
final_data = paddle.matmul(A, eig_vects) # 降维后的数据
return final_data, data_mean, eig_vects
降维后的数据计算公式为:
$$Y = AV_r$$
■ 代码实现
将上述步骤综合起来,实现完整的 PCA 降维操作函数:
def PCA(data, r): # 降低到r维
data = paddle.cast(data, 'float32')
rows, cols = data.shape
data_mean = paddle.mean(data, axis=0) # 计算均值
A = data - paddle.tile(data_mean, repeat_times=[rows, 1]) # 数据中心化
# 计算协方差矩阵
C = paddle.matmul(A, A, transpose_y=True)
# 计算特征值和特征向量
eig_vals, eig_vects = paddle.linalg.eigh(C)
# 选取前r个特征向量
eig_vects = paddle.matmul(A.T, eig_vects[:, :r])
for i inrange(r):
eig_vects[:, i] = eig_vects[:, i] / paddle.norm(eig_vects[:, i]) # 特征向量标准化
final_data = paddle.matmul(A, eig_vects) # 降维后的数据
return final_data, data_mean, eig_vects
通过以上步骤,我们成功实现了 PCA 降维操作,将高维数据映射到一个低维空间中,并且尽可能保留了数据的方差信息。这种方法在数据压缩、特征提取、以及数据可视化等方面非常有用,能够帮助我们更好地理解和分析数据集的本质特性。
3.4 训练与测试
最后,进行初次训练,随机选取每个人的7张图片作为训练图片,查看不同维数下的训练效果:
def face_recognize(data_path):
for r in range(10, 41, 10):
print(f"当降维到{r}时:")
dataset_train = ORLDataset(data_path, k=7, train=True)
dataset_test = ORLDataset(data_path, k=7, train=False)
train_data, train_labels = dataset_train.load_orl()
test_data, test_labels = dataset_test.load_orl()
data_train_new, data_mean, V_r = PCA(train_data, r)
temp_face = test_data - data_mean
data_test_new = paddle.matmul(temp_face, V_r)
true_num = 0
for i in range(len(test_labels)):
diffMat = data_train_new - data_test_new[i]
sqDiffMat = paddle.square(diffMat)
sqDistances = paddle.sum(sqDiffMat, axis=1)
sortedDistIndices = paddle.argsort(sqDistances)
if train_labels[sortedDistIndices[0]] == test_labels[i]:
true_num += 1
accuracy = float(true_num) / len(test_labels)
print(f'当每个人选择7张照片进行训练时,The classify accuracy is: {accuracy:.2%}')
3.5 结果
最终训练得到的结果如下:
-
当降维到10时:当每个人选择7张照片进行训练时,The classify accuracy is: 67.50%
-
当降维到20时:当每个人选择7张照片进行训练时,The classify accuracy is: 35.00%
-
当降维到30时:当每个人选择7张照片进行训练时,The classify accuracy is: 67.50%
-
当降维到40时:当每个人选择7张照片进行训练时,The classify accuracy is: 40.00%
-
以上是基于飞桨框架实现 PCA 的人脸识别算法的完整代码和结果。
04 小结
基于飞桨框架的 paddle.linalg API 为进行数据降维和特征提取提供了强大的支持,这对于机器学习和深度学习应用来说是非常重要的。特别是,paddle.linalg.eig 和 paddle.linalg.svd 函数允许用户有效地计算数据的特征值和特征向量,这是执行主成分分析(PCA)和奇异值分解(SVD)等降维方法的关键。此外,paddle.linalg.matmul 可以用于矩阵乘法,帮助将数据从高维空间映射到低维空间,保留了数据中最重要的信息。
这些功能的广泛应用不仅限于 PCA 相关的任务,还包括数据压缩、特征选择和提高学习算法的效率等领域。通过降维,可以显著减少模型训练的计算资源需求,提高模型的泛化能力,减少过拟合的风险。飞桨通过提供这些高效、灵活的线性代数 API,极大地简化了机器学习和深度学习中的数据处理和分析工作,为高维数据集的处理和分析提供了有效工具。
————END————
推荐阅读
统一多场景自动编译加速——支持动态shape场景,一套架构搞定训推需求