1 百度 aistudio 平台使用
免费算力支持
Notebook中使用Shell命令
使用pip来安装自己需要的package (但不支持apt-get)
查看当前环境中安装的package
持久化安装
使用git命令来同步代码 (暂时需要Paddle 1.4.1以上)
文件下载
项目启停
选择算力
执行和调试
Magic命令
Magic命令是Notebook的高级用法了. 可以运行一些特殊的指令. Magic 命令的前面带有一个或两个百分号(% 或 %%),分别代表行 Magic 命令和单元格 Magic 命令。行 Magic 命令仅应用于编写 Magic 命令时所在的行,而单元格 Magic 命令应用于整个单元格。
举个例子:
#显示全部可用的Magic命令
%lsmagic
#使用Magic命令来统计运行时长
import random
%%timeit
prize = 0
for i in range(100):
roll = random.randint(1, 6)
if roll%2 == 0:
prize += roll
else:
prize -= 1
甚至还可以直接嵌入可视化内容, 例如%matplotlib inline:
%matplotlib inline
%config InlineBackend.figure_format = 'retina'
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0,1,300)
for w in range(2,6,2):
plt.plot(x, np.sin(np.pi*x)*np.sin(2*w*np.pi*x))
在matplotlib中显示中文字体, 需要额外的处理:
方法一:参数指定
在需要显示中文的方法中设置fontproperties的值
%matplotlib inline
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.font_manager as font_manager
font = font_manager.FontProperties(fname='/usr/share/fonts/fangzheng/FZSYJW.TTF', size=16) # 创建字体对象
labels = ['娱乐', '育儿', '饮食', '房贷', '交通', '其它']
sizes = [2, 3, 12, 70, 2, 8]
explode = [0, 0, 0, 0.1, 0, 0]
plt.pie(sizes, explode=explode, labels=labels, textprops={'fontproperties': font})
plt.title('饼图', fontproperties=font)
plt.show()
方法二:全局设置
%matplotlib inline
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.font_manager as font_manager
# 设置显示中文
matplotlib.rcParams['font.sans-serif'] = ['FZSongYi-Z13S'] # 指定默认字体
matplotlib.rcParams['axes.unicode_minus'] = False # 解决保存图像是负号'-'显示为方块的问题
# 可以不设置
matplotlib.rcParams['font.size'] = 16
labels = ['娱乐', '育儿', '饮食', '房贷', '交通', '其它']
sizes = [2, 3, 12, 70, 2, 8]
explode = [0, 0, 0, 0.1, 0, 0]
plt.pie(sizes, explode=explode, labels=labels)
plt.title('饼图')
plt.show()
%env:设置环境变量
使用该命令, 可以在不必重启Kernel的情况下管理notebook的环境变量
# Running %env without any arguments
# lists all environment variables
# The line below sets the environment
# variable OMP_NUM_THREADS
%env OMP_NUM_THREADS=4
%run: 运行python代码
使用%run 可以运行.py格式的python代码
当然是用!python也是可以的
我在项目空间中上传了一个.py文件, 里面只有一行print代码. 我们执行一下看看.
%%writefile and %pycat: 导出cell内容/显示外部脚本的内容
AI Studio当前支持一定格式文件的预览和处理, 如果您的格式比较特殊, 尚未支持的话, 不妨试试这两个命令.
%%writefile magic可以把cell的内容保存到外部文件里。 而%pycat则可把外部文件展示在Cell中
%%writefile SaveToPythonCode.py
from math import sqrt
for i in range(2,10):
flag=1
k=int(sqrt(i))
for j in range(2,k+1):
if i%j==0:
flag=0
break
if(flag):
print(i)
关于快速查看某个对象/方法/接口的用法
在要查询的对象前输入?或??并执行即可. 单问号是普通信息, 双问号是详细信息.
前提: 该对象方法已经被正确导入(import)
import paddle
?paddle.nn.Conv2D
关于变量监控
你可以通过修改内核选项ast_note_interactivity,使得Jupyter对独占一行的所有变量或者语句都自动显示,这样你就可以马上看到多个语句的运行结果了。
!pip install pydataset
from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = "all"
from pydataset import data
quakes = data('quakes')
quakes.head()
quakes.tail()
关于调试代码
Notebook自带一个调试器, 叫The Python Debugger (pdb),我们来看看它是如何工作的.
# 对, 它也是依赖Magic命令启动的.
# 理论上pdb是可以通过脚本形式来启动. 但是在Notebook中不行, 会造成阻断
%pdb
def reverse(x: int) -> int:
inputnumber = x.__str__()
reversedStr = inputnumber[::-1]
strOriLen = len(reversedStr)
result = list()
flag = 1
for i in range(0, strOriLen):
if i == (strOriLen -1) and reversedStr[i] == '-' :
flag = -1
else:
result.append(reversedStr[i])
outputs = ''.join(result)
outputInt = int(outputs)
outputInt = outputInt * flag
if outputInt > pow(2,31)-1 or outputInt < -1 * pow(2,31):
outputInt = 0
return outputInt
print(reverse(134))
import pdb
pdb.set_trace()
import pdb
import sys
def add(num1=0, num2=0):
return int(num1) + int(num2)
def sub(num1=0, num2=0):
return int(num1) - int(num2)
def main():
#Assuming our inputs are valid numbers
num1 = 33
num2 = 44
pdb.set_trace() # <-- 这个表示添加断点
addition = add(num1, num2)
print (addition)
subtraction = sub(num1, num2)
print (subtraction)
if __name__ == '__main__':
main()
3. 下一行 - > n
4. 打印 - > p
5. 动态添加断点 - > b
6. 动态分配变量
7. 退出 - > q
还有一种更好的方式, 叫ipdb. 用法和PDB很相似. 不过返回的输出是彩色的.
- ENTER (重复上次命令)
- c (继续)
- l (查找当前位于哪里)
- s (进入子程序,如果当前有一个函数调用,那么 s 会进入被调用的函数体)
- n(ext) 让程序运行下一行,如果当前语句有一个函数调用,用 n 是不会进入被调用的函数体中的
- r (运行直到子程序结束)
- !<python 命令>
- h (帮助)
- a(rgs) 打印当前函数的参数
- j(ump) 让程序跳转到指定的行数
- l(ist) 可以列出当前将要运行的代码块
- p(rint) 最有用的命令之一,打印某个变量
- q(uit) 退出调试
- r(eturn) 继续执行,直到函数体返回
一些发展历程
并没有什么作用
学习模式
知道监督学习就行了,落地的项目基本都是监督学习
2 神经网络(重要但是理解就行)
3.1 神经网络
神经元分为三种不同类型的层次:
- 输入层接收输入数据。在我们的例子中,输入层有四个神经元:出发站、目的地站、出发日期和巴士公司。输入层会将输入数据传递给第一个隐藏层。
- 隐藏层对输入数据进行数学计算。创建神经网络的挑战之一是决定隐藏层的数量,以及每一层中的神经元的数量。
- 人工神经网络的输出层是神经元的最后一层,主要作用是为此程序产生给定的输出,在本例中输出结果是预测的价格值。
神经元之间的每个连接都有一个权重。这个权重表示输入值的重要性。模型所做的就是学习每个元素对价格的贡献有多少。这些“贡献”是模型中的权重。一个特征的权重越高,说明该特征比其他特征更为重要。
在预测公交票价时,出发日期是影响最终票价的最为重要的因素之一。因此,出发日期的神经元连接具有较大的“权重”。
每个神经元都有一个激活函数。它主要是一个根据输入传递输出的函数。 当一组输入数据通过神经网络中的所有层时,最终通过输出层返回输出数据。
3.2 通过训练改进神经网络
为了提高“AI价格评估”的精度,我们需要将其预测结果与过去的结果进行比较,为此,我们需要两个要素:
- 大量的计算能力;
- 大量的数据。
训练AI的过程中,重要的是给它的输入数据集(一个数据集是一个单独地或组合地或作为一个整体被访问的数据集合),此外还需要对其输出结果与数据集中的输出结果进行对比。因为AI一直是“新的”,它的输出结果有可能是错误的。
对于我们的公交票价模型,我们必须找到过去票价的历史数据。由于有大量“公交车站”和“出发日期”的可能组合,因而我们需要一个非常大的票价清单。
一旦我们遍历了整个数据集,就有可能创建一个函数来衡量AI输出与实际输出(历史数据)之间的差异。这个函数叫做成本函数。即成本函数是一个衡量模型准确率的指标,衡量依据为此模型估计X与Y间关系的能力。
模型训练的目标是使成本函数等于零,即当AI的输出结果与数据集的输出结果一致时(成本函数等于0)。
3.3 我们如何降低成本函数呢?
通过使用一种叫做梯度下降的方法。梯度衡量得是,如果你稍微改变一下输入值,函数的输出值会发生多大的变化。
梯度下降法是一种求函数最小值的方法。在这种情况下,目标是取得成本函数的最小值。 它通过每次数据集迭代之后优化模型的权重来训练模型。通过计算某一权重集下代价函数的梯度,可以看出最小值的梯度方向。
第一个例子:手写数字识别(哪里都是它)
我们这里要解决的问题是,将手写数字的灰度图像(28 像素×28 像素)划分到 10 个类别 中(0~9)。我们将使用 MNIST 数据集,它是机器学习领域的一个经典数据集,其历史几乎和这 个领域一样长,而且已被人们深入研究。这个数据集包含 60 000 张训练图像和 10 000 张测试图 像,由美国国家标准与技术研究院(National Institute of Standards and Technology,即 MNIST 中 的 NIST)在 20 世纪 80 年代收集得到。你可以将“解决”MNIST 问题看作深度学习的“Hello World”,正是用它来验证你的算法是否按预期运行。当你成为机器学习从业者后,会发现 MNIST 一次又一次地出现在科学论文、博客文章等中。
“MNIST是一个非常有名的手写体数字识别数据集,在很多资料中,这个数据集都会被用作深度学习的入门样例。
Step1:准备数据
1.MINIST数据集包含60000个训练集和10000测试数据集。分为图片和标签,图片是28*28的像素矩阵,标签为0~9共10个数字。
2.使用飞桨内置数据集 paddle.vision,datasets.MNIST
定义MNIST数据集的 train_dataset 和 test_dataset。
3.使用 Normalize
接口对图片进行归一化。
import paddle
from paddle.vision.transforms import Normalize
transform = Normalize(mean=[127.5],
std=[127.5],
data_format='CHW')
# 使用transform对数据集做归一化
print('download training data and load training data')
train_dataset = paddle.vision.datasets.MNIST(mode='train', transform=transform)
test_dataset = paddle.vision.datasets.MNIST(mode='test', transform=transform)
print('load finished')
#取一条数据,观察一下mnist数据集
import numpy as np
import matplotlib.pyplot as plt
train_data0, train_label_0 = train_dataset[0][0],train_dataset[0][1]
train_data0 = train_data0.reshape([28,28])
plt.figure(figsize=(2,2))
plt.imshow(train_data0, cmap=plt.cm.binary)
print('train_data0 label is: ' + str(train_label_0))
Step2: 配置网络
以下的代码判断就是定义一个简单的多层感知器,一共有三层,两个大小为100的隐层和一个大小为10的输出层,因为MNIST数据集是手写0到9的灰度图像,类别有10个,所以最后的输出大小是10。最后输出层的激活函数是Softmax,所以最后的输出层相当于一个分类器。加上一个输入层的话,多层感知器的结构是:输入层-->>隐层-->>隐层-->>输出层。
# 定义多层感知机
class MultilayerPerceptron(paddle.nn.Layer):
def __init__(self, in_features):
super(MultilayerPerceptron, self).__init__()
# 形状变换,将数据形状从 [] 变为 []
self.flatten = paddle.nn.Flatten()
# 第一个全连接层
self.linear1 = paddle.nn.Linear(in_features=in_features, out_features=100)
# 使用ReLU激活函数
self.act1 = paddle.nn.ReLU()
# 第二个全连接层
self.linear2 = paddle.nn.Linear(in_features=100, out_features=100)
# 使用ReLU激活函数
self.act2 = paddle.nn.ReLU()
# 第三个全连接层
self.linear3 = paddle.nn.Linear(in_features=100, out_features=10)
def forward(self, x):
# x = x.reshape((-1, 1, 28, 28))
x = self.flatten(x)
x = self.linear1(x)
x = self.act1(x)
x = self.linear2(x)
x = self.act2(x)
x = self.linear3(x)
return x
# 使用 paddle.Model 封装 MultilayerPerceptron
model = paddle.Model(MultilayerPerceptron(in_features=784))
# 使用 summary 打印模型结构
model.summary((-1, 1, 28, 28))
接着是配置模型,在这一步,我们需要指定模型训练时所使用的优化算法与损失函数,此外,这里我们也可以定义计算精度相关的API。
# 配置模型
model.prepare(paddle.optimizer.Adam(parameters=model.parameters()), # 使用Adam算法进行优化
paddle.nn.CrossEntropyLoss(), # 使用CrossEntropyLoss 计算损失
paddle.metric.Accuracy()) # 使用Accuracy 计算精度
Step3:模型训练
使用飞桨高层API,可以很快的完成模型训练的部分,只需要在 prepare
配置好模型训练的相关算法后,调用 fit
接口,指定训练的数据集,训练的轮数以及数据的batch_size,就可以完成模型的训练。
# 开始模型训练
model.fit(train_dataset, # 设置训练数据集
epochs=5, # 设置训练轮数
batch_size=64, # 设置 batch_size
verbose=1)
Step4: 模型评估
使用飞桨高层API完成模型评估也非常的简单,只需要调用 evaluate
接口并传入验证集即可。这里我们使用测试集作为验证集。
model.evaluate(test_dataset, verbose=1)
Step5:模型预测
使用飞桨高层API完成模型预测也非常的简单,只需要调用 predict
接口并传入测试集即可。
results = model.predict(test_dataset)
# 获取概率最大的label
lab = np.argsort(results) #argsort函数返回的是result数组值从小到大的索引值
# print(lab)
print("该图片的预测结果的label为: %d" % lab[0][0][0][-1]) #-1代表读取数组中倒数第一列
3OCR技术背景
1. OCR技术背景
1.1 OCR技术的应用场景
- OCR是什么 成熟且应用广泛的cv方向 卷的一批
OCR(Optical Character Recognition,光学字符识别)是计算机视觉重要方向之一。传统定义的OCR一般面向扫描文档类对象,现在我们常说的OCR一般指场景文字识别(Scene Text Recognition,STR),主要面向自然场景,如下图中所示的牌匾等各种自然场景可见的文字。
- OCR有哪些应用场景? 传统方案和深度学习方案对不同场景泛化性都没有那么好,所以分不同业务场景去解决问题
OCR技术有着丰富的应用场景,一类典型的场景是日常生活中广泛应用的面向垂类的结构化文本识别,比如车牌识别、银行卡信息识别、身份证信息识别、火车票信息识别等等。这些小垂类的共同特点是格式固定,因此非常适合使用OCR技术进行自动化,可以极大的减轻人力成本,提升效率。
这种面向垂类的结构化文本识别是目前ocr应用最广泛、并且技术相对较成熟的场景。
除了面向垂类的结构化文本识别,通用OCR技术也有广泛的应用,并且常常和其他技术结合完成多模态任务,例如在视频场景中,经常使用OCR技术进行字幕自动翻译、内容安全监控等等,或者与视觉特征相结合,完成视频理解、视频搜索等任务。
1.2 OCR技术挑战
OCR的技术难点可以分为算法层和应用层两方面。
- 算法层
OCR丰富的应用场景,决定了它会存在很多技术难点。这里给出了常见的8种问题:
这些问题给文本检测和文本识别都带来了巨大的技术挑战,可以看到,这些挑战主要都是面向自然场景,目前学术界的研究也主要聚焦在自然场景,OCR领域在学术上的常用数据集也都是自然场景。针对这些问题的研究很多,相对来说,识别比检测面临更大的挑战。
- 应用层
在实际应用中,尤其是在广泛的通用场景下,除了上一节总结的仿射变换、尺度问题、光照不足、拍摄模糊等算法层面的技术难点,OCR技术还面临两大落地难点:
- 海量数据要求OCR能够实时处理。 OCR应用常对接海量数据,我们要求或希望数据能够得到实时处理,模型的速度做到实时是一个不小的挑战。
- 端侧应用要求OCR模型足够轻量,识别速度足够快。 OCR应用常部署在移动端或嵌入式硬件,端侧OCR应用一般有两种模式:上传到服务器 vs. 端侧直接识别,考虑到上传到服务器的方式对网络有要求,实时性较低,并且请求量过大时服务器压力大,以及数据传输的安全性问题,我们希望能够直接在端侧完成OCR识别,而端侧的存储空间和计算能力有限,因此对OCR模型的大小和预测速度有很高的要求。
三要素 识别效果 + 模型大小 + 推理速度
2. OCR前沿算法
虽然OCR是一个相对具体的任务,但涉及了多方面的技术,包括文本检测、文本识别、端到端文本识别、文档分析等等。学术上关于OCR各项相关技术的研究层出不穷,下文将简要介绍OCR任务中的几种关键技术的相关工作。
2.1 文本检测
文本检测的任务是定位出输入图像中的文字区域。近年来学术界关于文本检测的研究非常丰富,一类方法将文本检测视为目标检测中的一个特定场景,基于通用目标检测算法进行改进适配,如TextBoxes[1]基于一阶段目标检测器SSD[2]算法,调整目标框使之适合极端长宽比的文本行,CTPN[3]则是基于Faster RCNN[4]架构改进而来。但是文本检测与目标检测在目标信息以及任务本身上仍存在一些区别,如文本一般长宽比较大,往往呈“条状”,文本行之间可能比较密集,弯曲文本等,因此又衍生了很多专用于文本检测的算法,如EAST[5]、PSENet[6]、DBNet[7]等等。
目前较为流行的文本检测算法可以大致分为基于回归和基于分割的两大类文本检测算法,也有一些算法将二者相结合。基于回归的算法借鉴通用物体检测算法,通过设定anchor回归检测框,或者直接做像素回归,这类方法对规则形状文本检测效果较好,但是对不规则形状的文本检测效果会相对差一些,比如CTPN[3]对水平文本的检测效果较好,但对倾斜、弯曲文本的检测效果较差,SegLink[8]对长文本比较好,但对分布稀疏的文本效果较差;基于分割的算法引入了Mask-RCNN[9],这类算法在各种场景、对各种形状文本的检测效果都可以达到一个更高的水平,但缺点就是后处理一般会比较复杂,因此常常存在速度问题,并且无法解决重叠文本的检测问题。
Mask-RCNN 是何凯明大神继Faster-RCNN后的又一力作,集成了物体检测和实例分割两大功能,并且在性能上上也超过了Faster-RCNN。
PaddelOCR 默认是基于分割DB 算法--- > 所以检测效果不错但慢且后处理复杂难以修改
图7 文本检测算法概览
图8 基于回归的CTPN[3]算法优化anchor 基于分割的DB[7]算法优化后处理 回归+分割的SAST[10]算法
2.2 文本识别
文本识别的任务是识别出图像中的文字内容,一般输入来自于文本检测得到的文本框截取出的图像文字区域。文本识别一般可以根据待识别文本形状分为规则文本识别和不规则文本识别两大类。规则文本主要指印刷字体、扫描文本等,文本大致处在水平线位置;不规则文本往往不在水平位置,存在弯曲、遮挡、模糊等问题。不规则文本场景具有很大的挑战性,也是目前文本识别领域的主要研究方向。
图9 (左)规则文本 VS. (右)不规则文本
规则文本识别的算法根据解码方式的不同可以大致分为基于CTC和Sequence2Sequence两种,将网络学习到的序列特征 转化为 最终的识别结果 的处理方式不同。基于CTC的算法以经典的CRNN[11]为代表。
图10 基于CTC的识别算法 VS. 基于Attention的识别算法
不规则文本的识别算法相比更为丰富,如STAR-Net[12]等方法通过加入TPS等矫正模块,将不规则文本矫正为规则的矩形后再进行识别;RARE[13]等基于Attention的方法增强了对序列之间各部分相关性的关注;基于分割的方法将文本行的各字符作为独立个体,相比与对整个文本行做矫正后识别,识别分割出的单个字符更加容易;此外,随着近年来Transfomer[14]的快速发展和在各类任务中的有效性验证,也出现了一批基于Transformer的文本识别算法,这类方法利用transformer结构解决CNN在长依赖建模上的局限性问题,也取得了不错的效果。
2.3 文档结构化识别
传统意义上的OCR技术可以解决文字的检测和识别需求,但在实际应用场景中,最终需要获取的往往是结构化的信息,如身份证、发票的信息格式化抽取,表格的结构化识别等等,多在快递单据抽取、合同内容比对、金融保理单信息比对、物流业单据识别等场景下应用。OCR结果+后处理是一种常用的结构化方案,但流程往往比较复杂,并且后处理需要精细设计,泛化性也比较差。在OCR技术逐渐成熟、结构化信息抽取需求日益旺盛的背景下,版面分析、表格识别、关键信息提取等关于智能文档分析的各种技术受到了越来越多的关注和研究。
1 版面分析 这里会有很多问题
版面分析(Layout Analysis)主要是对文档图像进行内容分类,类别一般可分为纯文本、标题、表格、图片等。现有方法一般将文档中不同的板式当做不同的目标进行检测或分割,如Soto Carlos[16]在目标检测算法Faster R-CNN的基础上,结合上下文信息并利用文档内容的固有位置信息来提高区域检测性能;Sarkar Mausoom[17]等人提出了一种基于先验的分割机制,在非常高的分辨率的图像上训练文档分割模型,解决了过度缩小原始图像导致的密集区域不同结构无法区分进而合并的问题。
图12 版面分析任务示意图
2 表格识别细分
1.1 启发式规则
基于规则恢复他的表格结构 但是后处理复杂泛化性困难
2.2 CNN
表格线 检测 加上少量规则 恢复表格结构 比较丰富实用
3.3 GCN 图连接网络
4.4 端到端 PaddleOCR 使用的这个算法
表格转化为html doc 字符串
- 表格识别
表格识别(Table Recognition)的任务就是将文档里的表格信息进行识别和转换到excel文件中。文本图像中表格种类和样式复杂多样,例如不同的行列合并,不同的内容文本类型等,除此之外文档的样式和拍摄时的光照环境等都为表格识别带来了极大的挑战。这些挑战使得表格识别一直是文档理解领域的研究难点。
图13 表格识别任务示意图
表格识别的方法种类较为丰富,早期的基于启发式规则的传统算法,如Kieninger[18]等人提出的T-Rect等算法,一般通过人工设计规则,连通域检测分析处理;近年来随着深度学习的发展,开始涌现一些基于CNN的表格结构识别算法,如Siddiqui Shoaib Ahmed[19]等人提出的DeepTabStR,Raja Sachin[20]等人提出的TabStruct-Net等;此外,随着图神经网络(Graph Neural Network)的兴起,也有一些研究者尝试将图神经网络应用到表格结构识别问题上,基于图神经网络,将表格识别看作图重建问题,如Xue Wenyuan[21]等人提出的TGRNet;基于端到端的方法直接使用网络完成表格结构的HTML表示输出,端到端的方法大多采用Seq2Seq方法来完成表格结构的预测,如一些基于Attention或Transformer的方法,如TableMaster[22]。
图14 表格识别方法示意图
3 关键信息提取 (要素抽取)
- 关键信息提取
关键信息提取(Key Information Extraction,KIE)是Document VQA中的一个重要任务,主要从图像中提取所需要的关键信息,如从身份证中提取出姓名和公民身份号码信息,这类信息的种类往往在特定任务下是固定的,但是在不同任务间是不同的。
图15 DocVQA任务示意图
KIE通常分为两个子任务进行研究:
- SER: 语义实体识别 (Semantic Entity Recognition),对每一个检测到的文本进行分类,如将其分为姓名,身份证。如下图中的黑色框和红色框。
- RE: 关系抽取 (Relation Extraction),对每一个检测到的文本进行分类,如将其分为问题和的答案。然后对每一个问题找到对应的答案。如下图中的红色框和黑色框分别代表问题和答案,黄色线代表问题和答案之间的对应关系。
图16 ser与re任务
一般的KIE方法基于命名实体识别(Named Entity Recognition,NER)[4]来研究,但是这类方法只利用了图像中的文本信息,缺少对视觉和结构信息的使用,因此精度不高。在此基础上,近几年的方法都开始将视觉和结构信息与文本信息融合到一起,按照对多模态信息进行融合时所采用的的原理可以将这些方法分为下面四种:
- 基于Grid的方法
- 基于Token的方法
- 基于GCN的方法
- 基于End to End 的方法
文档分析相关技术将在第六章进行详细解读和实战。
3. OCR技术的产业实践
3.1 产业实践难点
在实际的产业实践中,开发者常常需要依托开源社区资源启动或推进项目,而开发者使用开源模型又往往面临三大难题:
1. 找不到、选不出
开源社区资源丰富,但是信息不对称导致开发者并不能高效地解决痛点问题。一方面,开源社区资源过于丰富,开发者面对一项需求,无法快速从海量的代码仓库中找到匹配业务需求的项目,即存在“找不到”的问题;另一方面,在算法选型时,英文公开数据集上的指标,无法给开发者常常面对的中文场景提供直接的参考,逐个算法验证需要耗费大量时间和人力,且不能保证选出最合适的算法,即“选不出”。
2. 不适用产业场景
开源社区中的工作往往更多地偏向效果优化,如学术论文代码开源或复现,一般更侧重算法效果,平衡考虑模型大小和速度的工作相比就少很多,而模型大小和预测耗时在产业实践中是两项不容忽视的指标,其重要程度不亚于模型效果。无论是移动端和服务器端,待识别的图像数目往往非常多,都希望模型更小,精度更高,预测速度更快。GPU太贵,最好使用CPU跑起来更经济。在满足业务需求的前提下,模型越轻量占用的资源越少。
3. 优化难、训练部署问题多
直接使用开源算法或模型一般无法直接满足业务需求,实际业务场景中,OCR面临的问题多种多样,业务场景个性化往往需要自定义数据集重新训练,现有的开源项目上,实验各种优化方法的成本较高。此外,OCR应用场景十分丰富,服务端和各种移动端设备上都有着广泛的应用需求,硬件环境多样化就需要支持丰富的部署方式,而开源社区的项目更侧重算法和模型,在预测部署这部分明显支撑不足。要把OCR技术从论文上的算法做到技术落地应用,对开发者的算法和工程能力都有很高的要求。
3.2 产业级OCR开发套件PaddleOCR
3.2.1 PP-OCR与PP-Structrue
1 PP-OCR
PP系列特色模型是飞桨各视觉开发套件针对产业实践需求进行深度优化的模型,力求速度与精度平衡。PaddleOCR中的PP系列特色模型包括针对文字检测识别任务的PP-OCR系列模型和针对文档分析的PP-Structure系列模型。
PP-OCR中英文模型采用的典型的两阶段OCR算法,即检测模型+识别模型的组成方式,具体的算法框架如下:
可以看到,除输入输出外,PP-OCR核心框架包含了3个模块,分别是:文本检测模块、检测框矫正模块、文本识别模块。
- 文本检测模块:核心是一个基于DB检测算法训练的文本检测模型,检测出图像中的文字区域;
- 检测框矫正模块:将检测到的文本框输入检测框矫正模块,在这一阶段,将四点表示的文本框矫正为矩形框,方便后续进行文本识别,另一方面会进行文本方向判断和校正,例如如果判断文本行是倒立的情况,则会进行转正,该功能通过训练一个文本方向分类器实现; 这个方向分类器在实践的效果中并不好 ,建议关掉
- 文本识别模块:最后文本识别模块对矫正后的检测框进行文本识别,得到每个文本框内的文字内容,PP-OCR中使用的经典文本识别算法CRNN。
**2 PP-Structure文档分析模型 **
没体验过 感觉很强
PP-Structure支持版面分析(layout analysis)、表格识别(table recognition)、文档视觉问答(DocVQA)三种子任务。
PP-Structure支持版面分析(layout analysis)、表格识别(table recognition)、文档视觉问答(DocVQA)三种子任务。
PP-Structure核心功能点如下:
- 支持对图片形式的文档进行版面分析,可以划分文字、标题、表格、图片以及列表5类区域(与Layout-Parser联合使用)
- 支持文字、标题、图片以及列表区域提取为文字字段(与PP-OCR联合使用)
- 支持表格区域进行结构化分析,最终结果输出Excel文件
- 支持Python whl包和命令行两种方式,简单易用
- 支持版面分析和表格结构化两类任务自定义训练
- 支持VQA任务-SER和RE
3.2.2 工业级部署方案
飞桨支持全流程、全场景推理部署,模型来源主要分为三种,
第一种使用PaddlePaddle API构建网络结构进行训练所得, 自己训练的
第二种是基于飞桨套件系列,飞桨套件提供了丰富的模型库、简洁易用的API,具备开箱即用,包括视觉模型库PaddleCV、智能语音库PaddleSpeech以及自然语言处理库PaddleNLP等,别人训练的
第三种采用X2Paddle工具从第三方框架(PyTorh、ONNX、TensorFlow等)产出的模型。 其他框架训练的转化过来
飞桨模型可以选用PaddleSlim工具进行压缩、量化以及蒸馏,支持五种部署方案,分别为服务化Paddle Serving、服务端/云端Paddle Inference、移动端/边缘端Paddle Lite、网页前端Paddle.js, 对于Paddle不支持的硬件,比如MCU、地平线、鲲云等国产芯片,可以借助Paddle2ONNX转化为支持ONNX的第三方框架。
Paddle Serving是一套高性能服务框架,旨在帮助用户几个步骤快速将模型在云端服务化部署。目前Paddle Serving支持自定义前后处理、模型组合、模型热加载更新、多机多卡多模型、分布式推理、K8S部署、安全网关和模型加密部署、支持多语言多客户端访问等功能,Paddle Serving官方还提供了包括PaddleOCR在内的40多种模型的部署示例,以帮助用户更快上手。