一文弄懂梯度下降

目录

完整实现代码

核心公式

公式含义的讨论

①b = b-(y测-y实)

推论:b↑则y测上移,b↓则y测下移

②w = w-(y测-y实)X

推论:w↑则y测逆时针转

③推理合理性讨论

详细公式推理

参考文章


完整实现代码

import numpy as np
from matplotlib import pyplot as plt
 
 
def show_iter_process(w, b, train_x, train_y, i):
    # 可视化迭代情况
    if i%10==0:
        predict_y = w[0]*train_x**2 + w[1]*train_x + b
        plt.plot(train_x, train_y)
        plt.plot(train_x, predict_y)
        plt.legend(['true', 'predict'], loc='upper right')
        plt.show(block=False)
        plt.pause(0.5)  # 显示0.5秒
        plt.close()
 
def create_data():
    """根据函数 y=2x²+500 生成100个数据点"""
    x = np.linspace(0, 15, 100)   # x不要有负值, 现实数据都是正值
    y = 2*x**2 + 500
    return x,y
 
def grad_test(train_x, train_y):
    """猜测数据模型为 y=ax²+bx+c, 用梯度下降法, 更新猜测模型的参数使得逼近目标函数 y=2x²+500"""

    # 虽然输入数据只有一个 特征值x, 但我们假设 数据有两个 特征值x²和x, 所以扩张输入数据为两个属性. 
    # 这种扩张不增加输入数据的维度, 因为两个属性x²和x具有关联性
    train_x_expand = np.array([[x**2, x] for x in train_x])
    w = [1,1]                   # 初始化权重
    b = 1                       # 初始化偏置
    lrate = [0.001, 0.000001]  # [b的学习率, w的学习率]. 可以体验调参的重要性
    update_times = 1000         # 迭代次数
 
    old_loss = []               # 记录上一次误差损失, 当迭代出现损失值不降时停止迭代
    for i in range(update_times):
        # 梯度, b偏置部分
        grad_b = (np.dot(train_x_expand, w) + b - train_y).sum()
        # 梯度, w权重部分
        grad_w = np.dot(np.dot(train_x_expand, w) + b - train_y, train_x_expand)
 
        # 更新w,b
        b = b - lrate[0] * grad_b, 2
        w = w - lrate[1] * grad_w, 2
 
        # 计算平方误差
        loss = ((np.dot(train_x_expand, w) + b - train_y)**2).sum()
        # print("loss:", round(loss, 2))
 
        if not old_loss:
            old_loss.append(loss)
        elif loss == old_loss[-1]:
            print("迭代次数:", i, "最优损失值:", loss)
            return w,b,old_loss
        else:
            # print("迭代次数:", i, "损失值:", loss)
            # 可视化迭代情况
            show_iter_process(w, b, train_x, train_y, i)
            old_loss.append(loss)

    return w,b,old_loss

train_x, train_y = create_data()
w,b,loss_line = grad_test(train_x, train_y)
print(f"最优参数: w = {w}, b = {b}")
print(f"预测函数: y = {w[0]}x² + {w[1]}x + {b}")

# 损失值变化曲线
# plt.plot(range(len(loss_line)), loss_line)
# plt.xlabel("iter times")
# plt.ylabel("loss")
# plt.show()


# 原数据真实曲线及预测曲线
predict_y = w[0]*train_x**2 + w[1]*train_x + b
plt.plot(train_x, train_y)
plt.plot(train_x, predict_y)
plt.legend(['true', 'predict'], loc='upper right')
plt.show()

核心公式

b_{new}=b_{old}-\eta (\overset{\wedge }{Y}-Y)

w_{new}=w_{old}-\eta (\overset{\wedge }{Y}-Y)X

学习率η>0,X、Y是矩阵,\overset{\wedge }{Y}是预测结果

公式含义的讨论

①b = b-(y测-y实)

讨论这种情况

这种情况说明预测结果高于实际值,需要将预测函数下移

回到公式b_{new}=b_{old}-\eta (\overset{\wedge }{Y}-Y),对照图像,因为\overset{\wedge }{y}的平均值>y的平均值,

所以b_{new}<b_{old}

推论:b↑则y测上移,b↓则y测下移

②w = w-(y测-y实)X

讨论这种情况

假设\overset{\wedge }{Y}Y关于交点对称,x_ax_c关于x_b对称,即\sum (\overset{\wedge }{Y}-Y) = 0

这种情况预测函数需要逆时针转一定角度才能跟上实际函数

回到公式w_{new}=w_{old}-\eta (\overset{\wedge }{Y}-Y)X,结合图像,有:

d_a=\left | \overset{\wedge }{y}-y \right |=d_c

0<d_a\cdot x_a<d_c\cdot x_c

-\eta (\overset{\vee }{y}-y)x_a<0

-\eta (\overset{\vee }{y}-y)x_c>0

所以 w_{new}>w_{old}

推论:w↑则y测逆时针转

③推理合理性讨论

用y=kx+b验证:

  • b↑则y↑,所以①中推论合理
  • k↑(斜率增加),函数图像逆时针转,所以②的推论合理

详细公式推理

设函数关系为

$y=w1x1+w2x2+b$

损失值计算方法为

$L =\sum_{i=1}^{M} {(\overset{\wedge ^{}}{y}- y)}^2$

$x1,x2,y$是已知量,需要求解的是:当$w,b$取什么值时,损失值$L$最小

根据损失计算方法,设损失函数设为

$f_{loss}(w,b) = (w_1x_1+w_2x_2+b - y)^2$

初始化$w1,w2,b$ 值为1,计算损失值$L$,以梯度下降法更新$w1,w2,b$ 的值,使得$L$降低。

一阶泰勒展开公式:

$f(x)\approx f(x0)+(x-x_0)f^{'}(x_0)$

$f_{loss}(w)\approx f{loss}(w_0)+(w-w_0)f_{loss}^{'}(w_0)$

$f_{loss}(b)\approx f_{loss}(b_0)+(b-b_0)f_{loss}^{'}(b_0)$

①②应该用一个式子写,但为了清晰,拆成两个式子

合并写法是:

$f_{loss}(w,b)\approx f_{loss}(w_0,b_0)+((w,b)-(w_0,b_0))f_{loss}^{'}(w_0,b_0)$

目标是降低$f{loss}(w,b)$的值,也就是$f_{loss}(w,b)<f_{loss}(w_0,b_0)$

(这里的数学解释非常不专业)$f_{loss}^{'}(w_0,b_0)$的最大值是梯度值,我个人把梯度值理解为广义的正值,负梯度值是广义的负值,且 |偏导值|≤梯度值

所以,对①式,要使f降低,可令

$(x-x0)\triangledown f(x0)=-\left \| \triangledown f(x_0) \right \|$

$\triangledown f(x_0)=\left \| \triangledown f(x_0) \right \|*\overset{\rightarrow }{r}$     其中\underset{r}{\rightarrow}是梯度方向】

两边乘以▽$f$,然后两边除以▽f的模得:

$x-x_0 = -{\frac{\triangledown f}{\left \| \triangledown f \right \|} }$

\frac{1}{\left \| \triangledown f \right \|}= \eta【学习率η>0,通常取0.01】

所以 更新$w,b$的公式为:

$w=w_0-\eta {\partial f(w,b)\over \partial w}\mid_{w=w0}$【这里的ww_0是多维的】

$b=b_0-\eta {\partial f(w,b)\over \partial b}\mid_{b=b_0}$

理解为:ww_0小了0.01倍梯度值

$f_{loss}(w,b) = (w_1x_1+w_2x_2+b - y)^2$

求偏导举例

${\partial f(w,b)\over \partial w_1}=2(w_1x_1+w_2x_2+b - y)x_1$

更新w,b公式举例:

$w_1=w_1-\eta(w_1x_1+...+w_nx_n+b-y)x_1$

$w_n=w_n-\eta(w_1x_1+...+w_nx_n+b-y)x_n$

$b=b-\eta(w_1x_1+...+w_nx_n+b-y)$

求导结果中的“2”归入学习率,或者说忽略常数“2”

x = (x1,…,xn)是一个样例,这个样例有n个属性值

所以上述更新公式只针对单个数据样例,为了在所用数据上使用该公式,需要调整

$w_1$举例

$w_1=w_1-\eta \sum k$

$k_1=(w_1x_{11}+...+w_nx_{1n}+b-y)x_{11}$

$k_2=(w_1x_{21}+...+w_nx_{2n}+b-y)x_{21}$

针对所有样例求k_i,并求和,然后更新w_1更新$w_n,b$同理

公式-矩阵形式 n个属性值

公式对应代码

# train_x 形如 [ [  0.   0.]
                 [ 25.   5.]
                 [100.  10.]
                 [225.  15.]]
# train_y 形如 [  5.  55. 205. 455.]
# 注:train_x**2表示样例的第一个属性值是x²,train_x表示样例的第二个属性值是x
for i in range(update_times):
        # 梯度, b偏置部分
        grad_b = (np.dot(train_x_expand, w) + b - train_y).sum()
        # 梯度, w权重部分
        grad_w = np.dot(np.dot(train_x_expand, w) + b - train_y, train_x_expand)
 
        # 更新w,b
        b = b - lrate[0] * grad_b, 2
        w = w - lrate[1] * grad_w, 2 

参考文章

梯度下降详解csdnicon-default.png?t=N7T8https://blog.csdn.net/maizousidemao/article/details/106540576

  • 18
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
STM32F103是意法半导体(STMicroelectronics)推出的一款32位单片机系列,属于Cortex-M3内核。下面是一份简要的说明,帮助你了解STM32F103。 1. 内核架构:STM32F103采用ARM Cortex-M3内核,具有较高的性能和低功耗特性。它运行在最高72MHz频率下。 2. 存储器:STM32F103具有不同的存储器选项,包括闪存(Flash)和随机存取存储器(SRAM)。闪存用于存储程序代码和常量数据,SRAM用于存储变量和堆栈。 3. 外设:STM32F103拥有丰富的外设,包括通用定时器、串行通信接口(USART、SPI、I2C)、模数转换器(ADC)、通用输入输出引脚(GPIO)等。这些外设可用于实现各种应用,如控制、通信和传感器接口。 4. 开发环境:对于STM32F103的开发,你可以使用ST提供的官方开发工具——STM32CubeIDE,它是基于Eclipse的集成开发环境。此外,你还可以使用其他第三方软件,如Keil MDK或IAR Embedded Workbench。 5. 编程语言:你可以使用C/C++编程语言进行STM32F103的开发。ST提供了丰富的库函数和示例代码,方便开发者快速上手。 6. 资源:为了更好地了解STM32F103,你可以参考ST官方的技术档、数据手册和应用笔记。此外,CSDN等网站上也有很多关于STM32F103的教程和案例供你学习参考。 需要注意的是,上述信息只是对STM32F103的一个简要介绍,如果你希望深入了解它的特性和开发方法,建议你查阅更多资料并进行实际的开发练习。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值