js 创建file_用TensorFlow.js进行客户端预测

索引

  1. 介绍
  2. 创建一个模型
  3. 设置Flask服务器
  4. 创建index.html
  5. 创建main.js
  6. 更新Flask服务器

0. 导言

让我们想象一下,你已经创建了一些深奥的模型,它可以做一些伟大的事情,并帮助人们。比如说,这个模型通过杯子的照片来预测人们最喜欢的表情符号。你把这个模型放在一个web上,每天的使用量大概是1000次查询,不是很多。简单的服务器可以处理,但是有一天这个模型被大众发现了,你开始每天收到10万个查询,同样的服务器就会死。所以现在你可以选择扩大服务器规模,增加越来越多的内存,或者你可以尝试在客户端重写预测。如果你选择了第二种方案,这里有一个教程给你。 为了实现这个目标,我们将有以下组件

后端:Flask,任何你想用python对图像进行预处理的库。

前端: tensorflowjs

最近TensorflowJS增加了对node.js的支持,不过,我们将使用Flask,这是一个python库。通常情况下,有些训练好的模型需要对数据进行预处理才能正常执行。到目前为止,在python中的预处理比在javascript中更方便。(我希望有一天也能在客户端进行预处理)

1. 创建一个模型

你可以通过运行 train_model.py为 MNIST 训练模型,或者你可以创建并训练任何你想要的模型。这里很重要的一点是保存模型的拓扑结构和权重。如果你的模型是用keras写的,只需添加以下内容。

# Your training happens here# Hundreds and hundreds of layers# Stacked to create the greatest model ever# don't forget saving to json formatimport tensorflowjs as tfjstfjs.converters.save_keras_model(model, "model_js")

模型保存后,你会有一个文件夹,里面有以下内容

d5eaa96325366c11692d5e86b83e16ce.png

其中 group*-shard*of*--是二进制权重文件的集合和model.json--是模型的拓扑结构和配置。

2.设置Flask服务器

因为我们希望用户能够访问我们的模型。

from flask_cors import CORSfrom flask import Flask, render_templateapp = Flask(__name__)CORS(app)@app.route("/", methods=["GET"])def main():    return render_template('index.html')  if __name__ == "__main__":  app.run()

到目前为止,服务器很简单,没有什么复杂的东西,只有一条路径,返回静态页面index.html。

另外,系统的总体架构是这样的。

14b81f12c8ce06c3b6ecbd875959568b.png

3. 创建index.html

我们需要一些用户交互的入口点,并在那里运行我们的预测。因此,让我们来设置index.html.

        TF JS example    

我们的第一个版本会是这样的。这里唯一重要的事情是在第6行上通过 CDN添加了tensorflowjs。

下一步就是添加一些HTML主体,这样用户就可以上传图片和点击按钮了。:) 下面是这样的。

TensorflowJS client side prediction

When you first time press predict it will take more time, for model to load

       Choose files    Predict    Clear    

接下来,也是最后一步,也就是我们的HTML部分的,就是给我们的页面添加一些样式,从而给HTML元素带来类,同时创建main.js文件,这个文件将包含我们的神奇预测部分的。现在让我们来看看index.html.的最终版本吧

        TF JS example        

TensorflowJS client side prediction

When you first time press predict it will take more time, for model to load

       Choose files    Predict    Clear    

你的index.html可能和我的不一样,你也可以添加或删除其中的一些部分,不过,这里重要的是。

  • 第6行 (在头部添加了CDN的脚本--对于生产版的代码来说,这是一个不好的做法,不过,我不想麻烦大家解释什么是npmnode_modules)
  • 第13行(输入,如果你想让用户上传图片,也可以使用不同类型的输入,也可以使用不同类型的输入)
  • 第20行(我们的脚本做客户端的预测)

4. 创建main.js

现在是时候带来魔术了。首先我们需要初始化按钮、输入、模型预测功能

let model;const modelURL = 'http://localhost:5000/model';const preview = document.getElementById("preview");const predictButton = document.getElementById("predict");const clearButton = document.getElementById("clear");const numberOfFiles = document.getElementById("number-of-files");const fileInput = document.getElementById('file');const predict = async (modelURL) => {}

第3行我们有了我们的模型的URL,到目前为止,它是在我的本地机器上,但一般来说,它可以部署在任何地方。另外,稍后我们将在Flask中为这个模型创建一个路由。 现在,让我们添加一个部分来下载我们的模型,然后将用户上传的图片发送到服务器上进行预处理

let model;const modelURL = 'http://localhost:5000/model';const preview = document.getElementById("preview");const predictButton = document.getElementById("predict");const clearButton = document.getElementById("clear");const numberOfFiles = document.getElementById("number-of-files");const fileInput = document.getElementById('file');const predict = async (modelURL) => {    if (!model) model = await tf.loadModel(modelURL);    const files = fileInput.files;    [...files].map(async (img) => {        const data = new FormData();        data.append('file', img);        const processedImage = await fetch("/api/prepare",            {                method: 'POST',                body: data            }).then(response => {                return response.json();            }).then(result => {                return tf.tensor2d(result['image']);            });    })};

我们把图像发送到/api/prepare/,这个路由我们以后再添加。同时我们把服务器的响应从字段image到tf.tensor2d。 现在是时候为我们的张量添加预测,然后渲染预测和图像到用户的视图中。最后就是添加一些按钮例程和函数调用。 因此,最后一个版本的神奇脚本与客户端预测的神奇脚本会是这样的。

let model;const modelURL = 'http://localhost:5000/model';const preview = document.getElementById("preview");const predictButton = document.getElementById("predict");const clearButton = document.getElementById("clear");const numberOfFiles = document.getElementById("number-of-files");const fileInput = document.getElementById('file');const predict = async (modelURL) => {    if (!model) model = await tf.loadModel(modelURL);    const files = fileInput.files;    [...files].map(async (img) => {        const data = new FormData();        data.append('file', img);        const processedImage = await fetch("/api/prepare",            {                method: 'POST',                body: data            }).then(response => {                return response.json();            }).then(result => {                return tf.tensor2d(result['image']);            });        // shape has to be the same as it was for training of the model        const prediction = model.predict(tf.reshape(processedImage, shape = [1, 28, 28, 1]));        const label = prediction.argMax(axis = 1).get([0]);        renderImageLabel(img, label);    })};const renderImageLabel = (img, label) => {    const reader = new FileReader();    reader.onload = () => {        preview.innerHTML += `
                                                                           

${label}

                             
`;   };    reader.readAsDataURL(img);};fileInput.addEventListener("change", () => numberOfFiles.innerHTML = "Selected " + fileInput.files.length + " files", false);predictButton.addEventListener("click", () => predict(modelURL));clearButton.addEventListener("click", () => preview.innerHTML = "");

请随意重写fetch部分,以便在一次POST中发送完整的批文件。 不要忘了根据训练模型的形状来重塑返回的数组!

5. 更新Flask服务器

现在我们必须更新我们的服务器,这样它就可以为/api/prepare的图像做的预处理,同时也可以为/model的模型保存到前端。服务器的最终版本看起来类似于此。

from flask_cors import CORSfrom flask import Flask, request, render_template, json, jsonify, send_from_directoryimport jsonimport cv2import numpy as npimport io​app = Flask(__name__)CORS(app)​​@app.route("/", methods=["GET"])def main():    return render_template('index.html')​​@app.route("/api/prepare", methods=["POST"])def prepare():    file = request.files['file']    res = preprocessing(file)    return json.dumps({"image": res.tolist()})@app.route('/model')def model():    json_data = json.load(open("./model_js/model.json"))    return jsonify(json_data)@app.route('/')def load_shards(path):    return send_from_directory('model_js', path)def preprocessing(file):    in_memory_file = io.BytesIO()    file.save(in_memory_file)    data = np.fromstring(in_memory_file.getvalue(), dtype=np.uint8)    img = cv2.imdecode(data, 0)    res = cv2.resize(img, dsize=(28, 28), interpolation=cv2.INTER_CUBIC)    # file.save("static/UPLOAD/img.png") # saving uploaded img    # cv2.imwrite("static/UPLOAD/test.png", res) # saving processed image    return resif __name__ == "__main__":    app.run()

对于的预处理部分,我们有两个函数:

  • prepare(为/api/prepare调用)
  • preprocessing(取一张图片,返回调整后的图片到numpy数组,这个函数可以做任何你喜欢的预处理,只要返回numpy数组,一切都会正常工作。

模型部分:

  • model(调用/model)
  • load_shards(被调用的任何文件都是为/,这个函数是用来加载二进制权重文件的) 为什么我们需要2个函数和2个独立的API来服务一个模型,而不是一个?

在下面的tensorflowjs版本中,当我们为某个API加载模型时,model = await tf.loadModel(modelURL);

它首先从modelURL中加载模型,这是一个JSON文件,然后它会自动发送一些POSTs到domain根目录,以便加载shards(在演示中查看服务器日志中的POSTs)。因为我不想把模型和服务器一起放在根目录下,所以我需要两个函数,一个是model.json,另一个是shards

就是这样!现在你就可以把预测模型委托给我了。现在,你可以把预测人们最喜欢的表情符号通过杯子的照片委托给客户端,或者像我的案例中的MNIST一样,只需要在客户端进行预测。另外,如果一切正常,你会看到类似于这样的东西。

ea7b68cb4904d30354d2055ae209d3c6.gif
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值