| 聲明1)該文章整理自網(wǎng)上的大牛和機(jī)器學(xué)習(xí)專(zhuān)家無(wú)私奉獻(xiàn)的資料,具體引用的資料請(qǐng)看參考文獻(xiàn)。2)本文僅供學(xué)術(shù)交流,非商用。所以每一部分具體的參考資料并沒(méi)有詳細(xì)對(duì)應(yīng)。如果某部分不小心侵犯了大家的利益,還望海涵,并聯(lián)系博主刪除。
 3)博主才疏學(xué)淺,文中如有不當(dāng)之處,請(qǐng)各位指出,共同進(jìn)步,謝謝。
 4)此屬于第一版本,若有錯(cuò)誤,還需繼續(xù)修正與增刪。還望大家多多指點(diǎn)。大家都共享一點(diǎn)點(diǎn),一起為祖國(guó)科研的推進(jìn)添磚加瓦。
 深度學(xué)習(xí)入門(mén)筆記(五):神經(jīng)網(wǎng)絡(luò)的編程基礎(chǔ)1、Jupyter/iPython Notebooks快速入門(mén)學(xué)到現(xiàn)在,你需要知道常用的python的編譯器,推薦使用anaconda而不是官方的python,這樣的話更容易安裝各種第三方庫(kù),如何安裝可以看一下這個(gè)博客——Windows10 下 Anaconda和 PyCharm 的詳細(xì)的安裝教程(圖文并茂)。 至于IDE的話,pycharm 適合于大型項(xiàng)目的編寫(xiě)和調(diào)試,Jupyter Notebook 適合于學(xué)習(xí)和數(shù)據(jù)挖掘探索,這里我們就快速地學(xué)習(xí)一下 Jupyter Notebook 工具。
  這就是 Jupyter Notebook 的界面,讓我快速地講解下它的一些特性。
 
  這里有一些空白區(qū)域的代碼塊,可以編寫(xiě)代碼,而較長(zhǎng)的灰色區(qū)域就是代碼塊。
 
  比如,編寫(xiě)打印輸出著名的程序員入門(mén)語(yǔ)句——Hello World 的代碼,然后執(zhí)行這一代碼塊,最終,它就會(huì)輸出我們想要的 Hello World。
 
  在運(yùn)行一個(gè)單元格 cell 時(shí),你也可以選擇運(yùn)行其中的一塊代碼區(qū)域。通過(guò)點(diǎn)擊 Cell 菜單的 Run Cells 執(zhí)行這部分代碼。
 在你的計(jì)算機(jī)上,運(yùn)行 cell 的鍵盤(pán)快捷方式是 Ctrl + enter。但是也可以使用 shift + enter 來(lái)運(yùn)行 cell,不過(guò)這樣會(huì)默認(rèn)跳轉(zhuǎn)到下一個(gè)代碼區(qū)域。
  當(dāng)閱讀指南時(shí),如果不小心雙擊了它,點(diǎn)中的區(qū)域就會(huì)變成 markdown 語(yǔ)言形式。如果不小心使其變成了這樣的文本框,只要運(yùn)行下單元格 cell,就可以回到原來(lái)的形式。所以,點(diǎn)擊 cell 菜單的 Run Cells 或者使用 Ctrl + enter,就可以使得它變回原樣。markdown 格式可以用來(lái)寫(xiě)筆記,以免自己忘記了代碼中的知識(shí)。
 
  這里還有一些其他的小技巧。比如當(dāng)執(zhí)行上面所使用的代碼時(shí),它實(shí)際上會(huì)使用一個(gè)內(nèi)核在服務(wù)器上運(yùn)行這段代碼。如果你正在運(yùn)行超負(fù)荷的進(jìn)程,或者電腦運(yùn)行了很長(zhǎng)一段時(shí)間,或者在運(yùn)行中出了錯(cuò),又或者網(wǎng)絡(luò)連接失敗,這里依然有機(jī)會(huì)讓 Kernel 重新工作。你只要點(diǎn)擊 Kernel,選擇 Restart,它會(huì)重新運(yùn)行 Kernel 使程序繼續(xù)工作。
 所以,如果只是運(yùn)行相對(duì)較小的工作并且才剛剛啟動(dòng)你的臺(tái)式電腦或筆記本電腦,這種情況應(yīng)該是不會(huì)發(fā)生的。但是,如果你看見(jiàn)錯(cuò)誤信息,比如 Kernel 已經(jīng)中斷或者其他信息,你可以試著重啟 Kernel,這樣就簡(jiǎn)單地重啟程序了。
  當(dāng)使用 Notebook 時(shí)會(huì)有多個(gè)代碼區(qū)域塊。盡管并沒(méi)有在前面的代碼塊中添加自己的代碼,但還是要確保先執(zhí)行這塊代碼。因?yàn)樵谶@個(gè)例子,它導(dǎo)入了 numpy 包并另命名為 np 等,并聲明了一些可能需要的變量。為了能順利地執(zhí)行下面的代碼,就必須確保先執(zhí)行上面的代碼,即使不要求寫(xiě)其他的代碼,這樣其他程序就可以默認(rèn)是在這些庫(kù)的調(diào)用下運(yùn)行了。
 
  最后就是編譯環(huán)境的選擇,正常情況下 Notebook 的編譯環(huán)境是默認(rèn)的,但是你也可以自己新建一個(gè)環(huán)境,這個(gè)具體操作在這個(gè)博客中——Windows10 下 Anaconda和 PyCharm 的詳細(xì)的安裝教程(圖文并茂),比如我這里新建的環(huán)境 Pytorch for Deeplearning,就是專(zhuān)門(mén)為 pytorch 的學(xué)習(xí)而建立的,可以通過(guò) Kernel 下的 Change kernel,選擇 Pytorch for Deeplearning,就ok了。
 
  這個(gè)就是默認(rèn)的 kernel。
 
  這個(gè)是我自己建的 kernel。
 你會(huì)發(fā)現(xiàn)這種交互式的 shell 命令,在 Notebooks 是非常有用的,能使你快速地實(shí)現(xiàn)代碼并且查看輸出結(jié)果,便于學(xué)習(xí),同時(shí)還可以記錄在這個(gè)過(guò)程中的想法。好好學(xué)習(xí)它的使用,你會(huì)發(fā)現(xiàn)更多的驚喜。 2、Python 中的廣播 這是一個(gè)不同食物(每100g)中不同營(yíng)養(yǎng)成分的卡路里含量表格,表格為3行4列,列表示不同的食物種類(lèi),從左至右依次為蘋(píng)果(Apples),牛肉(Beef),雞蛋(Eggs),土豆(Potatoes)。行表示不同的營(yíng)養(yǎng)成分,從上到下依次為碳水化合物,蛋白質(zhì),脂肪。
 那么,現(xiàn)在假設(shè)我們想要計(jì)算不同食物中不同營(yíng)養(yǎng)成分中的卡路里百分比,應(yīng)該怎么做? 以計(jì)算蘋(píng)果中的碳水化合物卡路里百分比含量為例,首先計(jì)算蘋(píng)果(Apples)(100g)中三種營(yíng)養(yǎng)成分卡路里總和 56+1.2+1.8 = 59,然后用56 / 59 = 94.9%算出結(jié)果??梢悦黠@地看出蘋(píng)果(Apples)中的卡路里大部分來(lái)自于碳水化合物(Carb),而牛肉(Beef)則不同。對(duì)于其他食物,計(jì)算方法類(lèi)似。首先,按列求和,計(jì)算每種食物中(100g)三種營(yíng)養(yǎng)成分總和,然后分別用不用營(yíng)養(yǎng)成分的卡路里數(shù)量除以總和,計(jì)算百分比。 那么,能否在向量化(深度學(xué)習(xí)入門(mén)筆記(四):向量化)的基礎(chǔ)上用代碼完成這樣的一個(gè)計(jì)算過(guò)程呢? 當(dāng)然是可以的,假設(shè)上圖的表格是一個(gè)4行3列的矩陣 AA,記為 A3×4A3×4,接下來(lái)使用 Python 的 numpy 庫(kù)完成這樣的計(jì)算。使用兩行代碼就可以完成整個(gè)過(guò)程,第一行代碼對(duì)每一列進(jìn)行求和,第二行代碼分別計(jì)算每種食物每種營(yíng)養(yǎng)成分的百分比。 在 jupyter notebook 中輸入如下代碼,按 Ctrl + Enter 運(yùn)行,輸出如下:
  下面再計(jì)算每列的和,可以看到輸出是每種食物(100g)的卡路里總和。
 
  其中
 sum的參數(shù)axis=0表示求和運(yùn)算按列執(zhí)行,之后會(huì)詳細(xì)解釋。 接下來(lái)計(jì)算百分比,這條指令將 3×43×4 的矩陣 AA 除以一個(gè) 1×41×4 的矩陣,得到了一個(gè) 3×43×4 的結(jié)果矩陣,這個(gè)結(jié)果矩陣就是要求的百分比含量。
  到這里問(wèn)題就解決了,現(xiàn)在來(lái)解釋一下
 A.sum(axis = 0)中的參數(shù)axis。axis用來(lái)指明將要進(jìn)行的運(yùn)算是沿著哪個(gè)軸執(zhí)行,在numpy中,0軸是垂直的,也就是列,而1軸是水平的,也就是行。 而第二個(gè)A / cal.reshape(1, 4)指令則調(diào)用了 numpy 中的廣播機(jī)制。這里使用 3×43×4 的矩陣 AA 除以 1×41×4 的矩陣 calcal。技術(shù)上來(lái)講,其實(shí)并不需要再將矩陣 calcalreshape(重塑)成 1×41×4,因?yàn)榫仃?calcal 本身已經(jīng)是 1×41×4 了。但是當(dāng)我們寫(xiě)代碼的過(guò)程中出現(xiàn)不確定矩陣維度的時(shí)候,通常會(huì)對(duì)矩陣進(jìn)行重塑來(lái)確保得到想要的列向量或行向量。重塑操作reshape是一個(gè)常量時(shí)間的操作,時(shí)間復(fù)雜度是 O(1)O(1),它的調(diào)用代價(jià)極低,所以使用是沒(méi)問(wèn)題的,也推薦大家使用。 那么一個(gè) 3×43×4 的矩陣是怎么和 1×41×4 的矩陣做除法的呢?來(lái)看一些廣播的例子:
  在 numpy 中,當(dāng)一個(gè) 4×14×1 的列向量與一個(gè)常數(shù)做加法時(shí),實(shí)際上會(huì)將常數(shù)擴(kuò)展為一個(gè) 4×14×1 的列向量,然后兩者做逐元素加法。結(jié)果就是右邊的這個(gè)向量。這種廣播機(jī)制對(duì)于行向量和列向量均可以使用。
 再看下一個(gè)例子。
  用一個(gè) 2×32×3 的矩陣和一個(gè) 1×31×3 的矩陣相加,其泛化形式是 m×nm×n 的矩陣和 1×n1×n 的矩陣相加。在執(zhí)行加法操作時(shí),其實(shí)是將 1×n1×n 的矩陣復(fù)制成為 m×nm×n 的矩陣,然后兩者做逐元素加法得到結(jié)果。針對(duì)這個(gè)具體例子,相當(dāng)于在矩陣的第一列全部加100,第二列全部加200,第三列全部加300。這就是在前面例子中計(jì)算卡路里百分比的廣播機(jī)制,只不過(guò)那里是除法操作,這里是加法操作(廣播機(jī)制與執(zhí)行的運(yùn)算種類(lèi)無(wú)關(guān))。
 下面是最后一個(gè)例子。
  這里相當(dāng)于是一個(gè) m×nm×n 的矩陣加上一個(gè) m×1m×1 的矩陣。在進(jìn)行運(yùn)算時(shí),會(huì)先將 m×1m×1 矩陣水平復(fù)制 nn 次,變成一個(gè) m×nm×n 的矩陣,然后再執(zhí)行逐元素加法。
 廣播機(jī)制的一般原則如下: 這里的廣播和播音廣播是完全不同的,它的要求是什么呢?什么樣的條件下可以使用廣播? 要求:如果兩個(gè)數(shù)組的后緣維度的軸長(zhǎng)度相符或其中一方的軸長(zhǎng)度為1,則認(rèn)為它們是廣播兼容的。廣播會(huì)在缺失維度和軸長(zhǎng)度為1的維度上進(jìn)行。 如何計(jì)算后緣維度的軸長(zhǎng)度?可以使用代碼 A.shape[-1]即矩陣維度元組中的最后一個(gè)位置的值,就是矩陣維度的最后一個(gè)維度,比如卡路里計(jì)算的例子中,矩陣 A3,4A3,4 后緣維度的軸長(zhǎng)度是4,而矩陣 cal1,4cal1,4 的后緣維度也是4,故滿足了后緣維度軸長(zhǎng)度相符的條件,可以進(jìn)行廣播。廣播會(huì)在軸長(zhǎng)度為1的維度上進(jìn)行,軸長(zhǎng)度為1的維度對(duì)應(yīng)axis=0,即垂直方向,矩陣 cal1,4cal1,4 沿axis=0(垂直方向)復(fù)制成為 calTemp3,4calTemp3,4 ,之后兩者進(jìn)行逐元素除法運(yùn)算。 簡(jiǎn)單概括總結(jié)就是,先變成一樣大,再逐元素除法。 然后解釋圖中的例子
 
 矩陣 Am,nAm,n 和矩陣 B1,nB1,n 進(jìn)行四則運(yùn)算,后緣維度軸長(zhǎng)度相符,符合條件,可以廣播,廣播沿著軸長(zhǎng)度為1的軸進(jìn)行,即 B1,nB1,n 廣播成為 Bm,n′Bm,n′ ,之后做逐元素四則運(yùn)算。 矩陣 Am,nAm,n 和矩陣 Bm,1Bm,1 進(jìn)行四則運(yùn)算,后緣維度軸長(zhǎng)度不相符,但其中一方軸長(zhǎng)度為1,符合條件,可以廣播,廣播沿著軸長(zhǎng)度為1的軸進(jìn)行,即 Bm,1Bm,1 廣播成為 Bm,n′Bm,n′ ,之后做逐元素四則運(yùn)算。 矩陣 Am,1Am,1 和常數(shù) RR 進(jìn)行四則運(yùn)算,后緣維度軸長(zhǎng)度不相符,但其中一方軸長(zhǎng)度為1,符合條件,可以廣播,廣播沿著缺失維度的軸進(jìn)行,缺失維度就是 axis=0,軸長(zhǎng)度為1的軸是axis=1,即 RR 廣播成為 Bm,1′Bm,1′ ,之后做逐元素四則運(yùn)算。 最后總結(jié)一下 broadcasting,可以看看下面的圖:
  3、關(guān)于numpy向量的說(shuō)明Python 的特性允許你使用 廣播(broadcasting) 功能,這是 Python 的 numpy 程序語(yǔ)言庫(kù)中最靈活的地方,但這是程序語(yǔ)言的優(yōu)點(diǎn),也是缺點(diǎn)。 優(yōu)點(diǎn)的原因,在于它們創(chuàng)造出語(yǔ)言的表達(dá)性,Python 語(yǔ)言巨大的靈活性使得你僅僅通過(guò)一行代碼就能做很多事情。缺點(diǎn)的原因,由于廣播巨大的靈活性,有時(shí)候?qū)τ趶V播的特點(diǎn)以及廣播的工作原理這些細(xì)節(jié)不熟悉的話,可能會(huì)產(chǎn)生很細(xì)微或者看起來(lái)很奇怪的 bug。
 為了演示 Python-numpy 的一個(gè)容易被忽略的效果,特別是怎樣在 Python-numpy 中構(gòu)造向量,來(lái)做一個(gè)快速示范。 首先設(shè)置 a=np.random.randn(5)a=np.random.randn(5),這樣會(huì)生成存儲(chǔ)在數(shù)組 aa 中的5個(gè)高斯隨機(jī)數(shù)變量;然后輸出 aa,從屏幕上可以得知,此時(shí) aa 的 shape(形狀) 是一個(gè) (5,)(5,) 的結(jié)構(gòu)同樣地,a.Ta.T 的 shape 也是這樣的。這在 Python 中被稱(chēng)作 一個(gè)一維數(shù)組。它既不是一個(gè)行向量也不是一個(gè)列向量,這也導(dǎo)致它有一些不是很直觀的效果。 比如 aa 和 aa 的轉(zhuǎn)置陣最終結(jié)果看起來(lái)一樣,shape 也是一樣的。但是輸出 aa 和 aa 的轉(zhuǎn)置陣的內(nèi)積,你可能會(huì)想,aa 乘以 aa 的轉(zhuǎn)置,返回的可能會(huì)是一個(gè)矩陣。但如果這樣做,你只會(huì)得到一個(gè)數(shù)。
  所以在編寫(xiě)神經(jīng)網(wǎng)絡(luò)時(shí),不要使用 shape 為
 (5,)、(n,)或者其他一維數(shù)組的數(shù)據(jù)結(jié)構(gòu)。相反,設(shè)置 aa 為 (5,1)(5,1),這樣就是一個(gè)5行1列的向量。在先前的操作里 aa 和 aa 的轉(zhuǎn)置看起來(lái)一樣,而現(xiàn)在這樣的 aa 變成一個(gè)新的 aa 的轉(zhuǎn)置,并且它是一個(gè)行向量。當(dāng)輸出 aa 的轉(zhuǎn)置時(shí)有兩對(duì)方括號(hào),而之前只有一對(duì)方括號(hào),所以這就是 1行5列的矩陣和一維數(shù)組的差別。
  如果這次再輸出 aa 和 aa 的轉(zhuǎn)置的乘積,會(huì)返回一個(gè)向量的外積,也就是一個(gè)矩陣。這就符合我們的預(yù)期了,也就是在可控范圍內(nèi)了,因?yàn)槟阒雷约旱拇a輸出是什么了。
 除了,輸入確定維度的矩陣或向量之外,還有一件事,就是如果你不能完全確定一個(gè)向量的維度,建議你扔一個(gè) 斷言語(yǔ)句(assertion statement) 進(jìn)去。這樣,就可以確保在這種情況下是否是一個(gè) (5,1)(5,1) 向量了,或者說(shuō)是一個(gè)列向量。
  如果不對(duì)的話,就會(huì)報(bào)一個(gè)叫做 AssertionError 的錯(cuò)誤!??!
 4、編程框架的選擇問(wèn)題這個(gè)我在 大話卷積神經(jīng)網(wǎng)絡(luò)CNN(干貨滿滿) 中講過(guò),目前主流的是 Google的TensorFlow、Facebook的pytorch 還有 百度的paddlepaddle,如果是研究的話,我建議使用TensorFlow,因?yàn)樗美斫庖幌禄A(chǔ)原理,而不是單純的調(diào)包俠。大話卷積神經(jīng)網(wǎng)絡(luò)CNN(干貨滿滿) 博客中也寫(xiě)了相關(guān)的資源推薦,這里就不詳細(xì)說(shuō)了。 |