跳到内容。

如何训练 LSTM/神经网络 Tesseract

对训练过程有疑问吗?如果您在训练过程中遇到问题需要帮助,请使用 tesseract-ocr 邮件列表提出您的问题。请勿将您的问题作为 问题 报告!

使用 tesstrain.sh bash 脚本进行的训练在 Tesseract 5 中不受支持/已弃用。请使用 tesstrain 仓库 中的 python 脚本进行训练。

内容

介绍

Tesseract 4.00 引入了一种新的基于神经网络的识别引擎,与以前的版本相比,它在文档图像上提供了更高的准确性,但需要更高的计算能力。然而,在复杂语言上,它实际上可能比基本 Tesseract 更快

神经网络需要大量的训练数据,并且训练速度比基本 Tesseract 慢得多。对于拉丁语系语言,提供的现有模型数据已在约 400000 行文本中进行了训练,涵盖了约 4500 种字体。对于其他脚本,可用的字体并不多,但它们仍然在类似数量的文本行上进行了训练。Tesseract 4.00 的训练时间不是几分钟到几个小时,而是需要几天到几周。即使有了所有这些新的训练数据,您仍然可能会发现它不适合您的特定问题,因此您想要重新训练它。

有多种训练选项

虽然上述选项可能听起来不同,但训练步骤实际上几乎完全相同,除了命令行,因此在时间或硬件允许的情况下,可以相对轻松地尝试所有方法,以并行运行它们。

旧的识别引擎仍然存在,也可以训练,但已弃用,除非有充分的理由保留它,否则可能会在将来的版本中删除。

开始之前

您不需要任何神经网络方面的背景知识来训练 Tesseract,但这可能有助于理解训练选项之间的差异。请阅读 实现介绍,然后再深入研究训练过程。

重要说明:重要的是要注意,除非您使用的是非常不寻常的字体或新语言,否则重新训练 Tesseract 可能会无济于事。在您投入时间和精力训练 Tesseract 之前,强烈建议您阅读 提高质量 页面。许多时候,可以通过预处理输入图像来提高识别率。

硬件-软件要求

在撰写本文时,训练仅在 Linux 上有效。(macOS 几乎有效;它需要对 shell 脚本进行一些小的修改,以适应它提供的旧版本 bash 以及 mktemp 中的差异。)Windows 尚不清楚,但可能需要 msys 或 Cygwin。

至于运行 Tesseract,拥有多核(4 核很好)机器,支持 OpenMP 和 Intel Intrinsics 的 SSE/AVX 扩展,这很有用,但并非必需。基本上,它仍然可以在任何拥有足够内存的机器上运行,但您的处理器档次越高,运行速度就越快。不需要 GPU。(不支持。)可以通过 –max_image_MB 命令行选项控制内存使用情况,但您可能至少需要超过操作系统占用内存的 1 GB 内存。

所需的其他库

从 3.03 版本开始,需要额外的库来构建训练工具。

sudo apt-get install libicu-dev libpango1.0-dev libcairo2-dev

构建训练工具

从 3.03 版本开始,如果您从源代码编译 Tesseract,则需要使用单独的 make 命令来创建和安装训练工具。

对于训练,您必须确保所有这些可选依赖项都已安装,并且 Tesseract 的构建环境能够找到它们。在 ./configure 的输出中查找以下行

checking for pkg-config... [some valid path]
checking for lept >= 1.74... yes
checking for libarchive... yes
checking for icu-uc >= 52.1... yes
checking for icu-i18n >= 52.1... yes
checking for pango >= 1.22.0... yes
checking for cairo... yes
[...]
Training tools can be built and installed with:

(当然,版本号可能会随着时间的推移而发生变化。我们要找的是“是”,所有可选依赖项都可用。)

配置完成后,您可以尝试构建训练工具

make
make training
sudo make training-install

构建 ScrollView.jar 也很有用,但并非必需。

make ScrollView.jar
export SCROLLVIEW_PATH=$PWD/java

训练文本要求

对于拉丁语系语言,提供的现有模型数据已在约 400000 行文本中进行了训练,涵盖了约 4500 种字体。对于其他脚本,可用的字体并不多,但它们仍然在类似数量的文本行上进行了训练。

请注意,拥有更多训练文本并制作更多页面是有益的,因为神经网络的泛化能力不强,需要在与它们将要运行的场景类似的内容上进行训练。如果目标域非常有限,那么关于需要大量训练数据的严厉警告可能不适用,但可能需要更改网络规范。

训练过程概述

训练的主要步骤是

  1. 准备训练文本。
  2. 将文本渲染为图像 + 框文件。(或为现有的图像数据创建手工制作的框文件。)
  3. 制作 unicharset 文件。(可以部分指定,即手动创建)。
  4. 从 unicharset 和可选的字典数据中制作一个启动/原型 traineddata。
  5. 运行 tesseract 处理图像 + 框文件以制作训练数据集 (lstmf 文件)。
  6. 在训练数据集上运行训练。
  7. 组合数据文件。

与训练基本 Tesseract(Legacy Tesseract 3.04)的主要区别在于

训练不能像 3.04 的训练那样自动化,原因有几个

了解训练过程中使用的各种文件

与基本/传统 Tesseract 一样,完成的 LSTM 模型及其所需的一切都收集在 traineddata 文件中。与基本/传统 Tesseract 不同,训练过程中会给出 启动/原型 traineddata 文件,并且必须事先设置。它可以包含

粗体元素必须提供。其他元素是可选的,但如果提供了任何 dawg,则标点符号 dawg 也必须提供。

提供了一个新工具 combine_lang_model,用于从 unicharset 和可选的词语列表中制作 启动 traineddata,并且是训练所必需的。

在训练过程中,训练器会写入检查点文件,这是神经网络训练器的标准行为。如果需要,这允许训练停止并继续进行。通过使用 --stop_training 命令行标志,可以将任何检查点转换为完整的 traineddata 以供识别。

训练器还会定期在训练期间取得新的最佳成绩时写入检查点文件。

可以修改网络并仅重新训练部分网络,或者针对特定训练数据进行微调(即使使用修改后的 unicharset!),方法是告诉训练器 --continue_from 现有检查点文件,或从使用 combine_tessdata 从现有 traineddata 文件中提取的裸 LSTM 模型文件(前提是它尚未转换为整数)

如果与通过 --continue_from 提供的模型中使用的 unicharset 相比,--traineddata 标志中的 unicharset 发生了变化,那么必须使用相应的 traineddata 文件(其中包含 unicharsetrecoder)提供 --old_traineddata 标志。这使训练器能够计算字符集之间的映射。

训练数据通过 .lstmf 文件提供,这些文件是序列化 DocumentData。它们包含图像和相应的 UTF8 文本转录,可以使用 Tesseract 从 tif/box 文件对生成,类似于为旧引擎创建 .tr 文件的方式。

LSTMTraining 命令行

lstmtraining 程序是用于训练神经网络的多功能工具。下表描述了它的命令行选项

标志 类型 默认值 说明
traineddata 字符串 指向包含 unicharset、recoder 和可选语言模型的启动 traineddata 文件的路径。
net_spec 字符串 指定网络的拓扑结构。
model_output 字符串 输出模型文件/检查点的基路径。
max_image_MB int 6000 用于缓存图像的最大内存量。
learning_rate double 10e-4 SGD 算法的初始学习率。
sequential_training bool false 设置为 true 以进行顺序训练。默认情况下是使用循环方式处理所有训练数据。
net_mode int 192 来自 network.hNetworkFlags 的标志。可能的值:128 表示使用 Adam 优化而不是动量;64 允许不同的层具有自己的学习率,这些学习率会自动发现。
perfect_sample_delay int 0 当网络变得很好时,只有在看到此数量的不完美样本之后,才会对完美样本进行反向传播,自上次允许完美样本通过以来。
debug_interval int 0 如果非零,则每隔此数量的迭代显示一次可视化调试。
weight_range double 0.1 初始化权重的随机值的范围。
momentum double 0.5 用于 alpha 平滑梯度的动量。
adam_beta double 0.999 ADAM 算法中平滑因子平方梯度。
max_iterations int 0 经过此数量的迭代后停止训练。
target_error_rate double 0.01 如果平均百分比错误率低于此值,则停止训练。
continue_from 字符串 指向要从中继续训练或微调的先前检查点的路径。
stop_training bool false --continue_from 中的训练检查点转换为识别模型。
convert_to_int bool false 使用 stop_training,转换为 8 位整数以提高速度,但准确性略有下降。
append_index int -1 从给定索引处切断网络头部,并在切断的部分位置附加 --net_spec 网络。
train_listfile 字符串 列出训练数据文件的列表文件的名称。
eval_listfile 字符串 列出用于独立于训练数据评估模型的评估数据文件的列表文件的名称。

大多数标志使用默认值,并且某些标志仅在下面列出的特定操作中需要,但首先对更复杂的标志进行一些详细的评论

Unicharset 压缩-重新编码

LSTM 擅长学习序列,但当状态数太大时,速度会大幅下降。有经验结果表明,让 LSTM 学习长序列比学习短序列的多个类更好,因此对于复杂的脚本(汉语、韩语和印度语脚本),最好将每个符号重新编码为来自少量类别的少量代码的短序列,而不是拥有大量的类别。

combine_lang_model 命令默认情况下启用了此功能。它将每个汉字编码为 1-5 个代码的可变长度序列,将韩语使用 Jamo 编码为 3 个代码的序列,将其他脚本编码为其 Unicode 组件的序列。对于使用virama 字符来生成连字辅音的脚本(所有印度语脚本加上缅甸语和高棉语),函数 NormalizeCleanAndSegmentUTF8 将 virama 与适当的邻居配对,在 unicharset 中生成更面向字形的编码。为了充分利用此改进,应该为这些脚本设置 combine_lang_model--pass_through_recoder 标志。

随机训练数据和 sequential_training

为了使随机梯度下降正常工作,训练数据应该在所有样本文件中随机打乱,这样训练器就可以依次读取每个文件,并在到达文件末尾时返回到第一个文件。

如果使用渲染代码(通过 tesstrain.py),它将在每个文件内对样本文本行进行洗牌,但您将获得一组文件,每个文件包含来自单个字体的训练样本。为了添加更均匀的混合,默认情况下是依次从每个文件处理一个样本,即“循环”方式。如果您以其他方式生成了训练数据,或者它都是来自相同的样式(例如手写手稿书),那么您可以使用 --sequential_training 标志用于 lstmtraining。 这样更节省内存,因为它一次只从两个文件加载数据,并按顺序处理它们。(第二个文件是预读的,因此在需要时就可以准备好了。)

模型输出

训练器使用 --model_output 作为基名定期保存检查点。因此,您可以随时停止训练,并使用相同的命令行重新启动它,它将继续进行。要强制重新启动,请使用不同的 --model_output 或删除所有文件。

网络模式和优化

128 标志开启 Adam 优化,它似乎比普通的动量工作得更好。

64 标志启用自动层级学习率。当进度停滞时,训练器会调查哪些层应该独立降低学习率,并可能会降低一个或多个学习率以继续学习。

net_mode 的默认值为 192,它同时启用了 Adam 和层级学习率。

完美样本延迟

对“简单”样本进行训练并不一定是一个好主意,因为它是在浪费时间,但网络不应该被允许忘记如何处理它们,因此如果简单样本出现的频率过高,可以将其丢弃。 --perfect_sample_delay 参数会丢弃完美的样本,如果自上一个完美样本以来没有看到那么多的不完美样本。

当前的默认值为零,使用所有样本。在实践中,这个值似乎没有太大影响,如果允许训练运行足够长的时间,零会产生最好的结果。

调试间隔和可视化调试

使用零(默认) --debug_interval,训练器每 100 次迭代输出一个进度报告,类似于以下示例。

At iteration 717/10500/10500, Mean rms=0.113000%, delta=0.009000%, BCER train=0.029000%, BWER train=0.083000%, skip ratio=0.000000%,  New worst BCER = 0.029000 wrote checkpoint.

At iteration 718/10600/10600, Mean rms=0.112000%, delta=0.007000%, BCER train=0.023000%, BWER train=0.085000%, skip ratio=0.000000%,  New worst BCER = 0.023000 wrote checkpoint.

2 Percent improvement time=509, best error was 2.033 @ 209
At iteration 718/10700/10700, Mean rms=0.111000%, delta=0.006000%, BCER train=0.019000%, BWER train=0.069000%, skip ratio=0.000000%,  New best BCER = 0.019000 wrote best model:data/engRupee/checkpoints/engRupee_0.019000_718_10700.checkpoint wrote checkpoint.

2 Percent improvement time=509, best error was 2.033 @ 209
At iteration 718/10800/10800, Mean rms=0.108000%, delta=0.002000%, BCER train=0.007000%, BWER train=0.052000%, skip ratio=0.000000%,  New best BCER = 0.007000 wrote best model:data/engRupee/checkpoints/engRupee_0.007000_718_10800.checkpoint wrote checkpoint.

Finished! Selected model with minimal training error rate (BCER) = 0.007

使用 --debug_interval -1,训练器会为每次训练迭代输出详细的调试文本。文本调试信息包括真实文本、识别文本、迭代次数、训练样本 ID(lstmf 文件和行)以及多个错误指标的平均值。GROUND TRUTH 在所有情况下都会显示该行的内容。ALIGNED TRUTHBEST OCR TEXT 仅在与 GROUND TRUTH 不同时显示。

Iteration 455038: GROUND  TRUTH : उप॑ त्वाग्ने दि॒वेदि॑वे॒ दोषा॑वस्तर्धि॒या व॒यम् ।
File /tmp/san-2019-03-28.jsY/san.Mangal.exp0.lstmf line 451 (Perfect):
Mean rms=1.267%, delta=4.155%, train=11.308%(32.421%), skip ratio=0%
Iteration 455039: GROUND  TRUTH : मे अपराध और बैठे दुकानों नाम सकते अधिवक्ता, दोबारा साधन विषैले लगाने पर प्रयोगकर्ताओं भागे
File /tmp/san-2019-04-04.H4m/san.FreeSerif.exp0.lstmf line 28 (Perfect):
Mean rms=1.267%, delta=4.153%, train=11.3%(32.396%), skip ratio=0%
Iteration 1526: GROUND  TRUTH : 𒃻 𒀸 𒆳𒆳 𒅘𒊏𒀀𒋾
Iteration 1526: ALIGNED TRUTH : 𒃻 𒀸 𒆳𒆳 𒅘𒊏𒊏𒀀𒋾
Iteration 1526: BEST OCR TEXT :    𒀀𒋾
File /tmp/eng-2019-04-06.Ieb/eng.CuneiformComposite.exp0.lstmf line 19587 :
Mean rms=0.941%, delta=12.319%, train=56.134%(99.965%), skip ratio=0.6%
Iteration 1527: GROUND  TRUTH : 𒀭𒌋𒐊
Iteration 1527: BEST OCR TEXT : 𒀭𒌋
File /tmp/eng-2019-04-06.Ieb/eng.CuneiformOB.exp0.lstmf line 7771 :
Mean rms=0.941%, delta=12.329%, train=56.116%(99.965%), skip ratio=0.6%

使用 --debug_interval > 0,训练器会在网络的各个层上显示多个调试信息窗口。

--debug_interval 1 的特殊情况下,它会在继续进行下一个迭代之前等待 LSTMForward 窗口中的点击,但对于所有其他情况,它只会继续并按请求的频率绘制信息。

请注意,要使用 --debug_interval > 0,您必须构建 ScrollView.jar 以及其他训练工具。 请参阅 构建训练工具

可视化调试信息包括

每个网络层的正向和反向窗口。大多数只是随机噪声,但 Output/Output-backConvNL 窗口值得查看。Output 显示最终 Softmax 的输出,它最初是空字符的黄色线,并逐渐在它认为有字符的每个点上发展出黄色标记。(x 轴是图像的 x 坐标,y 轴是字符类。) Output-back 窗口使用相同的布局显示实际输出与目标之间的差异,但使用黄色表示“给我更多这个”和蓝色表示“给我更少这个”。随着网络的学习, ConvNL 窗口会发展出您期望从底层获得的典型边缘检测结果。

LSTMForward 显示整个网络在训练图像上的输出。LSTMTraining 显示训练图像上的训练目标。在这两者中,都会绘制绿线以显示每个字符的峰值输出,并且字符本身会被绘制在该线的右侧。

另外两个值得查看的窗口是 CTC OutputsCTC Targets。它们显示网络的当前输出和目标,作为输出强度对图像 x 坐标的线图。与 Output 窗口类似,不是使用热图,而是为每个字符类绘制不同的颜色线,y 轴是输出强度。

迭代和检查点

在训练过程中,我们会看到以下信息

2 Percent improvement time=100, best error was 100 @ 0
At iteration 100/100/100, Mean rms=6.042000%, delta=63.801000%, BCER train=98.577000%, BWER train=100.000000%, skip ratio=0.000000%,  New best BCER = 98.577000 wrote checkpoint.

2 Percent improvement time=200, best error was 100 @ 0
At iteration 200/200/200, Mean rms=5.709000%, delta=58.372000%, BCER train=98.399000%, BWER train=99.986000%, skip ratio=0.000000%,  New best BCER = 98.399000 wrote checkpoint.
...
At iteration 14615/695400/698614, Mean rms=0.131000%, delta=0.038000%, BCER train=0.207000%, BWER train=0.579000%, skip ratio=0.4%,  wrote checkpoint.

在上面的例子中,

14615 : learning_iteration
695400 : training_iteration
698614 : sample_iteration

sample_iteration : “训练样本集的索引。(sample_iteration >= training_iteration)。” 它表示训练文件被传递到学习过程中的次数。

training_iteration : “使用的实际反向训练步骤的数量。” 它表示训练文件被成功传递到学习过程中的次数。因此,每次出现错误:“图像太大,无法学习!!” - “字符串编码失败!” - “反序列化标题失败”,sample_iteration 会递增,但 training_iteration 不会递增。实际上,您有 1 - (695400 / 698614) = 0.4%,这就是 skip ratio : 被跳过的文件的比例,因为出现了错误。

learning_iteration : “产生非零增量误差的迭代次数,从而提供了重要的学习。(learning_iteration <= training_iteration)。learning_iteration_ 用于衡量学习进度的速率。” 因此,它使用增量值来评估迭代是否有效。

需要注意的是,当您为训练过程指定最大迭代次数时,它使用中间迭代次数 (training_iteration) 来确定何时停止。但当它写入检查点时,检查点名称也会使用最佳迭代次数 (learning_iteration),以及字符训练率。因此,检查点名称是 model_name & char_train & learning_iteration & training_iteration 的串联,例如 sanLayer_1.754_347705_659600.checkpoint。

lstmtraining 程序输出两种类型的检查点文件

使用 stop_trainingconvert_to_int 标志与 lstmtraining 一起使用,可以将任何类型的检查点文件转换为标准(最佳/浮点)traineddata 文件或精度略低(快速/整数)traineddata 文件。

训练中的错误消息

运行训练时可能会出现各种错误消息,其中一些可能很重要,而另一些则不那么重要

Encoding of string failed! 当训练图像的文本字符串无法使用给定的 unicharset 编码时,就会出现此错误。可能的原因包括

  1. 文本中存在未表示的字符,例如您的 unicharset 中没有的英镑符号。
  2. 文本中存在一个意外的不可打印字符(例如制表符或控制字符)。
  3. 文本中存在未表示的印度语音素/ aksara。

您可以使用 sed 脚本简单地删除 pct. 2 中的冒犯字符(CHARACTER TABULATION、CARRIAGE RETURN、NO-BREAK SPACE、LEFT-TO-RIGHT MARK、RIGHT-TO-LEFT MARK、ZERO WIDTH NO-BREAK SPACE、POP DIRECTIONAL FORMATTING、ZERO WIDTH NON-JOINER)

remove_control_chars.sed

s/\x09//g
s/\x0d//g
s/\x00\xa0/ /g
s/\x20\x0e//g
s/\x20\x0f//g
s/\xfe\xff//g
s/\x20\x2c//g
s/\x20\x0c//g

sed -i -f remove_control_chars.sed data/lang-ground-truth*.gt.txt

无论如何,这都会导致训练器忽略该训练图像。如果错误不经常发生,则无害,但这可能表明您的 unicharset 不足以表示您正在训练的语言。

Unichar xxx is too long to encode!!(很可能是印度语)。编码器中可以使用的 Unicode 字符长度存在上限,这简化了 LSTM 引擎的 unicharset。它只会继续,并将该 Aksara 排除在可识别集中,但如果有很多,那么您就遇到了麻烦。

Bad box coordinates in boxfile string! LSTM 训练器只需要完整的文本行的边界框信息,而不是字符级的边界框信息,但如果您在框字符串中添加空格,例如

<text for line including spaces> <left> <bottom> <right> <top> <page>

解析器会感到困惑,并向您显示错误消息。

Deserialize header failed 当训练输入不是 LSTM 格式或文件不可读时,就会出现此错误。检查您的文件列表文件,以查看它是否包含有效的文件名。

No block overlapping textline: 当版面分析未能正确分割作为训练数据给出的图像时,就会出现此错误。文本行会被删除。如果不多,则没有太大问题,但如果很多,则可能是在训练文本或渲染过程中存在问题。

<Undecodable> 可以在训练早期出现在 ALIGNED_TRUTH 或 OCR TEXT 输出中。这是 unicharset 压缩和 CTC 训练的结果。(请参阅上面的 Unicharset 压缩和 train_mode)。这应该是无害的,可以安全地忽略。它的频率应该随着训练的进行而降低。

组合输出文件

lstmtraining 程序输出两种类型的检查点文件

这两个文件都可以转换为标准的 traineddata 文件。这将从训练转储中提取识别模型,并将其插入 –traineddata 参数中,以及训练期间提供的 unicharset、编码器和任何 dawgs。

请注意 Tesseract 现在可以愉快地运行包含 lang.lstmlang.lstm-unicharsetlang.lstm-recoder 的 traineddata 文件。 lstm-*-dawgs 是可选的,其他任何组件都不是必需的,也不会在 OEM_LSTM_ONLY 作为 OCR 引擎模式时使用。 不需要二元语法、unichar 歧义或任何其他组件,即使存在也不会产生任何影响。唯一起作用的其他组件是 lang.config,它会影响版面分析和子语言。

如果添加到现有的 Tesseract traineddata 文件中, lstm-unicharset 不必与 Tesseract unicharset 相匹配,但必须使用相同的 unicharset 来训练 LSTM 并构建 lstm-*-dawgs 文件。

幻觉效应

如果您发现您的模型行为异常,例如

那么请阅读幻觉主题。