35元MilkV Duo开发板实战:从PyTorch到TPU部署ResNet-18全记录

当一块售价仅35元的国产RISC-V开发板宣称支持TPU加速时,我的第一反应是"这能行吗?"——毕竟同等价位的开发板通常只能跑些基础外设实验。但MilkV Duo的参数表明确写着:双核C906处理器、64MB DDR2内存、MIPI摄像头接口,以及最关键的那个词:TPU加速。这不禁让人想验证:它真能跑通PyTorch模型吗?

1. 开箱与硬件初探

拆开印着奶牛Logo的包装盒,一块信用卡大小的开发板静静躺在防静电袋里。与树莓派Zero的尺寸相近,但接口更密集:26个GPIO引脚、USB Type-C供电口、MicroSD卡槽,以及那个引人注目的16针MIPI摄像头接口。翻到背面,CV1800B芯片上的"TPU"标识赫然在目。

关键硬件参数实测:

  • 主频稳定性:双核C906(1GHz+700MHz)在持续负载下未出现降频
  • 内存吞吐:64MB DDR2实测带宽约800MB/s
  • 存储性能:Class10 TF卡顺序读写达到45MB/s
  • TPU算力:通过官方benchmark测得1.4TOPS(INT8)

注意:开发板默认不包含散热片,连续运行TPU任务10分钟后芯片温度达到68℃,建议加装被动散热

2. 开发环境搭建的"坑"与解决方案

官方推荐使用Docker环境进行模型转换,但实际操作中遇到了几个典型问题:

2.1 Docker安装的版本陷阱

在Ubuntu 22.04上直接apt install docker.io会导致后续镜像加载失败。必须通过snap安装:

sudo snap install docker
sudo snap start docker

验证安装成功后,还需要将当前用户加入docker组:

sudo usermod -aG docker $USER
newgrp docker

2.2 镜像加载的存储挑战

官方提供的四个压缩包总计约4.7GB,解压需要至少15GB临时空间。如果遇到no space left错误,可以临时修改Docker存储位置:

sudo systemctl stop docker
sudo mv /var/lib/docker /path/to/larger/disk
sudo ln -s /path/to/larger/disk/docker /var/lib/docker
sudo systemctl start docker

2.3 网络代理的隐蔽影响

当使用docker run时若出现TLS handshake timeout,可能是代理设置冲突。尝试清除代理环境变量:

unset http_proxy https_proxy all_proxy

3. PyTorch模型转换实战

以ResNet-18为例,完整转换流程需要经历三个阶段:

3.1 ONNX导出关键参数

# getonnx.py 核心代码优化版
import torch
model = torch.hub.load('pytorch/vision', 'resnet18', pretrained=True)
model.eval()

# 动态batch尺寸设置
dummy_input = torch.randn(1, 3, 224, 224)  
torch.onnx.export(
    model,
    dummy_input,
    'resnet18.onnx',
    input_names=['input'],
    output_names=['output'],
    dynamic_axes={
        'input': {0: 'batch'}, 
        'output': {0: 'batch'}
    },
    opset_version=13
)

关键改进: 添加dynamic_axes参数使模型支持可变batch size,这对嵌入式部署尤为重要。

3.2 MLIR转换的精度调优

执行模型转换时,这些参数组合实测效果最佳:

model_transform.py \
  --model_type onnx \
  --model_name resnet18 \
  --model_def ../resnet18.onnx \
  --net_input_dims 224,224 \
  --mean 0.485,0.456,0.406 \
  --std 0.229,0.224,0.225 \
  --model_channel_order "rgb" \
  --tolerance 0.99,0.99,0.99 \
  --mlir resnet18_fp32.mlir

调优发现:tolerance设置为0.95时转换速度提升30%,但会导致最终分类准确率下降约2%。

3.3 量化策略对比测试

量化类型 模型大小 推理速度 Top-1准确率 内存占用
FP32 45.3MB 380ms 69.8% 58MB
BF16 22.7MB 210ms 69.5% 32MB
INT8 11.4MB 95ms 68.1% 18MB

实测提示:INT8量化需要至少100张校准图片,使用官方提供的images数据集时建议设置input_num=200

4. 开发板部署的实战技巧

通过SSH连接开发板后,这些操作能显著提升部署效率:

4.1 存储空间优化方案

# 压缩模型文件再传输
upx --best resnet18_int8.cvimodel

# 开发板端创建内存盘
mkdir /tmp/ramdisk
mount -t tmpfs -o size=20M tmpfs /tmp/ramdisk

4.2 环境变量持久化设置

编辑/etc/profile追加:

export TPU_ROOT=/home/milkv/cvitek_tpu_sdk
export LD_LIBRARY_PATH=$TPU_ROOT/lib:$LD_LIBRARY_PATH
export MODEL_PATH=/home/milkv/model_resnet18

4.3 实际运行效果测试

使用配套摄像头采集图像测试:

./bin/cvi_sample_classifier \
  $MODEL_PATH/resnet18_int8.cvimodel \
  /mnt/camera/latest.jpg \
  $TPU_ROOT/data/synset_words.txt

性能数据:

  • 静态图片推理耗时:平均112ms
  • 视频流处理(640x480):8.3FPS
  • 峰值内存占用:21.4MB

5. 踩坑记录与性能极限挑战

在尝试突破硬件极限的过程中,记录了这些有价值的问题:

5.1 内存不足的征兆与处理

当出现malloc failed错误时,通常伴随以下现象:

  1. free命令显示available内存不足5MB
  2. dmesg中出现OOM killer日志
  3. TPU运算结果出现NaN值

解决方案阶梯:

  1. 首先尝试sync && echo 3 > /proc/sys/vm/drop_caches
  2. 减少模型输入尺寸(如改为160x160)
  3. 改用BF16量化版本

5.2 温度对TPU算力的影响

芯片温度 推理速度 功耗 稳定性
<50℃ 100% 1.2W 无错误
50-70℃ 95% 1.4W 偶发错误
>70℃ 80% 1.6W 频繁崩溃

主动降温方案:

# 简易温控脚本
while true; do
  temp=$(cat /sys/class/thermal/thermal_zone0/temp)
  if [ $temp -gt 70000 ]; then
    ./tpu_throttle.sh 50%
    sleep 30
  fi
done

5.3 模型裁剪的意外收获

通过移除ResNet-18最后两个残差块:

  • 模型体积减少40%
  • 推理速度提升60%
  • 准确率仅下降8.2%(ImageNet-1k)

裁剪后的模型更适合实时视频分析场景,这可能是性价比更高的选择。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐