パスワードを忘れた? アカウント作成
16476387 journal
人工知能

yasuokaの日記: ku-nlp/deberta-v2-base-japaneseのトークナイザをDebertaV2TokenizerFastのままでJuman++に繋ぐには

日記 by yasuoka

私(安岡孝一)の1月13日の日記に対し、ku-nlp/deberta-v2-base-japaneseのトークナイザをBertJapaneseTokenizerに置き換えるとreturn_offsets_mapping=Trueが使えなくなってしまう、との御指摘をいただいた。確かにそのとおりだ。BertJapaneseTokenizerFastもどきで行こうかとも思ったが、ここはあえてPreTokenizer.customにJumanppTokenizerを突っ込んでみることにした。Google Colaboratoryだと、こんな感じ。

!test -d jumanpp-2.0.0-rc3 || curl -L https://github.com/ku-nlp/jumanpp/releases/download/v2.0.0-rc3/jumanpp-2.0.0-rc3.tar.xz | tar xJf -
!test -x /usr/local/bin/jumanpp || ( mkdir jumanpp-2.0.0-rc3/build && cd jumanpp-2.0.0-rc3/build && cmake .. -DCMAKE_BUILD_TYPE=Release && make install )
!pip install 'transformers>=4.24.0' sentencepiece pyknp rhoknp pytextspan
from transformers import DebertaV2TokenizerFast,AutoModelForMaskedLM,FillMaskPipeline
from transformers.models.bert_japanese.tokenization_bert_japanese import JumanppTokenizer
from tokenizers.pre_tokenizers import PreTokenizer,Metaspace,Sequence
tkz=DebertaV2TokenizerFast.from_pretrained("ku-nlp/deberta-v2-base-japanese")
class JumanppPreTokenizer(JumanppTokenizer):
  def jumanpp_split(self,i,normalized_string):
    import textspan
    t=str(normalized_string)
    k=self.tokenize(t)
    return [normalized_string[s:e] for c in textspan.get_original_spans(k,t) for s,e in c]
  def pre_tokenize(self,pretok):
    pretok.split(self.jumanpp_split)
tkz._tokenizer.pre_tokenizer=Sequence([PreTokenizer.custom(JumanppPreTokenizer()),Metaspace()])
mdl=AutoModelForMaskedLM.from_pretrained("ku-nlp/deberta-v2-base-japanese")
fmp=FillMaskPipeline(model=mdl,tokenizer=tkz)
inp=tkz("夜の底が[MASK]なった。",return_offsets_mapping=True)
print(list(zip(tkz.convert_ids_to_tokens(inp["input_ids"]),inp["offset_mapping"])))
print(fmp("夜の底が[MASK]なった。"))

「夜の底が[MASK]なった。」をトークナイズしつつ、[MASK]を穴埋めさせてみたところ、私の手元では以下の結果になった。

[('[CLS]', (0, 0)), ('▁夜', (0, 1)), ('▁の', (1, 2)), ('▁底', (2, 3)), ('▁が', (3, 4)), ('[MASK]', (4, 10)), ('▁なった', (10, 13)), ('▁。', (13, 14)), ('[SEP]', (0, 0))]
[{'score': 0.29466712474823, 'token': 4130, 'token_str': '深く', 'sequence': '夜 の 底 が 深く なった 。'}, {'score': 0.10997577756643295, 'token': 19502, 'token_str': '暗く', 'sequence': '夜 の 底 が 暗く なった 。'}, {'score': 0.10800448060035706, 'token': 17340, 'token_str': '熱く', 'sequence': '夜 の 底 が 熱く なった 。'}, {'score': 0.07135284692049026, 'token': 27473, 'token_str': '浅く', 'sequence': '夜 の 底 が 浅く なった 。'}, {'score': 0.06170506030321121, 'token': 27949, 'token_str': '冷たく', 'sequence': '夜 の 底 が 冷たく なった 。'}]

ちゃんとSPIECE_UNDERLINEが各単語にくっつきつつ、offset_mappingも返ってきている。穴埋め結果は「深く」「暗く」「熱く」「浅く」「冷たく」となっており、scoreも含め、1月5日の結果とほぼ同等である。ただ、PreTokenizer.customを使うのは、ちょっとトリッキーな気もするので、さて、どうしたものかな。

この議論は、yasuoka (21275)によって ログインユーザだけとして作成されたが、今となっては 新たにコメントを付けることはできません。
typodupeerror

私は悩みをリストアップし始めたが、そのあまりの長さにいやけがさし、何も考えないことにした。-- Robert C. Pike

読み込み中...