从PyTorch官方的一篇教程说开去(3.1 - GD 梯度下降法)

发布于:2024-07-22 ⋅ 阅读:(62) ⋅ 点赞:(0)

在openAI以前,我们所讨论的“人工智能”基本上都是“人工智障”,即时在某些方面表现得非常出色,击败了世界冠军,但最多算个某领域的“专才”而不是“通才”。

那么这些“智障”程序们,究竟是靠什么才能做到“专才”呢?

答案就是多次迭代,不断优化,向目标收敛。

回到教程中,我们需要给AI程序(agent)准备大脑(Q网络,本例是3层的神经网络,用于储存,以及匹配输入和输出)以及策略(Q函数,这个函数用于给出在状态S下的动作A,并计算误差L以及预期回报R)。算法初始化时候,基本上是使用类似于醉汉”随机游走“的策略,笨的不要不要的,而用来化腐朽为神奇的策略的关键点,就是能够让误差L最小,并且最快收敛。

于是问题转化为了,对于L的函数,求极(小)值。

而GD梯度下降法本身,就是数学家创造出来,用来解决一般函数的极值问题的。

为了让大家放心食用,这里先给出实例程序运行的动图,以体现逐渐逼近极值的过程。

实例采用大家熟悉的二次函数y=x^2。

我们人类是初中学习了解析几何,得知它的函数图像有对称轴并向上开口,所以在x属于实数域时,有最(极)小值;另外,如果x的取值受到限制,因为有图像辅助,那么在某一段区间上,也很容易找到一个极小值。

只是,人天然是善于利用图形的,但计算机却不需要也不擅长这个;另外,如果碰到一个陌生的或者特别复杂的函数,显然图像就不是那么容易得到。这个时候你跺你也麻,该怎么来寻找极值呢?

是时候请出GD方法了。数学上的严格证明比较绕,我们就直接说结论 - 下山最快的路径,很可能就是在每一个位置上,都沿着坡度最陡的方向(法线方向),然后一路这么下降的走。用数学语言呢,这个”坡度最陡的方向“,就是梯度,也就是某点上,该函数的各个偏导数的极值。

Show me the code,此处直接上一个实例程序 -

【问题】对于y=x^2,求在区间[0,10]上的极小值。

这个示例很容易推广到其他函数,或者其他区间,并且直接生成动图,是所见即所得的代码:

import matplotlib.pyplot as plt
from PIL import Image, ImageSequence
import numpy as np

def f(x):
    # 目标函数
    return x**2

def df(x):
    # 目标函数的导数
    return 2 * x

# 初始参数值
x = 10
learning_rate = 0.1
iterations = 50

# 初始化图形和坐标轴
fig, ax = plt.subplots()
plt.xlabel('x value')
plt.ylabel('f(x) value')
plt.title('Gradient Descent Optimization')
plt.grid(True)

# 用于保存每一帧的列表
frames = []

# 梯度下降过程
for i in range(iterations):
    grad = df(x)
    new_x = x - learning_rate * grad
    x = new_x

    # 绘制当前的梯度下降路径
    # ax.clear()  # 清除之前的图形
    ax.plot([x], [f(x)], 'r.')  # 绘制当前点
    # ax.plot(range(iterations), [f(j * learning_rate) for j in range(iterations)], 'b-')  # 绘制路径

    # 调整坐标轴范围以确保所有点都在视图中
    ax.set_xlim([min(iterations * learning_rate - 1, x - 5), max(10, x + 5)])
    ax.set_ylim([min(f(iterations * learning_rate), f(x) - 5), max(f(x) + 5, 100)])

    # 保存当前帧
    fig.canvas.draw()
    canvas = np.frombuffer(fig.canvas.tostring_rgb(), dtype=np.uint8)
    canvas = canvas.reshape((fig.canvas.get_width_height()[1], fig.canvas.get_width_height()[0], 3))
    frames.append(Image.fromarray(canvas))

    print(f"Iteration {i+1}: x = {x:.2f}, gradient = {grad:.2f}, new x = {new_x:.2f}")

# 保存动画为文件
frames[0].save('gradient_descent.gif', format='GIF', append_images=frames[1:], save_all=True, duration=100, loop=0)

print(f"Minimum found at x = {x:.2f}")

您的进步和反馈是我最大的动力,小伙伴来个三连呗!共勉。


网站公告

今日签到

点亮在社区的每一天
去签到