具備基本的數學和程式設計知識,你就可以學習深度學習啦

dicksonjyl560101發表於2019-08-17

深度學習很難?對深度學習存有困惑,不知道從哪裡下手,不知道適不適合自己學習?只需瞭解基礎的數學和程式設計,如基礎的線性代數、微分和機率,以及基本的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替換成其他形狀,結果是否和預期一樣?

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/29829936/viewspace-2653976/,如需轉載,請註明出處,否則將追究法律責任。

相關文章