我花了两周把GLM模型塞进前端项目,面试官直接问我怎么做到的
去年年底,我们组接了个智能问答系统项目,产品经理要求必须纯前端运行,不能联网,数据不能出用户浏览器。当时觉得在浏览器里跑AI模型很离谱,但想到WebGPU和WASM的逐渐成熟,决定尝试把小型GLM变体塞进浏览器跑推理。
模型选型与量化压缩
直接使用Transformers.js时,发现多数预训练模型对中文支持不佳。智谱AI的GLM系列中文效果好且结构清晰,但最小模型也有几百兆。受llama.cpp启发,我决定对GLM进行量化压缩。
量化过程坑很多。直接对60M参数模型进行INT4量化后,输出全是乱码。分析发现attention层和输出层对精度极为敏感。最终采用混合精度策略:敏感层保持FP16,前馈网络层压缩到INT4,embedding层压缩到INT8。模型从240MB压缩到约80MB,推理效果基本持平。
import torch
from transformers import AutoModel
model = AutoModel.from_pretrained("THUDM/glm-10b-chinese", trust_remote_code=True)
sensitive_layers = []
for name, param in model.named_parameters():
if 'attention' in name or 'lm_head' in name:
sensitive_layers.append(name)
# 敏感层保留FP16,其他层量化到INT4
前端工程化实践
80MB模型加载缓慢,我采用CDN加速和渐进式加载策略,先加载embedding层展示loading动画。为避免主线程卡死,使用两个Web Worker分别处理模型推理和文本预处理,通过SharedArrayBuffer通信。
const inferenceWorker = new Worker('inference.worker.js');
const preprocessWorker = new Worker('preprocess.worker.js');
const sharedBuffer = new SharedArrayBuffer(1024 * 1024);
const tokenArray = new Int32Array(sharedBuffer);
preprocessWorker.postMessage({ text: userInput, buffer: sharedBuffer });
inferenceWorker.postMessage({ tokens: tokenArray, modelConfig: { maxLength: 128, temperature: 0.7 } });
WASM性能起初用Pyodide运行缓慢,改用ONNX Runtime Web配合SIMD指令,推理速度提升到800ms左右。SharedArrayBuffer需服务器配置特定HTTP头,本地开发时Vite dev server未配置导致报错,排查了很久。
推理加速与收获
为优化体验,实现了KV Cache,缓存已计算的Key和Value,避免自回归生成中的重复计算,长文本生成速度提升40%。又引入投机采样,用10M参数小模型生成候选token,大模型验证,推理速度再提升30%。
后来面试腾讯时,面试官对该项目很感兴趣。我详细讲解了量化压缩、KV Cache、投机采样等思路,与他们的边缘计算推理优化方向不谋而合。虽然最终未入职,但项目加深了我对模型推理优化的理解。核心代码开源后获得一百多star,还有人提PR建议用Flash Attention优化。
技术学习必须亲自动手折腾,踩坑、赶deadline才能真正理解算法论文的精髓。

评论 0