简介
简单介绍了最小二乘法原理,以直线拟合为例结合原理出发推导了拟合算法过程,最后c++实现拟合算法。
最小二乘法
最小二乘法(英文:least square method)是一种常用的数学优化方法,所谓二乘就是平方的意思。这平方一词指的是在拟合一个函数的时候,通过最小化误差的平方来确定最佳的匹配函数,所以最小二乘、最小平方指的就是拟合的误差平方达到最小。
推导过程
以直线拟合为例,已知有一组平面上的点集( x 1 , y 1 ) , ( x 2 , y 2 ) , … ( x n , y n ) (x_1, y_1), (x_2,y_2),…(x_n,y_n) ( x 1 , y 1 ) , ( x 2 , y 2 ) , … ( x n , y n ) 。基于这些点拟合一条直线,设直线方程为
y = a x + b y = ax + b y = a x + b
那么那么算法的输入就是这些点集,需要求取的为直线方程的参数a,b。
(1)平方偏差之和为
S ϵ 2 = ∑ i = 1 n ( y i − y ) 2 S_{\epsilon^2} = \sum_{i = 1}^{n}{(y_i - y)}^2 S ϵ 2 = i = 1 ∑ n ( y i − y ) 2
= ∑ i = 1 n ( y i − ( a x i + b ) ) 2 = \sum_{i = 1}^{n}{(y_i - (ax_i + b))}^2 = i = 1 ∑ n ( y i − ( a x i + b )) 2
那么,以上公式已知的是x i , y i x_i, y_i x i , y i , 未知且要求取的是a、b。不同的a、b会得到不同的S ϵ 2 S_{\epsilon^2} S ϵ 2 ,求取的是在S ϵ 2 S_{\epsilon^2} S ϵ 2 最小的时候求取a、b。这是一个二元(a,b)函数,此问题实际上就是多元函数的极值与最值问题,要求解函数极值时二元变量数值,这里要用到二元函数取极值的必要条件
设 z = f ( x , y ) 在点 ( x 0 , y 0 ) { 一阶偏导数存在 取极值 设z = f(x,y)在点(x_0, y_0) \begin{cases} \text{一阶偏导数存在} \ \text{取极值}\end{cases} 设 z = f ( x , y ) 在点 ( x 0 , y 0 ) { 一阶偏导数存在 取极值
则
f x , ( x 0 , y 0 ) = 0 f_x^,(x_0,y_0)=0 f x , ( x 0 , y 0 ) = 0
以及
f y , ( x 0 , y 0 ) = 0 f_y^,(x_0,y_0)=0 f y , ( x 0 , y 0 ) = 0
那么对 S ϵ 2 S_{\epsilon^2} S ϵ 2 求偏导且使得偏导为0,此时 S ϵ 2 S_{\epsilon^2} S ϵ 2 取得极值点最小值
∂ ∂ a S ϵ 2 = ∑ i = 1 n 2 ( y i − ( a x i + b ) ( − x i ) ) = 0 \frac{\partial}{\partial{a}}{S_{\epsilon^2}} = \sum_{i=1}^n{2(y_i -(ax_i + b)(-x_i))=0} ∂ a ∂ S ϵ 2 = i = 1 ∑ n 2 ( y i − ( a x i + b ) ( − x i )) = 0
= ∑ i = 1 n ( a x i 2 + b x i − y i x i ) = 0 = \sum_{i=1}^n{(ax_i^2 + bx_i - y_ix_i)=0} = i = 1 ∑ n ( a x i 2 + b x i − y i x i ) = 0
= > ( ∑ i = 1 n x i 2 ) a + ( ∑ i = 1 n x i ) b = ∑ i = 1 n y i x i => (\sum_{i=1}^n{x_i^2})a + (\sum_{i=1}^n x_i)b = \sum_{i=1}^n{y_ix_i} => ( i = 1 ∑ n x i 2 ) a + ( i = 1 ∑ n x i ) b = i = 1 ∑ n y i x i
∂ ∂ b S ϵ 2 = ∑ i = 1 n 2 ( y i − ( a x i + b ) ( − 1 ) = 0 \frac{\partial}{\partial{b}}S_{\epsilon^2} = \sum_{i=1}^n2(y_i -(ax_i + b)(-1) = 0 ∂ b ∂ S ϵ 2 = i = 1 ∑ n 2 ( y i − ( a x i + b ) ( − 1 ) = 0
= ∑ i = 1 n ( a x i + b − y i ) = 0 = \sum_{i=1}^n(ax_i + b - y_i) = 0 = i = 1 ∑ n ( a x i + b − y i ) = 0
= > ( ∑ i = 1 n x i ) a + n b = ∑ i = 1 n y i => (\sum_{i=1}^nx_i)a + nb = \sum_{i =1}^n y_i => ( i = 1 ∑ n x i ) a + nb = i = 1 ∑ n y i
联立两式可得方程组
{ ( ∑ i = 1 n x i 2 ) a + ( ∑ i = 1 n x i ) b = ∑ i = 1 n y i x i ( ∑ i = 1 n x i ) a + n b = ∑ i = 1 n y i \begin{cases}(\sum_{i=1}^n{x_i^2})a + (\sum_{i=1}^n x_i)b = \sum_{i=1}^n{y_ix_i} \ (\sum_{i=1}^nx_i)a + nb = \sum_{i =1}^n y_i\end{cases} { ( ∑ i = 1 n x i 2 ) a + ( ∑ i = 1 n x i ) b = ∑ i = 1 n y i x i ( ∑ i = 1 n x i ) a + nb = ∑ i = 1 n y i
求解方程组可得a、b的值。将方程组转为矩阵形式表示
( ∑ i = 1 n x i 2 ∑ i = 1 n x i ∑ i = 1 n x i n ) ( a b ) = ( ∑ i = 1 n y i x i ∑ i = 1 n y i ) {\begin{pmatrix} \sum_{i=1}^n{x_i^2} & \sum_{i=1}^n x_i \ \sum_{i=1}^nx_i & n \end{pmatrix}} {\begin{pmatrix} a \ b \end{pmatrix}} = \begin{pmatrix}{\sum_{i=1}^ny_ix_i} \ {\sum_{i=1}^ny_i} \end{pmatrix} ( ∑ i = 1 n x i 2 ∑ i = 1 n x i ∑ i = 1 n x i n ) ( a b ) = ( ∑ i = 1 n y i x i ∑ i = 1 n y i )
倘若给点集的每个点加上权重,方程组则变为
{ ( ∑ i = 1 n x i 2 w i ) a + ( ∑ i = 1 n x i w i ) b = ∑ i = 1 n y i x i w i ( ∑ i = 1 n x i w i ) a + ∑ i = 1 n w i = ∑ i = 1 n y i w i \begin{cases}(\sum_{i=1}^n{x_i^2}w_i)a + (\sum_{i=1}^n x_iw_i)b = \sum_{i=1}^n{y_ix_iw_i} \ (\sum_{i=1}^nx_iw_i)a + {\sum_{i=1}^n w_i} = \sum_{i =1}^n y_iw_i\end{cases} { ( ∑ i = 1 n x i 2 w i ) a + ( ∑ i = 1 n x i w i ) b = ∑ i = 1 n y i x i w i ( ∑ i = 1 n x i w i ) a + ∑ i = 1 n w i = ∑ i = 1 n y i w i
矩阵表示
( ∑ i = 1 n x i 2 w i ∑ i = 1 n x i w i ∑ i = 1 n x i w i ∑ i = 1 n w i ) ( a b ) = ( ∑ i = 1 n y i x i w i ∑ i = 1 n y i w i ) {\begin{pmatrix} \sum_{i=1}^n{x_i^2w_i} & \sum_{i=1}^n x_iw_i \ \sum_{i=1}^nx_iw_i & {\sum_{i=1}^n w_i} \end{pmatrix}} {\begin{pmatrix} a \ b \end{pmatrix}} = \begin{pmatrix}{\sum_{i=1}^ny_ix_iw_i} \ {\sum_{i=1}^ny_iw_i} \end{pmatrix} ( ∑ i = 1 n x i 2 w i ∑ i = 1 n x i w i ∑ i = 1 n x i w i ∑ i = 1 n w i ) ( a b ) = ( ∑ i = 1 n y i x i w i ∑ i = 1 n y i w i )
实现代码
实现思路
(1)输入点集( x 1 , y 1 ) , ( x 2 , y 2 ) , … ( x n , y n ) (x_1, y_1), (x_2,y_2),…(x_n,y_n) ( x 1 , y 1 ) , ( x 2 , y 2 ) , … ( x n , y n )
(2)根据点集数据和权重系数分别计算出
( ∑ i = 1 n x i 2 w i ∑ i = 1 n x i w i ∑ i = 1 n x i w i ∑ i = 1 n w i ) {\begin{pmatrix} \sum_{i=1}^n{x_i^2w_i} & \sum_{i=1}^n x_iw_i \ \sum_{i=1}^nx_iw_i & {\sum_{i=1}^n w_i} \end{pmatrix}} ( ∑ i = 1 n x i 2 w i ∑ i = 1 n x i w i ∑ i = 1 n x i w i ∑ i = 1 n w i )
以及
( ∑ i = 1 n y i x i w i ∑ i = 1 n y i w i ) \begin{pmatrix}{\sum_{i=1}^ny_ix_iw_i} \ {\sum_{i=1}^ny_iw_i} \end{pmatrix} ( ∑ i = 1 n y i x i w i ∑ i = 1 n y i w i )
矩阵。
(3)使用OpenCV解矩阵函数cv::solve()
计算出
( a b ) {\begin{pmatrix} a \ b \end{pmatrix}} ( a b )
下面放出函数实现,完整代码已托管在github,如果觉得有对你有所帮助,不妨给个star吧。
代码链接:https://github.com/mangosroom/machine-vision-algorithms-library/tree/master/src/linefit
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
void mv :: LineFit :: FitLineByRegression ()
{
// 设置权重 | weights setting
_weigths = std :: vector < double > ( _points . size (), 1 );
// AX = B
// 构造A矩阵 | Construct A mat
const int N = 2 ;
cv :: Mat A = cv :: Mat :: zeros ( N , N , CV_64FC1 );
for ( int row = 0 ; row < A . rows ; row ++ )
{
for ( int col = 0 ; col < A . cols ; col ++ )
{
for ( int k = 0 ; k < _points . size (); k ++ )
{
A . at < double > ( row , col ) = A . at < double > ( row , col ) + pow ( _points [ k ]. x , row + col ) * _weigths [ k ];
}
}
}
//构造B矩阵 | Construct B mat
cv :: Mat B = cv :: Mat :: zeros ( N , 1 , CV_64FC1 );
for ( int row = 0 ; row < B . rows ; row ++ )
{
for ( int k = 0 ; k < _points . size (); k ++ )
{
B . at < double > ( row , 0 ) = B . at < double > ( row , 0 ) + pow ( _points [ k ]. x , row ) * _points [ k ]. y * _weigths [ k ];
}
}
// 求解A*X = B | Solve the A*X = B
cv :: Mat X ;
cv :: solve ( A , B , X , cv :: DECOMP_LU );
// y = b + ax
_result . b = X . at < double > ( 0 , 0 );
_result . a = X . at < double > ( 1 , 0 );
} //FitLineByRegression
Copy Reference
【1】opencv学习——最小二乘法拟合直线-csdn
【2】最小二乘法本质是什么-知乎
本文由芒果浩明发布,转载请注明出处。
本文链接:https://blog.mangoeffect.net/opencv/least-square-method-line-fit.html