大家好,同事们!
Tensorflow库的粉丝也许已经在我们的预购中注意到了
这本书 ,他们也密切关注了浏览器中机器和深度学习的可能性,特别是因为
Francois Schollet本人
并没有忽略这个话题。 我们邀请对猫感兴趣的人,它告诉我们如何使用Tensorflow.js库识别图像。
TensorFlow.js是流行的开源库的新版本,该库通过深度学习功能丰富了JavaScript。 开发人员现在可以使用
高级库API定义,训练和运行模型。
借助
预先训练的模型,开发人员现在可以仅用几行JavaScript即可轻松解决复杂的任务,例如
模式识别 ,
音乐生成或
确定人员位置 。
Tensorflow.js最初是用于在浏览器中工作的前端库,但今年已添加了
对 Node.js的
实验性支持 。 因此,TensorFlow.js也可以在JavaScript后端应用程序中使用,这完全使我们免于诉诸Python。
阅读有关此库的信息后,我决定尝试执行一个简单的任务...使用来自Node.js的JavaScript时,使用TensorFlow.js来视觉识别图像上的图像
不幸的是,
文档和
代码示例主要描述了该库在浏览器中的使用,旨在简化编写时的预训练模型的加载和使用的
项目实用程序不支持Node.js。 我不得不花很多时间来很好地阅读该库的Typescript源代码。
但是,经过几天的苦战,我还是做到
了 ! 万岁!
在继续详细分析代码之前,让我们谈谈TensorFlow库的其他实现。张量流TensorFlow是一个用于机器学习应用程序的免费软件库。 TensorFlow可用于创建神经网络并实现其他深度学习算法。
这是Google在2015年11月发布的库,最初
是用Python编写的 。 为了训练和评估创建的模型,它使用CPU或GPU上的计算。 最初,创建该库是为了使用资源密集型GPU在高性能服务器上工作。
最近的更新使优化该库并在资源更有限的环境(例如在移动设备和Web浏览器上)中使用它成为可能。
TensorFlow LiteTensorflow Lite是该库针对移动设备和嵌入式系统的精简版,于2017年5月发布。 与之一起,为与模式识别相关的任务提供了一组新的预先训练的深度模型。 该集合称为
MobileNet 。 MobileNet模型是专为在资源有限的环境(例如移动设备)中高效运行而设计的。
TensorFlow.js
在Tensorflow Lite之后,TensorFlow.js于2018年3月发布。 该版本的库旨在在浏览器中运行,并且基于一个名为
deeplearn.js的早期项目。 WebGL提供对库的GPU访问。 开发人员使用JavaScript API训练,加载和运行模型。
后来TensorFlow.js扩展为可与Node.js
tfjs-node
使用,为此使用了
tfjs-node
库add tfjs-node
。
将现有模型导入TensorFlow.js可以使用TensorFlow.js库执行现成的TensorFlow和Keras模型。 在执行模型之前,必须使用
此工具将其转换为新格式。
Github上提供了用于图像分类,定义姿势和检测k最近邻的预训练和转换模型。
将TensorFlow.js与Node.js结合使用安装TensorFlow库
TensorFlow.js可以从
NPM注册表中安装。
npm install @tensorflow/tfjs @tensorflow/tfjs-node // ... npm install @tensorflow/tfjs @tensorflow/tfjs-node-gpu
Node.js的两个扩展都使用本机依赖项,这些依赖项将根据需要进行编译。
下载TensorFlow库Tensorflow
的JavaScript API是从核心库提供的。 支持Node.js的扩展模块中没有提供扩展API。
const tf = require('@tensorflow/tfjs')
下载TensorFlow模型TensorFlow.js提供了一个
NPM库 (
tfjs-models
),该
库可简化加载预训练和转换后的模型的过程,以
对图像进行
分类 ,
定义姿势并
检测k个最近的邻居 。
MobileNet的图像分类
模型是一个经过深度训练的神经网络,可以区分
1000种不同类别的图像 。
在项目的README文件中,以下代码用作
示例 ,用于加载模型。
import * as mobilenet from '@tensorflow-models/mobilenet';
我碰到的第一个问题是该代码不适用于Node.js。
Error: browserHTTPRequest is not supported outside the web browser.
在检查了
源代码之后 ,我们看到mobilenet库是
tf.Model
类的包装器。 调用时,
load()
方法自动下载位于外部HTTP地址的必要模型文件,并实例化TensorFlow模型。
在撰写本文时,Node.js扩展尚不支持用于动态模型检索的HTTP请求。 剩下的就是手动将模型加载到文件系统中。
但是,阅读了库的源代码后,我发现了一种解决方法...从文件系统下载模型如果MobileNet类是手动创建的,则无法调用模块的
load
方法,而是重写包含模型的HTTP地址的自动生成的变量
path
,并用文件系统中的本地路径替换该地址。 之后,在类实例中调用
load
方法时,将触发
文件系统加载器类; 在这种情况下,我们拒绝使用基于浏览器的HTTP下载器。
const path = "mobilenet/model.json" const mn = new mobilenet.MobileNet(1, 1); mn.path = `file://${path}` await mn.load()
太好了,一切正常!
但是模型文件从哪里来?
MobileNet模型TensorFlow.js的模型包含两种类型的文件:以JSON格式存储的模型配置文件和以二进制格式存储的模型权重。 模型权重通常分为许多部分,以优化浏览器缓存。
考虑了MobileNet模型的
自动下载代码后 ,我们看到这些模型,它们的配置和权重片段是从以下地址的公共容器中提取的。
https://storage.googleapis.com/tfjs-models/tfjs/mobilenet_v${version}_${alpha}_${size}/
URL中的模板参数描述了
此处列出的模型版本。 最终的分类精度也显示在同一页面上。
源代码表明,只能使用
tensorflow-models/mobilenet
下载MobileNet v1
tensorflow-models/mobilenet
。
HTTP提取代码从存储位置下载
model.json
文件,然后以引用的权重递归选择模型的所有片段。 这些是格式为
groupX-shard1of1
。
手动下载模型如果要将所有模型文件保存在文件系统中,可以执行以下操作:提取模型配置文件,解析配置文件中引用的所有加权文件的语法,然后手动下载每个加权文件。
我打算使用具有1.0的alpha值和224像素的图像的MobileNet V1模块 。 因此,我获得了模型配置文件的
以下URL 。
https://storage.googleapis.com/tfjs-models/tfjs/mobilenet_v1_1.0_224/model.json
在本地下载此文件后,您可以使用
jq
工具解析所有加权文件的名称。
$ cat model.json | jq -r ".weightsManifest[].paths[0]" group1-shard1of1 group2-shard1of1 group3-shard1of1 ...
使用
sed
工具,可以为每个HTTP元素的名称加上URL前缀,以为每个重量文件生成URL。
$ cat model.json | jq -r ".weightsManifest[].paths[0]" | sed 's/^/https:\/\/storage.googleapis.com\/tfjs-models\/tfjs\/mobilenet_v1_1.0_224\//' https:
然后使用
parallel
和
curl
命令将所有这些文件下载到我的本地目录中。
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
图片分类TensorFlow.js随附的
此示例代码演示了如何返回图像分类的结果。
const img = document.getElementById('img');
由于缺乏DOM支持,因此在Node.js中不起作用。
classify
方法接受各种DOM元素(
canvas
,
video
,
image
),并自动从这些元素中提取“图片”字节并将其转换为
tf.Tensor3D
类,用作模型输入。 或者,可以直接传输
tf.Tensor3D
。
我决定不尝试使用外部软件包手动模拟DOM元素,但发现tf.Tensor3D
易于手动组装 。
我们从图像生成Tensor3D阅读用于将DOM元素转换为Tensor3D类
的方法的
源代码 ,我们发现以下输入参数用于生成Tensor3D类。
const values = new Int32Array(image.height * image.width * numChannels);
pixels
是类型为
(Int32Array)
的二维数组,其中包含每个像素的通道值的顺序列表。
numChannels
是每个像素的通道值数量。
为JPEG创建输入值jpeg-js
库是使用纯JavaScript编写的Node.js的JPEG编码器/解码器。 使用此库,您可以提取每个像素的RGB值。
const pixels = jpeg.decode(buffer, true);
结果,我们得到一个Uint8Array,每个像素(
width * height
)具有四个通道值(
RGBA
)。 MobileNet模型仅使用三个颜色通道(
RGB
)进行分类,而忽略Alpha通道。 此代码将四通道数组转换为真正的三通道版本。
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]; } }
MobileNet模型的输入要求这里使用的
MobileNet模型将图像分为高和宽224个像素。 对于每个像素的三个通道值,输入张量必须包含介于-1到1范围内的浮点值。
分类前,必须将具有不同尺寸的图像的输入值转换为正确的尺寸。 另外,从JPEG解码器获得的像素值在0-255的范围内,而不是-1-1。在分类之前还需要转换这些值。
TensorFlow.js具有简化该过程的库方法,但更好的是,有一个
特殊的tfjs-models/mobilenet
可以自动解决此问题 !开发人员可以将类型为
int32
输入Tensor3D以及各种尺寸传递给
classify
方法,该方法将输入值转换为分类之前的正确格式。 也就是说,我们在这里无事可做。 太好了!
获得预测Tensorflow的MobileNet模型学习从
ImageNet数据
集中的
1000个最重要的类中识别对象。 在模型的输出处,给出概率值,这些值表征在分类图像中找到这些对象的机会。
此文件中包含使用的模型的训练有素类的完整列表 。
tfjs-models/mobilenet
在
MobileNet
类中提供了
classify
方法,该方法根据图片所示返回最有可能的类的前X个。
const predictions = await mn_model.classify(input, 10);
predictions
是X类别和概率的数组,格式如下。
{ className: 'panda', probability: 0.9993536472320557 }
例子因此,我们已经弄清楚了如何在Node.js中使用TensorFlow.js库和MobileNet模型,现在让我们看看该脚本如何将指定为命令行参数的图像分类。
源代码将此脚本文件和程序包描述符保存在本地文件中。
{ "name": "tf-js", "version": "1.0.0", "main": "script.js", "license": "MIT", "dependencies": { "@tensorflow-models/mobilenet": "^0.2.2", "@tensorflow/tfjs": "^0.12.3", "@tensorflow/tfjs-node": "^0.1.9", "jpeg-js": "^0.3.4" } }
const tf = require('@tensorflow/tfjs') const mobilenet = require('@tensorflow-models/mobilenet'); require('@tensorflow/tfjs-node') const fs = require('fs'); const jpeg = require('jpeg-js'); const NUMBER_OF_CHANNELS = 3 const readImage = path => { const buf = fs.readFileSync(path) const pixels = jpeg.decode(buf, true) return pixels } const imageByteArray = (image, numChannels) => { const pixels = image.data 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]; } } return values } const imageToInput = (image, numChannels) => { const values = imageByteArray(image, numChannels) const outShape = [image.height, image.width, numChannels]; const input = tf.tensor3d(values, outShape, 'int32'); return input } const loadModel = async path => { const mn = new mobilenet.MobileNet(1, 1); mn.path = `file://${path}` await mn.load() return mn } const classify = async (model, path) => { const image = readImage(path) const input = imageToInput(image, NUMBER_OF_CHANNELS) const mn_model = await loadModel(model) const predictions = await mn_model.classify(input) console.log('classification results:', predictions) } if (process.argv.length !== 4) throw new Error('incorrect arguments: node script.js <MODEL> <IMAGE_FILE>') classify(process.argv[2], process.argv[3])
测试中按照上述说明将模型文件下载到mobilenet目录。
使用NPM设置项目依赖项
npm install
下载样本JPEG文件进行分类
wget http://bit.ly/2JYSal9 -O panda.jpg

运行脚本,其参数将是模型文件和输入图像。
node script.js mobilenet/model.json panda.jpg
如果一切正常,则以下输出应出现在控制台中。
classification results: [ { className: 'giant panda, panda, panda bear, coon bear', probability: 0.9993536472320557 } ]
图片正确分类为包含熊猫的可能性为99.93%!
结论TensorFlow.js库为JavaScript开发人员提供了深度学习的机会。 通过将经过预训练的模型与TensorFlow.js库一起使用,您可以轻松地在JavaScript应用程序中构建新功能,从而以最少的精力和简洁的代码解决复杂的机器学习问题。
TensorFlow.js库的创建纯粹是为了在浏览器中工作,但现在它可以与Node.js交互,尽管并非所有工具和实用程序都支持此新运行时。 对该库进行了几天的修改后,我学会了将其与MobileNet模型一起使用,以可视方式识别本地文件中的图像。