08. 机器学习 - 线性回归
[TOC]
从本次课程开始,大部分时候我将不再将打印结果贴出来了,因为太占用篇幅。小伙伴可以根据我的输出执行敲一遍代码来进行学习和验证。 同样是为了节省篇幅,我也不会再一行行那么仔细的解释代码了,一般只会告诉你我代码做了什么。其中的逻辑关系和关键词,小伙伴们自行好好的琢磨一下。
Hi, 你好。我是茶桁。
前几节课咱们主要是了解到了什么是人工智能,整个机器学习的工作路径等等。不过可以说,前面的几节课,从机器学习导论到上一节课介绍 K-means。
咱们快速回顾一下,前几节课里的内容都有什么。之前咱们学习了什么是优化问题,然后还有什么是动态规划,什么是机器学习问题,以及监督学习和非监督学习的区别,我们还学习了一个非常著名的非监督学习方法:K-means。
这些都还是一个铺垫。真正的内容,从这节课才算事正式开始。
从这一节课开始,我会给大家开始系统的学习监督学习,监督学习其实内容比较多,我们可能需要多花多一点时间。
在最开始,我还是要更大家强调一下上一节课上更大家讲的问题:要学习算法,不仅仅是要学习很多很多的这个算法模型,更重要的是什么要能够把问题抽象成一个一个的算法。工作场景中的问题并不能使用一个单独的算法模块能够解决。
机器学习算法只是人工智能其中的一个部分,或者说人工智能的某个部分,比方说取它的特征等等。它的某一个部分用在真正的工作中,用在项目中是很杂揉、很混合的一个状态。
咱们篇幅短内容多,密度比较大,短短几节课,可能是大家研究生课程一个月时间的内容。可以说,咱们课程还是有点难,信息量也比较大。
OK,开始第一个问题。
线性回归 LINEAR REGRESSION
咱们第一个要讲的,也是非常重要的一个方法,就是线性回归。
线性回归非常的简单,也非常的基础。但是它作为我们整个人工智能,整个深度学习中要讲的第一课,里面蕴含了非常多的机器学习的基本思想。所以大家一定要把它学清楚。如果能把它学好,其实对于咱们以后学习帮助非常大。
咱们来看一下,什么是线性回归。
在生活中有很多这样的问题,比方说咱们的血糖,往往在吃饭的时候,吃的糖分、碳水化合物等比较多,饭后的半个小时、一个小时内血糖会更高。
还有一种情况,抽烟抽的越多的人,得肺病的概率往往会越高。并不是一定说抽烟抽的多的人就一定会生病,但是抽烟抽的多的人得肺病的概率往往会越高。
那么还有一种情况,比方在咱们工作的时候,随着工作时间的增加,收入往往也会越来越多。尤其是在日本,是一个非常典型的情况。在日本基本上一个人的收入和他的工作年限是最相关的。他们在一个固定的时间内的薪资变化不会非常大。
比方说都是 29 岁、都是 32 岁、都是 35 岁,假如都在同一家公司,那么薪资待遇也会很接近。
往往这个其实反映的是我们现实生活中一个非常基本的一个关系,随着有一些值的增大另外一些值也随着变化。
假如说我们把它变成自变量和因变量,所谓的自变量就是它的变化会引起因变量的变化。也就是说在现实生活中,我们最基本的关系是随着一个变量的变化另外一个变量要么增加,要么减小。当然不变可以算是一种特殊情况,就是变化为 0。
那么吃糖的多少、抽烟和工作年限就是整个自变量和因变量的一种关系。现在希望让机器来找到这个关系。
如果把这种关系画出来的话,我们用一个图表画出来就会发现其相关性。
就比如下方这三张图表:
这三个图是非常非常典型的,随着一个量的变化另外的一个量要么减少,要么增加。
这个时候工程师就希望我们能够对现实生活中这种随着一个变量的增加或减少导致另外一个量增加或减小,能够找到一种关系来刻画。
当然之间的这种关系可能会很多,可以是一种线性结构,y = kx+b, 也可以是 其他的什么结构。最简单的一种其实就是线性关系。 \[ \begin{align*} \vec {x} = [x_0, x_1, x_2, ..., x_n] \\ f(x) = \sum_{i \in N} w_i \times x_i +b \end{align*} \] 我们把这种关系称为是线性关系。
为什么称为线性关系呢?假设 x 现在是一维空间,\(x \in R^1\), 那么 f(x) 就是一条直线。如果 x 是二维空间的话,\(x\in R^2\), f(x) 就是一个平面。这种关系其实是自然界中最简单的一种关系。
除了这种关系之外,你还可以想象一下,如果我们要刻画 x 和 f(x) 之间的关系,你还能想到哪些函数呢?
比如咱们可以有:
- 二次函数 \(f(x) = ax^2 + bx + c\),
- 三角函数\(f(x) = sin(x)\),
- 幂函数\(f(x) = a^x\),
- 反函数\(f(x) = tanh(x)\),
- 对数函数\(f(x) = log(x)\),
咱们整个来了个数学回顾。这个时候你会发现好像有非常多种函数,甚至我们还可以在这个基础之上做一些复杂的函数,比如说$f(x) = x{bx3+cx2+dlogx}+elog^x_m $。
理论上,我们可以做有无数种可能的关系。
其实就是如何找到因变量和自变量之间的关系,长久以来,这其实是我们整个自然科学界一直在思考探索的一个问题。
像牛顿,笛卡尔、爱因斯坦、波尔这些人其实都是在研究这件事情。当观察到了很多事情,然后期望用一种函数关系能够把它来表证出来。
后来,在 14 世纪一个非常著名的哲学家就提出来了这样的一个理论,叫做奥卡姆剃刀原理。奥卡姆剃刀原理说的是对于一件事情,你要解释它的关系的话,最简单的:
The explanation requiring the fewest assumptions is most likely to be correct.
这个 fewest assumptions 指的是什么意思?就是最少的假设,你可以把它理解成是几个假设,假如对应到函数上,就有很多变量。
举个例子,你们单位上有一个同事经常迟到,有三个人对这个同事为什么迟到有不同的说法。
第一个人说这个同事迟到大概率是因为他前一天晚上吃坏了肚子,导致一晚上没睡好,一大早还因为拉肚子迟到了。
第二个人说同事昨天和一个男生出去了,可能玩太晚导致没起来。
第三个说这个同事可能对昨天法的工资有抱怨,去找领导议论没有得到结果,昨天找男朋友安慰了。今天也是因为赌气故意迟到。
那么对于一个女孩子第二天早上迟到,人们就有 3 种不同的说法。那么大家仔细思考一下,对于你来说,你要相信一个的话,在我们日常生活中你会发现把一件事情想的越复杂往往就错了。
因为比方说第三个条件的,首先昨天发没发工资是一种可能性,然后发了工资她有没有去找老板?找了老板老板有没有怼她?就算怼了她,那她有没有找男朋友?这一系列下来你会发现这个事情如果把它变得因素很多,你对这个事情的估计可能会更错误。
奥卡姆就提出:若无必要,勿增实体。如果对于同一现象有几种不同的假说,我们应该采取最简单的哪一种。那么在我们抽象的函数上,拟合也是这样,我们要拟合一种关系,一种最简单的关系其实往往是最有用的。
比方说在这种关系上:
在这种关系上,你可以说他是一条直线。
比方说我们看到绿色这条线,比较弯,可以说这条线就是这样的一个函数。但是最简单的一种方法,假设它就是一条直线,就像红色这条线。
我们如果把绿色这条定义为 a,把红色这条定义为 b。a 好像拟合的程度更高一些,b 其实是做了一个特别简单的假设,它假设就是一个简单的线性关系。线性关系就是随着一个变量的增多,另外一个也成比例的增多或者减小。
a 看起来更复杂,但 b 在整个状态上看更稳定。a 这个函数在没有看到的地方,其实按照趋势就有可能差的特别大。而 b 虽然在观察到的地方有一些差别,但是因为它这个模型很简单,假设很简单,所以在这些没有观测到的地方你会发现还是和我们整体的趋势会比较接近。
a 复杂,在做函数拟合的时候,不管这个关系看起来有多复杂,先假设它是最简单的一种线性关系。当线性关系实在不好的时候,再把它变复杂。
这就是为什么学习机器学习监督式学习要先学习线性拟合的原因。对简单的假设不一定对,但是除非这个简单的假设不行,否则我们就不要给他更复杂的假设。
比方说下面这个图:
这四张图中,都可以用一个直线去拟合。当然有的时候,比如说 (x2,y2), 当它数据量很多的时候用一个直线效果就会不太好,包括 (x4, y4) 效果可能也不会太好。
在这个时候,当我们发现它效果很差的时候,再去给他换一个模型。那像 (x1,y1),还有 (x3, y3),还有如下图这种:
这些其实都可以用一种线性关系来拟合。所谓的线性拟合,就是把函数写成自变量 x 和它的权重相乘相加,然后再加上一个 b。就是我们刚才所描述的:\(f(x) = \sum_{i\in N} w_i \times x_i + b\);这种形式。这个就是我们的线性模型。
如果 Regression 输出是一个实数,利用一种线性关系,就是我们图中下方的公式: \[ \begin{align*} y_i = \beta_0+\beta_1x_{i1}+...+\beta_px_{ip}+\varepsilon_i = x_i^T\beta + \varepsilon_i, \qquad i = 1, ..., n, \end{align*} \] 我们把它的关系假设成是一种线性关系,输出是一系列的实数,这个是一种回归现象,我们就把这个叫做线性回归。
图下方的式子是线性关系,Regression 是回归现象。我们就把要拟定的 f(x) 叫做线性回归。
假如 y 是一个向量,x 是一个矩阵,y 等于 x 矩阵和\(\beta\)矩阵做相乘运算。 \[ \begin{align*} y & = \begin{bmatrix} y_1 \\ y_2 \\ \vdots y_n\end{bmatrix}, y = X\beta + \varepsilon; \\ \\ X & = \begin{bmatrix} x_1^T \\ x_2^T \\ vdots \\ x_n^T \end{bmatrix} = \begin{bmatrix} 1 & x_{11} & \cdots & x_{1p} \\ 1 & x_{21} & \cdots & x_{2p} \\ \vdots & \vdots & \ddots & \vdots \\ 1 & x_{n1} & \cdots & x_{np} \\ \end{bmatrix}, \\ \\ \beta & = \begin{bmatrix} \beta_0 \\ \beta_1 \\ \beta_2 \\ \vdots \\ \beta_p \end{bmatrix}, \varepsilon = \begin{bmatrix} \varepsilon_1 \\ \varepsilon_2 \\ \vdots \\ \varepsilon_n \\ \end{bmatrix} \end{align*} \]
这个也是咱们之后做深度学习的时候之所以会经常接受矩阵的一个原因。
线性回归,刚给大家把原理讲了。现在咱们来演示一个非常基本的例子。
我给大家演示这个线性回归,用了一个非常经典的数据集。这个数据集叫做波士顿房价问题,很久前我在学习大数据的时候也成用过这个数据集。
其实我还曾经做过一个一线城市房价的研究,包括北京、上海等地区。但是为什么我没有用这些数据集,而是使用了一个很古老的波士顿地区房价数据集?
因为波士顿这个房价,和 room size,地点,地铁,高速路,周围的犯罪率等等有一个比较明显的关系。所以观察关系比较容易。但是北京的房价有个特点,它和远近没有关系,就是五环六环,也有些房子会很贵,三环也有房子会比较便宜。
和房屋的状况关系也不大,就是有的很老但是也是很贵。他唯一一个有关系的就是学区,这是最重要的一个决定因素。基本上周围有学区,这个房子就会非常贵,尤其是在海淀区。
波士顿数据集虽然很老,但是我们主要是为了学习他背后的这个线性回归原理。
北京这个房价要预测其实很简单,就是你用关键字来预测一下,看一下它里边包不包含学区两个字,然后再看一下那个学区排名就可以了。也并不是说说简单用学区就可以,而是学区对于北京房价的影响是最大的,别的因素都没有那么明显。
不过这个数据集在 scikit-learn 的 1.0 版本中被弃用,更甚的是在 1.2 版本中已经删除,所以我们要想使用这个数据集还需要费一番功夫。
我们可以使用公开库 openml 来进行下载:
1 |
|
这其中,name
就是数据集的名称,version
为版本,
return_X_y
是下载拆分的特征和标签还是字典,False
是默认值,下载的会是字典,如果设定为
True,这需要两个变量分别接收特征和标签。
1 |
|
然后我们来处理一下数据:
1 |
|
在我们获取的数据dataframe
中,我们可以使用一个corr()
,
show the correlation of dataframe variables
,
correlation
是相关系数。
那么相关系数的关系就是,如果一个值的增大,会引起另外一个值一定增大,而且是定比例增大,相关系数就越接近于 1。如果是 0,就是两者之间没有任何关系。那如果是 -1 呢,就是一个值增大,另外一个值就一定见效,而且减小是成相等比例的。
那我们来看一下dataframe
的correlation
之间的关系:
1 |
|
当然,我截图不全。小伙伴们下去自己去执行看看。
现在我们得到了一个 14 乘 14
的一个矩阵,我们可以通过seaborn
的heatmap
来图形化,方便我们更直接的看到其相关性。
在这张图中,越接近于黑色就越呈负相关,越接近于这个浅色就越是正相关,越接近于 1.
比方说 prime 和 prime 是 1, 也就说把 price 当成因变量,再把 price 也当成自变量的时候这两个值是一个增加另外一个一定增加。
price 除了自己本身之外,最亮的是 RM,这是和 price 相关性最大的一个。我们去查询数据源,RM 就是小区平均的卧室个数。
换句话说这个小区如果卧室越多,就意味着房子可能越大,就越是有钱人住的。
再接着找一下影响最负相关的是什么?就是哪一个值的增大会引起房价的降低。
最下面的 LSTAT 是最负面影响的,只要 LSTAT 增大,房价就会明显的随之下降。LSTAT 是什么呢?LSTAT 就是一个小区中的低收入人群在周围的额比例,比例越高,那么房价就会越低。
咱们现在把这两个数值给它全部拿出来:
1 |
|
现在我们发现,RM 是最房价正向影响最多的,LSTAT 是对房价负面影响最多的。现在我们要通过这两个值,因为这两个是影响房价最明显的特征,所以我们现在要建立一个模型,要根据我们已知的 RM 和 LSTAT 来预测房价是多少。
我们要假设一个关系,要建立一个模型。所谓模型其实就是假设关系。很多模型其实都是现实世界中的一种抽象和简化。
高等数学概率统计,第一册的后半部分专门有一个地方就讲相关系数的。有兴趣的回过头再去看看咱们「AI 秘籍」的数学篇。
这里,大家要知道相关系数的意义是什么就行。
我们现在假设和房价之间是一种最简单的线性关系。先从最简单的线性关系开始,假设它是线性关系的话:
1 |
|
这样,我们就用代码简单的实现了一个典型的线性关系。
这个时候,我们通过前面所讲的内容:
\[ \begin{align*} \vec {x} = [x_0, x_1, x_2, ..., x_n] \\ f(x) = \sum_{i \in N} w_i \times x_i +b \end{align*} \]
我们知道 x 是一个向量,wi
也是一个向量。我们来重新定义一下这个model
,你会发现更简单一些:
1 |
|
我们把模型重新写一下,如果每一个 x 就等于 (rm, lstat), 然后 w 就等于
(w1,w2),就是 x 和 w
是两个向量,那么model1()
就可以简写model2()
的形式。
就是 x1 乘以 w1 加 x2 乘以 w2,再加上 b。
那我们写成向量形式,和model1
有什么区别,或者说有什么好处呢?它的好处其实就是在后续需要添加数据的时候函数是不需要改动的。
有了这个 model,我们的目标是要获得一组 w 和 b,要能够使得对于我们的值的预测最好。
怎么预测呢?
$$ \[\begin{align*} loss(\theta) & = \frac{1}{2} \sum(f_{\theta}(x^i)-y^i)^2 = \frac{1}{2}\sum(\theta ^T - y^i)^2 \\ loss(\theta) & = \frac{1}{2} \sum|f_{\theta}(x^i)-y^i| = \frac{1}{2}\sum|\theta ^T - y^i| \end{align*}\] $$
做一个 loss 函数,这个 loss 函数里,\(\theta\)指的是我们所有的参数。就是在这一组参数下,xi 送到 f(x) 里面,它产生的估计的值,然后再计算和 y 之间的差别。
也就是为了获得最优的参数集合,比方说是 (w, b),我们定义了一个 loss 函数,这个 loss 函数在\(\theta\)下,我们输入一组 x: \(loss(\theta; \vec{x})\),然后它就等于求和,i 属于所有的 i:\(\sum_{i \in N}\), \(f_{\theta}(x_i)\)减去\(y_i\)之后的平方: \[ \begin{align*} loss(\theta; \vec {x}) = \sum_{i \in N}(f_{\theta}(x_i) - y_i)^2 \end{align*} \] 如果这个\(f_{\theta}\)对 x 的预测值越好,就说给的 x 都能非常准确的预测出来值是多少,预测值和实际值完全一样,那么这个时候 loss 就等于 0。因为我们的\(f_\theta(x_i)\)和\(y_i\)完全相等。两者相减必定等于 0。
当 loss 特别大的时候,其实是意味着预测值就和真实值差得很远。
在统计学里预估值往往会写成\(\hat y\),那我们就可以将式子变成如下这种形式: \[ \begin{align*} loss(x) = \frac{1}{n}\sum_{i \in N}(\hat y_i - y_i)^2 \end{align*} \] 之前的课程里咱们讲过,为了找出变量让 loss 能够取得最小值,我们可以使用梯度下降的方法。那么我们上面的式子就也可以是如下这种形式: \[ \begin{align*} loss(x) = \frac{1}{n}\sum(w_1 \times x_1 + w_2 \times x_2 +b - y_i)^2 \end{align*} \] 现在为了获得一组 (w,b), 使得 loss 最小。那写出 loss 对 w1,w2 的偏导,对 b 的偏导,就能求解出来了。
那么 loss 对于 W1 的偏导等于多少呢? \[ \begin{align*} \frac{\partial{loss}}{\partial{w_1}} \end{align*} \]
这个都不用手算,眼睛都能看出来。我们将 2 放下来, 把后边的指数 2 放下来,然后再把 X1 提出去。如果你还不会算这个,可以去复习一下导数怎么求。可以找一本高数去好好看看,也可以去我之前写的《数学篇》里去好好看一下。 \[ \begin{align*} \frac{\partial{loss}}{\partial{w_1}} = \frac{2}{n}\sum_{i \in N}(w_1 \times x_{i1} + w_2 \times x_{i2} + b - y_i) \times x_{i1} \end{align*} \]
与此类似,loss 对于 W2 的偏导就等于: \[ \begin{align*} \frac{\partial{loss}}{\partial{w_2}} = \frac{2}{n}\sum_{i \in N}(w_1 \times x_{i1} + w_2 \times x_{i2} + b - y_i) \times x_{i2} \end{align*} \] 对于 b 的偏导,直接就乘以 1 了: \[ \begin{align*} \frac{\partial{loss}}{\partial{b}} = \frac{2}{n}\sum_{i \in N}(w_1 \times x_{i1} + w_2 \times x_{i2} + b - y_i) \end{align*} \]
我们之前是要求什么?求 rm 和 lstat 对吧?那我们现在就可以将其中的 x1 和 x2 替换掉就可以了:
\[ \begin{align*} \frac{\partial{loss}}{\partial{w_1}} & = \frac{2}{n}\sum_{i \in N}(w_1 \times rm_i + w_2 \times lstat_i + b - y_i) \times rm_i \\ \frac{\partial{loss}}{\partial{w_2}} & = \frac{2}{n}\sum_{i \in N}(w_1 \times rm_i + w_2 \times lstat_i + b - y_i) \times lstat_i \\ \frac{\partial{loss}}{\partial{b}} & = \frac{2}{n}\sum_{i \in N}(w_1 \times rm_i + w_2 \times lstat_i + b - y_i) \end{align*} \]
写成这样之后,接下来只要在编程的时候实现出来就行了。也就是,把这一段数学翻译成代码。
我们现在来翻译一下 loss 函数:
1 |
|
我们有一个 loss, loss 的值先等于 0,我们循环一下 (y, yhat),然后 loss 就加等于 y_i - yhat_i 结果的平方。然后 loss 再除以 len(yhat)。
这样就是最简单的一种翻译,之前的 loss 函数就可以翻译成这样。
不过我们要知道另外一种方法,就是 NumPy
里提供了一个方法mean()
,意思是求平均值。
假如说里面有 12345:mean(1,2,3,4,5)
,就是把 12345
这些数字全部加起来,再求它的平均值。
那我们之前的内容就可以写成mean((yhat-y)**2)
,
其实就是y_i
和yhat_i
加起来求个平均值。
所以,我们就可以将代码写成如下这样:
1 |
|
这个写法就是 NumPy 的广播方法。咱们在之前的 Python 篇中有讲到这部分内容,不记得小伙伴可以回头去翻看一下,应该是第 26 章,大家可以去看一下,为了顺利进行下去,我们这里再提一下。
比如说,我们有两个 list:
1 |
|
那么我要进行计算,你会发现会报错:
1 |
|
当然,我们可以使用for
进行循环,但是这样的方法未免太过笨重。而
NumPy 中就提供了一种广播的方法:
1 |
|
将数据改变成 NumPy 的 array,其实就是把它进行向量法,这样去做减法就直接可以减了,这就是咱们代码这样改的一个原因。
接着,咱们要对 w 求偏导:
1 |
|
这里,我们的yhat
也就是\(\hat
y\),其实是相当于\(w_1 \times x_{i1} +
w_2 \times x_{i2} + b\)这一部分,也就是我们预计的值。
预计的值减去实际的值,再乘上 xi。假如把 rm 和 lstat 一起输入进来的话,x 是一个向量,第一个是 rm, 第二个是 x0。
接下来,对 b 求导也就很好写了:
1 |
|
现在已经定义好了线性模型,定义好了 loss,定义好了偏导。我们现在就初始化一个 w,一个一行两列的数组,b 默认可以是 0,也可以是一个随机值。
1 |
|
不过一般来说,w 初始化成一个 normal, 但是 b 一般要初始化成 0。至于为什么,咱们大概讲到深度学习的时候详细的来讲。
然后现在来得到 yhat,这个时候我们就要得到一个 x,w 是有的,b 是有的。
1 |
|
x
怎么求解呢?就需要带一个知识点了,这个知识点就叫做batch training
。就是在做机器学习的时候每次取一个或者少数几个数字来进行学习。
这个是为什么呢?其实理论上是可以把所有的数据一起放进去的,但是在真正的工作中,比方说阿里云里面那个数据那么多,在做模型的时候一下放进去,既存不下,速度还会很慢。所以随机的找几个。那这样,你就可以得到不同的yhat
了:
1 |
|
可以得到不同的yhats
,因为每次随机取的值不一样。因为他的
x 不一样,所以估计出来的 y 也不一样。
可以加一个 loss(yhat, y),我们来看一下它的 loss。
1 |
|
loss 一直比较大,偶尔会出现一个比较小的值,也是昙花一现,因为 w 是随机的,就是 loss 一直在随机波动。
现在咱们要做一件事:
1 |
|
然后我们每 100 下来打印一下:
1 |
|
讲到这,线性回归基本上原理就已经讲完了,咱们下节课再见。下节课,咱们先来总结一下本节课内容,然后咱们开讲「逻辑回归」。
关注「坍缩的奇点」,第一时间获取更多免费 AI 教程。
08. 机器学习 - 线性回归