ChatGPT对话:关于Tensorflow Serving的gRPC客户端

【编者按】已安装、配置好Tensorflow Seving,调试客户端代码,全程在ChatGPT支持下完成。
最后一个问题是关于gRPC和REST API区别,可以作为知识点学习。


1问:已编写Tensorflow serving客户端代码,要求读入图片文件请求服务器识别图片,返回结果,修改一下代码。(附源代码,此处不贴出,另:隐藏了真实IP)

ChatGPT
你的客户端代码中出现了 DNS 解析错误,这是因为 grpc.insecure_channel 方法只接受主机名或 IP 地址和端口号的形式,而不接受带有 http:// 前缀的 URL。

请修改以下部分:

原代码

channel = grpc.insecure_channel('http://IP:8501/')

修改后

channel = grpc.insecure_channel('IP:8501')

去掉 http:// 前缀后,再运行代码试试。

完整的修改后代码

import grpc
import tensorflow as tf
from tensorflow_serving.apis import predict_pb2, prediction_service_pb2_grpc
from google.protobuf import json_format
from PIL import Image
import numpy as np

# 定义读取和预处理图片的函数
def load_image(image_path):
    img = Image.open(image_path).resize((224, 224))
    img = np.array(img).astype(np.float32)
    img = np.expand_dims(img, axis=0)  # 扩展维度以符合模型输入
    return img

# 创建 gRPC 频道和 stub
channel = grpc.insecure_channel('IP:8501')
stub = prediction_service_pb2_grpc.PredictionServiceStub(channel)

# 创建预测请求
request = predict_pb2.PredictRequest()
request.model_spec.name = 'ai_flower'
request.model_spec.signature_name = 'serving_default'

# 读取并预处理图片
image_path = 'test_images/rose.jpg'
input_image = load_image(image_path)

# 将预处理后的图片设置为请求的输入张量
request.inputs['input_tensor_name'].CopyFrom(
    tf.make_tensor_proto(input_image, shape=input_image.shape))

# 发送请求并获取响应
response = stub.Predict(request)
print(response)

运行此代码,应该会解决 DNS 解析错误的问题。如果还有其他问题,随时告诉我!


2问:有错:Traceback (most recent call last):
File “E:\WorkspacePython\TensorFlow_Serving\gRPC_client.py”, line 33, in
response = stub.Predict(request)
File “e:\ProgramData\Anaconda3\envs\tensorflow27\lib\site-packages\grpc_channel.py”, line 946, in call
return _end_unary_response_blocking(state, call, False, None)
File “e:\ProgramData\Anaconda3\envs\tensorflow27\lib\site-packages\grpc_channel.py”, line 849, in _end_unary_response_blocking
raise _InactiveRpcError(state)
grpc._channel._InactiveRpcError: <_InactiveRpcError of RPC that terminated with:
status = StatusCode.UNAVAILABLE
details = “failed to connect to all addresses; last error: INTERNAL: ipv4:XXXX:8501: Trying to connect an http1.x server”
debug_error_string = “UNKNOWN:failed to connect to all addresses; last error: INTERNAL: ipv4:XXXX:8501: Trying to connect an http1.x server {grpc_status:14, created_time:“2024-07-31T15:29:54.366014749+00:00”}”

ChatGPT

看起来你正在使用的 TensorFlow Serving 服务器可能配置为使用 REST API 而不是 gRPC 服务。要使用 gRPC,你需要确保 TensorFlow Serving 服务器正确配置并运行在 gRPC 端口。

你可以尝试以下步骤来解决这个问题:

  1. 确保 TensorFlow Serving 使用 gRPC 端口运行:
    启动 TensorFlow Serving 时,确保正确配置 gRPC 端口。例如:

    tensorflow_model_server --port=8500 --rest_api_port=8501 --model_name=ai_flower --model_base_path=/xxxx/TensorFlow_Serving/saved_model/
    

    在这个示例中,--port=8500 是 gRPC 端口,--rest_api_port=8501 是 REST API 端口。

  2. 修改客户端代码以使用正确的 gRPC 端口:
    确保客户端代码中的端口号与服务器上的 gRPC 端口号一致。例如:

    channel = grpc.insecure_channel('XXXX:8500')
    
  3. 检查防火墙设置:
    确保服务器的 gRPC 端口(如 8500)在防火墙中是开放的,并且可以从客户端计算机访问。

  4. 确保模型输入名称正确:
    确保 request.inputs['input_tensor_name'] 中的 'input_tensor_name' 是你在 TensorFlow SavedModel 中定义的输入张量的名称。

修改后的完整代码示例

import grpc
import tensorflow as tf
from tensorflow_serving.apis import predict_pb2, prediction_service_pb2_grpc
from google.protobuf import json_format
from PIL import Image
import numpy as np

# 定义读取和预处理图片的函数
def load_image(image_path):
    img = Image.open(image_path).resize((224, 224))
    img = np.array(img).astype(np.float32)
    img = np.expand_dims(img, axis=0)  # 扩展维度以符合模型输入
    return img

# 创建 gRPC 频道和 stub
channel = grpc.insecure_channel('XXXX:8500')
stub = prediction_service_pb2_grpc.PredictionServiceStub(channel)

# 创建预测请求
request = predict_pb2.PredictRequest()
request.model_spec.name = 'ai_flower'
request.model_spec.signature_name = 'serving_default'

# 读取并预处理图片
image_path = 'test_images/rose.jpg'
input_image = load_image(image_path)

# 将预处理后的图片设置为请求的输入张量
request.inputs['input_tensor_name'].CopyFrom(
    tf.make_tensor_proto(input_image, shape=input_image.shape))

# 发送请求并获取响应
response = stub.Predict(request)
print(response)

确保 TensorFlow Serving 服务器和客户端配置的一致性,并且服务器端正确运行 gRPC 服务。如果还有问题,请提供更多详细信息,以便进一步诊断。


3问:有新错误:Traceback (most recent call last):
File “E:\WorkspacePython\TensorFlow_Serving\gRPC_client.py”, line 33, in
response = stub.Predict(request)
File “e:\ProgramData\Anaconda3\envs\tensorflow27\lib\site-packages\grpc_channel.py”, line 946, in call
return _end_unary_response_blocking(state, call, False, None)
File “e:\ProgramData\Anaconda3\envs\tensorflow27\lib\site-packages\grpc_channel.py”, line 849, in _end_unary_response_blocking
raise _InactiveRpcError(state)
grpc._channel._InactiveRpcError: <_InactiveRpcError of RPC that terminated with:
status = StatusCode.INVALID_ARGUMENT
details = “input tensor alias not found in signature: input_tensor_name. Inputs expected to be in the set {keras_layer_input}.”
debug_error_string = “UNKNOWN:Error received from peer ipv4:XXXX:8500 {created_time:“2024-07-31T15:33:22.315742596+00:00”, grpc_status:3, grpc_message:“input tensor alias not found in signature: input_tensor_name. Inputs expected to be in the set {keras_layer_input}.”}”

ChatGPT:

错误信息表明,在你的模型签名中,输入张量的名称是 keras_layer_input,而不是 input_tensor_name。你需要将客户端代码中请求输入张量的名称改为 keras_layer_input

修改后的客户端代码如下:

import grpc
import tensorflow as tf
from tensorflow_serving.apis import predict_pb2, prediction_service_pb2_grpc
from PIL import Image
import numpy as np

# 定义读取和预处理图片的函数
def load_image(image_path):
    img = Image.open(image_path).resize((224, 224))
    img = np.array(img).astype(np.float32)
    img = np.expand_dims(img, axis=0)  # 扩展维度以符合模型输入
    return img

# 创建 gRPC 频道和 stub
channel = grpc.insecure_channel('XXXX:8500')
stub = prediction_service_pb2_grpc.PredictionServiceStub(channel)

# 创建预测请求
request = predict_pb2.PredictRequest()
request.model_spec.name = 'ai_flower'
request.model_spec.signature_name = 'serving_default'

# 读取并预处理图片
image_path = 'test_images/rose.jpg'
input_image = load_image(image_path)

# 将预处理后的图片设置为请求的输入张量
request.inputs['keras_layer_input'].CopyFrom(
    tf.make_tensor_proto(input_image, shape=input_image.shape))

# 发送请求并获取响应
response = stub.Predict(request)
print(response)

确认模型签名

如果你不确定输入张量的确切名称,可以使用以下命令来查看模型的签名信息:

saved_model_cli show --dir /xxxx/TensorFlow_Serving/saved_model --all

这个命令会输出模型的输入和输出签名信息。根据这个信息,你可以确认输入张量的正确名称并在客户端代码中使用它。

进一步检查

  1. 确保输入张量名称正确: 根据 saved_model_cli 命令输出的签名信息,确认输入张量的名称。
  2. 模型签名的一致性: 确保模型的签名名称与客户端代码中的一致。

通过这些步骤,应该能解决输入张量名称不匹配的问题。如果还有问题,请提供更多详细信息,以便进一步诊断。


4问:正确了。下面是客户端代码,修改一下代码:要求:标签编号与类别名称对应文件在当前目录label_map-encn.json中,分析返回信心,print前5为的预测结果。

ChatGPT

下面是修改后的客户端代码,包含从 label_map-encn.json 文件中读取标签编号与类别名称的对应关系,并分析返回的结果,打印前5位的预测结果:

import grpc
import tensorflow as tf
from tensorflow_serving.apis import predict_pb2, prediction_service_pb2_grpc
from PIL import Image
import numpy as np
import json

# 定义读取和预处理图片的函数
def load_image(image_path):
    img = Image.open(image_path).resize((224, 224))
    img = np.array(img).astype(np.float32)
    img = np.expand_dims(img, axis=0)  # 扩展维度以符合模型输入
    return img

# 从JSON文件中加载标签映射
def load_label_map(label_map_path):
    with open(label_map_path, 'r', encoding='utf-8') as f:
        label_map = json.load(f)
    return label_map

# 创建 gRPC 频道和 stub
channel = grpc.insecure_channel('XXXX:8500')
stub = prediction_service_pb2_grpc.PredictionServiceStub(channel)

# 创建预测请求
request = predict_pb2.PredictRequest()
request.model_spec.name = 'ai_flower'
request.model_spec.signature_name = 'serving_default'

# 读取并预处理图片
image_path = 'test_images/rose.jpg'
input_image = load_image(image_path)

# 将预处理后的图片设置为请求的输入张量
request.inputs['keras_layer_input'].CopyFrom(
    tf.make_tensor_proto(input_image, shape=input_image.shape))

# 发送请求并获取响应
response = stub.Predict(request)

# 解析响应
predictions = tf.make_ndarray(response.outputs['predictions'])

# 加载标签映射
label_map = load_label_map('label_map-encn.json')

# 获取前5位预测结果
top_k = 5
top_k_indices = np.argsort(predictions[0])[::-1][:top_k]
top_k_labels = [label_map[str(index)] for index in top_k_indices]
top_k_scores = predictions[0][top_k_indices]

# 打印前5位预测结果
for i in range(top_k):
    print(f"Rank {i + 1}: {top_k_labels[i]} (score: {top_k_scores[i]:.4f})")

说明

  1. 读取并预处理图片: load_image 函数读取并预处理图片。
  2. 加载标签映射: load_label_map 函数从 label_map-encn.json 文件中加载标签映射。
  3. 创建 gRPC 频道和 stub: 与 TensorFlow Serving 服务器建立 gRPC 连接。
  4. 创建预测请求: 构造预测请求并发送到服务器。
  5. 解析响应: 从响应中解析预测结果,并获取前5位的预测类别及其对应的分数。
  6. 打印结果: 打印前5位预测结果及其分数。

确保 label_map-encn.json 文件在脚本的当前目录中,并且其内容格式正确。例如:

{
    "0": "daisy",
    "1": "dandelion",
    "2": "roses",
    "3": "sunflowers",
    "4": "tulips"
}

5问:Traceback (most recent call last):
File “E:\WorkspacePython\TensorFlow_Serving\gRPC_client.py”, line 42, in
predictions = tf.make_ndarray(response.outputs[‘predictions’])
File “e:\ProgramData\Anaconda3\envs\tensorflow27\lib\site-packages\tensorflow\python\framework\tensor_util.py”, line 662, in MakeNdarray
tensor_dtype = dtypes.as_dtype(tensor.dtype)
File “e:\ProgramData\Anaconda3\envs\tensorflow27\lib\site-packages\tensorflow\python\framework\dtypes.py”, line 852, in as_dtype
raise TypeError(f"Cannot convert the argument type_value: {type_value!r} "
TypeError: Cannot convert the argument type_value: 0 to a TensorFlow DType.

ChatGPT

这个错误通常表示在从响应中提取张量时,找不到指定的键。确保响应中的键名与模型导出的签名一致。

你可以通过调试代码来检查响应的具体内容,从而确定输出张量的名称。先打印响应内容,找到包含预测结果的键。修改如下:

import grpc
import tensorflow as tf
from tensorflow_serving.apis import predict_pb2, prediction_service_pb2_grpc
from PIL import Image
import numpy as np
import json

# 定义读取和预处理图片的函数
def load_image(image_path):
    img = Image.open(image_path).resize((224, 224))
    img = np.array(img).astype(np.float32)
    img = np.expand_dims(img, axis=0)  # 扩展维度以符合模型输入
    return img

# 从JSON文件中加载标签映射
def load_label_map(label_map_path):
    with open(label_map_path, 'r', encoding='utf-8') as f:
        label_map = json.load(f)
    return label_map

# 创建 gRPC 频道和 stub
channel = grpc.insecure_channel('XXXX:8500')
stub = prediction_service_pb2_grpc.PredictionServiceStub(channel)

# 创建预测请求
request = predict_pb2.PredictRequest()
request.model_spec.name = 'ai_flower'
request.model_spec.signature_name = 'serving_default'

# 读取并预处理图片
image_path = 'test_images/rose.jpg'
input_image = load_image(image_path)

# 将预处理后的图片设置为请求的输入张量
request.inputs['keras_layer_input'].CopyFrom(
    tf.make_tensor_proto(input_image, shape=input_image.shape))

# 发送请求并获取响应
response = stub.Predict(request)

# 打印响应内容,检查输出张量的键名
print("Response outputs:", response.outputs)

# 假设输出张量的键名是 'predictions',从响应中提取预测结果
# 如果键名不同,请根据打印结果进行修改
output_tensor_name = 'predictions'  # 修改为实际的键名
if output_tensor_name in response.outputs:
    predictions = tf.make_ndarray(response.outputs[output_tensor_name])
else:
    print(f"Output tensor '{output_tensor_name}' not found in the response.")
    predictions = []

# 加载标签映射
label_map = load_label_map('label_map-encn.json')

# 获取前5位预测结果
top_k = 5
if predictions.size > 0:
    top_k_indices = np.argsort(predictions[0])[::-1][:top_k]
    top_k_labels = [label_map[str(index + 1)] for index in top_k_indices]
    top_k_scores = predictions[0][top_k_indices]

    # 打印前5位预测结果
    for i in range(top_k):
        print(f"Rank {i + 1}: {top_k_labels[i]} (score: {top_k_scores[i]:.4f})")
else:
    print("No predictions found.")

说明

  1. 打印响应内容: 通过 print("Response outputs:", response.outputs) 打印响应内容,找到包含预测结果的键。
  2. 检查输出张量的键名: 根据打印结果,修改 output_tensor_name 变量为实际的键名。

执行此代码,观察打印的响应内容,确保找到了正确的键名。然后,修改 output_tensor_name 变量,重新运行代码进行预测和结果打印。


6问:正确了。再讨论下一个问题:gRPC与REST API有什么区别?

ChatGPT

gRPC 和 REST API 是两种用于服务间通信的不同协议。它们各有优缺点,适用于不同的场景。以下是它们的主要区别:

1. 协议与传输方式

  • gRPC:

    • 协议: gRPC 基于 HTTP/2 协议。它使用 Protocol Buffers(Protobuf)作为接口定义语言和消息交换格式。
    • 传输方式: 支持流式传输(双向流、客户端流、服务端流)。
  • REST API:

    • 协议: REST 通常基于 HTTP/1.1 或 HTTP/2 协议。
    • 传输方式: 请求和响应是基于 HTTP 方法(如 GET、POST、PUT、DELETE)。

2. 数据格式

  • gRPC:

    • 使用 Protocol Buffers(二进制格式),这使得数据序列化和反序列化非常高效。
    • 由于是二进制格式,数据传输较小且速度快,但不如 JSON 直观易读。
  • REST API:

    • 通常使用 JSON 或 XML 作为数据格式。
    • JSON 易于阅读和调试,但数据传输较大且可能比二进制格式慢。

3. 性能

  • gRPC:

    • 支持高效的二进制序列化,通常比 JSON 更节省带宽。
    • 支持 HTTP/2 的多路复用和压缩,可以提高性能和减少延迟。
    • 适合需要高效、低延迟通信的场景,如微服务之间的通信。
  • REST API:

    • 使用文本格式的 JSON,可能导致更大的数据传输量。
    • HTTP/1.1 不支持多路复用,可能会导致性能瓶颈,尤其是在高并发情况下。

4. 接口定义

  • gRPC:

    • 使用 Protobuf 定义接口,接口定义文件(.proto 文件)描述了服务、方法和消息格式。
    • 自动生成客户端和服务端代码,减少了手动编码错误和工作量。
  • REST API:

    • 通常使用 OpenAPI(Swagger)或其他文档工具来描述接口。
    • 接口文档可能不如 Protobuf 那样强类型,但可以使用工具生成客户端代码。

5. 错误处理

  • gRPC:

    • 提供了更丰富的错误处理机制,可以通过 gRPC 状态码进行详细的错误描述。
  • REST API:

    • 错误通常通过 HTTP 状态码和响应体中的错误信息进行描述。

6. 流式通信

  • gRPC:

    • 支持流式通信,可以进行双向流传输,适合实时数据传输和复杂的请求/响应模式。
  • REST API:

    • 通常不支持流式传输,主要是一次请求-一次响应模式。

7. 工具和语言支持

  • gRPC:

    • 支持多种编程语言,并且提供了强类型的接口定义和代码生成工具。
  • REST API:

    • 语言和框架支持广泛,许多工具和库可以用于实现和测试 REST API。

总结

  • gRPC 适合对性能和效率有较高要求的场景,如微服务架构和实时应用。
  • REST API 更加成熟,易于使用和调试,适合需要简单、易读的接口和广泛兼容性的场景。

选择哪种技术取决于具体的应用需求和环境。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值