用大台库赢得你的同事
使用 DSS 8.0 Apps-as-recipe 为整个企业创造价值。
由 Unsplash 上的 krakenimages 拍摄的照片
对于希望保持精干并完成大量工作数据科学家和分析师团队来说,Dataiku 是一个非常棒的工具。对于像我这样的数据科学家来说,它允许我花更多的时间做我喜欢的事情,并更快地为企业创造价值。轻松访问数据、轻松实现项目自动化、轻松进行质量保证监控、快速开发……一个梦想。本文并不是对该工具的介绍,但是如果您认为值得一读,我鼓励您留下评论。现在,让我们提醒大家,它是一个数据科学平台,在项目中组织工作,其管道被称为流,用户可以通过 GUI 和通过 DSS api 以编程方式获得。你可以在下面看到一个流程,由称为配方(圆形)的计算片段和称为数据集(方形)的数据片段组成,所有这些都组织在区域中。
项目的典型流程,由作者提供的图像
尽管有上述所有的赞扬和管道的易读性,使用该产品的挫折感会随着时间的推移而增长。您的工作的影响经常受到向其他人提供工作的困难的限制,特别是那些无法访问平台的人,如客户、C-level 等……对于那些可以访问平台的人,即您的数据科学家和分析师同事,有太多的项目内部工作暴露在流程中,有太多的细节需要导航。 决定需要运行什么来得到想要的东西,等等。对他们来说,大部分时间这样的麻烦是不值得的,否则他们将结束一个项目纠缠的节日。 我们离使用数据和模型的简单干净的体验还很远。甚至不要让我开始讨论关注点分离设计原则。如果你的公司希望让这个平台在规模上可行,它将需要放置相当数量的“最佳实践”文档。
我必须赞扬他们站在这些问题的前列,但仍有许多地方需要改进。解决这个问题的能力之一是标签。你可能同意一套标签,如果你的同事和你自己坚持使用它们,你可以很快看到流程中什么是什么,什么是要消费的。我坚持把 if 的成分放在那里。提供的另一个功能是他们所谓的插件。插件可以让你打包一个项目,并让它作为秘方在平台范围内可用。不幸的是,当设计和可用性已经足够让你担心的时候,这仍然需要在插件工程上的扩展投资。这意味着你必须为它写规格,把它交给一个工程师,测试他们的解决方案,然后与他们协调来修复/更新它。就生产率而言不理想。
插件是有用的,但是部署起来昂贵/耗时。进入 Dataiku 8.0 和“应用程序设计器”。问题解决了。
有了这个新功能,您可以构建一个管道,添加一个自动化场景,只需点击几下鼠标,就可以让每个人都可以使用它。真是个梦!如果它有吸引力,你可以把它交给一个工程师,他会有简单的解决方案把它变成一个真正的插件!不幸的是,就像 Dataiku 经常出现的情况一样,文档非常糟糕,无法帮助您超越最基本的用法。在这篇文章的剩余部分,我将尝试提供一些内容来加速你对作为菜谱的应用程序的学习。
允许用户定义的参数
您构建了一个食谱应用程序,该应用程序拍摄上个月穿着 ShoeDirect 的下半部分顾客的照片,并生成一个细分市场、每个细分市场的推荐列表以及他们的潜在 LTV,商店的销售人员将在本月使用该应用程序。这真是太棒了。这里来了一大群快乐的顾客和提高的底线。但是从你的成功来看,经理们现在要求你根据销售人员的销售风格使推荐人的输出有所不同。您需要在应用程序中制作一个下拉菜单,让用户选择销售风格。
你求助于文档/教程或论坛,但是你运气不好:它没有被提及。你只能猜测。
我将向你介绍我对这个问题的了解。
第一步//设计你的流程
这些是基本的。拿一张纸,记下应用程序需要的所有数据输入和它将产生的输出。然后记下用户需要定义的参数。使这些项目变量(不是必需的,但很有用)。
将您的应用程序变量定义为项目变量,图片由作者提供
在运行时,它们将被更改为用户定义的值。
第二步//建立你的流程
为所有输入和输出创建虚拟数据集,并填充它们之间的内容,就像您对任何项目所做的那样。
第三步//制作一个场景
然后,recipe 将在选择的输入上运行给定的场景,因此您需要准确定义您希望您的应用程序做什么,就像您在生产项目中定义场景一样。
步骤 4 //创建您的应用程序
现在是发光的时候了。进入应用程序设计器,选择应用程序作为配方,进入设置菜单。
作者图片
作者图片
作者图片
你的申请正在路上。在运行时,它不会在这个项目中运行,但是项目的流程将被打包,同时打包的还有您需要的元素的副本以及在使用配方时要运行的场景。由于复制元素需要时间和磁盘空间,您需要指定运行应用程序绝对需要的内容,其余的内容会随着应用程序的运行而构建。这是在“包含的内容”一节中完成的,但是文档会让你对它真正的含义/作用感到枯燥。为了安全起见,我包括了“输入数据集”,尽管我认为在大多数情况下这是不必要的。
包含的内容选项,按作者分类的图像
步骤 5 //从 GUI 中检索变量
您的应用程序有一个默认的 GUI 设置,它由以下部分组成:
- 文本框
- 整数选择器
- 下拉菜单
默认设置,图片由作者提供
这是大台库经常失败的地方。它把你带到了伟大的边缘,却把你抛弃在那里,更加困惑,被赋予了更差的文档和更差的 API 选择。
我很高兴地告诉你,解决方案非常简单,不需要任何工作。这些变量被自动添加到应用程序实例流的定制变量中,其名称对应于定义其 UI 元素的 JSON 的“name”字段。如果我们要重命名这些第一变量、第二变量和第三变量,我们在步骤 1 中设置的值将被简单地覆盖。因此,它们可以在任何地方使用,使用通常的 ${} 或data iku . get _ custom _ variables(typed = True)方法,如这里的所示。
让它发挥作用需要知道的事情
是时候缓和你的激动情绪了。作为食谱的应用程序仍然有很多陷阱。我将回顾一下到目前为止我遇到的问题。
失败将没有有用的错误日志。
相反,用户将获得:
java.lang.Exception:场景失败
仅此而已。在 Dataiku UI 中,没有简单的方法来诊断问题出在哪里。既不在使用该应用程序的项目中,也不在其源项目中。在应用程序实例运行并失败后,它会连同相应的日志一起被删除,至少就平台内使用而言是这样(我不知道 DSS 的管理员是否能以某种方式检索它们)。
解决方法是让用户在保持实例复选框打开的情况下重新运行配方。这将防止实例被删除,并且您将能够在那里进行您想要的所有故障排除。
作者图片
但是要小心!不取消选中此框可能会导致大量“单一用途”项目和数据集在您的 Dataiku 框和基础架构中累积。这些实例可以在一个特殊的文件夹中找到,也可以使用 Dataiku 自动赋予它们的特殊标签找到。
按作者查找实例、图像的方法
调试后删除这些项目时必须小心。默认情况下,确保从数据库/存储中删除该项目中使用的所有数据的复选框是未选中的。这意味着,如果您的管理员没有建立定期运行的适当清理流程,您将会在这些数据库/存储中填充 ghost 数据。
检查那个盒子!,作者图片
确保每次都检查它。
不允许版本控制
当您“部署”了一个应用程序时,您对其设置所做的任何更改都会影响到使用它的所有位置。用户没有选择,看不到更新,也无法跟踪您做出更改之前的情况。这当然是应用程序内部工作所需要的,但也适用于菜谱的字段、输入和输出!这可能会导致一些不好的意外。
权限处理不当
对于运行你的应用程序的人来说,他们需要被授予对定义它的项目的写访问权限。这意味着没有办法保护它不被用户篡改。这是非常糟糕的设计,需要行为良好的用户,随着公司的发展,这成了一个负担。这又会导致一些不好的意外。
SQL 依赖项
当使用 SQL 配方而不是可视化配方时,请确保使用链接到输入表的变量,而不是数据库中的表名。出于某种奇怪的原因,食谱不会因为原始数据集没有被设置为实例食谱的输入而失败,而是在应用程序的源项目的表上运行。
变量示例,按作者分类的图像
此外,如果在输入表上使用,替换实例中的输入表将会导致此操作失败。在使用自定义 SQL 之前,您必须将其与副本同步。python 食谱也是如此。
作者图片
等待未来
遵循 Dataiku 的设计理念,数据的来源将尽可能与用户无关,i/o 问题尽可能在技术上由平台静默管理。虽然在大多数情况下,它确实为用户提供了很好的体验,但在这里它也会带来挫败感。让我解释一下。
当在应用程序设计中选择数据集作为输入或返回输出数据集时,将复制数据。根据数据库/存储类型之间的关系,这种复制可能需要一段时间(我们公司的原生 Postgres snowflake 需要几个小时)。在此期间,用户只能看到以下消息:
[INFO] [dku.flow.app] —再等一会儿,以便将来完成实例化
这又是一个无用的日志/消息。
输入/输出混乱
对于每个输入/输出表,用户可以定义无限多个表。我不确定 Dataiku 是如何处理的,但是应该允许每个输入/输出只有一个表,就像可视化菜谱中所做的那样。
应该只有一个数据集替换产品输入,按作者排序的图像
此外,据我所知,如果输入不匹配或者至少包含所需的模式,也没有办法确保用户不能运行菜谱。这将非常有助于防止进一步令人沮丧的失败。
结论
有了上面提供的细节,我希望你能在让数据为你的企业中更多的人服务方面取得长足的进步,并满足你同事的需求。我们都知道,从数据驱动型企业到数据能力型企业还有很长的路要走,而像这样的工具在这条路上至关重要。
如果你相信自助式数据解决方案的力量,并且相信好的设计将对 ML 和数据改善世界大有帮助,我期待你的评论!
风能分析工具箱:迭代功率曲线滤波器
行业笔记
一个开源模块,用于过滤运行风力涡轮机的 SCADA 数据。
介绍
在本文中,我将介绍为分析运行中的风力涡轮机的数据而开发的一系列模块中的第一个——迭代功率曲线滤波器。
理论上,风力涡轮机的功率输出(WT)与风速的立方成正比。这种关系的曲线被称为功率曲线,这可能是风能分析中最重要的曲线。
原始设备制造商(OEM)提供理论功率曲线,该曲线将输入风速映射到给定理想条件下的输出功率。然而,这种关系在运行的涡轮机中很少出现,原因有很多,例如风电场地形、风速计的位置、效率问题以及由于与其他涡轮机的接近而产生的尾流效应。
因此,理解运行涡轮机的风速和功率之间的实际关系是令人感兴趣的,这被正确地命名为运行功率曲线,它可以与理论功率曲线完全不同。
运行功率曲线给出了在正常运行条件下给定涡轮机的风速和功率输出之间的关系。正常运行条件通常被定义为无停机、无故障和无事件数据点。
运行功率曲线是通过清理数百个传感器从风力发电机收集的时间序列数据创建的。记录数据的典型频率是 0.0017s^-1,即每 10 分钟一次。
WT 传感器通过阈值编程,在检测到异常运行条件时发出警报。这就是所谓的监控和数据采集(SCADA)系统。
然而,基于触发的警报简单地过滤 SCADA 数据通常不足以获得对应于正常操作条件的数据点。因此,需要进行统计过滤。
SCADA 数据过滤是许多风能分析项目的基本组成部分,包括涡轮机性能不佳分析、机组并排比较、异常检测以及使用机器学习技术的数据驱动自动化过程。
因此,开发用于预处理 SCADA 数据的现成模块将使工程师和分析师受益,显著减少通常用于数据清理的时间和精力,同时降低行业内利用高级分析的准入门槛。
过滤程序
功率曲线滤波程序
如果每个机组都有一个唯一的标识符,迭代功率曲线滤波器可以处理跨几个风电场的多个涡轮机。该程序包括以下两个主要步骤:
初级过滤
- 停机时间数据点被删除。
- 排除了可能的故障。这一步是经验性的,目的是去除中到高风速下不合理的生产数据。
二次过滤
- 计算来自初级过滤过程的部分过滤的功率曲线的统计参数(平均值和标准偏差)。
- 排除用户指定的+/- x 标准之外的数据点。
- 在用户选择的几个周期内重复上述两个步骤。
Abdul Mouez Khatab 的硕士论文“运行风力涡轮机的性能分析”于 2017 年描述了相关程序。
模块使用
托管在 PyPi 上的 scada 数据分析库包含过滤模块,代码实现的细节可以在 GitHub 上找到。可以通过简单的 pip 命令安装该库,如下所示。
# Pip install library
pip install scada-data-analysis
此外,GitHub repo 项目可以克隆如下:
# Clone github repo
git clone https://github.com/abbey2017/wind-energy-analytics.git
使用该库,您可以通过如下所示的 4 个步骤过滤杂乱的 SCADA 数据:
# Import relevant libraries
import pandas as pdfrom scada_data_analysis.modules.power_curve_preprocessing import PowerCurveFiltering
# Load turbine scada data
df = pd.read_csv('path\to\data')
# Instantiate power curve filtering class
pc_filter = PowerCurveFiltering(turbine_label='Wind_turbine_name', windspeed_label='Ws_avg', power_label='P_avg', data=df, cut_in_speed=3, bin_interval=0.5, z_coeff=2.5, filter_cycle=5, return_fig=True, image_path='..\images')
# Process raw scada data
normal_df, abnormal_df = pc_filter.process()
结果
这个项目的 GitHub repo 有关于功率曲线滤波器的示例数据集和 Jupyter 实验室笔记本用法。此处显示的样本结果基于由 Engie 运营的法国 La Haute Borne 风电场的公开 SCADA 数据。
对未来的展望
来自功率曲线滤波模块的结果可用作训练机器学习模型的基础事实,该模型可用于对 WT 性能或其他先进控制技术的近实时监控。
旨在使工程师和分析师能够利用高级分析的新模块将被添加到风能分析工具箱中。一些当前的想法包括用于生成模型的模块,该模型可以基于历史 SCADA 数据、故障设备检测和性能不佳分类模块来估计/预测来自 WTs 的预期功率。
我想听听你对这篇文章和风能分析工具包的反馈。欢迎模块建议,包括可能的应用。请给 windenergyanalytics@gmail.com 的项目维护者发邮件,或者在项目的 GitHub 页面上提出问题。
什么更有趣?你可以通过我下面的推荐链接订阅 Medium 来获得更多我和其他作者的启发性文章,这也支持我的写作。
https://aolaoye.medium.com/membership
SQL 中的窗口函数:聚集值
用窗口函数计算累计
来源:照片由 ChristopherPluta 从 Pixabay 拍摄
在 SQL 中使用表时,通常会希望聚合值,或者计算表中值的累计。
在本文中,我们将研究如何使用所谓的窗口函数来实现这一点。
此外,我们还将看到如何将 CASE 语句嵌套在窗口函数中,以进一步定制基于特定条件对数组执行的计算。
使用窗口函数求和平均
考虑以下假设的某一天商店中列出的服装项目表(由作者虚构的值)。
date | item | price | size
----------------+--------------------+---------+--------
2021-01-01 | Sky Blue Jeans | 79.99 | 31
2021-01-02 | Fire Red Jeans | 89.99 | 36
2021-01-03 | Sky Blue Shirt | 59.99 | 38
2021-01-04 | Grass Green Shirt | 69.99 | 34
2021-01-05 | Peach Purple Hat | 79.99 | 40
2021-01-06 | Sun Yellow Jeans | 109.99 | 42
2021-01-07 | Olive Green Hat | 89.99 | 37
现在,假设所有者希望在累积的基础上对每个值求和并求平均值,即创建一个新数组,显示前两个值的总和,然后是前三个值,依此类推。这同样适用于计算平均值。
可以使用窗口函数对这些值求和,如下所示(显示了前五行):
>>> SELECT date,price,
>>> SUM(price) OVER (ORDER BY date)
>>> AS total_price
>>> FROM table;date | price | total_price
---------------------+---------+------------
2021-01-01 | 79.99 | 79.99
2021-01-02 | 89.99 | 169.98
2021-01-03 | 59.99 | 229.97
2021-01-04 | 69.99 | 299.96
2020-01-05 | 79.99 | 379.95
(5 rows)
同理,也可以计算出平均累计价格。
>>> SELECT date,price,
>>> AVG(price) OVER (ORDER BY date)
>>> AS mean_price
>>> FROM table;date | price | mean_price
---------------------+---------+------------
2021-01-01 | 79.99 | 79.99
2021-01-02 | 89.99 | 84.99
2021-01-03 | 59.99 | 76.66
2021-01-04 | 69.99 | 74.99
2020-01-05 | 79.99 | 75.99
(5 rows)
将 CASE 语句与窗口函数相结合
CASE 语句的功能类似于 if-then 语句。如果满足条件,则返回一个特定值,否则,如果不满足条件,则返回另一个值。
让我们考虑这个例子。假设对于这个特定的服装店,商家必须为某些商品提供退款。这如何反映在累计总数中?
考虑这个扩展的表。
date | item | price | size | refund
----------------+--------------------+---------+--------+---------
2021-01-01 | Sky Blue Jeans | 79.99 | 31 | no
2021-01-02 | Fire Red Jeans | 89.99 | 36 | no
2021-01-03 | Sky Blue Shirt | 59.99 | 38 | no
2021-01-04 | Grass Green Shirt | 69.99 | 34 | yes
2021-01-05 | Peach Purple Hat | 79.99 | 40 | yes
2021-01-06 | Sun Yellow Jeans | 109.99 | 42 | no
2021-01-07 | Olive Green Hat | 89.99 | 37 | no
从上面我们可以看到,在这种情况下,商家在 1 月 4 日和 5 日提供退款。为了计算新的累积和,这些值需要从中减去——而不是加到总数中。
在这方面,CASE 语句嵌套在 window 函数中——如果退款变量包含是值,则使用一条指令使价格值为负。
>>> SELECT date,price,refund,SUM(CASE WHEN refund = 'yes' THEN -1*price ELSE price END) OVER (ORDER BY date) AS total_price FROM table;date | price | total_price | refund
---------------------+---------+--------------+------------
2021-01-01 | 79.99 | 79.99 | no
2021-01-02 | 89.99 | 169.98 | no
2021-01-03 | 59.99 | 229.97 | no
2021-01-04 | 69.99 | 159.98 | yes
2020-01-05 | 79.99 | 79.99 | yes
2020-01-06 | 79.99 | 189.98 | no
2020-01-07 | 79.99 | 279.97 | no(5 rows)
正如我们所看到的,一旦在 1 月 4 日和 5 日减去 69.99 和 79.99 的价格,就会计算出 279.97 的总价格,这是由 CASE 语句执行的,因为退款变量的 yes 条目会导致 CASE 语句指定一个负价格。
结论
在本文中,您已经看到:
- 使用窗口功能的目的
- 如何使用窗口函数获得累积值
- 将窗口函数与 CASE 语句结合起来,使窗口函数更加灵活
非常感谢阅读,任何问题或反馈都非常感谢!您还可以在这里找到原始文章以及有用的 SQL 实践的更多示例。
参考
免责声明:本文是在“原样”的基础上编写的,没有任何担保。它旨在提供数据科学概念的概述,不应被解释为专业建议。本文中的发现和解释是作者的发现和解释,不被本文中提到的任何第三方认可或隶属于任何第三方。作者与本文提及的任何第三方无任何关系。
葡萄酒数据集:一个分类问题
介绍
葡萄酒数据集由 13 个不同的葡萄酒参数组成,如酒精和灰分含量,共测量了 178 个葡萄酒样品。这些葡萄酒生长在意大利的同一地区,但来自三个不同的栽培品种;因此有三种不同等级的酒。这里的目标是找到一个模型,该模型可以在给定 13 个测量参数的情况下预测葡萄酒的类别,并找出三个不同类别之间的主要差异。这是一个分类问题,这里我将描述四个模型,并评估每个模型的准确性。此外,我将使用主成分分析来确定和探索这三类之间的差异。
多项逻辑回归
由于葡萄酒有三个类别,我们必须使用多项式逻辑回归,而不是有两个类别时使用的逻辑回归。为了做到这一点,我使用了 nnet 包中的 multinom 函数。
> dim(wine)[1] 178 14> attach(wine)> test=sample(178,45)> library(nnet)> LogReg=multinom(class~.,data=wine[-test,])> summary(LogReg)> Pre=predict(LogReg,wine[test,])> table(Pre,wine[test,]$class)
表 1。多项式逻辑回归模型的混淆矩阵
从表 1 可以看出,在 45 次观察中有 5 次分类错误;因此,多项式逻辑回归模型的准确率为 89%。
通过执行以下命令,我们可以多次重复上述过程,以获得对多项逻辑回归模型性能的更准确估计:
> Accuracy=rep(0,50)> for (i in 1:50) {+ test=sample(178,45)+ LogReg=multinom(class~.,data=wine[-test,])+ Pre=predict(LogReg,wine[test,])+ Accuracy[i]=mean(Pre==wine[test,]$class)+ }> sum(Accuracy)/50[1] 0.944
线性判别分析
当我们有两个以上的类,并且当观察数量很小时,LDA 是有用的。当预测值的分布在每一类中都是正态分布时,LDA 也更稳定。
> library(MASS)> lda.fit=lda(class~.,data=wine[-test,])> lda.fit
最后一个命令将生成关于模型的更多细节,如表 2 所示。
表二。各类葡萄酒的 13 个预测指标的平均值
然后,我们根据测试数据评估模型的性能:
> lda.pred=predict(lda.fit,wine[test,])> table(lda.pred$class,wine[test,]$class)
表 3。LDA 模型的混淆矩阵
从表 3 中我们可以看出,LDA 在预测测试数据类别方面具有 100%的准确性。
我们还可以使用下面的命令通过 LDA 可视化训练数据的分类,结果如图 1 所示:
> plot(lda.fit)
图一。通过 LDA 对训练数据进行分类(图片由作者提供)
由于数据集中有三个类,所以只需要两个线性判别式来对每个观察值进行分类。图 1 显示了 LD1 和 LD2 空间上的训练数据的图以及每个数据点的相应类别。基于 LDA 模型的系数计算 LD1 和 LD2 值。
通过执行以下命令,我们可以多次重复上述过程,以获得对 LDA 模型性能的更准确估计:
> for (i in 1:50) {+ test=sample(178,45)+ lda.fit=lda(class~.,data=wine[-test,])+ lda.pred=predict(lda.fit,wine[test,])+ Accuracy[i]=mean(lda.pred$class==wine[test,]$class)+ }> sum(Accuracy)/50[1] 0.9844444
二次判别分析(QDA)
另一个分类器是 QDA 模型,其语法与 r 中的 LDA 相似。我们可以多次运行该过程,以获得对 QDA 模型性能的更准确估计,如下所示:
> qda.fit=qda(class~.,data=wine[-test,])> qda.pred=predict(qda.fit,wine[test,])> table(qda.pred$class,wine[test,]$class)> for (i in 1:50) {+ test=sample(178,45)+ qda.fit=qda(class~.,data=wine[-test,])+ qda.pred=predict(qda.fit,wine[test,])+ Accuracy[i]=mean(qda.pred$class==wine[test,]$class)+ }> sum(Accuracy)/50[1] 0.9866667
K-最近邻(KNN)
KNN 是一种非参数方法,其中基于其 K-最近邻的类别对观测值进行分类。当决策边界是非线性时,这是一个有用的模型,但它不会告诉我们哪些预测是重要的。
> library(class)> knn.pred=knn(wine[-test,2:14],wine[test,2:14],wine[-test,]$class,k=1)> table(knn.pred,wine[test,]$class)> mean(knn.pred==wine[test,]$class)[1] 0.7777778
通过执行以下命令,我们可以多次重复上述过程,以获得对 KNN 模型性能的更准确估计:
> for (i in 1:50){+ test=sample(178,45)+ knn.pred=knn(wine[-test,2:14],wine[test,2:14],wine[-test,]$class,k=1)+ Accuracy[i]=mean(knn.pred==wine[test,]$class)+ }> sum(Accuracy)/50[1] 0.7435556
我们可以对 k=2 到 5 重复相同的过程,结果显示在表 4 的中间栏中。
从表 4 的中间一栏我们可以看出,KNN 模型的结果并不令人印象深刻。这是因为 KNN 模型使用欧几里德距离来测量两点之间的距离,如果要素具有不同的比例,它会影响模型。由于 13 个要素中的每一个都具有不同的比例,因此对数据进行归一化处理以使所有要素都具有相同的值范围非常重要。我们可以在缩放数据后重新运行 KNN 模型,如下所示:
> for (i in 1:50){+ test=sample(178,45)+ knn.pred=knn(scale(wine[-test,2:14]),scale(wine[test,2:14]),wine[-test,]$class,k=1)+ Accuracy[i]=mean(knn.pred==wine[test,]$class)+ }> sum(Accuracy)/50[1] 0.9382222
表 4 总结了 KNN 模型的结果,我们可以看到缩放数据极大地提高了模型的性能。
表 4。有无数据缩放的 KNN 模型的准确性
总结
表 5 总结了不同分类模型对葡萄酒数据集的准确性。LDA 和 QDA 的精确度最高,其次是 KNN (k=5)模型。
表 5。不同分类模型的准确性
主成分分析
上面描述的模型可以基于 13 个测量的预测值来预测葡萄酒的类别。然而,我们也有兴趣知道这三个类别之间的主要区别是什么,以及什么预测是重要的。为了做到这一点,我们可以执行主成分分析,这是一个探索性数据分析的有用工具。
> pr.out=prcomp(wine[,-1],scale=TRUE)> pr.out$rotation> biplot(pr.out,scale=0)> plot(pr.out$x[,1:2],col=wine$class)
前两个 PC 分数和相应的加载向量如图 2 所示。
图二。葡萄酒数据集在前两台电脑上的投影。每个箭头表示顶部和右侧轴上的前两个 PCs 的加载向量。图上的每个数字代表该特定数据点的 PC1 和 PC2 得分(图片由作者提供)
图 3。葡萄酒数据集的前两个 PC 分数。黑色代表 1 级,红色代表 2 级,绿色代表 3 级酒。(图片由作者提供)
图 2 和图 3 显示数据点被分成三个不同的组,对应于三种葡萄酒。1 班和 3 班的 PC2 分数相对相同,但 PC1 分数相差很大。另一方面,类 2 具有介于类 1 和类 3 之间的 PC1 分数,并且其 PC2 分数低于其他两个类。我们可以通过查看 PC 负载向量(图 2 中的箭头)来进一步检查每个类之间的差异。例如,“火山灰碱性”的方向是朝向 PC1 和 3 类数据点的高值。因此,我们可以预计,等级 3 具有较高的“灰分碱度”值,其次是等级 2 和等级 1。我们可以对其他 13 个预测因子进行类似的研究。这种调查的结果总结在表 6 和表 7 中,表 6 和表 7 显示了三种葡萄酒之间的主要差异。这些发现与表 2 所示的 LDA 结果一致。
表 6。总结了 1 级和 3 级葡萄酒的主要区别。对于上述参数,等级 2 的值介于等级 1 和等级 3 之间。
表 7。2 级和 1/3 级葡萄酒的主要区别
结论
使用四种分类方法来评估每个模型在预测葡萄酒类别中的准确性。QDA 和 LDA 的精确度最高,其次是 KNN 和多项逻辑回归。在应用 KNN 模型进行准确分类之前,对数据进行归一化是非常重要的。主成分分析用于识别三类葡萄酒之间的主要差异。
消息来源
UCI 机器学习知识库:葡萄酒数据集。知识共享署名 4.0 国际 (CC BY 4.0)许可”。
赢得 Kaggle 谷歌大脑——呼吸机压力预测
实践教程
在 LSTMs、变压器和 PID 控制器上…
一个人工机械肺(来自维基媒体
11 月 4 日,在 Kaggle 上组织的谷歌大脑——呼吸机压力预测中,我们成功地从 2650 个团队中脱颖而出。在这篇博文中,我想带你经历一次让我们赢得胜利的旅程。在 Kaggle 论坛上可以找到这篇文章的更概括的版本,我们已经公布了我们的 LSTM + CNN 变压器模型和 PID 匹配的代码。
赢得永恒的荣耀和 2500 美元!作者截图
来自 Kha 的邮件…
在完成我的博士学位和一段不愉快但短暂的工作经历后,我有一点被烧毁了。结果,我在 2021 年离开了 Kaggle 一段时间,你可以从我的活动日志中看到。我参加了一点“石头、剪子、布大赛”,并尝试了“室内定位和导航挑战”。
作者截图
9 月 23 日, Yves 和我收到了 Kha 发来的邮件。他在 Kaggle 上发现了一个有趣的时间序列比赛,这个比赛只持续了 1 个月。这个比赛吸引了我,因为我非常熟悉时间序列数据,也是短期比赛的忠实粉丝。此外,我知道当 Kha、Yves 和我合作时,好事往往随之而来!我回复 Kha 说我会有兴趣加入,但是在我真正投入之前,先要处理一些现实生活中的事情。其中一件必须做的事情是准备&介绍 Kha、Yves 和我在 2020 年“利物浦离子交换”竞赛中获得的第三名解决方案。Kha 和 Yves 开始着手解决这个问题,并取得了一些进展。过了一段时间,10 月 6 日,我加入了他们。
简要问题陈述
该竞赛的目标是根据吸气电磁阀打开的程度,预测任何给定时间步长的机械肺内的压力(u_in
;0 到 100 之间的值)。我们接受了大约 75000 次训练呼吸和 50000 次测试呼吸。每次呼吸由吸气阶段和呼气阶段组成,由提供的u_out
指示。我们的提交仅在吸气阶段评分(因此u_out
等于 0)。此外,呼吸被记录在具有不同阻力和容量属性的机械肺中,这些属性被编码在提供的R
和C
变量中。
1 训练呼吸的例子。我们被提供 u_in,u_out,R & C,并被要求预测压力。作者图片
深度学习斗争
从本次比赛中公布的公共笔记本来看,很明显 LSTMs 或任何类型的时间深度学习模型都将占据主导地位。我们三个人都很快增加了功能或对这些管道进行了小的修改,但都无济于事……很明显,深度学习不是我们的强项。然后,我们的思维方式发生了转变:“与其试图创建一个适合所有训练数据的模型,然后用于对所有测试数据进行预测,我们难道不能专注于单独的呼吸,看看我们是否可以对其建模以找到模式?”
拥有物理学背景的 Yves 研究了 PID 控制器,并很快发现了一个很好的简单的后处理技巧,它能够给我们的 LB**+0.004 的提升,这是非常显著的。由于这种后处理技巧,我们能够通过稍微修改的公共笔记本爬上排行榜,使我们在黄金区之外获得一席之地。Yves 的后处理技巧解释起来相当简单(但不容易找到),并且是基于 PID 控制器的理论。**
我们可以想出一个非常简单的公式:pressure = kt — (u_in/kp)
。有了必须调整的算法的kt
和kp
参数,u_in
是输入数据,pressure
是我们想要预测的输出数据。在组织者写的一篇论文中提到了搜索这些参数的网格。但是,在没有纸张的情况下,仅通过对训练数据中的每一次呼吸应用线性回归,就可以容易地发现这个网格(其中我们有 u_in 和压力)。
现在的问题是,我们如何知道某一组参数对测试呼吸有多好,因为我们没有计算误差的压力。为此,我们可以利用压力的离散性。只有 950 个唯一的压力值,它们之间的距离相等。因此,如果我们填入两个候选参数kt
和kp
,并且计算出的压力值与这 950 个独特的压力值完全一致,我们就有了匹配。这种匹配适用于大约 2.5%的数据。
KP_GRID = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0,
2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]
KT_GRID = [10, 15, 20, 25, 30, 35]def match_breath(breath):
u_in = breath['u_in'].values
inspiratory_len = np.sum(1 - breath['u_out'])
u_in = u_in[1:inspiratory_len]
breath_mask = (u_in > 0) & (u_in < 100)
if np.sum(breath_mask) < 2:
return None, None, None, None
for kt in KT_GRID:
for kp in KP_GRID:
preds = (kt - u_in / kp)
preds_round = (preds-MIN_PRESSURE)/DIFF_PRESSURE
tune_error = np.round(preds_round[breath_mask]) - preds_round[breath_mask]
if np.sum(np.abs(tune_error)) < 1e-6:
return preds, kt, kp, breath_mask
return None, None, None, None
派援军来!
在发现后处理技巧后,我们无法做出任何更大的改进。我们慢慢地看到我们在 LB 的排名下降,很明显,为了在这次比赛中取得好成绩,我们需要有更深层次学习经验的人。我们给当时正好在黄金地带下方的舒军发了消息。我知道 Shujun 在深度学习方面非常强,因为他在我也参加的“open vaccine—Covid mRNA”比赛中获得了一个具有独特和强大架构的单人金牌。男孩没有让他或失望。
舒骏的深度学习架构是 LSTM 细胞、1D 卷积和变形金刚的组合。LSTMs 是必要的,因为目标压力严重依赖于以前的时间点。卷积结合转换器是一个很好的组合,可以模拟全局依赖关系,同时弥补转换器无法捕捉局部交互的不足。网络相当深,我们遇到了一些梯度问题。因此,一个名为ResidualLSTM
的新模块被创建,它增加了一个前馈网络(FFN),并用一个剩余连接将输入连接到 LSTM,输出连接到 FFN。该模型对原始数据进行处理,此外还有一些滞后和差异特征、u_in 的累积和以及一个热编码的 R & C 特征。
LSTM + CNN 变压器架构。合著者 Shujun 的图片。
我们将我们当前的混合物与舒军的不同运行模型混合,并应用我们的 PP 技巧。轰,我们中了黄金!
作者图片
我们尝试向 Shujun 建议一些想法,以便在他的管道中实施,还尝试添加一些功能,以便管道(因为他的管道利用了功能)。但是,我们还是没有成功。
几天后,我们觉得我们的混合需要另一个独特的深度学习管道。这就是里卡多进入故事的地方!当我们给他发信息的时候,里卡尔多只比我们低几个点。他建立并运行了一个 LSTM 架构,只集中于原始数据。没有特征工程,没有花哨的东西,只有原始数据和许多时代。诀窍是有一个好的学习进度计划。我们选择了 30 个历元的 ReduceLROnPlateau。这与公共笔记本中通常使用的快速退火调度器形成了对比,后者容易过度适应。当我们将我们的验证损失曲线与公共笔记本的验证损失曲线进行比较时,可以看出这一点,如下所示。这里的红色(赌注)曲线对应于公共笔记本,并很快超拟合。
作者图片
这种方法甚至比 LSTM + 1D CNN 变压器编码器得分更高。这些渠道的结合让我们在排行榜上名列第四。我们决定将我们的目标从金牌转变为赢得金钱。
作者图片
同样,我们所有人都专注于改善我们已经启动并运行的两条管道。对两个管道进行轻微的修改,或者向 transformer 管道添加特性。只做了极其微小的改进,与我们前面的竞争对手差距太大。当然,他们发现的数据中隐藏着一些我们还没有发现的东西……
史上最好的生日礼物
10 月 26 日是我的生日,我决定学习更多关于 PID ( P 比例- I 积分- D 导数)控制器的知识,因为这些也是组织者论文中的核心概念。此外,我们的后处理技巧来自 Yves 对这些 PID 控制器的研究。
来戳这个兔子洞吧…图片作者
我在 Python 中摆弄了一下 simple-pid 包,因为这个包已经实现了 pid 公式。我也能够用这个软件包重现我们的后处理技巧。然而,对于大多数呼吸,我无法找到匹配。直到我决定专门研究代码和积分项。有多种方法来计算 PID 控制器的积分项,该软件包只包括普通的实现。我决定插入一些其他的替代品,突然,我有了一个突破……**多好的生日礼物啊!**下面的代码片段(基于 simple-pid 包)能够为某些呼吸完美地模拟压力- > u_in:
接近了,但还没有…仍然是一个巨大的突破!作者图片
几个小时后,修复一些细节…图片由作者
以下是能够完美模拟某些呼吸的 u_in 压力的代码片段:
def generate_u_in(pressure, time_step, kp, ki, kt, integral=0):
dt = np.diff(time_step, prepend=[0])
preds = []
for j in range(32):
error = kt - pressure[j]
integral += (error - integral) * (dt[j] / (dt[j] + 0.5))
preds.append(kp * error + ki * integral)
return preds
我想我们有了一个新的不错的后处理技巧,并把这个信息发给了 Yves。伊夫做了一些更大的事情。他想出了一些能够完美匹配压力的代码。
你能感受到兴奋吗?作者图片
完美的搭配
我们可以编写 PID 控制器的“普通”方程(对应于我提供的generate_u_in
片段):
其中epsilon
等于target — pressure
,其中目标代表由用户设置的理想压力。现在gamma
,也就是衍生项的权重,总是被组织者设置为零。所以我们只需要关注第一项(比例)和第二项(积分)。我们之前讨论的后处理技术实际上对应于这个等式,其中beta
设置为 0。为了便于记记,让我们重写第二个积分项。此外,积分项通过如下重量衰减实现:
时间步长 t 和 t-1 之间的时间差τ为常数,对于该数据等于 0.5。我们可以从 PID 方程中分离出积分项,以便计算任意时间点的积分项。这避免了必须在整个信号中传播:
有了这些等式,我们就可以开始匹配算法了。对于给定的参数配置(target、alpha 和 beta 或在本文的剩余部分分别称为 kt、kp 和 ki ),我们的匹配算法在时间步长t
填充给定呼吸的压力如下:
- 取两个随机压力值
P0
和P1
。有 950 种可能的压力值,因此有 950*950 种可能的组合。 - 计算
I0 = (u_in[t - 1] - kp * (kt - P0))/ki
- 计算
I1 = I0 + (kt - P1 - I0) * (dt / (dt + 0.5))
- 如果
kp * (kt - P1) + ki * I1 == u_in[t]
我们有一个匹配,我们可以填写 P1 压力[t]
在代码中:
P_RANGE = np.arange(
MIN_PRESSURE,
MAX_PRESSURE + DIFF_PRESSURE,
DIFF_PRESSURE
)
for P0 in P_RANGE:
for P1 in P_RANGE:
I0 = (u_in[t - 1] - kp * (kt - P0))/ki
I1 = I0 + (kt - P1 - I0) * (dt / (dt + 0.5))
u_in_hat = kp * (kt - P1) + ki * I1
if abs(u_in_hat - u_in[t]) < 1e-8:
print(P0, P1, pressure[t-1:t+1])
然而,这个代码相当慢。这是因为对于每个可能的参数组合,需要尝试 950950 个组合。kp 和 ki 有 20 个可能的值,kt 有 6 个可能的值,因此总共有 9509502020*6 个可能的组合。然而,对于固定的P0
,我们注意到abs(u_in_hat — u_in[t])
在 for 循环中线性增长。因此,我们可以通过两次测量来推断。如果这个外推函数与 x 轴的交集出现在一个整数处(或接近于一个整数),那么我们的 If 条件在内部 for 循环的迭代中将被评估为 True。
作者图片
这种观察使我们可以将代码加速 950 倍。
for P0 in P_RANGE:
I0 = (u_in[t - 1] - kp * (kt - P0))/ki
# Calculate 2 points for P1 so we can get the slope
I11 = I0 + (kt - MIN_PRESSURE - I0) * (dt / (dt + 0.5))
u_in_hat1 = kp * (kt - MIN_PRESSURE) + ki * I11
I12 = I0 + (kt - MIN_PRESSURE2 - I0) * (dt / (dt + 0.5))
u_in_hat2 = kp * (kt - MIN_PRESSURE2) + ki * I12
slope = u_in_hat2 - u_in_hat1
x_intersect = (u_in[t] - u_in_hat2) / slope
if abs(np.round(x_intersect) - x_intersect) < 1e-8:
print(P0, MIN_PRESSURE + (x_intersect + 1) * DIFF_PRESSURE)
开大声点!带来噪音!
有了上面的代码,我们能够匹配很多压力。但他们中的许多人仍然无法匹敌。在组织者的论文中,有人解释说 u_in 数据中加入了人为噪声。我们开始努力消除这种噪音。让我们首先看看当前版本的匹配算法对特定呼吸(训练呼吸 id 17)的输出
作者图片
注意,u_in 的第一个值和中间的一些值并不完全匹配。所有呼吸的第一个值都有噪声,不可匹配,但中间的点应该是可匹配的…让我们从索引 5 开始,根据提供的压力调用我们的 generate_u_in 代码,以生成“理想 u_in 值”,并从提供给我们的 u_in 中减去这个值。
kp, ki, kt = 1., 3., 30
I5 = (u_in[4] - kp * (kt - pressure[4]))/ki
u_in_hat = generate_u_in(pressure[5:], timestep[4:], kp, ki, kt, integral=I5)
noise = u_in[5:32] - u_in_hat[:-5]
plt.plot(timestep[5:32], noise, '-o')
plt.title('noise')
plt.show()
作者图片
理想 u_in 值和提供的 u_in 值之间的差值形成一个漂亮的三角形,除了在三角形的转折点/峰值上。关键是这个噪声的斜率((np.diff(noise) / np.diff(timestep[5:32]))
)对于这个三角形上的连续点是相等的。我们也可以重新编写积分计算向后工作。
有了这两种见解,我们能够匹配更多的数据:
作者图片
把它放在一起
给定我们的 DL 模型和匹配算法,我们的方法相当简单:
- 用我们的两个 DL 模型生成预测。训练他们在多个种子进行 10 重交叉验证。这为我们提供了多个略有不同的模型。将这些模型的(加权)中值作为一种集成技术,因为当 MAE 是度量标准时,这通常优于平均值。
- 根据这些预测,应用我们的匹配算法。如果我们找到匹配,用匹配器替换 DL 算法的预测。如果不匹配,则保留 DL 预测。
总的来说,我们能够大致匹配 66%的数据,这给了我们在排行榜上巨大的提升。
结束营业的时间
这是我在卡格尔的第一次胜利。虽然我承认 PID 控制器匹配有点厚脸皮,但我仍然对我们的结果感到非常自豪,因为 2650 个团队中只有 2 个能够找到这个漏洞。此外,我们是唯一能够在 u_in 数据中匹配噪声的团队。我要感谢我的队友们兹德米、卡、舒军和 B ,没有他们,这个结果是不可能的!
如果你有任何问题或不清楚的地方,随时留下一些评论或通过其他媒介与我联系(双关语)。
参考
- 主办方论文
- Kaggle 论坛报道
- PID 匹配代码+解释 (Kaggle)
- PID 匹配代码+解释(Github)
- LSTM + CNN 变压器代码(Kaggle)
- LSTM + CNN 变压器代码(Github)
- LSTM 原始数据代码(Github)
胜于替代 1.1 和预期目标 1.1:模型更新和验证
一些小改动。更多的数据,以及它真正的价值。
就在半年多前,我发布了两个评估 NHL 滑手和守门员的描述性模型:预期目标和胜于替补。对于任何不熟悉或想要刷新记忆的人来说,这里有一个每个型号的快速运行:
- 预期目标利用极端梯度推进(extreme gradient boosting),一种先进的机器学习技术,根据射门距离、角度和射门前发生的事件等因素,计算未被阻挡的射门尝试成为目标的概率。预期目标可以解释为加权投篮。
- 胜于替换(战争)使用岭回归(RAPM) 来隔离运动员在力量相等的情况下对预期目标的影响,以及他们对力量打法的预期目标和点球决胜的预期目标的影响。它以前还使用岭回归来分离每个球员对他们球队获得和获得点球的比率的影响,以及射手和守门员对他们获得或面临的未阻挡射门尝试的影响(射门/扑救)。对于滑冰运动员来说,这些回归的结果分为六个部分:力量进攻,力量防守,力量进攻,人手不足防守,处罚和投篮。(注意,这里的力量与 NHL 的定义略有不同,并且不包括空网的比赛;它只包括 3 对 3、4 对 4 或 5 对 5 比赛,其中两队都有一名守门员。这将适用于我在这篇文章中提到的任何力量游戏。)从玩家实际提供的进球数中减去替换级别玩家预期在每个部分中提供的进球数,然后使用与一个进球数的值相当的除法器将该进球数转换成获胜数,以便获得高于替换的获胜数。
从 2013–2014 年到 2019–2020 年的所有赛季都建立了预期目标,而从 2014–2015 年到 2019–2020 年的所有赛季都建立了胜于更替目标。这两种型号都用于 2020-2021 赛季。
我对两个模型都做了一些更新。最激动人心的更新是,我激动地宣布我现在有了这两款车型从 2007-2008 年到 2020-2021 年的数据,并将很快提供所有数据。不幸的是,2007 年至 2008 年和 2009 年至 2010 年间的一些比赛没有完整的比赛数据和位置坐标;我已经选择从这些模型中完全排除这些游戏。您可能会注意到在输出中,某些在某个赛季打了 82 场比赛的球队或球员被列为打得更少;这意味着他们玩的是一个无法模拟的游戏。
其他的更新都是无关紧要的,我可能会实现它们而不提及任何东西,但我认为透明度很重要,最重要的是,我只是喜欢写这些东西。
我的两个模型以前都是用 R 构建的,它们都是完全用 Python 重新构建的。除了我有意做的调整之外,建模过程被尽可能紧密地遵循,但是模型输出之间会有一些微小的差异也是很自然的。某个投篮在一个模型中可能值 0.2 个预期目标,在另一个模型中可能值 0.3 个,没有真正的原因。
考虑到这一点,以下是我选择对每个型号进行的具体调整:
预期目标
- 每一季的模型训练所依据的数据差异很大。从 2007-2008 年到 2009-2010 年,我从样本中删除了 100 个“目标”游戏,根据剩余的数据训练模型,然后在我删除的目标游戏上运行它。在这三个赛季中,我对每 100 个游戏样本重复了这个过程。我这样做是因为与 2010-2011 年至今的数据不同,所有拍摄位置坐标都完全来自 ESPN 的 XML 报告,这些报告与 NHL 的 API 报告在拍摄跟踪方式上略有不同。
- 从 2010–2011 年到 2016–2017 年,我只是从样本中删除了一个目标季节,在剩余的季节训练模型,并在目标季节运行模型。我在这 7 季中重复了这个过程。
- 从 2017–2018 到 2021 年,我使用了与 2007–2008 到 2009–2010 年相同的过程:从样本中删除 100 个目标游戏,在剩余的游戏上训练模型,然后在目标游戏上运行模型。我将这些赛季从 2010-2011 年分为 2016-2017 年,因为 NHL 从 2017-2018 年开始对守门员设备法规进行了调整。守门员设备尺寸的减小带来了射门成为进球的可能性的可预测的增加,并且一个模型应该考虑到这一点。
- 在每个模型被训练的季节之外,这个模型的建模过程保持尽可能类似于先前的建模过程;所有相同的变量都被考虑在内,模型训练的参数使用相同的交叉验证方法获得。
胜于替代
- 这个模型的前一个版本使用了一个先验(贝叶斯)RAPM,用于力量均衡进攻、力量均衡防守、强力进攻和人手不足防守。我从 2013 年至 2014 年的无事先知情(香草)RAPM 开始,然后计算 2014 年至 2015 年的事先知情 RAPM,然后使用 2014 年至 2015 年 RAPM 的输出作为 2015 年至 2016 年的先验。我在 2020-2021 年间重复了这一过程,有效地创造了所谓的菊花链。新型号与旧型号不同,它使用菊花链,但这种菊花链始于 2007 年至 2008 年,并简单地使用 2007 年至 2008 年香草 RAPM 的输出来创建力量进攻、力量防守、力量进攻和人手不足的防守组件。
- **对判罚的影响不再通过岭回归来计算,而是通过抽取和执行的个人判罚来计算。**虽然我相信玩家无法控制的因素确实会影响他们抽牌和罚点球的速度,但我不认为回归分析足以适应这种外部环境。类似于使用支持和反对的目标作为目标变量运行岭回归,我相信这在理论上听起来很棒,但在实践中,一个赛季中球员在冰上的目标变量的出现次数可能太低,无法在这样的回归输出中放置太多股票。这个模型的 1.0 版本中反直觉输出的数量和惩罚战的低重复性促使我做出这个决定。
- **对射门和救球的影响不再通过岭回归来计算,而是通过相对于预期目标的进球和允许进球来计算。**这个改变主要是将拍摄分解成 3 个部分,然后可以添加到模型的其他部分;该模型的 1.0 版本仅具有一个“投篮”组件和一个“救球”组件,这使得很难真正衡量球员在均匀力量和力量发挥方面的影响。这也使得衡量守门员为每支球队做了什么变得不可能。考虑到一个赛季进球的样本量很小,我目前还没有信心使用岭回归来分离这方面的影响;尤其是在将这些样本进一步细分为三个更小的子样本之后,这三个子样本分别是平均强度、强力打法和短手打法。我计划在某个时候使用某种逻辑脊回归来重新审视这一点,因为我确实认为评估守门员和守门员表现的指标应该考虑到他们面对的各自守门员和射手的影响。不过,就目前而言,我对计算模型的拍摄和保存组件的方式感到满意。
- 因为在一些早期赛季,所有选手的工资数据都不容易获得,所以替代水平不再由 850,000 美元或更低的工资帽和 UFA 签约状态来定义。更确切地说,替换等级是由冰时定义的,每个游戏强度的定义略有不同:
- 实力相当时的替补水平定义为在冰上百分比实力相当时,队内排名低于第 13 位的所有前锋和排名低于第 7 位的所有防守队员。
- 替补级别是指在冰上的力量比赛时间百分比中,所有排名低于第 9 位的前锋和所有排名低于第 4 位的防守队员。
- 点球决胜中的替补级别定义为队中所有排名低于第 8 位的前锋和所有排名低于第 6 位的防守队员在场上的人手不足时间百分比。
- 所有情况下的替补水平(出于处罚目的)被定义为在所有情况下球队中排名低于第 13 位的所有前锋和排名低于第 7 位的所有防守队员的上场时间百分比。
- 守门员替换级别定义为在比赛中排名低于第二的所有守门员。
- 请注意,虽然在所有情况下都使用了罚分,但没有其他组件使用空网比赛。因为这个原因,在战争旁边出现的总冰上时间值,以及随后的战争/冰上时间比率,都没有将冰上时间与空网合并。
- 在所有情况下仍然使用点球,因为不可能从 NHL 的比赛数据中确定地得出点球是由守门员在网内进行的,还是守门员在抽签后被推迟点球。在守门员被拉下的情况下进行的处罚很少,所以这个问题的影响可以忽略不计,但这是值得注意的。
- 我之前使用 Christopher Long 的方法计算了一个进球相对于一场胜利的价值,发现从 2017-2018 年到 2019-2020 年,一场胜利大约相当于 5.33 个进球。对于模型的 1.1 版本,我遵循克里斯托弗的方法,使用 2007-2008 年到 2020-2021 年的每个 NHL 赛季计算一个稳定的毕达哥拉斯指数:2.022。然后,我使用每个赛季每场比赛的联赛平均进球数——排除所有净胜球或点球大战中的进球数,就像我在模型的其余部分所做的那样——来确定每个赛季相当于一场胜利的进球数。以下是我获得的价值:
╔══════════╦═══════════════╗
║ **Season** ║ **Goals Per Win** ║
╠══════════╬═══════════════╣
║ 20072008 ║ 5.051 ║
║ 20082009 ║ 5.345 ║
║ 20092010 ║ 5.201 ║
║ 20102011 ║ 5.115 ║
║ 20112012 ║ 4.989 ║
║ 20122013 ║ 4.975 ║
║ 20132014 ║ 5.002 ║
║ 20142015 ║ 4.908 ║
║ 20152016 ║ 4.853 ║
║ 20162017 ║ 5.053 ║
║ 20172018 ║ 5.375 ║
║ 20182019 ║ 5.438 ║
║ 20192020 ║ 5.422 ║
║ 20202021 ║ 5.285 ║
╚══════════╩═══════════════╝
确认
我总结了对预期目标所做的更改,并在替换模型上取得了成功。但是有了 2007-2008 年的数据,我决定是时候使用整个样本来验证这些模型,看看这些老派的详细数据是否真的像它所描述的那样糟糕。
我用来验证我的预期目标模型的指标包括曲线下面积(AUC),你可以在这里了解更多关于的信息,以及每个实际目标的预期目标。以下是每个季节的测试指标值:
╔══════════╦════════════════╦═══════╦═════════════════╗
║ **Season** ║ **Game Strength** ║ **AUC** ║ **xGoals per Goal** ║
╠══════════╬════════════════╬═══════╬═════════════════╣
║ 20072008 ║ All Situations ║ 0.769 ║ 0.985 ║
║ 20072008 ║ Even Strength ║ 0.778 ║ 0.990 ║
║ 20072008 ║ Power Play ║ 0.710 ║ 0.977 ║
║ 20072008 ║ Shorthanded ║ 0.767 ║ 0.931 ║
║ 20082009 ║ All Situations ║ 0.775 ║ 0.993 ║
║ 20082009 ║ Even Strength ║ 0.783 ║ 0.991 ║
║ 20082009 ║ Power Play ║ 0.713 ║ 1.000 ║
║ 20082009 ║ Shorthanded ║ 0.815 ║ 0.983 ║
║ 20092010 ║ All Situations ║ 0.760 ║ 1.033 ║
║ 20092010 ║ Even Strength ║ 0.767 ║ 1.017 ║
║ 20092010 ║ Power Play ║ 0.700 ║ 1.071 ║
║ 20092010 ║ Shorthanded ║ 0.787 ║ 1.092 ║
║ 20102011 ║ All Situations ║ 0.773 ║ 1.000 ║
║ 20102011 ║ Even Strength ║ 0.782 ║ 0.987 ║
║ 20102011 ║ Power Play ║ 0.715 ║ 1.031 ║
║ 20102011 ║ Shorthanded ║ 0.773 ║ 1.075 ║
║ 20112012 ║ All Situations ║ 0.775 ║ 0.999 ║
║ 20112012 ║ Even Strength ║ 0.781 ║ 0.988 ║
║ 20112012 ║ Power Play ║ 0.722 ║ 1.028 ║
║ 20112012 ║ Shorthanded ║ 0.807 ║ 1.047 ║
║ 20122013 ║ All Situations ║ 0.771 ║ 0.973 ║
║ 20122013 ║ Even Strength ║ 0.779 ║ 0.981 ║
║ 20122013 ║ Power Play ║ 0.697 ║ 0.929 ║
║ 20122013 ║ Shorthanded ║ 0.780 ║ 1.168 ║
║ 20132014 ║ All Situations ║ 0.773 ║ 0.991 ║
║ 20132014 ║ Even Strength ║ 0.781 ║ 0.981 ║
║ 20132014 ║ Power Play ║ 0.715 ║ 1.042 ║
║ 20132014 ║ Shorthanded ║ 0.783 ║ 0.849 ║
║ 20142015 ║ All Situations ║ 0.771 ║ 1.002 ║
║ 20142015 ║ Even Strength ║ 0.778 ║ 0.996 ║
║ 20142015 ║ Power Play ║ 0.706 ║ 1.013 ║
║ 20142015 ║ Shorthanded ║ 0.812 ║ 1.083 ║
║ 20152016 ║ All Situations ║ 0.771 ║ 1.024 ║
║ 20152016 ║ Even Strength ║ 0.780 ║ 1.036 ║
║ 20152016 ║ Power Play ║ 0.696 ║ 0.996 ║
║ 20152016 ║ Shorthanded ║ 0.782 ║ 0.953 ║
║ 20162017 ║ All Situations ║ 0.771 ║ 1.011 ║
║ 20162017 ║ Even Strength ║ 0.777 ║ 1.017 ║
║ 20162017 ║ Power Play ║ 0.709 ║ 1.002 ║
║ 20162017 ║ Shorthanded ║ 0.789 ║ 0.925 ║
║ 20172018 ║ All Situations ║ 0.766 ║ 1.032 ║
║ 20172018 ║ Even Strength ║ 0.772 ║ 1.030 ║
║ 20172018 ║ Power Play ║ 0.697 ║ 1.050 ║
║ 20172018 ║ Shorthanded ║ 0.828 ║ 0.958 ║
║ 20182019 ║ All Situations ║ 0.762 ║ 1.001 ║
║ 20182019 ║ Even Strength ║ 0.771 ║ 1.004 ║
║ 20182019 ║ Power Play ║ 0.676 ║ 0.991 ║
║ 20182019 ║ Shorthanded ║ 0.798 ║ 0.974 ║
║ 20192020 ║ All Situations ║ 0.772 ║ 0.989 ║
║ 20192020 ║ Even Strength ║ 0.779 ║ 0.984 ║
║ 20192020 ║ Power Play ║ 0.696 ║ 1.004 ║
║ 20192020 ║ Shorthanded ║ 0.821 ║ 1.003 ║
║ 20202021 ║ All Situations ║ 0.774 ║ 0.975 ║
║ 20202021 ║ Even Strength ║ 0.782 ║ 0.966 ║
║ 20202021 ║ Power Play ║ 0.706 ║ 0.994 ║
║ 20202021 ║ Shorthanded ║ 0.813 ║ 1.107 ║
╚══════════╩════════════════╩═══════╩═════════════════╝
预期目标模型在 2007-2008 至 2009-2010 赛季的表现略差于后来几年,但仍好于我的预期。注意,根据我引用的 AUC 文件,0.6 和 0.7 之间的值为差,0.7 和 0.8 之间的值为一般,0.8 和 0.9 之间的值为好;这意味着在所有的情况下,在相同的强度下,这个模型是公平的,在每一个赛季中更接近好的而不是坏的。无论前三季的位置坐标存在什么问题,都不足以阻止模型发布令人尊敬的性能。然而,在权力游戏中,该模型在 5 个赛季中被归类为差,在其他 9 个赛季中更接近差而不是好。
这些数字证实了我对公众预期目标模型的总体立场:总的来说,它们是公平的,我要说它们更接近于好而不是坏。但是特别是在权力游戏上,他们忽略了很多重要的内容。
在测试和验证了我的预期目标模型在每个赛季都是公平的之后,是时候测试我的战争模型了。虽然 WAR 本质上是描述性的,但是很难实现模型的描述性测试,我相信对过去结果的准确描述通常可以很好地预测未来的结果,所以我选择测试模型的描述性和预测能力。
WAR 模型的一个常见的描述性测试是测试团队级别的 WAR 总和与另一个指标(如积分或目标差异)之间的相关性,后者明确定义了团队质量。这个测试本质上是对 WAR 的恭维,因为它不一定测试 WAR 如何评估一个团队中的选手,而是 WAR 如何评估该团队选手作为一个整体的总和。
例如,假设我们有一个完美的战争模型,它会告诉我们,在 2018-2019 年,塞德里克·帕盖特为坦帕湾闪电队提供了-1 场战争,尼基塔·库切罗夫为他们提供了 5 场战争。这意味着他们的贡献总和是 4 战。现在,假设我建立了一个可怕的模型,说帕盖特值 5 次战争,库切罗夫值-1。他们战争的总和仍然是 4,这将完全符合他们贡献的真实组合价值。但是我对每个玩家都很不满意,我的模型在隔离他们的影响方面做得很糟糕。这是一个虚构的例子;没有人的模型说 Paquette 或 Kucherov 有任何接近的东西,但在我们开始分析我的描述性测试的结果之前,值得记住。
正如我提到的,我的战争模型的一些输出是通过岭回归得到的。这些回归将一个赛季中的每个球员视为一个整体,不管他们是否在赛季中更换球队。这意味着我不能将为多支球队效力的球员的贡献划分回他们效力的球队,因此测试该模型的最佳方式是完全移除这些球员。这实际上有点不讨好模型。以下是我第一次描述性测试的结果:
0.82 的 R 值告诉我们,该模型可以解释每 82 场比赛中 82%的积分差异。其余的可以用为多支球队效力的球员所贡献的胜利、纯粹的运气和建模错误的某种组合来解释。
虽然这个值听起来很公平,但当我第一次看到它时,我发现它非常令人担忧。当我去年冬天发布我的模型的输出时,我使用了相同的精确测试方法,并获得了 0.89 的显著更高的 R 值。
这里发生了什么?模型变得明显更差了吗?
谢天谢地,没有。这只是适用于一系列季节,它并不十分有效。当在这张图片中测试 WAR 1.0 的同一三季中测试时,WAR 1.1 实际上表现稍好:
我可以一整天都给出 R 值,但是如果没有某种比较或基线,这些值就没什么价值了。为了了解我们从使用战争中真正获得了多少价值,下一步是将其与曲棍球当前最流行的统计数据进行比较:点数。
分析社区的普遍共识是,战争和类似的模型远远优于点。如果这是真的,他们应该更好地描述团队层面的表现。
为了在平等的基础上比较两者,我首先删除了所有守门员,因为没有人关心守门员得分的多少,所以我们不会在任何一个测试中使用它们。从 2007-2008 年到 2020-2021 年,只使用滑冰运动员重复相同的测试,导致 R:
相比之下,点数的表现并不像分析社区可能认为的那样糟糕:
我不得不承认,我对选手们的总分在这里保持得如此之好印象深刻。他们显然是劣等的,但不是我所期望的那样。这一证据表明,在没有更好的度量标准的情况下,用描述性的方式来使用分数绝对没有错。也就是说,我也认为有理由认为,虽然运动员总积分本身在描述团队层面的成功方面做得很好,但在团队中分配个人荣誉方面,他们可能比战争差得多,我们知道战争在这方面远非完美。
也许一个更好的方法来确定这些指标如何在队友之间恰当地分配信用,是转移到测试模型的预测能力。虽然一个模型说塞德里克·帕盖特是坦帕湾在 2018-2019 年成功的真正驱动力,可能已经通过了那个赛季的描述性测试,但一旦帕盖特换了球队,它就会大大失败,并有望以坦帕为代价为他的新俱乐部带来重大成功。
我为预测测试选择的方法仍然相当简单:使用目标指标作为第一年的比率统计,并将其插入到第二年玩家的游戏量中。在这种情况下,我首先在第一年使用 WAR 来预测第二年的排名:
我获得了 0.35 的 R 值,这是相当可靠的:这意味着第二年运动员预期贡献的胜利数足以解释团队层面上 35%的积分差异。另外的 65%可以用一些因素来解释,比如运动员的表现比他们预期的更好或更差,模型从未见过的运动员,第一年比赛少于 10 场因而被忽视的运动员,守门员和纯粹的运气。
然后我用点数重复了同样的测试:
再一次,就像描述性测试一样,溜冰者的分数显然远远高于其他人,但他们比像我这样的分析极客可能建议的做得更好。再说一次,在没有更好的衡量标准的情况下,我认为使用选手的总分就可以了。
我也很好奇,想看看战争的方式是如何累积到像积分这样的团队级别的指标的。使用排名分数进行描述性测试是没有意义的,因为这样做意味着将第一年的排名分数与第一年的排名分数进行比较。这些是完全相同的指标,将为您提供 1.0 的 R。但是使用第一年的积分来预测第二年的积分是完全有效的,所以我也选择这样做:
类似于运动员积分,单独的团体积分可以很好的预测未来的积分,但是它们不能叠加到战争中。如果你想确定下一年谁是最好的球队,并且你很清楚每个人会为每支球队打多少比赛,那么你会比只有去年的积分榜上的分数的情况好得多。
限制
这些模型有很多局限性:预期目标模型甚至不能被归类为好的,并且推动一些战争组成部分的岭回归有很大程度的误差。我也相信在玩家层面,这种特殊的战争模式过于强调射击,而对游戏驾驶重视不够。这有两个原因:
- 播放驱动成分通过岭回归获得,岭回归使系数偏向 0。这对于避免极端古怪的值是必要的,但这也意味着该模型可能会低估玩家在游戏驾驶上的真实每分钟贡献,并假设他们比实际情况更接近于平均玩家。
- 替换级别射击远低于平均水平比替换级别玩驾驶。我肯定这是为什么,但我认为,虽然这种差异在数学上被夸大了,因为对于上场时间较少的球员,模型将系数偏向 0 的程度更高,但这种差异也确实存在,因为教练更容易识别并迅速减少投篮不佳的运动员的冰上时间。
如果我根据我个人认为重要的东西,以完全任意的方式来“衡量”每一个组成部分,这个模型将更少地强调射击,而更多地强调游戏驾驶。如果我用战争来判断一个团队的收购,我会记住这样一个事实,即游戏驱动部分比射击更具可重复性。
这完全是在说,战争不仅是对过去附加值的不完美估计,而且在预测未来业绩时,应该考虑比战争更多的东西。简而言之,战争作为任何讨论的起点比作为终点更好。
考虑到这些限制,我还将声明 WAR 是对仍然被更频繁引用的更基本的度量标准的明显升级。你不一定要喜欢或使用战争,但如果你每次看到战争都捂住眼睛,或每次听到战争都塞住耳朵,并坚持使用滑手点数来代替,你会错得更多。
群体的智慧——投票分类器,装袋粘贴,随机森林和额外的树
使用多种算法,集成学习,用 python 实现
***Table of Contents*****1\. Introduction
2\. Voting Classifier
3\. Bagging and Pasting
3.1\. Out of Bag Evolution
3.2\. Random patches and Random Subspaces
4\. Random Forest
5\. Extremely Randomized Trees**
在 Unsplash 上 Cliff Johnson 拍摄的照片
1.介绍
在大多数问题上,人们在做决定之前都会咨询别人的意见。在决定一个集体环境时,通常是多数人说了算。虽然即使在个人层面上也是如此,但一些公司在全球层面上调查许多事情。由一群人而不是一个专家做出的决定被称为**【群体的智慧】**亚里士多德在他的著作《政治学》中首次使用了这一论点。这种方法集成到机器学习中的部分是集成学习。最简单的形式是训练多个模型并比较它们的结果来解决复杂的问题。目的是通过考虑泛化来提高监视下的准确率,并获得更成功的结果。本文通过最小化方法以及如何在 python 中实现方法来涉及决策机制。
2.投票分类器
投票分类器,顾名思义,是一种‘投票’——基于民主的分类。用一句话来解释,可以定义为多数表决。让我们假设我们用三种不同的算法来训练我们的分类问题——SVM、逻辑回归、决策树——我们在每一种算法中获得了不同的成功率。另一方面,投票分类器具体评估每个测试数据的结果,并决定支持投票占多数的一方。训练数据的测试结果如图 1 所示,不正确的预测与 y_test 进行比较,并以红色显示。从准确性结果来看,可以看出巨大的差异。当然,没有预料到真实数据集会有这样的差异,但是图 1。演示了投票分类器的工作原理。由于有 3 个分类器,投票分类器决定了其中至少有 2 个是相同的。这种基于民主多数投票的分类被称为硬投票分类器。
图一。投票分类器是如何工作的?,作者图片
软投票分类器采用更加灵活的策略。就像在硬投票中,评估每个分类器的结果,但后退一步就会出现,也就是说,在进行预测时,它会观察分类器的概率值。例如,让我们考虑测试结果为
- SVM[0 级=0.9,1 级=0.1],
- 决策树分类器[class-0 =0.4,class-1=0.6],
- 逻辑回归[0 类=0.35,1 类=0.65]
如果我们用硬投票分类器对其进行评估,因为有 2 个 1 类,而只有 1 个 0 类,硬投票分类器会判定为 1 类。如果用软投票分类器来评价:
- 0 类的概率:0.33 * 0.9+0.33 * 0.4+0.33 * 0.35 = 0.5445
- 第一类的概率:0.33 * 0.1+0.33 * 0.6+0.33 * 0.65 = 0.4455
可以看出,与硬投票不同,软投票分类器的偏好将是 0 类。
现在,让我们用 python 实现上面提到的乳腺癌数据集:
**OUT
LogisticRegression() 0.9525539512498058
DecisionTreeClassifier() 0.9227293898462972
SVC(probability=True) 0.9437820214252446
KNeighborsClassifier() 0.9455519329296692
VotingClassifier(estimators=[('lr', LogisticRegression()),
('dt', DecisionTreeClassifier()),
('svc', SVC(probability=True)),
('knn', KNeighborsClassifier())]) 0.9490451793199813
VotingClassifier(estimators=[('lr', LogisticRegression()),
('dt', DecisionTreeClassifier()),
('svc', SVC(probability=True)),
('knn', KNeighborsClassifier())],
voting='soft') 0.9455364073901569**
SVC 中*‘概率’的默认值为假。对于添加predict_proba()
方法,该值被定义为真。这是必要的。在投票分类器中,所有模型都用相同的数据集进行训练和测试。如图所示,软和硬的区别由“投票”*决定。
3.装袋和粘贴
另一种方法 bagging 及其衍生方法根据训练数据从投票分类器中分离出来。训练数据集被分成子集,分类器用这些子集来训练。在培训后的测试过程中,民主发挥作用,通过硬投票做出决定。如果随机选择行的子集进行替换,这称为引导聚合(Bagging) 。为了解释表达式*“with replacement”*,对应于一个分类器中用于训练的特定数据被另一个分类器使用的表达式是 with replacement。这个事件被称为引导。背后的想法是基于统计的,它用于估计数量。通过将数据集随机划分为子样本来评估数据集。在该划分期间,在多个子集中具有相同数据的事件用上述 with replacement 解释,并在 sklearn 中用bootstrap=True
定义。
另一方面,如果随机选择行的子集而不替换,这被称为粘贴。任何分类器在粘贴过程中使用的数据都不允许被另一个分类器使用。
由于在这些过程中使用了多个分类器,虽然偏差变化不大,但它降低了模型的方差,保护了模型不至于过拟合,模型的稳定性增加,变得更加通用。现在让我们用 python 实现这些提到的方法:
**OUT
bagging results [0.89473684 0.97368421 0.95767196]
average of bagging: 0.9420310034345123
********************************************
pasting results [0.89473684 0.97368421 0.96296296]
average of pasting: 0.9437946718648473
**********************************************
可以看出,装袋和粘贴的区别是由 bootstrap 决定的。如果拾取数据时有替换,即引导,则称为引导聚集(bagging) ,如果拾取数据时没有替换,即没有引导,则称为粘贴。
到目前为止,一直使用分类,但它也可以用于回归:
**OUT
numeric_cols Index(['wheelbase', 'carlength', 'carwidth', 'carheight', 'curbweight','enginesize', 'boreratio', 'stroke', 'compressionratio', 'horsepower','peakrpm', 'citympg','highwaympg'],
dtype='object')
categorical_cols Index(['fueltype', 'aspiration', 'drivewheel', 'enginetype', 'cylindernumber','fuelsystem'],dtype='object')
bagging results [0.88065606 0.84712127 0.7973921 0.72010695]
average of bagging: 0.8113190957164755
**********************************************
使用汽车价格数据集。分类列用OneHotEncoder
编码,大多数数字列用StandardScaler
标准化,一些数字列用宁滨分组。然后,我们获得的新数据集包含了max_features=0.4
,即 40%的特征和max_sampeles=0.8
,即 80%的数据。
3.1.出袋进化
**理论:**如果数据集足够大,bootstrap=True,36.79%的数据根本不会被选择。
**证明:**提到了一些数据被选择并用于一个以上的带有装袋的分类器。没有被任何分类器使用的选择数据的概率是(1–1/m ),其中 m 是样本数。
让我们概括一下:不拾取任何分类器都不使用的数据的概率是(1–1/m)ᵐ.当“m”足够大时,统计上它相当于(1–1/e)= 0.6321。
这意味着 63.21%的训练样本被每个分类器提取,36.79%的训练样本未被采样。这 36.79%的样本被称为**。由于在训练期间没有“oob”数据,这些数据可用于评估( test_data )。我们可以在 Scikit 学习库中实现它,如下所示:**
**OUT
bagging oob results: [0.88811189 0.97183099 0.95774648 0.97183099]
average of oob: 0.9473800847040283**
这只能通过设置 oob_score=True 来实现。
3.2.随机补丁和随机子空间
装袋的超参数:
max_features:
决定选择多少/多少特征。Float 表示百分比,integer 表示数字。max_samples:
决定采样多少数据。Float 表示百分比,integer 表示数字。bootstrap:
布尔,真意味着有替换;假的意思是没有替换。bootstrap_features:
布尔,True 表示特征是用替换绘制的;False 表示绘制的要素没有替换。
所有训练实例(bootstrap=False
,max_samples
为高(默认值=1)),但采样特征(bootstrap_features=True
)被称为随机子空间**。**
**OUT
randomsub 0.929824561403508**
训练和特征都被采样,称为随机面片**。**
**OUT
patch 0.9649122807017544**
这些主要用于高维数据集,如图像数据集。图像中有大量的特征,成百上千的像素,通过使用这些方法可以获得更可靠的结果。
4.随机森林
Bagging 已经用分类器方法的相同数据集的不同训练子集训练了多次。如果要选择分类器 DecisionTreeClassifier ,决策树的更优化版本 RandomForestClassifier 也可以作为 bagging 的替代方案。
**OUT
random forest results: [0.92105263 0.97368421 0.95767196]
average of rf: 0.9508029332590736
********************************************
bagging random forest results: [0.91578947 0.97368421 0.96296296]
average of bagging rf: 0.9508122157244964
**********************************************
5.额外的树
全名是极度随机树**,它建造多棵树以及随机森林。与其他不同,自举不提供随机性,而是在每个节点随机分裂节点。因为在每个节点上都提供了随机分割,所以方差非常低。**
**OUT
extra tree results: [0.91052632 0.98421053 0.96296296]
average of extra tree: 0.9525666016894087**
比较树间方差,说的是决策树方差>随机森林方差>极度随机化树方差。
回到指引点击这里。
**https://ibrahimkovan.medium.com/machine-learning-guideline-959da5c6f73d **
预测人群的智慧
用 Python 语言实现时间序列的集合预测
Chuotanhls / Chu Viet Don,黑人群体 Pixabay 上的免费照片
在昨天的文章中,我们讨论了飞镖多方法包以及如何在给定的时间序列上运行预测锦标赛。我们审查了五个选择的模型——指数平滑、Theta、SARIMA、nave forecast 和 Facebook Prophet——中的哪一个会更好地拟合源数据,即 Box & Jenkins 的经典航空公司乘客人数。时间序列预测的飞镖瑞士刀| 2021 年 10 月|走向数据科学
如果你读了这篇文章,你可能记得预测图的网格已经离开了右下角的第六个单元格,明显是空的。
作者图片
让我们通过比昨天更进一步来填补这一空白:
我们将创建一个集合预报,它结合了五种方法,不超过一行代码。
集合方案会显示出单独的方法都无法提供的预报质量吗?
作者图片
1.创建集合预报
在我们讨论集合预报背后的概念之前,让我们生成一个集合预报,然后将它的结果与单个模型进行比较,看看它能提供什么见解(如果有的话)。
我们拿起昨天的多方法预测教程中使用的 Jupyter 笔记本,在 dependencies 单元格中添加一行:我们导入 Darts 的**RegressionEnsembleModel。
作者图片
为了连接我们在变量模型中列出的各个方法,
作者图片
我们添加一行代码来处理集合预报:RegressionEnsembleModel 采用
- 型号列表作为其第一个参数;
- 以及我们想要训练集合的周期数。
接下来的三行与他们对单个预测者的描述相同:
- 训练模型
- 计算预测
- 计算残差
我们将RegressionEnsembleModel插入到一个集合评价函数中,该函数与昨天多方法笔记本中的模型评价函数具有相同的结构。在评估功能中,我们还计算准确性指标并绘制预测图。
作者图片
我们调用评估函数,其中包含我们希望它联系在一起的模型列表。
作者图片
作者图片
我修改了多方法 Jupyter 笔记本的准确性指标。由于我们需要不止一次地访问度量标准的公式——首先是单个方法,然后是整体——我将它们包装在一个函数中,我们可以用一行代码调用它,而不是在脚本中重复所有的代码行。
作者图片
我们调用度量函数,收集集合的准确性度量,并将它们与单个方法的准确性度量组合在一个数据框架中。
作者图片
集合预报返回与任何单独方法相同的输出:我们可以绘制的预报值;残差;和预测准确性度量。
作者图片
2.合奏贡献了什么?
集合提供了我们从其他方法中无法获得的洞察力吗?
作者图片
我们回顾了这些指标,以发现集成是否比其他方法有所改进。
请注意,我还添加了一个“avg”列,它是每个指标的 5 个其他方法的简单平均值。当然,总体指标不等于平均栏中的值,这是没有意义的。平均值不会对所有单个方法的改进有所贡献,因为它位于最好和最坏情况之间的 T2。
相比之下,回归集成模型能够做出比最好的个体方法更准确的预测。不能保证这种改善。您可以在度量表中看到,集合模型在三个度量方面领先于该领域:就 RMSE、R 平方和预测的标准误差而言,与 Theta 方法相比,差距较小。但在 MAPE 一行,θ以同样小的幅度领先。
RMSE 对预测误差进行平方,对较大误差的惩罚力度比 MAPE 更大。当残差的分布是左偏或右偏时,就会出现偏差。平均值将高于或低于中间值。最小化 RMSE 的预测将显示出较小的偏差。但是对于具有许多异常值的源数据,对异常值的敏感性可能不是优选的。在文献和评论部分,你可以找到关于 RMSE 和 MAPE 的相对优势和劣势的热烈讨论,以及许多其他指标的利弊。因此,我们不能通过一个简单的判断,一劳永逸地说,MAPE 或 RMSE 在决定模式之间的赛马方面更胜一筹。
这个集合的 R 平方要高半个百分点,所以它解释了实际观测中运动的额外部分。
预报的标准误差 se (也就是预报误差的估计标准偏差),对于集合预报来说要小一点。简单回归数学(duke.edu)
根据我们的指标,投票结果是 3 比 1 支持集合模型。但这种“投票”并不是宣布方法之间竞赛获胜者的硬性标准。对于其他时间序列,集合预报肯定可以获得更好的预报质量,并显示更明显的指标差异。因为创建集成只花费了我们三行代码,所以当我们在我们的源数据上运行方法比赛并发现它是否能比其他方法增加改进时,将集成添加到单独的模型中是值得的。
在这里,我们刚刚证实了 Theta 方法非常接近整个预测者群体的智慧,如此接近,以至于两者几乎可以互换。
3.使用集合结果
我们可以像处理任何单独方法的结果一样处理集合预报值。合奏只是另一个飞镖模型,一个我们在飞行中创建的模型,一个为具体的时间序列量身定制的模型,而不是像西塔或 ARIMA 那样带有公共名称标签的理论方法。
我们可以在系综上运行 Darts 的 plot_residuals_analysis() 函数。
作者图片
我们将合奏添加到右下角先前空着的第六个子剧情中。
作者图片
统计模型的永盒测试和统计模型的正态性测试也可以应用于总体的残差。
作者图片
3.集合预报背后的概念是什么?
集合预报的目标是获得一个更稳健的模型,并且可以更好地推广到新的数据点或其他时间序列。组合预测器的目的是减少预测的标准误差。
在处理具体的时间序列、识别和处理异常值或处理其趋势或季节性变化时,个别方法可能会表现出一些弱点。如果我们简单地用一种不同的方法代替一种方法,我们就有可能在某个时候被第二种方法的某些弱点绊倒。通过组合几种方法,预测者群体的智慧可以——在许多情况下,但不一定在所有情况下——消除单一方法模型的弱点。
用户选择 Darts 作为整体构建模块的方法。然后 RegressionEnsembleModel (如果我们没有指定额外的参数)将运行一个线性回归模型,将预测者作为其回归变量。回归计算所选预测器的线性组合,使预测值与实际观测值最接近。因此,回归模型研究每种方法在集合中的整合(加权)程度,以最小化与实际观测值的偏差。回归变量是复杂预测方法本身的结果,而不仅仅是简单的来源数据。回归目标,即系综,形成加权平均预测值。
RegressionEnsembleModel 接受比简单线性回归更复杂的集成函数,前提是这些函数通过实现 fit()和 predict()方法遵守 scikit-learn 模式。
Jupyter 笔记本可以在 GitHub 上下载:h3ik0th/Darts _ ensembleFC:Python 时间序列用 Darts 进行集合预报(github.com)
随着大数据的出现,了解正确背景变得越来越重要
在开发算法之前,理解 3 个关键问题中的业务目标
布雷特·乔丹在 Unsplash 上的照片
上下文,语境,语境。在数据科学领域,背景就是一切。我认为数据科学家犯的最大错误是不理解他们的工作背景和业务目标。相反,他们非常注重了解人工智能/人工智能技术。复杂算法用在正确的地方是很棒的。理解那个地方应该在何时何地是困难的。
从理解你的商业目标开始。从我的目标开始,这是我在过去两个月里更加关注的事情。当我在解决一个问题时,我不想马上找到解决方案。相反,我需要与 SME 的合作,了解情况的背景以及他们如何看待数据。自从开始做顾问以来,我理解商业目标的策略已经改变了。我们来看一些问题,以及回答这些问题时需要考虑的事项。
为了做好准备,让我们挑选一个精简版的 Kaggle 竞争问题,并假设这是我们给定的业务目标:
预测每个商店销售的产品总量。
这个目标要求什么?
着眼于你的目标,确保你理解企业提出的问题。在本例中,数据是由俄罗斯最大的软件公司之一 1C 公司提供的。了解您的目标的一个方法是与您的主要利益相关者进行演示,并浏览目标和期望的结果。讨论你所知道的,以及你需要澄清或弥补的地方。
乍一看,这个目标似乎很简单——你需要预测每家商店销售的产品总量——但是深入挖掘会发现更多的问题。
回答这个问题需要哪些数据?
经过初步的数据分析,您可以了解您的数据集中有什么和没有什么。有了这些信息,您应该与您的利益相关者讨论如何利用他们当前的数据。同样,你可以讨论什么不是。如果你的目标因为缺少数据而无法实现,这就是你需要让大家知道的时候了。
对于我们的例子,让我们看看数据本身。即使为您提供了每日历史销售数据,这些数据也可能逐月发生变化。商店和产品的列表会发生变化,这意味着创建一个能够处理给定数据的模型会更具挑战性。同样重要的是要注意,我们在这些数据中有唯一的标识符。我们有shop_id
、item_id
和item_category_id
,给你一个独特的商店、产品和物品类别。
这里要注意的另一件事是文本列。给定的目标不会询问这些字段。因此,此分析不需要它们。重要信息包括售出产品的数量、每件商品的当前价格和日期。所有这些都可以在数据集中找到。到目前为止,看起来我们理解了主要目标,并且有可用的数据来实现它,但是最终用户会用这些信息做什么呢?
用户希望用这些数据做什么或实现什么?您的数据能做到这一点吗?
您的用户可能是业务分析师、SME 或其他人。当您为您的目标开发方法时,您需要确定他们将如何利用输出。坐下来与用户交流。分析完成后,他们想用这些数据达到什么目的?当我进行这些对话时,我发现有时候最初的目标并没有涵盖真正想要的东西。这意味着我可能必须返回并确定是否需要任何额外的数据来获得期望的输出。
我们知道我们的示例目标是预测每家商店销售的产品总量,但是如何使用它呢?假设我们正在与利益相关方进行对话,可能会出现以下情况:
- 了解特定商店将订购多少商品有助于确定下个月需要准备多少商品才能发货。我们可以根据已提供的信息来确定该输出。
- 知道有多少不同的项目被购买显示哪些项目是高利润与低利润。了解了这一点,企业就可以决定停止销售什么。如果你同意这种说法,你可能需要更多的数据来了解每个项目的利润率。
- 希望创建一个仪表板,根据一周中的某一天来查看销售项目的趋势。特定商品是否比其他商品更有可能在不同的日子卖出?如果你更深入地挖掘你最初的目标,你可能会发现不同的目标从中浮现出来。如果是这种情况,你需要重新组织你的工作,并确保你有数据。在这个数据集中,我们的数据是按天给出的,因此这种类型的分析可以根据一周中的某一天来分析商品的销售趋势。
我们可以继续,但我想你明白了。当你开始分析你的业务目标时,你可能会发现最初的目标发生了变化或增长。这是完全正常的。我发现如果你允许的话,这些对话可以持续很长时间。我喜欢把这种讨论的开始时间限制在最多一个小时。有了在那次会议中学到的信息,我就可以去修改我的方法,并根据更新寻求反馈。通过这项工作,您和利益相关者都将更好地理解您的数据、您修改的方法以及什么是可能的。
概括起来
业务目标在开始时可能看起来非常简单,直到它们不再简单。这就是为什么在我开始写代码和构建模型之前,我开始问自己三个问题。这些问题有助于确定问题的框架,了解是否有解决问题的数据,并计算出结果。通常,你会发现用户想要的结果并不总是他们最初所说的那样。相反,当你开始深入讨论他们在寻找什么以及数据能告诉他们什么时,他们的愿景会发生变化。这些问题是:
- 这个目标要求什么?
- 回答这个问题需要哪些数据?
- 最终用户希望用这些数据做什么或实现什么?您的数据能做到这一点吗?
在接近业务目标时,你还有其他想问的问题吗?如果是,它们是什么?它们如何帮助您构建解决方案?
如果你想阅读更多,看看我下面的其他文章吧!
有了 MAPIE,不确定性又回到了机器学习中
展示 MAPIE,这是 Quantmetry 开发的 scikit-learn 兼容包,可让您轻松估计与您最喜爱的 ML 模型相关的不确定性
在 25 年的成功故事之后,现代机器学习(ML)仍然与不确定性的概念不一致。置信区间并不流行,在致力于机器学习的顶级开源库中几乎不存在。然而,从商业角度来看,不确定性的概念深深植根于风险管理中,是任何可靠的人工智能(AI)在生产中的必备条件。在这篇文章中,我们介绍了 MAPIE,这是一个开源包,由 Quantmetry 作为 Quantlab R&D 项目开发,重新引入了人工智能中的不确定性概念,由模型无关和用户友好的开源实现提供支持。
人工智能预测总是不确定的
机器学习模型使用历史数据来进行预测。本质上,机器学习模型的预测总是不确定的,因为数据在质量和数量方面都是不完美的。因此,与人工智能模型预测相关的不确定性可分解为两种主要类型:分别因数据质量和数据数量而异的随机和认知不确定性。
**任意的不确定性。**第一,历史数据总是有噪音的。例如,噪声可能是由于用于测量工业机器的物理尺寸的捕捉器的有限精度造成的。这也可能是由于人类在图像标记过程中不可避免的错误造成的,这对于像医学图像这样的敏感领域来说可能是复杂的。
**认知的不确定性。**其次,用于训练我们的模型的采集数据总是有限的,并且不能完美地捕捉生活中发现的整体和复杂的真实分布。不完整的分布将导致模型参数的不确定性,这些不确定性将传播到模型预测中。例如,假设您想根据过去一周温度计上显示的温度来预测明天花园的每小时温度。然而,当你睡觉的时候,你不能读出午夜到早上 8 点之间的温度。因此,你的人工智能模型估计的预测将捕捉这种不确定性:它在夜间比在白天更不确定,仅仅是因为夜间缺乏数据!
人工智能对不确定性的需求
人工智能模型需要将不确定性与其预测关联起来,主要有三个原因。首先,让公众对人工智能算法做出的决定放心。第二,更好地确定公司部署的人工智能模型在生产中的限制。第三,为企业的决策和风险管理提供强有力的有用指标。
在自动驾驶或医学成像等敏感领域,估计与人工智能预测相关的不确定性已经是强制性的,在这些领域,人工智能做出的或基于人工智能做出的决定会对人类生活产生直接影响。量化不确定性在其他领域也将很快成为强制性的,因为模型准确性和稳健性的概念在欧洲议会和理事会关于人工智能监管的提案中起着核心作用此处。例如,我们可以引用该提案的第 3.3 节:“这些要求将涉及数据、文档和可追溯性、信息和透明度的提供、人工监督以及稳健性和准确性,并将是高风险人工智能系统的强制性要求。”。因此,评估人工智能模型的不确定性正成为所有处理高风险人工智能系统的公司的监管问题,无论他们的业务线是什么。除了高风险部门,所有认可机构都可以通过建立信任和支持变革管理来利用不确定性量化。
我们开发 MAPIE 的原因
在过去的几十年中,已经开发了各种旨在量化不确定性的方法。我们可以将它们主要分为四类:分位数回归、数据扰动、模型扰动和贝叶斯推断。
回归分析中主要不确定性量化方法的比较。
上表列出了一些方法的主要特点,强调了它们的优缺点。首先,像分位数回归这样的简单方法可以很容易地用来估计任何类型的模型的不确定性,但没有理论上的保证。另一方面,贝叶斯推理等复杂技术使得捕捉不同类型的不确定性成为可能,并得到强大的数学框架的支持,但通常是特定于模型的,计算量大,需要对方法进行深入分析和理解。
允许数据科学家容易地估计与任何类型的 ML 模型的预测相关联的不确定性的健壮包在数据科学界仍然缺乏。例如,scikit-learn 库没有提供量化不确定性的可靠和标准方法,只有少数模型提供了不确定性量化方法(如 50 年代由来已久的刀切法)。
这就是我们开发 MAPIE 的原因,MAPIE 代表模型不可知预测区间估计器。MAPIE 允许您通过一个简单的用户友好的 API,使用您最喜欢的 scikit-learn 兼容 ML 模型轻松估计不确定性。该软件包基于最先进的不确定性量化方法,具有强大的理论保证。目前,MAPIE 只能用于回归问题,但它在分类和其他设置方面的扩展正在积极开发中。
“和梅皮在一起,我确定我的不确定性”
MAPIE 基于 R. Foygel-Barber 等人(2021) [1]在一篇最新研究论文中介绍的重采样方法,用于估计回归设置中的预测区间,并提供了强有力的保证。MAPIE 实现了本文中至少 8 种不同的方法,特别是 Jackknife+和 CV+。
所谓刀切+方法是基于一组留一模型的构建:每一个被扰动的模型都是在去掉一个点的整个训练数据上进行训练的。然后根据这些扰动模型估计的留一残差的分布来估计区间预测。这种优雅方法的新颖之处在于,对新测试样本的预测不再像标准刀切法那样以基础模型估计的预测为中心,而是以每个扰动模型的预测为中心。这个看似微小的变化引发了一个重要的结果:估计的预测区间总是稳定的,并且在理论上是有保证的!
标准重叠和新重叠+策略的比较。
在实践中,当你的目标是 90%的置信区间时,这意味着你希望 90%地确信你的新观察的真实值在你的预测区间内。像标准自助法或刀切法这样的历史方法并不能给你任何保证,而且会有很大的不稳定性。利用这种方法,Foygel-Barber 等人描述的定理保证这种机会总是高于 80%,并且在实践中大多数时候非常接近 90%。换句话说,这意味着大约 90%的新测试样本的目标值将位于用刀切+方法估计的预测区间内。
但是,标准的 Jackknife+方法计算量很大,因为它需要计算与训练样本数量一样多的模型,因此可以采用一种更简单的交叉验证方法,称为 CV+。CV+方法充当标准交叉验证:在去除了每个折叠的整个训练集上训练 K 个扰动模型,其中 K 的范围通常从 5 到 10,并且计算相应的残差。对于 Jackknife+,预测间隔以每个折叠模型执行的预测为中心。因此,该理论保证了相同的稳定性,尽管预测区间通常稍宽,因为每个扰动模型是在较少数量的样本上训练的。
在交叉验证的成本过高的情况下,例如由于模型非常庞大或数据集非常庞大,仍然可以估计预测区间。你只需要把你的训练集分成一个训练文件夹和一个验证文件夹。训练折叠用于训练基础模型,而验证折叠用于通过残差估计来校准预测区间。下图描述了这一过程。
用于训练模型、校准不确定性和评估模型的数据集拆分。
从最先进的研究到最先进的包装
我们在 SimAI R&D 项目的框架内开发了 MAPIE。这个项目是位于巴黎的人工智能咨询公司 Quantmetry 和 ENS Paris-Saclay 在法兰西岛地区的财政支持下合作完成的。这个项目的关键点是开发一个 Python 包,允许数据科学家使用任何类型的 scikit-learn 兼容模型作为基本估计器来轻松估计不确定性。因此,这个包是用最先进的开发标准编写的:100%覆盖率的单元测试,在几个环境中与 Github actions 的持续集成,readthedocs 上的完整文档,以及 PyPi 和 conda 上的包发布。
实践中的 MAPIE
现在,我们以一个简单的x*sin(x)
函数生成的一维数据集为例,说明如何利用 MAPIE 轻松估算不确定性,该函数具有正常的同方差(即,其与 x 保持恒定)噪声。数据如下图所示。
我们示例中使用的一维合成数据。
如前所述,MAPIE 可以与任何种类的 sklearn 兼容回归器一起使用。这里,我们使用三种模型比较 CV+方法估计的预测区间:
-
用 scikit-learn 定义的简单多项式函数。
-
一个
XGBRegressor
模型,基于著名的并行树提升算法,附带一个 scikit-learn API。 -
一个简单的神经网络,这里是一个具有三个密集层的多层感知器,用 Tensorflow-Keras 定义,并使用
KerasRegressor
包装器兼容。
现在让我们使用 MAPIE 来估计使用 CV+方法的预测区间,并比较它们的预测区间。为此,MapieRegressor
对象可以像任何 scikit-learn 回归器一样使用标准的fit
和predict
顺序过程。我们只需要定义基本模型、方法和在初始化对象时估计残差的折叠次数。然后,我们在训练数据上拟合回归量,并在测试集上估计预测和预测区间。这里,我们将 alpha 值设置为 0.05,以便为我们的预测区间实现 95%的置信度。预测间隔的下限和上限保存在单独的 numpy 数组中。
下图比较了 MAPIE 估计的预测区间(由蓝色区域给出)和用于生成数据噪声的真实置信区间(由虚线给出)。
MAPIE 使用三种基本模型估计的预测区间:多项式函数(左)、XGBoost 回归器(中)和多层感知器(右)。
可以看出,用 MAPIE 估计的预测区间确实非常接近真实的置信区间。事实上,有效覆盖分数,即真实值位于预测区间内的测试样本的数量,对于 95%的目标覆盖分数是 97%。
这个例子说明了 MAPIE 在一个非常简单的案例中的使用。当然,我们可以将 MAPIE 应用于更复杂的回归任务,如果您想探索其他问题,我们邀请您查看 MAPIE 文档的图库示例。
关键里程碑和要点
我们已经开始开发 MAPIE,这是一个用 Python 编写的开源包,允许您估计与机器学习模型预测相关的不确定性,具有双重目标。首先,MAPIE 与 scikit-learn 完全兼容,因此可以应用于任何具有 scikit-learn API 的模型。其次,MAPIE 使用最先进的重采样不确定性量化方法,使您能够为预测区间获得强有力的理论保证。
MAPIE 可以通过 pip 或 conda 轻松安装,现在作为 scikit-learn-contrib 项目成为 scikit-learn 生态系统的一部分。我们在 ICML 的一个研讨会上展示了 MAPIE,该研讨会聚焦于无分布不确定性量化。
目前,MAPIE 只能用于单输出回归问题。但是 MAPIE 才刚刚开始它的旅程!我们正在积极地将 MAPIE 扩展到其他设置,如时间序列、多输出回归、多类分类或图像分割。欢迎在 github MAPIE 网页上提交问题或建议适合 MAPIE 的新项目。
加入
我在量子力学公司工作。Quantmetry 自 2011 年成立以来一直是先驱和独立的公司,是法国领先的纯人工智能咨询公司。在提供卓越的数据治理和最先进的人工智能解决方案的愿望的驱动下,Quantmetry 的 120 名员工和研究顾问将他们的热情投入到为所有行业的公司提供高业务成果的服务中。
参考
[1] Rina Foygel Barber,Emmanuel J. Candès,Aaditya Ramdas 和 Ryan J. Tibshirani,用折叠刀+ 进行预测推理(2021)。安。统计学家。, 49(1):486–507.
数据科学中的女性(WiDS)ka ggle 上的 Datathon
我参与 WiDS 的经验和预测糖尿病的有希望的尝试
克里斯蒂娜@ wocintechchat.com 在 Unsplash 上的照片
第四届年度 WiDS Datathon 关注社会影响,即患者健康,重点关注糖尿病的慢性疾病。
该比赛由 WiDS 全球团队在斯坦福、西部大数据创新中心、 WiDS 数据大会委员会组织,并于 Kaggle 发起。
比赛的数据集由麻省理工学院的 GOSSIS(全球开源疾病严重程度评分)倡议提供。
此外,在线会议将于 2021 年 3 月 8 日举行。顶级数据科学女性之声将亲临现场,就各种主题发表见解。
只是为了达成共识
APACHE —急性生理、年龄和慢性健康评估,美国开发的严重性评分和死亡率估计工具。
ICU —重症监护病房,医院或医疗保健机构中提供重症监护药物的特殊部门。
身体质量指数——身体质量指数,一种基于身高和体重的身体脂肪测量方法。
问题描述
比赛的目标是确定入住 ICU 的患者之前是否被诊断患有糖尿病(一种特殊类型的糖尿病)。
您应该使用在患者重症监护的前 24 小时内收集的数据,建立糖尿病预测模型。
你很可能不是医学背景,所以我会推荐你在这里(美国糖尿病协会)阅读一篇简短的疾病概述。
数据探索
我从datadictionarywids 2021 . CSV文件开始数据探索,详细描述了所有特性。
共有 181 项功能,分为几个类别:
- 人口统计数据
- 阿帕奇协变量
- 阿帕奇共病
- 重要器官
- 实验室
- 实验室血液气体
- 糖尿病标志—目标变量
每个类别提供了每个特性的详细信息,即测量单位、描述、示例等。
在人口统计功能组中,我决定看一看每一个功能,并决定它是否对模型构建有用。
- Age 功能会很有用,但是我们需要在功能工程阶段进一步改造它。
- IBM 的功能对我们来说也很有趣,因为根据美国糖尿病协会的规定,任何体重指数高于 25 的人,无论年龄大小,都应该接受糖尿病筛查。
- 种族应该不会影响糖尿病,而且看起来大多数情况下它只包含一个种族,所以这个不会被进一步使用。
按种族分列的患者人数
- 性别可能有用,但需要使用编码进行转换,因为初始数据集仅包含“F”和“M”值。
- 身高和体重已经包含在 IBM 的计算中,因此不再需要。
- 除了 ICU id 和医院入院来源之外,我不会使用任何与医院或 ICU 类型、、相关的功能。
APACHE 协变量、生命体征、实验室和实验室血气组与各种医学测试的结果相关,因此包含许多内部特征。我决定检查它们和目标变量之间的相关性,而不是使用相关矩阵逐个检查。
对于模型训练,我将使用相关性大于 0.1 阈值的特征。在大多数情况下,选择的特征与葡萄糖浓度和血压有关。
实验室功能组中某些功能的相关矩阵
阿帕奇共病组从一些问卷中获取关于患者是否有某些诊断或病症的信息。该组中没有特征被选择,因为与糖尿病没有线性相关性。
APACHE 共病特征组的相关矩阵
探索数据集中有多少 NA 值也很重要。总的来说,大约有。130k 观测值,使用超过 50k NAs 的特性毫无意义。为它们处理缺失值是没有意义的,因为这会用相同的值创建 50k 个观察值,并使模型更加有偏差。
数据集是半空的。
深入到这个主题,7 个具有显著相关性的特性每个都有超过 70k 的 NAs,因此我们将放弃它们。作为参考,它们是
- h1_bun_min 和*_max,
- h1 _ 肌酐 _ 最小值和* _ 最大值,
- h1 _ 葡萄糖 _ 最小值和* _ 最大值,
- h1_diasbp_invasive_min。
也有四个或更多特征缺失的观测值。在缺失值处理过程中,您将获得只是其他观察的副本的观察。我们有 ca。我删除了 19k 个这样的观察值,以保持数据集中的原始分布。
性别特征缺失值替换为模式。其他 NAs 将被替换为中间值。
应在训练/测试分割后处理缺失值,因为训练数据集可能会影响测试数据集。
特征工程
我使用常识、与目标变量的相关性和 NAs 阈值选择了最终的特征集。他们是
- 年龄,
- bmi,
- icu_id,
- 医院 _ 入院 _ 来源,
- 性别,
- d1_bun_min 和*_max,
- d1 _ 肌酐 _ 最小值和* _ 最大值,
- d1 _ 葡萄糖 _ 最小值和* _ 最大值,
- h1 _ 葡萄糖 _ 最小值和* _ 最大值,
- arf_、bun_、葡萄糖 _*和肌酐 _apache。
如图所示,年龄和诊断为糖尿病的人的百分比之间存在相关性。
年龄特征也可以被分组到箱中。然而,不清楚应该创建多少个箱,以及选择哪一个——固定宽度宁滨还是自适应宁滨。所以你需要试验一下,找到最合适的形式。我添加了年龄特性,没有做任何转换。
所有分类特征都应该使用编码进行转换。例如,具有“F”和“M”值的年龄特征应该转换为具有 0 和 1 值的特征。这可以通过使用 scikit-learn 编码器之一来实现。
模特培训
为了找到最佳参数,我使用了带有两个评分标准的 GridSearch ,并优化了以下参数:n_estimators、max_depth、learning_rate 和 alpha。GridSearch 也使用交叉验证,所以我把折叠数设为 3 (cv=3)。这非常有用,因为这样你就不需要单独运行交叉验证了。
scor = {'AUC': 'roc_auc',
'Accuracy': metrics.make_scorer(metrics.accuracy_score)}
grid_param = {"colsample_bytree": [1],
"learning_rate": [0.01, 0.1, 0.5],
"n_estimators": [250, 300, 500],
"alpha": [0.1, 0.5, 1],
"max_depth": [2, 4, 5, 10]}
model = xgb.XGBClassifier(learning_rate = 0.1, alpha = 0.1)
grid_mse = GridSearchCV(estimator=model, param_grid=grid_param,
scoring=scor, cv=3, verbose=1, refit='AUC')
grid_mse.fit(x_train, y_train)
print("Best parameters found: ", grid_mse.best_params_)
有时参数的最佳值可能是我的网格值的最大值或最小值。所以我用这个参数的不同值再次运行 GridSearch。此外,您可以指定几个评分标准来进行更全面的评估。
也有像 GridSearch 这样的其他选择,例如随机搜索或任何其他方法。
模型评估
看一下学习曲线,很明显曲线之间的差距很小。它表明了我们模型的低方差。训练集中的误差并不是很高,因为该模型的精度约为 0.85。尽管如此,我们希望它变得更好。
在这种情况下,添加更多的观察值对我们没有帮助,因为模型的方差很低。看起来,增加模型的复杂性应该可以改善它,即增加新的功能或使用更复杂的算法。
如果学习曲线与您想要的准确度更高之间有很小的差距,额外的复杂性会有所帮助。
中华民国 UAC 证实了通过探索学习曲线得出的结论。曲线下面积为 0.85,这是一个很好的结果,但还可以改进。
尽管这是一个很好的起点,但模型本身肯定不能给我们所期望的精确度。它可以用作集成模型中的分类器之一。
摘要
总的来说,参与 WiDS 竞赛和研讨会让您有机会学习新方法、提出问题并与志同道合的专业人士讨论解决方案。
竞争。学习。成长。
不管你是数据科学的新手还是老手,你都可以从 WiDS Datathon 委员会找到一些教程来帮助你入门。
单词嵌入技术:Word2Vec 和 TF-IDF 讲解
需要让这些词对机器学习或深度学习算法有意义。因此,它们必须用数字来表示。诸如 One Hot Encoding、TF-IDF、Word2Vec、FastText 之类的算法使单词能够以数学方式表示为用于解决此类问题的单词嵌入技术。
Camille Orgel 在 Unsplash 上拍摄的照片
单词嵌入
单词嵌入技术用于以数学方式表示单词。One Hot Encoding、TF-IDF、Word2Vec、FastText 是常用的单词嵌入方法。根据数据处理的状态、大小和目的,这些技术中的一种(在某些情况下是几种)是首选的。
*一个热编码
用数字表示数据的最基本的技术之一是热编码技术[1]。在这种方法中,按照唯一单词总数的大小创建一个向量。向量的值被分配,使得属于其索引的每个单词的值是 1,而其他的是 0。作为一个例子,可以研究图 1。
图一。一个热编码的样本
在图 1 中,名为“X”的列由 3 个不同的单词组成。当对该列应用一个热编码时,创建了表示每个表达式的 3 个不同的列(换句话说,为每行创建了 3 个单位向量)。对应于每行中单词的列用值 1 填充,其他的用 0 填充。因此,这些表达被数字化了。它通常用于没有太多语言数据多样性的情况,并且不需要表示数据之间的语义和统计关系。
* TF-IDF
TF-IDF 是一种统计方法,用于确定文档中单词的数学意义[2]。矢量化过程类似于热编码。或者,对应于该单词的值被赋予 TF-IDF 值而不是 1。TF-IDF 值通过将 TF 和 IDF 值相乘获得。作为一个例子,让我们找到由 1 个句子组成的 3 个文档的 TF-IDF 值。
[他是沃尔特],
[他是威廉],
[他不是彼得或九月]
在上面的例子中,“他”在所有 3 个文档中使用,“是”在 2 个文档中使用,“或”只在一个文档中使用。根据这些,我们分别求出 TF,然后求出 IDF 值。
- TF(词频)
用最简单的术语来说,术语频率是文档中目标术语的数量与文档中术语总数的比率。如果根据上面的例子计算 TF 值,它将是
[0.33, 0.33, 0.33],
[0.33, 0.33, 0.33],
[0.20, 0.20, 0.20, 0.20, 0.20]
- IDF ( 逆文档频率 )
IDF 值是文档总数与目标术语出现的文档数之比的对数。在这个阶段,这个术语在文档中出现多少次并不重要。确定是否通过就足够了。在这个例子中,要取的对数的底值被确定为 10。但是,使用不同的值没有问题。
“他”:Log(3/3)= 0,
“是”:Log(3/2):0.1761,
“或者,彼得,.”:对数(3/1) : 0.4771
因此,获得了 TF 和 IDF 值。如果使用这些值创建矢量化,首先会为每个文档创建一个向量,该向量由等于所有文档中唯一单词数量的元素组成(在本例中,有 8 个术语)。在这个阶段,有一个问题需要解决。如术语“he”所示,由于 IDF 值为 0,因此 TF-IDF 值也将为零。但是,在矢量化过程中没有包含在文档中的单词(例如,第一句中没有包含短语“Peter”)将被赋值为 0。为了避免混淆,对 TF-IDF 值进行平滑处理以进行矢量化。最常见的方法是在获得的值上加 1。根据目的,可以在以后对这些值应用规范化。如果矢量化过程是根据上述内容创建的;
[1. , 1.1761 , 1.4771 , 0. , 0. , 0. , 0. , 0.],
[1. , 1.1761 , 0. , 1.4771 , 0. , 0. , 0. , 0.],
[1. , 0. , 0. , 0. , 1.4771 , 1.4771, 1.4771 , 1.4771],
* Word2Vec
Word2vec 是另一种常用的单词嵌入技术。扫描整个语料库,并通过确定目标单词更经常出现的单词来执行向量创建过程[3]。这样,单词之间的语义接近度也就显现出来了。例如,让序列中的每个字母…x y A z w…,…x y B z k…和…x l C d m…代表一个单词。在这种情况下,word_A 会比 word_C 更接近 word_B,当在向量构成中考虑到这种情况时,单词之间的语义接近程度就用数学来表示了。
图二。单词相似度
图 2 显示了 Word2Vec 中最常用的图像之一。这些单词之间的语义接近度是向量值彼此之间的数学接近度。经常举的一个例子是“国王-男人+女人=王后”这个等式。这里发生的情况是,作为向量彼此相减和相加的结果而获得的向量值等于对应于“queen”表达式的向量。可以理解,单词“king”和“queen”彼此非常相似,但向量差异仅因其性别而产生。
在 Word2Vec 方法中,与 One Hot Encoding 和 TF-IDF 方法不同,执行无监督学习过程。通过人工神经网络对未标记的数据进行训练,以创建生成单词向量的 Word2Vec 模型。与其他方法不同,向量大小不像语料库中唯一单词的数量那么多。向量的大小可以根据语料库的大小和项目的类型来选择。这对于非常大的数据尤其有益。例如,如果我们假设在大型语料库中有 300 000 个唯一单词,当用一个热编码执行向量创建时,为每个单词创建 300 000 大小的向量,其中只有一个元素的值为 1,其他元素的值为 0。然而,通过在 Word2Vec 侧选择向量大小 300(它可以根据用户的选择或多或少),避免了不必要的大尺寸向量操作。
图 3。Word2Vec 模型中单词的矢量化
“Royal”的矢量化可以在图 3 中看到。如果用一个热编码将五个单词句子中的单词“Royal”矢量化,则获得第一个向量表达式(输入向量)。可以看出,这个向量的大小和句子中的总字数一样多。但是,如果矢量化过程是用 Word2Vec 完成的,这一次将创建一个包含三个单位[5,12,2]的向量。
ka ggle(https://www.kaggle.com/anu0012/hotel-review)中的酒店评论数据集用于应用 Word2Vec 模型训练。作为例子给出的所有代码都可以在这里找到。
因为区分大小写,所以所有单词都转换成小写。然后,清除特殊字符和停用词。nltk 库用于无效单词。如果需要,这些单词也可以完全手动确定。执行这些操作之前的一个例句如下。
“我丈夫和我在这家酒店住过几次。虽然不是最高档的酒店,但我们喜欢这样的事实,我们可以步行大约英里到芬威。它很干净,工作人员也很通融。我唯一的抱怨是浴室里的风扇很吵,而且当你开灯的时候它会自动运转,我们也尽可能地让灯保持关闭。我们住过收费较高的酒店,包括网络和早餐。会再次留在那里。”
数据预处理后会出现的新情况如下。
“丈夫住过的酒店时代虽然最华丽的酒店爱事实步行英里芬威清洁员工住宿投诉范浴室噪音去自动关灯试着保持轻多可能我们住过的酒店收费较高互联网早餐包括住宿”
这些过程完成后,进行 Word2Vec 训练。培训期间使用的参数:
min_count : 目标词在语料库中出现的最小次数。特别是对于非常大的复合病毒,保持这个限制较高会增加更多的成功率。但是,对于小型数据集,保持较小的大小会更准确。
**窗口:**直接影响目标表达式向量计算的是相邻词的数量。比如“他是一个很好的人。”对于 window =1,单词“a”和“good”在“very”单词向量的形成中是有效的。当 window = 2 时,单词“是”、“一个”、“好”和“人”在创建“非常”单词向量时是有效的。
size : 它是为每个元素创建的向量的大小。
alpha : 初始学习率
min_alpha : 是训练时学习率会线性下降的最小值。
sg : 指定训练算法将如何工作。如果 sg 的值为 1,则使用 skip-gram 算法,否则使用 CBOW 算法。
图 4。Skip-gram vs CBOW
CBOW(连续单词包)和 Skip-gram 算法之间的区别可以在图 4 中看到。在使用 CBOW 算法的训练中,与目标单词相邻的单词作为输入给出,而目标单词本身作为输出获得。在 skip-gram 算法中,目标单词本身作为输入给出,相邻单词作为输出获得。
**工作人员:**培训可以并行进行。用于此目的的内核数量可以通过 workers 参数来确定。
如果您想通过使用作为训练结果获得的模型来查看单词“great”的向量;
w2v_model["great"]
>>>array([ 3.03658217e-01, -1.56424701e-01, -8.23674500e-01,
.
.
.-1.36196673e-01, 8.55127215e-01, -7.31807232e-01, 1.36362463e-01],
dtype=float32)print(w2v_model["great"].shape)
>>>(300,)
最接近“伟大”、“可怕”、“波士顿”、“代客”的 10 个词如下。
w2v_model.wv.most_similar(positive=["great"])
>>>[('excellent', 0.8094755411148071),
('fantastic', 0.7735758423805237),
('perfect', 0.7473931312561035),
('wonderful', 0.7063912153244019),
('good', 0.7039040327072144),
('amazing', 0.6384587287902832),
('loved', 0.6266685724258423),
('nice', 0.6253951787948608),
('awesome', 0.6186609268188477),
('enjoyed', 0.5889394283294678)]
---------------------------------
w2v_model.wv.most_similar(positive=["terrible"])
>>>[('bad', 0.5275813341140747),
('poor', 0.504431962966919),
('horrible', 0.4722219705581665),
('awful', 0.42389577627182007),
('worst', 0.40153956413269043),
('dirty', 0.3467090427875519),
('disgusting', 0.32588857412338257),
('horrendous', 0.3157917261123657),
('lousy', 0.30114778876304626),
('uncomfortable', 0.3005620837211609)]
---------------------------------
w2v_model.wv.most_similar(positive=["boston"])
>>>[('chicago', 0.519180417060852),
('seattle', 0.5126588940620422),
('dc', 0.4830571711063385),
('bostons', 0.4459514617919922),
('copley', 0.4455355107784271),
('city', 0.44090309739112854),
('newbury', 0.4349810481071472),
('fenway', 0.4237935543060303),
('philadelphia', 0.40892332792282104),
('denver', 0.39840811491012573)]
---------------------------------
w2v_model.wv.most_similar(positive=["valet"])
>>>[('parking', 0.7374086380004883),
('car', 0.6263512969017029),
('garage', 0.6224508285522461),
('retrieving', 0.5173929929733276),
('self', 0.5013973712921143),
('inandout', 0.4847780168056488),
('selfpark', 0.47603434324264526),
('fee', 0.47458043694496155),
('per', 0.4741314947605133),
('parked', 0.4707031846046448)]
*快速文本
FastText 算法的工作逻辑类似于 Word2Vec,但最大的不同是它在训练时也使用了 N 元词[4]。虽然这增加了模型的大小和处理时间,但它也赋予了模型预测单词的不同变化的能力。例如,假设单词“Windows”在训练数据集中,我们希望在训练结束后获得单词“Wndows”的向量。如果将 Word2Vec 模型用于这些操作,将会给出一个错误,即字典中不存在单词“Wndows ”,并且不会返回任何向量。但是,如果 FastText 模型用于此过程,则 vector 将返回,单词“Windows”将是最接近的单词之一。如上所述,训练中不仅包括单词本身,还包括 N-gram 变体(例如单词“Windows”-> Win、ind、ndo、dow、ows 的 3-gram 表达式)。尽管 FastText 模型目前在许多不同的领域中使用,但它经常是首选,尤其是在 OCR 工作中需要单词嵌入技术时。特别是与其他不能容忍最轻微 OCR 错误的技术相比,FastText 在获取不直接在其自身词汇表中的偶数单词的向量方面提供了很大的优势。正因如此,在可能出现用词错误的问题上,它比其他备选方案领先一步。
上述矢量化方法是当今最常用的技术。每一种都有不同的用途。在需要进行单词矢量化的研究中,应该首先确定问题,然后根据这个问题优先选择矢量化技术。事实上,每种技术都有不同的优势。
除此之外,还有语境表征,如 ELMO 和伯特[5]。这些问题将在下一篇文章中讨论。
开源代码库
所有代码都可以在https://github.com/ademakdogan/word2vec_generator找到。在这个项目中,word2vec 训练可以根据任何所需 csv 文件中确定的列自动完成。将作为操作结果创建的 word2vec 模型保存在“model”文件夹下。下面是用法示例。这个项目也可以在 docker 上运行。src/training.py 中的参数可以根据数据大小进行优化。
python3 src/w2vec.py -c </Users/.../data.csv> -t <target_column_name>
Github:【https://github.com/ademakdogan】T4
领英:https://www.linkedin.com/in/adem-akdo%C4%9Fan-948334177/
参照符号
[1]史蒂文斯,S. S. (1946)。《论测量的尺度》。科学,新系列,103.2684,677–680。
[2]会泽明子(2003)。“TF-IDF 测量的信息论观点”。信息处理与管理。 39 (1),45–65。doi:10.1016/s 0306–4573(02)00021–3
[3]托马斯·米科洛夫等人(2013 年)。“向量空间中单词表示的有效估计”。arXiv:1301.3781
[4]阿曼德·朱林,爱德华·格雷夫,皮奥特·博亚诺斯基,托马斯·米科洛夫,(2017)。“有效文本分类的锦囊妙计”,会议:计算语言学协会欧洲分会第 15 届会议论文集:第 2 卷。
[5]德夫林·雅各布,常明伟,李·肯顿,图塔诺娃·克里斯蒂娜,(2018)。“BERT:用于语言理解的深度双向转换器的预训练”
词、子词和基于字符的标记化:了解区别
实践教程
从事 NLP 项目的任何人都应该知道的区别
自然语言处理(NLP) 是人工智能(AI)的一个分支,它为机器(计算机)提供了像人类一样理解书面和口头人类语言的能力。NLP 几乎无处不在,帮助人们完成日常任务。😍这是一项如此普遍的技术,以至于我们经常认为这是理所当然的。一些例子是拼写检查,自动完成,垃圾邮件检测,Alexa 或谷歌助理。NLP 可以被认为是理所当然的,但人们永远不能忘记,机器是与数字而不是字母/单词/句子一起工作的。因此,为了处理互联网上的大量文本数据,我们需要对文本进行处理和清理,我们通常称之为自然语言处理中的文本预处理。
预处理是处理文本和建立模型来解决业务问题的第一步。预处理本身是一个多阶段的过程。在本文中,我们将只讨论标记化和标记化器。那么,我们开始吧。🏄🏼
注: 我们主要关注的是英语。
标记化
标记化是文本预处理中最重要的步骤之一。无论是使用传统的 NLP 技术还是使用先进的深度学习技术,都不能跳过这一步。🙅🏻
记号化简单来说就是将一个短语、句子、段落、一个或多个文本文档拆分成更小单元的过程。🔪这些更小的单元中的每一个都被称为令牌。现在,这些标记可以是任何东西——一个单词、一个子单词,甚至是一个字符。不同的算法在执行标记化时遵循不同的过程,但是下面给出的例子将让您对这三者之间的差异有一个基本的了解。
考虑下面的句子/原文。
“让我们学习记号化。”
一个基于单词的分词算法将把句子分解成单词。最常见的是基于空间的拆分。
【“让”、“我们”、“学”、“标记化”。】
基于子词的记号化算法将把句子分成子词。
【“让”、“我们”、“学”、“令牌”、“化”。】
基于字符的记号化算法将把句子分解成字符。
【" L “、” e “、” t “、” u “、” s “、” L “、” e “、” a “、” r “、” n “、” t “、” o “、” k “、” e “、” n “、” I “、” z “、” a “、” t “、” I “、” o “、” n “、” n "】]
标记实际上是 NLP 的构建块,所有的 NLP 模型都在标记级别处理原始文本。这些标记用于形成词汇表,词汇表是语料库(NLP 中的数据集)中的一组唯一标记。这个词汇表然后被转换成数字(id ),帮助我们建模。😎
我们在这里提到了三种不同的标记化技术。这些技术的工作方式各不相同,各有优缺点。让我们深入了解这些技术的细节。🏇🏻
基于单词的标记化
这是最常用的标记化技术。它根据分隔符将一段文本分割成单词。最常用的分隔符是空格。您也可以使用多个分隔符来拆分文本,如空格和标点符号。根据您使用的分隔符,您将获得不同的单词级标记。
使用定制的正则表达式或 Python 的 split()方法可以很容易地完成基于单词的标记化。除此之外,Python 中还有大量的库——NLTK、spaCy、Keras、Gensim,它们可以帮助您轻松地执行标记化。
示例:
“我不喜欢咖啡是不是很奇怪?”
通过使用空格作为分隔符执行基于单词的标记化,我们得到:
【“是”、“它”、“怪异”、“我”、“不要”、“喜欢”、“咖啡?”]
如果我们看看“不要”和“咖啡?”,我们会注意到这些单词都附有标点符号。如果我们的语料库中有另一个类似这样的原始文本(句子)会怎么样— **“我爱咖啡。”这一次将会有一个令牌“咖啡”**哪个可以引导模型学习单词 coffee(“coffee?”还有“咖啡”)并将使单词(记号)的表示不是最佳的。🙆🏻
在执行标记化时,我们应该考虑标点符号的原因是,我们不希望我们的模型使用每种可能的标点符号(当然是可以跟在单词后面的标点符号)来学习同一单词的不同表示。如果我们允许我们的模型这样做,我们将会被模型将要学习的表示的数量(一种语言中每个单词×标点符号的数量)所震惊。😳所以,让我们考虑点状。
【“是”、“它”、“wierd”、“我”、“唐”、“'”、“t”、“喜欢”、“咖啡”、“呢?”]
这比我们先前的要好。然而,如果我们注意到,标记化为单词“don”做了三个标记——“don”,“t”。更好的“不做”标记应该是“做”和“不做”,这样,如果模型在未来看到单词“不做”,它会将其标记为“做”和“不做”,因为模型在过去已经学习了“不做”,它会在这里应用它的知识。这个问题听起来很复杂,但是可以通过一些规则来解决。🤓
您一定已经注意到,最新的 NLP 模型有自己的标记化器,因为每个模型使用不同的规则来执行标记化以及使用空格进行标记化。因此,不同 NLP 模型的标记器可以为同一文本创建不同的标记。空格和标点符号,以及基于规则的标记化都是基于单词的标记化的例子。
然后每个单词用一个 ID 表示,每个 ID 包含大量信息,因为句子中的一个单词通常包含大量上下文和语义信息。😲
这种技术听起来令人印象深刻,但是这种类型的标记化会产生大量的语料库,进而产生大量的词汇。😏最先进的型号 Transformer XL ,使用空格和标点符号化,词汇量为 267,735。这是巨大的!这种巨大的词汇规模导致用于输入和输出层的巨大嵌入矩阵,导致模型更重并且需要更多的计算资源。
这种标记化还为英语中几乎相似的单词(一个是单数,另一个是复数)如“boy”和“boys”赋予了不同的 id。我们实际上希望我们的模型知道像这样的单词是相似的。
为了解决这个巨大的词汇问题,我们可以限制可以添加到词汇中的单词数量。例如,我们可以在词汇表中只保存最常见的(基于单词在语料库中出现的频率)5000 个单词。然后,该模型将为这 5000 个常用单词创建 id,并将其余单词标记为 OOV(不在词汇表中)。但是这导致了信息的丢失,因为模型不会学习任何关于 OOV 单词的内容。这对于模型来说可能是一个很大的妥协,因为它将为所有未知单词学习相同的 OOV 表示。🙄
另一个缺点是关于拼写错误的单词。如果语料库中的“知识”被拼错为“knowldge ”,则模型会将 OOV 令牌分配给后面的单词。
因此,为了解决所有这些问题,研究人员提出了基于字符的标记化。
基于字符的标记化
基于字符的记号赋予器将原始文本分割成单独的字符。这种标记化背后的逻辑是,一种语言有许多不同的单词,但有固定数量的字符。这导致词汇量非常小。😍
例如,在英语中,我们使用 256 种不同的字符(字母、数字、特殊字符),而它的词汇表中有将近 170,000 个单词。因此,与基于单词的标记化相比,基于字符的标记化将使用更少的标记。
基于字符的标记化的一个主要优点是不会有或很少会有未知或 OOV 单词。因此,它可以使用每个字符的表示来创建未知单词(在训练期间看不到的单词)的表示。另一个优点是拼写错误的单词可以被正确拼写,而不是将它们标记为 OOV 令牌并丢失信息。
这种类型的标记化非常简单,可以大大减少内存和时间复杂度。那么,它是标记化的最好或最完美的算法吗?🤔答案是否定的(至少对于英语来说)!一个字符通常不像一个单词那样携带任何意义或信息。😕
注: 几种语言的每个字符中都承载着大量的信息。因此,基于字符的标记化在这里会很有用。
此外,在基于字符的标记化中,减小词汇表的大小需要权衡序列长度。每个单词被分成每个字符,因此,标记化的序列比原始文本长得多。例如,单词“知识”将有 9 个不同的标记。🙄
注: 研究者karpathy, 拉德福德等人 , Kalchbrenner 等人,*Lee 等人 已经演示了基于字符的标记化的使用,并得出了一些令人印象深刻的结果阅读这些论文了解更多!*
基于字符的标记化尽管存在一些问题,但已经解决了基于单词的标记化所面临的许多问题。让我们看看我们是否也能解决基于字符的符号化所面临的问题。
基于子词的标记化
另一种流行的标记化是基于子词的标记化,这是一种介于基于词和基于字符的标记化之间的解决方案。主要思想是解决基于单词的标记化(非常大的词汇量,大量的 OOV 标记,以及非常相似的单词的不同含义)和基于字符的标记化(非常长的序列和不太有意义的单个标记)所面临的问题。
基于子词的标记化算法使用以下原则。
- 不要将常用词拆分成更小的子词。
- 将生僻字拆分成更小的有意义的子字。
例如,不应该将“男孩”拆分,而应该将“男孩”拆分为“男孩”和“s”。这将有助于模型了解单词“boys”是由意思略有不同但词根相同的单词“boy”构成的。
在本文的开始,我们将单词“tokenization”分为“token”和“ization”,其中“token”是词根,“ization”是第二个子单词,作为词根的附加信息。子词分割将帮助模型了解与“token”具有相同词根的词,如“tokens”和“tokenizing”在含义上是相似的。它还将帮助模型了解“标记化”和“现代化”由不同的词根组成,但具有相同的后缀“化”,并在相同的句法情况下使用。另一个例子可以是“惊讶”这个词。基于子词的标记化将把它分成“惊奇”和“谎言”,因为这些独立的子词会更频繁地出现。
基于子词的记号化算法通常使用特殊符号来指示哪个词是记号的开始,哪个词是记号开始的结束。例如,“标记化”可以分为“token”和“# #化”,这表明“token”是单词的开始,而“# #化”是单词的结束。
不同的 NLP 模型使用不同的特殊符号来表示子词。“##”由 BERT 模型用于第二个子字。请注意,特殊符号也可以添加到单词的开头。
在英语语言中获得最先进结果的大多数模型使用某种子词标记化算法。几个常见的基于子词的分词算法是 BERT 和 DistilBERT 使用的词块,由 XLNet 和 ALBERT 使用的 Unigram ,以及由 GPT-2 和罗伯塔使用的 Bye-Pair 编码。😊
基于子词的标记化允许模型具有适当的词汇量,并且还能够学习有意义的上下文无关的表示。模型甚至有可能处理一个它以前从未见过的单词,因为分解可以导致已知的子单词。😇 🙌🏻
因此,我们看到了标记化方法如何不断发展,以适应 NLP 领域不断增长的需求,并提出更好的问题解决方案。
参考文献:
- https://huggingface.co/docs/tokenizers/python/latest/
- 文章还提供了相关研究论文的链接。
感谢大家阅读这篇文章。请分享您宝贵的反馈或建议。快乐阅读!📗 🖌
词汇向量和词义
词向量能捕捉意思吗?这取决于“意义”是什么意思。
你知道,意义是什么意思吗?罗斯蒂斯拉夫·萨维钦在 Unsplash 上的照片
这篇文章旨在回答一个简单的问题:单词向量真的能捕捉到单词的意思吗?
答案是……看情况。特别要看你认为一个意义是什么。但是我想得太多了。首先,让我们快速分解一下什么是词向量。然后才能谈最适合他们的意义理论。
什么是词向量?
单词向量试图用数学方法表示单词的意思。从本质上说,计算机会遍历一些文本(理想情况下是大量文本)并计算单词相邻出现的频率。这些频率用数字来表示。因此,如果单词“good”总是出现在单词“friend”的旁边,那么“good”的单词向量的一部分将反映这种联系。一个给定的单词会有大量这样的值,通常是数百个,有时是数千个。
一旦你有了一个单词的这组数字,你就可以比较不同单词的向量。例如,你可以比较“香蕉”、“猕猴桃”和“雨云”这些词的不同向量。由于向量是数学对象,您可以计算不同向量的数值相似性。由于“香蕉”和“猕猴桃”在很多相似的上下文中使用,而不是在“雨云”出现的上下文中使用,它们的向量会彼此更接近,而远离“雨云”的向量
从这里,人们可以进行直观的跳跃,说向量代表单词“香蕉”的意思毕竟,向量可以产生类比(例如,它可以告诉你香蕉对于猕猴桃来说就像土豆对于胡萝卜一样),它可以将香蕉归类到正确的类别中(例如,告诉你香蕉更接近于水果而不是肉或蔬菜),它甚至可以用来标记句子中“香蕉”的正确和错误用法。
但是一组数字真的能代表一个词的全部含义吗?换句话说…
一个词向量是一个意思吗?
在用于生成单词向量的实际代码层面上,我们一直称之为单词的只是一个字符串——一系列字符,除了我们人类之外,没有特定的表示内容。单词向量的目标是模拟我们人类赋予该字符串的代表性内容,以便它不仅可以成为我们的单词,也可以成为计算机的单词。换句话说,我们正试图教计算机‘香蕉’这个词的意思。
现在,有许多不同的意义理论。在这里,我想把重点放在一个具体的例子上,这个例子可以用来支持这样一个观点,即词的向量事实上的确代表了意义。这是哲学家路德维希·维特斯坦根提出的观点。维特根斯坦在他的书《哲学研究》中提出,我们应该把一个词的意义理解为仅仅是这个词如何被使用。我们可以称之为“意义即用途论题”
一个有自己的意义理论的人的脸。摘自维基共享资源。
这听起来可能有点奇怪,或者是显而易见的,所以让我们花点时间把它和其他观点进行比较,这样我们就能明白为什么维特根斯坦的概念是一个突破。在你读到这里之前,如果有人问你一个单词的意思是什么,你会怎么说?也许你会说它是字典里的任何东西。或者你会说这就是这个词的意思。“香蕉”是什么意思?这意味着字面上的物理对象——香蕉。
这些观点有一些严重的问题。毕竟,字典不创造意义,它只是写下单词已经表达的意思;早在第一本字典出现之前,人们就已经在用他们的词来表达意思了。如果这个词的意思就是它所代表的事物,那么一些随机的声音又是如何代表任何事物的呢?为什么“香蕉”指的是实物香蕉,而“蛋糕”指的是蛋糕?这些声音并没有什么特别之处,可以让它们指向不同的方向。
维特根斯坦的意义即使用的观点回避了这两个问题。意义不是声音和物体之间的某种虚无缥缈的联系,也不是韦氏词典流传下来的某种宏大计划;只是社会习俗和日常使用的问题。如果你问我要一杯咖啡,说“bingle bangle bongle”,然后我给你买了一杯咖啡,那么“bingle bangle bongle”的意思是“请给我一杯咖啡。”这就是全部了。
意义即使用和词向量
也许你现在已经有了一些线索,为什么这个意义理论比其他理论对单词向量更友好。如果意义是声音和物体之间的某种神奇联系,那么向量这个词就没有希望捕捉到它。不管你扫描多少条短信,你都找不到那个链接。
然而,单词向量可以准确地告诉你一个单词是如何使用的。事实上,如果计算机已经阅读了所有的文本,听到了你在生活中听到的所有单词,它可能会比你更精确、更清楚地描述这些单词的用法。如果一个单词的意思只是它在句子中的用法,而单词向量可以捕捉到这一点,那么单词向量实际上捕捉到了单词的意思。
当然,维特根斯坦的理论有其自身的问题。特别要注意的是,在他看来,词语只是导航我们这个受社会制约的世界的工具。这意味着它们不一定代表自身之外的任何东西,这可能看起来是反直觉的。但也许一个词向量是否抓住了一个词的意思并不重要;你可能会说,重要的是我们能否让计算机正确使用这个词,或者做出准确的预测。
但是如果你对词向量(或者一般的 NLP)的工作是否真的不仅仅是产生一些功能产品感兴趣,那么花点时间考虑一下什么是意义是值得的。谢谢你花时间和我在一起。
词向量直觉和共现矩阵
照片由@拉斐尔摄影—Unsplash.com 拍摄
如果你对自然语言处理做过一些研究,你可能会偶然发现单词向量的概念。虽然这个概念似乎很直观,但如果你没有数据科学或机器学习方面的知识,可能会有点难以理解。
在这篇文章中,我们将检查构建单词向量背后的一些直觉,并了解当我们谈到几个 NLP 应用程序时,为什么它们是相关的。我们将使用一个简单的共现概念来解释为什么一键向量不是将单词表示为数字的最佳方法。
让我们先用两个词来举例——车和吉普。对于人类来说,这两个词可能有某种程度的相似性,因为我们知道吉普车和汽车有相似的特征——它们都是有发动机的四轮车辆,用于运输或休闲目的。我们可以用这样的短语:
- 吉普车是汽车的一种。
- 我的车是一辆吉普。
- 吉普车类似于其他汽车
- 吉普类似于车,但不是车。
一辆吉普车与其他汽车有一些共同的特征(照片由@ neon brand—Unsplash.com 拍摄)
一个有意思的事情是车和吉普都可能在一句话里做替身:
- 我正骑着我的车去海滩;
- 我正骑着我的吉普车去海滩;
这两个词都被描绘成主体用来走向海滩的物体。这两个词也可以属于车辆的范畴: 通常有轮子和发动机的机器,用于运送人或货物,尤指在陆地上。
我们人类有能力以一种非常简单的方式理解这两个词之间的相似性,因为当我们阅读这两个词时,我们可以在脑海中创建一辆汽车或一辆吉普车的图像。但是计算机怎么能像你我一样理解这两个词之间的这些概念呢?
让我们先做一些基本的实验——在排序单词时,我们能想到的第一个方法是按字母顺序排序。我们现在把字母表看成是某种固有的顺序,所以,让我们天真地假设这可能会把相似的单词组合在一起。
- 汽车以 c 启动
- 吉普始于 j
我们可以想出一大堆介于字母表中的车和吉普之间的单词——比如爸爸、欧洲、决赛、痒。所有这些单词都有完全不同的意思,所以我们可以排除这个想法。
如果我们比较单词的字母呢?有些词表达了相似的意思,有相似的字母,例如:
- 创建和创建
- 书和书架
不幸的是,英语并没有那么简单。吉普和轿车,为例,不共用任何字母。而鹿和深呢?他们共享四个字母中的三个,他们的意思不可能不同。
似乎使用单词中包含的字母是一个微不足道的练习。我们还可以根据上下文,在单词和相似单词之间建立一个巨大的查找表。但是想象一下,为了跟踪特定语言的自然进化而更新那个表会有多混乱。
让我们尝试另一种方法——用更“数学”的方式来看待我们的单词。
一个热点向量方法
直接看我们的信看起来像是一个不会给我们带来任何结果的练习。很自然,随之而来的是某种想法,将我们的文字表示为数字。
将一个单词表示为单个数字会导致我们得到与按字母顺序比较单词相同的结果——我们必须附加一个单词的单个数字。这个数字旁边的单词在某种程度上会被认为是相似的单词,我们仍然会有正确排序的问题。
所以,一个可行的方法是,我们可以用多维数字的方式来表示我们的单词。我们可以使用的一种数据结构是数组(或向量)——例如:
[1, 0, 0, 0, 0]
上面的数组是一个独热向量的例子——一个向量在单个值中包含 1,在其他值中包含 0。这些热点向量可以用来表示我们称之为词汇表的一组单词中的特定单词。
回到我们的例子,让我们想象我们的词汇将由以下单词组成:
- 自行车
- 步行
- 吉普车
- 汽车
- 卡车
- 自行车
看着这些单词,我们可以建立几个心理“集群”:
- 所有这些单词都与一个人从一个地方移动到另一个地方的方法有关。
- 看起来最“关”的词是走这个词
- 人们可以把两轮车(自行车和三轮车)与四轮或四轮以上的车(卡车、轿车和吉普车)区分开来。
我们能否重建数组,以某种方式传达这些单词的细节?
让我们先来整理一下我们的词汇:
bike, bicycle, car, jeep, truck, walk
现在,用我们的单词代替 0,将上面的元素转换成数组格式:
[0, 0, 0, 0, 0, 0]
我们现在可以用一个热点向量来表示我们的单词,例如:
- 自行车用[1,0,0,0,0,0]来表示
- 行走由[0,0,0,0,0,1]表示
这些词向量的主要问题是它们是**正交的。**我在这里就不跟你讲数学了,但是,这意味着这些向量的点积大约为 0——如果我们以二维方式可视化汽车和吉普车的向量:
一个热点向量的二维表示——由平面图标表示的图标
这些向量之间的角度约为 90 度。这将引起一些麻烦,因为代表汽车和吉普车的向量之间的相似性将与汽车和步行或汽车和自行车之间的相似性完全相同。
我们如何以向量反映相似性的方式来表示这些向量?
共现表示
同现表示帮助你建立单词的上下文。没有什么比语言学家约翰·弗斯的话更能阐明这个概念了:
“从一个人交的朋友就可以知道他说的一句话”
事实上,一个词是由其上下文反映出来的。让我们想象一些简单的句子来反映这一点:
- 我正开车去海滩。
- 我开着我的吉普车去海滩。
- 我的汽车是一辆吉普车。
- 我的吉普车是一辆小汽车。
- 我昨天吃了一根香蕉。
- 我昨天吃了一个桃子。
正如你可能意识到的,意思完全不同的单词在同一上下文中同时出现的可能性极小。让我们称之为单词的上下文,即围绕在特定单词两边的两个单词——例如在句子*中,我昨天吃了一个桃子,*桃子这个单词被以下单词所包围:
- 昨天吃了一个
在这些单词中,你找到不同于食物的东西的可能性很小。你不会发现下面这句话的可能性:
我昨天吃了一辆吉普车
或者
我昨天吃了一辆自行车
这就是共现表征背后的主要原理——彼此相似的单词往往会一起共现。最常见的共现表示是单个单词的单词表示。让我们检查一下!
共生矩阵
为了构建共现矩阵,我们必须从特定语料库中的完整词汇开始,就像我们在 one-hot vector 部分中所做的那样——让我们看看上面示例中这组句子的词汇,考虑单个 gram:
a, ate, banana, beach, car, in, is, I’m, jeep, my, riding, to, the, yesterday
在上面所有的句子中,我们的词汇由一组不同的单词组成。与 one-hot vectors 类似,我们的向量将由大小为 *k、*的向量组成,其中 k 是不同单词的数量——让我为单词 car 初始化一个示例向量:
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
在我们的句子中,单词 car 出现在三个句子中:
我正骑着我的车去海滩**。**
我的车是一辆吉普。
我的吉普是一辆车。
**我已经标出了句子中所有与单词 car 同时出现的单词。**如果我们将 1 加到每个共现的元素上,返回的单词向量会是多少?—更新单词 car 的向量:
a, ate, banana, beach, car, in, is, I’m, jeep, my, riding, to, the, [2, 0, 0, 0, 0, 1, 2, 0, 0, 2, 0, 1, 1, yesterday
0]
这个共现向量现在将表示单词 car。
如果我们为单词 jeep 建立相同的向量会怎么样?查看我们的示例:
我的车是一辆吉普车。
我正骑着我的吉普车去海滩。
我的车是一辆吉普。
产生的共现向量将是:
a, ate, banana, beach, car, in, is, I’m, jeep, my, riding, to, the, [2, 0, 0, 0, 0, 1, 2, 0, 0, 2, 0, 1, 1, yesterday
0]
如果你注意到了,这个向量和我们对单词 car 的向量完全一样。发生这种情况是因为,在我们的句子中,这些单词往往与相同的单词同时出现。这似乎是一个有前途的方法!
如果我们检查单词 banana 的向量,共现向量完全不同:
我昨天吃了一根香蕉**。**
a, ate, banana, beach, car, in, is, I’m, jeep, my, riding, to, the, [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, yesterday
1]
我们为单词 banana 生成的向量与为 Jeep 和 Car 生成的向量完全不同。
**我几乎能听到你在想——“等一下……这些例子似乎都是为了符合解释”而吹毛求疵。
这倒是真的!为了检查这在更大的文本中是否有意义,让我们在维基百科的一篇文章上做一个快速实现。
之后我们将学习如何量化向量之间的关系(提示:相似性度量!)
大文章单词共现
让我们直接进入 Python 实现,在这里我们将提取维基百科美利坚合众国文章中所有单词的共现向量— 首先,让我们检索数据并将其标记化(不要担心代码,我们最后会有一个要点!):
def retrieve_page(page_name: str) -> list:
'''
Retrieves page data from wikipedia
and stores words in lower case format in
a list - tokenized format.
'''
usa_article = wikipedia.page(page_name)
# Strip puncuation from page
usa_article = (
usa_article.content.translate(str.maketrans('', '', string.punctuation))
)
# Lower text case
usa_article = usa_article.lower()
# Tokenize using NLTK word tokenizer
usa_article_token = word_tokenize(usa_article)
return usa_article_tokenusa_article_token = retrieve_page('United States of America')
我们的 usa_article_token 以标记化的格式包含了整个维基百科文章——让我们看看文章的第一段:
美国维基百科文章的第一段
从文章中提取所有句子后,我们能够理解每个单词的上下文——在上面的文本中,单词平方(第四行)具有以下上下文,去掉标点符号: 3.8,百万,英里,9.8
鉴于此,我们现在可以为文本中的每个单词构建共现向量,让我们编码吧!
从构建我们的词汇开始,词汇将由我们的共现向量的大小组成:
def build_vocabulary(page:list) -> list:
'''
Builds vocabulary with all the words
present in the list page.
'''
vocab = list(set(page))
vocab.sort()
vocab_dict = {}
for index, word in enumerate(vocab):
vocab_dict[word] = index
return vocab_dictvocab_dict = build_vocabulary(usa_article_token)
警告:由于维基百科文章的更新,当您运行这段代码时,词汇表的大小可能会有所不同。
我们的词汇表中有大约 3.640 个单词——我们的 vocab_dict 对象包含单词和索引之间的映射——这个索引将使我们能够更快地填充共现向量。
将我们的同现矩阵初始化为 0-我将使用熊猫数据框来简化可视化:
co_ocurrence_vectors = pd.DataFrame(
np.zeros([len(vocab_dict), len(vocab_dict)]),
index = vocab_dict.keys(),
columns = vocab_dict.keys()
)
这是我们初始化的共现矩阵的大致外观——行由单词 i 的索引组成,列由单词 j 与单词 i. 共现 n 次组成
现在,根据上下文,每当我们在我们在行中看到的单词的上下文中看到特定的单词时,我将添加 1 。例如,在本文中,单词表示应该与单词 United 以及可能的 America 同时出现。
使用 Python 更新矩阵:
def build_context(
page:str,
co_ocurrence_vectors: pd.DataFrame
) -> pd.DataFrame:
'''
Updates co-ocurrence vectors based on
text read from the page.
'''
for index, element in enumerate(page):
# Build start and finish of context
start = 0 if index-2 < 0 else index-2
finish = len(page) if index+2 > len(page) else index+3 # Retrieve Context for word
context = page[start:index]+page[index+1:finish]
for word in context:
# Update Co-Occurrence Matrix
co_ocurrence_vectors.loc[element, word] = (
co_ocurrence_vectors.loc[element, word]+1
)
return co_ocurrence_vectorsco_ocurrence_vectors = build_context(usa_article_token, co_ocurrence_vectors)
现在,我们可以从我们的共现矩阵中提取与状态共现更多的词,例如前 10 个词:
co_ocurrence_vectors.loc['states'].sort_values(ascending=False).head(10)
与州共现的前 10 个单词
是与文本共现次数最多的第一个词——在我们的文本中出现了 189 次(在两个邻居的窗口中),其次是 united 和 is。另一方面,美国是第九个——如果你注意到这篇文章,美国在整篇文章中被用作美利坚合众国的别名,所以,这是有道理的。
爽!所以我们现在有了 3.640 个单词的多维向量。
我们如何衡量它们的相似性?
余弦相似性
测量两个向量之间相似性的最常见方法之一是使用余弦相似性——测量任意向量A 和向量 B 之间的相似性:**
余弦相似性公式(来自维基百科)
在我们的例子中,我们将有 3.640 个元素
余弦相似性为我们提供了一种相似性度量,其范围在以下值之间:
- 1 如果向量真的相似;
- 如果向量没有任何关系(正交向量!);
- 如果向量相反,则为-1;
按逻辑,相似的词会有更高的余弦相似度。如果向量完全相同,则余弦相似度将为 1。让我们看看维基百科文章中的一些例子——搜索与单词 States — 具有高余弦相似度的单词。首先,我们需要使用 scikit-learn 实现 : 在文章中的每个单词之间创建余弦相似度
**similarity_words = pd.DataFrame(
cosine_similarity(co_ocurrence_vectors),
columns = vocab_dict.keys(),
index = vocab_dict.keys()
)**
就相似性而言,检查顶部单词与单词的状态:**
有些词看起来确实有道理——比如国家、王国和宪法。其他词有点偏— 离开,的,在— 但是,即便如此,这些结果还是很有意思。**
让我们检查另一个词,如中国:**
同样,有些词似乎很有意义,比如菲律宾、加拿大和墨西哥等其他国家——但也有一些词看起来很“奇怪”,因为它们与“中国”这个词很接近。
当然,我们只使用了维基百科中的一篇文章,所以,我们并不期待完美。如今,共现矩阵是很好的第一手资料,可以很好地掌握单词向量背后的必要性和直觉。
使用共现向量有一些缺点,即:
- 同现向量变得很大,很快。你的词汇量越大,你的向量就越大。
- 大多数共生矩阵包含零和无信息。您可以通过使用稀疏格式来解决空间问题,但即使如此,一些罕见的单词可能具有非常糟糕的向量表示。
- 共现向量无法把握对立词的概念。
其他技术已经被用于表示单词向量,例如包含压缩版本的 Word2Vec 和可能的隐藏的共现表示,后者概括得更好一些。我们将把它留给下一篇文章!**
以下是这篇文章代码的要点:
感谢你花时间阅读这篇文章!我希望你喜欢它,并随时添加我到 LinkedIn 或者给我发消息!
这个例子摘自我的 NLP 课程,该课程面向 Udemy 平台上的绝对初学者——该课程非常适合初学者和希望学习自然语言处理基础知识的数据科学家。该课程还包含 50 多个编码练习,使您能够在学习新概念的同时进行练习。******
****https://ivopbernardo.medium.com/membership ****
Word2Vec 解释道
解释 Word2Vec 的直观性&用 Python 实现它
这张照片是由 Unsplash 的 Raphael Schaller 拍摄的
目录
- 介绍
- 什么是单词嵌入?
- Word2Vec 架构
- CBOW(连续单词包)模型
-连续跳格模型
- CBOW(连续单词包)模型
- 实现
-数据
-需求
-导入数据
-数据预处理
-嵌入
-嵌入的 PCA - 结束语
- 资源
介绍
Word2Vec 是 NLP 领域的最新突破。 Tomas Mikolov 捷克计算机科学家,目前是 CIIRC ( 捷克信息、机器人和控制论研究所)的研究员,是 word2vec 研究和实现的主要贡献者之一。单词嵌入是解决自然语言处理中许多问题不可缺少的一部分。它们描述了人类如何理解机器的语言。你可以把它们想象成文本的矢量表示。Word2Vec 是一种常见的生成单词嵌入的方法,具有多种应用,如文本相似性、推荐系统、情感分析等。
什么是单词嵌入?
在进入 word2vec 之前,让我们先了解一下什么是单词嵌入。了解这一点很重要,因为 word2vec 的整体结果和输出将是与通过算法传递的每个唯一单词相关联的嵌入。
单词嵌入是一种将单个单词转换成单词的数字表示(向量)的技术。其中每个单词被映射到一个向量,然后这个向量以类似于神经网络的方式被学习。向量试图捕捉该单词相对于整个文本的各种特征。这些特征可以包括单词的语义关系、定义、上下文等。有了这些数字表示,你可以做很多事情,比如识别单词之间的相似或相异。
显然,这些是机器学习各个方面的输入。机器不能处理原始形式的文本,因此将文本转换成嵌入将允许用户将嵌入馈送到经典的机器学习模型。最简单的嵌入是文本数据的一个热编码,其中每个向量将被映射到一个类别。
For example: have = [1, 0, 0, 0, 0, 0, ... 0]
a = [0, 1, 0, 0, 0, 0, ... 0]
good = [0, 0, 1, 0, 0, 0, ... 0]
day = [0, 0, 0, 1, 0, 0, ... 0] ...
然而,像这样的简单嵌入有多种限制,因为它们不能捕获单词的特征,并且它们可能非常大,这取决于语料库的大小。
Word2Vec 架构
Word2Vec 的有效性来自于它能够将相似单词的向量组合在一起。给定一个足够大的数据集,Word2Vec 可以根据单词在文本中的出现次数对单词的含义做出强有力的估计。这些估计产生了与语料库中其他单词的单词关联。例如,像“国王”和“王后”这样的词彼此非常相似。当对单词嵌入进行代数运算时,你可以找到单词相似性的近似。例如,“国王”的 2 维嵌入向量-“男人”的 2 维嵌入向量+“女人”的 2 维嵌入向量产生了与“皇后”的嵌入向量非常接近的向量。注意,下面的值是任意选择的。
King - Man + Woman = Queen
[5,3] - [2,1] + [3, 2] = [6,4]
你可以看到 King 和 Queen 这两个词的位置很接近。(图片由作者提供)
word2vec 的成功主要得益于两种架构。跳跃图和 CBOW 架构。
连续单词袋
这种架构非常类似于前馈神经网络。这种模型架构本质上试图从上下文单词列表中预测目标单词。这个模型背后的直觉非常简单:给定一个短语"Have a great day"
,我们将选择我们的目标词为“a”,我们的上下文词为[“have”、“great”、“day”]。这个模型要做的是利用上下文单词的分布式表示来尝试和预测目标单词。
CBOW 架构。图片取自向量空间中单词表示的有效估计
连续跳格模型
skip-gram 模型是一个简单的神经网络,具有一个经过训练的隐藏层,以便在输入单词出现时预测给定单词出现的概率。直觉上,你可以想象跳格模型是 CBOW 模型的对立面。在这种架构中,它将当前单词作为输入,并尝试准确预测当前单词前后的单词。该模型本质上试图学习和预测指定输入单词周围的上下文单词。基于评估该模型准确性的实验,发现在给定大范围的词向量的情况下,预测质量提高了,然而这也增加了计算复杂度。该过程可以直观地描述如下。
为 skip-gram 模型生成训练数据的示例。窗口大小为 3。图片由作者提供
如上所述,给定一些文本语料库,在一些滚动窗口上选择目标单词。训练数据由该目标单词和窗口中所有其他单词的成对组合组成。这是神经网络的最终训练数据。一旦模型被训练,我们基本上可以产生一个单词成为给定目标的上下文单词的概率。下图显示了 skip-gram 模型的神经网络体系结构。
跳格模型架构(图片由作者提供)
语料库可以表示为大小为 N 的向量,其中 N 中的每个元素对应于语料库中的一个单词。在训练过程中,我们有一对目标和上下文单词,输入数组中除目标单词外的所有元素都为 0。目标字将等于 1。隐藏层将学习每个单词的嵌入表示,产生 d 维嵌入空间。输出层是具有 softmax 激活功能的密集层。输出层基本上会产生一个与输入大小相同的向量,向量中的每个元素都由一个概率组成。这个概率指示了目标单词和语料库中的关联单词之间的相似性。
对于这两个模型的更详细的概述,我强烈推荐阅读概述这些结果的原始论文这里。
履行
我将展示如何使用 word2vec 来生成单词嵌入,并通过 PCA 使用这些嵌入来查找相似的单词和嵌入的可视化。
数据
出于本教程的目的,我们将使用莎士比亚数据集。你可以在这里找到我在本教程中使用的文件,它包括了莎士比亚为他的剧本写的所有台词。
要求
nltk==3.6.1
node2vec==0.4.3
pandas==1.2.4
matplotlib==3.3.4
gensim==4.0.1
scikit-learn=0.24.1
**注意:**因为我们正在使用 NLTK,你可能需要下载下面的语料库来完成本教程的剩余部分。这可以通过以下命令轻松完成:
import nltk
nltk.download('stopwords')
nltk.download('punkt')
输入数据
**注意:**将**PATH**
变量更改为您正在处理的数据的路径。
预处理数据
停用词过滤注释
- 请注意,从这些行中删除的停用词是现代词汇。应用程序和数据对于清理单词所需的预处理策略的类型非常重要。
- 在我们的场景中,单词“you”或“yourself”将出现在停用词中,并从行中删除,但是由于这是莎士比亚文本数据,因此不会使用这些类型的单词。相反,“你”或“你自己”可能是有用的删除。保持对这些类型的微小变化的热情,因为它们对好模型和差模型的性能产生了巨大的差异。
- 出于这个例子的目的,在识别不同世纪的停用词时,我不会涉及太多细节,但是请注意,您应该这样做。
把…嵌入
莎士比亚资料中与 thou 最相似的单词(图片由作者提供)
嵌入的主成分分析
彼此相似的单词将被放置在彼此更靠近的地方。图片由作者提供
Tensorflow 对 word2vec 模型做了非常漂亮、直观和用户友好的表示。我强烈建议您探索它,因为它允许您与 word2vec 的结果进行交互。链接在下面。
https://projector.tensorflow.org/
结束语
词嵌入是解决自然语言处理中许多问题的重要组成部分,它描述了人类如何理解机器语言。给定一个大的文本语料库,word2vec 产生一个与语料库中的每个单词相关联的嵌入向量。这些嵌入的结构使得具有相似特征的单词彼此非常接近。CBOW(连续单词包)和 skip-gram 模型是与 word2vec 相关的两个主要架构。给定一个输入单词,skip-gram 将尝试预测输入上下文中的单词,而 CBOW 模型将采用各种单词并尝试预测缺失的单词。
我还写过 node2vec,它使用 word2vec 生成给定网络的节点嵌入。你可以在这里读到它。
资源
- https://arxiv.org/pdf/1301.3781.pdf
- https://www . kdnugges . com/2019/02/word-embeddings-NLP-applications . html
- 【https://wiki.pathmind.com/word2vec
- https://projector.tensorflow.org/
如果您喜欢阅读这篇文章,请考虑关注我的后续文章,了解其他数据科学材料以及解决数据科学不同领域相关问题的材料(如 word2vec)。这里有一些我写的其他文章,我想你可能会喜欢。
[## 贝叶斯 A/B 测试解释
towardsdatascience.com](/bayesian-a-b-testing-explained-344a6df88c1a)