TensorFlow模型部署到服务器---TensorFlow2.0
前言
当一个TensorFlow模型训练出来的时候,为了投入到实际应用,所以就需要部署到服务器上。由于我本次所做的项目是一个javaweb的图像识别项目。所有我就想去寻找一下java调用TensorFlow训练模型的办法。
由于TensorFlow很久没更新的缘故,网上的博客大都是18/19年的,并且是基于TensorFlow1.0的,对于现在使用的TensorFlow2.0不太友好。
下面我简述一下TensorFlow1.0时期的方法:
1.动态模型生成不便
需要将训练的.h5模型转换成.pb模型,并且需要自己定义.pb模型的输入输出参数。(pb模型是一种基于动态图的模型)
pb的生成代码冗长、而且对初学者真滴不太友好
相比之下.h5模型的生成代码就一行
此外,这个生成pb模型的代码是否能照搬使用,还是一个问题,并且还可能报一些奇奇怪怪的错误。
2.maven导包不便
查阅资料发现java上的TensorFlow的jar包都是TensorFlow1.0的
现状:
并且maven官网上的TensorFlow2.0的api已经改名成了tensorflow-core-api,并且网上相关方面的教程十分难找。由于网上都是导入的1.0的包,自己导入2.0的包之后,详细的调用教程可以说是没有。从上面也可以看出来TensorFlow对java的调用也不怎么重视了。所以这又给学习的途中徒增了很多困难。
全新思路
思路一
用java直接调用训练好的模型很困难,那么我们想办法让java调用python脚本,让python脚本去调用.h5模型会不会更简单呢?
代码如下
package com.guard.service;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class api_service {
public String recognize(String path){
//此处的path是图片路径
Process proc;
String res = null;
try {
System.out.println("接受到的参数"+path);
String[] cmd = new String[] { "python", "E:\\machine_learning\\predict.py", path};
proc = Runtime.getRuntime().exec(cmd);
BufferedReader in = new BufferedReader(new InputStreamReader(proc.getInputStream()));
String line = null;
while ((line = in.readLine()) != null) {
System.out.println(line);
res = line;
}
in.close();
proc.waitFor();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(res+">>>>>>>>>>>");
return res;
}
}
但是我们可以看出,这个其实是用java在win上跑了这样一个指令
虽然这个确实是一个好办法,但是这个路径参数需要事先知道服务器上的路径,并且在协作开发的时候,每个人的路径和环境就不同,虽然该方法能用,但是我认为还不够好。
思路二
我们可以直接用python的flask框架,直接生成一个api接口,就可以远程直接调用TensorFlow训练好的模型进行结果预测。
个人认为,这种方法相较于用java调用命令行,这种方法还是更加直观的
并且flask仅仅需要加个@app.route的注解就能实现,可谓是十分方便
下面是模型调用代码
model.py
import glob
import sys
import os
import cv2
import numpy as np
import tensorflow as tf
import image_processing
def model_ues(path):
# 缩放图片大小为100*100
w = 100
h = 100
# 测试图像的地址 (改为自己的)
# path_test = "resource/test24.jpg"
api_token = "fklasjfljasdlkfjlasjflasjfljhasdljflsdjflkjsadljfljsda"
path_test = image_processing.download_img(path,api_token)
# 创建保存图像的空列表
imgs = []
img = cv2.imread(path_test)
img = cv2.resize(img, (w, h))
# 将每张经过处理的图像数据保存在之前创建的imgs空列表当中
imgs.append(img)
imgs = np.asarray(imgs, np.float32)
# print("shape of data:",imgs.shape)
# 导入模型
model = tf.keras.models.load_model(r"resource/rice_0.93.h5")
# 创建图像标签列表
rice_dict = {0: 'Rice blast', 1: 'Rice fleck',
2: 'Rice koji disease', 3: 'Sheath blight'}
# 将图像导入模型进行预测
prediction = model.predict_classes(imgs)
# prediction = np.argmax(model.predict(imgs), axis=-1)
# 绘制预测图像
for i in range(np.size(prediction)):
# 打印每张图像的预测结果
print(rice_dict[prediction[i]])
return rice_dict[prediction[0]]
为了实现图片外链接受,下面是图片下载脚本
image_processing.py
# coding: utf8
import requests
import random
def download_img(img_url, api_token):
print (img_url)
header = {"Authorization": "Bearer " + api_token} # 设置http header,视情况加需要的条目,这里的token是用来鉴权的一种方式
r = requests.get(img_url, headers=header, stream=True)
print(r.status_code) # 返回状态码
file_img = 'resource/img.png'
# file_img = 'resource/'
print(file_img)
if r.status_code == 200:
open(file_img, 'wb').write(r.content) # 将内容写入图片
print("done")
del r
return file_img
# if __name__ == '__main__':
# # 下载要的图片
# img_url = "https://z3.ax1x.com/2021/07/27/W5l6Qe.png"
# api_token = "fklasjfljasdlkfjlasjflasjfljhasdljflsdjflkjsadljfljsda"
# download_img(img_url, api_token)
主程序脚本
app.py
from flask import Flask,render_template, url_for, request, json,jsonify
import model
app = Flask(__name__)
#设置编码
app.config['JSON_AS_ASCII'] = False
@app.route('/test')
def hello_world():
return "hello world"
@app.route('/predict', methods=['GET', 'POST'])
def form_data():
my_path = request.form['path']
print(my_path)
str = model.model_ues(my_path)
print("http://127.0.0.1:5000/predict")
return jsonify({'result':str,'msg':'200'})
if __name__ == '__main__':
app.run()
数据解析
虽然我们能够通过postman进行测试接受到回传的结果,但是我们要怎么用java实现呢??
1.使用postman生成大致代码框架(postman生成的代码可能不能直接运行)
这里我选用的是java-okhttp的方法,但其实使用Unirest写出来的代码更加简洁易懂。
public class Get_result {
public String getResult(String path) throws IOException {
// String path = "https://i.loli.net/2021/07/29/badDNR2OCironUf.jpg";
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
RequestBody body = RequestBody.create(mediaType, "path="+path);
Request request = new Request.Builder()
.url("http://127.0.0.1:8000/predict")
.method("POST", body)
.addHeader("Content-Type", "application/x-www-form-urlencoded")
.build();
Response response = client.newCall(request).execute();
String result = response.body().string();
System.out.println(result);
}
}
{
"msg": "200",
"result": "Rice fleck"
}
获取到json数据之后,就需要对json数据进行解析
java上的解析原理是,先按照json编写一个类,之后用Gson对接受到的数据按照这个类进行规范化
(这里可以用GsonFormatPlus插件来自动生成这个实体类)
//Rice_result.java---为该json的实体类
package com.guard.tool;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@Data
public class Rice_result {
private String msg;
private String result;
}
下面是数据解析代码(和上面的okhttp获取json数据的代码连起来看)
//json数据解析
Gson gson = new Gson();
java.lang.reflect.Type type = new TypeToken<Rice_result>(){}.getType();
Rice_result rice_result = gson.fromJson(result, type);
System.out.println(rice_result);
if("200".equals(rice_result.getMsg())){
// System.out.println(rice_result.getResult());
return Rice_result.convertdata(rice_result.getResult());
}else {
// System.out.println("获取结果出错!!");
return "获取结果出错!!";
}
这样的话就可以进行json数据的解析了。
图链制作
由于需要使用java发送post请求给flask的预测端口,那么就需要把本地上传的数据做成图链,把图链作为数据传给flask的预测端口,从而来接收结果。
由于前端js的知识大多遗忘,这里就选用了用java来发送一个post请求,获得回传的信息。
这里我使用的是sm.ms的图床(该图床无需登录,且速度快,算得上是一个好的选择)
//sm.ms的使用方法,建议看官方文档
package com.guard.tool;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import okhttp3.*;
import java.io.File;
import java.io.IOException;
public class CloudUpload {
public String toUrl(String path) throws IOException {
// String file_path = "E:/machine_learning/test8.jpg";
String file_path = path;
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
MediaType mediaType = MediaType.parse("multipart/form-data");
RequestBody body = new MultipartBody.Builder().setType(MultipartBody.FORM)
.addFormDataPart("smfile",file_path,
RequestBody.create(MediaType.parse("application/octet-stream"),
new File(file_path)))
.addFormDataPart("format","json")
.build();
Request request = new Request.Builder()
.url("https://sm.ms/api/v2/upload")
.method("POST", body)
.addHeader("Content-Type", "multipart/form-data")
.addHeader("Authorization", "TlxzRSaVJj0o7HFZOd9sgdf4Jl60RA00")
//这里的user-agent和Cookie需要自己打开网站,到网站的页面去拿取
.addHeader("user-agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36")
.addHeader("Cookie", "SMMSrememberme=42417%3A10e8e9cb5281082b493fdee73381aeb2dca0bd3d; PHPSESSID=1gjog2em3ogof23vrqi79vd41m; SM_FC=runWNk3mPIiL8mzl%2FrlEfzM940LRKjLm182cm2qDrm4%3D")
.build();
Response response = client.newCall(request).execute();
String result = response.body().string();
System.out.println(result);
// String result = response.body().string();
Gson gson = new Gson();
java.lang.reflect.Type type = new TypeToken<Image_data>(){}.getType();
Image_data imge_data = gson.fromJson(result, type);
System.out.println(imge_data);
if (imge_data.getSuccess()){
System.out.println(imge_data.getData().getUrl());
return imge_data.getData().getUrl();
}
else{
System.out.println("图片已经上传过一次!!");
System.out.println(imge_data.getImages());
return imge_data.getImages();
}
}
}
回传的json结果--这个就需要使用上面的插件来进行处理
{
"success": true,
"code": "success",
"message": "Upload success.",
"data": {
"file_id": 0,
"width": 192,
"height": 454,
"filename": "test25.jpg",
"storename": "xICPNzFsfth5uJk.png",
"size": 124993,
"path": "/2021/08/01/xICPNzFsfth5uJk.png",
"hash": "2exIdQGvBru46RKMyNjg3DhCTO",
"url": "https://i.loli.net/2021/08/01/xICPNzFsfth5uJk.png",
"delete": "https://sm.ms/delete/2exIdQGvBru46RKMyNjg3DhCTO",
"page": "https://sm.ms/image/xICPNzFsfth5uJk"
},
"RequestId": "9BFE9DEB-8370-44C8-A8AF-AAB2DB753A18"
}
总结
以上就是我这次在小组编写<基于CNN图像分类的水稻病虫害识别>这个项目中的收获。在此记录下学习路上踩过的一些坑和一些解决方法。
TensorFlow模型部署到服务器---TensorFlow2.0的更多相关文章
- 【tensorflow-转载】tensorflow模型部署系列
参考 1. tensorflow模型部署系列: 完
- 移动端目标识别(1)——使用TensorFlow Lite将tensorflow模型部署到移动端(ssd)之TensorFlow Lite简介
平时工作就是做深度学习,但是深度学习没有落地就是比较虚,目前在移动端或嵌入式端应用的比较实际,也了解到目前主要有 caffe2,腾讯ncnn,tensorflow,因为工作用tensorflow比较多 ...
- 移动端目标识别(2)——使用TENSORFLOW LITE将TENSORFLOW模型部署到移动端(SSD)之TF Lite Developer Guide
TF Lite开发人员指南 目录: 1 选择一个模型 使用一个预训练模型 使用自己的数据集重新训练inception-V3,MovileNet 训练自己的模型 2 转换模型格式 转换tf.GraphD ...
- 移动端目标识别(3)——使用TensorFlow Lite将tensorflow模型部署到移动端(ssd)之Running on mobile with TensorFlow Lite (写的很乱,回头更新一个简洁的版本)
承接移动端目标识别(2) 使用TensorFlow Lite在移动设备上运行 在本节中,我们将向您展示如何使用TensorFlow Lite获得更小的模型,并允许您利用针对移动设备优化 ...
- 将训练好的Tensorflow模型部署到web应用中
做一个简易web使用Flask是最好的选择,不仅上手快,使用也很便利.Django很强大也很好用,但一次就会创建一个项目的所需的文件,我觉得对于测试一个模型在web端有没有效果没必要用它. flask ...
- 吴裕雄--天生自然python TensorFlow图片数据处理:解决TensorFlow2.0 module ‘tensorflow’ has no attribute ‘python_io’
tf.python_io出错 TensorFlow 2.0 中使用 Python_io 暂时使用如下指令: tf.compat.v1.python_io.TFRecordWriter(filename ...
- 一文上手TensorFlow2.0(一)
目录: Tensorflow2.0 介绍 Tensorflow 常见基本概念 从1.x 到2.0 的变化 Tensorflow2.0 的架构 Tensorflow2.0 的安装(CPU和GPU) Te ...
- 学习笔记TF022:产品环境模型部署、Docker镜像、Bazel工作区、导出模型、服务器、客户端
产品环境模型部署,创建简单Web APP,用户上传图像,运行Inception模型,实现图像自动分类. 搭建TensorFlow服务开发环境.安装Docker,https://docs.docker. ...
- tensorflow 模型保存与加载 和TensorFlow serving + grpc + docker项目部署
TensorFlow 模型保存与加载 TensorFlow中总共有两种保存和加载模型的方法.第一种是利用 tf.train.Saver() 来保存,第二种就是利用 SavedModel 来保存模型,接 ...
随机推荐
- Linux CentOS 配置Yaf框架
简介 Yaf框架想必大家都有所了解,它是一个开源的高性能的PHP框架 官网地址:https://www.php.net/manual/zh/book.yaf.php Yaf开发文档:https://w ...
- Linux分区,格式化概念理解
一.分区概念: 逻辑上分成不同的存储空间. 分区类型: 主分区:最多只能有4个 扩展分区:最多只能有1个. 主分区加扩展分区最多有4个. 布恩那个写入数据,只能包含逻辑分区 逻辑分区: 主分区为什么只 ...
- MEMORY_TARGET不够时解决办法
一.报错原因:设置的memory_max_target超过了系统中设置的share memory(/dev/shm)而已. 二.解决办法:增加tmpfs的大小 三.调整tmpfs的大小(两种方法) 1 ...
- Scala语言笔记 - 第二篇
目录 1 Map的基础操作 2 Map生成view和transform解析 最近研究了下scala语言,这个语言最强大的就是它强大的函数式编程(Function Programming)能力,记录 ...
- 『无为则无心』Python基础 — 11、Python中的数据类型转换
目录 1.为什么要进行数据类型转换 2.数据类型转换本质 3.数据类型转换用到的函数 4.常用数据类型转换的函数 (1)int()函数 (2)float()函数 (3)str()函数 (4)bool( ...
- ClickHouse学习系列之六【访问权限和账户管理】
背景 在之前写的文章[用户权限管理]里已经介绍了应该如何设置用户密码以及权限控制.但是只是针对修改配置文件的方式来进行用户权限管理,其实ClickHouse也支持基于RBAC(Role-Based A ...
- .NET Core/.NET5/.NET6 开源项目汇总10:实用工具
系列目录 [已更新最新开发文章,点击查看详细] 开源项目是众多组织与个人分享的组件或项目,作者付出的心血我们是无法体会的,所以首先大家要心存感激.尊重.请严格遵守每个项目的开源协议后再使用.尊 ...
- 关于Android Studio Emulator常见使用问题
Q:模拟器无法初始化声音相关设备 Emulator: dsound: Could not initialize DirectSoundCapture Emulator: dsound: Reason: ...
- 15、oracle多表查询
15.0.实验建表: --父表 create table class( id number(10)constraint class_id_pk primary key, class_name varc ...
- ORA-12560: 解决TNS:协议适配器错误
1)安装成功,但无法连接数据库 2)网上查找原因:32位的不能运行64位的oracle,而且不会有64位的版本 3)解决办法:大致是修改客户端数据库为32位的(此方法OK) (1)解压instantc ...