动手学深度学习第一课:从上手到多类分类-NDArray

Posted kisinfinite

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了动手学深度学习第一课:从上手到多类分类-NDArray相关的知识,希望对你有一定的参考价值。

使用NDArrary来处理数据

对于机器学习来说,处理数据往往是万事之开头。它包含两个部分:数据读取和当数据已经在内存里时如何处理。本章将关注后者。我们首先介绍NDArray,这是MXNet储存和变化数据的主要工具。如果你之前用过Numpy,你会发现NDArrayNumpy的多维数组非常类似。当然,NDArray提供更多的功能,首先是CPU和GPU的异步计算,其次是自动求导。这两点使得NDArray能更好地支持机器学习。

让我们开始

我们先介绍最基本的功能。如果你不懂我们用到的数学操作也不用担心,例如按元素加法,或者正态分布,我们会在之后的章节分别详细介绍。

我们首先从mxnet导入ndarray这个包

from mxnet import ndarray as nd

然后我们创推荐一个有3行和2列的的2D数组(通常也叫矩阵),并且把每个元素初始化成0

nd.zeros((3, 4))
[[ 0.  0.  0.  0.]
 [ 0.  0.  0.  0.]
 [ 0.  0.  0.  0.]]
<NDArray 3x4 @cpu(0)>

类似的,我们可以创建数组每个元素被初始化成1。

x = nd.ones((3, 4))
x
[[ 1.  1.  1.  1.]
 [ 1.  1.  1.  1.]
 [ 1.  1.  1.  1.]]
<NDArray 3x4 @cpu(0)>

或者从python的数组直接构造

nd.array([[1, 2], [2, 3]])
[[ 1.  2.]
 [ 2.  3.]]
<NDArray 2x2 @cpu(0)>

我们经常需要创建随机数组,就是说每个元素的值都是随机采样而来,这个经常被用来初始化模型参数。下面创建数组,它的元素服从均值0方差1的正态分布。

y = nd.random_normal(0, 1, shape=(3, 4))
y
[[ 1.16307855  0.48380461  0.29956347  0.15302546]
 [-1.16881478  1.55807102 -0.54594457 -2.35562968]
 [ 0.54144025  2.67850637  1.25463438 -0.54877406]]
<NDArray 3x4 @cpu(0)>

跟numpy一样,每个数组的形状可以通过.shape来获取

y.shape
(3, 4)

它的大小,就是总元素个数,是形状的累乘。

y.size
12

操作符

NDArray支持打量的数学操作符,例如按元素加法:

x + y
[[ 2.16307855  1.48380458  1.29956341  1.15302551]
 [-0.16881478  2.55807114  0.45405543 -1.35562968]
 [ 1.54144025  3.67850637  2.25463438  0.45122594]]
<NDArray 3x4 @cpu(0)>

乘法:

x * y
[[ 1.16307855  0.48380461  0.29956347  0.15302546]
 [-1.16881478  1.55807102 -0.54594457 -2.35562968]
 [ 0.54144025  2.67850637  1.25463438 -0.54877406]]
<NDArray 3x4 @cpu(0)>

指数运算:

nd.exp(y)
[[  3.19976878   1.6222347    1.34926963   1.16535461]
 [  0.31073502   4.74965048   0.57929432   0.09483377]
 [  1.71848011  14.56332493   3.50655603   0.57765752]]
<NDArray 3x4 @cpu(0)>
nd.exp(x)
[[ 2.71828175  2.71828175  2.71828175  2.71828175]
 [ 2.71828175  2.71828175  2.71828175  2.71828175]
 [ 2.71828175  2.71828175  2.71828175  2.71828175]]
<NDArray 3x4 @cpu(0)>
x
[[ 1.  1.  1.  1.]
 [ 1.  1.  1.  1.]
 [ 1.  1.  1.  1.]]
<NDArray 3x4 @cpu(0)>
z = nd.array([[1, 2, 3, 4],
              [5, 6, 7, 8]])
z
[[ 1.  2.  3.  4.]
 [ 5.  6.  7.  8.]]
<NDArray 2x4 @cpu(0)>
nd.exp(z)
[[  2.71828175e+00   7.38905621e+00   2.00855370e+01   5.45981483e+01]
 [  1.48413162e+02   4.03428802e+02   1.09663318e+03   2.98095801e+03]]
<NDArray 2x4 @cpu(0)>

上述结果说明exp函数是算“e的多少次方”,这个多少次方是数组里的数值。

也可以转置一个矩阵然后计算矩阵乘法:

a = y.T
a
[[ 1.16307855 -1.16881478  0.54144025]
 [ 0.48380461  1.55807102  2.67850637]
 [ 0.29956347 -0.54594457  1.25463438]
 [ 0.15302546 -2.35562968 -0.54877406]]
<NDArray 4x3 @cpu(0)>
nd.dot(x, a)
[[ 2.09947205 -2.51231813  3.92580676]
 [ 2.09947205 -2.51231813  3.92580676]
 [ 2.09947205 -2.51231813  3.92580676]]
<NDArray 3x3 @cpu(0)>

广播

当二元操作符左右两边ndarray形状不一样时,系统会尝试将其复制到一个共同的形状。例如a的第0维是3,b的第0维是1,那么a+b时会将b沿着第0维复制3遍:

a = nd.arange(3).reshape((3, 1))
b = nd.arange(2).reshape((1, 2))
print('a:', a)
print('b:', b)
print('a+b:', a+b)
a: 
[[ 0.]
 [ 1.]
 [ 2.]]
<NDArray 3x1 @cpu(0)>
b: 
[[ 0.  1.]]
<NDArray 1x2 @cpu(0)>
a+b: 
[[ 0.  1.]
 [ 1.  2.]
 [ 2.  3.]]
<NDArray 3x2 @cpu(0)>

跟NumPy的转换

ndarray可以很方便同numpy进行转换

import numpy as np
x = np.ones((2, 3))
y = nd.array(x)  # numpy -> mxnet
z = y.asnumpy()  # mxnet -> numpy
print([z, y])
print('x:', type(x))
print('y:', type(y))
print('z:', type(z))
[array([[ 1.,  1.,  1.],
       [ 1.,  1.,  1.]], dtype=float32), 
[[ 1.  1.  1.]
 [ 1.  1.  1.]]
<NDArray 2x3 @cpu(0)>]
x: <class 'numpy.ndarray'>
y: <class 'mxnet.ndarray.ndarray.NDArray'>
z: <class 'numpy.ndarray'>

替换操作

在前面的样例中,我们为每个操作新开内存来存储它的结果。例如,如果我们写y = x + y,我们会把y从现在指向的实例转到新建的实例上去。我们可以用Python的id()函数来看这个是怎么执行的:

x = nd.ones((3, 4))
y = nd.ones((3, 4))
before = id(y)  # id函数,计算出y对应的object是什么
y = y + x
print('before:', before)
print('id(y):', id(y))
id(y) == before
before: 2185981173432
id(y): 2185981171752





False

我们可以把结果通过[:]写到一个之前开好的数组里:

a = nd.zeros_like(x)
a
[[ 0.  0.  0.  0.]
 [ 0.  0.  0.  0.]
 [ 0.  0.  0.  0.]]
<NDArray 3x4 @cpu(0)>
before = id(a)
a[:] = x + y
id(a) == before
True

但是这里我们还是为x+y创建了临时空间,然后再复制到z,需要避免这个开销,我们可以使用操作符的全名版本中的out参数:

nd.elemwise_add(x, y, out=a)
id(a) == before
True

如果可以现有的数组之后不会再用,我们也可以用复制操作符达到这个目的

before = id(x)
x += y
id(x) == before
True

总结

ndarray是整个mxnet最基础的数据结构,ndarray模块提供一系列多维数组操作函数。

以上是关于动手学深度学习第一课:从上手到多类分类-NDArray的主要内容,如果未能解决你的问题,请参考以下文章

《动手学深度学习》图像分类数据集(Fashion-MNIST)

动手学深度学习 v2

机器学习笔记:多类逻辑回归

Softmax 回归 + 损失函数 + 图片分类数据集 动手学深度学习v2 pytorch

动手学深度学习——卷积层

《动手学深度学习》softmax回归(PyTorch版)