LFM2.5-230M-cMoE-e8s2a2-LoRA-merged-zh
基于 LiquidAI/LFM2.5-230M 的 条件混合专家 (cMoE) 转换 + 中文对话 LoRA 微调 + 权重融合 版本。
模型特点
- ✅ cMoE 转换 (e8s2a2): 8 路由专家 + 2 dense 层 + top-2 激活
- ✅ 完全原生 transformers 架构 (
lfm2_moe),无需trust_remote_code=True - ✅ 中文对话能力:通过 206 条中文日常对话 LoRA 微调获得
- ✅ 权重完全融合:无 adapter 残留,直接
from_pretrained即可使用
转换配置 — e8s2a2
| 代码 | 含义 | 值 |
|---|---|---|
e8 | 每个 MoE 层的路由专家数 | 8 |
s2 | 底部 dense(共享)层数 | 2 |
a2 | 每个 token 激活的路由专家数 (top-k) | 2 |
前 2 层(原 LFM2 的 conv 层)保持 dense MLP,其余 12 层转换为 MoE 层(8 专家,top-2 路由,sigmoid + expert-bias,原生 lfm2_moe 架构)。
为什么不需要 trust_remote_code=True?
本模型使用 transformers >= 5.19.0 原生支持的 lfm2_moe 模型类型。仓库内不含任何自定义 .py 文件,加载方式:
from transformers import AutoModelForCausalLM, AutoTokenizer
model = AutoModelForCausalLM.from_pretrained("dokoomae/LFM2.5-230M-cMoE-e8s2a2-LoRA-merged") # 无需 trust_remote_code
tok = AutoTokenizer.from_pretrained("dokoomae/LFM2.5-230M-cMoE-e8s2a2-LoRA-merged")
权重转换策略
| 源 (LFM2-230M dense) | 目标 (LFM2-MoE) |
|---|---|
model.embed_tokens.weight | 直接复制 |
model.embedding_norm.weight | 直接复制 |
lm_head.weight | 与 embed_tokens 共享 |
layers.{i}.conv.* / self_attn.* | 直接复制 |
layers.{i}.operator_norm / ffn_norm | 直接复制 |
layers.0..1.feed_forward.w1/w2/w3 | 直接复制(dense 层) |
layers.2..13.feed_forward.w1/w2/w3 | 复制到 8 个专家,作为 experts.gate_up_proj / experts.down_proj,加 σ=1e-3 高斯噪声打破对称 |
| — | feed_forward.gate.weight(路由器)随机初始化 |
| — | feed_forward.expert_bias 置零 |
中文对话 LoRA 微调
- 数据集:206 条中文日常对话,覆盖问候、自我介绍、天气、食物、旅行、地理、情感、数学、常识、节日、动物、文化等场景
- 格式:ChatML 风格
<|startoftext|>user {question}<|im_end|> assistant {answer}<|im_end|> - LoRA 配置:r=16, alpha=32, dropout=0.05
- 目标模块:
q_proj,k_proj,v_proj,out_proj(所有 attention 层)+w1,w3,w2(dense 层 0-1) - 可训练参数:1.29 M(0.145% of 891 M)
- 训练:4 epochs,batch=1,max_len=96,gradient checkpointing
- 训练 loss:2.66 → 1.85 → 1.21 → 0.76
- adapter 已通过
merge_and_unload()完全融合进基础权重
注:MoE 专家权重 (
gate_up_proj,down_proj) 和路由器是nn.Parameter而非nn.Linear,PEFT 无法直接 wrap。LoRA 因此作用于 attention 投影 + dense MLP 层,这是标准且有效的设置。
中文对话效果对比
| 提示 | base MoE (转换前) | merged (中文 LoRA 后) |
|---|---|---|
| 你好 | 复读 / 英文回复 | "你好!有什么我可以帮忙的吗?" |
| 中国首都是哪里 | "中国首都是哪里?"(复读) | "中国的首都是北京。" |
| 一加一等于几 | "一加一等于几"(复读) | "一加一等于二。" |
| 再见 | 空白 | "再见!很高兴见到你..." |
| 陪我聊聊天好吗 | 复读 | "当然可以,我会一直陪着你聊天。" |
| 怎么学英语 | 空白 | "学习英语是长期努力的过程..." |
模型规格
| 项 | 值 |
|---|---|
| 模型类型 | lfm2_moe(原生 transformers) |
| 总参数 | 891 M |
| 每 token 激活参数 | ~330 M |
| Hidden size | 1024 |
| Intermediate size (dense) | 2560 |
| MoE intermediate size (每专家) | 2560 |
| 层数 | 14(2 dense + 12 MoE) |
| 注意力头数 | 16 |
| KV 头数 | 8 (GQA) |
| 词表大小 | 65536 |
| 精度 | bfloat16 |
快速开始
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
model = AutoModelForCausalLM.from_pretrained(
"dokoomae/LFM2.5-230M-cMoE-e8s2a2-LoRA-merged",
torch_dtype=torch.bfloat16,
)
tok = AutoTokenizer.from_pretrained("dokoomae/LFM2.5-230M-cMoE-e8s2a2-LoRA-merged")
model.eval()
# ChatML 格式
def chat(question: str, max_new_tokens: int = 60) -> str:
prompt = f"<|startoftext|>user\n{question}<|im_end|>\nassistant\n"
inp = tok(prompt, return_tensors="pt", add_special_tokens=False)
with torch.no_grad():
out = model.generate(
**inp,
max_new_tokens=max_new_tokens,
do_sample=False,
pad_token_id=tok.pad_token_id,
eos_token_id=tok.eos_token_id,
)
text = tok.decode(out[0], skip_special_tokens=False)
if "assistant\n" in text:
text = text.split("assistant\n", 1)[1]
if tok.eos_token in text:
text = text.split(tok.eos_token, 1)[0]
return text.strip()
print(chat("你好"))
# 你好!有什么我可以帮忙的吗?
print(chat("中国首都是哪里"))
# 中国的首都是北京。
print(chat("一加一等于几"))
# 一加一等于二。
局限性
- 训练数据仅 206 条短对话,复杂多轮对话能力有限
- 部分回答可能啰嗦或不够自然(小模型 + 小数据固有限制)
- 无法获取实时信息(天气、时间、新闻等)
- 数学计算仅限简单加减乘除
- 中文 LoRA 仅作用于 attention + dense MLP,MoE 专家权重未被适配
改进方向
- 扩大训练数据(如使用 ShareGPT.zh、BELLE、Firefly 等开源中文对话数据集)
- 增加 LoRA rank(如 r=32 或 r=64)以提升表达能力
- 在 GPU 上训练更多 epochs(CPU 限制下 4 epochs 已是上限)
- 将 MoE 专家权重改写为
nn.Linear列表以支持 PEFT wrap,让 expert 也可被 LoRA 适配
License
Apache 2.0(继承自基础模型 LiquidAI/LFM2.5-230M)