最小二乘法,相信大家都不陌生,统计学中很是常见,而且其理论相对简单,用途也很广泛。今天小编就给大家具体介绍一下最小二乘法。
一、最小二乘概念
最小二乘,或者也可以叫做最小平方和,它目的就是通过最小化误差的平方和,使得拟合对象无限接近目标对象。也就意味着,最小二乘法可以用于对函数的拟合。
最小二乘法是勒让德( A. M. Legendre)于1805年在其著作《计算慧星轨道的新方法》中提出的。
在线性回归中,最小二乘法就是试图找到一条直线,使所有样本到直线的欧氏距离之和最小。更直观的解释:
假设有一条直线y=ax+b,要在这条直线上找到一点,距离(x0.y0)这个点的距离最短。如果用绝对值的方法寻找,也就是取min(|y−y0|+|x−x0|),由于绝对值最小为0.所以最小的情况就是x=x0或者y=y0处。
如果用平方和的方法寻找,就是取min(y−y0)2+(x−x0)2.可以看出该式是两点间距离公式,也就是距离的概念。那么最短的距离,就是点到直线的垂线。
二、最小二乘核心思想
最小二乘的主要思想就是求解未知参数,使得理论值与观测值之差(即误差,或者说残差)的平方和达到最小:
三、直线拟合/多元线性回归
求导计算最小值是通用解法,但矩阵法比代数法要简洁,且矩阵运算可以取代循环,所以现在很多书和机器学习库都是用的矩阵法来做最小二乘法。
故损失函数定义为:(系数1/2是为了简化计算添加的,求迹前和求迹后值不变)
应用矩阵迹的计算公式:
四、最小二乘法的适用场景
当样本量m很少,小于特征数n的时候,这时拟合方程是欠定的,需要使用LASSO。当m=n时,用方程组求解。当m>n时,拟合方程是超定的,可以使用最小二乘法。
但是同时最小二乘也具有局限性:
1.最小二乘法需要计算(XTX)−1逆矩阵,有可能逆矩阵不存在,这样就没有办法直接用最小二乘法。
2.如果是样本特征n非常的大的情况,计算逆矩阵是一个极为耗时的工作,甚至是不可行,通常不超过10000个特征。
3.若拟合函数不是线性的,则无法使用最小二乘法,这时就需要通过一些技巧转化为线性才能使用。
五、最小二乘实现
/*
最小二乘法的实现
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;
}