为何Numpy求奇异矩阵逆时不报错?探究其判定逻辑
关于Numpy处理奇异矩阵求逆的行为解析
核心问题原因
1. 奇异矩阵调用np.linalg.inv()不抛出异常
Numpy的np.linalg.inv()底层依赖LAPACK库的LU分解实现,它不是通过数学上的严格奇异性判断,而是基于数值精度下的主元阈值检测:
- 当矩阵进行LU分解时,若某个主元的绝对值小于
eps * max(abs(A))(eps为机器浮点精度,约1e-16),才会判定矩阵奇异并抛出异常。 - 你生成的低秩协方差矩阵,虽然数学上是奇异的,但由于浮点数运算的舍入误差,分解出的主元可能并未低于这个阈值,因此不会触发异常。
2. 返回矩阵并非原矩阵的逆
这种情况下np.linalg.inv()返回的是数值近似的“伪逆”(但不是严格的Moore-Penrose伪逆),本质是LU分解在主元未达奇异阈值时强行计算出的结果。由于原矩阵数学上不可逆,这个结果自然无法满足A @ inv(A) = I的严格逆矩阵条件。
Numpy判定奇异性的逻辑:不止依赖条件数
条件数是衡量矩阵接近奇异程度的指标,但Numpy的奇异性检测直接基于LU分解的主元大小,而非条件数:
- 条件数是
max奇异值 / min奇异值,属于全局指标,无法反映分解过程中单个主元的数值情况。 - 你的测试案例中,两个矩阵条件数接近,但第二个矩阵的主元在浮点精度下更接近0,触发了LAPACK的奇异检测阈值,因此调用
inv()时抛出异常;而第一个矩阵的主元刚好高于阈值,所以能完成计算。
代码验证解析
案例1:奇异协方差矩阵求逆无报错
import numpy as np from scipy.stats import ortho_group def generate_random_cov_matrix(sz, rank, eig_val_scale): """ 生成指定秩的随机协方差矩阵,基于特征向量和特征值构造 :param sz: 矩阵维度 sz x sz :param rank: 矩阵的秩 :param eig_val_scale: 特征值的缩放系数(基于卡方分布采样) :return: 协方差矩阵、特征向量、特征值 """ if rank > sz: raise ValueError('秩不能大于矩阵维度') eig_vecs = ortho_group.rvs(dim=sz) eig_vecs = eig_vecs[:, 0:rank] eig_vals = np.random.chisquare(1, rank) * eig_val_scale eig_vals = np.sort(eig_vals)[::-1] cov = eig_vecs @ np.diag(eig_vals) @ eig_vecs.T return cov, eig_vecs, eig_vals n_variables = 20 rank = 10 cov_mat,_,_ = generate_random_cov_matrix(n_variables,rank,100) assert np.isclose(np.linalg.det(cov_mat),[0.]) print(np.mean(cov_mat)) try: cov_inv = np.linalg.inv(cov_mat) print('No exception was raised') except: print('An exception was raised') try: assert np.isclose(cov_mat @ cov_inv,np.identity(n_variables)) except AssertionError: print('The computed inverse is not a proper inverse')
这段代码生成的低秩协方差矩阵,虽然行列式为0(数学奇异),但浮点数值下的LU分解主元未达奇异阈值,因此inv()不报错,返回的结果也不满足逆矩阵的严格条件。
案例2:条件数相同但奇异性判定结果不同
# 运行无报错 arr = np.array([[1.0, 1.2], [2.0 + 1e-14, 2.4 + 1e-14]]) rank = np.linalg.matrix_rank(arr) cond = np.linalg.cond(arr) np.linalg.inv(arr) print("rank: ", rank) print("cond: ", cond) # 条件数相同但求逆失败 arr = np.array([[1.0, 1.2], [2.0 + 1e-15, 2.4 + 1e-15]]) rank = np.linalg.matrix_rank(arr) np.testing.assert_approx_equal(cond, np.linalg.cond(arr)) cond = np.linalg.cond(arr) print("rank: ", rank) print("cond: ", cond) np.linalg.inv(arr)
两个矩阵的条件数接近,但第二个矩阵的LU分解主元更接近机器精度阈值,因此被判定为奇异,触发异常;第一个矩阵的主元刚好高于阈值,因此能完成计算。这说明Numpy的奇异性检测是基于分解过程中的主元数值,而非条件数。
内容的提问来源于stack exchange,提问作者Harry Matthews
相关产品推荐
相关产品推荐

