在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}")
您的进步和反馈是我最大的动力,小伙伴来个三连呗!共勉。