NDArray與NumPy的多維數組類似,但NDArray提供了更多的功能:GPU和CPU的異步計算;自動求導。這使得NDArray能更好地支持機器學習。
初始化
from mxnet import ndarray as nd
nd.zeros((3,4))
nd.ones((3,4))
nd.array([[1,2],[3,4]])
out:
[[1. 2.][3. 4.]] <NDArray 2x2 @cpu(0)>
nd.random_normal(0,1,shape=(3,4)) #標准正態分布
# 輸出信息
y.shape
y.size
操作符
按照相應元素運算
x+y
x*y
nd.exp(x)
矩陣的乘法
nd.dot(x, y.T)
廣播(Beoadcasting)
當二元操作符左右兩邊ndarray形狀不一樣時,系統會嘗試將它們擴充到共同的形狀。
a=nd.arange(3).reshape((3,1))
b=nd.arange(2),reshape((1,2))
print ('a+b', a+b)
out:
a+b:
[[ 0. 1.] [ 1. 2.] [ 2. 3.]] <NDArray 3x2 @cpu(0)>
與NumPy的轉換
x=np.ones((2,3))
y=nd.array(x) # numpy->mxnet
z=y.asnumpy() # mxnet->numpy
替換操作
如果我們寫y=x+y,會開辟新的內存來存儲計算結果,如:
x=nd.ones((3,4))
y=nd.ones((3,4))
before = id(y)
y=y+x
id(y)==before # False
可以通過[:]寫到之間建立好的數組中
z=nd.zeros_like(y)
before=id(z)
z[:]=x+y
id(z)==before # True
上述,系統還是為x+y創建了臨時空間,再復制給了z。為了避免這個開銷,可以使用操作符的全名版本並指定out參數
nd.elemwise_add(x,y,out=z)
id(z)==before # True
截取(Slicing)
x=nd.arange(0,9).reshape((3,3))
x[1:3]
out:
[[ 3. 4. 5.] [ 6. 7. 8.]] <NDArray 2x3 @cpu(0)>
#改變指定位置的值
x[1,2]=9.
#多維截取
x[1:2,1:3]
#多維寫入
x[1:2,1:3]=9.
out:
[[ 0. 1. 2.] [ 3. 9. 9.] [ 6. 7. 8.]] <NDArray 3x3 @cpu(0)>
使用autograd自動求導
import mxnet.ndarray as nd
import mxnet.autograd as ag
為變量附上梯度
x=nd.array([[1,2],[3,4]])
x.attach_grad() # ndarray的方法申請相應的空間
# 定義函數f=2x*x,顯式要求mxnet記錄我們要求導的程序
with ag.record():
y=x*2
z=y*x
# 通過z.backword()來進行求導,如果z不是一個標量,z.backward()等價於nd.sum(z).backward().
z.backward()
print('x.grad: ',x.grad)
x.grad == 4*x
# output
x.grad:
[[4., 8.]
[12., 16.]]
<NDArray 2x2 @cpu(0)>
[[ 1. 1.]
[ 1. 1.]]
<NDArray 2x2 @cpu(0)>
對控制流求導
命令式的編程的一個便利之處是幾乎可以對任意的可導程序進行求導,即使里面包含了 Python 的 控制流。對於計算圖框架來說,這個對應於動態圖,就是圖的結構會根據輸入數據不同而改變。
def f(a):
b=a*2
while nd.norm(b).asscalar() < 1000:
b=b*2
if nd.sum(b).asscalar() > 0:
c=b
else:
c = 100 * b
return c
使用record和backward求導
a = nd.random_normal(shape=3)
a.attach_grad()
with ag.record():
c = f(a)
c.backward()
頭梯度和鏈式法則
基於鏈式法則:
\[\frac{dz}{dx} = \frac{dz}{dy}\frac{dy}{dx} \]
\(\frac{dz}{dy}\)就是\(\frac{dy}{dx}\)的頭梯度,而計算\(\frac{dz}{dy}\),頭梯度則為默認值,即nd.ones_like(y)。我們也可以手動指定頭梯度。
with ag.record():
y = x * 2
z = y * x
head_gradient = nd.array([[10, 1.], [.1, .01]])
z.backward(head_gradient)
print(x.grad)
# out
x=
[[1.,2.]
[3.,4.]]
<NDArray 2x2 @cpu(0)>
x.grad=
[[40., 8.]
[12., 0.16]]
<NDArray 2x2 @cpu(0)>
