在六个月前的这个问题中,jez 很好地帮助我提出了行差外积的快速近似值,即:
K = np.zeros((len(X), len(X))) for i, Xi in enumerate(X): for j, Xj in enumerate(X): dij = Xi - Xj K += np.outer(dij, dij)
这对于找到一种 Fisher 判别分析形式的散点矩阵计算很有效。但现在我正尝试进行局部 Fisher 判别分析,其中每个外积都由一个矩阵 A 加权,该矩阵包含有关该对局部性的信息,因此新行是:
K += A[i][j] * np.outer(dij, dij)
不幸的是,前面答案中提出的计算非加权散点矩阵的快速方法对此不起作用,而且据我所知,进行快速改变并不容易。
线性代数绝对不是我的强项,我不擅长想出这类东西。有什么快速方法可以计算成对行差外积的加权和?
在你的问题中,你希望计算以下形式的加权和:
[ K = \sum_{i,j} A[i][j] \cdot (\mathbf{X}_i - \mathbf{X}_j)(\mathbf{X}_i - \mathbf{X}_j)^T ]
以下是利用 NumPy 提供的高效广播和 einsum 运算来解决此问题的方法。
einsum
给定: - ( \mathbf{X} ) 是一个形状为 ((N, D)) 的矩阵,表示 (N) 个样本的 (D) 维特征。 - ( A ) 是一个 (N \times N) 的加权矩阵,表示样本对的权重。
目标是高效地计算加权的成对行差外积的和。
首先需要计算成对差值 ( \mathbf{X}_i - \mathbf{X}_j )。这可以通过 NumPy 的广播机制完成:
import numpy as np # 示例数据 N, D = 100, 10 # N 个数据点,D 维特征 X = np.random.rand(N, D) # 形状 (N, D) A = np.random.rand(N, N) # 形状 (N, N) # 计算成对差 X_diff = X[:, np.newaxis, :] - X[np.newaxis, :, :] # 形状 (N, N, D)
X[:, np.newaxis, :]
X[np.newaxis, :, :]
对每对样本 ( (i, j) ),需要计算 ( (\mathbf{X}_i - \mathbf{X}_j)(\mathbf{X}_i - \mathbf{X}_j)^T ),并乘以权重 ( A[i][j] )。这可以通过 np.einsum 高效完成:
np.einsum
# 计算加权外积的加权和 K = np.einsum('ij,ijk,ijl->kl', A, X_diff, X_diff)
'ij,ijk,ijl->kl'
ij
ijk
ijl
kl
X_diff
以下是完整实现:
import numpy as np # 示例数据 N, D = 100, 10 # N 个数据点,D 维特征 X = np.random.rand(N, D) # 形状 (N, D) A = np.random.rand(N, N) # 形状 (N, N) # 计算成对差 X_diff = X[:, np.newaxis, :] - X[np.newaxis, :, :] # 形状 (N, N, D) # 计算加权外积的加权和 K = np.einsum('ij,ijk,ijl->kl', A, X_diff, X_diff) # 形状 (D, D) print("加权散点矩阵的形状:", K.shape)
如果 ( N ) 很大,内存可能会成为瓶颈,可以考虑将数据分块处理(例如通过分块计算 X_diff 或使用稀疏矩阵)。
使用 np.einsum 配合广播机制是计算这种加权和的最优雅和高效的方式。它避免了 Python 的显式循环,充分利用了 NumPy 的矢量化能力,适合处理中等规模的 ( N ) 和 ( D )。