14.1 Tensorflow.js简介
TensorFlow.js是一个开源的基于硬件加速的JavaScript库,用于训练和部署机器学习模型。
14.1.1 Tensorflow.js的由来
对于JavaScript 的程序员来说,2018年注定是一个不平凡的一年,比如年初 Google 将其基于 JavaScript 技术的机器学习库 machinelearning.js 正式更名为 TensorFlow.js,这也意味着 Google 将 JavaScript 语言升级为其人工智能战略的重要环节。从此 JavaScript 有了自己的机器学习框架,赶上了人工智能的风口,再次华丽转身,而迁移学习能用 JavaScript 实现了。
TensorFlow.js是一个开源的基于 WebGL 硬件加速技术的 JavaScript 库,用于训练和部署机器学习模型,其设计理念借鉴于目前广受欢迎的 TensorFlow 深度学习框架。谷歌推出的第一个基于 TensorFlow 的前端深度学习框架是 deeplearning.js,使用 TypeScript 语言开发,2018 年 Google 将其重新命名为 TensorFlow.js,并在TypeScript内核的基础上增加了 JavaScript 的接口以及 TensorFlow 模型导入等工程,组成了TensorFlow.js深度学习框架。
14.1.2 Tensorflow.js的优点
Tensorflow.js基于浏览器和 Javascript,与其他深度学习框架相比具有以下优点。
(1)不用安装驱动器和软件,通过链接即可分享程序.随着互联的普及,浏览器是目前世界上被安装次数最多的软件工具,几乎在任何用户的设备中都会安装有浏览器并能运行JavaScript语言。
(2)网页应用交互性更强,在互联网时代,网页设计已经成为界面交互设计的标准,互联网公司开源了大量设计美观使用方便的 JavaScript 交互式设计,利用这些交互设计可以很方便得实现人与深度学习算法的交互。
(3)有直接访问 GPS 定位、摄像头、麦克风、加速度计、陀螺等传感器,以及各种其他设备的标准 API。随着手机的普及以及手机浏览器标准的完善,为作为浏览器端的 JavaScript 语言提供了跨平台的标准 API,大大方便了程序包括深度学习程序的开发。
(4)安全性,因为数据都是保存在客户端的,无须将训练数据上传到服务器端。因为基于 JavaScript 的深度学习完全运行于客户端浏览器,无须在服务器端干预,训练的数据(比如声音图像)都可以直接通过 JavaScript 的 API 获得,并利用浏览器的 WebGL 环境进行运算,完全不需要上传数据,保证了数据的安全,避免泄漏隐私数据。
14.1.3 安装Tensorflow.js
在JavaScript项目中,有两种安装TensorFlow.js的方法:一种是通过script标签引入,另外一种就是通过npm进行安装。
(1)通过JavaScript标签引入
通过使用如下脚本代码,可以将TensorFlow.js添加到我们的HTML文件中。
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@2.0.0/dist/tf.min.js"></script>
(2)从NPM安装
可以使用 npm cli 工具或 yarn安装 TensorFlow.js,具体命令如下:
yarn add @tensorflow/tfjs
或:
npm install @tensorflow/tfjs
14.1.4 平台和环境
TensorFlow.js 可以在浏览器和Node.js中运行,并且在两个平台中都具有许多不同的可用配置。每个平台都有一组影响应用开发方式的独特注意事项。在浏览器中,TensorFlow.js 支持移动设备以及桌面设备。每种设备都有一组特定的约束(例如可用 WebGL API),系统会自动为您确定和配置这些约束。
1. 环境
在执行TensorFlow.js程序时,将特定配置称为环境。环境由单个全局后端以及一组控制 TensorFlow.js细粒度功能的标记构成。
2. 后端
TensorFlow.js支持可实现张量存储和数学运算的多种不同后端,在任何给定时间内,均只有一个后端处于活动状态。在大多数情况下,TensorFlow.js会根据当前环境自动为您选择最佳后端。但是,有时必须要知道正在使用哪个后端以及如何进行切换。
要确定使用的后端,请运行以下代码:
console.log(tf.getBackend());
如果要手动更改后端,请运行以下代码:
tf.setBackend('cpu');
console.log(tf.getBackend());
(1)WebGL后端
WebGL后端 'webgl' 是当前适用于浏览器的功能最强大的后端,此后端的速度比普通 CPU 后端快 100 倍。张量将存储为 WebGL 纹理,而数学运算将在WebGL着色器中实现。在使用WebGL后端时需要了解如下所示的实用信息:
- 避免阻塞界面线程
当调用诸如 tf.matMul(a, b)等运算时,生成的 tf.Tensor会被同步返回,但是矩阵乘法计算实际上可能还未准备就绪,这意味着返回的 tf.Tensor只是计算的句柄。当调用x.data()或x.array()时,这些值将在计算实际完成时解析。这样,就必须对同步对应项 x.dataSync()和 x.arraySync()使用异步 x.data()和 x.array()方法,以避免在计算完成时阻塞界面线程。
- 内存管理
在使用WebGL后端时需要显式内存管理,浏览器不会自动回收 WebGLTexture(最终存储张量数据的位置)的垃圾。要想销毁 tf.Tensor的内存,可以使用dispose()方法实现:
const a = tf.tensor([[1, 2], [3, 4]]);
a.dispose();
在现实应用中将多个运算链接在一起的情形十分常见,保持对用于处置这些运算的所有中间变量的引用会降低代码的可读性。为了解决这个问题,TensorFlow.js 提供了tf.tidy()方法,可以清理执行函数后未被该函数返回的所有tf.Tensor,这类似于执行函数时清理局部变量的方式:
const a = tf.tensor([[1, 2], [3, 4]]);
const y = tf.tidy(() => {
const result = a.square().log().neg();
return result;
});
注意:在具有自动垃圾回收功能的非WebGL环境(例如 Node.js 或 CPU 后端)中使用 dispose() 或 tidy() 没有弊端。实际上,与自然发生垃圾回收相比,释放张量内存的性能可能会更胜一筹。
- 精度
在移动设备上,WebGL可能仅支持16位浮点纹理。但是,大多数机器学习模型都使用 32 位浮点权重和激活进行训练。这可能会导致为移动设备移植模型时出现精度问题,因为16位浮点数只能表示 [0.000000059605, 65504] 范围内的数字。这意味着应注意模型中的权重和激活不超出此范围。要想检查设备是否支持 32 位的纹理,需要检查 tf.ENV.getBool('WEBGL_RENDER_FLOAT32_CAPABLE') 的值,如果为 false,则设备仅支持 16 位浮点纹理。可以使用 tf.ENV.getBool('WEBGL_RENDER_FLOAT32_ENABLED')检查 TensorFlow.js当前是否使用 32 位纹理。
- 着色器编译和纹理上传
TensorFlow.js 通过运行WebGL着色器程序的方式在 GPU上执行运算,当用户要求执行运算时,这些着色器会迟缓地进行汇编和编译。着色器的编译在 CPU 主线程上进行,可能十分缓慢。TensorFlow.js 将自动缓存已编译的着色器,从而大幅加快第二次调用具有相同形状输入和输出张量的同一运算的速度。通常,TensorFlow.js 应用在应用生命周期内会多次使用同一运算,因此第二次通过机器学习模型的速度会大幅提高。
TensorFlow.js会将 tf.Tensor 数据存储为 WebGLTextures。在创建 tf.Tensor时不会立即将数据上传到GPU,而是将数据保留在CPU上,直到在运算中使用到 tf.Tensor 为止。当第二次使用 tf.Tensor 时,因为数据已位于 GPU上,所以不存在上传成本。在典型的机器学习模型中,这意味着在模型第一次预测期间会上传权重,而第二次通过模型则会快得多。
如果开发者在意通过模型或 TensorFlow.js 代码执行首次预测的性能,建议在使用实际数据之前先通过传递相同形状的输入张量来预热模型。例如:
const model = await tf.loadLayersModel(modelUrl);
//在使用真实数据之前预热模型
const warmupResult = model.predict(tf.zeros(inputShape));
warmupResult.dataSync();
warmupResult.dispose();
//这时第二个predict()会快得多
const result = model.predict(userData);
(2)Node.js TensorFlow后端
在TensorFlow Node.js 后端 'node' 中,使用 TensorFlow C API 来加速运算。这将在可用情况下使用计算机的可用硬件加速(例如 CUDA)。
在这个后端中,就像 WebGL 后端一样,运算会同步返回 tf.Tensor。但与 WebGL 后端不同的是,运算在返回张量之前就已完成。这意味着调用 tf.matMul(a, b) 将阻塞 UI 线程。因此,如果打算在生产应用中使用,则应在工作线程中运行 TensorFlow.js 以免阻塞主线程。
(3)WASM后端
TensorFlow.js提供了WebAssembly后端 (wasm),可以实现CPU加速功能,并且可以替代普通的 JavaScript CPU (cpu)和 WebGL加速(webgl)后端。用法如下:
//将后端设置为WASM并等待模块就绪。
tf.setBackend('wasm');
tf.ready().then(() => {...});
如果服务器在不同的路径上或以不同的名称提供“.wasm”文件,则需要在初始化后端前使用 setWasmPath。
注意:TensorFlow.js会为每个后端定义优先级并为给定环境自动选择支持程度最高的后端。要显式使用WASM后端,需要调用 tf.setBackend('wasm')函数实现。
(4)CPU后端
CPU后端 'cpu'是性能最低但最简单的后端,所有运算均在普通的JavaScript中实现,这使它们的可并行性较差,这些运算还会阻塞界面线程。CPU后端对于测试或在 WebGL不可用的设备上非常有用。
14.1.5 第一个TensorFlow.js程序
请看下面的实例文件js01.html,在网页中引入了TensorFlow.js。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>tensorflow</title>
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@0.9.0"></script>
</head>
<body>
<script>
console.log(tf);
</script>
</body>
</html>
在浏览器中运行上述HTML文件,然后在浏览器中打开console控制台,可以看到tensorflow.js中的对象,其中包含了很多个属性和方法。如图14-1所示。
图14-1 tensorflow.js中的对象