我试图根据其第一个光谱分量来转换数组,但我得到的输出特征与原始特征非常不同,即使我保留了整个分量。
让我们考虑这个 4x4 数组
import numpy as np
from numpy.linalg import eig
arr = np.asarray([[23, 34, 78, 54],
[87, 98, 23, 67],
[76, 67, 98, 29],
[56, 88, 28, 27]])
它的邻接矩阵将是(对称矩阵)
A = np.asarray([[0., 1., 0., 0., 1., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[1., 0., 1., 0., 1., 1., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 1., 0., 1., 0., 1., 1., 1., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 1., 0., 0., 0., 1., 1., 0., 0., 0., 0., 0., 0., 0., 0.],
[1., 1., 0., 0., 0., 1., 0., 0., 1., 1., 0., 0., 0., 0., 0., 0.],
[1., 1., 1., 0., 1., 0., 1., 0., 1., 1., 1., 0., 0., 0., 0., 0.],
[0., 1., 1., 1., 0., 1., 0., 1., 0., 1., 1., 1., 0., 0., 0., 0.],
[0., 0., 1., 1., 0., 0., 1., 0., 0., 0., 1., 1., 0., 0., 0., 0.],
[0., 0., 0., 0., 1., 1., 0., 0., 0., 1., 0., 0., 1., 1., 0., 0.],
[0., 0., 0., 0., 1., 1., 1., 0., 1., 0., 1., 0., 1., 1., 1., 0.],
[0., 0., 0., 0., 0., 1., 1., 1., 0., 1., 0., 1., 0., 1., 1., 1.],
[0., 0., 0., 0., 0., 0., 1., 1., 0., 0., 1., 0., 0., 0., 1., 1.],
[0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 0., 0., 0., 1., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 1., 0., 1., 0., 1., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 1., 0., 1., 0., 1.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 0., 0., 1., 0.]])
其度数和拉普拉斯矩阵定义为:
degree = A.sum(axis=1)
D = np.diag(degree)
# Laplacian matrix
L = D - A
计算其特征向量和特征值,并按降序排序
eig_val, eig_vec = eig(L)
idx = eig_val.argsort()[::-1]
对特征向量进行相应排序
eig_vec = eig_vec[:,idx]
特征向量形成正交基,因此 2 个不同向量的乘积必须为 0。 我注意到这里的情况并非如此,例如第一个和第二个特征向量的乘积是
sum(np.multiply(eig_vec[0], eig_vec[1])) = 0.043247527085787975
我有什么遗漏的吗?
计算输入数组的光谱分量
spectral = np.matmul(eig_vec.transpose(), arr.flatten())
print(spectral.shape)
取前 k 个分量。这意味着屏蔽其余部分(用 0 替换)
k = 15
masked = np.zeros(spectral.shape)
m = spectral[:k]
masked[:k] = m
获取更新的功能
updated_arr = np.matmul(eig_vec, masked)
updated_arr = updated_arr.reshape(arr.shape[0], -1)
print(updated_arr)
array([[-40.29945921, -20.838019 , 15.23873386, -1.31402251],
[ 33.1372913 , 37.06095382, -34.98050288, 16.02987125],
[ 10.92036053, 8.63461724, 42.59316054, -34.61663745],
[ -5.31097749, 33.28191998, -33.21174921, -26.32554079]])
更新后的数组与原始数组有很大不同,即使我考虑了总光谱分量。 通常,随着组件数量的减少,图像会变得越来越模糊,并且使用所有组件应该会得到相同的图像。
欢迎任何建议或资源查看。
回答关于正交特征向量的部分: np.linalg.eig 返回特征向量,使得 eigenvectors[:,i] 列是对应于特征值 eigenvalues[i] 的特征向量。所以像这样计算乘积
sum(np.multiply(eig_vec[:,0], eig_vec[:,1]))
总和将为零。
也许您遇到了一些数字稳定性问题。要获得对称矩阵的特征分解,您应该使用
eigh
函数而不是 eig
。以下代码对我有用:
import numpy as np
from numpy.linalg import eigh
A = np.asarray([[0., 1., 0., 0., 1., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[1., 0., 1., 0., 1., 1., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 1., 0., 1., 0., 1., 1., 1., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 1., 0., 0., 0., 1., 1., 0., 0., 0., 0., 0., 0., 0., 0.],
[1., 1., 0., 0., 0., 1., 0., 0., 1., 1., 0., 0., 0., 0., 0., 0.],
[1., 1., 1., 0., 1., 0., 1., 0., 1., 1., 1., 0., 0., 0., 0., 0.],
[0., 1., 1., 1., 0., 1., 0., 1., 0., 1., 1., 1., 0., 0., 0., 0.],
[0., 0., 1., 1., 0., 0., 1., 0., 0., 0., 1., 1., 0., 0., 0., 0.],
[0., 0., 0., 0., 1., 1., 0., 0., 0., 1., 0., 0., 1., 1., 0., 0.],
[0., 0., 0., 0., 1., 1., 1., 0., 1., 0., 1., 0., 1., 1., 1., 0.],
[0., 0., 0., 0., 0., 1., 1., 1., 0., 1., 0., 1., 0., 1., 1., 1.],
[0., 0., 0., 0., 0., 0., 1., 1., 0., 0., 1., 0., 0., 0., 1., 1.],
[0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 0., 0., 0., 1., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 1., 0., 1., 0., 1., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 1., 0., 1., 0., 1.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 0., 0., 1., 0.]])
degree = A.sum(axis=1)
D = np.diag(degree)
# Laplacian matrix
L = D - A
eig_val, eig_vec = eigh(L)
print(np.mean(np.abs(np.dot(np.dot(eig_vec, np.diag(eig_val)), eig_vec.transpose()) - L)))
# -> 1.5500965941594069e-15
def compare(k):
cmp_val = eig_val[len(eig_val)-k:]
cmp_vec = eig_vec[:, len(eig_val)-k:]
print(np.mean(np.abs(np.dot(np.dot(cmp_vec, np.diag(cmp_val)), cmp_vec.transpose()) - L)))
compare(4)
# -> 0.4905237882047826
compare(8)
# -> 0.3885449860779778
compare(12)
# -> 0.1260071124311158
compare(14)
# -> 0.05127788166023957
compare(15)
# -> 1.5583365306702967e-15
这里是关于
eig
和 eigh
之间差异的进一步讨论:
NumPy:linalg.eig() 和 linalg.eigh() 之间的区别.