什么是聊天机器人?
一个聊天机器人(ChatBot)是一个基于人工智能的软件设备(例如,Siri,Alexa,Google Assistant等)。在应用程序,网站或其他网络中,ChatBot通常用于理解消费者的需求,然后协助他们执行特定的任务,如商业交易,酒店预订,表格提交等。今天几乎每家公司都部署了一个聊天机器人与用户互动。公司使用聊天机器人的一些方式是:
- 提供航班信息
- 连接客户和他们的财务
- 作为客户支持
不一而足,有无限种应用可能性。
聊天机器人的历史可以追溯到1966年,当时Weizenbaum发明了一种名为ELIZA的计算机程序。它只用200行代码模仿心理治疗师的语言。你仍然可以在这里与它交谈:伊丽莎。
Chatbots如何运作?
大致有两种变体聊天机器人:基于规则的(Rule-Based)和自学习(Self Learning)。
- Rule-based方法,机器人根据训练的一些规则来回答问题。定义的规则可以从非常简单到非常复杂。这类方法,机器人可以处理简单的查询,但无法管理复杂的查询。
- 自学习机器人基于机器学习方法,并且肯定比rule-based机器人更有效。这种机器人是另外两种类型:检索型 (Retrieval Based)或者 生成型(Generative)。
i)Retrieval-based型:聊天机器人使用一些启发式方法从预定义响应库中选择响应。聊天机器人使用对话的消息和上下文,从预定义的机器人消息列表中选择最佳响应。上下文可以包括对话树中的当前位置,对话中的所有先前消息,先前保存的变量(例如,用户名)。用于选择响应的启发式方法可以以多种不同方式设计,从rule-based if-else条件逻辑到机器学习分类器都是可以的。
II)Generative机器人可以生成答案,而并不总是回复一组已固定答案中的某一个。这使得它们更加智能,因为它从查询中逐字逐句地生成答案。
在本文中,我们将在python中基于NLTK库构建一个简单的基于检索的聊天机器人。
建立机器人
准备工作
了解scikit Library 和NLTK。但是,如果您不熟悉NLP,您仍然可以阅读该文章,然后再参考资源进行理解。
NLP
专注于人类语言和计算机之间相互作用的研究领域称为自然语言处理(简称NLP)。它位于计算机科学,人工智能和计算语言学的交叉点。NLP是计算机以智能和有用的方式分析,理解和从人类语言中获得意义的一种方式。通过利用NLP,开发人员可以组织和构建知识,以执行自动摘要,翻译,命名实体识别,关系提取,情感分析,语音识别和主题分割等任务。
NLTK:简介
NLTK(自然语言工具包)是用于构建Python程序以使用人类语言数据的领先平台。它提供易于使用的接口,超过50个语料库和词汇资源,例如WordNet,以及用于分类,分词,词干化,标记,解析和语义推理的一套文本处理库,是非常优秀的NLP工具包。
NLTK被称为“一个很好的教学和工作工具,使用Python的计算语言学”,以及“一个玩自然语言的神奇 Library ”。
Python的自然语言处理提供了语言处理编程的实用介绍。我强烈推荐这本书给那些开始使用Python学习NLP的人。
下载并安装NLTK
- 安装NLTK:运行
pip install nltk
- 测试安装:运行
python
然后键入import nltk
有关platform-specific的说明,请阅读这里。
安装NLTK包
导入NLTK并运行nltk.download().
这将打开NLTK下载器,您可以从中选择要下载的语料库和模型。您也可以一次下载所有包。
使用NLTK进行文本预处理
文本数据的主要问题是它都是文本格式(字符串)。但是,机器学习算法需要某种数值特征向量才能执行任务。因此,在我们开始任何NLP项目之前,我们需要使用预处理(pre-processing)。基本文字pre-processing包括:
- 将整个文本转换为大写或小写。因此,算法不会将不同情况下的相同单词视为不同。
- 符号化(Tokenization):Token化只是用于描述将普通文本字符串转换为Token列表(即我们实际需要的单词)的过程的术语。 Sentence tokenizer可用于查找句子列表,Word tokenizer可用于查找字符串中的单词列表。
NLTK数据包包括用于英语语境的预训练的Punkt标记器。
- 删除噪声即所有不符合标准数字或字母的东西。
- 删除停用词(Stop Words)。有时,一些非常常见的单词在帮助选择符合用户需求的文档时似乎没什么价值,完全从词汇表中排除。这些话被称为停用词。
- 词干:词干是将变形(或有时衍生)的词减少到它们的词干,词根或词形的过程 – 通常是书面文字形式。例如,如果我们对以下次做词干化处理的话:“Stems”,“Stemming”,“Stemmed”,“and Stemtization”,结果将是单字“stem”。
- 词形还原:词干化的一个轻微变体是词形还原。二者主要区别在于,词干通常可以创建不存在的词,而词词形还原得到的是实际词。词形还原的示例是“run”是诸如“running”或“ran”之类的单词的基本形式,或者单词“better”和“good”有相同的词形,因此它们被认为是相同的。
词袋(Bag of Words)
在初始预处理阶段之后,我们需要将文本转换为有意义的数字向量(或数组)。 bag-of-words是文本的表示,用于描述文档中单词的出现。它涉及两件事:
•已知单词的词汇表。
•衡量已知单词的存在。
为什么称它为词袋(Bag-Of-Words)?这是因为关于文档中单词的顺序或结构的任何信息都被丢弃,而模型只关注已知单词是否出现在文档中,而不是出现在文档中的位置。
Bag of Words背后的直觉是,如果文档具有相似的内容词,则它们是相似的。
例如,如果我们的字典包含单词{Learning,is,the,not,great},并且我们想要对文本“Learning is great”进行矢量化,那么我们将得到以下向量:(1,1,0,0,1)。
TF-IDF方法
Bag of Words方法的一个问题是高频率的单词在文档中开始占主导地位(例如,得分较高),但可能包含的信息量有限。此外,与较短的文档相比,词袋使更长的文档权重更高。
一种方法是通过它们在所有文档中出现的频率来重新调整单词的频率,使得在所有文档中频繁出现的频繁单词(如“the”)的分数受到惩罚。这种评分方法被称为词频率逆文档频率(Term Frequency-Inverse Document Frequency), 即TF-IDF,其中:
单词频率:是当前文档中单词频率的得分。
TF = (Number of times term t appears in a document)/(Number of terms in the document)
逆文档频率:这是该单词在文档中的罕见程度得分。
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.
Tf-idf权重是经常用于信息检索和文本挖掘的权重。此权重是用于评估单词对集合或语料库中的文档的重要程度的统计度量,
例:
考虑一个包含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实现:
从sklearn.feature_extraction.text导入TfidfVectorizer
余弦相似度
TF-IDF是应用于文本的变换,以在向量空间中获得两个实数向量。然后我们可以获得余弦:通过获取它们的点积并将其除以它们的标准化的乘积来表示任何一对矢量的相似性。使用以下公式,我们可以找出任何两个文件d1和d2之间的相似性。
Cosine Similarity (d1, d2) = Dot product(d1, d2) / ||d1|| * ||d2||
其中d1,d2是两个非零向量。
有关TF-IDF和余弦相似性的详细说明和实际示例,请参阅以下文档《Tf-Idf and Cosine similarity》。
现在我们对NLP流程有了一个初步的了解。现在是我们实现真正任务的时候了,即Chatbot的创建。下文中将聊天机器人命名为’ROBO’
导入(import)必须的库
import nltk
import numpy as np
import random
import string # to process standard python strings
语料
对于我们的示例,我们将使用维基百科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作为输入并返回规范化的Tokens。
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矢量化器,将原始文档集合转换为TF-IDF特征矩阵。
from sklearn.feature_extraction.text import TfidfVectorizer
Also, import cosine similarity module from scikit learn library
from sklearn.metrics.pairwise import cosine_similarity
这将用于查找用户输入的单词与语料库中的单词之间的相似性。这是聊天机器人最简单的实现方式。
我们定义一个函数响应,它搜索用户的话语中的一个或多个已知关键字,并返回几个可能的响应之一。如果找不到与任何关键字匹配的输入,则返回响应:“对不起!我不明白你的意思。“
def response(user_response):
robo_response=''
sent_tokens.append(user_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:
print("ROBO: ",end="")
print(response(user_response))
sent_tokens.remove(user_response)
else:
flag=False
print("ROBO: Bye! take care..")
到这里,我们在NLTK中编写了我们的第一个聊天机器人。您可以在此处找到包含语料库的完整代码。现在,让我们看看它如何与人类互动:
这并不算太糟糕。即使聊天机器人无法对某些问题给出满意的答案,但其他问题的表现还不错。
结论
虽然它是一个非常简单的机器人,几乎没有任何认知技能,但它是进入NLP并了解聊天机器人的好方法。对于一个生产系统,你会想要考虑一个现有的机器人平台或框架,这个例子可以帮助你思考创建一个聊天机器人的设计和挑战。