为什么要学习机器学习?
机器学习(maching learing)总是给人带来一种神秘的面纱,一是这个名字让人难免联想起人工智能。二是因为它过于强大和奇妙的应用。如人脸识别、ai换脸、语音助手、无人驾驶等。这些神奇而令人叹为观止的技术无疑让机器学习更显得更加“高大上”。但就因为这种高大上导致很多软件开发的同学看待这门技术,也觉得其像宇宙深处一样神秘且多变。
我相信很多同学在学习这门技术时,经常会望而却步。一是这门技术确实不是那么容易的入门,二是这门技术的从业者的门槛相对较高,一有挑战,二有顾虑所以很多初学者在学习这门技术时都会变成从入门到放弃。
其实我们不妨简单一点,这是一个当下应用较多的技术,而且未来的应用也会越加广泛。不妨把他作为一本武功秘籍,学学也无妨。如果是从事机器学习、ai相关的工作那自然是好。再退一步来说,这些算法在自然界中有很多的实例。就算不从事相关的工作,生活中用到岂不美妙。正如上个世纪八九十年代,一个业余会修电器的工人常人被邻里称赞。而当下信息爆炸的时代,掌握机器学习将来的应用可能丝毫不亚于以前的"修电"。
而线性回归作为机器学习最简单、又最基础且强大的算法,在生活中还是能得到很多应用的。是否学习机器学习,不妨花半个小时学完这篇文章,再做考量。
这个算法需要什么样的知识储备
- python基本语法
- numpy、skicent-learn库基本接口调用
- 基本数学知识
这篇文章是以入门为主,简单的会列出线性回归的推导过程,但至少体现其思想,如对数学掌握不是很牢靠的同学,也丝毫不影响使用。
简单线性回归
什么是线性回归?
生活中很多事物都是存在线性关系的,比如丈夫的收入与妻子颜值、电视的尺寸和价格、up主的粉丝数与视频播放量等等。简单的来说就是就是成一种直线的趋势,如图1.1所示:
从图中不难发现,java程序员的薪资跟工作年限大概是呈一种线性的关系。简单来说,就是java程序员的工资随着工作年限的增加而增加,而这种增加并不像指数式的喷发那么夸张。那么我们就想,能不能寻找一条直线,最大程度的拟合样本特征与样本输出标记之间的关系呢。
就像是中学的数学应用题,用一条光滑的曲线均匀的穿过点的两边。得到如下图所示的一根直线,这就是整个线性回归的过程。但是这里面的值不再是中学数学中所说的自变量跟因变量。而是特征与输出标记,比如图中的程序员工作年限就是特征,而其的薪资则是输出标记。这里面的特征只有一个,而实际上可能有很多个。而这种只有一个特征的回归我们又称为简单线性回归。
线性回归模型如何建立
图中的样本的个数,我们设为n个。而图中的直线的表达式我们也知道,是y=ax+b。而我们只要把这条曲线的斜率a跟截距b算出来。对于第i个样本,他的特征值值为x(i) ,他的标记值则为y(i)。
而当我们求出这条直线的截图和斜率时,我们可以把x(i)代入得到一个值,我们把其称为
y
^
\hat{y}
y^(i) ,这个值就代表的是样本i的预测值。这个预测值与真实的值会存在差距,我们希望的预测会比较准。而这个比较准就是希望
y
^
\hat{y}
y^(i) 与y(i)(样本实际值)的差距尽量小。也就是使
(
y
(
i
)
−
y
^
(
i
)
)
2
(y^{(i)}-\hat{y}^{(i)})^{2}
(y(i)−y^(i))2尽量小,那么考虑到所有的样本,则得出:
∑
i
=
1
n
(
y
(
i
)
−
y
^
(
i
)
)
2
\sum_{i=1}^n (y^{(i)}-\hat{y}^{(i)})^{2}
i=1∑n(y(i)−y^(i))2
所以我们应该找的一条直线使得上述式子的值最小。而同样不难得出
y
^
(
i
)
=
a
x
i
+
b
\hat{y}^{(i)}=ax^{i}+b
y^(i)=axi+b,则得到:
∑
i
=
1
n
(
y
(
i
)
−
a
x
(
i
)
−
b
)
2
\sum_{i=1}^n (y^{(i)}-ax^{(i)}-b)^{2}
i=1∑n(y(i)−ax(i)−b)2
而简单线性回归就是求出a、b使得上述的值尽可能的小。而所谓的线性回归建模的过程就是找到a、b的过程。而上述的式子又称为损失函数(loss function),可以简写为cost(a,b)。其实机器学习的建模就是优化损失函数,获得机器学习模型的过程。
推导出最优解
其实推导出最优解就是求cost(a,b)的最小值,而求最小值也就是极值最直接的办法就是求导,即得到
∂
c
o
s
(
a
,
b
)
∂
a
=
0
{∂cos(a,b)\over∂a} =0
∂a∂cos(a,b)=0
∂
c
o
s
(
a
,
b
)
∂
b
=
0
{∂cos(a,b)\over∂b} =0
∂b∂cos(a,b)=0
先对b求偏导使其为0
∑
i
=
1
n
(
y
(
i
)
−
a
x
(
i
)
−
b
)
=
0
\sum_{i=1}^n (y^{(i)}-ax^{(i)}-b)=0
i=1∑n(y(i)−ax(i)−b)=0
易得出
∑
i
=
1
n
y
(
i
)
−
a
∑
i
=
1
n
x
(
i
)
−
∑
i
=
1
n
b
=
0
\sum_{i=1}^ny^{(i)} -a\sum_{i=1}^nx^{(i)}-\sum_{i=1}^nb=0
i=1∑ny(i)−ai=1∑nx(i)−i=1∑nb=0
n
b
=
∑
i
=
1
n
y
(
i
)
−
a
∑
i
=
1
n
x
(
i
)
nb = \sum_{i=1}^ny^{(i)} -a\sum_{i=1}^nx^{(i)}
nb=i=1∑ny(i)−ai=1∑nx(i)
求出
b
=
y
(
均
)
−
a
x
(
均
)
b=y(均)-ax(均)
b=y(均)−ax(均)
这样b的值就求出来了,那么继续对a求偏导使其为0。
∑
i
=
1
n
(
y
(
i
)
−
a
x
(
i
)
−
b
)
x
(
i
)
=
0
\sum_{i=1}^n(y^{(i)}-ax^{(i)}-b)x^{(i)}=0
i=1∑n(y(i)−ax(i)−b)x(i)=0
再把b的值代入得:
∑
i
=
1
n
(
y
(
i
)
−
a
x
(
i
)
−
y
(
均
)
+
a
x
(
均
)
)
x
(
i
)
=
0
\sum_{i=1}^n(y^{(i)}-ax^{(i)}-y(均)+ax(均))x^{(i)}=0
i=1∑n(y(i)−ax(i)−y(均)+ax(均))x(i)=0
不难得出
∑
i
=
1
n
(
x
(
i
)
y
(
i
)
−
x
(
i
)
y
(
均
)
)
−
∑
i
=
1
n
(
a
(
x
(
i
)
)
2
−
a
x
(
均
)
x
(
i
)
)
=
0
\sum_{i=1}^n(x^{(i)}y^{(i)}-x^{(i)}y(均))-\sum_{i=1}^n(a(x^{(i)})^{2}-ax(均)x^{(i)})=0
i=1∑n(x(i)y(i)−x(i)y(均))−i=1∑n(a(x(i))2−ax(均)x(i))=0
这样就得到了a的表达式:
a
=
∑
i
=
1
n
(
x
(
i
)
y
(
i
)
−
x
(
i
)
y
(
均
)
)
∑
i
=
1
n
(
(
x
(
i
)
)
2
−
x
(
均
)
x
(
i
)
)
a={\sum_{i=1}^n(x^{(i)}y^{(i)}-x^{(i)}y(均))\over\sum_{i=1}^n((x^{(i)})^{2}-x(均)x^{(i)})}
a=∑i=1n((x(i))2−x(均)x(i))∑i=1n(x(i)y(i)−x(i)y(均))
再简化
∑
i
=
1
n
(
x
(
i
)
−
x
(
均
)
)
(
y
(
i
)
−
y
(
均
)
)
∑
i
=
1
n
(
x
(
i
)
−
x
(
均
)
)
2
{\sum_{i=1}^n(x^{(i)}-x(均))(y^{(i)}-y(均))\over\sum_{i=1}^{n}(x^{(i)}-x(均))^{2}}
∑i=1n(x(i)−x(均))2∑i=1n(x(i)−x(均))(y(i)−y(均))
具体实现
import numpy as np
class SimpleLinearRegression:
def __init__(self):
self.a_ = None
self.b_ = None
def fit(self, x_train, y_train):
"""根据训练集训练模型"""
x_mean = np.mean(x_train)
y_mean = np.mean(y_train)
num = 0.0
d = 0.0
for x,y in zip(x_train,y_train):
num += (x-x_mean) * (y -y_mean)
d += (x - x_mean) ** 2
self.a_ = num / d
self.b_ = y_mean - self.a_ * x_mean
return self
def predict(self,x_predict):
return np.array([self._predict(x) for x in x_predict])
def _predict(self, x_single):
return self.a_ * x_single + self.b_
def __repr__(self):
return "简单线性回归"
我们可以看到,如果b的值比较直接,而a的值则需要通过for循环遍历相加,这种效率在计算大数据集上的效率可想而知,所以有另一种向量化的运算,这里不一一推导,其实也只有fit函数的改动
import numpy as np
class SimpleLinearRegression:
def __init__(self):
self.a_ = None
self.b_ = None
def fit(self, x_train, y_train):
"""根据训练集训练模型"""
x_mean = np.mean(x_train)
y_mean = np.mean(y_train)
num = (x_train - x_mean).dot(y_train-y_mean)
d = (x_train-x_mean).dot(x_train-x_mean)
self.a_ = num / d
self.b_ = y_mean - self.a_ * x_mean
return self
def predict(self,x_predict):
return np.array([self._predict(x) for x in x_predict])
def _predict(self, x_single):
return self.a_ * x_single + self.b_
def __repr__(self):
return "简单线性回归"
验证模型好坏
每一种机器学习模型都会有好有坏,我们来验证模型的好坏,就是拿测试数据对模型进行验证。所以衡量模型好坏的参数极其重要,而对于线性回归算法,则有以下几类衡量方法。
- 均方根误差 RMSE (Root Mean Squared Error)
1 n ∑ i = 1 n ( y ( i ) − y ^ ( i ) ) 2 \sqrt{{1\over n}\sum_{i=1}^n(y^{(i)}-\hat{y}^{(i)})^{2}} n1i=1∑n(y(i)−y^(i))2 - 平均绝对误差 MAE (Mean Absolute Error)
1 n ∑ i = 1 n ∣ y ( i ) − y ^ ( i ) ∣ {1\over n}\sum_{i=1}^{n}|{y^{(i)-\hat{y}^{(i)}}|} n1i=1∑n∣y(i)−y^(i)∣
不难看出这两种算法再验证模型好坏的时候都能达到不错的效果,但是这个值是无限大的,就是控制不了其的大小,这样在同类型的时候或者同一种模型可以比较模型的好坏,这样如果不同的模型计算不同类型的好坏如何比较呢。所以在线性回归中使用最广的是另一种验证方式。
- R Squared =
R
2
R^{2}
R2
R
2
=
1
−
∑
i
(
y
^
(
i
)
−
y
(
i
)
)
2
∑
i
(
y
(
均
)
−
y
(
i
)
)
2
R^{2}=1-{\sum_{i}(\hat{y}^{(i)}-y^{(i)})^{2}\over\sum_{i}(y(均)-y^{(i)})^{2}}
R2=1−∑i(y(均)−y(i))2∑i(y^(i)−y(i))2
R 2 R^{2} R2越大越好, R 2 R^{2} R2<0 则说明不存在任何线性关系。
由于该算法在sklearn已经封装,故在此不再叙述,具体的引用代码如下所示。
import numpy as np
from sklearn.metrics import r2_score
r2_score(y_true=np.array([]),y_pred=np.array([]))
多元线性回归
多元线性回归,由于牵扯到多个维度,其函数表达式为:
y
^
(
i
)
=
θ
0
x
0
(
i
)
+
θ
1
x
1
(
i
)
+
θ
2
x
2
(
i
)
+
θ
n
x
n
(
i
)
\hat{y}^{(i)}=\theta_{0}x^{(i)}_{0}+\theta_{1}x^{(i)}_{1}+\theta_{2}x^{(i)}_{2}+\theta_{n}x^{(i)}_{n}
y^(i)=θ0x0(i)+θ1x1(i)+θ2x2(i)+θnxn(i)
同样,也可对其进行简写。
y
^
=
x
⋅
θ
T
\hat{y}=x\cdot\theta^{T}
y^=x⋅θT
θ
=
(
θ
1
,
θ
2
,
θ
3
,
θ
n
)
\theta= (\theta_{1},\theta_{2},\theta_{3},\theta_{n} )
θ=(θ1,θ2,θ3,θn)
同样求
∑
i
=
1
n
(
y
(
i
)
−
y
^
(
i
)
)
2
\sum_{i=1}^n (y^{(i)}-\hat{y}^{(i)})^{2}
i=1∑n(y(i)−y^(i))2
则得出:
(
y
−
x
b
⋅
θ
)
T
(
y
−
x
b
⋅
θ
)
(y-x_b\cdot\theta)^{T}(y-x_b\cdot\theta)
(y−xb⋅θ)T(y−xb⋅θ)
而我们的目标就是使上述式子尽可能小。
这个推导结果什么复杂,在这里就不细加推敲
θ
=
(
X
b
T
X
b
)
−
1
X
b
T
y
\theta=(X^{T}_bX_b)^{-1}X^{T}_by
θ=(XbTXb)−1XbTy