最近腾讯开源了一个比较小的翻译模型 hy-mt-1.8b。由于足够的小,而且能翻译的语言也比较多,于是在 huggingface 停留了很长时间(截止到现在 2026年1月11日,它还在总榜上第2名)。
我尝试用 rust 基于 candle 框架实现了这个模型,并在显卡上跑通了。
下面分享一些感受。
这个模型非常非常简单,简单到结构就没几行。
HunYuanDenseV1ForCausalLM(
(model): HunYuanDenseV1Model(
(embed_tokens): Embedding(120818, 2048, padding_idx=120002)
(layers): ModuleList(
(0-31): 32 x HunYuanDenseV1DecoderLayer(
(self_attn): HunYuanDenseV1Attention(
(q_proj): Linear(in_features=2048, out_features=2048, bias=False)
(k_proj): Linear(in_features=2048, out_features=512, bias=False)
(v_proj): Linear(in_features=2048, out_features=512, bias=False)
(o_proj): Linear(in_features=2048, out_features=2048, bias=False)
(query_layernorm): HunYuanDenseV1RMSNorm((128,), eps=1e-05)
(key_layernorm): HunYuanDenseV1RMSNorm((128,), eps=1e-05)
)
(mlp): HunYuanDenseV1MLP(
(gate_proj): Linear(in_features=2048, out_features=6144, bias=False)
(up_proj): Linear(in_features=2048, out_features=6144, bias=False)
(down_proj): Linear(in_features=6144, out_features=2048, bias=False)
(act_fn): SiLUActivation()
)
(input_layernorm): HunYuanDenseV1RMSNorm((2048,), eps=1e-05)
(post_attention_layernorm): HunYuanDenseV1RMSNorm((2048,), eps=1e-05)
)
)
(norm): HunYuanDenseV1RMSNorm((2048,), eps=1e-05)
(rotary_emb): HunYuanDenseV1RotaryEmbedding()
)
(lm_head): Linear(in_features=2048, out_features=120818, bias=False)
)
大概就长这样吧。
里面的一些模块,和其他模型的实现也基本差不多,因此基本没什么值得一说的。
于是,移植这个模型,就变成了对 python 库 transformers 使用到的模型的移植工作。
麻烦一
由于流行的模型都直接在 transformers 中实现了,那么必然地,transformers 的代码抽象程序会比较高。比如模型加载:
model_name_or_path = "tencent/HY-MT1.5-1.8B"
tokenizer = AutoTokenizer.from_pretrained(model_name_or_path)
model = AutoModelForCausalLM.from_pretrained(model_name_or_path, device_map="auto")
这是高度封装的结果,我们不能直接从这开始移植。
幸好,上面打印的模型结构已经给了我们足够的信息。它实际调用的是: HunYuanDenseV1ForCausalLM。
麻烦二
死代码,死参数。
由于 transformers 库的方法有一些约定参数和规范,又由于模型的结构各不相同,加上有一些模块明显的 ctrl+c + ctrl+v 其他模块的代码,导致了很多死代码和死参数。
这个模型的实现就特别的多。可能需要对 transformers 库进行一些 debug 输出来确定到底调用了哪些代码。
麻烦三
对齐输入输出。这是一个精细活,由于不同的编程语言,不同的框架实现,难免会有一些差异。
比如,python 版本官方的示例用的 It’s on the house. 翻译为 这是免费的。。
而我的实现明显有一些差异,翻译就变成了 这是一所房子而已。 有时候修改一些参数,它还会变成:这是房子。 或者 它在房子里。 也说明,这些 token 非常的接近,一些差异就会让输出完全改变。