如何快速使用Python神經網絡識別手寫字符?(文末福利)
在本文中,我們將進一步探討一些使用Python神經網絡識別手寫字符非常有趣的想法。如果只是想了解神經網絡的基本知識,那不必閱讀本文,可以先閱讀《Python神經網絡編程》前面2章節的內容。
這是一個有趣的額外部分,所以節奏會稍微加快一些,但是我們仍然嘗試使用簡單的語言來解釋這些想法。
1.1 自己的手寫數字
在本文中,我們一直使用來自MNIST數據集的數字圖片。為什麽不使用自己的筆跡呢?
在這個實驗中,我們將使用自己的筆跡創建測試數據集。我們也將嘗試使用不同的書寫風格,使用嘈雜或抖動的圖片,來觀察神經網絡的應對能力如何。
你可以使用任何喜歡的圖像編輯或繪畫軟件來創建圖片。不必使用昂貴的Photoshop,GIMP是免費開源的替代軟件,適用於Windows、Mac和Linux等系統。甚至可以用一支筆將數字寫在紙上,並用智能手機、相機或任何合適的掃描儀,將手寫數字變成圖片格式。唯一的要求是圖片為正方形(寬度等於長度),並且將其保存為PNG格式。在喜歡的圖像編輯器中,保存格式選項的菜單通常為“File→Save As”或“File→Export”。
下面是我制作的一些圖片。
數字5就是我的筆跡。數字4是用粉筆而不是馬克筆寫的。數字3是我的筆跡並有意切成一段一段的。數字2是傳統的報紙或書籍字體,但是進行了模糊處理。數字6有意做成抖動的樣子,好像是在水中的倒影。最後一張圖片與前面的數字相同,但是添加了噪聲,來看看我們是否可以增加神經網絡的工作難度。
雖然這很有趣,但是這裏蘊含了很嚴肅的一點。人類大腦在遭受損害後,其能力依然能夠得到良好發揮,科學家對此深感震驚。這暗示著,神經網絡將它們所學到的知識分布在幾條鏈接權重上,也就是說,如果若幹鏈接權重遭受了一定損害,神經網絡也可以表現得相當好。這同時意味著,如果輸入圖像被損壞或不完整,神經網絡也可以表現得相當好。這是一種很強大的功能,這就是我們希望用上圖中斷斷續續的3進行測試的能力。
我們需要創建較小的PNG圖片,將它們調整到28個像素乘以28個像素,這樣就可以匹配曾經用過的來自MNIST數據集的圖片。你可以使用圖像編輯器做到這一點。
Python庫再次幫助了我們,它從常見的圖像文件格式中(包括PNG格式)讀取和解碼數據。看看下面這段簡單的代碼:
scipy.misc.imread()函數幫助我們從圖像文件,如PNG或JPG文件中,讀取數據。必須導入scipy.misc庫來使用這個函數。參數“flatten=True”將圖像變成簡單的浮點數數組,如果圖像是彩色的,那麽顏色值將被轉換為所需要的灰度。
下一行代碼重塑數組,將其從28×28的方塊數組變成很長的一串數值,這是我們需要饋送給神經網絡的數據。此前,我們已經多次進行這樣的操作了。但是,這裏比較新鮮的一點是將數組的值減去了255.0。這樣做的原因是,常規而言,0指的是黑色,255指的是白色,但是,MNIST數據集使用相反的方式表示,因此不得不將值逆轉過來以匹配MNIST數據。
最後一行代碼是我們很熟悉的,它將數據值進行縮放,使得它們的範圍變成0.01到1.0。演示讀取PNG文件的示例代碼可以在GitHub上找到:
· https://github.com/makeyourownneuralnetwork/makeyourownneuralnetwork/ blob/master/part3_load_own_images.ipynb
我們需要創建基本的神經網絡,這個神經網絡使用MNIST訓練數據集進行訓練,然後,不使用MNIST測試集對網絡進行測試,而是使用自己創建的圖像數據對網絡進行測試。
在GitHub上,可通過如下鏈接獲得新程序:
·https://github.com/makeyourownneuralnetwork/makeyourownneuralnetwork/ blob/master/part3_neural_network_mnist_and_own_data.ipynb
這樣做成功了嗎?當然成功了。下圖總結使用我們自己制作的圖像查詢的結果。
可以看到,神經網絡能夠識別我們創建的所有圖像,包括有意損壞的數字“3”。只有在識別添加了噪聲的數字“6”時失敗了。
使用你自己的圖像,尤其是手寫的圖像試試看,證明你的神經網絡確實能夠工作。
並且,仔細觀察,要將圖像損壞或變形到什麽程度,神經網絡才會失敗。神經網絡的彈性將會給你留下深刻的印象。
1.2 神經網絡大腦內部
在求解各種各樣我們不知道如何使用簡約明快的規則解決的問題時,神經網絡發揮了重要作用。想象一下,寫下一組規則,將這些規則應用於手寫數字圖像,來確定數字是什麽,這件事並不是那麽容易,並且我們的嘗試也可能不會那麽成功。
1.2.1 神秘的黑盒子
一旦神經網絡得到了訓練,並且在測試數據上表現良好,那麽基本上你就擁有了一個神秘的黑盒子。你不知道這個黑盒子如何計算出答案,但是它確實成功地計算出了答案。
如果你只對答案感興趣,而不真正關心它們如何得出這個答案的,那麽對你來說,這就不是一個問題了。但是,我要指出這是這些機器學習方法類型的缺點,即雖然黑盒子(神經網絡)已經學會如何求解問題,但是其所學習到的知識常常不能轉化為對問題的理解和智慧。
讓我們來看看是否可以到神經網絡內部一探究竟,是否能夠理解神經網絡所學習到的知識,將神經網絡通過訓練搜集到的知識可視化。
我們可以觀察權重,這畢竟是神經網絡學習的內容。但是,權重不太可能告訴我們太多信息。特別是,神經網絡的工作方式是將學習分布到不同的鏈接權重中。這種方式使得神經網絡對損壞具有了彈性,這就像是生物大腦的運行方式。刪除一個節點甚至相當多的節點,都不太可能徹底破壞神經網絡良好的工作能力。
這裏有一個瘋狂的想法。
1.2.2 向後查詢
在通常情況下,我們饋送給已受訓練的神經網絡一個問題,神經網絡彈出一個答案。在我們的例子中,這個問題是人類的手寫數字圖像。答案是表示數字0到9中的某個標簽。
如果將這種方式反轉,向後操作,會發生什麽呢?如果饋送一個標簽到輸出節點,通過已受訓練的網絡反向輸入信號,直到輸入節點彈出一個圖像,那會怎麽樣?下圖顯示了正常的正向查詢和瘋狂的反向向後查詢的想法。
我們已經知道如何通過網絡傳播信號,使用鏈接權重調節信號,在應用激活函數之前在節點處重新組合信號。除了使用的是逆激活函數以外,所有這一切操作也都適用於反向傳播信號。如果y = f(x) 是正向激活函數,那麽這個函數的逆就是x = g(y)。使用簡單的代數,求出邏輯函數的逆,也並非難事:
y = 1 / (1 + e-x)
1 + e-x = 1/y
e-x = (1/y) -1 = (1 - y) / y
-x = ln [ (1-y) / y ]
x = ln [ y / (1-y) ]
這就是所謂的對數函數,就像Python為邏輯S函數提供scipy.special.expit()一樣,Python中的scipy.special庫也提供了這個函數,即scipy.special.logit()。
在應用逆激活函數logit()之前,我們需要確保信號是有效的。這是什麽意思呢?還記得吧,邏輯S函數接受了任何數值,輸出0和1之間的某個值,但是不包括0和1本身。逆函數必須接受相同的範圍0和1之間的某個值,不包括0和1,彈出任何正值或負值。為了實現這一目標,我們簡單地接受輸出層中的所有值,應用logit(),並將它們重新調整到有效範圍。我選擇的範圍為0.01至0.99。
這段代碼在網上始終可用,請訪問GitHub以獲取:
·https://github.com/makeyourownneuralnetwork/makeyourownneuralnetwork/ blob/master/part3_neural_network_mnist_backquery.ipynb
1.2.3 標簽“0”
來看看如果我們使用標簽“0”進行反向查詢,會發生什麽情況。也就是說,我們向輸出節點展示了一些值,除了使用值0.99展示給第一個節點表示標簽“0”,其余節點都展示了0.01。換句話說,也就是數組[0.99, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01,0.01]。
下圖顯示了輸入節點彈出的圖像。
這真是太有趣了!
這個圖像讓我們對神經網絡的“大腦”有了一種深刻的見解。這個圖像是什麽意思?該如何解釋這個圖像呢?
我們註意到最主要的特征是,圖像中的圓形。我們是在詢問神經網絡——對於答案“0”,最理想的問題是什麽,因此,這個圖像是有道理的。
我們也註意到深色、淺色和一些介中的灰色區域。
·深色區域是問題圖像中應該使用筆來標記的部分,這部分圖像組成了支持證據,證明答案為“0”,可以這樣理解,這些部分看起來組成了0的形狀輪廓。
· 淺色區域是問題圖像中應該沒有任何筆痕的部分,這支持了答案為“0”。同樣,可以這樣理解,這些部分形成了0形狀的中間部分。
· 大體上,神經網絡對灰色區域不是很敏感。
因此,粗略來講,我們實際上已經理解了,針對如何將圖像歸類為標簽“0”,神經網絡已經學習到的知識。
這是一種難得的見解,對於較多層、較復雜的神經網絡或較復雜的問題而言,可能沒有如此容易解釋的結果。我們鼓勵你進行實驗,親自動手試一試。
1.2.4 更多的大腦掃描
下圖顯示了其他數字向後查詢的結果。
哇!同樣是一些非常有趣的圖像,就像使用超聲波掃描神經網絡的大腦一樣。
關於這些圖像,我們做了一些註解:
·“7”真的很清楚。可以看到在查詢圖像中標記的深色位置,強烈暗示了這是標簽“7”。也可以看到額外的“白色”區域,這些區域沒有任何標記。這兩個特點結合起來,指示出了這是“7”。
· 這同樣適用於數字“3”,有標記的深色區域指示出了“3”,白色的區域也非常清晰。
·“2”和“5”具有類似的清晰度。
· 數字“4”有點有趣,這個形狀出現在4個象限中,是4個互相分隔的區域。
· “8”主要是由“雪人”構成的,這個“雪人”由白色區域形成,表明8的特征在於保持了“頭部和身體”區的標記。
· 數字“1”令人相當費解。這看起來好像神經網絡較多關註無需標記的區域,而較少關註需要標記的區域。沒關系,這就是網絡從樣本中學到的知識。
· 數字“9”一點都不清楚。它有一個明確的深色區域,還有一些形狀相對精細的白色區域。這就是神經網絡所學習到的知識,總體來說,當與網絡學會的其他數字結合時,這允許神經網絡的表現達到了97.5%的準確度。我們觀察一下這個圖片,並得出結論,有更多的培訓樣本將有助於神經網絡學到更清晰的“9”的模板。
現在,你對神經網絡大腦的工作方式應該有了一個深刻的了解了吧。
1.3 創建新的訓練數據:旋轉圖像
如果思考一下MNIST訓練數據,你就會意識到,這是關於人們所書寫數字的一個相當豐富的樣本集。這裏有各種各樣、各種風格的書寫,有的寫得很好,有的寫得很糟。
神經網絡必須盡可能多地學習這些變化類型。在這裏,有多種形式的數字“4”,有些被壓扁了,有些比較寬,有些進行了旋轉,有些頂部是開放的,有些頂部是閉合的,這對神經網絡的學習都是有幫助的。
如果我們能夠創造更多的變化類型作為樣本,會不會有用處呢?如何做到這一點呢?再多收集幾千個人類手寫數字樣本,對我們來說有點不太容易。我們可以這樣做,但是工作量有點大。
一個很酷的想法就是利用已有的樣本,通過順時針或逆時針旋轉它們,比如說旋轉10度,創建新的樣本。對於每一個訓練樣本而言,我們能夠生成兩個額外的樣本。我們可以使用不同的旋轉角度創建更多的樣本,但是,目前,讓我們只嘗試+10和-10兩個角度,看看這種想法能不能成功。
同樣,Python的許多擴展包和程序庫都很有用。ndimage.interpolation.rotate()可以將數組轉過一個給定的角度,這正是我們所需要的。請記住,由於我們將神經網絡設計成為接收一長串輸入信號,因此輸入的是784長的一串一維數字。我們需要將這一長串數字重新變成28×28的數組,這樣就可以旋轉這個數組,然後在將這個數組饋送到神經網絡之前,將數組解開,重新變成一長串的784個信號。
假設得到了先前的scaled_input數組,下列代碼演示了如何使用ndimage.interpolation.rotate()函數:
可以看到,原先的scaled_input數組被重新轉變為28乘以 28的數組,然後進行了調整。reshape=False,這個參數防止程序庫過分“熱心”,將圖像壓扁,使得數組旋轉後可以完全適合,而沒有剪掉任何部分。在原始圖像中,一些數組元素不存在,但是現在這些數組元素進入了視野,cval就是用來填充數組元素的值。由於我們要移動輸入值範圍,避免0作為神經網絡的輸入值,因此不使用0.0作為默認值,而是使用0.01作為默認值。
小型MNIST訓練集的記錄6(第7條記錄)是一個手寫數字“1”。在下圖中可以看到,原先的數字圖片和使用代碼生成的兩個額外的變化形式。
可以清楚地看到這種方式的好處。原始圖像的版本旋轉+10度,提供了一個樣本,就像某些人的書寫風格是將1向後傾斜。將原來圖片的版本順時針旋轉10度更有趣。和原始的版本相比,這個版本在某種意義上是更具代表性的學習圖片。
讓我們創建新的Python Notebook,使用原來的神經網絡代碼,不過,現在我們將原始圖片朝順時針和逆時針兩個方向旋轉10度,作為額外的訓練樣本,來訓練神經網絡。這段代碼在GitHub上可以得到,請訪問以下鏈接:
·https://github.com/makeyourownneuralnetwork/makeyourownneuralnetwork/ blob/master/part2_neural_network_mnist_data_with_rotations.ipynb
設定學習率為0.1,並且只使用一個訓練世代,初始運行神經網絡,所得的性能是0.9669。這對於沒有使用額外旋轉圖像進行訓練的神經網絡的性能0.954而言,是一個長足的進步。這樣的表現,和列在Yann LeCeun網站中的記錄相比也已經是名列前茅了。
讓我們進行一系列的實驗,改變世代的數目看看是否能夠讓已經不錯的表現更上一層樓。現在,我們創建了更多的訓練數據,可以采用更小、更謹慎的學習步長,因此將學習率減少到0.01,這樣就總體上延長了學習時間。
請記住,由於特定的神經網絡架構或訓練數據的完整性,事情很可能存在內在的限制,因此我們不會期待得到98%或以上的準確度,或者甚至是100%的準確度。我們說“特定的神經網絡架構”,意思是在每一層節點數目的選擇、隱藏層的選擇和激活函數的選擇等。
我們旋轉訓練圖像的角度,將其作為額外的訓練樣本,下圖顯示了在這種情況下的神經網絡的性能。同時,下圖也顯示了沒有使用額外旋轉的訓練樣本時神經網絡的性能,以便進行簡單的比較。
可以看到,在5個世代的情況下,最好的結果是0.9745或97.5%的準確度。這再一次打破了我們先前的紀錄。
值得註意的是,如果旋轉的角度過大,神經網絡的性能會出現下降。由於旋轉較大的角度意味著創建了實際上不能代表數字的圖像,因此神經網絡的性能出現了下降,這是可以理解的。想象一下,將“3”向一個方向旋轉90度,這就不再是3了。因此,將過度旋轉的圖像添加到訓練樣本中,增加了錯誤樣本,降低了訓練的質量。對於最大化附加數據的價值,10度看起來是最佳角度。
在10個世代的情況下,神經網絡的性能出現了峰值,打破了記錄,達到了0.9787,幾乎到達98%!對於這種簡單的神經網絡而言,這是一個驚人的結果,也是最佳的一種狀態。請記住,有些人會對神經網絡或數據進行一些巧妙的處理,我們還未這樣做,我們只是保持簡單的神經網絡,但是卻依然取得了令人驕傲的結果。
做得好!
1.4 結語
在本文中,我希望你已經明白,人類能夠輕而易舉解決的一些問題,對傳統計算機而言卻難以解決。圖像識別就是這些所謂的“人工智能”的挑戰之一。
神經網絡使圖像識別以及廣泛的其他各類難題,都獲得了空前的進步。求解這類難題的早期動力的一個關鍵性部分是生物大腦,如鴿子或昆蟲的大腦,雖然這些生物大腦比起今天的超級計算機似乎簡單一些,反應也較慢,但是它們依然能夠執行復雜的任務,如飛行、餵食、建設家園。這些生物大腦對損害或對不完美的信號,也非常有彈性。數字計算機和傳統計算卻不能擁有這種能力。
今天,在人工智能中,神經網絡是一些神奇的應用程序成功的關鍵部分。人們對神經網絡和機器學習,特別是深度學習——也就是使用了有層次結構的機器學習方法,依然充滿了巨大興趣。在2016年年初,在古老的圍棋對弈領域,谷歌的DeepMind擊敗了世界級大師。和國際象棋相比,圍棋需要更深入的戰略,更加微妙,研究人員原本以為計算機需要好幾年的時間才能下得好圍棋。因此,此次事件成為了人工智能史上一個巨大的裏程碑。神經網絡在計算機的成功中發揮了關鍵作用。
我希望你已經明白了,神經網絡背後的核心思想其實是非常簡單的。我希望你也可以從神經網絡的實驗中找到樂趣。也許,你已經有了探索其他類型的機器學習和人工智能的興趣。
如果你做到了這些事情,那麽我就算大功告成了。
本文摘自《Python神經網絡編程》
《Python神經網絡編程》
[英]塔裏克·拉希德(Tariq Rashid) 著
點擊封面購買紙書
當前,深度學習和人工智能的發展和應用給人們留下了深刻的印象。神經網絡是深度學習和人工智能的關鍵元素,然而,真正了解神經網絡工作機制的人少之又少。本書用輕松的筆觸,一步一步揭示了神經網絡的數學思想,並介紹如何使用Python 3.5編程語言開發神經網絡。
本書將帶領您進行一場妙趣橫生卻又有條不紊的旅行——從一個非常簡單的想法開始,逐步理解神經網絡的工作機制。您無需任何超出中學範圍的數學知識,並且本書還給出易於理解的微積分簡介。本書的目標是讓盡可能多的普通讀者理解神經網絡。讀者將學習使用Python開發自己的神經網絡,訓練它識別手寫數字,甚至可以與專業的神經網絡相媲美。
本書適合想要了解深度學習、人工智能和神經網絡的讀者閱讀,尤其適合想要通過Python編程進行神經網絡開發的讀者參考。
小福利
關註【異步社區】服務號,轉發本文至朋友圈或 50 人以上微信群,截圖發送至異步社區服務號後臺,並在文章底下留言你的開發經驗,或者試讀本書感受,我們將選出3名讀者贈送《Python神經網絡編程》1本,趕快積極參與吧!
活動截止時間:2018年4月25日
在“異步社區”後臺回復“關註”,即可免費獲得2000門在線視頻課程;推薦朋友關註根據提示獲取贈書鏈接,免費得異步圖書一本。趕緊來參加哦!
掃一掃上方二維碼,回復“關註”參與活動!
閱讀原文,購買《Python神經網絡編程》
閱讀原文
如何快速使用Python神經網絡識別手寫字符?(文末福利)