ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

数学公式识别实战:从截图到LaTeX源码的完整工程

2026/10/1 18:14:14 拓冰建站 浏览量
数学公式识别实战:从截图到LaTeX源码的完整工程 简介这份资源以神经网络模型实现数学公式识别面向毕业设计、课程项目以及OCR方向初学者提供从模型搭建、训练到预测的完整参考。压缩包共76个文件大小约44.5MB主要包含Python源码、Jupyter Notebook演示、JSON配置、txt数据说明、GIF可视化结果和docx文档手册py文件覆盖模型结构、数据预处理、训练评估与预测推理ipynb方便分步调试json管理数据与模型参数txt存放公式数据集配套文档则解释实现思路和运行方式。目前已有146人学习浏览源码经本地编译可运行评审分达95分以上适合作为高分毕业设计的对照实现。使用者可获得完整可运行的识别系统、注意力可视化演示、评估脚本和说明文档有助于理解编码器-解码器结构、注意力机制及公式序列生成等关键知识点并快速迁移到自己的项目中。1. 数学公式识别一套把公式截图翻译成 LaTeX 源码的完整工程如果你手头有上百张公式截图想批量转成 LaTeX 源码或者毕业设计里需要一个“图像到序列”的完整项目这套 Python 实现的基于神经网络模型的数学公式识别源码就是为此准备的。它并不只是训练一个 CNN 做分类而是把整条链路都打通了卷积神经网络负责从图片提取视觉特征LSTM 循环网络负责按公式语法逐个 token 生成 LaTeX 串中间用注意力机制对齐图片区域和符号。解压 zip 后能直接跑训练、预测、注意力可视化适合做毕业设计、课程设计也适合第一次接触 seq2seq 结构的人当练手工程。相比 MNIST 数字识别公式识别真正难在结构化语法和长序列对齐这也是这个项目最有价值的部分。2. 先看懂 LaTeX OCR 的骨架编码器-解码器结构与数据组织2.1 CNN 编码器 LSTM 解码器公式识别为什么用“视觉-序列”结构公式识别和普通 OCR 最大的区别在于公式不只是符号的堆叠它是有语法结构的上下标、分式、求和符号的上下界、根号里的内容都必须被解析成正确的嵌套关系。如果只是把图片里的每个字符单独识别出来再拼在一起\frac { 1 } { 2 }和1 / 2这类等价但语义不同的表达就没办法正确处理。因此这个项目采用了经典的图像转序列image-to-sequence结构CNN 编码器把公式图片压缩成一系列视觉特征LSTM 解码器逐个 token 地生成 LaTeX 代码注意力机制负责在每一步决定“当前该看图片的哪个区域”。从根目录的源码看这种结构落在几个核心文件里LaTeX_OCR-master/ ├── encoder.py # CNN 编码器 ├── decoder.py # LSTM 解码器 ├── img2seq.py # 编码-解码整体流程 ├── components/ │ ├── base.py │ ├── base_torch.py │ └── img2seq_torch.py # PyTorch 版完整实现 ├── train.py # 训练入口 ├── predict.py # 单张图片预测 ├── evaluate_img.py # 批量评估图片 └── visualize_attention.py # 注意力可视化代码结构上项目里同时保留了img2seq.py和components/img2seq_torch.py两套实现前者偏轻量、适合快速读逻辑后者是更完整的 PyTorch 版本。如果你是在校生做毕业设计答辩时能把这两套实现的差别讲清楚反而是加分项一套让你看懂原理一套让你跑出结果。CNN 这里不是做分类头而是做特征提取器最后的全连接层输出会被 reshape 成“时间步 × 特征维度”的形式喂给 LSTM这是这类模型最常见的对接方式。2.2 数据文件长什么样formulas.norm.txt 与 vocab.json 的分工解压后进入data/目录你会看到train.formulas.norm.txt、val.formulas.norm.txt和test.formulas.norm.txt三个文件。这个命名方式源自 im2latex 数据集norm表示已经做过规范化处理。每条公式一行没有$包裹符空白已经被压缩形如$ head -n 3 data/train.formulas.norm.txt \frac { a } { b } \sum _ { i 1 } ^ { n } x _ { i } \lim _ { x \rightarrow 0 } \frac { \sin x } { x } \sqrt [ 3 ] { x ^ { 2 } y ^ { 2 } }这几行是常见做法下的标准格式示例实际数据集里每行就是一个 LaTeX 表达式字符串。公式字符串不能直接喂给模型需要先转成 token 序列这就是configs/vocab.json的作用把\frac、\sum、_、^这类命令和符号映射成整数索引。用 Python 读取词典时可以看到它的基本结构import json with open(configs/vocab.json, r, encodingutf-8) as f: vocab json.load(f) # 打印前 10 个 token具体内容以你下载的 vocab.json 为准 for i, (token, idx) in enumerate(list(vocab.items())[:10]): print(token, -, idx)这段代码的作用是确认你的词典能正常加载顺便看一眼 token 到索引的映射方式。vocab.json是训练和预测共用的这意味着训练时用哪份词典预测时也必须用同一份否则模型输出的索引在另一份词典里可能对不上号。项目里还有vocab_small.json对应小规模数据的快速验证后面避坑章节会专门说这个文件的使用场景。2.3 配置文件体系data.json、model.json、training.json 各管哪一段这个项目把配置拆成了几个 JSON 文件这个设计思路比把所有参数硬编码在脚本里要清晰得多。简单说它们的边界是配置文件负责内容常见需要改的参数configs/data.json数据集路径、训练/验证集划分、图片目录train_path、val_path、image_dirconfigs/model.json模型结构超参数编码器卷积核数、解码器隐藏维度、LSTM 层数configs/training.json训练策略参数批大小、学习率、checkpoint 保存路径configs/vocab.json词典映射一般不改除非重建词典用一段小脚本把model.json打开就能看到你即将训练的模型到底长什么样import json with open(configs/model.json, r, encodingutf-8) as f: model_config json.load(f) print(json.dumps(model_config, indent2, ensure_asciiFalse))这段代码只是把配置打印出来让你在训练前对模型规模有个直观感受。重点看解码器隐藏维度和 LSTM 层数这两个参数直接决定显存占用和训练速度。隐藏维度 256 和 512 之间的训练耗时差距不是线性的因为每个时间步的矩阵乘法维度都跟着翻倍。项目还额外提供了training_small.json、data_small.json、vocab_small.json这一套小配置专门用来快速验证代码能不能跑通——这个习惯很好我自己做实验时也总是先跑小配置再上全量数据。3. 从零跑通训练与预测环境准备、数据预处理与三行命令3.1 环境搭建解压、虚拟环境与一键安装依赖老规矩先把环境弄干净。这个项目依赖 PyTorch、OpenCV、Pillow 和 numpy 这几个核心库requirements.txt里已经把依赖固定好了。我建议新建一个独立虚拟环境不要直接往系统 Python 里装不然日后跑其他项目时依赖冲突会折腾得很难受。如果你还在找 python 安装教程直接用 Anaconda 装好 Python 再创建环境是最省事的路径。# 解压项目 zip unzip Python实现基于神经网络模型的数学公式识别源码文档说明高分毕业设计.zip cd LaTeX_OCR-master # 创建并激活虚拟环境 python -m venv venv source venv/bin/activate # Windows 下用 venv\Scripts\activate # 安装依赖 pip install -r requirements.txt这里解压后进入的目录名称以你实际下载的文件名为准LaTeX_OCR-master是项目根目录的常见命名。安装依赖这一步如果网络状况不好可以考虑把 pip 换成国内镜像源速度会明显提升。依赖装完后项目根目录里带了一个makefile如果你的系统装了 make 工具也可以直接执行make查看它定义的任务通常会把预处理、训练、评估这些操作封装成简短命令。makemakefile 在 Linux 和 macOS 上默认可用Windows 下需要额外安装 make 工具或者直接忽略它、手动执行里面的 Python 命令。这一步的目的是让你快速了解项目作者预设的工作流不是必须依赖它。3.2 build.py 预处理把公式串和图片对齐成训练样本训练前必须先跑数据预处理这一步做的事是把formulas.norm.txt里的公式字符串转成 token 索引序列同时跟图片文件名建立一一对应的关系。build.py是这里的入口脚本python build.py当看到项目配置后它一般会根据data.json里的路径配置生成预处理产物输出到指定缓存目录。预处理产物包括每张图片经缩放后的张量、对应公式的 token 序列、每个 token 的索引和 mask。这套逻辑的作用是训练时不再需要每次都读原始图片和公式文本直接从预处理结果里取数据省掉了大量 IO 时间。我在跑这类项目时有个习惯预处理完先随机挑 3 到 5 个样本手动比对一下图片内容和对应的 token 序列确认它们真的对齐了。因为公式识别的训练样本一旦图片和文本错位模型学出来的东西就是乱的而且这种错误在 loss 曲线上常常看不出异常属于最危险的一类问题。数据划分上项目默认是训练集、验证集、测试集分开的比例通常在data.json里可以看到如果你想用自己的数据只需要按同样的格式替换公式文件和图片目录。3.3 train.py 训练入口日志怎么看、checkpoint 怎么存训练是整个项目里耗时最长的一步。启动命令本身不复杂python train.py --config configs/training.json不同分支的入口参数名可能略有差别核心逻辑是一致的读取training.json里的训练配置加载预处理好的数据开始迭代训练。训练过程中你会看到类似这样的日志输出每个 epoch 结束后打印平均 loss、验证集上的表现以及当前学习率。这里要特别提醒别只盯着 loss 一个指标。公式识别场景下loss 下降不代表模型真的学会了公式结构有可能它只是在反复输出eos或者高频 token 来蒙混过关。我一般会同时把验证集上生成的 LaTeX 串打印出来几条肉眼扫一眼语法是否正确。# 查看训练日志的最后 20 行 tail -n 20 training.log关于 checkpoint项目通常会在配置里指定保存路径每个 epoch 结束后保存一次模型权重。这里有个小建议不要只保留最后一个 epoch 的权重建议每个 epoch 都留一份。公式识别训练到后期验证集指标可能在第 8 个 epoch 就达到峰值第 10 个 epoch 反而因为过拟合开始退化。如果你只留了最后一个等于把最好的模型弄丢了。这个教训我踩过不止一次所以现在只要跑训练一定在配置里把保存间隔改小。3.4 predict.py 与 evaluate_img.py单张预测和批量评估训练完成后用单张图片试试效果python predict.py --image test.png # 或 python predict.py --image art/12.pngpredict.py的输入是一张公式截图输出是识别出的 LaTeX 字符串。根目录下那几个predict.png、6.png、14.png是作者留下的预测样例图跑完预测后可以拿它们对一下效果确认你的模型输出和原图内容是否匹配。如果输出里出现大量多余的花括号或者\_转义符通常说明训练数据的规范化方式和你喂进去的图片不一致。批量评估则用evaluate_img.pypython evaluate_img.py --data data/val.formulas.norm.txt它会读入一批图片和对应的真实 LaTeX 串跑完预测后给出整体指标常见的是 BLEU 分数和句级精确匹配率。BLEU 在公式识别里只能当参考因为它对公式这种严格语法结构的评价并不完全合理一个符号错了 BLEU 可能只扣很少的分但公式语义已经完全不同了。我更看重“句级精确匹配率”——真实值和预测值完全相同才算对这个指标虽然残酷但能真实反映模型实际可用的比例。4. 注意力可视化让模型说清楚它“看到”了什么4.1 visualize_attention.py把注意力权重画成 GIF公式识别模型是个不折不扣的黑匣子输入一张图片输出一段 LaTeX中间发生了什么很难感知。好在项目带了一个很好用的可视化入口visualize_attention.py能把解码器每个时间步的注意力权重叠加到原图上动态展示模型在生成每个 token 时目光落在哪里。运行方式也比较直接python visualize_attention.py --image art/12.png如果你打开的art/目录里那一堆 GIF比如visualization_12_short.gif、visualization_prediction_short.gif其实都是作者提前跑好的可视化结果短版 GIT 展示关键时间步长版逐帧展示全部解码过程。对毕业设计来说这个素材放进论文里的说服力远大于一张 loss 下降曲线因为它是模型注意力机制最直观的证据。4.2 从注意力热图判断训练状态三类典型的异常模式拿到热图后不要只看个热闹注意力分布的形态能透露训练状态。第一类是健康的注意力生成某个 token 时热区集中在该 token 对应的图片区域且随着解码步推进热区大体从左往右移动这是训练正常的信号。第二类是注意力涣散热区散布在图片的大片区域没有明显聚焦点通常说明模型没学会对齐出现这种情况要检查编码器输出的特征图尺寸是否太大或太小。第三类是注意力回跳热区在某几个位置反复跳动伴随生成重复的 token这在长公式上尤其常见解码器在长距离依赖上崩了。我的经验是训练每跑完几个 epoch 就随机抽 10 张图看一次注意力可视化比只看 loss 有效得多。有些模型 loss 一直降但注意力已经涣散最终预测出来的公式结构是乱的。把可视化脚本接进训练日志的定期流程里能尽早发现这类问题不用等整个训练跑完才发现模型是废的。项目根目录的architecture.jpg和visualize_attention.ipynb也可以配合看前者是模型结构图后者是交互式可视化 notebook适合在 Jupyter 里逐步回放注意力。4.3 用自己的截图做预测从 PNG 到模型输入的完整路径项目自带的图片能跑但你自己截的公式图能不能跑取决于图片能否被正确预处理。公式识别对输入图片的格式相当敏感一般是灰度图、白底黑字、背景干净、没有多余边框。我通常会写一段小脚本把截图统一处理成模型期望的格式import cv2 import numpy as np from PIL import Image def load_formula_image(path, target_sizeNone): # 读成灰度图 img Image.open(path).convert(L) # 统一转成 numpy 数组方便后续处理 arr np.array(img) # 如果图片是黑底白字就取反 if arr.mean() 128: arr 255 - arr # 按压四周空白避免符号贴边 coords np.argwhere(arr 128) if len(coords) 0: y0, y1 coords[:, 0].min(), coords[:, 0].max() x0, x1 coords[:, 1].min(), coords[:, 1].max() arr arr[max(y0 - 5, 0):y1 5, max(x0 - 5, 0):x1 5] # 按比例缩放到模型输入尺寸 if target_size is not None: h, w arr.shape[:2] scale target_size / max(h, w) arr cv2.resize(arr, (int(w * scale), int(h * scale))) return arr这个函数做的事情是灰度化、按需反色、边缘裁剪、等比缩放。其中反色这一步特别关键很多截图是白字蓝底或黑底白字而 im2latex 风格的数据集统一是白底黑字模型没见过反色图预测效果自然崩。裁剪留 5 像素的边距也是我试了很多次总结出来的经验符号贴到图片边界时 CNN 提取的特征会变形。处理完的数组直接转 tensor 就能送进predict.py对应加载逻辑里。5. 避坑指南公式识别训练里五个最典型的翻车现场5.1 训练 loss 不降反升或卡死在高位现象训练了好几个 epochloss 一直停在 4.0 以上不往下走甚至小幅上升。原因最常见的是学习率太大导致优化过程震荡其次是解码器在序列生成任务里出现梯度传播不稳定长序列的 loss 回传会把前面层的参数更新方向搅乱。解决把学习率降到 1e-3 以下重试同时检查训练配置里有没有max_grad_norm这一类梯度裁剪参数把它设置在 5.0 左右是这类模型常见的做法。如果降学习率后 loss 开始下降说明方向对了。5.2 预测结果全是eos或空串现象图片喂进去输出的 LaTeX 就一个eos或者把整段序列生成中断。原因训练时eos在 token 序列里占比过高模型学到“尽早结束”是损失最小化的策略尤其是当解码器容量不够、注意力建模不好时它更倾向于放弃生成。解决先确认训练数据里公式平均长度是不是过长把model.json里的最大序列长度适当压缩其次降低eos的过采样影响在 batch 采样时不要让短公式和空公式占据过大比例。从验证集里随机打印生成结果比盯 loss 更能暴露这个问题。5.3 换成自己的数据集后效果断崖式下跌现象拿项目自带数据训练效果不错换成自己采集的公式截图识别率瞬间从 80% 掉到 30%。原因大多是规范化没做到位。自带数据里formulas.norm.txt是经过严格清理的花括号和空格都被压成了固定模式而自己收集的 LaTeX 串可能混着\,\!、\quad这类排版命令模型没见过。解决把训练数据里所有的公式串做一遍统一清洗去除空白、统一花括号、删除不常见排版控制符最后跟现有vocab.json做一次 token 差集检查把超出词典的 token 要么替换、要么删掉。5.4 GPU 显存不足或训练到一半内存爆炸现象配置里写的是全量数据跑起来没过几个 batch 就 OOM。原因公式图片虽然做了缩放但预处理阶段如果没限制图片最大尺寸长公式图片会被等比放大到很大一张图占掉的显存比普通图片多几倍加上 LSTM 解码器的中间状态是按序列长度累积的序列越长占用越高。解决先用training_small.json和vocab_small.json跑通全流程确认代码没问题后再换大配置同时把输入图片的最长边限制到 256 像素附近虽然长公式会因此损失一部分细节但整个训练过程会稳定非常多。这也是项目特意提供 small 系列配置的原因。5.5 训练和预测用不同的 vocab 导致乱码现象训练时用的vocab.json训练得好好的换用vocab_small.json加载同一个 checkpoint 做预测输出变成一堆毫无意义的数字和符号。原因vocab 词典变了同一字符串映射到的索引完全不同之前的 checkpoint 权重自然全部错位。解决训练前把用到的vocab.json备份一份predict 时强制指定训练那一刻的同一份词典。顺便说一下这个现象在答辩演示时最容易翻车现场换了一台机器没把配套的 vocab 文件一起拷过去结果演示变成事故。我现在的习惯是每次训练完就把model.json、vocab.json、data.json和最终权重打包进同一个目录算是最简单的后悔药。6. 进阶技巧Beam Search 和评估脚本把识别精度再往上推训练完成后默认的解码方式是贪心解码每个时间步选概率最大的 token。贪心的问题在于它只看眼前一旦当前步选错后面全跟着错而且没有补救机会。公式识别这种语法强结构任务局部最优和全局最优经常不一致所以工程上通常把解码换成 Beam Search每一步保留前 K 个候选序列最后整体评分选择最优。简单来说贪心是每次只走一条路Beam Search 是同时维护 K 条路径。实现思路大概是这样def beam_search(decoder, encoder_features, beam_width5, max_len150): # 初始序列只包含 sos candidates [([vocab[sos]], 0.0)] # (token列表, 累计log概率) # 逐时间步扩展 for _ in range(max_len): new_candidates [] for seq, score in candidates: if seq[-1] vocab[eos]: new_candidates.append((seq, score)) continue # 解码一步得到下一步的概率分布 next_log_probs decoder.step(seq, encoder_features) # 取 top-k 继续扩展 topk next_log_probs.topk(beam_width) for token, log_prob in zip(topk.indices, topk.values): new_candidates.append((seq [token], score log_prob.item())) # 保留全局分数最高的 K 条 new_candidates.sort(keylambda x: x[1], reverseTrue) candidates new_candidates[:beam_width] return candidates[0][0]这段代码是 Beam Search 的核心骨架实际项目里要注意两个参数beam_width一般取 3 到 5再大收益会明显递减且耗时成倍增加分数累计用的是 log 概率而不是原始概率因为原始概率连乘会很快下溢成 0。beam width 和效果的关系大致是Beam Width速度精确匹配率趋势适用场景1贪心最快基线水平快速验证3适中明显提升日常实验5较慢收益递减最终结果配合 Beam Search项目里的evaluate_txt.py也值得用起来。这个脚本的特点是只评估文本层面对齐不依赖图片特征适合单独验证解码器逻辑有没有问题。跑的时候把预测出的 LaTeX 串和真实串逐条比对除了看 BLEU我建议再手动提取几条典型失败样本分式嵌套错误、上下标截断、根号范围出错。这些错误类型在 BLEU 分数上都看不出明显区别但你能从样本里直接看到模型的系统性问题。从那以后我每次训练完都强制走一遍完整的验证流程先evaluate_img.py看整体指标再抽几张错误样本看注意力热图最后换 Beam Search 重新跑一次对比结果三项检查全过才确认模型可用。公式识别的难点不在单点技术——单看 CNN 和 LSTM 都是成熟模块难的是整套链路严格对齐数据规范化、词典一致、尺度统一任何一环松了最终识别率都会真实地还给你。希望这篇拆解能帮你把项目跑通也少走几步我走过的弯路。本文还有配套的精品资源点击获取