转载地址:https://bbs.huaweicloud.com/forum/thread-69090-1-1.html
作者:Yesterday
最近在学习使用Mindpsore,经过这几个星期的学习,并同很多华为的朋友进行咨询以后,总算基本上理解了Mindpsore的基本操作。我在学习中发现,官网教程一上来就用一个图片分类的例子作为基础教程,对于初学者来说非常不友好。所以在这里我给大家分享一个我自己参照官网教程编写的,比图片分类更简单的线性函数拟合的例子,希望能对大家学习使用Mindpsore有所帮助。
我们希望实现一个对 y = w * x + b 这么一个简单函数进行参数拟合,这里我们用 y = 2x + 3 + noise 的方程产生随机数据,并使用线性回归函数 l = \sum_i 1/2 * ( y_i - y'_i )^2 作为损失函数。建模方面我们直接用Mindspore的Dense类生成一个1*1维的网络,优化算法使用了目前GPU版支持的RMSProp算子作为优化器。为了便于大家理解Mindspore的工作原理,首先我在PyNative模式下写了一个分步骤过程进行训练的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
|
输出结果:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
可见训练后的w和b的参数基本上已经非常接近预设的 w = 2 , b = 3 了。
上述代码是参照官网《使用PyNative模式调试》写的。下面对上面的几个地方做一下说明:
注释1:Mindspore自带的WithLossCell的代码其实非常简单:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
可以看到,其WithLossCell的作用是将网络模型和损失函数的表达式联系起来,在construct函数中才真正定义了损失函数作用形式。换句话说,其实这里WithLossCell才是真正意义上的“损失函数”,之前定义的“MyLoss”(也包括Mindspore自带的那些Loss function)本质上只是“损失函数的表达式”而以。因此,如果想要自定义复杂形式的损失函数,只需要自己定义一个类似WithLossCell的类即可。
注释2:GrapWrap这个类,除了计算参数的梯度之外,还有一个重要的作用就是为训练数据的输入提供接口。注意到WithLossCell类的construct函数的两个输入参数(data,label),是通过GradWrap类中的construct(data,label)函数进行输入的。而在训练过程中:
1 |
|
这一步data_x和data_y分别通过construct函数的两个形参data和label传到了GradWrap,从而进一步传到了WithLossCell。
上面我们在PyNative模式下,一步一步实现了训练过程。其实Mindspore本身有很多设计好的集成化模型,很多内容无需单独写代码。在理解了上述Mindspore训练过程之后,下面我们就利用Mindspore的dataset数据类型和Model类实现上述的功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
|
输出结果为:
1 2 3 4 5 6 7 8 9 10 |
|
上述代码参照了官网《实现一个图片分类应用》教程。此代码同样使用了Dense作为网络模型、RMSProp作为优化器,并且同样使用了自定义的损失函数形式。但与上面代码不同的是,这里用了mindspore自带的dataset类型作为训练数据的输入,并且训练过程是在mindspore的Model类中自动完成的。
下面对代码的部分内容进行说明:
注释1:使用dataset类作为训练数据的存储类型可以很方便的在mindspore的训练过程在进行数据输入,mindspore本身自带了很多dataset类型,比如官网《实现一个图片分类应用》教程就使用了了MnistDataset类型。其实我们完全可以使用GeneratorDataset产生自己的数据类型。具体方式有两种,一种是使用生成器(generator),另一种则是含有__getitem__的自定义类,具体方法请参见官网《加载数据集》教程和GeneratorDataset的使用说明。在这里我们使用了生成器的形式产生了自己的dataset类。关于dataset的设置有几个需要注意的点:
numpy生成的数组默认是float64类型,而Dense类中的参数默认是float32,两者需要统一。在这里我将numpy生成的数组用astype转换成了float32
r0.5版Mindpsore,用生成器产生的dataset类型的dataset_size是空的,所以需要在生成之后用set_dataset_size(num)设置数据的大小。据说r0.6版本会改进,不再需要手工设置。
dataset类型在向loss function输入数据的时候,是按照设置的顺序进行输入的。目前版本的自带的输入接口(TrainOneStepCell)有且只有两个,即data和label,所以生成器中必须data在前,label在后。据说r0.7将支持灵活的数据输入模式。
注释2:自带的TrainOneStepCell类的作用类似于上面代码中的GradWrap,但除了计算梯度和提供接口之外,也提供了优化器的接口。
以上就是我的使用mindspore实现线性函数拟合的代码以及我对于Mindpsore一些功能的个人理解,希望对大家有帮助。如果有什么解释不正确的地方也欢迎大家指正。