tensorflow模型必须在flask初始化阶段一次性加载并禁用gpu以防冲突;预处理需严格复现训练流程,输入须为归一化、带batch维的tf.tensor;预测应调用model(x, training=false)而非model.predict();输出需转为python原生类型再json序列化。

模型加载必须在Flask应用初始化阶段完成
TensorFlow模型(尤其是tf.keras.Model或SavedModel格式)加载耗时且线程不安全,不能在每个请求里重复调用tf.keras.models.load_model()或tf.saved_model.load()。否则会触发内存暴涨、CUDA上下文冲突,甚至返回Failed to get convolution algorithm这类GPU错误。
- 把模型加载写在
app.py顶层,或封装进全局变量(如model = None+ 初始化函数),确保只执行一次 - 使用
tf.config.set_visible_devices([], 'GPU')显式禁用GPU(除非你确认Flask进程能稳定绑定单个GPU) - 若用SavedModel,路径必须是绝对路径;相对路径在Gunicorn/uWSGI下极易变成
NotFoundError: ... No such file or directory
输入预处理必须与训练时完全一致
Flask接收的是原始HTTP请求数据(比如request.files['image']或request.json),但TensorFlow模型只认特定shape和dtype的tf.Tensor。常见错误是直接把PIL Image转成np.array后没归一化,或没expand_dims,导致InvalidArgumentError: expected shape [1, 224, 224, 3]。
- 用
tf.image.decode_jpeg()或tf.io.decode_image()替代PIL,保持图层一致性(尤其对uint8→float32缩放逻辑) - 务必复现训练时的resize方式:如果训练用
tf.image.resize(..., method='bilinear'),推理就不能用OpenCV的cv2.resize - batch维度不能漏:即使单样本,也要
tf.expand_dims(img_tensor, 0),否则model.predict()会报维度不匹配
预测调用要用model(x, training=False)而非model.predict()
model.predict()是Keras高层API,内部会启动额外的批处理、回调和进度条,在Web服务中不仅慢,还会干扰异步/多线程环境(比如并发请求时卡死)。而直接调用model(x, training=False)走的是纯前向传播路径,响应更快也更可控。
-
training=False必须显式传入,否则BatchNorm和Dropout层行为异常,输出不稳定 - 输入
x必须是tf.Tensor,不能是numpy array——否则会触发隐式转换,每次请求多花几十毫秒 - 若模型有多个输出(如分类+回归头),直接解包:
cls_out, reg_out = model(x, training=False),别用model.predict()再索引
避免JSON序列化Tensor或NumPy数组的坑
Flask的jsonify()无法直接序列化tf.Tensor或np.ndarray,常见错误是直接return jsonify({'pred': pred.numpy()}),结果抛出TypeError: Object of type ndarray is not JSON serializable。
- 用
pred.numpy().tolist()转Python原生list(注意:大数组会拖慢响应,建议限制输出维度) - 若需保留float32精度,用
np.float64(pred.numpy()).item()避免科学计数法失真 - 不要用
json.dumps(..., default=str)兜底——它会把tensor变成"<tf.tensor ...>"</tf.tensor>字符串,毫无意义
模型输入输出的类型、形状、数值范围,比路由设计重要得多。一个没对齐的tf.cast()就可能让整套服务返回全零预测。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











