核心摘要

小语言模型(Small Language Model, SLM)没有统一的参数量定义,小检查点也不自动等于低延迟、低成本、隐私或端侧可用。生产选型应遵循可复现契约:

text
工作负载
→ 模型制品身份
→ 运行时与后端
→ 设备与设备群切片
→ 内存、温度和能耗边界
→ 任务质量评测
→ 隐私与供应链审查
→ 本地发布、云端回退或拒绝

真正的发布单元不是模型家族名称,而是完整组合:

text
(工作负载,制品,运行时,后端,设备,运行边界)

核心要点

  • SLM 是相对类别。 “低于 100 亿参数”只是部分团队的约定,不是标准。
  • 参数量不等于设备适配。 量化权重字节不含 KV Cache、运行时缓冲、应用内存和安全余量。
  • 本地运行本身不是隐私控制。 还要审查下载、遥测、日志、备份、权限和回退。
  • 应评测设备群,而非一次启动演示。 质量、首字延迟、尾延迟、温度、能耗、OOM 和冷启动会随设备切片变化。
  • 按有效结果发布。 更快但严重错误或云端回退更多的小模型,总成本可能更高。

什么是小语言模型?

小语言模型是指相对于比较对象具有更小资源或能力边界的语言模型,这一名称没有标准组织规定的参数阈值。1B 模型对某些嵌入式产品仍然过大;经过量化的更大模型在工作站上却可能属于可控的小型部署。

“SLM”适合用来发现候选,但生产决策必须改用可测证据:

弱代理指标 生产所需证据
参数量 制品字节数与实测峰值常驻内存
模型家族 仓库、Revision、哈希、Tokenizer、模板与许可证
“可以在手机运行” 具体设备 SKU、系统、后端、上下文、热稳定与任务质量
每秒 Token 首字延迟、逐字延迟、端到端延迟与有效输出
“本地且隐私” 包含网络、日志和回退控制的数据流图
公共基准排名 目标任务集、切片、评分器、错误成本与验收策略

MobileLLM 论文说明了为什么不能只看参数量:在该论文限定的十亿参数以下模型、基准与 API 调用实验中,深而窄的架构、Embedding Sharing、GQA 和 Block-wise Sharing 都会影响结果。这证明小规模模型仍受架构设计影响,但不能据此定义全部 SLM,也不能证明端侧模型普遍优于云端模型。

先定义工作负载,再选择模型

只有相对于明确工作负载,模型才存在“足够小”和“足够好”。下载候选模型前,应先冻结任务契约。

至少记录:

  • 输入与输出 Schema、语言、模态及支持的最大长度。
  • 代表性、困难、对抗和分布外切片。
  • 必须阻断发布的严重错误。
  • 输出仅供建议,还是可以触发外部动作。
  • 离线要求、网络策略和允许的云端回退。
  • 设备群分布、并发、启动预算、电量与温度限制。
  • 统一的基线模型、评分器和人工复核策略。

信息抽取、分类或命令路由可以使用 Exact Match 或 Schema 有效率,但它们无法覆盖严重语义错误。生成任务应测任务验收率和失败分类,不能只使用一个相似度分数。如果模型能够触发动作,确定性授权必须留在模型之外。

MLPerf Client 展示了值得借鉴的测量拆分:Prompt 长度组件、质量资格、首字延迟(TTFT)和生成速率回答的是不同问题。不过,它选定的模型、硬件、任务和阈值属于特定基准;应采用这种测量纪律,而不是把其结果复制成产品 SLA。

固定完整的模型制品身份

模型名称不是可复现的部署身份。浮动标签和注册表可能变化,Chat Template 会改变行为,Tokenizer 不匹配也会让比较失效。

每次评测都应保存不可变 Manifest:

yaml
artifact:
  repository: "organization/model"
  revision: "commit-or-immutable-tag"
  sha256: "artifact-sha256"
  tokenizer_revision: "commit-or-immutable-tag"
  tokenizer_sha256: "tokenizer-sha256"
  chat_template_sha256: "template-sha256"
  quantization: "declared-format-and-parameters"
  license_revision: "archived-license-id"

runtime:
  name: "runtime-name"
  version: "release-or-commit"
  backend: "cpu-metal-vulkan-webgpu-npu"
  build_flags: ["record", "relevant", "flags"]

device:
  sku: "exact-device-sku"
  os: "exact-os-version"
  driver: "exact-driver-or-runtime"
  available_memory_bytes: 0

workload:
  task_set_revision: "sha256"
  prompt_slices: ["short", "medium", "long"]
  output_slices: ["short", "long"]
  concurrency: 1

许可证与 Acceptable Use Terms 也应随 Revision 归档。“开放权重”不必然等于符合 OSI 定义的开源许可证,允许研究下载也不表示所有产品用途都被授权。转换或量化后的制品同样要记录上游来源和新哈希。

计算总常驻内存,而非只看文件体积

量化权重文件只是部署预算的一部分。总常驻内存近似为:

text
模型权重
+ KV Cache
+ 激活与临时缓冲
+ 运行时和后端库
+ Tokenizer 与 Chat Template
+ 应用与 UI 内存
+ 操作系统压力
+ 安全余量

KV Cache 会随架构、上下文长度、Batch 或会话数、Cache 精度增长。模型加载、Prompt Prefill、图编译或后端回退也可能产生分配峰值。因此,“成功加载”不代表第一条长请求不会触发系统杀进程。

应把量化格式纳入制品身份。INT4、INT8 和浮点标签只有在目标后端存在对应 Kernel 时才可能带来预期性能。每个量化候选都应与同一高精度参考比较:

  • 加载、Prefill、Decode 与卸载阶段的峰值 RSS 和加速器内存。
  • 声明并发下可支持的上下文与输出长度。
  • 各任务切片的验收率与严重错误变化。
  • TTFT 与逐字延迟的 p50、p95、p99。
  • 持续会话中的热降频与能耗。
  • 崩溃、OOM 与后端回退率。

量化机制和校准权衡可参考模型量化指南。不能只用“参数量 × 位宽”得出总内存结论。

按部署契约选择运行时

正确的运行时,是经过固定版本测试后满足打包、加速、生命周期和可观测要求的运行时。运行时名称代表生态,并不代表等价性能。

路径 适用前提 必须核验的契约
llama.cpp 原生桌面、服务端、嵌入式或多后端实验 固定 Commit、GGUF 架构、量化器、Backend Build、上下文、服务暴露、取消与更新策略
ExecuTorch PyTorch 模型需要集成到 iOS 或 Android 应用 导出 .pte、Operator 与后端支持、Tokenizer、Sampler、Swift/Java/C++ 绑定、包体与回滚
LiteRT-LM 产品采用 Google 当前端侧编排栈 平台与语言成熟度、模型转换、加速路径、API 状态和版本兼容
WebLLM 兼容制品需在 WebGPU 浏览器环境推理 浏览器与设备支持、首次下载、缓存配额、Worker 生命周期、制品完整性、取消与回退
托管或混合 设备覆盖、质量、支持或快速更新更重要 区域与保留政策、网络尾延迟、配额、失败策略、路由授权和每个有效任务成本

当前 llama.cpp 文档使用 llama cli 与 llama serve 工作流,但命令和后端支持仍会随版本变化。生产自动化应固定经过测试的 Release 或 Commit,并保存精确调用参数,不能把一个未固定的模型标签当作架构决策。

ExecuTorch 明确分离导出与应用执行:先将模型导出成 .pte 程序,再通过 C++、Swift 或 Java 绑定集成。这一边界迫使团队明确 Tokenizer、Sampler、Backend Delegate 和应用生命周期。

LiteRT-LM 将自身定义为 Google 面向端侧 LLM 的 production-ready 编排层,但各语言绑定仍处于不同的 stable、preview 和 community support 状态。生产系统应固定 Release Tag,并保留每个接口的成熟度,不能笼统写成「支持 Android」。

WebLLM 通过 WebGPU 把兼容推理移入浏览器。生产工程还包括大体积首载、进度与取消体验、缓存淘汰、完整性校验、设备兼容和 Worker 恢复。Service Worker 可能在没有通知的情况下终止,不能被当作持久模型状态。

把隐私与离线当作系统属性测试

端侧推理可能减少发送给推理供应商的 Prompt 数据,但隐私属于完整应用数据流。需要检查:

  • 模型、Tokenizer、Adapter 与配置下载。
  • 分析、崩溃报告、Tracing、Prompt 日志和调试包。
  • 浏览器存储、备份、共享 Cache 和系统诊断。
  • 应用、用户与扩展之间的权限边界。
  • 云端安全检查、检索、工具调用与回退生成。
  • 制品来源、签名或哈希校验及更新通道。

离线测试应先准备声明的制品,再关闭网络并重启应用,运行全部必要任务切片,确认隐藏回退不会改变答案。还要记录 Cache Miss 和制品被淘汰后的恢复行为。“可以离线”不等于“始终离线”。

浏览器推理可以通过可选完整性哈希检测被篡改的模型资源,但不能证明页面不存在其他网络路径。原生应用的签名包也不能取代模型供应链清单。

在设备群与运行边界上做基准

一次启动演示不能证明生产性能。应在低资源、中位和高资源设备切片上,以受控运行条件测试。

维度 最低证据
质量 按任务、语言和难度切片统计任务验收率与严重错误逃逸率
响应 TTFT、逐字延迟或 TPOT、端到端 p50/p95/p99
容量 峰值常驻与加速器内存、支持上下文、并发和 OOM 率
生命周期 制品下载、校验、冷启动、热启动、Cache 命中、淘汰、更新和回滚
稳定性 崩溃率、取消延迟、前后台恢复和后端回退
持续运行 热降频、耗电和每个有效任务能耗
连接 离线成功率及带原因码的云端回退率
经济性 设备与支持成本、推理成本、复核成本和每个有效结果成本

测试时间必须足以达到热稳定状态。手机可能在前 20 秒生成很快,却在连续会话后降频或终止。如果产品会遇到低电量、内存压力、切入后台和下载中断,也必须覆盖这些状态。

不要直接相减无关时钟的时间戳。单进程使用单调时钟,跨组件保留 Clock Domain 信息,用户感知启动时间尽可能从客户端测量。

使用确定性的发布门禁

下面的零依赖 Go Fixture 评估已经记录的证据,不对模型品牌排名。团队应从产品要求设定阈值,并让阈值与任务集一起版本化。

go
package main

import (
	"fmt"
	"math"
	"strings"
)

type Candidate struct {
	ArtifactRevision     string
	RuntimeRevision      string
	DeviceSlice          string
	TaskSetRevision      string
	ArtifactHashVerified bool
	LicenseApproved      bool
	TaskAcceptance       float64
	CriticalErrorRate    float64
	TTFTP95MS            int
	PeakMemoryMB         int
	ThermalThrottling    bool
	OfflineSuccess       float64
	CrashRate            float64
}

type Gate struct {
	MinTaskAcceptance    float64
	MaxCriticalErrorRate float64
	MaxTTFTP95MS         int
	MaxPeakMemoryMB      int
	RequireOffline       bool
	MinOfflineSuccess    float64
	MaxCrashRate         float64
}

func validRate(value float64) bool {
	return !math.IsNaN(value) && !math.IsInf(value, 0) && value >= 0 && value <= 1
}

func releaseDecision(candidate Candidate, gate Gate) (string, error) {
	identity := []string{
		candidate.ArtifactRevision,
		candidate.RuntimeRevision,
		candidate.DeviceSlice,
		candidate.TaskSetRevision,
	}
	for _, value := range identity {
		if strings.TrimSpace(value) == "" {
			return "", fmt.Errorf("candidate identity is incomplete")
		}
	}
	rates := []float64{
		candidate.TaskAcceptance,
		candidate.CriticalErrorRate,
		candidate.OfflineSuccess,
		candidate.CrashRate,
		gate.MinTaskAcceptance,
		gate.MaxCriticalErrorRate,
		gate.MinOfflineSuccess,
		gate.MaxCrashRate,
	}
	for _, value := range rates {
		if !validRate(value) {
			return "", fmt.Errorf("rate %.4f is outside [0, 1]", value)
		}
	}
	if gate.MaxTTFTP95MS <= 0 || gate.MaxPeakMemoryMB <= 0 {
		return "", fmt.Errorf("resource limits must be positive")
	}
	if candidate.TTFTP95MS < 0 || candidate.PeakMemoryMB < 0 {
		return "", fmt.Errorf("candidate resource metrics must be non-negative")
	}
	if !candidate.ArtifactHashVerified || !candidate.LicenseApproved {
		return "reject", nil
	}
	if candidate.CriticalErrorRate > gate.MaxCriticalErrorRate {
		return "reject", nil
	}
	if gate.RequireOffline && candidate.OfflineSuccess < gate.MinOfflineSuccess {
		return "reject", nil
	}
	if candidate.TaskAcceptance < gate.MinTaskAcceptance ||
		candidate.PeakMemoryMB > gate.MaxPeakMemoryMB ||
		candidate.TTFTP95MS > gate.MaxTTFTP95MS ||
		candidate.ThermalThrottling ||
		candidate.CrashRate > gate.MaxCrashRate {
		return "cloud_fallback", nil
	}
	return "local_release", nil
}

func main() {
	gate := Gate{
		MinTaskAcceptance: 0.95, MaxCriticalErrorRate: 0.001,
		MaxTTFTP95MS: 800, MaxPeakMemoryMB: 3500,
		RequireOffline: true, MinOfflineSuccess: 0.99, MaxCrashRate: 0.001,
	}
	candidate := Candidate{
		ArtifactRevision: "sha256:model", RuntimeRevision: "litert-lm/release-validated",
		DeviceSlice: "android-high/4", TaskSetRevision: "offline-assistant/12",
		ArtifactHashVerified: true, LicenseApproved: true,
		TaskAcceptance: 0.97, CriticalErrorRate: 0.0005,
		TTFTP95MS: 620, PeakMemoryMB: 3100,
		OfflineSuccess: 0.995, CrashRate: 0.0002,
	}
	decision, err := releaseDecision(candidate, gate)
	if err != nil {
		panic(err)
	}
	unknown := candidate
	unknown.ArtifactRevision = ""
	_, unknownErr := releaseDecision(unknown, gate)
	fmt.Printf("decision=%s unknown_identity_rejected=%t\n", decision, unknownErr != nil)
}

预期输出:decision=local_release unknown_identity_rejected=true。

这些阈值只是可运行 Fixture 数据,不是通用建议。真实发布还应加入设备切片、置信区间、样本量、任务集 Revision、能耗上限和回退原因码。制品完整性或许可证审批未知时应 Fail Closed。

在本地、混合与云端推理之间决策

小模型与大模型不是互斥的产品类别。只有在相同验收策略下完成实测后,才可以路由。

text
不可变制品是否通过质量与严重错误门禁?
  ├── 否 → 拒绝,或使用已批准的托管基线
  └── 是
      ├── 所有支持设备切片是否通过内存、延迟、
      │   温度、能耗、生命周期与隐私门禁?
      │     ├── 是 → 本地发布
      │     └── 否
      │         ├── 是否允许且独立授权回退?
      │         │     ├── 是 → 带原因码的混合发布
      │         │     └── 否 → 缩小设备范围或拒绝

混合路由不能仅因设备变慢,就静默把隐私敏感请求发送到云端。数据传输前必须由策略授权;当本地与远程处理存在实质差异时,界面也应向用户说明。

成本应按每个有效结果计算:

text
设备与加速器
+ 能耗
+ 制品分发与存储
+ 兼容与支持
+ 评测与人工复核
+ 失败任务恢复
+ 云端回退

如果小模型产生更多拒绝、修正或回退,总成本可能高于更强的托管基线。反之,窄域、高频工作负载在通过同一质量门禁后,可能通过本地制品获得更好的延迟、可用性或数据最小化。

常见失败模式

  1. 把模型家族当作模型制品 修复:固定 Revision、哈希、Tokenizer、模板、量化、许可证与运行时。

  2. 把文件体积当作内存适配 修复:覆盖加载、Prefill、Decode、并发和应用压力,测量峰值常驻内存。

  3. 把一台设备的结果发布为设备群支持 修复:定义设备切片,测试冷启动、温度、电量、OOM、前后台切换与更新。

  4. 把本地推理直接称为隐私 修复:列出所有网络、日志、Cache、备份、工具、检索和回退路径。

  5. 比较不一致的基准结果 修复:统一任务集 Revision、Prompt/Template、上下文、评分器和验收策略。

  6. 只优化每秒 Token 修复:同时看质量、TTFT、TPOT、端到端尾延迟、稳定性、能耗和有效任务成本。

  7. 发布不可逆的静默回退 修复:独立授权路由、保留原因码,并让未知结果可以恢复。

常见问题

低于 100 亿参数的模型都属于 SLM 吗?

不是。这个阈值是团队约定而非标准。可以记录参数量,但部署适配必须由完整制品、运行时、设备、上下文和工作负载契约定义。

量化能让任何模型在手机上运行吗?

不能。量化可能降低权重内存,但运行时仍需要受支持的 Operator 或 Kernel、KV Cache、临时缓冲、库、应用内存和热余量,质量也可能变化。必须在每个支持设备切片上测试具体量化制品。

端侧推理一定比 API 更快吗?

不一定。端侧会减少部分网络和供应商排队时间,却增加本地加载、Prefill、Decode 与热降频。应同时比较冷、热状态的端到端 p50、p95 和 p99;低端设备或困难请求上,托管系统可能更快。

是否应该先微调小模型再评估?

先建立不可变基线并固定任务集。微调可能改善窄任务,但也新增数据来源、过拟合、Adapter 身份、训练复现和回归责任。微调制品应同时与原始候选和已批准托管基线比较。

哪些情况应该触发云端回退?

使用明确原因码,例如设备不支持、已验证的资源不足、质量策略拒答或本地能力不存在。不能仅因模型自报低置信度就路由,也不能绕过数据传输或外部动作授权。

总结

只有当一个具体模型制品在具体运行时、设备边界和工作负载上通过门禁,小语言模型才具备生产发布资格。固定完整制品身份,计算总常驻内存,评测代表性设备群,并把隐私与离线作为系统属性验证。质量、延迟、温度、能耗、完整性和生命周期任一关键门禁不通过时,应采用经过授权的混合或托管路径。

相关资源

参考资料