MXNet 深度学习实战 (29):MXNet 基础 3.1

阅读数:20 2019 年 12 月 28 日 22:58

MXNet深度学习实战(29):MXNet基础 3.1

(NDArray)

内容简介
本书分为四大部分:
第一部分为准备篇(第 1~2 章),简单介绍深度学习相关的基础背景知识、深度学习框架 MXNet 的发展过程和优缺点,同时介绍基础开发环境的构建和 docker 的使用,帮助读者构建必要的基础知识背景。
第二部分为基础篇(第 3~7 章),介绍 MXNet 的几个主要模块,介绍 MXNet 的数据读取、数据增强操作,同时介绍了常用网络层的含义及使用方法、常见网络结构的设计思想,以及介绍模型训练相关的参数配置。
第三部分为实战篇(第 8~10 章),以图像分类、目标检测和图像分割这三个常用领域为例介绍如何通过 MXNet 实现算法训练和模型测试,同时还将结合 MXNet 的接口详细介绍算法细节内容。
第四部分为扩展篇(第 11~12 章),主要介绍 Gluon 和 GluonCV。Gluon 接口是 MXNet 推出的用于动态构建网络结构的重要接口,GluonCV 则是一个专门为计算机视觉任务服务的深度学习库。

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 数据结构实现的,因此熟悉该数据结构非常重要。

MXNet深度学习实战(29):MXNet基础 3.1

购书地址 https://item.jd.com/12620056.html?dist=jd

评论

发布