2. 使用Flask创建模型API
任务目标
使用Flask框架封装一个已训练的图像分类模型(以PyTorch加载的ResNet18为例),创建REST API端点,接收客户端上传的图像文件,返回模型预测的类别名称及置信度。
环境依赖
需要安装:flask、torch、torchvision、pillow。若使用GPU推理则需对应CUDA版本PyTorch,CPU版本亦可运行。
完整实现代码
import io
import json
from flask import Flask, request, jsonify
from PIL import Image
import torch
import torchvision.transforms as transforms
from torchvision import models
app = Flask(__name__)
# 加载预训练模型并设为评估模式
model = models.resnet18(weights=models.ResNet18_Weights.IMAGENET1K_V1)
model.eval()
# ImageNet类别标签(仅加载所需映射)
LABELS_URL = "https://raw.githubusercontent.com/pytorch/hub/master/imagenet_classes.txt"
import urllib.request
with urllib.request.urlopen(LABELS_URL) as f:
classes = [line.strip() for line in f.readlines()]
# 图像预处理:与训练时一致
preprocess = 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])
])
def predict_image(image_bytes):
"""输入图像字节流,返回(类别名, 置信度)"""
img = Image.open(io.BytesIO(image_bytes)).convert('RGB')
input_tensor = preprocess(img).unsqueeze(0) # 增加batch维度
with torch.no_grad():
output = model(input_tensor)
probabilities = torch.nn.functional.softmax(output[0], dim=0)
conf, pred_idx = torch.max(probabilities, dim=0)
return classes[pred_idx.item()], conf.item()
@app.route('/predict', methods=['POST'])
def predict():
if 'file' not in request.files:
return jsonify({'error': '请求中缺少file字段'}), 400
file = request.files['file']
if file.filename == '':
return jsonify({'error': '未选择文件'}), 400
try:
image_bytes = file.read()
label, confidence = predict_image(image_bytes)
return jsonify({
'predicted_class': label,
'confidence': round(confidence, 4)
})
except Exception as e:
return jsonify({'error': f'图像处理失败: {str(e)}'}), 400
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
运行方式
- 将上述代码保存为
app.py。 - 在终端执行
python app.py,服务默认监听5000端口。 - 首次运行会自动下载ResNet18权重(约45MB)和类别标签文件,需保持网络畅通。
调用与验证
使用curl命令发送测试请求(以本地任意一张dog.jpg为例):
curl -X POST -F "file=@dog.jpg" http://127.0.0.1:5000/predict
预期输出(类别名称取决于测试图片内容):
{"predicted_class":"golden retriever","confidence":0.9123}
通过条件:
- 返回HTTP 200且JSON中包含
predicted_class与confidence字段; - 类别名称与图片内容相符(如狗图片返回犬种名);
- 置信度数值在0~1之间。
若上传非图像文件或缺少file字段,应返回HTTP 400及错误信息。
常见问题
- 端口被占用:修改
app.run中的port参数。 - 内存不足:将
model移到predict_image外部(已在代码中实现),避免每次请求重复加载。 - 首次请求慢:权重文件在服务启动时加载,首次请求仅需图像预处理与推理,通常秒级内完成。