當前位置:
首頁 > 科技 > 這一次,我拒絕了Python,選擇了Go

這一次,我拒絕了Python,選擇了Go

這篇文章將教會你如何運用 Go 語言實現人臉識別

作者 | Kagami Hiiragi

譯者 | linstancy

編輯 | Jane

出品 | AI科技大本營

▌前言

如今,神經網路已經非常流行,人們將它用於各種任務,特別是人臉識別應用。

最近,我用一個以 Go 語言為後端的軟體,實現了一個人臉識別項目。它能夠識別出上傳照片中的人像 (如流行歌手)是誰。這聽起來不錯,我決定試一下也給你們介紹一下項目的整個過程。

需要說明的是,我儘可能地將所需的系統配置控制在較低水平,以便更多用戶可以通過使用便宜的伺服器來進行安裝,而這也是為什麼實現過程不使用 CUDA 或 GPU 的原因——雖然你現在可以很容易地租用這樣的伺服器,但它需要很高的成本,從而也會將很多潛在的使用者拒之門外。如果它只需要 CPU 而不需要外部依賴就能工作,情況會好很多。


▌選擇合適的語言

如果你詢問數據科學家或者那些有神經網路實踐經驗的工作者,幾乎所有人都會建議你使用 Python 語言來解決機器學習任務。考慮到語言社區,可用庫的數量,語言的簡單性等,Python 語言確實是一個明智的選擇。此外,在 Python 中,你還可以通過一些精彩的實例說明和文檔來找到一些受歡迎的人臉識別庫。

然而,這一次,我決定選用 Go 語言,主要有幾以下幾個原因:

我的論壇是用 Go 語言編寫的,我個人也真的很喜歡以 single-binary 為後端所帶來的便捷性。因此,在後端部署並整合人臉識別過程,而不需要 Python 實現的一些依賴和 IPC,這是很棒的。

Go 語言通常比 Python 更快,消耗的內存更少。任何高性能 Python 庫的關鍵部分都是用 C / C++ 語言編寫的,因此,無論如何你都會有 Python VM 的開銷。我偏愛於更快的語言,除非這種語言會嚴重影響開發時間。我不會用 C或C++ 作為 Web 應用程序編寫的主要語言,但 Go語言很好,它幾乎和 Python 一樣簡單。

我沒有在 Go 語言中找到人臉識別的有關庫,因此用 Go 語言實現這樣一個應用,對於整個社區而言,都是一件有趣又有幫助的事。


▌選擇合適的框架

如前所述,神經網路以及相應的實現框架如今正被廣泛地使用。僅在計算機視覺領域,可用的框架就有 Caffe,Torch,TensorFlow 等。

但是,有一個非常酷的機器學習庫 —— dlib 庫,一下就吸引了我的注意力。首先,它是用 C ++ 語言編寫的,因此你可以使用 cgo 輕鬆地創建 Go 語言綁定。其次,在 Wild benchmarks 基準的人臉識別任務上,據說它能實現 99.38% 的準確性,這聽起來是很不可思議的。再者,現在一些流行的人臉識別庫 face_recognition 和 openface 在底層都使用 dlib 庫,因此它在該任務上會是一個非常好的選擇。

▌安裝依賴項

一旦框架確定下來,那麼我們要如何在機器上開發並部署這個項目呢?首先,C++ 依賴項的安裝將會有很大的困難,因為你無法通過簡便的「go get」或「pip install」命令來實現。要麼只能希望你的操作系統存儲庫中提供這些依賴庫,要麼你只能通過繁瑣的編譯過程來安裝,這樣的話,這個問題就更加令人討厭,因為有許多人都在 dlib 編譯過程碰到問題。

如果你不得不通過編譯過程來安裝,那麼可以參考一下下面的教程,也許會有幫助

https://gist.github.com/ageitgey/629d75c1baac34dfa5ca2a1928a7aeaf

幸運的是,我們有更好的選擇:如果用戶的目標系統已知,我們可以構建 dlib 庫的二進位安裝包來大大簡化整個過程。說到伺服器軟體,Ubuntu 幾乎是系統標配,因此首先要保證你能支持這個系統。

Ubuntu的標準倉庫中自帶有 dlib庫,但其版本太舊了:人臉識別僅支持 dlib19.3 版本,所以我們需要構建自己的包。我為 Ubuntu 16.04 和 18.04 創建了 PPA (自定義存儲庫),安裝過程非常簡單,如下:

sudo add-apt-repository ppa:kagamih/dlib

sudo apt-get update

sudo apt-get install libdlib-dev

以上命令將安裝最新的 dlib19.15 版本及 Intel 的數學核心庫,對於 Intel 處理器而言,這似乎是標準 BLAS 和 LAPACK 介面的最快實現。

對於 Debian sid 和 Ubuntu 18.10 (尚未發布) 而言,標準倉庫中同樣提供了 dlib 的安裝過程,你只需要如下命令:

sudo apt-get install libdlib-dev libopenblas-dev

這將使用 OpenBLAS 來代替 MKL,實現的速度同樣非常快。或者,你也可以通過 enable non-free package 並安裝libmkl-dev來實現。

我們還需要 libjpeg 來載入 JPEG 圖像:在 Ubuntu 上安裝libjpeg-turbo8-dev包,或在 Debian 上安裝libjpeg62-turbo-dev。

到目前為止,我沒有給出其他系統的安裝說明,如果你在安裝 dlib 過程中碰到問題,可以訪問我的 github 希望能為你提供合理有效的安裝建議。

GitHub 地址:

https://github.com/Kagami/go-face

此外,我還考慮為 dlib 庫提供 Docker 鏡像 (其中有少部分內容已存在),許多具有複雜依賴關係的項目都傾向於使用這種分散式方法。但在我看來,一個本機包能夠為用戶提供更好的體驗,你不需要在控制台編寫長命令,也不需要處理 sandbox 環境中的內容。


▌寫入依賴庫

當前人臉識別庫地工作原理通常是:通過為照片上的每張人臉返回一組數字 (矢量嵌入或描述符) 來比較區分它們,並通過比較這些數字來找到圖像中人的名字 (通常是通過計算歐幾里德距離向量,得到屬於同一個人的兩張人臉的最小距離)。這個概念這次就不在這裡贅述了。

創建圖像中人臉的原始代碼並不是個重要的問題,這個過程幾乎是遵循官方的例子就可以了。你可以查看 facerec.cc 及其相應的頭文件 facerec.h,其中定義了 5 個函數和幾個在 Go 語言和 dlib 庫之間的交互結構。

在這裡,雖然 dlib 庫支持所有流行的圖像格式,但它只能從文件中載入它們。這將導致混亂,因為我們通常只會將圖像保存在內存中並將其寫入臨時文件。因此,在這裡我使用 libjpeg 來編寫自己的圖像載入器。由於大多數照片都以該格式存儲的,因此這種格式的載入器足以勝任大部分的需要,以後有需要我還會添加其他格式的圖像載入器。

我把 C++ 和 Go 語言的連接層放在 face.go 中。它提供了 Face 結構,用於保存圖像中人臉的坐標及其描述符,並通過Recognizer為所有操作提供介面,如初始化和實際識別。

一旦我們有了描述符,我們能做什麼呢?在最簡單的情況下,你可以通過比較未知描述符與所有已知描述符之間的歐幾里德距離。但這並不完美,即使是當前最先進的人臉識別技術也會得到錯誤的答案。如果想稍微改善一下結果,我們需要使用每個人的許多圖像,並檢查這些圖像中是否有非常接近於所提供的人臉。

這也正是分類器 classify.cc 所做的工作。首先,計算距離,然後對這些距離進行排序,計算同一個人在前 10 個最小距離中的點擊數。)

諸如支持向量機,將會在這個任務上提供更好的演算法性能。 dlib 甚至為訓練此類模型提供了便捷的 API。很少有文章會提到 SVM 在大型數據集上的性能,因此我打算先在大型集合上測試它。


▌使用

下面得到的結果你可以在 github 中查看:

import"github.com/Kagami/go-face"

GitHub 地址:

https://github.com/Kagami/go-face

相關的所有結構和方法概述,請參閱 GoDoc 文檔,主要包括以下幾個內容:

初始化識別器

識別所有的已知圖像並收集描述符

將具有相應類別的已知描述符傳遞給識別器

獲取未知圖像的描述符

對其類別進行分類

以下是一個工作示例,來說明了上述的所有步驟:

packagemain

import(

"fmt"

"log"

"path/filepath"

"github.com/Kagami/go-face"

)

// Path to directory with models and test images. Here it"s

// assumed it points to the

// clone.

constdataDir ="testdata"

// This example shows the basic usage of the package: create an

// recognizer, recognize faces, classify them using few known

// ones.

funcmain(){

// Init the recognizer.

rec, err := face.NewRecognizer(dataDir)

iferr !=nil{

log.Fatalf("Can"t init face recognizer: %v", err)

}

// Free the resources when you"re finished.

deferrec.Close()

// Test image with 10 faces.

testImagePristin := filepath.Join(dataDir,"pristin.jpg")

// Recognize faces on that image.

faces, err := rec.RecognizeFile(testImagePristin)

iferr !=nil{

log.Fatalf("Can"t recognize: %v", err)

}

iflen(faces) !=10{

log.Fatalf("Wrong number of faces")

}

// Fill known samples. In the real world you would use a lot of

// images for each person to get better classification results

// but in our example we just get them from one big image.

varsamples []face.Descriptor

varcats []int32

fori, f :=rangefaces {

samples =append(samples, f.Descriptor)

// Each face is unique on that image so goes to its own

// category.

cats =append(cats,int32(i))

}

// Name the categories, i.e. people on the image.

labels := []string{

"Sungyeon","Yehana","Roa","Eunwoo","Xiyeon",

"Kyulkyung","Nayoung","Rena","Kyla","Yuha",

}

// Pass samples to the recognizer.

rec.SetSamples(samples, cats)

// Now let"s try to classify some not yet known image.

testImageNayoung := filepath.Join(dataDir,"nayoung.jpg")

nayoungFace, err := rec.RecognizeSingleFile(testImageNayoung)

iferr !=nil{

log.Fatalf("Can"t recognize: %v", err)

}

ifnayoungFace ==nil{

log.Fatalf("Not a single face on the image")

}

catID := rec.Classify(nayoungFace.Descriptor)

ifcatID

log.Fatalf("Can"t classify")

}

// Finally print the classified label. It should be "Nayoung".

fmt.Println(labels[catID])

}

運行下面命令:

mkdir -p ~/go&& cd ~/go# Or cd to your $GOPATH

mkdir -p src/go-face-example && cd src/go-face-example

git clone https://github.com/Kagami/go-face-testdata testdata

edit main.go# Paste example code

goget .

../../bin/go-face-example

由於在 dlib 的代碼中大量使用了 C++ 模板,因此需要一些時間來編譯 go-face (在我的 i7 上大約需要運行 1 分鐘)。 幸運的是,Go 語言能夠構建輸出緩存,這樣可以在今後構建的時候速度更快。

上面的示例輸出應列印「Nayoung」,表示能夠正確識別出未知圖像。


▌模型

go-face 需要shape_predictor_5_face_landmarks.dat和

dlib_face_recognition_resnet_model_v1.dat模型才能開始工作。你可以從 dlib-models 倉庫中下載它們:

mkdir models && cd models

wget https://github.com/davisking/dlib-models/raw/master/shape_predictor_5_face_landmarks.dat.bz2

bunzip2 shape_predictor_5_face_landmarks.dat.bz2

wget https://github.com/davisking/dlib-models/raw/master/dlib_face_recognition_resnet_model_v1.dat.bz2

bunzip2 dlib_face_recognition_resnet_model_v1.dat.bz2

此外,當你要運行示例代碼時,還可以通過 go-face-testdata 倉庫來訪問這些模型。


▌未來的工作

我對結果非常滿意,通過簡單的 API,得到不錯的識別結果,還可以輕鬆嵌入到 Go 的應用程序中。當然,還有需要改進的地方:

為了追求簡單性和速度,在創建描述符時,go-face 無法對圖像進行一些預處理,如抖動。但是,增加圖像預處理操作是很有必要的,因為它可能會提高識別的性能。

Dlib 庫支持很多圖像格式 (如 JPEG,PNG,GIF,BMP,DNG),但是 go-face 目前只能實現 JPEG 格式,未來的工作我們希望可以支持更多的格式。

正如 dlib 的作者 Davis 所建議的,相比於搜索最小距離,採用多類 SVM 可能會得到更好的分類結果,因此還需要進行額外的測試驗證。

在 go-face 中,除非真的需要,不然我盡量不複製值,但實際上它還測試過大樣本 (10,000+人臉數據集) 的測試性能,可能存在一些瓶頸,有待日後完善。

從人臉提取特徵向量是一個強大的概念,因為你不需要收集自己的訓練數據,這也是一項非常艱巨的任務 (Davis 曾提到創建 dlib 中 ResNet 模型所用到的 300 萬張人臉數據集),但為了獲得更高的識別性能這可能也是無法避免的,因此值得為自己模型的訓練提供相應的工具。

https://hackernoon.com/face-recognition-with-go-676a555b8a7e

BTA六折早鳥票倒計時進行中

掃碼搶票

2018年9月13-14日,區塊鏈技術及應用峰會(BTA)·中國將於上海再度乘勢來襲!

彙集100+區塊鏈技術領導人物、100+區塊鏈投資大咖、100+技術&財經媒體、1000+區塊鏈技術愛好者,深挖區塊鏈前沿技術及落地實踐。

六折早鳥票倒計時進行中,鎖定申城,年中超豪華乾貨空投,等你來搶。


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 AI科技大本營 的精彩文章:

TensorFlow 版本 1.10.0 發布
一篇文章告訴你AI工程師到底怎麼學!

TAG:AI科技大本營 |