使用 TensorFlow.js 預先訓練的機器學習模型,以 JavaScript 建立智慧型網路攝影機

1. 事前準備

機器學習技術已是當今熱門話題。這個應用程式似乎沒有限制,不久後似乎也會滿足幾乎所有產業的需求。如果您目前是工程師或設計人員、前端或後端,且已熟悉 JavaScript,本程式碼研究室就是為了協助您開始在技能組中加入機器學習。

必要條件

本程式碼研究室是為已熟悉 JavaScript 的資深工程師編寫。

建構項目

在本程式碼研究室中

  • 透過 TensorFlow.js 建立網頁,直接在網路瀏覽器中使用機器學習技術,透過網路攝影機直播分類及偵測常見物件 (可以同時包含多個物件)。
  • 強化一般網路攝影機辨識物體,並取得每個所找到物件的定界框座標
  • 醒目顯示影片串流中找到的物件,如下所示:

8f9bad6e49e646b.png

想像一下,只要能偵測影片中是否有人,您就可以計算任何指定時間的人口,估算一天內特定區域的繁忙程度,或是在您外出時偵測到狗狗,且您可能不在家中。如果可以,你可以使用自己的客製化硬體,製作自己的 Google Nest 攝影機,以便在偵測到任何類型的入侵者時收到快訊!效果好吃。很難完成嗎?不對,開始入侵吧...

課程內容

  • 如何載入預先訓練的 TensorFlow.js 模型。
  • 如何從即時網路攝影機直播擷取資料,並繪製到畫布上。
  • 如何對圖片影格進行分類,找出經過訓練的模型可辨識的任何物體定界框。
  • 如何使用從模型傳回的資料來醒目顯示找到的物件。

本程式碼研究室著重於如何開始使用 TensorFlow.js 預先訓練模型。我們不會解釋與 TensorFlow.js 和機器學習無關的概念和程式碼區塊,但會事先準備好這些區塊,屆時您只要複製及貼上即可。

2. 什麼是 TensorFlow.js?

1aee0ede85885520.png

TensorFlow.js開放原始碼的機器學習程式庫,可以在 JavaScript 中的任何位置執行。這個程式庫是以以 Python 編寫的原始 TensorFlow 程式庫為基礎,旨在重新建立 JavaScript 生態系統適用的一組開發人員體驗和 API。

哪些地方可以使用?

由於 JavaScript 具備可攜性,現在您可以使用 1 種語言來編寫程式碼,並在下列所有平台上輕鬆執行機器學習:

  • 運用基本 JavaScript,在網路瀏覽器的用戶端
  • 使用 Node.js 的伺服器端,甚至是 Raspberry Pi 等 IoT 裝置
  • 使用 Electron 的電腦版應用程式
  • 使用 React Native 的原生行動應用程式

TensorFlow.js 也支援這些環境中的多個後端,也就是可在 CPU 或 WebGL 等內部執行的實際硬體型環境。「後端」在這個情況下,並不表示伺服器端環境。舉例來說,執行作業的後端可能是 WebGL 中的用戶端,可確保相容性,同時讓執行速度飛快。目前 TensorFlow.js 支援:

  • 在裝置的顯示卡上執行 WebGL (GPU):這是在 GPU 加速功能下執行大型模型 (大小超過 3 MB) 的最快方法。
  • 在 CPU 上執行網路組合 (WASM):提升各裝置的 CPU 效能 (例如較舊的手機)。這種級別更適用於小型模型 (大小小於 3 MB),比起使用 WebGL,實際執行 WASM 的 CPU 執行速度較 WebGL,因為將內容上傳至圖形處理器時須耗用大量資源。
  • CPU 執行:若其他環境都無法使用,備用機制就應該沒有問題。這是最慢的圖示,但系統能隨時為您效勞。

注意:如果你知道要在哪個裝置上執行,可以選擇強制執行其中一個後端;如未指定,也可以直接讓 TensorFlow.js 決定。

用戶端超能力

在用戶端電腦的網路瀏覽器中執行 TensorFlow.js,能夠帶來許多值得評估的好處。

隱私權

您可以在用戶端電腦上訓練及分類資料,而不必將資料傳送至第三方網路伺服器。在某些情況下,可能需要遵守 GDPR 等當地法律,或者在處理使用者可能想保留在電腦上的資料,而不傳送至第三方時。

速度

由於您不需要將資料傳送至遠端伺服器,推論 (資料分類) 可以更快。更棒的是,在使用者授予您存取權的情況下,您可以直接存取裝置的感應器,例如相機、麥克風、GPS、加速計等。

觸及範圍廣大

全球使用者只要按一下您傳送給自己的連結,就能在瀏覽器中開啟網頁,並且使用您製作的內容。您不必透過 CUDA 驅動程式進行複雜的伺服器端 Linux 設定,而不只是透過機器學習系統完成。

費用

不需要伺服器,只要付費使用 CDN 代管 HTML、CSS、JS 和模型檔案即可。CDN 的費用比讓全天候運作的伺服器 (可能附有顯示卡) 便宜許多。

伺服器端功能

運用 TensorFlow.js 的 Node.js 實作可啟用下列功能。

完整的 CUDA 支援

在伺服器端,如要加速顯示顯示卡,您必須安裝 NVIDIA CUDA 驅動程式,才能使用 TensorFlow 搭配顯示卡 (這點與使用 WebGL 的瀏覽器不同,不需要安裝)。不過,享有完整的 CUDA 支援,可充分運用顯示卡層級較低的功能,加快訓練和推論速度。效能與 Python TensorFlow 實作不相上下,因為兩者都共用相同的 C++ 後端。

型號大小

針對研究提供的先進模型,您可能需要處理非常大型的模型 (可能多達 GB)。由於每個瀏覽器分頁的記憶體用量限制,這些模型目前無法在網路瀏覽器中執行。如要執行這類大型模型,您可以在自己的伺服器上使用 Node.js,同時將必要的硬體規格設為有效率地執行這類模型。

IOT

Node.js 支援熱門的單板電腦 (例如 Raspberry Pi),因此你也可以在這類裝置上執行 TensorFlow.js 模型。

速度

Node.js 是以 JavaScript 編寫,因此只需進行時間編譯即可。這表示您在使用 Node.js 時,通常可以發現效能提升,因為 Node.js 會在執行階段進行最佳化,特別是您執行的任何預先處理作業。透過這份個案研究,您可以看出 Hugging Face 如何運用 Node.js,讓自然語言處理模型的成效提升 2 倍。

現在您已瞭解 TensorFlow.js 的基本功能、可在何處執行,以及一些優點,讓我們開始運用 TensorFlow.js 有效執行各項工作了!

3. 預先訓練模型

TensorFlow.js 提供各種預先訓練的機器學習 (ML) 模型。這些模型由 TensorFlow.js 團隊訓練而成,內含簡單易用的類別,非常適合運用機器學習技術,順利踏出第一步。您可以選擇匯入預先訓練模型做為起點,不必建構及訓練模型。

我們在 Tensorflow.js JavaScript 模型頁面中,找到了簡單易用的預先訓練模型清單,內容持續增加。你也可以從其他位置取得轉換後的 TensorFlow 模型,可以使用 TensorFlow.js,包括 TensorFlow Hub

為什麼我要使用預先訓練模型?

如果能滿足您的需求,就可以從熱門的預先訓練模型著手許多優點,例如:

  1. 不必自行收集訓練資料。準備資料的格式正確並加上標籤,機器學習系統才能從中學習,可能相當耗時且成本高昂。
  2. 可快速設計構想的原型,節省成本和時間。
    「重新發明方向盤」並沒有什麼意義當預先訓練模型能滿足您的需求時,您就能全力運用模型提供的資訊來實現您的創意。
  3. 運用最先進的研究。預先訓練模型通常是參考熱門研究結果,因此您可以接觸到這類模型,同時瞭解這些模型在現實中的成效。
  4. 易用性與詳盡的說明文件。由於這類模型的人氣很高。
  5. 遷移學習 功能:某些預先訓練模型提供遷移學習功能,基本上就是將從某項機器學習任務學到的資訊,轉移到另一個類似的範例。舉例來說,假設某個模型原本經過訓練,能夠辨識貓,但只要提供新的訓練資料,就可以重新訓練以辨識狗的身分。這麼做的速度更快,因為您一開始就不會從空白的畫布開始。這種模型可透過已學到的知識辨識貓,然後識別新的東西。狗畢竟還是眼睛和耳朵一樣,因此知道如何找到這些特徵時,已經有一半了。以更快的速度重新訓練模型。

什麼是 COCO-SSD?

COCO-SSD 是您在本程式碼研究室中使用的預先訓練物件偵測機器學習模型名稱,可協助您在單一映像檔中本地化及識別多個物件。也就是說,此工具可以告訴您我們的模型經過訓練,找出物件的定界框,以便在您展示的任何圖片中呈現該物件的位置。範例如下圖所示:

760e5f87c335dd9e.png

如果上圖中有超過 1 隻狗,系統會提供 2 個定界框的座標,讓您知道每個定點的位置。COCO-SSD 經過預先訓練,可辨識 90 種常見日常物體,例如人群、汽車、貓等。

姓名來源為何?

這個名稱聽起來可能怪異,但取自 2 個縮寫:

  • COCO:是指對 COCO (情境中的常見物件) 資料集上訓練的事實,所有人都能免費下載並使用該資料集來訓練自己的模型。資料集內含超過 20 萬張已加上標籤的圖片,可供您從中學習。
  • SSD (單鏡頭 MultiBox 偵測):代表模型實作中模型架構的一部分,你在本程式碼研究室中並不需要瞭解這一點,但如果你想瞭解 SSD,可以在此進一步瞭解 SSD

4. 做好準備

軟硬體需求

  • 新型網路瀏覽器。
  • 具備 HTML、CSS、JavaScript 和 Chrome 開發人員工具 (查看控制台輸出內容) 的基本知識。

開始編寫程式碼

已為 Glitch.comCodepen.io 建立範本範本。只要按一下滑鼠,即可複製任一範本做為本程式碼研究室的基礎狀態。

在 Glitch 上按一下「重混這個」按鈕,即可建立分支,並建立一組可編輯的檔案。

你也可以在 Codepen 中,按一下畫面右下方的「fork」

這個簡易架構提供下列檔案:

  • HTML 網頁 (index.html)
  • 樣式表 (style.css)
  • 要編寫 JavaScript 程式碼 (script.js) 的檔案

為方便起見,我們在 TensorFlow.js 程式庫的 HTML 檔案中新增了匯入作業。這是訂閱按鈕的圖示:

index.html

<!-- Import TensorFlow.js library -->
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs/dist/tf.min.js" type="text/javascript"></script>

替代做法:使用偏好的網頁編輯器或在本機環境中運作

如想下載程式碼並透過本機或其他線上編輯器執行工作,只要在同一目錄中建立上述 3 個檔案,然後複製 Glitch 樣板的程式碼,然後貼到各個檔案中即可。

5. 填入 HTML 架構

所有原型都需要一些基本的 HTML 鷹架。您稍後會使用此程式碼轉譯機器學習模型的輸出內容。現在開始設定:

  • 網頁標題
  • 一些說明文字
  • 用於啟用網路攝影機的按鈕
  • 顯示網路攝影機串流至

如要設定這些功能,請開啟 index.html,並貼上現有的程式碼:

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Multiple object detection using pre trained model in TensorFlow.js</title>
    <meta charset="utf-8">
    <!-- Import the webpage's stylesheet -->
    <link rel="stylesheet" href="style.css">
  </head>  
  <body>
    <h1>Multiple object detection using pre trained model in TensorFlow.js</h1>

    <p>Wait for the model to load before clicking the button to enable the webcam - at which point it will become visible to use.</p>
    
    <section id="demos" class="invisible">

      <p>Hold some objects up close to your webcam to get a real-time classification! When ready click "enable webcam" below and accept access to the webcam when the browser asks (check the top left of your window)</p>
      
      <div id="liveView" class="camView">
        <button id="webcamButton">Enable Webcam</button>
        <video id="webcam" autoplay muted width="640" height="480"></video>
      </div>
    </section>

    <!-- Import TensorFlow.js library -->
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs/dist/tf.min.js" type="text/javascript"></script>
    <!-- Load the coco-ssd model to use to recognize things in images -->
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/coco-ssd"></script>
    
    <!-- Import the page's JavaScript to do some stuff -->
    <script src="script.js" defer></script>
  </body>
</html>

瞭解程式碼

請留意您新增的幾項重要功能:

  • 您為標題新增了 <h1> 標記和一些 <p> 標記,並說明如何使用這個頁面。這裡沒有什麼特別之處。

您也新增了區段標記來代表您的展示空間:

index.html

    <section id="demos" class="invisible">

      <p>Hold some objects up close to your webcam to get a real-time classification! When ready click "enable webcam" below and accept access to the webcam when the browser asks (check the top left of your window)</p>
      
      <div id="liveView" class="webcam">
        <button id="webcamButton">Enable Webcam</button>
        <video id="webcam" autoplay width="640" height="480"></video>
      </div>
    </section>
  • 一開始,您要將此 section 的類別設為「隱藏」。如此一來,您就可以在模型準備就緒時,以視覺化的方式向使用者說明,您可以放心點選「啟用網路攝影機」按鈕。
  • 您加入了「啟用網路攝影機」按鈕,方便您在 CSS 中設定樣式。
  • 您也新增了影片標記,用於串流播放網路攝影機輸入。您很快就會在 JavaScript 程式碼中設定這項功能。

如果您現在預覽輸出內容,看起來應像這樣:

b1bfb8c3de68845c.png

6. 新增樣式

元素預設值

首先,我們來為剛新增的 HTML 元素新增樣式,確保這些元素能正確顯示:

style.css

body {
  font-family: helvetica, arial, sans-serif;
  margin: 2em;
  color: #3D3D3D;
}

h1 {
  font-style: italic;
  color: #FF6F00;
}

video {
  display: block;
}

section {
  opacity: 1;
  transition: opacity 500ms ease-in-out;
}

接下來,請新增一些實用的 CSS 類別,以利處理使用者介面的各種狀態,例如當您想隱藏按鈕時,或在模型尚未準備就緒時,設為無法使用示範區域。

style.css

.removed {
  display: none;
}

.invisible {
  opacity: 0.2;
}

.camView {
  position: relative;
  float: left;
  width: calc(100% - 20px);
  margin: 10px;
  cursor: pointer;
}

.camView p {
  position: absolute;
  padding: 5px;
  background-color: rgba(255, 111, 0, 0.85);
  color: #FFF;
  border: 1px dashed rgba(255, 255, 255, 0.7);
  z-index: 2;
  font-size: 12px;
}

.highlighter {
  background: rgba(0, 255, 0, 0.25);
  border: 1px dashed #fff;
  z-index: 1;
  position: absolute;
}

太好了!這樣就大功告成了。如果使用上述 2 段程式碼成功覆寫樣式,即時預覽現在應如下所示:

336899a78cf80fcb.png

請注意,示範區域文字和按鈕無法使用的情況,因為 HTML 的預設類別為「invisible」已套用。模型準備就緒後,您將使用 JavaScript 移除此類別。

7. 建立 JavaScript 架構

參照主要 DOM 元素

首先,請確定您可以存取稍後在下列程式碼中修改或存取的網頁關鍵部分:

script.js

const video = document.getElementById('webcam');
const liveView = document.getElementById('liveView');
const demosSection = document.getElementById('demos');
const enableWebcamButton = document.getElementById('webcamButton');

檢查網路攝影機支援情形

您現在可以新增一些輔助函式,檢查目前使用的瀏覽器是否支援透過 getUserMedia 存取網路攝影機串流:

script.js

// Check if webcam access is supported.
function getUserMediaSupported() {
  return !!(navigator.mediaDevices &&
    navigator.mediaDevices.getUserMedia);
}

// If webcam supported, add event listener to button for when user
// wants to activate it to call enableCam function which we will 
// define in the next step.
if (getUserMediaSupported()) {
  enableWebcamButton.addEventListener('click', enableCam);
} else {
  console.warn('getUserMedia() is not supported by your browser');
}

// Placeholder function for next step. Paste over this in the next step.
function enableCam(event) {
}

擷取網路攝影機串流資料

接著請複製及貼上以下程式碼,為上方定義的先前空白 enableCam 函式填入程式碼:

script.js

// Enable the live webcam view and start classification.
function enableCam(event) {
  // Only continue if the COCO-SSD has finished loading.
  if (!model) {
    return;
  }
  
  // Hide the button once clicked.
  event.target.classList.add('removed');  
  
  // getUsermedia parameters to force video but not audio.
  const constraints = {
    video: true
  };

  // Activate the webcam stream.
  navigator.mediaDevices.getUserMedia(constraints).then(function(stream) {
    video.srcObject = stream;
    video.addEventListener('loadeddata', predictWebcam);
  });
}

最後,請新增一些臨時驗證碼,以便測試網路攝影機是否能正常運作。

下列程式碼會假設模型已載入並啟用相機按鈕,讓您可以點選該模型。您會在下一個步驟中替換所有這段程式碼,因此請稍後再次刪除程式碼:

script.js

// Placeholder function for next step.
function predictWebcam() {
}

// Pretend model has loaded so we can try out the webcam code.
var model = true;
demosSection.classList.remove('invisible');

太好了!如果您執行程式碼並點選按鈕,畫面目前顯示如下:

95442d7227216528.jpeg

8. 機器學習模型使用情形

載入模型

您現在可以載入 COCO-SSD 模型了。

完成初始化後,請啟用網頁上的示範區域和按鈕 (請將這段程式碼貼到您在最後一個步驟最後新增的暫時程式碼上方):

script.js

// Store the resulting model in the global scope of our app.
var model = undefined;

// Before we can use COCO-SSD class we must wait for it to finish
// loading. Machine Learning models can be large and take a moment 
// to get everything needed to run.
// Note: cocoSsd is an external object loaded from our index.html
// script tag import so ignore any warning in Glitch.
cocoSsd.load().then(function (loadedModel) {
  model = loadedModel;
  // Show demo section now model is ready to use.
  demosSection.classList.remove('invisible');
});

加入上述程式碼並重新整理即時檢視畫面後,會發現頁面載入後幾秒 (視網路速度而定),當模型可供使用時,「啟用網路攝影機」按鈕會自動顯示。不過,您同時也貼上了 predictWebcam 函式。現在請完整定義這個程式碼,因為我們的程式碼目前不會執行任何操作。

進行下一個步驟!

使用網路攝影機分類畫面

請執行以下程式碼,讓應用程式在瀏覽器準備就緒時,持續從網路攝影機串流中擷取影格,並傳遞給要分類的模型。

模型會剖析結果,並在返回的座標繪製 <p> 標記,並將文字設為物件標籤 (如果圖片的信賴水準超過一定程度)。

script.js

var children = [];

function predictWebcam() {
  // Now let's start classifying a frame in the stream.
  model.detect(video).then(function (predictions) {
    // Remove any highlighting we did previous frame.
    for (let i = 0; i < children.length; i++) {
      liveView.removeChild(children[i]);
    }
    children.splice(0);
    
    // Now lets loop through predictions and draw them to the live view if
    // they have a high confidence score.
    for (let n = 0; n < predictions.length; n++) {
      // If we are over 66% sure we are sure we classified it right, draw it!
      if (predictions[n].score > 0.66) {
        const p = document.createElement('p');
        p.innerText = predictions[n].class  + ' - with ' 
            + Math.round(parseFloat(predictions[n].score) * 100) 
            + '% confidence.';
        p.style = 'margin-left: ' + predictions[n].bbox[0] + 'px; margin-top: '
            + (predictions[n].bbox[1] - 10) + 'px; width: ' 
            + (predictions[n].bbox[2] - 10) + 'px; top: 0; left: 0;';

        const highlighter = document.createElement('div');
        highlighter.setAttribute('class', 'highlighter');
        highlighter.style = 'left: ' + predictions[n].bbox[0] + 'px; top: '
            + predictions[n].bbox[1] + 'px; width: ' 
            + predictions[n].bbox[2] + 'px; height: '
            + predictions[n].bbox[3] + 'px;';

        liveView.appendChild(highlighter);
        liveView.appendChild(p);
        children.push(highlighter);
        children.push(p);
      }
    }
    
    // Call this function again to keep predicting when the browser is ready.
    window.requestAnimationFrame(predictWebcam);
  });
}

在這個新程式碼中,最重要的呼叫是 model.detect()

TensorFlow.js 的所有預先建立模型都有類似這樣的函式 (名稱可能因模型而不同,因此請參閱說明文件瞭解詳細資料),實際執行機器學習推論作業。

推論只是由系統擷取輸入內容,並透過機器學習模型 (基本上為許多數學運算) 執行,然後提供結果。透過 TensorFlow.js 預先建立的模型,我們會以 JSON 物件的形式傳回預測結果,因此很容易使用。

如需這個預測函式的完整詳細資料,請參閱 COCO-SSD 模型的 GitHub 說明文件。這項功能可以在幕後執行大量繁重的作業:可接受任何「例如」這類圖片做為參數,例如圖像、影片、畫布等使用預建模型可節省許多時間和精力,因為您不需要自行編寫這類程式碼,而且可直接執行。

執行這個程式碼現在應該會產生如下的映像檔:

8f9bad6e49e646b.png

最後,下方是同時偵測多個物件的程式碼範例:

a2c73a72cf976b22.jpeg

太厲害了!各位現在想像一下,使用舊手機建立 Nest Cam 之類的裝置,在沙發上偵測到狗或你的貓時提醒你,其實相當簡單。如果程式碼發生問題,請按這裡查看最終的有效版本,確認是否複製有誤。

9. 恭喜

恭喜!您已經踏出在網路瀏覽器中使用 TensorFlow.js 和機器學習的第一步!接下來,我們將帶您一探究竟,並轉化為創意。你要製作什麼內容?

重點回顧

在本程式碼研究室中,我們:

  • 瞭解相較於其他形式的 TensorFlow,使用 TensorFlow.js 的優點。
  • 瞭解您需要先使用預先訓練的機器學習模型在什麼情況下開始採用。
  • 建立完全正常運作的網頁,可透過網路攝影機即時分類物件,包括:
  • 建立內容的 HTML 架構
  • 定義 HTML 元素和類別的樣式
  • 設置 JavaScript Scaffolding 以與 HTML 互動,並偵測網路攝影機是否存在
  • 載入預先訓練的 TensorFlow.js 模型
  • 使用已載入的模型建立網路攝影機串流的連續分類,並在圖片中的物件周圍繪製定界框。

後續步驟

分享您的作品!也能輕鬆將您在本程式碼研究室建立的內容擴充到其他創意用途。建議你跳脫傳統思維,畢竟最終是駭客攻擊。

(您也可以加入簡單的伺服器端層,讓其他裝置在偵測到您選擇的特定物件時使用 websocket 傳送通知,以傳送通知到其他裝置)。這是升級舊智慧型手機,賦予新用途的好方法。一切都無限可能!)

  • 在社群媒體上標記我們,並加上 #MadeWithTFJS 主題標記,你的專案就有機會登上我們的 TensorFlow 網誌,甚至在日後的 TensorFlow 活動中亮相。

更多 TensorFlow.js 程式碼研究室可深入探索

建議結帳網站