序
梯度下降算法(Gradient Descent)是机器学习中最常用的算法之一,从网上看到的关于梯度下降算法的介绍感觉都过于理论化(有好多抽象的数学符号和公式),这篇文章从最简单两个函数问题入手,轻松愉快的理解梯度下降算法,完整代码请见github
基础概述
数学基础:高数会求偏导,知道均方误差损失函数(至少要知道损失函数这个词)
python版本anaconda 4.3.1:Anaconda可以认为是一个python的发行版本,提供了版本管理和包管理的功能,并且已经预装了许多第三方软件包:如pip、zmq、numpy、pandas等。
两个例子理解梯度下降
梯度下降算法的本质就是求导,当输入变量x多于1个时,就是求偏导。
导数的几何意义是函数曲线在这一点上的切线斜率(也就是说导数在这一点绝对值越大,曲线就越陡,也可以解释为y对x的变化率的描述或表达);偏导数的几何意义是函数对某个变量的变化率的描述或表达(如果是二元函数z = x2 + y2,z对x的偏导数解释为z在x方向上的变化率)
数学理论部分这么多就足够了,如果想要复习,参考:
例子1:求解y = (x-3) * (x-3) + 3的最小值
先求y对x的导数:
y' = 2 * (x-3)
然后设定两个超参:
步长rate = 0.001
循环次数max_times = 10000
我们的任务是要找到一个x的值,使得y最小,很显然是当x=3时,y是最小的,下面我们用梯度下降算法来完成这个任务(注意,代码是不会像人一样用脑子计算函数的),算法很好解释:让x每次都沿着负梯度方向移动rate*导数的步长,循环max_times次后停止
import numpy as np
x = 2
rate = 0.001
max_times = 10000
def target_func(x):
"""这段函数就是y = (x-3) * (x-3) +3
"""
y = np.square(x - 3) + 3
return y
while count < max_times:
x = x - rate * 2*(x-3)
"""x的初始值为2(随便写),2*(x-3)是导数y',当x=2时y' = -2,那么就是x要向着-y'的方向移动0.001倍。新的x = 2 - 0.001 * -2 = 2 + 0.002 = 2.002。很好,离我们的目标值3近了一些(其实程序不知道3是最后要得到的目标值,程序也永远不会知道)。第二次循环后x = 2.002 - 0.001 * 2 * (2.002 -3) = 2.002 + 0.0019960000000000004 = 2.003996,离我们的目标值又近了一些。这里0.0019960000000000004只会保留17位的有效数字~
"""
print (count, "training-----current x is: ",x)
count = count + 1
print (x, target_func(x)) #最后看个结果
整个梯度下降算法的过程就是x不断的沿着负梯度方向一步一步一步的移动,直到循环完成(循环结束的标识不一定是次数,也可以设计为两次y之间的变化小于某个值,但如果这个值设定不恰当时,就可能造成抖动或提前结束循环)
例子2:根据训练集数据预测目标函数y = 2 * (x1) + (x2) + 3
首先,要处理的是最基本的线性回归问题,目标函数y = 2 * x1 + x2 +3是我们不知道的(计算机是真的不知道这个函数啊),只有一些待训练的数据(数据来源于网络):
x_train = np.array([ [1, 2], [2, 1], [2, 3], [3, 5], [1, 3], [4, 2], [7, 3], [4, 5], [11, 3], [8, 7] ])
y_train = np.array([7, 8, 10, 14, 8, 13, 20, 16, 28, 26]
我们的任务是找到一个函数去描述上边x1,x2和y之间的对应关系。根据训练数据,假设y和x之间的关系是y = ax1 + bx2 + c(最基本的线性回归模型函数,a,b,c是权重)
这里,就要引入损失函数了,损失函数是评价一个模型好坏程度的函数,通俗的讲就是把当前的权重值带入到模型后得到的预测结果和真实结果之间误差的一个评分函数,训练模型的过程,就是通过算法使得损失函数不断减小。
损失函数我们选用均方误差函数,见下图
引入损失函数后,我们的任务就变成了找到一组对应的权重值,使损失函数最小,是不是已经很像求解y = (x-3)*(x-3) +3最小值的问题了:)
下面我们用梯度下降算法来完成这个任务
import numpy as np
a = np.random.normal() # 初始化3个权重
b = np.random.normal()
c = np.random.normal()
rate = 0.001 #步长
max_times = 10000 # 循环次数
# 训练集
x_train = np.array([ [1, 2], [2, 1], [2, 3], [3, 5], [1, 3], [4, 2], [7, 3], [4, 5], [11, 3], [8, 7] ])
y_train = np.array([7, 8, 10, 14, 8, 13, 20, 16, 28, 26])
# 待测试数据
x_test = np.array([ [1, 4], [2, 2], [2, 5], [5, 3], [1, 5], [4, 1] ])
# 目标函数
def h(x):
return a*x[0]+b*x[1]+c
for i in range(max_times):
for x, y in zip(x_train, y_train):
a = a - rate * (h(x)-y) * x[0] #代码后边来解释这个
b = b - rate * (h(x)-y) * x[1]
c = c - rate * (h(x)-y)
print("a =", a)
print("b =", b)
print("c =", c)
result = [h(xi) for xi in x_train]
print(result)
result = [h(xi) for xi in x_test]
print(result)
下面单独注释一下这行代码
a = a - rate * (h(x)-y) * x[0]
要理解这行代码,就要对损失函数J求偏导了,J对参数a求偏导,见下图(x1就是代码中的x[0],文字表述上用x1,x2看着比较方便),同理,再求J对参数b的偏导,再求J对c的偏导。然后就是a、b、c不断的沿着各自的负梯度方向一步一步一步的移动,直到循环完成。
到此就写完了,不过计算出了y = 2x1 + x2 + 3后,这个东西有啥用呢?
假设x1是房屋面积,x2是开发商的成本,y是房屋价格,那么这个模型就勉强算个房价预测模型了。
github地址
https://github.com/dsgdtc/gradient_descent
结束语
最后给出机器学习求解问题的一般过程,会一一对应到例子2中:
1、数据预处理(给x_train和y_train赋值,实际生产中,拿到的数据并不规范,需要做整理)
2、设计假设函数,也可以叫假设空间(我们认定为线性回归问题,设计了目标函数)
3、设计损失函数(均方误差函数)
4、找算法(随即梯度下降,不同算法适用于不同问题,可能要凭经验或者看论文做选择)
5、根据算法进行训练,观察训练过程(栗子2中的循环,因为示例很简单,并不需要进行参数调节,所以这步基本算是跳过了)
6、用模型做预测(就是预测x_test)
网友评论