《MXNet深度学习实战》—3 MXNet基础

举报
华章计算机 发表于 2019/06/16 16:56:52 2019/06/16
【摘要】 本节书摘来自华章计算机《MXNet深度学习实战》一书中的第3章,第3.1节,作者是魏凯峰。

CHAPTER 3

第3章

MXNet基础

 

相信很多程序员在学习一门新的编程语言或者框架时,都会先了解下该语言或者该框架涉及的数据结构,毕竟当你清晰地了解了数据结构之后才能更加优雅地编写代码,MXNet同样也是如此。在MXNet框架中你至少需要了解这三驾马车:NDArray、Symbol和Module。这三者将会是你今后在使用MXNet框架时经常用到的接口。那么在搭建或者训练一个深度学习算法时,这三者到底扮演了一个什么样的角色呢?这里可以做一个简单的比喻,假如将从搭建到训练一个算法的过程比作是一栋房子从建造到装修的过程,那么NDArray就相当于是钢筋水泥这样的零部件,Symbol就相当于是房子每一层的设计,Module就相当于是房子整体框架的搭建。

还记得我们在引入深度学习框架时提到的命令式编程(imperative programming)和符号式编程(symbolic programming)吗?在本章中你将实际感受二者的区别,因为NDArray接口采用的是命令式编程的方式,而Symbol接口采用的是符号式编程的方式。

3.1 NDArray

NDArray是MXNet框架中数据流的基础结构,NDArray的官方文档地址是:https://mxnet.apache.org/api/python/ndarray/ndarray.html,与NDArray相关的接口都可以在该文档中查询到。在了解NDArray之前,希望你先了解下Python中的NumPy库(http://www.numpy.org/),因为一方面在大部分深度学习框架的Python接口中,NumPy库的使用频率都非常高;另一方面大部分深度学习框架的基础数据结构设计都借鉴了NumPy。在NumPy库中,一个最基本的数据结构是array,array表示多维数组,NDArray与NumPy库中的array数据结构的用法非常相似,可以简单地认为NDArray是可以运行在GPU上的NumPy array。

接下来,我会介绍在NDArray中的一些常用操作,并提供其与NumPy array的对比,方便读者了解二者之间的关系。

首先,导入MXNet和NumPy,然后通过NDArray初始化一个二维矩阵,代码如下:

import mxnet as mx

import numpy as np

a = mx.nd.array([[1,2],[3,4]])

print(a)

输出结果如下:

[[1. 2.]

 [3. 4.]]

<NDArray 2x2 @cpu(0)>

接着,通过NumPy array初始化一个相同的二维矩阵,代码如下:

b = np.array([[1,2],[3,4]])

print(b)

输出结果如下:

[[1 2]

 [3 4]]

实际使用中常用缩写mx代替mxnet,mx.nd代替mxnet.ndarray,np代替numpy,本书后续篇章所涉及的代码默认都采取这样的缩写。

再来看看NumPy array和NDArray常用的几个方法对比,比如打印NDArray的维度信息:

print(a.shape)

输出结果如下:

(2, 2)

打印NumPy array的维度信息:

print(b.shape)

输出结果如下:

(2, 2)

打印NDArray的数值类型:

print(a.dtype)

输出结果如下:

<class 'numpy.float32'>

打印Numpy array的数值类型:

print(b.dtype)

输出结果如下:

int64

在使用大部分深度学习框架训练模型时默认采用的都是float32数值类型,因此初始化一个NDArray对象时默认的数值类型是float32。

如果你想要初始化指定数值类型的NDArray,那么可以通过dtype参数来指定,代码如下:

c=mx.nd.array([[1,2],[3,4]], dtype=np.int8)

print(c.dtype)

输出结果如下:

<class 'numpy.int8'>

如果你想要初始化指定数值类型的NumPy array,则可以像如下这样输入代码:

d = np.array([[1,2],[3,4]], dtype=np.int8)

print(d.dtype)

输出结果如下:

int8

在NumPy的array结构中有一个非常常用的操作是切片(slice),这种操作在NDArray中同样也可以实现,具体代码如下:

c = mx.nd.array([[1,2,3,4],[5,6,7,8]])

print(c[0,1:3])

输出结果如下:

[2. 3.]

<NDArray 2 @cpu(0)>

在NumPy array中可以这样实现:

d = np.array([[1,2,3,4],[5,6,7,8]])

print(d[0,1:3])

输出结果如下:

[2 3]

在对已有的NumPy array或NDArray进行复制并修改时,为了避免影响到原有的数组,可以采用copy()方法进行数组复制,而不是直接复制,这一点非常重要。下面以NDArray为例来看看采用copy()方法进行数组复制的情况,首先打印出c的内容:

print(c)

输出结果如下:

[[1. 2. 3. 4.]

 [5. 6. 7. 8.]]

<NDArray 2x4 @cpu(0)>

然后调用c的copy()方法将c的内容复制到f,并打印f的内容:

f = c.copy()

print(f)

输出结果如下:

[[1. 2. 3. 4.]

 [5. 6. 7. 8.]]

<NDArray 2x4 @cpu(0)>

修改f中的一个值,并打印f的内容:

f[0,0] = -1

print(f)

输出结果如下,可以看到此时对应位置的值已经被修改了:

[[-1. 2. 3. 4.]

 [ 5. 6. 7. 8.]]

<NDArray 2x4 @cpu(0)>

那么c中对应位置的值有没有被修改呢?可以打印此时c的内容:

print(c)

输出结果如下,可以看到此时c中对应位置的值并没有被修改:

[[1. 2. 3. 4.]

 [5. 6. 7. 8.]]

<NDArray 2x4 @cpu(0)>

接下来看看如果直接将c复制给e,会有什么样的情况发生:

e = c

print(e)

输出结果如下:

[[1. 2. 3. 4.]

 [5. 6. 7. 8.]]

<NDArray 2x4 @cpu(0)>

修改e中的一个值,并打印e的内容:

e[0,0] = -1

print(e)

输出内容如下:

[[-1. 2. 3. 4.]

 [ 5. 6. 7. 8.]]

<NDArray 2x4 @cpu(0)>

此时再打印c的内容:

print(c)

输出结果如下,可以看到对应位置的值也发生了改变:

[[-1. 2. 3. 4.]

 [ 5. 6. 7. 8.]]

<NDArray 2x4 @cpu(0)>

实际上,NumPy array和NDArray之间的转换也非常方便,NDArray转NumPy array可以通过调用NDArray对象的asnumpy()方法来实现:

g=e.asnumpy()

print(g)

输出结果如下:

[[-1. 2. 3. 4.]

 [ 5. 6. 7. 8.]]

NumPy array转NDArray可以通过mxnet.ndarray.array()接口来实现:

print(mx.nd.array(g))

输出结果如下:

[[-1. 2. 3. 4.]

 [ 5. 6. 7. 8.]]

<NDArray 2x4 @cpu(0)>?

前面曾提到过NDArray和NumPy array最大的区别在于NDArray可以运行在GPU上,从前面打印出来的NDArray对象的内容可以看到,最后都有一个@cpu,这说明该NDArray对象是初始化在CPU上的,那么如何才能将NDArray对象初始化在GPU上呢?首先,调用NDArray对象的context属性可以得到变量所在的环境:

print(e.context)

输出结果如下:

cpu(0)

然后,调用NDArray对象的as_in_context()方法指定变量的环境,例如这里将环境指定为第0块GPU:

e = e.as_in_context(mx.gpu(0))

print(e.context)

输出结果如下:

gpu(0)

环境(context)是深度学习算法中比较重要的内容,目前常用的环境是CPU或GPU,在深度学习算法中,数据和模型都要在同一个环境中才能正常进行训练和测试。MXNet框架中NDArray对象的默认初始化环境是CPU,在不同的环境中,变量初始化其实就是变量的存储位置不同,而且存储在不同环境中的变量是不能进行计算的,比如一个初始化在CPU中的NDArray对象和一个初始化在GPU中的NDArray对象在执行计算时会报错:

f = mx.nd.array([[2,3,4,5],[6,7,8,9]])

print(e+f)

显示结果如下,从报错信息可以看出是2个对象的初始化环境不一致导致的:

mxnet.base.MXNetError: [11:14:13] src/imperative/./imperative_utils.h:56: Check failed: inputs[i]->ctx().dev_mask() == ctx.dev_mask() (1 vs. 2) Operator broadcast_add require all inputs live on the same context. But the first argument is on gpu(0) while the 2-th argument is on cpu(0)

下面将f的环境也修改成GPU,再执行相加计算:

f = f.as_in_context(mx.gpu(0))

print(e+f)

输出结果如下:

[[  1.   5.   7.   9.]

 [ 11.  13.  15.  17.]]

<NDArray 2x4 @gpu(0)>

NDArray是MXNet框架中使用最频繁也是最基础的数据结构,是可以在CPU或GPU上执行命令式操作(imperative operation)的多维矩阵,这种命令式操作直观且灵活,是MXNet框架的特色之一。因为在使用MXNet框架训练模型时,几乎所有的数据流都是通过NDArray数据结构实现的,因此熟悉该数据结构非常重要。


【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。