Machine Learning với Nodejs kết hợp TensorFlow.js

Bài viết được sự cho phép của tác giả Sơn Dương

TensorFlow.js là một phiên bản của thư viện machine learning TensorFlow nổi tiếng, giúp mang deep learning tới thế giới Javascript. Giờ đây, bạn có thể define, train, và chạy Machine Learning với Nodejs.

Pre-trained models nghĩa là giờ các bạn có thể dễ dàng thực hiện các tác vụ phức tạp như nhận dạng khuôn mặt, sáng tạo nhạc, phát hiện tư thế 96 hay 69… chỉ với vài dòng Javascript đơn giản.

TensorFlow.js ban đầu được phát triển như là một thư viện front-end dành cho trình duyệt. Sau đó, nó được cập nhật để hỗ trợ Node.Js. Điều này, cho phép bạn sử dụng TensorFlow.js cho các ứng dụng phía back-end mà không phải cần đến Python.

Nghe có vẻ hấp dẫn nhỉ? Chúng ta sẽ cùng nhau tìm hiểu Machine Learning với Nodejs sử dụng TensorFlow.js nhé!

Vẫn như mọi khi, để việc học đi đôi với thực hành. Chúng ta sẽ cùng nhau xây dựng một ứng dụng Machine Learning như sau:

Đầu bài: Sử dụng TensorFlow.js để thực hiện nhận dạng hình ảnh (visual recognition) trên hình ảnh bằng JavaScript, tất nhiên là với môi trường Node.js.

Nhưng, trước khi chúng ta đi vào viết code, mình cùng nhau tìm hiểu tổng quan về thư viện TensorFlow đã nhé!

Lưu ý: Bài viết về Machine Learning nên sẽ có nhiều thuật ngữ chuyên ngành như: Model, training, visual recognition, pre-trained models… Các bạn chịu khó tìm hiểu thêm nhé!
  Bạn biết gì về thuật toán Radix Sort trong JavaScript?
  5 điều gây rò rỉ bộ nhớ (memory leak) trong Node.js và cách khắc phục

#TensorFlow là gì?

TensorFlow
Thư viện TensorFlow chuyên về Machine Learning

TensorFlow là một thư viện mã nguồn mở (open-source) cho các ứng dụng machine learning. TensorFlow cũng có thể được sử dụng để implement các neural networks hay các thuật toán deep learning.

Được phát hành tháng 11, 2015, ban đầu TensoFlow là một thư viện Python. Nó được sử dụng khả năng tính toán của CPU và GPU để training và đánh giá các machine learing models. Và tất nhiên, thời điểm đó, nó được thiết để chạy trên các máy chủ có hiệu suất cực cao và đắt tiền.

Gần đây, TensorFlow đã thay đổi rất nhiều, khi nó có thể chạy trên các thiết bị có hiệu suất thấp hơn Mobile hay trình duyệt web.

1. TensorFlow Lite

Tensorflow Lite, là một phiên bản lightweight dành riêng cho các thiết bị di động như mobile hay các thiết bị nhúng.

Đi kèm với nó là một loạt các pre-trained deep learning models (các models được training sẵn từ trước, bạn chỉ việc sử dụng) cho các nhiệm vụ nhận dạng (vision recognition), người ta gọi là MobileNet.

MobileNet được thiết kế để hoạt động tốt và hiệu quả trên các thiết bị hạn chế về tài nguyên. Thế mới gọi là Lite, đúng không

Tham khảo tuyển dụng nodejs lương cao trên TopDevTopDev

2. TensorFlow.js

Theo sau Tensorflow Lite, TensorFlow.js mới được công bố vào tháng 3, 2018. Phiên bản này được phát triển để chạy trên trình duyệt, dựa trên một dự án trước đó là deeplearn.js.

Thông qua thư viện này, các nhà phát triển có thể sử dụng API Javascript để training, load, và chạy models.

TensorFlow.js gần đây có thể chạy trên Nodejs nhờ sử dụng module: tfjs-node

3. Import Models có sẵn vào TensorFlow.js

Như mình đã nói ở trên, đi kèm với TensorFlow.js, có rất nhiều models có sẵn, và bạn hoàn toàn có thể import vào dự án của mình.

Các models này cần phải convert sang định dạng phù hợp trước khi có thể chạy được.

Có một số models được training sẵn, thực hiện các tác vụ như:

  • Image classification
  • Pose detection
  • K-nearest neighbours

#Machine learning với Node.js kết hợp TensorFlow.js

Sau khi đã tìm hiểu tổng quan về thư viện TensorFlow rồi, chúng ta sẽ bắt tay vào việc tự xây dựng một ứng dụng machine learning với Nodejs đơn giản nhé.

1. Cài đặt thư viện TensorFlow

Bạn có thể cài đặt Tensorflow thông qua NPM.

npm install @tensorflow/tfjs @tensorflow/tfjs-node
// hoặc...
npm install @tensorflow/tfjs @tensorflow/tfjs-node-gpu

Trong đó tfjs-node-gpu là module nodejs có hỗ trợ GPU. Cả hai thư viện trên đều sử dụng native dependencie.

2. Loading thư viện TensorFlow

TensorFlow’s JavaScript API được exposed từ core library. Vì vậy , các module hỗ trợ Nodejs sẽ bị lộ các API không cần thiết.

const tf = require('@tensorflow/tfjs')
// Load the binding (CPU computation)
require('@tensorflow/tfjs-node')
// Or load the binding (GPU computation)
require('@tensorflow/tfjs-node-gpu')

3. Loading TensorFlow Models

TensorFlow.js cung cấp một NPM library (tfjs-models) để dễ dàng tải các model được training sẵn.

MobileNet model là model được dùng để phân loại hình ảnh, là một deep neural network trained được xác định dựa trên 1000 class khác nhau.

import * as mobilenet from '@tensorflow-models/mobilenet';

// Load the model.
const model = await mobilenet.load();

Nhưng đến đây thôi, bạn vẫn chưa  chạy được đâu, vì sẽ gặp lỗi:

Error: browserHTTPRequest is not supported outside the web browser.

Như mình đã nói ở trên, mobilenet không hoạt động trên NodeJs.

Bởi vì, thư viện mobilenet là một warraper của tf.Model class. Nên khi hàm load() được gọi, nó sẽ tự động tải các models từ một từ bên ngoài ( external source) thay vì các models đã import trong dự án.

Trong các module Nodejs chưa hỗ trợ việc import các model từ các request HTTP, mà chỉ hỗ trợ import thủ công mà thôi.

Sau khi đã hiểu rõ vấn đề, chúng ta sẽ khắc phục thôi.

const path = "mobilenet/model.json"
const mn = new mobilenet.MobileNet(1, 1);
mn.path = `file://${path}`
await mn.load()

4. MobileNet Models

Các models của TensorFlow.js bao gồm 2 tệp:

  • Configuration được lưu trữ dưới dạng JSON file.
  • Trọng số (weights) được lưu dưới dạng binary file.

Trong đó, Model weights thường được chia nhỏ thành nhiều files để trình duyệt có thể cache tốt hơn.

Nhìn vào log lúc tải tự động các mobileNet, bạn sẽ thấy được tải về từ địa chỉ sau:

https://storage.googleapis.com/tfjs-models/tfjs/mobilenet_v${version}_${alpha}_${size}/

Nhìn vào URL này bạn sẽ thấy rằng, khi tải các mobileNet, chúng sẽ tải chính xác version của model.

Khi bạn xem mã nguồn thì chỉ có MobileNet v1 được tải về bằng thư viện tenorflow-model / Mobilenet.

Tải Models thủ công

Bạn cần xác định version nào muốn tải về, kích thước kích hình ảnh (hay nói các khác chính cấu hình của model).

Ví dụ, với trường hợp của mình thì URL sẽ như sau:

https://storage.googleapis.com/tfjs-models/tfjs/mobilenet_v1_1.0_224/model.json

Khi file cấu hình JSON được tải xuống, bạn có thể sử dụng công cụ jq để parse các weight file names.

$ cat model.json | jq -r ".weightsManifest[].paths[0]"
group1-shard1of1
group2-shard1of1
group3-shard1of1
...

Tiếp theo, mình sử dụng công cụ sed để thêm các prefix vào URL để tải các weight files.

$ cat model.json | jq -r ".weightsManifest[].paths[0]" | sed 's/^/https:\/\/storage.googleapis.com\/tfjs-models\/tfjs\/mobilenet_v1_1.0_224\//'
https://storage.googleapis.com/tfjs-models/tfjs/mobilenet_v1_1.0_224/group1-shard1of1
https://storage.googleapis.com/tfjs-models/tfjs/mobilenet_v1_1.0_224/group2-shard1of1
https://storage.googleapis.com/tfjs-models/tfjs/mobilenet_v1_1.0_224/group3-shard1of1
.

Cuối cùng là sử dụng lệnh  parallel và curl, mình có thể download tất cả các files cần thiết về máy tính.

cat model.json | jq -r ".weightsManifest[].paths[0]" | sed 's/^/https:\/\/storage.googleapis.com\/tfjs-models\/tfjs\/mobilenet_v1_1.0_224\//' | parallel curl -O

5. Classifying Images

Đoạn code minh họa bên dưới được cung cấp bới TensorFlow.js, dùng để thể hiện các phân loại các kết quả trả về cho một hình ảnh.

const img = document.getElementById('img');

// Classify the image.
const predictions = await model.classify(img);
Lưu ý: Đoạn code này sẽ không hoạt động trên Nodejs vì thiếu các DOM.

Phương thức classify cho phép truyền vào các phần tử DOM (canvas, video, image), và tự động truy xuất, chuyển đổi cá bytes hình ảnh từ các thành phần này thành một lớp  tf.Tensor3D, được sử dụng làm đầu vào training cho model.

OK, để khắc phục lỗi không chạy trên Nodejs, chúng ta thay vì cố gắng fake các DOM trong Nodejs, mình sẽ tự xây dựng các tf.Tensor3D thủ công.

Generating Tensor3D từ một Image

Khi mình tham khảo source code, mình thấy hàm để biến DOM thành tf.Tensor3D class,  do vậy chúng ta cũng có thể học cách làm đó.

const values = new Int32Array(image.height * image.width * numChannels);
// fill pixels with pixel channel bytes from image
const outShape = [image.height, image.width, numChannels];
const input = tf.tensor3d(values, outShape, 'int32');

Trong đó:

  • pixels là mảng 2 chiều chứa danh sách các giá trị kênh cho mỗi pixel.
  • numChannels là số lượng các giá trị kênh trên mỗi pixel.

Tạo Input Values cho ảnh JPEG

jpeg-js library là một thư viện thuần javascript để mã hóa và giải mã JPEG cho Nodejs. Mình sẽ sử dụng thư viện này trích xuất giá trị RGB cho mỗi pixel.

const pixels = jpeg.decode(buffer, true);

Kết quả trả về là một Uint8Array, với bốn giá trị RGBA cho mỗi pixel. MobileNet Model chỉ sử dụng 3 giá trị RGB thôi, bỏ qua Alpha chanel.

Đoạn mã này giúp t chuyển đổi mảng 4 kênh (chanel) thành bản 3 kênh cho phù hợp với yêu cầu của MobileNet.

const numChannels = 3;
const numPixels = image.width * image.height;
const values = new Int32Array(numPixels * numChannels);

for (let i = 0; i < numPixels; i++) {
  for (let channel = 0; channel < numChannels; ++channel) {
    values[i * numChannels + channel] = pixels[i * 4 + channel];
  }
}

Những yêu cầu đầu vào của MobileNet model

Như mình đã download ở trên thì mình download MobileNet model để phân loại ảnh với kích thước width x height 224 pixel. Các Input tensors phải chứa giá trị trong dải từ -1 tới 1, và kiểu float, cho mỗi giá trị của kênh.

Giá trị đầu vào cho mỗi hình ảnh có kích thước khác nhau sẽ phải đặt lại kích thước khi phân loại. Ngoài ra, các giá trị pixel trong bộ giải mã JPEG nằm trong phạm vi 0 – 255, thay vì từ -1 đến 1. Các giá trị này cũng cần phải convert trước khi  classification.

TensorFlow.js có sẵn những hàm để bạn thực hiện convert dễ dàng, đừng quá lo lắng nhé. Đặc biệt hơn, tfjs-models/mobilenet library còn làm tự động những tác vụ convert đó nữa. Quá ngon!

Obtaining Predictions

MobileNet models đã được trained để có thể nhận biết được các thực thể từ 1000 classes trong bộ dữ liệu của ImageNet.

Output của model này sẽ xác suất chính xác mà mỗi thực thể đó có trong hình ảnh.

Bạn có thể tham khảo các thực thể mà MobileNet đã được trained để nhận biết tại đây.

MobileNet có cung cấp một hàm để bạn có thể  lấy một lớp X ( là một thực thể nào đó như: con gấu trúc, con gà, cột đèn…) có xác suất chính xác cao nhất mà xuất hiện trong hình ảnh.

const predictions = await mn_model.classify(input, 10);

Trong đó:

  • predictions là một mảng các lớp X và giá trị xác suất tương ứng.

Kiểu như sau:

{
 className: 'panda',
 probability: 0.9993536472320557
}

Chạy thử  với một ảnh bất kỳ

Ok, giờ chúng ta sẽ thử với một bức ảnh bất kỳ xem kết quả như thế nào nhé.

Machine Learning với Nodejs

Ví dụ, mình sẽ thử với ảnh con gấu trúc ở trên xem sao. Bạn chạy lênh bên dưới:

node script.js mobilenet/model.json panda.jpg

Kết quả trả về sẽ hiện trong console log như sau:

classification results: [ {
    className: 'giant panda, panda, panda bear, coon bear',
    probability: 0.9993536472320557
} ]

Như vậy, xác suất hình trên chụp con gấu trúc là 99,9999%. Chắc chắn là con gấu trúc rồi

#Tổng kết

Nói về lĩnh vực AI, machine learning thì nó vô vàn lắm. Đây là đang xu hướng công nghệ cực kỳ hot. Bài viết này mình chỉ hướng dẫn các bạn những hiểu biết cơ bản về TensorFlow. Cách sử dụng TensorFlow với NodeJs mà thôi.

Bài viết gốc được đăng tải tại vntalking.com

Có thể bạn quan tâm:

Hàng loạt việc làm IT hấp dẫn trên TopDev đang chờ bạn ứng tuyển