由于图像具有很高的维数,在许多计算机视觉应用中,我们经常使用降维操作。
PCA(Principal Component Analysis,主成分分析)可以在使用尽可能少维数的前提下,尽量多地保持训练数据的信息。
PCA 产生的投影矩阵可以被视为将原始坐标变换到现有的坐标系,坐标系中的各个坐标按照重要性递减排列。
为了对图像数据进行 PCA 变换,变换前,图像需要转换成一维向量表示。我们可以使用 NumPy 类库中的 flatten() 方法进行变换。
PCA计算步骤【引用 Dujing2019 作者的整理】(思想是把数据投影到方向向量使数据集的特征向量到方向向量的垂线长度最短)
- 去平均 (即为数据中心化)
- 计算协方差矩阵
- 计算协方差矩阵的特征向量和特征值
- 将特征值从小到大排列
- 保留最上面的n个特征向量
- 将数据转换到上述n个特征向量构建新的空间
备注:降维过的方向向量就是新的数据集、特征向量就是数据集维度个数,通过特征向量个数限制降维的维数。
我们通常使用 SVD(Singular Value Decomposition,奇异值分解)方法来计算主成分;但当矩阵的维数很大时, SVD 的计算非常慢,所以此时通常不使用 SVD 分解。
即,如果数据个数小于向量的维数,我们不用 SVD 分解,而是计算维数更小的协方差矩阵 的特征向量。通过仅计算对应前 k(k 是降维后的维数)最大特征值的特征向量,可以使上面的 PCA 操作更快。
代码如下:
from numpy import *
def pca(X):
""" 主成分分析:
输入:矩阵X,其中该矩阵中存储训练数据,每一行为一条训练数据
返回:投影矩阵(按照维度的重要性排序)、方差和均值 """
# 获取维数
num_data,dim = X.shape
# 数据中心化
mean_X = X.mean(axis=0)
X = X - mean_X
if dim > num_data:
# PCA- 使用紧致技巧
M = dot(X, X.T) # 协方差矩阵
e, EV = linalg.eigh(M) # 特征值和特征向量
tmp = dot(X.T, EV).T # 这就是紧致技巧
V = tmp[::-1] # 由于最后的特征向量是我们所需要的,所以需要将其逆转
S = sqrt(e)[::-1] # 由于特征值是按照递增顺序排列的,所以需要将其逆转
for i in range(V.shape[1]):
V[:, i] /= S
else:
# PCA- 使用 SVD 方法
U, S, V = linalg.svd(X)
V = V[:num_data] # 仅仅返回前 nun_data 维的数据才合理
return V, S, mean_X
如果想要保存一些结果或者数据以方便后续使用,Python 中的 pickle 模块非常有用。pickle 模块可以接受几乎所有的 Python 对象,并且将其转换成字符串表示,该过程叫做封装(pickling)。从字符串表示中重构该对象,称为拆封(unpickling)。 这些字符串表示可以方便地存储和传输。
下面举个例子:
# pickle模块主要函数的应用举例
import pickle
dataList = [[1, 1, 'yes'],
[1, 1, 'yes'],
[1, 0, 'no'],
[0, 1, 'no'],
[0, 1, 'no']]
dataDic = {0: [1, 2, 3, 4],
1: ('a', 'b'),
2: {'c': 'yes', 'd': 'no'}}
# 使用dump()将数据序列化到文件中
fw = open('dataFile.pkl', 'wb')
# Pickle the list using the highest protocol available.
pickle.dump(dataList, fw, -1)
# Pickle dictionary using protocol 0.
pickle.dump(dataDic, fw)
fw.close()
# 使用load()将数据从文件中序列化读出
print('............. open() close() ................')
fr = open('dataFile.pkl', 'rb')
data1 = pickle.load(fr)
print(data1)
data2 = pickle.load(fr)
print(data2, '\n')
fr.close()
# 或者使用with()思想,打开文件并保存
with open('dataFile_01.pkl', 'wb') as f:
pickle.dump(dataList, f, -1)
pickle.dump(dataDic, f)
# 或者使用with()思想,打开文件并读取
with open('dataFile_01.pkl', 'rb') as f:
dt_01 = pickle.load(f)
dt_02 = pickle.load(f)
print('............. with() ................')
print(dt_01)
print(dt_02, '\n')
# 使用dumps()和loads()举例
print('............. 使用dumps()和loads()举例 ................')
p = pickle.dumps(dataList)
print(pickle.loads(p))
p = pickle.dumps(dataDic)
print(pickle.loads(p))