MXNet官方文档中文版教程(2):GPUCPU张量计算(NDArray)
来源:互联网 发布:java 第三方登录 编辑:程序博客网 时间:2024/05/21 13:58
文档英文原版参见NDArray - Imperative tensor operations on CPU/GPU
在MXNet中,NDArray 是所有数学计算的核心数据结构。每个NDArray 代表了一个多维的,固定大小的齐次数组。如果你对python的科学计算包Numpy熟悉的话,你会发现mxnet.ndarray与numpy.ndarray在诸多方面十分相似。就像对应的NumPy数据结构,MXNet的NDArray也能够进行命令式计算。
所以你可能会想,为什么不用NumPy呢?MXNet提供了两种引人注目的优势。第一,MXNet的NDArray支持在各种硬件上的快速计算,包括CPU,GPU和多GPU集群;也可以扩展到云中的分布式系统上。第二,MXNet的NDArray惰性执行代码,允许在硬件设备上自动并行执行多种操作。
NDArray是一组同类型数据的集合,例如一个3D空间中的点的坐标值[2, 1, 6]就是一个shape=(3)的一维数组。而一个二维数组如下所示,其第一维度的长度为2,第二维度的长度为3:
[[0, 1, 2] [3, 4, 5]]
注意:这里的“维度”的使用时经过重载的。当我们说一个2维数组时,我们意思是一个有着两个维度的数组,而不是由两部分组成的数组。
NDArray对象常用的属性有:
- ndarray.shape:数组的维度。是一个表明每个维度的长度的整数元组。对于一个n行m列的矩阵,其shape 值为(n, m)
- ndarry.dtype:一个用于描述元素类型的numpy 对象
- ndarry.size:数组中的元素总个数,与shape 中元素的乘积相等
- ndarray.context:用来表明数组储存的设备,比如cpu() 或者gpu(1)
前提条件
为了完成以下教程,我们需要:
- MXNet:安装教程
- Jupyter
pip install jupyter
- GPUs:教程的部分实现需要用到GPU。如果没有GPU,只用把变量gpu_device 设置为mx.cpu()即可
创建数组
可以通过多种途径创建NDArray
- 我们可以通过array 函数由一般的Python列表或者元组建立数组:
import mxnet as mx# create a 1-dimensional array with a python lista = mx.nd.array([1,2,3])# create a 2-dimensional array with a nested python listb = mx.nd.array([[1,2,3], [2,3,4]]){'a.shape':a.shape, 'b.shape':b.shape}
- 或者由一个numpy.ndarray对象建立:
import numpy as npimport mathc = np.arange(15).reshape(3,5)# create a 2-dimensional array from a numpy.ndarray objecta = mx.nd.array(c){'a.shape':a.shape}
我们可以通过dtype 选项来指定元素的类型(支持numpy类型),元素类型默认为float32 。
# float32 is used in defaulta = mx.nd.array([1,2,3])# create an int32 arrayb = mx.nd.array([1,2,3], dtype=np.int32)# create a 16-bit float arrayc = mx.nd.array([1.2, 2.3], dtype=np.float16)(a.dtype, b.dtype, c.dtype)
如果我们仅仅知道数组的大小而还不确定元素的具体值。MXNet还提供了多种通过初始化占位符来创建数组的函数。
# create a 2-dimensional array full of zeros with shape (2,3)a = mx.nd.zeros((2,3))# create a same shape array full of onesb = mx.nd.ones((2,3))# create a same shape array with all elements set to 7c = mx.nd.full((2,3), 7)# create a same shape whose initial content is random and# depends on the state of the memoryd = mx.nd.empty((2,3))
打印数组
当要访问NDArray的内容时,一般先用asnumpy 函数将NDArray 转换为numpy.ndarray ,Numpy使用如下布局:
- 最后一个坐标轴从左至右打印
- 第二至最后一个坐标轴从上至下打印;
- 其余的也从上至下打印,每一个切片和下一个用空行隔开
b = mx.nd.arange(18).reshape((3,2,3))b.asnumpy()
基本操作
数组的算术运算符作用于两个数组依次对应的元素。结果被写入到一个新创建的数组。
a = mx.nd.ones((2,3))b = mx.nd.ones((2,3))# elementwise plusc = a + b# elementwise minusd = - c# elementwise pow and sin, and then transposee = mx.nd.sin(c**2).T# elementwise maxf = mx.nd.maximum(a, c)f.asnumpy()
与Numpy 相似,*用于矩阵对应元素乘法,而矩阵乘法则用dot。
a = mx.nd.arange(4).reshape((2,2))b = a * ac = mx.nd.dot(a,a)print("b: %s, \n c: %s" % (b.asnumpy(), c.asnumpy()))
赋值运算符,例如+= 和*= ,用于将结果写入一个已存在数组而不是新建一个数组。
a = mx.nd.ones((2,2))b = mx.nd.ones(a.shape)b += ab.asnumpy()
索引和切片
切片操作符 [ ] 从0维开始
a = mx.nd.array(np.arange(6).reshape(3,2))a[1:2] = 1a[:].asnumpy()
我们也可以通过slice_axis 方法来获得特定维的切片。
d = mx.nd.slice_axis(a, axis=1, begin=1, end=2)d.asnumpy()
形状修改
使用reshape 可以改变数组的形状,但要保证数组的大小保持不变。
a = mx.nd.array(np.arange(24))b = a.reshape((2,3,4))b.asnumpy()
concat 方法将多个数组堆叠在第一维(数组的形状必须一致)。
a = mx.nd.ones((2,3))b = mx.nd.ones((2,3))*2c = mx.nd.concat(a,b)c.asnumpy()
分解
像sum 和mean 这样的函数可以将一个数组分解为标量。
a = mx.nd.ones((2,3))b = mx.nd.sum(a)b.asnumpy()
也可以对某一维分解
c = mx.nd.sum_axis(a, axis=1)c.asnumpy()
广播
广播操作,沿长度为1的轴复制数组的值。以下代码在第1维广播数组:
a = mx.nd.array(np.arange(6).reshape(6,1))b = a.broadcast_to((6,4)) #b.asnumpy()
也可以沿多个轴同时广播,以下的例子是沿第1和2维广播:
c = a.reshape((2,1,1,3))d = c.broadcast_to((2,2,2,3))d.asnumpy()
广播在执行某些操作时将自动执行,比如用于不同形状数组的* 和 +
a = mx.nd.ones((3,2))b = mx.nd.ones((1,2))c = a + bc.asnumpy()
复制
当将NDArray分配给另一个Python变量时,我们将这个NDArray的引用复制过去了。然而,我们经常需要复制数据,以便我们可以在不覆盖原始值的情况下操作新数组。
a = mx.nd.ones((2,2))b = ab is a # will be True
copy 方法对数组和数据进行深拷贝。
b = a.copy()b is a # will be False
上面的代码申请一个新的NDArray,然后分配给b。当我们不想分配额外的内存时,我们可以使用copyto 方法或用切片操作[]来代替。
b = mx.nd.ones(a.shape)c = bc[:] = ad = ba.copyto(d)(c is b, d is b) # Both will be True
进阶
MXNet的NDArray还有一些高级的特性,让mxnet与其他库有所区别。
GPU支持
默认情况下,操作符在CPU上执行。MXNet可以很方便地切换到另一个计算资源,例如GPU(如果可行的话)。资源设备信息存储在ndarray.context。当MXNet以标志位USE_CUDA = 1 编译且存在至少一个英伟达GPU显卡时,我们可以通过使用上下文mx.gpu(0) 或者仅仅使用mx.gpu() 来将所有的的计算运行于GPU 0。如果有两个以上GPU,第二个GPU由mx.gpu(1) 表示, 以此类推。
gpu_device=mx.gpu() # Change this to mx.cpu() in absence of GPUs.def f(): a = mx.nd.ones((100,100)) b = mx.nd.ones((100,100)) c = a + b print(c)# in default mx.cpu() is usedf()# change the default context to the first GPUwith mx.Context(gpu_device): f()
我们也可以在创建数组时明确地指定上下文:
a = mx.nd.ones((100, 100), gpu_device)a
通常MXNet的计算需要两个数组位于同一个设备,有多种方法可以在不同设备之间拷贝数据:
a = mx.nd.ones((100,100), mx.cpu())b = mx.nd.ones((100,100), gpu_device)c = mx.nd.ones((100,100), gpu_device)a.copyto(c) # copy from CPU to GPUd = b + ce = b.as_in_context(c.context) + c # same to above{'d':d, 'e':e}
序列化 从/到 (分布式)文件系统
有两种简便的方法可以保存数据到(从读取数据)磁盘。第一种方法使用pickle,正如你所用其他任何Python对象。NDArray是与pickle模块兼容的。
import pickle as pkla = mx.nd.ones((2, 3))# pack and then dump into diskdata = pkl.dumps(a)pkl.dump(data, open('tmp.pickle', 'wb'))# load from disk and then unpackdata = pkl.load(open('tmp.pickle', 'rb'))b = pkl.loads(data)b.asnumpy()
第二种方法是通过save和load方法直接以二进制的格式存入磁盘。除了NDArray,我们也可以load/save单个NDArray或者列表(list):
a = mx.nd.ones((2,3))b = mx.nd.ones((5,6))mx.nd.save("temp.ndarray", [a,b])c = mx.nd.load("temp.ndarray")c
或者词典(dict):
d = {'a':a, 'b':b}mx.nd.save("temp.ndarray", d)c = mx.nd.load("temp.ndarray")c
load/save在两方面优于pickle模块:
1.使用Python接口保存的数据可以被用于其他的语言联编(Lanuage Binding)。例如我们可以在Python中保存数据:
a = mx.nd.ones((2, 3))mx.nd.save("temp.ndarray", [a,])
接着我们可以在R语言中读取它:
a <- mx.nd.load("temp.ndarray")as.array(a[[1]])## [,1] [,2] [,3]## [1,] 1 1 1## [2,] 1 1 1
2.如果建立了分布式文件系统例如Amazon S3或Hadoop HDFS,我们可以直接在其load和save。
mx.nd.save('s3://mybucket/mydata.ndarray', [a,]) # if compiled with USE_S3=1mx.nd.save('hdfs///users/myname/mydata.bin', [a,]) # if compiled with USE_HDFS=1
惰性计算与自动并行化
MXNet使用惰性计算以达到更好的性能。当我们在Python中运行a=b+1时,Python线程仅仅就是将操作符送入后端引擎然后返回。这种优化有两点好处:
- 一旦前一个操作符已经送出,python主线程可以继续执行其他计算。这对于有着巨大开销的前端语言十分有用。
- 使得后端引擎更加容易地探索深度优化,例如我们马上要讨论的自动并行化。
后端引擎能分解数据的相关性并保持计算的正确性。这些都对前端使用者透明。我们可以明确地使用结果数组的wait_to_read 方法来等待计算完成。从数组复制数据到其他包(例如Numpy)的操作会隐式调用wait_to_read 。
import timedef do(x, n): """push computation into the backend engine""" return [mx.nd.dot(x,x) for i in range(n)]def wait(x): """wait until all results are available""" for y in x: y.wait_to_read()tic = time.time()a = mx.nd.ones((1000,1000))b = do(a, 50)print('time for all computations are pushed into the backend engine:\n %f sec' % (time.time() - tic))wait(b)print('time for all computations are finished:\n %f sec' % (time.time() - tic))
除了分析数据读写的相关性,后端引擎能将非相关的计算并行。例如如下代码:
a = mx.nd.ones((2,3))b = a + 1c = a + 2d = b * c
第二和第三条语句可以并行执行。以下示例首先运行于CPU然后运行与GPU:
n = 10a = mx.nd.ones((1000,1000))b = mx.nd.ones((6000,6000), gpu_device)tic = time.time()c = do(a, n)wait(c)print('Time to finish the CPU workload: %f sec' % (time.time() - tic))d = do(b, n)wait(d)print('Time to finish both CPU/GPU workloads: %f sec' % (time.time() - tic))
现在我们同时下放所有工作量。后端引擎会尽量并行CPU和GPU计算。
tic = time.time()c = do(a, n)d = do(b, n)wait(c)wait(d)print('Both as finished in: %f sec' % (time.time() - tic))
- MXNet官方文档中文版教程(2):GPUCPU张量计算(NDArray)
- MXNet官方文档中文版教程(1):教程(Tutorials)
- MXNet官方文档中文版教程(3):神经网络图(Symbol)
- MXNet官方文档中文版教程(5):加载数据(Iterators)
- MXNet官方文档中文版教程(6):线性回归(LinearRegression)
- MXNet官方文档中文版教程(9):大规模图像分类
- MXNet官方文档中文版教程(4):神经网络训练和推断(Module)
- MXNet官方文档中文版教程(7):手写数字识别(Handwritten Digit Recognition)
- MXNet官方文档中文版教程(8):使用预训练模型预测
- MXNet官方文档教程(1):扬帆起航(Get Started)
- MXNet官方文档教程(6):神经网络图
- MXNet官方文档教程(2):基于卷积神经网络的手写数字识别示例
- MXNet官方文档教程(5):CPU&GPU多维数组
- MXNet官方文档教程(7):训练和推断模型
- Leveldb官方文档(中文版)
- Ruby官方文档翻译(Ruby官方文档中文版)
- mxnet深度学习(NDArray)
- NGUI 官方文档中文版2 UICamera
- js实现可输入的下拉框
- ios自带录音格式(caf)转MP3格式
- JAVA中StringBuffer类常用方法详解
- 【学习笔记】【数字图像处理】空间滤波
- Mysql MyISAM引擎的特征
- MXNet官方文档中文版教程(2):GPUCPU张量计算(NDArray)
- Qtcreator中常用快捷键总结
- Python 条件判断和循环(3)
- 一般论文发表流程有哪些
- 欢迎使用CSDN-markdown编辑器
- C#中String类的几个方法(IndexOf、LastIndexOf、Substring)
- mysql事务总结
- ZooKeeper(一)之为什么要使用ZooKeeper
- CSS 浮动(float:left),清除浮动(clear:both)