我们以函数f(x)=为例,这个函数专用于检测优化器的效果。
理论上得到的图像和极小值结果为
我们将这一部分用代码实现
import os
os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import pyplot as plt
import torch
def himmelblau(x):
return (x[0]**2+x[1]-11)**2+(x[0]+x[1]**2-7)**2
x=np.arange(-6,6,0.1)
y=np.arange(-6,6,0.1)
print('x,y range:',x.shape,y.shape)
X,Y=np.meshgrid(x,y) # meshgrid()将x,y的范围传进去后,生成两个图片,每张图片的点和他相同位置的点拼在一起,就形成一个坐标(x,y)
# 如果每个图片都有120个点,那这个map就是120*120的
print('X,Y maps:',X.shape,Y.shape)
Z=himmelblau([X,Y]) # 得到Z的坐标
fig=plt.figure('himmelblau')
ax=fig.gca(projection='3d')
ax.plot_surface(X,Y,Z)
ax.view_init(60,-30)
ax.set_xlabel('x')
ax.set_ylabel('y')
plt.show()
# [1,0],[-4,0],[4,0]
x=torch.tensor([0.,0.],requires_grad=True) # 梯度信息设置为True,初始化的值设为0和0
optimizer=torch.optim.Adam([x],lr=1e-3) # 使用了一个优化器,优化器的目标是x,学习率是1e-3。当接收到梯度信息后,自动的完成x=x-0.001*x的梯度,y同理
for step in range(20000):
pred=himmelblau(x) # x送进来,得到一个预测值。
optimizer.zero_grad() # 梯度信息清零
pred.backward() # 生成x,y的梯度信息
optimizer.step() # 生成上述优化器部分得到的x,y
# 一直循环,直到得到最好的x,y
if step % 2000==0:
print('step{}:x={},f(x)={}'
.format(step,x.tolist(),pred.item()))
此时初始值设置为(0,0)
运行结果
先汇出函数的图像
然后继续运行,得到优化的结果。
x,y range: (120,) (120,)
X,Y maps: (120, 120) (120, 120)
step0:x=[0.0009999999310821295, 0.0009999999310821295],f(x)=170.0
step2000:x=[2.3331806659698486, 1.9540694952011108],f(x)=13.730916023254395
step4000:x=[2.9820079803466797, 2.0270984172821045],f(x)=0.014858869835734367
step6000:x=[2.999983549118042, 2.0000221729278564],f(x)=1.1074007488787174e-08
step8000:x=[2.9999938011169434, 2.0000083446502686],f(x)=1.5572823031106964e-09
step10000:x=[2.999997854232788, 2.000002861022949],f(x)=1.8189894035458565e-10
step12000:x=[2.9999992847442627, 2.0000009536743164],f(x)=1.6370904631912708e-11
step14000:x=[2.999999761581421, 2.000000238418579],f(x)=1.8189894035458565e-12
step16000:x=[3.0, 2.0],f(x)=0.0
step18000:x=[3.0, 2.0],f(x)=0.0
可以看出x,y从0,0开始,此时f(x)为170然后不断优化,当x=3.0,y=2.0时,f(x)得到极小值0
我们可以改变初始值,比如x=-4,y=0
代码改变部分为
x=torch.tensor([-4.,0.],requires_grad=True) # 梯度信息设置为True,初始化的值设为0和0
输出结果为
step0:x=[-3.999000072479248, -0.0009999999310821295],f(x)=146.0
step2000:x=[-3.526559829711914, -2.5002429485321045],f(x)=19.4503231048584
step4000:x=[-3.777446746826172, -3.2777843475341797],f(x)=0.0012130826944485307
step6000:x=[-3.7793045043945312, -3.283174753189087],f(x)=5.636138666886836e-09
step8000:x=[-3.779308319091797, -3.28318190574646],f(x)=7.248672773130238e-10
step10000:x=[-3.7793095111846924, -3.28318452835083],f(x)=8.822098607197404e-11
step12000:x=[-3.7793102264404297, -3.2831854820251465],f(x)=8.185452315956354e-12
step14000:x=[-3.7793102264404297, -3.2831859588623047],f(x)=0.0
step16000:x=[-3.7793102264404297, -3.2831859588623047],f(x)=0.0
step18000:x=[-3.7793102264404297, -3.2831859588623047],f(x)=0.0
可以看出此时x=-3.77,y=-3.28
也可以将初始值改为其他值,进行操作。
经过两次初始值改变,我们可以看到与上文图片中的理论值相差无几。说明优化器结果还是很好的