欢迎光临寒舍

结构估计实操攻略(初级)

2392 字

结构估计经常让新人感到恐惧。一会是 OLS,一会是 MLE,一会是 GMM,一会又是 Indirect Inference。其实,这背后的逻辑非常简单。

结构模型参数估计与识别:小白实战攻略

想象你是一个侦探,你要还原案件真相(模型参数),你手头有不同的证据(数据)。

  • 有的证据直接写在脸上(比如指纹),直接读就行(直接估计)。
  • 有的证据是间接的(比如作案动机),你需要通过行为反推(结构估计)。

这份攻略将教你如何像侦探一样思考。

第一阶段:给参数“分成分” (Parameter Taxonomy)

拿到一个模型,面对几十个希腊字母,不要慌。先把它们分成两类。这是选择估计方法的第一步。

如何判断一个参数是"结构参数"还是"非结构参数"?

黄金法则:问自己"改变这个参数,代理人的决策规则是否改变?"

  • 改变 → 结构参数(深层偏好)
  • 不改变 → 非结构参数(技术系数或模型便宜)

例子:

  • $\sigma$(风险厌恶)→ 改变 → 储蓄决策改变 → 结构
  • $\alpha_0^j$(选项固定效用)→ 改变 → 选择概率改变 → 结构
  • VAR 回归系数 → 改变 → 脉冲响应改变,但不改变 DSGE 代理人决策 → 非结构

第 1 类:物理、技术与制度参数 (The “Hard” Parameters)

这类参数通常描述的是客观约束生理规律市场技术。它们不涉及人的主观选择(或者我们假设在控制了异质性后,它们是外生的)。

  • 特征: 如果你是上帝,你可以直接在数据里看到它们,或者通过简单的统计看到它们。
  • 典型例子:
    • 生产函数/工资方程 ($\beta$): 投入多少资本产出多少产品,或者读多少书拿多少工资。
    • 生理/物理法则 ($\eta$): 压力大是否伤身体?年龄大是否更容易生病?
    • 外生冲击 ($d$): 某种不可抗力(如工厂倒闭、甚至死亡)发生的概率。
    • 制度参数: 税率、利息(通常直接查文献或法条)。
  • Tips: 能直接算,绝不模拟!

第 2 类:偏好与摩擦参数 (The “Soft” / “Deep” Parameters)

这类参数描述的是人的内心世界市场的隐形障碍。它们看不见摸不着,只能通过人的行为后果来反推。

  • 特征: 数据里没有直接记录它们。你只能看到“结果”(Outcome),看不到“动因”(Driver)。
  • 典型例子:
    • 效用/痛苦成本 ($\kappa$): 工作有多累?失业有多爽?(没人会把这个写在脸上)。
    • 搜寻摩擦 ($\lambda$): 找工作有多难?(数据里只有“找到工作”的时间,没有“尝试找工作”的次数)。
    • 折扣因子 ($\rho$): 人有多不耐烦?(涉及到人的耐心时才需要估计,如果仅仅是贴现,那用经验数据即可。)
  • 攻略: 必须结构估计 (MLE / GMM / CCP / SMM)。详见 方法比较 | 如何选择

第二阶段:选择兵器——估计方法决策树 (Method Decision Tree)

面对具体的参数,按以下流程图提问,直到找到答案:

Q1: 这个变量可以直接观测吗?

  • YES (例如:此人是否被裁员?此人工资是多少?)
    • $\rightarrow$ 直接统计 / OLS

Q2: 如果不可观测,控制了异质性后,它是一个简单的概率过程吗?

  • YES (例如:健康演变 $h_{t+1} | h_t, x$)
    • $\rightarrow$ 最大似然估计 (MLE) (永远的第一选择
    • 逻辑:只要写得出似然函数 $L(\theta) = \Pi \ P(Data|\theta)$,且计算不复杂,MLE 是最有效的。
    • 原则:也就是说,只要你的数据和模型够干净,能直接推导出观测数据发生的概率密度函数 $f(Data|\theta)$,且状态空间没有大到你的电脑配置承受不了的地步,就一定优先选择 MLE 这个大杀器,因为它是统计效率最高且最准确的。
    • 场景 (模型选择):
      • 离散选择模型:Logit/Probit 及其变体。
      • 动态规划模型:通过嵌套不动点算法 (NFXP) 快速求解动态规划模型(如 Rust, 1987)。
    • 例子:J&P(2025) 中,控制 $x$ 后,用 Ordered Logit 估计健康转移。

Q3: 似然函数写不出来,或者太复杂怎么办?

  • 场景:数据缺失(看不到被拒绝的 Offer)、多重积分(连续状态空间)、模型是非线性的。
  • 当我写不出整个分布函数,但我知道理论上某些变量的期望应该是多少(能找到明确的矩条件,后文会教你如何寻找矩条件)时,首先考虑 GMM。
  • 最典型的例子就是欧拉方程估计。不用解出整个价值函数,只要满足边际条件即可。
  • 而且对假设不敏感,不需要向 MLE 那样严格要求误差项的分布
  • 还比 MLE 快,对于计算量大的数据更友好,不吃配置
  • 但是样本有限时,精度不太行,毕竟是矩估计,很依赖大数定律

Q4: 矩也写不出来怎么办 or 矩函数没有解析解?

  • 没有明确形式和解析解,那就只能暴力模拟了,别无他法。
  • 或者数据太差,有缺失,或者参数本身在现实中就是不可能找到数据的(如 J&P 2025 中看不到被拒绝的 Offer)。
  • 思路:只要我的模型生成的“假数据”和“真数据”长得像,参数就是对的。
  • $\rightarrow$ 基于模拟的方法
    • SMM (Simulated Method of Moments): 匹配均值、方差、相关系数
      • 适用: 关注数据的一阶、二阶矩特征。
    • Indirect Inference (间接推断): 匹配辅助回归模型的系数
      • 适用: 关注变量之间的动态关系或条件相关性(例如:工资随年龄增长的斜率)。
      • 例子: J&P(2025) 中,模拟工人职业生涯,匹配跳槽率和失业持续时间。
    • 优点: 只要你能写代码模拟,什么模型都能估。
    • 缺点: 计算量大,参数标准误较难计算。

Q5: 我实在不会构造似然函数,也不会设计“矩”,有没有弯道超车、曲线救国的办法?

有的,兄弟,有的!

只要你的数据足够好,足够大,就可以反其道而行之。详见 CCP 方法

采用逆向思维。既然“价值决定选择”,那么“观测到的选择概率”就包含了“价值”的信息。利用数据中观测到的 CCP(比如某状态下的辞职率),直接反推价值函数的差值,从而跳过其它步骤。

所以,理论上只要你能通过第一步非参估计(数数),算出每个状态下的转移概率,并且你的数据可以怼上这些概率,就可以用这个方法。

Brilliant!

  • 又快又好
  • 但吃数据

Step-by-Step

所以,当你建好模型和获得数据以后,先要问自己一些问题:

1. 能不能写出你的电脑配置能算出来的似然函数(MLE)
2. 不能就想办法推导出清晰的欧拉方程或解析矩(GMM)
3. 倘若还不能,而且你的模型很烂,有大量的非线性和断点,或者需要做大量搜寻工作来坍缩不可观测的状态(如失业搜寻模型),那就只能模拟了(SMM / Indirect Inference)
(J&P 2025 选择了 SMM,正是因为加入了搜寻摩擦和不可观测的状态,使得 MLE 和 CCP 都难以直接实施。)
4. 如果你的数据特别好,特别大(比如 N>100,000),那就可以考虑 CCP 方法,直接从数据里学价值函数。

前方高能

识别的艺术——如何找“矩” (The Art of Identification)

其实大多数情况,我们获得的数据不好也不差,模型还凑合,都能使用矩估计,但这也是最容易卡住的地方:“我怎么知道哪个数据矩能识别哪个参数?”

我先问大家一个问题,什么是矩?估计很多人答不上来吧……

矩 ── 就是一个应该等于零的数学式子。

因为这个式子的零点恰好出现在"真实参数"处,所以可以用来估计真实参数。比如:真实的消费者对价格敏感度是 $β = -0.1$,如果我算出某个矩条件 $m(β)$,那么 $m(-0.1) = 0$ ← 神奇吧!

矩估计就是:找参数$β$使得 $m(β) = 0$。

从过往经验来说,矩无外乎来自于以下几种来源:

  1. 经济理论的一阶条件,比如企业利润最大化:$∂π/∂P = 0$,这就是一个矩条件

  2. 统计假设:

    1. 比如 OLS 中假设随机误差与观测变量或工具变量无关,这导出 $E[X'·ε] = 0$ or $E[Z'·ε] = 0$ 这个矩条件
    2. Logit 模型 $P_j = \exp(V_{ij}) / \sum_k \exp(V_{ik})$,写出对数似然
    $$L(\beta) = \sum_i \sum_j y_{ij} \cdot \log(P_{ij}(\beta))$$

    其中

    $$y_{ij} = \begin{cases} 1 & \text{if 消费者 i 选 j} \\ 0 & \text{otherwise} \end{cases}$$

    矩就是似然函数的一阶条件,导数为 0。

  3. 模型的内在约束,比如市场份额必须和为 1,或者消费者的选择概率和为 1,矩就是$\sum p_i - 1 = 0$

  4. 市场份额

  5. 现在很多面板数据的因果推断也上科技与狠活了,你就记住,面板数据,差分是王道。所以,固定效应的矩是$m(β) = E[ΔX_it(ΔY_it - ΔX_it'β)] = 0$

  6. 涉及到时间维度就会有动态模型,这就是很多人只听说过却不知道是啥的,大名鼎鼎的 Arellano-Bond 矩条件,简称 AB 矩。原理很简单,就是滞后水平作为 IV,矩为 $E[Y_i,t-2(ΔY_it - ΔX_it'β - γ·ΔY_i,t-1)] = 0$。

  7. 还有一种特殊的动态模型是结构性动态模型,即消费者有前瞻性,会考虑未来价格做决策。矩条件是欧拉方程

  8. 分位数回归的矩是

    $$m_p(\beta) = E[(p - \mathbf{1}_{Y < X'\beta}) X] = 0$$
  9. 样本选择修正(Heckman)的矩条件

  10. 量化矩的矩条件

  11. 位置选择模型(spatial)的矩条件

  12. 网络效应模型的矩条件

  13. 不完全信息博弈的矩条件,比如企业不知道对手的成本,矩条件通常选择 E[观测价格 - 均衡价格预测] = 0


结构模型如何找矩呢?

核心心法:单调性原则

好比你是个 DJ。如果你拧动旋钮 A(参数),音响的某个指标 B(数据矩)发生了剧烈变化,而且是单向有规律地变化,那么 B 就识别 A。

比如音量旋钮,顺时针拧就是音量增大,逆时针就是减小,不会忽大忽小不确定。

为了能让你在拿到一个题目时,马上反应出来该用什么矩,我将经济学中常见的参数分为四大门派,并列出了它们的本命矩,帮助你产生条件反射。

门派一:偏好与痛苦 (Preferences & Costs)

描述人的内心世界:怕不怕累?有没有耐心?喜不喜欢孩子?

参数 (Parameter) 经济学含义 识别它的矩 (The Moment) 顿悟逻辑 (Eureka !)
工作负效用 ($\kappa$) 工作有多累? 辞职率 / 劳动参与率 如果工作超级累($\kappa \to \infty$),没人会干活,或者一有机会就辞职。
风险厌恶 ($\gamma$) 胆子有多小? 保险购买率 / 风险资产占比 如果我胆子极小($\gamma \to \infty$),我会买满保险,股票仓位为 0。
跨期折现因子 ($\beta$) 有多看重未来? 储蓄率 / 财富积累速度 / 消费增长率 如果我只活在当下($\beta \to 0$),我是“月光族”,资产为 0。如果我极度耐心,我会大量储蓄。
闲暇替代弹性 ($\eta$) 税高了就不干了吗? 工时对税率变动的反应 如果我对工资很敏感,政府一加税,我就大幅减少工作时间。
遗赠动机 ($\phi$) 想给孩子留钱吗? 临终前的资产持有量 如果不关心孩子($\phi=0$),死前我会把钱花光。如果死时还剩一大笔钱,说明有遗赠动机。

门派二:摩擦与障碍 (Frictions & Costs)

描述市场的流动性:路好不好走?有没有坑?

参数 (Parameter) 经济学含义 识别它的矩 (The Moment) 顿悟逻辑 (Eureka !)
搜寻摩擦 ($\lambda$) 找对象/工作有多难? 持续时间 (Duration) 如果满大街都是 Offer($\lambda \to \infty$),失业时间就是 0。失业越久,说明摩擦越大。
调整成本 ($C_{adj}$) 改投资计划有多贵? “不作为”区间的宽度 (Inaction Rate) 如果调整机器设备很贵,只要冲击不大,我就不动。如果数据里很多企业常年投资为 0,偶尔巨大跳跃,说明调整成本极高。
固定成本 ($F$) 进场门票有多贵? 在 0 处的堆积 (Bunching at zero) 如果上班有固定成本(如通勤、买西装),没人会只上 10 分钟班。大家要么不上(0 小时),要么上 8 小时。数据分布在 0 附近有个大洞。
借贷约束 ($\bar{b}$) 能借多少钱? 消费对收入冲击的敏感度 如果我可以随便借钱,我丢了工作照样吃龙虾(消费平滑)。如果你发现我很穷时一丢工作就吃泡面,说明我有借贷约束。

门派三:技术与生产 (Technology & Production)

描述硬约束:投入产出关系是怎样的?

参数 (Parameter) 经济学含义 识别它的矩 (The Moment) 顿悟逻辑 (Eureka !)
替代弹性 ($\sigma$) 机器能替代人吗? 要素价格比 vs 要素份额比 当工资上涨 10% 时,如果企业雇佣量暴跌 20%,说明机器很容易替代人($\sigma$ 大)。如果雇佣量几乎不变,说明很难替代。
规模报酬 ($RTS$) 做大更划算吗? 企业规模分布 / 利润率随规模变化 如果做大很划算,市场上应该全是巨头。如果全是小微企业,说明规模报酬递减。
议价能力 ($\alpha$) 分蛋糕谁更强? 工资对利润冲击的反应 公司突然赚了一笔横财(Shock),分给员工多少?如果分得多,说明工会/员工议价能力强。

门派四:信息与误差 (Information & Shocks)

描述能不能看清未来?

参数 (Parameter) 经济学含义 识别它的矩 (The Moment) 顿悟逻辑 (Eureka !)
测量误差方差 ($\sigma_{me}$) 数据有多脏? 两个独立数据源的差异 问卷里问“你赚多少”,税单里查“你赚多少”。两者的差值方差,识别测量误差。
预见能力 (Info Set) 知道多少内幕? 当前选择与未来结果的相关性 如果我今天买了某支冷门股票,明天它暴涨了,且统计上显著,说明我拥有“预见能力”(信息集包含未来冲击)。
冲击的持久性 ($\rho$) 坏运会持续多久? 变量的自相关系数 (Autocorrelation) 如果今年穷,明年大概率还穷,说明冲击很持久($\rho \to 1$)。

似然函数的构造艺术 (Deep Dive 2: The Art of Likelihood Construction)

如果你选择用 MLE,你不需要像上面那样去“找矩”,你需要的是讲故事

似然函数 $L(\theta)$ 的核心问题只有一个: “基于我的模型参数 $\theta$,观测到眼前这个数据 $y_i$ 的概率是多少?”

$$\max_\theta \sum_i \log \underbrace{P(y_i | x_i, \theta)}_{\text{写出这项,你就赢了}}$$

经济学中 99% 的似然函数构造,都逃不出以下五大基本型。掌握了这五个,你就能条件反射般地写出公式。

1. 连续型:正态分布的“钟形曲线” (The Bell Curve Intuition)

  • 场景: 你研究工资 $w_i$,假设工资由教育决定,有随机误差。

  • 顿悟: 数据点 $y_i$ 出现的概率,就是它落在正态分布钟形曲线上的高度。离均值($x_i \beta$)越近,高度越高。

  • 构造逻辑:

    $$ y_i = x_i \beta + \epsilon_i, \quad \epsilon_i \sim N(0, \sigma^2) $$

    $$ P(y_i) = \frac{1}{\sigma} \phi\left( \frac{y_i - x_i \beta}{\sigma} \right) $$
  • 实战代码 (Log-Likelihood):

    1
    2
    
    resid = y - X @ beta
    ll = -0.5 * np.log(2 * np.pi) - np.log(sigma) - (resid**2) / (2 * sigma**2)
    

2. 离散选择型:二选一的“门槛” (The Threshold Intuition)

  • 场景: 你研究人要不要工作 (Work=1, Not=0)。

  • 顿悟: 选择由看不见的“效用差”决定。如果效用差 $U_{work} - U_{home} + \epsilon > 0$,我就工作。概率 = 误差 $\epsilon$ 大于某个门槛的面积。

  • 构造逻辑:

    $$ P(y_i=1) = P(x_i \beta + \epsilon_i > 0) = P(\epsilon_i > -x_i \beta) $$
    • 如果 $\epsilon$ 是正态分布 $\rightarrow$ Probit ($\Phi(x_i \beta)$)
    • 如果 $\epsilon$ 是极值分布 $\rightarrow$ Logit ($\frac{e^{x \beta}}{1+e^{x \beta}}$)

呐,很多同学还搞不清楚 Logit 和 Probit 的区别。

  • 实战代码 (Logit):

    1
    2
    
    p = 1 / (1 + np.exp(-X @ beta))
    ll = y * np.log(p) + (1 - y) * np.log(1 - p)
    

3. 计数型:事件发生的“节奏” (The Arrival Intuition)

  • 场景: 你研究一家公司一年申请多少个专利 (0, 1, 2, 5…)。

  • 顿悟: 这是一个排队到达过程。有一个内在的“发生率” $\lambda$(intensity)。观测到 $k$ 次的概率,就是泊松分布的公式。

  • 构造逻辑:

    $$ \lambda_i = \exp(x_i \beta) \quad (\text{保证发生率为正}) $$

    $$ P(y_i = k) = \frac{e^{-\lambda_i} \lambda_i^k}{k!} $$
  • 实战代码 (Poisson):

    1
    2
    
    lam = np.exp(X @ beta)
    ll = y * np.log(lam) - lam - gammaln(y + 1)
    

4. 截断/归并型:看不见的“尾巴” (The Hidden Tail Intuition)

  • 场景: 你研究家庭医疗支出。很多人是 0(没生病),剩下的人是正数。或者研究工资,但最高工资被设了上限(Top-coded)。

  • 顿悟: 这是一个混合体

    • 看到正数 $y_i > 0$ 时,概率是高度(PDF)。
    • 看到零 $y_i = 0$ 时,概率是面积(CDF)。所有想花钱但没病的人,都堆在了 0 这一点。
  • 构造逻辑 (Tobit):

    $$ P(y_i) = \begin{cases} \frac{1}{\sigma}\phi(\frac{y_i - x\beta}{\sigma}) & \text{if } y_i > 0\\ \ 1 - \Phi(\frac{x\beta}{\sigma}) & \text{if } y_i = 0 \end{cases} $$
  • 实战代码 (Tobit):

    1
    2
    3
    4
    
    # 这里的代码需要写个 if-else 或者用 mask
    ll_pos = -0.5*np.log(2*np.pi) - np.log(sigma) - ((y - X@beta)**2)/(2*sigma**2)
    ll_zero = np.log(1 - norm.cdf(X@beta / sigma))
    ll = np.where(y > 0, ll_pos, ll_zero)
    

5. 结构动态选择型:路径的“接力赛” (The Path Probability Intuition)(难点

  • 场景: 最复杂的结构模型(如 Rust 1987 公交车引擎更换)。

  • 顿悟: 既然一切都是马尔可夫链,那么整条人生轨迹的概率 = 每一步选择概率的乘积 × 每一步状态转移概率的乘积

  • 构造逻辑:

    • 假设观测到一个人在 $t$ 时期状态为 $s_t$,做了选择 $d_t$(换引擎=1,不换=0)。
    • 第一部分(选择概率): 给定状态 $s_t$,他算出 $V(1)$ 和 $V(0)$,算出选择 $d_t$ 的概率 $P(d_t | s_t, \theta)$(通常是 Logit 形式)。
    • 第二部分(转移概率): 选了 $d_t$ 后,状态 $s_t$ 变成 $s_{t+1}$ 的概率 $\pi(s_{t+1} | s_t, d_t)$。
    • 总似然: $$ L(\theta) = \prod*{t=1}^T \underbrace{P(d_t | s_t, \theta)}*{\text{CCP / Logit}} \times \underbrace{\pi(s*{t+1} | s_t, d_t)}*{\text{Transition}} $$
  • 实战代码 (Structural):

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    # 1. 先解动态规划 (DP) 得到价值函数 V
    #    V_diff = V(换) - V(不换)
    
    # 2. 算选择概率 (Logit)
    prob_replace = 1 / (1 + np.exp(-V_diff))
    
    # 3. 构造似然
    # 如果数据里 d=1,概率是 prob_replace
    # 如果数据里 d=0,概率是 1 - prob_replace
    ll = d * np.log(prob_replace) + (1 - d) * np.log(1 - prob_replace)
    # (注:通常还要加上状态转移概率 log(pi))
    

间接推断与辅助模型的构造艺术 (Deep Dive 3: The Art of Indirect Inference)

这是结构估计中最高阶的技巧。当你无法直接写出似然函数(因为缺失数据或模型太复杂),但数据中又有明显的规律时,就用间接推断 (Indirect Inference, II)

核心思想

不要试图直接描绘真理(结构参数),而是去描绘影子的形状。

  • 结构模型是“那个人”。
  • 数据是他在地上的“影子”。
  • 辅助模型(Auxiliary Model)是你画的一幅“速写”。
  • 逻辑: 虽然速写(OLS 回归)不是真人,但如果速写里这人的鼻子很大,那么真人的鼻子一定也很大。我们通过让模型生成的假数据跑出来的回归系数(假鼻子),去匹配真实数据跑出来的回归系数(真鼻子)。

构造辅助模型的“四大流派” (The Four Archetypes)

不管你的研究题目多复杂,辅助模型的设计通常逃不出这四种套路。

场景流派 面对的问题 辅助模型 (Auxiliary Model) 顿悟逻辑 (Intuition)
1. 筛选流(Sorting/Selection) 谁选了谁── 高能力的人是不是选了高压工作? OLS 回归$y = \alpha + \beta_1 x + \beta_2 z$ 即使这个回归是内生的(有偏差),只要模型里的选择机制是对的,模型生成的偏差应该和数据里的偏差一模一样
2. 粘性流(Dynamics/Frictions) 过去如何影响现在── 失业是不是有疤痕效应? 滞后回归 / AR(1)$y_t = \rho y_{t-1} + \epsilon_t$ 如果数据中 $y_t$ 和 $y_{t-1}$ 高度相关($\rho$ 大),说明摩擦很大。模型必须生成同样的粘性 $\rho$。
3. 波动流(Variance/Risk) 不确定性有多大── 收入冲击是瞬时的还是永久的? 残差平方回归$\hat{\epsilon}_t^2 = \gamma_0 + \gamma_1 \hat{\epsilon}_{t-1}^2$ 不要只看均值。如果残差本身有聚集性(波动率聚类),说明风险是时变的。用残差的回归来捕捉这种二阶矩特征。
4. 形状流(Non-linearity) 有什么门槛或断点── 穷人和富人的行为一样吗? 分段回归 / 虚拟变量$y = \beta_{poor} D_{poor} + \beta_{rich} D_{rich}$ 如果数据是非线性的(穷人边际消费倾向高),简单的 OLS 会失败。此时辅助模型要包含平方项或分段项,捕捉这种弯曲的“形状”。

实战案例:Jolivet & Postel-Vinay (2025) 的做法

J&P (2025) 使用了 间接推断 来识别关键参数。让我们看看他们是如何运用上述流派的:

情景 A:识别“健康对工资的影响” (Selection vs Causality)

  • 难题: 我们看到健康好的人工资高。这是因为健康让人工作效率高(因果),还是因为有钱人能通过更好的医疗保持健康(筛选)?
  • 辅助模型: 简单的 Mincer 工资回归 $$ \ln w*{it} = \beta_0 + \beta_1 \cdot \text{Health}*{it} + \beta*2 \cdot \text{Age}*{it} + u\_{it} $$
  • 逻辑: 这个 $\beta_1$ 在真实数据中是正的(比如 0.2)。你怎么得到的 0.2?
    • 如果模型里没有“健康影响生产率”的机制,模拟出来的 $\beta_1$ 可能会很小(仅靠反向因果不够)。
    • 我们强迫模型里的 $\beta_1$ 也要等于 0.2。这迫使模型内部调整“健康-生产率”参数,直到吻合。

情景 B:识别“搜寻摩擦的持续性” (Friction Persistence)

  • 难题: 为什么有些人一直在烂工作里跳不出来?
  • 辅助模型: 状态转移概率矩阵(或 Logit 回归) $$ P(\text{Job}\_{t+1} | \text{Unemp}_t) \quad \text{vs} \quad P(\text{Job}_{t+1} | \text{Job}\_t) $$
  • 逻辑: 这种条件概率捕捉了路径依赖。如果真实数据里,从失业找工作很难(概率低),从在职找工作容易。模型必须生成同样的概率差。

操作代码模板 (Code Template)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 1. 准备真实数据
real_data = pd.read_csv("ukhls.csv")

# 2. 跑辅助回归(真实世界的影子)
# 例如:工资 ~ 年龄 + 健康
aux_model_real = sm.OLS.from_formula("log_wage ~ age + health", data=real_data).fit()
beta_real = aux_model_real.params.values  # 拿到目标系数,比如 [2.5, 0.03, 0.1]

# 3. 定义间接推断的目标函数
def objective_function(struct_params):
    # A. 解结构模型 & 模拟数据
    # 这里生成一个和真实数据一样大小的假数据集
    sim_data = simulate_structural_model(struct_params)

    # B. 跑同样的辅助回归(模型的影子)
    # 注意:公式必须一模一样!
    aux_model_sim = sm.OLS.from_formula("log_wage ~ age + health", data=sim_data).fit()
    beta_sim = aux_model_sim.params.values

    # C. 让影子重合
    # 最小化两个系数向量的距离
    distance = np.sum((beta_real - beta_sim)**2)
    return distance

导师划重点 (Tips)

  1. 辅助模型不需要是对的 (Mis-specification is OK): 你的 OLS 回归可以有遗漏变量偏差,可以有内生性。没关系!只要你的结构模型能生成同样的偏差,你就能识别出参数。
  2. 选矩要“对症下药”: 想估方差参数?辅助模型里必须有残差平方
    • 想估持续性参数?辅助模型里必须有滞后项 ($y_{t-1}$)
    • 想估非线性参数?辅助模型里必须有平方项 ($x^2$)
  3. 不要太复杂: 辅助模型越简单越好(比如 OLS),算得快。不要用另一个复杂的结构模型做辅助模型,那是套娃。

神奇的 CCP 两步法的魔法 (Deep Dive 4: The Magic of CCP / Two-Step Method)

这是对付“维数诅咒”的终极武器。如果你的状态变量很多(例如:年龄 $\times$ 健康 $\times$ 教育 $\times$ 资产),解一遍动态规划(DP)可能要几小时,放进 MLE 的循环里估计参数要跑几年。

CCP (Conditional Choice Probability, Hotz-Miller 1993) 的核心顿悟是:“作弊”

核心思想

通常我们解动态规划是为了算出期望收益价值 $E[V_{future}]$,从而推导出人会选什么的概率 $P(choice)$。

$$ \text{Parameters} \xrightarrow{\text{Solve DP}} E[V_{future}] \xrightarrow{\text{Compare}} P(\text{Choice}) $$

CCP 把它倒过来了

$$ \text{Data } P(\text{Choice}) \xrightarrow{\text{Invert}} E[V_{future}] \xrightarrow{\text{Estimate}} \text{Parameters} $$

直觉: 如果我看到你在 90% 的情况下都选择了“上大学”,即使我不解模型,我也知道对你来说 $E[V_{\text{college}}] - E[V_{\text{work}}]$ 肯定很大。数据里的选择概率,已经包含了未来价值的所有信息!

既然数据告诉你答案了,就不要自己去算了。

适用场景 (When to use?)

只要遇到动态离散选择模型 (Dynamic Discrete Choice),而且你不想解 DP,就用它。

  1. Rust 公交车模型: 什么时候换引擎?
  2. 市场进入/退出游戏: 沃尔玛要不要进入这个城市?(这是 CCP 最常用的地方,因为博弈论模型太难解了)。
  3. 职业选择: 要不要去读博?

关键公式:Hotz-Miller Inversion

假设误差项服从 Type 1 Extreme Value 分布(Logit),那么未来价值差当前选择概率之间有一个极简单的关系:

$$ v(1) - v(0) = \ln(P_1) - \ln(P_0) $$

利用这个,我们可以把复杂的 Bellman 方程重写成一个线性回归

操作两步走 (The Two-Step Recipe)

Step 1: 描述性统计 (First Stage - Reduced Form)

  • 任务: 忘掉你的结构模型。直接看数据。

  • 做法: 估算在每一个状态 $s$ 下,人们做各个选择的概率 $\hat{P}(d|s)$。

    • 如果数据多,直接用频率(数数)。

    • 如果数据少,那就要跑回归来用平滑对抗稀疏,跑一个 Logit/Probit 回归(包含 $s$ 的多项式)。

      比如你的状态 $s$:45 岁,已婚,有房,健康良好。你想找符合这些条件的人里面就业概率是多少,但你的样本只有 2000,根本填不满状态空间,大多数格子是空的。既然找不到完全一样的人,我们就找相似的人。我们假设:概率是状态 $s$ 的平滑函数。即:43 岁的人的选择概率,应该和 42 岁、44 岁的人差不多,不会突变。所以我们要跑一个 logit 回归:

      $$P(d=1|s) = \frac{\exp(f(s))}{1 + \exp(f(s))}$$

      这里的关键在于 $f(s)$ 怎么写。如果我们只写 $f(s) = \beta_0 + \beta_1 \text{Age}$,这就太僵硬了。它假设年龄每增加一岁,概率的变化是单调的。但现实中,职业选择可能随年龄呈“倒 U 型”。所以我们要加入多项式(Polynomials)和交互项(Interactions),需要你自己去试。

      这就是一个简单的 reduced-form 回归,在这个 Logit 回归里,$\beta$ 系数(比如年龄的系数)没有经济学含义,我们也不关心,我们只关心 $\hat{P}$(预测值)准不准,所以这个回归其实就是个拟合,我们用它只是为了在数据稀疏的地方,画出一条平滑的曲线,把那些断裂的点连起来。然后找到一个拟合的特别好的结果 $\hat{P}$,就用它当概率。

  • 产出: 你现在手里有了 $\hat{P}$,它不再是未知的,它是数据

Step 2: 结构估计 (Second Stage - Structural)

  • 任务: 估计效用参数 $\theta$。
  • 做法: 把 Bellman 方程里的 $E[V']$ 替换成 $\hat{P}$ 的函数。
  • 顿悟公式 (Finite Dependence 简化版): 本来的公式(你不用知道怎么来的,你就说从 Bellman 方程来的)是 $$\ln \left( \frac{P_{1t}}{P_{0t}} \right) = \underbrace{u_1(s_t) - u_0(s_t)}_{\text{当前效用差}} + \beta \underbrace{\left( E[V(s_{t+1})|1] - E[V(s_{t+1})|0] \right)}_{\text{未来期望价值差}}$$ 到现在为止,这个公式还是很烦人,因为还有 $E[V]$ 这个未知项(它是 Bellman 方程的解)。这时候我们刚才估计的概率估计值 $\hat{P}$ 就派上用场了,可以我们可以把第三步里那个讨厌的 $E[V(s_{t+1})]$ 换掉了! $$ \ln \left( \frac{P*{1t}}{P*{0t}} \right) = \underbrace{u*1(s_t) - u_0(s_t)}*{\text{当前效用差}} + \beta \underbrace{[\dots \ln \hat{P}_{t+1} \dots]}_{\text{未来价值差 (用 P 替换)}} $$ 看!这变成了一个形如 $Y = X\cdot \theta + \text{Future}(P)$ 的方程。 - 左边 $\ln(P_1/P_0)$:是数据(你可以直接统计辞职率)。 - 右边第一项 $u_1 - u_0$:包含参数 $\theta$(比如工资系数、固定成本)。 - 右边第二项 $\ln \hat{P}_{t+1}$:是数据(未来的辞职率)。 你可以用 OLS 或者简单的最小距离法直接解出 $\theta$。

实战代码模板 (Code Template)

假设我们要估算一个简单的停业/营业模型。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# === Step 1: 估算 CCP (第一步) ===
# 不需要解模型,直接跑 Logit
# 假设决策 decision 取决于状态 state
reduced_form = LogisticRegression().fit(df[['state']], df['decision'])
# 得到每个状态下的预测概率 P_hat
df['P_hat_1'] = reduced_form.predict_proba(df[['state']])[:, 1]
df['P_hat_0'] = 1 - df['P_hat_1']

# === Step 2: 结构估计 (第二步) ===
def structural_loss(theta_guess):
    # theta_guess 包含当前效用函数的参数
    u_1 = theta_guess[0] * df['state'] - theta_guess[1] # 营业利润 - 固定成本
    u_0 = 0 # 停业效用归零

    # 关键:计算 "Implied Value" (未来价值修正项)
    # 根据 Hotz-Miller,未来价值可以用 log(P_hat) 来表示
    # 这里是一个简化示意,实际需要根据 Euler Equation 构造
    future_correction = beta * (np.log(df['P_hat_0'].shift(-1)) - np.log(df['P_hat_1'].shift(-1)))

    # 构造预测的 Log Odds
    predicted_log_odds = (u_1 - u_0) + future_correction

    # 真实数据的 Log Odds
    actual_log_odds = np.log(df['P_hat_1'] / df['P_hat_0'])

    # 最小化两者差距 (Least Squares)
    return np.sum((predicted_log_odds - actual_log_odds)**2)

# 跑!不用解 Bellman,飞快!
opt_res = minimize(structural_loss, x0=[1.0, 0.5])

实例演示

鉴于这部分内容不太好理解,我打算用一个虚构的例子来演示一遍。看好了,我只演示一遍……

场景设定:

  • 主角:老王,公交车司机。
  • 决策:每年年初,老王要决定是 继续工作 (Work, d=1) 还是 退休 (Retire, d=0)
  • 状态变量:年龄 ($t$)健康 ($h$, 1=好, 0=差)
  • 核心权衡:工作有工资($Wage$),但工作很累($Cost$)。退休没钱但舒服。

我们想估计的结构参数 $\theta$ 是:老王觉得工作到底有多累? (即劳动成本函数)。

Step 0: 看看手里的 Excel 表

假设你有 2000 个像老王一样的司机的数据。表长这样:

ID 年龄 (t) 健康 (h) 决策 (d) 备注
101 55 1 (Good) 1 (Work) 身体好,干得动
102 60 0 (Poor) 0 (Retire) 身体差,退休了
103 60 0 (Poor) 1 (Work) 身体差,但还在干(可能是缺钱)

Step 1: 灵活 Logit(第一步)

我们完全不知道老王的效用函数长什么样。我们只关心:在不同年龄和健康下,大家平均来说是怎么选的?

我们跑一个 Logit 回归:$Choice \sim Age + Age^2 + Health + Age \times Health$。

stata 跑完 logit 回归后不要看系数表,直接 predict p_hta pr ,输出预测结果($\hat{P}_{work}$):

状态组合 (State) 预测的工作概率 ($\hat{P}_1$) 预测的退休概率 ($\hat{P}_0$) 直觉解读
55 岁, 健康好 0.95 0.05 大家都还在干
60 岁, 健康好 0.60 0.40 开始有人退休了
60 岁, 健康差 0.20 0.80 大部分人都干不动了
65 岁, 健康差 0.01 0.99 基本全退了

关键点: 这些数字(0.95, 0.20 等)现在就是已知数据了。我们不再把它们当成概率,而是当成“价值的显示”。

Step 2: 结构参数估计(第二步)

现在我们要解出结构参数:劳动成本 $\alpha$。

A. 设定模型

  • 工作效用:$u_1(t, h) = Wage - \alpha \cdot (Age \times (1-Health))$
    • (假设:年纪越大、身体越差,工作越累,$\alpha$ 是那个系数)
  • 退休效用:$u_0(t, h) = 0$ (标准化为 0)

B. 那个“魔法公式” 根据 CCP 原理,当前的选择概率差 = 当前的效用差 + 未来的修正项

$$ \ln \left( \frac{\hat{P}_{1t}}{\hat{P}_{0t}} \right) = \underbrace{u*1 - u_0}*{\text{Wage} - \alpha \cdot \text{Cost}} + \beta \underbrace{[\text{Future Correction}]}\_{\text{已知数据}} $$

移项整理,这就变成了形如 $Y = \text{Wage} - \alpha X + Z$ 的方程:

$$ \underbrace{\ln(\hat{P}_{1t}) - \ln(\hat{P}_{0t}) - \beta Z}_{\text{Dependent Variable (Y)}} = \text{Wage} - \alpha \underbrace{[Age \times (1-Health)]}_{\text{Regressor (X)}} $$

C. 只要做一次减法! 让我们看看 60 岁, 健康差 这一行数据:

  • $\hat{P}_1 = 0.20, \hat{P}_0 = 0.80$
  • 左边 $Y$ (近似) $= \ln(0.2) - \ln(0.8) \approx -1.38$ (忽略未来修正项以简化)。
    • 直觉: 这个负数 (-1.38) 告诉我们,在这个状态下,工作的总价值比退休低很多!
  • 右边 $X$ $= 60 \times (1-0) = 60$。
  • 假设工资 Wage = 100。

方程变成了:

$$ -1.38 = 100 - \alpha \cdot 60 $$

解得:

$$ 60\alpha = 101.38 \implies \alpha \approx 1.69 $$

结论: 我们就这样算出了 $\alpha$!我们不需要解任何 Bellman 方程,不需要模拟 10000 次。我们就用 Logit 算出的概率(0.2 和 0.8),做了一次代数运算,就反推出了老王心里的劳动成本系数。

这就是 CCP 的威力:它把概率视作价格,直接倒推出了成本。

聪明的你,可能马上看出两个问题,首先是你这个 logit 回归也太随意了吧,高次项随意添加,怎么保证拟合优度?怎么保证既没有过拟合也没有欠拟合

简单啊,stata 就用 AIC 和 BIC 指标来判定啊。python 就用 K-Fold 交叉验证。

至此,我们就学完了所有方法。

别急,这些方法都可以套用一个外壳,那就是贝叶斯。当你的数据里有足够的信念信息,就可以选择贝叶斯方法,这部分内容放到中级教程中吧。


第四阶段:小白实战步骤 (Action Plan)

如果你现在要开始做自己的研究,请按这个步骤操作:

  1. 列清单: 把你模型里所有的希腊字母列出来(假设有 10 个)。
  2. 做减法(Fix parameters):
    • 去查文献,把那些大家都公认的参数(如折现因子 $\beta=0.95$,税率 $\tau=0.2$)固定下来,不要去估计,这叫 Calibration。
    • 现在剩下 6 个参数。
  3. 找物理参数(Direct Estimation):
    • 看能不能用 OLS/MLE 直接算出其中 3 个(如生产函数、转移概率)。
    • 现在剩下 3 个最难的“行为参数”(比如 $\kappa, \lambda$)。
  4. 结构估计(Choose your weapon):
    • 根据上面的决策树,判断是写 Likelihood (MLE),还是写矩条件 (GMM),还是写模拟循环 (SMM)。
    • 如果选择了模拟 (SMM/II),在数据里找 3-5 个最敏感的特征(矩)。使用上面的**“识别字典”**来查找灵感。
    • 写代码:
      • Loop 1: 猜 $\kappa, \lambda$。
      • Loop 2: 解模型 / 模拟 10000 个人。
      • Loop 3: 算这 10000 个人的失业时间、辞职率。
      • Loop 4: 和真实数据对比,算距离,调整猜测。

导师划重点 (Tips)

  • 不要过度识别 (Over-identification) 是神话: 虽然理论上矩越多越好,但实际上,矩选得越精准越好。增加无关的矩只会引入噪音。
  • 识别是讲故事: 在论文里写 Estimation 部分时,不要只写数学公式。要用我上面的“侦探逻辑”告诉读者:“为什么我相信 $\lambda$ 是 0.5?因为数据里的平均失业周期是 6 个月,只有 $\lambda=0.5$ 才能生成这个数据。”
  • 先画图: 在跑复杂的 SMM 之前,先把那个矩随着参数变化的图画出来(Comparative Statics)。如果参数变了,矩都不动,说明这个矩选错了,无法识别该参数。

小白实战步骤

如果你现在要开始做自己的研究,请按这个步骤操作:

  1. 列清单: 把你模型里所有的希腊字母列出来(假设有 10 个)。
  2. 做减法(Fix parameters):
    • 去查文献,把那些大家都公认的参数(如折现因子 $\beta=0.95$,税率 $\tau=0.2$)固定下来,不要去估计,这叫校准,就是赵老师口中常说的 Calibration
    • 现在剩下 6 个参数。
  3. 找物理参数(Direct Estimation):
    • 看能不能用 OLS/MLE 直接算出其中 3 个(如生产函数、转移概率)。
    • 现在剩下 3 个最难的“行为参数”(比如 $\kappa, \lambda$)。
  4. 矩估计
    • 见下方.
  5. 如需要做模拟(SMM):
    • 为这 3 个参数,在数据里找 3-5 个最敏感的特征(矩)。
      • 想识别摩擦?找失业时间。
      • 想识别痛苦?找辞职率。
    • 先画图: 在跑复杂的 SMM 之前,先把那个矩随着参数变化的图画出来(Comparative Statics)。如果参数变了,矩都不动,说明这个矩选错了,无法识别该参数。
    • 写代码
      • Loop 1: 猜 $\kappa, \lambda$。
      • Loop 2: 解模型,模拟 10000 个人。
      • Loop 3: 算这 10000 个人的失业时间、辞职率。
      • Loop 4: 和真实数据对比,算距离,调整猜测。

通过一个实际的例子来了解找矩的逻辑

From Intuition to Operation

光看表格里的“顿悟逻辑”是不够的。你需要知道这些逻辑是如何变成代码里的方程的。我们通过两个经典案例,来完成从直觉 $\rightarrow$ 数学 $\rightarrow$ 代码的闭环。

案例 A:搜寻摩擦 ($\lambda$)

你负责估计一个劳动力市场模型,需要知道“找工作有多难”。

Step 1: 直觉 (Intuition)

啥东西单调影响找工作的难度 $\lambda$?$\longrightarrow$ 失业平均持续时间。

如果 $\lambda$ 很大(每天 10 个猎头打你电话),你失业的时间会非常短。

如果 $\lambda$ 很小(一年才有一个面试机会),你会在失业池里待很久。

结论: “失业平均持续时间”这个数据特征,应该能识别 $\lambda$。

Step 2: 建模

Offer 到达率: 设失业者收到 Offer 服从泊松分布,到达率参数为 $\lambda$。

接受率 (Acceptance Probability): 设收到 Offer 后,你接受它的概率为 $\Phi$(通常取决于你的保留工资)。

脱离率 (Hazard Rate): 你每一刻“成功找到工作”的概率是 $h = \lambda \times \Phi$。

持续时间 (Duration): 根据统计学原理,如果脱离率是常数 $h$,那么“失业持续时间” $T$ 服从指数分布。

指数分布?好东西啊,这不矩就送上门了

理论矩公式: 失业持续时间的期望值是:

$$E[T] = \frac{1}{h} = \frac{1}{\lambda \cdot \Phi}$$

你发现保留工资这个东西没人会告诉你,数据里查不到,看来传统的 GMM 不行了,只能上模拟了。

在模拟之前,必要的检验还得做。

Step 3: 识别逻辑检查,又为单调性检查 (Monotonicity Check)

检查单调性: 只要 $\Phi$ 不随着 $\lambda$ 反向剧烈变化,当 $\lambda \uparrow$ 时,分母变大,$E[T] \downarrow$。

结论: 参数 $\lambda$ 和数据矩 $E[T]$ 存在一一对应的单调关系,可以识别!

Step 4: 操作层面,首先确定思路,我们要用理论上模拟估计得出的失业时长去匹配数据里的失业时长,让他们尽可能一样,从而反解出 $\lambda$。 1. 想办法算工人的保留工资,进而得到他们的最优策略,也就是新工作的接受率 2. 有了前提参数,接着马上直奔主题,模拟 10000 个人来估计平均失业时长 3. 从数据里读出真实的平均失业时长 4. 最后把模拟的和数据的失业时长做个距离(方差最小)

(The Code) 在你的代码里,这实际上是这样写的:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
def objective_function(lambda_guess):
    # 1. 解模型:根据 lambda_guess 算出工人的最优策略(比如保留工资) # 进而得到接受率 phi
    phi = solve_model(lambda_guess)

    # 2. 算模型矩:理论上的平均失业时长
    # 或者通过模拟 10000 个人来算平均时长
    duration_sim = 1 / (lambda_guess * phi)

    # 3. 读数据矩:从数据里算出来的真实平均失业时长(比如 6.5 个月)
    duration_data = 6.5

    # 4. 返回距离
    return (duration_sim - duration_data)**2

案例 B:固定调整成本 ($F$)

你负责研究企业投资行为,想知道“安装新机器的停工成本”有多高。

Step 1: 直觉 (Intuition)

如果 $F$ 很大(每次调整都要关停工厂一个月),我会尽量不动。除非机器真的烂透了,或者需求真的太大了,我才动一次。

那我可以去数一数数据里有多少个“0”(即企业没有进行任何投资的年份占比)。这个比例越高,$F$ 应该越大,所以这个比例就是我千辛万苦要找的“矩”!

思路有了,可以写矩条件了。

Step 2: 建模

价值函数: 企业面临决策,要么不动(Wait),要么调整(Adjust)。

$$ V(k) = \max \{ V*{wait}(k), V*{adjust}(k) - F \} $$

决策规则 (S-s Rule): 这个最大化问题的解通常表现为一个“不作为区间” (Inaction Region) $[k_{lower}, k_{upper}]$。

如果当前资本 $k$ 在区间内:$i = 0$。

如果当前资本 $k$ 掉出区间:$i \neq 0$。

区间宽度: 区间宽度 $W = k_{upper} - k_{lower}$ 是 $F$ 的增函数。$F$ 越大,区间越宽,你掉进区间的概率越大。单调性原则不就来了?

Step 3: 识别逻辑检查 (Monotonicity Check)

Step 4: 操作层面

1. 求解动态优化的Bellman方程,得到上下边界
2. 如果从数据中能得到企业的资本,就用GMM,不能就模拟:让 10000 个企业经受随机冲击,记录它们的资本 k
3.看多少企业的资本落在了“不作为区间”里,这些企业的投资 i 都会是 0
4. 从数据里读出真实的“投资为 0”的比例
5. 用模拟的和数据的“投资为 0”的比例做个距离(方差最小)

划重点 (Tips)

  • 不要过度识别 (Over-identification) 是神话: 虽然理论上矩越多越好,但实际上,矩选得越精准越好。增加无关的矩只会引入噪音。
  • 识别是讲故事: 在论文里写 Estimation 部分时,不要只写数学公式。要用我上面的“侦探逻辑”告诉读者:“为什么我相信 $\lambda$ 是 0.5?因为数据里的平均失业周期是 6 个月,只有 $\lambda=0.5$ 才能生成这个数据。”
使用 Hugo 构建
主题 StackJimmy 设计