BertMaskedLMで遊んでいて「毎朝あなたのために[MASK]を作ります。」で[MASK]部分に入る語を予測したら 1位が「詩」(確率0.078)、2位が「番組」(確率0.077) …ってなってて、えーそうなん???となった。Wikipediaで学習したBERTは味噌汁作れへんねや。https://t.co/gogcAFEM99 pic.twitter.com/I0RjYpjcxI
— ピジェ/𐀠𐀋 ピージェイ (@xiPJ) 2021年9月16日
BertMaskedLMで遊んでいて「毎朝あなたのために[MASK]を作ります。」で[MASK]部分に入る語を予測したら 1位が「詩」(確率0.078)、2位が「番組」(確率0.077) …ってなってて、えーそうなん???となった。Wikipediaで学習したBERTは味噌汁作れへんねや。
…と思ったら、これは「味噌汁」がボキャブラリに入ってないためでは?と…
いろいろ文脈を試しても「味噌汁」はどうして出なくて、「[MASK]汁」にしても「味噌」は出なくて、「味噌[MASK]」にすると「# # 汁」が出る。これはなに?
— asaokitan (@asaokitan) 2021年9月17日
いろいろ文脈を試しても「味噌汁」はどうして出なくて、「[MASK]汁」にしても「味噌」は出なくて、「味噌[MASK]」にすると「# # 汁」が出る。これはなに?
後で元の単語に復元できるよう、単語の先頭でないサブワードに「##」のような記号を付けることが多いので、「味噌汁」が1単語扱いで「味噌」と「##汁」というサブワードに分割されていたように取れます。
— shigashiyama (@shigashiyama028) 2021年9月17日
後で元の単語に復元できるよう、単語の先頭でないサブワードに「##」のような記号を付けることが多いので、「味噌汁」が1単語扱いで「味噌」と「##汁」というサブワードに分割されていたように取れます。
たしかにそうだ。
$ grep -n "味噌" BERT-base_mecab-ipadic-bpe-32k/vocab.txt 16023:味噌 $ grep -n "汁" BERT-base_mecab-ipadic-bpe-32k/vocab.txt 16504:汁 30629:##汁
「味噌汁」は、味噌
##汁
という2つのサブワードに分割される。
ということは、穴埋め問題を 毎 ##朝 あなた の ため に [MASK] ##汁 を 作り ます 。
の ように変更して、[MASK]
に「味噌」が入ると予測されるかどうか?などしか確認できないな…
上で確認した数字は行番号。(行番号 - 1)がIDになるので、##汁
のID 30628
を直接指定して MaskedLM に入力しよう。
# bert3_maskedlm_misoshiru.py import torch from transformers import BertJapaneseTokenizer, BertForMaskedLM tknz = BertJapaneseTokenizer.from_pretrained("cl-tohoku/bert-base-japanese") ids = [2, 979, 28956, 6968, 5, 82, 7, 4, 30628, 11, 2580, 2610, 8, 3] print(tknz.convert_ids_to_tokens(ids)) # ['[CLS]', '毎', '##朝', 'あなた', 'の', 'ため', 'に', '[MASK]', '##汁', 'を', '作り', 'ます', '。', '[SEP]'] mskpos = ids.index(tknz.mask_token_id) print(mskpos) # 7 model = BertForMaskedLM.from_pretrained("cl-tohoku/bert-base-japanese") ids_tensor = torch.LongTensor(ids).unsqueeze(0) out = model(ids_tensor) # モデルの出力は要素が1つのタプル topk = torch.topk(out[0][0][mskpos], k=5) print(topk[0]) # 上位k個の値 # tensor([17.4937, 14.9048, 14.6679, 13.3002, 12.9762], grad_fn=<TopkBackward>) print(topk[1]) # 上位k個のindex #tensor([16022, 883, 10102, 6792, 10117]) pred_tokens = tknz.convert_ids_to_tokens(topk[1]) print(pred_tokens) #['味噌', '果', '豚', '灰', '墨']
よし、無事に「味噌」が1位にきた。
んー、ところで、topk[0]
で出てる値ってなんなんですかね。loss?
デモ版で出ている確率値(?)とはどうも違う…?