AIに欠かせない数学を、プログラミング言語Pythonを使って高校生の学習範囲から学び直す連載。今回は数学、AIがデータとの最適な対応関係を見つけるのに重要となる「確率」と「確率分布」についてPythonコードと図を交えて解説します。
この記事は会員限定です。会員登録(無料)すると全てご覧いただけます。
AIに欠かせない数学を、プログラミング言語Pythonを使って高校生の学習範囲から学び直す本連載『「AI」エンジニアになるための「基礎数学」再入門』。前々回で「関数」について解説し、前回の「微分・積分」では、微分・積分が単に関数の傾きや面積を求める数学的な手法であることを説明しました。その中でAIがデータ間の対応関係を見つけ出すとき、微分を用いて対応関係を見つけ出す計算を行っていることが分かったと思います。
今回のテーマは統計学において重要となる「確率と確率分布」です。AIにおいても当然、確率は重要であり、AIの計算結果を解釈するのに確率の理解は必須なので、その辺りも含めて解説します。これまでの連載と同じように数式をあまり用いることはせずに内容を理解できるように進めていきます。
まず確率とは物事の「起こりやすさ」を定量的に表す指標です。起こりやすさを定量的に表すことで、どのような利点があるのでしょうか。翌日の降水確率で考えてみましょう。翌日に雨が降るかどうかは「雨が降る」「雨が降らない」の2つの結果しか存在しませんが、この不確定な2つの結果しか分からない状態では翌日に傘を用意すればよいのかどうかを判断する基準がありません。そこで降水確率を用いて「どちらが起こりやすいか」を定量的に表すことで将来をある程度推測することができ、翌日の行動を決めることができます。このように未来をある程度推測できることが確率を用いることの大きな利点です。
確率については「数学の中でも抽象的で理解するのが難しい」「学生がつまずくポイント」といわれていますが、実は確率は難しい問題を簡単にするための方法として編み出されました。
まず確率を導入することの目的は不確定な現象に対して起こりやすさを定量的に表すことと説明しましたが、実は初期条件やその過程を物理的に計算すれば、予測することは不可能ではありません。
コイントスを例に取って説明します。サッカーでは試合開始時にコインを投げてその表(おもて)/裏でキックオフを行うチームを決めます。コインを投げて表が出る確率は、結果が出るまでどちらとなるかを知ることができないため、不平等な決め方にはならないと思いますが、実は使用するコインの重心の位置、投げるときの強さや高さ、投げるときの風の強さなどの情報が正確に分かっていれば、投げる前からコイントスの結果を計算から導くことができ、確率よりも正確な未来を知ることができます。従って、その計算を都度行えば確実に自分のチームがキックオフの権利を得ることが可能になります。
しかし、現実において逐一その情報(当然コインを投げるたびにこの情報は異なります)を知ることは困難であり、現実的ではありません。そこで、コイントス時のさまざまな情報から未来を推測するのはやめて、コイントスの結果のみに注目し、試行回数を重ねて得られる結果から未来を推測するための手段が確率です。
確率は、複雑な情報に注目する必要はなく、結果のみに注目すればよいため、「単純化した推測の手段」といえます。
先ほど起きている現象の結果のみに注目し、単純化したものが確率と説明しました。この時に重要となるのが、確率を求めるためのサンプルデータの数です。コイントス時の条件はさまざまなものがあり、その道のプロならともかく、普通は同じ人がやっても全く同じ条件となることはほとんどありません。表(おもて)/裏どちらが出やすいかを知るためには、何度もコイントスを試してデータを集める必要があります。例えば、コイントスの試行回数が4回のときに表(おもて)が4回出たとします。このとき表(おもて)が出る確率(起こりやすさ)は以下のように計算できます。
表(おもて)の出る確率 = 表(おもて)の出た回数 ÷ 試行回数 = 4 ÷ 4 = 1
確率が1(100%)なので、このコインでは常に表(おもて)が出ることになりましたが、本当にそうでしょうか?
もちろんその可能性もありますが、試行回数が少ないことでたまたま表(おもて)が連続して出た可能性もあり、この結果を素直に信頼することは難しいでしょう。この時、結果の信頼性を高めるためには、試行回数を増やすことであるのは直感的にも理解できると思います。たまたま表(おもて)が連続した場合であったとき、試行回数が増えれば、この“たまたま”は全体の結果から見て少なくなり、もっとも起こりやすいパターン(表《おもて》の出る確率)が表れてくることが考えられます。
このように試行回数が増えたときに真の確率に近づくことを「大数の法則」と呼びます。カジノなどのギャンブルでは、大数の法則によって得られる真の勝率が必ず50%未満となるように設定(そうでないと運営側がもうかりません)されており、試行回数が増える(同じゲームをやり続ける)と必ず損をすることになります。
大数の法則をPythonコードで実験してみましょう。「コイントスで表(おもて)が出る確率が50%であるとしたときに、試行回数ごとに求めた結果が正解の50%にどう近づくか」を以下のスクリプトを実行しその結果を確認してください。
# 乱数を計算するライブラリのインポート import random as ran # 表(おもて)の出る確率を設定 head_probability = 0.5 # 進行回数の数を与えたら、その回数での表(おもて)が出る確率を計算する関数 def coin_toss(N): head_num = 0 for i in range(N): if ran.random() < head_probability: head_num += 1 # 表の出た回数をカウント return head_num/N for n in [4,10,20,50,100,1000,10000,1000000]: print('試行回数 =',n,'表の出る確率 = ',coin_toss(n))
試行回数 = 4 表の出る確率 = 0.8 試行回数 = 10 表の出る確率 = 0.6 試行回数 = 20 表の出る確率 = 0.55 試行回数 = 50 表の出る確率 = 0.46 試行回数 = 100 表の出る確率 = 0.58 試行回数 = 1000 表の出る確率 = 0.514 試行回数 = 10000 表の出る確率 = 0.4996 試行回数 = 100000 表の出る確率 = 0.500177
結果から試行回数が増えれば、得られた確率が正解の確率に近づいていることが分かります。
コイントスの表(おもて)が出る確率が2分の1である場合、当然裏が出る確率は2分の1となります(コインが落ちたとき真横に立っていることはないとします)。つまり、表裏とその確率において以下のような対応関係があることになります。
表(おもて) | 裏 | 計 | |
---|---|---|---|
2分の1 | 2分の1 | 1 | |
このような試行によって得られる「結果」(今だと表《おもて》と裏)とその「確率」があるとき、前者を「確率変数」と呼びます。正確には確率変数はその期待値や分散を計算するために数値であることが求められるため、コインの表(おもて)/裏を確率変数として用いる場合は表(おもて)が1、裏が0などの数値に変換する必要があります。
また、以前解説した関数の説明(未読の方はチェック!)と同様に確率変数と確率は1対1の対応関係となります。そして確率変数に対応している確率を全て足し合わせると、その値は「1」になります。これは確率変数がその現象で起きる結果を全て網羅していることを意味しています。例としてイカサマのないサイコロで確率変数と確率を表したのが下記です。サイコロの出目は1〜6しか存在しないため、確率変数は6個です。
確率変数(サイコロの出目) | 1 | 2 | 3 | 4 | 5 | 6 | 計 |
---|---|---|---|---|---|---|---|
確率 | 6分の1 | 6分の1 | 6分の1 | 6分の1 | 6分の1 | 6分の1 | 1 |
このような確率変数とその確率の一覧のことを「確率分布」と呼びます。統計学では「ある現象には背後に確率分布があり、今観測したデータはその確率分布から生じた結果の一部である」と考えます。確率分布が分かることで、まだ手に入れていないデータについても推測できるのが、確率分布の利点です。
例えば、データからサイコロを2回投げたときの確率分布を求めることで、「どの出目パターンがどれくらいの確率で現れるか」を、試行回数を重ねなくても、計算することで求めることができます。今あるデータから背後に存在する確率分布を推定し、推定された確率分布から未知のデータを推測するという流れを覚えておいてください。
ここからは確率分布の性質について解説します。確率分布の性質を知る上で重要となるのが、「期待値」と「分散」です。
まず期待値とは、確率変数をその確率で重みを付けた平均値であり、試行回数を重ねたときに得られる確率変数の平均値です。試行回数を重ねると得られた確率変数の平均値は期待値に近づくので、ギャンブルでは期待値を知ることで掛け金に対して戻ってくる見込みの金額を事前に知ることができます。
宝くじを例に説明します。宝くじは当選すれば大金がもらえますが、当選する確率はとても低く、「年末ジャンボ」の1等当選(得られる金額は7億円)確率は0.00000005程度です。期待値を計算すれば、何度も宝くじを購入した際に得られる宝くじ1枚当たりの平均的な金額(期待値)を計算することができます。
計算してみましょう。計算を簡単にし理解しやすくするために1等当選以外は全て外れの場合を考えます。このとき確率分布は下記のように表せます。
確率変数(もらえる金額) | 7億円 | 0円 | 計 |
---|---|---|---|
確率 | 0.00000005 | 0.99999995 | 1 |
期待値は確率変数に対応する確率を掛けたものを足し合わせた結果であり、この確率分布での期待値は以下のように求められます。
期待値 = 7億円 × 0.00000005 + 0円 × 0.99999995 = 35円
期待値の計算から、宝くじ1枚当たりの得られる平均金額は35円となり、宝くじの購入金額は1枚300円なので平均的には1枚ごとに265円損をすることになります。逆にいうと、期待値が300円を超える場合は運営側が損をすることになるので、運営側はそうならないよう当選確率を下げる必要があります。保険なども同様に運営側が利益を出せるように病気になる確率から配当金が調整されています。
このことから統計学的には「宝くじなどのギャンブルはやめた方がよい」と判断できると思います。ですが、「自分に都合の良いことが起きる」と信じてしまうのが人間なので、ギャンブルがなくなることはないと思います。
次に、確率分布の分散について説明します。期待値が確率分布の平均値を表すのなら、分散は「期待値の周りに、どれだけのばらつきでデータ(確率変数)が分布しているか」を表す指標です。同じ期待値を持つ確率分布であっても分散が大きい場合と小さい場合でその振る舞いは大きく変わります。分散が小さい場合はデータが期待値のすぐ近くに分布していることになるので、未知のデータも期待値に近い値を取り得る確率が高くなります。一方、分散が大きい場合はデータが期待値の遠くまで離れて分布していることになり、未知のデータが期待値に近い値を取る確率は低くなります。
分散を理解するために普通のサイコロと細工したイカサマサイコロの2つの確率分布を例に見ていきます。以下の表は細工して6の目が出やすくなったサイコロの確率分布です。このサイコロは6が出やすくなっており、サイコロを投げなくても次の目がある程度予測できます。この予測しやすさが、分散が小さいことを意味しており、予測のしやすさを数値で定量的に表現したのが分散です。人間が感覚で理解できても数値に置き換えなければAIは理解することができません。期待値や分散を計算することでAIは確率分布の性質を理解できます。
では、通常のサイコロと細工したサイコロの分散を求めてみましょう。
確率変数(細工したサイコロの出目) | 1 | 2 | 3 | 4 | 5 | 6 | 計 |
---|---|---|---|---|---|---|---|
確率 | 100分の1 | 100分の5 | 100分の5 | 100分の5 | 100分の5 | 100分の79 | 1 |
分散は確率変数と期待値の差分の2乗に確率変数に対応する確率を掛けたものです。まずは通常のサイコロの分散を求めます。通常のサイコロの期待値をμとすると、μは下記のように求められます。
このため、通常のサイコロの分散Vは下記のように表せます。
同様の計算をイカサマのサイコロでも計算してみましょう。イカサマのサイコロの期待値をμ2、分散をV2とすると、下記のように表せます。
Vを小数で表すと2.9166であり、V2は1.4475なので、イカサマのサイコロの方が、分散が小さいことが分かります。このように分散を計算することで予測のしやすさといった抽象的なものを定量的に表すことができました。
ここでは分散を「V」と表現しましたが、統計では分散を「σ2」と表記することが多いでしょう。なぜわざわざσ2と表記するかというと、数学的には分散の方が扱いやすいのですが、分散の平方根をとったσ(必ず正の値を取ります)の方が人間には理解しやすいためです。
期待値は確率変数に確率を掛けた値の平均値であり、その単位も確率変数と同じです。一方、分散は導出の過程で確率変数の2乗が式に含まれているので、その単位は確率変数の2乗となり、確率変数との単純比較が難しくなります。例えば、確率変数が円の単位を持つ場合、分散は円の2乗が単位になります。そこで、分散の代わりにその平方根である標準偏差を用いることがあります。
標準偏差は確率変数と同じ単位を持ち、「σ(シグマ)」と表記されるので、標準偏差が基準となる場合は分散の表記はσ2となります。分散と標準偏差は過去の連載記事でも説明があるので、こちらも確認してください。
ここまでで確率分布が分かり、その期待値と分散を計算すれば分布の性質が定量的に理解することができると説明しました。しかし、現実のデータから確率分布を求めるには膨大なデータが必要になります。特に、確率変数がこれまでの解説にあるような離散的なものではなく、連続的な場合(日本人の身長など際限なく細かくできるもの)、全ての確率変数の確率を求めるのは現実的ではありません。統計学では確率変数のパターンに応じて確率分布の関数が用意されているため、データに適応する確率分布を選んでその性質を分析するのが現実的です。ここでは代表的な確率分布として2つの分布を紹介します。
コイントスのように、何かを行ったときに起こる結果が2つしかない試行のことを「ベルヌーイ試行」と呼び、ベルヌーイ試行をn回行って、成功する回数が従う確率分布を「二項分布」と呼びます。二項分布を使うと、コイントスでいう「表(おもて)の出る確率」を与えるだけで、すぐに確率を計算できるので、「試行回数が100回となったとき表(おもて)が10回出る確率」など未知のデータを知ることもできます。
二項分布を理解するためにコイントスを3回試行して2回成功した場合の確率を計算してみます。最初に2連続で成功した場合は「成功、成功、失敗」となるので、1回の試行において成功する確率をpとすると、その確率は下記のように表せます。
P2連続成功の確率 = p × p × (1 − p)
ここで(1−p)は失敗した際の確率を表しています。今考えるのは3回の内、2回成功した確率なので、考える必要のある確率は下記のように2パターンも存在します。
最初に2連続で成功した場合と合わせると、3回の内、2回成功した確率は下記のように表せます。
P2回成功確率
= p × p × (1 − p) + p × (1 − p) × p + (1 − p) × p × p
= 3 × p2 × (1 − p)1
これが二項分布の式です。この式を一般化して試行回数をN、成功回数をkとすると、その式は下記のように表せます。
P二項分布(N,k) = NCkpk(1 − p)N − k
ここでNCkは、N個からk個を選ぶ組み合わせの数を表し、N回からk回を選んだときの数(試行3回から、2回成功の場合は3)を表します。この式から成功確率p、試行回数N、成功回数kを与えれば、直ちにその確率が計算できます。
少し複雑でしたが、プログラムを用いれば数式を覚えていなくても簡単に計算できます。Pythonで二項分布を用いた計算を行ってみましょう。
例としてゲームでガチャを回したときに「最高レア」のキャラを獲得する際の確率について考えてみましょう。今、最高レアの排出確率が7%のゲームを想定します。最高レア以外のキャラも当然排出されますが、今回考えるベルヌーイ試行としては最高レアのキャラを「獲得した」「獲得していない」の2つです。100回ガチャを回した場合、最高レアのキャラを10枚獲得するのはどれくらいの確率になるのでしょうか?
下記のコードを実行して獲得枚数ごとの獲得確率を計算してみてください。100回の試行の中で、獲得枚数が0〜100ごとの最高レア獲得確率を計算しています。また、その結果から期待値も求めています。
import numpy as np from scipy.stats import binom # 二項分布を計算するライブラリのインポート # 最高レアの排出確率(7%) p = 0.07 # 試行回数 N = 100 probabilty = [] # 各獲得枚数での結果を保持するリストを用意 mu = 0. for n in np.arange(0, N + 1): P_bp = binom.pmf(n, N, p)# 二項分布で確率計算 probabilty.append(P_bp) # リストに結果を代入 mu += P_bp*n # 期待値計算 print(str(n) + "枚獲得の確率 = " , P_bp) print('期待値 = ', mu)
4枚獲得の確率 = 0.08875176203336242 5枚獲得の確率 = 0.1282606109385463 6枚獲得の確率 = 0.15285538758804804 7枚獲得の確率 = 0.15449899390618982 8枚獲得の確率 = 0.13518661966791728 9枚獲得の確率 = 0.10401455563457594 10枚獲得の確率 = 0.07124437842927483 期待値 = 6.999999999999935
出力された結果を見ると、10枚獲得する確率は約7%、期待値は約7枚となりました。この計算により、ガチャを行うユーザーは、支払う金額に見合う結果を得ているのかどうかを事前に知ることができます。
さらに結果を可視化してみましょう。下記のコードを実行してみてください。二項分布から計算された確率分布を見ることができます。図から期待値付近でピークを持つ分布であることが分かります。
import matplotlib.pyplot as plt # グラフを表示させるPythonライブラリインポート import japanize_matplotlib # 日本語表記対応のPythonライブラリインポート plt.style.use('ggplot') # グラフのフォントをggplotに設定 plt.plot(probabilty,marker='o') # plot plt.xlabel("獲得枚数") # x軸のラベル表記 plt.ylabel("獲得確率") # y軸のラベル表記 plt.show() # グラフを表示させる
ここからは統計の基本であり、最も重要な確率分布である「正規分布」について説明します。正規分布は左右対称で中心からの距離が離れるにつれてその確率が大きく減少していく分布です。これまで離散的な確率変数を扱ってきましたが、正規分布は確率変数が連続的に変化する場合の分布です。連続的な確率変数に従う分布は「確率密度分布」と呼びます。確率密度分布は、それ自体が確率ではなく、確率密度分布をある範囲で足し合わせた結果が確率になります。
確率密度関数を用いる理由は確率変数が連続値を取ることが原因です。確率変数が連続値の場合、その値を無限に細かく表現することが可能で、その場合個々の数値の確率は0となってしまいます。
身長で例えると、約170センチの人は一定数いますが、身長を細く表現してもちょうど170.00000000……センチとなる人は存在しません。すると、身長がちょうど170センチである人の存在確率は0となり、同様にちょうど171センチである人の存在確率も0になります。
このように、連続値の場合、確率変数の確率が0となってしまいます。従って確率変数が連続値の場合はちょうどの値での確率が出せないので、確率分布が定義できません。そこで、確率変数と確率が一対一で対応している確率分布ではなく、ある範囲で足し合わせた結果が確率となる、確率密度分布を用います。つまり、確率密度分布(170〜171センチ間の全ての値)を足し合わせると、身長170〜171センチになる確率になります。ここで「確率密度を足し合わせる」という行為をより数学的にいうと、「確率密度分布を、身長170〜171センチの間で積分する」となります。積分に関しては以前の解説をチェックしてください。
正規分布が重要である理由は多くの統計的手法において、データが正規分布に従うことを仮定しているからです。この仮定は世の多くの「分布」が正規分布であることに起因しています。例えば、全国における男女別の身長の分布、全国で学力テストを行った場合の得点の分布などは正規分布です。身長で考えると、全国で、身長の高い人もいれば、当然低い人もいます。それぞれ個人の身長が高い/低い理由には、その要因(遺伝や生活習慣など)がいろいろとあります。このような要因が多くなるにつれて、個々の要因が打ち消し合い左右対称である正規分布になります。
正規分布の形状を見てみましょう。正規分布がもつパラメーターは確率変数の期待値μと標準偏差σのみです。Pythonで分散が1の正規分布(μ = 0、σ = 1)と分散が2の正規分布(μ = 0、σ = 2)を描画するコードは下記のように表します。
from scipy.stats import norm #正規分布を計算するライブラリのインポート import numpy as np import matplotlib.pyplot as plt # グラフを表示させるPythonライブラリインポート import japanize_matplotlib # 日本語表記対応のPythonライブラリインポート X = np.linspace(-6,6,100) #-6から6までの区間で100点のデータ生成 norm_pdf_1 = norm.pdf(X, 0, 1)#期待値0、分散1の正規分布生成 norm_pdf_2 = norm.pdf(X, 0, 2)#期待値0、分散3の正規分布生成 plt.style.use('ggplot') # グラフのフォントをggplotに設定 plt.plot(X,norm_pdf_1,label='分散が1の正規分布') plt.plot(X,norm_pdf_2,label='分散が2の正規分布') plt.xlabel("X") # x軸のラベル表記 plt.ylabel("確率") # y軸のラベル表記 plt.legend() # 凡例表示 plt.show() # グラフを表示させる
先述したように中心に対して左右対称な分布です。
xを連続的な確率変数として正規分布を式で表したのが下記です。
ここで、「e」は「ネイピア数」と呼ばれるもので「e=2.71828182846……」となっており、永遠に終わることのない数です。分布の数式を書きましたが、この数式を覚える必要はなく、分析においてはその性質を理解していれば問題ありません。重要なのは「正規分布は左右対称であり、中心部の確率が最も高くなる」ということ、分散(標準偏差)の値が正規分布の対称性を変えることはありませんが、「分散が大きくなると正規分布はより広がった形になる」ということです。
正規分布を用いて議論する際、標準偏差を用いてその確率を表すことが多々あります。正分分布の中心から「+σ」と「-σ」で制限された領域の確率を足し合わせると約68%となります。従って、全体の7割近くはμ±σに収まることになり、確率分布が正規分布に従う場合は未知データも約68%の確率でこの範囲に収まることになります。また中心から+2σと-2σで制限された領域(μ±2σ)には約95%、μ±3σの領域では約99.7%の確率でその領域に収まることになります。
この表現を用いる例としては、受験のときによく聞く「偏差値」があります。偏差値は全国学力テストの平均値を「50」として1σが「10」となるように計算されています。つまり、偏差値40〜60の間には全体の約68%が存在し、偏差値が30〜70の間には約95%、偏差値80以上の人は0.015%しか存在しないということです(0.03%の半分)。同様に統計的には偏差値20以下の人も0.015%しか存在しないことを意味しています。偏差値80以上になることと偏差値20以下になることは正規分布を用いた確率表記だと同じくらいのレアな存在となります。「偏差値80以上」または「偏差値20以下」というのはその希少性で評価されることです。
最後に日本人の身長データを正規分布で表して、その1σ、2σ内での存在確率を見てみましょう。日本政府のデータによると30代男性の平均身長は171.2センチであり、標準偏差は5.5センチとなっています。
日本国民の30代男性全員の身長を測定することはできませんが、正規分布を用いることで、その存在確率を表すことができました。また、標準偏差を用いて下記のことがいえます。
1988年、岐阜県生まれ。大学院では宇宙物理を専攻し、卒業後はデータサイエンティストとしてキャリアをスタートする。
さまざまな会社で分析コンサルを経験した後、世界最大の外資系ECサイト会社でデータサイエンスの知識を深める。
現在は東京ITスクールの講師として、実務レベルのデータサイエンティスト輩出を目的としたAI研修に努める傍ら、分析や機械学習を用いたマーケティング支援を行う。
1991年、富山県生まれ。大学院を卒業し、企画コンサルタントとしてキャリアをスタートする。その後、統計学とプログラミングスキルの知見を基にデータサイエンティストとしてのキャリアを進める。現在は東京ITスクールの講師として、実務レベルのデータサイエンティスト輩出を目的としたAI研修に努める傍ら、分析や機械学習を用いたマーケティング支援を行う。
Copyright © ITmedia, Inc. All Rights Reserved.