如何用Python和深度神經網絡識別圖像?

只須要10幾行Python代碼,你就能構建本身的機器視覺模型,快速準確識別海量圖片。快來試試吧!html

視覺

進化的做用,讓人類對圖像的處理很是高效。python

這裏,我給你展現一張照片。git

若是我這樣問你:github

你可否分辨出圖片中哪一個是貓,哪一個是狗?bash

你可能當即會以爲本身遭受到了莫大的侮辱。而且大聲質問我:你以爲我智商有問題嗎?!服務器

息怒。微信

換一個問法:網絡

你可否把本身分辨貓狗圖片的方法,描述成嚴格的規則,教給計算機,以便讓它替咱們人類分辨成千上萬張圖片呢?app

對大多數人來講,此時感覺到的,就不是羞辱,而是壓力了。框架

若是你是個有毅力的人,可能會嘗試各類判別標準:圖片某個位置的像素顏色、某個局部的邊緣形狀、某個水平位置的連續顏色長度……

你把這些描述告訴計算機,它果真就能夠判斷出左邊的貓和右邊的狗了。

問題是,計算機真的會分辨貓狗圖片了嗎?

我又拿出一張照片給你。

你會發現,幾乎全部的規則定義,都須要改寫。

當機器好不容易能夠用近似投機取巧的方法正確分辨了這兩張圖片裏面的動物時,我又拿出來一張新圖片……

幾個小時之後,你決定放棄

彆氣餒。

你遭遇到的,並非新問題。就連大法官,也有過一樣的煩惱。

1964年,美國最高法院的大法官Potter Stewart在「Jacobellis v. Ohio」一案中,曾經就某部電影中出現的某種具體圖像分類問題,說過一句名言「我不許備就其概念給出簡短而明確的定義……可是,我看見的時候天然會知道」(I know it when I see it)。

原文以下:

I shall not today attempt further to define the kinds of material I understand to be embraced within that shorthand description (「hard-core pornography」), and perhaps I could never succeed in intelligibly doing so. But I know it when I see it, and the motion picture involved in this case is not that.

考慮到精神文明建設的須要,這一段就不翻譯了。

人類無法把圖片分辨的規則詳細、具體而準確地描述給計算機,是否是意味着計算機不能辨識圖片呢?

固然不是。

2017年12月份的《科學美國人》雜誌,就把「視覺人工智能」(AI that sees like humans)定義爲2017年新興技術之一。

你早已據說過自動駕駛汽車的神奇吧?沒有機器對圖像的辨識,能作到嗎?

你的好友可能(不止一次)給你演示如何用新買的iPhone X作面部識別解鎖了吧?沒有機器對圖像的辨識,能作到嗎?

醫學領域裏,計算機對於科學影像(如X光片)的分析能力,已經超過有多年從業經驗的醫生了。沒有機器對圖像的辨識,能作到嗎?

你可能一會兒以爲有些迷茫了——這難道是奇蹟?

不是。

計算機所作的,是學習

經過學習足夠數量的樣本,機器能夠從數據中本身構建模型。其中,可能涉及大量的判斷準則。可是,人類不須要告訴機器任何一條。它是徹底本身領悟和掌握的。

你可能會以爲很興奮。

那麼,下面我來告訴你一個更令你興奮的消息——你本身也能很輕易地構建圖片分類系統!

不信?請跟着我下面的介紹,來試試看。

數據

我們就不辨識貓和狗了,這個問題有點不夠新鮮。

我們來分辨機器貓,好很差?

對,我說的就是哆啦a夢。

把它和誰進行區分呢?

一開始我想找霸王龍,後來以爲這樣簡直是做弊,由於他倆長得實在差異太大

既然哆啦a夢是機器人,我們就另外找個機器人來區分吧。

一提到機器人,我馬上就想起來了它。

對,機器人瓦力(WALLE)。

我給你準備好了119張哆啦a夢的照片,和80張瓦力的照片。圖片已經上傳到了這個Github項目

請點擊這個連接,下載壓縮包。而後在本地解壓。做爲我們的演示目錄

解壓後,你會看到目錄下有個image文件夾,其中包含兩個子目錄,分別是doraemon和walle。

打開其中doraemon的目錄,咱們看看都有哪些圖片。

能夠看到,哆啦a夢的圖片真是五花八門。各類場景、背景顏色、表情、動做、角度……不一而足。

這些圖片,大小不一,長寬比例也各不相同。

咱們再來看看瓦力,也是相似的情況。

數據已經有了,下面咱們來準備一下環境配置。

環境

咱們使用Python集成運行環境Anaconda。

請到這個網址 下載最新版的Anaconda。下拉頁面,找到下載位置。根據你目前使用的系統,網站會自動推薦給你適合的版本下載。我使用的是macOS,下載文件格式爲pkg。

下載頁面區左側是Python 3.6版,右側是2.7版。請選擇2.7版本。

雙擊下載後的pkg文件,根據中文提示一步步安裝便可。

安裝好Anaconda後,咱們須要安裝TuriCreate。

請到你的「終端」(Linux, macOS)或者「命令提示符」(Windows)下面,進入我們剛剛下載解壓後的樣例目錄。

執行如下命令,咱們來建立一個Anaconda虛擬環境,名字叫作turi。

conda create -n turi python=2.7 anaconda
複製代碼

而後,咱們激活turi虛擬環境。

source activate turi
複製代碼

在這個環境中,咱們安裝最新版的TuriCreate。

pip install -U turicreate
複製代碼

安裝完畢後,執行:

jupyter notebook
複製代碼

這樣就進入到了Jupyter筆記本環境。咱們新建一個Python 2筆記本。

這樣就出現了一個空白筆記本。

點擊左上角筆記本名稱,修改成有意義的筆記本名「demo-python-image-classification」。

準備工做完畢,下面咱們就能夠開始編寫程序了。

代碼

首先,咱們讀入TuriCreate軟件包。它是蘋果併購來的機器學習框架,爲開發者提供很是簡便的數據分析與人工智能接口。

import turicreate as tc
複製代碼

咱們指定圖像所在的文件夾image。

img_folder = 'image'
複製代碼

前面介紹了,image下,有哆啦a夢和瓦力這兩個文件夾。注意若是未來你須要辨別其餘的圖片(例如貓和狗),請把不一樣類別的圖片也在image中分別存入不一樣的文件夾,這些文件夾的名稱就是圖片的類別名(cat和dog)。

而後,咱們讓TuriCreate讀取全部的圖像文件,而且存儲到data數據框。

data = tc.image_analysis.load_images(img_folder, with_path=True)
複製代碼

這裏可能會有錯誤信息。

Unsupported image format. Supported formats are JPEG and PNG    file: /Users/wsy/Dropbox/var/wsywork/learn/demo-workshops/demo-python-image-classification/image/walle/.DS_Store
複製代碼

本例中提示,有幾個.DS_Store文件,TuriCreate不認識,沒法看成圖片來讀取。

這些.DS_Store文件,是蘋果macOS系統建立的隱藏文件,用來保存目錄的自定義屬性,例如圖標位置或背景顏色。

咱們忽略這些信息便可。

下面,咱們來看看,data數據框裏面都有什麼。

data
複製代碼

能夠看到,data包含兩列信息,第一列是圖片的地址,第二列是圖片的長寬描述。

由於咱們使用了119張哆啦a夢圖片,80張瓦力圖片,因此總共的數據量是199條。數據讀取完整性驗證經過。

下面,咱們須要讓TuriCreate瞭解不一樣圖片的標記(label)信息。也就是,一張圖片究竟是哆啦a夢,仍是瓦力呢?

這就是爲何一開始,你就得把不一樣的圖片分類保存到不一樣的文件夾下面。

此時,咱們利用文件夾名稱,來給圖片打標記。

data['label'] = data['path'].apply(lambda path: 'doraemon' if 'doraemon' in path else 'walle')
複製代碼

這條語句,把doraemon目錄下的圖片,在data數據框裏打標記爲doraemon。反之就都視爲瓦力(walle)。

咱們來看看標記以後的data數據框。

data
複製代碼

能夠看到,數據的條目數量(行數)是一致的,只是多出來了一個標記列(label),說明圖片的類別。

咱們把數據存儲一下。

data.save('doraemon-walle.sframe')
複製代碼

這個存儲動做,讓咱們保存到目前的數據處理結果。以後的分析,只須要讀入這個sframe文件就能夠了,不須要從頭去跟文件夾打交道了。

從這個例子裏,你可能看不出什麼優點。可是想象一下,若是你的圖片有好幾個G,甚至幾個T,每次作分析處理,都從頭讀取文件和打標記,就會很是耗時。

咱們深刻探索一下數據框。

TuriCreate提供了很是方便的explore()函數,幫助咱們直觀探索數據框信息。

data.explore()
複製代碼

這時候,TuriCreate會彈出一個頁面,給咱們展現數據框裏面的內容。

原先打印data數據框,咱們只能看到圖片的尺寸,此時卻能夠瀏覽圖片的內容。

若是你以爲圖片過小,不要緊。把鼠標懸停在某張縮略圖上面,就能夠看到大圖。

數據框探索完畢。咱們回到notebook下面,繼續寫代碼。

這裏咱們讓TuriCreate把data數據框分爲訓練集合和測試集合。

train_data, test_data = data.random_split(0.8, seed=2)
複製代碼

訓練集合是用來讓機器進行觀察學習的。電腦會利用訓練集合的數據本身創建模型。可是模型的效果(例如分類的準確程度)如何?咱們須要用測試集來進行驗證測試。

這就如同老師不該該把考試題目都拿來給學生作做業和練習同樣。只有考學生沒見過的題,才能區分學生是掌握了正確的解題方法,仍是死記硬背了做業答案。

咱們讓TuriCreate把80%的數據分給了訓練集,把剩餘20%的數據拿到一邊,等待測試。這裏我設定了隨機種子取值爲2,這是爲了保證數據拆分的一致性。以便重複驗證咱們的結果。

好了,下面咱們讓機器開始觀察學習訓練集中的每個數據,而且嘗試本身創建模型。

下面代碼第一次執行的時候,須要等候一段時間。由於TuriCreate須要從蘋果開發者官網上下載一些數據。這些數據大概100M左右。

須要的時長,依你和蘋果服務器的鏈接速度而異。反正在我這兒,下載挺慢的。

好在只有第一次須要下載。以後的重複執行,會跳過下載步驟。

model = tc.image_classifier.create(train_data, target='label')
複製代碼

下載完畢後,你會看到TuriCreate的訓練信息。

Resizing images...
Performing feature extraction on resized images...
Completed 168/168
PROGRESS: Creating a validation set from 5 percent of training data. This may take a while.
          You can set ``validation_set=None`` to disable validation tracking.
複製代碼

你會發現,TuriCreateh會幫助你把圖片進行尺寸變換,而且自動抓取圖片的特徵。而後它會從訓練集裏面抽取5%的數據做爲驗證集,不斷迭代尋找最優的參數配置,達到最佳模型。

這裏可能會有一些警告信息,忽略就能夠了。

當你看到下列信息的時候,意味着訓練工做已經順利完成了。

能夠看到,幾個輪次下來,不管是訓練的準確度,仍是驗證的準確度,都已經很是高了。

下面,咱們用得到的圖片分類模型,來對測試集作預測。

predictions = model.predict(test_data)
複製代碼

咱們把預測的結果(一系列圖片對應的標記序列)存入了predictions變量。

而後,咱們讓TuriCreate告訴咱們,在測試集上,咱們的模型表現如何。

先別急着往下看,猜猜結果正確率大概是多少?從0到1之間,猜想一個數字。

猜完後,請繼續。

metrics = model.evaluate(test_data)
print(metrics['accuracy'])
複製代碼

這就是正確率的結果:

0.967741935484
複製代碼

我第一次看見的時候,震驚不已。

咱們只用了100多個數據作了訓練,竟然就能在測試集(機器沒有見過的圖片數據)上,得到如此高的辨識準確度。

爲了驗證這不是準確率計算部分代碼的失誤,咱們來實際看看預測結果。

predictions
複製代碼

這是打印出的預測標記序列:

dtype: str
Rows: 31
['doraemon', 'doraemon', 'doraemon', 'doraemon', 'walle', 'doraemon', 'walle', 'doraemon', 'walle', 'walle', 'doraemon', 'doraemon', 'doraemon', 'doraemon', 'doraemon', 'walle', 'doraemon', 'doraemon', 'walle', 'walle', 'doraemon', 'doraemon', 'walle', 'walle', 'walle', 'doraemon', 'doraemon', 'walle', 'walle', 'doraemon', 'walle']
複製代碼

再看看實際的標籤。

test_data['label']
複製代碼

這是實際標記序列:

dtype: str
Rows: 31
['doraemon', 'doraemon', 'doraemon', 'doraemon', 'walle', 'doraemon', 'walle', 'walle', 'walle', 'walle', 'doraemon', 'doraemon', 'doraemon', 'doraemon', 'doraemon', 'walle', 'doraemon', 'doraemon', 'walle', 'walle', 'doraemon', 'doraemon', 'walle', 'walle', 'walle', 'doraemon', 'doraemon', 'walle', 'walle', 'doraemon', 'walle']
複製代碼

咱們查找一下,到底哪些圖片預測失誤了。

你固然能夠一個個對比着檢查。可是若是你的測試集有成千上萬的數據,這樣作效率就會很低。

咱們分析的方法,是首先找出預測標記序列(predictions)和原始標記序列(test_data['label'])之間有哪些不一致,而後在測試數據集裏展現這些不一致的位置。

test_data[test_data['label'] != predictions]
複製代碼

咱們發現,在31個測試數據中,只有1處標記預測發生了失誤。原始的標記是瓦力,咱們的模型預測結果是哆啦a夢。

咱們得到這個數據點對應的原始文件路徑。

wrong_pred_img_path = test_data[predictions != test_data['label']][0]['path']
複製代碼

而後,咱們把圖像讀取到img變量。

img = tc.Image(wrong_pred_img_path)
複製代碼

用TuriCreate提供的show()函數,咱們查看一下這張圖片的內容。

img.show()
複製代碼

由於深度學習的一個問題在於模型過於複雜,因此咱們沒法精確判別機器是怎麼錯誤辨識這張圖的。可是咱們不難發現這張圖片有些特徵——除了瓦力之外,還有另一個機器人。

若是你看過這部電影,應該知道兩個機器人之間的關係。這裏咱們按下不表。問題在於,這個右上方的機器人圓頭圓腦,看上去與棱角分明的瓦力差異很大。可是,別忘了,哆啦a夢也是圓頭圓腦的。

原理

按照上面一節的代碼執行後,你應該已經瞭解如何構建本身的圖片分類系統了。在沒有任何原理知識的狀況下,你研製的這個模型已經作得很是棒了。不是嗎?

若是你對原理不感興趣,請跳過這一部分,看「小結」。

若是你對知識喜歡刨根問底,那我們來說講原理。

雖然不過寫了10幾行代碼,可是你構建的模型卻足夠複雜和高大上。它就是傳說中的卷積神經網絡(Convolutional Neural Network, CNN)。

它是深度機器學習模型的一種。最爲簡單的卷積神經網絡大概長這個樣子:

最左邊的,是輸入層。也就是我們輸入的圖片。本例中,是哆啦a夢和瓦力。

在計算機裏,圖片是按照不一樣顏色(RGB,即Red, Green, Blue)分層存儲的。就像下面這個例子。

根據分辨率不一樣,電腦會把每一層的圖片存成某種大小的矩陣。對應某個行列位置,存的就是個數字而已。

這就是爲何,在運行代碼的時候,你會發現TuriCreate首先作的,就是從新設置圖片的大小。由於若是輸入圖片大小各異的話,下面步驟沒法進行。

有了輸入數據,就順序進入下一層,也就是卷積層(Convolutional Layer)。

卷積層聽起來彷佛很神祕和複雜。可是原理很是簡單。它是由若干個過濾器組成的。每一個過濾器就是一個小矩陣。

使用的時候,在輸入數據上,移動這個小矩陣,跟原先與矩陣重疊的位置上的數字作乘法後加在一塊兒。這樣原先的一個矩陣,就變成了「卷積」以後的一個數字。

下面這張動圖,很形象地爲你解釋了這一過程。

這個過程,就是不斷從一個矩陣上去尋找某種特徵。這種特徵多是某個邊緣的形狀之類。

再下一層,叫作「池化層」(Pooling Layer)。這個翻譯簡直讓人無語。我以爲翻譯成「彙總層」或者「採樣層」都要好許多。下文中,咱們稱其爲「採樣層」。

採樣的目的,是避免讓機器認爲「必須在左上角的方格位置,有一個尖尖的邊緣」。實際上,在一張圖片裏,咱們要識別的對象可能發生位移。所以咱們須要用匯總採樣的方式模糊某個特徵的位置,將其從「某個具體的點」,擴展成「某個區域」。

若是這樣說,讓你以爲不夠直觀,請參考下面這張動圖。

這裏使用的是「最大值採樣」(Max-Pooling)。以原先的2x2範圍做爲一個分塊,從中找到最大值,記錄在新的結果矩陣裏。

一個有用的規律是,隨着層數不斷向右推動,通常結果圖像(其實正規地說,應該叫作矩陣)會變得愈來愈小,可是層數會變得愈來愈多。

只有這樣,咱們才能把圖片中的規律信息抽取出來,而且儘可能掌握足夠多的模式。

若是你仍是以爲不過癮,請訪問這個網站

它爲你生動解析了卷積神經網絡中,各個層次上到底發生了什麼。

左上角是用戶輸入位置。請利用鼠標,手寫一個數字(0-9)。寫得難看一些也沒有關係。

我輸入了一個7。

觀察輸出結果,模型正確判斷第一選擇爲7,第二可能性爲3。回答正確。

讓咱們觀察模型建構的細節。

咱們把鼠標挪到第一個卷積層。停在任意一個像素上。電腦就告訴咱們這個點是從上一層圖形中哪幾個像素,通過特徵檢測(feature detection)得來的。

同理,在第一個Max pooling層上懸停,電腦也能夠可視化展現給咱們,該像素是從哪幾個像素區塊裏抽樣得到的。

這個網站,值得你花時間多玩兒一下子。它能夠幫助你理解卷積神經網絡的內涵。

回顧咱們的示例圖:

下一層叫作全鏈接層(Fully Connected Layer),它其實就是把上一層輸出的若干個矩陣所有壓縮到一維,變成一個長長的輸出結果。

以後是輸出層,對應的結果就是咱們須要讓機器掌握的分類。

若是隻看最後兩層,你會很容易把它跟以前學過的深度神經網絡(Deep Neural Network, DNN)聯繫起來。

既然咱們已經有了深度神經網絡,爲何還要如此費力去使用卷積層和採樣層,致使模型如此複雜呢?

這裏出於兩個考慮:

首先是計算量。圖片數據的輸入量通常比較大,若是咱們直接用若干深度神經層將其鏈接到輸出層,則每一層的輸入輸出數量都很龐大,總計算量是難以想像的。

其次是模式特徵的抓取。即使是使用很是龐大的計算量,深度神經網絡對於圖片模式的識別效果也未必盡如人意。由於它學習了太多噪聲。而卷積層和採樣層的引入,能夠有效過濾掉噪聲,突出圖片中的模式對訓練結果的影響。

你可能會想,我們只編寫了10幾行代碼而已,使用的卷積神經網絡必定跟上圖差很少,只有四、5層的樣子吧?

不是這樣的,你用的層數,有足足50層呢!

它的學名,叫作Resnet-50,是微軟的研發成果,曾經在2015年,贏得過ILSRVC比賽。在ImageNet數據集上,它的分類辨識效果,已經超越人類

我把對應論文的地址附在這裏,若是你有興趣,能夠參考。

請看上圖中最下面的那一個,就是它的大略樣子。

足夠深度,足夠複雜吧。

若是你以前對深度神經網絡有一些瞭解,必定會更加以爲難以想象。這麼多層,這麼少的訓練數據量,怎麼能得到如此好的測試結果呢?而若是要得到好的訓練效果,大量圖片的訓練過程,豈不是應該花很長時間嗎?

沒錯,若是你本身從頭搭建一個Resnet-50,而且在ImageNet數據集上作訓練,那麼即使你有很好的硬件設備(GPU),也須要很長時間。

若是你在本身的筆記本上訓練……算了吧。

那麼,TuriCreate難道真的是個奇蹟?既不須要花費長時間訓練,又只須要小樣本,就能得到高水平的分類效果?

不,數據科學裏沒有什麼奇蹟。

究竟是什麼緣由致使這種看似神奇的效果呢?這個問題留做思考題,請善用搜索引擎和問答網站,來幫助本身尋找答案。

小結

經過本文,你已掌握瞭如下內容:

  • 如何在Anaconda虛擬環境下,安裝蘋果公司的機器學習框架TuriCreate。
  • 如何在TuriCreate中讀入文件夾中的圖片數據。而且利用文件夾的名稱,給圖片打上標記。
  • 如何在TuriCreate中訓練深度神經網絡,以分辨圖片。
  • 如何利用測試數據集,檢驗圖片分類的效果。而且找出分類錯誤的圖片。
  • 卷積神經網絡(Convolutional Neural Network, CNN)的基本構成和工做原理。

可是因爲篇幅所限,咱們沒有說起或深刻解釋如下問題:

  • 如何批量獲取訓練與測試圖片數據。
  • 如何利用預處理功能,轉換TuriCreate不能識別的圖片格式。
  • 如何從頭搭建一個卷積神經網絡(Convolutional Neural Network, CNN),對於模型的層次和參數作到徹底掌控。
  • 如何既不須要花費長時間訓練,又只須要小樣本,就能得到高水平的分類效果(提示關鍵詞:遷移學習,transfer learning)。

請你在實踐中,思考上述問題。歡迎留言和發送郵件,與我交流你的思考所得。

討論

你以前作過圖片分類任務嗎?你是如何處理的?用到了哪些好用的工具?比起我們的方法,有什麼優缺點?歡迎留言,把你的經驗和思考分享給你們,咱們一塊兒交流討論。

喜歡請點贊。還能夠微信關注和置頂個人公衆號「玉樹芝蘭」(nkwangshuyi)

若是你對數據科學感興趣,不妨閱讀個人系列教程索引貼《如何高效入門數據科學?》,裏面還有更多的有趣問題及解法。

相關文章
相關標籤/搜索