NDArray是从MXNet导入的一个类(from mxnet import nd),存储和变换数据的主要工具,跟Numpy的用法非常类似,语法区别不是很大,可以很快的上手,增加了对GPU计算以及可以自动求梯度等很多非常实用且方便的功能,简单来说就是NDArray变得更加适合深度学习了。
import numpy as np from mxnet import nd a=nd.arange(10) [0. 1. 2. 3. 4. 5. 6. 7. 8. 9.]
返回一个NDArray实例,语法跟Numpy一样,不同的是多了一个@cpu(0),意思就是创建在CPU使用的内存上。
a.shape (10,) a.size 10 a.reshape((2,5)) #这里有点区别,nd里的形状变换,数量可以小于10,而np.reshape需要数量刚好相等才可以 [[0. 1. 2. 3. 4.] [5. 6. 7. 8. 9.]]nd.zeros(10) [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.] nd.ones(10) [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.] nd.array([[1,2,3],[4,5,6]]) [[1. 2. 3.] [4. 5. 6.]] nd.random.normal(0,1,shape=(2,3)) [[ 1.1630785 0.4838046 0.29956347] [ 0.15302546 -1.1688148 1.558071 ]]
normal()的均值为0,标准差为1的正态分布,参数形状shape有点区别,在np里是size
np.random.normal(0,1,size=(2,3))
数据的连接有点区别(三处不同) np.concatenate((c,d),axis=0) nd.concat(a,b,dim=0) 其余nd里面的+,-,*,/,exp,sum,sqrt,dot等运算和np语法一样
类型转换
转换成标量 a=nd.arange(10) a.norm().asscalar()#16.881943,L2范数:平方和再开平方np.sqrt(np.sum(pow(c,2))) NDArray转换成NumPy x.asnumpy()
索引的操作和np的用法一样
x=nd.array([[1,2,3],[76,4,5],[21,23,2]]) x[1,2]=99 [[ 1. 2. 3.] [76. 4. 99.] [21. 23. 2.]]x[1:2,:]=999 [[ 1. 2. 3.] [999. 999. 999.] [ 21. 23. 2.]]
内存开销
在做运算的时候,如果不注意的话,容易出现开辟新的内存 x=nd.array([[1,2,3],[76,4,5],[21,23,2]]) y=nd.array([[1,2,3],[3,4,5],[6,7,8]]) print(id(x))#1561184090760 x=x+y print(id(x))#1561193379528 这两个x的id不一样,说明不是在同一个内存里面,可以使用运算符全名函数中的out参数来指定,避免产生新的内存开销 z=y.zeros_like() print(id(z))#1756866715912 nd.elemwise_add(x,y,out=z) print(id(z))#1756866715912 这个z在x+y的前后,id是一样的,说明没有产生新的内存 当然,如果确保程序在后面不再使用到前面定义的x,可以使用x+=y或者x[:]=x+y来代替x=x+y,这样也不会产生新的内存开销
自动求梯度【这个很关键,后期使用最多】
函数y=2x²,求关于x的梯度?实际梯度是4x,我们最后也可以看到结果是对的
from mxnet import nd,autograd
x=nd.arange(5).reshape((5,1))
x.attach_grad()#申请存储梯度所需要的内存
#record函数记录与梯度有关的计算(类似前向传播)
with autograd.record():
y=2*nd.dot(x.T,x)#[[60.]]
y.backward()#反向传播求出梯度(如果y不是标量,会先求和得到新变量,再求该变量有关x的梯度)
#assert(x.grad-4*x).norm().asscalar()==0
x.grad
[[ 0.]
[ 4.]
[ 8.]
[12.]
[16.]]
默认情况下的autograd会将运行模式从预测模式转为训练模式
print(autograd.is_training())#False预测模式
with autograd.record():
y=2*nd.dot(x.T,x)
print(autograd.is_training())#True训练模式
除了对函数求梯度之外,还可以对控制流(if判断,while等循环)求梯度,这样就显得更加方便了
from mxnet import nd,autograd
def f(x):
y=2*x
while y.norm().asscalar()<1000:
y=2*y
if y.sum().asscalar()>0:
z=y
else:
z=100*y
return z
x=nd.random.normal(shape=(2,3))
x.attach_grad()
with autograd.record():
z=f(x)
z.backward()
x.grad==z/x
print(x.grad==z/x,x.grad,z/x)
[[1. 1. 1.]
[1. 1. 1.]]
[[512. 512. 512.]
[512. 512. 512.]]
[[512. 512. 512.]
[512. 512. 512.]]
这个f函数,实质是一个线性函数,梯度的值就是z/x,结果也证明了是正确的。
当然对于有些语法忘记了,这个没有关系,help进行查看,很方便,比如help(nd.concat)
最后对于想更进一步了解如何手工求解梯度的过程,可以查看以前的两篇文章:
误差反向传播法(一)【计算图】https://blog.csdn.net/weixin_41896770/article/details/120461867
误差反向传播法(二)【神经网络以层的方式实现】https://blog.csdn.net/weixin_41896770/article/details/120487639



