在Python中使用NLTK建立一个简单的Chatbot

2018年10月12日 由 yuxiangyu 发表 534831 0
在Python中使用NLTK建立一个简单的Chatbot

也许你听说过Duolingo(多邻国):一种流行的语言学习应用程序,它可以通过游戏来练习一种新的语言。由于其创新的外语教学风格,它非常受欢迎。它的思想很简单:每天五到十分钟的交互式培训足以学习一门语言。

然而,尽管Duolingo正在让人们学习一门新语言,但它的用户却有所顾虑忧。人们觉得他们错过了学习有价值的会话技能,因为他们是自学的。由于害怕尴尬,人们也害怕和其他语言学习者一起学习。这已成为Duolingo计划的一大瓶颈。

因此,他们的团队通过在其应用程序中构建聊天机器人来解决此问题,帮助用户学习会话技巧并练习他们所学到的东西。

在Python中使用NLTK建立一个简单的Chatbot

由于机器人被设计成亲切而健谈,Duolingo的用户可以使用他们选择的角色在一天中的任何时间练习对话,直到他们有足够的勇气与其他人练习他们的新语言。这解决了一个重要的用户痛点,让通过应用程序学习变得更加有趣。

那么,什么是聊天机器人(chatbot)?

聊天机器人是一个设备上软件的人工智能驱动部分(Siri的,Alexa的,谷歌助手等),应用程序、网站或其他网络试图了解消费者的需求,并帮助他们执行特定任务(如商业事务、酒店预订、表单提交等等)。如今,几乎每家公司都有部署聊天机器人与用户互动。公司使用聊天机器人的方式有:

  • 提供航班信息

  • 关联客户和他们的资产

  • 作为客户支持


可能性(几乎)是无限的。

在Python中使用NLTK建立一个简单的Chatbot

Chatbots如何运作?


聊天机器人有两种变体:基于规则的和自学习的。




  1. 在基于规则的方法中,机器人根据训练的一些规则来回答问题。定义的规则可以非常简单,也可以非常复杂。机器人可以处理简单的查询,但很难管理复杂的查询。

  2. 自学习机器人使用一些基于机器学习的方法,而且肯定比基于规则的机器人更高效。他们主要分为两种类型:基于检索或生成


i)在基于检索的模型中,聊天机器人使用一些启发式方法从预定义的响应库中选择回应。这种聊天机器人使用对话的消息和上下文从预定义的机器人消息列表中选择最佳回答。上下文可以包括对话树(dialog tree)中的当前位置,对话中所有的先前消息和先前保存的变量(例如,用户名)。用于选择回应的启发式方法可以以许多不同的方式设计,从基于规则的if-else条件逻辑到机器学习分类器都可以。


ii)生成式的聊天机器人可以生成答案,而不是总是回答答案集合中的答案之一。这使得它们更加智能,因为它们从查询中逐字逐句地提取,然后生成答案。


在Python中使用NLTK建立一个简单的Chatbot

在本文中,我们将在python中用NLTK库构建一个简单的检索聊天机器人。

建立聊天机器人


先决条件


具有scikit库和NLTK的实践知识。但即使是NLP的新手,也可以先阅读本文,然后再参考资源。



NLP


专注于人类语言和计算机之间交互的研究领域,称为自然语言处理(Natural Language Processing)。它是计算机科学,人工智能和计算语言学的交集。NLP是一种让计算机以一种聪明而有用的方式分析、理解并从人类语言中获得意义的方法。通过利用NLP,开发人员可以整理和构建知识,以执行自动摘要,翻译,命名实体识别,关系提取,情感分析,语音识别和主题分割等任务。



NLTK


NLTK(Natural Language Toolkit)是构建用于处理人类语言数据的Python程序的领先平台。它为超过50个语料库和词汇资源(如WordNet)提供了易于使用的接口,还提供了一套用于分类,标记化,词干化,标记,解析和语义推理的文本处理库,以及工业级NLP库的包装器。


NLTK被称为“用于教学和工作的精彩工具,使用Python的计算语言学”,以及“用于自然语言的神奇的库”(建议阅读书:Natural Language Processing with Python)。



下载并安装NLTK


1.安装NLTK:运行 pip install nltk

2.测试安装:运行python然后键入import nltk

安装NLTK包


导入NLTK并运行nltk.download()。这将打开NLTK下载器,你可以从中选择要下载的语料库和模型。也可以一次下载所有包。

使用NLTK进行文本预处理


文本数据的主要问题是它是文本格式(字符串)。但是,机器学习算法需要某种数字特征向量才能执行任务。因此,在我们开始任何NLP项目之前,我们需要对其进行预处理,使其适合于工作。文本预处理包括:




  • 将整个文本转换为大写或小写,以便算法不会将不同情况下的相同单词视为不同。

  • 标记化(Tokenization):标记化是用于描述将普通文本字符串转换为标记列表(token,即我们实际需要的单词)过程的术语。句子标记器(Sentence tokenizer)可用于查找句子列表,单词标记器(Word tokenizer)可用于查找字符串中的单词列表。


NLTK数据包包括一个预训练的英语Punkt标记器。




  • 删除噪声 - 即不是标准数字或字母的所有内容。

  • 删除停止词。有时,一些极为常见的单词在帮助选择符合用户需求的文档时没什么价值,所以被排除在词汇表之外。这些词被称为停止词(stop words)。

  • 词干提取:词干提取(Stemming)是将变形(比如派生)的词语缩减回词干,词基或词根的过程 - 通常是书面形式。例如,“Stems ”,“Stemming ”,“Stemmed ”,“Stemtization”,结果将是单词“stem ”。

  • 词形还原:词干化的一个变体是词形还原。这些之间的主要区别在于,词干提取通常可以创建不存在的词,而词汇还原都是实际的词。所以,你词干提取的词根,意思是你最终得到的词,不是你只查字典就可以查找的,但词形还原可以查找。词形还原的例子是“run”是诸如“running”或“ran”之类的单词的基本形式,或者“better”和“good”这两个词在同一个词目中,因此它们被认为是相同的。


词袋


在初始预处理阶段之后,我们需要将文本转换为有意义的数字向量(或数组)。词袋(Bag of Words)是描述文档中单词出现的文本表示形式。它包括:




  • 已知单词的词汇表。

  • 已知单词存在的度量。


为什么它被称为单词的“ 袋”?这是因为关于文档中单词的顺序或结构的任何信息都被丢弃,并且模型仅涉及已知单词是否出现在文档中,而不涉及出现在文档中的位置。


它的原理是,如果文档具有相似的内容,则它们是相似的。此外,我们可以仅从其内容中了解文档的含义。


例如,如果我们的字典包含单词{Learning,is,the,not,great},并且我们想要对文本“Learning is great”进行矢量化,我们将得到以下向量:(1, 1, 0, 0, 1)。



TF-IDF方法


词袋方法的一个问题是高频率的单词在文档中开始占主导地位(例如,得分较高),但它们可能不包含那么多的“信息内容”。此外,与较短的文档相比,它给更长的文档更大权重。


解决这个的一种方法是通过它们在所有文档中出现的频率来重新调整单词频率,使得在所有文档中频繁出现的频繁单词(如“the”)的分数受到惩罚。这种评分方法称为词频逆文本频率指数(Term Frequency-Inverse Document Frequency),简称TF-IDF,其中:


Term Frequency:是当前文档中单词频率的得分。



TF = (Number of times term t appears in a document)/(Number of terms in the document)

Inverse Document Frequency:是对文档中单词的罕见程度的评分。
IDF = 1+log(N/n), where, N is the number of documents and n is the number of documents a term t has appeared in.

例:

考虑一个包含100个单词的文档,其中“phone”一词出现5次。

然后,phone的词频(即,tf)是(5/100)= 0.05。现在,假设我们有1000万个文档,其中有一千个是phone。然后,逆文本频率(即,IDF)被计算为log(10,000,000 / 1,000)= 4.因此,Tf-IDF权重是这些量的乘积:0.05 * 4 = 0.20。

Tf-IDF可以在scikit learn中实现为:

from sklearn.feature_extraction.text import TfidfVectorizer

余弦相似度


TF-IDF是一种在向量空间中得到两个实值向量的应用于文本的变换。变换后我们可以通过获取它们的点积并将其除以它们范数的乘积来获得任何一对矢量的余弦相似度。得到向量夹角的余弦值。余弦相似度是两个非零向量之间相似性的度量。使用下面公式,我们可以求出任意两个文档d1和d2的相似度。



Cosine Similarity (d1, d2) =  Dot product(d1, d2) / ||d1|| * ||d2||

其中d1,d2是两个非零向量(更多解释可以访问https://janav.wordpress.com/2013/10/27/tf-idf-and-cosine-similarity/)。

在我们对NLP流程有了一个大致的了解。现在是时候创建Chatbot了。我们将这里的聊天机器人命名为' ROBO’

导入必要的库


import nltk
import numpy as np
import random
import string # to process standard python strings

语料库


对于我们的示例,我们将使用维基百科页面chatbot作为我们的语料库(https://en.wikipedia.org/wiki/Chatbot)。复制页面中的内容并将其放在名为“chatbot.txt”的文本文件中。当然,你可以使用你选择的任何语料库。



阅读数据


我们将读入corpus.txt文件并将整个语料库转换为句子列表和单词列表以供进一步预处理



f=open('chatbot.txt','r',errors = 'ignore')
raw=f.read()
raw=raw.lower()# converts to lowercase
nltk.download('punkt') # first-time use only
nltk.download('wordnet') # first-time use only
sent_tokens = nltk.sent_tokenize(raw)# converts to list of sentences
word_tokens = nltk.word_tokenize(raw)# converts to list of words

让我们看一下sent_tokens和word_tokens的例子
sent_tokens[:2]
['a chatbot (also known as a talkbot, chatterbot, bot, im bot, interactive agent, or artificial conversational entity) is a computer program or an artificial intelligence which conducts a conversation via auditory or textual methods.',
'such programs are often designed to convincingly simulate how a human would behave as a conversational partner, thereby passing the turing test.']
word_tokens[:2]
['a', 'chatbot', '(', 'also', 'known']

预处理原始文本


我们现在将定义一个名为LemTokens的函数,它将token作为输入并返回标准化的token。



lemmer = nltk.stem.WordNetLemmatizer()
#WordNet is a semantically-oriented dictionary of English included in NLTK.
def LemTokens(tokens):
return [lemmer.lemmatize(token) for token in tokens]
remove_punct_dict = dict((ord(punct), None) for punct in string.punctuation)
def LemNormalize(text):
return LemTokens(nltk.word_tokenize(text.lower().translate(remove_punct_dict)))

关键字匹配


接下来,我们将为机器人定义一个问候函数,即如果用户的输入是问候语,机器人将返回问候语的响应。ELIZA使用简单的关键字匹配问候语。我们这里的实现理念与此相同。



GREETING_INPUTS = ("hello", "hi", "greetings", "sup", "what's up","hey",)
GREETING_RESPONSES = ["hi", "hey", "*nods*", "hi there", "hello", "I am glad! You are talking to me"]
def greeting(sentence):

for word in sentence.split():
if word.lower() in GREETING_INPUTS:
return random.choice(GREETING_RESPONSES)

生成响应


为了从我们的机器人生成输入问题的响应,我们使用文档相似度的概念。所以我们首先导入必要的模块。




  • 从scikit learn库中,导入TFidf vectorizer,以将原始文档集合转换为TF-IDF特征矩阵。


from sklearn.feature_extraction.text import TfidfVectorizer


  • 另外,从scikit学习库导入cosine_similarity模块


from sklearn.metrics.pairwise import cosine_similarity

它会用于查找用户输入的单词与语料库中的单词之间的相似度。这是聊天机器人最简单的实现方式。

我们定义一个函数响应,它搜索用户的语言中的一个或多个已知关键字,并返回可能的响应之一。如果找不到与任何关键字匹配的输入,则返回响应:“I am sorry! I don't understand you“
def response(user_response):
robo_response=''
TfidfVec = TfidfVectorizer(tokenizer=LemNormalize, stop_words='english')
tfidf = TfidfVec.fit_transform(sent_tokens)
vals = cosine_similarity(tfidf[-1], tfidf)
idx=vals.argsort()[0][-2]
flat = vals.flatten()
flat.sort()
req_tfidf = flat[-2]
if(req_tfidf==0):
robo_response=robo_response+"I am sorry! I don't understand you"
return robo_response
else:
robo_response = robo_response+sent_tokens[idx]
return robo_response

最后,我们将根据用户的输入提供我们希望机器人在对话开始和结束时说出的行。
flag=True
print("ROBO: My name is Robo. I will answer your queries about Chatbots. If you want to exit, type Bye!")
while(flag==True):
user_response = input()
user_response=user_response.lower()
if(user_response!='bye'):
if(user_response=='thanks' or user_response=='thank you' ):
flag=False
print("ROBO: You are welcome..")
else:
if(greeting(user_response)!=None):
print("ROBO: "+greeting(user_response))
else:
sent_tokens.append(user_response)
word_tokens=word_tokens+nltk.word_tokenize(user_response)
final_words=list(set(word_tokens))
print("ROBO: ",end="")
print(response(user_response))
sent_tokens.remove(user_response)
else:
flag=False
print("ROBO: Bye! take care..")

现在,我们用NLTK中编写了我们的第一个聊天机器人。现在,让我们看看它如何与人类互动:

在Python中使用NLTK建立一个简单的Chatbot

这并不算太糟糕。即使聊天机器人无法对某些问题给出满意的答案,但其他人的表现还不错。



总结


虽然它是一个十分简单的聊天机器人,几乎没有任何认知技能,但它是入门NLP并了解聊天机器人的好方法。


GitHub:https://github.com/parulnith/Building-a-Simple-Chatbot-in-Python-using-NLTK/blob/master/chatbot.py

 
欢迎关注ATYUN官方公众号
商务合作及内容投稿请联系邮箱:bd@atyun.com
评论 登录
写评论取消
回复取消