
本文介绍一种内存友好的方法:利用稀疏对角矩阵左乘与右乘,避免生成稠密的外积矩阵,从而在 o(n) 空间复杂度下完成稀疏协方差矩阵到稀疏相关矩阵的转换。
本文介绍一种内存友好的方法:利用稀疏对角矩阵左乘与右乘,避免生成稠密的外积矩阵,从而在 o(n) 空间复杂度下完成稀疏协方差矩阵到稀疏相关矩阵的转换。
在统计建模与高维数据分析中,常需将协方差矩阵标准化为相关系数矩阵。标准做法是按元素除以标准差的外积:
[
\text{corr}{ij} = \frac{\text{cov}{ij}}{\sigma_i \sigma_j}
]
但若直接计算 np.outer(np.sqrt(cov.diagonal()), np.sqrt(cov.diagonal())),会生成一个 $n \times n$ 的稠密矩阵——当 $n = 10^5$ 时,仅该矩阵就需约 80 GB 内存(float64),完全不可行。
幸运的是,上述除法等价于双侧缩放(bilinear scaling):
[
\mathbf{C}{\text{corr}} = \mathbf{D}^{-1/2} \, \mathbf{C}{\text{cov}} \, \mathbf{D}^{-1/2},
]
其中 $\mathbf{D}$ 是以协方差矩阵对角元(即方差)为对角线的对角矩阵,$\mathbf{D}^{-1/2}$ 是其元素级平方根倒数构成的稀疏对角矩阵。
该操作天然支持稀疏性:scipy.sparse.diags() 可零内存开销构造稀疏对角矩阵;@(矩阵乘法)自动保持稀疏结构,且时间复杂度仅为 $O(\text{nnz}(\mathbf{C}_{\text{cov}}))$,远优于 $O(n^2)$ 的稠密外积。
✅ 正确、高效、内存友好的实现如下:
import numpy as np
from scipy import sparse
# 构造示例稀疏协方差矩阵(CSR 格式)
A = np.array([
[1.0, 0.2, 0.3, 0.0, 0.0],
[0.2, 2.0, 1.0, 0.0, 0.0],
[0.3, 1.0, 3.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 1.0, 0.5],
[0.0, 0.0, 0.0, 0.5, 4.0]
])
cov = sparse.csr_matrix(A)
# 构造稀疏逆标准差对角矩阵:D^(-1/2)
std_inv = 1.0 / np.sqrt(cov.diagonal()) # shape (n,)
S = sparse.diags(std_inv, format='csr') # 高效稀疏对角矩阵
# 双侧缩放 → 稀疏相关矩阵
cor = S @ cov @ S
print(cor.toarray().round(4))
输出结果与原始方法一致,但全程不产生任何 $n \times n$ 稠密中间变量。
⚠️ 注意事项:
- cov.diagonal() 返回稠密一维数组,内存占用仅 $O(n)$,安全;
- sparse.diags(..., format='csr') 推荐显式指定格式,避免隐式转换开销;
- 若协方差矩阵含零方差变量(即 cov.diagonal() 出现 0),需提前处理(如加极小正则项 1e-12 或掩码过滤),否则将触发除零错误;
- 该方法适用于任意 scipy.sparse 格式(CSR、CSC、COO),但 CSR 在矩阵乘法中通常性能最优。
总结:用 S @ cov @ S 替代 cov / np.outer(...) 不仅代码更简洁,更是大规模稀疏场景下的唯一可行方案——它将空间复杂度从 $O(n^2)$ 降至 $O(n + \text{nnz})$,让百万维稀疏相关矩阵计算真正落地。










