背景是再翻译一些格式化的文本时出现了问题:
比如说这段游戏里的文本:
Completing the construction requires a minimum cost of {GOLD} gold
其中,{GOLD}是一个游戏文本系统定义的占位符,表示货币数量,翻译成中文的正确结果应该是:
完成建筑需要至少{GOLD}黄金
但翻译的时候会变成:
完成建筑需要至少{黄金}黄金
所以研究了一下怎么把这种不该翻译的词屏蔽掉。
打开opus-mt-en-zh根目录,找到config.json:
//吐槽:代码块居然不能选json,这不合理,xml都能选啊
{
"_name_or_path": "/tmp/Helsinki-NLP/opus-mt-zh-en",
"activation_dropout": 0.0,
"activation_function": "swish",
"add_bias_logits": false,
"add_final_layer_norm": false,
"architectures": [
"MarianMTModel"
],
"attention_dropout": 0.0,
"bad_words_ids": [
[
65000
]
],
"bos_token_id": 0,
"classif_dropout": 0.0,
"classifier_dropout": 0.0,
"d_model": 512,
"decoder_attention_heads": 8,
"decoder_ffn_dim": 2048,
"decoder_layerdrop": 0.0,
"decoder_layers": 6,
"decoder_start_token_id": 65000,
"decoder_vocab_size": 65001,
"dropout": 0.1,
"encoder_attention_heads": 8,
"encoder_ffn_dim": 2048,
"encoder_layerdrop": 0.0,
"encoder_layers": 6,
"eos_token_id": 0,
"extra_pos_embeddings": 65001,
"forced_eos_token_id": 0,
"id2label": {
"0": "LABEL_0",
"1": "LABEL_1",
"2": "LABEL_2"
},
"init_std": 0.02,
"is_encoder_decoder": true,
"label2id": {
"LABEL_0": 0,
"LABEL_1": 1,
"LABEL_2": 2
},
"max_length": 512,
"max_position_embeddings": 512,
"model_type": "marian",
"normalize_before": false,
"normalize_embedding": false,
"num_beams": 6,
"num_hidden_layers": 6,
"pad_token_id": 65000,
"scale_embedding": true,
"share_encoder_decoder_embeddings": true,
"static_position_embeddings": true,
"transformers_version": "4.22.0.dev0",
"use_cache": true,
"vocab_size": 65001
}
可以看到三个关键点:
bad_words_ids
id2label
label2id
实验发现如果把一个次添加为label,这个词就大概率会原封不动地返回来了。(我把三个地方都添加了,没有试过只添加一个会怎么样)
所以解决方案就有了:
因为我要做的是一个随时翻译的本地服务,所以我的考虑是最好不要每次都重新加载模型配置(我不确定重新创建pipeline的消耗,如果不大的话可以考虑动态加载config),所以我针对大括号括起来的这种占位符格式封了个替换方法,翻译前翻译后一共做两次替换。
大概逻辑是:
原文本:Completing the construction requires a minimum cost of {GOLD} gold
替换占位符:Completing the construction requires a minimum cost of LABEL_0 gold
翻译完成:完成建筑需要至少LABEL_0黄金
替换标签:完成建筑需要至少{GOLD}黄金