efficientnet-b0-mlop / app /inference.py
thk354777
deploy
6bf9a0e
Raw
History Blame Contribute Delete
4.89 kB
import io
import os
import time
import numpy as np
import onnxruntime as ort
import torch
import torch.nn as nn
import torch.nn.functional as F
from PIL import Image
from torchvision import transforms
from torchvision.models import efficientnet_b0
# Configuration
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
MODEL_DIR = os.path.join(BASE_DIR, "model")
THAI_FOOD_CLASSES = [
"Green_Curry",
"Khao_phat",
"Khao_soi",
"Massaman_Curry",
"Pad_Thai",
"Phanaeng_Curry",
"Phat_kaphrao",
"Roti_canai",
"Tom_kha_gai",
"Tom_yum"
]
# Global dictionary to hold model instances in memory
models = {}
def init_worker():
"""Initialize models in the worker process to avoid reloading on each request."""
global models
try:
# 1. Load ONNX
onnx_path = os.path.join(MODEL_DIR, "efficientnet-b0.onnx")
if os.path.exists(onnx_path):
models['onnx'] = ort.InferenceSession(onnx_path)
else:
print(f"Warning: File not found {onnx_path}")
# 2. Load FP16 ONNX (used as the quantized version)
fp16_path = os.path.join(MODEL_DIR, "efficientnet-b0_fp16.onnx")
if os.path.exists(fp16_path):
models['quantized'] = ort.InferenceSession(fp16_path)
else:
print(f"Warning: File not found {fp16_path}")
# 3. Load Original PyTorch with custom weights
pth_path = os.path.join(MODEL_DIR, "best_thai_food_model.pth")
models['original'] = efficientnet_b0(pretrained=False)
models['original'].classifier[1] = nn.Linear(
models['original'].classifier[1].in_features,
len(THAI_FOOD_CLASSES)
)
if os.path.exists(pth_path):
models['original'].load_state_dict(torch.load(pth_path, map_location='cpu'))
else:
print(f"Warning: File not found {pth_path} (Using random weights)")
models['original'].eval()
except Exception as e:
print(f"Error loading models in worker: {e}")
def preprocess_image(image_bytes: bytes):
"""Convert bytes to a normalized tensor suitable for EfficientNet."""
image = Image.open(io.BytesIO(image_bytes)).convert("RGB")
transform = transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])
return transform(image).unsqueeze(0)
def get_model_size_mb(model_type: str) -> float:
"""Helper function to calculate model file size in MB."""
if model_type == "original":
path = os.path.join(MODEL_DIR, "best_thai_food_model.pth")
elif model_type == "onnx":
path = os.path.join(MODEL_DIR, "efficientnet-b0.onnx")
elif model_type == "quantized":
path = os.path.join(MODEL_DIR, "efficientnet-b0_fp16.onnx")
else:
return 0.0
if os.path.exists(path):
return os.path.getsize(path) / (1024 * 1024)
return 0.0
def run_inference(image_bytes: bytes, model_type: str):
"""Main inference function executed by the process pool."""
global models
if model_type not in models:
raise ValueError(f"Model {model_type} is not loaded.")
input_tensor = preprocess_image(image_bytes)
start_time = time.perf_counter()
if model_type == "original":
with torch.no_grad():
logits = models['original'](input_tensor)
probs = F.softmax(logits, dim=1)
confidence, class_idx = torch.max(probs, 1)
prediction_id = class_idx.item()
confidence_score = confidence.item()
elif model_type in ["onnx", "quantized"]:
ort_sess = models[model_type]
input_data = input_tensor.numpy()
# Dynamically cast input to float16 if required by the model
input_type = ort_sess.get_inputs()[0].type
if 'float16' in input_type:
input_data = input_data.astype(np.float16)
ort_inputs = {ort_sess.get_inputs()[0].name: input_data}
logits = ort_sess.run(None, ort_inputs)[0]
# Numpy Softmax
exp_logits = np.exp(logits - np.max(logits))
probs = exp_logits / exp_logits.sum(axis=1, keepdims=True)
prediction_id = int(np.argmax(probs[0]))
confidence_score = float(probs[0][prediction_id])
latency_ms = (time.perf_counter() - start_time) * 1000
model_size_mb = get_model_size_mb(model_type)
return {
"model_type": model_type,
"prediction_class_id": prediction_id,
"prediction_class_name": THAI_FOOD_CLASSES[prediction_id],
"confidence_score": round(confidence_score * 100, 2),
"latency_ms": round(latency_ms, 2),
"model_size_mb": round(model_size_mb, 2)
}