You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

np.cov生成非正定协方差矩阵致马氏距离计算出NaN及警告

解决马氏距离近邻检索中协方差矩阵非半正定导致的NaN问题

我之前也碰到过一模一样的问题!核心原因就是你提到的——np.cov返回的协方差矩阵不是半正定的,这会让马氏距离的计算彻底出问题:当计算(x - y)^T @ VI @ (x - y)时,结果可能是负数,开根号就会触发RuntimeWarning,最终得到全NaN的距离值。下面是几个实测有效的解决办法:

1. 给协方差矩阵添加正则化项

最简单的方法是给协方差矩阵的对角线加上一个极小的正数,强制让矩阵变成半正定。这种方法能快速解决浮点误差或者样本量不足导致的非正定问题:

import numpy as np
from sklearn.neighbors import NearestNeighbors

# 计算原始协方差矩阵
c = np.cov(arr)
# 添加正则项,epsilon可以根据你的数据调整,比如1e-6到1e-4
epsilon = 1e-6
c_fixed = c + epsilon * np.eye(c.shape[0])
# 用修正后的矩阵计算逆
VI = np.linalg.inv(c_fixed)

# 重新初始化近邻检索器
neigh = NearestNeighbors(n_neighbors=100, metric='mahalanobis', metric_params={'VI': VI})
neigh.fit(dfeatures)

2. 用SVD分解修正非正定矩阵

如果正则化的效果不够理想,可以通过奇异值分解(SVD)把协方差矩阵里的负奇异值替换成极小的正数,确保矩阵半正定:

u, s, vh = np.linalg.svd(c)
# 把所有负的奇异值替换为1e-6,也可以根据你的数据调整阈值
s = np.maximum(s, 1e-6)
# 重构修正后的协方差矩阵
c_fixed = u @ np.diag(s) @ vh
VI = np.linalg.inv(c_fixed)

3. 使用sklearn的收缩协方差估计器

如果你的样本量小于特征维度,np.cov得到的矩阵天生就是奇异的(秩不足),这时候可以用sklearn提供的ShrunkCovariance或者LedoitWolf来估计更稳定的协方差矩阵:

from sklearn.covariance import ShrunkCovariance

# 初始化收缩协方差估计器,shrinkage参数可以调整,范围0到1
cov_estimator = ShrunkCovariance(shrinkage=0.1)
# 注意:np.cov默认是按行计算特征,而sklearn的协方差估计器需要按列传入样本,所以要转置arr
c_fixed = cov_estimator.fit(arr.T).covariance_
VI = np.linalg.inv(c_fixed)

验证修正效果

在使用修正后的矩阵之前,可以先验证它是否是半正定的(允许微小的负数值,因为浮点误差):

eigenvals = np.linalg.eigvals(c_fixed)
# 检查所有特征值是否都大于等于-1e-6(允许极小的负数,避免浮点误差)
print(np.all(eigenvals >= -1e-6))

只要矩阵变成半正定的,马氏距离的计算就不会再出现负数开根号的问题,警告和NaN也就消失了。

内容的提问来源于stack exchange,提问作者Utkarsh Mall

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 07:06:08