学习心得:DeepSeek-R1-Distill-Qwen-1.5B 模型推理与性能优化(昇思 + 香橙派 AIpro)
经过完整实战,我把推理与优化心得提炼为“3 个性能指标、4 级优化手段、5 段关键代码、6 条避坑指南”,可直接作为后续项目 checklist。
一、3 个性能指标(在香橙派 20 TOPS 上实测)
| 指标 | 基线(未优化) | 优化后 | 提升倍数 |
|---|---|---|---|
| 首 token 延迟 | 143 s(图编译) | 1.2 s(cache 复用) | 119× ↓ |
| 单 token 延迟 | 1.1 s | 0.32 s | 3.4× ↓ |
| 峰值内存 | 6.8 GB | 3.2 GB | 2.1× ↓ |
结论:边缘端跑 1.5 B 模型完全可以“秒回”,只要编译一次、缓存常驻即可。
二、4 级优化手段(层层递进,可独立生效)
| 级别 | 手段 | 收益 | 一句话口诀 |
|---|---|---|---|
| L0 环境 | cgroup 限制 Python 进程 + swap | 显存多 2 GB | “先锁内存,再谈性能” |
| L1 框架 | 禁用动态图多线程 | 单 token 从 1.1 s → 0.67 s | disable_multi_thread() 三板斧 |
| L2 编译 | @jit 图编译 + StaticCache | 0.67 s → 0.32 s | “一次编译,永久躺平” |
| L3 算子 | Top-p 采样 numpy 实现 + RotaryEmbedding 重写 | 再降 10 % | “算子不快,就自己写” |
三、5 段关键代码(复制即可用)
-
一键关多线程
from mindspore._c_expression import disable_multi_thread disable_multi_thread() -
StaticCache 预分配
from mindspore.nn import StaticCache cache = StaticCache( config=config, batch_size=1, max_cache_length=2048, ) -
JIT 装饰 decode 函数
@mindspore.jit(mode="O2", jit_level="O2") def decode_one_token(model, cur_token, position_ids, cache_position, past_kv): logits = model(cur_token, position_ids, cache_position=cache_position, past_key_values=past_kv) return logits -
Top-p 采样 numpy 加速
def sample_top_p(probs, p=0.9): probs_np = probs.asnumpy() sorted_idx = np.argsort(-probs_np) cumsum = np.cumsum(probs_np[sorted_idx]) mask = cumsum - probs_np[sorted_idx] > p probs_np[sorted_idx[mask]] = 0 probs_np /= probs_np.sum() return mindspore.Tensor(probs_np).multinomial(1) -
推理时间打点脚本
export INFERENCE_TIME_RECORD=True python inference.py # 输出每 token 耗时,方便 A/B 对比
四、6 条避坑指南
| 坑点 | 现象 | 解决 |
|---|---|---|
| 1. pad_token=eos_token 导致 attention_mask 报警 | 长文本重复 <think> | 手动传 attention_mask |
| 2. 首 token 超慢 | 每次都重新构图 | 把 StaticCache 提到全局,只编译一次 |
| 3. jit 后显存反而涨 | O2 整图下沉占 workspace | 适当降低 max_cache_length |
| 4. Top-p 用 mint.gather 报错 | aclnnGatherGetWorkspaceSize failed | 用 numpy 实现 or 回退到 mindspore.ops.gather |
| 5. 多并发推理崩溃 | 多进程竞争 NPU context | 加锁或改用单进程多线程 |
| 6. 温度=0 输出截断 | logits 全为 inf | 温度设极小值 0.01 替代 0 |
五、个人反思与下一步
| 反思 | 下一步行动 |
|---|---|
| 只优化单卡单线程,利用率低 | 用 MindSpore Serving + batch=4 做流水线 |
| 长文本 2048 后掉速 | 尝试 StreamingLLM 动态 cache 丢弃策略 |
| 无系统化基准 | 建立“首 token + 吞吐 + 功耗”三维基准板,持续追踪 |
一句话总结
“边缘端推理,慢的不是算力,而是编译与内存;把图焊死、把内存锁死,1.5 B 也能在 20 TOPS 上起飞。”