资源
-
DBNet 和 DBNet++:configs/det/dbnet/README_CN.md · MindSpore Lab/mindocr - Gitee.com
-
PSENet:configs/det/psenet/README_CN.md · MindSpore Lab/mindocr - Gitee.com
开跑
获取代码
电脑下从 mindocr: MindOCR is an open-source toolbox for OCR development and application based on MindSpore. It helps users to train and apply the best text detection and recognition models, such as DBNet/DBNet++ and CR (gitee.com) 下载得到 mindocr-main.zip
,拷贝到服务器的 work/
下。
解压。
1 |
|
将会解压得到 mindocr-main
文件夹,将其改名为 mindocr
。
创建环境
从智算中心里整一个 mindspore:2.0.2-alpha
的镜像,打开它。
创建虚拟环境:
1 |
|
检查 MindSpore 是否可用:
1 |
|
1 |
|
安装环境
由于 lanms 安装方式有点坑,先装好:
1 |
|
在 requirements.txt
里把 lanms
这行删了。
work/
目录下:
1 |
|
1 |
|
转换数据集
TotalText
Windows 下,分别从 图像 (size = 441Mb) 和 标注文件 (.txt 格式) 下载 totaltext.zip
和 txt_format.zip
。
解压这两个压缩包,将里面的文件组织成如下形式:
1 |
|
然后再打成压缩包 totaltext.zip
,上传到服务器,解压至(unzip
命令)相应目录data/ocr_datasets/
下:
返回 mindocr/
目录,开始转换数据集:
- Train
1 |
|
1 |
|
- Test
1 |
|
1 |
|
这样就可得到预期格式的数据集:
1 |
|
CTW1500
从 Yuliang-Liu/Curve-Text-Detector: This repository provides train&test code, dataset, det.&rec. annotation, evaluation script, annotation tool, and ranking. (github.com) 下载压缩包并解压成如下形式:
1 |
|
将文件夹 ctw1500
重新打包成 ctw1500.zip
,放到服务器中,解压出来:
mindocr/
下执行转换命令:
- Train
1 |
|
- Test
1 |
|
自己的数据集
将自己的数据集转换成 totaltext 的格式:
- Train
1 |
|
- Test
1 |
|
训练
db_r18_totaltext(效果没有官网说的那么好但还是能用)(7.12-7.14)
先修改 config configs/det/dbnet/db_r18_totaltext.yaml
里的数据集路径(我本来不想修改的,结果发现它默认是个绝对路径且不在 work
中,那必须改了)
将 train: dataset:
和 test: dataset:
下的 dataset_root
调整为自己的数据集路径,我这里是:
1 |
|
单卡训练(成)
单卡训练(请确保 yaml 文件中的
distribute
参数为 False。(emmmm 但好像 True 也不会影响。))
1 |
|
1 |
|
妈了个巴子,README.md 写的不清不楚的,终于能跑了。不过这个 first epoch takes longer 真的够 longer……
笑死大半夜实验室电脑寄了还让我学了怎么断点续训
训练到一半 Moba 居然还崩溃了?还得整个断点续训 orz:docs/cn/tutorials/advanced_train.md · MindSpore Lab/mindocr - Gitee.com
往 db_r18_totaltext.yaml
里的 model:
下添加:
1 |
|
然后再:
1 |
|
会提示:
1 |
|
就可以继续了!
分析训练结果
最后结果:
1 |
|
从 tmp_dbt/
中可以看到输出的结果,炼出的丹,日志信息等。
第 1110 个 epoch 的丹性能最好!
Epoch | Loss | Recall | Precision | F-score |
---|---|---|---|---|
1110 | 1.139845 | 83.43% | 84.73% | 84.08% |
emmmm 官网的最终效果为:
模型 | 环境配置 | 骨干网络 | 预训练数据集 | Recall | Precision | F-score | 训练时间 | 吞吐量 | 配置文件 | 模型权重下载 |
---|---|---|---|---|---|---|---|---|---|---|
DBNet | D910x1-MS2.0-G | ResNet-18 | SynthText | 83.66% | 87.65% | 85.61% | 12.9s/epoch | 96.9 img/s | yaml | ckpt |
写一个 python 读取 result.log
并画出图表:
1 |
|
可以看到
- loss 逐渐下降
- recall 逐渐接近于 1
- precision 逐渐下降?这好吗(ChatGPT 说这是正常的)
在训练深度神经网络时,precision(精确度)和 f-score(F1 分数)是衡量分类模型性能的指标。
- 精确度(precision)是指被正确预测为正例的样本数占所有被预测为正例的样本数的比例。它衡量了模型在预测为正例时的准确性。
- F1 分数(F1-score)则是同时考虑了召回率(recall)和精确度的指标,它是精确度和召回率的调和平均值。F1 分数越接近于 1 表示模型在保持高精确度和高召回率方面表现良好。
当训练过程中,随着训练的进行,精确度下降但是 F1 分数逐渐趋近于 1 的情况是可能存在的,尤其是当模型更注重于增加召回率(即尽可能捕捉到更多的正例)时。这种情况通常发生在数据标签不平衡、类别不均衡或存在较高的假阳性或假阴性的情况下。
此时,模型可能会将更多的样本预测为正例,导致假阳性增加,从而降低了精确度。然而,由于模型的预测更加倾向于正例,它也能更好地捕捉到真正的正例,并提高召回率。因此,F1 分数可能会逐渐趋近于 1,指示模型在整体上仍然具有较好的分类性能。
需要注意的是,对于具体的问题和数据集,还需要根据具体情况进行分析和评估,以确定模型的性能是否符合预期要求。
-
f-score 逐渐接近于 1
-
一开始 train_time 就会很慢,执行到 1049 的时候重启训练耗费了好多时间 orz
原文中第 800 个 epoch 时的性能:
Method | P | R | F |
---|---|---|---|
DB-ResNet-18 (800) | 88.3 | 77.9 | 82.8 |
查看 result.log
中第 800 个 epoch 时的性能:
Method | P | R | F |
---|---|---|---|
DB-ResNet-18 (800) | 85.0 | 83.2 | 84.0 |
好家伙训练一个这玩意扣我 665 多块钱……
分布式训练(寄)
还得装 openmpi 4.0.3 (for distributed training/evaluation)
下载 https://download.open-mpi.org/release/open-mpi/v4.0/openmpi-4.0.3.tar.gz 拷贝到服务器中,然后一阵操作:
1 |
|
然后就会喜提安装失败。
找了工作人员,还没得到解决方案……
改天试试 RANK_TABLE_FILE 方法。
db_r18_ctw1500(能跑但是没有跑完)(7.14)
先修改 config configs/det/dbnet/db_r18_ctw1500.yaml
里的数据集路径:
将 train: dataset:
和 test: dataset:
下的 dataset_root
调整为自己的数据集路径:
1 |
|
开跑!
1 |
|
1 |
|
可是我想整个服务器后台执行,避免之前实验室电脑 Moba 大半夜掉线的尴尬:
1 |
|
最后一个“&”表示后台运行程序
“nohup” 表示程序不被挂起
“python”表示执行 python 代码
“-u”表示不启用缓存,实时输出打印信息到日志文件(如果不加 -u,则会导致日志文件不会实时刷新代码中的 print 函数的信息)
“test.py”表示 python 的源代码文件(根据自己的文件修改)
“test.log”表示输出的日志文件(自己修改,名字自定)
“>”表示将打印信息重定向到日志文件
“2>&1”表示将标准错误输出转变化标准输出,可以将错误信息也输出到日志文件中(0-> stdin, 1->stdout, 2->stderr)
最牛逼的伟哥提示道:
不过这种方法 你想中途终止的话 你只能用 kill 杀掉进程来解决了
不然只能等到运行结束
那么直接重启服务器也是可以的。
8.2 重跑!
db++_r18_totaltext(魔改)(7.16)
原仓库没有这个选项,试试直接给 db_r18_totaltext.yaml
里填上
1 |
|
开跑!
1 |
|
分析下结果:
1 |
|
统计下前 10 epoch 下的 F-score 值:
1 |
|
1 |
|
emmmm 虽然 loss 一直在下降,但是 F-score 很早就趋于稳定了。但是最终的训练结果还不如 db_r18_totaltext?什么鬼啊!
原文中第 800 个 epoch 时的性能:
Method | P | R | F |
---|---|---|---|
DB-ResNet-18++ (800) | 84.3 | 81.0 | 82.6 |
查看 result.log
中第 800 个 epoch 时的性能:
Method | P | R | F |
---|---|---|---|
DB-ResNet-18++ (800) | 85.6 | 81.4 | 83.4 |
原文中第 1024 个 epoch 时的性能:
Method | P | R | F |
---|---|---|---|
DB-ResNet-18++ (1024) | 86.7 | 81.3 | 83.9 |
查看 result.log
中第 1024 个 epoch 时的性能:
Method | P | R | F |
---|---|---|---|
DB-ResNet-18++ (1024) | 84.9 | 81.3 | 83.0 |
笑死,效果还不如第 800 个 epoch。
db++_r18_ctw1500(8.4)
1 |
|
db_r50_totaltext(7.27)
1 |
|
分析下结果:
db_r50_ctw1500(8.3)
1 |
|
db++_r50_totaltext(魔改)
炼出了个不知道什么玩意儿(7.15-7.16)
原仓库没有这个选项,作死根据 db++_r50_icdar15.yaml
和 db_r50_totaltext.yaml
魔改成一个 db++_r50_totaltext.yaml
,DB++ 与 DB 的区别就是
1 |
|
这两行的区别:
1 |
|
开跑!(魔改的后的 db++_r50_totaltext.yaml
放在了 test_dbnet/
下)
1 |
|
1 |
|
笑死真的能跑。
7.16 结果到 800 个 epoch,需要评估的时候还是寄了。
1 |
|
断点重训又能跑了?好奇怪。
发现 F 分数好低,绝,感觉这个丹炼废了,Ctrl + Z,先搁置吧。
重跑(7.26)
试试直接给 db_r50_totaltext.yaml
里填上
1 |
|
开跑!
1 |
|
分析下结果:
最好的前 10 个模型:
1 |
|
这个算是性能最好的一个丹了。
原文中第 800 个 epoch 时的性能:
Method | P | R | F |
---|---|---|---|
DB-ResNet-50++ (800) | 87.9 | 82.8 | 85.3 |
查看 result.log
中第 800 个 epoch 时的性能:
Method | P | R | F |
---|---|---|---|
DB-ResNet-50++ (800) | 86.3 | 83.9 | 85.1 |
原文中第 1024 个 epoch 时的性能:
Method | P | R | F |
---|---|---|---|
DB-ResNet-50++ (1024) | 88.5 | 82.0 | 85.1 |
查看 result.log
中第 1024 个 epoch 时的性能:
Method | P | R | F |
---|---|---|---|
DB-ResNet-50++ (1024) | 85.8 | 83.4 | 84.6 |
db++_r50_ctw1500(8.5)
1 |
|
自己的数据集(8.24)
1 |
|
推理(7.26、7.29)
离线推理只能用于 昇腾310,但是服务器是 昇腾910,所以寄
折磨完华为工作人员后说可以使用在线推理,设置好 --image_dir
、--det_algorithm
、--det_model_dir
,然后开跑!
但是发现这个推理只支持 resnet50?拉了。
居然只支持画矩形框?天呐。
Ground Truth
0999.jpg
- Ground Truth
1 |
|
官网模型
dbnet_resnet50_td500-0d12b5e8.ckpt
从官网下载的训练好的模型:configs/det/dbnet/README_CN.md · MindSpore Lab/mindocr - Gitee.com 里的 dbnet_resnet50_td500-0d12b5e8.ckpt
:
1 |
|
1 |
|
会在 inference_results/
里获得 0999_det_res.png
和 det_results.txt
:
det_results.txt
1 |
|
0999_det_res.png
自己的丹
tmp_det_db_r50_totaltext/best.ckpt
1 |
|
det_results.txt
1 |
|
0999_det_res.png
居然差这么多……感觉参数不太一样……
tmp_det_db++_r50_totaltext/best.ckpt
1 |
|
det_results.txt
1 |
|
可用参数
查看 tools/infer/text/config.py
,有如下参数可用:
1 |
|
评估(8.2)
metrics/det_metrics.py
下存储着评估协议:
1 |
|
这段代码是一个在 mindocr(MindSpore OCR)评估模型中使用的模块。以下是对代码的解释:
from typing import List, Tuple
:导入类型提示(Type Hints),用于指定函数参数和返回值的类型。import numpy as np
:导入 NumPy 库,用于进行数值计算和数组操作。from shapely.geometry import Polygon
:从 shapely.geometry 模块中导入 Polygon 类,用于处理多边形几何对象。import mindspore as ms
:导入 MindSpore 库,一个开源的深度学习框架。import mindspore.ops as ops
:导入 MindSpore 框架的操作模块,用于执行各种操作。from mindspore import Tensor, ms_function, nn
:从 MindSpore 中导入 Tensor、ms_function 和 nn,它们是 MindSpore 框架提供的一些基础类和装饰器。__all__ = ["DetMetric"]
:定义了一个名为__all__
的变量,其中包含字符串 “DetMetric”。这表示该模块中只导出DetMetric
类,其他变量和函数不会被导入。这段代码主要是导入所需的依赖库和模块,为之后的代码提供必要的支持。在 MindOCR 评估模型中,这些导入的模块和类可能用于数据处理、模型定义、评估和计算等方面的操作。
1 |
|
这段代码定义了一个名为
_get_intersect
的函数,该函数接受两个参数pd
和pg
,并返回它们的交集面积。
pd
和pg
参数都是多边形(Polygon)对象,可能是由 shapely.geometry.Polygon 创建的。pd.intersection(pg)
表示计算pd
和pg
的交集,返回一个新的多边形对象。.area
是对交集多边形对象调用的方法,用于计算其面积。- 函数将交集多边形的面积作为结果进行返回。
此函数的目的是计算两个多边形的交集并返回其面积。在 MindOCR 评估模型中,这个函数可能被用于计算检测框与标注框之间的交集面积,用于评估模型的准确度和性能。
1 |
|
这段代码定义了一个名为
_get_iou
的函数,该函数接受两个参数pd
和pg
,并返回它们的交并比(Intersection over Union,IoU)。
pd
和pg
参数都是多边形(Polygon)对象,可能是由 shapely.geometry.Polygon 创建的。pd.intersection(pg)
表示计算pd
和pg
的交集,返回一个新的多边形对象。.area
是对交集多边形对象和并集多边形对象进行调用的方法,分别用于计算其面积。/
运算符将两个面积相除,得到交并比(IoU)。- 函数将交并比作为结果进行返回。
交并比是用于衡量两个集合重叠程度的指标。在目标检测任务中,常用于评估模型检测结果与真实标注框之间的匹配程度。在该评估模型中,
_get_iou
函数可能被用于计算检测框与标注框之间的交并比,以评估模型的准确度和性能。
1 |
|
这段代码定义了一个名为
DetectionIoUEvaluator
的类,用于将真实的多边形位置和预测的多边形位置转换为二元分类标签,以便进行度量计算,如召回率、精确率等。该类有两个参数:
min_iou
:真实值和预测值之间的最小交并比(Intersection over Union,IoU),用于被视为正确预测的阈值。min_intersect
:与被忽略的真实值的最小交集,用于被视为忽略的预测(从而在进一步计算中排除)的阈值。类的构造函数
__init__
接受这两个参数,并将它们保存为类的属性。类还实现了
__call__
方法,接受两个参数gt
和preds
,分别表示真实的多边形和预测的多边形。该方法首先过滤掉无效的真实多边形,并将它们分为有效多边形和被忽略的多边形。然后对预测多边形进行相同的处理。
接下来,对每个预测多边形,如果存在被忽略的真实多边形,并且预测多边形的面积大于 0,则计算它与每个被忽略的真实多边形的交集面积,并计算交集面积占预测多边形面积的比例(即 precision)。如果 precision 大于设定的阈值
min_intersect
,则将该预测多边形视为被忽略的预测;否则将其视为有效预测。然后,将每个有效预测多边形与真实多边形计算交并比(IoU),如果交并比大于设定的阈值
min_iou
,则将该真实多边形标记为正样本。最后,根据标记结果生成二元分类标签,其中 1 表示正样本,0 表示负样本。返回真实多边形的标签和预测多边形的标签。
该类可能用于目标检测任务中,用于根据交并比将模型的预测结果与真实标注进行匹配,并计算度量指标(如召回率、精确率)以评估模型性能。
1 |
|
这段代码定义了一个名为
DetMetric
的类,用于计算预测的多边形与真实标注之间的召回率、精确率和 F1 分数。该类继承自
nn.Metric
,并具有以下几个方法和属性:
__init__(self, device_num: int = 1, **kwargs)
:类的构造函数,接受一个整数参数device_num
,表示用于计算度量的设备数量。初始化了一个DetectionIoUEvaluator
对象作为度量计算的评估器,并初始化了一些其他属性。clear(self)
:清空保存的真实标签和预测标签。update(self, *inputs)
:在单个数据批次上计算度量。接受两个输入参数,preds
和gts
。preds
是一个字典,包含键为 “polys” 和 “score” 的两个项,分别表示预测的多边形和置信度得分。gts
是一个元组,其中包含真实标注的多边形和忽略标签。对每个样本,将真实标注和预测的多边形传递给DetectionIoUEvaluator
对象进行评估,并保存得到的标签。all_reduce_fun(self, x)
:用于分布式计算中的全局归约操作的函数。cal_matrix(self, det_lst, gt_lst)
:计算真阳性(True Positive),假阳性(False Positive)和假阴性(False Negative)的数量。eval(self) -> dict
:在所有批次上评估度量,并返回平均召回率、精确率和 F1 分数。其中,
_safe_divide
函数用于安全地进行除法运算,避免除以零的情况。该类可能用于目标检测任务中,通过对比预测的多边形和真实标注的多边形,计算模型的召回率、精确率和 F1 分数,以评估模型性能。
1 |
|
_safe_divide
是一个辅助函数,用于进行除法运算并安全处理分母为零的情况。它接受三个参数:numerator
(分子)、denominator
(分母)和val_if_zero_divide
(当分母为零时的返回值,默认为 0.0)。函数的逻辑如下:
- 如果分母
denominator
等于零,则返回val_if_zero_divide
。- 否则,返回
numerator / denominator
。该函数的作用是避免在除法运算中出现分母为零的错误,当分母为零时,可以选择返回一个指定的默认值,以免影响后续计算。