tensorflow-serving docker模型部署(以mnist为例)

news2025/1/16 12:39:48

✨ 博客主页:小小马车夫的主页
✨ 所属专栏:Tensorflow

文章目录

  • 前言
  • 一、环境介绍
  • 二、tensorflow-serving docker安装
  • 三、单模型部署 (以官方demo saved_model_half_plus_two_cpu为例)
    • 1、docker模型部署
    • 2、python requests模型预测
  • 四、多模型部署 (以mnist为例)
    • 1、配置文件models.config
    • 2、docker模型部署
    • 3、python reqests预测
    • 4、gRPC预测
  • 五、查看tensorflow模型参数
    • 1、saved_model_cli
    • 2、获取模型metadata参数
  • 总结


前言

tensorflow模型训练出来要部署到生产环境,就需要模型预测框架,其中tensorflow-serving应用的比较多,下面就对tensorflow-serving docker部署作一个简要的介绍。


一、环境介绍

Ubuntu 18.04.64 LTS 64位 WSL
docker 20.10.21
Python 3.9.13
tensorflow 2.11.0
tensorflow-serving 2.5.1

二、tensorflow-serving docker安装

docker pull tensorflow/serving #下载最新tensorflow-serving
service docker start # 启动docker

说明
docker pull tensorflow/serving:2.5.1 指定版本下载,不指定则下载最新版本

三、单模型部署 (以官方demo saved_model_half_plus_two_cpu为例)

tensorflow-serving源码中有很多官方训练好的模型,这里以saved_model_half_plus_two_cpu为例作介绍。
tensorflow-serving git地址:https://github.com/tensorflow/serving

模型目录结构如下:
saved_model_half_plus_two_cpu

1、docker模型部署

docker run -p 8501:8501 \
  --mount type=bind,\
source=/mnt/d/projects/Tests/tensorflow/serving/tensorflow_serving/servables/tensorflow/testdata/saved_model_half_plus_two_cpu,\
target=/tensorflow/models/half_plus_two \
  -e MODEL_NAME=half_plus_two -t tensorflow/serving &

说明:
-p 8501:8501 指要映射的端口,将容器8501端口映射到系统8501端口,8501是tensorflow-serving的http服务端口,用于提供RESTful服务
source 模型的绝对路径,要到模型目录版本号的上一级
target 模型挂载到docker容器中的目录
-e 用于传递环境变量, 这里是MODEL_NAME=half_plus_two, 此处批模型的别名
-t 指定挂载到目标容器

2、python requests模型预测

import requests
import json
pdata={"instances":[1,2,3]}
param=json.dumps(pdata)
res=requests.post('http://192.168.2.110:8501/v1/models/half_plus_two:predict',data=param)
print(res.text)

输出:

{
    "predictions": [2.5, 3.0, 3.5
    ]

四、多模型部署 (以mnist为例)

前面介绍了单模型部署的方法,如果有多个模型我们该怎么办,如果每加一个模型要新开一个容器,那样未免也太麻烦了,这里介绍一个一次部署多个模型的方法。

1、配置文件models.config

首先,要一个配置文件配置多个模型的信息,如下:

model_config_list:{
    
	config:{
		name:"half_plus_two"
		base_path:"/tensorflow/serving/tensorflow_serving/servables/tensorflow/testdata/saved_model_half_plus_two_cpu"
		model_platform:"tensorflow"
		model_version_policy {
			specific {
				versions: 123
			}
		}
	},
	config:{
		name:"mnist"
		base_path:"/tensorflow/models/mnist"
		model_platform:"tensorflow"
		model_version_policy {
			specific {
				versions: 1
			}
		}
	}
}

说明:
配置中有两个模型,一个前面介绍的half_plus_two,另一个是 mnist
name 模型名称
base_path 是一个相对路径
versions 指定版本

2、docker模型部署

docker run -p 8500:8500 -p 8501:8501 --mount type=bind,source=/mnt/d/projects/Tests/tensorflow/,target=/tensorflow   -t  tensorflow/serving --model_config_file=/tensorflow/models.config 2>&1 >/mnt/d/projects/Tests/tensorflow/model.log &

说明:
-p 8500:8500 -p 8501:8501 将容器端口8500、8501分别映射到系统的8500和8501端口,其中8501是tensorflow-serving的gRPC端口,8500是tensorflow-serving的RESTful端口
source 模型根目录
-t 指定挂载到目标容器
--model_config_file 指定模型配置文件
2>&1 >log 标准输出重定向到日志文件

3、python reqests预测

这里只演示mnist的预测,其他模型类似

import tensorflow as tf
from tensorflow import keras
from keras import layers,optimizers,datasets
import requests
import json
import numpy

#加载mnist数据集
(x,y),(x_val,y_val)=datasets.mnist.load_data()
print('datasets',x.shape,y.shape,x.min(),y.min())

#从数据集中取一个样本,作为预测使用
idx=1234
img=x_val[idx,:,:]
label=y_val[idx]
#样本图像数组重新定义shape
img=tf.cast(img.reshape(-1,784),tf.float32)
#tensor对象转换numpy数组
img=numpy.asarray(img)

#定义tensorflow-serving数据,其中signature_name和inputs为模型配置,具体内容需要查看模型内容,详见后续说明
pdata={"signature_name":"serving_default","inputs":{"dense_input":img.tolist()}}
param=json.dumps(pdata)
pdata['inputs']
header={"content-type":"application/json"}
#发送模型预测请求
res=requests.post("http://192.168.2.110:8501/v1/models/mnist:predict",data=param,headers=header)
print(res.json()['outputs'][0])
#获取预测结果
float_vals=numpy.array(res.json()['outputs'][0])
#预测结果是一个10个元素float, 每一个元素代表概率,其中概率最大的元素的下标就是本次预测的数字
prediction=numpy.argmax(float_vals)
print(prediction)
print(label)

输出:

datasets (60000, 28, 28) (60000,) 0 0
[12.0071669, 3.22835922, -12.5186815, -17.6188622, -13.0628424, -1.33576834, 9.98286, -6.54291344, 53.3233299, -14.6254959]
8
8

4、gRPC预测

import numpy
import grpc
import tensorflow as tf
from tensorflow_serving.apis import predict_pb2
from tensorflow_serving.apis import prediction_service_pb2_grpc
from keras.datasets import mnist
#加载mnist数据集
(x_train,y_train),(x_test,y_test)=mnist.load_data()

print(numpy.shape(x_train))

#从数据集中取一个样本,作为预测使用
idx=1234
img=x_val[idx,:,:]
label=y_val[idx]
#样本图像数组重新定义shape
img=tf.cast(img.reshape(-1,784),tf.float32)
#tensor对象转换numpy数组
img=numpy.asarray(img)

#定义tensorflow-srving服务基本参数
host='192.168.2.110'
port=8500 #对应gRPC端口
model_name='mnist' #模型名称
model_version=1
request_timeout=20
#tensorflow-serving gRPC 地址
url='%s:%s'%(host,port)

#转换样本图像数据类型
features_tensor_proto=tf.make_tensor_proto(img,dtype=tf.float32,shape=img.shape)

channel=grpc.insecure_channel(url)
stub=prediction_service_pb2_grpc.PredictionServiceStub(channel)
#创建预测请求对象
request=predict_pb2.PredictRequest()
#模型名称
request.model_spec.name=model_name
#模型版本
request.model_spec.version.value=model_version
#指定样本图像数据,dense_input为模型文件中指定,具体参数详见后续
request.inputs['dense_input'].CopyFrom(features_tensor_proto)
#指定签名
request.model_spec.signature_name='serving_default'

#开始预测
result=stub.Predict(request,request_timeout)
#获取预测结果,dense_2为模型中指定,模型参数模型详见后续
response=numpy.array(result.outputs['dense_2'].float_val)
#取概率最大值的下标作为预测数字
prediction=numpy.argmax(response)

print(result)
print(prediction)
print(label)

输出:

(60000, 28, 28)
(28, 28)
outputs {
  key: "dense_2"
  value {
    dtype: DT_FLOAT
    tensor_shape {
      dim {
        size: 1
      }
      dim {
        size: 10
      }
    }
    float_val: 0.07472114264965057
    float_val: 9.907261848449707
    float_val: -38.84326934814453
    float_val: 158.68698120117188
    float_val: -41.261207580566406
    float_val: -28.248952865600586
    float_val: -19.231698989868164
    float_val: -14.337512969970703
    float_val: -44.73484420776367
    float_val: 2.877924680709839
  }
}
model_spec {
  name: "minist"
  version {
    value: 1
  }
  signature_name: "serving_default"
}

3
3

五、查看tensorflow模型参数

经过前面的介绍,大家就对预测过程有所了解,其中比较关键的模型输入输出类型、签名等信息,下面介绍两种查看模型参数的方法。

1、saved_model_cli

saved_model_cli show --dir model-savedmodel --all

输出:

MetaGraphDef with tag-set: 'serve' contains the following SignatureDefs:

signature_def['__saved_model_init_op']:
  The given SavedModel SignatureDef contains the following input(s):
  The given SavedModel SignatureDef contains the following output(s):
    outputs['__saved_model_init_op'] tensor_info:
        dtype: DT_INVALID
        shape: unknown_rank
        name: NoOp
  Method name is: 

signature_def['serving_default']:
  The given SavedModel SignatureDef contains the following input(s):
    inputs['dense_input'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1, 784)
        name: serving_default_dense_input:0
  The given SavedModel SignatureDef contains the following output(s):
    outputs['dense_2'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1, 10)
        name: StatefulPartitionedCall:0
  Method name is: tensorflow/serving/predict
2022-11-24 14:55:37.147887: I tensorflow/core/platform/cpu_feature_guard.cc:193] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN) to use the following CPU instructions in performance-critical operations:  AVX2 FMA
To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags.

Concrete Functions:
  Function Name: '__call__'
    Option #1
      Callable with:
        Argument #1
          inputs: TensorSpec(shape=(None, 784), dtype=tf.float32, name='inputs')
        Argument #2
          DType: bool
          Value: False
        Argument #3
          DType: NoneType
          Value: None
    Option #2
      Callable with:
        Argument #1
          dense_input: TensorSpec(shape=(None, 784), dtype=tf.float32, name='dense_input')
        Argument #2
          DType: bool
          Value: False
        Argument #3
          DType: NoneType
          Value: None
    Option #3
      Callable with:
        Argument #1
          dense_input: TensorSpec(shape=(None, 784), dtype=tf.float32, name='dense_input')
        Argument #2
          DType: bool
          Value: True
        Argument #3
          DType: NoneType
          Value: None
    Option #4
      Callable with:
        Argument #1
          inputs: TensorSpec(shape=(None, 784), dtype=tf.float32, name='inputs')
        Argument #2
          DType: bool
          Value: True
        Argument #3
          DType: NoneType
          Value: None

  Function Name: '_default_save_signature'
    Option #1
      Callable with:
        Argument #1
          dense_input: TensorSpec(shape=(None, 784), dtype=tf.float32, name='dense_input')

  Function Name: 'call_and_return_all_conditional_losses'
    Option #1
      Callable with:
        Argument #1
          inputs: TensorSpec(shape=(None, 784), dtype=tf.float32, name='inputs')
        Argument #2
          DType: bool
          Value: True
        Argument #3
          DType: NoneType
          Value: None
    Option #2
      Callable with:
        Argument #1
          dense_input: TensorSpec(shape=(None, 784), dtype=tf.float32, name='dense_input')
        Argument #2
          DType: bool
          Value: True
        Argument #3
          DType: NoneType
          Value: None
    Option #3
      Callable with:
        Argument #1
          dense_input: TensorSpec(shape=(None, 784), dtype=tf.float32, name='dense_input')
        Argument #2
          DType: bool
          Value: False
        Argument #3
          DType: NoneType
          Value: None
    Option #4
      Callable with:
        Argument #1
          inputs: TensorSpec(shape=(None, 784), dtype=tf.float32, name='inputs')
        Argument #2
          DType: bool
          Value: False
        Argument #3
          DType: NoneType
          Value: None

2、获取模型metadata参数

metadata就是模型的基本信息,python requests方式获取metadata方法如下:

importrequests

res=requests.get("http://192.168.2.110:8501/v1/models/mnist/metadata")
print(res.text)

输出:

{
"model_spec":{
 "name": "minist",
 "signature_name": "",
 "version": "1"
}
,
"metadata": {"signature_def": {
 "signature_def": {
  "serving_default": {
   "inputs": {
    "dense_input": {
     "dtype": "DT_FLOAT",
     "tensor_shape": {
      "dim": [
       {
        "size": "-1",
        "name": ""
       },
       {
        "size": "784",
        "name": ""
       }
      ],
      "unknown_rank": false
     },
     "name": "serving_default_dense_input:0"
    }
   },
   "outputs": {
    "dense_2": {
     "dtype": "DT_FLOAT",
     "tensor_shape": {
      "dim": [
       {
        "size": "-1",
        "name": ""
       },
       {
        "size": "10",
        "name": ""
       }
      ],
      "unknown_rank": false
     },
     "name": "StatefulPartitionedCall:0"
    }
   },
   "method_name": "tensorflow/serving/predict"
  },
  "__saved_model_init_op": {
   "inputs": {},
   "outputs": {
    "__saved_model_init_op": {
     "dtype": "DT_INVALID",
     "tensor_shape": {
      "dim": [],
      "unknown_rank": true
     },
     "name": "NoOp"
    }
   },
   "method_name": ""
  }
 }
}
}
}

总结

本文主要介绍了docker下tensorflow-serving的安装、部署,以及预测相关的知识,具体如下:

  • tensorflow-serving docker镜像安装
  • tensorflow-serving单模型和多模型部署
  • tensorflow-serving模型预测方法,主要介绍python requests和gRPC两种方式
  • 两种查看tensorflow模型信息的方法

如果觉得有些帮助或觉得文章还不错,请关注一下博主,你的关注是我持续写作的动力。另外,如果有什么问题,可以在评论区留言,或者私信博主,博主看到后会第一时间进行回复。
【间歇性的努力和蒙混过日子,都是对之前努力的清零】
欢迎转载,转载请注明出处:https://blog.csdn.net/xxm524/article/details/128060790

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/41809.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

算法提升:图的拓扑排序算法

目录 概念 思路 代码 概念 拓扑序列:一些活动,其中某些活动必须在另一些活动完成之后才能开始,一定是无环的有向图,称为AOV网。 拓扑排序,其实就是对一个有向图构造拓扑序列的过程。构造时会有两个结果&#xff1a…

Go中赋值和转换关系

Go中的赋值跟类型转换: 在java中反射是可以获取继承关系,而go语言实际是不支持继承的,所以必须是相同的类型才能使用AssignableTo(),ConvertibleTo() package mainimport ("fmt""reflect" )type User struct {Name string } func demo(){user:User{Name:…

C#语言实例源码系列-实现自定义屏保

专栏分享点击跳转>Unity3D特效百例点击跳转>案例项目实战源码点击跳转>游戏脚本-辅助自动化点击跳转>Android控件全解手册 👉关于作者 众所周知,人生是一个漫长的流程,不断克服困难,不断反思前进的过程。在这个过程中…

Class文件结构

文章目录1.概述1.1 字节码文件的跨平台性1.2 Java的前端编译器1.3 透过字节码指令看代码细节2. 虚拟机的基石:Class文件3. Class文件结构3.1 魔数3.2 Class文件版本号3.3 常量池3.4 访问标识3.5 类索引、父类索引、接口索引集合3.6 字段表集合3.7 方法表集合3.8 属性表集合4. 使…

No.178# 混沌工程相关内容梳理

引言随着公司规模业务的快速增长,数以千计甚至万计的微服务,依赖的各类组件越来越多。分布式体系架构体系越来越复杂,没有任何一个人能够掌控所有复杂的耦合性。也就是说复杂性无法避免,不可能再回到单体应用,也无法彻…

西门子精彩触摸屏SMART V3组态配方的具体方法示例

西门子精彩触摸屏SMART V3组态配方的具体方法示例 本次和大家分享在精彩系列触摸屏中进行配方组态的具体方法,以下内容仅供大家参考: 如下图所示,首先,在连接中添加新的连接,这里以S7-200SMART为例,PLC和HMI的IP地址要设置在同一网段内, 如下图所示,在变量中,添加配…

【Java面试】来讲一讲你对String的理解

文章目录字符型常量和字符串常量的区别什么是字符串常量池?String 是最基本的数据类型吗String有哪些特性String为什么是不可变的?String真的是不可变的吗?String不可变的必要性是否可以继承 String 类数组有没有 length()方法?St…

windows线程 互斥锁CreateMutex、ReleaseMutex、CloseHandle

互斥 相关问题 多线程下代码或资源的共享使用。 互斥的使用 1.创建互斥 HANDLE CreateMutex( LPSECURITY_ATTRIBUTES lpMutexAttributes,//安全属性(废弃参数,置NULL) BOOL bInitialOwner,//初始的拥有者TRUE/FALSE LPCTSTR lpName //命名 );…

集成电路技术——如何制造芯片(1)

1.概述 电子工业是现在高新技术的核心,它在人类的科技发展中发挥了巨大作用,电子工业已经成为成为当今世界发展最快的高新技术产业,在全世界各国国民经济中起着举足轻重的作用。当今的电子技术离不开集成电路,集成电路是电子工业…

hadoop集群迁移

集群迁移 主要是要找到两个集群中active状态的namenode 集群迁移不同于服务器之间的文件发送,在hdfs中,文件是以块的形式,只可以通过namenode访问文件,所以迁移时需要通过hadoop命令 主要命令是distcp distcp有很多参数&#xf…

Kafka - 06 Kafka 集群环境搭建(三台服务器)

文章目录1. 克隆虚拟机2. Zookeeper 集群搭建3. Kafka 集群搭建4. 测试消息发送和消费1. 主题操作2. 生产者生产消息3. 消费者消费消息1. 克隆虚拟机 kafka集群搭建,需要3台虚拟机环境,但是我目前只安装了一台虚拟机,因此还需要准备两台虚拟…

[附源码]计算机毕业设计springboot-Steam游戏平台系统论文

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: SSM mybatis Maven Vue 等等组成,B/S模式 M…

纯CSS制作3D动态相册【流星雨3D旋转相册】HTML+CSS+JavaScriptHTML5七夕情人节表白网页制作

这是程序员表白系列中的100款网站表白之一,旨在让任何人都能使用并创建自己的表白网站给心爱的人看。 此波共有100个表白网站,可以任意修改和使用,很多人会希望向心爱的男孩女孩告白,生性腼腆的人即使那个TA站在眼前都不敢向前表白…

MySQL数据库管理及用户管理以及数据库用户授权

MySQL数据库管理及用户管理以及数据库用户授权MySQL数据库管理1、库和表2、常用的数据类型查看数据库结构SQL语句1、SQL语句分类:创建及删除数据库和表管理表中的数据记录(表数据的增删改查)向数据表中插入新的数据记录(增&#x…

大二Web课程设计——动漫海贼王(可以很好的应付老师的作业)

HTML实例网页代码, 本实例适合于初学HTML的同学。该实例里面有设置了css的样式设置,有div的样式格局,这个实例比较全面,有助于同学的学习,本文将介绍如何通过从头开始设计个人网站并将其转换为代码的过程来实践设计。 精彩专栏推荐&#x1f4…

Java基础:入门程序、常量、变量

第一章 HelloWorld入门程序 1.1 程序开发步骤说明 Java程序开发三步骤:编写、编译、运行。 1.2 编写Java源程序 在D:\java\javaCode目录下新建文本文件,完整的文件名修改为HelloWorld.java,其中文件名为 HelloWorld,后 缀名必须…

音视频技术开发周刊 | 273

每周一期,纵览音视频技术领域的干货。新闻投稿:contributelivevideostack.com。「紧急通知」LiveVideoStackCon 2022 音视频技术大会北京站改期各位LVSer们:因疫情影响,北京近期不再允许举办大型线下活动,我们无奈且抱…

如何向瑞芯微平台添加驱动

如何向瑞芯微平台添加驱动驱动配置驱动文件放置配置相应的编译选项与设备树结合设备树配置以avafpga视频驱动为例:驱动配置 驱动文件放置 为了保证驱动文件能够被系统正确识别编译,需要将驱动文件的源文件avafpga.c放在./kernel/drivers/media/i2c/下 …

外观专利申请定要注意哪些因素呢?

问题一:申请外观专利需要提交哪些申请材料呢? 1、外观专利请求书; 2、外观设计七视图的照片或者设计图; 3、申请人的身份证明材料; 4、外观设计的简要说明材料。 问题二:外观专利申请定要注意哪些因素…

Automatic Online Calibration of Cameras and Lasers 论文翻译

目录题目AbstractI. INTRODUCTIONII. SENSOR PROCESSINGA. Image processingB. Laser processingIII. MISCALIBRATION DETECTIONIV. AUTOMATIC CALIBRATION TRACKINGV. EXPERIMENTAL RESULTSVI. CONCLUSION题目 Automatic Online Calibration of Cameras and Lasers 下载地址&…