TT Lab
はじめる
学ぶ 学習パス コース

LLMエンジニアリング

BPEトークナイザを土台から作る

TT Labで続きを見る

目標

BPEトークナイザーを、ライブラリなしでPythonだけで実装し、学習からエンコードとデコードまで、1サイクルを完成させます。

なぜ重要なのか

トークナイザーは、LLMパイプラインで最初に実行され、最も理解されていない部品です。ところが、ここで決まることが多くあります。プロンプトのコストは、文字数ではなくトークン数で課金され、コンテキスト長の制限もトークン単位です。モデルが単語のスペルに関する質問に特に弱いのも、トークンの境界が文字の境界と違うからです。

自分で作ってみると、こうした性質が抽象的な話ではなく、実装の自然な帰結であることがわかります。特に最後のステップで圧縮比を測ると、韓国語がなぜ英語よりトークンを多く食うのかを、数値で確認できます。

このラボは、モデルをダウンロードしません。インターネットもGPUも必要ありません。コーパスは、ラボのデータベースのdocsテーブルに入っている韓国語の文書30件です。

ステップ

作業ディレクトリは/root/llmです。ルールを正確に守らないと採点されません。

  1. docsテーブルのbodyをidの昇順で1行ずつ/root/llm/corpus.txtに保存してください。30行です。
  2. コーパスに出てくる文字の頻度を/root/llm/char_freq.tsvに保存してください。문자<탭>빈도の形式で(プレースホルダーは文字と頻度で、間はタブです)、空白文字は数えません。並び順は頻度の降順で、同じなら文字の昇順です。
  3. 基本語彙を/root/llm/vocab_base.txtに1行に1つずつ保存してください。コーパスの文字(空白を除く)に単語末尾の印の文字(_)を加えた集合を、コードポイントの昇順でソートします。
  4. BPEのマージルール120個を/root/llm/merges.txtに保存してください。1行に앞토큰 뒤토큰を空白で区切って書きます(プレースホルダーは前のトークンと後ろのトークンです)。学習のルールは次のとおりです。
    • 各行を空白で分けて単語を作り、各単語を문자들 + ['_']のリストで始めます(プレースホルダーは文字の並びです)。
    • 各段階で、全単語にわたって隣接ペアの頻度を数え、最も頻度が大きいペアをマージします。
    • 頻度が同じなら、(앞토큰, 뒤토큰)タプルの辞書順が小さいペアを選びます(プレースホルダーは前のトークンと後ろのトークンです)。
    • マージは、各単語で左から重ならないように適用します。
  5. 最終語彙を/root/llm/vocab.tsvに토큰<탭>idの形式で保存してください(プレースホルダーはトークンで、間はタブです)。ステップ3の基本文字を順にidの0から入れ、そのあとにマージで生まれたトークンを、マージ順に付けます。
  6. 各文書をトークンid列に変換して/root/llm/encoded.tsvに保存してください。1行にdocs.id<탭>id id id ...の形式で(プレースホルダーはタブです)、30行です。エンコードは、学習したマージルールを記録された順序どおりに適用します。
  7. vocab.tsvとencoded.tsvだけで元の文を復元して、/root/llm/decoded.txtに保存してください。トークンをつなげて_を空白に置き換え、行末の空白を削除すれば、元の文と同じになるはずです。
  8. /root/llm/stats.tsvに1行で문자수<탭>토큰수<탭>압축비を保存してください(プレースホルダーは文字数、トークン数、圧縮比で、間はタブです)。文字数はコーパスの各行の長さの合計(空白を含む)、トークン数は全トークンの個数、圧縮比は文字数をトークン数で割って小数点以下3桁に丸めた値です。

参考

コーパスの取得

docsテーブルのbodyをidの昇順で1行ずつ/root/llm/corpus.txtに保存してください。30行です。

docsテーブルのbodyをid順に1行ずつファイルに書きます。psqlで結果だけを出力するオプションを使うと便利です。

文字の頻度を数える

コーパスに出てくる文字の頻度を/root/llm/char_freq.tsvに保存してください。문자<탭>빈도の形式で(プレースホルダーは文字と頻度で、間はタブです)、空白文字は数えません。並び順は頻度の降順で、同じなら文字の昇順です。

空白は数えません。並べ替えの基準が2つあることに注意してください。

基本の文字語彙を作る

基本語彙を/root/llm/vocab_base.txtに1行に1つずつ保存してください。コーパスの文字(空白を除く)に単語末尾の印の文字(_)を加えた集合を、コードポイントの昇順でソートします。

コーパスに出てくる文字に加えて、単語の末尾を表す印の文字も、語彙に入ります。

マージルール120個を学習する

BPEのマージルール120個を/root/llm/merges.txtに保存してください。1行に앞토큰 뒤토큰を空白で区切って書きます(プレースホルダーは前のトークンと後ろのトークンです)。学習のルールは次のとおりです。

各段階ごとに、隣接ペアの頻度を数え直す必要があります。同点処理のルールを正確に守ってください。

最終的な語彙辞書を作る

最終語彙を/root/llm/vocab.tsvに토큰<탭>idの形式で保存してください(プレースホルダーはトークンで、間はタブです)。ステップ3の基本文字を順にidの0から入れ、そのあとにマージで生まれたトークンを、マージ順に付けます。

基本文字を先に入れ、そのあとにマージが起きた順に付けます。idは0から連続です。

文書をトークンid列に変換する

各文書をトークンid列に変換して/root/llm/encoded.tsvに保存してください。1行にdocs.id<탭>id id id ...の形式で(プレースホルダーはタブです)、30行です。エンコードは、学習したマージルールを記録された順序どおりに適用します。

学習のときと同じ順序でマージルールを適用すると、同じ結果になります。

id列だけで元の文を復元する

vocab.tsvとencoded.tsvだけで元の文を復元して、/root/llm/decoded.txtに保存してください。トークンをつなげて_を空白に置き換え、行末の空白を削除すれば、元の文と同じになるはずです。

トークンをつなげてから、単語末尾の印を空白に戻します。行末の余分な空白は削除します。

圧縮比を計算する

/root/llm/stats.tsvに1行で문자수<탭>토큰수<탭>압축비を保存してください(プレースホルダーは文字数、トークン数、圧縮比で、間はタブです)。文字数はコーパスの各行の長さの合計(空白を含む)、トークン数は全トークンの個数、圧縮比は文字数をトークン数で割って小数点以下3桁に丸めた値です。

文字数をトークン数で割ります。文字数には空白も含めます。