yasuokaの日記: 常用漢字の「𠮟」「塡」「頰」をbert-large-japanese-charに追加するには
日本語BERTモデルcl-tohoku/bert-large-japanese-charのvocab.txtを見ていたところ、常用漢字2136字のうち「𠮟」「塡」「頰」が抜けているのに気づいた。第3水準の常用漢字4字のうち「剝」は収録されているので、残る「𠮟」「塡」「頰」も何とかしたい。Google ColaboratoryとGoogle Driveの助けを借りて、「叱」「填」「頬」の単語ベクトルをコピーしてみよう。
from google.colab import drive
drive.mount("/content/drive")
output_dir="/content/drive/My Drive/bert-large-japanese-char-extended"
variants={"𠮟":"叱","塡":"填","剝":"剥","頰":"頬"}
!pip install transformers fugashi unidic-lite sentencepiece
import torch,os
from transformers import AutoTokenizer,AutoModelForMaskedLM
tokenizer=AutoTokenizer.from_pretrained("cl-tohoku/bert-large-japanese-char")
model=AutoModelForMaskedLM.from_pretrained("cl-tohoku/bert-large-japanese-char")
c=[]
for k,v in variants.items():
if tokenizer.add_tokens([k,v])==1:
t=tokenizer.convert_tokens_to_ids([k,v])
c.append((max(t[0],t[1]),min(t[0],t[1])))
e=model.resize_token_embeddings(len(tokenizer))
with torch.no_grad():
for k,v in c:
e.weight[k,:]=e.weight[v,:]
model.set_input_embeddings(e)
tokenizer.save_pretrained(output_dir)
model.save_pretrained(output_dir)
t=tokenizer.convert_ids_to_tokens([i for i in range(tokenizer.vocab_size,len(tokenizer))])
with open(os.path.join(output_dir,"vocab.txt"),"a",encoding="utf-8") as f:
print("\n".join(t),file=f)
os.remove(os.path.join(output_dir,"added_tokens.json"))
Google Driveへのアクセス許可とかが、ちょっと面倒だが、bert-large-japanese-char-extendedがGoogle Driveに出来て、vocab.txtの末尾に「𠮟」「塡」「頰」が追加されるはずだ。ちなみに「剝」については、元のままで特に変更していない。うまく単語ベクトルがコピーされてるかどうか、「酸素ボンベを充[MASK]する。」で試してみよう。
from google.colab import drive
drive.mount("/content/drive")
!pip install transformers fugashi unidic-lite sentencepiece
import torch
from transformers import AutoTokenizer,AutoModelForMaskedLM
tokenizer=AutoTokenizer.from_pretrained("/content/drive/My Drive/bert-large-japanese-char-extended")
model=AutoModelForMaskedLM.from_pretrained("/content/drive/My Drive/bert-large-japanese-char-extended")
tokens=tokenizer.tokenize("酸素ボンベを充[MASK]する。")
print(tokens)
mask=tokens.index("[MASK]")
ids=torch.tensor([tokenizer.convert_tokens_to_ids(tokens)])
with torch.no_grad():
outputs=model(ids)
pred=outputs[0][0,mask].topk(5)
for i,t in enumerate(tokenizer.convert_ids_to_tokens(pred.indices)):
tokens[mask]=t
print(i+1,tokens)
私(安岡孝一)の手元では、以下の結果になった。
['酸', '素', 'ボ', 'ン', 'ベ', 'を', '充', '[MASK]', 'す', 'る', '。']
1 ['酸', '素', 'ボ', 'ン', 'ベ', 'を', '充', '塡', 'す', 'る', '。']
2 ['酸', '素', 'ボ', 'ン', 'ベ', 'を', '充', '填', 'す', 'る', '。']
3 ['酸', '素', 'ボ', 'ン', 'ベ', 'を', '充', '杯', 'す', 'る', '。']
4 ['酸', '素', 'ボ', 'ン', 'ベ', 'を', '充', '給', 'す', 'る', '。']
5 ['酸', '素', 'ボ', 'ン', 'ベ', 'を', '充', '蓄', 'す', 'る', '。']
「充塡」と「充填」が両方とも穴埋めされていて、素晴らしい。ただ、vocab.txtの末尾あたりに、元々U+FE0EとU+E0101が紛れ込んでいて、私個人としては、ちょっといただけない。削っちゃった方がいいかしら?
常用漢字の「𠮟」「塡」「頰」をbert-large-japanese-charに追加するには More ログイン