科学公式理解这件事,听起来像是一个纯粹的算法问题,但真正动手做过的人会发现,它最难的环节往往不在“识别”,而在“理解和表达”。我接触过不少做学术文档解析、科研知识库建索引、以及想用大模型处理数学公式的团队,大家卡住的位置惊人地一致:公式表面上是一串符号,但这一串符号背后同时存在两个层次——语法层和语义层。这篇就想把“Syntax Meets Semantics: Understanding Scientific Formulae”这个主题拆开讲清楚:科学公式里的语法是什么,语义是什么,两者在工程落地上如何分工,以及为什么很多方案看起来能识别公式,却没法真正“理解”公式。
先给一个总判断:一个能用的公式理解系统,不是把公式图片变成 LaTeX 字符串就结束了,而是一条从公式检测、结构识别、语义映射到检索或知识库接入的完整链路。这条链路上最值得优先把握的不是模型精度,而是语法层和语义层的边界——什么时候做字符级和结构级校验,什么时候引入上下文和数学规则,决定了下游系统是稳定运行还是频繁返工。
这篇文章适合三类人看:做学术文档解析的工程师、做科研知识库与文献检索的产品经理、以及想把数学公式接入 RAG 或大模型应用的技术同学。下面按实际落地顺序拆开讲。
1. 科学公式里的“语法”和“语义”,到底在说什么
1.1 语法层:眼睛能看到的结构
先说什么叫公式的语法层。简单说,语法层描述的是公式“长什么样”,包括符号的顺序、上下标的位置、分数线的结构、根号的范围、求和符号的上下限等。它关心的是表达式是否符合某种书写规则,能不能被解析成一个合法的结构树。
举个最直观的例子:\frac{1}{2}这个 LaTeX 串,语法层告诉我们这里有一个分数,分子是 1,分母是 2。如果把它写成\frac{1}{},分母为空,那从 LaTeX 编译的角度看,这个结构不合法,会报错。这和写代码时遇到syntaxerror: invalid syntax是同一类问题——不是“这个公式有没有意义”的问题,而是“这个表达式能不能被解析”的问题。
在实际工程里,语法层的产物通常是这样几种形式:
- LaTeX 字符串,人类可读,书写灵活,但同一个公式可以有多种等价的 LaTeX 写法。
- Presentation MathML,描述排版结构的 XML,保留位置和层级关系。
- 解析树或语法树,把公式拆成嵌套的节点结构,便于程序继续处理。
1.2 语义层:能算出来和能推理的含义
语义层描述的是公式“意味着什么”。同样一个\frac{1}{2},语义层应该告诉我们这是“1 除以 2”这个运算关系,而不是仅仅“上面有 1、下面有 2”的排版事实。换句话说,语义层要把结构翻译成数学对象:变量、常量、函数、运算符、关系表达式、集合、矩阵等。
这个翻译并不容易。因为同一个结构在不同上下文里可能表达不同的数学含义。比如f(x),在函数语境里是“函数 f 作用于 x”,在某个概率语境里可能是“概率密度函数 f 在 x 处的取值”。单看符号无法确定含义,必须依赖周围文本、前文定义甚至学科领域。
在技术上,语义层最常见的产物是 Content MathML,它不再关心公式怎么排版,而是直接标记divide、apply、ci(标识符)、cn(数字)这类语义元素。这也是 W3C 把 MathML 分成 Presentation 和 Content 两个体系的原因:一个是语法表达,一个是语义表达。
1.3 为什么这个区分对工程落地很关键
如果你只是想把公式从 PDF 里提取出来展示,那语法层就够用了,识别成 LaTeX 然后渲染出来,流程简单直接。
但如果你想做公式检索、公式相似度匹配、数学知识库、让模型理解和推导数学问题,只到语法层就会处处碰壁。一个典型现象是:同一个公式,在论文里可能被排版成不同样式,甚至有的用连字符-代替负号−,有的用 ASCII 的x代替数学斜体𝑥。语法层面它们长得不一样,语义层面却完全一样。如果不做语义归一化,检索系统就会把等价的公式当成不同内容处理。
所以我一直认为,做公式理解项目之前,必须先明确目标落在哪个层次。这个决定会影响后续的格式选型、标注方案、评估指标和预算分配,比选哪个模型重要得多。
2. 一条从文档到公式语义的完整处理链路
2.1 第一环:公式检测(公式在哪儿)
公式理解的第一步不是识别,而是定位。你要在文档页面上判断哪些区域是公式,哪些是正文文字,哪些是表格、图片或噪声。这一步通常叫公式检测(formula detection)。
公式检测的难点有两类。一类是行内公式和独立公式的混合:独立公式独占一行,检测相对容易;行内公式嵌在正文里,比如x = 1出现在一句话中间,容易被拆散或被当成普通文字。另一类是复杂页面的干扰:双栏排版、脚注、页眉页脚、图片里的注释文字,都会让检测模型产生误判。
从实操角度看,我建议先不要追求“一键全自动”。先用 20 到 50 页不同来源的文档做一次公式检测测试,重点看两个指标:漏检率(该检测出的公式没测出来)和误检率(把普通文本、表格编号或图片内容当成公式)。这两个数字和你的文档来源质量强相关,不要用公开数据集的结果直接代替实测。
2.2 第二环:公式识别(把图像变成结构化表达)
检测出公式区域之后,下一步是把公式图像或区域内容转换成结构化表达式,通常是 LaTeX 或 MathML。这一步在学术界叫光学公式识别(Optical Formula Recognition,OFR),也是现有工具相对成熟的环节。
识别结果的好坏,直接影响后续语义处理。一个很常见的经验是:识别错误大部分不是“整体失败”,而是局部错误,比如把下标i识别成1,把∑的上限位置放错,或者把sin识别成5in。这些错误在视觉上可能不明显,但在语法层和语义层都会产生连锁问题。
所以第二个建议是:识别阶段不要只看“整行匹配率”,要按错误类型统计。常见的错误类型包括:
- 字符混淆:数字与字母、相似符号、Unicode 字形差异。
- 结构错误:上下标层级、分数线位置、根号覆盖范围。
- 语法非法:生成了无法编译的 LaTeX,比如括号不配对。
2.3 第三环:从结构到语义的映射
公式识别输出的是语法结构,距离“理解”还有一步,就是语义映射。这一步要把\frac、\sum、\int这类结构节点转换成数学含义:分数变除法、求和变 apply、变量变标识符。
目前文本领域常把这类任务称为“公式语义解析”或“数学表达式解析”。做法大致分两类:基于规则的方法适合处理确定性的运算符和结构,比如分数、根号、上下标;基于模型的方法更适合处理需要上下文推断的部分,比如函数名、变量含义、算子优先级。更稳健的方案是把两者结合:先用规则做结构到基本语义的转换,再用模型处理歧义和上下文相关的部分。
这里有一个很重要的边界原则:语义映射不是越“深”越好。如果要做公式相似度检索,可能只需要把公式标准化成一种语义树;如果要做数学推理,还需要额外的定理、公理和推导规则;如果只是做展示,那语义映射可以完全不做。先定目标,再定映射深度,否则很容易陷入标注不完、效果无法评价的困境。
2.4 用什么判断每一步做对了
判断链路每个环节是否合格,不能只看最终能否跑通,要分环节设置验收标准:
- 检测环节:公式级别的精确率和召回率,尤其要单独统计行内公式的漏检率。
- 识别环节:LaTeX 字符串的字符正确率、结构树级别的配对正确率、以及生成结果能否被渲染器正常编译。
- 语义环节:语义树是否等价于人工标注的标准语义树;如果做检索,还要看检索结果的排序质量。
这套验收体系看起来繁琐,但能帮你快速定位问题。比如最终检索效果差,不一定是因为语义模型不行,很可能是前面识别阶段就把\frac结构识错了。
3. 公式表示格式选型与工具落地
3.1 三种常见表示:LaTeX、Presentation MathML、Content MathML
工程落地时,公式在系统内部到底用什么格式表达,是一个绕不开的决策点。三种常见选择各有适用场景:
| 表示格式 | 主要描述对象 | 优点 | 不足 |
|---|---|---|---|
| LaTeX | 排版语法 | 易读、工具生态丰富、存储体积小 | 同一个公式写法多样,不利于规范比对 |
| Presentation MathML | 排版结构 | 结构化、便于程序处理、适合做渲染 | 冗长、语义信息少 |
| Content MathML | 语义结构 | 表达数学含义,适合检索和推理 | 书写繁琐,部分语法需要专门工具支持 |
我的建议是:如果系统里同时存在识别、渲染、检索三类需求,就不要只在 LaTeX 和 Content MathML 之间二选一。更常见的做法是内部都保留三种转换能力:识别原始输出是 LaTeX,存储和展示用 LaTeX 或 Presentation MathML,检索和知识库索引用 Content MathML 或自定义语义树。三者之间通过转换器衔接。
3.2 工具和库怎么选
公式识别和渲染相关的开源工具不少,但选型时要关注的不是“哪个效果好”,而是“哪个在你的文档场景里稳定”。我在选型时会按这个顺序考察:
- 输入格式:支持图片格式、PDF 页面还是完整文档;是离线批量处理还是在线实时请求。
- 输出质量:对数学教材、论文原文、扫描件、拍照件这四类输入的字符正确率和结构正确率分别如何。
- 运行条件:显存占用、内存占用、单条公式推理耗时、并发处理能力。
- 接口复杂度:是否有现成 API、能否嵌入现有处理管线、输出是否方便二次清洗。
务必注意,公开榜单上的成绩通常来自高质量数据集,实际业务里的公式可能来自低分辨率截图、倾斜扫描页、或者被批注遮挡。换一个数据分布,效果差距会很大。所以选型阶段一定要拿自己的真实文档样本做评测,而不是看别人报告的指标。
3.3 环境与依赖的注意事项
公式处理的不少工具依赖特定的 Python 版本、PyTorch 版本或者渲染环境,安装时容易踩坑。我遇到过几类比较常见的情况:
- LaTeX 渲染器缺失,导致公式识别结果无法可视化,排查时看不到真实输出。
- 模型权重文件路径配置错误,程序启动时没有报错,但推理结果全是默认值或空结果。
- Unicode 字体和数学符号字体缺失,某些特殊符号(比如花体、双线体字母)在渲染和转换时显示为方框。
- 内存不足,批量处理时 OOM,但错误信息被日志系统吞掉,表现为进程卡住而不是直接报错。
所以第一次运行时,别急着处理全部文档。先准备一个包含 10 条不同难度公式的最小测试集,跑通检测、识别、渲染、转换四步,确认每一步都有可见输出,再扩大到全量数据。这样能省下大量排查问题的时间。
4. 批量处理公式时的工程化问题
4.1 批量任务最常踩的坑
单个公式跑通之后,很多同学会直接开批量。这里我非常建议先停下来想几个问题:你的批量任务是否有失败重试机制?一个文档处理失败了,是跳过还是终止整个任务?输出文件如何命名,会不会互相覆盖?日志能不能告诉你哪一条公式在哪个环节失败?
这些问题在演示环境里不会暴露,但真正处理成百上千篇论文时就会成为瓶颈。批量处理最常见的一种情况是:某个页面的公式检测一直失败,导致整篇文档后续全部中断。解决思路不是提高模型精度,而是先把任务队列和失败隔离做好——单页失败不影响整篇文档,单篇失败不影响整个批次。
4.2 输出命名、失败重试与日志
批量任务的工程规范,我总结为三件事:
- 输出命名要可溯源。每条公式输出都应该携带文档 ID、页码、公式序号,这样下游发现问题时能快速回到原始位置。
- 失败重试要分级。是输入格式问题、资源不足问题,还是模型本身输出为空,不同原因对应不同重试策略。不要无脑重试三次,那样只会耗尽资源。
- 日志要记录中间状态。至少记录检测阶段筛掉了多少区域、识别阶段有多少条生成结果无法通过语法校验、语义阶段有多少条无法映射。每一层的数量变化能告诉你瓶颈在哪。
我记得有一次排查公式检索系统效果差,最终发现是识别阶段的高错误率被“整体处理成功”的假象掩盖了——每页都能输出 LaTeX 字符串,但大量公式的局部字符是错的,导致后续语义索引全部失真。没有分阶段日志,这类问题很难定位。
4.3 公式检索和知识库场景怎么验收
如果你的目标是把公式接入检索系统或知识库,验收标准就不是“识别成功”,而是“用户能否找到想要的那个公式”。
建议用几类典型 query 做验收:
- 精确匹配:输入
E=mc^2,能否找到对应公式。 - 语义等价:输入一个写法不同的等价公式,能否找到同一数学含义的其他形式。
- 相似查询:输入
ax^2+bx+c=0,能否返回类似的二次方程。 - 上下文查询:输入“牛顿第二定律”,能否通过文本与公式的关联定位到
F=ma。
这里有个很容易犯的错误:公式检索只做字符串相似度匹配,结果往往很差。原因是 LaTeX 写法差异大,换一个排版风格匹配分就急剧下降。正确做法是先做语义归一化,再基于语义树或语义向量做匹配。同样地,做 RAG 时不要把公式当成普通文本切片,公式的语义依赖结构层级,直接切文本会把\frac{1}{2}切成两段,导致检索完全失效。
5. 常见错误与排查链路
5.1 先区分:是语法错,还是语义错
公式处理系统报错或者结果异常时,第一件事不是改模型,而是判断问题出在语法层还是语义层。
语法层的问题通常表现为:LaTeX 编译失败、括号不匹配、结构树解析异常、某些符号无法被解析器识别。这类问题常常可以通过校验规则快速拦截。比如检查公式字符串的括号配对、检查\begin和\end是否对应、检查分数分母是否为空,类似程序里的“无效输入语法”报错,多数是输入本身的格式问题。
语义层的问题表现则不同:结构完全合法、渲染正常,但含义不对。比如把log(x)识别成了1og(x),从语法检查看没有任何错误,但语义上完全变了。这类问题只能靠语义验证,比如代入数值验证、与人工标注对比、或者检查变量是否在全文中有定义。
5.2 从日志到输入的排查顺序
当公式理解链路出现异常,我的排查顺序通常是固定的:
- 先复现单条问题,看是整个链路失败还是某个环节失败。
- 看输入原始形态:是图片质量差、PDF 渲染问题,还是原始 LaTeX 就包含非法字符。
- 看中间输出:检测框是否覆盖正确区域,识别结果是否满足基本结构合法性。
- 看语义映射记录:映射过程中有多少节点被丢弃或无法解析。
- 最后才考虑模型参数和工具版本:是不是依赖版本不对、并发太高导致资源不足,或者模型调用方式不对。
这个顺序的核心逻辑是:从最底层、最容易确认的环节开始排查。很多所谓“模型效果差”的问题,实际查下来是输入格式不规范或路径配置错误,白白浪费了调参数的时间。
5.3 歧义:同一个符号在不同上下文里含义不同
公式语义处理里最棘手的是歧义。同一个符号或同一段结构,在不同学科、不同上下文里的含义可能完全不同。举几个常见例子:
d可以表示微分算子,也可以表示变量或直径。e可以表示自然常数,也可以表示变量名。- 竖线
|在集合里表示“满足条件”,在绝对值里表示取模,在行列式里表示矩阵规模。 sin(x)标准写法是正弦函数,但如果上下文是文本叙述,也可能出现特殊缩写含义。
解决歧义不能只靠公式本身,需要把公式所在的句子、段落甚至整篇论文的符号定义表纳入处理范围。我的做法是把公式语义解析设计成两阶段:先用语法层和局部结构做初步解析,再把上下文信息作为第二阶段的输入做消歧。如果项目时间有限,至少要对高频歧义符号建立规则库,能明显提升稳定性和可解释性。
6. 我个人建议的落地节奏
6.1 从小样本开始,别一上来就铺全量
很多团队做公式理解项目,第一个月就在追求端到端的高精度,结果数据标注、模型训练、工程联调全部挤在一起,什么问题都说不清楚。
我更建议的节奏是:先拿 50 到 100 条真实公式做全链路手工走查,确认每一步的输入输出都正常,然后对小批量文档做一轮自动化处理,统计各环节的错误分布。这个阶段不追求算法指标,只追求“系统是可用且可观测的”。能白盒地看到每一条公式是如何被处理的,比有一个漂亮的指标重要得多。
6.2 语法和语义分阶段验证
工程上最忌讳用一个端到端模型把所有问题包起来。识别和语义映射混在一起,出了问题根本不知道改哪里。
实际操作中,我会把系统拆成几个独立验证的模块:
- 识别模块:单独看字符准确率和结构准确率,构造一个只包含标准公式的评测集。
- 语法校验模块:验证所有输出能否通过 LaTeX 编译检查。
- 语义映射模块:用人工标注的语义树对照检查映射正确率。
- 检索或知识库模块:用业务 query 做端到端效果评估。
每个模块独立验收,再串联。这样任何一个环节效果不佳,都能快速定位并单独优化。
6.3 别忽视后处理与人工复核
最后说一个容易被忽略但很重要的环节:后处理和人工复核。
公式识别不是一次完事,后处理能解决很多问题。比如把 Unicode 字符统一成规范的数学符号、把空格和字体差异归一化、对常见易错字符按上下文进行规则修正、对无法确认的公式标记为“待人工确认”。这些规则不酷,但能大幅减少下游的无效处理。
对于高价值的数据集,人工复核仍然是必要的。尤其在语义标注阶段,完全自动化的标注结果往往带有系统性偏差。我一般会让标注员复核每种公式类型的首 20 条结果,确认标注口径一致后,再对自动结果做抽样抽查。这个过程的成本不高,但对最终质量的影响非常大。
公式理解这个领域,表面上是模型问题,实质上是工程问题。先把语法层和语义层分开对待,再按检测、识别、映射、检索的顺序逐步落地,比盲目追逐某个“SOTA 模型”靠谱得多。如果你正准备做类似项目,建议先拿着自己的真实数据跑一遍完整链路,大概率会发现,真正的瓶颈早在你预期之前就出现了。