diff --git a/optimizer/README.md b/optimizer/README.md new file mode 100644 index 0000000000000000000000000000000000000000..30b2e28828f00db65b00569d5d06f7107149f7b1 --- /dev/null +++ b/optimizer/README.md @@ -0,0 +1,825 @@ +# 优化实验 + +## 实验介绍 + +本实验主要介绍如何使用MindSpore进行优化实验。共包含两个实验分别为: + +- 实验一:利用不同优化器求解函数极值点实验 +- 实验二:鸢尾花数据在不同优化器下的分类实验 + +通过分析无优化器、SGD优化器、Momentum优化器、Adam优化器模型在求极值点和分类任务下的结果 ,得到不同优化器的作用。 + +## 实验目的 + +- 掌握MindSpore中优化器的使用及其作用。 +- 了解如何使用MindSpore进行SGD、Momentum、Adam优化器实验。 +- 了解不同优化器原理。 + +## 预备知识 + +- 熟练使用Python。 +- 具备一定的深度学习理论知识,如感知机、损失函数、优化器,训练策略等。 +- 了解华为云的基本使用方法,包括[ModelArts(AI开发平台)](https://www.huaweicloud.com/product/modelarts.html)、[训练作业](https://support.huaweicloud.com/engineers-modelarts/modelarts_23_0046.html)等功能。华为云官网:https://www.huaweicloud.com +- 了解并熟悉MindSpore AI计算框架,MindSpore官网:https://www.mindspore.cn/ + +## 实验环境 + +- MindSpore 0.5.0(MindSpore版本会定期更新,本指导也会定期刷新,与版本配套); +- 华为云ModelArts:ModelArts是华为云提供的面向开发者的一站式AI开发平台,集成了昇腾AI处理器资源池,用户可以在该平台下体验MindSpore。ModelArts官网:https://www.huaweicloud.com/product/modelarts.html + +## 实验准备 + +### 创建OBS桶 + +本实验需要使用华为云OBS存储脚本,可以参考[快速通过OBS控制台上传下载文件](https://support.huaweicloud.com/qs-obs/obs_qs_0001.html)了解使用OBS创建桶、上传文件、下载文件的使用方法。 + +> **提示:** 华为云新用户使用OBS时通常需要创建和配置“访问密钥”,可以在使用OBS时根据提示完成创建和配置。也可以参考[获取访问密钥并完成ModelArts全局配置](https://support.huaweicloud.com/prepare-modelarts/modelarts_08_0002.html)获取并配置访问密钥。 + +创建OBS桶的参考配置如下: + +- 区域:华北-北京四 +- 数据冗余存储策略:单AZ存储 +- 桶名称:全局唯一的字符串 +- 存储类别:标准存储 +- 桶策略:公共读 +- 归档数据直读:关闭 +- 企业项目、标签等配置:免 + +### 数据集准备 + +#### 求函数极值点 + +为了评价优化器的特性,我们模拟了一个目标函数求极值过程。定义不同优化器求其极值,与目标结果比较。 + +目标函数Beale 公式: + +$$ +f(x_1,x_2)=(1.5−x_1+x_1x_2)^2+(2.25−x_1+x_1x_2^2)^2+(2.625−x_1+x_1x_2^3)^2 +$$ + +表面如下图: +
+ +
+ +方程极值点$ (x_1, x_2) = (3, 0.5) $ + +我们需要通过各种不同的优化器来优化目标函数。优化器的主要目的是通过迭代找到目标函数的极小值或者极大值。常用的优化器有:SGD、Momentum、NAG、Adagrad、Adam等。 + +定义目标函数beale、目标函数的偏导函数dbeale_dx,并画出目标函数和极值点 + +```python +import numpy as np +import matplotlib.pyplot as plt +import matplotlib.colors as plt_cl + +# ------------------定义目标函数beale、目标函数的偏导函数dbeale_dx,并画出目标函数--------------------- +# 定义函数beale +def beale(x1, x2): + return (1.5 - x1 + x1 * x2) ** 2 + (2.25 - x1 + x1 * x2 ** 2) ** 2 + (2.625 - x1 + x1 * x2 ** 3) ** 2 + +# 定义函数beale的偏导 +def dbeale_dx(x1, x2): + dfdx1 = 2 * (1.5 - x1 + x1 * x2) * (x2 - 1) + 2 * (2.25 - x1 + x1 * x2 ** 2) * (x2 ** 2 - 1) + 2 * ( + 2.625 - x1 + x1 * x2 ** 3) * (x2 ** 3 - 1) + dfdx2 = 2 * (1.5 - x1 + x1 * x2) * x1 + 2 * (2.25 - x1 + x1 * x2 ** 2) * (2 * x1 * x2) + 2 * ( + 2.625 - x1 + x1 * x2 ** 3) * (3 * x1 * x2 ** 2) + return dfdx1, dfdx2 +step_x1, step_x2 = 0.2, 0.2 +X1, X2 = np.meshgrid(np.arange(-5, 5 + step_x1, step_x1), + np.arange(-5, 5 + step_x2, step_x2)) +Y = beale(X1, X2) +print("目标结果 (x_1, x_2) = (3, 0.5)") + +# 定义画图函数 +def gd_plot(x_traj): + plt.rcParams['figure.figsize'] = [6, 6] + plt.contour(X1, X2, Y, levels=np.logspace(0, 6, 30), + norm=plt_cl.LogNorm(), cmap=plt.cm.jet) + plt.title('2D Contour Plot of Beale function(Momentum)') + plt.xlabel('$x_1$') + plt.ylabel('$x_2$') + plt.axis('equal') + plt.plot(3, 0.5, 'k*', markersize=10) + if x_traj is not None: + x_traj = np.array(x_traj) + plt.plot(x_traj[:, 0], x_traj[:, 1], 'k-') + plt.show() + +gd_plot(None) +``` + + 目标结果 (x_1, x_2) = (3, 0.5) + +![png](images/gold.png) + +#### 鸢尾花分类数据集 + +鸢尾花数据集(Iris Dataset)是模式识别最著名的数据集之一。共收集了三类鸢尾花,即Setosa鸢尾花、Versicolour鸢尾花和Virginica鸢尾花,每一类鸢尾花收集了50条样本记录,共计150条。 + +数据集包括4个属性,分别为花萼的长、花萼的宽、花瓣的长和花瓣的宽。对花瓣我们可能比较熟悉,花萼是什么呢?花萼是花冠外面的绿色被叶,在花尚未开放时,保护着花蕾。四个属性的单位都是cm,属于数值变量,1个标签,表示鸢尾花的分类。 + +在Iris数据集的官网[Iris Data Set](http://archive.ics.uci.edu/ml/datasets/Iris)上下载[iris.data](http://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data)文件。 + +### 脚本准备 + +从[课程gitee仓库](https://gitee.com/mindspore/course)上下载本实验相关脚本。 + +### 上传文件 + +将脚本和数据集上传到OBS桶中,组织为如下形式: + +``` +optimizer +├── main1.py +├── main2.py +└── iris + └── iris.data +``` + +## 实验步骤 + +### 实验一:求函数极值点实验 + +#### 无优化器模型训练、测试、预测 + +原理:无优化器训练不更新参数,不管训练多少次,模型的参数没有发生变化。模型效果与模型初始化参数关系较大。 + +下面是不使用优化器求解Beale 公式极值点实现。 + +```python +# ------------------------------------------------------------无优化器------------------------------------------- +def gd_no(df_dx, x0, conf_para=None): + if conf_para is None: + conf_para = {} + conf_para.setdefault('n_iter', 1000) # 迭代次数 + conf_para.setdefault('learning_rate', 0.001) # 设置学习率 + x_traj = [] + x_traj.append(x0) + v = np.zeros_like(x0) + for iter in range(1, conf_para['n_iter'] + 1): + x_traj.append(x_traj[-1]) + return x_traj + +x0 = np.array([1.0, 1.5]) +conf_para_no = {'n_iter': 2000, 'learning_rate': 0.005} +x_traj_no = gd_no(dbeale_dx, x0, conf_para_no) +print("无优化器求得极值点 (x_1, x_2) = (%s, %s)" % (x_traj_no[-1][0], x_traj_no[-1][1])) +gd_plot(x_traj_no) +``` + +``` +无优化器求得极值点 (x_1, x_2) = (1.0, 1.5) +``` + +#### SGD优化器模型训练、测试、预测 + +梯度下降法:梯度下降(gradient descent)在机器学习中应用十分的广泛,是求解无约束优化问题最简单和最古老的方法之一。通过迭代,参数向梯度的反方向更新,直到收敛。 + +$$ +W_{new} = W - \eta\frac{\partial J(W)}{\partial W} +$$ + +其中$ \frac{\partial J(W)}{\partial W} $ 表示损失函数 J 关于参数W的梯度;$ \eta $表示学习率; + +>**缺点:** +> +>- 有可能会陷入局部最小值; +>- 不会收敛,最终会一直在最小值附近波动,并不会达到最小值并停留在此; +>- 下降速度慢; +>- 选择合适的学习率比较困难; +>- 在所有方向上统一的缩放梯度,不适用于稀疏数据; + +下面是使用SGD优化器求解Beale 公式极值点实现。 + +```python +# ------------------------------------------------------------SGD------------------------------------------- +def gd_sgd(df_dx, x0, conf_para=None): + if conf_para is None: + conf_para = {} + conf_para.setdefault('n_iter', 1000) # 迭代次数 + conf_para.setdefault('learning_rate', 0.001) # 设置学习率 + x_traj = [] + x_traj.append(x0) + v = np.zeros_like(x0) + for iter in range(1, conf_para['n_iter'] + 1): + dfdx = np.array(df_dx(x_traj[-1][0], x_traj[-1][1])) + v = - conf_para['learning_rate'] * dfdx + x_traj.append(x_traj[-1] + v) + return x_traj + +x0 = np.array([1.0, 1.5]) +conf_para_sgd = {'n_iter': 2000, 'learning_rate': 0.005} +x_traj_sgd = gd_sgd(dbeale_dx, x0, conf_para_sgd) +print("SGD求得极值点 (x_1, x_2) = (%s, %s)" % (x_traj_sgd[-1][0], x_traj_sgd[-1][1])) +gd_plot(x_traj_sgd) +``` + +``` +SGD求得极值点 (x_1, x_2) = (2.981957257903412, 0.4954609564102124) +``` + +![png](images/sgd.png) + +#### Momentum优化器模型训练、测试、预测 + +Momentum:是动量优化法中的一种(Momentum、NAG),即使用动量(Momentum)的随机梯度下降法(SGD),主要思想是引入一个积攒历史梯度信息的动量来加速SGD。其参数优化公式如下所示: + +$$ +v_{new} = \gamma v - \eta\frac{\partial J(W)}{\partial W} \\ +W_{new} = W + v_{new} +$$ + +其中$ \frac{\partial J(W)}{\partial W} $ 表示损失函数 J 关于参数W的梯度;$ \eta $表示学习率;$ \gamma $表示动量的大小,一般取值为0.9。 + +这个算法和之前的梯度下降法(SGD)相比,唯一不同的就是多了一个$ \gamma v$。这一改动使Momentum会观察历史梯度,若当前梯度的方向与历史梯度一致(表明当前样本不太可能为异常点),则会增强这个方向的梯度;若当前梯度与历史梯方向不一致,则梯度会衰减。一种形象的解释是:我们把一个球推下山,球在下坡时积聚动量,在途中变得越来越快,γ可视为空气阻力,若球的方向发生变化,则动量会衰减。 + +>**优点:** +> +>- 参考了历史梯度,增加了稳定性; +>- 由于引入加速动量,加快收敛速度。下降初期时,使用上一次参数更新,下降方向一致,乘上较大的$ \gamma $能够进行很好的加速; +>- 还有一定摆脱局部最优的能力。下降中后期时,在局部最小值来回震荡的时候,梯度趋近于0,$ \gamma $使得更新幅度增大,跳出陷阱(局部最优); + +下面是使用Momentum优化器求解Beale 公式极值点实现。 + +```python +# -------------------------------------------------------Momentum--------------------------------- +def gd_momentum(df_dx, x0, conf_para=None): + if conf_para is None: + conf_para = {} + conf_para.setdefault('n_iter', 1000) # 迭代次数 + conf_para.setdefault('learning_rate', 0.001) # 设置学习率 + conf_para.setdefault('momentum', 0.9) # 设置动量参数 + x_traj = [] + x_traj.append(x0) + v = np.zeros_like(x0) + for iter in range(1, conf_para['n_iter'] + 1): + dfdx = np.array(df_dx(x_traj[-1][0], x_traj[-1][1])) + v = conf_para['momentum'] * v - conf_para['learning_rate'] * dfdx + x_traj.append(x_traj[-1] + v) + return x_traj + +x0 = np.array([1.0, 1.5]) +conf_para_momentum = {'n_iter': 500, 'learning_rate': 0.005} +x_traj_momentum = gd_momentum(dbeale_dx, x0, conf_para_momentum) +print("Momentum求得极值点 (x_1, x_2) = (%s, %s)" % (x_traj_momentum[-1][0], x_traj_momentum[-1][1])) +gd_plot(x_traj_momentum) +``` + +``` +Momentum求得极值点 (x_1, x_2) = (3.0004236971560485, 0.5001057348328146) +``` + +![png](images/mom.png) + +#### 自适应优化器模型训练、测试、预测 + +自适应学习率优化算法主要有:AdaGrad算法,RMSProp算法,Adam算法以及AdaDelta算法。 + +**AdaGrad** + +AdaGrad的基本思想是对每个变量用不同的学习率。这个学习率在一开始比较大,用于快速梯度下降。随着优化过程的进行,对于已经下降很多的变量,则减缓学习率,对于还没怎么下降的变量,则保持一个较大的学习率。其参数优化公式如下所示: + +$$ +G_{new} = G + (\frac{\partial J(W)}{\partial W})^2 \\ +W_{new} = W - \frac{\eta}{(\sqrt{G_{new}} + \varepsilon)}\cdot\frac{\partial J(W)}{\partial W} +$$ + +其中$ \frac{\partial J(W)}{\partial W} $ 表示损失函数 J 关于参数W的梯度;$ \eta $表示学习率,一般取值0.01;$ \varepsilon $是一个很小的数,防止分母为0;$G_{new}$表示了前t 步参数$W$梯度的平方累加。把沿路的Gradient的平方根,作为Regularizer。分母作为Regularizer项的工作机制如下: + +1. 训练前期,梯度较小,使得Regularizer项很大,放大梯度。[激励阶段] +2. 训练后期,梯度较大,使得Regularizer项很小,缩小梯度。[惩罚阶段] + +>**优点:** +> +>- 在数据分布稀疏的场景,能更好利用稀疏梯度的信息,比标准的SGD算法更有效地收敛; +>- 对每个变量用不同的学习率,对输入参数学习率的依赖小,容易调节参数; + +>**缺点:** +> +>- 主要缺陷来自分母项的对梯度平方不断累积,随之时间步地增加,分母项越来越大,最终导致学习率收缩到太小无法进行有效更新; + +**RMSProp** + +为了解决 Adagrad 学习率急剧下降问题,RMSProp保留过去梯度的微分平方数项,旨在消除梯度下降中的摆动。与Momentum的效果一样,某一维度的导数比较大,则指数加权平均就大,某一维度的导数比较小,则其指数加权平均就小,这样就保证了各维度导数都在一个量级,进而减少了摆动。允许使用一个更大的学习率η。其参数优化公式如下所示: + +$$ +v_{new} = \gamma\cdot v + (1 - \gamma)\cdot{(\frac{\partial J(W)}{\partial W})}^2 \\ +W_{new} = W - \frac{\eta}{(\sqrt{v_{new}} + \varepsilon)}(\frac{\partial J(W)}{\partial W}) +$$ + +其中$ \frac{\partial J (W)}{\partial W} $ 表示损失函数 J 关于参数W的梯度;$ \eta $表示学习率,一般取值0.001;$ \varepsilon $是一个很小的数,防止分母为0;$ \gamma $表示动量的大小,一般取值为0.9。 + +**Adam** + +Adam算法是另一种计算每个参数的自适应学习率的方法。相当于 RMSprop + Momentum。除了像RMSprop存储了过去梯度的平方 $ v_t $ 的指数衰减平均值 ,也像 momentum 一样保持了过去梯度$ m_t $ 的指数衰减平均值。其参数优化公式如下所示: + +$$ +m_{new} = \beta _1 m + (1 - \beta _1)(\frac{\partial J(W)}{\partial W}) +$$ + +$$ +v_{new} = \beta _2 v + (1 - \beta _2)(\frac{\partial J(W)}{\partial W})^2 +$$ + +由于$\frac{m_0}{v_0}$初始化为0,会导致$\frac{m_{new}}{v_{new}}$偏向于0,尤其在训练初期阶段,所以,此处需要对梯度均值$\frac{m_{new}}{v_{new}}$进行偏差纠正,降低偏差对训练初期的影响。 + +$$ +\hat{m_{new}} = m_{new} / (1 - \beta _1) +$$ + +$$ +\hat{v_{new}} = v_{new} / (1 - \beta _2) +$$ + +$$ +W_{new} = W - \eta\frac{1}{\sqrt{\hat{v_{new}}} + \varepsilon}\hat{m_{new}} +$$ + +其中$ \frac{\partial J (W)}{\partial W} $ 表示损失函数 J 关于参数W的梯度;$ \eta $表示学习率,一般取值0.001;$ \varepsilon $是一个很小的数,一般取值10e−8,防止分母为0;$ \beta _1 $ $ \beta _2 $分别表示一阶和二阶动量的大小,一般取值为$ \beta _1 = 0.9 $ $ \beta _2 = 0.99 $。 + +>**优点** +> +>- 能够克服AdaGrad梯度急剧减小的问题,在很多应用中都展示出优秀的学习率自适应能力; +>- 实现简单,计算高效,对内存需求少; +>- 参数的更新不受梯度的伸缩变换影响; +>- 超参数具有很好的解释性,且通常无需调整或仅需很少的微调; +>- 更新的步长能够被限制在大致的范围内(初始学习率); +>- 能自然地实现步长退火过程(自动调整学习率); +>- 很适合应用于大规模的数据及参数的场景; +>- 适用于不稳定目标函数; +>- 适用于梯度稀疏或梯度存在很大噪声的问题; + +下面是使用自适应优化器求解Beale 公式极值点实现。 + +```python +# ----------------------------------------------------adagrad----------------------------- +def gd_adagrad(df_dx, x0, conf_para=None): + if conf_para is None: + conf_para = {} + conf_para.setdefault('n_iter', 1000) # 迭代次数 + conf_para.setdefault('learning_rate', 0.001) # 学习率 + conf_para.setdefault('epsilon', 1e-7) + x_traj = [] + x_traj.append(x0) + r = np.zeros_like(x0) + for iter in range(1, conf_para['n_iter'] + 1): + dfdx = np.array(df_dx(x_traj[-1][0], x_traj[-1][1])) + r += dfdx ** 2 + x_traj.append(x_traj[-1] - conf_para['learning_rate'] / (np.sqrt(r) + conf_para['epsilon']) * dfdx) + return x_traj + +x0 = np.array([1.0, 1.5]) +conf_para_adag = {'n_iter': 500, 'learning_rate': 2} +x_traj_adag = gd_adagrad(dbeale_dx, x0, conf_para_adag) +print("Adagrad求得极值点 (x_1, x_2) = (%s, %s)" % (x_traj_adag[-1][0], x_traj_adag[-1][1])) +gd_plot(x_traj_adag) +``` + + Adagrad求得极值点 (x_1, x_2) = (2.9993173156940776, 0.49982846432011524) + +![png](images/ada.png) + +从结果可以看出:无优化器参数不更新,求函数极值点无效。SGD、Momentum、自适应优化器求得的极值点与目标点(3.0, 0.5)较近。SGD、Momentum、自适应优化器求解极值点方法有效。其中SGD优化器实验需要的迭代次数2000多,相比与后边Momentum优化器多迭代1500次,证明了SGD优化器收敛速度慢。从图像可以看出自适应优化器对收敛方向把握比较好。 + +### 实验二:鸢尾花分类实验 + +#### 导入MindSpore模块和辅助模块 + +```python +import csv +import os +import time + +import numpy as np +from easydict import EasyDict as edict +from matplotlib import pyplot as plt + +import mindspore +from mindspore import nn +from mindspore import context +from mindspore import dataset +from mindspore.train.callback import TimeMonitor, LossMonitor +from mindspore import Tensor +from mindspore.train import Model +from mindspore.train.callback import ModelCheckpoint, CheckpointConfig + +context.set_context(mode=context.GRAPH_MODE, device_target="Ascend") +``` + +#### 变量定义 + +```python +cfg = edict({ + 'data_size': 150, + 'train_size': 120, #训练集大小 + 'test_size': 30 , #测试集大小 + 'feature_number': 4, #输入特征数 + 'num_class': 3, #分类类别 + 'batch_size': 30, + 'data_dir': 'iris.data', + 'save_checkpoint_steps': 5, #多少步保存一次模型 + 'keep_checkpoint_max': 1, #最多保存多少个模型 + 'out_dir_no_opt': './model_iris/no_opt', #保存模型路径,无优化器模型 + 'out_dir_sgd': './model_iris/sgd', #保存模型路径,SGD优化器模型 + 'out_dir_momentum': './model_iris/momentum', #保存模型路径,momentum模型 + 'out_dir_adam': './model_iris/adam', #保存模型路径,adam优化器模型 + 'output_prefix': "checkpoint_fashion_forward" #保存模型文件名 +}) +``` + +#### 读取数据并预处理 + +读取Iris数据集`iris.data`,并作检查。 + +```python +with open(cfg.data_dir) as csv_file: + data = list(csv.reader(csv_file, delimiter=',')) +print(data[0:5]); print(data[50:55]); print(data[100:105]) # 打印部分数据 +``` + + [['5.1', '3.5', '1.4', '0.2', 'Iris-setosa'], ['4.9', '3.0', '1.4', '0.2', 'Iris-setosa'], ['4.7', '3.2', '1.3', '0.2', 'Iris-setosa'], ['4.6', '3.1', '1.5', '0.2', 'Iris-setosa'], ['5.0', '3.6', '1.4', '0.2', 'Iris-setosa']] + [['7.0', '3.2', '4.7', '1.4', 'Iris-versicolor'], ['6.4', '3.2', '4.5', '1.5', 'Iris-versicolor'], ['6.9', '3.1', '4.9', '1.5', 'Iris-versicolor'], ['5.5', '2.3', '4.0', '1.3', 'Iris-versicolor'], ['6.5', '2.8', '4.6', '1.5', 'Iris-versicolor']] + [['6.3', '3.3', '6.0', '2.5', 'Iris-virginica'], ['5.8', '2.7', '5.1', '1.9', 'Iris-virginica'], ['7.1', '3.0', '5.9', '2.1', 'Iris-virginica'], ['6.3', '2.9', '5.6', '1.8', 'Iris-virginica'], ['6.5', '3.0', '5.8', '2.2', 'Iris-virginica']] + +共150条数据,将数据集的4个属性作为自变量X。将数据集的3个类别映射为{0, 1,2},作为因变量Y。 + +```python +label_map = {'Iris-setosa': 0,'Iris-versicolor': 1,'Iris-virginica':2 } +X = np.array([[float(x) for x in s[:-1]] for s in data[:cfg.data_size]], np.float32) +Y = np.array([label_map[s[-1]] for s in data[:cfg.data_size]], np.int32) +``` + +取样本的前两个属性进行2维可视化。发现Iris-setosa类和其他两类是线性可分的。Iris-versicolor类和Iris-virginica类是线性不可分的。 + +```python +plt.scatter(X[:50, 0], X[:50, 1], label='Iris-setosa') +plt.scatter(X[50:100, 0], X[50:100, 1], label='Iris-versicolor') +plt.scatter(X[100:, 0], X[100:, 1], label='Iris-virginica') +plt.xlabel('sepal length') +plt.ylabel('sepal width') +plt.legend() +``` + +![png](images/iris.png) + +将数据集分为训练集120条,测试集30条。 + +```python +# 将数据集分为训练集120条,测试集30条。 +train_idx = np.random.choice(cfg.data_size, cfg.train_size, replace=False) +test_idx = np.array(list(set(range(cfg.data_size)) - set(train_idx))) +X_train, Y_train = X[train_idx], Y[train_idx] +X_test, Y_test = X[test_idx], Y[test_idx] +print('训练数据x尺寸:', X_train.shape) +print('训练数据y尺寸:', Y_train.shape) +print('测试数据x尺寸:', X_test.shape) +print('测试数据y尺寸:', Y_test.shape) +``` + + 训练数据x尺寸: (120, 4) + 训练数据y尺寸: (120,) + 测试数据x尺寸: (30, 4) + 测试数据y尺寸: (30,) + +使用MindSpore GeneratorDataset接口将numpy.ndarray类型的数据转换为Dataset。 + +```python +def gen_data(X_train, Y_train, epoch_size): + XY_train = list(zip(X_train, Y_train)) + ds_train = dataset.GeneratorDataset(XY_train, ['x', 'y']) + ds_train.set_dataset_size(cfg.train_size) + ds_train = ds_train.shuffle(buffer_size=cfg.train_size).batch(cfg.batch_size, drop_remainder=True) + XY_test = list(zip(X_test, Y_test)) + ds_test = dataset.GeneratorDataset(XY_test, ['x', 'y']) + ds_test.set_dataset_size(cfg.test_size) + ds_test = ds_test.shuffle(buffer_size=cfg.test_size).batch(cfg.test_size, drop_remainder=True) + return ds_train, ds_test +``` + +为了更好的观察loss变化,重写Lossmonitor。 + +```python +class SubLossMonitor(LossMonitor): + def epoch_end(self, run_context): + pass + + def step_end(self, run_context): + cb_params = run_context.original_args() + step_loss = cb_params.net_outputs + + if isinstance(step_loss, (tuple, list)) and isinstance(step_loss[0], Tensor): + step_loss = step_loss[0] + if isinstance(step_loss, Tensor): + step_loss = np.mean(step_loss.asnumpy()) + + self.losses.append(step_loss) + cur_step_in_epoch = int((cb_params.cur_step_num - 1) % cb_params.batch_num) + 1 + + if isinstance(step_loss, float) and (np.isnan(step_loss) or np.isinf(step_loss)): + raise ValueError("Epoch: [{:3d}/{:3d}], step: [{:5d}/{:5d}]. " + "Invalid loss, terminating training.".format( + cb_params.cur_epoch_num - 1, cb_params.epoch_num, + cur_step_in_epoch, cb_params.batch_num)) + + if self._per_print_times != 0 and cb_params.cur_step_num % self._per_print_times == 0: + print("epoch: {} step {}, loss: {}, avg loss: {:5.3f}".format(cb_params.cur_epoch_num, + cur_step_in_epoch, + step_loss, np.mean(self.losses)), flush=True) +``` + +#### 定义训练、测试、预测过程 + +```python +# 训练 +def train(network, net_opt, ds_train, prefix, directory, print_times): + net_loss = nn.SoftmaxCrossEntropyWithLogits(is_grad=False, sparse=True, reduction="mean") + model = Model(network, loss_fn=net_loss, optimizer=net_opt, metrics={"acc"}) + loss_cb = SubLossMonitor(per_print_times=print_times) + config_ck = CheckpointConfig(save_checkpoint_steps=cfg.save_checkpoint_steps, + keep_checkpoint_max=cfg.keep_checkpoint_max) + ckpoint_cb = ModelCheckpoint(prefix=prefix, directory=directory, config=config_ck) + print("============== Starting Training ==============") + model.train(epoch_size, ds_train, callbacks=[ckpoint_cb, loss_cb], dataset_sink_mode=False) + return model +``` + +```python +# 评估预测 +def eval_predict(model, ds_test): + # 使用测试集评估模型,打印总体准确率 + metric = model.eval(ds_test) + print(metric) + # 预测 + test_ = ds_test.create_dict_iterator().get_next() + test = Tensor(test_['x'], mindspore.float32) + predictions = model.predict(test) + predictions = predictions.asnumpy() + for i in range(10): + p_np = predictions[i, :] + p_list = p_np.tolist() + print('第' + str(i) + '个sample预测结果:', p_list.index(max(p_list)), ' 真实结果:', test_['y'][i]) +``` + +#### 无优化器训练、测试、预测 + +```python +# --------------------------------------------------无优化器----------------------------------- +epoch_size = 20 +print('------------------无优化器--------------------------') +# 数据 +ds_train, ds_test = gen_data(X_train, Y_train, epoch_size) +# 定义网络并训练 +network = nn.Dense(cfg.feature_number, cfg.num_class) +model = train(network, None, ds_train, "checkpoint_no_opt", cfg.out_dir_no_opt, 4) +# 评估预测 +eval_predict(model, ds_test) +``` + + ============== Starting Training ============== + epoch: 1 step 4, loss: 1.0889194011688232, avg loss: 1.085 + epoch: 2 step 4, loss: 1.0786006450653076, avg loss: 1.085 + epoch: 3 step 4, loss: 1.087601900100708, avg loss: 1.085 + epoch: 4 step 4, loss: 1.0797476768493652, avg loss: 1.085 + epoch: 5 step 4, loss: 1.0873146057128906, avg loss: 1.085 + epoch: 6 step 4, loss: 1.0899147987365723, avg loss: 1.085 + epoch: 7 step 4, loss: 1.0889571905136108, avg loss: 1.085 + epoch: 8 step 4, loss: 1.0873101949691772, avg loss: 1.085 + epoch: 9 step 4, loss: 1.0905265808105469, avg loss: 1.085 + epoch: 10 step 4, loss: 1.0831623077392578, avg loss: 1.085 + epoch: 11 step 4, loss: 1.0847382545471191, avg loss: 1.085 + epoch: 12 step 4, loss: 1.079599142074585, avg loss: 1.085 + epoch: 13 step 4, loss: 1.086871862411499, avg loss: 1.085 + epoch: 14 step 4, loss: 1.0913140773773193, avg loss: 1.085 + epoch: 15 step 4, loss: 1.0870290994644165, avg loss: 1.085 + epoch: 16 step 4, loss: 1.0817103385925293, avg loss: 1.085 + epoch: 17 step 4, loss: 1.0873748064041138, avg loss: 1.085 + epoch: 18 step 4, loss: 1.0746941566467285, avg loss: 1.085 + epoch: 19 step 4, loss: 1.0819764137268066, avg loss: 1.085 + epoch: 20 step 4, loss: 1.0972342491149902, avg loss: 1.085 + {'acc': 0.43333333333333335} + 第0个sample预测结果: 2 真实结果: 2 + 第1个sample预测结果: 0 真实结果: 0 + 第2个sample预测结果: 0 真实结果: 0 + 第3个sample预测结果: 2 真实结果: 1 + 第4个sample预测结果: 2 真实结果: 1 + 第5个sample预测结果: 2 真实结果: 2 + 第6个sample预测结果: 2 真实结果: 1 + 第7个sample预测结果: 2 真实结果: 2 + 第8个sample预测结果: 2 真实结果: 2 + 第9个sample预测结果: 2 真实结果: 0 + +#### SGD优化器模型训练、测试、预测 + +```python +# ---------------------------------------------------SGD------------------------------------- +epoch_size = 200 +lr = 0.01 +print('-------------------SGD优化器-----------------------') +# 数据 +ds_train, ds_test = gen_data(X_train, Y_train, epoch_size) +# 定义网络并训练、测试、预测 +network = nn.Dense(cfg.feature_number, cfg.num_class) +net_opt = nn.SGD(network.trainable_params(), lr) +model = train(network, net_opt, ds_train, "checkpoint_sgd", cfg.out_dir_sgd, 40) +# 评估预测 +eval_predict(model, ds_test) +``` + +``` +============== Starting Training ============== +epoch: 10 step 4, loss: 0.9191734194755554, avg loss: 0.885 +epoch: 20 step 4, loss: 0.7677731513977051, avg loss: 0.764 +epoch: 30 step 4, loss: 0.7029662132263184, avg loss: 0.678 +epoch: 40 step 4, loss: 0.6191470623016357, avg loss: 0.633 +epoch: 50 step 4, loss: 0.5822886228561401, avg loss: 0.588 +epoch: 60 step 4, loss: 0.5903770923614502, avg loss: 0.562 +epoch: 70 step 4, loss: 0.48483023047447205, avg loss: 0.533 +epoch: 80 step 4, loss: 0.5035678148269653, avg loss: 0.514 +epoch: 90 step 4, loss: 0.44619810581207275, avg loss: 0.502 +epoch: 100 step 4, loss: 0.5394692420959473, avg loss: 0.483 +epoch: 110 step 4, loss: 0.4881630837917328, avg loss: 0.472 +epoch: 120 step 4, loss: 0.48909181356430054, avg loss: 0.462 +epoch: 130 step 4, loss: 0.48934054374694824, avg loss: 0.449 +epoch: 140 step 4, loss: 0.44362297654151917, avg loss: 0.438 +epoch: 150 step 4, loss: 0.4909963607788086, avg loss: 0.430 +epoch: 160 step 4, loss: 0.38809025287628174, avg loss: 0.421 +epoch: 170 step 4, loss: 0.43030044436454773, avg loss: 0.417 +epoch: 180 step 4, loss: 0.40883880853652954, avg loss: 0.408 +epoch: 190 step 4, loss: 0.4194946885108948, avg loss: 0.399 +epoch: 200 step 4, loss: 0.41460859775543213, avg loss: 0.397 +{'acc': 0.9666666666666667} +第0个sample预测结果: 0 真实结果: 0 +第1个sample预测结果: 0 真实结果: 0 +第2个sample预测结果: 0 真实结果: 0 +第3个sample预测结果: 0 真实结果: 0 +第4个sample预测结果: 1 真实结果: 1 +第5个sample预测结果: 1 真实结果: 1 +第6个sample预测结果: 2 真实结果: 2 +第7个sample预测结果: 2 真实结果: 2 +第8个sample预测结果: 2 真实结果: 2 +第9个sample预测结果: 2 真实结果: 2 +``` + +#### Momentum优化器模型训练、测试、预测 + +```python +# ----------------------------------------------------Momentum------------------------------- +epoch_size = 20 +lr = 0.01 +print('-------------------Momentum优化器-----------------------') +# 数据 +ds_train, ds_test = gen_data(X_train, Y_train, epoch_size) +# 定义网络并训练 +network = nn.Dense(cfg.feature_number, cfg.num_class) +net_opt = nn.Momentum(network.trainable_params(), lr, 0.9) +model = train(network, net_opt, ds_train, "checkpoint_momentum", cfg.out_dir_momentum, 4) +# 评估预测 +eval_predict(model, ds_test) +``` + + ============== Starting Training ============== + epoch: 1 step 4, loss: 1.0203474760055542, avg loss: 1.059 + epoch: 2 step 4, loss: 0.9105612635612488, avg loss: 0.989 + epoch: 3 step 4, loss: 0.808910608291626, avg loss: 0.863 + epoch: 4 step 4, loss: 0.7031147480010986, avg loss: 0.775 + epoch: 5 step 4, loss: 0.5979485511779785, avg loss: 0.680 + epoch: 6 step 4, loss: 0.6117547750473022, avg loss: 0.612 + epoch: 7 step 4, loss: 0.46994972229003906, avg loss: 0.569 + epoch: 8 step 4, loss: 0.5184788703918457, avg loss: 0.553 + epoch: 9 step 4, loss: 0.5600728988647461, avg loss: 0.514 + epoch: 10 step 4, loss: 0.46992775797843933, avg loss: 0.489 + epoch: 11 step 4, loss: 0.4271170496940613, avg loss: 0.474 + epoch: 12 step 4, loss: 0.49392855167388916, avg loss: 0.466 + epoch: 13 step 4, loss: 0.44602206349372864, avg loss: 0.442 + epoch: 14 step 4, loss: 0.45520901679992676, avg loss: 0.439 + epoch: 15 step 4, loss: 0.3911263346672058, avg loss: 0.421 + epoch: 16 step 4, loss: 0.4466801881790161, avg loss: 0.415 + epoch: 17 step 4, loss: 0.4483078718185425, avg loss: 0.421 + epoch: 18 step 4, loss: 0.4541526734828949, avg loss: 0.405 + epoch: 19 step 4, loss: 0.3124566078186035, avg loss: 0.397 + epoch: 20 step 4, loss: 0.43211615085601807, avg loss: 0.388 + {'acc': 1.0} + 第0个sample预测结果: 2 真实结果: 2 + 第1个sample预测结果: 2 真实结果: 2 + 第2个sample预测结果: 2 真实结果: 2 + 第3个sample预测结果: 1 真实结果: 1 + 第4个sample预测结果: 0 真实结果: 0 + 第5个sample预测结果: 0 真实结果: 0 + 第6个sample预测结果: 1 真实结果: 1 + 第7个sample预测结果: 0 真实结果: 0 + 第8个sample预测结果: 0 真实结果: 0 + 第9个sample预测结果: 0 真实结果: 0 + +#### Adam优化器模型训练、测试、预测 + +```python +# ----------------------------------------------------Adam----------------------------------- +epoch_size = 15 +lr = 0.1 +print('------------------Adam优化器--------------------------') +# 数据 +ds_train, ds_test = gen_data(X_train, Y_train, epoch_size) +# 定义网络并训练 +network = nn.Dense(cfg.feature_number, cfg.num_class) +net_opt = nn.Adam(network.trainable_params(), learning_rate=lr) +model = train(network, net_opt, ds_train, "checkpoint_adam", cfg.out_dir_adam, 4) +# 评估预测 +eval_predict(model, ds_test) +``` + +``` +============== Starting Training ============== +epoch: 1 step 4, loss: 0.779292106628418, avg loss: 0.987 +epoch: 2 step 4, loss: 0.6459528207778931, avg loss: 0.649 +epoch: 3 step 4, loss: 0.4705578088760376, avg loss: 0.528 +epoch: 4 step 4, loss: 0.44921058416366577, avg loss: 0.463 +epoch: 5 step 4, loss: 0.39382368326187134, avg loss: 0.412 +epoch: 6 step 4, loss: 0.35060781240463257, avg loss: 0.369 +epoch: 7 step 4, loss: 0.26220306754112244, avg loss: 0.346 +epoch: 8 step 4, loss: 0.270007461309433, avg loss: 0.324 +epoch: 9 step 4, loss: 0.2567645311355591, avg loss: 0.301 +epoch: 10 step 4, loss: 0.38261109590530396, avg loss: 0.314 +epoch: 11 step 4, loss: 0.3241625130176544, avg loss: 0.282 +epoch: 12 step 4, loss: 0.2498287558555603, avg loss: 0.253 +epoch: 13 step 4, loss: 0.23713502287864685, avg loss: 0.259 +epoch: 14 step 4, loss: 0.2676781117916107, avg loss: 0.253 +epoch: 15 step 4, loss: 0.1873977780342102, avg loss: 0.234 +{'acc': 1.0} +第0个sample预测结果: 2 真实结果: 2 +第1个sample预测结果: 2 真实结果: 2 +第2个sample预测结果: 2 真实结果: 2 +第3个sample预测结果: 0 真实结果: 0 +第4个sample预测结果: 0 真实结果: 0 +第5个sample预测结果: 1 真实结果: 1 +第6个sample预测结果: 2 真实结果: 2 +第7个sample预测结果: 2 真实结果: 2 +第8个sample预测结果: 0 真实结果: 0 +第9个sample预测结果: 0 真实结果: 0 +``` + +>**结果分析:** +> +>从无优化器、SGD、momentum、adam优化器的loss上看: +>1. 无优化器训练loss基本没有发生变化,测试结果效果差。多运行几次发现结果偏差太大,学员可以自己尝试。 +>2. SGD优化器loss下降速度很慢,而且在接近收敛处loss下降非常缓慢。增大学习率,减少迭代次数,会出现收敛到局部最优解的情况,学员可以自己尝试。 +>3. Momentum优化器loss下降速度较快,充分说明Momentum优化器改进了SGD收敛速度慢的问题。改变参数,比较不同学习率和迭代次数的结果,会发现该优化器稳定性很强,学习率容易选择。相比于SGD优化器容易调参。 +>4. adam优化器loss下降速度最快快,只需要15 epoch就可以达到收敛。改变模型学习率多跑几次,会发现,adam优化器可以适应不用的学习率,容易调节参数。 + +### 适配训练作业 + +创建训练作业时,运行参数会通过脚本传参的方式输入给脚本代码,脚本必须解析传参才能在代码中使用相应参数。如data_url和train_url,分别对应数据存储路径(OBS路径)和训练输出路径(OBS路径)。脚本对传参进行解析后赋值到args变量里,在后续代码里可以使用。 + +```python +import argparse +parser = argparse.ArgumentParser() +parser.add_argument('--data_url', required=True, default=None, help='Location of data.') +parser.add_argument('--train_url', required=True, default=None, help='Location of training outputs.') +args, unknown = parser.parse_known_args() +``` + +MindSpore暂时没有提供直接访问OBS数据的接口,需要通过MoXing提供的API与OBS交互。将OBS中存储的数据拷贝至执行容器: + +```python +import moxing as mox +mox.file.copy_parallel(src_url=os.path.join(args.data_url, 'iris.data'), dst_url='iris.data') +``` + +如需将训练输出(如模型Checkpoint)从执行容器拷贝至OBS,请参考: + +```python +import moxing as mox +mox.file.copy_parallel(src_url='model_iris', dst_url=args.train_url) +``` + +### 创建训练作业 + +可以参考[使用常用框架训练模型](https://support.huaweicloud.com/engineers-modelarts/modelarts_23_0238.html)来创建并启动训练作业。 + +>**创建训练作业的参考配置:** +> +>- 算法来源:常用框架->Ascend-Powered-Engine->MindSpore; +>- 代码目录:选择上述新建的OBS桶中的optimizer目录; +>- 启动文件:选择上述新建的OBS桶中的optimizer目录下的`main1.py/main2.py`; +>- 数据来源:数据存储位置->选择上述新建的OBS桶中的optimizer目录下的iris目录; +>- 训练输出位置:选择上述新建的OBS桶中的optimizer目录并在其中创建model_iris目录; +>- 作业日志路径:同训练输出位置; +>- 规格:Ascend:1*Ascend 910; +>- 其他均为默认; + +>**启动并查看训练过程:** +> +>1. 点击提交以开始训练; +>2. 在训练作业列表里可以看到刚创建的训练作业,在训练作业页面可以看到版本管理; +>3. 点击运行中的训练作业,在展开的窗口中可以查看作业配置信息,以及训练过程中的日志,日志会不断刷新,等训练作业完成后也可以下载日志到本地进行查看; +>4. 参考上述代码梳理,在日志中找到对应的打印信息,检查实验是否成功; + +## 实验小结 + +本实验目的是使用MindSpore对比不同优化器算法,通过比较不同优化器实验结果,得到如下结论: + +> - 无优化器条件训练,不更新参数,训练loss基本不变。模型与初始化参数关系大,随机性较强,效果差,不适合应用; +> - 有优化器条件下训练,更新模型参数。训练loss改变; +> - SGD优化器收敛速度慢,参数不容易调节; +> - Momentum优化器是SGD加入历史梯度动量,增加了稳定性,收敛速度有明显提高,参数较SGD容易调节; +> - Adam优化器是 RMSprop + Momentum。可以适应不同学习率,可以自动为不同参数调节学习率。稳定性高,参数容易调节; \ No newline at end of file diff --git a/optimizer/images/ada.png b/optimizer/images/ada.png new file mode 100644 index 0000000000000000000000000000000000000000..b4ecb696b8a9043bef449373d678a5575894d582 Binary files /dev/null and b/optimizer/images/ada.png differ diff --git a/optimizer/images/exp.png b/optimizer/images/exp.png new file mode 100644 index 0000000000000000000000000000000000000000..eaaf708c3cafac409bd20696ddcf24f201540e02 Binary files /dev/null and b/optimizer/images/exp.png differ diff --git a/optimizer/images/gold.png b/optimizer/images/gold.png new file mode 100644 index 0000000000000000000000000000000000000000..21be226a4b43be529233fb3886c2f445825eb873 Binary files /dev/null and b/optimizer/images/gold.png differ diff --git a/optimizer/images/iris.png b/optimizer/images/iris.png new file mode 100644 index 0000000000000000000000000000000000000000..1504d960a348565b03fb814682ebde3ad37b5f5f Binary files /dev/null and b/optimizer/images/iris.png differ diff --git a/optimizer/images/mom.png b/optimizer/images/mom.png new file mode 100644 index 0000000000000000000000000000000000000000..e29dde27de0fefd8993077875d1284e45ae354a1 Binary files /dev/null and b/optimizer/images/mom.png differ diff --git a/optimizer/images/sgd.png b/optimizer/images/sgd.png new file mode 100644 index 0000000000000000000000000000000000000000..313b68098552dbb309ce73f957c59135f0c4ce0a Binary files /dev/null and b/optimizer/images/sgd.png differ diff --git a/optimizer/main1.py b/optimizer/main1.py new file mode 100644 index 0000000000000000000000000000000000000000..9047a1f1fa5e4426282138d8ebbc06752eb93ba9 --- /dev/null +++ b/optimizer/main1.py @@ -0,0 +1,122 @@ +import numpy as np +import matplotlib.pyplot as plt +import matplotlib.colors as plt_cl + +# ------------------定义目标函数beale、目标函数的偏导函数dbeale_dx,并画出目标函数--------------------- +# 定义函数beale +def beale(x1, x2): + return (1.5 - x1 + x1 * x2) ** 2 + (2.25 - x1 + x1 * x2 ** 2) ** 2 + (2.625 - x1 + x1 * x2 ** 3) ** 2 + +# 定义函数beale的偏导 +def dbeale_dx(x1, x2): + dfdx1 = 2 * (1.5 - x1 + x1 * x2) * (x2 - 1) + 2 * (2.25 - x1 + x1 * x2 ** 2) * (x2 ** 2 - 1) + 2 * ( + 2.625 - x1 + x1 * x2 ** 3) * (x2 ** 3 - 1) + dfdx2 = 2 * (1.5 - x1 + x1 * x2) * x1 + 2 * (2.25 - x1 + x1 * x2 ** 2) * (2 * x1 * x2) + 2 * ( + 2.625 - x1 + x1 * x2 ** 3) * (3 * x1 * x2 ** 2) + return dfdx1, dfdx2 +step_x1, step_x2 = 0.2, 0.2 +X1, X2 = np.meshgrid(np.arange(-5, 5 + step_x1, step_x1), + np.arange(-5, 5 + step_x2, step_x2)) +Y = beale(X1, X2) +print("目标结果 (x_1, x_2) = (3, 0.5)") + +# 定义画图函数 +def gd_plot(x_traj): + plt.rcParams['figure.figsize'] = [6, 6] + plt.contour(X1, X2, Y, levels=np.logspace(0, 6, 30), + norm=plt_cl.LogNorm(), cmap=plt.cm.jet) + plt.title('2D Contour Plot of Beale function(Momentum)') + plt.xlabel('$x_1$') + plt.ylabel('$x_2$') + plt.axis('equal') + plt.plot(3, 0.5, 'k*', markersize=10) + if x_traj is not None: + x_traj = np.array(x_traj) + plt.plot(x_traj[:, 0], x_traj[:, 1], 'k-') + plt.show() + +gd_plot(None) + +# ------------------------------------------------------------无优化器------------------------------------------- +def gd_no(df_dx, x0, conf_para=None): + if conf_para is None: + conf_para = {} + conf_para.setdefault('n_iter', 1000) # 迭代次数 + conf_para.setdefault('learning_rate', 0.001) # 设置学习率 + x_traj = [] + x_traj.append(x0) + v = np.zeros_like(x0) + for iter in range(1, conf_para['n_iter'] + 1): + x_traj.append(x_traj[-1]) + return x_traj + +x0 = np.array([1.0, 1.5]) +conf_para_no = {'n_iter': 2000, 'learning_rate': 0.005} +x_traj_no = gd_no(dbeale_dx, x0, conf_para_no) +print("无优化器求得极值点 (x_1, x_2) = (%s, %s)" % (x_traj_no[-1][0], x_traj_no[-1][1])) +gd_plot(x_traj_no) + +# ------------------------------------------------------------SGD------------------------------------------- +def gd_sgd(df_dx, x0, conf_para=None): + if conf_para is None: + conf_para = {} + conf_para.setdefault('n_iter', 1000) # 迭代次数 + conf_para.setdefault('learning_rate', 0.001) # 设置学习率 + x_traj = [] + x_traj.append(x0) + v = np.zeros_like(x0) + for iter in range(1, conf_para['n_iter'] + 1): + dfdx = np.array(df_dx(x_traj[-1][0], x_traj[-1][1])) + v = - conf_para['learning_rate'] * dfdx + x_traj.append(x_traj[-1] + v) + return x_traj + +x0 = np.array([1.0, 1.5]) +conf_para_sgd = {'n_iter': 2000, 'learning_rate': 0.005} +x_traj_sgd = gd_sgd(dbeale_dx, x0, conf_para_sgd) +print("SGD求得极值点 (x_1, x_2) = (%s, %s)" % (x_traj_sgd[-1][0], x_traj_sgd[-1][1])) +gd_plot(x_traj_sgd) + +# -------------------------------------------------------Momentum--------------------------------- +def gd_momentum(df_dx, x0, conf_para=None): + if conf_para is None: + conf_para = {} + conf_para.setdefault('n_iter', 1000) # 迭代次数 + conf_para.setdefault('learning_rate', 0.001) # 设置学习率 + conf_para.setdefault('momentum', 0.9) # 设置动量参数 + x_traj = [] + x_traj.append(x0) + v = np.zeros_like(x0) + for iter in range(1, conf_para['n_iter'] + 1): + dfdx = np.array(df_dx(x_traj[-1][0], x_traj[-1][1])) + v = conf_para['momentum'] * v - conf_para['learning_rate'] * dfdx + x_traj.append(x_traj[-1] + v) + return x_traj + +x0 = np.array([1.0, 1.5]) +conf_para_momentum = {'n_iter': 500, 'learning_rate': 0.005} +x_traj_momentum = gd_momentum(dbeale_dx, x0, conf_para_momentum) +print("Momentum求得极值点 (x_1, x_2) = (%s, %s)" % (x_traj_momentum[-1][0], x_traj_momentum[-1][1])) +gd_plot(x_traj_momentum) + +# ----------------------------------------------------adagrad----------------------------- +def gd_adagrad(df_dx, x0, conf_para=None): + if conf_para is None: + conf_para = {} + conf_para.setdefault('n_iter', 1000) # 迭代次数 + conf_para.setdefault('learning_rate', 0.001) # 学习率 + conf_para.setdefault('epsilon', 1e-7) + x_traj = [] + x_traj.append(x0) + r = np.zeros_like(x0) + for iter in range(1, conf_para['n_iter'] + 1): + dfdx = np.array(df_dx(x_traj[-1][0], x_traj[-1][1])) + r += dfdx ** 2 + x_traj.append(x_traj[-1] - conf_para['learning_rate'] / (np.sqrt(r) + conf_para['epsilon']) * dfdx) + return x_traj + +x0 = np.array([1.0, 1.5]) +conf_para_adag = {'n_iter': 500, 'learning_rate': 2} +x_traj_adag = gd_adagrad(dbeale_dx, x0, conf_para_adag) +print("Adagrad求得极值点 (x_1, x_2) = (%s, %s)" % (x_traj_adag[-1][0], x_traj_adag[-1][1])) +gd_plot(x_traj_adag) diff --git a/optimizer/main2.py b/optimizer/main2.py new file mode 100644 index 0000000000000000000000000000000000000000..4420586c34643e7fe657cae92dace6b5e8ed8b82 --- /dev/null +++ b/optimizer/main2.py @@ -0,0 +1,189 @@ +import csv +import os +import time + +import numpy as np +from easydict import EasyDict as edict +from matplotlib import pyplot as plt + +import mindspore +from mindspore import nn +from mindspore import context +from mindspore import dataset +from mindspore.train.callback import TimeMonitor, LossMonitor +from mindspore import Tensor +from mindspore.train import Model +from mindspore.train.callback import ModelCheckpoint, CheckpointConfig + +context.set_context(mode=context.GRAPH_MODE, device_target="Ascend") + +# 解析执行本脚本时的传参 +import argparse +parser = argparse.ArgumentParser() +parser.add_argument('--data_url', required=True, default=None, help='Location of data.') +parser.add_argument('--train_url', required=True, default=None, help='Location of training outputs.') +args, unknown = parser.parse_known_args() + +import moxing as mox +mox.file.copy_parallel(src_url=os.path.join(args.data_url, 'iris.data'), dst_url='iris.data') # 将OBS桶中数据拷贝到容器中 + +cfg = edict({ + 'data_size': 150, + 'train_size': 120, # 训练集大小 + 'test_size': 30, # 测试集大小 + 'feature_number': 4, # 输入特征数 + 'num_class': 3, # 分类类别 + 'batch_size': 30, + 'data_dir': 'iris.data', # 执行容器中数据集所在路径 + 'save_checkpoint_steps': 5, # 多少步保存一次模型 + 'keep_checkpoint_max': 1, # 最多保存多少个模型 + 'out_dir_no_opt': './model_iris/no_opt', # 保存模型路径,无优化器模型 + 'out_dir_sgd': './model_iris/sgd', # 保存模型路径,SGD优化器模型 + 'out_dir_momentum': './model_iris/momentum', # 保存模型路径,momentum模型 + 'out_dir_adam': './model_iris/adam', # 保存模型路径,adam优化器模型 + 'output_prefix': "checkpoint_fashion_forward" # 保存模型文件名 +}) + + +# 读取数据并预处理: +with open(cfg.data_dir) as csv_file: + data = list(csv.reader(csv_file, delimiter=',')) + +# 共150条数据,将数据集的4个属性作为自变量X。将数据集的3个类别映射为{0, 1,2},作为因变量Y。 +label_map = {'Iris-setosa': 0, 'Iris-versicolor': 1, 'Iris-virginica': 2} +X = np.array([[float(x) for x in s[:-1]] for s in data[:cfg.data_size]], np.float32) +Y = np.array([label_map[s[-1]] for s in data[:150]], np.int32) + +# 将数据集分为训练集120条,测试集30条。 +train_idx = np.random.choice(cfg.data_size, cfg.train_size, replace=False) +test_idx = np.array(list(set(range(cfg.data_size)) - set(train_idx))) +X_train, Y_train = X[train_idx], Y[train_idx] +X_test, Y_test = X[test_idx], Y[test_idx] +print('训练数据x尺寸:', X_train.shape) +print('训练数据y尺寸:', Y_train.shape) +print('测试数据x尺寸:', X_test.shape) +print('测试数据y尺寸:', Y_test.shape) + +# 使用MindSpore GeneratorDataset接口将numpy.ndarray类型的数据转换为Dataset。 +def gen_data(X_train, Y_train, epoch_size): + XY_train = list(zip(X_train, Y_train)) + ds_train = dataset.GeneratorDataset(XY_train, ['x', 'y']) + ds_train.set_dataset_size(cfg.train_size) + ds_train = ds_train.shuffle(buffer_size=cfg.train_size).batch(cfg.batch_size, drop_remainder=True) + XY_test = list(zip(X_test, Y_test)) + ds_test = dataset.GeneratorDataset(XY_test, ['x', 'y']) + ds_test.set_dataset_size(cfg.test_size) + ds_test = ds_test.shuffle(buffer_size=cfg.test_size).batch(cfg.test_size, drop_remainder=True) + return ds_train, ds_test + + +# 继承并重写LossMonitor +class SubLossMonitor(LossMonitor): + def epoch_end(self, run_context): + pass + + def step_end(self, run_context): + cb_params = run_context.original_args() + step_loss = cb_params.net_outputs + + if isinstance(step_loss, (tuple, list)) and isinstance(step_loss[0], Tensor): + step_loss = step_loss[0] + if isinstance(step_loss, Tensor): + step_loss = np.mean(step_loss.asnumpy()) + + self.losses.append(step_loss) + cur_step_in_epoch = int((cb_params.cur_step_num - 1) % cb_params.batch_num) + 1 + + if isinstance(step_loss, float) and (np.isnan(step_loss) or np.isinf(step_loss)): + raise ValueError("Epoch: [{:3d}/{:3d}], step: [{:5d}/{:5d}]. " + "Invalid loss, terminating training.".format( + cb_params.cur_epoch_num - 1, cb_params.epoch_num, + cur_step_in_epoch, cb_params.batch_num)) + + if self._per_print_times != 0 and cb_params.cur_step_num % self._per_print_times == 0: + print("epoch: {} step {}, loss: {}, avg loss: {:5.3f}".format(cb_params.cur_epoch_num, + cur_step_in_epoch, + step_loss, np.mean(self.losses)), flush=True) + + +# 训练 +def train(network, net_opt, ds_train, prefix, directory, print_times): + net_loss = nn.SoftmaxCrossEntropyWithLogits(is_grad=False, sparse=True, reduction="mean") + model = Model(network, loss_fn=net_loss, optimizer=net_opt, metrics={"acc"}) + loss_cb = SubLossMonitor(per_print_times=print_times) + config_ck = CheckpointConfig(save_checkpoint_steps=cfg.save_checkpoint_steps, + keep_checkpoint_max=cfg.keep_checkpoint_max) + ckpoint_cb = ModelCheckpoint(prefix=prefix, directory=directory, config=config_ck) + print("============== Starting Training ==============") + model.train(epoch_size, ds_train, callbacks=[ckpoint_cb, loss_cb], dataset_sink_mode=False) + return model + + +# 评估预测 +def eval_predict(model, ds_test): + # 使用测试集评估模型,打印总体准确率 + metric = model.eval(ds_test) + print(metric) + # 预测 + test_ = ds_test.create_dict_iterator().get_next() + test = Tensor(test_['x'], mindspore.float32) + predictions = model.predict(test) + predictions = predictions.asnumpy() + for i in range(10): + p_np = predictions[i, :] + p_list = p_np.tolist() + print('第' + str(i) + '个sample预测结果:', p_list.index(max(p_list)), ' 真实结果:', test_['y'][i]) + + +# --------------------------------------------------无优化器----------------------------------- +epoch_size = 20 +print('------------------无优化器--------------------------') +# 数据 +ds_train, ds_test = gen_data(X_train, Y_train, epoch_size) +# 定义网络并训练 +network = nn.Dense(cfg.feature_number, cfg.num_class) +model = train(network, None, ds_train, "checkpoint_no_opt", cfg.out_dir_no_opt, 4) +# 评估预测 +eval_predict(model, ds_test) + +# ---------------------------------------------------SGD------------------------------------- +epoch_size = 200 +lr = 0.01 +print('-------------------SGD优化器-----------------------') +# 数据 +ds_train, ds_test = gen_data(X_train, Y_train, epoch_size) +# 定义网络并训练、测试、预测 +network = nn.Dense(cfg.feature_number, cfg.num_class) +net_opt = nn.SGD(network.trainable_params(), lr) +model = train(network, net_opt, ds_train, "checkpoint_sgd", cfg.out_dir_sgd, 40) +# 评估预测 +eval_predict(model, ds_test) + +# ----------------------------------------------------Momentum------------------------------- +epoch_size = 20 +lr = 0.01 +print('-------------------Momentum优化器-----------------------') +# 数据 +ds_train, ds_test = gen_data(X_train, Y_train, epoch_size) +# 定义网络并训练 +network = nn.Dense(cfg.feature_number, cfg.num_class) +net_opt = nn.Momentum(network.trainable_params(), lr, 0.9) +model = train(network, net_opt, ds_train, "checkpoint_momentum", cfg.out_dir_momentum, 4) +# 评估预测 +eval_predict(model, ds_test) + +# ----------------------------------------------------Adam----------------------------------- +epoch_size = 15 +lr = 0.1 +print('------------------Adam优化器--------------------------') +# 数据 +ds_train, ds_test = gen_data(X_train, Y_train, epoch_size) +# 定义网络并训练 +network = nn.Dense(cfg.feature_number, cfg.num_class) +net_opt = nn.Adam(network.trainable_params(), learning_rate=lr) +model = train(network, net_opt, ds_train, "checkpoint_adam", cfg.out_dir_adam, 4) +# 评估预测 +eval_predict(model, ds_test) + +# ------------------------------------------------------------------------------------------- +mox.file.copy_parallel(src_url='model_iris', dst_url=args.train_url) # 将容器输出拷贝到OBS桶中