• NNDL实验 优化算法3D轨迹 鱼书例题3D版


    这张图在网络上很流行。代码源自:

    深度学习入门:基于Python的理论与实现 (ituring.com.cn)

     2D版讲解:NNDL 作业11:优化算法比较

    调整学习率等超参数,观察动画,可以加深对各种算法的理解。

    配合实验的模型,CS231的模型,基本可以掌握各类算法特点。

    NNDL实验 优化算法3D轨迹程序改编自《神经网络与深度学习:案例与实践》(Paddle版)https://blog.csdn.net/qq_38975453/article/details/128179041NNDL实验 优化算法3D轨迹 复现cs231经典动画这个动画很有名气,学习深度学习的朋友大多都见过复现动画,向经典致敬~https://blog.csdn.net/qq_38975453/article/details/128192767真正的了解算法之后,才能在合适的时候选择合适的算法。

    3D版展示:

     

     

     源代码:

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

  • 相关阅读:
    SSM学习——Spring事务(9)
    css定位
    hadoop解决数据倾斜的方法
    简化城镇开发边界基数统计工作——基于ModelBuilder的“基数汇总范围图层”的制作思路
    计算机网络网络层数据链路层协议详解
    java面试题-设计模式基础
    MySQL高级篇知识点——索引优化与查询优化
    如何有效的进行代码测试呢?
    java计算机毕业设计列车票务信息管理系统源码+数据库+lw文档+系统
    c++二叉树的进阶--二叉搜索树
  • 原文地址:https://blog.csdn.net/qq_38975453/article/details/128200967