MegEngine基本概念


MegEngine基本概念

基本概念

MegEngine 是基於計算圖的深度神經網絡學習框架。 本文內容會簡要介紹計算圖及其相關基本概念,以及在 MegEngine 中的實現。

計算圖

結合一個簡單的數學表達式來介紹計算圖中的基本概念。下圖是 y = (w * x) + b 這一數學表達式的計算圖表示:

 

 

 從中可以看到,計算圖中存在:

  • 數據節點(圖中的實心圈):如輸入數據 x 、 w 、b ,運算得到的數據 p ,以及最終的運算輸出 y ;
  • 計算節點(圖中的空心圈):圖中 * 和 + 分別表示計算節點 乘法 和 加法,是施加在數據節點上的運算;
  • 邊(圖中的箭頭):表示數據的流向,體現了數據節點和計算節點之間的依賴關系;

如上便是一個簡單的計算圖示例。

張量(Tensor

與 PyTorch,TensorFlow 等深度學習框架類似,MegEngine 使用張量(Tensor)來表示計算圖中的數據。

張量(Tensor)可以看做 NumPy 中的數組,它可以是一個標量、向量、矩陣或者多維數組。 可以通過 NumPy 或者 Python List 來創建一個 Tensor 。

執行下列代碼並查看相關結果:

In [1]:

import numpy as np

import megengine as mge

 

# 初始化一個維度為 (2, 5) 的 ndarray,並轉化成 MegEngine 的 Tensor

# 注:目前 MegEngine Tensor 不支持 float64 數值類型,所以這里顯示指定了 ndarray 的數值類型

a = mge.tensor(np.random.random((2,5)).astype('float32'))

print(a)

 

# 初始化一個長度為3的列表,並轉化成 Tensor

b = mge.tensor([1., 2., 3.])

print(b)

Tensor([[0.2397 0.6569 0.8097 0.3523 0.4676]

 [0.9492 0.9415 0.5636 0.614  0.8424]], device=xpux:0)

Tensor([1. 2. 3.], device=xpux:0)

通過 dtype 屬性,可以獲取 Tensor 的數據類型;通過 astype() 方法,可以拷貝創建一個指定數據類型的新 Tensor ,原 Tensor 不變。

In [2]:

print(a.dtype)

d = a.astype("float16")

print(d.dtype)

<class 'numpy.float32'>

<class 'numpy.float16'>

通過 shape 屬性,可以獲取 Tensor 的形狀:

In [3]:

print(a.shape)

(2, 5)

通過 numpy() 方法,可以將 Tensor 轉換為 numpy.ndarray:

In [4]:

a = mge.tensor(np.random.random((2,5)).astype('float32'))

print(a)

 

b = a.numpy()

print(b)

Tensor([[0.8246 0.8447 0.3225 0.2583 0.6065]

 [0.4701 0.594  0.1612 0.7749 0.0067]], device=xpux:0)

[[0.8246328  0.8447421  0.32254046 0.25825405 0.60646415]

 [0.47006    0.59400123 0.16122891 0.77490866 0.0067136 ]]

通過 device 屬性,可以查詢當前 Tensor 所在的設備。創建的 Tensor 可以位於不同 device,這根據當前的環境決定。一般地,如果在創建 Tensor 時不指定 device,其 device 屬性默認為 xpux,表示當前任意一個可用的設備。如果存在 GPU 則優先使用 GPU,否則為 CPU。

In [5]:

print(a.device)

xpux:0

也可以在創建 Tensor 時,指定 device 為 cpu0, cpu1, …, gpu0, gpu1, … ,也可以是 cpux 或 gpux,表示當前任意一個可用的 CPU 或 GPU。

通過 to() 方法可以在另一個 device 上生成當前 Tensor 的拷貝,比如將剛剛創建的 Tensor a 遷移到 CPU 上,再遷移到 GPU 上:

In [6]:

b = a.to("cpu0")

print(b.device)

cpu0:0

GPU CPU 切換

MegEngine 在 GPU 和 CPU 同時存在時默認使用 GPU 進行訓練。用戶可以調用 set_default_device() 來根據自身需求設置默認計算設備。

如下代碼設置默認設備為 CPU:

In [7]:

import megengine as mge

 

# 默認使用 CPU

mge.set_default_device('cpux')

如下代碼設置默認設備為GPU:

mge.set_default_device('gpux')

如果不想修改代碼,用戶也可通過環境變量 MGE_DEFAULT_DEVICE 來設置默認計算設備:

export MGE_DEFAULT_DEVICE='cpux'

export MGE_DEFAULT_DEVICE='gpux'

算子(Operator

MegEngine 中通過算子 (Operator) 來表示運算。 類似於 NumPy,MegEngine 中的算子支持基於 Tensor 的常見數學運算和操作。 下面介紹幾個簡單的示例:

Tensor 的加法:

In [8]:

a = mge.tensor(np.random.random((2,5)).astype('float32'))

print(a)

b = mge.tensor(np.random.random((2,5)).astype('float32'))

print(b)

print(a + b)

Tensor([[0.1337 0.5079 0.0929 0.0834 0.7817]

 [0.461  0.7906 0.81   0.6579 0.5813]], device=cpux:0)

Tensor([[0.8897 0.451  0.6765 0.3549 0.88  ]

 [0.4421 0.7505 0.6881 0.9912 0.6448]], device=cpux:0)

Tensor([[1.0234 0.9589 0.7694 0.4383 1.6617]

 [0.9031 1.5411 1.4981 1.6491 1.2261]], device=cpux:0)

Tensor 的切片:

In [9]:

print(a[1, :])

Tensor([0.461  0.7906 0.81   0.6579 0.5813], device=cpux:0)

Tensor 形狀的更改:

In [10]:

a.reshape(5, 2)

Out[10]:

Tensor([[0.1337 0.5079]

 [0.0929 0.0834]

 [0.7817 0.461 ]

 [0.7906 0.81  ]

 [0.6579 0.5813]], device=cpux:0)

reshape() 的參數允許存在單個缺省值,用 -1 表示。此時,reshape 會自動推理該維度的值:

In [11]:

# 原始維度是 (2, 5),當給出 -1的缺省維度值時,可以推理出另一維度為10

a = a.reshape(1, -1)

print(a.shape)

(1, 10)

創建的Tensor可以位於不同device,這根據當前的環境決定。通過 device 屬性查詢當前 Tensor 所在的設備。

In [12]:

print(a.device)

cpux:0

以上是一些簡單操作。 可以在 MegEngine API 文檔中查詢更多算子的用法,比如矩陣乘,卷積等。

求導器(Grad Manager

神經網絡的優化通常通過隨機梯度下降來進行。需要根據計算圖的輸出,對所有的中間數據節點求梯度,這一過程被稱之為 “反向傳播”,也就是鏈式求導法則。 例如,希望得到圖1中輸出 $ y $ 關於輸入 $ w $ 的梯度,那么反向傳播的過程如下圖所示:

 

 

 首先 $ y = p + b $ ,因此 $ \partial y / \partial p = 1 $ ; 接着,$ p = w * x $ ,因此,$ \partial p / \partial w = x $ 。 根據鏈式求導法則,$ \partial y / \partial w = \partial y / \partial p * \partial p / \partial w $ , 因此, $ y $ 關於輸入 $ w $ 的梯度為 $ 1 * x = x $ 。

MegEngine 為計算圖中的張量提供了自動求導功能,以上圖的例子說明: 假設圖中的 $ x $ 是 shape 為 (1, 3) 的張量, $ w $ 是 shape 為 (3, 1) 的張量, $ b $ 是一個標量。 利用MegEngine 計算 $ y = x * w + b $ 的過程如下:

In [13]:

import megengine as mge

import megengine.functional as F

from megengine.autodiff import GradManager

 

x = mge.tensor([1., 3., 5.]).reshape(1, 3)

w = mge.tensor([2., 4., 6.]).reshape(3, 1)

b = mge.tensor(-1.)

 

gm = GradManager().attach([w, b])   # 新建一個求導器,綁定需要求導的變量

with gm:                            # 開始記錄計算圖

    p = F.matmul(x, w)

    y = p + b

    gm.backward(y)                  # 計算 y 的導數

 

print(w.grad)

print(b.grad)

Tensor([[1.]

 [3.]

 [5.]], device=cpux:0)

Tensor([1.], device=cpux:0)

可以看到,求出的梯度本身也是 Tensor。

with 代碼段中的前向運算都會被求導器記錄。也可以用 record() 和 release() 來替代 with,分別控制求導器的開啟和關閉,代碼如下所示。

gm = GradManager().attach([w, b])   # 新建一個求導器,綁定需要求導的變量

gm.record()                         # 開始記錄計算圖

 

p = F.matmul(x, w)

y = p + b

 

gm.backward(y)                      # 計算 y 的導數

gm.release()                        # 停止記錄計算圖並釋放資源

此外,可以使用 detach 方法,把 Tensor 當作一個常量,這樣求導器將不會對其求導。如下所示:

In [14]:

x = mge.tensor([1., 3., 5.]).reshape(1, 3)

w = mge.tensor([2., 4., 6.]).reshape(3, 1)

b = mge.tensor(-1.)

 

gm = GradManager().attach([w, b])   # 新建一個求導器,綁定需要求導的變量

with gm:                            # 開始記錄計算圖

    p = F.matmul(x, w)

    y = p + b.detach()              # 停止對 b 求導

    gm.backward(y)                  # 計算 y 的導數

 

print(b.grad)

None

可以看到,梯度本身也是 Tensor。 注意:這里 F.grad() 的第一個參數 y 是個標量,目前尚不支持其為一個多維 Tensor。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM