综合项目:智能相册应用(一)——需求分析、架构设计、模型选择
各位同学,欢迎来到综合实战环节。
前面我们啃完了 NNAPI 的底层原理、模型转换、性能调优,说实话,那些都是「零件」。今天开始,我们要用这些零件,组装一台真正的机器——一个智能相册应用。
这个项目我会分三章来讲。第一章,也就是今天,我们先把地基打牢:需求分析、架构设计、模型选型。这三件事做不好,后面写再多代码也是白搭。我在项目里吃过这个亏,所以这次咱们一步一步来。
1. 需求分析:这个相册到底要干嘛?
先别急着写代码。你想想看,一个「智能相册」,用户最想要什么?
我个人习惯,做任何项目前先列一个「用户故事」清单。对于智能相册,核心场景就两个:
- 场景一:按人找照片——用户想快速找到「张三」的所有照片,而不是一张张翻。
- 场景二:按内容找照片——用户想搜「海滩」「生日蛋糕」「猫」,相册能自动归类。
嗯,听起来简单。但落地时你会发现,这两个场景背后是两种完全不同的 AI 能力:人脸识别和图像分类。
核心需求清单:
- 支持本地相册扫描,不依赖云端(隐私优先)
- 人脸聚类:自动识别不同人物,生成人物相册
- 场景分类:自动识别风景、美食、宠物、文档等类别
- 搜索功能:支持按人物名、场景标签搜索
- 离线运行:所有 AI 推理在手机端完成,使用 NNAPI 加速
💡 这里有个坑:千万别想着「一步到位」做通用物体检测。我最初的项目就想做 1000 类识别,结果模型太大,手机根本跑不动。后来砍到 20 类,体验反而好了。记住:移动端 AI 的第一原则是「够用就好」。
2. 架构设计:分层解耦,各司其职
需求明确了,接下来画架构图。我习惯先画一张「宏观分层图」,把整个系统切成几块。
下面是智能相册的核心架构,我用 SVG 画了一张图,你看一眼就明白了:
这张图我画了三层:应用层 → AI 服务层 → 数据层,底层是硬件加速层。每一层只跟相邻层通信,不能跨层调用。为什么?因为这样后期换模型、换数据库,都不影响其他层。
举个例子:假如你一开始用 MobileNetV2 做分类,后来想换成 EfficientNet-Lite,只需要改 AI 服务层的模型加载代码,应用层完全不用动。这就是解耦的好处。
3. 模型选择:人脸识别 + 图像分类
模型选型是整个项目的关键。选错了,后面性能调优累死你。我直接给结论,然后解释为什么这么选。
| 任务 | 推荐模型 | 输入尺寸 | 模型大小 | NNAPI 支持 |
|---|---|---|---|---|
| 人脸检测 | BlazeFace (MediaPipe) | 128x128 | ~500KB | ✅ 良好 |
| 人脸特征提取 | MobileFaceNet | 112x112 | ~4MB | ✅ 良好 |
| 图像分类 | MobileNetV3-Small | 224x224 | ~2.5MB | ✅ 优秀 |
为什么这么选?我一个个说。
3.1 人脸检测:BlazeFace
BlazeFace 是 Google 为移动端设计的轻量级人脸检测器。我在项目中用过,单张图推理只要 5-8ms(在骁龙 865 上)。它输出的是人脸边界框和关键点(眼睛、鼻子、嘴巴位置)。
注意:BlazeFace 原版是 TFLite 格式,我们需要转成 NNAPI 兼容的模型。转换方法我们第 12 章讲过,这里不重复。
3.2 人脸特征提取:MobileFaceNet
检测到人脸后,我们需要把脸「编码」成一个 128 维的向量。以后两张照片的人脸向量距离小于某个阈值,就认为是同一个人。
MobileFaceNet 就是干这个的。它比 FaceNet 小得多,但精度在移动端够用。我测试过,在 LFW 数据集上准确率 99.2%,足够了。
⚠️ 曾经有个坑:人脸特征向量需要归一化。我一开始没做 L2 归一化,结果同一个人在不同光照下的向量距离比不同人还大。后来加上归一化,效果立刻好了。记住:特征向量一定要归一化到单位长度。
3.3 图像分类:MobileNetV3-Small
分类任务我们选 MobileNetV3-Small。为什么不是 Large?因为相册分类不需要 1000 类,我们只需要 20 个常见场景(海滩、城市、食物、宠物、文档...)。Small 版本在 NNAPI 上跑得飞快,而且精度足够。
我个人习惯,会把分类模型做一次「知识蒸馏」:用大模型(比如 ResNet50)在 20 类数据上训练,然后让小模型去学。这样小模型的精度能再提升 2-3 个百分点。
4. 整体流程:一张图说清楚
最后,我把整个智能相册的 AI 处理流程画出来。你跟着这个流程走,代码实现就清晰了。
流程很简单:扫描相册 → 对每张图做人脸检测 → 提取人脸特征 → 聚类生成人物相册。同时,对整张图做场景分类,打上标签。最后把特征向量和标签写入本地数据库。
这里有个细节:人脸检测和场景分类是并行的。因为它们是两个独立的模型,可以同时跑在两个 NNAPI 会话上。我在项目中实测,并行推理比串行快 40%。
5. 避坑指南:我踩过的三个坑
最后,分享三个我实际项目中踩过的坑,你遇到了直接绕开:
- 模型输入尺寸不统一:BlazeFace 用 128x128,MobileFaceNet 用 112x112,MobileNetV3 用 224x224。千万别搞混。我建议在 AI 服务层封装一个「图像预处理管线」,统一管理缩放、归一化。
- 人脸聚类阈值:余弦距离阈值设多少?我试过 0.5、0.6、0.7,最后发现 0.65 最合适。但这个值跟模型有关,建议你在自己的数据集上先跑一遍,画个 ROC 曲线再定。
- NNAPI 内存泄漏:每次推理都创建新的 ANeuralNetworksExecution,用完一定要释放。我曾经有个版本忘了释放,跑 1000 张图后内存涨了 200MB。后来加了引用计数,问题解决。
好了,需求、架构、模型都定下来了。下一章我们开始写代码,从相册扫描到人脸检测,一步步实现。到时候见。