一. 前言
本文旨在记录自己在做PCA人脸识别的时候遇到的一些问题和解决的办法,并分享自己的源码。本文更侧重于代码的分享,对于PCA的具体原理就不再过多赘述,具体内容涉及到特征脸的输出、重构后的人脸图片输出以及准确率的预测,欢迎大家一起交流讨论!
二. 简介
本文采用orl4646的数据集,其中包括400张46*46的人脸照片,照片中一共有40个不同的人的人脸照片,每个人有10张人脸照片。本文采用PCA降维的主成分分析的方法,采用7:3的比例把数据分为训练集和验证集。把46*46的人脸图片降维到100维,通过比较验证集和训练集的余弦相似度,采用KNN的方法预测模型。
三. 代码
首先在主函数前,是一些超参数的设定和导包:
import scipy.io as scio
import numpy as np
# 将所有照片按顺序分成40个组
num_groups = 40
# 每组有10张照片
images_per_group = 10
images_per_train_set = 7
images_per_validation_set = 3
# 降维后的训练集和验证集
reduced_face_train = np.random.rand(280, 100)
reduced_face_valid = np.random.rand(120, 100)
# 降维后的验证集人脸与所有测试集的人脸的余弦相似度
similarities = np.random.rand(280)
output_folder = "C:\\Users\\10099\\PycharmProjects\\PCA\\reduced_face"
# 正确预测的数量
count = 0
在主函数中,我们首先要提取数据集中的数据,并将数据集分为训练集和验证集。从orl4646中提取到的是numpy的数据,形状为46*46*400。在提取之后,我们要把这个数据拉长为二维向量,行数为像素的个数46*46,列数为样本的数量,具体代码如下:
train_set = np.empty([46, 46, num_groups * images_per_train_set])
valid_set = np.empty([46, 46, num_groups * images_per_validation_set])
data = scio.loadmat("ORL4646.mat")
image_data = data['ORL4646']
num_groups = 40
images_per_group = 10
image_groups = []
# 将数据分为测试集和验证集
for i in range(num_groups):
start_index = i * images_per_group
end_index = (i + 1) * images_per_group
start_index2 = i * images_per_train_set
start_index3 = i * images_per_validation_set
train_set[:, :, start_index2:start_index2 + images_per_train_set] = image_data[:, :, start_index:start_index + images_per_train_set]
valid_set[:, :, start_index3:start_index3 + images_per_validation_set] = image_data[:, :, start_index + images_per_train_set:end_index]
train_set = train_set.reshape((46*46, num_groups*images_per_train_set))
valid_set = valid_set.reshape((46*46, num_groups*images_per_validation_set))
如果想要直观的观察到图片的话,可以采用plt来查看具体某一张图片:
Image1 = Image.fromarray(train_set[:, 0].reshape(46, 46)
plt.imshow(Image1)
plt.show()
可视化操作,把训练集的数据保存到到文件夹中,验证集同理:
# 保存训练集图片
for i in range(280):
Image_train = Image.fromarray(train_set[:, i].reshape(46, 46))
Image_train = Image_train.convert("L")
filename = os.path.join(output_folder, f"{i}.jpg")
Image_train.save(filename)
下面就要进行关键的PCA操作,首先要将图片规格化,减去每个特征的平均,也就是减去平均脸。然后计算协方差矩阵,得到特征值和特征向量。由于这里的特征值和特征向量是复数的形式,虽然虚数部分为0,但是在代码中还是要取实数的部分,否则会报错。由于特征值默认就是从大到小排列的,因此只要取特征向量的前100个:
# 求平均脸
mean_face = np.mean(train_set, axis=1)
# 样本中心化
train_set = train_set - mean_face[:, np.newaxis]
# 计算协方差矩阵
cov_train = np.cov(train_set)
# 计算特征值和特征向量
eigenvalues, eigenvectors = np.linalg.eig(cov_train)
# 转置特征
eigen_face = np.transpose(np.real(eigenvectors[:, :100]))
特征脸就是协方差矩阵的特征向量,但是在输出特征脸之前,要把特征向量归一化,否则特征脸的亮度会非常低,看起来就是黑色的,因此要定义一个normalization函数实现图片归一化:
# 将图片数据标准化
def normalize(input_data):
min_value = np.min(input_data)
max_value = np.max(input_data)
# 将数据缩放到0-255范围
normalized_data = 255 * (input_data - min_value) / (max_value - min_value)
return normalized_data
特征脸输出:
# 输出前100个特征脸
for i in range(100):
Image1 = Image.fromarray(normalize(eigen_face.reshape(46, 46)))
Image1 = Image1.convert("L")
filename = os.path.join(output_folder, f"{i}.jpg")
Image1.save(filename)
如果要输出重构后的人脸图像的话,就是要将前100的特征向量分别和人脸图片做内积,每一次内积会得到一个常数,这个常数代表着这个人脸在这个特征向量的维度的投影的值,再把这个常数乘以该特征向量,得到一个特征,再把这些特征加起来再加上平均脸,就得到了可视化的重构后的人脸图像:
# 输出重构之后的人脸图像
ref_pic = mean_face
for i in range(280):
for j in range(100):
ref_pic = ref_pic + np.dot(eigen_face[j, :], train_set[:, i])*eigen_face[j, :]
ref_pic = normalize(ref_pic)
image1 = Image.fromarray(ref_pic.reshape(46, 46))
image1 = image1.convert("L")
filename = os.path.join(output_folder, f"{i}.jpg")
image1.save(filename)
ref_pic = mean_face
求训练集降维后的人脸向量:
# 求训练集降维后的人脸向量
for i in range(280):
for j in range(100):
reduced_face_train[i, j] = np.dot(eigen_face[j, :], train_set[:, i])
以上,对训练集的处理已经全部完毕,下面要对验证集进行降维:
# 求验证集降维之后的数据
for i in range(120):
for j in range(100):
reduced_face_valid[i, j] = np.dot(eigen_face[j, :], valid_set[:, i])
对验证集降维后,用余弦相似度判断验证集中每一个人脸向量和训练集的相似度,构造predict函数对相似度进行排序,取前7个相似度最高的人脸,最多的那一个即预测的人脸:
#predict函数
def predict(vector_similarities):
sorted_similarities = np.argsort(vector_similarities)
max_similarities = sorted_similarities[-7:] // 7
counts = np.bincount(max_similarities)
most_common_value = np.argmax(counts)
return most_common_value
# 判断模型的准确率
for i in range(120):
for j in range(280):
# 计算余弦相似度
cos_similarity = cosine_similarity(reduced_face_valid[i, :], reduced_face_train[j, :])
similarities[j] = cos_similarity
# 预测结果和真实结果相比较
if predict((similarities.flatten())) == i//3:
count = count + 1
print(f"第{i}个图像预测正确!")
else:
print(f"第{i}个图像预测错误!预测结果为{predict((similarities.flatten()))},实际结果为{i//3}")
print("准确率为:", count/120*100, '%')
四.源码链接
源码链接:https://pan.baidu.com/s/1xTXLKINg1b3UExkqE1UtLw?pwd=5gra
提取码:5gra
五.结语
有任何疑问欢迎和我交流,如果这篇文章帮助到了你的话,请你点一个赞可以吗?十分感谢!