目次
スパムフィルターがなぜ学習しながら精度を上げるのか。医療検査の陽性結果がなぜ「必ずしも病気とは言えない」のか。これらの問いに答えるのがベイズ統計だ。ベイズ統計は「既知の情報(事前知識)に新しい証拠を組み合わせて、信念を更新する」という推論の枠組みを提供する。
頻度主義 vs ベイズ統計
まず、統計の2つの流派の違いを押さえておこう。
頻度主義(Frequentist): 確率は「長期的な頻度」として定義される。コインを無限回投げたときに表が出る割合が0.5なら、表の確率は0.5だ。データを見て「この仮説が正しいか」を検定する。
ベイズ統計(Bayesian): 確率は「信念の度合い」として定義される。「明日雨が降る確率は70%」という文が意味を持つ。事前に持つ信念(事前確率)を新しいデータで更新して事後確率を得る。
どちらが正しいというわけではなく、問題の性質によって使い分ける。AI・機械学習ではベイズ的な考え方が多く使われる。
ベイズの定理
ベイズ統計の核心はベイズの定理だ。
P(H|E) = P(E|H) × P(H) / P(E)
| 記号 | 名称 | 意味 |
|---|---|---|
P(H) | 事前確率(Prior) | データを見る前の「H が正しい」信念 |
| `P(E | H)` | 尤度(Likelihood) |
| `P(H | E)` | 事後確率(Posterior) |
P(E) | 周辺尤度(Evidence) | 証拠 E の周辺確率(正規化定数) |
言葉で表すと:事後確率 ∝ 尤度 × 事前確率
新しい証拠を見るたびに、事後確率が新しい事前確率になる。これが「ベイズ更新」だ。
直感的な例:医療検査
医療検査を例にベイズの定理の威力を見てみよう。
設定:
- ある病気の有病率: 1%(事前確率)
- 検査の感度(病気の人が陽性になる確率): 99%
- 検査の特異度(健康な人が陰性になる確率): 95%
検査が陽性だったとき、本当に病気である確率は?
def bayes_medical(prevalence, sensitivity, specificity):
"""
prevalence: 有病率 P(病気)
sensitivity: 感度 P(陽性|病気)
specificity: 特異度 P(陰性|健康)
"""
p_disease = prevalence
p_healthy = 1 - prevalence
p_positive_given_disease = sensitivity
p_positive_given_healthy = 1 - specificity # 偽陽性率
# ベイズの定理
# P(病気|陽性) = P(陽性|病気) × P(病気) / P(陽性)
# P(陽性) = P(陽性|病気)×P(病気) + P(陽性|健康)×P(健康)
p_positive = (
p_positive_given_disease * p_disease +
p_positive_given_healthy * p_healthy
)
p_disease_given_positive = (
p_positive_given_disease * p_disease / p_positive
)
return p_disease_given_positive
result = bayes_medical(
prevalence=0.01, # 有病率 1%
sensitivity=0.99, # 感度 99%
specificity=0.95 # 特異度 95%
)
print(f"陽性的中率: {result:.1%}") # 約 16.7%
結果は約16.7%——99%精度の検査で陽性でも、本当に病気の確率は17%に過ぎない。
これは直感に反するが数学的には正しい。有病率が低い(事前確率が低い)ため、偽陽性の絶対数が多くなるからだ。この「検査の精度に対する過信」を「基準率の無視(Base Rate Neglect)」と呼ぶ。
スパムフィルター——ナイーブベイズ
メールのスパムフィルターはナイーブベイズ分類器の代表例だ。
import numpy as np
from collections import defaultdict
class NaiveBayesSpamFilter:
def __init__(self):
self.word_spam_count = defaultdict(int)
self.word_ham_count = defaultdict(int)
self.spam_total = 0
self.ham_total = 0
self.vocab = set()
def train(self, emails):
"""emails: [(テキスト, ラベル)] のリスト"""
for text, label in emails:
words = text.lower().split()
self.vocab.update(words)
if label == 'spam':
self.spam_total += 1
for word in words:
self.word_spam_count[word] += 1
else:
self.ham_total += 1
for word in words:
self.word_ham_count[word] += 1
def predict(self, text):
total = self.spam_total + self.ham_total
# 事前確率 P(スパム), P(正常)
log_prob_spam = np.log(self.spam_total / total)
log_prob_ham = np.log(self.ham_total / total)
words = text.lower().split()
for word in words:
if word not in self.vocab:
continue
# ラプラス平滑化(未知語を0にしない)
vocab_size = len(self.vocab)
p_word_spam = (self.word_spam_count[word] + 1) / (
sum(self.word_spam_count.values()) + vocab_size
)
p_word_ham = (self.word_ham_count[word] + 1) / (
sum(self.word_ham_count.values()) + vocab_size
)
log_prob_spam += np.log(p_word_spam)
log_prob_ham += np.log(p_word_ham)
return 'spam' if log_prob_spam > log_prob_ham else 'ham'
# 使用例
filter = NaiveBayesSpamFilter()
training_data = [
("無料 お金 今すぐ クリック", "spam"),
("会議 明日 資料 送ります", "ham"),
("当選 お祝い 無料 プレゼント", "spam"),
("プロジェクト 進捗 報告 よろしく", "ham"),
]
filter.train(training_data)
print(filter.predict("無料 クリック 当選")) # spam
print(filter.predict("明日 会議 資料")) # ham
「ナイーブ」と呼ばれる理由は、各単語が独立と仮定しているから(実際は相関がある)。それでも実用上は十分な精度を発揮する。
ベイズ更新——逐次的な信念の更新
ベイズ統計の強みは、データが増えるにつれて信念を更新できることだ。
コインが公正かどうかを判断する例を考えよう。
import numpy as np
import matplotlib.pyplot as plt
def beta_posterior(heads, tails, alpha_prior=1, beta_prior=1):
"""
ベータ分布を事前分布として使ったベイズ推定
alpha_prior, beta_prior: 事前分布のパラメータ(均一分布=1,1)
"""
alpha_posterior = alpha_prior + heads
beta_posterior = beta_prior + tails
return alpha_posterior, beta_posterior
def posterior_mean(alpha, beta):
return alpha / (alpha + beta)
# 初期状態(均一事前分布: どんな確率値も等しく可能)
alpha, beta = 1, 1
print(f"初期推定: {posterior_mean(alpha, beta):.2f}")
# コインを投げるたびに更新
observations = [1, 0, 1, 1, 0, 1, 1, 0, 1, 1] # 1=表, 0=裏
for i, obs in enumerate(observations):
if obs == 1:
alpha, beta = beta_posterior(1, 0, alpha, beta)
else:
alpha, beta = beta_posterior(0, 1, alpha, beta)
print(f"投げ {i+1}回後: 表率推定 = {posterior_mean(alpha, beta):.2f} "
f"(α={alpha}, β={beta})")
初期推定: 0.50
投げ 1回後: 表率推定 = 0.67 (α=2, β=1)
投げ 2回後: 表率推定 = 0.60 (α=2, β=2)
投げ 3回後: 表率推定 = 0.67 (α=3, β=2)
...
投げ10回後: 表率推定 = 0.69 (α=8, β=4)
データが増えるほど事後確率の「幅」が狭まり、推定が確実になっていく。これがベイズ更新の直感だ。
ベイズ推論とAI
不確かさの定量化
ベイズ的ニューラルネットワーク(BNN)では、重みを点推定ではなく確率分布として扱う。これにより予測の「自信度」を定量化できる。
# 概念コード(実際は PyMC や TensorFlow Probability を使う)
# 通常のNN: 重みは固定値
# weight = 0.7
# ベイズNN: 重みは分布
# weight ~ Normal(mean=0.7, std=0.1)
# 予測時にサンプリングして不確かさを推定
# 実用的な近似: モンテカルロドロップアウト
import torch
import torch.nn as nn
class BayesianApproxNet(nn.Module):
def __init__(self, input_dim, hidden_dim, output_dim, dropout_p=0.1):
super().__init__()
self.fc1 = nn.Linear(input_dim, hidden_dim)
self.fc2 = nn.Linear(hidden_dim, output_dim)
self.dropout = nn.Dropout(p=dropout_p)
def forward(self, x):
x = torch.relu(self.fc1(x))
x = self.dropout(x) # 推論時もドロップアウトをONにする
return self.fc2(x)
def predict_with_uncertainty(self, x, n_samples=50):
"""複数回予測して分散を不確かさとして使う"""
self.train() # ドロップアウトを有効にする
predictions = torch.stack([self(x) for _ in range(n_samples)])
mean = predictions.mean(0)
std = predictions.std(0)
return mean, std
ハイパーパラメータ最適化
ベイズ最適化(Bayesian Optimization)はハイパーパラメータ探索に使われる。過去の評価結果から「次にどこを試すべきか」を確率モデルで推定する。
# scikit-optimize ライブラリを使ったベイズ最適化の例
from skopt import gp_minimize
from skopt.space import Real, Integer
def objective(params):
learning_rate, n_layers = params
# ここで実際にモデルを学習して検証スコアを返す
# (サンプルなので代替の計算を使う)
return -(learning_rate * 10 - (learning_rate - 0.01)**2 * 1000)
result = gp_minimize(
objective,
dimensions=[
Real(1e-4, 1e-1, prior='log-uniform'), # 学習率
Integer(1, 5) # 層数
],
n_calls=20, # 試行回数
random_state=42
)
print(f"最適パラメータ: lr={result.x[0]:.4f}, layers={result.x[1]}")
事前分布の選び方
事前分布の選択がベイズ推論の結果に影響する。
| 分布 | 用途 | 特性 |
|---|---|---|
| 一様分布 | 事前知識がない場合 | すべての値が等確率 |
| ベータ分布 | 確率値(0〜1)の推定 | スパムフィルターの単語確率 |
| 正規分布 | 実数値パラメータ | 重みの正則化(L2=ガウス事前分布) |
| ラプラス分布 | スパースな解 | 重みの正則化(L1=ラプラス事前分布) |
正則化とベイズ統計の関係は興味深い。L2正則化は「重みはゼロ付近のガウス分布に従う」という事前分布に相当し、L1正則化は「重みはラプラス分布に従う(ゼロが多い)」という事前分布に相当する。
頻度主義の限界とベイズの強み
頻度主義が苦手なケース:
- データが少ない(過学習しやすい)
- 複数の仮説を比較したい
- 予測の不確かさを表現したい
ベイズの強み:
- 少量データでも事前知識を活かせる
- 不確かさを確率として自然に表現
- 新しいデータが来るたびに逐次更新できる
ベイズのデメリット:
- 計算コストが高い(MCMC サンプリングなど)
- 事前分布の選択が主観的
- 大規模モデルへの適用が難しい
まとめ
ベイズ統計は「不確かさを確率で表現し、証拠が増えるほど信念を更新する」という直感的な推論の枠組みだ。
- ベイズの定理: 事後確率 = 尤度 × 事前確率 / 周辺尤度
- スパムフィルター: ナイーブベイズの実用的な成功例
- 医療検査: 基準率の無視に注意——有病率が低いと陽性的中率も低い
- ベイズ更新: データが増えるほど推定が確実に
- 機械学習への応用: 不確かさの定量化・ベイズ最適化・正則化の統一的理解
「確率は長期的な頻度ではなく、信念の度合い」というベイズ的世界観を身に付けると、AI が「どれくらい自信を持って予測しているか」を問う視点が生まれる。
免責事項 — 掲載情報は執筆時点のものです。料金・機能は変更される場合があります。最新情報は各公式サイトをご確認ください。