特征值与特征向量
定义:
A
x
⃗
=
λ
x
⃗
A\vec{x}=\lambda\vec{x}
Ax=λx 则称
λ
\lambda
λ为矩阵A的特征值,
x
⃗
\vec{x}
x称为
λ
\lambda
λ对应的特征向量。
假设A是一个可对角化的矩阵,并且具有n个线性独立的特征向量
P
=
[
P
1
⃗
P
2
⃗
P
3
⃗
…
P
n
⃗
]
P=[\vec{P_1} \ \vec{P_2}\ \vec{P_3} … \vec{P_n}]
P=[P1 P2 P3…Pn],对应的特征值为
Λ
=
[
λ
1
⋱
λ
n
]
\Lambda=\left[ \begin{matrix} \lambda_1 & & \\ & \ddots & \\ && \lambda_n \end{matrix} \right]
Λ=⎣⎡λ1⋱λn⎦⎤, 可以将
y
⃗
=
A
x
⃗
\vec{y}=A\vec{x}
y=Ax进行这样的理解
由于P由n个线性独立的向量组成,所以 x ⃗ \vec{x} x肯定可以表示再P的基底下,即 x ⃗ = I x ⃗ = a 1 P 1 ⃗ + a 2 P 2 ⃗ + … + a n P n ⃗ = P [ a 1 a 2 ⋮ a n ] \vec{x}=I\vec{x}=a1\vec{P_1}+a2\vec{P_2}+…+an\vec{P_n}=P \left[\begin{matrix} a_1\\a_2\\ \vdots \\a_n \end{matrix} \right] x=Ix=a1P1+a2P2+…+anPn=P⎣⎢⎢⎢⎡a1a2⋮an⎦⎥⎥⎥⎤
则
y ⃗ = A x ⃗ = A P [ a 1 a 2 ⋮ a n ] = [ A P 1 ⃗ A P 2 ⃗ A P 3 ⃗ … A P n ⃗ ] [ a 1 a 2 ⋮ a n ] = a 1 λ 1 P 1 ⃗ + a 2 λ 2 P 2 ⃗ + … + a n λ n P n ⃗ \vec{y}=A\vec{x}=AP \left[\begin{matrix} a_1\\a_2\\ \vdots \\ a_n \end{matrix} \right] \\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ = [A\vec{P_1} \ A\vec{P_2}\ A\vec{P_3} … A\vec{P_n}]\left[\begin{matrix} a_1\\a_2\\ \vdots \\ a_n \end{matrix} \right] \\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ =a_1\lambda_1\vec{P_1}+a_2\lambda_2\vec{P_2}+…+a_n\lambda_n\vec{P_n} y=Ax=AP⎣⎢⎢⎢⎡a1a2⋮an⎦⎥⎥⎥⎤ =[AP1 AP2 AP3…APn]⎣⎢⎢⎢⎡a1a2⋮an⎦⎥⎥⎥⎤ =a1λ1P1+a2λ2P2+…+anλnPn
可以看到矩阵A的作用就是在以P为基的情况下,将在各个P轴分量上的值拉伸 λ \lambda λ倍。
例子
假设
A
=
[
3
0
0
1
]
,
x
⃗
=
[
1
1
]
T
A=\left[\begin{matrix}3&0\\0&1 \end{matrix} \right],\vec{x}=[1 \ 1]^T
A=[3001],x=[1 1]T
则
P
1
=
[
1
0
]
T
,
λ
1
=
3
P_1=[1\ 0]^T,\lambda_1=3
P1=[1 0]T,λ1=3
P
2
=
[
0
1
]
T
,
λ
2
=
1
P_2=[0\ 1]^T,\lambda_2=1
P2=[0 1]T,λ2=1,
此时的P=E,则正好在E的基底下,就是在P的基底下,则
y
⃗
=
A
x
⃗
\vec{y}=A\vec{x}
y=Ax得到的结果就是将在
[
1
0
]
T
[1\ 0]^T
[1 0]T分量上的值即x拉伸
λ
1
=
3
\lambda_1=3
λ1=3倍;在
[
0
1
]
T
[0\ 1]^T
[0 1]T分量上的值即y拉伸
λ
2
=
1
\lambda_2=1
λ2=1倍,即
y
⃗
=
[
3
1
]
T
\vec{y}=[3 \ 1]^T
y=[3 1]T,可以直接运算进行验证。
在图片上表示则是
特征分解
由之前的推导可以看到
矩阵A的作用与在各个特征向量方向上进行特征值(
λ
\lambda
λ)倍拉伸的作用是一样的。
既然如此,我们在进行运算
y
⃗
=
A
x
⃗
\vec{y}=A\vec{x}
y=Ax可以将矩阵A等价于三部分的功能
- 将 x ⃗ \vec{x} x从I的基底下,表示成在P的基底下。
- 将已经转换成P基底的 x p ⃗ \vec{x_p} xp的各个分量进行 λ \lambda λ倍的拉伸。
- 将拉伸后的 x p ⃗ \vec{x_p} xp转换回I的基底下 x ⃗ \vec{x} x。
特征分解做的正是以上的三件事
先来看下怎么换基底
x ⃗ = [ x 1 x 2 ⋮ x n ] = I x ⃗ = a 1 P 1 ⃗ + a 2 P 2 ⃗ + … + a n P n ⃗ = P [ a 1 a 2 ⋮ a n ] \vec{x}=\left[\begin{matrix} x_1\\x_2\\ \vdots \\x_n \end{matrix} \right]=I\vec{x}=a1\vec{P_1}+a2\vec{P_2}+…+an\vec{P_n}=P \left[\begin{matrix} a_1\\a_2\\ \vdots \\a_n \end{matrix} \right] x=⎣⎢⎢⎢⎡x1x2⋮xn⎦⎥⎥⎥⎤=Ix=a1P1+a2P2+…+anPn=P⎣⎢⎢⎢⎡a1a2⋮an⎦⎥⎥⎥⎤
我们的目标就是将 x ⃗ = [ x 1 x 2 ⋮ x n ] \vec{x}=\left[\begin{matrix} x_1\\x_2\\ \vdots \\x_n \end{matrix} \right] x=⎣⎢⎢⎢⎡x1x2⋮xn⎦⎥⎥⎥⎤转换成 x p ⃗ = [ a 1 a 2 ⋮ a n ] \vec{x_p}=\left[\begin{matrix} a_1\\a_2\\ \vdots \\a_n \end{matrix} \right] xp=⎣⎢⎢⎢⎡a1a2⋮an⎦⎥⎥⎥⎤
我们先逆过来讨论,怎么将 x p ⃗ \vec{x_p} xp转化成 x ⃗ \vec{x} x,再上面的推导中,可以看到 x ⃗ = P [ a 1 a 2 ⋮ a n ] = P x p ⃗ \vec{x}=P \left[\begin{matrix} a_1\\a_2\\ \vdots \\a_n \end{matrix} \right]=P\vec{x_p} x=P⎣⎢⎢⎢⎡a1a2⋮an⎦⎥⎥⎥⎤=Pxp
只要将P乘上 x p ⃗ \vec{x_p} xp即可以得到 x ⃗ \vec{x} x,即 x ⃗ = P x p ⃗ \vec{x}=P\vec{x_p} x=Pxp
那我们要得到 x p ⃗ \vec{x_p} xp只需要两边同时乘上 P − 1 P^{-1} P−1,即 x p ⃗ = P − 1 x ⃗ \vec{x_p}=P^{-1}\vec{x} xp=P−1x
那么第一步的操作便是等价于
P
−
1
P^{-1}
P−1,第三步的操作便是等价于
P
P
P
接下来看第二步的拉伸操作
[ a 1 λ 1 a 2 λ 2 ⋮ a n λ n ] = [ λ 1 ⋱ λ n ] [ a 1 a 2 ⋮ a n ] = Λ [ a 1 a 2 ⋮ a n ] \left[\begin{matrix} a_1\lambda_1\\ a_2\lambda_2\\ \vdots \\ a_n\lambda_n \end{matrix} \right] =\left[ \begin{matrix} \lambda_1 & & \\ & \ddots & \\ && \lambda_n \end{matrix} \right]\left[ \begin{matrix} a_1 \\a_2 \\ \vdots \\a_n \end{matrix} \right]=\Lambda \left[\begin{matrix} a_1 \\a_2 \\ \vdots \\a_n \end{matrix} \right] ⎣⎢⎢⎢⎡a1λ1a2λ2⋮anλn⎦⎥⎥⎥⎤=⎣⎡λ1⋱λn⎦⎤⎣⎢⎢⎢⎡a1a2⋮an⎦⎥⎥⎥⎤=Λ⎣⎢⎢⎢⎡a1a2⋮an⎦⎥⎥⎥⎤
即第二步的拉伸操作等价于
Λ
\Lambda
Λ
将以上的三步组合起来,即可以得到特征分解的公式,即
A
=
P
Λ
P
−
1
A=P\Lambda P^{-1}
A=PΛP−1
注意:
1.不是所有的矩阵都可以对角化
2.实对称阵都可以对角化,并且P是正交矩阵,此时公式可以简化为
A
=
P
Λ
P
T
A=P\Lambda P^{T}
A=PΛPT
3.可以对角化的情况下,P不一定是正交矩阵
接下来看一下对图片使用特征分解的效果
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main(int argc, char const* argv[])
{
if (argc != 2) {
cout << "error" << endl
<< "./matrixFeature pic" << endl;
return -1;
}
Mat pic = imread(argv[1], CV_LOAD_IMAGE_GRAYSCALE);
pic.convertTo(pic, CV_64FC1, 1 / 255.0);
resize(pic, pic, cv::Size(pic.rows, pic.rows));
Mat eValuteM;
Mat eVectorsInvM;
Mat eVectorsM;
eigen(pic, eValuteM, eVectorsM);
eVectorsM = eVectorsM.t();
eVectorsInvM = eVectorsM.t(); //如果不是正交矩阵的话,要用inv
Mat diag(eValuteM.rows, eValuteM.rows, CV_64FC1);
diag = 0;
for (int i = 0; i < diag.rows; i++) {
diag.at<double>(i, i) = eValuteM.at<double>(i, 0);
}
Mat res;
res = eVectorsM * diag * eVectorsInvM;
imshow("the spirit of strom", pic);
imshow("evd", res);
waitKey(0);
}
原图
由于图片不是实对称矩阵,使用eigen函数,只能接受对称矩阵,强行使用的结果是得到一张对关于对角线对称的图片
opencv提供了另一个函数eigenNonSymmetric可以对非对称矩阵求特征向量,但是有特征向量不一定满足对角化的要求,如果强行进行evd分解再重构,效果是这样的
图片明显效果不好,但是有些部分还是可以看出来和原图有些地方是一样的。
关于特征分解的推广(SVD分解可以看我的另一篇文章)
奇异值分解的理解与oepncv代码
参考连接:
如何理解特征值和特征向量