1. 程式人生 > >機器學習實戰:用nodejs實現人臉識別

機器學習實戰:用nodejs實現人臉識別

機器學習實戰:用nodejs實現人臉識別

 

在本文中,我將向你展示如何使用face-recognition.js執行可靠的人臉檢測和識別 。 我曾經試圖找一個能夠精確識別人臉的Node.js庫,但是沒有找到,因此,我決定自己搞一個! 
cover

這個npm包基於dlib實現,因為我發現dlib的識別精度很高。 dlib庫使用深度學習方法,並附帶一些預訓練的模型,這些預置的模型,在LFW人臉識別基準測試上可以達到驚人的準確度:99.38% 。

為什麼要搞這個東西?

最近我一直在嘗試使用Node.js來構建一個人臉識別應用程式,以便從《生活大爆炸》劇中來提取和識別人物的面部。最初,我想用OpenCV的人臉識別器來實現,思路類似於我在教程

Node.js + OpenCV for Face Recognition中的類似。

但是,雖然這些人臉識別器能夠提供快速的預測結果,但是我發現它們不夠健壯。 更確切地說,雖然他們適用於正面的人臉影象,但只要人臉姿勢略有不同,就會產生相當不靠譜的預測結果。

因此,我就開始尋找替代方案,然後發現了dlib這個 C ++庫,我用它的Python API試了一下,結果給我留下了深刻的印象,最後我決定: 用Node.js來實現這個功能! 因此,我建立了這個npm包,提供一個簡化的Node.js 的API用於人臉識別。

face-recognition.js是什麼?

我開發face-recognitiontion.js的目的,是想提供一個這樣的npm包:

  • 有一個簡單的API,可以讓人快速上手人臉識別
  • 如果需要的話仍然允許更細粒度的識別控制
  • 易於設定(最好敲一行命令就能安裝,例如npm install)

雖然這個軟體包還在開發中,現在已經可以用它做點事情了:

人臉檢測

你可以使用深度神經網路進行人臉檢測,也可以使用簡單的正面人臉識別器進行快速但但不那麼可靠的檢測:

face detector

人臉識別

人臉識別器是一個深度神經網路,它使用我提到的模型來計算一個獨特的人臉描述符。 這個人臉識別器用標註過的人臉影象進行訓練後,就可以預測輸入人臉影象的標籤:

face recognizer

人臉特徵點檢測

你也可以使用這個包來檢測面部的特徵點(5個或68個):

face landmarks

實戰效果!

好吧,正如我所說的,我開始時嘗試用OpenCV,但沒能完成這個任務。 現在我有一堆150x150大小的人臉影象(分別來自Sheldon 、Raj 、 Lennard 、Howard和Stuart),我將向你展示使用這些資料來訓練人臉識別器、識別新面孔是多麼的簡單。 這個例子的程式碼可以在這個倉庫上找到。

準備資料

劇中每個角色不同的姿態分別採集大概20張影象:

data

我們將分別使用每個人的10個影象來訓練識別器,其餘部分來評估識別器的準確性:

const path = require('path')
const fs = require('fs') const fr = require('face-recognition') const dataPath = path.resolve('./data/faces') const classNames = ['sheldon', 'lennard', 'raj', 'howard', 'stuart'] const allFiles = fs.readdirSync(dataPath) const imagesByClass = classNames.map(c => allFiles .filter(f => f.includes(c)) .map(f => path.join(dataPath, f)) .map(fp => fr.loadImage(fp)) ) const numTrainingFaces = 10 const trainDataByClass = imagesByClass.map(imgs => imgs.slice(0, numTrainingFaces)) const testDataByClass = imagesByClass.map(imgs => imgs.slice(numTrainingFaces))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

每個臉部影象的檔名中包含了人名,所以我們可以簡單地實現影象和其分類的對映:

['sheldon', 'lennard', 'raj', 'howard', 'stuart']
  • 1

你可以使用fr.loadImage(fp)讀取給定檔案路徑的影象。

檢測人臉

正如我所說的,影象已經提取為150x150的大小,這是我事先用opencv4nodejs完成的。 但是你也可以用下面的程式碼來檢測和提取人臉、儲存並進行標註:

const image = fr.loadImage('image.png')
const detector = fr.FaceDetector()
const targetSize = 150 const faceImages = detector.detectFaces(image, targetSize) faceImages.forEach((img, i) => fr.saveImage(img, `face_${i}.png`))
  • 1
  • 2
  • 3
  • 4
  • 5

訓練識別器

現在我們有了資料,可以開始訓練識別器:

const recognizer = fr.FaceRecognizer()

trainDataByClass.forEach((faces, label) => {
  const name = classNames[label]
  recognizer.addFaces(faces, name)
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

基本上這就是把每張人臉影象輸入到神經網路中,神經網路輸出面部的描述符並存儲指定類別的所有描述符。 你還可以通過將numJitters指定為第三個引數來利用訓練資料生成一些抖動後的影象,這個引數將應用旋轉 、縮放和映象等變換來建立每個輸入人臉的不同版本影象。 增加抖動版本的數量可能有助於提高預測的準確性,但同時也會增加訓練時間。

此外,我們可以儲存識別器的狀態,這樣就不必在下次要用時重新進行訓練了,只需要簡單地從一個檔案中載入訓練好的模型:

儲存:

const modelState = recognizer.serialize()
fs.writeFileSync('model.json', JSON.stringify(modelState))
  • 1
  • 2

載入:

const modelState = require('model.json')
recognizer.load(modelState)
  • 1
  • 2

識別新面孔

現在我們可以用剩餘的樣本資料來檢查預測精度並記錄結果:

const errors = classNames.map(_ => [])
testDataByClass.forEach((faces, label) => {
  const name = classNames[label] console.log() console.log('testing %s', name) faces.forEach((face, i) => { const prediction = recognizer.predictBest(face) console.log('%s (%s)', prediction.className, prediction.distance) // count number of wrong classifications if (prediction.className !== name) { errors[label] = errors[label] + 1 } }) }) // print the result const result = classNames.map((className, label) => { const numTestFaces = testDataByClass[label].length const numCorrect = numTestFaces - errors[label].length const accuracy = parseInt((numCorrect / numTestFaces) * 10000) / 100 return `${className} ( ${accuracy}% ) : ${numCorrect} of ${numTestFaces} faces have been recognized correctly` }) console.log('result:') console.log(result)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

當前的預測是通過計算輸入人臉影象的描述符向量到分類中每個描述符的歐式距離來完成的,同時也計算出所有距離的平均值 。 有人可能會說, kmeans聚類或SVM分類器將更適合這個任務,我也可能在將來實現這些演算法。 但是現在使用歐幾里德距離的實現,看起來執行速度很快而且效率夠高。

呼叫predictionBest將輸出具有最小距離(例如最高相似度)的結果。 輸出看起來像這樣:

{className:'sheldon',距離:0.5} 
  • 1

如果你想獲得所有分類的臉部描述符到輸入的人臉影象的距離,可以簡單地使用recognitionizer.predict(image) ,它將輸出一個數組,其成員包含輸入影象到每個分類的距離:

  [ 
  {className:'sheldon',距離:0.5}, 
  {className:'raj',距離:0.8}, 
  {className:'howard',距離:0.7}, {className:'lennard',距離:0.69}, {className:'stuart',距離:0.75} ] 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

結果

執行上面的例子將給出以下結果。

使用10個面孔進行訓練:

sheldon ( 90.9% ) : 10 of 11 faces have been recognized correctly
lennard ( 100% ) : 12 of 12 faces have been recognized correctly raj ( 100% ) : 12 of 12 faces have been recognized correctly howard ( 100% ) : 12 of 12 faces have been recognized correctly stuart ( 100% ) : 3 of 3 faces have been recognized correctly
  • 1
  • 2
  • 3
  • 4
  • 5

每個訓練只使用5個面孔 :

sheldon ( 100% ) : 16 of 16 faces have been recognized correctly
lennard ( 88.23% ) : 15 of 17 faces have been recognized correctly raj ( 100% ) : 17 of 17 faces have been recognized correctly howard ( 100% ) : 17 of 17 faces have been recognized correctly stuart ( 87.5% ) : 7 of 8 faces have been recognized correctly
  • 1
  • 2
  • 3
  • 4
  • 5

結論

從結果看,即使只用一小組資料進行訓練,也可以獲得相當準確的結果。甚至對這些我從網上抓取的小尺寸影象(一些提取的臉部非常模糊), 結果也令人非常滿意。

如果你喜歡這篇文章,記得關注我的頭條號:新缸中之腦!

原文:Node.js + face-recognition.js : Simple and Robust Face Recognition using Deep Learning