自然语言处理在词性标注和命名实体识别等方面的应用

举报
lwq1228 发表于 2021/07/28 08:47:00 2021/07/28
【摘要】 本博文主要结合实际例子讲述自然语言处理在词性标注和命名实体识别等方面的应用。

词类:词类是我们大多数人在学习英语的早期被教授的东西。它们是根据自身句法或语法功能分配给单词的类别。这些功能是不同单词之间存在的功能关系。

1、词性

英语有九个主要的词性:

  • 名词:事物或人
  • 代词:代替名词的词
  • 动词:动作词
  • 形容词:描述名词的词
  • 限定词:限制名词的词
  • 副词:描述动词、形容词或副词本身的词
  • 介词:将名词和其他词联系起来的词
  • 连词:连接两个句子或单词的单词
  • 感叹词:感叹词

2、词性标注器

词性标注器可以是有监督学习类型和无监督学习类型。
词性标注是给单词指定标签的过程。这是通过一种称为词性标注器的算法来完成的。算法的目的就这么简单。
大多数词性标注器都是有监督学习算法。有监督学习算法是机器学习算法,学习根据以前标记的数据执行任务。
无监督学习算法的词性标注器的迭代。这些算法将仅由特征组成的数据作为输入。这些特征与标签无关,因此算法不是预测标签,而是形成输入数据的组或簇。

有监督学习方法和无监督学习方法的主要区别如下:
有监督词性标注器将预标注语料库作为输入进行训练,而无监督词性标注器将未标注的语料库作为输入来创建一组词性标注。
有监督词性标注器根据标注的语料库创建带有各自词性标注的单词词典,而无监督词性标注器使用自己创建的词性标注集生成这些词典。

训练好的有监督和无监督词性标注器的输入和输出是相同的:分别是标记和带有词性标注的标记。

3、词性标注的应用

理解哪些单词对应哪些词性,有助于机器以多种方式处理自然语言:

词性标注有助于区分同音异义词——拼写相同但含义不同的词。
词性标注建立在句子和分词需求的基础上,这是自然语言处理的基本任务之一。
词性标注被其他算法用于执行更高级别的任务,我们将在本章讨论命名实体识别。
词性标注也有助于情感分析和问题回答的过程。

词性标注是理解自然语言过程中的重要一步,因为它有助于完成其他任务。

4、词性标注的类型

4.1、基于规则的词性标注器

这些词性标注器的工作方式几乎和它们的名字一样——按照规则。这些规则通常被称为上下文框架规则,并为标注器提供上下文信息,以理解给一个模棱两可的单词加什么标记。

# 执行基于规则的词性标注
# 导入nltk和punkt
import nltk

# 将输入字符串存储在名为s的变量中
s = 'i enjoy playing the piano'

# 将句子标记化
tokens = nltk.word_tokenize(s)

# 在标记上应用词性标注器,然后打印标注集
tags = nltk.pos_tag(tokens)
print(tags)

# 要理解“NN”词性标注代表什么,可以使用以下代码行
nltk.help.upenn_tagset("NN")

# 将包含同音异义词的输入字符串存储在名为sent的变量中:
sent = 'and so i said im going to play the piano for the play tonight'

# 标注这个句子,然后在标记上应用词性标注符
tagset = nltk.pos_tag(nltk.word_tokenize(sent))
print(tagset)

4.2、随机的词性标注器

随机词性标注器是使用除了基于规则的方法之外的任何方法来给单词指定标注的标注器。因此,有许多方法属于随机范畴。当确定单词的词性标注时,所有结合统计方法(如概率和频率)的模型都是随机模型。

我们将讨论三种模型:

  • 单位法或词频法:最简单的随机词性标注器仅根据一个单词与一个标签一起出现的概率将词性标注分配给模棱两可的单词。这称为词频法,因为标记器会检查分配给单词的词性标注的频率。
  • n元法:这基于前面的方法。名称中的n代表在确定一个单词属于特定词性标注的概率时要考虑多少个单词。在单位标注器中,n=1,因此只考虑单词本身。增加n值会导致标注器计算n个词性标注的特定序列一起出现的概率,并基于该概率为单词分配标签。最流行的n元标注器被称为维特比算法(Viterbi Algorithm)。
  • 隐马尔可夫模型:隐马尔可夫模型结合了词频法和n元法。马尔可夫模型是描述一系列事件或状态的模型。每种状态发生的概率仅取决于前一事件所达到的状态。这些事件基于观察。隐马尔可夫模型的“隐藏”方面是事件可能隐藏的一组状态。在词性标注的情况下,观察值是单词标记,隐藏的状态集是词性标注。
# 执行随机词性标注
# 导入spaCy
import spacy

# 加载spaCy的'en_core_web_sm'模型
# spaCy有特定于不同语言的模型。'en_core_web_sm'模型是一种英语语言模型
# 已经在博客和新闻文章等书面网络文本上训练了,包括词汇、语法和实体。
nlp = spacy.load('en_core_web_sm')

# 将模型与你想要分配词性标注的句子相匹配。这里使用我们给NLTK的词性标注器的句子
doc = nlp(u"and so i said i'm going to play the piano for the play tonight")

# 现在,让我们标记这个句子,分配词性标注,并打印它们
for token in doc:
    print(token.text, token.pos_, token.tag_)

# 要理解词性标注代表什么,请使用以下代码行
spacy.explain('VBZ')

注意:要了解更多关于spaCy模型的信息,请访问https://spacy.io/models。

5、分块

分块是一种以单词及其词性标注作为输入的算法。它处理这些单独的标记及其标签,以查看它们是否可以组合。一个或多个单独标记的组合称为块,分配给这种块的词性标注称为分块标签。有五个主要的分块标签:

名词短语(NP):这些短语以名词为词头。它们充当动词或动词短语的主语或宾语。
动词短语(VP):这些短语以动词为词头。
形容词短语(ADJP):这些短语以形容词为词头。描述和限定名词或代词是形容词短语的主要功能。它们直接位于名词或代词之前或之后。
副词短语(ADVP):这些短语以副词为词头。通过提供描述和限定名词和动词的细节,它们被用作名词和动词的修饰语。
介词短语(PP):这些短语以介词为词头。它们在时间或空间上定位一个行为或实体。

NLTK库中的块解析器是基于规则的,因此需要将正则表达式作为规则输出带有块标注的块。

spaCy可以在没有规则的情况下执行分块。让我们看看这两种方法。

# 用NLTK进行分块
# 导入nltk和punkt
import nltk

# 将包含同音异义词的输入字符串存储在名为sent的变量中:
sent = "and so i said i'm going to play the piano for the play tonight"

# 标注这个句子,然后在标记上应用词性标注符
tagset = nltk.pos_tag(nltk.word_tokenize(sent))
tagset

# 创建一个正则表达式来搜索名词短语
rule = r"""Noun Phrase: {<DT>?<JJ>*<NN>}"""

# 创建RegexpParser的实例,并为其提供规则
chunkParser = nltk.RegexpParser(rule)

# 给chunkParser包含标记及其各自的词性标注的标签集(tagset),以便它可以执行分块,然后绘制分块
chunked = chunkParser.parse(tagset)
chunked.draw()

# 让我们用另一句话来试试同样的东西。将输入句子存储在另一个变量中
a = "the beautiful butterfly flew away into the night sky"

# 标记句子,并使用NLTK的词性标注器进行词性标注
tagged = nltk.pos_tag(nltk.word_tokenize(a))
tagged

# 给chunkParser包含标记及其各自的词性标注的标签集(tagged),以便它可以执行分块,然后绘制分块
chunked = chunkParser.parse(tagged)
chunked.draw()
# 用spaCy语进行分块
# 导入spaCy
import spacy

# 加载spaCy的'en_core_web_sm'模型
nlp = spacy.load('en_core_web_sm')

# 将spaCy的英语模式运用到句子中
doc = nlp(u"the beautiful butterfly flew away into the night sky")

# 在此模型上应用noun_chunks,并为每个分块打印分块的文本、分块的词根以及连接词根和词头的依存关系
for chunk in doc.noun_chunks:
    print(chunk.text, chunk.root.text, chunk.root.dep_)

6、加缝

加缝是分块的延伸,你可能已经从它的名字中猜到了。它不是处理自然语言的强制性步骤,但可能是有益的。

分块是在加缝后进行的。分块之后,你有分块及其分块标签,以及单个单词及其词性标注。通常,这些多余的词是不必要的。它们对理解自然语言的最终结果或整个过程没有贡献,因此是一种麻烦。加缝的过程通过提取分块来帮助我们处理这个问题,分块标注形成标注语料库,从而去除不必要的位。这些有用的分块一旦从标注语料库中提取出来,就被称为缝隙。

# 执行加缝
# 导入nltk
import nltk

# 创建一个规则,将整个语料库分成块,并且只在标注为名词或名词短语的单词或短语中创建缝隙
rule = r"""Chink: {<.*>+}
                    }<VB.?|CC|RB|JJ|IN|DT|TO>+{"""

# 创建RegexpParser的实例,并为其提供规则
chinkParser = nltk.RegexpParser(rule)

# 将包含同音异义词的输入字符串存储在名为sent的变量中:
sent = "and so i said i'm going to play the piano for the play tonight"

# 标注这个句子,然后在标记上应用词性标注符
tagset = nltk.pos_tag(nltk.word_tokenize(sent))
tagset

# 给chinkParser包含标记及其各自的词性标注的标签集,以便它可以执行加缝,然后绘制缝隙
chinked = chinkParser.parse(tagset)
chinked.draw()

举例:建立和训练自己的词性标注

#  导入nltk
import nltk

# 首先要做的是挑选语料库来训练我们的标注器。导入必要的Python包。这里我们使用nltk treebank语料库来处理
tagged_sentences = nltk.corpus.treebank.tagged_sents()
# print(tagged_sentences[0])
# print("Tagged sentences: ", len(tagged_sentences))
# print("Tagged words: ", len(nltk.corpus.treebank.tagged_words()))

# 接下来需要确定我们的标记器在决定给一个单词分配什么标记时会考虑哪些特征。这些可以包括单词是全部大写、小写还是只有一个大写字母
# 自定义的标记器
def features(sentence, index):
    """ sentence: [w1, w2, ...], index: the index of the word """
    return {
        'word': sentence[index],
        'is_first': index == 0,
        'is_last': index == len(sentence) - 1,
        'is_capitalized': sentence[index][0].upper() == sentence[index][0],
        'is_all_caps': sentence[index].upper == sentence[index],
        'is_all_lower': sentence[index].lower == sentence[index],
        'prefix-1': sentence[index][0],
        'prefix-2': sentence[index][:2],
        'prefix-3': sentence[index][:3],
        'suffix-1': sentence[index][-1],
        'suffix-2': sentence[index][-2:],
        'suffix-3': sentence[index][-3:],
        'prev_word': '' if index == 0 else sentence[index - 1],
        'next_word': '' if index == len(sentence) - 1 else sentence[index + 1],
        'has_hyphen': '-' in sentence[index],
        'is_numeric': sentence[index].isdigit(),
        'capitals_inside': sentence[index][1:].lower() != sentence[index][1:]
    }

# 创建一个函数来剥离标签中的标记词,以便我们可以将它们输入到标记器中
def untag(tagged_sentence):
    return [w for w, t in tagged_sentence]

# 现在我们需要建立训练集。标记器需要为每个单词单独提取特征,但是语料库实际上是句子的形式,所以需要做一些转换。
# 将数据分成训练集和测试集。将此函数应用于训练集
# Split the dataset for training and testing
cutoff = int(.75 * len(tagged_sentences))
# 75%的数据作为训练集
training_sentences = tagged_sentences[:cutoff]
# 25%的数据作为测试集
test_sentences = tagged_sentences[cutoff:]

# print(len(training_sentences))
# print(len(test_sentences))

# and create a function to assign the features to 'x' and append the pos tags to 'y'
# 将特征指定给“X”,并将词性标注附加到“Y”
def transform_to_dataset(tagged_sentences):
    x, y = [], []
    for tagged in tagged_sentences:
        for index in range(len(tagged)):
            # print(untag(tagged), index)
            x.append(features(untag(tagged), index))
            y.append(tagged[index][1])
    return x, y

# 将此函数应用于训练集。现在我们可以训练我们的标记器了。
x, y = transform_to_dataset(training_sentences)

# 它基本上是一个分类器,因为它将单词分类,所以可以使用分类算法。
# 读者可以用喜欢的任何一种或者多种,看看哪一种效果最好。这里我们使用决策树分类器。
# 导入分类器,对其进行初始化,并将模型与训练数据相匹配。打印精度分数
# DecisionTreeClassifier:决策树分类器
from sklearn.tree import DecisionTreeClassifier
# DictVectorizer:字典特征提取器,将字典类型数据结构的样本,抽取特征,转化成向量形式
from sklearn.feature_extraction import DictVectorizer
from sklearn.pipeline import Pipeline

# DictVectorizer(sparse=False):sparse=False意思是不产生稀疏矩阵
# criterion='entropy':
# 选择结点划分质量的度量标准,默认使用‘gini’,即基尼系数,基尼系数是CART算法中采用的度量标准,
# 该参数还可以设置为 “entropy”,表示信息增益,是C4.5算法中采用的度量标准。
# Pipeline()函数可以把多个“处理数据的节点”按顺序打包在一起,数据在前一个节点处理之后的结果,转到下一个节点处理
clf = Pipeline([('vectorizer', DictVectorizer(sparse=False)), ('classifier', DecisionTreeClassifier(criterion='entropy'))])

# Use only the first 10K samples if you're running it multiple times. It takes a fair bit
clf.fit(x[:10000], y[:10000])

print('Training completed')
x_test, y_test = transform_to_dataset(test_sentences)
print('Accuracy: ', clf.score(x_test, y_test))

7、命名实体识别

这是信息提取过程中的第一步。信息提取是机器从非结构化或半结构化文本中提取结构化信息的任务。这促进了机器对自然语言的理解。

经过文本预处理和词性标注,我们的语料库成为半结构化和机器可读的。因此,信息提取是在我们准备语料库后执行的。

7.1、命名实体识别器(NER)

命名实体识别器(NER)是一种从语料库中识别和提取命名实体并给它们分配类别的算法。

命名实体识别问题分两个阶段进行:
1)找到并识别命名实体(例如,“London”);
2)对这些名称实体进行分类(例如,“London”is a“location”)。

很像词性标注器,大多数命名实体识别器都是有监督学习算法。它们接受包含命名实体及其所属类别的输入训练,从而使算法能够学习如何在未来对未知命名实体进行分类。
这种包含命名实体及其各自类别的输入通常被称为知识库。

7.2、命名实体识别的应用

在线内容:包括文章、报告和博客帖子,它们通常会被标记,以便用户能够更容易地搜索,并快速了解确切内容。命名实体识别器可用于搜索该内容,并提取命名实体以自动生成这些标签。这些标签也有助于将文章分类到预定义的层次结构中。
搜索算法:也受益于这些标签。如果用户要在搜索算法中输入关键词,而不是搜索每篇文章的所有单词(这将需要很长时间),该算法只需要参考命名实体识别产生的标签,就可以提取包含或属于输入关键词的文章。这大大减少了计算时间和操作。
这些标签的另一个目的是创建一个高效的推荐系统:如果你读了一篇讨论印度当前政治形势的文章,因此可能被标注为“印度政治”(这只是一个例子),新闻网站可以使用这个标签来建议不同的文章使用相同或相似的标签。这也适用于电影和表演等视觉娱乐。在线流媒体网站使用分配给内容的标签(例如,“动作”“冒险”“惊悚”等类型)来更好地理解你的品味,从而向你推荐类似的内容。
客户反馈:对任何服务或产品提供公司都很重要。通过命名实体识别器运行客户投诉和审查,生成标签,可以帮助根据位置、产品类型和反馈类型(正面或负面)对其进行分类。然后,这些评论和投诉可以发送给负责特定产品或特定领域的人员,并可以根据反馈是正面的还是负面的来处理。推特、图片说明、脸书帖子等也可以做到这一点。

7.3、命名实体识别器类型

与词性标注器的情况一样,有两种设计命名实体识别器的一般方法:通过定义规则来识别实体的语言学方法,或者使用统计模型来准确确定命名实体属于哪个类别的随机方法。

1、基于规则的NER

基于规则的NER的工作方式与基于规则的词性标注器的工作方式相同。

2、随机NER

这些模型包括使用统计数据命名和识别实体的所有模型。随机命名实体识别有几种方法。让我们看看其中两个:

最大熵分类:这是一个机器学习分类模型。它仅根据提供给它的信息(语料库)来计算命名实体落入特定类别的概率。
隐马尔可夫模型:该方法与词性标注部分中解释过的方法相同,但隐藏的状态集不是词性标注,而是命名实体的类别。

举例1:用NLTK进行命名实体识别

在本练习中,我们将使用NLTK的ne_chunk算法对句子执行命名实体识别。我们不使用在前面练习中使用的句子,而是创建一个新的句子,其中包含可以分类的专有名词,以便你可以实际看到结果,具体步骤如下:

# 导入nltk
import nltk

# 将输入句子存储在变量中
ex = "Shubhangi visited the Taj Mahal after taking a SpiceJet flight from Pune."

# 标记句子并为标记分配词性标注
tags = nltk.pos_tag(nltk.word_tokenize(ex))
tags

# 对标记单词应用ne_chunk()算法,并打印或绘制结果
# 将“True”值赋给“binary”参数会告诉算法只识别命名实体,而不对它们进行分类
ne = nltk.ne_chunk(tags, binary = True)
ne.draw()

# 要知道算法已将哪些类别分配给这些命名实体,只需将“False”值分配给“binary”参数即可
ne = nltk.ne_chunk(tags, binary = False)
ne.draw()

# 该算法准确地对“Shubhangi”和“SpiceJet”进行了分类。
# 然而,“TajMahal”不应该是一个组织,它应该是一个设施。因此,NLTK的ne_chunk()算法不是最好的算法。

举例2:使用spaCy执行命名实体识别

# 导入spacy
import spacy

# 加载spaCy的'en_core_web_sm'模型
nlp = spacy.load('en_core_web_sm')

# 将spaCy的英语模型与我们在前面练习中使用的句子相匹配
doc = nlp(u"Shubhangi visited the Taj Mahal after taking a SpiceJet flight from Pune.")

# 对于这句话中的每个实体,打印实体的文本和标签
for ent in doc.ents:
    print(ent.text, ent.label_)
    
# 根据新句子调整模型
doc1 = nlp(u"Shubhangi Hora visited the Taj Mahal after taking a SpiceJet flight from Pune.")

# 对于这句话中的每个实体,打印实体的文本和标签
for ent in doc1.ents:
    print(ent.text, ent.label_)

举例3:在标记语料库上运行NER

# 导入NLTK和其他必要的包
import nltk

# 打印nltk.corpus.treebank.tagged_sents(),查看需要从中提取命名实体的标记语料库
tagged_sentence = nltk.corpus.treebank.tagged_sents()

# 将标记句子的第一句存储在变量中
first_sentence = tagged_sentence[0]
first_sentence

# 用nltk.ne_chunk来执行NER。将二进制参数设置为True,并打印命名实体
ne = nltk.ne_chunk(first_sentence, binary = True)
ne.draw()

8、小结

更好地理解语言并使机器为现实世界做出贡献的两种方法是词性标注和命名实体识别。

词性标注:是将词性标签分配给单个单词的过程,以便机器能够学习上下文
命名实体识别:是识别命名实体并对其进行分类,以便从语料库中提取有价值的信息

这些过程的执行方式有所不同:算法可以是有监督的或无监督的,方法可以是基于规则的或随机的。不管怎样,目标是一样的,那就是用人类的自然语言理解和交流。
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。