用 QLoRA 微调轻量埃及阿拉伯语模型
MasryGPT-Chat-1.5B 基于 Qwen2.5-1.5B-Instruct,使用8万条埃及阿拉伯语样本及 QLoRA、Unsloth 进行指令微调,以 Apache 2.0 许可发布。文章介绍数据、训练、评估与部署,并提供模型及 Gradio 演示。
AI 深度解读
让模型学会埃及阿拉伯语,难点不只是替换几个词,还要让回答符合当地人的日常表达。MasryGPT-Chat-1.5B 的尝试是:在 Qwen2.5-1.5B-Instruct 上,用 80,000 条方言样本做轻量微调,把「教科书口吻」改成聊天口吻。
它展示了小模型适配特定语言社区的可行路径,也暴露出一个问题:训练损失很低、语气更地道,都不能证明模型真正理解了习语。对想做垂直助手的程序员,这个落差比训练成绩更值得看。
为什么选择 1.5B,而不是直接用大模型
作者瞄准的是现代标准阿拉伯语(MSA)与埃及方言 Masry 之间的距离。原文称,约 1.05 亿埃及人日常使用 Masry,而 Qwen、Jais、AceGPT 等模型更偏向 MSA。回答即使语法正确,也可能像翻译稿。
因此,项目首先追求表达适配。底座选 Qwen2.5-1.5B-Instruct,通过 QLoRA 和 Unsloth 微调,并以 Apache 2.0 发布。QLoRA 可以理解为:将底座量化,主要训练额外加入的少量参数,降低训练资源需求。
作者称,模型以 4-bit 加载约占 1.2 GB,能够在单张 T4 上运行;训练则用了 Kaggle T4×2。这里要分清模型大小与完整运行开销。原文另有约 0.8 GB 的权重估算,却没解释统计口径;「7B 需要 14 GB+ 显存」也缺少精度条件,不能当成所有部署方案的固定门槛。手机和边缘设备运行的说法,同样没有延迟或设备实测支撑。
低损失为什么没能换来习语理解
数据文件是 qwen_egyptian_80k_terms.jsonl,共 80,000 条,来自埃及词典驱动的合成生成,再人工过滤粗俗内容。样本采用 messages 格式,包含 user 和 assistant。
预处理使用 get_chat_template(tokenizer, chat_template="qwen-2.5"),再通过 tokenizer.apply_chat_template(..., tokenize=False) 转成拼接文本。训练流程依次经过 HF Hub、4-bit FastLanguageModel、用于 QLoRA 的 get_peft_model、TRL 的 SFTTrainer,最后以 save_pretrained_merged 合并保存为 16-bit 并上传。
作者报告训练进行了 2,500 步,耗时 2 小时 13 分钟:loss 从第 10 步的 0.091025,经过第 100 步的 0.098729,降到第 2,500 步的 0.074394;平均为 0.07995,并给出困惑度 exp(0.079) ≈ 1.083。
但训练样例里明明包含「عيش وملح」及其关于交情、承诺的解释,展示的测试回答却仍然出错。这个反例很有价值:模型能预测训练文本,不等于能在提问时稳定调用其中的知识。
作者将第 1,500 步之后 0.074–0.077 的平台期解释为数据饱和,并认为继续训练会过拟合。不过原文没有验证集曲线,也没交代 loss 的掩码范围,证据还不足以确定原因。标为「最终超参数」的位置,也没有实际列出完整配置。
跑起来容易,判断有没有用更难
下面这段代码展示了量化加载、套用聊天模板和生成回答的完整过程:
from unsloth import FastLanguageModel
model, tokenizer = FastLanguageModel.from_pretrained(
model_name="ISLAM-PO/MasryGPT-Chat-1.5B",
max_seq_length=2048, dtype=None, load_in_4bit=True,
)
FastLanguageModel.for_inference(model)
messages = [{"role": "user", "content": "ازيك يا باشا؟"}]
inputs = tokenizer.apply_chat_template(
messages, tokenize=True, add_generation_prompt=True,
return_tensors="pt", return_dict=True).to("cuda")
outputs = model.generate(**inputs, max_new_tokens=256, temperature=0.8,
top_p=0.95, do_sample=True, repetition_penalty=1.1)
print(tokenizer.batch_decode(outputs[:, inputs["input_ids"].shape[1]:],
skip_special_tokens=True)[0])
重点看 max_new_tokens=256 与 return_dict=True:前者控制生成长度,后者让模板处理返回结构化输入。作者还建议创作用 temperature=0.7–0.8,事实问答用 0.3;遇到相关警告时添加 fix_mistral_regex=True,但没有明确适用版本。
评测包含两部分。ARC-Challenge 使用 lm_eval、H100 上的 vLLM、greedy 和 temp 0,在 1,172 道题上得到 acc_norm 47.7%、acc 43.69%。另一部分由 3 位母语者判断,作者称回答的「埃及味」提高了 48%。
这些都是内部试测,原文明确表示尚未独立复现。「提高 48%」的评分尺度、样本量和计算方法,也没有在正文交代。
局限则更直接:习语表现约 40%,数据中习语仅占 15%;MSA 表现下降约 10%;模型可能编造埃及相关事实,没有 RAG,也没有超出底座 Qwen 的额外安全过滤。调高生成长度可以给回答更多空间,却不能补上缺失的理解。
我的判断
我认可这个方向:当需求集中在语气、措辞和交流习惯时,小模型微调有明确价值。但这篇材料更适合作为项目记录,距离可复现的效果报告还有差距。
Unsloth 快 2×、当前 2–4 GB 显存配置最优,以及关闭量化会慢 3×,都没有完整对照实验支持。它们可以作为作者的经验参考,不能直接变成团队选型结论。
同样,方言微调、提示词约束和 RAG 解决的问题不同。提示词能否已经满足表达要求,需要先测;涉及事实依据时,RAG 更贴近缺少知识来源的问题。原文没有比较这些路线,不能据此认定微调更划算。
作者计划补充约 5k 条真实习语对,并提出加入 40k 条多样真实对话、以 LR 1e-4 再训练 1 个 epoch。这些仍是改进方案,尚未证明有效。
能拿来做什么
- 如果要做面向特定语言社区的聊天助手,先建立独立评测集,分别检查表达自然度、习语理解和事实正确性,避免一个总分掩盖短板。
- 在 Node 服务接入模型前,先用同一组真实问题比较底座、提示词方案和微调模型,再决定是否承担数据整理与训练成本。
- 前端聊天界面可以提供「原问题、错误回答、期望回答」的反馈入口;真实聊天记录用于训练前,应确认授权、匿名化和来源。
- 部署时分别测量模型加载、长上下文和并发请求的显存与延迟,不要直接把 4-bit 权重大小当成服务容量。