2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Python】「なぜ遅刻したのか?」を確率で暴く。pgmpyで学ぶ2階層ベイジアンネットワーク

2
Posted at

はじめに

朝、オフィスで同僚が遅れてやってきて、こう言います。
「すいません、電車が遅れて…」

この「電車が遅れた」という言葉、本当に電車のせいでしょうか?それとも、ただの寝坊を隠すための言い訳でしょうか?

不確実な世の中の事象の因果関係を確率で計算できる「ベイジアンネットワーク」を使えば、この身近な問題を数学的にモデル化し、「結果から原因を逆算する」ことができます。

今回は、Pythonの強力な確率的グラフモデルライブラリpgmpyを使い、2階層のネットワークを構築して、この「遅刻の言い訳」の真相を確率で解き明かしてみましょう。

今回作る2階層ネットワークの構造

まずは、どのような因果関係があるのかを整理します。今回構築するネットワークは以下のようになります。ご提供いただいた図が、この構造を視覚化しています。

image.png

遅刻要因のネットワーク (円の大きさ = 発生確率)

この図から、以下の2階層の構造が読み取れます。

  • 第1階層(電車の遅延要因): 「悪天候」と「事故」が起きると、「電車遅延」が発生する。
  • 第2階層(遅刻の要因): 「電車遅延」と、個人の「寝坊」が合わさることで、最終的な「遅刻」が発生する。

各事象(ノード)の下に書かれている確率(%)は、証拠(結果)がない状態での初期確率(周辺確率)を示しています。例えば、「遅刻」の初期確率は32.8%となっています。

それでは、このネットワークをPythonで実装していきましょう。

実装の準備

Google Colabなどの環境で、まずはライブラリをインストールします。

pip install pgmpy

ネットワークの構築と確率の定義

Pythonコードでネットワークの構造と、それぞれの条件付き確率表(CPT)を定義します。CPTの設定値は、図の周辺確率と整合性を保ちつつ、より自然な数値に設定しました。

from pgmpy.inference import VariableElimination
import matplotlib.pyplot as plt
import networkx as nx

# 推論エンジンの初期化(最新のモデルを使用)
infer_ja = VariableElimination(model)

# 1. 各ノードの周辺確率を計算
marginal_probs_ja = {}
for node in model.nodes():
    prob = infer_ja.query(variables=[node], show_progress=False)
    marginal_probs_ja[node] = prob.values[1]

# 2. グラフ構造の準備
G_final = nx.DiGraph(model.edges())

# 3. 描画設定(面積を確率に比例させる)
plt.figure(figsize=(12, 8))
base_size = 4000
node_sizes = [base_size + (marginal_probs_ja[node] * 12000) for node in G_final.nodes()]

nx.draw_networkx_nodes(G_final, pos_refined, 
                       node_size=node_sizes, 
                       node_color="#A2D9CE", 
                       edgecolors="#16A085",
                       linewidths=2)

nx.draw_networkx_edges(G_final, pos_refined, 
                       arrows=True, 
                       arrowstyle='-|>', 
                       arrowsize=40, 
                       width=2.5, 
                       edge_color="#5D6D7E")

# ラベルと数値(%)の描画
for node, (x, y) in pos_refined.items():
    p_val = marginal_probs_ja[node]
    label = f"{node}\n({p_val*100:.1f}%)"
    plt.text(x, y, label, 
             fontsize=12, 
             fontweight="bold",
             horizontalalignment='center', 
             verticalalignment='center', 
             fontproperties=font_prop)

plt.title("確率を反映した遅刻要因ベイジアンネットワーク", size=16, pad=25, fontproperties=font_prop)
plt.axis('off')
plt.tight_layout()
plt.show()

推論してみよう

モデルができたので、推論(Inference)エンジンを使って様々なシチュエーションを計算してみます。

infer = VariableElimination(model)

シナリオ1:普通に予測する(順方向)

「今日はあいにくの悪天候だ。同僚が遅刻する確率は?」

# 証拠(evidence)として悪天候=1を設定し、遅刻の確率を計算
result_forward = infer.query(variables=['遅刻'], evidence={'悪天候': 1})
print(result_forward)
+-------+-----------+
| 遅刻    |   phi(遅刻) |
+=======+===========+
| 遅刻(0) |    0.4303 |
+-------+-----------+
| 遅刻(1) |    0.5697 |
+-------+-----------+

実行すると、悪天候の影響で電車遅延リスクが高まり、最終的に遅刻する確率が初期の32.8%からグッと上がることが確認できます。

シナリオ2:結果から原因を暴く(逆推論)★ここが本番

ベイジアンネットワークの醍醐味は、結果から原因の確率を逆算できることです。

「同僚が遅刻してきた!しかも今日は快晴で天気は良い。あいつ、寝坊したんじゃないか?」

これを数学的に暴いてみましょう。

# 証拠:遅刻した(遅刻=1)、かつ悪天候ではない(悪天候=0)
# ターゲット:寝坊の確率
result_backward = infer.query(variables=['寝坊'], evidence={'遅刻': 1, '悪天候': 0})
print(result_backward)

【出力結果のイメージ】
出力結果は、以下のような完全に日本語化された表形式になります。

+-------+-----------+
| 寝坊    |   phi(寝坊) |
+=======+===========+
| 寝坊(0) |    0.3552 |
+-------+-----------+
| 寝坊(1) |    0.6448 |
+-------+-----------+

なんと、快晴の日に遅刻してきた場合、それが寝坊によるものである確率が約65.8%まで跳ね上がりました!(デフォルトの寝坊確率は15%でした)。
事故が起きている可能性もゼロではありませんが、確率論的には「寝坊を疑うのが妥当」という結果になります。

まとめ

ベイジアンネットワークを使うと、「もし〜だったらどうなるか」という単純な予測だけでなく、「いま手元にある証拠(結果)から、見えない要因(原因)の確率を逆算して推測する」ことが可能になります。

今回は提供図を活用し、「遅刻」というカジュアルなテーマで2階層のネットワークを構築しましたが、この手法は医療診断(症状から病気を特定)、障害対応(エラーログから故障箇所を特定)など、実務における不確実な事象の推論に幅広く応用できます。ぜひ皆さんも pgmpy と視覚的な図を活用して、複雑な問題の確率的なシミュレーションを楽しんでみてください!

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?