CodeIQ MAGAZINECodeIQ MAGAZINE

池澤あやかとプログラミングで学ぼう!機械学習のための数学講座【確率分布編】

2017.01.05 Category:【連載】池澤あやか☆勉強部 Tag: , , ,

  • 309
  • このエントリーをはてなブックマークに追加

最近とても注目を浴びている「機械学習」「アルゴリズム」を学ぶべく、「CodeIQ数学ゼミ」を開講しました。
増井雄一郎さんをはじめ、一緒にゼミを受けてくれるエンジニアの皆さんと一緒に学んだことをレポートします!
by 池澤あやか

機械学習のための数学ゼミ開講!

今なにかと話題の機械学習。これを期に機械学習をはじめてみようかなと思っている方も多いのではないでしょうか。

しかし、実際に機械学習をはじめてみると、数字や数式、グラフのオンパレードに目眩を起こしそうになります。数学が苦手な方にとってはなかなかハードルが高いのが現状です。

ただすごく面白そうな分野なので、このまま指をくわえてみているだけというのも寂しいですよね。

というわけでこの度、CodeIQ MAGAGINEでは「機械学習のための数学ゼミ」を開催することになりました。数学が苦手な皆さま、これを期に復習したいと思っている皆さま、一緒に勉強していきましょう!

▲「CodeIQ数学ゼミ」問題の挑戦者の中から、厳正な審査を経てゼミに参加してくれた皆さん。増井雄一郎さんも普通に応募してくれました(笑)。

<登場人物>


池澤あやか
タレントときどきエンジニア。数学は苦手だけど、機械学習入門したい。


増井雄一郎
株式会社トレタのえらいエンジニア。本人曰く、プログラムは書けるけど数学をまともに勉強したことがない。


梅崎先生
今回数学を教えてくれる先生。社会人のための数学教室「すうがくぶんか」の講師。

プログラミングで数学を学ぶ!


「ぶっちゃけ数学がすごく苦手で、全然わからないです」


「僕もです」


「皆さんはエンジニアなので、数学もプログラムに起こしてみると、もしかしたら理解が早いかもしれませんね。例えば、数学でよく使うΣをプログラムで書いてみましょう。するとこんな感じになります。言語は機械学習でよく用いられるPythonを使って書いてみます」


「例えばこれをプログラムに直すと、以下のようになります。Σはfor文だったんですね」


「プログラムにしてみると理解できる!不思議!」


「そんな感じで今回は、プログラミングを交えつつ数学を学んでいきましょう!」

機械学習に必要な数学の知識とは


「機械学習のために、数学をやり直すとはいえ、さすがに全部勉強し直していたらキリがありません。機械学習をするときにあると嬉しい数学の知識は『線形代数』『確率・統計』『微分・積分』です」


「ひえ~!高校時代は文系コースだったから、線形代数はやってないですね…」


「僕は数Iも終わってないので、線形代数どころか行列すらやってないですね」


「そんなお二人のために、この数学講座では数学をプログラムを交えながら教えていければと思います。それではまず今回は『確率・統計』のうち、確率について勉強していきましょう!」


「どうぞよろしくお願いします!」

データの予測に使える「確率・統計」


「そもそもなぜ機械学習に確率・統計が必要なんですか?」


機械学習とは、事前に学習しておいたデータをもとに、新しく来たデータがどこに分類されるかや、どんなデータが次に来る可能性が高いかを予測するというものです。データを集めたり整理する時に統計、予測する時に確率を使います。もちろん前のデータを元に予測しているだけなので、100%正解できるわけではないですよ!」

確率分布は、出来事が起こる可能性を全て書くこと!


「それでは、まずは確率分布について学んでいきましょう。はじめにどんな出来事が起こりうるかを考えます。例えば『コイン投げ』ではどうなるでしょうか?」


表が出るか裏が出るかの2つですかね」


「そうですね。それぞれの出来事がどれぐらいの割合で起こるか数字で表します。そしてその数のことをP(◯◯)と表します。例えば何の細工もない普通のコインなら、表と裏が同じ割合で出てほしいですよね。

これを数式ではP(コインを投げて表が出る)=1/2、P(コインを投げて裏が出る)=1/2と表記します。長ったらしいので、ここではP(表)=1/2、P(裏)=1/2と書くことにしましょう。

この式が言っているのは『コインを投げて表が出る確率は1/2で、裏が出る確率も1/2です』ということです。そしてコインを投げて表または裏が出る確率は1、つまりP(表または裏)=1/2+1/2=1となりますね」


コインの場合は表裏の2通りの結果が出ますが、起こりうる結果が複数ある場合もありますよね


「そうですね。一般的には、起こりうる結果がX1からXnまである時に、このようにP(X1)からP(Xn)に対して、0から1以下の数をそれらの合計が1となるように与えたものを確率分布と呼びます」


「ちょっと確率分布がどういうものか感じをつかむために、プログラミングで実験してみましょう。好きな確率分布を設定して、その結果を表示させることができます。以下のプログラムは、1~6までの数字が設定された確率で出るものです。サイコロの目の確率分布と一緒ですね」

>>> import numpy as np
>>> np.random.choice([1,2,3,4,5,6],p=[.1, .1, .5, .1, .1, .1])

こうすると100回一気に出してくれます。

>>> np.random.choice([1,2,3,4,5,6],100,p=[.1, .1, .5, .1, .1, .1])


「pを使って重み付けをしているので、半分の確率で3が出るように指定しているんですね」


「その通りです。どの出来事がどれぐらいの割合で起こるのか重み付けをするのが確率分布ということですね。ただ、実際に100回行った結果が50回出るというわけではない、というところに注意してください」

二項分布はコイン投げのことである!


「それではもっとも基本的で重要な確率分布である二項分布について確認していきましょう。二項分布というのは、例えばコイン投げを10回やって表の出る回数の確率分布のことです。何をやってるのか理解するために実験してみましょう。手元にコインがある方は、実際にコイン投げを4回やって表が何回出るのか、というのを記録してみてください」


「表3回、裏1回出ました」


「同時にコンピュータでもシミュレーションしてみましょう。以下のようにすると、表(0)と裏(1)が均等な確率のコイン投げを4回繰り返した結果を表示できます」

 import numpy.random as rd
 print rd.randint(low=0,high=2,size=4)


「上のプログラムを動かすと、それっぽい結果が出てきました」

>>> import numpy.random as rd
>>> print rd.randint(low=0,high=2,size=4)
[1 1 0 1]
>>> print rd.randint(low=0,high=2,size=4)
[1 1 0 0]
>>> print rd.randint(low=0,high=2,size=4)
[1 0 0 0]


「コインを4回投げて表の出る回数は0回、1回、2回、3回、4回の可能性がありますよね。つまり確率分布としてはP(表が0)、P(表が1)、P(表が2)、P(表が3)、P(表が4)がそれぞれどんな数になるかを決めればいいわけですね。でもどうやって求めればいいんだろう?」


「ひとまず感じをつかむために、乱数を使って上のようにコイン投げを4回して、表が出た回数を100回分集計してみましょう」

>>> import numpy.random as rd
>>> dict={0:0,1:0,2:0,3:0,4:0}
>>> for i in xrange(100):
>>>  n=rd.randint(low=0,high=2,size=4).sum()
>>>  dict[n]+=1
>>> print dict


{0: 4, 1: 24, 2: 39, 3: 22, 4: 11}


「表が出た回数、0回が4回、1回が24回、2回が39回、3回が22回、4回が11回ですね」


「そうですね。もう一度実行すると違う結果が出てきます。これがだいたいどういう値になるか知りたいわけですが、次の公式で、表が1/2の確率で出るコインをN回投げた時、表がk回出る確率を求めることができます」


というのはN個のものからi個を選ぶ選び方の総数を表します。

これは実際には

を計算することになります。N!は大丈夫ですか?」


「5!だったら、5 * 4 * 3 * 2 * 1ってなるやつですよね。!は階乗って読むんですよね。大丈夫です」


「ちなみにこの式って口に出して読む時、どうやって読むんですか?」


「ピーかっこエックスイコールけーはエヌシーアイかけるニブンノイチのケー乗かけるニブンノイチのエヌひくケー乗、ですかね。それでは、問題を解いてみましょう!」

Q. 表と裏がそれぞれ1/2の確率で出るコインを4回投げるとします。この場合の表の出る回数の確率分布を計算してください。


「どれどれ、先程の式をプログラムにおとしこんでみますね!」

import matplotlib.pyplot as plt


N = 4
head_p = 0.5


# グラフを表示する
def bar_plot(p):
    plt.bar(range(0, N+1), p, label="N=" + str(N))
    plt.legend()
    plt.title("COIN")
    plt.xlabel("k")
    plt.ylabel("p")
    plt.show()


# 組み合わせの公式
def collaboration (N, k):
    denom = 1
    numer = 1
    denom_list = list(reversed(range(1, N+1)))
    numer_list =  list(reversed(range(1, k+1)))
    for _N, _k in zip(denom_list, numer_list):
        denom *= _N
        numer *= _k
    return denom / numer


# 実行
p = [0] * (N + 1)


for k  in range(N + 1):
    c = collaboration(N, k)
    p[k] = c * ((head_p ** k) * ((1 - head_p) ** (N - k)))


print p


bar_plot(p)


「この理論的な結果を元に100回実験すると、表が0回出るのは6.25回、1回出るのは25回、2回出るのは37.5回、表が3回出るのは25回、4回出るのは6.25回、となりそうということです。ところが上で実験した結果とは必ずしも一致しませんよね。確率分布と実際の結果にはズレが生じるものなんですよ」


「この公式では表が出る確率も裏が出る確率もどちらも1/2になっていますが、出る確率が表が1/4で裏が3/4だったらそれに置き換えればいいってことですね。さっきのコードだと、head_p=0.25かな。数式を見るとめまいがしますが、プログラムになっているとギリギリ理解できますね」

二項分布がコイン投げなら、多項分布はサイコロ投げ!


「続いては、多項分布についてです。二項分布は結果が2通り起こる出来事を繰り返した時の確率分布だったのに対して、多項分布はサイコロ投げのようにより多くの結果を持つものを繰り返した時の確率分布です。さっきと同じようにちょっとシミュレーションしてみましょう」

>>> import numpy.random as rd
>>> print rd.randint(low=1,high=7,size=20)
[1 6 2 2 5 2 2 2 1 6 6 1 4 5 2 4 1 3 3 1]


「こう書くと、1から6まで均等な確率のサイコロを20回振った結果を表示できます」


「サイコロ投げた時の結果っぽいですね。ただの乱数ですけど」


「サイコロ20回降った結果の確率分布ってどうなるのか想像できますか?」


「二項分布のときと同じで、サイコロを20回振った時に1が20回出る回数、1が19回出て2が1回出る回数、1が19回出て3が1回出る回数…ってめちゃめちゃ多いですけど、それを列挙していけばいいんですか?」


「そうですね。そのすべての回数のパターンごとにそれが起こる確率を並べたものが、多項分布です。基本的に多項分布は二項分布と考え方は一緒です」

▲二項分布とは違い、多項分布はとにかくパターンが多いので、確率分布をグラフにするのはちょっと難しそう


「それぞれのサイコロの目の出方が何通りかを求めるには、異なるN個を全て並べたパターン数からパターンが重複している分を割ればよいです。公式は以下のを使いましょう」


確率分布を求める場合はこれに確率をかけます。サイコロの目のでやすさの確率です。まあ、細工をしてない普通のサイコロであれば平等に1/6ですよね」


「ではこれを踏まえて、実際に確率分布を求めてみましょう。20回サイコロを振るのは多すぎるので、5回ぐらいにしときましょうか」

Q. サイコロを5回投げて出る目の回数の確率分布を求めてください。確率分布は確率を列挙したものです。


「これは僕がプログラムを書いてみました」

# 階乗
def factorial(n):
    prod = 1
    for i  in range(1, n+1):
        prod *= i
    return prod


#目の出方を列挙する
def prod(list1,list2):
 p=[]
 for l1 in list1:
  for l2 in list2:
   p.append(l1+l2)
 return p


def selfprod(list, n):
 sp=list
 for i in xrange(n):
  sp=prod(list,sp)
 return sp


k=[]
for i in xrange(6):
 k.append([i])


pattern=selfprod(k,5)




for p in pattern:
 sum=0
 for i in p:
  sum=sum+i
 if sum==5:
  denom=1
  for j in xrange(6):
   denom=denom*factorial(p[j])
  print p
  print factorial(5)/denom*(1.0/6)**5


「プログラミングを通してみると、苦手な数学も少し食べやすくなることが分かりました!」

次回もまだまだ続きます。続いては機械学習でかなり大切な概念になってくるベイズの定理を解説します。どうぞお楽しみに!

CodeIQで機械学習クイズに挑戦しよう!

機械学習の理解度をはかるクイズに挑戦してみませんか?
ITエンジニアのスキルの腕試しができる「CodeIQ」では初心者向けの基礎知識をチェックできるクイズを出題中!
ぜひチャレンジしてみてくださいね♪

  • 309
  • このエントリーをはてなブックマークに追加

■関連記事

ドキッ!女性だらけの「第3級アマチュア無線技士試験勉強会」に潜入してみた!... 女性限定!第3級アマチュア無線技士試験勉強会 こんにちは、池澤あやかです。 いろいろな技術系勉強会に潜入している私ですが、今回潜入したのは、エレクトロニクスとアマチュア無線の専門出版社、CQ出版が主催する「女性限定!第3級アマチュア無線技士試験勉強会」です!! アマチュア無線というと、高校...
ブラウザでVR?なにそれおいしいの?池澤あやかがWebVRの勉強会に潜入してみました!... WebにVRを求めるのは間違っているだろうか? 今回潜入してきた勉強会は、渋谷のイベントスペースdots.で行われた「html5j Webプラットフォーム部勉強会 第13回勉強会~WebにVRを求めるのは間違っているだろうか?~」。 ▲受付嬢は、勉強会やハッカソンでよく見るあの人だ…! ...
【池澤あやか勉強部】もしもPepperが金融マンになったら!?「Mizuho.hack」に潜入してみ... みずほ銀行の主催する「Mizuho.hack」に潜入! 2015年7月にみずほ銀行に入行した新米金融マンのPepper。 そんなPepperと一緒に「全く新しい『銀行』というサービスを創る!」というのが、今回のハッカソンのテーマです。 なので会場に入ってみるとたくさんのPepperたちが出...

今週のPickUPレポート

新着記事

週間ランキング

CodeIQとは

CodeIQ(コードアイキュー)とは、自分の実力を知りたいITエンジニア向けの、実務スキル評価サービスです。

CodeIQご利用にあたって
関連サイト
codeiq

リクルートグループサイトへ