zeerd's blog         Search     Categories     Tags     Feed

闲来添雅趣,无事自逍遥。对窗静望雪,一盏茶香绕。

轻量化决策模型微调方法(端侧/移动端场景)

#Jev #SystemOne #Train @AI


一句话总结

我们采用「小基座 + LoRA 冻结微调 + 轻量决策头」的路线,把端侧决策建模为状态理解 + 选项打分的单次前向任务。训练数据融合用户指令与设备上下文,采用概率软标签保留决策不确定性,并显式建模「不操作」以保障安全。整套方法与基座规模解耦,可在 0.6B、1B、1.5B 等不同量级基座上复用,仅需训练百万级参数即可收敛,具备低延迟、可解释、易迭代、易迁移、隐私原生、离线可用的端侧部署特性。

零、导语

本文档说明如何用「小基座 + 冻结主干 + 轻量适配」的路线,把端侧决策能力压缩进一个轻量模型,单次前向传播直接输出决策概率,不做文本生成。

文中以 Qwen3-0.6B 作为具体示例,但整套方法与基座规模无关,可平滑迁移到 1B、1.5B 乃至其他同量级开源基座。

本文是基于对NeelContractor/jev-train的学习理解生成的。感谢原作者及原作者声明的鸣谢对象。

一、总体思路

采用 「小基座 + 冻结主干 + 轻量适配」 的路线,把决策能力压缩进一个轻量模型,单次前向传播直接输出决策概率,不做文本生成。这样在手机端、穿戴设备、IoT 终端等算力受限、对延迟敏感的端侧设备上可以稳定部署。

核心设计:决策 = 状态理解 + 选项打分,而不是「让模型写一段话再解析」。

这套思路的关键在于解耦:

  • 基座负责「理解」——把用户指令(语音/文本)和设备上下文编码成语义表示;
  • 适配层 + 决策头负责「决策」——在语义表示上做选项打分。

因此基座可以按算力预算自由替换,而决策逻辑与训练流程保持不变。

二、模型结构特征

1. 通用结构

flowchart TB
    subgraph Model[轻量决策模型]
        direction TB
        Input[输入序列
任务提示+问题+状态+选项+答案槽]

        subgraph Backbone[主干网络 前N层 冻结]
            direction LR
            L1[Layer 1] --> L2[Layer 2] --> L3[...] --> LN[Layer N]
        end

        subgraph LoRA[LoRA 适配层 r=8
作用于中后段注意力投影]
            direction LR
            LoRA_Q[q_proj] --> LoRA_K[k_proj]
            LoRA_K --> LoRA_V[v_proj]
            LoRA_V --> LoRA_O[o_proj]
        end

        subgraph Head[决策头 2层注意力 512维
+ 任务类型嵌入]
            direction TB
            Attn1[注意力层1] --> Attn2[注意力层2]
            Attn2 --> Score[双线性打分
答案表示 ⋅ 选项表示]
        end
    end

    Input --> Backbone
    Backbone --> LoRA
    LoRA --> Head
    Score --> Output[输出概率分布
P(option_i | state, question)]

    style Backbone fill:#e8f5e9,stroke:#2e7d32
    style LoRA fill:#fff3e0,stroke:#ef6c00
    style Head fill:#e3f2fd,stroke:#1565c0
组件 设计 轻量化考量
主干 选取开源基座的前 N 层(按算力预算裁剪) 直接削减参数量与计算量
适配层 LoRA,仅作用于中后段若干层的注意力投影 可训练参数占比通常 < 1%
决策头 轻量注意力头,带任务类型嵌入 参数量在百万级,替代整个生成式输出层

N的选定原则见下文「层数 N 的选定」。

2. 基座选型(按算力预算)

基座规模 适用场景 说明
0.5B–0.6B 手机端实时推理、资源极度受限 示例:Qwen3-0.6B 前 20 层,适合 NPU/ DSP 部署
1B–1.5B 高性能手机、平板、座舱域控 语义理解更强,适合复杂多轮指令
更大规模 云端/离线批处理、边缘服务器 通常不用于端侧实时决策

示例(Qwen3-0.6B):主干取前 20 层(共 28 层),LoRA(r=8, alpha=16)作用于第 8–19 层的 q/k/v/o_proj,决策头为 2 层 512 维注意力头。总参数约 0.4B,其中可训练参数约 800 万(占比约 2%)。

3. 层数 N 的选定

没有闭式理论公式,N 是一个经验性的「效果—成本」折中,可依据以下原则并结合消融实验确定:

  • 层功能分化:Transformer 各层表征有分工——浅层偏词法/句法,中层偏语义/实体,深层偏任务特定与「下一个词预测」。
  • 决策任务不需要生成层:本方案是判别式打分,不依赖自回归生成,因此最深的若干层(专为生成优化)往往可以裁掉,损失最小。
  • 收益递减:准确率随 N 增长通常呈「先快后平」的曲线,存在拐点。
  • 确定方法(消融扫描):在 {12, 16, 20, 24, 28} 等候选值上各训一次,选达到最佳效果 X% 以内的最小 N。
  • 硬约束:端侧延迟/显存/NPU 算力预算给出 N 的上界。

示例中取 20/28 层,即是在「保留足够语义理解」与「满足端侧延迟/功耗」之间的经验折中。

4. 关键机制:并行打分,而非逐字生成

sequenceDiagram
    participant User as 用户指令
    participant Encoder as 编码器(主干+LoRA)
    participant Options as 选项并行编码
    participant Mask as 注意力掩码
    participant Head as 决策头
    participant Output as 概率输出

    User->>Encoder: 状态+问题
    Encoder->>Options: 语义表示 h_state
    Options->>Options: 并行展开各选项
    Note over Options: 共享起始位置编码
    Options->>Mask: 应用掩码
    Note over Mask: 选项仅可见 问题+自身
    Mask->>Head: 答案槽汇总表示 h_ans
    Head->>Head: 双线性打分
    Note over Head: score_i = h_ans^T W h_opt_i
    Head->>Output: Softmax 得概率分布

传统做法是让模型生成一段文本(如「答案是:开启勿扰模式」)再解析,慢且易错。本方案改为一次前向、并行打分:

  1. 选项并行编码:所有选项共享同一个起始位置编码,各自独立展开,相当于「并排」放在序列里;
  2. 选项互不可见:注意力掩码让每个选项只能看到「问题 + 自己」,看不到其他选项;
  3. 答案槽汇总:末尾的「答案」token 能看到全部内容,形成一个汇总表示;
  4. 双线性打分:用答案表示与每个选项表示做内积,得到每个选项的分数。

由此带来两个好处:

  • 顺序无关:因为选项之间互不可见,把「开启勿扰」放在第一位还是最后一位,它的表示和得分都不变——决策不会因选项排列而改变;
  • 一次前向:所有选项同时打分,无需逐个生成,延迟低且稳定(手机端典型 < 50ms)。

三、训练数据的特征

1. 数据结构:状态 + 问题 + 答案

每条样本由三部分组成:

{
  "state": {
    "input": "把静音模式开一下",
    "context": {
      "time": "22:30", "location": "家",
      "battery": 45, "network": "wifi",
      "active_app": "微信", "screen_state": "on",
      "ringer_mode": "normal", "dnd_schedule": "22:00-07:00"
    }
  },
  "question": {
    "kind": "choice",
    "options": ["开启勿扰", "调低音量", "关闭震动", "不操作"]
  },
  "target": [0.82, 0.10, 0.05, 0.03]
}
  • state.input:用户指令(语音识别文本或文本输入)
  • state.context:设备/环境上下文(时间、位置、电量、网络、前台应用、屏幕状态、系统设置等)
  • question.kind:问题类型(choice / noul / score)
  • target:概率分布,非硬标签

2. 数据特征(端侧场景特有)

  • 多模态融合:用户指令(语音/文本)+ 设备传感器上下文(时间、位置、电量、网络、应用状态、系统设置)联合建模,而非只看文本。
  • 软标签监督:target 是概率分布而非 one-hot。这保留了决策的不确定性——例如「安静一点」可能 70% 开启勿扰、20% 调低音量,符合真实使用中意图模糊的情况。
  • 多任务统一:同一套结构支持三类问题:
    • choice:多选一(如「开哪个功能」「切换到哪个模式」)
    • noul:是/否(如「是否紧急」「是否需要确认」)
    • score:有序打分(如「紧急程度」「用户满意度」)
  • 口语化与噪声鲁棒:数据包含大量口语、省略、ASR 错字(如「静音模式」→「静音模试」、「勿扰」→「物流」)、方言变体,迫使模型学习语义而非字面匹配。
  • 负样本设计:包含「今天天气怎样」「帮我查快递」等与设备控制无关的指令,标签指向「不操作」,避免模型对任何输入都强行执行系统动作。
  • 隐私敏感感知:上下文中包含位置、应用使用等敏感字段,训练时需脱敏/本地化处理,推理时严格在端侧完成,不上传原始数据。

3. 数据规模与切分

  • 数据规模按任务复杂度与基座规模灵活调整:小基座 + 简单任务可用千级样本起步,复杂场景建议万级以上。
  • 建议按 训练 / 验证 / 测试 划分,测试集保持独立,用于选择最佳 checkpoint。
  • 数据量偏小时,优先通过数据增强、扩充负样本、提高数据多样性(改写指令、替换上下文、合成 ASR 错误)来提升泛化,而非单纯增加训练轮数。

四、微调步骤的特征

flowchart TD
    subgraph Train[训练流程]
        direction TB
        Data[训练数据
状态+问题+软标签] --> Encode[步骤1: 输入编码
结构化→序列+位置编码]
        Encode --> Mask[步骤2: 注意力掩码
选项互不可见+答案全见]
        Mask --> Forward[前向传播
冻结主干+LoRA+决策头]
        Forward --> Loss[Loss 计算
KL散度/交叉熵
Target: 概率分布]
        Loss --> Backward[反向传播
仅更新 LoRA+决策头]
        Backward --> Opt[优化器步进
AdamW+分层LR+梯度裁剪]
        Opt --> Check{早停?
测试Loss稳定}
        Check -->|否| Forward
        Check -->|是| Export[导出模型
LoRA合并进主干]
    end

    subgraph Deploy[部署流程]
        direction TB
        Export --> Convert[格式转换
MNN/NCNN/TFLite/ONNX]
        Convert --> Quant[量化
INT8/FP16]
        Quant --> Device[端侧部署
手机/穿戴/IoT]
        Device --> API[推理API
输入状态+选项
输出选择+概率]
        API --> Threshold{置信度
> 阈值?}
        Threshold -->|是| Action[执行动作]
        Threshold -->|否| Fallback[追问/兜底/不操作]
    end

    style Data fill:#e3f2fd
    style Export fill:#e8f5e9
    style Device fill:#fff3e0
    style Action fill:#e8f5e9
    style Fallback fill:#ffebee

步骤 1:输入编码(结构化 → 序列)

把状态、问题、选项拼成一条序列,并为每个选项单独生成位置编码:

[任务提示] + [问题] + [状态] + <option>选项1</option> <option>选项2</option> ... + "The answer is:"
  • 状态截断到 512 token,选项截断到 64 token(显存/NPU 内存约束下的取舍)
  • 任务提示放在最前,让每个状态 token 在编码时就知道「要回答什么」

步骤 2:注意力掩码设计(关键)

输入序列结构:

flowchart TB
    T[任务提示] --> Q[问题]
    Q --> S[状态]
    S --> O1[<option>选项1</option>]
    S --> O2[<option>选项2</option>]
    S --> O3[<option>选项3</option>]
    S --> O4[<option>选项4</option>]
    O1 --> Ans[答案槽\nThe answer is:]
    O2 --> Ans
    O3 --> Ans
    O4 --> Ans

注意力可见性矩阵(各 token 能看到的内容):

flowchart TB
    M1[问题: 因果掩码\n仅看过去]
    M2[选项1: 看 问题+自身\n不看 选项2/3/4]
    M3[选项2: 看 问题+自身\n不看 选项1/3/4]
    M4[选项3: 看 问题+自身\n不看 选项1/2/4]
    M5[选项4: 看 问题+自身\n不看 选项1/2/3]
    M6[答案槽: 看全部\n汇总决策]

    style M1 fill:#e3f2fd
    style M2 fill:#fff3e0
    style M3 fill:#fff3e0
    style M4 fill:#fff3e0
    style M5 fill:#fff3e0
    style M6 fill:#e8f5e9
  • 问题 token:因果掩码(只能看过去)
  • 每个选项:只能看问题和自身,看不到其他选项 → 保证顺序无关
  • 答案 token:能看到全部 → 汇总信息做决策

步骤 3:训练配置

项 设置 说明
主干 冻结 只训 LoRA + 决策头
优化器 AdamW 决策头学习率高于 LoRA(分层学习率)
精度 混合精度 + fp32 决策头 头用 fp32 保证 loss 稳定
调度 余弦退火 + warmup  
梯度累积 按显存调整 小显存/NPU 内存下模拟大 batch
梯度裁剪 1.0 防梯度爆炸

步骤 4:过拟合控制

小数据集上,减少训练轮数 + 加 LoRA dropout 是抑制过拟合最有效的手段:

  • 训练轮数过多时,训练准确率趋近 1.0,但测试 loss 持续上升,说明模型在记忆而非泛化;
  • 适当降低轮数并加入 dropout 后,测试 loss 趋于稳定,泛化更健康,训练时间也显著缩短。

实践建议:以测试 loss 是否稳定作为早停依据,而非只看准确率;准确率在小测试集上波动大,参考价值有限。

步骤 5:推理与部署

  • 单次前向传播输出概率,无自回归生成,延迟低(手机 NPU 典型 < 50ms)
  • 导出时把 LoRA 合并进主干权重,推理时无额外开销,便于转换为 MNN/NCNN/TFLite/ONNX 等端侧格式
  • 支持单条推理 API:输入状态+选项,返回选择结果和概率分布
  • 可配置置信度阈值:低于阈值时触发「追问/兜底到云端/不操作」

五、端侧/移动端场景的适配优势

  1. 低延迟:单次前向,无逐 token 生成,适合实时交互(语音助手、快捷指令、系统级控制)
  2. 可解释:输出概率分布,可设阈值触发「不确定则追问/兜底」
  3. 安全兜底:显式建模「不操作」类,避免误触发系统级动作(如误关机、误删除、误支付)
  4. 多任务统一:一套模型覆盖选择、判断、打分,减少部署模型数量,节省存储与内存
  5. 轻量可迭代:LoRA 微调成本低,可针对不同机型、OS 版本、用户偏好快速定制,支持 OTA 增量更新
  6. 基座可替换:决策逻辑与基座解耦,可按算力预算在 0.6B / 1B / 1.5B 等规模间平滑迁移,适配高中低端机型
  7. 隐私原生:全流程端侧推理,用户指令与上下文不出设备,满足合规要求
  8. 离线可用:无网络依赖,地铁、飞行模式、弱网环境下依然可用