分かち書きする言語しない言語のトークン化を統一的に扱う方法を妄想してみる

日本語で文を書くときには、分かち書き(文節や単語の間にスペースを入れる)をしないことが多い。 一方で英語などでは分かち書きをする。

ニューラルな言語処理の文脈で英語と日本語の語彙を共有して扱うとき1、この違いは地味に扱いにくい。

実は、分かち書きしない言語はマイナーで、日本語、タイ語、中国語くらいしかないらしいので、言語の割合的には統一的に扱う方法はあまり意味がないのかもしれないが、今回はそれを考えてみる。

分かち書きする言語しない言語のトークナイズを統一的に扱う方法

A. 分かち書きする言語しない言語の基本的なトークン化

分かち書きを行う言語 では、基本的にスペースをトークンの目印として使い、トークナイズする。 トークナイズした後はスペースはいったん消して、モデルの出力をスペースでつなぎ合わせて最終的な文を得る。
ただし、最近はスペースで分けたトークンをさらに細かく分割することで限られた語彙数の下で効率と精度の向上を狙うことも多い。 この場合、あるトークンが前のトークンにつながるトークンであることを表示する。

'But Tenjin are not relieved from Bonno (earthly desires).'
['but', 'ten', '##jin', 'are', 'not', 'relieved', 'from', 'bonn', '##o', '(', 'earthly', 'desires', ')', '.']

分かち書きをしない言語 では、ひとつながりの文字列を何らかのモデルで分割してトークナイズする。 スペース(を使う場合)は、そのまま残しておいて、モデルの出力を''でつなぎ合わせて最終的な文を得る。

'しかしながら煩悩から解き放たれては居ない。'
['しかしながら', '煩', '悩', 'から', '解', 'き', '放', 'た', 'れて', 'は', '居', 'ない', '。']

(それぞれの文は『Wikipedia日英京都関連文書対訳コーパス』より引用)

これらの特徴をまとめると次のようになると考えられる。

分かち書きする 分かち書きしない
トークン化したときのスペース 消す 残す
トークン化したときの連結 残す 消す
連結するときの文字列 ' ' ''

2種類の扱い方は相補的になっている。

B. どちらにも対応できるようにするには

では、この2つのトークンの扱い方を統一するにはどうするのが良いだろうか?2

連結を表示する方法を採用すると、スペースを採用する言語の表記が長くなってしまうし、逆もまたしかりだ。

そこで、どちらかに限定するのではなく両方を扱える方針で考えてみる。

例を示すとこのような感じ('_'はスペースを'##'は連結を表す):

'But Tenjin are not relieved from Bonno (earthly desires).'
['but', '_', 'ten', '##', 'jin', '_', 'are', '_', 'not', '_', 'relieved', '_', 'from', '_', 'bonn', '##', 'o', '_', '(', '##', 'earthly', '_', 'desires', '##', ')', '##', '.']
'しかしながら煩悩から解き放たれては居ない。'
['しかしながら', '##', '煩', '##', '悩', '##', 'から', '##', '解', '##', 'き', '##', '放', '##', 'た', '##', 'れて', '##', 'は', '##', '居', '##', 'ない', '##', '。']

トークン列の'##'を見つけたらその両端のトークンをつなぎ合わせたうえで、''で連結すればどちらの立場のトークン化でも、もとの文が復元できる。

C. 制御文字を導入して冗長性を減らす

しかし、このままでは冗長でかなり文字列が長くなってしまう。

そこで、2つの立場を表す制御文字を先頭に導入する。

  • <j=' '>: スペースを省略していて、スペースで連結する。
  • <j=''>: 連結を省略していて、''で連結する。
['<j=' '>', 'but', 'ten', '##', 'jin', 'are', 'not', 'relieved', 'from', 'bonn', '##', 'o', '(', '##', 'earthly', 'desires', '##', ')', '##', '.']
['<j=''>', 'しかしながら', '煩', '悩', 'から', '解', 'き', '放', 'た', 'れて', 'は', '居', 'ない', '。']

D. 制御文字の選択

できるだけ文の長さは短いほうがよいので、いったん何の言語で書かれているかは忘れて、短くなる方を採用することを考えてみる。

言語によらず出現割合に基づいてトークナイズできるSentencePieceを用いてトークン化のモデルを作ったとする(ただし、スペースを強制的に1トークンとする)。

文をトークン化したとき、トークン数をn、スペースの数をsとすれば、自分の前がスペースではないトークン(つまり、連結しているトークン)の数cは次の式で表せる。

c = (スペースではないトークン数) - (スペースの数) = (n - s) - s

sとcを比較して、sが大きいときは連結を表示する方が系列長を短くでき、逆にsがc以下の時はスペースを表示した方が短くできる。

s > c ⇔ s/n > 1/3

従って、全トークンに対するスペースの占める割合が1/3を超えたときは、連結を表示し、1/3を下回ったときはスペースを表示する選択が効率的である。

実験と結果

では、現実に存在するコーパスに適用した場合どのようになるだろうか。

今回は、次の3つトークナイズのモデルを用意し、トークン列の長さの観点で比較を行った。

  1. githubgoogle/bertで使われているWordPiece (語彙数: 色々な言語の合計約30,000)

  2. (スペース表示) スペースを1トークンとして強制したSentencePiece3 (語彙数: 日英の合計約40,000)

  3. (space-switching) 2 + スペース/連結両者の表示 + 制御文字 + 制御文字の選択

Wikipedia日英京都関連文書対訳コーパス』から日英44359文対をサンプルし、それぞれのモデルでトークナイズを行った。

結果は下表。

スペースを表示 space-switching (WordPiece)
平均トークン化時間[マイクロ秒/文] 87 92 433
英語平均トークン数[token/文] 54.8 45.8 32.9
日本語平均トークン数[token/文] 23.0 24.0 36.6
  • 日本語では制御文字<join=''>を先頭に付加した分、1トークン増えている。
    これは想定できる結果である。

  • 英語については平均トークン数が54.8から45.8に減少(83%)し系列長の削減に一定の効果があることがわかる。 しかし、bertに付属したWordPieceの平均長に対しては139%長く、期待したほど系列長は短くなってはいない。

英語のトークナイズを連結表示に変えても系列長があまり減少しない原因には次のようなものが考えられる。

  1. カンマやピリオド、引用符、かっこ等の記号が連結記号を伴ってしまい2倍の場所をとる。
  2. eが含まれる部分が分割されることが多く場所をとる。

トークナイズの結果を観察していると、we, be, -ed, -es, -erなど頻出する語、部分文字列が分割されている場合が多かった。 これは推測だが、英語ではeを含む部分文字列が多く、それらを個別にトークン化するよりもeを単体としてトークン化した方がカバレッジが上がると、sentencepieceは判断しているものと思われる。

1, 2で上げたようなトークンを特別に扱うルールを設ければ系列長はもう少し減少できそうだが、汎用性とのトレードオフになる。

この方法は汎用性と効率(系列長)が比較的バランスしているのではないだろうか。


  1. 基本的な語彙に交わりは少ないが、日本語の文で英単語をそのまま使ったりすることはあるし、ネットワークの構造を簡単にできるので意味はあると思う。

  2. この記事では一つの入力文では扱い方が統一されている場合に限定して考えている。

  3. 英語wikipediaと日本語wikipediaからサンプルしてきた文を用いてSentencePieceのモデルを学習したモデル(sp_uncase_en_ja_40000)。