树莓派Zero部署谷歌Teachable Machine模型:边缘AI实战指南
1. 项目概述:当树莓派Zero遇上谷歌Teachable Machine
最近在捣鼓一个挺有意思的小项目:在树莓派Zero上跑谷歌的Teachable Machine。这听起来可能有点“小马拉大车”的感觉,毕竟树莓派Zero的性能和资源都相当有限,而Teachable Machine作为谷歌推出的一个在线机器学习模型训练工具,通常给人的印象是在浏览器里点点鼠标就能训练出图像、声音或姿态识别模型。但把训练好的模型部署到Zero这块小小的板子上,让它脱离云端、脱离强大的电脑,在本地独立运行,这背后的想法和实践,对于想做嵌入式AI、边缘智能设备的朋友来说,非常有吸引力。
简单来说,这个项目的核心就是将云端训练的轻量级机器学习模型,部署到资源极度受限的嵌入式硬件(树莓派Zero)上,实现离线、低功耗的实时推理。它能做什么?比如,你可以训练一个识别特定手势的模型,让Zero控制一个玩具小车;或者训练一个声音分类模型,让Zero在听到特定关键词(如“开灯”)时做出反应;甚至做一个简单的垃圾分类小装置。它特别适合那些对AI和物联网感兴趣,想从“玩具级”项目入手,深入理解模型训练、转换、优化和嵌入式部署全流程的开发者、创客和学生。
为什么是树莓派Zero?因为它便宜、功耗极低、体积小巧,是很多微型嵌入式项目的首选。为什么是Teachable Machine?因为它极大地降低了机器学习的入门门槛,无需编写复杂的训练代码,通过网页界面和摄像头/麦克风就能快速收集数据、训练模型。将两者结合,正是探索“边缘AI”落地最直接、最有趣的路径之一。接下来,我将详细拆解从模型训练、导出、转换,到在Zero上部署运行的完整过程,并分享其中踩过的坑和积累的经验。
2. 核心思路与技术选型解析
2.1 为什么选择Teachable Machine + 树莓派Zero组合?
这个组合的核心优势在于极低的入门门槛与完整的流程体验。对于初学者或快速原型开发,Teachable Machine解决了最头疼的数据标注和模型训练问题。你不需要安装TensorFlow或PyTorch,不需要理解损失函数和优化器,甚至不需要写一行训练代码。它的图形化界面让你能专注于“教”机器认识什么,而不是“编”程序怎么学。
而选择树莓派Zero作为部署平台,则是对“边缘计算”概念的一次亲手实践。边缘计算强调在数据产生的源头就近处理,减少对云端的依赖,降低延迟、保护隐私、节省带宽。树莓派Zero作为一款售价仅数十元、功耗仅1W左右的微型计算机,是验证边缘AI想法的绝佳沙盒。在这个项目里,我们追求的不是极高的准确率或复杂的模型,而是在严苛的资源限制(单核CPU、512MB内存)下,让一个有用的AI功能跑起来。
技术路径上,Teachable Machine训练出的模型可以导出为多种格式,其中对我们最有用的两种是:TensorFlow.js模型和TensorFlow Lite模型。前者主要用于Web环境,后者则是为移动和嵌入式设备设计的轻量级格式。我们的目标就是将模型最终转换为能在树莓派Zero上高效运行的TensorFlow Lite格式。
2.2 关键挑战与应对策略
在Zero上部署模型,主要面临三大挑战:
- 性能瓶颈:Zero的ARM11单核处理器主频仅1GHz,性能孱弱。直接运行未经优化的模型,推理速度可能慢到无法接受(例如数秒一帧)。
- 内存限制:512MB的共享内存需要同时承载操作系统、Python环境、运行时库和模型本身,容易导致内存不足。
- 模型兼容性:Teachable Machine导出的模型需要经过正确的转换和优化,才能与树莓派上的TensorFlow Lite运行时完美配合。
应对策略如下:
- 模型轻量化:优先使用Teachable Machine中结构简单的模型(如MobileNetV2的轻量级变体),并在导出时选择较小的输入尺寸(如224x224甚至更小)。
- 格式转换与优化:确保将模型转换为TensorFlow Lite格式,并可能使用量化技术(如INT8量化)来减小模型体积、提升推理速度,这对Zero至关重要。
- 高效的代码实现:在Python中使用
tflite_runtime而非完整的TensorFlow包,以节省资源和加速加载。同时,优化图像预处理和结果后处理的代码逻辑。
3. 从训练到部署:完整实操流程
3.1 第一步:使用Teachable Machine训练模型
首先,访问Teachable Machine官网。我们以最常用的“图像项目”为例。
- 收集数据:为每个类别(例如“有猫”、“无猫”)采集样本。直接用电脑摄像头或上传图片均可。每个类别建议至少提供30-50个样本,尽可能覆盖不同的角度、光照和背景,以提高模型泛化能力。
- 训练模型:点击“训练模型”按钮。这里有一个关键选择:训练时长。对于在Zero上部署,我建议选择“标准”或“更少”的训练周期。过长的训练可能导致模型轻微过拟合,且对最终在嵌入式设备上的精度提升有限,反而可能增加模型复杂度。Teachable Machine底层使用的是迁移学习,基于预训练网络(如MobileNet)进行微调,因此训练很快。
- 导出模型:训练完成后,进入“导出模型”页面。
- 导出格式:选择“TensorFlow Lite”。这是为嵌入式设备准备的格式。
- 模型类型:选择“量化”。量化会将模型权重从浮点数转换为整数,能显著减小模型文件大小(可缩减至1/4)并加速推理,这对树莓派Zero是必选项。精度可能会有微小损失,但在大多数简单分类任务中可接受。
- 下载:点击“下载我的模型”。你会得到一个压缩包,里面包含
model.tflite和labels.txt文件。
注意:在训练时,务必考虑树莓派Zero的实际使用场景。如果你最终用Zero的摄像头模块采集图像,那么在Teachable Machine上收集数据时,最好也用类似的摄像头,并模拟相似的环境,这样可以减少“领域差距”,提升部署后的准确率。
3.2 第二步:准备树莓派Zero开发环境
在将模型部署到Zero之前,需要先准备好它的软件环境。由于Zero性能弱,推荐使用轻量级的操作系统,如Raspberry Pi OS Lite(无桌面环境),并通过SSH进行远程操作。
- 系统安装与基础配置:将Raspberry Pi OS Lite镜像刷入SD卡,启动并完成基础设置(地区、语言、网络等)。建议启用SSH并配置Wi-Fi,方便无头运行。
- 安装必要软件包:
sudo apt update sudo apt upgrade -y sudo apt install -y python3-pip python3-pil python3-numpy libatlas-base-devpython3-pip:Python包管理器。python3-pil(Pillow):用于图像处理。python3-numpy:科学计算库,许多AI框架依赖它。libatlas-base-dev:优化数学库,能加速NumPy运算。
- 安装TensorFlow Lite运行时:不要安装完整的TensorFlow,那对Zero来说太庞大。安装专门为嵌入式设备优化的
tflite_runtime。
这个索引源提供了针对ARM架构预编译的轮子,安装更快更稳定。pip3 install --extra-index-url https://google-coral.github.io/py-repo/ tflite_runtime
3.3 第三步:模型部署与推理代码编写
将下载的model.tflite和labels.txt文件上传到树莓派Zero上。然后,我们编写一个Python脚本进行推理。这里以使用树莓派官方摄像头(CSI接口)为例。
import tflite_runtime.interpreter as tflite from PIL import Image import numpy as np import picamera import time import io # 1. 加载标签 with open('labels.txt', 'r') as f: labels = [line.strip() for line in f.readlines()] # 2. 加载TFLite模型并分配张量 interpreter = tflite.Interpreter(model_path='model.tflite') interpreter.allocate_tensors() # 3. 获取输入输出详情 input_details = interpreter.get_input_details() output_details = interpreter.get_output_details() # 通常Teachable Machine导出的是浮点量化模型,输入是uint8 height = input_details[0]['shape'][1] width = input_details[0]['shape'][2] # 4. 初始化摄像头 camera = picamera.PiCamera() camera.resolution = (width, height) # 设置摄像头分辨率与模型输入一致 camera.rotation = 180 # 根据摄像头安装方向调整 time.sleep(2) # 让摄像头预热 def preprocess_image(image_data): """预处理图像:转换为数组,调整尺寸,归一化(如果需要)""" image = Image.open(io.BytesIO(image_data)).convert('RGB') image = image.resize((width, height)) # 对于量化模型,输入通常是uint8,无需归一化到0-1 input_array = np.array(image, dtype=np.uint8) # 添加批次维度 (1, height, width, 3) input_array = np.expand_dims(input_array, axis=0) return input_array try: while True: # 捕获图像到内存流 stream = io.BytesIO() camera.capture(stream, format='jpeg', use_video_port=True) # use_video_port更快 stream.seek(0) # 预处理 input_data = preprocess_image(stream.getvalue()) # 设置输入张量 interpreter.set_tensor(input_details[0]['index'], input_data) # 执行推理 start_time = time.time() interpreter.invoke() inference_time = time.time() - start_time # 获取输出 output_data = interpreter.get_tensor(output_details[0]['index']) # output_data形状通常是 (1, num_classes) predicted_class_idx = np.argmax(output_data[0]) confidence = output_data[0][predicted_class_idx] # 对于量化模型,输出可能是整数,需要根据模型详情解量化 # Teachable Machine导出的量化模型,输出通常已经是近似的概率值 # 简单处理:将整数输出除以255.0(如果输出是0-255的整数)或直接使用 # 更严谨的做法是检查output_details中的量化参数 scale, zero_point = output_details[0]['quantization'] if scale: # 如果存在量化参数 confidence = scale * (confidence - float(zero_point)) predicted_label = labels[predicted_class_idx] print(f"预测: {predicted_label}, 置信度: {confidence:.2f}, 推理时间: {inference_time:.3f}秒") time.sleep(1) # 控制推理频率 except KeyboardInterrupt: print("程序停止") finally: camera.close()代码关键点解析:
use_video_port=True:在picamera中,这能利用视频端口进行捕获,速度比静态捕获模式快很多,对于实时推理至关重要。- 量化处理:代码中包含了处理量化模型输出的逻辑。这是部署量化模型时最容易出错的地方。务必通过
output_details检查输出的量化参数(scale和zero_point),并据此将整数输出转换回浮点数概率。Teachable Machine导出的模型通常已处理好,但显式检查是良好习惯。 - 输入数据类型:通过
input_details确认模型期望的输入数据类型(dtype)是uint8还是float32。我们的代码按uint8处理,因为之前导出时选择了量化。
3.4 第四步:性能优化与实用化调整
在Zero上直接运行上述代码,你可能会发现推理速度在1-3秒左右,这离“实时”还有差距。以下是几个优化方向:
- 降低输入分辨率:重新在Teachable Machine中训练模型时,尝试选择更小的输入尺寸(如128x128)。这是提升速度最有效的方法,但可能会降低精度。
- 模型量化:确保导出时选择了“量化”选项。如果第一次没有,需要重新导出。
- 减少预处理开销:
PIL的resize操作可能较慢。可以尝试使用picamera的resize参数在捕获时直接生成小图,或者使用OpenCV(如果安装)的cv2.resize,但安装OpenCV在Zero上比较繁琐。 - 调整推理频率:如果不是每帧都需要,可以降低推理频率,比如每5秒或检测到画面有变化时才推理。
- 使用Coral USB加速器(如果预算允许):这是一个为边缘设备设计的AI加速棒,通过USB连接到树莓派Zero,可以大幅提升TensorFlow Lite模型的推理速度(数十倍提升)。但这需要模型编译成适用于Coral的版本(
.tflite兼容,但需特定编译),且增加了成本。
4. 常见问题与排查技巧实录
在实际操作中,你几乎一定会遇到下面这些问题。这里是我的排查记录和解决方案。
4.1 模型在电脑上测试正常,在Zero上预测结果混乱
- 现象:推理能执行,但输出的置信度都很低,或者类别预测完全错误。
- 排查:
- 检查输入预处理:这是最常见的原因。对比电脑上测试时的预处理代码和Zero上的代码是否完全一致。重点关注:颜色通道顺序(RGB vs BGR)、归一化范围(0-255 vs 0-1)、图像尺寸(是否严格resize到模型输入大小)。
- 检查量化参数:如果使用了量化模型,必须正确处理输入输出的量化/反量化。使用
input_details和output_details打印出量化参数,确保代码中的处理逻辑正确。 - 验证模型和标签:再次确认上传到Zero的
model.tflite和labels.txt文件是最新导出的,且没有损坏。可以计算一下文件的MD5哈希值进行对比。
- 解决:我通常会在预处理函数中加入调试语句,打印出输入数组的形状、数据类型和前几个像素值,与在电脑上运行时的值进行比对。确保完全一致。
4.2 运行脚本时出现内存不足(Memory Error)或卡死
- 现象:运行一段时间后程序崩溃,或系统响应极其缓慢。
- 排查:
- 检查内存使用:在另一个SSH会话中运行
free -h命令,查看可用内存。树莓派Zero的512MB内存非常紧张。 - 检查交换空间:运行
swapon --show和df -h,查看交换分区是否启用以及空间是否充足。默认的Raspberry Pi OS可能会启用100MB的交换文件。
- 检查内存使用:在另一个SSH会话中运行
- 解决:
- 增加交换空间:这是最直接的方法。可以编辑
/etc/dphys-swapfile文件,将CONF_SWAPSIZE从100增加到512(单位MB),然后重启交换服务:sudo systemctl restart dphys-swapfile。注意,这会影响SD卡寿命,因为频繁读写。 - 优化代码,释放资源:确保在循环中及时释放大对象(如大的图像数据流)。使用
del语句或确保变量离开作用域。 - 关闭不必要的进程:用
sudo systemctl stop停止不需要的系统服务(如蓝牙、桌面环境相关服务,如果你用的是Lite版则已最小化)。
- 增加交换空间:这是最直接的方法。可以编辑
4.3 摄像头初始化失败或图像捕获异常
- 现象:
picamera模块报错,如“Camera is not enabled”或捕获的图像全黑/花屏。 - 排查:
- 检查硬件连接:确保CSI排线已正确插入,摄像头模块已通过
sudo raspi-config在接口选项中启用。 - 检查摄像头独占访问:确保没有其他程序(如
libcamera相关进程)正在占用摄像头。可以尝试重启树莓派。 - 检查供电:树莓派Zero的供电要求是5V/1.2A以上。供电不足可能导致摄像头无法正常工作。使用质量好的电源和USB线。
- 检查硬件连接:确保CSI排线已正确插入,摄像头模块已通过
- 解决:一个实用的技巧是在初始化摄像头后,增加一个
time.sleep(2),给摄像头传感器和自动对焦(如果有)足够的启动时间。对于picamera,捕获静态图片时使用camera.capture(),而用于视频流或连续捕获时,使用camera.capture(..., use_video_port=True)能获得更稳定的性能。
4.4 推理速度太慢,无法满足实时性要求
- 现象:每张图片推理时间超过2秒。
- 排查与解决:
- 基准测试:单独测试模型推理时间(不包括图像捕获和预处理)。在代码中只对一张静态图片反复进行
interpreter.invoke(),计算平均时间。这能确定瓶颈是模型本身还是IO/预处理。 - 模型层面:
- 换用更小的模型:在Teachable Machine中重新训练,选择更小的输入尺寸和更轻量的基础网络(如果有选项)。
- 确认量化:模型必须是量化过的(
.tflite)。
- 代码层面:
- 使用
tflite_runtime:确保没有误装完整的tensorflow。 - 预热:在正式循环前,先对一张虚拟图片执行一次
allocate_tensors()和invoke(),这能完成运行时的一些初始化,使后续推理时间更稳定。 - 减少操作:如前述,尝试优化图像读取和resize的流程。
- 使用
- 基准测试:单独测试模型推理时间(不包括图像捕获和预处理)。在代码中只对一张静态图片反复进行
5. 项目扩展与进阶思路
让模型在Zero上跑起来只是第一步。一个完整的项目通常需要将其集成到一个具体的应用场景中。
5.1 与硬件交互:从识别到控制
假设我们做了一个“手势开关灯”的项目。当识别出“开灯”手势时,控制GPIO引脚输出高电平,驱动一个继电器模块打开台灯。
import RPi.GPIO as GPIO # GPIO设置 LED_PIN = 17 GPIO.setmode(GPIO.BCM) GPIO.setup(LED_PIN, GPIO.OUT) # 在主循环的推理结果判断部分加入 if predicted_label == 'open_hand' and confidence > 0.8: # 置信度阈值 GPIO.output(LED_PIN, GPIO.HIGH) print("灯已打开") elif predicted_label == 'fist' and confidence > 0.8: GPIO.output(LED_PIN, GPIO.LOW) print("灯已关闭")注意事项:GPIO操作涉及硬件,务必注意安全。添加适当的防抖逻辑(例如,连续识别到3次同一手势再触发动作),避免因误识别导致设备频繁开关。
5.2 模型更新与持续学习
Teachable Machine训练好的模型是静态的。如果部署后发现了新的识别错误,或者想增加新的类别,传统做法是回到网页重新训练、导出、再部署。我们可以设计一个简单的机制,让Zero在本地收集“困难样本”(即低置信度或错误分类的图片),并保存下来。定期将这些样本传回给主电脑,用于在Teachable Machine上增量训练,生成新模型后再更新到Zero。这就构成了一个简单的“边缘数据采集-中心模型优化”的闭环。
5.3 探索其他传感器与模型类型
Teachable Machine不仅支持图像,还支持声音分类和姿态识别。
- 声音项目:可以训练一个关键词识别模型,比如识别拍手声、口哨声或简单的语音命令。在Zero上部署时,需要使用麦克风采集音频,并按照模型要求的格式(如MFCC特征)进行预处理。这涉及到音频信号处理库(如
librosa,但在Zero上安装可能较慢)。 - 姿态项目:训练一个识别身体姿势(如站立、坐下、举手)的模型。这需要使用PoseNet等姿态估计算法,对Zero的计算能力要求更高。可能需要使用Teachable Machine导出的TensorFlow.js模型,并通过
tfjs的Node版本在Zero上运行,或者寻找更轻量级的TFLite姿态估计模型。
在树莓派Zero这块小小的板子上成功运行自己训练的AI模型,那种成就感是巨大的。它让你真切地触摸到“智能”从云端落到实体的过程。整个过程里,最大的收获往往不是最终的识别准确率有多高,而是在解决一个个具体问题中积累的经验:如何为资源受限的环境优化模型,如何调试跨平台的推理代码,如何平衡速度与精度。这个项目就像一个微缩的实验室,涵盖了边缘AI应用从数据到部署的完整链条。如果你也感兴趣,不妨就从手头的一个小想法开始,用Teachable Machine和树莓派Zero把它实现出来。
