PDF Search 系列教程来咯,在 Part 1 中,我们将演示如何从 PDF 中提取、处理并存储图像及文本。

随着神经搜索 (Neural Search) 技术的普及,越来越多开发者,开始尝试用 Jina 解决非结构化数据的索引和搜索问题。本系列教程中,我们将演示如何用 Jina 搭建一个 PDF 搜索引擎。

具体内容如下:

* Part 1 将介绍如何从 PDF 中提取、处理并存储图像及文本

* Part 2 将演示如何将这些信息输入到 CLIP 中(CLIP 是一个可以理解图像及文本的深度学习模型)。提取 PDF 图像及文本信息后,CLIP 将生成索引,输入图像或文本,即可进行语义相似性搜索。

* Part 3 通过客户端及 Streamlit 前端,对索引进行搜索。

* Part 4 为其他相关演示,如提取元数据等。

GitHub Repo 详见:

https://github.com/alexcg1/example-pdf-search/

前序简介:预期目标 & 技术栈

预期目标:搭建一个 PDF 搜索引擎,用户输入文本或上传图片,搜索引擎即可返回类似的图片和文本片段,并附带原始 PDF 链接。

本文将着重讲解如何将一个 900 多页的 PDF 处理成可供搜索的向量。

本教程将涉及以下技术栈:

DocArray:a data structure for unstructured data. 通过这个工具可以封装 PDF 文件、文本块、图像块以及搜索引擎的其他输入/输出。

详见:http://docarray.jina.ai/

Jina:为 DocArray Document 搭建流水线及神经搜索引擎,并将其扩展到云端。

详见:https://github.com/jina-ai/jina/

Jina Hub:无需逐一创建处理单元,可直接使用云端可复用模块。

详见:https://hub.jina.ai/

教程详解:提取 PDF 中的文本及图像

提取 PDF 中的文本及图像,有以下方法可供选择:

1. 用 Jina Hub 上的 PDFSegmenter Executor,提取 PDF 中文本块和图像块。

详见:

https://hub.jina.ai/executor/x9w7lcwg

2. 用 ImageMagick 和 OCR 对 PDF 中的每一页进行截图。

详见:

https://imagemagick.org/

3. 将 PDF 转换为 HTML,图片提取到目录,再次将 HTML 转换为文本(这里我们使用的是 Pandoc )。

详见:

https://pandoc.org/

本文将使用方法 1,提取 PDF 中的文本及图像。

 1、创建 PDF(也可使用已有文件) 

首先,我们需要一个示例文件,从维基百科中选择一个词条,并导出为 PDF 作为示例文档。本教程中我们用到的是 Rabbit 词条(也可以称为文章)。

具体地址见:

https://github.com/alexcg1/example-pdf-search/blob/main/data/rabbit.pdf

本教程中使用的浏览器为 Chrome

注意:

* 禁用页眉、页脚等设置,以免索引中出现类似 4/798 页等无关信息。

* 可以尝试通过改变页面大小来避免分页

 2、提取 PDF 中的文本及图像 

借助 Jina Hub 中的 Executor,在 Flow 中运行并提取 PDF 中的数据。在 Jina 中,Flow 是执行重要任务的 Pipeline,可以建立可搜索的 PDF 文档索引,或通过索引进行搜索。

每个 Flow 包括多个 Executor,每个 Executor 负责一个小任务。这些 Executor 串联在一起,对 Document 进行端到端的处理。

这里我们用到了 Jina Hub 上的 Executor--PDFSegmenter。

详见:

https://hub.jina.ai/executor/x9w7lcwg

使用 Jina Sandbox,即可释放本地资源,将运行转移到云端:

from docarray import DocumentArray
from jina import Flowdocs = DocumentArray.from_files("data/*.pdf", recursive=True)flow = (Flow().add(uses="jinahub+sandbox://PDFSegmenter", install_requirements=True, name="segmenter")
)with flow:indexed_docs = flow.index(docs)

将 PDF 文档转换为 DocumentArray 形式。在 Jina 中,每一段数据(文本、图像、PDF 等)都是一个Document,一组Document 组成一个 DocumentArray。

通过 documentary.from _ files () 即可从一个目录自动加载所有内容。

DocumentArray 输入到 Flow 后,处理过的 DocumentArray 将存储在 indexed _ docs 中。

在 rabbit.pdf 中, Indexed _ docs 只包含了一个包括文本块和图像块的 Document。

下图为 DocumentArray 摘要,其中包含了 indexed_docs.summary() 

通过 indexed_docs[0].chunks.summary() 查看部分文本块或图像块:

如上图所示,Document 中一共包括 58 个块,分为 tensor(图像)和字符串(文本)。

从每个 chunk 中打印 chunk.content 

chunks = indexed_docs[0].chunksfor chunk in chunks:print(chunk.content)

图像的形式为 tensor

文本则是一段很长的字符串

 3、处理数据 

对数据进行以下处理:

* 将文本片段分片为更小的块,如句子。上述长字符串包含了过多信息,通过 sentencize,可以从每一个文本块中得到一个明确的语义信息。

* 对图像进行归一化处理,便于后续在深度学习模型中进行编码。

 3.1 将文本进行分句 (sentencizing) 

句子示例如下:

* It was a dark and stormy night.

* What do a raven and a writing desk have in common?

* Turn to p.13 to read about J.R.R. Tolkien pinging google.com in 3.4 seconds.

使用 Jina Hub 的 Sentencizer Executor,运行这些字符串。

Sentencizer Executor 详见:

https://hub.jina.ai/executor/c6focg47

from docarray import DocumentArray, Document
from jina import Executordocs = DocumentArray([Document(text="It was a dark and stormy night."),Document(text="What do a raven and a writing desk have in common?"),Document(text="Turn to p.13 to read about J.R.R. Tolkien pinging google.com in 3.4 seconds")]
)exec = Executor.from_hub("jinahub://Sentencizer")exec.segment(docs, parameters={})for doc in docs:for chunk in doc.chunks:print(chunk.text)print("---")

输入上述三个句子后,得到以下输出:

上图可知 p.13 中的标点符号,被识别成了句号。这里可以借助 SpacySentencizer 进行优化。

SpacySentencizer 是一个 Executor,可以将  spaCy 的 sentencizer 集成到 Jina。

详细链接见:

https://github.com/alexcg1/executor-spacy-sentencizer

只需修改第 12 行代码如下:

from docarray import DocumentArray, Document
from jina import Executordocs = DocumentArray([Document(text="It was a dark and stormy night."),Document(text="What do a raven and a writing desk have in common?"),Document(text="Turn to p.13 to read about J.R.R. Tolkien pinging google.com in 3.4 seconds")]
)exec = Executor.from_hub("jinahub://SpacySentencizer")exec.segment(docs, parameters={})for doc in docs:for chunk in doc.chunks:print(chunk.text)print("---")

现在的结果如下图所示:

将 Executor 添加到 Flow 中:

from docarray import DocumentArray
from jina import Flowdocs = DocumentArray.from_files("data/*.pdf", recursive=True)flow = (Flow().add(uses="jinahub+sandbox://PDFSegmenter", install_requirements=True, name="segmenter").add(uses=ChunkSentencizer, name="chunk_sentencizer")
)with flow:indexed_docs = flow.index(docs)

 3.2 对图像进行归一化处理 

from jina import Executor, requests
import numpy as npclass ImageNormalizer(Executor):@requests(on="/index")def normalize_chunks(self, docs, **kwargs):for doc in docs:for chunk in doc.chunks[...]:if chunk.blob:chunk.convert_blob_to_image_tensor()if hasattr(chunk, "tensor"):if chunk.tensor is not None:chunk.convert_image_tensor_to_uri()chunk.tags["image_datauri"] = chunk.urichunk.tensor = chunk.tensor.astype(np.uint8)chunk.set_image_tensor_shape((64, 64))chunk.set_image_tensor_normalization()

代码解读:

1-6: 通用 Executor 调用代码。第 5 行规定Executor 只有在调用索引 endpoint 时才能处理 Document。

8: 通过 [ ... ] 启用递归,依次对 chunk 进行处理。

9: 出现 blob 后将其转换为张量,以适应 CLIP 编码器。

12-18: 假设出现张量,我们需要把未处理张量的数据 uri 添加到元数据(即 tags)中,以便于后续检索并在前端展示图像。

为了防止文本块与图像块互相干扰:

from docarray import DocumentArray
from jina import Flowdocs = DocumentArray.from_files("data/*.pdf", recursive=True)flow = (Flow().add(uses="jinahub+sandbox://PDFSegmenter", install_requirements=True, name="segmenter").add(uses=ChunkSentencizer, name="chunk_sentencizer").add(uses=ImageNormalizer, name="image_normalizer")
)with flow:indexed_docs = flow.index(docs)

通过上述过程,我们实现了:

* 构建一个全新的 PDF

* 将 PDF 分成文本和图像两部分

* 进一步将文本块分割成句子块

* 对图像进行归一化处理

效果如下图所示:

如图所示,文本块和图片块并没有处在同一个 level

通过一个新的 Executor--ChunkMerger,将文本块和图像块放在同一个 level:

from jina import Executor, requests
import numpy as npclass ImageNormalizer(Executor):@requests(on="/index")def normalize_chunks(self, docs, **kwargs):...class ChunkMerger(Executor):@requests(on="/index")def merge_chunks(self, docs, **kwargs):for doc in docs:  # level 0 documentfor chunk in doc.chunks:if doc.text:docs.pop(chunk.id)doc.chunks = doc.chunks[...]

完成分句 (sentencize) 后,将其直接放到 Flow 中,代码如下:

from docarray import DocumentArray
from executors import ChunkSentencizer, ChunkMerger, ImageNormalizer
from jina import Flowdocs = DocumentArray.from_files("data/*.pdf", recursive=True)flow = (Flow().add(uses="jinahub+sandbox://PDFSegmenter", install_requirements=True, name="segmenter").add(uses=ChunkSentencizer, name="chunk_sentencizer").add(uses=ChunkMerger, name="chunk_merger").add(uses=ImageNormalizer, name="image_normalizer")
)with flow:indexed_docs = flow.index(docs)

以上就是本系列教程 Part 1 的全部内容。在 Part 2 中,我们将为 Flow 添加一个编码器,使用 CLIP 将文本和图像编码为向量,从而简化的语义搜索的过程。

欢迎大家关注下方公众号,持续关注本系列教程更新~

神经搜索、深度学习、推荐系统

教程、Demo、干货分享

扫码备注加入讨论组

更多精彩内容(点击图片阅读)

系列教程 | 用Jina搭建PDF搜索引擎Part 1相关推荐

  1. 王姨劝我学HarmonyOS鸿蒙2.0系列教程之四Git搭建下载实例!

    原创PDF |<Android 深入系统完全讲解>免费开源,可能价值百万! 王姨劝我学HarmonyOS鸿蒙2.0系列教程之三Ability概述&&调用方法! 相信大家看了 ...

  2. matlab 单尾检验,生物统计学系列教程——From-ZJU-CAS(第一篇).pdf

    生物统计学系列教程--From ZJU CAS Preface 最近很多人询问我生物统计的问题,联想起自己也曾受益于热心网友制作的NCBI 使用 教程 (苏州大学urbest)和生物信息学软件使用教程 ...

  3. 王姨劝我学HarmonyOS鸿蒙2.0系列教程之一环境搭建跑起来模拟器!

    原创PDF |<Android 深入系统完全讲解>免费开源,可能价值百万! 学习一门新的技术前,我一般会翻看官方文档,源码,以及网上的一些总结,好形成一个初步印象,让开发的时候心中有谱. ...

  4. 涂鸦Zigbee SDK开发系列教程——2.环境搭建

    本章节主要介绍如何搭建涂鸦 Zigbee ZSU 模组 SDK 开发环境. IAR安装 前往 IAR 官网下载 IAR Embedded Workbench IDE(IAR for Arm),下载安装 ...

  5. 涂鸦蓝牙SDK开发系列教程——3.环境搭建

    本节课主要介绍在应用开发前如何搭建开发环境,包括 SDK 获取.IDE 安装.烧录工具.调试工具等,同样以 BTU 模组为例,演示开发必备环境的准备过程. 一. SDK 获取 获取涂鸦蓝牙芯片 SDK ...

  6. python3.6 django教程_【Python3.6+Django2.0+Xadmin2.0系列教程一】环境搭建及项目创建

    由于工作需要,接触了大半年时间的Django+xadmin框架,一直没空对这块对进行相关的梳理.最近在同事的怂恿下,就在这分享下笔者的学习及工作经验吧. 好了,话不多说,下面开始进入正题: 环境需求: ...

  7. 王姨劝我学HarmonyOS鸿蒙2.0系列教程之二应用知识梳理逻辑!

    原创PDF |<Android 深入系统完全讲解>免费开源,可能价值百万! 王姨劝我学HarmonyOS鸿蒙2.0系列教程之一环境搭建&&跑起来模拟器! 写完了第一篇,跑去 ...

  8. 王姨劝我学HarmonyOS鸿蒙2.0系列教程之三Ability概述调用方法!

    原创PDF |<Android 深入系统完全讲解>免费开源,可能价值百万! 王姨劝我学HarmonyOS鸿蒙2.0系列教程之一环境搭建&&跑起来模拟器! 王姨劝我学Harm ...

  9. keil如何添加h文件_【专栏】Keil系列教程

    置顶/星标公众号,不错过每一条重要消息! 本教程由作者strongerHuang原创发布. 版权所有:禁止商用 申明:该文档仅供个人学习使用,转载请公众号联系作者授权. 该系列教程结合MDK-ARM整 ...

最新文章

  1. 剑指offer:面试题27. 二叉树的镜像
  2. 难以想象SpringBoot中的条件注解底层居然是这样实现的
  3. Raspberry Zero 上实现平滑视频图传
  4. js 处理十万条数据_Python数据可视化2018:为什么这么多的库?
  5. (八) shiro + spring + mybatis整合开发
  6. douchat 4.0 新版发布,助力小程序后台开发
  7. Hibernate hql getHibernateTemplate()常用方法汇总
  8. java ssm框架 缓存_SSM框架之Mybatis(7)延迟加载、缓存及注解
  9. 研究做得好,贡献也要大!腾讯AI Lab正式开源业内最大规模多标签图像数据集...
  10. symfony2的中文视频教程更新中(原创),对Symfony感兴趣的学员可以看下
  11. Python一题三解:查找字符串中每个字符的首次出现
  12. 神经网络中的分类器该如何改成生成器?
  13. linux之我常用的系统重要文件备份命令
  14. 14.UA池和代理池
  15. 用递归的方式处理数组 把递归方法方法定义到数组的原型上 (这是一次脑洞大开的神奇尝试)...
  16. 验证手机号码归属地_excel函数爬取网页手机号码归属地,1.1%的人还不会!
  17. 风力摆?这是不是太简单了点
  18. http://atom8023.blog.51cto.com/2651558/1333582
  19. 2021年中国仪器仪表制造业经营现状分析:营业收入达9101.4亿元,利润总额达957亿元[图]
  20. 从小米雷军的逆天布局你能读出什么?

热门文章

  1. centos7 oracle_使用PLSQL远程连接Oracle
  2. 从实验室走向生活,亚略特推动AI商业价值落地
  3. 卑鄙者的墓志铭:REvil勒索软件罪魁首次被锁定
  4. 一文讲透微服务下如何保证事务的一致性
  5. matrix67 on a plane
  6. 平板如何下载鸿蒙系统,安装了鸿蒙系统的平板,很丝滑很流畅
  7. 初学者学习MFC的资源汇总
  8. MFC类层次结构仿真(参考了侯俊杰的《深入浅出MFC》)
  9. 三种语言的CMS搭建
  10. 基于区块链构建的农产品溯源系统开发