Mímisbrunnr知恵の泉

← マーケティングサイエンス 一覧

🎓 レベル:標準 | 重要度:A(必須)

📎 関連:第5章 顧客選好と選択モデル 目次 | 前提:需要曲線と価格弾力性

要点(BLUF)

1. ランダム効用モデル(RUM):選択を確率で表す

需要曲線と価格弾力性 では、価格に対して需要が連続的にどう動くかを「集計レベル」で見ました。離散選択モデルは、同じ需要を「個人がどの選択肢を選ぶか」という離散の意思決定から組み立て直します。鍵になるのが**ランダム効用モデル(Random Utility Model, RUM)**です。

消費者 nn が選択肢 jj から得る効用を

Uij=Vij+εijU_{ij} = V_{ij} + \varepsilon_{ij}

と置きます。Vij=βxijV_{ij}=\beta^\top x_{ij}観測できる属性(価格・品質・ブランドなど xijx_{ij})から決まる確定効用εij\varepsilon_{ij} は分析者には見えない要因をまとめたランダムな誤差です。消費者は効用が最大の選択肢を選ぶ——これが行動の仮定です。分析者には ε\varepsilon が見えないので、「選ぶ/選ばない」は確率的にしか予測できません。だから選択を確率で表すのです。

flowchart LR
  X["属性 x(価格・品質)"] --> V["確定効用 V = β·x"]
  E["Gumbel 誤差 ε"] --> U["ランダム効用 U = V + ε"]
  V --> U
  U --> P["選択確率 = ソフトマックス"]
  P --> L["対数尤度を最大化して β を推定"]

2. Gumbel 誤差からソフトマックスへ(数式)

選択肢 ii が選ばれるのは、その効用が他のすべてを上回るときです。

Pni=Pr ⁣(UniUnj  j)=Pr ⁣(Vni+εniVnj+εnj  j)P_{ni} = \Pr\!\left(U_{ni} \ge U_{nj}\ \ \forall j\right) = \Pr\!\left(V_{ni}+\varepsilon_{ni} \ge V_{nj}+\varepsilon_{nj}\ \ \forall j\right)

ここで誤差 εnj\varepsilon_{nj} が互いに独立に同一の第I種極値分布(Gumbel) F(ε)=exp(eε)F(\varepsilon)=\exp(-e^{-\varepsilon}) に従うと仮定すると、上の最大化確率は驚くほどきれいな閉じた形になります(導出は2つの Gumbel の差がロジスティック分布になることを使いますが、ここでは結果を採ります)。

Pni=eVnik=1JeVnkP_{ni} = \frac{e^{V_{ni}}}{\sum_{k=1}^{J} e^{V_{nk}}}

これが多項ロジット(Multinomial Logit, MNL)で、右辺はまさにソフトマックス関数です。確定効用 VV が大きい選択肢ほど指数で増幅され、合計が1になるよう正規化されて選択確率になります。

NN 機会ぶんの選択データ {yn}\{y_n\}yny_n は機会 nn で実際に選ばれた選択肢)が得られたら、対数尤度

(β)=n=1NlogPn,yn=n=1Nlogeβxnynkeβxnk\ell(\beta) = \sum_{n=1}^{N} \log P_{n,y_n} = \sum_{n=1}^{N} \log \frac{e^{\beta^\top x_{n y_n}}}{\sum_{k} e^{\beta^\top x_{nk}}}

を最大化して β\beta を推定します(実装では符号を反転した負の対数尤度を最小化します)。

ここで MNL の核心的な性質が見えます。選択肢 iijj の選択確率の比は

PniPnj=eVnieVnj=eVniVnj\frac{P_{ni}}{P_{nj}} = \frac{e^{V_{ni}}}{e^{V_{nj}}} = e^{V_{ni}-V_{nj}}

で、他のどの選択肢 kk にも依存しません。これを **IIA(Independence of Irrelevant Alternatives、無関係な選択肢からの独立)**と呼びます。計算を軽くする便利な性質ですが、似た選択肢が混じると非現実的な予測を生みます(§⚠️で扱う赤バス・青バス問題)。

もうひとつ。ε\varepsilon の分散は Gumbel で π2/6\pi^2/6 に固定されています。これは効用に絶対的な単位がないことを意味し、β\beta は誤差のばらつきを基準にした相対値として識別されます。全係数を定数倍しても誤差スケールを同じだけ変えれば確率は不変なので、規模そのものは決まらず、符号と係数間の比だけが解釈可能です。後で WTP(係数の比)が意味を持つのはこのためです。

3. β の推定と選択確率カーブ(コード)

3選択肢 × 4000機会の合成選択データを作ります。各選択肢に価格 price~U(1,5)・品質 quality~U(1,5) を機会ごとに与え、真の係数 βprice=0.8, βquality=1.5\beta_{\text{price}}=-0.8,\ \beta_{\text{quality}}=1.5 の確定効用に Gumbel ノイズを足して、効用最大の選択肢を選ばせます。そのデータから負の対数尤度を scipy.optimize.minimize(BFGS)で最小化して β\beta を推定し、真値を回復することを確かめます。

import numpy as np
import pandas as pd
from scipy.optimize import minimize

# 3選択肢×4000機会の合成選択データ。各選択肢に価格と品質を機会ごとに与え、
# 真の係数 β_price=-0.8, β_quality=1.5 の効用に Gumbel ノイズを足し、効用最大を選ぶ。
rng = np.random.default_rng(42)
N, J = 4000, 3
price = rng.uniform(1, 5, (N, J))      # 各機会・各選択肢の価格(順序厳守)
quality = rng.uniform(1, 5, (N, J))    # 各機会・各選択肢の品質
beta_true = np.array([-0.8, 1.5])      # [価格, 品質] の真の係数

V = beta_true[0] * price + beta_true[1] * quality   # 確定効用 V_ij
eps = rng.gumbel(0, 1, (N, J))                       # 第I種極値分布の誤差
U = V + eps                                          # ランダム効用 U_ij = V_ij + ε_ij
choice = U.argmax(axis=1)                             # 各機会で効用最大の選択肢を選ぶ

# 負の対数尤度。β を受け取り、ソフトマックスで選択確率を作り、選ばれた肢の対数確率の和の符号を反転。
def neg_loglik(beta):
    v = beta[0] * price + beta[1] * quality          # (N, J)
    v = v - v.max(axis=1, keepdims=True)             # オーバーフロー防止の平行移動
    expv = np.exp(v)
    P = expv / expv.sum(axis=1, keepdims=True)       # ソフトマックス
    chosen_p = P[np.arange(N), choice]
    return -np.sum(np.log(chosen_p))

res = minimize(neg_loglik, x0=np.zeros(2), method="BFGS")
beta_hat = res.x

tbl = pd.DataFrame({"真値": beta_true, "推定値": beta_hat}, index=["price", "quality"])
print("=== MNL:負の対数尤度最小化(BFGS)による係数の回復 ===")
print(tbl.to_string(formatters={"真値": "{:.2f}".format, "推定値": "{:.3f}".format}))
print(f"\n収束: {res.success} 最終の負の対数尤度: {res.fun:.1f}")

# 推定 β での選択確率から予測シェアを作り、実データの選択シェアと比べる
v = beta_hat[0] * price + beta_hat[1] * quality
v = v - v.max(axis=1, keepdims=True)
P = np.exp(v) / np.exp(v).sum(axis=1, keepdims=True)
share_pred = P.mean(axis=0)
share_obs = np.bincount(choice, minlength=J) / N
share = pd.DataFrame({"実シェア": share_obs, "予測シェア": share_pred},
                     index=["選択肢A", "選択肢B", "選択肢C"])
print("\n=== 選択シェア:実データ vs 推定モデル予測 ===")
print(share.to_string(formatters={"実シェア": "{:.1%}".format, "予測シェア": "{:.1%}".format}))

出力:

=== MNL:負の対数尤度最小化(BFGS)による係数の回復 ===
           真値    推定値
price   -0.80 -0.856
quality  1.50  1.556

収束: True 最終の負の対数尤度: 2307.2

=== 選択シェア:実データ vs 推定モデル予測 ===
      実シェア 予測シェア
選択肢A 33.0% 33.7%
選択肢B 32.4% 33.0%
選択肢C 34.7% 33.3%

出力の意味:まず推定係数が真値をよく回復しました——price 0.856-0.856(真 0.8-0.8)、quality 1.5561.556(真 1.51.5)。符号も正しく、価格係数は負(高いほど選ばれにくい)、品質係数は正(良いほど選ばれやすい)です。係数の比 0.856/1.5560.55-0.856/1.556 \approx -0.55 も真の比 0.8/1.50.53-0.8/1.5 \approx -0.53 に近く、「価格と品質の重み付け」を取り戻せています。4000機会ぶんの argmax 選択というノイズの多い離散データから、最尤推定が連続的な β\beta を推定できているのがポイントです。次に選択シェア。3つの選択肢は属性の分布が対称なので、実シェアはほぼ 1/31/3 ずつ(33.0/32.4/34.7%33.0/32.4/34.7\%)。推定モデルの予測シェア(33.7/33.0/33.3%33.7/33.0/33.3\%)もこれに一致し、モデルが選択行動を再現できていることを確認できます。

推定した係数を使うと、ある選択肢の属性が変わったとき選択確率がどう動くかを描けます。選択肢 B・C を「価格3・品質3」に固定し、選択肢 A の品質を3に保ったまま価格を 11 から 55 へ動かして、A の選択確率を見ます。

import numpy as np
import matplotlib.pyplot as plt
import japanize_matplotlib

# 推定で得た係数(前のブロックの出力)を使い、選択肢の価格に対する選択確率の変化を描く。
beta = np.array([-0.856, 1.556])   # [価格, 品質]

# 選択肢B・Cは価格3・品質3に固定。選択肢Aの品質を3に固定し、価格を1〜5で動かす。
prices = np.linspace(1, 5, 100)
qA = 3.0
VB = beta[0] * 3 + beta[1] * 3
VC = beta[0] * 3 + beta[1] * 3
VA = beta[0] * prices + beta[1] * qA
expA, expB, expC = np.exp(VA), np.exp(VB), np.exp(VC)
PA = expA / (expA + expB + expC)
PB = expB / (expA + expB + expC)
PC = expC / (expA + expB + expC)

fig, ax = plt.subplots(figsize=(7, 4.3))
ax.plot(prices, PA, color="C3", lw=2.2, label="選択肢A(価格が変化)")
ax.plot(prices, PB, color="C0", lw=1.6, ls="--", label="選択肢B(価格3・品質3で固定)")
ax.plot(prices, PC, color="C2", lw=1.6, ls=":", label="選択肢C(価格3・品質3で固定)")
ax.axvline(3.0, color="gray", lw=1.0, alpha=0.6)
ax.set_xlabel("選択肢Aの価格")
ax.set_ylabel("選択確率")
ax.set_title("価格が上がると選択肢Aの選択確率は下がる(MNL)")
ax.legend(loc="center right", fontsize=9)
fig.tight_layout()
plt.show()

3本の曲線が描くのは「個人の選択レベルの需要曲線」です。3つが同条件(価格3・品質3)になる中央では A の選択確率は 1/30.3371/3 \approx 0.337。A の価格が 11 まで下がると A は割安になり選択確率は 0.7350.735 まで上がり、逆に 55 まで上がると 0.0830.083 まで落ちます。価格係数が負であることが、この右下がりの曲線として現れています。集計の需要曲線(需要曲線と価格弾力性)を、ソフトマックスを通じて選択肢間の競争として描き直したものだと捉えてください——A が下がった分は B・C から奪っています。

⚠️ よくある誤解

関連ノート