NNDL 作业13 优化算法3D可视化 [HBU]

2024-01-04 08:52

本文主要是介绍NNDL 作业13 优化算法3D可视化 [HBU],希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

老师作业原博客:【23-24 秋学期】NNDL 作业13 优化算法3D可视化-CSDN博客

NNDL 作业13 优化算法3D可视化-CSDN博客


编程实现优化算法,并3D可视化

1. 函数3D可视化

分别画出x[0]^{2}+x[1]^{2}+x[1]^{3}+x[0]*x[1] 和 x^{2} /20+y^{2}的3D图

NNDL实验 优化算法3D轨迹 鱼书例题3D版_优化算法3d展示-CSDN博客

代码:

from mpl_toolkits.mplot3d import Axes3D
import numpy as np
from matplotlib import pyplot as plt
import torch
from nndl.op import Op# 画出x**2
class OptimizedFunction3D(Op):def __init__(self):super(OptimizedFunction3D, self).__init__()self.params = {'x': 0}self.grads = {'x': 0}def forward(self, x):self.params['x'] = xreturn x[0] ** 2 + x[1] ** 2 + x[1] ** 3 + x[0] * x[1]def backward(self):x = self.params['x']gradient1 = 2 * x[0] + x[1]gradient2 = 2 * x[1] + 3 * x[1] ** 2 + x[0]grad1 = torch.Tensor([gradient1])grad2 = torch.Tensor([gradient2])self.grads['x'] = torch.cat([grad1, grad2])# 使用numpy.meshgrid生成x1,x2矩阵,矩阵的每一行为[-3, 3],以0.1为间隔的数值
x1 = np.arange(-3, 3, 0.1)
x2 = np.arange(-3, 3, 0.1)
x1, x2 = np.meshgrid(x1, x2)
init_x = torch.Tensor(np.array([x1, x2]))model = OptimizedFunction3D()# 绘制 f_3d函数 的 三维图像
fig = plt.figure()
ax = plt.axes(projection='3d')
X = init_x[0].numpy()
Y = init_x[1].numpy()
Z = model(init_x).numpy()
ax.plot_surface(X, Y, Z, cmap='plasma')ax.set_xlabel('x1')
ax.set_ylabel('x2')
ax.set_zlabel('f(x1,x2)')
plt.show()# 画出x * x / 20 + y * y
def func(x, y):return x * x / 20 + y * ydef paint_loss_func():x = np.linspace(-50, 50, 100)  # x的绘制范围是-50到50,从改区间均匀取100个数y = np.linspace(-50, 50, 100)  # y的绘制范围是-50到50,从改区间均匀取100个数X, Y = np.meshgrid(x, y)Z = func(X, Y)fig = plt.figure()  # figsize=(10, 10))ax = Axes3D(fig)plt.xlabel('x')plt.ylabel('y')ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap='plasma')plt.show()paint_loss_func()

结果:


2.加入优化算法,画出轨迹

分别画出x[0]^{2}+x[1]^{2}+x[1]^{3}+x[0]*x[1] 和 x^{2} /20+y^{2}的3D轨迹图

结合3D动画,用自己的语言,从轨迹、速度等多个角度讲解各个算法优缺点

NNDL实验 优化算法3D轨迹 pytorch版_nndl 实验三 将数据转换为 pytorch 张量-CSDN博客

代码为:
 

import torch
import numpy as np
import copy
from matplotlib import pyplot as plt
from matplotlib import animation
from itertools import zip_longest
from nndl.op import Opclass Optimizer(object):  # 优化器基类def __init__(self, init_lr, model):"""优化器类初始化"""# 初始化学习率,用于参数更新的计算self.init_lr = init_lr# 指定优化器需要优化的模型self.model = modeldef step(self):"""定义每次迭代如何更新参数"""passclass SimpleBatchGD(Optimizer):def __init__(self, init_lr, model):super(SimpleBatchGD, self).__init__(init_lr=init_lr, model=model)def step(self):# 参数更新if isinstance(self.model.params, dict):for key in self.model.params.keys():self.model.params[key] = self.model.params[key] - self.init_lr * self.model.grads[key]class Adagrad(Optimizer):def __init__(self, init_lr, model, epsilon):"""Adagrad 优化器初始化输入:- init_lr: 初始学习率 - model:模型,model.params存储模型参数值  - epsilon:保持数值稳定性而设置的非常小的常数"""super(Adagrad, self).__init__(init_lr=init_lr, model=model)self.G = {}for key in self.model.params.keys():self.G[key] = 0self.epsilon = epsilondef adagrad(self, x, gradient_x, G, init_lr):"""adagrad算法更新参数,G为参数梯度平方的累计值。"""G += gradient_x ** 2x -= init_lr / torch.sqrt(G + self.epsilon) * gradient_xreturn x, Gdef step(self):"""参数更新"""for key in self.model.params.keys():self.model.params[key], self.G[key] = self.adagrad(self.model.params[key],self.model.grads[key],self.G[key],self.init_lr)class RMSprop(Optimizer):def __init__(self, init_lr, model, beta, epsilon):"""RMSprop优化器初始化输入:- init_lr:初始学习率- model:模型,model.params存储模型参数值- beta:衰减率- epsilon:保持数值稳定性而设置的常数"""super(RMSprop, self).__init__(init_lr=init_lr, model=model)self.G = {}for key in self.model.params.keys():self.G[key] = 0self.beta = betaself.epsilon = epsilondef rmsprop(self, x, gradient_x, G, init_lr):"""rmsprop算法更新参数,G为迭代梯度平方的加权移动平均"""G = self.beta * G + (1 - self.beta) * gradient_x ** 2x -= init_lr / torch.sqrt(G + self.epsilon) * gradient_xreturn x, Gdef step(self):"""参数更新"""for key in self.model.params.keys():self.model.params[key], self.G[key] = self.rmsprop(self.model.params[key],self.model.grads[key],self.G[key],self.init_lr)class Momentum(Optimizer):def __init__(self, init_lr, model, rho):"""Momentum优化器初始化输入:- init_lr:初始学习率- model:模型,model.params存储模型参数值- rho:动量因子"""super(Momentum, self).__init__(init_lr=init_lr, model=model)self.delta_x = {}for key in self.model.params.keys():self.delta_x[key] = 0self.rho = rhodef momentum(self, x, gradient_x, delta_x, init_lr):"""momentum算法更新参数,delta_x为梯度的加权移动平均"""delta_x = self.rho * delta_x - init_lr * gradient_xx += delta_xreturn x, delta_xdef step(self):"""参数更新"""for key in self.model.params.keys():self.model.params[key], self.delta_x[key] = self.momentum(self.model.params[key],self.model.grads[key],self.delta_x[key],self.init_lr)class Nesterov(Optimizer):def __init__(self, init_lr, model, rho):"""Nesterov优化器初始化输入:- init_lr:初始学习率- model:模型,model.params存储模型参数值- rho:动量因子"""super(Nesterov, self).__init__(init_lr=init_lr, model=model)self.delta_x = {}for key in self.model.params.keys():self.delta_x[key] = 0self.rho = rhodef nesterov(self, x, gradient_x, delta_x, init_lr):"""Nesterov算法更新参数,delta_x为梯度的加权移动平均"""delta_x_prev = delta_xdelta_x = self.rho * delta_x - init_lr * gradient_xx += -self.rho * delta_x_prev + (1 + self.rho) * delta_xreturn x, delta_xdef step(self):"""参数更新"""for key in self.model.params.keys():self.model.params[key], self.delta_x[key] = self.nesterov(self.model.params[key],self.model.grads[key],self.delta_x[key],self.init_lr)class Adam(Optimizer):def __init__(self, init_lr, model, beta1, beta2, epsilon):"""Adam优化器初始化输入:- init_lr:初始学习率- model:模型,model.params存储模型参数值- beta1, beta2:移动平均的衰减率- epsilon:保持数值稳定性而设置的常数"""super(Adam, self).__init__(init_lr=init_lr, model=model)self.beta1 = beta1self.beta2 = beta2self.epsilon = epsilonself.M, self.G = {}, {}for key in self.model.params.keys():self.M[key] = 0self.G[key] = 0self.t = 1def adam(self, x, gradient_x, G, M, t, init_lr):"""adam算法更新参数输入:- x:参数- G:梯度平方的加权移动平均- M:梯度的加权移动平均- t:迭代次数- init_lr:初始学习率"""M = self.beta1 * M + (1 - self.beta1) * gradient_xG = self.beta2 * G + (1 - self.beta2) * gradient_x ** 2M_hat = M / (1 - self.beta1 ** t)G_hat = G / (1 - self.beta2 ** t)t += 1x -= init_lr / torch.sqrt(G_hat + self.epsilon) * M_hatreturn x, G, M, tdef step(self):"""参数更新"""for key in self.model.params.keys():self.model.params[key], self.G[key], self.M[key], self.t = self.adam(self.model.params[key],self.model.grads[key],self.G[key],self.M[key],self.t,self.init_lr)class OptimizedFunction3D(Op):def __init__(self):super(OptimizedFunction3D, self).__init__()self.params = {'x': 0}self.grads = {'x': 0}def forward(self, x):self.params['x'] = xreturn x[0] ** 2 + x[1] ** 2 + x[1] ** 3 + x[0] * x[1]def backward(self):x = self.params['x']gradient1 = 2 * x[0] + x[1]gradient2 = 2 * x[1] + 3 * x[1] ** 2 + x[0]grad1 = torch.Tensor([gradient1])grad2 = torch.Tensor([gradient2])self.grads['x'] = torch.cat([grad1, grad2])class Visualization3D(animation.FuncAnimation):"""    绘制动态图像,可视化参数更新轨迹    """def __init__(self, *xy_values, z_values, labels=[], colors=[], fig, ax, interval=600, blit=True, **kwargs):"""初始化3d可视化类输入:xy_values:三维中x,y维度的值z_values:三维中z维度的值labels:每个参数更新轨迹的标签colors:每个轨迹的颜色interval:帧之间的延迟(以毫秒为单位)blit:是否优化绘图"""self.fig = figself.ax = axself.xy_values = xy_valuesself.z_values = z_valuesframes = max(xy_value.shape[0] for xy_value in xy_values)self.lines = [ax.plot([], [], [], label=label, color=color, lw=2)[0]for _, label, color in zip_longest(xy_values, labels, colors)]super(Visualization3D, self).__init__(fig, self.animate, init_func=self.init_animation, frames=frames,interval=interval, blit=blit, **kwargs)def init_animation(self):# 数值初始化for line in self.lines:line.set_data([], [])# line.set_3d_properties(np.asarray([]))  # 源程序中有这一行,加上会报错。 Edit by David 2022.12.4return self.linesdef animate(self, i):# 将x,y,z三个数据传入,绘制三维图像for line, xy_value, z_value in zip(self.lines, self.xy_values, self.z_values):line.set_data(xy_value[:i, 0], xy_value[:i, 1])line.set_3d_properties(z_value[:i])return self.linesdef train_f(model, optimizer, x_init, epoch):x = x_initall_x = []losses = []for i in range(epoch):all_x.append(copy.deepcopy(x.numpy()))  # 浅拷贝 改为 深拷贝, 否则List的原值会被改变。 Edit by David 2022.12.4.loss = model(x)losses.append(loss)model.backward()optimizer.step()x = model.params['x']return torch.Tensor(np.array(all_x)), losses# 构建6个模型,分别配备不同的优化器
model1 = OptimizedFunction3D()
opt_gd = SimpleBatchGD(init_lr=0.01, model=model1)model2 = OptimizedFunction3D()
opt_adagrad = Adagrad(init_lr=0.5, model=model2, epsilon=1e-7)model3 = OptimizedFunction3D()
opt_rmsprop = RMSprop(init_lr=0.1, model=model3, beta=0.9, epsilon=1e-7)model4 = OptimizedFunction3D()
opt_momentum = Momentum(init_lr=0.01, model=model4, rho=0.9)model5 = OptimizedFunction3D()
opt_adam = Adam(init_lr=0.1, model=model5, beta1=0.9, beta2=0.99, epsilon=1e-7)model6 = OptimizedFunction3D()
opt_Nesterov = Nesterov(init_lr=0.1, model=model6, rho=0.9)models = [model1, model2, model3, model4, model5, model6]
opts = [opt_gd, opt_adagrad, opt_rmsprop, opt_momentum, opt_adam, opt_Nesterov]x_all_opts = []
z_all_opts = []# 使用不同优化器训练for model, opt in zip(models, opts):x_init = torch.FloatTensor([2, 3])x_one_opt, z_one_opt = train_f(model, opt, x_init, 150)  # epoch# 保存参数值x_all_opts.append(x_one_opt.numpy())z_all_opts.append(np.squeeze(z_one_opt))# 使用numpy.meshgrid生成x1,x2矩阵,矩阵的每一行为[-3, 3],以0.1为间隔的数值
x1 = np.arange(-3, 3, 0.1)
x2 = np.arange(-3, 3, 0.1)
x1, x2 = np.meshgrid(x1, x2)
init_x = torch.Tensor(np.array([x1, x2]))model = OptimizedFunction3D()# 绘制 f_3d函数 的 三维图像
fig = plt.figure()
ax = plt.axes(projection='3d')
X = init_x[0].numpy()
Y = init_x[1].numpy()
Z = model(init_x).numpy()  # 改为 model(init_x).numpy() David 2022.12.4
ax.plot_surface(X, Y, Z, cmap='plasma')ax.set_xlabel('x1')
ax.set_ylabel('x2')
ax.set_zlabel('f(x1,x2)')labels = ['SGD', 'AdaGrad', 'RMSprop', 'Momentum', 'Adam', 'Nesterov']
colors = ['#8B0000', '#0000FF', '#000000', '#008B00', '#FF0000']animator = Visualization3D(*x_all_opts, z_values=z_all_opts, labels=labels, colors=colors, fig=fig, ax=ax)
ax.legend(loc='upper left')plt.show()
animator.save('animation.gif')

(一直整不出来动态图,先攒着,等过了期末考试再回来研究,最近实在是太忙了)

代码:

import torch
import numpy as np
import copy
from matplotlib import pyplot as plt
from matplotlib import animation
from itertools import zip_longest
from matplotlib import cmclass Op(object):def __init__(self):passdef __call__(self, inputs):return self.forward(inputs)# 输入:张量inputs# 输出:张量outputsdef forward(self, inputs):# return outputsraise NotImplementedError# 输入:最终输出对outputs的梯度outputs_grads# 输出:最终输出对inputs的梯度inputs_gradsdef backward(self, outputs_grads):# return inputs_gradsraise NotImplementedErrorclass Optimizer(object):  # 优化器基类def __init__(self, init_lr, model):"""优化器类初始化"""# 初始化学习率,用于参数更新的计算self.init_lr = init_lr# 指定优化器需要优化的模型self.model = modeldef step(self):"""定义每次迭代如何更新参数"""passclass SimpleBatchGD(Optimizer):def __init__(self, init_lr, model):super(SimpleBatchGD, self).__init__(init_lr=init_lr, model=model)def step(self):# 参数更新if isinstance(self.model.params, dict):for key in self.model.params.keys():self.model.params[key] = self.model.params[key] - self.init_lr * self.model.grads[key]class Adagrad(Optimizer):def __init__(self, init_lr, model, epsilon):"""Adagrad 优化器初始化输入:- init_lr: 初始学习率 - model:模型,model.params存储模型参数值  - epsilon:保持数值稳定性而设置的非常小的常数"""super(Adagrad, self).__init__(init_lr=init_lr, model=model)self.G = {}for key in self.model.params.keys():self.G[key] = 0self.epsilon = epsilondef adagrad(self, x, gradient_x, G, init_lr):"""adagrad算法更新参数,G为参数梯度平方的累计值。"""G += gradient_x ** 2x -= init_lr / torch.sqrt(G + self.epsilon) * gradient_xreturn x, Gdef step(self):"""参数更新"""for key in self.model.params.keys():self.model.params[key], self.G[key] = self.adagrad(self.model.params[key],self.model.grads[key],self.G[key],self.init_lr)class RMSprop(Optimizer):def __init__(self, init_lr, model, beta, epsilon):"""RMSprop优化器初始化输入:- init_lr:初始学习率- model:模型,model.params存储模型参数值- beta:衰减率- epsilon:保持数值稳定性而设置的常数"""super(RMSprop, self).__init__(init_lr=init_lr, model=model)self.G = {}for key in self.model.params.keys():self.G[key] = 0self.beta = betaself.epsilon = epsilondef rmsprop(self, x, gradient_x, G, init_lr):"""rmsprop算法更新参数,G为迭代梯度平方的加权移动平均"""G = self.beta * G + (1 - self.beta) * gradient_x ** 2x -= init_lr / torch.sqrt(G + self.epsilon) * gradient_xreturn x, Gdef step(self):"""参数更新"""for key in self.model.params.keys():self.model.params[key], self.G[key] = self.rmsprop(self.model.params[key],self.model.grads[key],self.G[key],self.init_lr)class Momentum(Optimizer):def __init__(self, init_lr, model, rho):"""Momentum优化器初始化输入:- init_lr:初始学习率- model:模型,model.params存储模型参数值- rho:动量因子"""super(Momentum, self).__init__(init_lr=init_lr, model=model)self.delta_x = {}for key in self.model.params.keys():self.delta_x[key] = 0self.rho = rhodef momentum(self, x, gradient_x, delta_x, init_lr):"""momentum算法更新参数,delta_x为梯度的加权移动平均"""delta_x = self.rho * delta_x - init_lr * gradient_xx += delta_xreturn x, delta_xdef step(self):"""参数更新"""for key in self.model.params.keys():self.model.params[key], self.delta_x[key] = self.momentum(self.model.params[key],self.model.grads[key],self.delta_x[key],self.init_lr)
class Nesterov(Optimizer):def __init__(self, init_lr, model, rho):"""Nesterov优化器初始化输入:- init_lr:初始学习率- model:模型,model.params存储模型参数值- rho:动量因子"""super(Nesterov, self).__init__(init_lr=init_lr, model=model)self.delta_x = {}for key in self.model.params.keys():self.delta_x[key] = 0self.rho = rhodef nesterov(self, x, gradient_x, delta_x, init_lr):"""Nesterov算法更新参数,delta_x为梯度的加权移动平均"""delta_x_prev = delta_xdelta_x = self.rho * delta_x - init_lr * gradient_xx += -self.rho * delta_x_prev + (1 + self.rho) * delta_xreturn x, delta_xdef step(self):"""参数更新"""for key in self.model.params.keys():self.model.params[key], self.delta_x[key] = self.nesterov(self.model.params[key],self.model.grads[key],self.delta_x[key],self.init_lr)class Adam(Optimizer):def __init__(self, init_lr, model, beta1, beta2, epsilon):"""Adam优化器初始化输入:- init_lr:初始学习率- model:模型,model.params存储模型参数值- beta1, beta2:移动平均的衰减率- epsilon:保持数值稳定性而设置的常数"""super(Adam, self).__init__(init_lr=init_lr, model=model)self.beta1 = beta1self.beta2 = beta2self.epsilon = epsilonself.M, self.G = {}, {}for key in self.model.params.keys():self.M[key] = 0self.G[key] = 0self.t = 1def adam(self, x, gradient_x, G, M, t, init_lr):"""adam算法更新参数输入:- x:参数- G:梯度平方的加权移动平均- M:梯度的加权移动平均- t:迭代次数- init_lr:初始学习率"""M = self.beta1 * M + (1 - self.beta1) * gradient_xG = self.beta2 * G + (1 - self.beta2) * gradient_x ** 2M_hat = M / (1 - self.beta1 ** t)G_hat = G / (1 - self.beta2 ** t)t += 1x -= init_lr / torch.sqrt(G_hat + self.epsilon) * M_hatreturn x, G, M, tdef step(self):"""参数更新"""for key in self.model.params.keys():self.model.params[key], self.G[key], self.M[key], self.t = self.adam(self.model.params[key],self.model.grads[key],self.G[key],self.M[key],self.t,self.init_lr)class OptimizedFunction3D(Op):def __init__(self):super(OptimizedFunction3D, self).__init__()self.params = {'x': 0}self.grads = {'x': 0}def forward(self, x):self.params['x'] = xreturn x[0] * x[0] / 20 + x[1] * x[1] / 1# return x[0] ** 2 + x[1] ** 2 + x[1] ** 3 + x[0] * x[1]def backward(self):x = self.params['x']gradient1 = 2 * x[0] / 20gradient2 = 2 * x[1] / 1grad1 = torch.Tensor([gradient1])grad2 = torch.Tensor([gradient2])self.grads['x'] = torch.cat([grad1, grad2])class Visualization3D(animation.FuncAnimation):"""    绘制动态图像,可视化参数更新轨迹    """def __init__(self, *xy_values, z_values, labels=[], colors=[], fig, ax, interval=100, blit=True, **kwargs):"""初始化3d可视化类输入:xy_values:三维中x,y维度的值z_values:三维中z维度的值labels:每个参数更新轨迹的标签colors:每个轨迹的颜色interval:帧之间的延迟(以毫秒为单位)blit:是否优化绘图"""self.fig = figself.ax = axself.xy_values = xy_valuesself.z_values = z_valuesframes = max(xy_value.shape[0] for xy_value in xy_values)self.lines = [ax.plot([], [], [], label=label, color=color, lw=2)[0]for _, label, color in zip_longest(xy_values, labels, colors)]self.points = [ax.plot([], [], [], color=color, markeredgewidth=1, markeredgecolor='black', marker='o')[0]for _, color in zip_longest(xy_values, colors)]# print(self.lines)super(Visualization3D, self).__init__(fig, self.animate, init_func=self.init_animation, frames=frames,interval=interval, blit=blit, **kwargs)def init_animation(self):# 数值初始化for line in self.lines:line.set_data_3d([], [], [])for point in self.points:point.set_data_3d([], [], [])return self.points + self.linesdef animate(self, i):# 将x,y,z三个数据传入,绘制三维图像for line, xy_value, z_value in zip(self.lines, self.xy_values, self.z_values):line.set_data_3d(xy_value[:i, 0], xy_value[:i, 1], z_value[:i])for point, xy_value, z_value in zip(self.points, self.xy_values, self.z_values):point.set_data_3d(xy_value[i, 0], xy_value[i, 1], z_value[i])return self.points + self.linesdef train_f(model, optimizer, x_init, epoch):x = x_initall_x = []losses = []for i in range(epoch):all_x.append(copy.deepcopy(x.numpy()))  # 浅拷贝 改为 深拷贝, 否则List的原值会被改变。 Edit by David 2022.12.4.loss = model(x)losses.append(loss)model.backward()optimizer.step()x = model.params['x']return torch.Tensor(np.array(all_x)), losses# 构建6个模型,分别配备不同的优化器
model1 = OptimizedFunction3D()
opt_gd = SimpleBatchGD(init_lr=0.95, model=model1)model2 = OptimizedFunction3D()
opt_adagrad = Adagrad(init_lr=1.5, model=model2, epsilon=1e-7)model3 = OptimizedFunction3D()
opt_rmsprop = RMSprop(init_lr=0.05, model=model3, beta=0.9, epsilon=1e-7)model4 = OptimizedFunction3D()
opt_momentum = Momentum(init_lr=0.1, model=model4, rho=0.9)model5 = OptimizedFunction3D()
opt_adam = Adam(init_lr=0.3, model=model5, beta1=0.9, beta2=0.99, epsilon=1e-7)model6 = OptimizedFunction3D()
opt_Nesterov = Nesterov(init_lr=0.1, model=model6, rho=0.9)  # 将 model4 改为 model6models = [model1, model2, model3, model4, model5, model6]
opts = [opt_gd, opt_adagrad, opt_rmsprop, opt_momentum, opt_adam, opt_Nesterov]x_all_opts = []
z_all_opts = []# 使用不同优化器训练
for model, opt in zip(models, opts):x_init = torch.FloatTensor([-7, 2])x_one_opt, z_one_opt = train_f(model, opt, x_init, 100)  # epoch# 保存参数值x_all_opts.append(x_one_opt.numpy())z_all_opts.append(np.squeeze(z_one_opt))# 使用numpy.meshgrid生成x1,x2矩阵,矩阵的每一行为[-3, 3],以0.1为间隔的数值
x1 = np.arange(-10, 10, 0.01)
x2 = np.arange(-5, 5, 0.01)
x1, x2 = np.meshgrid(x1, x2)
init_x = torch.Tensor(np.array([x1, x2]))model = OptimizedFunction3D()# 绘制 f_3d函数 的 三维图像
fig = plt.figure()
ax = plt.axes(projection='3d')
X = init_x[0].numpy()
Y = init_x[1].numpy()
Z = model(init_x).numpy()  # 改为 model(init_x).numpy() David 2022.12.4
surf = ax.plot_surface(X, Y, Z, edgecolor='grey', cmap=cm.coolwarm)
# fig.colorbar(surf, shrink=0.5, aspect=1)
# ax.set_zlim(-3, 2)
ax.set_xlabel('x1')
ax.set_ylabel('x2')
ax.set_zlabel('f(x1,x2)')labels = ['SGD', 'AdaGrad', 'RMSprop', 'Momentum', 'Adam', 'Nesterov']
colors = ['#8B0000', '#0000FF', '#000000', '#008B00', '#FF0000']animator = Visualization3D(*x_all_opts, z_values=z_all_opts, labels=labels, colors=colors, fig=fig, ax=ax)
ax.legend(loc='upper right')plt.show()
# animator.save('teaser' + '.gif', writer='imagemagick',fps=10) # 效果不好,估计被挡住了…… 有待进一步提高 Edit by David 2022.12.4

图像结果:


3.复现CS231经典动画

结合3D动画,用自己的语言,从轨迹、速度等多个角度讲解各个算法优缺点

NNDL实验 优化算法3D轨迹 复现cs231经典动画_深度学习 优化算法 动画展示-CSDN博客

Animations that may help your intuitions about the learning process dynamics. 

Left: Contours of a loss surface and time evolution of different optimization algorithms. Notice the "overshooting" behavior of momentum-based methods, which make the optimization look like a ball rolling down the hill. 

import torch
import numpy as np
import copy
from matplotlib import pyplot as plt
from matplotlib import animation
from itertools import zip_longest
from matplotlib import cmclass Op(object):def __init__(self):passdef __call__(self, inputs):return self.forward(inputs)# 输入:张量inputs# 输出:张量outputsdef forward(self, inputs):# return outputsraise NotImplementedError# 输入:最终输出对outputs的梯度outputs_grads# 输出:最终输出对inputs的梯度inputs_gradsdef backward(self, outputs_grads):# return inputs_gradsraise NotImplementedErrorclass Optimizer(object):  # 优化器基类def __init__(self, init_lr, model):"""优化器类初始化"""# 初始化学习率,用于参数更新的计算self.init_lr = init_lr# 指定优化器需要优化的模型self.model = modeldef step(self):"""定义每次迭代如何更新参数"""passclass SimpleBatchGD(Optimizer):def __init__(self, init_lr, model):super(SimpleBatchGD, self).__init__(init_lr=init_lr, model=model)def step(self):# 参数更新if isinstance(self.model.params, dict):for key in self.model.params.keys():self.model.params[key] = self.model.params[key] - self.init_lr * self.model.grads[key]class Adagrad(Optimizer):def __init__(self, init_lr, model, epsilon):"""Adagrad 优化器初始化输入:- init_lr: 初始学习率 - model:模型,model.params存储模型参数值  - epsilon:保持数值稳定性而设置的非常小的常数"""super(Adagrad, self).__init__(init_lr=init_lr, model=model)self.G = {}for key in self.model.params.keys():self.G[key] = 0self.epsilon = epsilondef adagrad(self, x, gradient_x, G, init_lr):"""adagrad算法更新参数,G为参数梯度平方的累计值。"""G += gradient_x ** 2x -= init_lr / torch.sqrt(G + self.epsilon) * gradient_xreturn x, Gdef step(self):"""参数更新"""for key in self.model.params.keys():self.model.params[key], self.G[key] = self.adagrad(self.model.params[key],self.model.grads[key],self.G[key],self.init_lr)class RMSprop(Optimizer):def __init__(self, init_lr, model, beta, epsilon):"""RMSprop优化器初始化输入:- init_lr:初始学习率- model:模型,model.params存储模型参数值- beta:衰减率- epsilon:保持数值稳定性而设置的常数"""super(RMSprop, self).__init__(init_lr=init_lr, model=model)self.G = {}for key in self.model.params.keys():self.G[key] = 0self.beta = betaself.epsilon = epsilondef rmsprop(self, x, gradient_x, G, init_lr):"""rmsprop算法更新参数,G为迭代梯度平方的加权移动平均"""G = self.beta * G + (1 - self.beta) * gradient_x ** 2x -= init_lr / torch.sqrt(G + self.epsilon) * gradient_xreturn x, Gdef step(self):"""参数更新"""for key in self.model.params.keys():self.model.params[key], self.G[key] = self.rmsprop(self.model.params[key],self.model.grads[key],self.G[key],self.init_lr)class Momentum(Optimizer):def __init__(self, init_lr, model, rho):"""Momentum优化器初始化输入:- init_lr:初始学习率- model:模型,model.params存储模型参数值- rho:动量因子"""super(Momentum, self).__init__(init_lr=init_lr, model=model)self.delta_x = {}for key in self.model.params.keys():self.delta_x[key] = 0self.rho = rhodef momentum(self, x, gradient_x, delta_x, init_lr):"""momentum算法更新参数,delta_x为梯度的加权移动平均"""delta_x = self.rho * delta_x - init_lr * gradient_xx += delta_xreturn x, delta_xdef step(self):"""参数更新"""for key in self.model.params.keys():self.model.params[key], self.delta_x[key] = self.momentum(self.model.params[key],self.model.grads[key],self.delta_x[key],self.init_lr)class Nesterov(Optimizer):def __init__(self, init_lr, model, rho):"""Nesterov优化器初始化输入:- init_lr:初始学习率- model:模型,model.params存储模型参数值- rho:动量因子"""super(Nesterov, self).__init__(init_lr=init_lr, model=model)self.delta_x = {}for key in self.model.params.keys():self.delta_x[key] = 0self.rho = rhodef nesterov(self, x, gradient_x, delta_x, init_lr):"""Nesterov算法更新参数,delta_x为梯度的加权移动平均"""delta_x_prev = delta_xdelta_x = self.rho * delta_x - init_lr * gradient_xx += -self.rho * delta_x_prev + (1 + self.rho) * delta_xreturn x, delta_xdef step(self):"""参数更新"""for key in self.model.params.keys():self.model.params[key], self.delta_x[key] = self.nesterov(self.model.params[key],self.model.grads[key],self.delta_x[key],self.init_lr)class Adam(Optimizer):def __init__(self, init_lr, model, beta1, beta2, epsilon):"""Adam优化器初始化输入:- init_lr:初始学习率- model:模型,model.params存储模型参数值- beta1, beta2:移动平均的衰减率- epsilon:保持数值稳定性而设置的常数"""super(Adam, self).__init__(init_lr=init_lr, model=model)self.beta1 = beta1self.beta2 = beta2self.epsilon = epsilonself.M, self.G = {}, {}for key in self.model.params.keys():self.M[key] = 0self.G[key] = 0self.t = 1def adam(self, x, gradient_x, G, M, t, init_lr):"""adam算法更新参数输入:- x:参数- G:梯度平方的加权移动平均- M:梯度的加权移动平均- t:迭代次数- init_lr:初始学习率"""M = self.beta1 * M + (1 - self.beta1) * gradient_xG = self.beta2 * G + (1 - self.beta2) * gradient_x ** 2M_hat = M / (1 - self.beta1 ** t)G_hat = G / (1 - self.beta2 ** t)t += 1x -= init_lr / torch.sqrt(G_hat + self.epsilon) * M_hatreturn x, G, M, tdef step(self):"""参数更新"""for key in self.model.params.keys():self.model.params[key], self.G[key], self.M[key], self.t = self.adam(self.model.params[key],self.model.grads[key],self.G[key],self.M[key],self.t,self.init_lr)class OptimizedFunction3D(Op):def __init__(self):super(OptimizedFunction3D, self).__init__()self.params = {'x': 0}self.grads = {'x': 0}def forward(self, x):self.params['x'] = xreturn - x[0] * x[0] / 2 + x[1] * x[1] / 1  # x[0] ** 2 + x[1] ** 2 + x[1] ** 3 + x[0] * x[1]def backward(self):x = self.params['x']gradient1 = - 2 * x[0] / 2gradient2 = 2 * x[1] / 1grad1 = torch.Tensor([gradient1])grad2 = torch.Tensor([gradient2])self.grads['x'] = torch.cat([grad1, grad2])class Visualization3D(animation.FuncAnimation):"""    绘制动态图像,可视化参数更新轨迹    """def __init__(self, *xy_values, z_values, labels=[], colors=[], fig, ax, interval=100, blit=True, **kwargs):"""初始化3d可视化类输入:xy_values:三维中x,y维度的值z_values:三维中z维度的值labels:每个参数更新轨迹的标签colors:每个轨迹的颜色interval:帧之间的延迟(以毫秒为单位)blit:是否优化绘图"""self.fig = figself.ax = axself.xy_values = xy_valuesself.z_values = z_valuesframes = max(xy_value.shape[0] for xy_value in xy_values)self.lines = [ax.plot([], [], [], label=label, color=color, lw=2)[0]for _, label, color in zip_longest(xy_values, labels, colors)]self.points = [ax.plot([], [], [], color=color, markeredgewidth=1, markeredgecolor='black', marker='o')[0]for _, color in zip_longest(xy_values, colors)]# print(self.lines)super(Visualization3D, self).__init__(fig, self.animate, init_func=self.init_animation, frames=frames,interval=interval, blit=blit, **kwargs)def init_animation(self):# 数值初始化for line in self.lines:line.set_data_3d([], [], [])for point in self.points:point.set_data_3d([], [], [])return self.points + self.linesdef animate(self, i):# 将x,y,z三个数据传入,绘制三维图像for line, xy_value, z_value in zip(self.lines, self.xy_values, self.z_values):line.set_data_3d(xy_value[:i, 0], xy_value[:i, 1], z_value[:i])for point, xy_value, z_value in zip(self.points, self.xy_values, self.z_values):point.set_data_3d(xy_value[i, 0], xy_value[i, 1], z_value[i])return self.points + self.linesdef train_f(model, optimizer, x_init, epoch):x = x_initall_x = []losses = []for i in range(epoch):all_x.append(copy.deepcopy(x.numpy()))  # 浅拷贝 改为 深拷贝, 否则List的原值会被改变。 Edit by David 2022.12.4.loss = model(x)losses.append(loss)model.backward()optimizer.step()x = model.params['x']return torch.Tensor(np.array(all_x)), losses# 构建5个模型,分别配备不同的优化器
model1 = OptimizedFunction3D()
opt_gd = SimpleBatchGD(init_lr=0.05, model=model1)model2 = OptimizedFunction3D()
opt_adagrad = Adagrad(init_lr=0.05, model=model2, epsilon=1e-7)model3 = OptimizedFunction3D()
opt_rmsprop = RMSprop(init_lr=0.05, model=model3, beta=0.9, epsilon=1e-7)model4 = OptimizedFunction3D()
opt_momentum = Momentum(init_lr=0.05, model=model4, rho=0.9)model5 = OptimizedFunction3D()
opt_adam = Adam(init_lr=0.05, model=model5, beta1=0.9, beta2=0.99, epsilon=1e-7)model6 = OptimizedFunction3D()
opt_Nesterov = Nesterov(init_lr=0.1, model=model6, rho=0.9)models = [model1, model2, model3, model4, model5, model6]
opts = [opt_gd, opt_adagrad, opt_rmsprop, opt_momentum, opt_adam, opt_Nesterov]x_all_opts = []
z_all_opts = []# 使用不同优化器训练for model, opt in zip(models, opts):x_init = torch.FloatTensor([0.00001, 0.5])x_one_opt, z_one_opt = train_f(model, opt, x_init, 100)  # epoch# 保存参数值x_all_opts.append(x_one_opt.numpy())z_all_opts.append(np.squeeze(z_one_opt))# 使用numpy.meshgrid生成x1,x2矩阵,矩阵的每一行为[-3, 3],以0.1为间隔的数值
x1 = np.arange(-1, 2, 0.01)
x2 = np.arange(-1, 1, 0.05)
x1, x2 = np.meshgrid(x1, x2)
init_x = torch.Tensor(np.array([x1, x2]))model = OptimizedFunction3D()# 绘制 f_3d函数 的 三维图像
fig = plt.figure()
ax = plt.axes(projection='3d')
X = init_x[0].numpy()
Y = init_x[1].numpy()
Z = model(init_x).numpy()  # 改为 model(init_x).numpy() David 2022.12.4
surf = ax.plot_surface(X, Y, Z, edgecolor='grey', cmap=cm.coolwarm)
# fig.colorbar(surf, shrink=0.5, aspect=1)
ax.set_zlim(-3, 2)
ax.set_xlabel('x1')
ax.set_ylabel('x2')
ax.set_zlabel('f(x1,x2)')labels = ['SGD', 'AdaGrad', 'RMSprop', 'Momentum', 'Adam', 'Nesterov']
colors = ['#8B0000', '#0000FF', '#000000', '#008B00', '#FF0000']animator = Visualization3D(*x_all_opts, z_values=z_all_opts, labels=labels, colors=colors, fig=fig, ax=ax)
ax.legend(loc='upper right')plt.show()
# animator.save('teaser' + '.gif', writer='imagemagick',fps=10) # 效果不好,估计被挡住了…… 有待进一步提高 Edit by David 2022.12.4
# save不好用,不费劲了,安装个软件做gif https://pc.qq.com/detail/13/detail_23913.html


4. 结合3D动画,用自己的语言,从轨迹、速度等多个角度讲解各个算法优缺点


SGD
SGD较于其他几个算法,速度相对较慢,会呈现“之”字型的轨迹,并且在cs231经典动画中,SGD出现了陷入局部最小值,出不来的情况。

所以根据动画可以看出SGD的缺点有:

(1)容易陷入局部最优

(2)速度相对较慢且需要调整学习率

AdaGrad
可以看出,AdaGrad图中的轨迹图都是刚开始速度明显大于RMSprop和SGD算法的,偶尔比Momentum和Nesterov还要快,但是随着时间的增长,AdaGrad会成为图中速度最慢的算法。方向上,该算法的方向一直都很准确,并且明显解决了SGD的“之”字型问题,收敛稳定。

相较于SGD算法,AdaGrad的优点:

(1)自适应算法:AdaGrad算法根据每个参数的历史梯度信息来自适应地调整学习率,使得梯度不会太大或太小。

(2)“之”字形的变动程度衰减,呈现稳定的向最优点收敛

缺点:

学习率衰减过快,可能发生早停现象:随着训练的进行,AdaGrad会累积历史梯度的平方和,导致学习率不断减小。在训练后期,学习率可能会变得非常小,甚至接近于零,导致训练过早停止。

RMSprop
RMSprop的轨迹图,速度上很稳定,在前期比AdaGrad要慢,但是后期AdaGrad很慢的时候,RMSprop依然稳定前进。在轨迹方向上,基本和AdaGrad是一样的。

所以相较于AdaGrad而言,RMSprop在它的基础上进行改进,优点为:

收敛速度快解决了AdaGrad算法的早停问题: 引入了衰减率,不会一直累积梯度平方,而是通过梯度平方的指数衰减移动平均来调整学习率,解决了AdaGrad的早衰问题。

Momentum
Momentum算法在速度上,要明显快于前几个函数,跟Nesterov差不多,但是在方向上,Momentum算法每次都是去错的方向转几次,然后才能修正过来。所以Momentum的优点为:

很快的收敛速度,特别是对于类似鞍点的问题,由于动量维持了运动,能够更有效地收敛至局部最小值或平坦区域。但是方向要相对差些,之前的动量仍然会对下一次的下降造成影响,导致Momentum其实有一点大幅度的“之”字型的轨迹。

Nesterov
Nesterov算法的方向和速度效果都是很好的,速度上,它是最快的;方向上,轨迹正确性要好于Momentum,但是仍然要比AdaGrad、RMSprop要差些。Nesterov是对Momentum进行的改进,不仅仅根据当前梯度调整位置,而是根据当前动量在预期的未来位置计算梯度。它的优点为速度快且轨迹呈现出更加平滑、更有方向性的路径朝向最优点。

Adam
根据3D轨迹图:Adam算法的轨迹为稳定,快速的向最小值收敛,就速度和方向的正确性、稳定性而言,都是居中。所以Adam算法的优点就是结合了调整学习率的算法:RMSprop和梯度估计修正算法:Momentum二者的优点:稳定、快速,实用性较高。 
 


这是我的另一篇博客总结的优化算法,需要期末复习的同学可以点击链接:

NNDL学期知识点总结 [HBU]-CSDN博客

动图我一直贴不上去,考完试了回来研究研究,怎么才能贴上动图。

这篇关于NNDL 作业13 优化算法3D可视化 [HBU]的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/568792

相关文章

Vue3 的 shallowRef 和 shallowReactive:优化性能

大家对 Vue3 的 ref 和 reactive 都很熟悉,那么对 shallowRef 和 shallowReactive 是否了解呢? 在编程和数据结构中,“shallow”(浅层)通常指对数据结构的最外层进行操作,而不递归地处理其内部或嵌套的数据。这种处理方式关注的是数据结构的第一层属性或元素,而忽略更深层次的嵌套内容。 1. 浅层与深层的对比 1.1 浅层(Shallow) 定义

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

无人叉车3d激光slam多房间建图定位异常处理方案-墙体画线地图切分方案

墙体画线地图切分方案 针对问题:墙体两侧特征混淆误匹配,导致建图和定位偏差,表现为过门跳变、外月台走歪等 ·解决思路:预期的根治方案IGICP需要较长时间完成上线,先使用切分地图的工程化方案,即墙体两侧切分为不同地图,在某一侧只使用该侧地图进行定位 方案思路 切分原理:切分地图基于关键帧位置,而非点云。 理论基础:光照是直线的,一帧点云必定只能照射到墙的一侧,无法同时照到两侧实践考虑:关

HDFS—存储优化(纠删码)

纠删码原理 HDFS 默认情况下,一个文件有3个副本,这样提高了数据的可靠性,但也带来了2倍的冗余开销。 Hadoop3.x 引入了纠删码,采用计算的方式,可以节省约50%左右的存储空间。 此种方式节约了空间,但是会增加 cpu 的计算。 纠删码策略是给具体一个路径设置。所有往此路径下存储的文件,都会执行此策略。 默认只开启对 RS-6-3-1024k

作业提交过程之HDFSMapReduce

作业提交全过程详解 (1)作业提交 第1步:Client调用job.waitForCompletion方法,向整个集群提交MapReduce作业。 第2步:Client向RM申请一个作业id。 第3步:RM给Client返回该job资源的提交路径和作业id。 第4步:Client提交jar包、切片信息和配置文件到指定的资源提交路径。 第5步:Client提交完资源后,向RM申请运行MrAp

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

康拓展开(hash算法中会用到)

康拓展开是一个全排列到一个自然数的双射(也就是某个全排列与某个自然数一一对应) 公式: X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0! 其中,a[i]为整数,并且0<=a[i]<i,1<=i<=n。(a[i]在不同应用中的含义不同); 典型应用: 计算当前排列在所有由小到大全排列中的顺序,也就是说求当前排列是第

使用opencv优化图片(画面变清晰)

文章目录 需求影响照片清晰度的因素 实现降噪测试代码 锐化空间锐化Unsharp Masking频率域锐化对比测试 对比度增强常用算法对比测试 需求 对图像进行优化,使其看起来更清晰,同时保持尺寸不变,通常涉及到图像处理技术如锐化、降噪、对比度增强等 影响照片清晰度的因素 影响照片清晰度的因素有很多,主要可以从以下几个方面来分析 1. 拍摄设备 相机传感器:相机传

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖