BPQP:加速可微凸优化层的端到端学习框架

论文:BPQP: A Differentiable Convex Optimization Framework for Efficient End-to-End Learning (NeurIPS 2024) 作者:Jianming Pan, Zeqi Ye, Xiao Yang, Xu Yang, Weiqing Liu, Lewen Wang, Jiang Bian 单位:UC Berkeley, Nankai University, Microsoft Research Asia 代码:microsoft/qlib 这篇论文是 NeurIPS 2024 上一篇关于"如何让神经网络中的优化层反向传播变快"的工作。读完后我觉得它的核心 idea 非常优雅——一句话概括是: 把反向传播中昂贵的 KKT 矩阵求逆问题,重构为一个等价的 QP 问题,用 ADMM 高效求解。 但要真正理解这句话,需要补充不少前置知识。本文面向对优化理论不太熟悉的算法工程师,会沿着"问题 → 抽象 → 前置知识 → 现有方案 → 论文方法"的路线,把整个故事讲清楚。中间会有相对详细的凸优化、KKT、隐函数定理介绍——熟悉的读者可以自行跳过。 一、背景:金融中的一个真实难题 投资组合优化 假设你是一个量化基金的投资经理,手上管理着 500 只股票,每天都要决定一件事:这些股票各应该配置多少比例的资金? 经典的解法是马科维茨的均值-方差优化(Mean-Variance Optimization, MVO): $$ \max_w\ \mu^\top w - \frac{\gamma}{2} w^\top \Sigma w \quad \text{s.t.}\ \mathbf{1}^\top w = 1,\ w \geq 0 $$其中: ...

Date: June 10, 2026 | Estimated Reading Time: 21 min | Author: Coldeye

深入理解 Python GIL:从原理到实践

写 Python 写了几年,你或许曾遇到过这样的困惑:明明给数据处理加了多线程,CPU 占用率飙上去了,速度却几乎没变。这不是你的代码有 bug,而是 GIL 在起作用。 本文从原理出发,用实验数据说话,帮你彻底搞清楚 GIL 的边界——以及在机器学习、深度学习、量化金融场景下如何系统性地绕开它。 实验环境:Apple M3 Pro,Python 3.11.13,numpy 2.2.3,pandas 2.3.3,torch 2.11.0 实验代码:https://github.com/Coldeye2020/gil_experiments 一、GIL 是什么,为什么存在? 一句话定义:GIL(Global Interpreter Lock,全局解释器锁)是 CPython 中的一把互斥锁,保证同一时刻只有一个线程在执行 Python 字节码。 从引用计数说起 CPython 用引用计数(reference counting)管理内存。每个 Python 对象都维护一个 ob_refcnt 字段,记录当前有多少个引用指向它;当计数归零时,对象被立即释放。 这个机制简洁高效,但存在一个问题:如果多个线程同时对同一个对象的引用计数做 +1 或 -1,就会产生竞态条件(race condition)——两个线程同时读到旧值 n,各自写回 n+1,结果只加了一次而不是两次。这会导致内存泄漏,甚至在错误时机释放仍在使用的对象,引发崩溃。 理论上可以给每个对象的引用计数加一把细粒度锁,但这意味着几乎每次对象访问都要加锁解锁,开销极大。CPython 的设计者 Guido van Rossum 在 1990 年代初选择了一个更简单的方案:加一把大锁,锁住整个解释器。这把锁就是 GIL。 用代码验证引用计数 import sys a = [] print(sys.getrefcount(a)) # 输出 2:a 本身持有 1 个引用,getrefcount 调用时临时持有 1 个 b = a print(sys.getrefcount(a)) # 输出 3:b 也引用了同一个列表 del b print(sys.getrefcount(a)) # 输出 2:b 被删除,引用数减 1 sys.getrefcount() 让我们直接观察引用计数的变化。正是这个机制的存在,让 GIL 成为 CPython 的"必要之恶"。 ...

Date: April 19, 2026 | Estimated Reading Time: 28 min | Author: Coldeye