线性回归 - 宝可梦案例

线性回归 - 宝可梦案例

一、案例分析

假设有10个已知的 x_data 和 y_data ,x 和 y之间的关系是
y_data = b + w * x_data ,需要通过线性回归学习得到w与b。

其中已知:
进化前cp值,x_data为[338., 333., 328., 207., 226., 25., 179., 60., 208., 606.];
进化后cp值,y_data为[640., 633., 619., 393., 428., 27., 193., 66., 226., 1591.]

模型使用:linear model
工具使用:jupyter

二、案例实现

(1)准备工作

需要用到的库有:numpy、 matplotlib.pyplot

  • 导入库函数
import numpy as np
import matplotlib.pyplot as plt
  • Matplotlib没有中文字体等的动态解决方法
plt.rcParams['font.sans-serif'] = ['Simhei']  # 显示中文
from pylab import mpl
mpl.rcParams['axes.unicode_minus'] = False  # 解决保存图像是负号'-'显示为方块的问题

(2)进行数据的导入

# 进化前的cp值
x_data = [338., 333., 328., 207., 226., 25., 179., 60., 208., 606.]
# 进化后的cp值
y_data = [640., 633., 619., 393., 428., 27., 193., 66., 226., 1591.]

(3)绘制损失函数的热点图

什么是损失函数热点图??
即通过将所有的可能范围内的w和b的组合情况下的损失结果直接在图中通过热力图的形式显示出来,并作为之后的训练过程的背景。
达到的效果:在这里插入图片描述
线性回归中常用的损失函数 Loss function 是:

L(w,b) = ∑ n = 1 10 ( y ˇ n − ( b + w ⋅ x c p ) ) 2 \sum_{n=1}^{10}(\check{y}^{n}-(b+w\cdot x_{cp}))^2 n=110(yˇn(b+wxcp))2

x = np.arange(-200, -100, 1)   # x轴坐标-200到200,间距为1
y = np.arange(-5, 5, 0.1)      # y轴坐标-5到5,间距为0.1
#按照x,y轴的长度生成一个二维矩阵,元素全0填充
L = np.zeros((len(x), len(y)))
# 损失值Loss
for i in range(len(x)):
    for j in range(len(y)):
        b = x[i]  # b表示横轴
        w = y[j]  # w表示纵轴
        # 每一个w和b组合 => 一个Loss Function
        for n in range(len(x_data)):
            L[j][i] = L[j][i] + (y_data[n] - b - w * x_data[n] ) ** 2 # 损失累加
    # 计算这种w和b的组合下,损失的平均值
        L[j][i]  /=  len(x_data)

(4)线性回归梯度下降计算w和b

需要计算 Loss 关于 w 和 b 的偏微分,模拟梯度下降过程。
由Loss function推导公式如下:

∂ L ∂ w = ∑ 1 n 2 ⋅ [ y _ t r u e − ( w ⋅ x i + b ) ] ⋅ ( − x i ) \frac{∂L}{∂w}=\sum_{1}^{n}2\cdot[y\_true-(w\cdot x^i+b)]\cdot(-x^i) wL=1n2[y_true(wxi+b)](xi)

∂ L ∂ b = ∑ 1 n 2 ⋅ [ y _ t r u e − ( w ⋅ x i + b ) ] ⋅ ( − 1 ) \frac{∂L}{∂b}=\sum_{1}^{n}2\cdot[y\_true-(w\cdot x^i+b)]\cdot(-1) bL=1n2[y_true(wxi+b)](1)

# linear regression
#1、给参数赋任意初始值
b = -120    #bias
w = -4      #表示x特征的权重
lr = 0.0000001 #学习率
iteration = 100000 #迭代次数
b_history = [b]   #保存训练过程的所有b
w_history = [w]  #保存训练过程的所有w
#2、模拟梯度下降过程
for i in range(iteration):
    grad_b = 0.0 # b的偏微分
    grad_w = 0.0 # w的偏微分
    #求w和b的偏导
    for j in range(len(x_data)):
        grad_b += 2 * (y_data[j] - w * x_data[j] -b) * (-1)
        grad_w += 2 * (y_data[j] - w * x_data[j] -b) * (-x_data[j])
    # update w、b
    b -= lr * grad_b
    w -= lr * grad_w
#保存参数w、b的历史值
    b_history.append(b)
    w_history.append(w)

(5)绘制结果图

利用 matplotlib 里的函数即可

# 填充背景,按照损失的大小绘制等高线
plt.contourf(x, y, L, 50, alpha=0.5, cmap=plt.get_cmap('jet'))
# 绘制出最佳结果点,用x标记
plt.plot([-188.4], [2.67], 'x', ms=12, mew=3, color='orange')
#绘制出w和b历史轨迹
plt.plot(b_history, w_history, 'o-', ms=3, lw=1.5, color='black')
# 图像的基本参数设置
plt.xlim(-200, -100)
plt.ylim(-5, 5)
plt.xlabel('b')
plt.ylabel('w')
plt.title('线性回归')
plt.show()

三、结果显示及优化

(1)第一次结果

在这里插入图片描述
这张图是 学习率 Ir 为 0.0000001时 损失函数随w、b变化图,黑色线区域为计算的 w、b的历史轨迹,黄色叉叉处是我们需要得到的最优解。

结果可知,经过100000次的update,我们最终的参数离最佳解仍然非常的遥远。
结论:学习率learning rate不够大,将其增大到Ir = 0.000001(初始的10倍)

(2)优化后第二次结果

在这里插入图片描述
结果可知,经过100000次的update,参数离最佳解更近了,但出现了剧烈的震荡,且仍未达到最佳解。

结论:学习率learning rate不够大,将其增大到Ir = 0.00001(初始的100倍)

(3)优化后第三次结果

在这里插入图片描述
结果可知,我们的损失函数变化过程中,并没有和理想中的一样 靠近最佳解,反而出现了很大的震荡,说明由于学习速率过大,导致更新参数的时候,一次性步长过大,就很难收敛到我们设定好的理想损失值处,从而呈现出了发散状态。

结论:学习速率过大了,因此上一次直接增大lr为初始值的100倍是不合理的。

解决方案

  • 很明显,若直接将学习率增大到100倍,会导致学习率过大,但是增大到10倍则学习率过小,可以尝试继续采用更新学习率的方式,一步一步的调整学习率,缩小范围,从而得到最佳解。
  • 直接采用 Adagrad 算法更新参数 ,怎么实现呢?接下来,我们来学习尝试一下。

四、采用Adagrad 算法更新参数

(1)什么是Adagrad 算法?

直观看公式
如果常规的梯度下降表示方法是:

w t + 1 = w t − η t ⋅ g t w^{t+1}=w^t-\eta^t\cdot g^t wt+1=wtηtgt

那么Adagrad算法的梯度下降就是:

w t + 1 = w t − η t σ t ⋅ g t w^{t+1}=w^t-\frac{\eta^t}{\sigma^t}\cdot g^t wt+1=wtσtηtgt

通俗来讲:Adagrad算法就是加入了 σ ,使得原本不变的学习率开始时刻随着 w 与 b 的梯度变化着。

所以公式中的 σ t \sigma^t σt的含义就是:之前参数的所有微分的均方根

(2)Adagrad算法举例及化简

w 1 ⟵ w 0 ⟵ η 0 σ 0 ⋅ g 0 σ 0 = ( g 0 ) 2 w^1\longleftarrow w^0\longleftarrow\frac{\eta^0}{\sigma^0}\cdot g^0\qquad\sigma^0=\sqrt{(g^0)^2} w1w0σ0η0g0σ0=(g0)2
w 2 ⟵ w 1 ⟵ η 1 σ 1 ⋅ g 1 σ 1 = 1 2 [ ( g 0 ) 2 + ( g 1 ) 2 ] w^2\longleftarrow w^1\longleftarrow\frac{\eta^1}{\sigma^1}\cdot g^1\qquad\sigma^1=\sqrt{\frac{1}{2}[(g^0)^2+(g^1)^2]} w2w1σ1η1g1σ1=21[(g0)2+(g1)2]
w 3 ⟵ w 2 ⟵ η 2 σ 2 ⋅ g 2 σ 2 = 1 3 [ ( g 0 ) 2 + ( g 1 ) 2 + ( g 2 ) 2 ] w^3\longleftarrow w^2\longleftarrow\frac{\eta^2}{\sigma^2}\cdot g^2\qquad\sigma^2=\sqrt{\frac{1}{3}[(g^0)^2+(g^1)^2+(g^2)^2]} w3w2σ2η2g2σ2=31[(g0)2+(g1)2+(g2)2]
↓ ↓ ↓ ↓ \downarrow\qquad\qquad\downarrow\qquad\qquad\downarrow\qquad\downarrow
w t + 1 ⟵ w t ⟵ η t σ t ⋅ g t σ t = 1 t + 1 ∑ i = 0 t ( g i ) 2 w^{t+1}\longleftarrow w^t\longleftarrow\frac{\eta^t}{\sigma^t}\cdot g^t\qquad\sigma^t=\sqrt{\frac{1}{t+1}\sum_{i=0}^t(g^i)^2} wt+1wtσtηtgtσt=t+11i=0t(gi)2

注意:根号中累加后需要除以计数,然后才开根号(即求均方根)

最后公式可以化简得到 w t + 1 = w t − η ∑ i = 0 t ( g i ) 2 ⋅ g t w^{t+1}=w^t-\frac{\eta}{\sqrt{\sum_{i=0}^t(g^i)^2}}\cdot g^t wt+1=wti=0t(gi)2 ηgt
(化简步骤在本文后的总结疑问中会提到)

(3)通过Adagrad算法实现参数更新

# 1.给参数附任意初始值
b = -120
w = -4
lr = 1  # 学习率
iteration = 100000  # 迭代次数

b_history = [b]  # 保存训练过程的所有b
w_history = [w]  # 保存训练过程的所有w

lr_w = 0
lr_b = 0
# 2.模拟回归过程
for i in range(iteration):
    grad_w = 0.0  # w的偏微分
    grad_b = 0.0  # b的偏微分
    for j in range(len(x_data)):
        grad_w += 2 * (y_data[j] - w * x_data[j] - b) * (-x_data[j])
        grad_b += 2 * (y_data[j] - w * x_data[j] - b) * (-1)

    # 采用化简后的Adagrad算法公式更新参数
    lr_w = lr_w + grad_w ** 2
    lr_b = lr_b + grad_b ** 2
    w = w - lr / np.sqrt(lr_w) * grad_w
    b = b - lr / np.sqrt(lr_b) * grad_b

    # 保存历史参数
    w_history.append(w)
    b_history.append(b)

(4)使用算法后的结果展示

在这里插入图片描述
该图是采用Adagrad算法更新参数函数的损失变化图,如图可以直观看到,最终我们的参数达到了我们期望的最优解,即完成目标。

五、案例总结

(1)个人感受

本次案例 通过少量的数据 重现了线性回归的模型

涉及的知识点:

  • 损失函数Loss
  • 梯度下降(Gradient Descent)
    • Loss关于w、b的偏微分公式
  • Numpy、matplotlib函数的使用
  • Adagrad算法

通过这次的案例,让我体会了线性回归的整个过程,更深刻得理解了其中w、b的update过程(梯度下降),以及利用梯度下降和Adagrad算法找到最优解的过程,很奇妙。

(2)过程遇到的疑惑及解答

1为什么Adagrad算法可以达到目的,算法如何化简得到公式: w t + 1 = w t − η ∑ i = 0 t ( g i ) 2 ⋅ g t w^{t+1}=w^t-\frac{\eta}{\sqrt{\sum_{i=0}^t(g^i)^2}}\cdot g^t wt+1=wti=0t(gi)2 ηgt,化简后的公式有什么用?
答:
普通的梯度下降需要人工手动调整学习率,很明显这是一个麻烦的方法,因为学习率过大或者过小都是不行的,很难手动找到那个合适的值。
而Adagrad算法能够让学习率随着迭代次数增加而变小,初始迭代时,使用较大的学习率加速下降;迭代几次后,减小学习率防止振荡和越过;且对每个参数都使用了不同的却合适的学习率。
公式化简得到 w t + 1 = w t − η ∑ i = 0 t ( g i ) 2 ⋅ g t w^{t+1}=w^t-\frac{\eta}{\sqrt{\sum_{i=0}^t(g^i)^2}}\cdot g^t wt+1=wti=0t(gi)2 ηgt
初始公式: w t + 1 = w t − η t σ t ⋅ g t w^{t+1}=w^t-\frac{\eta^t}{\sigma^t}\cdot g^t wt+1=wtσtηtgt

η t = η t + 1 σ t = 1 t + 1 ⋅ ∑ i = 0 t ( g i ) 2 \eta^t =\frac{\eta}{\sqrt{t+1}}\qquad\sigma^t=\sqrt{\frac{1}{t+1}\cdot \sum_{i=0}^t(g^i)^2} ηt=t+1 ησt=t+11i=0t(gi)2

将它们代入初始公式中,化简相约,即可得到化简公式

w t + 1 = w t − η ∑ i = 0 t ( g i ) 2 ⋅ g t w^{t+1}=w^t-\frac{\eta}{\sqrt{\sum_{i=0}^t(g^i)^2}}\cdot g^t wt+1=wti=0t(gi)2 ηgt
而我们就是利用了化简后的公式去计算更新参数的:

# 采用化简后的Adagrad算法公式更新参数
lr_w = lr_w + grad_w ** 2
lr_b = lr_b + grad_b ** 2
w = w - lr / np.sqrt(lr_w) * grad_w
b = b - lr / np.sqrt(lr_b) * grad_b

2、初始加入数据时,数字后 有点 和 没点 有什么区别吗?
在这里插入图片描述
答:
其实很简单,有点表示float类型,没点表示int类型,然而在我们的计算中,要保证精确,自然是使用float类型,因此,加上点更合适。
3、除了用常规公式求偏导,其实还有另一种(下图)求偏导的方式,是怎么做到的呢?
在这里插入图片描述

答:
常规求偏导是:
在这里插入图片描述
对比两种方法,一种用了循环逐一求,一种直接使用了函数对矩阵进行计算,说明一下w的求导变换:
变换后的是:grad_w = -2.0 * np.dot(y_d - y_hat, x_d)
其中:y_hat = w * x_data[j] -b,表示的是预测的y值,则y_d - y_hat就是实际值与预测值间的差值,y_d - y_hat = y_data[j] - w * x_data[j] -b,np.dot()将y_d - y_hat与x_d每一项分别相乘再相加,即达到了(y_data[j] - w * x_data[j] -b) * x_data[j]的效果,最后乘以-2 即 得到2 * (y_data[j] - w * x_data[j] -b) * (-x_data[j])。
b的求导变换也类似。
所以两种方法的式子最后结果都一样,只是运用的方法不同,常规求导更好理解,但另一种方法显得更高级,各有千秋。

六、完整代码

import numpy as np
import matplotlib.pyplot as plt
# matplotlib没有中文字体,动态解决
plt.rcParams['font.sans-serif'] = ['Simhei']  # 显示中文
from pylab import mpl
mpl.rcParams['axes.unicode_minus'] = False  # 解决保存图像是负号'-'显示为方块的问题
# 进化前的cp值
x_data = [338., 333., 328., 207., 226., 25., 179., 60., 208., 606.]
# 进化后的cp值
y_data = [640., 633., 619., 393., 428., 27., 193., 66., 226., 1591.]
x = np.arange(-200, -100, 1)   # x轴坐标-200到200,间距为1
y = np.arange(-5, 5, 0.1)      # y轴坐标-5到5,间距为0.1
#按照x,y轴的长度生成一个二维矩阵,元素全0填充
L = np.zeros((len(x), len(y)))
# 损失值Loss
for i in range(len(x)):
    for j in range(len(y)):
        b = x[i]  # b表示横轴
        w = y[j]  # w表示纵轴
        # 每一个w和b组合 => 一个Loss Function
        for n in range(len(x_data)):
            L[j][i] = L[j][i] + (y_data[n] - b - w * x_data[n] ) ** 2 # 损失累加
    # 计算这种w和b的组合下,损失的平均值
        L[j][i]  /=  len(x_data)
# 1.给参数附任意初始值
b = -120
w = -4
lr = 1  # 学习率
iteration = 100000  # 迭代次数

b_history = [b]  # 保存训练过程的所有b
w_history = [w]  # 保存训练过程的所有w

lr_w = 0
lr_b = 0
# 2.模拟回归过程
for i in range(iteration):
    grad_w = 0.0  # w的偏微分
    grad_b = 0.0  # b的偏微分
    for j in range(len(x_data)):
        grad_w += 2 * (y_data[j] - w * x_data[j] - b) * (-x_data[j])
        grad_b += 2 * (y_data[j] - w * x_data[j] - b) * (-1)

    # 采用化简后的Adagrad算法公式更新参数
    lr_w = lr_w + grad_w ** 2
    lr_b = lr_b + grad_b ** 2
    w = w - lr / np.sqrt(lr_w) * grad_w
    b = b - lr / np.sqrt(lr_b) * grad_b

    # 保存历史参数
    w_history.append(w)
    b_history.append(b)
# 绘制变化图
# 填充等高线
plt.contourf(x, y, L, 50, alpha=0.5, cmap=plt.get_cmap('jet'))
plt.plot([-188.4], [2.67], 'x', ms=12, mew=3, color='orange')
plt.plot(b_history, w_history, 'o-', ms=3, lw=1.5, color='black')
plt.xlim(-200, -100)
plt.ylim(-5, 5)
plt.xlabel('b')
plt.ylabel('w')
plt.title('线性回归')
plt.show()
  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值