作为机器学习的第一课,先啰嗦地简单介绍一下,这是一个从公式推导到python实践的一门 “ 机器通俗演义 ” 系列课程(就是阐述一下自己的拙见),不定期更新,欢迎大家提出意见!
顺便推荐一下吴恩达老师的机器学习课程!(B站有资源)
(下面是正文部分)
线性回归模型
我们由浅入深,先从简单的二元线性回归模型开始。这节课从简单的二维x, y入手,如果对于数学公式推导有些障碍的,可以略过,关于里面每一个公式的 “ 实际意义 ” 会在之后的课程里介绍!
模型简介
有这么一副图,我们想要找到一条直线,让它可以尽可能的经过所有的点
就好像这两根线,我们就需要通过 “ 一番操作 ” 拟合出一条类似绿线那样尽可能经过所有点的线。
一次函数
首先让我们看一下我们从初中开始第一个接触的函数——一次函数!
y = f(x) = kx + b
很简单对不对?恭喜你,你已经会线性回归的绝大部分了!
代价函数(CostFunction)
刚才说“尽可能经过所有点的线”,我们可以看图得出结论,绿线比红线 “ 厉害 ” ,但是机器还不会这么高级的操作啊?所以,在数学上,我们就可以通过“距离”来评估一条直线的 “ 优劣程度 ” ,这个就是我们的代价函数——
J
(
w
,
b
)
=
1
2
m
∑
i
=
1
m
(
(
w
∗
x
(
i
)
+
b
)
−
y
(
i
)
)
2
J(w, b) =\frac{1}{2m} \sum_{i=1}^{m} ( (w * x^{(i)}+ b) - y^{(i)})^2
J(w,b)=2m1i=1∑m((w∗x(i)+b)−y(i))2
看起来有一点复杂,且听我逐步分解。
首先w, b,就是上面一次函数对应的 k, b; 然后 m 就是样本个数(简单理解为你有几个点,比如例图中就是m = 4)然后
x
(
i
)
x^{(i)}
x(i) 就是每一个点的横坐标,
y
(
i
)
y^{(i)}
y(i) 就是每一个点的纵坐标 (其实 i 表示第 i 个样本)所以令
h
(
x
)
=
w
x
+
b
h(x)=wx+b
h(x)=wx+b
那么原式就可以写成
J
(
w
,
b
)
=
1
2
m
∑
i
=
1
m
(
h
(
x
(
i
)
)
−
y
(
i
)
)
2
J(w, b) =\frac{1}{2m} \sum_{i=1}^{m} (h(x^{(i)}) - y^{(i)})^2
J(w,b)=2m1i=1∑m(h(x(i))−y(i))2
那么不是恰好是点纵向到线的距离吗?!
即为途中蓝色虚线长度的平方,
我们把点到线的垂直距离求和,那么总和越小的,是不是就可以说明这根线就尽量贴合我们的数据了?所以这个就是代价函数的意义
模型简介之梅开二度
有了上面的铺垫,我们不难这么想——不断搜索着w, b的值,然后通过代价函数来评估出最好的那一条。于是就有如下过程
Step 1:
Initialization(w, b)
Step 2:
while(CostFunction(w, b) > 0.00001): #--- 误差在0.00001以内的时候我们觉得差不多了, 停止训练
Update(w, b)
那么如何Update呢?我们可以利用梯度下降来做,可能数学比较厉害的同志想到了,这种问题不是可以用 “ 最小二乘法 ” 来解决吗?是的,但是以后我们还要拓展到多元呢!最小二乘的复杂性就体现出来了
梯度下降
想象一下你站在高峰之巅,然后突然口渴想下山喝水,你要走那一条路呢?
显然得,我们这么走一定是最快的,但是为什么呢?
啊!因为这里的坡度最陡啊!
是的,这个就是梯度下降的精髓,每一次选取 “ 坡度 ” 最陡的地方往下走。根据数学知识可以得知,导数指示方向是增长方向,那么我们将其取负方向,不就是最陡的斜坡而下降吗?!于是有
Θ
i
=
Θ
i
−
1
−
α
∇
J
(
Θ
)
\Theta_i = \Theta_{i-1} - \alpha\nabla J(\Theta)
Θi=Θi−1−α∇J(Θ)
我们来举一个例子来理解
上图是一个经典的二次函数
f
(
x
)
=
x
2
f(x)=x^2
f(x)=x2,此时假设我们站在这里(
x
0
=
1
x_0 = 1
x0=1),我们要去往 “ 谷底 ”
先令
α
=
0.3
\alpha = 0.3
α=0.3 根据公式可以得到,迭代的
x
1
=
x
0
+
0.3
∗
f
′
(
x
0
)
=
1
−
0.3
∗
2
=
0.4
x_1=x_0+0.3*f'(x_0) =1-0.3*2=0.4
x1=x0+0.3∗f′(x0)=1−0.3∗2=0.4
大概就是这个样子
同理,我们可以一直迭代,直到我们得到最低点——大概长这个样子(大家熟悉的图像)
这个就是我们的梯度下降法。
对于CostFunction的梯度下降
这里给出推导过程(如果不感兴趣可以直接看结论)
J
(
w
,
b
)
=
1
2
m
∑
i
=
1
m
(
(
w
∗
x
(
i
)
+
b
)
−
y
(
i
)
)
2
J(w, b) =\frac{1}{2m} \sum_{i=1}^{m} ( (w * x^{(i)} + b) - y^{(i)})^2
J(w,b)=2m1i=1∑m((w∗x(i)+b)−y(i))2
令
g
(
x
)
=
x
2
g(x) = x^2
g(x)=x2
h
(
x
)
=
(
w
∗
x
(
i
)
+
b
)
−
y
(
i
)
h(x) = (w * x^{(i)} + b) - y^{(i)}
h(x)=(w∗x(i)+b)−y(i)
则
J
(
w
,
b
)
=
1
2
m
∑
i
=
1
m
g
(
h
(
x
)
)
J(w, b)=\frac{1}{2m}\sum_{i=1}^{m}g(h(x))
J(w,b)=2m1i=1∑mg(h(x))
根据链式法则得到
∂
J
(
w
,
b
)
∂
w
=
1
2
m
∑
i
=
1
m
g
′
(
h
(
w
)
)
h
′
(
w
)
\frac{\partial J(w, b)}{\partial w}=\frac{1}{2m}\sum_{i=1}^{m} g'(h(w))h'(w)
∂w∂J(w,b)=2m1i=1∑mg′(h(w))h′(w)
把除
w
w
w 外所有字母看成参数,代入
∂
J
(
w
,
b
)
∂
w
=
1
2
m
∑
i
=
1
m
2
∗
(
(
w
∗
x
(
i
)
+
b
)
−
y
(
i
)
)
∗
x
(
i
)
\frac{\partial J(w, b)}{\partial w}=\frac{1}{2m}\sum_{i=1}^{m} 2*((w * x^{(i)} + b) - y^{(i)})*x^{(i)}
∂w∂J(w,b)=2m1i=1∑m2∗((w∗x(i)+b)−y(i))∗x(i)
∂
J
(
w
,
b
)
∂
w
=
1
m
∑
i
=
1
m
(
(
w
∗
x
(
i
)
+
b
)
−
y
(
i
)
)
∗
x
(
i
)
\frac{\partial J(w, b)}{\partial w}=\frac{1}{m}\sum_{i=1}^{m} ((w * x^{(i)} + b) - y^{(i)})*x^{(i)}
∂w∂J(w,b)=m1i=1∑m((w∗x(i)+b)−y(i))∗x(i)
同理可得
∂
J
(
w
,
b
)
∂
b
=
1
m
∑
i
=
1
m
(
(
w
∗
x
(
i
)
+
b
)
−
y
(
i
)
)
\frac{\partial J(w, b)}{\partial b}=\frac{1}{m}\sum_{i=1}^{m} ((w * x^{(i)} + b) - y^{(i)})
∂b∂J(w,b)=m1i=1∑m((w∗x(i)+b)−y(i))
代码实现
综上所述,最后一番梳理,只要按步骤来逐个计算,就可以得到最终我们的模型了—— Python代码送的上(numpy)
import numpy as np
#--- Step 0: 定义真实函数 (y)
def RealFunction(x):
return 0.5 * x - 4.3
start = 5
#--- Step 1: 设置数据集(刚才例子里的点集)
trainX = np.array([i for i in range(start, start + n)], dtype = np.float32)
trainY = np.array([RealFunction(i) for i in range(start, n + start)], dtype = np.float32)
#--- Step 2: 设置超参数
np.random.seed(1)
w, b = np.random.uniform(-1, 1, (2)) # --- 初始化 w, b
n = 20 # --- 样本数 (m)
esp = 100000 # --- 训练次数
alpha = 0.003 # --- 学习速率
#--- Step 3: 定义CostFunction, 即误差Loss
def Loss(x, y): # --- 返回误差
return sum((w * x + b - y) ** 2) / float(n * 2)
#--- Step 4: 定义训练框架
def Train(x, y):
global w, b
for i in range(esp):
if(Loss(x, y) < 0.000001): # --- 误差太小不继续训练
break
predictY = w * x + b # --- 预测的结果
deltaW = np.dot(x, (predictY - y)) / float(n)
deltaB = np.sum(predictY - y) / float(n)
w = w - alpha * deltaW
b = b - alpha * deltaB
if (i + 1) % 1000 == 0: # --- 查看更新的 w, b
print(w, b)
print("Loss:", Loss(x, y))
#--- Step 5: 运行
if __name__ == '__main__':
Train(trainX, trainY)
已知上面的 w = 0.5, b = -4.3,运行结果如下所示——
可以看到误差已经非常小了!说明算法的正确性,确实收敛了
总结回顾
首先我们提出了 “ 拟合绿线问题 ” ,接着利用梯度下降法来优化算法。后面的课程会具体介绍里面的每一个小步骤,并且介绍它在现实生活中的运用,并且提出一些问题——比如:为什么可以用梯度下降法优化问题?如果陷入局部最优解怎么办?如何判断算法优劣?究竟什么才是人工智能?以及微积分线性代数的补充知识,推广线性回归模型等,也许会在将来更新!
设置悬念,尽情期待!
我是Machillka,蒟蒻一枚。如果有说错的地方,各位大神千千万万要指出来。
谢谢大家!欢迎各位神犇指点!
(转载请标注出处与博主姓名)
(QQ:2437844684)
(欢迎各位大牛评论)
——————都看到这里了,不点个赞是不是也觉得不好意思呀~