和两位计算机大二的同学,一位大二金融数学,一位大四食品科学的同学参加了 2020 年毕马威商赛,虽然商赛没晋级,但还是学到了不少 CV 的知识,这里整理一下。大家都是 CV 领域的萌新,所以也是重在参与吧。
选题
这次毕马威商赛比较特殊,时长两周,同时给了 两个 题目。一道是「知识图谱构建」,一道是「无边框表格的区域识别」。完整题目如下:
知识图谱构建
构建行业知识图谱(行业不限,金融,法务,审计等),主要包括,1)知识抽取:从不同知识源(尤其是非结构化数据)抽取实体、属性、关系、事件等;2)知识表示:一种计算机可以接受的用于描述知识的数据结构。
需求概述
尽管人工智能依靠机器学习和深度学习取得了快速进展,但这些都是弱人工智能,对于机器的训练,需要人类的监督以及大量的数据来喂养,更有甚者需要人手动对数据进行标记,对于强人工智能而言,这是不可取的。要实现真正的类人智能,机器需要掌握大量的常识性知识,以人的思维模式和知识结构来进行语言理解、视觉场景解析和决策分析。
什么是知识图谱
知识图谱又称为科学知识图谱,在图书情报界称为知识域可视化,或知识领域映射地图,用来显示知识发展进程与结构关系的一系列各种不同的图形,用可视化技术描述知识资源及载体,挖掘、分析、构建、绘制和显示知识及他们互相之间的关系。(百度百科定义)
例如:天眼查、企查查的企业知识图谱,数据包括:企业基础数据、投资关系、任职关系、企业专利数据、企业招标数据、企业招聘数据、企业诉讼数据、企业失信数据、企业新闻数据;利用知识图谱融合以上数据,制作企业知识图谱,并在企业知识图谱之上利用图谱特性,针对金融业务场景制定一系列策略。
表格检测
解析图像内无表格线的表格(或者表格线缺失的表格),并记录下每个表格 4 个顶点的坐标。
需求概述
目前针对各类报告(如财报等)的解读、检验等工作多为人工完成,费时费效。我们希望可以通过图像识别技术自动化此流程,将文件中的表格数据进行抽取、整合,提高服务效率。
由于报告中除了表格还包含了文本、图表、logo 等。因此为实现表格数据的抽取流程,首先需要从每页文文件中检测出表格区域。以便针对表格区域进行后续的识别流程。
如下图所示,文檔中的每一页转化为图像格式,并将其输入到表格检测模型中根据训练的参数得到每个表格 4 个顶点的坐标,从而确定表格的具体位置。样例中为方便展示,将顶点信息在原图中进行连线,绘制出表格区域,实际比赛中输出为各顶点坐标。
评价指标
主要重点关心预测的召回率和精确度。精确度使用 iou 进行评判。
IoU :交并比(Intersection over Union),计算 “预测的边框” 和 “真实的边框” 的交集和并集的比值。
我们的选择
说实话感觉主办方对于出题有点随意,不太实际。在很多选手对题目提出审核标准的质疑的时候,往往也是回答要「商业分析」之类的。感觉主办方想往技术转型但是没有落实好。以往商赛就是商业分析,比拼 PPT 的内容完善和精美程度。但是一个技术型的比赛不给出具体的审核标准和规则是不合理的。
对于题目一,要求确实给的比较抽象,只是介绍了什么是「知识图谱」,对于要做什么没有说明,只知道是做「知识图谱」。
考虑到这个因素,我们选择了题目二,即「无边框表格的区域识别」。要求是给定图片,从中找到「表格」,提取出四个顶点坐标。利用真实坐标围成的区域和我们算法给出的坐标围成的区域计算交并比来衡量准确率。这个任务目标相对比较具体。这个相当于一个算法竞赛。
审题
第二题的思路比较明确,我们需要设计一个算法,使得输入是一张图片,输出是四个点坐标。含义是表格的四个顶点。
那么图片是什么样的图片呢?图片是直接对书本等资料拍摄还是 .pdf
格式截图的图片?根据观察题目意思和例子,我们判断应该是后者。这简化了我们的赛题难度。根据对例子的观察,应该是中英文都要有。
但是,不同于 Kaggle
,天池
等竞赛平台,这次的竞赛没有一个格式规范,甚至没有一个测试集。所以测试的好不好完全就是参赛选手可以随意发挥的。我说我的交并比是 100% 那就是 100%,反正你没有测试集可以给我跑。这是非常致命的很严重的问题,一个比算法的赛题不能以算法作为衡量标准。最后其实就回落到比拼 PPT 的精美程度,而完全没有办法衡量算法怎么样。
这也是我觉得我们没有进入决赛的很关键的一点,我们把重心放在了打磨算法上,而没有在 PPT 美化或者说和“商业案例分析”结合。应该说,这是我们审题的一个重大失误。
思路
审题后开始定方向,首先传统的利用 opencv
处理图像,直接找空白、对齐等特征来检测表格位置很直观,但是感觉不够“高大上”而且找特征其实也挺麻烦的,于是我们直接敲定了使用「深度学习」来做。
这里插一句,其实在 Stackoverflow
上能找到现成的使用 opencv
提取表格位置的代码,挺简单但是效果我觉得其实也还行。
那么怎么利用「深度学习」呢?首先我们的分工是先去找论文和一些入门资料来看。
算法模型
我们的赛题属于「计算机视觉」中的「目标检测」。
论文方向
在研究了一些资料后,我们收集了很多论文。以「Table Detection」为关键词,可以得到很多篇论文。不过,它们大多聚焦于「表格检测」而不是「无边框表格检测」。而且,这些论文通常比较晦涩难懂。
比较突破的是我们发现了 ICDAR 2019
这个会议的主题正好有「表格检测」,而且又是 2019 年的比赛,非常新,让我们狂喜。找其中的论文,发现正好有专注于「无边框表格检测」的论文「A GAN-based Feature Generator for Table Detection」,再次狂喜。这应该是学术界最前沿的「无边框表格检测」的论文,搞定了这篇,岂不是稳赢了?
然而这篇还没发表,只列在了网站上的接受论文里。但是我们没有放弃,通过搜索作者,我们发现他们是北京大学王选计算机研究所的团队。于是团队成员向他们发邮件,成功搞到了这篇文献,并且获取到了微信号。
可是他们不提供数据和代码(这要求是有点过了),只给了论文。无奈我们一行人都是萌新,实在是看不懂,不太能用的上。但这个发邮件联系作者套论文的操作还是挺有意思的经历,哈哈!
简单综述
参照 Dhruv Parthasarathy 在 Athelas 发表的文章和机器之心的翻译,经过了大量的学习后,我们简单理一下「计算机视觉」的发展脉络。
背景
自从 Alex Krizhevsky、Geoff Hinton 和 Ilya Sutskever 在 2012 年赢得了 ImageNet 的冠军,卷积神经网络就成为了分割图像的黄金准则。事实上,从那时起,卷积神经网络不断获得完善,并已在 ImageNet 挑战上超越人类。
虽然这些结果令人印象深刻,但与真实的人类视觉理解的多样性和复杂性相比,图像分类还是简单得多。
在分类中,图像的焦点通常是一个单一目标,任务即是对图像进行简单描述(见上文)。但是当我们在观察周遭世界时,我们处理的任务相对复杂的多。
我们看到的情景包含多个互相重叠的目标以及不同的背景,并且我们不仅要分类这些不同的目标还要识别其边界、差异以及彼此的关系!
卷积神经网络可以帮我们处理如此复杂的任务吗?也就是说,给定一个更为复杂的图像,我们是否可以使用卷积神经网络识别图像中不同的物体及其边界?事实上,正如 Ross Girshick 和其同事在过去几年所做的那样,答案毫无疑问是肯定的。
卷积神经网络的发展
在本文中,我们将介绍目标检测和分割的某些主流技术背后的直观知识,并了解其演变历程。具体来说,我们将介绍 R-CNN(区域 CNN),卷积神经网络在这个问题上的最初的应用,及变体 Fast R-CNN 和 Faster R-CNN。最后,我们将介绍 Facebook Research 最近发布的一篇文章 Mask R-CNN,它扩展了这种对象检测技术从而可以实现像素级分割。上述四篇论文的链接如下:
2014 年:R-CNN - 首次将 CNN 用于目标检测
受到多伦多大学 Hinton 实验室的研究的启发,加州伯克利大学一个由 Jitendra Malik 领导的小组,问了他们自己一个在今天看来似乎是不可避免的问题:
Krizhevsky et. al 的研究成果可在何种程度上被推广至目标检测?
目标检测是一种找到图像中的不同目标并进行分类的任务(如上图所示)。通过在 PASCAL VOC Challenge 测试(一个知名的对象检测挑战赛,类似于 ImageNet),由 Ross Girshick(将在下文细讲)、Jeff Donahue 和 Trevor Darrel 组成的团队发现这个问题确实可通过 Krizhevsky 的研究结果获得解决。他们写道:
Krizhevsky et. al 第一次提出:相比基于更简单、HOG 般的特征的系统,卷及神经网络可显著提升 PASCAL VOC 上的目标检测性能。
现在让我们花点时间来了解他们的架构 R-CNN 的运作的方式。
理解 R-CNN
R-CNN 的目的为接收图像,并正确识别图像中主要目标(通过边界框)的位置。
- 输入:图像
- 输出:边界框+图像中每个目标的标注
但是我们如何找出这些边界框的位置?R-CNN 做了我们也可以直观做到的——在图像中假设了一系列边界,看它们是否可以真的对应一个目标。
R-CNN 创造了这些边界框,或者区域提案(region proposal)关于这个被称为选择性搜索(Selective Search)的方法,可在这里 阅读更多信息。在高级别中,选择性搜索(如上图所示)通过不同尺寸的窗口查看图像,并且对于不同尺寸,其尝试通过纹理、颜色或强度将相邻像素归类,以识别物体。
一旦创建了这些提案,R-CNN 简单地将该区域卷曲到一个标准的平方尺寸,并将其传递给修改版的 AlexNet(ImageNet 2012 的冠军版本,其启发了 R-CNN),如上所示。
在 CNN 的最后一层,R-CNN 添加了一个支持向量机(SVM),它可以简单地界定物体是否为目标,以及是什么目标。这是上图中的第 4 步。
提升边界框
现在,在边界框里找到了目标,我们可以收紧边框以适应目标的真实尺寸吗?我们的确可以这样做,这也是 R-CNN 的最后一步。R-CNN 在区域提案上运行简单的线性回归,以生成更紧密的边界框坐标从而获得最终结果。下面是这一回归模型的输入和输出:
-
输入:对应于目标的图像子区域
-
输出:子区域中目标的新边界框坐标
总结
所以,概括一下,R-CNN 只是以下几个简单的步骤
- 为边界框生成一组提案。
- 通过预训练的 AlexNet 运行边界框中的图像,最后通过 SVM 来查看框中图像的目标是什么。
- 通过线性回归模型运行边框,一旦目标完成分类,输出边框的更紧密的坐标。
2015: Fast R-CNN - 加速和简化 R-CNN
R-CNN 性能很棒,但是因为下述原因运行很慢:
- 它需要 CNN(AlexNet)针对每个单图像的每个区域提案进行前向传递(每个图像大约 2000 次向前传递)。
- 它必须分别训练三个不同的模型 - CNN 生成图像特征,预测类别的分类器和收紧边界框的回归模型。这使得传递(pipeline)难以训练。
2015 年,R-CNN 的第一作者 Ross Girshick 解决了这两个问题,并创造了第二个算法——Fast R-CNN。下面是其主要思想。
Fast R-CNN 见解 1:ROI(兴趣区域)池化
对于 CNN 的前向传递,Girshick 意识到,对于每个图像,很多提出的图像区域总是相互重叠,使得我们一遍又一遍地重复进行 CNN 计算(大约 2000 次!)。他的想法很简单:为什么不让每个图像只运行一次 CNN,然后找到一种在 2000 个提案中共享计算的方法?
这正是 Fast R-CNN 使用被称为 RoIPool(兴趣区域池化)的技术所完成的事情。其要点在于,RoIPool 分享了 CNN 在图像子区域的前向传递。在上图中,请注意如何通过从 CNN 的特征映射选择相应的区域来获取每个区域的 CNN 特征。然后,每个区域的特征简单地池化(通常使用最大池化(Max Pooling))。所以我们所需要的是原始图像的一次传递,而非大约 2000 次!
Fast R-CNN 见解 2:将所有模型并入一个网络
Fast R-CNN 的第二个见解是在单一模型中联合训练卷积神经网络、分类器和边界框回归器。之前我们有不同的模型来提取图像特征(CNN),分类(SVM)和紧缩边界框(回归器),而 Fast R-CNN 使用单一网络计算上述三个模型。
在上述图像中,你可以看到这些工作是如何完成的。Fast R-CNN 在 CNN 顶部用简单的 softmax 层代替了支持向量机分类器(SVM classfier)以输出分类。它还添加了与 softmax 层平行的线性回归层以输出边界框坐标。这样,所有需要的输出均来自一个单一网络!下面是整个模型的输入和输出:
- 输入:带有区域提案的图像
- 输出:带有更紧密边界框的每个区域的目标分类
2016:Faster R-CNN—加速区域提案
即使有了这些进步,Faster R-CNN 中仍存在一个瓶颈问题——区域提案器(region proposer)。正如我们所知,检测目标位置的第一步是产生一系列的潜在边界框或者供测试的兴趣区域。在 Fast R-CNN,通过使用选择性搜索创建这些提案,这是一个相当缓慢的过程,被认为是整个流程的瓶颈。
2015 年中期,由 Shaoqing Ren、Kaiming He、Ross Girshick 和孙剑组成的微软研究团队,找到了一种被其命为 Faster R-CNN 的架构,几乎把区域生成步骤的成本降为零。
Faster R-CNN 的洞见是,区域提案取决于通过 CNN 的前向(forward pass)计算(分类的第一步)的图像特征。为什么不重复使用区域提案的相同的 CNN 结果,以取代单独运行选择性搜索算法?
事实上,这正是 Faster R-CNN 团队取得的成就。上图中你可以看到单个 CNN 如何执行区域提案和分类。这样一来,只需训练一个 CNN,我们几乎就可以免费获得区域提案!作者写道:
我们观察到,区域检测器(如 Fast R-CNN)使用的卷积特征映射也可用于生成区域提案 [从而使区域提案的成本几乎为零]。
以下是其模型的输入和输出:
- 输入:图像(注意并不需要区域提案)。
- 输出:图像中目标的分类和边界框坐标。
如何生成区域
让我们花点时间看看 Faster R-CNN 如何从 CNN 特征生成这些区域提案。Faster R-CNN 在 CNN 特征的顶部添加了一个简单的完全卷积网络,创建了所谓的区域提案网络。
区域生成网络的工作是在 CNN 特征映射上传递滑动窗口,并在每个窗口中输出 k 个潜在边界框和分值,以便评估这些框有多好。这些 k 框表征什么?
我们知道图像中的目标应该符合某些常见的纵横比和尺寸。例如,我们想要一些类似人类形状的矩形框。同样,我们不会看到很多非常窄的边界框。以这种方式,我们创建 k 这样的常用纵横比,称之为锚点框。对于每个这样的锚点框,我们在图像中每个位置输出一个边界框和分值。
考虑到这些锚点框,我们来看看区域提案网络的输入和输出:
- 输入:CNN 特征图。
- 输出:每个锚点的边界框。分值表征边界框中的图像作为目标的可能性。
然后,我们仅将每个可能成为目标的边界框传递到 Fast R-CNN,生成分类和收紧边界框。
2017:Mask R-CNN - 扩展 Faster R-CNN 以用于像素级分割
到目前为止,我们已经懂得如何以许多有趣的方式使用 CNN,以有效地定位图像中带有边框的不同目标。
我们能进一步扩展这些技术,定位每个目标的精确像素,而非仅限于边框吗?这个问题被称为图像分割。Kaiming He 和一群研究人员,包括 Girshick,在 Facebook AI 上使用一种称为 Mask R-CNN 的架构探索了这一图像分割问题。
很像 Fast R-CNN 和 Faster R-CNN,Mask R-CNN 的基本原理非常简单直观。鉴于 Faster R-CNN 目标检测的效果非常好,我们能将其简单地扩展到像素级分割吗?
Mask R-CNN 通过简单地向 Faster R-CNN 添加一个分支来输出二进制 mask,以说明给定像素是否是目标的一部分。如上所述,分支(在上图中为白色)仅仅是 CNN 特征图上的简单的全卷积网络。以下是其输入和输出:
- 输入:CNN 特征图。
- 输出:在像素属于目标的所有位置上都有 1s 的矩阵,其他位置为 0s(这称为二进制 mask)。
但 Mask R-CNN 作者不得不进行一个小的调整,使这个流程按预期工作。
RoiAlign——重对齐 RoIPool 以使其更准确
当运行没有修改的原始 Faster R-CNN 架构时,Mask R-CNN 作者意识到 RoIPool 选择的特征图的区域与原始图像的区域略不对齐。因为图像分割需要像素级特异性,不像边框,这自然地导致不准确。
作者通过使用 RoIAlign 方法简单地调整 RoIPool 来更精确地对齐,从而解决了这个问题。
想象一下,我们有一个尺寸大小为 128x128 的图像和大小为 25x25 的特征图。想象一下,我们想要的是与原始图像中左上方 15x15 像素对应的区域(见上文)。我们如何从特征图选择这些像素?
我们知道原始图像中的每个像素对应于原始图像中的〜25/128 像素。要从原始图像中选择 15 像素,我们只需选择 15 * 25/128〜=2.93 像素。
在 RoIPool,我们会舍弃一些,只选择 2 个像素,导致轻微的错位。然而,在 RoIAlign,我们避免了这样的舍弃。相反,我们使用双线性插值来准确得到 2.93 像素的内容。这很大程度上,让我们避免了由 RoIPool 造成的错位。
一旦这些掩码生成,Mask R-CNN 简单地将它们与来自 Faster R-CNN 的分类和边界框组合,以产生如此惊人的精确分割:
展望
在过去短短 3 年里,我们看到研究界如何从 Krizhevsky 等人最初结果发展为 R-CNN,最后一路成为 Mask R-CNN 的强大结果。单独来看,像 MASK R-CNN 这样的结果似乎是无法达到的惊人飞跃。然而,通过这篇文章,我希望你们认识到,通过多年的辛勤工作和协作,这些进步实际上是直观的且渐进的改进之路。R-CNN、Fast R-CNN、Faster R-CNN 和最终的 Mask R-CNN 提出的每个想法并不一定是跨越式发展,但是它们的总和却带来了非常显著的效果,帮助我们向人类水平的视觉能力又前进了几步。
特别令我兴奋的是,R-CNN 和 Mask R-CNN 间隔只有三年!随着持续的资金、关注和支持,计算机视觉在未来三年会有怎样的发展?我们非常期待。
实战框架
数据
数据从哪儿找?怎么标注格式?
Github
我们首先从 Github 上找到了资源,里面有 400+ 张图片,并且是标注好的数据,以 .csv
的格式。这个数据集的优先是可以直接拿来用,缺点是样本太少,只有 400+ 张,同时缺少中文数据。
我们也找过 ICADR 2019
的比赛数据,可是里面的图片是直接拍的,不是 .pdf
导出的,和我们的需求差异挺大的。还有好多数据集也是这个问题。
简单了解了这些算法以后,摆在眼前的问题就是我们无法复现,除非有现成的轮子。
北大王选计算机研究院
我们又找来了了北大的 Marmot Dataset 公开数据集。这个数据集包含中英文,也是扫描版的 .pdf
,符合我们的要求。总共数量有 2000 张,这个数量级够了。不过这里的数据格式标注的比较复杂(因为原来是为了表格中内容的识别,所以也标注了表格的内容),提取表格坐标还要写个脚本 parse 一下。
我们将数据按照 7:2:1 来划分训练集,验证集和测试集。这些数据还是存在一些表格是有边框的,我们尽可能人工删掉了一些,但是数据集太大,无法全部都人工删除,只能稍微意思一下,所以结果是存在提升的空间的。
框架
我们对数据先进行预处理,然后交给深度学习的框架来跑。预处理部分我们使用openCV
,而深度学习框架,我们选择了基于 Tensorflow
的 luminoth
。
预处理
因为原数据其实就整挺好,所以这里的预处理其实意义不大,只是象征性地做了一下。其实案例里要求的应该是 .pdf
直接导出的图片格式,不过为了严谨和凑字数,我们假定可能存在从现实纸张扫描出来的数据集,因此加上了旋转,降噪这两步,但是实际上并没有用到。
旋转
我们假定给定的图片可能存在歪的现象,所以有拉直操作。
1 | def rotate_bound(image, angle): |
降噪
我们假定图片存在扫描过程中的一些热噪声,需要去除。
1 | from numpy import * |
膨胀
就是 Dilation,实际上就是加粗,对于我们的文字部分和边框(如果有)进行加粗,便于强调那些是特征。
1 | struct = cv2.getStructuringElement(cv2.MORPH_RECT, morph_size) |
二值化
其实就这一步最重要。因为我们只关心一个像素点是有东西还是没东西,一切颜色啊,深度信息啊都不重要,因此我们需要二值化,使得一个像素点只有黑和白两个信息。
1 | pre = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) |
深度学习
考虑到团队的 cv 经验都是 0,因此我们尽量使用现成的轮子。在这里推荐一下 luminoth
,这个框架属实好用,还有自带的可视化分析,效果还不错。可惜已经停止维护了。
分割数据
将数据分割成训练集和验证集。里面的参数根据实际需要,做一定的修改。
1 | lumi dataset transform --type csv --data-dir data/ --output-dir tfdata/ --split train --split val --only-classes=table |
设置训练参数
我们使用的基本上就是默认的,因为不太懂 CV 的调参。这里选用了 faster-RCNN
作为训练的算法。
1 | train: |
开始跑模型
1 | lumi train -c config.yml |
为了加快速度,我们找到了浙江大学超算队,把任务放到超算上跑,最后只跑了一天,速度还不错。
模型分析
使用
1 | lumi checkpoint create config.yml |
来保存模型。
使用
1 | lumi server web --checkpoint c2155084dca6 |
可以直接打开 web server ,来放图片进去并查看检测结果,非常方便。
同时关于模型结果,还可以在 Tensorboard
上来进行可视化的分析。
关于 luminoth
的具体内容可以查看它的文档,总结一下就是非常简单好用。
PPT 展示
要求 PPT 页数有限制,最后成果是这样,没晋级,看着也就图一乐。我们按照
问题背景-> 算法分析 -> 建模流程 -> 模型结果分析
这样的框架来写我们的 PPT。