跳转至

边缘推理

一句话总结:边缘推理让模型直接跑在手机、笔电与 IoT 设备上,不上云——靠一整套压缩流水线 (蒸馏 → 剪枝 → 量化 → 编译) 把大模型塞进算力/内存/功耗都受限的设备里。

边缘推理让模型在用户设备 (手机、笔电、IoT 传感器) 本地运行,数据不出设备。本节涵盖边缘端约束、模型压缩流水线、设备端运行时、编译器栈、硬件目标 (NPU、Neural Engine)、端侧大语言模型、联邦学习,以及延迟优化。

  • 云端推理需要网络连接,引入延迟 (往返 50-200 ms),每次请求都要花钱,而且把用户数据交给第三方服务器。边缘推理 (edge inference) 一次性干掉这四点:模型本地跑,响应瞬时,每次推理零成本,数据留在设备内。

  • 代价:边缘设备的算力和内存比数据中心 GPU 少 100-1000 倍。想让模型在这么苛刻的约束下跑起来,必须在每一层都做激进优化。

  • Cactus (github.com/cactus-compute/cactus) 是一款专为移动和可穿戴设备打造的低延迟 AI 引擎。它在生产环境里落地了本节涵盖的很多技术:为注意力和矩阵运算定制的 ARM 单指令多数据 (Single Instruction Multiple Data, SIMD) 内核 (第 16 章)、KV 缓存量化 (第 17 章 file 01)、分块 prefill、在 Apple 和 Qualcomm 芯片上的神经处理单元 (Neural Processing Unit, NPU) 加速推理、把内存占用降到原来 1/10 的零拷贝内存映射,以及当设备端算力不足时的自动云端回退。Cactus 支持跨 iOS、Android、macOS 和嵌入式 Linux 的多模态推理 (大语言模型 (Large Language Model, LLM)、视觉、语音),并提供 Swift、Kotlin、Python、Flutter、React Native 和 Rust 的 SDK。它的 benchmark 显示:一个 INT4 量化的 1.2B 模型,在 M4 Pro 上解码达到 100 tokens/s,在 iPhone 17 Pro 上达到 48 tokens/s——这是"优化到位的边缘推理"到底能做到什么的一个具体例子。

边缘端约束

资源 云端 GPU (H100) 笔电 (M4) 手机 (Snapdragon 8 Gen 3) IoT (ESP32)
RAM 80 GB HBM3 16-36 GB 统一内存 8-12 GB LPDDR5 520 KB
算力 989 TFLOPS (FP8) 38 TOPS (Neural Engine) 45 TOPS (NPU) 0.001 TOPS
功耗 700 W 15-30 W 5-10 W 0.1 W
存储 TB 级 256 GB-2 TB 128-512 GB 4 MB
  • 云端 GPU 和手机 NPU 之间的算力差距大约是 20 倍。GPU 和微控制器之间的差距是 100 万倍左右。不同的设备需要不同程度的压缩,也需要不同的模型架构。

模型压缩流水线

  • 对于边缘部署,压缩不是单一技术——它是把若干互补技术按顺序串起来的流水线:
Full model (FP32, 70B params)
    ↓ Knowledge distillation → smaller model (7B params)
    ↓ Structured pruning → remove redundant heads/layers (4B effective)
    ↓ Quantisation (INT4) → 4x smaller (2 GB)
    ↓ Compiler optimisation → fused kernels, optimised memory layout
    ↓ Runtime → on-device execution
  • 每一步都会减小体积、降低延迟。顺序很关键:先蒸馏 (缩减架构),再剪枝 (去掉结构),然后量化 (降低精度),最后编译 (针对目标硬件优化)。如果先量化再蒸馏,就是在压缩一个已经有损的模型。

设备端运行时

  • 运行时 (runtime) 负责加载模型、分配内存,并在目标硬件上执行推理。每个平台都有自己偏好的运行时:

  • ONNX Runtime:跨平台 (Windows、Linux、macOS、iOS、Android)。支持 CPU、GPU (统一计算设备架构 (Compute Unified Device Architecture, CUDA)、DirectML、CoreML、NNAPI),以及众多加速器后端。可移植性最好的选择。模型从 PyTorch/TensorFlow 导出为 ONNX 格式。

  • TensorFlow Lite (TFLite):谷歌的边缘运行时。针对 ARM CPU 和 Android NPU 优化。二进制体积极小 (约 1 MB)。支持 INT8 和 float16。Android 部署的事实标准。

  • Core ML: Apple 为 iOS/macOS 打造的运行时。根据模型特征自动决定用 Neural Engine、GPU 还是 CPU。用 coremltools 从 PyTorch/TensorFlow 转换过来。与 Apple 硬件 (统一内存、Neural Engine) 深度整合。

  • ExecuTorch: Meta 推出的、面向设备端 PyTorch 的新运行时。为边缘部署设计,支持提前编译 (ahead-of-time compilation) 和算子级别的硬件加速器委派。PyTorch Mobile 的继任者。

  • TensorRT: NVIDIA 的 GPU 推理优化运行时 (第 15 章)。融合层、挑选最优内核,并自动做量化。在 NVIDIA GPU 上比 PyTorch eager 模式快 2-5 倍。

  • llama.cpp:单文件 C++ 大语言模型推理引擎。支持 GGUF 量化 (Q4、Q5、Q8)、CPU (AVX/NEON)、Metal (Apple GPU)、CUDA 和 Vulkan。在消费级硬件上跑大语言模型的首选。

编译器栈

  • 高层模型 (PyTorch 计算图) 与硬件 (NPU 指令) 之间,是编译器栈 (compiler stack),它针对具体目标做优化:
PyTorch model
    ↓ Export (torch.export, ONNX, TorchScript)
Graph IR (intermediate representation)
    ↓ Graph optimisations
        - Constant folding (compute constant expressions at compile time)
        - Dead code elimination (remove unused operations)
        - Operator fusion (conv + bn + relu → single fused op)
        - Layout transformation (NCHW → NHWC for ARM, channels-last)
    ↓ Lowering
Hardware-specific IR
    ↓ Backend optimisations
        - Tiling and loop ordering (cache-friendly access patterns)
        - Vectorisation (SIMD, chapter 16)
        - Memory planning (reuse buffers to minimise peak memory)
        - Kernel selection (choose the best implementation per op)
    ↓ Code generation
Machine code / NPU instructions
  • 算子融合 (operator fusion) 是影响最大的一项优化。一个 Transformer 块大约有 20 个算子 (matmul、add、layernorm、softmax 等)。不做融合的话,每个算子都要把结果写回内存,下一个算子再读回来。做了融合,多个算子被合并成一个内核,数据一直留在寄存器/缓存里。这能带来 2-5 倍的加速 (第 16 章,Roofline 模型)。

  • 内存规划 (memory planning):编译器分析模型计算图,找出哪些张量在生命周期上不重叠,可以共用同一块内存缓冲区。一个有 100 个中间张量的模型可能只需要 10 份内存空间——因为大多数张量在别的张量诞生之前就已经被消费并释放了。这在 RAM 受限的设备上至关重要。

硬件目标

移动 GPU

  • Qualcomm Adreno (Android):支持 OpenCL、Vulkan compute (第 16 章),以及 Qualcomm 自家的 SNPE (Snapdragon Neural Processing Engine)。Adreno GPU 有 256-1024 个 ALU,支持 FP16 和 INT8。

  • ARM Mali (Android):支持 OpenCL 和 Vulkan。Mali GPU 采用基于图块 (tile-based) 的架构 (与桌面 GPU 不同),这会影响最佳内存访问模式。

  • Apple GPU (iOS/macOS):通过 Metal (Apple 的 GPU API) 访问。统一内存架构意味着没有 CPU↔GPU 拷贝开销。Metal Performance Shaders (MPS) 提供了优化过的 ML 基本原语。

神经处理单元 (Neural Processing Unit, NPU)

  • NPU 是专门为 ML 推理设计的固定功能加速器。对于标准 ML 算子 (matmul、conv、激活函数),它们比 GPU 能效高得多。

  • Apple Neural Engine: 16 核,约 38 TOPS (INT8)。通过 Core ML 访问。特别适合视觉模型和设备端扩散模型。不能跑任意代码——只跑 Core ML 支持的算子。

  • Qualcomm Hexagon NPU:集成在 Snapdragon SoC 里。支持 INT8 和 INT4 推理。通过 SNPE 或带 QNN 后端的 ONNX Runtime 访问。为背景虚化、语音识别、实时翻译等设备端功能提供算力。

  • Google Edge TPU:云端张量处理单元 (Tensor Processing Unit, TPU) 的低功耗小型版本。4 TOPS, 2 W。用在 Coral 设备上做设备端推理。只支持 INT8 量化的 TFLite 模型。

  • 委派模式 (delegation pattern):运行时把模型计算图切分——NPU 跑它支持的算子,CPU 跑不支持的部分。让尽量多的算子跑在 NPU 上,是性能和能效的关键。

端侧大语言模型

  • 得益于小模型加上激进量化,在手机和笔电上跑大语言模型已经可行:
模型 参数量 量化后大小 目标设备 性能
Phi-3 Mini 3.8B ~2 GB (Q4) 手机/笔电 iPhone 15 上 ~15 tokens/s
Gemma 2B 2B ~1.5 GB (Q4) 手机 Pixel 8 上 ~20 tokens/s
Llama 3.2 1B 1B ~700 MB (Q4) 手机 ~30 tokens/s
Llama 3.2 3B 3B ~2 GB (Q4) 手机/笔电 ~15 tokens/s
Llama 3.1 8B 8B ~4.5 GB (Q4) 笔电 M2 上 ~20 tokens/s
  • 挑战:

    • 内存:一个 3B Q4 模型能塞进 2 GB,但长对话的 KV 缓存会加上不少额外开销。手机上上下文长度通常被限制在 2-4K token。
    • 热节流:持续推理会让手机发热。持续生成 30 秒后,SoC 会降频以防过热,性能掉 30-50%。
    • 电池: 3B 模型以 15 tokens/s 跑,大约耗电 3-5 W。30 分钟对话会耗掉一部典型手机约 5% 电量。偶尔用可以接受,常驻应用就有麻烦。
  • llama.cpp 是端侧大语言模型的事实标准。它能跑在 CPU (AVX2、NEON、I8MM)、Apple GPU (Metal)、NVIDIA GPU (CUDA)、AMD GPU (ROCm/Vulkan) 上,甚至能跑在手机上 (Android 上通过 Termux)。

联邦学习

  • 联邦学习 (federated learning) 让模型在成千上万台设备上训练,但不集中数据。每台设备在本地数据上训练,计算一个梯度更新,只把更新 (不是数据) 发给中心服务器,服务器再把这些更新聚合起来。

  • 算法 (FedAvg):

    1. 服务器把当前模型发给选中的 \(K\) 台设备。
    2. 每台设备在本地数据上把模型微调若干步。
    3. 每台设备把更新后的模型 (或者差值) 发回服务器。
    4. 服务器对更新做平均:\(W_{\text{new}} = \frac{1}{K} \sum_{k=1}^{K} W_k\)。
    5. 重复。
  • 隐私:原始数据从不离开设备。服务器只看到聚合后的模型更新。差分隐私 (differential privacy) 会给更新加噪声,让人没法从梯度反推出单条数据。

  • 通信效率:模型更新很大 (和模型本身一样大)。压缩技术可以减轻这一点:梯度量化 (发 INT8 梯度而非 FP32)、稀疏化 (只发最大的那些梯度),以及梯度累积 (本地多训几步,少发几次)。

  • 应用:谷歌键盘的输入预测 (Gboard)、Apple 的语音识别、健康监测 (在敏感健康数据上训练,且不集中数据)。

延迟优化

  • 除了压缩之外,还有几种技术可以减少端到端推理延迟:

  • 提前退出 (early exit):在中间层加分类头。如果模型在第 6 层 (共 24 层) 就有把握了,就直接返回预测,不再跑 7-24 层。简单输入提前退出,困难输入才走完整模型。对"简单和困难混合"的任务,平均延迟会显著下降。

  • 模型切分 (model partitioning):把模型切开——NPU 跑高效的 matmul, GPU 跑不规则算子,CPU 跑剩下的一切。编译器根据 profiling 决定每个算子去哪里。

  • 缓存:对于有重复查询的应用 (自动补全、代码补全),缓存最近的计算结果。比如用户敲了 "How do I",模型最近刚为 "How do I" 生成过补全,那么可以复用缓存的 KV 缓存,完全跳过 prefill 阶段。

  • 推测性预取 (speculative prefetching):预测用户接下来会做什么,在他们提问之前就开始推理。聊天应用可以在用户还在读当前回答时,就为最可能的后续问题开始生成回答。

编码任务 (用 CoLab 或 notebook)

  1. 模拟模型压缩流水线。从一个 float32 模型出发,依次做蒸馏 (模拟)、剪枝、量化,追踪每一步的体积。

    def compression_pipeline(original_params_M, original_bits=32):
        size_mb = original_params_M * 1e6 * original_bits / 8 / 1e6
    
        print(f"Original: {original_params_M}M params, {original_bits}-bit → {size_mb:.0f} MB")
    
        # 第 1 步: 知识蒸馏 (减少参数量)
        distilled_params = original_params_M * 0.15  # 70B → ~10B equivalent
        size_mb = distilled_params * 1e6 * original_bits / 8 / 1e6
        print(f"After distillation ({distilled_params:.0f}M params): {size_mb:.0f} MB")
    
        # 第 2 步: 结构化剪枝 (在剩余参数上再砍掉 30%)
        pruned_params = distilled_params * 0.7
        size_mb = pruned_params * 1e6 * original_bits / 8 / 1e6
        print(f"After pruning ({pruned_params:.0f}M params): {size_mb:.0f} MB")
    
        # 第 3 步: INT4 量化
        size_mb = pruned_params * 1e6 * 4 / 8 / 1e6
        print(f"After INT4 quantisation: {size_mb:.0f} MB")
    
        print(f"Total compression: {original_params_M * 1e6 * original_bits / 8 / 1e6 / size_mb:.0f}x")
    
    print("=== Starting from 70B model ===")
    compression_pipeline(70000)
    
    print("\n=== Starting from 7B model ===")
    compression_pipeline(7000)
    

  2. 估算设备端推理延迟。给定模型的算子数量和硬件规格,计算是否能达到延迟目标。

    def estimate_latency(model_name, params_M, bits, compute_tops, mem_bw_gbs, seq_len=256):
        """Estimate token generation latency for a memory-bandwidth-bound model."""
        # 模型体积 (字节)
        model_bytes = params_M * 1e6 * bits / 8
    
        # 解码阶段受内存带宽限制: 每生成一个 token 都要把整个模型加载一遍
        time_per_token_ms = model_bytes / (mem_bw_gbs * 1e9) * 1000
    
        # 每秒 token 数
        tokens_per_sec = 1000 / time_per_token_ms
    
        print(f"{model_name}: {params_M/1000:.1f}B params @ {bits}-bit = {model_bytes/1e9:.1f} GB")
        print(f"  Memory bandwidth: {mem_bw_gbs} GB/s")
        print(f"  Time per token: {time_per_token_ms:.1f} ms")
        print(f"  Tokens/sec: {tokens_per_sec:.0f}")
        print()
    
    # Apple M2 Pro: 200 GB/s 统一内存带宽
    print("=== Apple M2 Pro (200 GB/s) ===")
    estimate_latency("Llama-7B Q4", 7000, 4, 15.8, 200)
    estimate_latency("Llama-7B Q8", 7000, 8, 15.8, 200)
    estimate_latency("Llama-70B Q4", 70000, 4, 15.8, 200)
    
    # 手机 (Snapdragon 8 Gen 3): ~50 GB/s LPDDR5
    print("=== Snapdragon 8 Gen 3 (50 GB/s) ===")
    estimate_latency("Phi-3 Mini Q4", 3800, 4, 45, 50)
    estimate_latency("Llama-3B Q4", 3000, 4, 45, 50)
    


📌 本节要点

  • 边缘推理四大优势:无需联网、瞬时响应、零边际成本、数据不出设备。
  • 算力鸿沟:云端 GPU 与手机 NPU 差约 20 倍,与微控制器差约百万倍,需匹配不同压缩策略。
  • 压缩流水线顺序:蒸馏 → 剪枝 → 量化 → 编译,顺序不能乱,否则会压缩一个已经有损的模型。
  • 主流运行时: ONNX Runtime 跨平台,TFLite 主 Android, Core ML 主 Apple, ExecuTorch 是 PyTorch 官方边缘方案,llama.cpp 是端侧 LLM 首选。
  • 算子融合:编译器栈里影响最大的优化,把多个算子合并成一个内核,数据留在寄存器/缓存,提速 2-5 倍。
  • 内存规划:分析张量生命周期共享内存缓冲,100 个中间张量可能只需 10 份空间,对小内存设备关键。
  • NPU 委派模式:运行时把图切给 NPU 与 CPU,最大化 NPU 承担比例是能效关键。
  • 端侧 LLM 三大挑战:内存 (KV 缓存吃上下文)、热节流 (30 秒后降频 30-50%)、电池 (30 分钟对话耗 5%)。
  • 联邦学习 (FedAvg):数据不出设备,只传梯度;配合差分隐私与梯度量化保证隐私和通信效率。
  • 延迟优化四板斧:提前退出、模型切分、KV 缓存复用、推测性预取,都在压缩之外进一步压平延迟。