使用MXNet的NDArray来处理数据
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数组:
- 使用
ndarray.array
直接将一个list或numpy.ndarray转换为一个NDArray - 使用一些内置的函数
zeros
,ones
以及一些随机数模块ndarray.random
创建NDArray,并预填充了一些数据。 - 从一个一维的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,那么我们就说这两个数组在该维度上是相容的。
- 如果两个数组在所有维度上都是相容的,他们就能使用广播。
- 如果两个输入数组的尺寸不同,那么注意其中较大的那个尺寸。因为广播之后,两个数组的尺寸将和那个较大的尺寸一样。
- 在任何一个维度上,如果一个数组的长度为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到了后端的执行引擎,然后就返回了。这样做有下面两个好处:
- 当操作被push到后端后,Python的主线程可以继续执行下面的语句,这对于Python这样的解释性的语言在执行计算型任务时特别有帮助。
- 后端引擎可以对执行的语句进行优化,比如进行自动并行化处理。
后端引擎必须要解决的问题就是数据依赖和合理的调度。但这些操作对于前端的用户来说是完全透明的。我们可以使用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来处理数据的更多相关文章
- base64图片数据类型转numpy的ndarray矩阵类型数据
1.两种方法如下链接 https://www.cnblogs.com/mtcnn/p/9411683.html 2.第一种方法: # coding: utf-8 # python base64 编解码 ...
- 调参过程中的参数 学习率,权重衰减,冲量(learning_rate , weight_decay , momentum)
无论是深度学习还是机器学习,大多情况下训练中都会遇到这几个参数,今天依据我自己的理解具体的总结一下,可能会存在错误,还请指正. learning_rate , weight_decay , momen ...
- mxnet安装及NDArray初体验
一.mxnet安装 (以下均为mac环境) 有二种方式: 1.1 用conda安装 #创建gluon目录 mkdir gluon-tutorials && cd gluon-tutor ...
- 使用MxNet新接口Gluon提供的预训练模型进行微调
1. 导入各种包 from mxnet import gluon import mxnet as mx from mxnet.gluon import nn from mxnet import nda ...
- mxnet框架样本,使用C++接口
哇塞,好久么有跟进mxnet啦,python改版了好多好多啊,突然发现C++用起来才是最爽的. 贴一个mxnet中的C++Example中的mlp网络和实现,感觉和python对接毫无违和感.真是一级 ...
- Caffe、TensorFlow、MXnet三个开源库对比
库名称 开发语言 支持接口 安装难度(ubuntu) 文档风格 示例 支持模型 上手难易 Caffe c++/cuda c++/python/matlab *** * *** CNN ** MXNet ...
- MXNet的新接口Gluon
为什么要开发Gluon的接口 在MXNet中我们可以通过Sybmol模块来定义神经网络,并组通过Module模块提供的一些上层API来简化整个训练过程.那MXNet为什么还要重新开发一套Python的 ...
- MXNET:深度学习计算-GPU
mxnet的设备管理 MXNet 使用 context 来指定用来存储和计算的设备,例如可以是 CPU 或者 GPU.默认情况下,MXNet 会将数据创建在主内存,然后利用 CPU 来计算.在 MXN ...
- MXNET:权重衰减-gluon实现
构建数据集 # -*- coding: utf-8 -*- from mxnet import init from mxnet import ndarray as nd from mxnet.gluo ...
随机推荐
- GOF23设计模式
单例设计模式 饿汉式:
- cocoapods 安装中出的太多问题
前言: 新欢的公司,新买的电脑,新安装 cocoapods.然后开开心心去百度如何安装 cocoapods,前面的步骤我就不说了. 在 pod setup 上之后,网速超慢然后就失败 fatal: T ...
- 监控服务器配置(五)-----Redis_exporter安装配置
1.下载redis_exporter安装包(linux版)到 /opt/minitor/redis_exporter . 下载地址:https://download.csdn.net/download ...
- node.js中对 mysql 进行增删改查等操作和async,await处理
要对mysql进行操作,我们需要安装一个mysql的库. 一.安装mysql库 npm install mysql --save 二.对mysql进行简单查询操作 const mysql = requ ...
- jenkins远程执行脚本时报Bad version number in .class file
这几天在学习jenkins的持续集成和部署,到了最后一步启动服务的时候,遇到了一个这个Bad version number in .class file的报错(如下图). 这个报错在最开始手工部署的时 ...
- sqoop mysql导入hive 数值类型变成null的问题分析
问题描述:mysql通过sqoop导入到hive表中,发现有个别数据类型为int或tinyint的列导入后数据为null.设置各种行分隔符,列分隔符都没有效果. 问题分析:hive中单独将有问题的那几 ...
- SSL及使用openssl实现CA
TLS如何实现各种功能?数据如何加密在网络上传输? 网景(Netscape)公司在应用层和传输层加入了半层,把这个半层称之为SSL,SSL不是软件,可以理解是一个库,当http交给tcp层之前先通过s ...
- Java 字符编码(三)Reader 中的编解码
Java 字符编码(三)Reader 中的编解码 我们知道 BufferedReader 可以将字节流转化为字符流,那它是如何编解码的呢? try (BufferedReader reader = n ...
- 南昌邀请赛I.Max answer 单调栈+线段树
题目链接:https://nanti.jisuanke.com/t/38228 Alice has a magic array. She suggests that the value of a in ...
- 较大的互联网公司对Java的要求(转)
现在各大互联网公司,对Java类的校招要求越来越高,导致很多小伙伴都很迷茫,今天分享一篇谢照东关于Java学习进阶之路,希望能帮助到一些人 佛说五蕴六毒是妄,将因果都念作业障 把看过的书罗列一下 &l ...