給你10萬張圖片,讓你找出與其中某張圖片最爲近似的10張,你會怎麼作?不要輕言放棄,也不用一張張瀏覽。使用Python,你也能夠輕鬆搞定這個任務。python
《如何用Python和深度神經網絡識別圖像?》一文寫完後,我收到了很多讀者的反饋。其中一個很廣泛的疑問是:git
識別相同或類似的圖像,有什麼好的方法麼?github
我雖然樂於幫助讀者解決問題,但實話實說,一開始不太理解這種需求。瀏覽器
我文章裏的樣例圖片(哆啦a夢和瓦力),都是從網絡蒐集來的。若是你須要從網上找到跟某張圖片近似的圖像,可使用Google的「以圖搜圖」功能啊。安全
很快,我忽然醒悟過來。bash
這種需求,每每不是爲了從互聯網上大海撈針,尋找近似圖片。而是在一個私有海量圖片集合中,找到近似圖像。微信
這種圖片集合,也許是你團隊的科研數據。例如你研究鳥類。某天瀏覽野外拍攝設備傳回來的圖像時,忽然發現一個新奇品種。網絡
你因而很想搞清楚這種鳥類的出現時間、生活狀態等。這就須要從大量圖片裏,找到與其近似的圖片(最有多是拍到了同一種鳥)。app
這種圖片集合,也許是社會安全數據。例如你在反恐部門,系統忽然發現某個疑似恐怖分子出如今敏感區域。這傢伙每一次現身,都伴隨着惡性刑事案件的發生,給人民羣衆的生命財產安全帶來嚴重威脅。框架
這時候不管對其衣着、外貌仍是交通工具的類似度搜索,就顯得相當重要了。
上述例子中,由於你都沒有把圖像上傳到互聯網,Google的「以圖搜圖」引擎功能再強大,也無能爲力。
好吧,解決這個問題,頗有意義。
下一個問題天然是:這種需求,解決起來複雜嗎?
是否是須要跨過很高的技術門檻才能實現?是否是須要花大量經費僱傭專家才能完成?
本文,我爲你展現如何用10幾行Python代碼,解決這個問題。
爲了講解的方便,咱們依然採用《如何用Python和深度神經網絡識別圖像?》一文中使用過的哆啦a夢和瓦力圖片集合。
我給你準備好了119張哆啦a夢的照片,和80張瓦力的照片。圖片已經上傳到了這個Github項目。
請點擊這個連接,下載壓縮包。而後在本地解壓。做爲我們的演示目錄。
解壓後,你會看到目錄下有個image文件夾,其中包含兩個子目錄,分別是doraemon和walle。
doraemon的目錄下,都是各式各樣的藍胖子圖片。
瓦力目錄下的圖片是這個樣子的:
數據已經有了,下面咱們來準備一下環境配置。
本文中,咱們須要使用到蘋果公司的機器學習框架TuriCreate。
請注意TuriCreate發佈時間不久,目前支持的操做系統列表以下:
這就意味着,若是你用的操做系統是Windows 7及如下版本,那麼目前TuriCreate還不支持。如需使用,有兩種辦法:
第一種,請升級到Windows 10,而且使用WSL。關於如何使用WSL,我幫你找到了一個中文教程。請按照教程一步步完成安裝。
第二種,採用虛擬機。推薦採用Virtualbox虛擬機,開源免費。一樣地,我也幫你找到了很詳盡的Virtualbox安裝Ubuntu Linux的中文教程。你能夠參照它安裝好Linux。
解決了系統兼容性問題,下面咱們在TuriCreate支持的系統中,安裝Python集成運行環境Anaconda。
請到這個網址 下載最新版的Anaconda。下拉頁面,找到下載位置。根據你目前使用的系統,網站會自動推薦給你適合的版本下載。我使用的是macOS,下載文件格式爲pkg。
下載頁面區左側是Python 3.6版,右側是2.7版。請選擇2.7版本。
雙擊下載後的pkg文件,根據中文提示一步步安裝便可。
裝好Anaconda後,咱們安裝TuriCreate。
請到你的「終端」下面,進入我們剛剛下載解壓後的樣例目錄。
執行如下命令,咱們來建立一個Anaconda虛擬環境,名字叫作turi。若是你以前跟隨我在《如何用Python和深度神經網絡識別圖像?》一文中創立過這個虛擬環境,此處請跳過。
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-similarity」。
準備工做完畢,下面咱們就能夠開始編寫程序了。
首先,咱們讀入TuriCreate軟件包。
import turicreate as tc
複製代碼
咱們指定圖像所在的文件夾image。讓TuriCreate讀取全部的圖像文件,而且存儲到data數據框。
data = tc.image_analysis.load_images('./image/')
複製代碼
咱們來看看,data數據框的內容:
data
複製代碼
data包含兩列信息,第一列是圖片的地址,第二列是圖片的長寬描述。
下面咱們要求TuriCreate給數據框中每一行添加一個行號。這將做爲圖片的標記,好在後面查找圖片時使用。
data = data.add_row_number()
複製代碼
再看看此時的data數據框內容:
data
複製代碼
咱們來看看數據框裏面的這些信息對應的圖片。
data.explore()
複製代碼
TuriCreate會彈出一個頁面,給咱們展現數據框裏面的內容。
把鼠標懸停在某張縮略圖上面,就能夠看到對應清晰大圖。
第一張圖片,是哆啦a夢:
第二張圖片,是瓦力:
下面,是重頭戲。咱們讓TuriCreate根據輸入的圖片集合,創建圖像類似度判別模型。
model = tc.image_similarity.create(data)
複製代碼
這個語句執行起來,可能須要一些時間。若是你是第一次使用TuriCreate,它可能還須要從網上下載一些數據。請耐心等待。
Resizing images...
Performing feature extraction on resized images...
Completed 199/199
複製代碼
注意這裏的提示,TuriCreate自動幫咱們作了圖片尺寸調整等預處理工做,而且對每一張圖片,都作了特徵提取。
通過或長或短的等待,模型已經成功創建。
下面,咱們來嘗試給模型一張圖片,讓TuriCreate幫咱們從目前的圖片集合裏,挑出最爲類似的10張來。
爲了方便,咱們就選擇第一張圖片做爲查詢輸入。
咱們利用show()
函數展現一下這張圖片。
tc.Image(data[0]['path']).show()
複製代碼
確認無誤,仍是那張哆啦a夢。
下面咱們來查詢,咱們讓模型尋找出與這張圖片最類似的10張。
similar_images = model.query(data[0:1], k=10)
複製代碼
很快,系統提示咱們,已經找到了。
咱們把結果存儲在了similar_images
變量裏面,下面咱們來看看其中都有哪些圖片。
similar_images
複製代碼
返回的結果一共有10行。跟咱們的要求一致。
每一行數據,包含4列。分別是:
有了這些信息,咱們就能夠查看到底哪些圖片與輸入查詢圖片最爲類似了。
注意其中的第一張結果圖片,其實就是咱們的輸入圖片自己。考慮它沒有意義。
咱們提取所有結果圖片的標記(索引)值,忽略掉第一張(自身)。
similar_image_index = similar_images['reference_label'][1:]
複製代碼
剩餘9張圖片的標記都在結果中:
similar_image_index
複製代碼
dtype: int
Rows: 9
[194, 158, 110, 185, 5, 15, 79, 91, 53]
複製代碼
下面咱們但願TuriCreate可以可視化幫咱們展現這9張圖片的內容。
咱們要把上面9張圖片的標記在全部圖片的索引列表中過濾出來:
filtered_index = data['id'].apply(lambda x : x in similar_image_index)
複製代碼
看看過濾後的索引結果:
filtered_index
複製代碼
dtype: int
Rows: 199
[0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, ... ]
複製代碼
你能夠本身數一數,其中標爲1的那些圖片位置,和咱們存儲在similar_image_index
中的數字是否一致。
驗證完畢之後,請執行如下語句。咱們再次調用TuriCreate的explore()
函數,展示類似度查詢結果圖片。
data[filtered_index].explore()
複製代碼
系統會彈出如下對話框:
咱們能夠看到,所有查詢結果圖片中,只出現了哆啦a夢。瓦力的圖片,一張都沒有出現。
近似圖片查找成功!
隨着本文操做樣例數據後,你不妨換用本身的數據,來動手嘗試一番。
展現瞭如何用10幾行Python代碼幫你查找類似圖形後,咱們來聊聊這種強大、簡潔背後的原理。
若是你對原理不感興趣,請跳過這一部分,看「小結」。
雖然咱們剛剛只是用了一條語句構建模型:
model = tc.image_similarity.create(data)
複製代碼
然而實際上,TuriCreate在後臺爲咱們作了不少事情。
首先,它調用了一個很是複雜的,在龐大數據集上訓練好的模型。
《如何用Python和深度神經網絡識別圖像?》一文中,咱們介紹過,這個模型就是上圖中的最後一行。它的名字叫作Resnet-50,足足有50層,訓練的圖片數以百萬計,訓練時長也好久。
這裏,機智的你必定會問個問題:那些數以百萬計的預訓練圖片裏面,是否有哆啦a夢和瓦力呢?
沒有。
那就怪了,不是嗎?
若是這個複雜的模型以前根本就沒有見過哆啦a夢和瓦力,那它怎麼知道如何區分它們呢?又怎麼可以判別兩張哆啦a夢之間的差異,就必定比哆啦a夢和瓦力之間更小呢?
《如何用Python和深度神經網絡識別圖像?》一文裏,我已經提示給你一個關鍵詞:遷移學習(transfer learning)。
這裏我們就不深刻技術細節了。我只給你在概念層次講解一下。
還記得這張描述計算機視覺(卷積神經網絡)的示意圖嗎?
在全鏈接層(Fully Connected Layer)以前,你可能進行了屢次的卷積、抽樣、卷積、抽樣……這些中間層次,幫咱們描繪了圖片的一些基本特徵,例如邊緣大概是個什麼形狀,某個區塊主要的顏色是哪些等。
到了全鏈接層,你只剩下了一組數據,這組數據可能很長,它抽取了你輸入數據的所有特徵。
若是你的輸入是一隻貓,此時的全鏈接層裏就描述了這隻貓的各類信息,例如毛髮顏色、面部組成部分排列方式、邊緣的形狀……
這個模型能夠幫你提取貓的特徵,但它並不知道「貓」的概念是什麼。
你天然能夠用它幫你提取一條狗的特徵。
同理,哆啦a夢的照片,與貓咪的照片同樣,都是二維圖片,都是用不一樣顏色分層。
那用其餘圖片訓練的模型,可否提取哆啦a夢照片裏的特徵呢?
固然也能夠!
使用遷移學習的關鍵,在於凍結中間過程的所有訓練結果,直接把一幅圖,利用在其餘圖片集合上訓練的模型,轉化爲一個特徵描述結果。
後面的工做,只把這個最後的特徵描述(全鏈接層),用來處理分類和類似度計算。
前面的好幾十層參數迭代訓練,通通都被省卻了。
難怪能夠利用這麼小的數據集得到如此高的準確度;也難怪能夠在這麼短的時間裏,就得到整合後的模型結果。
把在某種任務上積累下的經驗與認知,遷移到另外一種近似的新任務上,這種能力就叫作遷移學習。
比起機器來,人的遷移學習能力更爲強大。
雨果獎做者郝景芳在最近的一篇文章裏,描述了人的這種強大學習能力:
小孩子能夠快速學習,進行小數據學習,並且能夠獲得「類」的概念。小孩子輕易分得清「鴨子」這個概念,和每一隻具體不一樣的鴨子,有什麼不一樣。前者是抽象的「類」,後者是具體的東西。小孩子不須要看多少張鴨子的照片,就能獲得「鴨子」這個抽象「類」的概念。
用成語來描述,大概就是「舉一反三」吧。
若是人類不善於遷移學習,把生活中的全部事物,全都當成新的東西從頭學起,那後果簡直不堪設想。對比咱們一輩子中所能處理的信息總量,這種認知負荷將是沒法承受的。
回到咱們的問題裏,若是模型能夠幫咱們把每一張圖片,都變成全鏈接層上的那一長串數字(特徵),那麼咱們分辨這些圖片的類似程度,就變得太簡單了。由於這變成了一個簡單的空間向量距離問題。
處理這種簡單的數值計算,咱們人類可能以爲很繁瑣。可是計算機算起來,那就很歡快了。
根據距離大小排序,找出其中最小的幾個向量,它們描述的圖片,就被模型斷定爲類似度最高的。
在《如何用Python和深度神經網絡識別圖像?》一文的基礎上,本文進一步介紹瞭如下內容:
若是你沒有讀過《如何用Python和深度神經網絡識別圖像?》,強烈建議你讀一讀。閱讀過程能夠幫助你更好地理解基於深度神經網絡的計算機視覺工做原理。
你以前遭遇過大海撈針,尋找近似圖片的工做嗎?你是如何處理的?使用過哪些好的工具與方法?與本文相比較,它們的優點有哪些?歡迎留言,把你的經驗和思考分享給你們,咱們一塊兒交流討論。
喜歡請點贊。還能夠微信關注和置頂個人公衆號「玉樹芝蘭」(nkwangshuyi)。
若是你對數據科學感興趣,不妨閱讀個人系列教程索引貼《如何高效入門數據科學?》,裏面還有更多的有趣問題及解法。