前言
在常见的算法问题使用中,线性回归使用的非常广泛,本篇文章来进行简单介绍。
正文
线性回归是一种统计分析方法,用于建模两个或多个变量之间的关系,通过寻找一个最佳拟合方程,来描述自变量与因变量之间的关系,目的是为了基于一个或者多个自变量的值来预测因变量的值。
线性回归与最小二乘法
很多人认为最小二乘法就是线性回归,其实并不是,最小二乘法是一种思想,它可以拟合任意函数,线性回归的线性方程只是其中一个比较简单且常用的函数,所以最小二乘法一般都是以它为例。
线性回归方程定义为:
其中就是因变量,就是自变量,而是回归系数,表示自变量对因变量的影响程度。
假设现在有m组数据(样本),每个样本有n-1维特征,将数据样本点带入线性回归方程就可以得到:
这里有m个方程,有n个参数需要求解,记住在juejin.cn/post/730387… 文章中有说,矩阵最初的雏形就是多元一次方程的简写,所以上述m个方程使用矩阵来表达就是:
这里我们与方程组进行比较一下,每一个方程,共有n个未知数,所以上述矩阵方程中,是一个mx1
的向量,代表模型的理论值;为nx1
维的矩阵,表示共有n个参数待求解;为mxn
的矩阵,表示数据,即m个样本个数,n表示特征数。
于是目标损失函数用矩阵表示就是:
其实这个就是最小二乘,当该方程值最小的时候,就是最佳拟合,至于为什么是平方,不是绝对值,也不是立方、次方等,这里涉及一个推到过程,比较复杂,就不说了。
在该方程中,是样本数据的实际值,是mx1
的矩阵。这里需要求函数极值,即倒数为0的地方,只需要对损失函数求导,并且使其等于0即可解出,首选对目标损失函数进行化简:
求导令其等于0:
求解上述方程可以得到:
看到这里你就应该非常高兴了,因为这里的与都是已知的,就可以一下子求出方程的参数。
直线拟合实践
有了这个矩阵公式以及最小二乘法的原理,我们来实践一下。假如我们有3个点,分别为(0,1.1)
、(1,1.9)
和(2,3)
,这3个点在坐标如图:
看到这种点,我们想用线性方程来进行拟合,根据最小二乘法公式,带入图中3个数据点:
我们可以看到这时的损失函数就成了和的二元方程,这时想得到一个值使得损失函数值最小,直接进行求导即可,因为二元方程开口向上,最小值就是倒数为0的地方,所以对和进行求导且设置倒数为0,可以得到:
解出上述方程,可以得到k=0.95,b=1.05
,带入拟合方程,效果如下:
当然这种做法很麻烦,我们可以利用前面刚学习的矩阵来试试,我们已知求解的系数的方程为:
其中就是把样本值带入到拟合方程的矩阵数据,是样本输出结果,所以可以得到:
我们使用Eigen库来进行计算,可以查看文章:juejin.cn/post/730384… ,代码如下:
//定义一个3x2的矩阵
Eigen::MatrixXf X(3, 2);
//定义一个向量
Eigen::Vector3f Y;
//对矩阵进行初始化
X << 0, 1,
1, 1,
2, 1;
Y << 1.1, 1.9, 3;
std::cout << "------ X ------" << std::endl << X << std::endl;
std::cout << "------ Y ------" << std::endl << Y << std::endl;
Eigen::MatrixXf kb = (X.transpose() * X).inverse() * (X.transpose()) * Y;
std::cout << "------ kb ------" << std::endl << kb << std::endl;
运行结果如下:
------ X ------
0 1
1 1
2 1
------ Y ------
1.1
1.9
3
------ kb ------
0.95
1.05
可以发现这里计算出的方程和前面手动计算是完全一样的。
二次多项式拟合实践
我们再来看一下二次多项式拟合的例子,假如有如下4个点:
这里我们就不使用手动计算了,我们期望的拟合方程是,我们把4组数据带入方程中,可以得到矩阵与:
同样使用Eigen来计算,代码如下:
//定义一个4x3的矩阵
Eigen::MatrixXf X(4, 3);
//定义一个向量
Eigen::Vector4f Y;
//对矩阵进行初始化
X << 0, 0, 1,
1, 1, 1,
4, 2, 1,
9, 3, 1;
Y << 2, 0, 3, 7;
std::cout << "------ X ------" << std::endl << X << std::endl;
std::cout << "------ Y ------" << std::endl << Y << std::endl;
Eigen::MatrixXf abc = (X.transpose() * X).inverse() * (X.transpose()) * Y;
std::cout << "------ abc ------" << std::endl << abc << std::endl;
结果如下:
------ X ------
0 0 1
1 1 1
4 2 1
9 3 1
------ Y ------
2
0
3
7
------ abc ------
1.5
-2.70001
1.8
拟合方程与原数据的匹配如下:
可以发现使用矩阵进行拟合还是非常方便的。
总结
这里通过2个简单的例子,我们可以理解最小二乘法用于线性拟合的实践,同时也可以感受到使用矩阵可以进一步简化表达,让人更容易理解。
更多算法相关总结移步专栏: juejin.cn/column/7415…