文字から単語へ:日本語BPEトークナイザーの構築

出版日: 2025年7月5日

文字から単語へ:日本語BPEトークナイザーの構築

この記事で学べること

  • バイトペアエンコーディング(BPE)とは何か、なぜ現代のNLPで重要なのか
  • BPEが意味を保持しながら語彙サイズを削減する方法
  • 日本語テキスト用の完全なBPEトークナイザーの構築
  • アルゴリズムの動作を示すインタラクティブな例

バイトペアエンコーディングとは?

バイトペアエンコーディング(BPE)は、GPTやBERTなどの現代の言語モデルを支えるサブワードトークン化アルゴリズムです。元々はデータ圧縮用に設計されましたが、現在はNLPに欠かせない技術となっています。

アルゴリズムはシンプルです:個別の文字から始めて、最も頻度の高い隣接ペアを反復的にマージし、目標の語彙サイズに達するまで続けます。これにより文字レベルの粒度と単語レベルの意味論のバランスが取れます。

なぜBPEが必要なのか?

従来のトークン化手法には大きな制限があります:

単語レベルの問題

  • • 巨大な語彙サイズ(10万語以上)
  • • 語彙外単語を処理できない
  • • 形態論的に豊富な言語で苦戦
  • • 稀な単語の処理が困難

BPEの利点

  • • 管理しやすい語彙(5K-50Kトークン)
  • • 語彙外単語なし
  • • サブワードパターンを捉える
  • • 言語に依存しない手法

特に日本語では、BPEは一般的な漢字の組み合わせ、助詞、文法パターンを明示的な言語知識なしに自動的に学習できるため、特に価値があります。

インタラクティブBPEデモ

日本語テキストでBPEがどのように段階的に動作するかを見てみましょう:

元のテキスト: 最近TVでも人気者のお笑い

ステップ 0 / 5

T V

実装:日本語データの読み込み

ライブドアニュースコーパスを使用しました。日本語ニュース記事を含み、トークナイザーの学習に最適です:

# 日本語ニュースコーパスのダウンロードと読み込み
import tarfile
import urllib.request
import os

filename = "ldcc-20140209.tar.gz"
if not os.path.exists(filename):
    print("ダウンロード中...")
    urllib.request.urlretrieve("https://www.rondhuit.com/download/ldcc-20140209.tar.gz", filename)

text = ""
articles = []
article_count_limit = 1000

with tarfile.open(filename, "r") as tar:
    for member in tar.getmembers():
        if member.isfile() and member.name.endswith(".txt"):
            file_content = tar.extractfile(member).read().decode("utf-8")
            lines = file_content.split("\n")
            if len(lines) >= 4:  # URL、日付、タイトルをスキップ
                content = "\n".join(lines[3:]).strip()
                if content:
                    articles.append(content)
                    text += content + "\n\n"

print(f"テキスト長: {len(text):,}")
print(f"記事数: {len(articles):,}")
print(f"初期語彙サイズ: {len(set(text)):,}")

データセット統計

  • 659,423 総文字数
  • 1,000 ニュース記事
  • 2,913 ユニーク文字(初期語彙)

コアBPEアルゴリズム

BPEアルゴリズムは2つの主要な関数で構成されます:頻度の高いペアの発見とマージ:

def get_pairs(tokens):
    """隣接する文字ペアとその頻度を見つける"""
    pairs = defaultdict(int)
    for i in range(len(tokens) - 1):
        pairs[(tokens[i], tokens[i + 1])] += 1
    return pairs

def merge_tokens(tokens, pair):
    """最も頻度の高いペアを単一のトークンにマージ"""
    new_tokens = []
    i = 0
    while i < len(tokens):
        if (i < len(tokens) - 1 and 
            tokens[i] == pair[0] and tokens[i + 1] == pair[1]):
            new_tokens.append(pair[0] + pair[1])  # ペアをマージ
            i += 2
        else:
            new_tokens.append(tokens[i])
            i += 1
    return new_tokens

トークナイザーの学習

個別の文字から始めて、最も頻度の高いペアを反復的にマージします:

# 文字レベルのトークンで初期化
tokens = list(text)  # 個別の文字に分割
vocab = set(tokens)
target_vocab_size = 5000

while len(vocab) < target_vocab_size:
    # 最も頻度の高い隣接ペアを見つける
    pairs = get_pairs(tokens)
    if not pairs:
        break
    
    best_pair = max(pairs, key=pairs.get)
    
    # このペアのすべてのインスタンスをマージ
    tokens = merge_tokens(tokens, best_pair)
    vocab.add("".join(best_pair))
    
    if merge_count % 100 == 0:
        print(f"マージ {merge_count}: 語彙 {len(vocab)}, トークン {len(tokens)}")
マージ# マージされたペア 新しいトークン 頻度
1 最 + 近 最近 847
2 T + V TV 523
3 で + も でも 412
4 人 + 気 人気 389
5 者 + の 者の 301

新しいテキストのトークン化

学習後、BPE語彙を適用して新しいテキストをトークン化できます:

def tokenize_with_bpe(text):
    """学習したBPEマージを新しいテキストに適用"""
    tokens = list(text)  # 文字から開始
    changed = True
    
    while changed:
        changed = False
        new_tokens = []
        i = 0
        while i < len(tokens):
            # 隣接ペアが語彙に存在するかチェック
            if (i < len(tokens) - 1 and 
                tokens[i] + tokens[i + 1] in vocab):
                new_tokens.append(tokens[i] + tokens[i + 1])
                i += 2
                changed = True
            else:
                new_tokens.append(tokens[i])
                i += 1
        tokens = new_tokens
    return tokens

結果とパフォーマンス

結果は日本語テキストに対するBPEの効果を示しています:

# 学習結果:
テキスト長: 659,423
記事数: 1,000
初期語彙サイズ: 2,913
BPE後語彙サイズ: 5,000

# 圧縮結果:
元の文字数: 659,423
BPEトークン: 379,352
圧縮率: 42.5%

入力

659,423

文字

出力

379,352

BPEトークン

圧縮

42.5%

削減

トークン化の例

学習したBPEモデルで日本語の文がどのようにトークン化されるかの例:

元のテキスト: 最近TVでも人気者のお笑いコンビ「しずる」のメンバーで...

BPEトークン:

最近TVでも人気者の笑いコンビしずるメンバーリアル ...

BPEが言語学的前処理なしに「最近」(最近)、「人気」(人気)、「者の」(〜の人)などの意味のあるサブワード単位を自動的に学習していることに注目してください!

日本語にとって重要な理由

日本語はトークン化にユニークな課題を提示します:

  • 単語間にスペースがない: BPEは単語境界を自動的に学習
  • 複数の文字体系: ひらがな、カタカナ、漢字をシームレスに処理
  • 複合語: 頻度の高い漢字の組み合わせを単一トークンとして学習
  • 助詞と文法: 「の」、「で」、「も」などの文法パターンを捉える

試してみる

完全な実装はGitHubでインタラクティブなJupyterノートブックとして利用できます。以下ができます:

  • 日本語コーパスでBPE学習を実行
  • 異なる語彙サイズで実験
  • 独自の日本語テキストでトークン化をテスト
  • 圧縮統計を分析

コードを取得

GitHubで完全な実装をチェック:

GitHubで見る →

結論

BPEは文字レベルの堅牢性と単語レベルの意味論の完璧なバランスを実現します。シンプルから始めてデータ駆動型マージにより複雑性を構築し、コンパクトかつ意味のある語彙を作成します。

特に日本語テキストでは、BPEが言語パターンを自動的に発見する能力により、現代のNLPアプリケーションに理想的な選択となります。意味的意味を維持しながら達成した42.5%の圧縮がその効果を実証しています。

言語モデル、機械翻訳システム、テキスト分析ツールを構築する場合、BPEの理解と実装は、あらゆる言語、特に日本語などの形態論的に豊富な言語で作業する際に不可欠です。

このブログは英語からLLMによって翻訳されました。不明な点がある場合は、お問い合わせページからご連絡ください。

コメントを残す

コメント

現在コメントがありません。

その他のブログ

Q学習:インタラクティブ強化学習の基礎

2025/07/07

Q学習:インタラクティブ強化学習の基礎

Q学習強化学習探索と利用機械学習AIインタラクティブグリッドワールド価値関数
最適化アルゴリズム:SGD、モメンタム、Adam

2025/07/06

最適化アルゴリズム:SGD、モメンタム、Adam

最適化SGDAdamモメンタム機械学習ディープラーニングニューラルネットワークアルゴリズム
SvelteとJavaScriptを使用してシンプルで動的なツールチップを作成する

2024/06/19

SvelteとJavaScriptを使用してシンプルで動的なツールチップを作成する

JavaScriptSvelteTooltip動的シンプルツールチップフロントエンド
JavaScriptを用いて東京都のインタラクティブな地図を作成する

2024/06/17

JavaScriptを用いて東京都のインタラクティブな地図を作成する

SvelteSVGJavaScriptTailwindインタラクティブな地図東京市区町村23区地図
Matplotlibで日本語文字化けを解決できる簡単な方法

2024/06/14

Matplotlibで日本語文字化けを解決できる簡単な方法

MatplotlibグラフチャートPython日本語文字化け問題バグ
書評 | トーキング・トゥ・ストレンジャーズ 「よく知らない人」について私たちが知っておくべきこと by マルコム・グラッドウェル

2024/06/13

書評 | トーキング・トゥ・ストレンジャーズ 「よく知らない人」について私たちが知っておくべきこと by マルコム・グラッドウェル

書評トーキング・トゥ・ストレンジャーズ「よく知らない人」について私たちが知っておくべきことマルコム・グラッドウェル
日本語で最もよく使われる3000字の漢字

2024/06/07

日本語で最もよく使われる3000字の漢字

3000よく使う準漢字使用回数漢字日本語漢字リスト漢字普及率日本語能力試験独学勉強単語
VSCodeでRegexを使用してReplaceする方法

2024/06/07

VSCodeでRegexを使用してReplaceする方法

VSCodeRegex検索置き換える条件付き置換FindReplaceConditional Replace
SvelteではReadable Storeを使用するな

2024/06/06

SvelteではReadable Storeを使用するな

SvelteReadableWritableステート管理ストアStore速度メモリファイルサイズ
GzipとPakoでデータを圧縮してWebサイトのローディング速度を上げる方法

2024/06/05

GzipとPakoでデータを圧縮してWebサイトのローディング速度を上げる方法

Gzip圧縮PakoWebサイトローディング速度SvelteKit
JavaScriptを使用してWebページ上でマウスが指している単語を特定する

2024/05/31

JavaScriptを使用してWebページ上でマウスが指している単語を特定する

JavascriptマウスPointerHoverWeb開発
SvelteとSVGを用いてインタラクティブな地図を作成する

2024/05/29

SvelteとSVGを用いてインタラクティブな地図を作成する

SvelteSVGインタラクティブな地図フロントエンド
書評 | Originals 誰もが「人と違うこと」ができる時代 by アダム・グラント & シェリル・サンドバーグ

2024/05/28

書評 | Originals 誰もが「人と違うこと」ができる時代 by アダム・グラント & シェリル・サンドバーグ

書評Originals誰もが「人と違うこと」ができる時代アダム・グラント & シェリル・サンドバーグ
Javascriptを使用して数独を解く方法

2024/05/27

Javascriptを使用して数独を解く方法

数独を解くアルゴリズムJavaScriptコーディング
ウェブサイトへのトラフィックを1か月で10倍に増やした方法

2024/05/26

ウェブサイトへのトラフィックを1か月で10倍に増やした方法

ウェブサイトへのトラフィック増加クリックインプレッションGoogle Search Console
人生はサイクリングに似ている

2024/05/24

人生はサイクリングに似ている

サイクリング人生哲学成功
JavaScriptでバックトラッキング・アルゴリズムを用いて完全な数独グリッドを生成する

2024/05/19

JavaScriptでバックトラッキング・アルゴリズムを用いて完全な数独グリッドを生成する

数独バックトラッキング・アルゴリズム完全なグリッドJavaScript
Tailwindが素晴らしい理由とWeb開発をいかに楽にするか

2024/05/16

Tailwindが素晴らしい理由とWeb開発をいかに楽にするか

Tailwind素晴らしいフロントエンドWeb開発
PythonとGitフックを使用してサイトマップを自動的に生成する

2024/05/15

PythonとGitフックを使用してサイトマップを自動的に生成する

GitフックPythonサイトマップSvelteKit
書評 | Range (レンジ) 知識の「幅」が最強の武器になる by デイビッド・エプスタイン

2024/05/14

書評 | Range (レンジ) 知識の「幅」が最強の武器になる by デイビッド・エプスタイン

書評Range (レンジ)David Epstein (デイビッド・エプスタイン)知識の「幅」が最強の武器になる
SvelteとSvelteKitはなんですか?

2024/05/13

SvelteとSvelteKitはなんですか?

SvelteSvelteKitFront-endVite
SvelteKitで国際化(多言語化)

2024/05/12

SvelteKitで国際化(多言語化)

国際化多言語SvelteKitI18N
SvelteでCachingを用いてDeploy時間を短縮する方法

2024/05/11

SvelteでCachingを用いてDeploy時間を短縮する方法

SvelteEnhanced ImageCachingDeploy Time
SvelteとIntersection Oberverによるレイジーローディング

2024/05/10

SvelteとIntersection Oberverによるレイジーローディング

レイジーローディングウェブサイト速度の最適化SvelteIntersection Observer
遺伝的アルゴリズムで最適な株式ポートフォリオを作る方法

2024/05/10

遺伝的アルゴリズムで最適な株式ポートフォリオを作る方法

株式書状ポートフォリ最適化遺伝的アルゴリズムPython
Pythonを用いてShapeFileをSVGに変換できる方法

2024/05/09

Pythonを用いてShapeFileをSVGに変換できる方法

ShapeFileSVGPythonGeoJSON
Svelteの反応性:変数、バインディング、およびキー関数

2024/05/08

Svelteの反応性:変数、バインディング、およびキー関数

Svelte反応性バインディングキー関数
書評 | 孫子の兵法

2024/05/07

書評 | 孫子の兵法

書評The Art Of War (兵法)Sun Tzu (孫子)Thomas Cleary
スペシャリストは終了。ゼネラリスト万歳!

2024/05/06

スペシャリストは終了。ゼネラリスト万歳!

専門家ジェネラリストパラダイムシフトソフトウエア・エンジニアリング
トルコ人の有権者の投票行動をPythonでの分析

2024/05/03

トルコ人の有権者の投票行動をPythonでの分析

トルコ投票者年齢分析国家投票有権者行動分析
Seleniumを用いてトルコ投票データベースを作る方法

2024/05/01

Seleniumを用いてトルコ投票データベースを作る方法

PythonSeleniumWeb Scrapingトルコ国家投票
SvelteとTailwindを使用してInfinite Scrollできる方法

2024/04/30

SvelteとTailwindを使用してInfinite Scrollできる方法

SvelteTailwindInfinite ScrollFront-end
1年間以内で日本語を駆使できるようになるための方法

2024/04/29

1年間以内で日本語を駆使できるようになるための方法

日本語短時間言語学習日本語能力試験ビジネス日本語
SvelteとTailwindを用いたWebサイトテンプレート

2024/04/25

SvelteとTailwindを用いたWebサイトテンプレート

Web開発フロントエンドSvelteTailwind
怠惰なエンジニアとひどいデザイン

2024/01/29

怠惰なエンジニアとひどいデザイン

怠け者エンジニア質の悪い製品StarbucksSBI証券
偉大さについて

2024/01/28

偉大さについて

雄大さ人生の意味満足できる人生目的
MacBook で PDF を PNG に変換する

2024/01/28

MacBook で PDF を PNG に変換する

PDFPNGMacBookAutomator
2023年振り返り:24冊の読んだ本のまとめ

2023/12/31

2023年振り返り:24冊の読んだ本のまとめ

読書 2023振り返り
Python PILを使用して写真コラージュを作成する方法

2023/12/30

Python PILを使用して写真コラージュを作成する方法

PythonPIL画像処理コラージュ
ウェブサイトの訪問者のデバイスとブラウザを検出する方法

2024/01/09

ウェブサイトの訪問者のデバイスとブラウザを検出する方法

Javascript端末検知ブラウザ検知Website分析
ChatGPT回答の解析

2024/01/19

ChatGPT回答の解析

ChatGPT大規模言語モデル機械学習生成AI