我正在Principal Component Analysis
中实现Python
人脸识别,而不使用PCA
或numpy
中已定义的OpenCV
方法。但我的结果实际上是垃圾。
我在OpenCV
的{{3}}阅读了文档和算法说明。但有些事情并不清楚。
X = {x1, x2, ..., xn}
那么我相信它不是计算协方差矩阵时使用的X矩阵?但是你要减去平均值等,如前两步所述。我实施了以下内容:
MU = X.mean(axis=0)
for i in range(n):
X[i,:] -= MU
S = (np.dot(X, X.T) / float(n))
#Hermitian (or symmetric) matrix.
eigenvalues, eigenvectors = numpy.linalg.eigh(S)
eigenvectors = numpy.dot(X.T, eigenvectors)
for i in range(n):
eigenvectors[:,i] = normalize_vector(eigenvectors[:,i])
请注意,示例值存储在行而不是列中。因此X
的形状为nxd
,样本数为n
,样本的维度为d
。
以上图片是参考。第一个是均值,以下三个是最大的特征向量。 下图是我的结果。第一个是均值,以下是某些顺序的所有特征向量。但它们似乎与结果不符。
cv2.PCACompute(X, 6)
仍会产生更好的结果。
答案 0 :(得分:1)
第一个问题很好回答here。
对于第二个答案,你需要根据它们对应的特征值的值来对特征向量进行排序。为此,您可以使用python np.argsort()
然后反转此顺序(argsort顺序从小到大):
indexes = np.argsort(eigenvalues)[::-1]
eigval = eigenvalues[indexes]
eigvec = eigenvectors[:,indexes]
检查您的代码,我发现了一些问题:
现在,你正在获得所有特征向量,你忽略了nb_components
参数,你应该只选择你要求的向量。这是用
eigenvectors = eigenvectors[:,indexes][0:nb_components]
对于归一化向量(在pca函数内),你使用的是从0到n
的for循环,但是如果你只被要求,比如3个特征向量,那么你只有3列。要解决此问题,请从0迭代到nb_components
。
除此之外,您的代码运作完美。我尝试了它只采用3个Principals组件,我得到6/6作为最终结果。在我看来,当从float转换为uint8以使用imshow时,显示特征向量的差异只是一个表示问题。
关于负特征值,它只是eigh
的问题。由于特征值显示方向的方差,我们关心绝对值,但如果我们改变符号,我们还必须改变"方向" (特征向量)。你可以将这个乘以负特征值及其相应的特征向量乘以-1.0
(见this):
s = np.where(eigenvalues < 0)
eigenvalues[s] = eigenvalues[s] * -1.0
eigenvectors[:,s] = eigenvectors[:,s] * -1.0
您也可以使用numpy.linalg.svd
(docs)解决此问题,但速度应该比numpy.linalg.eigh
慢。
总结一下,这是我从你的代码中提出的代码(我在删除所有注释时将其缩短):
def pca(X, nb_components=0):
[n,d] = X.shape
if (nb_components <= 0) or (nb_components>6):
nb_components = n
MU = X.mean(axis=0)
for i in range(n):
X[i,:] -= MU
S = (np.dot(X, X.T) / float(n))
eigenvalues, eigenvectors = np.linalg.eigh(S)
s = np.where(eigenvalues < 0)
eigenvalues[s] = eigenvalues[s] * -1.0
eigenvectors[:,s] = eigenvectors[:,s] * -1.0
indexes = np.argsort(eigenvalues)[::-1]
eigenvalues = eigenvalues[indexes]
eigenvectors = eigenvectors[:,indexes][:,0:nb_components]
eigenvectors = np.dot(X.T, eigenvectors)
for i in range(nb_components):
eigenvectors[:,i] = normalize_vector(eigenvectors[:,i])
return (eigenvalues, eigenvectors, MU)