深入理解 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 的"必要之恶"。 ...
让 AI 自己学会挖掘股票信号?聊聊 QuantaAlpha 这篇论文
论文:QuantaAlpha: An Evolutionary Framework for LLM-Driven Alpha Mining GitHub:QuantaAlpha/QuantaAlpha 如果对QuantAlpha的代码感兴趣,可以试试配套互动代码教程。 如果你听说过"量化基金"“AI 炒股"这类词,可能会好奇:AI 究竟是怎么在股市里找到赚钱机会的?它找到的方法可靠吗?能持续有效吗? 这篇文章想借一篇 2026 年的论文——QuantaAlpha——来回答这些问题。这篇论文做了一件有意思的事:不只是让 AI 去预测股价,而是让 AI 像一个量化研究员一样自己想出预测方法,然后不断进化改进。 我会从量化研究是什么讲起,一路讲到这篇论文的核心机制和实验结果。涉及公式的地方会附上直觉解释,不需要数学背景也能理解。 量化研究在做什么? 从"选股"说起 普通投资者选股,靠的可能是新闻、财报、直觉。量化研究员则不同——他们的工作是找到可以被数学描述、可以被计算机执行的选股规律。 这类规律叫做因子(factor)。一个因子本质上是一条规则:给市场上所有股票打一个分,然后买高分股票、卖低分股票,看这样做长期下来能不能赚钱。 最简单的因子:过去一个月涨幅最大的股票,下个月继续跑赢的概率更高(动量因子)。这条规律在很多市场里真的存在,背后有经济学解释(机构资金买入需要时间,趋势会延续),也有大量实证数据支持。 因子本身通常是一个数学表达式,作用在价格、成交量等原始数据上,输出一个分数。比如: $$f_t(i) = \frac{P_t(i) - P_{t-20}(i)}{P_{t-20}(i)}$$这就是一个 20 日动量因子——用今天的价格除以 20 天前的价格再减一,得到这 20 天的涨跌幅,作为对股票 $i$ 的打分。 “Alpha”是量化圈里的另一个核心词。它指的是扣除市场整体涨跌之后,策略额外赚到的收益(超额收益)。如果大盘涨了 10%,你的策略涨了 16%,那多出来的 6% 就是 Alpha。挖掘 Alpha,就是寻找能持续产生超额收益的因子。 量化因子挖掘的传统流程 在大语言模型(LLM)出现之前,量化研究员找因子的方式主要有两种: 人工驱动:研究员根据金融理论提出假设,手动设计数学表达式,回测验证。速度慢,但每个因子背后有清晰的经济学逻辑。 机器搜索:用遗传算法或强化学习在因子空间里暴力搜索,速度快,但搜出来的因子往往是无法解释的"天书"公式。 LLM 的出现提供了第三条路:它既理解金融语义,又能生成代码,理论上可以结合两者的优点。 现有 LLM Agent 方法是怎么做的? 大多数现有的 LLM Agent 因子挖掘方法,流程大致如下: 通用 LLM Agent 方法 ───────────────────────────────────────── 市场数据 ──→ [LLM] 提出假设 ──→ 生成因子代码 ↑ │ │ ↓ 修改假设 ←──── 回测评估(IC等) │ ↓ 搜索空间逐渐收缩 (反复修改,越来越像) ───────────────────────────────────────── 这个流程有一个隐藏的问题:LLM 每一轮都根据上一轮的回测结果"打补丁”,慢慢地,所有的修改方向都指向同一片区域,搜索空间在不断收缩。结果是生成了大量长相相似的因子,整体多样性越来越差。 ...
从"绝活哥"到认知负荷控制
最近在看一个英雄联盟博主的视频,他讲了一个我觉得挺有意思的观点:真正能快速上分到高段位的玩家,往往不是英雄池宽广、什么都会的那种人,而是把一两个英雄练到极致的"绝活哥"。 博主的解释是:如果你的英雄池很宽,你在每一局游戏里都要花注意力去熟悉技能怎么放、连招顺序是什么。这些操作层面的东西会把大脑塞满,让你根本没空去想那些更重要的事——对面打野在哪,这波兵线能不能推,团战应该怎么站。你不是不想想,是真的想不了,因为大脑已经满了。只有当一个英雄的操作被练到"根本不需要想"的程度,才能腾出空间去思考别的东西。 记得之前国服长期排位霸榜第一的莎莉也说他自己的经历——曾经在白金段位卡了很久,直到专注练习豹女这一个英雄之后,突然开窍,从白金一路连胜到王者。 我听完之后,结合一些自身的经历很有感触,把一些思考记录到了这篇博客中。 高三的一段经历 这让我想起了高三备考时的一段经历。 我高中三年英语一直不好,所以就默默接受了这个现实。有一次考试,完形填空20道题,我错了13个,这个数字把我自己都吓了一跳。当时刚好接触到了"刻意练习"的概念,就决定做个实验:先不管其他题型,只盯着完形填空一个题型死磕。每次拿到试卷,我都会带着最高的兴奋度去第一时间完成完形填空,似乎正常考试只有这一个题目是重要的,每次考试成绩出来也只会关注完形填空错了哪几道,犯了那些错误,其他题目是死是活我基本是完全不care了。做英语练习的时候也是类似的状态,只关注完形填空题型相关的错题。 后面的结果出乎意料。我做完形填空错误数量从13个慢慢降到7-8个,再降到3-4个,最后基本维持在0-2个。与此同时,英语总分在一个月内从平均110分涨到了140分往上,从一开始英语倒数第一,到后面考进了前10。 一开始我以为是刻意练习的功劳。但听完这个博主的解释之后,我开始重新想这件事——刻意练习可能只是形式,背后真正在起作用的,似乎是另一件事。 做题的过程中,我能感受到一个很明显的变化。刚开始刷题时,脑子里什么都在想但什么也没想清楚。到了后期,做题的感觉越来越不一样——不是说变得更轻松了,而是思考的内容明显变少了,思考的方向变得明显清晰了,开始能感知到"这道题在考什么"、“我哪里比较薄弱”。 这个变化让我觉得,可能不是我变聪明了,而是我主动降低了一些大脑负荷。 工作记忆和认知负荷 带着这个疑问,我后来找到了一些相关的研究。 认知心理学里有个概念叫工作记忆(Working Memory),可以理解成大脑里用来"当下处理信息"的临时空间。这块空间非常有限,研究表明人类工作记忆一次大约只能同时处理4到7个独立的信息块 1。 Sweller在1988年提出的认知负荷理论(Cognitive Load Theory)在此基础上进一步指出 2,认知资源是固定的总量,当低层次的操作占用了大量资源,留给真正学习的空间就少了。技能从生疏到熟练,心理学家Fitts和Posner描述了三个阶段 3:先是每个细节都要有意识地控制,极度消耗工作记忆;然后动作开始串联,有意识监控逐渐减少;最后达到自动化,执行几乎不再消耗工作记忆,意识可以去处理其他事情。 用这个框架回头看,我高三的经历似乎说得通了。之前没有采用刻意刷题的方式前,我每切换一次题型,工作记忆被"这类题的做题技巧有哪些"、“回忆语法的考察方式"这些基础但是不重要的操作塞满,没有余量去感知更重要的知识。而控制刷题类型的好处在于做题技巧是固定的,同时每种语法在这些题型中的考察方式也类似,我可以真正只关注到语法使用本身,从而快速的提升自己的英语能力。 当家教时的反面教材 这个理解让我想到自己当家教时犯的一个错误。 因为自己的英语高效提升,我后来去当了两次英语家教,想把这套方法分享给学生。我让他们也专门刷完形填空,但每次做完题,我会把所有错误的知识点全部讲一遍——一道题做下来,发现可能有10个知识点需要补充,就全讲了。另外,我还喜欢做知识拓展,因为在我自己的经历里,把知识点串联起来对巩固很有帮助。 但是效果很差。 回头看,我当时大概是把自己的认知状态投射到了学生身上。对我来说,那10个知识点在脑子里可能已经是2-3个更大的模式;对基础薄弱的学生来说,这是10个毫无关联的全新信息,早就超出他们工作记忆的上限了。知识拓展对我有用,是因为我已经有了稳定的基础,新东西可以挂上去;对他们来说,多余的信息可能只是额外的负担。 Sweller的研究里有个叫专家反转效应(Expertise Reversal Effect)的现象 4:对专家有效的教学方式,对新手往往是有害的。专家太清楚知识之间的关联,反而感知不到基础薄弱者面对同样信息时的认知压力。 更合适的做法,或许是只让他们关注那些最简单、最常见的错误,其余的暂时忽略,等在这一层建立了稳定的感觉,再引入下一层的复杂性。损失一些题目表面上的"利用率”,但认知负荷控制住了,进步可能反而会快一些。 深度学习的联系 在找资料的过程中,我发现深度学习领域有一些有意思的研究,和这个思路不谋而合。 Bengio等人在2009年提出的课程学习(Curriculum Learning)发现,按照从易到难的顺序训练神经网络,模型收敛更快,泛化能力更强 5。另一项研究则发现,神经网络在训练过程中会优先拟合低频特征(全局结构、普遍规律),然后才逐渐拟合高频特征(局部细节、特殊情况) 6。换句话说,在训练初期,那些高频的细节信号对网络来说更像是噪声,不仅没有帮助,反而会干扰模型对基础规律的学习。 这让我觉得,或许对于学习初期的人来说,过多的细节和拓展知识,并不像直觉上感觉的那样是"赚到了",反而可能因为超出当前处理能力而变成噪声——被大脑直接过滤掉,什么也没留下,有时还会影响到真正有价值内容的学习。 一边是人脑,一边是人工神经网络,两者在完全不同的背景下走向了类似的结论。我不确定这是否意味着什么更深层的东西,但这个巧合本身让我觉得挺有意思的。 想到哪写到哪 我没有什么实践上的结论,只是把这些零碎的观察和思考写下来。 如果这些想法有一个共同的方向的话,大概是:学习的瓶颈,可能不在于接收了多少信息,而在于认知资源有没有被集中在当前能够消化的那一层上。信息量超出处理能力,不会带来更快的进步,只会制造更多的混乱。 就像现在信息爆炸的时代,我常会有一种信息不过脑的焦虑,现在想来可能就是因为现代社会过多的信息,对于还在和数万年前古人类用着同等型号大脑的现代人来说,实在是有一些认知负荷过载了。 Miller, G. A. (1956). The magical number seven, plus or minus two: Some limits on our capacity for processing information. Psychological Review, 63(2), 81–97. Cowan, N. (2001). The magical number 4 in short-term memory. Behavioral and Brain Sciences, 24(1), 87–114. ↩︎ ...
OmniDrive 论文解读
一.论文总览 问题背景: 现有的将多模态大模型(Multimodal Large Language Model, MLLM)引入自动驾驶领域的方法中,大多不具备3D场景理解能力,但这一能力对于自动驾驶场景而言是不可或缺的,以一个驾驶场景中常见的问题为例:“询问当前车道是否可以左转”,该问题看似在做一个简单的语义判断,本质上需要涉及到对车道与自车的几何关系判断、车道与交通灯语义、地面标识语义的匹配,要回答这些问题,需要 MLLM 模型将 2D 理解和推理能力扩展到复杂的 3D场景中。 现有 MLLM 模型按照对图像的处理方式的不同,可以分为两种流派:一种是以 Flamingo 、BLIP-2、Qwen等方法为代表,以交叉注意力机制为基础,特点是不论图像分辨率都处理成统一长度的 token 序列,而另一种是以 Vit、 BLIP、LLAVA 为代表,以自注意力机制为基础,每个 token 用于代表固定像素大小的图像局部信息,这意味着不同的分辨率图像对应变长的 token 序列。对于自动驾驶场景而言,多视角、高分辨率、连续帧(视频)是感知任务的数据特点,而变长序列代表时延、不稳定。所以作者以 BLIP-2 结构为基础,引入 Stream-PETR(作者之前的工作),构建用于自动驾驶场景的 3D MLLM。 过去的 Benchmark 大多采用简单的 QA 的形式进行评测,已有工作证明端到端自动驾驶目前 open-loop 评测方式的局限性。另外,这种评测方式驱动的方法也无法完全利用到大语言模型强大的涌现能力。对于自动驾驶场景,类似于世界模型的反事实推断能力是更符合人类思考方式和习惯的。作者希望仿照 LLAVA 的做法,提出一种高效的数据构建方式,用于构建大规模 VQA 数据集,一方面用于 MLLM 的 Instruction-tuning 训练,另一方面用于评测。 贡献: 一种具有3d 能力的vision-language model结构,将多模态大语言模型用于自动驾驶场景中的3d场景任务; 一种基于 GPT-4o 的数据构建方法,用于生成自动驾驶场景的 VQA 问答数据,包括反事实推断形式的问答。 二.方法 结构总览: 图像编码器:作者采用的是基于 Clip 结构的 Eva-02 模型作为图像编码器,通过多视角图像特征提取 3d 信息,这一部分没太多需要讲的。 Q-Former3D:参考 BLIP-2 中的 Q-Former 结构,这里作者敏锐地发现了 Q-Former 结构和 Petr 系列模型结构的相似之处,将结构引入的同时也引入了 3D 目标检测任务作为辅助监督。 LLM:之前的结构的作用在于提取 3D 信息特征,最终需要对齐到 LLM 模型能够理解的特征空间,进行 VQA 问答。 Q-Former3D: 作为最能体现作者贡献的模块,这一部分选择 Stream-PETR 出于以下考虑:PETR 所代表的稀疏 BEV 建模方式,适用于检测任务,相比于需要构建 dense bev feature 的方法来说,需要更小的计算量,同时加快模型的推理速度,毕竟OmniDrive 的核心在于多模态大模型的能力,检测任务仅作为辅助监督,所以尽量简化降低存在感。 ...
The begining
作为这个blog 的开始...