参考
Eigen3 主页,Eigen3 官网教程
特征值与特征向量含义,强推
相关文章
QR 分解(Q 正交矩阵,R上三角矩阵)
矩阵条件数
向量是一种实体,向量的范数是对向量长度的一种衡量。
矩阵是一种变换,矩阵的范数是对矩阵变换能力的一种衡量。矩阵范数越大,对原有数据的变换越大。
矩阵范数定义为
x 是向量,||x|| 是向量的范数,也就是长度。
矩阵的逆的范数定义为
如果原矩阵是拉伸操作,那么矩阵的逆是压缩操作。
同时考虑这两者的影响:
称为矩阵条件数。
条件数越大,向量在变换后形变越大。
LU 分解有时候得到的解精度不够,因为拆分成 L 和 U 矩阵会破坏原有矩阵变换的精度。
我们需要找到一种与原矩阵 A 等效的分解,同时单个分解矩阵的条件数又不会太大的变换方式。
正交矩阵
正交矩阵的元素为实数,而且行向量与列向量皆为正交的单位向量,使得该矩阵的转置矩阵为其逆矩阵:
正交矩阵一个重要性质就是保持距离不变,所以它是一个保距映射,具体例子为旋转与镜射。
正因为正交矩阵是保距映射,所以它的条件数是 1 !也就是说正交变换对原数据的影响非常小。
旋转变换很熟悉了,这就不画图了。看看镜射变换的情况:
假设镜射变换 是关于 x 轴的对称变换
镜像变换 Q 的逆矩阵可以想象是把 A'X 向量重新恢复到 A 的变换,其实就还是关于 X 轴对称变换回去,所以
!!!注意,正交矩阵的逆并不总是等于它本身,例如旋转矩阵就是不是!!!
Q 的转置矩阵经过计算可以知道 。
QR 分解
QR 分解将矩阵 A 分解为
Ax = b,令 A = QR,那么 QRx = b,令 Rx = y,Qy = b
由于 Q 是正交矩阵,有
QR 分解一般使用 householder 变换得来,householder 是一种平面镜射变换,具体过程就不展开了。
-
QR 分解对比 LU 分解
求解精度更高:QR 分解生成正交变换(旋转或镜射),不会放大或缩小原数据。
计算内存更高:QR 分解需要更多内存空间来存储中间数据。
SVD 分解(奇异值分解)
特征值和特征向量
矩阵 A 作用于 x,等效于 x 进行 λ 倍常量伸缩。称 x 是 A 的特征向量,λ 是对应的特征值。
当特征值 > 1 时候拉伸特征向量,当特征值 < 1 时候缩短特征向量。
一个矩阵可能不止一个特征向量和特征值,也可能没有特征向量和特征值。多组特征向量互相正交。
当一个矩阵 A 作用于一个向量时候,该向量会朝着矩阵 A 的特征值所在的特征向量方向变换。
假设矩阵 ,最大特征值
,其特征向量是
,红色箭头
此时有向量 ,P1=A*P0,P2=A*P1,P3=A*P2,P4=A*P3 ...
可以看到向量 P0 的值在矩阵 A 的反复作用下,朝着 A 的特征向量方向运动。
矩阵 A 的另一个特征值是 ,其特征向量是
,蓝色箭头
当 ,Q1=A*Q0,Q2=A*Q1,Q3=A*Q2,Q4=A*Q3
当 ,M1=A*M0,M2=A*M1,M3=A*M2,M4=A*M3,情况如下:
奇异值分解
特征分解
其中 Q 的列向量是 A 的特征向量单位化,Λ 是对角矩阵,其对角线上的元素为对应的特征值。
只有可对角化矩阵才可以作特征分解。
奇异值分解
特征分解要求 A 矩阵是方阵,而奇异值分解可以对 m*n 矩阵进行特征分解。
其中 U 是 m*m 方阵,Σ 是 m*n 主对角线上元素是奇异值,V 是 n*n 方阵。U 和 V 是正交矩阵。
Σ 中的奇异值是从大到小排列的,且奇异值减少特别地快。多数情况下,前 10% 的奇异值的和占据全部奇异值和的 99% 以上。
SVD 这个性质可以用于数据压缩,去噪,推荐算法分解用户喜好得到隐含的用户需求。
-
SVD 求解最小二乘问题
y = kx + b 模型
void TestSVD()
{
Eigen::MatrixXd P(8, 2);
P << 1.2, 0.63,
1.88, 1.85,
2.4, 3.51,
3.5, 4.49,
3.66, 6.17,
-0.78, -1.51,
-2.26, -2.33,
-2.92, -4.13;
Eigen::MatrixXd A(P.rows(), 2);
Eigen::VectorXd b(P.rows());
for (int i = 0; i < P.rows(); ++i) {
A(i, 0) = P(i, 0);
A(i, 1) = 1;
b(i) = P(i, 1);
}
Eigen::JacobiSVD<Eigen::MatrixXd> svd(A, Eigen::ComputeThinU | Eigen::ComputeThinV);
Eigen::VectorXd params = svd.solve(b);
ROS_INFO_STREAM("y = " << params(0) << "x +" << params(1));
}
y=ax^2+bx+c 模型
void TestSVD1()
{
Eigen::MatrixXd P(8, 2);
P << 1.82, 5.1,
3.2, 4.49,
4.24, 3.72,
5.47, 2.38,
6.69, 3.09,
7.38, 4.39,
7.92, 5.64,
9.43, 6.22;
Eigen::MatrixXd A(P.rows(), 3);
Eigen::VectorXd b(P.rows());
for (int i = 0; i < P.rows(); ++i) {
A(i, 0) = pow(P(i, 0), 2); // x^2
A(i, 1) = P(i, 0); // x
A(i, 2) = 1; // 常数项
b(i) = P(i, 1); // y
}
Eigen::JacobiSVD<Eigen::MatrixXd> svd(A, Eigen::ComputeThinU | Eigen::ComputeThinV);
Eigen::VectorXd params = svd.solve(b);
ROS_INFO_STREAM("y = " << params(0) << "x^2 +" << params(1) << "x +" << params(2));
}