🎄

データエンジニアがベイズ統計に入門してみた

に公開

はじめに

こんにちは!なおずみです!
datatech-jpアドベントカレンダー2025の21日目の記事です〜
普段はデータエンジニアとして働いていますが、MLOps関連のタスクをやる機会があり、ベイズ統計を少し勉強してみたので、超基本をまとめてみようと思います
データエンジニアがサイエンス領域をやるメリットなんかも話せればと思います
数式はあまり使わず、自分の理解ベースで書こうと思います

ベイズ統計とは

そもそも、ベイズ統計とはなんぞや?というところからですが、Wikipediaでは「ベイズ統計学は、確率のベイズ的解釈に基づく統計学」らしいです。
自分の理解だと、「取得したデータを使って、自分の予想した分布をどんどんブラッシュアップする統計」みたいな感じです。

(参考)ベイズの定理
alt text

ベイズをPythonのサンプルコードで理解してみる

簡単なお題を用意してベイズを触ってみます
今回は実務に繋がりそうなテーマということで、
「新しく公開したWebサイトを訪れた人で、クリック率はどれくらいか?」と言うテーマで考えてみます!

  1. まずは、必要なライブラリをimportします。
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import beta
  1. 真のCTR(クリック率)を10%に設定してみます。これに向けて分布がブラッシュアップされていく感じですね。
true_ctr = 0.10
  1. データを以下のように設定します。
steps = [
   {"imps": 0, "clicks": 0, "label": "初期状態 (一様分布)"},
   {"imps": 10, "clicks": 2, "label": "10回表示 (2回クリック)"},
   {"imps": 50, "clicks": 4, "label": "50回表示 (4回クリック)"},
   {"imps": 200, "clicks": 20, "label": "200回表示 (20回クリック)"}
]
  1. 事前分布を定義して、観測したデータで分布を更新していきます。
ちょっと数学的なお話

クリック率(2値の問題)を扱うのでベータ分布を採用します。
すると、クリック率pの事前分布は次のようになります。

f(p) \propto p^{\alpha-1}(1-p)^{\beta-1}

観測データの尤度(クリックが k 回、試行が n 回のとき)は次のように表せます。

f(\mathrm{data}\mid p) \propto p^{k}(1-p)^{n-k}

この2つを掛け合わせてみましょう。

f(p\mid\mathrm{data}) \propto \{p^{k}(1-p)^{n-k}\} \times \{p^{\alpha-1}(1-p)^{\beta-1}\}

指数の法則(x^{a}\times x^{b}=x^{a+b})を使って整理すると……

f(p\mid\mathrm{data}) \propto p^{\alpha+k-1}(1-p)^{\beta+(n-k)-1}

こうすると、計算結果も元のベータ分布と同じ形になります

新しいパラメータ \alpha' = \alpha + k (成功数を足す
新しいパラメータ \beta' = \beta + (n-k) (失敗数を足す)

つまり、今回のベイズ更新はただの足し算になるわけです

for i, step in enumerate(steps):
  
    # ベイズ更新
    a = 1 + step["clicks"]
    b = 1 + (step["imps"] - step["clicks"])
    y = beta.pdf(x, a, b)
    
    # グラフの作成
    fig = go.Figure()
    
    # メインの分布
    fig.add_trace(go.Scatter(
        x=x, y=y, 
        fill='tozeroy',
        line=dict(color='#2980b9', width=4),
        name='事後分布'
    ))
    
    # 真の10%を示す赤い線
    fig.add_vline(x=true_ctr, line_dash="dash", line_color="#e74c3c")

    # レイアウト
    fig.update_layout(
        title=dict(text=f"{step['label']}"),
        xaxis_title="クリック率 (CTR)",
        yaxis_title="確信度 (密度)",
        template="plotly_white",
        width=700,
        height=450,
        showlegend=False,
        margin=dict(t=80)
    )
    
    # 軸の範囲を固定(変化をわかりやすくするため)
    fig.update_xaxes(range=[0, 0.5])
    
    # 表示
    fig.show()
  1. 結果を見ていきます。

まずは、初期状態ですが、今回は新しく公開したWebサイトで、特に事前情報もないので一様分布になります。なお、今回はわかりやすくするために、真のCTRを赤線で表示してます。
alt text

次に、10回分のデータが取れた時点での分布を見てみます。2回のクリックがあり、少し上振れていますが、まだまだ意思決定するには難しい分布になっていますね。
alt text

次に、50回分のデータが取れた時点での分布を見てみます。4回のクリックがあり、真のCTRに近づいてきました。この段階で、「CTRは8%前後でありそうということはわかったので、売上予想は〇〇だから、効果の大きい施策を打つ必要があるか、、」みたいな意思決定に繋がりそうですね。そこまでいかなくても、CTRが30未満でありそうだぞと言うのはわかりますよね。
alt text

最後に、100回分のデータが取れた時点での分布を見てみます。21回のクリックがあり、かなり真のCTRに近づきました。ここまでくれば、意思決定するには十分ですね!
alt text

ベイズのいいところ

ベイズを少し触ってみて感じたメリットとしては以下の3点かなと思います。

  • 頻度論である、統計的仮説検定と違い、サンプルサイズやテスト期間を厳格に定めなくても、評価が可能な点
  • データが少ない場合でも、「だいたい〇〇くらい」や「なんかよさそう」といったある程度の結果が得られること。方向性が間違っていても早期に気付けると言う点で、このメリットはかなり大きいと感じました。
  • ハズレ値に強い。先のサンプルコードでもありましたが、ノイズにも対応できる点が良い。

データエンジニアがサイエンス領域触る意義

最近、MLOpsのタスクでサイエンスの領域に少し触れることがあるのですが、その中でデータエンジニアでよかったなと思う点が、データを準備するところと地続きになっているので、そこまで考えてDWH、データマートの設計ができる点です。
余分なコミュニケーションコストをかけることなくスムーズに実装でき、また、分析に必要なデータを自分で用意できるため、かなりメリットを感じました。

まとめ

今回は、最近触れたベイズ統計の基本について書きましたが、まだまだ使いこなせる段階ではないので、もっと勉強せんといかんですね。
次は、PyMCなどを使った実践的な内容で、記事をかければと思います。
誰かの何かの参考になれば幸いです〜

GitHubで編集を提案

Discussion