前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >明月机器学习系列(五):从零动手实现梯度下降

明月机器学习系列(五):从零动手实现梯度下降

作者头像
明月AI
发布2021-10-28 14:26:37
5140
发布2021-10-28 14:26:37
举报
文章被收录于专栏:野生AI架构师
代码语言:javascript
复制
决定给这个系列起一个响亮的名字:明月机器学习系列

我们从简单的线性模型入手,讲解了梯度下降是如何学习其中的参数的。而本篇主要是讲怎么从零开始,使用Python实现梯度下降算法。

最简单的梯度下降算法


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

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

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

代码语言:javascript
复制
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)              # 初始权重

def cal_loss(a, b):
    """计算损失"""
    sum_loss = [(a*x+b-y)**2 for x, y in points]
    return sum(sum_loss) / len(points)

def gradient_a(a, b):
    """损失函数在a方向上的梯度:损失函数对a求偏导"""
    loss_grad = [2*(a*x+b-y)*x for x,y in points]
    return sum(loss_grad)

def gradient_b(a, b):
    """损失函数在b方向上的梯度:损失函数对b求偏导"""
    loss_grad = [2*(a*x+b-y) for x,y in points]
    return sum(loss_grad)

def update(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))

运行所得结果如下:

代码语言:javascript
复制
a=0.800, b=0.600, loss=1.800
a=0.440, b=0.480, loss=1.296
a=0.512, b=0.624, loss=1.166
a=0.426, b=0.667, loss=1.092
a=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,我们标准化一一下:

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

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

展开如下:

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

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

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

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

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

支持多特征的梯度下降算法实现


完整代码实现如下:

代码语言:javascript
复制
import numpy as np

# 使用梯度下降算法训练模型:Y = AX 
# 支持多个特征
# points = [(1,1), (2,2), (4,2)]        # 样本数据: y = ax + b
points = [(1,1,1), (1,2,2), (2,2,3)]  # 样本数据: y = ax1 + bx2 + c
learning_rate = 0.01     # 学习率,注意对于[(1,1), (2,2), (4,2)],如果学习率为0.1,将不再收敛
epochs = 500             # 迭代次数

def parse_points(points):
    """生成特征集合目标集"""
    return np.array([np.append(row[:-1], 1) for row in points]), np.array([row[-1] for row in points])

def init_w(n):
    """初始化权重"""
    return np.zeros(n)

def cal_loss(w):
    """计算损失"""
    sum_loss = [(sum(w*x)-y)**2 for x, y in zip(train_X, train_Y)]
    return sum(sum_loss) / len(points)

def gradient(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

def update(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. 加上正则化项

ps:这是第三篇关于梯度下降的文章了感觉要写到通俗易懂也是不容易。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-07-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 野生AI架构师 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档