ITPub博客

首页 > 人工智能 > 深度学习 > 具备基本的数学和编程知识,你就可以学习深度学习啦

具备基本的数学和编程知识,你就可以学习深度学习啦

深度学习 作者:dicksonjyl560101 时间:2019-08-17 10:28:13 0 删除 编辑

深度学习很难?对深度学习存有困惑,不知道从哪里下手,不知道适不适合自己学习?只需了解基础的数学和编程,如基础的线性代数、微分和概率,以及基本的Python编程知识,你就可以真正的去学习深度学习喽!

从哪开始呢?从一本入手的《 动手学深度学习 》开始吧!

在了解这本书之前,带你看一下主要符号表

以下图片为学习深度学习的主要符号表,请点击大图预览。

具备基本的数学和编程知识,你就可以学习深度学习啦

具备基本的数学和编程知识,你就可以学习深度学习啦

具备基本的数学和编程知识,你就可以学习深度学习啦

动手学深度学习

具备基本的数学和编程知识,你就可以学习深度学习啦

目前市面上有关深度学习介绍的书籍大多可分两类,一类侧重方法介绍,另一类侧重实践和深度学习工具的介绍。本书同时覆盖方法和实践。本书不仅从数学的角度阐述深度学习的技术与应用,还包含可运行的代码,为读者展示如何在实际中解决问题。为了给读者提供一种交互式的学习体验,本书不但提供免费的教学视频和讨论区,而且提供可运行的Jupyter记事本文件,充分利用Jupyter记事本能将文字、代码、公式和图像统一起来的优势。这样不仅直接将数学公式对应成实际代码,而且可以修改代码、观察结果并及时获取经验,从而带给读者全新的、交互式的深度学习的学习体验。

本书面向希望了解深度学习,特别是对实际使用深度学习感兴趣的大学生、工程师和研究人员。本书不要求读者有任何深度学习或者机器学习的背景知识,读者只需具备基本的数学和编程知识,如基础的线性代数、微分、概率及Python编程知识。本书的附录中提供了书中涉及的主要数学知识,供读者参考。

本书的英文版Dive into Deep Learning是加州大学伯克利分校2019年春学期“Introduction to Deep Learning”(深度学习导论)课程的教材。截至2019年春学期,本书中的内容已被全球15 所知名大学用于教学。本书的学习社区、免费教学资源(课件、教学视频、更多习题等),以及用于本书学习和教学的免费计算资源(仅限学生和老师)的申请方法在本书配套网站zh.d2l.ai上发布。读者在阅读本书的过程中,如果对书中某节内容有疑惑,也可以扫一扫书中对应的二维码寻求帮助。

样章截选

在深度学习中,我们通常会频繁地对数据进行操作。作为动手学深度学习的基础,本节将介绍如何对内存中的数据进行操作。

在 MXNet 中,NDArray是一个类,也是存储和变换数据的主要工具。为了简洁,本书常将NDArray实例直接称作NDArray。如果你之前用过 NumPy,你会发现 NDArray和NumPy 的多维数组非常类似。然而,NDArray提供 GPU 计算和自动求梯度等更多功能,这些使NDArray更加适合深度学习。

2.2.1 创建NDArray

我们先介绍NDArray的最基本功能。如果对这里用到的数学操作不是很熟悉,可以参阅附录A。

首先从MXNet 导入ndarray 模块 。这里的nd是ndarray的缩写形式。


In [1]: from mxnet import nd

然后我们用arange 函数 创建一个行向量。


In [2]: x = nd.arange(12)

x
Out[2]:
[ 0. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11.]
<NDArray 12 @cpu(0)>

这时返回了一个NDArray 实例,其中包含了从 0 开始的 12 个连续整数。从打印X时显示的属性<NDArray 12 @cpu(0)>可以看出,它是长度为 12 的一维数组,且被创建在 CPU 使用的内存上。其中@cpu(0)里的 0 没有特别的意义,并不代表特定的核。

我们可以通过shape 属性 来获取NDArray实例的形状。


In [3]: x.shape

Out[3]: (12,)

我们也能够通过size 属性 得到NDArray实例中 元素 (element)的总数。


In [4]: x.size

Out[4]: 12

下面使用reshape 函数 把行向量x的形状改为(3, 4),也就是一个 3 行 4 列的矩阵,并记作X(矩阵变量常用大写字母表示)。除了形状改变之外,X中的元素保持不变。


In [5]: X = x.reshape((3, 4)) 

X
Out[5]:
[[ 0. 1. 2. 3.]
[ 4. 5. 6. 7.]
[ 8. 9. 10. 11.]]
<NDArray 3x4 @cpu(0)>

注意,X属性中的形状发生了变化。上面x.reshape((3, 4))也可写成x.reshape((-1, 4))或x.reshape((3, -1))。由于x的元素个数是已知的,这里的-1是能够通过元素个数和其他维度的大小推断出来的。

接下来,我们创建一个各元素为0,形状为( 2, 3, 4 )的张量。实际上,之前创建的向量和矩阵都是特殊的张量。


In [6]: nd.zeros((2, 3, 4))

Out[6]:
[[[0. 0. 0. 0.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]]
[[0. 0. 0. 0.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]]]
<NDArray 2x3x4 @cpu(0)>

类似地,我们可以创建各元素为 1 的张量。


In [7]: nd.ones((3, 4))

Out[7]:
[[1. 1. 1. 1.]
[1. 1. 1. 1.]
[1. 1. 1. 1.]]
<NDArray 3x4 @cpu(0)>

我们也可以通过 Python 的列表(list)指定需要创建的 NDArray 中每个元素的值。


In [8]: Y = nd.array([[2, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]]) 

Y
Out[8]:
[[2. 1. 4. 3.]
[1. 2. 3. 4.]
[4. 3. 2. 1.]]
<NDArray 3x4 @cpu(0)>

有些情况下,我们需要随机生成 NDArray 中每个元素的值。下面我们创建一个形状为(3, 4)的NDArray。它的每个元素都随机采样于均值为0、标准差为1的正态分布。


In [9]: nd.random.normal(0, 1, shape=(3, 4))

Out[9]:
[[ 2.2122064 0.7740038 1.0434405 1.1839255 ]
[ 1.8917114 -1.2347414 -1.771029 -0.45138445]
[ 0.57938355 -1.856082 -1.9768796 -0.20801921]]
<NDArray 3x4 @cpu(0)>

2.2.2 运算

NDArray支持大量的 运算符 (operator)。例如,我们可以对之前创建的两个形状为(3, 4)的NDArray做按元素加法。所得结果形状不变。


In [10]: X + Y

Out[10]:
[[ 2. 2. 6. 6.]
[ 5. 7. 9. 11.]
[12. 12. 12. 12.]]
<NDArray 3x4 @cpu(0)>

按元素乘法如下:


In [11]: X * Y

Out[11]:
[[ 0. 1. 8. 9.]
[ 4. 10. 18. 28.]
[32. 27. 20. 11.]]
<NDArray 3x4 @cpu(0)>

按元素除法如下:


In [12]: X / Y

Out[12]:
[[ 0. 1. 0.5 1. ]
[ 4. 2.5 2. 1.75]
[ 2. 3. 5. 11. ]]
<NDArray 3x4 @cpu(0)>

按元素做指数运算如下:


In [13]: Y.exp()

Out[13]:
[[ 7.389056 2.7182817 54.59815 20.085537 ]
[ 2.7182817 7.389056 20.085537 54.59815 ]
[54.59815 20.085537 7.389056 2.7182817]]
<NDArray 3x4 @cpu(0)>

除了按元素计算外,我们还可以使用dot 函数 做矩阵乘法。下面将X与Y的转置做矩阵乘法。由于X是 3 行 4 列的矩阵,Y转置为 4 行 3 列的矩阵,因此两个矩阵相乘得到 3 行 3 列的矩阵。


In [14]: nd.dot(X, Y.T)

Out[14]:
[[ 18. 20. 10.]
[ 58. 60. 50.]
[ 98. 100. 90.]]
<NDArray 3x3 @cpu(0)>

我们也可以将多个 NDArray 连结 (concatenate)。下面分别在行上(维度 0,即形状中的最左边元素)和列上(维度 1,即形状中左起第二个元素)连结两个矩阵。可以看到,输出的第一个NDArray在维度0的长度(6)为两个输入矩阵在维度0的长度之和(3 + 3),而输出的第二个NDArray在维度1的长度(8)为两个输入矩阵在维度1的长度之和(4 + 4)。


In [15]: nd.concat(X, Y, dim=0), nd.concat(X, Y, dim=1)

Out[15]: (
[[ 0. 1. 2. 3.]
[ 4. 5. 6. 7.]
[ 8. 9. 10. 11.]
[ 2. 1. 4. 3.]
[ 1. 2. 3. 4.]
[ 4. 3. 2. 1.]]
<NDArray 6x4 @cpu(0)>,
[[ 0. 1. 2. 3. 2. 1. 4. 3.]
[ 4. 5. 6. 7. 1. 2. 3. 4.]
[ 8. 9. 10. 11. 4. 3. 2. 1.]]
<NDArray 3x8 @cpu(0)>)

使用 条件判别式 可以得到元素为 0 或 1 的新的 NDArray。以X == Y为例,如果X和Y在相同位置的条件判断为真(值相等),那么新的 NDArray 在相同位置的值为 1;反之为 0。


In [16]: X == Y

Out[16]:
[[0. 1. 0. 1.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]]
<NDArray 3x4 @cpu(0)>

对NDArray中的所有元素求和得到只有一个元素的NDArray。


In [17]: X.sum()

Out[17]:
[66.]
<NDArray 1 @cpu(0)>

我们可以通过asscalar 函数 将结果变换为 Python 中的标量。下面例子中X的 L 2范数结果同上例一样是单元素 NDArray,但最后结果变换成了 Python 中的标量。


In [18]: X.norm().asscalar()

Out[18]: 22.494442

我们也可以把Y.exp()、X.sum()、X.norm()等分别改写为nd.exp(Y)、nd.sum(X)、nd.

norm(X)等。

2.2.3 广播机制

前面我们看到如何对两个形状相同的 NDArray 做按元素运算。当对两个形状不同的 NDArray按元素运算时,可能会触发 广播 (broadcasting)机制:先适当复制元素使这两个 NDArray形状相同后再按元素运算。

先定义两个NDArray。


In [19]: A = nd.arange(3).reshape((3, 1))

B = nd.arange(2).reshape((1, 2))
A, B
Out[19]: (
[[0.]
[1.]
[2.]]
<NDArray 3x1 @cpu(0)>,
[[0. 1.]]
<NDArray 1x2 @cpu(0)>)

由于A和B分别是3 行1列和1行2列的矩阵,如果要计算A + B,那么A中第一列的3个元素被广播(复制)到了第二列,而B中第一行的2个元素被广播(复制)到了第二行和第三行。如此,就可以对2个 3 行 2 列的矩阵按元素相加。


In [20]: A + B

Out[20]:
[[0. 1.]
[1. 2.]
[2. 3.]]
<NDArray 3x2 @cpu(0)>

2.2.4 索引

在NDArray中, 索引 (index)代表了元素的位置。NDArray的索引从 0 开始逐一递增。例如,一个 3 行 2 列的矩阵的行索引分别为 0、1 和 2,列索引分别为 0 和 1。

在下面的例子中,我们指定了NDArray的行索引截取范围[1:3]。依据左闭右开指定范围的惯例,它截取了矩阵X中行索引为 1 和 2 的两行。


In [21]: X[1:3]

Out[21]:
[[ 4. 5. 6. 7.]
[ 8. 9. 10. 11.]]
<NDArray 2x4 @cpu(0)>

我们可以指定 NDArray中需要访问的单个元素的位置,如矩阵中行和列的索引,并为该元素重新赋值。


In [22]: X[1, 2] = 9

X
Out[22]:
[[ 0. 1. 2. 3.]
[ 4. 5. 9. 7.]
[ 8. 9. 10. 11.]]
<NDArray 3x4 @cpu(0)>

当然,我们也可以截取一部分元素,并为它们重新赋值。在下面的例子中,我们为行索引为 1 的每一列元素重新赋值。


In [23]: X[1:2, :] = 12

X
Out[23]:
[[ 0. 1. 2. 3.]
[12. 12. 12. 12.]
[ 8. 9. 10. 11.]]
<NDArray 3x4 @cpu(0)>

2.2.5 运算的内存开销

在前面的例子里我们对每个操作新开内存来存储运算结果。举个例子,即使像Y = X + Y这样的运算,我们也会新开内存,然后将Y指向新内存。为了演示这一点,我们可以使用 Python 自带的id函数:如果两个实例的 ID 一致,那么它们所对应的内存地址相同;反之则不同。


In [24]: before = id(Y)

Y = Y + X
id(Y) == before
Out[24]: False

如果想指定结果到特定内存,我们可以使用前面介绍的索引来进行替换操作。在下面的例子中,我们先通过zeros_like创建和Y形状相同且元素为 0 的 NDArray,记为Z。接下来,我们把X + Y的结果通过[:]写进Z对应的内存中。


In [25]: Z = Y.zeros_like()

before = id(Z)
Z[:] = X + Y
id(Z) == before
Out[25]: True

实际上,上例中我们还是为X + Y开了临时内存来存储计算结果,再复制到Z对应的内存。如果想避免这个临时内存开销,我们可以使用运算符全名函数中的out参数。


In [26]: nd.elemwise_add(X, Y, out=Z)

id(Z) == before
Out[26]: True

如果X的值在之后的程序中不会复用,我们也可以用 X[:] = X + Y 或者 X += Y 来减少运算的内存开销。


In [27]: before = id(X)

X += Y
id(X) == before
Out[27]: True

2.2.6 NDArray和NumPy相互变换

我们可以通过array 函数 和asnumpy 函数 令数据在NDArray和NumPy格式之间相互变换。下面将NumPy实例变换成NDArray实例。


In [28]: import numpy as np

P = np.ones((2, 3))
D = nd.array(P)
D
Out[28]:
[[1. 1. 1.]
[1. 1. 1.]]
<NDArray 2x3 @cpu(0)>

再将 NDArray 实例变换成NumPy 实例。


In [29]: D.asnumpy()

Out[29]: array([[1., 1., 1.],
[1., 1., 1.]], dtype=f loat32)

小结

  • NDArray 是 MXNet 中存储和变换数据的主要工具。
  • 可以轻松地对 NDArray 创建、运算、指定索引,并与NumPy 之间相互变换。

练习

(1)运行本节中的代码。将本节中条件判别式X == Y改为X < Y或X > Y,看看能够得到什么样的 NDArray。

(2)将广播机制中按元素运算的两个NDArray替换成其他形状,结果是否和预期一样?

https://www.toutiao.com/a6725904669828186635/

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/29829936/viewspace-2653976/,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论
行万里路,读万卷书,阅无数人。 吃货一枚,爱吃湘菜,川菜,粤菜与杭帮菜,尝遍天下美食。 摄影爱好者,游遍名川大山,江河胡海,赏遍人间春色。 爱看影,尤其是港片,好莱坞大片。英文名:DicksonJin, 网名:风一样的BOY。精通SAP供应链咨询。2017年开始研习人工智能。

注册时间:2014-08-27

  • 博文量
    2126
  • 访问量
    3237619