使用 Uvicorn 修复“无法将字典更新序列元素 #0 转换为序列”时出现问题

Nom*_*ems 3 python server tensorflow fastapi uvicorn

我有以下使用 FastApi 和 Uvicorn 的代码用于 ASGI 服务器实现。它应该通过发布请求获取上传的图像,并在返回响应之前使用模型对其进行分类。该错误似乎与 Uvicorn 有关,但我不知所措。任何帮助将非常感激。以前有人见过这样的错误吗?这是代码:

import uvicorn
from fastapi import FastAPI, File, UploadFile
import sys

from PIL import Image
from io import BytesIO
import numpy as np

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
import matplotlib.pyplot as plt
from tensorflow.keras.preprocessing import image
import PIL
import sys 
from cv2 import cv2
from scipy import misc
import os

import shutil
from pathlib import Path
from tempfile import NamedTemporaryFile
from typing import Callable

app = FastAPI()

model = keras.models.load_model('best_model6.h5')
input_shape = (180, 180) 

@app.post('/api/predict')
async def predict_image(file: UploadFile = File(...)):

    suffix = Path(file.filename).suffix

    with NamedTemporaryFile(delete=False, suffix=suffix) as tmp:
        shutil.copyfileobj(file.file, tmp)
        tmp_path = Path(tmp.name)
    
    img = keras.preprocessing.image.load_img(
    tmp_path, target_size=input_shape
)
    
    img_array = image.img_to_array(img)

    img_array = tf.expand_dims(img_array, 0)  # Create batch axis

    predictions = model.predict(img_array)
    score = predictions[0]

    file.file.close()
    tmp_path.unlink()
    
    return score


if __name__ == "__main__":
    uvicorn.run(app, port=8080, host='0.0.0.0', debug=True)
Run Code Online (Sandbox Code Playgroud)

错误是:

ValueError: [TypeError('cannot convert dictionary update sequence element #0 to a sequence'), TypeError('vars() argument must have __dict__ attribute')]
Run Code Online (Sandbox Code Playgroud)

以及整个回溯:

Traceback (most recent call last):
  File "/Users/.../Desktop/project/venv/lib/python3.9/site-packages/uvicorn/protocols/http/h11_impl.py", line 373, in run_asgi
    result = await app(self.scope, self.receive, self.send)
  File "/Users/.../Desktop/project/venv/lib/python3.9/site-packages/uvicorn/middleware/proxy_headers.py", line 75, in __call__
    return await self.app(scope, receive, send)
  File "/Users/.../Desktop/project/venv/lib/python3.9/site-packages/uvicorn/middleware/debug.py", line 96, in __call__
    raise exc from None
  File "/Users/.../Desktop/project/venv/lib/python3.9/site-packages/uvicorn/middleware/debug.py", line 93, in __call__
    await self.app(scope, receive, inner_send)
  File "/Users/.../Desktop/project/venv/lib/python3.9/site-packages/fastapi/applications.py", line 208, in __call__
    await super().__call__(scope, receive, send)
  File "/Users/.../Desktop/project/venv/lib/python3.9/site-packages/starlette/applications.py", line 112, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/Users/.../Desktop/project/venv/lib/python3.9/site-packages/starlette/middleware/errors.py", line 181, in __call__
    raise exc
  File "/Users/.../Desktop/project/venv/lib/python3.9/site-packages/starlette/middleware/errors.py", line 159, in __call__
    await self.app(scope, receive, _send)
  File "/Users/.../Desktop/project/venv/lib/python3.9/site-packages/starlette/exceptions.py", line 82, in __call__
    raise exc
  File "/Users/.../Desktop/project/venv/lib/python3.9/site-packages/starlette/exceptions.py", line 71, in __call__
    await self.app(scope, receive, sender)
  File "/Users/.../Desktop/project/venv/lib/python3.9/site-packages/starlette/routing.py", line 656, in __call__
    await route.handle(scope, receive, send)
  File "/Users/.../Desktop/project/venv/lib/python3.9/site-packages/starlette/routing.py", line 259, in handle
    await self.app(scope, receive, send)
  File "/Users/.../Desktop/project/venv/lib/python3.9/site-packages/starlette/routing.py", line 61, in app
    response = await func(request)
  File "/Users/.../Desktop/project/venv/lib/python3.9/site-packages/fastapi/routing.py", line 234, in app
    response_data = await serialize_response(
  File "/Users/.../Desktop/project/venv/lib/python3.9/site-packages/fastapi/routing.py", line 148, in serialize_response
    return jsonable_encoder(response_content)
  File "/Users/.../Desktop/project/venv/lib/python3.9/site-packages/fastapi/encoders.py", line 144, in jsonable_encoder
    raise ValueError(errors)
ValueError: [TypeError('cannot convert dictionary update sequence element #0 to a sequence'), TypeError('vars() argument must have __dict__ attribute')]
Run Code Online (Sandbox Code Playgroud)

Leo*_*ons 9

Keras 模型的 predit 函数返回的是一个 Numpy 预测数组(请参阅此处),每个预测也是一个 numpy 数组。

但是 FastApi 在响应中使用 jsonable_encoder (请参阅此处)并且 numpy 数组是不可接受的。score.tolist()例如,您应该转换为 list( ) 以返回预测分数。在同一链接中,您将看到可以直接返回响应而不使用 jsonable_encoder

我希望我对你有所帮助。祝你好运