精华内容
下载资源
问答
  • 这个系列文章【文本信息抽取与结构化】,在自然语言处理中是非常有用和有难度的技术,是文本处理与知识提取不可或缺的技术。 本篇介绍如何从非结构的文档中,提取想要的信息,进而结构化文本。 作者&编辑 | 小Dream...
  • 回答下列问题: (1)如何能构建一个系统,以至从非结构化文本提取结构化数据? (2)有哪些稳健的方法识别一个文本描述的实体和关系? (3)哪些语料库适合这项工作,如何使用它们来训练和评估模型?信息有...
  • 常常在想,自然语言处理到底在做的是一件什么样的事情?到目前为止,我所接触到的NLP其实都是在做一件事情,即将自然语言转化为一种计算机能够理解的形式。这一点在知识图谱、信息抽取、文本摘要这...

    常常在想,自然语言处理到底在做的是一件什么样的事情?到目前为止,我所接触到的NLP其实都是在做一件事情,即将自然语言转化为一种计算机能够理解的形式。这一点在知识图谱、信息抽取、文本摘要这些任务中格外明显。不同的任务的差异在于目标的转化形式不一样,因而不同的任务难度、处理方式存在差异。

    这个系列文章【文本信息抽取与结构化】,在自然语言处理中是非常有用和有难度的技术,是文本处理与知识提取不可或缺的技术。

    本篇介绍如何从非结构的文档中,提取想要的信息,进而结构化文本。

    作者&编辑 | 小Dream哥

    1  文本结构化的意义

    数字化、线上化在21世纪前15年是非常流行的概念,经过多年的发展,大部分企业都进入了无纸化、线上化的办公、生产和销售时代。

    例如,我们大家熟悉的求职过程,很可能就是一个全程无纸化线上化的过程。过程可能如下所示:

    1.你在某招聘网站填写线上简历;

    2.某公司HR在线上看到你的简历,匹配到其公司的某个职位,打电话问你是否感兴趣;

    3.你觉得职位、公司都挺满意,欣然答应,并约好了面试时间。细心的你最后还询问HR是否需要带纸质简历,HR告诉你不需要,简历已经在线发给了面试官。面试过程,面试官在该公司的面试系统记录了你的面试表现,表示你通过了该公司的面试。

    4.随后,你收到了该公司的邮件,邮件不仅通知你通过了该公司的考核,发给你offer。邮件里还附带一个二维码,告诉你下载这个APP,后续的报道流程,都通过这个APP进行。

    5.你非常开心的下载并打开这个APP,APP让你填了基础的信息,并请你提供二甲以上的医院的入职体检证明。

    6.你的体检证明通过了该公司的审核,你们在线上签订了劳动合同,并确定了入职日期。

    这是一个完全线上化和无纸化的过程,我们可以想一下,这各过程的这些步骤,哪些部分是最繁琐,也是目前最可能应用AI技术替代人处理的。在我看来,至少有加粗的两个部分是可以用AI技术来减轻HR工作的。以“匹配到其公司的某个职位”也就是筛选简历为例,我听过很多猎头和HR对海量筛选简历之抱怨。不过,用目前的NLP技术是完全可以实现大部分的简历分析和匹配的。因为不同候选人的简历格式不一,简历的分析和匹配势必就会涉及简历的结构化,以提取候选人的姓名、技能、学历以及工作经验等重要信息。

    我们细想一下,其实人工处理大量文本的场景是很常见的。例如,大公司的签报流程处理人员和财务审计人员,招投标公司的标书筛选过程等等。随着线上化的普及,这些场景给了NLP技术展现其能力的大好时机,通过文本的结构化相关的技术,从线上化文本中,提取相应场景中感兴趣的信息,能够极大的减少人工的工作量,提高效率。

    总的来说,文本的结构化通过快速实现文本的理解和信息提取,大量的减少人工负荷。在线上化、无纸化流程作业的今天,具有很广泛的应用空间。

    2 文本如何结构化

    文本的结构化是一个相当复杂的工程问题,通常情况下,办公或者生产过程中出现的文本为word、PDF等有一定段落结构和篇幅的文档。按照现在的NLP技术,端到端的实现文本的结构化,是不太可能的事情。

    基于此,面对一个复杂的问题,通常需要步步分解,将一个大的看上去很艰难问题,分解成一些小的容易处理的问题。那我们来看看,笔者认为,通过哪些步骤能够完成这样一个任务。

    2.1 定义需求

    我这里提到的文本结构化,通常是基于某一个场景的某一些需求,例如,求职招聘场景中的简历筛选与匹配需求。所以,要对文本结构化,首先需要了解的是,要从源文本中获取哪些信息?也就是定义需求。通过需求定义,我们给问题划了一个范围,明确了目标。

    例如,在求职招聘场景中,面对形形色色的候选人简历,HR感兴趣的是候选人的如下信息:

    姓名:周公举

    出生年月:1983年5月

    毕业学校:中央音乐学院

    学历:本科

    专业:流行音乐

    技能:演唱,作曲,导演

    工作经验:曾发行数张音乐唱片,创作N首原创歌曲;参演5部电影,自导自演2部。

    其实结构化简历,还有一个场景需要,就是目前很多招聘网站的收费服务:简历优化。将简历结构化的信息抽取之后,后续的简历筛选,简历优化以及岗位匹配运用NLP技术或者甚至一些简单的判断就能有不错的效果了。

    这个过程,通常是跟业务强相关的,可以由业务专家、产品经理主导,算法工程师配合完成。

    2.2 文本的预处理

    前面提到,通常情况下,办公或者生产过程中出现的文本为word、PDF等有一定段落结构篇幅的文档。要用算法处理这些原始文档,通常会面临这些问题:

    1.NLP模型无法直接处理PDF等类图像格式的文档;

    2.文档有可能篇幅很长,不可能直接将文本内容输入模型进行处理;

    3.比较难获取文档中的文档结构信息、篇章层次信息、字体格式信息等。

    对于第一个问题,通常需要PDFReader,将类图像格式的文档转化为文本文档;对于第二个问题,通常会将文档进行分段落处理,舍弃无意义的段落,截取感兴趣的段落;对于第三个问题,是最复杂的问题,这往往已经不是单纯的自然语言处理的问题,通常需要结合计算机图像处理的技术以及OCR技术,才能比较好的解决第三个问题。

    文本的预处理过程,是一个复杂且重要的步骤,预处理的效果直接影响后续信息抽取模型的效果。

    通常,预处理步骤的输出应该是如下形式的json文件:

    {

        {

            “基本信息”:“

                    姓名:***

                    出生年月:1***

                   ”

        },

        {

            “个人介绍”:“擅长C,python,java等编程语言,熟悉NLP算法。。。”

        },

        {

            “教育经历”:{

                “本科”:“华中科技大学”,

                “硕士”:“中国科学院”

            }

        }

    }

    通过输出这种层次丰富、信息准确的JSON数据,后面的NLP信息抽取模型,就能够大展身手了。

    出于篇幅,“文本的结构化【上】”部分就先讲到这里,后续的部分,在我们下集部分继续给大家介绍,感兴趣的同学敬请关注。

    总结

    文本信息抽取与结构化是目前NLP中最为实际且效益最大的任务,熟悉这个任务是一个NLP算法工程师必需要做的事情。

    读者们可以留言,或者加入我们的NLP群进行讨论。感兴趣的同学可以微信搜索jen104,备注"加入有三AI NLP群"

    下期预告:详聊文本的结构化【下】

    知识星球推荐

    _

    _

    扫描上面的二维码,就可以加入我们的星球,助你成长为一名合格的自然语言处理算法工程师。

    知识星球主要有以下内容:

    (1) 聊天机器人;

    (2) 知识图谱;

    (3) NLP预训练模型。

    转载文章请后台联系

    侵权必究

    往期精选

    _

    展开全文
  • 技术组件nlp允许提取结构化信息,此提取在代理中定义。 代理商是会说多种语言的助手,以查找相关数据。 nlp组件将一组JSON格式的代理和非结构化文本内容作为输入,以提供结构化数据的JSON流作为输出。 第二个技术...
  • 从半结构化文本文档中提取数据的库。 它最适合于不具有正式结构且格式为纯文本(或易于转换)的文件中的数据处理。 XML,CSV和HTML等结构化文件不适合Raspador用例,并且有很好的替代方法来提取数据,例如 , , ...
  • 如提供N个病历,识别是否有高血压,糖尿病,如何用NLP技术直接生成一个数据集,一行表示一个病人,有高血压就是1,没有就是0
  • 如何用 Python 和正则表达式抽取文本结构化信息

    千次阅读 多人点赞 2019-09-26 14:28:31
    简单、直接、高效地从文本中提炼关键内容。让你体验正则表达式的威力。痛点很多人的日常工作,都是要和大量的文本打交道的。例如学者需要阅读大量的文献材料,从中找到灵感、数据与论...

    简单、直接、高效地从文本中提炼关键内容。让你体验正则表达式的威力。

    痛点

    很多人的日常工作,都是要和大量的文本打交道的。

    例如学者需要阅读大量的文献材料,从中找到灵感、数据与论据。

    学生需要阅读很多教科书和论文,然后写自己的报告或者做幻灯。

    财经分析师,需要从大量的新闻报道中,找到行业的发展趋势和目标企业动态的蛛丝马迹。

    不是所有的文本处理,都那么新鲜而有趣。

    有一项重要但繁琐的工作,就是从大量的文本当中抽取结构化的信息。

    许多数据分析的场景,都要求输入结构化的信息。

    例如在咱们之前介绍过的《贷还是不贷:如何用 Python 和机器学习帮你决策?》和《如何用 Python 和深度神经网络锁定即将流失的客户?》中,你都看到了,机器模型更喜欢被结构化的表格信息来喂养。

    然而,结构化的信息,不一定就在那里,静候你来使用。很多时候,它蕴藏在以往生成的非结构化文本中。

    你可能早已习惯,人工阅读文本信息,把关键点摘取出来,然后把它们拷贝粘贴放到一个表格中。从原理上讲,这样做无可厚非。但是实际操作里面,效率太低,而且太麻烦。

    大部分人,是不愿意从事这种简单重复的枯燥工作的。

    一遍遍机械重复鼠标划定文本范围,“Ctrl+C”、切换到表格文档、找准输入位置,再 “Ctrl+V”……

    这种事儿做得太多,对你的肩肘关节,甚至是身心健康,都有可能造成不利影响。

    想不想尝试用一种更简单的自动化方式,替你快速完成这些烦人的操作步骤呢?

    读过本文后,希望你能找到答案。

    样例

    这里,我们举一个极端简化的中文文本抽取信息例子。

    之所以这样做,是为了避免你在解读数据上花费太多时间。

    我更希望,你能够聚焦于方法,从而掌握新知。

    假设一个高中班主任,高考后让班长统计一下学生们的毕业去向。班长很认真地进行了调查,然后做了如下汇报:

    张华考上了北京大学 

    李萍进了中等技术学校 

    韩梅梅进了百货公司 

    ……

    为了让你对样例足够熟悉,甚至有共鸣,这里我从 1998 年版的新华字典中,“借鉴” 了部分内容。

    够贴心吧?

    现实生活中,一个班大概不会只有 3 个人,因此你可以想象这是一个长长的句子列表。

    但其实班主任有个隐含的意思没有表达出来,即:

    我想要一张表格

    所以,看到这一长串的句子,你可以想象他的表情。

    班长估计也很难堪:

    想要表格你早说啊!

    这时候,假设你是班长,怎么办?

    信息都在文本里面。但如果需要转换成表格,就得一个个信息点去寻找和处理。

    其实,对于四五十人的班级来说,手动操作也不是什么太难的事情。

    但是设想一下,如果你需要处理的数据量,是这个例子的十倍、百倍甚至千万倍呢?

    继续坚持手动处理?

    这不仅麻烦,而且不现实。

    我们需要找到一种简单的方法,帮助我们自动抽取相应的信息。

    此处我们使用的方法,是正则表达式

    正则

    “正则表达式” 这个名字,初听起来好像很玄妙。实际上,它是从英文 “regular expression” 翻译过来的。

    如果译成白话,那就是 “有规律的表述形式”。

    这,听起来,是不是就更加接地气了?

    但是,给你补一下 “假行家 101” 课程:

    说别人听得懂的话,你能唬得住谁?

    约定俗成,咱们继续沿用 “正则表达式”,来称呼它好了。

    从创生之日起,它就给文本处理带来了高效率。

    但是,用它的主要人群,却不是时常跟文字打交道的作家、编辑、学者、文员,而是……

    程序员!

    程序员写的代码,是文本;程序员处理的数据,很多也是文本格式。其中便有很多显著的规律可循。

    正是靠着正则表达式这种独门秘籍,许多别人做起来,需要昏天黑地一整周的任务,程序员可以半小时搞定,然后喝着咖啡等下班。

    即便到了泛人工智能的今天,正则表达式依然有许多令你意想不到的应用。

    例如人机对话系统。

    你可能看了新闻报道,总以为人机对话都是靠着知识图谱或者深度学习搞出来的。

    不能说这里面,没有上述炫酷技术的参与。但它们充其量,只占其中一部分,或许还只是一小部分。

    生产实践里面,大量的对话规则后面,并不是让你倍感神奇深奥的神经网络,而是一堆正则表达式。

    你可能会担心,这样高端的应用技术,自己能掌握吗?

    答案是:

    当然!

    正则表达式,并不难学。

    尤其是当你把它和 Python 结合到一起,那简直就是效率神器了。

    我们这就来看看,正则表达式怎么帮我们识别出样例文本里面 “人名” 和 “去向” 信息。

    试练

    请你开启一个浏览器,键入这个网址(https://regex101.com/)。

    你会看见如下界面。

    它可是一个正则表达式实验的利器。我教 INFO 5731 课程时,学生们就是在掌握了这个工具以后,迅速玩儿转了正则表达式。

    这么好的工具,一定要价不菲吧?

    不,它是免费的。你放心大胆使用就好了。

    我们首先把左侧的编程语言,从默认的 PHP ,调整为 Python。

    之后,把需要进行处理的文本,贴到中间空白的大文本框里面。

    下面我们来尝试进行“匹配”。

    什么叫做匹配呢?

    就是你写一个表达式,电脑便拿着鸡毛当令箭,在每一行文本上,都认认真真地找有没有符合该表达式的文本段落。

    如有,则会高亮显示出来。

    这里我们观察一下,发现每个句子里面,人员去向前面,都有一个 “了” 字。

    好,我们就在中部上方小文本框里,把 “了” 字输入进去。

    可以看到,三句话里面的“了”,全都亮了

    这就是你接触到的第一种匹配方式 —— 按照字符原本的意思来查找一致的内容。

    因为样例文本的规律性,我们可以把 “了” 当成一个定位符,它后面,到句子结束位置,是 “去向” 信息。

    咱们需要找的一半结构化信息,不就是这个 “去向” 吗?

    我们尝试匹配 “去向”。

    怎么匹配呢?这次每一行的字儿都不一样啊?

    没关系,正则表达式强大之处,此时就显示出来了。

    你可以用一个点号,也就是.,表示任意字符。

    字母、数字、标点…… 甚至是中文,也能涵盖在内。

    然后我们继续想想看,去向信息这里,会有几个字呢?

    不好说。

    例子里面这简单的三句话,就有 “4 个字” 或者 “6 个字” 两种情况。

    所以,我们无法指定去向信息里面字符的长度。

    但这也没关系,我们只需要用一个星号(*),就可以代表出现次数,从 0 到无穷大都可以匹配。

    当然,实际情况中,是不会真出现无穷大的。

    我们在刚才输入的基础上,加上.*,结果就成了这个样子:

    不错嘛!

    不过似乎去向信息和 “了” 字儿都是一样颜色的高亮。那不就混到了一起吗?

    我们可不想这样。

    怎么办?

    请你在.* 的两侧,尝试加入一对小括号(注意,不要用中文全角符号)试试看。

    你会发现,这次 “了” 依然用蓝色表示,而后面的去向信息,已经变成了绿色。

    这一对小括号,很重要,它叫做 “分组”,是提取信息的基本单位。

    我们的任务已经解决了一半了,是吧?

    下面我们来尝试把人名一并抽取出来。

    我们来找人名的锚定位置。

    细细观察,你不难发现,每个人名的后面,都有个动词跟着。

    升学的同学,用的是 “考” 字,而就业的同学,用的是 “进” 字。

    我们先尝试一下 “考” 字。

    这里我们尝试直接把 “考” 字放在 “了” 字以前。但是你会发现,什么匹配结果也没有。

    为什么?

    回看数据,你会发现,人家用的原词是 “考上了”。

    当然这里我们可以输入 “上” 字。不过你要考虑一下更为通用的情况。

    好比说,“考取了” 怎么办?“考入了” 呢?

    更好的方式,是继续使用我们刚才学会的“大招”,在“考”和“了”之间,插入一个.*

    这时候,你的正则表达式的样子是 考.*了(.*)

    看,第一行的信息成功匹配了吧?

    但是,那后面还有两行没有匹配,怎么办?

    我们依样画葫芦,就会发现,使用进.*了(.*) 就能正确匹配后两行。

    问题来了:

    匹配第一行的,匹配不了后两行,反之亦然。

    这不好。我们希望写的表达式,能够更通用

    怎么办?

    我们看看正则表达式当中 “或” 关系的表示。

    这里,我们可以把两个字符用竖线隔开,旁边用中括号括起来,代表两者任一出现,都算匹配成功。

    也就是,把正则表达式,写成这样:[考|进].*了(.*)

    太棒了,三行的内容都已经匹配成功。

    这里,动词词组,和代表时态的 “了” 作为中间锚定信息,我们可以放心大胆,把之前的人名信息,提取出来了。

    也就是这样写:(.*)[考|进].*了(.*)

    注意此时,人名分组是绿色,去向分组是红色的。

    我们成功提取了两组信息!庆祝一下!

    可是,如果你给班主任看这里的结果,估计他不会满意。

    表格,我要表格!

    别着急,该 Python 出场了。

    下面我们尝试在 Python 把数据正式提取出来。

    环境

    本文的配套源代码,我放在了 Github 上。

    你可以在我的公众号 “玉树芝兰”(nkwangshuyi)后台回复 “regex”,查看完整的代码链接。

    如果你对我的教程满意,欢迎在页面右上方的 Star 上点击一下,帮我加一颗星。谢谢!

    注意这个页面的中央,有个按钮,写着 “在 Colab 打开”(Open in Colab)。请你点击它。

    然后,Google Colab 就会自动开启。

    我建议你点一下上图中红色圈出的 “COPY TO DRIVE” 按钮。这样就可以先把它在你自己的 Google Drive 中存好,以便使用和回顾。

    Colab 为你提供了全套的运行环境。你只需要依次执行代码,就可以复现本教程的运行结果了。

    如果你对 Google Colab 不熟悉,没关系。我这里有一篇教程,专门讲解 Google Colab 的特点与使用方式。

    为了你能够更为深入地学习与了解代码,我建议你在 Google Colab 中开启一个全新的 Notebook ,并且根据下文,依次输入代码并运行。在此过程中,充分理解代码的含义。

    这种看似笨拙的方式,其实是学习的有效路径。

    代码

    首先,读入 Python 正则表达式包。

    import re
    

    然后,我们把数据准备好。注意为了演示代码的通用性,我这里在最后加了一行文字,区别于之前的文字规律,看看我们的代码能否正确处理它。

    data = """张华考上了北京大学
    李萍进了中等技术学校
    韩梅梅进了百货公司
    他们都有光明的前途"""
    

    然后,该写正则表达式了。你真的需要自己手动来写吗?

    当然不必。

    强大的 regex101 网站,已经帮助我们准备好了。

    请你点击上图中红色圈出的按钮,网站会为你准备好一个初始代码的模板,可以匹配你需要的模式。

    你不需要完全照搬代码。其中有这样一句,是很重要的,拷贝过来,贴到 Colab Notebook 就好。

    regex = r"(.*)[考|进].*了(.*)"
    

    以上就是你的正则表达式,在 Python 里面应有的样子。

    我们准备一个空列表,用来接收数据。

    mylist = []
    

    接着,写一个循环。

    for line in data.split('\n'):
      mysearch = re.search(regex, line)
      if mysearch:
        name = mysearch.group(1)
        dest = mysearch.group(2)
        mylist.append((name, dest))
    
    

    我给你解释一下这个循环里面,各条语句的含义:

    • data.split('\n') 把文本数据按行来拆分开。这样我们就可以针对每一行,来获取数据。

    • mysearch = re.search(regex, line) 这一句尝试匹配模式到该行内容。

    • if mysearch 这个判断语句,是让程序分辨一下,该行是否有我们要找的模式。例如最后一行文字,里面并没有咱们前面分析的文字模式。遇到这样的行,直接跳过。

    • name = mysearch.group(1) 是说匹配的第一组内容,也就是 regex101 网站里绿色代表的人名分组存到 name 变量里。下一句依次类推。注意 group 对应你正则表达式里面小括号出现的顺序,从 1 开始计数。

    • mylist.append((name, dest)) 把该行抽取到的信息,存入到咱们之前定义的空列表里面。

    注意,如果不加 mysearch = re.search(regex, line) 这一句,程序会对每一行都尝试匹配并且抽取分组内容,那么结果就会报这样的错误:

    所以你看,用正则表达式抽取信息时,不能蛮干。

    此时,我们查看一下 mylist 这个列表里面的内容:

    mylist
    

    结果为:

    [('张华', '北京大学'), ('李萍', '中等技术学校'), ('韩梅梅', '百货公司')]
    

    不错,一个不多,一个不少,恰好是我们需要的。

    我们要把它导出成为表格。方法有很多,但是最简便顺手的,是用 Pandas 数据分析软件包。

    import pandas as pd
    

    只需要利用 pd.DataFrame 函数,我们就能把上面列表和元组(tuple)组成的一个二维结构,变成数据框。

    df = pd.DataFrame(mylist)
    df.columns = ['姓名', '去向']
    

    注意,这里我们还非常细心地修改了表头。

    看看你的劳动成果吧:

    df
    

    有了数据框,转换成为 Excel ,就是一行代码的事情了:

    df.to_excel("dest.xlsx", index=False)
    

    进入 Files 标签页,刷新并且查看一下当前目录下的内容:

    这个 dest.xlsx 就是输出的结果了。下载之后我们可以用 Excel 打开查看。

    任务完成!

    你可以把结果提交给班主任,看他满意的笑容了。

    小结

    这篇教程里面,咱们谈了如何利用文本字符规律,借助 Python 和正则表达式,来提取结构化信息。

    希望你已经掌握了以下本领:

    • 了解正则表达式的功用;

    • 用 regex101 网站尝试正则表达式匹配,并且生成初步的代码;

    • 用 Python 批量提取信息,并且根据需求导出结构化数据为指定格式。

    再次强调一下,对于这么简单的样例,使用上述方法,绝对是大炮轰蚊子。

    然而,如果你需要处理的数据是海量的,这个方法给你节省下来的时间,会非常可观。

    希望你能够举一反三,在自己的工作中灵活运用它。

    资源

    先别急着走。

    由于篇幅所限,教程中我只给你介绍了几项最简单正则语法。肯定会有很多你感兴趣的语法知识,没来得及一一讲给你听。

    如果你对于正则表达式很感兴趣,因为科研或者工作目的,需要马上学习,也请不要着急。

    我这里刚好有一份免费的优秀教程资源分享给你。

    我在 UNT 讲授 INFO 5731 课程的时候,就是用这份教程结合翻转教学,让从没听说过正则表达式的学生们,在一两周之内掌握并且熟练应用。相信对你来说,这份教程资源也会很有用。

    你可以在我的公众号 “玉树芝兰”(nkwangshuyi)后台回复 “regex”,查看教程链接。

    祝学习愉快!

    One more thing

    在后台经常有同学提问,那个 “知识星球” 是干什么用的?

    它是个社群工具。除了答疑解惑之外,我经常会在里面,分享一些最新的资源给成员们。

    例如说,最近我找到了一份非常好的 Transformer 模型原理与实践学习资源,就第一时间写了篇短文,分享到了知识星球。

    知识星球里,文章写作的特点是沟通便捷。

    我不需要像写公众号一样,字斟句酌来写文章,且形成项目级别输出。所以沟通的时效性会非常强。

    感觉有用的话,请点 “在看”,并且把它 转发给你身边有需要的朋友。

    赞赏就是力量。

    由于微信公众号外部链接的限制,文中的部分链接可能无法正确打开。如有需要,请点击文末的 “阅读原文” 按钮,访问可以正常显示外链的版本。

    订阅我的微信公众号“玉树芝兰”,第一时间免费收到文章更新。别忘了加星标,以免错过新推送提示。

    如果你对 Python 与数据科学感兴趣,希望能与其他热爱学习的小伙伴一起讨论切磋,答疑解惑,欢迎加入知识星球。

    如果本文可能对你身边的亲友有帮助,也欢迎你把本文通过微博或朋友圈分享给他们。让他们一起参与到我们的讨论中来。

    延伸阅读

    你可能也会对以下话题感兴趣。点击链接就可以查看。

    题图: Photo by Tim St. Martin on Unsplash

    展开全文
  • 第七章-从文本提取信息

    千次阅读 2019-05-15 18:56:35
    然而,自然语言的复杂性使得获取文本中的信息非常困难。NLP的技术水平离从无限制的文本构建通用意义表示还有很长的路要走。如果我们把精力集中在有限的一系列问题或“实体关系”上,比如“不同的设施在哪里”或...

    对于任何给定的问题,都可能有人在某处写下了答案。以电子形式提供的自然语言文本数量确实惊人,而且每天都在增加。然而,自然语言的复杂性使得获取文本中的信息非常困难。NLP的技术水平离从无限制的文本构建通用意义表示还有很长的路要走。如果我们把精力集中在有限的一系列问题或“实体关系”上,比如“不同的设施在哪里”或“哪家公司雇用了谁”,我们就能取得重大进展。本章的目标是回答以下问题:
    1.如何构建一个从非结构化文本中提取结构化数据(如表)的系统?
    2.有哪些健壮的方法可以识别文本中描述的实体和关系?
    3.哪些语料库适合这项工作,我们如何使用它们来训练和评估我们的模型?
    在此过程中,我们将应用前两章中的技术来解决分块和命名实体识别的问题。

    7.1 信息提取

    信息有多种形状和大小。一种重要的形式是结构化数据,其中存在规则且可预测的实体和关系组织。
    例如,我们可能对公司和地点之间的关系感兴趣。 鉴于某家公司,我们希望能够确定其开展业务的地点; 相反,在给定位置的情况下,我们希望了解哪些公司在该位置开展业务。 如果我们的数据是表格形式,例如1.1中的示例,那么回答这些查询很简单。
    Table1.1 locations data

    如果这个位置数据作为元组列表(实体,关系,实体)存储在Python中,则问题是“哪些组织在亚特兰大运行?” 可翻译如下:

    locs=[('Omnicom', 'IN', 'New York'),
          ('DDB Needham', 'IN', 'New York'),
          ('Kaplan Thaler Group', 'IN', 'New York'),
          ('BBDO South', 'IN', 'Atlanta'),
          ('Georgia-Pacific', 'IN', 'Atlanta')]
    query = [e1 for (e1, rel, e2) in locs if e2=='Atlanta']
    

    Table1.2 result

    如果我们试图从文本中获取类似的信息,事情就会变得更加棘手。 例如,请考虑以下代码段(corpus.ieer里的某个文件)

    text (1)
    如果您通读(1),您将收集回答示例问题所需的信息。但是我们如何让机器充分了解(1)返回table1.2中的答案?这显然是一项艰巨的任务。与table1.1不同,(1)不包含将组织名称与位置名称链接的结构。
    解决这个问题的一种方法是建立一个非常普遍的意义表示(第十章)
    在本章中,我们采用不同的方法,事先决定我们只会在文本中查找非常具体的信息,例如组织和位置之间的关系。
    我们首先将自然语言句子的非结构化数据转换为1.1的结构化数据,而不是尝试使用像(1)这样的文本来直接回答问题。
    然后我们获得了强大的查询工具(如SQL)的好处。
    这种从文本中获取意义的方法称为信息提取。
    信息提取有许多应用,包括商业智能,简历收集,媒体分析,情感检测,专利检索和电子邮件扫描。
    当前研究中一个特别重要的领域涉及尝试从电子可用的科学文献中提取结构化数据,特别是在生物学和医学领域。

    7.1.1信息提取架构

    图1.1显示了简单信息提取系统的体系结构。
    它首先使用第三章和第五章中讨论的几个过程处理文档:首先,使用句子分割器将文档的原始文本分成句子,并且使用分词器将每个句子进一步细分为单词。
    接下来,每个句子都标有词性标签,这将在下一步命名实体检测中证明非常有用。
    在这一步中,我们在每个句子中搜索可能有趣的实体。
    最后,我们使用关系检测来搜索文本中不同实体之间的可能关系。

    Simple Pipeline Architecture for an Information Extraction System

    要执行前三个任务,我们可以定义一个简单的函数,简单地将NLTK的默认句子分割器,单词标记器和词性标注器连接在一起.
    接下来,在命名实体检测中,我们对可能参与彼此有趣关系的实体进行细分和标记。
    最后,在关系提取中,我们搜索文本中彼此接近的实体对之间的特定模式,并使用这些模式来构建记录实体之间关系的元组。

    7.2分块

    我们将用于实体检测的基本技术是分块,分段和标记多标记序列,如2.1所示。
    较小的框显示字级标记化和词性标记,而大框显示更高级别的分块。
    这些较大的盒子中的每一个都称为块。 与省略空格的标记化一样,分块通常选择标记的子集。与标记化一样,由chunker生成的片段在源文本中不重叠。

    Figure 2.1: Segmentation and Labeling at both the Token and Chunk Levels

    7.2.1 名词短语分块

    我们将首先考虑名词短语分块或NP分块的任务,其中我们搜索对应于单个名词短语的块。例如,这里有一些华尔街日报文本,NP块使用括号标记:

    text (2)
    NP分块最有用的信息来源之一是词性标签。这是在我们的信息提取系统中执行词性标注的动机之一。

    sentence = [("the", "DT"), ("little", "JJ"), ("yellow", "JJ"),
                ("dog", "NN"), ("barked", "VBD"), ("at", "IN"),  ("the", "DT"), ("cat", "NN")]
    grammar = "NP: {<DT>?<JJ>*<NN>}"
    cp = nltk.RegexpParser(grammar)
    result = cp.parse(sentence)
    result.draw()
    

    result_draw

    7.2.2 标签模式

    构成块语法的规则使用标记模式来描述带标记的单词序列。标记模式是使用尖括号分隔的词性标记序列,例如

    ?*。标记模式类似于正则表达式模式(3.4)。现在,考虑一下《华尔街日报》上的下列名词短语:
    WallStreetJournal

    我们可以使用上面第一个标记图案的略微细化来匹配这些名词短语,即

    ?<JJ. ><NN.*>+。
    JJ.*可以匹配形容词,以及其比较级最高级,NN.*可以匹配常用名词单复数,专有名词单复数
    但是,很容易找到许多更复杂的例子,这条匹配规则就不适用了,如下:
    complicated_text

    7.2.3 用正则表达式分块

    2.3展示了一个由两条规则组成的简单块语法。第一条规则匹配一个可选的限定词或所有格代词,零或多个形容词,然后是名词。第二条规则匹配一个或多个专有名词。我们还定义了一个被分块[1]的示例语句,并在这个输入[2]上运行分块程序。

    grammar = r"""
      NP: {<DT|PP\$>?<JJ>*<NN>}  
          {<NNP>+}                 
    """
    cp = nltk.RegexpParser(grammar)
    sentence = [("Rapunzel", "NNP"), ("let", "VBD"), ("down", "RP"),
                     ("her", "PP$"), ("long", "JJ"), ("golden", "JJ"), ("hair", "NN")]
    result=cp.parse(sentence)
    print(result)
    result.draw()
    

    如果标记模式在重叠的位置匹配,则最左边的匹配优先。例如,如果我们将匹配两个连续名词的规则应用到包含三个连续名词的文本中,那么只有前两个名词将被分块:

    nouns = [("money", "NN"), ("market", "NN"), ("fund", "NN")]
    grammar = "NP: {<NN>+}  # Chunk two consecutive nouns"
    cp = nltk.RegexpParser(grammar)
    result=cp.parse(nouns)
    print(result)
    result.draw()
    

    7.2.4 探索文本语料库

    在2中,我们看到了如何查询标记语料库以提取与特定词性标签序列匹配的短语。我们可以使用chunker更轻松地完成相同的工作,如下所示

    cp = nltk.RegexpParser('CHUNK: {<V.*> <TO> <V.*>}')
    for sent in brown.tagged_sents():
      tree = cp.parse(sent)
      for subtree in tree.subtrees():
        if subtree.label() == 'CHUNK':
          print(subtree)
    

    7.2.5 分块

    Chinking是从块中删除一个令牌序列的过程。如果匹配的令牌序列跨越整个块,则删除整个块;如果令牌序列出现在块的中间,这些令牌将被删除,留下两个之前只有一个令牌的块。如果序列位于块的外围,则删除这些标记,并保留较小的块。这三种可能性在2.1中进行了说明。

    chinking rules applied to the same chunk

    grammar = r"""
      NP:
        {<.*>+}          # Chunk everything
        }<VBD|IN>+{      # Chink sequences of VBD and IN
      """
    sentence = [("the", "DT"), ("little", "JJ"), ("yellow", "JJ"),
           ("dog", "NN"), ("barked", "VBD"), ("at", "IN"),  ("the", "DT"), ("cat", "NN")]
    cp = nltk.RegexpParser(grammar)
    result=cp.parse(sentence) 
    result.draw()
    

    7.2.6 表示块:标签与树

    I (inside), O (outside), or B (begin).
    标记表示块结构

    Tag Representation of Chunk Structures

    IOB标记已经成为在文件中表示块结构的标准方法,我们也将使用这种格式。以下是2.5中的信息如何显示在文件中:

    IOB in files

    在这种表示中,每行有一个标记,每个标记都带有词性标记和块标记。这种格式允许我们表示多个块类型,只要这些块没有重叠。正如我们前面看到的,块结构也可以使用树来表示。这样做的好处是,每个块都是可以直接操作的组成部分。如2.6所示:
    树表示块结构

    Tree Representation of Chunk Structures

    注意:NLTK使用树来表示块的内部表示,但提供了将这些树读取和写入IOB格式的方法。
    开发和评价分块器

    7.3 开发和评价分块器

    现在您已经了解了分块的功能,但我们还没有解释如何评估分块器。像往常一样,这需要适当注释的语料库。我们首先看一下将IOB格式转换为NLTK树的机制,然后讨论如何使用分块语料库在更大规模上完成此操作。我们将看到如何评估chunker相对于语料库的准确性,然后查看一些更多数据驱动的方法来搜索NP块。我们始终关注的重点是扩大分组的覆盖范围。

    7.3.1阅读IOB格式和CoNLL 2000语料库

    转换函数chunk.conllstr2tree()从这些多行字符串之一构建树表示。
    此外,它允许我们选择要使用的三种块类型的任何子集,这里仅用于NP块:

    text = '''
    he PRP B-NP
    accepted VBD B-VP
    the DT B-NP
    position NN I-NP
    of IN B-PP
    vice NN B-NP
    chairman NN I-NP
    of IN B-PP
    Carlyle NNP B-NP
    Group NNP I-NP
    , , O
    a DT B-NP
    merchant NN I-NP
    banking NN I-NP
    concern NN I-NP
    . . O
    '''
    nltk.chunk.conllstr2tree(text, chunk_types=['NP']).draw()
    nltk.chunk.conllstr2tree(text).draw()
    

    String to Tree Representation

    String to Tree Representation

    我们可以使用NLTK语料库模块访问更多的分块文本。CoNLL 2000语料库包含270k字的华尔街日报文本,分为“训练”和“测试”部分,用IOB格式的词性标签和块标签注释。我们可以使用nltk.corpus.conll2000访问数据。下面是读取语料库的“训练”部分的第100个句子的例子:

    print(conll2000.chunked_sents('train.txt')[99])
    

    String to Tag Representation

    正如您所看到的,CoNLL 2000语料库包含三种块类型:名词语块,我们已经看过了; 动词语块,如“has already delivered”; 介词语块,例如“because of”。
    由于我们现在只对NP块感兴趣,我们可以使用chunk_types参数来选择它们:

    String to Tag Representation

    7.3.2简单的评价和基准

    现在,我们可以访问分块语料库,我们可以评估chunkers。我们首先为简单的块解析器cp(chunkparser)建立一个不创建分块的基线:
    FirstResult_no training no grammar
    IOB标签准确度表示超过三分之一的单词被标记为O,即不在NP块中。但是,由于我们的标记器没有找到任何块,因此其精度,召回率和f度量均为零。现在让我们尝试一个正则表达式chunker,它寻找以名词短语标签(例如CD,DT和JJ)为特征的字母开头的标签。(初级的正则表达式分块器)
    SecondResult_no training with grammar
    在3.1中,我们定义了UnigramChunker类,它使用unigram标记符来标记带有块标记的句子。此类中的大多数代码仅用于在NLTK的ChunkParserI接口使用的块树表示和嵌入式标记器使用的IOB表示之间来回转换。该类定义了两个方法:在构建新的UnigramChunker时调用的构造函数; 以及用于分块新句子的解析方法

    # 使用训练语料找到对每个词性标记最有可能的块标记(I、O或B)
    # 可以用unigram标注器建立一个分块器,但不是要确定每个词的正确词性标记,
    # 而是给定每个词的词性标记,尝试确定正确的块标记
    class UnigramChunker(nltk.ChunkParserI):
      def __init__(self, train_sents):
        train_data = [[(t, c) for w, t, c in nltk.chunk.tree2conlltags(sent)]
                      for sent in train_sents]
        self.tagger = nltk.UnigramTagger(train_data)
      def parse(self, sentence):
        pos_tags = [pos for (word, pos) in sentence]
        tagged_pos_tags = self.tagger.tag(pos_tags)
        chunktags = [chunktag for (pos, chunktag) in tagged_pos_tags]
        # 为词性标注IOB块标记
        conlltags = [(word, pos, chunktag) for ((word, pos), chunktag)
                     in zip(sentence, chunktags)]
        # 转换成分块树状图
        return nltk.chunk.conlltags2tree(conlltags)
    

    构造函数需要一个训练句列表,这些句子将以块树的形式出现。它首先将训练数据转换为适合训练标记器的形式,使用tree2conlltags将每个块树映射到word,tag,chunk三元组列表。然后,它使用转换后的训练数据来训练unigram标记器,并将其存储在self.tagger中供以后使用。

    解析方法将标记的句子作为其输入,并从该句子中提取词性标签开始。然后,它使用在构造函数中训练的tagger self.tagger,使用IOB块标记标记词性标记。接下来,它提取块标签,并将它们与原始句子组合,以产生conlltags。最后,它使用conlltags2tree将结果转换回块树。

    现在我们有了UnigramChunker,我们可以使用CoNLL 2000语料库对其进行训练,并测试其最终性能:
    ThirdResult_with training with grammar
    这个组合相当不错,整体f-measure得分为83%。
    让我们看一下它的学习内容,使用它的unigram标记器为语料库中出现的每个词性标签分配一个标签:
    postag
    它发现大多数标点符号都出现在NP块之外,除了#和 , 它 们 都 被 用 作 货 币 标 记 。 研 究 还 发 现 , 限 定 词 ( D T ) 和 所 有 词 ( P R P ,它们都被用作货币标记。 研究还发现,限定词(DT)和所有词(PRP (DT)(PRP和WP$)出现在NP块的开头,而名词类型(NN、NNP、NNPS、NNS)大多出现在NP块内部
    构建了一个unigram chunker后,很容易构建一个bigram chunker:我们只需将类名更改为BigramChunker,并修改3.1中的行来构造一个BigramTagger而不是一个UnigramTagger。
    由此产生的chunker性能略高于unigram chunker:
    class BigramChunker
    基于正则表达式的chunkers和n-gram chunkers都决定了基于词性标签完全创建的块。
    但是,有时词性标签不足以确定句子应该如何分块。 例如,请考虑以下两个陈述

    class statements
    基于分类器的NP chunker的基本代码如3.2所示。它由两个类组成:
    第一个类几乎与1.5中的ConsecutivePosTagger类相同。唯一的两个区别是它调用了一个不同的特征提取器,它使用的是MaxentClassifier而不是NaiveBayesClassifier。
    第二个类基本上是一个围绕tagger类的包装器,它将它变成一个chunker。
    在训练期间,该第二类将训练语料库中的块树映射到标签序列中;在parse()方法中,它将标记器提供的标记序列转换回块树
    Figure 3.2: Noun Phrase Chunking with a Consecutive Classifier

    ConsecutivePosTagger
    剩下要填写的唯一部分是特征提取器。我们首先定义一个简单的特征提取器,它只提供当前令牌的词性标记。使用这个特征提取器,我们的基于分类器的chunker与unigram chunker非常相似,正如其性能所反映的那样。

    展开全文
  • 介绍了一些传统但是被验证是非常有用的,现在都还在用的策略,用来对非结构化文本数据提取特征。 介绍 在本文中,我们将研究如何处理文本数据,这无疑是最丰富的非结构化数据来源之一。文本数据通常由文档组成,...

    640?wx_fmt=jpeg


    作者 | Dipanjan (DJ) Sarkar

    编译 | ronghuaiyang

    来源 | AI公园(ID:AI_Paradise)

    【导读】本文介绍了一些传统但是被验证是非常有用的,现在都还在用的策略,用来对非结构化的文本数据提取特征。

    介绍


    在本文中,我们将研究如何处理文本数据,这无疑是最丰富的非结构化数据来源之一。文本数据通常由文档组成,文档可以表示单词、句子甚至是文本的段落。文本数据固有的非结构化(没有格式整齐的数据列)和嘈杂的特性使得机器学习方法更难直接处理原始文本数据。因此,在本文中,我们将采用动手实践的方法,探索从文本数据中提取有意义的特征的一些最流行和有效的策略。这些特征可以很容易地用于构建机器学习或深度学习模型。


    动机


    特征工程通常被称为创建性能更好的机器学习模型的秘密武器。只要有一个出色的特征就可能是你赢得Kaggle挑战的门票!特征工程的重要性对于非结构化的文本数据更为重要,因为我们需要将自由流动的文本转换成一些数字表示形式,然后机器学习算法就可以理解这些数字表示形式。即使出现了自动化的特征工程,在将不同的特征工程策略应用为黑盒模型之前,你仍然需要理解它们背后的核心概念。永远记住,“如果给你一盒工具来修理房子,你应该知道什么时候使用电钻,什么时候使用锤子!”


    理解文本数据


    我相信你们所有人都对这个场景中包含的文本数据有一个合理的概念。请记住,文本数据总是可以以结构化数据属性的形式存在,但通常这属于结构化分类数据的范畴。

    在这个场景中,我们讨论的是单词、短语、句子和整个文档形式的自由流动文本。本质上,我们有一些句法结构,比如单词组成短语,短语组成句子,句子又组成段落。然而,文本文档没有固有的结构,因为可以有各种各样的单词,这些单词在不同的文档中会有所不同,而且与结构化数据集中固定数量的数据维度相比,每个句子的长度也是可变的。


    特征工程策略


    让我们看看一些流行的和有效的策略来处理文本数据,并从中提取有意义的特征,这些特征可以用于下游的机器学习系统。请注意,你可以在https://github.com/dipanjanS/practical-machine-learning-with-python中访问本文中使用的所有代码,以供将来参考。我们将从加载一些基本的依赖项和设置开始。
     import pandas as pd	
     import numpy as np	
     import re	
     import nltk	
     import matplotlib.pyplot as plt	
     pd.options.display.max_colwidth = 200	
     %matplotlib inline
    现在,让我们以一个示例文档语料库为例,我们将在该语料库上运行本文中的大部分分析。corpus是具有一个或多个主题的文本文档集合。
     corpus = ['The sky is blue and beautiful.',	
               'Love this blue and beautiful sky!',	
               'The quick brown fox jumps over the lazy dog.',	
               "A king's breakfast has sausages, ham, bacon, eggs, toast and beans",	
               'I love green eggs, ham, sausages and bacon!',	
               'The brown fox is quick and the blue dog is lazy!',	
               'The sky is very blue and the sky is very beautiful today',	
               'The dog is lazy but the brown fox is quick!'    	
     ]	
     labels = ['weather', 'weather', 'animals', 'food', 'food', 'animals', 'weather', 'animals']	
     	
     corpus = np.array(corpus)	
     corpus_df = pd.DataFrame({'Document': corpus,	
                               'Category': labels})	
     corpus_df = corpus_df[['Document', 'Category']]
    640?wx_fmt=png

    可以看到,我们已经为我们的toy语料库获取了一些属于不同类别的文本文档示例。像往常一样,在讨论特征工程之前,我们需要进行一些数据预处理或整理,以删除不必要的字符、符号和tokens。


    文本预处理

    可以有多种方法来清理和预处理文本数据。在接下来的几点中,我们将重点介绍在自然语言处理(NLP)中大量使用的一些最重要的方法。

    • 删除标签: 我们的文本经常包含不必要的内容,如HTML标签,分析文本的时候这不会增加多少价值。BeautifulSoup库可以帮我们做很多必须的工作。
    • 删除重音字符: 在任何文本语料库中,特别是在处理英语时,通常可能要处理重音字符/字母。因此,我们需要确保将这些字符转换并标准化为ASCII字符。一个简单的例子是将é转换为e。
    • 扩展缩略语: 在英语中,缩略语基本上是单词或音节的缩写形式。这些现有单词或短语的缩略形式是通过删除特定的字母和声音来创建的。例如,do not变为don 't以及I would 变为I 'd 。将每个缩略语转换为其扩展的原始形式通常有助于文本标准化。
    • 删除特殊字符 :非字母数字字符的特殊字符和符号通常会增加非结构化文本中的额外噪音。通常,可以使用简单正则表达式(regexes)来实现这一点。
    • 词根提取和词形还原 :词干通常是可能的单词的基本形式,可以通过在词干上附加词缀,如前缀和后缀来创建新单词。这就是所谓的拐点。获取单词基本形式的反向过程称为“词根提取”。一个简单的例子是单词WATCHES, WATCHING,和WATCHED。它们以词根WATCH作为基本形式。词形还原与词根提取非常相似,在词根提取中,我们去掉词缀以得到单词的基本形式。然而,在这种情况下,基本形式被称为根词,而不是词根。不同之处在于,词根总是一个词典上正确的单词(存在于字典中),但根词的词干可能不是这样。
    • 删除停止词: 在从文本中构造有意义的特征时,意义不大或者没有意义的词被称为停止词或停止词。如果你在语料库中做一个简单的词或词的频率,这些词的频率通常是最高的。像a、an、the、and等词被认为是停止词。没有一个通用的停止词列表,但是我们使用了一个来自“nltk”的标准英语停止词列表。你还可以根据需要添加自己的域特定的停止词。

    除此之外,你还可以执行其他标准操作,如标记化、删除额外的空格、文本小写转换和更高级的操作,如拼写纠正、语法错误纠正、删除重复字符等等。
    由于本文的重点是特征工程,所以我们将构建一个简单的文本预处理程序,该程序的重点是删除特殊字符、额外的空格、数字、停止词和把文本语料库的大写变成小写。
    wpt = nltk.WordPunctTokenizer()	
     stop_words = nltk.corpus.stopwords.words('english')	
     	
     def normalize_document(doc):	
         # lower case and remove special characters\whitespaces	
         doc = re.sub(r'[^a-zA-Z\s]', '', doc, re.I|re.A)	
         doc = doc.lower()	
         doc = doc.strip()	
         # tokenize document	
         tokens = wpt.tokenize(doc)	
         # filter stopwords out of document	
         filtered_tokens = [token for token in tokens if token not in stop_words]	
         # re-create document from filtered tokens	
         doc = ' '.join(filtered_tokens)	
         return doc
    一旦我们准备好了基本的预处理pipeline,我们可以将其应用于示例语料库。
    norm_corpus = normalize_corpus(corpus)	
     norm_corpus	
     Output	
     ------	
     array(['sky blue beautiful', 'love blue beautiful sky',	
            'quick brown fox jumps lazy dog',	
            'kings breakfast sausages ham bacon eggs toast beans',	
            'love green eggs ham sausages bacon',	
            'brown fox quick blue dog lazy',	
            'sky blue sky beautiful today',	
            'dog lazy brown fox quick'],	
           dtype='<U51')
    上面的输出应该可以让你清楚地看到我们的每个示例文档在预处理之后的样子。现在让我们来设计一些特征!


    词袋模型


    这可能是非结构化文本最简单的向量空间表示模型。向量空间模型只是一个数学模型,它将非结构化文本(或任何其他数据)表示为数值向量,这样向量的每个维度都是一个特定的特性\属性。单词包模型将每个文本文档表示为一个数字向量,其中每个维度都是来自语料库的特定单词,其值可以是其在文档中的频率、出现频率(用1或0表示),甚至是加权值。模型的名称是这样的,因为每个文档都按照字面意思表示为自己单词的“包”,不考虑单词顺序、序列和语法。
    from sklearn.feature_extraction.text import CountVectorizer	
     	
     cv = CountVectorizer(min_df=0., max_df=1.)	
     cv_matrix = cv.fit_transform(norm_corpus)	
     cv_matrix = cv_matrix.toarray()	
     cv_matrix
     Output	
     ------	
     array([[0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0],	
            [0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0],	
            [0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0],	
            [1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0],	
            [1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0],	
            [0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0],	
            [0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 1],	
            [0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0]	
          ], dtype=int64)
    因此,可以看到我们的文档已经被转换成数字向量,这样每个文档都由上面的特征矩阵中的一个向量(行)表示。下面的代码将帮助以更容易理解的格式表示这一点。
    # get all unique words in the corpus	
     vocab = cv.get_feature_names()	
     # show document feature vectors	
     pd.DataFrame(cv_matrix, columns=vocab)
    640?wx_fmt=png

    可以清楚地看到,特征向量中的每一列表示语料库中的一个单词,每一行表示我们的一个文档。任何单元格中的值表示该单词(用列表示)在特定文档中出现的次数(用行表示)。因此,如果一个文档语料库由所有文档中的N唯一单词组成,那么每个文档都有一个N维向量。


    N-Grams袋模型

    一个单词只是一个符号,通常被称为unigram或1-gram。我们已经知道词袋模型不考虑单词的顺序。但是,如果我们也想考虑按顺序出现的短语或单词的集合呢?N-gram帮助我们达到这个目的。N-gram基本上是文本文档中单词tokens的集合,这些标记是连续的,并以序列的形式出现。Bi-gram表示2阶n-grams(2个单词),Tri-grams表示3阶n-grams(3个单词),依此类推。因此,N-Grams袋模型只是词袋模型的一个扩展,因此我们也可以利用基于N-gram的特征。下面的示例描述了每个文档特征向量中基于bi-gram的特征。
    # you can set the n-gram range to 1,2 to get unigrams as well as bigrams	
     bv = CountVectorizer(ngram_range=(2,2))	
     bv_matrix = bv.fit_transform(norm_corpus)	
     	
     bv_matrix = bv_matrix.toarray()	
     vocab = bv.get_feature_names()	
     pd.DataFrame(bv_matrix, columns=vocab)
    640?wx_fmt=png

    这为我们的文档提供了特征向量,其中每个特征由表示两个单词序列的bi-gram组成,值表示该bi-gram出现在文档中的次数。


    TF-IDF模型


    在大型语料库中使用词袋模型可能会产生一些潜在的问题。由于特征向量是基于绝对频率,可能有一些项在所有文档中都经常出现,这可能倾向于掩盖其他方面的特征。TF-IDF模型试图解决这一问题,在计算中使用了缩放或归一化因子。TF-IDF是Term Frequency- reverse Document Frequency的缩写。

    其计算方法为:词频(tf)和逆文档频率(idf)。该技术是为搜索引擎中查询结果的排序而发展起来的,目前已成为信息检索和自然语言处理领域中一个不可或缺的模型。

    在数学上,我们可以将TF-IDF定义为tfidf = tf x idf,可以进一步展开为:

    640?wx_fmt=png

    这里,tfidf(w, D)是文档D中单词w的TF-IDF得分。tf(w, D)表示文档D中w的词频,可以从词袋模型中得到。idf (w, D)是w这个单词的逆文档频率,可以通过计算语料库中的文档的总数C除以w这个词的文档频率的对数变换得到, 这基本上是文档的语料库词w的频率。这个模型有多种变体,但最终都得到了非常相似的结果。现在让我们把它应用到我们的语料库上!

    640?wx_fmt=png

    每个文本文档的基于TF-IDF的特征向量与原始的词袋模型值相比具有了缩放和标准化的值。


    文档相似度

    文档相似度是使用基于距离或相似度的度量的过程,该度量可用于根据从文档中提取的特征(如词袋或tf-idf)确定文本文档与任何其他文档的相似程度。

    因此,可以看到,我们可以构建在上一节中设计的基于tf-idf的特征的基础上,并使用它们来生成新的特征,通过利用基于这些特征的相似性,可以在搜索引擎、文档集群和信息检索等领域中发挥作用。

    语料库中的成对文档相似性涉及到为语料库中的每对文档计算文档相似性。因此,如果在一个语料库中有C文档,那么最终将得到一个C x C矩阵,其中每一行和每一列表示一对文档的相似度得分,这对文档分别表示行和列的索引。有几个相似度和距离度量用于计算文档相似度。其中包括余弦距离/相似度、欧几里德距离、曼哈顿距离、BM25相似度、jaccard距离等。在我们的分析中,我们将使用可能是最流行和广泛使用的相似性度量, 余弦相似度和基于TF-IDF特征向量的成对文档相似度比较。

    from sklearn.metrics.pairwise import cosine_similarity	
    
    	
    similarity_matrix = cosine_similarity(tv_matrix)	
    similarity_df = pd.DataFrame(similarity_matrix)	
    similarity_df
    640?wx_fmt=png

    余弦相似度给出了一个度量,表示两个文本文档的特征向量表示之间夹角的余弦值。文档之间的夹角越小,它们之间的距离就越近,也就越相似,如下图所示。

    640?wx_fmt=png

    仔细观察相似矩阵可以清楚地看出,文档(0,1和6)、(2,5和7)彼此非常相似,文档3和文档4彼此略有相似,但幅度不是很大,但仍然比其他文档强。这必须表明这些类似的文档具有一些类似的特性。这是一个完美的分组或聚类的例子,可以通过无监督学习来解决,尤其是在处理数百万文本文档的大型语料库时。


    使用相似特征对文档进行聚类


    聚类利用无监督学习将数据点(本场景中的文档)分组或聚集。在这里,我们将利用一种无监督的分层聚类算法,通过利用前面生成的文档特征相似性,尝试将我们的玩具语料库中的类似文档分组在一起。层次聚类算法有两种,即聚合算法和分裂算法。我们将使用一个聚合聚类算法,这是分层聚类使用自底向上的方法,即从自己的簇中开始,然后使用一个度量数据点之间距离的距离度量和一个链接合并准则将簇依次合并在一起。下图显示了一个示例描述。

    640?wx_fmt=png

    链接准则的选择控制了合并的策略。链接准则的例子有Ward、Complete、Average等。该准则对于选择每一步合并的簇对(最低级的单个文档和较高级的簇)非常有用,它基于目标函数的最优值。我们选择Ward 's minimum variance method作为我们的链接准则来最小化总簇内方差。因此,在每个步骤中,我们都找到了合并后总簇内方差增加最小的一对簇。既然我们已经有了特征相似性,让我们在示例文档上构建链接矩阵。
    
     

    如果仔细查看链接矩阵,可以看到链接矩阵的每一步(行)都告诉我们哪些数据点(或簇)合并在一起。如果有n数据点,链接矩阵Z的形状将是(n - 1) x 4,其中Z[i]将告诉我们在步骤i合并了哪些集群。每一行有四个元素,前两个元素要么是数据点标识符,要么是簇标签(在矩阵的后半部分中有一次合并了多个数据点),第三个元素是前两个元素(数据点或集群)之间的簇距离,最后一个元素是合并完成后簇中元素\数据点的总数。

    现在让我们把这个矩阵形象化为一个树形图,以便更好地理解这些元素!
    plt.figure(figsize=(8, 3))	
    plt.title('Hierarchical Clustering Dendrogram')	
    plt.xlabel('Data point')	
    plt.ylabel('Distance')	
    dendrogram(Z)	
    plt.axhline(y=1.0, c='k', ls='--', lw=0.5)
    640?wx_fmt=png

    我们可以看到,每个数据点开始时是一个单独的簇,然后慢慢地开始与其他数据点合并,形成聚类。从颜色和树状图的高度来看,如果考虑距离度量在1.0或以上(用虚线表示),则可以看到模型正确地识别了三个主要聚类。利用这个距离,我们得到了聚类标签。
    from scipy.cluster.hierarchy import fcluster	
    max_dist = 1.0	
    
    	
    cluster_labels = fcluster(Z, max_dist, criterion='distance')	
    cluster_labels = pd.DataFrame(cluster_labels, columns=['ClusterLabel'])	
    pd.concat([corpus_df, cluster_labels], axis=1)
    640?wx_fmt=png

    可以清楚地看到,我们的算法根据分配给文档的聚类标签正确地标识了文档中的三个不同类别。这将使你对如何利用TF-IDF特征来构建相似特征有一个很好的了解,而相似特征反过来又有助于对文档进行聚类。


    总结


    这些示例应该让你对文本数据上的特征工程的流行策略有一个很好的了解。请记住,这些都是基于数学、信息检索和自然语言处理概念的传统策略。因此,随着时间的推移,这些经过尝试和测试的方法在各种数据集和问题中都证明是成功的。下一步将是利用文本数据上的特性工程的深度学习模型的详细策略!

    (*本文为AI科技大本营转载文章,转载联系作者)


    精彩推荐



    640?wx_fmt=png

    推荐阅读

    640?wx_fmt=png

    你点的每个“在看”,我都认真当成了喜欢“

    展开全文
  • 内容一般分为两部分,非结构化的数据 和 结构化的数据。 非结构化数据:先有数据,再有结构, 结构化数据:先有结构、再有数据 不同类型的数据,我们需要采用不同的方式来处理。 非结构化的数据处理 文本、电话号码...
  • 常常在想,自然语言处理到底在做的是一件什么样的事情?到目前为止,我所接触到的NLP其实都是在做一件事情,即将自然语言转化为一种计算机能够理解的形式。这一点在知识图谱、信息抽取、文本摘要这...
  • 常常在想,自然语言处理到底在做的是一件什么样的事情?到目前为止,我所接触到的NLP其实都是在做一件事情,即将自然语言转化为一种计算机能够理解的形式。这一点在知识图谱、信息抽取、文本摘要这...
  • 结构化数据与结构化的数据提取

    千次阅读 2019-03-13 10:19:40
    内容一般分为两部分,非结构化的数据 和 结构化的数据。 非结构化数据:先有数据,再有结构, 结构化数据:先有结构、再有数据 不同类型的数据,我们需要采用不同的方式来处理。 非结构化的数据处理 ...
  • 文字分析 非结构化文本中的情感,实体,关系和关键短语的存储库。 内容 情绪分析 关键短语提取 注意:项目中使用的数据(可在数据目录下访问)仅用于演示目的。
  • 常常在想,自然语言处理到底在做的是一件什么样的事情?到目前为止,我所接触到的NLP其实都是在做一件事情,即将自然语言转化为一种计算机能够理解的形式。这一点在知识图谱、信息抽取、文本摘要这...
  • 文本挖掘、数据挖掘、文本挖掘、数据挖掘、文本挖掘、数据挖掘
  • 为了充分利用领域特征进行Web文本结构化分析,文章提出了一种面向领域的Web文本结构化分析方法。该方法以领域特征为基础,依据半结构化文本的结构特征和Html文本的层次特性构造Html树;利用本体论的相关思想和方法...
  • 基于注意力的街景图像提取结构化信息一种用于真实图像文本提取问题的TensorFlow模型。该文件夹包含在FSNS数据集数据集上训练新的注意OCR模型所需的代码,以在法国转录街道名称。 您还可以使用它来根据自己的数据进行...
  • 百度AI结构化数据提取

    千次阅读 2017-11-28 09:44:39
    http://brain.baidu.com/tech/kg/pie 网络爬虫 https://www.cnblogs.com/qianzf/p/6796588.html
  • 基于结构化数据生成文本(data-to-text)的任务旨在生成人类可读的文本来直观地描述给定的结构化数据。然而,目前主流任务设定所基于的数据集有较好的对齐 (well-aligned)关系,即输入(i.e. 结构化数据)和输出...
  • 文本特征提取

    万次阅读 多人点赞 2019-03-05 20:36:57
    文本特征的通用信息文本分类问题当中的对象 词:在英文文本处理当中面对的是单个词组用空格隔开容易区分,在中文文本当中需要通过特定的词库如python中的jieba、中科院、清华、哈工大的一些分词工具来进行分词...
  • 对于word中的数据,我们可能存在将其抽取为结构化数据的需求。抽取思想为对整个word文档从上至下扫描,并对其中的文字和表格进行区分处理,可以记录文字和表格的顺序,并自由选择是否抽取出表格中的文字。
  • 混乱的数据包括混合在文本、表格和图片等中的非结构化数据而难以被软件处理。DeepDive帮助从非结构化数据中抽取数据并整合到已有的结构化数据库中。DeepDive被用来抽取数据实体中的复杂关系并且推断出他们之间的联系...
  • 目录 正则表达式re模块 案例:使用正则表达式的爬虫 XPath与lxml类库 案例:使用XPath的爬虫 ...内容一般分为两部分,非结构化的数据 和 结构化的数据。 非结构化数据:先有数据,再有结构,(http:...
  • 文中提到了一种方法:非结构化数据(通过提取有关的元数据)——>xml文档(根据约束条件)——>结构化数据 通过这样的方式来使得数据从非结构化结构化。 如何提取有关的元数据呢? 一般采用了文件模板...
  • 文本提取信息
  • 如题,例如从小票中提取卖出去的商品种类及其总数:   package demo; import java.util.HashMap; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; public class ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 117,729
精华内容 47,091
关键字:

文本提取结构化信息