1. 程式人生 > >這一次,我拒絕了Python,選擇了Go

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

640?wx_fmt=gif

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

640?wx_fmt=png

作者:Kagami Hiiragi

譯者:linstancy

出品:AI科技大本營

00 前言

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

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

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

01 選擇合適的語言

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

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

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

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

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

02 選擇合適的框架

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

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

03 安裝依賴項

一旦框架確定下來,那麼我們要如何在機器上開發並部署這個專案呢?首先,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 環境中的內容。

04 寫入依賴庫

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

建立影象中人臉的原始程式碼並不是個重要的問題,這個過程幾乎是遵循官方的例子就可以了。你可以檢視 facerec.cc 及其相應的標頭檔案 facerec.h,其中定義了 5 個函式和幾個在 Go 語言和 dlib 庫之間的互動結構。

在這裡,雖然 dlib 庫支援所有流行的影象格式,但它只能從檔案中載入它們。這將導致混亂,因為我們通常只會將影象儲存在記憶體中並將其寫入臨時檔案。因此,在這裡我使用 libjpeg 來編寫自己的影象載入器。由於大多數照片都以該格式儲存的,因此這種格式的載入器足以勝任大部分的需要,以後有需要我還會新增其他格式的影象載入器。

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

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

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

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

05 使用

下面得到的結果你可以在 github 中檢視:

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

GitHub 地址:

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

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

  • 初始化識別器

  • 識別所有的已知影象並收集描述符

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

  • 獲取未知影象的描述符

  • 對其類別進行分類

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

package main
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
// <https://github.com/Kagami/go-face-testdata> clone.
const dataDir = "testdata"
// This example shows the basic usage of the package: create an
// recognizer, recognize faces, classify them using few known
// ones.
func main() {
  // Init the recognizer.
  rec, err := face.NewRecognizer(dataDir)
  if err != nil {
    log.Fatalf("Can't init face recognizer: %v", err)
  }
  // Free the resources when you're finished.
  defer rec.Close()
  // Test image with 10 faces.
  testImagePristin := filepath.Join(dataDir, "pristin.jpg")
  // Recognize faces on that image.
  faces, err := rec.RecognizeFile(testImagePristin)
  if err != nil {
    log.Fatalf("Can't recognize: %v", err)
  }
  if len(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.
  var samples []face.Descriptor
  var cats []int32
  for i, f := range faces {
    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)
  if err != nil {
    log.Fatalf("Can't recognize: %v", err)
  }
  if nayoungFace == nil {
    log.Fatalf("Not a single face on the image")
  }
  catID := rec.Classify(nayoungFace.Descriptor)
  if catID < 0 {
    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
go get .
../../bin/go-face-example

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

上面的示例輸出應列印“Nayoung”,表示能夠正確識別出未知影象。

06 模型

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 倉庫來訪問這些模型。

07 未來的工作

我對結果非常滿意,通過簡單的 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

640?

更多精彩

在公眾號後臺對話方塊輸入以下關鍵詞

檢視更多優質內容!

PPT|報告|讀書| 書單 | 乾貨

Python | 機器學習 | 深度學習 | 神經網路

區塊鏈 | 揭祕 | 高考 | 數學

猜你想看

Q: 你要拋棄Python嗎

歡迎留言與大家分享

覺得不錯,請把這篇文章分享給你的朋友

轉載 / 投稿請聯絡:[email protected]

更多精彩,請在後臺點選“歷史文章”檢視

640?wx_fmt=jpeg

相關推薦

拒絕Python選擇Go

導讀:這篇文章將教會你如何運用 Go 語言實現人臉識別。作者:Kagami Hiiragi譯者:

的郵箱又收到封信關乎愛情

發件人: 「維克多多多的」 傳送時間: 2018年12月1日 收件人: 上官冷兒 主題: 獨白 by @「維克多多多的」 某友,你好呀!        我現在

撿起Python看看這次能堅持多久

電子產品 是什麽 數據 script out 身體 mac 發現 編程 對於一個數理化實在不怎麽樣的我來說,學習編程可不是什麽容易事兒,多年來最感興趣的是二戰史,以及語言的學習,自2009年開始對各類電子產品開始產生興趣,這一下就是將近10年的時間,從第一臺iPod Shu

連 web.xml 都不要純 Java 搭建 SSM 環境!

在 Spring Boot 專案中,正常來說是不存在 XML 配置,這是因為 Spring Boot 不推薦使用 XML ,注意,並非不支援,Spring Boot 推薦開發者使用 Java 配置來搭建框架,Spring Boot 中,大量的自動化配置都是通過 Java 配置來實現的,這一套實現方案,我們也可

連 web.xml 都不要純 Java 搭建 SSM 環境

在 Spring Boot 專案中,正常來說是不存在 XML 配置,這是因為 Spring Boot 不推薦使用 XML ,注意,

參加天搜集團微公益認識位心有乾坤的老人

天搜集團昨天和一個朋友聊微信,她是我前段時間去半山參加公益認識的一個小姑娘,也是組織那次公益活動的天搜集團的工作人員。她說周末她又去了一趟半山頤養院,順便把上次和爺爺奶奶一起拍的好些照片打印出來送過去了,還說上次活動裏和我結對的陳爺爺問起我了。我聽了以後心裏有點小感觸,就和她約好過段時間一起再去頤養院。 公

分頁又做變換似乎是成功

圖下面的程式碼,那個sprintf是剽別人的各位還是自己從網上下載吧 kernel.c #include "sprintf.h" void myhlt(void); void setup_page(void); void change_page(void); v

Google 誓要紮根中國

相比於去年有李飛飛女士的激動亮相宣佈 Google 在中國的 AI 動作,今年的 Google 開發者大會似乎更多的還是幾個月前 Google I/O 的延續,但是全面鋪排的技術生態或許彰顯了它意欲更深層次搶佔中國市場的勃勃雄心。 作者 | 郭芮發自 G

【疾風知勁草智者必懷仁】此生之路,我將走過;走過這一次,便再也無法重來。所有力所能及的善行所有充盈於心的善意,我將毫不吝惜即刻傾於。將不再拖延再不淡漠只因此生之路再也無法重來。醒掌天下事醉臥美人膝

此生之路,我將走過;走過這一次,便再也無法重來。所有力所能及的善行,所有充盈於心的善意,我將毫不吝惜,即刻傾於。我將不再拖延,再不淡漠,只因此生之路,再也無法重來。醒掌天下事,醉臥美人膝...

乾淨解除安裝mysql個人親測終於成功

一、在控制面板中解除安裝mysql軟體 二、解除安裝過後刪除C:\Program Files (x86)\MySQL該目錄下剩餘了所有檔案,把mysql資料夾也刪了 三、windows+R執行“regedit”檔案,開啟登錄檔 四、刪除登錄檔:HKEY_LOCAL_MA

“小筆記系列”之《演算法導論》----最近修改於2019/1/9更新到第六章。文章主要記錄本書的感受每看完章就在篇文章的基礎上修改一下。

本人大四上即將結束,於2018年12月18日購《演算法導論》這本書,慢慢看,第一階段先主要理解各個章節說的演算法都是什麼意思,書上的課後習題先不做,用得上什麼演算法我再詳細學習。這是官方課後答案的連結。  放在開頭:沒有好的演算法,壞的演算法之說,重點是針對不同的

任正非對華為熱點問題的迴應亮終於知道華為為什麼能扛過的衝擊!

任正非對華為熱點問題的迴應亮了,終於知道華為為什麼能扛過這一次的衝擊! 如果你是華為的老闆,看到一條傳遍網路的“美國封鎖華為”、“

終於系統的學習 JVM 記憶體結構

最近在看《 JAVA併發程式設計實踐 》這本書,裡面涉及到了 Java 記憶體模型,通過 Java 記憶體模型順理成章的來到的 JVM 記憶體結構,關於 JVM 記憶體結構的認知還停留在上大學那會的課堂上,一直沒有系統的學習這一塊的知識,所以這一次我把《 深入理解Java虛擬機器JVM高階特性與最佳實踐 》、

終於弄懂協變和逆變

一、前言 劉大胖決定向他的師傅燈籠法師請教什麼是協變和逆變。   劉大胖:師傅,最近我在學習泛型介面的時候看到了協變和逆變,翻了很多資料,可還是不能完全弄懂。 燈籠法師:阿胖,你不要被這些概念弄混,編譯器可不知道你說的什麼協變逆變。這個問題,首先你得弄懂什麼叫型別的可變

更新解決以前的所有問題!新版CAD看圖軟件的十大看點!

剛剛學習CAD的朋友們都不知道,區域覆蓋是什麽。CAD中的區域覆蓋就是將CAD中的一部分內容進行覆蓋,用新的內容替換掉舊的內容。在CAD看圖軟件中如何實現CAD的區域覆蓋呢?下面小編給大家講講操作方法。 1.先通過迅捷CAD編輯器打開需要操作CAD圖紙文件,並切換至“編輯器”菜

真正搞懂信用評分模型(上篇)

工程師 集中 重要 sklearn app 目的 概率 單變量 是我 python風控評分卡建模和風控常識 https://study.163.com/course/introduction.htm?courseId=1005214003&utm_campaign

為什麽放棄 Python 選擇 Go

讀取 ssa 語言排行 time 支持 javascrip node 更多 命令行 根據維基百科數據統計(https://en.wikipedia.org/wiki/List_of_programming_languages)一共有 600 余種不同的編程語言。對於五花八門

徹底解決Java的值傳遞和引用傳遞

信息 getter 數據類型 裝載 而已 訪問 實參 數據結構 修改 本文旨在用最通俗的語言講述最枯燥的基本知識 學過Java基礎的人都知道:值傳遞和引用傳遞是初次接觸Java時的一個難點,有時候記得了語法卻記不得怎麽實際運用,有時候會的了運用卻解釋不出原理,而且坊間討論

[精]--讓你徹底明白Java的值傳遞和引用傳遞!

本文旨在用最通俗的語言講述最枯燥的基本知識 學過Java基礎的人都知道:值傳遞和引用傳遞是初次接觸Java時的一個難點,有時候記得了語法卻記不得怎麼實際運用,有時候會的了運用卻解釋不出原理,而且坊間討論的話題又是充滿爭議:有的論壇帖子說Java只有值傳遞,有的部落格說兩者皆有;這讓人有點摸不著頭

欠的債都還給你們

一、Centos7安裝JDK 3123123 1 {template '_header'} 2 {template 'merch/common'} 3 <link rel="stylesheet" type="text/css" href="../addons/ew