论文复现 | CEO特质与企业数字化转型:QUBO模型变量选择 + 模拟退火求解
一、论文想解决什么问题?
传统实证研究经常遇到一个问题:解释变量很多,但文章篇幅和理论解释能力有限。
以 CEO 特质为例,至少可以分成三类:
| 维度 | 变量 |
|---|---|
| 性别 | 是否为女性 CEO |
| 年龄 | 31-40、41-50、51-60 |
| 职能背景 | 生产、研发、设计、人力资源、管理、金融、市场、财务、法律 |
如果全部放进机制分析,文章会非常散;如果只挑其中几个,又容易被质疑"为什么选它们,不选别的"。
所以论文先做变量选择,再进入机制研究。核心思想是:用模型帮助研究者识别哪些 CEO 特质更重要,再围绕这些变量展开经济解释。
二、本次复现的主要输出
final_panel_logrd.csv
ols_results_logrd.txt
qubo_selection_results_sa_logrd.csv
table5_qubo_logrd.csv
table7_lag_regression_logrd.csv
table8_psm_regression_logrd.csv
table9_ceo_change_logrd.csv
paper_tables_5_7_8_9_logrd.xlsx三、第一步:构造 CEO 特质变量
CEO 特质来自 CSMAR 的上市公司人物特征数据。复现中将 TMTP=1 或 TMTP=3 视为 CEO 样本,并从性别、年龄和职业背景中构造 0/1 变量。
职能背景的处理也很直观。CSMAR 中 Funback 是职业背景编码:
def has_funback(value, code):
if pd.isna(value):
return 0
parts = [p.strip() for p in str(value).replace(",", ",").split(",")]
return int(str(code) in parts)
df["ceo_produce"] = df["Funback"].apply(lambda x: has_funback(x, 1))
df["ceo_research"] = df["Funback"].apply(lambda x: has_funback(x, 2))
df["ceo_design"] = df["Funback"].apply(lambda x: has_funback(x, 3))
df["ceo_hr"] = df["Funback"].apply(lambda x: has_funback(x, 4))
df["ceo_manage"] = df["Funback"].apply(lambda x: has_funback(x, 5))
df["ceo_market"] = df["Funback"].apply(lambda x: has_funback(x, 6))
df["ceo_finance"] = df["Funback"].apply(lambda x: has_funback(x, 7))
df["ceo_account"] = df["Funback"].apply(lambda x: has_funback(x, 8))
df["ceo_law"] = df["Funback"].apply(lambda x: has_funback(x, 9))如果同一公司同一年存在多个 CEO 记录,0/1 特质取最大值,年龄取均值:
agg_dict = {v: "max" for v in ceo_vars}
agg_dict["ceo_age"] = "mean"
out = df.groupby(["stock_code", "year"], as_index=False).agg(agg_dict)
四、第二步:先做 OLS,得到"全变量模型"的系数
QUBO 模型不是凭空构造的。论文的做法是先估计一个包含所有 CEO 特质、控制变量、行业固定效应和年份固定效应的 OLS 模型:
formula = "DT ~ " + " + ".join(ceo_vars + control_vars)
formula += " + C(industry_code) + C(year)"
model = smf.ols(formula=formula, data=panel).fit()因变量 DT 是数字化转型指数。控制变量包括资产负债率、资产周转率、独董占比、董事人数、高管持股比例、无形资产比例、固定资产比例、研发投入等。
研发投入的处理:
rd_raw = to_num(df["RDExpenses"])
"rd_investment": np.log1p(rd_raw.clip(lower=0))即使用 log(1 + RDExpenses),避免研发费用分布过度偏斜。
OLS 的作用有两个:
- 1. 给出各变量对数字化转型的基础估计。
- 2. 为 QUBO 模型提供系数,把"选不选变量"转化为"启用不启用 OLS 系数"。
五、第三步:QUBO 是什么?
QUBO 的全称是:
Quadratic Unconstrained Binary Optimization
中文通常称为"二次无约束二值优化模型"。它有两个特点:
- 1. 决策变量只能是 0 或 1。
- 2. 目标函数可以写成二次型:min x'Qx
这非常适合变量选择。因为"某个变量是否被选中"天然就是一个 0/1 问题:
- • x_j = 1:第 j 个 CEO 特质进入模型
- • x_j = 0:第 j 个 CEO 特质不进入模型
本次复现的决策变量为:
Intercept, Female,
age31_40, age41_50, age51_60,
Produce, Research, Design, HR, Manage, Finance, Market, Account, Law六、第四步:把残差平方和写成 QUBO
论文借鉴 OLS 的思想,仍然以残差平方和最小为目标。区别在于,CEO 特质部分要乘上 0/1 选择变量。
names = ["Intercept"] + ceo_vars
beta0 = model.params.get("Intercept", 0.0)
beta_ceo = np.array([model.params.get(v, 0.0) for v in ceo_vars])
beta_qubo = np.concatenate([[beta0], beta_ceo])
X_ceo = panel[ceo_vars].values
X_qubo = np.column_stack([np.ones(len(panel)), X_ceo])
y_hat_total = model.fittedvalues.values
ceo_part = X_qubo @ beta_qubo
control_fe_part = y_hat_total - ceo_part构造 QUBO 的二次型矩阵:
y = panel["DT"].values
residual_target = y - control_fe_part
weighted_x = X_qubo * beta_qubo
q_objective = weighted_x.T @ weighted_x
linear_term = -2 * (residual_target @ weighted_x)
q_objective = q_objective.copy()
np.fill_diagonal(q_objective, np.diag(q_objective) + linear_term)这一步是从"回归问题"走向"QUBO 问题"的关键。
七、第五步:把约束也写进 QUBO
论文中 CEO 特质分为三个维度:性别、年龄、职能背景。设置如下约束:
- • Intercept 必选
- • Female 必选
- • 年龄变量选择 b 个,b in {1, 2}
- • 职能背景变量选择 c 个,c in {1, ..., 8}
QUBO 是"无约束"优化,不能直接把约束丢给求解器。解决方法是把约束改写成惩罚项。
def add_cardinality_penalty(q, indices, target, weight):
"""Add weight * (sum(x_i) - target)^2 to a QUBO matrix."""
q = q.copy()
diag_add = weight * (1 - 2 * target)
for i in indices:
q[i, i] += diag_add
for pos, i in enumerate(indices):
for j in indices[pos + 1:]:
q[i, j] += weight
q[j, i] += weight
return q完整约束:
q = add_cardinality_penalty(q, [0], 1, penalty_weight)
q = add_cardinality_penalty(q, [1], 1, penalty_weight)
q = add_cardinality_penalty(q, age_indices, b, penalty_weight)
q = add_cardinality_penalty(q, bg_indices, c, penalty_weight)八、第六步:用模拟退火求解
模拟退火可以理解为一种"带有随机跳跃能力的搜索算法"。它不是从一开始就死盯局部最优,而是在高温阶段允许接受一些更差的解,以便跳出局部最优;随着温度下降,算法逐渐收敛到较优解。
本次复现使用的参数:
ANNEAL_READS = 300
ANNEAL_SWEEPS = 1800
ANNEAL_SEED = 20260428def anneal(q, b, c, seed):
rng = np.random.default_rng(seed)
best_feasible_x = None
best_feasible_loss = np.inf
t_start = max(1.0, float(np.nanpercentile(
np.concatenate([diag, off_diag]), 95
)))
t_end = max(1e-6, t_start * 1e-4)
for _ in range(ANNEAL_READS):
x = make_initial_state(rng, b, c)
energy = qubo_energy(q, x)
for sweep in range(ANNEAL_SWEEPS):
temperature = t_start * (t_end / t_start) ** (
sweep / max(ANNEAL_SWEEPS - 1, 1)
)
k = int(rng.integers(0, len(x)))
candidate = x.copy()
candidate[k] = 1 - candidate[k]
candidate_energy = qubo_energy(q, candidate)
delta = candidate_energy - energy
if delta <= 0 or rng.random() < math.exp(
-delta / max(temperature, 1e-12)
):
x = candidate
energy = candidate_energy
if constraint_violation(x, b, c) == 0:
rss = loss(x)
if rss < best_feasible_loss:
best_feasible_loss = rss
best_feasible_x = x.copy()
return best_feasible_x, best_feasible_loss约束检查函数:
def constraint_violation(x, b, c):
return (
(int(x[0]) - 1) ** 2
+ (int(x[1]) - 1) ** 2
+ (int(x[2:5].sum()) - b) ** 2
+ (int(x[5:14].sum()) - c) ** 2
)最终 16 组 (b, c) 的 constraint_violation 都为 0,说明求解结果满足论文设定的维度约束。
九、复现结果:QUBO 表 5
本次运行的最终可回归样本为 15420 条。模拟退火输出的表 5 风格结果如下:
| b | c | 31-40 | 41-50 | 51-60 | Produce | Research | Design | HR | Manage | Finance | Market | Account | Law |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
| 1 | 2 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
| 1 | 3 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 1 | 0 | 0 |
| 1 | 4 | 0 | 1 | 0 | 0 | 1 | 0 | 1 | 1 | 0 | 1 | 0 | 0 |
| 1 | 5 | 0 | 1 | 0 | 0 | 1 | 0 | 1 | 1 | 0 | 1 | 1 | 0 |
| 1 | 6 | 0 | 1 | 0 | 0 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 0 |
| 1 | 7 | 0 | 1 | 0 | 0 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 |
| 1 | 8 | 0 | 1 | 0 | 0 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 |
| 2 | 1 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
| 2 | 2 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 |
| 2 | 3 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 1 |
| 2 | 4 | 0 | 1 | 1 | 0 | 1 | 0 | 0 | 1 | 0 | 1 | 0 | 1 |
| 2 | 5 | 0 | 1 | 1 | 0 | 1 | 0 | 1 | 1 | 0 | 1 | 1 | 0 |
| 2 | 6 | 0 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 0 | 1 |
| 2 | 7 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 1 | 0 | 1 | 1 | 1 |
| 2 | 8 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 |
结果分析:
第一,年龄维度里,41-50 稳定出现;当允许选择两个年龄变量时,51-60 也进入模型。这与论文关于"大龄 CEO 更能解释数字化转型"的结论方向一致。
第二,职能背景中,Manage 几乎是最稳定的变量。也就是说,从"全局解释力"的角度看,管理背景 CEO 对企业数字化转型具有较强解释能力。
第三,随着允许选择的职能背景数量增加,Research、HR、Market、Account 等变量逐步进入模型。这说明 QUBO 不是简单地给变量排一个固定名次,而是在不同约束下寻找整体残差平方和更小的组合。
十、方法小结:为什么这套方法值得学?
OLS 回归回答的是:在控制其他因素后,每个 CEO 特质和数字化转型有什么统计关系?
LASSO 回答的是:哪些变量的影响更显著、更不容易被压缩为 0?
QUBO 回答的是:在给定变量数量约束下,哪一组 CEO 特质能让整体解释误差更小?
所以 QUBO 的优势不是替代 OLS,而是提供一种新的变量选择视角。它尤其适合这种"变量很多、变量有分组、每组想选固定数量"的研究场景。
十一、复现后的几点理解
第一,机器学习变量选择并不意味着放弃经济学解释。模型只是帮助我们更客观地缩小变量范围,真正的研究价值仍然来自后续机制解释。
第二,QUBO 的关键不是"量子"二字,而是把问题转写成适合 0/1 优化的形式。即使没有量子计算机,也可以先用模拟退火等经典算法求解。
第三,对论文复现来说,照着结果表做出来还不够。更重要的是理解原文为什么这样建模。如果只是用穷举得到类似表格,结果可能相近,但方法已经偏离了论文。
这次复现最大的收获也在这里:从"找一个最优组合"回到"构造一个 QUBO 模型",才算真正抓住了原文的方法核心。
