使用Tensorflow.js库在Node.js上进行机器学习

大家好,同事们!

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 Lite

Tensorflow 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') //   ( CPU) require('@tensorflow/tfjs-node') //    ( GPU) require('@tensorflow/tfjs-node-gpu') 

下载TensorFlow模型

TensorFlow.js提供了一个NPM库tfjs-models ),该可简化加载预训练和转换后的模型的过程,以对图像进行分类定义姿势检测k个最近的邻居

MobileNet的图像分类模型是一个经过深度训练的神经网络,可以区分1000种不同类别的图像

在项目的README文件中,以下代码用作示例 ,用于加载模型。

 import * as mobilenet from '@tensorflow-models/mobilenet'; //   const model = await mobilenet.load(); 

我碰到的第一个问题是该代码不适用于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://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 ... 

然后使用parallelcurl命令将所有这些文件下载到我的本地目录中。

 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'); //   const predictions = await model.classify(img); 

由于缺乏DOM支持,因此在Node.js中不起作用。

classify 方法接受各种DOM元素( canvasvideoimage ),并自动从这些元素中提取“图片”字节并将其转换为tf.Tensor3D类,用作模型输入。 或者,可以直接传输tf.Tensor3D

我决定不尝试使用外部软件包手动模拟DOM元素,但发现tf.Tensor3D易于手动组装

我们从图像生成Tensor3D

阅读用于将DOM元素转换为Tensor3D类方法的源代码 ,我们发现以下输入参数用于生成Tensor3D类。

 const values = new Int32Array(image.height * image.width * numChannels); //     ,    const outShape = [image.height, image.width, numChannels]; const input = tf.tensor3d(values, outShape, 'int32'); 

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/mobilenetMobileNet类中提供了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模型一起使用,以可视方式识别本地文件中的图像。

Source: https://habr.com/ru/post/zh-CN432984/


All Articles