基礎(chǔ)入門:深度學(xué)習(xí)矩陣運(yùn)算的概念和代碼實(shí)現(xiàn)(矩陣運(yùn)算算法)
選自Medium
機(jī)器之心編譯
參與:蔣思源
本文從向量的概念與運(yùn)算擴(kuò)展到矩陣運(yùn)算的概念與代碼實(shí)現(xiàn),對(duì)機(jī)器學(xué)習(xí)或者是深度學(xué)習(xí)的入門者提供最基礎(chǔ),也是最實(shí)用的教程指導(dǎo),為以后的機(jī)器學(xué)習(xí)模型開發(fā)打下基礎(chǔ)。
在我們學(xué)習(xí)機(jī)器學(xué)習(xí)時(shí),常常遇到需要使用矩陣提高計(jì)算效率的時(shí)候。如在使用批量梯度下降迭代求最優(yōu)解時(shí),正規(guī)方程會(huì)采用更簡(jiǎn)潔的矩陣形式提供權(quán)重的解析解法。而如果不了解矩陣的運(yùn)算法則及意義,甚至我們都很難去理解一些如矩陣因子分解法和反向傳播算法之類的基本概念。同時(shí)由于特征和權(quán)重都以向量?jī)?chǔ)存,那如果我們不了解矩陣運(yùn)算,代碼實(shí)現(xiàn)將變得十分艱難。
什么是線性代數(shù)?
在深度學(xué)習(xí)中,線性代數(shù)是一個(gè)強(qiáng)大的數(shù)學(xué)工具箱,它提供同時(shí)計(jì)算多維數(shù)組的方法。線性代數(shù)不僅會(huì)提供如同向量和矩陣那樣的結(jié)構(gòu)來(lái)儲(chǔ)存這些數(shù)字,還會(huì)提供矩陣的加、減、乘、除和其他運(yùn)算規(guī)則。
線性代數(shù)為什么如此實(shí)用?
線性代數(shù)將復(fù)雜問(wèn)題轉(zhuǎn)變?yōu)楹?jiǎn)單、直觀和高效的計(jì)算問(wèn)題。下面的例子可以表明實(shí)現(xiàn)同樣的功能,線性代數(shù)的代碼表達(dá)是如何的簡(jiǎn)潔與美觀。
# Multiply two arrays
# Linear algebra version
線性代數(shù)怎樣應(yīng)用到深度學(xué)習(xí)?
神經(jīng)網(wǎng)絡(luò)將權(quán)重儲(chǔ)存在矩陣當(dāng)中。而線性代數(shù)特別是在 GPU 上,可以對(duì)矩陣進(jìn)行簡(jiǎn)單迅捷的計(jì)算處理。實(shí)際上,GPU 的設(shè)計(jì)就是源于向量和矩陣計(jì)算處理的基本概念。這和圖像由像素塊陣列構(gòu)成,視頻游戲使用巨量、連續(xù)展開的矩陣生成引人注目的游戲體驗(yàn)是一樣的。GPU 會(huì)并行地操作整個(gè)矩陣?yán)镌兀皇且粋€(gè)接一個(gè)地處理。
向量
向量由數(shù)字或其它項(xiàng)組成的一維陣列。在幾何學(xué)中,向量?jī)?chǔ)存了空間中一個(gè)點(diǎn)潛在的改變方向。向量 [3,-2] 也就代表著原點(diǎn)向(3,-2)這一點(diǎn)運(yùn)動(dòng)的趨向。若向量所具有的維度超過(guò)一維,那么就稱之為矩陣。
向量的符號(hào)表示
有很多符號(hào)方式都能表示向量,下面是在本篇文章中你可能會(huì)遇到的:
幾何學(xué)中的向量
向量一般表征著一個(gè)點(diǎn)的運(yùn)動(dòng),一個(gè)向量同時(shí)儲(chǔ)存其潛在變化的方向和大小。如下圖所示,在平面空間中畫出了向量 [-2,5],因?yàn)橄蛄恐粌?chǔ)存了方向和大小,那么平移并不會(huì)改變向量的值,所以所有平移的向量(方向和大小不變)都是相等的。
標(biāo)量運(yùn)算
標(biāo)量運(yùn)算即為向量和數(shù)字間的運(yùn)算。向量與數(shù)的運(yùn)算就是向量?jī)?nèi)每一個(gè)元素與這一個(gè)數(shù)進(jìn)行相應(yīng)的運(yùn)算。如下圖的一個(gè)標(biāo)量運(yùn)算:
向量間運(yùn)算
在向量間的運(yùn)算中,對(duì)應(yīng)位置的值可以組合而產(chǎn)生一個(gè)新向量。第一個(gè)向量的第 i 個(gè)值只與第二個(gè)向量的第 i 個(gè)值相匹配。這也就意味著向量之間的維度必須相等才能進(jìn)行運(yùn)算。下圖表明向量之間的加減法是對(duì)應(yīng)元素之間的加減,代碼表明了向量之間的加減和除法。
y = np.array([1,2,3])
在 numpy 中,如果向量是一維的,那么他就能看作是一個(gè)標(biāo)量,與其他多維向量的運(yùn)算就相當(dāng)于一個(gè)數(shù)。
向量乘法
向量的乘法有兩種類型:一種是點(diǎn)積,另一種是 Hadamard 積。
點(diǎn)積
兩個(gè)向量的點(diǎn)積結(jié)果是一個(gè)標(biāo)量。向量和矩陣(矩陣乘法)的點(diǎn)積在深度學(xué)習(xí)中是最重要的運(yùn)算之一。
Hadamard 積
Hadamard 積是元素之間的乘積,并得出一個(gè)向量。從下圖可以看出來(lái) Hadamard 積就是將向量對(duì)應(yīng)元素相乘積。
y = np.array([1,2,3])
向量場(chǎng)
向量場(chǎng)展示了如果我們運(yùn)用一個(gè)向量函數(shù)(如向量加法或乘法等),其中任意點(diǎn)(x,y)會(huì)有什么樣的運(yùn)動(dòng)傾向。在空間中給定一點(diǎn),向量場(chǎng)就是我們使用的向量運(yùn)算在該點(diǎn)的方向和大小。
該向量場(chǎng)很有意思,因?yàn)楦鶕?jù)不同的出發(fā)點(diǎn),其都會(huì)有不同的方向。出現(xiàn)這種情況是因?yàn)樵谠撓蛄繄?chǎng)中,向量背后儲(chǔ)存的項(xiàng)不是一個(gè) 5 或 2 那樣的實(shí)數(shù),它是 2x 或 x^2 那樣的變量。對(duì)于圖表中的每一個(gè)點(diǎn),我們將坐標(biāo)軸變換為 2x 或 x^2,然后將起始點(diǎn)畫一個(gè)箭頭到新的坐標(biāo)點(diǎn),這樣就制成了上圖。向量場(chǎng)對(duì)機(jī)器學(xué)習(xí)算法(如梯度下降算法)的可視化十分重要。
矩陣
矩陣就是一個(gè)由數(shù)字或其它項(xiàng)組成的表格,只不過(guò)是該表格會(huì)有特定的加法、減法和乘法規(guī)則。
矩陣的階
我們描述矩陣的維度由階來(lái)表達(dá):即行數(shù)×列數(shù)(如 3×2)階矩陣。
a = np.array([
b = np.array([
矩陣的標(biāo)量運(yùn)算
矩陣的標(biāo)量運(yùn)算和向量的標(biāo)量運(yùn)算是一樣的??梢院?jiǎn)單地將標(biāo)量和矩陣中的每一個(gè)元素做運(yùn)算處理(如加、減、乘、除等)。
a = np.array(
矩陣間的運(yùn)算
為了能進(jìn)行加減運(yùn)算,兩個(gè)矩陣的階必須相等。然后我們可以對(duì)兩個(gè)矩陣相應(yīng)的元素進(jìn)行運(yùn)算處理。如下圖就是兩階方陣的加法。
a = np.array([
a b
a — b
Numpy broadcasting
在 Numpy 中,矩陣之間運(yùn)算所需要的階相等可以通過(guò)一個(gè)稱之為 broadcasting 的機(jī)制變得不那么嚴(yán)格。如果兩個(gè)矩陣相應(yīng)的階(行數(shù)×列數(shù))滿足下面兩個(gè)要求,那么它們就是可以進(jìn)行運(yùn)算的:
-
兩個(gè)矩陣的階相等
矩陣的階有一個(gè)維度是 1
a = np.array([
# Same no. of rows
# Same no. of columns
# Different no. of columns
而在高維(三維或四維等)矩陣的情況下,矩陣間運(yùn)算更有意思,不過(guò)在深度學(xué)習(xí)里并不常見。
矩陣 Hadamard 乘積
Hadamard 乘積同樣是矩陣間的運(yùn)算,即兩個(gè)矩陣間相同位置的元素相互乘積。
a = np.array(
# Uses python’s multiply operator
在 numpy 中,矩陣和向量的 Hadamard 乘積只需要兩個(gè)矩陣滿足 broadcasting 機(jī)制的要求就行。
矩陣轉(zhuǎn)置
神經(jīng)網(wǎng)絡(luò)在處理不同大小的權(quán)重或輸入矩陣時(shí),經(jīng)常出現(xiàn)矩陣的階不符合矩陣乘法的要求。矩陣的轉(zhuǎn)置通過(guò)將矩陣旋轉(zhuǎn)一下以滿足矩陣乘法所需要的維度要求。下面,我們可以通過(guò)兩步完成矩陣的轉(zhuǎn)置。
1. 旋轉(zhuǎn)矩陣 90 度
2. 將每一行的元素都反向?qū)懸槐?/p>
以下我們將矩陣 M 轉(zhuǎn)置為矩陣 T
a = np.array([
[1, 2],
[3, 4]])
a.T
矩陣乘法
矩陣乘法是由一組乘法法則組成,他們共同作用以乘得一個(gè)新矩陣。
規(guī)則
并不是所有矩陣都能進(jìn)行矩陣乘法運(yùn)算的,如果兩個(gè)矩陣能相乘,那么它需要滿足以下條件:
1. 第一個(gè)矩陣列的數(shù)量必須等于第二個(gè)矩陣行的數(shù)量
2. m×n 階矩陣左乘 n×k 階矩陣的結(jié)果是 m×k 階矩陣。新得出來(lái)矩陣就等于第一個(gè)矩陣的行數(shù)×第二矩陣的列數(shù)。
步驟
矩陣乘法的步驟和向量點(diǎn)積的過(guò)程是相似的,它們都是由對(duì)應(yīng)位置的元素進(jìn)行乘積并相加而得出。第一個(gè)矩陣每一行的維度和第二個(gè)矩陣每一列的維度相等,所以第一個(gè)矩陣第 i 行元素與第二個(gè)矩陣第 j 列對(duì)應(yīng)元素的乘積和就等于新矩陣的第 i 行第 j 列的元素值。在下圖中,A 矩陣左乘 B 矩陣得到 C 矩陣。A 矩陣行向量與 B 矩陣列向量點(diǎn)積就等于 C 矩陣的元素,具體可以通過(guò)下圖 C 矩陣內(nèi)部元素的構(gòu)成來(lái)了解。
A 矩陣行向量 a1 與 B 矩陣列向量 b1 的點(diǎn)積,即下圖所示:
下面是另一個(gè)矩陣的乘積:
矩陣乘法是不可交換的(即AB ≠ BA)。因?yàn)椴豢赡茴A(yù)期在改變向量的部分后還能得到相同的結(jié)果,而且第一個(gè)矩陣的列數(shù)必須要和第二個(gè)矩陣的行數(shù)相同,也可以看出為什么矩陣相乘的順序會(huì)影響其結(jié)果。雖然矩陣乘法是人為的規(guī)則,但它確實(shí)大大簡(jiǎn)化了計(jì)算的表達(dá),可以將巨大的計(jì)算量很簡(jiǎn)潔地表達(dá)出來(lái),這一點(diǎn)對(duì)機(jī)器學(xué)習(xí)算法的開發(fā)和使用有重要的作用。
最后你可以用以下案例檢測(cè)一下是否你已經(jīng)掌握了矩陣運(yùn)算的基本原理:
下面矩陣乘法的階是多少?
下面矩陣乘法的階是多少?
下面矩陣的乘法是多少?
下面矩陣的乘法是多少?
下面矩陣的乘法是多少?
使用 Numpy 進(jìn)行矩陣乘法運(yùn)算
在 Numpy 中,np.dot(a,b) 函數(shù)可以進(jìn)行向量和矩陣點(diǎn)積。并且該函數(shù)還有許多有意思的特征,所以我建議你在使用該函數(shù)前先看看該函數(shù)的用法:https://docs.scipy.org/doc/numpy/reference/generated/numpy.dot.html
a = np.array([
b = np.array([
# Multiply
深度學(xué)習(xí)通常會(huì)有巨大的計(jì)算量。從最開始的特征輸入,我們會(huì)使用一個(gè)個(gè)高維向量將特征輸入到神經(jīng)網(wǎng)絡(luò)中,而每一層的權(quán)重作為列向量組成一個(gè)權(quán)重矩陣。每一層的正向傳播都需要使用矩陣乘法進(jìn)行計(jì)算,而反向傳播更需要理解矩陣運(yùn)算才能對(duì)其運(yùn)行原理有一個(gè)較為深入的理解。本文是矩陣運(yùn)算的基礎(chǔ)性文章,其不僅對(duì)概念的理解很是重要,同時(shí)在新手開始學(xué)著搭建機(jī)器學(xué)習(xí)系統(tǒng)時(shí)更為有用,因?yàn)榫仃囘\(yùn)算的代碼在實(shí)際操作中是我們看懂一段代碼或?qū)懗鲆欢未a的基礎(chǔ)。并且采用矩陣運(yùn)算代碼實(shí)現(xiàn)也遠(yuǎn)比采用循環(huán)語(yǔ)句或條件語(yǔ)句代碼實(shí)現(xiàn)的算法要簡(jiǎn)潔易讀得多。