基於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: //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的測試用例,驗證了其基本功能的準確性。

版權聲明

本文首發鏈接為://www.cnblogs.com/dechinphy/p/mindspore.html
作者ID:DechinPhy
更多原著文章請參考://www.cnblogs.com/dechinphy/

參考鏈接

  1. //baike.baidu.com/item/MindSpore/23697508
  2. //www.mindspore.cn/
  3. //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.
Tags: