• NNDL实验 优化算法3D轨迹 pytorch版


    3D轨迹有助于直观理解,掌握这样一个工具还是有必要的。

    换模型、调参数,可以更加深入的了解算法。


    程序改编自《神经网络与深度学习:案例与实践》(Paddle版)

    7.3 不同优化算法的比较分析

    神经网络与深度学习 (nndl.github.io)

    最终效果:

    动画:

    plt.show()看动画没问题。

    导出的gif动画轨迹被曲面挡住了。不知道什么原因,有待进一步研究。

    ​​​​​​​ 

     

     

    源代码:

    1. import torch
    2. import numpy as np
    3. import copy
    4. from matplotlib import pyplot as plt
    5. from matplotlib import animation
    6. from itertools import zip_longest
    7. class Op(object):
    8. def __init__(self):
    9. pass
    10. def __call__(self, inputs):
    11. return self.forward(inputs)
    12. # 输入:张量inputs
    13. # 输出:张量outputs
    14. def forward(self, inputs):
    15. # return outputs
    16. raise NotImplementedError
    17. # 输入:最终输出对outputs的梯度outputs_grads
    18. # 输出:最终输出对inputs的梯度inputs_grads
    19. def backward(self, outputs_grads):
    20. # return inputs_grads
    21. raise NotImplementedError
    22. class Optimizer(object): # 优化器基类
    23. def __init__(self, init_lr, model):
    24. """
    25. 优化器类初始化
    26. """
    27. # 初始化学习率,用于参数更新的计算
    28. self.init_lr = init_lr
    29. # 指定优化器需要优化的模型
    30. self.model = model
    31. def step(self):
    32. """
    33. 定义每次迭代如何更新参数
    34. """
    35. pass
    36. class SimpleBatchGD(Optimizer):
    37. def __init__(self, init_lr, model):
    38. super(SimpleBatchGD, self).__init__(init_lr=init_lr, model=model)
    39. def step(self):
    40. # 参数更新
    41. if isinstance(self.model.params, dict):
    42. for key in self.model.params.keys():
    43. self.model.params[key] = self.model.params[key] - self.init_lr * self.model.grads[key]
    44. class Adagrad(Optimizer):
    45. def __init__(self, init_lr, model, epsilon):
    46. """
    47. Adagrad 优化器初始化
    48. 输入:
    49. - init_lr: 初始学习率 - model:模型,model.params存储模型参数值 - epsilon:保持数值稳定性而设置的非常小的常数
    50. """
    51. super(Adagrad, self).__init__(init_lr=init_lr, model=model)
    52. self.G = {}
    53. for key in self.model.params.keys():
    54. self.G[key] = 0
    55. self.epsilon = epsilon
    56. def adagrad(self, x, gradient_x, G, init_lr):
    57. """
    58. adagrad算法更新参数,G为参数梯度平方的累计值。
    59. """
    60. G += gradient_x ** 2
    61. x -= init_lr / torch.sqrt(G + self.epsilon) * gradient_x
    62. return x, G
    63. def step(self):
    64. """
    65. 参数更新
    66. """
    67. for key in self.model.params.keys():
    68. self.model.params[key], self.G[key] = self.adagrad(self.model.params[key],
    69. self.model.grads[key],
    70. self.G[key],
    71. self.init_lr)
    72. class RMSprop(Optimizer):
    73. def __init__(self, init_lr, model, beta, epsilon):
    74. """
    75. RMSprop优化器初始化
    76. 输入:
    77. - init_lr:初始学习率
    78. - model:模型,model.params存储模型参数值
    79. - beta:衰减率
    80. - epsilon:保持数值稳定性而设置的常数
    81. """
    82. super(RMSprop, self).__init__(init_lr=init_lr, model=model)
    83. self.G = {}
    84. for key in self.model.params.keys():
    85. self.G[key] = 0
    86. self.beta = beta
    87. self.epsilon = epsilon
    88. def rmsprop(self, x, gradient_x, G, init_lr):
    89. """
    90. rmsprop算法更新参数,G为迭代梯度平方的加权移动平均
    91. """
    92. G = self.beta * G + (1 - self.beta) * gradient_x ** 2
    93. x -= init_lr / torch.sqrt(G + self.epsilon) * gradient_x
    94. return x, G
    95. def step(self):
    96. """参数更新"""
    97. for key in self.model.params.keys():
    98. self.model.params[key], self.G[key] = self.rmsprop(self.model.params[key],
    99. self.model.grads[key],
    100. self.G[key],
    101. self.init_lr)
    102. class Momentum(Optimizer):
    103. def __init__(self, init_lr, model, rho):
    104. """
    105. Momentum优化器初始化
    106. 输入:
    107. - init_lr:初始学习率
    108. - model:模型,model.params存储模型参数值
    109. - rho:动量因子
    110. """
    111. super(Momentum, self).__init__(init_lr=init_lr, model=model)
    112. self.delta_x = {}
    113. for key in self.model.params.keys():
    114. self.delta_x[key] = 0
    115. self.rho = rho
    116. def momentum(self, x, gradient_x, delta_x, init_lr):
    117. """
    118. momentum算法更新参数,delta_x为梯度的加权移动平均
    119. """
    120. delta_x = self.rho * delta_x - init_lr * gradient_x
    121. x += delta_x
    122. return x, delta_x
    123. def step(self):
    124. """参数更新"""
    125. for key in self.model.params.keys():
    126. self.model.params[key], self.delta_x[key] = self.momentum(self.model.params[key],
    127. self.model.grads[key],
    128. self.delta_x[key],
    129. self.init_lr)
    130. class Adam(Optimizer):
    131. def __init__(self, init_lr, model, beta1, beta2, epsilon):
    132. """
    133. Adam优化器初始化
    134. 输入:
    135. - init_lr:初始学习率
    136. - model:模型,model.params存储模型参数值
    137. - beta1, beta2:移动平均的衰减率
    138. - epsilon:保持数值稳定性而设置的常数
    139. """
    140. super(Adam, self).__init__(init_lr=init_lr, model=model)
    141. self.beta1 = beta1
    142. self.beta2 = beta2
    143. self.epsilon = epsilon
    144. self.M, self.G = {}, {}
    145. for key in self.model.params.keys():
    146. self.M[key] = 0
    147. self.G[key] = 0
    148. self.t = 1
    149. def adam(self, x, gradient_x, G, M, t, init_lr):
    150. """
    151. adam算法更新参数
    152. 输入:
    153. - x:参数
    154. - G:梯度平方的加权移动平均
    155. - M:梯度的加权移动平均
    156. - t:迭代次数
    157. - init_lr:初始学习率
    158. """
    159. M = self.beta1 * M + (1 - self.beta1) * gradient_x
    160. G = self.beta2 * G + (1 - self.beta2) * gradient_x ** 2
    161. M_hat = M / (1 - self.beta1 ** t)
    162. G_hat = G / (1 - self.beta2 ** t)
    163. t += 1
    164. x -= init_lr / torch.sqrt(G_hat + self.epsilon) * M_hat
    165. return x, G, M, t
    166. def step(self):
    167. """参数更新"""
    168. for key in self.model.params.keys():
    169. self.model.params[key], self.G[key], self.M[key], self.t = self.adam(self.model.params[key],
    170. self.model.grads[key],
    171. self.G[key],
    172. self.M[key],
    173. self.t,
    174. self.init_lr)
    175. class OptimizedFunction3D(Op):
    176. def __init__(self):
    177. super(OptimizedFunction3D, self).__init__()
    178. self.params = {'x': 0}
    179. self.grads = {'x': 0}
    180. def forward(self, x):
    181. self.params['x'] = x
    182. return x[0] ** 2 + x[1] ** 2 + x[1] ** 3 + x[0] * x[1]
    183. def backward(self):
    184. x = self.params['x']
    185. gradient1 = 2 * x[0] + x[1]
    186. gradient2 = 2 * x[1] + 3 * x[1] ** 2 + x[0]
    187. grad1 = torch.Tensor([gradient1])
    188. grad2 = torch.Tensor([gradient2])
    189. self.grads['x'] = torch.cat([grad1, grad2])
    190. class Visualization3D(animation.FuncAnimation):
    191. """ 绘制动态图像,可视化参数更新轨迹 """
    192. def __init__(self, *xy_values, z_values, labels=[], colors=[], fig, ax, interval=600, blit=True, **kwargs):
    193. """
    194. 初始化3d可视化类
    195. 输入:
    196. xy_values:三维中x,y维度的值
    197. z_values:三维中z维度的值
    198. labels:每个参数更新轨迹的标签
    199. colors:每个轨迹的颜色
    200. interval:帧之间的延迟(以毫秒为单位)
    201. blit:是否优化绘图
    202. """
    203. self.fig = fig
    204. self.ax = ax
    205. self.xy_values = xy_values
    206. self.z_values = z_values
    207. frames = max(xy_value.shape[0] for xy_value in xy_values)
    208. self.lines = [ax.plot([], [], [], label=label, color=color, lw=2)[0]
    209. for _, label, color in zip_longest(xy_values, labels, colors)]
    210. super(Visualization3D, self).__init__(fig, self.animate, init_func=self.init_animation, frames=frames,
    211. interval=interval, blit=blit, **kwargs)
    212. def init_animation(self):
    213. # 数值初始化
    214. for line in self.lines:
    215. line.set_data([], [])
    216. # line.set_3d_properties(np.asarray([])) # 源程序中有这一行,加上会报错。 Edit by David 2022.12.4
    217. return self.lines
    218. def animate(self, i):
    219. # 将x,y,z三个数据传入,绘制三维图像
    220. for line, xy_value, z_value in zip(self.lines, self.xy_values, self.z_values):
    221. line.set_data(xy_value[:i, 0], xy_value[:i, 1])
    222. line.set_3d_properties(z_value[:i])
    223. return self.lines
    224. def train_f(model, optimizer, x_init, epoch):
    225. x = x_init
    226. all_x = []
    227. losses = []
    228. for i in range(epoch):
    229. all_x.append(copy.deepcopy(x.numpy())) # 浅拷贝 改为 深拷贝, 否则List的原值会被改变。 Edit by David 2022.12.4.
    230. loss = model(x)
    231. losses.append(loss)
    232. model.backward()
    233. optimizer.step()
    234. x = model.params['x']
    235. return torch.Tensor(np.array(all_x)), losses
    236. # 构建5个模型,分别配备不同的优化器
    237. model1 = OptimizedFunction3D()
    238. opt_gd = SimpleBatchGD(init_lr=0.01, model=model1)
    239. model2 = OptimizedFunction3D()
    240. opt_adagrad = Adagrad(init_lr=0.5, model=model2, epsilon=1e-7)
    241. model3 = OptimizedFunction3D()
    242. opt_rmsprop = RMSprop(init_lr=0.1, model=model3, beta=0.9, epsilon=1e-7)
    243. model4 = OptimizedFunction3D()
    244. opt_momentum = Momentum(init_lr=0.01, model=model4, rho=0.9)
    245. model5 = OptimizedFunction3D()
    246. opt_adam = Adam(init_lr=0.1, model=model5, beta1=0.9, beta2=0.99, epsilon=1e-7)
    247. models = [model1, model2, model3, model4, model5]
    248. opts = [opt_gd, opt_adagrad, opt_rmsprop, opt_momentum, opt_adam]
    249. x_all_opts = []
    250. z_all_opts = []
    251. # 使用不同优化器训练
    252. for model, opt in zip(models, opts):
    253. x_init = torch.FloatTensor([2, 3])
    254. x_one_opt, z_one_opt = train_f(model, opt, x_init, 150) # epoch
    255. # 保存参数值
    256. x_all_opts.append(x_one_opt.numpy())
    257. z_all_opts.append(np.squeeze(z_one_opt))
    258. # 使用numpy.meshgrid生成x1,x2矩阵,矩阵的每一行为[-3, 3],以0.1为间隔的数值
    259. x1 = np.arange(-3, 3, 0.1)
    260. x2 = np.arange(-3, 3, 0.1)
    261. x1, x2 = np.meshgrid(x1, x2)
    262. init_x = torch.Tensor(np.array([x1, x2]))
    263. model = OptimizedFunction3D()
    264. # 绘制 f_3d函数 的 三维图像
    265. fig = plt.figure()
    266. ax = plt.axes(projection='3d')
    267. X = init_x[0].numpy()
    268. Y = init_x[1].numpy()
    269. Z = model(init_x).numpy() # 改为 model(init_x).numpy() David 2022.12.4
    270. ax.plot_surface(X, Y, Z, cmap='rainbow')
    271. ax.set_xlabel('x1')
    272. ax.set_ylabel('x2')
    273. ax.set_zlabel('f(x1,x2)')
    274. labels = ['SGD', 'AdaGrad', 'RMSprop', 'Momentum', 'Adam']
    275. colors = ['#f6373c', '#f6f237', '#45f637', '#37f0f6', '#000000']
    276. animator = Visualization3D(*x_all_opts, z_values=z_all_opts, labels=labels, colors=colors, fig=fig, ax=ax)
    277. ax.legend(loc='upper left')
    278. plt.show()
    279. animator.save('animation.gif') # 效果不好,估计被挡住了…… 有待进一步提高 Edit by David 2022.12.4

  • 相关阅读:
    什么软件可以识别图片上的文字?分享三个实用的识别软件
    二叉树【Java】
    RocketMQ笔记3
    mysql函数获取全路径
    transition和animation的区别?
    信道复用技术
    从此再也不用怕崩溃闪退了
    axios+跨域-笔记
    APP自动化之Poco框架
    Lua04 实例列举 ESB2CP的报文转换流程
  • 原文地址:https://blog.csdn.net/qq_38975453/article/details/128179041