使用 TensorFlow.js 部署模型
TensorFlow.js 介紹
TensorFlow.js 是一個 JavaScript 庫宋渔,用于在瀏覽器或 Node.js 訓(xùn)練和部署機(jī)器學(xué)習(xí)模型少态。TensorFlow.js 的優(yōu)點(diǎn)有:
不用安裝驅(qū)動器和軟件,通過鏈接即可分享程序递递。
網(wǎng)頁應(yīng)用客税,交互性強(qiáng)。
有訪問 GPS,Camera揖闸,Microphone,Accelerator料身,Gyroscope 等傳感器的標(biāo)準(zhǔn) API汤纸。
安全性,因?yàn)閿?shù)據(jù)都保存在客戶端芹血。
本節(jié)實(shí)驗(yàn)將學(xué)習(xí) TensorFlow.js 基本語法贮泞,并部署 MobileNetV2 圖像識別的應(yīng)用。
在 JavaScript 項(xiàng)目中獲取 TensorFlow.js 的主要方法有兩種:
通過腳本標(biāo)簽導(dǎo)入:<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@0.9.0"> </script>幔烛。
使用 NPM 安裝 TensorFlow.js:npm install @tensorflow/tfj啃擦。
在本節(jié)實(shí)驗(yàn)中我們主要使用通過腳本標(biāo)簽導(dǎo)入的方法。
在桌面下創(chuàng)建 HTML 文件 demo.html饿悬,我們將在此文件中介紹 TensorFlow.js 的核心概念令蛉。
張量
TensorFlow.js 的數(shù)據(jù)單元是張量(Tensor):一組數(shù)值存儲于一維或者多維數(shù)組里。一個張量的實(shí)例有 shape 的屬性用于構(gòu)造多維數(shù)組狡恬。其中最主要的 Tensor 的構(gòu)造函數(shù)是 tf.tensor 珠叔,同時為了方便書寫,還有 tf.tensor2d弟劲,tf.tensor3d祷安,tf.scalar,tf.zeros 等函數(shù)兔乞,這樣也會增強(qiáng)代碼的可讀性汇鞭。
<html>
<head>
<!-- 導(dǎo)入 TensorFlow.js -->
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@0.9.0"></script>
<!-- 在下方實(shí)現(xiàn) TensorFlow.js 的代碼 -->
<script>
const shape = [2, 3]; // 先定義張量的形狀
const a = tf.tensor([1, 2, 3, 4, 5, 6], shape); //使用 tf.tensor 定義一個 2 * 3 的張量
a.print(); //在瀏覽器的控制臺中打印結(jié)果
// 輸出為:[[1, 2, 3],
// [4, 5, 6]]
const b = tf.tensor2d([
[6, 5, 4],
[3, 2, 1],
]); //使用 tf.tensor2d 定義一個 2 * 3 的張量
b.print();
// 輸出為:[[6, 5, 4],
// [3, 2, 1]]
const c = tf.scalar(3.5); // 使用 tf.scalar 定義常數(shù)
c.print();
// 輸出為:3.5
const d = tf.zeros([2, 2]); // 使用 tf.zeros 定義一個 2 * 2凉唐, 值全為 0 的張量
d.print();
// 輸出為:[[0, 0],
// [0, 0]]
</script>
</head>
</html>
以 Chrome 瀏覽器為例:選擇 demo.html 文件,右鍵霍骄,選擇 Open With台囱,選擇使用 Preview 打開。在界面任意位置點(diǎn)擊右鍵腕巡,選擇檢查打開控制臺玄坦,點(diǎn)擊上邊欄的 Console,就可以看到程序的輸出了绘沉。
運(yùn)算
使用張量去存儲數(shù)據(jù)煎楣,那么運(yùn)算(Operation)允許你去利用這些數(shù)據(jù)。TensorFlow.js 提供了一整套適用于線性代數(shù)和機(jī)器學(xué)習(xí)的操作函數(shù)车伞。
<html>
<head>
<!-- 導(dǎo)入 TensorFlow.js -->
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@0.9.0"></script>
<!-- 在下方實(shí)現(xiàn) TensorFlow.js 的代碼 -->
<script>
const shape = [2, 3]; // 先定義張量的形狀
const a = tf.tensor([1, 2, 3, 4, 5, 6], shape); //使用 tf.tensor 定義一個 2 * 3 的張量
a.print(); //在瀏覽器的控制臺中打印結(jié)果
// 輸出為:[[1, 2, 3],
// [4, 5, 6]]
const b = tf.tensor2d([
[6, 5, 4],
[3, 2, 1],
]); //使用 tf.tensor2d 定義一個 2 * 3 的張量
b.print();
// 輸出為:[[6, 5, 4],
// [3, 2, 1]]
const c = tf.scalar(2.0); // 使用 tf.scalar 定義常數(shù)
c.print();
// 輸出為:2
const a_plus_b = a.add(b); // 進(jìn)行 a + b 的運(yùn)算
a_plus_b.print();
// 輸出為:[[7, 7, 7],
// [7, 7, 7]]
const reshape = a_plus_b.reshape([3, 2]); // 修改張量的形狀
reshape.print();
// 輸出為:[[7, 7],
// [7, 7],
// [7, 7]]
//同時 TensorFlow.js 也支持鏈?zhǔn)綄懛ǎ? const a_sub_div_c = a.sub(c).div(c); // 進(jìn)行 (a - c) / c 的運(yùn)算
a_sub_div_c.print();
// 輸出為:[[-0.5, 0, 0.5],
// [1, 1.5, 2]]
</script>
</head>
</html>
同樣择懂,選擇 demo.html 文件,右鍵另玖,選擇 Open With困曙,選擇使用 Preview 打開。在界面任意位置點(diǎn)擊右鍵谦去,選擇檢查打開控制臺慷丽,點(diǎn)擊上邊欄的 Console,就可以看到程序的輸出了鳄哭。
下面是視頻演示:
https://labfile.oss.aliyuncs.com/courses/1435/4-1.mp4
模型轉(zhuǎn)換
環(huán)境配置
使用一個 Web 服務(wù)器為模型文件提供服務(wù)時要糊,需要將服務(wù)器配置為允許跨源資源共享(CORS), 以允許在 JavaScript 中提取文件。
進(jìn)入之前實(shí)驗(yàn)所新建的虛擬環(huán)境(如果沒有虛擬環(huán)境則需要先執(zhí)行 virtualenv -p /usr/bin/python3.6 pyenv 進(jìn)行安裝)妆丘,安裝 TensorFlow.js 用于模型轉(zhuǎn)換锄俄,安裝 Flask-CORS 用于跨源資源共享。
$ . pyenv/bin/activate
$ pip install tensorflowjs==1.4.0 flask_cors==3.0.8
下載預(yù)訓(xùn)練模型勺拣,并將其放于 ~/.keras/models 目錄下奶赠,如果實(shí)驗(yàn)了上節(jié)實(shí)驗(yàn)的保存環(huán)境,則可以跳過此步驟药有。
$ wget https://labfile.oss.aliyuncs.com/courses/1435/mobilenet_v2_weights_tf_dim_ordering_tf_kernels_1.0_224.h5
$ mkdir -p ~/.keras/models
$ cp mobilenet_v2_weights_tf_dim_ordering_tf_kernels_1.0_224.h5 ~/.keras/models
轉(zhuǎn)換 Keras 模型
在桌面創(chuàng)建文件腳本 convert.py毅戈,將 Keras 模型轉(zhuǎn)換為 TensorFlow.js 需要的格式。
import tensorflowjs as tfjs
from tensorflow.keras.applications.mobilenet_v2 import MobileNetV2
# 初始化 Keras 的 MobileNet 模型愤惰,并導(dǎo)入 ImageNet 權(quán)重
model = MobileNetV2(weights='imagenet')
# 進(jìn)行轉(zhuǎn)換苇经,將轉(zhuǎn)換后的模型保存在 model 文件夾下
tfjs.converters.save_keras_model(model, 'model/')
在終端輸入 python convert.py 執(zhí)行轉(zhuǎn)換程序。
開啟 CORS
在桌面創(chuàng)建文件腳本 cors.py羊苟,CORS 功能將在此文件中實(shí)現(xiàn)。其中感憾,我們初始化 CORS蜡励,以允許對所有路由上的所有域進(jìn)行 CORS令花。
from flask import Flask
from flask_cors import CORS
# 創(chuàng)建 Flask 實(shí)例
app = Flask(__name__,
static_url_path='/model',
static_folder='model')
# 初始化 CORS,以允許對所有路由上的所有域進(jìn)行 CORS
cors = CORS(app)
# 注冊路由
@app.route("/")
def hello():
return "Hello Shiyanlou!"
# 開啟服務(wù)
if __name__ == '__main__':
app.run(host='0.0.0.0', port='8080', debug=True)
在這個應(yīng)用中凉倚,我們把 URL 路徑 /model 映射到了文件目錄 model(相對于本文件)兼都,外界通過 {host}/model 就能訪問到 model 內(nèi)的文件。
在終端輸入 python cors.py 開啟 CORS 服務(wù)稽寒。
此時點(diǎn)擊 Web 服務(wù)扮碧,在瀏覽器地址欄里顯示的 URL 就是 host 的地址,在這里域名部分 c8ce7fff3a04-service 每個人都不同杏糙,需要實(shí)際運(yùn)行中進(jìn)行替換慎王。
這樣我們就可以通過訪問 https://c8ce7fff3a04-service.simplelab.cn/model 來訪問文件。
使用 TensorFlow.js 進(jìn)行預(yù)測
導(dǎo)入模型
在 TensorFlow.js 中導(dǎo)入模型的方法如下宏侍,即通過提供 model.json 文件的 URL 將模型加載到 TensorFlow.js 中赖淤。
const model = await tf.loadLayersModel('http://***/model.json');
在上述語句中關(guān)鍵詞 await 的意思是等待,即需要等到模型加載完成后再執(zhí)行后面的語句谅河。同時 await 只能在 async 函數(shù)中使用咱旱,不能在常規(guī)函數(shù)中使用,不能工作在頂級作用域绷耍。
在桌面創(chuàng)建文件腳本 tfjs.html吐限,在此文件中將實(shí)現(xiàn) TensorFlow.js 的全部功能。
<html>
<!-- 導(dǎo)入 TensorFlow.js -->
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@1.2.8/dist/tf.min.js"></script>
<!-- 在下方實(shí)現(xiàn) TensorFlow.js 的代碼 -->
<script>
// 首先定義 model
let model;
// 定義異步函數(shù) demo褂始,在這個函數(shù)中執(zhí)行所有處理
const demo = async () => {
// 載入模型诸典,記得將域名替換為自己的
model = await tf.loadLayersModel(
'https://c8ce7fff3a04-service.simplelab.cn/model/model.json'
);
// 定義一個 1 * 224 * 224 * 3 的全 0 張量,用于測試模型是否可用
const batched = tf.zeros([1, 224, 224, 3]);
// 打印模型的預(yù)測結(jié)果
console.log(model.predict(batched).data());
};
// 運(yùn)行 demo 函數(shù)
demo();
</script>
</html>
對結(jié)果進(jìn)行預(yù)覽
以 Chrome 瀏覽器為例:選擇 tfjs.html 文件病袄,右鍵搂赋,選擇 Open With,選擇使用 Preview 打開益缠。在界面任意位置點(diǎn)擊右鍵脑奠,選擇檢查打開控制臺,點(diǎn)擊上邊欄的 console 就可以看到模型輸出了幅慌。
https://labfile.oss.aliyuncs.com/courses/1435/4-2.mp4
導(dǎo)入圖片
接下來我們導(dǎo)入之前實(shí)驗(yàn)中使用的圖片進(jìn)行預(yù)測宋欺,為方便操作,我們直接通過終端下載圖片和 ImageNet 對應(yīng)的類別標(biāo)簽胰伍。
新開一個終端窗口齿诞,在此終端中下載文件。
$ wget https://labfile.oss.aliyuncs.com/courses/1435/image.jpg
$ wget https://labfile.oss.aliyuncs.com/courses/1435/imagenet_classes.js
接下來骂租,我們在 tfjs.html 文件中導(dǎo)入圖片祷杈,并進(jìn)行預(yù)測,輸出結(jié)果渗饮。
<html>
<!-- 創(chuàng)建圖片元素但汞,設(shè)置 id 屬性為 image宿刮,以供 js 讀取調(diào)用 -->
<img src="image.jpg" id="image" width="224" height="224" />
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@1.2.8/dist/tf.min.js"></script>
<!-- 創(chuàng)建一個標(biāo)題元素,用于顯示預(yù)測結(jié)果 -->
<h5 id="output">正在預(yù)測...</h5>
<script type="module">
// 從 imagenet_classes.js 中獲取標(biāo)簽列表
import { IMAGENET_CLASSES } from './imagenet_classes.js';
let model;
const demo = async () => {
// 載入模型私蕾,記得將域名替換為自己的
model = await tf.loadLayersModel(
'https://c8ce7fff3a04-service.simplelab.cn/model/model.json'
);
// 通過 getElementById 獲取圖片元素
const imageElement = document.getElementById('image');
// 將圖片元素轉(zhuǎn)換為 float 格式
const img = tf.browser.fromPixels(imageElement).toFloat();
// 將圖片像素每個位置減去 127.5 再除以 127.5 以歸一化到 [-1, 1] 之間
const offset = tf.scalar(127.5);
const normalized = img.sub(offset).div(offset);
// 將歸一化后的圖片 reshape 到模型需要的輸入形狀
const batched = normalized.reshape([1, 224, 224, 3]);
// 進(jìn)行預(yù)測
const pred = model.predict(batched);
// 獲取預(yù)測結(jié)果最大值所在索引
const index = await tf.argMax(pred, 1).data();
// 從 IMAGENET_CLASSES 獲取所對應(yīng)的標(biāo)簽
const label = IMAGENET_CLASSES[index];
// 將標(biāo)簽輸出到 h5 元素中顯示
document.getElementById('output').innerHTML = label;
};
// 運(yùn)行 demo 函數(shù)
demo();
</script>
</html>
對結(jié)果進(jìn)行預(yù)覽
選擇 tfjs.html 文件僵缺,右鍵,選擇 Open With踩叭,選擇使用 Preview 打開磕潮。
https://labfile.oss.aliyuncs.com/courses/1435/4-3.mp4