Are you sure you want to delete this task? Once this task is deleted, it cannot be recovered.
unknown d9dd42f6cb | 1 year ago | |
---|---|---|
PaddleClas | 1 year ago | |
work | 1 year ago | |
Readme.md | 1 year ago | |
main.ipynb | 1 year ago | |
prepare_data.ipynb | 1 year ago |
本项目基于开源项目的流程,提出想法进行改进,荣获B榜第5,A榜第13。
人们在使用移动设备进行文档扫描、证照拍摄等过程中,有时受限于使用和拍摄场景,人们会将拍摄设备旋转后拍摄,导致得到的图片也是不同方向的。此时,标准的文档扫描与识别的流程并没有办法正常帮助用户处理文件。
因此,为了便于用户使用,需要各位选手通过技术对给定文档图像进行处理,识别并返回给定图像对比正向原图顺时针旋转的方向角度
本次比赛不提供训练数据集,仅提供A榜测试集,B榜测试集不做公开, 因此本项目收集几个公开数据集,并对部分数据集进行三种旋转(90度,180度,270度)变换,并打上对应的分类角度标签,构成本项目模型最终采用的数据集,其中对每个公开数据集分别进行9:1的比例随机划分将数据集分成了训练集和验证集。接下来将介绍本项目收集的几个公开数据集。
该数据集来源于十进制到二进制。该数据集基于 ICDAR2019-ArT、 XFUND 和 ICDAR2015 三个公开数据集构造了一个小规模含文字图像方向分类数据集。考虑到原始图片的分辨率较高,模型训练时间较长,该数据集已将所有数据预先进行了缩放处理,在保持长宽比不变的前提下,将短边缩放到了384。然后将数据进行顺时针旋转处理,分别生成90度、180度和270度的合成数据。其中,将 ICDAR2019-ArT 和 XFUND 生成的41460张数据按照 9:1 的比例随机划分成了训练集和验证集,图片路径及标签信息见PaddleClas/dataset/text_image_orientation/train_list.txt及PaddleClas/dataset/text_image_orientation/test_list.txt。图片示例如下:
该数据集来源于被褐怀玉上传的VOC2012。因为测试集中包含自然图像,而公开数据集1主要是文档图像,因此该数据集基于用于目标检测的VOC2012数据集,并将该数据集图片分别旋转90度、180度和270度,得到最终的扩充数据集,此时数据集包含图片68500张,然后按照 9:1 的比例随机划分成了训练集和验证集,图片路径及标签信息见PaddleClas/dataset/text_image_orientation/jpeg_train_list.txt及PaddleClas/dataset/text_image_orientation/jpeg_test_list.txt。
该数据集来源于鸿飞往里上传的文档增强数据集。由于公开数据集2主要增强自然图像的旋转角度识别,为了使数据集更均衡,该数据集对文档图像数据进行扩充和增强,因此该数据集进一步将本身包含的文档图片分别旋转90度、180度和270度,得到最终的扩充数据集,此时数据集包含图片4800张,然后按照 9:1 的比例随机划分成了训练集和验证集,图片路径及标签信息见PaddleClas/dataset/text_image_orientation/gt_blur_train_list.txt及PaddleClas/dataset/text_image_orientation/gt_blur_test_list.txt。
该数据集来源于张牙舞爪上传的水印消除比赛标签图。由于公开数据集2主要增强自然图像的旋转角度识别,为了使数据集更均衡,该数据集继续对文档图像数据进行扩充和增强,因此该数据集进一步将本身包含的文档图片分别旋转90度、180度和270度,得到最终的扩充数据集,此时数据集包含图片7368张,然后按照 9:1 的比例随机划分成了训练集和验证集,图片路径及标签信息见PaddleClas/dataset/text_image_orientation/mask_train_list.txt及PaddleClas/dataset/text_image_orientation/mask_test_list.txt。
综合以上四个数据集,得到本文最终的数据集,其中包含图片125964张,然后按照 9:1 的比例随机划分成了训练集(109910张图片)和验证集(16054),图片路径及标签信息见PaddleClas/dataset/text_image_orientation/new_train_list.txt及PaddleClas/dataset/text_image_orientation/new_test_list.txt。
fork之后的第一次运行,用户需要按照prepare_data.ipynb流程执行挂载数据集、解压缩、旋转处理等操作获得最终的训练集及验证集图片及信息文件(包含每张图片路径和标签)。
本节简述本项目曾做过的产生主要影响的几种尝试。
由于是一卡运行,学习率由0.4调整为0.1。在A榜的结果为分数:0.625,用时:0.00875s。模型文件大小:6.5MB。
公共数据集1主要是文档图像,缺乏自然图像,引入公共数据集2。
在A榜的结果为分数:0.662,用时:0.00883s。模型文件大小:6.5MB。
ResizeImage(resize_short=256)及CropImage(size=224)在predict.py的实现如下所示:
img_h, img_w = img.shape[:2]
percent = 256 / min(img_w, img_h)
w = int(round(img_w * percent))
h = int(round(img_h * percent))
img = cv2.resize(img,(w,h))
w_start = (w - 224) // 2
h_start = (h - 224) // 2
w_end = w_start + 224
h_end = h_start + 224
img = img[h_start:h_end, w_start:w_end, :]
在A榜的结果为分数:0.722,用时:0.00883s。模型文件大小:6.5MB。
因为pplcnet_x1_0模型虽然满足推理时间小于等于10ms的要求,但是模型文件大小不满足小于等于3MB的要求,所以本项目基于论文复现赛的论文GENet,设计满足推理时间要求和模型文件大小要求的GENet变体模型。
本项目是基于上表对深度d及通道数c、卷积核大小、瓶颈比r进行调整以满足要求,GENet_lightV1的结构参数如下所示:
[{"d":1, "c":12, "s":2, "k":3, "e":1, "act":"relu"},
{"d":2, "c":24, "s":2, "k":3, "e":1, "act":"relu"},
{"d":3, "c":48, "s":2, "k":3, "e":1, "act":"relu"},
{"d":4, "c":128, "s":2, "k":3, "e":4, "act":"relu"},
{"d":2, "c":160, "s":2, "k":5, "e":2, "act":"hardswish"},
{"d":1, "c":192, "s":1, "k":3, "e":3, "act":"hardswish"},
{"d":1, "c":384, "s":1, "k":1, "e":1, "act":"hardswish"}
]
这里的e即为r,对于包含深度卷积的block,其激活函数本项目采用与pplcnet_x1_0模型一致的hardswish,除最后一个卷积层外,其他为relu。详细的模型实现见PaddleClas/ppcls/arch/backbone/legendary_models/genet.py
该模型在A榜的结果为分数:0.695,用时:0.00876s。模型文件大小:3.0MB。
考虑的数据量不够及相比自然图像文档图像过少,数据集不均衡,引入公共数据集3和4。又考虑到pplcnet_x1_0模型训练了360(ImageNet预训练)+60个epochs,本项目增加GENet_lightV1的训练epochs为120。此时该模型在A榜的结果为分数:0.841,用时:0.00861s。模型文件大小:3.0MB。
除了GENet_lightV1,还是设计了GENet_lightV2,如下所示:
[{"d":1, "c":12, "s":2, "k":3, "e":1, "act":"relu"},
{"d":1, "c":24, "s":2, "k":3, "e":1, "act":"relu"},
{"d":2, "c":48, "s":2, "k":3, "e":1, "act":"relu"},
{"d":4, "c":128, "s":2, "k":3, "e":4, "act":"relu"},
{"d":4, "c":160, "s":2, "k":3, "e":2, "act":"hardswish"},
{"d":3, "c":192, "s":1, "k":3, "e":2, "act":"hardswish"},
{"d":1, "c":384, "s":1, "k":1, "e":1, "act":"hardswish"}
]
该模型在A榜的结果为分数:0.839,用时:0.01069s。模型文件大小:4.5MB。
还有分别在最后一个DW块阶段增加se模块的SEGENet_lightV1和SEGENet_lightV2,但均没有提升分数。例如:
[{"d":1, "c":12, "s":2, "k":3, "e":1, "act":"relu"},
{"d":2, "c":24, "s":2, "k":3, "e":1, "act":"relu", "se":False},
{"d":3, "c":48, "s":2, "k":3, "e":1, "act":"relu", "se":False},
{"d":4, "c":128, "s":2, "k":3, "e":4, "act":"relu", "se":False},
{"d":2, "c":160, "s":2, "k":5, "e":2, "act":"hardswish", "se":False},
{"d":1, "c":192, "s":1, "k":3, "e":3, "act":"hardswish", "se":True},
{"d":1, "c":384, "s":1, "k":1, "e":1, "act":"hardswish"}
]
还有其他激活函数调整、数据增强调整等尝试,但均没起到提升作用。
对GENet的提升遇到瓶颈,遂重新回归到pplcnet_x1_0模型,利用pplcnet_x1_0模型的预训练权重。但我相信如果对GENet_lightV1同样进行360epochs的imagenet预训练,GENet_lightV1在本任务可能优于pplcnet_x1_0,因为不基于预训练,相同配置下训练这两模型,在A榜GENet_lightV1取得了更高的分数。
在A榜的结果为分数:0.847,用时:0.00883s。模型文件大小:6.5MB。
本项目基于pplcnet_x1_0模型增加一个模块来聚集三个阶段的输出特征信息,利用低层和高层的特征信息进行预测,如下所示:
class FPLayer(nn.Layer):
def __init__(self, in_c, out_c, ks):
super().__init__()
assert len(in_c)==len(ks)
layer = []
if len(out_c)!=len(in_c):
out_c = out_c*len(ks)
for (i, j, k) in zip(in_c, out_c, ks):
layer.append(nn.Sequential(nn.AvgPool2D(kernel_size=k, stride=k),
ConvBNLayer(i, 1, j, 1)))
self.layer = nn.LayerList(layer)
def forward(self, inputs):
out = []
for i, x in enumerate(inputs[:-1]):
out.append(self.layer[i](x))
out.append(inputs[-1])
out = paddle.concat(out, axis=1)
return out
pplcnet_x1_0变体模型(即pplcnet_fp_x1_0)的前向传播过程如下:
def forward(self, x):
x = self.conv1(x)
x = self.blocks2(x)
x = self.blocks3(x)
x = self.blocks4(x)
x1 = x
x = self.blocks5(x)
x2 = x
x = self.blocks6(x)
x = self.fp_layer([x1, x2, x])
x = self.avg_pool(x)
if self.last_conv is not None:
x = self.last_conv(x)
x = self.hardswish(x)
x = self.dropout(x)
x = self.flatten(x)
x = self.fc(x)
return x
详细实现见PaddleClas/ppcls/arch/backbone/legendary_models/pp_lcnet_fp.py
在A榜的结果为分数:0.85,用时:0.00882s。模型文件大小:12.2MB。
相比3.6,在训练时使用focal loss损失函数自适应增大对困难图片的学习,实现如下所示:
class FocalLoss(nn.Layer):
"""
Focal loss
"""
def __init__(self, epsilon=None, alpha=1.0, gamma=2):
super().__init__()
if epsilon is not None and (epsilon <= 0 or epsilon >= 1):
epsilon = None
self.epsilon = epsilon
self.alpha = alpha
self.gamma = gamma
def _labelsmoothing(self, target, class_num):
if len(target.shape) == 1 or target.shape[-1] != class_num:
one_hot_target = F.one_hot(target, class_num)
else:
one_hot_target = target
soft_target = F.label_smooth(one_hot_target, epsilon=self.epsilon)
soft_target = paddle.reshape(soft_target, shape=[-1, class_num])
return soft_target, one_hot_target.squeeze(axis=1)
def forward(self, x, label):
if isinstance(x, dict):
x = x["logits"]
pt = F.softmax(x.detach(), axis=-1)
if self.epsilon is not None:
class_num = x.shape[-1]
label, one_hot_target = self._labelsmoothing(label, class_num)
x = -F.log_softmax(x, axis=-1)
loss = paddle.sum(x * label, axis=-1)
else:
if label.shape[-1] == x.shape[-1]:
label = F.softmax(label, axis=-1)
else:
label = F.one_hot(label, x.shape[-1]).squeeze(axis=1)
one_hot_target = label
loss = F.cross_entropy(x, label=label, soft_label=True, reduction="none")
pt = paddle.max(pt*one_hot_target, axis=-1, keepdim=False)
#print(loss.shape, pt.shape)
loss = ((1-pt)**self.gamma)*self.alpha*loss
loss = loss.mean()
return {"FocalLoss": loss}
此时在A榜的结果为分数:0.853,用时:0.00905s。模型文件大小:12.2MB。
这个验证集最优模型也是我们B榜采用的模型,最终B榜为:分数:0.791,用时:0.00908s,模型文件大小:12.2MB,推理时间满足要求,模型文件大小没有。
除此之外,我们还对分类器进行调整,设计两种双分类器加权融合预测方法,在A榜的最好结果为分数:0.852,用时:0.00882s,略逊于原单分类器结构,最终未采纳。例如:
if self.training:
if paddle.rand([1]).item()>=0.5:
out_w = 1.
else:
out_w = 0.
x = out_w*self.fc1(x)+(1.-out_w)*self.fc2(x)
else:
x = (F.softmax(self.fc1(x), axis=-1)+F.softmax(self.fc2(x), axis=-1))/2
return x
请阅读并运行prepare_ipynb准备好训练数据和验证数据
"""
# 安装paddleclas包快速体验
%cd ~
!git clone --depth=1 https://gitee.com/PaddlePaddle/PaddleClas.git
"""
# 不需要运行以上,本项目是基于PaddleClas增加配置文件、数据集、模型文件来实现,项目已内置修改后的PaddleClas套件
#运行解压项目已内置修改后的PaddleClas套件
!unzip -qo PaddleClas.zip
# 切换工作目录
%cd /home/aistudio/PaddleClas
/home/aistudio/PaddleClas
# 一定要执行,不然训练会卡住不动
!export CUDA_VISIBLE_DEVICES=0
-c 后指定相应训练配置文件,参数修改可以进入到相应配置文件修改,PPLCNet_FP_x1_0_aug_fl.yaml为我们最终选择的模型的训练配置
# 开始训练
!python3 -m paddle.distributed.launch \
--gpus="0" \
tools/train.py \
-c ./ppcls/configs/PULC/text_image_orientation/PPLCNet_FP_x1_0_aug_fl.yaml
-o Global.pretrained_model= 后指定想要验证的模型文件名,不包含后缀,模型文件需要与配置文件要求的模型保持一致。
# 快速验证
!python3 tools/eval.py \
-c ./ppcls/configs/PULC/text_image_orientation/PPLCNet_FP_x1_0_aug_fl.yaml \
-o Global.pretrained_model="output/pplcnet_fp_x1_0_aug_fl/PPLCNet_FP_x1_0/best_model"
-o Global.pretrained_model= 后指定想要导出的模型文件名,不包含后缀,模型文件需要与配置文件要求的模型保持一致,-o Global.save_inference_dir= 后指定导出的推理模型的存放路径。
# 导出模型
!python3 tools/export_model.py \
-c ./ppcls/configs/PULC/text_image_orientation/PPLCNet_FP_x1_0_aug_fl.yaml \
-o Global.pretrained_model=output/pplcnet_fp_x1_0_aug_fl/PPLCNet_FP_x1_0/best_model \
-o Global.save_inference_dir=deploy/models/text_image_orientation_infer
使用推理模型预测测试集
%cd /home/aistudio/PaddleClas/deploy
/home/aistudio/PaddleClas/deploy
!pip install paddleclas
# 对测试数据集进行推理,输出结果
!python python/predict_cls.py -c configs/PULC/text_image_orientation/inference_text_image_orientation.yaml -o Global.infer_imgs="/home/aistudio/work/images/"
评测文件: 评测代码以及模型
注意事项:
# 代码示例
# python predict.py [src_image_dir] [predict_filename]
import os
import sys
import glob
import cv2
def process(src_image_dir, output_filename):
current_path = os.path.dirname(__file__)
image_paths = glob.glob(os.path.join(src_image_dir, "*.jpg"))
with open(os.path.join(current_path, output_filename), 'w') as f:
for image_path in image_paths:
image_name = image_path.split('/')[-1]
# do something
# pred_label output
# 保存结果
f.write(f'{image_name} {pred_label}\n')
f.close()
if __name__ == "__main__":
assert len(sys.argv) == 3
src_image_dir = sys.argv[1]
output_filename = sys.argv[2]
process(src_image_dir, output_filename)
创建ZIP压缩包,所有提交内容均放在压缩包的根目录下,比如:result.zip 解压缩之后就是提交的内容文件,而不应该是predict.txt文件;压缩包中的内容可能如下:
|- root
- predict.py
|- model
- model.pdmodel
- model.pdiparams
本项目实现的predict.py文件有两种,一种是调用paddle的静态模型的,一种是调用onnx模型的(推荐且最终采用),详情分别见work/predict.py和work/onnx/predict.py
# 拷贝推理模型至work文件夹下
!cp -r /home/aistudio/PaddleClas/deploy/models/text_image_orientation_infer /home/aistudio/work
%cd /home/aistudio/work
/home/aistudio/work
"""
# 校验预测文件是否能正常推理
!python predict.py images predict.txt
"""
W0821 22:40:28.421113 3058 gpu_resources.cc:61] Please NOTE: device: 0, GPU Compute Capability: 7.0, Driver API Version: 11.2, Runtime API Version: 10.1
W0821 22:40:28.426131 3058 gpu_resources.cc:91] device: 0, cuDNN Version: 7.6.
#打包下载
!zip -r submit.zip predict.py text_image_orientation_infer
# 按照要求安装环境
!pip install onnx==1.10.1 onnxruntime-gpu==1.10 paddle2onnx
# 导出onnx模型
!paddle2onnx --model_dir text_image_orientation_infer/ --model_filename inference.pdmodel --params_filename inference.pdiparams --opset_version 11 --save_file onnx/result.onnx
# aistudio环境无法执行
#!python onnx/predict.py images predict.txt
%cd /home/aistudio/work/onnx/
!zip -r submit_onnx.zip predict.py result.onnx
/home/aistudio/work/onnx
adding: predict.py (deflated 58%)
adding: result.onnx (deflated 8%)
保存的最好模型可见PaddleClas/output/pplcnet_fp_x1_0_aug_fl_my,训练日志见PaddleClas/output/pplcnet_fp_x1_0_aug_fl_my/PPLCNet_FP_x1_0/train.log。最终提交的静态推理checkpoint存放在work目录下的text_image_orientation_infer和onnx中的result.onnx,提交的压缩包为work/onnx/submit_onnx_testB.zip
将压缩包下载下来在比赛提交页面提交,等待几分钟即可有任何问题,欢迎评论区留言交流。
百度网盘AI大赛-文档图像方向识别赛第5名方案
Markdown Python Text C++ Shell other
Dear OpenI User
Thank you for your continuous support to the Openl Qizhi Community AI Collaboration Platform. In order to protect your usage rights and ensure network security, we updated the Openl Qizhi Community AI Collaboration Platform Usage Agreement in January 2024. The updated agreement specifies that users are prohibited from using intranet penetration tools. After you click "Agree and continue", you can continue to use our services. Thank you for your cooperation and understanding.
For more agreement content, please refer to the《Openl Qizhi Community AI Collaboration Platform Usage Agreement》