【NLP】NLP中的消歧

机器学习初学者

共 28740字,需浏览 58分钟

 ·

2021-07-18 17:02


作者 | Nesrine Sfar

编译 | VK
来源 | Towards Data Science

如果你点开这篇文章,这意味着你有足够的好奇心去学习关于NLP/NLU中解决歧义的不同方法。

背景信息是机器产生歧义的原因。这种模棱两可的信息来源于人类在交流中使用的自然语言。将这种语言“翻译”时可能会产生歧义。这可以用人类语言本身固有的不正式和模棱两可来解释。

传统的方法是基于词向量化。这里显示的替代方法是基于知识图。

本教程将通过一个简单方便的应用程序,即自然语言API(nl api),重点介绍一些可以用来解决这个问题的方法。

机器无法解释或理解文本;要解决语言歧义,就需要对文本进行多层次的语言分析。处理歧义的现象称为“消歧”。这是帮助机器检测文本意义(语义)的过程。词义的确定要考虑语境、句法和词语之间的关系。


下面的文章将强调不同的方法,可以用来帮助机器减少歧义,如词形还原,词性标注等。

这项工作将基于一个名为expert.ai NL的API。


Expert.ai NL API


Expert.ai NL API是一个能够通过几行代码在文本中提供多层次信息的应用程序。

为了构建NLP模块,API提供了深入的语言处理技术。它可以进行深层语言分析,例如token化、词形还原、词性标注、形态/句法/语义分析。

除此之外,该库还可以解决命名实体识别、实体间的语义关系和情感分析等问题。

1.如何使用Expert.ai NL API for Python?

库的安装

首先,你需要使用以下命令安装客户端库:

pip install expertai-nlapi

一旦你在developer.expert.ai网站上创建了账号,API就可用了。Python客户端代码将开发人员帐户指定为环境变量:

  • Linux操作系统:

export EAI_USERNAME=YOUR_USER
export EAI_PASSWORD=YOUR_PASSWORD
  • windows:

SET EAI_USERNAME=YOUR_USER
SET EAI_PASSWORD=YOUR_PASSWORD

你的用户是你在注册期间指定的电子邮件地址。

你还可以在代码中定义:

import os
os.environ["EAI_USERNAME"] = 'YOUR_USER'
os.environ["EAI_PASSWORD"] = 'YOUR_PASSWORD'



2.深层语言分析


语言学把语言分析分成不同的部分。所有这些分支都是相互依存的,语言中的一切都是相互联系的。

对文档进行多级文本分析;每一个文本被分成句子,这些句子被解析成token、词形和词性,寻找句法成分和谓词之间的关系,并解释语法以构建完整的依赖树。

要检索这些信息,首先要导入库的client:

from expertai.nlapi.cloud.client import ExpertAiClient
client = ExpertAiClient()

让我们举一个例子来说明这些操作:

"Sophia is a social humanoid robot developed by Hong Kong-based company Hanson Robotics. Sophia was activated on February 14, 2016.

a.文本细分

这个操作允许将文本从最长的形式划分到最小的形式,在这种情况下,可以是段落、句子,短语,token。当token是一个搭配(复合词)时,文本细分可以在分析中得到更深入的分析,直到它不能被进一步细分的原子级。

导入库并实例化客户端后,应设置文本语言和API参数:

text = "Sophia is a social humanoid robot developed by Hong Kong-based company Hanson Robotics. Sophia was activated on February 14, 2016." 
language= 'en'

output = client.specific_resource_analysis(
    body={"document": {"text": text}}, 
    params={'language': language, 'resource''disambiguation'
})

在API请求中,应该在主体中提到要分析的句子,在参数中提到要分析的语言。资源参数与你需要对文本执行的操作有关,例如,消除歧义。

这种多层次的文本分析通常分为三个阶段:

1.词法分析:一个文本细分阶段,允许将文本分解为基本实体(token)。

2.句法分析:包括识别构成句法实体的词素组合(包括词性标注)。

3.语义分析:消歧是在这个层次上进行的,它根据交际语境和它们之间可能的关系来检测这些实体的意义。

词汇分析从第一部分开始:

# 我们使用元素output.paragraphs分成几个段落
# start和end是分析文本中段落的位置
for paragraph in output.paragraphs:
    print (f'Paragraphs:{text[paragraph.start:paragraph.end]:{20}}')
Paragraphs: Sophia is a social humanoid robot developed by Hong Kong-based company Hanson Robotics. Sophia was activated on February 142016.

因为我们的文本已经是一个段落(这里是两句话),所以细分的输出提供了与输入相同的文本。让我们尝试将段落分成句子级别,在本例中,我们只需要将元素.paragraphs修改为.sentences。最常见的分隔句子的方法是用点(.):

# 我们使用output.sentences把文本细分成句子
# start和end是所分析文本中句子的位置
for sentence in output.sentences:
    print (f'Sentences:{text[sentence.start:sentence.end]:{20}}')
Sentences: Sophia is a social humanoid robot developed by Hong Kong-based company Hanson Robotics.
Sentences: Sophia was activated on February 142016.

结果我们得到了两句话。让我们更深入地进行细分,以检索短语级别。我们使用与上面相同的方法,用.phrases,得到:

# 我们使用output.sentences把文本细分成短语
# start和end是所分析文本中短语的位置
for phrase in output.phrases:
    print (f'Phrases:{text[phrase.start:phrase.end]:{20}}')
Phrases: Sophia              
Phrases: is a social humanoid robot
Phrases: developed           
Phrases: by Hong Kong-based company Hanson Robotics
Phrases:.                   
Phrases: Sophia              
Phrases: was activated       
Phrases: on February 142016
Phrases:.

我们注意到,我们得到更多的元素。我们还可以得到文本中短语的数量:

# 数量
print("phrases array size: ", len(output.phrases))
phrases array size:  10

b.token化

此外,我们可以将短语级别分解为更小的单位,即token。这是“token化”任务,在NLP中非常常见。它帮助机器理解文本。要使用Python执行token化,我们可以使用.split函数,如下所示:

例如,考虑以下句子:

text = "CNBC has commented on the robot's  lifelike skin and her ability to emulate more than 60 facial expressions."

tokens = text.split()
print('These are the tokens of the sentence', tokens)
These are the tokens of the sentence ['CNBC''has''commented''on''the'"robot's"'lifelike''skin''and''her''ability''to''emulate''more''than''60''facial''expressions.']

如果不在split中指定分隔符,文本将按空格分隔。

通过expert.ai NL API,我们还可以执行token化,并提供其他特性。换句话说,API提供了不同的级别token分析;API产生的token可以是单词、字符(如缩写)甚至标点符号。

让我们看看如何使用API执行此任务,我们使用与上面相同的过程,使用.tokens修改element.phrases:

text = "CNBC has commented on the robot's  lifelike skin and her ability to emulate more than 60 facial expressions." 
language= 'en'

output = client.specific_resource_analysis(
    body={"document": {"text": text}}, 
    params={'language': language, 'resource''disambiguation'
})

# 打印到句子内的token
print (f'{"TOKEN":{20}} ')
print (f'{"----":{20}}'

# 我们使用元素output.tokens将文本细分为token
# start和end是所分析文本中token的位置
for token in output.tokens:
    print (f'{text[token.start:token.end]:{20}}')
TOKEN                
----                
CNBC                
has                 
commented           
on                  
the                 
robot               
's                  
lifelike            
skin                
and                 
her                 
ability             
to                  
emulate             
more                
than                
60                  
facial expressions  
.

我们注意到,这些token要么是像skin , ability, emulate这样的单词,要么是像‘s、60”这样的缩写,甚至是像点.这样的标点符号。

token化会导致搭配,这在split函数中是不可能的。该API能够根据单词和上下文的位置检测句子内的复合词。这种搭配还可以进一步划分为原子级,原子级是我们可以拥有的最后一个单位:

for token in output.tokens:
    print (f'{text[token.start:token.end]:{20}}')
    # 我们对token数组进行迭代,将复合词细分为原子级
    for atom in token.atoms:
        print (f'\t atom:{text[atom.start:atom.end]:{20}}')
CNBC                
has                 
commented           
on                  
the                 
robot               
's                  
lifelike            
skin                
and                 
her                 
ability             
to                  
emulate             
more                
than                
60                  
facial expressions  
     atom: facial              
     atom: expressions         
.

c.词性标注

token化导致了自然语言处理中的第二个过程,即词性标注(词性标注)。在这一阶段,我们引入句法分析,其中包括词性标注任务。后者包括为每个token分配一个POS或一个语法类。

POS描述了每个token的句法性质。这些标签可以反映文本的一部分含义。我们可以列出英语语言中常用的几种词性:限定词、名词、副词、动词、形容词、介词、连词、代词、感叹词……

一个词与其他词有相同的形式(同形异义词)可能有不同的含义(多义词)。这个词可以有不同的位置,即使它有相同的形式。语法类别取决于单词在文本中的位置及其上下文。让我们考虑这两句话:

The object of this exercise is to raise money for the charity.
A lot of people will object to the book.

从语言学的角度来看,在第一个句子中,object是名词,而在第二个句子中,object是动词。词性标注是消除歧义的关键步骤。根据这种标注,单词的含义可以从上下文、单词的形式(例如,专有名词开头的大写字母)、位置(SVO词序)等推断出来。

让我们尝试使用API为前两句生成词性标注:

from expertai.nlapi.cloud.client import ExpertAiClient
client = ExpertAiClient()

我们首先导入库并创建客户端,如下所示:

object_noun表示object是名词的句子,object_verb表示object是动词的句子:

object_noun = "The object of this exercise is to raise money for the charity."
object_verb = "A lot of people will object to the book."

单词object在两个句子中的形式相同,但位置不同。

首先,我们指定要进行词性标注的文本,对于第一个句子,它是object_noun,对于第二个句子,它是object_verb。如下所示:

# 第一句话
object_noun = "The object of this exercise is to raise money for the charity."
# 第二句
object_verb = "A lot of people will object to the book."
language = 'en'
# 请求第一句话的API
output = client.specific_resource_analysis(
    body={"document": {"text": object_noun}}, 
    params={'language': language, 'resource''disambiguation'
})

# 请求第二句话的API
output_2 = client.specific_resource_analysis(
    body={"document": {"text": object_verb}}, 
    params={'language': language, 'resource''disambiguation'
})

一旦我们设置了这些参数,就需要对token进行迭代,分别为每个示例分配一个POS;

# 第一句的POS
print (f'\t \033[1mOutput of the first sentence : \033[0m \n'
# 在输出数组的开始部分以粗体打印TOKEN和POS:
print (f'\033[1m{"TOKEN":{20}{"POS":{6}}\033[0m')

# 迭代token,并为第一个句子的每个token分配POS
for token in output.tokens:
    print (f'{object_noun[token.start:token.end]:{20}{token.pos:{6}}')

# 第二句的POS
print (f'\t \033[1mOutput of the second sentence : \033[0m \n'
# 在输出数组的开始部分以粗体打印TOKEN和POS:
print (f'\033[1m{"TOKEN":{20}{"POS":{6}}\033[0m')

# 迭代token,并为第一个句子的每个token分配POS
for token in output_2.tokens:
    print (f'{object_verb[token.start:token.end]:{20}{token.pos:{6}}')
       Output of the first sentence :  

TOKEN                POS   
The                  DET   
object               NOUN  
of                   ADP   
this                 DET   
exercise             NOUN  
is                   VERB  
to                   PART  
raise                VERB  
money                NOUN  
for                  ADP   
the                  DET   
charity              NOUN  
.                    PUNCT 
     Output of the second sentence :  

TOKEN                POS   
A lot of             ADJ   
people               NOUN  
will                 AUX   
object               VERB  
to                   ADP   
the                  DET   
book                 NOUN  
.                    PUNCT

一方面,宾语确实是一个名词,前面是冠词/限定词(DET)。另一方面,宾语实际上是句子的动词,它把主语“很多人”和宾语“书”联系起来。

NLP中使用的传统词性标注工具通常使用相同类型的信息来标注文本中的单词:其上下文和词形。expert.Ai-NL-API中词性标注的独特之处不仅在于为每个token识别一个语法标签,还在于介绍其含义。

换言之,一个词可以和其他词有相同的形式,但它包含多种含义(多义词)。每一个意义都在一个概念中表达,与其他概念联系起来,形成一个知识图。上面看到的单词object有不止一个含义,因此,它属于不同的语义概念,我们在expert.ai NL API的知识图中称之为“Syncons”。词性标注可以揭示同一个词的不同标签,从而产生不同的意义。

# object_noun的Concept_ID
print (f'\t \033[1mConcept_ID for object when NOUN \033[0m \n'

# 在输出数组的开始部分以粗体打印TOKEN、POS和ID:
print (f'\033[1m{"TOKEN":{20}{"POS":{15}{"ID":{6}}\033[0m')

# Syncon代表“概念”,我们用一个ID来表示
for token in output.tokens:
    print (f'{object_noun[token.start:token.end]:{20}{token.pos:{15}{token.syncon:{6}} ')

# object_verb的Concept_ID
print (f'\t \033[1mConcept_ID for object when VERB \033[0m \n'

# 在输出数组的开始部分以粗体打印TOKEN、POS和ID:
print (f'\033[1m{"TOKEN":{20}{"POS":{15}{"ID":{6}}\033[0m')

# Syncon代表“概念”,我们用一个ID来表示
for token in output_2.tokens:
    print (f'{object_verb[token.start:token.end]:{20}{token.pos:{15}{token.syncon:{6}} ')
        Concept_ID for object when NOUN  

TOKEN                POS             ID    
The                  DET                 -1 
object               NOUN             26946 
of                   ADP                 -1 
this                 DET                 -1 
exercise             NOUN             32738 
is                   VERB             64155 
to                   PART                -1 
raise                VERB             63426 
money                NOUN             54994 
for                  ADP                 -1 
the                  DET                 -1 
charity              NOUN              4217 
.                    PUNCT               -1 
     Concept_ID for object when VERB  

TOKEN                POS             ID    
A lot of             ADJ              83474 
people               NOUN             35459 
will                 AUX                 -1 
object               VERB             65789 
to                   ADP                 -1 
the                  DET                 -1 
book                 NOUN             13210 
.                    PUNCT               -1

可以注意到,名词对象属于ID为26946的概念。这个概念包括其他具有相同含义的词(同义词)。相比之下,它在第二句中的同形异义词与ID 65789有关。这些ID是知识图中每个概念的token。

因此,不同的词性导致不同的意思,即使我们有相同的词形。

请注意,以-1作为ID的单词,如ADP(Adposition指介词和后置词)、PUNCT(表示标点符号)、DET(表示限定词)等,在知识图中不可用,因为它们不是固有的语义。

d.词形还原

这是自然语言处理中的另一个核心任务,称为词形还原。它与token化和词性标注一样重要。

简而言之,词形还原将每个token还原为它的规范形式:

  • 动词的不定式:wore, worn -> wear / ran, running, runs -> run

  • 名词的单数形式:mice -> mouse / die -> dice

  • 等等。…

概念通常可以包含许多词形。在消歧过程中,文本中的每个token都返回到其基本形式。每个词形都与知识图中的一个概念相关联。因此,词形还原可以将不同的token集简化为不同的词形集。这可以通过这个例子来解释;

起初,“living”这个词的听者几乎是无意识地能辨别出这个词的意思。这对于人类来说是可能的,因为人类可以根据世界的知识等做出推论。如果没有上下文,这对于机器来说是不可能的。

为了使机器能够预测出一个单词在相同拼写和相同发音的情况下产生的几种意义,词形还原是处理这种词汇歧义的关键。

我们可以使用expert.ai NL API执行此任务。让我们考虑以下两个例子:

She’s living her best life. What do you do for a living?

living_from_live = "She's living her best life"
living_from_living = "What do you do for a living?"

language = 'en'

# 请求API
output = client.specific_resource_analysis(
    body={"document": {"text": living_from_live }}, 
    params={'language': language, 'resource''disambiguation'
})

# 请求API
output_2 = client.specific_resource_analysis(
    body={"document": {"text": living_from_living }}, 
    params={'language': language, 'resource''disambiguation'
})

# 在输出数组的开始部分以粗体打印TOKEN、POS和ID:
print (f'\t \033[1mOuput of the first sentence : \033[0m \n'
print (f'\033[1m{"TOKEN":{20}{"LEMMA":{15}{"POS":{6}} \033[0m')

#Syncon代表“概念”,我们用一个ID来表示
for token in output.tokens:
    print (f'{living_from_live [token.start:token.end]:{20}{token.lemma:{15}{token.pos:{6}} ')

print (f'\t \033[1mOuput of the second sentence : \033[0m \n')   
print (f'\033[1m{"TOKEN":{20}{"LEMMA":{15}{"POS":{6}} \033[0m')
for token in output_2.tokens:
    print (f'{living_from_living [token.start:token.end]:{20}{token.lemma:{15}{token.pos:{6}} ')
        Output of the first sentence :  

TOKEN                LEMMA           POS    
She                  she             PRON   
's                   's              AUX    
living               live            VERB   
her                  her             PRON   
best                 good            ADJ    
life                 life            NOUN   
     Output of the second sentence :  

TOKEN                LEMMA           POS    
What                 what            PRON   
do                   do              AUX    
you                  you             PRON   
do                   do              VERB   
for                  for             ADP    
a                    a               DET    
living               living          NOUN   
?                    ?               PUNCT

如上所述,“living”有两个不同的词形,这取决于上下文及其在句子中的位置。在第一个例子中,living对应于词形live,live是句子的动词。相反,living在第二句中是一个名词,有词形living。意思也不同,第一个词形描述的是“活着”的概念,而另一个是一个名词属于“一种收入或谋生手段”的概念。

因此,词形还原有助于机器推断同形词的意义。



结论


往期精彩回顾




本站qq群851320808,加入微信群请扫码:
浏览 47
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报