numpy 中检查向量是否对齐或方向相反的最快方法(截断 SVD post 处理)
Fastest way in numpy to check if vectors are aligned or have opposite direction (truncated SVD post processing)
我有一堆向量存储在矩阵 U 的列中。
我还有一个包含列向量的矩阵 V。 V 中的每个向量可以是
- 与 U 中的对应物几乎相同,具有数值近似值
- 或符号相反,数值近似。
目标是找到一个数组,如果向量方向相同则为 1,如果方向相反则为 -1。
另外两件事:
- 我只对第一个迹象感兴趣,而不是全部(下例中 10000 个中的 100 个)。
- V 以其转置形式给出 (Vt)
到目前为止我找到了三种方法来解决这个问题:
from timeit import timeit
import numpy as np
# create fake SVD output
n_components = 100
n_samples = 10000
U = np.random.random((n_samples, n_samples - 1))
true_signs = np.sign(np.random.random(n_samples - 1) - 0.5)
Vt = np.multiply(U, true_signs[None, :]).T
# simulate some numerical imprecision
Vt += np.random.random((n_samples - 1, n_samples)) * 0.001
# 3 competing methods
def compute_signs_dot():
VtU = np.dot(Vt[:n_components, :], U[:, :n_components])
signs = np.sign(np.diag(VtU))
np.testing.assert_equal(signs, true_signs[:n_components])
def compute_signs_mul():
diag_VtU = np.multiply(Vt[:n_components, :].T,
U[:, :n_components]).sum(axis=0)
signs = np.sign(diag_VtU)
np.testing.assert_equal(signs, true_signs[:n_components])
def compute_signs_sign():
signs = np.multiply(np.sign(Vt[:n_components, :].T),
np.sign(U[:, :n_components])).sum(axis=0)
signs = np.sign(signs)
np.testing.assert_equal(signs, true_signs[:n_components])
# compare execution times
print("compute_signs_dot: %.3fs" % timeit(compute_signs_dot, number=100))
print("compute_signs_mul: %.3fs" % timeit(compute_signs_mul, number=100))
print("compute_signs_sign: %.3fs" % timeit(compute_signs_sign, number=100))
产量
compute_signs_dot: 2.001s
compute_signs_mul: 0.786s
compute_signs_sign: 1.693s
所以到目前为止最快的方法似乎是计算每个列索引处的向量对之间的标量积,方法是将项乘以项和总和 (compute_signs_mul
)。
我们将不胜感激任何其他更快或具有类似速度的方法。
注意:正如一些读者会注意到的,这是对截断 SVD 的输出进行后处理,以便通过找到它们的符号将奇异值转换为特征值。
我们可以使用np.einsum
-
diag_VtU = np.einsum('ji,ij->j',Vt[:n_components, :], U[:, :n_components])
或者,使用 np.matmul/@-operator
得到 diag_VtU
-
(Vt[:n_components, :][:,None,:] @ (U[:, :n_components].T)[:,:,None])[:,0,0]
我有一堆向量存储在矩阵 U 的列中。 我还有一个包含列向量的矩阵 V。 V 中的每个向量可以是
- 与 U 中的对应物几乎相同,具有数值近似值
- 或符号相反,数值近似。
目标是找到一个数组,如果向量方向相同则为 1,如果方向相反则为 -1。
另外两件事:
- 我只对第一个迹象感兴趣,而不是全部(下例中 10000 个中的 100 个)。
- V 以其转置形式给出 (Vt)
到目前为止我找到了三种方法来解决这个问题:
from timeit import timeit
import numpy as np
# create fake SVD output
n_components = 100
n_samples = 10000
U = np.random.random((n_samples, n_samples - 1))
true_signs = np.sign(np.random.random(n_samples - 1) - 0.5)
Vt = np.multiply(U, true_signs[None, :]).T
# simulate some numerical imprecision
Vt += np.random.random((n_samples - 1, n_samples)) * 0.001
# 3 competing methods
def compute_signs_dot():
VtU = np.dot(Vt[:n_components, :], U[:, :n_components])
signs = np.sign(np.diag(VtU))
np.testing.assert_equal(signs, true_signs[:n_components])
def compute_signs_mul():
diag_VtU = np.multiply(Vt[:n_components, :].T,
U[:, :n_components]).sum(axis=0)
signs = np.sign(diag_VtU)
np.testing.assert_equal(signs, true_signs[:n_components])
def compute_signs_sign():
signs = np.multiply(np.sign(Vt[:n_components, :].T),
np.sign(U[:, :n_components])).sum(axis=0)
signs = np.sign(signs)
np.testing.assert_equal(signs, true_signs[:n_components])
# compare execution times
print("compute_signs_dot: %.3fs" % timeit(compute_signs_dot, number=100))
print("compute_signs_mul: %.3fs" % timeit(compute_signs_mul, number=100))
print("compute_signs_sign: %.3fs" % timeit(compute_signs_sign, number=100))
产量
compute_signs_dot: 2.001s
compute_signs_mul: 0.786s
compute_signs_sign: 1.693s
所以到目前为止最快的方法似乎是计算每个列索引处的向量对之间的标量积,方法是将项乘以项和总和 (compute_signs_mul
)。
我们将不胜感激任何其他更快或具有类似速度的方法。
注意:正如一些读者会注意到的,这是对截断 SVD 的输出进行后处理,以便通过找到它们的符号将奇异值转换为特征值。
我们可以使用np.einsum
-
diag_VtU = np.einsum('ji,ij->j',Vt[:n_components, :], U[:, :n_components])
或者,使用 np.matmul/@-operator
得到 diag_VtU
-
(Vt[:n_components, :][:,None,:] @ (U[:, :n_components].T)[:,:,None])[:,0,0]