olmOCR-Bench 数学教材页样本解析:从 OpenStax 微积分习题页到机器可判定的 OCR 评测事实
【免费下载链接】olmocrToolkit for linearizing PDFs for LLM datasets/training项目地址: https://gitcode.com/GitHub_Trending/ol/olmocr
这份指南围绕 olmOCR-Bench 中一份真实样本输出 openstax_caculus_pg_273_pg1_repeat1.md 展开:它来自 OpenStax 微积分教材第 273 页,是数学公式密集的典型教材页面经 olmOCR 流水线线性化后的 Markdown 结果。通过逐行拆解这份样本、追溯它的生成流水线与评测逻辑,读者可以完整掌握 olmOCR-Bench 如何把「页面是否被正确识别」转化为可机器判定的布尔事实(fact),并理解数学公式、阅读顺序、页眉页脚等维度在评测中的具体判定机制,进而有能力自行把任意 OCR 工具接入该基准。
1. 样本文件是什么:一份「可直接判定」的 OCR 线性化输出
olmOCR-Bench 的评测粒度是单页 PDF。每个 PDF 页面经 OCR 流水线处理后,会生成一个同名 Markdown 文件,文件名遵循{pdf基名}_pg{页码}_repeat{重复次数}.md的约定(对应 convert.py 中的命名逻辑)。这份样本的路径为:
olmocr/bench/sample_data/olmocr_pipeline/openstax_caculus_pg_273_pg1_repeat1.md其含义是:
| 片段 | 含义 |
|---|---|
openstax_caculus_pg_273 | 源 PDF 文件名,即 OpenStax《Calculus》第 273 页 |
pg1 | 本文件对应 PDF 的第 1 页(页码从 1 开始) |
repeat1 | 第 1 次重复生成(通过--repeats N可生成多次,用于估计稳定性) |
所在目录olmocr_pipeline | 生成该输出的评测候选(candidate)工具,即官方 olmOCR 流水线 |
该文件存放在 sample_data/olmocr_pipeline/ 目录下,与dataset.jsonl、blanks.jsonl一起构成仓库内置的迷你评测集(见 olmocr/bench/sample_data/)。评测时,基准(benchmark)会读取 JSONL 中定义的每条「事实」测试,并针对该 PDF 对应的.md文件逐一判定通过与否。
2. 逐段拆解:一份数学教材页面的线性化结果
该样本是 OpenStax 微积分教材「3.4 EXERCISES」一节(第 273 页)的完整 OCR 输出,全部内容如下(与原文件逐字一致):
3.4 EXERCISES
For the following exercises, the given functions represent the position of a particle traveling along a horizontal line.
a. Find the velocity and acceleration functions.
b. Determine the time intervals when the object is slowing down or speeding up.
150.( s(t) = 2t^3 - 3t^2 - 12t + 8 )
151.( s(t) = 2t^3 - 15t^2 + 36t - 10 )
152.( s(t) = \frac{t}{1 + t^2} )
153.A rocket is fired vertically upward from the ground. The distance ( s ) in feet that the rocket travels from the ground after ( t ) seconds is given by ( s(t) = -16t^2 + 560t ).
a. Find the velocity of the rocket 3 seconds after being fired.
b. Find the acceleration of the rocket 3 seconds after being fired.
154.A ball is thrown downward with a speed of 8 ft/s from the top of a 64-foot-tall building. After ( t ) seconds, its height above the ground is given by ( s(t) = -16t^2 - 8t + 64 ).
a. Determine how long it takes for the ball to hit the ground.
b. Determine the velocity of the ball when it hits the ground.
155.The position function ( s(t) = t^2 - 3t - 4 ) represents the position of the back of a car backing out of a driveway and then driving in a straight line, where ( s ) is in feet and ( t ) is in seconds. In this case, ( s(t) = 0 ) represents the time at which the back of the car is at the garage door, so ( s(0) = -4 ) is the starting position of the car, 4 feet inside the garage.
a. Determine the velocity of the car when ( s(t) = 0 ).
b. Determine the velocity of the car when ( s(t) = 14 ).
156.The position of a hummingbird flying along a straight line in ( t ) seconds is given by ( s(t) = 3t^3 - 7t ) meters.
a. Determine the velocity of the bird at ( t = 1 ) sec.
b. Determine the acceleration of the bird at ( t = 1 ) sec.
c. Determine the acceleration of the bird when the velocity equals 0.
157.A potato is launched vertically upward with an initial velocity of 100 ft/s from a potato gun at the top of an 85-foot-tall building. The distance in feet that the potato travels from the ground after ( t ) seconds is given by ( s(t) = -16t^2 + 100t + 85 ).
a. Find the velocity of the potato after 0.5 s and 5.75 s.
b. Find the speed of the potato at 0.5 s and 5.75 s.
c. Determine when the potato reaches its maximum height.
d. Find the acceleration of the potato at 0.5 s and 1.5 s.
e. Determine how long the potato is in the air.
f. Determine the velocity of the potato upon hitting the ground.
158.The position function ( s(t) = t^3 - 8t ) gives the position in miles of a freight train where east is the positive direction and ( t ) is measured in hours.
a. Determine the direction the train is traveling when ( s(t) = 0 ).
b. Determine the direction the train is traveling when ( s(t) = 0 ).
c. Determine the time intervals when the train is slowing down or speeding up.
159.The following graph shows the position ( y = s(t) ) of an object moving along a straight line.
Graph of position function
a. Use the graph of the position function to determine the time intervals when the velocity is positive, negative, or zero.
b. Sketch the graph of the velocity function.
c. Use the graph of the velocity function to determine the time intervals when the acceleration is positive, negative, or zero.
d. Determine the time intervals when the object is speeding up or slowing down.
这份输出体现了 olmOCR 对数学教材页的关键处理特征:
2.1 数学公式使用\( ... \)定界符
页面上所有内联公式(如 ( s(t) = 2t^3 - 3t^2 - 12t + 8 )、( s(t) = \frac{t}{1 + t^2} ))都以\(与\)包裹。这正是 olmOCR-Bench 认可的四种数学定界符之一($、$$、\(、\[,见 olmocr/bench/README.md)。在评测时,MathTest会先用正则把这些定界符包裹的内容抽取出来,再做公式级比较(详见第 5 节)。
2.2 图片以 Markdown 占位符表示
习题 159 中的函数图像被线性化为Graph of position function占位符,而不是试图把图像「识别成文本」。这与基准的设计原则一致:评测关注的是文本与公式层面的事实,图像本身不作为 OCR 文本的判定对象(仅在BaselineTest中,可通过max_length_skips_image_alt_tags决定是否把图片 alt 文本计入页面长度)。
2.3 题号与阅读顺序
页面保留了「150.」到「159.」的题号与多级小题编号(a./b./c.)。这看似平凡,却是评测阅读顺序的天然锚点——下一节可以看到,dataset.jsonl 正是利用「150.」出现在「157.」「158.」「159.」之前这一事实来验证页面的自然阅读顺序。
3. 这份样本是怎么生成的:convert.py 与 olmocr_pipeline Runner
样本所在的olmocr_pipeline/目录对应评测候选工具「olmocr 官方流水线」。仓库中与这份输出直接相关的生成链路是:
python -m olmocr.bench.convert olmocr_pipeline --dir ./olmOCR-bench/bench_data3.1 convert.py 的职责
convert.py 以「方法名[:key=value...]」的形式接收要运行的候选工具(如olmocr_pipeline、gotocr、marker、mineru、chatgpt、gemini、mistral等,完整映射见 convert.py),随后为--dir下pdfs/目录中的每个 PDF、每个页面、每个 repeat 生成一个.md输出。其关键命令行参数如下:
| 参数 | 默认值 | 说明 |
|---|---|---|
methods | 必填 | 要运行的候选方法,支持method:key=value追加参数,如marker:u=3、name=自定义文件夹名 |
--dir | sample_data | 数据目录,其中必须含pdfs/子目录存放源 PDF |
--repeats | 1 | 每个页面重复生成的次数,多次生成可评估稳定性 |
--force | False | 输出文件已存在时是否强制重新生成 |
--parallel | 1 | 最大并发任务数,0表示完全串行 |
--remove_text | False | 先对 PDF 截图再 OCR,以抹掉数字文本层(会禁用 olmOCR 的 doc-anchoring) |
--failfast | False | 页面处理抛异常时立即终止并打印完整堆栈 |
输出文件即按第 1 节所述规则命名(convert.py),并保留pdfs/下的相对子目录结构。
3.2 Runner 内部:调用真实 pipeline 的process_page
run_olmocr_pipeline.py 是「olmocr_pipeline」候选的实现:它以allenai/olmOCR-2-7B-1025-FP8为默认模型,先探测/启动 vLLM 推理服务(vllm_server_ready与vllm_server_host),然后调用olmocr.pipeline的process_page处理单页 PDF,最终返回page_result.response.natural_text——即模型产出的自然文本(Markdown)。若处理失败或触发 fallback,runner 返回None,convert 会写出空文件,使其在评测中如实记为失败而不是被跳过。
4. 样本如何被评测:dataset.jsonl 中的「事实」
评测的核心思想是把每个断言当作类似单元测试的「事实」。仓库内置评测集 dataset.jsonl 中,针对openstax_caculus_pg_273.pdf第 1 页定义了 5 条测试:
{"pdf": "openstax_caculus_pg_273.pdf", "page": 1, "id": "openstax_caculus_pg_273_minediff_02", "type": "present", "checked": "verified", "text": "Use the graph of the position function to determine the time intervals when the velocity is positive, negative, or zero."} {"pdf": "openstax_caculus_pg_273.pdf", "page": 1, "id": "openstax_caculus_pg_273_minediff_03", "type": "present", "checked": "verified", "text": "Use the graph of the velocity function to determine the time intervals when the acceleration is positive, negative, or zero."} {"pdf": "openstax_caculus_pg_273.pdf", "page": 1, "id": "openstax_caculus_pg_273_minediff_04", "type": "order", "before": "150.", "after": "157."} {"pdf": "openstax_caculus_pg_273.pdf", "page": 1, "id": "openstax_caculus_pg_273_minediff_05", "type": "order", "before": "150.", "after": "158."} {"pdf": "openstax_caculus_pg_273.pdf", "page": 1, "id": "openstax_caculus_pg_273_minediff_06", "type": "order", "before": "150.", "after": "159."}可见这两类测试正好覆盖了样本的两大特征:
- present(文本存在)测试:验证第 159 题的两条关键题干确实出现在页面某处。这是从原文长句抽取的整句级断言,与样本中第 159 题的 a、c 小题文字完全吻合,属于「minediff」类(由
mine_diffs.py等挖掘脚本产出、经人工checked: "verified"复核的测试)。 - order(阅读顺序)测试:验证「150.」必须出现在「157.」「158.」「159.」之前。这是对教材页面自然阅读顺序(按题号递增)的机器可判定表达。
TextOrderTest的实现使用fuzzysearch.find_near_matches在归一化后的全文里分别定位 before/after 文本,只要存在任意一对before_match.start < after_match.start即通过(见 tests.py)。
两条 present 断言在样本中都能精确命中,三条 order 断言也因题号顺序正确而全部成立——这正是这份样本作为「合格候选输出」的体现。
5. 数学公式如何被判定:MathTest 与 KaTeX 渲染
对于数学公式,olmOCR-Bench 不依赖文本编辑距离,而是采用「相对符号级」的结构比较,这一点在 README 的 Benchmark Principles 中阐述得很清楚:例如把 ( 3x^2 ) 输出成 ( \int_a^b 3x^2 dx ) 仍然算命中,因为符号的相对位置关系成立。
5.1 MathTest 的判定流程
MathTest 的run流程是:
- 用正则从候选输出中抽取
\(...\)、\[...\]定界符内的公式(默认还包含$$...$$与$...$,可通过ignore_dollar_delimited关闭); - 若某个抽取的公式与测试的
math字符串完全一致,直接通过(快速路径); - 否则按模糊相似度对页面上所有公式排序,逐个用 KaTeX 渲染后与参考公式的渲染结果做结构比较;
compare_rendered_equations返回 True 即通过。
5.2 渲染与比较的底层实现
katex/render.py 基于 Playwright 无头 Chromium 加载仓库自带的 katex.min.js 与 katex.min.css(render_equation,见 render.py)。渲染结果包含两部分:
- MathML 输出:直接从
.katex-mathml math元素抽取,比较时先做去语义标注、去空白归一化,若假设公式的 MathML 内嵌于参考公式的 MathML,则直接判定命中; - 最内层 span 及其包围盒:每个不可再分的符号(如
∫、x、dx)带有一个坐标盒。compare_rendered_equations(render.py)随后对符号序列做回溯式匹配:为参考公式的每个符号在假设公式中找同文本候选,并强制要求上/下/左/右邻居关系一致——这实现了「∫ 在 x 左边、x 在 dx 左边」这类相对符号级判定。
另外,render.py内置了基于 SHA1 哈希的 SQLite 缓存(EquationCache,默认位于~/.cache/olmocr/bench/equations/cache.db),重复公式无需反复启动浏览器渲染,并默认用 8 线程的ThreadPoolExecutor并行渲染。
6. 评测前的归一化与模糊匹配规则
所有文本类测试都会先经过 normalize_text 处理,这决定了 OCR 输出的「容差边界」:
- 将
<br>/<br/>归一为空格; - 剥离 Markdown 加粗(
**...**、__...__)与斜体(*...*、_..._)标记,以及<b>/<i>等 HTML 标签——因此输出**enlightenment**仍算命中enlightenment; - 压缩所有连续空白为单个空格;
- 统一 Unicode 为 NFC 形式(
é与e+ 组合重音等价); - 将各类花式引号/破折号映射为 ASCII 等价物:
‘’‚→',“”„→",–—‑‒−→-,µ→μ(希腊字母 mu)。
在此基础上,TextPresenceTest 通过rapidfuzz的partial_ratio计算模糊匹配度,并用max_diffs换算阈值:threshold = 1.0 - max_diffs / len(text),当相似度 ≥ 阈值时 present 通过 / absent 不通过。测试还支持:
case_sensitive:默认True,可设为False忽略大小写;first_n/last_n:把搜索范围限制在文档开头 N 个或结尾 N 个字符,常用于页眉页脚(如"last_n": 20检查页码是否出现在页尾)。
7. 底线检查:BaselineTest 与空白页
除了面向具体内容的测试,每个 PDF 页面还默认附加一条 BaselineTest(由 benchmark.py 自动补全),用于保证输出的基本质量:
- 输出不含任何字母数字字符 → 失败;
- 结尾出现大量重复 n-gram(通过
olmocr.repeatdetect.RepeatDetector检测,默认max_repeats=30)→ 失败; - 出现中日韩统一表意文字、日文假名、emoji 等「不应出现」的字符集 → 失败。
空白页场景由 blanks.jsonl 覆盖:
{"pdf": "blank_book_pg1.pdf", "page": 1, "id": "test1_blank", "type": "baseline", "checked": "verified", "max_length": 10}即对预期为空白页的 PDF,若 OCR 输出的字母数字字符数超过max_length=10则判失败(可配合max_length_skips_image_alt_tags排除图片 alt 文本)。基准在计算总得分时,将每个 JSONL 文件的通过率取平均作为最终分(见 benchmark.py),并输出基于分组 bootstrap 的 95% 置信区间与逐类型(present/order/table/math/absent/baseline 等)的通过率明细。
8. 端到端运行:复现这条样本的评测
在本地复现这份样本从「转换」到「评分」的完整流程如下(参见 README 下载与运行章节):
# 1. 安装环境(bench 依赖 + 数学渲染所需的无头浏览器) conda create -n olmocr python=3.11 conda activate olmocr pip install -e .[bench] playwright install chromium # 2. 下载基准数据(含 PDF 与 JSONL 标注) huggingface-cli download --repo-type dataset --resume-download allenai/olmOCR-bench --local-dir ./olmOCR-bench # 3. 用 olmocr 流水线转换(GPU 推理需先安装 [gpu] 依赖) python -m olmocr.bench.convert olmocr_pipeline --dir ./olmOCR-bench/bench_data # 或走完整 pipeline 再整理成基准格式 python -m olmocr.pipeline ./localworkspace --markdown --pdfs ./olmOCR-bench/bench_data/pdfs/**/*.pdf python olmocr/bench/scripts/workspace_to_bench.py localworkspace/ olmOCR-bench/bench_data/olmocr --bench-path ./olmOCR-bench/ # 4. 运行评测 python -m olmocr.bench.benchmark --dir ./olmOCR-bench/bench_databenchmark还支持若干实用参数:--candidate只评测单个候选、--skip_baseline跳过底线检查、--bootstrap_samples/--confidence_level控制置信区间、--sample N随机抽样、--test_report results.html输出 HTML 报告、--output_failed导出在所有候选上都失败的测试。如需在 Beaker 集群上规模化运行(含多 repeat 与性能测试任务),可参考 scripts/run_benchmark.sh(支持--model、--repeats、--benchrepo、--benchpath、--max-tokens等参数)。
9. 小结
从openstax_caculus_pg_273_pg1_repeat1.md这一份小小的样本出发,可以串起 olmOCR-Bench 的完整设计:它把数学教材页面线性化为带\(...\)定界符的 Markdown,用「present/order/math/baseline」等布尔事实替代编辑距离这类软指标,靠 KaTeX 渲染实现相对符号级公式匹配,靠归一化与模糊匹配容忍合理差异,并以「每个 PDF 页面 → 每个 repeat 的 .md 文件 → 每条事实」三层结构给出可复现、可解释的评分。对于想要验证自家 OCR 工具的团队而言,这意味着:只要你的工具能输出 Markdown 或纯文本,就能以同样的方式被客观、机器可判定地检验。
【免费下载链接】olmocrToolkit for linearizing PDFs for LLM datasets/training项目地址: https://gitcode.com/GitHub_Trending/ol/olmocr
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考