使用 Python 的 AI – NLTK 包

在本章中,我们将学习如何开始使用自然语言工具包。

先决条件

如果我们想构建具有自然语言处理的应用程序,那么上下文的变化会使其变得最困难。上下文因素会影响机器理解特定句子的方式。因此,我们需要使用机器学习方法开发自然语言应用程序,以便机器也能理解人类理解上下文的方式。

要构建此类应用程序,我们将使用名为 NLTK(自然语言工具包)的 Python 包。

导入 NLTK

在使用 NLTK 之前,我们需要安装它。可以借助以下命令安装它 −

pip install nltk

要为 NLTK 构建 conda 包,请使用以下命令 −

conda install -c anaconda nltk

现在安装 NLTK 包后,我们需要通过 Python 命令提示符导入它。我们可以通过在 Python 命令提示符上写入以下命令来导入它 −

>>> import nltk

下载 NLTK 的数据

现在导入 NLTK 后,我们需要下载所需的数据。可以在 Python 命令提示符上借助以下命令完成 −

>>> nltk.download()

安装其他必要的包

要使用 NLTK 构建自然语言处理应用程序,我们需要安装必要的包。软件包如下 −

gensim

它是一个强大的语义建模库,对许多应用程序都很有用。我们可以通过执行以下命令来安装它 −

pip install gensim

pattern

它用于使gensim包正常工作。我们可以通过执行以下命令来安装它

pip install pattern

标记化、词干提取和词形还原的概念

在本节中,我们将了解什么是标记化、词干提取和词形还原。

标记化

它可以定义为将给定的文本(即字符序列)分解为称为标记的较小单元的过程。标记可以是单词、数字或标点符号。它也被称为分词。以下是标记化的一个简单示例 −

输入 − 芒果、香蕉、菠萝和苹果都是水果。

输出标记化

可以借助定位单词边界来完成给定文本的分解过程。单词的结尾和新单词的开头称为单词边界。书写系统和单词的印刷结构会影响边界。

在 Python NLTK 模块中,我们有不同的与标记化相关的包,我们可以根据需要使用这些包将文本划分为标记。其中一些包如下 −

sent_tokenize 包

顾名思义,此包将输入文本划分为句子。我们可以借助以下 Python 代码导入此包 −

from nltk.tokenize import sent_tokenize

word_tokenize 包

此包将输入文本划分为单词。我们可以借助以下 Python 代码导入此包 −

from nltk.tokenize import word_tokenize

WordPunctTokenizer 包

此包将输入文本分为单词和标点符号。我们可以借助以下 Python 代码导入此包 −

from nltk.tokenize import WordPuncttokenizer

词干提取

在处理单词时,我们会遇到很多由于语法原因而产生的变体。这里变体的概念意味着我们必须处理相同单词的不同形式,如 democracy、democratizationdemocratization。机器非常有必要理解这些不同的单词具有相同的基本形式。这样,在分析文本时提取单词的基本形式会很有用。

我们可以通过词干提取来实现这一点。这样,我们可以说词干提取是通过切断单词的末尾来提取单词基本形式的启发式过程。

在 Python NLTK 模块中,我们有不同的与词干提取相关的包。这些包可用于获取单词的基本形式。这些包使用算法。其中一些包如下 −

PorterStemmer 包

此 Python 包使用 Porter 算法提取基本形式。我们可以借助以下 Python 代码导入此包 −

from nltk.stem.porter import PorterStemmer

例如,如果我们将单词 'writing' 作为此词干提取器的输入,则在词干提取后我们将得到单词 'write'

LancasterStemmer 包

此 Python 包将使用 Lancaster 算法提取基本形式。我们可以借助以下 Python 代码导入此包 −

from nltk.stem.lancaster import LancasterStemmer

例如,如果我们将单词'writing'作为此词干提取器的输入,则在提取词干后,我们将得到单词'write'

SnowballStemmer 包

此 Python 包将使用 snowball 的算法来提取基本形式。我们可以借助以下 Python 代码导入此包 −

from nltk.stem.snowball import SnowballStemmer

例如,如果我们将单词'writing'作为此词干提取器的输入,则在提取词干后,我们将得到单词'write'

所有这些算法的严格程度都不同。如果我们比较这三个词干提取器,则 Porter 词干提取器最不严格,而 Lancaster 最严格。 Snowball 词干提取器在速度和严格性方面都很好用。

词形还原

我们还可以通过词形还原提取单词的基本形式。它基本上是使用词汇和词的形态分析来完成这项任务,通常只旨在删除词尾。任何单词的这种基本形式称为词干。

词干提取和词干还原之间的主要区别在于词汇的使用和词的形态分析。另一个区别是,词干提取最常见的是折叠派生相关词,而词干还原通常只折叠词干的不同屈折形式。例如,如果我们提供单词 saw 作为输入词,那么词干提取可能会返回单词"s",但词形还原会尝试返回单词 see 或 saw,具体取决于标记的用法是动词还是名词。

在 Python NLTK 模块中,我们有以下与词形还原过程相关的包,我们可以使用它来获取单词 − 的基本形式

WordNetLemmatizer 包

此 Python 包将根据单词是用作名词还是动词来提取单词的基本形式。我们可以借助以下 Python 代码导入此包 −

from nltk.stem import WordNetLemmatizer

分块:将数据分成块

这是自然语言处理中的重要过程之一。分块的主要工作是识别词性和短语,如名词短语。我们已经研究了标记化的过程,即标记的创建。分块基本上就是标记这些标记。换句话说,分块将向我们展示句子的结构。

在下一节中,我们将了解不同类型的分块。

分块的类型

分块有两种类型。类型如下 −

分块

在这个分块过程中,对象、事物等变得更加通用,语言变得更加抽象。达成一致的机会更多。在这个过程中,我们缩小了范围。例如,如果我们将"汽车的用途是什么"这个问题分块?我们可能会得到答案"运输"。

分块

在分块过程中,对象、事物等变得更加具体,语言也更加深入。分块将检查更深层的结构。在这个过程中,我们会放大。例如,如果我们将问题"具体介绍一下汽车?"分块,我们将获得有关汽车的更小的信息。

示例

在此示例中,我们将使用 Python 中的 NLTK 模块 − 进行名词短语分块,这是一种分块,它将在句子中找到名词短语块。

按照 Python 中的以下步骤实现名词短语分块 −

步骤 1 − 在此步骤中,我们需要定义分块的语法。它将包含我们需要遵循的规则。

步骤 2 − 在此步骤中,我们需要创建一个块解析器。它将解析语法并给出输出。

步骤 3 − 在此最后一步中,输出以树格式生成。

让我们导入必要的 NLTK 包,如下所示 −

import nltk

现在,我们需要定义这个句子。这里,DT 表示行列式,VBP 表示动词,JJ 表示形容词,IN 表示介词,NN 表示名词。

sentence=[("a","DT"),("clever","JJ"),("fox","NN"),("was","VBP"),
          ("jumping","VBP"),("over","IN"),("the","DT"),("wall","NN")]

现在,我们需要给出语法。在这里,我们将以正则表达式的形式给出语法。

grammar = "NP:{<DT>?<JJ>*<NN>}"

我们需要定义一个解析语法的解析器。

parser_chunking = nltk.RegexpParser(grammar)

解析器按如下方式解析句子 −

parser_chunking.parse(sentence)

接下来,我们需要获取输出。输出在名为 output_chunk 的简单变量中生成。

Output_chunk = parser_chunking.parse(sentence)

执行以下代码后,我们可以以树的形式绘制输出。

output.draw()

Nltk

词袋 (BoW) 模型

词袋 (BoW) 是自然语言处理中的一种模型,主要用于从文本中提取特征,以便文本可用于建模,例如机器学习算法。

现在出现了一个问题,为什么我们需要从文本中提取特征。这是因为机器学习算法无法处理原始数据,它们需要数字数据,以便从中提取有意义的信息。将文本数据转换为数字数据称为特征提取或特征编码。

工作原理

这是一种从文本中提取特征的非常简单的方法。假设我们有一个文本文档,我们想将其转换为数字数据,或者说想从中提取特征,那么首先这个模型会从文档中的所有单词中提取一个词汇表。然后通过使用文档术语矩阵,它将建立一个模型。这样,BoW 将文档仅表示为一个词袋。文档中有关单词顺序或结构的任何信息都会被丢弃。

文档词矩阵的概念

BoW 算法使用文档词矩阵建立模型。顾名思义,文档词矩阵是文档中出现的各种单词计数的矩阵。借助此矩阵,可以将文本文档表示为各种单词的加权组合。通过设置阈值并选择更有意义的单词,我们可以构建文档中所有单词的直方图,可将其用作特征向量。以下是理解文档词矩阵概念的示例 −

示例

假设我们有以下两个句子 −

  • 句子 1 − 我们正在使用 Bag of Words 模型。

  • 句子 2 −使用词袋模型来提取特征。

现在,通过考虑这两个句子,我们有以下 13 个不同的单词 −

  • we
  • are
  • using
  • the
  • bag
  • of
  • words
  • model
  • is
  • used
  • for
  • extracting
  • features

现在,我们需要使用每个句子中的单词计数来为每个句子构建直方图 −

  • 句子 1 − [1,1,1,1,1,1,1,1,0,0,0,0,0]

  • 句子 2 − [0,0,0,1,1,1,1,1,1,1,1,1,1,1]

这样,我们就得到了提取的特征向量。每个特征向量都是 13 维的,因为我们有 13 个不同的单词。

统计概念

统计概念称为词频-逆文档频率 (tf-idf)。文档中的每个词都很重要。统计数据帮助我们了解每个单词的重要性。

词频 (tf)

它是衡量每个单词在文档中出现频率的指标。可以通过将每个单词的计数除以给定文档中的单词总数来获得。

逆文档频率 (idf)

它是衡量单词在给定文档集中对此文档的独特性。为了计算 idf 并制定独特特征向量,我们需要降低常见单词(如)的权重并加权罕见单词。

在 NLTK 中构建词袋模型

在本节中,我们将使用 CountVectorizer 从这些句子中创建向量来定义字符串集合。

让我们导入必要的包 −

from sklearn.feature_extraction.text import CountVectorizer

现在定义句子集。

Sentences = ['We are using the Bag of Word model', 'Bag of Word model is
           used for extracting the features.']

vectorizer_count = CountVectorizer()

features_text = vectorizer.fit_transform(Sentences).todense()

print(vectorizer.vocabulary_)

上述程序生成如下所示的输出。这表明,在上述两个句子中,我们有 13 个不同的单词 −

{'we': 11, 'are': 0, 'using': 10, 'the': 8, 'bag': 1, 'of': 7,
'word': 12, 'model': 6, 'is': 5, 'used': 9, 'for': 4, 'extracting': 2, 'features': 3}

这些是可用于机器学习的特征向量(文本到数字形式)。

解决问题

在本节中,我们将解决一些相关问题。

类别预测

在一组文档中,不仅单词,而且单词的类别也是重要;特定单词属于文本的哪个类别。例如,我们想要预测给定的句子是否属于电子邮件、新闻、体育、计算机等类别。在下面的例子中,我们将使用 tf-idf 来制定特征向量以查找文档的类别。我们将使用来自 sklearn 的 20 个新闻组数据集的数据。

我们需要导入必要的包 −

from sklearn.datasets import fetch_20newsgroups
from sklearn.naive_bayes import MultinomialNB
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.feature_extraction.text import CountVectorizer

定义类别图。我们使用五个不同的类别,分别为宗教、汽车、体育、电子和太空。

category_map = {'talk.religion.misc':'宗教','rec.autos''汽车',
'rec.sport.hockey':'曲棍球','sci.electronics':'电子', 'sci.space': '太空'}

创建训练集 −

training_data = fetch_20newsgroups(subset = 'train',
categories = category_map.keys(), shuffle = True, random_state = 5)

构建计数向量化器并提取术语计数 −

vectorizer_count = CountVectorizer()
train_tc = vectorizer_count.fit_transform(training_data.data)
print("
Dimensions of training data:", train_tc.shape)

tf-idf 转换器的创建方式如下 −

tfidf = TfidfTransformer()
train_tfidf = tfidf.fit_transform(train_tc)

现在,定义测试数据 −

input_data = [
   'Discovery was a space shuttle',
   'Hindu, Christian, Sikh all are religions',
   'We must have to drive safely',
   'Puck is a disk made of rubber',
   'Television, Microwave, Refrigrated all uses electricity'
]

上述数据将帮助我们训练多项式朴素贝叶斯分类器 −

classifier = MultinomialNB().fit(train_tfidf, training_data.target)

使用计数向量化器转换输入数据 −

input_tc = vectorizer_count.transform(input_data)

现在,我们将使用 tfidf 转换器转换向量化数据 −

input_tfidf = tfidf.transform(input_tc)

我们将预测输出类别 −

predictions = classifier.predict(input_tfidf)

输出生成如下 −

for sent, category in zip(input_data, predictions):
   print('
Input Data:', sent, '
 Category:', \
      category_map[training_data.target_names[category]])

类别预测器生成以下输出 −

Dimensions of training data: (2755, 39297)

Input Data: Discovery was a space shuttle
Category: Space

Input Data: Hindu, Christian, Sikh all are religions
Category: Religion

Input Data: We must have to drive safely
Category: Autos

Input Data: Puck is a disk made of rubber
Category: Hockey

Input Data: Television, Microwave, Refrigrated all uses electricity
Category: Electronics

性别查找器

在此问题陈述中,将训练分类器通过提供姓名来查找性别(男性或女性)。我们需要使用启发式方法来构建特征向量并训练分类器。我们将使用来自 scikit-learn 包的标记数据。以下是构建性别查找器的 Python 代码 −

让我们导入必要的包 −

import random

from nltk import NaiveBayesClassifier
from nltk.classify import accuracy as nltk_accuracy
from nltk.corpus import names

现在我们需要从输入单词中提取最后 N 个字母。这些字母将作为特征 −

def extract_features(word, N = 2):
   last_n_letters = word[-N:]
   return {'feature': last_n_letters.lower()}
	
if __name__=='__main__':

使用 NLTK 中可用的带标签名称(男性和女性)创建训练数据 −

male_list = [(name, 'male') for name in names.words('male.txt')]
female_list = [(name, 'female') for name in names.words('female.txt')]
data = (male_list + female_list)

random.seed(5)
random.shuffle(data)

现在,将按如下方式创建测试数据 −

namesInput = ['Rajesh', 'Gaurav', 'Swati', 'Shubha']

使用以下代码定义用于训练和测试的样本数量

train_sample = int(0.8 * len(data))

现在,我们需要迭代不同的长度,以便可以比较准确率 −

for i in range(1, 6):
   print('
Number of end letters:', i)
   features = [(extract_features(n, i), gender) for (n, gender) in data]
   train_data, test_data = features[:train_sample],
features[train_sample:]
   classifier = NaiveBayesClassifier.train(train_data)

分类器的准确率可按如下方式计算 −

accuracy_classifier = round(100 * nltk_accuracy(classifier, test_data), 2)
	print('Accuracy = ' + str(accuracy_classifier) + '%')

现在,我们可以预测输出 −

for name in namesInput:
   print(name, '==>', classifier.classify(extract_features(name, i)))

上述程序将生成以下输出 −

Number of end letters: 1
Accuracy = 74.7%
Rajesh -> female
Gaurav -> male
Swati -> female
Shubha -> female

Number of end letters: 2
Accuracy = 78.79%
Rajesh -> male
Gaurav -> male
Swati -> female
Shubha -> female

Number of end letters: 3
Accuracy = 77.22%
Rajesh -> male
Gaurav -> female
Swati -> female
Shubha -> female

Number of end letters: 4
Accuracy = 69.98%
Rajesh -> female
Gaurav -> female
Swati -> female
Shubha -> female

Number of end letters: 5
Accuracy = 64.63%
Rajesh -> female
Gaurav -> female
Swati -> female
Shubha -> female

在上面的输出中,我们可以看到最大结束字母数的准确度为 2,并且随着结束字母数的增加而降低。

主题建模:识别文本数据中的模式

我们知道,文档通常按主题分组。有时我们需要识别文本中与特定主题相对应的模式。这样做的技术称为主题建模。换句话说,我们可以说主题建模是一种在给定的文档集中发现抽象主题或隐藏结构的技术。

我们可以在以下场景中使用主题建模技术 −

文本分类

借助主题建模,分类可以得到改进,因为它将相似的单词组合在一起,而不是将每个单词单独用作特征。

推荐系统

借助主题建模,我们可以使用相似性度量来构建推荐系统。

主题建模算法

主题建模可以通过算法实现。算法如下 −

潜在狄利克雷分配 (LDA)

该算法是主题建模中最流行的算法。它使用概率图模型来实现主题建模。我们需要在 Python 中导入 gensim 包才能使用 LDA 算法。

潜在语义分析 (LDA) 或潜在语义索引 (LSI)

此算法基于线性代数。基本上,它在文档术语矩阵上使用 SVD(奇异值分解)的概念。

非负矩阵分解 (NMF)

它也基于线性代数。

上述所有主题建模算法都将主题数量作为参数,将文档-单词矩阵作为输入,将WTM(单词主题矩阵)TDM(主题文档矩阵)作为输出。