基於Docker的MindSpore安裝與使用基礎介紹
阿新 • • 發佈:2021-03-15
# 技術背景
[MindSpore](https://www.mindspore.cn/)是一款新一代AI開源計算框架,其特色在於:創新程式設計正規化,AI科學家和工程師更易使用,便於開放式創新;該計算框架可滿足終端、邊緣計算、雲全場景需求,能更好保護資料隱私;可開源,形成廣闊應用生態。MindSpore的軟體架構如下圖所示:
![](https://img2020.cnblogs.com/blog/2277440/202103/2277440-20210314214825931-149857048.png)
![](https://img2020.cnblogs.com/blog/2277440/202103/2277440-20210314215327855-567146241.png)
(圖片來自於參考連結1的內容截圖)
其中關於自動微分的部分被整合在了GHLO這個模組上,該模組主要內容是一些不依賴於硬體體系的優化,也是本次安裝與測試指導中特別關注的內容。
由於MindSpore的支援系統列表(如下圖所示)中不包含本機主作業系統`Manjaro Linux`,因此這裡我們使用Docker的方式來進行安裝和使用。Docker是一款最常用的基於NameSpace和Cgroup隔離的容器解決方案,其在保障了容器內部資料和程序隔離的安全基礎之上,開發了更加靈活的系統級隔離和排程解決方案。
![](https://img2020.cnblogs.com/blog/2277440/202103/2277440-20210314215823619-1351333693.png)
# Manjaro Linux下Docker容器基礎操作介紹
由於Docker的安裝較為容易,在各種Linux發行版的源中一般都有,這裡就不過多介紹,我們就先假設Docker已經安裝成功。在使用時,需要先啟動Docker的服務:
```bash
[dechin-manjaro dechin]# systemctl start docker
```
啟動之後我們可以通過檢視status來確認是否啟動成功,以及docker服務是否已經處於正在執行的狀態,如果顯示`running`則表示docker正在執行:
```bash
[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](https://hub.docker.com/)裡面直接拉取別人已經制作好的基礎系統映象。也可以基於這些基礎系統映象,撰寫一份屬於自己的dockerfile,建立一個定製化的容器化程式設計環境。我們可以用`docker images`指令來檢視本地已有的映象:
```bash
[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版本等,各個版本的介面上存在一定的區別,效能上也有較大差異)的映象檔案:
```bash
[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`指令檢視映象列表,我們發現剛才拉取的映象已經在映象列表中:
```bash
[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來拉取映象,只要輸入前幾位的字元,只要保障其唯一性,也一樣可以成功拉起映象,如下所示的用例就是使用了這樣的一個操作技巧:
```bash
[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的測試用例試試:
```bash
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測試用例如下(相關示例程式碼來自於官方網站上面提供的驗證用例):
```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指令來執行相關程式碼即可:
```bash
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,也就是張量。關於張量的一些基礎知識,可以檢視之前寫的這一篇[關於張量網路計算的部落格](https://www.cnblogs.com/dechinphy/p/tensor.html),張量網路不僅僅是一個高維的矩陣,其更加重要的一個含義是在圖表示的理論框架下,對中間計算的過程進行復雜性的優化。在拓撲網路圖中可以先找到一個複雜性較好的計算順序,再對整個張量網路進行計算。當然,在上述給出的示例中,僅僅執行了Tensor的加法,構造了兩個元素全部為1的Tensor,然後加起來,得到的是一個元素全為2的Tensor,因此我們最後的列印輸出是一個全是2的陣列。
# 在Docker中儲存資料
在Docker的操作中,如果我們不對相關的資料執行儲存的操作,則這些資料不會被儲存到映象中,這也是符合大部分時候容器的使用場景需求的。但是這裡我們將容器作為一個程式設計環境來使用,因此我們希望可以把相關的資料寫入到新的容器映象中,例如上述用例中在`home`目錄下所建立的`test_mindspore.py`檔案。首先我們用`docker ps`的指令來檢視歷史記錄中的CONTAINER ID,上述用例的讀寫操作實際上被儲存到了這個名為`00637dfcdc2a`的映象中,而這裡的IMAGE顯示為`98a3`,是因為我們之前直接使用部分的ID來拉取的緣故。
```bash
[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)的新的容器映象內,這裡我們先用一個字元編碼來命名:
```bash
[dechin-manjaro dechin]# docker commit 0063 98a3
sha256:5d2e3caaca6970f0c003593a8c5606a4f2798ea7da032ff8308fc46b36dcf9f0
```
commit結束之後我們再檢視本地映象,我們發現多出來了一個名為98a3的映象,而原來IMAGE ID開頭為98a3的映象還在:
```bash
[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,是拉取哪一個映象呢?
```bash
[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來拉起:
```bash
[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相關函式和介面的呼叫方法:
```python
# 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)
```
![](https://img2020.cnblogs.com/blog/2277440/202103/2277440-20210314225701592-631903965.png)
這個案例中給出了一個關於$x,y,z$三個變數的函式,光看函式形式的話其實就是$f(x,y,z)=xyz$。但是這裡面展開來看的話,$x,y$分別是2×3的矩陣與3×3的張量/多維矩陣,$z$可以當成是一個單獨的變數來計算,這裡值就是`1`。具體矩陣乘法的呼叫方法可以參考官網上給出的介面文件:
![](https://img2020.cnblogs.com/blog/2277440/202103/2277440-20210314230424882-844203877.png)
綜合上面這些函式介面,最終計算的結果形式,如果用函式式來表達大概就是:
$$
\frac{df(x,y,z)}{dx}=\frac{d(\sum xyz)}{dx}
$$
其中的求和項與MindSpore中所採用的自動微分計算方法有關,相關參考文獻為參考連結4。最後驗證一下我們的輸出結果:
```bash
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): 55