美文网首页
通过梯度下降法估计数据x与y的关系

通过梯度下降法估计数据x与y的关系

作者: 芦志强 | 来源:发表于2020-01-01 21:03 被阅读0次

算法思路

  1. 加载并图形化数据散点
  2. 预估函数模型
  3. 通过梯度下降法求得模型中的未知参数
  4. 将未知参数代入模型,并用原始数据计算出损失函数值
  5. 对比预测模型和原始数据的对比图像

一、预设函数模型为y=ax+b的形式,进行计算

加载数据并展示

数据通过npz的方式加载

参考代码

import numpy as np
import matplotlib.pyplot as plt

#加载数据
npz = np.load(
    './1229/homework.npz')
x = npz['X']
d = npz['d']

plt.scatter(x, d, c='g')
plt.show()

图形如下

原始散点图

假设函数模型为 y=ax+b

通过已知数据训练未知参数 a b的过程,实际上是通过梯度下降法求得损失函数
loss = Σ(y-d)^2
取最小值时的 a 和 b 其中 y=ax+b x为数据中心的x列 d为数据中心的d列, 此时 x为已知量而a、b为未知量。所以需要求出损失函数对于 a和b的偏导数,作为梯度下降的方向,进行迭代训练。

  • 定义函数模型
#假设函数形式为y=ax+b 定义此函数
def func(xx, a, b):
    return a*xx+b
  • 求偏导
#损失函数梯度
def dfunc(xx, dd, a, b):
    yy = func(xx, a, b)
    dfdu = 2*(yy-dd)
    return dfdu*xx, dfdu

通过梯度下降法求得a、b

几个重要的参数及作用

  • 学习率:表示每次迭代,变量沿梯度方向前几的步长
  • 训练样本长度:因为可能样本数据比较庞大,需要从样本数据中随机抽取一定数量的数据进行训练以提高训练效率,这个随机抽取的数量即为训练样本长度。一般取 32 64 128 256 512 很少超过1000

训练代码

#学习率
eta = 0.01
#a,b初始值
a, b = 0, 0
#每次训练的样本取样大小
batch_size = 32
#开始训练
for i in range(1000):
    #抽样
    idx = np.random.randint(0, 1000, [batch_size])
    xin = x[idx]
    din = d[idx]
    #求梯度方向
    da, db = dfunc(xin, din, a, b)
    da = np.mean(da)
    db = np.mean(db)
    #梯度下降
    a -= eta*da
    b -= eta*db
    #输出每次得到的 a,b进行观察
    print("a={},b={}".format(a, b))

输出结果节选

...
a=5.000158971271038,b=-1.216844509845388
a=5.008072201329656,b=-1.2207602687746635
a=5.004705377973434,b=-1.2237844250445051
a=4.992377923571635,b=-1.233933937545008

从输出的结果可以看到,a、b的取值收敛,并未发生发散现象。如果训练过程中出现发散,可能是学习率过大引起的,可以适当调整学习率,但学习率也不是越小越好,因为学习率越小,收敛速度越慢。如果经过调整仍然发散,说明训练数据质量太低。

计算损失函数

#计算损失函数
y = func(x, a, b)
fv = (y-d)**2
fv = np.sum(fv)
print(fv)

输出

2506.357443475962

绘制对比图像

#训练得出的a/b代入方程并绘制图像
#训练数据集散点
plt.scatter(x, d, c='g')
#拟合数据线
xline = np.linspace(-2, 5, 100)
yline = func(xline, a, b)
plt.plot(xline, yline, c='r')
plt.show()

拟合图

拟合图

可以看到 图像在x∈(0,3)时的拟合较好,其余区间相差较大。说明预估的函数模型可能不是太好,下边设计更为复杂的函数模型进行训练。

二、预设函数模型为y = ax^2+bx+c 的形式,进行计算

加载数据并展示

数据及图像与上边没有差别,略过

假设函数模型为y = ax^2+bx+c

此时损失函数不变,仍然是
loss = Σ(y-d)^2
而函数模型发生了变化
y = ax^2+bx+c

,需要分别求出对于a、b、c的三个偏导数

  • 定义函数模型
#假设函数形式为y=ax^2+bx+c 定义此函数
def func(xx, a, b, c):
    return a*xx**2+b*xx+c
  • 求偏导
#损失函数梯度
def dfunc(xx, dd, a, b, c):
    yy = func(xx, a, b, c)
    dfdu = 2*(yy-dd)
    return 2*dfdu*xx, dfdu*xx, dfdu

通过梯度下降法求得a、b、c

几个重要的参数及作用

训练代码

#学习率
eta = 0.01
#a,b,c初始值
a, b, c = 0, 0, 0
#每次训练的样本取样大小
batch_size = 32
#开始训练
for i in range(1000):
    #抽样
    idx = np.random.randint(0, 1000, [batch_size])
    xin = x[idx]
    din = d[idx]
    #求梯度方向
    da, db, dc = dfunc(xin, din, a, b, c)
    da = np.mean(da)
    db = np.mean(db)
    dc = np.mean(dc)
    #梯度下降
    a -= eta*da
    b -= eta*db
    c -= eta*dc
    #输出每次得到的 a,b,c进行观察
    print("a={},b={},c={}".format(a, b, c))

输出结果节选

...
a=1.4479619445681633,b=0.7239809722840816,c=0.43759564975538157
a=1.397188399611841,b=0.6985941998059205,c=0.4329935818605244
a=1.414083870087496,b=0.707041935043748,c=0.43991713435825186
a=1.4270933097900267,b=0.7135466548950133,c=0.4432868578503474
a=1.4256464616514657,b=0.7128232308257328,c=0.4461338224887574
a=1.447148708817503,b=0.7235743544087515,c=0.4512765222504626

同样预测参数未出现发散现象,如果学习率设为0.1则会出现发散。

计算损失函数

#计算损失函数
y = func(x, a, b, c)
fv = (y-d)**2
fv = np.sum(fv)
print(fv)

输出

750.8559171548692

可见,本次预估的函数模型下,损失函数值大幅度减小,说明函数模型更由于第一次的函数模型。

绘制对比图像

#训练得出的a/b代入方程并绘制图像
#训练数据集散点
plt.scatter(x, d, c='g')
#拟合数据线
xline = np.linspace(-2, 5, 100)
yline = func(xline, a, b, c)
plt.plot(xline, yline, c='r')
plt.show()

拟合图

拟合图

从拟合图中可以看出,拟合效果比上个模型的效果好了很多,但仍然存在一些区间上,预测值与真实值相差较大。

总结

本文只是简单说明了梯度下降法的基本思想,并通过两个简单的模型进行了训练和预测。实际中遇到的数据模型会非常复杂,如何构建更加复杂的模型使预测结果更加贴合实际数据,还需要通过不断的学习。

相关文章

网友评论

      本文标题:通过梯度下降法估计数据x与y的关系

      本文链接:https://www.haomeiwen.com/subject/owmgoctx.html