それでは毛玉諸君、これにて失敬

日々の精進を備忘録的に綴ります。

ビジネスマンがはじめて学ぶベイズ統計学1

ベイズ統計に入門したいのでこれを読み始めました。

ビジネスマンがはじめて学ぶ ベイズ統計学 ―ExcelからRへステップアップ― | 朝野 煕彦, 朝野 煕彦 |本 | 通販 | Amazon

以下は個人的メモ。

第1章 確率分布の早わかり

離散型 連続型
確率変数Xの範囲  \cdots , -2, -1, 0, 1, 2, \cdots  - \infty \lt x \lt \infty, その他の実数区間
関数の呼び方 確率関数 確率密度関数
関数の値の範囲  0 \le P(x) \le 1  0 \le f(x) \le \infty
確率の合計が1であるということの数式表現  \sum_j P(X = x_j) = 1  \int^{\infty}_{- \infty} f(x)dx = 1

第2章 ベイズの定理と再解釈

復習

事象Bが起きたときに事象Aが起きる条件付き確率を P(A|B)と書く。 P(B) > 0と仮定する(事象Bが起こらなければもはや考える必要がなくなるので)。
すると、この条件付き確率は、


P(A|B) = \frac{P(AB)}{P(B)} \qquad (1)

と定義される。事象をBに限定した範囲でAが起きる確率というふうに理解できる。
事象AとBが同時に起きる確率は P(AB) = P(B|A)P(A)なので、先程の式は、


P(A|B) = \frac{P(B|A)P(A)}{P(B)} = \frac{P(B|A)P(A)}{\sum^{n}_{i=1} (B|A_i)P(A_i)}  \qquad (2)

と置き換えられる。


近年のベイズ統計を理解するには、

結果を知って原因を逆推論する

という従来的な解釈から5つほどハードルを超える必要がある。

1. パラメータの推定に関心を持つ

例)ある製造機械からお菓子が作り出されている。お菓子の重量が仕様どおりになっているかに関心がある。

ベイズ統計では、データは何らかの統計モデルに沿って発生すると仮定する。その統計モデルの具体的な中身が確率分布である。
ここで「分析者が決めるべき事」とは、

  • 重量が従う確率分布は何か
  • 仕様どおりか否かを判定する基準をどう決めるか

が主な内容となる。
サンプルデータがあったとき、分析者が知りたい平均は標本平均ではなく「データ発生機構」である製造機械が生産し続けるであろうお菓子の重量の平均にある。
データの背後には確率分布があると仮定しているが、そのパラメータは分からない。そこで、データに基づいてパラメータを推定しなければならない。

2. 連続変数も離散的に扱う

(2)式を連続的な確率変数 Xに置き換えた場合のベイズの定理は、


f(x|B) = \frac{f(B|x)P(x)}{\int f(B|x)f(x) dx} \qquad (3)

となる。(3)式左辺の f(x|B)を事後分布、右辺の f(x)を事前分布という。
ここで、(2)式が「確率」と「確率」の関係式だったのに対し、(3)式では「関数」と「関数」の関係式に変わったという点に注意(確かに...!)。
実務では、たいてい(3)式右辺の分母の積分が出来ないことが多い。
6章では、確率変数Xの乱数を大量に発生させれば、その度数分布の形状が確率密度関数に近似してくるという性質を使う。

3. パラメータを確率変数として扱う

(3)式の確率変数 xをパラメータ \theta、Bと書かれていた事象をデータDにそれぞれ置き換えると、


f(\theta|D) = \frac{f(D|\theta)P(\theta)}{\int f(D|\theta)f(\theta) d\theta} \qquad (4)

確率変数は観測が終わったら固定したデータに過ぎないので、Dは定数となる。
(4)式は、測定結果Dを前にして、それを発生させた確率分布のパラメータ \thetaがどうであるかを導く式だと解釈できる。
伝統的な統計学ではパラメータを定数としていたが、しょせん人間には真の値など知りようがない、だったらパラメータ \theta を何ら下の定義域の中で変動する確率変数とみなそうぜ!ということ。

4. 正規化定数というアイディア

(4)式の右辺分母、 f(D|\theta)f(\theta) = f(D\theta)はDと確率変数 \thetaの同時分布を意味するが、それを \thetaについて積分しているので \thetaは消去される。
 f(D)はDが定数なので1つの数値になる。よって(4)式の右辺分母はスカラーである。
(4)式の右辺分母は事後分布を \thetaに関して積分すると1になるように調整する定数なので「正規化定数」と呼ばれる。
(4)式の右辺の定数部分をkと置き換え、比例関係で示すと、


f(\theta|D) = kf(D|\theta)f(\theta) \propto f(D|\theta)f(\theta) \qquad (5)

と表すことができる。

5. 尤度関数によってパラメータを更新する

(5)式はデータDを収集する前に持っていたパラメータに関する情報 f(\theta)と、データDを集めることによってパラメータについての知識がより明確になり、事後分布 f(\theta|D)に更新される、というストーリーを表す。これをベイズ更新という。
また、 f(D|\theta)を尤度関数と呼ぶ。

第3章 ナイーブベイズで即断即決

ナイーブベイズベイズ統計の中では特殊なモデルだが、ベイズ的な学習機能が理解しやすいモデルです。実社会でベイズの定理がどう活用されているかを見てみましょう。

スパムメールのフィルタリング

2群に分類するという目的では、古くから線形判別関数があり、確率的な予測にはロジスティック回帰分析という手法が用いられてきました。
それらに対しナイーブベイズ

  • データを処理しつつ判定が逐次的に行われる
  • システムの運用を通じて予測精度が向上する

というダイナミズムに特徴があります。

本文ではエクセルで計算する例が紹介されていましたが、pythonで実装してみました。

from typing import List, Dict

import numpy as np

# 第3章 ナイーブベイズでメールのスパムチェック
# スパムメールの事前確率
# ユーザーに多くのスパムメールが送信されないように高めに設定する
spam_p = 0.9

# 単語の出現確率(index 0: スパム, index 1: 通常)
# 過去のメールを集計して求める
word_p = {
    "出会い": [0.8, 0.3],
    "無料": [0.7, 0.2],
    "お知らせ": [0.4, 0.6],
    "ベイズ": [0.001, 0.1],
}
word_lst = np.array(list(word_p.keys()))

# しきい値、事後確率がこの値を超えたらスパムと判定
threshold_p = 0.98

# メール本文の単語数
mail_word_n = 6

# 試行回数
m = 3 

def calc_bayes_p(word: str, d: Dict[str, List[float]], p: float):
    """
    Args:
        word: メール本文に含まれている単語
        d: 各単語の条件付き確率
        p: 事前確率
    """
    return (d[word][0] * p) / (d[word][0] * p + d[word][1] * (1 - p))

for i in range(m):
    print("=" * 64)
    # word_lstからランダムに単語を抽出したものを判定用のメールと想定
    print(f"now: {i + 1}")
    mail_l = np.random.choice(word_lst, mail_word_n, replace=True)
    print("Received mail")
    print(mail_l)
    p = spam_p
    spam_f = 0
    for word in mail_l:
        p = calc_bayes_p(word, word_p, p) 
        print(f"now p: {p}")
        if p >= threshold_p:
            spam_f = 1
            break
    if spam_f:
        print("Spam mail!!!!")
    else:
        print("No spam mail")

これを実行してみると、、、

================================================================
now: 1
Received mail
['無料' '出会い' 'ベイズ' '無料' '出会い' 'ベイズ']
now p: 0.9692307692307692
now p: 0.988235294117647
Spam mail!!!!
================================================================
now: 2
Received mail
['無料' 'ベイズ' '無料' 'お知らせ' '出会い' '無料']
now p: 0.9692307692307692
now p: 0.23954372623574138
now p: 0.5243757431629013
now p: 0.4236311239193083
now p: 0.6621621621621621
now p: 0.8727735368956743
No spam mail
================================================================
now: 3
Received mail
['ベイズ' '出会い' 'お知らせ' 'ベイズ' '出会い' 'お知らせ']
now p: 0.08256880733944956
now p: 0.19354838709677427
now p: 0.13793103448275867
now p: 0.0015974440894568696
now p: 0.004248539564524697
now p: 0.0028363765289842243
No spam mail

という出力が得られます。
ここではメールに含まれる単語を逐次的に処理していき、しきい値よりも高い確率になった時点でスパムメールとして判定しています。
逐次的に処理する理由は、

  • メールはユーザーが自発的に書くので、ワード数の上限も具体的な内容もシステム側から分からない
  • すばやく振り分けが可能

というわけで、実に簡単にナイーブベイズの計算を行うことができました。
ナイーブベイズは単語と単語の独立性を仮定しているので、このような単純な計算が可能となります。