如何制作一个聊天机器人？
 熟悉数据科学的人应该知道，该领域一个最大的挑战就是获取合格的训练数据。拥有可靠的“真实”数据样本才能让机器学习取得好效果。特别是自然语言处理这样的任务，更是需要大量数据来学习文字语境、标签或通用的语言理解方式。 而作为人类的我们其实每时每刻都在制造训练数据——说话，所以干嘛不用我们自己的文本信息作为训练数据来创建一个聊天机器人呢？本文，就为大家展示如何用 Python 和你自己的文本消息创建一个简单的、能和你对话的聊天机器人。具体说来，是使用 elasticsearch 或 Doc2Vec 模型，本教程暂时只支持 iPhone/macOS。 我们要创造的这个聊天机器人非常简单，主要是教给大家一种入门方法，所以如果机器人表现非常蠢萌，也不要紧。更为复杂和高级的聊天机器人比如有一个模型架构的那种，通常涉及到实体识别、意图识别、量身打造的响应数据库、LSTM 网络等等。如果你想创建那种商业用的聊天机器人，能够作为客服回答顾客问题，那么可以试试 Rasa NLU 这种更复杂的开发框架。如果想了解一下怎么初步做一个简单的聊天机器人，本文就是你的菜！  两种方法  创建一个简单的聊天机器人有很多种容易的方法，但是选哪种方法应该根据你的数据情况。这里为大家列出两种方法，可以根据你手头的文本消息数据大小，选择合适的方法，也可以都尝试一下。   整体准备 首先应当安装 Python，当然也应懂得一些 Python 编程知识。 每种方法用到的程序包，我们后面也会讲到。你用的 Python 应该已经安装了 splite3 和 Pandas，开始阶段我们会用到。  在收集数据之前，将你的iPhone连接Mac，备份到最新时间点的数据。你需要尽可能多的数据来优化机器人。等完成这些后，我们就开始动手编程。我们首先创建一个项目目录，就像下面这样：  然后，需要将数据放在便于获取的位置。你备份的所有iPhone数据应该在~/Library/Application Support/MobileSync/Backup中。然后把数据库文件和文本数据拷贝到一个不那么复杂的路径中，在Mac终端上输入：  最后，通过cd进入到我们的项目目录后，启动Jupyter（或Python shell）。待Jupyter界面出现后，创建一个新的notebook：   准备数据 现在我们来到了有趣的环节。首先导入两个包——sqlite3和Pandas。Sqlite3能让我们从数据库文件中读取数据，Pandas能让我们将数据加载为dataframe结构，更方便我们操作。  这里是我们读取数据的地方，必须指明绝对路径，不然行不通，所以不能用~符号。在下方代码中，将kylegallatin修改为你的主目录名字，如果你不知道，就打开终端，输入~。  现在我们可以使用一些简单的SQL语句来获取我们所需的数据，将其加载到Pandas中进行处理。我们只需几列，所以指明它们。text是消息本身，handle_id是你以整数格式发送文字消息的人，is_from_me是一个二进制指示符——如果是1时，你发送文本，如果是0时，这一行的handle_id会发送文本，最终日期就是文本被发送时的日期。如果你想更深入的分析你的社交生活，可以检查其它列。  检查你获取的信息：   你应该能看到你最早备份的文本，应该已经按时间顺序排列，后面的工作也要求这一点。如果你不确定，可以运行df = df.sort_values(by='date')进行确认。要想检查你有多少条信息，可以运行len(df)。  接下来我们需要开始考虑训练数据了。虽然我们有很多消息数据，但都不是对话式训练数据的形式，只是按时间顺序排列而已。我们的目标就是获取包含两个列的dataframe：一列是text，一列是response，这样我们的聊天机器人就有样本去学习了。  我们不仅需要将对话按照人物（或handle_id）分组，而且数据显示我们会在一行中从同一个人身上获取多个文本，这就很难判断哪个是text，哪个是response。因此我们需要连接所有同步发生的文本，然后向该文本的response， df['text'][i+1]，分配每个df['text'][i]或text。  可以参考下面的代码进行优化：   以及最后一点预处理：   现在你可能会想“这样你就忽略了很多种不同的情况”——你这样想也正常。群聊怎么办？怎样定义对话的开始和结束？我们这里要考虑一大堆东西，不过本文我们不再考虑它们，因为这是一篇快速的实践指南，不是要做个很复杂的产品。  话说回来，我们现在获取了训练数据。对于每个handle_id，我们现在都获得了每个映射为response的text，然后每个response会成为下一个text。  有了这种格式的数据，我们现在就可以通过某种方式，将我们要说的话映射到聊天机器人中，然后它会回复我们。  方法一：使用Elasticsearch完成任务  方法一使用基于关键字的方法，它会搜索我们的文本列，使用我们“发送”给机器人的任何消息。通过将我们的文本和其它文本进行比较，我们可以尝试发送回一个合适的回复。  Elasticsearch和TF-IDF简介 Elasticsearch是一个可扩展的搜索平台，使用类似于TF-IDF的算法，TF-IDF是一种用于信息检索与数据挖掘的常用加权技术。  基本上讲，这是一个简单的函数，常用于面向基于关键字的文档的相似空间中。同时它也会关注出现频率较高的字词。比如，因为“的”这个字出现在太多文本中，我们不想让它被当成搜索查询中的重要部分。TF-IDF在将你的查询和文档进行比较时，会考虑到这一点。  我们实际要做的比较会基于余弦相似性。由于TF-IDF会向量化我们的文本，所以我们匹配出数据集中“最相似”文本的方式就需要基于该指标。它会帮我们找到任何大小的向量空间中最接近的向量，以此来表示我们的文档。  方法一准备 当然我们使用像sklearn这样的工具包也能很容易的实现TF-TDF算法，但是用Elasticsearch会更快更容易些。我们需要两样东西——elasticsearch及其客户端。我们使用软件包管理工具homebrew安装前者，如果还没安homebrew，从这里下载： https://brew.sh/    然后打开终端：  安装好Elasticsearch后，打开就行，Homebrew应该已经将它添加到你的路径中了。该终端命令应该会在端口9200上启动Elasticsearch。 elasticsearch  现在我们返回到Python notebook中。  索引和搜索 为了能在Elasticsearch中索引和处理数据，我们可以使用下方代码。我们首先导入包，然后创建我们的Python客户端的一个实例。最后，我们用于将数据加载到Elasticsearch的函数应为字典格式，所以我们将dataframe转为字典。  这部分的代码/客户端和Elasticsearch不再深入展开细说，不过我们这里所做的就是稍微处理一下数据，在我们叫做textbot的Elasticsearch实例上创建一个索引，现在就可以查询信息了。   基本上就是这样，现在唯一要做的事情就是创建一个函数来查询我们的数据。  方法一的聊天机器人 下面是一个简单的函数，执行一个连续循环。每一次，它会向用户提示输入输入，搜索我们的数据寻找最相关的text信息，然后返回同一行中的response。如果response为quit()，程序就会停止。我们从搜索中获得的变量response是由Elasticsearch所用的语言Query DSL生成的。  Response会是个json文件，包含了大量信息，但我们只想让一个值，所以我们只需获取这个值并把它打印出来即可。此外还可以引入一个randomness变量，它的用途就是每次你向聊天机器人发送文本信息时，不会得到一模一样的回复，因为回复是按照顺序来的。如果在索引i处的text记录中没有相关信息能匹配你发生的查询指令，你只会得到“idk”的回复。   现在可以去试试了。 chatbot()  下方是使用这种方法创建的一个聊天机器人示例，可以在代码中调试randomness参数，优化你的查询指令。    方法二：训练你自己的语境式Doc2Vec模型  这个方法基于两个模型：word2vec和doc2vec。  Word2vec和doc2vec简介 要想理解doc2vec，你首先需要看看Word2vec，该模型最初由谷歌的团队所创，旨在学习词汇的语境。它的架构如下所示：   Word2vec是一个比较浅层的神经网络，它将文本作为训练数据。其工作原理就是通过不断迭代来预测一个字词子集出现在给定字词附近的概率，或根据邻近词的窗口预测一个字出现的概率（根据你使用的架构）。随着每次迭代，网络的隐藏层中每个字的权重也会更新。最终我们丢弃预测结果但保留隐藏层中的权重。有了足够文本输入后，这些权重值应该能表示字词的语境。相同的词应该有相同的权向量，然后可以通过余弦相似度对比。更多关于Word2vec的介绍，可以查看这篇文章： http://mccormickml.com/2016/04/19/word2vec-tutorial-the-skip-gram-model/  Doc2vec与此相似只是输入变成了完整的文本块或句子，而不是词汇。和Word2vec中抛弃预测结果不同，这里我们可以选择评估一个标签。在Word2vec中，每个词已经是它自己的实体，所以没必要这么做。而在doc2vec中，我们有包含多个实体的句子或段落，因此我们可以向这些群组添加标签，将它们分类。  和方法一一样，我们还是使用余弦相似性。由于doc2vec会给我们发向机器人的文本的权重，并将其向量化，因此我们匹配数据集中最相似的text就需要基于该指标。下图很好地展示了我们这里说的东西。    方法二准备 数学啊理论啊这些，我们就不管了，直接开撸代码。  我们会使用genism这个包，能让我们很容易地部署谷歌最初发布的Word2vec和doc2vec包。通过命令行中的pip可以很容易地安装它： pip install gensim  训练你的doc2vec模型 首先我们需要定义将数据输入到模型中的方式。下面展示了可以通过一个迭代器，每次向模型输入一个句子。   Simple_preprcess函数只是做一些简单的事情，比如将文本变为小写来将其标准化。这里的关键部分是标签——这里我们将其设为[i]而非实际的response。  接着我们训练模型。   %%time只是用来乘以Jupyter中的cell，所以如果你使用的是shell或其它工具，把它去掉即可。断言语句确保了我们使用C编译器，这样能够快速训练模型。这里的200是我们会得到的词向量的大小，还可以试着调整其它参数。我们使用build_vocab函数让doc2vec来获取语料库，然后用文本数据训练我们的模型15个周期。OS包会让我们创建一个目录，用来在后面保存我们的模型。这样就行了。  完整性检查 Word2vec和doc2vec都带有很方便的余弦相似性函数，用于检查我们的200维度空间中词汇的距离。我们检查一两个词的情况。  我们看看结果：  如果你的结果不是很理想也不要泄气，可能是训练数据不足导致的。其实，最重要的意义在于你的模型能否从你和朋友之间的对话中学习。  方法二的聊天机器人 下面的部分就比较轻松了，我们只需创建一个简单的函数，获取输入，将输入标记上字符串，获取最相似的text，并返回合适的response。由于模型可能不会看到我们输入给它的全部文本，所以我们使用infer_vector函数将我们发送给聊天机器人的文本进行向量化处理。   现在运行你的聊天机器人，可以和它唠嗑了！  以上就是我们用自己的文本消息创建一个简单的聊天机器人的两种方法。