混合 Logit:随机系数、仿真积分与 MSLE
为什么 MNL 的 IIA 必须放松?如何让系数 $\beta_i$ 随个体随机?写出混合选择概率的积分形式、用 Halton 序列做仿真近似、用极大模拟似然(MSLE)估计,并用贝叶斯 posterior 反推每个个体的偏好。它就是 BLP 的微观版本。
numpy + scipy,需自行实现 Halton 抽样)或 Stata(mixlogit、mixlogitwtp)。01 为什么需要 Mixed Logit
MNL 有两个"硬伤",上一页已经点过:
- IIA 过强:任意两备选的相对份额只取决于二者效用差,与其他备选无关。红巴士-蓝巴士悖论就是这个假设的直接反例;
- 系数不能异质:所有人对价格的边际效用都一样。现实里,有的人对价格极其敏感(穷人、学生),有的人几乎不在乎(富人)。把这种异质性平均成一个 $\beta$,会严重扭曲福利测算和反事实。
Mixed Logit(又称 Random Parameters Logit, RPL) 的想法很直接:把 $\beta$ 从一个常数改成服从某种分布的随机变量 $\beta_i$,让它在个体间波动。一旦允许 $\beta_i$ 随机,IIA 就被自动放松——因为不同人 $\beta_i$ 不同,相对份额不再是固定比例;同时,偏好异质性被显式建模,可以做"政策对价格敏感者 vs 不敏感者"的异质性分析。Train (2009) 证明:任意形式的离散选择模型都可以被 Mixed Logit 任意逼近——这是它被称为"结构估计工作母机"的原因。
02 模型设定:随机系数 $\beta_i\sim f(\beta|\theta)$
效用 $U_{ij}=x_{ij}'\beta_i+\varepsilon_{ij}$,其中 $\varepsilon_{ij}\overset{iid}{\sim}\mathrm{Gumbel}$(保留 Logit 核的便利),而 $\beta_i$ 是个体 $i$ 特有的随机系数,服从一个由超参数 $\theta$ 控制的分布:
常用设定:
- 正态:$\beta_i\sim N(b,\Sigma)$——最常用,但允许系数符号翻转,价格系数可能"对富人变正";
- 对数正态:$\log\beta_i\sim N(b,\sigma^2)$——适合"必然为正"的系数(如价格的负效用取 $-\exp(\cdot)$);
- 三角形 / 均匀:Train 推荐,便于在有限区间内估计异质性,数值上更稳。
$\theta=(b,\Sigma)$ 是我们要估计的总体参数。注意:$\Sigma$ 不是扰动方差,它是偏好异质性的方差,和 Gumbel 扰动完全是两回事。
03 混合选择概率:积分形式
对给定的 $\beta_i=\beta$,个体选 $j$ 的条件概率就是 MNL:
但 $\beta_i$ 本身是随机的,所以无条件(marginal)选择概率要对 $f(\beta|\theta)$ 积分:
这个积分一般没有解析解(除非 $f$ 退化到常数,退化为 MNL),必须数值/仿真积分。这就是 Mixed Logit 计算上比 MNL 贵一到两个数量级的根本原因。
04 仿真近似:Halton 与伪随机数
用 $R$ 个从 $f(\beta|\theta)$ 抽出的模拟 draws $\{\beta^1,\dots,\beta^R\}$,把积分换成蒙特卡洛平均:
抽样方法的选择直接决定效率:
- 伪随机数(pseudo-random, i.i.d. uniform + inverse CDF):简单但 $R$ 要大(通常 1000+)才能把积分做准;
- Halton 序列:准蒙特卡洛(quasi-Monte Carlo),用素数基生成低差异(low-discrepancy)序列,在单位立方体上比 i.i.d. 均匀得多。$R=100$ 就能达到 i.i.d. $R=1000$ 的精度。这是 Train 推荐的工业标准。
对维度 $d$,选第 $d$ 个素数 $p_d=2,3,5,7,\dots$,把区间 $[0,1]$ 按 $p_d$ 进制递归分割,取分割点序列。它在每一维上都"不重不漏、均匀铺开",避免伪随机数经常出现的聚集/空洞。
变量定义:$\sigma^2=\text{Var}_f[L_{ij}(\beta)]$;$d$ 为维数。设定理由:i.i.d. 蒙特卡洛收敛率 $O(R^{-1/2})$,Halton 准蒙特卡洛收敛率 $O(R^{-1}(\log R)^d)$,快一个数量级。经济直觉:低差异序列把"采样点"均匀铺在积分域上,避免随机聚簇造成的系统性误差,这是 Mixed Logit 从"学术玩具"变"工业工具"的关键。
05 极大模拟似然 MSLE
MSLE 就是用模拟把混合选择概率近似出来后的极大似然估计。不熟悉 MLE 的似然构造、数值优化与标准误?先学 基础知识库·MLE →
把 $\hat P_{ij}$ 代回似然函数,得到极大模拟似然估计(Maximum Simulated Likelihood, MSLE):
关键性质(Train 2009 证明):
- 模拟误差 $\hat P-P$ 的阶是 $O(1/\sqrt R)$,当 $R\to\infty$ 时 MSLE 收敛到 MLE;
- 对数似然对 $\theta$ 连续且可导(只要 draws 不随 $\theta$ 变,用 Halton 时固定随机种子即可),可以用拟牛顿(BFGS);
- 偏差来源:$\log\hat P$ 不是无偏的(因为 $\log$ 凹),所以 $R$ 太小会系统性高估对数似然。实务建议 $R\ge 100$(Halton)或 $R\ge 1000$(伪随机)。
5.1 MSLE 的仿真得分与渐近分布(补全)
直接对仿真目标函数求导即可得仿真得分(注意 draws $\beta^r$ 不随 $\theta$ 变,否则得分有偏):
变量定义:$L_{ij}(\beta^r)$ 为第 $r$ 次 draw 处的条件 MNL 概率;$\hat P_{ij}$ 为仿真平均概率。当 $\beta\sim N(b,\Sigma)$ 时,$\partial\log f/\partial b=\Sigma^{-1}(\beta-b)$、$\partial\log f/\partial\Sigma$ 为 Wishart 得分。渐近分布:$\sqrt N(\hat\theta_{MSLE}-\theta_0)\xrightarrow{d}N(0,H^{-1}G H^{-1})$,$G$ 为仿真得分外积,$H$ 为 Hessian——因仿真引入额外噪声,$G\ne -H$,必须用 sandwich 形式。
变量定义:$\bar\beta_k$ 为基准系数。识别条件:(a) 至少有一个非随机系数,否则整体尺度不可识别;(b) 分布形式须事先设定(Train 证明 Normal/Log-normal 对二阶矩识别等价,但符号假设影响极大);(c) Halton draws 须固定且不随 $\theta$ 变。经济直觉:与 MNL 一样,效用只在"差"上有意义,须钉住一个锚。
06 个体偏好:posterior expectation
MSLE 只给了总体分布 $\theta=(b,\Sigma)$。但结构估计常被用来做"个体层面的反事实"——比如预测"这位消费者如果价格涨 1 元会买什么"。这需要把 $\beta_i$ 的后验期望反推出来:
仿真实现:用同一组 draws $\beta^r$,把分母换成 $\frac1R\sum_r L_i(\beta^r)$,分子换成 $\frac1R\sum_r \beta^r L_i(\beta^r)$,做加权平均。这一步本质上就是贝叶斯后验均值(以 $f(\beta|\hat\theta)$ 为先验),常用于做"个性化推荐 / 价格歧视反事实"。
07 与 BLP 的关系
BLP(Berry, Levinsohn & Pakes 1995,下一页)和 Mixed Logit 是同一枚硬币的两面:
| 维度 | Mixed Logit (Train) | BLP |
|---|---|---|
| 数据 | 个体级 micro 数据(每个个体的选择) | 市场级 aggregate 份额数据 |
| 估计方法 | MSLE(嵌套在 simulation 里) | BLP 嵌套:内层 invert share 得 $\delta_{jt}$,外层 GMM 求 $\theta$ |
| 随机系数 | $\beta_i\sim N(b,\Sigma)$ | 同样 $(\beta,\Sigma)$,只是用微观 moment 识别 |
| 内生性 | 假设外生 | 用 BLP instruments 处理价格内生 |
| 后验偏好 | 容易,直接 posterior | 需要从 aggregate 份额反推 |
学完 Mixed Logit 再学 BLP,就会发现 BLP 本质上是"把 MSLE 换成 GMM、把个体数据换成聚合份额、加上价格内生性修正"。反过来,掌握 BLP 后再回来看 Mixed Logit,会觉得它"简单太多"。
08 逐步估计流程
09 完整 Python 代码(含 Halton 与 MSLE)
"""
混合 Logit(Random Parameters Logit)完整实现
- Halton 准蒙特卡洛抽样
- 仿真选择概率 P_ij = (1/R) sum_r L_ij(beta^r)
- 极大模拟似然 MSLE
- 个体偏好后验期望 E[beta_i | y_i, X]
依赖:pip install numpy scipy pandas
"""
import numpy as np
from scipy.stats import norm
from scipy.optimize import minimize
# =========================================================
# 1. Halton 序列生成器(准蒙特卡洛低差异抽样)
# =========================================================
def halton(dim: int, n_sample: int, leap: int = 1):
"""
生成 dim 维 Halton 序列,返回 shape=(n_sample, dim),值域 [0,1)
dim: 随机系数的维度(用前 dim 个素数作基)
"""
# 前 20 个素数,足够覆盖 dim<=20
primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29,
31, 37, 41, 43, 47, 53, 59, 61, 67, 71]
assert dim <= len(primes), "维度超出预设素数表"
H = np.zeros((n_sample, dim))
for d in range(dim):
p = primes[d]
# van der Corput 一维序列
n, base_inv = 0, 1.0 / p
while n_sample >= (p ** n):
n += 1
seq = np.zeros(n_sample)
for i in range(1, n_sample + 1):
u = i * leap
f, denom = 0.0, 1.0
while u > 0:
f += (u % p) / denom
u //= p
denom *= p
seq[i - 1] = f
H[:, d] = seq
return H
def draws_normal(mean, std, R, dim, seed=0):
"""把 [0,1) 上的 Halton 反演为 N(mean, std^2) draws"""
H = halton(dim, R)
Z = norm.ppf(np.clip(H, 1e-6, 1 - 1e-6)) # 反 CDF
return mean + std * Z # (R, dim)
# =========================================================
# 2. 模拟数据:N 个个体,J=3 个备选,K=2 个随机系数
# =========================================================
np.random.seed(2026)
N, J, K = 2000, 3, 2
# 备选属性 X[i, j, k]:价格(越负越好)+ 品质
X = np.random.normal(0, 1, (N, J, K))
# 真实 beta_i 服从 N(mu, Sigma)
mu_true = np.array([-1.5, 1.0]) # 价格为负效用
sig_true = np.array([0.5, 0.7]) # 异质性标准差
beta_true = mu_true + sig_true * np.random.randn(N, K)
# 生成选择:logit 核
def choice_from_beta(X, beta):
"""X:(N,J,K), beta:(N,K) -> y:(N,)"""
V = np.einsum('njk,nk->nj', X, beta) # (N,J)
V -= V.max(axis=1, keepdims=True) # log-sum-exp 数值稳定
P = np.exp(V)
P /= P.sum(axis=1, keepdims=True)
y = np.array([np.random.choice(J, p=P[i]) for i in range(N)])
return y
y = choice_from_beta(X, beta_true)
# =========================================================
# 3. ★ 仿真选择概率(MSLE 的核心)
# =========================================================
R = 100 # Halton 抽样数
halton_base = halton(K, R) # 固定 draws,避免目标函数抖动
def simulated_choice_probs(params):
"""
params: 长度 2K,前 K 是均值 mu,后 K 是 log(std)(保证 std>0)
返回 P_hat: (N, J)
"""
mu = params[:K]
std = np.exp(params[K:]) # 保证正定
# 把 Halton [0,1) 反演为正态 draws: (R, K)
Z = norm.ppf(np.clip(halton_base, 1e-6, 1 - 1e-6))
draws = mu + std * Z # (R, K)
# 对每个 draw r 算 MNL 概率,再对 r 平均
P = np.zeros((N, J))
for r in range(R):
V = X @ draws[r] # (N, J)
V -= V.max(axis=1, keepdims=True)
p_r = np.exp(V)
p_r /= p_r.sum(axis=1, keepdims=True)
P += p_r
P /= R
return P
# =========================================================
# 4. ★ 极大模拟似然(MSLE)目标函数
# =========================================================
def neg_loglik(params):
P = simulated_choice_probs(params)
P = np.clip(P, 1e-12, 1 - 1e-12)
# 选中那一行的概率
chosen = P[np.arange(N), y]
return -np.sum(np.log(chosen))
# =========================================================
# 5. 优化:从 MNL 系数起步
# =========================================================
# 起步:普通 Logit 的均值估计(用全部数据压扁成 (N*J, K))
Xflat = X.reshape(N * J, K)
yflat = (np.tile(np.arange(J), (N, 1)).reshape(-1) == np.repeat(y, J)).astype(float)
# 简单 OLS 起步
beta_start = np.linalg.lstsq(Xflat, yflat - yflat.mean(), rcond=None)[0]
params0 = np.concatenate([beta_start, np.log(np.ones(K) * 0.5)])
res = minimize(neg_loglik, params0, method='BFGS',
options={'maxiter': 200, 'gtol': 1e-5})
mu_hat = res.x[:K]
std_hat = np.exp(res.x[K:])
print("=== MSLE 估计 ===")
print(f"mu 真值={mu_true}, 估计={mu_hat.round(3)}")
print(f"std 真值={sig_true}, 估计={std_hat.round(3)}")
print(f"对数似然 = {-res.fun:.1f}")
# =========================================================
# 6. ★ 个体偏好后验期望(posterior expectation)
# =========================================================
def posterior_beta_i(i, params):
"""对个体 i,用其观测 y_i 做贝叶斯加权平均"""
mu = params[:K]; std = np.exp(params[K:])
Z = norm.ppf(np.clip(halton_base, 1e-6, 1 - 1e-6))
draws = mu + std * Z # (R, K)
weights = np.zeros(R)
for r in range(R):
V = X[i] @ draws[r] # (J,)
V -= V.max()
p_r = np.exp(V); p_r /= p_r.sum()
weights[r] = p_r[y[i]] # 选 y_i 那一行的似然
weights /= weights.sum()
# 加权平均:E[beta | y_i] = sum_r beta^r * w_r
return weights @ draws
# 抽查前 5 个个体的后验 vs 真值
print("\n=== 个体偏好后验 vs 真值(前 5 人)===")
for i in range(5):
pb = posterior_beta_i(i, res.x)
print(f"个体 {i}: 后验={pb.round(2)} 真值={beta_true[i].round(2)}")
所有 softmax 都先减去 $\max V$ 再取 $\exp$,避免上溢;$\log \hat P$ 前 clip 到 $[10^{-12},1-10^{-12}]$;Halton 反 CDF 前对 $H$ clip,防止 $0/1$ 处 $\Phi^{-1}=\pm\infty$。这三点是 Mixed Logit 代码能跑通的"生存手册"。
09+ 数据来源对照表(混合 Logit 常用数据集)
混合 Logit 需要个体级的选择数据(每个个体在多个备选之间选了哪个),且最好有重复选择/面板(同一个人多次选择)来识别随机系数。下表对照经典与中国数据集:
| 数据集 | 国家/地区 | 典型选择场景 | 为何适合 Mixed Logit | 获取 |
|---|---|---|---|---|
| Train 电动车实验数据 | 美国 | 电动车属性选择(Range/Price/Accel) | Scarlet 经典,识别随机系数 | Train 个人主页公开 |
| NHTS/CHTS | 美国/加州 | 交通方式选择 | 大样本、个体级、重复选择 | NHTS 公网免费 |
| SP 问卷实验 | — | 属性陈述选择(conjoint) | 研究者自行设计,控制属性 | 自行发放/问卷调查 |
| CFPS | 中国 | 养老/教育/职业多次选择 | 面板、个体级,可估随机偏好 | 中国家庭追踪调查 |
| CHARLS | 中国 | 退休路径、健康险选择 | 老年群体重复选择 | 中国健康与养老追踪调查 |
| CHFS | 中国 | 金融产品选择 | 家庭层面风险偏好异质 | 中国家庭金融调查 |
Mixed Logit 用个体级 micro 数据(每人每次选择);BLP 用市场级 aggregate 份额。没有个体级选择数据时,不要硬套 Mixed Logit——那就该走 BLP 路线。Train 的电动车 SP 数据是 Mixed Logit 复现的入门基准。
10 论文案例
11 常见错误与进阶资料
$R=20$ 时仿真噪声盖住真实信号,优化器会"找不到"方差参数。Halton 至少 $R=100$,伪随机至少 $R=1000$;且每次迭代固定同一组 draws,否则目标函数抖动。
截距、品牌 dummy 这些"备选特有"系数若也设随机,$\Sigma$ 会过参数化、识别失败。一般只让关键属性(价格、核心质量)随机。
必须把 $\Sigma$ 参数化为 $\exp(\cdot)$ 或 Cholesky $LL'$,否则优化器会跑出负方差。代码里用 std = exp(log_std) 就是这个道理。
$\exp(1000)$ 在 float 下溢为 0 或 inf。务必先减 $\max V$ 再 softmax,否则第一轮迭代就 nan。
进阶资料(真实 URL)
- Train 教材在线版(Berkeley):
https://eml.berkeley.edu/books/train2/ - Python pylogit 包(工业实现):
https://github.com/pylogit/pylogit - Stasb mixlogit 命令(Hole 2007):
https://www.stata-journal.com/article.html?article=st0132 - Halton 序列综述(Bhat 2003):
https://www.sciencedirect.com/science/article/pii/S0191261502000982
方程总清单 / Equation Summary
本页全部方程按出现顺序汇总如下,共 9 个。每个方程均可在正文中找到对应的变量定义、设定理由与经济直觉。
| 编号 | 方程名称 | 核心公式 | 所在节 |
|---|---|---|---|
| Eq.09-01 | 随机系数分布设定 | $\beta_i\sim f(\beta\mid\theta)$,如 $N(b,\Sigma)$ | 02 |
| Eq.09-02 | 条件 MNL 概率 | $L_{ij}(\beta)=e^{x_{ij}'\beta}/\sum_k e^{x_{ik}'\beta}$ | 03 |
| Eq.09-03 | 混合选择概率(积分) | $P_{ij}(\theta)=\int L_{ij}(\beta)f(\beta\mid\theta)d\beta$ | 03 |
| Eq.09-04 | 仿真选择概率(蒙特卡洛) | $\hat P_{ij}=\frac1R\sum_r L_{ij}(\beta^r)$ | 04 |
| Eq.09-05 | 仿真方差与 Halton 收敛率(补全) | Var(伪随机)=$\sigma^2/R$;Var(Halton)=$O((\log R)^d/R^2)$ | 04(补全) |
| Eq.09-06 | MSLE 目标函数 | $\hat\theta=\arg\max_\theta\sum_i\sum_j\mathbf{1}\{y_i=j\}\log\hat P_{ij}(\theta)$ | 05 |
| Eq.09-07 | MSLE 仿真得分(补全) | $\hat s(\theta)=\sum_i \hat P_{i,j_i^*}^{-1}\cdot\frac1R\sum_r L_{i,j_i^*}(\beta^r)\partial\log L/\partial\theta$ | 05.1(补全) |
| Eq.09-08 | 识别条件与尺度归一化(补全) | 固定一个系数为确定性;固定扰动方差 | 05.1(补全) |
| Eq.09-09 | 个体偏好后验期望 | $\hat\beta_i=\int\beta L_i(\beta)f(\beta\mid\hat\theta)d\beta\big/\int L_i(\beta)f(\beta\mid\hat\theta)d\beta$ | 06 |