中国高校SAS数据分析大赛2018宣讲会-华南赛区巡讲

达人专栏丨机器学习之 从零动手实现梯度下降(下)

前面已经有两篇关于梯度下降的入门文章,本文从简单的线性模型入手,讲解了梯度下降是如何学习其中的参数的。而本篇主要是讲怎么从零开始,使用Python实现梯度下降算法。


承接上一篇文章,过两个点(1,2)和(2,1),训练一个最简单的线性模型:y = ax + b,其在参数a和参数b上的梯度为:

1.png

代入其参数更新迭代公式如下:

2.png

假设学习率为0.1。实现代码不复杂,完整如下:



































import numpy as np
# 使用梯度下降算法训练模型:y = ax + b# 给定两个点(1,2)和(2,1),过这两个点拟合一条曲线points = [(1,2), (2,1)] # 样本数据learning_rate = 0.1# 学习率epochs = 100# 迭代次数w = (0, 0)              # 初始权重
defcal_loss(a, b):"""计算损失"""    sum_loss = [(a*x+b-y)**2for x, y in points]    return sum(sum_loss) / len(points)
defgradient_a(a, b):    """损失函数在a方向上的梯度:损失函数对a求偏导"""    loss_grad = [2*(a*x+b-y)*x for x,y in points]return sum(loss_grad)
defgradient_b(a, b):"""损失函数在b方向上的梯度:损失函数对b求偏导"""    loss_grad = [2*(a*x+b-y) for x,y in points]return sum(loss_grad)
defupdate(a, b):"""更新权重"""return (a - learning_rate * gradient_a(a, b),             b - learning_rate * gradient_b(a, b))
for i in range(epochs):    w = update(w[0], w[1])    loss = cal_loss(w[0], w[1])if i % 10 == 9:        print("a=%.3f, b=%.3f, loss=%.3f" % (w[0], w[1], loss))

运行所得结果如下:








a=0.800, b=0.600, loss=1.800a=0.440, b=0.480, loss=1.296a=0.512, b=0.624, loss=1.166a=0.426, b=0.667, loss=1.092a=0.400, b=0.745, loss=1.028。。。。。。a=-0.916, b=2.865, loss=0.004

参数a和b越来越接近模型:y = -x + 3 这条直线。该程序支持多个点,只需要修改对应的points的定义即可。


上面已经实现了一个简单的梯度下降算法,但是只能对y = ax + b这样简单的情况,如果多一个特征就必须修改源码。对于y = ax + b,我们标准化一一下:

3.png

其中x0=1,也就是我们所理解的一个特征,可以理解为两个特征,只是其中一个特征的值一直都是1(对应线性模型中的常数项,任何数值乘以1都等于其本身)。写成更加通用的矩阵形式如下:

4.png

这就是通用的线性模型了。对于有m个特征的模型:

5.png

展开如下:

6.png

输入一组特征值(x0, x1, ..., xn),会得到一个预测值y,我们还是定义该模型的损失函数为:

7.png

其中n为样本数量。把预测值代入展开如下:

8.png

对于第i个参数ai计算梯度:

9.png

代入参数的梯度下降迭代更新公式:

10.png

从第t次迭代到第t+1次迭代的公式如上。


完整代码实现如下:













































import numpy as np
# 使用梯度下降算法训练模型:Y = AX # 支持多个特征# points = [(1,1), (2,2), (4,2)]        # 样本数据: y = ax + bpoints = [(1,1,1), (1,2,2), (2,2,3)]   # 样本数据: y = ax1 + bx2 + clearning_rate = 0.01# 学习率,注意对于[(1,1), (2,2), (4,2)],如果学习率为0.1,将不再收敛epochs = 500# 迭代次数
defparse_points(points):"""生成特征集合目标集"""return np.array([np.append(row[:-1], 1) for row in points]), np.array([row[-1] for row in points])
definit_w(n):"""初始化权重"""return np.zeros(n)
defcal_loss(w):"""计算损失"""    sum_loss = [(sum(w*x)-y)**2for x, y in zip(train_X, train_Y)]return sum(sum_loss) / len(points)
defgradient(w, i):"""计算损失函数在第i个权重上的梯度"""    loss_grad = [2*(sum(w*x) - y)*x[i] for x, y in zip(train_X, train_Y)]    grad = sum(loss_grad)return grad
defupdate(w):"""更新权重"""    new_w = [wi - learning_rate * gradient(w, i) for i, wi in zip(range(len(w)), w)]return np.array(new_w)
train_X, train_Y = parse_points(points)print('特征字段:', train_X)print('目标字段:', train_Y)w = init_w(len(train_X[0]))print('初始权重:', w)
for i in range(epochs):    w = update(w)    loss = cal_loss(w)if i % 10 == 9:        print(w, loss)

这是一个相对通用的梯度下降算法了。

后续可以优化的点:

1. 学习率优化算法

2. 加上正则化项



商务合作
BD@ibbd.net
简历投递
fuwu@ibbd.net
关于迪奥
产品中心
联系我们
行业应用
公司简介
新闻动态
人才招聘
自然语言理解
场景文字识别
文档OCR识别
金融行业
政府&公共事业
零售行业
020-29179586
工作日:9:30-18:30
广州市天河区华观路田头岗二路一横街起点国际三层
能源行业
文本数据挖掘