NDArray.ipynb

NDArray介绍

机器学习处理的对象是数据,数据一般是由外部传感器(sensors)采集,经过数字化后存储在计算机中,可能是文本、声音,图片、视频等不同形式。

这些数字化的数据最终会加载到内存进行各种清洗,运算操作。

几乎所有的机器学习算法都涉及到对数据的各种数学运算,比如:加减、点乘、矩阵乘等。所以我们需要一个易用的、高效的、功能强大的工具来处理这些数据并组支持各种复杂的数学运算。

在C/C++中已经开发出来了很多高效的针对于向量、矩阵的运算库,比如:OpenBLAS,Altlas,MKL等。

对于Python来说Numpy无疑是一个强大针对数据科学的工具包,它提供了一个强大的高维数据的数组表示,以及支持Broadcasting的运算,并提供了线性代数、傅立叶变换、随机数等功能强大的函数。

MXNet的NDArray与Numpy中的ndarray极为相似,NDAarray为MXNet中的各种数学计算提供了核心的数据结构,NDArray表示一个多维的、固定大小的数组,并且支持异构计算。那为什么不直接使用Numpy呢?MXNet的NDArray提供额外提供了两个好处:

  • 支持异构计算,数据可以在CPU,GPU,以及多GPU机器的硬件环境下高效的运算
  • NDArray支持惰性求值,对于复杂的操作,可以在有多个计算单元的设备上自动的并行运算。

NDArray的重要属性

每个NDarray都具有以下重要的属性,我们可以通过相应的api来访问:

  • ndarray.shape:数组的维度。它返回了一个整数的元组,元组的长度等于数组的维数,元组的每个元素对应了数组在该维度上的长度。比如对于一个n行m列的矩阵,那么它的形状就是(n,m)。
  • ndarray.dtype:数组中所有元素的类型,它返回的是一个numpy.dtype的类型,它可以是int32/float32/float64等,默认是'float32'的。
  • ndarray.size:数组中元素的个数,它等于ndarray.shape的所有元素的乘积。
  • ndarray.context:数组的存储设备,比如:cpu()gpu(1)
import mxnet as mx
import mxnet.ndarray as nd a = nd.ones(shape=(2,3),dtype='int32',ctx=mx.gpu(1))
print(a.shape, a.dtype, a.size, a.context)

NDArray的创建

一般来常见有2种方法来创建NDarray数组:

  1. 使用ndarray.array直接将一个list或numpy.ndarray转换为一个NDArray
  2. 使用一些内置的函数zeros,ones以及一些随机数模块ndarray.random创建NDArray,并预填充了一些数据。
  3. 从一个一维的NDArray进行reshape
import numpy as np

l = [[1,2],[3,4]]
print(nd.array(l)) # 从List转到NDArray
print(nd.array(np.array(l))) # 从np.array转到NDArray # 直接利用函数创建指定大小的NDArray
print (nd.zeros((3,4), dtype='float32'))
print (nd.ones((3,4), ctx=mx.gpu()))
# 从一个正态分布的随机数引擎生成了一个指定大小的NDArray,我们还可以指定分布的参数,比如均值,标准差等
print (nd.random.normal(shape=(3,4)))
print (nd.arange(18).reshape(3,2,3))

NDArray的查看

一般情况下,我们可以通过直接使用print来查看NDArray中的内容,我们也可以使用nd.asnumpy()函数,将一个NDArray转换为一个numpy.ndarray来查看。

a = nd.random.normal(0, 2, shape=(3,3))
print(a)
print(a.asnumpy())

基本的数学运算

NDArray之间可以进行加减乘除等一系列的数学运算,其中大部分的运算都是逐元素进行的。

shape=(3,4)
x = nd.ones(shape)
y = nd.random_normal(0, 1, shape=shape)
x + y # 逐元素相加
x * y # 逐元素相乘
nd.exp(y) # 每个元素取指数
nd.sin(y**2).T # 对y逐元素求平方,然后求sin,最后对整个NDArray转置
nd.maximum(x,y) # x与y逐元素求最大值

这里需要注意的是*运算是两个NDArray之间逐元素的乘法,要进行矩阵乘法,必须使用ndarray.dot函数进行矩阵乘

nd.dot(x, y.T)

索引与切片

MXNet NDArray提供了各种截取的方法,其用法与Python中list的截取操作以及Numpy.ndarray中的截取操作基本一致。

x = nd.arange(0, 9).reshape((3,3))
x[1:3] # 截取x的axis=0的第1和第2行
x[1:2,1:3] # 截取x的axis=0的第1行,axis=1的第一行和第二行

存储变化

在对NDArray进行算法运算时,每个操作都会开辟新的内存来存储运算的结果。例如:如果我们写y = x + y,我们会把y从现在指向的实例转到新创建的实例上去。我们可以把上面的运算看成两步:z = x + y; y = z

我们可以使用python的内置函数id()来验证。id()返回一个对象的标识符,当这个对象存在时,这个标识符一定是惟一的,在CPython中这个标识符实际上就是对象的地址。

x = nd.ones((3,4))
y = nd.ones((3,4))
before = id(y)
y = x + y
print(before, id(y))

在很多情况下,我们希望能够在原地对数组进行运算,那么我们可以使用下面的一些语句:

y += x
print(id(y)) nd.elemwise_add(x, y, out=y)
print(id(y)) y[:] = x + y
print(id(y))

在NDArray中一般的赋值语句像y = x,y实际上只是x的一个别名而已,x和y是共享一份数据存储空间的

x = nd.ones((2,2))
y = x
print(id(x))
print(id(y))

如果我们想得到一份x的真实拷贝,我们可以使用copy函数

y = x.copy()
print(id(y))

Broadcasting

广播是一种强有力的机制,可以让不同大小的NDArray在一起进行数学计算。我们常常会有一个小的矩阵和一个大的矩阵,然后我们会需要用小的矩阵对大的矩阵做一些计算。

举个例子,如果我们想要把一个向量加到矩阵的每一行,我们可以这样做

# 将v加到x的每一行中,并将结果存储在y中
x = nd.array([[1,2,3], [4,5,6], [7,8,9], [10, 11, 12]])
v = nd.array([1, 0, 1])
y = nd.zeros_like(x) # Create an empty matrix with the same shape as x for i in range(4):
y[i, :] = x[i, :] + v
print (y)

这样是行得通的,但是当x矩阵非常大,利用循环来计算就会变得很慢很慢。我们可以换一种思路:

x = nd.array([[1,2,3], [4,5,6], [7,8,9], [10, 11, 12]])
v = nd.array([1, 0, 1])
vv = nd.tile(v, (4, 1)) # Stack 4 copies of v on top of each other
y = x + vv # Add x and vv elementwise
print (y)
# 也可以通过broadcast_to来实现
vv = v.broadcast_to((4,3))
print(vv)

NDArray的广播机制使得我们不用像上面那样先创建vv,可以直接进行运算

x = nd.array([[1,2,3], [4,5,6], [7,8,9], [10, 11, 12]])
v = nd.array([1, 0, 1])
y = x + v
print(y)

对两个数组使用广播机制要遵守下列规则:

  1. 如果数组的秩不同,使用1来将秩较小的数组进行扩展,直到两个数组的尺寸的长度都一样。
  2. 如果两个数组在某个维度上的长度是一样的,或者其中一个数组在该维度上长度为1,那么我们就说这两个数组在该维度上是相容的。
  3. 如果两个数组在所有维度上都是相容的,他们就能使用广播。
  4. 如果两个输入数组的尺寸不同,那么注意其中较大的那个尺寸。因为广播之后,两个数组的尺寸将和那个较大的尺寸一样。
  5. 在任何一个维度上,如果一个数组的长度为1,另一个数组长度大于1,那么在该维度上,就好像是对第一个数组进行了复制。

在GPU上运算

NDArray支持数组在GPU设备上运算,这是MXNet NDArray和Numpy的ndarray最大的不同。默认情况下NDArray的所有操作都是在CPU上执行的,我们可以通过ndarray.context来查询数组所在设备。在有GPU支持的环境上,我们可以指定NDArray在gpu设备上。

gpu_device = mx.gpu(0)
def f():
a = mx.nd.ones((100,100))
b = mx.nd.ones((100,100), ctx=mx.cpu())
c = a + b.as_in_context(a.context)
print(c) f() # 在CPU上运算 # 在GPU上运算
with mx.Context(gpu_device):
f()

上面语句中使用了with来构造了一个gpu环境的上下文,在上下文中的所有语句,如果没有显式的指定context,则会使用wtih语句指定的context。

当前版本的NDArray要求进行相互运算的数组的context必须一致。我们可以使用as_in_context来进行NDArray context的切换。

NDArray的序列化

有两种方法可以对NDArray对象进行序列化后保存在磁盘,第一种方法是使用pickle,就像我们序列化其他python对象一样。

import pickle

a = nd.ones((2,3))
data = pickle.dumps(a) # 将NDArray直接序列化为内存中的bytes
b = pickle.loads(data) # 从内存中的bytes反序列化为NDArray pickle.dump(a, open('tmp.pickle', 'wb')) # 将NDArray直接序列化为文件
b = pickle.load(open('tmp.pickle', 'rb')) # 从文件反序列化为NDArray

在NDArray模块中,提供了更优秀的接口用于数组与磁盘文件(分布式存储系统)之间进行数据转换

a = mx.nd.ones((2,3))
b = mx.nd.ones((5,6))
nd.save("temp.ndarray", [a, b]) # 写入与读取的路径支持Amzzon S3以及Hadoop HDFS等。
c = nd.load("temp.ndarray")

惰性求值与自动并行化

MXNet使用了惰性求值来追求最佳的性能。当我们在Python中运行a = b + 1时,Python线程只是将运算Push到了后端的执行引擎,然后就返回了。这样做有下面两个好处:

  1. 当操作被push到后端后,Python的主线程可以继续执行下面的语句,这对于Python这样的解释性的语言在执行计算型任务时特别有帮助。
  2. 后端引擎可以对执行的语句进行优化,比如进行自动并行化处理。

后端引擎必须要解决的问题就是数据依赖和合理的调度。但这些操作对于前端的用户来说是完全透明的。我们可以使用wait_to_read来等侍后端对于NDArray操作的完成。在NDArray模块一类将数据拷贝到其他模块的操作,内部已经使用了wait_to_read,比如asnumpy()

import time
def 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 + 1
c = a + 2
d = b * c

下面的代码演示了在不同设备上并行调度

n = 10
a = 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))
tic = time.time()
c = do(a, n)
d = do(b, n) #上面两条语句可以同时执行,一条在CPU上运算,一条在GPU上运算
wait(c)
wait(d)
print('Both as finished in: %f sec' % (time.time() - tic))

参考资源

使用MXNet的NDArray来处理数据的更多相关文章

  1. base64图片数据类型转numpy的ndarray矩阵类型数据

    1.两种方法如下链接 https://www.cnblogs.com/mtcnn/p/9411683.html 2.第一种方法: # coding: utf-8 # python base64 编解码 ...

  2. 调参过程中的参数 学习率,权重衰减,冲量(learning_rate , weight_decay , momentum)

    无论是深度学习还是机器学习,大多情况下训练中都会遇到这几个参数,今天依据我自己的理解具体的总结一下,可能会存在错误,还请指正. learning_rate , weight_decay , momen ...

  3. mxnet安装及NDArray初体验

    一.mxnet安装 (以下均为mac环境) 有二种方式: 1.1 用conda安装 #创建gluon目录 mkdir gluon-tutorials && cd gluon-tutor ...

  4. 使用MxNet新接口Gluon提供的预训练模型进行微调

    1. 导入各种包 from mxnet import gluon import mxnet as mx from mxnet.gluon import nn from mxnet import nda ...

  5. mxnet框架样本,使用C++接口

    哇塞,好久么有跟进mxnet啦,python改版了好多好多啊,突然发现C++用起来才是最爽的. 贴一个mxnet中的C++Example中的mlp网络和实现,感觉和python对接毫无违和感.真是一级 ...

  6. Caffe、TensorFlow、MXnet三个开源库对比

    库名称 开发语言 支持接口 安装难度(ubuntu) 文档风格 示例 支持模型 上手难易 Caffe c++/cuda c++/python/matlab *** * *** CNN ** MXNet ...

  7. MXNet的新接口Gluon

    为什么要开发Gluon的接口 在MXNet中我们可以通过Sybmol模块来定义神经网络,并组通过Module模块提供的一些上层API来简化整个训练过程.那MXNet为什么还要重新开发一套Python的 ...

  8. MXNET:深度学习计算-GPU

    mxnet的设备管理 MXNet 使用 context 来指定用来存储和计算的设备,例如可以是 CPU 或者 GPU.默认情况下,MXNet 会将数据创建在主内存,然后利用 CPU 来计算.在 MXN ...

  9. MXNET:权重衰减-gluon实现

    构建数据集 # -*- coding: utf-8 -*- from mxnet import init from mxnet import ndarray as nd from mxnet.gluo ...

随机推荐

  1. Ubuntu 16.04 安装OpenCV 3.4.3

    cmake过程中可能遇到的问题:1.如果网络不好,出现ippicv_linux_20151201.tgz无法在终端下载的情况,则可以先单独下载 ippicv_linux_20151201.tgz之后, ...

  2. 关于HTML5中的sessionStorage的会话级缓存使用

    sessionStorage作为HTML5的Web Storage的两种存储方式之一.    用于本地存储一个会话(session)中的数据,这些数据只有在同一个会话中的页面才能访问并且当会话结束后数 ...

  3. AFNetworking Delete请求,报参数为空的错误

    使用AFNetWorking进行网络请求的时候,AFNetWorking会默认把get head delete这三个方法的请求参数拼到了url的后面,然后造成body为空,一行代码解决: manage ...

  4. 浅谈spring为什么推荐使用构造器注入

    转载自: https://www.cnblogs.com/joemsu/p/7688307.html 一.前言 ​ Spring框架对Java开发的重要性不言而喻,其核心特性就是IOC(Inversi ...

  5. VSCode下调试mocha测试用例

    之前使用tape做Node.js的单元测试,最方便一条就是使用它就和自己写个控制台应用程序测试一样,控制起来比较灵活,直接用VSCode进行调试也比较方便.然而tape输出中文字符总是乱码,想了很多办 ...

  6. 24. pt-slave-find

    pt-slave-find --host 192.168.100.101 --port 3306 --user admin --password admin [root@server01 test]# ...

  7. 为nginx创建windows服务自启动

    1.下载最新版的 Windows Service Wrapper 程序,比如我下载的名称是 "winsw-1.9-bin.exe",然后,把它命名成你想要的名字(比如: " ...

  8. GUI学习之二——PyQt控件初识

    一.控件概念 控件是一个程序界面上的各个独立的元素, 它具备用户点击.接收用户输入.展示不同内容.存放其他控件等功能. 二.控件分类 常用的控件按功能可以分为以下几种类 按钮 QPushButton— ...

  9. Python 的经典设计格言,格言来源于 Python 但不限于 Python

    The Zen of Python, by Tim Peters Beautiful is better than ugly. Explicit is better than implicit. Si ...

  10. OO第二单元单元总结

    总述 OO的第二单元主题是电梯调度,与第一单元注重对数据的输入输出的处理.性能的优化不同,第二单元的重心更多的是在线程安全与线程通信上.这此次单元实验之前,我并未对线程有过了解,更谈不上“使用经验”, ...