最小二乘法
最小二乘法是一种优化方法。通过最小化误差的平方和来寻找数据的最佳函数进行匹配。
线性函数模型:
线性函数模型:
Y
=
B
^
0
+
B
^
1
X
Y = \hat B_0 + \hat B_1 X
Y=B^0+B^1X
残差形式写为:
Y
i
=
B
^
0
+
B
^
1
X
1
+
e
i
Y_i = \hat B_0 + \hat B_1 X_1+e_i
Yi=B^0+B^1X1+ei
可将
e
i
e_i
ei写为
e
i
=
Y
i
−
B
^
0
−
B
^
1
X
1
e_i=Y_i -\hat B_0 - \hat B_1 X_1
ei=Yi−B^0−B^1X1
e_i为样本
(
x
i
,
y
i
)
(x_i,y_i)
(xi,yi)的误差。
所以,平方损失函数可以表示为
Q
=
∑
i
=
1
n
e
i
2
=
∑
i
=
1
n
(
Y
i
−
Y
^
i
)
2
=
∑
i
=
1
n
(
Y
i
−
B
^
0
−
B
^
1
X
i
)
Q=\sum_{i=1}^{n}e_i^2=\sum_{i=1}^{n}(Y_i-\hat Y_i)^2=\sum_{i=1}^{n}(Y_i-\hat B_0- \hat B_1 X_i)
Q=i=1∑nei2=i=1∑n(Yi−Y^i)2=i=1∑n(Yi−B^0−B^1Xi)
即,使Q最小确定直线,Q可看作是以
B
^
0
\hat B_0
B^0,
B
^
1
\hat B_1
B^1为变量的Q的函数。
问题转换成一个极值问题:
再对Q求偏导。
{
∂
Q
∂
B
^
0
=
2
∑
i
=
1
n
(
Y
i
−
B
^
0
−
B
^
1
X
i
)
∗
(
−
1
)
=
0
∂
Q
∂
B
^
1
=
2
∑
i
=
1
n
(
Y
i
−
B
^
0
−
B
^
1
X
i
)
∗
(
−
X
i
)
=
0
\begin{cases} \frac {\partial Q}{\partial \hat B_0} =2\sum_{i=1}^{n}(Y_i-\hat B_0- \hat B_1 X_i)*(-1)=0\\ \frac {\partial Q}{\partial \hat B_1} =2\sum_{i=1}^{n}(Y_i-\hat B_0- \hat B_1 X_i)*(-X_i)=0\end{cases}
{∂B^0∂Q=2∑i=1n(Yi−B^0−B^1Xi)∗(−1)=0∂B^1∂Q=2∑i=1n(Yi−B^0−B^1Xi)∗(−Xi)=0
解得
{
B
^
0
=
∑
X
i
2
∑
Y
i
−
∑
X
i
∑
X
i
Y
i
n
∑
X
i
2
−
(
∑
X
i
)
2
B
^
1
=
n
∑
X
i
Y
i
−
∑
X
i
∑
Y
i
n
∑
X
i
2
−
(
∑
X
i
)
2
\begin{cases} \hat B_0 = \frac {\sum X_i^2 \sum Y_i-\sum X_i \sum X_i Y_i}{n \sum X_i^2-(\sum X_i)^2}\\\hat B_1= \frac {n\sum X_i Y_i-\sum X_i \sum Y_i}{n \sum X_i^2-(\sum X_i)^2} \end{cases}
⎩⎨⎧B^0=n∑Xi2−(∑Xi)2∑Xi2∑Yi−∑Xi∑XiYiB^1=n∑Xi2−(∑Xi)2n∑XiYi−∑Xi∑Yi
矩阵表达形式
Y = B ^ 0 + B ^ 1 X Y = \hat B_0 + \hat B_1 X Y=B^0+B^1X
推广到一般情况下,假如有更多的模型变量
x
1
,
x
2
,
.
.
.
,
x
m
x^1,x^2,...,x^m
x1,x2,...,xm(指样本里的模型相关的变量),可以用线性函数表示如下:
y
(
x
1
,
.
.
.
,
x
m
;
B
0
,
B
1
,
.
.
.
,
B
m
)
=
B
0
+
B
1
x
1
+
.
.
.
+
B
m
x
m
y(x^1,...,x^m;B_0,B_1,...,B_m)=B_0+B_1 x^1+...+B_m x^m
y(x1,...,xm;B0,B1,...,Bm)=B0+B1x1+...+Bmxm
对于n个样本来说,可以用如下线性方程组表示:
B 0 + B 1 x 1 1 + . . . + B m x 1 m = y 1 B_0+B_1 x_1^1+...+B_m x_1^m=y_1 B0+B1x11+...+Bmx1m=y1
B 0 + B 1 x 2 1 + . . . + B m x 2 m = y 2 B_0+B_1 x_2^1+...+B_m x_2^m=y_2 B0+B1x21+...+Bmx2m=y2
. . . ... ...
B 0 + B 1 x i 1 + . . . + B m x i m = y i B_0+B_1 x_i^1+...+B_m x_i^m=y_i B0+B1xi1+...+Bmxim=yi
. . . ... ...
B 0 + B 1 x n 1 + . . . + B m x n m = y n B_0+B_1 x_n^1+...+B_m x_n^m=y_n B0+B1xn1+...+Bmxnm=yn
将上式记为矩阵形式为:
[
1
x
1
1
.
.
.
x
1
m
1
x
2
1
.
.
.
x
2
m
.
.
.
.
.
.
.
.
.
.
.
.
1
x
n
1
.
.
.
x
n
m
]
[
B
0
B
1
.
.
.
B
m
]
=
[
y
y
.
.
.
y
n
]
\left[ \begin{matrix} 1 & x_1^1 & ... & x_1^m \\ 1 & x_2^1 & ... & x_2^m \\ ... & ... & ... & ...\\ 1 & x_n^1 & ... & x_n^m \end{matrix} \right] \left[ \begin{matrix} B_0 \\B_1 \\... \\B_m \end{matrix} \right]= \left[ \begin{matrix} y\\y\\...\\y_n \end{matrix} \right]
⎣⎢⎢⎡11...1x11x21...xn1............x1mx2m...xnm⎦⎥⎥⎤⎣⎢⎢⎡B0B1...Bm⎦⎥⎥⎤=⎣⎢⎢⎡yy...yn⎦⎥⎥⎤
最终形式为
A
⋅
B
=
Y
A·B=Y
A⋅B=Y
最小二乘形式,可以表示为: m i n ∣ ∣ A B − Y ∣ ∣ 2 min||AB-Y||_2 min∣∣AB−Y∣∣2
最优解为: B ^ = ( A T A ) − 1 A T Y \hat B=(A^TA)^{-1}A^TY B^=(ATA)−1ATY
代码
/*
最小二乘法的实现
C++版
命令行输入数据文件
最后输入x得到预测的y值
*/
#include<iostream>
#include<fstream>
#include<vector>
using namespace std;
class LeastSquare {
double b0, b1;
public:
LeastSquare(const vector<double>& x, const vector<double>& y)
{
double t1 = 0, t2 = 0, t3 = 0, t4 = 0;
for (int i = 0; i<x.size(); ++i)
{
t1 += x[i] * x[i];
t2 += x[i];
t3 += x[i] * y[i];
t4 += y[i];
}
b0 = (t1*t4 - t2*t3) / (t1*x.size() - t2*t2); // 求得 B0
b1 = (t3*x.size() - t2*t4) / (t1*x.size() - t2*t2); // 求得 B1
}
double getY(const double x) const
{
return b0+b1*x;
}
void print() const
{
if (b1>=0)
cout << "y = " << b0 << "+" << b1 << 'x' << "\n";
else
cout << "y = " << b0 << "" << b1 << 'x' << "\n";
}
};
int main(int argc, char *argv[])
{
if (argc != 2)
{
cout << " data.txt don't exit " << endl;
return -1;
}
else
{
vector<double> x;
vector<double> y;
int count = 1;
ifstream in(argv[1]);
for (double d; in >> d; count++)
if (count % 2 == 1)
x.push_back(d);
else
y.push_back(d);
LeastSquare ls(x, y);
ls.print();
cout << "Input x:\n";
double x0;
while (cin >> x0)
{
cout << "y = " << ls.getY(x0) << endl;
cout << "Input x:\n";
}
}
int endline;
cin >> endline;
}
int main(int argc,char* argv[])
argc是命令行总的参数个数,argv[]是argc个参数,其中第0个参数是程序的全名,以后的参数命令行后面跟的用户输入的参数 比如:
int main(int argc, char* argv[])
{
}
两种方法:
第一种,无需调试的情况:
直接用dos命令进入到.exe目录下然后输入:*.exe pra1 pra2
第二种,需要调试的情况:
-
先选择项目-〉右键-〉属性
-
调试 -〉命令行参数
在命令行参数里面输入命令行参数即可。
需要注意的是,不需要像第一种那样样输入***.exe了。只需要输入 pra1 pra2 ,中间用空格隔开。例如:以上实现代码,需要输入一个data.txt,输入格式是(x,y)的点值。
1 0
2 1
3 2
0 1
1 2
2 3