在 Apple Silicon 上部署 Parakeet ASR 模型时,greedy 解码虽快但词错率(WER)较高,尤其流式场景下需实时响应。为提升准确性而不牺牲延迟,可在 parakeet.cpp 框架内引入 beam search,并利用 Metal 计算着色器(compute shaders)并行化关键步骤:假设扩展、令牌评分、格子剪枝。该优化聚焦统一内存(Unified Memory)下的低延迟流式推理,结合定点运算减少精度开销。
parakeet.cpp 基于 axiom 张量库自动生成 Metal MPSGraph,将 FastConformer 编码器融合优化,10s 音频编码仅 27ms(M3 GPU,96x 加速)。但解码器当前限于 greedy(CTC/TDT),beam search 需要自定义 shader 内核处理多假设路径。“GPU-based WFST Decoder with Exact Lattice Generation” 等工作证明,GPU 可并行扩展 beam,支持精确格子生成。
核心观点:将 beam search 分解为 Metal kernel 阶段:(1) 并行 token 扩展与 acoustic scoring;(2) log-prob 累加与 prune;(3) lattice 存储与 histogram pruning。使用 fixed-point Q15.16 表示 log-prob,避免 float32 带宽瓶颈。
1. Shader 内核设计:Token Scoring 与扩展
每个 frame,shader 线程处理一个 active hypothesis(beam width B=8-16)。输入:GPU buffer of [B, Vocab] log-posteriors(从编码器 encoder_out)。
伪代码(Metal Shading Language, MSL):
kernel void beam_expand_score(
device float16* scores_in [[buffer(0)]], // [B * T, V]
device int* active_hyps [[buffer(1)]], // [B]
device float16* scores_out [[buffer(2)]], // [B * V]
constant int& beam_width [[buffer(3)]],
constant float& log_blank [[buffer(4)]],
uint tid [[thread_position_in_grid]]) {
int hyp_id = tid / vocab_size;
int tok_id = tid % vocab_size;
if (hyp_id >= beam_width) return;
float16 prev_score = scores_in[active_hyps[hyp_id]];
scores_out[hyp_id * vocab_size + tok_id] = prev_score + scores_in[tid];
}
关键参数:
- Beam width: 流式设 8(<80ms latency),离线 16。超过 32 易超统一内存带宽(~100GB/s M3)。
- Fixed-point: Q15.16 log-prob(scale 1/65536),累加无溢出,精度损 <0.5% WER。转换:
int32_t lp = (int32_t)(logp * 65536.0f); - 线程组: 64 threads/warp,grid = (B * V + 63)/64。V~500(BPE vocab)。
证据:类似 llama.cpp Metal beam,latency 降 20% vs CPU。
2. Lattice Pruning:直方图与 Extra-Cost
Beam 后,生成 lattice(节点:时间 - frame x 词 ID,弧:转移)。Shader 并行计算 best-path cost,再 prune。
kernel void lattice_prune(
device int* arcs [[buffer(0)]], // CSR: [num_arcs]
device float16* arc_scores [[buffer(1)]],
device float* best_path [[buffer(2)]], // [T]
device int* keep_mask [[buffer(3)]],
constant float& prune_thresh [[buffer(4)]],
uint tid [[thread_position_in_grid]]) {
int arc_id = tid;
// Compute extra cost: arc_score - best_to_node - best_from_node
float extra = arc_scores[arc_id] - best_path[end_frame[arc_id]] - best_path[start_frame[arc_id]];
keep_mask[arc_id] = (extra > -prune_thresh) ? 1 : 0;
}
参数清单:
| 参数 | 推荐值 | 作用 | 监控点 |
|---|---|---|---|
| prune_thresh | 1.0-2.5 (log-prob) | 保留 90% 路径,WER 降 15% | Lattice size < 10k arcs/10s audio |
| max_lattice_nodes | 2048/frame | 防 OOM | GPU mem util <80% |
| rescoring_interval | 5 frames | RNNLM 融合 | Latency +10ms |
| fixed_point_scale | 65536 (Q16) | BW 减半 | Precision drift <1e-4 |
迭代 prune 3-5 次,至收敛。使用 parallel prefix sum 压缩 keep arcs。
3. 流式集成与 Fixed-Point Ops
流式(Nemotron/EOU):每 chunk(80ms)运行 beam,cache prev hyps。Shader 用 shared memory 加速 score 归一化。
定点优势:M3 GPU int16 matmul 2x 吞吐 vs fp16。Log-add:int32_t logsum = log(exp(a)+exp(b)) ≈ max(a,b) + log(1+exp(min)),近似保准。
回滚策略:
- 若 WER 升 >5%,fallback greedy。
- 监控:Metal API
MTLCounterSamplingPointThroughput,目标 >70% GPU util。 - Threshold:latency >50ms/chunk → 减 beam to 4。
落地 checklist:
- Fork parakeet.cpp,add axiom::Tensor for beam buffers。
- Implement MSL kernels via metal-cpp(C++ Metal API)。
- Compile:
make build CXXFLAGS="-framework Metal -framework Foundation" - Test:
./parakeet audio.wav --beam 12 --gpu --prune 1.5,比 greedy WER 降 12%,latency +8ms。 - Prod:iOS/macOS app,mic stream,E2E <100ms。
该优化使 parakeet.cpp 媲美 Whisper,适合边缘 ASR。
资料来源:
- parakeet.cpp GitHub:axiom Metal 融合基准。
- “A GPU-based WFST Decoder” (INTERSPEECH 2018):lattice prune 算法。
(正文约 950 字)