基于Docker的MindSpore安装与使用基础介绍

技术背景

MindSpore是一款新一代AI开源计算框架,其特色在于:创新编程范式,AI科学家和工程师更易使用,便于开放式创新;该计算框架可满足终端、边缘计算、云全场景需求,能更好保护数据隐私;可开源,形成广阔应用生态。MindSpore的软件架构如下图所示:

(图片来自于参考链接1的内容截图)
其中关于自动微分的部分被集成在了GHLO这个模块上,该模块主要内容是一些不依赖于硬件体系的优化,也是本次安装与测试指导中特别关注的内容。

由于MindSpore的支持系统列表(如下图所示)中不包含本机主操作系统Manjaro Linux,因此这里我们使用Docker的方式来进行安装和使用。Docker是一款最常用的基于NameSpace和Cgroup隔离的容器解决方案,其在保障了容器内部数据和进程隔离的安全基础之上,开发了更加灵活的系统级隔离和调度解决方案。

Manjaro Linux下Docker容器基础操作介绍

由于Docker的安装较为容易,在各种Linux发行版的源中一般都有,这里就不过多介绍,我们就先假设Docker已经安装成功。在使用时,需要先启动Docker的服务:

[dechin-manjaro dechin]# systemctl start docker

启动之后我们可以通过查看status来确认是否启动成功,以及docker服务是否已经处于正在运行的状态,如果显示running则表示docker正在运行:

[dechin-manjaro dechin]# systemctl status docker
● docker.service - Docker Application Container Engine
     Loaded: loaded (/usr/lib/systemd/system/docker.service; disabled; vendor preset: disabled)
     Active: active (running) since Sun 2021-03-14 21:12:10 CST; 48s ago
TriggeredBy: ● docker.socket
       Docs: https://docs.docker.com
   Main PID: 2292 (dockerd)
      Tasks: 91 (limit: 47875)
     Memory: 216.0M
     CGroup: /system.slice/docker.service
             ├─2292 /usr/bin/dockerd -H fd://
             └─2303 containerd --config /var/run/docker/containerd/containerd.toml --log-level info

一般可以通过docker pull的方式简单的从dockerhub里面直接拉取别人已经制作好的基础系统镜像。也可以基于这些基础系统镜像,撰写一份属于自己的dockerfile,创建一个定制化的容器化编程环境。我们可以用docker images指令来查看本地已有的镜像:

[dechin-manjaro dechin]# docker images
REPOSITORY                      TAG       IMAGE ID       CREATED         SIZE
centos-singularity              latest    57d72e8a5ed8   8 weeks ago     824MB
centos-python36                 latest    8507baed96a2   2 months ago    651MB
ubntu-python38                  latest    ac734d47a39d   2 months ago    1.01GB
centos                          latest    3c1f6b9f7e91   2 months ago    215MB
cplex                           latest    55d067c32a95   2 months ago    1.16GB
shekyan/slowhttptest            latest    9cf05b8a7d93   4 months ago    7.86MB
ubuntu                          latest    d70eaf7277ea   4 months ago    72.9MB

在Docker环境已经准备好的前提下,我们就可以开始准备MindSpore的编程环境。

在Docker上配置MindSpore编程环境

首先我们按照官方提示,从华为云的镜像库中拉取MindSpore的cpu版本(MindSpore分为cpu版本和gpu版本等,各个版本的接口上存在一定的区别,性能上也有较大差异)的镜像文件:

[dechin-manjaro dechin]# docker pull swr.cn-south-1.myhuaweicloud.com/mindspore/mindspore-cpu:1.1.1
1.1.1: Pulling from mindspore/mindspore-cpu
f22ccc0b8772: Pull complete 
3cf8fb62ba5f: Pull complete 
e80c964ece6a: Pull complete 
fe0abcdea904: Pull complete 
6533a255e1da: Pull complete 
3e74722304a0: Pull complete 
bd14fc4220fc: Pull complete 
2540aadb4e52: Pull complete 
acd020acb001: Pull complete 
3cea35fa8bdc: Pull complete 
96eddf603bb3: Pull complete 
Digest: sha256:d7db718a62fb3a0ab56ef4aa548e3c9a62e42094b018d99751be5f7b6b006749
Status: Downloaded newer image for swr.cn-south-1.myhuaweicloud.com/mindspore/mindspore-cpu:1.1.1
swr.cn-south-1.myhuaweicloud.com/mindspore/mindspore-cpu:1.1.1

拉取完成后,可以通过docker images指令查看镜像列表,我们发现刚才拉取的镜像已经在镜像列表中:

[dechin-manjaro dechin]# docker images
REPOSITORY                                                 TAG       IMAGE ID       CREATED         SIZE
swr.cn-south-1.myhuaweicloud.com/mindspore/mindspore-cpu   1.1.1     98a3f041e3d4   4 weeks ago     1.18GB

在准备好MindSpore的容器化编程环境之后,我们可以进入这个镜像去测试一下MindSpore的一些基础用例。运行容器的方式是docker run,但是为了持久化的运行镜像中的/bin/bash,我们需要加上-it指令配置。拉取容器镜像时一般是用REPOSITORY这一列的镜像名称来拉取,但是由于这个名字实在是有点长,我们也可以通过拉取IMAGE ID来进入容器实例内,每一个镜像或者实例都有一个唯一的ID作为标识码。甚至在日常使用过程中,我们并不需要输入完整的IMAGE ID来拉取镜像,只要输入前几位的字符,只要保障其唯一性,也一样可以成功拉起镜像,如下所示的用例就是使用了这样的一个操作技巧:

[dechin-manjaro dechin]# docker run -it 98a3
root@00637dfcdc2a:/# ll
total 72
drwxr-xr-x   1 root root 4096 Mar 14 13:17 ./
drwxr-xr-x   1 root root 4096 Mar 14 13:17 ../
-rwxr-xr-x   1 root root    0 Mar 14 13:17 .dockerenv*
drwxr-xr-x   1 root root 4096 Feb  8 02:20 bin/
drwxr-xr-x   2 root root 4096 Apr 24  2018 boot/
drwxr-xr-x   5 root root  360 Mar 14 13:17 dev/
drwxr-xr-x   1 root root 4096 Mar 14 13:17 etc/
drwxr-xr-x   2 root root 4096 Apr 24  2018 home/
drwxr-xr-x   1 root root 4096 Feb  8 02:20 lib/
drwxr-xr-x   1 root root 4096 Feb  8 02:20 lib64/
drwxr-xr-x   2 root root 4096 Nov 19 13:07 media/
drwxr-xr-x   2 root root 4096 Nov 19 13:07 mnt/
drwxr-xr-x   2 root root 4096 Nov 19 13:07 opt/
dr-xr-xr-x 311 root root    0 Mar 14 13:17 proc/
drwx------   1 root root 4096 Feb  8 02:22 root/
drwxr-xr-x   1 root root 4096 Nov 25 22:25 run/
drwxr-xr-x   1 root root 4096 Feb  8 02:19 sbin/
drwxr-xr-x   2 root root 4096 Nov 19 13:07 srv/
dr-xr-xr-x  13 root root    0 Mar 14 13:17 sys/
drwxrwxrwt   1 root root 4096 Feb  8 02:22 tmp/
drwxr-xr-x   1 root root 4096 Nov 19 13:07 usr/
drwxr-xr-x   1 root root 4096 Nov 19 13:09 var/

拉起镜像之后我们可以看到这里的目录跟普通的Linux操作系统基本一致。这里我们重点需要确认在该环境下MindSpore是否已经被成功安装,因此我们进入home目录,去创建一个MindSpore的测试用例试试:

root@00637dfcdc2a:/# cd home/
root@00637dfcdc2a:/home# ll
total 8
drwxr-xr-x 2 root root 4096 Apr 24  2018 ./
drwxr-xr-x 1 root root 4096 Mar 14 13:17 ../
root@00637dfcdc2a:/home# mkdir mindspore
root@00637dfcdc2a:/home# cd mindspore/

创建的python测试用例如下(相关示例代码来自于官方网站上面提供的验证用例):

# test_mindspore.py

import numpy as np
import mindspore.context as context
import mindspore.ops as ops
from mindspore import Tensor

context.set_context(mode=context.PYNATIVE_MODE, device_target="CPU")

x = Tensor(np.ones([1,3,3,4]).astype(np.float32))
y = Tensor(np.ones([1,3,3,4]).astype(np.float32))
print(ops.tensor_add(x, y))

在官方提供的这个容器镜像中,只存在一个python3的python版本,因此一般情况下直接使用python指令来运行相关代码即可:

root@00637dfcdc2a:/home/mindspore# python test_mindspore.py  
WARNING: 'ControlDepend' is deprecated from version 1.1 and will be removed in a future version, use 'Depend' instead.
[WARNING] ME(28:140090633642112,MainProcess):2021-03-14-13:19:14.205.404 [mindspore/ops/operations/array_ops.py:2302] WARN_DEPRECATED: The usage of Pack is deprecated. Please use Stack.
[[[[2. 2. 2. 2.]
   [2. 2. 2. 2.]
   [2. 2. 2. 2.]]

  [[2. 2. 2. 2.]
   [2. 2. 2. 2.]
   [2. 2. 2. 2.]]

  [[2. 2. 2. 2.]
   [2. 2. 2. 2.]
   [2. 2. 2. 2.]]]]

在得到这一串数字之后,我们知道即使有一些版本相关的告警信息,但是这个例子是已经被成功的执行了的,现在我们再来回顾一下这个例子执行的是一个什么样的功能。

这个案例其实是创建两个多维的数组,这里相关的数据结构名为Tensor,也就是张量。关于张量的一些基础知识,可以查看之前写的这一篇关于张量网络计算的博客,张量网络不仅仅是一个高维的矩阵,其更加重要的一个含义是在图表示的理论框架下,对中间计算的过程进行复杂性的优化。在拓扑网络图中可以先找到一个复杂性较好的计算顺序,再对整个张量网络进行计算。当然,在上述给出的示例中,仅仅执行了Tensor的加法,构造了两个元素全部为1的Tensor,然后加起来,得到的是一个元素全为2的Tensor,因此我们最后的打印输出是一个全是2的数组。

在Docker中保存数据

在Docker的操作中,如果我们不对相关的数据执行保存的操作,则这些数据不会被保存到镜像中,这也是符合大部分时候容器的使用场景需求的。但是这里我们将容器作为一个编程环境来使用,因此我们希望可以把相关的数据写入到新的容器镜像中,例如上述用例中在home目录下所创建的test_mindspore.py文件。首先我们用docker ps的指令来查看历史记录中的CONTAINER ID,上述用例的读写操作实际上被保存到了这个名为00637dfcdc2a的镜像中,而这里的IMAGE显示为98a3,是因为我们之前直接使用部分的ID来拉取的缘故。

[dechin-manjaro dechin]# docker ps -n 2
CONTAINER ID   IMAGE                  COMMAND                  CREATED         STATUS                       PORTS     NAMES
00637dfcdc2a   98a3                   "/bin/bash"              5 minutes ago   Exited (127) 3 seconds ago             stupefied_kare
dfb2634d0ab4   ubuntu                 "/bin/bash"              8 minutes ago   Exited (127) 8 minutes ago             hardcore_engelbart

在获取到需要保存的CONTAINER ID之后,我们就可以使用docker commit指令,把该数据保存到一个指定名称(比如直接取名为mindspore)的新的容器镜像内,这里我们先用一个字符编码来命名:

[dechin-manjaro dechin]# docker commit 0063 98a3
sha256:5d2e3caaca6970f0c003593a8c5606a4f2798ea7da032ff8308fc46b36dcf9f0

commit结束之后我们再查看本地镜像,我们发现多出来了一个名为98a3的镜像,而原来IMAGE ID开头为98a3的镜像还在:

[dechin-manjaro dechin]# docker images
REPOSITORY                                                 TAG       IMAGE ID       CREATED         SIZE
98a3                                                       latest    5d2e3caaca69   6 seconds ago   1.18GB
swr.cn-south-1.myhuaweicloud.com/mindspore/mindspore-cpu   1.1.1     98a3f041e3d4   4 weeks ago     1.18GB

那么在这个时候,如果我们直接拉取98a3,是拉取哪一个镜像呢?

[dechin-manjaro dechin]# docker run -it 98a3
root@5ad904a0bdc1:/# ll
total 76
drwxr-xr-x   1 root root 4096 Mar 14 13:25 ./
drwxr-xr-x   1 root root 4096 Mar 14 13:25 ../
-rwxr-xr-x   1 root root    0 Mar 14 13:25 .dockerenv*
drwxr-xr-x   1 root root 4096 Feb  8 02:20 bin/
drwxr-xr-x   2 root root 4096 Apr 24  2018 boot/
drwxr-xr-x   5 root root  360 Mar 14 13:25 dev/
drwxr-xr-x   1 root root 4096 Mar 14 13:25 etc/
drwxr-xr-x   1 root root 4096 Mar 14 13:18 home/
drwxr-xr-x   1 root root 4096 Feb  8 02:20 lib/
drwxr-xr-x   1 root root 4096 Feb  8 02:20 lib64/
drwxr-xr-x   2 root root 4096 Nov 19 13:07 media/
drwxr-xr-x   2 root root 4096 Nov 19 13:07 mnt/
drwxr-xr-x   2 root root 4096 Nov 19 13:07 opt/
dr-xr-xr-x 310 root root    0 Mar 14 13:25 proc/
drwx------   1 root root 4096 Mar 14 13:22 root/
drwxr-xr-x   1 root root 4096 Nov 25 22:25 run/
drwxr-xr-x   1 root root 4096 Feb  8 02:19 sbin/
drwxr-xr-x   2 root root 4096 Nov 19 13:07 srv/
dr-xr-xr-x  13 root root    0 Mar 14 13:25 sys/
drwxrwxrwt   1 root root 4096 Feb  8 02:22 tmp/
drwxr-xr-x   1 root root 4096 Nov 19 13:07 usr/
drwxr-xr-x   1 root root 4096 Nov 19 13:09 var/
root@5ad904a0bdc1:/# cd home/
root@5ad904a0bdc1:/home# ll
total 12
drwxr-xr-x 1 root root 4096 Mar 14 13:18 ./
drwxr-xr-x 1 root root 4096 Mar 14 13:25 ../
drwxr-xr-x 2 root root 4096 Mar 14 13:19 mindspore/
root@5ad904a0bdc1:/home# cd mindspore/
root@5ad904a0bdc1:/home/mindspore# ll
total 12
drwxr-xr-x 2 root root 4096 Mar 14 13:19 ./
drwxr-xr-x 1 root root 4096 Mar 14 13:18 ../
-rw-r--r-- 1 root root  332 Mar 14 13:19 test_mindspore.py
root@5ad904a0bdc1:/home/mindspore# exit
exit

这个测试结果告诉我们,虽然这里存在一个ID为98a3开头的镜像,但是docker还是会优先拉起名字相同,也就是REPOSITORY相同的容器镜像。在这个结果中,上述用例所创建的test_mindspore.py文件依然存在于这个系统镜像之中。我们可以再对比一下原始的镜像,这里因为不能再用98a3来拉起,因此我们多加一位字符,使用98a3f来拉起:

[dechin-manjaro dechin]# docker run -it 98a3f
root@6ff7a03fa3cb:/# ll
total 72
drwxr-xr-x   1 root root 4096 Mar 14 13:25 ./
drwxr-xr-x   1 root root 4096 Mar 14 13:25 ../
-rwxr-xr-x   1 root root    0 Mar 14 13:25 .dockerenv*
drwxr-xr-x   1 root root 4096 Feb  8 02:20 bin/
drwxr-xr-x   2 root root 4096 Apr 24  2018 boot/
drwxr-xr-x   5 root root  360 Mar 14 13:25 dev/
drwxr-xr-x   1 root root 4096 Mar 14 13:25 etc/
drwxr-xr-x   2 root root 4096 Apr 24  2018 home/
drwxr-xr-x   1 root root 4096 Feb  8 02:20 lib/
drwxr-xr-x   1 root root 4096 Feb  8 02:20 lib64/
drwxr-xr-x   2 root root 4096 Nov 19 13:07 media/
drwxr-xr-x   2 root root 4096 Nov 19 13:07 mnt/
drwxr-xr-x   2 root root 4096 Nov 19 13:07 opt/
dr-xr-xr-x 313 root root    0 Mar 14 13:25 proc/
drwx------   1 root root 4096 Feb  8 02:22 root/
drwxr-xr-x   1 root root 4096 Nov 25 22:25 run/
drwxr-xr-x   1 root root 4096 Feb  8 02:19 sbin/
drwxr-xr-x   2 root root 4096 Nov 19 13:07 srv/
dr-xr-xr-x  13 root root    0 Mar 14 13:25 sys/
drwxrwxrwt   1 root root 4096 Feb  8 02:22 tmp/
drwxr-xr-x   1 root root 4096 Nov 19 13:07 usr/
drwxr-xr-x   1 root root 4096 Nov 19 13:09 var/
root@6ff7a03fa3cb:/# cd home/
root@6ff7a03fa3cb:/home# ll
total 8
drwxr-xr-x 2 root root 4096 Apr 24  2018 ./
drwxr-xr-x 1 root root 4096 Mar 14 13:25 ../
root@6ff7a03fa3cb:/home# exit
exit

我们发现,前面所创建的python测试用例是没有保存到这个原始镜像中的。

MindSpore自动微分测试

在众多的组合优化问题中,我们通常需要计算一个目标函数的导数,在一个可微的函数下,其最大值或最小值一定会满足一阶导数为0这一个条件,因此我们常常会去寻找构建的这个函数在某一个点的导数。寻找极值点的方法有很多,比如梯度下降等,但是计算导数的方法大概就是三种方案:手动求导、差分求导以及自动微分。手动求导只能应用于小规模的简单模型,对于比较复杂的模型来说是不太现实的;差分求导是在各种优化器中常用的技巧,但是性能比较受限;因此自动微分现在成为一个比较主流的方案,通过机器来优化计算一个给定模型的导数。当然,这里面也分为了Google的TensorFlow、FaceBook的PyTorch以及华为主推的MindSpore等几种不同的算法框架,在这篇博客中我们就不一一展开来介绍,这里我们仅用一个测试用例展示MindSpore相关函数和接口的调用方法:

# test_gradient.py

import numpy as np
import mindspore.context as context
import mindspore.nn as nn
import mindspore.ops as ops
from mindspore import Tensor
from mindspore import ParameterTuple, Parameter
from mindspore import dtype as mstype
context.set_context(mode=context.GRAPH_MODE, device_target="CPU")
class Net(nn.Cell):
    def __init__(self):
        super(Net, self).__init__()
        self.matmul = ops.MatMul()
        self.z = Parameter(Tensor(np.array([1.0], np.float32)), name='z')
    def construct(self, x, y):
        x = x * self.z
        out = self.matmul(x, y)
        return out

class GradNetWrtX(nn.Cell):
    def __init__(self, net):
        super(GradNetWrtX, self).__init__()
        self.net = net
        self.grad_op = ops.GradOperation()
    def construct(self, x, y):
        gradient_function = self.grad_op(self.net)
        return gradient_function(x, y)

x = Tensor([[0.8, 0.6, 0.2], [1.8, 1.3, 1.1]], dtype=mstype.float32)
y = Tensor([[0.11, 3.3, 1.1], [1.1, 0.2, 1.4], [1.1, 2.2, 0.3]], dtype=mstype.float32)
output = GradNetWrtX(Net())(x, y)
print(output)


这个案例中给出了一个关于\(x,y,z\)三个变量的函数,光看函数形式的话其实就是\(f(x,y,z)=xyz\)。但是这里面展开来看的话,\(x,y\)分别是2×3的矩阵与3×3的张量/多维矩阵,\(z\)可以当成是一个单独的变量来计算,这里值就是1。具体矩阵乘法的调用方法可以参考官网上给出的接口文档:

综合上面这些函数接口,最终计算的结果形式,如果用函数式来表达大概就是:

\[\frac{df(x,y,z)}{dx}=\frac{d(\sum xyz)}{dx} \]

其中的求和项与MindSpore中所采用的自动微分计算方法有关,相关参考文献为参考链接4。最后验证一下我们的输出结果:

root@a94bc5f2320e:/home/mindspore# python test_gradient.py 
WARNING: 'ControlDepend' is deprecated from version 1.1 and will be removed in a future version, use 'Depend' instead.
[WARNING] ME(37:140326817828992,MainProcess):2021-03-14-13:45:14.989.5 [mindspore/ops/operations/array_ops.py:2302] WARN_DEPRECATED: The usage of Pack is deprecated. Please use Stack.
[[4.5099998 2.7       3.6000001]
 [4.5099998 2.7       3.6000001]]

我们发现这个输出结果中虽然包含了一部分的告警信息以及有一些精度上的缺失,但是基本上是符合我们的预期的。

总结概要

写这篇文章的目的,主要是为了验证一下MindSpore编程环境的搭建,以及基本的测试用例的测试。由于MindSpore的CPU版本在x86架构上只适配了Ubuntu的操作系统,因此这里我们额外介绍了Docker的安装部署方案以及Docker的一些基本操作。最后我们通过两个MindSpore的测试用例,验证了其基本功能的准确性。

版权声明

本文首发链接为:https://www.cnblogs.com/dechinphy/p/mindspore.html
作者ID:DechinPhy
更多原著文章请参考:https://www.cnblogs.com/dechinphy/

参考链接

  1. https://baike.baidu.com/item/MindSpore/23697508
  2. https://www.mindspore.cn/
  3. https://www.mindspore.cn/tutorial/training/zh-CN/r1.1/advanced_use/achieve_high_order_differentiation.html#id4
  4. Baydin A G, Pearlmutter B A, Radul A A, et al. Automatic differentiation in machine learning: a survey[J]. The Journal of Machine Learning Research, 2017, 18(1): 5595-5637.
“留一手”加剧内卷,“讲不清”浪费时间。
posted @ 2021-03-14 23:15  DECHIN  阅读(707)  评论(0编辑  收藏  举报