Search code examples
pythontensorflowserverfastapiuvicorn

Trouble fixing "'cannot convert dictionary update sequence element #0 to a sequence'" with Uvicorn


I have the following code with FastApi and Uvicorn for ASGI sever implementation. It's supposed to take an uploaded image via post request and classify it with a model before returning a response. The error seems related to Uvicorn but I am at a loss. Any help would be much appreciated. Has anyone seen an error like this before? Here is the code:

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)

The error is:

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

And the whole traceback:

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')]

Solution

  • The return of predit function from Keras Model is a Numpy array(s) of predictions (see here) and each prediction is a numpy array as well.

    But FastApi use jsonable_encoder in a response (see here) and numpy array is not acceptable. You should convert to list(score.tolist()), for example, to return the prediction scores. In the same link, you will see that is possible return a response directly without use jsonable_encoder

    I hope I have helped you. Good luck