结果与诊断#
当你已经拿到一次求解结果,并且想回答“它到底解出了什么、这些对象看起来是否合理”时,就应该看这一页。
请在 库快速上手 或 快速开始 之后阅读。如果你现在只想查对象成员,请改看 API 参考。如果你还需要看推导、齐次性降维和公式如何映射到代码,请先回到四个 BCW walkthrough。
这一页的目标,是让你在看到求解输出时不必靠猜。
你应该能用它回答:
求解器到底返回了什么对象?
哪些属性总是安全可读的?
什么样的数值形状可以算“健康”?
哪些症状更像是建模错误,哪些更像是数值调参问题?
最常配合阅读的页面#
求解器返回类型#
在本仓库的代表性运行中,各工作流的返回对象如下:
工作流 |
返回对象 |
常见伴随输出 |
|---|---|---|
|
|
一条 |
|
|
一条边界更新误差序列 |
|
|
解出的 |
|
|
summary DataFrame + |
例子:
state, history = solver.solve()
print(type(state).__name__)
print(len(history))
search_state = solver.boundary_search(method="bisection", verbose=False)
print(type(search_state).__name__)
print(search_state.grid.boundary)
最值得先看的对象#
state.grid#
这是完整的已求解网格对象,通常也是你后续分析时第一时间要拿到的对象:
grid = state.grid
最常用成员有:
grid.boundarygrid.sgrid.vgrid.dvgrid.d2vgrid.policygrid.df
state.df#
每个 solver state 都暴露了:
print(state.df.head())
print(state.df.tail())
它本质上就是 state.grid.df 的快捷入口。
本仓库里一次代表性 solve() 输出的前几列是:
['s', 'v', 'dv', 'd2v', 'investment']
如果你的模型有更多控制变量,列会更多。
history#
对 solve() 和 boundary_update() 来说,第二个返回对象都是历史误差数组。
它的意义在于:
记录每步迭代误差有多大;
区分“很快失败”和“缓慢收敛”;
用来比较两套配置谁更稳。
不要把 history 当成经济结果本身。它是迭代诊断,不是价值函数。
grid.boundary#
这是读取最终边界值最直接、最整洁的地方:
print(grid.boundary)
对仓库里的四个 BCW 案例来说,一个健康的 grid.boundary 通常会有这些模式:
liquidation:
s_min=0、s_max≈0.22、v_left=0.9;refinancing:
s_min=0、s_max≈0.19、v_left>0.9,并且内部会出现m≈0.06;hedging:
s_min=0、s_max≈0.14、v_left>0.9,同时对冲区域切点大致在w_-≈0.07和w_+≈0.11;credit line:
s_min≈-0.2、s_max≈0.08,并且w=0附近的边际价值明显更平。
重点不是逐位复现,而是确认每个案例的量级和边界关系是否合理。
grid.df:逐列解读#
列名 |
含义 |
为什么重要 |
|---|---|---|
|
状态网格 |
告诉你当前处于哪个现金状态 |
|
价值资本比 |
价值函数本体 |
|
一阶导数 |
现金的边际价值 |
|
二阶导数 |
曲率与右边界接触条件诊断 |
|
投资策略 |
真实决策如何随现金变化 |
|
hedging 案例中的对冲策略 |
识别绑定区、内部区和零对冲区 |
|
hedging 案例中的未裁剪对冲策略 |
用来诊断何时需要把策略裁剪到 |
三个信息量最高的诊断#
如果你时间很少,优先看这三个:
grid.boundarygrid.df.tail()grid.d2v[-1]
原因:
grid.boundary告诉你最终到底解了哪个边界问题;尾部表格可以直观看出右端是否接近正确边界行为;
grid.d2v[-1]是 BCW 中最关键的接触条件指标。
边界诊断#
左边界#
要问的问题:
v[0]是否符合你定义的左边界条件?liquidation 中,它是否接近 liquidation value?
refinancing 和 hedging 中,它是否因为发行而高于 liquidation 的值?
credit line 中,左边界是否真的落在
s=-c,并且和发行条件拼接一致?
右边界#
要问的问题:
dv[-1]是否逼近预期的支付边界斜率?d2v[-1]是否逼近零?曲线是否平滑地进入右边界,而不是在尾部震荡?
对 BCW 来说,一个健康的右尾通常意味着:
斜率逼近预期值,
曲率数值上消失。
策略诊断#
投资策略#
BCW 中比较典型的模式是:
左端:投资大幅收缩,甚至为负;
中间:逐步恢复;
右端:变成小幅正值。
再细一点看:
refinancing 中,投资恢复通常会和发行目标
m附近的区域对齐;credit line 中,有额度时
w=0附近的投资可以保持为正,而无额度基准仍然更受约束。
关键在于经济形状是否合理,而不是曲线必须线性或特别平滑。
对冲策略 psi#
在 hedging 案例中:
左端:
psi应该绑定在-pi;中间:出现内部解;
右端:回到
0。
如果这三个区域都没有出现,就该回查 hedging 逻辑。
grid.aux 是可选的,不保证总能用#
grid.aux 会调用模型中的可选钩子 auxiliary(grid)。
因此有一个重要后果:
如果你的模型没有实现
auxiliary(grid),访问grid.aux会直接抛NotImplementedError。
所以通用、安全的默认诊断对象是:
grid.boundarygrid.dfhistoryGrid/Grids/SensitivityResult的保存结果
只有在你自己实现了 auxiliary(grid) 以后,才建议依赖 grid.aux。
一个很稳妥的模式,是让 auxiliary(grid) 返回一个小字典,比如:
@staticmethod
def auxiliary(grid: fjb.Grid):
return {"value_mean": jnp.mean(grid.v)}
敏感性分析结果怎么看#
sensitivity_analysis() 返回的是 SensitivityResult:
result = solver.sensitivity_analysis(
method="hybr",
param_name="sigma",
param_values=...,
)
第一眼最值得看的两个对象是:
result.dfresult.grids
本仓库代表性结果的列名包括:
['sigma', 'boundary_error', 'converged', 's_min', 's_max', 'v_left', 'v_right']
这说明:
result.df是 continuation summary;result.grids里则保存每个参数点的完整求解网格。
本仓库一次代表性 continuation 的参数键值为:
[0.08, 0.09]
你因此既可以看:
边界如何随参数变化,
也可以回到每个参数点对应的完整
Grid上看价值函数和策略函数。
保存、重载、再检查#
推荐做法:
state.grid.save("outputs/liquidation_grid")
grid = fjb.load_grid("outputs/liquidation_grid")
print(grid.df.tail())
result.save("outputs/sigma_result")
loaded = fjb.load_sensitivity_result("outputs/sigma_result")
print(loaded.df)
当求解很耗时时,这样做尤其重要,因为你可以把“求解”和“解释结果”分开。
症状 -> 可能原因 -> 第一动作#
症状 |
可能原因 |
第一动作 |
|---|---|---|
|
边界目标错或搜索不稳定 |
先看 |
|
右边界不一致 |
先看 |
|
策略更新不稳或网格太粗 |
先核对方程,再考虑增大 |
|
对冲逻辑没进入内部区 |
回查 clipping 与 |
|
可选钩子没实现 |
暂时忽略或实现 |
|
更可能是模型写错,不只是收敛慢 |
退回固定边界 baseline 重新检查 |
一个最小但非常有用的 BCW 诊断脚本#
grid = state.grid
print(grid.boundary)
print(grid.df.head())
print(grid.df.tail())
print("right slope:", grid.dv[-1])
print("right curvature:", grid.d2v[-1])
如果你不知道从哪里开始看结果,这几行代码的信息量是最高的。
什么时候应该停止调参,回去重读模型#
如果出现下面这些情况,就不该继续盲调容忍度了,而应回到模型本身:
整个区间的经济形状都不合理;
换了不同求解器也都失败;
边界条件和模型故事本身对不上;
你的诊断结果与论文的定性图形完全相反。
这时最可能是建模问题,而不是数值微调问题。
相关页面#
如果你还在决定用哪种工作流:看 求解器指南
如果你准备迁移到自己的模型:看 把 BCW 改成你自己的模型
如果诊断已经告诉你求解不健康:看 排障