使用 TensorFlow.js 部署模型
TensorFlow.js 介绍
TensorFlow.js 是一个 JavaScript 库,用于在浏览器或 Node.js 训练和部署机器学习模型。TensorFlow.js 的优点有:
不用安装驱动器和软件,通过链接即可分享程序。
网页应用,交互性强。
有访问 GPS,Camera,Microphone,Accelerator,Gyroscope 等传感器的标准 API。
安全性,因为数据都保存在客户端。
本节实验将学习 TensorFlow.js 基本语法,并部署 MobileNetV2 图像识别的应用。
在 JavaScript 项目中获取 TensorFlow.js 的主要方法有两种:
通过脚本标签导入:<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@0.9.0"> </script>。
使用 NPM 安装 TensorFlow.js:npm install @tensorflow/tfj。
在本节实验中我们主要使用通过脚本标签导入的方法。
在桌面下创建 HTML 文件 demo.html,我们将在此文件中介绍 TensorFlow.js 的核心概念。
张量
TensorFlow.js 的数据单元是张量(Tensor):一组数值存储于一维或者多维数组里。一个张量的实例有 shape 的属性用于构造多维数组。其中最主要的 Tensor 的构造函数是 tf.tensor ,同时为了方便书写,还有 tf.tensor2d,tf.tensor3d,tf.scalar,tf.zeros 等函数,这样也会增强代码的可读性。
<html>
<head>
<!-- 导入 TensorFlow.js -->
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@0.9.0"></script>
<!-- 在下方实现 TensorFlow.js 的代码 -->
<script>
const shape = [2, 3]; // 先定义张量的形状
const a = tf.tensor([1, 2, 3, 4, 5, 6], shape); //使用 tf.tensor 定义一个 2 * 3 的张量
a.print(); //在浏览器的控制台中打印结果
// 输出为:[[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 定义常数
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 打开。在界面任意位置点击右键,选择检查打开控制台,点击上边栏的 Console,就可以看到程序的输出了。
运算
使用张量去存储数据,那么运算(Operation)允许你去利用这些数据。TensorFlow.js 提供了一整套适用于线性代数和机器学习的操作函数。
<html>
<head>
<!-- 导入 TensorFlow.js -->
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@0.9.0"></script>
<!-- 在下方实现 TensorFlow.js 的代码 -->
<script>
const shape = [2, 3]; // 先定义张量的形状
const a = tf.tensor([1, 2, 3, 4, 5, 6], shape); //使用 tf.tensor 定义一个 2 * 3 的张量
a.print(); //在浏览器的控制台中打印结果
// 输出为:[[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 定义常数
c.print();
// 输出为:2
const a_plus_b = a.add(b); // 进行 a + b 的运算
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 也支持链式写法:
const a_sub_div_c = a.sub(c).div(c); // 进行 (a - c) / c 的运算
a_sub_div_c.print();
// 输出为:[[-0.5, 0, 0.5],
// [1, 1.5, 2]]
</script>
</head>
</html>
同样,选择 demo.html 文件,右键,选择 Open With,选择使用 Preview 打开。在界面任意位置点击右键,选择检查打开控制台,点击上边栏的 Console,就可以看到程序的输出了。
下面是视频演示:
https://labfile.oss.aliyuncs.com/courses/1435/4-1.mp4
模型转换
环境配置
使用一个 Web 服务器为模型文件提供服务时,需要将服务器配置为允许跨源资源共享(CORS), 以允许在 JavaScript 中提取文件。
进入之前实验所新建的虚拟环境(如果没有虚拟环境则需要先执行 virtualenv -p /usr/bin/python3.6 pyenv 进行安装),安装 TensorFlow.js 用于模型转换,安装 Flask-CORS 用于跨源资源共享。
$ . pyenv/bin/activate
$ pip install tensorflowjs==1.4.0 flask_cors==3.0.8
下载预训练模型,并将其放于 ~/.keras/models 目录下,如果实验了上节实验的保存环境,则可以跳过此步骤。
$ 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
转换 Keras 模型
在桌面创建文件脚本 convert.py,将 Keras 模型转换为 TensorFlow.js 需要的格式。
import tensorflowjs as tfjs
from tensorflow.keras.applications.mobilenet_v2 import MobileNetV2
# 初始化 Keras 的 MobileNet 模型,并导入 ImageNet 权重
model = MobileNetV2(weights='imagenet')
# 进行转换,将转换后的模型保存在 model 文件夹下
tfjs.converters.save_keras_model(model, 'model/')
在终端输入 python convert.py 执行转换程序。
开启 CORS
在桌面创建文件脚本 cors.py,CORS 功能将在此文件中实现。其中,我们初始化 CORS,以允许对所有路由上的所有域进行 CORS。
from flask import Flask
from flask_cors import CORS
# 创建 Flask 实例
app = Flask(__name__,
static_url_path='/model',
static_folder='model')
# 初始化 CORS,以允许对所有路由上的所有域进行 CORS
cors = CORS(app)
# 注册路由
@app.route("/")
def hello():
return "Hello Shiyanlou!"
# 开启服务
if __name__ == '__main__':
app.run(host='0.0.0.0', port='8080', debug=True)
在这个应用中,我们把 URL 路径 /model 映射到了文件目录 model(相对于本文件),外界通过 {host}/model 就能访问到 model 内的文件。
在终端输入 python cors.py 开启 CORS 服务。
此时点击 Web 服务,在浏览器地址栏里显示的 URL 就是 host 的地址,在这里域名部分 c8ce7fff3a04-service 每个人都不同,需要实际运行中进行替换。
这样我们就可以通过访问 https://c8ce7fff3a04-service.simplelab.cn/model 来访问文件。
使用 TensorFlow.js 进行预测
导入模型
在 TensorFlow.js 中导入模型的方法如下,即通过提供 model.json 文件的 URL 将模型加载到 TensorFlow.js 中。
const model = await tf.loadLayersModel('http://***/model.json');
在上述语句中关键词 await 的意思是等待,即需要等到模型加载完成后再执行后面的语句。同时 await 只能在 async 函数中使用,不能在常规函数中使用,不能工作在顶级作用域。
在桌面创建文件脚本 tfjs.html,在此文件中将实现 TensorFlow.js 的全部功能。
<html>
<!-- 导入 TensorFlow.js -->
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@1.2.8/dist/tf.min.js"></script>
<!-- 在下方实现 TensorFlow.js 的代码 -->
<script>
// 首先定义 model
let model;
// 定义异步函数 demo,在这个函数中执行所有处理
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]);
// 打印模型的预测结果
console.log(model.predict(batched).data());
};
// 运行 demo 函数
demo();
</script>
</html>
对结果进行预览
以 Chrome 浏览器为例:选择 tfjs.html 文件,右键,选择 Open With,选择使用 Preview 打开。在界面任意位置点击右键,选择检查打开控制台,点击上边栏的 console 就可以看到模型输出了。
https://labfile.oss.aliyuncs.com/courses/1435/4-2.mp4
导入图片
接下来我们导入之前实验中使用的图片进行预测,为方便操作,我们直接通过终端下载图片和 ImageNet 对应的类别标签。
新开一个终端窗口,在此终端中下载文件。
$ wget https://labfile.oss.aliyuncs.com/courses/1435/image.jpg
$ wget https://labfile.oss.aliyuncs.com/courses/1435/imagenet_classes.js
接下来,我们在 tfjs.html 文件中导入图片,并进行预测,输出结果。
<html>
<!-- 创建图片元素,设置 id 属性为 image,以供 js 读取调用 -->
<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>
<!-- 创建一个标题元素,用于显示预测结果 -->
<h5 id="output">正在预测...</h5>
<script type="module">
// 从 imagenet_classes.js 中获取标签列表
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');
// 将图片元素转换为 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]);
// 进行预测
const pred = model.predict(batched);
// 获取预测结果最大值所在索引
const index = await tf.argMax(pred, 1).data();
// 从 IMAGENET_CLASSES 获取所对应的标签
const label = IMAGENET_CLASSES[index];
// 将标签输出到 h5 元素中显示
document.getElementById('output').innerHTML = label;
};
// 运行 demo 函数
demo();
</script>
</html>
对结果进行预览
选择 tfjs.html 文件,右键,选择 Open With,选择使用 Preview 打开。
https://labfile.oss.aliyuncs.com/courses/1435/4-3.mp4