从头开始创建一个不和谐机器人并连接到 MongoDB
使用 python 为您的 discord 服务器创建一个功能记分员 bot,并将数据存储在 MongoDB 数据库中。
Discord 是一个对开发者非常友好的消息平台。discord 开发者的一个主要出路是生产 discord 机器人,这些机器人可以执行无限多的任务,如发送消息、播放音乐,甚至允许在聊天服务器上玩小游戏。在本教程中,我们将使用 Python 创建一个 discord bot,它将跟踪用户发送的某些消息,并使用 MongoDB 添加一个评分系统。
首先,在终端中安装 discord.py、dnspython 和 pymongo 模块。Dnspython 和 pymongo 将用于我们的 MongoDB 连接。
接下来,导入您刚刚安装的所有模块。
在这之后,我们必须在 discord web 应用程序上激活我们的机器人。首先,请前往:
不和谐是通过语音、视频和文本进行交流的最简单的方式,无论你是学校俱乐部的一员,还是晚间……
discordapp.com](https://discordapp.com/)
然后点击开发者门户。
点击开发者门户后,点击右上角的蓝色按钮“新应用”。一旦你为你的机器人输入了一个名字,你应该会看到这样一个屏幕:
一般信息屏幕
接下来,点击屏幕右侧显示“Bot”的标签。您应该会看到这样的屏幕:
添加 bot 屏幕
当你到达这个屏幕后,点击右边的蓝色按钮“添加机器人”。您应该会看到这样的屏幕:
机器人信息屏幕
在这里,您将看到您的机器人的所有信息,包括允许您访问机器人的令牌。请确保这个令牌是安全的,因为它是决定谁可以访问 bot 并更改其功能的唯一因素。接下来,我们必须将机器人添加到服务器。在本教程中,有一个为测试而创建的单独的服务器。要将 bot 添加到服务器中,请点击屏幕左侧的“OAuth2”选项卡。该工具为您的 bot 创建一个访问 URL,然后连接到 Discord“oauth 2 API”。您应该会看到这样的屏幕:
OAuth2 屏幕
一旦你到达这个屏幕,选择写着“机器人”的盒子。单击该框后,屏幕上会显示可选的权限。对于这个机器人,您可以选择标有“管理员”的框,以允许出于测试目的的所有权限。但是,在创建要添加到公共服务器的 bot 时,您可能不想添加所有权限。
范围类型和权限
一旦选择了所有选项,就会自动生成一个 URL。将该 URL 复制到剪贴板并粘贴到浏览器中。一旦你被发送到那个 URL,选择你想要添加机器人的服务器。恭喜你,你已经正式将你的第一个机器人添加到你的服务器上了!
成功屏幕
现在我们已经将机器人添加到我们的服务器中,我们可以开始编程了!我们必须首先创建一个与机器人的连接。这就是令牌的用途。
在这个代码片段中,我将我的机器人的令牌保存在一个单独的文本文件中,然后将该值存储在变量“token”中。在这之后,我创建了一个客户端,它是你的机器人与 Discord 及其 API 之间的连接。在代码片段中,您可以看到客户端实现了它的“on_ready()”函数。一旦机器人准备好进一步的动作,就调用这个函数。深入研究 on_ready()函数,关键字“async”表示该函数是异步的,并且与正在运行的任何其他函数一起发生。一旦机器人实现了更多的功能,这就更有意义了。最后,我们使用保存在变量中的令牌运行客户端。如果您运行这个代码片段,您应该在屏幕上看到您的 bot 输出的名称,后面跟着剩余的 print 语句。
接下来,我们必须对机器人进行编程,以读取用户消息,并确定是否有一个关键字将被它的调平系统选中。对于这个机器人,我将使用“不和谐”、“python”、“代码”和“乐高”这些词。这些关键字不区分大小写。
这里,我们为我们的客户机创建了另一个事件,它实现了函数“on_message()”。这个函数只是在用户向通道发送消息时运行。它应该打印关于发送的消息的基本信息,比如它的通道、作者、作者的名字以及消息是什么。一旦 bot 检查到诸如“python”之类的关键字,它将向通道发送它被接受的消息。下面是发送和接收消息的完整代码片段:
既然我们已经有了发送和接收消息的基本功能,现在是时候在我们的 MongoDB 数据库中记录用户级别了!我们将简单地通过计算用户说一个关键词的次数来计算用户级别。
首先,我们必须在mongodb.com创建一个 MongoDB 帐户,以便创建一个集群,也就是一组数据库。创建帐户后,屏幕会提示您:
集群创建
在创建数据库时,MongoDB 将您的集群存储在它的一个物理数据库中,因此您必须选择您的地区和提供商。选择离您最近的地区,以确保最快的反馈时间。如果愿意,您也可以更改集群的名称。创建集群后(需要 1-3 分钟),您将看到以下屏幕:
集群屏幕
我们现在必须连接到我们的集群。为此,请点击“连接”按钮。这将通过以下屏幕提示您:
连接到群集
只需点击绿色按钮,上面写着“添加您当前的 IP 地址”。在那里,创建一个用户来链接到集群。请确保记住您的用户凭据,因为它将用于后续步骤。一旦你完成了,继续你的连接方式。这是将出现的屏幕:
连接方法
在本教程中,我们将使用 MongoDB Compass,它是 MongoDB 的 GUI,允许轻松访问集群,是对数据进行分析的好方法。如果您没有 MongoDB Compass,请按照它提供的说明下载该程序。一旦您下载了 MongoDB Compass,您将会看到这样一个屏幕:
MongoDB 指南针连接屏幕
回到 MongoDB online,您会看到这样一个屏幕:
MongoDB 集群连接
您得到的 URL 是您将复制并粘贴到 MongoDB Compass 门户中的内容。在网址中,你会看到它的一部分有“”。您将用您的群集用户帐户的密码替换它。一旦你把这个链接粘贴到你的 MongoDB Compass 门户,你就会看到你的集群!
完整集群
现在,让我们回到 MongoDB 的 web 门户,探索我们的集群。请点击“收藏”按钮。集合是群集中可以存储数据的子组。它将询问您是否希望加载样本数据集。让我们加载它来了解集群的结构。加载可能需要几秒钟。
加载的样本数据集
我们接下来要做的是创建一个数据库来存储我们的用户数据。点击显示“+创建数据库”的按钮。您可以随意命名数据库。MongoDB 中的数据存储在一个层次结构中。它的结构是集群>数据库>集合。一旦我们创建了一个数据库,我们还需要创建一个将存储在数据库中的集合。
空用户数据数据库
我们现在有一个空的数据库等待填充我们的用户数据!
为了检索和上传数据到我们的数据库,MongoDB API 创建了许多函数,我们不会完全涵盖所有使用的函数。如果您想进一步探索,这里是 MongoDB 文档:
[## PyMongo 3.10.1 文档— PyMongo 3.10.1 文档
如果您对 PyMongo 有困难或有疑问,最好的地方是 MongoDB 用户组。一旦你得到…
pymongo.readthedocs.io](https://pymongo.readthedocs.io/en/stable/)
首先,让我们从停止的地方开始。我们将在现有的不和谐机器人代码中添加内容。
这些是我们运行代码所需的导入。我们接下来要做的是在代码中连接到我们的集群,然后导航到我们存储数据(用户数据)的位置。
首先,我们必须连接到我们的集群。在代码中显示“CONNECTION_URL”的地方,用您用来连接 MongoDB Compass 的 URL 替换它,并用引号将它括起来。该链接在本教程中没有显示,因为它包含一个安全漏洞。之后,我们必须连接到我们的数据库,然后连接到我们的集合。我们现在在我们需要在的位置!
我们接下来要做的是为放入数据库的每个条目选择属性。每个条目将被视为一个独特的用户,其中应包括用户名,他们的分数,和他们的 ID。MongoDB 数据库中的每个条目都与一个惟一的 ID 相关联。您可以让它自动生成,也可以自己决定。出于我们的目的,最好是让条目 ID 与用户 ID 不一致。
在这个代码片段中,我们创建了一个名为“post”的变量,它包含了条目的属性。然后,我们将这些属性插入到“UserData”集合中。一旦我们运行程序并在我们的 Discord 服务器中键入“python ”,我们将看到我们的数据存储在数据库中。
存储的用户数据
将向我们的数据库发送数据的代码添加到我们的机器人正在搜索的所有其他关键字中。现在,代码将如下所示:
然而,我们的计划有一个小问题。一旦我们键入另一个关键字,数据库就不能再次输入同一个用户。我们可以用一些简单的条件语句来解决这个问题。
在这里,我们创建了一个对数据库的查询,主要是询问是否有任何条目的预先存在的 ID 与当前输入的 ID 相同。如果当前输入的 ID 不同,那么新用户将登录到数据库中。接下来,如果用户已经存在于数据库中,我们必须修改它将会发生什么。
在这个代码片段中,如果用户已经在数据库中注册,我们基本上是在更新一个预先存在的条目。我们首先找到带有。find()方法,然后我们遍历用户并捕获其“score”属性。然后我们给这个值加 1。最后,我们使用。update_one()函数专门更新“score”属性。把这个函数添加到所有关键词之后,你就有了一个完整的链接到 MongoDB 的 Discord bot!以下是完整的代码:
信息和图像来源:
[## https://discordapp . com/assets/f7a 4131 e 47 f 50 b 48 B3 f 85 f 73 c 47 ff 1 DC . png 的 Google 图片结果
编辑描述
www.google.com](https://www.google.com/imgres?imgurl=https%3A%2F%2Fdiscordapp.com%2Fassets%2Ff7a4131e47f50b48b3f85f73c47ff1dc.png&imgrefurl=https%3A%2F%2Fdiscordapp.com%2F&tbnid=llbk160_bIKyPM&vet=12ahUKEwi7xrC5pPvoAhVDCN8KHWGZBtQQMygBegUIARCbAg…i&docid=tL3jcu20AJy75M&w=1200&h=630&q=discord&ved=2ahUKEwi7xrC5pPvoAhVDCN8KHWGZBtQQMygBegUIARCbAg) [## 如何用 Python 制作一个不和谐机器人——真正的 Python
在这个循序渐进的教程中,您将学习如何用 Python 制作一个 Discord bot,并与几个 API 进行交互。你会…
realpython.com](https://realpython.com/how-to-make-a-discord-bot-python/)
用 Python 和 Dask 创建分布式计算机集群
如何在您的家庭网络上建立分布式计算机集群,并使用它来计算大型相关矩阵。
计算相关矩阵会非常快地消耗大量的计算资源。幸运的是,相关性(和协方差)计算可以智能地分成多个过程,并分布在许多计算机上。
在本文中,我们将使用 Dask for Python 来管理局域网中多台计算机之间的大型相关矩阵的并行计算。
什么是相关矩阵
相关矩阵显示了两个变量之间的线性统计关系。众所周知,相关性并不意味着因果关系,但我们仍然经常利用它来理解我们所处理的数据集。
如果你只是想在你自己的家庭计算机集群上计算相关性矩阵,那么跳过这一节,但是如果你对如何用数学方法计算相关性感兴趣,那么继续读下去。
直观显示这些步骤的一个简单方法是在 Excel 中重新创建,因此我将向您展示如何在 Excel 中计算三组外汇数据之间的相关性和协方差。
首先,我们获取三组时间序列数据,按每种货币对的列进行排列,然后获取每组汇率的平均值(值的总和除以值的数量):
然后,对于每个时间序列数据点,我们计算该点与平均值的差值:
然后,我们对这些差异进行平方,这将给出每个数据集的方差。
然后,我们计算平方值范围的总和,除以我们的样本大小减一,得到我们的方差,例如 GBPUSD 为 0.0013%,如第 3 行所示。我们从数据点的计数中减去 1,因为这是我们数据集的样本(我们没有每一个历史数据点),而不是整个人口。这就是所谓的“样本方差”。如果我们有全部人口,我们不会减去一个。
我们还可以计算这一阶段的标准差,它就是方差的平方根:
我们可以通过将每个时间点的两个“均值差”值相乘来计算两个给定数据集之间的协方差。以下示例显示了 GBPUSD 和 JPYUSD 的单一时间点协方差计算:
我们称 GBPUSD 数据集为 A,JPYUSD 数据集为 B,依此类推。有了四个单独的数据集,我们就有 6 个组合用于协方差计算:AB、AC、AD、BC、BD 和 CD。所有这些都是以相同的方式计算的,即乘以每个时间点每个数据集平均值的差值。
然后,我们取这些值的平均值,就像我们对方差所做的一样,从分母中减去一,得到协方差:
最后,我们可以通过将协方差除以两个标准差的乘积来计算这两个数据集的相关性:
在数学上,我们可以将其表示为:
这可能看起来令人望而生畏,但这正是我们已经计算过的。请看右边的等式,分数的顶部表示“取数据集 X 中每个数据点与数据集 X 的平均值之差,然后乘以数据集 Y 中的等效计算结果”,然后将所有这些值相加。带有下标 I 的 x 仅表示数据集 x 的给定点,而 x̅表示数据集的平均值。y 值也是如此。
分母也是说,对于 X 上的每一个点,从那个点减去 X 的平均值,然后平方你得到的值。把所有这些加在一起,对 y 做同样的事情。现在把这两个值相乘,然后求这个数的平方根。结果是两个数据集的标准差的乘积。
这正是我们刚刚为自己计算的,也是我们今天正在做的,只是我们使用家用计算机集群来计算大量金融工具在大量时间点的相关性。
设置环境
网络
第一步是在构成集群的每台计算机上建立计算环境。您将需要确保计算机可以在本地网络上看到彼此(通过防火墙例外)。您还需要记下每台计算机的 IP 地址。
在 Windows 上,您可以在命令提示符下键入:
ipconfig
这将为您提供本地网络上的 IP 地址。在 Linux 机器上,您可以键入:
hostname -I
或者在 OSX,下面的内容应该有用:
ipconfig getifaddr en0
您应该记下每台机器上的每个 IP 地址,因为您将需要它来设置集群。您还可以通过运行以下命令来检查每台计算机是否可以看到其他计算机:
ping <Target IP Address>
如果有数据响应,那么两台计算机可以互相看到对方,如果没有,您需要仔细检查 IP 地址,并确保防火墙没有阻止通信。
我们将使用三台计算机作为小型集群的一部分,它们分别是 192.168.1.1、192.168.2 和 192.168.1.3。
Python 和Dask
需要注意的非常重要的一点是每台计算机上的每个 Python 环境必须相同。如果库版本中存在不一致,这可能会导致问题。
如果更简单的话,可以使用 Anaconda 或类似的工具创建一个新的 Python 环境,其中包含您将使用的库的匹配版本,比如 Numpy 和 Pandas。
您还需要在每台计算机上安装 Dask 本身。您可以使用 Conda 实现这一点:
conda install dask distributed -c conda-forge
或者您可以使用画中画:
python -m pip install dask distributed --upgrade
现在,您应该在集群中的所有计算机上安装了一致的 Python,并且具有匹配的库版本。
获取数据
在这个例子中,我使用的是每日股票数据的大型数据集。这包括 2000 年至 2020 年约 7000 只美国股票的每日收盘价。在我的集群中的任何一台个人计算机上进行计算都是非常耗时的。当我最初尝试计算时,由于在写入页面文件时遇到内存限制,系统在几个小时后崩溃。
这些数据存储在一个大型 SQL 表中,我们将按顺序访问每个股票,然后进行合并。事实证明,这比用 Python 执行操作要快得多。
我们还将使用 MySQL 数据库连接器的不同实现。标准的 MySQL 连接器库在处理大型数据集时相对较慢,因为它本身是用 Python 编写的。此连接器的 C 实现必须更快,您可以在这里找到安装细节:
[## MySQLdb 用户指南- MySQLdb 1.2.4b4 文档
MySQLdb 是流行的 MySQL 数据库服务器的接口,它提供 Python 数据库 API。文件有…
mysqlclient.readthedocs.io](https://mysqlclient.readthedocs.io/user_guide.html)
一旦安装了 MySQLdb,您就可以开始查询数据了。我首先从表中提取所有唯一的、按时间顺序排列的日期列表。这将是整个数据库中每个时间步长的黄金来源。
然后,我逐个提取股票的时间序列数据,并将其左连接到黄金时间序列源。然后,我将这个单独的数据帧添加到一个数据帧列表中,每个数据帧对应一只被提取的股票。我推迟了合并操作,将每个股票时间序列合并成一个数据集。
这种查询方法本身可以并行化,而从表中提取所有数据的单个查询是不可能的。
这给我们留下了一个带有公共时间序列索引的数据集列表,然后我们可以使用它。
这里我们提取股票列表,构建时间序列的黄金来源,然后遍历列表,以这种通用格式提取每只股票的时间序列数据。
我们从中提取股票数据的查询:
SELECT date, close as {} FROM history where stock = '{}'
获取股票的收盘价,并用该股票的代码命名该列。这意味着,我们将有 7,000 个列,每个列的名称都是“close ”,而不是 7,000 个列,每个列都用相关的股票代码命名,并映射到一个公共的时间序列。这就好办多了。
计算相关矩阵
好消息来了。我们将得到最终格式的数据,并开始计算相关矩阵。
这里,我们为列表中的每个数据帧设置日期索引,并将它们合并在一起。因为我们根据相关的股票代码来命名每一列,所以现在我们有了一个大表,它有一个作为索引的公共日期列表和一个针对每只股票的带标签的列。我们将通过删除所有“NaN”值的列来简化表格,这里没有相关数据。
这正是我们计算相关矩阵所需的格式,因此我们将:
import dask.dataframe as dd
并创建 Dask 数据帧
merged = dd.from_pandas(merged, 20)
此时,您需要做出一个重要的设计决策,该决策将显著影响相关矩阵的处理速度。
在这里,我们将 Pandas 数据帧转换为 Dask 数据帧,我们还必须指定一个数字,这里是“20”,表示数据集中的分区数量。
出于并行计算的目的,每个分区将被视为独立的单元。例如,如果您选择“1”,相关矩阵将由单个 CPU 上的单个线程计算,您将不会获得任何并行化优势。
另一方面,如果您将这个数字设置得太高,您的性能会受到影响,因为加载和处理每个任务会产生开销。
最初,我将它设置为小型集群中计算机上的线程数。然而,我很快发现其中一台机器慢得多,两台速度较快的机器闲置着,等待这台机器完成。为了减轻这种情况,我增加了进程的数量,以便速度较快的计算机可以继续处理后面的任务,而速度较慢的计算机可以继续处理最初分配的进程。
做出这个决定后,我们将合并的数据帧从 Float 32 数据类型转换为 Float 16 数据类型,因为额外的精度是不必要的,并且会减慢我们接下来的计算。
然后我们运行关键线路:
merged = merged.corr().compute()
这里有两个函数,。corr() 计算我们的相关矩阵,以及*。compute()* 发送给 Dask 进行计算。
在这个处理之后,我们将得到一个关联矩阵,显示每只股票之间的关联。下面是这种情况的简化示意图(由于我们没有计算矩阵,所以数据是虚构的
你会注意到这里有很多重复和不必要的数据。AAPL 和 APPL 的相关系数是 1。AMZN 与 AMZN 的相关性为 1。这将是你整个矩阵对角线上的情况。
同样,也有重复。AMZN 与 AAPL 的相关性显然与 AAPL 与 AMZN 的相关性相同(这是一回事!).
当您要将这些数据保存回数据集中时,复制所有这些数据是没有意义的,尤其是对于如此大的数据集。所以让我们摆脱这种重复。
您会注意到,不必要的数据值形成了数据集的一半,横跨一个对角线形状。所以在我们保存这个数据集之前,让我们屏蔽掉对角线。
corrs = merged.mask(np.tril(np.ones(merged.shape)).astype(np.bool))
这条线就是这么做的。它以我们的相关矩阵的形式创建了一个 1 的矩阵。然后,它使用 Numpy 返回数据的副本,并使用 tril()函数对给定的对角线进行清零。然后,通过将这些零转换为 bool 类型,它将这些零转换为 False 条件。最后,它使用 Pandas 将此作为掩膜应用于数据集,并将其保存为我们新的相关矩阵。
然后,我们可以将这个简化的矩阵转换成表格,然后上传回我们的数据库。
levels = merged.columns.nlevels...df1 = corrs.stack(list(range(levels))).reset_index()
del df1["level_0"]
del df1["level_2"]
df1.columns = ["Stock 1", "Stock 2", "Correlation"]
print("Uploading SQL")
df1.to_sql("correlations", con=engine, if_exists='append', chunksize=1000, index=False)
print("Process Complete")
在这里,我们使用 stack()函数将它转换回数据库的一个表。这给了我们一个类似这样的表格:
您可以看到,没有重复的值或冗余的 1 来显示股票与自身的相关性。然后,可以将其上传回数据库,整个过程就完成了。
矩阵的分布式计算
我们必须在代码中添加最后一行,也是至关重要的一行,但是首先我们需要创建我们的集群。
在每台计算机上,您都需要使用 Python 终端。如果您正在使用 Anaconda,您可以使用 GUI 启动终端,或者您可以进入 Anaconda 提示符并使用以下命令选择您的环境:
conda activate <environment>
您现在需要决定哪台计算机将充当调度程序并管理任务的分配,哪台计算机将充当工作程序。计算机完全有可能既是调度者又是工作者。
在调度器上,继续在 Python 终端中输入以下内容:
dask-scheduler
这将启动调度程序并管理集群的工作流。记下您完成此操作的本地网络 IP 地址,在我们的示例中,我们将使用 192.168.1.1。
我们希望这台机器也作为一个工作者,所以我们将打开另一个 Python 终端并键入:
dask-worker 192.168.1.1:8786
这应该表明我们已经在 worker 和 scheduler 之间建立了连接。
现在,我们需要在集群中的其他每台计算机上重复这个命令,每台计算机都指向位于 192.168.1.1 的调度程序。
我之前提到其中一台电脑比其他的慢。然而,它有两倍的 RAM,可以方便地在最后组装相关矩阵。我没有将它从集群中完全删除,而是决定通过限制 Dask 可用的线程数量来限制它可以运行的进程数量。您可以通过将以下内容附加到 Dask-worker 指令中来实现这一点:
dask-worker 192.168.1.1:8786 --nprocs 1--nthreads 1
现在您的集群已经启动并运行了,您可以在代码中添加关键的一行了。将以下内容添加到 Python 文件的顶部附近(或者在任何情况下,添加到对。corr()。计算():
client = _get_global_client() or Client('192.168.1.1:8786')
确保将 IP 地址替换为调度程序的 IP 地址。这将把 Dask 指向调度程序来管理计算。
现在,你可以运行你的程序了。
监控计算
Dask 提供了一个很好的仪表盘来监控你的计算进度。
转到 192.168.1.1:8787(或使用端口 8787 建立调度程序的任何 IP 地址)。
这将向您显示 Dask 仪表板,以便您可以跟踪进度并监控内核和内存的利用情况。
集群的内存和 CPU 使用情况
上面的“Workers”页面显示了集群中的节点及其当前的利用率水平。您可以使用它来跟踪哪些工作做得最多,并确保这保持在一个可接受的范围内。值得注意的是,CPU 利用率百分比是基于每个内核的,因此如果使用两个内核,您可以获得 200%的 CPU 利用率,如果使用八个内核,则可以获得 800%的 CPU 利用率。
Dask 仪表板状态屏幕
状态屏幕显示正在进行的任务的明细。上面的屏幕截图显示,我们已经将 20 个分区中的 4 个从 Pandas 数据帧转换为 Dask 数据帧,其他的正在整个集群中工作。我们还可以看到,我们已经存储了超过 4 GB 的数据,未来还会有更多。
Dask 仪表板图形屏幕
图形屏幕以图形方式显示任务是如何划分的。我们可以看到,每个分区都被单独处理,因此如预期的那样有 20 个分区。每个分区经历三个阶段,阶段 0,在那里它从 Pandas 转换到 Dask,阶段 1,在那里计算该特定分区的相关矩阵,以及阶段 3,在那里组合所有分区并计算互相关。
Dask 仪表板系统屏幕
系统屏幕显示当前系统的资源利用情况,包括 CPU、内存和带宽。这向您显示了在任何给定时间集群消耗了多少计算能力,对于查看网络上当前的压力和负载非常有用。
您可以使用这些屏幕来观察您的计算进度,您将逐渐看到各种任务在完成计算后变成绿色。然后,结果将通过网络传播到单个节点,该节点将编译结果并将其返回到您的 Python 程序。
正如你已经编码的那样,生成的矩阵将被精简到相关的对角线部分,制成表格,并上传到你的数据库。
结论
现在,您已经成功地设置了一个计算机集群,并使用它来计算相关矩阵,简化您的结果,并将其存储起来以供进一步分析。
您可以使用这些核心组件对您的集群做更多有趣的事情,从机器学习到统计分析。
让我知道你进展如何。
可以在 Twitter 上关注我 @mgrint 或者在https://Grint . tech查看更多来自我。发邮件给我matt @ Grint . tech。
在 3 分钟内创建一个财务 Web 应用程序!
了解如何使用 Python 中的 Streamlit 创建技术分析应用程序!
任何数据驱动项目的一个重要部分是它能够被容易地解释和查看,甚至对于那些事先可能对数据一无所知的人。Python 中的 Streamlit 介绍!
Streamlit 是一个极其易用和直观的工具,用于在 Python 中构建高度交互式、数据驱动的 web 应用程序。有了这个工具,您可以只关注数据方面,而不用担心使用 Flask 或 Django 进行繁琐的部署。
这个过程是完全直观的,到本文结束时,您也应该能够在几分钟内部署您的 web 应用程序,并且只需要几行代码!
设置项目
要开始使用我们的 Streamlit web 应用程序,我们必须首先使用 PyPi (Python 的包管理器)下载它。对于您可能没有的任何包(包括 streamlit ),只需在您的终端中键入以下内容,我们就可以开始了。
pip install streamlit
如果您的计算机上没有安装 pip,您可以快速浏览一下这篇文章并在安装后返回这里。
开始项目!
导入依赖项
首先,我们必须导入整个程序中需要的所有依赖项。我们将使用的主要库是 Yfinance、Streamlit 和 TaLib。Yfinance 将允许我们接收任何股票的历史股价,Streamlit 将允许我们将 web 应用程序部署到本地主机上,TaLib 将允许我们计算稍后将在应用程序上显示的技术指标。
现在我们已经设置了依赖项,我们终于可以开始构建我们的应用程序了!
设置 web 应用程序!
首先,为了运行程序,到你的终端键入命令streamlit run file _ name . py。用您选择从此程序创建的文件名替换 file_name.py。然后,一个 web 应用程序将在您的本地主机上打开!
为了在网站上写文章,我们可以使用。Streamlit 中的 write()方法。为了显示更大更粗的文本,我们可以在前面使用一个标签(# ), Streamlit 会自动改变文本,使其看起来像一个标题(Markdown formatting)。要加粗文本,我们可以简单地用两个星号将我们想要加粗的文本括起来。
接下来,我们可以使用 header 方法创建一个标题,并设置一个函数来获取用户的输入。因为我们正在创建一个技术分析 web 应用程序,所以我们需要一个股票代码、开始日期和结束日期。我们可以将值自动设置在那里,以防用户没有输入自己的值。在这种情况下,我使用苹果公司的股票代码(AAPL),2019-01-01 作为开始日期,今天作为结束日期。
为了使 web 应用程序看起来更好,我们可以创建一个向 Yahoo Finance 发送请求的函数,并检索我们输入的任何股票的公司名称!
我们可以使用 pandas.to_datetime()方法将开始和结束日期从字符串转换为日期时间格式,这样我们就不会在以后出现任何错误。最后,我们可以用前面提到的 Yfinance 模块检索历史股票数据。只是为了跟踪我们目前所处的位置,这是 web 应用程序现在应该呈现的样子。
到目前为止,web 应用程序应该是什么样子!
现在,剩下的工作就是创建我们将要显示的图表。
计算并绘制技术指标!
正如我之前提到的,奇妙的 TaLib 库使得计算我们将要绘制的技术指标变得简单得可笑。对于这个项目,我选择了流行的指标,如移动平均线交叉,布林线,移动平均线收敛发散,商品通道指数,相对强度指数,和总成交量。要想更深入地了解这些指标能显示什么,我建议在Investopedia.com上查找它们。
对于这一部分代码,我们将显示每个指标的三个主要部分。首先,我们必须使用 TaLib 计算指标,然后我们必须设置一个包含指标实际内容的标题,最后,我们可以使用 streamlit.line_chart()显示图表。这就够了!
现在,我们有了一个完全交互式的、实时的、响应迅速的、易于使用的 web 应用程序,只需几分钟,代码不到 100 行。如果你一直遵循这段代码,我们应该有一个类似的网站如下。
网站现在应该是什么样子!
我已经在下面的 GitHub 要点中包含了这个程序的全部代码!
后续步骤
在接下来的步骤中,你可以扩展我们显示的数据,因为 Streamlit 允许用户显示几乎任何类型的数据,包括熊猫数据帧。或者您可以决定使用 Heroku 将它部署到云上(如果您想这么做,请查看汉密尔顿·张的文章)。既然您已经知道如何创建自己的数据 web 应用程序,那么这种可能性真的是无穷无尽的!
非常感谢你的阅读,我希望你喜欢它!
注意:如果您正在将这个特定的应用程序部署到 Heroku,请记住从 requirements.txt 中删除 TaLib,而是为它添加一个build pack!
如果你喜欢这篇文章,可以看看下面我写的其他一些 Python for Finance 文章!
了解如何在不到 3 分钟的时间内解析顶级分析师的数千条建议!
towardsdatascience.com](/parse-thousands-of-stock-recommendations-in-minutes-with-python-6e3e562f156d) [## 用 Python 进行股票新闻情绪分析!
对财经新闻进行秒级情感分析!
towardsdatascience.com](/stock-news-sentiment-analysis-with-python-193d4b4378d4) [## 在 3 分钟内创建一个财务 Web 应用程序!
了解如何使用 Python 中的 Streamlit 创建技术分析应用程序!
towardsdatascience.com](/creating-a-finance-web-app-in-3-minutes-8273d56a39f8)
使用 Python 创建财务仪表板
用 Dash、Plotly 和 Python 构建财务仪表盘
这篇文章将会和我之前的有所不同。我们不会使用 Python 来分析财务数据或评估公司价值。相反,我们将学习如何使用 Plotly 和 Dash 用 Python 创建一个财务仪表板。下面是我们将建立什么样的样本!
使用 Python 的财务仪表板
使用 Python 的交互式仪表盘
Plotly 是一个伟大的免费 Python 库,用于创建交互式图形和仪表板。 Plotly 文档非常棒,对于开始创建我们自己的交互式图形来说已经足够了,不需要了解 HTML、CSS 或 JavaScript。当然,先前的 HTML 知识会对你有利,但这不是必需的。
在这篇文章中,我将介绍拥有一个可工作的交互式仪表板的基本部分。
我们将建立一个非常基本的仪表板,在这里我们将在几秒钟内输入一家公司的股票 和,我们将在屏幕上看到两个财务条形图 显示一段时间内公司的收入和净收入。
一旦你很好地理解了我们在做什么以及 Dash 是如何工作的,你就能够自己创建更高级的仪表板了。
在 Python 中启动我们的财务仪表板—安装 Dash
首先,在开始安装 Dash 的代码之前。我们可以通过在终端中运行下面的命令很容易地做到这一点。如果您已经安装了 Dash,可以跳过这一部分。
pip install dash==1.12.0
很好,有了这一行代码,您已经安装了运行 Dash 所需的所有组件。
Dash 如何工作
我们用 Dash 做的是创建一个将在我们本地服务器上运行的应用程序(我稍后将创建一个帖子,展示如何用 Heroku 在线发布它)。
Dash 应用由两部分组成。一个布局组件,我们将需要输入 HTML 来创建页面的设计。和定义应用功能的第二组件。
为了用 Python 创建我们的财务仪表板,我们将有一个非常基本的布局。让我们开始我们的交互式仪表板,并在下一部分提供基本布局。
创建财务仪表板布局
我们将在一个标准的 Python 文件中编写代码,例如, dashboard_fin.py 。首先,我们导入所有需要的包。使用核心 Dash 功能需要以下四行代码。它们取自 Dash 文档。如果您想了解每个软件包提供的更多信息,请随意查看:
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
核心组件是我们将使用在我们的财务仪表板中创建交互式仪表盘图的组件。这个 HTML 组件将被用来构建和设计页面的布局。最后,我们还从 dash 依赖项中导入输入和输出。
输入将是用户在仪表板中输入的信息。这些输入将进入 Python 函数,以便返回某些数据。
另一侧的输出将成为我们绘制数据的图形占位符。我们将构建一个 Python 函数,它将接受输入,转换输入,并以图形的形式返回输出,以包含在 HTML 布局部分中。
然后,我们将使用请求从 API 获取数据,并使用构建我们的图表。
import requests
import plotly.graph_objects as go
接下来,我们可以通过传递下面的代码行来创建应用程序。这将创建一个包含我们的应用程序的对象:
app = dash.Dash()
最后,我们准备创建页面布局。我们将有一个 H1 标题,然后是一个 Div 部分,包含我们的输入文本框和两个图形:
app.layout = html.Div([
html.H1('Financial Dashboard'),html.Div([
dcc.Input(id='company_selection',value='AAPL'),
html.H3(id='text'),
dcc.Graph(id ='revenue'),
dcc.Graph(id ='netincome'),
],style= {'padding':10})
])
因此,在我们的 Div 部分中有三个元素:
- 输入:文本框,用户将在其中输入要求财务数据的公司名称。
- 图形:这是显示图形的占位符。它将是我们财务仪表板的主要组成部分。图形数据和类型将构建在我们的 Python 函数中。
- H3 :占位符,显示我们正在显示数据的公司的名称。
请注意,每个组件的 id 将在稍后的 Python 函数中用作引用,以便传递数据或输出数据。它是布局和财务仪表板功能之间的链接。
在我们的 Python 财务仪表板中构建功能
现在我们已经准备好了布局,我们可以简单地创建一个函数来获取数据并创建一个交互式图形。我们将使用 financialmodelingprep 来检索公司的财务状况。它每月提供 250 个免费的 API 调用。
对于我们的 Python 财务仪表板,我将只检索公司收入和净收入收入。由于我在之前的帖子中已经多次提到过这个部分,所以我将不再赘述如何检索财务数据的细节。
然后,我们的函数将返回字典形式的数据点,我们将使用 Plotly 来构建图形。更多关于如何绘制图表的细节可以在这里找到。然后,我们使用一个 app.callback() decorator 来将输入和输出链接到我们的 Dash 布局组件。这是让我们的财务仪表板能够互动并响应用户输入的部分。
我们将有两个功能。每个功能都将在我们的财务仪表板中显示单个条形图的数据。第一个,它将绘制一段时间内公司的收入。
[@app](http://twitter.com/app).callback(Output('revenue','figure'),
[Input('company_selection','value')])
def retrieve_revenue(company):
demo = 'your api key'
stock = company
print(stock)
IS = requests.get(f'[https://financialmodelingprep.com/api/v3/financials/income-statement/{company}?apikey={demo}'](https://financialmodelingprep.com/api/v3/financials/income-statement/{company}?apikey={demo}'))
IS = IS.json()
IS = IS['financials']
Revenues = []
Dates = []
count = 0
for item in IS:
Revenues.append(float(IS[count]['Revenue']))
Dates.append(IS[count]['date'])
count += 1
print(Revenues)datapoints = {'data': [go.Bar(x=Dates, y=Revenues)],'layout': dict(xaxis={'title':'Date'},
yaxis={'title':'Revenue'},
)}return datapoints
而第二个函数将绘制公司随时间的净收入。注意,这两个函数几乎是相同的。我们只改变了 id 和字典键来从 API 中提取净收入*。*
[@app](http://twitter.com/app).callback(Output('netincome','figure'),
[Input('company_selection','value')])
def retrieve_revenue(company):
demo = 'your api key'
stock = company
IS = requests.get(f'[https://financialmodelingprep.com/api/v3/financials/income-statement/{company}?apikey={demo}'](https://financialmodelingprep.com/api/v3/financials/income-statement/{company}?apikey={demo}'))
IS = IS.json()
IS = IS['financials']
Revenues = []
Dates = []
count = 0
for item in IS:
Revenues.append(float(IS[count]['Net Income']))
Dates.append(IS[count]['date'])
count += 1
datapoints = {'data': [go.Bar(x=Dates, y=Revenues,marker_color='lightsalmon',name='Net Income')],
'layout': dict(xaxis={'title':'Date'},
yaxis={'title':'Net Income'},
)}return datapoints
仪表板输入-输出链接
这两个函数都返回用户在财务仪表板输入框中请求的公司财务数据。例如,如果用户传递股票代码 AAPL ,Python 函数将把 AAPL 作为输入,并将其传递给 url 请求,以便检索苹果的财务数据。
然后,我们将每年的收入(或净收入)和日期添加到一个 Python 列表中,并将它们传递给 go。条形图图创建一个条形图。
最后,该函数返回一个包含图形数据点和布局的字典,然后在具有相同 id 的 HTML 图形占位符中输出。
为了让用户知道我们正在显示数据的公司的名称,我们可以在 HTML 布局中添加一个链接到我们的 H3 部分的附加函数:
[@app](http://twitter.com/app).callback(
Output(component_id='text', component_property='children'),
[Input(component_id='company_selection', component_property='value')]
)
def update_output_div(input_value):
return 'Displaying Data for "{}"'.format(input_value)
启动财务仪表板
要运行财务仪表板,我们只需添加以下代码行,并从终端运行我们的 python 脚本。
if __name__ == '__main__':
app.run_server(debug=True)
然后,该应用程序将在您的本地服务器上运行于*http://127 . 0 . 0 . 1:8050/。你可以简单地在浏览器中复制并粘贴网址,你应该能看到类似下面的内容。只需在文本框中输入任何一家公司的股票代码,图表就会交互更新!*
带破折号的财务仪表板
现在,您应该准备开始构建自己的更高级的仪表板了。订阅我的社交媒体频道,在我的下一篇文章中了解如何在线发布仪表板。
查看我一步一步解释代码的视频教程:
***import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output import requests
import plotly.graph_objects as goapp = dash.Dash() app.layout = html.Div([
html.H1('Financial Dashboard'),
html.Div([ dcc.Input(id='company_selection',value='AAPL'),
html.H3(id='text'), dcc.Graph(id ='revenue'),
dcc.Graph(id ='netincome'), ],style= {'padding':10})
])
@app.callback(Output('revenue','figure'), [Input('company_selection','value')])
def retrieve_revenue(company):
demo = 'your api key'
stock = company print(stock)
IS = requests.get(f'https://financialmodelingprep.com/api/v3/financials/income-statement/{company}?apikey={demo}')IS = IS.json()
IS = IS['financials']
Revenues = []
Dates = []
count = 0for item in IS:
Revenues.append(float(IS[count]['Revenue']))
Dates.append(IS[count]['date'])
count += 1 datapoints = {'data': [go.Bar(x=Dates, y=Revenues)],'layout': dict(xaxis={'title':'Date'}, yaxis={'title':'Revenue'}, )} return datapoints @app.callback(Output('netincome','figure'), [Input('company_selection','value')])
def retrieve_revenue(company): demo = 'your api key'
stock = company
IS = requests.get(f'https://financialmodelingprep.com/api/v3/financials/income-statement/{company}?apikey={demo}')
IS = IS.json()
IS = IS['financials']
Revenues = []
Dates = []
count = 0 for item in IS:
Revenues.append(float(IS[count]['Net Income']))
Dates.append(IS[count]['date'])
count += 1datapoints = {'data': [go.Bar(x=Dates, y=Revenues,marker_color='lightsalmon',name='Net Income')], 'layout': dict(xaxis={'title':'Date'}, yaxis={'title':'Net Income'}, )}return datapoints @app.callback( Output(component_id='text', component_property='children'), [Input(component_id='company_selection', component_property='value')] )
def update_output_div(input_value):
return 'Displaying Data for "{}"'.format(input_value)if __name__ == '__main__': app.run_server(debug=True)***
原载于 2020 年 6 月 14 日 https://codingandfun.com。
创建一个 Flask 应用程序,使用自然语言处理对消费者投诉进行分类(使用 TFIDF/Word2Vec 进行多类分类)
在本系列的第 1 部分中,我将介绍获得产生这些分类的工作模型的工作流程。这篇文章将在第 2 部分继续,其中涉及到构建这个模型的 Flask 应用程序。因此,如果你只是对烧瓶部分感兴趣,点击 这里(仍在生产) 前往那个职位!
事不宜迟,让我们直接进入本系列的第 1 部分。
I am dissatisfied with the current outcome of a dispute that was initiated with Discover Card regarding a single transaction that occurred on XXXX/XXXX/2015 in the amount of {$280.00}. I have corresponded with Discover Card at least four times since XXXX/XXXX/2015 ( which I have enclosed as an attachment to this complaint ). I believe that the credit card issuer has violated consumer protection laws by failing to implement the Special Rule for Credit Card Purchase protection despite overwhelming paperwork evidence submitted by me that shows the merchant has conducted business in bad faith less favorable to the consumer. I have sustained a monetary loss as a result of merchants bad faith and intent. I have patiently utilized the internal Discover Card dispute process over the past three months with the credit card issuer always favoring the merchant ; I have repeatedly submitted irrefutable paperwork evidence that has shown that the merchant has conducted business in bad faith. I have tried in good faith to address my complaint with the merchant and Discover Card but believe that I will not receive a favorable outcome. I suffered a work-related injury in XXXX and am now permanently XXXX. My income has dropped substantially in the past 5 years and I am now on a low fixed income. On XXXX separate occasions from XXXX to XXXX I had zero income and had to use my Sams Club card to purchase food, thus running up debt out of necessity. I am currently in the process of attempting to pay down my Sams Club ( Synchrony Bank ) card, and stopped using the card some time ago. I have always made at least minimum payments and have never missed a payment. Despite this, my interest rate has been unilaterally raised three times in the past two years, and is now at 23.15 %. I called a Sams Club account rep today to file a complaint over the phone, because I am never going to be able to pay down this card when I am paying almost {$50.00} a month in interest and can only afford to pay the minimum payment + {$4.00} or {$5.00} dollars. They would not work with me, which I expected. In my opinion, Synchrony Bank is taking unfair advantage of recent interest rate hikes to gouge customers, especially those who are financially unable to make substantial monthly payments on their accounts. Therefore I am contacting the CFPB to file a complaint through which I might receive some relief.
停下来。
你读了上面的文字墙了吗?很可能你没有(因为我也不会)。谁有时间去阅读一整块没有标题、没有摘要和糟糕的格式(粗体、空格等等)的文本呢?)可能与你无关?
如果你真的读了上面的文字,你会意识到它们实际上是客户投诉。正如盖茨先生曾经提到的:
如果他这么说,那一定是真的。(图片来源:图片 via me.me )
我们可以从顾客的投诉中学到很多东西。最好的品牌总是与他们的消费者联系在一起,并围绕他们做出决定(想想派拉蒙,因为糟糕的评论和随之而来的无数迷因,派拉蒙实际上推迟了索尼克电影的上映日期,并改变了我们最喜欢的蓝色刺猬的设计)。
真正可怕的设计(左),但值得称赞的是你,派拉蒙,重新设计(右)和倾听你的观众(来源:汤姆·巴特勒通过 sg.news )
理解你的客户固然重要,但这并不能改变这样一个事实:愤怒的客户发来的一大堆文字不仅让人难以通读,还会让人失去动力。很多时候,这不仅仅是一个投诉。
好的,是的,如果你仔细看,他们都是同样的抱怨。但是你明白我的意思。
通常,在银行(或任何大公司的客户服务部门),他们每天都会看到数以千计这样的投诉,它们不会像上面这些投诉那样措辞和格式令人愉快。
客户投诉的实际图像(来源:via yandex )
此外,消费者自己可能会将投诉归入错误的部门,而审查每一项投诉并将其转交给相关部门处理将是一项巨大的痛苦。不用说,这些缓慢的处理时间也会导致对客户的缓慢响应时间,从而导致您已经愤怒的客户变得更加愤怒。
伙计,如果有什么方法可以快速分类每一个抱怨来加快你的阅读过程就好了。
项目目标
事实证明是有的,这正是这个项目的目的。在我的项目中,我开发了一个模型,可以根据产品类型对客户投诉进行正确分类,准确率达到 83%。
该应用程序利用机器学习算法结合自然语言处理(NLP)方法来处理文本,以预测投诉所指的产品类型。
定义问题
这本质上是一个 监督(标签) 文本多类分类问题,我们的目标是用一个新的输入(投诉)进行预测(分配到正确的类别)。在下面的步骤中,我将带您完成以下内容:
- 数据探索(了解数据集)
- 数据(文本)预处理和在 NLP 项目中可能采取的一般步骤
- 型号选择以及为什么使用 Micro-F1 作为评估指标
- 选定型号的最终测试和结果
在本系列的第 2 部分中…
- Flask 应用程序演示&如何构建一个
这个项目的所有代码都可以在我的 github 上找到。如果你需要任何澄清,请随时给我留言。
第一部分:数据
用来训练这个模型的数据取自 这里 。并且对每一列的含义的解释可以在 这里 找到。它本质上是一个由消费者金融保护局(CFPB)提供的关于金融产品和服务投诉的标签数据集。
我们观察到数据集包含 1,437,716 行数据,以及许多许多包含空值的行。
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1437716 entries, 0 to 1437715
Data columns (total 18 columns):
Date received 1437716 non-null object
Product 1437716 non-null object
Sub-product 1202551 non-null object
Issue 1437716 non-null object
Sub-issue 887045 non-null object
Consumer complaint narrative 463991 non-null object
Company public response 530104 non-null object
Company 1437716 non-null object
State 1414039 non-null object
ZIP code 1304830 non-null object
Tags 196144 non-null object
Consumer consent provided? 831624 non-null object
Submitted via 1437716 non-null object
Date sent to company 1437716 non-null object
Company response to consumer 1437715 non-null object
Timely response? 1437716 non-null object
Consumer disputed? 768482 non-null object
Complaint ID 1437716 non-null int64
dtypes: int64(1), object(17)
memory usage: 197.4+ MB
然而,由于我们只分析投诉本身,我们只需要Product
和Consumer Complaint Narrative
列。
在只保留我们需要的列、删除空值并将它们重命名为更好使用的名称之后,我们只剩下 463,991 行数据:
<class 'pandas.core.frame.DataFrame'>
Int64Index: 463991 entries, 4 to 1437711
Data columns (total 2 columns):
PRODUCT 463991 non-null object
CONSUMER_COMPLAINT 463991 non-null object
dtypes: object(2)
memory usage: 10.6+ MB
在PRODUCT
列上做一点 EDA(探索性数据分析),我们观察到 18 个类,分布如下:
然而,有些类别(产品)是重叠的。例如,Credit card or prepaid card
与Credit card
和Prepaid card
冲突。此外,还有一个问题是要对 463,991 行执行分析(我怀疑您的笔记本电脑是否能够在这个项目的后期处理和模型训练。我的不行)。
在删除了一些产品类别、合并了一些类别并减少了 90%的观察数量之后(具体情况可以在我的 jupyter 笔记本上的我的 github 中找到),我们还剩下大约 30,000 行投诉(对于 8GB ram 的笔记本电脑来说,这是可以管理的)。
<class 'pandas.core.frame.DataFrame'>
Int64Index: 31942 entries, 72 to 1437623
Data columns (total 2 columns):
PRODUCT 31942 non-null object
CONSUMER_COMPLAINT 31942 non-null object
dtypes: object(2)
memory usage: 748.6+ KB
具有以下分布:
不算太寒酸。现在我们准备对我们的投诉数据进行一些文本预处理!
第 2 部分:文本预处理
第 2.1 节:简单地说,自然语言预处理
现在我们有了数据,我们需要完成清理过程。对于一般的文本数据集,这通常包括:
- 删除标点符号
- 删除停用词(如“the”、“this”、“what”)
- 小写字母
- 标记化
- 词干化/词尾化(通过删除诸如“-ed”、“-ing”等后缀,将单词简化为基本形式)
如果您正在处理在线文本和评论,您将需要进一步删除超链接、用户名和自动消息等项目。
您所做的预处理因项目而异,为此,除了词干化和词汇化之外,我取得了最好的准确度分数。我考虑过使用二元语法,但是这导致我经常遇到内存问题,因此我只能考虑一元语法(标准的单词标记化)。
下面是一个删除标点符号、删除停用词、小写字母及其后的标记化的示例。
删除标点符号
停用词删除
用小写字体书写
标记化
第 2.2 节:矢量化
为了将单词转化为机器学习算法可以理解和处理的东西,我们需要做一些叫做 矢量化 的事情。简而言之,这是将单词转化为多维向量的过程,其方式是将单词的含义或上下文与向量指向的位置相关联。从某种意义上来说,矢量化允许计算机通过将相似的词义映射到相似的向量空间来量化词义。
第 2.3 节:词频—逆文档频率矢量化
对于这个应用程序,我们需要基于上下文的矢量化。这就是术语频率逆文档频率矢量化的用武之地(TF-IDF)。这种矢量化方法查看一个单词在注释中出现的次数相对于它在其他注释中出现的次数。两件事导致 TF-IDF 得分更高:
- 该词在被评分的具体投诉中出现的频率较高。
- 该词在所有其他投诉中出现的频率较低。
第 3 部分:培训方法
第 3.1 节:模型评估分数
在多类场景中,按照二进制分类不再像 Precision、Recall 和 F1 那么简单(就像我之前的项目对违约者进行分类)。这是一个全新的水平。如果您需要更好地理解多分类场景中模型使用的评估指标,我建议参考 Boaz Shmueli 关于多分类器中评估指标的解释。他很好地解释了关于精确度、召回和 F1 评分的 二进制 和 多类 分类指标。
和这个多分类案例一样,我决定使用 Micro-F1 作为模型间的评价指标,因为它衡量模型的 整体精度 。
第 3.2 节:使用的模型
我已经决定使用以下模型进行多类文本分类:
- 多项式朴素贝叶斯
- 高斯朴素贝叶斯
- 逻辑回归
- 随机森林
- 线性 SVC
第 4 部分:型号选择
每个模型都在分层的***【5 折分裂训练-验证(整个数据集的 80)*上交叉验证。剩下的 20%的数据被保留下来,用于模拟我们最终选择的模型在现实生活中的表现,在现实生活中,它必须处理从未见过的输入。
像往常一样,如何做到这一点的代码可以在我的 jupyter 笔记本(笔记本的第 3 部分)的 github 上找到。
从我们的初步培训结果中,我们看到:
逻辑回归给了我们 83%的最高准确率。
83%是相当高的准确率。但是我们能做得更好吗?我们不能确定是否会更好,但我们可以一直尝试,努力改进。
第 4.1 节:斯坦福手套和谷歌的 Word2Vec
我们可以在将投诉输入到我们的分类器之前,利用嵌入在投诉上的单词,而不是利用矢量化(我们上面使用的 Tf-idfVectorizer)。要了解更多关于 WordEmbeddings 的内容,你可以在这里找到它。
一些方法,如 Word2Vec 和 spaCy 涉及预先训练的、数千兆字节的模型,这些模型在数十万(如果不是数百万的话)文档上训练,然后将单词的含义减少到一组数百个数字。一般来说,这些模型在保持单词的上下文和含义方面非常出色,但是它们速度慢且庞大。
对于这个项目,我选择了使用斯坦福德的手套(越来越复杂)和谷歌的 Word2Vec。
第 4.2 节:tfidf 矢量器和 word 嵌入结果
综合所有测试结果,我们发现普通 TFidfVectorizer 比 WordEmbeddings 要好得多,最高准确率为 83%。
因此, 使用逻辑回归的 TfidfVectorizer被选为本项目使用的文本预处理和模型组合。
第 5 部分:在看不见的数据上测试我们最终选择的逻辑回归分类器
还记得我们之前保留的没有在交叉验证中使用的 20%的数据吗?接下来,我们使用它来评估我们的模型在看不见的数据上的表现(即,它在现实生活中的表现)。
这个模型在看不见的数据上给了我们 83%的准确率! 表示它可以在 83%的时间内正确地对投诉进行分类!现在我们有了自己的模型,我们终于可以看到它的实际应用了!
第 6 部分:烧瓶演示
为了能够以用户友好的方式使用我们的模型,我创建了一个 Flask 应用程序,允许输入投诉。此后,该应用程序将预测投诉属于哪个产品类别。参考这个 post (WIP) 了解一下 Flask app 是怎么创建的!
*I am dissatisfied with the current outcome of a dispute that was initiated with Discover Card regarding a single transaction that occurred on XXXX/XXXX/2015 in the amount of {$280.00}. I have corresponded with Discover Card at least four times since XXXX/XXXX/2015 ( which I have enclosed as an attachment to this complaint ). I believe that the credit card issuer has violated consumer protection laws by failing to implement the Special Rule for Credit Card Purchase protection despite overwhelming paperwork evidence submitted by me that shows the merchant has conducted business in bad faith less favorable to the consumer. I have sustained a monetary loss as a result of merchants bad faith and intent. I have patiently utilized the internal Discover Card dispute process over the past three months with the credit card issuer always favoring the merchant ; I have repeatedly submitted irrefutable paperwork evidence that has shown that the merchant has conducted business in bad faith. I have tried in good faith to address my complaint with the merchant and Discover Card but believe that I will not receive a favorable outcome.*
还记得我们在帖子开头看到的这面可怕的文字墙吗?让我们来测试一下我们的模型。花点时间读读它,猜猜它属于哪一类。
现在让我们来测试一下我们的应用程序。
有没有得出和 app 一样的结论?现在让我们用一个更难的投诉来试试,充满了错别字、大写字母和千年行话。
*Omg where is my money?????? AOSINIONSAD WHY DID YOU GUYS TAKE MY MONEY AWAY. SAOIDNSIOADNOIAODNNIOASDNSADNOSDNOASDNI TRANSFERRED IT LAST NIGHT AND I WOKE UP TO NOTHING IN!!!!!! MY BANK ACCOUNT. HELP PLEASE!!!!!!!!!!!!!!!!!!!!!!!!!!!!!I NEED THE MONEY OR ELSE I WILL BE HUNTED BY LOAN SHARKS!!!!!!!!YOU *(#&$) PEOPLE HAVE NO RIGHT TO DO THIS TO ME!!!! I NEED MY MONEY!!!!*
我们看到该应用程序可以正确无误地处理混乱的投诉,并正确地将它们归类到哪个产品类别。
第 7 部分:应用优势
使用这个应用程序可以带来很多好处,从解放人力到提高筛选客户投诉的效率。
第 8 节:未来的工作
作为对该应用程序的增强,以提高其实用性,我们或许可以研究以下内容:
话题总结 目前 app 只将投诉归入正确的产品类别。虽然它确实将投诉归入了正确的类别,但客户投诉专员仍然需要通读投诉本身。这将是很好的应用程序也能够提供一个简短的投诉是什么的摘要。这将是一个有趣的话题,可以研究和设计如何创建这个功能。
投诉优先级 我们还可以考虑实施情感分析,以提供某种投诉优先级。听起来更愤怒的投诉(即愤怒的客户)可以被给予更高的优先级,因为愤怒的客户往往会提供最多的问题。因此,我们的应用程序将为客户投诉官自动排序,以确定哪些投诉需要紧急处理。
- 问是否有人愿意一起工作,可以给我发短信
第九节:离别赠言
这绝不是一篇短文,希望在你的数据科学之旅中是一篇信息丰富的文章。感谢您花时间阅读到这里。
如果你学到了什么,觉得这很有帮助,或者认为它可能会帮助这个领域的人,请与他人分享这篇文章!
和往常一样,在 xianjinseow92@gmail.com,你可以随时通过我的 Linkedin 或 gmail 联系我!
干杯。
创建一个全自动的每日幻想运动策略
虽然我在 DFS 中没有盈利——但我非常接近,并且很开心地制定了一个策略,在实时 NBA DFS 比赛中部署真金白银。
DFS 自动化策略的最后一步:在 FanDuel 上自动加入比赛。由于该网站是基于 JS 的,我们必须模拟一个真正的网络浏览器,并点击网站进入竞争
2018 年末,我(在我哥哥扎克的帮助下)着手创建一个全自动的每日幻想体育(DFS)战略。我的目标是在假期期间做一个有趣的兼职项目,而不是赚钱——我从来没有玩过任何日常幻想游戏,但我是一个半铁杆篮球迷,也是一个铁杆数据迷。
下面是我如何在 2018 年底的两个月时间里制定了一个 DFS 战略的故事,虽然不太有利可图,但完全不干涉,超过了平均的 DraftKings 球员,总体胜率为 40%,并在 2018-19 NBA 赛季的前 5 个月中的 2 个月实现了盈利。
从一开始,我就列出了这个项目的一些要求。我最初将范围限制在 NBA 双打和 DraftKings 上的 50/50 比赛。
要求
- 自动化:整个流水线都要自动化,包括参赛。
- 可回溯测试:策略应该是完全可回溯测试的。我们应该能够验证对管道的任何部分所做的任何更改都会带来比过去更好的性能。
- 人工干预:不需要任何“专家知识”。例如,如果勒布朗在与科怀比赛时一直表现不佳,策略应该找出这一点,而不是被明确告知。
- 可复制:使用的任何数据都应该是公开信息(即没有订阅服务,没有像现金额度这样的私人历史数据)。
- 可扩展:管道应该扩展到多个平台和运动,只需最少的额外工作。
- **快速:**开发应该是流畅的——管道的每一步都应该能够独立运行,长时间运行或 I/O 密集型的部分应该很少执行并且离线。
管道
这里有一个“实时模式”管道步骤的概述,或每天运行的进入幻想联盟的过程。“模拟模式”(回溯测试)只是其中一些步骤的重组和循环——我们将在后面讨论。
“实时模式”管道步骤
- 统计数据检索:我们从检索历史数据开始,我们将使用这些数据作为每个玩家预测的基础。对于 NBA 来说,这包括从篮球参考收集的每场比赛的球员和球队统计数据,以及辅助数据,如投注线和预测的首发阵容。
- **特征创建:**使用这些统计数据,我们构建派生的特征,这将是学习算法的基础。
- **模型拟合:**使用这些衍生特征,我们拟合一个模型,该模型创建了从特征到幻想点的映射。
- **花名册检索:**检索当天符合条件的球员、伤病情况、比赛,以及最重要的每位球员的成本(薪水)。
- **预测特征创建:**应用我们用来创建特征的相同过程,将花名册数据转换成我们用来创建球员模型的完全相同的特征。
- **预测:**使用我们之前创建的模型和预测功能,我们进行幻想点预测。
- **团队选择:**我们运行一个线性优化(根据薪水和职位限制最大化预测的幻想点数)来产生我们将加入的团队。
- **联赛参赛:**我们使用一个 Selenium 机器人来导航幻想场地,找到目标比赛(例如,50/50 全石板,25 美元参赛费),并进入我们的团队。
在接下来的几节中,我将深入一些(希望如此)有趣的具体步骤。
特征创建
在理想世界中,我们可以给一个学习算法原始数据,然后弹出我们想要的信息(幻想点预测)。这是图像识别和自然语言处理等许多领域的当前最新技术。虽然对于这个问题来说这很有可能,但我采用了创建手工特征的方法,提前“手动”提取高阶信息,而不是让学习算法来做这件事。
为了创建这些特性,我构建了一个名为“特性框架”的工具,它允许我们的特性被表达为一个有向无环图 (DAG)。注意,这不是我的新想法,这是研究驱动交易的常见模式。这种方法有两个主要优点:
- 我们不必为依赖于相同或重叠子特征的多个特征重复计算。
- 框架一起处理连接特性——我们不必明确地说幻想点需要在幻想点之前计算。
计算的 DAG 表示(1 + 2) * 4 = 12
案例分析:防守 vs 站位
我们将“防守对位置”(DVP)定义为在预先定义的时间范围内,对手在给定位置允许的历史得分。在 NBA DFS 中,如果对手通常允许针对给定位置的超大分数,这可能会很有趣(想象一下霍福德对约基奇的比赛)。
DvP 特征的 DAG 表示。请注意,尽管许多要素都依赖于这种计算,但幻想点只计算一次。
当我们完成特征创建过程时,我们将有一个二维矩阵(玩家-游戏特征)用于学习算法:
上面的列是特征值的例子,其中行对应于唯一的玩家-游戏组合。我们明确地不给算法“过度指定”的信息,比如该行对应于哪个玩家。
模型创建
该模型的目的是考虑特征(具体来说,玩家历史表现的各个方面)并预测他们在给定游戏中会获得多少幻想点数。我们使用所有可用的历史数据来拟合该模型,2018-19 赛季的历史数据约为 60,000 行(也使用 2017-18 年的数据)。
需要注意的是,在这个问题的表述中,我们试图最小化(预测的优点 - 实现的优点)。这至少有两个潜在的缺点:
- 这种方法对每个玩家和每个游戏都一视同仁,而实际上,有些玩家可能比其他人更重要。
- 最小化玩家预测的误差可能与赢得比赛没有直接关系。
这里有一个关于前 5 个游戏的跟踪幻想点数的线性回归的输出权重的例子,这是我们能做的最简单的模型。交叉验证的平均误差(MAE)为 6.4 幻想点。
简单变量的线性回归系数——这些系数验证了我们的直觉,即最近的游戏更能预测性能
非线性拟合
我们可以通过使用更现代、也是最重要的非线性拟合算法来大幅改善这些拟合结果。在尝试了许多非线性模型,如 SVM、神经网络和决策树之后, XGB 模型表现最佳。这并不令人惊讶,因为 XGB 经常在关于表格数据的 Kaggle 获奖条目中使用。即使在这一组简单的功能上,XGB 也将交叉验证的 MAE 降低到大约 6.2 。在全套功能上,我们使用 XGBoost 可以实现每个玩家只有**5.38fantasy points**的平均误差。相比之下,FanDuel 的股票预测误差更接近于 6.0 ,在评估付费订阅模式时,我找不到任何低于 5.5 的预测。
XGB 回归函数下特性的相对重要性——有趣的是,我们的衍生特性(如“相对速度”)和其他数据挖掘特性(如“投注线”)正在增值!
预言;预测;预告
预测是将先前拟合的模型应用于日数据以获得幻想点预测的流水线步骤。对于线性回归模型,计算是简单的点积:
线性回归预测是特征系数的简单点积*
XGB 预测是通过遍历决策树进行的。下面是 XGB 模型对 2019 年 1 月 30 日艾尔·霍福德幻想点的预测(27.6)的细分。
2019 年 1 月 30 日艾尔·霍福德 XGB 预测分解。
回溯测试
许多(通常比我好得多的)DFS 玩家采取的方法中的一个陷阱是无法评估他们的策略改变是否成功。如果没有回溯测试的能力,可能就没有足够的数据来自信地说变革是有益的。为了解决这个问题,我确保管道的每一部分都可以在过去的任何日期重新运行(当然不考虑未来)。以下是模拟的步骤:
模拟工作流—我们可以并行化机器中的部件(每天模拟),这意味着只要有足够的计算,工作流就和“实时模式”版本一样快!
为了确定过去比赛的现金额度,我们可以使用 RotoGrinders ResultsDB 从 DraftKings 的“双倍”比赛中获得历史现金额度。请注意,当我在管道上工作时,我使用了我参加的 Fanduel 50/50 竞赛的私有结果。
回溯测试工作流程如下:
- 创建可能具有某种预测能力的特征(例如,DvP)
- 检查模型误差的一些度量是否减少
- 验证历史比赛成绩有所提高
2018-19 赛季模拟胜率。管道开始时相当强劲,在 60 天内赢得率超过 60%,然后稳定在 40%左右。
虽然我没有记录我所做的改进,但是回溯测试框架允许我们迭代地添加回特性,以查看它们如何改进我们的性能。从这些结果中,我们可以验证:
- 新功能减少了模型误差
- 模型误差减少导致竞争绩效提高
拟合误差(MAE)与模拟胜率。我们可以看到,拟合误差越低,模拟成功率越高。
最终,我们的模型在整个赛季中取得的最佳成绩是 39%的胜率,其中 50%在我们评估的双倍现金额度中是有利可图的(在 50/50 竞争中,截止值为约 56%)。我们在赛季的前两个月也做得更好。我推测,算法没有考虑到的功能,如球员匹配,在赛季结束时变得越来越重要。
2018 赛季的累计胜率:模拟胜率为 39.16%,预测胜率为 54%——根据 DraftKings 的统计,比普通玩家要好!
其他 DFS 的经验教训
如果你阅读这篇文章是为了学习如何成为一名盈利的 DFS 玩家,很遗憾我不能帮你实现这个目标——但是我可以分享我学到的一些东西,这些东西似乎对(在某种程度上)具有竞争力是必要的。很可能通过一些额外的“秘方”或预测的调整,一个经验丰富的玩家可以在一个几乎自动化的系统下变得有利可图。
- 有时你可能真的认为一个想法很棒,但它并没有改善你的策略,甚至导致倒退!
- ***首发阵容:*你必须得到正确的首发阵容/后期抓痕才有机会参赛,即使每五场比赛中有一场搞糟也能侵蚀你的优势。
- 受伤&替补球员: DFS,尤其是 NBA 50/50,通常会选择一个球员,考虑到他们的工资和球队中新受伤的球员。如果一个球员因为他上面的先发球员受伤而比正常情况下多打了 3 分钟,你需要让他在你的阵容中才有机会竞争。DFS 的竞争如此激烈,以至于这些“不用动脑筋”的选择(通常是你可能没听说过的球员)通常在 50/50 中拥有 80%以上的所有权。**
- ****位置可以是一个有噪声的变量:位置,以及由其产生的所有特征(如 DvP)都可以是有噪声的。像勒布朗这样的球员可以被列为职业球员,而事实上在某些情况下,他可能更像一个职业球员。我们真正想要提取的东西可能因用例而异,但通常我们更感兴趣的是匹配,而不是特定的位置,这可能是一个稀疏的数据点,人类可以更好地分析它。确定“可能的匹配”的算法可能是这个子问题上的一个进步。
- ****DFS 很难:根据 DraftKings 的数据,只有 14%的玩家在一个月内盈利。这个自动化系统在 2018-19 赛季的 5 个日历月中有 2 个月盈利,这使得它远远领先于普通人类玩家。即使有一个能给出不错的基线预测的系统,你仍然需要在球场上有很大的优势来克服 DraftKings 和 fan 决斗在 50/50 和双冠王联赛中约 8%的差距。
结论
目前,DFS 问题可能需要大量的人工努力才能解决。尽管我们能够在 DraftKings 上取得超过普通玩家的表现,但我们离盈利战略还有很长的路要走。最重要的是,我从构建这个项目中获得了很多乐趣,并了解了很多关于 DFS 和机器学习的实际应用。如果任何读者对该项目有任何疑问,请随时与我们联系!
为 Instacart 创建杂货产品推荐器
斯科特·沃曼在 Unsplash 上的照片
使用 K-Means 聚类和关联规则挖掘向 Instacart 客户推荐产品
在电子商务购物体验中,产品推荐有多种形式:它们可能用于在一个产品的页面上推荐其他产品(例如,亚马逊的“经常一起购买”功能),或者它们可能用于结账页面,根据客户的总订单向他们显示他们可能感兴趣的产品。在这篇文章中,我将详细介绍我是如何为前一种情况制作推荐器的。
此外,如果推荐是针对特定的客户群,而不是统一的,可能会更有帮助。例如,如果一组顾客倾向于购买大量的非乳制品牛奶替代品,而另一组顾客倾向于购买传统牛奶,那么对这盒麦片提出不同的建议可能是有意义的。为了提供量身定制的推荐,我首先使用 K-Means 聚类根据 Instacart 用户的购买历史对他们进行细分,然后根据这些聚类中的产品关联规则提供推荐。在这篇文章中,我将介绍这个过程,并给出一些推荐的例子。
数据和 Instacart 背景
Instacart 是一种在线杂货交付服务,允许用户通过他们的网站或应用程序订购杂货,然后由个人购物者完成并交付——非常类似于 Uber Eats,但用于杂货店。2017 年,他们发布了一年的数据,其中包括来自约 20 万客户的约 330 万份订单。以关系数据库的形式发布,我将单独的表组合在一起执行 EDA,留给我以下内容:
EDA
执行 EDA 时,我想回答一些关键问题:
- 最受欢迎的产品和过道有哪些?
- 产品组合有多“头重脚轻”?即排名靠前的产品占总订购份额的多少?
- 订单有多大?
为了大致了解 Instacart 倾向于销售什么,我们可以听从他们的部门销售。Instacart 有 21 个部门位于其产品分类的顶端。以下是他们各自的单位销售份额:
Instacart 似乎是农产品的热门选择。最受欢迎的 Instacart“通道”,其分类中的下一层是水果和蔬菜通道,下图显示了前 10 个通道在总销售额中的单位份额:
总共有 134 个通道,共有 49685 件产品,因此上图显示了产品销售的“头重脚轻”分布,前 3 个通道占售出产品的 25%以上。下图显示了前 3 种产品的单位份额,遵循相同的趋势:
几乎所有排名前 30 的产品都是农产品,在最受欢迎的产品之后,市场份额急剧下降。看看 K-Means 聚类是否可以从这种以农产品为主的分布中揭示出不同的客户群体,这将是一件有趣的事情。
以下是订单大小的描述性特征:
count 3.346083e+06
mean 8.457334e+01
std 1.355298e+02
min 1.000000e+00
25% 1.500000e+01
50% 3.600000e+01
75% 1.050000e+02
max 1.058500e+04
这是一个分界点为 500 单位的分布图:
图表表明,考虑到订单规模,Instacart 可能还有改进的空间——右偏分布表明,大多数订单可能无法满足各自客户的所有杂货需求,其中一半订单的商品数量少于 36 种。产品推荐系统将让顾客更容易找到他们想要的产品,并向顾客展示他们从未在 Instacart 上购买过的商品。
主成分分析和 K-均值聚类
K-Means 聚类的目标是根据客户过去购买的产品将客户分组。为了实现这一点,我打算对从每个客户以前的订单总和中购买的单位份额实施 K-Means。我的第一步是将前面显示的组合表转换成一个表,其中每行代表一个客户,列代表从每个通道购买的份额。下表中有一个示例:
然后,我执行 PCA 来减少 K-Means 算法的特征数量。这将允许我更好地可视化我的集群,并帮助 K-Means 更有效地运行。在决定要使用的成分数量时,我参考了每个成分代表总方差的方差%,并在边际方差相加后选择了一个截止值:
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt
pca = PCA(n_components = 30)
principalComponents = pca.fit_transform(aisle_share_pivot)features = range(pca.n_components_)
plt.bar(features, pca.explained_variance_ratio_, color = 'black')
plt.xlabel('PCA features')
plt.ylabel('variance %')
plt.xticks(features)
plt.xticks(rotation = 45)PCA_components = pd.DataFrame(principalComponents)
根据图表,发生这种情况的部件是部件 5。然后,我将 sci-kit-learn 的 K-Means 算法应用于 6 个主成分分析,并查看了由此产生的 SSE 曲线:
from sklearn.cluster import KMeans
import matplotlib.pyplot as plt
sse = {}
labels = {}
for k in range(2,15):
kmeans = KMeans(n_clusters = k).fit(PCA_components[[0,1,2,3,4,5]])
sse[k] = kmeans.inertia_
labels[k] = kmeans.labels_
plt.figure()
plt.plot(list(sse.keys()), list(sse.values()))
plt.xlabel("Number of cluster")
plt.ylabel("SSE")
plt.show()
看起来曲线在第 5 组开始变平,所以我用第 6 组向前移动。以下是绘制在 6 种主成分散点图矩阵上的聚类:
虽然并不完美,但我似乎已经确定了 6 个不同的群体,它们应该会导致过道购买历史的差异。当然,检查这一点的最佳方式是查看每个集群的通道单位购买份额。以下是 Instacart 前 20 名通道的购买份额热图:
这 6 个群体有明显的差异,最明显的是他们购买新鲜水果和新鲜蔬菜的相对数量。鉴于农产品占 Instacart 单位销售额的 25%以上,这是有道理的。每个集群的差异可以通过单独查看来更好地显示出来,我在下面的图表中这样做了:
聚类 0 是重度蔬菜购物者,聚类 1 似乎主要使用 Instacart 购买饮料,聚类 2 有更均衡的调色板,聚类 3 更喜欢包装产品,聚类 4 是重度水果购物者,聚类 5 几乎平等地购买新鲜水果和蔬菜。在购买频率较低的通道中也会发现差异,例如,“婴儿食品配方”在第 5 类中的购买频率排名第 8,但在其他任何类中均未出现在前 10 名中。
Instacart 的业务也感兴趣的是这些集群在用户数量和购买力方面的规模。下表在左侧列中显示了属于每个集群的用户百分比,在右侧显示了属于每个集群的单位购买百分比。
有趣的是,集群 5 代表了大约 35%的用户,但几乎 50%的单位购买量。回想一下,该集群购买最多的通道是新鲜水果和新鲜蔬菜,但数量相等,并且在其前 10 个通道中还有婴儿食品配方。这表明该集群可能包含使用 Instacart 为有婴儿和幼儿的家庭购物的用户,他们似乎是 Instacart 最重要的客户。整个项目可能会进一步进行这种分析,以隔离 Instacart 的最佳客户!然而,在这一点上,我继续创建产品推荐器。
关联规则挖掘
随着 200,000 个用户被划分到集群中,我准备通过对订单进行关联规则挖掘来执行购物篮分析。这是通过将总订单表拆分成 6 个不同分类的 6 个表,并找出每个产品之间的关联规则来实现的。关联规则根据产品在同一订单中被一起购买的可能性来指定产品之间的关系。
三个常见的规则是支持,信心和电梯。支持度就是项目集在数据集中出现的频率,计算方法是将频率除以日期集的大小(通过 Wikipedia):
置信度是包含一个项目的交易与包含另一个项目的交易的比例,计算方法是将一个或多个项目的支持度除以分子子集的支持度,根据维基百科:
提升是两个或两个以上项目的观察频率与预期频率之比。它表明两个或多个项目出现的频率是否比它们随机一起出现的频率更高。大于 1 的值表示非随机关系。下面的公式:
我使用一个采用生成器的 python 脚本,针对最低支持度超过 0.01%的所有项目,按集群计算了这些指标。鉴于数据集的规模(330 万份订单,包含约 50,000 种产品),这是必要的。下表显示了群集 5 按提升排序的输出:
可以看出,整个数据集的最高提升值是彼此相似的产品,这是可以预期的。
产品推荐者
为了执行要在产品页面上显示的产品推荐,我编写了一个 python 脚本,它接受 user_id、product_id、所需的提升截止值和要返回的 num_products。通过这些输入,它确定用户的聚类(存储在从聚类分析输出的数据帧中),过滤包含该聚类的产品关联规则的数据帧,并返回提升大于提升输入的指定数量的产品,优先考虑提升最大的项目。如果符合条件的商品数量少于 num_products,它将返回所有符合条件的商品。推荐器的代码可以在 Github 知识库中找到,链接在文章的末尾。
在下面,我展示了推荐器的运行情况,展示了 5 个聚类的“有机全脂牛奶”的输出,每个聚类限于 5 个项目。
cluster 0['Whole Milk Plain Yogurt', 'YoBaby Blueberry Apple Yogurt', 'Organic Muenster Cheese', 'Apples + Strawberries Organic Nibbly Fingers', 'Yo Toddler Organic Strawberry Banana Whole Milk Yogurt']
cluster 1['Beef Tenderloin Steak', 'Original Dairy Free Coconut Milk', 'Pumpkin & Blueberry Cruncy Dog Treats', 'MRS MEYERS 12.5OZ HANDSOAP RHUBAR', 'Organic Sprouted Whole Grain Bread']
cluster 2['Whole Milk Plain Yogurt', 'Organic Skim Milk', "Elmo Mac 'n Cheese With Carrots & Broccoli", 'Kids Sensible Foods Broccoli Littles', 'Apples + Strawberries Organic Nibbly Fingers']
cluster 3['Organic Nonfat Milk', 'Paneer', 'Organic Whole Milk Yogurt', 'Organic Plain Yogurt', 'Extra Light Olive Oil']
cluster 4['Puffed Corn Cereal', 'String Cheese, Mozzarella', 'Cold-Pressed Sweet Greens & Lemon Juice', 'Organic Stage 2 Broccoli Pears & Peas Baby Food', 'Superberry Kombucha']
cluster 5['Whole Milk Plain Yogurt', 'YoTot Organic Raspberry Pear Yogurt', 'Organic Atage 3 Nibbly Fingers Mangoes Carrots', "Elmo Mac 'n Cheese With Carrots & Broccoli", 'Blueberry Whole Milk Yogurt Pouch']
上面列出的所有产品都包含有机全脂牛奶在每个聚类中提升最高的产品。可能突出的是,第一组的建议不如其他组的建议直观。这最有可能是因为该集群占不到 1%的单位购买量和不到 2%的用户,并且似乎专门针对非牛奶饮料利用 Instacart。将需要进一步的工作来确定更少的聚类是否是最佳的,但是考虑到来自该组的用户不太可能观看奶制品,生成非直观的推荐不是太大的问题。对于另一个不太常用的产品,下面是“温和的 Salsa Roja”的结果:
cluster 0['Thin & Light Tortilla Chips', 'Red Peppers', 'Organic Lemon', 'Organic Cucumber', 'Organic Grape Tomatoes']cluster 2['Real Guacamole', 'Thin & Light Tortilla Chips', 'Original Hummus', 'Organic Reduced Fat 2% Milk', 'Thick & Crispy Tortilla Chips']cluster 4['Organic Raspberries', 'Banana', 'Organic Strawberries', 'Organic Hass Avocado']cluster 5['Thin & Light Tortilla Chips', 'Organic Large Brown Grade AA Cage Free Eggs', 'Organic Reduced Fat 2% Milk', 'Organic Large Grade AA Brown Eggs', 'Thick & Crispy Tortilla Chips']
对于该产品,分类 1 和分类 3 没有任何提升超过 1 的项目。
暂时就这样吧!Jupyter 笔记本与 GitHub 的链接在这里是。
使用 Django 创建基于机器学习的 Web 应用程序
使用 Python 著名的 Django 框架在 web 上部署您的机器学习模型,并增加其可见性。
本文面向那些希望使用 Python 的 Django 框架将其机器学习模型部署为 Web 应用程序的读者。对于许多数据科学和机器学习爱好者来说,这可能是转换他们的简单。py 模型文件转换成更加动态和强大的 web 应用程序,该应用程序可以接受用户的输入并生成预测。
为了文章的简单起见,我们将更多地关注如何创建一个基于 ML 的 web 应用程序,而不是花费大部分时间来解决一个棘手的机器学习问题。此外,这不是一个完整的 django 教程,而是成为 AI/ML 开发者的一步。
首先,我们将使用著名的 titanic 数据集创建一个简单的 ML 模型,然后将它转换成一个全功能的、基于 web 的动态应用程序。
开发机器学习模型
如上所述,我们将研究著名的泰坦尼克号数据集,该数据集用于根据乘客的不同属性(乘客级别、性别、年龄、票价等)预测乘客的存活率。).由于这是一个二元分类问题,我们将使用逻辑回归算法(一个强大而简单的机器学习算法)来创建我们的模型。
可以参考下面的代码来创建分类模型。
注意:创建机器学习模型本身是一个完整的详细任务,包含多个步骤(如数据预处理、EDA、特征工程、模型开发、模型验证、调整超参数)。这里我们跳过所有这些步骤,因为我们更感兴趣的是使用这个模型来驱动我们基于 web 的应用程序。
泰坦尼克号生存预测模型
一旦我们创建了我们的最终模型,我们简单地将这个模型存储为一个 model.sav 文件(使用 pickle)。此外,请注意,我们将 scaler 对象存储为一个 scaler.sav 文件(因为在将用户输入提供给模型之前,我们将使用相同的缩放对象将用户输入转换为与训练数据相同的级别)。
创建我们自己的 Django 项目
在创建 django 项目之前,首先我们需要使用下面的命令将 django 安装到我们的开发机器上。
pip 安装 django
这将自动安装 django 框架正常运行所需的所有依赖项。如果此命令对您不起作用,请参考下面的链接-
[## 如何安装 Django | Django 文档| Django
这份文件会让你和 Django 一起工作。如果你只是想尝试一下 Django,请直接跳到…
docs.djangoproject.com](https://docs.djangoproject.com/en/3.1/topics/install/)
一旦你安装了 django,你需要在你的机器上打开命令提示符(Ubuntu 或 Mac 上的终端),然后按照以下步骤操作
- cd 桌面(你可以移动到任何你想创建项目文件夹的位置)
- mkdir Titanic _ Survial _ Prediction(你可以为你的网络应用选择任何你想要的名字)
- cd Titanic _ Survial _ Prediction(将 CD 放入您在上一步刚刚创建的文件夹中)
- django-admin start project Titanic _ Survial _ Prediction _ Web(该命令将在主项目文件夹中自动创建一个名为Titanic _ Survial _ Prediction _ Web的 django 项目)
- CD Titanic _ Survial _ Prediction _ Web(CD 到上一步创建的 django 项目中)
- 现在,如果您正确遵循了上述步骤,只需在命令提示符屏幕中键入python manage . py runserver,然后将命令提示符上出现的链接( http://127.0.0.1:8000/ )复制到 web 浏览器中。一旦你按下回车键,你就会看到下面的屏幕
Django 默认主页视图
如果上面的网页也出现了,那么您已经成功地完成了创建您自己的 django web 应用程序的第一步。
如果对某些人来说这个网页没有出现,请再次参考上述步骤,看看你是否错过了什么。
现在使用任何 IDE (PyCharm,Atom 等。)打开你的 django 项目(Titanic _ Survial _ Prediction _ Web
默认 Django 项目结构
现在,在 urls.py 所在的同一个文件夹中,创建一个名为***‘views . py’的新 python 文件。*** 这个视图文件将负责从使用网页的任何用户那里获得输入,使用相同的输入来生成预测,然后使用网页向用户显示这个预测。
此外,我们需要将我们的“model.sav”和“scaler.sav”文件移到与 urls.py 相同的文件夹中。
在继续之前,让我们先完成 django 项目的配置。在我们的主项目文件夹中,创建一个名为***【templates】***的空文件夹(您可以使用任何名称,但建议遵循已定义的框架命名约定)。这个文件夹将保存我们将在项目中使用的所有 html 文件。
现在打开 setting.py 文件,把“模板”(或者你给你的 html 文件夹起的名字)添加到“模板”列表中高亮显示的“DIRS”列表中。
最终项目结构
现在在 urls.py 文件中添加下面的代码来配置我们网站的 URL(不同的网页有不同的 URL)。
urls.py 配置
我们首先在 urls.py 文件中导入了视图文件,然后在 urlpatterns 列表中添加了主页(打开 web 应用程序时的默认页面,而不是 django 默认视图)和结果页面(用于向用户显示结果)的路径,以及它们各自的名称(使用这些名称可以在我们的 html 文件中引用它们)。
现在我们需要在 views.py 文件中定义这两个函数。除了这两个,我们还将创建一个 getPredictions 函数,它能够通过加载我们预先训练好的模型来生成预测。
可以参考下面的代码来完成这项任务。
views.py 文件
result 方法负责收集用户输入的所有信息,然后以键值对的形式返回结果。
注意:总是建议在单独的 python 文件中创建 getPredictions 方法,然后将其导入到 views.py 文件中。这里为了保持简单明了,我们将函数放在 views.py 文件中。
现在我们已经完成了后端逻辑,让我们创建网页,通过表单接受用户的输入并显示结果。
在“***”***模板文件夹内,创建一个 index.html 文件。这个 index.html 将是我们的主页,并将有我们的形式,我们将使用从我们的用户输入。
index.html
在继续之前,我们需要了解一些事情。
- 表单操作参数→收集表单数据的视图,使用此数据生成预测,然后重定向到包含结果的网页。对于 django,我们有一种特殊的方法-action = " { % URLs ’ name _ of _ URL ’ % } "。在我们的例子中,url 的名称是 result(因为我们已经在 urls.py 文件中提供了这个名称)。
- 此外,我们还需要提供一个 {% csrf_token %} (即跨站点引用伪造令牌),这使得 django 中的数据处理更加安全。这部分是必须的,因此它应该总是在你的表单标签的第一行。
注: {%…%} 都叫做脚本标签。
一旦我们创建了 index.html 文件,我们就需要创建向用户显示预测的 result.html。
result.html
在“ {{ }} ”标记中,我们指定了包含结果的键的名称(在我们的例子中是 result)。
完成所有这些步骤后,在命令提示符下按 CTRL + C 停止服务器,然后使用“python manage.py runserver”命令重新启动它。现在在浏览器中重新打开链接。这一次,默认页面将是我们的输入表单的 index.html 网页。
填写相应的数据并提交表单后,您将被重定向到 result.html 页面,该页面将显示您输入数据的预测。
这就是我们如何创建一个简单而强大的基于 ML 的 django web 应用程序,它本质上是动态的(接受用户的输入),并基于机器学习模型进行预测。
如果您想了解更多关于 django 的知识,请参考 django 文档(下面的链接)。
Django 是一个高级 Python Web 框架,它鼓励快速开发和干净、实用的设计。建造者…
www.djangoproject.com](https://www.djangoproject.com/)
创建房屋销售地图
在本教程中,我将指导你一步一步地创建一个使用散景显示房屋销售的地图,并用彩色地图显示销售价格。我想让观众一眼就能分辨出哪个街区的生活成本最高。这也是价格预测建模目的的一个有用的可视化,以了解位置有多重要,以及与位置相关的新功能是否对工程师有用。
我们将使用国王郡地区销售价格的数据集,该数据集可以在 Kaggle 上找到,但是这些原则也可以应用于您选择的数据集。
这是最终结果:
首先,让我们确保我们的数据集符合目的。请注意,散景需要墨卡托坐标来绘制地图上的数据点。我们的数据集有纬度和经度坐标,所以需要一个函数来转换这些坐标。假设所有数据清理步骤(例如,缺失值)都已完成,数据应该是这样的。
df.head()
包含邮政编码、墨卡托 x、墨卡托 y 和价格列的熊猫数据框架
是时候策划了!
我将分解代码并解释每个元素的作用。
让我们从从 Bokeh 进口必要的工具开始。
from bokeh.io import output_notebook, show
from bokeh.plotting import figure, ColumnDataSource
from bokeh.tile_providers import get_provider, CARTODBPOSITRON
from bokeh.palettes import Turbo256
from bokeh.transform import linear_cmap
from bokeh.layouts import row, column
from bokeh.models import ColorBar, NumeralTickFormatter
散景的突出特点之一是内置的地图拼贴集合。虽然你可以使用谷歌地图,但这需要获得一个 API 密钥。我们将使用 Carto DB。让我们用get_provider()
功能选择这个图块。
chosentile = get_provider(CARTODBPOSITRON)
接下来,我们将选择一个调色板的颜色映射。我们希望确保我们的可视化能够有效地辨别出最昂贵的房子在哪里。Bokeh 拥有丰富的预定义调色板选项,根据您希望的粒度,您可以从调色板中选择使用多少种颜色。还有一些具有更大频谱的扩展调色板,这就是我们在这里要做的。
palette = Turbo256
我们还将定义要使用的数据源,即数据的来源。最常见的类型是ColumnDataSource
,它接受一个数据参数。一旦创建了ColumnDataSource
,就可以将它传递给绘图方法的source
参数,让我们传递一个列的名称作为数据值的替代。
source = ColumnDataSource(data=df)
接下来,让我们定义我们的颜色映射器。这是一个线性彩色地图,应用于价格领域。我们将低和高参数分别设置为最低和最高价格。
color_mapper = linear_cmap(field_name = ‘price’, palette = palette, low = df[‘price’].min(),high = df[‘price’].max())
一个简洁的功能是当用户将鼠标悬停在数据点上时包含附加信息。这是使用工具提示定义的。我们选择显示房子的价格和邮政编码。@符号表示该值来自源。
tooltips = [(“Price”,”@price”), (“Zipcode”,”@zipcode”)]
现在我们准备创建实际的图形。姑且称之为p
。我们将给它一个标题,并将 x 和 y 轴类型设置为墨卡托,以便在渲染时显示我们更习惯的纬度和经度刻度。为了完整起见,我们还将定义我们的轴标签。
p = figure(title = ‘King County House Sales 2014–2015’,
x_axis_type=”mercator”, y_axis_type=”mercator”,
x_axis_label = ‘Longitude’, y_axis_label = ‘Latitude’, tooltips = tooltips)
让我们使用add_tile()
函数添加我们选择的地图标题。
p.add_tile(chosentile)
通过将 x 和 y 指定为墨卡托坐标x_merc
和y_merc
,每栋房屋将被绘制成一个圆。圆圈的颜色将由我们上面定义的线性颜色图决定。
p.circle(x = ‘mercator_x’, y = ‘mercator_y’, color = color_mapper, source=source)
让我们添加一个颜色条,它将有助于理解我们的颜色映射。默认情况下,散景在适当的地方使用科学符号,但在这种情况下,我认为对价格使用普通符号看起来更好。默认导致价格标签与颜色条重叠,所以我们需要给label_standoff
添加一个值。不幸的是,没有直接的方法来添加一个标题到颜色栏,所以现在,我们凑合着没有。
color_bar = ColorBar(color_mapper=color_mapper[‘transform’], formatter = NumeralTickFormatter(format=”0,0"),
label_standoff = 13, width=8, location=(0,0))
让我们通过创建一个布局来指定颜色条应该在图的右边。
p.add_layout(color_bar, ‘right’)
最后,我们希望地图显示在笔记本和展示。
output_notebook()
show(p)
哒哒!
带有房屋销售标记的金县地图
从这个图像中,很明显最昂贵的房子位于海滨,尤其是在麦地那、克莱德希尔和默瑟岛。南北之间也有明显的差异,北方要求更高的价格。
我们还可以放大以深入了解特定的街区。
克莱德希尔街区房屋销售
现在你知道了!我希望这个简短的教程对你有用。如果你想进一步调整,我建议你深入 Bokeh 的文档。即使你像我一样是初学者,也很容易理解。我也希望你能给我任何反馈。
要查看我的项目,包括使用线性回归的数据探索和预测建模,这里的是 GitHub 链接。
使用 GeoPandas 和 Matplotlib 创建街道名称地图
本帖灵感来源于和 城市美丽的隐藏逻辑——全球 。绘制街道名称可以揭示一些有趣的见解。就像旧金山的西半部由大道和东半部的街道组成。或者说,街道是如何只出现在伦敦历史悠久的 市中心,而道路却在外面。我将使用 GeoPandas 和 Matplotlib 为我自己的城市莫斯科创建一个类似的地图,看看我们能发现什么。
我先说结果;技术细节如下,源 Jupyter 笔记本可以在 Github 上找到。
俄罗斯街道名称侧记
在俄语中,街道名称可以放在名称前面( 、улица 、пестеля),或者放在名称后面(малаягрузинская**、улица*** ),如果街道有编号,甚至可以放在中间(1-я 、улицаэнтузиастов).)运用常识,列出所有前缀和后缀,得出以下街道名称列表:*
- улица街
- шоссе高速公路
- 驱动
- 大道
- 巷
- бульвар大道
- площадь广场
- набережная[naberezhnaya]——路堤
- уупик[tupik]—关闭
- вал [val] —壁垒
- магистраль高速公路
地图
让我们放大城市中心:
城市社区的不同特征显而易见。市中心是密集的街道和公园网络。更远的地方的微孔消失了。穿过内城花园环的唯一大道是院士萨哈罗夫大道,它于 20 世纪 80 年代完工。另一条真实的大街——在 1968 年建成后被命名为加里宁大街——在 1990 年更名为新阿尔巴特街*。*
一圈林荫大道勾勒出老城的边界。莫斯科的几条街道名字中都含有“rampart”(俄语为вал — val)一词。我把它们挑出来,只是因为我喜欢强调这些古老的防御工事是如何仍然嵌入在今天的地形中的。直到 1917 年,外围的山谷一直是城市的界限。
与规划城市的规则网格相比,莫斯科的街道看起来更像是一个有生命的、不断进化的有机体,有时毫无规律可言。街道变成大道,又回到街道。大道从无到有,通向死胡同。一些街道是真正的高速公路,一些历史上的高速公路已经变成了狭窄的车道,但仍然保留着它的名字。城市街道保留了俄罗斯混乱历史的痕迹,奇怪的过去的坚守者和从未实现的项目的遗迹。
这是怎么做到的
*我从http://download.geofabrik.de/下载了 OpenStreetMap 数据摘录,特别是地区页面上的. shp.zip 文件。在这个档案中有 gis_osm_roads_free_1。包含道路信息的文件。使用 GeoPandas,我们可以根据这些文件创建地理数据框架。使用 shapely 和 GeoPy,我们定义了一个 21 乘 17 公里的椭圆。它几乎与莫斯科环路相吻合,该环路在 1984 年之前一直是该市的行政边界。
地理数据文件列出了所有“道路”,包括车道和公园车道。选择有名称的街道进行绘制。还包括根据绘制的 OSM 类型分类为“主干”或“主要”的 strets。一些立交桥,桥梁和交叉路口被指定为这样,所以他们被显示以填补空白。
地图是使用 matplotlib 绘制的,街道名称映射到颜色。为了更好地观察 T2 市区的密集网络,它们被绘制得比其他的更薄。
源代码可以在 Github 上找到。
使用 EventBridge、AWS Lambda、SNS 和 Node.js 创建监控服务—无服务器优先
克里斯·利维拉尼在 Unsplash 上的照片
比方说,我们只想检查服务是否正在运行,如果它停止了,您希望得到通知,就这么简单。
此外,这个想法是为了展示我们在 AWS 中使用无服务器概念并尝试总是考虑“无服务器优先”的方法,只使用 3 个服务:Amazon EventBridge、Aws Lambda 和 Amazon Simple Notification Service,可以多么容易地做到这一点。
先决条件
- AWS 帐户
- Node.js 的基础知识
亚马逊简单通知服务——SNS
SNS 定义:
面向微服务、分布式系统和无服务器应用的完全托管发布/订阅消息
在我们的例子中,我们将使用这个服务来发送电子邮件。
在 SNS 服务中,让我们创建一个主题,我将使用的名称是 MonitoringAlert
社交网络话题
创建主题后,现在让我们创建订阅:
SNS 主题已创建
对于订阅,我们需要设置:
协议:电子邮件
端点:您的电子邮件地址
社交网络订阅
下标已创建,状态为“待确认”:
SNS 主题等待确认
一旦您确认状态消息将发生变化,您将通过电子邮件收到一个确认链接:
社交网络话题已确认
完成,SNS 话题已经创建并确认。现在是时候创建 Lambda 函数了。
自动气象站λ
λ定义:
运行代码时不考虑服务器。只为您消耗的计算时间付费。
λ许可
允许我们成为:
λ代码
lambda 将负责检查服务是否正在运行,如果出现问题,将触发 SNS:
λ环境变量
我们的 lambda 函数依赖于两个环境变量:
- 社交网站:社交网站创造的 ARN
- serviceToCheck:我们想要检查的 URL,我的情况是我使用的是 NASA 的 API (https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY)
AWS Lambda 环境变量
现在我们已经创建了 SNS 主题和 Lambda 函数,是时候使用最新的服务 Amazon EventBrigde 了。
亚马逊事件桥
事件桥定义:
连接您自己的应用程序、SaaS 和 AWS 服务的应用程序数据的无服务器事件总线
在我们的例子中,我们将使用 EventBrigde 作为调度程序,它将以我们定义的频率运行并触发 Lambda 函数。
规则模式
关于规则的重要思考是模式。这种模式决定了频率,在我们的例子中,我们将把计划设置为每 1 分钟一次的固定频率(您可以根据自己的需求进行替换):
Amazon EventBridge 创建
其次,在目标中,我们将选择 Lambda 函数,并在基础中添加我们的 webAppMonitor:
Amazon EventBridge 目标
最后,创建了 EventBrigde 规则:
事件桥规则
现在,每次服务离线时,我们都会收到这样的消息:
SNS 通知电子邮件
也就这些了,希望能对你有用:)
你怎么想?有没有其他方法可以解决同样的问题?非常感谢您的反馈。
非常感谢您的宝贵时间!
从头开始创建神经网络
通过自己构建算法来理解算法背后的关键概念
[图片由作者提供]
在我们开始之前—一点历史…
神经网络已经存在很多年了。事实上,该算法背后的思想是由一位名叫弗兰克·罗森布拉特的心理学家在 60 多年前首次提出的。
然而,直到最近十年开始,这些机器学习模型才开始受到更多的关注,随着 这篇 论文的发表,证明了神经网络在机器学习中的用途和有效性。
今天,神经网络是深度学习的核心。人工智能中最复杂的任务通常由人工神经网络来完成,并且有许多库可以用非常少的几行代码来抽象神经网络的创建。
目标
在本文中,我们将介绍神经网络背后的基本概念,并理解它们的内部工作原理。我们将通过从头开始创建一个灵活的神经网络来做到这一点。
本文的完整源代码可以在这个链接中找到!👇👇👇
在 GitHub 上创建一个帐户,为 jzsiggy/NN 从零开始的开发做出贡献。
github.com](https://github.com/jzsiggy/NN-from-scratch)
我们将看到的代码是用 javascript 编写的,但是,每个概念和步骤都将被深入地记录和解释,所以你可以使用任何你喜欢的语言!
在我们继续之前,你必须知道…
神经网络是属于深度学习的一种算法。就此而言,深度学习是机器学习的一个子类,它本身也是人工智能的一个子类。
来源—【https://www.qubole.com/
机器学习背后的核心思想是,可以训练算法对数据进行分类和处理,而无需明确告知分类的规则。
这意味着,我们可以在大量的输入/输出对(即训练集*)上训练*模型,并在一段时间后,让模型为从未见过的给定输入提出自己的分类规则,而不是让模型基于硬编码指令做出决策。
这个想法在所有机器学习算法上都是有效的。
好了——既然机器学习的基础知识已经讲完了,让我们继续学习神经网络吧!
第 1 部分—层??
让我们来谈谈神经网络的基本结构。数据处理是通过分层进行的。任何网络都有一个输入层,一个隐藏层的子集(我们马上会谈到),和一个输出层。
网络的每一层由若干个神经元组成,每一层的神经元通过权值与下一层的神经元相连。
具有 1 个输入层、1 个隐藏层和 1 个输出层的简单网络— [来源:维基百科]
输入层是数据将进入算法的地方。比方说,我们想创建一个网络来预测某个病人患有某种疾病的概率,而不是他所表现出的症状。
在这种情况下,我们的训练数据将被构造成一个输入/输出对。输入将是 0 和 1 的数组,代表我们正在分析的症状,输出将是 0 或 1,代表出现“输入”症状的患者的感染状态。
示例 ML 算法的训练数据
如果我们要根据这些数据训练一个神经网络,我们必须有一个包含五个神经元的输入层,每个神经元对应一种症状。我们可以有任意数量的隐藏层,每层中有任意数量的神经元,但我们必须有一个只有一个神经元的输出层。
当算法认为输入代表生病的患者时,输出层中的神经元将具有接近 1 的激活,当算法认为输入代表未感染的患者时,输出层中的神经元将具有接近 0 的激活。
我们示例神经网络的层结构
你可以把神经元的激活想象成它储存的值。第一层中神经元的激活将与我们输入数据的值相同。这些值将通过数学运算转化为下一层的神经元,我们稍后将对此进行检验。
我们输出层中神经元的激活将导致我们网络的预测。(它可能只是一个神经元,就像上面的例子一样)
第 2 部分—激活、权重和偏差
(还有乙状结肠函数!)
好吧,您可能想知道进入输入层的数据是如何穿过网络并最终到达输出层的,这与它是如何进来的完全不同。这个过程称为前馈,完整的解释涉及一些线性代数和矩阵乘法,我们很快就会看到。现在,我们将坚持这个过程背后的想法。
正如我们之前看到的,每个神经元都通过我们称之为的权重连接到前一层的神经元。这意味着,比方说,第二层中神经元的激活将与该层中神经元在乘以连接它们的权重之前的所有激活的总和相关。
网络表示
如果我们用字母 a 表示一个神经元的激活,连接两个神经元的权重为 w ,则第二层中单个神经元的激活可以表示为下图所示。
神经元的“激活”
对于这件事,上面的等式实际上是方式过于简化了。实际上,这只是计算任何给定神经元激活的第一步。下图将向我们介绍找到最终激活必须执行的剩余操作。
来源——hackernoon.com
我们还没有考虑的两个新因素是:偏差,由字母 *b 表示;*和激活函数,用函数 g(z) 表示。
让我们从解决偏见开始。
这里的想法是,每个神经元将有一个唯一的数字,该数字将被添加到先前神经元激活的加权和中。
添加偏差是有用的,这样神经网络可以更好地推广到任何给定的数据。你可以认为它是一个线性函数中的常数 y=mx+c.
给每个神经元添加一个偏差对于网络正确处理简单数据来说并不是基本的,然而,当我们处理非常复杂的任务时,它变得不可或缺。
现在我们得到了偏差,我们的激活方程看起来像这样:
加权总和+偏差
为了理解数据如何在网络中传输,我们必须了解的最后一件事是激活函数。
让我们用字母 z 表示给定神经元的加权和加上它的偏差。所述神经元的激活将是应用于 z 因子的激活函数的结果。
激活函数用于平滑神经元激活的小变化,并将其保持在 0 和 1 之间。一个非常常见的激活函数,也是我们将在自己的网络中使用的函数,是 sigmoid 函数。
sigmoid 作为激活函数非常有用,因为一旦通过 Sigmoid 函数,远远大于 0 的输入将很快产生非常接近 1 的数字。同样的原理,输入越小,得到的数字就越接近 0。非常接近 0 的输入数字将具有在 0 和 1 之间平滑振荡的值。
Sigmoid 函数
综上所述,我们可以通过将前一层神经元激活的加权和与偏差相加,并将该结果传递给 sigmoid 函数,来找到任何神经元的激活。
发现神经元的激活
从任何给定的输入中找到输出层中神经元的激活的过程被称为前馈。它的工作原理是从输入层的神经元和连接它们的权重中找到第二层神经元的所有激活。第二层中神经元的激活被用于寻找下一层的激活,如此类推,直到输出层。
权重和偏差都是在未经训练的网络中随机初始化的。训练网络的过程意味着找到这些权重和偏差的值,使任何给定输入的结果输出层极其接近预期输出。
让我们对所有这些进行编码,然后我们将检查如何通过调整这些权重和偏差来训练我们的网络,以获得最佳性能!
班级网络{…}
||→[{[{免责声明}]}] ←||
除了 math.js 之外,我们不会在本文中使用任何外部 JS 库来优化矩阵运算*。*
好了——现在让我们开始编码。
让我们从初始化我们的网络类开始。
为了使我们的网络更灵活,更好地概括数据,我们将在类的构造函数中接收层数和每层中神经元的数量。
这里的想法是用一个数组来实例化这个类。数组的每个元素代表一个新的层,每个元素的值是相应层中神经元的数量。
初始化我们的网络
为此,我们将把连接每一层和前一层的权重表示为一个矩阵。这个权重矩阵的每一列将代表将前一层的所有神经元连接到下一层的单个神经元的权重。
对于我们网络中的每一层,我们将有一个权重矩阵,权重将随机初始化,值在 0 到 1 之间波动。
为了简化代码,我们将网络神经元的偏差设置为零。
初始化网络类的权重
概括一下:我们网络的权重属性将是一个矩阵列表。每一个矩阵都代表连接一层和下一层的所有权重。
现在我们已经设置了所有的权重,让我们编写前馈方法的代码。
该方法将接收一个以输入为参数的数组。这些输入将被映射到我们网络的输入层。
为了找到下一层的最终激活,我们可以使用输入激活和连接输入层与下一层的权重矩阵之间的点积的结果。
这种代数运算类似于将输入层的每次激活乘以其与第二层中的神经元的连接权重,并对每个神经元的这些乘积求和。然后,我们可以将 sigmoid 函数应用于第二层中的每个结果加权和,以找到所有结果激活。
这些产生的激活将是点再次乘以我们的权重数组中的下一个权重矩阵,并且点积将经历 sigmoid 函数,从而产生下一层中神经元的激活。这种情况一直发生,直到产生的激活是我们输出层中的激活,因此产生了我们的预测。
为了在代码中实现这一点,我们可以对权重矩阵数组进行循环。循环将从输入与第一个权重矩阵点乘开始,并将 sigmoid 函数应用于结果。由此产生的激活将被映射到一个激活变量,该变量将与第二层权重相乘,依此类推。
在每次迭代中,我们将更新激活变量,以便激活和权重矩阵之间的点积导致下一层的激活(当然,在经历了 sigmoid 之后)。
我们网络的前馈方法
为了让上面的代码工作,我们必须首先编写 sigmoid 函数。代码非常简单:
到目前为止,我们网络的完整代码是这样的:
第 3 部分:培训—🏃♂️ 🏃♀ 🏃♂️ 🏃♀ 🏃♂️ 🏃♀
现在,我们的网络已经可以做出预测了!!!!
…嗯,不完全是。
我们已经可以通过前馈方法输入一些输入数据并获得一个输出预测,但是我们的权重和偏差具有随机值,所以我们的预测根本不会精确。我们的网络会盲目猜测结果。
我们如何调整这些权重和偏见,让我们的网络停止猜测,开始做出“明智”的决定?
完整的答案涉及偏导数和一些多变量微积分,但现在,让我们坚持计算背后的想法。
让我们的预测更好的第一步是找出它们有多糟糕。
完成这一任务的函数类型被称为成本函数。有许多可用的成本函数,但我们将在网络中使用的是均方误差函数,或 MSE。
MSE 成本函数背后的思想是将输出层中每个神经元的激活与该输出的期望值进行比较。我们将从预测的激活中减去预期的输出值,并对每个神经元的结果求平方。对所有这些平方误差求和将给出我们的成本函数的最终值。
MSE 成本函数示例
这里的想法是调整每层的权重和偏差,以最小化成本函数。MSE 的值越小,我们的预测就越接近实际结果。
这就是偏导数出现的地方。让我们这样来看:每个权重和偏差都是以某种方式作用于成本函数的变量。我们网络中任何层的任何权重的任何变化都会或大或小地影响我们的成本。
如果我们可以找到每个权重的偏导数,即单个权重的微小变化对成本函数的结果有多大影响,我们就可以找出如何改变该权重,以最小化我们手头训练示例的成本函数。
例如:当我们计算单个重量的偏导数时,如果我们看到该重量的微小增加将增加成本函数,我们知道我们必须减少该重量以最小化成本。另一方面,如果重量的微小增加降低了成本函数,我们将知道增加这个重量以减少我们的成本。
除了告诉我们应该增加或减少每个重量之外,偏导数还会指出重量应该改变多少。如果通过对权重的值施加微小的推动,我们看到我们的成本函数有显著的变化,我们知道这是一个重要的权重,它的值严重影响我们网络的成本。因此,我们必须对其进行重大更改,以最小化我们的 MSE。倒数也是有效的。
对于我们训练集中的每个训练示例,我们将找到手头示例的成本,并相应地调整所有权重。当我们重复这个过程数千次(如果不是数百万次的话)时——每个训练示例一次——我们将最终得到一个优化的网络,它可以对任何给定的输入做出出色的预测。
为每个神经元寻找最佳偏差的过程非常相似,但由于我们的简单网络将所有偏差设置为零,因此我们不会深入研究偏差计算。
让我们开始编码吧!
班级网{…} —续…
让我们首先向我们的网络类添加一个方法,该方法计算单个训练示例的成本。
成本方法将接收我们的训练集的一个输入、输出对。
首先,通过前馈方法从给定的输入中找到网络预测。输出层的激活将存储在激活数组中。我们现在将创建一个新的数组,每个元素都是输出神经元的平方误差。
我们将返回数组中所有平方误差的总和。
MSE 成本函数
现在我们已经到了网络中最复杂的部分。调整权重以找到最佳网络。计算这些新权重的过程被称为反向传播。
有复杂的算法可以计算出我们的网络中权重相对于成本值的所有偏导数,从而加快训练过程,但为了简单起见,我们将通过数值而不是分析来计算这些导数(目前)。
这意味着我们将选择一个很小的数,比如 0.01,并用这个值来改变给定的权重。然后,我们将重新计算这个调整后的网络的成本函数,看看我们的预测是更好还是更差。
原始成本和我们将权重改变 0.01 后的成本之间的差除以 0.01,是该权重相对于成本函数的偏导数的数值近似值。
计算导数
让我们给我们的网络类添加一个新方法,叫做 backProp。
对于网络的每个权重,我们将计算它对成本的偏导数,并将值存储在一个数组中。该数组的结构将与具有我们网络的所有权重的数组的结构相同,但是不是具有每个权重值,而是具有每个偏导数值。
最后,在我们计算完所有的偏导数之后,我们将遍历权重数组,并从每个权重中减去其相对于成本函数的偏导数。
通过这样做,我们将放大减少成本的权重的值,并减少使成本最大化的权重的值。
backProp 方法将如下所示:
数值反向传播
唷…那是一大堆东西!但是现在我们已经建立并运行了网络的基础部分!
我们要做的最后一件事是创建一个训练方法来接收一组输入/输出对训练数据,并为每个条目运行反向传播算法。
训练之后,我们将有一个优化了权重的网络,准备好预测任何新输入的输出。
训练方法也将接收一个时期值。
epochs 值表示网络应该在相同训练数据上训练的次数。当我们没有一个非常大的训练集时,或者在这篇文章的 中很好地解释了其他原因时,这可能是有趣的。
训练方法
我们网络的完整代码现在看起来像这样:
请注意,我们还添加了一个预测方法,它只实现前馈方法并返回结果。
现在让我们看看我们可以用我们的网络做些什么!
第 4 部分:示例📊 📈 📉
为了证明我们的网络功能,让我们训练一个网络来分类坐标平面上的点。
在这个例子中,让我们画一条穿过平面的对角线。这条线代表函数y = x。
线性函数 y=x
我们将尝试训练我们的网络来将点分类为在线上或线下。
为此,我们将向网络中输入 1,000 个点的示例,并为它们加上正确的标签:如果点在线下,则为 0,如果点在线上,则为 1。
现在,我们只考虑值在 0 和 1 之间的点的 x 和 y 的值。
我们网络的培训示例
我已经创建了一个简单的函数来自动化创建训练数据的过程。该函数接收要生成的输入/输出对的数量,然后返回输入数组和相应的输出数组。
生成训练数据的函数
现在我们可以这样训练我们的网络:
训练我们的网络来分类坐标
在上面的代码中,首先我们初始化一个新的网络,输入层有两个神经元,隐藏层有 5 个神经元,输出层有 1 个神经元。
在第二行中,我们创建了 x 和 y 训练数据。
接下来,我们用我们生成的带标签的数据训练我们的网络一个时期。
最后,为了看看我们是否能够训练一个精确的网络,我们将检查网络对一个新值的预测,在这个例子中(0.5;0.1) 。
我们期望网络将输出分类为 0,因为坐标平面上的这个点在我们正在评估的线之下。
上面代码的输出是
node index.js//returns [ 0.000019314711178699818 ]
现在让我们试着预测点(0.3,0.7)的输出。预期值应该接近 1。
当我们运行 predict 方法时,返回值是:
[ 0.9999203743668156 ]
这正是我们预期的结果。我们现在已经创建了一个网络,在经过 10,000 个示例的训练后,该网络可以对坐标网格上的点进行分类。
第 5 部分:优化
我们已经完成了一项有趣的任务:理解神经网络的具体细节并自己创建一个,但仍有很大的改进空间。
我们可以对我们的网络进行的第一个改进是解析地而不是数值地实现反向传播算法。
反向传播演算— 3b1b
上面的视频真的很有见地,我强烈推荐你观看!
通过这种算法实现反向传播可以成倍地提高我们的训练时间,并允许我们在更短的时间内训练更复杂的网络。
我们可以对网络进行的另一个调整是给每个神经元增加偏差值。在参考资料部分,你可以找到解释和实现其他神经网络偏差的文章。
最后,我们可以对我们的网络进行的另一个修改是通过批次来训练它。这意味着,我们可以计算 100 个训练示例的平均成本,并基于该值调整权重,而不是计算每个训练示例的成本并调整权重。这项技术可以大大加快我们的训练时间!
在我们的网络中,肯定还有许多其他的东西可以调整和修补来提高性能,但是我希望这个简单的例子可以帮助你理解算法背后的关键概念!
我希望你会发现这篇文章很有启发性,并且它可能对你有所帮助!😁😁😁
参考
神经网络和深度学习是一本免费的在线书籍。这本书将教你:神经网络,一个美丽的…
neuralnetworksanddeeplearning.com](http://neuralnetworksanddeeplearning.com/) [## 如何用 Python 从零开始构建自己的神经网络
理解深度学习内部运作的初学者指南
towardsdatascience.com](/how-to-build-your-own-neural-network-from-scratch-in-python-68998a08e4f6) [## 张量流-神经网络游乐场
这是一种构建从数据中学习的计算机程序的技术。它非常松散地基于我们如何思考…
playground.tensorflow.org](https://playground.tensorflow.org/#activation=tanh&batchSize=10&dataset=circle®Dataset=reg-plane&learningRate=0.03®ularizationRate=0&noise=0&networkShape=4,2&seed=0.38334&showTestData=false&discretize=false&percTrainData=50&x=true&y=true&xTimesY=false&xSquared=false&ySquared=false&cosX=false&sinX=false&cosY=false&sinY=false&collectStats=false&problem=classification&initZero=false&hideText=false) [## 从头开始构建人工神经网络:第 1 部分
在我以前的文章人工神经网络(ANN)介绍中,我们了解了与…相关的各种概念
www.kdnuggets.com](https://www.kdnuggets.com/2019/11/build-artificial-neural-network-scratch-part-1.html)
用https://carbon.now.sh/制作的代码片段
可控随机性训练
用 fast.ai 对宠物安全植物进行分类
创建和比较 fast.ai 学习者
在第一部分:建立一个数据库中,我们已经在网上搜集了关于植物及其对宠物毒性的信息,对照第二个数据库交叉引用了这些字段,然后最终通过 Google Images 下载了每个类别的独特图像。在这一部分,我们将训练基线神经网络(使用新的 fast.ai 框架),以根据图片识别植物的种类。然后,我们将评估我们收集的数据集对于训练神经网络有多好,并寻找改进的方法。
这里的主要目标是比较改变每类图像数量的影响,以及我们如何通过控制随机性来公平地比较每次训练。
我发现 fast.ai 是一个非常有用的框架(位于 PyTorch 库之上),可以直接用于机器学习。杰瑞米·霍华德(fast.ai 的创始研究员)用了一个类比,类似于学习如何踢足球——我们是想研究如何踢球的精确物理和力学,还是融入其中,边踢边学?后者更吸引人,由于网上有大量的可用资源,我们可以在学习的过程中获得重要的理论知识。其中一个例子是 fast.ai 自己的书,它很好地概述了这个过程,并包含了一个关于数据伦理的特别有趣的章节。
目录
- 一个训练基线
1.1 - 导入和播种随机性
1.2 - 将数据加载到 Colabs
1.3 - 数据块和数据加载器
1.4 - 分层拆分
1.5 - 创建数据块和数据加载器
1.6 - 创建学习器 - 训练模型
2.1 - 我们如何选择学习率?
2.2 - 示例训练运行 - 训练模型
3.1 - 为什么图像越多越好?
1.训练基线
1.1 -导入和植入随机性
fast.ai 正在快速更新,这需要安装特定的包版本以实现可再现性。为了便于使用,我们将简单地把我们需要的所有东西(以及我们以后需要的东西)导入到全局名称空间中。
为了比较训练运行,我们需要控制系统中存在的随机性来源(扩充、分裂等。)虽然有很多关于这个主题的讨论,但我发现,对于 Colabs 来说,在创建DataBlock
之前使用下面的函数将允许即使在内核重启之间也能产生可再现的结果。您还需要在您的数据加载器中设置num_workers = 0 (or 1)
,但是默认情况下它是 0,我们不会在这里更改它。
在 NumPy、PyTorch 和 random 包中设置随机种子。
请注意,如果您调用任何使用随机设置的函数(如learn.dls.show_batch()
)或更改使用的 GPU(如特斯拉 P100 vs Colabs 上的 V100),结果将会改变。这可能导致需要在工厂重置运行时并重新连接,直到提供相同的 GPU。
驱动版本和 CUDA 版本的一致性也很重要。
1.2 -将数据加载到 Colabs 中
目前,这些数据保存在 Google Drive 中带有类别标签的文件夹中,共有 150 张图片。jpgs)。Colab 可以直接将链接到您的 Google Drive,但是简单地将您的学习者指向该驱动器并继续进行培训将会大大减慢该过程,因为每批培训都需要不断地传输图像。
为了解决这个问题,我们可以递归地将包含每个子文件夹的文件夹直接复制到当前内核中(但是,如果您有大量小文件,这可能会相对较慢)。
recursife 意味着
cp
复制目录的内容,如果一个目录有子目录,它们也被复制。
执行主文件夹的递归复制。
如果这种方法太慢,我们可以先下载文件,压缩文件,然后上传到 Google Drive。然后,每当我们需要数据时,我们可以简单地下载该文件并在内核中解压缩。
下载并解压缩包含所有文件夹的文件。
不管怎样,我们在这里所做的确保我们的图像出现在 Colabs 内核上的工作将会在训练中节省大量的时间。
1.3 -数据块和数据加载器
现在,我们的数据直接呈现在内核中,可以在训练期间轻松访问。为了使用这些数据,fast.ai 开发了一个名为DataBlock
API 的灵活系统。在高层次上,DataBlock
只是在构建批处理和我们的DataLoaders
时作为一个指令列表。这在 fastai 书的第 5 章中有更详细的讨论。
我们的DataBlock
会是这样的:
使用的数据块结构。
blocks = (ImageBlock, CategoryBlock) blocks
使用内置块元组指定自变量和因变量类型。在这种情况下,我们传入图像并寻找类别。splitter = stratifiedsplitter
splitter
定义使用什么函数将数据分割成训练集和验证集。stratifiedsplitter
是一个简单的定制函数,它将基于数据帧中的一列进行拆分(我们将在后面看到),但是可以定义任何类型的拆分——从随机拆分到基于文件夹位置或名称的拆分。get_x = get_x
get_x
定义使用什么函数来获取我们数据集中的图像列表。get_y = get_y
get_y
定义使用什么函数为数据集创建分类标签。item_tfms = item_tfms
item_tfms
是运行在每个单独项目上的代码片段。fastai 包括许多预定义的变换,这个步骤通常用于标准化每个图像的大小。batch_tfms = batch_tfms
batch_tfms
作为一个组合操作在 GPU 上只能应用一次。与单独执行操作和多次插值相比,这保持了图像清晰度并减少了伪影的数量。这里通常是定义图像增强步骤(如调整大小和旋转)的地方。
1.4 -分层分裂
splitter
定义了哪些图像将出现在训练数据集和验证数据集中。有许多方法可以实现这一点,但这里我们准备了一个函数,它查看包含每个类的图像的所有文件夹的路径,并返回一个包含将一个类与每个图像配对的数据帧。
一个简单的函数,创建一个数据帧,包含“类”和“路径”列,从路径到文件夹,包含每个类的图像文件夹。
现在我们有了这个数据框架,我们可以选择我们想要如何进行拆分。为了实现未来的 k 折叠验证,让我们准备一种生成分层折叠的方法,它保留了每个类的样本百分比。我们在 sklearn 的StratifiedKFold
函数的帮助下做到这一点,将数据帧的适当列作为X
和y
传入。
使用 sklearn 的 StratifiedKFold 函数,在提供的数据帧上准备分层 k-fold 分割。
太好了!现在我们有了一个 DataFrame(上面例子中的df_cnn
,它包含了Class
、Path
和is_valid
标签。
此外,由于我们希望尽可能公平地比较具有不同数量图像的数据集之间的训练情况,我们不想每次都使用随机训练/验证分割。如果我们这样做,用于训练和验证的图像的差异将固有地改变模型性能。为了对此进行控制,我们首先对所有图像进行随机分层分割(见下面的 a )。然后,在我们从数据集中移除任何图像后,我们对之前定义的分割进行内部连接,以便任何图像仍保留在与之前相同的训练或验证集中(创建伪分层分割,参见下面的 b ,因为我们不能保证每组中保留的图像的比例与完全相同)。
修复所有的随机性可能通常不是一个好主意,因为不同运行之间的高变化可能会给你一些错误的提示。如果使用交叉验证,分数的自然变化可以帮助你获得更好的分数。
(a)从所有图像中创建主混洗分层分割。(b)在数据集中的一些图像被移除之后,先前定义的分裂上的内部连接将保持相同的训练/验证分裂。
1.5 -创建数据块和数据加载器
如前所述,拆分是在DataLoader
的DataBlock
中定义的,这里我们使用一个get_dataloader
函数来自动化这个过程。
该功能首先定义get_x
、get_y
、splitter
、item_tfms
和batch_tfms
。这里,get_x
和get_y
告诉我们的DataBlock
查看数据帧中适当的列,分别找到图像路径和标签。如前所述,splitter
使用“is_valid”列将图像识别为训练或验证。
对于我们的变换,我们遵循一个预调整策略,其中item_tfms
将每个图像调整到一个比目标训练尺寸大得多的尺寸,而batch_tfms
将所有常见的增强操作(包括调整到最终目标尺寸)组合成一个 GPU 的组合操作。
注意,这里的batch_tfms
使用由 fastai 定义的基础aug_transforms
,它应用翻转、旋转、缩放、扭曲、光照变换的列表,然后使用 ImageNet stats 应用归一化。我们添加一个随机擦除变换的附加条款,这将在后面讨论。最后,如前所述,这些片段中的每一个都被用在了DataBlock
的定义中。
然后,我们检查是否已经定义了一组图像的split_path
,我们应该使用该组图像准备一个“主”分层分割,如果已经定义了,则在我们的img_path
中应用图像的内部连接来定义我们的伪分层分割。否则,我们为img_path
中的图像生成分层分割。该过程包含前两个函数(create_path_df
和stratified_split
),采用之前定义的参数。
该函数的输出是一个DataLoader
,它表示一个具有扩展功能的数据集上的 Python iterable,支持自动批处理、混排和多进程数据加载等功能。
1.6 -创建学习者
同样,为了让我们接下来的工作更加方便,我们将把目前为止我们编写的所有内容包装到一个函数中,该函数定义了参数,并通过 fastai 便利函数cnn_learner
提供这些参数,最终得到我们的学习者。
这里,我们为默认的学习者设置一些参数。我们将使用 224x224 像素大小的图像,批量大小为 64。使用预训练的 ResNet34 架构,这是一个经典且可靠的神经网络。在调用random_seed
函数修复所有随机性之前,我们使用的优化函数(Adam)的参数也在函数体中定义。我们还设置了一系列有用的回调,将结果保存为. csv 格式,并在图中显示训练和验证损失,在训练期间实时显示。最后,在添加切换到混合精度训练的选项之前,我们使用 fastai 便利函数创建学习者,该函数接受我们之前准备的所有单独的项目。
唷!我们准备好训练了。达到这一步需要做很多工作,而且没有必要以这种方式设置。然而,努力将事情包装成一个简单的函数将在以后带来好处,因为我们现在可以轻松地改变一系列参数(以一致的方式),包括数据集中的图像数量和神经网络的架构。这样做可以让一切变得更整洁,避免每次都需要复制和粘贴代码,从而降低出错的可能性。
2.训练模型
正如我们在create_simple_cnn_learner
函数中看到的,我们将从一个简单但健壮的 CNN 开始,ResNet34。让我们首先创建一个只使用 1/3 数据集的学习者(每个类 150 张图片中的 50 张)。
使用 50 张(共 150 张)图像创建学习者。
然后,我们可以使用以下工具来查看这些图像
learn.dls.show_batch(max_n=9)
显示一批 9 幅图像。感谢我们随机性的播种,这将是每次都一样。
使用从 ImageNet 预训练的权重,将使用转移学习程序来微调我们图像的网络。迁移学习背后的基本思想是,由create_simple_cnn_learner
函数创建的预训练 ResNet34 模型在识别 ImageNet 数据集中存在的东西方面已经有了一个不错的想法。由于我们使用的图像与 ImageNet 中使用的真实图像不会有太大的不同,所以我们不希望过多地改变权重是有道理的。使用迁移学习的理论和决策可能更加细致入微,参见这篇出色的博客文章以获得更深入的解释。
这里,我们将在解冻所有内容之前,使用冻结的初始层中的权重训练我们的模型(仅训练最后完全连接的层的权重),并以相对较低的区别学习速率 s 对所有权重进行“微调”训练。这意味着在 fastai 定义的层组中,学习速率将从小(在早期层中)交错到相对较大(当我们接近最终层时)。直观地说,这与每一层所看到的图像细节有关。早期图层倾向于查看图像的大致轮廓,如梯度、边缘和拐角,所有细节的权重都不需要进行任何重大程度的重新训练。对于后面的层反之亦然。
我们将使用迁移学习程序来确定我们的训练结果。
上面的代码展示了我们将在这里用来训练我们的分类器的基本迁移学习过程。固定的学习速率和时期将用于比较训练运行,10 个时期用于训练模型头部,另外 10 个时期用于微调网络。注意,enumerate_params
是一个小函数,当被调用时,它告诉我们一个给定的学习者有多少冻结和未冻结的参数。
2.1 -我们如何选择学习率?
许多优秀的博客文章解释了为手头的问题选择合适的学习率的重要性。由于这不是这篇文章的重点,我们将不会进入太多的细节。在这里(按照 Leslie N. Smith 的建议,许多人也是这样做的),我们使用内置的 fastai 函数learn.lr_find()
来绘制损失与学习率的关系,并在损失仍然改善的最小值之前选择一个值,以确定转移过程中每一步的适当学习率。
learn.lr_find()的结果示例,所选的学习率用红色标记。
在实践中,我们使用fit_one_cycle
函数,所用的学习率代表余弦退火的单周期策略中的最大学习率,如下所示。请注意,下面的 x 轴代表通过学习者的批次数量。
余弦退火的单周期策略。
这项政策是 Leslie Smith 的工作在超参数(学习率、动量和重量衰减)方面的成果,结合 fastai 的调整,在复杂模型的训练中提供快速结果。直观上,我们可以认为该策略从一个较低的值开始,以预热培训。随着我们的进展,学习率增加,动量减少,以鼓励优化器快速调查损失函数的新区域,作为一种正则化方法,通常落在具有更平坦最小值的区域(这些区域更擅长泛化)。在单周期策略的最后部分,学习率的降低允许优化器在较平坦的区域内进入较陡的局部最小值。参见 Nachiket Tanksdale 的这篇精彩文章,了解更详细的解释。
2.2 -训练运行示例
在跑完baseline_fit(learn)
之后,一次单独的训练跑将会看起来像这样。
训练运行示例。
我们可以看到ShowGraphCallback()
的用处,它可以在过度训练发生时给我们一个指示。通常,我们可能会寻找训练和验证损失的差异,如果验证损失开始增加,我们应该特别小心。
3.训练模型
现在我们已经设置好了一切,我们可以开始做一些有趣的比较。让我们先来比较一下,当我们改变第 1 部分的数据集合中的图像数量时(在创建学习器时使用pct_images
参数),我们的前 5 个精度是如何变化的。
验证准确性随所用图像数量的变化。
虽然总训练时间与图像数量成线性比例,但我们看到,当每类使用 150 幅图像时,模型的前 5 名准确度会下降。啊哦。通常我们会期望更多的图像给我们一个更好的结果!
3.1 -为什么图像越多越好?
这个问题与我们为创建数据库而下载的图像的质量有关。回想一下每个文件夹的图片都是根据每种植物的学名从谷歌图片上下载的。让我们来看看一个随机类的早期搜索结果和后期搜索结果,比如说 Peperomia peltifolia 。
从谷歌图片 Peperomia peltifolia。
啊哈!在我们可以为每个类生成 150 个独特的图像之前,必须查看 150 多个搜索结果(仅这个随机类就有 430 多个)。)随着我们越来越深入搜索结果,我们的结果会变得越来越不相关。这将产生像图画、图表和事实表这样的图像——它们都不擅长训练我们的模型做我们想要它做的事情(根据自然照片对植物进行分类)。事实上,我们对训练结果的比较表明,由于包含了许多糟糕的训练示例,试图在每个类别中使用超过 100 个图像会开始损害我们的模型。
我们希望对这些图像进行一些清理,而不必手动检查 500 多个文件夹中的每个文件。请加入我们的第 3 部分:锁定和删除不良训练数据,我们将研究一些方法来实现这一点。
建立图像数据库
用 fast.ai 对宠物安全植物进行分类
使用美素、硒和 FastAI
进入深度学习领域,有大量高质量的资源,但每个人都同意,有意义的项目对真正掌握诀窍至关重要。
有一个喜欢买植物的未婚妻,和一只喜欢啃植物的猫——我想还有什么比组装一个分类器来告诉我植物是否安全更好的呢!
需要注意的一点是,这里所做的所有工作都是在 Google Colabs 上完成的,所以如果您想在本地机器上做同样的事情,那么设置和导入方面的差异是必要的。用的笔记本可以在我的 Github 上找到。
步骤 1 —获取数据
不幸的是,我无法在那里找到一个预制的图像数据集,它适合我想在 Kaggle 上做的事情,或者使用谷歌的数据集搜索。所以,除了自己造还能做什么!
我决定使用 ASPCA 的猫和狗的植物毒性列表,因为我在苗圃的时候已经不止一次地使用过这个网站。这给了我们一个很好的工作核心。为了从网站上抓取这些文本数据,我们可以求助于 BeautifulSoup ,这是一个 Python 库,用于从 HTML 和 XML 文件中提取数据。
然而,当查看他们的网站时,该表并不是一个易于访问的 html 表,而是将数据存储为面板中的行。幸运的是,BeautifulSoup 为我们提供了一种简单的方法来搜索解析树以找到我们想要的数据。例如:
将原始数据收集在一起后,我们需要进行一些创造性的拆分,将其分成几列:
然后,我们可以对特定于狗的列表重复此过程,然后合并数据帧并清除来自任何条目的 nan:
照片由 Unsplash 上的 Sereja Ris 拍摄
第二步——浅层清洁
接下来,我们可以开始浅层清理,包括查看数据集,决定我们要使用哪些关键要素,并标准化它们的格式。
(脏的)数据帧。注意 spp 的不一致使用。/sp。还有失踪的家人。
我们目前有名字,别名,学名,家族以及我们的毒性专栏,这些都是从 ASPCA 网站上用 BeautifulSoup 搜集来的。由于我们将在谷歌图片搜索的基础上收集图片,因此我们决定根据每种植物的确切学名进行搜索,以获得尽可能具体的图片。像“珍珠点”、“大象耳朵”、“蓬松的褶边”和“粉红珍珠”这样的名字会很快返回不是我们要找的植物的结果。
纵观该系列,我们注意到在大小写和 sp 的使用上有细微的差别。/spp。/species 表示一个物种,cv。/var。对于一个品种来说。基于这些观察,我们编写了几个应用于该系列的快速函数,试图标准化数据以便进一步清理。
步骤 3 —通过交叉引用进行深度清理
仔细查看我们的学名(并对数据进行了几次迭代),我们发现许多名称都是更被接受的物种的过时同义词,或者被拼错了。这将在图像收集和稍后训练模型以识别拥有不同标签的相同图像时引起问题。
一次谷歌搜索之后,我们找到了世界植物在线数据库,一个开放的、基于网络的世界植物物种简编。它们列出了同义词和公认的物种名称,并由“分类专家网络”定期更新。非常适合交叉引用我们不可靠的学名。该数据库以. txt 文件的形式提供他们的数据,我们可以读取这些数据并与 ASPCA 植物毒性数据库中的数据进行比较。
来自 WFO 的数据——由“分类专家网络”定期更新。
作为第一步,我们将对来自 ASPCA 的数据进行左合并,保留所有的类,并添加任何与我们当前拥有的科学名称相匹配的数据。我们的目标是将数据库中的所有植物更新为最新的公认学名,因此我们通过该关键字(taxonomicStatus)进行快速排序,并删除保留第一个条目的任何重复条目(如果它存在,将被接受)。
步骤 3.1 —使用字符串匹配修复打字错误
许多学名指的是同一物种,但由于 ASPCA 数据库中的拼写错误,相差了几个字母。让我们使用 difflib 的 SequenceMatcher 来量化字符串距离(使用一种格式塔模式匹配的形式),通过将每个不被接受的条目与 WFO 数据库中的条目进行比较来发现这些错误。为了节省时间,我们可以对数据帧进行排序,只与以相同字母开头的学名进行比较。如果名称足够相似,我们就保留它,并最终返回最接近的匹配。这里,我们将阈值设置为 0.9,以避免任何不正确的匹配。
我们还定义了一个函数来修复数据中有问题的条目,这将把它们的学名、科、属和分类状态更新为 WFO 数据库中相应的(正确的)条目。
现在,我们可以遍历我们的数据,搜索名称匹配,并当场纠正它们对应的数据帧条目。
这一过程有助于我们发现错误,否则这些错误需要深入的检查和高水平的领域知识:
即使知道有一个错误,并排看名字,也很难发现数据库中的错别字。
步骤 3.2-手动清理未识别物种
不幸的是,许多这些未识别的物种在数据库中没有足够近的条目,足以让我对自动修复感到舒服。因此,我们对剩余的未知进行一些手动修复。令人欣慰的是,上面的代码已经将需要人工关注的样本数量减少到只有 50 个左右的条目,我们可以根据在 Google 上找到的正确条目,重用之前的 fix_name 函数来修复这些条目。
凯瑟琳·希斯在 Unsplash 拍摄的照片
步骤 3.3 —匹配同义的科学名称
既然学名已经全部更正,我们仍然需要对它们进行标准化,因为学名可能会由于更新的研究而随时间发生变化(导致分类状态列中的同义词标签)。如果一个科学名称是一个公认名称的同义词,我们希望在未来的 Google 图片搜索中使用这个公认名称。
同一个类(scientificName)的多个标签(taxonomicStatus 下的同义词)在以后会是一件非常糟糕的事情。
幸运的是,WFO 数据库包含一个acceptedNameUsageID
字段,其中包含了给定同义学名的公认名称,我们可以利用它来查找公认的学名,并将它们传递给fix_name
函数。
第 3.4 步—收尾
现在,我们已经(自动和手动)纠正了拼写错误,并将剩余的同义词与其最新接受的名称进行了匹配。剩下的工作就是为图像下载清理数据帧。
咻!这个过程花费了相当多的迭代来得到正确的方法。然而,在花时间训练模型之前,确保我们在建立图像数据库之前有干净的数据是至关重要的。
从最终的 pet 植物毒性数据框架中获得一些有趣的信息:
- 110 个植物家族中有 33 个并非完全有毒或无毒。
- 350 个植物属中有 7 个不是完全有毒或无毒的。
- 只有两种植物表现出物种特异性毒性,对猫来说是百合,对狗来说是核桃!
步骤 4—下载图像
下载图像的第一步是获取我们想要抓取的每个图像的 URL。为了做到这一点,我们采用了一种基于硒的方法,该方法基于法比安·博斯勒的一篇文章。Selenium 是用于测试 web 应用程序的可移植框架,几乎所有与网站的交互都可以模拟。Selenium webdriver 充当我们的虚拟浏览器,可以通过 python 命令进行控制。这里使用一个脚本来搜索谷歌图片的基础上,我们给它一个查询,只寻找和下载缩略图网址,因为我们将抓取大量的图像。一个问题是,谷歌的许多图像缩略图都是以 base64 编码的图像存储的。我们也想抓住这些,这样我们就不会错过任何具有高相关性的图像,因为我们在搜索结果中走得越远,图像对训练目的就变得越差。
太好了!现在我们有办法刮谷歌图片换图片了!为了下载我们的图像,我们将利用 fast.ai v2 中的一个函数,download_images
下载图像。但是,我们将深入源代码并对其进行一点升级,以便在图像出现时对其进行哈希处理,并忽略/删除任何重复的图像,这样我们就能获得一致的独特图像集。我们也将允许它解码和下载编码。jpg 和。png 图像,这是 Google Images 用来存储缩略图的格式。
现在,我们可以遍历我们的每个科学植物名称来收集它们的 URL,然后下载这些图像,同时验证这些图像中的每一个都是独一无二的。每组图片都被下载到我在 Colabs 上的链接驱动器中它们自己的文件夹中。需要注意的一点是,由于 Google Images 上存在大量重复图片,要抓取的 URL 数量需要远远多于您最终想要的图片数量。
下载后,我们采取措施确保每个文件夹包含正确数量的唯一图像。更多细节和代码见链接 Github repo。
因此,在这一阶段,我们已经将有用的标签信息以及为每个类下载的唯一图像放在了一起。这些图片被整齐地分成各自的文件夹,并直接放在我们的 Google Drive 中。值得注意的是,如果你想用这些图像来训练 CNN,在使用它们之前,如果你把这些图像放到本地的 Colab 环境中,你将会获得巨大的加速,但是这将在下一篇文章中详细讨论。
最后的想法
从零开始建立一个数据库对于简单的玩具例子来说,图像分类项目是直接的(参见 fast.ai v2 的书中一个棕色/黑色/泰迪熊分类器的好例子)。对于这个项目,我想扩展相同的方法,但是将它应用到更大的类集合中。这个过程实际上可以分为几个步骤:
- 获取类列表 从网页中抓取表格或文本数据非常简单,这要感谢 BeautifulSoup,通常只需要通过正则表达式或内置 python 方法进行一点点处理。
清理和验证你下载的数据的准确性是这一步最大的挑战。当我们有 10 个类和领域知识时,很容易发现错误并在继续之前修复它们。当我们有 500 个类,却不知道“薰衣草”和“薰衣草”哪个是正确的,事情会变得更加困难。我们可以用来验证数据的独立数据源至关重要。在这种情况下,我们相信 ASPCA 数据中的毒性信息,但不相信它们提供的科学名称,并且不得不使用提供最新分类信息的 WFO 数据库来纠正它们。 - 获取每个类的图片 URL 列表 在这里,Selenium非常灵活。我推荐看看 Fabian Bosler 的文章以获得更多关于如何使用 Selenium webdriver 的想法。你可以手动做的任何事情,网络驱动程序都可以模拟。我们可以执行搜索,找到缩略图并下载它们,如果我们想要更高分辨率的图像,甚至可以点击更大的图像并下载它们。
- 将每个图像下载到一个带标签的文件夹 下载图像的 fastai 功能运行良好,但一个主要的障碍是下载重复的图像。如果你想要更多的图片(10-15 张),如果你下载了谷歌图片搜索的每一个结果,你很快就会得到大量的重复图片。此外,该功能无法处理 base64 编码的图像,因为许多缩略图存储为。令人欣慰的是,fastai 提供了他们的源代码,可以修改这些源代码来解释编码图像,下载 http 链接,在它们进来时对它们进行哈希处理,只保留唯一的图像。
凯文在 Unsplash 上拍摄的照片
下一步是什么?
我将看看我们使用的图像的数量是如何产生影响的,以及将一些带有人工(文本、图片等)的图像放在一起进行过滤。)我们可能在这个自动下载过程中无意中抓取的组件。然后,使用 fast.ai 架构构建和训练 CNN 图像分类模型!