线性回归:机器学习的“Hello, World“

想要解决的问题

假设我们知道若干房子的面积以及对应的房价,那么如果随便告诉我们一套房子的面积,怎么才能知道这套房子最可能买多少钱呢?再假设我们知道了一些零件的使用时间和对应的维修费用,那么如果告诉我们一个零件的使用时间,我们要怎样推测出来对应的维修费用呢?没错,线性回归就是用来解决这些问题的,也就是通过拟合曲线来进行预测。

线性回归(Linear Regression)

线性回归大体分为三个部分,选择合适的模型(model),确定损失函数,梯度下降求解最优参数。

选择模型(Model)

首先我们要知道模型(model)是什么,在这个问题中,我们所说的model是指一个函数族。我们最终想要的结果,就是输入一个\(x\),得到一个十分可信的输出\(\hat{y}\),其实也就是找一个比较好的函数而已。问题是,这种函数可能有非常多,以单变量线性回归为例子,我们可以提出以下函数来进行拟合: \[ \begin{aligned} \hat{y} &= \theta_0 + \theta_1 x \\ \hat{y} &= \theta_0 + \theta_1 x + \theta_2 x^2\\ \hat{y} &= \theta_0 + \theta_1 x + \theta_2 x^2 + \theta_3 x^3\\ \vdots \end{aligned} \] 同样地,我们挑一个最简单的,也就是第一个函数。 \[ \hat{y} = \theta_0 + \theta_1 x \]

很显然,我们的最终目标就是找出\(\theta_0\)\(\theta_1\)使得这个函数足够准确。那么问题又来了,我们要怎么样判断这个函数是否足够准确呢?

损失函数(Loss function)

我们在说准确这个词的时候,实际上是在讨论什么?是误差。足够准确,也就是说误差足够小。损失函数(Loss function)就是用来描述这个误差的。如果要衡量误差,直接用预测值减去实际值就可以得出来了,如下: \[ \delta = \hat{y} - y \] 对于单个数据点我们当然可以这样做,那对于一堆数据点呢?一个很简单的想法就是直接将多个点的误差相加,问题是如果正的误差和负的误差相加起来抵消掉了怎么办。比如我们现在测量一个5cm的木块,第一次测出来是4.9cm,此时的误差就是-0.1cm,第二次测出来是5.1cm,此时的误差就是0.1cm。两次误差加起来就是0了,那岂不是相当于没有误差?那么现在聪明的你应该会想到,可以用绝对值。没错,这确实是一个可以选择的方式,然而,在实际应用中,带有绝对值的变量往往在求导等操作上会有一点麻烦。所以我们选用另一种方法,就是把这个数平方,即: \[ \delta = (\hat{y} - y)^2 \] 同样,上式也是对一个数据点的计算,如果有多个数据点,假如一共有\(m\)个,直接相加的话,得到的就是所有数据点的误差之和了,但是当数据点非常多时,这个数字就变得很大了,为了方便计算,我们在前面再除以一个数据点的数量,也就得到了我们的损失函数(Loss function): \[ L(\theta_0, \theta_1) = \frac{1}{m}\sum_{i=1}^m(\hat{y}^{(i)} - y)^2 \]\(\hat{y}^{(i)}\)里的上标\((i)\)表示第\(i\)个数据点) 好的,那么现在我们会发现数学符号逐渐多了起来,但是没有关系,我们把整个过程捋一捋。

还记得我们的model吗?就是下面这个: \[ \hat{y} = \theta_0 + \theta_1 x \] 我们的任务是什么,就是找到最合适的\(\theta_0\)\(\theta_1\),使得上式可以取得较好的预测结果。怎么评估这种合适程度呢,我们将会用Loss函数来描述。不断改变\(\theta_0\)\(\theta_1\)的值,利用手上已有的数据 \(\{ (x^{(1),} y^{(1)}), (x^{(2),} y^{(2)}), (x^{(3),} y^{(3)}), \dots, (x^{(m),} y^{(m)}) \}\)计算出Loss函数的值即可。那么什么时候才算是找到了最合适的\(\theta_0\)\(\theta_1\)呢,显然,就是Loss函数最小的时候。误差最小就代表最准确,这是非常明显的。

那么接下来,我们会介绍一种求解\(\theta_0\)\(\theta_1\)的最常用的方法,梯度下降法(Gradient Descent)。

梯度下降法(Gradient Descent)

经过高中数学的学习,想必大家都知道了导数的几何意义,在某一点的导数就等于过这一点切线的斜率,如下图所示:

显然,在\(x_1\)处的导数小于0,意味着在这个点的右边还有会使\(y\)更小的\(x\)值,暗示我们向右取值;相反地,在\(x_2\)处的导数大于0,意味着在这个点的左边有能使\(y\)更小的\(x\)值,暗示我们向左取值。所以就有如下的迭代策略: \[ \theta_j := \theta_j - \eta \frac{\partial}{\partial \theta_j}L(\theta_0, \theta_1) \ \ \ \ \ \ \ \ (j = 0, 1) \] 公式逐渐复杂了起来,不过问题不大,我们来分析一下各个参数的意义。

\(\theta_j\):就是代指\(\theta_0\)\(\theta_1\)

\(:=\)赋值的意思,让左边的数等于右边的数

\(\eta\):学习率(Learning Rate),把降低Loss函数的过程比喻成下山,那么学习率\(\eta\)就是下山的步长,步子跨大了容易扯着蛋,步子跨大了就可能一步走到另外一边的上坡路去了,但是步子小了就会走得很慢。

\(\frac{\partial}{\partial \theta_j}L(\theta_0, \theta_1)\):Loss函数对\(\theta_j\)的偏导,可以大概理解为其余变量为常数时,对单变量的导数。

总的来说,整个过程我们就是在变化\(\theta_0, \theta_1\),利用训练集(Training Data)来让我们的“假想函数”与实际的差距越来越小。

求导公式

我们这里把Loss函数的形式写完整: \[ L(\theta_0, \theta_1) = \sum_{i=1}^m(\hat{y}^{(i)} - y)^2 = \sum_{i=1}^m(\theta_0+\theta_1x^{(i)}-y^{(i)})^2 \] 那么两个参数的偏导数就如下所示: \[ \frac{\partial}{\partial \theta_0}L(\theta_0, \theta_1)=\frac{2}{m} \sum_{i=1}^m(\theta_0+\theta_1x^{(i)}-y^{(i)}) \]

\[ \frac{\partial}{\partial \theta_1}L(\theta_0, \theta_1)=\frac{2}{m} \sum_{i=1}^m(\theta_0+\theta_1x^{(i)}-y^{(i)}) \cdot x^{(i)} \]

拓展(幂次)

Loss函数构造的思想依然是一样的,只是我们选取的model发生变化了,当然,也就意味着偏导数的形式也发生变化了。假设我们一开始选取的model如下: \[ \hat{y} = \theta_0 + \theta_1 x + \theta_2 x^2 + \dots + \theta_n x^n \] 那么偏导数的形式变为: \[ \frac{\partial}{\partial \theta_0}L(\theta_0, \theta_1, \dots, \theta_n)=\frac{2}{m} \sum_{i=1}^m(\theta_0+\theta_1x^{(i)}+\dots + \theta_n x^{(i)n}-y^{(i)}) \]

\[ \frac{\partial}{\partial \theta_j}L(\theta_0, \theta_1, \dots, \theta_n)=\frac{2}{m} \sum_{i=1}^m(\theta_0+\theta_1x^{(i)}+\dots + \theta_n x^{(i)n}-y^{(i)})\cdot x^{(i)j} \]

其中\(j\)\(1, 2, \dots, n\).