機械学習のための数学ゼミ第二回はベイズの定理編!
ということで、第一回の確率分布に続き、第二回はベイズの定理を学びます。
まずは今回の登場人物をご紹介します!
<登場人物>
池澤あやか
タレントときどきエンジニア。数学は苦手だけど、機械学習入門したい。
増井雄一郎
株式会社トレタのえらいエンジニア。本人曰く、プログラムは書けるけど数学をまともに勉強したことがない。
梅崎先生
今回数学を教えてくれる先生。社会人のための数学教室「すうがくぶんか」の講師。
機械学習での大切な概念「ベイズの定理」を学ぶ!
「前回は確率・統計の基礎の基礎、二項分布と多項分布について学びました。今日は『ベイズの定理』を学んでいきます。機械学習においてとても大事になってくる概念なので、しっかり覚えておいてください」
「前回もなかなか息切れものでしたが、頑張ります(涙)」
「では端的に説明しますね(笑)。ズバリ、ベイズの定理とは以下のような公式のことです」
「ヒーーッ!!!!公式!!!(涙)。でもこの記号たち、機械学習勉強してて出てきた気がしますね…」
「P(X)はXが起こる確率、P(Y)はYが起こる確率、P(Y|X)は、Xが起こった上でYが起こる確率という意味です」
「Xをコーヒーを買った人、Yをクッキーを買った人で例えると、P(Y|X)はコーヒーを買った人の中でクッキーを買った人の割合ということになりますね」
「上の公式は、こういう式にも変形できます」
「P(Y|X)P(X)というのは、P(X)は全体のなかでXが起こる確率、P(Y|X)は、Xが起こった上でYが起こる確率で、掛け合わせるとP(X∩Y)つまり、XかつYが起こる確率ということになります。下のベン図でいうとこの緑色のZの部分のことですね。逆もまたしかりで、P(X|Y)P(Y)もこの緑色の部分のことになります。意外と簡単じゃないですか?」
「Xをコーヒー、Yをクッキーで例えると、コーヒーを買ってクッキーを買った人の確率と、クッキーを買ってコーヒーを買った人の確率が同じってことですね」
「そうなんです!ベイズの定理の問題を解くときは、何がXで何がYにあたるかをうまく設定することが重要です」
「なるほど」
「では、ベイズの定理を理解するために、ここでは例えば次のような問題を考えてみましょう。まずは単純に計算で求めてみてください」
【問題】あるお店で、コーヒーを買った人の中でクッキーを買った人の割合が50%でした。来客者全体でみてみると、コーヒーを買った人の割合が70%、クッキーを買った人の割合が60%です。では、クッキーを買った人の中でコーヒーを買った人の割合はいくらになるでしょうか。
「先程のように、ベン図にまとめてみると分かりやすいかもしれませんね」
「こういうベン図になりそうなので、ベイズの定理にもとづいて以下のように計算すると…」
「ということは約58.3%ってことですかね?」
「正解です!」
ベイズの定理をプログラムにしてみよう!
「それでは、先程の問題をプログラムに落とし込んでみましょう」
「はい!!公式を書き下しただけですが、このようなプログラムになるかと思います」
x = 0.7 # コーヒーを買った人の割合
y = 0.6 # クッキーを買った人の割合
yx = 0.5 # コーヒーを買った中でクッキーを買った人の割合
def bayes(x, y, yx):
return yx * x / y
print bayes(x, y, yx)
「正解です。それでは同じプログラムを利用して以下の問題を解いてみてください」
【問題】箱Aには白玉4個と赤玉1個、箱Bには白玉1個と赤玉9個が入っているとします。1/2ずつの確率でどちらかの箱を選んで中身をひとつとった時、白玉が出てきました。この時、どちらの箱からとった可能性が高いでしょうか。
「複雑に見えるかもしれませんが、こういった問題ではまず、ベイズの定理の公式でいうと何がXで何がYとなるかを考えてみます。」
「ここでは、Xとしては『箱Aを選んだ』か『箱Bを選んだ』かの二通りを考えて、Yとしては『赤いボールをとった』か『白いボールをとった』かの二通りを考えるってことですか?」

「そうですね。さっきはXとしてコーヒーを買ったか買わないかの二通り、クッキーを買ったか買わないかの二通りです。そして、コーヒーを買ったうちでクッキーを買った人の割合をP(X|Y)で表しました。今回も『箱Aを選んだ上で赤いボールをとった確率』とか『赤いボールを取った時に箱Aを選んでいた確率』を同じようにP(X|Y)とかP(Y|X)と表せることに注意してください」
「イメージしずらいのでわかりやすくベン図を書こうと思います。箱Aと赤、箱Aと白、箱Bと赤、箱Bと白で、全部で4つベン図が書けますね。この交わっている部分の確率がどれが一番高いかを求めるってことですか?」
「はい。今回はとりだしたボールが白の場合のみ比較検討すればいいので、白いボールをとった二通りについて考えればよいというわけです」
「では先程のプログラムに当てはめると、箱Aから白が出た場合と箱Bから白が場合の確率を比べればよいということですね。実行してみます!」
x = 0.5 # 箱Aが選ばれる確率
y = (4.0 / 5) * 0.5 + (1.0 / 10) * 0.5 # 白が出る確率
yx = 4.0 / 5 # 箱Aの中で白が出る確率
def bayes(x, y, yx):
return yx * x / y
print bayes(x, y, yx)
>> 0.888888888889
x = 0.5 # 箱Aが選ばれる確率
y = (4.0 / 5) * 0.5 + (1.0 / 10) * 0.5 # 白が出る確率
yx = 1.0 / 10 # 箱Bの中で白が出る確率
def bayes(x, y, yx):
return yx * x / y
print bayes(x, y, yx)
>> 0.111111111111
「約0.9と約0.1なので白が出たら箱Aの確率が高いですね!」
「ここまで分かると、『ナイーブベイズ分類器』と呼ばれる機械学習のモデルを作ることができます。先ほどの問題のプログラムが書けていれば、半分くらい作ったも同然です」
「ナイーブベイズ分類器はベイズの定理を利用したモデルだったんですね」
「はい。ナイーブベイズ分類器は迷惑メールフィルタにもよく使われているモデルなのですが、簡単に考えるとXが『迷惑メール』なのか『迷惑でない』のかで、Yがいろんな単語が入っているかどうかです。例えば『儲かる』という単語が入っているメールは迷惑メールっぽいということだとP(Y[儲かる]|X[迷惑])が大きいとか、『数学』という単語が入って入れば迷惑メールでなさそうということだとP(X[迷惑でない]|Y[数学])とかいう感じです」
ナイーブベイズ分類器の実際のプログラムを読んでみよう
「ナイーブベイズ分類器のプログラムを作ってみました。ちょっと一緒に読んでみましょう」
#docing:utf-8
import math
from janome.tokenizer import Tokenizer
import os
class NaiveBayes():
def __init__(self):#学習結果を記録するobjectたち
self.categories={}#{文書のカテゴリc:カテゴリcに属する文書の数}
self.words={}#{文書のカテゴリc:{単語w:カテゴリcの文書全体での単語wの出現数}}
self.smoothing=1#初見の単語の確率
self.texts={}#学習データ{カテゴリc:[カテゴリcの文書]}
#与えられたディレクトリに入ったテキストファイルを学習データに加工する
def pre_training(self, dir):
self.texts={c.name:[] for c in os.scandir(dir) if c.is_dir()}
for c in self.texts:
file_list=[dir+'/'+c+'/'+f.name for f in os.scandir('train_data/'+c) if f.name[-3:]=='txt']
for file in file_list:
f=open(file,'r')
text=f.read()
f.close()
self.texts[c].append(text)
#実際にテキストファイルの単語の数を数えて学習させる
def training(self, data):
self.categories={c:len(data[c]) for c in data}
self.words={c:{} for c in self.categories}
for c in data:
for text in data[c]:
tokens=self.tokenize(text)
for w in tokens:
if w not in self.words[c]:
self.words[c][w]=int()
self.words[c][w]+=1
self.smoothing=.1/(sum([sum(self.words[c].values()) for c in self.categories]))#初見の単語の確率を小さく設定
#学習済み分類器をつかって与えられた文書の分類をする
def classification(self, test, uselog=True):
p_categories=probability(self.categories)
p_words={c:probability(self.words[c]) for c in self.categories}
test=self.tokenize(test)
results={}
#以下がベイズの定理を使うところ。ここではlogを使って実装している。
if uselog:
for c in self.categories:
results[c]=math.log(p_categories[c])
for t in test:
if t in self.words[c].keys():
results[c]+=math.log(p_words[c][t])
else:
results[c]+=math.log(self.smoothing)
#logを使わないと以下のようになる。
else:
for c in self.categories:
results[c]=p_categories[c]
for t in test:
if t in self.words[c].keys():
results[c]*=p_words[c][t]
else:
results[c]*=self.smoothing
return results
#与えられた文書を単語に分解する
def tokenize(self,text):
tokens=[]
t=Tokenizer()
pre_tokens=t.tokenize(text)
for token in pre_tokens:
tokens.append(token.surface)
return tokens
#出現頻度を割合に変換する
def probability(dict):
return {i:float(dict[i])/sum(dict.values()) for i in dict}
nb=NaiveBayes()
nb.pre_training('train_data')
nb.training(nb.texts)
#テストデータを用意する
file='test.txt'
f=open(file,'r')
text=f.read()
f.close()
#学習済み分類器で分類する
print(nb.classification(text))
「このモデルを利用して、スポーツニュースの文章を入力として、野球の記事なのか、サッカーの記事なのかを予測しましょう」
「このプログラムと同じ階層に、train_dataディレクトリを作り、その下のbaseballディレクトリには野球の記事のテキストデータを、soccerディレクトリにはサッカーの記事のテキストデータを入れてみました。はたしてどちらに分類されるでしょうか…」
「野球の方が出てきた値が大きいですね。ということはインプットした文章が野球である可能性が高いことになりますね」
「このプログラムを簡単に説明すると、インプットした文章を形態素解析を行い、野球の記事に出てきやすい単語、サッカーの記事に出てきやすい単語をそれぞれ集計します。それが新しくインプットした文書にどれくらい出てくるかをみているというわけです」
「ちなみに、このプログラムのどこでベイズの定理が使われているんですか?ベイズの定理はさきほどやったようにP(Y|X)=P(X|Y)P(Y)P(X)という公式だったと思いますが、それらしい計算をしているところが見当たりません」
「実はその処理はclassificationメソッドの中で行っています。つまり今回のプログラムでは、P(野球|文書)とP(サッカー|文書)を比較してどちらが大きいかを見ているわけですが、分母はどちらもP(文書)となっているので、省略しています」
「お気付きかもしれませんが、インプットした文章と同じものを丸々学習させているわけではないので、完全なるP(文書|野球)は求めることはできません。この確率の代わりにP(単語[0]|野球) * P(単語[1]|野球) * P(単語[2]|野球) * … * P(単語[k]|野球)で単純化して計算しています。なのでこのモデルのことを、「ナイーブベイズ分類器」と呼びます」
「でもこれたくさん文書を学習させると、値がどんどん小さくなっていきますね」
「その通りです。なのでここではlogを使って値が小さくなるのを防いでいます。大小を比べるだけだったらどっちでも同じですね。もし気になる方はuselog=Falseとしてみてください」
「なるほど、今まで機械学習ってブラックボックスだと思っていましたが、基本になっているのはベイズの定理のようなシンプルな公式の応用なんだと思うと、ちょっと親しみがわきますね」
次回は確率分布のパラメーターの推定を扱います。そこで必要な数学として微分について説明します。次回もどうぞお楽しみに!
CodeIQで機械学習クイズに挑戦しよう!
機械学習の理解度をはかるクイズに挑戦してみませんか?
ITエンジニアのスキルの腕試しができる「CodeIQ」では初心者向けの基礎知識をチェックできるクイズを出題中!
ぜひチャレンジしてみてくださいね♪
↓










