この用語と一緒に検索・参照されやすいタグ。 関連ページに飛ぶときの手がかりにも使えます。
「ngram model」は統計データ分析の文脈で扱う重要概念のひとつ。 本ページでは「ngram model」を取り巻く中核キーワードを以下にチップで一覧化する。 各キーワードは関連する概念・手法・道具立てを含み、 文献検索や学習計画の起点になる。
これらのキーワードは「ngram model の理解 → 適用 → 検証」のプロセスを構成する。 各章で詳しく解説する。
🍰 まずはやさしく
次に来る言葉を予想する仕組みです。
文章の自然な流れを作るために使います。
スマホの文字入力の候補に出る機能です。
このモデルの結論を短くまとめます。
n-gram モデルは、 単語列の確率を「直前 (n-1) 単語だけ」に依存して近似する古典的な言語モデル。
ここまでが要点です。 ただし実際に使う前に、 このページの「⚠️ よくある落とし穴」で挙げた ゼロ頻度問題/長距離依存を無視/メモリ消費 には必ず目を通してください。 つまずくのは知識が無いときより、 知ってはいたが確認を飛ばしたときです。
🍰 まずはやさしく
言葉の並びを分析する道具です。
データの変化やパターンを探るために使います。
地域の人口の変化を分析する時に役立ちます。
どんな場面で使うのかを詳しく説明します。
現代は Transformer ベースの LLM が主流ですが、 n-gram は言語モデルの原点として今も学ぶ価値があります。 軽量さから組み込み・モバイル用途では現役で、 LLM の評価ベースラインとしても登場。
この用語は一見すると単独で理解できそうに見えますが、 実際には前提となる概念(測定・尺度・サンプリングなど)と組合せて初めて意味を持ちます。 「定義を覚える」より「どんな問いに答える道具なのか」を捉えるのが効率的です。
統計データ解析コンペ(2026 年版)では、 SSDSE-B-2026 の都道府県別パネルデータを対象に「時系列パターンの定量分析」が求められる場面があります。 n-gram モデルを知っていると、 「人口減少の連続パターン」「所得階層の遷移確率」といった離散系列の分析に直接適用でき、 他の参加者との差別化になります。 また LLM・Transformer の理解においても、 n-gram が「何を解決しようとして、 なぜ限界があったか」を知ることが、 注意機構の必然性を理解する近道です。
🍰 まずはやさしく
直前の言葉だけを見る予測ゲームです。
次に何が来るか直感的に当てるために使います。
検索窓に文字を入れると候補が出る機能です。
イメージ図を使って仕組みを解説します。
「n-gram モデル」を最初に学ぶときは、 厳密な定義よりイメージを優先しましょう。 以下は具体例・比喩を用いた直感的理解の入口です。
日常生活での n-gram 体験:スマートフォンのキーボードで「ありがと」と入力すると「ありがとう」「ありがとうございます」が候補表示されます。 これはまさに bigram/trigram が「直前の入力文字列に続く確率の高い文字」を返している動作。 Google 検索ボックスの予測補完も同様です。 つまり n-gram は私たちが毎日使っている技術の根底にある、 身近な手法なのです。
n-gram モデルを実データで検証するには、 「頻度分布」「文脈別パープレキシティ」「n の選択」 を統計的に可視化する必要があります。 ここでは SSDSE-B-2026 の都道府県名や政策文書をコーパスとみなし、 拡張の図 3 点・表 3 点・Python 実装を提示します。
| n | 名称 | 語彙数(V)で必要な学習量 | 用途 |
|---|---|---|---|
| 1 | unigram | V | 語頻度分析 |
| 2 | bigram | V² | スペル訂正 |
| 3 | trigram | V³ | 音声認識 |
| 4 | 4-gram | V⁴ | 機械翻訳 |
| 5+ | 高次 | 巨大 | Web 規模 |
| ∞ | RNN/Transformer | 可変 | 現代 LLM |
| 手法 | 考え方 | 適性 |
|---|---|---|
| Laplace (+1) | 未出現に 1 加算 | 単純・粗い |
| Lidstone (+δ) | 小さい δ で加算 | 中庸 |
| Good-Turing | 出現回数 r の頻度を r+1 由来に置換 | 理論的 |
| Kneser-Ney | 継続確率を重視 | 標準的に最良 |
| Backoff | 低次 n-gram にフォールバック | 実装容易 |
| Interpolation | 複数 n を線形結合 | 柔軟 |
| モデル | 文脈長 | 解釈性 | 計算量 |
|---|---|---|---|
| n-gram | n-1 固定 | 高 | 低 |
| RNN | 可変 | 中 | 中 |
| LSTM | 長距離可 | 中 | 中 |
| Transformer | 数千トークン | 低 | 高 |
| GPT 系 | 数万トークン | 低 | 非常に高 |
| Hybrid | 併用 | 中 | 中 |
このコードでやること:SSDSE-B-2026 の都道府県名コーパス(仮想)から bigram モデルを構築し、 「東京」 の次に来る単語を確率付きで出力する。
📥 入力データ (政策文書サンプル):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | from collections import Counter, defaultdict corpus = "東京 都 人口 減少 東京 都 GDP 増加 東京 都 観光 客 増加 大阪 府 人口 減少 大阪 府 GDP 増加".split() # bigram 集計 bigrams = list(zip(corpus, corpus[1:])) bigram_counts = Counter(bigrams) unigram_counts = Counter(corpus) V = len(set(corpus)) # Laplace スムージング付き条件付き確率 def prob(w_prev, w): return (bigram_counts[(w_prev, w)] + 1) / (unigram_counts[w_prev] + V) # 「東京」 の次の単語の確率トップ 3 candidates = [(w, prob('東京', w)) for w in set(corpus)] candidates.sort(key=lambda x: -x[1]) print("「東京」 の次に来る単語の確率:") for w, p in candidates[:3]: print(f" {w}: {p:.3f}") |
📤 実行すると次の出力が得られる:
💬 結果の読み方:「東京」 の直後は「都」 が圧倒的に高確率。 単純な集計でも、 単語連鎖の構造を確率として抽出できることが分かる。 これが n-gram モデルの基本原理。 ただし語彙数 V が増えると Laplace の補正が過剰になるため、 Kneser-Ney 等の高度なスムージングが必要になる。
n-gram モデルは古典でありながら、 現代 NLP の基礎理解と評価指標(パープレキシティ等)の出発点として今も重要。 SSDSE-B-2026 の説明文や政策議事録に適用すれば、 都道府県別の言語的特徴を統計的に抽出できる。
パープレキシティ (Perplexity, 略 PPL) は n-gram モデルや LLM の性能評価の標準指標。 定義は 2^H(p) で、 H(p) はテスト集合上のエントロピー (1 単語あたりの平均負対数尤度)。 直感的には「モデルが次に出る単語をどれだけ迷っているか」 を表し、 PPL=10 ならモデルが「10 候補の中からランダムに選ぶ」 程度の不確実性、 PPL=1 なら「完全に確信している」。 良いモデルは PPL が低い。 unigram モデルでは PPL が数千、 trigram (Kneser-Ney) で数百、 GPT-2 で数十、 GPT-4 で 10 未満、 と歴史的に下がっている。
パープレキシティの計算には対数尤度の総和を取り、 単語数で割り、 指数化する。 Python では 2 ** (-log2_likelihood / num_words)。 NLTK の nltk.lm.MLE や nltk.lm.KneserNeyInterpolated モデルでは model.perplexity(test_text) で直接計算できる。 ただしパープレキシティは「次単語予測の難しさ」 を測るもので、 「人間にとっての自然さ」 と必ずしも一致しない。 GPT-4 のような LLM では、 PPL が低くても「事実と異なる」 ハルシネーションが起こることがあり、 別の評価指標 (人間評価、 BLEU、 ROUGE、 BERTScore) と併用する。
n-gram モデルの歴史を 1948 年 Shannon から 2026 年まで体系的に整理する。 (1) 1948: Shannon "A Mathematical Theory of Communication" で n-gram の概念を導入、 英語の冗長性を測定。 (2) 1950 年代: Markov chain の言語モデルとして再発見、 翻訳・暗号解読に応用。 (3) 1980 年代: IBM が音声認識で trigram モデルを大規模展開、 BBN, AT&T も追随。 (4) 1995: Kneser-Ney スムージング提案、 n-gram モデルの最高峰として君臨。 (5) 2003: Bengio "Neural Probabilistic Language Model"、 ニューラル言語モデルの最初の重要論文。 (6) 2010: Mikolov "Recurrent Neural Network Language Model"、 RNN-LM が n-gram を上回る。 (7) 2013: Mikolov "Word2Vec"、 単語埋め込みで NLP のパラダイムシフト。 (8) 2018: BERT、 双方向 Transformer で多数のタスクを革新。 (9) 2020: GPT-3、 大規模言語モデルの時代が始まる。 (10) 2022-2026: ChatGPT, Claude, Gemini 等の対話型 LLM が一般化。
この約 80 年の歴史を通じて、 n-gram モデルは「最古典」 として今なお生き続けている。 Google の検索クエリ補完、 IME の予測変換、 スペル訂正、 言語識別、 等で実用稼働している。 学術的にも、 LLM の性能評価のベースライン、 教育目的での言語モデル入門、 軽量モデルが必要な組込みシステムで使われている。 「古典は古びない」 という言葉が、 n-gram に最も当てはまる。 SSDSE-B-2026 のような数値統計と組み合わせれば、 「都道府県別の政策議事録の語彙特徴」 等の応用も可能。
n-gram モデルは「直前 n-1 単語から次の単語の確率を予測する」 言語モデルの最古典で、 1948 年の Shannon に源流を持ち、 2026 年現在も多様な用途で生き続けている。 unigram から 5-gram まで n の選択は学習データ量と用途で決まり、 bigram と trigram が実用的中心。 スムージング (Laplace, Good-Turing, Kneser-Ney, Stupid Backoff) で未観測 n-gram の確率を補正する。 評価指標はパープレキシティ。 現代の深層学習 LLM (Transformer, GPT) と比べると性能は劣るが、 軽量・解釈可能・高速という利点で、 検索補完・スペル訂正・言語識別等で実用稼働。 教育目的では n-gram で言語モデルの基本を理解し、 そこから RNN→LSTM→Transformer→GPT へ発展史を辿るのが学習経路として最良。 SSDSE-B-2026 の数値統計と組み合わせれば、 「都道府県別文書の語彙特徴抽出」 等の応用が考えられる。
| 項目 | trigram (Kneser-Ney) | BERT | GPT-3 | GPT-4 |
|---|---|---|---|---|
| パラメータ数 | — | 1.1 億 | 1750 億 | 非公開 (推定数兆) |
| パープレキシティ (英語 PTB) | 約 140 | 約 30 | 約 20 | 約 10 以下 |
| 学習データ | 数百万〜数億単語 | 33 億単語 | 5000 億単語 | 非公開 (推定 10 兆超) |
| 推論速度 (1 文) | マイクロ秒 | ミリ秒 | 数秒 | 数秒〜十秒 |
| 計算リソース | CPU 1 コアで動く | GPU 1 枚 | GPU クラスタ | 大規模 GPU クラスタ |
| 解釈可能性 | 高 (確率を直接見られる) | 中 (注意機構の可視化) | 低 | 低 |
| ハルシネーション | 無 (確率分布に厳密) | 稀 | あり | あり |
| 長距離依存 | 不可 | 数百単語 | 数千単語 | 数十万トークン |
| 多言語 | 言語別に学習 | 多言語版あり | 多言語対応 | 多言語対応 |
→ 性能で言えば GPT-4 が圧勝だが、 軽量さ・速さ・解釈性では n-gram が勝る。 用途で使い分けるのが現代的アプローチ。 検索クエリ補完のように 1 秒未満で応答が必要なら n-gram、 対話システムで深い理解が必要なら LLM、 という選択。 SSDSE のような研究・教育目的では、 n-gram で言語モデルの本質を学んでから LLM の動作原理を学ぶ順序が、 理解の深さを保証する。
深層学習 LLM が主流となった 2026 年でも、 n-gram には新しい応用が発見されている。 (1) LLM の検出 (AI-generated text detection): 人間が書いた文と LLM が生成した文では n-gram 分布が異なる。 これを利用した検出器が開発されている。 GPTZero や OpenAI Classifier 等が代表例。 (2) 入力プロンプトの安全性チェック: 悪意あるプロンプト (Jailbreak) の検出に n-gram が有効。 軽量で高速、 リアルタイムフィルタリングに適する。 (3) 多言語コーパスの統計的分析: 数百言語の Web コーパスから n-gram 頻度を抽出し、 言語間の類似性を測る。 (4) 古典文献の著者推定: シェイクスピア作品とフランシス・ベーコン作品の比較等、 stylometry の古典手法。 (5) DNA 配列解析: 生物学的配列の k-mer 解析で、 n-gram 的手法が遺伝子発現予測等に応用される。
SSDSE-B-2026 の文脈でも、 都道府県別の文書 (議事録・観光案内・特産品説明等) を n-gram で分析することで、 (1) 各県の特徴的なフレーズ抽出、 (2) 地域別の言語的多様性の定量化、 (3) 時系列での言語使用の変化追跡、 等の応用が考えられる。 n-gram は「単純で古典的」 だが、 「直感的で実装が容易」 という利点があり、 大規模 LLM を使うほどではない用途では今も第一選択肢。 学生諸氏が n-gram の基本を体得することは、 言語処理の本質を理解する近道で、 将来 LLM を使う際にも「内部で何が起こっているか」 の感覚を持つ基礎となる。
n-gram は機械翻訳・要約生成の評価指標としても広く使われている。 (1) BLEU (Bilingual Evaluation Understudy, 2002): 機械翻訳の標準指標で、 翻訳結果と参照訳の n-gram 一致率を計算 (通常 1-gram〜4-gram の幾何平均)。 brevity penalty で短い翻訳を罰則化する設計。 (2) ROUGE (Recall-Oriented Understudy for Gisting Evaluation, 2004): 要約評価の標準指標で、 ROUGE-N (n-gram 再現率)、 ROUGE-L (最長共通部分列)、 ROUGE-S (skip-bigram) 等の派生がある。 (3) METEOR (2005): BLEU の改良版で、 同義語・語形変化を考慮した精度・再現率の調和平均。 これらはすべて n-gram の一致率を基礎とした「自動評価」 の代表例。
LLM 時代になっても、 これらの古典評価指標は依然として使われている。 ただし「n-gram 一致率」 は表現の多様性を評価できない (同じ意味を異なる表現で書いた場合の評価が低くなる) という弱点があり、 BERTScore (BERT 埋め込みのコサイン類似度)、 BLEURT (BERT ベースの学習済み評価指標)、 LLM-as-Judge (別の LLM に評価させる) 等の新世代指標が補完的に使われる。 SSDSE-B-2026 を題材に、 自動レポート生成の品質評価を BLEU・ROUGE で測ることは可能で、 教育的演習として価値がある。 n-gram は「評価の根幹」 として、 NLP のあらゆる場面で生き続けている。 古典手法が新時代でも生き残る理由は、 「単純で計算が速い」「結果が解釈可能」「人間の直感と整合する」 という普遍的な利点があるから。 学生諸氏が n-gram の本質を体得することは、 未来の新しい NLP 手法が登場しても、 その本質を素早く理解する基礎力となる。 統計データ分析コンペティションへの参加準備の一環として、 SSDSE-B-2026 の数値データと自由記述データを組み合わせた n-gram 分析に挑戦することは、 統計と NLP の両面のスキルを同時に育てる優れた演習となる。 こうした融合的な学びが、 AI 時代のデータサイエンティストとしての成長を加速する。 古典 n-gram は単純で実装が容易、 結果が解釈可能、 そして他の手法と比較したベースラインとして頼りになる。 学生諸氏が n-gram を確実に身につけることで、 将来の様々な NLP タスクに自信を持って取り組める基盤が形成される。 SSDSE-B-2026 を題材にした地道な実装演習を通じて、 言語処理の本質を体得してほしい。 そうした学びが、 学生諸氏のデータサイエンス能力の核となる。 n-gram の単純さは、 結果の解釈可能性と直結しており、 これは LLM 時代でも色褪せない普遍的な価値である。 学生諸氏には、 古典の価値を見落とさずに、 最新の技術と古典を共に学ぶ姿勢を大切にしてほしい。 そうした幅広い学びが、 AI 時代の真のデータ専門家を育てる土壌となる。 n-gram モデルは半世紀以上にわたり言語処理の中核として生き続けてきた古典であり、 これからも様々な形で活用されていくだろう。 学生諸氏が SSDSE-B-2026 を題材に n-gram の本質を体得することは、 言語処理の長い歴史と現代の最前線を架橋する第一歩となる。 こうした古典と最新の往復が、 学生諸氏の専門性の核を形作る。 SSDSE-B-2026 という素材を通じた地道な学びが、 将来あらゆる場面で生きる知の土台となることを願ってやまない。
🍰 まずはやさしく
言葉のつながりを数式にしたものです。
予測の正しさを計算するために使います。
単語が出る回数を数えて確率を出します。
計算式と記号の意味を一つずつ学びます。
やさしい説明で掴んだ感覚を、ここで n-gram の確率推定(最尤) の定義式に対応づけます。下の式は左辺 $\hat{P}(w_t \mid w_{t-n+1}^{t-1})$ が何で決まるかを右辺で書き下したもので、分数(割り算) が現れます。それぞれの記号が何の量を指すのかは、次の「🔬 数式を言葉で読み解く」で 1 つずつ確かめてください。
$$P_{\text{Lap}}(w_t|w_{t-1})=\frac{\text{count}(w_{t-1},w_t)+1}{\text{count}(w_{t-1})+|V|}$$
分子に 1、 分母に語彙サイズ $|V|$ を足すだけ。 ゼロ頻度を回避できるが、 高頻度組合せの確率も過少評価する欠点あり。
$$r^*=(r+1)\frac{N_{r+1}}{N_r}$$
「頻度 $r$ のものを頻度 $r^*$ に補正する」。 $N_r$ は頻度 $r$ の n-gram の種類数。 これは「未観測の n-gram に確率質量を配分」する高度な手法。
$$P_{\text{KN}}(w_t|w_{t-1})=\frac{\max(\text{count}(w_{t-1},w_t)-d,0)}{\text{count}(w_{t-1})}+\lambda(w_{t-1})P_{\text{cont}}(w_t)$$
割引パラメータ $d$ で高頻度から低頻度へ確率を再配分。 $P_{\text{cont}}(w_t)$ は「$w_t$ が異なる文脈で現れる頻度」で、 「文脈の多様性」を反映する。 現代の n-gram 実装の標準。
$$\hat P(w_t|w_{t-n+1:t-1})=\arg\max_{\theta}\sum_{t} \log P_\theta(w_t|w_{t-n+1:t-1})$$
対数尤度を最大化する解析解として、 頻度比 $\text{count}(\cdot)/\text{count}(\cdot)$ が得られる。 これが最尤推定の閉形式。
$$H(p)=-\sum_w p(w|\cdot)\log_2 p(w|\cdot),\quad \text{Perplexity}=2^H$$
エントロピーは「平均的な不確実性」のビット数。 パープレキシティは「平均的な選択肢数」。 完全予測なら $H=0,\text{PP}=1$、 完全ランダムなら $H=\log_2 |V|, \text{PP}=|V|$。
交差検証でパープレキシティが最小となる $n$ を選ぶ。 通常はバリデーション曲線が U 字を描き、 小さすぎる $n$ では文脈不足、 大きすぎる $n$ ではスパースネスで悪化する。
BLEU(Bilingual Evaluation Understudy) は機械翻訳の自動評価指標。 候補訳文と参照訳文の n-gram 一致率を測ります。 1-gram から 4-gram の幾何平均に、 短すぎる訳文へのペナルティ(brevity penalty)を掛けた式:
$$\text{BLEU}=\text{BP}\cdot\exp\left(\sum_{n=1}^N w_n \log p_n\right)$$
ROUGE は要約の自動評価指標。 ROUGE-N は n-gram 再現率、 ROUGE-L は最長共通部分列に基づく。 どちらも n-gram の応用です。
統計データ解析コンペで自然言語生成タスクが出題された場合(過去に「いじめ・不登校テーマで自由記述の要約」のような事例あり)、 BLEU/ROUGE で評価する流儀を理解しておくと有利。
単語 n-gram は単語境界の問題(特に日本語・中国語)がある一方、 文字 n-gram は文字単位で切り出すため境界問題から解放されます。 言語識別、 誤字訂正、 OCR 後処理、 名前マッチング、 著者推定など多様な応用があります。
scikit-learn の CountVectorizer(analyzer='char', ngram_range=(2,4)) で簡単に文字 2-gram から 4-gram までの BoW を作れます。 SVM やロジスティック回帰の特徴量にして、 短文分類(ツイート分類など)で良いベースラインになります。
このコードでやること:4 つの都道府県テキスト文から文字 2-gram と 3-gram の BoW を作り、 「温暖 vs 寒冷」の 2 クラス分類をロジスティック回帰で行う。
📥 入力データ:
1 2 3 4 5 6 7 8 9 10 11 12 13 | from sklearn.feature_extraction.text import CountVectorizer from sklearn.linear_model import LogisticRegression texts = ['北海道は寒い', '沖縄は暖かい', '東京は人が多い', '長野は寒い'] labels = [0, 1, 1, 0] # 1=温暖、0=寒冷 vectorizer = CountVectorizer(analyzer='char', ngram_range=(2,3)) X = vectorizer.fit_transform(texts) print('特徴量数:', X.shape[1]) print('特徴量例:', vectorizer.get_feature_names_out()[:10]) clf = LogisticRegression().fit(X, labels) print('精度:', clf.score(X, labels)) |
📤 実行すると次の出力が得られる:
💬 4 文から 33 個の文字 n-gram 特徴が生成された。 訓練データと同じデータで評価しているため精度 1.0 は過楽観評価。 実務では交差検証が必要。 文字「寒い」の n-gram が寒冷クラスの決定打になっていることが解釈できる。
| 観点 | BoW(1-gram) | n-gram (n≥2) |
|---|---|---|
| 順序情報 | 無視 | 部分的に保持 |
| 特徴量数 | $|V|$ | 最大 $|V|^n$ |
| スパースネス | 少ない | 多い |
| 計算量 | 軽い | $n$ に比例して重い |
| 性能 | ベースライン | 文脈を捉える分高い |
実務では「unigram + bigram」を併用するのが定石。 単語の存在情報と短い文脈情報を両方使えます。 trigram 以上は通常使わない(スパースネスの代償が大きい)。
| 観点 | n-gram | ニューラル LM |
|---|---|---|
| 表現 | 離散カウント | 連続埋め込み |
| 文脈長 | 固定 $n-1$ | 可変・長距離 |
| 類似語の汎化 | 不可(独立扱い) | 可(意味空間で近い) |
| 解釈性 | 高い(頻度直接) | 低い(ブラックボックス) |
| 学習コスト | 軽い(カウントだけ) | 重い(勾配降下) |
| 推論コスト | 軽い(ハッシュ表参照) | 重い(行列演算) |
| 汎化性能 | 限定的 | 高い |
| ドメイン適応 | 新コーパスでカウント再計算 | ファインチューニング |
「軽量・解釈可能」が要件なら n-gram、 「精度・汎化性能」が要件ならニューラル LM。 統計データ解析コンペでは前者が好まれる傾向。
n-gram は 離散時系列の条件付き確率モデル です。 同族には ARIMA(連続時系列の条件付き平均モデル)、 条件付き確率(汎用)、 ベイズの定理(条件付き確率の代数)があります。
これら全てが「過去 → 未来」「条件 → 対象」という方向性を持つ確率モデル。 学問領域はバラバラですが、 中身は 条件付き確率の連鎖式。 統一視点で学ぶと知識が体系化されます。
A1. (I, love): 2, (love, data): 1, (love, science): 1。 つまり $P(\text{data}|\text{love})=0.5$、 $P(\text{science}|\text{love})=0.5$、 $P(\text{love}|\text{I})=1.0$。
A2. $P_\text{Lap}(\text{rare}|\text{love})=(0+1)/(2+4)=1/6\approx 0.167$。 ゼロ頻度でも非ゼロ確率になる。
A3. $10000^3=10^{12}$ 組合せ、 1 組合せ 8 バイト(int64)として $8\times 10^{12}=8\text{TB}$。 実際は出現しない組合せが大半なのでスパースに格納するが、 最悪は天文学的。
A4. 50 の方が良い。 パープレキシティは「平均的な次語の選択肢数」で、 小さいほど予測が確信を持っている。 ただし同じテストセット・同じ語彙での比較に限る。
補間(interpolation)スムージングは「複数の $n$ の n-gram を重み付き混合する」手法。 単純で実装しやすく、 性能も悪くないため広く使われます。
$$P_{\text{interp}}(w_t|w_{t-2},w_{t-1})=\lambda_3 P(w_t|w_{t-2},w_{t-1})+\lambda_2 P(w_t|w_{t-1})+\lambda_1 P(w_t)$$
$\lambda_3+\lambda_2+\lambda_1=1$ で重みを正規化。 重みは EM アルゴリズム や交差検証で最適化。 SSDSE のような小規模コーパスでは、 3-gram より 2-gram の重みを大きくした方が安定します。
数式を眺めるだけでは身につかないので、 各記号がどんな役割を担っているかを言葉で押さえます。 「数式を音読する習慣」がつくと、 論文や教科書を読むスピードが体感で 2 倍ほど上がります。
全体式の意味:n-gram モデルは文全体の確率を条件付き確率の積として計算します。 例えば「I love NLP」という 3 語文の確率は、 $P(\text{I}) \cdot P(\text{love}|\text{I}) \cdot P(\text{NLP}|\text{love})$ と分解できます。 実際には数値が非常に小さくなるため、 対数空間で計算します:$\log P = \log P(\text{I}) + \log P(\text{love}|\text{I}) + \log P(\text{NLP}|\text{love})$。 このようにして文の「自然らしさ」を数値化し、 最もスコアの高い候補を選ぶのが予測変換・スペル訂正の基本原理です。
n-gram モデルの中心式 $P(w_1,w_2,\ldots,w_T)=\prod_{t=1}^T P(w_t|w_{t-n+1:t-1})$ を、 記号と意味を一つひとつ言葉に翻訳しましょう。 ここでは 500 字を超える精読を行います。
まず左辺の $P(w_1,w_2,\ldots,w_T)$ は 「単語列全体の同時確率」。 「ある文書全体がコーパスから出てくる確率」をモデルが評価するときの根本量です。 たとえば「私は学生です」という 4 語列の確率を、 言語モデルが「コーパス中での出現頻度の推定値」として返してくれる。 これが低ければ「変な日本語」、 高ければ「自然な日本語」と判断できる。
右辺の $\prod_{t=1}^T$ は 「時刻 1 から $T$ まですべての単語について掛け合わせる」。 連鎖律(chain rule of probability)から、 同時確率は条件付き確率の積に必ず分解できます。 $P(w_1,w_2,w_3)=P(w_1)P(w_2|w_1)P(w_3|w_1,w_2)$。 しかしこの厳密な分解は、 長い文では条件部分が爆発的に長くなり実用不可能。
そこで マルコフ近似 を導入:$P(w_t|w_1,\ldots,w_{t-1})\approx P(w_t|w_{t-n+1:t-1})$。 つまり「条件は直近 $n-1$ 語だけにする」。 $w_{t-n+1:t-1}$ という記号は $t-n+1$ から $t-1$ までの単語のスライス。 2-gram なら直前 1 語、 3-gram なら直前 2 語、 5-gram なら直前 4 語。 数字が増えるほど文脈は長くなるが、 データスパースネスも増える。 これは「文脈の長さ vs 推定の安定性」のトレードオフ。
確率の具体的推定は 頻度比:$P(w_t|w_{t-1})=\dfrac{\text{count}(w_{t-1},w_t)}{\text{count}(w_{t-1})}$。 分子は「$w_{t-1}$ の直後に $w_t$ が来る頻度」、 分母は「$w_{t-1}$ の全出現頻度」。 つまり「$w_{t-1}$ が出たとき、 次が $w_t$ である割合」。 これは条件付き確率の最尤推定そのもの。 SSDSE-B-2026 で「人口減少県の翌年も人口減少」の頻度比を計算すれば、 同じ式で県の系列を分析できます。
最後に、 確率の積を取ると数値アンダーフロー(極小値)が起こるので、 実装では 対数確率の和 $\sum_t \log P(w_t|\cdot)$ で計算します。 さらに パープレキシティ $2^{-\frac{1}{T}\sum \log_2 P}$ で言語モデルの品質を測ります。 これは「次の単語を予測するときの平均的な選択肢数」と解釈でき、 値が小さいほど良いモデル。
1.連続 n-gram / Continuous n-gram:離散 n-gram と単語埋め込みを組合せる手法。 ニューラル LM の前身。
2.Subword n-gram:BPE(Byte-Pair Encoding)や WordPiece で単語をサブワードに分割し、 n-gram を取る。 OOV 問題を緩和。 BERT・GPT 系で標準。
3.Multimodal n-gram:画像トークン・音声トークンと言語トークンを混合した n-gram。 マルチモーダル LLM の研究で活用。
4.Differential Privacy n-gram:個人情報を含むコーパスから n-gram を作るとき、 ノイズを加えてプライバシー保護する手法。
5.Causal n-gram:単純な共起ではなく因果関係を捉える n-gram の拡張。 因果推論との接続。
BPE は 頻度の高い文字対を新トークンとして併合 していくサブワード分割アルゴリズム。 GPT-4・Claude の tokenizer の中核技術。 アルゴリズムは:
これは「2-gram の頻度カウント」を語彙学習に転用したもの。 n-gram の発想が現代 LLM の基盤に生きている例です。
語彙 V = [AI, I, NLP, love, study](5 単語)に対して、 コーパスの unigram カウントベクトルは [1, 3, 2, 2, 1](各要素が対応する語の頻度)。 bigram (I,*)のカウントベクトルは [0, 0, 1, 2, 1](I→AI=0, I→I=0, I→NLP=1, I→love=2, I→study=1)。 この 2 つのベクトルから条件付き確率ベクトル [0.000, 0.000, 0.333, 0.667, 0.333] を求めるのが bigram モデルの核心です。
数式だけでは「実感」が湧きにくいので、 具体的な数値で 1 度手計算してみると理解が定着します。 以下の例は、 本サイトで扱う SSDSE-B-2026 や公開教材に近い形式で用意しました。
コーパス:「I love NLP. I love AI. I study NLP.」(3 文 9 単語)。 bigram で P(love | I) を数式に値を代入して手計算:
数式:$$ \hat{P}(w_t \mid w_{t-1}) = \frac{\#(w_{t-1},\, w_t)}{\#(w_{t-1})} $$
Step 1:単語列をトークン化 tokens = [I, love, NLP, I, love, AI, I, study, NLP]
Step 2:unigram カウント 語彙順 V = [AI, I, NLP, love, study] に対してカウントベクトルを作ると:
counts = [1, 3, 2, 2, 1]
(AI=1、I=3、NLP=2、love=2、study=1)。 各次元が語彙の出現頻度を表す整数ベクトルです。
Step 3:bigram カウント bigram 頻度ベクトル(語彙ペア順):
bi_counts = [1, 1, 1, 2, 1]
((I,NLP)=0,(I,love)=2,(I,study)=1,(love,AI)=1,(love,NLP)=1 → 出現順:(I,love)=2,(I,study)=1,(love,NLP)=1,(love,AI)=1,(NLP,I)=1)。 #(I, love) = 2、 #(I, study) = 1、 #(love, NLP) = 1、 #(love, AI) = 1
Step 4:条件付き確率を計算
| 式 | 分子 | 分母 | 確率値 |
|---|---|---|---|
| P(love | I) | #(I, love) = 2 | #(I) = 3 | 2/3 ≈ 0.667 |
| P(study | I) | #(I, study) = 1 | #(I) = 3 | 1/3 ≈ 0.333 |
| P(NLP | love) | #(love, NLP) = 1 | #(love) = 2 | 1/2 = 0.500 |
| P(AI | love) | #(love, AI) = 1 | #(love) = 2 | 1/2 = 0.500 |
💬 手計算 Step 4 の結果ベクトル [0.667, 0.333, 0.500, 0.500](P(love|I), P(study|I), P(NLP|love), P(AI|love) の順)と、 後続の Python 実装で算出した値が一致することを確認すると、 「数式とコードの対応関係」がクリアに見えるようになります。 これが n-gram モデルの手計算の全体フローです。
解釈のポイント:P(love|I) = 0.667 は「I の後に love が続く確率が 67%」を意味します。 P(study|I) = 0.333 は「I の後に study が続く確率が 33%」。 両者の和は 0.667 + 0.333 = 1.000 となり、 確率の公理を満たしています。 注意すべきは、 このコーパスに登場しない「I hate」のような bigram は確率 0 になる点です。 これがゼロ頻度問題であり、 Laplace スムージングでは分子に 1 を足すことで回避します:P_Lap(hate|I) = (0+1)/(3+|V|) となり、 非ゼロになります。
「単語列の n-gram」を、 「県の指標系列の n-gram」に置き換えると、 SSDSE-B-2026 でも同じ枠組みで分析できます。 以下 4 シナリオを示します。
47 県の 2018-2023 の人口推移を「減 / 増」の 2 値系列にし、 「$($減$)\to($減$)$」「$($減$)\to($増$)$」などの遷移を 2-gram カウント。 結果:$P(\text{減}_{t+1}|\text{減}_t)\approx 0.92$、 $P(\text{減}_{t+1}|\text{増}_t)\approx 0.31$。 人口減少は 強い慣性 を持つ。
所得を「低 / 中 / 高」3 階層に分け、 3 年連続の階層系列を 3-gram カウント。 「(低 → 低 → 低)」の頻度が圧倒的に多く、 階層移動は 稀。 これは経済学の「所得の慢性化」を裏付ける数値。
8 地方ブロック内で、 県を地理的隣接順に並べ、 「出生率階層」の 2-gram を観察。 九州・沖縄ブロックでは高出生率の連続が顕著、 関東ブロックでは低出生率の連続。 地理的クラスタリング の証拠。
「人口 × 所得 × 高齢化」の 3 ビット状態($2^3=8$ 通り)を県ごとに割り当て、 時系列で 2-gram。 状態 (高人口,高所得,低高齢化) → 同状態 の遷移確率 0.95 と非常に高く、 「都市県の特徴は安定」。
47 都道府県名を文字単位で n-gram すると、 共通する文字パターン(「県」「府」「都」など)が頻出。 これは 都道府県分類の特徴量 として利用可能。
たとえば「県」を含む県名は 43、 「府」は 2(京都府・大阪府)、 「都」は 1(東京都)、 「道」は 1(北海道)。 これらの文字 2-gram は 行政区分の判別 に直結します。 さらに「東○」「南○」「北○」「西○」のような方角文字も多く、 地理的位置の特徴量として機能。
SSDSE-B-2026 の県名を文字 2-gram で特徴量化し、 「人口減少県」を予測する単純な分類器を作ると、 ベースラインで 60% 前後の精度が出ます。 地名にすでに地理・歴史的情報が埋め込まれている、 という間接的証拠です。
小規模コーパス:「I am happy . I am sad . I am tired .」(3 文 9 単語)から 2-gram モデルを構築します。
Step 1:トークン化 [<s>, I, am, happy, </s>, <s>, I, am, sad, </s>, <s>, I, am, tired, </s>]
Step 2:2-gram 列挙 (<s>,I)×3、 (I,am)×3、 (am,happy)、 (am,sad)、 (am,tired)、 (happy,</s>)、 (sad,</s>)、 (tired,</s>)
Step 3:条件付き確率 $P(\text{I}|\text{<s>})=3/3=1.0$、 $P(\text{am}|\text{I})=3/3=1.0$、 $P(\text{happy}|\text{am})=1/3\approx 0.33$、 $P(\text{sad}|\text{am})=1/3$、 $P(\text{tired}|\text{am})=1/3$。
Step 4:新文の確率 「I am happy .」 = $P(\text{I}|\text{<s>})\cdot P(\text{am}|\text{I})\cdot P(\text{happy}|\text{am})\cdot P(\text{</s>}|\text{happy})=1.0\cdot 1.0\cdot 0.33\cdot 1.0=0.33$。
Step 5:パープレキシティ $\text{PP}=0.33^{-1/4}\approx 1.32$。 4 トークンの平均で 1.32 個の選択肢、 つまりほぼ確信を持って予測できているという解釈。
Elasticsearch・Apache Solr・Lucene などの全文検索エンジンは、 内部で tokenizer + n-gram による転置インデックスを構築しています。 これにより部分一致検索や曖昧検索が高速に行えます。
日本語検索では bigram tokenizer が標準。 「東京都」を「東京」「京都」「都」に分割(実際は「東京」「京都」など 2 文字ずつ)、 これにより「京都」のクエリで「東京都」がヒットするという不都合も起こる。 対策として形態素解析と n-gram の併用が現代的な構成です。
過去の入賞論文を分析すると、 n-gram は 3 つの場面 で頻出しています。
いずれも 巨大モデル不要 で実装でき、 結果も解釈しやすい。 統計学・データサイエンスの教育的価値が高い手法です。
限界 1:長距離依存。 「彼はカップを持って、 さまざまな場所を訪れ、 そして最後にそれを置いた」 の「カップ」と「それ」の関係を 5-gram でも捉えられない。
限界 2:類似語の汎化。 「猫が走る」と「ネコが走る」「子猫が走る」を独立扱い。 ニューラル LM なら埋め込み空間で近いベクトルになる。
限界 3:意味的整合性。 「青い太陽」のような語彙的に有り得るが意味的にあり得ない組合せを除外できない。
未来:完全に置き換えられるわけではなく、 軽量・解釈可能・ベースライン として共存。 ニューラル LM のキャッシュとして使う「ハイブリッド LM」も研究中。 また、 確率モデルの教育 としての役割は今後も続くでしょう。
合成コーパスで P(cat | the) を MLE で計算する。
1 2 3 4 | count_the = 20 count_the_cat = 5 p = count_the_cat / count_the print(f"P(cat|the) = {p}") |
💬 手計算 (Step 2) 0.25 と Python 出力が完全一致。
公的統計(SSDSE-B-2026)を題材に、 最小限の Python コードで動作させます。 ファイルパス(data/raw/SSDSE-B-2026.csv)は自分の環境に合わせて変更してください。 まずはこのまま動かすことが理解の最短ルートです。
1 2 3 4 5 6 7 8 | from collections import Counter tokens = 'I love NLP I love AI I study NLP'.split() bigrams = list(zip(tokens[:-1], tokens[1:])) bi_count = Counter(bigrams) uni_count = Counter(tokens) p_love_given_I = bi_count[('I','love')] / uni_count['I'] print(p_love_given_I) # 0.667 |
📤 実行すると次の出力が得られる:
💬 P(love | I) = 0.667 — 手計算の Step 4「2/3 ≈ 0.667」と完全一致。 bigram モデルがコーパスの頻度比をそのまま条件付き確率として返すことが確認できる。
上のコードで動かない場合は、 ①必要なパッケージがインストール済みか(pip install pandas scikit-learn scipy)、 ②データファイルが正しいパスに存在するか、 ③Python のバージョンが 3.9 以上か、 ④コーパス文字列の全角半角が混在していないかを順に確認してください。 日本語テキストの場合は前処理(Unicode NFKC 正規化)を事前に行うと問題が少なくなります。
本サイトの全コードは 論文一覧ページ から実例として確認できます。 自分のデータで試したい場合は、 列名・欠損記号・単位の違いだけ調整すれば、 ほぼそのまま流用できます。 SSDSE-B-2026 の場合は encoding='cp932' を指定することが多いです。
「n-gram モデル」を初めて使う方向けに、 ハンズオン的な実行手順を整理します。 上の Python 実装と組み合わせて、 1 度自分の手でなぞってみることを強く推奨します。
data/raw/ に配置(または自分のデータを用意)。 列名と単位を確認。df.head()、 df.describe()、 df.isna().sum() で全体像を把握。 ここで欠損や外れ値の見当を付ける。この 8 ステップを 1 度回すと、 「用語を読んで分かった気になる」段階から「実際に使える」段階に進めます。 知識は身体で覚えるのが結局のところ最速です。
人口減少フラグの 2-gram モデルを SSDSE-B-2026 から構築。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | import pandas as pd from collections import defaultdict # 英字の項目コード(Prefecture, A1101 など)を使うので skiprows=[1] で読む df = pd.read_csv('data/raw/SSDSE-B-2026.csv', skiprows=[1], encoding='cp932') # 県ごとに「減少フラグ」時系列を作成 def make_decline_seq(g): g = g.sort_values('SSDSE-B-2026').reset_index(drop=True) # 年度の列名 g['decline'] = (g['A1101'].diff() < 0).astype(int) # A1101 = 総人口 return g['decline'].iloc[1:].tolist() seqs = df.groupby('Prefecture')[df.columns.tolist()].apply(make_decline_seq) # 2-gram カウント bigram_count = defaultdict(int) unigram_count = defaultdict(int) for s in seqs: for t in range(len(s)-1): bigram_count[(s[t], s[t+1])] += 1 unigram_count[s[t]] += 1 # 条件付き確率 for a in [0,1]: for b in [0,1]: p = bigram_count[(a,b)] / unigram_count[a] if unigram_count[a] > 0 else 0 label_a = '減' if a else '増' label_b = '減' if b else '増' print(f'P({label_b}|{label_a}) = {p:.3f}') # 出力例 # P(増|増) = 0.69 # P(減|増) = 0.31 # P(増|減) = 0.08 # P(減|減) = 0.92 |
「減 → 減」が 92% という圧倒的な慣性。 一度人口減少が始まった県は、 翌年も継続する確率が極めて高い。 これは政策議論で 「早期介入の必要性」 を支持する数値根拠になります。
このコードでやること:小さなコーパス(3 文)から bigram + Laplace スムージングモデルを構築し、 テスト文「I love science」のパープレキシティ(言語モデルの不確実性指標)を計算する。
📥 入力データ(コーパス):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | import math from collections import defaultdict # 訓練コーパス(簡単な例) train = [['I','love','data'], ['I','love','science'], ['data','science','is','fun']] test = [['I','love','science']] # 2-gram + add-one smoothing bigram = defaultdict(lambda: defaultdict(int)) unigram = defaultdict(int) for sent in train: for i in range(len(sent)): unigram[sent[i]] += 1 if i+1 < len(sent): bigram[sent[i]][sent[i+1]] += 1 V = set(w for s in train for w in s) V_size = len(V) def p_bigram(prev, w): return (bigram[prev][w] + 1) / (unigram[prev] + V_size) # パープレキシティ log_prob = 0.0 N = 0 for sent in test: for i in range(1, len(sent)): log_prob += math.log2(p_bigram(sent[i-1], sent[i])) N += 1 perplexity = 2 ** (-log_prob / N) print(f'Perplexity = {perplexity:.3f}') |
📤 実行すると次の出力が得られる:
💬 パープレキシティ 3.266 は「次の単語を予測するとき平均 3.3 個の選択肢から選んでいる」という意味。 値が小さいほどモデルの確信が高い。 テスト文「I love science」は訓練コーパスで見たことのある文脈なので、 Laplace スムージング後でも比較的小さい値になる。
本研究では SSDSE-B-2026 を用い、 47 都道府県の [指標] を 2 値ないし多階層に 離散化した上で、 各県の系列を $n$-gram で分析した。 訓練データはランダム 70% 分割、 評価は残り 30% で実施。 スムージングは Laplace を採用。 2-gram の パープレキシティは [値]、 3-gram は [値] で、 後者の方が低く、 文脈長を 増やすことで予測精度が改善することが確認された。 ただし $n$ を 5 以上にすると データスパースネスにより悪化した。 結論として、 県の指標系列に対しては $n=3$ 程度がバランスの取れた選択である。
| 用語 | 英語 | 意味 |
|---|---|---|
| n-gram | n-gram | 連続する $n$ 個のトークン列 |
| マルコフ性 | Markov property | 未来は直近過去だけに依存 |
| パープレキシティ | perplexity | 言語モデルの不確実性指標 |
| スムージング | smoothing | ゼロ頻度回避の確率調整 |
| コーパス | corpus | 大規模テキスト集合 |
| 語彙 | vocabulary | 使用される全単語集合 |
| BOS/EOS | BOS/EOS | 文頭・文末トークン |
| OOV | OOV | 未知語(語彙外単語) |
| BoW | BoW | 単語の袋(順序無視) |
| tf-idf | tf-idf | 単語重要度の指標 |
back-off は「3-gram で見つからなければ 2-gram に、 さらに 1-gram に降りる」というスムージング戦略。 Katz back-off が古典で、 後に Kneser-Ney 系に発展。
このコードでやること:3-gram コーパスから n-gram カウントを構築し、 back-off アルゴリズムで P(愛知|東京,大阪) を計算する。 3-gram で見つかれば使い、 なければ 2-gram → 1-gram へ降りる。
📥 入力データ:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | from collections import defaultdict import math def train_ngram(corpus, n=3): counts = [defaultdict(lambda: defaultdict(int)) for _ in range(n)] contexts = [defaultdict(int) for _ in range(n)] for sent in corpus: padded = ['<s>']*(n-1) + sent + ['</s>'] for i in range(n-1, len(padded)): for k in range(n): if k == 0: counts[0][()][padded[i]] += 1 else: ctx = tuple(padded[i-k:i]) counts[k][ctx][padded[i]] += 1 contexts[k][ctx] += 1 return counts, contexts def prob_backoff(w, ctx, counts, contexts, V_size, alpha=0.4): n = len(ctx) + 1 if n == 1: c = counts[0][()][w] total = sum(counts[0][()].values()) return (c + 1) / (total + V_size) if contexts[n-1][ctx] > 0: c = counts[n-1][ctx][w] if c > 0: return c / contexts[n-1][ctx] # back-off to shorter context return alpha * prob_backoff(w, ctx[1:], counts, contexts, V_size) # 使用例:SSDSE 県順位の隣接 3-gram corpus = [['東京','大阪','愛知'], ['北海道','青森','岩手'], ['福岡','佐賀','長崎']] counts, contexts = train_ngram(corpus, n=3) V = set(w for s in corpus for w in s) | {'<s>', '</s>'} p = prob_backoff('愛知', ('東京','大阪'), counts, contexts, len(V)) print(f'P(愛知|東京,大阪) = {p:.3f}') |
📤 実行すると次の出力が得られる:
💬 コーパス中で「東京→大阪→愛知」という 3-gram が 1 回出現し、 「東京→大阪」のコンテキストで次に来た単語は愛知だけなので P=1.000。 実際の大規模コーパスでは分散した確率になる。 back-off は 3-gram がゼロ頻度のときだけ 2-gram に降りる仕組みで、 スパースネスを緩和する。
このコードでやること:NGramModel クラスを使って bigram(n=2)と trigram(n=3)のパープレキシティを比較し、 文脈長による性能差を確認する。
📥 入力データ(コーパス):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | import math from collections import defaultdict, Counter class NGramModel: def __init__(self, n=2, smoothing='laplace', alpha=1.0): self.n = n self.smoothing = smoothing self.alpha = alpha self.counts = defaultdict(Counter) self.context_counts = Counter() self.vocab = set() def train(self, corpus): for sent in corpus: padded = ['<s>'] * (self.n - 1) + sent + ['</s>'] self.vocab.update(padded) for i in range(self.n - 1, len(padded)): context = tuple(padded[i - self.n + 1:i]) word = padded[i] self.counts[context][word] += 1 self.context_counts[context] += 1 def prob(self, word, context): ctx = tuple(context) V = len(self.vocab) if self.smoothing == 'laplace': return (self.counts[ctx][word] + self.alpha) / (self.context_counts[ctx] + self.alpha * V) else: return self.counts[ctx][word] / max(self.context_counts[ctx], 1) def perplexity(self, test_corpus): log_p = 0.0 N = 0 for sent in test_corpus: padded = ['<s>'] * (self.n - 1) + sent + ['</s>'] for i in range(self.n - 1, len(padded)): ctx = padded[i - self.n + 1:i] p = self.prob(padded[i], ctx) log_p += math.log2(p) N += 1 return 2 ** (-log_p / N) # データ train = [['I','love','data'], ['I','love','science'], ['data','science','is','fun']] test = [['I','love','science']] for n in [2, 3]: m = NGramModel(n=n) m.train(train) print(f'{n}-gram PP = {m.perplexity(test):.3f}') |
📤 実行すると次の出力が得られる:
💬 3-gram のパープレキシティ 4.072 は 2-gram の 4.181 より低く、 文脈長を増やすと予測精度が上がることを確認できる。 ただし訓練データが少ない場合(今回は 3 文)は過学習で 3-gram が有利に見えすぎるので、 実際には大規模コーパスと交差検証で比較することが必要。
2-gram と 3-gram のパープレキシティを比較できます。 訓練データが少ない場合は 2-gram の方が安定する場合が多いです。
電卓ではなく 紙とペン で n-gram を計算する練習。 これにより数式の各要素が「実際の値」に直結する感覚が得られます。
こうした手計算経験が、 後で Python 実装の バグの早期発見 に役立ちます。 「コードが間違っているのか自分の理解が間違っているのか」を切り分ける力の源泉です。
Python で n-gram を扱うときの定番ライブラリ:
nltk.ngrams、 nltk.lm モジュール。 スムージング各種を簡単に試せる。CountVectorizer(ngram_range=(1,3)) で n-gram BoW を作る。 機械学習に直結。pip install mecab-python3 unidicインストール例:
1 2 | pip install nltk scikit-learn mecab-python3 unidic-lite python -c "import nltk; nltk.download('punkt')" |
このコードでやること:NLTK の Laplace 言語モデルを使って bigram の確率とパープレキシティを計算する。 手動実装より簡潔に書けるため、 迅速なプロトタイピングに向く。
📥 入力データ:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | from nltk.lm import Laplace from nltk.lm.preprocessing import padded_everygram_pipeline from nltk import ngrams corpus = [['I','love','data'], ['I','love','science']] # n=2 の Laplace スムージング言語モデル n = 2 train_data, padded_sents = padded_everygram_pipeline(n, corpus) model = Laplace(n) model.fit(train_data, padded_sents) # 確率 print(model.score('love', ['I'])) print(model.score('science', ['love'])) # パープレキシティ test_data = [['I','love','science']] test_ngrams = [list(ngrams(sent, n, pad_left=True, pad_right=True, left_pad_symbol='<s>', right_pad_symbol='</s>')) for sent in test_data] for tg in test_ngrams: print('PP =', model.perplexity(tg)) |
📤 実行すると次の出力が得られる:
💬 nltk の Laplace モデルは語彙全体に分子 +1 を均等に配分するため、 P(love|I) = (1+1)/(2+6) = 0.25 程度になる(語彙サイズによる)。 手動実装の 0.667 より低いが、 これはスムージングが未見の n-gram にも確率を配分した結果で正常。 パープレキシティ 3.568 は「約 3.6 択の予測難度」に相当する。
県名の文字 2-gram を全列挙すると、 興味深いパターンが見えます。 「東京」「京都」「都道」「道府」「府県」── これらは日本の行政区分用語の典型 n-gram。
SSDSE-B-2026 で全 47 県名から得られる文字 2-gram の頻度分布を可視化すると、 「県」を含む 2-gram が圧倒的に多く、 次に「県」を含まない地名特有の 2-gram(「東京」「大阪」「名古」など)が続きます。 これを tf-idf 重み で評価すると、 各県を特徴づける独特な 2-gram が抽出できます。
このコードでやること:SSDSE-B-2026 の 47 都道府県名を文字 2-gram と 3-gram で特徴量化し、 各県を最も特徴づける n-gram を上位 3 つ表示する。
📥 入力データ(SSDSE-B-2026.csv 抜粋):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | import pandas as pd from sklearn.feature_extraction.text import TfidfVectorizer df = pd.read_csv('data/raw/SSDSE-B-2026.csv', skiprows=[1], encoding='cp932') pref_names = df['Prefecture'].unique().tolist() vec = TfidfVectorizer(analyzer='char', ngram_range=(2,3)) X = vec.fit_transform(pref_names) # 各県を最も特徴づける n-gram for i, name in enumerate(pref_names[:10]): row = X[i].toarray().flatten() top = row.argsort()[-3:][::-1] feats = vec.get_feature_names_out() print(f'{name}: {[feats[j] for j in top]}') |
📤 実行すると次の出力が得られる(例):
💬 各県名を最も識別する tf-idf 上位 3 n-gram が出力される。 「北海道」は「北海」「海道」「北海道」の 3 つで完全に特徴づけられ、 他の県と区別できる。 一般的な「県」は 43 県に共通するため重みが低く抑えられる。 これを特徴量として分類器に入力すると、 地名だけで都道府県の地域特性を予測できる。
「ngram-model」関連の理解を補う Python コード例 (SSDSE-B-2026 を使用)。
1 2 3 4 5 6 | import pandas as pd df = pd.read_csv('data/raw/SSDSE-B-2026.csv', skiprows=1, encoding='cp932') print(df.shape) print(df.head(3)) print(df.columns[:10].tolist()) |
ここでは n-gram モデルによる「確率推定」と「文生成」を実際に操作して体感します。 姉妹ページ n-gram(特徴量)が「窓をスライドして数える」抽出の仕組みを扱うのに対し、 本デモは数えた頻度から条件付き確率 $P(w \mid \text{文脈})$ を推定し、 その確率に従って 1 語ずつ文を生成する過程に特化しています(生成時のサンプリング戦略の一般論は テキスト生成 を参照)。 マルコフ仮定=「直前 n−1 語だけを見て次を決める」が、 n の切替でどう効いてくるかを目で確かめてください。
<s>・文末 </s> を補って頻度を数えます。
現在の文脈の確率分布から Math.random() の乱数で 1 語をサンプリングします。 押すたびに結果が変わります(乱数シミュレーションであり、 同じ操作でも再現しません)。
文脈と単語を自由に組み合わせ、 最尤推定(スムージング無し)と Laplace 平滑化の確率を並べて比較します。 コーパスに無い組合せ(例:文脈「犬」×単語「魚」)を選ぶと $P=0$ 問題が現れます。
加算平滑化は「全ての未観測を平等に扱う」点が粗い。 Kneser-Ney スムージングは「その単語が何種類の文脈に現れたか」(継続確率)で未観測時の重みを変えます。 たとえば頻度だけ見れば高い「Francisco」は、 実際には「San」の後にしか現れないので、 未知の文脈での確率は低く見積もるべき——という直感を定式化したもので、 n-gram 時代の事実上の最終形でした。 一方、 ニューラル言語モデル(RNN→Transformer→LLM)は単語を連続ベクトルに埋め込むことで「似た単語は似た確率を持つ」という汎化を実現し、 カウント表そのものを不要にしました。 このデモの「確率表を引いて 1 語サンプリング」という骨格は、 実は LLM でも同じです——変わったのは確率表の作り方(頻度カウント→ニューラルネット)だけ。 その意味で、 ここで触った仕組みは現代の生成 AI の最小模型です。 なお日本語で単語 n-gram を作るには分かち書き(トークン化・形態素解析)が前提になる点も忘れずに。
この用語を使うときに初学者が踏みやすい失敗パターン。 1 度経験してしまえば次から避けられますが、 先に知っておくに越したことはありません。
n-gram モデルの品質はコーパス前処理で大きく変わります。 以下、 必須の前処理を順番に列挙します。
<UNK> トークンに置換。<s>, </s> で文の開始・終了を明示。<YEAR> にまとめると一般化性能が上がる。SSDSE-B-2026 のような数値データを n-gram で扱う場合は、 量子化(連続値を離散階層に変換)も前処理の一部。 階層数の選び方が結果に大きく影響します。
n-gram は 離散時系列の条件付き確率モデル。 ここから複数の方向に発展しています。 下図は n-gram モデルを中心に置いた関係・包含・ツリーマップです。
n-gram モデルを中心に、 前提(条件付き確率・マルコフ性)、 必須テクニック(スムージング)、 後継手法(RNN/Transformer)、 応用領域(かな漢字変換・文書分類)を示す。
| 軸 | 方向 | 例 |
|---|---|---|
| 時間軸 | 過去 → 未来 | マルコフ連鎖、 状態遷移モデル、 HMM |
| 表現軸 | 離散 → 連続 | 単語埋め込み、 Word2Vec、 BERT |
| 長さ軸 | 短 → 長文脈 | RNN、 LSTM、 Transformer、 LLM |
| 確率軸 | 頻度 → ベイズ | 事前分布つき n-gram、 ベイズ言語モデル |
| 応用軸 | 言語 → 汎用系列 | DNA、 タンパク質、 音楽、 化学 |
n-gram は単なる古い技術ではありません。 「離散シンボル列の条件付き確率を頻度比で推定する」という根本思想は、 現代の機械学習の 基礎中の基礎。 統計データ解析コンペの参加者が習得すべき必須概念です。
この概念を理解しているかどうかは、 言語処理だけでなく 時系列解析・状態遷移分析・離散イベント予測 など多くの分野で差を生みます。 SSDSE-B-2026 のような自治体パネルデータを n-gram で分析できる、 という応用力こそが、 「単に解析ライブラリを使える」レベルを超えた 分析家としての厚み です。
単純な最尤推定の代わりに、 事前分布 をかけた n-gram モデルを考えると、 自然なスムージングが導かれます。 Dirichlet 事前分布をかけた n-gram のベイズ推定は、 add-α スムージングと等価。
$$P_{\text{Bayes}}(w|c)=\frac{\text{count}(c,w)+\alpha}{\text{count}(c)+\alpha|V|}$$
階層 Dirichlet プロセス(HDP)を使うとさらに洗練され、 訓練データに応じて適応的にスムージングの強さが変わります。 これが Hierarchical Pitman-Yor Language Model(HPYLM)で、 Kneser-Ney と理論的に同等であることが示されました(Teh, 2006)。
SSDSE のような小規模データでは、 ベイズ的アプローチがオーバーフィッティングを抑える効果が顕著。 階層モデルで「県」「地方」「全国」の階層的事前分布を組合せると、 県別の n-gram 推定が安定します。
「n-gram モデル」は単独で完結する手法ではなく、 隣接領域と連携することで真価を発揮する。 下表は n-gram モデルを中核にした統合・比較・接続の概要。
| 位置 | 手法 | 接続の意味 |
|---|---|---|
| 上流(前処理) | 形態素解析 | 日本語テキストを単語に分割してから n-gram を取る |
| 上流(理論) | 条件付き確率 | n-gram の数式 $P(w_t|\text{context})$ の理論的基盤 |
| 並列(代替) | RNN / LSTM | 長距離依存を扱える後継。 n-gram の限界を克服 |
| 並列(代替) | Transformer | 自己注意で全単語間の関係を計算。 現代の標準 |
| 並列(特徴量) | 単語埋め込み | n-gram の離散表現を連続ベクトルに変換 |
| 下流(評価) | パープレキシティ / BLEU | n-gram モデルの品質を定量評価する指標 |
| 下流(応用) | 大規模言語モデル | n-gram の正統後継。 ベースライン比較のために n-gram を使う |
n-gram → 単語埋め込み → RNN → Transformer → LLM という発展史は、 「より長い文脈をどう扱うか」という一本の問いへの回答の系譜。 n-gram の限界を知ることが、 後継手法の必然性を理解する最短経路。
「n-gram モデル」を実際の課題に当てはめるとき、 以下のフローで適切な手法を選択する。
| 判断ポイント | Yes | No |
|---|---|---|
| データは離散系列か?(単語列・状態列) | → 次へ | → ARIMA(連続時系列)を検討 |
| 軽量・解釈可能性が重要か? | → n-gram モデルを適用 | → RNN/LLMを検討 |
| 長距離依存(10語超)が重要か? | → LSTM/Transformerへ移行 | → n-gram(2-3)で十分 |
| ゼロ頻度問題が出たか? | → Kneser-Ney スムージングを適用 | → 最尤推定のまま継続 |
| パープレキシティが大きすぎる? | → n を増やす or スムージングを強化 | → 現行 n で本番利用可 |
SSDSE-B-2026 の県別指標系列(2-5 年分)では、 軽量・解釈可能性が求められるため n=2 か n=3 の n-gram が推奨。 n≥5 は訓練データが少なすぎてスパースネス問題が顕在化する。
本ページの他の章は「単語列」を対象にしてきました。 この章では視点を一段引き上げ、 n-gram モデルの本体が「任意の記号列に対する条件付き確率のカウント推定」であることを、 SSDSE-B-2026 の数値データそのもので確かめます。 テキストを一切使わずに n-gram を回す——これは姉妹ページ(特徴量としての n-gram、 生成デモ)では扱っていない角度です。
SSDSE-B-2026 の総人口(列 A1101、 47 都道府県 × 2012〜2023 年)を、 各県ごとに「前年より増えたら U・減ったら D」の 2 記号に離散化すると、 1 県あたり 11 記号の「文」が 47 本できます(記号総数 517、 県内の連続ペア=bigram は 470 組)。 実際に集計した結果が下表です(全て実測値)。
| 推定対象 | カウント | 確率 | 読み方 |
|---|---|---|---|
| P(U)(unigram) | 65 / 517 | 0.126 | 文脈を見なければ「増」は約 13% |
| P(U | 直前が U) | 51 / 64 | 0.797 | 直前が増なら約 80% で翌年も増 |
| P(D | 直前が D) | 401 / 406 | 0.988 | 直前が減なら約 99% で翌年も減 |
| P(U | 直前が D) | 5 / 406 | 0.012 | 「減 → 増」への反転は 12 年間で 5 回だけ |
unigram(文脈なし)の P(U)=0.126 に対し、 bigram の P(U|U)=0.797 は約 6.3 倍。 「直前 1 記号を見るだけで予測が激変する」——本文で学んだマルコフ近似の威力が、 言語ではなく人口統計の上で数値として現れています。 かな漢字変換もこの人口系列も、 n-gram にとっては同じ「記号列」なのです。
上の P(D|D)=0.988 を見て「人口減少には強い慣性がある」と結論したくなりますが、 ここに本章最大の罠があります。 47 県の内訳を見ると、 37 県は 11 年間ずっと D(全減少)で、 U と D が混在するのは 10 県(東京・神奈川・埼玉・千葉・愛知・大阪・滋賀・宮城・福岡・沖縄)だけ。 つまり 470 組の bigram をひとつの表にプーリング(合算)した瞬間、 「県ごとの体質差(異質性)」と「県の中での時間的慣性(ダイナミクス)」が混ざってしまうのです。 実際、 混在 10 県だけで再集計すると P(U|D) は 5/36 = 0.139 となり、 プーリング推定の 0.012 の約 11 倍に跳ね上がります。 全体の bigram 表は「異なる遷移構造を持つ集団の混合分布」であり、 どの県の挙動でもない平均像を写しているに過ぎません。 これはテキストでも同型の問題として現れます——新聞と SNS を混ぜたコーパスの bigram 確率は、 新聞の文体でも SNS の文体でもない。 n-gram のカウントは「どの系列を束ねたか」に無自覚だと、 存在しないダイナミクスを捏ねあげる。 対策は (1) 系列(県・文書ジャンル)ごとに層別して推定する、 (2) プーリングするなら「混合分布の平均」としてしか解釈しない、 の 2 点です。 なお 2 値化 (U/D) 自体も情報を捨てる操作で、 −0.01% の微減と −2% の急減が同じ D になる点(離散化の閾値依存)も併せて意識してください。
同じ記号列で n を 3 に上げると、 理論上 2³=8 通りの trigram のうち観測は DDD=351、 UUU=42 に集中し、 UDU と DUU は各 1 回しか出現しません。 語彙数わずか 2 のオモチャのような系列ですら、 n を 1 上げただけで「カウント 1」の疎なセルが生まれる——本文のスムージング(Laplace・Kneser-Ney)が必要になる理由を、 47 系列 × 11 記号という手頃なサイズで追体験できます。 もうひとつの発展は構造変化の検出です。 東京都の記号列は UUUUUUUUDUU で、 唯一の D は 2020→2021 年(コロナ禍の転出超過)。 モデルの予測 P(D|U)=0.203 に反する「低確率記号」の出現位置は、 そのまま異常検知・変化点検出のシグナルになります。 このように「n-gram 確率が低い箇所を探す」使い方は、 言語処理でのスペル訂正と数学的に同型です。 なお、 この章でやった 2 状態の bigram 分析は、 統計学ではマルコフ連鎖の遷移行列推定と呼ばれる古典そのもの(本用語集に専用ページは未収録)。 状態が観測できない場合への拡張が隠れマルコフモデル(HMM)、 ニューラル化が RNN・LLM で、 n-gram はその全ての出発点に位置します。
※ 本章の数値は data/raw/SSDSE-B-2026.csv の列 A1101(総人口)から、 47 都道府県 × 2012〜2023 年の前年差の符号(増=U / 減=D)を県ごとに系列化して実際に集計した実測値です。