4、完全信息动态博弈:扩展式表述、子博弈完美均衡、逆向归纳法
好,咱们进入第四章。这一章讲的是动态博弈,说白了就是“你一步,我一步”的博弈。跟之前静态博弈那种大家同时出招不一样,动态博弈里,出招是有先后顺序的。
我个人觉得,动态博弈在量化风控里更贴近真实世界。你想想看,信贷审批、反欺诈、交易监控,哪个不是先有用户行为,然后风控系统再响应?用户看到你的规则,又会调整自己的策略。这就是一个动态博弈的过程。
4.1 扩展式表述:把博弈画出来
静态博弈我们用矩阵(表格)来表示,动态博弈呢?我们用“树”。这棵树就叫扩展式表述。
一棵博弈树包含几个要素:
- 节点:决策点。谁在什么时候做决策。
- 分支:行动选项。从节点出发的每一条线,代表一个可选的动作。
- 信息集:玩家知道多少信息。完全信息下,每个信息集只有一个节点,玩家清楚自己处在哪个位置。
- 支付:终点的收益。树的最末端,每个路径对应的结果。
举个例子,一个简单的信贷审批场景:
- 用户先决定:申请贷款(A)还是不申请(N)。
- 如果申请,风控系统再决定:批准(P)还是拒绝(R)。
- 如果批准,用户最后决定:按时还款(G)还是违约(B)。
这就是一棵三层的博弈树。每个节点都对应一个玩家,每个分支都是一种策略选择。
核心要点:扩展式表述把时间顺序、信息结构、行动空间都可视化出来了。在风控建模中,我习惯先用博弈树把业务流程画一遍,再去找最优策略。
4.2 子博弈完美均衡:剔除不可信的威胁
动态博弈里,有个经典概念叫子博弈完美均衡(Subgame Perfect Equilibrium,SPE)。
什么意思呢?就是要求博弈的每个“子博弈”上,玩家的策略都是最优的。子博弈就是博弈树中任何一个节点开始,到终点的部分。
为什么要强调这个?因为有些策略在全局看是均衡,但在某个局部看,根本不合理。比如,风控系统威胁用户说:“你敢违约,我就把你拉黑一辈子。” 这个威胁在用户违约后,系统真的会执行吗?如果拉黑一个违约用户对系统没有好处,那这个威胁就是不可信的。
子博弈完美均衡,就是要剔除这些不可信的威胁。只保留那些“说到做到”的策略。
避坑指南:我曾经在做一个反欺诈规则时,设计了一个“一旦检测到异常,立即冻结账户”的策略。理论上能吓住欺诈者。但后来发现,误冻了大量正常用户,导致投诉率飙升。这个威胁在子博弈上(即误冻发生后)是不可信的,因为系统承受不了误冻的代价。后来我改成了分级预警,才真正实现了均衡。
4.3 逆向归纳法:从后往前推
怎么求解子博弈完美均衡?最常用的方法就是逆向归纳法(Backward Induction)。
思路很简单:从博弈树的最后一步开始,往前推。每一步,当前玩家都选择对自己最有利的行动。然后把这个最优行动的结果,作为上一层的支付,继续往前推。
具体步骤:
- 找到博弈树的所有终端节点(叶子节点)。
- 从最后一个决策节点开始,看那个玩家会选哪个分支(选对自己最有利的)。
- 把选中的分支对应的支付,作为该节点的“价值”。
- 往前推一层,重复步骤2-3,直到根节点。
- 最终得到的路径,就是子博弈完美均衡路径。
咱们用代码实现一下刚才的信贷审批例子:
# 逆向归纳法求解信贷审批博弈
# 支付格式:(用户收益, 系统收益)
# 定义博弈树(用字典表示)
# 结构:节点名 -> (当前玩家, {行动: 子节点或支付})
game_tree = {
'root': ('User', {
'申请': 'system_node',
'不申请': (0, 0) # 用户不申请,双方收益为0
}),
'system_node': ('System', {
'批准': 'user_repay_node',
'拒绝': (-1, 0) # 用户被拒,收益-1;系统无损失
}),
'user_repay_node': ('User', {
'按时还款': (5, 3), # 用户获得信用,系统赚利息
'违约': (8, -5) # 用户占便宜,系统亏本
})
}
def backward_induction(node):
"""逆向归纳法求解"""
if isinstance(node, tuple):
# 叶子节点,直接返回支付
return node
player, actions = node
best_value = None
best_action = None
for action, next_node in actions.items():
# 递归求解子博弈
value = backward_induction(next_node)
if best_value is None:
best_value = value
best_action = action
else:
# 根据当前玩家选择最优
if player == 'User':
# 用户最大化自己的收益(第一个值)
if value[0] > best_value[0]:
best_value = value
best_action = action
else: # System
# 系统最大化自己的收益(第二个值)
if value[1] > best_value[1]:
best_value = value
best_action = action
print(f"节点 {player} 选择 '{best_action}',得到支付 {best_value}")
return best_value
# 求解
print("逆向归纳法求解过程:")
final_payoff = backward_induction(game_tree)
print(f"\n最终均衡路径的支付:{final_payoff}")
运行结果会显示:用户会申请,系统会批准,用户会按时还款。最终均衡路径是(申请,批准,按时还款),支付为(5, 3)。
注意:逆向归纳法要求博弈是完美信息的,即每个玩家都知道之前所有玩家的行动。如果存在同时行动或信息不完全,逆向归纳法就不适用了,得用更复杂的解法。
4.4 知识体系图
下面我用一张SVG图,把本章的核心逻辑串起来:
这张图把三个核心概念的关系理清了。扩展式表述是基础,子博弈完美均衡是目标,逆向归纳法是求解工具。三者缺一不可。
4.5 实战中的避坑指南
最后,分享几个我在项目中踩过的坑:
- 别忽略信息集:完全信息动态博弈假设玩家知道所有历史。但在风控中,用户可能不知道你的规则细节,系统也可能不知道用户的真实意图。这时候信息集就不完美了,得用不完全信息博弈。
- 逆向归纳法不是万能的:如果博弈树太大(比如有几十层),手工推演不现实。我一般用动态规划或递归算法来求解,就像上面的代码示例那样。
- 均衡不等于现实:子博弈完美均衡是理论上的最优策略。但现实中,用户可能不理性,系统也可能有执行偏差。所以,我通常把均衡解作为基准线,再结合业务经验做调整。
嗯,这一章就到这里。记住:动态博弈的核心是“时序”和“可信性”。在风控中,你设计的每一个规则,都是在和用户进行一场动态博弈。想清楚每一步的后果,才能做出真正有效的策略。
公众号:蓝海资料掘金营,微信deep3321