简介:这是一份面向金融风险研究方向的银行间网络重构方法学习文档,适合金融工程、系统性风险建模领域的硕士生、博士生及研究人员参考。资源围绕最大熵方法与最小密度方法的局限展开,重点讨论如何融合局部聚类特征、自适应调整连边密度,以更贴近实证网络的「核心—外围」结构,涉及稀疏性、无标度性、异配性与风险传染模拟等关键议题。包内为1个docx文档,约215KB,内容包含模型方法推导、拆借矩阵约束优化建模、链路预测的马尔可夫过程描述及文献综述,结构完整,可当作论文写作与模型实现的参考底稿。该资源已有63人学习下载。读者可据此理解最小密度方法在核心银行聚类刻画与连边密度估计上的不足,掌握改进重构思路,并应用于网络构型生成与压力测试实验设计。
1. 当最大熵把银行间网络摊成一张全连接网,重构该往哪走
系统性风险传染模拟的输入是一张 N×N 的银行间敞口矩阵,X_ij 表示 i 银行拆借给 j 银行的资金数量。这张矩阵决定了冲击在银行体系里的传播路径,可它属于保密数据,研究者通常只能拿到各家银行的同业拆入总额和拆出总额,于是整个领域都靠这两组边际约束去反推矩阵。最大熵是最早被广泛采用的思路:假定网络结构完全未知,在满足边际约束的所有矩阵里挑熵最大的那个,结果是任何一对有资金往来的银行都分到一笔敞口,网络被摊成一张完全连接图。实证数据并不支持这个假设——德国、英国的银行间网络呈现稀疏、无标度、异配的特征,还带有明显的“核心−边缘”结构,少数核心银行充当货币中心枢纽。有研究用意大利市场数据做过检验,最大熵重构会把系统性风险的传染范围估得偏高,压力测试的结论跟着失准。最小密度方法换了个出发点,把每一条拆借关系视为一笔联系成本,在边际约束下最小化连边总数,能复现异配与无标度,却压低了核心银行之间的相互拆借,局部聚类丢失,连边密度也被系统性低估。下面拆的就是怎么在保住稀疏性的前提下,把这两块补回来。
2. 最小密度方法的约束优化与马尔可夫式连边生成
要把改进方法讲清楚,得先把最小密度方法的每一步拆开看。它的骨架是一个带约束的稀疏化问题,外面套一层随机链路预测的迭代,理解了概率矩阵 Q 是怎么算出来的,后面的修正矩阵才有落点。
2.1 从资产负债边际约束到稀疏化目标函数
银行间网络抽象成矩阵后,行和是银行间资产总额 A_i,列和是银行间负债总额 L_i,对角线上 X_ii = 0,因为银行不会向自己拆借。最小密度方法引入联系成本参数 c,每生成一条拆借关系,整个系统的联系成本就增加 c,目标函数写成连边指示函数的求和,整体是一个 0-1 型约束优化:
- 行约束:Σ_j X_ij = A_i,保证银行的拆出总额被完全分配
- 列约束:Σ_i X_ij = L_j,保证拆入总额被完全吸收
- 非负约束:X_ij ≥ 0,且 i ≠ j 时才有连边
表 1 列出了后面所有推导都会用到的符号,参数量级弄错是最常见的错误来源之一。
| 符号 | 含义 | 说明 |
|---|---|---|
| N | 银行节点数 | 2018 年实验取 272 |
| A_i | 银行 i 的银行间资产(拆出) | 行和约束 |
| L_j | 银行 j 的银行间负债(拆入) | 列和约束 |
| c | 单条连边的联系成本 | 目标函数权重 |
| AD_i | 当前未分配的拆出需求 | A_i − Σ_j X_ij |
| LD_j | 当前未分配的拆入需求 | L_j − Σ_i X_ij |
| Q_ij | 银行对 (i,j) 的拆借概率 | 状态空间 |
| V(X) | 适应度函数 | 评价新增连边 |
2.2 概率矩阵 Q 与迭代中的连边生成
初始状态下邻接矩阵是全零矩阵,每轮迭代先算剩余需求,再构造概率矩阵。流动性拆出需求写为 AD_i = A_i − Σ_j X_ij,拆入需求写为 LD_i = L_i − Σ_j X_ji,两者都随已分配额度动态收缩。拆借概率取两者比值的较大者:
Q_ij = max{AD_i / LD_j, LD_j / AD_i}
随机从 Q 的状态空间里抽出一对 (i,j),按 X'_ij = min{AD_i, LD_j} 分配权重,也就是“有多少拆多少,取缺口小的一侧”。新增连边后还要更新状态空间,再用适应度方程
V(X) = −c·ΣΣ1[X_ij>0] − Σ(α_i·AD_i² + δ_i·LD_i²)
评估这条边是否值得保留。后半项是剩余需求的平方惩罚,需求分配得越干净,惩罚越小。由于下一轮的连边选择只依赖当前的 Q,整个链路预测过程是一个马尔可夫过程;方法里还并行跑第二个马尔可夫过程,对已有连边做随机淘汰,用来避免早期错误连接固化。把这几行翻译成代码:
import numpy as np def build_demand(A, L, X): """返回当前状态下未分配的拆出需求 AD 与拆入需求 LD""" AD = A - X.sum(axis=1) # 行方向:还没拆出去的额度 LD = L - X.sum(axis=0) # 列方向:还没拆进来的额度 return AD, LD def prob_matrix(AD, LD, eps=1e-12): """最小密度方法的概率矩阵 Q_ij = max(AD_i/LD_j, LD_j/AD_i)""" r1 = AD[:, None] / np.maximum(LD[None, :], eps) r2 = LD[None, :] / np.maximum(AD[:, None], eps) Q = np.maximum(r1, r2) np.fill_diagonal(Q, 0.0) # 禁止自拆借 Q[AD <= eps, :] = 0.0 # 无拆出需求的银行不参与 Q[:, LD <= eps] = 0.0 # 无拆入需求的银行不参与 return Q def min_density(A, L, max_iter=200000, seed=42): rng = np.random.default_rng(seed) N = len(A) X = np.zeros((N, N)) for _ in range(max_iter): AD, LD = build_demand(A, L, X) if AD.max() < 1e-8 or LD.max() < 1e-8: break # 流动性分配完毕,退出迭代 Q = prob_matrix(AD, LD) flat = Q.ravel() s = flat.sum() if s <= 0: break idx = rng.choice(N * N, p=flat / s) i, j = divmod(idx, N) X[i, j] += min(AD[i], LD[j]) # 按缺口小的一侧确定权重 return Xbuild_demand每轮重算 AD、LD,这两组向量是整个算法的状态量;prob_matrix的对角线清零和需求掩码是必须的,否则会在已经分配完的银行上反复抽样,迭代次数白白耗尽。rng.choice按归一化后的 Q 抽样,等价于在马尔可夫链上按概率转移一步。真实实现里还应在每轮滚动淘汰若干已有连边,并把适应度增量作为接受准则,否则网络容易在早期几步就被写死。
2.3 异配先验为什么压掉了核心银行间的连边
问题出在 Q 的构造上。中国银行体系中,大型银行的拆出与拆入规模都在同一量级,AD 与 LD 数值接近,两个比值都趋于 1;而小型银行的缺口绝对值小,与大型银行配对时 AD_i/LD_j 或 LD_j/AD_i 反而更容易出大值。抽样概率被这类“大对小”的组合占满,结果就是先验层面强制了异配性——从刻画稀疏和异配的角度这是优点,代价是核心银行之间的大额拆借几乎排不上号。
这一点在实证里得不到支持。核心银行之间通过相互拆借大量资金完成流动性融通,在体系内承担货币中心枢纽的功能,它们之间的连边密度本应显著高于全网平均水平。最小密度方法重构出的网络里,21 家核心银行之间的连边稀疏到看不出任何聚类结构,而局部聚类特征恰恰是“核心−外围”结构区别于纯无标度网络的关键。
3. 核心增强矩阵修正概率与自适应因子调权
改进思路有两条:一是从概率矩阵入手,把核心银行之间的抽样概率抬上来;二是从权重分配入手,让连边密度不再被最小化的目标函数一路压到底。两条线分别对应核心增强矩阵和自适应因子。
3.1 用 core 增强矩阵修正概率矩阵
修正矩阵 M 按分块方式构造:核心−核心块整体乘上一个增强系数 core,其余位置保持全 1。规模由预设的核心银行数量决定,如果取资产规模排名前 21 家的银行作为核心,那么 core 块就是 21×21 的子矩阵。修正后的概率矩阵为 Q̇ = M · Q,即对每个元素做逐点乘法。
$M=\begin{bmatrix} core & \mathbf{1} \ \mathbf{1} & \mathbf{1} \end{bmatrix}, \quad \dot{Q}=M\cdot Q$
增强系数的大小直接跟数据的量纲挂钩。实验里以万亿元为量纲、取 core = 500,是因为原始资产负债表科目本身就在万亿级,这个数量级下的 500 才能在归一化抽样里产生明显偏置;如果数据换成亿元量纲,同一个系数会立刻失真。矩阵 M 也可以不按 0/1 分块,而是参考银行间的拆借偏好逐对设定,原文采用“核心−边缘”结构,属于最省参数的一种取法。
3.2 自适应因子 λ 的取值与切换条件
权重分配加入自适应因子后改为 X'_ij = λ·min{AD_i, LD_j},λ 定义为一个随连边数增长的函数:
λ = 1 − e^{−(1+d)/N}
其中 d 是当前网络连边数量,N 是节点数量。d 很小时 λ 接近 0,新边只分配很小一部分额度,网络有机会铺开更多连接;d 增大后 λ 向 1 靠近。但 λ = 1 才能实现资源完全分配,如果全程都按公式取值,迭代后期会陷入几乎所有剩余额度都被 λ 打折、永远分不完的困境,迭代次数失控。实验里的处理是加一个切换判据:当 min{AD_i, LD_j}/A_i > λ 或 min{AD_i, LD_j}/L_j > λ 时按公式取 λ,否则直接把 λ 置为 1,让这一笔额度一次性分完。
这个判据的物理含义是:如果单笔可转移额度相对该银行的资产规模已经足够小,继续打折没有意义,直接释放。# 两个分母选择 A_i 和 L_j 而不是统一的 n,是因为拆出方受资产约束、拆入方受负债约束,两侧的容忍度不同。
3.3 完整的重构流程与代码实现
把修正矩阵和自适应因子接进去,算法结构与最小密度方法基本一致,但去掉了适应度方程这一层——因为目标不再是严格的最小密度网络,无需再用 −c·ΣΣ1[X_ij>0] 去否定新增连边。
def clustered_recon(A, L, core_idx, core_boost=500.0, max_iter=200000, seed=42): rng = np.random.default_rng(seed) N = len(A) X = np.zeros((N, N)) M = np.ones((N, N)) M[np.ix_(core_idx, core_idx)] = core_boost # 仅抬升核心-核心块 for _ in range(max_iter): AD, LD = build_demand(A, L, X) if AD.max() < 1e-8 or LD.max() < 1e-8: break Qc = M * prob_matrix(AD, LD) # 修正概率矩阵 flat = Qc.ravel() s = flat.sum() if s <= 0: break i, j = divmod(rng.choice(N * N, p=flat / s), N) remain = min(AD[i], LD[j]) d = np.count_nonzero(X) # 当前连边数 lam = 1 - np.exp(-(1 + d) / N) # 自适应因子 if remain / A[i] > lam or remain / L[j] > lam: w = lam * remain # 打折分配 else: w = remain # 一次性分配完 X[i, j] += w return Xcore_idx传核心银行的下标数组,core_boost就是前面说的增强系数。修正只在M * prob_matrix(...)这一行生效,其余流程与最小密度方法共用同一套需求更新逻辑,改动面小、可回退。真正影响重构质量的三个开关是核心银行数量、core_boost取值、以及 λ 的切换判据,这三者决定了核心连边密度和全网连边密度之间的平衡点落在哪。
4. 272 家银行年报数据上的重构与结构指标对比
4.1 数据准备与虚拟银行平衡处理
实验数据来自 2018 年 Bankscope 数据库公布的 272 家银行年报,包含总资产、银行间借贷款、股权资本、存款等资产负债表科目,样本总资产规模占当年银行业总资产的 78.16%。年报只披露拆借总额,不披露交易对手,所以只有边际约束可用。
由于样本未覆盖全部金融机构,同业拆借数据在加总后不平衡:ΣA_i ≠ ΣL_j。常见做法是构造一家虚拟银行吸收多余的拆借数据,把差额挂到它的资产或负债端,让矩阵在数学上可解。# 虚拟银行必须从后续的结构指标计算中剔除,否则它的度值会异常大,直接把同配性和聚类系数带偏。
核心银行的选取按资产规模排序取前 21 家,包括 6 家国有商业银行、3 家政策性银行和 12 家股份制商业银行。增强系数的量纲按万亿元处理,取 500。
4.2 三个结构指标的计算方式
连边密度衡量连接紧密程度,有向图的定义是 D = m / (n(n−1)),m 为连边数,n 为节点数;把 m、n 换成核心银行子图的对应值就得到核心银行连边密度。平均聚类系数用局部聚类系数求均值,节点 i 的聚类系数 C_i 由经过该节点的三角形数 T_i 和度数 deg_i 决定,C_i = 2T_i / (deg_i(deg_i−1))。同配性系数 r 用来判断度值相近的节点是否倾向互连,正值同配、负值异配。
import networkx as nx def struct_features(X, drop_last=True): """计算连边密度、平均聚类系数、同配性""" A_bin = (X > 0).astype(int) if drop_last: A_bin = A_bin[:-1, :-1] # 剔除虚拟银行 n = A_bin.shape[0] m = int(A_bin.sum()) G = nx.from_numpy_array(A_bin, create_using=nx.DiGraph) density = m / (n * (n - 1)) # 有向图密度 clustering = nx.average_clustering(G.to_undirected()) assort = nx.degree_assortativity_coefficient(G) return density, clustering, assortfrom_numpy_array把 0/1 邻接矩阵转成有向图,权重信息在这里被刻意丢掉,因为三个指标只看是否存在连接。to_undirected()是为聚类系数服务的,有向三角形的定义在文献里分歧较大,转成无向图后再算更稳定。同配性则保留有向结构,用入度、出度计算。
4.3 三种方法的结果对比与数据解读
在同一数据集上跑最小密度、引入自适应因子但未融合局部聚类、以及融合局部聚类三种重构,结果如下表。
| 结构特征 | 最小密度网络 | 未融合局部聚类 | 融合局部聚类 |
|---|---|---|---|
| 连边密度 / % | 0.43 | 0.94 | 0.86 |
| 核心连边密度 / % | 5.00 | 7.38 | 13.57 |
| 平均聚类系数 | 1.79 | 7.59 | 12.15 |
| 同配性 | −0.346 | −0.339 | −0.259 |
三组数字可以读出三层信息。自适应因子单用,连边密度翻了一倍多(+118.6%),说明它确实修正了最小密度方法低估密度的问题,可核心连边密度只涨了 47.6%,同配性几乎不动,聚类特性没被触及。融合局部聚类后,连边密度相对未融合版本反而降了 8.5%,核心连边密度却提升 83.9%,平均聚类系数提升 60.1%——两升一降的组合正好说明增强矩阵把连接从“外围铺开”转向了“核心聚拢”,在整体稀疏性不受损的前提下重构出核心枢纽。
注意:原文结束语把这两个提升数字的对应关系写颠倒了,表述为“平均聚类系数和核心银行间连边密度上提升了 83.9% 和 60.1%”,按表 1 实际计算,83.9% 对应核心连边密度、60.1% 对应平均聚类系数。复现时以表格数据为准。
同配性从 −0.346 回到 −0.259,仍为负值,说明网络保持异配。这一点容易被误读成“改进引入了同配性缺陷”,实际上增强局部聚类必然会让部分同规模核心银行互连,同配性向零靠近是结构性代价,只要仍显著为负就还符合实证网络的异配特征。
5. core 系数敏感性、λ 切换与重构网络的验证技巧
5.1 增强系数的量纲敏感性
core_boost不是一个可以跨数据集直接照抄的参数。它在抽样中起的是相对偏置作用,一旦资产负债表科目的量纲从万亿元换成亿元,原值 500 会被归一化过程稀释成几乎无偏置的 1,核心聚类特征会完全消失。稳妥做法是先固定核心银行集合,再把增强系数从小到大扫一遍,观察核心连边密度的拐点:
for boost in [10, 50, 100, 300, 500, 1000]: X = clustered_recon(A, L, core_idx, core_boost=boost) d_all, d_core, clus, r = struct_features(X) print(f"boost={boost:5d} 密度={d_all:.4f} 核心密度={d_core:.4f} " f"聚类={clus:.2f} 同配={r:.3f}")系数过小时核心块与普通块没有区分度,结果退化成未融合版本;系数过大时抽样几乎只在核心块内部循环,外围银行的需求分不出去,连边密度会异常下降。拐点通常出现在核心连边密度开始显著高于全网密度的那个量级。
5.2 λ 切换判据的两个边界
切换判据里的分母 A_i、L_j 决定了提前一次性释放额度的时机。如果改成用全网总额 ΣA 做分母,阈值会小得几乎总是不满足,λ 永远按公式取,迭代在后期会明显变慢;反过来把阈值整体放大,大量额度在第一轮就被一次性分完,自适应因子退化成常数 1,密度调节能力丢失。判断实现是否正确,一个轻量办法是记录每轮 d 和 λ 的轨迹,正常情况下 d 应从 0 快速上升然后放缓,λ 单调递增并在接近 1 前切换到常数 1。
5.3 无标度特性的双对数验证
重构出的网络是否保留了无标度性,靠一句“看起来像”不够。把入度和出度分布画到双对数坐标下,若近似为直线则说明度分布服从幂律。最小密度网络和融合局部聚类网络的入度、出度分布在双对数坐标下都呈近似线性,说明引入局部聚类并没有破坏无标度骨架,这一点是判断改进是否“过头”的关键回归测试——如果核心增强系数调大后双对数图上出现明显的截断或弯曲,说明网络已被重构成星型结构,不再具备无标度特征。
import numpy as np import matplotlib.pyplot as plt A_bin = (X > 0).astype(int)[:-1, :-1] out_deg = A_bin.sum(axis=1) in_deg = A_bin.sum(axis=0) for deg, label in [(out_deg, "out"), (in_deg, "in")]: vals, cnts = np.unique(deg[deg > 0], return_counts=True) plt.loglog(vals, cnts, "o", label=label) plt.xlabel("degree k"); plt.ylabel("P(k)") plt.legend()对数分箱时注意给vals加最小整数保护,度值为 1 的点在对数轴上会被丢弃,导致低度区的尾部信息缺失;同时样本量只有 272 家银行,高度区每个点可能只对应一两家银行,点数过少的区间不适合单独下结论,判断幂律要看整体斜率而不是局部抖动。
本文还有配套的精品资源,点击获取