拒绝云端依赖!3MB 模型跑在手机上,TFLite 转换保姆级教程
一句话记住它模型量化就是给 AI 做“脱水压缩”,牺牲一丢丢精度,换取手机上的极致速度。核心三要点ONNX是中转站,TFLite是终点站。FP16性价比最高(体积减半,精度不降)。INT8虽快,但需要校准数据,且可能有精度损失。本周作业运行export.py导出你自己的 TFLite 模型。用 Netron 截图模型的输入输出节点。(选做) 尝试开启int8=True,看看体积还能小多少?下篇,我

关键词:ONNX, TFLite, 量化 (Quantization), YOLOv8, Android
大家好,我是飞哥!👋
前几个月我们一直在云端“冲浪”,用 Python 调 API、玩 Docker。
但作为一名 Android 开发者,你肯定心痒痒:“能不能不联网,直接在手机上跑 AI?”
答案是:必须能! 但有个前提:你不能把云端那个几百 GB 的“大胖子”模型直接塞进手机里,手机会爆炸的(夸张了,是会卡死)。💥
今天这节课,飞哥就教你一门“瘦身缩骨功”——模型量化与格式转换。
1. 为什么要量化?(Why)
锚定已知 ⚓️
大家平时发微信视频,是不是经常会被压缩?原本 100MB 的高清视频,发过去只有 5MB,画质稍微糊了一点点,但看起来差不多,而且传输极快。
生动类比 🍔
模型量化,就是给 AI 模型做“脱水处理” (Freeze-drying)。
- 云端模型 (FP32):就像一颗饱满多汁的新鲜苹果。精度极高(小数点后 32 位),但又重又大,必须放冷库(GPU 服务器)。
- 量化模型 (INT8):就像一片苹果干。虽然水分(精度)少了一点点,口感(效果)略有差异,但营养(核心能力)还在,最重要的是——轻便! 随便一个小口袋(手机 NPU/CPU)就能装下。
提炼骨架 🦴
所以,端侧 AI 的核心流程就是:
训练 (PyTorch) ➡️ 转换 (ONNX) ➡️ 压缩 (TFLite/INT8) ➡️ 部署 (Android)
2. 核心概念详解 (What)
(1) ONNX:AI 界的“普通话” 🌏
不同的 AI 框架(PyTorch, TensorFlow, PaddlePaddle)就像各地的方言,互相听不懂。
ONNX (Open Neural Network Exchange) 就是它们共同商定的“普通话”。
不管你是哪里训练出来的模型,先转成 ONNX,然后再翻译成手机能听懂的 TFLite 或 NCNN。
(2) TFLite:Android 的“亲儿子” 🤖
Google 专门为移动端定制的格式。它去掉了训练相关的累赘功能,只保留“推理”能力,专门为安卓手机优化。
(3) 量化 (Quantization) 📉
这是一个用精度换速度的游戏:
| 模式 | 精度 | 体积 | 速度 | 推荐指数 |
|---|---|---|---|---|
| FP32 | 32位浮点 | 100% (大) | 🐢 慢 | ❌ (云端用) |
| FP16 | 16位浮点 | 50% (中) | 🏃 快 | ✅ (Android 首选) |
| INT8 | 8位整数 | 25% (小) | 🚀 极快 | ⚡️ (NPU 加速必备) |
⚠️ 飞哥提示:INT8 虽然快,但需要“校准 (Calibration)”。这就像拍照时的“自动测光”。真实世界的光线(FP32)范围极大,而照片(INT8)能容纳的亮度有限。转换时,你需要先让模型看一些典型图片(校准数据),让它算出最佳的“曝光参数”(数据分布),这样才能保证压缩后的模型既不过曝也不死黑,保留住核心细节。
3. 实战项目:模型量化实验室 (How) 🛠️
我们要完成一个任务:下载 YOLOv8 模型,把它转换成 Android 能用的 TFLite 格式,并验证它没坏。
本项目代码已开源在:Week13_Model_Quantization 文件夹。
第一步:安装依赖 📦
pip install ultralytics onnx onnxruntime tensorflow opencv-python
第二步:一键导出 (export.py) 📤
YOLOv8 官方极其贴心,一行代码就能搞定。
from ultralytics import YOLO
import shutil
import os
def export_models():
print("🚀 开始加载 YOLOv8n 模型...")
# 1. 下载/加载模型
model = YOLO('yolov8n.pt')
# 2. 导出为 ONNX (通用格式)
print("\n📦 正在导出为 ONNX 格式...")
model.export(format='onnx', opset=12)
# 3. 导出为 TFLite (FP16 - 推荐)
# half=True 表示使用 FP16 半精度,体积减半,精度几乎不损
print("\n📦 正在导出为 TFLite (FP16) 格式...")
model.export(format='tflite', half=True)
print("\n✅ 导出完成!")
# 移动文件到 models 目录
os.makedirs("models", exist_ok=True)
for f in os.listdir('.'):
if f.endswith('.onnx') or f.endswith('.tflite') or f.endswith('.pt'):
try:
shutil.move(f, f"models/{f}")
except:
pass
print("\n📂 模型已移动到 models/ 文件夹")
if __name__ == "__main__":
export_models()
运行后,你会发现 models 文件夹里多了 yolov8n_float16.tflite,大小只有 yolov8n.pt 的一半!
第三步:Python 端验证 (inference.py) 🔍
很多同学转完模型直接扔进 Android 项目,结果报错一脸懵。
飞哥教你一招:先在 Python 里用 tensorflow.lite 跑一遍,确保模型本身没问题。
import tensorflow as tf
import numpy as np
import cv2
import time
import os
def run_inference(tflite_path, image_path):
print(f"\n🔍 开始测试模型: {tflite_path}")
# 1. 加载 TFLite 模型
interpreter = tf.lite.Interpreter(model_path=tflite_path)
interpreter.allocate_tensors()
# 2. 获取输入输出详情
input_details = interpreter.get_input_details()
output_details = interpreter.get_output_details()
input_shape = input_details[0]["shape"] # [1, 640, 640, 3]
print(f"📥 模型输入形状: {input_shape}")
# 3. 预处理图片
img = cv2.imread(image_path)
if img is None:
print("❌ 找不到图片,请检查路径")
return
# Resize 到模型要求的尺寸 (通常是 640x640)
img_resized = cv2.resize(img, (640, 640))
# 归一化 (0-255 -> 0-1)
input_data = img_resized.astype(np.float32) / 255.0
input_data = np.expand_dims(input_data, axis=0) # 增加 batch 维度
# 4. 推理
start_time = time.time()
interpreter.set_tensor(input_details[0]["index"], input_data)
interpreter.invoke()
output_data = interpreter.get_tensor(output_details[0]["index"])
end_time = time.time()
print(f"⚡️ 推理耗时: {(end_time - start_time)*1000:.2f} ms")
print(f"📤 输出形状: {output_data.shape}")
# ---------------------------------------------------------
# 💡 飞哥小课堂:这个 (1, 84, 8400) 是什么意思?
# ---------------------------------------------------------
# 1. Batch Size (1): 我们一次只喂了一张图片。
# 2. Channels (84): 代表每个预测框包含的信息量。
# - 前 4 个数:是框的位置 (中心点x, 中心点y, 宽w, 高h)。
# - 后 80 个数:是 COCO 数据集 80 个类别的概率 (比如它是猫的概率、是车的概率...)。
# - 4 + 80 = 84
# 3. Anchors (8400): YOLO 在这张图上总共生成了 8400 个候选框。
# - 它是怎么算出来的?(640x640 图片)
# - 80x80 (大特征图,看小物体) = 6400
# - 40x40 (中特征图,看中物体) = 1600
# - 20x20 (小特征图,看大物体) = 400
# - 总和:6400 + 1600 + 400 = 8400
# ---------------------------------------------------------
print("✅ 测试通过!模型可以正常工作。")
if __name__ == "__main__":
# 自动查找模型并运行
# 确保你已经有了 models/yolov8n_saved_model/yolov8n_float16.tflite 和 assets/bus.jpg
pass
# (完整代码请参考 Week13_Model_Quantization/inference.py)
第四步:使用 Netron 查看“内脏” 🔬
拿到模型后,必须看一眼它的结构。
下载神器 Netron (也有网页版)。
打开 yolov8n.onnx,关注两个点:
- Input:
images(1, 3, 640, 640) -> 记住这个尺寸,Android 端处理图片时必须一模一样。 - Output:
output0(1, 84, 8400) -> 这是 YOLO 的原始输出(84 = 4个坐标 + 80个类别概率)。
⚠️ 进阶技巧:
在 Android 上解析1x84x8400这种原始数据非常痛苦(需要自己写 NMS 算法)。
如果你想偷懒,可以在导出时加上nms=True(仅部分版本支持),或者使用专门为 TFLite 优化的 YOLO 版本。
4. 总结与作业 📝
一句话记住它:
模型量化就是给 AI 做“脱水压缩”,牺牲一丢丢精度,换取手机上的极致速度。
核心三要点:
- ONNX 是中转站,TFLite 是终点站。
- FP16 性价比最高(体积减半,精度不降)。
- INT8 虽快,但需要校准数据,且可能有精度损失。
本周作业:
- 运行
export.py导出你自己的 TFLite 模型。 - 用 Netron 截图模型的输入输出节点。
- (选做) 尝试开启
int8=True,看看体积还能小多少?
下篇,我们将正式进入 Android (Jetpack Compose),把这个 TFLite 模型装进手机,做一个能看懂世界的 App!🚀
🎁 课程示例
我用夸克网盘给你分享了「Week13_Model_Quantization.zip」,点击链接或复制整段内容,打开「夸克APP」即可获取。
/483d3ADEGx😕 链接:https://pan.quark.cn/s/69cf8e9d25e4
源码包内含:
- ✅
export.py:一键导出脚本 - ✅
inference.py:Python 验证脚本 - ✅
requirements.txt:环境依赖清单 - ✅ 超详细 README 文档,小白也能跑通!
更多推荐

所有评论(0)