机器学习解决方案-全-
机器学习解决方案(全)
原文:
annas-archive.org/md5/b07f17bc9671b217e2992493907316c0译者:飞龙
前言
这本书,名为《机器学习解决方案》,为你提供了关于这个主题的广泛了解。作为读者,你将有机会学习如何使用各种机器学习(ML)技术开发尖端的数据科学应用。这本书是一本实用的指南,可以帮助你构建和优化你的数据科学应用。
我们通过实际操作来学习。各种机器学习技术、技巧、优化技术等实践应用将增强你在机器学习和数据科学应用开发领域的理解。
现在我来回答我经常从我的朋友和同事那里听到的关于机器学习和数据科学应用开发方面最常见的问题之一。这个问题真正激发了我写这本书的灵感。对我来说,非常重要的一点是,让所有我的读者了解我为什么写这本书。让我们找出这个问题…!
问题是,“我如何实现机器学习应用程序的最佳可能准确性?”答案包括许多人们应该注意的事项:
- 
真正理解应用程序的目标。为什么你的组织想要构建这个应用程序? 
- 
列出应用程序的预期输出以及这种输出如何帮助组织。这将向您阐明应用程序的技术方面和业务方面。 
- 
你有什么样的数据集?为了生成所需的输出,你还需要什么? 
- 
仔细探索数据集。试图从数据集中获得洞察。 
- 
检查数据集是否有标签。如果是标记数据集,则可以应用监督算法;如果不是标记数据集,则应用无监督算法。你的问题陈述是一个回归问题或分类问题。 
- 
使用简单的机器学习技术构建一个非常简单的基准方法。测量准确性。 
- 
现在你可能会想,“我没有选择正确的算法,这就是基准方法准确性不好的原因。”没关系! 
- 
尝试列出你认为是基准方法可能存在的所有可能问题。诚实地面对这些问题。 
- 
现在逐一解决问题并测量准确性。如果准确性在提高,那么就继续朝这个方向前进;否则,尝试其他解决方案,这些解决方案最终会解决基准方法的不足并提高准确性。 
- 
你可以重复这个过程多次。每次迭代后,你都会得到一个新的明确方向,这将引导你找到最佳可能的解决方案以及准确性。 
我在这本书中涵盖了所有指定的方面。在这里,主要目标是让读者了解如何使用机器学习算法为自己的数据科学问题获得最先进的结果,为了实现这一点,我们将只使用最必要的理论以及许多不同领域的实践示例。
我们将涵盖分析领域、NLP 领域和计算机视觉领域。这些示例都是行业问题,读者将学习如何获得最佳结果。阅读本书后,读者将应用他们的新技能来解决任何类型的行业问题,以实现机器学习应用的最佳效果。
本书面向的对象
典型的读者将具备本科数学的基本到中级知识,例如概率、统计学、微积分和线性代数。本书将主要自包含,因此不需要高级数学。还需要具备机器学习(ML)算法的基本到中级知识。由于本书将主要自包含,因此不需要机器学习的高级概念。还需要具备一定的 Python 知识,因为介绍 Python 将超出范围,但每个过程都将逐步解释,以确保可重复性。
本书充满了实用的示例。读者希望了解如何高效地将机器学习(ML)算法应用于实际数据科学应用。本书从基本的 ML 技术开始,这些技术可以用来开发基础方法。之后,读者将学习如何为每个应用应用优化技术,以达到最先进的结果。对于每个应用,我都指定了基本概念、技巧和代码。
为了充分利用本书
需要具备数学、概率、统计学和微积分的基本到中级知识。
需要具备机器学习(ML)算法的基本到中级知识。
需要具备一定的 Python 知识。
在阅读章节时,请运行代码,以便您能够理解应用的流程。所有代码都可在 GitHub 上找到。链接是:github.com/jalajthanaki/Awesome_Machine_Learning_Solutions。
代码链接在章节中指定。每个应用的安装说明也可在 GitHub 上找到。
您需要至少 8GB 的 RAM 才能顺利运行应用。如果您能在 GPU 上运行代码,那就太好了;否则,您可以使用预训练模型。您可以使用 GitHub 链接或 Google Drive 链接下载预训练模型。链接在章节中指定。
下载示例代码文件
您可以从www.packtpub.com的账户下载本书的示例代码文件。如果您在其他地方购买了本书,您可以访问www.packtpub.com/support并注册,以便将文件直接通过电子邮件发送给您。
按照以下步骤下载代码文件:
- 
在 www.packtpub.com登录或注册。
- 
选择支持选项卡。 
- 
点击代码下载与勘误。 
- 
在搜索框中输入书籍名称,并遵循屏幕上的说明。 
文件下载完成后,请确保使用最新版本解压缩或提取文件夹:
- 
WinRAR / 7-Zip for Windows 
- 
Zipeg / iZip / UnRarX for Mac 
- 
7-Zip / PeaZip for Linux 
书籍的代码包也托管在 GitHub 上,网址为github.com/PacktPublishing/Machine-Learning-Solutions。如果代码有更新,它将在现有的 GitHub 仓库中更新。
我们还有其他来自我们丰富图书和视频目录的代码包可供在github.com/PacktPublishing/上获取。查看它们吧!
使用的约定
本书使用了多种文本约定。
CodeInText:表示文本中的代码单词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 昵称。例如;“将下载的WebStorm-10*.dmg磁盘映像文件作为系统中的另一个磁盘挂载。”
代码块设置如下:
from __future__ import print_function
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
当我们希望您注意代码块中的特定部分时,相关的行或项目将以粗体显示:
from __future__ import print_function
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
任何命令行输入或输出都应如下编写:
# cp /usr/src/asterisk-addons/configs/cdr_mysql.conf.sample
 /etc/asterisk/cdr_mysql.conf
粗体:表示新术语、重要单词或您在屏幕上看到的单词,例如在菜单或对话框中,这些单词在文本中也会以这种方式出现。例如:“从管理面板中选择系统信息。”
注意
警告或重要提示将以如下框的形式出现。
小贴士
小贴士和技巧看起来像这样。
联系我们
我们欢迎读者的反馈。
一般反馈:请通过feedback@packtpub.com发送电子邮件,并在邮件主题中提及书籍的标题。如果您对本书的任何方面有疑问,请通过questions@packtpub.com与我们联系。
勘误:尽管我们已经尽一切努力确保内容的准确性,但错误仍然可能发生。如果您在本书中发现错误,我们将不胜感激,如果您能向我们报告,请访问www.packtpub.com/submit-errata,选择您的书籍,点击勘误提交表单链接,并输入详细信息。
盗版:如果您在互联网上以任何形式遇到我们作品的非法副本,如果您能提供位置地址或网站名称,我们将不胜感激。请通过copyright@packtpub.com与我们联系,并附上材料的链接。
如果您有兴趣成为作者:如果您在某个领域有专业知识,并且您有兴趣撰写或为本书做出贡献,请访问authors.packtpub.com。
评论
请留下您的评价。一旦您阅读并使用了这本书,为何不在购买它的网站上留下评价呢?潜在读者可以查看并使用您的客观意见来做出购买决定,我们 Packt 公司可以了解您对我们产品的看法,并且我们的作者可以查看他们对书籍的反馈。谢谢!
如需了解更多关于 Packt 的信息,请访问packtpub.com。
第一章:信用风险建模
本书的所有章节都是实际应用。我们将每章开发一个应用。我们将了解应用,并选择合适的数据库以开发应用。在分析数据库后,我们将为特定应用构建基线方法。随后,我们将开发一个改进的方法,以解决基线方法的不足。最后,我们将了解如何使用适当的优化策略为给定应用开发最佳解决方案。在这个过程中,我们将学习有关机器学习技术的必要关键概念。我建议我的读者运行本书中给出的代码。这将帮助你真正理解这些概念。
在本章中,我们将探讨预测分析众多有趣应用之一。我选择了金融领域作为起点,我们将构建一个能够预测贷款违约的算法。这是金融领域最广泛使用的预测分析应用之一。在这里,我们将探讨如何为预测贷款违约开发一个最优解。我们将涵盖所有有助于我们构建此应用的因素。
本章我们将涵盖以下主题:
- 
介绍问题陈述 
- 
理解数据集 - 
理解数据集的特征 
- 
数据分析 
 
- 
- 
基线模型的特征工程 
- 
选择机器学习算法 
- 
训练基线模型 
- 
理解测试矩阵 
- 
测试基线模型 
- 
现有方法的问题 
- 
如何优化现有方法 - 
理解关键概念以优化方法 
- 
超参数调整 
 
- 
- 
实施改进的方法 - 
测试改进的方法 
- 
理解改进方法中的问题 
 
- 
- 
最佳方法 
- 
实施最佳方法 
- 
概述 
介绍问题陈述
首先,让我们尝试理解我们想要开发的应用或我们试图解决的问题。一旦我们理解了问题陈述及其用例,开发应用将变得容易得多。所以,让我们开始吧!
我们希望帮助金融机构,如银行、NBFS、贷款人等。我们将开发一个算法,可以预测金融机构应该向谁发放贷款或信用。现在你可能想知道这个算法的意义是什么?让我详细解释一下。当金融机构向客户贷款时,他们是在承担某种风险。所以,在贷款之前,金融机构会检查借款人未来是否有足够的钱来偿还贷款。基于客户的当前收入和支出,许多金融机构会进行某种分析,帮助他们决定借款人是否是那家银行的优质客户。这种分析是手动且耗时的。因此,需要某种自动化。如果我们开发一个算法,将帮助金融机构高效且有效地评估他们的客户。你的下一个问题可能是我们的算法的输出是什么?我们的算法将生成概率。这个概率值将表示借款人违约的可能性。违约意味着借款人在一定时间内无法偿还贷款。在这里,概率表示客户不能按时支付贷款 EMI 的可能性,从而导致违约。所以,较高的概率值表示客户可能是一个不良或不合适的借款人(客户),因为他们在接下来的两年内可能会违约。较低的概率值表示客户将是一个良好或合适的借款人(客户),在接下来的两年内不会违约。
在这里,我已经提供了关于问题陈述及其输出的信息,但这个算法的一个重要方面是:它的输入。所以,让我们来讨论我们的输入将是什么!
理解数据集
在这里,我们将讨论我们的输入数据集,以便开发应用程序。你可以在github.com/jalajthanaki/credit-risk-modelling/tree/master/data找到数据集。
让我们详细讨论数据集及其属性。在这里,在数据集中,你可以找到以下文件:
- 
cs-training.csv- 这个文件中的记录用于训练,因此这是我们训练数据集。
 
- 
cs-test.csv- 这个文件中的记录用于测试我们的机器学习模型,因此这是我们测试数据集。
 
- 
Data Dictionary.xls- 这个文件包含了关于数据集中每个属性的信息。因此,这个文件被称为我们的数据字典。
 
- 
sampleEntry.csv- 这个文件让我们了解我们需要为测试数据集生成最终输出的格式。如果你打开这个文件,你会看到我们需要生成测试数据集中每个记录的概率。这个概率值表示借款人违约的可能性。
 
理解数据集的属性
数据集有 11 个属性,如下所示:

图 1.1:数据集的属性(变量)
我们将逐个查看每个属性,并理解其在应用程序上下文中的含义:
- 
SeriousDlqin2yrs: - 
在数据集中,此特定属性表示借款人是否在过去两年内有过 90 天以上的逾期还款记录。 
- 
如果借款人在过去两年内有过超过 90 天的逾期还款记录,则此属性值为 Yes。如果借款人在 EMI 到期日 90 天后仍未支付 EMI,则此标志值是 Yes。 
- 
如果借款人在过去两年内没有超过 90 天的逾期还款记录,则此属性值为 No。如果借款人在 EMI 到期日 90 天前支付了 EMI,则此标志值是 No。 
- 
此属性具有目标标签。换句话说,我们将使用我们的算法来预测测试数据集中的此值。 
 
- 
- 
RevolvingUtilizationOfUnsecuredLines: - 
此属性表示在排除任何当前贷款债务和房地产后,借款人的信用卡额度。 
- 
假设我有一张信用卡,其信用额度为$1,000。在我的个人银行账户中,我有$1,000。我的信用卡余额是$500,总额度为$1,000。 
- 
因此,我通过信用卡和个人银行账户可以拥有的总最大余额是$1,000 + $1,000 = $2,000;我已经使用了信用卡额度的$500,所以我拥有的总余额是$500(信用卡余额)+ $1,000(个人银行账户余额)= $1,500。 
- 
如果账户持有人已经申请了住房贷款或其他财产贷款并支付了这些贷款的 EMI,则我们不考虑财产贷款的 EMI 价值。在这里,对于这个数据属性,我们考虑了账户持有人的信用卡余额和个人账户余额。 
- 
因此,循环未担保线使用率的价值是 = $1,500 / $2,000 = 0.7500 
 
- 
- 
Age: - 此属性值一目了然。它表示借款人的年龄。
 
- 
NumberOfTime30-59DaysPastDueNotWorse: - 此属性值的数量表示借款人支付 EMI 逾期但支付时间在到期日 30 天后或到期日前 59 天内的次数。
 
- 
DebtRatio: - 
这也是一个一目了然的属性,但我们将通过一个例子来尝试更好地理解它。 
- 
如果我的月度债务是$200,我的其他支出是$500,那么我每月支出$700。如果我的月收入是$1,000,那么债务比率的价值是$700/$1,000 = 0.7000 
 
- 
- 
MonthlyIncome: - 此属性包含借款人每月收入的价值。
 
- 
NumberOfOpenCreditLinesAndLoans: - 此属性表示借款人持有的开放贷款和/或信用卡的数量。
 
- 
NumberOfTimes90DaysLate: - 此属性表示借款人支付其 EMI 到期日 90 天后的欠款次数。
 
- 
NumberRealEstateLoansOrLines: - 此属性表示借款人持有的房地产贷款数量或借款人的住房贷款数量。
 
- 
NumberOfTime60-89DaysPastDueNotWorse: - 此属性表示借款人支付 EMI(等额本息)的次数,但支付时间是在到期日后的 60 天或到期日前的 89 天。
 
- 
NumberOfDependents: - 此属性也是不言自明的。它表示借款人拥有的依赖家庭成员数量。依赖人数不包括借款人本人。
 
这些是数据集的基本属性描述,因此您可以对我们所拥有的数据集类型有一个基本的概念。现在,是时候动手实践了。所以从下一节开始,我们将开始编写代码。我们将通过执行基本数据分析来探索我们的数据集,以便我们可以找出数据集的统计特性。
数据分析
本节分为两个主要部分。您可以参考以下图表,了解我们将如何处理本节:

图 1.2:数据分析的各个部分和步骤
在第一部分中,我们只有一个步骤。在前面的图中,这被称为步骤 1.1。在这个第一步中,我们将进行基本数据预处理。一旦完成,我们将开始下一部分。
第二部分有两个步骤。在图中,这被称为步骤 2.1。在这个步骤中,我们将使用统计和可视化技术进行基本数据分析,这将帮助我们理解数据。通过这个活动,我们将了解一些关于数据集的统计事实。之后,我们将跳到下一个步骤,这在图 1.2 中被称为步骤 2.2。在这个步骤中,我们将再次进行数据预处理,但这次我们的预处理将高度基于我们在给定训练数据集上进行基本数据分析后得出的发现。您可以在以下 GitHub 链接中找到代码:github.com/jalajthanaki/credit-risk-modelling/blob/master/basic_data_analysis.ipynb。
那么,让我们开始吧!
数据预处理
在本节中,我们将执行最小量的基本预处理。我们将探讨方法及其实现。
第一次更改
如果您打开cs-training.csv文件,您会发现其中有一列没有标题,因此我们将在那里添加一个标题。该属性的标题为ID。如果您想删除此列,您可以这样做,因为它只包含记录的sr.no。
第二次更改
这种更改不是强制性的。如果你想跳过它,你可以,但我个人喜欢执行这种预处理。这个更改与属性的标题有关,我们将从标题中移除"-"。除此之外,我还会将所有列标题转换为小写。例如,名为NumberOfTime60-89DaysPastDueNotWorse的属性将被转换为numberoftime6089dayspastduenotworse。这些类型的更改将有助于我们在进行深入数据分析时。在处理时我们不需要注意这些连字符符号。
实施更改
现在,你可能想知道我将如何执行所描述的更改? 好吧,有两种方法。一种是手动方法。在这种方法中,你将打开cs-training.csv文件并手动执行更改。这种方法当然不是很好。所以,我们将采取第二种方法。在第二种方法中,我们将使用 Python 代码执行更改。你可以在以下代码片段中找到所有更改。
参考以下截图以获取执行第一个更改的代码:

图 1.3:实现重命名或删除索引列的代码片段
对于第二个更改,你可以参考图 1.4:

图 1.4:删除列标题中的"-"并将所有列标题转换为小写的代码片段
同样的预处理需要在cs-test.csv文件上执行。这是因为给定的更改对训练集和测试集都是通用的。
你可以通过点击此链接在 GitHub 上找到整个代码:github.com/jalajthanaki/credit-risk-modelling/blob/master/basic_data_analysis.ipynb。
你也可以在阅读的同时动手操作。
我正在使用 Python 2.7 以及一系列不同的 Python 库来实现此代码。你可以在README部分找到有关 Python 依赖项以及安装的信息。现在让我们继续到基本数据分析部分。
基本数据分析随后是数据预处理
让我们进行一些基本数据分析,这将帮助我们找到训练数据集的统计特性。这种分析也称为探索性数据分析(EDA),它将帮助我们了解我们的数据集如何表示事实。在推导出一些事实之后,我们可以使用它们来进行特征工程。所以,让我们探索一些重要的事实!
从本节开始,所有代码都是 iPython 笔记本的一部分。你可以使用此 GitHub 链接引用代码:github.com/jalajthanaki/credit-risk-modelling/blob/master/Credit%20Risk%20Analysis.ipynb。
以下是我们将要执行的步骤:
- 
列出统计属性 
- 
查找缺失值 
- 
替换缺失值 
- 
相关性 
- 
检测异常值 
列出统计属性
在本节中,我们将了解训练数据集的统计属性。使用 pandas 的 describe 函数,我们可以找出以下基本事项:
- 
count:这将让我们了解训练数据集中记录的数量。
- 
mean:这个值给我们每个数据属性的均值指示。
- 
std:这个值表示每个数据属性的标准差。您可以参考以下示例:www.mathsisfun.com/data/standard-deviation.html。
- 
min:这个值让我们了解每个数据属性的最低值是多少。
- 
25%:这个值表示第 25 百分位数。它应该在 0 和 1 之间。
- 
50%:这个值表示第 50 百分位数。它应该在 0 和 1 之间。
- 
75%:这个值表示第 75 百分位数。它应该在 0 和 1 之间。
- 
max:这个值让我们了解每个数据属性的最高值是多少。
请查看以下图中的代码片段:

图 1.5:使用 pandas 的 describe 函数的基本统计属性
我们需要为我们的数据集找到一些其他的统计属性,这将帮助我们理解它。因此,在这里,我们将找到每个数据属性的均值和中位数。您可以在下面的图中看到查找中位数的代码:

图 1.6:生成每个数据属性的均值和中位数的代码片段
现在我们来检查我们的数据集中存在什么样的数据分布。我们绘制了目标属性seriousdlqin2yrs的频率分布,以便理解训练数据集中目标变量的整体分布。在这里,我们将使用seaborn可视化库。您可以通过以下代码片段进行参考:

图 1.7:理解目标变量分布以及分布可视化的代码片段
您可以参考以下图中的可视化图表:

图 1.8:目标数据属性变量分布的可视化
从这个图表中,您可以看到有许多带有目标标签0的记录,而带有目标标签1的记录较少。您可以看到带有0标签的数据记录大约占 93.32%,而 6.68%的数据记录被标记为1。我们将在接下来的章节中使用所有这些事实。现在,我们可以将我们的结果变量视为不平衡的。
查找缺失值
为了在数据集中找到缺失值,我们需要检查每个数据属性。首先,我们将尝试识别哪个属性有缺失或空值。一旦我们找到了数据属性的名字,我们将用更有意义的价值替换缺失值。有几种选项可用于替换缺失值。我们将探索所有这些可能性。
让我们编写第一步的代码。在这里,我们将查看哪些数据属性存在缺失值,以及每个具有缺失值的数据属性有多少条记录。您可以在下面的图中查看代码片段:

图 1.9:识别哪些数据属性有缺失值的代码片段
如前图所示,以下两个数据属性存在缺失值:
- 
monthlyincome:此属性包含 29,731 条记录,其中存在缺失值。
- 
numberofdependents:此属性包含 3,924 条记录,其中存在缺失值。
您还可以参考下面的图中的代码片段,以图形方式表示到目前为止描述的事实:

图 1.10:生成缺失值图表的代码片段
您可以在下面的图中查看图表本身:

图 1.11:缺失值的图形表示
在这种情况下,我们需要用更有意义的价值替换这些缺失值。我们可以使用各种标准技术来完成这项工作。我们有以下两种选项:
- 
用该特定数据属性的平均值替换缺失值 
- 
用该特定数据属性的中位数替换缺失值 
在上一节中,我们已经为所有数据属性推导出了平均值和中位数,我们将使用它们。在这里,我们的重点是标题为monthlyincome和numberofdependents的属性,因为它们有缺失值。我们已经确定了哪些数据属性有缺失值,现在是时候执行实际的替换操作了。在下一节中,您将看到我们如何用平均值或中位数替换缺失值。
替换缺失值
在上一节中,我们确定了训练数据集中哪些数据属性包含缺失值。我们需要用该特定数据属性的平均值或中位数来替换缺失值。因此,在本节中,我们将特别关注我们如何执行实际的替换操作。这种替换缺失值的操作也称为缺失数据插补。
在进入代码部分之前,我觉得你们可能会有一些问题,比如:我应该用平均值还是中位数替换缺失值? 还有其他选项吗? 让我逐一回答这些问题。
第一个问题的答案实际上将是一个试错法。所以你首先用平均值替换缺失值,并在模型训练过程中测量是否在训练数据集上得到好的结果。然后,在第二次迭代中,我们需要尝试用中位数替换值,并测量是否在训练数据集上得到好的结果。
为了回答第二个问题,有许多不同的插补技术可用,例如删除记录、使用 KNN 方法替换值、使用最频繁的值替换值等等。你可以选择这些技术中的任何一种,但你需要训练模型并测量结果。没有实施技术,你无法真正确定特定的插补技术是否适用于给定的训练数据集。在这里,我们谈论的是信用风险领域,所以我不太会深入理论,只是为了刷新你的概念,你可以参考以下文章:
- 
scikit-learn.org/stable/modules/generated/sklearn.preprocessing.Imputer.html
- 
www.analyticsvidhya.com/blog/2016/01/guide-data-exploration/
我们可以在以下图中看到使用属性的平均值和中位数替换缺失值的代码:

图 1.12:替换平均值代码片段
在前面的代码片段中,我们用平均值替换了缺失值,在第二步中,我们验证了所有缺失值都已用该特定数据属性的平均值替换。
在下一个代码片段中,你可以看到我们用来用这些数据属性的中位数替换缺失值的代码。请参考以下图:

图 1.13:用中位数替换缺失值的代码片段
在前面的代码片段中,我们已经用中位数替换了缺失值,在第二步中,我们已经验证了所有缺失值都已用该特定数据属性的中位数替换。
在第一次迭代中,我希望用中位数替换缺失值。
在下一节中,我们将看到基本数据分析的一个重要方面:寻找数据属性之间的相关性。所以,让我们从相关性开始吧。
相关系数
我希望您基本上知道在机器学习中相关性表示什么。术语相关性指的是数量之间的相互关系或关联。如果您想在这方面刷新概念,可以参考 www.investopedia.com/terms/c/correlation.asp。
因此,在这里,我们将找出不同数据属性之间存在的关联类型。有些属性高度依赖于一个或多个其他属性。有时,特定属性的值相对于其依赖属性增加,而有时特定属性的值相对于其依赖属性减少。因此,相关性表示数据属性之间的正负关联。您可以参考以下代码片段来了解相关性:

图 1.14:生成相关性的代码片段
您可以在以下图中看到表示相关性的图形表示的代码片段:

图 1.15:生成图形片段的代码片段
您可以在以下图中看到相关性的图表:

图 1.16:相关性热图
让我们看看前面的图表,因为它将极大地帮助您理解相关性。以下事实可以从图表中得出:
- 
值为 1.0 的单元格彼此高度相关。 
- 
每个属性与其自身都有非常高的相关性,因此所有对角线值都是 1.0。 
- 
数据属性 numberoftime3059dayspastduenotworse(参考垂直线或 y 轴上的数据属性)与两个属性高度相关,numberoftimes90dayslate 和 numberoftime6089dayspastduenotworse。这两个数据属性位于 x 轴(或水平线上)。 
- 
数据属性 numberoftimes90dayslate 与 numberoftime3059dayspastduenotworse 和 numberoftime6089dayspastduenotworse 高度相关。这两个数据属性位于 x 轴(或水平线上)。 
- 
数据属性 numberoftime6089dayspastduenotworse 与 numberoftime3059dayspastduenotworse 和 numberoftimes90dayslate 高度相关。这两个数据属性位于 x 轴(或水平线上)。 
- 
数据属性 numberofopencreditlinesandloans 也与 numberrealestateloansorlines 相关,反之亦然。在这里,数据属性 numberrealestateloansorlines 位于 x 轴(或水平线上)。 
在继续前进之前,我们需要检查这些属性是否包含任何异常值或不显著值。如果有的话,我们需要处理这些异常值,因此我们下一节将介绍如何从我们的训练数据集中检测异常值。
检测异常值
在本节中,你将学习如何检测异常值以及如何处理它们。本节涉及两个步骤:
- 
异常值检测技术 
- 
处理异常值 
首先,让我们从检测异常值开始。现在你们可能想知道为什么我们应该检测异常值。为了回答这个问题,我想给你们举一个例子。假设你有 5 岁孩子的体重。你测量了五个孩子的体重,并想找出平均体重。孩子们的体重分别是 15、12、13、10 和 35 公斤。现在如果你尝试找出这些值的平均值,你会看到答案是 17 公斤。如果你仔细观察体重范围,你会发现最后一个观测值与其他观测值相比超出了正常范围。现在让我们移除最后一个观测值(其值为 35)并重新计算其他观测值的平均值。新的平均值是 12.5 公斤。这个新值与最后一个平均值相比更有意义。因此,异常值对准确性有很大影响;因此,检测它们非常重要。一旦完成,我们将在名为处理异常值的下一节中探讨处理它们的技术。
异常值检测技术
在这里,我们使用了以下异常值检测技术:
- 
基于百分比的异常值检测 
- 
基于中位数绝对偏差(MAD)的异常值检测 
- 
基于标准差(STD)的异常值检测 
- 
基于多数投票的异常值检测 
- 
异常值的可视化 
基于百分比的异常值检测
在这里,我们使用了基于百分比的异常值检测,这是基于基本的统计理解推导出来的。我们假设我们应该考虑所有位于 2.5 到 97.5 百分比范围内的数据点。我们通过决定一个 95%的阈值来推导出百分比范围。你可以参考以下代码片段:

图 1.17:基于百分比的异常值检测代码片段
我们将使用这种方法对每个数据属性进行检测异常值。
基于中位数绝对偏差(MAD)的异常值检测
中位数绝对偏差(MAD)是一个非常简单的统计概念。它涉及四个步骤。这也被称为修改后的 Z 分数。步骤如下:
- 
找到特定数据属性的中位数。 
- 
对于数据属性给出的每个值,减去之前找到的中位数值。这个减法是以绝对值的形式进行的。因此,对于每个数据点,你将得到一个绝对值。 
- 
在第三步中,生成我们在第二步中得到的绝对值的平均值。我们将对每个数据点以及每个数据属性执行此操作。这个值被称为 MAD 值。 
- 
在第四步中,我们将使用以下公式来推导修改后的 Z 分数: ![基于中值绝对偏差(MAD)的异常值检测]() ![基于中值绝对偏差(MAD)的异常值检测]() ![基于中值绝对偏差(MAD)的异常值检测]() 
现在是时候参考以下代码片段了:

图 1.18:基于 MAD 的异常值检测代码片段
基于标准差(STD)的异常值检测
在本节中,我们将使用标准差和平均值来查找异常值。在这里,我们选择了一个随机的阈值值为 3。你可以参考以下代码片段:

图 1.19:基于标准差(STD)的异常值检测代码
基于多数投票的异常值检测:
在本节中,我们将构建投票机制,以便我们可以同时运行之前定义的所有方法——例如基于百分比的异常值检测、基于 MAD 的异常值检测和基于 STD 的异常值检测——并了解数据点是否应该被视为异常值。到目前为止,我们已经看到了三种技术。因此,如果两种技术指示数据应该被视为异常值,那么我们就将这个数据点视为异常值;否则,我们不将其视为异常值。因此,这里所需的最低投票数是两个。请参考以下图中的代码片段:

图 1.20:异常值检测的投票机制代码片段
异常值可视化
在本节中,我们将绘制数据属性以直观地了解异常值。同样,我们使用seaborn和matplotlib库来可视化异常值。你可以在以下图中找到代码片段:

图 1.21:异常值可视化代码片段
请参考前面的图来了解我们的定义方法如何检测异常值。在这里,我们选择了 5,000 个样本的大小。这个样本是随机选择的。

图 1.22:异常值检测的图表
在这里,你可以看到所有定义的技术如何帮助我们从一个特定的数据属性中检测异常数据点。你可以在以下 GitHub 链接上看到所有属性可视化图表:github.com/jalajthanaki/credit-risk-modelling/blob/master/Credit%20Risk%20Analysis.ipynb。
到目前为止,你已经学习了如何检测异常值,但现在你需要处理这些异常值点。在下一节中,我们将探讨如何处理异常值。
处理异常值
在本节中,您将学习如何删除或替换异常数据点。这一步骤尤为重要,因为如果您只是识别了异常值,但无法正确处理,那么在训练时,我们有很大的可能性会过度拟合模型。因此,让我们学习如何处理这个数据集的异常值。在这里,我将通过逐个查看数据属性来解释这个操作。
未担保线路的循环利用率
在这个数据属性中,当您绘制异常值检测图时,您将了解到大于 0.99999 的值被认为是异常值。因此,大于 0.99999 的值可以被替换为 0.99999。因此,对于这个数据属性,我们执行替换操作。我们为数据属性revolvingutilizationofunsecuredlines生成了新的值。
对于代码,您可以参考以下图表:

图 1.23:用 0.99999 替换异常值的代码片段
年龄
在这个属性中,如果您探索数据并查看基于百分比的异常值,那么您会看到有一个值为 0 的异常值,数据属性中最年轻的年龄是 21 岁。因此,我们将 0 的值替换为 22。我们编写了这样的条件,即年龄应该大于 22。如果不是,那么我们将年龄替换为 22。您可以参考以下代码和图表。
下图显示了数据集中年龄的频率分布。通过观察数据,我们可以得出结论,0 是异常值:

图 1.24:每个数据值的频率显示 0 是异常值
参考以下箱形图,它给出了年龄的分布指示:

图 1.25:年龄数据属性的箱形图
在删除异常值之前,我们得到了以下异常值检测图:

图 1.26:数据属性年龄检测异常的图形表示
替换异常值的代码如下:

图 1.27:用最小年龄值 21 替换异常值
在代码中,您可以看到我们已经检查了年龄列的每个数据点,如果年龄大于 21,则我们没有应用任何更改,但如果年龄小于 21,则我们用 21 替换了旧值。之后,我们将所有这些修改后的值放入我们的原始数据框中。
30-59 天逾期次数未恶化
在这个数据属性中,我们不仅探索了数据,还参考了异常值检测图。在这样做之后,我们知道 96 和 98 是我们的异常值。我们将这些值替换为平均值。您可以参考以下代码和图表来更好地理解这一点。
参考以下图表中的异常值检测图:

图 1.28:异常值检测图
参考下图中数据的频率分析:

图 1.29:频率计算中的异常值
用中位数替换异常值代码片段如下所示:

图 1.30:替换异常值的代码片段
负债比率
如果我们查看这个属性的异常值检测图,那么它有点令人困惑。请参考下图:

图 1.31:负债比率列的异常值检测图
为什么?这很令人困惑,因为我们不确定应该考虑哪种异常值检测方法。所以,在这里,我们只通过计算每种方法产生的异常值数量来进行一些比较分析。请参考下图:

图 1.32:各种异常值检测技术的比较
最大异常值检测是通过基于 MAD 的方法进行的,因此我们将考虑该方法。在这里,我们将找到最小上界值以替换异常值。最小上界是从异常值中得出的最小值。请参考以下代码片段:

图 1.33:最小上界代码
月收入
对于这个数据属性,我们将选择基于投票的异常值检测方法,如下图所示:

图 1.34:异常值检测图
为了替换异常值,我们将使用与“负债比率”数据属性相同的逻辑。我们通过生成一个最小上界值来替换异常值。你可以参考下图中给出的代码:

图 1.35:用最小上界值替换异常值
开放信用额度及贷款数量
如果你参考下图中给出的图表,你会看到在这个列中没有出现高度偏离的异常值:

图 1.36:异常值检测图
因此,我们将不对这个数据属性执行任何替换操作。
90 天逾期次数
对于这个属性,当你分析数据值频率时,你会立即看到 96 和 98 是异常值。我们将用数据属性的中位数替换这些值。
参考下图中频率分析的代码片段:

图 1.37:数据点的频率分析
异常值替换代码片段如下所示:

图 1.38:使用中位数替换异常值
房地产贷款或信用额度数量
当我们看到数据属性中值的频率时,我们会知道超过 17 的频率值太低。所以,在这里,我们将每个小于 17 的值替换为 17。
你可以参考以下图中的代码片段:

图 1.39:替换异常值代码片段
60-89 天逾期次数未变差
对于这个属性,当你分析数据值频率时,你会立即看到 96 和 98 是异常值。我们将用数据属性的均值替换这些值。
参考以下图中的频率分析代码片段:

图 1.40:数据频率分析
异常值替换代码片段如下所示:

图 1.41:使用中位数替换异常值的代码片段
你可以参考图 1.38中的removeSpecificAndPutMedian方法代码。
受抚养人数
对于这个属性,当你看到数据点的频率值时,你会立即看到大于 10 的数据值是异常值。我们将大于 10 的值替换为 10。
参考以下图中的代码片段:

图 1.42:替换异常值代码片段
这就结束了异常值部分。在本节中,我们以更有意义的方式替换了数据点的值。我们也到达了基本数据分析部分的终点。这次分析让我们对数据集及其值有了很好的理解。下一节全是关于特征工程。所以,我们首先从基础知识开始,然后在本章的后面,你将学习到特征工程如何以积极的方式影响算法的准确性。
基线模型的特征工程
在本节中,你将学习如何选择重要的特征来开发预测模型。所以现在,首先,我们不会过多关注在这一阶段推导新的特征,因为首先,我们需要知道哪些输入变量/列/数据属性/特征至少能给我们提供基线准确率。因此,在这个第一次迭代中,我们的重点是选择可用的训练数据集中的特征。
确定特征重要性
我们需要知道哪些是重要的特征。为了找出这些特征,我们将使用随机森林分类器来训练模型。之后,我们将对我们来说重要的特征有一个大致的了解。所以让我们直接进入代码。你可以参考以下图中的代码片段:

图 1.43:推导特征的重要性
在此代码中,我们使用 scikit-learn 的 RandomForest 分类器。我们使用fit()函数进行训练,然后,为了生成特征的重要性,我们将使用 scikit-learn 库中可用的feature_importances_ 函数。然后,我们将按重要性值从高到低打印出特征。
让我们绘制一个图表,以便更好地理解最重要的特征。您可以在以下图中找到代码片段:

图 1.44:生成特征重要性图的代码片段
在此代码片段中,我们使用 matplotlib 库来绘制图表。在这里,我们使用条形图并输入所有数据属性及其重要性值,这些值是我们之前推导出来的。您可以参考以下图中的图表:

图 1.45:特征重要性图
在第一次迭代中,我们在特征工程方面做了一些相当多的工作。我们将在接下来的章节中重新审视特征工程。现在,是时候实现机器学习算法来生成基线预测模型了,这将让我们了解一个人在接下来的两年内是否会违约。所以,让我们跳到下一节。
选择机器学习算法
这一节是最重要的。在这里,我们将尝试几种不同的机器学习算法,以便了解哪种机器学习算法表现更好。我们还将进行训练准确度比较。
到这时,你肯定知道这个特定问题被认为是分类问题。我们将选择的算法如下(这个选择基于直觉):
- 
K-Nearest Neighbor (KNN) 
- 
逻辑回归 
- 
AdaBoost 
- 
GradientBoosting 
- 
RandomForest 
我们的第一步是生成特定格式的训练数据。我们将把训练数据集分成训练集和测试集。所以,基本上,我们正在为训练准备输入。这对于所有机器学习算法都是常见的。请参考以下图中的代码片段:

图 1.46:生成用于训练的关键值格式训练数据集的代码片段
如代码所示,变量 x 包含除了名为seriousdlqin2yrs的目标列之外的所有列,因此我们已删除此列。删除此属性的原因是,该属性包含每行的答案/目标/标签。机器学习算法需要以键值对的形式输入,因此目标列是键,所有其他列是值。我们可以这样说,某种值模式将导致特定的目标值,这是我们需要使用机器学习算法进行预测的。
在这里,我们也分割了训练数据。我们将使用 75%的训练数据用于实际训练目的,一旦训练完成,我们将使用剩余的 25%训练数据来检查我们训练的 ML 模型的训练准确度。所以,我们不浪费任何时间,将直接跳到 ML 算法的编码,随着我们的前进,我会解释代码。注意,在这里,我不会深入到每个 ML 算法的数学解释,但我将解释代码。
K-Nearest Neighbor (KNN)
在这个算法中,通常我们的输出预测遵循其邻居的相同趋势。K 是我们将要考虑的邻居数量。如果 K=3,那么在预测输出时,检查三个最近的邻居点,如果一个邻居属于X类别,而两个邻居属于Y类别,那么预测标签将是Y,因为大多数最近的点属于Y类别。
让我们看看我们编写了什么。参考以下图:

图 1.47:定义 KNN 分类器的代码片段
让我们逐个了解这些参数:
- 
根据代码,K=5 意味着我们的预测基于五个最近的邻居。在这里, n_neighbors=5。
- 
权重均匀选择,这意味着每个邻域中的所有点都被同等加权。在这里,weights='uniform'。 
- 
algorithm='auto': 此参数将尝试根据我们传递的值决定最合适的算法。 
- 
leaf_size = 30: 此参数影响模型构建和查询的速度。在这里,我们使用了默认值,即 30。 
- 
p=2: 这表示 Minkowski 度量的幂参数。在这里,p=2 使用 euclidean_distance。
- 
metric='minkowski': 这是默认的距离度量,它帮助我们构建树。 
- 
metric_params=None: 这是我们在使用的默认值。 
逻辑回归
逻辑回归是最广泛使用的 ML 算法之一,也是最古老的算法之一。该算法使用 sigmoid 和其他非线性函数生成目标变量的概率,以预测目标标签。
让我们参考代码和用于逻辑回归的参数。您可以参考以下图中的代码片段:

图 1.48:逻辑回归 ML 算法的代码片段
让我们逐个了解这些参数:
- 
penalty='l1': 此参数表示梯度下降算法的选择。在这里,我们选择了牛顿-共轭梯度方法。 
- 
dual=False: 如果样本数量大于特征数量,我们应该将此参数设置为 false。 
- 
tol=0.0001: 这是算法的一个停止标准。 
- 
c=1.0: 此值表示正则化强度的倒数。此参数必须是一个正浮点值。 
- 
fit_intercept = True:这是此算法的默认值。此参数用于指示算法的偏差。 
- 
solver='liblinear':此算法对小型数据集表现良好,因此我们选择了它。 
- 
intercept_scaling=1:如果我们选择 liblinear 算法并且 fit_intercept=True,那么这个参数帮助我们生成特征权重。 
- 
class_weight=None:没有与类标签关联的权重。 
- 
random_state=None:在这里,我们使用此参数的默认值。 
- 
max_iter=100:在这里,我们迭代 100 次,以便将我们的 ML 算法收敛到给定的数据集。 
- 
multi_class='ovr':此参数表示给定问题是二元分类问题。 
- 
verbose=2:如果我们使用 liblinear 作为求解器参数,那么我们需要为冗余性输入一个正数。 
AdaBoost
AdaBoost 算法代表自适应提升。提升是一种集成方法,其中我们将使用多个弱分类器来构建强分类器。AdaBoost 是一种针对二元分类问题给出良好结果的提升算法。如果您想了解更多关于它的信息,请参阅这篇文章machinelearningmastery.com/boosting-and-adaboost-for-machine-learning/。
此特定算法有 N 次迭代。在第一次迭代中,我们从训练数据集中随机选择数据点并构建模型。每次迭代后,算法都会检查分类器表现不佳的数据点。一旦算法根据误差率识别出这些数据点,权重分布就会更新。因此,在下一轮迭代中,算法有更大的机会选择之前分类不佳的数据点并学习如何分类它们。这个过程会持续进行,直到达到您提供的迭代次数。
让我们参考以下图中给出的代码片段:

图 1.49:AdaBoost 分类器的代码片段
参数相关描述如下:
- 
base_estimator = None:这是构建提升集成的基础估计器。 
- 
n_estimators=200:这是提升终止的最大估计器数量。在 200 次迭代后,算法将终止。 
- 
learning_rate=1.0:此速率决定了我们的模型收敛的速度。 
GradientBoosting
此算法也是 ML 算法集成的一部分。在这个算法中,我们使用基本的回归算法来训练模型。训练后,我们将计算误差率以及找到算法表现不佳的数据点,在下一轮迭代中,我们将重新训练模型以更好地预测这些数据点。该算法使用已生成的模型以及新生成的模型来预测数据点的值。
你可以在下面的图中看到代码片段:

图 1.50:Gradient Boosting 分类器的代码片段
让我们来看看分类器的参数:
- 
loss='deviance': 这意味着我们正在使用具有概率输出的逻辑回归进行分类。 
- 
learning_rate = 0.1: 此参数告诉我们模型需要多快收敛。 
- 
n_estimators = 200: 此参数表示需要执行的提升阶段的数量。 
- 
subsample = 1.0: 此参数有助于调整偏差和方差。选择 subsample < 1.0 会导致方差减少和偏差增加。 
- 
min_sample_split=2: 这是分割内部节点所需的最小样本数。 
- 
min_weight_fraction_leaf=0.0: 样本具有相等的权重,因此我们提供了值 0。 
- 
max_depth=3: 这表示单个回归估计器的最大深度。最大深度限制了树中的节点数。 
- 
init=None: 对于此参数,loss.init_estimator 用于初始预测。 
- 
random_state=None: 此参数表示随机状态是通过 numpy.random函数生成的。
- 
max_features=None: 此参数表示我们有 N 个特征。因此, max_features=n_features。
- 
verbose=0: 没有打印进度。 
RandomForest
这个特定的机器学习算法生成决策树的数量,并使用投票机制来预测目标标签。在这个算法中,生成了多个决策树,形成了一个树丛,因此被称为 RandomForest。
在下面的代码片段中,注意我们如何声明了 RandomForest 分类器:

图 1.51:Random Forest 分类器的代码片段
让我们了解这里的参数:
- 
n_estimators=10: 这表示森林中的树的数量。
- 
criterion='gini': 信息增益将通过 gini 计算。
- 
max_depth=None: 此参数表示节点会扩展,直到所有叶子节点都是纯的,或者直到所有叶子节点包含少于 min_samples_split 个样本。
- 
min_samples_split=2: 此参数表示执行分割以生成树所需的最小样本数至少为两个。
- 
min_samples_leaf=1: 这表示叶子节点的样本大小。
- 
min_weight_fraction_leaf=0.0: 此参数表示达到叶子节点所需的最小加权分数总和(所有输入样本的权重总和)。在这里,权重是均匀分布的,因此样本权重为零。
- 
max_features='auto': 此参数被认为是使用自动策略。我们选择自动值,然后选择 max_features=sqrt(n_features)。
- 
max_leaf_nodes=None: 此参数表示可以有无限数量的叶子节点。
- 
bootstrap=True: 此参数表示在构建树时使用自助样本。
- 
oob_score=False:此参数表示是否使用袋外样本来估计泛化精度。在这里我们不考虑袋外样本。
- 
n_jobs=1:如果n_job = 1,则 fit 和 predict 作业可以并行运行。
- 
random_state=None:此参数表示随机状态是通过numpy.random函数生成的。
- 
verbose=0:这控制了树构建过程的详细程度。0 表示我们不打印进度。
到目前为止,我们已经看到了我们如何声明我们的机器学习算法。我们也已经定义了一些参数值。现在,是时候在训练数据集上训练这个机器学习算法了。让我们来讨论一下。
训练基线模型
在本节中,我们将使用以下机器学习算法进行实际训练。这一步很耗时,因为它需要更多的计算能力。我们使用训练数据集的 75%进行实际训练,并使用数据集的 25%进行测试,以便衡量训练精度。
你可以在下面的图中找到代码片段:

图 1.52:执行训练的代码片段
在前面的代码片段中,你可以看到我们使用 scikit-learn 库中的fit()函数执行了实际训练操作。此函数使用给定的参数,通过获取目标数据属性和其他特征列的输入来训练模型。
一旦完成这一步,你就会看到我们的不同机器学习算法生成了不同的训练模型。现在,是时候检查我们的训练模型在预测方面的表现了。对于 25%的数据集,我们可以使用某些技术。在下一节中,我们将了解这些技术。
理解测试矩阵
在本节中,我们将查看一些广泛使用的测试矩阵,我们可以使用这些矩阵来了解我们的训练模型是好是坏。这个测试分数让我们对哪个模型在预测 25%的数据时达到最高精度有一个公平的了解。
这里,我们使用两个基本的测试矩阵级别:
- 
训练模型的平均精度 
- 
ROC-AUC 分数 
训练模型的平均精度
在本节中,我们将了解当使用 scikit-learn 函数score()生成训练精度时,scikit-learn 是如何计算准确度分数的。函数 score()返回平均精度。更确切地说,它使用残差标准误差。残差标准误差不过是均方误差的正平方根。在这里,计算精度的方程如下:

最佳可能分数是 1.0,模型也可以有负分数(因为模型可能非常糟糕)。如果一个常数模型总是预测 y 的预期值,而不考虑输入特征,它将得到 0.0 的残差标准误差分数。
ROC-AUC 分数
ROC-AUC 分数用于确定分类器的准确性。ROC 和 AUC 是两个不同的术语。让我们逐一了解每个术语。
ROC
ROC 代表接收者操作特征。它是一种曲线。我们绘制 ROC 曲线来可视化二元分类器的性能。既然我已经提到 ROC 是一条曲线,你可能想知道它是什么类型的曲线,对吧?ROC 曲线是一个二维曲线。它的x轴代表假阳性率(FPR),而它的y轴代表真阳性率(TPR)。TPR 也称为灵敏度,FPR 也称为特异性(SPC)。你可以参考以下方程式了解 FPR 和 TPR。
TPR = 真阳性 / 正样本数量 = TP / P
FPR = 假阳性 / 负样本数量 = FP / N = 1 - SPC
对于任何二元分类器,如果预测概率≥0.5,则它将获得类别标签 X,如果预测概率<0.5,则它将获得类别标签 Y。这在大多数二元分类器中是默认的。预测概率的这个截止值被称为预测的阈值值。对于所有可能的阈值值,已经计算了 FPR 和 TPR。这对 FPR 和 TPR 是我们的一个 x,y 值对。因此,对于所有可能的阈值值,我们得到 x,y 值对,当我们把这些点放在 ROC 图上时,它将生成 ROC 曲线。如果你的分类器完美地分离了两个类别,那么 ROC 曲线将紧贴图的右上角。如果分类器的性能基于某种随机性,那么 ROC 曲线将更接近 ROC 曲线的对角线。参考以下图示:

图 1.53:不同分类分数的 ROC 曲线
在前面的图中,最左侧的 ROC 曲线对应的是完美分类器。中间的图表显示了在现实世界问题中具有更好准确性的分类器。非常随机的猜测分类器在最右侧的图表中显示。当我们绘制 ROC 曲线时,如何量化它呢?为了回答这个问题,我们将介绍 AUC。
AUC
AUC 代表曲线下的面积。为了量化 ROC 曲线,我们使用 AUC。在这里,我们将看到 ROC 曲线覆盖了多少面积。如果我们获得一个完美的分类器,那么 AUC 分数是 1.0,如果我们有一个在猜测中随机的分类器,那么 AUC 分数是 0.5。在现实世界中,我们不会期望 AUC 分数达到 1.0,但如果分类器的 AUC 分数在 0.6 到 0.9 的范围内,那么它将被认为是好的分类器。你可以参考以下图示:

图 1.54:ROC 曲线的 AUC
在前面的图中,你可以看到曲线下覆盖了多少面积,这成为我们的 AUC 分数。这为我们提供了关于我们的分类器表现好坏的指示。
这些是我们将要使用的两个矩阵。在下一节中,我们将实现实际的代码测试,并查看我们训练的机器学习模型的测试矩阵。
测试基线模型
在本节中,我们将实现代码,这将让我们了解我们的训练机器学习模型在验证集上的表现是好是坏。我们使用平均准确度分数和 AUC-ROC 分数。
在这里,我们生成了五个不同的分类器,并在验证数据集上对每个分类器进行了测试,验证数据集是从训练数据集中保留的 25%的数据集,我们将找出哪个机器学习模型表现良好,并给出一个合理的基线分数。所以让我们看看代码:。

图 1.55:获取训练机器学习模型测试分数的代码片段
在前面的代码片段中,您可以看到三个分类器的分数。
参考以下图中的代码片段:

图 1.56:获取训练机器学习模型测试分数的代码片段
在代码片段中,您可以看到两个分类器的分数。
使用 scikit-learn 的score()函数,您将获得平均准确度分数,而roc_auc_score()函数将为您提供 ROC-AUC 分数,这对我们来说更为重要,因为平均准确度分数只考虑了一个阈值值,而 ROC-AUC 分数考虑了所有可能的阈值值,并给出了分数。
如您在上述代码片段中看到的,AdaBoost 和 GradientBoosting 分类器在验证数据集上获得了良好的 ROC-AUC 分数。其他分类器,如逻辑回归、KNN 和 RandomForest 在验证集上表现不佳。从这一阶段开始,我们将使用 AdaBoost 和 GradientBoosting 分类器来提高它们的准确度分数。
在下一节中,我们将了解为了提高分类准确度我们需要做什么。我们需要列出可以做什么来获得良好的准确度,以及当前分类器中存在的问题。所以让我们分析现有分类器的问题并看看它们的解决方案。
现有方法的缺点
我们使用 AdaBoost 和 GradientBoosting 分类器获得了基线分数。现在,我们需要提高这些分类器的准确度。为了做到这一点,我们首先列出所有可以改进但尚未充分工作的领域。我们还需要列出基线方法中可能存在的问题。一旦我们有了需要工作的问题或领域的列表,我们将很容易实现改进的方法。
在这里,我列出了一些我们基线迭代中没有工作的领域或问题:
- 
问题:我们没有广泛使用交叉验证技术来检查过拟合问题。 - 解决方案:如果我们正确使用交叉验证技术,那么我们将知道我们的训练机器学习模型是否受到过拟合的影响。这将帮助我们,因为我们不希望构建一个连泛化都做不到很好的模型。
 
- 
问题:我们还没有关注超参数调整。在我们的基线方法中,我们主要使用默认参数。我们在声明分类器时定义了这些参数。你可以参考图 1.52中给出的代码片段,在那里你可以看到分类器在训练模型时使用了一些参数。我们还没有改变这些参数。 - 解决方案:我们需要以这种方式调整这些超参数,以便我们可以提高分类器的准确率。我们需要使用各种超参数调整技术。
 
在下一节中,我们将探讨这些优化技术是如何实际工作的,以及讨论我们将采取的方法。所以,让我们开始吧!
优化现有方法
在本节中,我们将了解关于交叉验证和超参数调整的基本技术性内容。一旦我们了解了基础知识,实施它们对我们来说将会非常容易。让我们从对交叉验证和超参数调整的基本理解开始。
理解关键概念以优化方法
在这个修订的迭代中,我们需要提高分类器的准确率。在这里,我们将首先介绍基本概念,然后进入实现部分。因此,我们将了解两个有用的概念:
- 
交叉验证 
- 
超参数调整 
交叉验证
交叉验证也被称为旋转估计。它基本上用于跟踪一个称为过拟合的问题。让我先从过拟合问题开始,因为使用交叉验证的主要目的是为了避免过拟合的情况。
基本上,当你使用训练数据集训练模型并检查其准确率时,你会发现你的训练准确率相当不错,但当你在尚未见过的数据集上应用这个训练好的模型时,你会意识到训练好的模型在未见过的数据集上表现不佳,只是在目标标签方面模仿了训练数据集的输出。因此,我们可以说我们的训练模型无法正确泛化。这个问题被称为过拟合,为了解决这个问题,我们需要使用交叉验证。
在我们的基线方法中,我们没有广泛使用交叉验证技术。好处是,到目前为止,我们已经生成了 25%的训练数据集的验证集,并在其上测量了分类器的准确率。这是一种基本技术,用于了解分类器是否受到过拟合的影响。
有许多其他的交叉验证技术可以帮助我们完成两件事:
- 
使用 CV 跟踪过拟合情况:这将给我们一个关于过拟合问题的完美想法。我们将使用 K 折交叉验证。 
- 
使用 CV 进行模型选择:交叉验证将帮助我们选择分类模型。这也会使用 K 折交叉验证。 
现在,让我们看看这两个任务将使用的单一方法。你会发现实现起来很容易理解。
使用 CV 的方法
scikit-learn 库提供了出色的交叉验证实现。如果我们想实现交叉验证,我们只需要导入交叉验证模块。为了提高准确度,我们将使用 K 折交叉验证。这里基本解释了 K 折交叉验证是如何工作的。
当我们使用训练-测试分割时,我们将使用 75%的数据进行模型训练,并使用 25%的数据进行模型验证。这种方法的主要问题是,实际上,我们没有使用整个训练数据集进行训练。因此,我们的模型可能无法遇到训练数据集中存在的所有情况。K 折交叉验证解决了这个问题。
在 K 折 CV 中,我们需要为 K 提供一个正整数。在这里,你将训练数据集分成 K 个子数据集。让我给你举个例子。如果你在训练数据集中有 125 条数据记录,并且将值设置为 k=5,那么每个数据子集将得到 25 条数据记录。因此,我们现在有五个训练数据子集,每个子集有 25 条记录。
让我们了解这些数据集的五个子集是如何被使用的。根据提供的 K 值,将决定我们需要迭代这些数据子集的次数。在这里,我们取 K=5。因此,我们迭代数据集 K-1=5-1=4 次。请注意,K 折 CV 中的迭代次数通过方程 K-1 计算。现在让我们看看每次迭代会发生什么:
- 
第一次迭代:我们取一个子集用于测试,剩余的四个子集用于训练。 
- 
第二次迭代:我们取两个子集用于测试,剩余的三个子集用于训练。 
- 
第三次迭代:我们取三个子集用于测试,剩余的两个子集用于训练。 
- 
第四次迭代:我们取四个子集用于测试,剩余的子集用于训练。在第四次迭代之后,我们没有剩余的子集用于训练或测试,所以我们停止在 K-1 次迭代后。 
这种方法有以下优点:
K 折 CV 使用所有数据点进行训练,因此我们的模型可以利用所有数据点进行训练。
- 
在每次迭代后,我们都会得到准确度分数。这将帮助我们决定模型的性能。 
- 
我们通常在所有迭代完成后考虑交叉验证的均值和标准差值。对于每一次迭代,我们跟踪准确度分数,一旦所有迭代完成,我们就取准确度分数的均值,并从准确度分数中推导出标准差(std)值。这个交叉验证的均值和标准差分数将帮助我们判断模型是否出现过拟合。 
- 
如果你为多个算法执行此过程,那么基于这个平均分数和标准分数,你也可以决定哪个算法最适合给定的数据集。 
这种方法的缺点如下:
- 这种 k 折交叉验证是一种耗时且计算成本高昂的方法。
所以在阅读完这篇文章后,你希望理解这种方法,并且通过使用这个实现,我们可以确定我们的模型是否出现过拟合。这种技术还将帮助我们选择机器学习算法。我们将在实现修订方法的部分查看这一实现的细节。
现在我们来了解一下下一个优化技术,即超参数调整。
超参数调整
在本节中,我们将探讨如何使用超参数调整技术来优化我们模型的准确度。有一些参数在训练过程中无法学习到它们的值。这些参数表达了机器学习模型的高级属性。这些高级参数被称为超参数。这些是机器学习模型的调整旋钮。我们可以通过试错法获得超参数的最佳值。你可以通过使用此链接了解更多信息:machinelearningmastery.com/difference-between-a-parameter-and-a-hyperparameter/。如果我们找到了超参数的最优值,那么我们将能够实现我们模型的最佳准确度,但挑战在于我们并不知道这些参数的确切值。这些参数是算法的调整旋钮。因此,我们需要应用一些技术,以获得最佳可能的超参数值,这样我们就可以在训练时使用它。
在 scikit-learn 中,有两个函数我们可以用来找到这些超参数值,如下所示:
- 
网格搜索参数调整 
- 
随机搜索参数调整 
网格搜索参数调整
在本节中,我们将探讨网格搜索参数调整的工作原理。我们在一个名为网格的列表中指定参数值。网格中指定的每个值在参数调整过程中都被考虑过。模型是基于指定的网格值构建和评估的。这种技术全面考虑了所有参数组合,并生成了最终的优化参数。
假设我们想要优化五个参数。使用这种技术,如果我们想为每个参数尝试 10 个不同的值,那么总共需要进行 10⁵ 次评估。假设平均来说,每个参数组合的训练需要 10 分钟;那么,对于 10⁵ 次的评估,可能需要数年。听起来疯狂,对吧?这是这种技术的主要缺点。这种技术非常耗时。因此,一个更好的解决方案是随机搜索。'
随机搜索参数调整
直观的想法与网格搜索相同,但主要区别在于我们不会尝试所有可能的组合,而是只从选定的网格子集中随机选择参数。如果我想在我的前一个例子中添加内容,那么在随机搜索中,我们将从 105 个参数值中随机选择一个子集值。假设我们从 105 个值中只取 1,000 个值,并尝试为我们的超参数生成最佳值。这样,我们将节省时间。
在改进的方法中,我们将使用这项特定的技术来优化超参数。
从下一节开始,我们将看到 K 折交叉验证和超参数调整的实际实现。所以,让我们开始实现我们的方法。
实现改进的方法
在本节中,我们将看到我们改进方法的实际实现,这个改进的方法将使用 K 折交叉验证和超参数优化。我将实现部分分为两个部分,这样您在看到代码时可以连接这些点。两个实现部分如下:
- 
实现基于交叉验证的方法 
- 
实现超参数调整 
实现基于交叉验证的方法
在本节中,我们将看到 K 折交叉验证的实际实现。在这里,我们使用 scikit-learn 交叉验证评分模块。因此,我们需要选择 K 折的值。默认情况下,值为 3。我使用 K = 5 的值。您可以参考以下图中的代码片段:

图 1.57:K 折交叉验证实现的代码片段
如前图所示,我们获得cvScore.mean()和cvScore.std()评分来评估我们的模型性能。请注意,我们已经考虑了整个训练数据集。因此,这些参数的值是X_train = X和y_train = y。在这里,我们定义了cvDictGen函数,该函数将跟踪准确率的平均值和标准差。我们还实现了cvDictNormalize函数,如果我们想获得归一化的平均值和标准差(std)评分,我们可以使用它。目前,我们不会使用cvDictNormalize函数。
现在是运行cvDictGen方法的时候了。您可以在下面的图中看到输出:

图 1.58:K 折交叉验证输出的代码片段
我们对五种不同的机器学习算法进行了交叉验证,以检查哪种机器学习算法表现良好。如图所示,在我们的输出中,GradientBoosting 和 Adaboot 分类器表现良好。我们使用了交叉验证分数来决定我们应该选择哪种机器学习算法,以及哪些算法不应该选择。除此之外,基于平均值和标准差,我们可以得出结论,我们的 ROC-AUC 分数没有太大偏差,因此我们没有过度拟合的问题。
现在是时候查看超参数调优的实现。
实现超参数调优
在本节中,我们将探讨如何获取超参数的最优值。在这里,我们使用了RandomizedSearchCV超参数调优方法。我们已经为 AdaBoost 和 GradientBoosting 算法实现了这种方法。您可以在以下图中看到 Adaboost 算法的超参数调优实现:

图 1.59:Adaboost 算法超参数调优的代码片段
在对给定参数值运行RandomizedSearchCV方法后,它将生成最优参数值。如图所示,我们希望得到参数的最优值;n_estimators.RandomizedSearchCV获取n_estimators的最优值,为 100。
您可以在以下图中看到 GradientBoosting 算法的超参数调优实现:

图 1.60:GradientBoosting 算法超参数调优的代码片段
如前图所示,RandomizedSearchCV方法获取了以下超参数的最优值:
- 
'loss': 'deviance' 
- 
'max_depth': 2 
- 
'n_estimators': 449 
现在是时候测试我们修订的方法了。让我们看看我们将如何测试模型以及测试的结果将如何。
实现和测试修订的方法
在这里,我们需要插入超参数的最优值,然后我们将查看验证数据集上的 ROC-AUC 分数,以便我们知道分类器的准确性是否会有所提高。
您可以通过以下图来查看实现以及如何使用最佳超参数进行训练:

图 1.61:使用最优超参数值进行训练的代码片段
一旦完成训练,我们可以使用训练好的模型来预测验证数据集的目标标签。之后,我们可以获得 ROC-AUC 分数,这让我们了解我们能够优化分类器准确度多少。这个分数也有助于验证我们的方向,所以如果我们不能提高分类器的准确度,那么我们可以识别问题并在下一次迭代中提高准确度。您可以在以下图中看到 ROC-AUC 分数:

图 1.62:修订方法的 ROC-AUC 分数代码片段
如您在输出中看到的,经过超参数调整后,与我们的基线方法相比,ROC-AUC 分数有所提高。在我们的基线方法中,AdaBoost 的 ROC-AUC 分数为 0.85348539,而经过超参数调整后,为 0.86572352。在我们的基线方法中,GradientBoosting 的 ROC-AUC 分数为 0.85994964,而经过超参数调整后,为 0.86999235。这些分数表明我们正在朝着正确的方向前进。
问题是:我们能否进一步提高分类器的准确度? 当然,总有改进的空间,所以我们将遵循相同的方法。我们列出所有尚未触及的可能问题或领域。我们试图探索它们,并生成最佳可能的方法,以便在验证数据集以及测试数据集上获得良好的准确度。
那么,让我们看看在这个修订方法中我们未触及的领域是什么。
理解修订方法中的问题
在修订方法之前,我们没有在特征工程上花费太多时间。因此,在我们的最佳可能方法中,我们花费时间在特征工程的转换上。我们需要实现一个投票机制,以便在真实测试数据集上生成预测的最终概率,这样我们就可以获得最佳的准确度分数。
这是我们需要应用的两个技术:
- 
特征转换 
- 
具有投票机制的集成机器学习模型 
一旦我们实现了这些技术,我们将检查验证数据集上的 ROC-AUC 分数。然后,我们将为真实测试数据集中的每个记录生成一个概率分数。让我们从实现开始。
最佳方法
如前所述,在本迭代中,我们将重点关注特征转换以及实现一个使用 AdaBoost 和 GradientBoosting 分类器的投票分类器。希望使用这种方法,我们将在验证数据集以及真实测试数据集上获得最佳的 ROC-AUC 分数。这是生成最佳结果的最佳可能方法。如果您有任何创新解决方案,您也可以尝试它们。现在我们将跳到实现部分。
实施最佳方法
在这里,我们将实现以下技术:
- 
特征的对数转换 
- 
基于投票的集成模型 
让我们先实现特征变换。
特征的对数变换
我们将对我们的训练数据集应用对数变换。这样做的原因是我们有一些非常倾斜的属性和一些数据属性,其值在本质上更分散。因此,我们将对输入特征数组加一的自然对数进行取对数。您可以参考以下图中所示的代码片段:

图 1.63:特征对数(p+1)变换的代码片段。
我还在验证数据集上测试了 ROC-AUC 准确性,这给我们带来了轻微的准确度变化。
基于投票的集成机器学习模型
在本节中,我们将使用基于投票的集成分类器。scikit-learn 库已经有一个模块可供使用。因此,我们为未变换的特征以及变换后的特征都实现了一个基于投票的机器学习模型。让我们看看哪个版本在验证数据集上的得分更高。您可以参考以下图中给出的代码片段:

图 1.64:基于投票的集成分类器的代码片段
在这里,我们使用了两个参数:梯度提升的权重为 2,AdaBoost 算法的权重为 1。我还将投票参数设置为 soft,以便分类器可以更加协作。
我们几乎完成了使用投票机制尝试最佳方法的工作。在下一节中,我们将在真实测试数据集上运行我们的机器学习模型。所以,让我们做一些真正的测试!
在真实测试数据上运行机器学习模型
在这里,我们将测试基于投票的机器学习模型在测试数据集上的准确性。在第一次迭代中,我们不会对测试数据集进行对数变换,而在第二次迭代中,我们将对测试数据集进行对数变换。在这两种情况下,我们将生成目标类的概率。在这里,我们生成概率是因为我们想知道在接下来的两年内,特定个人违约贷款的可能性有多大。我们将把预测概率保存到csv文件中。
您可以在以下图中看到执行测试的代码:

图 1.65:测试代码片段
如果您能看到图 1.64,那么您就会知道在这里,我们实现了 86% 的准确率。这个分数是目前按照行业标准最有效的准确率。
摘要
在本章中,我们探讨了如何使用各种统计技术来分析数据集。之后,我们获得了一种基本方法,并通过使用这种方法,我们开发了一个连基准线都没有达到的模型。因此,我们分析了这种方法中出了什么问题,并尝试了另一种方法,这种方法解决了我们基准模型的难题。然后,我们评估了这种方法,并使用交叉验证和集成技术优化了超参数,以实现该应用的最佳可能结果。最后,我们找到了最佳可能的方法,这为我们带来了最先进的结果。您可以在 GitHub 上找到所有相关代码:github.com/jalajthanaki/credit-risk-modelling。您可以在github.com/jalajthanaki/credit-risk-modelling/blob/master/README.md找到所有安装相关信息。
在下一章中,我们将探讨分析领域另一个非常有趣的应用:预测特定股票的价格。这听起来是不是很有趣?我们还将使用一些现代机器学习(ML)和深度学习(DL)方法来开发股票价格预测应用,所以也要为此做好准备!
第二章. 股票市场价格预测
在本章中,我们将介绍一个属于预测分析的精彩应用。我希望章节的名称已经给了你一个大致的印象,本章将要讲述什么。我们将尝试预测股票指数的价格。我们将应用一些现代机器学习技术以及深度学习技术。
本章我们将涵盖以下主题:
- 
介绍问题陈述 
- 
收集数据集 
- 
理解数据集 
- 
数据预处理和数据分析 
- 
特征工程 
- 
选择机器学习(ML)算法 
- 
训练基线模型 
- 
理解测试矩阵 
- 
测试基线模型 
- 
探索现有方法的问题 
- 
理解修订方法 - 理解概念和方法
 
- 
实施修订方法 - 
测试修订方法 
- 
使用修订方法理解问题 
 
- 
- 
最佳方法 
- 
摘要 
那么,让我们开始吧!
介绍问题陈述
股票市场是一个你可以买卖公司所有权单位的地方,我们称之为股票。如果公司表现良好并增加其利润,那么你也会获得一些利润,因为你拥有公司的股票,但如果公司的利润下降,那么你将失去你在公司中的钱。所以如果你在正确的时间和正确的公司投资,可能会让你赚很多钱。问题是你应该购买哪只公司的股票?有没有一种方法,我们可以根据公司股票的历史价格预测任何公司股票的未来价格,这样我们就有更高的机会获得良好的回报?答案是肯定的。这就是我们在本章要探讨的内容。
如果你投资股市,你可能听说过股票价格是完全随机且不可预测的。这被称为有效市场假说,但大多数大型金融公司,如摩根大通、花旗集团和高盛,都有数学和定量分析师,他们试图开发预测模型,帮助这些大公司决定何时投资以及投资哪只股票。
在投资任何股票之前,我们会对公司的概况进行一些基本研究。我们试图了解其商业模式。我们还检查公司的资产负债表,以了解公司的盈亏情况。公司将在接下来的几个月内推出哪些产品?关于公司的哪些新闻正在传来?当前的行业趋势是什么?在研究了所有这些参数之后,如果我们觉得我们将会获得一些利润,我们就会投资特定公司的股票;否则,我们不会投资那个公司。
我们依赖各种信息来源来了解我们是否需要购买股票或出售股票。您不觉得所有这些分析都花费了我们很多时间吗?我想向您提出两个问题。首先,我们能否使用这里讨论的一些数据点构建一个系统,帮助我们找出未来的股票价格?还有,我们能否使用历史股票价格来预测未来的股票价格?这两个问题的答案都是肯定的:我们可以构建一个系统,使用历史股票价格和一些其他数据点,以便我们能够预测股票的未来价格。根据有效市场假说,通过使用股票的历史价格和多种其他数据点,我们可以获得股票的未来价格,这将比随机猜测更好。在本章中,我们将构建一个预测模型,该模型将预测股票的收盘价。在下一节中,我们将探讨如何收集数据集以构建模型。那么,让我们开始吧!
收集数据集
为了构建模型,我们首先需要收集数据。我们将使用以下两个数据点:
- 
道琼斯工业平均指数(DJIA)指数价格 
- 
新闻文章 
道琼斯工业平均指数价格给我们一个特定日子股市走势的整体概念,而新闻文章帮助我们了解新闻如何影响股票价格。我们将使用这两个数据点来构建我们的模型。现在,让我们收集数据。
收集道琼斯工业平均指数价格
为了收集道琼斯工业平均指数价格,我们将使用雅虎财经。您可以访问此链接:finance.yahoo.com/quote/%5EDJI/history?period1=1196706600&period2=1512325800&interval=1d&filter=history&frequency=1d。一旦您点击此链接,您就可以看到价格数据出现。您可以更改时间范围并点击下载数据链接,就这样;您就可以拥有所有以.csv文件格式存储的数据。请参考以下雅虎财经道琼斯工业平均指数价格页面截图:

图 2.1:道琼斯工业平均指数价格雅虎财经页面
在这里,我们已下载了 2007-2016 年的数据集,这意味着我们拥有 10 年的道琼斯工业平均指数价格数据。您也可以在图 2.1中看到这一点。您可以使用以下 GitHub 链接找到这个数据集:github.com/jalajthanaki/stock_price_prediction/blob/master/data/DJIA_data.csv。
请稍等片刻;我们将理解本章“理解数据集”部分中每个数据属性的意义。现在,让我们看看我们如何收集新闻文章。
收集新闻文章
我们想要收集新闻文章,以便我们可以建立新闻如何影响道琼斯工业平均指数(DJIA)值之间的相关性。我们将对新闻文章进行情感分析。您可能会想知道为什么我们需要进行情感分析。如果任何新闻对金融市场有负面影响,那么股票价格很可能会下降;如果有关金融市场的新闻是积极的,那么股票价格很可能会上涨。对于这个数据集,我们将使用《纽约时报》(NYTimes)的新闻文章。为了收集新闻文章数据集,我们将使用《纽约时报》的开发者 API。那么,让我们开始编码吧!
首先,您需要在《纽约时报》开发者网站上注册并生成您的 API 密钥。链接是developer.nytimes.com/signup。我已经为存档 API 生成了 API 密钥。在这里,我们使用newsapi, JSON, requests和sys依赖项。您也可以通过以下链接参考《纽约时报》开发者文档:developer.nytimes.com/archive_api.json#/Documentation/GET/%7Byear%7D/%7Bmonth%7D.json。
您可以在以下 GitHub 链接找到代码:github.com/jalajthanaki/stock_price_prediction/blob/master/getdata_NYtimes.py。您可以在下面的屏幕截图中看到代码片段:

图 2.2:从《纽约时报》获取新闻文章数据的代码片段
如您在代码中所见,有三个方法。前两个方法是用于异常处理,第三个方法用于验证并请求为我们生成新闻文章数据的 URL。这个《纽约时报》API URL 需要三个参数,如下所示:
- 
年份 
- 
月份 
- 
API 密钥 
在这一步之后,我们将调用第三个函数,并传递从 2007 年到 2016 年的年份值。我们将以JSON格式保存数据。您可以在下面的屏幕截图中的代码片段中参考:

图 2.3:从《纽约时报》获取新闻文章数据的代码片段
您可以使用以下 GitHub 链接找到原始 JSON 数据集:github.com/jalajthanaki/stock_price_prediction/blob/master/data/2016-01.json。
现在,让我们进入下一节,我们将了解我们迄今为止收集的数据集和属性。
理解数据集
在本节中,我们将了解数据属性的含义,这将帮助我们了解我们将要处理的数据集类型以及数据集需要进行的预处理类型。我们将在两个部分中理解我们的数据集,如下所示:
- 
理解道琼斯工业平均指数(DJIA)数据集 
- 
理解《纽约时报》新闻文章数据集 
理解道琼斯工业平均指数数据集
在道琼斯工业平均指数数据集中,我们有七个数据属性。它们很容易理解,所以让我们逐一查看它们:
- 
Date: 第一列表示在.YYMMDD 格式的.csv 文件中看到的数据的日期。
- 
Open: 这表示市场开盘时的价格,因此它是特定交易日的道琼斯工业平均指数的开盘值。
- 
High: 这是特定交易日的道琼斯工业平均指数的最高价格。
- 
Low: 这是特定交易日的道琼斯工业平均指数的最低价格。
- 
Close: 交易日的道琼斯工业平均指数收盘价。
- 
Adj close: 调整后的收盘价(adj close price)以收盘价为基础,并考虑诸如股息、股票分割和新股票发行等因素。调整后的收盘价代表了道琼斯工业平均指数的真实反映。让我给你举一个例子,以便你能更好地理解调整后的收盘价:如果一家公司提供每股 5 美元的股息,并且该公司的股票收盘价为 100 美元,那么调整后的收盘价将变为 95 美元。因此,调整后的收盘价考虑了各种因素,并根据这些因素生成公司股票的真实价值。在这里,我们关注的是道琼斯工业平均指数的价值,所以,大多数情况下,收盘价和调整后的收盘价是相同的。
- 
Volume: 这些值表示特定交易日在交易所交易的数量。
这些是道琼斯工业平均指数数据集的基本细节。我们使用历史数据并尝试预测道琼斯工业平均指数的未来走势。
在下一节中,我们将查看《纽约时报》新闻文章数据集。
理解《纽约时报》新闻文章数据集
我们已经使用了《纽约时报》开发者 API,并以 JSON 形式收集了新闻文章,因此,在这里,我们将查看 JSON 响应,以便我们可以识别出最重要的数据属性,并集中关注。在下一张图中,你可以看到我们从《纽约时报》得到的 JSON 响应:

图 2.4:使用《纽约时报》开发者工具的新闻文章的 JSON 响应
在这个图表中,我们可以看到单篇新闻文章的 JSON 响应。正如你所见,有一个主要数据属性响应,它携带了所有其他数据属性。我们将关注 docs 数组内部给出的数据属性。不用担心;我们不会使用所有数据属性。在这里,我们将关注以下数据属性:
- 
type_of_material: 此属性表示特定的新闻文章是从某种特定的来源中提取的,无论是博客、新闻文章、分析等等。
- 
headlines: 标题数据属性有两个子数据属性。主要数据属性包含新闻的实际标题,而 kicker 数据属性则传达文章的亮点。
- 
pub_date: 这个数据属性表示新闻文章的发布日期。你可以在文档数组的倒数第二部分找到这个属性。
- 
section_name: 这个数据属性出现在前面图像的最后一部分。它提供了新闻文章的类别。
- 
news_desk: 这个数据属性也指示新闻类别。当响应中缺少section_name时,我们将参考这个属性。
正确理解数据属性后,我们应该继续到下一部分,即数据预处理和数据分析部分。
数据预处理和数据分析
在本节中,我们将主要介绍数据预处理和数据分析。作为数据预处理的一部分,我们正在准备我们的训练数据集。你可能想知道我所说的数据准备是什么,考虑到我们已经有数据了。让我告诉你,我们有两个不同的数据集,并且这两个数据集都是独立的。因此,我们需要合并道琼斯工业平均指数数据集和纽约时报新闻文章数据集,以便从这些数据集中获得有意义的见解。一旦我们准备好了训练数据集,我们就可以使用不同的机器学习(ML)算法来训练数据。
现在我们开始编写代码来准备训练数据集。我们将使用numpy、csv、JSON和pandas作为我们的依赖库。在这里,我们的代码分为两部分。首先,我们将为道琼斯指数数据集准备数据集,然后我们将转到下一部分,即准备纽约时报新闻文章数据集。在准备训练数据集的过程中,我们将编写基本的数据分析步骤。
准备道琼斯工业平均指数训练数据集
你可以在下面的屏幕截图中看到代码片段。你可以在以下 GitHub 链接中找到代码:github.com/jalajthanaki/stock_price_prediction/blob/master/datapreparation.ipynb。

图 2.5:准备道琼斯工业平均指数数据集的代码片段
正如你在前面的代码片段中看到的,我们正在读取我们从雅虎财经页面下载的 csv 文件。之后,我们将数据转换为列表格式。我们还从列表中分离了标题和实际数据。一旦我们有了列表格式的数据,我们将数据转换为 numpy 数组。我们从 DIJA 数据集中选择了仅三个列,如下所示:
- 
日期 
- 
收盘价 
- 
调整后收盘价 
您可能有一个疑问:为什么我们只考虑了 DJIA csv 文件中的收盘价和调整后的收盘价?让我澄清一下:正如我们所知,开盘价通常是前一天收盘价的一个附近值,所以我们没有考虑开盘价。我们没有考虑最高价和最低价,因为我们不知道这些最高价和最低价发生在哪个特定的时间戳。对于第一次迭代来说,预测股票指数何时达到高或低值相当复杂,所以,在此期间,我们忽略这两个列。我们主要对 DJIA 指数的整体趋势感兴趣。如果我们能精确地找出趋势,我们就可以在以后预测高和低的价格值。在这里,我们限制我们的目标为预测未来交易日的 DJIA 指数的收盘价。
现在回到编码部分:我们以这种方式构建了 pandas 数据框,使得日期列作为索引列,而收盘价和调整后的收盘价是数据集的两个其他列。您可以在代码片段中看到以df变量形式定义的数据框的输出,该代码片段见图 2.5。您可以在以下图中看到数据框 df 的输出:

图 2.6:pandas 数据框的输出,该数据框在图 2.5 的代码片段中定义为df变量
希望现在您已经清楚地理解了我们迄今为止所遵循的步骤。我们已经创建了基本的数据框,所以现在我们将继续进行 DJIA 数据集的基本数据分析部分。
DJIA 数据集的基本数据分析
在本节中,我们将对 DJIA 数据集进行基本数据分析。这个数据集有日期值,但如果您仔细查看日期值,您会发现有一些缺失的日期。假设数据缺失于 2006 年 12 月 30 日、31 日、2007 年 1 月 1 日以及许多其他日期。在这种情况下,我们将添加缺失的日期值。您可以参考图 2.7 中的代码片段,以及在此 GitHub 上找到此代码:github.com/jalajthanaki/stock_price_prediction/blob/master/datapreparation.ipynb。

图 2.7:在 DJIA 数据集中添加所有缺失日期值的代码片段
如前图所示,我们在添加这些缺失的日期值之后遇到了另一个挑战。我们已经添加了日期值,但是没有对应每个日期的收盘价或调整后的收盘价,因此我们需要逻辑地替换 NaN 值,而不是随机替换。
为了替换收盘价和调整后收盘价的 NaN 值,我们将使用 pandas 插值功能。我们使用线性插值生成 NaN 的缺失值。有几种插值类型可用,但在这里我们使用线性插值,线性插值的数学方程如下:

公式 2.1:线性插值数学公式
如果两个已知点由坐标(x1,y_1)和(x_3,y_3)给出,线性插值是这两个点之间的直线。
你可以参考以下截图中的代码片段:

图 2.8:基本数据分析与插值实现的代码片段
此代码可在 GitHub 上找到:github.com/jalajthanaki/stock_price_prediction/blob/master/datapreparation.ipynb。
如代码片段所示,我们尚未定义应在我们的数据集上执行哪种类型的插值;在这种情况下,默认执行了线性插值。因此,在应用线性插值后,我们可以用实际的逻辑值替换 NaN 值。我们还删除了 2006 年的三条记录。因此,现在我们总共有 3653 条记录。
这是我们为道琼斯工业平均指数(DJIA)数据集所做的基本数据预处理和数据分析。现在让我们继续到纽约时报新闻文章数据集。首先,我们需要准备训练数据集,所以让我们从这里开始。
准备纽约时报新闻数据集
在本节中,我们将了解如何准备纽约时报新闻数据集。我们已经下载了整个新闻文章数据集,但我们还没有添加选择新闻文章类别的过滤机制。在准备纽约时报数据集时,请执行以下步骤:
- 
将发布日期转换为 YYYY-MM-DD 格式。 
- 
通过类别过滤新闻文章。 
- 
实现过滤功能并合并数据集。 
- 
将合并后的数据集保存为 pickle 文件格式。 
因此,让我们开始为每个步骤编写代码。
将发布日期转换为 YYYY-MM-DD 格式
首先,我们将新闻文章的发布日期转换为 YYYY-MM-DD 格式,以便我们可以在以后合并道琼斯工业平均指数(DJIA)和纽约时报新闻文章数据集。为了实现这一点,你可以参考以下代码片段:

图 2.9:转换新闻文章发布日期格式的代码片段
在这里,我们编写了一个可以将发布日期格式解析并转换为必要的 YYYY-MM-DD 格式的函数。稍后当我们读取存储 JSON 响应的 JSON 文件时,我们将调用此函数。
通过类别过滤新闻文章
我们在这里要做的另一件事是按新闻类别过滤我们的新闻文章数据集。我们下载了所有类型的新闻文章,但为了股票市场价格预测应用程序,我们需要属于特定新闻类别的新闻文章。因此,我们需要实现过滤器,帮助我们提取必要的新闻文章子集。您可以在以下代码片段中参考:

图 2.10:按类别过滤新闻文章的代码片段
您可以参考以下 GitHub 链接提供的代码:github.com/jalajthanaki/stock_price_prediction/blob/master/datapreparation.ipynb.
如前图所示,我们正在提取属于以下新闻类别的新闻文章:
- 
商业 
- 
国内 
- 
世界 
- 
美国 
- 
政治 
- 
评论 
- 
科技 
- 
科学 
- 
健康 
- 
外国 
实现过滤器功能并合并数据集
现在,我们需要迭代每个 JSON 文件,并提取上一节中定义的新闻类别之一的新闻文章。您可以参考实现过滤器功能的代码片段。在即将到来的代码片段中,您还可以找到合并道琼斯工业平均指数(DJIA)数据集和纽约时报新闻文章数据集的实现。为了合并这两个数据集,我们将每个新闻文章的标题添加到 pandas 数据框中,然后我们将从这个数据框生成我们的最终训练数据集。此功能在以下屏幕截图中显示:

图 2.11:过滤和合并功能的代码片段
我们还编写了一部分异常处理功能。这样做是为了如果任何 JSON 响应没有 data 属性中的 section_name、news_desk 或 type_of_material 的值,则此代码将抛出异常。您可以在以下屏幕截图中查看代码片段:

图 2.12:异常处理的实现
我们还将考虑没有section_name和news_desk的新闻文章。我们将把所有新闻文章的标题添加到我们的数据集中,并将它们放入 pandas 数据框中。您可以在以下屏幕截图中看到代码片段:

图 2.13:处理没有 section_name 和 news_desk 的新闻文章
您可以在下面的屏幕截图中以 pandas 数据框的形式看到最终的合并数据集:

图 2.14:最终合并的训练数据集
在这里,对于每个日期,我们对应所有属于商业、国家、世界、美国、政治、观点、科技、科学和健康类别的新闻标题。我们已下载了 1,248,084 篇新闻文章,并从中考虑了 461,738 篇新闻文章用于我们的模型。
您可以通过此 GitHub 链接访问代码:github.com/jalajthanaki/stock_price_prediction/blob/master/datapreparation.ipynb.
将合并后的数据集保存为 pickle 文件格式
一旦我们合并了数据,我们需要保存数据对象,因此我们将使用 Python 的 pickle 模块。Pickle 帮助我们序列化和反序列化数据。Pickle 依赖库运行速度快,因为大部分是用 C 语言编写的,就像 Python 解释器本身一样。在这里,我们将我们的训练数据集保存为.pkl文件格式。您可以参考以下代码片段*:

图 2.15:将数据保存为 pickle 格式的代码片段
我们已将数据集保存为pickled_ten_year_filtered_lead_para.pkl文件。您可以在 GitHub 上找到代码:github.com/jalajthanaki/stock_price_prediction/blob/master/datapreparation.ipynb.
在下一节中,我们将主要关注特征工程部分。我们还将进行一些小的数据清洗步骤。所以,让我们跳到下一节。
特征工程
如前所述,我们想要预测特定交易日的道琼斯工业平均指数的收盘价。在本节中,我们将根据我们的直觉进行特征选择,以构建我们的基本股价预测模型。我们已经生成了训练数据集。因此,现在我们将加载保存的.pkl 格式数据集,并执行特征选择以及一些小的数据处理。我们还将为每个过滤后的《纽约时报》新闻文章生成情感分数,并使用这个情感分数来训练我们的基线模型。我们将使用以下 Python 依赖项:
- 
numpy 
- 
pandas 
- 
nltk 
本节包含以下步骤:
- 
加载数据集 
- 
小型预处理 
- 
特征选择 
- 
情感分析 
那么,让我们开始编码!
加载数据集
我们已将数据保存为 pickle 格式,现在我们需要从其中加载数据。您可以参考以下代码片段*:

图 2.16:从 pickle 文件加载数据的代码片段
您可以通过点击此 GitHub 链接来查看代码:github.com/jalajthanaki/stock_price_prediction/blob/master/Stock_Price_Prediction.ipynb.
如您所见,在数据框输出中,整个数据集中的每篇文章标题前都有一个点(.),因此我们需要移除这些点。我们将在下一节中执行此更改。
微小预处理
作为预处理的一部分,我们将执行以下两个更改:
- 
将调整后的收盘价转换为整数格式 
- 
从新闻标题中移除最左侧点(.) 
将调整后的收盘价转换为整数格式
我们知道调整后的收盘价是浮点格式。因此,在这里我们将浮点值转换为整数格式,并将转换后的值作为prix属性存储在我们的 pandas 数据框中。现在,您可能想知道为什么我们只考虑调整后的收盘价。请稍等片刻,我将给出原因。您可以在下面的屏幕截图中的代码片段中找到收敛代码:

图 2.17:将调整后的收盘价转换为整数格式的代码片段
小贴士
您可以参考以下 GitHub 链接中的代码:github.com/jalajthanaki/stock_price_prediction/blob/master/Stock_Price_Prediction.ipynb.
现在,让我们继续进行第二个更改。
从新闻标题中移除最左侧点
在本节中,我们将看到移除最左侧点的实现。我们将使用lstrip()函数来移除点。您可以在下面的屏幕截图中的代码片段中参考:

图 2.18:从新闻文章标题中移除点的代码片段
小贴士
您可以参考以下 GitHub 链接中的代码:github.com/jalajthanaki/stock_price_prediction/blob/master/Stock_Price_Prediction.ipynb.
现在,让我们继续下一节,这一节是特征工程。
特征工程
特征选择是特征工程和任何机器学习(ML)应用最重要的方面之一。因此,我们将重点关注特征选择。在前一节中,我提出了为什么我们只选择调整后的收盘价而不是收盘价的问题。这个问题的答案在于特征选择。我们选择调整后的收盘价,因为这些价格能让我们更好地了解道琼斯工业平均指数的最后一个价格,包括股票、共同基金、股息等。在我们的数据集中,收盘价大多与调整后的收盘价相同,在未来,如果我们考虑未见过的新数据记录的收盘价,我们无法推导出调整后的收盘价,因为它可能等于收盘价或高于收盘价,道琼斯工业平均指数的调整后的收盘价可能高于收盘价,因为它将包括股票、共同基金、股息等。但我们不知道在只考虑收盘价的未见过数据集中,它将高出多少。所以如果我们考虑调整后的收盘价,那么我们将知道收盘价可能小于或等于调整后的收盘价,但不会超过调整后的收盘价。调整后的收盘价是收盘价可能的最大值。因此,我们考虑了调整后的收盘价进行开发。对于基线模型,我们将考虑调整后的收盘价。我们将列名重命名为price。您可以参考以下代码片段:

图 2.19:将调整后的收盘价作为特征选择一部分的代码片段
作为下一步,我们现在将对新闻文章数据集进行情感分析。我们可以在训练模型时使用情感得分。所以,让我们继续进行情感分析部分。
对《纽约时报》新闻文章进行情感分析
为了实现情感分析,我们使用了 nltk 内置的情感分析模块。我们将获得负面、正面和复合情感得分。我们使用了基于词典的方法。在基于词典的方法中,分析每个句子的单词,并根据sentiwordnet得分,每个单词被赋予一个特定的情感得分;然后,决定句子级别的聚合得分。
注意
Sentiwordnet 是包含单词情感得分的词典。
我们将在第五章中详细介绍与情感分析相关的细节,情感分析。您可以参考以下情感分析代码片段:

图 2.20:情感分析代码片段
所有的得分都由前面的代码生成并存储在数据框中,因此您可以在以下屏幕截图中看到新闻文章标题的聚合得分:*

图 2.21:存储在数据框中的聚合情感分析得分
到本节结束时,我们将获得 NYTimes 新闻文章数据集的情感分数,并将这些情感分数作为训练数据集的一部分。到目前为止,我们已经进行了轻微的预处理,根据我们的直觉选择了数据属性,并生成了情感分数。现在,我们将选择机器学习算法,并尝试构建基线模型。因此,让我们进入下一节。
选择机器学习算法
在本节中,我们将根据我们的直觉选择机器学习(ML)算法,然后使用我们的训练数据集进行训练。这是本章的第一个模型,因此训练的模型是我们的基线模型,我们将在以后对其进行改进。因此,让我们决定哪种 ML 算法适合这个股价预测应用。
股价预测应用是一个时间序列分析问题,我们需要预测时间序列中的下一个点。这种预测活动类似于线性回归,因此我们可以说这个应用是一种回归问题,回归家族中的任何算法都应该适用。让我们选择集成算法,即RandomForestRegressor,来开发我们的基线模型。因此,让我们训练我们的基线模型,并根据该模型的结果,我们将修改我们的方法。
训练基线模型
如你所知,我们已经选择了RandomForestRegressor算法。我们将使用 scikit-learn 库来训练模型。以下是我们需要遵循的步骤:
- 
分割训练和测试数据集 
- 
将训练和测试数据集的预测标签分开 
- 
将情感分数转换为 numpy 数组 
- 
训练机器学习模型 
因此,让我们逐一实现这些步骤。
分割训练和测试数据集
我们有 10 年的数据值。因此,为了训练目的,我们将使用 8 年的数据,这意味着从 2007 年到 2014 年的数据集。为了测试目的,我们将使用 2 年的数据,这意味着 2015 年和 2016 年的数据。你可以参考以下截图中的代码片段以了解其实施:

图 2.22:分割训练和测试数据集
如前述截图所示,我们的训练数据集已存储在 train 数据框中,而我们的测试数据集已存储在 test 数据框中。
将训练和测试数据集的预测标签分开
在我们分割训练和测试数据集的同时,我们还需要单独存储调整后的收盘价,因为我们需要预测这些调整后的收盘价(在代码中标记为prices);这些价格值是我们训练数据的标签,这种训练成为监督训练,因为我们将以标签的形式提供实际的价格。你可以参考以下代码以了解其实施:

图 2.23:分割训练和测试数据集的预测标签
在这里,除了价格以外的所有属性都是以特征向量格式给出的,而价格是以标签的形式。ML 算法接受这个特征向量,标记这对数据,学习必要的模式,并预测未见数据的价格。
将情感分数转换为 numpy 数组
在我们开始训练之前,还有一个最后、必要的问题需要我们记住:我们将情感分析分数转换为 numpy 数组格式。这是因为一旦我们将价格属性设置为预测标签,我们的特征向量将只包含情感分数和日期。因此,为了生成一个合适的特征向量,我们将情感分数转换为 numpy 数组。实现此功能的代码片段在以下截图提供:

图 2.24:将情感分析分数转换为 numpy 数组的代码片段
如您从代码片段中可以看到,我们对训练数据集和测试数据集都执行了相同的转换操作。
注意
注意,如果您得到一个值错误,请检查数据集,因为可能存在数据集中某一列有空白或空值的情况。
现在,让我们训练我们的模型!
ML 模型的训练
在第一次迭代中,我们使用的是作为 scikit-learn 依赖部分提供的 RandomForestRegressor 算法。您可以在以下截图找到此代码:

图 2.25:使用 RandomForestRegressor 进行训练的代码片段
如您从前面的截图中所见,我们已经为我们的超参数使用了所有默认值。有关超参数的更详细描述,您可以参考scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestRegressor.html。
现在我们已经训练了模型,我们需要使用我们的测试数据集来测试它。在我们测试之前,让我们讨论我们将采取的测试模型的方法。
理解测试矩阵
在本节中,我们将了解测试矩阵和可视化方法来评估训练好的 ML 模型的表现。所以让我们了解这两种方法,它们如下:
- 
默认测试矩阵 
- 
可视化方法 
默认测试矩阵
我们使用 scikit-learn 的默认分数 API 来检查 ML 的表现如何。在这个应用程序中,分数函数是平方误差和的系数。它也被称为 R2 系数,其定义如下方程:

这里,u 表示残差平方和。u 的方程如下:

变量 v 表示平方和的总和。v 的方程如下:

最佳可能分数是 1.0,它也可以是负分数。负分数表示训练模型可以任意糟糕。一个始终预测标签 y 的预期值,而忽略输入特征的恒定模型将产生 R2 分数为 0.0。
为了获得分数,我们只需调用分数函数。测试的代码将与“测试基线模型”部分中的代码相同。现在让我们看看另一种有助于理解输出与真实测试标签的测试方法。那么,让我们来看看吧!
可视化方法
在本节中,我们将探讨一种有效且直观的方法,即预测输出与实际输出的可视化。这种方法提供了很多洞察力,因为图表易于理解,你可以决定下一步如何改进模型。
在这个应用中,我们将使用测试数据集中的实际价格和预测价格,这将表明预测的好坏。你将在下一节中找到这个过程的相关代码和图表,该节名为“测试基线模型”。
测试基线模型
在本节中,我们将实现测试方法,以便评估我们模型的准确性。我们首先生成输出预测,然后开始测试。以下是我们将在这里实施的步骤:
- 
生成和解释输出 
- 
生成分数 
- 
可视化输出 
生成和解释输出
为了生成预测,我们正在使用 treeinterpreter 库。我们使用以下代码为测试数据集中的每条记录预测价格值:

图 2.26:生成预测的代码片段
这里,prediction 是一个数组,其中包含与测试数据集中所有记录对应的预测 adj close price 元素。现在,我们将比较这个预测输出与测试数据集的实际 adj close price。通过这样做,我们将了解我们的第一个模型在预测 adj close price 方面的准确性。为了进一步评估,我们将生成准确度分数。
生成准确度分数
在本节中,我们将根据“默认测试矩阵”部分提供的方程生成准确度分数。相应的代码如下:

图 2.27:为测试数据集生成分数的代码片段
如前述代码片段所示,我们的模型表现并不太好。在这个阶段,我们还不知道我们犯了什么错误或者出了什么问题。当你试图解决或构建一个机器学习模型时,这种情况很常见。我们可以使用可视化技术更好地把握问题。
可视化输出
在本节中,我们将使用可视化图表。使用图表,我们将识别我们犯的错误类型,以便我们可以在下一次迭代中修复这个错误。我们将绘制一个图表,其中Y 轴代表调整后的收盘价,而X 轴代表日期。我们在图表上绘制实际价格和预测价格,以便我们大致了解我们的算法表现如何。我们将使用以下代码片段生成图表:

图 2.28:生成预测价格与实际价格对比图的代码片段。
如前述图表所示,最上面的单行(橙色)代表实际价格,而线下的杂乱尖峰(蓝色)代表预测价格。从这个图中,我们可以总结出我们的模型无法正确预测价格。在这里,你可以看到实际价格和预测价格并没有对齐。我们需要解决这个问题。有一些技术我们可以尝试,比如对齐、平滑和尝试不同的算法。所以,让我们在下一节中讨论这个方法的问题。
备注
你可以从 GitHub 链接github.com/jalajthanaki/stock_price_prediction/blob/master/Stock_Price_Prediction.ipynb中获取这个主题的完整代码。
探索现有方法的问题
在本节中,我们将讨论现有方法的问题。我们可能犯的主要有三个错误,如下列所示:
- 
对齐 
- 
平滑 
- 
尝试不同的机器学习算法 
让我们逐一点讨论每个问题。
对齐
正如我们在图中看到的,我们的实际价格和预测价格并没有对齐。这成为一个问题。我们需要对股票的价格进行对齐。我们需要考虑我们数据集的平均值,并根据这个平均值生成对齐。你可以在接下来的名为基于对齐的方法的部分中了解更多关于对齐的信息。
平滑
我感觉我们第一个模型存在的问题是,我们没有应用任何平滑技术。所以,对于我们的模型,我们也需要应用平滑技术。我们将使用指数加权移动平均(EWMA)技术进行平滑。这种技术用于调整数据集的方差。
尝试不同的机器学习算法
对于我们的模型,我们使用了 RandomForestRegressor 算法。但如果我们尝试使用不同的算法,比如 逻辑回归 来做同样的事情,会怎样呢?在下节中,你将学习如何实现这个算法——当然是在应用必要的对齐和平滑之后。
我们已经看到了我们第一个基线方法可能存在的问题。现在,我们将尝试理解实现对齐、平滑和 Logistic Regression 算法的途径。
理解修订的方法
在本节中,我们将探讨对齐和平滑的关键概念和方法。实现 逻辑回归 算法并不困难;我们将使用 scikit-learn API。因此,我们将从理解实现的概念和方法开始。
理解概念和方法
在这里,我们将讨论对齐和平滑将如何工作。一旦我们理解了对齐和平滑的技术细节,我们将专注于基于逻辑回归的方法。
基于对齐的方法
使用这种方法,我们将使用一个常数来增加价格,以便我们的预测价格和测试数据集中的实际价格对齐。假设我们考虑 10 天。我们将生成价格的平均值。之后,我们生成第一个机器学习模型预测的价格的平均值。一旦我们生成了这两个平均值,我们需要减去这些值,得到的答案就是这 10 天的对齐值。
让我们用一个直观的工作示例来帮助你澄清思路。考虑从 2015 年 1 月 2 日到 1 月 11 日的 10 天。对于每条记录,你将取实际价格的平均值。假设这个数字将是 17,676,预测价格的平均值将是 13,175。在这种情况下,你将得到 4,501 的差异,这就是对齐的值。我们将把这个值添加到我们的测试数据集中,以便测试价格值和预测价格值对齐。你将在 实现修订方法 部分找到代码实现。
基于平滑的方法
在这种方法中,我们将使用 EWMA。EWMA 代表 指数加权移动平均。平滑方法基于加权平均的概念。一般来说,加权移动平均是通过以下方程计算的:

在这里,x[t] 是输入,y[t] 是输出。权重使用以下方程计算:

图 2.29:计算 EWMA 权重的方程
图片来源:pandas.pydata.org/pandas-docs/stable/computation.html#exponentially-weighted-windows
在这里,α 是平滑常数。如果平滑常数的值较高,则它将接近实际值;如果平滑常数较低,则它将更平滑,但不会接近实际值。通常,在统计学中,平滑常数的范围在 0.1 到 0.3 之间。因此,我们可以使用平滑常数生成平滑值。
让我们用一个工作示例来演示。取平滑常数 = 0.3;如果实际值是 100,预测值是 110,那么平滑值可以通过以下公式获得,即(平滑常数 * 实际值)+(1-平滑常数)* 预测值。我们将获得的价值是 (0.3 100) + (1-0.3)110 = 107。更多信息,您可以参考pandas.pydata.org/pandas-docs/stable/computation.html#exponentially-weighted-windows。
我们将在“实现改进方法”部分看到实际的代码级实现。pandas 已经有了 API,因此我们可以轻松实现 EWMA。
基于逻辑回归的方法
实现逻辑回归算法是一个简单的任务,因为我们只需要使用 scikit-learn API。对于测试数据集,我们将应用对齐和平滑。在评估准确度后,我们将决定是否需要更改机器学习算法。我们从直觉出发,逐渐改进我们的方法。我并不需要真正解释逻辑回归算法本身,但在实现过程中,我们将讨论重要点。
现在,是时候继续我们改进方法的具体实现部分了。让我们看看下一节。
实现改进的方法
在本节中,我们将讨论实现的三个部分,如下所述:
- 
实现 
- 
测试改进的方法 
- 
理解改进方法中的问题 
实现
在这里,我们正在实现以下内容:
- 
对齐 
- 
平滑 
- 
逻辑回归 
我们已经讨论了方法和关键概念,所以现在我们只需关注这里的代码部分。您可以在以下 GitHub 链接中找到所有代码:github.com/jalajthanaki/stock_price_prediction/blob/master/Stock_Price_Prediction.ipynb。
实现对齐
对齐是在测试数据集上进行的。您可以参考以下代码片段:

图 2.30:测试数据集上的对齐代码片段
如您在前面的代码片段中看到的,我们使用过去 5 天的平均价格和预测的下一个 5 天的平均价格来获得 10 天的adj close price差异,以便对齐测试数据。在这里,我们还把日期从字符串转换为日期格式。如您所见,5096.99 是测试预测价格中的差异,我们将将其添加到我们的预测adj close price值中。我们再次生成了图表,以便我们能够轻松理解对齐方法得到了很好的实现。您可以参考以下代码片段:

图 2.31:对齐方法的代码片段
如您在前面的代码片段中看到的,对齐图表显示我们的测试数据集价格和预测价格是对齐的。对齐图表的好处是,现在我们可以精确地定义RandomForestRegressor没有以高精度完成其工作,因为它的性能对于所有数据记录来说都不太理想。对齐图表为我们之前的迭代提供了一个清晰的画面。因此,当我们现在训练逻辑回归时,我们将使用对齐来评估预测价格。
实现平滑处理
我们使用 pandas EWMA API,时间跨度为 60 天,频率时间为D. 这个"D"表示我们在数据集中处理的是日期时间格式。您可以在以下代码片段中看到代码实现:

图 2.32:EWMA 平滑的代码片段
我们还在生成一个图表,其中包含预测价格、平均预测价格、实际价格和平均实际价格。您可以参考以下代码和图表:

图 2.33:平滑后生成图表的代码片段
在这个图表中,您可以看到在平滑平均预测价格之后,曲线遵循实际价格的趋势。尽管准确性不是很高,但我们将会朝着积极的方向发展。如果我们想调整我们的算法,平滑技术将对我们很有用。您可以参考以下图表查看平均预测价格与实际价格:

图 2.34:表示平均预测价格与实际价格的代码片段
通过参考前面的图表,我们可以指出我们应用了对齐和平滑,因为这有助于调整我们的机器学习模型以进行下一次迭代。
实现逻辑回归
在本节中,我们将实现逻辑回归。请看以下截图:

图 2.35:逻辑回归的代码片段
在这里,我们再次使用逻辑回归机器学习算法训练了模型。我们还为测试数据集实现了对齐和平滑。现在,让我们评估逻辑回归模型。
测试改进方法
我们已经测试了逻辑回归模型。您可以参考以下图表形式的可视化,显示这种改进方法确实比RandomForesRegressor(没有对齐和光滑)要好,但还没有达到标准:

图 2.36:年度预测图表
如前一个屏幕截图所示,我们为逻辑回归生成了年度图表;我们可以看到使用此模型有轻微的改进。我们还使用了对齐和光滑,但效果并不太明显。
现在,让我们讨论一下这个改进方法存在的问题,然后我们可以实施最佳方法。
理解改进方法中的问题
在本节中,我们将讨论为什么我们的改进方法没有给我们带来良好的结果。ML 模型不起作用是因为数据集没有归一化。第二个原因是,即使在归一化和光滑之后,RandomForestRegression ML 模型仍然面临过拟合问题。对于最佳方法,我们需要处理归一化和过拟合。我们可以使用基于神经网络的 ML 算法来解决这个问题。因此,在我们的最后一次迭代中,我们将开发一个可以给我们最佳精度的神经网络。
最佳方法
在这里,我们将实现基于神经网络的算法多层感知器(MLP)。您可以参考以下代码片段:

图 2.37:多层感知器的代码片段
在这里,您可以看到我们正在使用 ReLU 激活函数,梯度下降求解器函数是 ADAM。我们使用的学习率是 0.0001。您可以通过参考以下图表来评估结果:

图 2.38:生成实际和预测价格图表的代码片段
此图表显示,所有数据记录的预测价格都遵循实际价格模式。您可以说我们的 MLP 模型在预测股票市场价格方面表现良好。您可以在以下 GitHub 链接中找到代码:github.com/jalajthanaki/stock_price_prediction/blob/master/Stock_Price_Prediction.ipynb。
摘要
在本章中,你学习了如何预测股票价格。我们介绍了可以帮助我们在这一领域的一些不同的机器学习算法。我们尝试了随机森林回归器、逻辑回归和多层感知器。我们发现多层感知器效果非常好。我真的很想讨论一些超出我们目前所做内容的事情。如果你认为通过使用新闻的情感分析和预测方法,我们现在可以以百分之百的准确性正确预测股票市场价格,那么你就错了。我们无法以百分之百的准确性预测股票价格。许多社区、金融机构和学术研究人员正在这个方向上努力,以创建一个高度准确的股票市场价格预测模型。这是一个活跃的研究领域。
所以,如果你对研究和自由职业感兴趣,那么你可以加入一些相当酷的社区。其中有两个社区非常受欢迎。其中一个就是 Quantopian(www.quantopian.com/)。在这个社区中,你可以提交你的股票价格预测算法,如果它优于其他竞争对手的算法,那么你将赢得现金奖励,如果你获得了你算法的许可,那么你将从通过你许可的算法完成的交易中获得一些利润。第二个社区是 numer.ai(numer.ai/)。这个社区与 Quantopian 类似。因此,这个应用的潜力是无限的。这两个社区都提供了一些优秀的教程。所以尝试一些不同的事物,希望你能想出一个出色的算法。
在下一章中,我们将探索零售或电子商务领域,并试图找出一些关于用户行为数据集和用户社交足迹的有趣事实。这将帮助我们了解公司应该如何改变他们的网站或网站上的某些功能。电子邮件营销活动成功的可能性有多大?哪些类型的用户会对这项活动做出回应?继续阅读这本书!我们将在下一章讨论所有这些内容。
第三章:客户分析
客户分析是一个过程,我们使用客户行为数据,通过市场细分和预测分析来推导出最重要的商业决策。市场细分是根据用户的行为和其他类型的共享特征将用户基础划分为子组的过程。这将帮助公司为每个用户细分提供定制化产品。此类分析的结果将引导公司以有效的方式扩展业务。公司也能获得更多的利润。有很多优势。我知道这只是一个关于市场细分简短的讨论,但请稍等片刻。我将在接下来的部分中为您提供所有必要的信息。
公司可以使用市场细分和预测模型生成的结果进行直接营销、选址、客户获取和客户关系管理。简而言之,借助客户分析,公司可以决定最优化和有效的营销策略以及增长策略。公司可以在有限的营销支出下取得显著成果。客户分析包括各种方法。您可以在以下图表中查看这些方法的名称:

图 3.1:客户分析的各种方法
在本章中,我们不会涵盖前图中给出的所有方法,但我们将介绍在行业中应用最广泛的方法。我们将构建一个客户细分应用。在本章中,我们将涵盖以下主题:
- 
介绍客户细分: - 介绍问题陈述
 
- 
理解数据集 
- 
构建客户细分基线方法: - 
实施基线方法 
- 
理解测试矩阵 
- 
测试基线方法的结果 
- 
基线方法的局限性 
- 
优化基线方法 
 
- 
- 
构建客户细分的修订方法: - 
实施修订方法 
- 
测试修订方法 
- 
修订方法的局限性 
- 
理解如何改进修订方法 
 
- 
- 
客户细分最佳方法: - 实施最佳方法
 
- 
测试最佳方法 
- 
不同领域的客户细分 
- 
概述 
我们将从客户细分开始。
介绍客户细分
在本节中,我们将详细介绍客户细分。最初,我仅提供了一个关于客户细分的基本介绍,以便您对这一术语有所了解。在这里,我们将更深入地了解客户细分,这将有助于我们在构建客户细分分析时进一步工作。
如前所述,客户细分是一个过程,我们将公司的消费者基础划分为子群体。我们需要通过使用一些特定的特征来生成子群体,以便公司以更少的营销支出销售更多产品。在继续前进之前,我们需要了解基础知识,例如,我所说的消费者基础是什么?我所说的细分是什么?我们是如何生成消费者子群体的?我们在细分消费者时考虑哪些特征?让我们逐一回答这些问题。
基本上,任何公司的消费者基础由两种类型的消费者组成:
- 
现有消费者 
- 
潜在消费者 
通常,我们需要将我们的消费者基础划分为子群体。这些子群体被称为细分市场。我们需要以这种方式创建群体,使得每个客户子群体都有一些共同的特征。为了解释如何生成子群体,让我给你举一个例子。
假设一家公司正在销售婴儿产品。那么,它需要提出一个消费者细分市场(消费者子群体),包括想要购买婴儿产品的消费者。我们可以借助一个简单的标准来构建第一个细分市场(子群体)。我们将包括那些家庭中有一个孩子并在过去一个月购买了婴儿产品的消费者。现在,公司推出了一款成本过高或高端的婴儿产品。在这种情况下,我们可以进一步将第一个子群体细分为月收入和社会经济状况。基于这些新的标准,我们可以生成第二个消费者子群体。公司将为第二个子群体的消费者定位高端和高端产品,而对于一般产品,公司将针对属于第一个子群体的消费者。
当我们有不同的细分市场时,我们可以设计定制化的营销策略以及适合特定细分市场客户的定制产品。这种按细分市场进行的营销将帮助公司以更低的营销费用销售更多产品。因此,公司会获得更多利润。这就是为什么公司现在使用客户细分分析的主要原因。客户细分在零售领域、金融领域以及基于客户关系管理(CRM)的产品等领域也被使用。我已经提供了一份在细分过程中可以考虑的基本特征列表。您可以在以下屏幕截图中参考它们:

图 3.2:客户细分中使用的基本特征列表
你可能会想知道公司是如何基于客户细分分析制定营销策略的。答案是公司正在使用 STP 方法来制定营销策略。什么是 STP 方法?首先,STP 代表细分-定位-定位。在这个方法中,有三个阶段。我们在每个阶段处理的问题如下所述:
- 
细分:在这个阶段,我们使用客户的个人特征以及前图中提供的特征来创建客户基础细分。一旦细分确定,我们就进入下一阶段。 
- 
目标:在这个阶段,营销团队评估细分,并试图了解哪种产品适合哪个特定的细分市场(s)。团队对每个细分进行这项练习,最后,团队设计出吸引一个或多个细分市场客户的定制产品。他们还将选择哪些产品应该提供给哪个细分市场。 
- 
定位:这是 STP 过程的最后阶段。在这个阶段,公司研究市场机会以及他们的产品向客户提供的价值。营销团队应该提出一个独特的销售主张。在这里,团队还试图了解特定细分市场如何看待产品、品牌或服务。这是公司确定如何最好地定位其产品的一种方式。公司的营销和产品团队创建一个价值主张,清楚地解释他们的产品如何优于任何其他竞争对手。最后,公司开始他们的宣传活动,以这种方式展示价值主张,让消费者对他们所得到的东西感到满意。 
我已经在以下图中总结了所有前面的要点:

图 3.3:STP 方法的总结
我们已经涵盖了客户细分的大部分基本部分。现在,是时候进入问题陈述了。
介绍问题陈述
如你所知,客户细分有助于公司保留现有客户以及吸引新的潜在客户。基于细分,公司可以为特定客户细分创建定制产品,但到目前为止,我们还不知道如何生成细分。这就是我们在本章中要关注的问题。你需要学习如何创建客户细分。我们可以为许多领域构建客户细分,例如电子商务、旅游、金融、电信等。在这里,我们将仅关注电子商务领域。
下面是对我们将要构建的电子商务客户细分应用的问题陈述、输入和输出的详细说明:
- 
问题陈述:我们客户细分应用的目标是为以下问题提供解决方案: - 
我们能否根据客户的购买模式将客户分类到特定的细分市场? 
- 
我们能否根据他们的细分情况预测他们未来会购买哪些类型的商品? 
 
- 
- 
输入:我们将使用包含 4000 名客户 1 年内购买列表的电子商务数据。 
- 
输出:第一个目标是我们需要将我们的消费者基础分类到合适的客户细分中。第二个目标是,我们需要根据客户的首次购买预测当前年和下一年度的购买。 
你可能会想知道我们如何通过细分来预测即将到来的购买。好吧,让我告诉你细分如何帮助我们!所以,我们不知道新客户的购买模式,但我们知道客户档案。我们还知道客户购买过哪些产品。因此,我们可以将客户放入一个细分中,其中所有其他客户都购买过类似的产品,并具有类似的档案。
让我给你举一个例子。比如说,一个人买了一本哈利·波特的书,这个人住在英国。客户的年龄组是 13-22 岁。如果我们已经生成了一个满足这些特征的客户细分,那么我们将把这个新客户放入那个特定的子组中。我们将推导出客户未来可能购买的商品列表。我们还将提供该子组中其他客户所享有的类似服务。
我们将用于开发电子商务领域客户细分的方法也可以用于其他领域,但每个领域的数据点(特征)将不同。在章节的后面,我们将讨论你可能考虑用于其他领域(如旅行、金融等)的数据点类型。我将提供其他领域的数据点列表,这将帮助你从头开始构建客户细分应用程序。
现在是时候理解用于构建电子商务领域客户细分的数据集了。
理解数据集
在数据科学中,找到合适的数据集是一项具有挑战性的任务。有时,你找到了一个数据集,但它不是合适的格式。我们的问题陈述将决定我们需要什么类型的数据集和数据格式。这类活动是数据清洗的一部分。
备注
数据清洗被定义为将数据从一种数据形式转换到另一种数据形式的过程。通过转换和映射,我们的目标应该是创建一个合适且有价值的数据集,以便用于开发分析产品。数据清洗也被称为数据整理,并且是任何数据科学应用的关键部分。
通常,电子商务数据集是专有数据集,你很少能获得真实用户的交易数据。幸运的是,UCI 机器学习仓库托管了一个名为在线零售的数据集。这个数据集包含了来自英国零售商的实际交易数据。
数据集描述
此在线零售数据集包含 2010 年 12 月 1 日至 2011 年 12 月 9 日之间的实际交易。所有交易均来自注册的非实体店在线零售平台。这些在线零售平台大多位于英国。这些在线零售平台销售独特的全场合礼物。这些在线零售平台的许多消费者是批发商。此数据集中有 532610 条记录。
下载数据集
您可以使用以下任一链接下载此数据集:
数据集的属性
这些是数据集中的属性。我们将查看每个属性的简要描述:
- 
InvoiceNo: 此数据属性表示发票号码。它是一个六位数的整数。记录为每笔交易唯一分配。如果发票号码以字母'c'开头,则表示取消。 
- 
StockCode: 此数据属性表示产品(项目)代码。它是一个五位数的整数。所有项目代码都是唯一分配给每个不同产品的。 
- 
Description: 此数据属性包含关于项目的描述。 
- 
Quantity: 此数据属性包含每笔交易中每种产品的数量。数据为数值格式。 
- 
InvoiceDate: 此数据属性包含发票日期和时间。它表示每次交易生成的日期和时间。 
- 
UnitPrice: 价格表示每单位产品的英镑价格。 
- 
CustomerID: 此列包含客户识别号码。它是一个五位数的整数,唯一分配给每个客户。 
- 
Country: 此列包含有关客户的地理信息。它记录了客户的国家名称。 
您可以参考以下截图中的数据集样本:

图 3.4:数据集的样本重编码
现在我们将开始构建客户细分应用程序。
构建基线方法
在本节中,我们将开始实现客户细分应用程序的基本模型。此外,我们将改进这个基线方法。在实现过程中,我们将涵盖执行该特定步骤所必需的概念、技术方面和重要性。您可以在以下 GitHub 链接找到客户细分应用程序的代码:github.com/jalajthanaki/Customer_segmentation
与本章相关的代码在一个单独的 iPython 笔记本中给出。你可以使用此 GitHub 链接访问笔记本:github.com/jalajthanaki/Customer_segmentation/blob/master/Cust_segmentation_online_retail.ipynb。
请参考 GitHub 上给出的代码,因为它将帮助你更好地理解事物。现在让我们开始实施!
实施基线方法
为了实现客户细分模型,我们的实现将包括以下步骤:
- 
数据准备 
- 
探索性数据分析(EDA) 
- 
生成客户类别 
- 
客户分类 
让我们从数据准备开始!
数据准备
这是在尝试构建任何分析应用程序时的一个基本步骤。首先,我们需要确保数据格式是适当的。如果不是,那么我们需要以这种方式准备我们的数据集,以便我们可以轻松构建我们的应用程序。在这个步骤中,我们将找出我们是否有一个高质量的数据集。我们还可以找出有关数据集的一些基本事实。
幸运的是,我们不需要更改我们电子商务数据集的格式,但我们将以这种方式探索数据集,以便我们可以找出数据集的质量。如果数据集的格式不正确,那么你需要决定数据集的格式,以便可以使用数据集进行任何类型的分析。你可以将数据记录转换为 CSV 格式、JSON 格式或 XML 格式。此外,我们还可以推导出关于数据集的一般事实,例如我们的数据集是否存在偏差,数据集是否包含任何空值,客户与Customer_ID的映射是否正确,以及他们的购买是否在数据集中正确记录,等等。
加载数据集
为了加载数据集,我们将使用 pandas 的read_csv API。你可以在下面的屏幕截图中找到给出的代码片段:

图 3.5:加载数据集的代码片段
如你所见,数据集的维度是(541909,8)。这意味着数据集中有 541,909 条记录和八个数据属性。我们已涵盖了这八个数据属性。
现在我们需要进行探索性数据分析(EDA),这可以帮助我们预处理我们的数据集。
探索性数据分析(EDA)
在本节中,我们需要检查数据集的统计属性并执行一些预处理步骤:
- 
删除空数据条目 
- 
删除重复数据条目 
- 
对各种数据属性进行 EDA 
删除空数据条目
首先,我们需要检查每个属性的 数据类型,并找出哪个列有空值。你可以参考以下屏幕截图中显示的代码片段:

图 3.6:探索数据集的代码片段
如代码所示,我们已经为每个数据属性生成了总空值数。我们还为每个数据属性生成了空值百分比。我们可以观察到,对于CustomerID列,大约有 25%的数据条目是空的。这意味着大约 25%的数据集中没有CustomerID值。这表明有许多条目不属于任何客户。这些都是异常数据条目。我们无法将它们映射到现有的客户 ID 上。因此,我们需要删除它们。您可以在以下截图中找到从数据集中删除空数据条目的代码片段:

图 3.7:删除空数据条目
删除重复数据条目
在这一步之后,我们将检查数据集中是否存在重复的数据条目。为了回答这个问题,我们将使用 pandas 的duplicate()函数。您可以参考以下截图中的代码片段:

图 3.8:删除重复数据条目
如您所见,我们找到了 5,225 个重复数据条目。因此,我们已经删除了它们。
现在我们来详细分析每个数据属性。
对各种数据属性进行 EDA
对每个数据属性进行 EDA 可以帮助我们更深入地了解数据集。稍后,我们将使用这些事实来构建一个准确的客户细分应用程序。
我们将按照以下顺序开始探索数据属性:
- 
国家 
- 
客户和产品 
- 
产品类别 
- 
定义产品类别 
国家
我们需要找出诸如数据集中有多少个国家等事实。为了回答这个问题,我们需要执行以下截图中的代码:

图 3.9:生成数据集中存在的县数的代码片段
我们还需要找出我们收到订单数量最多的国家。我们可以通过使用 pandas 的groupby()和count()函数来找出这一点。我们将按订单数量降序排序。您可以参考以下截图中的代码片段:

图 3.10:生成按国家划分的订单数量的代码片段
如前所述的代码片段所示,大多数订单来自英国客户。现在我们需要探索客户和产品变量。
客户和产品
在这里,我们大约有 40 万个数据项。我们需要知道这些数据条目中包含的用户和产品数量。我们将使用pandas库中的value_counts()函数。请查看以下截图中的代码片段:

图 3.11:探索客户和产品的代码
如上图所示,这个数据集包含了 4372 个用户的记录,他们购买了 3684 种不同的商品
我们已经推导出一些有趣的事实。在给定的数据集中,有 4,372 位客户购买了 3,684 种不同的产品。交易总数为 22,190。
我们还应该找出每个交易中购买了多少产品。为此,我们将使用InvoiceNo和InvoiceDate数据属性,并且我们将计算每个交易购买的产品数量。你可以参考以下屏幕截图中的代码片段:

图 3.12:探索每个交易产品数量的代码片段
如前述代码片段所示,我们可以得出以下观察结果:
- 
有一些用户在电子商务平台上只购买过一次,并且只购买了一件商品。这类用户的例子是 customerID 12346。
- 
有一些用户经常在每笔订单中购买大量商品。这类用户的例子是 customerID 12347。
- 
如果你查看 InvoiceNo数据属性,那么你可以看到有一个发票的前缀C。这个'C'表示特定的交易已被取消。
如我们所知,在我们的数据集中可能存在几个取消订单,我们需要计算与取消订单对应的交易数量。我们使用了一个简单的 lambda 表达式检查条件。现在我们将计算取消订单的百分比。你可以参考以下屏幕截图中的代码片段:

图 3.13:生成取消订单百分比的代码片段
让我们列出一些取消订单的条目,以便我们可以找出如何处理它们。请看以下屏幕截图:

图 3.14:取消订单列表
基本上,为了处理取消订单,我们需要采取以下步骤:
- 
如你所观察到的,如果订单被取消,那么将会有另一个交易,除了数量和发票日期外,其他方面大致相同。首先,我们需要检查这是否适用于所有条目。 
- 
我们可以通过简单的逻辑执行此检查操作。大多数情况下,取消订单的数量是负数,因此我们将检查是否存在一个数量相同(但为正数)且描述值相同的订单。 
- 
同时也有一些折扣条目,我们需要处理它们。我们将丢弃这些折扣条目。 
你可以参考以下屏幕截图中的代码,如下所示:

图 3.15:处理取消订单的代码
当我们运行前面的代码时,我们发现数据集中没有所有已取消交易的类似条目。为了克服这种情况,我们将在我们的数据框中创建一个新的变量,该变量指示交易是否已被取消。已取消订单有三种可能性:
- 
有一些交易在没有对应项的情况下被取消了。其中一些可能是因为购买订单是在 2010 年 12 月之前执行的。我们拥有从 2010 年 12 月到 2011 年 12 月的数据集。 
- 
有一些订单是与恰好一个对应项一起取消的。我们也将考虑这些订单。 
- 
有些条目是可疑的。我们将检查是否存在至少一个与确切数量相同的对应项。如果存在,则我们可以将这些条目标记为可疑。 
您可以参考以下截图中的代码:

图 3.16:生成已取消订单标志的代码片段
如前述代码片段所示,有 7,521 条条目显示了带有对应项的已取消订单。有 1,226 条条目显示了没有对应项的已取消订单。为了简化,我们将删除所有与已取消订单相关的条目。删除这些记录的代码在以下截图给出:

图 3.17:删除已取消订单的代码片段
现在,让我们根据股票代码分析条目,因为我们知道在识别已取消订单的过程中,我们根据股票代码 D发现了折扣商品。因此,首先,我们将列出所有股票代码及其含义。您可以参考以下截图:

图 3.18:股票代码的代码片段
现在,让我们专注于单个订单的定价。在给定的数据集中,单个客户的订单已被拆分为几行。我所说的几行是什么意思?为了理解这一点,请参考以下截图:

图 3.19:理解订单数据条目
我们数据集中的每个条目都表示一种产品的价格。如果一个订单包含不同的产品,并且由同一个客户下单,那么该订单将有多个条目。数据条目的数量取决于订单中有多少种不同的产品。正如您在前面的图中看到的,一个订单中包含了三种不同的产品。我们需要获取每个订单的总价格。为了实现这一点,我们将添加一个名为TotalPrice的列,它给出了订单的总价值或单个订单的篮子价格。计算TotalPrice的主要逻辑是我们将UnitPrice与净数量相乘。我们通过从总量中扣除取消的数量来获得净数量。请查看以下截图所示的代码片段:

图 3.20:获取 TotalPrice 的代码
一旦我们获得总价格,我们将为单个订单生成金额总和,然后根据发票数据对条目进行分组。我们只列出篮子价格大于 0 的数据条目。实现这一点的代码如下截图所示:

图 3.21:基于发票日期生成篮子价格的代码
现在是时候了解给定数据集中订单金额的分布情况了。我所说的订单金额分布是什么意思呢?嗯,我们应该了解数据集中所有订单的价格,并根据所有订单的金额范围进行分类。这将帮助我们得出数据集中金额超过 200 英镑的订单数量。这也有助于我们识别金额低于 100 英镑的订单数量。这类信息有助于我们了解基于订单数量的数据分布。这将给我们一个电子商务平台上销售的基本情况。以下截图显示了基于订单金额生成数据分布的代码片段:

图 3.22:基于订单金额生成数据分布的代码片段
您可以如下看到此数据分布的图示:

图 3.23:数据分布的图示
如我们所见,大约 65%的订单金额超过 200 英镑。我们已经对订单进行了详细的探索。现在让我们开始分析产品类别。
产品类别
在本节中,我们将对产品相关的数据属性进行 EDA。我们将在本节中包含以下类型的分析:
- 
分析产品描述 
- 
定义产品类别 
- 
描述聚类内容 
分析产品描述
在本节中,我们将使用两个数据属性。我们将使用StockCode数据属性,它包含每个产品的唯一 ID。我们还将使用Description数据属性来对产品进行分类。让我们从产品描述开始。
首先,我们将定义一个函数,该函数将数据框作为输入,然后我们将执行以下操作:
- 
我们将从产品描述中提取名称(名词)。 
- 
然后,我们将生成提取名称的词根形式。我们将名称的词根作为键,所有相关名称作为其值。我们将使用 NLTK 库中的词干提取器来完成这一步骤。词干提取器基本上通过删除后缀和前缀来生成单词的词根形式。 
- 
我们将计算名称根的频率,这意味着我们将计算每个名称的词根形式出现的次数。 
- 
如果各种名称有相同的根,那么我们将根形式视为关键词标签。 
您可以在下面的屏幕截图中查看此函数的代码:

图 3.24:从产品描述生成关键词的函数代码片段
现在我们需要调用这个函数并传入输入的数据框。您可以在下面的屏幕截图中查看提供的代码片段:

图 3.25:实际生成关键词的代码片段
在这里,我们正在返回三个变量:
- 
Keyword:这是提取的名称列表
- 
Keywords_roots:这是一个字典,其中键是名称的根,值是与根名称关联的名称列表。
- 
Count_keywords:这是一个字典,用于跟踪每个名称的频率。计数表示特定名称在描述中出现的次数。稍后,我们将字典转换为列表。
现在,让我们绘制关键词与其频率的图表。代码如下所示:

图 3.26:生成频率图的代码片段
如前图所示,单词(指名词或名称)"heart"在产品描述中出现的次数最多。您可能会想知道生成这个单词频率的意义是什么。嗯,我们正在使用它来对产品进行分类。现在,让我们看看如何提出产品类别。
定义产品类别
在这里,我们将获取产品类别。我们已经获得了 1400 多个关键词,最频繁出现的名称出现在 200 多个产品中。现在我们需要删除不太重要的单词。我们可以观察到一些无用的单词,例如颜色名称,并将它们丢弃。因此,我们将考虑在数据集中出现超过 13 次的单词。您可以在下面的屏幕截图中查看代码片段:

图 3.27:保留重要单词的代码片段
现在,我们需要对数据进行编码。在这里,我们有文本数据,我们需要将其转换为数值格式。为此,我们将使用 one-hot 编码。one-hot 编码是一个简单的概念。为了理解它,请参考给出的矩阵 x。看一下以下截图:

图 3.28:理解 one-hot 数据编码的表格
如果某个单词出现在产品描述中,则系数的值为 1,如果单词没有出现在产品描述中,则系数的值为 0。您可以参考以下截图:

图 3.29:one-hot 数据编码的直观示例
如您所见,这种数据编码是一种二进制类型的向量化,因为我们放置的是零或一。编码后,我们将为每个单词得到一个稀疏向量。用通俗易懂的话来说,这种向量化表示了单词在产品描述中的存在。
现在,让我们根据价格范围创建产品的组或聚类。为此,我们将使用我们生成的关键词列表,检查产品描述中是否包含关键词中的单词,并取UnitPrice的平均值。您可以参考以下截图中的代码:

图 3.30:基于价格范围生成产品组的代码片段
现在,我们将创建产品的聚类。我们将使用 k-means 聚类算法。我们还将使用 scikit-learn 库来实现 K-means 聚类算法。scikit-learn 中的算法使用欧几里得距离。在我们的情况下,这并不是最佳选择。我们应该使用汉明距离。最适合的库是Kmods,但这个库并不是所有操作系统都可用,所以我们必须使用 scikit-learn 库。我们需要定义可以完美表示数据的簇数量。我们将得出理想的簇数量,然后使用轮廓分数。
您可以通过使用本书的链接了解 k-means 聚类算法的工作原理:www.packtpub.com/big-data-and-business-intelligence/python-natural-language-processing,参考第八章的 K-means 聚类部分第八章,机器学习解决 NLP 问题。
让我们退一步,首先了解轮廓分数。轮廓系数是通过两个因素计算的。第一个是每个样本在数据集中的平均簇内距离(a),第二个是平均最近簇距离(b)。因此,方程如下:
(b-a) / max (a, b)
b表示样本与其不属于的最近簇的距离。当标签数量为2<= n_labels <= n_samples –1时,此评分有效。此评分的最佳可能值为 1,最差值为-1。值为 0 表示我们有重叠的簇。负值表示样本已被分配到错误的簇。请参考以下截图中的代码片段:

图 3.31:使用轮廓评分选择理想簇数量的代码片段
在这里,我们使用 scikit-learn API 实现了代码。正如我们所见,超过五个簇时,簇中可能包含非常少的元素,因此我们选择将产品分类为五个簇。我们将尝试提高轮廓评分。为此,我们将遍历数据集。你可以参考以下截图中的代码:

图 3.32:改进轮廓评分的代码片段
现在,让我们继续到描述簇的内容部分,这有助于我们了解产品被分类到特定簇中的效果如何。
描述簇的内容
在本节中,我们将分析产品簇的性质。这里将分为三个小节:
- 
轮廓簇内评分分析 
- 
使用词云进行分析 
- 
主成分分析(PCA) 
在我们深入分析之前,我们需要检查每个簇中的产品数量。为此,我们将使用以下截图中的代码片段:

图 3.33:计算每个簇产品数量的代码片段
如输出所示,有 1,009 个产品属于第 3 簇,而只有 470 个产品属于第 4 簇。我们将开始对这些五个簇及其元素进行深入分析。首先,我们将从轮廓簇内评分分析开始。
轮廓簇内评分分析
在本节中,我们将检查每个元素的簇内评分。我们将对轮廓簇内评分进行排序。排序后,我们将绘制一个图表,其中x轴代表轮廓系数值,y轴代表簇标签。我们为所有样本生成轮廓簇内评分。我们绘制这个图表是为了根据轮廓簇内评分选择n_clusters的最佳值。
由于我们之前已经生成了轮廓簇内评分,我们知道n_clusters = 5是我们理想的选项,因此我们将以图形方式表示这些簇。你可以参考以下截图中生成图表的函数:

图 3.34:轮廓内聚群得分分析函数的代码片段
执行并调用此函数后,我们可以获得以下截图显示的图表:

图 3.35:轮廓内聚群分析的代码片段和图表
注意
注意,在这里,我们获得了最优n_cluster值的图表。在我们的例子中,这个值是 5。
使用词云进行分析
在本节中,我们将根据关键词分析聚类。我们将检查每个聚类包含哪些单词。为此分析,我们将使用词云库。你可能想知道为什么我们使用这种分析方法。在我们的聚类中,我们期望类似的产品属于一个聚类。作为人类,我们知道语言。当我们看到整个聚类的单词时,我们可以很容易地判断我们的聚类是否包含类似的产品。我们将生成足够直观的图表,以便我们判断聚类的准确性。
你可以参考以下截图中的代码片段:

图 3.36:生成词云的代码片段
你可以参考以下截图中的代码片段:

图 3.37:生成词云图表的代码片段
你可以参考以下截图中的图表:

图 3.38:所有五个聚类的词云图表
从前面的图表中,我们可以得出以下结论:
- 
第 2 号聚类包含所有与礼物相关的单词,如圣诞节、包装、礼物、卡片等。 
- 
第 4 号聚类包含所有与奢侈品和珠宝相关的单词。因此,项链、银、蕾丝等关键词都存在于这个聚类中。 
- 
有些单词在所有聚类中都存在,因此很难清楚地区分它们。 
现在让我们跳到下一节,我们将进行主成分分析。
主成分分析(PCA)
为了检查所有聚类是否确实具有不同的值,我们需要关注它们的组成。正如我们所知,关键词的一热编码矩阵具有大量的维度或变量。可能存在这样的情况,由于变量的数量庞大,我们的聚类算法可能会过度拟合数据集。首先,我们需要减少变量的数量,但我们不能随意减少。我们需要选择最能代表数据集大部分特征的最重要的变量。减少变量数量的逻辑过程称为降维。
为了实现这一点,我们将使用 PCA,这是一种统计技术,我们将执行正交变换,将高度相关的数据样本集转换为线性不相关的变量集,这些变量被称为主成分。所以,基本上,我们将使用 PCA,因为我们想减少我们迄今为止考虑的变量数量。PCA 是降维的著名技术。通过使用 PCA,我们可以避免过拟合问题。
现在,你可能想知道可以使用 PCA 的情况,如下所示:
- 
如果我们想减少变量的数量(特征或维度的数量),但又无法确定哪些变量可以考虑,哪些不能 
- 
如果我们想确保我们的变量彼此独立 
- 
如果我们愿意使我们的独立变量不那么可解释 
在我们的案例中,我们需要减少变量的数量。为此,我们将实施以下截图中的代码:

图 3.39:实现 PCA 的代码片段
如你所见,我们在前面的代码中正在检查每个成分解释的方差量。我们需要考虑超过 100 个成分来解释我们数据集 90%的方差。
在这里,我将考虑有限数量的成分,因为这种分解只是为了可视化数据。你可以参考以下截图中的代码:

图 3.40:生成 PCA 分解图形的代码片段
如你所见,我们使用了PCA(n_components=50)的 PCA 成分,并将值存储在数据框mat中,我们可以在未来使用。
前面代码的输出是图形形式。因此,你可以参考以下截图:

图 3.41:每个聚类的 PCA 图形
在这里,我们使用了tight_layout,这是图形缩小一点的原因。
到目前为止,我们已经进行了足够的 EDA,以帮助我们生成对数据集的基本洞察。现在我们将进入下一节,我们将开始构建客户类别或客户细分。我们将考虑我们迄今为止实施的所有发现。
生成客户类别
如你所知,我们的第一个目标是开发客户细分。从本节开始,我们将主要关注我们如何进行客户细分。到目前为止,我们已经对订单、产品、价格等方面进行了分析。在这里,我们的主要重点是依据我们在 EDA 过程中获得的洞察来生成客户类别。
这些是我们将遵循的步骤来开发客户类别:
- 
数据格式化: - 
分组产品 
- 
数据集拆分 
- 
分组订单 
 
- 
- 
创建客户类别: - 
数据编码 
- 
生成客户类别 
 
- 
现在我们来看看在每个步骤中我们将做什么。
格式化数据
如前所述,我们将使用我们在 EDA 过程中生成的发现。在前一节中,我们为产品生成了五个聚类。为了执行剩余的分析,我们将使用这个已经生成的关键词列表、矩阵和聚类列表。通过使用它们,我们将生成一个新的分类变量categ_product。这个变量表示每个产品的聚类。您可以参考以下截图所示的代码片段:

图 3.42:生成新分类变量 categ_product 的代码片段
如您所见,新变量表示每个数据条目的聚类编号。现在让我们对产品进行分组。
分组产品
您可能会想知道,如果我们已经开发了产品的类别,那么为什么我们在这里执行分组步骤。在这里,我们将以这种方式进行分组,以便我们知道在每个产品类别中花费了多少钱。为此,我们将添加五个新变量,例如,categ_0、categ_1、categ_2、categ_3 和 categ_4。您可以参考以下截图显示的代码片段:

图 3.43:生成每个产品类别的花费金额的代码片段
订单被分割成多个条目,因此我们需要使用篮子价格。这次,我们将合并篮子价格以及它在五个产品类别中的分布方式。我们将把所有这些信息放入新的数据框中。请参考以下截图:

图 3.44:获取五个聚类篮子价格分布的代码片段
最后,我们有每个订单的篮子价格,我们也知道价格在五个聚类中的分布。新的数据框是basket_price。现在让我们进入下一节。
分割数据集
在本节中,我们将使用包含过去 12 个月数据条目的数据框basket_price。本应用的第二个目标是根据客户的首次网站访问或购买预测客户购买行为。因此,为了现在实现这个目标,我们将分割数据集。我们将使用 10 个月的数据集进行训练,2 个月的数据集进行测试。我包括这一步是因为稍后我们可以使用这些训练和测试数据集,您也可以轻松地使用新的数据框。您可以参考以下截图给出的代码:

图 3.45:使用时间分割数据集的代码片段
现在我们将根据篮子价格分布对客户及其订单进行分组。
分组订单
在这里,我们将合并客户及其订单,以便我们可以了解哪个客户下了多少订单。我们还将生成最小订单金额、最大订单金额和平均订单金额。请参考以下截图中的代码:

图 3.46:生成每个客户订单统计信息的代码片段
我们还将生成两个变量,表示自最后一次购买和第一次购买以来过去的天数。这些变量的名称是FirstPurchase和LastPurchase。请参考以下截图中的代码片段:

图 3.47:生成最后和第一次购买过去天数的代码片段
我们感兴趣的客户类别是只下过一次订单的客户。我们的主要目标之一就是以这种方式定位这些客户,以便我们能够保留他们。我们需要获取属于这个类别的客户数量数据。为此,请参考以下截图中的代码:

图 3.48:生成只有一个购买客户的数量的代码片段
从前面的代码中,我们可以发现 40%的客户基础只下过一次订单,我们需要保留他们。
现在让我们构建客户类别。
创建客户类别
基本上,我们将在这里生成客户细分。因此,我们将在这个部分努力实现章节的第一个目标。我们将基于客户的购买模式构建客户细分。本节有两个步骤:
- 
数据编码 
- 
生成客户类别或客户细分 
我们将从数据编码开始。
数据编码
我们将生成包含我们迄今为止所执行的所有操作的汇总的 dataframe。这个 dataframe 的每一条记录都与单个客户相关联。我们可以使用这些信息来描述各种类型的客户。
我们生成的 dataframe 有不同的变量。所有这些变量都有不同的范围和变化。因此,我们需要生成一个矩阵,其中这些数据条目被标准化。你可以参考以下截图中的代码:

图 3.49:为每个客户生成汇总数据条目的代码片段
在创建客户细分之前,我们需要创建基础。这个基础应该包括重要变量。我们需要包括少量重要变量。为了选择重要变量,我们将使用主成分分析。这样我们就可以准确地描述细分。我们将为此任务使用 PCA。代码片段如下截图所示:

图 3.50:用于生成客户细分的 PCA 代码片段
在这里,我们可以看到有八个主成分。现在让我们进入下一节,我们将在这里生成客户细分。
生成客户类别
我们将使用 k-means 聚类算法来生成细分。聚类数量将通过使用轮廓分数来推导。我们之前已经使用过轮廓分数,通过使用相同的方法,我们可以推导出聚类数量。在这里,根据轮廓分数,我们获得了 11 个聚类。您可以参考以下截图:

图 3.51:生成客户细分代码片段
如您所见,细分的大小存在很大差异,因此我们需要使用 PCA 分析来分析聚类的成分。
PCA 分析
我们将在这里使用六个成分。11 个聚类的 PCA 代码片段和图形表示在以下截图给出:

图 3.52:实现 PCA 和生成图表的代码片段
作为输出,以下图表已经生成:

图 3.53:客户细分的 PCA 图表
我在这里只显示了三个图表。在代码中,有九个图表。当您运行代码时,您可以看到它们全部。请注意,第一个成分将最小的聚类与其他聚类分开。对于这个数据集,我们可以说,总会有一种表示,其中两个细分将看起来是不同的。现在让我们获取轮廓分数。
使用轮廓分数分析聚类
在本节中,我们将为每个聚类生成轮廓分数。这将表明数据样本分离的质量。您可以参考以下截图中的代码片段和图表:

图 3.54:生成轮廓分数图表的代码片段
从前面的图表中,我们可以确保所有聚类都是不相交的。现在我们需要更多地了解每个聚类的客户习惯。为此,我们将添加定义每个客户所属聚类的变量。
为了做到这一点,我们将生成一个新的数据框,selected_customers。在生成新的数据框后,我们将平均数据框的内容。这将为我们提供平均篮子价格、总访问量等。您可以参考以下截图中的代码:

图 3.55:存储客户习惯的代码片段
现在我们需要重新组织数据框的内容。我们将考虑以下两点:
- 
我们需要根据每个产品类别的花费金额重新组织数据 
- 
之后,我们将根据总花费金额重新组织内容 
您可以查看以下截图中的实现:

图 3.56:重新组织数据集的代码片段
如您所见,我们已经获得了每个细分市场的客户行为。现在我们可以根据这些特征进行推荐。我们可以根据生成的数据设计营销活动。
特定的营销策略可以应用于属于第 4 个和第 8 个聚类的客户。我们应该向第 1 个聚类的客户推荐高端产品。
到目前为止,我们已经实现了我们的第一个目标。现在是我们瞄准第二个目标的时候了。让我们开始吧!
客户分类
在我们开始之前,让我们回顾一下我们的目标。这有助于您更清晰地理解事物。目标是我们要构建一个分类器,将客户分类到上一节中建立的不同客户细分市场。我们还需要一个额外的功能。我们的分类器应该在客户第一次访问平台时生成这个分类结果。为了实现这种功能,我们将使用各种监督机器学习算法。我们将使用 scikit-learn API。
为了开发基线分类器,我们需要执行以下步骤:
- 
定义辅助函数 
- 
将数据分为训练集和测试集 
- 
实施机器学习(ML)算法 
定义辅助函数
基本上,我们定义一个名为 class_fit 的类,然后定义各种函数,这些函数在训练机器学习模型时可以帮助我们。这些是我们将使用的辅助函数:
- 
train函数帮助我们训练模型
- 
predict函数帮助我们预测测试数据集或新数据样本的结果。
- 
grid_search函数帮助我们找到合适的超参数和交叉验证(CV)折的值。
- 
grid_fit函数帮助我们使用交叉验证训练模型并生成最佳超参数。
- 
grid_predict函数帮助我们生成预测以及准确度评分。
您可以参考以下截图所示的代码片段:

图 3.57:辅助函数的代码片段
现在,让我们进入下一节。
将数据分为训练集和测试集
我们将使用存储在 selected_customers 数据框中的数据。您可以看到我们将应用机器学习算法的数据集的一些条目。请查看以下截图:

图 3.58:数据集的样本条目
如您所见,我们将预测新客户的聚类编号,因此我们将该值存储为 Y,而 mean, categ_0 到 categ_4 等列被用作机器学习模型的输入特征,因此我们将它们存储在 X 变量中。现在我们需要将此数据拆分为训练集和测试集。为此,我们使用 sklearn API 的 train_test_split()。我们使用 80%的数据进行训练,20%的数据进行测试。请看以下截图:

图 3.59:将数据集拆分为训练集和测试集的代码片段
我们已经有了训练集和测试集。现在,我们需要开始实现机器学习算法。
实现机器学习(ML)算法
对于基线方法,我们将实现支持向量机(SVM)分类器。我们将使用之前定义的辅助函数。在这里,我将创建一个类的实例并调用之前声明的函数。请看以下截图中的代码片段:

图 3.60:使用 SVM 分类器训练模型的代码片段
如代码片段所示,svc 是类实例。我们正在使用线性 SVM。我们使用了 grid_search 来搜索最优超参数以及获取交叉验证的折数。之后,我们调用了 grid_fit 方法,该方法用于使用我们的训练数据集来训练机器学习模型。
这是我们的基线方法实现方式。现在让我们测试一下结果。
理解测试矩阵
我们将使用混淆矩阵和学习曲线来评估机器学习模型。所以在开始测试之前,我们需要了解混淆矩阵和学习曲线是什么。我们将逐一介绍这些概念。
混淆矩阵
当我们实现多类分类器时,自然会有多个类,并且属于所有类的数据条目数量不同,所以在测试时,我们需要知道分类器是否对所有类都表现良好,或者它是否偏向某些类。这种分析可以使用混淆矩阵来完成。它将记录有多少数据条目被正确分类,有多少被错误分类。
让我们举一个例子。假设有 10 个属于一个类的数据条目,该类的标签为 1。现在当我们从我们的机器学习模型生成预测时,我们将检查 10 个条目中有多少被预测为类标签 1。假设有六个数据条目被正确分类并得到类标签 1。在这种情况下,对于六个条目,预测标签和真实标签是相同的,所以准确率是 60%,而对于剩余的数据条目,机器学习模型将它们错误分类。机器学习模型预测的类标签不是 1。
从前面的例子中,你可以看到混淆矩阵让我们了解有多少数据条目被正确分类,以及有多少被错误分类。我们可以探索分类器的类别准确度。请看以下截图:

图 3.61:混淆矩阵的示例
现在让我们看一下学习曲线。
学习曲线
我们在这里绘制了两条线。一条线表示训练分数,另一条线表示测试分数。在这里,训练和测试分数决定了不同训练数据集大小的交叉验证训练和测试分数。通过使用这个学习曲线,我们可以监控 ML 模型是否正确收敛。CV 分数和训练分数都将帮助我们确定训练是否朝着正确的方向进行,或者 ML 模型是否过度拟合或欠拟合。随着数据集大小的增加,如果 CV 分数和训练分数达到低分,那么这意味着训练没有以正确的方式进行。然而,如果 CV 分数和训练分数随着数据集大小的增加而增加,那么这意味着训练正在朝着正确的方向进行。请参考以下截图:

图 3.62:学习曲线的良例和劣例
现在我们已经理解了测试矩阵背后的基本直觉,我们可以开始测试我们的基线方法。
测试基线方法的结果
在本节中,我们将使用以下方法测试基线模型:
- 
生成分类器的准确度分数 
- 
生成分类器的混淆矩阵 
- 
生成分类器的学习曲线 
生成分类器的准确度分数
首先,我们将使用 grid_predict 来生成测试数据集的准确度分数。我们将检查 SVM 算法的准确度。为此,以下截图给出了代码片段:

图 3.63:生成准确度分数的代码片段
对于基线方法,我们得到了 79.50%的精确度。现在让我们通过混淆矩阵来看看预测的质量。
生成分类器的混淆矩阵
现在我们将生成混淆矩阵,这将给我们一个关于哪个类别被正确分类以及哪些类别大多数时候错误分类数据的公平概念。要生成混淆矩阵,请参考以下截图中的代码:

图 3.64:生成混淆矩阵的代码片段
我们使用了 sklearn 的confusion_matrix API。为了绘制图表,我们将定义一个名为plot_confusion_matrix的方法。借助前面的代码,我们已经生成了以下截图中的混淆矩阵:

图 3.65:基准方法的混淆矩阵
如您所见,分类器能够准确地将数据分类为标签 0、2、4、6 和 10,而对于标签 1、5、7 和 8,分类器的表现并不理想。
让我们绘制基准方法的 learning curve。
生成分类器的学习曲线
学习曲线表明分类器是否面临过拟合或欠拟合的问题。plot_learning_curve 方法用于绘制分类器的学习曲线。您可以通过以下截图中的代码片段进行参考:

图 3.66:生成基准方法学习曲线的代码片段
学习曲线显示在以下截图:

图 3.67:基准方法的学习曲线
如您所见,当我们增加样本大小时,CV 曲线收敛到相同的极限。这意味着我们具有低方差,并且没有过拟合的问题。方差是表示如果我们提供不同的训练数据集,我们的目标函数将如何变化的价值。理想情况下,目标函数的值是通过机器学习算法从训练数据集中推导出来的,然而,如果我们使用另一个训练数据集,估计函数的值不应该变化太多。预计估计函数会有轻微的变化(轻微的方差)。在这里,准确度分数具有低偏差,这意味着模型也没有面临欠拟合的问题。
基准方法的问题
在本节中,我们将讨论我们在基准方法中遇到的问题,以便优化当前方法。问题如下:
- 
精确度分数较低。有改进的空间。 
- 
我们需要尝试其他机器学习算法,以便进行比较。稍后,如果有需要,我们可以构建投票机制。 
在改进方法中,基本上我们需要尝试各种机器学习算法,以确保我们知道哪些算法可以使用,哪些不应该使用。
优化基准方法
在本节中,我们将考虑所有问题,并讨论我们将通过哪些方法来提高分类器的准确率。如前节所述,我们需要实现其他机器学习算法。以下是我们要用改进方法实现的六个算法:
- 
逻辑回归 
- 
K-最近邻 
- 
决策树 
- 
随机森林 
- 
AdaBoost 分类器 
- 
梯度提升分类器 
根据所有先前算法的精确度分数,我们将决定哪些算法可以使用,哪些不能使用。
不浪费任何时间,让我们开始实现改进方法。
构建改进方法
在本节中,我们将实现各种机器学习算法,检查它们的精确度得分,并监控它们的学习曲线。总共有六种机器学习算法将被用于确定哪一种最适合我们的应用。
实现改进的方法
在本节中,我们将实现逻辑回归、K 最近邻、决策树、随机森林、Adaboost 和梯度下降。为了实现这一点,我们将使用我们之前构建的辅助类。您可以在下面的屏幕截图中查看提供的代码片段:

图 3.68:使用各种机器学习分类器进行训练的代码片段
我们已经为所有分类器生成了精确度得分。我们可以看到随机森林和梯度提升分类器具有很高的精确度。然而,我们还没有检查它们的学习曲线。首先,我们将检查它们的学习曲线,然后判断是否有分类器面临过拟合或欠拟合的问题。
测试改进的方法
在本节中,我们将检查所有分类器的学习曲线。您可以参考以下屏幕截图中的学习曲线:

图 3.69:各种机器学习分类器的学习曲线
您可以看到所有分类器都得到了适当的训练。没有欠拟合或过拟合的问题。随着数据量的增加,得分也在提高。
改进方法的问题
这个方法的主要问题是,我们需要决定使用哪种算法,以及应该停止使用哪种算法。我们将放弃 Adaboost 分类器,因为它的精确度得分太低。
这里还有一个需要注意的问题。没有一种分类器对所有类别标签都有效。可能有一种分类器对类别标签 0 有效,而另一种可能对类别标签 8 有效。我相信,我们不应该放弃任何其他分类器。我们需要提出一个投票机制。用更技术性的话来说,我们需要开发一个集成模型,以便我们的预测质量高且准确。
现在,我们将探讨我们的方法,以便构建一个投票分类器,它可以给我们提供最佳可能的准确度。
理解如何改进改进的方法
如前所述,为了改进改进的方法,我们将使用投票机制。为此,我们将使用 scikit-learn 投票分类器 API。首先,我们将使用网格搜索来为每个分类器生成适当的超参数。然后,我们将使用 scikit-learn 的投票分类器 API 来训练模型。方法很简单,让我们开始实现它。
最佳方法
在这种方法中我们将生成的分类器模型应该给我们提供最佳可能的准确度。我们之前已经讨论了这种方法。如果你对集成机器学习模型还不熟悉,那么让我给你一个基本的直观想法。用通俗易懂的话来说,集成机器学习模型基本上是使用各种机器学习算法的组合。将各种机器学习模型组合在一起有什么好处呢?我们知道没有单一的分类器可以完美地分类所有样本,所以如果我们结合多个分类器,那么我们可以获得更高的准确度,因为一个分类器的问题可以被另一个分类器克服。正因为如此,我们将使用投票分类器,这是一种集成分类器。
实施最佳方法
如你所知,我们使用网格搜索和投票分类器 API 来实现最佳方法。正如讨论的那样,首先,我们将使用网格搜索来获得最佳的超参数,然后使用投票分类器 API。逐步实现如下屏幕截图所示:

图 3.70:最佳方法的代码片段
如你所见,这种方法达到了 90%的精确度。这次,我们需要在我们的两个月保留数据集上测试这种方法,以便我们可以找出投票分类器在未见数据集上的表现如何。
在下一节中,我们将测试这种方法。
测试最佳方法
我们在 20%的数据集上测试我们的机器学习模型,这个数据集在我们开始训练之前就已经被留出来了。这个数据集对我们来说相当于一个开发数据集。对于训练,我们考虑了 10 个月的数据集。现在是我们测试模型在保留数据集上的时间了。在这里,我们的保留数据集由 2 个月的数据条目组成。以下是我们需要实施的步骤:
- 
将保留数据集转换为训练数据集的形式 
- 
将转换后的数据集转换为矩阵形式 
- 
生成预测结果 
让我们从第一步开始。
将保留数据集转换为训练数据集的形式
首先,我们需要将存储在set_test数据框中的数据转换为训练数据集的形式。为此,我们将将其副本存储在新数据框中,命名为basket_price。
现在,我们将使用与基线方法相同的操作来生成用户特征数据。不用担心。当你看到代码时,你会记得我们之前执行的步骤。在转换数据集后,我们将将其存储在数据框transactions_per_user中。你可以参考以下屏幕截图所示的代码片段:

图 3.71:将测试数据集转换为与训练数据集相同形式的代码片段
现在,让我们将数据集转换为矩阵形式。
将转换后的数据集转换为矩阵形式
我们的分类器以矩阵作为输入,因此我们需要将转换后的数据集转换为矩阵格式。为此,我们将使用以下截图所示的代码片段:

图 3.72:将测试数据集转换为矩阵格式的代码片段
我们在这里使用基本的类型转换。
生成预测
在本节中,我们将使用投票分类器生成精确分数。因此,为了生成测试数据集的预测,我们需要使用以下截图给出的代码片段:

图 3.73:生成测试数据集精确分数的代码片段
如你所见,我们将在我们的保留语料库上达到 76%的准确率。这很好,因为我们只用了 10 个月的数据来构建这个模型。通过使用 10 个月的数据库,我们达到了这个领域的最佳可能准确率。如果我们考虑更多的数据记录,我们仍然可以改进结果。这可以成为你们考虑更多数据集并改进结果的一个练习。
各领域的客户细分
注意,我们在这里考虑的是电子商务数据,但你也可以考虑其他各种领域的数据集。你可以为提供旅行服务、金融服务等公司构建客户细分。数据点会因领域而异。
对于旅行服务,你可以考虑用户使用旅行平台预订航班或房间的频率。人口统计信息和专业信息有很大帮助,例如,用户使用促销优惠的次数。用户活动数据同样重要。
如果你正在为金融领域构建细分应用,那么你可以考虑以下数据点:账户持有人的交易历史,例如,使用借记卡或信用卡的频率,每月收入,每月支出,客户在银行账户中保持的平均余额,账户用户的类型,客户的专业信息,等等。对于这两个领域,还有其他一些常见的数据点可以考虑,例如在网站或移动应用上花费的时间。
目前,我将限制自己在这两个领域,但你可以为电信领域、营销领域、教育领域、娱乐领域等执行客户细分。
摘要
我们迄今为止开发的所有分析模型对于运营一家成功的业务至关重要。在本章中,我们根据客户的行为开发了客户细分。为了做到这一点,我们使用了各种算法,例如支持向量机(SVM)、线性回归、决策树、随机森林、梯度提升、基于投票的模型等等。通过使用基于投票的模型,我们实现了最佳可能的准确率。客户细分分析对于小型和中型组织非常重要,因为这些分析有助于它们优化其营销策略,以及显著降低客户获取成本。我为客户流失分析开发了代码,可在以下链接找到:github.com/jalajthanaki/Customer_churn_analysis,以及客户终身价值分析在以下链接:github.com/jalajthanaki/Customer_lifetime_value_analysis。您可以参考它们来了解更多关于客户分析的信息。您可以在以下链接阅读有关客户分析的内容:github.com/Acrotrend/Awesome-Customer-Analytics。
在即将到来的章节中,我们将构建一个针对电子商务产品的特定推荐系统。我们将构建一个推荐应用,根据用户在平台上的浏览和购买活动向他们推荐书籍。我们将实施各种技术来构建最佳可能的推荐引擎。所以请继续阅读!
第四章:电子商务推荐系统
在前三章中,我们介绍了很多可以用来构建各种类型分析产品的技巧和窍门。在本章中,我们将构建电子商务领域的推荐引擎。让我们回顾一下推荐系统的一些背景。然后,我们将讨论本章试图解决的问题陈述。
让我们从现实生活中的一个相关例子开始。我们几乎每天都在 YouTube 上浏览视频,对吧?假设你昨晚在 YouTube 上看到了一些关于摇滚音乐的视频。今天早上,当你打开 YouTube 时,你可能会发现有几个推荐的视频频道,它们有关于摇滚音乐的好视频。实际上,YouTube 是根据你的观看习惯改变推荐的。你想知道这个算法是如何工作的吗?让我们再举一个可能对我们本章有用的例子。我们中的大多数人都会从各种电子商务网站上购买东西。假设你正在尝试从亚马逊购买一本书。当你搜索书籍时,有一个部分会推荐同一类别的其他书籍。这个部分的标题是购买此商品的用户还购买了;你可能觉得这些推荐很有用,并会购买另一本书。看看下面的截图:

图 4.1:亚马逊上的书籍推荐
你在电子商务网站上找到的所有这些推荐都使用特定的算法,这个算法被称为推荐算法。本章全部关于如何使用不同类型的机器学习(ML)算法构建推荐系统。除了电子商务,还有许多领域已经使用了推荐系统;例如,Netflix 和 YouTube 使用推荐算法来推荐我们可能喜欢的视频,Airbnb 根据我们在他们网站上的活动提供推荐。这个列表是无穷无尽的,所以现在让我们学习如何构建推荐系统。
在本章中,我们将涵盖以下主题:
- 
介绍问题陈述 
- 
理解数据集 
- 
构建基线方法: - 
理解基本概念 
- 
实施基线方法 
- 
理解测试矩阵 
- 
测试基线方法的结果 
- 
基线方法的问题 
- 
优化基线方法 
 
- 
- 
构建修订后的方法: - 
实施修订后的方法 
- 
测试修订后的方法 
- 
修订后方法的问题 
- 
理解如何改进修订后的方法 
 
- 
- 
最佳方法: - 
理解关键概念 
- 
实施最佳方法 
 
- 
- 
概述 
因此,让我们讨论问题陈述以及从推荐系统的基本概念开始。
介绍问题陈述
如您所知,在本章中,我们试图构建一个推荐系统。主要使用推荐系统的领域是电子商务。因此,在我们的基本推荐引擎版本中,我们将构建一个可以根据产品类别建议产品名称的算法。一旦我们了解了推荐引擎的基本概念,我们将构建一个可以像亚马逊网站一样建议书籍的推荐引擎。
我们将构建三个版本的推荐算法。基线方法简单直观,以便读者可以了解推荐算法确切能做什么。基线易于实现。在第二和第三种方法中,我们将使用机器学习算法构建书籍推荐引擎。
让我们看看构建推荐系统所使用的基本方法或方法。有两种主要方法,您可以在以下图中找到:

图 4.2:推荐引擎的方法
尽管还有其他方法,如基于知识的方法或混合方法,但我们将使用这两种方法。但在本章中,我们将专注于这两种给定方法。
现在我们来看看我们将要使用的数据集。
理解数据集
在本章中,我们使用以下两个数据集:
- 
电子商务商品数据 
- 
Book-Crossing 数据集 
电子商务商品数据
此数据集包含从实际库存单位(SKU)中提取的数据项。它来自户外服装品牌的产品目录。我们正在为这个户外服装品牌的产品目录构建推荐引擎。您可以通过使用此链接访问数据集:www.kaggle.com/cclark/product-item-data/data。
此数据集包含 500 个数据项。数据集中有两列。
- 
ID:此列表示数据项的索引。用通俗的话说,它是数据集的序号。 
- 
描述:此列包含有关产品的所有必要描述,我们需要使用这些数据来构建推荐引擎。 
您可以参考以下图:

图 4.3:电子商务商品数据片段
如您所见,描述列包含文本数据,我们需要处理这个文本数据集以便构建推荐引擎。现在让我们转到下一个数据集。
Book-Crossing 数据集
Book-Crossing 数据集被广泛用于构建推荐系统。您可以通过www2.informatik.uni-freiburg.de/~cziegler/BX/访问它。此数据集以两种格式提供,如下所示:
- 
SQL 转储 
- 
CSV 转储 
我们使用数据集的 CSV 转储。两种格式都有三个表,具有不同的数据属性。这三个文件的名称如下:
- 
BX-Book-Ratings.csv
- 
BX-Books.csv
- 
BX-Users.csv
让我们探索每个数据表中给出的数据。
BX-Book-Ratings.csv
这个 CSV 文件包含与书籍评分相关的数据。这个表格包含以下三个数据属性:
- 
User-ID: 这个数据属性表示唯一的用户 ID。这个列具有数值。用户 ID 的长度为六。 
- 
ISBN: ISBN 的全称是国际标准书号。这个数据属性表示书籍的唯一识别号码。 
- 
Book rating: 这个数据属性表示书籍的用户评分。书籍的评分范围从 0 到 10,0 表示不太喜欢,10.0 表示最高评价。 
BX-Books.csv
这个文件包含有关书籍的所有详细信息。该表格包含以下数据属性:
- 
ISBN: ISBN 用于识别书籍。所有无效的 ISBN 都已删除。这个数据表只包含有效的 ISBN。 
- 
Book-Title: 这个数据属性包含书籍的名称。 
- 
Book-Author: 这个数据属性包含书籍作者的姓名。 
- 
Year-Of-Publication: 这表示书籍的出版年份,格式为 YYYY。 
- 
Publisher: 这个数据列包含出版书籍的出版社名称。 
- 
Image-URL-S: 这个数据属性包含书籍封面图片的 URL。S 表示封面图片为小尺寸。 
- 
Image-URL-M: 这个数据属性包含书籍封面图片的 URL。M 表示封面图片为中尺寸。 
- 
Image-URL-L: 这个数据属性包含书籍封面图片的 URL。L 表示封面图片为大尺寸。 
现在让我们看看上一个数据表的详细信息。
BX-Users.csv
这是 Book-Crossing 数据集的第三个数据表。这个文件包含用户信息。
这个特定的数据文件包含以下数据属性:
- 
User-ID: 这个数据列表示用户 ID,是一个六位数的整数。 
- 
Location: 这个数据是关于用户人口统计细节的一部分。位置表示城市名称和缩写。并非所有用户的位置信息都可用,因此对于那些位置信息未找到的用户,您将找到 null值。
- 
Age: 这也是一个人口统计数据点。如果跟踪了用户的年龄,则它存在于数据集中;如果没有,则年龄的值为 null。
我们已经收集了两个数据集的基本信息。我们将继续构建推荐引擎的基本版本。
构建基线方法
从本节开始,我们将专注于如何构建推荐引擎的基本版本(在本章的上下文中意味着推荐系统)。为了开发基线方法,我们将使用基于内容的方法。以下是我们将要讨论的主题:
- 
理解基本概念 
- 
实施基线方法 
- 
理解测试矩阵 
- 
测试基线方法的结果 
- 
基线方法的问题 
- 
学习基于基线方法的优化技巧 
不浪费任何时间,让我们看看基于内容的方法是如何被用来构建推荐引擎的。
理解基本概念
正如我之前所指定的,我们正在使用基于内容的方法。你可能想知道这种方法是什么,以及我是如何决定使用它的。为了找到这些问题的答案,我们首先需要理解这种方法,然后我们可以讨论为什么我选择了它。
理解基于内容的方法
该算法背后的直觉很简单。如果你在购买或对某一种商品感兴趣,那么你很可能也会喜欢类似的产品。让我们举一个例子。如果你在买一条牛仔裤,那么你很可能也会想买 T 恤或上衣,以及正装裤或其他类型的裤子。基本上,对产品的推荐是基于你探索过、购买过或感兴趣的内容的。当每个商品的上下文和属性可以轻松确定时,这种方法效果很好。这种类型的推荐系统用于向用户推荐视频和音频内容。
当你在 YouTube 上观看喜剧视频时,你可能会注意到有其他搞笑片段和喜剧视频的建议。这是因为根据你的观看和浏览历史,你很可能喜欢类似的内容。你可以通过以下图示来理解这个例子:

图 4.4:基于内容方法思想的图示
因此,当我们需要构建一个可以推荐与用户的购买模式或浏览模式相似的物品或产品的系统时,我们使用这种方法。选择这种方法的原因是这种类型的推荐不受其他用户选择的影响。这将为用户提供个性化的体验。推荐完全基于用户喜欢的物品及其特征。这种方法有助于电子商务公司以更少的努力提高销售额。它需要的手动工作较少,这是需要注意的一个好点。我们还可以使用电子商务平台新推出的产品。
为了实现这种方法,我们需要关注其架构部分,并查看基本概念,如 TF-IDF 和余弦相似度。我们将在下一节中探讨所有这些主题。
实现基线方法
在本节中,我们将设计基于内容的推荐系统架构。之后,我们将探讨如何构建一个简单的推荐系统。因此,我们将涵盖以下两个子主题:
- 
推荐系统架构 
- 
实现基线方法的步骤 
推荐系统架构
在本节中,我们将介绍基于内容的推荐系统的基本架构。参考以下图表,它更详细地解释了组件:

图 4.5:基于内容的推荐系统架构
如您所见,我们需要使用许多组件来构建推荐系统。我们使用数据源或信息源来存储关于项目或产品的详细信息。内容分析器将项目描述转换为特定格式,以便推荐引擎可以消费这些信息。我们拥有所有产品或与项目相关的信息。现在我们需要知道用户在电子商务平台上浏览、购买或搜索的内容。这类与用户相关的信息被用作推荐系统的训练示例。这些训练示例是配置文件学习模块的输入,该模块实际分析年龄、性别、在网站上的时间以及其他基于人口统计和用户活动信息。
这类集体信息将被传递给过滤组件。基于电子商务平台上可用的产品信息和用户活动,我们将向客户推荐项目列表。
推荐引擎的逻辑在这里变得重要。我们将把推荐信息推送给电子商务平台的活跃用户。在这里,活跃用户是指在上个月购买过产品或浏览平台频率较高的用户。我们需要跟踪用户的活动,这些活动作为我们推荐引擎的反馈。
在反馈中,我们可以跟踪用户从推荐列表中点击的项目数量。他们是否购买了推荐列表中的任何项目?这类反馈很有用,因为基于这种反馈,我们可以微调推荐引擎的逻辑。我们将反馈发送给配置文件学习器,并使用它来更新每个用户的兴趣区域,以便在将来,如果某人之前浏览过运动鞋,我们可以给他们提供更多关于运动服的建议。现在您已经了解了组件及其工作原理,让我们来看看基线方法的逐步实现。
实施基线方法的步骤
在本节中,我们将介绍基本推荐引擎的编码。你可以通过使用以下 GitHub 链接来参考代码:github.com/jalajthanaki/Basic_Ecommerce_Recomendation_System
这些是我们需要遵循的步骤:
- 
加载数据集 
- 
使用 TF-IDF 生成特征并构建余弦相似度矩阵 
- 
生成预测 
加载数据集
在这里,我们使用的是电子商务商品数据集。在这个数据集中,有一个我们需要使用的商品描述。我们将使用pandas库来加载数据集。你可以参考以下截图:

图 4.6:加载数据集的代码片段
使用 TF-IDF 生成特征
我们将使用 TF-IDF 的概念,这是一种简单但有效的统计特征技术。TF-IDF 代表词频-逆文档频率。我将简要解释一下。
TF-IDF 包含两部分:词频和逆文档频率。让我们从词频开始。术语是自解释的,但我们仍然会解释这个概念。词频表示文档或数据集中每个单词出现的频率。TF 的公式如下:

图 4.7:TF 的公式
现在我们来谈谈逆文档频率。IDF 表示单词对文档的重要性。这是因为当我们计算 TF 时,我们对每个单词给予同等的重要性。如果一个单词“the”在数据集中出现得更频繁,那么它的词频(TF)值就高,但这个单词对文档的重要性并不大。如果一个单词“the”在文档中出现了 100 次,那么这意味着它所携带的信息量并不比数据集中出现频率较低的单词多。因此,我们需要对频繁的术语进行一些降权,同时提升罕见术语的权重,这就是决定每个单词重要性的因素。我们将通过以下公式给出的方程来实现这一点:

图 4.8:IDF 的公式
因此,计算 TF-IDF 的最终公式如下:

图 4.9:TF-IDF 的公式
如果你想详细了解,我建议你阅读这本书中的这个主题:第五章,Python 自然语言处理。为此,你可以参考以下链接:www.packtpub.com/big-data-and-business-intelligence/python-natural-language-processing
这个概念的实际实现相当简单。我们使用 scikit-learn 库来编写代码。您可以参考以下截图:

图 4.10:使用 TF-IDF 生成特征的代码片段
在这里,我们使用了TfidfVectorizer API 并为项目描述生成了 TF-IDF 向量。我们使用stop_words参数去除了英语停用词。在这里,我们提供了从 1 到 3 的ngram_range。现在让我们构建余弦相似度矩阵。
构建余弦相似度矩阵
在本节中,我们将构建余弦相似度矩阵,这是构建基于内容的推荐引擎所需的主要步骤。这个矩阵表示一个产品的描述与其他产品描述的相似程度。在这里,我们将检查所有产品的 TF-IDF 向量的余弦相似度。我们需要找到两个 TF-IDF 向量之间的角度。这个角度表示 TF-IDF 向量之间的接近程度或距离。为此,我们需要使用以下方程来获取 TF-IDF 向量之间的点积:

图 4.11:点积的方程
现在,借助给定的余弦方程,我们可以生成这些向量之间的角度。您可以参考以下公式中的方程:

图 4.12:向量余弦相似度和范数的方程
现在,让我们看一个基本示例,以便您能够理解其背后的基本数学原理。为此,您需要参考以下方程:

图 4.13:基本的余弦相似度示例
如前图所示,有两个向量,每个向量有三个元素。首先,我们计算了它们的范数,然后对它们进行了点积运算。之后,我们使用了余弦相似度公式,并找到了这些向量之间的角度。请注意,我们可以测量两个非零向量之间的余弦相似度。余弦角度的区间是0,2π)。
这个代码实现相当简单。您可以参考以下截图所示的代码片段:

图 4.15:生成预测的代码片段
如您所见,我们从字典中检索结果。我们已打印出 cos θ的值作为我们的评分值。如果分数接近 1,则可以说这些项目更相似,用户喜欢推荐的可能性更高。如果分数更接近 0 或-1,则项目对用户来说不太吸引人。所以请注意,在这里,分数表示 cos θ的值,而不是角度本身。
现在我们来看看测试矩阵,它可以帮助我们评估这种方法以及在本章中我们将要实现的其他方法。
理解测试矩阵
在本节中,我们将探讨基于内容的推荐引擎的测试或演变矩阵。在这里,余弦相似度分数是我们最大的测试分数。这是因为有了这个分数的帮助,我们可以轻松地了解算法是否可以建议余弦相似度分数接近 1 或 0 的项目。
对于某些项目,我们将获得一个接近 1 的分数,而对于其他项目,我们获得的分数则接近 0。因此,我们需要关注这个余弦分数,以便了解推荐引擎表现的好坏。您可以参考以下图表:

图 4.16:余弦相似度分数和角度的理解
如前图所示,我们需要使用余弦分数来测试这种方法。我们可以执行以下步骤进行测试。我们需要计算超过一定分数的项目数量,这意味着我们可以决定余弦相似度分数的阈值,并计算推荐引擎建议超过该阈值的项目数量。让我给您举一个例子。假设我们决定一个截止分数为 0.15。在这种情况下,所有余弦分数高于 0.15 的项目都被认为是良好的推荐。在这里,技巧是您需要对这个阈值进行实验,因为根据用户的活动,您可能稍后会改变它。这个参数将是我们可调整的参数。在下一节中,我们将查看测试的代码。
测试基线方法的结果
在本节中,我们将了解如何实现阈值逻辑。之后,我们将比较不同项目的结果。您可以参考以下截图所示的代码片段:

图 4.17:测试代码片段
现在,你可以看到不同item_ids的结果。你可以找到三个项目的结果。我随机选择了item_id。看一下下面的屏幕截图:

图 4.18:项目结果
看一下下面的代码片段:

图 4.19:基于有用建议的分析
如前图所示,这种方法有 69.8%的时间提供了有用的建议,有 7.2%的时间提供了四个有用的建议。在查看结果分析后,我们可以说基线方法表现良好,我们肯定可以通过其他方法来提高结果。
在下一节中,我们将讨论这种基线方法存在的问题以及我们如何解决这些问题。
基线方法的问题
在本节中,我们将讨论基线方法中存在的问题。我们需要了解这些问题,以便在修订方法中注意它们。这个方法的问题如下:
- 
内容分析有限:如果我们没有足够的信息来更准确地区分项目,那么推荐引擎就不会提供有用或更精确的建议。 
- 
过度专业化:基于内容的方法基于用户资料和他们正在浏览的项目,因此如果用户反复浏览相同的内容,他们将得到相同类型的建议。用户找不到不同或新颖的项目。这是不好的,因为如果我们更频繁地提供相同的推荐,那么用户就没有惊喜元素,他们也不会有购买东西的动力。这个问题被称为过度专业化。 
- 
新用户:如果一个新用户正在探索电子商务平台,而我们对其信息非常有限,那么我们最初无法给他们提供好的推荐。这种情况是由于缺乏稳固的资料。 
所有上述问题都是基于内容推荐引擎的已知问题。为了解决这些问题,我们可以尝试其他方法。有关这些的详细信息将在下一节中给出。
优化基线方法
在这里,我们将概述我们如何解决上一节中遇到的问题。在基线方法中,我们基本上依赖于用户资料和项目描述,但这种方法并没有取得好的效果。为了改进这一点,我们将使用两种方法。在修订的方法中,我们将使用基于校正的方法。之后,我们将尝试基于协同过滤的方法。
这种基于相关性的方法依赖于用户的活动,而不是依赖于项目的内容或描述。这有助于我们解决新用户、过度专业化和内容分析有限等问题。我们正在使用相关系数来构建推荐引擎。这是一种简单的统计技术,可以非常有帮助。对于实现来说,重要的基本概念将在我们开始构建改进方法时进行描述。
因此,让我们构建改进的方法。
构建改进的方法
在这次迭代中,我们将使用一个称为相关性的统计概念来构建推荐引擎。我们将研究用户的活动和选择是如何相互关联的。我们试图从用户在电子商务平台上的活动和行为中找出模式。
在这里,我们将使用 Book-Crossing 数据集。构建推荐系统的一个关键参数是书籍评分属性。我将结合实现部分解释这些概念,这样你就可以更容易地理解。
实现改进的方法
为了实现改进的方法,我们需要执行以下步骤。你可以在 GitHub 上的代码中参考:github.com/jalajthanaki/Book_recommendation_system/blob/master/correlation_based_recommendation_system.ipynb
- 
加载数据集 
- 
书籍评分数据文件的探索性数据分析(EDA) 
- 
探索书籍数据文件 
- 
用户数据文件的 EDA(探索性数据分析) 
- 
实现推荐引擎的相关逻辑 
加载数据集
作为第一步,我们将使用pandas库来加载我们的 Book-Crossing 数据集。正如你所知,这个数据集有三个数据文件。我们正在加载所有这些文件。你可以参考以下代码片段:

图 4.20:加载数据的代码片段
我们的数据分隔符是分号,我们使用 latin-1 作为编码。我们定义了三个pandas数据框。
现在让我们跳到下一步,即对所有三个数据文件进行 EDA 步骤。
书籍评分数据文件的 EDA
对于这个数据文件,我们已经生成了评分数据框。我们需要知道这个数据文件的数据分布类型。这意味着我们需要检查有多少本书得到了 10 分(满分),有多少本书得到了 5 分,以及有多少本书没有任何评分。请参考以下代码片段以生成这些信息:

图 4.21:书籍评分数据文件的 EDA 代码片段
你可以在以下图中找到这个条形图:

图 4.22:书籍评分分数分布的条形图
如我们所见,有 7,16,109 本书的评分为零,而 1,03,736 本书的评分为八。基于这种分析,我们可以推断出有很多书的评分为零,因此数据分布存在偏差。我们需要记住这一点。
探索书籍数据文件
在本节中,我们将对书籍数据文件进行 EDA 分析。我们还需要检查数据属性并格式化数据。对于这个数据文件,不需要应用其他技巧。请查看以下截图中的代码片段:

图 4.23:探索书籍数据文件的代码片段
您可以看到,我们已经检查了书籍数据文件的形状和列列表。在构建推荐引擎时,我们没有需要考虑的任何关键问题。
用户数据文件的 EDA 分析
在这里,我们需要对用户的数据文件进行分析。这个数据文件很重要,因为我们经常使用它来获取一些重要的事实。首先,我们需要获取年龄分布。当我们构建推荐系统时,年龄分布是关键数据点之一,因为相似年龄组的用户有相似的阅读模式,如果我们获得这种模式,那么我们可以为我们的用户提供更有效的推荐。您可以参考以下截图中的代码片段:

图 4.24:生成年龄分布的代码片段
您可以参考箱形图,它显示了以下图中的年龄分布:

图 4.25:年龄分布的箱形图
根据分布,我们可以得出结论,大多数用户的年龄在 20 到 40 岁之间。因此,如果我们关注他们的阅读和浏览模式,那么我们的工作将更容易。
实现推荐引擎的相关性逻辑
在本节中,我们将介绍推荐引擎的核心逻辑。逻辑可以分为两部分:
- 
基于书籍评分的推荐 
- 
基于相关性的推荐 
那么,让我们开始吧!
基于书籍评分的推荐
为了构建一个基于书籍评分的推荐系统,所有评分都由读者提供。因此,为了实现这种方法,我们需要提取评分最高的前五本书,这意味着我们需要从读者那里获得评分最高的书籍列表。相应的代码片段如下所示:

图 4.26:基于书籍评分生成前五本书的代码片段
我们已经生成了根据书籍评分计数排名前五的书籍的 ISBN,但我们还需要检查这些书籍的名称以及每本书的平均评分。你可以通过合并书籍和书籍评分数据框来找到书籍的名称。你可以在下面的屏幕截图中看到这段代码:

图 4.27:生成前 5 本书名称的代码片段
现在,你可能想知道这种方法的益处是什么。让我告诉你,我们根据书籍评分的顺序列出了一个书籍列表。如果一个用户根据书籍的评分购买书籍,那么我们可以推荐其他具有相同评分的书籍。这样,用户会得到比之前方法更准确的建议。
如果你查看前五本书的结果,那么你会了解到评分最高的是 Rich Shapero 的书籍《Wild Animus》。这五本书都是小说。如果有人想购买《Wild Animus》,那么用户也可能购买《The Lovely Bones: A Novel》。这就是为什么这种方法是有道理的。
现在让我们看看基于相关性的推荐引擎。
基于相关性的推荐
我们使用双变量相关性和皮尔逊相关系数(PCC)。这也被称为“皮尔逊 r”。这种相关性提供了两个变量a 和 b之间线性相关性的度量。在这里,我们考虑了两本书的评分,并将 PCC 技术应用于它们。皮尔逊 r 的值在+1到-1之间。这个相关值的解释如下:
- 
+1:这个值表示完全正线性相关性。这意味着如果变量 1 的值增加,那么变量 2 的值也会增加。 
- 
0:这个值表示没有线性相关性。这意味着两个变量之间没有关系。 
- 
-1:这个值表示存在完全负线性相关性。这意味着如果变量 1 的值增加,那么变量 2 的值会减少。 
PCC 的公式如下所示:

图 4.28:PCC 或皮尔逊相关系数的公式
让我们考虑一个简单的数学例子,这样你就知道我们是如何计算皮尔逊 r 的。看看下面的公式:

图 4.29:皮尔逊 r 的数学示例
注意
注意:我们正在考虑两本书的评分,以找到它们之间的相关性。
首先,我们需要获取所有书籍的平均评分。代码片段如下面的屏幕截图所示:

图 4.30:生成平均书籍评分的代码片段
注意,获得最多评分的书籍并不一定是评分最高的。这意味着有些书籍读者更频繁地分享他们的反馈,但这并不意味着这些书籍的评分很高。也许有些书籍被 100 个用户评分,但书籍的评分是 4.3。这是我最需要强调的重要观点,因为错误可能就发生在这里。为了构建一个更好的系统,我们需要考虑书籍的评分数量和评分分数。
在这里,我们将排除那些提供少于 200 个评分的用户以及那些获得少于 100 个评分的书籍。这意味着我们正在设置一个阈值,以便构建一个更好的系统。我们可以通过以下截图中的代码片段来实现这一点:

图 4.31:为考虑用户和书籍设置阈值的代码片段
现在我们正在将评分数据框转换为 2D 矩阵。这个矩阵是一个稀疏矩阵,因为并非每个用户都对每本书进行了评分。你可以在以下截图中的代码中看到:

图 4.32:生成评分稀疏矩阵的代码片段
我们已经完成了一些基本工作,现在是时候找出与第二高评分书籍《可爱的骨头:一部小说》相关的书籍了。我想引用这本书的摘要,该摘要来自维基百科:en.wikipedia.org/wiki/The_Lovely_Bones
“这是一个关于一个少女的故事,她在被强奸和谋杀后,从她个人的天堂看着她的家人和朋友努力继续他们的生活,同时她也在接受自己的死亡”。
现在我们需要找到一本可以向用户推荐的书籍,如果他们正在尝试购买这本书。帮助我们获取推荐的代码在下面的截图中有展示:

图 4.33:基于相关性生成推荐的代码片段
在这里,你可以看到我们正在使用稀疏矩阵,并应用了corrwith API 来生成相关性。可能会有一些运行时警告。它们与浮点数据类型有关。除此之外,我们已经编写了所需的条件,以便推荐那些获得超过或等于 300 个用户评分的书籍。我们使用前面的代码获取了 ISBN。因此,我们还需要获取书籍的名称。为此,我们需要使用以下截图中的代码片段:

图 4.34:生成书籍名称的代码片段
让我们选择这本书的前三个推荐,它们是,《The Nanny Diaries: A Novel》(保姆日记:一部小说),《The Pilot's Wife: A Novel》(飞行员妻子:一部小说),以及《1st to Die: A Novel》(第一死:一部小说)。《保姆日记:一部小说》通过孩子们的保育员视角批评了曼哈顿的上层社会。《飞行员妻子:一部小说》是由写了《美好骨头》的同一作者所写。《第一死》是女性谋杀俱乐部系列的第一本书。
如果你实际查看这三本书的内容,那么我们可以看到所有这些推荐都是有意义的。
测试修订后的方法
我们已经获得了推荐,如果我们使用这种修订后的方法检查建议的书籍,那么我们可以看到这种基于简单相关性的方法工作得相当好。我们进行了手动测试并评估了推荐的品质,建议出乎意料地更加合理和有用。
在下一节中,我们将讨论这种方法的问题以及我们如何进一步改进方法。在实施优化之前,我们需要讨论我们将关注的要点。因此,让我们列出所有的问题或改进领域。
修订后方法的问题
在本节中,我们需要列出问题或改进领域,以便我们可以改进修订后的方法。以下是改进领域的要点:
- 
基于相关性的方法并没有适用于所有情况,因此我们需要一个更复杂的方法。基本上,如果模型在训练期间看到了类似的数据示例,基于相关性的方法表现非常好。对于未见过的数据示例,它可能不会产生好的结果。 
- 
我们不能总是进行手动测试,因此我们需要一个易于开发、构建和测试的推荐引擎。新的方法也可以适应未来的变化,这意味着新的方法应该易于我们根据需要更改或修改。 
现在我们来看看我们如何改进这种修订后的方法。
理解如何改进修订后的方法
为了改进修订后的方法,我们将使用众所周知的推荐算法,协同过滤(CF)。我们将使用机器学习(ML)算法 K 最近邻(KNN)。这是我们如何改进修订后方法的基本概述。
在 CF 算法和 ML 算法的帮助下,我们将很容易测试算法以及根据我们的要求修改算法。你可能知道 KNN 是如何工作的,所以我们不会深入探讨 KNN 算法的细节,但我们将努力理解 KNN 算法背后的直觉。我们还将详细了解基于 CF 的推荐引擎的工作原理,以确保在实施过程中所有概念都清晰。借助这些算法,我们将构建最佳可能的书籍推荐系统。我们将比较我们算法的结果与亚马逊。
在下一节中,我们将首先介绍算法,然后开始实现我们的方法。
最佳方法
在本节中,我们试图构建最好的推荐引擎。本节有两个部分:
- 
理解关键概念 
- 
实施最佳方法 
我们的第一部分涵盖了基本概念,例如 CF 和 KNN 算法是如何工作的,我们需要选择什么样的特征,等等。在第二部分,我们将使用 KNN 和 CF 算法实现推荐引擎。我们将生成准确度分数以及书籍推荐。那么,让我们开始吧!
理解关键概念
在本节中,我们将了解协同过滤的概念。这涵盖了推荐系统的许多方面。那么,让我们来探索 CF。
协同过滤
协同过滤主要有两种类型,如下所示:
- 
基于记忆的 CF: - 
用户-用户协同过滤 
- 
项目-项目协同过滤 
 
- 
- 
基于模型的 CF: - 
基于矩阵分解的算法 
- 
深度学习 
 
- 
我们将首先介绍基于记忆的 CF,然后转向基于模型的 CF。
基于记忆的 CF
基于记忆的 CF 进一步分为两个部分。我之前已经定义了这些部分。请参阅介绍问题陈述部分。在这里,我们需要理解这些概念。我们将从用户-用户 CF 开始,然后探讨项目-项目 CF。
用户-用户协同过滤
在用户-用户 CF 中,我们考虑一个特定的用户。现在我们需要找到与我们的特定用户相似的用户。我们通过观察他们对项目的购买模式和评分模式来找到相似用户。基于评分和购买模式中的相似性,我们向类似类型的用户推荐产品。为了理解用户-用户 CF,您可以参考以下图示:

图 4.35:用户-用户 CF 的图示表示
然而,项目-项目 CF 的工作方式不同。
项目-项目协同过滤
在项目-项目 CF 中,我们考虑项目。我们找到喜欢特定项目以及其他用户或类似用户也喜欢并购买的项目。因此,我们推荐与用户正在寻找的特定项目一起的项目。在这里,我们需要以项目作为输入,生成推荐项目列表。您可以参考以下图示:

图 4.36:表示项目-项目 CF 的图像
这两种方法可以总结如下:
- 
项目-项目 CF:我们考虑喜欢 x 项目和 y 项目的用户 
- 
用户-用户 CF:我们考虑与您相似,也喜欢 x 和 y 项目的用户 
基于记忆的模型使用基于相似度的技术。在这个方法中,没有涉及优化技术,例如梯度下降,因此它将很容易实现。我们可以使用 KNN 机器学习算法,因为它不使用基于梯度的优化策略。因此,在实现过程中,我们将使用 KNN 算法。
KNN 算法背后的思想很简单。我们需要为每个用户或物品获得权重。我们可以通过余弦相似度或人的相关系数来生成这个权重。我们使用相似度值,但我们需要限制相似用户的数量,因为我们不能认为所有用户都是相似的。这个数字用 K 表示。在这里,K 表示我们需要考虑的相似邻居或用户的数量。这就是为什么算法被称为 K 近邻(KNN)。如果您想了解更多关于 KNN 算法的细节,可以参考这篇文章:www.analyticsvidhya.com/blog/2018/03/introduction-k-neighbours-algorithm-clustering/
基于模型的 CF
在这个方法中,我们将使用基于机器学习的技巧来预测用户的推荐,特别是对于那些未评分的项目。为此,我们可以使用矩阵分解方法或基于深度学习的途径。我们将重点关注矩阵分解方法。
因此,让我们来看看矩阵分解。
基于矩阵分解的算法
矩阵分解算法背后的主要思想是用户的偏好可以通过矩阵运算来确定。我们需要定义少数几个隐藏或潜在因素。我们可以将这个矩阵称为因素或嵌入。让我们通过一个例子来更好地理解它。
我们需要定义嵌入矩阵。在这里,值是随机初始化的,然后我们执行嵌入矩阵和书籍嵌入矩阵的点积。生成的矩阵是以一种方式生成的,我们可以预测哪本书可以推荐给哪个用户。对于矩阵分解,我们需要在结果矩阵中使用非负元素。我们将使用奇异值分解(SVD)模型来识别潜在因素。还有一些其他技术也可以使用,例如概率矩阵分解、非负矩阵分解等。我们将实现这种矩阵分解技术。
基于记忆的 CF 与基于模型的 CF 之间的区别
基于记忆的 CF 与基于模型的 CF 之间的主要区别在于,在基于记忆的方法中,没有涉及优化技术,而在基于模型的方法中,有一个优化策略和其他优化函数涉及其中,这些函数可以在一段时间内提高模型的准确性。现在我们将实现基于 CF 的方法。
实施最佳方法
我们将通过以下步骤实现这一方法。您可以在 GitHub 上查看代码:github.com/jalajthanaki/Book_recommendation_system/blob/master/KNN_based_recommendation_system.ipynb。
- 
加载 aset 
- 
合并数据框 
- 
对合并的数据框进行 EDA 分析 
- 
根据地理位置过滤数据 
- 
应用 KNN 算法 
- 
使用 KNN 算法进行推荐 
- 
应用矩阵分解 
- 
使用矩阵分解进行推荐 
加载数据集
就像我们在改进的方法中加载数据集一样,我们在这里也需要实现它。请看以下截图:

图 4.37:加载数据集代码片段
合并数据框
我们需要合并书籍和评分数据框。我们将生成每本书至今收到的总评分。以下代码片段显示了这一过程:

图 4.38:生成评分计数代码片段
之后,我们还将生成书籍评分。请参考以下截图:

图 4.39:生成书籍评分代码片段
对合并的数据框进行 EDA 分析
在这里,我们将对总评分计数进行数据分析。之后,我们需要获取书籍评分的量数值。这个量数值能给我们关于数据分布的良好概念。您可以在以下截图所示的代码片段中参考:

图 4.40:对总书籍评分进行 EDA 的代码片段
如您所见,只有 1%的书籍获得了 50 或以上的用户评分。在这个数据集中有很多书籍,但我们只考虑这些书籍中的 1%。独特书籍的总数是 2,713。
根据地理位置过滤数据
我们将限制我们的用户数据仅限于美国和加拿大地区。这个过滤器可以加快计算速度。我们需要合并用户数据和总书籍评分计数数据。以下截图显示了相应的代码:

图 4.41:基于地理位置过滤的代码片段
如您所见,现在我们有来自美国和加拿大的用户。
应用 KNN 算法
是时候应用主要逻辑了。我们将使用sklearn库应用 KNN 算法。我们的主要目标是确定数据实例的接近程度。您可以在以下截图所示的代码片段中查看:

图 4.42:实现 KNN 算法的代码片段
我们已使用余弦相似度作为 KNN 矩阵参数,并考虑了五个最近的邻居。这意味着 K=5 的值。在模型训练后,我们需要使用它们来获取推荐。
使用 KNN 算法进行推荐
在这里,我们需要使用刚刚训练好的 KNN 算法来获取推荐。代码显示在以下截图:

图 4.43:使用 KNN 获取推荐的代码片段
为了推荐的目的,我们选择了 K=6 的值,这意味着我们在为任何用户推荐书籍时考虑了最近的六个邻居。在这里,我们从us_canada_user_rating_pivot数据框中随机选择了这本书。
这些建议看起来很棒。这里推荐了《绿里奇迹》系列的所有书籍。
应用矩阵分解
现在我们来实现矩阵分解方法。我们将把美国和加拿大用户评分数据框转换成一个二维矩阵。这个矩阵也被称为效用矩阵。我们用 0 替换了缺失值。您可以参考以下截图中的代码:

图 4.44:生成效用矩阵的代码片段
现在我们需要转置效用矩阵。bookTitles变成行,userID转换为列。之后,我们将应用TruncatedSVD进行降维。这个操作是在列——在userID上——执行的,因为我们之后需要使用书籍的标题。您可以参考以下截图中的代码:

图 4.45:SVD 降维的代码片段
在这里,我们选择了n_components的值为 12。所以如您所见,我们数据框的维度已经大大减少。之前,数据框的维度是 40017 x 2442,现在变成了 2442 x 12。
现在我们对最终矩阵中的每一对书籍执行皮尔逊相关系数。我们将比较这些结果与 KNN 算法。基本上,我们应该使用这种方法以及 KNN 算法得到之前得到的建议。
使用矩阵分解进行推荐
在这里,我们需要使用矩阵分解技术生成一个推荐。我们将列出《绿里奇迹:夜行》(绿里奇迹系列)的所有推荐。算法应该建议高度相关的书籍。我们已应用了一个相关性阈值。只有那些相关性得分在 0.9 到 1 之间的书籍才使用这种方法列出。您可以参考以下截图中的代码片段:

图 4.46:使用矩阵分解生成推荐的代码片段
正如你所见,使用基于 KNN 方法推荐的所有书籍的推荐也在这里列出。因此,这个基于 CF 的推荐系统工作得最好。你同样可以在亚马逊上找到类似的推荐。请参考以下截图:

图 4.47:亚马逊上的推荐
我们可以确认我们的推荐引擎工作得很好。
摘要
这是分析领域的最后一章。到目前为止,你已经学到了很多可以帮助我们构建令人惊叹的分析应用的概念。在本章中,你学习了如何为电子商务产品制作推荐引擎。在基线方法中,我们使用了 TF-IDF 和余弦相似度的概念。在修订方法中,我们构建了一个使用相关性的概念的书本推荐系统。在最佳方法中,我们使用了 KNN 算法构建了一个基于协同过滤方法的推荐引擎。我们探讨了所有方法的优缺点。你还学习了推荐系统的架构。所有这些主题都将帮助你理解和构建你自己的推荐系统。你也可以构建一个基于计算机视觉的推荐引擎。这种类型的推荐引擎真正改变了内容推荐给用户的方式。所以,不要犹豫去构建新的推荐系统类型。
从下一章开始,我们将讨论属于自然语言处理领域或自然语言生成领域的应用。下一章全部关于情感分析,这是一个众所周知且简单的 NLP 应用。我们将使用各种机器学习算法来达到最佳结果。
第五章:情感分析
到目前为止,我们在分析领域探索了一些真正酷的应用。在本章中,我们将探讨著名的自然语言处理(NLP)技术,你可能已经猜到了,因为章节的名称。绝对正确;我们将构建一个基于情感分析的应用。一般来说,每个人对基于情感分析的应用都很熟悉。如果你不熟悉,那么不用担心。我们将讨论和理解所有必要的细节。
首先,我想给你一个关于情感分析的基本概念。我会提供一个例子,这样你就能更容易理解。无论我们住在哪里,我们都会看电影。如今,我们在各种社交媒体平台上阅读评论或他人的意见。然后,如果大多数关于电影的评论都是好的,那么我们就看那部电影。如果评论不令人印象深刻,我们可能不会看那部电影。所以在这个过程中,我们的思维分析了这些意见,并将它们分类为正面意见、负面意见或中性意见。在本章中,我们将进行同样的分析。
让我介绍情感分析的正确定义。情感分析是一种技术,我们考虑一个句子、段落、文档或任何以自然语言形式存在的信息,并确定该文本的情感基调是正面、负面还是中性。我们将应用机器学习和深度学习来构建情感分析应用。
在本章中,我们将涵盖以下主题:
- 
介绍问题陈述 
- 
理解数据集 
- 
为基线模型构建训练和测试数据集 
- 
基线模型的特征工程 
- 
选择机器学习(ML)算法 
- 
训练基线模型 
- 
理解测试矩阵 
- 
测试基线模型 
- 
现有方法的问题 
- 
如何优化现有方法 - 理解优化方法的关键概念
 
- 
实施修订后的方法 - 
测试修订后的方法 
- 
理解修订后方法的问题 
 
- 
- 
最佳方法 - 实施最佳方法
 
- 
摘要 
介绍问题陈述
我们生活在一个竞争激烈的世界。在购买任何产品,或者在我们投入时间或金钱到任何事物之前,我们试图了解其他人对此产品(或服务)的看法。我们尝试分析他们的评论或意见。如果我们发现它们是积极的且值得信赖的,那么我们会购买该产品,并在该特定服务上投资我们的金钱或时间。另一方面,如果我们发现这些意见或评论是负面的,那么我们可能不会购买该产品,也不会在该特定服务上投资我们的金钱或时间。在互联网时代,在社交媒体平台、博客、新闻来源等地方找到评论很容易。这种分析评论的活动对消费者以及产品或服务提供商都有用。这是因为,基于客户的评论,他们可以有效地改变他们的产品,为他们的客户提供更多的满足,并从该产品或服务中获得良好的利润。我已经给你们提供了情感分析的正确定义,所以我不想再重复一遍。让我们尝试理解本章的主要关注点是什么。
我们将开发一个针对电影评论的情感分析应用程序。在训练过程中,我们将考虑与每条电影评论相关的标签,以便我们可以根据给定的标签训练我们的机器学习算法。训练完成后,当我们传递任何未见过的电影评论时,我们的训练好的机器学习算法将预测情感,这意味着提供的电影评论是否表示积极的情感或消极的情感。
我们将考虑使用 IMDb(互联网电影数据库)电影评论数据集来开发电影评论的情感分析。我们将在下一节中查看有关数据集的详细信息。
理解数据集
在本节中,我们将查看我们的数据集。我们考虑了一个 IMDb 数据集,您可以在以下链接下载:ai.stanford.edu/~amaas/data/sentiment/。点击此链接后,您会看到页面上提供了一个链接。此链接的标题为 Large Movie Review Dataset v1.0;我们需要点击它。这样,我们就可以下载 IMDb 数据集。一旦您下载了数据集,您需要解压 .tar.gz 文件。一旦解压了 .tar.gz 文件,您会看到提取的文件夹中有两个文件夹和一些其他文件。让我们在下一节中查看每个部分。
理解数据集的内容
在解压数据集文件后,我们会看到其中有一些文件夹和文件。我们将讨论所有内容的含义以及我们将用于训练目的的内容。此数据集包含两个文件夹和三个文件:
- 
train 文件夹 
- 
test 文件夹 
- 
imdb.vocab文件
- 
imdbEr.txt
- 
README 
Train 文件夹
这个文件夹包含用于训练的数据。在这个文件夹内,有两个主要文件夹。pos文件夹包含正面电影评论,而neg文件夹包含负面电影评论。在pos文件夹内,有 12,500 条正面电影评论。在neg文件夹内,有 12,500 条负面电影评论。所以总共我们有 25,000 条电影评论;通过使用它们,我们将训练我们的机器学习(ML)模型。为了测试目的,我们可以使用unsup文件夹内提供的电影评论。这些电影评论是无标签的,因此我们可以使用它们进行测试,或者将我们的标记数据分成训练和测试组,这样我们就可以更容易地了解我们的训练 ML 模型的工作情况。
训练文件夹内还有其他文件,但我们不会使用它们。这些文件包含已经标记化的词袋(BOW)特征的数据。为了清楚地了解目录结构,你可以参考以下图中提供的代码片段:

图 5.1:训练文件夹的目录结构
如果你想更详细地探索数据集,可以参考以下提供的文档:www.paddlepaddle.org/docs/0.10.0/documentation/en/tutorials/sentiment_analysis/index_en.html。
测试文件夹
这个文件夹包含用于测试的数据。在这个文件夹内,有pos和neg文件夹,分别包含正面和负面的电影评论。每个文件夹包含 12,500 条电影评论,所以总共我们有 25,000 条电影评论用于测试。这些电影评论被标记为正面,这样我们就可以使用这个数据集来测试我们的训练模型。还有其他BOW文件和url文件,我们不会使用。你可以在以下图中看到测试文件夹的目录结构:

图 5.2:测试文件夹的目录结构
imdb.vocab 文件
这个文件包含所有电影评论中使用的唯一单词,因此它是IMDb数据集的词汇表文件。如果你打开这个文件,你可以看到单词,并观察到它们都是唯一的。你可以在以下图中看到这个文件的内容:

图 5.3:imdb.vocab 文件的内容
imdbEr.txt 文件
这个文件表示imdb.vocab文件中每个标记的预期评分。这意味着所有这些数值都表示imdb.vocab文件中每个单词的评分。如果一个单词是正面的,那么数值是一个正浮点数。如果一个单词是负面的,那么数值是一个负浮点值。你可以在以下图中看到文件的内容:

图 5.4:imdb.vocab 文件中每个单词的得分的 imdbEr.txt 文件
README
此文件包含有关数据集的文档。您可以使用此文件获取基本信息。
注意,为了开发这个情感分析应用,我们将只考虑train文件夹中的数据,因为处理高达 50,000 条电影评论需要大量的计算能力,所以我们将只考虑train文件夹中的 25,000 条电影评论,并将一些电影评论保留用于测试。现在让我们尝试理解电影评论文件的内容是如何提供的。
理解电影评论文件的内容
在pos和neg文件夹内,有.txt文件包含电影评论。pos文件夹内的所有.txt文件都是正面电影评论。您可以通过以下图中的示例内容进行参考:

图 5.5:来自pos文件夹的样本电影评论;文件名为 0_9.txt
电影评论以简单的纯文本形式提供。在这里,我们将进行一个小型的预处理更改,即将pos和neg文件夹名称分别重命名为positiveReviews和negativeReviews。这个IMDb数据集已经过预处理,所以我们不会进行任何广泛的预处理。您可以通过以下 GitHub 链接下载最终的训练数据集:github.com/jalajthanaki/Sentiment_Analysis/blob/master/data.tar.gz
现在我们需要开始构建我们的情感分析应用的机器学习模型。我们将执行以下步骤:
- 
构建训练和测试数据集 
- 
基线模型的特征工程 
- 
选择机器学习算法 
- 
训练基线模型 
- 
理解测试矩阵 
- 
测试基线模型 
让我们尝试理解所有这些步骤。
为基线模型构建训练和测试数据集
在本节中,我们将生成训练数据集和测试数据集。我们将遍历数据集的文件,并将所有以数字 12 开头的文件视为我们的测试数据集。因此,大约 90%的数据集被认为是训练数据集,10%的数据集被认为是测试数据集。您可以在以下图中的代码中参考这一点:

图 5.6:构建训练和测试数据集的代码片段
如您所见,如果文件名以 12 开头,则我们认为这些文件的内容是测试数据集。除了这些文件之外的所有文件都被认为是训练数据集。您可以在以下 GitHub 链接中找到代码:github.com/jalajthanaki/Sentiment_Analysis/blob/master/Baseline_approach.ipynb.
基线模型的特征工程
对于这个应用,我们将使用基本的统计特征提取概念来从原始文本数据中生成特征。在 NLP 领域,我们需要将原始文本转换为数值格式,以便将机器学习算法应用于这些数值数据。有许多技术可用,包括索引、基于计数的向量化、词频-逆文档频率(TF-IDF)等。我已在第四章中讨论了 TF-IDF 的概念,使用 TF-IDF 生成特征:
注意
索引主要用于快速数据检索。在索引中,我们提供一个唯一的识别号。这个唯一的识别号可以按字母顺序或基于频率分配。您可以参考此链接:scikit-learn.org/stable/modules/generated/sklearn.preprocessing.LabelEncoder.html
基于计数的向量化将单词按字母顺序排序,如果某个特定单词存在,则其向量值变为 1,否则为 0。向量的大小与我们的训练数据集的词汇表大小相同。您可以参考以下链接中的简单代码:github.com/jalajthanaki/NLPython/blob/6c74ddecac03b9aec740ae2e11dd8b52f11c0623/ch5/bagofwordsdemo/BOWdemo.py
在这里,我们使用 scikit-learn 的 TF-IDF 向量化技术。TfidfVectorizer函数将一组原始文档转换为 TF-IDF 特征矩阵。如果您对 TF-IDF 不熟悉,我建议您参考www.tfidf.com/或www.packtpub.com/mapt/book/big_data_and_business_intelligence/9781787121423/5。
您可以参考以下图中提供的代码片段:

图 5.7:使用 TF-IDF 生成特征向量的代码片段
正如您在前面用于生成 TF-IDF 特征向量的代码片段中所看到的,我们定义了一些参数,我想对此进行适当的解释:
- 
min_df:此参数为文档频率提供了一个严格的下限。我们将此参数设置为 5。因此,在数据集中出现次数少于 5 次的术语将不会被考虑用于生成 TF-IDF 向量。
- 
max_df:此参数忽略具有严格高于给定阈值的文档频率的术语。如果此参数的值为浮点数,则表示文档的比例。我们将参数值设置为 0.8,这意味着我们考虑了数据集的 80%。
- 
sublinear_tf: 此参数用于应用缩放。默认情况下,此参数的值为 False。如果其值为 True,则 tf 的值将被替换为1+log(tf)公式。此公式将帮助我们对我们词汇表进行缩放。
- 
use_idf: 此参数表示是否启用了 IDF 重新加权机制。默认情况下,IDF 重新加权是启用的,因此此参数的标志值为 True。
这里使用了两种方法,如下所示:
- 
fit_transform(): 通过使用此方法,你已经学习了词汇和 IDF,并且此方法返回词-文档矩阵。
- 
transform(): 此方法将文档转换为文档-词矩阵。此方法使用从 fit_transform 方法中学习到的词汇和文档频率。
你可以在以下 GitHub 链接中找到前面的代码 github.com/jalajthanaki/Sentiment_Analysis/blob/master/Baseline_approach.ipynb。
现在我们来看看哪种算法最适合构建基线模型。
选择机器学习算法
情感分析是一个分类问题。有一些算法对我们非常有帮助。在电影评论中,你可能会发现有些短语出现得相当频繁。如果这些常用短语表明某种情感,那么很可能是表明积极情感或消极情感的短语。我们需要找到表明情感的短语。一旦我们找到了表明情感的短语,我们只需要将情感分类为积极情感类别或消极情感类别。为了确定实际的情感类别,我们需要识别最可能出现的积极短语和最可能出现的消极短语的概率,以便基于更高的概率值,我们可以确定给定的电影评论属于积极情感还是消极情感。我们将考虑的概率是先验概率和后验概率值。这是朴素贝叶斯算法的基本基础。因此,我们将使用多项式朴素贝叶斯算法。除此之外,我们还将使用支持向量机(SVM)算法。我们将使用不同类型的核技巧来实现它。
如果您想了解更多关于朴素贝叶斯的信息,可以参考www.saedsayad.com/naive_bayesian.html,如果您想了解更多关于 SVM 的信息,可以参考:www.analyticsvidhya.com/blog/2017/09/understaing-support-vector-machine-example-code/ 或 www.packtpub.com/mapt/book/big_data_and_business_intelligence/9781787121423/8/ch08lvl1sec77/understanding-ml-algorithms-and-other-concepts。
在下一节中,我们将查看帮助我们执行训练的代码。
训练基线模型
在本节中,我们将查看帮助我们在实际训练数据集上执行实际训练的代码。我们首先查看实现,然后我将逐步解释代码。在这里,我们将实现朴素贝叶斯和 SVM 算法。对于实现,我们将使用 scikit-learn 库。您可以在以下 GitHub 链接找到代码:github.com/jalajthanaki/Sentiment_Analysis/blob/master/Baseline_approach.ipynb。
实现基线模型
为了理解基线模型实现,您可以参考以下代码片段:

图 5.8:使用朴素贝叶斯和 SVM 进行训练的代码片段
我们在这里实现了以下四种算法:
- 
多项式朴素贝叶斯 
- 
带核函数 rbf 的 C 支持向量机分类 
- 
带核函数线性的 C 支持向量机分类 
- 
线性支持向量机分类 
多项式朴素贝叶斯
如前述代码片段所示,我们使用了多项式朴素贝叶斯。多项式朴素贝叶斯分类器适用于具有离散特征的分类,这意味着如果特征是词频或 TF-IDF 向量,则可以使用此分类器。多项式分布通常需要整数特征计数。然而,如 TF-IDF 之类的分数计数也可能有效。因此,我们必须将此算法应用于train_vectors。fit()方法是实际训练执行的步骤。在这里,我们默认使用了所有超参数。
带核函数 rbf 的 C 支持向量机分类
我们还实现了具有rbf核的支持向量机。核是一个帮助训练算法的函数。rbf核函数的方程如下:

带核函数线性的 C 支持向量机分类
我们还实现了具有线性核的支持向量机。线性核函数的方程如下:

线性支持向量分类
我们还使用了线性支持向量分类。我们将使用LinearSVC()方法类型来实现这种分类。这与带有参数kernel=linear的 SVC 类似,但它是基于 liblinear 而不是libsvm实现的,因此它在选择惩罚和损失函数方面具有更大的灵活性,并且应该更好地扩展到大量样本。
对于所有前面的机器学习算法,我们已经提供了输入,即train_vectors和train_labels。我们将通过使用测试向量和比较预测标签与实际的test_labels来测试机器学习模型的准确性。在进行测试之前,我们将决定使用哪种类型的测试参数。因此,让我们看看测试矩阵。
理解测试矩阵
在本节中,我们将探讨我们应该考虑的测试矩阵,以评估训练好的机器学习模型。对于基线方法,我们将使用以下五个测试矩阵:
- 
精确度 
- 
召回率 
- 
F1 分数 
- 
支持度 
- 
训练准确度 
在我们理解这些术语之前,让我们先了解一些基本术语,这将有助于我们理解前面的术语。
- 
真阳性(TP)—如果分类器预测给定的电影评论带有积极情感,而在实际情况下该电影评论也是积极情感,那么这类测试案例被认为是 TP。因此,你可以将 TP 定义为测试结果检测到实际上存在的情况。 
- 
真阴性(TN)—如果分类器预测给定的电影评论带有消极情感,而在实际情况下该电影评论也是消极情感,那么这类测试案例被认为是真阴性(TN)。因此,你可以将 TN 定义为测试结果没有检测到实际上不存在的情况。 
- 
假阳性(FP)—如果分类器预测给定的电影评论带有积极情感,而在实际情况下该电影评论是消极情感,那么这类测试案例被认为是假阳性(FP)。因此,你可以将 FP 定义为测试结果检测到实际上不存在的情况。这就像是一个错误的警报。 
- 
假阴性(FN)—如果分类器预测给定的电影评论带有消极情感,而在实际情况下该电影评论是积极情感,那么这类测试案例被认为是假阴性(FN)。因此,你可以将 FN 定义为测试结果没有检测到实际上存在的情况。这是某些条件被忽视的情况。 
现在我们将查看所有五个主要测试矩阵,这些矩阵使用了 TP、TN、FP 和 FN 术语。
精确度
精确度是分类器为原本属于正类标签的样本分配正类标签的能力。精确度不会为原本属于负类标签的给定样本分配正类标签。生成精确度分数的公式如下:

召回率
召回率是分类器找到所有正样本的能力。生成召回率分数的公式如下:

F1 分数
F1 分数是精确度和召回率的调和平均值。所以你可以找到以下方程:

支持度
支持度是每个类别在真实目标标签中出现的次数。支持度的值在计算精确度、召回率和 F1 分数的平均值时很有帮助。你可以看到计算精确度、召回率和 F1 分数平均值的公式如下:



实际使用前面公式进行的计算将在测试基线模型部分提供。所以请稍等片刻,我们将看到实际的测试结果。
训练准确度
训练准确度指导我们获得开发任何机器学习应用的正确方向。我们在测试数据集上测试训练好的机器学习模型。当我们进行这项测试时,我们拥有每个测试记录的实际情感类标签,我们也拥有所有测试记录的预测情感类标签,因此我们可以比较结果。所以,预测测试数据集的标签集合必须与实际测试数据集中的相应标签集合完全匹配。我们计算我们的预测标签与实际标签相同的记录,然后将这个计数转换为百分比。
在下一节中,我们将看到每个实现机器学习算法的所有测试矩阵,然后我们将决定哪个算法表现良好。所以,让我们看看测试基线模型的实现。
测试基线模型
在这里,我们将查看执行实际测试的代码片段。我们将获取到目前为止所解释的所有测试矩阵。我们将测试所有不同的机器学习算法,以便我们可以比较准确度分数。
多项式朴素贝叶斯测试
你可以在下面的图中看到多项式朴素贝叶斯算法的测试结果:

图 5.9:测试多项式朴素贝叶斯算法的代码片段
如你所见,使用这个算法我们实现了 81.5%的准确度分数。
使用 rbf 核的 SVM 测试
你可以在下面的图中看到使用 rbf 核的 SVM 算法的测试结果:

图 5.10:使用 rbf 核测试 SVM 的代码片段
如您所见,我们在测试数据集上进行了测试,并获得了 65.4%的准确率。
使用线性核测试 SVM
您可以在以下图中看到使用线性核算法的 SVM 的测试结果:

图 5.11:测试使用线性核的 SVM 的代码片段
如您所见,我们在测试数据集上进行了测试,并获得了 83.6%的准确率。
使用 linearSVC 测试 SVM
您可以在以下图中看到使用 linearSVC 核算法的 SVM 的测试结果:

图 5.12:测试使用 linearSVC 核的 SVM 的代码片段
我们在这里对测试数据集进行了测试,并获得了 83.6%的准确率。
因此,在看到每个实现算法的准确率得分后,我们可以说,使用线性核和 linearSVC 的 SVM 表现非常好。现在,您可能会想知道我们是否可以提高准确率。我们当然可以做到。那么,让我们讨论这个基线方法中存在的问题。
现有方法的问题
在基线方法中,我们获得了很高的准确率。然而,我们忽略了以下这些点,这些点我们可以在我们的改进方法中实现:
- 
我们没有关注基于词嵌入的技术 
- 
深度学习(DL)算法,如 CNN,对我们可能有所帮助 
我们需要关注这两个点,因为基于词嵌入的技术确实有助于保留文本的语义。因此,我们应该使用这些技术以及基于深度学习算法,这有助于我们提供更高的准确率,因为当涉及嵌套数据结构时,深度学习算法表现良好。我所说的嵌套数据结构是什么意思呢?这意味着任何由短语组成的书面句子或口头句子,短语由单词组成,依此类推。因此,自然语言具有嵌套数据结构。深度学习算法帮助我们理解文本数据集中的句子嵌套结构。
如何优化现有方法
有某些技术可以帮助我们改进这个应用程序。以下是可以帮助我们改进基线方法的关键技术:
- 
我们可以使用基于词嵌入的技术,如 Word2Vec、glove 等 
- 
我们还应该实现卷积神经网络(CNN)来了解深度学习算法如何帮助我们 
因此,在改进方法中,我们将重点关注词嵌入技术和深度学习算法。我们将使用具有 TensorFlow 后端的 Keras。在实施之前,让我们详细了解改进方法。
理解优化方法的关键概念
在本节中,我们将详细了解改进方法,以便我们知道应该实施哪些步骤。我们正在使用 Keras,这是一个提供高级 API 的深度学习库,使我们能够轻松实现 CNN。以下步骤包括:
- 
导入依赖项:在这个步骤中,我们将导入不同的依赖项,如 Numpy 和 Keras(使用 TensorFlow 后端)。我们将使用 Keras 库的不同 API。 
- 
下载和加载 IMDb 数据集:在这个步骤中,我们将下载 IMDb 数据集,并使用 Keras API 加载此数据集。 
- 
选择高频词和最大文本长度:在这个阶段,我们将设置我们可以在词嵌入阶段使用的词汇表值。因此,我们选择了前 10,000 个高频词。之后,我们将电影评论的长度限制为 1600 个字符。 
- 
实现词嵌入:在代码的这个阶段,我们将使用 Keras 的默认嵌入技术,并生成长度为 300 的词向量。 
- 
构建 CNN:在这个阶段,我们将构建一个三层神经网络,其中第一层有 64 个神经元,第二层有 32 个神经元,最后一层有 16 个神经元。在这里,我们使用 sigmoid 作为激活函数。激活函数为神经网络引入非线性,这样我们就可以使用数学函数为每个类别生成一个概率分数。 
- 
训练并获得准确率:最后,我们将训练模型并生成准确率分数。我们将 epoch 值设置为 3,将 adam 作为优化函数,我们的损失函数是 binary_crossentropy。Epoch 基本上表示我们需要在全部数据集上执行训练的次数。交叉熵损失衡量的是输出概率值在 0 到 1 之间的分类器的性能。当预测概率与实际标签不同时,交叉熵损失会增加。训练完成后,我们将生成模型的准确率。这个训练阶段可能需要时间,以及大量的计算能力。
现在让我们看看下一节中的代码。
实现改进方法
在本节中,我们将以代码片段的形式展示实现。我们将遵循之前章节中看到相同的步骤。所以,无需任何延迟,让我们来看看代码。您可以通过以下 GitHub 链接参考此代码:github.com/jalajthanaki/Sentiment_Analysis/blob/master/Revised_approach.ipynb。
导入依赖项
您可以在以下图中的代码片段中找到导入的依赖项:

图 5.13:我们可以看到导入依赖项的代码片段
如您所见,我们使用了 TensorFlow 后端和 Keras 库。
下载和加载 IMDb 数据集
您可以在以下图中参考代码片段,其中也可以找到下载和加载 IMDb 数据集的代码:

图 5.14:下载和加载 IMDb 数据集的代码片段
我们还设置了词汇的值。
选择顶级词汇和最大文本长度
在这个阶段,我们已经设置了顶级词汇值以及最大文本长度值。您可以参考以下图中的代码片段:

图 5.15:设置词汇值和最大文本长度值的代码片段
在代码的第一部分,我们将 top_word 参数设置为 10,000,在代码的第二部分,我们将电影评论的长度设置为 1,600。top_word 表示词汇表的大小。从我们的数据集中,我们选择了前 10,000 个独特的单词。大多数电影评论中的单词都包含在我们的词词汇表中。在这里,我们不是处理非常长的电影评论,因此我们设置了电影评论的长度。
实现词嵌入
在这个阶段,我们实现了默认的 Keras 词嵌入方法,以获得一个大小为 300 的特征向量。您可以参考以下代码片段:

图 5.16:基于词嵌入技术获取词特征向量的代码片段
构建卷积神经网络(CNN)
在本节中,您可以参考代码,这将帮助您理解神经网络架构。在这里,我们使用了卷积神经网络(CNN),因为它处理数据集的高级特征或嵌套结构非常出色。您可以参考以下图中的代码片段:

图 5.17:构建卷积神经网络(CNN)的代码片段
在这里,我们构建了一个包含两个密集层的神经网络。
训练和获取准确率
在这个阶段,我们已经完成了训练。您可以参考以下图中的代码:

图 5.18:在训练数据集上执行训练的代码片段
在这里,我们将进行三次训练,因为我们的 epoch 值设置为 3。由于情感分析是一个二元问题,我们使用了binary_crossentropy作为损失函数。如果您有一台基于 GPU 的计算机,那么训练时间会更短;否则,这一步会花费更多的时间和计算能力。
一旦完成训练,我们就可以获得训练准确率。对于训练准确率,您可以参考以下图:

图 5.19:获取训练准确率的代码片段
在这里,准确率是训练准确率。现在我们需要获得测试准确率,因为这将给我们一个关于训练模型在未见数据上表现如何的实际想法。那么,让我们看看测试准确率是多少。
测试改进的方法
在本节中,我们将获得测试数据集的准确率。您可以在以下图中的代码片段中参考:

图 5.20:获取测试准确率的代码片段
在获得测试准确率后,我们得到了 86.45%的准确率。这个测试准确率比我们的基线方法更好。现在让我们看看我们可以改进哪些方面,以提出最佳方法。
理解改进方法中的问题
在本节中,我们将讨论我们可以改进的改进方法的哪些方面。这些是我们可以实现以获得最佳可能方法的点:
- 
我们可以使用预训练的 Word2Vec 或 GloVe 模型来生成词向量 
- 
我们应该使用具有 LSTM 的循环神经网络来获得更好的输出 
在本节中,我们将了解并实现最佳方法,其中我们将加载预训练的 GloVe(全局词向量)模型,并使用 RNN 和 LSTM 网络。
最佳方法
为了获得最佳方法,我们可以遵循以下步骤。在这个方法中,我们使用了 GloVe 预训练模型,并使用 RNN 和 LSTM 网络进行模型训练。GloVe 模型在大型数据集上进行了预训练,以便为单词生成更准确的向量值。这就是我们在这里使用 GloVe 的原因。在下一节中,我们将查看最佳方法的实现。所有代码都可以在这个 GitHub 链接中找到:github.com/jalajthanaki/Sentiment_Analysis/blob/master/Best_approach_sentiment_analysis.ipynb。
实现最佳方法
为了实现最佳方法,我们将执行以下步骤:
- 
加载 GloVe 模型 
- 
加载数据集 
- 
预处理 
- 
加载预计算的 ID 矩阵 
- 
划分训练和测试数据集 
- 
构建神经网络 
- 
训练神经网络 
- 
加载训练模型 
- 
测试训练模型 
加载 GloVe 模型
为了获得最佳性能,我们将使用预训练的 GloVe 模型。您可以在nlp.stanford.edu/projects/glove/下载它。我们已生成二进制文件并将其保存为.npy扩展名。此文件与wordsList.npy文件一起。您可以在以下图中的代码片段中参考:

图 5.21:加载 GloVe 预训练模型的代码片段
词向量的维度是 50,该模型包含 400,000 个单词的词向量。
加载数据集
您可以参考以下代码片段:

图 5.22:加载数据集的代码片段
我们总共考虑了 25,000 条电影评论。
预处理
您可以参考以下图中的代码片段:

图 5.23:执行预处理的代码片段
加载预计算的 ID 矩阵
在本节中,我们正在为每个单词生成索引。这个过程计算量很大,所以我已经生成了索引矩阵并使其准备好加载。您可以参考以下图中的代码片段:

图 5.24:生成单词 ID 矩阵的代码片段
分割训练和测试数据集
在本节中,我们将看到生成训练和测试数据集的代码片段。您可以参考以下图中的代码片段:

图 5.25:将数据集分割成训练集和测试集的代码片段
构建神经网络
我们已经使用了一个循环神经网络(RNN)和作为其隐藏状态一部分的长短期记忆单元(LSTMs)。LSTM 单元用于存储序列信息。如果您有多个句子,那么 LSTM 存储前一个或前前个句子的上下文,这有助于我们改进此应用。如果您想详细了解 LSTM,可以参考colah.github.io/posts/2015-08-Understanding-LSTMs/。
您可以参考以下图中的代码片段:

图 5.26:构建具有 LSTM 的 RNN 的代码片段
首先,我们定义超参数。我们将批大小设置为 64,LSTM 单元设置为 64,类别数量设置为 2,然后执行 100,000 次迭代。
训练神经网络
在本节中,您可以参考以下图中的代码片段,该代码片段用于执行训练:

图 5.27:执行训练的代码片段
每经过 10,000 次迭代后,我们保存模型。在训练过程中,您可以使用 TensorBoard 来监控进度。您可以参考以下图,它显示了训练期间的进度。您可以在训练期间监控准确率和损失百分比,以便了解深度学习模型在训练过程中的收敛情况:

图 5.28:在 TensorBoard 上训练过程中生成的准确率和损失图
训练耗时且计算量大,因此使用 GPU 可能需要 2-3 小时来训练模型。因此,您可以从 GitHub 下载预训练模型:github.com/jalajthanaki/Sentiment_Analysis/blob/master/models.tar.gz。
加载训练好的模型
一旦完成训练,我们就可以保存训练好的模型。加载此模型后,我们可以检查其准确率。您可以参考以下图中的代码片段:

图 5.29:生成测试准确率的代码片段
在这里,我们生成了一个测试准确率为 91.66%。
测试训练模型
在本节中,我们将传递新的电影评论,并通过加载训练模型来生成情感预测。你可以参考以下图中的代码片段:

图 5.30:加载训练模型并为电影评论的给定句子生成情感分析的代码片段
在这个片段中,我们传递了一个作为电影评论一部分的句子,我们的训练模型将其识别为负面情感。你也可以参考以下图中的代码片段,它为包含负面词汇的句子生成了情感分析:

图 5.31:加载训练模型并为电影评论的给定句子生成情感分析的代码片段
这种方法能给你带来非常好的结果。
摘要
在本章中,我们探讨了如何构建一个能够给出最先进结果的情感分析模型。我们使用了一个包含正面和负面电影评论的 IMDb 数据集,并理解了这个数据集。我们应用了机器学习算法以获得基线模型。之后,为了优化基线模型,我们改变了算法并应用了基于深度学习的算法。我们使用了 glove、RNN 和 LSTM 技术以实现最佳结果。我们学习了如何使用深度学习构建情感分析应用。我们使用 TensorBoard 来监控我们模型的训练进度。我们还简要介绍了用于开发情感分析的现代机器学习算法以及深度学习技术,深度学习方法在这里效果最佳。
我们使用 GPU 来训练神经网络,所以如果你发现你的端需要更多的计算能力来训练模型,那么你可以使用 Google Cloud 或亚马逊网络服务(AWS)基于 GPU 的实例。我已经上传了预训练模型,所以你也可以直接使用它。你可以在以下 GitHub 链接中找到预训练模型:github.com/jalajthanaki/Sentiment_Analysis/blob/master/models.tar.gz。
在下一章中,我们将构建一个工作推荐系统,帮助人们找到工作,特别是与他们感兴趣的工作档案相关的工作。对于工作推荐系统,我们将使用各种资源来链接简历、工作搜索查询等。同样,我们将使用机器学习和深度学习系统来开发这个系统。
第六章。职位推荐引擎
我们已经在第四章中看到了如何开发电子商务产品的推荐系统,电子商务推荐系统,现在,我们将应用你在第四章中学习到的相同概念,但数据集的类型和格式不同。基本上,我们将构建一个职位推荐引擎。对于这个应用,我们已经考虑了文本数据集。构建推荐引擎的主要概念不会改变,但本章将给你一个很好的想法,了解如何将相同的概念应用于不同类型的数据集。
在本章中,我们将涵盖以下主题:
- 
介绍问题陈述 
- 
理解数据集 
- 
构建基线方法 - 
实现基线方法 
- 
理解测试矩阵 
- 
基线方法的问题 
- 
优化基线方法 
 
- 
- 
构建修订方法 - 
实现修订方法 
- 
测试修订方法 
- 
修订方法存在的问题 
- 
理解如何改进修订方法 
 
- 
- 
最佳方法 - 实现最佳方法
 
- 
概述 
那么,让我们来讨论问题陈述。
介绍问题陈述
在本章中,我们将构建一个可以向任何用户推荐工作的引擎。这是我们想要实现的最简单目标。我们将如何构建它?为了回答这个问题,让我给你一个关于我们将采取什么方法来构建职位推荐系统的大致想法。
对于我们的基线方法,我们将抓取模拟用户的简历,并尝试基于抓取的数据集构建一个职位推荐引擎。我们抓取数据集的原因是,在许多数据科学应用中,大多数时候都不会有任何数据集可用。假设你处于一个没有找到任何数据集的位置。那时你会怎么做?我想为这类场景提供解决方案。所以,你将学习如何抓取数据并构建基线解决方案。
在修订方法中,我们将使用 Kaggle 托管的数据集。使用基于内容的策略,我们将构建一个职位推荐引擎。对于最佳方法,我们将使用基于用户的协同过滤概念来构建这个领域的最终职位推荐系统。
现在,让我们来看看数据集。
理解数据集
在这里,我们使用了两个数据集。这两个数据集如下:
- 
抓取的数据集 
- 
职位推荐挑战数据集 
让我们从抓取的数据集开始。
抓取的数据集
对于这个数据集,我们从 indeed.com 抓取了模拟简历(我们使用这些数据仅用于学习和研究目的)。我们将下载用户的 PDF 格式的简历。这些将成为我们的数据集。这个代码可以在以下 GitHub 链接中找到:github.com/jalajthanaki/Basic_job_recommendation_engine/blob/master/indeed_scrap.py。
请查看以下截图给出的代码:

图 6.1:抓取数据的代码片段
使用前面的代码,我们可以下载简历。我们使用了requests库和urllib来抓取数据。所有这些下载的简历都是以 PDF 形式,因此我们需要解析它们。为了解析 PDF 文档,我们将使用一个名为PDFminer的 Python 库。我们需要从 PDF 文档中提取以下数据属性:
- 
工作经验 
- 
教育 
- 
技能 
- 
奖项 
- 
证书 
- 
其他信息 
您可以查看以下截图所示的代码片段:

图 6.2:解析 PDF 文档的代码片段
基本上,PDFminer将 PDF 的内容转换为文本。一旦我们使用正则表达式获取了文本数据,我们就可以获取必要的详细信息。您可以通过使用以下 GitHub 链接查看整个代码:github.com/jalajthanaki/Basic_job_recommendation_engine/blob/master/pdf_parse.py。
一旦我们获取了所有必要的信息,我们将以 pickle 格式保存数据。现在,您不需要抓取数据并获取所有必要的信息。我已经将数据上传到 pickle 文件格式,您可以通过以下 GitHub 链接查看:github.com/jalajthanaki/Basic_job_recommendation_engine/blob/master/resume_data.pkl
我们将使用resume_data.pkl文件作为我们的基线方法。
职业推荐挑战数据集
这个数据集由www.careerbuilder.com提供,并在 Kaggle 上托管。您可以通过此链接下载数据集:www.kaggle.com/c/job-recommendation/data。这些是我们将要用于我们修订和最佳方法的数据文件。这些数据文件中的所有值都是用制表符分隔的:
- 
apps.tsv
- 
users.tsv
- 
jobs.zip
- 
user_history.tsv
apps.tsv
此数据文件包含用户求职申请的记录。它表示特定用户申请的职位。职位由 JobID 列描述。有关此数据文件的必要信息在以下截图给出:

图 6.3:关于 apps.tsv 的数据信息
有五个数据列:
- 
UserId: 这表示给定用户的唯一标识符。通过使用此 ID,我们可以访问用户的个人资料。
- 
WindowsID: 这是一个具有常量值 1 的掩码数据属性。此数据属性对我们来说并不重要。
- 
Split: 此数据属性表示我们应该考虑哪些数据记录用于训练和测试。
- 
Application date: 这是用户申请工作的时间戳。
- 
JobID: 此属性表示用户提名的工作的JobIds。使用此JobId,我们可以访问特定工作的其他信息。
users.tsv
此数据文件包含用户个人资料和所有相关用户信息。您可以在以下屏幕截图中找到所有必要的信息:

图 6.4 关于 users.tsv 的数据信息
这些是数据属性:
- 
UserID: 此数据属性表示用户的唯一识别号码。
- 
WindowID: 这是一个具有常量值 1 的掩码数据属性。此数据属性对我们来说并不重要。
- 
Split: 此数据属性表示我们应该考虑哪些数据记录用于训练和测试。
- 
City: 此属性表示用户的当前城市。
- 
State: 此属性表示用户的州。
- 
国家:此属性表示用户的国籍。 
- 
ZipCode: 此数据属性表示用户的邮政编码。
- 
DegreeType: 此数据列表示用户的学位;用户是高中毕业生还是拥有学士学位。
- 
Major: 此数据属性表示用户学位的主修科目。
- 
GraduationDate: 此数据属性表示用户的毕业日期。
- 
WorkHistoryCount: 此数据属性表示用户工作过的公司数量。
- 
TotalYearsExperience: 此数据列表示用户的总工作经验年数。
- 
CurrentlyEmployed: 此数据属性具有二进制值。如果用户目前有工作,则值为是;如果没有,则值为否。
- 
ManagedOthers: 此数据属性也有二进制值。如果用户正在管理其他人,则此列的值为是;如果用户没有管理其他人,则此列的值为否。
- 
ManagedHowMany: 此数据属性具有数值。此列的值表示由用户管理的员工人数。如果用户没有管理任何人,则此值为 0。
Jobs.zip
当您解压此 ZIP 文件时,您可以获取jobs.tsv文件。以下屏幕截图中有更多信息:

图 6.5:关于 jobs.tsv 的数据信息
- 
JobID: 这是数据集中每个工作唯一的标识符。
- 
WindowID: 这是一个具有常量值 1 的掩码数据属性。此数据属性对我们来说并不重要。
- 
Title: 此数据属性表示工作标题。
- 
Description: 此数据属性表示工作描述。
- 
Requirements:这个数据属性表示工作要求。
- 
City:这个数据字段表示工作地点的城市。
- 
State:这个数据字段表示工作地点的州。
- 
Country:这个数据字段表示工作地点的国家。
- 
Zip5:这个数据字段表示工作地点的 ZIP 代码。
- 
StartDate:这个日期表示工作发布或开放申请的时间。
- 
EndDate:这个日期是工作申请的截止日期。
user_history.tsv
user_history.tsv 文件包含用户的职业历史。关于此信息,以下截图提供了更多内容:

图 6.6:关于 user_history.tsv 的数据信息
这个数据文件只有两个新列。
- 
Sequence:这个序列是一个数值字段。数字表示用户职业的顺序。
- 
JobTitle:这个数据字段表示用户的职位名称。
我们已经覆盖了我们数据文件中的所有属性;现在让我们开始构建基线方法。
构建基线方法
在本节中,我们将构建基线方法。我们将使用抓取的数据集。我们将使用的主要方法是 TF-IDF(词频,逆文档频率)和余弦相似度。这两个概念已经在第四章,电子商务推荐系统中描述过。相关章节的名称是使用 TF-IDF 生成特征和构建余弦相似度矩阵。
由于这个应用程序有更多的文本数据,我们可以使用 TF-IDF、CountVectorizers、余弦相似度等。没有任何工作有评分。因此,我们没有使用其他矩阵分解方法,如 SVD,或基于相关系数的方法,如皮尔逊 R 相关系数。
对于基线方法,我们试图找出简历之间的相似度,因为这将告诉我们用户配置文件有多相似。通过使用这个事实,我们可以向所有具有类似专业配置文件的用户推荐工作。对于基线模型,我们的上下文是生成简历之间的相似度分数。
实施基线方法
为了开发一个简单的职位推荐系统,我们需要执行以下步骤:
- 
定义常量 
- 
加载数据集 
- 
定义辅助函数 
- 
生成 TF-IDF 向量和余弦相似度 
定义常量
我们将定义一些常量值。这些值基于我们抓取的数据集。在我们的数据集中,我们抓取了七家公司的模拟简历,并且通过解析简历生成了七个数据属性。我们将 100 份简历视为我们的第一个训练数据集,50 份简历作为我们的测试数据集。我们的第二个训练数据集的大小是 50。您可以参考以下截图所示的代码片段:

图 6.7:定义常量的代码片段
在此步骤之后,我们将加载数据集。
加载数据集
如您所知,我们已解析了 PDF 文件格式的简历。我们将解析的数据存储为 pickle 格式,并需要加载该 pickle 文件。我们将使用dill库来加载 pickle 文件。您可以在以下屏幕截图所示的代码片段中参考:

图 6.8:加载数据集的代码片段
我们已经恢复了数据集。作为下一步,我们需要定义函数,以便我们可以构建一个基本的职位推荐系统。
定义辅助函数
有各种辅助函数对我们很有用。对于这种方法,总共有三个辅助函数:
- 
my_normalize
- 
get_sim_vector
- 
get_class
第一个函数用于标准化测试分数。我们将以矩阵的形式获得测试分数。您可以在以下屏幕截图所示的代码片段中查看:

图 6.9:辅助函数 my_normalize 的代码片段
这种标准化实际上就是测试分数矩阵的加权平均值。因此,它接受测试分数矩阵并生成标准化的测试分数矩阵。请稍等片刻;当我们生成基线方法的结果时,我们将看到测试分数矩阵的样子。
第二个函数基本上将 TF-IDF 向量矩阵和数据集作为输入。作为输出,它生成余弦相似度分数。您可以在以下屏幕截图中找到提供的代码片段:

图 6.10:辅助函数 get_sim_vector 的代码片段
第三个函数基本上将余弦相似度数组作为输入,并按顺序遍历它以从余弦相似度数组中获得最大余弦值。您可以在以下屏幕截图中找到提供的代码片段:

图 6.11:辅助函数 get_class 的代码片段
我们已经理解了辅助函数的输入、输出和工作原理。现在,让我们看看在生成 TF-IDF 向量和余弦相似度时它们的用法。因此,让我们继续到下一节。
生成 TF-IDF 向量和余弦相似度
在本节中,我们将开发基线方法的核心理念。我们将使用简单的 TF-IDF 概念。为了使用简单的 TF-IDF 构建工作推荐引擎,我们需要执行以下步骤:
- 
构建训练数据集 
- 
为训练数据集生成 IF-IDF 向量 
- 
构建测试数据集 
- 
生成相似度分数 
让我们构建训练数据集。
构建训练数据集
基本上,我们并没有将数据集划分为训练集和测试集。因此,对于训练,我们需要使用以下截图中的代码片段来生成训练数据集:

图 6.12:生成训练数据集的代码片段
代码易于理解。如您所见,我们使用了之前定义的train1_size常量值,以便我们可以生成 100 份可用于训练的简历。
现在,让我们继续下一步。
为训练数据集生成 IF-IDF 向量
为了生成 TF-IDF 向量,我们将使用 scikit-learn 的TfidfVectorizer API。这基本上将我们的文本数据转换为数值格式。您可以查看以下截图中的代码片段:

图 6.13:生成 TF-IDF 的代码片段
通过使用前面的代码,我们可以将我们的文本训练数据集转换为向量格式。当我们为测试数据集生成预测时,使用 TF-IDF 矩阵。现在,让我们构建测试数据集。
构建测试数据集
我们已经训练了模型。现在,我们需要构建测试数据集,以便我们可以检查我们的训练模型在测试数据集上的表现是好是坏。我们已从数据集中使用了 100 份简历进行训练,因此现在我们需要使用那些不属于训练数据集的简历。为了生成测试数据集,我们将执行以下代码,以便我们可以生成测试数据集。您可以参考以下截图中的代码片段:

图 6.14:生成测试数据集的代码片段
如您所见,我们使用简历的索引生成了测试数据集,并仅选取了那些不属于训练集的文档。
生成相似度分数
在本节中,首先,我们将以测试数据集作为输入并为其生成 TF-IDF 向量。一旦生成了 TF-IDF 向量矩阵,我们将使用余弦相似度 API 来生成相似度分数。对于此 API,我们将传递两个 TF-IDF 矩阵。一个矩阵是我们最近使用测试数据集生成的,另一个矩阵是我们使用训练数据集生成的。当我们传递这两个矩阵时,我们将得到余弦相似度数组作为输出。您可以参考以下截图中的代码片段:

图 6.15:生成测试数据集余弦相似度的代码片段
作为输出,我们可以生成以下截图显示的余弦相似度数组:

图 6.16:余弦相似度数组
前一截图显示的数组包含七个元素。每个元素表示该简历与七个公司的相似度。因此,如果最高的余弦值出现在 0 号索引,那么这意味着给定的简历与其他在亚马逊工作的用户的简历更相似。所以,我们将向该特定用户推荐亚马逊的工作机会,因为他们的简历与其他在亚马逊工作的员工更相似。
现在,让我们探讨一些与测试矩阵相关的事实。
理解测试矩阵
当我们使用 TF-IDF、计数向量化器和余弦相似度构建推荐引擎时,我们实际上是在构建基于内容的推荐引擎。没有预定义的测试矩阵可用于生成准确度分数。在这种情况下,我们可能需要手动检查推荐的相关性,或者我们可以采用启发式方法来获得基本的直观分数。在第四章,电子商务推荐系统中,对于基线方法,我们实现了一些基于阈值的启发式方法来了解推荐引擎的工作效果。我建议您参考第四章中的测试基线方法的结果部分。
基线方法的问题
基线方法存在许多问题。我将逐一列出:
- 
数据集中可用的数据属性不足以构建一个良好的职位推荐系统。 
- 
基线方法实际上无法提供准确的职位推荐,因为我们只有用户简历的数据集,基于这些数据,我们只能说“你的简历看起来像亚马逊的其他员工,所以请申请亚马逊的工作机会”。现在的问题是确定我们需要向用户推荐哪种类型的工作:我们应该推荐亚马逊的所有职位空缺,还是其中的一些。 
- 
在我看来,由于数据集的质量和数量,基线解决方案无法提供完整的图景。 
这些问题的解决方案将在下一节讨论。
优化基线方法
在前一节中,我们列出了基线方法的不足。在本节中,我们将探讨如何克服这些不足。我们面临一个主要问题,因为我们没有使用适当的质量和数量来处理数据集。因此,首先,我们需要使用包含用户资料信息以及职位空缺信息的数据库。在这里,我们不再抓取更多简历或发布关于工作的信息。我们正在使用由职业建设者发布的数据库。我们已经在本章前面看到了关于这个数据库的基本信息。
为了构建修订的方法,我们将使用这个新的数据集。现在,让我们开始构建修订的方法。
构建修订的方法
在本节中,我们将使用现成的职位推荐挑战数据集。我们已经覆盖了该数据集的数据属性。我们将使用基于上下文的方法来构建推荐引擎。为了构建修订的方法,我们需要执行以下步骤。修订方法的代码在以下 GitHub 链接中给出:github.com/jalajthanaki/Job_recommendation_engine/blob/master/Job_recommendation_engine.ipynb
让我们执行以下步骤:
- 
加载数据集 
- 
分割训练和测试数据集 
- 
探索性数据分析 
- 
使用工作数据文件构建推荐引擎 
加载数据集
如您所知,数据集在多个文件中。我们需要加载所有这些文件。请记住,所有数据文件都是.tsv格式,因此我们需要使用\t作为参数。您可以参考以下屏幕截图中显示的代码片段:

图 6.17:加载数据集的代码片段
如您所见,我们使用了 pandas 的read_csv方法,并将分隔符作为参数,以五种不同的数据框形式加载数据集。
分割训练和测试数据集
有三个数据文件,其中包含训练和测试两种类型的数据记录。以下数据框如下:
- 
应用程序 
- 
user_history 
- 
users 
在前面的数据框中,一些记录被标记为Train,一些记录被标记为Test。数据属性Split指示哪些数据记录被认为是训练数据集的一部分,哪些用于测试。因此,我们需要过滤我们的数据集。您可以查看以下屏幕截图中给出的代码片段:

图 6.18:分割训练和测试数据集的代码片段
我们对三个数据框都应用了一个简单的过滤操作,并将它们的输出存储在新数据框中。
现在,让我们继续到探索性数据分析(EDA)部分。
探索性数据分析
在本节中,我们将执行一些基本分析,以便我们可以找出数据集中存在哪些类型的数据。对于修订的方法,我们正在使用工作数据框中给出的数据属性构建推荐系统。因此,在将其用于构建推荐引擎之前,我们将检查数据记录的质量。我们需要检查数据集中是否存在任何空白值。除此之外,我们还需要检查这个数据框的数据分布。
我们将对地理定位数据属性执行 EDA。在这里,我们对三个数据列:城市、州和国家进行了分组操作。您可以查看以下屏幕截图给出的代码片段:

图 6.19:按城市、州和国家进行分组
如您在代码片段中看到的,有许多记录中缺少州名称。我们需要注意这些记录。
除了这个之外,我们还需要按国家计数数据记录,以便我们可以找出每个国家有多少数据记录。您可以参考以下屏幕截图所示的代码片段:

图 6.20:按国家计数数据记录的代码片段
如您在前面的代码片段中看到的,大约有 100 万个来自美国地区的职位。我们可以说,在我们的数据集中,大多数职位的国家位置是美国。为了使我们的工作更简单,我们只考虑国家为美国的职位。您可以参考以下屏幕截图给出的代码片段:

图 6.21:所有国家为美国的记录的代码片段
在这里,我们需要检查城市或州数据列中是否存在空数据值。观察前面代码的输出后,我们可以看到没有数据记录中缺少城市或州名称。
现在,让我们看看我们拥有最多职位空缺的州。请记住,我们只考虑了国家位置为美国的职位。为了找出按州划分的职位数量,您可以参考以下屏幕截图给出的代码片段:

图 6.22:按州生成就业数量代码片段
您也可以参考以下屏幕截图所示的图形:

图 6.23:按州划分的就业数量图
如您所见,最多的工作机会在加利福尼亚州、德克萨斯州、佛罗里达州、伊利诺伊州和纽约州。我们已经为改进的方法做了足够的 EDA。现在,我们将开始构建推荐引擎。
使用工作数据文件构建推荐引擎
在本节中,我们将探索代码,看看我们如何构建一个职位推荐引擎。我们将使用 TF-IDF 和余弦相似度概念来构建推荐引擎。
我们已经考虑了jobs_US dataframe。这个 dataframe 包含国家为美国的职位。因此,我们没有任何垃圾数据记录。我们将只考虑 10,000 条数据记录进行训练,因为训练 1 百万条数据记录是耗时的。您可以参考以下屏幕截图所示的代码:

图 6.24:构建修订后方法的职位数据集代码片段
在这里,我们将关注职位名称和职位描述,以构建推荐引擎。由于我们使用的是职位的元数据,这是基于内容的方法。我们对职位名称和职位描述应用连接操作,并将nan 值替换为空字符串值。您可以参考以下截图中的代码:

图 6.25:应用连接操作的代码片段
现在,我们将为连接字符串生成 TF-IDF 向量。我们将使用 TF-IDF 向量矩阵来生成余弦相似度分数。我们将使用 scikit-learn 中的linear_kernel函数来生成余弦相似度。这个函数比 scikit-learn 中的cosine_similarity函数生成余弦相似度所需的时间更短。您可以参考以下截图中的代码片段:

图 6.26:生成 TF-IDF 和余弦相似度的代码片段
如您所见,我们在这里生成了一个高维的 TF-IDF 矩阵。通过使用linear_kernel,我们还生成了余弦相似度分数。
在我们完成修订后方法的实现后,现在我们需要测试推荐。
测试修订后的方法
在本节中,我们将基于任何给定的职位名称生成类似类型的职位推荐。在这里,我们将职位名称作为输入传递,借助余弦相似度分数,我们可以生成任何用户可以申请的 10 个类似类型的职位。
例如,假设一个人是 SAP 商业分析师。那个人可能想申请类似类型的职位,所以在这里,我们的函数将职位名称作为输入,并为该特定用户生成前 10 个类似类型的职位。生成前 10 个职位推荐的代码如下截图所示:

图 6.27:生成前 10 个职位推荐的代码片段
当我们看到输出时,推荐开始变得有意义。作为 SAP 商业分析师的人将获得如 SAP FI/ Co 商业分析师的职位推荐。修订后方法的结果对我们来说令人满意,推荐看起来是相关的。
修订后方法的问题
在本节中,我们将讨论修订后方法的问题。在最佳方法中,我们可以解决这个问题。在修订后的方法中,我们只使用了职位数据属性。我们没有考虑用户的个人资料或用户的偏好。在实施最佳方法时,我们也将考虑用户的个人资料,并根据用户的个人资料向他们推荐职位。
在下一节中,我们将探讨如何优化改进后的方法的直观想法。
理解如何改进改进后的方法
到目前为止,我们使用了工作数据文件中给出的数据属性,但我们还没有使用users数据文件和apps数据文件中的数据属性。users数据文件包含用户的个人资料信息,而apps数据文件包含关于哪些用户申请了哪些工作的信息。
最佳方法有三个简单的步骤:
- 
首先,借助用户的个人资料,我们将找到并生成前 10 个相似用户。 
- 
我们将尝试找出这 10 个人申请的工作。然后我们可以生成 JobIDs。
- 
现在,我们将使用 JobIDs生成工作标题。
在这里,我们已经考虑了用户的个人资料,因此推荐更加具体地针对特定的用户群体。现在,让我们开始实施它。
最佳方法
我们已经看到了如何构建最佳可能方法的直观方法。在这里,我们将使用与改进方法中相同的技巧。在这个方法中,我们添加了更多的数据属性,以使推荐引擎更加准确。您可以通过使用此 GitHub 链接来参考代码:github.com/jalajthanaki/Job_recommendation_engine/blob/master/Job_recommendation_engine.ipynb。
实施最佳方法
这些是我们需要采取的步骤,以实现最佳可能的方法:
- 
过滤数据集 
- 
准备训练数据集 
- 
应用连接操作 
- 
生成 TF-IDF 和余弦相似度分数 
- 
生成推荐 
让我们开始实施这些列出的步骤。
过滤数据集
在这一步中,我们需要过滤用户数据框。我们正在对国家数据列应用过滤器。我们需要考虑基于美国的用户,因为大约有 30 万用户基于美国之外,其他用户来自世界各地的其他地方。过滤用户数据框的代码在以下截图中给出:

图 6.28:过滤用户数据框的代码片段
现在,让我们准备训练数据集。
准备训练数据集
有 30 万用户,但由于训练时间和计算能力的限制,我们并没有考虑所有用户。在这里,我们只考虑了 10,000 个用户。如果您有更多的计算资源,那么您可以考虑更多的用户数量。您可以参考以下截图中的代码片段:

图 6.29:用于选择训练数据记录的代码片段
现在,让我们继续下一步。
应用连接操作
在这一步,我们基本上是在执行连接操作。为了找到一个相似的用户个人资料,我们将连接用户的学位类型、专业和经验年数。我们将为这个连接的数据值生成 TF-IDF 和余弦相似度。您可以参考以下截图给出的代码片段:

图 6.30:应用连接操作代码片段
现在,我们将使用这个连接的数据值生成 TF-IDF 和余弦相似度分数。
生成 TF-IDF 和余弦相似度分数
在本节中,我们将使用 scikit-learn API 生成 TF-IDF 和余弦相似度分数。我们使用的是我们在改进方法中使用的相同 API。在这里,我们没有改变技术,但我们将更改数据属性。您可以参考以下截图所示的代码片段:

图 6.31:生成 TF-IDF 和余弦相似度代码片段
如您所见,我们已经生成了余弦相似度分数,因此基于这个分数,我们可以生成一个相似的用户个人资料,并根据他们的求职记录给出职位推荐。
生成推荐
为了生成职位推荐,我们需要执行以下步骤:
- 
步骤 1:为了生成前 10 个相似的用户个人资料,我们需要传递 UserID,作为输出,我们得到与输入 UserID 最相似的 10 个 UserID。您可以参考以下截图: ![生成推荐]() 图 6.32:生成前 10 个相似用户代码片段 
- 
步骤 2:我们将取步骤 1 中生成的 userIDs列表,并尝试在 apps 数据框中找到相同的UserIDs。这种搜索操作的目的在于我们需要知道哪个用户申请了哪个职位。通过使用 apps 数据框,我们得到JobIDs。
- 
步骤 3:一旦我们获得 JobIDs,我们将使用 jobs 数据框获取职位名称。
步骤 2 和步骤 3 的代码片段如下截图所示:

图 6.33:获取 JobIDs 和职位名称代码片段
如您所见,我们已经为UserID 47找到了相似用户,并且正如我们在工作推荐中看到的那样,我们根据用户的个人资料和教育资格得到了相当相关的职位。在推荐中,我们可以看到佛罗里达地区的医疗领域职位。这是因为,在我们的用户基础中,大多数用户的个人资料来自医疗背景。因为我们同时考虑了用户个人资料和职位个人资料,所以我们能够得到最相关的职位推荐。
摘要
在本章的整个过程中,我们使用基于内容的方法来开发一个职位推荐引擎,并且你学习了如何抓取数据集并构建基线职位推荐引擎。之后,我们探索了另一个数据集。为了改进和最佳的方法,我们使用了现成的数据集。在改进方法的发展过程中,我们考虑了职位的元数据,并构建了一个表现相当不错的推荐系统。对于最佳方法,我们试图找出相似的用户档案。基于用户的档案,我们向用户群体推荐了职位。
在下一章中,我们将构建一个摘要应用。在那里,我们将查看医学领域的文档并尝试对它们进行总结。我们将使用深度学习算法来构建这个应用。所以,继续阅读吧!
第七章:文本摘要
在本章中,我们将构建摘要应用。我们将特别关注文本数据集。我们的主要目标是针对病历进行摘要任务。基本上,我们的想法是提出一个很好的解决方案来总结医学转录文档。
这种摘要应用以极大的方式帮助医生。你问如何?让我们举一个例子。假设一个患者有 10 年的某种疾病病史,10 年后,他为了更好的结果而咨询一位新医生。在第一天,患者需要将他们过去 10 年的医疗处方交给这位新医生。之后,医生将需要研究所有这些文件。医生还依赖于他与患者之间的对话。通过使用病历和与患者的对话,医生可以了解患者的健康状况。这是一个相当费时的方法。
然而,如果我们能够为患者的病历生成摘要并提供这些摘要文档给医生,会怎样呢?这似乎是一个很有前景的解决方案,因为这样我们可以节省医生的时间和精力。医生可以以高效和准确的方式了解他们的患者问题。患者可以从与医生的第一次会面开始接受治疗。这对双方都是双赢的局面,这正是我们在这里试图构建的解决方案。因此,在本章中,我们将涵盖以下主题:
- 
理解摘要的基本原理 
- 
介绍问题陈述 
- 
理解数据集 
- 
构建基线方法: - 
实现基线方法 
- 
基线方法的缺陷 
- 
优化基线方法 
 
- 
- 
构建修订的方法: - 
实现修订的方法 
- 
修订方法的缺陷 
- 
理解如何改进修订的方法 
 
- 
- 
最佳方法: - 实现最佳方法
 
- 
最佳方法:为亚马逊评论构建摘要应用 
- 
摘要 
理解摘要的基本原理
在本节中,我们将关注摘要的基本概念。在当今信息快速发展的时代,文本摘要已成为一个重要的工具。人类为大型文本文档生成摘要将变得困难。如今,网络上有很多文档。因此,我们需要一个能够高效、准确、智能地自动生成文档摘要的解决方案。这项任务被称为自动文本摘要。
自动文本摘要主要是关于在很短的时间内从大量文本文档中找到相关信息。基本上,有两种类型的摘要:
- 
提取式摘要 
- 
抽象式摘要 
让我们逐一看看摘要的类型。
提取式摘要
在提取式摘要方法中,我们将通过从原始文档中选择单词、短语或句子来生成文档的摘要。我们将使用诸如词频-逆文档频率(TF-IDF)、计数向量器、余弦相似度和排名算法等概念来生成此类摘要。
我们已经在第四章 电子商务推荐系统 的 理解 TF-IDF 部分介绍了 TF-IDF、计数向量器和余弦相似度等概念。当我们在本章中实现代码时,我们将探讨排名机制。
抽象摘要
在抽象摘要方法中,我们将尝试让机器学习内部语言表示,以便通过释义生成更类似人类的摘要。
为了实现此类摘要,我们将使用深度学习算法,如具有注意力机制的序列到序列模型。你将在本章后面部分了解该算法和概念。
介绍问题陈述
在本章开头,我们已经概述了问题陈述。在这里,我们将进一步深入细节。我们想要构建一个自动文本摘要应用程序。我们将提供一个医疗转录文档作为输入。我们的目标是生成该文档的摘要。请注意,在这里,我们将提供一个单独的文档作为输入,并且作为输出,我们将生成该单个文档的摘要。我们希望生成一个信息性摘要。信息性摘要是一种摘要,其中摘要文档在信息聚合方面是原始文档的替代品。这是因为我们正在处理医疗领域。
最初,我们在我们的方法中使用提取式摘要方法。我们将为医疗文档生成提取式摘要。在本章的后面部分,我们还将开发一个可以生成亚马逊评论抽象摘要的解决方案。
现在,是时候探索数据集并查看我们在访问数据集时面临的挑战了。
理解数据集
本节分为两部分。在第一部分,我们需要讨论我们在生成数据集时面临的挑战。在后面的部分,我们将讨论数据集的属性。
获取数据集的挑战
如我们所知,在获取数据集方面,健康领域是一个高度监管的领域。以下是我想要强调的一些挑战:
- 
对于摘要,理想情况下,我们需要一个包含原始文本及其摘要的语料库,这被称为并行语料库。不幸的是,目前没有好的、免费的并行语料库可用于医学文档摘要。我们需要获取这种类型的英语并行数据集。 
- 
有一些免费的数据集可用,例如 MIMIC II 和 MIMIC III 数据集,但它们不会包含医学转录的摘要。我们只能从这个数据集中获取医学转录。获取这个数据集是一个漫长且耗时的过程。 
为了解决上述挑战,专业人士、研究人员、学者和大型科技公司需要站出来,为医学领域提供高质量、免费的数据集。现在让我们看看如何获取医学转录数据集。
理解医学转录数据集
你可能会想知道如果没有并行数据集,我们如何构建摘要应用?这里有一个解决方案。我有一个来自 MIMIC – II 数据集的样本医学转录。我们将使用这些数据并生成文档的提取摘要。除此之外,我们还将参考www.mtsamples.com以了解我们可能拥有的不同类型的医学转录。通过使用最少数量的文档,我们将构建摘要应用。你可以在下面的图中看到这些医学转录的样子:

图 7.1:样本医学转录
通常,在医学转录中,有几个部分,具体如下:
- 
主诉: 本节描述了患者面临的主要问题或疾病 
- 
患者病史: 本节详细描述了患者的医疗状况及其类似疾病或其他类型疾病的病史 
- 
既往病史: 本节描述了患者过去所患疾病的名称 
- 
既往手术史: 如果患者过去有过手术,那么这些手术的名称将在此处提及 
- 
家族史: 如果家庭成员有相同类型的疾病或家族中存在某些疾病的病史,那么这些信息将在此节中提及 
- 
药物: 本节描述了药物名称 
- 
体格检查: 本节包含所有与体格检查相关的描述 
- 
评估: 本节包含在考虑所有先前参数后,患者可能患有的潜在疾病的详细信息 
- 
建议: 本节描述了针对患者投诉的推荐解决方案 
- 
关键词:本节包含可以正确描述整个文档的关键词,因此数据集也可以用于主题建模任务 
这种转录在某个部分是随机的。一些转录包含所有前面的部分,而一些则不包含。因此,这种文档的章节数量可能会有很大的变化。
现在让我们看看与亚马逊评论数据集相关的细节。
理解亚马逊评论数据集
在本章的后面部分,我们将使用亚马逊评论数据集来生成摘要。因此,了解这个数据集的基本数据属性会更好。首先,您可以通过使用此链接下载该数据集:www.kaggle.com/currie32/summarizing-text-with-amazon-reviews/data。您需要下载的文件名为Reviews.csv。
您可以通过参考以下截图来查看这个数据集的内容:

图 7.2:来自亚马逊评论数据集的数据记录
让我们了解这个数据集的每个数据属性:
- 
ID:这个属性表示数据记录的序列号。
- 
ProductId:这个属性表示特定产品的唯一 ID。
- 
UserId:这个属性表示分享特定产品评论的用户的唯一用户 ID。
- 
ProfileName:这个数据属性是用户的个人资料名称。使用这个个人资料名称,用户将提交他们的评论。
- 
HelpfulnessNumerator:这个属性表示有多少其他用户认为这条评论在积极方面是有用的。
- 
HelpfulnessDenominator:这个属性表示对这条评论是否有用进行投票的总用户数。
- 
Score:这是特定产品的评分。零表示用户不喜欢它,而五表示用户非常喜欢它。
- 
Time:这个属性表示评论提交的时间戳。
- 
Summary:这个属性非常有用,因为它表示整个评论的摘要。
- 
Text:这个属性是任何给定产品的长文本评论。
现在我们已经看过这两个数据集了。让我们继续到下一节。
构建基线方法
在本节中,我们将实现文本摘要应用的基线方法。我们将使用医疗转录来生成摘要。在这里,我们将使用一个小型的 MIMIC-II 数据集进行试验,该数据集包含一些样本医疗文档,以及www.mtsamples.com来获取医疗转录。您可以通过使用此 GitHub 链接找到代码:github.com/jalajthanaki/medical_notes_extractive_summarization/tree/master/Base_line_approach。
让我们开始构建基线方法。
实现基线方法
在此,我们将按照以下步骤构建基线方法:
- 
安装 Python 依赖项 
- 
编写代码并生成摘要 
安装 Python 依赖项
为了开发摘要应用程序,我们将使用两个非常容易使用的 Python 依赖项。一个是 PyTeaser,另一个是 Sumy。您需要执行以下命令来安装这两个依赖项:
$ sudo pip install pyteaser
$ sudo pip install sumy or $ sudo pip3 install sumy
注意
注意,PyTeaser 库仅适用于 python 2.7。Sumy 可以与 python 2.7 和 python 3.3+ 一起使用。
现在让我们编写代码。
编写代码和生成摘要
PyTeaser 和 Sumy 库都拥有出色的功能。它们可以将任何网页 URL 作为输入,并为给定的网页 URL 生成摘要。您可以参考以下截图中的代码片段:

图 7.3:使用 PyTeaser 生成摘要的代码片段
如您所见,我们正在传递来自 www.mtsamples.com 的样本医学转录的 weburl。PyTeaser 库将生成文档的前五句话作为摘要。要查看输出,您可以查看以下截图:

图 7.4:使用 PyTeaser 对医学转录的摘要
现在让我们尝试使用 Sumy 库。您可以参考以下截图中的代码:

图 7.5:使用 Sumy 生成摘要的代码片段
在 Sumy 库中,我们需要将 weburl 作为输入,但有一个区别。正如您在前面的代码中所看到的,我们提供了 SENTENCES_COUNT = 10,这意味着我们的摘要或输出有 10 个句子。我们可以通过使用 SENTENCES_COUNT 参数来控制语句的数量。您可以参考以下图中的输出:

图 7.6:使用 Sumy 对医学转录的摘要
如果您查看并比较 Sumy 和 PyTeaser 库的输出,那么您可以说与 PyTeaser 库相比,Sumy 库的表现非常好。如您所见,这两个库都获得了给定文档的基本摘要。这些库正在使用排名算法和单词频率来获取摘要。我们无法控制它们的内部机制。您可能会想知道我们是否可以自己进行摘要,以便我们可以根据需要优化代码。答案是肯定的;我们可以为这个任务开发我们的代码。在那之前,让我们讨论这个方法的缺点,然后我们将使用改进的方法构建自己的代码。
基线方法的问题
在这里,我们将讨论基线方法的缺点,以便我们可以在下一次迭代中注意这些缺点:
- 
如前所述,我们并不完全拥有这些库的代码。因此,我们无法轻松地更改或添加功能。 
- 
我们已经获得了一种基本的摘要类型,因此我们需要提高摘要的结果。 
- 
由于缺乏平行语料库,我们无法构建一个可以为医学文档生成摘要的解决方案。 
这些是基线方法的三个主要缺点,我们需要解决它们。在本章中,我们将重点关注前两个缺点。对于第三个缺点,我们无法做太多。所以,我们必须忍受这个缺点。
让我们讨论我们将如何优化这种方法。
优化基线方法
在本节中,我们将讨论如何优化基线方法。我们将实现一个简单的摘要算法。这个算法背后的思想很简单:这种方法也为医学文档生成提取摘要。我们需要执行以下步骤:
- 
首先,我们需要确定给定文档中单词的频率。 
- 
然后,我们将文档拆分为一系列句子。 
- 
为了生成摘要,我们选择具有更多频繁单词的句子。 
- 
最后,我们重新排序摘要句子,以确保生成的输出与原始文档对齐。 
前面的算法可以解决我们的两个缺点,尽管我们可能需要帮助来解决第三个缺点,因为目前没有可用于摘要任务的语料库,尤其是在医学领域。对于这一章,我们必须忍受这个缺点(不幸的是,我们没有其他选择),但不用担心。这并不意味着我们不会学习如何生成摘要。为了学习如何生成摘要,我们将在本章后面使用亚马逊评论数据集。
现在,让我们实现本节中描述的算法步骤。
构建修订的方法
现在,我们将编写我们在上一节中讨论的算法。实现后,我们将检查我们的算法表现得好还是不好。这个算法很容易实现,所以让我们从代码开始。您可以在以下 GitHub 链接中找到代码:github.com/jalajthanaki/medical_notes_extractive_summarization/tree/master/Revised_approach。
实施修订的方法
在本节中,我们将逐步实现摘要算法。以下是我们将在这里构建的函数:
- 
get_summarized函数
- 
reorder_sentences函数
- 
摘要函数 
让我们从第一个开始。
get_summarized 函数
基本上,这个函数执行摘要任务。首先,它将以字符串形式将文档内容作为输入。然后,这个函数生成单词的频率,因此我们需要将句子标记化成单词。之后,我们将从给定文档中生成前 100 个最频繁的单词。对于小数据集,前 100 个最频繁的单词可以很好地描述给定数据集的词汇,所以我们不考虑更多的单词。如果您有大数据集,则可以根据数据集的大小考虑前 1,000 个或前 10,000 个最频繁的单词。您可以参考以下图中的代码:

图 7.7:从给定输入文档生成最频繁单词的代码片段
现在我们来编写第二步的代码。我们需要将文档拆分成句子。我们将句子转换为小写。在这里,我们将使用 NLTK 句子分割器。您可以参考以下图中的代码:

图 7.8:从输入文档生成句子的代码片段
在第三步中,我们将遍历最频繁单词的列表,找出包含更多频繁单词的句子。您可以参考以下图中的代码:

图 7.9:生成包含更多频繁单词的句子的代码片段
现在是时候重新排列句子,以便句子顺序与原始输入文档一致。
reorder_sentences 函数
这个函数基本上重新排列了总结的句子,使得所有句子都与原始文档的句子顺序一致。我们将考虑总结句子和原始文档中的句子,并执行排序操作。您可以参考以下图中的代码:

图 7.10:重新排列总结句子的代码片段
现在我们继续到最后一步。
summarize 函数
这个函数基本上生成摘要。这是我们可以在任何其他文件中调用的方法。在这里,我们需要传递输入数据和所需总结内容中的句子数。您可以参考以下图中的代码:

图 7.11:定义可以在类外调用的函数的代码片段
生成摘要
现在我们来看一下这个代码的演示,并为文档生成摘要。我们将传递来自www.mtsamples.com的文本内容,然后尝试生成内容摘要。您可以参考以下图中的代码片段:

图 7.12:调用摘要函数的代码片段
上述代码的输出如下所示:

图 7.13:修订方法的输出
如您所见,输出比基线方法更相关。我们知道我们迄今为止所执行步骤的方法。这种方法让我们清楚地了解如何为医学转录生成提取摘要。这种方法的好处是,我们不需要任何并行摘要语料库。
现在我们来讨论修订方法的缺点。
修订方法的问题
在本节中,我们将讨论修订方法的缺点,如下所述:
- 
修订方法没有排名机制来根据其重要性对句子进行排名。 
- 
到目前为止,我们已经考虑了词频;我们还没有考虑它们相对于其他词的重要性。假设词a在一篇文档中出现了 1000 次。这并不意味着它具有更大的重要性。 
现在我们来看看我们如何克服这些缺点。
理解如何改进修订方法
在本节中,我们将讨论我们应该采取的步骤来改进修订方法。为了获得提取摘要的最佳结果,我们需要使用 TF-IDF 和句子排名机制来生成摘要。我们在第四章的“使用 TF-IDF 生成特征”部分已经介绍了 TF-IDF。我们将通过使用余弦相似度和 LSA(潜在语义分析)来构建排名机制。我们已经在第四章中了解了余弦相似度。让我们来探索 LSA 算法。
LSA 算法
LSA 算法类似于余弦相似度。我们将使用文档段落中的单词生成矩阵。矩阵的行将代表每个段落中存在的唯一单词,列代表每个段落。您可以在以下图中查看 LSA 算法的矩阵表示:

图 7.14:LSA 算法的矩阵表示
LSA 算法的基本假设是,意义相近的单词将出现在相似文本中。正如您从前面的例子中可以看到的,如果我们说单词对(猫,是)出现的频率更高,这意味着它比(猫,老鼠)单词对具有更高的语义意义。这是算法背后的假设的含义。我们生成前面图中给出的矩阵,然后尝试使用奇异值分解(SVD)方法减少矩阵的行数。SVD 基本上是矩阵的分解。
注意
您可以通过以下链接了解更多关于奇异值分解(SVD)的信息:en.wikipedia.org/wiki/Singular-value_decomposition。
在这里,我们正在减少行数(即单词的数量),同时保留列(即段落)之间的相似性结构。为了生成单词对之间的相似度分数,我们使用余弦相似度。这对于构建摘要应用来说已经足够了。
现在,让我们讨论我们采取的方法,以构建生成医学文档提取摘要的最佳解决方案。
最佳方法背后的理念
我们将执行以下步骤以构建最佳方法:
- 
首先,我们将以字符串的形式获取文档的内容。 
- 
我们将解析句子,然后移除停用词和特殊字符。我们将缩写词转换为它们的完整形式。 
- 
之后,我们将生成单词的词元及其词性(POS)标签。词元不过是单词的词根形式,而词性标签表示单词是作为动词、名词、形容词还是副词使用。有许多词性标签可用。您可以在以下网站上找到词性标签列表: www.ling.upenn.edu/courses/Fall_2003/ling001/penn_treebank_pos.html。
- 
我们将为单词生成 TF-IDF 向量的矩阵。 
- 
我们将使用 SciPy库为给定的 TF-IDF 矩阵生成 SVD 矩阵。
- 
最后,使用余弦相似度,我们可以对句子进行排序并生成摘要。 
现在,让我们看看这些步骤的实现。
最佳方法
在本节中,我们将探讨最佳方法的实现。我们还将讨论代码的结构。因此,让我们不浪费时间,直接开始实施。您可以通过以下 GitHub 链接找到代码:github.com/jalajthanaki/medical_notes_extractive_summarization/tree/master/Best_approach。
实施最佳方法
实现代码所需采取的步骤在以下列表中提供:
- 
理解项目结构 
- 
理解辅助函数 
- 
生成摘要 
让我们从第一步开始。
理解项目结构
项目的结构在这里非常重要。我们将有四个不同的文件,我们将在这四个文件中编写代码。您可以在以下图中看到项目的结构:

图 7.15:项目代码文件的结构
有四个代码文件。我将逐一解释它们的用法:
- 
Contractions.py:此文件包含所有缩写的详尽列表,特别是语法缩写。您可以查看以下图中的缩写列表:![Understanding the structure of the project]() 图 7.16:缩写及其全称列表 
- 
Normalization.py:此文件包含预处理步骤的各种辅助函数
- 
Utils.py:此文件包含用于计算 TF-IDF 和获取给定 TF-IDF 矩阵的 SVD 矩阵的辅助函数
- 
Document_summarization.py:此文件使用已定义的辅助函数并为文档生成摘要
现在我们来看看每个文件中定义了哪些辅助函数。
理解辅助函数
我们将按文件方式讨论辅助函数,这样您就会了解哪个辅助函数属于哪个文件。
Normalization.py
此文件包含许多辅助函数。我将根据其使用顺序解释每个辅助函数:
- 
parse_document:此函数将文档内容作为输入,并按句子进行分词。这意味着我们按句子分割字符串。在这里,我们将仅考虑 Unicode 字符串。您可以参考以下图中给出的代码片段:![Normalization.py]() 图 7.17:解析文档的代码片段 
- 
remove_special_characters:此函数将从字符串中删除特殊字符。您可以参考以下图中给出的代码片段以获得更好的理解:![Normalization.py]() 图 7.18:从字符串中删除特殊字符的代码片段 
- 
remove_stopwords:此函数将从句子中删除停用词。您可以参考以下图中给出的代码片段:![Normalization.py]() 图 7.19:删除停用词的代码片段 
- 
unescape_html:此函数将从句子中删除 HTML 标签。您可以参考以下图中给出的代码片段:![Normalization.py]() 图 7.20:删除 HTML 标签的代码片段 
- 
pos_tag_text:此函数将句子分词成单词,之后将为这些单词提供词性标注。您可以参考以下图中给出的代码片段:![Normalization.py]() 图 7.21:生成词性标注的代码片段 
- 
lemmatize_text:此函数将句子分词成单词,然后生成单词的词元。您可以参考以下图中给出的代码:![Normalization.py]() 图 7.22:生成单词词元的代码片段 
- 
expand_contractions:这个函数检查缩写。如果给定句子中存在我们列表中的任何缩写,那么我们将用其全称替换该缩写。你可以参考下面图中显示的代码:![Normalization.py]() 图 7.23:替换缩写为全称的代码片段 
- 
normalize_corpus:这个函数调用所有前面的辅助函数并生成预处理句子。你可以参考下面图中给出的代码:![Normalization.py]() 图 7.24:生成预处理句子的代码片段 
现在我们来看看在utils.py文件中定义了哪些函数。
Utils.py
在这个文件中,只有两个辅助函数。它们在这里进行了描述。
- 
build_feature_matrixs:这个函数使用 scikit-learn 的TfidfvectorizerAPI 生成 TF-IDF 向量。我们提供预处理文本作为输入,输出是一个矩阵。这个矩阵包含了给定单词的向量值。你可以参考下面的代码片段,它提供在以下图中:![Utils.py]() 图 7.25:生成 TF-IDF 向量的代码片段 
- 
low_rank_svd:这个特定的函数使用 Python 的SciPy库的 API。它对 TF-IDF 矩阵执行奇异值分解(SVD),然后我们获得余弦相似度分数。基于这个分数,我们将对句子进行排名。在这里,我们只定义了一个可以生成 TF-IDF 矩阵 SVD 的函数。你可以参考下面图中给出的代码片段:![Utils.py]() 图 7.26:生成 SVD 的代码片段 
现在我们将使用所有这些辅助函数来生成摘要。
生成摘要
在本节中,我们将查看document_summarization.py文件中给出的代码。有两个方法负责生成给定文档的摘要。它们如下:
- 
textrank_text_summarizer:这个方法将预处理文档作为输入,通过使用build_feature_matrix辅助函数,我们将生成 TF-IDF 矩阵。之后,我们将生成相似度分数。基于相似度分数,我们将对句子进行排序并赋予它们排名。作为输出,我们将显示这些排序后的句子。在这里,句子序列与原始文档对齐,所以我们不需要担心这一点。你可以看看下面图中给出的代码片段:![生成摘要]() 图 7.27:使用 textrank_text_summarizer方法生成摘要的代码片段
- 
lsa_text_summarizer:此函数将预处理文本作为输入并生成 TF-IDF 矩阵。然后,在矩阵上应用low_rank_svd方法,我们得到我们的因子矩阵。我们将使用这些因子矩阵生成相似度分数。根据这个相似度分数对句子进行排序后,我们可以生成摘要。您可以参考以下图中显示的代码片段:![生成摘要]() 图 7.28:使用 lsa_text_summarizer 生成摘要的代码片段 
我们将调用这些函数并生成输出。相应的代码片段如下所示:

图 7.29:生成输出摘要的代码片段
您可以查看以下图中显示的输出:

图 7.30:文档 _1 的输出摘要
另一个文档的输出如下所示:

图 7.31:文档 _2 的输出摘要
如您所见,与修订方法相比,我们将为给定文档获得一个更相关的提取型摘要。现在让我们使用亚马逊的产品评论数据集构建抽象摘要应用。
使用亚马逊评论构建摘要应用
我们构建此应用是为了让您了解如何使用并行语料库来为文本数据集生成抽象摘要。我们已经在章节中解释了与数据集相关的基本内容。在这里,我们将介绍如何使用深度学习(DL)算法构建抽象摘要应用。您可以参考以下 GitHub 链接中的代码:github.com/jalajthanaki/Amazon_review_summarization/blob/master/summarize_reviews.ipynb。
您也可以使用此链接下载预训练模型:drive.google.com/open?id=1inExMtqR6Krddv7nHR4ldWTYY7_hMALg。
对于此应用,我们将执行以下步骤:
- 
加载数据集 
- 
探索数据集 
- 
准备数据集 
- 
构建 DL 模型 
- 
训练深度学习(DL)模型 
- 
测试 DL 模型 
加载数据集
在本节中,我们将看到如何加载数据集的代码。我们的数据集是 CSV 文件格式。我们将使用 pandas 来读取我们的数据集。您可以参考以下图中给出的代码片段:

图 7.32:加载数据集的代码片段
探索数据集
在本节中,我们将对数据集进行一些基本分析。我们将检查是否存在任何空值条目。如果有,我们将删除它们。您可以参考以下图中给出的代码片段:

图 7.33:移除空数据条目的代码片段
现在让我们准备可以用于训练模型的数据库。
准备数据集
这些是我们将执行以准备数据集的步骤:
- 
我们将用全称替换文本中出现的缩写 
- 
我们将从审查数据列中移除特殊字符、URL 和 HTML 标签 
- 
我们将从评论中移除停用词 
我们已经完成了所有前面的步骤并生成了无垃圾评论。你可以参考以下图中的代码片段:

图 7.34:执行评论预处理的代码片段
这里共有 132,884 个独特的单词。当你运行代码时,你可以找到词汇表的大小。这些独特的单词是本应用的词汇表,我们需要将这些单词转换为向量格式。单词的向量格式称为词嵌入。你可以使用 Word2vec、Numberbatch 或 GloVe 来生成词嵌入。在这里,我们将使用 Numberbatch 的预训练嵌入模型来生成本应用的词嵌入。Numberbatch 的预训练模型比 GloVe 更优化且更快,所以我们使用 Numberbatch 的模型。你可以参考以下图中的代码片段:

图 7.35:使用 Numberbatch 预训练模型生成词嵌入的代码片段
如果你想了解更多关于 word2vec 的信息,你可以参考我的上一本书,Python 自然语言处理,特别是第六章,高级特征工程和 NLP 算法。链接是www.packtpub.com/mapt/book/big_data_and_business_intelligence/9781787121423/6。
构建深度学习模型
在本节中,我们将构建深度学习算法。我们使用 seq2seq 神经网络。基本上,seq2seq 模型用于处理序列数据。语言或句子是单词的序列。在这个算法中,有一个编码器接受词嵌入并学习语言表示。这个层的输出被输入到解码层。在这里,我们也将使用注意力机制。注意力机制将关注句子的最重要部分。它将存储句子的语义表示。对于注意力机制,我们将使用具有循环神经网络架构的 LSTM 单元,它学习语言的复杂语义表示并将其存储在 LSTM 网络中。当我们生成最终输出时,我们将使用解码器单元的权重以及 LSTM 单元的权重,并生成最终的词嵌入。基于词嵌入,我们将生成摘要。
为了实现这一点,我们需要使用具有注意力机制的循环神经网络(RNN)来构建 seq2seq。您可以在以下图中查看给出的代码:

图 7.36:构建 RNN 编码层的代码片段
您可以参考以下图中给出的代码片段:

图 7.37:构建 RNN 解码层的代码片段
构建 seq2seq 模型的代码片段如下所示:

图 7.38:构建 seq2seq 的代码片段
现在让我们开始训练模型。
训练深度学习模型
基本上,我们已经构建了神经网络,现在是时候开始训练了。在本节中,我们将定义所有超参数的值,例如学习率、批量大小等。您可以在以下图中查看给出的代码:

图 7.39:训练模型的代码片段
在训练过程中,我们将追踪损失函数并使用梯度下降算法,并尝试最小化我们的损失函数值。您可以在以下图中查看给出的代码片段:

图 7.40:追踪损失函数的代码片段
在这里,我们已经将模型在 CPU 上训练了 6 到 8 小时,并且我们得到了损失值 1.413。您也可以将模型训练更长的时间。现在让我们测试一下训练好的模型。
测试深度学习模型
在本节中,我们加载了训练好的模型,并为随机选择的评论生成了摘要。您可以在以下图中查看给出的代码片段:

图 7.41:生成给定评论摘要的代码片段
前述代码的输出如下所示:

图 7.42:给定评论的总结
如果我们想要为给定的文本数据生成一行总结,这种方法非常好。在未来,如果我们有用于总结任务的并行医学转录数据集,那么这种方法将工作得很好。
摘要
在本章中,我们为医学转录构建了摘要应用。一开始,我们列出了为了在医学领域生成好的平行语料库以用于摘要任务所面临的挑战。之后,为了我们的基线方法,我们使用了现成的 Python 库,如PyTeaser和Sumy。在改进的方法中,我们使用了词频来生成医学文档的摘要。在最佳的方法中,我们结合了基于词频的方法和排名机制,以生成医学笔记的摘要。
最后,我们开发了一个解决方案,其中我们使用了亚马逊的评论数据集,这是摘要任务的平行语料库,并构建了基于深度学习的摘要模型。我建议研究人员、社区成员以及其他人积极向前,构建高质量的可以用于构建健康和医疗领域的一些优秀数据科学应用的数据集。
在下一章中,我们将构建聊天机器人。聊天机器人,或虚拟助手,在过去的几年中已经成为数据科学领域的一个热门话题。因此,在下一章中,我们将考虑一个电影对话数据集和 Facebook 的bAbI数据集。借助这些数据集,并通过使用深度学习算法,我们将构建聊天机器人。所以,如果你想学习如何为自己构建一个,那么请继续阅读!
第八章. 开发聊天机器人
2017 年是关于聊天机器人的年份,这一趋势在 2018 年继续。聊天机器人根本不是什么新鲜事物。聊天机器人的概念自 20 世纪 70 年代以来就已经存在。有时,聊天机器人应用程序也被称为问答系统。这是一个更具体的聊天机器人技术术语。让我们回顾一下历史。Lunar 是第一个基于规则的问答系统。使用这个系统,地质学家可以就阿波罗任务中的月球岩石提出问题。为了改进在阿波罗任务中使用的基于规则的系统,我们必须找到一种方法来编码基于模式的问题和答案。为此,我们使用了人工智能标记语言,也称为AIML。这有助于程序员用更少的代码行来实现我们通过使用硬编码的基于模式系统所生成相同的结果。随着机器学习(ML)领域的最新进展,我们可以构建一个无需硬编码响应的聊天机器人。
由于聊天机器人具有许多优势,它们现在被用于应用程序中;例如,用户不需要在他们的手机上安装不同种类的应用程序。如果有聊天机器人可以提供新闻,那么你可以要求 CNN 或《经济学人》上的新闻。像 Facebook、Hike、微信、Snapchat、Slack 等大型科技公司提供聊天机器人以实现更好的客户互动。他们通过创建一个可以引导客户执行某些操作的聊天机器人来实现这一点;它还提供了有关产品及其平台的有用信息。
聊天机器人提供不同的服务。通过使用 Facebook 聊天机器人平台,你可以订购鲜花并查看新闻。这听起来酷吗?从技术上讲,这些聊天机器人是当前时代的应用程序。我简要讨论了聊天机器人的好处,但我们将在本章中详细探讨它们。
在本章中,我们将涵盖以下主题:
- 
介绍问题陈述 
- 
理解数据集 
- 
构建聊天机器人的基本版本: - 
理解基于规则的系统 
- 
理解方法 
- 
理解架构 
 
- 
- 
实施聊天机器人的基于规则系统 
- 
测试基于规则的聊天机器人 
- 
现有方法的缺点: - 理解优化方法的关键概念
 
- 
实施修订的方法: - 
数据准备 
- 
实施序列到序列(seq2seq)模型 
 
- 
- 
测试修订的方法: - 
理解测试指标 
- 
测试聊天机器人的修订版本 
 
- 
- 
修订方法的缺点: - 理解解决现有问题的关键概念
 
- 
最佳方法: - 实施最佳方法
 
- 
摘要 
介绍问题陈述
在本章中,我们的主要目标是了解如何构建一个聊天机器人。聊天机器人,或问答系统(QA 系统),非常有用。让我们考虑一个有趣的例子。假设你是一名学生,你有五本书要读。阅读和理解五本书可能需要时间。如果你能将这些五本书的内容输入到电脑中,只提出相关的问题呢?通过这样做,学生可以更快地学习概念和新信息。众所周知,主要的互联网产品公司正在整理信息,使其易于访问。聊天机器人或 QA 系统将帮助我们理解这些信息背后的含义。这就是为什么聊天机器人是 2017 年的热门词汇。无论你能想到什么应用,你都可以为它制作一个聊天机器人。现在许多消息平台都托管了开发者构建的聊天机器人,包括 Facebook Messenger、Slack、微信等等。聊天机器人是新的应用程序,因为它们已经存在于你每天可能使用十几次的已安装应用程序中。使用 Facebook 聊天机器人 API 开发的聊天机器人位于 Facebook Messenger 应用程序中。你可能每天会使用 Messenger 十几次。因此,用户不需要为特定的功能安装一个单独的应用程序。这将帮助公司更好地与客户互动。
在我们继续之前,我想介绍一些重要的术语,这些术语可以帮助我们了解在本章中我们针对的是哪种聊天机器人开发。首先,让我们了解开发聊天机器人的不同方法:
- 
基于检索的方法 
- 
基于生成的方法 
基于检索的方法
在基于检索的方法中,我们需要定义一组预定义的响应,并将某种启发式方法应用于预定义的响应,以便聊天机器人可以为给定的问题生成最佳可能的答案。这些答案非常依赖于输入问题和该输入问题的上下文。
在开发基于检索的模型的过程中,我们之前只使用了表达式匹配,这可以帮助我们得到适当的答案,但仅使用表达式匹配在这里不会有所帮助。因此,最近研究人员和程序员开始使用表达式匹配和高级机器学习(ML)分类器技术。让我们通过一个例子来了解机器学习分类器如何有助于构建基于检索的聊天机器人。
假设 Alice 需要在朋友的生日时送花。她的一个朋友 Emma 喜欢玫瑰,另一个朋友 Lucy 喜欢百合。Alice 使用一个花订购聊天机器人来预订她的订单。她写道:“我想预订一束多色的玫瑰花束和一束百合花。”在这种情况下,如果我们实现一个基本的机器学习分类器,聊天机器人可以轻松地识别 Alice 正在预订的两个不同的订单,并且它还能够解释每个订单的数量。聊天机器人还会要求提供不同的地址等。通过使用机器学习技术,我们可以编写更复杂的启发式算法,这有助于我们生成更合适的聊天机器人回答。Facebook messenger 聊天机器人 API 就是这样一个例子。
有另一个有趣的例子可以通过使用机器学习启发式算法来解决。比如说,你问聊天机器人:“今天是什么日子?”或者“今天是星期几?”如果我们实现了高级机器学习技术,那么它可以识别出这两个问题虽然措辞不同,但意图相同。在聊天机器人的开发过程中,意图和上下文检测是更复杂的任务,可以通过机器学习技术和一些启发式算法来实现。
现在让我们转向更困难的方法,即基于生成的方法。
基于生成的方法
在基于生成的方法中,没有为聊天机器人提供任何预定义的响应。聊天机器人从头开始生成响应。为了构建基于生成的聊天机器人,我们需要提供大量数据,机器将仅通过观察数据来学习如何回答用户提出的问题。2015 年,谷歌研究人员 Oriol Vinyals 和 Quoc V. Le 提出了一种称为 A 神经对话网络的方法。您可以参考以下论文:arxiv.org/pdf/1506.05869v2.pdf。
在本文中,研究人员使用了康奈尔电影对白数据集。这个数据集被输入到机器中,以便它能够学习基本的英语语言。为此,他们使用了序列到序列(seq2seq)神经网络架构。之后,他们使用了 IT 支持数据集,以便机器获得领域知识。一旦机器在这个数据集上训练完毕,他们就在 IT 支持部门测试了聊天机器人,这个聊天机器人将能够以极高的准确性回答问题。在接下来的章节中,我们将构建自己的神经对话网络。这种方法耗时较少,并克服了我们在基于检索的模型中面临的挑战,如意图识别、上下文识别等。
在此我们还需要讨论一些其他的重要术语。在开发聊天机器人之前,我们需要考虑一些重要的约束条件。第一个与对话领域相关:
- 
开放域 
- 
封闭域 
开放域
首先,让我们了解什么是开放域。有时候对话是模糊和不确定的。让我给你举一个例子。假设你遇到了一个多年未见的老同学。在对话的过程中,你不知道你们将要谈论哪个特定的话题。在这种情况下,对话可以随意展开。因此,对话的领域是不固定的。你可以谈论生活、工作、旅行、家庭等等。你可以谈论无数的话题。这种我们无法限制谈话领域的对话,被称为开放域。开发一个开放域聊天机器人是困难的,因为理想情况下,这种聊天机器人能够以人类水平的准确性回答来自任何领域的每一个问题。
目前,这类聊天机器人尚未被制造。当我们能够制造这种聊天机器人时,它将必须通过图灵测试。让我给你一个图灵测试的简要介绍,以便你能更好地理解解释。这个实验是由伟大的计算机科学家艾伦·图灵在 1950 年创造的。在这个实验中,一个被称为裁判的人向一个人和一个机器提出一系列问题。现在,裁判不知道哪个答案是来自人类,哪个答案是来自机器。但是,在看到或听到答案后,如果裁判无法区分哪些答案是来自人类,哪些答案是来自机器,那么机器就通过了图灵测试,我们可以说机器表现出了人类水平的智能,因为它表现得像人类一样聪明。到目前为止,还没有任何一个聊天机器人能够以人类水平的准确性通过图灵测试。你可以通过访问en.wikipedia.org/wiki/Turing_test了解更多关于图灵测试的信息。这一段技术正在快速发展,所以接下来的五年可能会非常激动人心。
谷歌在开发开放域聊天机器人方面非常积极。它正以谷歌助手的形式构建这个产品,但通过图灵测试的准确性和功能性仍然有限。现在让我们了解第二种类型的领域。
封闭域
封闭域是开放域的对立面。对于封闭域,我们需要限制对话话题。让我们举一个例子:在办公室,我们有时会有会议。在会议之前,参与者知道将要讨论的话题。因此,在会议期间,我们只关注那些话题。在这里,我们不会有无限的话题和领域可以谈论。这种我们限制了可以谈论的领域和话题的对话,被称为封闭域。
如果一家金融机构,如银行,为他们的客户推出聊天机器人,那么开发的聊天机器人不能回答诸如“你能告诉我今天新加坡的天气吗?”这样的问题。但它可以帮助你检查申请信用卡的程序,这是因为聊天机器人可以理解与特定领域相关的提问。一个针对封闭领域的聊天机器人是肯定可能的,而且有很多公司正在构建特定领域的聊天机器人,因为这对与客户群互动是有益的。所以在本章中,我们将重点关注封闭领域聊天机器人。
让我们尝试理解最后一个约束条件;对话长度,这意味着我们将从聊天机器人那里得到的答案的长度。基于这一点,我们需要理解以下术语:
- 
简短对话 
- 
长对话 
简短对话
这种类型的聊天机器人可以生成简短答案。在聊天机器人的开发过程中,我们需要问自己是否期望简短对话。如果我们期望简短答案,那么你应该很高兴,因为基于简短对话的聊天机器人可以很容易地构建。以下是一个简短对话的例子:
人:嗨
机器:你好
人:你好吗?
机器:我很好
这个例子表明了聊天机器人生成的简短对话。
长对话
这种类型的聊天机器人可以生成长答案。机器学习长对话是困难的,因此构建能够生成长对话的聊天机器人是困难的。让我们看看一个长对话的例子:
人:我想给你讲一个故事。
机器:请继续。
人:给你。约翰去了市场。丹尼尔正在去印度的路上。Siri 有一个苹果。Siri 在厨房。所以我的问题是,Siri 在哪里?
机器:根据你的故事,我认为 Siri 在厨房。
正如你在本例中看到的,为了生成正确的答案,机器也应该存储和处理给定的信息,以便它能生成正确的答案。因此,长对话和基于推理的聊天机器人开发起来有点困难。
到目前为止,你已经学习了很多术语。现在让我们看看它们在我们开发聊天机器人时将如何影响我们。根据方法和领域类型,我们可以构建不同类型的聊天机器人。
开放领域和基于生成的方法
我们希望使用基于生成的方法来构建聊天机器人,这种方法在开放领域上运行。这意味着聊天机器人需要从头开始学习如何回答来自任何领域的问题。这里的对话可以朝任何方向发展。这种类型的聊天机器人是通用人工智能(AGI)的一个例子,我们还没有达到那个阶段。
因此,开发这种类型的聊天机器人不是本章的一部分。
开放领域和基于检索的方法
如果我们想要构建一个可以使用基于检索的方法在开放域上运行的聊天机器人,那么作为编码者,我们需要硬编码几乎所有的响应以及可能的问题和变体。这种方法消耗了大量的时间,因此这种类型的聊天机器人也不是本章的一部分。
封闭域和基于检索的方法
我们已经了解到我们无法在开放域进行操作,但关于封闭域呢?我们当然可以在封闭域工作,因为用户可以向聊天机器人提出有限数量的问题,而聊天机器人可以回答。如果我们为封闭域使用基于检索的方法,那么我们可以编写相对简单的问题。我们可以集成一些 NLP 工具,例如解析器、命名实体识别(NER)等,以便生成最准确的答案。
让我们举一个例子。假设我们想要构建一个可以为我们提供任何位置的实时天气信息的聊天机器人。如果我们使用基于检索的方法构建聊天机器人,那么用户将肯定能够得到关于“孟买的天气如何?加利福尼亚的天气如何?今天有降雨的可能吗?”等问题准确的答案。聊天机器人会很好地回答前两个问题,但在第三个问题时,它会感到困惑,因为我们没有提供降雨可能性的位置。如果聊天机器人使用了某些启发式方法,那么你可能会得到一个响应。聊天机器人可能会询问你想要了解降雨可能性的位置,但大多数情况下,这种情况不会发生。聊天机器人会直接告诉你,例如,加利福尼亚的降雨可能性。实际上,我想知道孟买的降雨可能性。所以,这类与上下文相关的问题在基于检索的方法中很常见。我们需要实施基于生成的方法来克服与上下文相关的问题。
封闭域和基于生成的方法
当我们为封闭域使用基于生成的方法时,这种类型聊天机器人的开发所需编码时间更少,答案的质量也得到了提高。如果我们想让我们的聊天机器人理解用户一系列问题中的长上下文和意图,那么基于生成的方法是正确的选择。经过在大语料库上的训练和优化后,聊天机器人可以理解问题的上下文和意图,并且能够提出推理类型的问题。这个聊天机器人开发空间对于研究和实施新想法来说既令人兴奋又有趣。
让我们举一个例子。假设我们构建了一个聊天机器人来向银行申请住房贷款。当用户运行这个聊天机器人时,它可能会问这些问题:我的住房贷款申请状态如何?我这边还有哪些文件需要上传?我会在接下来的 2 天内获得批准吗?您收到我的税务报表和工资条了吗?最后一个问题的上下文取决于第二个问题。这些类型的问题及其答案可以很容易地通过基于生成的方法生成。
参考以下图,它将帮助我们总结前面的讨论:

图 8.1:开发聊天机器人的方法示意图
在本章中,我们将构建一个基于封闭域的聊天机器人,它将使用基于检索和基于生成的方法。
现在让我们看看本章我们将使用的数据集。
理解数据集
为了开发聊天机器人,我们使用了两个数据集。这些数据集如下:
- 
康奈尔电影对话数据集 
- 
bAbI 数据集 
康奈尔电影对话数据集
这个数据集已被广泛用于开发聊天机器人。您可以从以下链接下载康奈尔电影对话语料库:www.cs.cornell.edu/~cristian/Cornell_Movie-Dialogs_Corpus.html。这个语料库包含从原始电影剧本中提取的大量丰富元数据的虚构对话集合。
这个语料库包含 10,292 对电影角色之间的 220,579 个对话交换。它涉及来自 617 部电影中的 9,035 个角色。总共有 304,713 个话语。这个数据集还包含电影元数据。以下是一些元数据类型:
- 
与电影相关的元数据包括以下细节: - 
电影类型 
- 
发布年份 
- 
IMDb 评分 
 
- 
- 
与角色相关的元数据包括以下细节: - 
3,774 个角色的性别 
- 
电影中的角色总数 
 
- 
当您下载这个数据集时,您会注意到我们将在这个章节中使用两个文件。这两个文件的名称是movie_conversations.txt和movie_lines.txt。让我们看看每个文件的内容细节。
movie_conversations.txt 的内容细节
这个文件包含movie_lines.txt文件的line_id。您可以在以下图中看到movie_conversations.txt的内容:

图 8.2:movie_conversations.txt 文件的内容示例
正如您在前面的图中可以看到,这个文件包含行号,实际对话内容位于movie_lines.txt中。+++$+++充当分隔符。您肯定非常想知道如何处理这个数据集;请稍等片刻,我们将在接下来的章节中介绍这一方面。
现在让我们看看下一个文件的内容。
movie_lines.txt 的内容细节
这个文件包含实际的电影对白。你可以在下面的图像中看到 movie_lines.txt 的样本内容:

图 8.3:movie_lines.txt 的样本内容
正如你在前面的图像中可以看到的,每一行都有一个唯一的对话行 ID。这个 line_id 指的是 movie_conversations.txt 文件。这个文件包含相同的行分隔符和参与对话的角色名称。
如果你同时看到这两个文件,那么可能对你来说更有意义。在 movie_conversations.txt 文件中,参考 行号 194, 195, 196 和 197 上的对话。所有这些对话都可以在 movie_lines.txt 文件中找到。在前面的图像中,你可以看到 行号 194 包含这个问题:我们能快点吗?Roxanne Korrine 和 Andrew Barrett 在广场上正在进行一场可怕的公开分手。再次。 另一方面,行号 195 包含这个答案:嗯,我想如果我们从发音开始,如果你觉得可以的话。
在将数据集以问答格式准备并输入到机器之前,我们需要准备这个数据集。我们将在使用它进行训练之前实现数据准备步骤。
现在,让我们看看 bAbI 数据集。
bAbI 数据集
这个数据集是由 Facebook AI Research (FAIR) 构建的,其中 AI 代表人工智能。这个数据集属于 bAbI 项目。你可以从 research.fb.com/downloads/babi/ 下载这个数据集。这是一个维护良好的数据集。bAbI 项目的目标是尝试构建一个自动文本理解和推理系统。这个数据集包括以下子数据集:
- 
(20) QA bAbI 任务 
- 
(6) 对话 bAbI 任务 
- 
儿童书籍测试 
- 
电影对话数据集 
- 
WikiMovies 数据集 
- 
基于对话的语言学习数据集 
- 
简单问题数据集 
- 
HITL 对话模拟器 
我们在这里将只使用一个子集,即 (20) QA bAbI 任务,因为它对于构建聊天机器人最有用。
(20) QA bAbI 任务
让我们详细看看这个子数据集。在这里,使用这个 (20) QA bAbI 数据集执行了 20 个不同的任务。让我们看看这些任务是什么。这些任务赋予机器进行某些推理的能力,基于这些推理,机器可以回答问题。你可以参考以下图像中给出的任务名称:

图 8.4: (20) QA bAbI 任务细节
图片来源:http://www.thespermwhale.com/jaseweston/babi/abordes-ICLR.pdf
Facebook 的研究员 Jason Weston、Antoine Bordes、Sumit Chopra、Alexander M. Rush、Bart van Merriënboer、Armand Joulin 和 Tomas Mikolov 发表了一篇论文,在论文中他们提出了一种有趣的基于 AI 的 QA 系统。你可以通过访问arxiv.org/abs/1502.05698来参考他们的研究论文。在本章中,我们将尝试实现任务 T1 的结果,并将重新生成其结果。
这个数据集包含两种语言的语料库,英语和印地语。这里有两种类型的文件夹:名为en的文件夹有 1,000 个训练示例,而en-10K有 10,000 个训练示例。每个任务数据集的格式如下图所示:

图 8.5:单个支持 QA bAbI 任务的格式
支持的事实被称为故事。基于故事,用户可以向机器提问,机器应该给出可以从提供的支持文本中推导出的逻辑上正确的答案。这是一个困难的任务,因为在这种情况下,机器应该记住它可以随时使用的长上下文。我们很快就会使用这个有趣的数据集。
现在我们开始构建聊天机器人的基本版本。
构建聊天机器人的基本版本
在本节中,我们将构建聊天机器人的基本版本。对于任何公司来说,获取数据都不是问题,但获取特定领域的对话数据集具有挑战性。
现在有那么多公司的目标是要创建一个创新的特定领域聊天机器人,但他们的主要挑战是获取正确的数据。如果你面临同样的问题,那么这个基本方法可以帮助你。这个聊天机器人的基本版本是基于封闭领域和基于检索的方法,它使用基于规则的系统。所以,让我们开始了解基于规则的系统的每个方面。
为什么基于规则的系统有效?
如我之前提到的,基于规则的系统是实现基于检索的方法的方式。现在,你可能想知道为什么我们需要基于规则的系统。考虑到我们生活在机器学习(ML)的时代,这难道听起来不古老吗?让我与你分享我的个人经历。我密切合作了许多初创公司。其中一些在金融领域运营,一些在人力资源领域运营,还有一些在法律领域运营。在这个聊天机器人的时代,初创公司非常热衷于开发特定领域的聊天机器人,以帮助用户。最初,他们使用一些通用数据集,以便机器可以学习语言并为它们生成逻辑因果答案,但他们很快意识到他们没有足够的特定领域数据来帮助他们构建一个好的聊天机器人。让我给你举一个例子。
我与一家金融科技初创公司合作,我们需要构建一个聊天机器人。聊天机器人的具体要求是它应该帮助想要申请住房贷款的客户,以及那些已经申请并需要一些帮助的客户。现在,这家金融科技初创公司刚刚开始运营 1 年半。因此,他们没有关于客户可能提出的问题的大量聊天记录。简而言之,公司没有足够的特定领域数据,例如住房贷款申请人可能提出的问题类型,以及如何将这些客户查询同步到这家金融科技公司遵循的贷款程序。在这种情况下,我们需要关注两个主要方面:
- 
我们需要构建一个最小可行聊天机器人,以帮助客户处理基本的常见问题解答(FAQs)。 
- 
通过这个最小可行聊天机器人的帮助,你还可以了解人们提出的问题类型,并根据这些问题,调整聊天机器人。 
在这类情况下,当我们没有特定领域的数据集时,基于规则的或基于检索的模型将为我们工作。从下一节开始,我们将探讨基于规则的系统以及开发基本聊天机器人和其架构的方法。
理解基于规则的系统
在本节中,我们将介绍基于规则的系统,这样当你我们开始开发基于检索的聊天机器人时,你不会感到被排除在外。基于规则(RB)系统定义为:使用可用的知识或规则,我们开发一个使用规则的系统,在语料库上应用可用的系统规则,并尝试生成或推断结果。从聊天机器人的角度来看,RB 系统包含所有可能的问题和答案,并且它们是硬编码的。我们绝对可以使用正则表达式和模糊逻辑来实现某种启发式方法,以使 RB 系统更加准确。
参考以下图示,它将给你一个关于使用基于检索方法的聊天机器人工作流程的思路:

图 8.6:基于规则的聊天机器人工作流程
根据前面的图示,你知道在一个基于规则的(RB)系统中,我们将手动编写所有可能的问题和答案,以及实现正则表达式和模糊逻辑,这将使聊天机器人具备生成适当答案的能力。根据业务需求,可以从这个系统中添加和删除问题。现在让我们讨论我们的方法,并基于这个方法,我们将构建聊天机器人的基本版本。
理解方法
在本节中,我们将查看有助于我们实现聊天机器人基本版本的步骤。在这里,我正在为金融领域构建一个聊天机器人,它将帮助用户申请住房贷款。我们将编写一些问题,以便你知道如何开发基于规则的聊天机器人。我们需要执行以下步骤:
- 
列出可能的问题和答案。 
- 
决定标准消息。 
- 
理解架构。 
列出可能的问题和答案
首先,我们需要列出所有我们可以代表用户想到的问题。一旦我们决定了这些问题,然后我们需要逐一决定这些问题的答案。假设我们要求用户提供全名、电子邮件 ID、电话号码和贷款金额,以便在用户中途退出时,客户代表可以回电。之后,我们询问用户他们需要什么样的帮助,然后他们可以提出问题。他们可能会询问资格标准、申请状态、文件要求等等。在第一次迭代中,您需要添加用户经常提出的最基本问题。一旦我们决定了问题和答案,编码它们就会变得容易。
例如,我在这个金融领域聊天机器人的基本版本中包含以下问题:
- 
请告知我获得住房贷款的资格标准 
- 
请告知我的贷款申请状态 
- 
告知我需要提交的文件清单 
每个问题的答案如下:
- 
我们需要至少 3 年的工作经验、3 年的 IT 税单以及至少 35 万卢比的收入 
- 
您的申请已提交至我们的信用风险管理团队 
- 
您需要提交过去 6 个月的工资条、身份证明、3 年的 IT 税单以及您房屋的租赁文件。 
我们还需要决定一些标准消息,这些内容将在下一节中介绍。
决定标准消息
我们需要决定标准消息,例如聊天机器人发出的欢迎信息。如果用户提出聊天机器人无法回答的问题,那么应该弹出什么信息?我们还需要决定用户结束聊天时的消息。
这些标准消息帮助用户了解他们可以向聊天机器人提出什么问题以及不能提出什么问题。现在让我们看看聊天机器人基本版本中的架构部分。
理解架构
在本节中,让我们谈谈架构。当我们构建一个特定领域的基于规则的聊天机器人时,我们需要存储用户提出的所有问题。我们还需要构建一个快速且可扩展的聊天机器人。在这种方法中,我们构建了 Web 服务。Web 服务的 REST API 将很容易与网站和前端集成。我们需要一个可以存储用户对话的数据库。这些对话数据将有助于我们改进聊天机器人或用于机器学习训练。我使用的库如下所示:
- 
使用 Flask 实现 Web 服务和 REST API 
- 
使用 MongoDB 来存储对话。选择 NoSQL 数据库的原因是对话没有特定的格式。NoSQL 是存储无模式数据的良好选择。我们需要存储原始对话,因此 NoSQL 是一个很好的选择。 
您可以参考以下图表,它将帮助您理解整个架构和流程:

图 8.7:聊天机器人基本版本的架构设计
根据这个架构,您会发现聊天机器人基本版本的处理流程相当简单。这个流程涉及七个简单的步骤:
- 
用户将向聊天机器人提问。 
- 
聊天机器人的基于规则的引擎将处理这个问题。在这里,已经调用了 REST API 来生成响应。 
- 
如果提出的问题对 RB 系统可用,那么用户将得到一个适当的答案。 
- 
如果提出的问题对 RB 系统不可用,那么用户将不会得到答案,而是收到一个标准错误信息。 
- 
用户的对话将被存储在 MongoDB 数据库中。这个响应是 JSON 格式的。 
- 
REST API 将发送相同的 JSON 响应到前端。在前端,JavaScript 解析这个响应并弹出适当的答案。 
- 
当用户得到他们的答案后,他们可能会结束聊天或提出另一个问题。 
我还想强调的一个主要观点是,在将数据存储到 MongoDB 之前,我们需要最终确定 JSON 响应的属性,这些属性实际上在用 JavaScript 解析 JSON 响应时对我们有帮助。您可以参考以下截图,这将帮助您了解我决定采用哪种类型的 JSON 模式:

图 8.8:理解 JSON 响应属性
每个 JSON 属性的使用方法如下:
- 
current_form_action:这个属性指示当前正在调用的 REST API。
- 
message_bot:这个字段携带来自机器人的答案。
- 
message_human:这个字段携带用户的查询。
- 
next_field_type:如果我们需要在下一个问题中填充文本框或按钮,这个属性对于生成动态 HTML 组件很有用。
- 
next_form_action:这个属性指示在即将到来的请求中我们应该调用哪个 REST API。
- 
placeholder_text:如果您想在文本框中放置水印文本,那么这个属性可以帮助您实现 HTML 功能。
- 
previous_field_type:这个属性跟踪最后一个字段类型。
- 
previous_form_action:这个属性跟踪我们最后调用的 REST API。
- 
suggestion_message:有时,我们需要一条消息来调用特定的规则。这就像您说“OK Google”并调用 Google Home 助手一样。这个属性基本上指导用户在提问时可以期待什么。
现在,让我们开始实现基于规则的聊天机器人。
实现基于规则的聊天机器人
在本节中,我们将了解聊天机器人的实现。这个实现分为两部分。您可以通过访问以下链接找到此代码:github.com/jalajthanaki/Chatbot_Rule_Based:
- 
实现对话流程 
- 
使用 flask 实现 RESTful API 
实现对话流程
为了实现对话逻辑,我们编写了一个单独的 Python 脚本,这样我们每次需要添加或删除一些逻辑时都会很容易。在这里,我们创建了一个 Python 包,我们将这个对话逻辑放入其中。文件名为 conversationengine.py,它使用 JSON、BSON 和 re 作为 Python 依赖项。
在这个文件中,我们将每个对话以函数的形式实现。当用户第一次打开聊天机器人时,应该会弹出一条欢迎信息。你可以在下面的屏幕截图中查看给出的代码:

图 8.9:欢迎信息的代码片段
现在用户需要输入 Hi 来开始对话。当用户输入 hi 时,start_coversation_action 函数将被调用,聊天机器人将询问一些信息,以便它能给出更准确、个性化的答案。首先,它会询问用户的姓名,然后询问他们的电子邮件 ID 和电话号码。你可以在下面的屏幕截图中查看:

图 8.10:询问基本用户信息的代码片段
同样,还有 borrowers_name_asking、borrowers_email_id_asking 和 mobilenumber_asking 函数,它们要求用户提供他们的姓名、电子邮件 ID 和电话号码。除此之外,还有一些问题可以帮助用户了解他们的贷款申请状态。如果客户是新的,他们可以提出诸如 为了申请住房贷款需要哪些文件? 之类的问题。你可以在 other_cases 函数中找到这些状态和文件相关的问题。你可以在下面的屏幕截图中查看该函数的代码:

图 8.11:与贷款申请状态和申请住房贷款所需文件相关的代码片段
如前图所示,我们在这里使用了一个正则表达式,以便聊天机器人能够回答状态和文件相关的问题。这是纯粹使用基于关键字的逻辑编写的。
现在,让我们看看如何使用 Flask 构建这个函数的 Web 服务。
使用 Flask 实现 RESTful API
到目前为止,我们只编写了接收用户输入查询并基于该查询调用相应函数的功能。为了更好的维护和易于集成,我们需要使用 Flask 实现 RESTful API。为了实现这一点,我们使用了 flask、json、os、uuid、datetime、pytz 和 flsk_pymongo 库。Flask 是一个易于使用的 Web 框架。你可以在下面的图中找到代码片段:

图 8.12:为聊天机器人创建 RESTful API 的代码片段
如前图所示,每个路由调用一个不同的方法,这些方法都是我们之前提到的conversationengine.py文件的一部分。为了运行这个 Flask 引擎,我们需要使用 Flask 的app.run()命令。你可以通过访问以下链接找到所有 API 及其功能:github.com/jalajthanaki/Chatbot_Rule_Based/blob/master/flaskengin.py。
现在我们来测试这个基于规则的聊天机器人。
测试基于规则的聊天机器人
在本节中,我们将测试聊天机器人的基本版本。让我们从聊天机器人从用户那里询问的基本个人信息开始。在这里,我将生成由 Flask RESTful API 生成的 JSON 响应。如果我们将这些 API 与前端集成,我们需要 JavaScript 来解析这个 JSON 响应。我不会在这里解释前端集成部分,所以让我们分析 JSON 响应。
对于欢迎信息,请参考以下截图:

图 8.13:欢迎信息的 JSON 响应
当聊天机器人询问用户姓名时的 JSON 响应如下所示:

图 8.14:询问用户姓名的 JSON 响应
如果用户询问其应用程序的状态,那么他们将得到以下图中给出的 JSON 响应:

图 8.15:获取状态相关信息的 JSON 响应
如果用户用印地语-英语(Hinglish)混合提问并使用查询中的单词状态,那么聊天机器人将生成响应。你可以在以下图中看到响应:

图 8.16:获取印地语-英语(Hinglish)语言相关信息的 JSON 响应
如果用户提出未编码的查询,它将生成以下 JSON 响应:

图 8.17:未知问题的 JSON 响应
经过测试,我们了解到已经编码的查询运行良好,但这个聊天机器人的基本版本对于未编码的问题处理不正确。我想在测试基于规则的聊天机器人后指出一些优点。然而,这种方法也有各种缺点,我们将在下一节中讨论。
基于规则的聊天机器人的优点
你可以参考以下基于规则的聊天机器人的优点:
- 
编码简单。 
- 
计算能力需求较低。 
- 
使用模式匹配方法,因此如果用户在对话中使用英语和其他语言,他们仍然会得到答案。这是因为聊天机器人识别出用户在问题中提供的特定关键词。假设用户用英语问:“你能提供我需要提交的文档列表吗?”另一个用户可能用印地语提问:“Kya aap mujhe bata sakte hain mujhe kaun se documents submit karne hain?”对于这个问题,聊天机器人将生成答案,因为它从用户查询中找到特定的关键词,如果这些关键词存在,那么聊天机器人将生成答案,而不考虑语言。 
现在,让我们看看与这种方法相关的问题,我们需要解决这些问题来改进聊天机器人。
现有方法的问题
在本节中,我们将讨论我们聊天机器人的基本版本存在的问题。正如我们所知,对于未见过的查询,这种方法不起作用,这意味着基本方法无法正确地泛化用户的问题。
我在这里列出了一些问题:
- 
耗时,因为我们需要为每个场景硬编码,这根本不可行 
- 
它无法处理未见过的用例 
- 
用户应该处理对话的严格流程 
- 
它无法理解长上下文 
大多数这些问题可以通过基于生成的方法来解决。让我们看看将帮助我们改进这种方法的关键概念。
理解优化方法的关键概念
在本节中,我们将讨论一些关键概念,这些概念可以帮助我们改进聊天机器人的基本版本。我们之前列出的问题可以通过使用深度学习(DL)技术来解决,这些技术可以帮助我们在更短的时间内构建一个更通用的聊天机器人。
在继续前进之前,我们需要决定我们将使用哪种深度学习技术来改进我们的方法。深度学习帮助我们取得很好的成果。在这里,我们需要使用端到端深度学习方法,它不对数据、对话结构和用例结构做出任何假设。这正是我们想要的。为了实现这一点,我们将使用循环神经网络(RNN)。现在你可能想知道为什么 RNN 是有用的。让我通过一个例子来解释这一点。假设我们想要将温度分类为热或冷;为了做到这一点,我们将使用前馈神经网络将温度分类为热或冷,但对话并不是固定大小的。对话是一系列单词。我们需要使用一个能够帮助我们处理单词序列的神经网络。RNN 最适合处理这类序列。在 RNN 中,我们在训练过程中将数据反馈到输入中,形成一个循环。
在改进的方法中,我们将使用 TensorFlow 中的序列到序列(seq2seq)模型。因此,让我们先讨论一下序列模型。
理解 seq2seq 模型
使用 seq2seq 模型的好处是我们不需要执行特征工程。像大多数深度学习技术一样,它通过自身生成特征。我们将简要讨论 seq2seq 模型。seq2seq 模型由两个长短期记忆(LSTM)循环神经网络组成。第一个神经网络是一个编码器。它处理输入。第二个神经网络是一个解码器。它生成输出。通常,深度学习算法需要输入和输出的维度是固定大小的,但在这里,我们接受一个句子中的单词序列作为输入,并输出一个新的单词序列。因此,我们需要一个能够学习具有长距离记忆依赖性的数据序列模型。LSTM 架构最适合这种情况。编码器 LSTM 将可变长度的输入句子转换为一个固定维度的向量表示。我们可以将其视为一个思维向量或上下文向量。我们使用 LSTM 的原因是它可以记住序列中远处的单词;在这里,我们处理 seq2seq 模型的大序列注意力机制,这有助于解码器选择性地查看与提高准确性最相关的序列部分。
您可以参考以下图中 seq2seq 模型的架构:

图 8.18:seq2seq 模型的架构
图片来源:suriyadeepan.github.io/img/seq2seq/seq2seq2.png
当我们提供一个足够大的问题和回答的数据集时,它将识别问题集的相似性,并将它们表示为一个单独的思维向量。这种表示有助于机器理解问题的意图,而不管句子的结构如何,因此机器可以识别“现在几点了?”和“现在几点?”这样的问题具有相同的意图,因此它们将落入一个单独的思维向量中。训练后,我们将拥有一个巨大的集合,不仅包括突触权重,还有思维向量。之后,在训练模型时,我们需要使用额外的超参数以及适当的损失函数。一旦训练了模型,我们就可以与它聊天。
如果您想了解更多关于 seq2seq 模型的信息,那么您应该参考由谷歌研究人员发表的标题为一个神经对话模型的研究论文。您也可以参考这篇论文:arxiv.org/pdf/1506.05869v3.pdf以及这篇关于 LSTM 的精彩文章:colah.github.io/posts/2015-08-Understanding-LSTMs/。
现在,让我们使用 seq2seq 模型来实现聊天机器人。
实现改进的方法
在本节中,我们将介绍实现的每个部分。您可以通过使用此 GitHub 链接找到代码:github.com/jalajthanaki/Chatbot_tensorflow。请注意,在这里,我使用 TensorFlow 版本 0.12.1。我在 GeForce GTX 1060 6GB GPU 上训练了几小时。在这个实现中,我们不需要生成特征,因为 seq2seq 模型会为句子中给出的单词序列生成其内部表示。我们的实现部分有以下步骤:
- 
数据准备 
- 
实现 seq2seq 模型 
让我们开始编码。
数据准备
在这个实现过程中,我们将使用康奈尔电影对白数据集。首先,我们需要以我们可以用于训练的格式准备数据。有一个 Python 脚本用于执行数据准备。您可以在以下位置找到脚本:github.com/jalajthanaki/Chatbot_tensorflow/blob/master/data/prepare_data_script/data.py。
数据准备可以细分为以下步骤:
- 
生成问答对 
- 
预处理数据集 
- 
将数据集分割为训练数据集和测试数据集 
- 
为训练和测试数据集构建词汇表 
生成问答对
为了从康奈尔电影对白数据集中生成问答对,我们使用 movie_lines.txt 和 movie_conversations.txt 文件。movie_lines.txt 文件提供了关于每个对话的 line_id 以及实际对话的信息,而 movie_conversations.txt 只包含 line_ids。在这种情况下,我们需要从数据集中生成适当的问答对。为此,我们将这两个文件结合起来。在 Python 脚本中,有一些函数帮助我们合并这些文件。有关函数的详细信息如下:
- 
get_id2line():这个函数帮助我们使用 +++$+++ 模式来分割数据。我们在movie_lines.txt文件上执行分割。分割后,借助这个函数,我们创建了一个字典,我们将 line_id 作为键,将电影对白作为值。所以,键 = line_id 和 值 = 文本
- 
get_conversations():这个函数分割movie_conversations.txt文件中给出的数据。这将帮助我们创建一个列表。这个列表包含 line_ids 的列表。
- 
gather_dataset():这个函数实际上生成问答对。在这个函数中,应用了一个简单的逻辑。我们取 line_ids 的列表,我们知道最后一个元素表示答案。因此,我们分离问题和答案。借助get_id2line()函数,我们搜索问题和相应的答案。在这里,我们使用键的值来搜索问题和答案。
您可以通过以下截图查看实际的编码:

图 8.19:用于生成问答对的函数
现在,让我们探索数据预处理部分。
预处理数据集
这里涉及一些预处理和过滤步骤。作为预处理的一部分,我们执行以下步骤:
- 
我们使用内置的字符串函数 lower()将对话转换为小写。
- 
我们还移除了垃圾字符以及过短或过长的对话。为此,我们使用基于列表的方法移除垃圾字符,并使用 filter_data()函数移除过短或过长的对话。当我们对数据集应用filter_data()函数时,28%的数据集被过滤。
- 
我们还过滤掉了包含大量未知词汇的对话。在这里,2%的数据集受到了影响。为此,我们使用了 filter_unk()方法。
- 
我们还将对句子进行分词。在这个过程中,我们将文本行列表转换为单词行列表。这种分词对于训练过程很有帮助,因为机器可以处理句子中的单个单词,借助单词 ID,数据检索变得更快。 
您可以参考以下屏幕截图中给出的代码:

图 8.20:预处理代码片段
将数据集分为训练数据集和测试数据集
预处理完成后,我们将数据集分为训练数据集和测试数据集,为此,我们将使用以下函数:
- 
我们可以使用 prepare_seq2seq_files()函数保存训练和测试数据集。
- 
您可以直接从以下 GitHub 链接访问 train.enc、train.dec、test.enc和test.dec数据文件:github.com/jalajthanaki/Chatbot_tensorflow/tree/master/data
为训练和测试数据集构建词汇表
现在是时候从数据集中生成词汇表了。对于词汇表的生成,我们将执行以下步骤:
- 
使用 data_utils.py文件中的prepare_custom_data()函数,我们可以在训练时生成要输入到 seq2seq 模型中的词汇表。
- 
您可以使用此链接访问 data_uti``ls.py文件:github.com/jalajthanaki/Chatbot_tensorflow/blob/master/data_utils.py
- 
注意,当开始训练时,会生成词汇表文件。 
- 
词汇表文件的文件名是 train.enc.ids20000、train.dec.ids20000、test.enc.ids20000和test.dec.id``s20000。这里的 20000 表示我们提供的词汇表大小。
- 
您可以通过此链接访问该文件: github.com/jalajthanaki/Chatbot_tensorflow/tree/master/data
您可以在以下屏幕截图中看到prepare_custom_data()函数的代码:

图 8.21:生成词汇的代码片段
现在我们实际上将使用 TensorFlow 实现 seq2seq 模型。
实现 seq2seq 模型
在本节中,我们将使用 seq2seq 模型进行实际训练。我们将使用 TensorFlow 来实现 seq2seq 模型。在开始训练之前,让我们看看超参数配置文件,您可以通过以下 GitHub 链接访问:github.com/jalajthanaki/Chatbot_tensorflow/blob/master/seq2seq.ini。
在聊天机器人开发过程中:seq2seq 模型,构建"训练,我们的脚本使用这些文件及其参数。以下参数位于此配置文件中:
- 
Mode: 这可以是 train 或 test
- 
train_enc:这包含了解码器训练数据集的路径。
- 
train_dec:这包含了解码器训练数据集的路径。
- 
test_enc:这包含了解码器测试数据集的路径。
- 
test_dec:这包含了解码器测试数据集的路径。
- 
Working_directory:这是我们可以存储我们的检查点、词汇和临时数据文件的文件夹
- 
enc_vocab_size:这个数字定义了解码器的词汇量大小。我们将其设置为 20,000。
- 
dec_vocab_size:这个数字定义了解码器的词汇量大小。我们将其设置为 20,000。
- 
num_layers:这表示 LSTM 层的数量。在这里,我们将其设置为 3。
- 
layer_size:这表示 seq2seq 模型中的层数。我们将其设置为 256。
- 
steps_per_checkpoint:在检查点,模型的参数被保存,模型被评估,并打印结果。
- 
learning_rate:这表示我们训练模型的速度有多快或多慢。我们目前将其值设置为 0.5。
大多数前面的参数都可以更改以获得最佳结果。在训练期间,我们需要将模式设置为 train 并运行以下命令:
$ python execute.py
现在是时候了解 execute.py 文件内部的内容了。您可以通过以下 GitHub 链接访问此文件:github.com/jalajthanaki/Chatbot_tensorflow/blob/master/execute.py。
在这个脚本中,我们调用 TensorFlow API。这个脚本可以分为以下几部分:
- 
创建模型 
- 
训练模型 
创建模型
我们在这里使用 TensorFlow 的Seq2SeqModel()函数。这个函数读取配置文件并使用配置文件中定义的值。为了存储训练模型,我们使用saver.restore()函数,为了获取检查点的状态,我们使用get_checkpoint_state()函数。您可以参考以下图中的代码片段:

图 8.22:创建 seq2seq 模型的代码片段
训练模型
我们在execute.py文件中定义了train()方法。此函数初始化 TensorFlow 会话并开始训练。您可以参考以下截图中的代码片段:

图 8.23:训练模型的代码片段
现在是训练模型的时候了。当我们执行python execute.py命令时,您将看到以下截图中的输出:

图 8.24:使用 TensorFlow 训练 seq2seq 模型
在这里,训练是在 GPU 上进行的。我训练了这个模型 3 个小时。我已经训练了这个模型 15,000 个检查点。您可以参考以下截图:

图 8.25:seq2seq 训练的输出
在 CPU 上,训练将花费很多时间,因此我还上传了预训练模型供您使用。您可以通过以下 GitHub 链接下载它们:github.com/jalajthanaki/Chatbot_tensorflow/tree/master/working_dir。
现在是了解帮助我们评估训练模型的测试指标的时候了。
测试改进的方法
在本节中,我们将对改进的方法进行测试。在执行实际测试并查看聊天机器人对话的好坏之前,我们需要了解我们将为此方法和最佳方法使用的测试指标。这些测试指标帮助我们评估模型精度。让我们首先了解测试指标,然后我们将继续对改进的方法进行测试。
理解测试指标
在本节中,我们需要了解以下测试指标:
- 
混淆度 
- 
损失 
混淆度
在 NLP 领域,混淆度也被称为每词混淆度。混淆度是衡量训练模型预测未见数据输出好坏的度量。它也用于比较概率模型。低混淆度表明概率分布擅长预测样本。即使在训练过程中,您也可以看到对于每个检查点,混淆度都在下降。理想情况下,当混淆度没有变化时,我们需要停止训练。在 seq2seq 模型的训练过程中,我在 3 小时后停止了训练,因此当您从您的端点训练模型时,您可以等待混淆度不再进一步下降。
混淆度是使用熵的概念。如果您想了解混淆度,可以参考www.youtube.com/watch?v=BAN3NB_SNHY。每词混淆度基于熵。因此,为了理解熵,您可以参考以下链接:
一旦你理解了熵,理解困惑度方程就会变得容易。参考以下图中给出的方程:

图 8.26:困惑度方程
图片来源:https://www.tensorflow.org/tutorials/recurrent
在这里,N 是样本数量,P 是一个概率函数。我们使用自然对数函数来计算熵。现在让我们看看另一个测试指标。
损失
训练损失表示训练进展的方向。通常,当我们开始训练时,损失值较高,训练准确率较低,但在训练过程中,损失值会下降,训练准确率会上升。在深度学习算法中使用了多种错误函数。在这里,我们使用交叉熵作为损失函数。交叉熵和对数损失在上下文中略有不同,但在机器学习中,当计算 0 到 1 之间的错误率时,它们是同一回事。你可以参考以下图中给出的方程:

图 8.27:交叉熵方程
图片来源:http://ml-cheatsheet.readthedocs.io/en/latest/loss_functions.html#cross-entropy
如果你想要探索交叉熵损失函数,可以参考:neuralnetworksanddeeplearning.com/chap3.html。
我们在这里没有深入数学细节,因为仅仅通过跟踪训练过程,你就可以知道损失值是增加还是减少。如果在训练时间内损失值在下降,那么训练就是在正确的方向上。这也适用于困惑度。最初,困惑度值很大,但在训练过程中它会逐渐下降,在某个时刻既不增加也不减少。那时,我们需要停止训练。现在让我们测试修改后的聊天机器人。
测试聊天机器人的修改版本
在本节中,我们将对修改后的聊天机器人进行测试。我仅在 GPU 上训练了 3 小时;现在让我们看看我们的聊天机器人能告诉我们多少。为了测试,我们需要在seq2seq.ini配置文件中进行一些小的更改。我们需要将模式值设置为测试,然后执行python execute.py命令。
执行给定的命令后,你将得到以下图中给出的输出:

图 8.28:修改后方法的输出
如果你训练的时间更长,那么你将得到一个更令人印象深刻的结果。我觉得深度学习算法可以帮助我们,如果我们想使用基于生成的方法来构建聊天机器人。现在让我们讨论如何改进这个修订方法。
修订方法的问题
在本节中,我们将讨论修订方法存在的问题。我们是否有任何方法可以优化修订方法?所以首先,让我们讨论改进的领域,以便在即将到来的方法中,我们可以专注于那个特定的点。
我想强调的一些要点如下:
- 
在我们聊天机器人的上一个版本中,缺乏推理能力,这意味着聊天机器人不能通过应用基本推理来回答问题。这正是我们需要改进的地方。 
- 
让我给你举一个例子。假设我告诉聊天机器人一个故事:“约翰在厨房。丹尼尔在浴室。”之后,我说,我向聊天机器人提出这个问题:“约翰在哪里?”我们迄今为止构建的聊天机器人将无法回答这个简单的问题。我们作为人类,很好地回答这类问题。 
- 
我们试图在我们的下一个方法中实现这种功能,以便我们可以在聊天机器人中启用一些人工智能功能。 
让我们看看可以帮助我们构建人工智能聊天机器人的重要概念。
理解关键概念以解决现有问题
Facebook 人工智能研究小组发表了一篇论文,提出了一个可以证明机器也可以回答基于推理的问题的记忆网络。你当然可以参考这篇题为《迈向 AI 完整问答:一系列先决条件玩具任务》的论文。链接。你还可以参考这篇关于记忆网络的论文:链接。
在这里,我们将使用 bAbI 数据集,并基于改进的记忆网络来训练模型。一旦训练完成,我们将检查我们的聊天机器人是否能够根据使用简单的推理能力来回答问题。我们将重新创建 Facebook 研究论文的结果。在我们进入实现部分之前,我们需要了解记忆网络是什么,以及我们如何构建一个将使用逻辑来回答问题的系统。所以,让我们简要地看看记忆网络。
记忆网络
在本节中,我们将探讨记忆网络,以便我们能够理解在我们即将在下一节中实现它时幕后实际发生的事情。
在 LSTM 网络中,记忆是通过使用隐藏状态和权重进行编码的。这些隐藏状态和权重对于极长的数据序列来说太小了,无论是书籍还是电影。因此,在语言翻译应用中,为了选择适合上下文的翻译,会使用多个 LSTM 状态,并结合注意力机制。Facebook 研究人员开发了一种名为记忆网络的新策略,在问答系统中优于 LSTM。
记忆网络背后的基本思想是允许神经网络使用外部数据结构作为记忆存储,并且以监督方式学习从这种外部记忆结构中检索所需记忆的位置。您可以参考以下图示中给出的记忆网络架构:

图 8.29:记忆网络架构
当涉及到回答由少量数据生成的问题时,使用记忆网络处理信息相当容易,但在现实世界中,处理具有长期依赖关系的数据是一个具有挑战性的任务。在 Kaggle 上,有一个名为 The Allen AI Science Challenge 的竞赛,获胜者使用了一种特殊的记忆网络变体,称为动态记忆网络(DMN),这是我们用来构建聊天机器人的。
动态记忆网络(DMN)
DMN 的架构在以下图示中给出:

图 8.30:DMN 架构
图像来源:https://yerevann.github.io/public/2016-02-06/dmn-high-level.png
DMN 的架构定义了两种类型的记忆,如下所示:
- 
语义记忆:我们使用预训练的 Glove 模型,该模型将为输入数据生成向量。这些向量是 DMN 模型的输入,并用作语义记忆。 
- 
事件记忆:这种记忆包含其他知识。这种记忆的灵感来源于我们大脑的海马体功能。它能够检索由响应(如图像或声音)触发的时态状态。我们将在稍后看到这种事件记忆的用法。 
这些是我们需要理解的一些重要模块:
- 
输入模块 
- 
问题模块 
- 
事件记忆模块 
在我开始解释模块之前,请参考以下图示以更好地理解:

图 8.31:每个 DMN 模块的详细信息
图像来源:https://yerevann.github.io/public/2016-02-06/dmn-details.png
输入模块
输入模块是一个在一系列词向量上运行的 GRU(门控循环单元)。GRU 单元有点像 LSTM 单元,但它更计算高效,因为它只有两个门,并且不使用记忆单元。这两个门控制内容何时更新和何时删除。GRU 只执行两个任务:一个是 更新,另一个是 重置。你可以参考以下展示 LSTM 和 GRU 的图:

图 8.32:LSTM 和 GRU 的示意图
图片来源:https://cdn-images-1.medium.com/max/1200/0*1udenjz1XCZ5cHU4
输入模块的隐藏状态以向量的形式表示了到目前为止的输入过程。它在每个句子后输出隐藏状态,这些输出在论文中被称为事实,因为它们代表了所提供内容的本质。你可能想知道 GRU 中隐藏状态是如何计算的。为此,你可以参考以下方程:
Ht = GRU(Xt, ht-1)
在这里,Ht 是当前时间步,ht-1 是前一个时间步,Xt 是给定的词向量。前面的方程是 GRU 隐藏状态计算的简单格式。你可以在下面的图中看到更详细和复杂的方程:

图 8.33:GRU 中计算隐藏状态的方程
图片来源:https://yerevann.github.io/public/2016-02-06/gru.png
在这个方程中,借助给定的词向量和前一个时间步向量,我们计算当前时间步向量。更新给我们一个单层神经网络。我们将矩阵乘法求和并添加偏置项。然后,sigmoid 函数将其压缩到 0 到 1 之间的值列表,这就是我们的输出向量。我们用不同的权重集重复这个过程两次,然后使用重置门,它会学习在必要时忽略过去的时步。例如,如果下一句话与前面的句子无关,更新门也是类似的,它可以学习完全忽略当前的时间步。也许当前句子与答案无关,而前面的句子有关。
问题模块
此模块逐词处理问题,并使用与输入模块相同的 GRU 和相同的权重输出一个向量。我们需要为输入语句(输入数据)和我们将提出的问题进行编码。我们可以通过为它们实现嵌入层来实现这一点。现在我们需要为两者创建一个短时记忆表示。
短时记忆
如我之前所述,情景记忆的概念源于我们大脑的海马体功能。从输入中提取的事实和问题向量进入情景记忆模块。它由两个嵌套的 GRU 组成。内层 GRU 生成所谓的情景。它通过遍历输入模块中的事实来完成这一任务。在更新其内部状态时,它考虑了当前事实上的注意力函数的输出。注意力函数给每个事实分配一个介于 0 到 1 之间的分数,因此 GRU 忽略了得分低的事实。在训练过程中,在遍历所有可用事实的完整遍历之后,内层 GRU 输出一个情景,然后将其输入到外层 GRU。我们需要多个情景,以便我们的模型能够学习它应该关注句子中的哪个部分。在第二次遍历中,GRU 意识到句子中还有其他重要因素。通过多次遍历,我们可以收集越来越相关的信息。
这是对 DMN 的简要说明。您还可以参考这篇优秀的文章:yerevann.github.io/2016/02/05/implementing-dynamic-memory-networks/。
现在,让我们看看这个实现的细节。
最佳方法
我们已经涵盖了所有有助于我们实现基于 DMN 的聊天机器人的概念。为了实现这种方法,我们将使用 Keras 和 TensorFlow 后端。我们不浪费时间,将直接跳到实现部分。您可以通过以下 GitHub 链接查看此方法的代码:github.com/jalajthanaki/Chatbot_based_on_bAbI_dataset_using_Keras。
实施最佳方法
在这里,我们将使用给定的 bAbI 任务 1 数据集来训练我们的模型。首先,我们需要解析故事并构建词汇。您可以参考以下图中的代码:

图 8.34:解析故事和构建词汇的代码片段
我们可以初始化我们的模型,并将其损失函数设置为 Keras 中使用 RMSprop 实现的分类交叉熵。您可以在以下屏幕截图中参考:

图 8.35:构建模型的代码片段
在训练之前,我们需要设置一个超参数。借助超参数脚本中的值,我们将决定是否以训练模式或测试模式运行脚本。您可以在以下图中看到我们训练过程中需要设置的所有超参数:

图 8.36:训练的超参数值
在这里,我们使用了三个超参数。我们可以对它们进行实验。让我们讨论一下:
- 
train_epochs:此参数表示训练示例在神经网络中完成前向传递和反向传递的次数。一个 epoch 意味着一个训练示例的前向传递和一个反向传递。在这里,我们将 train_epochs 设置为 100 次。你可以增加它,但这样训练时间也会增加。 
- 
batch_size:此参数表示在一次前向传递和反向传递中训练示例的数量。较高的批量大小需要更多的内存,所以我们把这个值设置为 32。如果你有更多的内存可用,你可以增加批量大小。请参考以下信息框中给出的简单示例。 
- 
lstm_size:此参数表示我们神经网络中存在的 LSTM 单元的数量。你可以增加或减少 LSTM 单元的数量。在我们的案例中,少于 64 个 LSTM 单元将不会给我们好的输出,所以我将 lstm_size 设置为 64。 
注意
如果你有一千个训练示例,并且你的批量大小是 500,那么完成一个 epoch 需要 2 次迭代。
我已经在 GPU 上训练了这个模型。如果你没有使用 GPU,那么它可能需要很长时间。你可以通过执行以下命令开始训练:python main.py。训练的输出在以下图中给出:

图 8.37:训练输出的代码片段
一旦我们训练了模型,我们就可以加载并测试它。有两种测试模式可用:
- 
随机测试模式 
- 
用户交互测试模式 
随机测试模式
在这种模式下,脚本本身将加载一个随机故事并给出其答案。你可以在以下图中看到超参数的值:

图 8.38,随机测试模式的超参数值。
对于测试,执行python main.py命令,你可以看到测试结果。这些结果在以下图中已经展示:

图 8.39:随机测试模式的结果
用户交互测试模式
在这种模式下,如果测试用户可以给出自己的故事并提问,聊天机器人将生成对该问题的答案。你只需要记住,在每一个词之前,你需要提供空格。你可以在以下图中参考用户交互测试模式超参数的值:

图 8.40:用户交互测试模式超参数的值
对于测试,执行python main.py命令,你可以看到测试结果。这些结果在以下图中已经展示:

图 8.41:用户交互测试模式的结果
如果你想要测试所有其他任务,那么你可以使用这个网络应用:ethancaballero.pythonanywhere.com/。
这种方法可以使我们的准确率达到 92%到 95%。这种方法帮助我们构建了具有 AI 功能的聊天机器人。
讨论混合方法
在实际场景中,为了构建聊天机器人,我们还可以结合这里描述的一些技术。根据业务需求,我们可以使用混合方法。
让我们举一个例子。假设你正在为金融领域构建聊天机器人。如果用户询问其账户中的可用余额,我们只需要一个基于规则的系统,它可以查询数据库并为该用户生成账户余额详情。如果用户询问如何将资金从一个账户转账到另一个账户,聊天机器人可以通过生成逐步信息来帮助用户了解如何转账。在这里,我们将使用基于深度学习的生成方法。我们应该有一个系统,它包括一个基于规则的引擎以及一个用于生成最佳可能输出的深度学习算法。在这个系统中,用户的问题首先发送到基于规则的系统。如果该问题的答案可以通过基于规则的系统生成,那么答案将被传递给最终用户。如果答案不能由基于规则的系统生成,那么问题将进一步传递到深度学习算法,并生成答案。最后,最终用户将看到他问题的回答。
摘要
在本章中,我们引用了不同的数据集来构建聊天机器人。你学习了在没有数据集的情况下可以使用的基于规则的途径。你还了解了开放和封闭领域。之后,我们使用了基于检索的方法来构建聊天机器人的基本版本。在改进的方法中,我们使用了 TensorFlow。这种改进的方法对我们来说非常好,因为它与基本方法相比节省了时间。我们在 Cornell Movie-Dialogs 数据集上实现了谷歌的神经对话模型论文。对于最佳方法,我们构建了一个使用 Facebook bAbI 数据集的模型,并建立了基本的推理功能,这有助于我们为聊天机器人生成良好的结果。尽管改进和最佳方法的训练时间真的很长,但那些想在云平台上训练模型的人可以选择这样做。到目前为止,我喜欢亚马逊网络服务(AWS)和谷歌云平台。我还将一个预训练模型上传到了我的 GitHub 仓库,以便你可以重现结果。如果你是一个初学者,并且想制作一个非常好的聊天机器人,那么谷歌的 API.AI 是一个好的聊天机器人开发平台。它现在被称为 Dialogflow,可在以下网址找到:dialogflow.com/。你还可以参考 IBM Watson API:www.ibm.com/watson/how-to-build-a-chatbot/。这些 API 可以帮助你在构建聊天机器人方面取得很大进展;而且它需要的编码知识也更少。
在下一章中,我们将构建一个基于计算机视觉的应用程序,该程序将帮助我们识别图像和视频中存在的命名对象。这个应用程序将实时检测对象。对象检测应用程序用于构建自动驾驶汽车、机器人等,所以请继续阅读!
第九章:构建实时目标识别应用
在本章中,我们将构建一个能够检测对象的应用。这个应用将帮助我们识别图像或视频流中存在的对象。我们将使用实时输入,例如来自网络摄像头的实时视频流,我们的实时目标检测应用将检测视频流中的对象。我们将使用实时视频流,这也是这种目标检测被称为实时目标检测的主要原因。在本章中,我们将使用迁移学习方法构建实时目标检测。我将在本章的讲解过程中详细解释迁移学习。
在本章中,我们将涵盖以下主题:
- 
介绍问题陈述 
- 
理解数据集 
- 
迁移学习 
- 
设置编码环境 
- 
基线模型的特征工程 
- 
选择机器学习(ML)算法 
- 
构建基线模型 
- 
理解测试指标 
- 
测试基线模型 
- 
现有方法的问题 
- 
如何优化现有方法 - 理解优化过程
 
- 
实施改进方法 - 
测试改进方法 
- 
理解改进方法的问题 
 
- 
- 
最佳方法 - 实施最佳方法
 
- 
概述 
介绍问题陈述
在本章中,我们将构建一个目标检测应用。我们不仅会检测对象,还会构建一个实时检测对象的应用。这种应用可以用于自动驾驶汽车、农业领域的分离任务,甚至可以在机器人领域使用。让我们了解我们的目标和实际构建的内容。
我们希望构建一个应用,其中我们将提供实时网络摄像头视频流或实时视频流作为输入。我们的应用将使用预训练的机器学习模型,这将帮助我们预测视频中出现的对象。这意味着,如果视频中有一个人物,那么我们的应用可以将其识别为人物。如果视频包含椅子、杯子或手机,那么我们的应用应该以正确的方式识别所有这些对象。因此,本章的主要目标是构建一个能够检测图像和视频中的对象的应用。在本章中,你还将学习迁移学习的概念。我们所有的方法都是基于深度学习技术的。
在下一节中,我们将讨论数据集。
理解数据集
在本节中,我们将介绍用于训练深度学习模型的那个数据集。当我们试图构建目标检测应用时,有两个数据集被广泛使用,这些数据集如下:
- 
COCO 数据集 
- 
PASCAL VOC 数据集 
我们将逐一查看每个数据集。
COCO 数据集
COCO 代表上下文中的常见对象。因此,这个数据集的简称是 COCO 数据集。许多科技巨头,如谷歌、Facebook、微软等,都在使用 COCO 数据来构建对象检测、对象分割等令人惊叹的应用程序。你可以在这个官方网页上找到有关此数据集的详细信息:
COCO 数据集是一个大规模的对象检测、分割和标题数据集。在这个数据集中,总共有 330,000 张图像,其中超过 200,000 张被标记。这些图像包含 1.5 百万个对象实例,分为 80 个对象类别。所有标记的图像都有五个不同的标题;因此,我们的机器学习方法能够有效地泛化对象检测和分割。
通过使用 COCO 探索器,我们可以探索 COCO 数据集。你可以使用cocodataset.org/#explore URL 来探索数据集。COCO 探索器是一个出色的用户界面。你只需选择对象标签,例如我想看到图片中有人、自行车和公共汽车的图像,探索器就会提供包含人、自行车和公共汽车的图像。你可以参考以下图示:

图 9.1:COCO 数据集探索片段
在每张图像中,都为每个主要对象提供了适当的对象边界。这就是为什么如果你想要从头开始构建自己的计算机视觉应用,这个数据集非常棒的主要原因。
在这里,我们不是下载数据集,因为如果我们需要在这个数据集上从头开始训练模型,那么这将需要大量的时间和大量的 GPU 才能获得良好的准确率。因此,我们将使用预训练模型,并通过迁移学习实现实时对象检测。现在让我们继续讨论 PASCAL VOC 数据集。
PASCAL VOC 数据集
PASCAL 代表模式分析、统计建模和计算学习,VOC 代表视觉对象类别。在这个数据集中,图像被标记为 20 个类别用于对象检测。动作类别和人物布局标签也是可用的。在人物布局标签中,边界框是关于人体每个部分的标签(头部、手、脚)。你可以在这个网页上找到有关此数据集的详细信息:host.robots.ox.ac.uk/pascal/VOC/voc2012/index.html。
PASCAL VOC 类别
图像已被分为四个主要类别:
- 
人类 
- 
动物 
- 
车辆 
- 
室内 
每张图像都标记了前面的主要类别,并且还为图像中的对象提供了特定的标签。前四个类别都有特定的标签,以下列表中我将描述它们:
- 
人类:人 
- 
动物:鸟、猫、牛、狗、马、羊 
- 
车辆:飞机、自行车、船、公共汽车、汽车、摩托车、火车 
- 
室内:瓶子、椅子、餐桌、盆栽植物、沙发、电视/显示器 
您可以参考以下图示,它将帮助您理解这个 PASCAL VOC 数据集中的标签:

图 9.2:PASCAL VOC 标记图像示例
如您在前面的图示中看到的,已经标记了两个主要类别:人和动物。对于图像中出现的特定对象,即人和羊,已经给出了特定的标记。我们不会下载这个数据集;我们将使用使用此数据集训练的预训练模型。
到现在为止,您已经多次听说过迁移学习和预训练模型这两个术语。让我们了解它们是什么。
迁移学习
在本节中,我们将探讨迁移学习是什么,以及它在我们构建实时目标检测时将如何对我们有用。我们将本节分为以下部分:
- 
什么是迁移学习? 
- 
什么是预训练模型? 
- 
我们为什么要使用预训练模型? 
- 
我们如何使用预训练模型? 
让我们从第一个问题开始。
什么是迁移学习?
我们首先将探讨迁移学习的直觉,然后我们将介绍其技术定义。让我通过一个简单的师生类比来解释这个概念。一位教师拥有多年教授某些特定主题或学科的经验。无论教师拥有什么信息,他们都会传授给学生。因此,教学的过程就是将知识从教师转移到学生的过程。您可以参考以下图示:

图 9.3:迁移学习概述
现在,请记住这个类比;我们将将其应用于神经网络。当我们训练一个神经网络时,它会从给定的数据集中获得知识。这个经过训练的神经网络有一些权重,帮助它从给定的数据集中学习;训练后,我们可以将这些权重以二进制格式存储。我们以二进制格式存储的权重可以被提取出来,然后转移到任何其他神经网络。因此,我们不是从头开始训练神经网络,而是转移先前训练的模型获得的知识。我们将学习到的特征转移到新的神经网络中,这将节省我们大量的时间。如果我们已经为特定应用训练了一个模型,那么我们将将其应用于新的但类似类型的应用,这将反过来帮助我们节省时间。
现在,是时候用更技术性的术语来定义迁移学习了。迁移学习是机器学习中的一个研究问题,它关注于在解决特定问题时获得的知识存储,并将其应用于不同但相关的问题。有时,迁移学习也被称为归纳迁移。让我们用一个具体的例子来巩固你的理解。如果我们构建一个在识别汽车时获得知识的机器学习模型,它也可以在我们尝试识别卡车时应用。在本章中,我们使用迁移学习来构建这个实时物体检测应用程序。
什么是预训练模型?
我想给你一个简单的解释。预训练模型是由其他人创建和构建来解决特定问题的。这意味着我们正在使用一个已经训练好的模型,并对其进行插入和播放。通过使用预训练模型,我们可以构建具有相似领域的新的应用程序。
让我给你举一个例子:假设我们想要创建一辆自动驾驶汽车。为了构建它,我们的第一步将是构建一个不错的物体识别系统。你可以花费一年或更长时间从头开始构建一个不错的图像和物体识别算法,或者你可以使用预训练模型,例如使用 PASCAL VOC 数据集构建的 Google Inception 模型或 YOLO 模型。
使用预训练模型有一些优势,如下所示:
- 
预训练模型可能不会给你 100%的准确率,但它可以节省大量的努力。 
- 
你可以优化你正在工作的真实问题的准确度,而不是从头开始制作一个算法;正如我们有时所说的,没有必要重新发明轮子。 
- 
现在有众多库可以帮助我们保存训练好的模型,这样我们就可以在需要时轻松加载和使用它们。 
除了预训练模型为我们提供的优势外,我们还需要了解其他真实的原因,为什么我们应该使用预训练模型。那么,让我们在下一节中讨论这个问题。
为什么我们应该使用预训练模型?
当我们专注于以不同方式开发现有算法时,我们的目标将是构建一个能够超越所有其他现有算法并使其更高效的算法。如果我们只关注研究部分,那么这可以是一个开发高效且精确算法的好方法,但如果你的目标是开发一个应用程序,而这个算法是整个应用程序的一部分,那么你应该关注你如何快速且高效地构建应用程序。让我们通过一个例子来理解这一点。
在这一章中,我们想要构建实时目标检测技术。现在,我的主要焦点将是构建一个可以实时检测对象的应用程序。不仅如此;我还需要结合目标检测和实时跟踪活动。如果我忽略我的主要目标,开始制作新的但有效的目标检测算法,那么我会失去专注。我的焦点将是构建整个实时目标检测应用程序,而不仅仅是某个特定的算法。
有时候,如果我们失去了专注,试图从头开始构建算法,那么这将会花费很多时间。我们也会浪费我们的努力,因为世界上可能已经有聪明的人为我们构建了它。当我们使用这个已经开发的算法时,我们将节省时间并专注于我们的应用。在构建我们实际应用的原型之后,我们将有时间对其进行改进,以便它可以被许多人使用。
让我告诉你我的故事。几个月前,我开始构建一个可以用于安全领域的图像检测应用。当时,我不想使用预训练模型,因为我想要探索从头开始构建算法需要多少努力。所以,我开始自己动手做。我尝试了多种不同的算法,例如 SVM、多层感知器(MLP)和卷积神经网络(CNN)模型,但准确率真的很低。我失去了构建一个可以用于安全领域的图像检测算法应用的专注,只是开始专注于使算法更好。过了一段时间,我意识到,如果使用一个带有优化技术的预训练模型,将节省我的时间并使我能够构建一个更好的解决方案,那会更好。这次经历之后,我试图探索在我解决的问题陈述中使用迁移学习的选项,如果我发现没有迁移学习的空间,那么我会从头开始构建算法;否则,我更愿意使用预训练模型。所以,在构建算法之前,总是探索所有选项。了解应用的使用情况,并基于此构建你的解决方案。假设你正在构建自己的自动驾驶汽车;那么,实时目标检测将成为自动驾驶汽车的一个重要组成部分,但它只是整个应用的一部分;因此,如果我们使用预训练模型来检测对象,那么我们可以用我们的时间来构建一个高质量的自动驾驶汽车。
我们如何使用预训练模型?
通常,预训练模型是二进制格式,可以下载后用于我们的应用程序。一些库,如 Keras、TensorFlow、Darknet 等,已经包含了那些可以加载和使用的预模型,你可以通过某些可用的 API 来使用这些预训练网络。这些预训练网络具有通过迁移学习泛化 PASCAL VOC 或 COCO 数据集之外图像的能力。我们可以通过微调模型来修改预存在的模型。我们不想修改权重太多,因为它是在大量数据集上使用大量 GPU 训练的。预训练模型具有泛化预测和对象分类的能力,因此我们知道这个预训练模型可以泛化到足够好的程度,以给出最佳的可能结果。然而,如果我们想从头开始训练模型,我们可以改变一些超参数。这些参数可以是学习率、周期数、层大小等。让我们讨论其中的一些:
- 
学习率:学习率基本上控制了我们应该更新神经元权重多少。我们可以使用固定学习率、递减学习率、基于动量的方法或自适应学习率。 
- 
周期数:周期数表示整个训练数据集应该通过神经网络的次数。我们需要增加周期数以减少测试误差和训练误差之间的差距。 
- 
批处理大小:对于卷积神经网络,小批量大小通常更可取。对于卷积神经网络来说,从 16 到 128 的范围确实是一个很好的起点。 
- 
激活函数:正如我们所知,激活函数向模型引入非线性。ReLU 激活函数是卷积神经网络的第一个选择。你也可以使用其他激活函数,如 tanh、sigmoid 等。 
- 
Dropout 用于正则化:正则化技术用于防止过拟合问题。Dropout 是深度神经网络的正则化技术。在这个技术中,我们会丢弃神经网络中的一些神经元或单元。神经元的丢弃基于概率值。默认值为 0.5,这是一个很好的起点,但我们可以根据观察到的训练误差和测试误差来改变这个值以进行正则化。 
在这里,我们将使用预训练模型,如 Caffe 预训练模型、TensorFlow 目标检测模型和你只需看一眼(YOLO)。对于从我们的摄像头进行实时流,我们使用 OpenCV,这对于绘制边界框也非常有用。所以,首先,让我们设置 OpenCV 环境。
设置编码环境
在本节中,我们将列出运行即将到来的代码所需的库和设备。你需要有一个至少可以以良好清晰度流式传输视频的摄像头。我们将使用 OpenCV、TensorFlow、YOLO、Darkflow 和 Darknet 库。我不会解释如何安装 TensorFlow,因为它是一个简单的过程,你可以通过点击www.tensorflow.org/install/install_linux来找到安装文档。
在本节中,我们将探讨如何首先设置 OpenCV,在接下来的章节中,我们将看到如何设置 YOLO、Darkflow 和 DarkNet。
设置和安装 OpenCV
OpenCV 代表开源计算机视觉。它旨在提高计算效率,重点在于实时应用。在本节中,你将学习如何设置 OpenCV。我使用的是 Ubuntu 16.04,并且我有一个 GPU,因此我已经安装了 CUDA 和 CUDNN。如果你还没有安装 CUDA 和 CUDNN,那么你可以参考这个 GitHub 链接:gist.github.com/vbalnt/a0f789d788a99bfb62b61cb809246d64。完成这些后,开始执行以下步骤:
- 
这将更新软件和库: $ sudo apt-get update
- 
这将升级操作系统并安装操作系统级别的更新: $ sudo apt-get upgrade
- 
这是用于编译软件的: $ sudo apt-get install build-essential
- 
这个命令安装 OpenCV 的先决条件: $ sudo apt-get install cmake git libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-dev
- 
这个命令安装 OpenCV 的可选先决条件: $ sudo apt-get install python-dev python-numpy libtbb2 libtbb-dev libjpeg-dev libpng-dev libtiff-dev libjasper-dev libdc1394-22-dev
- 
使用此命令创建一个目录: $ sudo mkdir ~/opencv
- 
跳转到我们刚刚创建的目录: $ cd ~/opencv
- 
在 opencv 目录内从 GitHub 克隆以下 OpenCV 项目: - 
$ sudo git clone https://github.com/opencv/opencv.git
- 
$ sudo git clone https://github.com/opencv/opencv_contrib.git
 
- 
- 
在 opencv 文件夹内,创建一个名为 build 的子目录: $ sudo mkdir ~/opencv/build
- 
跳转到 build 目录或文件夹: $ cd ~/opencv/build
- 
一旦你处于 build 文件夹位置,运行此命令。这可能需要一些时间。如果你运行此命令没有错误,则继续下一步: $ sudo cmake -D CMAKE_BUILD_TYPE=RELEASE \ -D CMAKE_INSTALL_PREFIX=/usr/local \ -D INSTALL_C_EXAMPLES=ON \ -D INSTALL_PYTHON_EXAMPLES=ON \ -D WITH_TBB=ON \ -D WITH_V4L=ON \ -D WITH_QT=ON \ -D WITH_OPENGL=ON \ -D OPENCV_EXTRA_MODULES_PATH=../../opencv_contrib/modules \ -D BUILD_EXAMPLES=ON ..
- 
使用此命令识别你的 CPU 核心数: $ nproc
- 
一旦你知道 CPU 的核心数,你可以用它来处理多线程。我分配了四个 CPU 核心,这意味着有四个线程同时运行。这个命令是 $ make -j4,它会编译 OpenCV 中用 C 编写的所有类。
- 
现在,执行以下命令以实际安装 OpenCV: $ sudo make install
- 
将配置文件的路径添加到配置文件中: $ sudo sh -c 'echo "/usr/local/lib" >> /etc/ld.so.conf.d/opencv.conf'
- 
使用此命令检查适当的配置: $ sudo ldconfig
一旦成功安装了 OpenCV,我们就可以使用这个库来流式传输实时视频。现在,我们将开始构建我们的基线模型。让我们开始吧!
基线模型的特征工程
为了构建基线模型,我们将使用预训练权重的 Google MobileNet SSD 检测网络的 Caffe 实现。这个模型已经在 PASCAL VOC 数据集上进行了训练。因此,在本节中,我们将探讨谷歌如何训练这个模型的方法。我们将了解 MobileNet SSD 背后的基本方法,并使用预训练模型来帮助节省时间。为了创建这种精确的模型,我们需要大量的 GPU 和训练时间,所以我们使用预训练模型。这个预训练的 MobileNet 模型使用卷积神经网络(CNN)。
让我们看看 MobileNet 如何使用 CNN 提取特征。这将帮助我们理解 CNN 背后的基本思想,以及 MobileNet 是如何被使用的。CNN 网络由层组成,当我们向 CNN 提供图像时,它会扫描图像的区域,并尝试使用区域提议方法提取可能的物体。然后,它找到包含物体的区域,使用扭曲的区域,并生成 CNN 特征。这些特征可以是像素的位置、边缘、边缘的长度、图像的纹理、区域的尺度、图片的明暗度、物体部分等等。CNN 网络通过自身学习这些类型的特征。您可以参考以下图示:

图 9.4:理解 CNN 中的特征提取
如您在前面的图像中看到的,图像区域已经被扫描,由 C1 和 S1 组成的第一个 CNN 层将生成特征。这些特征可以识别构成整个物体的边缘表示。在第二阶段,CNN 层学习有助于神经网络识别物体部分的特征表示。在最后阶段,它学习所有必要的特征,以便在给定的输入图像中识别存在的物体。如果您想探索 CNN 网络的每个方面,可以参考cs231n.github.io/convolutional-networks/。不用担心;我们将在下一节中概述 CNN 架构,以便您了解目标检测是如何工作的。现在,是时候探索 CNN 了。
选择机器学习算法
我们已经知道我们正在使用卷积神经网络(CNN)来开发这个应用程序。你可能想知道为什么我们选择了 CNN 而不是其他神经网络。你可能已经知道这个问题的答案。我们选择 CNN 有三个原因:
- 
现今存在的、经过精心手工标注的视觉数据量 
- 
通过 GPU 打开优化大门的负担得起的计算机器 
- 
CNN 的各种架构优于其他算法 
由于这些原因,我们选择了带有 SSD 的 CNN。在开发基线模型的过程中,我们将使用 MobileNet,它使用带有单次检测器(SSD)技术的 CNN。因此,在本节中,我们将查看开发 MobileNet 时使用的 CNN 架构。这将帮助我们理解预训练模型。
MobileNet SSD 模型的架构
MobileNet SSD 速度快,在图像和视频中执行对象检测任务很好。这个模型比基于区域的卷积神经网络(R-CNN)更快。SSD 之所以能达到这种速度,是因为它以不同的方式扫描图像和视频帧。
在 R-CNN 中,模型在两个不同的步骤中执行区域提议和区域分类,如下所示:
- 
首先,他们使用区域提议网络来生成感兴趣的区域 
- 
之后,全连接层或正敏感宪法层对所选区域中的对象进行分类。 
这些步骤与 R-CNN 相当,但 SSD 在单次操作中完成这些步骤,这意味着它同时预测边界框和边界框中出现的对象的类别。SDD 在图像或视频流以及一组基本真实标签作为输入时执行以下步骤:
- 
将图像通过一系列卷积层,生成不同尺寸矩阵形式的特征图集合。输出可以是 10×10 矩阵、6×6 矩阵或 3×3 矩阵。 
- 
对于每个特征图中的每个位置,使用 3×3 卷积滤波器来评估一组默认边界框。这些生成的默认边界框相当于锚框,这些锚框是使用 Faster R-CNN 生成的。 
- 
对于每个框,同时预测对象的边界框偏移和类别概率。 
- 
在训练过程中,将真实框与这些预测框进行匹配。这种匹配是通过使用交集并集(IoU)来完成的。最佳预测框将被标记为正边界框。这发生在每个边界框上。这里考虑的 IoU 的真实值超过 50%。 
看看以下图,以图形方式表示。MobileNets 具有简化的架构,使用深度可分离卷积来构建适用于移动和嵌入式视觉应用的超轻量级深度神经网络。MobileNets 是计算机视觉应用中更高效的机器学习模型。您也可以参考 MobileNet 的原始论文,链接为arxiv.org/pdf/1704.04861.pdf。

图 9.5:MobileNet SSD 的基本架构模块
图片来源:https://arxiv.org/pdf/1704.04861.pdf
如前图所示,我们使用了标准的卷积网络和深度卷积网络。MobileNet SDD 使用了 ReLU 激活函数。您可以通过以下图了解该网络具有什么样的滤波器形状:

图 9.6,MobileNet 主体架构
图片来源:https://arxiv.org/pdf/1704.04861.pdf
如果您想解释这个表格,那么让我们考虑一个例子。如果我们有一个原始图像,像素大小为 224×224,那么这个 Mobilenet 网络将图像缩小到 7×7 像素;它还有 1,024 个通道。之后,有一个平均池化层作用于所有图像,生成一个 1×1×1,024 大小的向量,实际上就是一个包含 1,024 个元素的向量。如果您想了解更多关于 MobileNet SSD 的信息,请参考以下资源:
- 
towardsdatascience.com/deep-learning-for-object-detection-a-comprehensive-review-73930816d8d9
- 
medium.com/ilenze-com/object-detection-using-deep-learning-for-advanced-users-part-1-183bbbb08b19
- 
machinethink.net/blog/googles-mobile-net-architecture-on-iphone/
现在,让我们继续到实现部分。
构建基线模型
在本节中,我们将探讨编码部分。您可以参考以下 GitHub 链接中的代码:github.com/jalajthanaki/Real_time_object_detection/tree/master/base_line_model。
首先,从提供的链接下载项目并安装 OpenCV,如本章前面所述的信息。当您下载此项目文件夹时,其中包含一个使用 Caffe 库实现的预训练的 MobileNet SSD,但在这里,我们使用预训练的二进制模型。我们使用 OpenCV 加载预训练模型以及从摄像头流式传输视频流。
在代码中,首先,我们指定需要导入的库和定义用于运行脚本的命令行参数。我们需要提供参数文件和预训练模型。参数文件的名称是MobileNetSSD_deploy.prototxt.txt,预训练模型的文件名是MobileNetSSD_deploy.caffemodel。我们还定义了模型可以识别的类别。之后,我们将使用 OpenCV 加载预训练模型。您可以在以下屏幕截图中参考此阶段的编码:

图 9.7:基线模型的代码片段
现在,让我们看看如何从我们的网络摄像头中流式传输视频。在这里,我们使用imutils库及其视频 API 从网络摄像头流式传输视频。使用 start 函数,我们将开始流式传输,然后定义帧大小。我们获取帧大小并将其转换为 blob 格式。此代码始终验证检测到的对象置信度分数将高于最小置信度分数或置信度分数的最小阈值。一旦我们得到更高的置信度分数,我们将为这些对象绘制边界框。我们可以看到到目前为止已经检测到的对象。您可以参考以下图中的基线模型视频流:

图 9.8:基线模型视频流的代码片段
为了停止流式传输,我们需要通过按 Q 键或 Ctrl + C 来中断循环,并且我们需要注意,当我们关闭程序时,所有窗口和进程都将适当地停止。您可以在以下屏幕截图中看到这一点:

图 9.9:结束脚本的代码片段
在我们运行脚本测试之前,让我们了解对象检测应用程序的测试指标。一旦我们理解了测试指标,我们将运行代码并检查我们获得了多少精度。
理解测试指标
在本节中,我们将介绍测试指标。我们将查看两个矩阵,这两个矩阵将帮助我们理解如何测试对象检测应用程序。这些测试矩阵如下:
- 
交并比 (IoU) 
- 
平均精度均值 (mAP) 
交并比 (IoU)
对于检测,使用 IoU(交并比)是为了确定对象提议是否正确。这是确定对象检测是否完美完成的一种常规方法。IoU 通常取提议的对象像素集 A 和真实对象像素集 B,并基于以下公式计算 IoU:

通常,IoU > 0.5,这意味着这是一个命中或识别了对象的像素或边界框;否则,它失败。这是对 IoU 的更正式理解。现在,让我们看看其背后的直觉和含义。让我们以一个图像为参考,帮助我们理解这个矩阵的直觉。您可以参考以下截图:

图 9.10:理解 IoU 背后的直觉
上述截图是检测图像中停车标志的一个示例。预测的边界框用红色绘制,属于这个红色框的像素被认为是集合 A 的一部分,而真实边界框用绿色绘制,属于这个绿色框的像素被认为是集合 B 的一部分。我们的目标是计算这些边界框之间的交并比。因此,当我们的应用程序绘制边界框时,它应该至少与真实边界框匹配超过 50%,这被认为是一个好的预测。IoU 的方程如下所示:

图 9.11:基于直观理解的 IoU 方程
在现实中,我们的预测边界框的(x, y)坐标与真实边界框的(x, y)坐标完全匹配的机会很少。在下面的图中,您可以看到各种关于 IoU 较差、较好和优秀的示例:

图 9.12:各种 IoU 边界框示例
IoU 帮助我们确定应用程序识别对象边界和区分不同对象的能力。现在,是时候了解下一个测试指标了。
平均精度均值
在本节中,我们将介绍平均精度均值(mAP)。在目标检测中,首先,我们识别目标边界框,然后将其分类到某个类别。这些类别有一些标签,我们将适当的标签提供给识别出的对象。现在,我们需要测试应用程序分配这些标签的效果如何,这意味着我们如何将对象分类到不同的预定义类别中。对于每个类别,我们将计算以下内容:
- 
真阳性 TP(c):预测的类别是 C,且该对象实际上属于类别 C 
- 
假阳性 FP(c):预测的类别是 C,但实际上该对象不属于类别 C 
- 
类 C 的平均精度由以下方程给出: ![平均精度均值]() 
因此,对于所有类别,我们需要计算 mAP,其计算公式如下:

如果我们想要更好的预测,那么我们需要将 IoU 从 0.5 增加到更高的值(最高可达 1.0,这将是最完美的)。我们可以用这个方程表示:mAP[@p],其中 p ∈ (0,1)是 IoU。mAP[@[0.5:0.95]]表示 mAP 是在多个阈值上计算,然后再次平均。
现在,让我们测试基线模型并检查此实现的 mAP 值。
测试基线模型
在本节中,我们将运行基线模型。为了运行脚本,我们需要跳转到放置名为real_time_object_detection.py的脚本的位置,并在命令提示符中执行以下命令:

图 9.13:基线方法的执行
看看下面的图。在这里,我刚刚放置了示例图像,但当你运行脚本时,你可以看到整个视频。这里是使用基线方法进行实时物体检测的整个视频链接:drive.google.com/drive/folders/1RwKEUaxTExefdrSJSy44NugqGZaTN_BX?usp=sharing。

图 9.14:基线方法的输出(图像是视频流的一部分)
在这里,MobileNet SSD 的 mAP 为 71.1%。你将在下一节中学习如何优化这种方法。首先,我们将列出下一次迭代中我们可以改进的点。所以,让我们跳到下一节。
现有方法的弊端
虽然 MobileNet SSD 速度快且给出了良好的结果,但它仍然无法识别像杯子、钢笔等类别。因此,我们需要使用在多种物体上训练过的预训练模型。在即将到来的迭代中,我们需要使用预训练模型,例如 TensorFlow 对象检测 API,它将能够识别与基线方法相比的不同物体。所以现在,让我们看看我们将如何优化现有方法。
如何优化现有方法
如前所述,为了优化现有方法,我将使用 TensorFlow 对象检测 API。你可以通过以下链接参考 Google 的 TensorFlow GitHub 仓库中的此 API:github.com/tensorflow/models/tree/master/research/object_detection。此 API 使用 COCO 数据集以及 PASCAL VOC 数据集进行训练;因此,它将具有识别多种类别的功能。
理解优化过程
对于我们来说,最重要的部分是如何使用各种预训练模型。步骤如下:
- 
首先,使用此链接拉取 TensorFlow 模型存储库: github.com/tensorflow/models
- 
一旦您拉取了仓库,您就可以找到我提到的 iPython Notebook,以了解如何使用预训练模型,并找到 iPython 笔记本的链接 github.com/tensorflow/models/blob/master/research/object_detection/object_detection_tutorial.ipynb。
- 
在这里,我们使用了 SSD 与 MobileNet,但我们使用的是检测模型库。这个模型是在 COCO 数据集上训练的,它们的版本基于模型的速度和性能。您可以从以下链接下载预训练模型: github.com/tensorflow/models/blob/master/research/object_detection/g3doc/detection_model_zoo.md。我已经将这些部分组合在一起,因此对每个人来说实现代码都很容易。
- 
主要的是,这个模型是使用 SSD 方法训练的,但它使用了如 kitti 数据集和 Open Image 数据集等数据集。因此,这个模型能够检测更多的对象,并且更加通用。Kitti 数据集的链接是 www.cvlibs.net/datasets/kitti/,Open Image 数据集的链接是github.com/openimages/dataset。
一旦我们下载了仓库和预训练模型,我们将加载预训练模型。在 TensorFlow 中,众所周知,模型以.pb 文件保存。一旦我们加载了模型,我们将使用 OpenCV 来流式传输视频。在下一节中,我们将实现修订方法的代码。
实现修订方法
在本节中,我们将了解修订方法的实现。您可以参考以下 GitHub 链接:github.com/jalajthanaki/Real_time_object_detection/tree/master/revised_approach,其中包含预训练模型和 TensorFlow 的物体检测文件夹。在我们开始编写代码之前,我将提供有关此方法文件夹结构的详细信息。您可以参考以下图示:

图 9.15:理解修订方法的文件夹结构
这里是下载自 TensorFlow 模型仓库的对象检测文件夹:github.com/tensorflow/models/tree/master/research/object_detection。在 utils 文件夹中,有一些辅助函数帮助我们流式传输视频。帮助我们运行脚本的主要脚本为 object_detection_app.py。预训练模型已保存在对象检测文件夹中。该文件夹中预训练模型的路径如下:~/PycharmProjects/Real_time_object_detection/revised_approach/object_detection/ssd_mobilenet_v1_coco_11_06_2017/frozen_inference_graph.pb。
现在,让我们一步一步地查看编码实现。在第一步中,我们将导入依赖库并加载预训练模型。您可以参考以下图示:

图 9.16:改进方法中加载预训练模型的代码片段
在这个模型中,有 90 种不同的对象可以被识别。一旦我们加载了模型,下一步就是 detect_objects() 函数,它用于识别对象。一旦对象被识别,就会为该对象绘制边界框,并且我们同时在这些对象上运行预训练模型,以便我们可以得到对象的识别标签,无论是杯子、瓶子、人等等。您可以参考以下图示:

图 9.17:detect_objects()函数的代码片段
在此之后,我们有 worker() 函数,它帮助我们流式传输视频以及执行一些 GPU 内存管理。您可以参考以下图示:

图 9.18:worker()函数的代码片段
如您所见,我们已定义了 GPU 内存分数,以及检测对象时使用的颜色类型。现在,让我们看看脚本的主要功能。在主函数中,我们定义了一些可选参数及其默认值。这些参数的列表如下:
- 
摄像头设备索引: --source=0
- 
视频流中帧的宽度 --width= 500
- 
视频流中帧的高度 --height= 500
- 
工作者数量 --num-workers=2
- 
队列的大小 --queue-size=5
您可以参考以下图示中显示的主函数实现:

图 9.19:主函数的代码片段
当我们运行脚本时,我们可以看到即将给出的图示中的输出。在这里,我们放置了图像,但您可以通过此链接查看视频:
drive.google.com/drive/folders/1RwKEUaxTExefdrSJSy44NugqGZaTN_BX?usp=sharing

图 9.20:改进方法的结果输出
一旦我们结束视频,资源和过程也应该结束。为此,我们将使用以下图中提供的代码:

图 9.21:脚本终止后释放资源的代码片段
测试改进方法
一旦我们执行了这个方法,我们就可以识别杯子、钢笔等对象。因此,我们可以说我们的基线方法是肯定得到了改进的,并且有一些修改后的标签,例如沙发(在这个方法中,被识别为 couch)。除此之外,如果我们谈论这个预训练模型的 mAP,那么根据文档,在 COCO 数据集上,这个模型大约有 52.4%的准确性。在我们的输入中,我们将大约有 73%的准确性。这种方法识别了更多不同类别的对象,这是一个很大的优势。
在下一节中,我们将讨论我们可以用来找到最佳解决方案的要点。
理解改进方法中的问题
我们已经尝试了既快又准确的方法,但我们需要一个既快又准确且经过优化的方法。这些是我们开发最佳解决方案时需要记住的要点:
- 
基于 SSD 的方法很好,但当你使用 COCO 或 PASCAL VOC 数据集训练自己的模型时,它的准确性并不高。 
- 
TensorFlow 检测模型库在 COCO 测试数据集上的 mAP 分数将在 20%到 40%之间。因此,我们需要探索其他技术,这些技术可以帮助我们在处理和目标检测准确性方面取得更好的结果。 
因此,在下一节中,我们将探讨可以帮助我们优化改进方法的方法。
最佳方法
在本节中,我们将尝试名为YOLO的方法。YOLO 代表 You Only Look Once。这项技术提供了良好的准确性,速度快,内存管理简单。本节将分为两部分:
- 
理解 YOLO 
- 
使用 YOLO 实现最佳方法 
在第一部分,我们将了解 YOLO 的基本知识。在实现过程中,我们将使用预训练的 YOLO 模型来使用 YOLO。那么,让我们开始吧!
理解 YOLO
YOLO 是一种最先进的实时目标检测系统。在 GPU Titan X 上,它以 40-90 FPS 的速度处理图像,在 PASCAL VOC 数据集上的 mAP 为 78.6%,在 coco 测试-dev 数据集上的 mAP 为 48.1%。因此,现在我们将探讨 YOLO 是如何工作以及如何处理图像以识别对象的。我们使用 YOLOv2(YOLO 版本 2),因为它是一个更快的版本。
YOLO 的工作原理
YOLO 重新定义了目标检测问题。它将对象识别任务视为从图像像素到边界框坐标和类别概率的单个回归问题。单个卷积网络同时预测多个边界框及其类别概率。YOLO 在完整图像上训练并直接优化检测性能。与传统方法相比,这种方法具有以下优点:
- 
YOLO 非常快。这是因为,在 YOLO 中,帧检测是一个回归问题,我们不需要使用复杂的流程。 
- 
我们可以在测试时简单地在我们新的图像上运行我们的神经网络来预测检测。我们的基础网络在 Titan X GPU 上以每秒 45 帧的速度运行,没有批处理,而快速版本则超过 150 fps。这意味着我们可以以小于 25 毫秒的延迟实时处理流媒体视频。 
YOLO 在预测关于类别及其外观的信息时,会全局处理图像。它还学习对象的泛化。
YOLO 将输入图像划分为一个 S×S 网格。如果一个物体的中心落在网格单元中,那么该网格单元负责检测该物体。每个网格单元预测 B 个边界框及其置信度分数。这些置信度分数反映了模型对框包含对象的信心程度以及它认为预测的框有多准确。因此,正式来说,我们可以通过这个符号定义 YOLO 的置信度机制。我们定义置信度为 Pr(object) * IoU。如果该单元格中没有对象存在,那么置信度分数应该是零;否则,我们希望置信度分数等于预测框与真实框之间的 IoU。每个边界框由五个预测组成:x, y, w, h 和置信度。其中(x, y)坐标表示框相对于网格单元边界的中心。w 和 h 代表宽度和高度,它们相对于整个图像进行预测。最后,置信度分数代表 IoU。对于每个网格单元,它预测 C 个条件类别概率,Pr(Classi|Object)。这些概率是在包含对象的网格单元上条件化的。我们每个网格单元只预测一组类别概率,无论边界框 B 的数量是多少。在测试时,我们乘以条件类别概率和由这个方程定义的个体框置信度预测:

前面的方程为我们提供了每个框的类别特定置信度分数。这个分数包含了该类别出现在框中的概率以及预测框与对象拟合得有多好。YOLO 整个过程的图示如下所示:

图 9.22:YOLO 目标检测的图示表示
现在,让我们了解 YOLO 架构的基本知识。
YOLO 的架构
在本节中,你将学习 YOLO 架构的基本知识。在 YOLO 中,有 24 个卷积层,后面跟着两个全连接层。YOLO 没有使用 GoogleNet 中的 inception 模块,而是简单地使用 1×1 的降维层,然后是 3×3 的卷积层。Fast YOLO 使用具有较少卷积层的神经网络。我们使用 9 层而不是 24 层。在这些层中,我们也使用了较少的过滤器。除此之外,在训练和测试期间,YOLO 和 Fast YOLO 的所有参数都是相同的。你可以参考以下图中的架构:

图 9.23:YOLO 架构
现在,让我们继续到实现部分。
使用 YOLO 实现最佳方法
为了实现 YOLO,我们需要安装 Cython 模块。除此之外,你可以使用 Darknet 或 Darkflow,它是 Darknet 上的 TensorFlow 包装器。Darknet 使用 C 和 CUDA 编写,因此速度相当快。在这里,我们将实现这两种选项。在实现之前,我们需要设置环境。实现部分应分为两个部分:
- 
使用 Darknet 实现 
- 
使用 Darkflow 的实现 
你可以参考此 GitHub 仓库中的所有代码:github.com/jalajthanaki/Real_time_object_detection_with_YOLO。
使用 Darknet 实现
我们按照以下步骤使用 Darknet 实现 YOLO:
- 
为 Darknet 设置环境 
- 
编译 Darknet 
- 
下载预训练权重 
- 
对图像运行目标检测 
- 
对视频流运行目标检测 
为 Darknet 设置环境
在这一步,我们需要下载 Darknet 的 GitHub 仓库。我们可以使用以下命令完成此操作。你可以将此仓库下载到任何路径:
$ git clone https://github.com/pjreddie/darknet
执行此命令后,将创建名为 Darknet 的目录。之后,你可以跳到下一步。
编译 Darknet
下载完 Darknet 后,我们需要切换到名为 Darknet 的目录。之后,我们需要编译 Darknet。因此,我们需要依次执行以下命令:
$ cd darknet
$ make
下载预训练权重
配置文件已经包含在 darknet 目录中的 cfg/ 子目录内。因此,通过执行以下命令,你可以下载 YOLO 模型的预训练权重:
$ wegt https://pjreddie.com/media/files/yolo.weights
此下载可能需要一些时间。一旦我们有了预训练权重,我们就可以运行 Darknet。
对图像运行目标检测
如果你想要识别图像中的对象,那么你需要执行以下命令:
./darknet detect cfg/yolo.cfg yolo.weights data/dog.jpg
你可以在以下图中查看此命令的输出:

图 9.24:使用 Darknet 对图像进行目标检测的输出
现在,让我们在视频流上实现 YOLO。
在视频流上运行目标检测
我们可以使用以下命令在视频流上运行 YOLO:
./darknet detector demo cfg/coco.data cfg/yolo.cfg yolo.weights <video file>
在这里,我们需要传递视频的路径。有关更多信息,您可以参考以下 Darknet 文档:pjreddie.com/darknet/yolo/。现在,让我们了解 Darkflow 的实现。
使用 Darkflow 的实现
在这个实现中,您需要参考名为 Darkflow 的文件夹中给出的代码。我们需要执行以下步骤:
- 
安装 Cython 
- 
构建已提供的设置文件 
- 
测试环境 
- 
加载模型并在图像上运行目标检测 
- 
加载模型并在视频流上运行目标检测 
安装 Cython
为了安装 Cython,我们需要执行以下命令。这个 Cython 包是必需的,因为 Darkflow 是一个 Python 包装器,它使用来自 Darknet 的 C 代码:
$ sudo apt-get install Cython
一旦安装了 Cython,我们就可以构建其他设置。
构建已提供的设置文件
在这个阶段,我们将执行设置必要的 Cython 环境的命令。命令如下:
$ python setup.py build_ext --inplace
当我们执行此命令时,我们将在克隆的 Darkflow 目录中使用./flow而不是flow,因为 Darkflow 没有全局安装。一旦此命令成功运行,我们需要测试是否完美安装了所有依赖项。您可以使用以下命令下载预训练权重:
$ wegt https://pjreddie.com/media/files/yolo.weights
测试环境
在这个阶段,我们将测试 Darkflow 是否运行得完美。为了检查这一点,我们需要执行以下命令:
$ ./flow --h
您可以参考以下图示:

图 9.25:Darkflow 成功测试结果
一旦您看到前面的输出,您就会知道您已成功配置了 Darkflow。现在,让我们运行它。
加载模型并在图像上运行目标检测
我们可以在图像上运行 Darkflow。为此,我们需要加载 YOLO 预训练权重、配置文件和图像路径,然后您可以执行以下命令:
./flow --imgdir sample_img/ --model cfg/yolo.cfg --load ../darknet/yolo.weights
如果您想将目标检测保存为 json 格式,也是可能的。您需要执行以下命令:
./flow --imgdir sample_img/ --model cfg/yolo.cfg --load ../darknet/yolo.weights --json
您可以在sample_img/out文件夹内看到输出;参考以下图示:

图 9.26:使用 Darkflow 预测对象输出的图像
您也可以参考以下图示:

图 9.27:图像中检测到的对象的 json 输出
加载模型并在视频流上运行目标检测
在本节中,我们将对视频流运行目标检测。首先,我们将看到如何使用网络摄像头并执行目标检测。该命令如下:
./flow --model cfg/yolo.cfg --load ../darknet/yolo.weights --demo camera --saveVideo --gpu 0.60
你可以参考以下图表。你可以在以下链接中查看视频:drive.google.com/drive/folders/1RwKEUaxTExefdrSJSy44NugqGZaTN_BX?usp=sharing

图 9.28:使用 Darkflow 对网络摄像头视频流进行目标检测的输出
我们也可以为预录制的视频运行 Darkflow。为此,你需要运行以下命令:
./flow --model cfg/yolo.cfg --load ../darknet/yolo.weights --demo ~/Downloads/Traffic.avi --saveVideo --gpu 0.60
你可以参考以下图表。你可以在以下链接中查看视频:drive.google.com/drive/folders/1RwKEUaxTExefdrSJSy44NugqGZaTN_BX?usp=sharing。

图 9.29:使用 Darkflow 对预录制的视频进行目标检测的输出
在这两个命令中,我们都使用了—save Video 标志来保存视频和—gpu 0.60 标志,它使用 GPU 的 60% 内存。使用这种方法,我们将获得 78% 的准确率。
摘要
在本章中,你学习了迁移学习。我们探索了不同的库和方法,以构建实时目标检测应用程序。你学习了如何设置 OpenCV,并了解了它在构建基线应用程序中的实用性。在基线方法中,我们使用了使用 caffe 深度学习库训练的模型。之后,我们使用 TensorFlow 构建了实时目标检测,但最终我们使用了预训练的 YOLO 模型,它优于其他所有方法。这种基于 YOLO 的方法为我们提供了更通用的目标检测应用程序方法。如果你对构建计算机视觉的创新解决方案感兴趣,那么你可以报名参加 VOC 挑战赛。这会提升你的技能,并给你一个学习的机会。你可以通过以下链接获取更多信息:host.robots.ox.ac.uk/pascal/VOC/(PASCAL VOC 挑战赛 2005-2012)。你也可以构建自己的算法,检查结果,并将你的结果与现有方法进行比较,如果它优于现有方法,你绝对可以在知名期刊上发表论文。通过使用 YOLO 方法,我们在 PASCAL VOC 数据集上获得了 78% 的平均精度均值(mAP),当将此模型应用于任何视频或图像时,它表现得相当不错。本章的代码归功于 Adrian Rosebrock、Dat Tran 和 Trieu。我们根据 COCO 数据集或 PASCAL VOC 数据集得到的 mAP 来定义 mAP 分数。
在下一章中,我们将探讨属于计算机视觉领域的另一个应用:人脸检测和面部表情检测。为了构建这个应用,我们将使用深度学习技术。所以,继续阅读吧!
第十章. 面部识别与面部情感识别
在上一章中,我们探讨了如何使用卷积神经网络和 YOLO(You Only Look Once)算法检测诸如汽车、椅子、猫和狗等物体。在本章中,我们将检测人脸。除此之外,我们还将研究人脸的表情,例如人脸看起来快乐、中性、悲伤等。因此,本章将很有趣,因为我们将关注一些最新的面部检测和面部情感识别技术。我们将把本章分为两部分:
- 
面部检测 
- 
面部情感识别 
首先,我们将介绍面部检测的工作原理,然后我们将继续到面部情感识别部分。总的来说,我们将在本章中涵盖以下主题:
- 
介绍问题陈述 
- 
设置编码环境 
- 
理解面部识别的概念 
- 
面部识别实现的途径 
- 
理解面部情感识别的数据集 
- 
理解面部情感识别的概念 
- 
构建面部情感识别模型 
- 
理解测试矩阵 
- 
测试模型 
- 
现有方法的局限性 
- 
如何优化现有方法 - 理解优化过程
 
- 
最佳方法 - 实施最佳方法
 
- 
总结 
介绍问题陈述
我们希望开发两个应用。一个应用将识别人脸,另一个将识别人脸的情感。我们将在本节中讨论这两个应用。我们将探讨我们到底想开发什么。
面部识别应用
此应用应基本能够从图像或实时视频流中识别出人脸。参考以下照片,它将帮助你理解我所说的从图像或实时视频流中识别人脸的含义:

图 10.1:理解面部识别应用演示输出
图片来源:https://unsplash.com/photos/Q13lggdvtVY
如前图(图 10.1)所示,当我们提供任何图像作为输入时,在第一步,机器可以识别图像中存在多少人脸。作为输出,我们可以得到人脸的裁剪图像。
此外,我还希望应用能够根据面部识别出人的名字。我想你熟悉这类应用。让我提醒你。当你将图片上传到 Facebook 时,Facebook 的人脸识别机制会立即识别出图片中的人名,并建议你在图片中标记他们。在这里,我们将开发类似的功能,即面部识别应用。现在让我们继续到应用的另一部分。
面部情感识别应用
在这个应用的部分,我们想要构建一个能够检测人脸情绪类型的应用程序。我们将尝试识别以下七种情绪:
- 
愤怒 
- 
厌恶 
- 
恐惧 
- 
幸福 
- 
悲伤 
- 
惊讶 
- 
中性 
因此,我们将面部情绪分为这七种类型。这种应用程序将有助于了解人们正在经历什么样的感受,这种洞察力将有助于进行情感分析、肢体语言分析等。
在这里,我们首先构建人脸识别应用程序,然后继续构建人脸情绪识别应用程序。
设置编码环境
在本节中,我们将设置人脸识别应用程序的编码环境。我们将查看如何安装依赖项。我们将安装以下两个库:
- 
dlib 
- 
face_recognition 
让我们开始安装过程。
安装 dlib
为了安装 dlib 库,我们需要执行以下步骤。我们可以在 Linux 操作系统(OS)或 macOS 上安装此库。让我们按照逐步说明进行:
- 
通过执行此命令下载 dlib 的源代码: sudo git clone https://github.com/davisking/dlib.git.
- 
现在通过执行此命令跳转到 dlib目录:cd dlib。
- 
现在我们需要构建主要的 dlib库,因此我们需要逐步执行以下命令:- 
sudo mkdir build。
- 
cd build。
- 
cmake .. -DDLIB_USE_CUDA=0 -DUSE_AVX_INSTRUCTIONS=1。
- 
cmake --build。
 
- 
一旦项目成功构建,你可以进入下一个安装步骤。你还需要安装 OpenCV。OpenCV 的安装步骤已在第十章中给出。
安装 face_recognition
为了安装 face_recognition 库,我们需要执行以下命令:
$ sudo pip install face_recognition (This command is for python 2.7)
$ sudo pip3 install face_recognition (This command is for python 3.3+)
之前的命令只有在dlib库完美安装的情况下才会安装face_recognition库。
一旦安装了前两个库,我们就可以进入下一部分,我们将讨论人脸识别的关键概念。
理解人脸识别的概念
在本节中,我们将探讨人脸识别的主要概念。这些概念将包括以下主题:
- 
理解人脸识别数据集 
- 
人脸识别算法 
理解人脸识别数据集
你可能会想知道为什么我直到现在还没有讨论与数据集相关的内容。这是因为我不想通过提供两个不同应用程序的数据集的所有细节来混淆你。我们将在这里覆盖的数据集将用于人脸识别。
如果你想要从头开始构建人脸识别引擎,则可以使用以下数据集:
- 
CAS-PEAL 人脸数据集 
- 
野生人脸标签 
让我们进一步详细讨论它们。
CAS-PEAL 人脸数据集
这是一个用于面部识别任务的大型数据集。它包含各种类型的人脸图像。它包含具有不同变化来源的人脸图像,特别是用于面部识别任务的姿态、情绪、配饰和光照(PEAL)。
这个数据集包含 1,040 个人的 99,594 张图片,其中 595 人是男性,445 人是女性。捕捉到的个人图像具有不同的姿态、情绪、配饰和光照。请参考以下照片查看这一点。如果您想查看样本数据集,也可以参考以下链接:www.jdl.ac.cn/peal/index.html。

图 10.2:CAS-PEAL 人脸数据集样本图像
图片来源:http://www.jdl.ac.cn/peal/Image/Pose_normal/NormalCombination-9-Cameras.jpg
您可以从以下链接下载这个数据集:www.jdl.ac.cn/peal/download.htm
LFW 人脸数据集
这个数据集也被称为 LFW 数据集。它在face_recognition库中被使用。我们将使用这个库来构建我们的面部识别应用。这个数据集包含了从网络上收集的超过 13,000 张人脸图片。每张人脸都标有图片中人物的姓名。因此,这个数据集是一个标记数据集。数据集中有 1,680 个人的两张或更多不同的人脸图像。您可以通过以下图表来参考样本数据集:

图 10.3:LFW 数据集的样本图像
图片来源:http://vis-www.cs.umass.edu/lfw/person/AJ_Cook.html
注意
您可以通过点击vis-www.cs.umass.edu/lfw/index.html了解更多关于这个数据集的信息。您也可以通过相同的链接下载数据集。您还可以通过点击www.vision.caltech.edu/Image_Datasets/Caltech_10K_WebFaces/来参考 Caltech 10,000 网络人脸数据集。您还应该参考 INRIA 人脸数据集,这将非常有用。INRIA 人脸数据集的链接是pascal.inrialpes.fr/data/human/.
为了构建面部识别应用,我们将使用face_recognition库。我们正在使用这个库通过其 API 提供的预训练模型。我们肯定会探索这个预训练模型和库背后的算法和概念。所以,让我们开始吧!
面部识别算法
在本节中,我们将查看用于人脸识别的核心算法。该算法的名称是方向梯度直方图(HOG)。我们将了解 HOG 在人脸识别任务中的应用。人脸识别(FR)任务基本上是一个分类任务,因为我们不仅从图像中检测人脸,还试图通过人脸识别出人的名字。HOG 是一个很好的尝试选项。
另一种方法是使用卷积神经网络(CNN)。在本节中,我们还将介绍 CNN 在人脸识别任务中的应用。因此,让我们从 HOG 开始吧!
方向梯度直方图(HOG)
HOG 算法是用于人脸识别的最佳方法之一。HOG 方法由 Dalal 和 Triggs 在他们的开创性 2005 年论文中提出,该论文可在lear.inrialpes.fr/people/triggs/pubs/Dalal-cvpr05.pdf找到。HOG 图像描述符和线性支持向量机可以用来训练高度准确的分类器,这些分类器可以分类人脸检测器。因此,HOG 也可以应用于人脸识别任务。首先,我们将介绍算法背后的基本直觉。
HOG 是一种特征描述符。特征描述符是一种通过提取有用信息并忽略其他信息来简化图像的图像表示。在这里,我们的重点将放在人脸上。因此,如果有其他物体,我们将忽略它们。LWF 数据集噪声较少,因此生成准确特征描述符的任务相对容易。逐步过程如下:
第 1 步:为了在图像中找到人脸,我们首先将彩色图像转换为黑白图像,因为我们不需要颜色数据来识别人脸。参考以下图表:

图 10.4:将彩色图像转换为黑白图像
第 2 步:在这个步骤中,我们将一次查看图像中的每个单独的像素。对于每个单独的像素,我们希望查看直接围绕它的像素。参考以下图表:

图 10.5:扫描图像中每个单独像素的过程
第 3 步:在这里,我们的目标是找出当前像素相对于其直接周围的像素有多暗。我们需要画一个箭头,指示图像像素变暗的方向。为了实现这一点,我们需要扫描整个图像。参考以下图表:

图 10.6:从亮像素到暗像素的箭头方向
如前图所示,我们考虑了一个像素及其周围的像素。通过观察像素,我们可以轻松地判断箭头头指向较暗的像素。
第 4 步:如果我们对图像中的每个像素重复此过程,那么最终每个像素都将被箭头替换。这些箭头被称为梯度。这些梯度显示了整个图像中从亮到暗像素的流动。参考以下图示:

图 10.7:整个图像的梯度箭头
在前面的图中,您可以看到在为输入图像生成梯度后的输出类型。扫描整个图像可能看起来像是一件随机的事情,但用梯度替换像素是有原因的。如果我们直接分析原始像素值,那么同一个人的非常暗和非常亮的图像将具有完全不同的像素值,这在我们尝试识别该人的面部时会使事情变得更加复杂。在这里,我们考虑像素亮度变化的方向。我们发现,同一个人的非常暗和非常亮的图像最终都会得到面部完全相同的表示。这种表示对我们处理面部识别任务来说将很容易处理。这就是生成整个图像梯度的主要原因。然而,尽管如此,我们将在下一步讨论一个挑战。
第 5 步:为每个像素保存梯度给我们提供了太多的信息,而且我们可能会以低效的方式使用这些信息。因此,我们需要获得我们将用于 FR 任务的最基本信息。我们将通过只考虑更高层次上的亮度和暗度的基本流动来实现这一点,这样我们就可以看到图像的基本模式。实现这一点的过程在第 6 步中给出。
第 6 步:我们将此图像分解成每个 16 x 16 像素的小方块。在每个方块中,我们将计算每个主要方向上的梯度点数,这意味着我们将计算有多少箭头向上、向下、向右、向左等。在计数之后,我们将用最强的箭头方向替换图像中的那个方块。最终结果是,我们将原始图像转换成一个简单的表示,该表示捕捉到了面部的基本结构。参考以下图示:

图 10.8:HOG 版本中的简单面部表示
这种表示对于 FR 任务来说很容易处理;它被称为图像的 HOG 版本。它代表了我们在 FR 任务中要考虑的特征,这就是为什么这种表示被称为 HOG 特征描述符。
第 7 步:为了找出这个 HOG 图像中的面部,我们必须找出我们的图像中看起来最像从其他训练面部中提取的已知 HOG 模式的部分。参考以下图示:

图 10.9:使用我们图像的 HOG 版本识别人脸的过程
使用这项技术,我们可以轻松地识别任何图像中的人脸。
用于 FR 的卷积神经网络(CNN)
在本节中,我们将探讨如何使用 CNN 从图像中识别人脸。本节分为两部分:
- 
简单的 CNN 架构 
- 
理解 CNN 在 FR 中的应用 
简单的 CNN 架构
我不想深入探讨 CNN 是如何工作的,因为我已经在第九章构建实时物体检测中提供了大部分必要的细节;然而,我想提醒你一些关于 CNN 的必要信息。首先,参考以下图表:

图 10.10:CNN 架构
如您在前面的图表中所见,有一个卷积层、一个池化层、一个全连接层和一个输出层。涉及不同的激活函数、惩罚和 SoftMax 函数。这是高级信息。对于这个 FR 任务,我们可以使用三个卷积层和池化层,使用 ReLU 作为激活函数。您可以添加更多层,但这会使训练的计算成本更高。
理解 CNN 在 FR 中的应用
直观地说,CNN 模型按照以下步骤构建一个良好的 FR 应用。基本过程如下:
第 1 步:观察一张图片。裁剪只包含人脸的图像。
第 2 步:现在,在这个步骤中,我们专注于人脸,并试图理解即使人脸朝向奇怪的方向,或者图像是在不良光照下拍摄的,我们仍需要识别这类图像中人脸的正确位置。第 3 步将为我们提供解决方案。
第 3 步:为了从任何图像中识别人脸,无论图像是在不良光照条件下拍摄的,还是人脸的朝向看起来非常奇怪,我们需要识别人脸。为了实现这一点,我们挑选出人脸的独特特征,这些特征可以告诉我们关于人脸的独特信息。借助这些独特特征,我们可以识别同一个人的脸,以及不同人的脸。这些特征可以包括眼睛的大小、脸的长度等等。有 68 个特定的点需要考虑;它们被称为关键点。这些点是基于人脸关键点估计定义的。请参考以下论文以获取更多详细信息:www.csc.kth.se/~vahidk/papers/KazemiCVPR14.pdf。请查看以下图表:

图 10.11:用于人脸关键点估计的 68 个点
第 4 步:我们需要通过名字识别某人的面孔,因此为了实现这一点,我们将比较该面孔的独特特征与我们已经知道的所有人,以确定该人的名字。假设你已经添加了比尔·盖茨、巴拉克·奥巴马等人的图片。你已经为他们生成了独特的面部特征,现在我们将比较这些已经生成的面部特征,如果特征相似,那么我们就能知道图像中的人的名字,即巴拉克·奥巴马或比尔·盖茨。基于面部特征的识别是一个分类问题,可以通过 CNN 轻松解决。我们正在生成一个 128 维度的面孔嵌入向量。作为输入,我们应该提供这个面孔嵌入向量。一旦我们完成训练,我们的应用程序将准备好识别人的名字。
第 5 步:训练好的模型会查看我们过去测量过的所有面孔,并找到与我们的面孔测量值最接近的人。那就是我们的匹配对象。
上述方法是为我们的基于 CNN 的 FR 和实时人脸识别任务。我们已经涵盖了 FR 任务中使用的算法的基本概念和背后的思想。现在让我们开始实现。
实现人脸识别的方法
在本节中,我们将实现 FR 应用程序。我们正在使用face_recognition库。我们已经为它配置了环境。我们将在这里实现以下方法:
- 
基于 HOG 的方法 
- 
基于 CNN 的方法 
- 
实时人脸识别 
现在我们开始编码!
实现基于 HOG 的方法
在这种方法中,我们使用 HOG 算法找出两件事:图像中的面孔总数和步态。我们使用face_recognition库的 API。您可以通过点击以下 GitHub 链接找到代码:github.com/jalajthanaki/Face_recognition/blob/master/face_detection_example.py。代码片段如下所示:

图 10.12:基于 HOG 的 FR 方法代码片段
在前面的图中,我们提供了一个图像作为输入,借助face_recognition库的 API,我们可以找到图像中面孔的像素位置。在这里,我们还将计算图像中有多少个面孔,借助Image库,我们可以从提供的图像中裁剪面孔。您可以在以下图中找到此脚本的输出:

图 10.13:基于 HOG 的 FR 方法输出
参考以下截图中的裁剪面孔输出:

图 10.14:裁剪后的面孔输出
如您在最后一个输出图中所见,通过简单的 API,我们可以构建一个简单的人脸识别应用程序。这种方法对我们来说是一种基线方法。
现在让我们转向基于 CNN 的方法。与基于 HOG 的方法相比,基于 CNN 的方法更准确。如果我们为基于 CNN 的方法使用 GPU,那么我们可以以更短的时间训练模型。现在让我们看看基于 CNN 方法的代码。
实现基于 CNN 的方法
在这种方法中,我们将使用face_recognition库,其中我们指定了模型的名称。我们模型的名称是cnn。这个特定的方法将通过face_recognition API 加载预训练模型,我们可以生成更准确的结果。您可以通过点击以下 GitHub 链接找到代码:github.com/jalajthanaki/Face_recognition/blob/master/face_detection_GPU_example.py。请参考以下图中的代码片段:

图 10.15:基于 CNN 方法的 FR 代码片段
在这里,实现代码几乎与之前相同,但不同之处在于我们在 API 调用期间提供了模型名称cnn。您可以在以下图中看到这个实现的输出:

图 10.16:基于 CNN 方法的 FR 输出
这个实现的输出与上一个相同。这个版本的实现速度快,并且准确性更高。现在让我们尝试为实时视频流实现 FR 任务。
实现实时人脸识别
在本节中,我们将为实时视频流实现 FR 任务。我们将尝试识别视频中出现的个人的名字。这难道不很有趣吗?让我们开始吧。您可以通过点击以下 GitHub 链接找到代码:github.com/jalajthanaki/Face_recognition/blob/master/Real_time_face_detection.py。
再次,我们正在使用face_recognition库的 API。我们也在使用OpenCV。首先,我们需要提供带有个人名字的个人的样本图像,这样机器就可以在学习过程中识别出这个人的名字,并在测试时识别它。在这个实现中,我提供了巴拉克·奥巴马和乔·拜登的图像。您也可以添加您的图像。如果人脸特征熟悉并且与已提供的图像匹配,则脚本将返回该人的名字;如果人脸特征与给定图像不熟悉,则该人的脸被标记为未知。请参考以下图中的实现:

图 10.17:实时 FR 的实现
如前述代码所示,我提供了巴拉克·奥巴马和乔·拜登的样本图像。我还提供了我将图像输入脚本的人的名字。我使用了相同的面部识别 API 来检测和识别视频流中的面部。当你运行脚本时,你的网络摄像头会实时传输视频,此脚本会检测面部,如果你提供机器已知的该人的图像,那么这次它也能正确识别。

图 10.18:实时 FR 的输出
如您所见,我没有向机器提供我的图像,因此它将我识别为未知,而它可以识别巴拉克·奥巴马的图像。您也可以通过点击github.com/jalajthanaki/Face_recognition/blob/master/img/Demo.gif找到动画图像。
我们完成了本章的第一部分,即开发一个可以识别人类面部,并根据面部识别出人的名字的应用程序。我们实现了三种不同的面部识别(FR)变体。
在下一节中,我们将探讨如何开发一个面部表情识别(FER)应用。为了构建此应用,我们需要不同类型的数据集,因此我们将从理解一个用于 FER 的数据集开始。
理解面部表情识别数据集
为了开发一个面部表情识别(FER)应用,我们正在考虑使用FER2013数据集。您可以从www.kaggle.com/c/challenges-in-representation-learning-facial-expression-recognition-challenge/data下载此数据集。我们需要了解关于此数据集的基本细节。数据集归功于 Pierre-Luc Carrier 和 Aaron Courville,作为持续研究项目的一部分。
此数据集包含 48x48 像素灰度人脸图像。任务是根据图像中显示的表情将每个面部分类。以下为七个类别,每个类别都有一个表示情绪类别的数字标签:
- 
0= 生气
- 
1= 厌恶
- 
2= 恐惧
- 
3= 快乐
- 
4= 悲伤
- 
5= 惊讶
- 
6= 中立
此数据集包含fer2013.csv文件。此 csv 文件将用作我们的训练数据集。现在让我们看看文件的属性。文件中有三列,如下所示:
- 
情绪: 这列包含面部表情的数字标签。对于恐惧,此列包含值为 2;对于悲伤,此列包含值为4,依此类推。
- 
像素: 这列包含单个图像的像素值。它代表图像的像素值矩阵。 
- 
用法:此列包含有关特定数据记录是否用于训练目的或测试目的的一般标签。此列有三个标签,分别是Training(训练)、PublicTest(公共测试)和PrivateTest(私有测试)。用于训练目的的数据样本有 28,709 个。公共测试集包含 3,589 个数据样本,私有测试集也包含 3,589 个数据样本。 
现在,让我们了解有助于我们开发 FER 应用的概念。
理解面部情感识别的概念
我们使用卷积神经网络(CNN)来开发 FER 应用。之前,我们看到了 CNN 的基本架构。为了开发 FER 应用,我们将使用以下 CNN 架构和优化器。我们正在构建一个两层深的 CNN。我们将使用两个全连接层和 SoftMax 函数来分类面部情感。
我们将使用由卷积层构成的多层结构,随后是 ReLU(修正线性单元)层,再之后是最大池化层。参考以下图表,它将帮助您理解 CNN 层的排列。让我们看看 CNN 的工作原理。我们将涵盖以下层:
- 
卷积层 
- 
ReLU 层 
- 
池化层 
- 
全连接层 
- 
SoftMax 层 
理解卷积层
在这个层中,我们将以像素值的形式输入我们的图像。我们使用一个 3 x 3 维度的滑动窗口,它在整个图像上滑动。滑动窗口选中的区域被称为感受野。它是图像的一部分。滑动窗口只是一个 3 x 3 维度的矩阵,它可以扫描整个图像。通过使用滑动窗口,我们使用 3 x 3 维度的矩阵扫描图像的九个像素值。这个感受野或图像的一部分是卷积网络的输入。
参考以下图表:

图 10.19:滑动窗口和感受野
这个感受野以输入图像的像素值形式携带值。这些像素值被称为特征图、特征、滤波器、权重矩阵或核。我们已经有了一个 3 x 3 的矩阵,被称为特征图。特征图的大小是一个超参数。我们可以取 n x n 矩阵,其中 n >= 1。在这里,我们考虑了一个 3 x 3 的矩阵来理解操作。现在进行简单的数学运算,步骤如下:
第一步:获取特征图。在这里,特征图指的是以感受野形式生成的图像块。
第二步:我们需要在特征图和整个图像之间执行点积。再次,我们使用滑动窗口扫描整个图像,并生成点积的值。
第 3 步:我们需要将我们从点积中获得的所有值加起来。
第 4 步:我们需要将点积求和的值除以特征中的总像素数。在这个解释中,我们总共有九个像素,所以我们将总和除以 9。作为输出,我们得到被称为特征图像的图像。
参考以下图表:

图 10.20:卷积层的数学运算
我们将重复这个操作,几乎针对图像的所有可能位置,并尝试所有可能的组合,这就是为什么这个操作被称为卷积操作。现在让我们看看 ReLU 层。
理解 ReLU 层
这个层基本上为卷积网络引入了非线性。在这里,我们需要使用激活函数。对于这个应用,我们选择了修正线性单元作为激活函数。这个层对我们的特征图进行某种归一化。让我们看看它对特征图做了什么:
第 1 步:这个层以卷积层生成的特征图作为输入。
第 2 步:这个层只是将负值转换为零。
参考以下图表:

图 10.21:ReLU 层的操作
现在是时候看看池化层了。
理解池化层
使用这个层,我们缩小图像。我们将在这里使用最大池化操作。我们需要执行以下步骤:
第 1 步:我们需要将特征图作为输入,这次,ReLU 层的输出被作为这个层的输入。
第 2 步:我们需要选择窗口大小。通常,我们选择 2 x 2 像素或 3 x 3 像素的大小。我们将 2 x 2 作为我们的窗口大小。
第 3 步:我们将根据这个窗口大小扫描整个图像,并从四个像素值中取最大值。
你可以通过参考以下图表来理解这个操作:

图 10.22:最大池化层的操作
我们可以将这些层堆叠得尽可能深。你可以重复卷积、ReLU 和池化层 n 次,以使 CNN 变深。
理解全连接层
所有层的输出都传递到全连接层。这一层有一个投票机制。所有图像块都被考虑在内。2 x 2 矩阵的图像块以水平方式排列。投票取决于一个值预测面部表情的强度。如果这一层的某些值很高,这意味着它们接近 1,如果某些值很低,这意味着它们接近 0。对于每个类别,某些单元格的值接近 1,而其他值是 0,这样我们的网络就会预测类别。请参考以下图表:

图 10.23:全连接层的直观理解
这里只执行了一个数学运算。我们正在取平均值。正如您在前面的图表中看到的,第一、第四、第五、第十和第十一行的全连接层正在预测一个类别,因此我们需要将这些单元格中所有现有值的总和,并找到它们的平均值。这个平均值告诉我们我们的网络在预测类别时的信心程度。我们可以堆叠尽可能多的全连接层。在这里,神经元的数量是超参数。
理解 SoftMax 层
我们还可以使用 SoftMax 层,它将特征值转换为概率值。这个方程如下:

图 10.24:SoftMax 方程
这一层获取特征值,并使用前面的方程生成概率值。请参考以下图表:

图 10.25:计算 SoftMax 函数的过程
基于反向传播更新权重
基于反向传播技术,CNN 网络的权重已经更新。我们将测量我们的预测答案与实际答案之间的差异。基于这个误差测量,我们计算损失函数的梯度,这告诉我们是否应该增加权重或减少它。如果预测答案和实际答案相同,则权重将不会发生变化。
我们已经理解了我们将用于开发面部情感识别模型的 CNN 的大部分核心概念。
构建面部情感识别模型
在本节中,我们将实现使用 CNN 的 FER 应用。为了编码目的,我们将使用TensorFlow、TFLearn、OpenCV和Numpy库。您可以通过使用此 GitHub 链接找到代码:github.com/jalajthanaki/Facial_emotion_recognition_using_TensorFlow。这是我们需要遵循的步骤:
- 
准备数据 
- 
加载数据 
- 
训练模型 
准备数据
在本节中,我们将准备可用于我们应用程序的数据集。如您所知,我们的数据集是灰度的。我们有两种选择。一种是我们只需要使用黑白图像,如果我们使用黑白图像,那么将有两个通道。第二种选择是将灰度像素值转换为 RGB(红色、绿色和蓝色)图像,并使用三个通道构建 CNN。为了我们的开发目的,我们使用两个通道,因为我们的图像是灰度的。
首先,我们正在加载数据集并将其转换为numpy数组。转换后,我们将以.npy格式保存它,以便我们可以随时加载该数据集。我们将实际数据记录保存在一个文件中,而它们的标签则保存在另一个文件中。我们的输入数据文件名为fer2013.csv。包含数据的输出文件是data_set_fer2013.npy,标签位于data_labels_fer2013.npy文件中。执行此任务的脚本名称是csv_to_numpy.py。您可以通过此 GitHub 链接参考其代码:github.com/jalajthanaki/Facial_emotion_recognition_using_TensorFlow/tree/master/data
参考以下图中加载数据的代码片段:

图 10.26:加载数据代码片段
helper函数的代码如下所示:

图 10.27:helper函数代码片段
现在我们来看看如何加载我们已保存为.npy格式的数据。
加载数据
在本节中,我们将探讨如何使用我们已准备的数据集,以便我们可以用它进行训练。在这里,我们创建一个单独的脚本以帮助我们加载数据。在这个脚本中,我们定义了一个测试数据集,我们将在测试期间使用它。
这是一段简单直接代码。您可以在以下图中找到代码片段:

图 10.28:数据加载脚本代码片段
当我们编写训练脚本时,将使用此类及其方法。您可以通过此 GitHub 链接查看此脚本的代码:github.com/jalajthanaki/Facial_emotion_recognition_using_TensorFlow/blob/master/dataset_loader.py。
训练模型
在本节中,我们将探讨如何训练模型,以便它可以识别面部表情。以下是我们将执行的操作步骤。您可以通过参考此 GitHub 链接找到此训练步骤的代码:github.com/jalajthanaki/Facial_emotion_recognition_using_TensorFlow/blob/master/emotion_recognition.py。
使用 dataset_loader 脚本加载数据
在这里,我们使用我们在上一节中编写的并理解的脚本加载数据集。您可以在下面的图中找到代码片段:

图 10.29:在模型训练过程中加载数据
现在,让我们构建实际用于训练的 CNN。
构建卷积神经网络
在这一步,我们将构建用于训练目的的 CNN。在这里,我们有三层卷积网络、ReLU 层和池化层。前两层有 64 个神经元,最后一层有 128 个神经元。我们添加了 dropout 层。对于一些神经元,dropout 层将值设置为 0。这个层选择那些长时间没有改变权重或长时间未激活的神经元。这将使我们的训练更加有效。我们有两个全连接层,以及一个使用 SoftMax 函数来推导面部情感类概率的全连接层。我们使用momentum函数来进行梯度下降。在这里,我们的损失函数是分类交叉熵。参考下面的图中代码片段:

图 10.30:构建 CNN 的代码片段
现在,让我们看看如何进行训练。
为 FER 应用进行训练
在这一步,我们需要开始训练,以便我们的模型可以学会预测面部情感。在这一步,我们将定义一些训练的超参数。参考下面的图中代码片段:

图 10.31:执行训练的代码片段
如前图所示,我们将 epoch 设置为100。训练批次大小为50。我们可以看到shuffle参数,它作为标志。此参数的值为true,表示我们在训练过程中正在打乱我们的数据集。
开始训练的命令是$ python emotion_recognition.py train。
预测和保存训练模型
在这一步,我们正在定义predict方法。这个方法帮助我们生成预测。我们还定义了一个可以帮助我们保存训练模型的方法。我们需要保存模型,因为我们可以在需要时加载它进行测试。您可以在下面的图中找到代码片段:

图 10.32:预测类别的代码片段
参考下面的图中代码片段:

图 10.33:保存训练模型的代码片段
现在是查看测试矩阵的时候了。之后,我们需要测试我们的训练模型。所以,在我们测试模型之前,我们应该理解测试矩阵。
理解测试矩阵
在本节中,我们将查看面部情绪应用的测试矩阵。测试的概念非常简单。我们需要开始观察训练步骤。我们正在跟踪损失和准确率的值。基于这些,我们可以决定我们模型的准确率。这听起来简单吗?我们已经训练了模型 30 个 epoch。这么多的训练需要超过三个小时。我们达到了 63.88%的训练准确率。请参考以下图中的代码片段:

图 10.34:训练进度,以了解训练准确率
这是训练准确率。如果我们想检查验证数据集上的准确率,那么它也会在训练步骤中给出。我们已经定义了验证集。借助这个验证数据集,训练好的模型生成其预测。我们比较预测类别和实际类别标签。之后,我们生成您在前面的图表中可以看到的验证准确率。这里,val_acc为 66.37%,这很棒。到目前为止,这个应用程序已经能够达到 65%到 70%的准确率。
测试模型
现在我们需要加载训练好的模型并对其进行测试。在这里,我们将使用视频流。FER 应用程序将根据我的面部表情检测情绪。您可以参考以下 GitHub 链接中的代码:github.com/jalajthanaki/Facial_emotion_recognition_using_TensorFlow/blob/master/emotion_recognition.py。
您可以在以下图中找到此代码片段:

图 10.35:加载训练好的模型并进行测试的代码片段
为了开始测试,我们需要执行以下命令:
$ python emotion_recognition.py poc
这次测试将使用您的网络摄像头。我有一些演示文件想要在这里分享。请参考以下图:

图 10.36:用于识别厌恶情绪的 FER 应用程序的代码片段
也请参考以下图:

图 10.37:识别快乐情绪的 FER 应用程序
请参考以下图中的代码片段:

图 10.38:用于识别中性情绪的 FER 应用程序的代码片段
请参考以下图中的代码片段:

图 10.39:用于识别愤怒情绪的 FER 应用程序的代码片段
现在我们来看看如何改进这种方法。
现有方法存在的问题
在本节中,我们将列出所有造成问题的点。我们应该尝试改进它们。以下是我认为我们可以改进的地方:
- 
如果你发现你的案例中类采样不合适,那么你可以采用采样方法 
- 
我们可以给我们的神经网络添加更多层 
我们可以尝试不同的梯度下降技术。
在这种方法中,训练需要花费大量时间,这意味着训练计算成本很高。当我们训练模型时,我们使用了 GPU,尽管 GPU 训练需要很长时间。我们可以使用多个 GPU,但这很昂贵,带有多个 GPU 的云实例也不划算。因此,如果我们能在这种应用中使用迁移学习或使用预训练模型,那么我们将获得更好的结果。
如何优化现有方法
正如你在上一节中看到的,由于计算硬件的缺乏,我们已经达到了 66%的准确率。为了进一步提高准确率,我们可以使用预训练模型,这将更加方便。
理解优化过程
在前几节中,我描述了一些问题。我们可以给我们的 CNN 添加更多层,但这将使计算成本更高,所以我们不会这么做。我们已经很好地采样了我们的数据集,所以我们不需要担心这一点。
作为优化过程的一部分,我们将使用使用keras库训练的预训练模型。这个模型使用了多个 CNN 层。它将在多个 GPU 上训练。因此,我们将使用这个预训练模型,并检查结果如何。
在下一节中,我们将实现可以使用预训练模型的代码。
最佳方法
我们已经实现了大约 66%的准确率;对于一个 FER 应用,最佳准确率大约是 69%。我们将通过使用预训练模型来实现这一点。所以,让我们看看实现方法,以及我们如何使用它来实现最佳结果。
实施最佳方法
在本节中,我们将实现适用于 FER 应用的最好方法。这个预训练模型是通过使用密集和深度卷积层构建的。由于六层深度 CNN,以及随机梯度下降(SGD)技术的帮助,我们可以构建预训练模型。每一层的神经元数量分别为 32、32、64、64、128、128、1,024 和 512。所有层都使用 ReLU 作为激活函数。3 x 3 的矩阵将用于生成初始特征图,2 x 2 的矩阵将用于生成最大池化。你可以从这个 GitHub 链接下载模型:github.com/jalajthanaki/Facial_emotion_recognition_using_Keras
你可以通过参考以下图表来查看代码:

图 10.40:使用预训练 FER 模型的代码片段
在前面的代码中,我们加载了预训练的keras模型。我们提供了两个方案。我们可以使用这个脚本从图像中检测面部表情,也可以通过提供视频流来实现。
如果你想测试任何图像中存在的面部表情,那么我们需要执行$ python image_test.py tes.jpg命令。我已经将此模型应用于tes.jpg图像。你可以看到以下输出图像:

图 10.41:图像中 FER 应用的输出
如果你想测试视频流的模型,那么你需要执行此命令:$python realtime_facial_expression.py。
参考以下图中的输出:

图 10.42:视频流中 FER 应用的输出
你可以在以下图中找到输出文件:

图 10.43:视频流中 FER 应用的输出
此应用为我们提供了大约 67%的准确率,这非常不错。
摘要
在本章中,我们探讨了如何使用face_recognition库开发人脸检测应用,该库使用基于 HOG 的模型来识别图像中的人脸。我们还使用了预训练的卷积神经网络,它可以从给定图像中识别人脸。我们开发了实时人脸识别来检测人的名字。对于人脸识别,我们使用了预训练的模型和现成的库。在章节的第二部分,我们开发了人脸表情识别应用,它可以检测人脸所携带的七种主要情绪。我们使用了TensorFlow、OpenCV、TFLearn和Keras来构建人脸表情识别模型。此模型在预测人脸情绪方面具有相当好的准确率。我们达到了最佳可能的准确率 67%。
目前,计算机视觉领域在研究方面发展迅速。你可以探索许多新鲜和酷的概念,例如 Facebook AI Research 团队提出的deepfakes和 3D 人体姿态估计(机器视觉)。你可以通过点击此处参考deepfakesGitHub 仓库:github.com/deepfakes/faceswap。你可以通过点击此链接参考 3D 人体姿态估计的论文:arxiv.org/pdf/1705.03098.pdf。这两个概念都是新颖的,所以你可以参考它们并制作一些有趣的应用。
下一章将是本书的最后一章。在这一章中,我们将尝试制作一个游戏机器人。这一章将大量使用强化学习技术。我们将开发一个可以自己玩 Atari 游戏的机器人。所以请继续阅读!
第十一章:构建游戏机器人
在前面的章节中,我们介绍了属于计算机视觉领域的一些应用。在本章中,我们将制作一个游戏机器人。我们将介绍构建游戏机器人的不同方法。这些游戏机器人可以用来玩各种 Atari 游戏。
让我们快速回顾一下过去两年。让我们从 2015 年开始。一家名为 DeepMind 的小型伦敦公司发布了一篇名为《用深度强化学习玩 Atari》的研究论文,可在arxiv.org/abs/1312.5602找到。在这篇论文中,他们展示了计算机如何学习和玩 Atari 2600 视频游戏。计算机只需观察屏幕像素就能玩游戏。我们的计算机游戏代理(即游戏玩家)在游戏得分增加时会获得奖励。这篇论文中展示的结果非常引人注目。这篇论文引起了很大的轰动,那是因为每个游戏都有不同的得分机制,而且这些游戏被设计成人类难以获得最高分。这篇研究论文的美丽之处在于,我们可以使用该概念和给定的模型架构,无需任何修改来学习不同的游戏。这个模型架构和算法应用于七款游戏,其中三款游戏中,算法的表现远远超过了人类!这在人工智能领域是一个巨大的飞跃,因为希望我们能够构建一个可以掌握许多任务的单一算法,并在未来几十年内的某个时刻构建一个通用人工智能或人工通用智能(AGI)系统。你可以在en.wikipedia.org/wiki/Artificial_general_intelligence上了解更多关于 AGI 的信息。我们都知道 DeepMind 很快就被谷歌收购了。
2017 年,谷歌 DeepMind 和 OpenAI 取得了重大里程碑,这让我们有理由相信通用人工智能(AGI)很快就会实现。让我们先从谷歌 DeepMind 开始;你一定听说过谷歌 DeepMind 的 AlphaGo 人工智能(一个游戏机器人)在一场三局比赛中击败了世界上最优秀的围棋选手。围棋是一项复杂的游戏,因为它对于每一个走法都有大量的排列组合。你可以通过点击这个 YouTube 视频观看这场比赛:www.youtube.com/watch?v=vFr3K2DORc8。现在让我们来谈谈 OpenAI。如果你第一次听说 OpenAI,这是一个简要的介绍。OpenAI 是一个非营利性的人工智能研究组织,由埃隆·马斯克共同创立,致力于构建既安全又能确保人工智能(AI)系统的利益尽可能广泛且均匀地分布的 AI。2017 年,OpenAI 的游戏机器人击败了世界上最优秀的 Dota 2 选手。你可以观看这个 YouTube 视频作为参考:www.youtube.com/watch?v=7U4-wvhgx0w。所有这些成就都是通过科技巨头创建的 AGI 系统环境实现的。创建 AGI 系统的目标是让一个系统能够执行各种复杂任务。理想的 AGI 系统可以帮助我们在医疗保健、农业、机器人技术等领域解决大量复杂任务,而无需对其算法进行任何更改。因此,如果我们能理解基本概念,以便开发 AGI 系统,那就更好了。
在本章中,我们首先尝试制作一个能够玩简单 Atari 游戏的机器人。我们将通过使用强化学习来实现这一点。
在本章中,我们将讨论以下主题:
- 
介绍问题陈述 
- 
设置编码环境 
- 
理解强化学习(RL) 
- 
基本 Atari 游戏机器人用于路径搜索 - 理解关键概念
 
- 
实现游戏机器人的基本版本 
- 
构建 Space Invaders 游戏机器人 - 理解关键概念
 
- 
实现 Space Invaders 游戏机器人 
- 
构建 Pong 游戏机器人 - 理解关键概念
 
- 
实现 Pong 游戏机器人 
- 
仅为了乐趣 - 实现 Flappy Bird 游戏机器人 
- 
总结 
介绍问题陈述
我们知道我们正在尝试开发一个游戏机器人:一个能够玩简单 Atari 游戏的程序。如果我们提供足够的时间和计算资源,那么它就能超越那些在玩某些游戏方面专家级的人类。我将列出一些著名的 Atari 游戏,以便你能看到我所说的游戏类型。你肯定玩过这些游戏中的至少一个。一些著名的 Atari 游戏包括 Casino、Space Invaders、Pac-man、Space War、Pong(乒乓球)等等。简而言之,我们试图解决的问题是如何构建一个能够学习玩 Atari 游戏的机器人?
在本章中,我们将使用已经内置的游戏环境,利用gym和dqn库。因此,我们不需要创建游戏视觉环境,我们可以专注于制作最佳游戏机器人的方法。首先,我们需要设置编码环境。
设置编码环境
在本节中,我们将介绍如何设置一个编码环境,以帮助我们实现我们的应用程序。我们需要安装 gym 库。以下是您可以遵循的步骤。我使用Ubuntu 16.04 LTS作为我的操作系统:
- 
第一步:通过执行以下命令从 GitHub 克隆 gym 仓库: $ sudo git clonehttps://github.com/openai/gym.git
- 
第二步:通过执行以下命令跳转到 gym 目录: $ cd gym
- 
第三步:执行以下命令安装 gym所需的最小数量库:$ sudo pip install -e
- 
第四步:通过执行以下命令安装 Atari 游戏的游戏环境: $ sudo pip install gym[atari]
- 
第五步:这一步是可选的。如果您想安装所有游戏环境,则可以执行以下命令: - 
$ sudo apt-get install -y python-numpy python-dev cmake zlib1g-dev libjpeg-dev xvfb libav-tools xorg-dev python-opengl libboost-all-dev libsdl2-dev swig
- 
$ sudo pip install gym[all]
 
- 
这就是您安装gym机器学习库的方法,我们将使用它来开发游戏机器人。我们将使用dqn库的 TensorFlow 实现,因此不需要单独安装dqn,但您当然可以参考这个安装说明:github.com/deepmind/dqn。
由于我们已经完成了环境设置,我们需要继续到下一个部分,这将帮助我们了解在开发游戏机器人时有用的技术。那么,让我们开始吧!
理解强化学习(RL)
在本章中,我们将借助强化学习技术制作游戏机器人。强化学习的动机很简单。RL 给机器或任何软件代理一个机会,根据该代理从环境中收到的反馈来学习其行为。这种行为可以一次性学习,或者您可以随着时间的推移不断适应。
让我们通过一个孩子学习说话的有趣例子来理解 RL。当孩子学习如何说话时,他们会采取以下步骤:
- 
第一步:首先,孩子开始观察您;您是如何说话的,以及您是如何与他或她互动的。孩子从您那里听基本单词和句子,并了解到他们也可以发出类似的声音。因此,孩子试图模仿您。 
- 
第 2 步:孩子想要说出完整的句子或单词,但他们可能不理解,甚至在说话句子之前,他们需要学习简单的单词!这是他们在尝试说话时遇到的挑战。现在孩子试图发出声音,有些声音听起来很滑稽或奇怪,但他们仍然决心说出单词和句子。 
- 
第 3 步:孩子面临的另一个挑战是他们需要理解并记住他们试图说出的单词背后的含义。但孩子设法克服了这个挑战,学会了说出他们最初的一些单词,这些单词非常简单,例如mama, papa, dadda, paa, maa等等。他们通过不断观察周围的环境来学习这项任务。 
- 
第 4 步:真正的挑战始于如何使用特定的单词,何时使用哪个单词,以及记住他们第一次听到的所有单词。尝试向孩子提供所有单词的意义以及他们需要使用的情境。听起来像是一项挑战性的任务,不是吗? 
对于一个孩子来说,这是一项困难的任务,但一旦开始理解语言并练习句子,它就会成为孩子生活的一部分。在 2-3 年内,孩子可能已经足够练习,可以轻松地开始互动。如果我们考虑我们自己说话,对于我们来说这是一项简单的任务,因为我们已经学会了足够关于如何在我们的环境中互动的知识。
现在,让我们尝试将这些点联系起来。借助前面的例子,我们将尝试理解强化学习(RL)的概念。给定例子的问题陈述是说话,其中孩子是代理,他们试图通过采取行动(在这里,行动是说话)来操纵环境(孩子首先说出哪个单词),并尝试说出一个单词或另一个单词。当孩子完成任务的子模块时,他们会得到奖励——比如说,一块巧克力——这意味着他们在一天内说出了一些单词,而如果他们无法说出任何东西,则不会得到巧克力。这是强化学习的简化描述。您可以参考以下图表:

图 11.1:强化学习基本概念的形象表示
所以基本上,强化学习(RL)允许机器和软件代理在特定任务或特定情境中自动确定理想和最佳的行为,以最大化软件代理的性能。代理需要简单的奖励反馈来学习其行为,这被称为强化信号。每次软件代理尝试采取导致其获得最大奖励的行动时,它都会尝试。最终,它学会了所有导致代理达到任务最优解的行动或移动,从而成为该领域的专家。强化学习的算法学会对环境做出反应。
为了构建游戏机器人,强化学习算法是完美的选择,这背后有原因。假设有许多带有随机回报的老丨虎丨机,你想要赢得最大金额。你该如何赢得最大金额?一个简单的方法是只选择一台机器,整天拉动它的杠杆,它可能会给你一些回报。如果你足够幸运,那么你可能会中大奖。为了尝试这种方法,你可能会损失一些钱。这种方法被称为纯利用方法。这不是一个最优的方法。
让我们采取另一种方法。在这个方法中,我们将拉动每台老丨虎丨机的杠杆,并祈祷至少有一台能够中大奖。这同样是一个简单的方法。在这个方法中,我们需要整天拉动杠杆。这种方法被称为纯探索方法。这种方法也不是最优的,因此我们需要在这两种方法之间找到一个适当的平衡,以获得最大的奖励。这被称为强化学习中的探索与利用的困境。现在我们需要解决这个问题。好吧,为了做到这一点,我们需要一个数学框架,可以帮助我们实现最优解,而这个数学方法就是马尔可夫决策过程(MDP)。让我们来探讨一下。
马尔可夫决策过程(MDP)
马尔可夫决策过程使用以下参数:
- 
状态集合,S 
- 
行动集合,A 
- 
奖励函数,R 
- 
策略,π 
- 
价值,V 
为了将一个状态转换到最终状态(S),我们必须采取一个动作(A)或一系列动作。我们将为每个采取的动作获得奖励(R)。我们的行动可以提供正奖励或负奖励。我们采取的行动集合定义了我们的策略(π)。执行每个行动后获得的奖励定义了我们的价值(V)。我们的目标是通过对策略的正确选择来最大化奖励。我们可以通过执行最佳可能的行动来实现这一点。从数学上讲,我们可以像以下截图所示那样表达这一点:

图 11.2:马尔可夫决策过程的数学表示
我们将应用前面的方程式来计算时间 t 时所有可能的状态值。我们有一组状态和动作。我们需要考虑这些状态、动作以及将智能体从一个状态转换到另一个状态的规则。当我们执行改变游戏智能体状态的行动时,智能体将因该行动而获得奖励。这个状态、行动和获得奖励的整个过程构成了马尔可夫决策过程(MDP)。一场游戏的一轮被认为是 MDP 的一个回合。这个过程包括有限的状态、动作和奖励序列。请看以下方程式,以表示这个过程::
S0, a0, r1, s1, a1, r2, s2, a2, r3 ,…,sn-1, an-1, rn, sn
在这里,s[i] 代表状态,ai 是动作,r[i+1] 是执行动作后我们将获得的奖励。sn 表示一个特定的场景以终端状态结束,这发生在“游戏结束”屏幕出现时。马尔可夫决策过程基于马尔可夫假设,下一个状态 s[i+1] 的概率取决于当前状态 s[i] 和执行的动作 ai,而不取决于前面的状态或动作。
折现未来奖励
从长远来看,如果我们想让我们的游戏代理做得好,那么我们需要考虑即时奖励,但我们也需要考虑我们的代理将获得的未来奖励。我们应该如何处理这种情况呢?嗯,答案在于折现未来奖励的概念。
给定一个 MDP 的运行,我们可以通过以下方程计算一个场景的总奖励:
R = r1 + r2 + r3 + … + rn
基于前面的方程,我们可以计算出从时间戳 t 开始的总未来奖励,这可以用给定的方程表示:
Rt = rt + rt+1 + rt+2 + rt+3 + … + rn
在这里,我们处理的是一个随机的游戏环境,我们无法确定我们是否会在执行相同的动作来玩特定游戏时获得相同的奖励。你考虑未来得越多,它就会越偏离。因此,我们最好使用折现未来奖励而不是总奖励:
Rt = rt+ γrt+1 + γ2rt+2+ … + γn-1 rn
在这里,γ 是折现系数。它的值在 0 到 1 之间。很容易理解,在特定的时间步 t 的折现未来奖励可以用当前状态的奖励加上时间步 t+1 的奖励来表示:
Rt = rt + γ (rt+1 + γ (rt+2 + …)) = rt + γRt+1
现在让我告诉你调整这个折现系数的实际意义:如果我们把折现系数 γ 的值设为 0,那么我们的游戏策略将是短视的,我们只是基于即时奖励来做游戏决策。我们需要在即时奖励和未来奖励之间找到一个平衡,所以我们应该把折现系数的值设为大于 0.7。
例如,我们可以将值设为 γ = 0.9。如果我们知道我们的游戏环境是确定性的,并且相同的动作总是带给我们相同的奖励,那么我们可以将折现系数 γ 的值设为 1。对于游戏代理来说,一个好的策略是始终选择一个最大化折现未来奖励的动作。
我们已经涵盖了强化学习的基础。从现在开始,我们将开始实现我们的游戏机器人。所以,让我们为一些乐趣做好准备!
基本 Atari 游戏机器人
在本章中,我们尝试了一种动手的方式来构建一些基本的游戏机器人。我们选择了几乎每个人在生命中某个时刻都玩过的著名雅达利游戏。我们选择雅达利游戏是因为我们知道如何玩它们,这使得我们的生活变得简单,因为我们能够理解我们的机器人应该执行什么样的动作,以便在一段时间内变得更好。
在本节中,我们将构建自己的游戏。这个游戏很简单,因此我们可以看看我们如何应用 Q-Learning 算法。在这里,我们将自己设计游戏世界。让我们开始吧!
理解关键概念
在本节中,我们将探讨许多重要的方面,这些方面将帮助我们进行编码,因此在这里,我们将涵盖以下主题:
- 
游戏规则 
- 
理解 Q-Learning 算法 
游戏规则
在我们开始基本概念或算法之前,我们需要了解我们正在构建的游戏规则。这个游戏很简单,很容易玩。这个游戏的规则如下:
- 
游戏规则: 游戏代理意味着一个黄色方块必须达到一个目标以结束游戏:它可以是绿色单元格或红色单元格。这意味着黄色方块应该到达绿色单元格或红色单元格。 
- 
奖励: 每一步都会给我们一个负奖励 -0.04。如果我们的游戏代理到达红色单元格,那么红色单元格会给我们一个负奖励 -1。如果我们的游戏代理到达绿色单元格,那么绿色单元格会给我们一个正奖励 +1。 
- 
状态: 每个单元格都是代理的一个状态,它需要找到其目标。 
- 
动作: 这个游戏只有四个动作:向上方向、向下方向、向右方向、向左方向。 
我们需要 tkinter 库来实现这个方法。我已经在这个 GitHub 链接中提供了如何安装它的描述:github.com/jalajthanaki/Q_learning_for_simple_atari_game/blob/master/README.md。
现在,让我们看看在本章构建游戏机器人时我们将使用的 Q 学习算法。
理解 Q-Learning 算法
这个算法最初由 DeepMind 在两篇论文中发表。第一篇论文在 2013 年 NIPS 上以 Playing Atari with Deep Reinforcement Learning 为标题发表。论文的链接是 arxiv.org/pdf/1312.5602.pdf。第二篇论文在 2015 年《自然》杂志上以 Human-level control through deep reinforcement Learning 为标题发表。这篇论文的链接是 www.davidqiu.com:8888/research/nature14236.pdf。你绝对应该阅读这些论文。我已经为你简化了这些论文的主要概念。
在 Q 学习中,我们需要定义一个Q(s, a)函数,它表示我们在状态s执行动作a时的折扣因子奖励,并从该点开始进行最优操作。你可以在下面的截图中看到帮助我们选择最大奖励的方程:

图 11.3:Q 函数的方程
我们可以将 Q(s, a)函数视为在特定状态s执行动作a后,在游戏结束时给我们提供最佳可能得分的函数。这个函数是 Q 函数,因为它表明了在特定状态下某个动作的质量。
让我来简化一下。假设你处于状态s,正在考虑是否应该执行动作a或b。你真的想以高分赢得游戏。所以,为了实现你的目标,你想要选择在游戏结束时给你带来最高得分的动作。如果你有这个 Q 函数,那么动作的选择就变得相当简单,因为你只需要选择具有最高 Q 值的动作。你可以在下面的截图中看到你可以使用的方程来获得最高的 Q 值:

图 11.4:使用 Q 函数选择最大奖励的方程
在这里,π代表策略。策略表明游戏的规则和动作。借助策略,我们可以选择在每个状态下可用的典型动作。我们的下一步是获取这个 Q 函数。为此,我们需要专注于仅一个转换。这个转换由四个状态组成:< s, a, r, s' >。记住折扣因子奖励,其中我们可以用下一个状态s'的 Q 值来表示当前状态s和当前动作a*的 Q 值。计算奖励的方程式在下面的截图提供:

图 11.5:计算奖励的 Bellman 方程
上述方程被称为 Bellman 方程,它是 Q 学习算法背后的主要思想。这个方程相当合理,它表明这个状态和动作的最大未来奖励是即时奖励和下一个状态的最大未来奖励的总和。
主要的直觉是,通过跟随迭代次数和近似步骤,我们可以生成 Q 函数的值。我们将通过使用Bellman 方程来实现这一点。在最简单的情况下,Q 函数以表格的形式实现,其中状态是其行,动作是其列。这个 Q 学习算法的伪步骤很简单。你可以看看它们,如下所示:
- 
第 1 步:任意初始化 Q [状态数量,动作数量] 
- 
第 2 步:观察初始状态 
- 
第 3 步:重复 Select and perform an action a Observe two things: reward r and new state s' Q [s, a] = Q [s, a] + α (r + γmaxa' Q [s', a'] - Q [s, a]) s = s'
- 
直到终止 
我们需要遵循以下步骤,其中α是学习率。学习率验证了先前 Q 值和新提出的 Q 值之间的差异。这个差异值被考虑在内,以便我们可以检查我们的模型何时会收敛。借助学习率,我们可以调节训练速度,使我们的模型不会变得太慢以至于无法收敛,或者太快以至于无法学习任何东西。我们将使用maxa'Q [s', a']来更新Q [s, a]以最大化奖励。这是我们唯一需要执行的操作。这个估计操作将给我们提供更新的 Q 值。在训练的早期阶段,当我们的智能体在学习时,可能会出现我们的估计完全错误的情况,但随着每一次迭代的进行,估计和更新的 Q 值会越来越准确。如果我们进行这个过程足够多次,那么 Q 函数将收敛。它代表了真实和优化的 Q 值。为了更好地理解,我们将实现前面的算法。请参考以下截图中的代码片段:

图 11.6:构建和更新 Q 表的代码片段
你可以在以下截图中以 Q 表的形式看到输出:

图 11.7:Q 表值
你可以通过参考以下 GitHub 链接来查看前面算法的实现:github.com/jalajthanaki/Q_learning_for_simple_atari_game/blob/master/Demo_Q_table.ipynb。
现在让我们开始实现游戏。
实现游戏机器人的基本版本
在本节中,我们将实现一个简单的游戏。我已经定义了这个游戏的规则。为了快速提醒你,我们的智能体,黄色方块试图到达红色方块或绿色方块。如果智能体到达绿色方块,我们将获得+1 作为奖励。如果它到达红色方块,我们得到-1。智能体每走一步都将被视为-0.04 的奖励。如果你想查看游戏的规则部分,可以翻回前面的页面。你可以通过参考这个 GitHub 链接来查看这个基本版本的游戏机器人代码:github.com/jalajthanaki/Q_learning_for_simple_atari_game。
对于这个游戏,游戏世界或游戏环境已经构建好了,所以我们不需要担心它。我们只需要通过导入语句来包含这个游戏世界。我们正在运行的脚本主程序是Lerner.py。该代码的代码片段如下所示:

图 11.8:游戏机器人基本版本的代码片段 - I
如前述代码所示,我们通过循环中给出的代码帮助跟踪智能体的状态和动作。之后,我们将定义这个游戏的四种可能动作,并基于此计算奖励值。我们还定义了max_Q函数,它为我们计算最大的 Q 值。您也可以参考以下截图:

图 11.9:游戏机器人基本版本代码片段 - II
如前述代码片段所示,辅助函数使用inc_Q方法来更新 Q 值。通过使用run函数,我们可以更新 Q 值,使我们的机器人学会如何找到最佳解决方案。您可以通过执行以下命令来运行此脚本:
$ python Learner.py
当您运行脚本时,您可以看到以下输出窗口,在 1-2 分钟内,这个机器人将找到最佳解决方案。您可以在以下截图中找到机器人的初始状态和最终状态输出:

图 11.10:游戏机器人基本版本的输出
您可以通过使用奖励分数来跟踪机器人的进度。您可以参考以下截图:

图 11.11:跟踪游戏机器人的进度
如您所见,在初始迭代中,游戏机器人在经过几次迭代后表现不佳,但随后开始根据其获得的经验学习如何采取行动。我们在奖励分数没有显著提高时停止了代码。这是因为我们的游戏机器人能够找到最佳解决方案。
现在,让我们构建一个更复杂的游戏机器人;我们将使用深度 Q 网络进行训练。那么,让我们开始吧。
构建太空侵略者游戏机器人
我们将构建一个能够玩太空侵略者的游戏机器人。你们大多数人可能都玩过这个游戏,或者至少听说过它。如果您没有玩过,或者现在想不起来,那么请看看以下截图:

图 11.12:太空侵略者游戏片段
希望您现在还记得这个游戏以及它是如何玩的。首先,我们将看看我们将用于构建这个版本游戏机器人的概念。让我们开始吧!
理解关键概念
在这个版本的游戏机器人中,我们将使用深度 Q 网络并训练我们的机器人。因此,在实现此算法之前,我们需要理解这些概念。看看以下概念:
- 
理解深度 Q 网络(DQN) 
- 
理解经验回放 
理解深度 Q 网络(DQN)
深度 Q 网络算法基本上是两个概念的组合。它使用深度神经网络中的 Q 学习逻辑。这就是为什么它被称为深度 Q 网络(DQN)。
每个游戏世界都有不同的环境。例如,超级马里奥与太空侵略者看起来不同。我们不能每次都为单个游戏提供整个游戏环境,因此首先我们需要决定所有游戏的通用表示,以便我们将它们作为 DQN 算法的输入。屏幕像素是明显的输入选择,因为它们显然包含了关于游戏世界及其状况的所有相关信息。没有屏幕像素的帮助,我们无法捕捉游戏代理的速度和方向。
如果我们将与 DeepMind 论文中提到的相同的前处理步骤应用于游戏屏幕,那么我们需要遵循以下步骤:
第 1 步:我们需要考虑游戏的最后四张屏幕图像作为输入。
第 2 步:我们需要将它们调整到 84 x 84 的大小,并将它们转换为 256 级灰度。这意味着我们将有 256^(84x84x4),大约是 10⁶⁷⁹⁷⁰种可能的游戏状态。这意味着在我们的想象中的 Q 表中将有 10⁶⁷⁹⁷⁰行,这是一个很大的数字。你可以争论说,许多像素组合或状态从未发生,所以我们可能将其表示为稀疏矩阵。这个稀疏矩阵只包含已访问的状态。然而,大多数状态很少被访问。因此,Q 表的收敛需要很长时间。坦白说,我们也希望为代理从未见过的状态提供一个好的 Q 值猜测,这样我们就可以为游戏代理生成一个合理的动作。这就是深度学习介入的地方。
第 3 步:神经网络非常适合为高度结构化的数据生成良好的特征。借助神经网络,我们可以表示我们的 Q 函数。这个神经网络将状态(即四个游戏屏幕和动作)作为输入,并生成相应的 Q 值作为输出。或者,我们也可以只将游戏屏幕作为输入,并为每个可能的动作生成 Q 值作为输出。 这种方法有一个很大的优点。让我解释一下。我们在这里做了两件主要的事情。首先,我们需要获得更新的 Q 值。其次,我们需要选择具有最高 Q 值的动作。
因此,如果我们有所有可能动作的 Q 值,那么我们可以很容易地更新 Q 值。我们也可以很轻松地选择具有最高 Q 值的动作。有趣的部分是,我们可以通过执行网络的前向传递来生成所有动作的 Q 值。经过单次前向传递,我们可以拥有所有可能动作的 Q 值列表。这次前向传递将节省大量时间,并为游戏代理提供良好的奖励。
DQN 架构
你可以在以下图表中找到深度 Q 网络的理想架构:

图 11.13:DQN 架构
上述架构已在 DeepMind 论文中使用并发表。神经网络架构在下面的屏幕截图中显示:

图 12.14:DQN 架构
提供的架构使用了一个经典的卷积神经网络(CNN)。它包含三个卷积层,随后是两个全连接层,这与我们在对象检测和面部识别 CNN 架构中看到的带有池化层的 CNN 架构相同。在这里,没有使用池化层。这是因为使用池化层的主要目的是使神经网络对位置不敏感。这意味着如果我们使用池化层,那么图像中对象的放置就不会被神经网络考虑。对于分类任务来说,这种位置不敏感性是有意义的,但对于游戏来说,游戏环境中对象的放置位置很重要。它们帮助我们确定动作以及潜在的奖励,我们不想丢弃这些信息。因此,我们在这里没有使用池化层。
DQN 算法的步骤
让我们看看 DQN 算法的步骤:
网络输入: 四个 84 x 84 的灰度游戏屏幕像素。
网络输出: 作为输出,我们将为每个可能的行为生成 Q 值。Q 值可以是任何实数,这意味着它可以是你能想象到的任何实数,这使得它成为一个回归任务。我们知道我们可以通过简单的平方误差损失来优化回归函数。误差损失的方程式在下面的屏幕截图中显示:

图 11.15:误差损失函数的方程式
Q 表更新步骤: 存在着过渡 < s, a, r, s' >,但这次,更新 Q 表的规则与 Q-learning 不同。有一些变化。因此,更新 Q 表的步骤如下:
- 
第 1 步:我们需要对当前状态 s 执行前向传播,以获得所有动作的预测 Q 值。 
- 
第 2 步:对下一个状态 s' 执行前向传播,并计算所有网络输出 maxa'Q(s', a'). 
- 
第 3 步:为动作 a 设置 Q 值目标为 r + γmaxa'Q(s', a')。在这里,我们可以使用我们在第 2 步中已经计算出的 maxa'Q(s', a') 值。对于所有其他动作,设置从第 1 步继承的 Q 值,使得那些输出的误差为零。 
- 
第 4 步:我们需要使用反向传播来更新神经网络的权重。 
现在我们来看一下经验回放的概念。
理解经验回放
我们正在使用两个概念来估计每个状态的未来奖励。我们使用 Q-learning,并使用卷积神经网络来近似 Q 函数。在这里,Q 值的近似是通过一个非线性函数完成的,而这个函数对于模型收敛来说并不非常稳定。因此,我们需要尝试各种超参数。这需要很长时间:在单个 GPU 上几乎需要一周的时间来训练游戏机器人。
我们将使用一个称为经验回放的概念。在训练过程中,所有经验 < s, a, r, s' > 都存储在回放记忆中。当我们进行训练时,网络将使用回放记忆中的随机样本而不是最近的转换。这样,训练时间会更短,而且还有一个优点。借助经验回放,我们的训练任务将更类似于常规的监督学习。现在我们可以轻松地对算法进行调试和测试操作。借助回放记忆,我们可以存储所有我们的游戏人类经验,然后根据这个数据集来训练模型。
因此,最终在 DQN 中使用的 Q-learning 算法的步骤如下。此算法来自原始的 DQN 论文,该论文可在arxiv.org/pdf/1312.5602.pdf找到:
- 
第 1 步:我们需要初始化回放记忆 D 
- 
第 2 步:我们需要用随机权重初始化动作值函数 Q 
- 
第 3 步:观察初始状态值 
- 
第 4 步:重复 Choose an action a with probability ε we need to select a random action otherwise we need to select a = argmaxa'Q(s,a') Perform action a Check reward r and new state s' store the gameplay experience <s, a, r, s'> in replay memory D sample random transitions <ss, aa, rr, ss'> from replay memory D calculate target for each minibatch transition if ss' is terminal state then tt = rr otherwise tt = rr + γmaxa'Q(ss', aa') We need to train the Q network using (tt - Q(ss, aa))² as loss s = s' until terminated
我们正在使用 Q-learning 和 DQN 来实现空间入侵者游戏机器人。所以,让我们开始编码。
实现空间入侵者游戏机器人
在本节中,我们将使用 DQN 和 Q-learning 来编写空间入侵者游戏。对于编码,我们将使用gym、TensorFlow和virtualenv库。你可以通过使用这个 GitHub 链接查看整个代码:github.com/jalajthanaki/SpaceInvaders_gamingbot。
我们正在使用卷积神经网络(CNN)。在这里,我们已经在单独的文件中定义了 CNN。这个文件的名称是convnet.py。请看下面的截图:以下图:

图 11.16:convnet.py的代码片段
你可以通过这个 GitHub 链接查看代码:github.com/jalajthanaki/SpaceInvaders_gamingbot/blob/master/convnet.py。
我们在dqn.py脚本中定义了 DQN 算法。你可以参考以下截图中的代码片段:

图 11.17:dqn.py的代码片段
对于训练,我们已经在train.py中定义了我们的训练逻辑。你可以参考以下截图中的代码片段:

图 11.18:train.py 的代码片段
最后,我们将所有这些独立的脚本导入到主atari.py脚本中,在该脚本中,我们定义了所有参数值。您可以参考以下截图中的代码片段:

图 11.19:atari.py 的代码片段
您可以通过执行以下命令开始训练:
$ python atari.py --game SpaceInvaders-v0 --display true
训练这个机器人以达到人类水平的表现至少需要 3-4 天的训练。我没有提供那么多的训练,但您肯定可以做到。您可以查看以下截图中的训练输出:以下图:

图 11.20:训练步骤的输出片段
您可以通过参考以下截图来查看游戏环境初始得分的代码片段:

图 11.21:游戏机器人前几场比赛得分的代码片段
要停止训练,将有两个参数:要么当我们的损失函数值在几次迭代中变为常数时结束训练,要么完成所有训练步骤。在这里,我们定义了 50,000 个训练步骤。您可以参考以下截图中的训练输出代码片段:

图 11.22:训练日志的代码片段
您可以通过查看以下截图来查看游戏机器人经过 1,000 次迭代后的得分:

图 11.23:经过 1,000 次迭代的游戏机器人的代码片段
我已经为您上传了预训练模型。您可以通过使用此 GitHub 链接下载它:github.com/jalajthanaki/SpaceInvaders_gamingbot/tree/master/model。
现在是时候构建乒乓球游戏的机器人了。如果您使用单个 GPU 训练这个机器人一周,它可以击败游戏制造商团队编写的 AI 规则。因此,我们的智能体肯定会比计算机智能体表现得更好。
构建乒乓球游戏机器人
在本节中,我们将探讨如何构建一个能够学习乒乓球游戏的游戏机器人。在我们开始之前,我们将查看构建乒乓球游戏机器人所使用的方法和概念。
理解关键概念
在本节中,我们将介绍构建乒乓球游戏机器人的一些方面,具体如下:
- 
游戏机器人的架构 
- 
游戏机器人的方法 
游戏机器人的架构
为了开发乒乓球游戏机器人,我们选择了一个基于神经网络的方案。我们神经网络的架构至关重要。让我们一步一步地查看架构组件:
- 
我们将游戏屏幕作为输入,并按照 DQN 算法对其进行预处理。 
- 
我们将预处理后的屏幕传递给一个神经网络 (NN)。 
- 
我们使用梯度下降来更新 NN 的权重。 
- 
权重 [1]: 此矩阵持有传递到隐藏层的像素权重。维度将是 [200 x 80 x 80] – [200 x 6400]。 
- 
权重 [2]: 此矩阵持有传递到输出的隐藏层权重。维度将是 [1 x 200]。 
您可以参考以下图表:

图 11.24:Pong 游戏机器人的神经网络架构
当我们看到这个游戏机器人的详细方法时,NN 的每个组件的任务更有意义。
游戏机器人的方法
为了构建 Pong 游戏机器人,我们将采用以下方法:
- 
对于实现,我们使用预处理后的图像向量,它是一个 [6400 x 1] 维度数组。 
- 
在神经网络 (NN) 的帮助下,我们可以计算向上移动的概率。 
- 
在那个概率分布的帮助下,我们将决定智能体是否向上移动。 
- 
如果游戏回合结束,这意味着游戏智能体以及对手都错过了球。在这种情况下,我们需要找出我们的游戏智能体是赢了还是输了。 
- 
当剧集结束时,这意味着如果任何一名玩家得分达到 21 分,我们需要传递结果。借助损失函数,我们可以找到错误值。我们应用梯度下降算法来找出我们的神经网络权重应该更新的方向。基于反向传播算法,我们将错误传播回网络,以便我们的网络可以更新权重。 
- 
一旦完成 10 个剧集,我们需要汇总梯度,然后,我们沿着梯度的方向更新权重。 
- 
重复此过程,直到我们的网络权重调整完毕,我们可以击败电脑。 
现在让我们来介绍编码步骤。
实现 Pong 游戏机器人
这些是我们需要遵循的实现步骤:
- 
参数初始化 
- 
以矩阵形式存储的权重 
- 
更新权重 
- 
如何移动智能体 
- 
使用神经网络理解过程 
您可以通过使用此 GitHub 链接来参考整个代码:github.com/jalajthanaki/Atari_Pong_gaming_bot。
参数初始化
首先,我们定义并初始化我们的参数:
- 
batch_size:此参数表示在更新我们网络的权重之前应该玩多少轮游戏。
- 
gamma:这是折扣因子。我们使用它来折扣游戏旧动作对最终结果的影响。
- 
decay_rate:此参数用于更新权重。
- 
num_hidden_layer_neurons:此参数表示我们应该在隐藏层中放置多少个神经元。
- 
学习率:这是我们游戏代理从结果中学习的速度,以便我们可以计算新的权重。学习率越高,我们对外部结果的反应越强烈,而学习率越低,我们对外部结果的反应就越不强烈。
您可以参考以下截图所示的代码片段:

图 11.25:参数初始化
以矩阵形式存储的权重
神经网络的权重以矩阵的形式存储。NN 的第一层是一个 200 x 6400 的矩阵,表示隐藏层的权重。如果我们使用符号w1_ij,那么这意味着我们正在表示第i^(th)个神经元在层 1 中对输入像素j的权重。第二层是一个 200 x 1 的矩阵,表示权重。这些权重是隐藏层的输出。对于层 2,元素w2_i表示放置在隐藏层第i^(th)个神经元激活上的权重。
您可以参考以下截图给出的代码片段:

图 11.26:权重矩阵
更新权重
为了更新权重,我们将使用 RMSprop。您可以参考以下论文以了解有关此函数的更多详细信息:
sebastianruder.com/optimizing-gradient-descent/index.html#rmsprop。请参考以下图示。

图 11.27:RMSprop 方程
代码显示在以下截图:

图 11.28:更新权重的代码片段
如何移动代理
在预处理输入的帮助下,我们将权重矩阵传递给神经网络。我们需要生成告诉代理向上移动的概率。您可以参考以下截图所示的代码片段:

图 11.29:移动代理的代码片段
我们已经完成了所有主要的辅助函数。我们需要将这些逻辑应用到神经网络中,以便它能够根据观察结果生成游戏代理向上移动的概率。
使用神经网络理解过程
这些步骤可以帮助我们生成代理向上移动的概率,以便它们可以决定何时向上移动
- 
我们需要通过将权重[1]和 observation_matrix之间的点积来计算隐藏层值。权重[1]是一个 200 x 6400 的矩阵,而observation_matrix是一个 6400 x 1 的矩阵。输出矩阵的维度是 200 x 1。在这里,我们使用了 200 个神经元。Q 函数的每一行代表一个神经元的输出。
- 
我们将对隐藏层值应用非线性函数 ReLU。 
- 
我们正在使用隐藏层的激活值来计算输出层的值。再次,我们在 hidden_layer_values[200 x 1]和权重[2] [1 x 200]之间执行点积。这个点积给我们一个单一值[1 x 1]。
- 
最后,我们将 sigmoid 函数应用于输出值。这将给出一个关于概率的答案。输出值介于 0 和 1 之间。 
你可以参考以下屏幕截图中的代码片段:

图 11.30:使用神经网络的过程代码片段
要运行此代码,你需要执行以下命令:
$ python me_Pong.py
如果你想要构建一个能够打败电脑的机器人,那么你至少需要在单个 GPU 上训练三天到四天。你可以在以下屏幕截图中参考机器人的输出:

图 11.31:Pong 游戏机器人的输出
你可以在以下屏幕截图中看到训练日志:

图 11.32:Pong 游戏机器人的训练日志
现在让我们仅作娱乐地构建一个游戏机器人。这个机器人使用 Flappy Bird 游戏环境。
仅作娱乐 - 实现 Flappy Bird 游戏机器人
在本节中,我们将构建 Flappy Bird 游戏机器人。这个游戏机器人是使用 DQN 构建的。你可以在以下 GitHub 链接中找到完整的代码:github.com/jalajthanaki/DQN_FlappyBird。
这个机器人有一个预训练的模型,所以你可以使用预训练的模型来测试它。为了运行这个机器人,你需要执行以下命令:
$ python deep_q_network.py
你可以在以下屏幕截图中看到输出:

图 11.33:Flappy Bird 游戏机器人的输出
你可以在这次实现中看到我们迄今为止所研究的所有概念的组合,所以请确保你探索这段代码。这将是本章的练习。
摘要
恭喜读者们;你们已经到达了本章的结尾!我们在这章中介绍了与强化学习相关的基本概念。你学习了构建游戏机器人的各种概念和算法。你还了解了深度 Q 学习算法是如何工作的。使用gym库,我们加载了游戏世界。通过使用dqn库,我们将能够训练模型。训练一个能够击败人类级别专家的游戏机器人需要花费大量时间。所以,我只训练了几个小时。如果你想训练更长的时间,你绝对可以做到。我们尝试构建了各种简单的 Atari 游戏,例如简单的路径寻找游戏机器人、太空侵略者、乒乓球和 Flappy Bird。你可以将这种基本方法扩展到更大的游戏环境中。如果你想更新自己并做出贡献,那么你可以查看 OpenAI 的 GitHub 仓库:github.com/openai。深度学习的新闻和博客部分可以在以下链接找到:deepmind.com/blog/。
在接下来的部分,你将找到一个附录,可以帮助你获得一些额外信息。这些额外信息将帮助你在构建机器学习(ML)应用或参加黑客马拉松或其他竞赛时。我还提供了一些可以帮助你在构建 ML 应用时的速查表。
附录 A. 速查表列表
这个附录主要帮助你获取我们在这本书中使用的不同 Python 库的速查表。除此之外,我已经为每个应用程序提供了安装 Python 依赖项的信息。你可以在每个应用程序的 README 部分找到这些安装相关信息。
那么,让我们跳转到相关的速查表信息。
速查表
我已经将所有速查表放在 GitHub 上,你可以随时参考,根据需要使用。以下是我包括的速查表:
- 
基本的 Linux 操作系统命令 
- 
数据科学领域的 Python 
- 
NumPy 
- 
scipy 
- 
pandas 
- 
flask 
- 
scikitlearn 
- 
TensorFlow 
- 
Keras 
- 
pyspark 
- 
重要的数学概念 
- 
可视化库:Bokeh 和 matplotlib 
- 
Git 命令 
如果你希望添加其他库的速查表,也可以提供宝贵的反馈。你可以通过加入这个 Slack 频道提出你的请求和问题:jalajthanaki.slack.com。
所有速查表都可以在这个 GitHub 链接中找到:github.com/jalajthanaki/cheat_sheets_of_python_libraries。
摘要
在这个附录中,我们看到了可以探索的速查表列表。每个章节的安装指南已经提供,可以在它们各自的 GitHub 仓库下的相应 README 文件中找到。除此之外,如果有人想要澄清疑问,他们可以加入提供的 Slack 频道。
在附录 B 中,我将涵盖如何制定有效的策略以赢得黑客马拉松的不同方面。我还想让你知道如何更新自己,因为目前,数据科学相关领域发展非常迅速。所以,大家继续阅读吧!
附录 B. 赢得黑客马拉松的策略
这个附录真的很有趣,因为在这里我们正在讨论两个重要的方面。这两个方面如下:
- 
赢得黑客马拉松的策略 
- 
保持更新 
那么,让我们开始吧。
赢得黑客马拉松的策略
如果你想赢得黑客马拉松或任何 Kaggle 竞赛,那么以下是一些需要记住的事情:
- 
一旦遇到竞赛,就尽快开始。 
- 
正确地进行探索性数据分析。这真的会帮助你构建一个好的解决方案。 
- 
尝试在前五次迭代中实现一个基线模型。 
- 
通过查看公开和私人排行榜来跟踪自己的进度以及他人的进度。 
- 
尝试不同的方法,并保持创新。 
- 
在论坛上提出合适的问题,但不要浪费太多时间。 
- 
如果你觉得为了赢得比赛你需要组建一个团队,那么就邀请其他成员加入你。 
- 
当你组建团队时,这很重要。你需要至少在比赛截止日期前 15 天组建团队,这样你的团队就可以一起工作,并提出最佳的方法。 
- 
团队成员的选择也起着重要的作用。你应该与那些技能互补的人组队,这样你们就可以利用彼此的知识。 
- 
理解团队成员的解决方案,并深入讨论方法。 
- 
你的团队成员应该了解彼此的优缺点,这样合作起来就更容易。 
- 
你的团队应该讨论机会——这意味着,你需要讨论其他可能的方法,以便在可能的情况下尝试它们。 
- 
如果你输掉了比赛,不要气馁。下次更加努力,并学习这次出了什么问题。列出那些出错的事情,下次你不会犯那样的错误。尝试改进这些事情。你肯定会赢。 
保持更新
数据科学和人工智能(AI)相关技术和应用发展迅速,因此你需要保持更新。更新自己的最佳方式是关注科技巨头的科研博客。社交媒体平台如 Twitter、LinkedIn 和 Slack 真的会帮助你。你必须知道该跟随谁,以便向他们提出合适的问题。成为你感兴趣的各个社区的成员。尝试探索一些 YouTube 频道。你还可以使用像 Udacity、Coursera 等教育平台。你还可以在 Quora 上做出贡献。成为开源项目的一部分。在 GitHub 上更新你的工作。你还应该阅读或订阅博客和新闻。以下是一些有助于你更新知识的博客:
摘要
在本附录中,我们讨论了一些可以帮助你理解与数据科学相关的黑客马拉松策略的技巧和窍门,并列出了一些真正有助于你的学习资源,以便你对人工智能领域的当前情况有一个大致的了解。记住,始终保持开放和好奇的心态去学习新事物,如果你知道些什么,那么就与他人分享你的知识。
第十二章:索引
A
- 
抽象摘要 - 关于 / 抽象摘要
 
- 
AdaBoost 算法 - 
关于 / AdaBoost 
- 
参考链接 / AdaBoost 
- 
参数 / AdaBoost 
 
- 
- 
算法,基线模型 - 
多项式朴素贝叶斯 / 多项式朴素贝叶斯 
- 
C-支持向量机分类,带核 rbf / C-支持向量机分类,带核 rbf 
- 
C-支持向量机分类,带核线性 / C-支持向量机分类,带核线性 
- 
线性支持向量机分类 / 线性支持向量机分类 
 
- 
- 
对齐 / 对齐 
- 
亚马逊评论数据集 - 
关于 / 理解亚马逊评论数据集 
- 
数据属性 / 理解亚马逊评论数据集 
 
- 
- 
apps.tsv - 
关于 / apps.tsv 
- 
用户 ID / apps.tsv 
- 
WindowsID / apps.tsv 
- 
分割 / apps.tsv 
- 
申请日期 / apps.tsv 
- 
职位 ID / apps.tsv 
 
- 
- 
曲线下面积 (AUC) / AUC 
- 
人工智能 (AI) - 参考链接 / 保持更新
 
- 
Atari 游戏机器人 - 关于 / 基本 Atari 游戏机器人
 
- 
属性,BX-Book-Ratings.csv - 
用户 ID / BX-Book-Ratings.csv 
- 
ISBN / BX-Book-Ratings.csv 
- 
书籍评分 / BX-Book-Ratings.csv 
 
- 
- 
属性,BX-Books.csv - 
ISBN / BX-Books.csv 
- 
书名 / BX-Books.csv 
- 
书籍作者 / BX-Books.csv 
- 
出版年份 / BX-Books.csv 
- 
出版社 / BX-Books.csv 
- 
图片 URL-S / BX-Books.csv 
- 
图片 URL-M / BX-Books.csv 
- 
图片 URL-L / BX-Books.csv 
 
- 
- 
属性,BX-Users.csv - 
用户 ID / BX-Users.csv 
- 
位置 / BX-Users.csv 
- 
年龄 / BX-Users.csv 
 
- 
- 
属性,异常值 - 
不保证线的循环利用 / 不保证线的循环利用 
- 
年龄 / 年龄, 30-59 天逾期次数不更糟 
- 
债务比率 / 债务比率 
- 
月收入 / 月收入, 开放信用额度贷款的属性数量,异常月收入 
- 
数据值频率,分析 / 90 天逾期次数, 60-89 天逾期但未恶化次数 
- 
值的频率 / 房地产贷款或信用额度数量 
- 
数据点,频率值 / 受抚养人数 
 
- 
B
- 
bAbI 数据集,聊天机器人开发 - 
关于 / The bAbI dataset 
- 
下载链接 / The bAbI dataset 
- 
(20) QA bAbI 任务 / The (20) QA bAbI tasks QA bAbI tasks") 
 
- 
- 
基线方法 - 
问题 / 现有方法的问题, 基线方法的问题, 现有方法的问题 
- 
优化 / 优化现有方法, 优化基线方法, 优化基线方法, 如何优化现有方法 
- 
关键概念,用于优化 / 理解优化方法的关键概念 
- 
构建 / 构建基线方法, 构建基线方法 
- 
实现 / 实现基线方法, 实现基线方法, 实现最佳方法 
- 
数据准备 / 数据准备 
- 
探索性数据分析 (EDA) / 探索性数据分析 (EDA)") 
- 
客户类别,生成 / 生成客户类别 
- 
客户分类 / 分类客户 
- 
客户,分类 / 分类客户 
- 
测试矩阵 / 理解测试矩阵 
- 
结果,测试 / 测试基准方法的结果, 测试基准方法的结果 
- 
准确率分数,为分类器生成 / 为分类器生成准确率分数 
- 
混淆矩阵,为分类器生成 / 为分类器生成混淆矩阵 
- 
学习曲线,为分类器生成 / 为分类器生成学习曲线 
- 
基本概念 / 理解基本概念 
- 
推荐系统,架构 / 推荐系统架构 
- 
步骤,实现 / 实现基准方法的步骤 
- 
数据集,加载 / 加载数据集, 加载数据集 
- 
特征生成,使用 TF-IDF / 使用 TF-IDF 生成特征 
- 
余弦相似度矩阵,构建 / 构建余弦相似度矩阵 
- 
预测,生成 / 生成预测 
- 
关键概念,优化 / 理解优化方法的关键概念 
- 
关于 / 最佳方法 
- 
GloVe 模型,加载 / 加载 GloVe 模型 
- 
预处理 / 预处理 
- 
预计算 ID 矩阵,加载 / 加载预计算的 ID 矩阵 
- 
训练集和测试集,分割 / 分割训练集和测试集 
- 
神经网络,构建 / 构建神经网络 
- 
神经网络,训练 / 训练神经网络 
- 
训练好的模型,加载 / 加载训练好的模型 
- 
训练好的模型,测试 / 测试训练好的模型 
 
- 
- 
基准方法,职位推荐引擎 - 
构建 / 构建基准方法 
- 
实现 / 实现基准方法 
- 
常量,定义 / 定义常量 
- 
数据集,加载 / 加载数据集 
- 
辅助函数,定义 / 定义辅助函数 
- 
TF-IDF 向量,生成 / 生成 TF-IDF 向量和余弦相似度 
- 
测试矩阵 / 理解测试矩阵 
- 
缺陷 / 基线方法的问题 
- 
优化 / 优化基线方法 
 
- 
- 
基线方法,摘要应用 - 
构建 / 构建基线方法 
- 
实现 / 实现基线方法 
- 
Python 依赖项,安装 / 安装 Python 依赖项 
- 
代码,用于生成摘要 / 编写代码并生成摘要 
- 
缺陷 / 基线方法的问题 
- 
优化 / 优化基线方法 
 
- 
- 
基线模型 - 
特征工程 / 为基线模型进行特征工程, 找出特征重要性, 为基线模型进行特征工程 
- 
训练 / 训练基线模型, 训练基线模型, 训练基线模型 
- 
测试 / 测试基线模型, 测试基线模型, 测试基线模型 
- 
训练,分割 / 分割训练和测试数据集 
- 
数据集,测试 / 分割训练和测试数据集 
- 
预测标签,分割用于训练 / 分割预测标签用于训练和测试数据集 
- 
预测标签,分割用于测试数据集 / 分割预测标签用于训练和测试数据集 
- 
情感分数,转换为 numpy 数组 / 将情感分数转换为 numpy 数组 
- 
机器学习模型,训练 / 机器学习模型的训练 
- 
输出,生成 / 生成和解释输出 
- 
输出,解释 / 生成和解释输出 
- 
准确度分数,生成 / 生成准确度分数 
- 
输出,可视化 / 可视化输出 
- 
训练和测试数据集,构建 / 构建基线模型的训练和测试数据集 
- 
实现 / 实现基线模型 
- 
多项式朴素贝叶斯,测试 / 多项式朴素贝叶斯的测试 
- 
SVM,使用 rbf 核进行测试 / 使用 rbf 核测试 SVM 
- 
SVM,使用线性核进行测试 / 使用线性核测试 SVM 
- 
SVM,使用 linearSVC 进行测试 / 使用 linearSVC 测试 SVM 
 
- 
- 
基线模型,实时物体识别应用 - 
特征工程 / 基线模型的特征工程 
- 
构建 / 构建基线模型 
- 
测试 / 测试基线模型 
- 
缺陷 / 现有方法的缺陷 
- 
优化 / 如何优化现有方法 
- 
优化过程 / 理解优化过程 
 
- 
- 
基本概念,基线方法 - 基于内容的方法 / 理解基于内容的方法
 
- 
基本版本,聊天机器人开发 - 
关于 / 构建聊天机器人的基本版本 
- 
基于规则的系统 / 为什么基于规则的系统会工作?, 理解基于规则的系统 
- 
实现 / 理解方法 
- 
问题,列出 / 列出可能的问题和答案 
- 
答案,列出 / 列出可能的问题和答案 
- 
标准消息,决定 / 决定标准消息 
- 
架构 / 理解架构 
- 
缺陷 / 现有方法的缺陷 
- 
优化,理解关键概念 / 理解优化方法的关键概念 
- 
seq2seq 模型 / 理解 seq2seq 模型 
 
- 
- 
游戏机器人的基本版本 - 实施基本版本的游戏机器人 / 实施游戏机器人的基本版本
 
- 
最佳方法 - 
关于 / 最佳方法 
- 
实施最佳方法 / 实施最佳方法 
 
- 
- 
最佳方法,聊天机器人开发 - 
实施最佳方法 / 实施最佳方法 
- 
随机测试模式 / 随机测试模式 
- 
用户交互测试模式 / 用户交互测试模式 
 
- 
- 
最佳方法,职位推荐引擎 - 
关于 / 最佳方法 
- 
实施最佳方法 / 实施最佳方法 
- 
数据集,过滤 / 过滤数据集 
- 
准备训练数据集 / 准备训练数据集 
- 
连接操作,应用 / 应用连接操作 
- 
TF-IDF 和余弦相似度分数,生成 / 生成 TF-IDF 和余弦相似度分数 
- 
推荐生成 / 生成推荐 
 
- 
- 
最佳方法,实时物体识别应用 - 
实施最佳方法 / 实施最佳方法 
- 
YOLO / 理解 YOLO 
- 
实施 Darkflow,使用 / 使用 YOLO 实施最佳方法 
- 
实施 Darknet,使用 / 使用 Darknet 进行实施 
- 
实施 Darkflow,使用 / 使用 Darkflow 进行实施 
 
- 
- 
最佳方法,摘要应用 - 
构建 / 最佳方法背后的理念, 最佳方法 
- 
实施最佳方法 / 实施最佳方法 
- 
项目结构 / 理解项目结构 
- 
辅助函数 / 理解辅助函数 
- 
摘要,生成 / 生成摘要 
 
- 
- 
Book-Crossing 数据集 - 
关于 / Book-Crossing 数据集 
- 
参考链接 / Book-Crossing 数据集 
- 
BX-Book-Ratings.csv / BX-Book-Ratings.csv 
- 
BX-Books.csv / BX-Books.csv 
 
- 
- 
BX-Book-Ratings.csv / BX-Book-Ratings.csv 
- 
BX-Books.csv / BX-Books.csv 
- 
BX-Users.csv / BX-Users.csv 
C
- 
CAS-PEAL Face Dataset - 
关于 / CAS-PEAL Face Dataset 
- 
URL,用于下载 / CAS-PEAL Face Dataset 
 
- 
- 
聊天机器人开发 - 
问题陈述 / 介绍问题陈述 
- 
基于检索的方法 / 基于检索的方法 
- 
基于生成的方法 / 基于生成的方法 
- 
开放域 / 开放域 
- 
闭域 / 闭域 
- 
简短对话 / 简短对话 
- 
长对话 / 长对话 
- 
开放域,基于生成的方法 / 开放域和基于生成的方法 
- 
开放域,基于检索的方法 / 开放域和基于检索的方法 
- 
闭域,基于检索的方法 / 闭域和基于检索的方法 
- 
闭域,基于生成的方法 / 闭域和基于生成的方法 
- 
数据集 / 理解数据集 
- 
基本版本,构建 / 构建聊天机器人的基本版本 
- 
修订方法,实现 / 实现修订方法 
- 
关键概念,用于解决现有问题 / 理解关键概念以解决现有问题 
- 
记忆网络 / 记忆网络 
- 
最佳方法 / 最佳方法 
- 
混合方法 / 讨论混合方法 
 
- 
- 
分类器模型 - 
生成 / 最佳方法 
- 
实现 / 实现最佳方法 
 
- 
- 
闭域聊天机器人 - 开发 / 闭域
 
- 
基于 CNN 的方法 - 实现 / 实现基于 CNN 的方法
 
- 
COCO 数据集 - 
关于 / COCO 数据集 
- 
URL / COCO 数据集 
 
- 
- 
编码环境 - 
设置 / 设置编码环境, 设置编码环境 
- 
dlib,安装 / 安装 dlib 
- 
人脸识别,安装 / 安装 face_recognition 
 
- 
- 
协同过滤 - 
关于 / 协同过滤 
- 
基于记忆的协同过滤 / 基于记忆的协同过滤 
- 
基于模型的协同过滤 / 基于模型的协同过滤 
 
- 
- 
概念和方法,基于逻辑回归的方法 - 
关于 / 理解概念和方法, 基于逻辑回归的方法 
- 
基于对齐的方法 / 基于对齐的方法 
- 
基于平滑的方法 / 基于平滑的方法 
 
- 
- 
混淆矩阵 / 混淆矩阵 
- 
消费者 - 类型 / 介绍客户细分
 
- 
上下文向量 / 理解 seq2seq 模型 
- 
卷积神经网络 - 构建 / 构建卷积神经网络
 
- 
卷积神经网络 (CNN) - 
关于 / 用于人脸识别的卷积神经网络 (CNN) for FR") 
- 
简单 CNN 架构 / 简单 CNN 架构 
- 
工作,用于人脸识别 / 理解 CNN 在人脸识别中的应用 
 
- 
- 
卷积神经网络 (CNN) / 为什么我们应该使用预训练模型? 
- 
卷积神经网络 (CNN) / 如何优化现有方法 
- 
Cornell Movie-Dialogs 数据集,聊天机器人开发 - 
关于 / Cornell Movie-Dialogs 数据集 
- 
下载链接 / Cornell Movie-Dialogs 数据集 
- 
movie_conversations.txt 的内容 / movie_conversations.txt 的内容详情 
- 
movie_lines.txt 的内容 / movie_lines.txt 的内容详情 
 
- 
- 
相关性 / 相关性 
- 
交叉验证 - 
关于 / 交叉验证 
- 
技术 / 交叉验证 
- 
使用 / 使用 CV 的方法 
- 
优点 / 使用简历的方法 
- 
缺点 / 使用简历的方法 
 
- 
- 
基于交叉验证的方法 - 实现 / 实现基于交叉验证的方法
 
- 
客户类别 - 
生成 / 生成客户类别, 生成客户类别 
- 
数据格式化 / 格式化数据 
- 
创建 / 创建客户类别 
- 
数据编码 / 数据编码 
- 
PCA 分析 / PCA 分析 
- 
聚类分析,使用轮廓分数 / 使用轮廓分数分析聚类 
 
- 
- 
客户分类 - 
关于 / 分类客户 
- 
辅助函数,定义 / 定义辅助函数 
- 
数据,分为训练集 / 将数据分为训练集和测试集 
- 
数据,分为测试集 / 将数据分为训练集和测试集 
- 
机器学习(ML)算法,实现 / 实现机器学习(ML)算法 
 
- 
- 
客户细分 / 介绍客户细分 - 
问题陈述 / 介绍问题陈述 
- 
参考 / 构建基线方法 
- 
用于领域 / 各种领域的客户细分 
 
- 
- 
Cython - 安装 / 安装 Cython
 
D
- 
Darkflow - 使用,用于 YOLO 实现 / 使用 Darkflow 实现
 
- 
Darknet - 使用,用于 YOLO 实现 / 使用 Darknet 实现
 
- 
数据 - 
过滤,基于地理位置 / 基于地理位置的数据过滤 
- 
加载,使用 dataset_loader 脚本 / 使用 dataset_loader 脚本加载数据 
 
- 
- 
数据分析 - 
关于 / 数据分析 
- 
数据预处理 / 数据预处理, 基本数据分析随后进行数据预处理 
- 
统计属性,列出 / 列出统计属性 
- 
缺失值,查找 / 查找缺失值 
- 
缺失值,替换 / 替换缺失值 
- 
相关性 / 相关性 
- 
异常值检测 / 检测异常值 
- 
异常值,处理 / 处理异常值/ 数据预处理和数据分析 
 
- 
- 
数据属性,纽约时报新闻文章数据集 - 
type_of_material / 理解纽约时报新闻文章数据集 
- 
标题 / 理解纽约时报新闻文章数据集 
- 
pub_date / 理解纽约时报新闻文章数据集 
- 
section_name / 理解纽约时报新闻文章数据集 
 
- 
- 
数据属性,探索性数据分析(EDA) - 
国家 / 国家 
- 
客户和产品 / 客户和产品 
- 
客户和产品 / 客户和产品 
- 
产品类别 / 产品类别 
 
- 
- 
数据格式化 - 
关于 / 格式化数据 
- 
产品,分组 / 分组产品 
- 
数据集,拆分 / 拆分数据集 
- 
订单,分组 / 分组订单 
 
- 
- 
数据帧 - 合并 / 合并数据帧
 
- 
数据准备 - 关于 / 数据准备
 
- 
数据预处理 - 
关于 / 数据预处理 
- 
变更 / 第一次变更, 第二次变更 
- 
变更,实施 / 实施变更/ 数据预处理和数据分析 
 
- 
- 
数据科学 - 参考链接 / 保持更新
 
- 
数据集 - 
关于 / 理解数据集 , 理解数据集, 理解数据集, 理解数据集 
- 
参考链接 / 理解数据集 
- 
属性 / 理解数据集的属性, 数据集的属性 
- 
收集 / 收集数据集 
- 
DJIA 指数价格,收集 / 收集 DJIA 指数价格 
- 
新闻文章,收集 / 收集新闻文章 
- 
加载 / 加载数据集, 加载数据集, 加载数据集 
- 
描述 / 数据集描述 
- 
下载 / 下载数据集 
- 
内容 / 理解数据集的内容 
- 
训练文件夹 / 训练文件夹 
- 
测试文件夹 / 测试文件夹 
- 
imdb.vocab 文件 / imdb.vocab 文件 
- 
imdbEr.txt 文件 / imdbEr.txt 文件 
- 
README 文件 / README 
- 
评论文件,内容 / 理解电影评论文件的内容 
- 
用于人脸情感识别应用 / 理解人脸情感识别数据集 
 
- 
- 
数据集,人脸识别 - CAS-PEAL 人脸数据集 / CAS-PEAL 人脸数据集
 
- 
数据集,实时物体识别应用 - 
关于 / 理解数据集 
- 
COCO 数据集 / COCO 数据集 
- 
PASCAL VOC 数据集 / PASCAL VOC 数据集 
 
- 
- 
数据集 - 
关于 / 理解数据集 
- 
电子商务商品数据 / 电子商务商品数据 
- 
Book-Crossing 数据集 / Book-Crossing 数据集 
 
- 
- 
数据集,聊天机器人开发 - 
关于 / 理解数据集 
- 
康奈尔电影对话数据集 / 康奈尔电影对话数据集 
- 
bAbI 数据集 / bAbI 数据集 
 
- 
- 
数据集,职业推荐引擎 - 
关于 / 理解数据集 
- 
爬取数据集 / 爬取数据集 
- 
职业推荐挑战数据集 / 职业推荐挑战数据集 
 
- 
- 
数据集,摘要应用 - 
关于 / 理解数据集 
- 
获取挑战 / 获取数据集的挑战 
- 
医疗转录数据集 / 理解医疗转录数据集 
- 
亚马逊评论数据集 / 理解亚马逊评论数据集 
 
- 
- 
数据集训练 - 参考链接 / 理解电影评论文件的内容
 
- 
解码器 / 理解 seq2seq 模型 
- 
深度学习 (DL) 算法 / 现有方法的局限性 
- 
深度学习 (DL) 技术 / 理解优化方法的关键概念 
- 
深度 Q 网络 (DQN) / 理解深度 Q 网络 (DQN)") - 
架构 / DQN 的架构 
- 
算法 / DQN 算法的步骤 
 
- 
- 
折现未来奖励 / 折现未来奖励 
- 
DJIA 数据集 / 理解 DJIA 数据集 - 
准备 / 准备 DJIA 训练数据集 
- 
数据分析 / DJIA 数据集的基本数据分析 
 
- 
- 
dlib - 安装 / 安装 dlib
 
- 
道琼斯工业平均指数 (DJIA) / 收集数据集 
- 
Dropout - 关于 / 我们如何使用预训练模型?
 
- 
动态内存网络 (DMN) - 
关于 / 动态内存网络 (DMN)") 
- 
架构 / 动态内存网络 (DMN)") 
- 
语义记忆 / 动态内存网络 (DMN)"), 情景记忆 
- 
情景记忆 / 动态内存网络 (DMN)"), 情景记忆 
- 
模块 / 动态内存网络 (DMN)") 
- 
输入模块 / 输入模块 
- 
问题模块 / 问题模块 
 
- 
E
- 
电子商务商品数据 - 参考链接 / 电子商务商品数据
 
- 
电子商务商品数据 - 关于 / 电子商务商品数据
 
- 
编码器 / 理解 seq2seq 模型 
- 
现有方法 - 
问题,探索 / 探索现有方法的局限性 
- 
问题 / 现有方法的局限性 
- 
优化 / 如何优化现有方法 
- 
流程,优化 / 理解优化流程 
 
- 
- 
探索性数据分析 (EDA) / 基本数据分析随后进行数据预处理 - 
关于 / 探索性数据分析 (EDA)") 
- 
空数据条目,移除 / 移除空数据条目 
- 
重复数据条目,移除 / 移除重复数据条目 
- 
数据属性 / 针对各种数据属性的 EDA 
 
- 
- 
探索性数据分析 (EDA) / 实施修订的方法 - 关于 / 探索性数据分析
 
- 
指数加权移动平均 (EWMA) / 基于平滑的方法 
- 
提取式摘要 - 关于 / 提取式摘要
 
F
- 
F1 分数 / F1 分数 
- 
人脸情感识别应用 / 人脸情感识别应用 - 
数据集 / 理解人脸情感识别数据集 
- 
概念 / 理解人脸情感识别概念 
- 
卷积层 / 理解卷积层 
- 
ReLU 层 / 理解 ReLU 层 
- 
池化层 / 理解池化层 
- 
完全连接层 / 理解完全连接层 
- 
SoftMax 层 / 理解 SoftMax 层 
- 
基于反向传播的权重更新 / 基于反向传播更新权重 
 
- 
- 
人脸情感识别模型 - 
构建 / 构建人脸情感识别模型 
- 
数据,准备 / 准备数据 
- 
数据,加载 / 加载数据 
- 
模型,训练 / 训练模型 
 
- 
- 
人脸识别 - 
概念 / 理解人脸识别概念 
- 
数据集 / 理解人脸识别数据集 
- 
Labeled Faces in Wild / Labeled Faces in the Wild 
- 
算法 / 人脸识别的算法 
- 
方向梯度直方图 (HOG) / 方向梯度直方图 (HOG)") 
- 
卷积神经网络 (CNN) / 卷积神经网络 (CNN) 用于 FR 用于 FR") 
- 
实现,方法 / 实现人脸识别的方法 
- 
基于 HOG 的方法,实现 / 实现基于 HOG 的方法 
- 
基于 CNN 的方法,实现 / 实现基于 CNN 的方法 
- 
实时人脸识别,实现 / 实现实时人脸识别 
 
- 
- 
人脸识别应用 / 人脸识别应用 - 训练 / 为 FER 应用进行训练
 
- 
人脸识别 - 安装 / 安装 face_recognition
 
- 
假阴性 (FN) / 理解测试矩阵 
- 
假阳性 (FP) / 理解测试矩阵 
- 
特征工程 - 
关于 / 基于生成的方法, 基于生成的方法 
- 
数据集,加载 / 加载数据集 
- 
微量预处理 / 微量预处理 
- 
调整后的收盘价,转换为整数格式 / 将调整后的收盘价转换为整数格式 
- 
NYTimes 新闻文章,情感分析 / NYTimes 新闻文章的情感分析 
 
- 
- 
特征工程,对于基线模型 - 关于 / 基线模型的特征工程
 
- 
FER2013 数据集 - 参考链接 / 理解人脸情感识别的数据集
 
- 
Flappy Bird 游戏机器人 - 实现 / Just for fun - implementing the Flappy Bird gaming bot
 
G
- 
gather_dataset() 函数 - 关于 / 生成问答对
 
- 
基于生成的方法,聊天机器人 - 关于 / 基于生成的方法
 
- 
get_conversations() 函数 - 关于 / 生成问答对
 
- 
get_id2line() 函数 - about / 生成问答对
 
- 
get_summarized 函数 - about / get_summarized 函数
 
- 
梯度提升 - 
about / 梯度提升 
- 
参数 / 梯度提升 
 
- 
- 
网格搜索参数调整 / 网格搜索参数调整 
H
- 
黑客马拉松 - 策略 / 赢得黑客马拉松的策略, 保持更新
 
- 
辅助函数,摘要应用 - 
Normalization.py / Normalization.py 
- 
Utils.py / Utils.py 
 
- 
- 
原点梯度直方图 (HOG) / 原点梯度直方图 (HOG)") 
- 
基于 HOG 的方法 - 实现 / 实现基于 HOG 的方法
 
- 
混合方法,聊天机器人开发 - about / 讨论混合方法
 
- 
超参数 - 
调整 / 超参数调整 
- 
网格搜索参数调整 / 网格搜索参数调整 
- 
随机搜索参数调整 / 随机搜索参数调整 
 
- 
- 
超参数 - 
训练轮数 / 实现最佳方法 
- 
批处理大小 / 实现最佳方法 
- 
lstm_size / 实现最佳方法 
 
- 
- 
超参数调整 - 实现 / 实现超参数调整
 
I
- 
IMDb 数据集 - URL,用于下载 / 理解数据集
 
- 
意义交并比 (IoU) - about / 意义交并比 (IoU)")
 
- 
问题,基线方法 - 
about / 基线方法的局限性 
- 
有限内容分析 / 基线方法的局限性 
- 
过度专业化 / 基线方法的局限性 
- 
新用户 / 基线方法的局限性 
 
- 
J
- 
职位推荐挑战数据集 - 
about / 职位推荐挑战数据集 
- 
apps.tsv / apps.tsv 
- 
users.tsv / users.tsv 
- 
jobs.tsv / Jobs.zip 
- 
user_history.tsv / user_history.tsv 
 
- 
- 
职位推荐引擎 - 
问题陈述 / 介绍问题陈述 
- 
数据集 / 理解数据集 
- 
基准方法,构建 / 构建基准方法 
- 
修订方法,构建 / 构建修订方法 
- 
最佳方法,构建 / 最佳方法 
 
- 
- 
jobs.tsv - 
关于 / Jobs.zip 
- 
职位 ID / Jobs.zip 
- 
WindowID / Jobs.zip 
- 
标题 / Jobs.zip 
- 
描述 / Jobs.zip 
- 
要求 / Jobs.zip 
- 
城市 / Jobs.zip 
- 
状态 / Jobs.zip 
- 
国家 / Jobs.zip 
- 
Zip5 / Jobs.zip 
- 
开始日期 / Jobs.zip 
- 
结束日期 / Jobs.zip 
 
- 
- 
JSON 属性 - 
当前表单操作 / 理解架构 
- 
机器人消息 / 理解架构 
- 
人类消息 / 理解架构 
- 
下一个字段类型 / 理解架构 
- 
下一个表单操作 / 理解架构 
- 
占位符文本 / 理解架构 
- 
前一个字段类型 / 理解架构 
- 
前一个表单操作 / 理解架构 
- 
建议消息 / 理解架构 
 
- 
K
- 
K-最近邻(KNN) - 
关于 / K 最近邻(KNN)") 
- 
参数 / K 最近邻(KNN)") 
 
- 
- 
K 最近邻(KNN)算法 - 
关于 / 项目-项目协同过滤 
- 
参考链接 / 项目-项目协同过滤 
- 
应用 / 应用 KNN 算法 
- 
使用 / 使用 KNN 算法进行推荐 
 
- 
- 
关键概念,Atari 游戏机器人 - 
关于 / 理解关键概念 
- 
游戏规则 / 游戏规则 
- 
Q 学习算法 / 理解 Q 学习算法 
 
- 
- 
关键概念,基准方法 - 
交叉验证 / 交叉验证 
- 
超参数调整 / 超参数调整 
 
- 
- 
关键概念,Pong 游戏机器人 - 
关于 / 理解关键概念 
- 
架构 / 游戏机器人的架构 
- 
方法 / 游戏机器人的方法 
 
- 
- 
关键概念,推荐引擎 - 
协同过滤 / 协同过滤 
- 
数据集,加载 / 加载数据集 
- 
数据框,合并 / 合并数据框 
- 
EDA,针对合并后的数据框 / 合并数据框的 EDA 
- 
数据,基于地理位置过滤 / 基于地理位置过滤数据 
- 
K 最近邻(KNN)算法,应用 / 应用 KNN 算法 
- 
K 最近邻(KNN)算法,使用 / 使用 KNN 算法进行推荐 
- 
矩阵分解,应用 / 应用矩阵分解 
- 
矩阵分解,使用 / 使用矩阵分解进行推荐 
 
- 
- 
关键概念,Space Invaders 游戏机器人 - 
关于 / 理解关键概念 
- 
深度 Q 网络(DQN) / 理解深度 Q 网络(DQN) 
- 
经验回放 / 理解经验回放 
 
- 
L
- 
学习曲线 / 学习曲线 
- 
相关性逻辑 - 
实现,针对推荐引擎 / 实现推荐引擎的相关逻辑 
- 
书籍评分,推荐 / 基于书籍评分的推荐 
- 
相关性,推荐 / 基于相关性的推荐 
 
- 
- 
逻辑回归 - 
关于 / 逻辑回归 
- 
参数 / 逻辑回归 
 
- 
- 
特征的对数变换 / 特征的对数变换 
- 
长短期记忆单元(LSTMs) - 参考链接 / 构建神经网络
 
- 
长短期记忆单元(LSTMs);关于 / 构建神经网络 
- 
长对话,聊天机器人开发 - 关于 / 长对话
 
- 
长短期记忆 (LSTM) 循环神经网络 / 理解 seq2seq 模型 
- 
损失 - 关于 / 损失
 
- 
LSA 算法 - 关于 / LSA 算法
 
- 
lsa_text_summarizer 函数 - 
关于 / 生成摘要 
- 
使用 / 生成摘要 
 
- 
M
- 
机器学习算法 - 选择 / 选择机器学习算法, 选择机器学习算法
 
- 
机器学习算法,实时物体识别应用 - 选择 / 选择机器学习算法
 
- 
机器学习算法 - 
选择 / 选择机器学习算法 
- 
K-最近邻 (KNN) / K-最近邻 (KNN)") 
- 
逻辑回归 / 逻辑回归 
- 
AdaBoost 算法 / AdaBoost 
- 
GradientBoosting / GradientBoosting 
- 
RandomForest / RandomForest 
 
- 
- 
马尔可夫决策过程 (MDP) - 
关于 / 马尔可夫决策过程 (MDP)") 
- 
参数 / 马尔可夫决策过程 (MDP)") 
 
- 
- 
矩阵分解 - 
应用 / 应用矩阵分解 
- 
使用 / 使用矩阵分解进行推荐 
 
- 
- 
平均精度 (mAP) - 关于 / 平均精度 (mAP)")
 
- 
医学转录数据集 - 
关于 / 理解医学转录数据集 
- 
主要症状 / 理解医学转录数据集 
- 
病人疾病史 / 理解医学转录数据集 
- 
过去病史 / 理解医学转录数据集 
- 
过去手术史 / 理解医学转录数据集 
- 
家族病史 / 理解医学转录数据集 
- 
药物 / 理解医学转录数据集 
- 
体格检查 / 理解医学转录数据集 
- 
评估 / 理解医学转录数据集 
- 
推荐 / 理解医学转录数据集 
- 
关键词 / 理解医学转录数据集 
 
- 
- 
基于记忆的协同过滤 - 用户-用户协同过滤 / 用户-用户协同过滤
 
- 
基于记忆的协同过滤 - 
关于 / 基于记忆的协同过滤 
- 
项目-项目协同过滤 / 项目-项目协同过滤 
 
- 
- 
记忆网络 - 
关于 / 记忆网络 
- 
动态记忆网络 (DMN) / 动态记忆网络 (DMN)") 
 
- 
- 
合并后的数据帧 - EDA / 合并数据帧的 EDA
 
- 
方法,词频-逆文档频率 (TF-IDF) - fit_transform() / 基线模型的特征工程
 
- 
微小预处理 / 微小预处理 - 最左侧的点,从新闻标题中移除 / 从新闻标题中移除最左侧的点
 
- 
缺失值 - 
寻找 / 寻找缺失值 
- 
替换 / 替换缺失值 
 
- 
- 
机器学习算法 / 尝试不同的机器学习算法 
- 
机器学习模型 - 
测试 / 测试最佳方法 
- 
保留样本语料库,在数据集训练中转换 / 将保留样本语料库以训练数据集的形式转换 
- 
转换后的数据集,转换为矩阵形式 / 将转换后的数据集转换为矩阵形式 
- 
精确度分数,生成 / 生成预测 
 
- 
- 
真实测试数据上的机器学习模型 - 执行 / 在真实测试数据上运行机器学习模型
 
- 
MobileNet SSD - 架构 / MobileNet SSD 模型的架构
 
- 
模型 - 测试 / 测试模型
 
- 
基于模型的协同过滤 - 
关于 / 基于模型的协同过滤 
- 
基于矩阵分解的算法 / 基于矩阵分解的算法 
- 
相比,基于记忆的协同过滤 / 基于记忆的协同过滤与基于模型的协同过滤之间的区别 
- 
最佳方法,实施 / 实施最佳方法 
 
- 
- 
多层感知器 (MLP) / 为什么我们应该使用预训练模型? 
- 
多层感知器 (MLP) / 最佳方法 
N
- 
简单贝叶斯 - 参考链接 / 选择机器学习算法
 
- 
基于神经网络的算法 - 实施 / 最佳方法
 
- 
Normalization.py 文件 - 
parse_document 函数 / Normalization.py 
- 
remove_special_characters 函数 / Normalization.py 
- 
remove_stopwords 函数 / Normalization.py 
- 
unescape_html 函数 / Normalization.py 
- 
pos_tag_text 函数 / Normalization.py 
- 
lemmatize_text 函数 / Normalization.py 
- 
expand_contractions 函数 / Normalization.py 
- 
normalize_corpus 函数 / Normalization.py 
 
- 
- 
Numberbatch 的预训练模型 / 准备数据集 
- 
纽约时报新闻文章数据集 / 理解纽约时报新闻文章数据集 
- 
纽约时报新闻文章 - 情感分析 / 纽约时报新闻文章的情感分析
 
- 
纽约时报新闻数据集 - 
准备 / 准备纽约时报新闻数据集 
- 
出版日期,转换为 YYYY-MM-DD 格式 / 将出版日期转换为 YYYY-MM-DD 格式 
- 
新闻文章,按类别过滤 / 按类别过滤新闻文章 
- 
过滤功能,实施 / 实施过滤器功能并合并数据集 
- 
数据集,合并 / 实施过滤器功能并合并数据集 
- 
合并数据集,以 pickle 文件格式保存 / 以 pickle 文件格式保存合并数据集 
 
- 
O
- 
OpenCV - 
设置 / 设置和安装 OpenCV 
- 
安装 / 设置和安装 OpenCV 
 
- 
- 
开放域聊天机器人 - 开发 / 开放域
 
- 
优化过程,实时物体识别应用基线模型 - 关于 / 理解优化过程
 
- 
异常值 - 处理 / 处理异常值
 
- 
异常值检测 - 关于 / 检测异常值
 
P
- 
参数,与超参数对比 - 参考链接 / 超参数调整
 
- 
参数,预训练模型 - 
学习率 / 我们如何使用预训练模型? 
- 
迭代次数 / 我们如何使用预训练模型? 
- 
批处理大小 / 我们如何使用预训练模型? 
- 
激活函数 / 我们如何使用预训练模型? 
 
- 
- 
参数,词频-逆文档频率(TF-IDF) - 
min_df / 基线模型的特征工程 
- 
max_df / 基线模型的特征工程 
- 
sublinear_tf / 基线模型的特征工程 
- 
use_idf / 基线模型的特征工程 
- 
transform() / 基线模型的特征工程 
 
- 
- 
词性(POS)标签 / 最佳方法背后的理念 
- 
PASCAL VOC 数据集 - 
关于 / PASCAL VOC 数据集 
- 
PASCAL VOC 类别 / PASCAL VOC 类别 
 
- 
- 
PDFminer - 关于 / 抓取的数据集
 
- 
皮尔逊相关系数(PCC) / 基于相关性的推荐 
- 
混淆度 - 关于 / 混淆度
 
- 
Pong 游戏机器人 - 
构建 / 构建 Pong 游戏机器人 
- 
实现 / 实现 Pong 游戏机器人 
- 
参数,初始化 / 参数初始化 
- 
权重,以矩阵形式存储 / 以矩阵形式存储的权重 
- 
权重,更新 / 更新权重 
- 
代理,移动 / 如何移动代理 
- 
神经网络(NN),使用 / 使用神经网络理解过程 
 
- 
- 
精度 / 精度 
- 
预训练的 GloVe 模型 - 参考链接 / 加载 GloVe 模型
 
- 
问题陈述 / 介绍问题陈述 
- 
产品类别 - 
产品描述,分析 / 分析产品描述 
- 
定义 / 定义产品类别 
- 
簇的内容,特征描述 / 特征描述簇的内容 
- 
簇内轮廓分数 / 簇内轮廓分数分析 
- 
词云,分析 / 使用词云进行分析 
- 
主成分分析 (PCA) / 主成分分析 (PCA)") 
 
- 
- 
项目结构,总结应用 - 
关于 / 理解项目结构 
- 
Contractions.py / 理解项目结构 
- 
Normalization.py / 理解项目结构 
- 
Utils.py / 理解项目结构 
- 
Document_summarization.py / 理解项目结构 
 
- 
- 
PyTeaser - 关于 / 安装 python 依赖项
 
R
- 
RandomForest - 
关于 / 随机森林 
- 
参数 / 随机森林 
 
- 
- 
随机搜索参数调整 / 随机搜索参数调整 
- 
实时人脸识别 - 实现 / 实现实时人脸识别
 
- 
实时物体识别应用 - 
问题陈述 / 介绍问题陈述 
- 
数据集 / 理解数据集 
- 
迁移学习 / 迁移学习 
- 
编码环境,设置 / 设置编码环境 
- 
OpenCV,设置 / 设置和安装 OpenCV 
- 
OpenCV,安装 / 设置和安装 OpenCV 
- 
特征工程,基线模型 / 基线模型的特征工程 
- 
机器学习算法,选择 / 选择机器学习算法 
- 
MobileNet SSD 架构 / MobileNet SSD 模型架构 
- 
基准模型,构建 / 构建基准模型 
- 
测试指标 / 理解测试指标 
- 
基准模型,测试 / 测试基准模型 
- 
修订方法,实现 / 实现修订的方法 
- 
最佳方法 / 最佳方法 
 
- 
- 
回忆 / 回忆 
- 
受试者工作特征 (ROC) / ROC 
- 
推荐引擎 - 
关于 / 最佳方法 
- 
关键概念 / 理解关键概念 
 
- 
- 
循环神经网络 (RNN) / 构建神经网络 
- 
循环神经网络 (RNN) / 理解优化方法的关键概念 
- 
循环神经网络 (RNN) / 构建深度学习模型 
- 
基于区域的卷积神经网络 (R-CNN) / MobileNet SSD 模型的架构 
- 
强化学习 (RL) - 
关于 / 理解强化学习 (RL)") 
- 
马尔可夫决策过程 (MDP) / 马尔可夫决策过程 (MDP)") 
- 
折现未来奖励 / 折现未来奖励 
 
- 
- 
reorder_sentences 函数 - 关于 / reorder_sentences 函数
 
- 
基于检索的方法,聊天机器人 - 关于 / 基于检索的方法
 
- 
修订,基准方法 - 
实现 / 实现修订的方法 
- 
依赖项,导入 / 导入依赖项 
- 
IMDb 数据集,下载 / 下载和加载 IMDb 数据集 
- 
IMDb 数据集,加载 / 下载和加载 IMDb 数据集 
- 
最常用词汇,选择 / 选择最常用词汇和最大文本长度 
- 
最大文本长度 / 选择最常用词汇和最大文本长度 
- 
词嵌入,实现 / 实现词嵌入 
- 
卷积神经网络 (CNN),构建 / 构建卷积神经网络 (CNN)") 
- 
准确率,训练 / 训练并获得准确率 
- 
准确性,获得 / 训练和获得准确性 
- 
测试 / 测试修订方法 
- 
问题 / 理解修订方法中的问题 
 
- 
- 
修订方法 - 
实现 / 实现修订方法, 实现和测试修订方法, 实现修订方法, 实现, 实现修订方法, 实现修订方法 
- 
基于交叉验证的方法,实现 / 实现基于交叉验证的方法 
- 
超参数调整,实现 / 实现超参数调整 
- 
测试 / 实现和测试修订方法, 测试修订方法, 测试修订方法, 测试修订方法 
- 
问题 / 理解修订方法中的问题, 理解修订方法中的问题, 修订方法中的问题, 修订方法中的问题 
- 
关于 / 理解修订方法 
- 
对齐,实现 / 实现对齐 
- 
平滑,实现 / 实现平滑 
- 
逻辑回归,实现 / 实现逻辑回归 
- 
构建 / 构建修订方法, 构建修订方法 
- 
提高 / 理解如何改进修订方法, 理解如何改进修订方法 
- 
数据集,加载 / 加载数据集 
- 
书籍评分数据文件的 EDA / 书籍评分数据文件的 EDA 
- 
书籍数据文件,探索 / 探索书籍数据文件 
- 
用户数据文件的 EDA / 用户数据文件的 EDA 
- 
相关性逻辑,为推荐引擎实现 / 为推荐引擎实现相关性逻辑 
 
- 
- 
改进的方法,聊天机器人开发 - 
实现 / 实现改进的方法 
- 
数据准备 / 数据准备 
- 
问答对,生成 / 生成问答对 
- 
数据集,预处理 / 预处理数据集 
- 
数据集,分割为训练数据集和测试数据集 / 将数据集分割为训练数据集和测试数据集 
- 
词汇表,为训练和测试数据集构建 / 为训练和测试数据集构建词汇表 
- 
seq2seq 模型,构建 / 实现 seq2seq 模型 
- 
模型,创建 / 创建模型 
- 
模型,训练 / 训练模型 
- 
测试 / 测试改进的方法,测试聊天机器人的改进版本 
- 
测试指标 / 理解测试指标 
- 
缺陷 / 改进方法的问题 
 
- 
- 
改进的方法,职位推荐引擎 - 
构建 / 构建改进的方法 
- 
数据集,加载 / 加载数据集 
- 
训练和测试数据集,分割 / 分割训练和测试数据集 
- 
探索性数据分析 (EDA) / 探索性数据分析 
- 
推荐引擎,使用工作数据文件构建 / 使用工作数据文件构建推荐引擎 
- 
测试 / 测试改进的方法 
- 
缺陷 / 改进方法的问题 
- 
提高 / 理解如何改进改进的方法 
 
- 
- 
改进的方法,实时物体识别应用 - 
实现 / 实现修订方法 
- 
测试 / 测试修订方法 
- 
缺点 / 理解修订方法的问题 
 
- 
- 
修订方法,摘要应用 - 
构建 / 构建修订方法 
- 
实现 / 实现修订方法 
- 
get_summarized 函数 / get_summarized 函数 
- 
reorder_sentences 函数 / reorder_sentences 函数 
- 
summarize 函数 / summarize 函数 
- 
摘要,生成 / 生成摘要 
- 
缺点 / 修订方法的问题 
- 
提高 / 理解如何改进修订方法 
- 
LSA 算法 / LSA 算法 
 
- 
- 
基于规则的聊天机器人 - 
实现 / 实现基于规则的聊天机器人 
- 
对话流程,实现 / 实现对话流程 
- 
RESTful API,使用 flask 实现 / 使用 flask 实现 RESTful API 
- 
测试 / 测试基于规则的聊天机器人 
- 
优点 / 基于规则的聊天机器人的优点 
 
- 
- 
基于规则的系统 - 
工作 / 为什么基于规则的系统可以工作? 
- 
关于 / 理解基于规则的系统 
 
- 
S
- 
抓取的数据集 - 关于 / 抓取的数据集
 
- 
seq2seq 模型 - 关于 / 理解 seq2seq 模型
 
- 
序列到序列(seq2seq)神经网络架构 - 关于 / 基于生成的方法
 
- 
简短对话,聊天机器人开发 - 关于 / 简短对话
 
- 
简单 TF-IDF - 
使用,用于构建职位推荐引擎 / 生成 TF-IDF 向量和余弦相似度 
- 
训练数据集,构建 / 构建训练数据集 
- 
IF-IDF 向量,为训练数据集生成 / 为训练数据集生成 IF-IDF 向量 
- 
测试数据集,构建 / 构建测试数据集 
- 
相似度分数,生成 / 生成相似度分数 
 
- 
- 
单次检测器(SSD) / 选择机器学习算法 
- 
平滑 / 平滑 
- 
太空侵略者游戏机器人 - 
构建 / 构建太空侵略者游戏机器人 
- 
实现 / 实现太空侵略者游戏机器人 
 
- 
- 
标准差 - 参考链接 / 列出统计属性
 
- 
统计属性 - 列出 / 列出统计属性
 
- 
股票 / 介绍问题陈述 
- 
摘要 - 
基础知识 / 理解摘要的基础知识 
- 
抽取式摘要 / 抽取式摘要 
- 
抽象摘要 / 抽象摘要 
 
- 
- 
摘要应用 - 
问题陈述 / 介绍问题陈述 
- 
数据集 / 理解数据集 
- 
基准方法,构建 / 构建基准方法 
- 
修订方法,构建 / 构建修订方法 
- 
最佳方法 / 最佳方法的理念 
 
- 
- 
摘要应用,使用亚马逊评论构建 - 
关于 / 使用亚马逊评论构建摘要应用 
- 
数据集,加载 / 加载数据集 
- 
数据集,探索 / 探索数据集 
- 
数据集,准备 / 准备数据集 
- 
DL 模型,构建 / 构建 DL 模型 
- 
DL 模型,训练 / 训练 DL 模型 
- 
DL 模型,测试 / 测试 DL 模型 
 
- 
- 
摘要函数 - 关于 / 摘要函数
 
- 
Sumy - 关于 / 安装 python 依赖项
 
- 
支持 / 支持 
- 
支持向量机(SVM)算法 - 
关于 / 选择机器学习算法 
- 
参考链接 / 选择机器学习算法 
 
- 
T
- 
技术,异常检测 - 
基于百分比的异常检测 / 基于百分比的异常检测 
- 
基于中位数绝对偏差 (MAD) 的异常检测 / 基于中位数绝对偏差 (MAD) 的异常检测 的异常检测") 
- 
基于标准差 (STD) 的异常检测 / 基于标准差 (STD) 的异常检测 的异常检测") 
- 
基于多数投票的异常检测 / 基于多数投票的异常检测: 
- 
异常的可视化 / 异常的可视化 
 
- 
- 
词语频率-逆文档频率 (TF-IDF) - 关于 / 基线模型的特征工程
 
- 
测试矩阵 - 
关于 / 理解测试矩阵,理解测试矩阵,默认测试矩阵,理解测试矩阵,理解测试矩阵,理解测试矩阵,理解测试矩阵 
- 
训练模型,平均准确率 / 训练模型的平均准确率 
- 
ROC-AUC 分数 / ROC-AUC 分数 
- 
可视化方法 / 可视化方法 
- 
混淆矩阵 / 混淆矩阵 
- 
学习曲线 / 学习曲线 
- 
精确率 / 精确率 
- 
召回率 / 召回率 
- 
F1 分数 / F1 分数 
- 
支持 / 支持 
- 
训练准确率 / 训练准确率 
 
- 
- 
测试指标,实时物体识别应用 - 
关于 / 理解测试指标 
- 
交集与并集 (IoU) / 交集与并集 (IoU)") 
- 
平均精度均值 (mAP) / 平均精度均值 
 
- 
- 
测试指标,聊天机器人的修订版 - 
关于 / 理解测试指标 
- 
惊奇度 / 惊奇度 
- 
损失 / 损失 
 
- 
- 
textrank_text_summarizer 函数 - 
关于 / 生成摘要 
- 
使用 / 生成摘要 
 
- 
- 
思维向量 / 理解 seq2seq 模型 
- 
训练模型 - 
预测 / 预测和保存训练模型 
- 
保存 / 预测和保存训练模型 
 
- 
- 
迁移学习 - 
关于 / 迁移学习, 什么是迁移学习? 
- 
预训练模型 / 什么是预训练模型? 
- 
预训练模型,优势 / 什么是预训练模型?, 为什么我们应该使用预训练模型? 
- 
预训练模型,使用 / 如何使用预训练模型? 
 
- 
- 
真阴性 (TN) / 理解测试矩阵 
- 
真阳性 (TP) / 理解测试矩阵 
- 
图灵测试 - 参考 / 开放域
 
U
- 
users.tsv - 
关于 / users.tsv 
- 
用户 ID / users.tsv 
- 
窗口 ID / users.tsv 
- 
分割 / users.tsv 
- 
城市 / users.tsv 
- 
州 / users.tsv 
- 
国家 / users.tsv 
- 
ZipCode / users.tsv 
- 
学位类型 / users.tsv 
- 
专业 / users.tsv 
- 
毕业日期 / users.tsv 
- 
工作历史数量 / users.tsv 
- 
总工作经验年数 / users.tsv 
- 
目前就业状态 / users.tsv 
- 
管理他人 / users.tsv 
- 
管理人数 / users.tsv 
 
- 
- 
user_history.tsv - 
关于 / user_history.tsv 
- 
序列 / user_history.tsv 
- 
职位名称 / user_history.tsv 
 
- 
- 
Utils.py 文件 - 
build_feature_matrixs 函数 / Utils.py 
- 
low_rank_svd 函数 / Utils.py 
 
- 
V
- 
可视化 / 可视化方法 
- 
基于投票的集成机器学习模型 / 基于投票的集成机器学习模型 
Y
- 
YOLO - 
关于 / 如何使用预训练模型?, 理解 YOLO 
- 
工作 / YOLO 的工作原理 
- 
优势 / YOLO 的工作原理 
- 
架构 / YOLO 的架构 
 
- 
- 
YOLO 实现,使用 Darkflow - 
执行 / 使用 Darkflow 的实现 
- 
Cython,安装 / 安装 Cython 
- 
设置文件,构建 / 构建已提供的设置文件 
- 
环境,测试 / 测试环境 
- 
目标检测,在图像上运行 / 加载模型并在图像上运行目标检测 
- 
目标检测,在视频流上运行 / 加载模型并在视频流上运行目标检测 
 
- 
- 
YOLO 实现,使用 Darknet - 
执行 / 使用 Darknet 的实现 
- 
环境设置,针对 Darknet / 为 Darknet 设置环境 
- 
Darknet. 编译 / 编译 Darknet 
- 
预训练权重,下载 / 下载预训练权重 
- 
图像目标检测,运行 / 运行图像目标检测 
 
- 

 
                     
                    
                 
                    
                


















 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号