2. 使用FastAPI部署TensorFlow文本分类模型

任务目标

构建一个FastAPI异步接口,加载预训练的TensorFlow BERT模型(中文情感分类),接收JSON格式的文本输入,返回情感类别(正面/负面)及对应的置信度分数。

环境依赖

tensorflow==2.12.0
transformers==4.30.0
fastapi==0.100.0
uvicorn==0.23.0
pydantic==2.0.0

完整实现代码

import asyncio
from functools import lru_cache
from typing import Dict

import numpy as np
import tensorflow as tf
from fastapi import FastAPI
from pydantic import BaseModel
from transformers import TFAutoModelForSequenceClassification, AutoTokenizer

# ---------- 模型与分词器加载 ----------
MODEL_NAME = "bert-base-chinese"
tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
model = TFAutoModelForSequenceClassification.from_pretrained(
    MODEL_NAME, num_labels=2
)

# 编译模型(TensorFlow SavedModel 或权重加载后需要)
dummy_input = tokenizer("测试", return_tensors="tf")
_ = model(dummy_input)  # 触发一次前向传播以构建图

# ---------- FastAPI 应用 ----------
app = FastAPI(title="BERT情感分类API")

class TextRequest(BaseModel):
    text: str

class PredictionResponse(BaseModel):
    label: str
    confidence: float

@lru_cache(maxsize=128)
def predict_sync(text: str) -> Dict[str, float]:
    """同步推理函数,使用 lru_cache 缓存重复请求结果"""
    inputs = tokenizer(text, return_tensors="tf", truncation=True, max_length=128)
    logits = model(inputs).logits
    probs = tf.nn.softmax(logits, axis=-1).numpy()[0]
    return {"positive": float(probs[1]), "negative": float(probs[0])}

@app.post("/predict", response_model=PredictionResponse)
async def predict(request: TextRequest):
    """异步接口:在线程池中执行同步推理,避免阻塞事件循环"""
    loop = asyncio.get_running_loop()
    result = await loop.run_in_executor(None, predict_sync, request.text)

    if result["positive"] >= result["negative"]:
        return PredictionResponse(label="positive", confidence=result["positive"])
    else:
        return PredictionResponse(label="negative", confidence=result["negative"])

@app.get("/health")
async def health_check():
    return {"status": "ok"}

运行方式

# 启动服务(默认端口8000)
uvicorn main:app --host 0.0.0.0 --port 8000

调用与验证

使用curl或浏览器访问接口:

curl -X POST http://localhost:8000/predict \
  -H "Content-Type: application/json" \
  -d '{"text": "这部电影非常精彩,演员演技出色"}'

预期输出(基于模型权重,数值可能略有不同):

{"label":"positive","confidence":0.87}

验证通过条件:

  1. 服务启动无报错,/health返回{"status":"ok"};
  2. POST请求返回200状态码,响应体包含label和confidence字段;
  3. 输入正面文本(如示例)时label为positive,输入“这部电影很糟糕,浪费时间”时label为negative;
  4. 置信度值在0到1之间,且正面与负面概率之和为1。

关键说明

  • 模型使用bert-base-chinese预训练权重,未经过微调,因此分类精度有限,但接口逻辑完整可用;
  • 若需更高精度,可用标注数据微调模型后替换加载路径;
  • 异步接口通过run_in_executor将阻塞推理放入线程池,保证高并发下事件循环不被阻塞;
  • 使用lru_cache缓存相同文本的推理结果,减少重复计算。