PCA(主成分分析)算法介绍
PCA是多元统计同用来分析数据的一种方法。通过正交变换将一组可能存在相关性的变量转换为一组线性不相关的向量,转换后的这组变量叫做主成分。
而这个转换的过程中,可以丢弃很多相关的成分或者对描述这个物体不重要的成分。从而达到对原始数据降维,提取重要特征的目的。
为什么要降维?
- 简化计算,便于分析
- 去掉冗余,显现特征
基本知识:
首先需要知道几个相关的数学概念,这是我们进行PCA分析的基础:标准差(Standard Deviation)、方差(Variance)、协方差(Covariance)、特征向量(eigenvectors)、特征值(eigenvalues)
-
Standard Deviation(标准差)
标准差就是用来描述一组数据与平均值的偏离程度,反映了一组数据的波动情况,平均值数学表达公式如下:
标准差的表达公式如下:
需要注意的是分母是选择
n
还是n-1
,取决于你选取的数据是整个完整数据还是数据中的一部分。 -
Variance(方差)
方差是数据集中数据分布的另一种度量。实际上,它几乎与标准差相同。
方差的数学表达公式如下:
-
Covariance(协方差)
标准差与方差只针对一维数据进行衡量的指标,协方差是针对二维数据或者是更高维的数据进行的衡量指标,主要用来表示多维度与平均值的偏离程度。
协方差的数学表达公式如下:
-
The covariance Matrix(协方差矩阵)
协方差矩阵主要是用于当数据的维度超过3或者更多的时候,我们可以通过一个矩阵来存储各个维度的协方差,这个矩阵就被称为“协方差矩阵”。
用数学方法来表示一个N为数据的协方差矩阵可以表示为:
现在假设我们有一个三个维度的数据,使用一个协方差矩阵将这三维数据的协方差表示如下:
-
Eigenvectors(特征向量)
在矩阵论中,我们可以这样去理解特征值和特征向量,一个矩阵由一个变换到另一个矩阵,Aα=λα,其中α称为矩阵A 的一个特征向量,λ称为矩阵A的一个特征值。
特征向量确定了矩阵变换的方向,特征值确定了矩阵变换的比例。
在协方差矩阵中,协方差矩阵的特征向量又反应了什么物理意义呢?
协方差矩阵的特征向量代表的意思是方差最大的数据所在的方向。
在n维数据空间中,第一特征向量指向的是数据方差最大的方向,第二特征向量是与第一特征向量垂直的数据方差最大的方向,第三特征向量是与第二特征向量垂直的数据方差最大的方向,以此类推。
-
Choosing components and forming a feature vector(选择主成分并生成特征向量)
一个协方差矩阵有着不同的特征值与特征向量,事实上最高特征值的对应的特征向量就是这个数据集的主成分。
通常来说,一旦协方差矩阵的特征值和特征向量被计算出来了之后,就是按照特征值的大小从高到低依次排列。特征值的大小确定了主成分的重要性。
原理:
PCA原理就是将一个高维向量x,通过一个特殊的特征向量矩阵U,投影到一个低维的向量空间中,表征为一个低维向量y,并且仅仅损失了一些次要信息。
也就是说,通过低维表征的向量和特征向量矩阵,可以基本重构出所对应的原始高维向量。
即选择特征值较大的作为主成分,从而进行降维。
比如:一开始你的数据集是N维的,在进行了协方差矩阵的特征值计算后, 你得到了N个特征值和与这些特征值相对应的特征向量。然后在主成分分析时,你选取了前P个较大的特征值,如此一来,就将原来N维的数据降维到只有P维。这样就起到了降维的效果了。
方法:
其方法主要是通过对协方差矩阵进行特征分解,以得出数据的主成分(即特征向量)与它们的权值(即特征值)。
计算新生成的数据集的公式如下:
其中rowFeatureVector是由模式矢量作为列组成的矩阵的转置,因此它的行就是原来的模式矢量,而且对应最大特征值的特征矢量在该矩阵的最上一行。rowdataAdjust是每一维数据减去均值后,所组成矩阵的转置,即数据项目在每一列中,每一行是一维,对我们的样本来说即是,第一行为x维上数据,第二行为y维上的数据。
正是由于特征向量是两两正交的,那么我们就可以使用任何的特征向量来将原始数据变换到正交的这些坐标轴上。
在计算出协方差矩阵的特征值及特征向量后,接下来,我们选取一个较大的特征值对应的特征向量将原始数据降到一维:
将较大的特征值对应的特征向量转置然后乘以原始数据集,这样就得到新的降维后的一维数据。
换而言之,PCA提供了一种降低数据维度的有效办法;如果分析者在原数据中除掉最小的特征值所对应的成分,那么所得的低维度数据必定是最优化的(也即,这样降低维度必定是失去讯息最少的方法)。主成分分析在分析复杂数据时尤为有用,比如人脸识别。
步骤:
设有m条n维数据(训练集):
- 将原始数据按列组成n行m列矩阵X
- 将X的每一行(代表一个属性字段)进行零均值化,即减去这一行的均值
- 求出协方差矩阵C=(1/m)XX^T(实对称矩阵)
- 求出协方差矩阵的特征值及对应的特征向量
- 将特征向量按对应特征值大小从上到下按行排列成矩阵,取前k行组成矩阵P
- Y=PX 即为降维到k维后的数据
PCA应用–人脸识别
主要思想就是用主成分分析的思想将图像从欧氏空间降维映射到特征空间中,根据不同人脸图像在特征空间中的度量距离来判断其相似度,从而实现人脸识别。
主要流程:
通过上图的PCA人脸识别流程可以看出,PCA方法可以总结为以下几个阶段:训练样本、特征提取、构造特征空间、投影计算。
步骤:
-
使用ORL数据库。人脸图像总样本为(Q=400),共有P(P=40)个人,每人人均L(L=10)幅图像,每张图像大小为 112 x 92 = 10304。
-
对每张图片进行矢量化处理。其中Xi为由第i个人的图像的每一列向量堆叠而成的一列M*N维列向量,即把矩阵向量化。需要注意的是,由于每个人都有L张图像,所以Xi对应的是每个人的平均脸,其中Xij为第i个人第j个样本:
-
计算所有训练图像的平均脸:
-
计算每张人脸与平均脸的差值(去均值):
-
构造协方差矩阵:
-
是求解协方差矩阵的特征值和特征向量,构造特征脸:
- 求出协方差矩阵的特征向量ui和对应的特征值λi,这些特征向量组成的矩阵U就是人脸空间的正交基底,用它们的线性组合可以重构出样本中任意的人脸图像。并且图像信息集中在特征值大的特征向量中,即使丢弃特征值小的向量也不会影响图像质量:
为什么用特征向量和特征值的线性组合可以重构出样本中任意的人脸图像?
-
在人脸识别过程中,对输入的一个测试样本x,求出它与平均脸的偏差,则这个偏差在特征脸空间U的投影,可以表示为系数向量y:
U的维数为M×d(d为特征值个数),偏差的维数为M×1,y的维数为d×1。若M为200*200=40000维,取200个主成分,即200个特征向量,则最后投影的系数向量y维数降维200维。
-
根据上述式子,可以得出:
这里的x就是根据投影系数向量y重构出的人脸图像,丢失了部分图像信息,但不会影响图像质量。
- 将协方差矩阵的特征值按大到小排序,λ 1 > λ 2 . . . > λ t > λ P。根据特征值的贡献率选取前t个最大的特征值及其对应的特征向量v i 。
- 按列取前r个特征向量,r为想降低到r维度。将r个特征向量向每一维数据减去均值后,所组成矩阵的转置矩阵的特征向量过渡。
- 特征向量归一化后,得到测试脸在特征向量下的数据。
- 获取训练数据与测试脸之间的距离,其中最小的数据对应的类别就是该类别人脸。
代码实现:
首先定义一个函数用于将人脸图像矢量化为一个向量,向量的大小与图片的像素有关,代码如下:
# 图片矢量化
def img2vector(image):
img = cv2.imread(image, 0) # 读取图片
rows, cols = img.shape #获取图片的像素
imgVector = np.zeros((1, rows * cols)) #初始值均设置为0,大小就是图片像素的大小
imgVector = np.reshape(img, (1, rows * cols))#使用imgVector变量作为一个向量存储图片矢量化信息
return imgVector
接下来定义一个函数用来选取训练图片,并对每张图片进行前面定义过的矢量化处理:
def load_orl(k):#参数K代表选择K张图片作为训练图片使用
'''
对训练数据集进行数组初始化,用0填充,每张图片尺寸都定为112*92,
现在共有40个人,每个人都选择k张,则整个训练集大小为40*k,112*92
'''
train_face = np.zeros((40 * k, 112 * 92))
train_label = np.zeros(40 * k) # [0,0,.....0](共40*k个0)
test_face = np.zeros((40 * (10 - k), 112 * 92))
test_label = np.zeros(40 * (10 - k))
# sample=random.sample(range(10),k)#每个人都有的10张照片中,随机选取k张作为训练样本(10个里面随机选取K个成为一个列表)
sample = random.permutation(10) + 1 # 随机排序1-10 (0-9)+1
for i in range(40): # 共有40个人
people_num = i + 1
for j in range(10): # 每个人都有10张照片
image = orlpath + '/s' + str(people_num) + '/' + str(sample[j]) + '.jpg'
# 读取图片并进行矢量化
img = img2vector(image)
if j < k:
# 构成训练集
train_face[i * k + j, :] = img
train_label[i * k + j] = people_num
else:
# 构成测试集
test_face[i * (10 - k) + (j - k), :] = img
test_label[i * (10 - k) + (j - k)] = people_num
return train_face, train_label, test_face, test_label
前期将所有训练图片矢量化之后,开始进行PCA算法的降维操作:
def PCA(data, r):#参数r代表降低到r维
data = np.float32(np.mat(data))
rows, cols = np.shape(data)
data_mean = np.mean(data, 0) # 对列求平均值
A = data - np.tile(data_mean, (rows, 1)) # 将所有样例减去对应均值得到A
C = A * A.T # 得到协方差矩阵
D, V = np.linalg.eig(C) # 求协方差矩阵的特征值和特征向量
V_r = V[:, 0:r] # 按列取前r个特征向量
V_r = A.T * V_r # 小矩阵特征向量向大矩阵特征向量过渡
for i in range(r):
V_r[:, i] = V_r[:, i] / np.linalg.norm(V_r[:, i]) # 特征向量归一化
final_data = A * V_r
return final_data, data_mean, V_r
最后我们进行初次训练,随机选取每个人物的五张图片作为训练图片使用。将降低的维数设定为10维,查看一下训练效果如何:
def face_recongize():
#对每一个人随机选取5张照片作为训练数据
train_face, train_label, test_face, test_label = load_orl(5)#随机选择每个人物的5张图片作为训练数据
x_value = []
y_value = []
#将图片降维到10维
data_train_new, data_mean, V_r = PCA(train_face, 10)
num_train = data_train_new.shape[0] # 训练脸总数
num_test = test_face.shape[0] # 测试脸总数
temp_face = test_face - np.tile(data_mean, (num_test, 1))
data_test_new = temp_face * V_r # 得到测试脸在特征向量下的数据
data_test_new = np.array(data_test_new) # mat change to array
data_train_new = np.array(data_train_new)
true_num = 0
for i in range(num_test):
testFace = data_test_new[i, :]
diffMat = data_train_new - np.tile(testFace, (num_train, 1)) # 训练数据与测试脸之间距离
sqDiffMat = diffMat ** 2
sqDistances = sqDiffMat.sum(axis=1) # 按行求和
sortedDistIndicies = sqDistances.argsort() # 对向量从小到大排序,使用的是索引值,得到一个向量
indexMin = sortedDistIndicies[0] # 距离最近的索引
if train_label[indexMin] == test_label[i]:
true_num += 1
else:
pass
accuracy = float(true_num) / num_test
x_value.append(5)
y_value.append(round(accuracy, 2))
print('当对每个人随机选择%d张照片降低至%d维进行训练时,The classify accuracy is: %.2f%%' % (5,10, accuracy * 100))
最终训练得到的结果如下:
为了对比实验,我们分别选取5张、7张、9张,还是降低到10维进行对比实验: