Eigen与NumPy的SVD结果差异:V矩阵不一致问题排查
SVD分解中Eigen与numpy的V矩阵差异分析
问题背景
将Python原型迁移到C++时,使用相同矩阵执行SVD计算,发现Eigen的JacobiSVD与numpy.linalg.svd得到的H、U、S结果一致,但V矩阵存在视觉差异。
Python代码与输出
(U, S, V) = np.linalg.svd(H) print("h\n", H) print("u\n", U) print("v\n", V) print("s\n", S) rotation_matrix = np.dot(U, V)
输出:
h [[ 1.19586781e+00 -1.36504900e+00 3.04707238e+00] [-3.24276981e-01 4.25640964e-01 -6.78455372e-02] [ 4.58970250e-02 -7.33566042e-02 -2.96605698e-03]] u [[-0.99546325 -0.09501679 0.0049729 ] [ 0.09441242 -0.97994807 0.17546529] [-0.01179897 0.17513875 0.98447306]] v [[-0.34290622 0.39295764 -0.85322893] [ 0.49311955 -0.6977843 -0.51954806] [-0.79953014 -0.59890012 0.04549948]] s [3.5624894 0.43029207 0.00721429]
C++代码与输出
std::cout << "H\n" << HTest << std::endl; Eigen::JacobiSVD<Eigen::MatrixXd> svd; svd.compute(HTest, Eigen::ComputeThinV | Eigen::ComputeThinU); std::cout << "h is" << std::endl << HTest << std::endl; std::cout << "Its singular values are:" << std::endl << svd.singularValues() << std::endl; std::cout << "Its left singular vectors are the columns of the thin U matrix:" << std::endl << -1*svd.matrixU() << std::endl; std::cout << "Its right singular vectors are the columns of the thin V matrix:" << std::endl << -1*svd.matrixV() << std::endl;
输出:
h is 1.19587 -1.36505 3.04707 -0.324277 0.425641 -0.0678455 0.045897 -0.0733566 -0.00296606 Its singular values are: 3.56249 0.430292 0.00721429 Its left singular vectors are the columns of the thin U matrix: -0.995463 -0.0950168 0.0049729 0.0944124 -0.979948 0.175465 -0.011799 0.175139 0.984473 Its right singular vectors are the columns of the thin V matrix: -0.342906 0.49312 -0.79953 0.392958 -0.697784 -0.5989 -0.853229 -0.519548 0.0454995
差异原因
1. V矩阵的存储形式不同
numpy的np.linalg.svd返回的V是右奇异向量矩阵的转置——每个行向量对应一个右奇异向量;而Eigen的JacobiSVD::matrixV()返回的是右奇异向量矩阵本身——每个列向量对应一个右奇异向量。
对比输出可以发现:
- Python的V矩阵第一行:
[-0.34290622, 0.39295764, -0.85322893] - Eigen的
-1*svd.matrixV()第一列:-0.342906, 0.392958, -0.853229
两者数值完全一致,只是一个是行向量,一个是列向量。也就是说,numpy返回的V等价于Eigen的matrixV()的转置(再加上你代码中的符号调整)。
2. 奇异向量的符号具有任意性
SVD分解的数学特性决定了:对于任意奇异值对应的左奇异向量u_i和右奇异向量v_i,将两者同时取反(-u_i和-v_i),仍然满足原矩阵的分解关系H = U Σ V^T。因此,不同库返回的奇异向量符号可能不同,只要对应的U和V的符号同时调整,分解就是正确的。
你在C++代码中对matrixU乘-1对齐了Python的U矩阵,若要让V矩阵视觉上完全匹配,只需将Eigen的matrixV()转置后再调整符号即可。
验证分解正确性
可以通过重构原矩阵验证两者的分解有效性:
- Python中执行:
H_recon = U @ np.diag(S) @ V - C++中执行:
Eigen::MatrixXd H_recon = svd.matrixU() * svd.singularValues().asDiagonal() * svd.matrixV().transpose();
两者的重构结果会在数值精度范围内完全一致,说明两种分解都是正确的,只是V的存储形式和符号差异导致了视觉上的不同。
内容的提问来源于stack exchange,提问作者cheesemas46
相关产品推荐
相关产品推荐

