最小二乘法及算法实现

最小二乘法

最小二乘法是一种优化方法。通过最小化误差的平方和来寻找数据的最佳函数进行匹配。

线性函数模型:

线性函数模型:
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=YiB^0B^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=1nei2=i=1n(YiY^i)2=i=1n(YiB^0B^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^0Q=2i=1n(YiB^0B^1Xi)(1)=0B^1Q=2i=1n(YiB^0B^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=nXi2(Xi)2Xi2YiXiXiYiB^1=nXi2(Xi)2nXiYiXiYi

矩阵表达形式

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...xnmB0B1...Bm=yy...yn
最终形式为 A ⋅ B = Y A·B=Y AB=Y

最小二乘形式,可以表示为: m i n ∣ ∣ A B − Y ∣ ∣ 2 min||AB-Y||_2 minABY2

最优解为: 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
第二种,需要调试的情况:

  1. 先选择项目-〉右键-〉属性

  2. 调试 -〉命令行参数

    在命令行参数里面输入命令行参数即可。
    需要注意的是,不需要像第一种那样样输入***.exe了。只需要输入 pra1 pra2 ,中间用空格隔开。

    例如:以上实现代码,需要输入一个data.txt,输入格式是(x,y)的点值。
    1 0
    2 1
    3 2
    0 1
    1 2
    2 3

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值