TowardsDataScience-博客中文翻译-2022-三十七-

TowardsDataScience 博客中文翻译 2022(三十七)

原文:TowardsDataScience

协议:CC BY-NC-SA 4.0

班长!使用 New Relic 轻松监控 MLOps 模型

原文:https://towardsdatascience.com/monitor-easy-mlops-model-monitoring-with-new-relic-ef2a9b611bd1

监控 ML 模型的简单方法

“可观察性仪表板”,Wallusy, Pixabay

我们现在都知道数据和模型监控极其重要。即使您的生产就绪模型没有发生变化,数据分布也可能会发生变化,并可能会影响您的输出,从而直接影响您的用户,一旦发生这种情况,您可能会遇到面向客户的生产问题。

本文旨在成为一个简短的教程,帮助您开始 ML 模型监控,在本指南的结尾,您将知道如何监控任何 ML 模型。

仅供参考,如果您想了解更多关于监控必要性的原因,请阅读下面的文章。

监控和可观察性

开发人员和数据科学生产系统之间的监控和可观察性差距是真实的,我们需要谈论它。有许多平台设计用于监控工程系统,也有许多其他平台设计用于监控 ML 系统,但没有一个具有高度先进功能的系统能够开箱即用地支持这两种系统,至少目前还没有。

NR 开始向这个方向发展,提供 ML 模型监控,允许数据科学家将他们的模型度量和输出发送到开发人员使用的同一个平台。您可以获得自动和定制的仪表板、警报和相关的系统事件。如果您想要更高级的功能和见解来减少平均解析时间,您还可以与 ML 模型监控公司集成,如 SuperwiseAporiaCometTrueraDagsHubMona Labs 、&、 Algorithmia (DataRobot)。

ML 模型监控集成,如 NR I/O 中所示,图片由作者提供。

ML 监控入门

通过使用 GitHub 上提供的开源“ML-performance-monitoring”python 库,发送 ML 数据变得很容易。

https://github.com/newrelic-experimental/ml-performance-monitoring

这组工具将允许您直接从代码中监控各种 ML 输出。首先,你需要注册一个免费 NR 账户,每月最多 100GB。接下来,您将能够直接从 python 脚本或 Jupyter 笔记本进行监控,利用预先构建的仪表板,创建自定义仪表板,以及创建警报。这可以针对模型度量、数据漂移、概念漂移、概率、标签等等来完成。

安装

首先,你需要安装 GitHub 包。

$ pip install git+https://github.com/newrelic-experimental/ml-performance-monitoring.git

获取插入键

现在去你的 insert 键,这是一个允许你发送数据到平台的键,把它放在你的NEW_RELIC_INSERT_KEY环境变量中。这个键可以在下面的 API 键 UI 中找到。

钥匙管理 UI,文档,新遗物。

准备的

我们从导入、加载波士顿数据集、分割和拟合模型开始。这些步骤非常基本,不需要进一步解释。

from ml_performance_monitoring.monitor import wrap_model
import numpy as np
from sklearn.metrics import mean_squared_error
from sklearn.datasets import load_boston
from sklearn.model_selection import train_test_split
import xgboost as xgbboston_dataset = load_boston()X, y = (
    boston_dataset["data"],
    boston_dataset["target"],)X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=123
)print(X_train[:5], y_train[:5])xg_reg = xgb.XGBRegressor(
    objective="reg:linear",
    colsample_bytree=0.3,
    learning_rate=0.1,
    max_depth=5,
    alpha=10,
    n_estimators=10,
)xg_reg.fit(X_train, y_train)

监视

然后我们想发送一些基于模型的数据。我们用一组元数据键值创建一个字典,并准备需要发送的数据。使用 wrap_model()包装您的模型或管道,因为您仍然可以正常使用它,例如在 fit()、predict()等方面。这个包装器会将您的推理数据和 data_metrics 自动发送给 NR。

metadata = {"environment": "aws", "dataset": "Boston housing prices", "version": "1.0"}
features_columns, labels_columns = (
    list(boston_dataset["feature_names"]),
    ["target"],
)ml_performence_monitor_model = wrap_model(
    insert_key=None,  # set the environment variable NEW_RELIC_INSERT_KEY or send your insert key here
    model=xg_reg,
    model_name="XGBoost Regression on Boston Dataset",
    metadata=metadata,
    send_data_metrics=True,
    features_columns=features_columns,
    labels_columns="categorical",
)y_pred = ml_performence_monitor_model.predict(
    X=X_test,
)rmse = round(np.sqrt(mean_squared_error(y_test, y_pred)), 3)
print(f"RMSE: {rmse}")
metrics = {"RMSE": rmse,}# Send your model metrics as a dictionary.
ml_performence_monitor_model.record_metrics(metrics=metrics)

查询您的数据

现在,您将希望看到您的数据,并在平台上验证它,即确保它在一个表中。

我们可以通过以下查询查询来做到这一点:
Select * From InferenceData Where entity.name='XGBoost Regression on Boston Dataset' Since 1 hour Ago Limit Max

或者转到 ML 模型监控视图

ML 模型监控,图片由作者提供。

选择“XGBoost Regression on Boston Dataset”模型,这将显示嵌入式模型视图。另一方面,如果它没有显示任何模型,可能是出了问题,您需要进行调试。

实体视图,作者图片。

一旦您选择了您的模型,您将会看到嵌入式模型视图的内部。如下图所示,报告了大量有价值的信息,如预测量、漂移、分布和特征。

嵌入式模型视图,作者图片。

如果您单击底部表格中的某一行,将会出现一个新视图并显示附加信息。

深入了解更多信息,图片由作者提供。

从 Jupyter 笔记本电脑进行监控

如果你正在寻找一个端到端的例子。有一款笔记本可以完成以上所有功能,并深入展示如何直接从笔记本内部进行监控,例如,如下图所示,我们可以看到如何记录 F1、精度、召回、漂移等性能指标。

演示笔记本,作者图片。

摘要

我希望这篇文章能够帮助您完成监控 ML 模型的第一步。如果这种方法对你不起作用,记住市场上有无数其他不可思议的解决方案,其中一些是自助的,它允许你进行快速 POCing。如果你有兴趣了解更多,我整理了以下市场评论。

https://www.stateofmlops.com/

Ori Cohen 博士拥有计算机科学博士学位,主要研究机器学习。他是 ML & DL 纲要StateOfMLOps.com的作者,并且对 AIOps & MLOps 领域很感兴趣。现在,他是 Justt.ai 数据科学的高级数据总监。

用谷歌地球引擎监测植被

原文:https://towardsdatascience.com/monitor-vegetation-with-google-earth-engine-909a2ad51a48

为你喜欢的森林创建 NDVI + EVI 时间序列

Geran de Klerk 在 Unsplash 上拍摄的照片

2022 年的热浪一直在炙烤着地球。南美、美国、欧洲、印度、中国和日本都遭受了高温袭击。在这个迅速变暖的地球上,植被是我们的天然盟友。但它也承受着气候变化的冲击。2022 年,野火肆虐全球。大片森林被烧毁。这一系列事件敲响了关于气候变化的最新警钟。我们需要高度关注植物的健康。

为了保护植物,我们首先需要更多地了解它们。卫星可以提供帮助。有了卫星图像,我们可以监测面积变化和植物的健康状况。此外,我们可以先发制人地计算野火风险。当野火发生时,我们可以通过天空中的这只眼睛迅速探测到它们,并在事后评估损失。

有了谷歌地球引擎,我们可以在非商业和研究用途上免费使用它们。此外,我们既不需要安装软件,也不需要在本地硬盘上下载映像。在我之前的文章 用谷歌地球引擎 监控土地利用变化中,我已经展示了如何使用谷歌地球引擎来监控给定位置的土地利用土地覆盖(LULC)。在随后的一篇文章 用谷歌地球引擎 监控地区气候中,我展示了如何监控一个给定地区的长期气候(温度和降水)和海拔。在本文中,我将向您展示如何基于相同的代码构建两个新的植被监测应用程序(图 1)。我们将计算 2017 年至 2022 年间任何土地面积的归一化差异植被指数 (NDVI)和增强植被指数 (EVI)。

图一。基于谷歌地球引擎的 NDVI+EVI 应用。图片作者。

这个项目的代码存放在我的 GitHub 库中。

https://github.com/dgg32/ndvi

我把它们都放进了网络应用程序。

1.NDVI 和 EVI

从你的飞机窗口,你会说一个地区被植物覆盖时,它是绿色的。但奇怪的是,卫星图像并不是这样工作的。植物吸收红光(620-750 纳米)进行光合作用。但是它们反射近红外光(NIR,700–1100nm)以避免组织损伤,因为这些波长的光子能量太大。因此,被植物覆盖的区域在红色通道中是暗的,但在近红外通道中是亮的。相比之下,雪和云的情况正好相反。NDVI 利用了这一现象。其定义如下:

NIR 和 Red 分别代表近红外和红色表面反射率。NDVI 的值介于-1 和 1 之间。

顾名思义,EVI 是 NDVI 的加强版。根据维基百科,

它增强了植被信号,提高了高生物量区域的灵敏度,并通过解耦冠层背景信号和减少大气影响改善了植被监测。鉴于归一化差异植被指数 (NDVI)对叶绿素敏感,EVI 对冠层结构变化更敏感,包括叶面积指数 (LAI)、冠层类型、植物外貌和冠层结构。这两个植被指数在全球植被研究中相互补充,并改善了植被变化的检测和冠层生物物理参数的提取。在有雪的情况下,NDVI 减少,而 EVI 增加。

EVI 可以计算如下。

如公式所示,EVI 不仅考虑了近红外和红光,还考虑了蓝光反射率。换句话说,如果你有足够的信息来计算 EVI,你就可以计算 NDVI。四个系数是 G(增益系数)= 2.5,C1 = 6,C2 = 7.5,L=1。对于健康的植被,EVI 通常在 0.2 和 0.8 之间波动,但是它可以具有超出[-1,1]范围的值。然而,在我的应用程序的缩略图中,我将其值裁剪为[-1,1]。

2.数据集

在这个项目中,我实现了两个应用程序。它们基于[COPERNICUS/S2_SR_HARMONIZED](https://developers.google.com/earth-engine/datasets/catalog/COPERNICUS_S2_SR_HARMONIZED#description)(Creative Commons CC BY-SA 3.0 IGO license)和[MODIS/061/MOD13A1](https://developers.google.com/earth-engine/datasets/catalog/MODIS_061_MOD13A1#terms-of-use) ( MODIS 数据和通过 LP DAAC 获得的产品对后续使用、销售或再分发没有限制。)数据集。COPERNICUS/S2_SR_HARMONIZED欧盟/欧空局/哥白尼提供。它包含了 EVI 和 NDVI 所需的所有三个波段。此外,它还包含一个名为场景分类图(SCL)的波段。数据集中的每个像素都被归入 11 个类别之一(植被、裸土等)。这些分类结果存储在 SCL 中。这个 app 的老版本用的是S2_SR产品。但正如我的读者 Philipp Gaertner 指出的那样,S2_SR从 2022 年 1 月 25 日起将数字(DN)移动了 1000,之后的数据不具有可比性。现在我使用的是它的协调版本,因为它把新场景中的数据转移到与旧场景相同的范围内。

MODIS/061/MOD13A1NASA 美国地质调查局 EROS 中心的陆地进程分布式活动档案中心提供。MODIS 代表中分辨率成像光谱仪。与COPERNICUS/S2_SR_HARMONIZED不同,MODIS/061/MOD13A1只存储预计算的 NDVI 和 EVI,而不存储电磁带。谷歌在其教程中使用这个数据集制作折线图。

这两个数据集具有不同的分辨率。MODIS/061/MOD13A1只能分辨 500 米。相比之下,COPERNICUS/S2_SR_HARMONIZED中蓝色、红色和 NIR 波段的分辨率为 10 米。因此,我们可以在它的图像中看到更多的细节。

此外,MODIS/061/MOD13A1每 16 天造访同一个地方。虽然COPERNICUS/S2_SR_HARMONIZED重访频率为五天,但我需要合成它的图像以获得更好的像素质量。即便如此,其月度综合数据仍有缺口。但在一些欧洲地区,其季度复合数据似乎是连续的。

3.应用程序

这两款应用与我的 LULC 地区监控应用具有相同的结构。它们有四个主要组件:generate_collection、控件、折线图和缩略图。作为区域监控应用程序的前身,generate_collection 函数在开始和结束日期之间生成图像采集系列。在每个图像集合中,NDVI 和 EVI 都是从相应的数据集中计算或提取的。这些值被发送到折线图功能和缩略图功能。折线图为读者提供快速概览,而缩略图提供详细信息。前者记录了 NDVI 和 EVI 一段时间以来的变化。后者实际上是一个空间热图。对于每个像素,我将其 NDVI 或 EVI 值映射到五色标度:红色、橙色、白色、钢蓝色和绿色。控制组件协调它们并管理布局。最后三个功能与 LULC 应用程序中的功能大致相同。更多细节请参考我的文章。换句话说,这个项目只新实现了generate_collection功能。这两个应用程序基于两个数据集,它们具有不同的波段。这样一来,两个 app 的generate_collection功能就不一样了。

3.1 哥白尼应用程序

在哥白尼应用程序中,我首先对图像进行预处理。模糊像素百分比小于 20%的图像被保留(第 48 行)。然后,我通过修改这篇文章中的代码(第 1–17 行和第 50–51 行),来遮盖云彩,只保留植被和裸露的土壤像素。然后我用波段数据计算 NDVI 和 EVI(第 19–44 行和第 53–54 行)。之后,我将图像分组到时间箱中,并计算它们的平均合成(第 56–65 行)。这些合成反过来形成一个新的图像集合(第 56–74 行),它将由函数返回。如果在给定的时间段内没有合成图像,我会用一个空图像填充该时间段(第 69–70 行)。

3.2 MODIS 应用程序

MODIS 应用程序中的代码更简单,因为MODIS/061/MOD13A1已经将两个指数存储为NDVIEVI波段。所以我只需要选择(第 4 行)并规范化(第 7 行)它们。我的代码灵感来自于谷歌的例子

4.测试应用程序

现在是时候测试这两个应用程序了。COPERNICUS中的值是每月的综合值,而MODIS中的数据是从单个采样日期得到的测量值。此外,它们的预处理也不同。最后,分辨率不同。因此,两个应用程序之间的结果不会完全相同,即使是来自同一个目标区域。第一个问题是,这两个应用程序是否能提供类似的长期趋势。

4.1 拉普博德塔尔斯珀雷地区

在图 1 和图 3 中,可以看到 2017 年至 2022 年初为德国 Rappbodetalsperre 地区生成的时间序列(图 2)。

图二。拉普博德塔尔斯珀雷地区。图片作者。

图 3。由我的COPERNICUS 和 MODIS 应用程序生成的 2017 年至 2022 年初 Rappbodetalsperre 地区的 NDVI 和 EVI 时间序列。左:COPERNICUS/S2_SR_HARMONIZED;右:MODIS/061/MOD13A1\. 图片作者。

在图 3 中,您可以看到COPERNICUS应用程序在其时间序列中有几个间隙,而MODIS应用程序中的曲线是连续的。然而,这两个应用程序在价值范围和趋势方面提供了相似的结果。例如,这两个指数每年的夏季上涨和冬季下跌在两个应用程序中都清晰可见。它们的数值也是可比较的。比如 2017 年 6 月的 NDVI 值在COPERNICUS app 是 0.9,MODIS app 是 0.875。最后,NDVI 和 EVI 在这个测试案例中同步得很好。

图 4。我的COPERNICUS 和 MODIS 应用程序拍摄的 NDVI 和 EVI 拉普博德塔尔斯珀雷地区的缩略图。左:COPERNICUS/S2_SR_HARMONIZED;右图:MODIS/061/MOD13A1\. 作者图片。

在图 4 中,您可以看到它们分辨率的差异。COPERNICUS不仅展示了水体的轮廓,还展示了南缘几处农田的形状。相比之下,MODIS应用程序在整个区域有大约 40 个大正方形像素。水体的轮廓完全消失了。

4.2 大兴安岭地区

因为谷歌在中国无法访问,我担心谷歌地球引擎在中国地区无法工作。为了验证这个假设,我用这些应用程序监测了黑龙江省靠近中俄边境的大兴安岭地区。

图 5。大兴安岭地区 2017 年至 2022 年初的 NDVI 和 EVI 时间序列。左:COPERNICUS/S2_SR_HARMONIZED;右图:MODIS/061/MOD13A1。图片作者。

在这种情况下,MODISCOPERNICUS拥有更多的数据。其数据覆盖了 2017 年 4 月至 2022 年初的整个时间段。相比之下,COPERNICUS 数据只追溯到 2019 年 1 月。同样明显的是,COPERNICUS中的图像并不总是覆盖整个目标区域。尽管有这些差异,我们仍然可以看到这两个指数在这个森林地区的年周期。然后我在中国其他地区测试了这些应用程序,很高兴看到它们也能工作。

4.3 亚马逊雨林

在我的第三次测试中,我监测了亚马逊雨林。我选择了 Carauari 旁边的一个地区。

图 6。我的应用程序显示的 2017 年至 2022 年初亚马逊雨林的 NDVI 和 EVI 时间序列。左:COPERNICUS/S2_SR_HARMONIZED;右图:MODIS/061/MOD13A1。图片作者。

同样,MODIS应用程序拥有整个测试期间的所有数据。【2019 年 1 月才开始。我们也可以在COPERNICUS的月度综合数据中看到这些分类是多么不完整。最后,来自COPERNICUS的时间序列包含太多的间隙,在这个测试用例中没有用。

我们也可以从MODIS中得到一些有趣的观察结果。首先,与拉普博德塔尔斯珀雷和大兴安岭相比,亚马逊雨林的激增期更长,从 6 月持续到 11 月。其次,尽管 NDVI 和 EVI 同步得很好,但还是有一些小的差异。例如,2019 年 11 月 17 日,NDVI 仍高达 0.85,但 EVI 值降至 0.4。此外,NDVI 通常在 6 月达到每年的高原。相比之下,EVI 在 6 月份上涨,然后再次飙升,在 8 月至 10 月间达到夏季峰值。

结论

在本文中,我向您展示了如何创建两个应用程序来监控给定地区的 NDVI 和 EVI。这两个指数给我们暗示了潜在生态系统的生产力。一个生态系统的生产力越高,它能支持的生物就越多,生物多样性也就越丰富。所以 NVDI 和 EVI 可以作为我们生物多样性的代理人。它们可以补充传统的指标,如物种丰富度或物种密度,并告诉我们更多关于生态系统健康的信息。

COPERNICUS/S2_SR_HARMONIZEDMODIS/061/MOD13A1这两个数据集各有优缺点。COPERNICUS/S2_SR_HARMONIZED空间分辨率高,时间分辨率低。MODIS/061/MOD13A1中的图像是粗粒度的,但是它们可以覆盖我所有测试用例中的整个测试周期,没有间隙。有时,MODIS是亚马逊雨林等地区的唯一数据源。正如这个项目中的测试运行所示,应用程序的结果是可比较的。因此,建议同时使用这两种应用程序,并重叠它们的结果,以便更好地了解目标区域。您甚至可以将它们与我的 LULC 地区监测应用程序结合起来,调查遭受野火、干旱或其他干扰的地区。

还有很大的改进空间。例如,我们可以允许用户上传他们的 GeoJSON 文件。因为 Google Earth 引擎中的函数调用是异步的,所以在我当前的实现中,这两个缩略图是不同步的。所以我在寻找一种方法来同步它们。最后,我们甚至可以将两个应用程序合二为一,用户可以一次看到所有结果。

你的想法是什么?你也用谷歌地球引擎监测植被吗?如果有,请分享你的经验。

https://dgg32.medium.com/membership

在 Slack 中监控您的 AWS Lambda 函数(或任何其他 AWS 服务)

原文:https://towardsdatascience.com/monitor-your-aws-lambda-functions-or-any-other-aws-service-in-slack-809aaefe7e40

照片由布雷特·乔丹Unsplash 拍摄

一种简单的基于 Python 的方法,将完整的错误回溯发送到 Slack

随着组织的发展,在 AWS Lambda 中定期运行 ELT 或批处理推理作业是很常见的。有很多关于使用 CloudWatch 警报监控 lambda 错误的文章,但是对于周期性的 Lambda 作业,这既过于复杂,也不能在通知中提供太多有用的信息,除了有一个错误。

在这里,我提出了一种更直接的方法来监控这些周期性的λ函数。使用这种方法,您将直接在您的 slack 通道中获得包含错误消息和每个错误追溯的通知,消除了挖掘日志来排除错误的需要。虽然这种方法确实涉及到了 CloudWatch,但它不需要 CloudWatch 警报或任何像 SQS 或凯尼斯这样的消息队列服务。

在我们开始之前,有一个快速的警告:如果你的 Lambda 函数每分钟处理很多请求并开始失败,你的 slack 通道将会崩溃💥带有错误消息。大容量 lambda 函数最好使用以下参考资料中详细描述的方法之一:

该方法概述

参见下面的基本架构。

图片作者。根据发布的使用政策使用 AWS 架构图标和 Slack 徽标(1)。

用户向 API Gateway 发送一个请求,API Gateway 将该请求转发给我们用 Lambda 部署的模型。Lambda 模型将向 API 网关返回一个预测或错误,以发送回用户,并且还会将一条日志消息写入云观察日志。

到目前为止,我们在 AWS Lambda 上有一个标准的模型部署。每当日志包含文本ERROR时,CloudWatch 触发我们的通知 Lambda 向 Slack 发送一条消息,神奇的事情就发生了。

我们将如何实施:

  1. 将简单的线性模型部署到 lambda 函数,并将其连接到 API 网关端点
  2. 制造一个松弛的网钩
  3. 创建一个 Lambda 函数向 Slack 发送消息
  4. 配置一个 CloudWatch 来触发我们的通知 Lambda

让我们开始吧:

作为一个测试案例,我们将部署一个简单的模型,它将一个数字加倍,如果输入不是一个数字,就引发一个ValueError。如果你已经有一个 lambda 函数或者其他想要监控的 AWS 服务,你可以跳到创建一个 Slack Webhook

创建新的 Lambda 函数:

  1. 转到 AWS 控制台中的 Lambda 服务。
  2. 从侧边栏中选择Functions
  3. 选择Create function来——你猜对了——创建一个新功能。

作者图片

4.选择Author from Scratch,输入 Lambda 函数的名称,并选择运行时。我将调用我的函数MonitorMe,我使用 Python 3.8 作为运行时。接受其余默认设置并点击Create function

5.将下面的代码用于我们简单的 Lambda 模型。

单步执行代码:

API Gateway 通过event参数将查询字符串参数转发给我们的 Lambda 函数。

上面的代码首先将变量x设置为event['queryStringParameters']['x']event['queryStringParameters']中的每个元素都是一个字符串,所以要使用x进行任何数值计算,我们必须将其转换为数值数据类型。我们用x = float(x)来做这件事。float是表示浮点数的数据类型,所以试图将任何不是数字的东西强制转换为float会导致我们的 Lambda 抛出异常。最后,我们将x加倍,并在我们的响应体中返回它。

创建 API 网关端点

没有这一步通知也可以工作,但是设置 API 相对简单,并且为我们的MonitorMe Lambda 提供了一个实际的event 变量来处理。

  1. 访问 API 网关服务,在 HTTP API 面板上选择Build

作者图片

2.将我们的MonitorMe Lambda 函数作为 API 集成进行连接。

点击Add integration

  • 选择Lambda作为集成类型
  • 确保选择了正确的区域(这应该与 Lambda 所在的 AWS 区域相匹配),然后搜索我们在上面创建的MonitorMe Lambda 函数。
  • 为 API 提供一个名称。我正在调用我的 API MyAPI。(聪明,我知道)
  • 点击Next

作者图片

3.现在我们需要一条路线。路由将操作分配给 URL 路径。我们将要设置的动作将一个请求转发给我们的MonitorMe Lambda 函数。

  • 输入资源路径的逻辑路径。我使用了我们的 Lambda 函数的名字MonitorMe,但是你可以使用任何东西
  • 选择MonitorMe作为整合目标
  • 点击下一个的

作者图片

4.在下一个屏幕上,接受默认值。

确保自动展开打开。没有它,我们必须手动部署对 API 的任何更改。如果我们需要在部署到生产 API 之前部署到开发 API 进行测试,这将非常有用。对于这个例子,我们希望事情简单一些。

作者图片

API 已部署。来测试一下吧!

  1. MyAPI页面上找到$default阶段的Invoke URL。这是我们的基本网址。复制它。

作者图片

2.将我们在 API 创建步骤 3 中创建的路由路径添加到基本 URL。用您在上一步中复制的基本 URL 替换{YOUR API BASE URL}。(URL 不区分大小写,但为了清晰起见,我将其大写):

https://{YOUR API BASE URL}/**MonitorMe**

3.通过追加一个?x=后跟要测试的值,将查询添加到 URL。这是我们向MonitorMe Lambda 函数发送参数的地方。例如:

https://{YOUR API BASE URL}/MonitorMe**?x=2**

4.把这个插入你的浏览器,然后。2x2= 4.0

作者图片

这是一个非常有用的模型。😉

创建一个松弛的网钩

最后,我们开始进入项目的实质部分。我们将通过 webhook 向 Slack 发送消息。

  1. 为您的通知创建一个渠道。我给我的打电话aws-notifications

作者图片

2.参观 api.slack.com/apps。如果您尚未登录,您将看到一条消息,要求您登录您的 Slack 帐户以创建应用程序。动手吧。

作者图片

3.登录后,您会看到一个标签为创建应用的按钮。点击它。

作者图片

4.我们想从头开始创建我们的应用程序。

作者图片

5.在下一个屏幕上,添加应用程序名称,选择您的工作区,然后单击创建应用程序

作者图片

6.一旦你创建了应用程序,Slack API 网站会方便地推荐一些功能。我们想要传入的 Webhooks,所以找到那个框并单击它。

作者图片

7.在“传入 webhooks”页面上,单击右上角的滑块打开 webhooks。这也将在页面底部展开一个设置部分。

作者图片

8.点击页面底部设置部分的Add New Webhook to Workspace按钮。

作者图片

9.选择我们之前创建的频道并允许。

作者图片

10.现在,您将看到您已经创建了一个 webhook URL。curl 示例更新了,所以我们可以在命令行测试它。

图片作者(写完这个帖子后我删除了这个 webhook😁)

只需抓取Sample curl request to post to channel代码,并将其粘贴到您的终端中,如下所示(如果您使用的是旧版本的 Windows,您可以按照this stack overflow answer中的说明安装 cURL):

作者图片

它将返回ok。检查你的 slack 频道,你会看到 AWS 通知程序的一个帖子,上面写着Hello, World!

作者图片

耶!我们现在可以发布到我们的 slack 频道。

11.复制 Webhook URL。我们以后会需要它。

作者图片

创建一个 Lambda 函数向 Slack 发送消息

在本节中,我们将设置一个 Lambda 函数,当它被触发时,将向我们发送包含错误详细信息的 Slack 消息。

就像前面一样,创建一个新的 Lambda 函数:

  1. 转到 AWS 控制台中的 Lambda 服务。
  2. 从工具条中选择Functions
  3. 选择Create function创建一个新功能,您又猜对了。

作者图片

4.选择Author from Scratch,输入函数名,然后选择运行时。对于这一步,我将调用函数Notify并使用 Python 3.8 作为运行时。接受其余默认设置,并点击Create function

作者图片

5.将以下代码复制并粘贴到新的 Lambda 函数中。

让我们单步执行代码

  • SLACK_WEBHOOK设置为您之前创建的 slack webhook URL。 你必须将其更新为你创建的网页挂钩的 URL。
  • 当 Lambda 被调用时,AWS 调用lambda_handler函数。AWS 将eventcontext作为参数传入。event包含编码的错误信息。
  • 接下来,Lambda 调用decode_logpayload来解码错误消息。这个函数做三件事:
    1。使用base64decode解码错误字符串。
    2。使用gzip解压解码后的错误字符串。
    3。将生成的 JSON 转换成 Python 字典并返回它。
  • 然后我们构建松弛消息。关于 Slack 消息构造的细节可以在 Slack API 文档中的[创建丰富的消息布局](http://Creating rich message layouts)中找到。
    我们将每个logEvent添加到附件列表中。这确保了我们获得完整的日志消息。
    然后,我们用日志组名和包含错误日志的logEvent列表构建消息,日志组名标识抛出错误的 Lambda。
  • 最后,我们用 urllib 的Request类创建一个请求对象,并用的geturl方法将其发送给SLACK_ENDPOINT

最后一步:为我们的通知 Lambda 配置一个 CloudWatch 触发器。

这里有一个细节非常重要。我们在**Notify** 上创建触发器,Lambda 不是 Lambda 抛出错误。这意味着,如果您需要监控几个 lambda 函数,您需要为每个函数添加一个触发器到Notify Lambda。幸运的是,AWS 没有对单个 Lambda 函数的触发器数量进行限制。

设置触发器

  1. 仍然在我们的Notify Lambda 中,从页面顶部选择Add trigger

作者图片

2.这将打开触发器配置对话框。
选择CloudWatch Logs作为触发类型。
选择与您想要监控的 Lambda 日志相对应的日志组。如果你跟随,这将是/aws/lambda/MonitorMe
命名过滤器。这可以是任何东西。狂野一点。
过滤模式将决定哪些日志触发通知 Lambda。这里我将简单地输入ERROR,它将匹配任何包含文本ERROR的日志。

您可以在 CloudWatch 文档中的过滤器和模式语法页面上找到有关 CloudWatch 过滤器的更多信息。

点击对话框右下角的添加。

作者图片

我们完事了。🌴

测试

让我们确保这一切按预期进行。我们有两个案例要测试:

  1. 如果MonitorMe Lambda 记录了一个而不是错误的消息,我们应该而不是在 Slack 中得到一个通知。
  2. 如果MonitorMe Lambda 确实记录了一个错误,我们应该会得到一个有用的消息。

案例一:MonitorMe执行成功

为了测试这一点,我们使用最初测试端点时使用的相同 URL 来访问我们的 API 端点:

https://{YOUR API BASE URL}/MonitorMe?x=2

作者图片

我们验证没有消息被发送到 Slack:

作者图片

什么都没发生。成功!

案例 2:执行MonitorMe失败

让我们修改 API 调用来发送一个字符串而不是一个数字。这将导致 Lambda 抛出一个错误。

https://{YOUR API BASE URL}/MonitorMe?x=wompwomp

作者图片

非常好。一个错误。我们来看看 Slack。

作者图片

太神奇了!我们不仅在 Slack 中收到了一个通知,而且通知还附带了错误回溯!我们可以看到这是lambda_function.py的第 7 行,错误来自试图将wompwomp转换为 float。

结论

还有其他方法可以从任何 AWS 服务中捕获错误并将其发送到 Slack。我在这篇文章的开头列出了一些文章,但是当您期望错误数量很少时,这种方法是更好的,因为它更直接,并且在 Slack 中给您完整的错误消息。

Komaza ,我们使用这种方法来监控概念验证项目中的管道、数据质量检查,以及由我们的内部 web 应用之一手动触发的一些 Lambda 函数。我们立即发现了数据库模式更改、过期令牌和各种其他问题等错误,并能够在所有情况下在 24 小时内部署修复。这种相对简单的 Lambda 监控方法通过确保更高质量的数据和帮助我们更快地响应系统故障,改善了我们的利益相关者体验。

科马扎在招人

如果你想帮助我们为小农户提供改变生活的经济机会,同时为地球上最有效的碳封存引擎之一提供动力,请查看我们的职业页面。我们发展迅速,在肯尼亚和美国都有数据团队。

现在去做点好事吧。

参考

1 AWS 架构图标 & 备用介质套件

其他有用的文章:

通过调用 REST API 监控数据块作业

原文:https://towardsdatascience.com/monitoring-databricks-jobs-through-calls-to-the-rest-api-4c02d7d27278

监视在 Databricks 生产环境中运行的作业不仅需要在出现故障时设置警报,还需要能够轻松提取有关作业运行时间、故障率、最常见故障原因和其他用户定义的 KPI 的统计信息。

Databricks workspace 通过其 UI 提供了一种非常简单直观的方式来可视化各个作业的运行历史。例如,矩阵视图允许快速查看最近的故障,并显示不同运行之间的运行时间的粗略比较。

作业运行,矩阵视图(图片由作者提供)

计算失败率的统计数据或者比较不同工作之间的平均运行时间怎么样?这就是事情变得不那么简单的地方。

“工作流”面板中的“作业运行”选项卡显示了过去 60 天内在 Databricks 工作区中运行的所有作业的列表。但是这个列表不能直接从 UI 导出,至少在编写本文时是这样。

“工作流”面板中的“作业运行”选项卡显示了过去 60 天在您的工作区中运行的作业列表(图片由作者提供)

幸运的是,同样的信息(和一些额外的细节)可以通过调用 Databricks jobs list API 来提取。数据以 JSON 格式检索,可以很容易地转换成数据帧,从中可以得出统计数据和进行比较。

在本文中,我将展示如何从运行在 Databricks 工作区中的 Jupiter 笔记本连接到 Databricks REST API,提取所需的信息,并执行一些基本的监控和分析。

1.生成数据块个人访问令牌

要连接到 Databricks API,您首先需要进行身份验证,与通过 UI 连接时要求您进行身份验证的方式相同。在我的例子中,我将使用通过调用数据块令牌 API 生成的数据块个人访问令牌进行身份验证,以避免将连接信息存储在我的笔记本中。

首先,我们需要通过提供请求 URL、请求主体及其头来配置对令牌 API 的调用。在下面的例子中,我使用 Databricks secrets 来提取租户 ID,并为 Microsoft Azure 托管的 Databricks 工作区构建 API URL。资源2ff 814 a 6–3304–4ab 8–85cb-CD 0 e 6 f 879 C1 d代表数据块的 Azure 编程 ID,而应用 ID 和密码再次从数据块机密中提取。

使用数据块机密来存储这种类型的敏感信息并避免直接在笔记本中输入凭据是一种很好的做法。否则,所有对 dbutils.secrets 的调用都可以替换为上面代码中的显式值。

设置完成后,我们可以简单地使用 Python 的请求库调用令牌 API 并生成令牌。

2.调用数据块作业 API

现在我们有了个人访问令牌,我们可以配置对 Databricks jobs API 的调用。我们需要提供 Databricks 实例的 URL、目标 API(在本例中是 jobs/runs/list,用于提取作业运行的列表)和 API 版本(2.1 是当前最新的版本)。我们使用之前生成的令牌作为 API 调用头中的承载令牌

默认情况下,从提供的偏移量开始,返回的响应被限制为最多 25 次运行。我创建了一个循环,根据返回响应的 has_more 属性提取完整列表。

3.提取和分析数据

API 调用将运行的作业列表作为 JSON 列表返回,我使用 Pandas json_normalize 将该列表转换为 Pandas 数据帧。该操作将数据转换为以下格式:

通过 API 调用检索的作业运行信息(图片由作者提供)

要在响应中包含任务和集群细节,您可以在请求参数中将 expand_tasks 参数设置为 True,如 API 文档中所述。

从这些信息开始,我们可以执行一些监控和分析。例如,我使用 state.result_state 信息来计算过去 60 天中失败运行的百分比:

(图片由作者提供)

可以轻松提取许多有用的统计信息,例如所有计划的数据块作业中每天失败的作业数量。通过查看列 state.state_message,我们可以快速了解集群为失败的作业记录的错误消息。

因为我们可以访问每次运行的开始和结束时间,所以我们可以计算作业运行时间,轻松地观察任何趋势,并尽早检测潜在的问题。

作为运行日期函数的作业运行时间(图片由作者提供)

一旦我们能够以这种易于利用的格式访问这些数据,我们想要计算的监控 KPI 的类型就可以取决于应用程序的类型。计算这些 KPI 的代码可以存储在一个笔记本中,该笔记本计划定期运行并发送监控报告。

结论

这篇文章展示了一些 Databricks 作业监控的例子,这些例子可以基于通过 Databricks REST API 提取的信息来实现。此方法可以提供数据块工作空间中所有活动作业的总体视图,其格式可轻松用于执行调查或分析。

监控模型性能

原文:https://towardsdatascience.com/monitoring-model-performance-51635c044f52

您的模型是否如预期的那样持续运行?

照片由 Ibrahim BoranUnsplash 上拍摄

故事是这样的

因此,您已经构建并部署了您的模型。无论是使用简单的逻辑回归,SVM,随机森林,还是臭名昭著的深度学习。

业务用户也很高兴看到它的影响。无论是通过新的个性化和有针对性的活动留住客户,通过追加销售/交叉销售增加交易量和销售额,还是您承诺实现的任何 KPI。

恭喜你。

头几个月,一切都很顺利。

然后,你突然查看公司仪表板/报告,KPI 突然回到模型前的状态,或者更糟。利益相关者正在轰炸你的推送通知,要求回答。他们在质疑你模特的表现。

发生了什么事?

最常见的解释是:

  1. 你的模型过拟合。也许它没有考虑到一些关键因素,如季节性。也许你没有正确地对数据进行采样。
  2. 后期数据问题。也许是负载平衡器出了故障,导致系统一整天都没有更新数据。因此,要么是最新的报告不准确,要么是模型根据不完整的数据做出了推断。只需对有问题的表格执行count(*)操作,并将其上报给 IT/数据工程团队。或者,也可能是…
  3. 数据本身发生了变化。

数据被转移意味着什么?

这意味着数据发生了根本性的变化,你建立的模型不再能够代表企业的现状。无论是内部因素还是外部因素。换句话说,模型所依据的数据不再相关,因此模型已经过时。

这很可能发生在任何地方的企业。看一下 covid。还记得它带来了多大的改变吗?或者现在的热点新闻——通货膨胀。这两者都导致客户和企业以一种显著的方式改变他们的行为和工作方式。

或者让我们举一个更简单、更普通、更少启示性的例子。假设你在一家大型电信公司工作。你的一个竞争对手在他们的预付费套餐中提供了巨大的折扣和丰厚的福利。这是其他公司从未做过的事情。结果是,你的市场太喜欢这个了,以至于他们决定抛弃你去找你的竞争对手。不是你,是他们。

这些都是外部因素。内部的呢?

嗯,这肯定是你们公司干的。政策/管理的改变。该业务的增长/亏损超过了总体盈利,因此会出现新产品/裁员。也许他们试推出了一款新产品,与现有产品相比非常独特。或者你工作了 10 年的明星零售员工已经离开/退休/终于马上用完了他们的假期津贴,而顾客不喜欢替换服务。

这能避免吗?是的。如果您的企业将所有这些事件捕获到您的数据存储中。如果你曾在任何公司工作过,你就能理解这一点,要做到这一点是极其困难和昂贵的。所以,你只需要做好你所拥有的。

这就是为什么在结果发出之前实施模型监控实践是非常重要的。每次模型对最新数据进行推断时,在将结果提供给业务用户之前,您都需要注意这些数据变化。如果都是好的,那么结果就可以炸开了。如果没有,根据严重性,您可以很容易地快速修复它,或者提高对发生了不同事情以及可测量证据的认识。

如何发送可衡量的证据来证明数据已经更改?

有三种简单的方法可以做到:

  • 描述统计学

一个简单的时间序列报告可以很容易地告诉你的数据是否有变化。例如,一个简单的月度收入环比趋势的下降是一个明确的指标,表明你的业务整体表现不佳。如果业务每个月都在恶化,那么模型不会像以前那样识别销售数据只是时间问题。

  • 人口稳定指数

该指数主要衡量模型结果的总体情况,以及有多少结果已经改变了类别/群体。您需要使用最新的数据,并将其与模型表现良好的时间进行比较,例如其训练数据,或之后的一个月(也表现良好)。

假设模型产生 N 个类/组/类别。或者也可以是二进制分类,如客户流失,在这种情况下,例如,您可以获取客户流失的概率,将他们分为 N 个相等或不相等的组。例如,0–10%作为第 1 组,11–20%作为第 2 组,等等。也可以是 0–50%第 1 组,51–60%第 2 组,依此类推。重要的是整个过程的一致性。确定这个 bin 可能还需要一些商业头脑,因为来自相同数据和模型的不同 bin 会显著影响模型监控指标。

只需计算训练数据和最新数据中有多少案例/客户属于这些组。取各自占总数据的百分比。然后将训练(DT)和最新数据(DL)之差乘以DT/DL的自然对数。

这是一个计算的例子,你可以尝试在 Excel 中重新创建公式。

来源:图片来自作者

PSI 的规则是:

  • 至少一个 bin >20% —数据肯定发生了偏移。重新训练模型。如果不是,那么
  • 至少一个箱子是 10–20%—需要稍微改变。这可能会使模型的性能有所下降。如果不是,那么
  • 不到 10% —没有明显的数据偏移。继续

我们可以看到上面的例子已经有两个组的 PSI > 20%。因此,我们需要调查客户的行为发生了什么变化,并重新训练模型。

请记住,这些阈值不是固定的。这取决于你和企业愿意在多大程度上容忍这种变化。例如,在 bin 3 中,你可以清楚地看到绝对数字有巨大的差异,但是 PSI 很小。

  • 特征稳定性指数

如果 PSI 确定总体上是否存在数据偏移,那么 CSI 将确定哪些特征对其产生了影响。计算基本上与 PSI 完全相同。唯一不同的是,我们深入到有问题的箱(在上面的例子中,分组 5、8 和 9)并根据特征进一步分组。

假设你有 10 个特征。年龄。每月开支。未付账单。不管是什么情况。相应地将它们分组,并进行与 PSI 中相同的计算。

下面的例子是 bin 8 的 2 个特征的 CSI。

来源;图片来自作者

来源:图片来自作者

从上面的例子可以推测,对于 bin 8 来说,年轻客户数量上升,年长客户下降。以至于它导致了我们这些箱子的数据发生了变化。你这个群体的月支出也明显减少了。

关键要点

描述性统计、PSI 和 CSI 是监控模型性能的非常简单且非常有效的指标。但是,在确定数据是否已经转移方面,有一点比这些指标更好,那就是定期更新的业务和市场理解。始终保持对您的业务战略、市场和客户需求的更新。

火花应用的监控

原文:https://towardsdatascience.com/monitoring-of-spark-applications-3ca0c271c4e0

使用自定义指标来检测问题

照片由德拉戈 ș 格里高利Unsplash 拍摄

在这篇文章中,我将描述我们为 Spark 应用程序设置监控的经验。我们将研究显示 Spark 应用程序关键指标的定制仪表板,并帮助检测开发人员遇到的常见问题。此外,我们将更详细地讨论其中的一些问题。

动机和目标

如果你没有太多的 Spark 应用程序,控制它们的性能似乎很容易。但是,随着你的 Spark 工作量显著增加,情况可能会变得复杂。例如,我们在 Joom 逐渐发现,我们有超过 1000 个不同的 Spark 批处理应用程序(几乎都是每天或每小时运行的),它们是由十几个团队开发的。新的 Spark 应用程序会定期添加,并不是所有的应用程序都会得到很好的优化。此外,处理的数据量也在不断增加。所有这些都导致执行时间和计算成本的不断增加。因此,有必要监控 Spark 在我们公司的使用情况,这样我们就可以用一个工具来回答以下问题:

  • 我们的 Spark 工作负载如何随时间变化?
  • 我们的应用程序有多稳定和优化?
  • 在我们的应用程序中,有没有什么常见的(通常是可以解决的)问题使它们比我们希望的慢得多(因此更昂贵)?
  • 我们公司总共要花多少钱?

因此,我们创建了一组仪表板,显示我们的 Spark 应用程序的关键指标,并帮助检测一些典型问题。

我们在 Kubernetes/EKS 上使用 Spark 3,所以这篇文章中描述的一些东西是特定于这个设置的。

收集统计数据

这一切都从收集统计数据开始。为了全面了解情况,我们收集并存储了以下数据:

  • 您可以在 Spark Web UI 中看到的标准 Spark 指标(例如,任务时间、洗牌、输入、溢出、执行/存储内存峰值、失败原因、执行者移除原因等。).为了收集这些指标,我们使用一个定制的 SparkListener ,它在 Spark 应用程序运行时监听各种事件,提取我们感兴趣的所有指标,并将它们发送给 Kafka 。然后,一个特殊的 Spark 应用程序从 Kafka 读取这些指标,转换成方便的格式,并将其保存为一组 Spark 表(Spark _ app _ level _ statisticsSpark _ stage _ level _ statisticsSpark _ executor _ level _ statistics)。
  • 与 Spark 驱动器和执行器的 K8S Pods 相关的指标(参数、寿命)。我们使用普罗米修斯社区/kube-普罗米修斯堆栈
  • AWS 计费数据:EC2 实例、EBS 磁盘、数据传输的成本。
  • 关于我们执行的数据查询的信息(表名、请求的时间段等。).

关键应用指标

我们使用上面描述的 Spark 应用程序执行统计数据来构建仪表板,每个团队可以在仪表板上看到关于他们的 Spark 应用程序的最重要的信息。例如,这里有一个摘要控制面板,显示了指标如何随时间变化。

作者图片

一些指标纯粹是信息性的。它们显示了每个应用程序相对于其他应用程序的重量。我们还使用它们来了解我们的 Spark 统计数据,一般来说,以及每个团队(或应用程序)的单独统计数据,是如何随时间变化的。这些指标在正常情况下会略有波动,只有在出现可能表明火花使用不当的意外大变化时才需要注意。

这些指标包括:

  • 应用 —已完成的 Spark 应用数量。
  • 应用正常运行时间(h) —应用运行时间。即使应用程序本身和处理的数据量没有变化,该指标也可能会随着发布的不同而变化。例如,如果在下一次执行期间,应用程序没有获得所有请求的执行器(可能缺少 AWS Spot 实例),那么这一次,应用程序将需要更长的时间来完成。因此,它不是评估应用程序效率变化的最佳指标(例如,在优化的情况下),但如果您对执行时间的动态感兴趣,这可能是有用的。
  • 任务时间(h) —所有应用任务的总执行时间。在大多数情况下,当使用相同的代码、数据和执行环境时,这个指标是稳定的。因此,它通常可用于评估应用程序优化的有效性。
  • 混洗写入 Gb —所有写入混洗数据的总大小。
  • 混洗读取 Gb —所有读取混洗数据的总大小。
  • 输入 Gb —从外部来源读取的所有数据的总和(在我们的例子中,几乎总是 S3)。
  • 删除的执行器:spot 中断 —应用程序运行时由于 AWS 回收回 Spot 实例而丢失的执行器数量。我们对这个过程几乎没有影响力。如果您使用 Spot EC2 实例,您应该意识到 AWS 可以在任何时候取走它们,这将影响应用程序的执行时间。您只能优化您的 AWS 基础设施,以减少此类事件的可能性。

费用

成本,$ —运行应用程序的成本。更准确地说:我们在运行应用程序的 AWS 资源上花了多少钱(不包括存储在 S3 的数据成本)。有时候,很想知道我们在计算上花了多少钱,包括整个团队和每个单独的应用程序。
计算原理(简化):

  • 对于每个 Spark K8S Pod ,我们确定运行它的 EC2 实例,并根据 AWS 计费数据计算该 EC2 实例在其生命周期内的全部成本:EC2 instance cost + EBS cost + AWSDataTransfer cost associated with the instance
  • 接下来,我们确定 Pod 使用了多少实例:
    >Time share=Pod running time/EC2 instance running time
    >Capacity share—Pod 占用 EC2 实例的多少份额(例如,我们的标准 Spark executor Pod 占用 4xlarge EC2 实例的 1/2,8xlarge 实例的 1/4,等等。).
  • Pod cost = Full cost of the EC2 instance * Time share * Capacity share
  • 最后,一个应用程序的成本就是其所有组件的成本。

描述问题的度量

上面的度量标准给出了我们的应用程序有多重的一般概念,但是并没有说明是否有任何可以改进的地方。为了给用户更直接的帮助,我们增加了更高层次的指标,以引起对我们在实践中遇到的常见问题的注意。
此类指标的主要特征:

  • 它们只显示可以修复的问题(例如,通过设置更合适的火花参数或优化代码)。
  • 通常,任何此类指标的每个值都会告诉我们相应问题的严重程度。通过这种方式,我们可以对其进行排序,并首先看到需要注意的最有问题的应用程序。当按某些维度(例如,按日期或团队名称)聚合指标值时也是如此。
  • 对于任何应用程序,只有当问题的严重性值得关注时,我们才显示任何此类大于 0 的度量值。例如,一个小的溢出通常对应用程序没有负面影响,我们可以忽略它。在这种情况下,我们仍然将相应的度量显示为 0,以免打扰任何人。

以下是其中的一些指标。

浪费任务时间问题

该指标显示了由于应用程序中的各种故障而浪费的大约任务时间。对于失败的应用程序运行,我们认为它们所有的总任务时间都被浪费了。对于成功完成的应用程序,我们认为所有失败任务的总时间以及先前成功完成的阶段(或单个任务)的所有重试的总任务时间都是浪费的,因为这种重试通常发生在需要重新处理先前由被杀死的执行者拥有的数据时。

我们对此指标感兴趣的几点原因:

  • 这一指标允许我们检测 Spark 应用中的“隐藏”问题。当应用程序运行时,可能会有一些阶段或任务的失败会降低应用程序的速度,这可以通过使用正确的设置或环境来避免。但是由于应用程序可能最终成功完成,您的工作流管理平台(例如 Airflow )将不会报告任何此类影响应用程序效率的问题。此外,由于某些原因,应用程序可能会在第一次尝试时失败,但如果您使用重试策略,它可能会在后续尝试中成功完成。在这种情况下,这样的故障也经常发生而不被注意。
  • 这是一个定量指标,可以更清楚地报告特定应用程序问题的严重性,而不仅仅是失败的应用程序/阶段/任务的数量。

以下是浪费任务时间的一些常见原因示例,可能需要使用此类指标来检测问题:

  • 执行者的丢失,这导致已经部分处理的数据的丢失,进而导致它们的重新处理。最常见的原因是因为内存不足错误 (OOM)而杀死执行程序。OOM 有两种:
    > K8S 容器 OOM :这通常是由于*spark.executor.memoryOverhead*对于具体应用的不足。在这种情况下,我们通常要么减小分区的大小(通过增加*spark.sql.shuffle.partitions*),要么通过减少*spark.executor.memory*来增加*memoryOverhead*(*memory*+*memoryOverhead*的总和保持不变,因为它受到所使用的 EC2 实例的内存量的限制)。
    >执行器堆 OOM: 这是相当少见的,要解决它,可以尝试以下步骤(考虑到增加内存是有问题的):减少分区的大小,减少*spark.memory.fraction*,毕竟要减少每个执行器的*spark.executor.cores*(即同时执行的任务数)。
  • 由于使用了太多数据,任何 EC2 实例上的磁盘空间都不足。
  • 一些任务的随机失败。这通常是因为访问外部系统(Mongo、Cassandra、ClickHouse 等)时出现临时问题。).

我们特别注意因为 AWS 偶尔回收回点实例而丢失执行程序的情况。这不是一个应用问题,在应用层面我们也无能为力。因此,对于发生这种情况的应用程序,我们不会增加浪费的任务时间指标。

该控制面板还显示了与此问题相关的几个额外指标,例如:

  • 应用/阶段/任务失败 —失败的应用/阶段/任务的数量。
  • 删除的执行人:OOM —由于 OOM 而丢失的执行人数量。

未使用的执行者问题

该指标显示了任何已完成的 Spark 应用的理论上最大可能总任务时间与实际总任务时间之间的差异。一个应用程序理论上可能的总任务时间我们计算为:<the total time spent by all its executors in the Running state> * *spark.executor.cores*
实际的总任务时间通常总是小于理论上可能的时间,但如果它小得多,那么这就是执行器(或单个内核)大部分时间都没有被使用的迹象(但同时,它们占用了 EC2 实例上的空间)。对于每个应用程序,只有当ActualTaskTime / MaxPossibleTaskTime比率小于某个阈值时,我们才显示该指标大于 0(因此需要注意)。

可能有各种情况导致这种不合理的资源使用。仅举几个例子:

  • 由于驱动程序上的长时间同步操作(例如,当使用第三方 API 时)或在某些阶段使用非常少的并行性,执行器可能会空闲。我们使用 Sparkdynamic allocation,但是默认情况下,如果这些执行器已经缓存了数据块或者存储了 shuffle 数据,它不会缩减执行器的数量。
  • 有人循环运行大量非常短的作业。

实践表明,通常有可能以这样或那样的方式优化这种情况。此外,您可以尝试减少执行程序的数量(*spark.dynamicAllocation.maxExecutors*选项),因为在某些情况下,这可以显著减少使用的资源,而对应用程序的运行时间几乎没有影响。

溢出问题

此指标显示任何 Spark 应用程序的总溢出(磁盘)。数值越高,问题越严重。
Spark 对数据分区执行各种操作(例如,在执行 SortMergeJoin 时进行排序)。如果任何分区太大,无法在执行内存中完全处理,那么 Spark 会将部分数据溢出到磁盘。任何溢出都不是好事,但是大量溢出可能会导致严重的性能下降(特别是如果您已经用完了带有 SSD 磁盘的 EC2 实例)。

消除溢出的主要方法是减少数据分区的大小,这可以通过增加这些分区的数量来实现。

  • 如果在 Shuffle 之后发生溢出,那么值得尝试增加这个应用程序的*spark.sql.shuffle.partitions*属性。理论上,你可以尝试将这个属性设置得足够大,并依靠 AQE 动态合并洗牌分区,但目前,这并不总是很好,你应该小心设置太高,因为它也可能导致性能下降。同样值得注意的是,基于过去运行的统计数据,为每个应用程序单独自动计算分区的最佳数量相对容易。
  • 如果您使用的 Spark 表已经在右列存储了,那么 Spark 会跳过洗牌,在这种情况下,溢出会在从源读取数据后立即发生。在这种情况下,您应该尝试增加源表中的存储桶数量。

偏斜问题

不对称是指一个或多个分区的处理时间明显长于所有其他分区,从而显著增加应用程序的总运行时间。这通常是由于分区之间的数据分布不均匀造成的。该度量包含差异taskTimeMax — taskTime75Percentile(所有阶段的总和),但是目前,我们只考虑满足条件(taskTimeMax — taskTime75Percentile) > 5 min AND taskTimeMax/taskTime75Percentile > 5的那些阶段。也就是说,作为一个值得注意的不对称问题,我们只显示了可能严重影响运行时间的最严重的情况。

从 Spark 3 开始,有了一个偏斜优化特性,在 SortMergeJoin 中动态处理偏斜。但是目前,这种优化并不适用于我们所有的情况。此外,在没有这种优化的其他操作(例如,窗口函数、分组)中也可能出现偏斜。在这些情况下,我们仍然需要自己处理不对称问题。首先,我们看看数据中的哪些值是它的原因。例如,在联接/分组列中可能有许多具有空值/未知值的记录,这些记录无论如何都应该被丢弃。如果我们需要所有的数据,我们会尝试应用各种代码优化。

应用程序时间警告

该字段包含耗时过长的 Spark 应用程序的数量。我们现在用的确切规则:AppUptime > 4 hours OR TotalTaskTime > 500 hours
长时间运行的应用不一定需要修复,因为可能没有其他选择,但我们无论如何都会关注它们。

  • 如果任何此类应用程序失败(例如,由于 Spot 实例中断),Airflow 会重新启动该应用程序,并再次完成大量工作。因此,如果可能的话,最好将这些应用程序分成几个部分。
  • 长时间执行的原因可能是其他指标并不总是显示的各种问题。

输入大小警告

该指标突出显示了读取过多数据的 Spark 应用程序。我们考虑了从外部来源读取的数据量(输入 Gb 度量)和查询中使用的日期范围。这只是一个警告,因为在某些情况下,读取大量数据是必要的。但与此同时,这一指标可能会提醒某些人,有时没有必要每天读取很长时间,数据处理可以是增量的。

还值得注意的是,上面描述的一些问题可以部分解决,而不必关注每个应用程序。例如,我们已经实现了对一些火花参数(例如,spark.sql.shuffle.partitionsspark.dynamicAllocation.maxExecutors等)的最佳值的自动选择。)基于对先前运行的火花统计数据的分析,单独地用于每个应用。但是这种自动选择并不能在所有情况下都有帮助,所以所有描述的度量标准的使用仍然与我们相关。

仪表板

最后,简单介绍一下我们使用的一些仪表板。

应用性能 —时间维度中的指标。我们用它来跟踪关键指标的变化,并显示最近几天发生的问题。通常我们的应用程序每天运行,但是我们也有其他的计划选项:每小时、每周、每月等等。

作者图片

在这里,我们可以看到每个指标的数字和图形表示。团队和个人 Spark 应用的过滤器都是可用的。单击列中的值会打开一个向下钻取页面,其中列出了已完成的 Spark 应用程序运行。

按名称划分的应用性能 —按应用名称汇总的指标。

作者图片

我们主要用它来为选定的指标/问题找到最重要的应用,这样我们就知道首先关注什么。

结论

在这篇文章中,我们看了一些指标和显示它们的仪表板,这些指标和仪表板允许我们监控 Spark 在我们公司的使用情况,并检测各种问题。我们计划进一步研究这个主题:添加新的指标(特别感兴趣的是一些基于 Spark 应用程序执行计划分析的指标)并改进现有的指标。而且也有计划来提高这些工具的可用性。例如,我们正在考虑使用异常探测器。但是这些都是单独文章的主题。

监控以保护数据

原文:https://towardsdatascience.com/monitoring-to-protect-data-890b21a186c1

您如何监控您部署的模型发生了什么?

罗伯特·拉尔森在 Unsplash 上拍摄的照片

每当有人访问一个站点或进行 API 调用时,都会在操作系统的某个日志文件中记录一个条目。如果没有,真的应该有一个!阅读和处理这些日志文件对于理解好的和坏的参与者在做什么以及谁在做什么是至关重要的。当然,python 对于这种活动来说非常方便,但是处理这些日志文件的选择就像咖啡的味道一样多。你的日志策略是什么?

我在的任务是向求职者揭露 NLP 程序,安全是我的首要任务。如果你是第一次收听,那么需要注意的是我已经使用 FastAPI 和 vue.js 构建了一个应用程序,并且将它托管在 AWS 上。应用程序基础结构已经就绪,我已经对安全性进行了分类;我相信是合理的!前一段时间我已经制定出了 NLP 战略。今天我在看日志文件,一些请求来自坏人,所以我开始分析自从几周前网站上线以来的流量。我是这样做的!

在虚拟服务器上

我只运行一个小的免费层服务器,所以我不想做任何花哨的东西,而是让它服务于页面和响应 API 调用。为了简化,我在服务器上安装了 AWS 命令行工具。使用命令行需要凭证,并且您必须用一个一次性命令配置那些

sudo apt install awscli
aws configure

使用 IAM,我在 AWS 控制台上创建了一个拥有完全 S3 读写权限的新用户。配置命令要求:-

  • 访问密钥 ID;当您创建一个新用户时,您会得到这个。
  • 秘密访问密钥;创建该用户时会得到这个。
  • 默认的地区名称,在我的例子中是 EU-西-1;
  • 默认输出格式,我保留为 JSON。

接下来,我建立了一个名为“justresumes”的全新 S3 桶,这是我的网站的名字。最后,我发出了一个命令。

cd /var/log
aws s3 cp . s3://justresumes/ --recursive --include "*log"

使用 AWS 命令行,使用递归将/var/log 中的所有文件复制到新的 S3 存储桶中,并且只包括文件名中包含“log”的文件。接下来可以看到终端的截图。

AWS 虚拟服务器终端的屏幕截图—将日志文件复制到 S3 存储桶。作者图片

这样,任何闻起来像日志文件的东西现在都在 S3 桶上,准备进行分析。

将日志提取到本地机器

我知道你会说使用本地机器?但是我们正在调查那些坏人在做什么!因此,我再次创建了一个新的 IAM 用户,并检索了凭证以允许与 S3 存储桶进行编程连接。下面是我用来将特定文件从 S3 下载到本地的代码。

import os
import boto3session = boto3.Session(
    aws_access_key_id=s3_creds['id'],
    aws_secret_access_key=s3_creds['secret'])s3 = session.resource('s3')justresumes = s3.Bucket('justresumes')
store = '/Users/davidmoore/Documents/projects/semantic/logs/'for file in justresumes.objects.all():
    path, filename = os.path.split(file.key)
    if ('nginx' in path) and ('access' in filename):
        print(path, filename)
        writer=store+filename
        justresumes.download_file(file.key, writer)

使用新 IAM 用户的 S3 凭证,我创建了一个 boto3 会话,并将该会话链接到 S3 服务。今天我在找来自

/var/log/nginx -- specifically access*.*

运行该代码片段会交付一组文件。有些用 GZIP 表示压缩文件,但有些是未压缩的。Nginx 旋转日志文件并增加压缩以节省部署的 web 服务器上的存储空间。

脚本运行后日志文件夹的屏幕截图。图片由作者提供。

在那个阶段,我们有数据文件(15)。

在本地机器上处理文件

有了从 S3 下载并存储在本地的数据文件,我只需要一点解析和一个熊猫数据框就可以开始研究最近的事件了。

第一步:获取所有待处理文件的列表

store = '/Users/davidmoore/Documents/projects/semantic/logs/'import os
import gziplogs = os.listdir(store)

我们需要 gzip 来管理那些压缩文件(。gz)。

第二步:将所有的日志条目读入一个列表

logLines = []for log in logs:
    if 'access' in log:
       filetype = log.split('.')
       fullPath=store+log
       if filetype[len(filetype)-1] == 'gz':
           logFile=gzip.open(fullPath, 'rb')
           while True:
               logContent=logFile.readline()
               logLines.append(logContent)

               if not logContent:
                   break

       else:
           logFile=open(fullPath, 'rb')
           while True:
               logContent=logFile.readline()
               logLines.append(logContent)

               if not logContent:
                   break 

该脚本逐行遍历这 15 个文件,并创建一个 python 列表。如果您使用 Spyder IDE,有一种简单的方法可以使用变量浏览器查看变量的内容。下面是运行脚本后 logLines 变量的屏幕截图。

Spyder IDE 变量资源管理器—作者图片。

因此,自从服务器启动以来,我们有来自 15 个不同文件的 7790 个单独的 Nginx 条目。如果你以前没有见过 Nginx 标准日志文件,这里有一个截图。

Nginx 标准日志条目—作者图片。

下一步是解析和移动到熊猫!

我们遍历 logLines 对象中的每个条目(7,790 个列表元素)。接下来,我去 Nginx 弄清楚列和解析策略。通过反复试验,分成引号和空格,让我最终到达了我想去的地方。下面是组合日志格式的 nginx.conf 配置。

# nginx.conf
http {
  ...
  log_format combined '$remote_addr - $remote_user [$time_local] '
                      '"$request" $status $body_bytes_sent '
                      '"$http_referer" "$http_user_agent"';
  ...
}

最终,我得到了一个字典列表,其数据属性如截图所示。

解析整组日志记录后的单个记录。

终于可以开始对数据提问了!

图片来自 Spyder

有 1002 个唯一的远程 IP 地址

有 9 个 HTTP 状态代码,它们是 502 到 413。200 是 OK 的代码,这是一次成功的互动。4%%都是错误。304 被从 HTTP 重定向到 HTTPS。502 是一个糟糕的网关,当我的应用程序出现配置错误时就会出现这种情况。

一些例子

" GET/robots . txt HTTP/1.1 "403118 "-" Mozilla/5.0(兼容;blex bot/1.0;+【http://webmeup-crawler.com/)”——前住

" GET/AWS . yml HTTP/1.1 "404181 "-"—未找到

所以现在我有一些工作要做,检查所有这些记录,并确定我是否需要更改安全策略。收集和使用个人或敏感的个人数据需要绝对的安全,因此我继续探索向求职者展示安全可靠的 NLP 服务!

https://cognitivedave.medium.com/membership

摆弄 DALL-E

原文:https://towardsdatascience.com/monkeying-with-dall-e-fe1d35369c74

生成艺术讲故事

会不会有一部电影或一本漫画有人工智能生成的角色、场景和情节?这是越来越接近的可能性,让我们得到一个预览。

艾已经暴怒了。用这些人工智能系统自动生成东西是趋势。其中一个子集是从文本输入中创建图像。我们可以用这个来创作图片故事吗?让我们用 DALL-E 试试。

维基百科:DALL-E 是一个人工智能程序,它从文本描述中创建图像。它使用 120 亿参数版本的 GPT-3 转换器模型来解释自然语言输入并生成相应的图像。

我们在拥抱脸空间使用这个无代码版本:https://huggingface.co/spaces/dalle-mini/dalle-mini

今天的人工智能系统几乎是一个黑匣子,任何人都可以在没有编码知识的情况下使用。通过提供正确的文本提示,我们可以自动创建图像。让我们考虑给出一系列像故事一样流动的文本提示。

既然我们在胡闹,那就让我们用“街上的两只猴子”来提示我们的人工智能吧

作者图片

大约 30 秒后,你在右边看到的图片出现了。你可以看到整个过程…给出你的输入,DALL-E 处理后输出图像。就是这样。

作者图片

什么变了?同一句话,但我现在增加了几个词“骑自行车在一个晚上”。现在我们有了一个新的循环图像,光线从白天变成了黑夜。AI 理解并解读。

作者图片

让我们增加更多的光。添加“熙熙攘攘”改变了这一点。你明白了。通过系统地改变文本提示中的单词,你可以得到一系列与核心情节相关的图像。

嘣!!!给你。猴子夜游:一个短篇故事

作者图片

刚刚发生了什么?前面举例说明的过程在不断变化的单词中重复,我们就有了这个小故事。

作者图片

你可以在上图中看到所有的提示。每改变几个词,图像就会改变。创建了一个短的连环漫画。这个概念非常类似于使用“动画书”创建动画,当快速连续查看时,一系列图像中的细微变化显示运动/动作。在这里,我们使用文本的微妙变化,而不是图像…文本动画书。

随着我们走向未来,有许多可能性。

能不能用同一个人或物,贯穿剧情?是的,这里有一个例子。除了文本描述之外,通过给出图像提示来修改相同的吉他图像。类似地,通过将相同的字符作为提示输入,可以在整个脚本中使用这些字符。

图片提供:https://medium . datadriveninvestor . com/generative-art-2271566 DD 28 f

我们能提高图像质量吗?正如我们所说,这正在改善(查看 OpenAI 的 Dall-E 2)

我们能以编程方式生成文本提示&自动生成连贯的叙述吗?是的,现在已经可以使用 NLP 自动生成完整的报告了(checkwww . decodem . ai))&随着语言模型越来越大,这项技术也在不断改进。(检查 GPT-3、龙猫等)

我们能让它卡通化吗?(查漫画甘)

我们能换衣服吗?(查时尚甘)?

等等…所有这些技术都在发展、成熟和融合。

人工智能生成的情节、电影和漫画即将问世。系好安全带。!!

时间序列的蒙特卡罗交叉验证

原文:https://towardsdatascience.com/monte-carlo-cross-validation-for-time-series-ed01c41e2995

如何获得更好的预测性能估计值带有一点随机性

马库斯·斯皮斯克Unsplash 上拍摄的照片

对时间序列应用交叉验证需要注意几个关键方面。这些要点对于防止泄漏和获得可靠的性能估计非常重要。

在这里,您将了解蒙特卡罗交叉验证。流行的时间序列分割法的替代方法。

时间序列交叉验证

时间序列分割通常是使用时间序列数据进行交叉验证的首选方法。图 1 说明了这种方法是如何操作的。可用的时间序列被分成几个大小相等的折叠。然后,每个折叠首先用于测试一个模型,然后重新训练它。除了第一折,只用于训练。

图 1:时间序列分成 5 个部分。使用 plotnine Python 库创建的图。下面是源代码。图片作者。

使用 TimeSeriesSplit 进行交叉验证的主要好处如下:

  1. 它保留了观察的顺序。这个问题在有序数据集(如时间序列)中很重要。
  2. 它会产生许多裂缝。几个分割导致更健壮的性能估计。如果数据集不大,这一点尤其重要。

TimeSeriesSplit 的主要缺点是跨折叠的训练样本大小不一致。

这是什么意思?

假设该方法应用了 5 次折叠,如图 1 所示。在第一次迭代中,所有可用观测值的 20%用于训练。但是,这个数字在最后一次迭代中是 80%。因此,初始迭代可能不代表完整的时间序列。这个问题会影响性能估计。

你如何解决这个问题?

蒙特卡罗交叉验证

蒙特卡洛交叉验证是一种可用于时间序列的方法。想法是在不同的随机起点重复典型的维持周期。

以下是对这种方法的直观描述:

图 2:蒙特卡洛交叉验证,5 倍。图片作者。

像时间序列一样,MonteCarloCV 也保留了观测的时间顺序。它还重复几次估计过程。

MonteCarloCV 与 TimeSeriesSplit 的区别主要体现在两个方面:

  • 训练和验证样本量。如上所述,使用 TimeSeriesSplit 时,训练集的大小会增加。在 MonteCarloCV 中,训练集大小在过程的每次迭代中都是固定的。这防止了不代表整个数据的训练大小;
  • 随机分区。在 MonteCarloCV 中,验证起点是随机选择的。这个原点是标志训练集结束和验证开始的点。在时间序列分裂的情况下,这一点是决定性的。它是根据迭代次数预先定义的。

蒙特卡洛夫最早是由皮卡尔和库克使用的。详情可查阅参考文献1

我在博士期间研究过蒙特卡洛夫的一些细节。这包括与其他方法的比较,如 TimeSeriesSplit。我的目标之一是了解哪种方法能提供最好的估计。结论指向蒙特卡洛夫,所以我一直用到了现在。您可以查看参考文献[2]中的完整研究。

不幸的是, scikit-learn 没有提供 MonteCarloCV 的实现。所以,我决定自己来做:

from typing import List, Generator

import numpy as np

from sklearn.model_selection._split import _BaseKFold
from sklearn.utils.validation import indexable, _num_samples

class MonteCarloCV(_BaseKFold):

    def __init__(self,
                 n_splits: int,
                 train_size: float,
                 test_size: float,
                 gap: int = 0):
        """
        Monte Carlo Cross-Validation

        Holdout applied in multiple testing periods
        Testing origin (time-step where testing begins) is randomly chosen according to a monte carlo simulation

        :param n_splits: (int) Number of monte carlo repetitions in the procedure
        :param train_size: (float) Train size, in terms of ratio of the total length of the series
        :param test_size: (float) Test size, in terms of ratio of the total length of the series
        :param gap: (int) Number of samples to exclude from the end of each train set before the test set.
        """

        self.n_splits = n_splits
        self.n_samples = -1
        self.gap = gap
        self.train_size = train_size
        self.test_size = test_size
        self.train_n_samples = 0
        self.test_n_samples = 0

        self.mc_origins = []

    def split(self, X, y=None, groups=None) -> Generator:
        """Generate indices to split data into training and test set.
        Parameters
        ----------
        X : array-like of shape (n_samples, n_features)
            Training data, where `n_samples` is the number of samples
            and `n_features` is the number of features.
        y : array-like of shape (n_samples,)
            Always ignored, exists for compatibility.
        groups : array-like of shape (n_samples,)
            Always ignored, exists for compatibility.
        Yields
        ------
        train : ndarray
            The training set indices for that split.
        test : ndarray
            The testing set indices for that split.
        """

        X, y, groups = indexable(X, y, groups)
        self.n_samples = _num_samples(X)

        self.train_n_samples = int(self.n_samples * self.train_size) - 1
        self.test_n_samples = int(self.n_samples * self.test_size) - 1

        # Make sure we have enough samples for the given split parameters
        if self.n_splits > self.n_samples:
            raise ValueError(
                f'Cannot have number of folds={self.n_splits} greater'
                f' than the number of samples={self.n_samples}.'
            )
        if self.train_n_samples - self.gap <= 0:
            raise ValueError(
                f'The gap={self.gap} is too big for number of training samples'
                f'={self.train_n_samples} with testing samples={self.test_n_samples} and gap={self.gap}.'
            )

        indices = np.arange(self.n_samples)

        selection_range = np.arange(self.train_n_samples + 1, self.n_samples - self.test_n_samples - 1)

        self.mc_origins = \
            np.random.choice(a=selection_range,
                             size=self.n_splits,
                             replace=True)

        for origin in self.mc_origins:
            if self.gap > 0:
                train_end = origin - self.gap + 1
            else:
                train_end = origin - self.gap
            train_start = origin - self.train_n_samples - 1

            test_end = origin + self.test_n_samples

            yield (
                indices[train_start:train_end],
                indices[origin:test_end],
            )

    def get_origins(self) -> List[int]:
        return self.mc_origins

实际例子

MonteCarloCV 有四个参数:

  • n_splits :折叠或迭代的次数。这个值趋向于高达 10;
  • training_size :每次迭代训练集的大小,为时间序列大小的比值;
  • test_size :类似 training_size,但是针对验证集;
  • gap :分离训练集和验证集的观察值的数量。与 TimeSeriesSplits 中一样,该参数的值默认为 0(无间隙)。

每次迭代的训练和验证规模取决于输入数据。我发现一个 0.6/0.1 的分区工作得很好。也就是说,在每次迭代中,60%的数据用于训练。以下 10%的观察值用于验证。

下面是上述配置的一个示例:

from sklearn.datasets import make_regression
from src.mccv import MonteCarloCV

X, y = make_regression(n_samples=120)

mccv = MonteCarloCV(n_splits=5, 
                    train_size=0.6, 
                    test_size=0.1, 
                    gap=0)

for train_index, test_index in mccv.split(X):
    print("TRAIN:", train_index, "TEST:", test_index)
    X_train, X_test = X[train_index], X[test_index]
    y_train, y_test = y[train_index], y[test_index]

该实现也与 scikit-learn 兼容。下面介绍如何将其与 GridSearchCV 结合起来:

from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import GridSearchCV

model = RandomForestRegressor()
param_search = {'n_estimators': [10, 100]}

gsearch = GridSearchCV(estimator=model, cv=mccv, param_grid=param_search)
gsearch.fit(X, y)

希望你觉得 MonteCarloCV 有用!

感谢阅读,下一个故事再见。

参考

1皮卡尔、理查德·库克和丹尼斯·库克。"回归模型的交叉验证."美国统计协会杂志 79.387(1984):575–583。

[2]熊伟·塞尔奎拉、路易斯·托尔戈和伊戈尔·莫泽蒂奇。"评估时间序列预测模型:性能估计方法的实证研究."机器学习 109.11(2020):1997–2028。

蒙特 卡罗模拟

原文:https://towardsdatascience.com/monte-carlo-simulation-1ee81e6991d6

第七部分:制图

埃菲社Unsplash 上拍摄的照片

这是第七篇关于蒙特卡罗模拟的文章。我们将重申我们在之前的文章中的定义:“蒙特卡罗模拟(MCS)是一种抽样实验,其目的是估计取决于一个或多个随机输入变量的感兴趣的量的分布”。

我鼓励您阅读我以前的一些文章( MCS 第 1 部分MCS 第 2 部分MCS 第 3 部分),以了解该技术的更多方面,特别是如何用 Python 编写一些可以轻松解决的决策问题。

任何 MCS 的最后一个基本步骤是分析过程输出。请记住,在 MCS 中,我们使用大量随机输入数据来复制模拟,从而获得输出变量的大量随机值。

然后,我们使用经典的统计推断方法来获得集中趋势的一些度量,如平均值和中值,并通过其他度量来计算结果的可变性,如方差和标准差。最后,我们画一个图表来完成故事的讲述。

因此,MCS 为分析师提供了一系列关于所研究的决策问题的可能结果及其相关概率。相应地,分析流程输出的最佳图表是直方图

正如前一篇文章中所指出的:“直方图是一个图表,它让你显示潜在的频率分布或单个连续数值变量的概率分布。”

我还写了另一篇关于直方图的文章,我说:“直方图是有两个轴的二维图;纵轴是频率轴,而横轴被分成一系列数值(间隔或)或时间间隔。每个仓的频率由垂直矩形条的面积表示。每个柱覆盖了所研究变量的连续数值范围。纵轴显示从每个箱的计数中导出的频率值。

我推荐阅读这两篇文章,不仅是为了图表的“为什么&如何”,也是为了我们将在本文中使用的一些扩展(重叠直方图、密度图、频率多边形)。

在下文中,我们将绘制一组直方图或类似的图表,试图阐明哪一个是具有最佳故事性的 CSM 的图形表示。

我们将使用蒙特卡罗模拟列表中第第四篇的数据,在该列表中,我们采用了 MCS 技术,根据相应的回报率在不同的备选方案之间进行选择。

蒙特卡洛模拟直方图

使用直方图时要执行的第一个任务是确定仓或区间的数量。在以下前提下,应该总是用不同数量的箱进行实验:如果间隔的数量很小,则确定分布的真实结构是不可行的;相反,如果箱的数量很大,那么采样误差就显得太重要了。****

图 1 显示了对应于来自第四篇文章的数据的直方图,分别具有 10、20、30 和 40 个间隔。我认为选择 30 或 40 的区间是合适的,因为它允许我们确认我们正处于单峰、对称分布中,在输出中没有异常值。****

图 1:作者用 Matplotlib 制作的直方图

绘制直方图的一个有趣的替代方法是 Plotly Express (PE)。请记住:“Plotly Express 是 Plotly.py 的高级包装器,与 Plotly 生态系统的其余部分完全兼容。”这是一个免费的、开源的、交互式的、基于浏览器的 Python 图形库。

数据通信的一个重要方面是图形的整体风格。在这方面,PE 提供了十一个主题或模板用于简单快速地设计图表。此外,使用直方图 ( px.histogram() ),PE 有四种类型的归一化用于呈现数据:a)没有 histnorm 参数用于每个 bin 中的标准计数(默认模式, template = 'ggplot2' ,图 2);b)hist norm =‘percent’用于百分比计数(每个区间中样本的分数,template =‘simple _ white’,图 3);c)hist norm =‘density’用于密度直方图(所有条形面积的总和等于样本点的总数,template =‘simple _ white’,图 4);d)hist norm =‘概率密度’对于概率密度直方图(现在,所有条形面积之和等于 1,template =‘simple _ white’,图 5)。****

图 2,作者用 Plotly Express 做的。

分布的视觉表现可以用**边缘支线剧情来增强。使用关键字 marginal ,我们可以给任何用 PE 绘制的直方图添加一个地毯、一把小提琴或者一个盒子支线剧情。rug 图(marginal =‘rug’,图 3)就像一个直方图,带有零宽度条块和代表输出数据每个值的矩形标记。****

图 3,作者用 Plotly Express 做的。

箱形图(marginal =‘box’,图 4)显示了五个数字的统计汇总,包括最小值、第一个四分位数、中值、第三个四分位数和最大值。它还显示数据集中出现的任何异常值。

图 4,作者用 Plotly Express 做的。

最后,小提琴图类似于盒子图,在每一侧增加了旋转的核密度图。小提琴图显示了所表示的数据的概率密度,尽管通过核密度估计器进行了平滑1。图 5 显示了添加了小提琴子图的概率密度直方图(marginal =‘violin’)。

图 5,作者用 Plotly Express 做的。

我更喜欢盒子子图,因为它可以很容易地检测四分位数,中位数,异常值和四分位数之间的范围。它还允许我们容易地想象输出变量的分布是否不对称或偏离正态分布。如果有非常高的最大值或非常低的最小值。

第 4 篇中,我们用一个重叠的阶跃直方图表示了三个备选项的频率分布(图 6)。我们指出:“强烈建议使用阶跃直方图同时比较两个以上的频率分布,以避免图表混乱。”在 Matplotlib 中,我们用关键字 histtype ='step '生成一个步长直方图。我没有在 Plotly 中找到一个等价的关键字。

图 6:作者用 Matplotlib 制作的重叠阶梯直方图。

现在,我们将使用累积频率直方图来分析输出数据。除了显示累积频率而不是标准频率之外,它们看起来与经典直方图相同。它们计算累计观察次数,直到预先指定的时间间隔。图 7 显示了 3000 次重复的累积直方图(“累积启用=真”)

图 7:作者用 Plotly Express 制作的标准计数累积直方图。

但是毫无疑问,如图 8 所示,使用百分比归一化( histnorm = 'percent ',cumulative_enabled = True )可以最好地传达该消息。我们在第 2 篇(MCS 风险分析)中使用了累积直方图来回答涉及投资项目风险的问题。

图 8:作者用 Plotly Express 制作的百分比累积直方图。

最后,我们借鉴了 BEXGBoost [2]的一些想法,使用 empiricaldist 库[3]绘制累积分布函数(CDF)。概念上的想法是显示一个平滑干净的图表,只关注一组统计测量,没有直方图特有的锯齿状。

图 9:作者用 empiricaldist 做的一个累积分布函数。

结论

蒙特卡洛模拟是预测不同结果或输出变量的概率的简单而强大的方法,这些结果或输出变量取决于一个或多个随机输入变量。

该方法广泛应用于风险管理、定量分析、金融、工程和科学等领域。

MCS 包括以下步骤:1)建立预测模型;2)确定随机输入变量的概率分布;3)用不同的随机输入复制模拟;4)使用推断统计方法分析随机输出,并选择适当的图表以更好地讲述故事。

我们指出直方图是分析 MCS 流程输出的最佳图表。特别是带有边缘方框子图的百分比直方图。此外,累积直方图可以帮助决策者和项目经理定量评估风险对项目的影响。

参考

1:https://en.wikipedia.org/wiki/Violin_plot

[2]: 直方图的 3 个最佳(通常更好)替代方案| BEXGBoost |走向数据科学

**【3】:【https://github.com/AllenDowney/empiricaldist **

蒙特 卡罗模拟

原文:https://towardsdatascience.com/monte-carlo-simulation-2b24fc810683

第一部分:报童问题

Unsplash 上的Cup 先生/杨奇煜·巴拉拍摄

在本系列的第一篇文章中,我们将模拟定义为一种数值技术,包括建立被研究系统的数学和/或逻辑模型,然后对其进行实验,收集数据,使我们能够获得一个估计器来帮助解决复杂的决策问题。

在同一篇文章中,我们将模型定义为真实流程或系统的简化但有效的表示,旨在获得对其行为的一些理解。

我们还对模型进行了分类,特别区分了连续模型离散模型,前者的行为(状态变量)随时间不断变化,后者的状态变量仅在单独的时间点发生变化。另一个重要的分类包括静态模型(T18)和动态模型(T20),静态模型是系统在特定时间的代表,动态模型是随着时间的推移而发展的。

与上述分类相关的有三种不同类型的模拟:连续事件模拟、离散事件模拟和蒙特卡罗模拟。

关于离散事件模拟(DES) 的原理和概念在前面指出的系列中提供。我们用 SimPy 编写了几个例子,这是一个基于纯 Python 的面向对象、基于流程的离散事件模拟框架。在以后的文章中,我们将发展与连续事件模拟相关的概念和原则。

在这篇文章中(可能在其他几篇文章中),我们正在处理蒙特卡罗模拟。

蒙特卡罗方法

蒙特卡罗方法( MCM )是一组用于解决数学问题的数值方法,其中随机样本的使用将它们与等效方法区分开来。

这个术语是希腊裔美国物理学家尼古拉斯·米洛斯发明的,当时他正在洛斯阿拉莫斯国家实验室与 T2·约翰·冯·诺依曼和 T4·斯坦尼斯劳·乌拉姆一起研发第一颗原子弹。这个术语源自位于摩纳哥公国的著名赌场

MCM 的概念思想在于通过从计算机中表示的模型中重复采样来估计某些量。两类数学问题通常用这些技术来解决:积分和优化。

关于本系列文章中描述的内容,当我们提到蒙特卡罗模拟模型时,我们谈论的是试图解决优化问题的静态、离散、随机模型。

从方法的角度来看,蒙特卡罗模拟是一种抽样实验,其目的是估计取决于一个或多个随机输入变量的感兴趣的量的分布。我们对计算这些量的点估计和置信区间特别感兴趣。不可避免地,我们的估计量会有抽样误差,我们的首要任务是确定重复次数,以提高估计值的确定性。

我们将通过库存模型中的一个经典例子来说明蒙特卡罗模拟:新闻供应商库存问题。

报摊问题

这里的术语是从面临不确定需求的报纸销售商所面临的日常情况中创造出来的:在一天开始时有多少报纸可用,以便不至于短缺和失去销售,或者有剩余的报纸而不能在第二天出售(我希望数字本地人知道报纸是什么;以防万一这里有个链接

许多现实世界的系统类似于上述情况:零售店、飞机座位、时尚商品行业、制造系统。因此,通过解决报童问题获得的洞察力可能有助于在更有价值的商业环境中解决决策问题。

问题的一般设置如下:有一个卖主,一个供应商,和客户。我们只考虑一个单周期的情况。在该阶段开始时,供应商有兴趣确定向供应商订购多少个产品( Q )来满足客户需求。这个客户需求是随机的,用一个随机变量 D 及其对应的概率密度函数 f(D) 和累积分布函数 F(D) 来表示。

供应商完全知道以下值:购买成本;销售价格(s);残值( u )。残值是产品在使用寿命结束时的估计价值。典型的情况是销售价格高于购买成本,而购买成本又高于残值。

供应商必须考虑两种情况:1)订购数量大于日需求量(Q>D);2)订购数量等于或小于日需求( Q < =D )。在第一种情况下, Q — D 单位剩余,必须由卖方以低于售价的残值回收。在第二种情况下, D — Q 单位代表销售损失。

因此,我们为卖方开发了以下利润等式:

利润=每次销售收入+每次回收收入-每次订单成本

在哪里

Revenue _ per _ Sales = s * minimum(D,Q)

Revenue_per_Salvage = u *。最大值(0,Q — D)

每订单成本= c * Q

所以,利润是 Q & D 的函数。由于需求是随机变量,利润也是随机变量。为了确定向供应商订购多少单位,我们必须计算利润的最大期望值。

这种计算可以使用蒙特卡罗方法以非常简单和有效的方式来完成。

用 Python 进行蒙特卡洛模拟

在我们的示例中,供应商以 15.50 美元的价格购买产品,然后以 27.00 美元的价格出售。残值是 2.50 美元。

下表显示了每日单位需求的分配情况:

如前所述,首先我们必须确定样本中的观察值数量。

第 5 篇中我们说过估计量的精度是指置信区间的半宽度( HW )。它由以下等式描述:

其中 n 是样本数, tα/2,n1是自由度为 n1的 t 分布的上 100(1-α/2)个百分点,而 s 是样本标准偏差。

我们希望在置信区间的半宽度值上建立一个界限 (B)。

求解 n 的结果为:

这是 python 代码,用于确定大小为 n0= 100、界限为 10.00、置信度为 95%的导频样本的观测值数量。

[@author](http://twitter.com/author): darwt
"""
# Import Modules
import pandas as pd
import numpy  as npfrom scipy import stats
from scipy.stats import uniform
from scipy.stats import semimport matplotlib.pyplot as plt  
your_path = 'C:/Users/darwt/desktop/'#...................................................................
# initialization module

Price_Per_Unit   = 27.0
Cost_Per_Unit    = 15.50
Salvage_Per_Unit = 2.50list_of_orders = [40]                 ## quantity ordered for the pilot run
length = len(list_of_orders)bound = 10.0                           ## selected by the analyst 
confidence = 0.95                      ## selected by the analyst 
# .........................................
# historical daily demand
daily_demand = [5, 10, 40, 45, 50, 55, 60]# discrete probabilities for daily demand
pr1, pr2, pr3, pr4, pr5, pr6, pr7 = 0.1, 0.2, 0.3, 0.2, 0.1, 0.05, 0.05

pr1 = round(pr1, 2)
pr2 = round(pr1 + pr2,2)
pr3 = round(pr2 + pr3,2)
pr4 = round(pr3 + pr4,2)
pr5 = round(pr4 + pr5,2)
pr6 = round(pr5 + pr6,2)
pr7 = round(pr6 + pr7,2)list_of_probs = [pr1, pr2, pr3, pr4, pr5, pr6, pr7]df1 = pd.DataFrame(daily_demand, columns = ['demand'])
df2 = pd.DataFrame(list_of_probs,columns = ['range'])

df_demand = pd.concat([df1, df2], axis = 1)#....
Number_of_Replications = 100for j in range(length):
    list_of_profits = []
    for run in range(Number_of_Replications):
        # uniform distribution for a random variate
        r_v = uniform.rvs(size=1)

        for i,row in df_demand.iterrows():
            probab = df_demand.loc[i, 'range']

            if r_v < probab:
                Qty_Demand = df_demand.loc[i, 'demand']
                break

        Qty_Ordered = 40   ##list_of_orders[j]

        Qty_Sold = np.minimum(Qty_Demand, Qty_Ordered)
        Qty_Left = np.maximum(0, Qty_Ordered - Qty_Demand)

        Revenue_per_Sales = Qty_Sold * Price_Per_Unit        
        Revenue_per_Salvage = Qty_Left * Salvage_Per_Unit        
        Cost_per_Order =  Qty_Ordered * Cost_Per_Unit

        Profit = Revenue_per_Sales + 
                 Revenue_per_Salvage - Cost_per_Order        
        list_of_profits.append(Profit)

    media = np.mean(list_of_profits)
    stand = np.std(list_of_profits)

    dof  = Number_of_Replications - 1    
    t_crit = np.abs(stats.t.ppf((1-confidence)/2,dof))numb_of_samples = int((stand *t_crit / bound) ** 2)
print('')
print(' The number of samples is %3s' %numb_of_samples)

在上面的代码中,特定的随机数流是不可重复的。我们进行了几次试运行,用最后一个方程计算的样本数在 5000 左右波动。所以,我们用这个数字进行生产。

订单数量为 20、30、40、45、50 和 60 单位的新闻供应商问题的 python 代码如下:

[@author](http://twitter.com/author): darwt
"""# Import Modules
import pandas as pd
import numpy  as npfrom scipy import stats
from scipy.stats import uniform
from scipy.stats import semimport matplotlib.pyplot as plt  
your_path = 'C:/Users/darwt/desktop/'#...................................................................
# initialization module

Price_Per_Unit   = 27.0
Cost_Per_Unit    = 15.50
Salvage_Per_Unit = 2.50list_of_orders = [20,30,40,45,50,60]
length = len(list_of_orders)bound = 0.10                           ## selected by the analyst 
confidence = 0.95                      ## selected by the analyst 
# .........................................
# historical daily demand
daily_demand = [5, 10, 40, 45, 50, 55, 60]# discrete probabilities for daily demand
pr1, pr2, pr3, pr4, pr5, pr6, pr7 = 0.1, 0.2, 0.3, 0.2, 0.1, 0.05, 0.05

pr1 = round(pr1, 2)
pr2 = round(pr1 + pr2,2)
pr3 = round(pr2 + pr3,2)
pr4 = round(pr3 + pr4,2)
pr5 = round(pr4 + pr5,2)
pr6 = round(pr5 + pr6,2)
pr7 = round(pr6 + pr7,2)list_of_probs = [pr1, pr2, pr3, pr4, pr5, pr6, pr7]df1 = pd.DataFrame(daily_demand, columns = ['demand'])
df2 = pd.DataFrame(list_of_probs,columns = ['range'])

df_demand = pd.concat([df1, df2], axis = 1)column_labels = ["Order Quantity", "Mean","Std.Dev","Var.",
                 "Std. Error","Median", "Skewness", "Kurtosis",
                 "CI Half Width", "CI LL", 'CI UL']df = pd.DataFrame(columns=column_labels)
# ...............................................
Number_of_Replications = 5000for j in range(length):
    list_of_profits = []
    for run in range(Number_of_Replications):
        # uniform distribution for a random variate
        r_v = uniform.rvs(size=1)

        for i,row in df_demand.iterrows():
            probab = df_demand.loc[i, 'range']

            if r_v < probab:
                Qty_Demand = df_demand.loc[i, 'demand']
                break

        Qty_Ordered = list_of_orders[j]

        Qty_Sold = np.minimum(Qty_Demand, Qty_Ordered)
        Qty_Left = np.maximum(0, Qty_Ordered - Qty_Demand)

        Revenue_per_Sales = Qty_Sold * Price_Per_Unit        
        Revenue_per_Salvage = Qty_Left * Salvage_Per_Unit        
        Cost_per_Order =  Qty_Ordered * Cost_Per_Unit

        Profit = Revenue_per_Sales +
                 Revenue_per_Salvage - Cost_per_Order        
        list_of_profits.append(Profit)

    media = np.mean(list_of_profits)
    stand = np.std(list_of_profits)
    var   = np.var(list_of_profits) 
    std_error = sem(list_of_profits)

    median = np.median(list_of_profits)
    skew   = stats.skew(list_of_profits)
    kurt   = stats.kurtosis(list_of_profits)

    dof  = Number_of_Replications - 1    
    t_crit = np.abs(stats.t.ppf((1-confidence)/2,dof))

    half_width=round(stand*t_crit/np.sqrt(Number_of_Replications),2)  
    inf = media - half_width
    sup = media + half_width  

    inf = round(float(inf),2)
    sup = round(float(sup),2)    

    list_of_statistics = []
    list_of_statistics.append(round(Qty_Ordered,2))
    list_of_statistics.append(round(media,2))
    list_of_statistics.append(round(stand,2))
    list_of_statistics.append(round(var,2))
    list_of_statistics.append(round(std_error,2))

    list_of_statistics.append(round(median,2))
    list_of_statistics.append(round(skew,2))
    list_of_statistics.append(round(kurt,2))

    list_of_statistics.append(round(half_width,2))
    list_of_statistics.append(round(inf,2))
    list_of_statistics.append(round(sup,2))

    df.loc[len(df)] = list_of_statistics

fig, ax = plt.subplots(1,1)
ax.axis('tight')
ax.axis('off')runs_table = ax.table(cellText = df.values, 
                      colLabels = df.columns, 
                      rowLabels = df.index +1 ,
                      rowColours =["skyblue"]*(length), 
                      colColours =["cyan"]*11, 
                      cellLoc='center', loc="center",
                      bbox = [0.1, 0, 1.9, 1.0])
ax.set_title("Monte Carlo Simulation: Profit Statistics vs Order Quantity", fontsize=18, y= 1.2 , pad = 4)runs_table.auto_set_font_size(False)
runs_table.set_fontsize(8)
plt.savefig(your_path +'MonteCarlo.png',
            bbox_inches='tight', dpi=150)                 
plt.show()x  = df['Order Quantity']
y  = df['Mean']
ci = df['CI Half Width']fig, ax = plt.subplots()
ax.plot(x,y)
ax.fill_between(x, (y-ci), (y+ci), color='b', alpha=.1)
fig.suptitle('Monte Carlo Simulation', fontsize=20)
plt.xlabel('Order Quantity', fontsize=18)
plt.ylabel('Profit (U$S)', fontsize=16)plt.savefig(your_path +'MonteCarlChart.png',
            bbox_inches='tight', dpi=150)
plt.show()

表 1 总结了关键的描述性统计指标。利润的最大期望值对应于 40 个单位的订单数量。记住,偏斜度描述的是分布不对称的程度。我们的计算显示了适度的负偏态分布。另一方面,峰度指的是分布的峰值或平坦性。我们的计算表明,分布相对平坦,具有高度的分散性。对于我们的决策问题,我们的主点估计量的置信区间的半宽度是合理的。

表 1:由作者用 Matplotlib 制作

图 1 显示了 20、30、40、45、50 和 60 个单位的平均利润和相应的置信区间。可以看出,利润最大化的订货量对应 Q = 40,产生的平均利润为 230.09 美元。

图 1:作者用 Matplotlib 制作的

前面的例子显示了蒙特卡罗模拟的本质:通过重复采样来估计随机输出变量的期望值的静态模拟。

在以后的文章中,我们将继续详述 MCMs 的其他应用,特别是风险分析。

不要忘记给作者小费,尤其是当你把这篇文章添加到列表中的时候。

蒙特 卡罗模拟

原文:https://towardsdatascience.com/monte-carlo-simulation-421110b678c5

第 5 部分:随机性&随机数生成

埃里克·麦克莱恩Unsplash 上拍摄的照片

在本系列的第一篇文章中,我们将蒙特卡罗方法 ( MCM )定义为一组用于数学问题求解的数值方法,其中随机样本的使用将它们与等效技术区分开来。这些方法提供了模拟结果的分布,从中可以很容易地获得汇总统计数据和图表。

由于大多数现实生活中的系统表现出大量的随机行为,MCM,特别是蒙特卡罗模拟(MCSs) 是分析和解决复杂决策问题的有价值的工具,因为它们可以灵活地将随机性融入建模中。

因此,为了使用 MCSs 作为有效的分析工具,清楚地理解与随机性相关的一组概念和基本定义是非常重要的。

定义和概念

《韦氏词典词典》将随机性定义为“随机或看似随机的性质或状态(如缺乏或看似缺乏明确的计划、目的或模式)”。剑桥词典[2]将其定义为“随机的性质(偶然而非按计划发生、完成或选择)”。当然,这两个都是非技术定义,与该术语的常见用法有关。或者,在蒙特卡罗模拟中,通过使用随机数和概率分布来结合随机性。

一个概率分布表示某个随机变量可能取的所有可能值,加上这些值的概率总和【3】。有由它们的密度函数定义的连续概率分布函数和由概率质量函数定义的离散概率分布函数。MCSs 中最常用的概率分布是均匀分布(连续)、正态分布(连续)、指数分布(连续)、泊松分布(离散)、威布尔分布(连续)和伯努利分布(离散)。

泊松分布的概率质量函数为:

根据平均值 μ 和方差 σ 的不同值,正态分布的概率密度函数。资料来源:https://en.wikipedia.org/wiki/Normal_distribution。

随机变量是实验输出的数字描述。随机变量可以是连续的,也可以是离散的。随机变量的期望值对应于平均值或统计平均值的概念。我以前的文章中描述的蒙特卡洛模拟的结果(第一篇中的利润,第二篇中的总成本,第三篇中的损失时间百分比,第四篇中的回报率)是随机变量的例子。

一个随机变量是一个随机变量的特定实现(一个实例)。这是一个概率分布的特殊结果。

随机数是“一个从某个特定的分布中随机选择的数,从这些数的大集合中选择出来的一个数可以再现基本的分布”[4]。在模拟中,特别是在 MCS 中,随机数是从 0 和 1 之间的均匀分布中选择的随机变量。

随机数可以通过以下方式获得:1)物理设备(掷骰子、掷硬币、旋转轮盘、看特定的桌子、大气噪声、盖革计数器等)。);2)专门设计的电子器件;3)递归算法。

正如在计算机模拟中,我们需要来自非常不同的概率分布的数千个随机数,唯一合理的解决方案是使用名为随机数生成器的递归算法。这种递归算法从前一个随机数生成新的随机数。

由计算机算法生成的数字序列并不是真正随机的,因为它们是由确定性公式生成的。所以,他们被称为伪随机数。那些特定的计算机算法产生的数字流看起来是随机的。

总之,随机数生成器是一种用于生成伪随机数序列的递归算法。

伪随机数序列是(0,1)中的确定性数字序列,具有与均匀分布在 0 和 1 [5]之间的真随机数序列相同的相关统计特性

一个好的通用(伪)随机数发生器必须包括以下特性:

  • 所有数字必须均匀分布在(0,1)中
  • 连续值之间没有序列相关性
  • 很长的一段时间(周期的长度)
  • 指定起始种子的可重复性
  • 简单的实现
  • 计算效率高

随机数发生器

最常用于生成(伪)随机数流的递归算法是线性同余生成器(LCG) 。如前所述,递归算法需要前一个伪随机数来生成下一个伪随机数。这也意味着需要一个种子号来启动序列。

LCG 的基本公式是:

其中 a 为常数乘数, c 为常数加值(增量), R0 为种子数, m 为模数。它们(a,c,R0,m)都是整数,并具有以下附加条件:

mod 是数学运算取模,返回一个数除以另一个数后的余数或有符号余数(模数)[6]。比如 10 mod 3 = 1,因为我们可以把 3 除以 10 三次,剩下余数 1;251 mod 17 = 13,因为我们可以将 17 除以 251 的 14 倍,剩下 13 的余数。

由于伪随机数流必须在(0,1)中,我们必须计算如下:

让我们通过几个例子来看看它是如何工作的:

  1. LCG,其中 a = 4,c = 3,m = 16,R0 = 11

可以看出,不可能产生一个以上不同的随机数。显然,这不是一个随机数字序列。

  1. LCG,其中 a = 13,c = 13,m = 16,R0 = 37

该算法生成一个由 16 个不同随机数组成的序列,然后该序列开始重复。序列的长度称为 LCG 的周期。我们想要一个非常长的周期,理想情况下等于模数 m (本例中为 16),其中产生每个小于 m 的正整数。当这种情况发生时,该算法被认为实现了其全周期全周期。

当然,只有 16 个不同的随机数对于蒙特卡罗模拟是没有用的,因为蒙特卡罗模拟需要数千个独立的重复,每个重复都有不同的随机变量。大量的数学家和统计学家研究了如何开发具有完整周期和数百万不同随机数序列的算法。这些发展超出了本文的范围。我只想指出,有几个算法使用质数来表示模数,常数乘数 a 必须以 01、21、41、64 或 81 结尾,增量 c 必须以数字 1、3、7 或 9 结尾。

梅森扭结器是松本诚和西村拓治在 1997 年开发的一种强伪随机数发生器【7】。这是一个经过充分测试的生成器,基于梅森素数2 * (19937)-1,它在令人印象深刻的 2 * (19937)-1 周期内产生 53 位精度浮点。像 Python,R 这样的软件,以及在更近的版本中,MATLAB 提供了梅森图作为标准,因为它具有不可思议的周期和极好的统计特性。然而,由于生成器是完全确定的,因此它并不适用于所有目的。显然,你不能将它用于加密或高安全性的目的。

其他优秀的伪随机发生器包括滞后斐波那契发生器和 L'Ecuyer 发生器(“一个普通桌面大约需要 219 年才能耗尽算法的周期”)[8]。

一个随机变量生成器是一个算法函数,用于从特定的概率分布中生成一个随机观察流。

在 Python 中,有两组随机变量生成函数:(1)来自 Python 标准库中的 random 模块。它使用梅森捻线机作为核心发电机;(2)来自 Numpy 的随机变量发生器。在 Numpy 中,随机数生成分为两个部分,一个位生成器和一个随机生成器。位生成器的职责有限。它管理状态,并提供产生随机双精度值和随机无符号 32 位和 64 位值的函数。随机发生器采用位发生器提供的流,并将它们转换成更有用的分布,例如模拟的正态随机值[9]。

Scipy 没有随机数生成器。它使用 Numpy 模块。scipy.stats 模块包括 80 多个连续随机变量和至少 10 个离散随机变量。产生随机变量的方法是 rvs 。使用 Scipy 的最大优势在于,每个发行版都从它继承了一组泛型方法。例如,指数连续随机变量继承了计算分布的平均值、中值、方差、标准差、峰度和偏斜度的方法。其他方法计算 pdf、CDF、生存和逆生存函数[10]。

在模拟研究中,经常需要再现随机数序列,特别是当试图使用一种称为普通随机数的技术来比较模型对不同备选方案的响应时。为此,发生器需要一个数字来启动序列(一个一个种子值)。默认情况下,它使用当前系统时间。但是,如果需要再现流,每次需要重新开始序列时,都必须编写类似于 random.seed(1234) 的代码。

让我们看看它是如何工作的:

首先,你导入模块(导入随机)然后调用相应的函数( random.random() )

没有种子编号:

import randomr1 = random.random()
print(r1)r2 = random.random()
print(r2)

你会得到两个不同的均匀分布的随机数:

0.007491470058587191

0.9109759624491242

带有种子编号

random.seed(1234)r3 = random.random()
print(r3)
r4 = random.random()
print(r4)print()random.seed(1234)r5 = random.random()
print(r5)
r6 = random.random()
print(r6)

现在,您将生成相同的观察流:

0.9664535356921388

0.4407325991753527

0.9664535356921388

0.4407325991753527

以下代码尝试使用 Python 标准库、Numpy 库和 Scipy 库中的 random 模块,比较从指数分布生成 500.000 个随机观测值和从正态分布生成另外 500.000 个随机观测值所需的时间。

import numpy as np
from scipy.stats import norm
from scipy.stats import expon
import timemu = 5
sigma = 2lamb = 1.2
#........................................................start1 = time.time()list_of_normal, list_of_expon = [], []
for i in range(500000):
    expon_with_random_module  = random.expovariate(1/lamb)
    normal_with_random_module = random.gauss(mu, sigma)
    list_of_expon.append(expon_with_random_module)
    list_of_normal.append(normal_with_random_module)end1 = time.time()delta1 = round(float(end1 - start1), 4)
print(" The time used to execute with the random module is:")
print(delta1)start2 = time.time()expon_with_numpy  = np.random.exponential(1/lamb, 500000)
normal_with_numpy = np.random.normal(mu, sigma,   500000)end2 = time.time()delta2 = round(float(end2 - start2), 4)
print(" The time used to execute with the numpy module is:")
print(delta2)start3 = time.time()expon_with_scipy = expon.rvs(scale = 1/lamb, size = 500000)   
normal_with_scipy = norm.rvs(loc = mu, scale = sigma, size= 500000)end3 = time.time()delta3 = round(float(end3 - start3), 4)
print(" The time used to execute with the scipy module is:" )
print(delta3)

我的计算清楚地显示了使用 Numpy 库的优势。

在蒙特卡罗模拟中使用伪随机序列之前,应该对序列的随机性进行彻底的分析。首先,你必须通过应用卡方科尔莫戈罗夫-斯米尔诺夫测试来测试的一致性序列相关性(与自相关相同)可以用德宾-沃森测试进行测试。必须对每个随机序列进行运行测试,例如使用 Wald-Wolfowitz 测试。您还必须使用间隙测试和/或扑克测试来测试数字模式

别忘了给小费,尤其是当你把文章添加到列表中的时候。

结论

随机性与任何结果不可预测的过程有关。几乎所有现实世界的系统都表现出一定程度的随机性。由于这个原因,像蒙特卡罗模拟这样的技术对于分析这样的系统是非常有价值的工具。但是在使用它们之前,最好先弄清楚与随机性和随机数生成相关的某些定义和概念。

参考文献

[1]https://www.merriam-webster.com/dictionary/randomness

[2]https://dictionary . Cambridge . org/dictionary/English/randomnes

https://medium.com/@dar.wtz/histograms-why-how-431a5cfbfcd5

https://mathworld.wolfram.com/RandomNumber.html

[5] B.D. Ripley,随机模拟(Wiley,纽约,1987 年)。

https://en.wikipedia.org/wiki/Modulo_operation

[7]https://en.wikipedia.org/wiki/Mersenne_Twister

[8] 皮埃尔·勒古耶 ( 1988 )。 高效便携的组合随机数发生器 。美国计算机学会通讯第 31 卷第 6 期

[9]https://numpy.org/doc/stable/reference/random/index.html#

[10]https://docs . scipy . org/doc/scipy/reference/generated/scipy . stats . expon . html?highlight = scipy % 20 stats % 20 expon

蒙特 卡罗模拟

原文:https://towardsdatascience.com/monte-carlo-simulation-5e58f054bebb

第四部分:回报率

UnsplashGuerrillaBuzz Crypto PR 拍摄的照片

蒙特卡罗方法是一组基于重复随机抽样确定多种可能结果概率的数值技术。这是一个非常简单而有效的工具,用于解决工程、科学、商业和管理问题。蒙特卡罗模拟(MCS)** 是一种处理静态、离散、随机模型的特殊技术,试图解决优化问题。你可以阅读我以前的文章( MCS 第一部分MCS 第二部分、MCS 第三部分)了解更多关于该技术的细节。**

为了创建 MCS,您必须执行以下三(或四)个基本步骤:

1.建立数学模型:定义将输入变量与输出变量相关联的适当公式或方程。习惯上,MCS 涉及的数学模型从基本的商业公式到复杂的科学方程。

2.确定输入值:当我们处理随机变量时,我们必须确定它们的概率分布(将历史或经验数据拟合到相应的理论或经验分布)。

3.为每个随机输入变量创建一个非常大的数据集。如本文和之前文章所述,通过对算法进行适当编码,可以绕过这一步。

4.使用大量随机输入数据复制模拟,以获得输出或结果变量的随机值。使用统计方法计算结果的一些描述性统计度量(均值、中值、置信区间、偏度、峰度)。使用图表库分析输出,特别是绘制直方图来显示随机输出的基本频率分布。

根据收益率选择产品

蒙特卡洛模拟的另一个实际应用包括根据不同产品相应的回报率在它们之间进行选择。回报率是特定投资在特定时期内的净收益或净损失。

假设我们有可能只生产三种产品中的一种。我们知道每种产品的售价、年销售额和成本,以及各自的初始投资。

以下是计算收益率(RoR)的经典公式:

然而,我们知道我们的成本和销售预测并不准确;有一些随机组件可以修改它们的值。因此,在做出决策之前,开发一个蒙特卡罗模拟来帮助确定最佳解决方案是很方便的。

基于历史数据,我们假设销售额正态分布,标准差等于预测年值的 10%。我们还假设年度成本均匀分布在预测年度价值的+10%和-10%之间。

现在,我们可以使用以下 Python 代码进行计算:

首先,我们导入了几个 Python 库:

[@author](http://twitter.com/author): darwt
"""# Import Modules

import numpy  as npfrom scipy import stats
from scipy.stats import semimport matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
from prettytable import PrettyTable

your_path = 'your_path'

在初始化模块中,我们指出了产品销售价格、预测的年销售额、预测的年成本以及相应的初始投资。我们还指出了正态分布和均匀分布的相应参数。基于几次试运行,我们决定对生产运行使用 3000 次复制。最终置信区间的置信水平为 95%。

# initialization module

Number_of_Prods = 3Prod_Text          = ["Prod. 1","Prod. 2", "Prod. 3"]
Prod_Selling_Price = [54,12,5100] 
Prod_Yearly_Sales  = [10000, 500000, 200]
Prod_Yearly_Cost   = [450000,5700000,750000] 
Prod_Init_Invest   = [960000,2400000,1500000]Sales_std_dev = 0.1
Cost_unif_scale = 0.2Number_of_Replications = 3000
confidence = 0.95

MCS 背后的逻辑在下面几行代码中描述。我们在这些方面发展了重复随机抽样。每个样品的回报率( ror )用前面指出的公式计算。

list_of_rors = []
for j in range(Number_of_Prods):
    list_of_rors.append([])
    for run in range(Number_of_Replications):
        cost = np.random.uniform(Prod_Yearly_Cost[j]*(1-Cost_unif_scale/2), Prod_Yearly_Cost[j]*(1+Cost_unif_scale/2), 1)
        sale = np.random.normal(loc= Prod_Yearly_Sales[j], scale =Prod_Yearly_Sales[j]*Sales_std_dev, size =1)
        ror  = round(float((Prod_Selling_Price[j] * sale - cost)/ Prod_Init_Invest[j]),4)

        list_of_rors[j].append(ror)

我们使用 NumPy 和 SciPy 来计算经典的描述性统计量。然后,我们使用库 PrettyTable 来打印每个产品的统计报告。

 media = round(float(np.mean(list_of_rors[j])),3)
    stand = round(float(np.std(list_of_rors[j])),3)
    var   = round(float(np.var(list_of_rors[j])),3) 
    std_error = round(float(sem(list_of_rors[j])),3)

    median = round(float(np.median(list_of_rors[j])),3)
    skew   = round(float(stats.skew(list_of_rors[j])),4)
    kurt   = round(float(stats.kurtosis(list_of_rors[j])),4)

    dof  = Number_of_Replications - 1    
    t_crit = np.abs(stats.t.ppf((1-confidence)/2,dof))

    half_width=round(stand*t_crit/np.sqrt(Number_of_Replications),4)  
    inf = media - half_width
    sup = media + half_width  

    inf = round(float(inf),4)
    sup = round(float(sup),4)

    t = PrettyTable(['Statistic', 'Value'])
    t.add_row(['Product', j+1])
    t.add_row(['Mean', media])
    t.add_row(['Median', median])
    t.add_row(['Variance', var])
    t.add_row(['Stand Dev', stand])
    t.add_row(['Skewness', skew])
    t.add_row(['Kurtosis', kurt])
    t.add_row(['Half Width', half_width])
    t.add_row(['CI inf', inf])
    t.add_row(['CI sup', sup])

    print(t)

最后,我们用 Matplotlib 编码了一个重叠阶梯直方图,以显示三种产品的 RoR 的频率分布:

# Overlapping Step Histogramsn_bins = 20
list_of_colors = ['red', 'darkblue', 'green']fig, ax = plt.subplots(figsize=(8, 6))
ax.set_title('Frequency Chart')
ax.set_ylabel('Counts')
ax.set_xlabel('U$S')
ax.grid(axis = 'y')for j in range(Number_of_Prods):
    ax.hist(list_of_rors[j],  histtype ='step', 
            bins= n_bins, stacked = True, 
            fill = False, edgecolor= list_of_colors[j],
            density = False)# create legends
cmap = plt.get_cmap('jet')
low =  cmap(0.5)
medium=cmap(0.25)
high = cmap(0.8)handles = [Rectangle((0,0),1,1,color=c,ec="k") for c in [low,medium,high]]
labels  = Prod_Text
plt.legend(handles, labels)

plt.savefig(your_path +'MC4',bbox_inches='tight', dpi=150)
plt.show()

输出分析

每种产品我们复制了 3000 份。我们将为每个样本计算的 RoR 添加到列表 list_of_rors[j] 中。然后,我们计算了经典的统计度量,并用相应的统计报告显示出来:

作者用 PrettyTable 做的。

图 1 将每种产品的频率分布显示为重叠的阶梯直方图。请记住,重叠直方图用于比较连续变量在两个或更多类别中的频率分布。强烈建议使用阶跃直方图同时比较两个以上的频率分布,以避免图表混乱。

图 1:作者用 Matplotlib 做的。

现在是你做决定的时候了:如果你是一个风险承受能力强的投资者,产品 2 为你提供了最高的正回报率,但也很有可能带来重大损失。另一方面,如果你是一个规避风险的投资者,产品 1 为你提供了一个相对较低的回报率,但物质损失的可能性最小。

这是如何使用蒙特卡罗模拟来预测具有随机组件的真实世界系统的可能结果范围的另一个例子。

不要忘记给小费,尤其是当你把文章添加到列表中的时候。

蒙特 卡罗模拟

原文:https://towardsdatascience.com/monte-carlo-simulation-7c198d01c531

第 3 部分:直方图&密度图

大英图书馆Unsplash 上拍摄的照片

蒙特卡洛模拟 (MCS)是一种抽样实验,其目的是估计取决于一个或多个随机输入变量的感兴趣的量的分布。

MCS 的开发包括三个基本步骤:

1.建立一个预测模型,明确识别因变量(结果、未知的关注量、待评估的函数)和自变量(随机输入变量)。

2.确定输入变量的概率分布。标准程序包括分析经验或历史数据,并使数据符合特定的分布。当这些数据不可用时,选择一个合适的概率分布并明智地选择其参数。

3.多次重复模拟,获得输出或因变量的随机值。使用统计方法计算一些描述性的统计指标,并绘制图表,如结果变量的直方图或密度图。

正如在之前的一篇文章中所指出的,我们可以使用蒙特卡洛模拟来定量说明业务或运营管理任务中的风险。

机器故障&修理

蒙特卡洛模拟的实际应用包括制造操作,其中机器故障之间的时间和它们相应的修理时间是随机变量。

假设一个大工厂,其中一个处理单元每年 335 天每天 24 小时运行(30 天用于人员休假和年度维护)。管理层希望购买另一个处理单元来生产另一个最终产品。他们知道这样的处理单元会导致随机的机械故障,这需要非常熟练的维修团队。因此,每一次故障都代表着一笔重要的成本。

管理层决定进行蒙特卡洛模拟,从数字上评估与新项目相关的风险。

当前处理单元的历史数据显示,机器故障间隔时间具有以下经验分布:

修理这种处理单元所需的时间具有以下经验分布:

用于蒙特卡洛模拟的 Python 代码

下面的 Python 代码允许我们开发一个 MCS 来对新投资所涉及的风险进行数字评估:

首先,我们导入了几个 Python 库:

NumPy:Python 编程语言的一个库,为创建大型多维向量和矩阵提供支持,并提供大量高级数学函数来操作它们。

SciPy:Python 的免费开源库。基于 NumPy,包含统计、线性代数、最优化、插值、信号处理和其他科学和工程常用方法的模块。

MatplotlibSeaborn 是用于制图的传统 Python 库。

PrettyTable 是一个 Python 库,用于以视觉上吸引人的 ASCII 表格格式轻松显示表格数据。

[@author](http://twitter.com/author): darwt
"""# Import Modules
import numpy  as npfrom scipy import stats
from scipy.stats import semimport matplotlib.pyplot as plt
import seaborn as sns 
from prettytable import PrettyTable

your_path = 'your path'

现在我们指出每年的运行小时数、维修团队产生的每小时成本、故障间隔运行小时数的经验分布以及维修所需的经验分布。

# initialization module

Operating_hours_year = 335 * 24Cost_LostTime_hour = 55.50    
# .........................................
# historical data of hours of operation between failures (HbF)
Hours_between_Failures = [200, 400, 600, 800, 1000, 1200, 1400]# discrete probabilities for hours of operation between failures
probabilities_HBF = [0.1, 0.2, 0.1, 0.2, 0.3, 0.05, 0.05]# historical data of hours needed to repair (TTR)
Time_to_Repair = [24, 48, 72, 96, 120]probabilities_TTR =[0.10, 0.20, 0.25, 0.25, 0.20]

我们设置了大量的重复,因此我们的样本平均值将非常接近预期值。我们还定义了置信区间(CI)的置信水平。

Number_of_Replications = 2000confidence = 0.95                      ## selected by the analyst# Lists to be filled during calculations
list_of_HBF,    list_of_TTR = [], []
list_Lost_Time, list_cost_TTR = [], []
list_Hours_Prod = []

MCS 背后的逻辑在下面几行代码中描述。我们有两个循环:外部循环(用于在范围(复制次数)内运行)与运行或复制次数相关;内环(而 acum _ time<= Operating _ hours _ year)与年度生产和故障有关。我们用了两次 nprandom.choice 根据相应的概率,从两个经验分布中产生一个大小为 1 的随机样本。

for run in range(Number_of_Replications):
    acum_time, acum_hbf, acum_ttr = 0, 0, 0
    while acum_time <= Operating_hours_year:
        HBF = np.random.choice(Hours_between_Failures, 
                               1, p=list(probabilities_HBF)) 
        TTR = np.random.choice(Time_to_Repair,
                               1, p=list(probabilities_TTR))
        acum_time += (HBF + TTR)
        acum_hbf  += HBF
        acum_ttr  +=TTR

    Total_HBF = round(float(acum_hbf),4)
    Total_TTR = round(float(acum_ttr),4)
    Perc_lost_Time = round(float(Total_TTR/Total_HBF * 100),2)
    Hours_Prod = Operating_hours_year - Total_TTR
    Cost_TTR   = Total_TTR * Cost_LostTime_hour

    list_of_HBF.append(Total_HBF)
    list_of_TTR.append(Total_TTR) 
    list_Lost_Time.append(Perc_lost_Time)

    list_cost_TTR.append(Cost_TTR)
    list_Hours_Prod.append(Hours_Prod)

我们使用 NumPy 和 SciPy 来计算关键的描述性统计指标,特别是我们的主要结果Cost _ TTR = Total _ TTR * Cost _ lost time _ hour的百分位表。此输出变量表示与机器故障和相应维修时间相关的成本。媒体代表了这种成本的预期值。

media =  round(np.mean(list_cost_TTR),2)
median = round(np.median(list_cost_TTR),2)
var   = round(np.var(list_cost_TTR), 2) 
stand = round(np.std(list_cost_TTR),2)std_error = round(sem(list_cost_TTR),2)skew   = round(stats.skew(list_cost_TTR),2)
kurt   = round(stats.kurtosis(list_cost_TTR),2)minimum = round(np.min(list_cost_TTR),2)
maximum = round(np.max(list_cost_TTR),2)dof  = Number_of_Replications - 1    
t_crit = np.abs(stats.t.ppf((1-confidence)/2,dof))half_width =  round(stand *t_crit/np.sqrt(Number_of_Replications),2)  
inf = media - half_width
sup = media + half_width  

inf = round(float(inf),2)
sup = round(float(sup),2)

我们将 PrettyTable 用于统计报告和百分比报告:

t = PrettyTable(['Statistic', 'Value'])
t.add_row(['Trials', Number_of_Replications])
t.add_row(['Mean', media])
t.add_row(['Median', median])
t.add_row(['Variance', var])
t.add_row(['Stand Dev', stand])
t.add_row(['Skewness', skew])
t.add_row(['Kurtosis', kurt])
t.add_row(['Half Width', half_width])
t.add_row(['CI inf', inf])
t.add_row(['CI sup', sup])
t.add_row(['Minimum', minimum])
t.add_row(['Maximum', maximum])
print(t)percents = np.percentile(list_cost_TTR, [10, 20, 30, 40, 50, 60, 70, 80, 90, 100])p = PrettyTable(['Percentile (%)', 'Lost Time Cost'])
p.add_row(['  10  ', percents[0]])
p.add_row(['  20  ', percents[1]])
p.add_row(['  30  ', percents[2]])
p.add_row(['  40  ', percents[3]])
p.add_row(['  50  ', percents[4]])
p.add_row(['  60  ', percents[5]])
p.add_row(['  70  ', percents[6]])
p.add_row(['  80  ', percents[7]])
p.add_row(['  90  ', percents[8]])
p.add_row([' 100  ', percents[9]])
print(p)

最后,我们使用 Matplotlib 和 Seaborn 制作图表:一个直方图显示了每年预期损失时间成本的潜在频率分布,一个密度图显示了由于故障和维修造成的损失时间百分比的概率密度函数。

# Lost Time Cost Histogramn_bins = 20fig, ax = plt.subplots(figsize=(8, 6))
ax.hist(list_cost_TTR,  histtype ='bar', bins=20, color = 'c',
         edgecolor='k', alpha=0.65, density = False)  # density=False show countsax.axvline(media,  color='g', linestyle='dashed', linewidth=3)
ax.axvline(median, color='r', linestyle='dashed', linewidth=3)

ax.set_title('Frequency Chart')
ax.set_ylabel('Counts')
ax.set_xlabel('U$S')
ax.grid(axis = 'y')plt.savefig(your_path +'Expected Lost Time Cost per Year',
            bbox_inches='tight', dpi=150)
plt.show()# Percent Lost Time Cost Density Plotsns.distplot(list_Lost_Time, hist=False, kde=True, 
             bins=int(180/5), color = 'lightblue', 
             axlabel = 'Percent Lost Time (%)',
             hist_kws={'edgecolor':'black'},
             kde_kws = {'shade': True, 'linewidth': 3})plt.savefig(your_path + 'Percent Lost Time' , 
            bbox_inches='tight', dpi=150)          
plt.show()

输出分析

我们做了 2000 次复制,附加了几个列表。然后,我们计算了与机器故障和相应维修时间相关的成本的关键描述性统计指标。下表显示了统计报告:

表 1,作者用 PrettyTable 做的。

表 1 显示平均值和中值之间的差异很小,半宽度区间几乎可以忽略不计。结果分布是正偏的,峰度可以忽略不计。

表 2 显示了百分位数报告。我们可以看到,损失的时间成本超过 5000 万美元的可能性超过 20%。

表 2,作者用 PrettyTable 做的。

图 1 显示了我们的结果的分布(损失时间成本)作为一个有 20 个箱的直方图。平均值和中值分别用绿色和红色垂直线表示。像往常一样,直方图提供了我们感兴趣的量的分布的可视化表示:它的位置、分布、偏斜度和峰度。我们声称期望值(95%置信水平)在 44.000 美元左右。

图 1,作者用 Matplotlib 做的。

图 2 显示了输出变量百分比损失时间的密度图。请记住,密度图的关键思想是消除直方图的锯齿特征,从而更好地显示分布形状。

显然,分布是单峰的。我们声称,由于机器故障,平均会损失 10%的工作时间。

图 2,作者和 Seaborn 一起做的。

蒙特卡洛模拟是决策者和项目经理用来定量评估风险影响的计算技术。任何模拟研究的基本步骤是对输出数据进行适当的统计分析。统计报告和分布图(直方图和密度图)对于评估风险暴露水平是强制性的。

不要忘记给小费,尤其是当你把文章添加到列表中的时候。

蒙特 卡罗模拟

原文:https://towardsdatascience.com/monte-carlo-simulation-8db846f3d8ed

第二部分:风险分析

梅里达勒Unsplash 上的照片

在本系列的上一篇文章中,我们将蒙特卡罗模拟 (MCS)定义为“一个抽样实验,其目的是估计取决于一个或多个随机输入变量的感兴趣的量的分布”。我们为研究中的问题建立一个数学模型,并通过重复采样来估计其结果。该方法提供了模拟结果的分布,从中可以很容易地获得图表和汇总统计数据。

另一方面,风险被定义为不良结果发生的概率。定量风险分析是用数字资源估计任何项目风险的数值程序。

因此,如果蒙特卡洛模拟提供了一系列可能的结果及其相应的概率,我们就可以用它来量化决策中的风险。

我们将举例说明蒙特卡罗模拟来说明投资项目中的风险。

投资项目的风险分析

大型投资项目、股票投资组合的回报、规划建设时间表以及类似项目都是必须进行风险分析的典型情况。我们必须能够回答这样的问题:成本超过收益的可能性有多大?我们遭受财务损失的可能性有多大?这个项目按时完成的可能性有多大?在我们投资第一美元之前,我们应该对这些问题进行评估。

让我们假设实施一个重要投资项目的决定权在我们手中。该项目有六个成本组成部分:国际贸易商品、当地贸易商品、劳动力、设备和机械、土地征用、税收和财务成本。这些组成部分的总和就是项目的最终成本。

我们的首要任务是获得每项成本的最可能值的估计值。表 1 显示了每个成本组成部分最可能的值。相应的总成本为 54.56 百万美元

表 1:最可能的值

当然,如果我们完全确定成本计算的准确性,剩下的唯一任务就是将六个组成部分相加以显示最终成本。但当项目达到一定规模时,这种情况在实践中很少出现。在一个真实的项目中,每一个组成部分都会表现出一定程度的随机性,我们必须进行风险分析 来量化不良结果的概率。

在缺乏严格数据的情况下,习惯上使用三角分布作为未知输入分布的粗略近似值。三角分布只取决于三个参数:最小值(a)、最大可能性和最大值(b)。这种分布简单、灵活,并且可以呈现各种形状。

三角形分布的参数和公式,来源:维基百科1

三角形分布的 PDF 和 CDF 图表,来源:维基百科1

表 2 显示了每个成本构成的参数值。

表 2:每个组件的参数

MCS 的 Python 代码

以下 Python 代码允许我们开发一个带有统计摘要和频率直方图的 MCS:

首先,我们将 Numpy 库作为 np 导入,将 Scipy 用于一些统计计算,将 Matplotlib 和 PrettyTable 用于最终输出。

[@author](http://twitter.com/author): darwt
"""# Import Modulesimport numpy  as npfrom scipy import stats
from scipy.stats import sem
from scipy.stats import percentileofscoreimport matplotlib.pyplot as plt
from prettytable import PrettyTable

your_path = your path 

以下三个列表描述了三角形分布的参数:

minimum   = [13.80, 4.80, 4.14, 15.00, 3.29, 1.44]
most_like = [16.25, 5.33, 6.44, 19.23, 4.43, 2.88]
maximum   = [17.20, 7.20, 7.45, 22.50, 6.97, 4.32]Sum_most_like = sum(most_like)

Number_of_Component_Costs = 6

为了用 MCS 获得有效的结果,我们需要进行大量的重复。我们还定义了置信区间(CI)的置信水平。

Number_of_Replications = 3000confidence = 0.95

MCS 背后的逻辑在以下代码行中描述:

list_of_costs = []
for run in range(Number_of_Replications):
    acum_cost = 0
    for i in range(Number_of_Component_Costs):
        cost = np.random.triangular(minimum[i], 
                                    most_like[i],  maximum[i], 1)
        acum_cost +=cost

    Total_Cost = round(float(acum_cost),1)

    list_of_costs.append(Total_Cost)

我们使用 Numpy 和 Scipy 获得关键描述性统计指标的汇总,特别是一个百分位表。

media = round(np.mean(list_of_costs),2)
stand = round(np.std(list_of_costs),2)
var   = round(np.var(list_of_costs),2) 
std_error = round(sem(list_of_costs),2)median = round(np.median(list_of_costs),2)
skew   = round(stats.skew(list_of_costs),2)
kurt   = round(stats.kurtosis(list_of_costs),2)dof  = Number_of_Replications - 1    
t_crit = np.abs(stats.t.ppf((1-confidence)/2,dof))half_width =  round(stand *t_crit/np.sqrt(Number_of_Replications),2)  
inf = media - half_width
sup = media + half_width  

inf = round(float(inf),2)
sup = round(float(sup),2)t = PrettyTable(['Statistic', 'Value'])
t.add_row(['Trials', Number_of_Replications])
t.add_row(['Mean', media])
t.add_row(['Median', median])
t.add_row(['Variance', var])
t.add_row(['Stand Dev', stand])
t.add_row(['Skewness', skew])
t.add_row(['Kurtosis', kurt])
t.add_row(['Half Width', half_width])
t.add_row(['CI inf', inf])
t.add_row(['CI sup', sup])
print(t)percents = np.percentile(list_of_costs,[10, 20, 30, 40, 50, 
                                        60, 70, 80, 90, 100])p = PrettyTable(['Percentile (%)', 'Cost'])
p.add_row(['  10  ', percents[0]])
p.add_row(['  20  ', percents[1]])
p.add_row(['  30  ', percents[2]])
p.add_row(['  40  ', percents[3]])
p.add_row(['  50  ', percents[4]])
p.add_row(['  60  ', percents[5]])
p.add_row(['  70  ', percents[6]])
p.add_row(['  80  ', percents[7]])
p.add_row(['  90  ', percents[8]])
p.add_row([' 100  ', percents[9]])
print(p)

最后,我们绘制了两个图表:结果变量(总成本)的直方图,包括一条指示媒体的绿色垂直线,以及另一条指示最可能值的垂直线(红色)。第二个图表是累积直方图,显示了最终成本低于最可能值的概率。

percentile_of_most_like = round(percentileofscore(list_of_costs, Sum_most_like),2)n_bins = 20fig, ax = plt.subplots(figsize=(8, 6))
ax.hist(list_of_costs,  histtype ='bar', bins=20, color = 'c',
        edgecolor='k', alpha=0.65,
        density = True)  # density=False show countsax.axvline(media, color='g', linestyle='dashed', linewidth=3)
ax.axvline(Sum_most_like, color='r', 
           linestyle='dashed', linewidth=3)
ax.text(48,0.185, 'Mean - - -', color = 'green'  )
ax.text(48,0.155, 'Most Like - - -', color = 'red'  )ax.set_title('Frequency Chart')
ax.set_ylabel('Probability')
ax.set_xlabel('Total Cost (MM*1000 U$S)')
ax.grid(axis = 'y')plt.show()fig, ax = plt.subplots(figsize=(8, 6))
n, bins, patches = ax.hist(list_of_costs, n_bins,
                           density=True, histtype='bar',
                           cumulative=True, edgecolor='k',
                           alpha = 0.65)
ax.hlines(percentile_of_most_like/100, 46, Sum_most_like, 
          color='r', linestyle='dashed', linewidth=2)
ax.text(46 + 0.1, percentile_of_most_like/100 + 0.1, 
        'Cumulative Probability:' + str(percentile_of_most_like) +'%',rotation=360)ax.set_title('Cumulative Histogram')
ax.set_ylabel('Cumulative Probability')
ax.set_xlabel('Total Cost (MM*1000 U$S)')
ax.grid(axis = 'y')plt.show()

分析

我们进行了 3000 次复制,将每次复制的总成本附加到一个列表中(list _ of _ costs . append(Total _ Cost)。然后,我们计算了关键的描述性统计指标:

表 3:统计报告

表 3 显示,平均值和中值实际上具有相同的值,半宽度区间几乎可以忽略不计,并且分布具有零偏斜度和几乎为零峰度。

表 4:百分位报告

表 4 显示了百分比报告。我们认为总成本低于 5300 万美元的可能性等于或小于 30%。

图 1 显示了我们的结果的分布(总成本)作为一个有 20 个箱的直方图。平均成本和最可能成本分别用绿色和红色垂直线表示。请记住,直方图提供了数据集分布的可视化表示:数据的位置、分布、偏斜度和峰度。显然,分布的外观与统计报告中指示的值相匹配。

图 1:作者用 Matplotlib 做的频率表

涉及风险的问题可以用累积直方图来回答(图 2)。红色水平线对应最可能值的百分位数。超出我们预测成本的概率为 44.1%(100–55.9)。现在,我们可以用数字来评估与我们投资项目相关的风险。

图 2:作者用 Matplotlib 制作的累积直方图

如果开发得当,蒙特卡罗模拟是一种相对简单的数值技术,可以对极具挑战性的项目进行风险分析。

不要忘记给作者小费,尤其是当你把这篇文章添加到列表中的时候。

参考

蒙特卡罗模拟——实用指南

原文:https://towardsdatascience.com/monte-carlo-simulation-a-practical-guide-85da45597f0e

一种通用的参数估计方法。Python 编程语言中的示例性实现。

图片由 15299 来自 Pixabay 作者修改

蒙特卡洛模拟(或方法)是一种概率数值技术,用于估计给定的不确定(随机)过程的结果。这意味着这是一种模拟无法隐式建模的事件的方法。当我们的过程中有随机变量时,这通常是一种情况。

在本文中,我将简要介绍这种技术的背景,展示实现这种技术需要遵循的步骤,最后,将给出两个使用 Python 编程语言中的蒙特卡罗解决问题的例子。

  1. 一点历史

蒙特卡洛模拟和许多其他数值方法一样,是在现代计算机出现之前由两位数学家发明的——它是在第二次世界大战期间开发的:斯塔尼斯瓦夫·乌拉姆约翰·冯·诺依曼。当时,他们都参与了曼哈顿计划,他们想出了这种技术来模拟高浓缩铀中的连锁反应。简单地说,他们在模拟原子弹爆炸。

解决“中子扩散”模型太复杂,难以描述和明确解决,特别是要记住他们只有 IBM 穿孔卡片机或后来的一台叫做 ENIAC 的计算机。在住院期间,斯坦尼斯瓦夫·乌拉姆靠打牌来打发无聊的时光,然后他有了一个新的想法。回到工作岗位后,他与约翰·冯·诺依曼实验室的一位同事分享了他的新想法。一种新的求解方法的发展得到了一个代号“蒙特卡洛”。方法以随机抽样和统计学为基础。多亏了它,两位数学家都能够加快计算过程,做出令人难以置信的好预测,并为项目提供当时非常需要的有用结果。

斯塔尼斯瓦夫·乌拉姆(左)和约翰·冯·诺依曼(右)在曼哈顿计划中一起工作;图片由 AlexAntropov86 提供,来自 PixabayWikimedia (鸣谢:LANL)由作者修改

在洛斯阿拉莫斯国家实验室工作期间,斯坦尼斯瓦夫·乌拉姆于 1949 年发表了第一份描述蒙特卡洛模拟的非机密文件。

2。现在申请

目前,由于易于实现和可用的高计算能力,该技术被广泛用于各种行业。让我们看一些有文档记录的用例。

健康:

金融:

  • "使用随机准蒙特卡罗对风险条件值的敏感性估计"
  • “金融中的复杂系统:首次通过时间密度函数的蒙特卡罗评估”

生产:

运输:

工程/科学:

3。蒙特卡洛方法的支柱

蒙特卡洛模拟背后的核心概念是从一组给定的概率分布中进行多次随机抽样。这些可以是任何类型,例如:正常的,连续的,三角形,贝塔,伽玛,你能想到的。

要使用这种技术,您必须遵循四个主要步骤:

  1. 确定流程的所有输入组件以及它们如何相互作用,例如,它们是相加还是相减?
  2. 定义分布的参数。
  3. 从每个分布中取样,并根据第 1 点整合结果。
  4. 想重复多少次就重复多少次。

在此模拟的运行过程中,您的最终参数(例如成本或风险)将向正态分布收敛,即使源分布可能不同。这就是中心极限定理的效果,也是这种技术在各种行业大受欢迎的原因之一。

4。Python 实现—基础知识

蒙特卡罗模拟可以使用任何编程语言轻松实现。在这种情况下,我们将使用 Python。NumPy 库在这里非常方便,因为它实现了多个最流行的概率分布。例如:

5。示例 1

让我们假设我们有一个由 3 个阶段(X1,X2,X3)构成的过程。每一个都有一个平均持续时间(5、10 和 15 分钟),它按照正态分布变化,我们知道它们的方差(都是 1 分钟)。

带参数的正态分布的符号

流程组件;作者图片

我们想知道这个过程超过 34 分钟的概率是多少?

这个例子是如此的简单,以至于我们可以手动解决它,然后验证蒙特卡罗结果。

我们知道所有的单个组件,所以让我们定义它们之间的关系(它是累加的):

现在我们可以开始编码了。单分量可以用一个短函数来表示:

下面显示的蒙特卡罗模拟代码使用这个函数作为一个基本块。这个用例的迭代次数被设置为 10 000,但是您可以更改它。代码的最后一部分检查退出 34 分钟限制的概率(再次使用抽样技术)。

运行以下代码后,我们得到以下答案,但每次运行代码时答案都会有所不同:

Probability of exceeding the time limit:  1.035 %

现在我们可以绘制估计参数(时间)的直方图。我们清楚地看到它遵循正态分布。

总时间直方图;作者图片

让我们用手工计算来验证我们的结果。

因此,总时间遵循带参数的正态方程:

为了计算概率,我们必须先找到 z 值。

我们现在从 z 分数表的中读取 p 值。右尾概率我们计算为:

正如你所记得的,我们的蒙特卡罗模拟给出了 1.05%的结果,这是非常接近的。

6。例 2

在这个例子中,假设我们想要在一个给定宽度的容器中组装三个积木。下图显示了标称尺寸。我们看到,通过设计,名义间隙为 0.5 毫米

容器内的三个块;作者图片

然而,由于技术原因,这三个块和容器的实际尺寸可以变化。为了便于演示,让我们假设这些变化都不符合正态分布。三个块将遵循下图所示的三角形分布,容器的尺寸分布将遵循+/-0.1 毫米范围内的均匀分布

三个区块 X1、X2 和 X3 的维度分布;作者图片

现在,通过简单地计算极端值,我们可以看到,在最糟糕的情况下,砖块有 17 毫米,集装箱的宽度只有 16.4 毫米,这意味着,在这种情况下,我们无法将它们全部放在一起。

问题是:我们无法将所有的块装进一个容器的概率是多少?

在这种情况下,块之间的关系如下所示:

通过修改前面的代码,我们获得了一个采样三角分布的函数。同样,我们可以获得均匀分布的采样函数。

MC 模拟的修改核心代码;

运行上述代码后,我们按以下顺序得到答案:

Probability of not fitting the blocks:  5.601 %

在检查了平均值和标准偏差之后,我们可以说关于预期间隙尺寸的更多信息:

The mean gap width will be 0.33mm with a standard deviation of 0.2mm.

现在,让我们绘制估计参数(间隙宽度)的直方图,以显示它遵循正态分布,即使没有任何输入分布属于这种类型。

遵循正态分布的间隙宽度直方图;作者图片

您现在可能想知道随着样本数量的增加,结果会如何变化。根据样本大小(从 100 到 7000 个样本),查看下图,该图显示了 95%置信区间的间隙宽度估计值:

具有 95%置信区间的估计间隙宽度;作者图片

从该图中可以明显看出,估计值的平均值没有显著变化,但是分布随着样本数量的增加而减小。这意味着,随着新一轮的模拟,更大的样本给你更小的结果传播。然而,在某些时候添加更多的样本不再有帮助。

7。总结

正如你所看到的,蒙特卡洛基本上是一个非常简单而又非常强大的想法。读完这篇文章后,我希望你理解这个方法的核心概念,何时使用它,以及如何用 Python 编程语言实现它。

蒙特 卡罗模拟

原文:https://towardsdatascience.com/monte-carlo-simulation-bf31bb78d39c

第 6 部分:比较备选方案

西蒙·伯格Unsplash 上拍摄的照片

在本系列的第一篇第二篇文章中,我们将蒙特卡罗模拟 (MCS)定义为“一个抽样实验,其目的是估计取决于一个或多个随机输入变量的感兴趣的量的分布”。这是一种定量技术,其现代发展是基于第二次世界大战期间曼哈顿计划中科学家约翰·冯·诺依曼、斯塔尼斯拉夫·乌拉姆和尼可拉斯·大都会研制第一颗原子弹的工作。从概念上讲,它是一种基于统计学的方法,用于确定基于重复随机抽样的多个结果的可能性。

一般来说,模拟技术,尤其是 MCS,在比较备选方案方面表现出色。对被研究系统的许多假设进行建模的能力使它们成为最强大、最灵活的运筹学工具。

任何模拟研究的最重要的目的之一是使用描述系统性能的定量统计方法来比较系统的选择。最终目标是根据决策问题确定哪个场景表现最好,并深入了解系统的行为。

因此,适当的模拟方法必须包括输入的彻底选择、客观和可测量的结果度量,以及对备选方案的最终分析和排序。

我们将通过运营管理模型中的一个经典例子来说明蒙特卡罗模拟的这些思想和概念:新闻供应商库存问题。

需求对广告敏感的报童问题

新闻供应商库存问题经常出现在商业和运筹学决策问题中。仿照所谓的报摊模式。它试图回答下面的问题:对于易腐的单一产品订购多少,其特点是随机需求和固定售价。销售商希望最大化他/她的利润,但是,由于产品必须在销售期之前可用,他/她必须在观察实际产品需求之前决定订单的大小。

如本系列第一篇所述,问题的大致设置如下:

  • 有一个卖主、一个供应商和客户。
  • 未来一段时间的需求是不确定的(单周期模型)。
  • 有一个决策变量(库存水平 Q ),必须对其进行计算,以确定利润最大化的决策(最优决策)。
  • 不确定的客户需求由随机变量 d 及其对应的概率密度函数 f(d) 和累积分布函数 F(d)表示。
  • 卖方完全知道单位固定初始成本(c)、单位销售价格( s )和残值( u )。残值是产品在使用寿命结束时的估计价值。习惯性的商业情况表明,销售价格高于购买成本,这反过来又高于残值。
  • 高于库存水平的每一个需求单位都作为销售损失掉了;低于库存水平的每个需求单位都以残值出售。如果 Q > d,(Q — d) 单位剩余,必须由供应商回收;否则,(d-Q)单位代表销售损失。

经典报童问题的几个扩展已经被开发出来。其中之一涉及广告敏感需求:客户需求如何受到广告和营销努力的影响。

该扩展将客户需求建模为营销工作的函数。一定数量的潜在顾客收到了广告,但是做出购买决定的人数将取决于一个名为广告效果的因素。另一方面,所谓的广告强度与营销努力所针对的潜在客户数量有关。此参数将根据以下公式确定广告和营销成本:

由作者制作

其中α是常数。

因此,我们的具有广告敏感需求的报童模型包括以下等式:

1) 成本 _ 每订单= c * Q

2)**Revenue _ per _ Sales = s * minimum(d,Q)

3)**Revenue _ per _ Salvage = u * maximum(0,Q — d)

4) 广告成本= C(AI)

利润等式是:

利润=每次销售收入+每次回收收入-每次订单成本-广告成本

因此,利润是订货量(库存水平)和客户需求(广告强度)的函数。我们将使用蒙特卡罗模拟来估计订单数量,使不同广告强度值的预期利润最大化。该计算将通过对给定数据和场景进行采样来执行。订单数量和广告强度的每一个组合都是一个场景。

一个假设的报童问题的蒙特卡罗模拟

我们将使用来自 Negahban 1的数据模拟一个假设的报童问题。

我们公司想知道从供应商那里订购多少产品以便在下一季销售。

供应商为我们提供了四种选择:

备选 1: 500 件,每件成本为 200 美元

备选 2: 1000 件,每件成本为 190 美元

备选 3: 1500 件,每件成本为 175 美元

备选 4: 2000 件,每件 165 美元

我们为每个备选方案定义了相应的销售价格,并假设最初的残值相当于预测销售价值的 50%。

最后,我们选择了六种不同的广告强度(5%、10%、15%、20%、25%和 30%)。下表显示了代表不同强度随机需求的正态分布locscale 参数(平均值和标准偏差)的相应值。

由作者制作

我们有四个供应商选项和六个广告强度:这转化为 24 个场景。我们将为每个场景复制 3000 次运行,因此我们的主要点估计量(利润)的置信区间的半宽度对于我们的决策问题应该是合理的。

我们假设的新闻供应商问题的 python 代码如下:

[@author](http://twitter.com/author): darwt# Import Modules
import pandas as pd
import numpy  as npfrom scipy import stats
from scipy.stats import semimport matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties
from prettytable import PrettyTable

your_path = 'your_path'#..................................................................
# initialization module

list_Price_Per_Unit   = [330, 300, 270, 230]
list_Cost_Per_Unit    = [200, 190, 175, 165]multiplier = 0.50
list_Salvage_Per_Unit = list(np.array(list_Price_Per_Unit) * multiplier)list_advert_intens = [5, 10, 15, 20, 25, 30]
list_Mean_Normal = [288, 938, 1306, 1691, 1940, 2243]
list_Std_Normal  = [211, 262, 281,  308,  294,  289]
length1 = len(list_advert_intens)list_of_orders = [500,1000,1500,2000]
length2 = len(list_of_orders)alfa = 0.5 
confidence = 0.95                      ## selected by the analyst 
# .......................................................
column_labels = ["Advert.Intens.", "Order Quantity",
                 "Mean","Std.Dev","Var.","Std. Error", 
                 "Median", "Skewness", "Kurtosis",                    
                 "CI Half Width", "CI LL", 'CI UL']df = pd.DataFrame(columns=column_labels)

下面几行代码描述了这个特定报刊杂志模型的 MCS 背后的逻辑:

listM1, listM2, listM3, listM4, listM5, listM6 =[],[],[], [], [], [] 
list_of_Means = [listM1, listM2, listM3, listM4, listM5, listM6]Number_of_Replications = 3000for i in range(length1):

    for j in range(length2):
        list_of_profits = []
        for run in range(Number_of_Replications):

            Qty_Demanded = np.random.normal(                                          loc=list_Mean_Normal[i],scale=list_Std_Normal[i],size=1) 

            if Qty_Demanded >= 0:
                Qty_Demanded = int(Qty_Demanded)
            else:
                Qty_Demanded = 0

            Qty_Ordered  = list_of_orders[j]

            Qty_Sold = np.minimum(Qty_Demanded, Qty_Ordered)
            Qty_Left = np.maximum(0, Qty_Ordered - Qty_Demanded)

            Revenue_per_Sales = Qty_Sold * list_Price_Per_Unit[j]            
            Revenue_per_Salvage= Qty_Left * list_Salvage_Per_Unit[j]

            Cost_per_Order =  Qty_Ordered * list_Cost_Per_Unit[j]

            Cost_per_Advert = alfa * np.log(-1/((list_advert_intens[i]/100)-1))            
            Cost_per_Advert = Cost_per_Advert * 100000

            Profit = Revenue_per_Sales + Revenue_per_Salvage - Cost_per_Order - Cost_per_Advert

            list_of_profits.append(Profit)

我们使用 Numpy 和 Scipy 获得了一组关键的描述性统计指标,尤其是利润的期望值:

 media = np.mean(list_of_profits)
        stand = np.std(list_of_profits)
        var   = np.var(list_of_profits) 
        std_error = sem(list_of_profits)

        median = np.median(list_of_profits)
        skew   = stats.skew(list_of_profits)
        kurt   = stats.kurtosis(list_of_profits)

        dof  = Number_of_Replications - 1    
        t_crit = np.abs(stats.t.ppf((1-confidence)/2,dof))

        half_width =  round(stand *t_crit/np.sqrt(Number_of_Replications),2)  
        inf = media - half_width
        sup = media + half_width  

        inf = round(float(inf),2)
        sup = round(float(sup),2)

        list_of_statistics = []
        list_of_statistics.append(list_advert_intens[i])
        list_of_statistics.append(Qty_Ordered)
        list_of_statistics.append(round(media,2))
        list_of_statistics.append(round(stand,2))
        list_of_statistics.append(round(var,2))
        list_of_statistics.append(round(float(std_error),2))

        list_of_statistics.append(round(median,2))
        list_of_statistics.append(round(float(skew),2))
        list_of_statistics.append(round(float(kurt),2))

        list_of_statistics.append(round(half_width,2))
        list_of_statistics.append(round(inf,2))
        list_of_statistics.append(round(sup,2))

        df.loc[len(df)] = list_of_statistics

        list_of_Means[i].append(round(media,2))

我们使用 Matplotlib 显示了一个 6 行 4 列的表格,显示了 24 种情况下的利润。

row_list = [str(x) + ' %'     for x in list_advert_intens]
col_list = [str(x) + ' Units' for x in list_of_orders]

subtitle_text = 'Salvage Value equal to %s' %multiplier +
                ' of the price value'fig, ax = plt.subplots(1,1)
ax.axis('tight')
ax.axis('off')
profits_table = ax.table(cellText  =  list_of_Means, 
                         rowLabels = row_list,
                         colLabels = col_list,                      
                         rowColours =["skyblue"]*(length1),  
                         colColours =["cyan"]*length2, 
                         cellLoc='center', loc="center",
                         bbox = [0.1, 0, 1.9, 1.0])
ax.set_title(" Profits according to Order Quantity & Advertising Intensity",fontsize=18, y= 1.2 , pad = 4)plt.figtext(0.5, 0.95,
            subtitle_text,
            horizontalalignment='center',
            size= 12, style='italic',
            color='black'
           )profits_table.auto_set_font_size(False)
profits_table.set_fontsize(12)

for (row, col), cell in profits_table.get_celld().items():
  if (row == 0) or (col == -1):
      cell.set_text_props(
                       fontproperties=FontProperties(weight='bold'))plt.savefig(your_path +'Scenarios.png',bbox_inches='tight', dpi=150)
plt.show()

然后,我们使用 Matplotlib 绘制相同的数据,以实现更好的可视化:

fig, ax = plt.subplots()

for i in range(len(list_of_Means[0])):
    plt.plot(list_advert_intens,[pt[i] for pt in list_of_Means],
             label = list_of_orders[i],
             linestyle='--', marker = '*')
plt.legend()
plt.axhline(y = 0.0, color = 'k', linestyle = '-')

fig.suptitle('Monte Carlo Simulation',  fontsize=20)
plt.xlabel('Advertising Intensity (%)', fontsize=16)
plt.ylabel('Profit (U$S)', fontsize=16)
plt.xticks()plt.savefig(your_path +'ScenarioChart.png',
            bbox_inches='tight', dpi=150)
plt.show()

最后,我们将 PrettyTable 用于对应利润最大的场景的统计报告。

max_profit = df.loc[df["Mean"].idxmax()]

t = PrettyTable(['Statistic', 'Value'])
t.add_row(['Runs', Number_of_Replications])
t.add_row(['Advert.Intens.',max_profit['Advert.Intens.']])
t.add_row(['Order Quantity',max_profit['Order Quantity']])
t.add_row(['Mean', max_profit['Mean']])
t.add_row(['Median', max_profit['Median']])
t.add_row(['Variance', max_profit['Var.']])
t.add_row(['Stand. Error', max_profit['Std. Error']])
t.add_row(['Skewness', max_profit['Skewness']])
t.add_row(['Kurtosis', max_profit['Kurtosis']])
t.add_row(['Half Width',max_profit['CI Half Width']])
t.add_row(['CI inf', max_profit['CI LL']])
t.add_row(['CI sup', max_profit['CI UL']])

print(t)

分析

我们对 24 个场景中的每一个进行了 3000 次复制,将统计测量附加到数据帧上。表 1 显示了每个场景的平均利润。最佳的数字决策包括购买 1500 个单位,并以 25%的广告强度进行营销。

表 1,作者用 Matplotlib 做的。

图 1 显示了相同数据的线性图。图表显示,我们可以将广告强度降低到 20%,而不会严重损害我们的利润。

图 1,作者用 Matplotlib 做的。

表 2 显示了最大利润的置信区间的半宽度。置信区间的半宽度通常表示模拟研究中性能测量的估计精度。表中所示的半宽度符合我们的精度要求。

表 2,作者用 PrettyTable 做的。

最后,我们用两个不同的残值百分比重复了模拟:最初预测销售价值的 25%和 75 %。表 3 显示了新方案的平均利润(75%)。可以看出,我们的决策问题没有变化:我们必须购买 1500 台,并以 20%的广告强度进行营销努力。

表 3,作者用 Matplotlib 做的。

这是蒙特卡罗模拟的另一个实际实现,以非常简单的方式解决复杂的决策问题。毫无疑问,对于许多现实世界的系统来说,它是一种非常强大的建模、分析和计算性能指标的技术。

不要忘记给小费,尤其是当你把文章添加到列表中的时候。

1 Negahban,A .“广告和病毒式营销的报童问题的混合模拟框架”。2013 年冬季模拟会议录。R. Pasupathy、s-h . Kim、A. Tolk、R. Hill 和 M. E. Kuhl 编辑。

更多情感:文本分析包

原文:https://towardsdatascience.com/morethansentiments-a-python-library-for-text-quantification-e57ff9d51cd5

帮助研究人员计算样本、冗余、特异性、相对患病率等的函数集合。,在 python 中

帕特里克·托马索在 Unsplash 上的照片

介绍

【morethanworthies(Jiang 和 Srinivasan,2022)】是一个 python 库,用于帮助研究人员计算样本(Lang 和 Stice-Lawrence,2015)、冗余度(Cazier 和 Pfeiffer,2017)、特异性(Hope 等人,2016)、相对患病率(Blankespoor,2019)等。如今,人们经常谈论文本嵌入、语义相似性、意图检测和情感分析……然而,MoreThanSentiments 受到这样一种想法的启发,即适当量化文本结构也将有助于研究人员提取大量有意义的信息。并且这个独立于领域的包易于在各种文本量化任务的项目中实现。

支持的测量

样板文件

在文本分析中,样板文件是可以从句子中删除的单词组合,不会显著改变原来的意思。换句话说,它是信息含量的一种度量。它是根据包含样板文件的句子占总字数的比例来计算的。

作者图片

裁员

冗余是对文本有用性的一种衡量。它被定义为在每个文档中出现一次以上的超长句子/短语(例如 10 克)的百分比。直觉上,如果一个超长的句子/短语被重复使用,这意味着作者试图重复前面提到的信息。

特征

特异性是对与特定主题唯一相关的质量的度量。它被定义为特定实体名称、数量值和时间/日期的数量,所有这些都与文档中的总字数成比例。目前,特异性的功能建立在 spaCy 的命名实体识别器上。

相对患病率

相对患病率是对硬信息的一种衡量。它是相对于整个文本长度的数值数量。它有助于评估给定文本中的定量信息部分。

装置

安装工具箱最简单的方法是通过 pip(某些发行版中的 pip3):

使用

导入包

从 txt 文件中读取数据

这是一个内置的功能,可以帮助你读取一个文件夹的分隔。python 中的 txt 文件。如果你已经把所有的数据存储在一个. csv 文件中,你可以像平常一样用熊猫来读这个文件。

句子标记

如果您想要计算样板和冗余,有必要对句子进行标记,因为 n 元语法是在句子级别上生成的。

干净的数据

如果你想在句子层面上清理:

如果要在文档级别进行清洗:

对于数据清理功能,我们提供以下选项:

  • lower:使所有单词小写
  • 标点符号:删除语料库中的所有标点符号
  • 数字:删除语料库中的所有数字
  • unicode:删除语料库中的所有 Unicode
  • 停用词:删除语料库中的停用词

样板文件

参数:

  • input_data:这个函数需要标记化的文档。
  • n:要使用的 ngrams 的数量。默认值为 4。
  • min_doc:在构建 ngram 列表时,忽略文档频率严格低于给定阈值的 ngram。默认为 5 个文档。建议文件数量的 30%。min_doc 还可以将 0 到 1 之间的数字读取为百分比。(例如,0.3 将被读作 30%)
  • get_ngram:如果该参数设置为“True”,将返回一个包含所有 ngram 和相应频率的数据帧,“min_doc”参数将失效。

裁员

参数:

  • input_data:这个函数需要标记化的文档。
  • n:要使用的 n 元语法的数量。默认值为 10。

特征

参数:

  • input_data:该函数需要没有标记化的文档

相对患病率

参数:

  • input_data:该函数需要没有标记化的文档

结论

morthan opportunities 仍是一个发展中的项目。然而,它已经显示出在不同领域帮助研究人员的潜力。这个软件包简化了量化文本结构的过程,并为他们的 NLP 项目提供了各种文本分数。

以下是完整示例的链接:

引用

如果这个包对你的工作有帮助,请随意引用它为

《情感:一个文本分析包》。软件影响,100456 (2022)。https://doi.org/10.1016/J.SIMPA.2022.100456

相关阅读

使用 R 计算用于会计分析的样板文件

参考

BLANKESPOOR,E. (2019),信息处理成本对企业披露选择的影响:来自 XBRL 要求的证据。会计研究杂志,57:919–967。https://doi.org/10.1111/1475-679X.12268

希望,好的。胡,d .和陆,H. (2016),特定风险因素披露的好处。启帐螺柱 21 1005–1045。https://doi.org/10.1007/s11142-016-9371-1

理查德·a·卡齐尔,雷·j·普发。(2017),10-K 披露重复和管理报告激励。财务报告杂志;2 (1): 107–131.https://doi.org/10.2308/jfir-51912

马克·朗,洛里安·斯泰斯·劳伦斯。(2015),文本分析和国际财务报告:大样本证据,会计和经济学杂志,第 60 卷,第 2-3 期,第 110-135 页,ISSN 0165-4101。https://doi.org/10.1016/j.jacceco.2015.09.002。

OpenCV 中图像预处理的形态学操作,详细

原文:https://towardsdatascience.com/morphological-operations-for-image-preprocessing-in-opencv-in-detail-15fccd1e5745

疾控中心Unsplash 拍摄的照片

侵蚀、扩张、打开、关闭、形态梯度、tophat / whitehat 和 blackhat 用例子解释

在我的上一篇文章中,我写了关于 OpenCV 中的一些基本图像处理。今天,我们将进一步研究图像处理中常用的形态学运算。形态学操作用于提取区域、边缘、形状等。

什么是形态学运算?

对二值图像执行形态学操作。二进制图像可能包含许多缺陷。尤其是通过一些简单的阈值化操作(如果你不熟悉阈值化,现在不用担心它)产生的二值图像可能包含大量的噪声和失真。OpenCV 库中提供了不同的形态学操作来处理这些噪声和缺陷。

形态学操作生成与原始图像形状相同的图像。形态学操作将结构化元素应用于输入图像。结构化元素可以是任何形状。在我们今天将要学习的所有形态学运算中,输入图像的每个像素都与相邻像素进行比较,以产生输出图像。

对于不同的形态学操作,比较发生的方式不同。我们将详细讨论这一点。

在深入研究这个问题之前,下面是我们将在本教程中使用的图片:

作者图片

在这里,我们导入必要的包,将图像作为数组读取,并将其转换为二进制图像。正如我们前面提到的,形态学运算应用于二进制图像:

import cv2 
import matplotlib.pyplot as plt
#Reading the image to an array
image = cv2.imread('practice.jpg')

#converting it to a binary image
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

这是“灰色”的样子:

作者图片

我们将使用这个灰度图像来看看形态学操作是如何工作的。

在深入例子之前,我想创建一个 3x3 内核和一个 6x3 内核:

kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
kernel1 = cv2.getStructuringElement(cv2.MORPH_RECT, (6, 3))

我们将在下面的形态学操作中使用这两个内核。如果有必要,我们以后会创建更多的内核。

核仁可以是任何形状。请尝试其他不同形状的内核,如 1x4、4x4、5x5、7x18 或更多。根据您的项目,内核的形状和大小会有很大的不同。

侵蚀

侵蚀就像它听起来那样。它侵蚀一幅图像就像水侵蚀河岸一样。在腐蚀操作中,它从左到右和从上到下滑动输入图像的结构元素。如果结构元素内的所有像素都大于 0,则保持原始像素值。否则,像素被设置为 0。

腐蚀用于去除被认为是噪声的小斑点。

侵蚀的语法如下:

eroded1 = cv2.erode(gray.copy(), kernel, iterations = 1)
cv2.imwrite('erode1.jpg', eroded1)

侵蚀函数 cv2.erode()使用图像、结构化元素和迭代次数。“迭代”参数在这里是可选的。如果您不提供“迭代次数”值,它会自动进行一次迭代。

以下是“erode1”的样子:

作者图片

与原始图像进行比较。它被侵蚀掉了。此外,原始图像中的其他小元素也被删除。

在任何 OCR(光学字符识别)项目中,我们只想识别字母或数字。但是图像中可能有其他更小的字母和元素会混淆你的算法。侵蚀可以消除这些噪音。

如果我们尝试 2 或 3 次迭代,它将被更多地侵蚀:

eroded2 = cv2.erode(gray.copy(), kernel, iterations = 2)
cv2.imwrite('erode2.jpg', eroded2)

eroded3 = cv2.erode(gray.copy(), kernel, iterations = 3)
cv2.imwrite('erode3.jpg', eroded3)

这些分别是 2 次和 3 次迭代的结果:

作者图片

作者图片

正如你所看到的,随着迭代次数的增加,图像越来越模糊。因此,如果您需要提取粗体且周围有大量噪声的字母,可以通过侵蚀图像来消除噪声。

扩张

膨胀的作用与侵蚀正好相反。它增加了前景,从而有助于连接断裂的部分。腐蚀后可以用它来连接破损的部分。在膨胀中,如果结构元素中的任何像素大于 0,则结构元素中的像素值被设置为白色或 255。我们在这里使用 1 次和 3 次迭代的膨胀来查看差异并理解它是如何工作的。

dilated1 = cv2.dilate(gray.copy(), kernel, iterations=1)
cv2.imwrite('dilate1.jpg', dilated1)

dilated3 = cv2.dilate(gray.copy(), kernel, iterations=3)
cv2.imwrite('dilate3.jpg', dilated3)

这些分别是 1 次迭代(第二次图像)和 3 次迭代(第三次图像)膨胀后的图像。原来的灰色图像在上面。

作者图片

作者图片

作者图片

如果我们将顶部的原始图像与具有一次膨胀迭代的第二图像进行比较,则存在微小的差异,并且在 3 次迭代之后,差异变得更加显著。根据您的项目,您可以根据需要使用任意多的迭代。

开始

开口也是从图像中去除噪声的另一种方式。在一次迭代中,它先腐蚀,然后膨胀。这里有两个例子,我使用了我们在开始时准备的 kernel 和 kernel1:

opening1 = cv2.morphologyEx(gray, cv2.MORPH_OPEN, kernel)
cv2.imwrite('open1.jpg', opening1)

opening2 = cv2.morphologyEx(gray, cv2.MORPH_OPEN, kernel1)                         )
cv2.imwrite('open2.jpg', opening2)

在这里,我将原始图像放在顶部,然后是“打开 1”操作的图像,在底部是“打开 2”操作的图像。

作者图片

作者图片

作者图片

正如你所看到的,在中间的图片中,我们使用了一个 3x3 的内核,左下角的小文本不见了,在底部的图片中,我们使用了 6x3 大小的内核 1,添加了一个黑色的阴影

关闭

听起来,关闭是打开的反义词。在闭合时,膨胀首先发生,然后是侵蚀。

让我们来看一些例子:

closing1 = cv2.morphologyEx(gray.copy(), cv2.MORPH_CLOSE, kernel, iterations=1)
cv2.imwrite('close1.jpg', closing1)

closing3 = cv2.morphologyEx(gray.copy(), cv2.MORPH_CLOSE, (3, 3), iterations=3)
cv2.imwrite('close3.jpg', closing3)

像以前一样,我将原始的灰色图像放在顶部进行比较,然后是“关闭 1”和“关闭 2”的输出。

作者图片

作者图片

作者图片

形态梯度

形态学梯度对于检测物体的轮廓是有用的。它可以用于边缘检测。基本上,这是膨胀和腐蚀操作之间的区别。

这是两个使用 kernel 和 kernel1 的例子:

grad1 = cv2.morphologyEx(gray.copy(), cv2.MORPH_GRADIENT, kernel)
cv2.imwrite('grad1.jpg', grad1)

grad2 = cv2.morphologyEx(gray.copy(), cv2.MORPH_GRADIENT, kernel1)
cv2.imwrite('grad3.jpg', grad2)

这些是来自“梯度 1”和“梯度 2”的输出图像:

作者图片

作者图片

如你所见,两种不同形状的内核为我们提供了两种不同类型的输出。

托哈特/怀特哈特

tophat 操作是原始二进制图像和开之间的差异。当你需要从黑暗的背景中找到一个明亮的区域时,这是很有帮助的。

我们将为这个操作和其余操作使用不同的输入图像:

以下是输入图像:

作者图片

这辆车的号牌是白的比车亮。让我们看看如何提取白色区域。像往常一样,我们应该把它转换成灰度图像,我们将定义两个不同的内核。

image = cv2.imread('car.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (23, 5))

kernel1 = cv2.getStructuringElement(cv2.MORPH_RECT, (35, 8))

我尝试了几种不同形状的内核,然后使用这两种内核大小。请随意尝试其他内核大小并比较结果。下面是 tophat 的操作:

tophat1 = cv2.morphologyEx(gray.copy(), cv2.MORPH_TOPHAT, kernel1)
cv2.imwrite('tophat1.jpg', tophat1)

以下是输出图像:

作者图片

看,它从车本身检测出车牌的亮区。

黑帽子

黑帽行动正好相反。

blackhat1 = cv2.morphologyEx(gray.copy(), cv2.MORPH_BLACKHAT, kernel)
cv2.imwrite('blackhat1.jpg', blackhat1)

这是 blackhat 操作的输出:

作者图片

如果你注意到,它集中在号码牌的字母上。

在 tophat 之后,车牌区域被检测到,在 blackhat 之后,白色车牌上的黑色字母被突出显示。

因此,如果我们想从号码牌中检测出数字,我们将执行 tophat 操作,然后执行黑帽操作。

结论

本文试图举例说明一些著名的形态学运算。在以后的一些文章中,我会用它们来解决一些现实问题。那会更有趣。

更多阅读

https://pub.towardsai.net/some-simple-but-advanced-styling-in-pythons-matplotlib-visualization-107f3be56a24

“大多数挑战都是伪装的机遇”

原文:https://towardsdatascience.com/most-challenges-are-opportunities-in-disguise-74eff05c2a66

作者聚焦

Tessa Xie 反思了数据科学职业生涯的曲折、咨询的好处以及写作对广大受众的价值。

在 Author Spotlight 系列中,TDS 编辑与我们社区的成员谈论他们在数据科学领域的职业道路、他们的写作以及他们的灵感来源。今天,我们很高兴与 泰莎谢 分享我们的对话。

照片由 Tessa Xie 提供

Tessa 从麻省理工学院金融工程专业毕业后,以定量研究员的身份开始了她的金融职业生涯。她后来在麦肯锡从事数据科学咨询工作,随后进入科技行业,在自动驾驶公司 Cruise 担任数据科学经理。最近,她开始了在 LinkedIn 担任数据科学经理的新旅程。

你是如何决定成为数据科学家的?

我第一次对数据科学感兴趣是在研究生院,我的专业是金融数学。因为金融数学和数据科学之间有很多重叠,我们有机会选修数据科学课程。令我惊讶的是,我发现自己对数据科学课程比对专门关注金融的课程更感兴趣。

但是,我毕业后并没有马上进入数据科学,因为我想尝试一下我最初着手探索的行业——金融。做了一年多的定量分析师后,我最初的假设得到了证实:我喜欢与数据打交道,但不一定是财务数据。

我知道我想离开金融业,但不确定我想进入哪个行业。在进入一个行业之前,咨询似乎是探索不同行业数据科学的最佳方式。咨询公司还会面试背景比技术领域更多样化的数据科学家,因此这是一个从金融领域转向数据科学的好机会。尽管量化金融和数据科学之间存在重叠,但我仍然决定参加一些在线课程来更新我的数据科学知识,这些课程在面试中肯定有所收获。

你最近在自动驾驶汽车领域呆了一段时间。在那个行业做数据科学家是什么感觉?

因为自动驾驶是一个如此未来的领域,最大的挑战之一是我们第一次解决大量数据科学问题。这是一个挑战,因为当你陷入一个问题时,通常你没有剧本可以借鉴;您没有可以查找的最佳实践。这也是一个机会,因为这意味着作为一名数据科学家,你可以采取很多主动,这是一次很好的学习经历,更不用说即使作为一名相对初级的数据科学家,你也能够产生有意义的影响。

我注意到的另一个挑战是,通过不同的渠道(如激光雷达、雷达、相机等)收集了大量数据。)获取交通或道路相关信息。能够高效、准确地连接来自这些脱节渠道的数据至关重要,因为这将成为任何基于此的数据科学分析的基础。它为数据科学家提供了与数据工程师和数据基础架构团队密切合作的机会。在这个过程中,我能够了解数据工程和数据基础设施,并理解这些领域的复杂性。

第三点值得一提的是,由于大多数自动驾驶公司要么仍处于研发阶段,要么刚刚开始将其产品商业化,所以作为数据科学家,你可以处理的客户数据并不多。因此,如果你是一名对产品分析、用户研究和/或实验非常感兴趣的数据科学家,你只需要知道,你不会有大量的数据来驱动洞察力(还没有)。

对于正在考虑这个行业的数据科学家,你有什么其他的指点吗?

谈到评估机会,我目前把两个因素放在其他因素之上——学习机会和我对产品的兴趣和热情。我最初被自动驾驶领域吸引正是因为这两个原因。因为这是一个如此新颖的领域,我知道在数据方面会有很多挑战。在我看来,大多数挑战都是变相的机遇;这是迫使你走出舒适区,学习新事物的最好方法。所以进入一个没人完全搞清楚的领域,参与“零到一”的转变,我非常兴奋。

此外,自动驾驶汽车是我有信心和热情的产品。作为一个不懂驾驶的人,我对自动驾驶汽车的可能性及其潜力感到非常兴奋。更不用说 AV 技术将有望比人类司机更安全,并有可能挽救我们每年在车祸中失去的无数生命。能够从事一项能给世界带来积极影响的技术,并且有一天我会使用这项技术,这使得这项工作变得更加有趣和有意义。

你最近 写了你离开 麦肯锡顾问的经历。数据科学家在做出加入或离开一家公司的决定时,应该问哪些问题?

数据科学是一个宽泛的术语,包含大量行业中的许多不同角色,外人很难对其有一个全面的了解。为了帮助解决这个问题,我写了两篇文章,关于如何决定你想加入什么类型的公司,以及如何决定在数据世界中的正确角色。

在公司之间做决定时,我的经验中最大的区分因素是公司的规模。加入一家大公司通常意味着你有更多的最佳实践可以学习,有更好的,或者至少更可预测的薪酬,但与此同时,直接影响更小,潜在的企业文化更注重流程。

另一方面,较小的公司通常会提供更多的机会来采取主动并产生影响,因为很多事情还没有完全弄清楚。与企业文化相反,你会在小公司体验到更多的忙碌文化,因为事情需要快速发展。我在关于你应该选择加入小公司还是大公司的文章中详细介绍了这一点。当然,任何概括都有例外,所以你真的需要根据具体情况来判断公司;但是,在做出这个决定时,你应该问自己的问题类型应该或多或少围绕相同的维度——公司文化、边做边学或从最佳实践中学习、薪酬和职业前景。

决定追求哪个特定的角色呢?

当谈到您想要选择的角色类型时,这是一个更难的问题,因为有太多的角色可以归入数据科学的保护伞下。有数据工程师、数据科学家、数据分析师、决策科学家、ML 工程师等等;许多公司交替使用这些头衔并没有帮助。但你要问自己的最重要的问题是:你更喜欢什么,探索问题还是实施解决方案?

这个角色的工程成分越多(MLE,数据工程师),它就越关注“如何”,你就要把大部分时间花在实现解决方案上。更接近业务团队的角色(数据分析师、数据科学家)将更加关注探索“是什么”和“为什么”只要你知道这个问题的答案,你就能更容易地将工作描述与你的兴趣联系起来。

这些问题应该根据你所处的职业阶段而改变吗?

如果你处于入门级的位置,这些问题就不那么重要了——对于职业生涯的最初几年,最重要的是学习。所以你唯一需要问自己的是,“这个角色给我提供了足够的学习机会吗?”如果这个角色不符合你的所有标准,你会有很多机会改变你的职业生涯。如果你正处于职业生涯的中期,根据你多年的探索和学习,你会更容易回答上述问题,而且更重要的是专注于建立一个符合你感兴趣的职业道路的清晰的个人资料,因为招聘经理和招聘人员已经提高了对有经验的候选人的背景要求。

你写了大量关于数据科学职位面试经验的文章,为什么与更广泛的受众分享这些见解对你来说很重要?

当我第一次决定转向数据科学时,网上关于如何准备 DS 面试的资源没有其他职位那么多,特别是对于那些希望从其他类型的职位和行业(如金融)转换的人来说。有著名的面向软件工程师的 Leetcode,还有一本面向产品经理的书,破解 PM 访谈。但是当谈到数据科学时,我或多或少地独自准备面试,因为这是一个如此新的领域。

经过几轮招聘,我面试了无数家公司,从大型科技公司到小型创业公司和咨询公司。在这个过程中,我从这一领域的朋友那里获得了很多见解,我自己也做了很多研究。在我换工作后,我通过 LinkedIn 得到了很多有抱负的数据科学家的帮助,询问我关于面试准备的建议。我很喜欢支持同行,避免重复工作,所以我认为在 Medium 这样的公共平台上以可扩展的方式与更广泛的受众分享我自己的面试经验对我会有帮助。

我最近开始了一个新的系列,专门针对典型 DS 面试中涉及的面试主题。目标是让读者能够使用这个系列作为他们面试准备和求职的集中起点。说到这一点,我很想从我的听众那里得到反馈,知道什么对他们找工作有帮助;我会尽我所能在以后的帖子中体现这些要求。

最后,作为一个多产的作者,你对可能有兴趣为广大读者写作的数据专业人士有什么建议吗?

对于有抱负的作家来说,患上冒名顶替综合症是很常见的事情。我当时肯定在想,“我不是数据科学的副总裁,也不是机器学习的博士,我怎么可能为数据社区增加价值?”

我的建议是忘记你是什么而不是,专注于你是什么。你是谁,你的经历是什么并不重要,你可以带来一些东西,你可以用这些见解潜在地帮助别人。所以找到合适的位置是关键。如果作为数据专业人员,您的大部分工作是构建模型,我相信您有许多最佳实践可以与刚刚开始建模的人分享。

说到为文章寻找新的想法,我有三个建议,我发现对我自己的工作很有帮助。

在 Medium 上看别人的文章。你读得越多,你就越能感觉到当前数据社区中什么是重要的,并有可能通过这种方式获得灵感。在这个过程中,你会发现所涵盖的主题中的空白,也许你可以通过填补这些空白来增加一些价值。

从你与人的日常互动中汲取灵感。花些时间反思一下你的一天通常是有帮助的。我文章的很多灵感来自于我和人们的互动。当很多人问你同样的问题时,这表明你有一种需要和一个缺口,你可以帮助填补。我写了一篇关于使用数据可视化工具的最佳方式的文章,因为当团队成员入职时,每个人似乎都对数据可视化工具和数据仓库应该采用什么逻辑感到困惑。做一个好的倾听者,关注人们的需求:这些都是你文章的好主题。

疏导你的沮丧情绪。无论你的工作有多完美,你都会对工作中的事情感到沮丧。利用它们成为你的优势。你对没有被完美处理的情况感到沮丧。所以问问你自己,“处理这种情况的更好的方法是什么?”以及“我能不能把这个归纳成一个可以帮助别人的概念?”因为如果你遇到这种问题,很可能其他人也在经历同样的事情,你的文章可能会帮助他们。**

至于找时间写作,最重要的是克服心理障碍。很多人认为他们没有时间写作,因为他们正在想象写一篇有截止日期的学校论文,为此你必须花一整个晚上把事情写在一张纸上。实际上,你不需要一口气坐下来写一篇文章 1-2 个小时。相反,我的很多好主意都是在去咖啡店的路上或者吃午饭的时候想出来的。花一分钟在手机上快速记下它们。每天写一点。它可以是一个介绍,甚至只是一句话:没关系。最重要的是要养成写字的习惯。

要了解更多关于泰莎的工作和探索她的最新文章,请点击 MediumLinkedIn 关注她。除了 Tessa 在上面的回复中分享的帖子,这里还有一些来自 TDS 档案的亮点:

想和广大观众分享一些你自己的作品吗?我们很乐意收到你的来信。

这个 Q & A 是为了长度和清晰而稍加编辑的。

数据科学中最常见的 Python 错误类型

原文:https://towardsdatascience.com/most-common-error-types-in-data-science-396f9ebda40f

当您将 Python 用于数据科学时,了解这些错误类型以及如何解决它们

布雷特·乔丹Unsplash 上拍摄的照片

错误

编码出现错误。他们不是想要的——当然是——但是他们应该是被期待的。我这么说是因为错误是数据科学家日常工作的一部分。有时原因会是一个简单的分心。其他时候将需要更深入的搜索和研究,以找出是什么在阻止我们的程序正常运行。

因此,如果我们知道 Python 中最常见的错误类型,这给了我们寻找答案的优势。

在数据科学中,一般来说,我们编写代码来探索和转换数据,以使它们符合确定的 ML 模型,或者只是为了获得对数据集的一些见解。

通常,错误出现在对象中或由于函数使用不当而弹出:

  • 当你忘记创建或声明一个变量时。
  • 当您试图在错误类型的对象中执行操作时,如 sum 文本。
  • 如果您试图在错误类型的对象上使用属性。

此外,当我们使用数据框架或操作数据时,以及当我们拟合模型时,也会发生错误。

在我开始举例之前,让我先说明一下我将使用的数据集:我将使用 Seaborn 库中的数据集 Tips

让我们看一些例子。

名称错误

NameError 在局部或全局范围内找不到变量时发生。

简单地说,就是当你忘记在运行的命令中声明一个变量的时候。如果您没有运行 Python 将值赋给变量的单元格,您可能会看到该错误。

# Printing an object I never declared.
print(my_object)-------------------------------------------------------------NameError             Traceback (most recent call last)[<ipython-input-1-1f04422500d2>](/<ipython-input-1-1f04422500d2>) in <module>()
----> 1 print(my_object)NameError: name 'my_object' is not defined

另一个很好的例子:如果我们想使用来自提示的数据拟合一个回归模型,我们必须将我们的模型分成独立(X)和非独立(y)变量。如果您试图让 total_bill 放入 X,但是您忘记了引号,您将看到同样的错误。

# Trying to assign total_bill to X
X = df[total_bill]NameError                   Traceback (most recent call last)[<ipython-input-83-cc074c7738f3>](/<ipython-input-83-cc074c7738f3>) in <module>()
----> 1 X = df[total_bill]NameError: name 'total_bill' is not defined

如何解决:大多数时候,只要声明 Python 说没有定义的变量或者对变量名使用合适的引号就可以了。

类型错误

TypeError弹出当一个函数或操作被应用到一个不正确类型的对象时。

是的,比如说,当你试图对一个文本进行数学运算时。你不能将两个字母相加,除非它们被赋予了数值。

# trying to multiply two strings
my_sum = 'a' * 'b'---------------------------------------------------------TypeError                   Traceback (most recent call last)[<ipython-input-3-b463ecefa547>](/<ipython-input-3-b463ecefa547>) in <module>()
----> 1 my_sum = 'a' * 'b'TypeError: can't multiply sequence by non-int of type 'str'

如果您给出的参数多于函数所需的数量,也会发生这种情况。

# Predict does not need the target variable
model.predict(X,y)--------------------------------------------------------------TypeError               Traceback (most recent call last)[<ipython-input-85-ec74152c0fb0>](/<ipython-input-85-ec74152c0fb0>) in <module>()
----> 1 model.predict(X,y)TypeError: predict() takes 2 positional arguments but 3 were given

如何解决:仔细检查你用于该操作的对象类型,或者检查你是否没有给一个函数提供太多的参数。

属性错误

AttributeError —如果您试图将属性用于错误的对象类型,将会发生这种情况,Python 告诉它不能这样做。

示例:尝试对列表使用 dataframe 属性。

# list
a = [1,2,3]# Trying to use an attribute from Pandas objects for a list
a.shape--------------------------------------------------------------AttributeError                Traceback (most recent call last)[<ipython-input-4-bb809aa8b209>](/<ipython-input-4-bb809aa8b209>) in <module>()
 **3** 
 **4** # Trying to use an attribute from Pandas objects for a list
----> 5 a.shapeAttributeError: 'list' object has no attribute 'shape'

如何求解:确保使用的属性适用于那个对象。知道这一点的一个好方法是使用dir(object_name)。它将列出可用的属性和方法。

索引错误

IndexError 序列的索引超出范围时发生。

如果您有一个包含 3 个元素的列,但是您想要打印 4 个元素,您的循环将不会再次开始。它将升高索引错误

# list
a = [1,2,3]# Loop out of range
for i in range(4):
print( a[i] )1
2
3----------------------------------------------------------------IndexError                   Traceback (most recent call last)[<ipython-input-95-cd66a0748b5d>](/<ipython-input-95-cd66a0748b5d>) in <module>()
 **4** # Loop out of range
 **5** for i in range(4):
----> 6   print( a[i] )IndexError: list index out of range

如何解决:确保你在物体的范围/长度内循环。

键盘错误

当在字典或数据帧中找不到关键字时,KeyError

这个错误有时让我有些头疼。假设您正在处理一个数据框,并且删除了一行。如果您忘记重置索引(或出于某种原因不想这么做),并且您稍后将执行使用索引的操作,如果没有找到给定的数字,则 KeyError 将弹出。

# Loop through Tips dataset and make an operation for every row.
# However, the dataset goes from 0-243 index# length is 3, but let's try a loop 4 times.
for i in range(245):
df.tip[i] - 0.1 -----------------------------------------------------------ValueError                      Traceback (most recent call last)[/usr/local/lib/python3.7/dist-packages/pandas/core/indexes/range.py](/usr/local/lib/python3.7/dist-packages/pandas/core/indexes/range.py) in get_loc(self, key, method, tolerance)
 **384**                 try:
--> 385                     return self._range.index(new_key)
 **386**                 except ValueError as err:ValueError: 244 is not in rangeThe above exception was the direct cause of the following exception:KeyError                                  Traceback (most recent call last)[/usr/local/lib/python3.7/dist-packages/pandas/core/indexes/range.py](/usr/local/lib/python3.7/dist-packages/pandas/core/indexes/range.py) in get_loc(self, key, method, tolerance)
 **385**                     return self._range.index(new_key)
 **386**                 except ValueError as err:
--> 387                     raise KeyError(key) from err
 **388**             raise KeyError(key)
 **389**         return super().get_loc(key, method=method, tolerance=tolerance)KeyError: 244

再比如。

# Create a Dataframe
df = pd.DataFrame( {'number': range(1,6), 'val': np.random.randn(5)})# Drop row 2
df.drop(2, axis=0, inplace=True)# Print Index 2
df.loc[2]---------------------------------------------------------------KeyError: 2The above exception was the direct cause of the following exception:KeyError                                  Traceback (most recent call last)[/usr/local/lib/python3.7/dist-packages/pandas/core/indexes/base.py](/usr/local/lib/python3.7/dist-packages/pandas/core/indexes/base.py) in get_loc(self, key, method, tolerance)
 **3361**                 return self._engine.get_loc(casted_key)
 **3362**             except KeyError as err:
-> 3363                 raise KeyError(key) from err
 **3364** 
 **3365**         if is_scalar(key) and isna(key) and not self.hasnans:KeyError: 2

如何解决:仔细检查指标没有任何遗漏数字。此外,您可以检查该“键”是否存在于您的数据集中。

值错误

ValueError 当一个函数得到一个类型正确但值不正确的参数时弹出。

当你有正确的函数,但是你输入了错误的参数时,就会发生这种情况。例如,尝试将文本列指定为整数。

# Create a Dataframe
df = pd.DataFrame( {'Letter': ['a', 'b', 'c', 'd', 'e'], 'val': np.random.randn(5)})# Try to assign string to integer
df.Letter.astype('int')------------------------------------------------------------------ValueError                     Traceback (most recent call last)[<ipython-input-61-805ac8fc7e9b>](/<ipython-input-61-805ac8fc7e9b>) in <module>()
----> 1 df.Letter.astype('int')/usr/local/lib/python3.7/dist-packages/pandas/_libs/lib.pyx in pandas._libs.lib.astype_intsafe()ValueError: invalid literal for int() with base 10: 'a'

或者你可以看到,如果你试图运行一个非数字变量的线性回归。不同场景下也是一样的情况。

import seaborn as sns
from sklearn.linear_model import LinearRegression# Load dataset Tips
df = sns.load_dataset('tips')# Regression Model
model = LinearRegression()# Assign X and y
X = df[['sex']]
y= df.tip# Mode fit
model.fit(X, y)-------------------------------------------------------------------ValueError        Traceback (most recent call last)ValueError: could not convert string to float: 'Female'The above exception was the direct cause of the following exception:ValueError                Traceback (most recent call last)[/usr/local/lib/python3.7/dist-packages/sklearn/utils/validation.py](/usr/local/lib/python3.7/dist-packages/sklearn/utils/validation.py) in check_array(array, accept_sparse, accept_large_sparse, dtype, order, copy, force_all_finite, ensure_2d, allow_nd, ensure_min_samples, ensure_min_features, estimator)
 **790**           "Unable to convert array of bytes/strings "
 **791**          "into decimal numbers with dtype='numeric'"
--> 792                 ) from e
 **793**         if not allow_nd and array.ndim >= 3:
 **794**             raise ValueError(ValueError: Unable to convert array of bytes/strings into decimal numbers with dtype='numeric'

如何求解:理解你的变量,打印它们type(variable)以了解你正在处理的对象,阅读文档并检查函数期望接收的预期参数是什么。

照片由Olav Ahrens rtneUnsplash 上拍摄

在你走之前

编程是一个逻辑问题。你必须学会如何像计算机一样思考,因为它期望接收的信息中的微小差异都会给你带来错误。

学习如何阅读文档和在互联网上查找有用的信息会给你很大的帮助,特别是如果你知道你正在处理什么类型的错误。

当然,随着你越来越有经验,这些错误对你来说也越来越熟悉。

总而言之:

  • 你忘了定义变量名。
  • TypeError:你试图用错误类型的对象做一些事情。不能取文字的中值。
  • AttributeError:属性不针对该对象。列表没有形状。使用dir()查看有什么可用的。
  • IndexError:你的循环超出范围。你试图将某些东西应用到物体不存在的部分。
  • KeyError:钥匙不存在。例如,您删除了第一行。索引 0 将不再作为键存在。
  • 你正试图使用一个错误的值作为函数的参数。如果函数需要文本,则不能输入数字。解决这个问题的最好方法是阅读文档。

参考

https://www.tutorialsteacher.com/python/error-types-in-python https://github.com/gurezende/Studying/tree/master/Python/errors

如果这个内容有意思就关注我吧。

https://gustavorsantos.medium.com/

如果你想订阅 Medium,这里有我的推荐链接

自然语言处理中最常见的文本处理任务

原文:https://towardsdatascience.com/most-common-text-processing-tasks-in-natural-language-processing-d8cd7a076b79

计算机没有人类的能力,文本数据需要处理,以便更好地理解和解释

介绍

人类具有理解书面文本信息的能力。另一方面,机器不具备这种内在能力。这就是文本处理变得重要的地方,因为它允许这些机器理解和分析自然语言。

在这篇概念性文章中,我们将解释如何使用流行的 Python 库(如 NLTK 和 Spacy)执行最常见的文本处理任务。

最常见的任务

文本预处理包括标记化、停用词去除、词干化和词条化、词性标注和命名实体识别。本节重点解释它们中的每一个及其 python 实现。

先决条件

首先,您需要在计算机上安装 Python 以及以下库:

  • NLTK
  • 空间
  • sci kit-学习

您可以使用 Python 包管理器pip安装这些库,如下所示:

# Install NLTK
pip install nltk

# Install Spacy
pip install spacy

# Install Scikit-learn
pip install scikit-learn

现在,让我们导入必要的模块并加载用于实验的数据集。我们将使用 Scikit-learn 中内置的新闻文章数据。

import nltk
import spacy
nltk.download('punkt')
from sklearn.datasets import fetch_20newsgroups

然后,我们可以使用fetch_20newsgroups函数通过访问data属性来下载和加载新闻数据,如下所示:

news_data = fetch_20newsgroups(subset='all')
articles = news_data.data

让我们看看第一条:

print(articles[0])

这将生成以下输出:

From: Mamatha Devineni Ratnam <@andrew.cmu.edu">mr47+@andrew.cmu.edu>
Subject: Pens fans reactions
Organization: Post Office, Carnegie Mellon, Pittsburgh, PA
Lines: 12
NNTP-Posting-Host: po4.andrew.cmu.edu

I am sure some bashers of Pens fans are pretty confused about the lack
of any kind of posts about the recent Pens massacre of the Devils. Actually,
I am  bit puzzled too and a bit relieved. However, I am going to put an end
to non-PIttsburghers' relief with a bit of praise for the Pens. Man, they
are killing those Devils worse than I thought. Jagr just showed you why
he is much better than his regular season stats. He is also a lot
fo fun to watch in the playoffs. Bowman should let JAgr have a lot of
fun in the next couple of games since the Pens are going to beat the pulp out of Jersey anyway. I was very disappointed not to see the Islanders lose the final
regular season game.          PENS RULE!!!

既然一切都设置好了,是时候深入每个任务了,从标记化开始。

标记化

这是文本处理中最简单的步骤,包括将文本分割成标记。生成的令牌取决于底层的令牌化方法。例如:

  • 单词标记化生成单词。
  • 句子标记化将文本分割成句子。

使用NLTK库中的work_tokenize()sent_tokenize()函数分别执行单词和句子的分词。

# Import word and sentence tokenizers
from nltk.tokenize import word_tokenize, sent_tokenize

在用前一篇文章的第二个代码块初始化变量first_article之后,我们可以继续进行标记化,如下所示:

# Generate Word tokens
word_tokens = word_tokenize(first_article)

# Generate Sentence Tokens
sentence_tokens = sent_tokenize(first_article)

# Print the results
print(word_tokens)
print(sentence_tokens)

前面的print语句应该会生成这些输出。第一个是单词标记,第二个是句子标记。

['I', 'am', 'sure', 'some', 'bashers', 'of', 'Pens', 'fans', 'are', 'pretty', 'confused', 'about', 'the', 'lack', 'of', 'any', 'kind', 'of', 'posts', 'about', 'the', 'recent', 'Pens', 'massacre', 'of', 'the', 'Devils', '.', 'Actually', ',', 'I', 'am', 'bit', 'puzzled', 'too', 'and', 'a', 'bit', 'relieved', '.', 'However', ',', 'I', 'am', 'going', 'to', 'put', 'an', 'end', 'to', 'non-PIttsburghers', "'", 'relief', 'with', 'a', 'bit', 'of', 'praise', 'for', 'the', 'Pens', '.', 'Man', ',', 'they', 'are', 'killing', 'those', 'Devils', 'worse', 'than', 'I', 'thought', '.', 'Jagr', 'just', 'showed', 'you', 'why', 'he', 'is', 'much', 'better', 'than', 'his', 'regular', 'season', 'stats', '.', 'He', 'is', 'also', 'a', 'lot', 'fo', 'fun', 'to', 'watch', 'in', 'the', 'playoffs', '.', 'Bowman', 'should', 'let', 'JAgr', 'have', 'a', 'lot', 'of', 'fun', 'in', 'the', 'next', 'couple', 'of', 'games', 'since', 'the', 'Pens', 'are', 'going', 'to', 'beat', 'the', 'pulp', 'out', 'of', 'Jersey', 'anyway', '.', 'I', 'was', 'very', 'disappointed', 'not', 'to', 'see', 'the', 'Islanders', 'lose', 'the', 'final', 'regular', 'season', 'game', '.', 'PENS', 'RULE', '!', '!', '!']

显示每个单词标记可能太多了,但我们可以按如下方式显示所有句子:

I am sure some bashers of Pens fans are pretty confused about the lack
of any kind of posts about the recent Pens massacre of the Devils.

Actually, I am  bit puzzled too and a bit relieved.

However, I am going to put an end to non-PIttsburghers' relief with a bit of praise for the Pens.

Man, they are killing those Devils worse than I thought.

Jagr just showed you why he is much better than his regular season stats.

He is also a lot
fo fun to watch in the playoffs.

Bowman should let JAgr have a lot of
fun in the next couple of games since the Pens are going to beat the pulp out of Jersey anyway.

I was very disappointed not to see the Islanders lose the final regular season game.

PENS RULE!!

句子分词器在.符号后识别一个新句子。

停止单词删除

查看之前的单词标记,我们可以看到一些术语,如an, a, of, the,等。这些单词被称为stop words,因为与其他单词相比,它们没有太多的含义。因此,删除它们可以使信息更容易处理。这可以通过使用nltk.corpus.stopwords模块中的words()功能来实现。

from nltk.corpus import stopwords

由于我们正在处理一个英文文本,我们需要如下加载底层停用词:

# Acquire the stop words
english_stw = stopwords.words("english")

最后,我们可以过滤所有单词标记,只保留不间断的单词。

non_stop_words = [word for word in word_tokens if word not in english_stw]

# Show the final stop words
print(non_stop_words)

前面的print语句显示了以下结果:

['I', 'sure', 'bashers', 'Pens', 'fans', 'pretty', 'confused', 'lack', 'kind', 'posts', 'recent', 'Pens', 'massacre', 'Devils', '.', 'Actually', ',', 'I', 'bit', 'puzzled', 'bit', 'relieved', '.', 'However', ',', 'I', 'going', 'put', 'end', 'non-PIttsburghers', "'", 'relief', 'bit', 'praise', 'Pens', '.', 'Man', ',', 'killing', 'Devils', 'worse', 'I', 'thought', '.', 'Jagr', 'showed', 'much', 'better', 'regular', 'season', 'stats', '.', 'He', 'also', 'lot', 'fo', 'fun', 'watch', 'playoffs', '.', 'Bowman', 'let', 'JAgr', 'lot', 'fun', 'next', 'couple', 'games', 'since', 'Pens', 'going', 'beat', 'pulp', 'Jersey', 'anyway', '.', 'I', 'disappointed', 'see', 'Islanders', 'lose', 'final', 'regular', 'season', 'game', '.', 'PENS', 'RULE', '!', '!', '!']

标点符号删除

如果停用词不相关,标点符号也不相关!我们可以很容易地去掉标点符号(.,;等)。)使用 Python 中的原生string模块。

import string

without_punct = list(filter(lambda word: word not in string.punctuation, non_stop_words))

print(without_punct)
['I', 'sure', 'bashers', 'Pens', 'fans', 'pretty', 'confused', 'lack', 'kind', 'posts', 'recent', 'Pens', 'massacre', 'Devils', 'Actually', 'I', 'bit', 'puzzled', 'bit', 'relieved', 'However', 'I', 'going', 'put', 'end', 'non-PIttsburghers', 'relief', 'bit', 'praise', 'Pens', 'Man', 'killing', 'Devils', 'worse', 'I', 'thought', 'Jagr', 'showed', 'much', 'better', 'regular', 'season', 'stats', 'He', 'also', 'lot', 'fo', 'fun', 'watch', 'playoffs', 'Bowman', 'let', 'JAgr', 'lot', 'fun', 'next', 'couple', 'games', 'since', 'Pens', 'going', 'beat', 'pulp', 'Jersey', 'anyway', 'I', 'disappointed', 'see', 'Islanders', 'lose', 'final', 'regular', 'season', 'game', 'PENS', 'RULE']

词干化和词汇化

有时候在同一个文档里,我们可以找到类似confused, confusing, confused, confuses, confuse, confused这样的词。在性能至关重要的大型数据集的情况下使用它们可能会有问题。这就是词干化和词汇化有用的地方。他们的目标是将这些单词减少到基本单词。

你可以从我的文章词干化、词汇化中深入了解这些技术,以及它们的区别——哪一个更值得一试?

让我们考虑本节的以下示例文本:

sample_text = """This thing really confuses. 
                 But you confuse me more than what is written here.  
                 So stay away from explaining things you do not understand. 
              """

然后,我们可以使用nltk.stem来导入PorterStemmerWordNetLemmatizer,以使用这两个辅助函数分别执行词干化和词汇化。

def stem_words(sentence, model=my_stemmer):

    for word in sentence.split():
      stem = model.stem(word)
      print("Word: {} --->: {}".format(word, stem))

def lemmatize_words(sentence, model = my_lemmatizer):

    for word in sentence.split():
      lemma = model.lemmatize(word)
      print("Word: {} --->: {}".format(word, lemma))

stem_words通过在箭头的左侧和右侧显示原始单词和已词干化的单词来执行词干化。同样的方法也适用于使用lemmatize_words函数的词汇化。

在使用这两个函数之前,我们需要设置如下所示的两个模型:

Lemmatizer:Lemmatizer 需要使用多语言 wordnet 的wordnet词典数据库和OMW模块,因此它们也需要下载。

# Import the Lemmatizer module
from nltk.stem import WordNetLemmatizer

# Download wordnet lexicon database
nltk.download('wordnet')
nltk.download('omw-1.4')

# Instanciate Lemmatizer
my_lemmatizer = WordNetLemmatizer()

斯特梅尔:这很简单,配置如下:

Import the Stemmer module
from nltk.stem.porter import PorterStemmer

# Create instance of stemmer
my_stemmer = PorterStemmer()

现在我们已经配置了两个模型,让我们在示例文本上运行它们。

# Run the stemming
stem_words(sample_text, model=my_stemmer)

这应该会显示以下结果

词干插图(图片由作者提供)

类似地,我们可以如下执行术语化:

lemmatize_words(sample_text, model = my_lemmatizer)

词汇化插图(图片由作者提供)

从每个输出中,我们可以在右侧观察到,一些世界已经被认为与它们的词干 lemma 相同,而一些则完全被转换,尤其是things, confuse

词性标注

对于给定的句子,如何知道哪个是名词、动词、形容词、代词等?这些部分被称为Part of Speech,它们可以帮助你理解句子的结构和潜在含义。这个任务叫做Part of speech tagging。它会自动为句子中的每个单词分配词性。

使用标记列表,我们可以使用pos_tag函数为每个标记分配相应的词性。pos_tag的最终结果是一个元组列表,其中每个元组都有一个标记,并且是语音标签的一部分。下面是一个插图。

标签员需要模块averaged_perceptron_tagger,其中包含预先培训的英语。

# Import the module
from nltk.tag import pos_tag

# Download the tagger
nltk.download('averaged_perceptron_tagger')

# Perform the post tagging
tagged_tokens = pos_tag(without_punct)

# Show the final result
print(tagged_tokens)

前面的print语句显示了以下结果:

术语和标签(作者图片)

在我们之前的例子中:

  • 是一个Pronoun (PRP)
  • 糊涂了是个adjective (JJ)
  • 非匹兹堡人是一个Noun, Plural (NNS)
  • 因为是一个preposition or subordinating conjuction (IN)

你可以找到词性标签集

命名实体识别

命名实体识别(简称 NER)和词性标注有时会混淆。尽管它们是两个相关的任务,但它们是完全不同的。NER 包括对命名实体(如个人、组织、地点等)的识别和分类。一条短信。

我的文章用 Spacy 和强大的 roBERTa 命名实体识别提供了对 NER 的清晰简明的解释,以及 Python 代码。

结论

在这篇概念性文章中,您了解了一些在自然语言处理中执行常见文本处理任务的常用方法。

此外,如果你喜欢阅读我的故事,并希望支持我的写作,请考虑成为一个媒体成员。每月支付 5 美元,你就可以无限制地阅读媒体上的故事。

欢迎在媒体推特YouTube 上关注我,或者在 LinkedIn 上打招呼。讨论人工智能、人工智能、数据科学、自然语言处理和人工智能是一种乐趣!

在数据科学领域取得成功最重要的软技能

原文:https://towardsdatascience.com/most-essential-soft-skill-to-exceed-in-data-science-60be3f2af965

实现您的分析项目真正价值的最佳实践

说到数据科学家这个职位,众所周知,他们是精通技术的人。主要是因为数据科学家应用各种数学和分析技术来从数据中获得洞察力。然而,很少有人谈论数据科学软技能,根据我自己的经验,这与技术技能一样重要,如果不是更重要的话。

在这篇文章中,我将分享最重要的软技能——向利益相关者、领导或高管沟通和展示你的工作!为什么我认为这是最重要的?好吧,不管你的技术技能有多好,如果你不能与他人交流,你就不能实现你的分析的真正价值。

数据科学的利益相关者

数据科学团队的利益相关者通常分为四个主要类别,

  • 利益相关方群体#1 客户和客户,尤其是在咨询领域工作的数据科学家,您和您的团队可能会定期与客户召开会议,让他们了解最新的分析项目。目的是让他们参与治理和决策过程。
  • 利益相关方集团#2 贵公司的行政领导。对于数据科学团队,我们需要确保我们的数据分析与企业战略保持一致。
  • 利益相关者组织#3 业务运营合作伙伴。例如,客户服务团队通常不精通技术;然而,他们拥有非常丰富的领域知识,这就要求他们对数据科学团队可以在业务的哪些方面进行改进有好的想法。
  • 利益主体组织#4 IT 和数据架构师团队,他们也是我们的合作伙伴。数据科学团队与他们一起工作,构建交付渠道,为业务决策流程提供支持。

***更新:你也可以在这里看我频道的视频版🎥,

显然,数据科学家与不同背景的不同团队交流。最常见的情况是,我们在跨职能的环境中工作,我们的沟通和演示必须能够将分析结果传达给每个人,这一点很重要。这为我们的下一部分提供了一个良好的开端。

准备向利益相关方进行分析演示的技巧

首先也是最重要的,在开始准备之前,确保你了解 你的听众和这次演讲的目的

  • 对于技术人来说,有时候需要讲术语、术语,甚至编程代码。这样做将有助于他们更好地了解你在努力实现什么,以及他们可能会做出多大的贡献。然而,对于不太懂技术的人,在一个非常高的层次上谈论你的方法论就足够了;
  • 至于目标,如果演示是为了将分析结果传达给运营团队,那么我会使结果更具可操作性,并且还会将业务影响呈现出来。一般的经验法则是强调商业价值,同时用数据来加强你的分析,但是要非常简洁;

话虽如此,我也准备用通俗易懂的英语解释某些技术概念。举个例子,我们可以用下山的类比来解释梯度下降算法,其中(山的)底部点是我们的预测误差达到最小的地方。

另一个有用的准备技巧是在演讲之前把你的提纲发过来。它不必是什么花哨的东西,所需要的只是关于你要展示的内容的要点以及关键结论和要点

最后一个准备技巧是从技术角度来看的,确保你的代码、原型或 MVP 工作正常。想想看,有多少次您将要演示的分析产品突然停止工作了?还有,这总是发生在你开会之前,不是吗?因此,对您的代码或 MVP 进行双重检查为成功的交流和演示增加了一层额外的安全保障。

现在,与顾客或客户相反,他们联系数据科学家时已经想到了他们的任务或问题,我们的执行领导是一个特殊的利益相关者群体;因为在开始任何分析项目之前,我们需要首先获得他们的认可。

获得领导认同的最佳实践

1。确定动机

向高管推销分析想法时,作为第一步,我们应该主动确定他们的真正动机,以及我们的数据分析对他们的目标有多大影响。例如,如果行政领导关心的是如何防止信用卡欺诈,那么建立一个图像处理模型将超出他们的兴趣,不管它可能有多准确!这意味着,作为数据科学家,除了技术细节,我们还必须关注组织的全局。

2。业务绩效指标

一旦我们理解了整个难题,我们将设置清晰的项目计划、执行管道,以及业务性能指标,这些指标通常不同于构建模型本身的模型评估指标。

例如,我将总结这种预测模型可能带来的潜在财务节约或财务收益,而不是展示您的模型准确性或您的错误率有多低。基本上,这些数字是激励高管实际买入的原因!

3。展示您的原型并解释您的模型

当我们在模型开发过程中取得一些进展后,我会展示一个工作原型。根据具体的项目,它可以是简单的概括我们初步发现的可视化,或者,它可以是一个带有可变解释的基线模型。通过从业务角度展示驱动因素,我们将增加获得领导层认同的可能性。

4。让反馈回路运转起来

最后但同样重要的一点是,我们需要经常不断地从高管那里收集反馈或意见;此外,确保管理他们的期望,因为我们不想过度宣传项目。教育高管,数据科学项目更像是一个迭代实验!

重要的是,他们要明白没有什么是完美的,数据科学之旅也不例外!从长远来看,让他们参与进来将有助于建立信任文化。

结论

技术技能对于成为一名优秀的数据科学家非常重要,但是,要真正为我们的组织提供最大的分析价值,数据科学家需要发展他们的软技能,如有效的沟通和演示!

希望你喜欢这篇文章,并从这篇文章中得到一些启发!

想要更多数据科学和编程技巧?使用 我的链接 注册 Medium,获得我所有内容的全部访问权限。

还订阅我新创建的频道 《数据与吉谈》 😀

更多有趣的文章

</6-sql-tricks-every-data-scientist-should-know-f84be499aea5> https://levelup.gitconnected.com/6-hilarious-programmers-data-scientists-jokes-to-kick-start-2021-187f86dd6a4c

贝叶斯定理的简单直观解释

原文:https://towardsdatascience.com/most-intuitive-explanation-of-bayes-theorem-b19eb168555b

用一盒巧克力理解数据科学中的一个重要定理

杰西卡·约翰斯顿在 Unsplash 上的照片

每个从事统计和数据科学的人都知道贝叶斯定理。如果你不知道,那么你一定要了解它。

很久以前我自己也努力去理解它的数学。

相信我,看数学公式和计算不会让它更容易理解。

但是,有一个简单而直观的方法来理解它。

让我们开门见山,看看如何!

想象一下这个场景

有个浪漫的家伙斯科特给他的女朋友简买了一盒巧克力。

盒子里有两种口味——5 种是焦糖口味,20 种是奶糖口味。

巧克力外面有相同颜色的涂层,你真的看不出哪种口味是哪种。

作为顽皮的一对,斯科特抱着琼,从盒子里随意拿起巧克力,放进她的嘴里。

让惊叹道:

嗯,美味可口的焦糖,我最喜欢的味道!

但是斯科特知道吉恩通常不善于辨别味道。

  • 她 80% 的时间 正确识别焦糖为焦糖。
  • 但只有 60%的时间她可以说这不是焦糖,而实际上这不是 T21。

斯科特想知道——她真的得到焦糖味巧克力的几率有多大?

他轻松计算出她实际吃焦糖的几率是 的三分之一(33.33%)。

究竟如何?!

你也能这么快吗?

这真的很容易。

但是在我们深入到斯科特使用它的直观方式之前,让我们先看看数学。

贝叶斯定理

贝叶斯定理是统计、概率和数据科学领域中最重要的定理之一。

所以,让我们戴上数学眼镜。

我想,所有读到这篇文章的人都熟悉概率。

从盒子里得到焦糖的几率或者得到焦糖的概率:

这叫做先验概率。

接下来,我们可以把 Jean 想象成一台有一定容错能力的测试机器。

比方说,阳性测试结果意味着焦糖口味被识别。

当它真的是焦糖时,机器检测为阳性的几率:

这被称为测试的灵敏度

现在,当它不是焦糖的时候,机器测试为阴性的几率是:

这被称为测试的特异性

假设测试结果为阳性,我们最终需要计算的是得到焦糖的概率。

根据贝叶斯定理,计算如下:

我们得到 P(+ve) ,即通过加上用实际焦糖得到阳性结果的几率即使不是焦糖也得到阳性结果的几率,测试为阳性的总概率。

数学上:

代入贝叶斯公式:

这给出了 0.3333

33.33%

这也叫后验概率。

但是你可能想知道:

几率怎么会这么少,也就是 33.33%?

其实比 Jean 猜对焦糖 80%的命中率低多了

这是因为挑选焦糖本身的几率很小(25 块巧克力中只有 5 块)。这影响到 Jean 猜对的最终几率。

还有那个朋友,是贝叶斯定理!

如果我们用文字来表达数学,

贝叶斯定理描述了一个事件发生的概率,基于可能与该事件相关的条件的先验知识

现在让我们试着更直观地理解一下

直观的解释

有一种简单的方法来看待 Scott 的问题,他想知道 Jean 实际上有焦糖的几率是多少。

有 25 种巧克力,其中 5 种是焦糖味的。

Jean 在 80%的情况下都能正确检测出焦糖:

  • 那是 5 块焦糖巧克力中的 4 块。

剩下的 20 块巧克力是奶糖味的。

珍有 60%的可能说它不是焦糖,而实际上它不是——所以她很可能会把 40%的奶油糖果巧克力误认为是焦糖!

  • 那是 20 颗奶糖巧克力中的 8 颗。

所以我们要关心的总样本只有 4+8 ,也就是 12

现在,鉴于她兴奋地声称焦糖是焦糖,她真的吃了焦糖的可能性有多大?

简单— 她能从关注的全部样本中正确猜出焦糖的次数 ,即 4/12 简化为三分之一或:

33.33%

没错,就是这么简单!

你只是非常直观地计算了贝叶斯概率,而不需要数学公式!

如果你很好地了解它,你将永远拥有优势。

因为它有很多现实世界的优势!

贝叶斯定理的好处

除了计算你的女朋友得到正确口味巧克力的几率之外,这个定理还被用在了几个高层次的问题上!

  • 测试疫苗的有效性
  • 识别疾病的医学诊断
  • 根据证据识别罪犯
  • 在约会应用程序上找到合适的人(也许不适合斯科特,因为珍喜欢巧克力**
  • 几种预测应用

贝叶斯定理在机器学习中非常重要,因为它可以对不确定的信息进行推理,从而让计算机根据概率做出正确的决定。

这使得它成为数据科学和统计学中的重要支柱之一。

一些你现在能真正理解的事情!

我希望你像我喜欢写这篇文章一样喜欢这篇文章的解释。

敬请关注更多内容!

干杯!

注释和来源:

  • 所有未加字幕的图像都是作者创作的
  • 1来自维基百科的描述

山地车分类分析

原文:https://towardsdatascience.com/mountain-bike-categorization-analysis-61eb1d8bb259

山地车(MTB)的规格是否足以区分不同类型的山地车类别?

贾斯汀·舒尔伯格和迈克·切尔文斯基

照片由扬·kopřivaUnsplash 上拍摄

概观

对于这篇文章,我与 Mike Czerwinski 一起确定山地车(MTB)的规格是否足以区分不同类型的山地车类别。

目前,全悬挂山地车分为 5 类:

  • 越野(XC) |往往是最轻便、最灵活的车型,旨在让骑手处于高效的踩踏位置
  • Enduro (EN) |更重的车架、更大的行驶距离和更适合下坡的几何形状
  • Trail (TR) |最常见的自行车种类,被认为是 XC 和耐力赛的中间点
  • All Mountain (AM) |这是一个更小众的类别,一些制造商声称它比越野自行车更注重下坡,但不是像 Enduro 自行车那样为下坡比赛而设计的
  • Downcountry (DC) |介于 XC 和特雷尔之间的一个相对较新的类别。类似于所有的山地类,这些自行车不像 XC 自行车往往是特定的比赛,但比越野自行车更轻,更快。

设计自行车时要考虑的所有因素,这些类别之间没有明确的界限。例如,一个品牌的越野自行车可能是另一个品牌认为的越野自行车。流行的山地自行车网站 PinkBike 对所有类别的许多自行车进行了深入的分析,并涵盖了自行车应该归类为哪个类别以及多少类别才足够的主题,如这里的视频所示。

此分析的目标是确定山地自行车应存在多少(如果有的话)独立类别。

由于大多数规格和几何尺寸在自行车系列中有一个方向,因此有理由相信这些尺寸可以减少到更少的维度,甚至可能是一个连续的主要组成部分,而不是离散的类别。我们还可以根据这些规格和几何尺寸将自行车组合在一起。

例如,您可以在这里找到一些不同类型的山地车几何规格图。

数据

这些数据是从每个山地车公司的网站上手动获取的。这个分析的所有数据和相关代码都可以在团队相应的 GitHub 资源库中获得。我们来看看数据。

作者图片

电子设计自动化(Electronic Design Automation)

在这一节中,我们将看看数据集中的 74 辆山地自行车和 27 个特征中的一些。我们将尝试根据label来分解我们对数据的理解,这是我们作为每辆山地车类别的目标变量。

标签(山地车类别)

如前所述,我们的数据集中有 5 个山地车类别:

  1. 越野赛
  2. 耐力赛
  3. 拖车(tr)
  4. 所有山(上午)
  5. 市中心

让我们来看看在我们的数据集中每一种有多少。

作者图片

我们看到,在我们的 74 辆自行车中,大多数都是越野自行车,最小的自行车组都是山地自行车。

分类变量

为了更好地理解我们的数据,我们将看看 4 个分类变量:

  1. 环境
  2. 大小
  3. 前活塞(f_piston)
  4. 后活塞(r_piston

作者图片

  • 我们看到,只有少数自行车有一个设置值,这是一个功能,允许骑车人稍微调整框架的几何形状,以提高骑车人的舒适度。稍后,我们将根据同一辆自行车的设置进行分组,并对结果进行平均,以获得自行车规格的更准确表示。
  • 大多数分析的自行车有 4 个后/前活塞。这两个变量似乎完全同步,让我们相信它们高度相关。

但是,实际上,我们关心的是理解这些不同的变量如何与我们的目标变量label相互作用。让我们看看它们的分布,并寻找任何模式。

作者图片

这里我们看到:

  • 大多数自行车的尺寸分布相当均匀。在很大程度上,我们试图找到适合本报告作者身高的自行车(大约。5 英尺 8 英寸-5 英尺 11 英寸),这往往是大型自行车;然而,对于一些自行车,如 Trail,我们从中提取数据的特定自行车公司网站推荐了一辆中型自行车。
  • 虽然大多数自行车有 4 活塞制动器,但在有 2 个活塞的自行车中,大多数是越野( xc )自行车。众所周知,4 活塞制动器具有更高的制动力,这在骑车人越想下坡时就越重要。然而,它们以额外的重量为代价,这是大多数 XC 车手会不惜一切代价避免的。

连续变量

为了分析数据集中的连续要素,我们为每个要素构建了密度图,以更好地了解它们的分布。

作者图片

作者图片

~正态分布变量:

  • 链条长度
  • 叉 _ 旅行
  • Bb _ 高度
  • 座椅 _ 角度

偏斜变量:

  • 头部 _ 角度(向右倾斜)
  • 车把 _ 宽度(向左倾斜)
  • 轴距(向左倾斜)

多模态分布变量:

  • f _ 转子 _ 尺寸/r _ 转子 _ 尺寸
  • 茎长度

就像我们对连续变量所做的那样,让我们通过我们的目标变量label来观察这些预测值的分布,以寻找任何可辨别的模式。

作者图片

作者图片

这里我们看到:

  • 与其他自行车相比,越野( xc )自行车往往具有最大的头部角度和最小的座位角度。它们的茎长也是最长的,相差很大。总体而言,越野自行车往往是最有别于其他自行车类别;
  • 所有的山地车( am )都有一个明显更小的高度,和耐力赛( en )自行车一起,比其他种类的自行车有更大的覆盖范围;
  • 正如人们普遍预期的那样,对于这些连续变量中的大部分,越野自行车往往最适合处于中间位置。这是有道理的,因为他们倾向于区分越野自行车和耐力自行车。

通过倒装芯片设置平均自行车

因为一些自行车的网站对于相同尺寸的自行车会有两种不同的“设置”,所以我们选择包含这两种选项,并将两者平均在一起,以获得该类型自行车的一个中间值。我们最终对数据集中 47%的自行车执行了这个操作。

方法学

现在我们对山地车数据集有了更好的了解,我们将制定一个计划来证明以下假设:

为此,我们将:

  • 尝试使用各种方法来减少功能集,看看是否有某些变量可以更好地用于区分不同的山地车类别。
  • 应用各种聚类和分类算法,包括 K-Means 聚类,高斯混合模型和多类支持向量机,以反驳 5 种不同类别的山地车存在的概念。

功能集之间的差异

我们要做的第一件事是查看数据集中的任何要素是否比其他要素更能解释不同自行车之间的差异。也就是说,完全有可能两个特征是相似的,没有太大的差异,即使在一些不同的自行车类别中也是如此。为此,我们将:

  1. 寻找高度相关的特征,并标记这些特征以进行潜在的移除;
  2. 运行主成分分析(PCA ),看看某些特征是否比其他特征更能解释数据中的变化。

1.相互关系

首先,让我们来看看我们最高度相关的功能。我们将使用corrplot()函数按照特征向量的角度顺序对高度相关的特征进行更好的排序。

作者图片

这里我们看到一些明显的相关性,例如:

  • f_piston(前制动器)与r_piston(后制动器)完美关联,这是有道理的,因为山地车往往对前轮和后轮使用相同类型/规格的制动器。
  • fork_travel与:c("后 _ 行程","叉 _ 行程")的相关性在. 95 以上。这是有道理的;比如rear_travel 应该是fork_travel高度相关。

总之,以下是相关性最高的变量(即相关性高于. 9 或低于. 9 的变量):

作者图片

这是一个很大的数目,特别是考虑到我们的数据集中只有 18 个连续的列!目前,我们将选择包含所有内容。但是稍后,当我们分析不同特性的重要性时,我们将首先去除上面的一些变量。

2.主成分分析

接下来,我们将对数据集应用 PCA。在这样做的时候,我们将不得不集中和调整我们的数据,因为某些测量的范围是如此的不同。让我们来看看解释数据中最大方差比例的 5 个主成分:

作者图片

作者图片

我们可以看到,实际上,我们的第一个主成分解释了超过一半的数据。从第二个主分量开始,有一个明显的拐点。之后,我们有一个巨大的下降。从我们的第 5 个主成分开始,近 82.4%的数据变异得到了合理的解释。这让我们相信,我们数据中的大部分变化都可以用一个主成分来解释!

让我们来看看我们的两大主要因素是如何解释 5 种不同的山地车类别的:

作者图片

这里我们可以看到,我们的前 2 个主成分,解释了我们数据中大约 63.2%的变化,已经是描述我们数据集中不同成分的很好的表示。即便如此,这些分组在二维图上还是清晰地标出来了,很容易看出不同的自行车类别(用颜色表示)是如何用我们现有数据的线性变换来解释的。

使聚集

因为我们正在调查山地自行车类别的有效性,一种方法是将该数据集视为无监督的,剥离自行车的label,并查看各种聚类算法是否可以重新创建 5 个不同的label。为此,我们将了解以下算法:

  • k 均值
  • 高斯混合模型(GMM)
  • 支持向量机(SVM)

k 均值

我们将从使用 K-Means 聚类算法开始,查看不同数量的聚类( k )并查看自行车是否逻辑地分组在一起。

作者图片

作者图片

上面,我们试图用我们数据中的前 2 个主成分来绘制 3 个聚类图。例如,我们可以在图表的右侧看到聚类#1,主要由越野自行车(图表中的三角形)和一些乡村自行车(用正方形表示)组成。越野自行车似乎也是第二类(灰色点)的一部分,还有越野自行车(用带“x”的方块表示)和一些耐力自行车(用“+”表示)。然而,越野自行车和大多数耐力自行车也在第三组中占有重要地位。

总的来说,很明显,我们的集群之间有明显的重叠,主要是沿着主成分 1 轴;相信我们的自行车可以在一个单一的、连续的范围内被区分开来。

注意:在图的右下方( *PC2 < -4* ),我们看到两辆 Niner 自行车,几乎充当离群值。对于一个 5 英尺 10 英寸的骑手 Niner 建议一个中等大小,这导致其自行车上的低接触数。从早期的 PCA 图中,我们看到 Reach 与 PC2 非常一致,因此这些自行车在视觉上显得较低。

高斯混合模型(GMM)

在这一节中,我们将采用一个更具概率性的模型来进行聚类。也就是说,我们将使用高斯混合模型(GMM)在山地车数据集中构建正态分布的子分组,其中每个子分组的密度代表一辆自行车属于该子分组的概率。与更基于质心的聚类方法 K-Means 不同,GMM 更基于分布的聚类方法。

一般来说,我们希望看到如下内容:

作者图片

其中,给定一种特定类型的自行车,我们可以预测一辆自行车属于类似于Cross Country (xc) vs. Trail vs. Enduro的类别的概率 p(x)。

我们将在 R 中运行ClusterR::GMM()函数来计算出最佳的集群数量。它使用期望最大化算法进行概率聚类;在每次迭代中,它的目标是最大化贝叶斯信息准则(BIC ),以确定最佳的聚类数。

## iteration: 1  num-clusters: 1
## iteration: 2  num-clusters: 2
## iteration: 3  num-clusters: 3
## iteration: 4  num-clusters: 4
## iteration: 5  num-clusters: 5
## iteration: 6  num-clusters: 6
## iteration: 7  num-clusters: 7
## iteration: 8  num-clusters: 8
## iteration: 9  num-clusters: 9
## iteration: 10  num-clusters: 10

作者图片

从上面的图中,我们看到,随着集群数量的增加,BIC 值一般会降低。然而,看来第一次大跌是在clusters = 3的时候。让我们尝试一下这个值,看看有哪些自行车类别被映射到 3 个分类中。

GMM — 3 个集群

GMM — 3 个集群

在这里,我们可以看到预测的分类标签以及数据中实际的 5 个自行车类别。越野和耐力自行车主要被归为第 1 和第 2 类,而越野和越野自行车被归为第 3 类。这将使我们相信,最初的 5 个自行车类别可以用更少的聚类来充分解释。

即便如此,让我们通过查看属于某个分类标签的自行车的每个关联概率的密度,来看看每辆自行车属于某个分类的概率是如何出现的。

作者图片

上面,我们看到了与预测正确的类标签相关联的预期概率。也就是说,左边的图显示了 3-聚类高斯混合模型对于预测适合聚类#1 的自行车的准确度。一般来说,预测的概率都在 0.95 以上;也就是说,GMM 非常有信心将不同的自行车分成这三组。

GMM——6 个集群

之前 BIC 图表中的下一个逻辑“拐点”在 6 个集群处。出于演示的目的,我们也展示一下这是什么样子。

GMM——6 个集群

在这里,我们可以看到 6 个预测的分类标签以及数据中实际的 5 个自行车类别。这里有一些比我们之前看到的更清晰的可分性。例如,集群 2 仅由两辆耐力自行车组成,集群 5 仅由越野自行车组成。然而,耐力自行车在混合组 1 中占据显著位置;越野自行车在第 4 组和第 6 组中略有特色。总的来说,与使用 3 个集群相比,使用 6 个集群会混淆分组。

多级 SVM

如果我们采取稍微不同的方法,选择将我们预先分配的label视为真理,那么我们可以将这种分析视为监督学习问题。

在本节中,我们选择将 All Mountain 和 Enduro 分类,因为它们在之前的 PCA 图表中完全重叠。我们还选择切换 Downcountry 类别的分类,将其作为一个单独的类别,并将其与 Trail 和 XC 分组,以试验模型的结果。

我们选择使用多类支持向量机(SVM)和网格搜索来调整核函数和γ值。对于每组参数,我们对所有数据行进行了 10 重交叉验证。我们决定不将数据作为测试集,因为我们的数据如此有限,10 倍 CV 应该评估模型在盲数据上的表现。

使用所有的数据,最好的 SVM 模型是 73%准确,使用γ=2.595024 的径向基核函数。

将县以下类别视为 XC,最佳模型的准确率为 81.6%,径向基核函数的γ=0.02983647。

将县以下类别视为 Trail,使用γ=3.764936 的径向基核函数,最佳模型的准确率为 80.0%。

从逻辑上讲,将越野自行车与它们相邻的类别组合在一起自然会提高性能;然而,这可能表明,内地类别更倾向于 XC 自行车。

为了可视化 SVM 分类器的结果,我们将所有特征映射到 2 个主成分。通过这样做,我们能够使用线性核达到 75%的更高精度,同时继续将县以下类别视为其自己的独特类别。

SVM — 4 个标签

在上图中,我们可以看到 SVM 是如何将自行车类别的实际标签紧密地映射到它们的预测类别的。存在一些错误分类(当不同颜色的点出现在不同的阴影区域时),但总体而言,阴影 SVM 特征空间似乎是自行车类别的准确表示。

一个有趣的观察是,大多数边界线或多或少是垂直的,这表明类之间的大多数变化是沿着主成分 1 的。我们看到了 XC 和下郡边界之间的这种偏离,然而这种边界的有效性仍然是个问题,因为下郡类别本身或多或少是非正式的。让我们尝试将越野自行车重新映射到越野(XC)类别,并查看更新后的结果。

SVM — 3 个标签

将所有的乡村自行车映射到 XC,边界变得几乎完全垂直,这再次表明自行车的分类可以归因于主成分 1。不同的区域似乎是 3 种自行车类别的准确预测器,并且易于解释,这意味着我们已经有效地减少了模型中的偏差。

调查的结果

  • 所有的结果表明,试图离散分类全悬挂山地车或多或少是任意的。
  • 山地车的分类应被视为一个连续的规模,越野(XC)自行车在一端,耐力赛(EN)自行车在另一端。
  • 要获得特定自行车在该比例上的位置,可以使用自行车规格和第一主成分的线性组合。
  • 这种新的测绘自行车系列可以为自行车制造商和消费者提供一种量化自行车骑行时操控性能的方法。

让我们从上面的数据中来看一个例子。一些自行车公司,如 Transition 和 Revel,并不像其他公司那样明确地对他们的自行车进行分类。对于这些品牌,我们根据一般属性以及媒体对它们的报道对它们进行了分类。让我们来看看狂欢游侠:

漂亮的自行车!

狂欢游侠规格

转换为输入向量:

Revel Ranger w/ PCA

将 Revel Ranger 映射到其第一个主成分,我们得到的值为 1.7,这使我们正好在 Trail 和 XC 之间的边界附近,这与概述部分提到的 PinkBike 编辑在 视频 中对 Downcountry 的标注一致。

改进分析的机会

本演示文稿和即将发布的报告中包含了一些改进分析的机会:

  • 包含更多的自行车(行) |我们可以做出的最明显的改进是向我们的数据集添加更多的数据点。每辆自行车的数据都是由该报告的作者之一手工输入的。在输入大多数主要自行车品牌的自行车数据后,我们有了足够的数据来准确地可视化不同的自行车类别;然而,随着更多的自行车,我们的算法将变得更加稳健,受离群值的影响更小。
  • 包含更多自行车特征(专栏) |尽管我们包含了所分析自行车的最有意义的规格/几何形状,但还有许多其他更小的特征可用于区分不同类型的自行车。
  • 包括所有尺寸的自行车 |我们选择使用与 5 英尺 10 英寸的骑手相对应的尺寸,但一些自行车制造商可能会将其解释为中号,而另一些制造商会将其解释为大号。
  • 包括多年的自行车 |随着自行车趋势的缓慢变化,观察数据如何随时间变化会很有趣。例如,在自行车的一次迭代中,落基山元素将其头部角度从 70 度减小到 65.8 度。包括过去几年的数据可以提供有价值的市场洞察,了解整个行业的发展趋势。

杂项

这篇文章的范围是用 r 写的。完整的博文可以在我的博客网站这里找到。源代码、数据等。可以在迈克的 GitHub 上找到这里。如果你想获得改进的想法/反馈/想法,请在这篇文章上留下评论,或者在我的博客网站上找到我的联系方式。

除非另有说明,本文所有图片均由作者创作。

使用这些 Julia 软件包将数据科学迁移到 REPL

原文:https://towardsdatascience.com/move-data-science-to-the-repl-with-these-julia-packages-d9dd739bfaf1

为了朱莉娅,抛弃朱庇特的一些最好的工具

能够动态操作和处理代码内类型的反应式环境无疑是大多数数据科学家工作所需要的。虽然许多科学家可能会在他们基于 web 的笔记本服务器中逗留,但也有许多数据科学家在读取评估打印循环或 REPLs 中完成大部分或全部工作。这当然有一些好处。笔记本有一些数据科学家经常强调的基本问题。首先,典型笔记本会话的状态可以在没有视觉反馈的情况下改变,屏幕上的代码不需要与应用程序的状态实际对应,这可能会有问题。另一个重要的问题是内核,简单地说,这是因为很难获得很好的性能,或者当内核不断有死亡的可能性时,不用担心超时。尽管内核的抽象层很棒,但它仍然带来了一些缺点。

另一方面,REPLs 通常不会遇到这类问题,因为它们通常是直接的输入和输出流。总的来说,除了使用 REPL 实际编程之外,朱莉娅·REPL 对各种不同的事情都非常有用。特别是在 Julia 的案例中,REPL 经常专门致力于添加包和创建项目环境。

与其他类似的语言相比,比如 Python,Julia 有一个非常棒的 REPL,有很多不同的特性。作为一名 Julia 程序员,我经常发现 REPL 能够方便我的大部分工作。在 REPL 工作时,总是在目录周围 CD 和使用 readdir()是很烦人的,所以 Julia 用 pressing 覆盖了你;进入巴什或批处理 REPL,这是如此方便,几乎感觉犯罪。需要加个包?按]并输入 Pkg REPL,不需要导入甚至键入 Julia 语法——您可以只键入简单的命令和参数,就像它是 Bash 一样。

因此,朱莉娅·REPL 的出色是不争的事实。更棒的是,REPL 只是一个 Julia 包,这意味着你可以查看它的代码并操作它…或者在某些情况下,你可以直接使用它来产生完全独特的结果!后者是 Julia 生态系统中一些非常棒的软件包的情况,它们可以非常有效地最大化您在 Julia 的 REPL 体验。当然,总有一个问题,为什么在 REPL 工作而不是在笔记本上?

UnicodePlots

(图片由作者提供)

我想在这个列表中展示的第一个包是 UnicodePlots.jl。该软件包有一套通用的功能,客观上真的很酷。该模块具有各种统计和其他可视化功能,这意味着如果他们真的想的话,甚至可以完全在 REPL 外工作并进行数据科学研究。如果您想进一步了解如何实际使用该模块,我有一篇完整的文章,您可以在这里阅读:

关于这个模块,我注意到了几件事。首先,假设可视化是 Unicode 的,它们比典型的可视化更加通用。首先,阅读和重新解释这些数据要容易得多。更进一步说,这可以在任何可以使用文本的地方使用,基本上是在任何地方。这意味着您可以在 REPL 或任何可以打印文本的地方可视化您的数据。在正常情况下,为了查看可视化效果,您需要显示一个图像。这意味着通过 REPL 和其他基于文本的解决方案,通过可视化进行分析是完全可能的。

与传统的可视化库相比,该模块的另一个巨大优势是性能明显更好。该软件包也是相对良好的编程和 API 非常容易使用。也就是说,如果你想把你的数据科学经验带到朱莉娅·REPL,这可能是一个值得尝试的有效选择!

蜡笔

(图片由作者提供)

Crayons.jl 包是一个非常基本的包,它提供了一个可以修改的类型,以及像 print()和 write()这样的函数的调度。Crayons.jl 允许你改变 REPL 中不同字符串的颜色。这在许多不同的情况下都很有用。实际上,我最近在自己的模块 Toolips.jl 上使用了蜡笔模块。蜡笔模块用于帮助演示终端内部的日志级别,下面是实现这一功能的代码:

println(Crayon(foreground = :light_gray, bold = true), "[", levels[level],
     string(time), Crayon(foreground = :light_gray, bold = true), "]: ",
     message)

这里被索引的“级别”实际上调用了一个蜡笔字典,其中的键对应于不同的蜡笔配置。

复制器

另一个很棒的包是 REPLMaker.jl,这个模块可以让你制作自己的 Julia REPLs。这非常容易做到,真正需要的只是一些解释字符串的函数。例如,我们可以从文档中的朱莉娅·REPL 内部创建一个 Lisp REPL:

using LispSyntax, ReplMaker
using REPL: REPL, LineEdit; using LispSyntax: ParserCombinator

lisp_parser = LispSyntax.lisp_eval_helper

function valid_sexpr(s)
  try
    LispSyntax.read(String(take!(copy(LineEdit.buffer(s)))))
    true
  catch err
    isa(err, ParserCombinator.ParserException) || rethrow(err)
    false
  end
end

然后,我们只需使用 initrepl()方法完成它。

initrepl(LispSyntax.lisp_eval_helper,
                valid_input_checker=valid_sexpr,
                prompt_text="λ> ",
                prompt_color=:red,
                start_key=")",
                mode_name="Lisp Mode")

这不仅很棒,而且非常有用。虽然它可以是一个有趣的玩具,但您也可以制作自己的 REPLs 来完成各种任务,这意味着您可以轻松地用最少的努力获得某个版本的用户界面,而不需要遵循 Julian 语法。虽然这看起来不像是一个能让你远离网络浏览器和笔记本电脑的属性,但它实际上可以达到一些可能的目的。使用这个包,您可以创建一个 REPL 来做任何事情,例如,如果您想在 Julia 中使用 conda 包管理器,或者在 Julia 中使用 Pip(虽然您可以通过输入 Bash 的 REPL 来完成这两个任务;。)

OhMyREPL

我想谈论的朱莉娅·REPL 的下一个很酷的包是 OhMyREPL.jl。如果你真的想用朱莉娅·REPL 取代 IDE 或笔记本,OhMyREPL.jl 是一个非常重要的模块。最重要的是,一般来说,在你的 REPL 里有这样的东西是件好事。该模块附带了一系列工具,使 REPL 更适合作为 IDE,最值得注意的可能是语法高亮。它还允许自动完成分隔符,如“,”分隔符,这通常由优秀的文本编辑器提供。

很容易理解为什么人们想要在朱莉娅·REPL 的程序中突出显示语法和这种自动格式化。对于那些想用 REPL 取代笔记本的人来说尤其如此。我要注意的一点是,如果你的终端有一个浅背景,就像我的一样,语法高亮可能与你的配色方案不一致,就像我的情况一样。要解决这个问题,您可以随时重写蜡笔,并从本质上制作您自己的 OhMyREPL 版本。总的来说,这个包非常棒,如果你想提高你在 REPL 的开发体验,它绝对是必须的。

Revise.jl

另一个可能彻底改变你使用朱莉娅·REPL 的方式的包是 Revise.jl。不能使用内核的问题是,当状态变得混乱时,你几乎必须命令+D 你的整个会话,因此;你会失去不同的你不想失去的东西。最重要的是,很难在 REPL 的文本文件中工作。通常,REPL 工作不是保存的工作。但是,Revise.jl 模块允许您动态地更改代码。

这样做的好处是,您不再需要编辑文件,然后跳回 REPL,然后编辑文件,然后重新启动您的 REPL,找到您的环境,然后编辑更多的文件。所有这些听起来都很乏味,Revise 巧妙地解决了这个问题,它允许您在环境中用新的字段重新定义名称。不用说,如果您想利用声明性的特性来改变您想要的任何代码段并独立地执行它,这可能是一条可行之路。对于数据科学来说,Revise.jl 绝对是进入 REPL 的一个必不可少的有价值的工具。

DrWatson.jl

今天我想看的最后一个模块是 DrWatson.jl. DrWatson 是 Scientific Julia 项目的项目组织者,工作非常出色。虽然它主要面向科学,但它肯定可以应用于人们决定在 Julia 中构建的任何软件。该项目也建立在 DataFrames.jl 之上,这是一个数据管理框架,大多数基于 Julia 的数据科学家可能都很熟悉。

DrWatson 能够防止加载错误,为您命名和运行不同的代码,并自动化许多我们作为软件开发人员经常遇到的小文件操作和更改。

还有— Startup.jl

在今天的专题节目《Julia 是如何定制的——Startup.jl 中,虽然你可以在不了解任何 Julia 的情况下编写自己独立版本的 Julia 及其 REPL 来完成任务,但你也可以使用 startup . JL 来避免使用与其他人不同版本的 Julia,但仍然可以获得定制的体验。无论 Julia 是否是 REPL,Startup.jl 中的代码都会在 Julia 启动时运行,所以这一点需要记住。

如果这个列表中有一个模块是我推荐放入他们的 startup.jl 文件中的,它可能是 OhMyREPL。原因是在 REPL 更好的语法理解只适用于 REPL 内部。因此,在其他地方工作时,已经加载的包不会导致很多问题。对于这个文件,Revise 可能也是一个不错的选择,但是如果有一个软件包是我推荐的,那就是 OhMyREPL。我也曾几次尝试在 startup.jl 中加入 REPL,这样我就可以在运行其他命令或使用类似命令的界面来完成某项计算机任务之间切换,但这对我来说从未奏效。

startup.jl 文件位于~/.julia/config/startup.jl中,或者 Julia 目录所在的任何地方。如果文件不存在,那也没关系,不用担心,您只需要创建一个文件。我无法告诉你当我第一次这么做时,我花了多长时间才明白。当然,那时关于朱莉娅的信息更少。

结论

我认为笔记本是数据科学的伟大工具,我喜欢使用它们。然而,我确实看到了 Jupyter 笔记本在再现性和状态方面可能存在的问题。也就是说,在笔记本上工作仍然有一些优势,但最棒的是你仍然有选择的自由。对于某些项目,REPL 可能更理想,对于其他项目,基于网络的笔记本服务器可能是唯一的选择。

在这种情况下,朱莉娅和朱莉娅 REPLs 的伟大之处在于,它可能是从事数据科学工作的最可行的副本之一。这有很多好处,例如 Revise.jl 和 DrWatson 的代码可再现性,以及快速方便地使用所有 REPL 和终端带来的额外便利。桌子的两边肯定都有理由支持用 REPL 替换笔记本和不要用 REPL 替换笔记本。不管你站在哪一边,或者如果你像我一样是不可知论者,并且两者都喜欢,我想我们都会同意,一个如此小的社区开发出如此棒的工具是非常酷的!

movie lens-1 米深潜—第一部分

原文:https://towardsdatascience.com/movielens-1m-deep-dive-part-i-8acfeda1ad4

使用流行的基准数据集的实践推荐系统之旅

照片来自 pexels

建议。我们都消费它们。无论是通过我们最喜欢的电影流媒体应用程序,在线购物,甚至被动地成为广告活动的目标。这些推荐是如何产生的?一个推荐系统如何利用互联网交易的巨大数据集来产生高质量和个性化的推荐?我发现这些问题很吸引人,因此我决定开始一段学习之旅,这篇文章旨在与你分享我的发现,亲爱的读者:)

我发现有足够多的关于推荐系统基础的理论博客帖子,并决定这将是一次关于最受欢迎的以推荐为重点的数据集之一的实践之旅— MovieLens-1M 1。在这个数据集中,我们得到了 6040 个独立用户对 2894 部电影的大约 100 万个历史评级。

该员额的结构如下:

  • 第一部分—探索性数据分析(EDA)
  • 第二部分—数据预处理(检索电影内容信息)
  • 第三部分—设置推荐问题并获得基线分数
  • 第四部分—使用协同过滤推荐电影
  • 第五部分—使用基于内容的过滤推荐电影
  • 第六部分——结论和展望

包含这个项目的所有代码和更多内容的笔记本可从这里获得:https://colab . research . Google . com/drive/132 N2 lgqtt 1 nnz au 6cg 1 ifwpaefkk 9 qqe?usp =分享

这是对 MovieLens-1M 的两部分深潜的第一部分。下一部分将于近期发布,将专注于更高级的算法,并继续永无止境的 EDA 进程。快乐阅读!

第一部分— EDA

首先,从这里下载 MovieLens-1M 数据集。然后,让我们做一些必要的导入,并为这个项目做好准备:

MovieLens-1M 数据集由 3 个文件组成— users.dat, ratings.datmovies.dat.让我们来研究其中的每个文件,并了解我们正在处理的内容。我们将从电影的数据开始。

让我们画出每种类型电影的分布图。为此,我们必须将“流派”列转换成一个列表并“展开”它。注意,每部电影可能是几个流派的,在这种情况下,它将被多次计数。我们还将绘制每部电影发行年份的电影分布图。

让我们转到用户数据并检查它。“职业”列用代表每个职业的数字编码,但是对于 EDA,我们感兴趣的是实际数据。我们将通过从数据集的自述文件中提取相关行并相应地交换数据集中的值来获得它。

现在让我们来看看收视率。

让我们根据电影的平均等级来给电影分类。我们还将绘制每种类型被评级的次数。

现在,让我们将所有三个表(电影、用户和收视率)结合起来,看看每种电影类型的男性和女性收视率之间是否有任何差异。请注意,这里我们也将根据男性/女性的总数进行标准化,以便更好地回答我们的问题。

同样,我们在这个数据集中看到了一些典型的男性/女性差异(男性对动作片的评分高于女性,反之亦然)。这是一个很好的数据完整性检查,因为这些图是有意义的。

第二部分—数据预处理(检索电影内容信息)

在这一部分中,我们将检索一个重要的数据源,它将帮助我们稍后推荐电影,这就是电影情节摘要。我们将使用维基百科 python 包从维基百科获得它们。

There are 615 NaN movie plots

我们发现我们无法获得 615 部电影的电影情节。这可能是因为他们的维基百科页面没有“情节”类别或其他原因。由于网页抓取不是这里的重点,我们将只放弃这些电影和所有与它们相关的评级。

第三部分—设置推荐问题并获得基线分数

我们将选择一个用户作为案例研究,我们将对其进行实际预测,看看这对我们是否有意义。最方便的是,我们将选择 ID #1 的用户。显示在下面的用户和她评价的电影中。我们可以看到她是一名女 K-12 学生(1 岁是数据中的一个错误,但我认为这无论如何都不是很重要)。她喜欢的前 20 部电影主要是经典的儿童热门电影,但也有一些异常的地方(《辛德勒的名单》、《飞越疯人院》)。

我们还将关注电影《玩具总动员》(1995),对于每种预测方法,我们将检查哪些电影与嵌入空间中的这部电影最接近。

最后,开始推荐素材吧!我们将创建两个数据集分割:

1.标准训练/测试分割。这将用于评级预测回归任务

2.留一交叉验证分割。这将用于命中率预测(这通常被认为是推荐系统领域中更相关的度量)。通过从数据集中为每个用户取出一部电影并将其用作训练集来执行分割。所有其他用户的电影都将在列车上。然后,点击率是我们取出的电影在该用户的前 K 收视率中的百分比。

对于每种方法,我们将计算:

  • 测试装置上的 RMSE
  • 留一交叉验证集的命中率
  • 用户#1 的最佳预测(案例研究)
  • 与《玩具总动员》(1995)最相似的十部电影

现在,让我们定义一些辅助函数来帮助我们评估我们的算法(主要是—命中率)。我们将考察每种算法的两个衡量指标——RMSE 和命中率。这里的一些代码摘自 Sundog Education 的精彩推荐系统课程。我还将对最初的 KNN 和奇异值分解算法进行简单的调整,因为计算命中率需要很长时间,我希望至少能够实时测量进度。

让我们获得一个完全随机的推荐系统的结果,这样我们将能够评估我们的算法是比随机的更好还是更差(毕竟,我们希望事情变得更好而不是更差……)

  • 注意—计算命中率需要很长时间
RMSE: 1.4960
HitRate: 0.02185430463576159

第四部分—使用协同过滤推荐电影

我们将使用经典的香草算法,从惊喜包中开箱即用(有一些小的调整,以显示在 KNN 的进展)。我不会在这里深入算法细节,因为我认为有很多很好的教程可以学习这些东西(只要谷歌推荐系统 SVD/KNN,你就可以开始了)。我将检查这些算法:

  1. 德拉贡诺夫狙击步枪(Snayperskaya Vinyovka Dragunov 的缩写)
  2. 基于用户的 KNN
  3. 基于项目的 KNN

我们从奇异值分解开始:

RMSE: 0.8809
HitRate: 0.03923841059602649

不错!这是对随机分数的一个重大改进。但是当我们看用户#1 的预测时,他们仍然感觉有点“不对”。唯一的儿童电影是《宝贝》( 1995 年),我个人希望更多的儿童电影会出现在给这个用户的推荐中。

现在转到 KNN。我们将从基于用户的 KNN 开始。这意味着对电影 m 和用户 u 的预测将基于与 u 相似的用户口味。

RMSE: 0.9595
HitRate: 0.002152317880794702

RMSE 比随机要好,但命中率要差一些。我们还可以看到,user_1 的顶部预测似乎比 SVD 算法给出的预测更远。在基于用户的 KNN 中,项目之间没有相似性的概念,因此我们不会为该算法计算与玩具总动员(1995)最相似的电影。

现在让我们检查基于项目的 KNN 的性能,它通过用户已经评价的项目与他们尚未评价的其他项目的相似性向用户推荐项目。在代码方面,只需要对类实例化稍作修改。

RMSE: 0.9851
HitRate: 0.002152317880794702

RMSE 比基于用户的 KNN 差一点,命中率几乎为零。与《玩具总动员》(1995)最相似的电影似乎是合理的,但对 user_1 的预测再次不令人满意。到目前为止,KNN 算法未能胜过奇异值分解,奇异值分解被认为是一种更好的协同过滤算法。

到目前为止,我们还没有考虑到电影本身的特点。如果我了解一些电影的细节,也许会有助于我做出更好的推荐?我们去看看。这被称为基于内容的过滤。

第五部分—使用基于内容的过滤推荐电影

对于基于内容的过滤,我们将在三种方法中使用基于 KNN 的算法(其中两种基于项目,一种基于用户):

1.电影情节(基于项目):根据情节描述创建所有电影的矢量表示。我们将首先对图描述中的所有单词进行词干分析,然后应用 TF-IDF 对每个文档进行矢量化。我们将根据以下内容生成相似性矩阵:

a.使用完整的 TF-IDF 矩阵

b.在特征选择之后使用 TF-IDF 矩阵

c.在特征选择和去除人名之后使用 TF-IDF 矩阵

2.电影类型(基于项目) : 我们将使用电影类型作为推荐的唯一来源,看看效果如何。

3.用户年龄+性别(基于用户):我们将使用用户数据作为 KNN 预测器的特征。

我们将创建一个从 Surprise 的KNNBasic.继承而来的类,它的功能将与协作过滤的基于项目的 KNN 相同,唯一的不同是我们将为 fit 函数提供一个预先计算的相似性矩阵,而不是从评级数据中计算出来。

第五部分 I —使用电影情节进行基于内容的过滤

现在,让我们创建 TF-IDF 相似性矩阵

现在,获得方法#1 的结果(使用完整的 TF-IDF 矩阵)。注意,对于常规训练集和留一训练集,我们需要不同的余弦相似性矩阵,用于计算命中率,因为它们包含每个电影的不同内部 id。

RMSE: 1.0268
HitRate: 0.003973509933774834

毫无疑问,比协同过滤更糟糕。我们还看到,相似性矩阵并没有真正在电影之间产生有意义的关系(唯一与《玩具总动员》相似的儿童电影是《玩具总动员 2》)。让我们看看减少功能的数量是否有帮助。

Number of selected features:  784List of selected features:   ['abbi', 'abel', 'ace', 'adam', 'adel', 'adrian', 'adrien', 'affair', 'agent', 'agn', 'al', 'aladdin', 'alan', 'albert', 'alex', 'alfr', 'ali', 'alic', 'alicia', 'alien', 'allen', 'alli', 'alvin', 'alyssa', 'amanda', 'amelia', 'american', 'ami', 'amo', 'andi', 'andrea', 'andrew', 'angel', 'angela', 'angelo', 'angus', 'ann', 'anna', 'anni', 'antoin', 'anton', 'antonio', 'ape', 'archer', 'archi', 'ariel', 'arjun', 'armstrong', 'arni', 'arroway', 'art', 'arthur', 'arturo', 'ash', 'audrey', 'aurora', 'austin', 'axel', 'babe', 'babi', 'balto', 'bambi', 'band', 'bank', 'barbara', 'barn', 'barney', 'bastian', 'bate', 'bateman', 'batman', 'beach', 'beal', 'bean', 'beatric', 'beckett', 'becki', 'beldar', 'bella', 'ben', 'bendrix'...RMSE: 1.0314
HitRate: 0.003642384105960265

并没有真正的帮助…我们看到很大一部分信息特征是名字。我们不希望这样,因为名字并不能真正说明电影的内容。让我们去掉这些名字。

RMSE: 1.0284
HitRate: 0.0041390728476821195

好吧,这比前两种方法更合理。首先,命中率更高(尽管仍然比协同过滤方法低),其次,类似于《玩具总动员》(1995)的电影实际上是相似的。第三,对 user_1 的最高预测似乎还过得去。现在让我们试试电影海报法,看看效果如何。

第五部分第二部分——使用电影类型进行基于内容的过滤

在这一部分,我将降低算法的复杂性,并创建一个仅基于电影类型的 KNN 推荐器。这意味着,如果一个人对儿童电影的评价很高,我们应该期待算法会推荐儿童电影。我们将使用 pythonitertools包创建所有可能的流派组合,然后应用我们的老朋友 TF-IDF 从这些数据中生成一个特征矩阵。

RMSE: 1.0138
HitRate: 0.0031456953642384107

毫不奇怪,与《玩具总动员》(1995)最相似的电影是那些具有相同或几乎相同类型的电影。除此之外,结果相当令人困惑,为什么 user_1 只获得战争电影的推荐?让我们提醒一下本文第三部分中的表格(它显示了 user_1 的所有评级),我们看到在 user_1 的评级列表中有两部类型为“战争”的电影,并且它们都得了 5 分。user_1 评价的其他类型出现得更频繁,因此如果我们按类型平均 user_1 的分数——“战争”类型将获得满分,而其他类型则不会。

因为基于项目内容的 KNN 推荐器通过新项目与已经被用户 _1 评分的其他项目的相似性来为用户 _1 预测新项目,所以它将为它将在用户 _1 的上下文中看到的每部战争电影给出满分!KNN 推荐器忽略属于 0 相似性项目的分数,当生成战争电影的推荐时,它将只考虑战争电影的分数,因此总是预测完美的分数。

第五.三部分——使用用户年龄+性别的基于内容的过滤

让我们从根据用户的年龄和性别创建特征向量开始。我们将为“男性”分配 0,为“女性”分配 1,并保留“年龄”列。最后,我们将对列进行规范化,以便不受列大小差异的影响。使用这些值,我们将创建一个余弦相似性矩阵,就像我们对电影所做的那样,但这次我们将计算每个用户之间的相似性得分。这个相似性分数将会是我们基于用户的 KNN 推荐器的输入。

RMSE: 0.9784
HitRate: 0.00347682119205298

我们看到 RMSE 优于基于项目的方法,类似于我们在第四部分(协同过滤)中得到的结果,其中基于用户的推荐优于基于项目的推荐。但是命中率比我们尝试的一些基于项目的方法要差一点。看起来每种方法都有其优点和缺点,一个好的方法可能是以某种方式将它们结合起来,这将是下一部分的重点。

第六部分——结论和展望

在这篇文章中,涵盖了很多内容。

首先,我们检查了 MovieLens-1M 数据集,并从我们看到的图表中获得了一些非常有趣的见解,例如哪些电影类型的平均得分往往高于其他类型。然后,我们使用了一些来自Surprise python 包的普通推荐系统算法,并通过 SVD 算法获得了一些非常好的结果。

之后,我们尝试了基于内容的过滤,但不幸的是,这是一个徒劳的尝试。尽管如此,在研究过程中,我们揭示了 KNN 推荐系统(或一般的基于内容的过滤)的一个固有弱点,即针对特定用户的推荐仅基于他/她过去交互过的项目,如果该用户的交互中存在偏见,它将在 KNN 推荐中显示出来。这就是为什么当我们使用流派作为推荐的基础时,我们的 spotlight 用户(user_1)获得了战争电影的最高分,即使我们作为人类不一定同意。对于该用户,更可能的推荐是儿童/音乐剧等。

此外,我们看到,在处理自然文本时,比如电影情节,了解哪些特性对推荐影响最大是值得的。当我们删除在 TF-IDF 算法中排名非常高的人名时,我们突然获得了更好的结果,当与《玩具总动员》(1995)最相似的电影突然变得有意义时,这一点显而易见。

在下一部分中,我将把基于内容的过滤和协作过滤结合起来,这样可以两全其美。我已经看中了 TensorFlow 推荐人套餐,我迫不及待地想一试。

下次见!

埃拉德

推荐人:

1 麦克斯韦·哈珀和约瑟夫·a·康斯坦。2015.电影镜头数据集:历史和背景。美国计算机学会交互式智能系统汇刊 5,4:19:1–19:19。https://doi.org/10.1145/2827872

MovieLens-1M 深潜—第二部分,Tensorflow 推荐工具

原文:https://towardsdatascience.com/movielens-1m-deep-dive-part-ii-tensorflow-recommenders-4ca358cc886e

Nathan Engel 的照片:https://www . pexels . com/photo/time-lapse-photography-of-car-lights-in-front-of-cinema-436413/

读者们好,

对于那些没有读过前一部分的人,这里是链接:

在那篇文章中,我展示了 MovieLens-1M 1数据集(一个电影推荐数据集,包含不同用户对电影的 100 万个评级)以及一些探索性的数据分析,并尝试了一些经典的推荐系统算法。虽然,那篇文章不是先决条件,你不读它也能理解这里的内容。

在 MovieLens 深潜系列的第二部分中,我们将使用 TensorFlow 推荐器(TFRS)。TFRS 将允许我们抽象出处理推荐系统时涉及的许多血淋淋的细节,如评估指标和模型训练程序。我们的模型将基于双塔架构,我们将在深入研究代码之前对其进行回顾。

在编码部分,我将稍微扩展一个现有的官方 TFRS 教程— 构建深度检索模型 [3]。在本教程中,目标是使用项目(电影)和用户的丰富上下文特征来构建一个检索推荐系统。我们将测试添加不同功能的影响,并尝试通过将更智能和更相关的功能引入手头的任务来获得改进。

文章的提纲如下:

  1. 现代推荐系统方法概述
  2. 了解 TFRS 和检索任务——我们将使用的基本构件
  3. 超越基准——通过使用更多上下文相关的特性,尝试超越最初的 TFRS 教程模型
  4. 超越基准—结果
  5. 结论和未来思考

让我们开始吧!

第一部分——现代推荐系统方法概述

推荐系统管道通常分为两个阶段——检索和排名。在检索阶段,我们为系统中的项目和用户生成表示(通常以嵌入的形式),并选择用户可能感兴趣的项目子集。这一阶段可能处理数百万用户/项目,因此计算效率必须很高。在排序阶段,我们获取检索阶段的输出,并对它们进行排序,以选择将被推荐给用户的最后几个项目。该模型可能计算量更大,因为它必须处理的数据要少得多。这个过程的描述如图 1 所示。

图 1:推荐系统过程

在本演练中,我们将重点关注检索阶段,并通过在嵌入生成过程中考虑不同的上下文特征,尝试使其更加高效和准确。

现在来看一些术语:

  • 项目—这些是我们将向用户推荐的项目。就我们而言——电影。
  • 查询—这是我们的系统将用来推荐商品的信息。在我们的例子中,这将包括我们推荐的用户的 ID 和其他特征,例如用户的年龄和性别。我们也将在这里包括它

第二部分——了解 TFRS 和检索任务

进入 TFRS,这是一个基于 Tensorflow 构建的推荐系统库,旨在简化构建、培训和部署推荐系统的过程。

我们在 TFRS 生产的车型将继承 tfrs。模型。型号级。这个类本身是围绕tf.keras.Model的包装器,为我们省去编写 train_step 和 test_step 方法的麻烦。作为用户,我们要做的是编写 compute_loss 函数,它接收一批输入查询和条目,并计算模型的损失。

在我们的教程中,我们将构建一个双塔架构模型。这个模型包含一个用于项目的塔和一个用于查询的塔。如图 2 所示,双塔架构非常简单。嵌入被创建来表示查询和用户,最后,我们对两个嵌入向量执行余弦相似性操作来预测它们的相似性(查询是这个项目的良好匹配吗?).通过使用嵌入每个项目的余弦相似性对特定查询的结果进行排序,系统可以检索该用户的顶部项目(通常大约 1000 个),然后最有可能将这些项目馈送到排序模型中,用于对将呈现给用户的最终项目(通常多达 10 个项目)进行细粒度控制。

图 2:双塔架构的简单示意图

从上面的解释中我们不明白的是,模型是如何学习的?当然是通过梯度下降。有正面的例子(在我们的例子中,是某个用户评级的电影)和负面的例子(不是某个用户评级的电影)。我们的算法将把这视为多类分类任务,并试图最大化已评级项目和查询之间的相似性,最小化未评级项目和查询之间的相似性。这个操作是在每个批处理中完成的(每个批处理只包含正例,负例是通过将批处理中的不同查询和项目配对生成的,如图 3 所示)。

图 3:检索任务(FFN =前馈网络)

第三部分——超越基准

在这一部分中,我将向您展示我的代码,我们将通过使用额外的上下文特性来丰富项目和查询表示,从而尝试改进我们的深度检索模型的性能。

让我们回顾一下我们将用于条目和查询的特性。对于每个特性,我将指定它是否在最初的 TFRS 教程中使用过。

查询功能(用户+上下文信息):

  • user_id [int] —最基本的特性。用于索引到用户嵌入矩阵中。(在原始教程中使用)
  • timestamp [int] —用户执行查询的时间,以 UNIX 时间戳格式表示。这可以帮助我们捕捉数据中的季节性趋势(例如,在周末,人们可能更喜欢较长的电影)。(在原始教程中使用)
  • user_age [int] —一个额外的数字特征,可能暗示用户首选项中的模式。(原始教程中未使用)

物品特征(电影信息):

  • movie_title [string] —项目的唯一标识符。用于索引到项目嵌入矩阵中。该特性还将用于创建嵌入电影标题文本的句子(而不是仅仅将其用作简单的标识符)。(在原始教程中使用)
  • movie _ genders[list]—每部电影都与一个或多个流派相关联,其中每个流派都由一个唯一的整数定义。我们将使用这些整数索引到一个流派嵌入矩阵中。对于每部电影,我们将对其所有类型的嵌入进行平均。(原始教程中未使用)
  • movie_length [int] —电影的长度,以分钟为单位。这些信息是使用 Cinemagoer [2] python 包收集的。一些电影长度数据丢失了,对于这些电影,我给出了 90 分钟的默认长度。

代码:

我在这里展示了一种非常规的探索性数据科学工作方式,那就是通过脚本而不是通过 Jupyter 笔记本。我认为脚本相对于笔记本的优势在于:

1)更清洁的代码。鼓励您使用良好的编程实践,例如将公共过程提取到函数中,而不是在单元格之间复制粘贴。

2)以合理的方式跟踪你的工作的能力,这是最重要的,尤其是在与其他人合作的时候。

缺点是它可能会引入对缓存机制的需求,正如我在本例中所做的那样(这样您就不必为您希望执行的每个实验运行繁重的数据加载/模型训练过程)。在这个例子中,我保存了每个配置的训练模型,这样我就不必在每次运行脚本时都重新运行训练。此外,我使用tf.data.Dataset.savetf.data.Dataset.load保存和加载数据集,因为数据集创建过程可能需要一段时间。最后,我利用了 cachier python 包,当从Cinemagoer加载电影长度信息时,它在磁盘上生成函数结果的持久缓存,因为这是一个非常漫长的过程,我们希望避免对每部电影运行这个函数超过一次。

运行这个脚本需要的非标准包如下:tensorflow, tensorflow_datasets, tensorflow_recommenders, plotly, numpy。注意,该脚本假设您的主目录中有两个子目录,分别名为datasetssaved_models

培训过程将由MovieLensTrainer班管理。给定训练设置,如图层大小、嵌入大小和要使用的特征,它将训练模型或从磁盘加载相关模型。在通过命令行参数获得我们定义的所有特征组合的训练模型后,我们将运行函数plot_training_runs,该函数将返回每次运行的前 100 个分类准确度的曲线图,并允许我们比较我们使用的不同设置。

从最初的 TFRS 教程开始,我对模型做了一个重要的改变,那就是能够在MovieModel(物品塔)中使用额外的功能。在本教程中,这个模型只将movie_title作为输入。在这个项目中,它可以接受任何额外的特性,最终创建一个所有特性嵌入的连接。这很重要,因为它使我们能够利用额外的电影信息,如类型,电影长度,在 IMDB 上的平均评级,以及您可能想到的任何其他功能。

命令行参数+运行示例

我们有几个命令行参数来控制我们的脚本做什么。我将讨论一些不太明显的问题:

layer_sizes (list[int]) —为两个塔(查询和项目模型)中的每一个定义嵌入层之后的层的数量和大小。我们添加的层越多,给它们的神经元越多,我们的模型就会变得越复杂(因此也更容易过度拟合)

additional_feature_sets (list[list[string]]) —该参数是一个列表列表,其中每个内部列表是我们希望训练 TFRS 模型的一组特征。例如,使用这个参数我们可以训练两个模型,其中一个训练使用movie_genres, timestamp特性,另一个训练使用movie_title_text, timestamp特性。

--retrain (bool) —这是一个二元切换。关闭时,如果我们以前已经训练过(从文件系统加载),我们将使用选定的设置加载以前训练过的模型。如果启用,我们将总是重新训练并覆盖当前在磁盘上的模型。

--generate_recommendations_for_user (int) —这是一个用户的 ID,我们将为其生成我们生成的每个训练模型的推荐。这意味着让我们的模型“脚踏实地”,并看到它为偏好已知的用户生成推荐的真实示例。

此流程的一组命令行参数示例(本次运行将训练 6 个不同的模型,每个模型训练 300 个时期。它将为每个训练模型的 ID 为 1 的用户生成推荐):

--num_epochs
300
--additional_feature_sets
None
--additional_feature_sets
timestamp
--additional_feature_sets
movie_title_text
--additional_feature_sets
timestamp
movie_title_text
--additional_feature_sets
timestamp
movie_title_text
movie_genres
--additional_feature_sets
timestamp
movie_title_text
movie_genres
movie_length
--embedding_size
32
--layer_sizes
64
32
--generate_recommendations_for_user
1

让我们来看看这次运行的输出(为了清楚起见,删除了警告):

These are the ratings for user 1:

 **Recommendations for model ('timestamp',), user 1:**
 [b'Alien (1979)' b'Sense and Sensibility (1995)' b'Sea Wolves, The (1980)'
 b'Ninth Gate, The (2000)' b'Beautiful (2000)' b'Sweet Nothing (1995)'
 b'Titan A.E. (2000)' b'Saint, The (1997)'
 b'Killer: A Journal of Murder (1995)' b'Man of the Year (1995)']
Recommendations for model ('movie_title_text',), user 1:
 [b'Angel and the Badman (1947)' b'Alien (1979)' b'Shadows (Cienie) (1988)'
 b'Two Moon Juction (1988)' b'Sea Wolves, The (1980)' b'Booty Call (1997)'
 b'Apple Dumpling Gang Rides Again, The (1979)'
 b'Psycho Beach Party (2000)' b'Local Hero (1983)'
 b"Shaft's Big Score! (1972)"]
**Recommendations for model ('timestamp', 'movie_title_text'), user 1:**
 [b'Fried Green Tomatoes (1991)' b'Alien (1979)'
 b'Angel and the Badman (1947)' b'Sarafina! (1992)'
 b'Color Purple, The (1985)' b'Sea Wolves, The (1980)'
 b'Madame Sousatzka (1988)' b'Little Shop of Horrors (1986)'
 b"One Flew Over the Cuckoo's Nest (1975)" b'L.A. Story (1991)']
**Recommendations for model ('timestamp', 'movie_title_text', 'movie_genres'), user 1:
** [b'Being There (1979)' b'Alien (1979)' b'Crimes and Misdemeanors (1989)'
 b'Two Moon Juction (1988)' b'Psycho (1998)'
 b'Apple Dumpling Gang Rides Again, The (1979)' b'Me Myself I (2000)'
 b'Loser (2000)' b'Killer: A Journal of Murder (1995)'
 b"Barney's Great Adventure (1998)"]
**Recommendations for model ('timestamp', 'movie_title_text', 'movie_genres', 'movie_length'), user 1:
** [b'Sid and Nancy (1986)' b'Angel and the Badman (1947)' b'Alien (1979)'
 b'Two Moon Juction (1988)' b'Crimes of the Heart (1986)'
 b'Sea Wolves, The (1980)' b'One False Move (1991)'
 b'Rich and Strange (1932)' b'Storefront Hitchcock (1997)'
 b'Celebration, The (Festen) (1998)']
**Recommendations for model (), user 1:**
 [b'Alien (1979)' b'Booty Call (1997)'
 b'Day the Earth Stood Still, The (1951)' b'Angel and the Badman (1947)'
 b'Sea Wolves, The (1980)' b'Celebration, The (Festen) (1998)'
 b'Midnight Cowboy (1969)' b'Desperado (1995)' b'Tingler, The (1959)'
 b'Blue Chips (1994)']

这只是个人想法,但是作为用户 1,我想我会非常失望。出现在所有推荐中的唯一的儿童电影是“巴尼大冒险(1998)”,尽管用户对这些类型的电影有明显的亲和力。另外,我们甚至可以看到一些不合适的电影(用户 1 被归类为 K-12 岁)。在这个系统投入生产之前,还需要更多的建模改进和启发式布局。

现在,让我们查看一些准确性结果,看看我们是否通过在训练过程中包含更多功能来击败教程基准。

第四部分——超越基准——结果

我们将训练具有以下特征组合的模型:

  1. movie_title, user_id
  2. movie_title, user_id, timestamp
  3. movie_title, user_id, movie_title_text
  4. movie_title, user_id, timestamp, movie_title_text **(this setting was used in the official tutorial)**
  5. movie_title, user_id, timestamp, movie_title_text, movie_genres
  6. movie_title, user_id, timestamp, movie_title_text, movie_genres, movie_length

并使用以下图层大小:

[64, 32]

总共 6 个训练好的模型。我们将为每个模型训练 300 个历元,并对所有嵌入层使用 32 的嵌入大小。我假设额外的特性将丰富项目/查询嵌入,并帮助生成更准确的表示。我们将用于比较模型的指标是前 100 名的准确度(越高=越好)。这意味着,对于验证集中的每个项目/查询对,是否为该查询生成了前 100 对中的配对的相似性?

图 4 所示(包括静态图像和交互图,因为移动设备上的可见性问题)是每个模型训练的验证前 100 名准确度(在 5 个时期间隔内)。

图 4(静态):模型比较结果

图 4(交互式):模型比较结果

我们可以始终如一地看到,随着我们添加更多的功能,验证的准确性会增加。这是令人鼓舞的,因为这意味着我们的工作产生了积极的影响!请注意,整体准确性似乎很低,这是因为这是一个非常困难的问题(需要从大约 6000 部电影中猜出正确的电影)。就验证准确性而言,最好的方法是使用所有建议特性的方法,实际上,在 300 个时期后,它比本教程中的基准高出大约 0.015。

第五部分——结论和展望

在这篇文章中我们已经看到:

  1. 如何用 tensorflow 推荐器搭建推荐系统?
  2. 如何将上下文特性添加到项目和查询中以获得更好的性能。
  3. 如何通过简单的脚本而不是使用 Jupyter 笔记本进行研究。

我们可以看到,查询和项目的额外上下文可以提高我们的推荐性能(即使在相对密集的数据集上,如 MovieLens-1M)。当构建你自己的推荐系统时,试着想想你是否可以在你的嵌入中加入更多的上下文,而不仅仅是使用用户和商品标识符:)

下次见,

埃拉德

参考文献:

1 麦克斯韦·哈珀和约瑟夫·a·康斯坦。2015.电影镜头数据集:历史和背景。美国计算机学会交互式智能系统汇刊 5,4:19:1–19:19。https://doi.org/10.1145/2827872

[2] Tensorflow 深度检索系统教程—https://www . tensor flow . org/re commenders/examples/deep _ re commenders

[3] Cinemagoer python 包—https://cinemagoer.github.io/

将代码从 Python 移植到 Julia:小心虚假的朋友

原文:https://towardsdatascience.com/moving-code-from-python-to-julia-beware-of-false-friends-160573a5d552

照片由布伦丹·丘奇Unsplash 上拍摄

一般来说,将代码从 Python 转移到 Julia 是很容易的。但是有些陷阱你应该知道。

虚假的朋友

学习外语时,你会遇到拼写和发音与母语中你所知道的单词相似的单词。但有时这些看似熟悉的词在另一种语言中却有着完全不同的含义。例如德语动词“ bekommen ”看似等同于英语“t o 成为”,实际上是“t o 获得”或“t o 接收”。当说德语的人学习英语时,这有时会导致一些相当有趣的句子,比如“她在 11 岁生日时变成了一只猫”。当然这里的意图不是表达女孩在生日那天变异成了一只猫,而是她得到了一只猫作为生日礼物。这种看似相关的词就叫做“假朋友”。

当您从一种您熟悉的编程语言切换到一种新的编程语言时,例如当您从 Python 切换到 Julia 时,也会出现同样的情况。这两种语言有一些概念上的差异,但也有一些方面非常相似,似乎可以互换——但实际上并不是所有的都可以互换!

Python 列表和 Julia 数组

最近,我在阅读 René的文章《Julia 和 Python 的黄金时间》时,遇到了一个很好的例子。他展示了一个计算质数的 Python 函数(最初来自这里是),将它 1:1 地翻译给 Julia,做了一些基准测试,当注意到 Julia 实现的性能低于预期时有点失望(Python ca。7.5 秒,朱莉娅·卡。4.5s;我在我的环境中重复了基准测试,分别得到了 9.5 s 和 7.5 s)。

让我们看一下这段代码,看看发生了什么,可以做些什么来改善这种情况(:对于进一步的分析,完全理解这段代码并不重要。我们只会对它的某些方面有更深入的了解;尤其是使用的数据结构。)

首先是 Python 代码:

上述文章中的原始 Python 代码

这是对朱莉娅的翻译:

上面引用的文章中的原始 Julia 代码

:茱莉亚版本有小错误。第 15 行中的delete!语句必须放在 for 循环之后,以便与 Python 代码相同。在本文中,已经使用了修正的版本来创建基准数字。

使用的数据结构(ab)

这些函数中使用的主要数据结构是

  • 在 Python 中:一个字典中的D列出了
  • 在 Julia 中:一个字典 D中的数组
    (或者更确切地说是Vector s,它是一维数组的同义词)。

上面代码中使用的数组字典 D[图片由作者提供]

分析(Julia-)代码,我们看到,这个字典D最初是空的,它的槽被递增地填充,或者通过分配一个具有一个元素的数组(第 8 行),分配一个空数组(第 13 行),或者通过使用push!(第 9 行和第 14 行)向这些数组添加单个数字(如果它们已经存在)。如上图中的例子所示,在函数执行过程中,大多数数组只包含一个元素。随着时间的推移,一些数组不再需要并被删除(第 15 行)。

所以基本上,下面的“翻译”步骤已经完成:在 Julia 版本中,数组已经被用作 Python 列表的“替代物”。这两种数据结构在几个方面非常相似,如示例所示,甚至可以或多或少以相同的方式使用。

但是:Python 列表被设计成动态的数据结构,可以在生命周期中以很小的增量增长和收缩。用 Julia 数组也可以这样做,但是不应该这样做!Julia 数组(正如它们的名字已经告诉我们的那样)不是列表,而是数组,传统上是静态的结构。所以它们的设计有着不同的目的。

以上面例子中的方式使用 Julia 数组是最低效的使用方式之一。Julias @time宏显示,在超过 6500 万次单独的分配调用中,n = 1,000,000 的函数调用分配了大约 4 GB 的内存。那不可能有效率!

7.532386 seconds (65.79 M allocations: 3.933 GiB, 19.15% gc time)

丰富

让我们深入了解一些细节,看看在第 13 & 14 行中发生了什么,以展示这些用于创建和增长数据结构的微小步骤对性能的影响有多大:

  • 在第 13 行中,函数get!被调用仅仅是因为它的副作用:如果插槽p+q为空,开发人员想要在插槽D处为字典D分配一个空数组,或者如果已经有一个数组,则什么也不做。
    get!的实际用途是读取特定槽位的值。
  • 这样做是为了在第 14 行将值p追加到数组中。即第 13 行保证了在任何情况下该位置都存在一个数组。

我们可以用下面的代码替换这两行,这样可以更清楚地表达这些意图:

if haskey(D, p+q)
    push!(D[p+q], p)   # append `p` to the array, if there is one
else
    D[p+q] = [p]       # assign a new array `[p]`, if there is none
end

因此,如果p+q处的槽为空(即else部分),则在一个步骤中分配一个包含p的新数组,而不是原始代码中的两个

这个小小的改变,用一个(稍微大一点的)步骤代替了两个小步骤,减少了大约 43%的运行时间和 50%的内存分配!

4.242410 seconds (26.15 M allocations: 2.047 GiB, 13.62% gc time)

除此之外,仅仅为了副作用而使用函数(如第 13 行)无论如何都是不好的做法,因为它隐藏了真正的意图。这使得代码更难阅读和理解。此外,如果没有清楚地表达出来,编译器应该如何理解真正的含义(并生成最佳代码)?

移动算法,而不是代码

但这不是你应该改进代码的方式。决定性的一点是,Python 代码不应该被逐行翻译(这将导致一些类似 Python 的 Julia 代码),而是算法作为一个整体应该以 Julian 的方式来考虑和实现。

算法,而不是代码,应该从一种语言转移到另一种语言。

厄拉多塞筛

让我们这样做:上面使用的算法是著名的厄拉多塞的筛子。是的,如果你想知道,上面的代码是“筛子”的一个相当特殊的实现。

计算 1 到 n 范围内素数的厄拉多塞筛的主要思想如下:

  • 列出从 1 到 n 的所有数字。
  • 最初我们假设所有这些数字都是质数。我们可以省略 1,因为根据定义,2 是最小的素数。
  • 丢弃所有已经识别的素数的倍数的数:
    -丢弃所有 2 的倍数
    -丢弃所有 3 的倍数
    -省略 4,因为它已经被识别为 2 的倍数
    -丢弃所有 5 的倍数…等等。
  • 当我们到达sqrt(n)时,我们可以停止这个过程,因为此时我们已经检查了所有大于该阈值的倍数。例如,对于 n = 100(此阈值为 10),我们不必检查 11 的倍数,因为我们在检查 2 的倍数时已经检查了 2 x 11,在检查 3 的倍数时已经检查了 3 x 11,等等。
  • 当检查一个数 k 的倍数时,我们不必从 1 x k 开始。从 k x k 开始就足够了,因为已经检查了所有较低的倍数。例如,当检查 5 的倍数时,我们可以从 25 开始,因为 2 x 5、3 x 5 和 4 x 5 已经在之前的过程中检查过了。

:本说明中的参数 n 与上述代码中的含义不同。这里它表示素数搜索范围的上限。在上面的实现中, n 是将被识别的素数的数量。所以用 n = 1,000,000 产生一百万个质数。在接下来的部分中,我们将实现一个函数fastPrimes,它遵循这里给出的算法的“经典”版本,其中 n 是上限。为了用fastPrimes得到与上面代码相同的结果,我们必须用 n = 15,495,863 来调用它,因为这是前一百万个素数中最大的一个。

在 Julia 中实现

实现“筛子”算法的基础是一个适当的数据结构。一个直接反映一串数字的概念连同信息“的结构是质数?"是一个布尔值数组。如果我们希望实现也是内存高效的,那么我们选择一个BitArray。这是一个布尔数组的变体,它只使用一位内存存储每个布尔。

这种考虑和上面的算法描述直接导致了下面的代码:

  • 在第 2 行中,我们创建了一个具有 n 个元素的BitArray(所有元素都设置为真)和,我们省略了 1(第 3 行)。
  • 从第 4 行开始的 for 循环检查从 2 到sqrt(n)的所有倍数。
  • 已经被标记为不是质数的数字被省略(第 5 行)。
  • 从第 6 行开始的 for 循环遍历从 i x i 开始的所有 i 的倍数,并将它们标记为非质数。
  • 就是这样!第 10 行的语句只是用来将所有质数都标有 trueBitArray转换成一个质数数组。

这个函数的运行时间大约是 76 ms,并且它只使用大约 11 MB 的内存,正如BenchmarkTools包的结果所示。与最初的实现相比,使用不到 0.3%的内存,速度快了近 100 倍。

“fastPrimes”的基准测试结果[图片由作者提供]

用 Python 实现

这种方法当然也导致了 Python 中更好的数字。代码如下:

由于 Python 中数组的索引从 0 开始,我们在这里创建一个大小为 n+1 的数组,并忽略索引 0 处的元素。

这段代码使用与 Julia 示例中相同的参数化运行了大约 2.5 s。所以比原代码快了差不多 4 倍。不幸的是,用于进行基准测试的 Python timeit模块并没有产生关于所分配内存的信息。

结果

下图总结了本文介绍的不同变体的性能,并再次显示了正确使用 Julia 可以获得哪些出色的结果。

不同变体的性能[图片由作者提供]

基准测试使用了以下环境:苹果 M1,8 GB 内存,Julia 1.7.1,Python 3.9.7。

对于 Julia 变体,我们还可以看到与分配的内存相关的性能。这两项措施都有可能取得重大进展。

旁注

Julia 相对于 Python 的主要优势是性能和奇特的概念,比如多重调度。但是我认为还有更多:它也是关于日常使用中更重要的实际的东西,比如简单性、一致性和运行良好的包管理器。

当我准备这篇文章时,我再次被这些问题绊倒。几年前促使我寻找 Python 替代品的问题,为什么我想与你分享它们。

朱莉娅

在 Julia 中可以使用@time宏对函数进行计时,该宏包含在基本库中,如下所示:

@time fastPrimes(100)

使用相同的模式,您可以通过详细版本@timev获得更多的信息:

@timev fastPrimes(100)

如果您想要“真正的”基准测试,自动进行几次迭代并获得一些有用的统计数据等。,可以使用BenchmarkTools包。因为它是第三方软件包,所以必须先安装。因此我切换到包模式,输入add BenchmarkTools,包管理器在不到一分钟的时间内下载、安装并预编译了这个包。Julia 有一个包管理器,它是根据该语言设计和开发的。

进入using BenchmarkTools后,可以使用该包的功能。它和上面的宏有着相同的使用模式(尽管是完全不同的包,由不同的人开发)。你称之为:

@benchmark fastPrimes(100)

计算机编程语言

在 Python 中,您可以利用来自同名模块的timeit-方法。它也包含在基本库中。为了给fastPrimes函数计时,我不得不使用下面的表达式:

timeit.Timer(‘fastPrimes(100)’, setup=’from __main__ import fastPrimes; gc.enable()’).timeit(number = 1)

from __main__ import是必要的,这样timeit就可以“看到”fastPrimes功能。gc.enable()打开垃圾收集器,因为timeit在默认情况下关闭它,这既不是真实情况,也不能与 Julia 设置相比。

不,这既不容易理解也不容易使用。我花了很长时间才弄明白如何正确使用timeit

由于timeit只测量运行时间,但不给出关于内存分配的信息(像 Julia 工具那样),我在寻找一个更好的替代方法。网上不同地方提到的另外两个 Python 基准测试函数不符合这些要求。但是第三个模块benchmarkit似乎做到了。所以我试着安装了它。Python 有两个包管理器(PIP 和 Conda);两者都是独立于语言开发的。

因为我的计算机上有一个 Anaconda 环境,所以我首先尝试用conda进行安装。但是在康达宇宙中benchmarkit模块是未知的。所以我切换到pip,它开始在我的电脑上加载兆字节的软件。几分钟后,它通知我它现在将安装pandas模块,这让我很恼火,因为那个模块已经安装好了。时间一分一秒地过去了……然后几十条错误信息淹没了我的屏幕。此后,它报告说在我的电脑上发现了一个pandas安装,这让我的心情有所好转。大约 15 分钟后(使用 M1 iMac 和 100 MBit/s 的互联网连接!)一切似乎都准备好了。所以我输入了import benchmarkit …导致一条消息告诉我,这个模块没有安装。也就是说,在摆弄了这么长时间之后,我除了一个可能更乱的 Python 装置之外一无所有!

我想我不必提及使用benchmarkit的基准测试将遵循与使用timeit完全不同的模式。

四处移动熊猫柱

原文:https://towardsdatascience.com/moving-pandas-columns-around-c0fad1043d3a

简化更改数据帧中列顺序的过程

概观

有几个原因可能会让你改变你的熊猫专栏的顺序。

  • 你希望目标变量在最右边。
  • 您希望逐列跟踪数据准备的进度,在处理数据时按顺序移动它们。
  • 您(匆忙地)创建了一些测试列……现在是时候整理一下了。
  • 您有一个感兴趣的变量,它被埋在 400 列中的第 332 列。将它放在零位置会提高输出的可读性。

有几种方法可以完成这项任务。有些人比其他人更好。

本文展示了一些优雅地利用原生 Python list.remove()list.insert()方法的例子。

数据+设置

为了便于参考,本文引用了 Seaborn 和 Stata 示例数据(全部在线提供)。我们从导入 Pandas、Seaborn 的标准设置开始,然后加载数据(auto2.dta 和 tips.csv)。参见本文末尾的数据许可

import pandas as pd
import seaborn as snscars = pd.read_stata('http://www.stata-press.com/data/r15/' + /
                      'auto2.dta')
tips = sns.load_dataset('tips')

图片鸣谢:作者使用 Jasper.Ai 创作。

汽车:将价格移到最左边

当它第一次加载时,你会在汽车数据的最左边找到价格。

图片鸣谢:作者插画。屏幕截图。

我们的任务是将价格移动到数据集中的第一个位置。我们只需要四行代码。

# Get the column names in their current order.
new_order = cars.columns.to_list()# Remove price from the list.
new_order.remove('price')# Place price at the beginning of the list.
new_order.insert(0, 'price')# Redefine the data frame with the new column order.
cars = cars[new_order]

我们现在在最左边的列(第一列,或者零列)中找到价格。

图片鸣谢:作者插画。屏幕截图。

提示:将日期+时间移到最左边

一次移动两列(如下所示)与移动一列(如上所示)大致相同。当第一次加载该数据时,我们看到日期和时间分别位于位置 4 和位置 5。

图片鸣谢:作者插画。屏幕截图。

我们希望将它们移动到最左边的位置 0 和 1。在此过程中,我们还将颠倒它们的顺序,使时间位于第一位,位置为 0,而日期位于第二位,位置为 1。

list.remove()list.insert()方法不接受列表作为参数。这意味着移动多列需要相应增加代码。

# Get the column names in their current order.
new_order = cars.columns.to_list()# Remove the columns from their original positions.
new_order.remove('day')
new_order.remove('time')# Add the columns back but in desired positions.
new_order.insert(0, 'day')
new_order.insert(0, 'time')

这里显示的结果。

图片鸣谢:作者插画。屏幕截图。

请注意,要实现这些结果,您可以按任何顺序执行删除。然而,要使time在列中的顺序在day之前,插入方法必须按照所示顺序。

结论

本文演示了两个代码示例,可以让您快速更改 Pandas 数据帧的顺序。其他工具提供现成的解决方案来实现这一功能。对熊猫来说,有趣的是,你必须发明自己的方式。

我之前写道:

在合并多个数据源之后,拥有一个可以快速排序和重新排序列的方法是很方便的。这样做是对未来审查你的工作的科学家的一种常见的礼貌。如果我们把最重要的栏目放在左边,其他人会花更少的时间寻找必要的信息来理解我们的工作。当我们知道我们将返回到我们的工作中以供将来参考时,这个额外的步骤对我们自己也是有帮助的。(来源:重新排序熊猫数据框列:否定标准解决方案)

您使用过哪些解决方案来移动列?

感谢阅读

感谢阅读。把你的想法和主意发给我。你可以写信只是为了说声嗨。如果你真的需要告诉我是怎么错的,我期待着尽快和你聊天。推特:@ adamrossnelson| LinkedIn:亚当·罗斯·纳尔逊 |脸书:亚当·罗斯·纳尔逊

数据许可

Seaborn 数据许可。Seaborn 提供这些数据用于教学、培训、演示和测试目的。你可以在文档相关知识库中阅读更多关于这些数据的信息。

Stata 数据许可。Stata 提供这些以及其他数据,用于教学、培训、演示和测试目的。你可以在之前的文章中读到更多关于这些和其他数据的信息。

多智能体模拟:推理时间智能中的关键功能

原文:https://towardsdatascience.com/multi-agent-simulation-a-key-function-in-inference-time-intelligence-f032dabf9a16

在涉及多人或智能机器的假设场景中避免组合爆炸

图片来源: 本地 _ 医生 途经 土坯股票

我们将看到模拟的角色发生重大变化,以评估实现机器智能的实时假设场景。我相信,如果扩展到推理时包括基于代理的模拟,它可以发挥更有意义的作用。这种类型的计算寻求基于来自多个代理(人类或其他人工智能)的输入迭代地解决问题,这是更真实世界学习的特征。因此,它有可能在机器学习过程中传授多种“思维模式”,并推动下一代人工智能的发展。

什么是模拟,真的吗?

为了给下面的讨论打下基础,我们需要在讨论的上下文中从模拟的定义开始。

在这里,我们将模拟定义为一种方法,该方法使用专门的模型来模拟真实的或提议的系统操作,以在各种场景或过程变化下为决策提供证据。

模拟使用专门的模型来模拟真实的或建议的系统操作,为各种场景或过程变化下的决策提供依据。

为了更好地理解模拟与人类认知的关系,考虑一个人类经常遇到的情况——一个中等规模的个人团体的会议。例如,这可能是一个学校运动队和他们的教练在一场重要的比赛或比赛之前的一次会议。会议中的所有人都有稍微不同的背景和目标。

图一。教练有每个队员个性的详细模型。图片来源: 土坯股票

教练将能够以相当高的精确度模拟会议的展开,并将积极利用这种模拟能力来计划说什么以及如何达到最佳效果。这个模拟需要哪些认知功能?

  • 教练必须能够跟踪哪些个人可以获得哪些信息。一些信息是公开的,比如对方球队的名称和比赛日期,而其他信息是私人的,比如球员个人的健康记录。她知道不要重复不必要的公开信息,也不要隐瞒私人信息。
  • 她需要模拟每个玩家的精神和身体状态,以及他们的目标。她知道哪些球员最近受伤了,哪些球员打破了他们的个人记录。她明白,有些人是在捍卫自己已经很强大的地位,而有些人则希望有一个展示自己的机会。她还知道哪些球员对挑战的反应很好,哪些球员需要额外的鼓励。
  • 在整个会议期间,她将继续构建她的玩家模型。例如,如果一个孩子表现出强烈的个人成长,教练会注意到这一点,并相应地调整她未来的行为。
  • 最后,教练可以模拟一系列潜在的互动。例如,她知道批评一个玩家一次与连续批评同一个玩家三次会有不同的效果。

这种因果多主体模拟能力是人类社会认知的核心。如果我们要将上述特征翻译和提炼为更专业的术语,我们需要推断出以下特征,这些特征是人工智能必须具有的,以更类似于人类的方式进行模拟:

  • 能够建模、实例化和更新环境中单独的、可区分的代理和其他复杂对象。
  • 迭代环境和代理状态的能力——也就是说,人工智能需要能够迭代地完成代理本身和代理与环境之间的相关行为和交互序列。
  • 能够将每个代理/对象的行为建模为通用和潜在自定义功能的组合(即,所有孩子的行为都像 F(x) ,特别是凯利,具有 F(x=a) 行为)。
  • 跟踪每个代理的相关输入序列和内部状态(包括知识状态)的能力。

在现代人工智能的标准环境中,模拟通常不包括上述能力,尤其是在推理时。

环境模拟及其局限性

今天,大多数基于模拟的人工智能研究都集中在像环境模拟这样的问题上,用于机器人自动驾驶汽车的运动训练。它还被用于计算视频游戏等强化学习场景中的最佳动作。这种类型的模拟基于一个整体模型,这意味着所有的推断都基于内部存储的数据。它通常以明确定义的目标为特征(例如赢得比赛)。人工智能代理的目标不考虑环境中潜在的质量变化,也不考虑它必须与之交互的其他代理的目标。

环境模拟已经取得了几个令人印象深刻的里程碑。其中值得注意的是教授约书亚·特南鲍姆和麻省理工学院大脑和认知科学系团队的工作,他们在发展里程碑和物理场景理解的背景下研究模拟。同样,谷歌大脑的研究人员通过注入来自物理模拟引擎的信息,在大型语言模型中实现了更强大的推理能力。OpenAI 的 Dota 机器人是第一个在 Dota 2 中击败世界冠军电子竞技队的人工智能机器人,Dota 2 是一款在线多人对战竞技场游戏。

然而,机器学习的标准方法缺乏几个特征:

  • 模拟通常在训练时间而不是在推理时间运行。
  • 模拟环境通常是“无面孔”的,因为它不包括复杂的、不断进化的代理,这些代理的行为会根据前面的交互序列而变化。
  • 他们不能模拟作用于不同目标的代理,而这是人类可以轻易做到的。这将需要一种融入更复杂的世界模型和心理理论的模拟——这些高级智能的关键原则如此无缝地嵌入儿童发育中的大脑,并在幼儿园小朋友的蜡笔画中表现出来。

开放的现实世界的相互作用涉及代理人对各种目标的行动,因此不能容易地使用给定环境状态的最佳可能行动的范例来模拟。此外,强化学习(这是传统上在这种情况下使用的范式)已经被巨大的状态空间所困扰,即使对于目前使用的狭窄定义的环境也是如此。

聚焦于基于因果代理的模拟

大多数机器学习不包含多智能体模拟,这在很大程度上是计算上禁止的,因为它会导致样本空间大小的爆炸。这是一个必须跨越的障碍,以赋予人工智能解决世界上一些更重要的问题所需的预测能力。

有没有一种方法可以克服开放式多智能体环境的计算困难,并允许人工智能智能智能体有效地集成到这样的环境中?

首先,让我们更精确地描述传统端到端方法的计算困难性来自哪里。

今天,基于人工智能的解决方案所针对的大多数智能任务都是非情境性的,也就是说,输出是而不是依赖于进行查询的上下文或特定情境。它们也不跟踪特定个人或其环境中复杂物体的近期历史。相比之下,人类总是在非常强的语境/情境设置中应用他们的智力;他们的回答很少是“通用的”。下一代人工智能必须结合代表性结构和功能建模来弥补这一差距。

当一个具有情景智能的 AI 被置于一个有多个复杂代理的环境中时,它必须能够执行两个关键功能:

  • 跟踪这些代理的输入和以前的行为;
  • 模拟具有潜在响应序列的假设情景,并确定这些序列可能如何影响环境和那些代理。

在当前的方法中,系统试图创建综合的输入-输出函数(例如,实现为大规模神经网络),使得当呈现一种情况时,它可以预测或推荐下一步。为了将多主体设置映射到这样一个“平面”输入输出函数,需要在训练期间展开所有潜在的序列和多主体交互,这可能很快变得难以处理。

然而,如果范式被改变为在推理过程中使用“假设”情景的模拟,则没有必要展开大的组合空间。人们只能在推理时模拟要评估的相关序列。这将包括无限小数量的序列,从而避免组合爆炸。

在这种情况下,使用封装的代理模型进行因果模拟不仅是实现预期结果的最有效方式,也是唯一的方式。这种模拟将允许代理与部分假设场景进行交互,而不需要立即展开整个环境。然后,可以通过从不可行到可行的场景迭代地进行推理。

为了说明这个过程,考虑我们前面的运动队和教练的例子。假设我们有十个玩家(代理),每个玩家有 100 种可能的行为。我们的人工智能试图生成潜在的假设场景,以选择最佳行动方案。如果一个人工智能试图学习十个智能体中的每一个的模型,为每个可能的环境状态执行每个可能的行为,这将导致大规模的组合爆炸。但是在任何现实场景中,只有一小部分代理的行为和世界状态是相关的。如果代理模型被单独封装并与世界模型分离,AI 可以执行搜索以首先选择相关的行为和世界状态,然后仅展开那些因果上可能相关的模拟场景。

这将类似于一个整体嵌入空间(由端到端网络学习),它被分解成离散的单元,每个单元保存相关环境或个体代理的表示。然后,这些离散的单元可以被查询以生成反事实的场景,从而包含组合爆炸。

总结

随着人工智能系统从实验室走向企业和家庭,它们将需要新的能力来变得更具适应性、情境性、深度情境性,并擅长与周围的人和实体进行持续互动。基于因果代理的模拟是下一代人工智能解决方案的关键。它解决了两个巨大的需求:需要用基于人工智能的合作代理来支持人类劳动力,并执行依赖于情况意识但超出人类能力的任务。使这些进步易于处理和扩展将不可避免地要求人工智能体系结构的模块化,以实现推理时间模拟能力。

参考

  1. 维基百科贡献者。(2022 年 10 月 10 日)。模拟。维基百科。https://en.wikipedia.org/wiki/Simulation
  2. 什么是模拟?这是什么意思?(定义和例子)。(未注明)。TWI。检索于 2022 年 10 月 24 日,来自https://www . twi-global . com/technical-knowledge/FAQ/FAQ-what-is-simulation
  3. 李玉英,郝,谢,佘玉英,李树生,于,米(2021)。基于深度强化学习的自由漂浮双臂空间机械臂约束运动规划。航天科技,109,106446。
  4. 佩雷斯-吉尔。(2022 年)。基于深度强化学习的自主车辆控制。多媒体工具和应用,81(3),3553–3576。
  5. 约书亚·特南鲍姆。(2022 年 10 月 6 日)。麻省理工学院-IBM 沃森人工智能实验室。https://mitibmwatsonailab.mit.edu/people/joshua-tenenbaum/
  6. 巴塔格利亚,P. W .,汉姆里克,J. B .,&特南鲍姆,J. B. (2013)。模拟作为物理场景理解的引擎。美国国家科学院学报,110(45),18327–18332。
  7. 刘,魏军,顾,吴,吴,崔,崔,戴(2022)。通过模拟的基础语言模型推理。arXiv 预印本 arXiv:2210.05359。
  8. Berner,c .、Brockman,g .、Chan,b .、Cheung,v .、Dę biak,p .、Dennison,c .……、张,S. (2019)。大规模深度强化学习的 Dota 2。arXiv 预印本 arXiv:1912.06680。
  9. 派珀,K. (2019,4 月 14 日)。OpenAI 的 Dota AI 击败职业队 OG,成为第一个击败卫冕世界冠军的 AI。Vox。https://www . vox . com/2019/4/13/18309418/open-ai-dota-triumph-og
  10. 歌手 g(2022 年 8 月 17 日)。超越输入输出推理:认知人工智能的四个关键特性。中等。https://towards data science . com/beyond-input-output-reasoning-four-key-properties-of-cognitive-ai-3f 82 CDE 8 cf 1e
  11. 歌手 g(2022 b,10 月 7 日)。推进机器智能:为什么语境决定一切。中等。https://towards data science . com/advancing-machine-intelligence-why-context-is-everything-4 bde 90 FB 2d 79

多臂土匪算法:汤普森采样

原文:https://towardsdatascience.com/multi-armed-bandit-algorithms-thompson-sampling-6d91a88145db

直觉、贝叶斯和一个例子

想象你面前有一排老丨虎丨机。你不知道哪台机器会给你最好的获胜机会。你如何找到最好的吃角子老丨虎丨机玩?你应该怎么做才能有更高的胜算?在一个更常见的场景中,想象你有各种版本的广告或网站布局,你想测试,什么是最好的策略?本文将带您通过 Thompson 采样算法来解决这些问题。

之前,我已经制作了两个视频,关于两个非常基本的算法的直觉: ETC(探索然后提交)epsilon greedy 。这两种算法都需要很长时间来找到最佳老丨虎丨机,有时他们不能找到最佳解决方案。可以考虑的更好的多臂 bandit 算法是 Thompson 采样。汤普森抽样也称为后验抽样。它是一种随机化的贝叶斯算法,容易理解和实现,而且用对数后悔法要快很多。

Thompson 采样在行业中也广泛用于各种用例。举个例子,

  • Doordash 使用 Thompson 采样来动态学习哪些 dash 对消息更敏感,然后优化在给定时间内向谁发送消息的决策。
  • 亚马逊利用汤普森抽样选择网站最优布局,一周内提升转化率 21%。
  • 脸书在视频上传过程中,使用一种称为约束汤普森采样的改进汤普森采样算法来优化视频质量。
  • 网飞在他们的推荐系统中使用了 Thompson 抽样和其他 bandit 框架。
  • Stitch Fix 在其实验平台上增加了汤普森采样。

贝叶斯统计

让我们首先从一些基本的贝叶斯统计开始。假设一只手臂只能给我们奖励或者不给我们奖励。一个手臂(X)的奖励遵循一个伯努利分布:X ~伯努利(θ),意思是这个手臂成功会有概率θ的奖励,失败没有奖励概率 1-θ。

如果我们把这个手臂玩 n 次,总奖励/成功次数(Y)遵循一个二项式分布:Y ~二项式(n,θ)。

假设θ的先验分布为β(α,β):θ~β(α,β)

同样,θ是每个吃角子老丨虎丨机/手臂的获胜概率。我们的目标是找出这个θ是给定的什么数据,即 p(θ|y)。

根据贝叶斯规则,我们可以计算θ的后验分布,它与似然和先验成正比,然后我们写下二项式分布和β分布的方程,它与θ的 y+α+1 次方成正比,与(1-θ)的 n-y+β-1 次方成正比,后者与β(y+α,n-y+β)成正比。

有趣的是,贝塔分布与二项式家族共轭,这意味着如果我们从贝塔先验开始,我们将得到贝塔后验。

作者图片

在等式中,y 是成功的次数,n-y 是失败的次数。这意味着,在每个时间步,如果我们得到了奖励,我们就在第一个参数上加 1,如果我们没有得到奖励,我们就在第二个参数上加 1。最后,第一个参数应该是成功的次数加上 alpha,第二个参数应该是失败的次数加上 beta。接下来我会给你看一个具体的例子。

伯努利·汤普森抽样的一个例子

让我们以两台老丨虎丨机(双臂)为例。注意,我们不知道两臂的实际获胜概率,但假设它们是 0.6 和 0.2。

在时间步长 1:

让我们假设我们没有任何关于每只手臂的获胜概率的先验信息,因此让我们对两只手臂使用β(1,1),即均匀分布。

作者图片

然后让我们从每个分布中随机抽取一个样本,比如我们为臂 1 抽取 0.8,为臂 2 抽取 0.4。因为 0.8 > 0.4,我们玩 arm 1。未知真概率 0.6,arm 1 会给我们奖励。假设手臂 1 确实给了我们奖励。然后,臂 1 的后验分布更新为β(2,1):

作者图片

在时间步长 2:

让我们再次从每个分布中随机抽取一个样本。正如你在新的分配中看到的,手臂 1 有更高的机会抽到了更多的号码,手臂 2 有同等的机会抽到了任何号码。虽然相比 arm2,arm 1 抽到更高号码的几率更大。因为我们是随机抽取数字,所以 arm 2 仍然有机会抽取更高的数字。例如,我们为手臂 1 绘制 0.7,为手臂 2 绘制 0.9。因为 0.7 < 0.9, we play arm 2. With 0.2 probability, arm 2 will give us a reward, let’s assume that arm 2 failed to give us a reward. Then the posterior distribution for arm 2 updates to Beta(1,2):

Image by Author

在时间步长 3:

让我们再次从每个分布中随机抽取一个样本。假设我们为臂 1 绘制 0.8,为臂 2 绘制 0.3。我们玩 arm1。以 0.6 的概率,arm 1 会给我们一个奖励。假设这次 arm 1 没有给我们奖励。然后,臂 1 的后验分布更新为β(2,2):

作者图片

我们一遍又一遍地继续这个过程。随着时间步数的增加,后验分布应该越来越接近真实值。例如,在时间步长 100,我们可以得到 arm1 的β(40,30)和 arm2 的β(8,26)。请注意,这四个数字加起来应该是 104,因为我们从两臂的β(1,1)开始。

作者图片

然后在时间步长 1000,我们可以得到臂 1 的β(432,290)和臂 2 的β(56,226),分布的平均值几乎与实际获胜概率完全匹配。随着时间的推移,标准差应该会越来越小。获胜手臂的标准偏差应该小于另一只手臂,因为一般来说,我们应该有更大的机会从获胜手臂获得更大的价值,因此我们更经常地玩那只手臂。但尽管如此,我们玩 arm 2 的非零概率非常小,这意味着在这一点上,我们将主要做开发而不是探索。对于 Thompson 采样,没有一个有限的截止时间来决定我们什么时候探索,什么时候利用,就像你在其他算法中看到的那样。

作者图片

总的来说,Thompson 采样执行以下操作:

  • 在每个时间步,我们计算每个臂的θ的后验分布。
  • 我们从每一个后验分布中抽取一个样本,并播放具有最大值的臂。

在我们的例子中,我们假设每个时间步的每个手臂遵循伯努利分布,并且获胜概率的先验分布遵循贝塔先验分布。使用其他分布也很常见,例如,可以使用具有高斯先验的高斯分布,或者一般指数族中的其他分布。

希望这篇文章能帮助你对 Thompson 采样算法有一个更好的直觉和理解。详细的遗憾分析,请看一下这本书。谢谢!

参考资料:

https://door dash . engineering/2022/03/15/using-a-multi-armed-bandit-with-Thompson-sampling-to-identify-responsive-dashers/

https://multithreaded . stitchfix . com/blog/2020/08/05/bottoms/

https://dl.acm.org/doi/pdf/10.1145/3097983.3098184

https://www.youtube.com/watch?v=A-JJvYaBPUU&t = 1474s

https://info . dataengconf . com/hub fs/SF % 2018% 20-% 20 slides/DS/A % 20 multi-Armed % 20 bandit % 20 framework % 20 for % 20 recommendations % 20 at % 20 net flix . pdf

https://tor-lattimore.com/downloads/book/book.pdf

多维决策边界:当前方法失败的原因及如何使其有效

原文:https://towardsdatascience.com/multi-dimensional-machine-learning-decision-boundary-how-to-get-it-to-work-7122dca3b3a

决策边界是模型评估的一个非常重要的可视化工具。了解如何让它处理复杂的数据集。

丹尼尔·林肯在 Unsplash 上的照片

所有方法在玩具数据集(如 Iris 数据集)上都运行良好,看起来很棒。然而,当您在真实数据集上尝试您的方法时,事情就崩溃了。决策边界或决策面就是这种情况的一个很好的例子。

决策边界或决策表面是理解机器学习分类器结果的一种很好的方式。在开源软件包网站上有各种各样的例子来解释如何制定决策界限。这些例子是理解这个概念的一个很好的来源,然而,它们在实践中是不够的。

这种不足的主要原因是现有的例子是玩具数据集,如虹膜数据集,其维度有限。在实际应用中,数据集有非常多的维度。

在这个故事中,我将展示一种多维决策边界的方法。

让我们首先回顾一下决策边界或决策面的目标。

决策边界或决策面的目标

机器学习分类器的视觉理解非常重要。有许多基于性能的可视化技术,如 ROC 曲线或精度和召回图。然而,基于性能的可视化不足以理解机器学习分类器。

此外,在许多应用中,仅基于性能的评估并不是最佳的。例如,机器学习分类器可以具有极好的 ROC,然而,底层分类器模型可能太复杂。模型的复杂性不能用性能指标来判断。

决策边界或决策面有助于我们通过以下方式更好地理解机器学习模型:

  • 它帮助我们理解底层模型的复杂性。复杂的决策边界或决策面可能意味着模型复杂或过度拟合。
  • 它告诉我们,在预测新数据时,我们应该有多大的信心。如果有太多的点非常接近决策边界的边缘,那么对新的和看不见的数据的预测就会出错。

当前确定多维决策边界的方法

以下是当前用于制定多维决策边界的不同方法。

取所有特征的两两组合

在这种方法中,采用所有特征的成对组合。在下面的例子中,数据有 4 个特征-萼片宽度、萼片长度、萼片宽度、花瓣长度。当我们对所有特征进行成对组合时,这给了我们 6 个可视化。

图片来源https://sci kit-learn . org/stable/auto _ examples/tree/plot _ iris _ DTC . html # sphx-glr-auto-examples-tree-plot-iris-DTC-py

使用网格方法生成决策边界。对于每个可视化,X 轴和 Y 轴被分成小方框。这就形成了一个网格。例如,在下图中,网格有 9 个(在 X 轴上)* 12 个(在 Y 轴上)= 108 个框。

网格法—作者图片

每个框可以代表萼片长度和萼片宽度的值。对于每个盒子,然后使用训练的分类器来预测类别。框的颜色基于预测的类。

一旦获得彩色网格,来自测试数据集的数据点就绘制在网格上。

使用主成分分析(PCA)

在这种方法中,主成分分析用于将多维数据集简化为 2D 数据集。如上所述,网格方法用于将 2D 空间切割成许多盒子。一个 新的 机器学习分类器以 X 为 PCA 2D 值,Y 为类进行训练。

然后,该机器学习分类器将预测网格中每个点的类别。然后,预测的类可以用于制作网格栅格框的颜色。下图给出了一个说明性的例子。

一旦获得彩色网格,来自测试数据集的数据点就绘制在网格上。

主成分分析和网格方法—图片由作者提供

够了艾瑞斯。欢迎来到现实世界。

正如您到目前为止所看到的,决策边界就像在 Iris 数据集上散步一样。现在让我们看看上述方法是如何用于真实世界的多维数据集的。让我们来看一个电信公司客户的数据集。该数据集包含人口统计信息、服务、账单信息以及客户是否有过交易。

PS:数据集引用在文末说明

电信客户流失数据集(图片由作者提供)

这个数据大约有 20 个字段。几乎 50%的字段是分类的(非数字)。机器学习的任务是开发一个分类器来预测客户流失。现在让我们看看上述决策边界的方法是否可行。

对所有特征进行成对组合的方法对于高维数据不是可行的方法。

成对组合方法仅适用于少量维度。对于大量的特征,例如我们的电信数据集中的特征,它将悲惨地失败。会产生太多的可视化效果,同时查看许多可视化效果会变得很麻烦。此外,您将错过所有分类特征,因为这种方法只对数字特征有效。

PCA 方法的问题在于…PCA 技术本身。

PCA 方法的问题是 PCA 技术的使用。PCA 技术通过基于最高变化分离点来工作。这意味着从数据的角度来看,PCA 空间中的两个相邻点可能非常不同。

为了理解为什么 PCA 不适合,让我们比较一下 PCA 和 TSNE。TSNE(t-分布式随机邻居嵌入)也是一种降维技术。下图显示了五氯苯甲醚和 SNE 霸王龙之间的对比。

PCA vs TSNE(图片由作者提供)

您可以观察到两种可视化效果看起来完全不同。主成分分析只有两种聚类结构,而 SNE 霸王龙有多种聚类结构。该数据集根据人口统计、服务和账单信息拥有各种各样的客户。如主成分分析所示,只有两个集群结构是不现实的。另一方面,T-SNE 是电信客户数据集的更现实的表示。

区别的原因是 PCA 本质上是通过基于最高变化场***点尽可能远地分离来工作的。***** TSNE 基本上是通过分组** 根据点的特性尽可能接近来工作的。**

PCA 是一种很好的降维方法,但如果你想重用结果来制定决策边界,它就不好了。T-SNE 是一种更好的决策边界方法,因为我们希望实际数据中接近的点在决策表面上也接近。

要不要网格…这是个问题

这里是一个网格着色工作原理的回顾。首先,我们基于对应于训练数据集(如 X)和实际标签(如 Y)的 TSNE 点创建分类器。

网格步骤 1(图片由作者提供)

一旦我们训练了分类器,我们就可以用它来预测网格中某个点的颜色。

网格步骤 2(图片由作者提供)

这种方法适用于简单的数据集。然而,对于复杂的数据集,mesh grid 并不理想。下面显示的是两个网格。左边的是基于逻辑回归分类器的颜色。右边的是基于决策树分类器的颜色。这些点对应于测试数据集。点的颜色基于流失预测。蓝点是测试数据集中没有搅动的客户。绿色的点是测试数据集中的顾客。

决策面(图片由作者提供)

查看决策面,它给人的印象是,我们预测客户流失的分类器非常不准确,因为决策面和测试点之间有很多差异。然而,预测流失的分类器具有 83%的 ROC。那么,问题出在哪里?

问题在于预测网格颜色的分类器。数据集太复杂了,不能只用两个 TSNE 点来预测客户流失与否。

因此,网格方法不适用于非常复杂和高维的数据集。

多维数据集的决策边界方法

因此,基于目前的观察,这里给出了我们可以适用于多维数据集的方法。

  • 不要使用成对散点图方法。
  • 不要使用 PCA。用 TSNE 代替
  • 放下网格。专注于创建一个正确的决策面,而不是一个带有网格的不正确的决策边界。如果我们直接使用训练数据集来生成决策面,就可以做到这一点。

这是基于训练数据集决策面没有使用网格。颜色基于直接获取训练数据集的 TSNE 点及其相应的标签(流失或不流失)。

基于训练数据的决策图(图片由作者提供)

现在我们可以叠加我们的测试点,得到如下所示的最终结果。回想一下,蓝点是测试数据集中没有搅动的客户。绿色的点是测试数据集中的顾客。

带有测试数据覆盖的决策表面(图片由作者提供)

这是上面的同一个图像以及所有的颜色代码注释。

带有测试数据覆盖的决策表面(图片由作者提供)

这看起来好多了,我们可以理解为什么分类器的准确率是 83%。大多数客户流失的测试点都落在预测流失的决策面上。此外,客户没有流失的大多数测试点都落在没有预测流失的决策面中。

这种方法适用于复杂的多维数据集。但是,有一些限制:

  • 训练数据集需要非常大(至少超过 10000 行)。原因是我们使用从训练数据集得到的决策表面,而不是网格方法。因此,这需要大量的训练数据来制作一个好的决策表面。
  • 测试数据集应该是训练数据集的子集。尽可能地去除异常值。这确保了测试数据的 TSNE 点被正确地映射到 2D 网格。
  • 一般来说,TSNE 在非稀疏数据上工作得很好。所以这种方法通常用于非稀疏数据集。

希望你喜欢这篇文章,现在准备在复杂和多维数据集上做决策表面。

作为的额外奖励,这里有不同颜色和风格的决策面。享受吧。

决策表面的不同颜色和样式(图片由作者提供)

数据集引用

电信数据集可在此获得。允许与引文一起使用

https://www.kaggle.com/blastchar/telco-customer-churn

Blastchar (2018)。Kaggle 数据集。[www.kaggle.com]https://www.kaggle.com/blastchar/telco-customer-churn

附加资源

网站(全球资讯网的主机站)

你可以访问我的网站进行零编码分析。https://experiencedatascience.com

每当我发布一个新的故事,请订阅保持通知。

**https://pranay-dave9.medium.com/subscribe

你也可以通过我的推荐链接加入 Medium

https://pranay-dave9.medium.com/membership

Youtube 频道

这是我的 YouTube 频道
https://www.youtube.com/c/DataScienceDemonstrated的链接**

基于 TensorFlow 的口袋妖怪类型多标签分类

原文:https://towardsdatascience.com/multi-label-classification-of-pokemon-types-with-tensorflow-8217a38038a6

用深度学习创建 Pokedex

布兰登·斯凯利在 Unsplash 上的照片

为了庆祝新口袋妖怪游戏口袋妖怪传说:阿尔宙斯的发布,我认为做另一个口袋妖怪主题的数据科学项目会很有趣:训练一个神经网络,它将口袋妖怪的图像作为输入,并输出它们的类型。

底漆

在我继续之前,先给那些不熟悉的人简单介绍一下口袋妖怪。口袋妖怪是像动物一样的生物,可以被捕获并训练来与其他口袋妖怪战斗。每个口袋妖怪都有一两个元素类型,表示它可以使用的最强的移动类型,以及它在战斗中对什么类型的口袋妖怪有优势或劣势。比如《口袋妖怪》系列的标志性吉祥物皮卡丘就是电动型的。这意味着它对地面型口袋妖怪有弱点,对水和飞行型口袋妖怪有优势。在游戏中,你可以捕捉野生口袋妖怪,并使用一种名为 Pokedex 的类似百科全书的设备记录它们的类型和其他信息。大多数游戏的目标之一是捕捉和记录土地上每一个口袋妖怪的数据,但我们如何能仅仅通过看它来确定口袋妖怪的类型?这是本文的重点。

数据

我们将在口袋妖怪游戏的各种迭代或世代中使用精灵图像,这些精灵图像来自 Github 资源库,名为 PokeAPI ,可以免费使用,图像属于任天堂。我们还将使用来自 Kaggle 的数据集,它列出了国家 Pokedex 中每个口袋妖怪的类型和数量,我们将使用它来索引和标记我们的图像。总共有 18 种。以前的读者可能还记得,我们使用这个数据集来确定口袋妖怪的最佳团队,在最近的两个游戏中使用。

图像组织如下:

sprites
\- pokemon
    \- other
        \- dream world (SVGs)
        \- official artwork (PNGs)
    \- versions
        \- generation i
            \- red and blue (PNGs with back, gray, transparent, back-gray variants)
            \- yellow (PNGs with back, gbc, gray, transparent, back-gbc, back-gray, back-transparent variants)
        \- generation ii
            \- crystal (PNGs with back, shiny, back-shiny, transparent, transparent-shiny, back-transparent, back-transparent-shiny variants)
            \- gold (PNGs with back, shiny, transparent, back-shiny variants)
            \- silver (PNGs with back, shiny, transparent, back-shiny variants)
        \- generation iii
            \- emerald (PNGs with shiny variants)
            \- fire red and leaf green (PNGs with back, shiny, back-shiny variants)
            \- ruby and sapphire (PNGs with back, shiny, back-shiny variants)
        \- generation iv
            \- diamond and pearl (PNGs with back, female, shiny, back-female, back-shiny, shiny-female variants)
            \- heart gold and soul silver (PNGs with back, female, shiny, back-female, back-shiny, shiny-female variants)
            \- platinum (PNGs with back, female, shiny, back-female, back-shiny, shiny-female variants)
        \- generation v
            \- black and white (PNGs with back, female, shiny, back-female, back-shiny, shiny-female, animated variants)
        \- generation vi
            \- omega ruby and alpha sapphire (PNGs with female, shiny, shiny-female variants)
            \- x and y (PNGs with female, shiny, shiny-female variants)
        \- generation vii
            \- ultra sun and ultra moon (PNGs with female, shiny, shiny-female variants)
            \- icons (PNGs)
        \- generation viii
            \- icons (PNGs with female variants)
    \- default PokeAPI sprites (PNGs with back, female, shiny, back-female, back-shiny, shiny-female variants)
\- items
    \- default PokeAPI items (PNGs)

一些关键术语需要澄清:“闪亮的”是指口袋妖怪有一种不同于正常外观的颜色。例如,闪亮的皮卡丘被太阳晒成了橙色,而不是标志性的黄色皮卡丘。女性口袋妖怪是不言自明的:一些口袋妖怪根据他们的生物性别有不同的外表。例如,雌性皮卡丘有一条心形尾巴,而不是闪电尾巴。最后,“back”指的是口袋妖怪的 back-sprite ,用户在与对手的口袋妖怪战斗中挥舞,对手的前面 sprite 被显示。当我们训练我们的模型时,同一只口袋妖怪的这些不同视图将非常有用,因为截至 2021 年只有 898 只口袋妖怪,这本身并不构成一个大数据集。通过包括来自不同游戏和世代的精灵,考虑到前后精灵、闪亮的口袋妖怪和两性异形的口袋妖怪,我们有了一个更大的数据集,这将对训练我们的模型非常有帮助。在这一点上,让我们过渡到如何使用这些数据来训练我们的分类器。

机器学习方法

由于每个口袋妖怪可以有 1 或 2 个类型,我们将这建模为机器学习中的多标签分类问题。在这里,我们可以为一个目标输入分配多个标签。例如,将一条牛仔布裤子的图像分类为“蓝色”和“牛仔裤”。这需要与更简单的多类分类问题不同的架构,在多类分类问题中,我们有单个互斥的类来分配目标(例如,确定图像中的动物是猫还是狗)。

在多标签场景中,我们定义了一组标签,在本例中为 18 种类型,我们的模型为每个标签分配了一个概率,如果标签概率超过某个阈值(在本例中为 0.5),就可以将我们的目标分类为多个标签。在许多神经网络架构中,我们获得原始输出值的向量。由于口袋妖怪有 18 种可能的类型,我们的分类器将为我们输入模型的每个口袋妖怪图像输出 18 个输出。这些值被转换成概率,允许我们基于这些值和阈值对口袋妖怪的类型进行预测。在我们只是给每个输入分配一个类的情况下(例如,拍摄一个宠物的图像并确定它是猫还是狗),我们经常使用一个 softmax 函数来转换这些原始输出。

这实质上是对输出应用指数变换,并通过对分母中每个原始输出的指数求和来计算概率,分子中是指数化的目标输出。这类似于我们通常通过考虑所有其他事件的概率来计算单个事件的概率。然而,在我们的例子中,由于一些口袋妖怪有不止一种类型,彼此独立,我们需要使用不同的方法。这就是 sigmoid 函数的用武之地。

与 softmax 一样,原始输出也经过指数变换,但请注意,这些概率是相互独立计算的(即,它们的总和不需要等于 1)。所以,比方说,Charizard——火/飞行型口袋妖怪——成为火型的概率与其成为飞行型的概率没有关系,反之亦然。因此,我们有 83%的几率它会着火,74%的几率它会飞起来。因此,我们将在训练阶段评估我们的预测并相应地微调模型时使用该函数。

我们需要考虑的另一个因素是我们将如何评估我们的模型。出于与为什么我们不能使用 softmax 函数来推导概率类似的原因,我们不能简单地使用传统的准确性度量作为我们的模型在训练和验证期间表现如何的手段;准确性本质上是我们正确分类的案例的分数。数学上,这被定义为:

像 softmax 一样,这在我们每个类只有一个正确答案的情况下很有用,但在我们有多个像 Pokemon 模型一样的正确答案的情况下,如果输入不完全正确,它会严重惩罚损失函数,从而导致糟糕的性能。所以回到我们的 Charizard 例子,它不会因为猜对了其中一种类型而给予部分奖励:要么全有,要么全无。当标签分布不均匀时,这尤其成问题,如下所示:

从第 1 代到第 6 代的口袋妖怪类型的频率。作者图片

相反,我们将使用F1-得分指标。这是精度召回的调和平均值,分别测量所有预测阳性病例中正确预测阳性病例和所有实际阳性病例中正确预测阳性病例的比例:

通过使用来自这两个指标的信息,我们可以得到一个更可靠的衡量模型性能的方法。

在我们转到本文的编码部分之前,还有两件关于 F1 分数的事情。先说,以上 F1 分具体到每个类型。为了获得我们的分类器在所有这些类中表现如何的总体感觉,我们可以取 F1 分数的平均值。有几种方法可以做到这一点,但你可以在这里阅读更多的和。在我们的例子中,我们将取 F1 分数的全球平均值,称为宏观 F1 分数。这一指标对所有类型的权重相等,因此不受类别不平衡的影响,这是我们数据的一个挑战:例如,我们有过多的水和正常类型,但很少有幽灵和冰类型的口袋妖怪。通过平等地权衡类型,我们不会受到数据集中主要类型的严重影响。其次,我们将使用一个称为软 F1 分数的损失函数来评估我们的模型性能。这是 F1 分数的修改版本,它是可微分的(这是损失函数允许反向传播的重要先决条件)。为了简洁起见,我不会在这里讲太多细节,但是你可以在这里和这里了解更多信息。

模型实现

我们将使用 TensorFlow 库对我们的神经网络进行编程。我将首先说明,该模型的大部分实现改编自 Ashref Maiza 的教程,用于 TensorFlow 的多标签分类,此外还有将在本文结尾链接的其他重要资源。

让我们从加载我们的库开始,这将是相当多的:

接下来,我们将对口袋妖怪类型字典及其在 Pokedex 中的索引进行一些数据清理。

作者图片

现在,我们可以开始创建我们的训练数据集。我们将使用 get_images()函数加载从第 1 代到第 5 代的口袋妖怪精灵的图像,该函数将每代的文件夹作为输入,因此我们将总共运行该函数 5 次:

回想一下,由于单个口袋妖怪的数量相当少,我们使用来自多个世代和游戏的口袋妖怪精灵,除了背部精灵、闪亮的变体和雌性口袋妖怪,作为增加我们训练数据大小的内置方式。通过这种方式,我们的训练数据集包含超过 15,000 张图像。

接下来,我们将使用 one-hot 编码将我们的类型二进制化,并使用tf.data API 将我们的数据转换成适合多标签分类的格式。

Labels:
0\. Bug
1\. Dark
2\. Dragon
3\. Electric
4\. Fairy
5\. Fighting
6\. Fire
7\. Flying
8\. Ghost
9\. Grass
10\. Ground
11\. Ice
12\. Normal
13\. Poison
14\. Psychic
15\. Rock
16\. Steel
17\. Watersprites/sprites/pokemon/versions/generation-iv/diamond-pearl/125.png [0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
sprites/sprites/pokemon/versions/generation-i/yellow/back/gbc/67.png [0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0]
sprites/sprites/pokemon/versions/generation-ii/silver/180.png [0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0]

sprites/sprites/pokemon/versions/generation-iv/diamond-pearl/125.png下的口袋妖怪是电动型口袋妖怪,名为电击兽,所以在二值化的类型向量中,它在对应于该类型的索引 3 处有一个 1。类似地,sprites/sprites/pokemon/versions/generation-i/yellow/back/67.pngsprites/sprites/pokemon/versions/generation-ii/silver/180.png分别是战斗型、豪力和电动型、弗拉菲,它们的标签向量也相应格式化。最后,我们将格式化该数据集,以便以 TensorFlow 可以处理的方式将图像和标签链接到该数据集。

既然我们的训练和验证数据集已经相应地准备好并格式化了,我们终于准备好创建和训练我们的模型了!尽管我们能够扩充我们的数据集,但我们将使用迁移学习来提高其性能并减少训练时间。通过迁移学习,我们在一个更大的数据集上使用一个预先训练好的模型——并且不需要与我们自己的数据集相关——作为我们模型的主干,以识别新环境下的类别。TensorFlow 的一个好处是,他们有一个丰富的预训练模型数据库,您可以轻松导入,特别是像我们这样的计算机视觉。我们唯一需要添加的是一个密集的隐藏层和一个对应于 18 种口袋妖怪类型的输出层,然后我们可以编译和训练模型。

这花了 15 分多一点的时间在我的电脑上运行,它有一个英伟达 GTX 1660 超级 GPU。查看训练和验证性能,我们似乎没有任何明显的过度拟合,训练和验证的最终宏观 F1 分数分别为 0.5413 和 0.4612,尽管在训练和验证数据集的最后几个时期,性能显示出收益递减,但仍然非常好。同样,训练和验证的最终损失值分别为 0.54 和 0.5976。

分别用于训练和验证数据的软宏 F1 损失函数和宏 F1 分数的图。作者图片

现在让我们测试模型。我特意在前 5 代口袋妖怪上训练它,因为 Kaggle 的数据集一直到第 6 代,所以我很好奇它在新口袋妖怪上的表现如何,而没有在训练或验证阶段看到他们的图像。我还要补充一点,从第 6 代开始,口袋妖怪从常规的 2D 精灵转变为口袋妖怪的 3D 模型,所以看看我们的模型如何利用它以前从未见过的数据将会更加有趣。

Delphox
Type
['Fire', 'Psychic']

Prediction
['Fire', 'Psychic']

Clauncher
Type
['Water']

Prediction
['Water']

Noivern
Type
['Flying', 'Dragon']

Prediction
['Dragon', 'Flying', 'Psychic', 'Water']

Quilladin
Type
['Grass']

Prediction
['Grass']

Gogoat
Type
['Grass']

Prediction
[]

Hawlucha
Type
['Fighting', 'Flying']

Prediction
['Flying', 'Psychic']

Goomy
Type
['Dragon']

Prediction
['Water']

Sylveon
Type
['Fairy']

Prediction
['Flying', 'Water']

我们看到,我们的分类器在预测这些口袋妖怪的类型方面做得相当好。给我留下特别深刻印象的是,它完美地完成了妖火红狐的(一只狐狸般的口袋妖怪)类型。它努力正确预测仙子精灵的仙女类型,并放弃了 Gogoat ,因为没有一个类型概率超过 0.5(然而,我会注意到,仙女是在这一代中作为第 18 种类型首次添加的,一些老口袋妖怪要么追溯性地将他们的类型从正常类型改变为仙女类型,要么将其添加到最初的单一类型)。让我们来看看一些更老的口袋妖怪,如皮卡丘Charizard :

Pikachu
Type
['Electric']

Prediction
['Water']

Charizard
Type
['Fire', 'Flying']

Prediction
['Flying', 'Normal', 'Psychic']

有趣的是,皮卡丘被预测为一种精神类型,可能是因为它是一种非常常见的类型,如类型柱状图所示(加上精神口袋妖怪与其他类型相比在颜色上有很大不同);Charizard 的火分类不符合我们的模型。这也可能是因为我们在训练中没有使用任何旧口袋妖怪的 3D 模型,所以不同的格式也可能使我们的模型有点偏离。

在本文中,我们学习了如何训练一个神经网络来预测口袋妖怪类型,利用多年来各种游戏迭代中精灵和形式的多样性。我们从更新的 3D 模型游戏中获得了相当好的预测。尽管如此,仍有很大的改进空间。例如,可以执行子采样来抑制类别不平衡问题,并进一步降低我们的损失函数。我们还可以使用不同的损失函数来评估模型性能,例如二进制交叉熵而不是软 F1 损失,这在多标签分类问题中更常用。arXiv 上最近的预印本介绍了另一个损失函数,该函数来自多标签分类的 F1 分数,称为 sigmoidF1,可能有希望。

尽管如此,我希望这是对口袋妖怪多标签分类的有趣介绍。请在评论中告诉我您对该模型的任何想法或问题,或者其他改进性能的建议。编码快乐!

参考

[1]https://towardsdatascience . com/multi-label-image-class ification-in-tensor flow-2-0-7 D4 cf 8 a4 BC 72
【2】https://github.com/PokeAPI/sprites
【3】https://www.kaggle.com/abcsds/pokemon
【4】https://glassboxmedicine . com/2019/05/26/class ification-sigmoid-vs-soft max/
【5】【T12

基于 BERT 和张量流的多类文本分类

原文:https://towardsdatascience.com/multi-label-text-classification-using-bert-and-tensorflow-d2e88d8f488d

从数据加载到预测的分步教程

照片由阿尔方斯·莫拉莱斯Unsplash 拍摄

目录

  1. 简介
  2. 数据准备
    2.1 加载数据集
    2.2 【可选】观察随机样本
    2.3 在训练和测试集中拆分
  3. 数据建模
    3.1 用 TensorfFlow Hub 加载 BERT
    3.2【可选】观察语义文本相似度
    3.3 创建并训练分类模型
    3.4 预测
    3.5 盲集评估
  4. [可选]保存并加载模型以备将来使用
  5. 参考文献

1.介绍

在这篇文章中,我们将开发一个多类文本分类器。

分类的任务是指对一个给定的观察值预测一个类别。由于这个原因,训练这样一个模型唯一需要的输入是一个数据集,它包括:

  • 文本示例
  • 关联标签

出于这篇文章的目的,我们需要知道BERT****(BI directionEn coderRpresentations fromTtransformers)是一个基于 transformers 的机器学习模型,即能够学习单词之间上下文关系的注意力组件。更多细节可在参考文献中找到。

有趣的是,我们将为非英语文本开发一个分类器,我们将展示如何通过从 TensorFlow Hub 导入不同的 BERT 模型来处理不同的语言。

2.数据准备

2.1 加载数据集

在之前的一篇文章中,我们分析了罗马图书馆的读者评论公开数据集,该数据集由“ 罗马图书馆研究院”⁴.公开提供在没有标签的情况下,我们使用主题建模(一种无监督的技术)来发现读者评论中重复出现的主题,从而通过推理确定所借书籍的主题和读者的兴趣。

我们现在将使用前面分析中出现的主题作为标签来对用户评论进行分类。

值得注意的是,之后的步骤可以应用于任何包含至少两列的数据集,分别用于文本样本及其标签:

图片作者。

由于数据集包含来自罗马图书馆的用户评论,他们的语言是意大利语。我们从之前的分析中随机抽取了五个主题,每个主题对应一个标签。它们的分布如下:

  1. 关于女性在社会中的地位的评论,或者有着强势女性主角的小说(n=205,25.5%)
  2. 专辑和音乐会的评论,或音乐家的传记(n=182,22.64%)
  3. 关于经济和社会政治状况的书籍和论文的评论(n=161,20.02%)
  4. 与日本或日本文化相关的评论(n=134,13.67%)
  5. 科技泄密论文综述(122 篇,15.17%)

图片作者。

出于分类的目的,我们需要数字标签。因此,我们将主题描述映射到整数,如下所示:

图片作者。

2.2[可选]观察随机样本

虽然对文本分类的目标来说,这不是绝对必要的,但是我们可能想要检查来自不同主题的一些随机样本,以便更好地理解数据。

因此,我们定义了一个函数,它将一个列上的过滤条件作为输入,并打印一个满足该条件的随机评论,以及一个可读性更好的英文翻译:

现在,让我们观察一些样品。通过拨打print_rand_example(df, "Labels", 1),我们可以看到一些顾客对女性及其社会地位的评论:

作者输出的print_rand_example(df, "Labels", 1). 图像。

当阅读评论的第一句话时,之前通过无监督方法制造的主题描述似乎是合理的。我们可以从同一个主题看另一个随机样本,同样调用print_rand_example(df, "Labels", 1):

图片作者。

日语相关的话题呢?让我们通过print_rand_example(df, "Labels", 3)了解一下:

图片作者。

2.3 在训练和测试装置中的分离

3.数据建模

3.1 用 TensorFlow Hub 加载 BERT

TensorFlow Hub 是经过训练的机器学习 models⁵.的存储库

数据科学家可以方便地从 TensorFlow Hub 加载大型复杂的预训练模型,并在需要时重用它们。

有趣的是,当我们在 TensorFlow Hub 上搜索“ bert ”时,我们可能还会应用问题域(分类、嵌入、…)、架构、语言等过滤器,以方便检索更适合我们需求的模型:

在 TensorFlow Hub⁵.网站上搜索“bert”的结果图片作者。

在这个例子中,我们使用了universal-sentence-encoder-cmlm/multilingual-base ⁶模型,这是一个支持 100 多种语言的通用句子编码器。如参考 paper⁷.中所述,使用条件屏蔽语言模型来训练它

我们要实现的是把文本变成捕捉句子级语义的高维向量。因此,我们从 TensorFlow Hub 提供的端点加载preprocessorencoder层,并定义一个简单的函数从输入文本中获取嵌入。

重要的一点是,可以根据任务和输入语言选择导入任何偏好的模型。

由于模型基于 BERT transformer 架构,它将生成形状为[batch size, 768]pooled_output(整个序列的输出嵌入),如下例所示:

图片作者。

3.2[可选]观察语义文本相似性

由于嵌入提供了句子级语义的向量表示,我们可能想要观察不同文本序列之间的相似性。

为此,我们绘制了通过cosine similarity ⁸计算的不同文本样本的语义文本相似度作为 follows⁹:

例如,我们期望两个几乎相同的句子,如:

  • 这本书很有趣
  • Il romanzo è interessante (小说有趣)

将显示出高度的语义相似性,并且我们还期望这两个句子将与具有不同含义的第三个句子共享可比较的相似性度量,并且事实正是如此:

图片作者。

由于模型的性质,我们可以有效地估计不同语言中句子之间的语义相似度:

图片作者。

3.3 创建和训练分类模型

由于我们面临着一个多类分类问题,并且我们之前注意到我们的主题分布略有不平衡,我们可能希望在模型训练期间观察不同的度量。

由于这个原因,我们定义了函数来分别计算训练期间每个类的precisionrecallF1 score,然后返回这些类的平均值:

精确度、召回率和 F1 分数的定义。图片修改自⁰.维基百科

我们现在将模型定义为preprocessorencoder层,后面是dropoutdense层,具有softmax激活函数和等于我们想要预测的类别数量的输出空间维度:

一旦我们定义了模型的结构,我们就可以编译并适应它。我们选择训练 20 个时期的模型,但是我们也使用EarlyStopping回调以便在训练期间监控validation loss:如果度量在至少 3 个时期内没有改善(patience = 3),则训练被中断,并且来自validation loss显示最佳值(即最低值)的时期的权重被恢复(restore_best_weights = True):

训练日志。图片作者。

最后,我们可以绘制训练过程中每个受监控指标的假设值,并比较训练和验证曲线:

训练历史。图片作者。

3.4 预测

模型现在已经准备好了,是时候测试一些预测了:

图片作者。

给定这三个输入句子,我们期望模型分别预测主题 id3(Japan/Japanese/..)、 1 ( Woman/Women/..)和 0 ( Economy/Politics/..)。

为了测试我们的假设,我们定义了一个简单的函数包装model.predict。特别是,当我们试图在五个可能的标签之间进行分类时,model.predict将返回一个大小为五的numpy.ndarray。我们在模型的最后一层使用的softmax激活函数实际上提供了目标类的离散概率分布。因此,我们可以简单地采用与较高概率(np.argmax)相关联的指数来推断预测的标签:

上面的代码片段产生了预期的结果:[3,1,0]

3.5 盲集评估

我们最初将数据集分为训练集和测试集,但在训练和验证过程中同时使用了这两个数据集。

为了公正地评估我们的性能,我们在一个新的数据集上评估预测的质量,该数据集包含模型在训练(盲集)期间没有“T22”看到的观察值:

盲评估测试集。图片作者。

我们可以通过使用数据准备部分中使用的相同代码来观察主题分布:

图片作者。

通过遵循用于准备训练集和验证集的相同步骤,我们可以将主题描述映射到数字标签,并检查一些随机样本。

例如,让我们检查来自 id 为 1 的主题的评论:print_rand_example(test_set, "Labels", 1)

图片作者。

让我们也观察来自 id 为 3 的主题的评论:print_rand_example(test_set, "Labels", 3)

图片作者。

我们现在可以测试盲集性能:

图片作者。

4.[可选]保存并加载模型以备将来使用

这项任务对于文本分类模型的开发来说不是必不可少的,但它仍然与机器学习问题有关,因为我们可能希望保存模型并在需要时加载它以用于未来的预测。

通过调用model.save,可以将模型保存为SavedModel格式,其中包括模型架构、权重和调用函数的追踪张量流子图。这使 Keras 能够恢复内置层和自定义对象:

我们现在可以根据需要加载模型以供将来使用:

就是这样!

5.参考

1德夫林,雅各布;张明伟;李,肯顿;图塔诺娃、克里斯蒂娜、伯特:语言理解的深度双向变形金刚前期训练 2018、arXiv:1810.04805 v2

[2]阿希什·瓦斯瓦尼、诺姆·沙泽尔、尼基·帕尔马、雅各布·乌兹科雷特、利永·琼斯、艾丹·戈麦斯、卢卡兹·凯泽、伊利亚·波洛舒欣,“关注是你所需要的一切”,2017 年, arXiv:1706.03762

[3]https://towards data science . com/romes-libraries-readers-comments-analysis-with-deep-learning-989d 72 bb 680 c

[4]https://www . bibliotechediroma . it/it/open-data-commenti-lettori

https://tfhub.dev/

[6]https://tfhub . dev/Google/universal-sentence-encoder-cmlm/multilingual-base/1

[7],,,杨,Daniel Cer,Jax Law,Eric Darve,用条件掩蔽语言模型学习通用句子表征,2021, arXiv:2012.14388

https://en.wikipedia.org/wiki/Cosine_similarity

https://www.tensorflow.org/hub/tutorials/bert_experts

https://en.wikipedia.org/wiki/Precision_and_recall

[11]https://www.tensorflow.org/guide/saved_model

基于 Python 中 MSTL 的多季节时间序列分解

原文:https://towardsdatascience.com/multi-seasonal-time-series-decomposition-using-mstl-in-python-136630e67530

了解如何使用 MSTL 分解多季节时间序列,了解 MSTL 如何工作,并查看 MSTL 在现实世界数据中的表现

图片作者。

在本文中,我们将分解一个具有多个季节成分的时间序列。我们将探索一种最近开发的算法,称为使用黄土(MSTL) [ 1 ]的多季节趋势分解,并讨论其相对于现有方法的优势。最后,我们将使用 Statsmodels 中新添加的模块在 Python 中尝试 MSTL,并将其应用于真实世界的数据。

介绍

时间序列分解就是将时间序列分解成几个部分,最明显的是:趋势部分、季节部分和残差部分。用 Python 实现的单季节成分分解时间序列的方法有很多,比如STL【2】和X-13-ARIMA-席【3】。但是有多个季节成分的时间序列呢?

以电力需求为例。电力需求具有每日季节性(白天比深夜需求更多)、每周季节性(周末与工作日)和每年季节性(由于供暖和制冷需求不同,夏季和冬季的需求不同)。

多季节时间序列有多普遍?出奇的普遍。天气可以驱动多个季节。想想温度和日照时间是如何在每天和每年的循环中重复的。仅仅是因为天气吗?不会。人类活动(例如,朝九晚五的工作日、工作日与周末、每月发薪日)也每天、每周等等重复。因此,从空气污染到餐馆需求的时间序列可能有多个季节成分。

现在我们知道许多时间序列可能有多个季节性,让我们想想如何分解它们。2021 年 7 月 Bandara、Hyndman 和 Bergmeir 提出了一种新的多季节分解算法,称为黄土多季节趋势分解(MSTL) 1。MSTL 将是本文的重点。

我们将首先重温时间序列分解的概念。然后,我们将探索 MSTL,它是如何工作的,为什么要使用它。最后,我们将通过 Statmodels 中的一个模块使用 MSTL,我最近添加了这个模块,并使用它来分解电力需求时间序列。

时间序列分解

正如你现在所知道的,时间序列分解就是将时间序列分解成趋势、季节性和残差(图 1)。

图一。时间序列分解方法可以是加法或乘法。分解方法的目的是计算单个组件。图片作者。

但是你为什么要分解一个时间序列呢?时间序列分解用于任务的时间序列分析,例如:

  • 探索性数据分析(例如,调整季节性因素后,本季度失业率是否有所上升?);
  • 预处理时间序列,以确定和估算异常值和缺失数据;
  • 从时间序列中提取特征,供以后在分类、回归和预测任务中使用;
  • 构建预测(例如,可以分别预测各个组成部分,然后汇总得出最终预测)。

一些常用的时间序列分解方法包括:

这些方法旨在从时间序列中提取单个季节性成分。

可以提取多个季节成分的方法包括 Prophet [4]、TBATS [5]和 STR [6]。Prophet 和 TBATS 推断季节性成分,作为训练预测模型的一部分。相反,MSTL 直接关注于时间序列的分解。稍后,我们将看到 MSTL 在一组基准时间序列上的表现优于其他方法1。至此,让我们深入了解 MSTL 的细节。

如果您想了解更多关于时间序列分解方法以及如何提取预测特征的信息,请查看本课程 时间序列预测特征工程

MSTL:这是什么?

MSTL 代表使用Loess【1】的 M 倍数 S 季节- T 渲染分解。这是一种将时间序列分解为趋势分量、多个季节分量和残差分量的方法。

MSTL 假设时间序列可以表示为加法分解(图 2):

图二。多季节成分的可加分解。图片作者。

其中每个季节性成分代表不同的季节性(例如,每天、每周、每年等)。).我们将在后面看到 MSTL 在 STL 的基础上迭代提取每个季节成分。

MSTL:为什么要用它?

1中,作者将 MSTL 与其他多季节分解方法进行了比较:Prophet [4]、TBATS [5]和 STR [6]。他们比较了这些方法的准确性和计算效率。他们认为 MSTL 是有用的,因为它是:

  • 准确地说,MSTL 通常在一系列基准时间序列上产生最低的 RMSE;
  • 在计算效率方面,MSTL 的执行时间是所有方法中最短的;
  • 对异常值鲁棒,MSTL 能够通过向底层 STL 拟合传递一个标志来使用 STL 的异常值鲁棒版本;

我还要补充一点:

  • MSTL 可以模拟随时间变化的季节性(例如,与夏季相比,冬季的每日电力需求模式将有所不同)。

简单是 MSTL 相对于提取季节成分的预测模型(如 Prophet 和 TBATS)的另一个优势。对于预测模型,要创建一个好的预测和一个好的分解,需要调整许多参数和考虑许多事情。而 MSTL 完全专注于分解时间序列,根据我的经验,需要调整的参数较少。

所以现在我们知道什么是 MSTL 以及为什么要使用它。让我们继续了解 MSTL 是如何工作的。

MSTL:它是如何工作的?

MSTL 建立在 STL(使用 L oess 的 S easonal- T rend 分解的缩写)之上,这是一种可以提取单个季节成分的分解方法。STL 反过来建立在一种叫做黄土[7]的平滑方法之上(简称LocallyEestimatedScatter plotSmoo thing)。黄土和 STL 值得单独写一篇博文,所以我只给出它们的概要,这样我们就可以把注意力集中在 MSTL 了。

黄土概述

黄土是一种用于拟合散点图的平滑曲线的方法[7]。黄土通过将多项式拟合到该点周围的数据窗口来计算任意给定点处的平滑曲线。这给了我们两个重要的参数:窗口大小,它决定了拟合的平滑程度,以及多项式 l 的次数(通常是 0 或 1)。

黄土更容易直观理解(图 3)。在动画中,我们可以看到从真实数据(蓝点)计算黄土曲线(灰点)的过程:我们查看数据窗口(红点),我们希望在该窗口中计算黄土值并拟合该窗口内的曲线(直线)。在这个例子中,多项式次数是 1。

黄土拟合时间序列的曲线可用于模拟时间序列的趋势(图 3)。在图 4 中,我们看到当选择了适当的窗口大小时,黄土能够捕捉零售时间序列的趋势。

图 3。黄土拟合散点图的图解。在每个点上,一个多项式(在这种情况下是一条线)被拟合到一个数据点窗口(红色)。绿色曲线表示多项式拟合中使用的权重,使得窗口边缘的数据比中心的数据具有更小的权重。图片作者。

图 4。对于不同的窗口大小参数值“frac ”,黄土拟合(橙色)每月零售销售数据集(蓝色)。当窗口尺寸太小(frac=0.02)时,我们看到黄土过拟合季节性,当窗口尺寸太大(frac=0.8)时,它欠拟合数据,当选择一个好的窗口尺寸(frac=0.1)时,黄土捕捉时间序列的总体趋势。零售额数据集是零售额的月度数据集,可在这里找到。图片作者。

STL 概述

STL 的目标是从时间序列中提取一个单一的季节性成分、一个趋势成分和一个残差成分(图 5)。它通过将黄土应用于原始时间序列的多重变换来实现这一点,并递归地提取趋势和季节分量。在原论文[2]和这篇博文[ 8 ]中对 STL 有很大的解释。

图 5。零售数据集的 STL 分解。零售数据集是零售量的月度数据集,可在这里找到。图片作者。

那么要了解 MSTL,需要知道 STL 的哪些主要参数呢?我们将看到 MSTL 使用 STL 提取的季节性成分。STL 使用黄土计算季节分量。因此,STL 中有一个与季节分量相关的窗口大小和多项式次数的参数。因此,STL 季节性参数汇总如下:

  • period,我们希望 STL 提取的季节性成分的周期(例如,period=12如果我们希望每年季节性,数据的频率是每月);
  • seasonal,STL 中用于提取季节分量的黄土的窗口大小(这决定了提取的季节分量有多平滑和规则);
  • seasonal_deg,黄土提取 STL 中季节分量所用的多项式次数(一般设置为 0 或 1)。

MSTL 算法

现在我们知道了什么是 MSTL(一种建立在 STL 上提取多个季节成分的算法)以及为什么要使用它(它优于现有的方法),让我们来讨论一下算法。MSTL 算法可以分为:1)预处理步骤和 2)分解步骤。

预处理步骤

有两个预处理步骤通常用于许多时间序列分析任务,而不仅仅是 MSTL。

第一步:估算缺失数据。在 MSTL 的 R 实现中,这是使用[na.interp](https://www.rdocumentation.org/packages/forecast/versions/8.16/topics/na.interp)来完成的。在 Python 实现中,您必须在使用 MSTL 之前估算缺失数据。

步骤 2:如果用户指定,应用一个 框 Cox 变换。如果我们认为时间序列是由加法分解描述的而不是,则使用此选项。Box Cox 变换可以将原始时间序列转换成可以用加法分解描述的新时间序列。

如果输入时间序列没有缺失数据并且不需要 Box Cox 变换,则可以跳过这两个步骤。

分解

我们将算法的分解部分分成多个步骤。

第一步:使用 STL 提取每个季节成分。我们将想要提取的每个季节性成分的时间段传递到 MSTL(例如,对于具有每日和每周季节性的每小时数据periods = (24, 24*7)——每日季节性的时间段为 24 小时,每周季节性的时间段为 24*7=168 小时,等等)。).

我们将从最短的时间段(例如,每天)到最长的时间段(例如,每年)开始迭代每个季节组件。在每次迭代中,我们通过 STL 提取季节性成分(图 6a、6b),然后从时间序列中减去它(图 6c)。然后,我们将得到的去季节性时间序列传递到下一次迭代,在该迭代中,我们提取下一个季节性成分(图 6d)等等,直到所有季节性成分都被提取出来(图 6e)。

图 6。MSTL 算法分解部分的第一步。此图显示了具有趋势、每日季节性、每周季节性和噪声的时间序列示例。从原始时间序列中反复提取和减去季节性成分,直到提取出所有的季节性。图片作者。

为什么我们从最短的周期开始迭代?因为如果首先提取较长的季节性,则算法会错误地将较短的季节性作为较长季节性的一部分。

第二步:提炼每个提取的季节性成分。到目前为止,我们已经对每个季节性因素和完全去季节性的时间序列进行了估计(图 7a)。我们现在再次迭代每个季节组件。在每次迭代中,我们将这个单一的季节性成分加回到从步骤 1 结束时开始的完全去季节性的时间序列中(图 7b)。然后使用 STL 从该时间序列中提取相同的季节性成分(图 7c)。从时间序列中减去季节性成分的新估计值,使其再次完全消除季节性因素(图 7d)。

这一步很有帮助,因为我们现在传递给 STL 的时间序列只包含感兴趣的单个季节性成分、趋势和噪声。这使得 STL 可以更容易地重新捕获它在步骤 1 中遗漏的季节性成分的任何部分。

重复此步骤 N 次,在1中使用 N = 2。

图 7。MSTL 算法分解步骤的第二步。通过将季节性成分添加回去季节性的时间序列并使用 STL 重新提取季节性成分来细化每个季节性成分。该图显示了这一步骤在每日季节性中的一次迭代。图片作者。

第三步:提取趋势。从步骤 2 中出现的最后一次 STL 拟合中提取趋势分量(即最长周期的季节性分量)。

第四步:提取残渣。在第 2 步结束时,通过从完全去季节性的时间序列中减去趋势分量来计算残差分量。

在这 4 个步骤之后,我们剩下一个趋势部分、一组精确的季节性部分和一个残差部分。

注意:在原始论文1中,算法的另一个分支处理用户指定没有季节成分的情况。在这种情况下,时间序列只是被平滑以提供趋势分量,而不会返回季节分量。

MSTL 参数

实际上,在 Python 中使用 MSTL 只需要知道几个参数。让我们总结一下最重要的参数:

  • periods:传递到 STL 的每个季节性成分的周期period(例如,对于带有我们设置的periods = (24, 24*7)的每日和每周季节性的每小时数据);
  • windows:季节性窗口大小seasonal,传递给每个相应季节性组件的 STL(例如windows = (11, 15))。MSTL 使用基于实验的默认值,这些实验给出了1中的最佳结果;
  • seasonal_deg:在【1】中使用的季节分量的多项式的次数为seasonal_deg = 0
  • lmbda:如果需要 Box Cox 变换,那么我们需要为 Box Cox 变换的λ参数选择一个值。λ可以手动设置,lmbda=0.7,也可以自动设置,lmbda="auto"

Python 中 MSTL 应用于电力需求数据

澳大利亚维多利亚的电力需求,数据集

我们现在来看一个现实世界的数据集,我们可以在其中应用 MSTL。让我们来看看澳大利亚维多利亚州的电力需求,数据集1中使用。我们可以在这里找到以半小时为间隔记录需求的原始数据【9】。我们遵循与1相同的步骤:我们将数据重新采样到每小时,并将我们的视角限制在 2012 年上半年,以关注每日和每周的季节性。以下部分的所有代码都可以在这里找到。

让我们绘制时间序列图(图 8 ),并对数据中的任何季节性建立直觉。

图 8。2012 年 1 月至 5 月澳大利亚维多利亚每小时电力需求(兆瓦)。图片作者。

我们预计每天都会有与电力需求相关的季节性。让我们通过绘制每天的每小时需求并按月划分来确认这一点。

图 9。每日季节性。相应月份中每天的需求由浅蓝色线条表示。深蓝色线表示平均需求。有一种日常模式,白天的需求比深夜的需求多。图片作者。

在图 9 中,我们看到有每日季节性。我们还可以及时看到每日的季节性变化。在夏季月份(例如,一月),在下午 4 点左右有一个每日峰值,而在冬季月份(例如,五月),现在有两个峰值,一个在上午 8 点左右,另一个在下午 6 点左右。每日季节性变化的一个原因可能是从夏天使用空调到冬天使用供暖设备的转变。

现在,让我们绘制数据图,看看每周的季节性。

图 10。每周季节性。淡蓝色的线表示一年中每周的需求。深蓝色线表示平均需求。周末的平均需求似乎低于平日。图片作者。

在图 10 中,我们看到存在周季节性,也就是说,周末的需求比平日少。

这些图证实了每日和每周季节性的存在,并给了我们关于它们如何变化的信息。让我们继续,用 MSTL 来提取这些季节性。

利用 MSTL 分解电力需求数据

我们可以像这样导入 MSTL:

from statsmodels.tsa.seasonal import MSTL

我们需要指定的主要参数是periods,它是时间序列中每个季节成分的周期。我们期望有每日和每周的季节性,因此,我们设置periods = (24, 24*7)。我们也可以通过向stl_kwargs传递一个字典来设置输入到底层 STL 模型的参数。

stl_kwargs = {"seasonal_deg": 0} model = MSTL(data, periods=(24, 24 * 7), stl_kwargs=stl_kwargs)res = model.fit()

趋势、季节和残差分量都可以从结果对象res中访问:

seasonal = res.seasonal # contains both seasonal components
trend = res.trend
residual = res.resid

让我们使用res.plot()绘制分解图(图 11)。

图 11。电力需求时间序列的 MSTL 分解为趋势、每日季节性(季节性 _24)、每周季节性(季节性 _168)和剩余分量。图片作者。

让我们检查来自 MSTL 的每日季节性成分,并检查它是否抓住了我们从图 9 的每日曲线图中获得的直觉(即,在夏季,每天下午 4 点有一个峰值,在冬季有两个峰值)。

图 12。MSTL 在一月和五月的样本日期间提取的每日季节性成分。图片作者。

我们看到,MSTL 已经正确地捕捉到了每日的季节性,甚至显示了 5 月份的额外每日峰值(图 12)。这显示了 MSTL 在模拟随时间变化的季节性因素方面的作用。

让我们检查来自 MSTL 的每周季节性成分,并检查它是否抓住了我们从图 10 中的每周曲线图得到的直觉(即,周末期间的需求比工作日少)。

图 13。MSTL 在一月和五月的三周内提取的每周季节性成分。每周季节性因素捕捉了周末的下跌,并且在两个月内保持稳定。我们仍然可以看到一些每日季节性已经泄漏到每周的组成部分。图片作者。

在图 13 中,我们看到 MSTL 的每周季节性因素确实能够捕捉到周末需求的下降。

结论

我们做到了!MSTL 是分析多季节时间序列的一个很好的工具。在本文中,我们看到了如何使用 Python 通过 MSTL 分解现实世界中的多季节时间序列。

如果你做到了这一步,感谢你的阅读,我希望你发现这是有帮助的。

如果您想了解有关黄土、STL 和 MSTL 的更多信息,本课程将在时间序列预测的 特征工程 中详细介绍。

密码

笔记本:https://github.com/KishManani/MSTL

参考

1 班达拉,k .,海德曼,R.J .和伯格梅尔,c .,2021 年。MSTL:一种用于多季节模式时间序列的季节趋势分解算法。arXiv 预印本 arXiv:2107.13462 。

[2]r . b .克利夫兰、W.S .克利夫兰、J.E .麦克雷和 I .特潘宁,1990 年。STL:季节性趋势分解。 J .罗腾飞。Stat6 (1),第 3–73 页。

[3]贝尔、W.R .和希尔默,南卡罗来纳州,1984 年。经济时间序列的季节调整问题。商业杂志&经济统计2 (4),第 291–320 页。

[4]泰勒,S.J .和勒撒姆,b .,2018。大规模预测。《美国统计学家72 (1),第 37–45 页。

[5]a . m . De Livera、r . j . hynd man 和 R.J. and Snyder,2011 年。用指数平滑法预测具有复杂季节模式的时间序列。美国统计协会杂志106 (496),第 1513–1527 页。

[6]多库门托夫和海因德曼,R.J .,2021 年。STR:使用回归的季节趋势分解。通知数据科学杂志

[7]w . s .克利夫兰和 S.J .德夫林,1988 年。局部加权回归:一种通过局部拟合进行回归分析的方法。美国统计协会杂志83 (403),第 596–610 页。

http://www.gardner.fyi/blog/STL-Part-II/

[9]奥哈拉-怀尔德,m .海德曼,R.J .王,e . 2021。tsibbledata:“tsibble”的不同数据集。网址:https://CRAN.R-project.org/package=tsibbledata。r 包版本 0.3.0。(知识共享许可)

XGBoost 多步时间序列预测

原文:https://towardsdatascience.com/multi-step-time-series-forecasting-with-xgboost-65d6820bec39

本文以 24 小时电价预测为例,展示了如何使用 XGBoost 生成多步时间序列预测。

阿格巴洛斯Unsplash 上拍摄的照片

许多博客文章和 Kaggle 笔记本都将 XGBoost 应用于时间序列数据。然而,根据我的经验,现有资料要么将 XGBoost 应用于时间序列分类,要么应用于一步预测。本文展示了如何将 XGBoost 应用于多步提前时间序列预测,即预测跨度大于 1 的时间序列预测。这与一步到位预测有很大不同,因此需要这篇文章。

XGBoost 1是梯度提升树的快速实现。它在包括时间序列预测在内的许多领域取得了良好的效果。例如,论文“我们真的需要深度学习模型进行时间序列预测吗?”表明 XGBoost 可以在许多时间序列预测任务中胜过神经网络[2]。

请注意,本文的目的不是对所选的预测问题产生高度准确的结果。相反,目的是说明如何使用 XGBoost 生成多输出预测。因此,本文不详细讨论时间序列数据探索和预处理,也不讨论超参数调优。关于这个主题已经有很多写得很好的材料了。

本文的其余部分结构如下:

  1. 首先,我们将仔细看看本教程中使用的原始时间序列数据集。
  2. 然后,我将描述如何获得一个带标签的时间序列数据集,该数据集将用于训练和测试 XGBoost 时间序列预测模型。
  3. 最后,我将展示如何训练 XGBoost 时间序列模型,以及如何使用它生成多步预测。

数据集描述和问题表述

本教程中的数据是丹麦以欧元/兆瓦时为单位的批发电力“现货市场”价格。这些数据可以在 Energidataservice [4]上免费获得(根据“全球范围的、免费的、非排他性的和其他无限制的使用许可”[5])。数据具有每小时的分辨率,这意味着在给定的一天中,有 24 个数据点。我们将使用 2017 年 1 月 1 日至 2021 年 6 月 30 日的数据,这将产生一个包含 39,384 个批发电价每小时观察值的数据集。

本教程的目的是展示如何使用 XGBoost 算法生成一个预测 Y ,包括给定输入的 m 小时的预测电价, X,包括 n 小时的过去电价观测值。这类问题可以认为是单变量时间序列预测问题。更具体地说,我们将把预测问题公式化为有监督的机器学习任务。

用 XGBoost 进行时间序列预测的数据准备

与任何其他机器学习任务一样,我们需要将数据分成训练数据集和测试数据集。请注意,数据点不被打乱是很重要的,因为我们需要保持观察的自然顺序。

对于有监督的 ML 任务,我们需要一个带标签的数据集。我们通过所谓的固定长度滑动窗口方法获得由 (X,Y) 对组成的标记数据集。使用这种方法,长度为 n+m 的窗口“滑过”数据集,并且在每个位置,它创建一个 (X,Y) 对。滑动窗口从数据集的第一次观察开始,每次滑动移动 S 步。在本教程中,我们将使用步长为 12。滑动窗口方法摘自论文《我们真的需要深度学习模型进行时间序列预测吗[2]其中作者还使用 XGBoost 进行多步提前预测。

在代码中,通过首先产生元组的列表来获得标记的数据集,其中每个元组包含用于切片数据的索引。第一个元组可能是这样的: (0,192)。这意味着创建了一个由数据点 0-192 组成的切片。索引元组列表由函数get_indices_entire_sequence()生成,该函数在 repo 的utils.py模块中实现。为了方便起见,它显示如下。

然后,索引元组列表被用作函数get_xgboost_x_y()的输入,该函数也在 repo 的utils.py模块中实现。同样,它显示在下面。该函数的参数是指数列表、数据集(例如训练数据)、预测范围、 m、和输入序列长度、n。该函数输出两个 numpy 数组:

  1. 所有的模型输入,即具有形状的 X,(实例数, n )。
  2. 所有目标序列,即形状为【实例数, m ) Y、

然后,这两个函数用于生成由 (X,Y) 对组成的训练和测试数据集,如下所示:

使用 XGBoost 生成多步时间序列预测

一旦我们创建了数据,就必须实例化 XGBoost 模型。

然后我们将它包装在 scikit-learn 的MultiOutputRegressor()功能中,使 XGBoost 模型能够产生长度大于 1 的输出序列。这个包装器适合每个目标的一个回归量,目标序列中的每个数据点都被认为是这个上下文中的一个目标。因此,当我们预测提前 24 小时时,包装器实际上适合每个实例的 24 个模型。这使得该函数相对低效,但是该模型仍然比诸如变压器模型的神经网络训练得更快。对于好奇的读者来说,xgboost 包现在似乎天生支持多输出预测[3]。

包装的对象还具有我们从其他 scikit-learn 和 xgboost 模型中了解到的predict()函数,因此我们使用它来生成测试预测。

XGBoost 时间序列预测模型能够立即生成合理的预测,无需调整超参数。正如本文回购中的笔记本所示,其预测的平均绝对误差为 13.1 欧元/兆瓦时。测试数据集的平均值为 54.61 欧元/兆瓦时。

仔细观察下图中的预测,该图显示了相对于目标值的预测,我们可以看到该模型的预测通常遵循目标值的模式,尽管当然还有改进的空间。

XGBoost 预测值与实际值(图片由作者提供)

完整示例可在本报告的笔记本中找到:

https://github.com/KasperGroesLudvigsen/xgboost_time_series

摘要

在本教程中,我们介绍了如何处理时间序列数据,以便将其用作 XGBoost 时间序列模型的输入,我们还了解了如何将 XGBoost 模型包装在多输出函数中,从而允许模型生成长度超过 1 的输出序列。从 MAE 和上面的图可以看出,XGBoost 可以产生合理的结果,而无需任何高级数据预处理和超参数调整。这表明 XGBoost 非常适合时间序列预测——这一概念也在前面提到的学术文章[2]中得到支持。

就是这样!我希望你喜欢这篇文章🤞

请留下评论让我知道你的想法。

关注更多与数据科学的时间序列预测绿色软件工程环境影响相关的帖子🍀

并随时在 LinkedIn 上与我联系。

参考

[1]https://arxiv.org/abs/1603.02754

https://arxiv.org/abs/2101.02118

[3]https://www . LinkedIn . com/posts/tunguz _ data science-machine learning-人工智能-activity-6985577378005614592-HnXU?UTM _ source = share&UTM _ medium = member _ desktop

[4]https://www . energidataservice . dk/tso-electricity/Elspotprices

[5]https://www . energidataservice . dk/Conditions _ for _ use _ of _ Danish _ public _ sector _ data-License _ for _ use _ of _ data _ in _ ed . pdf

基于 Keras 的多任务分类学习

原文:https://towardsdatascience.com/multi-task-learning-for-computer-vision-classification-with-keras-36c52e6243d2

了解如何构建一个能够同时执行多种图像分类和多任务学习的模型

马库斯·温克勒在 Unsplash 上的照片

M 多任务学习(multi-task Learning,MLT)是机器学习的一个子领域,通过一个共享模型同时学习多个任务。这种类型的学习有助于提高数据效率和训练速度,因为共享模型将从同一数据集中学习几个任务,并且由于不同任务的辅助信息,将能够更快地学习。此外,它还减少了过度拟合,因为考虑到训练数据的标签对于每个任务是不同的【1】,模型将更难与训练数据完美拟合。

本文将介绍 MTL,并展示如何使用 tensorflow 的 Keras 模块在真实数据上实现和训练它。在我的 GitHub 资源库中可以找到完整的代码,以及一个 Jupyter 笔记本,您可以在其中试验您所学到的内容:

https://github.com/JavierMtz5/ArtificialIntelligence

数据预处理

为了使解释简单易懂,将使用 CIFAR-10【2】数据集,该数据集在 MIT 许可 下提供。该数据集由 60000 幅 32×32 像素的 RGB 图像组成,分为 10 个不同的类别。它被分为 50000 个训练样本和 10000 个测试样本,并且是完全平衡的,这意味着数据集每类包含 6000 个图像。可以通过执行以下命令轻松加载数据集:

数据加载。作者图片

数据集包含以下类:飞机汽车鹿青蛙卡车。多任务模型要学习的两个任务是这些标签上的分类,参见:

  • 任务 1: 修改后的 CIFAR10 数据集上的多类分类(飞机汽车卡车标签,修改说明如下)。
  • 任务二:二元分类(标签为动物车辆)。

实现上述两个分类任务的一个更有效的选择是训练一个只学习第一个任务的模型,其输出将用于预测二元类动物车辆。这方面的一个例子是将青蛙的图像作为输入传递给模型,为此模型将获得作为输出的 frog 类。因为青蛙是一种动物,那么图像将被分类到动物类(这个解决方案的模式可以在下面看到)。

图片作者。乔尔·亨利在 Unsplash 上拍摄的青蛙照片

尽管如此,本文将通过应用多任务学习来解决这个问题,因为,虽然它不是最有效的例子,但它完美地展示了 MTL 在这种类型的问题中的有用性和应用,并且它是发展知识的极好基础。

考虑到这一点,并且为了有一个用于训练的平衡数据集,属于鹿的类的实例将被删除。这样做是因为最初数据集包含 30000 个属于动物的样本(5000 个样本×6 个类别)和仅 20000 个属于车辆的样本(5000 个样本×4 个类别),这将使数据集在二进制分类任务方面不平衡。马和鹿的实例被删除,因为它们与猫和狗具有非常相似的特征,因此可能会增加训练的复杂性,因为区分这些类的实例会更加困难。

请注意,在此清理之后,标签具有以下类别:[0、1、2、3、5、6、8、9](缺少 4 和 7,分别对应于鹿和马)。因此,有必要更新标签,使它们从 0 到 7 进行编号,这一步可以在我的资料库中的 Jupyter 笔记本中找到。但是,为了更好地学习,请尝试自己编写代码!

对于多任务学习来说,训练标签是特定于任务的,这一点至关重要。因此,在 n 任务训练中,将定义不同标签的 n 个数组。在这种情况下,第一个任务要求标签是从 0 到 7 的整数(每个类一个数字),第二个任务要求标签是 0 和 1(因为是二进制分类)。数据先前被预处理,使得标签是从 0 到 7 的数字,并且如所期望的,用于二进制分类的标签将基于初始的 0 到 7 标签来构建,使得如果实例对应于动物,则它将是 0,并且当它对应于车辆时,它将是 1。

创建模型

由于这两个任务都使用图像作为训练数据,卷积网络(CNN)将被用作共享模型,因此它将学习最初从图像中提取最重要的特征。共享模型的输出将被展平,并被引入到对应于每个任务的分支。两个分支都将由密集层组成(因为我们已经展平了它们的输入),每个密集层具有不同数量的神经元,其输出层将由 2 个和 8 个神经元组成,分别用于二进制和多类分类任务。除了输出层使用 softmax 和 sigmoid 分别进行多类和二类分类之外,所有层都使用 ReLu 作为激活函数。下面可以看到所描述模型的低级概要以及定义它的代码。

模型建筑。作者图片

在代码中,可以看到当定义神经网络的每一层时,对象接收前一层,即其输出是被定义层的输入的层。通过这种方式,Keras 模型可以针对每个任务进行分离,使得两个分支/子模型都从同一个共享模型开始。

通常模型会更新它们的权重,寻求优化它们的损失函数(参见我以前的文章用单层感知器进行数字分类),但是在 MTL,一般模型的每个分支都在学习不同的任务,所以有必要为每个任务指定一个损失函数。然而,张量流在反向传播过程中仅使用单个损失函数的结果,因此还必须定义联合损失函数,其最简单的形式是不同损失函数值的加权和。在这种情况下,联合损失函数定义如下:

关节损失功能。作者图片

注意,当 ɣ 等于 0 时,模型将仅接收从任务 2 获得的损失,而如果 ɣ 等于 1,模型将接收任务 1 的损失。这允许仅针对任务 1(如果 ɣ = 1)、仅针对任务 2(如果 ɣ = 0)或针对两个任务(如果 0 < ɣ < 1)来训练模型,这允许模型用于多个场景,这取决于模型需要什么。

通过在 loss_weights 参数中指定模型每个分支的损失函数的权重,可以在编译时在先前创建的模型中定义联合损失函数。见下面的函数。

训练模型

一旦对数据进行了预处理,并且为这两项任务定义了模型,就该对其进行训练了。。fit() 方法用于训练,与定义正常模型时不同,它接收与模型中的分支/输出一样多的数组,如 y 参数。另一方面,与正常模型一样,必须指定批次大小和时期数。在这种情况下,将使用 128 的批量大小,并且它将被训练 15 个时期。

用 3 个不同的γ值训练模型: 00.51 ,代码测量并打印每次训练的运行时间。

评估模型的性能

最后,我们将通过绘制这两项任务在 15 个时期内的精确度来观察不同伽马值的 MTL 模型的结果。这要归功于上面定义的fit _ batch _ multask _ models()函数返回的历史列表。

不同伽马值的精度与时期图。作者图片

从不同的伽马值获得的图表清楚地显示了上述内容:伽马的极值意味着模型将学习仅执行任务之一,伽马的中间值意味着模型将设法学习完成两个任务。此外,该模型对两种任务都获得了超过 90%的准确率,能够更好地执行二元分类。应该提到的是,二分类比多分类更简单,所以得到的结果是可以预料的。

此外,在 Jupyter 笔记本中,随机拍摄测试数据集的图像,并从模型中提取预测,以检查它是否有效,随时修改它并尝试新事物!

结论

当伽玛值平衡两个任务的损失函数的权重时,多任务学习已经设法在两个任务中实现非常好的结果,并且当取极端伽玛值时,它也完美地完成单任务模型的任务,因此对于模型需要偶尔完成某些任务的情况,这是非常有趣的选择。必须考虑到,模型中的分叉意味着更多的计算成本,因此需要更长的执行时间。此外,尽管建立了极端的伽马值,但是执行时间将继续很长,因为模型在两个分支中以任一方式执行正向和反向传播过程。如果模型只需要执行一个任务,这是很重要的,因为训练单任务模型(没有分支)在计算成本方面会更有效。

最后,应该提到这种类型的架构的巨大可能性。将第一过滤器(共享模型)应用于输入,然后基于定制分支进行预测的事实使得极大地优化训练模型所需的资源成为可能,这些模型需要执行在某些方面具有相似性的任务。已知的和强大的模型可以用作这种类型的架构的基础或共享模型,实现能够在许多不同的任务中实现非常好的结果的模型,总是具有它们的共同性质的一部分。

参考

迈克尔·克劳肖。基于深度神经网络的多任务学习:综述。 arXiv 预印本 arXiv:2009.09796 ,2020

【2】首页:

Tensorflow API:

https://www.tensorflow.org/datasets/catalog/cifar10

用 ROC 曲线和 ROC AUC 进行多类分类评价

原文:https://towardsdatascience.com/multiclass-classification-evaluation-with-roc-curves-and-roc-auc-294fd4617e3a

使用 OvR 和 OvO 策略使最常用的分类评估度量适应多类分类问题

作者图片

当评估多类分类模型时,我们有时需要调整二元分类中使用的指标,以便在这种情况下工作。我们可以通过使用 OvR 和 OvO 策略来做到这一点。

在这篇文章中,我将展示如何适应 ROC 曲线和 ROC AUC 指标的多类分类。

ROC 曲线和 ROC AUC 评分是评估二元分类模型的重要工具。总之,它们通过所有可能的阈值向我们显示了类别的可分性,或者换句话说,模型对每个类别的分类有多好。

正如我在另一篇文章中已经解释过的,我们可以比较 ROC 曲线(上图)和它们各自的直方图(下图)。直方图越分离,ROC 曲线也越好。

ROC 曲线比较。图片作者。

类分离直方图比较。图片作者。

但是这个概念不能立即应用于多类分类器。为了在这个场景中使用 ROC 曲线和 ROC AUC,我们需要另一种方法来比较类别:OvR 和 OvO。

在接下来的部分中,我会更好地解释它,您也可以在我的 github 上查看代码:

https://github.com/vinyluis/Articles/tree/main/ROC Curve and ROC AUC

OvR 还是 OvO?

OvR —一个与其余

OvR 代表“One vs Rest”,顾名思义,它是一种通过同时将每个类与所有其他类进行比较来评估多类模型的方法。在这种情况下,我们选择一个类,并将其视为“积极”类,而所有其他类(其余的)则被视为“消极”类。

通过这样做,我们将多类分类输出减少为二类分类输出,因此可以使用所有已知的二类分类度量来评估这种情况。

我们必须对数据中出现的每个类重复这一过程,因此对于一个 3 类数据集,我们得到 3 个不同的 OvR 分数。最后,我们可以对它们进行平均(简单平均或加权平均)以得到最终的 OvR 模型分数。

三级设置的 OvR 组合。图片作者。

卵——一对一

正如你可能想象的那样,OvO 代表“一对一”,与 OvR 非常相似,但我们不是将每个类与其他类进行比较,而是比较数据集所有可能的两类组合。

假设我们有一个 3 类场景,我们选择组合“类 1 对类 2”作为第一个场景。第一步是获得仅包含这两个类的数据集的副本,并丢弃所有其他类。然后,我们将 real class = "Class1 "的观察定义为我们的积极类,将 real class = "Class2 "的观察定义为我们的消极类。既然问题是二进制的,我们也可以使用我们用于二进制分类的相同度量。

请注意,“类别 1 与类别 2”不同于“类别 2 与类别 1”,因此这两种情况都应考虑在内。因此,在 3 类数据集中,我们得到 6 个 OvO 分数,在 4 类数据集中,我们得到 12 个 OvO 分数。

在 OvR 中,我们可以平均所有的 OvO 分数,以获得最终的 OvO 模型分数。

三级设置的 OvO 组合。图片作者。

OvR ROC 曲线和 ROC AUC

我将使用我在二元分类 ROC 文章中使用的函数来绘制曲线,只做一些修改,这些修改可在这里获得。如果您愿意,也可以使用 scikit-learn 版本。

在这个例子中,我将使用一个包含三个类的合成数据集:“苹果”、“香蕉”和“橙子”。它们在每个类的组合中都有一些重叠,这使得分类器很难正确地学习所有的实例。数据集只有两个特征:“x”和“y”,如下所示:

多类散点图。图片作者。

对于这个模型,我训练了 scikit-learn 的 RandomForestClassifier 的一个默认实例。

在下面的代码中,我们:

  • 迭代所有类
  • 准备一个辅助数据帧,使用一个类为“1”,其他类为“0”
  • 绘制类别分布的直方图
  • 绘制每种情况的 ROC 曲线
  • 计算特定类别的 AUC

上面的代码输出了每个类别相对于其他类别的直方图和 ROC 曲线:

ROC 曲线和直方图。图片作者。

正如我们所看到的,“橙色”类的分数比其他两个类低一点,但在所有情况下,分类器在预测每个类方面都做得很好。我们还可以在直方图上注意到,我们在真实数据中看到的重叠也存在于预测中。

为了显示每个 OvR AUC 分数,我们可以简单地打印它们。我们也可以取分类器的平均分数:

# Displays the ROC AUC for each class
avg_roc_auc = 0
i = 0
for k in roc_auc_ovr:
    avg_roc_auc += roc_auc_ovr[k]
    i += 1
    print(f"{k} ROC AUC OvR: {roc_auc_ovr[k]:.4f}")
print(f"average ROC AUC OvR: {avg_roc_auc/i:.4f}")

输出是:

apple ROC AUC OvR: 0.9425
banana ROC AUC OvR: 0.9525
orange ROC AUC OvR: 0.9281
average ROC AUC OvR: 0.9410

在这种情况下,平均 ROC AUC OvR 为 0.9410,这是一个非常好的分数,反映了分类器在预测每一类中的表现。

OvO ROC 曲线和 ROC AUC

使用与上一个实验相同的设置,首先需要做的是建立一个包含所有可能的类对的列表:

classes_combinations = []
class_list = list(classes)
for i in range(len(class_list)):
    for j in range(i+1, len(class_list)):
        classes_combinations.append([class_list[i], class_list[j]])
        classes_combinations.append([class_list[j], class_list[i]])

classes_combinations列表将包含所有组合:

[['apple', 'banana'],
 ['banana', 'apple'],
 ['apple', 'orange'],
 ['orange', 'apple'],
 ['banana', 'orange'],
 ['orange', 'banana']]

然后我们迭代所有的组合,类似于 OvR 的情况,我们

  • 准备一个辅助数据帧,只包含两个类的实例
  • 将类 1 的实例定义为“1”,将类 2 的实例定义为“0”
  • 绘制类别分布的直方图
  • 绘制每种情况的 ROC 曲线
  • 计算特定组合的 AUC

上面的代码绘制了所有直方图和 ROC 曲线:

ROC 曲线和直方图。图片作者。

请注意,正如所料,“苹果 vs 香蕉”图与“香蕉 vs 苹果”图不同。和前面的例子一样,我们可以单独评估每个组合,并检查模型的不一致性。

我们还可以显示 AUC 并计算平均 OvO AUC:

# Displays the ROC AUC for each class
avg_roc_auc = 0
i = 0
for k in roc_auc_ovo:
    avg_roc_auc += roc_auc_ovo[k]
    i += 1
    print(f"{k} ROC AUC OvO: {roc_auc_ovo[k]:.4f}")
print(f"average ROC AUC OvO: {avg_roc_auc/i:.4f}")

输出是:

apple vs banana ROC AUC OvO: 0.9561
banana vs apple ROC AUC OvO: 0.9547
apple vs orange ROC AUC OvO: 0.9279
orange vs apple ROC AUC OvO: 0.9231
banana vs orange ROC AUC OvO: 0.9498
orange vs banana ROC AUC OvO: 0.9336
average ROC AUC OvO: 0.9409

在这种情况下,平均 ROC AUC 为 0.9409,接近 OvR 方案获得的分数(0.9410)。

结论

OvR 和 OvO 策略可以(并且应该)用于使任何二元分类度量适应多类分类任务。

评估 OvO 和 OvR 结果还有助于了解模型难以描述的类,以及可以添加或移除哪些功能来改善模型的结果。

如果你喜欢这个帖子…

支持我一杯咖啡!

给我买杯咖啡!

看看这个很棒的帖子

使用 Keras 预测情感的多类文本分类:有无单词嵌入的比较

原文:https://towardsdatascience.com/multiclass-text-classification-using-keras-to-predict-emotions-a-comparison-with-and-without-word-5ef0a5eaa1a0

单词嵌入增加了文本分类模型的价值吗?让我们在这个探测情绪的多类预测任务中找出答案

腾雅特Unsplash 上拍摄的照片

介绍

自然语言处理中的单词嵌入

在自然语言表达中,相似的词出现在相似的上下文中。例如,在这个句子中——“我不舒服,我生病了。“我可能得了 covid,”。单词— 不舒服生病covid 是表示一个人生病的关键词,这是显而易见的。像头痛疼痛跑步 - 鼻子咳嗽等单词也有类似的上下文。然而,关于政治体育的句子中很少会出现这些单词。在体育的上下文中,最频繁出现的关键词可以是速度耐力比赛等等。

格伦·凯莉Unsplash 上拍摄的照片

因此,前一组词更多地出现在人们谈论健康或疾病的上下文中,而第二组关键词更可能出现在体育文章或新闻的上下文中。他们很少会重合,但如果重合,可能是关于一个可能生病的球员或谈论从事体力要求高的运动的后果,两者都有体育和健康的重叠背景。这些案例预计会相对较少。

因此,我们可以说,如果一个单词在其他文档中的类似上下文或单词分布中出现过,则在给定上下文或单词分布中找到该单词的概率更高。

那么什么是单词嵌入呢?

单词嵌入是自然语言文本的密集矢量表示,包含关于给定单词上下文的信息。该概念解决了其他语言建模策略的局限性,如以稀疏矩阵表示文本的 tf-idf 和词袋模型,这些模型包含冗余信息,使该过程在计算上昂贵且耗时,并增加了模型的复杂性。此外,它们也特别大,因为矩阵的大小通常和词汇表的大小一样,这就带来了“维数灾难”的问题。

换句话说,在单词嵌入中,单词以这样一种方式被表示为向量(即数字),即在相似上下文中出现的单词在该词汇表的向量空间中间隔很近。为了进一步解释这一点,如果我们记下我们目前拥有的关键字,假设它们已经出现在 N 个文档中,那么,“生病”和“咳嗽”的向量的距离将小于“咳嗽”和“速度”或“咳嗽”和“胜利”之间的距离,而“胜利”和“匹配”之间的距离将小于“胜利”和“生病”。

这是一种确定文本的数字表示的方法,它试图捕捉在多个文档中出现的单词之间的上下文相似性。

获得单词嵌入有多种方式。word2vec 算法学派用于使用ann导出嵌入。Word2vec 的经典实现是在 Gensim 中,您可以在连续单词包(CBOW)模型或跳过 Gram 模型之间进行选择。CBOW 通过尝试预测给定上下文中最合适的单词来学习表示,而在 skip grams 中,它通过尝试预测给定单词的最合适上下文来学习。

关于多级分类

在机器学习中,一个 监督的多类分类任务 是一个样本可以被分配给一组类中的一个且仅一个类。例如,在情感分析任务中,样本可以是正面的,也可以是负面的,其中有两个类别可供选择。很多时候,情绪也可以是积极的、消极的或中性的,有三类可供选择。

第一个例子是特殊类型的多类分类过程。由于有两类可供选择,即正类和负类,所以称之为二元分类任务。另一个典型的例子是在欺诈检测任务中,交易可能是欺诈或真实的。

皮卡伍德Unsplash 上拍摄的照片

这里的第二个例子有两个以上的类可供选择。有两个以上类别可供选择的分类任务统称为多类别分类问题,其中在两个以上的目标类别中,一个且仅一个类别可以分配给一个样本。

多类分类的评估比二进制分类稍微复杂一些,因为您将评估一个 NxN 矩阵,其中 N 是任务中类的数量,而不是二进制分类的 2x2 矩阵。因此,为了简化,使用了平均技术,如算术平均(宏)、加权平均和总体精度。由于准确度不是最佳指标,通常根据数据集的类别分布在算术平均值和加权平均值之间做出选择。

他的案例研究是一个多类分类任务。在这里,我将预测与给定文本相关的情绪,从六个不同的类别中选择——快乐、悲伤、愤怒、爱、惊讶和恐惧。本文解决了以下问题:

  1. 使用 Word2vec 嵌入启动 Keras 嵌入层权重是否会显著提升模型的性能?
  2. 从编程角度看哪个好?
  3. 通过 word2vec 模型、预测模型训练后更新的 Keras 嵌入层权重以及没有使用 word2vec 嵌入初始化层权重的情况下的相似性的比较。

为了回答这些问题,我将使用两种嵌入策略来训练分类器:

策略 1: Gensim 的嵌入,用于初始化 Keras 嵌入层的权重

策略 2: 使用反向传播进行改进,随机初始化嵌入层,即跳过单词嵌入的使用。

注意:按照教程,Python 库需求在这里 列出

一、关于数据

一、数据来源

我在 Kaggle 这里和拥抱脸数据集这里上使用了一个公开的数据集。数据集包含带有相应情感标签的文档列表。这些数据被分成用于构建机器学习模型的训练、测试&验证集。

二世。基础数据统计

  1. 16k 训练、2k 测试和 2k 验证实例。我将使用训练数据来分割和验证模型,并使用测试数据进行测试。我没有在本文中使用验证数据。
  2. 没有丢失的值
  3. 以下是训练和测试数据的分布:

作者在 Plotly 图表工作室上绘制的图表

作者在 Plotly Chart Studio 上绘制的图形

处理没有缺失值的数据更容易😊显然,数据集是不平衡的。训练和测试数据遵循相同的情绪分布。大多数样本要么被标为“悲伤”,要么被标为“快乐”。为了简单起见,我将保留这个分布用于分类器训练。

二。数据预处理和特征工程

i .标签的 OneHot 编码— 因为我们的标签是分类的,所以它们被转换成大小为 1 x 6 的二进制数组,其中每个位置可以取值为 01 。在这个数组中,除了代表与数据样本相关联的标签的索引之外,所有的值都是 0 。例如,如果在第一个位置我们有' joy ,并且样本被标记为' joy ',那么该数组将看起来像 [1,0,0,0,0,0] ,其中每隔一个位置引用其他标签。同样,假设第三个位置是'悲伤',样本标记为'悲伤',数组变成 [0,0,1,0,0,0]

在这段代码中,我首先加载了具有标准列名的数据集。我使用了 Panda 的类别列类型 ,它自动为列类别分配数字类别代码。接下来,在第 10 行,我已经使用了 TensorFlow 的 one_hot 方法为六种情绪构建了一个 hot 编码矩阵。

二。使用 Gensim 的 Word2Vec 模型训练— 代码非常简单。 Gensim 的 word2vec 所需的输入是样本的标记化形式。为此,我使用了 NLTK 的 word_tokenize 方法。

接下来,我使用下面的代码为这个数据集生成嵌入。注意,我只使用了训练数据集。除此之外,我已经设置了通常的默认配置,并使用 sg=1 的 skip-gram 模型进行指示。和..窗口大小为 20,这意味着模型将被训练,同时试图从给定的单词中预测前面的 20 个单词和后面的 20 个单词。

使用 word2vec 的注意事项是 —当您测试模型性能或将模型应用于未知数据样本时,您需要像准备训练样本一样预处理令牌。现在,如果 word2vec 模型看到一个未知单词,那么它将引发一个 KeyError

为什么?

因为每个单词嵌入都是使用唯一标识该嵌入所针对的单词的关键字来存储的。对于新的 vocab,密钥将不可用,因此会出现错误。这使得 Word2Vec 的定制模型的使用不灵活,因为它是在一个小数据集上训练的,这使得它无法捕获完整范围的词汇。但是,如果它是一个在大型数据集上预先训练的英语模型,那么在“正确”英语中出现的大多数单词都有可能在模型中被捕获。

有两种方法可以处理定制模型中的这种不灵活性—

  1. 通过使用用于训练 Word2Vec 模型的词汇,从测试数据中比较并丢弃 Vocab(OOV) 中的个单词。这样,您就不太可能遇到测试数据中缺少 vocab 的错误。
  2. 错误处理 —在这个方法中,对于你遇到的每一个错误,你都应该删除或者处理 OOV 词。

你应该避免的解决方案:一起使用训练和测试集 vocab,因为那可能导致数据泄漏。此外,这并不能保证看不见的实例不会失败。

我选择了方法 1,下面是实现。我简单地遍历了列表,删除了测试数据中没有出现在 word2vec 模型的 vocab 中的单词。请注意,如果您使用 Python 的 set 操作来删除令牌,令牌的顺序将被打乱,因此用处不大。

三世。生成嵌入矩阵:

为了在 Keras 嵌入层中使用 word2vec 嵌入,我们需要在一个vocab _ size x Embedding _ size矩阵中组织权重,在本例中为 15210 x 300 。下面是我如何使用 Gensim 获得这个。换句话说,这只是一个查找矩阵,其中第行的单词向量是 word2vec 模型词汇表中第行的单词向量。**

四。准备培训和测试数据

接下来,准备训练和测试数据的时间到了,我已经用 word2vec 词汇表中的单词索引替换了标记。当分类器训练时,通过将标记索引与嵌入矩阵中的行号进行匹配来提取单词向量。

最终,样品的长度被标准化为 20 。如果长度超过 20 ,则样本在末尾被截断,如果长度小于 20,则在末尾再次用零填充,如第 24 行和第 25 行所示。

如何确定序列长度:

现在,为了使用 Keras 的嵌入层训练一个人工神经网络模型,我需要标准化输入文本长度。因此,我通过绘制每个样本中单词数量的直方图来分析样本长度。平均来说,大多数样本包含大约 15 个 T21 式的单词。为了捕捉比平均字数多一点的单词,我选择了 20 ,介于第 50 个和第 75 个百分位数之间。

作者在 Plotly Chart Studio 上绘制的图形

三。训练多类分类器

现在我们已经准备好了数据,是时候深入研究分类器训练了。在本文中,我将只关注 Keras 嵌入层是如何工作的。 关于如何使用 Keras 进行 ML 模型训练的详细概述包括代码在此。

那么,Keras 嵌入层是做什么的呢?

Keras 嵌入层通常用于使用深度学习的文本建模任务。它简单地通过输出维数初始化输入维数矩阵,其中输入维数是词汇表的大小,输出维数是代表向量的大小,以构成整个 vocab 的所有单词嵌入的查找表。对于词汇表中代表单词或标记的每个输入整数,该数字将用于从查找表中找到单词嵌入的索引。

现在,如果我们有一个预训练的权重,我们可以使用权重参数加载它,并且我们已经生成了一个查找表。另一种情况是,您没有预先训练的权重,查找表是随机生成的(权重=无),并使用预测中的误差进行改进。

i .使用 Word2vec 嵌入来训练分类器:

在本节中,我将展示用于训练分类器的代码。请注意,我在结尾使用了一个具有 6 个单元的全连接层(因为我们有 6 种情绪要预测)和一个“softmax”激活层。Softmax 为多类别问题中的每个类别分配概率,这些概率的总和必须为 1.0 。因此,一旦我们获得这些概率,我们就使用概率最高的标签作为与样本相关的最有可能的标签。

此外,我使用了 10 纪元,并耐心地提前停止了 2 纪元。解释和细节可以在本节开头链接的博客中找到。

在训练时,分类器,基于确认损失选择的最佳模型,在第六个时期。下面是模型的训练和验证损失曲线。请注意,在最后一个时期,训练损失是最低的,而验证损失是均匀的,约为 0.6。

作者在 Plotly Chart Studio 上绘制的图形

型号性能:

为了评估模型性能,我从检查点重新加载了它-

然后,我用它来获得对训练集的预测,如下面的代码所示:

并且在测试集上显示如下代码:

最后,我使用 SkLearn 的分类报告生成分类指标,如下所示:

现在,让我们完成剩下的程序,在最后比较和评估这两种方法。

二。无 Word2Vec 嵌入初始化的模型训练

我使用 Keras tokenizer 和使用 Keras 嵌入层进行了相同的分类器训练

完整的代码如下:

在模型训练之后,这是训练和验证损失的样子:

作者在 Plotly Chart Studio 上绘制的图形

四。评估:

唷!所以你在这里成功了,让我们称之为—

模型 1: 模型用 word2vec 嵌入来训练

模型 2: 模型在没有 word2vec 嵌入的情况下训练

一、模型性能对比

产生的分类矩阵中代码和模型性能的基本差异总结如下:

模特表演对比总结|作者图片

显然,性能没有显著不同,这些结果表明模型 2 在召回率和 F1 分数方面更好,而模型 1 在精确度方面更好。让我们仔细看看…

微观、宏观、加权—准确度、精密度或召回—哪一个?

两种型号的分类报告|作者图片

想象一下,你正在设计一个用于心理健康咨询的聊天机器人,这些预测被用来检测情绪和做出反应,不识别悲伤或愤怒的人的成本会很高,因为咨询可能会出错,这对每个人都很重要。辨识度越高,服务越好。在这种情况下,对于每一个假阴性,客户都会受到影响,这将影响你作为顾问的服务。因此,在这种情况下(通常在医学用例中),回忆是重要的,因为它测量了多少预测实际上是正确预测的。

此外,由于我们的数据集是不平衡的,最合适的评分技术是 加权 它考虑到了类的不平衡,并根据每个类的真实实例数计算标准化的指标。

因此,似乎模型 2 只是稍微好一点,请查看下面的报告解释和混淆矩阵比较。

解读分类报告和混淆矩阵:

这是两个混淆矩阵,混淆出现在相似的地方——观察这里的模式。两个模型都成功地预测了快乐悲伤,模型 2 中的真阳性略多一些。相反,模型 1 在识别愤怒恐惧真阳性方面表现稍好——这些类别具有较少的训练样本数量。似乎模型 1 错误地将其他样本归类为愤怒,比其他人更高。大约 78%的惊喜样本被模型 1 错误地归类为愤怒,令人惊讶的是,在六十六个惊喜样本中只有一个被正确预测。因此,在分类报告中,愤怒惊讶的准确率都很低。惊喜的精度看似很高,因为没有其他职业被错误地预测为惊喜。所以,回忆 _ 惊喜—1(TP)/1(TP)+0(FP)= 1****

作者在 Plotly 图表工作室绘制的图表

在模型 2 中,预测正确的预测惊喜样本更多——66 个中的 42 个(TP)(所有阳性= TP + FN )。因此,召回率更高,为 64%。这个模型也较少与愤怒混淆,因此整体性能略有提高,因为在这个案例研究中他们是少数。****

作者在 Plotly Chart Studio 上绘制的图形

二。方法比较

就分类器的编程而言,使用 word2vec 来训练可能在预测时遇到未知词汇的模型稍微复杂一些,而 Keras 本质上处理词汇外的问题。

此外,使用 word2vec 嵌入时 分类器训练时间 更长(也是历元数),此外不要忘记 word2vec 模型训练时间。

三。Keras 嵌入中的相似商

在本案例研究的最后步骤,我还使用 Gensim 将模型 1 和 2 的 Keras 嵌入层权重转换为键控矢量格式。所以,我现在有三种类型的词向量-

1.跳过 gram 嵌入
2。训练分类器
3 时获得的更新的跳格嵌入。在没有任何初始嵌入权重的情况下训练分类器时获得的嵌入

以下是从跳过语法嵌入和两个嵌入层权重获得的各个词汇向量空间中更接近单词“happy”的十个单词的列表:

分别从 Word2Vec 模型、模型 1 和模型 2 获得相似性。笔记本中的结果不同,但可以用相同的方式解释|作者图片

需要注意的是,虽然 word2vec 被设计为捕获给定单词的上下文,但 Keras 嵌入层只是一个查找层,它的权重根据它正在解决的任务和传播的错误进行更新。由于它们具有根本不同的工作原理,这里的比较不能集中在所获得的相似性的质量上,因此对于没有初始权重的 Keras 嵌入层来说,与‘happy’最相似的前 10 个单词看起来没有太大的相关性。然而,在 skip-gram 模型中学习的嵌入在表示与“happy”一起出现的单词方面更好。

此外,注意,对于没有 word2vec 权重初始化的 Keras 模型,相似性得分在 most_similar 列表中也很低。由此得出结论,这些嵌入,或者更确切地说是单词向量,并没有借鉴单词嵌入的思想,后者采用了一种分布式语义方法来将文本编码为数字。

注意事项和未来工作:

  1. 我已经使用准确性来编译模型和损耗,以便进行监控。由于对于不平衡的数据集来说,准确性是欺骗性的,所以召回率或精确度会更合适。
  2. 阶级不平衡没有得到解决。
  3. 我重新运行了多次,模型在不同的执行中略微优于对方。然而,不管好不好,在大多数运行中,模型 1 的混淆矩阵更加丰富多彩。我认为,这是因为 word2vec 单词嵌入将更接近的向量分配给上下文中一起出现的单词 ,而不是类 ,这使模型变得混乱。

数据集引用:

1萨拉维亚,刘,洪春涛,黄,杨海红,吴,陈永胜(2018)。 Carer:用于情绪识别的情境化情感表征。载于2018 自然语言处理经验方法会议论文集(第 3687-3697 页)**

许可证在拥抱面:未知 | 许可证在 Kaggle: CC BY-SA 4.0

资源和代码:

数据分析笔记本 | 分类器培训笔记本

我最喜欢谈论单词嵌入:

  1. Robert Meyer——用 Doc2Vec 和机器学习分类分析用户评论【这个很搞笑……】
  2. Lev Konstantinovskiy —文本相似性与 Gensim 中的下一代单词嵌入

有兴趣阅读更多关于提高该数据集模型性能的信息吗?

**** ****

感谢光临!

我的链接: |LinkedIn|GitHub

多重共线性:有问题吗?

原文:https://towardsdatascience.com/multicollinearity-problem-or-not-d4bd7a9cfb91

多重共线性及其如何影响多元回归模型的简要指南

凯莉·德·阿桂在 Unsplash 上的照片

您可能在大学统计学课程中模糊地记得多重共线性是多重回归模型的一个问题。但是多重共线性到底有多成问题?嗯,就像统计学中的许多主题一样,答案并不完全简单,这取决于你想用你的模型做什么。

在这篇文章中,我将逐步介绍多重共线性在多重回归模型中面临的一些挑战,并提供一些直觉来解释为什么它会有问题。

共线性和多重共线性

在继续之前,最好先明确什么是共线性和多重共线性。幸运的是,如果你熟悉回归,这些概念非常简单。

如果两个解释变量之间存在线性关联,则存在共线性。这意味着,对于所有观察值 i ,以下情况成立:

说明共线性的方程(图片由作者提供。)

多重共线性是这一思想的延伸,如果两个以上的解释变量之间存在线性关联,则存在多重共线性。例如,在您的设计矩阵中,一个变量可能与另外两个变量线性相关,如下所示:

说明多重共线性的方程(图片由作者提供)。

严格地说,多重共线性不是相关性:相反,它意味着几个解释变量之间存在线性相关性。这是一个微妙的点——但也是重要的一点——两个例子都说明了预测者之间的确定性关联。这意味着可以对一个变量进行简单的转换来计算另一个变量的精确值。如果这是可能的,那么我们说我们有完美共线性完美多重共线性

在拟合多元回归模型时,我们需要避免的正是这一点。具体来说,多元回归的一个关键假设是不存在完美的多重共线性。所以,我们现在知道什么是完美多重共线性,但是为什么是个问题?

打破行列

简而言之,问题在于冗余,因为完美的多重共线变量提供了相同的信息。

例如,考虑虚拟变量陷阱。假设我们有一个设计矩阵,包括两个虚拟编码的{0,1}列。在第一列中,对应于“是”回答的行被赋予值 1。然而,在第二列中,具有“否”响应的行被赋予值 1。在这个例子中,“否”列是完全多余的,因为它可以从“是”列计算出来(“否”= 1 —“是”)。因此,我们不需要单独的列来编码“否”响应,因为“是”列中的零提供了相同的信息。

让我们通过考虑下面的矩阵来进一步扩展冗余的概念:

矩阵示例(图片由作者提供)。

这里,前两列是线性独立的;然而,第三个实际上是第一列和第二列的线性组合(第三列是前两列的总和)。这意味着,如果第一列和第三列,或者第二列和第三列是已知的,那么剩下的一列总是可以计算出来的。这里的结论是,最多只能有两个栏目做出真正独立的贡献——第三个栏目总是多余的。

冗余的下游效应是它使得模型参数的估计不可能。在解释为什么快速定义矩矩阵会有用之前:

矩矩阵 (图片由作者提供)。

关键的是,要导出普通最小二乘(OLS)估计器 r 必须是可逆的,这意味着矩阵必须具有满秩。如果矩阵是秩亏的,那么 OLS 估计器不存在,因为矩阵不能求逆。

好吧,这意味着什么?

不涉及太多细节,矩阵的是其列所跨越的向量空间的维数。解析这个定义最简单的方法如下:如果你的设计矩阵包含 k 个独立解释变量,那么rank(r)应该等于 k 。完美多重共线性的效果是将 r 的秩降低到小于设计矩阵中最大列数的某个值。那么,上面的矩阵 A 只有秩 2,并且是秩亏的。

直觉上,如果你将 k 条信息放入模型,你会希望每条信息都做出有用的贡献。每个变量都应该有自己的权重。相反,如果你有一个完全依赖于另一个变量的变量,那么它不会贡献任何有用的东西,这将导致退化。

不完美的联想

到目前为止,我们一直在讨论变量完全多重共线的最坏情况。然而,在现实世界中,你很少会遇到这个问题。更现实的情况是,你会遇到两个或更多变量之间存在近似线性关联的情况。在这种情况下,线性关联不是确定性的,而是随机的。这可以通过重写上面的等式并引入误差项来描述:

随机多重共线性(图片由作者提供)。

这种情况更易于管理,并且不一定导致 r 的退化。但是,如果误差项很小,变量之间的关联可能几乎完全多重共线。在这种情况下,尽管rank(r)= k,模型拟合可能会变得计算不稳定,并产生表现相当不稳定的系数。例如,数据中相当小的变化都会对系数估计值产生巨大影响,从而降低其统计可靠性。

充气模型

好的,如果你想对你的解释变量进行统计推断,那么随机多重共线性会带来一些障碍。具体来说,它的作用是增大估计系数周围的标准误差,这可能导致在存在真实效应时无法拒绝零假设(第二类误差)。幸运的是,统计学家已经来帮忙了,并且创造了一些非常有用的工具来帮助诊断多重共线性。

其中一个工具是方差膨胀因子* (VIF),它量化了多重回归模型中多重共线性的严重程度。从功能上来说,当存在共线性时,它测量估计系数方差的增加。所以,对于协变量 j,VIF 定义如下:*

方差膨胀因子(图片由作者提供)。

这里,决定系数是从协变量 j 对剩余 k — 1 变量的回归中得出的。

对 VIF 的解释也相当简单。如果 VIF 接近 1,这意味着协变量 j 与所有其他变量线性无关。大于 1 的值表示某种程度的多重共线性,而介于 1 和 5 之间的值被认为表现出轻度到中度的多重共线性。这些变量可以留在模型中,尽管在解释它们的系数时需要小心。我很快就会谈到这一点。如果 VIF 大于 5,这表明存在中度到高度的多重共线性,您可能需要考虑不考虑这些变量。然而,如果 VIF 是 10,你可能有合理的理由抛弃这个变量。

VIF 的一个非常好的属性是它的平方根指示了系数标准误差的增加。例如,如果一个变量的 VIF 为 4,那么它的标准误差将是与所有其他预测值零相关时的 2 倍。

好了,现在提醒一下:如果你决定保留 VIF 为 1 的变量,那么估计系数的大小将不再提供完全精确的影响度量。回想一下,变量 j 的回归系数反映了在保持所有其他变量不变的情况下,仅 j的每单位增加量的预期响应变化。如果解释变量 j 和其他几个变量之间存在关联,这种解释就不成立,因为 j 的任何变化都不会独立影响响应变量。当然,这种情况的程度取决于 VIF 有多高,但这肯定是需要注意的。**

设计问题

到目前为止,我们已经探讨了多重共线性的两个颇成问题的方面:

1) 完美多重共线性:定义协变量之间的确定性关系,产生退化矩矩阵,从而无法通过 OLS 进行估计;和

2) 随机多重共线性:协变量近似线性相关,但矩矩阵的秩不受影响。在解释模型系数和关于它们的统计推断时,这确实会产生一些令人头痛的问题,尤其是当误差项很小时。

不过,你会注意到,这些问题只涉及到对预测者的计算——没有提到模型本身。这是因为多重共线性只影响设计矩阵而不影响模型的整体拟合。

所以…..问题,还是没有?嗯,就像我开头说的,看情况。

如果您感兴趣的只是解释变量的集合对响应变量的预测程度,那么即使存在多重共线性,模型仍会产生有效的结果。然而,如果你想对个体预测因子* s 做出具体声明,那么多重共线性就更是个问题;但是,如果你意识到了这一点,你可以采取行动来减轻它的影响,在以后的文章中,我将概述这些行动是什么。*

同时,我希望你在这篇文章中找到一些有用的东西。如果您有任何问题、想法或反馈,请随时给我留言。

如果你喜欢这篇文章,并且想保持更新,那么请考虑在 Medium 上关注我。这将确保你不会错过新的内容。如果你更喜欢的话,你也可以在 LinkedIn 和 Twitter 上关注我😉

用 PyTorch 在 5 分钟内进行多标记分类

原文:https://towardsdatascience.com/multilabel-classification-with-pytorch-in-5-minutes-a4fa8993cbc7

您自己的分类任务蓝图

亚历克斯·苏普伦在 Unsplash 上的照片

当处理图像分类时,人们通常从对一个类别中的一个或多个类别进行分类开始。例如,如果您想对汽车进行分类,您可以区分它是否是敞篷车。这是二元分类的一个例子。更复杂的任务可能是区分几个类别。它是奥迪、宝马、奔驰还是福特?汽车品牌中有不止一个类别。如果我们想把两个例子结合起来呢?我们可以一次对显示车辆的每张图像的多个特征进行分类,例如品牌、车型和制造年份。一种方法是训练三个独立的分类器,但是也有可能将所有东西集成到一个模型

分类任务(由作者完成)

我们将与出于教育目的免费使用的斯坦福汽车数据集一起完成这项工作。我们开始吧:

首先,我们创建了两个函数来 a)下载和提取图像本身,b)存储相应的元数据(包含关于品牌和型号的信息)。在下一步中,我们创建一个类,该类合并两种信息并提取总共三个相关特征:

数据集中所有超过 1000 张图片的品牌。我们将所有其他品牌归入“其他”类别。

我们区分不同类型的车辆:敞篷车、轿跑、SUV、面包车。所有不涉及车型的车型,我们都归纳为“其他”类别。

我们将拼车分成两个时间相关的队列:2009 年及更早发布的所有汽车和 2010 年及更晚发布的所有汽车。

所以我们有三个不同类别的目标,我们希望同时预测其中的每一个。我们可以从元数据中提取所有需要的信息。

如类的文档字符串中所述,我们可以传递包含类的类别的字典:

正如所料,我们得到了一个列表,其中包含三个类别(品牌、类型、年份)的三个数字特性列表。这些是我们的培训标签。我们可以稍后使用字典重新分配它们:

乍一看,每个类都有足够的案例。我们确实有偏斜的分布,但是我们可以通过加权来减轻。我们让这些类保持原样,并为我们的自定义数据集创建一个字典。我们为每个文件名分配相应的训练标签:

接下来,我们将创建自定义数据集。更深入的介绍,你可以看看我的这篇文章。基本上还没什么特别的。唯一的不同是,我们为每个样本加载三个训练标签,而不是一个,并将所有三个标签传递到我们的训练循环中:

我们可以用数据加载器加载一个样本并查看它:

我们的定制数据集和数据加载器按预期工作。我们每批得到一个字典,包含图像和 3 个目标标签。这样我们就有了多标记分类器的先决条件。

自定义多标签分类器(由作者提供)

首先,我们加载一个预训练的 ResNet34 并显示最后 3 个子元素。首先是顺序块,然后是池操作,最后是线性层。这会得到 512 个特征作为输入,并给出 1000 个特征作为输出。我们想要删除这最后一层,并用新的层替换它。我们已经知道,我们各有 512 个内特征,需要 a) 6 个品牌外特征,b) 5 个车型外特征,c) 2 个时代外特征。我们可以通过将所有子元素放入一个列表并删除最后一个元素来删除最后一层:

我们可以在没有分类器头的情况下使用 ResNet 处理输出,并查看各自的张量形状:

结果我们得到一个格式为[16,512,1,1]的张量。我们的批次中有 16 个样本,每个图像有 512 个特征。第三和第四个维度的大小为 1,可以通过 torch.flatten() 进行平滑。现在,我们可以将此输出传递给新的分类器层:

这正是我们想要的。在我们的批次中,每个样品有 6 个对数。我们现在可以像往常一样使用训练循环中的损失函数来处理这些。现在,我们添加另外两个分类器层,并将所有内容放在一个自定义模型中:

我们创建了一个灵活的训练程序,考虑了我们模型的所有输出。因此,我们是否有 2 个、3 个或例如 5 个分级机头并不重要。对于多分类任务,我们简单地使用传统的损失函数。我们计算每个头部的 交叉弯曲 并合计损失。这样,我们可以通过一个优化步骤来优化所有三个头的权重:

我们还编写了验证例程,以便我们可以传递灵活数量的类别进行分类。我们计算每个类别的总绩效和每个类别的绩效:

结论

每一类大约 90%的准确率,我们能够做出很好的预测。我们看到,我们可以用一个模型对多个类进行分类,而不需要多个模型或运行。在我们的例子中,我们使用 PyTorch,并且看到我们可以使用定制数据集和定制模型快速创建定制训练例程。此外,尽管任务很复杂,我们还是利用了迁移学习来快速获得好的结果。在现实世界中,有很多这样的应用领域。想象一下,你运行一个二手车平台,想要直接从图像中提取关于单个车辆特征的建议。在我们的例子中,我们离这并不远。还有另一种形式的多标签分类。例如,想想社交网络中的图像标签。这里,我们也给出了某些类,但并不是每个图像都被强制分配给每个类。我们将在下一章讨论这个问题。感谢阅读!

进一步阅读

笔记本:https://jovian . ai/droste-benedikt/02-article-py torch-multi label-classification
关于多类:https://scikit-learn.org/stable/modules/multiclass.html

数据集信用

用于细粒度分类的 3D 对象表示
Jonathan Krause,Michael Stark,Jia Deng,李菲菲
第四届 IEEE 表示和识别研讨会,ICCV 2013(3 drr-13)。澳大利亚悉尼。2013 年 12 月 8 日。
【pdf】【BibTex】【幻灯片】

https://ai.stanford.edu/~jkrause/cars/car_dataset.html

如果您喜欢中级数据科学,并且还没有注册,请随时使用我的推荐链接加入社区。

使用 Scikit-learn 和堆叠概括正确完成多标签文本分类

原文:https://towardsdatascience.com/multilabel-text-classification-done-right-using-scikit-learn-and-stacked-generalization-f5df2defc3b5

实践教程

以及如何使用递归正则表达式匹配 LaTeX 语法

模型堆叠本身就是一门艺术|照片由肖恩·斯特拉顿Unsplash 上拍摄

**Table of Contents**· [Preprocessing](#dd97)
· [Split Dataset](#7cd9)
· [Modeling](#ad85)
  ∘ [Random Prediction](#2de0)
  ∘ [Rule-based Prediction](#6de1)
  ∘ [Machine Learning](#0ccf)
  ∘ [Stacking Models](#1c60)
· [Wrapping Up](#0515)

在这个故事中,您将作为多标签分类任务为数据集进行基线建模。在整个过程中,您将学习如何:

  1. 使用递归正则表达式预处理包含 LaTeX 的文本。
  2. 适当分割多标记观测值,保持关于第级标记组合的平衡表示。
  3. 使用随机预测、基于规则的预测、机器学习预测和模型堆叠从头开始构建基线模型。

如果你不知道什么是多标签分类任务,让我们倒回去一点。基于目标/标签/类别,机器学习中有几种类型的分类问题:

  1. 二进制:由两个类组成的分类问题,表示为一维数组,其中正标签用 1 表示,负标签通常为 0。
  2. 多类:包含两个以上类别的分类问题。多类目标可以表示为从 0 开始的一维整数数组。
  3. Multiclass-multioutput:一个分类问题,其中每个样本的目标由固定整数n1 的 n 个输出组成,每个输出是一个类/标签。每个输出都有一组固定的可用类,每个样本都标有一个对应于每个输出的类。
  4. 多标签:多类多输出分类问题,其中每个输出都是二进制的。

多类-多输出和多标签分类可以表示为整数的二维数组。具体来说,对于多标签,数组的每一列都是一个单独的二进制目标。

如果你仍然迷茫,这张对比汇总表很好地总结了这一点。

机器学习中的分类问题类型比较|图片作者作者

你在这个故事中使用的数据集名为math_problems.json,由我和我来自奥林匹克社区的朋友创建。我们来读一下。

它有两个特性:problem包含 LaTeX 格式的数学问题,而tags包含代数、组合学、几何或数论中的一两个类。在建立模型时,problem和/或其提取的特征将是你的预测器,而tags将是你的目标。

您将逐步构建您的模型,从最简单的开始,一步步增加复杂性:

  1. 随机预测:不利用problem中的任何信息进行随机预测
  2. 基于规则的预测:基于problem中存在的关键字
  3. 机器学习:从problem中提取特征,然后输入经典模型

我知道许多术语和术语有点令人困惑,尤其是当你在处理多标签分类任务时。所以从现在开始,我们要就如何称呼事物达成一致:

  1. problem中的每个观察被称为问题
  2. tags中的每个观察值或其来自模型的预测被称为一个标签。换句话说,['代数','几何']是一个标签,它的二值化[1,0,1,0]也是。
  3. 标签中的每个元素称为一个标记。使用与前面相同的示例,“代数”和“几何”是标签,1、0、1 和 0 也是标签。

预处理

让我们对一些问题进行取样,以了解数据集。

Let $k\ge 2$ be an integer and $a_1,a_2,\cdots,a_k$ be $k$ non-zero reals. Prove that there are finitely many pairs of pairwise distinct positive integers $(n_1,n_2,\cdots,n_k)$ such that
$$a_1\cdot n_1!+a_2\cdot n_2!+\cdots+a_k\cdot n_k!=0.$$

In $\triangle ABC$, point $M$ is the middle point of $AC$. $MD//AB$ and meet the tangent of $A$ to $\odot(ABC)$ at point $D$. Point $E$ is in $AD$ and point $A$ is the middle point of $DE$. $\{P\}=\odot(ABE)\cap AC,\{Q\}=\odot(ADP)\cap DM$. Prove that $\angle QCB=\angle BAC$.
https://z3.ax1x.com/2021/09/12/4pZ7Zj.jpg

If $n\ge 4,\ n\in\mathbb{N^*},\ n\mid (2^n-2)$. Prove that $\frac{2^n-2}{n}$ is not a prime number.

Find the minimum value of $c$ such that for any positive integer $n\ge 4$ and any set $A\subseteq \{1,2,\cdots,n\}$, if $|A| >cn$, there exists a function $f:A\to\{1,-1\}$ satisfying
$$\left| \sum_{a\in A}a\cdot f(a)\right| \le 1.$$

In triangle $ABC,$ $AB=BC,$ and let $I$ be the incentre of $\triangle ABC.$ $M$ is the midpoint of segment $BI.$ $P$ lies on segment $AC,$ such that $AP=3PC.$ $H$ lies on line $PI,$ such that $MH\perp PH.$ $Q$ is the midpoint of the arc $AB$ of the circumcircle of $\triangle ABC$. Prove that $BH\perp QH.$

探索性数据分析提出了一些清理策略:

  1. 小写所有文本
  2. 删除 URL
  3. 删除所有 LaTeX 数学表达式和渐近线代码块。它们可能传达有用的信息,所以稍后我们也会提取它们。
  4. 删除错误的 LaTeX 语法和停用词
  5. 处理过滤器和非字母数字字符

上述清洁步骤中的第 3 步是最具挑战性的一步。为此,我们形式化了 4 种类型的数学表达式:

  1. 单或双美元符号,\$...\$\$\$...\$\$
  2. 一对括号(...)
  3. 一对支架[...]
  4. \begin...\end命令

为了使用正则表达式匹配 LaTeX 语法,我们使用递归,因为四种类型中的每一种都可以包含在其他类型中。模式相当复杂,如下所示。

这同样适用于渐近线模式。

现在,该打扫卫生了!

如果仔细观察,preprocess_problem函数返回 3 列:

  1. token,遵循上述清洁策略。
  2. latex,包含所有存在于problem中的 LaTeX 命令。
  3. token_latex,与token相同,但带有 LaTeX 命令。

在处理多标签分类任务时,将tags转换为 4 个二进制列,依次表示代数、组合学、几何和数论,然后将结果连接回原始数据集。

array(['algebra', 'combinatorics', 'geometry', 'number theory'],
      dtype=object)

最后,将预处理后的数据集保存到一个名为math_problems_preprocessed.json的新 JSON 文件中。

分割数据集

在分割数据集之前,最好了解标签分布,分别计算单标签和双标签。单标签有 4 种,双标签有 6 种组合,共 10 类。根据下面的图,有一个阶级不平衡的迹象,特别是几何。

图片作者作者

您将数据集分为 3 部分:训练(70%用于训练模型)、验证(15%用于超参数调整)和测试(15%用于评估模型性能)。目前,我们不会进行超参数调优,但我们将坚持 3-split 场景。Scikit-learn 提供了一种使用train_test_split将数据集一分为二的快速简单的方法。所以,要把它分成 3,用两次train_test_split,如下。为了确保分割中相似的标签分布,不要忘记使用stratify参数。

train: 15952 (70%)
val: 3419 (15%)
test: 3419 (15%)

让我们看看分割后的标签分布。我们希望在分割中看到类似的分布,因为我们希望在尽可能类似于未知数据(验证数据和测试数据)的训练数据上训练我们的模型,以获得良好的泛化能力。

图片作者作者

我们看到标签分布在分割中几乎是相同的,这正是我们想要的。如果不是这种情况,对于其他多标签分类任务,特别是对于不平衡的高阶标签组合,我们需要另一种分裂方法。其中一个叫做迭代分层,由于 scikit-multilearn 中的迭代分层模块,可以很容易地使用。你应该自己试试。

建模

在实际应用中,选择模型时需要考虑许多因素。除了性能,我们还应该考虑模型对推理的响应速度、重新训练的时间、计算成本、内存大小、可解释性等。

在性能方面,我们要做的第一件事是构建一个简单的基线来比较模型。由于这是一个分类问题,我们将考虑性能的简单指标,如精确度、召回率和 f1 分数。

因此,构建一个score()函数,它接受真正的标签和预测,并以熊猫数据帧的形式返回精度、召回率和 f1 值。我们使用来自scikit-learnprecision_recall_fscore_support并设置参数average='weighted'来解决等级不平衡的问题。

随机预测

这是最简单的基线。不应该有比随机预测性能更低的模型。如果您找到了一个,很可能是您在建模步骤中走错了方向,或者您的标签没有正确编码。

scores初始化为一个空的数据帧,稍后通过比较验证分割的真实标签和预测标签,该数据帧将用于列出所有模型的分数。

为了维护验证分割和预测的标签分布,将根据您之前建立的标签分布随机选择每个标签。为此,使用参数p等于标签分布的np.random.choice。不要忘记使用仅来自训练数据的标签分布,否则将会有数据泄漏。

您可以使用与前面相同的方法来确认验证分割和预测之间的标记分布的相似性。

图片由作者

我们看到分布是相似的。接下来,计算分数并制表到scores。正如预期的那样,分数非常低(大约 0.28),因为没有使用来自预测者的信息。

基于规则的预测

现在,如果我们使用来自token的一些关键字来确定标签会怎么样?我所说的关键字是指针对特定标签的问题经常出现的常用词。当然,这需要数学领域的专业知识。首先,我们将为每个标签使用三个关键字,如下所示。比如一道题包含“角”、“圆”、“四边形”这些词,那么就把它归类为几何。

这些启发式预测的一个问题是,它们不能涵盖所有问题,因为可能会有不包含这 12 个词的问题。这些问题的治疗方法是什么?只需随机选择一个标签。

计算分数并将其列表到scores。基于规则的预测比随机预测好很多,但仍然不够好。我们迫切需要机器学习。

较低的召回率是由于大量的假阴性,因为许多观察结果没有被我们选择的关键字标记(默认为 0)。

机器学习

这就是乐趣的开始!

我们将尝试选择最好的模型,因此为了使改变许多模型更容易,将它们包装在一个类中(在我们的例子中称为ClfSwitcher),该类继承了 scikit-learn BaseEstimator中的主要方法,如fit()predict()predict_proba()score()

接下来,构建一个管道,从预测器中提取特征,并使用ClfSwitcher从提取的特征中预测标签。我们选择的特征提取器是TfidfVectorizer。Tf 表示术语频率,而 TF–IDF 表示术语频率乘以逆文档频率。它将计数特征重新加权为适合分类器使用的浮点值,以惩罚非常频繁的术语(例如,英语中的“the”、“a”、“is”)。

我们将分别使用 7 种型号:

  1. 逻辑回归
  2. 随机梯度下降
  3. 线性支持向量机(SVM)
  4. k-最近邻(KNN)
  5. 随机森林
  6. 极端梯度增强(XGBoost)
  7. 光梯度推进机(LightGBM)

其中一些模型支持 scikit-learn 实现中的多标签分类,如 k-最近邻、随机森林和 XGBoost。其他的只支持单输出,我们就传给MultiOutputClassifier。请注意,所有模型都尽可能使用不一致的类别权重来解决类别不平衡问题。

至于TfidfVectorizer,我们试图改变 n 元模型,只使用单元模型(1,1),或者同时使用单元模型和二元模型(1,2)。更多细节参见文档

将参数网格中的每组参数分配给管道,在训练分割上训练管道,在验证分割上进行预测,计算分数,并将分数列表到scores。由于有 7 个模型和 2 种 n-gram,参数组的总数是 14。

100%|███████████████████████████████| 14/14 [01:39<00:00,  7.11s/it]

定义接受scoresplot()函数,并根据 f1 分数将几个最高分绘制成柱状图。具体来说,它将按降序绘制 f1 值超过 0.80 的分数。

图片作者作者

可以看出,几乎所有的模型都是低精度高召回的。这意味着这些模型预测的假阳性比假阴性多。这是有意义的,因为每个标签的最大标签数量是两个,所以当一个模型预测一个问题有两个以上的标签时,它肯定会有一个假阳性。

你会问,如何平衡精确度和召回率?一种方法是使用自定义阈值进行预测(不是默认的 0.5),另一种方法是堆叠一些模型。

堆叠模型

从上面的图中可以明显看出,对于几乎所有的模型,n-gram (1,2)都比(1,1)好。这是因为当我们同时使用单词和双词时,比只使用单词时提取了更多的特征。

基于 f1 分数的最好的三个模型是 SVM、SGD 和逻辑回归。让我们用StackingClassifier把这 3 个排名前 3 的模型堆起来。不要忘记将结果传递给MultiOutputClassifier并设置参数class_weight='balanced'

像以前一样,使用TfidfVectorizer和堆叠模型构建一个管道。对训练分割进行管道训练,对验证分割进行预测,计算分数,并将分数列表给scores

图片由作者

CPU times: total: 1.7 s
Wall time: 17.3 s

尽管svm2提供了最佳性能,我们还是会选择stack_model,因为stack_modelsvm2不同,它具有大致相同的精度、召回率和 f1 分数。将此模型保存为 pickle 对象。

您可以再次加载该模型,并对测试分割进行预测,以获得最终性能。我们看到测试分割的分数与验证分割的分数相似(大约 0.85),这是意料之中的。

包扎

在本故事中,您将了解如何用 python 解决多标签分类任务。您已经学会了如何:

  1. 使用递归正则表达式预处理包含 LaTeX 的文本。
  2. 适当分割多标记观测值,保持关于第级标记组合的平衡表示。您还会注意到类的不平衡,并知道如何使用 scikit-learn 中的class_weight参数来处理它。
  3. 使用随机预测、基于规则的预测、机器学习预测和模型堆叠从头开始构建基线模型。您可以看到,堆叠模型平衡了单个模型的精确度和召回率。

🔥你好!如果你喜欢这个故事,想支持我这个作家,可以考虑 成为会员 。每月只需 5 美元,你就可以无限制地阅读媒体上的所有报道。如果你注册使用我的链接,我会赚一小笔佣金。

🔖想了解更多关于经典机器学习模型如何工作以及如何优化其参数的信息?或者 MLOps 大型项目的例子?有史以来最优秀的文章呢?继续阅读:

Albers Uzila

艾伯斯·乌兹拉

从零开始的机器学习

View list8 storiesAlbers Uzila

艾伯斯·乌兹拉

高级优化方法

View list7 storiesAlbers Uzila

艾伯斯·乌兹拉

MLOps 大型项目

View list6 storiesAlbers Uzila

艾伯斯·乌兹拉

我最好的故事

View list24 storiesAlbers Uzila

艾伯斯·乌兹拉

R 中的数据科学

View list7 stories

多语言 NLP:在 5 分钟或更短时间内开始使用 PAWS-X 数据集

原文:https://towardsdatascience.com/multilingual-nlp-get-started-with-the-paws-x-dataset-in-5-minutes-or-less-45a70921d709

检索、处理和使用数据集的实践教程

照片感谢来自 Unsplash汉娜·莱特

介绍

PAWS-X [ 1 ]是一个多语言序列分类数据集,使用单词加扰(PAWS)数据集【2】从原始英语释义对手创建。该数据集由 49401 个句子对组成,每个句子对都有一个相关联的标签,该标签指示该句子对是意译(y=1)还是非意译(y=0)。每个句子对由机器从原始英语数据集翻译成以下语言:德语(de)、西班牙语(es)、法语(fr)、日语(ja)、韩语(ko)和中文(zh)。因此,数据集有 7 种语言,涵盖 4 种文字(拉丁文、表意文字、韩文和中文表意文字)和 4 种语系(印欧语(日耳曼语和罗曼语)、日语、韩语和汉藏语)。下面的表 1 描述了一些数据集统计数据。表 2 显示了来自英语数据集子集的示例实例。

表 1: 不同数据分割可用的数据集统计。请注意,括号中的数字表示“已清理”实例的数量。

表 2:PAWS-x 英文子集的两个例子,这些例子直接取自 HuggingFace 上的数据集。

PAWS-X 通常被用作多语言 NLP 的基准,它是 DeepMind 的 XTREME [ 3 ]数据集的一部分。总的来说,这是一个相对容易的数据集,模型达到了 89%的准确率。然而,与人的表现(97.5%)相比,在表现上有相当大的差距。这使得数据集适合于 a)评估新的 NLP 模型的性能(特别是测量跨语言性能),以及 b)在剽窃检测管道中使用它们来微调 NLP 模型。

本文提供了检索、处理和使用数据集的简短指南。如果你不能在 5 分钟内使用数据集(即因为代码中的错误或因为我的文章写得不好),那么我让你失望了。请标记任何问题,以便我可以让其他人的过程更顺利。

检索数据集

数据集可以很容易地从其 HuggingFace 存储库中检索出来。下面的函数可用于检索数据。

务必先安装好数据集库。

pip install datasets

处理数据集

检索数据集后,我运行了一些简单的验证测试。这些是:

  • 确保每种语言的数据集大小相同
  • 确保没有一个句子对是空的

令我失望的是,后一项测试失败了。翻译中的系统性错误导致 272 个实例为空字符串。这个错误被记录在谷歌的 PAWS-X Github repo 的下一期,然而不管什么原因,它还没有被修复。如果你想重现最先进的(SOTA)结果,那么最好而不是在数据集中过滤这些不完整的实例,因为其他研究人员不太可能这么做。

但是,如果您对保持数据集的并行性感兴趣,那么您可以使用下面的函数删除这些损坏的实例(跨所有语言):

注意,我们是基于德国数据集生成 PAWSX_FILTER_IDS 的。事实上,我们可以使用任何翻译的语言(不是英语),因为这个错误是系统性的(我检查过,这个公式为所有翻译的语言提供了相同的 id!)

如果您不想在每次运行代码时定位被排除的 id(取决于您的设置),那么您可能希望将它们硬编码到一个配置文件中。这些 id 以 Python 可复制的格式提供如下:

['306', '473', '624', '1209', '1698', '1858', '1975', '2325', '2530', '2739', '2912', '2991', '3046', '3135', '3394', '3437', '3664', '3726', '3846', '4135', '4518', '4721', '4826', '5107', '5457', '5857', '5934', '6048', '6147', '6506', '6650', '7200', '7350', '7374', '7508', '7666', '7808', '8656', '8789', '8905', '9114', '9259', '9368', '9471', '9854', '10115', '10285', '10386', '10666', '10757', '10992', '11252', '11305', '11385', '11732', '11772', '11783', '11784', '11804', '11843', '11870', '11944', '12484', '12642', '12679', '12754', '12794', '12830', '13136', '14108', '14442', '14525', '14693', '14812', '14820', '14889', '15170', '15395', '15397', '15594', '15647', '16131', '16346', '16359', '16441', '16478', '16777', '17067', '17123', '17563', '17607', '17615', '17863', '17995', '18213', '18443', '18549', '18606', '19075', '19181', '19289', '19311', '19329', '19476', '19597', '19672', '19762', '19882', '19888', '19988', '20028', '20126', '20219', '20752', '20818', '20902', '20903', '21162', '21248', '21520', '21556', '22294', '22585', '22621', '22733', '22785', '22822', '23414', '23588', '23752', '23907', '24964', '25002', '25075', '25088', '25092', '25369', '25587', '25889', '26172', '26787', '26881', '27137', '27223', '27446', '27829', '27925', '28192', '28242', '28517', '28654', '28836', '28846', '29020', '29060', '29066', '29465', '29632', '30314', '30568', '30649', '30882', '31284', '31458', '31712', '31715', '31963', '32035', '32043', '32067', '32334', '32489', '32534', '32976', '33502', '33538', '33974', '34119', '34619', '34634', '34706', '34793', '34820', '34976', '35221', '35251', '35334', '35406', '35439', '35568', '36246', '36406', '36524', '36589', '36651', '36685', '36719', '36816', '36947', '37331', '37397', '37672', '38068', '38093', '38198', '38378', '39005', '39020', '39195', '39633', '39674', '39683', '39744', '40325', '40337', '40397', '40406', '40457', '40509', '40574', '40750', '40799', '40814', '40870', '40913', '41342', '41498', '41579', '41595', '41782', '42177', '42253', '42490', '42568', '42757', '42862', '43161', '43417', '44037', '44467', '44488', '44861', '45243', '45365', '45498', '45594', '45750', '45975', '45982', '46143', '46593', '46672', '46691', '46743', '46751', '47436', '47632', '47657', '47667', '47677', '48090', '48217', '48243', '48307', '48678', 
'48687', '48973', '48994', '49183', '49219', '49312', '49358']

使用数据集

从这一点开始,如何使用数据集主要取决于您。在这里,我提供了关于标记数据集的重要说明,以及一个 Python 类,它允许您在 PyTorch 训练循环中直接加载和使用数据集。如果你对使用 TensorFlow 或 HuggingFace 的训练器感兴趣,可以跳过后半部分。我还提供了 PAWS-X 数据集的两个常见用例。

将数据集符号化

由于我们的问题是对一对句子进行序列分类,我们需要将这两个句子作为单独的参数提供给记号赋予器函数。此外,我们指定 max_length 为 128。这是直接从 XTREME GitHub 中获取的。它相对较小,这意味着我们可以在 Colab 上运行大多数模型,而没有任何内存限制。我们使用“longest_first”进行截断(即,如果句子对超过最大长度,则我们截断较长的句子),并将短句对填充到最大长度。以下代码可用于标记数据集:

PyTorch 训练循环的数据集类

以下代码准备 PAWS-X 数据集(来自 HuggingFace 源代码),以便在 PyTorch 样式的循环中进行训练。

学术用例:基准 NLP 模型

PAWS-X 对于 NLP 模型的基准测试非常有用。尽管它是一个简单的数据集,但与人类的表现相比,它的表现仍有相当大的差距(88.9%比 97.5% [ 3 ])。这主要是因为在非拉丁脚本语言[ 3 ]上性能较弱。这使得数据集成为检查您的 NLP 模型是否具有拉丁文字中心偏向的良好选择。

此外,数据集的大小使其成为开发合成数据集的良好起点。例如,可以创建对立的例子来评估模型的健壮性。因为 PAWS-X 是一个简单的数据集,所以在模型不再有用之前,可以用对立的例子来推动它们。最后,PAWS-X 结构不会对给定的一对句子混合使用不同的语言(也就是说,我们永远不会有一对由一个英语句子和一个西班牙语句子组成的句子)。然而,因为数据集的大小很大,所以可以使用它的很大一部分来用跨句子对混合语言的例子补充数据集,使得该模型在多语言释义检测方面更好,而不损害其在单语释义检测方面的性能。

实际使用案例:PAWS-X 微调抄袭检测

PAWS-X 数据集用于释义检测,这意味着它可以用于微调简单的抄袭检测模型。但是,由于句子长度相对较小,该模型不太可能适用于非常大的文档。因此,任何使用这种模型的管道都必须先将文本分割成句子,然后再输入模型。

使用 PAWS-X 微调抄袭检测模型背后的核心逻辑是“释义”是句子相似性的一种度量。有了这个逻辑,PAWS-X 微调模型测量句子相似度也是可能的(支持传统的度量,比如计算嵌入的余弦相似度)。PAWS-X 的多语言特性意味着也可以跨语言找到相似性(例如,给定一个英语句子“I love you”和一个法语句子“Je t'adore”(简单地说,法语是 I love you),确定它们的相似性)*。

*虽然这是事实,但为了在多语言句子相似性方面获得更好的性能,在具有多语言翻译对的增强 PAWS-X 数据集上进行微调将是一个好主意,如章节“学术用例:NLP 模型基准测试”中所述

结束语

  • PAWS-X 是以释义检测形式的多语言序列分类数据集
  • 它有超过 49k 个实例,以及额外的 2k 个验证和测试实例
  • 它涵盖 7 种语言,跨越 4 种文字和 4 个语系
  • 这是一个相对简单的任务和数据集
  • 对于对多语言 NLP 感兴趣的人来说,这是一个很好的介绍

作者说明

我写这篇文章的动机是因为我最近在做一个处理多语言数据集的项目。在这个过程中,我发现虽然网上有许多丰富的数据集,但处理它们是一项非常困难的任务(许多数据集以不同的版本出现在不同的位置,可用数据集的大小与论文中报告的大小之间存在差异,存在翻译错误等等)。我心想,其他人可能也经历过同样的问题,将来还会有更多的人遇到,所以为什么不写一些简明的教程来让人们的生活更轻松呢?

这是我打算写的关于多语言 NLP 的系列文章的一部分。前几个将专注于数据集(接下来将是 TyDiQA [ 4 ]和 xf quad[5])。稍后我将把重点转向任务本身(表示数据、有趣的实验、培训和评估教程)。

如果您对我的工作感兴趣,请考虑通过我的推荐链接获得中等会员资格来支持我:

https://namiyousef96.medium.com/membership

这对我很有帮助,因为你的一部分会员费会归我(别担心,这对你没有额外的费用!)同时让您完全访问 Medium 上的所有文章!

参考

相关资源

数据集位置

https://huggingface.co/datasets/paws-x

数据集论文

https://arxiv.org/abs/1908.11828

引用表

1https://arxiv.org/abs/1908.11828

[2]https://aclanthology.org/N19-1131.pdf

[3]https://arxiv.org/abs/2003.11080

https://aclanthology.org/2020.tacl-1.30/

https://arxiv.org/abs/1910.11856

声明

根据许可,PAWS-X 数据集可用于任何用途(参见hugging face上的许可信息)。它是由谷歌公司提供的。

所有图片、表格和代码均由作者提供,除非另有说明

多语言 NLP:在 10 分钟或更短时间内开始使用 TyDiQA-GoldP 数据集

原文:https://towardsdatascience.com/multilingual-nlp-get-started-with-the-tydiqa-goldp-dataset-in-10-minutes-or-less-c56c01ad47d2

检索、处理和使用数据集的实践教程

照片感谢来自 Unsplash汉娜·莱特

介绍

TyDiQA-GoldP [ 1 ]是一个困难的抽取式问答数据集,通常用于测试问答模型。数据集的价值在于数据的创建方式。注释者被给予随机维基百科文章的前 100 个字符,并被要求提出他们有兴趣找到其答案的问题。引用这篇论文中的一个例子,给定提示“苹果是一种水果”,人类注释者可能会问“史蒂夫·乔布斯死于什么疾病?”。这种生成数据集的策略模拟了人类的好奇心,这可能是 TyDiQA-GoldP 比其他多语言提取 QA 数据集更难的原因之一,例如 XQuAD [ 2 ]和 MLQA [ 3 ]。一旦创建了问题,通过选择问题提示的 Google 搜索结果中出现的第一篇文章,就可以找到匹配的维基百科文章。然后要求注释者在文章中找到与问题匹配的最佳答案,如果存在这样的答案的话。那些没有答案的问答配对被丢弃,而对于那些有答案的配对,只有包含答案的段落被保留。

每个实例由以下内容组成:一个问题、一个答案(文本)、答案的起始范围和实例 ID。该数据集涵盖以下语言:英语(en)、孟加拉语(bn)、韩语(ko)、泰卢固语(te)、斯瓦希里语(sw)、俄语(ru)、芬兰语(fi)、印度尼西亚语(id)、阿拉伯语(ar)。因此,它涵盖了 5 种文字(拉丁文、梵文、西里尔文、韩文、阿拉伯文)和 7 个语系(印欧语系(印度雅利安语、日耳曼语、斯拉夫语)、亚非语系、乌拉尔语系、南岛语系、朝鲜语系、尼日尔-刚果语系、德拉威语系)。与许多多语言 NLP 数据集不同,原始的 TyDiQA-GoldP 是而不是并行的。这意味着实例不能匹配,因为它们不是通过翻译创建的。然而,DeepMind [ 4 ]通过获取英语子集并将其翻译成其他语言,创建了一个平行版本的 TyDiQA-GoldP。表 1 显示了原始 TyDiQA-GoldP 数据集中每种语言的实例数量,而表 2 显示了 DeepMind 生成的数据集的统计数据。表 3 显示了数据集英文子集的一个实例。

表 1: 原始 TyDiQA-GoldP 数据集中每种语言的示例数量

表 2: 并行数据集中每种语言的示例数量。注意,数字 3969 对应于数据集的英语子集,该子集被用作翻译成其他语言的基础。括号中的数字显示了所有语言的公共数据点的数量(15%由于翻译错误而丢失)

表 3:ty diqa-GoldP 英文子集的一个示例

TyDiQA-GoldP 通常用作多语言 NLP 的基准,并行数据集作为 DeepMind 的 XTREME [ 4 ]数据集的一部分出现。总的来说,这是一个非常困难的数据集,模型在 F1 得分上达到 77.6,在精确匹配上达到 68[4]。作为比较,人类的表现是 90.1。最初的 TyDiQA-GoldP 相对较大,适合进行微调,特别是提高非拉丁语系语言的性能。并行 TyDiQA-GoldP 数据集相对较小,适合在公开可用的 GPU(如 Colab)上进行训练。

在这篇文章中,我提供了一个实践教程,用于从多个来源(从平面文件和通过datasets API 从 HuggingFace)检索数据集,处理它(检查数据有效性,找到匹配的实例)并将其用于原始设置和来自 DeepMind 的并行设置(将其标记用于训练)。为了确保流畅的用户体验,我写这篇文章时考虑了以下几点:

  • 不到 10 分钟
  • 用于快速检索数据集的可用脚本
  • 数据差异的解释(如有)

检索数据集

非平行设置

在非并行设置中,开发集和训练集都可以从 TyDiQA 下载。json 文件。开发集可以在这里找到,而训练集可以在这里找到。下载后,文件可以读入datasets.Dataset类,如下所示:

值得注意的是,非平行的 TyDiQA-GoldP 数据集也存在于 HuggingFace 上,并且在两个不同的位置复制!它可以从 TyDiQA HuggingFace 数据集和 XTREME HuggingFace 数据集中下载。将两者都作为datasets.Dataset类加载的代码如下所示(我个人更喜欢 XTREME 类,因为它更快……):

值得注意的是,虽然。json 文件与 HuggingFace 数据中的不匹配,数据集是相同的。原始格式的两个数据集混合了所有语言。我们将在“处理数据集”一节中看到如何为每种语言创建单独的数据集。

平行设置

数据集只能从 XTREME 存储库中下载,具体来说就是这里的不要使用 HuggingFace XTREME 存储库中的版本,因为那只适用于非平行设置(我是通过艰苦的努力才知道的……)。

验证数据:注意,虽然与训练数据存在差异,但验证数据并非如此。首先,验证数据没有“平行”设置。来自 TyDiQA 文件的验证子集(称为“dev”)和 XTREME/TyDiQA HuggingFace 存储库都是相同的。因此,最简单的方法是使用非平行设置的函数,并为分割指定“验证”。值得注意的是,来自 XTREME GitHub repo 的 translate-test 不要与验证数据混淆。

处理数据集

检索数据集后,我运行了一些简单的验证检查。这些是:

  • 确保没有空洞的问题、上下文或答案
  • 确保训练子集只有一个答案
  • 检查每个数据集的 id 是否唯一

幸运的是,这些测试通过了非并行设置和并行设置。

非平行设置

此部分是可选的。只有当您希望按语言分割数据集时,它才是有用的,请记住数据集不是并行的

平行设置

对于这个设置,我运行了两个额外的测试来确保数据确实是并行的:

  • 对照文献中报告的数据集大小检查数据集大小(在平行设置的情况下)
  • 确保每种语言的数据集大小相同(在并行设置的情况下)

不幸的是,这两项测试都失败了。对于后者,我得到了每种语言的以下数据集大小:

bn: 3585/3585 ids unique
fi: 3670/3670 ids unique
ru: 3394/3394 ids unique
ko: 3607/3607 ids unique
te: 3658/3658 ids unique
sw: 3622/3622 ids unique
id: 3667/3667 ids unique
ar: 3661/3661 ids unique

对于为什么会有缺失的数据点,我的最佳猜测是翻译过程本身会导致错误。这是因为问答任务并不简单,直接翻译可能提供不再匹配的问答对,因此这些例子被丢弃。在匹配 id 以找到真正并行的示例总数之后,我剩下 3150 个数据点,这意味着丢失了 15%的数据集(从并行数据的角度来看)。

我发现的问题是,TyDiQA-GoldP 验证集的大小似乎与 XTREME 论文中报告的任何数字都不匹配。首先,据称数据集有一个“开发”集和一个“测试”集,但是,在 XTREME GitHub repo 上没有找到。其次,“验证”数据集的大小与“开发”和“测试”数据集的大小不匹配。这是我在他们的 GitHub 页面上提出的一个公开的问题。

也就是说,下面给出了查找公共实例和检查是否有空实例的函数:

(可选—仅当您希望将数据集用作本文提供的 PyTorch 类的一部分时)

我们可以保存处理过的数据集,供 PyTorch 数据类以后使用。

使用数据集

在这一节中,我提供了 TyDiQA 的标记化参数(和代码),以及允许在训练循环中直接使用的 PyTorch 数据集类(仅用于并行情况)。我还为 TyDiQA-GoldP 数据集提供了一个学术和实践用例。

将数据集符号化

因为我们的问题是抽取式问答,所以我们需要在标记之前对每个例子做一些处理。主要是,我们必须注意不要从上下文中截断答案。因此,当提供一个最大长度的时,我们还需要提供一个步幅。这样,我们可以确保很长的上下文被拆分成多个实例,确保至少在其中一个实例中,我们会有完整的答案。我们还将标记器参数截断设置为“second_only ”,以确保只有上下文被截断。我们指定 max_length384stride128 ,直接取自 XTREME GitHub 。我们还需要确保训练示例的处理方式不同于验证示例。下面提供了执行此操作的函数:

PyTorch 训练循环的数据集类

以下代码准备了 TyDiQA-GoldP 数据集(来自预处理的源),以便在 PyTorch 样式的循环中进行训练。

学术用例:将你的 QA 模型推向极限

TyDiQA-GoldP 很难,因为它的创建方式,也因为语言的选择(例如,它有低资源语言,如斯瓦希里语和泰卢固语)。这使得它成为评估 QA 模型跨语言性能的绝佳选择。

然而,值得注意的是,由于上面提出的开放问题,复制您在文献中看到的结果可能需要一点反复试验的过程,因为不清楚在得出该结果时使用了哪种状态的数据。

实际用例:TydiQA-GoldP 微调问答

原始的 TyDiQA-GoldP 数据集有利于微调,原因有二:a)数据集相当大,b)很难。更重要的是,它包含了一套非常多样化的语言。除了介绍中提到的 7 个语系和 4 种文字外,该数据集中的语言还涵盖了一系列有趣的语言现象,例如[ 4 ]:

  • 音调符号:字母上决定发音的符号。TyDiQA-GoldP 示例:阿拉伯语
  • 广泛复合:多个词的组合,如:Note+book=Notebook。TyDiQA-GoldP 的例子:泰卢固语
  • 绑定词:句法上独立,但音韵上依赖的词,例如 it's = it is。TyDiQA-GoldP 的例子:孟加拉语
  • 词形变化:修饰一个词来表达语法信息,如:桑,唱,唱。TyDiQA-GoldP 示例:俄语
  • 派生:由动词创造名词,例如 slow→slow。TyDiQA-GoldP 示例:韩语

结束语

  • TyDiQA-GoldP 是一个多语言的抽取式问答数据集
  • 本质上它是不平行的,但是存在一个基于原始英语子集的小型平行版本
  • 非平行数据集有 1636–14805 个数据点,而平行数据集有 3150 个数据点
  • 它涵盖 9 种语言,跨越 4 种文字和 7 个语系
  • 这是一项艰巨的任务和数据集
  • 对于对多语言问答感兴趣的人来说,这是一个很好的介绍,因为它的大小,但不要期望很高的分数!

作者说明

我个人花了很长时间来确定使用哪些 TyDiQA 数据集进行并行训练和评估。由于在网上没有找到类似的文章,我决定写这篇文章,这样至少可以在网上找到一些参考资料来总结 TyDiQA 数据集的不同来源。我真的希望保持更新,好像我找到了我提出的公开问题的答案。

如果您对这一行感兴趣,请考虑通过我的推荐链接获得中级会员资格来支持我:

https://namiyousef96.medium.com/membership

这对我很有帮助,因为你的一部分会员费会归我(别担心,这对你没有额外的费用!)同时让您完全访问 Medium 上的所有文章!

参考

GitHub 仓库

TyDiQA

https://github.com/google-research-datasets/tydiqa

极限

https://github.com/google-research/xtreme

拥抱面孔储存库

泰迪卡

https://huggingface.co/datasets/tydiqa

Xtreme

https://huggingface.co/datasets/xtreme#licensing-information

引用表

1 Clark J et al. TYDI QA:用类型多样的语言回答信息搜索问题的基准。供货地:【https://aclanthology.org/2020.tacl-1.30.pdf

[2] Artetxe 等论单语表征的跨语言迁移性。来自:https://arxiv.org/pdf/1910.11856.pdf

[3] Lewis 等人 MLQA:评估跨语言抽取问题回答。可从:https://arxiv.org/abs/1910.07475

[4]胡等 XTREME:一个评估跨语言泛化的大规模多语言多任务基准。来自:https://arxiv.org/abs/2003.11080

声明

  • ty diqa-GoldP 可根据 Apache 2.0 许可使用(参见GitHub上的许可信息)
  • 所有图片、表格和代码均由作者提供,除非另有说明

具有多阶段预训练的多语言语音翻译

原文:https://towardsdatascience.com/multilingual-speech-translation-with-multi-phase-pretraining-305d642b8a66

如何将不同模态的预训练模型与单语数据混合成强大的多语言翻译模型

Unsplash 上由 Austin Distel 拍摄的照片

注意:这是本系列的第三篇文章。之前的文章解释了如何在双语和多语言环境下将海量预训练用于机器翻译:
第 1 部分:用于双语机器翻译的海量预训练
第 2 部分: mBART50:可扩展的多语言预训练的多语言微调
第 3 部分:具有多阶段预训练的多语言语音翻译

IWSLT

自动语音翻译是从音频形式的语音输入开始,研究从一种人类语言到另一种语言的翻译的领域。它的研究跨越了几十年,多年来提出了许多不同的方法。

国际口语翻译会议(IWSLT)已经到了第 19 届,其目标是跟踪这一领域的进展。他们从两方面着手,一方面接受科学贡献,另一方面组织共同的任务,在不同的场景中,根据共同的基准比较真实的系统。

我在以前的一篇文章中谈到过 IWSLT:

https://medium.com/machine-translation-fbk/machines-can-learn-to-translate-directly-your-voice-fbk-iwslt18-bb284ccae8bc

多语言语音翻译

去年的一个场景是多语言语音翻译,包括从西班牙语(es)、法语(fr)、葡萄牙语(pt)和意大利语(it)到英语(en)和西班牙语(es)的翻译。首先要注意的是,所有涉及到的语言都有些相似,英语是这个群体中唯一的非浪漫语言。这使得多语言翻译更加有效,因为对于模型来说,使用许多类似语言的数据进行改进相对容易。
其次,英语只出现在目标端,而很多研究和很多数据集都集中在源端的英语上。

https://medium.com/machine-translation-fbk/must-c-a-large-corpus-for-speech-translation-8e2350d01ea3

该任务的三个平移方向(It-En、It-Es 和 Pt-Es)被认为是相对于受约束轨迹的零炮,这意味着没有专门针对这些方向的训练数据。在不受约束的赛道上,参与者可以使用他们想要的所有额外的训练数据,因此没有任何控制。

这里描述的论文是去年(2021 年)IWSLT 多语言赛道的获奖作品,由脸书 AI Research (FAIR)开发。他们提交的目标是探索用大量并行和未标记的数据预训练多模态翻译模型的解决方案。通过训练语音翻译、文本机器翻译和语音识别的模型,利用来自不同任务的数据。

数据

共享任务提供的训练集是 TEDx、CoVoST 和 EuroParlST。

TEDx 是一个在 TEDx 上发表的演讲的集合,共有 13 个语言方向的翻译。这里,只考虑了任务的 7 个方向。 CoVoST 是 Mozilla Common Voice 的扩展,提供大型数据集的翻译,包括 11 个翻译成英语的方向。europarlist是一个相对较小的多语言数据集,包含欧洲议会 6 种语言的翻译演讲,共 11 个翻译方向。

此外,作者使用两个已与单语文本对齐的多语转录音频数据集挖掘了并行数据。

转录音频的两个多语种语料库分别是 CommonVoice (29 种语言)和Multilingual LibriSpeech(8 种语言的有声读物),而 CCNet 则作为多种语言的高质量单语文本大集合。

给定这些数据,通过使用激光器从 CCNet 中的源音频文本的抄本中提取句子嵌入以对齐具有相似语义的句子,如嵌入相似度所给出的,来获得另外的语音翻译数据。因为在源语言中音频和文本是对齐的,所以这个过程导致源音频与目标语言中的文本对齐。对于零炮点方向,得到的对准数据相当于几十个对准音频。

文本数据

5 种语言的单语数据用于训练 mBART 。单语数据来自 CC100。然后,mBART 在从 OPUS 下载的 7 种语言的并行数据上进行微调。所得的微调模型将在以后用于初始化语音翻译模型。

方法

模型培训遵循基于 3 个连续步骤的迁移学习方法:

  1. 经过自我监督学习预处理的单模态模块
  2. 多任务联合训练
  3. 特定于任务的微调

他们的目标分别是:

  1. 从大量未标记数据中训练
  2. 从文本到文本再到语音到文本
  3. 对最终任务进行微调以获得更好的结果

单一模态预训练

Wav2vec 2.0 用大量未标记音频数据训练,mBART 用不同语言的大量单语文本训练。 Wav2vec 2.0 然后用于初始化第二训练阶段的语音编码器。mBART 的编码器和解码器用于初始化下一阶段模型的编码器和解码器。

多任务联合训练

在第二阶段,语音到文本模型与文本到文本模型被联合学习。因此,整个模型由两个编码器和一个解码器组成。这两个编码器共享文本编码器的权重,但在处理音频输入时会使用附加层。适配器层用于促进在两个编码器之间共享的纯语音编码器和文本编码器权重之间的连接。一些培训技巧像交叉注意规则(CAR)和在线知识提炼(online KD)已经被用来促进任务间的知识转移。

特定于任务的微调

在最后阶段,文本编码器被丢弃,并且剩余的语音到文本翻译模型使用简单的交叉熵在并行音频翻译上被微调。

结果

本文的主要结果是使用所描述的过程训练的三个系统的集合,但是具有稍微不同的编码器,比强语音翻译基线高出 8.6 个 BLEU 点。基线是使用上述所有数据建立的,但没有第二阶段的联合训练。

此外,该集合仅比翻译正确音频转录本的强 MT 模型弱 3 个 BLEU 点(在语言方向上平均),这代表了语音翻译性能的巨大进步。

结论和意见

这篇论文展示的结果无疑是惊人的,但不幸的是,并不是所有的小组都能够训练这种类型的模型。的确,虽然第 2 期和第 3 期相对便宜(5 + 2 天 8 个 NVidia V100 GPUs),但像 wav2vec 和 mBART 这样从零开始的训练模型确实很昂贵,所需资源甚至没有在论文中提及。尽管如此,尽管共享任务的范围有限,但结果是显著的,并且清楚地表明大型预训练模型在跨模态设置中也是使能器。

这种强大的模型在该领域开辟了新的可能性,现在下一个前沿是在实时设置中也获得良好的结果,而该系统仅在批处理模式下工作。对于进一步的发展,我们只需要等待第 19 版 IWS lt T1 的结果,其中包括 8 个评估语音翻译不同方面的共享任务。

中等会员

你喜欢我的文章吗?你是否正在考虑申请一个中级会员来无限制地阅读我的文章?

如果您通过此链接订阅,您将通过您的订阅支持我,无需为您支付额外费用【https://medium.com/@mattiadigangi/membership

使用嵌入的多语言文本相似性匹配

原文:https://towardsdatascience.com/multilingual-text-similarity-matching-using-embedding-f79037459bf2

使用句子转换器进行对称的语义搜索

拉奎尔·马丁内斯在 Unsplash 上拍摄的照片

今天的主题是计算相同或不同语言的两个句子之间的相似度。我们将利用sentence-transformer框架,它自带预先训练好的多语言 transformer 模型。

我们可以利用这些模型来计算 50 多种语言的文本嵌入。然后,输出嵌入可以用于对称语义搜索。

对称和非对称语义搜索的区别

对称语义搜索专注于基于输入查询从语料库中寻找相似问题。例如,鉴于“如何在线学习人工智能?”作为输入查询,预期的输出应该类似于“如何在 web 上学习人工智能?”大多数时候,您可能会翻转查询和语料库中的数据,最终仍然得到与输出相同的配对。对称语义搜索主要用于文本挖掘或意图分类任务。

另一方面,不对称语义搜索围绕基于输入查询从语料库中寻找答案。例如,鉴于“什么是 AI?”作为输入查询,您可能会期望输出类似“AI 是一种模仿人类智能来执行任务的技术。他们可以根据获得的信息学习和提高自己的知识。”输入查询不仅限于问题。可以是关键词,也可以是短语。非对称语义搜索适用于搜索引擎相关的任务。

在撰写本文时,sentence-transformer框架提供了以下用于多语言对称语义搜索的预训练模型:

  • distiluse-base-multilingual-cased-v1—15 种语言通用语句编码器的多语言模型。
  • distiluse-base-multilingual-cased-v2—50 种语言通用语句编码器的多语言模型。
  • paraphrase-multilingual-MiniLM-L12-v2 —paraphrase-multilingual-MiniLM-L12-v2 的多语言模型,扩展到 50+种语言。
  • paraphrase-multilingual-mpnet-base-v2 —paraphrase-mpnet-base-v2 的多语言模型,扩展到 50+种语言。

实际上,我们可以利用这些模型来计算英语句子和西班牙语句子之间的相似度。例如,给定我们语料库中的以下句子:

What are you doing?
I am a boy
Can you help me?
A woman is playing violin.
The quick brown fox jumps over the lazy dog

并输入如下查询:

Qué estás haciendo

相似度最高的句子应该是:

What are you doing?

为简单起见,我们的对称语义搜索的工作流程如下:

  1. 计算查询和语料库文本的嵌入
  2. 计算两个嵌入之间的余弦相似度
  3. 查找具有最高相似性得分的前 5 个索引

设置

在此之前,让我们创建一个新的虚拟环境,并安装所有必需的包。

用 pip 安装

您可以轻松安装sentence-transformer包:

pip install -U sentence-transformers

用康达安装

对于 Anaconda 用户,您可以直接安装该软件包,如下所示:

conda install -c conda-forge sentence-transformers

继续下一节的实施。

履行

在您的工作目录中,创建一个名为main.py的新 Python 文件。

导入

在文件顶部添加以下导入语句:

from sentence_transformers import SentenceTransformer, util
import torch

模型初始化

然后,通过调用SentenceTransformer类初始化模型,并传入所需模型的名称:

model = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2')

在初始运行期间,模块将下载预训练的模型文件,作为缓存在以下目录中:

# linux
~/.cache/huggingface/transformers# windows (replace username with your username)
C:\Users\<username>\.cache\huggingface\transformers

您可以将缓存文件夹修改为当前工作目录,如下所示:

# save model in current directory
model = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2', cache_folder='./')# save model in models folder (you need to first create the folder)
model = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2', cache_folder='./models/')

对于生产,您应该将模型移动到工作目录并在本地加载。例如,假设模型文件位于models文件夹,您可以如下初始化您的模型:

model = SentenceTransformer('models/sentence-transformers_paraphrase-multilingual-MiniLM-L12-v2')

如果您在一台只有 CPU 的机器上进行测试,只需将device参数设置为cpu:

model = SentenceTransformer('models/sentence-transformers_paraphrase-multilingual-MiniLM-L12-v2', device='cpu')

语料库和查询

接下来,初始化语料库和查询的数据。在这种情况下,我有一个 7 个字符串的列表作为corpus数据,而queries包含一个不同语言的 3 个字符串的列表。

corpus = [
'I am a boy',
'What are you doing?',
'Can you help me?',
'A man is riding a horse.',
'A woman is playing violin.',
'A monkey is chasing after a goat',
'The quick brown fox jumps over the lazy dog'
]queries = ['I am in need of assistance', '我是男孩子', 'Qué estás haciendo']

将数据编码到嵌入中

调用encode函数将语料库转换为嵌入。将convert_to_tensor参数设置为True以获取 Python 张量作为输出。同样,初始化一个名为top_k的新变量,并将其赋值为最小值 5 和语料库的总长度。我们稍后将使用这个变量来获得具有最高相似性得分的索引。

corpus_embedding = model.encode(corpus, convert_to_tensor=True)top_k = min(5, len(corpus))

encode函数接受字符串列表或单个字符串作为输入。

计算余弦相似度

最后一步是遍历查询中的所有项目,并执行以下操作:

  • 计算单个查询的嵌入。每个嵌入具有以下形状:torch.Size([384])
  • 调用 util.cos_sim函数来获得输入查询和语料库之间的相似性得分
  • 调用torch.topk函数获得 topk 结果
  • 打印输出作为参考
for query in queries:
    query_embedding = model.encode(query, convert_to_tensor=True)

    cos_scores = util.cos_sim(query_embedding, corpus_embedding)[0]
    top_results = torch.topk(cos_scores, k=top_k) print("Query:", query)
    print("---------------------------")
    for score, idx in zip(top_results[0], top_results[1]):
        print(f'{round(score.item(), 3)} | {corpus[idx]}')

top_results变量是一个元组,包含:

  • 表示输入查询和语料库之间的相似性得分的张量阵列
tensor([ 0.3326,  0.2809,  0.2258, -0.0133, -0.0333])
  • 表示输入查询索引的张量数组
tensor([2, 0, 1, 4, 3])

您可以在以下要点的中找到完整的代码:

输出

运行该脚本时,您应该在终端上得到以下输出:

Query: I am in need of assistance
---------------------------
0.333 | Can you help me?
0.281 | I am a boy
0.226 | What are you doing?
-0.013 | A woman is playing violin.
-0.033 | A man is riding a horse.Query: 我是男孩子
---------------------------
0.919 | I am a boy
0.343 | What are you doing?
0.192 | Can you help me?
0.058 | A monkey is chasing after a goat
-0.001 | The quick brown fox jumps over the lazy dogQuery: Qué estás haciendo
---------------------------
0.952 | What are you doing?
0.396 | I am a boy
0.209 | Can you help me?
0.037 | A woman is playing violin.
0.032 | The quick brown fox jumps over the lazy dog

最佳化

上面的实现对于小型语料库(低于 100 万个条目)非常有用。对于大型语料库,执行会相对较慢。因此,我们需要优化实现,以便它可以无缝地工作。一些最流行的优化技术包括:

  • 归一化嵌入并使用点积作为得分函数
  • 使用近似最近邻将语料库划分成相似嵌入的较小部分

为了保持简单和简短,本教程将只涵盖第一种技术。当您规范化嵌入时,输出向量的长度将为 1。因此,我们可以使用点积而不是余弦相似度来计算相似度得分。点积是一个更快的损失,你会得到相同的相似性分数。

标准化嵌入

有两种方法可以标准化嵌入。第一种方法是在调用encode函数时将normalize_embeddings参数设置为True

corpus_embedding = model.encode(corpus, convert_to_tensor=True, normalize_embeddings=True)

或者,您可以利用util.normalize_embeddings函数来规范化一个现有的嵌入:

corpus_embedding = model.encode(corpus, convert_to_tensor=True)
corpus_embedding = util.normalize_embeddings(corpus_embedding)

计算点积

调用util.semantic_search函数并传入util.dot_score作为score_function的输入参数。它将返回带有关键字corpus_idscore的字典列表。此外,该列表按余弦相似性得分递减排序。

hits = util.semantic_search(query_embedding, corpus_embedding, score_function=util.dot_score)

修改后,新的执行代码应该如下:

当您运行脚本时,您应该得到与第一个实现相同的输出:

Query: I am in need of assistance
---------------------------
0.333 | Can you help me?
0.281 | I am a boy
0.226 | What are you doing?
-0.013 | A woman is playing violin.
-0.033 | A man is riding a horse.Query: 我是男孩子
---------------------------
0.919 | I am a boy
0.343 | What are you doing?
0.192 | Can you help me?
0.058 | A monkey is chasing after a goat
-0.001 | The quick brown fox jumps over the lazy dogQuery: Qué estás haciendo
---------------------------
0.952 | What are you doing?
0.396 | I am a boy
0.209 | Can you help me?
0.037 | A woman is playing violin.
0.032 | The quick brown fox jumps over the lazy dog

结论

让我们回顾一下你今天所学的内容。

本文首先简要介绍了sentence-transformer 模块。然后,比较了对称和非对称语义搜索的区别。

随后,它介绍了设置和安装。sentence-transformer可以安装pipconda

在实现部分,本文重点介绍了将语料库编码到嵌入中的步骤,以及使用余弦相似度计算相似度得分的步骤。

最后一节讨论优化技术。一种优化技术是将嵌入归一化为长度 1,然后使用点积计算相似性得分。

感谢你阅读这篇文章。祝你有美好的一天!

参考

  1. 句子变压器—安装
  2. 句子变压器—预训练模型
  3. SentenceTransformer —语义搜索

MultiMAE:在无监督预训练中利用标记数据的灵感

原文:https://towardsdatascience.com/multimae-an-inspiration-to-leverage-labeled-data-in-unsupervised-pre-training-9739a2dbf97c

通过多模式屏蔽自动编码器提高模型性能

巴勃罗·阿里纳斯在 Unsplash 上拍摄的照片

自监督预训练是提高传统监督学习性能的一种主要方法,其中大量的标记数据是必要的和昂贵的。在自我监督学习方法中,对比学习因其简单有效而广受欢迎。然而,大多数对比学习方法使用全局向量,其中像素级信息的细节丢失,这在转移到下游密集任务时留下了改进的空间。我向感兴趣的读者推荐我以前的关于对比学习方法的文章。

https://medium.com/geekculture/understanding-contrastive-learning-and-moco-efe491e4eed9

对于密集下游任务的迁移学习,我们需要一些自我监督的预训练方法,可以恢复整个特征图的细节,而不仅仅是合并的全局向量。只有通过这个才能了解到整个特征地图分布的细节,更重要的是,不需要标注。

在对比学习中,提出了一些方法来训练图像块[4],或局部特征[5],甚至像素[6]。然而,这些方法要么使用消耗内存的动量编码器和队列[4,5],要么使用难以训练的带有伪标签的半监督学习方式[6]。

屏蔽自动编码器

没有对比思维的方法是何在 2021 年提出的[7]。这与自然语言处理中的 BERT [8]的方法类似,在该方法中,使用交叉熵损失对句子中的屏蔽记号进行分类和训练。然而,在计算机视觉中,表征不能被分类,因为图像模式几乎是无限的,而在 NLP 中,表征是有限的,并且在语料库中预定义。因此,在计算机视觉中,被屏蔽的标记只能以回归方式预测,而不能以分类方式预测

( MAE 不对称架构)

该架构被称为屏蔽自动编码器(MAE ),其中非屏蔽令牌被编码,屏蔽令牌被重建并以 MSE 损失进行训练。这是一个基于视觉转换器的简单而有效的架构[9]。由于编码器仅编码未屏蔽的标记,因此它可以以较小的计算开销扩展到大的输入图像。此外,由于解码器是浅层的,并且仅针对屏蔽令牌计算损失,所以它也是可扩展的,几乎没有额外的计算开销。作者对标记使用了随机掩蔽,但我认为训练资源可以集中在尚未训练好的标记上,就像焦点丢失[10]中使用的方式。

(MAE 的重建结果

我们可以看到,即使当图像被 95%的遮挡时,也可以重建出真实的图像,显示了模型强大的细节学习能力。

多模式多任务屏蔽自动编码器

(多模态架构)

当一个模型成功时,它通常会被社区中的研究人员扩展到许多其他形式。多式联运就是这些扩展模式之一。由于多模态模型产生更健壮的特征,如果你有数据,训练多模态模型通常是一个好的选择。

在 MultiMAE 中,作者使用了三种模态:RGB、深度和语义。由于很难在这三种模式下收集大量的相应数据,作者建议使用一些现成模型生成的伪标签。然而,作者还表明,用伪标签训练的模型不如用真实标签训练的模型性能好。

该模型如上所示进行了扩展。简单明了的是,每个模态面片都用模态线性投影仪投影到标记向量。所有三种模态的标记向量用相同的编码器编码,但是用模态解码器分别解码。在预训练之后,编码器可以用相应的线性投影仪和特定任务头以单模态和多模态方式进行微调。

我发现关于 MultiMAE 的一件有趣的事情是标记的数据可以在预训练和微调阶段利用。假设您有一些 RGB 语义对用于训练语义分割模型,,而不是以传统的监督学习方式训练,您可以用 MultiMAE 预训练模型,其中 RGB 和语义都被屏蔽和重建。在此之后,由于模型已经学习了 RGB 的分布和语义细节,因此在接下来的单模态微调阶段,标签效率可以提高很多。如果在耗费数据的预训练阶段缺乏地面真实语义标签,也可以使用一些现成模型产生的伪标签

参考

1 理解对比学习和 MoCo,2021

[2] 像素级密集对比学习,2022

[3] 视觉语言模型对比预训练,2022

[4] DetCo:对象检测的无监督对比学习,2021

[5] 用于自监督视觉预训练的密集对比学习,2021

[6] 带区域对比的自举语义分割,2022

[7] 屏蔽自动编码器是可伸缩视觉学习器,2021

[8] BERT:面向语言理解的深度双向变压器预训练,2019

[9] 一幅图像抵得上 16X16 个字:用于大规模图像识别的变形金刚,2021 年

[10] 用于密集物体检测的焦损失,201 8

[11] MultiMAE:多模态多任务屏蔽自动编码器,2022

https://dushuchen.medium.com/membership

多模态:认知人工智能的新前沿

原文:https://towardsdatascience.com/multimodality-a-new-frontier-in-cognitive-ai-8279d00e3baf

通过创新的多模式系统实现更智能、适应性更强的人工智能

图片来源:AgsandrewviaAdobe Stock

与英特尔实验室的Vasudev Lal和认知人工智能团队合作编写。

认知人工智能中一个令人兴奋的前沿领域涉及构建能够集成多种模态并综合语言、图像、视频、音频和关系图等结构化知识源的意义的系统。像对话式人工智能这样的自适应应用;使用语言的视频和图像搜索;自主机器人和无人机;人工智能多模态助手将要求系统能够使用所有可用的模态与世界进行交互,并在特定的上下文中做出适当的响应。在这篇博客中,我们将介绍多模态学习的概念及其一些主要用例,并讨论英特尔实验室在创建强大的多模态推理系统方面取得的进展。

在过去的几年里,深度学习(DL)解决方案在许多自然语言处理(NLP)基准测试(例如, SuperGLUEGLUESQuAD )和计算机视觉基准测试(例如, ImageNet )中的表现都优于人类基准。单个模态的进展证明了神经网络学习的高效统计映射所实现的感知或类似识别的能力。

仅仅在十年前,这些单模态任务被认为是极其难以处理的,但目前却是数据中心、客户端和边缘产品中的主要人工智能工作负载。然而,在多模态环境中,许多可以使用自动化方法收集的见解仍然没有被利用。

以人为中心的认知人工智能的多模态性

人类的认知能力通常与多种形式的成功学习相关联。例如,一个苹果的概念应该包括从视觉获得的信息:它通常在颜色、形状、质地等方面看起来像什么。但是,人类和高级人工智能系统形成的苹果概念还应该受到苹果被咬下时发出的声音、人们谈论苹果派时的意思以及维基百科等文本语料库或维基数据等结构化知识库中关于苹果的全面知识的影响。

图一。与“苹果”概念相关的各种形态。图片来源:英特尔实验室 2022 英特尔公司。

多模态人工智能系统可以从多个来源和模态获取知识,并利用它来解决涉及任何模态的任务。通过图像和知识库学习的信息应该可用于回答自然语言问题;同样,从文本中学习到的信息应该在视觉任务需要时使用。这一切都通过贯穿所有形态的概念联系在一起,或者正如人们所说的:狗就是狗就是狗。

图二。狗是狗是狗。图片来源:英特尔实验室 2022 英特尔公司。

常识知识本来就是多模态的

人类拥有很多关于这个世界的常识,比如意识到鸟儿在天空飞翔,汽车在路上行驶。这种常识性的知识通常是通过视觉、语言和感官线索的结合而不是仅仅通过语言获得的。艾伦人工智能研究所首席执行官柳文欢·埃齐奥尼将常识称为“人工智能的暗物质”。这是因为常识由隐性信息组成——人类自动用来理解世界的一组广泛(且广泛共享)的未写假设和经验法则。

有趣的是,多模态系统可以提供一种途径来解决人工智能系统中常识知识的缺乏。提高基于转换器的语言模型(如伯特 / GPT-3 )的常识知识的一种方法是将跨越其他模态的训练信号合并到模型架构中。实现这一功能的第一步是在不同的模态之间调整内部表示。

当人工智能接收到图像和相关文本并处理这两者时,它需要在模态之间关联相同的对象或概念。例如,考虑这样一个场景,AI 看到一张汽车图片,上面有提到汽车轮子的文字。当人工智能处理涉及车轮的文本部分时,它需要处理图像中车轮的部分。人工智能需要“知道”汽车轮子的图像和提到轮子的文本是指不同形态的同一物体。

当前多模态人工智能任务和架构

截至 2022 年初,多模态人工智能系统正在试验将文本/NLP 和视觉驱动到一个对齐的嵌入空间,以促进多模态决策。存在许多要求模型至少具有一定量的多模式能力的任务。以下是四种常见工作负载和相应 SotA 模型的简要概述

  • 图像描述生成,文本到图像生成

也许,处理图像描述和文本到图像生成任务的最知名的模型是 OpenAI 的剪辑DALL-E ,以及它们的继任者 GLIDE

CLIP 预先训练单独的图像和文本编码器,并学习预测数据集中的哪些图像与各种描述配对。有趣的是,正如人类的“Halle Berry”神经元一样,CLIP 已经被证明具有多模态神经元,当暴露于分类器标签文本以及相应的图像时,这些神经元会激活,这表明了一种融合的多模态表示。DALL-E 是 GPT-3 的 130 亿参数变体,它将文本作为输入,并生成一系列输出图像来匹配文本;然后使用 CLIP 对生成的图像进行排名。GLIDE 是 DALL-E 的发展,它仍然使用 CLIP 对生成的图像进行排序;然而,图像生成是使用扩散模型完成的。

  • 视觉问答

视觉问答,正如在像 VQA 这样的数据集中所呈现的,是一项需要模型根据图像正确回答基于文本的问题的任务。微软研究院的团队已经为这项任务开发了一些领先的方法。 METER 是一个通用框架,用于训练高性能的端到端视觉语言转换器,使用各种可能的子架构用于视觉编码器、文本编码器、多模式融合和解码器模块。统一视觉语言预训练模型( VLMo )使用模块化变压器网络来联合学习双编码器和融合编码器。网络中的每个模块都包含一个特定模式的专家池和一个共享的自我关注层,为微调提供了极大的灵活性。

  • 文本到图像和图像到文本搜索

网络搜索是多模态学习的另一个重要应用。呈现这个任务的数据集的一个例子是 WebQA ,它是一个模拟 web 搜索的多模态和多跳基准测试。WebQA 是由微软和卡内基梅隆大学的团队构建的。

在此任务中,模型需要识别可以帮助回答查询的来源(基于图像或文本)。对于大多数问题,模型需要考虑多个来源才能得到正确答案。然后,系统需要使用这些多个来源进行推理,以自然语言生成查询的答案。

谷歌已经用大规模图像和噪声文本嵌入模型解决了多模态搜索任务。这个模型利用与互联网上的图像相关联的容易获得但有噪声的 alt-text 数据来训练单独的视觉( EfficientNet-L2 )和文本( BERT-Large )编码器,然后使用对比学习来组合它们的输出。生成的模型存储了多模态表示,无需任何进一步的微调即可支持跨模态搜索。

  • 视频语言建模

从历史上看,基于视频的任务对人工智能系统来说一直具有挑战性,因为它们是资源密集型的;但这种情况正在开始改变。视频语言建模和其他视频相关的多模态任务领域的主要工作之一是由微软的佛罗伦萨-VL 项目驱动的。2021 年年中,佛罗伦萨-VL 项目推出了 ClipBERT ,它涉及 CNN 和 transformer 模型的组合,在稀疏采样的帧上运行,并以端到端的方式进行优化,以解决流行的视频语言任务。 VIOLETSwinBERT 是 ClipBERT 的演变,它们引入了掩蔽视觉令牌建模和稀疏注意力,以改善视频问答、视频检索和视频字幕中的 SotA。

区别在于细节,但是上面所有的模型都有一个共同的特点,那就是使用基于 transformer 的架构。这种类型的架构通常与并行学习模块相结合,以从各种模态中提取数据,然后将它们统一到单个多模态表示中。

英特尔实验室和微软创建视觉和语言预培训模型

与上述方法类似,英特尔实验室认知人工智能(CAI)研究团队的工作重点是使用基于 transformer 的模型架构创建多模态表示。然而,与 CLIP(擅长图像和文本的实例级配对)等一些模型不同,认知 AI 团队的方法是实现图像和文本中实体的细粒度对齐。所开发的架构还允许将完整的图像上下文提供给同样处理文本的多模态转换器。

认知人工智能团队与微软研究自然语言计算(NLC) 团队合作,最近推出了 KD-VLP ,这是一个在概念层面视觉语言对齐方面特别有效的模型。架构和预训练任务强调系统中的实体级表示,或对象。KD-VLP 在诸如视觉问题回答( VQA2.0 )、视觉常识推理( VCR )、在 MSCOCOFlickr30K 上的图像和文本检索(IR/TR)、用于视觉推理的自然语言( NLVR2 )和视觉蕴涵( SNLI-VE )等任务上展示了竞争性能。

该模型的自我监督训练导致了也是可解释的紧急注意模式。例如,下面的剪辑显示了当模型思考伴随文本中的每个单词时,其视觉注意力是如何变化的。这些模式为模型的内部工作和推理机制提供了有价值的见解。当探索模型推理能力中需要解决的差距时,这种洞察力是有价值的。

图 3:追踪多模态注意力的热图。图片来源:英特尔实验室 2022 英特尔公司。

与微软研究团队的这一研究合作产生了解决多模态挑战的解决方案,例如多模态数据集上的问题回答。一个基于知识的多模态系统目前在 VisualCOMET 任务上领先公共排行榜,其中 AI 系统需要推理静态图像的动态内容。该模型可以从单个图像中唤起一个动态的故事情节,就像人类如何能够想象出以前发生了什么以及接下来会发生什么。

这种单一模型解决方案在视觉常识推理(VCR)挑战赛的公共排行榜上也颇具竞争力。它目前在单一模型解决方案中排名前五,我们的 WebQA 解决方案在neur IPS 2021 竞赛的获奖名单中名列前茅。WebQA 解决方案涉及一种新颖的方法,将多模态源合并到语言生成模型中。该系统可以通过多模式编码器将图像和文本源与问题联系起来,并有效地聚集多个源的信息。解码器使用跨多个多模态源的这种融合的结果来以自然语言回答查询。

图 4:带有注意力热图的网络问答问题示例。Trogon Surrucura 图片鸣谢:维基媒体Cláudio Dias Timm

结论

现实生活环境本质上是多模态的。这个应用领域允许人工智能研究社区进一步推动人工智能从单一感知模态(如图像或文本)的统计分析过渡到对象及其交互的多面视图,帮助在从“形式”到“意义”的旅程中取得进展。

参考

  1. Wang,a .,Pruksachatkun,y .,Nangia,n .,Singh,a .,Michael,j .,Hill,f .,… & Bowman,S. R. (2019)。Superglue:通用语言理解系统的一个更具粘性的基准。arXiv 预印本 arXiv:1905.00537。
  2. 王,a .,辛格,a .,迈克尔,j .,希尔,f .,利维,o .,&鲍曼,S. R. (2018)。GLUE:自然语言理解的多任务基准和分析平台。arXiv 预印本 arXiv:1804.07461。
  3. Rajpurkar,p .,贾,r .,,梁,P. (2018)。知道你不知道的:无法回答的问题。arXiv 预印本 arXiv:1806.03822。
  4. Rajpurkar,p .,贾,r .,,梁,P. (2021)。斯坦福问答数据集。https://rajpurkar.github.io/SQuAD-explorer/
  5. 何刚,张,徐,任,孙(2015)。深入研究整流器:在 imagenet 分类上超越人类水平的性能。IEEE 计算机视觉国际会议论文集(第 1026–1034 页)。
  6. 维基数据。(2019).检索于 2022 年 1 月 31 日,来自https://www.wikidata.org/wiki/Wikidata:Main_Page
  7. 骑士,W. (2020,4 月 2 日)。美军想教 AI 一些基本常识。麻省理工科技评论。https://www . technology review . com/2018/10/11/103957/the-us-military-wants-to-teach-ai-some-basic-common-sense/
  8. 帕夫卢斯,J. (2020 年 5 月 4 日)。常识到电脑。广达杂志。https://www . quanta magazine . org/common-sense-comes-to-computers-2020 04 30/
  9. Devlin,j .,Chang,M. W .,Lee,k .,& Toutanova,K. (2018 年)。Bert:用于语言理解的深度双向转换器的预训练。arXiv 预印本 arXiv:1810.04805。
  10. 布朗、T. B .、曼恩、b .、赖德、n .、苏比亚、m .、卡普兰、j .、达里瓦尔、p .……和阿莫代伊,D. (2020)。语言模型是一次性学习者。预印本 arXiv:2005.14165。
  11. 拉德福德、a .、金、J. W .、哈拉奇、c .、拉梅什、a .、高、g .、阿加瓦尔、s .……和苏茨基弗(2021)。从自然语言监督中学习可转移的视觉模型。arXiv 预印本 arXiv:2103.00020。
  12. 拉梅什,a .、巴甫洛夫,m .、戈,g .、格雷,s .、沃斯,c .、拉德福德,…和苏茨基弗,I. (2021 年)。零镜头文本到图像生成。arXiv 预印本 arXiv:2102.12092。
  13. Nichol,a .、Dhariwal,p .、Ramesh,a .、Shyam,p .、Mishkin,p .、McGrew,b .、…、陈,M. (2021)。Glide:使用文本引导扩散模型实现照片级真实感图像生成和编辑。arXiv 预印本 arXiv:2112.10741。
  14. Quiroga,R. Q .、Reddy,l .、Kreiman,g .、Koch,c .、和 Fried,I. (2005)。人脑中单个神经元的不变视觉表征。自然,435(7045),1102–1107。
  15. Goh,g .,Cammarata,n .,Voss,c .,Carter,s .,Petrov,m .,Schubert,l .,… & Olah,C. (2021)。人工神经网络中的多模态神经元。蒸馏,6(3),e30。
  16. 使用非平衡热力学的深度无监督学习。arXiv:1503.03585,2015。
  17. 纽约州戈亚尔、茨韦塔纳州 Khot、萨默斯-斯蒂尔、巴特拉和帕里克(2017 年)。让 vqa 中的 v 变得重要:提升视觉问答中图像理解的作用。IEEE 计算机视觉和模式识别会议论文集(第 6904–6913 页)。
  18. 窦志英,徐玉英,甘,张,王,王,王,李,…曾,米(2021)。训练端到端视觉语言转换器的实证研究。arXiv 预印本 arXiv:2111.02387。
  19. 王文伟,鲍海红,董丽兰,魏凤芳(2021)。VLMo:混合模态专家的统一视觉语言预训练。arXiv 预印本 arXiv:2111.02358。
  20. 常,那朗,米,,h,曹,高,j,,y(2021)。网络问答:多跳和多模态问答。arXiv 预印本 arXiv:2109.00590。
  21. 贾,杨,杨,夏,陈,李玉亭,帕瑞克,郑,范,李,…和杜里格(2021)。在嘈杂文本监督下扩大视觉和视觉语言表征学习。arXiv 预印本 arXiv:2102.05918。
  22. 贾,杨,杨(2021 年 5 月 11 日)。ALIGN:在嘈杂文本监督下扩大视觉和视觉语言表征学习。谷歌人工智能博客。https://ai . Google blog . com/2021/05/align-scaling-up-visual-and-vision . html
  23. 谭,男,乐,qv(2019 . 5 . 29)。EfficientNet:通过 AutoML 和模型缩放提高准确性和效率。谷歌人工智能博客。https://ai . Google blog . com/2019/05/efficient net-improving-accuracy-and . html
  24. Devlin 和 m . Chang(2018 年 11 月 2 日)。开放源码 BERT:自然语言处理的最先进的预备培训。谷歌人工智能博客。https://ai . Google blog . com/2018/11/open-sourcing-Bert-state-of-art-pre . html
  25. 微软。(2021 年 12 月 14 日)。佛罗伦萨-VL 项目。微软研究院。https://www . Microsoft . com/en-us/research/project/project-Florence-VL/
  26. 雷,李,李,周,李,甘,钟,彭大林,班萨尔,米,刘(2021)。通过稀疏采样进行视频和语言学习。IEEE/CVF 计算机视觉和模式识别会议论文集(第 7331-7341 页)。
  27. 傅廷杰,李,李,甘,郑,林,王,王维元,王,刘,郑(2021)。VIOLET:端到端视频语言转换器,带屏蔽视觉令牌建模。arXiv 预印本 arXiv:2111.12681。
  28. 林,李,李,林春春,艾哈迈德,甘,刘,…,王,(2021)。SwinBERT:对视频字幕关注较少的端到端变压器。arXiv 预印本 arXiv:2111.13196。
  29. 刘,杨,吴,曾善友,李,何,段,倪(2021)。Kd-vlp:通过对象知识提炼改进端到端的视觉和语言预训练。arXiv 预印本 arXiv:2109.10504。
  30. Antol,s .、Agrawal,a .、Lu,j .、Mitchell,m .、Batra,d .、Zitnick,C. L .、& Parikh,D. (2015)。Vqa:视觉问答。IEEE 计算机视觉国际会议论文集(第 2425-2433 页)。
  31. 泽勒斯,共和党,比斯克,纽约州,法尔哈迪,和崔,纽约州(2019)。从识别到认知:视觉常识推理。IEEE/CVF 计算机视觉和模式识别会议论文集(第 6720-6731 页)。
  32. 林,T. Y .,梅尔,m .,贝隆吉,s .,海斯,j .,,p .,拉马南,d .,… &兹尼克,C. L. (2014 年 9 月)。微软 coco:上下文中的公共对象。在欧洲计算机视觉会议上(第 740–755 页)。斯普林格,查姆。
  33. Young,p .,Lai,a .,Hodosh,m .,& Hockenmaier,J. (2014 年)。从图像描述到视觉指示:事件描述语义推理的新相似性度量。计算语言学协会汇刊,2,67–78。
  34. 苏尔亚,周,s,张,a,张,I,白,h .,&阿奇,Y. (2018)。基于照片的自然语言推理语料库。arXiv 预印本 arXiv:1811.00491。
  35. 谢,倪,赖,多兰,丁,& Kadav,A. (2018)。视觉基础语言学习的视觉蕴涵任务。arXiv 预印本 arXiv:1811.10582。
  36. 微软。(2022 年 1 月 19 日)。自然语言计算。微软研究院。https://www . Microsoft . com/en-us/research/group/natural-language-computing/
  37. Park,J. S .,Bhagavatula,c .,Mottaghi,r .,法尔哈迪,a .,和 Choi,Y. (2020 年 8 月)。VisualCOMET:对静态图像的动态背景进行推理。在欧洲计算机视觉会议上(第 508–524 页)。斯普林格,查姆。

多重比较:A/B 测试的常见陷阱

原文:https://towardsdatascience.com/multiple-comparison-a-common-pitfall-for-a-b-testing-d773f19a4a95

实验和因果推断

多重比较:A/B 测试的常见陷阱

数据科学家很酷!

埃里克·普劳泽特在 Unsplash 上拍摄的照片

简介

科技行业最近的一个趋势是,公司越来越多地诉诸科学方法来指导决策。特别是,他们对产品发布和创新采取严格的测试策略。潜在的想法是:

每当有疑问时,A/B 就测试它。

为了满足大规模测试需求,科技公司建立了需要卓越工程技术的内部实验平台。例如, Google 构建了一个重叠测试基础设施,支持数百甚至数千个并发实验。微软开发了一个自动触发系统,如果有任何事情出错,它会提醒实验所有者。 Airbnb 创建了一个指标存储库,其中包含数以千计的指标,可以轻松提取并进行实时监控。

这些例子表明,我们需要一个强大的工程团队来构建和维护所有这些基础设施,这给人留下了一个错误的印象,即实验可以仅由平台工程师和数据工程师完成,而无需数据科学家的帮助。令我惊讶的是,这是业内相当普遍的误解。

在这篇博文中,我们通过一个典型的实验设置,看看如果团队中没有合格的数据科学家或统计学家,事情会变得多么糟糕。

为什么要进行假设检验?

在我们正式开始之前,让我们试着回答这个问题:

为什么我们需要假设检验?

在线实验的核心思想是从有限的样本中推断总体参数。我们对样本数据进行统计测试,并决定治疗组和对照组之间的差异是实际改善还是仅仅由于随机性。

在任何测试场景下,都不可能完全消除不确定因素。没有 100%的把握,因为随机性带来的意外因素总是存在的。相反,我们可以通过赋值来量化不确定性的水平,这就是为什么统计学家创造了阿尔法水平(又名。假阳性率)。

对于任何实验平台来说,准确评估 FPR 的水平至关重要。太高的 FPR 使平台无用,因为它总是发射错误的信号,我们不知道什么时候是真信号,什么时候是假信号。

穆利亚迪Unsplash 上的照片

为什么多重测试是个问题

这是一个典型的 A/B 测试用例:产品经理希望了解一个新功能将如何影响整体评估标准(例如,保留)和用户参与度的多个方面,包括购物车添加率、平均会话持续时间、每日活跃用户数、回头客数量和客户满意度得分。此外,她关注网站性能,选择跳出率、会话崩溃率、网站加载时间、功耗指标和收入作为护栏指标。实验中共有 10 个指标。

实验小组建立了一个实验。3 周后,她收集了足够的数据,并对新产品充满信心,因为结果返回了几个小 P 值。那么,让我们推出新功能。

有问题吗?

是的,膨胀的 FPR 有一个大问题。具体来说,在多重假设检验中观察到至少一个假阳性的概率,家族误差率(FWER)显著增加。

下面是一个粗略的计算:将 alpha 级别设置为 5%。

  1. 单次检测的假阳性率:0.05
  2. 单次测试第一类错误不落的概率:1 — 0.05
  3. 10 次测试中第一类错误的不落概率:(1 — 0.05) ⁰
  4. 10 次测试至少犯一次 1 型错误的概率:1 — (1 — 0.05) ⁰ =0.40,或 40%。

上述过程表明,我们至少会在 40%的时间里观察到一个假阳性,即使没有实际差异。

作为第一个旁注,对 5%的 alpha 水平的正确解释是,如果我们重复重新运行实验,我们会错误地拒绝 5%的无效假设,即使没有差异。

第二点要注意的是,A/A 测试是一项实验,我们对两组患者进行相同的治疗条件。据估计,任何单个 A/A 测试的 FPR 应接近标称 alpha 水平(如 5%),多个 A/A 测试的 p 值的最终分布应遵循均匀分布。如果你想了解更多关于 A/A 测试和它们潜在的重要性,看看这篇文章。

到目前为止,你可能同意没有适当的统计调整的多重假设检验会增加 FWER,这总是发出错误的信号。

在下一节中,我们将深入研究三种流行的统计调整。

本·哈里特在 Unsplash 上拍摄的照片

解决方案 1: Bonferroni 校正

让我们从最简单的 Bonferroni 校正(BC)开始。BC 方法将α水平除以比较次数,即 α/n. 如果我们将α/n 乘以比较次数 n,BC 方法将 FWER 保持在α。

在我们的示例中,我们对 10 个指标进行了多次比较。默认情况下,我们选择 0.05 作为单独的 alpha 级别。使用 BC 方法,我们将 0.05 除以比较次数:0.05/10 = 0.005。如果我们想要得到任何有统计学意义的结果,相应的 p 值必须小于 0.005。

正如所见,我们已经将拒绝标准从 0.05 降低到 0.005,使得拒绝零假设变得更加困难。这种方法经常被批评为过于保守和缺乏统计能力。BC 方法以假阴性为代价降低了假阳性。

当然,这种方法有几个优点。例如,它是通用的,适用于所使用的任何检验统计量,与 p 值无关,或 H0 的性质( James 等人 2021,统计学习介绍)。

解决方案 2:霍尔姆-邦费罗尼

由于其保守性,BC 方法在实践中很少使用,统计学家已经提出了一种改进的版本,称为 Holm-Bonferroni 方法。它也控制 FWER,但通过对 p 值进行排序并调整每个无效假设的拒绝标准。这两种方法的最大区别在于,与 BC 方法不同,H-B 程序的拒绝阈值取决于所有 p 值。

就能力而言,H-B 比 Bonferroni 方法更加强大:它拒绝的无效假设至少与 BC 一样多。如果你使用 Bonferroni,考虑转换到 Holm 的程序,因为它总是优于。

方案三:本杰明-霍赫伯格

与控制 FWER 的 Bonferroni 家族不同,Yoav Benjamini 和 Yosef Hochberg 提出了一种称为 Benjamini-Hochberg 程序的新方法,该方法控制错误发现率(FDR) ,该错误发现率被定义为所有发现(显著结果)中错误阳性的比例。比如你做了 100 个有意义的结果,其中 5 个是错误发现(或者错误拒绝);罗斯福是 5%。

这里有一个 FDR 和 FWER 的简单比较:

FWER:测试中至少有一个或多个假阳性的概率。

FDR:错误发现占发现总数的百分比。

在生产中,FDR 提供了比 FWER 更直观的解释。错误发现在整个发现中所占的比例,这对业务人员来说有直观的意义。

相比之下,Benjamini-Hochberg 在假阳性率附近提供了较弱的保证,但是比 BC 显著降低了假阴性率。Benjamini-Hochberg 在生产中的 I 型和 II 型误差之间提供了一个更好的折衷。

Medium 最近推出了作家伙伴计划,支持像我这样的普通作家。如果你还不是订户,通过下面的链接注册,我会收到一部分会员费。

https://leihua-ye.medium.com/membership

外卖

  • 由于假设检验的随机性,我们希望考虑抽样方差,并评估出现这种极端数据分布的概率。
  • 一种常见的方法是使用假阳性率。
  • 然而,没有适当调整的多重比较会增加累积的α水平,使 A/B 测试变得无用。
  • 这篇文章认为 Benjamini-Hochberg 方法是平衡假阳性和假阴性的“最佳”方法。
  • 这并不意味着 Benjamini-Hochberg 程序是一个适合所有情况的解决方案。您可能需要选择另一种适合您特定需求的校正方法。
  • 最后,数据科学家超级酷。

喜欢读这本书吗?

请在 LinkedInYouTube 上找到我。

还有,看看我其他关于人工智能和机器学习的帖子。

如何在 Python 中捕捉多个异常

原文:https://towardsdatascience.com/multiple-exceptions-python-545a89c50d46

在 Python 中处理多个异常

照片由丘特尔斯纳普Unsplash 上拍摄

介绍

一个开发良好的应用程序必须总是能够以适当的方式处理意外事件——比如异常。这对于在开发期间调试源代码也很重要,对于在应用程序启动并运行(最终进入生产)时检查应用程序日志也很重要。

在今天的简短教程中,我们将展示如何在 Python 中处理多个异常。我们还将探索 Python 中的一些新特性,这些特性可以帮助您以一种更直观、更简洁的方式完成这项工作。我们开始吧!

对于 Python < 3.11

Now let’s suppose that we have the following (fairly dumb) code snippet, that raises 【 , 【 and 【 .

def my_function(x):
    if x == 1:
        raise AttributeError('Example AttributeError')
    elif x == 2:
        raise ValueError('Example ValueError')
    elif x == 3:
        raise TypeError('Example TypeError')
    else:
        print('Hello World')

And let’s also suppose that we want to to call the function 【 but at the same time, we also need to ensure that we handle any unexpected errors appropriately. To do so, we can use the 【 clauses.

But let’s assume that we want to perform a certain action if an 【 is being thrown and a different action when either of 【 or 【 are raised by 【 .

try:
    my_function(x)
except AttributeError:
    # Do something
    ...
except (ValueError, TypeError):
    # Do something else
    ...

A 【 statement may have more than one except clause, to specify handlers for different exceptions. At most one handler will be executed. Handlers only handle exceptions that occur in the corresponding try clause, not in other handlers of the same 【 statement. An except clause may name multiple exceptions as a parenthesized tuple.

Python 文档

如果您不打算对引发的任何错误做任何特殊处理(例如,您只需pass),您甚至可以使用如下所示的suppress上下文管理器:

from contextlib import suppress

with suppress(AttributeError, ValueError, TypeError):
     my_function(x)

注意[suppress()](https://docs.python.org/3/library/contextlib.html#contextlib.suppress)从 Python 3.4 开始可用。此外,只有当您希望程序的特定部分无声地失败并继续执行时,才必须使用这种方法。但是在大多数情况下,您可能希望对某些异常采取某些措施。

用 Python 3.11 处理多个异常

从 Python 3.11 开始,引入了新的标准异常类型,即ExceptionGroup。这个新异常用于一起传播一组不相关的异常

在下面的创建示例中,我们创建了一个包含四种不同类型错误的ExceptionGroup实例,即TypeErrorValueErrorKeyErrorAttributeError。然后,我们使用多个except*子句来处理ExceptionGroup,无论是针对单个异常类型还是多个异常类型。

try:
    raise ExceptionGroup('Example ExceptionGroup', (
        TypeError('Example TypeError'),
        ValueError('Example ValueError'),
        KeyError('Example KeyError'),
        AttributeError('Example AttributeError')
    ))
except* TypeError:
    ...
except* ValueError as e:
    ...
except* (KeyError, AttributeError) as e:
    ...

但是请注意,一个except*子句中提出的异常不适合匹配同一try语句中的其他子句

关于ExceptionGroupexcept*条款背后的基本原理的更多细节,您可以参考 PEP-654

此外,要更全面地阅读 Python 3.11 中的新增内容和更新,包括我们之前讨论的内容,您可以参考我最近在下面分享的一篇文章。

最后的想法

在今天的简短教程中,我们展示了在 Python 中处理多个异常的各种不同方法。我们已经看到了如何使用传统的except子句来捕获多个异常,但是我们也展示了如何使用 Python 3.11 中将要引入的新的except*子句来这样做。

最后,您应该始终记住,在整个源代码中处理意外事件是一个重要的方面,如果执行得当,可以显著提高代码质量。

成为会员 阅读介质上的每一个故事。你的会员费直接支持我和你看的其他作家。你也可以在媒体上看到所有的故事。

https://gmyrianthous.medium.com/membership

相关文章你可能也喜欢

多元线性回归:理论与应用

原文:https://towardsdatascience.com/multiple-linear-regression-theory-and-applications-677ec2cd04ac

详细解释了线性最小二乘法,并用 Python 从头开始实现

费迪南·斯托尔在 Unsplash 拍摄的照片

多元线性回归由于其简单性和结果的可解释性而成为最基本的统计模型之一。出于预测目的,线性模型有时会优于更好的非线性模型,尤其是在训练案例数量少、信噪比低或数据稀疏的情况下(Hastie 等人,2009 年)。顾名思义,在这些模型中,预测变量(或响应变量)是由预测变量的线性组合来描述的。倍数一词指的是预测变量。

在整篇文章中,将详细描述普通最小二乘(OLS)回归模型的基本原理,并将从头开始用 Python 实现一个回归器。所有用到的代码都在这个 示例笔记本 里。

线性回归已经在许多 Python 框架中可用。因此,在实践中,不需要从零开始实现它来估计回归系数和进行预测。然而,我们在这里的目标是深入了解这些模型是如何工作的,以及它们的假设在处理未来项目时会更加有效。从通常的框架来看,我建议检查来自 statsmodelsOLS 和来自 sklearn线性回归

让我们开始吧。

线性最小二乘法

在进入方程之前,我想定义一些符号指南。

  • 矩阵:大写斜体粗体。
  • 向量:小写斜体粗体。
  • 标量:常规斜体。

多元线性回归模型或 OLS 可以用下面的等式来描述。

线性回归模型元素表示法。(图片由作者提供)。

其中 yᵢ 为观察值 i 的因变量(或响应);β₀为回归截距, βⱼ 为与决策变量 j 相关的系数, xᵢⱼ 为观察值 i 的决策变量 jε 为剩余项。在矩阵符号中,它可以描述为:

线性回归模型矩阵符号。(图片由作者提供)。

其中 β 为参数的列向量。

线性模型对结构进行了大量假设,并产生稳定但可能不准确的预测(Hastie 等人,2009)。当采用线性模型时,应该了解这些假设,以便对结果做出正确的推断,并执行必要的更改。

残差项 ε 假设正态独立分布,均值为零,方差为常数 σ 。一些模型属性,比如参数和预测的置信区间,强烈依赖于这些关于 ε 的假设。因此,核实这些数据对于获得有意义的结果至关重要。

线性最小二乘回归模型的目标是找到使残差平方和(或误差平方和)最小的 β 的值,由下面的等式给出。

误差平方和(残差):最小二乘回归的损失函数。(图片由作者提供)。

这是一个有解析解的优化问题。该公式基于每个预测相对于参数向量 β 的梯度,参数向量对应于自变量向量本身 x 。考虑一个矩阵 C ,由下面的等式给出。

c 矩阵。(图片由作者提供)。

β 的最小二乘估计由下式给出:

参数的最小二乘估计。(图片由作者提供)。

在接下来的步骤中,让我们创建一个 Python 类, LinearRegression,来执行这些估计。但是在此之前,让我们导入一些有用的库和函数,以便在本文中使用。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy.stats import shapiro
from scipy.stats import t as t_fun

如果我们想考虑截距β₀.,第一步是创建一个估计类和一个方法,在估计矩阵中包含一列 1

class LinearRegression:

    def __init__(self, fit_intercept=True):
        self.fit_intercept = fit_intercept

    def _prepare_X(self, X):
        X = np.array(X)
        if len(X.shape) == 1:
            X = np.atleast_2d(X).reshape((-1, 1))
        if self.fit_intercept:
            ones = np.ones((X.shape[0], 1))
            X = np.column_stack((ones, X))
        else:
            pass
        return X

现在,让我们实现一个 fit 方法( sklearn -like)来估计 β。

def fit(self, X, y=None):
    X = self._prepare_X(X)
    n, p = X.shape
    self.n = n
    self.p = p
    C = np.linalg.inv(X.T.dot(X))
    self.C = C
    betas = C.dot(X.T.dot(y))
    self.betas = betas

预测的方法。

def predict(self, X, y=None):
    X = self._prepare_X(X)
    y_pred = X.dot(self.betas)
    return y_pred

以及用于计算 R 度量的方法。

def r2_score(self, X, y):

    y_pred = self.predict(X)
    epsilon = y_pred - y
    sse = np.sum(epsilon * epsilon)

    y_mean = np.mean(y)
    mean_res = y_mean - y
    sst = np.sum(mean_res * mean_res)

    return 1 - sse / sst

参数的统计显著性

测试参数 β 的统计显著性以验证预测值的相关性是有用的。当这样做时,我们能够去除不良预测,避免混淆效应,并提高新预测的模型性能。为此,应该测试与给定预测器相关联的参数 β 为零的零假设。让我们利用矩阵和残差的方差 σ̂ 来计算参数 β 的方差-协方差矩阵 V 及其相应的标准差。

残差方差。(图片由作者提供)。

参数的方差协方差矩阵。(图片由作者提供)。

参数的标准误差。(图片由作者提供)。

在 Python 中,我们只需向 fit 方法添加以下代码行:

*epsilon = y - X.dot(betas)
sse = np.sum(epsilon * epsilon)

sigma_squared = sse / (n - p)
self.sigma_squared = sigma_squared

V = C * sigma_squared
self.V = V

sigma_betas = np.sqrt(np.diag(V))
self.sigma_betas = sigma_betas*

然后,我们可能会得到与零假设相关联的t-值及其对应的p-值。在 Python 中,可以通过使用来自 scipy.stats (之前在这里作为 t_fun 导入)的 t 生成器实例来完成。

*pvalues = t_fun.sf(abs(betas) / sigma_betas, (n - p)) * 2.0
self.pvalues = pvalues*

现在,我们已经准备好工具来估计回归系数及其统计意义,并根据新的观察结果进行预测。让我们在下一节应用这个框架。

引线键合示例

这是一个示例,其中的目标是基于导线长度和芯片高度来预测半导体制造过程中导线接合的拉伸强度。它是从 Montgomery & Runger (2003 年)检索的。这是一个小数据集,在这种情况下,线性模型尤其有用。我将它的一个副本保存在与示例笔记本相同的存储库中的一个. txt 文件中。

让我们首先导入数据集。

*dataset = pd.read_csv("../data/montgomery/wire_bond.txt", sep=" ", index_col=0)*

然后定义自变量 X 的矩阵和预测变量 y 的观测值的向量。

*X = dataset.iloc[:, :-1].values
y = dataset.iloc[:, -1].values*

我画了一些散点图,看看预测因子和因变量之间的关系。

引线键合问题中的目标与预测。(图片由作者提供)。

请注意,预测变量和回归器线长度之间存在很强的线性关系。相反,芯片高度和焊线之间的线性关系在成对可视化中并不明显,尽管这可能归因于其他预测因素的影响。

接下来,让我们创建一个 LinearRegression 类的实例,使其适合数据,并基于 R 度量验证其性能。

*linreg = LinearRegression()
linreg.fit(X, y)
print(linreg.r2_score(X, y))*

它返回值 0.9811。

好像很有希望!现在,为了验证参数的统计意义,让我们运行以下代码:

*for i, val in enumerate(linreg.betas):
    pval = linreg.pvalues[i]
    print(f"Beta {i}: {val:.2f}; p-value: {pval:.2f}")*

它返回:

*Beta 0: 2.26; p-value: 0.04
Beta 1: 2.74; p-value: 0.00
Beta 2: 0.01; p-value: 0.00*

因此,我们有一个具有很好的性能和统计意义的模型,只要数据分布没有显著变化,它就可能在新的观察结果上表现良好。注意,根据它的p-值,我们可能会考虑放弃截距。最后,让我们验证模型假设。

  • 残差服从正态独立分布。
  • 残差的平均值为零。
  • 残差具有恒定的方差。

让我们首先验证残差的平均值。

*y_hat = linreg.predict(X)
epsilon = y - y_hat
print(f"Mean epsilon: {np.mean(epsilon):.3f}")
print(f"Sigma epsilon: {np.std(epsilon):.3f}")*

这似乎没问题:

*Mean epsilon: -0.000
Sigma epsilon: 2.146*

现在,让我们使用夏皮罗-维尔克正态性检验。

*shapiro_stat, shapiro_pvalue = shapiro(epsilon)
print(f"Shapiro test p-value: {shapiro_pvalue:.2f}")*

它返回:

*Shapiro test p-value: 0.38*

因此,我们不能拒绝零假设,即我们的残差来自正态分布。

最后,让我们绘制残差与预测变量和回归量的关系图,以验证它们是否独立分布。

残差与预测变量。(图片由作者提供)。

残差与自变量。(图片由作者提供)。

残差大多分布良好,尤其是在导线长度小于 7 和目标长度小于 25 的区域。然而,在目标和导线长度之间可能存在一些非线性,因为中间值的残差偏向负值,而高值(目标> 50)偏向正值。那些有兴趣更详细地探索该问题的人可以尝试创建多项式要素,看看这种偏差是否会减少。此外,方差似乎不是有条件分布的。因此,我们不可能通过加权残差来提高模型性能。

在下一节中,让我们创建一个与第一个自变量相关的假预测值,并验证统计显著性检验是否能识别它。

添加错误的预测值

假预测值将等于线长度加上随机噪声项,该随机噪声项遵循平均值 0 和σ1 的正态分布。

*# Predefined random seed to reproductibility of results
np.random.seed(42)

# Create feature
x_false = X[:, 0] + np.random.randn(X.shape[0])
X_false = np.column_stack((X, x_false.reshape((-1, 1))))*

请注意,它也与响应变量线性相关。事实上,甚至比芯片高度更相关。

目标预测值与错误预测值。(图片由作者提供)。

让我们重复上一节的过程来看看结果。

*linreg_alt = LinearRegression()
linreg_alt.fit(X_false, y)
print(linreg_alt.r2_score(X_false, y))*

看起来得分指标仍然很高(0.9828),但我们的结果不一定像以前一样有意义。让我们来验证置信区间。

*for i, val in enumerate(linreg_alt.betas):
    pval = linreg_alt.pvalues[i]
    print(f"Beta {i}: {val:.2f}; p-value: {pval:.2f}")*

它返回:

*Beta 0: 2.13; p-value: 0.05
Beta 1: 3.43; p-value: 0.00
Beta 2: 0.01; p-value: 0.00
Beta 3: -0.69; p-value: 0.16*

看来我们找到了冒名顶替者…

因此,我们的框架有效地验证了错误的预测器没有向模型提供额外的信息,假设我们仍然具有线长度的原始值,即使错误的预测器与预测变量具有强线性相关性。相反,相关性较弱的芯片高度对模型的贡献具有统计学意义。

在这种情况下,强烈建议从模型中删除不必要的特征,以提高其可解释性和通用性。在示例笔记本中,我还提出了一个基于 p 值的递归特征消除策略。

进一步阅读

作为一个有工程背景的人,在这方面我应该推荐的第一本参考书是 Montgomery Runger(2003)写的《T4 工程师应用统计和概率》。在那里,你可能会发现这里提出的基本原理的更详细的描述和其他相关的回归方面,如多重共线性,平均响应的置信区间,和特征选择。

那些对机器学习更感兴趣的人可以参考 Hastie 等人(2009)的统计学习的要素。特别是,我发现看到作者如何解释最近邻与线性模型之间的偏差-方差权衡非常有趣。

当一个人的目标是估计具有描述非线性现象的一些基本意义的参数时,非线性回归也是令人着迷的。贝茨&瓦特(1988)的《T2【非线性回归分析及其应用】T3》一书是一个很好的参考。Myers 等人(2010 年)的《T4 广义线性模型》一书的第 3 章也介绍了这个主题。

科斯马·沙立兹教授的课堂讲稿可以在这个链接上找到。目前,该草案的标题为从初级角度进行高级数据分析。在那里,人们可以找到有趣的主题,如加权和方差,因果推断,和相关数据。

我很欣赏 Python 包 statsmodels 。在应用了 OLS (正如我们在本文中所做的)之后,人们可能会有兴趣尝试 WLS 来解决残差方差不均匀的问题,并使用 VIF 来检测要素多重共线性。

结论

在本文中,介绍了多元线性回归的主要原理,然后用 Python 从头开始实现。该框架应用于一个简单的例子,其中除了线性最小二乘问题中关于残差的主要假设之外,还验证了参数的统计显著性。完整的代码和额外的例子可以在这个链接中找到。

参考

贝茨博士和瓦茨博士,1988 年。非线性回归分析及其应用。威利。**

茨韦塔纳·哈斯蒂和 J. H .弗里德曼,2009 年。统计学习的要素:数据挖掘、推理和预测。第二版。纽约:斯普林格。

蒙哥马利特区和龙格,2003 年。工程师应用统计和概率。第三版。约翰·威利父子公司。

迈尔斯,R. H .,蒙哥马利特区,维宁,G. G .和罗宾逊,T. J .,2012 年。广义线性模型:在工程和科学中的应用。第二版。霍博肯:约翰·威利&之子。**

c .沙里齐,2021。从初级观点看高级数据分析。剑桥大学出版社。

支持医院运营的多元线性回归

原文:https://towardsdatascience.com/multiple-linear-regression-to-support-hospital-operations-34f5516384c3

探索一种用于进行预测和了解数据集不同要素之间的相互作用的强大工具。

处理人员配备问题的行业,如医院管理行业,可以从使用多元线性回归中受益,以更好地安排他们的工人,并更好地了解他们当前运营中的哪些因素可能会增加员工的工作时间。

国家癌症研究所Unsplash 上拍摄的照片

简介

线性回归是进行预测和检查数据集不同要素之间的相互作用的一种很好的技术。不仅能够用 Python 实现代码很重要,而且突出模型输出的意义和重要性对于验证模型是否准确解释数据集也至关重要。

履行

数据集

我们今天要使用的数据集是一个 SAS 医院人员数据集。数据集的独立特征是 X 射线(每月 X 射线数量)、床位天数(每月占用床位天数)长度(患者每月平均住院时间 ) 。因变量为医院每月手术小时数。数据集可在此处找到

分析

我们希望对数据集执行多元线性回归,以了解 X 射线、住院天数和住院时间如何影响医院的运营时间。这对于医院管理层确定医生、护士等的数量是很重要的。每个月都需要人手。此分析的代码可在处找到。

首先,让我们导入我们希望使用的库。今天我将介绍两个不同的能够实现线性回归的库。sci-kit learn拥有大量用户熟悉的机器学习库,可以轻松实现线性回归执行。我喜欢使用stats models,因为它输出的 ANOVA(方差分析)表提供了我们创建的模型的大量统计信息。****

****#Import Libraries**
import pandas as pd 
import numpy as np 
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
import seaborn as sns 
from statsmodels.formula.api import ols
from statsmodels.stats.anova import anova_lm**

下一步是加载数据框并查看前 5 行。

**df = pd.read_excel('Hospital_dataset.xlsx')
df.head()**

数据框中的前 5 行(图片来自作者)

既然数据已经载入,我个人喜欢得到自变量(X 列)和因变量(y 列)之间的相关性。这可以让我们更好地了解在实现我们的回归时会有什么样的交互。对于此分析,我们的响应(因变量)变量 y小时。

**#Correlation 
df.corr()['Hours'].sort_values(ascending=False)**

相关性(图片来自作者)

不出所料,小时与自身完美关联。手术时间、x 射线和卧床天数之间有很高的相关性。这表明小时随着 x 射线和卧床天数的增加而增加。小时与患者的平均住院时间没有很大的相关性。当增加患者平均住院时间时,这可能会导致小时的低增长或负增长(患者住院时间的长短可能不会产生太大的影响)。

接下来,我们将创建我们的 X 和 y 值。

**#Create x and y values
X = df[['Xray','BedDays','Length']]
y = df['Hours']**

同样,在制作我们的模型之前,我们将使用 sns.regplot 调查每个 x 列和小时之间的相互作用。regplot 允许数据的回归拟合和绘图。

**#XRays vs. Hours Regplot 
sns.regplot(x='Xray',y='Hours',data=df)**

X 射线和时间的回归图(图片来自作者)

**#BedDays vs. Hours regplot 
sns.regplot(x='BedDays',y='Hours',data=df)**

卧床天数和时间的回归图(图片来自作者)

**#Length vs. Hours Regplot 
sns.regplot(x='Length',y='Hours',data=df)**

长度和小时的回归图(图片来自作者)

前两个回归图显示,这些特征和时间有更好的拟合。在第三个情节中,住院时间的长短与小时数没有很大的关系。由于相关值较低,这在意料之中。可能需要进行进一步的分析,以寻找可能对模型产生影响的异常点(尤其是具有异常长度的异常点)。长度的值的方差比床日X 射线的值的方差大得多,这可能在模型中产生偏差。

下一步将是创建一个线性回归模型。对于本例,我们将使用所有三个解释变量(X 特征)来创建模型。我们将使用 scikit-learn 中的 LinearRegression() 来创建我们的第一个模型。

**lm  = LinearRegression()
lm.fit(X,y)**

接下来,我们将获得 R 值。r 让我们了解到我们的模型能多好地解释数据。较高的 R 值意味着模型在解释数据集的潜在关系方面做得更好。

**print('The R^2 value is:', lm.score(X,y))**

r 值(图片来自作者)

R 值很高,这是一个好现象!这表明这是一个很好的模型。需要明确的是,真的没有完美的 R 值(被验证为“好”的计量经济学模型可能具有较低的 R 值)。您还可以使用进行预测。scikit 包中的 predict()。

**#Predictions | Error
predictions = lm.predict(X)print(predictions)**

预测(图片来自作者)

现在我们有了一个能够做出预测的模型!我们可以用于多元线性回归的另一个包是 statsmodel 包,由于它的 ANOVA 表输出,我通常会更多地使用它。ANOVA 表收集关键统计数据,这些数据可以帮助我们判断我们创建的模型是否适合数据集。

首先,让我们来拟合我们的模型。

**# Fit the model
y= df['Hours']
x = df[['Xray','BedDays','Length']]model = ols("y ~ x", df).fit()**

接下来,让我们打印出该模型所有重要统计数据的摘要。

**# Print the summary
print(model.summary())# Peform analysis of variance on fitted linear model
anova = anova_lm(model)print('\nANOVA results')
print(anova_results)**

方差分析输出(图片来自作者)

ANOVA 输出提供了许多有用的信息,有助于理解我们的模型如何很好地解释给定月份医院的手术小时数。我首先关注的是 R and R 调整值。R 值非常高,为 0.99,这是一个好现象。R 调整值 0.988 也接近 R 值,这表明这是一个强模型。R and R 调整的区别在于,R 调整考虑了模型中参数(β系数)的数量。尽管数据越多越好,但使用数据集中的每个要素及其值并不一定会为该数据集创建最佳模型。另一个可以为模型的有效性提供快速信息的值是系数的 p 值。一般来说,我们希望 p 值小于 0.05。截距具有较高的 p 值,如果我们去掉β3 系数,该值可能会降低,因为它的 p 值大于 0.05。最后,德宾-沃森值是 2.546(我们希望该值接近 2)。这并不太令人担忧,但该值表明数据中几乎不存在自相关。

接下来,我们可以进一步查看β系数值,以了解不同因素如何影响医院的运营小时数。

 **#Parameter Values
print('The intercept is',model.params[0])
print('The beta 1 coefficient is',model.params[1])
print('The beta 2 coefficient is', model.params[2])
print('The beta 3 coefficent is', model.params[3])**

贝塔系数值(图片来自作者)

截距是 1,523.39,这意味着模型的平均值是 1,523.39 小时,如果所有其他值都为零。β1 系数为 0.53。这可以解释为 x 射线增加 1 个单位,导致每月医院手术时间大约增加 0.53 小时。β2 系数为 0.98。对该值的解释是,病床天数每增加 1 个单位,住院时间就增加 0.98 个小时。在看到 x 射线和卧床天数与小时数的正相关后,正值是意料之中的。就住院时间而言,每增加 1 个单位,医院的手术时间就会减少-320.95 小时。

现在,如果给我们一个值,我们也可以使用这个模型来快速预测我们期望的小时数。举个例子,如果我们相信一个月照 x 光的次数是 56194 次,一个月的卧床天数是 14077.88 天,平均住院时间是 6.89 天,会怎么样?

**pred = model.params[0] + model.params[1]*56194 + model.params[2]*14077.88 + model.params[3]*6.89
print('The prediction is',pred, 'hours.')**

预测(图片由作者提供)

这组 X 值的预测值是 16,064.55 小时。

结论

今天我们看了两种在 Python 中快速运行多元线性回归模型的方法。研究了多元线性回归的基本原理以及如何解释所建立的线性模型的不同值。多元线性回归是一个很好的工具,可以用来预测完成一个项目所需的时间,或者是一个组织在计划人员安排时需要考虑的时间。如果你喜欢这篇文章,或者想让我更深入地研究如何建立一个更好的线性回归模型,并剖析模型的有效性,请在下面发表评论,或者随时联系我!

请在 LinkedIn 上加我或者随时联系!感谢阅读!

来源

威廉·j·肯尼迪和默文·马拉辛格..数据分析 SAS:中间统计方法。英国,纽约斯普林格,2008。

R 中序数变量和预测概率的多元逻辑回归

原文:https://towardsdatascience.com/multiple-logistic-regression-for-ordinal-variable-and-predicted-probabilities-in-r-3e3ef3ba6ca2

R 系列中的统计

Unsplash 上由 Zaini Izzuddin 拍照

简介

在上一篇文章中,我们已经介绍了有序变量的简单逻辑回归,当看到结果时,这变得非常有趣。在本文中,我将讨论相似有序数据的多元逻辑回归,并使用 R 包预测概率。在有序逻辑回归中,预测变量可以是有序的、二元的或连续的,而响应变量是有序的。

例如,如果我们使用有序教育水平来预测收入,它只有两个水平的响应。我们可以有从一年级到博士学位的教育水平,并分配有序的数字来进行回归。我们也可以用二元变量来预测收入。例如,我们可以将 1 分配给有学士学位的人,将 0 分配给没有学士学位的人。在某种意义上,这也可以被认为是两个 kevels 的序数变量。最后,我们也可以使用教育年限等连续变量作为预测收入水平的预测因子。下面的文章给出了准确的分析。

这里,我们现在感兴趣的是同时引入多个变量作为预测因子。

数据集

作为一个案例研究,我们将使用位于 UCI 机器学习库中的成人数据集。该数据集中收集了超过 30000 个人的人口统计数据,包括他们的种族、教育、职业、性别、工资、每周工作时间、就业水平以及收入水平。

来自 UCI 机器学习知识库的成人数据集

为了进行有序逻辑回归分析,有必要修改给定的数据。尽管如此,让我首先提出一个问题来进行研究。

受教育程度、性别、种族对收入有什么影响?

为了回答这个问题,我们需要教育和收入水平的标签编码数据。正如你在数据集中看到的,从一年级到博士学位有不同的教育水平。由于收入水平是二进制的,它提供了个人收入是否超过 50000 美元的信息。在这种情况下,我们正在处理一个二元响应变量和一个序数预测变量(Education_code 列),两个二元预测变量(Gender_code 和 Race_code)。

对于性别,男性分配 2,女性分配 1。稍后我们将交换这些数字,看看结果有什么不同。对于种族,我们有兴趣看看白人和非白人之间的收入是否有差异。非白种人被分配 1,白种人被分配 0。分析要在 R 中进行,开始吧。

链接 excel 文件:成人-v3.xlsx

在 R 实施

我们将在这里使用相同的 clm()函数。但首先,我想澄清不同教育程度的价值观。如下图所示。

要添加多个变量,我们可以简单地在 clm()命令中使用下面的格式。

模型 _clm

To show the effect of non-related variables, I have introduced a new column names “Random_code” and we will see it’s effect on the result.

结果解释

在上面的代码中,显示了两个模型。第一个模型评估了教育水平、性别和种族对收入的影响。第二个模型引入了一个新的随机变量。

  1. 模型 1:预测变量是序数教育水平、二元性别和二元种族变量。响应变量是二元收入水平。
  2. 模型 2:预测变量是序数教育水平和一个连续的随机变量。响应变量是二元收入水平。

模型 1 结果

模型 1 结果

从结果总结来看,关键要点是:

  • 教育水平每提高一级,收入超过 50000 美元的对数或对数几率增加 0.581。
  • 性别代码每增加一个单位,收入大于 50000 美元的对数或对数几率增加 1.357。这意味着,如果被研究的个体是女性(1),而我们用男性(2)代替这个个体,那么高收入的 logit 概率增加了 1.357。
  • Race_code 每增加一个单位,收入> 50000 美元的对数或对数几率增加 0.475。这意味着,如果被研究的个体是非白人(1),而我们用一个白人来代替这个个体(2),更高收入的 logit 概率增加了 0.475。
  • 所有的预测变量都有意义(p<0.05)。

如果我们交换性别值(将 1 分配给男性,将 2 分配给女性),系数的符号会发生如下变化

交换性别代码后的模型 1 结果

这向我们表明,性别代码每增加一个单位,收入> 50000 美元的对数或对数几率减少 1.357。这意味着,如果被研究的个体是男性(1),而我们用女性(2)来代替这个个体,那么高收入的 logit 概率降低了 1.357。如果我们交换种族代码,同样的事情也会发生。

模型 1 结果

关于伪 R 值,麦克法登给我们的值是 0.149,我们将在后面的讨论中把这个值与其他模型进行比较。我们还将比较两个不同模型的 AIC 和 BIC 统计,因为单个模型的单个值在逻辑回归中没有太大的意义。

模型 2 结果

模型 2 结果

从结果总结来看,关键要点是:

  • 教育水平每提高一级,收入超过 50000 美元的对数或对数几率增加 0.562。
  • 在这种情况下,由于 p>0.05,随机变量不显著。

模型 2 结果

麦克法登伪 R 值 ios 小于模型 1,AIC/BIC 统计值高于模型 1。这意味着当我们比较这些拟合优度统计时,模型 1 具有更好的性能。

预测

我们将使用 ggpredict()命令来有效地预测预测变量的任何给定值。我们需要 ggeffects libarary 来实现它。让我们预测一组人的收入水平,假设他们的教育水平是 3、5、9 和 13。

预测结果

由于我们有两个收入水平(收入> 50000 美元和收入≤ $50000 美元),响应水平的数量也是 2,如上面的红色所示。每个教育水平的预测概率显示在第二列。当教育水平为 3(5 到 6 年级)时,收入≤ $50000 的概率是 0.99,而如果教育水平为 13(博士),收入≤ $50000 的概率是 0.36。从第二响应水平预测结果可以做出相同的推断。

我们可以在这里引入多个预测变量。包括性别后,我们得到以下结果。

预测结果

现在我们有 4 个表,因为性别有两个类似于收入水平的值。对于性别代码 1(女性),如果个人有博士学位(教育代码 13),收入> 50000 美元的预测概率是 0.42,而如果个人是男性(性别代码 2)并且他有博士学位,收入> 50000 美元的预测概率是 0.74。这表明女性在同等教育水平下获得了某种不平等的报酬。

结论

已经讨论了两个模型,它们结合了一个顺序预测变量和一个二元响应变量的逻辑回归。第一个模型包含序数教育变量,二元性别和种族变量,以及收入变量。第二个模型由一个连续的随机变量、顺序教育变量和一个二元收入变量组成。我们在伪 R2 和 AIC/BIC 统计的基础上比较了这些模型,以确定它们的性能。此外,从一组给定的数据中,根据预测结果的概率和 95%的置信区间进行预测。

数据集确认

杜瓦博士和格拉夫博士(2019 年)。UCI 机器学习知识库[http://archive . ics . UCI . edu/ml]。加州欧文:加州大学信息与计算机科学学院。

感谢阅读。

https://mdsohel-mahmood.medium.com/membership https://mdsohel-mahmood.medium.com/subscribe

R 中二分变量的多元 Logistic 回归

原文:https://towardsdatascience.com/multiple-logistic-regression-in-r-aa65c2a91e22

R 系列中的统计

金伯利农民在 Unsplash 上拍摄的照片

简介

简单的逻辑回归只包含一个预测变量,我们之前已经用 R 实现了它。我们还讨论了拟合优度的统计数据。通常,真实世界的数据有几个预测变量。有时,我们根本不知道将额外的变量作为预测因素是否会使模型更加稳健。因此,我们需要始终将完整模型与嵌套模型进行比较,并从数据中得出结论。这里,我们将增加独立参数的数量,并扩展我们对多元逻辑回归的研究。

数据集

2016 年综合社会调查(GSS)的数据将用于证明逻辑回归。数据从宗教数据档案协会下载,由汤姆·w·史密斯收集。该数据集包括从近 3,000 名受访者收集的回答,并包含与若干社会经济因素相关的数据。它包含各种类型的数据,如与一个人的婚姻状况、教育状况、工作时间、就业状况等相关的信息。让我们仔细看看这个数据集,以便更好地理解它。

对于每个个体,学位列提供教育水平,而 MADEG 列提供每个个体母亲的教育水平。在这项研究中,我们旨在确定母亲的学士学位水平是否是子女学士学位水平的良好预测因素。该数据集包含普通编码的分类数据。

学位数据[图片由作者提供]

MADEG 数据[图片由作者提供]

我们正在努力寻找的答案

在之前的多元逻辑回归文章中(链接如下),

我们试图回答以下问题。

母亲的教育水平会影响孩子的教育水平吗?

我们对数据的分析揭示了 MADEG 的正系数,它代表了母亲的教育水平。从下图结果部分的解释中可以看出,截距估计值为 0.257,MADEG 系数估计值为 0.316。因此,预测变量(即母亲的教育水平)每增加一个单位,孩子的教育水平值为 1 的 logit 概率将增加 0.31598。然而,这仍然代表正斜率,表明响应变量随着预测变量的增加而增加。因此,如果孩子的母亲拥有学士学位,孩子获得学士学位的可能性就会增加。

现在,我们将提出一个新的问题。

父亲的受教育程度与孩子的受教育程度有关系还是不相关?

当我们引入另一个独立变量时,简单的逻辑回归就变成了多元逻辑回归。

R 中的实现

为了在 R 中执行这个回归研究,我们需要安装以下库。数据存储在一个 excel 文件中,我们将使用 glm()函数。现在的区别是增加了 PADEG,代表父亲的教育水平。

r 代码[按作者]

R 中的输出窗口[图片由作者提供]

R 中的输出窗口[图片由作者提供]

R 中的输出窗口[图片由作者提供]

解读结果

作为实施逻辑回归过程的第一步,我们需要将输出成功的概率转换为对数度量,以便确定预测变量的系数和截距。我对下面的数据做了简单的解释。此外,为了便于比较,我还将简单逻辑回归的输出窗口放在了同一个位置。

仅考虑 MADEG 时简单逻辑回归的系数

仅考虑 MADEG 时简单逻辑回归的伪 R

仅考虑 MADEG 时,简单逻辑回归的 AIC/BIC

  1. MADEG 系数为 0.136,PADEG 系数为 0.375,截距系数保持相似。我们可以得出结论,母亲的教育水平每增加一个单位,孩子的教育水平为 1 的对数概率增加 0.135,这仍然是正的;父亲的教育水平每增加一个单位,孩子的教育水平为 1 的对数概率增加 0.375,这也是正的。换句话说,当母亲和父亲的学士学位都被考虑在内时,孩子获得学士学位的可能性就增加了。
  2. 相关的 p 值小于 0.05,这也告诉我们拒绝零假设。这里的零假设是“预测变量的系数为 0,基本上不影响响应变量”。因此,我们可以得出结论,母亲和父亲的学士学位教育显著影响孩子的学士学位。
  3. 伪 R 值也可以与简单的逻辑回归对应物进行比较。很明显,当父亲的教育水平包括在内时,这种情况下的伪 R 值增加了。这意味着完整的模型比简单的逻辑斯蒂模型更适合。
  4. AIC/BIC 的统计数据也可以进行比较。同样明显的是,在完整模型中,AIC/BIC 值较小。较小的 AIC/BIC 值表示更好的拟合,这也支持 pseudo R 的陈述,其中我们也得出完整模型更好的结论。
  5. 简单逻辑回归模型的偏差为 532.11,而新模型的偏差为 395.40,这意味着新模型与饱和模型的偏差较小。在饱和模型中,参数的数量等于样本大小,因为它包含每个观察值的一个参数。零偏差和剩余偏差之间的差异用于确定当前模型的显著性。
  6. 我们也可以通过减去这两个模型之间的偏差来计算对数似然比。这是为了在几个嵌套模型与考虑了所有可能预测变量的完整模型之间进行比较。

对数似然比=简化模型的偏差-完整模型的偏差

结论

我们已经讨论了多元逻辑回归及其在 R 中的实现。我们还浏览了 R 的输出并解释了来自一般社会调查的结果。预测变量的正系数表明,随着母亲和父亲的学士学位值从 0 增加到 1,孩子的学士学位变为 1 的概率分别增加 0.135 和 0.375,或者换句话说,可以得出结论,母亲和父亲的教育对我们数据集中孩子的教育有显著影响。

感谢阅读。

https://mdsohel-mahmood.medium.com/membership https://mdsohel-mahmood.medium.com/subscribe

浓缩咖啡中 DiFluid 折光仪的多个样品

原文:https://towardsdatascience.com/multiple-samples-for-the-difluid-refractometer-in-espresso-c9a9cda08aab

咖啡数据科学

再次仔细检查一个特征

之前,与 VST 和 Atago 相比,关于二流体折光率仪的研究相当多。我确实忽略了钻井液的一个特点:多样本。VST 和 Atago 都采用了多个样本,但是他们没有给出选项,也没有显示样本之间的误差。DiFluid 有,所以我用这个多重特性收集了更多的数据,再看一看。

设备/技术

浓缩咖啡机 : 像样的浓缩咖啡机

咖啡研磨机 : 小生零

咖啡:家庭烘焙咖啡,中杯(第一口+ 1 分钟)

镜头准备:断奏夯实

预灌注:长,约 25 秒

过滤篮 : 20g VST

其他设备: Atago TDS 计DiFluid TDS 计Acaia Pyxis 秤

性能指标

我用 TDS 评估了这两个传感器。总溶解固体(TDS)是用折射仪测量的,这个数字结合弹丸的输出重量和咖啡的输入重量用来确定提取到杯中的咖啡的百分比,称为提取率(EY)** 。**

数据

我使用 DiFluid 和 Atago 收集了 29 对数据点,对于 28 对,我还冷却了样本并收集了另一个 TDS 样本。对于 DiFluid,我只使用了 10 个样本的平均特性。总的趋势是钻井液低于报告的 TDS,但该趋势有一个有趣的偏移,因为它非常接近 y = x。

所有图片由作者提供

我也添加了误差线,但是我不认为它们有什么不同。

当冷却样品时,钻井液超过报告的 TDS,但趋势相似。我没有画误差线,因为误差通常低于 0.05%

我比较了热样品和冷样品的折射率,温度对折射的影响非常明显。

从误差率来看,冷误差率非常低,我怀疑不需要多个样本。如前所述,DiFluid 的质量较小,因此其温度稳定性不如 Atago 或 VST。

****

在查看冷/热 TDS 时,Atago 和 DiFluid 具有相似的趋势斜率,但存在偏移。

DiFluid 是一种有趣的传感器,因为它可以提供其他折光率仪无法提供的数据。这个分析的难点在于我只有一个样本,而不是全部 10 个。通常,当样品冷却时,相同的读数会发生变化。很有可能我的设备是最好的,所以这种差异应该是麻烦的。或者,我的设备可能是最差的样本。幸运的是,新的 DiFluid 模型即将出现,因此可以获得更多的数据!

如果你愿意,可以在推特、 YouTubeInstagram 上关注我,我会在那里发布不同机器上的浓缩咖啡照片和浓缩咖啡相关的视频。你也可以在 LinkedIn 上找到我。也可以关注我在订阅

我的进一步阅读:

我未来的书

我的链接

浓缩咖啡系列文章

工作和学校故事集

多重系列?将它们与任何 Sklearn 模型一起预测

原文:https://towardsdatascience.com/multiple-series-forecast-them-together-with-any-sklearn-model-96319d46269

用 Python 同时预测多个序列的趋势

劳埃德·威廉姆斯在 Unsplash 上拍摄的照片

一种流行的经典时间序列预测技术叫做向量自回归(VAR) 。这种方法背后的思想是,多个序列的过去值(滞后)可以用来以线性方式预测其他序列的未来值。它以这种方式一起预测多个时间序列。

你什么时候会想用这样的方法?当你有两个或更多你怀疑会相互影响的序列时,比如利率和通货膨胀,这是一个有效的方法。运行 VAR 时,所有系列都是稳定的,这一点非常重要。一旦确定了要预测的序列并确保了它们的平稳性(通过对非平稳序列进行差分或转换),剩下要考虑的唯一参数就是用作预测的滞后数。这可以通过在不同数量的滞后上进行信息标准搜索来完成。VAR 方法有一些扩展,包括用误差项估计(VARMA)、应用误差修正项(VECM)、添加外生变量(VARMAX)和用季节性估计(SVAR)。

将 VAR 概念扩展到机器学习

逻辑上的问题是,如果你在这个过程中改变了基本的线性函数会怎样?或者,如果你把上面描述的几个概念混合搭配会怎么样?有多种方法可以使用这种通用方法,但需要更多基于机器学习的过程,例如使用 Scikit-Learn 库中可用的模型。考虑到应该如何准备时间序列数据以及更新预测和模型输入的困难,从头开始编写这样一个程序将是非常耗时的。令人欣慰的是,一些 Python 包,如 dartsscalecast 和其他包,为您解决了许多令人头疼的问题。

今天,我将演示如何使用 scalecast 将这种方法应用于预测。设置过程和提取最终结果很容易。该程序包通过多步过程动态预测您输入的所有系列。一个缺点是,以这种方式将机器学习模型应用于多变量预测的研究并不多,至少据我所知是这样。不过,探索还是很有趣的。看完整笔记本这里。这些数据可以在拥有开放数据库许可证的 Kaggle 上获得。如果您觉得 scalecast 有趣,请在 GitHub 上给它打一颗星:

https://github.com/mikekeith52/scalecast

探索性数据分析

我们将了解如何预测以下两个系列:

作者图片

这些测量了从 2015 年 1 月到 2018 年 3 月底加州每周传统和有机鳄梨的销售情况。该图需要一个双轴,因为传统的比有机的具有更高的体积。对它们进行皮尔逊相关计算,我们看到它们的系数是 0.48。这两个系列肯定一起移动,并显示出类似的趋势,尽管在不同的规模。

接下来,我们检查两个序列的平稳性。使用一个共同的测试来确定这一点,扩大的 Dickey-Fuller 测试,我们看到这两个系列可以被认为是 95%的确定性平稳。这意味着我们可以尝试在它们最初的水平上模拟它们。然而,两者看起来似乎都遵循一个趋势(因此不是平稳的),并且两个测试都没有在 99%的显著性水平上确认平稳性。我们可以假设它们现在是稳定的,但是这种分析的一个有趣的扩展是对每个序列进行一次差分,scalecast 可以让你很容易地做到这一点。

作者图片

让我们使用自相关和偏自相关函数来查看每个序列中有多少滞后具有统计显著性:

作者图片

从这些图中,很难准确地说出有多少滞后才是理想的预测。看起来至少有三个,但也可能多达 20 个,中间有一些间隙。让我们宁可增加更少的延迟——3。稍后,我们将展示如何在代码中结合这个决定。

另一个要考虑的重要因素是每个系列的季节性。我们可以通过季节分解法直观地看到季节性:

作者图片

作者图片

看看这一产出,至少在有机系列中确实出现了上升趋势,尽管扩大的 Dickey-Fuller 检验表明了平稳性。也有很强的季节性,看起来好像有一年(52 个周期)和半年(26 个周期)周期。以上是数据的线性分解,但残差看起来仍然遵循一种模式,表明线性模型可能不是最适合的。我们可以尝试使用线性和非线性方法,并添加不同类型的季节性。

最后,让我们使用我们所了解的关于这些系列的一切来做出我们的建模决策。首先,我们将应用的模型:

models = ('mlr','elasticnet','knn','rf','gbt','xgboost','mlp')

MLR 和 ElasticNet 模型都是线性应用,ElasticNet 是混合了 L1 和 L2 正则化参数的线性模型。其他模型都是非线性的,包括 k-最近邻、随机森林、两个提升树和一个多层感知器神经网络。

使用 scalecast 过程,我们现在可以创建预测器对象来存储关于每个系列的信息以及我们想要尝试预测它们的方式:

# load the conventional series
fcon = Forecaster(y=data_cali_con['Total Volume'],
                  current_dates = data_cali_con['Date'])
# load the organic series
forg = Forecaster(y=data_cali_org['Total Volume'],
                  current_dates = data_cali_org['Date'])for f in (fcon,forg):
    # set forecast horizon of 1 year
    f.generate_future_dates(52)    
    # set 20% testing length    
    f.set_test_length(.2)
    # set aside 4 weeks for validation
    f.set_validation_length(4)
    # add seasonality in the form of wave functions
    f.add_seasonal_regressors(
        'week',
        'month',
        'quarter',
        raw=False,
        sincos=True,
    )
    # add the year as a regressor
    f.add_seasonal_regressors('year')
    # add a time trend
    f.add_time_trend()
    # add an irregular seasonal cycle of 26 periods
    f.add_cycle(26)
    # add three dep variable lags    
    f.add_ar_terms(3)

单变量预测

在我们将分析扩展到多序列预测之前,让我们通过应用单变量过程来测试性能。通过从 scalecast 导入以 Grids.py(用于单变量过程)和 MVGrids.py(用于多变量过程)格式保存到工作目录的验证网格,我们可以使用回归器(包括我们已经添加的滞后、季节回归器和时间趋势)自动调整、验证和预测我们选择的模型:

GridGenerator.get_example_grids(overwrite=False)
GridGenerator.get_mv_grids(overwrite=False)

我们可以称预测过程为:

fcon.tune_test_forecast(models,feature_importance=True)
forg.tune_test_forecast(models,feature_importance=True)

我们还可以为每个对象添加一个加权平均集合模型:

fcon.set_estimator('combo')
fcon.manual_forecast(how='weighted')
forg.set_estimator('combo')
forg.manual_forecast(how='weighted')

并绘制结果:

fcon.plot_test_set(ci=True,order_by='LevelTestSetMAPE')
plt.title('Conventional Univariate Test-set Results',size=16)
plt.show()forg.plot_test_set(ci=True,order_by='LevelTestSetMAPE')
plt.title('Organic Univariate Test-set Results',size=16)
plt.show()

作者图片

作者图片

有趣的模式和预测出现了。让我们也导出一些模型摘要,从数字上了解每个模型的表现:

pd.set_option('display.float_format',  '{:.4f}'.format)
ms = export_model_summaries({'Conventional':fcon,'Organic':forg},
                            determine_best_by='LevelTestSetMAPE')
ms[
    [
        'ModelNickname',
        'Series',
        'Integration',
        'LevelTestSetMAPE',
        'LevelTestSetR2',
        'InSampleMAPE',
        'InSampleR2',
        'best_model'
    ]
]

作者图片

仅从测试集 MAPE 来看,XGBoost 在常规系列中表现最好,KNN 在有机非线性模型中表现最好。然而,XGBoost 似乎严重超载。让我们转到多元建模,看看我们是否可以改善结果。

多元预测

为了将上面的单变量建模扩展到多变量概念,我们需要将 scalecast 中创建的预测器对象传递到一个 mvpredictor 对象中。这是这样做的:

mvf = MVForecaster(fcon,forg,names=['Conventional','Organic'])

更多关于这行代码的深度,请看这里的。基本上,任何数量的预测器对象都可以传递给这个新对象。在构建这个新对象之前,您应该已经设置了预测范围并添加了您想要使用的任何 Xvars,否则,您将只有每个系列的滞后来进行预测,并且将失去添加季节性和外生回归量的机会。使用这个新的 mv predictor 对象,它能够从我们提供给它的两个 predictor 对象中获得那些其他参数,但是我们确实需要重新设置测试和验证长度:

mvf.set_test_length(.2)
mvf.set_validation_length(4)

我们有不止一个系列可以预测,但是我们仍然可以使用类似于单变量部分的自动化方法。然而,现在我们的模型将尝试在两个方面进行优化,不仅是所选的误差指标(在优化模型时默认为 RMSE),而且是多个系列的误差指标的集合。例如,如果出于某种原因,准确预测传统系列比正确预测其他系列更重要,我们可以告诉对象只优化该系列的选定误差度量:

mvf.set_optimize_on('Conventional')

要更改它以优化跨系列的平均度量(这也是默认行为),我们可以使用:

mvf.set_optimize_on('mean')

现在,我们运行自动预测程序:

mvf.tune_test_forecast(models)

完成后,我们告诉对象根据我们选择的度量设置最佳模型。我选择测试集 MAPE。这将选择在两个系列中平均具有最佳测试集 MAPE 的模型:

mvf.set_best_model(determine_best_by='LevelTestSetMAPE')

现在,让我们看看结果。我们可以一起绘制所有系列的所有模型,但是由于常规和有机体积的比例非常不同,我们仍然希望逐个绘制它们,就像我们在单变量部分所做的那样:

mvf.plot_test_set(series='Conventional',
                  put_best_on_top=True,
                  ci=True)
plt.title('Conventional Multivariate Test-set Results',size=16)
plt.show()mvf.plot_test_set(series='Organic',
                  put_best_on_top=True,
                  ci=True)
plt.title('Organice Multivariate Test-set Results',size=16)
plt.show()

作者图片

作者图片

非常好!在进一步检查这些之前,让我们探索另一种类型的集合模型,它可以在 scalecast 中与多变量预测一起执行。

模型堆叠

在单变量部分,我们应用了 scalecast 固有的集成模型——加权平均模型。多变量预测只允许应用 Scikit-learn 模型,因此我们没有相同的组合模型可用,但有一个不同的集成模型可以使用:StackingRegressor。

from sklearn.ensemble import StackingRegressor
mvf.add_sklearn_estimator(StackingRegressor,'stacking')

现在,我们使用之前应用和调整过的其他模型来构建模型,如下所示:

这看起来可能很复杂,但这是将我们之前定义的 MLR、ElasticNet 和 MLP 模型合并为一个,其中每个模型的预测都成为最终模型的输入,即 KNN 回归量。我选择这些模型是因为它们显示出最少的过度拟合可靠测试集误差度量的迹象。让我们称这个模型为 scalecast。我们可以使用 13 个滞后来训练这个模型,因为正如我们很快就会看到的,13 个滞后是模型调整过程中最常选择的,尽管我们可以根据需要通过网格搜索来调整该参数。

mvf.set_estimator('stacking')
mvf.manual_forecast(estimators=estimators,final_estimator=final_estimator,lags=13)

现在,我们可以看到所有型号的型号性能:

mvf.set_best_model(determine_best_by='LevelTestSetMAPE')
results2 = mvf.export_model_summaries()
results2[
    [
        'ModelNickname',
        'Series',
        'HyperParams',
        'LevelTestSetMAPE',
        'LevelTestSetR2',
        'InSampleMAPE',
        'InSampleR2',
        'Lags',
        'best_model'
    ]
]

作者图片

ElasticNet 在两个系列之间具有最好的平均误差,这就是为什么它在上面的输出中被标记为两个系列中最好的,但是 KNN 在有机系列中比 ElasticNet 做得更好。我们可以选择 ElasticNet 作为常规系列的最终模型,选择 KNN 作为有机系列的最终模型。这两个最佳模型的 MAPE 度量都低于单变量方法的最佳模型,表明整体性能更好。让我们看看它们在 52 期预测范围内的表现:

mvf.plot(series='Conventional',models='elasticnet',ci=True)
plt.title('Elasticnet Forecast - Conventional - MAPE 0.1168',
          size=16)
plt.show()mvf.plot(series='Organic',models='knn',ci=True)
plt.title('KNN Forecast - Organic - MAPE 0.0889',
          size=16)
plt.show()

作者图片

作者图片

对于这些情节来说,重要的是考虑它们看起来是否可信。对我来说,如果一个预测通过了视力测试,那就是它在现实世界中有用的最好迹象。据我所知,这两个模型看起来都还可以,虽然,两个都不能很好地预测这个系列的整体尖峰。也许有了更多的数据或更复杂的建模过程,这种不规则趋势可以更好地建模,但目前,这是我们将坚持的。

结论

这是 Python 中使用 scalecast 进行多元预测的概述。建模过程非常简单和自动化,这有利于快速获得结果,但这种方法有一些问题。通过应用许多模型,有可能幸运地获得一些技术,并在验证数据上过度拟合。我响应来自飞镖包开发者的警告:

“那么[哪种应用模型最好]?嗯,在这一点上,实际上很难说哪一个是最好的。我们的时间序列很小,我们的验证集更小。在这种情况下,很容易将整个预测工作过度适应于这样一个小的验证集。如果可用模型的数量及其自由度很高(例如深度学习模型),或者如果我们在单个测试集上使用许多模型(如在本笔记本中所做的),则尤其如此。

带着这种情绪,看看每种技术的平均误差并记住我们的几个模型显示出过度拟合的迹象可能也是好的。

作者图片

作者图片

从这个角度来看,这两种技术并不明显孰优孰劣,因为一些模型的误差指标会降低,而另一些会增加。无论我们做出什么决定,都需要用常识来调节。一个人在这种分析中做出的决定可能与另一个人的决定不同,两者都可能是正确的。说到底,预测是一门讲述未来的科学,没有人能 100%预测未来。完成 darts 开发者的想法:

“作为数据科学家,我们有责任了解我们的模型可以信任到什么程度。所以,一定要对结果有所保留,尤其是在小数据集上,并在做出任何预测之前应用科学方法:)建模快乐!”

感谢您的关注!

多个选项卡现在是 Streamlit 的一部分

原文:https://towardsdatascience.com/multiple-tabs-are-now-part-of-streamlit-9f15169aab9a

一个新的小部件,可以更好地构建应用程序的布局

照片由大卫·布鲁诺·席尔瓦Unsplash 上拍摄

作为一名数据科学家,我经常使用 Streamlit 来构建交互式 web 应用程序并展示演示和原型。

Streamlit 非常容易使用,它有一个非常直观的语法,可以让你很快熟悉原型制作。你现在不需要了解 HTML 或 JavaScript 就能构建优秀的 web 应用。

Streamlit 提供了一个简洁的 API 来用 Python 制作和设计交互式应用。

这个 API 在一个活跃的社区的帮助下不断发展,这就是为什么我们今天将探索最近添加到它的一个功能。

本帖是对这一新特性的快速回顾。它包括代码示例、语法概述,并概述了一些可以从添加选项卡中受益的用例。

让我们看一看🔍

https://medium.com/membership/@ahmedbesbes

快速提醒

从版本1.11.0开始,Streamlit 引入了st.tabs;一个将多个容器插入不同标签的新组件。

Streamlit 宣布推出新的 st.tabs 功能

语法💻

和往常一样,语法非常简单,⭐️.

首先通过向st.tabs函数传递一个选项列表来创建选项卡。

每个选项对应一个选项卡的名称。

作者截图

现在,要向每个选项卡添加内容,您有两种选择。

1 —通过with语句在每个选项卡上使用上下文管理器

2-直接调用每个选项卡的方法。

我个人更喜欢第一种方法,因为它使代码更干净,更容易阅读。使用上下文管理器有助于快速识别每个选项卡的内容。

例子📊

你可以在标签页中添加任何你想要的东西。

→指标

作者截图

→一个情节

作者截图

→多幅图

作者截图

→混合图表和牛郎星图表(或 plotly、bokeh 和 matplotlib 图表)

作者截图

→输入部件(选择框、输入文本、滑块等。)

作者截图

选项卡基本上是你想在 Streamlit 中创建的任何东西的容器。
它们提供了相关内容组之间的简单导航。

活性天然成分

就 UX 而言,我发现标签之间的转换快速而平滑。

作者截图

以编程方式创建选项卡

st.tabs还允许您动态创建选项卡并向其中插入内容。

下面是一个创建任意数量选项卡的示例。

作者 GIF

那么什么时候应该使用 Streamlit 选项卡呢?

使用选项卡的目的是在独立的视图中对相关内容进行分组。

以下是我使用它们的地方:

  • 将机器学习实验总结为多个选项卡,这些选项卡包含数据帧格式的度量(训练和测试)、交互图、原始数据和预测
  • 可视化工作流:例如,数据在管道的每个阶段经历的所有转换。比如一个话题抽取 app
  • 一个深入的探索性数据分析:类似于熊猫概况产生的东西,但是结果分散在多个标签上

最后的话

我喜欢 Streamlit 和背后的社区不懈地生产。

选项卡,当然还有许多即将推出的功能,将为这个伟大的库带来更多的功能,让人们能够构建有用的应用程序,并获得更好的用户体验。

如果你是 Streamlit 及其生态系统的新手,你可以看看我以前的一些帖子:

</7-reasons-why-you-should-use-the-streamlit-aggrid-component-2d9a2b6e32f0> </5-streamlit-components-to-build-better-applications-71e0195c82d4>

或者查看以下链接:

今天到此为止。下次见!👋

新到中?你可以每月订阅 5 美元,并解锁各种主题的无限文章(技术、设计、创业……)你可以通过点击我的推荐链接来支持我

https://ahmedbesbes.medium.com/membership

how-To:Python 中多重处理的 4 个基本部分

原文:https://towardsdatascience.com/multiprocessing-in-python-9d498b1029ca

像专家一样编码!

有效的 Python

图一。进程、锁、队列和池对于理解多处理 Python 包至关重要。写完博客后,理解以上内容将使编码人员能够在他们的源代码中利用并行处理,并理解在其他代码中的用法。

经过多次请求、一些计划,并有时间交付一个实用的摘要之后,我很高兴地分享一个指南,它将允许您开始在 Python 代码中使用并行处理!

TL;速度三角形定位法(dead reckoning)

P 并行处理可以加速你的代码,并处理每个数据集样本要执行的许多任务。遵循这种范式,Python 中的多处理允许您同时运行多个进程。我们讨论 Python 中多处理包的四个重要组件:进程、锁、队列和池(图 1)。

目录

· [Introduction](#43d9)
· [What is multiprocessing, and why use it?](#0df6)
· [Multiprocessing in Python](#2a93)
· [Using the multiprocessing library — Process, Lock, Queue, Pool](#cd53)
  ∘ [1\. Process](#f318)
  ∘ [2\. Lock](#0385)
  ∘ [3\. Queue](#30eb)
  ∘ [4\. Pool](#943e)
· [Tips on how to get started with multiprocessing](#8d4e)
· [Tips on Best Practices for Using Multiprocessing](#bf67)
· [Conclusion](#9794)
· [Supplemental Material](#4705)

介绍

D 数据科学家经常被赋予使用各种转换技术处理一组数据点的任务。换句话说,给定一个数据集,我们的目标是通过过滤、转换、清理、缩放等对数据进行预处理。此外,为了突出和理解,或者为了分析和可视化结果,必须经常对得到的数据进行后处理。因此,相同的工作必须对 N 个数据样本执行 N 次——正如有经验的数据科学家所争论的那样,建模前后的步骤会成为额外的瓶颈。想了解如何利用 CPU 的全部功能来减少时间开销吗?如果我说使用多处理 Python 包很容易呢?

让我们学习如何通过几行额外的代码来加快 Python 的运行时间!但是首先,让我们并行运行这组进程!入门所需的所有知识涵盖了多处理包的四个组件——进程、锁、队列和池(图 1)。

我们从定义多处理开始,同时强调它的用例。接下来,我们讨论 Python 编程特有的多重处理。然后,通过定义、示例代码和视觉效果描述并举例说明入门所需的四个组件;下面是采用最佳实践的技巧。最后,在总结时,我们回顾了那些希望将学习提高到下一个水平的人的补充资源。完整的代码在 Github 上。

什么是多重处理,为什么要使用它?

多重处理指的是同时运行多个进程,这对加速代码和处理大型数据集和任务非常有用。例如,并行运行一个操作可以将一个作业分成几个可以同时处理的较小部分。典型的单处理器执行,时间成本为N×M,其中 M 为单个进程的时间(即时间单位),下至( N / C ) x M ,其中 C 为 CPU 核心数。考虑到 N 在现代大数据范式中可能会变得非常大,我们可以缩短时间。当您的计算机有多个内核或处理器时,这尤其有用。此外,多处理可以将 I/O 密集型任务分散到不同的进程中,从而提高这些任务的性能(图 2)。

图二。对加工类型的描述。作业期间访问的资源(蓝色文本框)访问内存资源(绿色)以在 CPU 核心线程(红色)上处理作业。如前所述,本质上可并行化的 CPU 密集型任务可以在多个处理器上运行(右图),这是本博客的主题。多线程值得一个单独的博客——期待后续的主题。有关内存和资源的资源,请参见末尾的补充资料。作者创作了这幅插图。

虽然多重处理非常有用,但是注意到一些与使用它相关的潜在危险是很重要的。例如,如果两个进程试图同时访问同一资源,这可能会导致数据损坏或不一致。因此,锁对于保护进程间的共享资源至关重要。与此同时,一个有效的程序员将了解硬件的能力,因此,充分利用它的潜力(图 3)。

图 3。GIF 展示了单核处理(上图)和多核处理(即多处理,下图)之间的区别。该图描绘了具有四个核心的同一个 CPU,四个核心被四个任务(即 T1、T2、…、T4)所包围。请注意单核是如何连续处理任务的,必须完成第一个任务,然后是下一个任务,依此类推。另一方面,多重处理利用所有四个核心来处理程序(也称为作业、任务等)。)并行。作者创作了动画。

对于那些视觉学习最好的人,让我们用另一种方式来比较单处理和多处理:作为处理器和作业的函数(图 4)。

图 4。应该清楚的是,比较单处理和多处理范例,M 个作业(例如,本例中为 4 个)或者一个接一个地串行执行(top ),或者作为独立的进程同时执行。哪个看起来更有效率?作者创作的人物。

Python 中的多重处理

多重处理利用多个 CPU 内核来加速计算负载。Python 采用了一个全局解释器锁(即 GIL),这是一种通过为内存管理实现的引用计数方案来避免内存泄漏和不必要的竞争情况的范例。像大多数实现策略一样,GIL 有其优点和缺点,让我们考虑一个主要缺陷:它限制了 Python 字节码在同一个内核的单个线程上运行。除此之外,GIL 是未来博客的主题。现在,我们知道在核心 Python 中,并行编程是被有意禁用的。

Python 提供了几个工具,通过同名的包来实现多处理,包括进程、锁、队列和池。我们将讨论每个组件,并举例说明接下来如何使用它们。首先,让我们导入包并确定系统中可用的内核数量(图 5)。

图 5。导入并调用' cpu_count()'来了解当前 cpu 上有多少核心。作者创作了这幅插图。

使用多处理库—进程、锁、队列和池

Python 的多重处理的四个基本组件都有特定的用途。下面是每一个的简要概述。

1.过程

这是 Python 中的基本执行单元。每个进程都有自己的 Python 解释器副本和内存空间,允许多个作业同时执行而不会发生冲突。

流程类的处理如下:(1)通过 forked 复制当前流程;(2)创建新的进程标识符;(3)任务作为独立的子进程运行。

具体来说,如下例所示,进程通过两个函数start()join()进行管理。

举例和动手学习。

让我们首先定义一个为sleep_sec休眠的函数,默认值设置为半秒。

import time

def sleep(sleep_sec=0.5):
    print(f'Sleeping for {sleep_sec} seconds')
    time.sleep(sleep_sec)
    print('Finished sleeping')

接下来,让我们使用多重处理包创建多个进程。

import multiprocessing
p1 = multiprocessing.Process(target=task)
p2 = multiprocessing.Process(target=task)

Process()target参数指定了流程运行的目标函数。但是这些进程在启动之前不会立即运行。

p1.start()
p2.start()

将所有这些放在一起,我们有以下内容:

import multiprocessing
import time

def sleep(sleep_sec=0.5):
    print(f'Sleeping for {sleep_sec} seconds')
    time.sleep(sleep_sec)
    print('Finished sleeping')

if __name__ == "__main__":
    start_time = time.perf_counter()

    # Creates two processes
    p1 = multiprocessing.Process(target=sleep)
    p2 = multiprocessing.Process(target=sleep)

    # Starts both processes
    p1.start()
    p2.start()

    finish_time = time.perf_counter()

    print(f"Program finished in {(finish_time - start_time):.3f} seconds")

打印报表将被设置为如下引号:

程序 0.013 秒结束
休眠 0.5 秒
休眠 0.5 秒
休眠结束
休眠结束

请注意,虽然执行了两次对sleep的调用,但是人们会认为程序至少需要 1 秒钟(即,睡眠两次,每次半秒钟)。此外,程序末尾的 print 语句出现在之前通过函数调用执行的语句之前。这是为什么呢?答案很简单。如上所述,start()join()Process()类一起使用的方法。在我们做start()的地方,我们从来不join()。让我们使用原理图来检查程序流程(图 6)。

图 6。当没有调用‘join()’时,程序将执行与其他进程并行的主进程。因此,需要将打印的时间打印在作者创建的主示意图的末尾。

在解决这个问题之前,让我们强调一下关于 Python 中多重处理的一个重要注意事项。

ℹ️ The main program (i.e.,if *__name__ == "__main__"*) must encapsulate our execution; else, the multiprocessing module complains. This safety construct guarantees Python finishes analyzing the program before the sub-process is created.

现在,回到我们的程序,它似乎没有按顺序运行,事实并非如此,但它没有被设置为等待。我们需要在两个进程上调用join()函数,让它们在打印时间之前运行。这是因为有三个过程正在进行:p1p2和主过程。主进程是跟踪和打印执行时间的进程。我们应该使finish_time线的运行不早于p1p2工序的完成。我们需要在start()函数调用后立即添加这段代码:

p1.join()
p2.join()

join()方法强制其他进程等待,直到调用它的进程完成。以下是添加了 join 语句的输出:

休眠 0.5 秒
休眠 0.5 秒
休眠完毕
休眠完毕
程序在 0.568213340181392 秒内完成

图 7。现在,程序将等待并行作业完成,因为每个流程实例都调用了“join()”。作者创建的示意图。

让我们看看更新后的程序流(图 7)。

通过类似的推理,我们可以运行更多的进程。下面是从上面修改的具有 10 个进程的完整代码:

import multiprocessing
import time

def sleep(sleep_sec=0.5):
    print(f'Sleeping for {sleep_sec} seconds')
    time.sleep(sleep_sec)
    print('Finished sleeping')

if __name__ == "__main__":
    start_time = time.perf_counter()
    processes = []

    # Creates 10 processes then starts them
    for i in range(10):
        p = multiprocessing.Process(target=sleep, args=(1.0,))
        p.start()
        processes.append(p)

    # Joins all the processes
    for p in processes:
        p.join()

    finish_time = time.perf_counter()

    print(f"Program finished in {finish_time - start_time):.3f} seconds")

睡 1.0 秒
睡 1.0 秒
睡 1.0 秒
睡 1.0 秒
睡 1.0 秒
睡 1.0 秒
睡 1.0 秒
睡 1.0 秒
睡 1.0 秒
睡 1.0 秒
睡完
睡完
睡完
睡完
睡完

进程结束,退出代码为 0

如果我们运行上面的代码片段,它调用了sleep(1.0)十次:如果没有多核处理,我们预计代码至少需要十秒钟。

ℹ️ Referring to the complete code above: notice that input arguments can be passed via keyword *args*, which expects a tuple. Also, notice that we loop and store each process in a list, which is better practice for there could be variable processes, opposed to hard-coding *p1, p2, ..., p10*.

2.锁

如上所述,进程是程序的运行实例,封装了每个 Python 程序(即,每个都是 Python 解释器的新实例)。MainProcess是当python <file>.py作为脚本运行时的流程名称——这是我们前面通过 schematics 看到的一个方面。此外,我们了解到子进程可以通过一个Process实例派生出来并同时运行。通常,这些并发程序在进程间共享数据或资源。互斥锁保护共享资源并防止竞争情况。

最常用的确保互斥的机制是互斥锁或互斥体,或简单的锁。互斥体是一种特殊类型的对象,在底层硬件中有支持。基本思想是每个关键部分都有锁保护。

——第 53 页,并行编程介绍,2020。

用于保护进程间的共享资源。它们允许多个作业无冲突地访问一个资源。此外,对于确保作业之间的数据一致至关重要。

允许代码断言,在锁被释放之前,没有其他进程可以执行类似的代码。因此,类的目的是双重的:(1)通过acquire()函数来声明锁;(2)通过release()功能解除锁定。

让我们吸取上面Process的教训。下面是要运行的完整代码:

# example of a mutual exclusion (mutex) lock for processes
import time
from random import random
from multiprocessing import Process
from multiprocessing import Lock

# work function
def sleep(lock, identifier, value):
    # acquire the lock
    with lock:
        print(f'>process {identifier} got the lock, sleeping for {value:.3f}')
        time.sleep(value)

# entry point
if __name__ == '__main__':
    # create the shared lock
    lock = Lock()
    # create a number of processes with different sleep times
    processes = [Process(target=sleep, args=(lock, i, random())) for i in range(10)]
    # start the processes
    for process in processes:
        process.start()
    # wait for all processes to finish
    for process in processes:
        process.join()

输出如下。

进程 0 获得锁,休眠 0.297
进程 1 获得锁,休眠 0.908
进程 2 获得锁,休眠 0.223
进程 3 获得锁,休眠 0.016
进程 4 获得锁,休眠 0.323
进程 5 获得锁,休眠 0.796
进程 6 获得锁,休眠 0。

进程结束,退出代码为 0

如果我们再次运行相同的代码会怎么样:

进程 0 得到锁,休眠 0.223
进程 1 得到锁,休眠 0.175
进程 2 得到锁,休眠 0.148
进程 3 得到锁,休眠 0.773
进程 4 得到锁,休眠 0.180
进程 5 得到锁,休眠 0.294
进程 7 得到锁

进程结束,退出代码为 0

注意第二次运行时 ID5ID7 之前,返回到 ID6 。这怎么可能?

我们可以开发一个例子,通过将上面的示例代码分成几部分来演示如何使用互斥锁。

首先,我们可以定义一个目标任务函数,它将一个锁作为参数,并使用锁来保护一个关键部分。

关键部分包括报告消息和几分之一秒的阻塞。

# work function
def sleep(lock, identifier, value):
    # acquire the lock
    with lock:
        print(f'>process {identifier} got the lock, sleeping for {value:.3f}')
        time.sleep(value)

接下来,我们可以创建一个Lock 实例供流程共享。

...
# create the shared lock
lock = Lock()

然后,我们可以创建许多流程来执行我们的 task() 函数,并竞争执行关键部分。

每个进程将接收共享锁、一个 0 到 9 之间的整数 ID 和一个 0 到 1 之间的随机睡眠时间(秒)作为输入。

正如上一节对完整代码所做的那样,我们可以通过列表理解来实现这一点,创建一个由十个已配置的Process实例组成的列表。

...
# create a number of processes with different sleep times
processes = [Process(target=task, args=(lock, i, random())) for i in range(10)]

接下来,我们可以开始所有的过程。

...
# start the processes
for process in processes:
    process.start()

最后,我们可以等待所有新的子进程终止。

...
# wait for all processes to finish
for process in processes:
    process.join()

下面列出了使用锁的完整示例。

代码从十个被配置为执行我们的自定义函数的进程开始。

然后启动子进程,主进程阻塞,直到所有子进程完成。

每个子进程试图获取sleep()函数中的。一次只有一个方法可以获取,一旦它们这样做了,它们就报告一条消息,包括它们的 id 和它们将休眠多长时间。然后,在释放锁之前,该进程会阻塞几分之一秒。

3.长队

允许进程相互通信。例如,数据可以放在一个队列中,当它可用时由另一个处理器处理,这允许我们将一个任务分解成可以同时处理的更小的部分。

from multiprocessing import Queue

colors = ['red', 'green', 'blue', 'black']
cnt = 1
# instantiating a queue object
queue = Queue()
print('pushing items to queue:')
for color in colors:
    print('item no: ', cnt, ' ', color)
    queue.put(color)
    cnt += 1

print('\npopping items from queue:')
cnt = 0
while not queue.empty():
    print('item no: ', cnt, ' ', queue.get())
    cnt += 1

将物品推入队列:
1 号物品红色
2 号物品绿色
3 号物品蓝色
4 号物品黑色

从队列中弹出项目:
项目编号:0 红色
项目编号:1 绿色
项目编号:2 蓝色
项目编号:3 黑色

4.泳池

池是用于并行执行任务的进程的集合。池有助于将一个巨大的任务分成多个处理器可以处理的小部分。

from multiprocessing import Pool

import time

work = (["A", 5], ["B", 2], ["C", 1], ["D", 3])

def work_log(work_data):
    print(" Process %s waiting %s seconds" % (work_data[0], work_data[1]))
    time.sleep(int(work_data[1]))
    print(" Process %s Finished." % work_data[0])

def pool_handler():
    p = Pool(4)
    p.map(work_log, work)

if __name__ == '__main__':
    pool_handler()

输出:

流程 A 等待 5 秒
流程 B 等待 2 秒
流程 C 等待 1 秒
流程 D 等待 3 秒
流程 C 完成。
流程 B 完成。
流程 D 完成。
流程一结束。

请注意,我们现在可以设置并行执行的工作线程数量:

def pool_handler():
    p = Pool(4)
    p.map(work_log, work)

Pool设置为具有nworkers=4,并且列表具有 4 个元素,这意味着同时执行每个组件。因此,请注意,打印语句显示每次修整的顺序与设置为sleep的秒数相同。

关于如何开始多重处理的提示

如果您是 Python 多重处理的新手,这里有一些技巧可以帮助您入门:

  1. 熟悉 Python 流程管理的基础知识。流程模块的文档可以帮助您理解如何启动和控制流程。
  2. 确保你知道锁的基本知识。模块提供了保护进程间共享资源所需的所有工具。
  3. 使用队列在进程间通信使得在作业间来回传递数据变得容易。
  4. 实验!学习如何使用多重处理的最好方法是尝试不同的技术,看看什么最适合您的需要。

使用多重处理的最佳实践技巧

当在项目中使用多重处理时,请记住一些最佳实践。以下是一些帮助您充分利用多重处理的技巧:

1.使用 CPU 密集型任务:并非所有作业都适合并行运行。然而,CPU 密集型作业非常适合多处理,因为它们可以分成更小的部分并同时运行。

2.限制共享资源的使用:如前所述,两个进程试图访问同一个资源会导致数据损坏或不一致。限制进程间共享资源的使用以避免任何潜在的冲突是很重要的。

3.小心使用锁:锁对于保护进程间的共享资源至关重要,但是小心使用它们也很重要。如果您需要更加小心的话,您可以很容易地创建死锁或其他同步问题。

4.使用队列进行通信:队列允许进程相互通信,这对于需要在多个任务之间进行协调的任务很有帮助。

5.彻底测试您的代码:在将它部署到生产环境之前进行测试总是一个好主意。多重处理会引入新的和不可预见的问题,所以在它们给应用程序带来问题之前捕捉任何潜在的问题是非常重要的。

结论

Python 中的多重处理是加速代码的强大工具。通过在多个处理器之间分配任务,您通常可以比在单个处理器上运行相同的任务更快地获得结果。在本文中,我们看了四个重要的 Python 多处理组件。我们还讨论了有效使用多处理的一些最佳实践。

将来,我们将探索 Python 中多处理的用例,例如消息传递和共享内存概念。因此,我们将返回到Queue并引入其他数据结构,使其能够相对快速地实现。

请对未来的博客提出建议,无论是多重处理还是另一个主题。

补充材料

在过去的关于智能指针的三部分系列文章中介绍了内存管理:

https://betterprogramming.pub/smart-pointers-in-cpp-708486276526 https://betterprogramming.pub/understanding-smart-pointers-in-cpp-6c3854593503 [## 现代 C++:智能指针的近距离观察

《理解智能指针》续集 betterprogramming.pub](https://betterprogramming.pub/understanding-smart-pointers-in-cpp-6c3854593503) https://betterprogramming.pub/understanding-smart-pointer-iii-909512a5eb05

接触

**Want to Connect?** Follow Dr. Robinson on [LinkedIn](https://www.linkedin.com/in/jrobby/), [Twitter](https://twitter.com/jrobvision), [Facebook](https://www.facebook.com/joe.robinson.39750), and [Instagram](https://www.instagram.com/doctor__jjj/). Visit my homepage for papers, blogs, email signups, and more!

https://www.jrobs-vision.com/

10 分钟内完成多线程和多处理

原文:https://towardsdatascience.com/multithreading-and-multiprocessing-in-10-minutes-20d9b3c6a867

Python 示例简化了多任务处理

默里·坎贝尔在 Unsplash 上拍摄的照片

多线程和多处理是实现多任务的两种方式(想想分布式计算!)在 Python 中。 多任务 在并行运行函数和代码时很有用,比如将数学计算分解成多个更小的部分,或者在一个 for 循环中拆分彼此独立的项。本文将介绍和比较多线程和多处理之间的区别,何时使用每种方法,以及如何在 Python 中实现它们。

更新 :本文是系列文章的一部分。查看其他“10 分钟内”话题 此处

目录

  1. 多线程与多处理
  2. 多线程作为 Python 函数
  3. 作为 Python 类的多线程
  4. 作为 Python 函数的多重处理

多线程与多处理

根据正式定义, 多线程 是指处理器并发执行多个线程的能力,其中每个线程运行一个进程。而 多处理 指的是系统并发运行多个处理器的能力,其中每个处理器可以运行一个或多个线程。

图 1:多线程与多处理的对比——作者图片

从上图中,我们可以看到在多线程中(中间的图),多个线程共享相同的代码、数据和文件,但运行在不同的寄存器和堆栈上。多重处理(右图)使单个处理器倍增,复制代码、数据和文件,这会产生更多开销。

多线程对于 IO 绑定进程 很有用,比如从网络或数据库读取文件,因为每个线程可以并发运行 IO 绑定进程。多重处理对于 CPU 受限的进程 很有用,例如计算量大的任务,因为它将受益于拥有多个处理器;类似于多核计算机比单核计算机工作得更快。

请注意,对 CPU 受限的进程使用多线程可能会降低性能,因为竞争资源确保一次只能执行一个线程,并且在处理多个线程时会产生开销。

另一方面,多重处理可以用于 IO 绑定的进程。然而,如上所述,管理多个进程的开销高于管理多个线程的开销。您可能会注意到,由于程序使用多个 CPU 内核,多处理可能会导致更高的 CPU 利用率,这是意料之中的。

作为 Python 函数的多线程

多线程可以使用 Python 内置库threading实现,并按以下顺序完成:

  1. 创建线程:每个线程都用它的参数标记到一个 Python 函数
  2. 开始任务执行
  3. 等待线程完成执行:有助于确保完成或“检查点”

在下面的代码片段中,实现了上面的步骤,以及一个线程锁(第 22 行)来处理竞争资源,这在我们的例子中是可选的。

有一些值得注意的观察

  • 第 12–15 行:进程在不同的线程(线程 ID)上运行,但是使用相同的处理器(进程 ID)
  • 第 8 行:如果在获取和释放锁之间实现time.sleep(sleep_duration),线程将按顺序运行,不会节省任何时间——您可以试试!

作为 Python 类的多线程

对于喜欢面向对象编程的用户来说,多线程可以作为一个继承自threading.Thread超类的 Python 类来实现。使用类而不是函数的一个好处是能够通过类对象共享变量。

将多线程实现为函数和类的区别在于第 1 步(创建线程),因为线程现在被标记为类方法而不是函数。调用t1.start()t1.join()的后续步骤保持不变。

import time

class Sleep(threading.Thread):
    def __init__(self, sleep_duration):
        self.sleep_duration = sleep_duration

    def sleep(self):
        time.sleep(self.sleep_duration)

if __name__ == "__main__":
    # Create thread
    sleep_class = Sleep(2)
    t1 = threading.Thread(target=sleep_class.sleep)

作为 Python 函数的多重处理

多重处理可以通过 Python 内置库multiprocessing使用两种不同的方法实现——进程和池。

进程 方法类似于上面的多线程方法,其中每个进程都用其参数标记为一个函数。在下面的代码片段中,我们可以看到多处理比多线程花费的时间更长,因为运行多个处理器的开销更大。

Pool 方法允许用户定义工作进程的数量,以先进先出的调度方式将所有进程分配给可用的处理器,自动处理进程调度。池方法用于使用mapstarmap(第 19 行)将一个函数分成多个小部分——用不同的输入参数运行同一个函数。而 Process 方法用于运行不同的功能。

希望本文介绍了多线程和多重处理的概念,以及何时使用每种方法。Python 示例是可以用您的函数替换的框架代码片段,您已经准备好了!

感谢您的阅读!如果你喜欢这篇文章,请随意分享。

相关链接

https://docs.python.org/3/library/threading.html】Python 文档:T2

multiprocessing Python 文档:https://docs.python.org/3/library/multiprocessing.html

Python 中的多线程和多处理

原文:https://towardsdatascience.com/multithreading-multiprocessing-python-180d0975ab29

深入探讨 Python 中的多线程和多处理,以及它们与并发性和并行性的关系

奥斯曼·拉纳在 Unsplash 上拍摄的照片

介绍

线程和多处理是编程中两个最基本的概念。如果你已经写了一段时间的代码,你应该已经遇到过一些用例,在这些用例中,你想要在你的代码的某些部分中加速特定的操作。Python 支持各种机制,使得各种任务能够(几乎)同时执行。

在本教程中,我们将理解多线程多处理,并在实践中了解如何在 Python 中实现这些技术。我们还将根据应用程序是由 I/O 还是 CPU 绑定来讨论使用哪种技术。

在讨论线程和多处理之前,理解这两个经常互换使用的术语很重要。并发并行是密切相关截然不同的概念。

并发性和并行性

在许多情况下,我们可能需要加速代码库中的一些操作,以提高执行的性能。这通常可以通过并行或并发执行多个任务来实现(即,通过在多个任务之间交错)。您能否利用并发性或并行性实际上取决于您的代码,但也取决于运行它的机器。

并发执行中,两个或多个任务可以在重叠的时间段内开始、执行和完成。因此,这些任务不一定要同时运行——它们只需要以重叠的方式取得进展。

并发性:至少有两个线程正在进行的情况。一种更通用的并行形式,可以将时间片作为虚拟并行的一种形式。

孙的多线程编程指南

现在让我们考虑一个用例,我们有一台带有单核 CPU 的计算机。这意味着需要作为应用程序的一部分执行的任务不能在完全相同的时间进行,因为处理器一次只能处理一个任务。并发运行多个任务意味着处理器执行上下文切换,以便多个任务可以同时进行。

并发性的主要目标之一是当其中一个任务被迫等待(比如等待来自外部资源的响应)时,通过来回切换来防止任务相互阻塞。例如,任务 A 进行到某个点时,CPU 停止处理任务 A,切换到任务 B 并开始处理一段时间,然后它可以切换回任务 A 完成它,最后返回到任务 B,直到它也完成这个任务。

下图显示了一个由两个任务组成的应用,这两个任务在一个内核中同时执行。

并发执行—来源:作者

另一方面,在并行性中,多个任务(或者甚至一个任务的几个组件)实际上可以同时运行(例如,在一个多核处理器或者一台有多个 CPU 的机器上)。因此,在具有单个处理器和单核的机器上不可能有并行性。

并行性:当至少两个线程同时执行时出现的情况。

孙的多线程编程指南

借助并行技术,我们能够最大限度地利用硬件资源。考虑一下我们有 16 个 CPU 内核的情况,启动多个进程或线程来利用所有这些内核可能比只依赖一个内核而其余 15 个内核处于空闲状态更明智。

在多核环境中,每个内核可以同时执行一项任务,如下图所示:

平行——来源:作者

概括来说,并发性可以被视为系统或程序的属性,指的是单个 CPU(内核)如何在看似的同时(即并发地)处理多个任务,而并行性是实际的运行时行为,即几乎同时并行执行至少两个任务。此外,需要强调的是在任务执行过程中,并发性和并行性可以结合在一起。事实上,我们可以有各种各样的组合;

  • 既不并发,也不并行:这也称为顺序 执行 任务严格地一个接一个地执行。
  • 并发,但不并行:这意味着任务看似同时进行但实际上系统在并发进行的各种任务之间切换,直到全部执行完毕。因此,没有真正的并行性,因此没有两个任务在完全相同的时间被执行。
  • 并行,但不并发:这是一个相当罕见的场景,在任何给定的时间只有一个任务被执行,但任务本身被分解成并行处理的子任务。但是,每一个任务都必须在下一个任务被拾取和执行之前完成。
  • 并发和并行:这可能以两种方式发生;第一种是简单的并行和并发执行,应用程序启动多个线程在多个 CPU 和/或内核上执行。实现这一点的第二种方式是当应用程序能够同时处理多个任务,但同时它也将每个单独的任务分解成子任务,以便这些子任务最终可以并行执行。

并行与并发——来源:作者

现在,我们已经对并发和并行的工作原理有了基本的了解,让我们使用 Python 中的一些例子来探索多处理和多线程。

Python 中的线程

线程是在进程上下文中执行的一系列指令。一个进程可以产生多个线程,但是所有的线程都将共享相同的内存。

当在 Python 中对 CPU 受限的任务进行多线程实验时,您最终会注意到执行并没有得到优化,甚至在使用多线程时会运行得更慢。通常,在多核机器上使用多线程代码应该能够充分利用可用的内核,从而提高整体性能。

事实上,Python 进程不能并行运行线程,但它可以在 I/O 绑定操作期间通过上下文切换来并发运行它们。

这一限制实际上是由 GIL 实施的。Python 全局解释器锁 (GIL)防止同一进程中的线程同时执行。

GIL 是一个互斥体,保护对 Python 对象的访问,防止多个线程同时执行 Python 字节码— Python Wiki

GIL 是必要的,因为 Python 的解释器不是线程安全的。每当我们试图访问线程中的 Python 对象时,这个全局锁就会被强制执行。在任何给定时间,只有一个线程可以获取特定对象的锁。因此,受 CPU 限制的代码不会因为 Python 多线程而获得性能提升。

CPython 实现细节:

在 CPython 中,由于全局解释器锁,一次只有一个线程可以执行 Python 代码(尽管某些面向性能的库可能会克服这一限制)。

如果你想让你的应用更好的利用多核机器的计算资源,建议你使用[*multiprocessing*](https://docs.python.org/3/library/multiprocessing.html#module-multiprocessing)或者[*concurrent.futures.ProcessPoolExecutor*](https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.ProcessPoolExecutor)

您可以在我最近的一篇文章中阅读更多关于 Python 的全局解释器锁的内容,但是现在提供的信息应该足以理解全局解释器锁如何限制 Python 中多线程应用程序的能力(以及潜在的为什么它是“必需的”)。

现在,我们已经了解了 Python 中多线程应用程序的工作原理,让我们编写一些代码并利用这一技术。

在 Python 中,线程可以通过使用[**threading**](https://docs.python.org/3/library/threading.html)模块来实现。现在让我们考虑一个用于下载图像的函数——这显然是一个 I/O 绑定的任务:

示例 CPU 绑定函数—来源:作者

然后,让我们尝试使用下面的代码片段从 Unsplash 下载一些图像。请注意,为了更清楚地展示线程化的效果,我们有意尝试下载这些图像 5 次(参见for循环):

使用 Python 从 Unsplash 下载图像(I/O 绑定任务)—来源:作者

因此,我们的小应用程序运行良好,但我们肯定可以做得更好,并通过利用线程优化代码(不要忘记,下载多个图像是一个 I/O 绑定的任务)。

从带线程的 Unsplash 下载图像—来源:作者

概括地说,Python 中的线程化允许在单个进程中创建多个线程,但是由于 GIL,它们不会完全同时运行。在并发运行多个 I/O 绑定任务时,线程仍然是一个非常好的选择。现在,如果您想要利用多核机器上的计算资源,那么多处理是一条可行之路。

您还应该注意到,线程化伴随着与管理线程相关的开销,因此您应该避免将它们用于基本任务。此外,它们还增加了程序的复杂性,这意味着调试可能会变得有点棘手。因此,只有在有明确价值时才使用线程。

Python 中的多重处理

现在,如果我们想要利用多核系统并最终在真正并行的环境中运行任务,我们需要执行多处理而不是多线程。

在 Python 中,可以使用[**multiprocessing**](https://docs.python.org/3/library/multiprocessing.html)模块( [*concurrent.futures.ProcessPoolExecutor*](https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.ProcessPoolExecutor))来实现多处理,该模块可用于生成多个操作系统进程。因此,Python 中的多重处理避开了 GIL 和由此产生的限制,因为现在每个进程都有自己的解释器,从而拥有自己的 GIL。

[*multiprocessing*](https://docs.python.org/3/library/multiprocessing.html#module-multiprocessing)是一个支持使用类似于[*threading*](https://docs.python.org/3/library/threading.html#module-threading)模块的 API 来生成进程的包。

[*multiprocessing*](https://docs.python.org/3/library/multiprocessing.html#module-multiprocessing)包提供了本地和远程并发,通过使用子进程而不是线程,有效地避开了全局解释器锁

因此,[*multiprocessing*](https://docs.python.org/3/library/multiprocessing.html#module-multiprocessing)模块允许程序员充分利用给定机器上的多个处理器。它可以在 Unix 和 Windows 上运行。

在上一节中,我们讨论了线程化,我们看到线程化根本不能改善 CPU 受限的任务。这可以通过使用多重处理来实现。让我们使用与前一节相同的函数append_to_list(),但这次我们将使用multiprocessing而不是threading,以便利用我们的多核机器。

现在让我们考虑一个 CPU 受限的操作,它涉及一个将多个随机整数追加到一个列表中的函数。

受 CPU 限制的任务——来源:作者

现在让我们假设我们想要运行这个函数两次,如下所示:

没有多重处理的 CPU 受限任务——来源:作者

让我们time这个执行并检查结果。

现在让我们稍微重构一下我们的代码,现在使用两个不同的进程,以便每个函数调用都在它自己的进程中执行:

CPU 受限任务的多重处理——来源:作者

最后让我们来看一下time以及执行和检查结果:

我们可以清楚地看到(尽管usersys时间保持大致相同),但是real时间下降了一倍,甚至比两倍还多(这是意料之中的,因为我们实际上将负载分配给了两个不同的进程,以便它们可以并行运行)。

总的来说,当我们需要利用多核系统的计算能力时,可以使用 Python 中的多处理。事实上,多处理模块允许您在并行中运行多个任务和进程。与线程相比,multiprocessing通过使用子进程而不是线程来避开 GIL,因此多个进程可以同时运行。这种技术最适合 CPU 密集型任务。

最后的想法

在今天的文章中,我们介绍了编程中的两个最基本的概念,即并发性和并行性,以及它们在执行时如何区别甚至组合。此外,我们还讨论了线程和多处理,探讨了它们的主要优点和缺点,以及一些最终可以帮助您了解何时使用其中一个的用例。最后,我们展示了如何用 Python 实现线程或多处理应用程序。

概括一下,

穿线

  • 线程共享相同的内存,可以读写共享的变量
  • 由于 Python 全局解释器锁,两个线程不会同时执行,而是并发执行(例如上下文切换)
  • 对受 I/O 限制的任务有效
  • 可以用[threading](https://docs.python.org/3/library/threading.html#module-threading)模块实现

多处理

  • 每个进程都有自己的内存空间
  • 每个进程可以包含一个或多个子进程/线程
  • 由于进程可以在不同的 CPU 内核上运行,因此可以通过利用多核机器来实现并行性
  • 对 CPU 受限的任务有效
  • 可以用[multiprocessing](https://docs.python.org/3/library/multiprocessing.html#module-multiprocessing) 模块(或[concurrent.futures.ProcessPoolExecutor](https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.ProcessPoolExecutor))实现

成为会员 阅读介质上的每一个故事。你的会员费直接支持我和你看的其他作家。你也可以在媒体上看到所有的故事。

https://gmyrianthous.medium.com/membership

你可能也会喜欢

文本、分类和数字数据的多元匹配引擎

原文:https://towardsdatascience.com/multivariable-matching-engine-on-textual-categorical-and-numerical-data-d90b0dca7f4c

照片由 Pixabay 拍摄

发现嵌入和相似性的力量

如今,许多公司不得不在其活动中处理类似的案件。他们中的少数人利用旧的和以前的工作,而绝大多数人从头再来。

在本文中,我们将发现如何利用人工智能算法来构建一个匹配引擎,该引擎可以使用文本、分类和数字变量。

为简单起见,我们将考虑一个案例由以下数据描述:

  • Num :数值型变量
  • 类别:分类变量(一个单词)
  • 已发送:文本变量(1 个句子-需要嵌入)
  • 段落:文本变量(段落-需要嵌入)

如果您的数据库不遵循相同的结构,您可以应用预处理管道来实现相同的场景。

目录

摘要如下:

1-嵌入

2-相似性
3-匹配引擎

嵌入

单词嵌入

单词嵌入是在大小为 N 的向量空间中表示单词的艺术,其中语义相似的单词,从数学上来说,彼此接近。
在处理文本数据时,这是一个重要的步骤,因为 ML 算法主要处理数值。

作者图片

单词嵌入算法有 Word2VecFastTextBERT 、…,在不同语言中有几种改编。

句子嵌入

一个句子是一系列单词。也就是说,我们可以按照以下步骤聚合单词嵌入来生成句子嵌入:

作者图片

其中:

作者图片

  • NB1:在清理阶段,我们可以只保留要嵌入的关键字
  • NB2:聚合公式可以平衡长句和短句
  • NB3:句子嵌入的大小与单词嵌入的大小相同

段落嵌入

鉴于段落是一系列句子的事实,我们可以使用与上面相同的方法:

作者图片

  • NB1:在普通语言中,句子通常用字符串“.”来分隔,但在某些情况下可能需要定制。
  • NB2:段落嵌入的大小是(N,k ),其中 N 是句子嵌入的大小,k 是段落中句子的数量。

类似

数字相似性

让我们考虑两个数值 x、y 和 NumSim,两者之间的距离定义如下:

作者图片

如果:

  • x=y => NumSim(x,y)=1
  • x>>y => NumSim(x,y) ≈1/(|x|+1)≈0

词汇相似性

有许多方法可以计算两个单词之间的相似度,下面我们列举两种最著名的方法:

  • 近似字符串匹配(基于字符):Levenshtein 的距离,…
  • 语义串匹配(基于嵌入):余弦相似度、欧几里德距离、…

在本文中,我们将主要关注余弦相似度,它只不过是两个比较单词的两个嵌入向量之间的角度的余弦:

作者图片

其中:

作者图片

句子相似性

我们遵循相同的方法,这次使用上面定义的句子嵌入算法:

作者图片

其中:

作者图片

相似之处

这一段的相似之处稍微复杂一些,因为它们的嵌入被认为是 2D 矩阵而不是向量。为了比较这两个段落,我们计算每个对等句子的 SentSim,并生成 2D 相似度矩阵,然后将该矩阵汇总为一个分数:

作者图片

其中:

作者图片

匹配引擎

如前所述,让我们考虑一个业务案例,其中我们必须处理许多案例。每种情况都用四个变量描述: Num、Categ、Sent 和 Parag (见简介)。
我们可以利用历史数据将每个新案例与整个数据库进行比较,并使用下图选择最相似的案例:

作者图片

其中:

作者图片

我们对从 1 到 L(数据库的大小)的所有情况执行这些操作,并输出最高的匹配分数。

  • NB1:如果 Categ 变量具有唯一且稳定的模态,我们可以用简单的过滤代替匹配
  • NB2:如果每种类型有不止一个变量,可以使用正确的相似性方法将它们堆叠到同一个引擎中
  • NB3:每个变量的权重根据其业务优先级和重要性进行选择

结论

匹配引擎在科技界非常流行和使用。它们为企业提供了更快的处理工具,节省了时间和资金。
在本文中,我们关注的是特定类型的输入(数字和文本),但是您也可以想象使用一个图像作为我们案例的额外描述。这需要使用 CNN 技术,例如暹罗网络,其生成与余弦相似性一起使用的图像嵌入,以生成额外的匹配分数。

基于生长神经气体和谱聚类的多元时间序列聚类

原文:https://towardsdatascience.com/multivariate-time-series-clustering-using-growing-neural-gas-and-spectral-clustering-adc83680639f

一种基于图论的时间序列聚类方法

聚类是一种无监督的方法,有很多应用领域。它也可以应用于时间序列,尽管它不像时间序列的预测和异常检测那样流行。重点是分析整个系统,该系统有许多不同的 KPI 随时间流动,并将系统的行为表示为状态,并将相似的行为聚集在一起。换句话说,获取特定时间段的系统快照,并检测变化模式。

在这篇文章中,我提到了一种新颖的实现方法,它将生长神经气体(GNG)和谱聚类结合起来用于这个目的。

艾伯·布朗在 Unsplash 上的照片

场景定义和数据集检查

让我们假设一个系统由几个设备组成,每个设备由 100 个不同的 KPI 表示,这些 KPI 随时间流动,换句话说,多变量时间序列用于确定系统的总体概况。目标是检测系统的不同行为,并沿着定义的时间段对它们进行聚类。类似地,我们也可以洞察我们遇到的异常情况。为了保持较小的数据量和简单快速的计算,我使用了一个简化的数据集。为了澄清这一点,请看一下图 1 中数据集的简化版本。您甚至可以通过忽略“设备”列并研究随时间推移会创建多个 KPI 的单个设备,将此数据集转换为更简化的数据集。

为了不失去对研究的关注,我简单地假设数据已经过预处理,这意味着丢失的值已经得到处理,并且数据已经过规范化。

图片由作者提供—图 1

1-应用 GNG

GNG 主要用于学习图形格式数据的拓扑结构。GNG 算法是由 Fritzke 于 1995 年首先提出的。在原论文中,基本说明了它的工作原理。这是一种简单的无监督神经网络方法,类似于自组织映射(SOMs) 。然而,GNG 和 SOM 都不包括常见的神经网络结构和步骤,如层、前馈和反向传播。类似地,GNG 和 SOM 中的权重概念基本上对应于节点在空间中的位置,并由具有特征大小的向量来表示。

它们都是迭代方法,并且在其迭代期间具有相似的最佳匹配单元/最近单元推理。然而,它们在实现上也有基本的区别。与 SOM 不同的是,在 GNG 中,您不需要在流程开始时初始化节点,这些节点是通过迭代动态确定的,这就是“增长”的来源。GNGs 试图将数据包含到拓扑中,在拓扑中,模型在每次迭代中表现最差。通过这种方式,它们不会均匀增长,也不会覆盖整个数据。gng 通常也用于从图像生成有趣的动画。我利用 NeuPy 包来实现 python 中的 GNG,它提供了一个简单的抽象。

您可以在下面找到一个简单的介绍性代码片段,它为我们的数据组成了一个简单的 GNG 模型。直接影响模型的性能和准确性的一个重要参数是节点的数量,换句话说,就是数据表示的分辨率。要找到节点计数的最佳数量并不那么简单,在我们的例子中,您可能只需要根据唯一的设备数量和工作时间窗口实现一些简单的推理。注意,在 Neupy 包中为 GNG 模型实现的属性提供了对数据拓扑结构的洞察。

from neupy import algorithms
import pandas as pd
import numpy as np
from sklearn.neighbors import KNeighborsClassifier
from sklearn.cluster import KMeans
from scipy import statsgng = algorithms.GrowingNeuralGas(
 n_inputs=self.n,
 n_start_nodes=5,
 shuffle_data=True,
 verbose=True,
 step=0.1,
 neighbour_step=0.01,
 max_edge_age=50,
 max_nodes=100,
 n_iter_before_neuron_added=100,
 after_split_error_decay_rate=0.5,
 error_decay_rate=0.995,
 min_distance_for_update=0.05
 )
epoch = 100
gng.train(df_scaled_data, epochs=epoch) # only including features, features are named as '0', '1', '2' ...
g = gng.graph
node_list = g.nodes

在构建了 GNG 模型之后,我们还有一个问题需要补充。也就是找出哪个样本匹配到哪个节点。我们可以简单地利用具有 n_neighbors=1 参数的 KNN 算法来将样本与最近的节点进行匹配。

number_of_features = 92 # we have 92 different KPIs in our data
node_koor_node = []
for node in gng.graph.nodes:
 row = node.weight[0].tolist()
 row.append(node_list.index(node))
 node_koor_node.append(row)df_train = pd.DataFrame(node_koor_node)
df_test = pd.DataFrame(df_scaled_data)X_train = df_train.iloc[:, 0:number_of_features] 
y_train = df_train.iloc[:, number_of_features]
X_test = df_test.iloc[:, 0:number_of_features]knn = KNeighborsClassifier(n_neighbors=1)
knn.fit(X_train, y_train)
knn_result = knn.predict(X_test)scaled_sample_node_matching = []
for i, data in enumerate(df_scaled_data):
 row = data.tolist()
 node_idx = knn_result[i]
 row.append(node_idx)
 scaled_sample_node_matching.append(row)df_scaled_sample_node_matching = pd.DataFrame(scaled_sample_node_matching)
df_scaled_sample_node_matching = df_scaled_sample_node_matching.rename(columns={number_of_features: “node”})df_node_sample_match = pd.merge(df_static_cols, df_scaled_sample_node_matching, left_index=True,
                   right_index=True) # df_static_cols includes static columns and datetime

2-包括时间行为

好的,到目前为止还不错,但是我们忽略了一些重要的东西。这是关于时间序列的。GNG 没有考虑数据中的时间行为,换句话说,样本之间的时间依赖性。我们必须在数据中包含时间行为。出于这个原因,我们简单地忽略了所有的边在前面的步骤中建立的 GNG,而保持节点和他们在空间中的位置。在我们的例子中,通过考虑相同器件的连续时间周期之间的转换来重新计算边缘。为了澄清这一点,请看下图。

作者图片—图 2

在上一步中,我们确定了分布在数据空间中的节点及其包含的所有样本。换句话说,我们知道哪些设备在什么时间位于哪个节点上。通过利用这些信息,我们还能够组成邻接矩阵,向我们显示节点之间的转换。邻接矩阵是图结构的一个非常重要的表示。在我们的例子中,我们不构成仅由 0 和 1 组成的二元邻接矩阵。我们计算节点之间转移的概率(换句话说,接近程度),将该值保存在邻接矩阵的每个单元中。在这个意义上,我们构造了一个加权邻接矩阵。它也被称为亲和矩阵。

此外,我们还构建了用新边更新的图的度矩阵。度矩阵在除对角线以外的每个单元中包括零,并且基本上指定每个节点的度,从对应的节点出去多少条边。

device_list = df_node_sample_match['DEVICE'].unique()
n = len(gng.graph.nodes)
mat_transition = np.zeros((n, n))
previous_node = -1for device in device_list:
    df_device = df_node_sample_match.loc[df_node_sample_match['DEVICE'] == device]
    df_device = df_device.sort_values('DATETIME') for i, row in df_device.iterrows():
        current_node = row['node']
        if (previous_node != -1):
            mat_transition[previous_node, current_node] = mat_transition[previous_node, current_node] + 1
        else:
            print('It is the starting node, no transaction!')
        previous_node = current_node
    previous_node = -1df_adjacency = pd.DataFrame(mat_transition)df_adjacency['sum_of_rows'] = df_adjacency.sum(axis=1)
list_degree = df_adjacency['sum_of_rows'].tolist()
degree_matrix = np.diag(list_degree)# calculating transition probability
df_affinity = df_adjacency.loc[:, :].div(df_adjacency["sum_of_rows"], axis=0)
df_affinity = df_affinity.drop(["sum_of_rows"], axis=1)
df_affinity = df_affinity.fillna(0)

3-应用谱聚类

谱聚类是一种降维技术,主要是将高维数据集聚类成更小的相似聚类。它的推理来自于图论,目的是根据拓扑中的连通性来检测接近的节点,并分别对它们进行聚类。它也适用于不同的数据形式。谱聚类的主要步骤如下:

谱聚类的主要步骤,来源

为了更好地理解谱聚类,请看一下 K-means 和谱聚类在图 3 中下面的圆形数据上的行为。谱聚类利用拉普拉斯矩阵(图拉普拉斯)的谱(特征向量),该谱是通过使用亲和度矩阵和度矩阵计算的。注意,我们已经在上一步中计算了两个矩阵。所以,我们准备好看拉普拉斯矩阵了。

使用两种不同方法对圆形数据进行聚类,图片由作者提供—图 3

而拉普拉斯矩阵可以用等式 L = D - A 来计算;其中 D 是度矩阵,而 A 是邻接矩阵(在我们的情况下是亲和矩阵),归一化拉普拉斯矩阵可以被定义为以下等式:

归一化拉普拉斯方程,来源

对于我们的问题,我们更喜欢使用归一化拉普拉斯算子而不是拉普拉斯矩阵。可以借助下面的代码片段进行计算。

I = np.identity(df_affinity.shape[0])
sqrt = np.sqrt(degree_matrix)
D_inv_sqrt = np.linalg.inv(sqrt)
normalised_laplace = I — np.dot(D_inv_sqrt, df_affinity).dot(D_inv_sqrt)
df_normalised_laplace = pd.DataFrame(normalised_laplace)

作为第二步,我们确定具有对应的最小 k 特征值的特征向量,其中 k 是指聚类的数量。调整最佳聚类数并不简单。你可以找到一个解释的方法来确定它这里。归一化拉普拉斯矩阵的第二特征值给出了对图的连通性的洞察。在我们的例子中,我们选择它为 10。为了找到图拉普拉斯的特征向量和特征值,我们利用了奇异值分解(svd) 技术。另一种可能是使用 NumPy 包的 eigh 方法。

number_of_nodes = df_affinity.shape[0]
cluster_number = 10
u, s, vN = np.linalg.svd(normalised_laplace, full_matrices=False)
vN = np.transpose(vN)
eigen_vecs = vN[:, number_of_nodes — cluster_number:number_of_nodes] # 10 eigenvectors with smallest 10 eigenvalues

作为最后一步,组成的特征向量子集可用于对数据进行聚类,只需输入 k-means 。聚类之后,我们将节点与相应的聚类进行匹配。

# Clustering
kmeans = KMeans(n_clusters=cluster_number, max_iter=1000, random_state=0)
kmeans.fit(eigen_vecs)
labels = kmeans.labels_
cluster_centers = kmeans.cluster_centers_# node-cluster matching
clusters = dict()
for i, n in enumerate(node_list):
 if labels[i] not in clusters:
    clusters[labels[i]] = list()
 clusters[labels[i]].append(i)

4-确定异常群集和根本原因

集群是最终目的地吗?不太可能。一个可能的问题是定义异常集群。另一种说法是找出设备和相应的时间在我们的情况下以异常的方式表现。此外,这种异常的原因也是一个问题。很明显,小星团往往是异常的。以这种方式,例如,我们可以得出结论,表示小于整个数据的 10%的聚类是异常聚类。我们预计少数几个聚类将覆盖大部分数据。对我们来说这是一个开放的研究领域,仍然在研究。更先进的技术比基于规则的方法更受欢迎。此外,领域专家还可以解释聚类结果,并对异常情况发表意见。

在定义异常集群之后,能够检测这些异常的根本原因将是研究的一个资产。此时,异常群集和正常群集之间的每个 KPI 分布的差异出现在表中。为了测量和排序两种分布之间的差异,您可以利用 Kolmogorov-Smirnov 测试。你也可以在这里找到不同的选项。对于异常聚类中样本的每个 KPI(特征),我们只需应用 Kolmogorov-Smirnov 检验来找到与正常聚类的相应 KPI 的相似性。特征越不相似,就越有可能是异常的原因。

# We somehow determine the anomaly clusters
anomaly_clusters_list = [3, 4, 5, 7, 8, 9]
normal_cluster_list = [0, 1, 2, 6]node_cluster_matching = []
for i in range(len(node_list)):
 for j in range(cluster_number):
   if i in clusters[j]:
     node_cluster_matching.append([i, j])df_node_cluster_matching = pd.DataFrame(node_cluster_matching)
df_node_cluster_matching.columns = [‘node’, ‘cluster’]
df_sample_node_cluster = pd.merge(df_node_sample_match, df_node_cluster_matching, on=[‘node’], how=’inner’)list_ = np.arange(0, number_of_features, 1).tolist()
list_ = [str(i) for i in list_]
list_.append(‘cluster’)df_anomaly_clusters_data = df_sample_node_cluster.loc[
 df_sample_node_cluster[‘cluster’].isin(anomaly_clusters_list)]
df_anomaly_clusters_data = df_anomaly_clusters_data.loc[:, df_anomaly_clusters_data.columns.isin(list_)]
df_anomaly_clusters_data = df_anomaly_clusters_data.reset_index(drop=True)df_normal_clusters_data = df_sample_node_cluster.loc[
 df_sample_node_cluster[‘cluster’].isin(normal_cluster_list)]
df_normal_clusters_data = df_normal_clusters_data.loc[:, df_normal_clusters_data.columns.isin(list_)]
df_normal_clusters_data = df_normal_clusters_data.reset_index(drop=True)dict_of_cluster_feature_pvaluelist = dict()
for c in anomaly_clusters_list:
 filtered_anomaly_data = df_anomaly_clusters_data.loc[df_anomaly_clusters_data[‘cluster’] == c]
 pvalue_feature_list = []
 for i in range(number_of_features):
 anomaly_clusters_data_feature = filtered_anomaly_data[str(i)].to_numpy().tolist()
 normal_clusters_data_feature = df_normal_clusters_data[str(i)].to_numpy().tolist()
 result = stats.ks_2samp(anomaly_clusters_data_feature, normal_clusters_data_feature)
 print(‘Result of cluster ‘ + str(c) + ‘ feature ‘ + str(i))
 print(result)
 pvalue = result.pvalue
 if pvalue <= 0.05:
 pvalue_feature_list.append([i, pvalue])
 pvalue_feature_list.sort(key=lambda x: x[1])
 pvalue_feature_list = pvalue_feature_list[0: 3] # lets say we pick top 3 probable features for root cause 
 dict_of_cluster_feature_pvaluelist[‘c’ + str(c)] = pvalue_feature_list

目前就这些。感谢 Sezin Gürkan、Ersin Aksoy 和 Amir Yavariabdi,我非常感谢他们对这项研究做出的宝贵贡献。

有用的链接

https://medium.com/starschema-blog/growing-neural-gas-models-theory-and-practice-b63e5bbe058d https://math.stackexchange.com/questions/1113467/why-laplacian-matrix-need-normalization-and-how-come-the-sqrt-of-degree-matrix/1113572 https://www.mygreatlearning.com/blog/introduction-to-spectral-clustering/#WhatisSpectralclustering https://math.stackexchange.com/questions/573888/how-do-i-compute-the-eigenvectors-for-spectral-clustering-from-a-singular-value https://stats.stackexchange.com/questions/314046/why-does-andrew-ng-prefer-to-use-svd-and-not-eig-of-covariance-matrix-to-do-pca https://machinelearningmastery.com/singular-value-decomposition-for-machine-learning/ https://stackoverflow.com/questions/24913232/using-numpy-np-linalg-svd-for-singular-value-decomposition

基于深度学习的多元时间序列预测

原文:https://towardsdatascience.com/multivariate-time-series-forecasting-with-deep-learning-3e7b3e2d2bcf

用 LSTM 网络进行时间序列预测并解释结果

照片由德鲁·比默Unsplash 上拍摄

预测,对未来做出预测,在任何想要保持业务成功的公司的决策过程中起着关键作用。这是因为明天的成功取决于今天的决策,而决策是基于预测的。因此,良好的预测是至关重要的,例如,预测销售以更好地规划库存,预测经济活动以通知业务发展决策,甚至预测组织内的人员流动以改进人员规划。

在这篇文章中,我们展示了如何利用多个历史时间序列结合递归神经网络(RNN),特别是长短期记忆(LSTM)网络1,来预测未来。此外,我们使用基于 DeepLIFT [4][5]的方法来解释结果。

我们选择这种建模方法是因为它在传统方法不适用的环境中提供了最先进的性能。特别是,当时间序列数据复杂时,即趋势和季节模式随时间变化时,像 LSTM 网络这样的深度学习方法是 ARMA(自回归移动平均)等更传统方法的可行替代方法[2]。LSTM 模型最初是为自然语言处理(NLP)任务开发的,现在已经进入了时间序列预测领域,因为与文本一样,时间序列数据是按顺序出现的,序列不同部分之间的时间关系对于确定预测结果很重要。

此外,我们希望通过找到对预测贡献最大的重要特征来揭示经过训练的神经网络。

我们使用的示例是根据价格本身的历史时间序列以及其他特征(如交易量和日期衍生特征)来预测比特币的未来价格。我们选择这个数据是因为它体现了个人投资者在决定购买或出售资产时所做决策的动态变化和行为方面。这些方面也确实出现在其他预测问题中,如引言中提到的那些问题。这篇文章只是为了教育目的而写的,你不应该把它看作是投资建议。

数据探索和准备

数据集由 2016 年 7 月至 2021 年 7 月的每日价格(包括开盘价、最高价、最低价、收盘价,均以美元报价)和成交量(以美元计算的每日交易量)组成。

下图显示了每日收盘价的时间序列(图 1)。您已经可以看到时间序列的特点是频繁的峰值、低谷以及不断变化的(局部)趋势。

图 1:每日比特币价格[图片由作者提供]

时间序列平稳性

传统的统计预测技术要求数据是稳定的,即具有恒定的均值、标准差和自相关性。然而,如果不满足平稳性的条件,预测技术,如 ARMA,不能模拟数据随时间的相关性结构,因此必须使用其他技术。

让我们仔细看看我们的例子,并做一些视觉检查。简单地看一下上面的价格时间序列(图 1),你可以看到它并不是平稳的,因为平均值是随着时间变化的。典型的时间序列问题,下一步将是转换时间序列,试图使其平稳。我们采用价格水平的一阶百分比差来获得每日价格变化。此外,我们计算每日价格随时间变化的滚动平均值和滚动标准差。在图 2 中,你可以看到每日价格变化的均值和标准差都不是恒定的,因此时间序列不是稳定的。

图 2:每日价格变化(a .),价格变化的 90 天滚动平均值(b .),价格变化的 90 天滚动标准差(c .)[图片由作者提供]

时间序列表现出强烈的、不规则的动态性,这是传统预测技术不太适合的。另一方面,rnn,尤其是 LSTM 模型,已经证明可以很好地处理更复杂的时间序列,正如在许多 Kaggle 比赛中看到的那样,这就是我们关注它们的原因。

特征生成和准备

我们从数据集中获得了另外两个特征,包括作为日内价格变动衡量标准的高低点之间的百分比差异,以及作为隔夜价格变动衡量标准的第二天开盘和收盘之间的百分比差异。此外,我们从日期列派生出三个特性,包括星期几、月份和季度,以帮助预测我们的目标特性,即收盘价(Close)。

鉴于我们的金融时间序列数据相对干净和结构化,我们不必花太多时间清理和准备数据。准备步骤包括将我们的数据集分为训练集和测试集,以及将所有要素重新调整到介于 0 和 1 之间的通用比例,这有助于防止要素处于不同比例时模型过度拟合。通常,我们会在分析结束时保留一个用于模型评估的保留数据集,但在我们的情况下,观察模型在测试数据集上的性能就足够了。

多元时间序列预测

接下来,我们致力于建立一个时间序列预测模型,该模型可以将多个变量(及其各自的历史)作为输入,以预测未来的价格。

目标

在我们深入建模方面之前,有必要确定一个与业务目标一致的目标(或成本)函数,以确保模型能够实际帮助实现期望的业务结果。目标是创建一个预测模型,尽可能准确地预测价格,同时优先考虑更持久的价格变动(如每周趋势),而不是更小、更易变的周内变动。

我们做出以下考虑:

  • 我们选择均方误差(MSE)作为我们的主要成本函数,因为我们的数据集是高质量的,也就是说,不存在因数据误差而导致的异常值,否则这些异常值可能会导致使用此误差指标的模型过度拟合。
  • 实际上,我们选择了均方根误差(r MSE ),见图 3,而不是均方根误差,原因很简单,因为 RMSE 与预测变量有相同的单位。
  • 在其他情况下,如预测销售额,数据可能会有错误,例如,如果没有考虑假期。该模型将错误地预测低销售额,并且由此产生的大误差将在训练期间被错误地惩罚。
  • 鉴于我们的数据中存在一些较大但罕见的价格波动,RMSE 可能会导致模型过度拟合。此外,我们更感兴趣的是预测总体趋势,而不是时间序列的短期运动。因此,我们选择包括平均绝对误差(MAE ),见图 4,作为额外的性能测量,因为它比 RMSE 对异常值更宽容。

图 3:均方根误差[图片由作者提供]

图 4:平均绝对误差[图片来自作者]

我们的目标是尽量减少 RMSE 和梅。

递归神经网络和长短期记忆

让我们专注于构建预测模型。为此,我们将快速回顾递归神经网络(RNN)以及长短期记忆(LSTM)网络。

RNNs 是一种神经网络架构,主要用于检测语言等序列数据中的模式,或者在我们的情况下,检测数字时间序列中的模式。为了检测数据中的序列模式,信息通过网络循环传递,即信息被传输回模型[3]。这使得 RNN 除了考虑当前输入 X(t)之外,还能够考虑先前的输入 X(t-1)。在图 5 中,您可以看到信息 h(t)是如何从网络的一个步骤传递到下一个步骤的。

图 5:展开的 RNN,X(t)作为时间 t 的输入,H 作为隐藏层,h(t)作为时间 t 的隐藏层输出[图片由作者提供]

关于如何通过随时间反向传播来训练 RNN 的更多信息也可以在[3]中找到。随着序列变得更长,当前隐藏状态将需要保留来自先前隐藏状态的越来越多的信息,这对于非常长的序列来说会变得有问题。对于每个时间步长,随着涉及更多的变换,渐变可以减少(或增加),最终导致渐变消失(或爆炸)。这意味着远早于当前时间步长发生的状态的贡献会丢失(或占主导地位)。

LSTMs 是一种特殊类型的 rnn,它比标准 rnn[1][3]更好地处理了这种长距离依赖性。与 RNNs 类似,LSTMs 具有链状结构,但每个重复的块(LSTM 单元)与标准 RNN 相比,具有 3 个额外的全连接层(图 6)。这些附加层也称为栅极层,因为它们的激活(sigmoid)与标准层(tanh)不同。当信息通过每个 LSTM 单元时,单元状态 C(t)可以通过经由栅极层添加或移除信息来更新。这就是 LSTM 模型如何决定是保留还是丢弃先前时间步长的信息。

图 6:简化的 LSTM 单元,X(t)为 t 时刻的输入,C(t)为当前单元状态,h(t)为 t 时刻的隐藏层输出[图片由作者提供]

关于 LSTMs 的更多细节和确切结构,可以参考1

模型训练和评估

我们的特定预测模型由两个 LSTM 层和一个全连接层组成,用于预测第二天的价格。我们使用一个数据集类来生成序列长度为 30 天的特征集的时间序列,并使用一个数据加载器类来批量加载它们。我们使用 Adam 优化器和 RMSE 以及 MSE 作为损失函数来训练模型(在单独运行期间)。此外,如果没有显著的性能改进,我们实施提前停止以允许培训作业提前完成。

由于输入数据被缩放到 0 和 1 之间的级别,我们必须将模型的输出按比例缩小(去缩放)到原始比例,以便能够根据实际价格级别评估预测。重点不在于调整最佳模型,所以我们只使用一组超参数进行训练,并将其视为基线模型。在对该模型进行了大约 10-15 个时期的训练并对其输出进行了缩放之后,该模型在测试数据集上实现了大约 10,000 的 RMSE 和大约 6,500 的 MAE。

让我们来看看对测试数据的预测(图 7)。您可以看到,该模型在预测整体价格发展方面做得相对较好,这是我们在测试数据集的第一季度的目标。在中间阶段(300-450 天),该模型似乎总是高估价格,并且未能预测到接近结束时(680 天及以后)的极端价格上涨,这解释了上面提到的较大误差。

图 7:模型预测(预测)与实际价格(真实)及其差异(残差=真实-预测)[图片由作者提供]

如果我们只在测试数据集的前 30 天验证模型的性能,它会显著提高。RMSE 和 MAE 都下降到大约 750,这意味着该模型的预测平均错过了大约 750 的价格。我们可以在图 7 中看到,随着训练时间(第 0 天)的推移,模型的预测越来越差。在时间序列预测中,必须经常重新训练模型,以便它可以从最新的观察中学习。根据最近的数据训练模型,可以让它学习和吸收最新的模式和信号。

该模型未能预测价格的几次高峰和低谷,这一事实表明缺少输入因素。向训练数据集中添加更多要素有助于进一步改进模型预测。

这并不是对模型预测误差的详尽分析,但足以满足我们的目的。接下来,我们将使用这个基线模型,并尝试解释它的预测。

重要特征

对于神经网络,理解模型做出预测的原因可能很困难。我们使用一种基于深度提升算法[4][5]的方法,该算法近似于经典博弈论中已知的 SHAP 值。基本上,这种方法旨在回答这样一个问题,即当某个要素存在时(在输入中)与不存在时(不在输入中)相比,该要素对模型预测的贡献有多大,从而得出该要素的重要性。更具体地,该方法通过反向传播网络中所有神经元对输入的每个特征的贡献来分解神经网络对特定输入的输出预测。然后,它将每个神经元的激活与其参考激活进行比较,并根据差异分配贡献分数(或乘数)。参考输入表示根据域选择的一些默认或中性输入,例如零或特征平均值。基于代表性数据集(或背景数据集)计算乘数后,我们可以基于一些样本输入计算输入要素对模型输出的贡献,并按照贡献最大的顺序对要素进行排序,以获得其重要性。

我们使用 900 个训练数据样本作为背景数据集,并使用 100 个测试数据样本来解释模型的输出。由于在每个时间步长计算每个输入样本的特征重要度,因此我们对所有 100 个输入样本进行平均,并按特征和时间步长绘制重要度图(图 8)。前三个输入特征是交易量、收盘价和日内高低百分比价格变化。我们还可以看到,与更远的时间步长(第-30 天到第-8 天,其中第 0 天是预测时间)相比,更近的时间步长(第-7 天到第-1 天)在进行预测时起着更重要的作用。

图 8:基于深度 SHAP 的重要特征

理解深度学习模型的预测对于减少在需要可解释性的应用中采用的障碍是至关重要的。除了以一定的准确度预测结果之外,我们现在可以测量其最重要的贡献者。例如,如果我们要预测一家公司的产品销售,通过应用这种特征重要性技术,我们可以进一步了解销售为什么会发生变化,并设计行动计划来促进预期的业务成果(例如,通过支持有助于提高销售额的特定营销活动来增加销售额)。

此外,从发现重要特征中获得的见解也有助于为模型优化过程提供信息。例如,在这里,缩短输入序列长度以及删除不重要的特征将提高训练和推理速度,同时可能不会对预测性能产生太大影响。

结论

在本文中,我们展示了如何基于 LSTM 网络构建一个多元时间序列预测模型,该模型可以很好地处理具有复杂模式的非平稳时间序列,例如,在传统方法缺乏的领域。在将预测目标与我们的“业务”目标联系起来之后,我们只需要很少的数据准备就可以对模型进行训练和评估。此外,我们使用基于 DeepLIFT 的技术发现了哪些特征(以及哪些时间步长)驱动了模型的预测。这使得我们能够解释深度学习模型,并从中得出更好的决策。

这篇博文附带的 Github 代码可以在这里找到。

参考资料:

1 Hochreiter 和 Schmidhuber。“长短期记忆”。(1997)

[2]博克斯和詹金斯。时间序列分析、预测和控制。(1994)

[3]施密特。“递归神经网络(RNNs):一个温和的介绍和概述”。(2019)

[4] Shrikumar、Greenside 和 Kundaje。“通过传播激活差异学习重要特征”。(2017)

[5]伦德伯格和李。“解释模型预测的统一方法”。(2017)

来自随机性的分形音乐

原文:https://towardsdatascience.com/music-from-randomness-3452061e0f6

音乐作为数据系列

精心挑选的变量可以让随机的音符序列听起来像音乐

分形构图“布雷克 8”的动画中的一帧(图片由作者提供)

我的生活似乎是一连串的事件和事故。然而当我回头看时,我看到了一个模式。

―本华·B·曼德博(1924 年至 2010 年)

动机和背景

音乐是多维数据的丰富来源。该数据可以采用乐谱的符号形式,通常由 MIDI 值表示,或者采用音频信号的形式。

多年来,我一直使用 MIDI 数据来识别音乐中的分形结构。MIDI 文件因质量不稳定而臭名昭著,这是由人为和机器错误造成的。需要对数据集进行质量筛选和清理,以处理缺失、损坏或重复的值、异常值,有时还有全局计时问题。这项工作大量使用探索性数据分析工具,包括图形检查、统计汇总、数据平滑、拟合和关键分析。

一个自然产生的问题是,我是否能在某种程度上逆转这个过程;也就是说,用一个已知分布的音乐特征的合成数据集,通过算法把它变成音乐,或者说,任何人都可能喜欢听的音乐。

算法音乐目前是一个活跃的领域,其结果可以采取多种形式。虽然方法之间的界限可能是模糊的,但流行的生成方法包括随机方法进化/遗传方法基于马尔可夫的模型、非音乐媒体(例如图像和文本)的音频/MIDI 映射,以及神经网络

以某种程度上独立于人类作曲家的方式创作音乐的想法至少可以追溯到莫扎特的时代。他的Musikalisches würfelspiel(“音乐骰子游戏”)可能是将随机输入源应用于音乐创作的最著名的例子。计算机生成和计算机辅助作曲的现代时代于 1957 年到来,伴随着勒贾伦·希勒和伦纳德·艾萨克森的 伊利亚克组曲 (更多细节参见妙瑞卡信息)。当然,当前通过算法创作各种风格的现实音乐的努力的前沿集中在人工智能的复杂性和力量上,正如 OpenAI 的自动点唱机所证明的那样。

这些现有的方法大多试图使用、编纂或学习现有的音乐结构。对我来说,问题是,我能做多少小事情来创作出悦耳的音乐?向大自然看齐似乎是个不错的起点。

幂律

在这里,我们将介绍一种似乎是算法合成的新方法,它使用了一种叫做https://www.youtube.com/watch?v=7jUp1rR-Q4I幂律的分形几何的基本属性。幂律存在于自然科学和社会科学中令人难以置信的一系列现象中。以下是几个例子:**

  • 生物多样性
  • 植物和动物的循环系统
  • 棉花价格
  • 星系分布
  • 财富分配
  • 地震
  • 飓风和洪水
  • 语言词汇用法
  • 海岸线的长度
  • 肿瘤生长
  • 城市系统

揭示星系分布的近红外天空全景。(图片: T .杰瑞特,Image 加州理工)

幂律也可能出现在音乐的许多特征中,如音高、音程、持续时间、节奏变化,以及一种叫做motive scaling 的分形结构形式。动机音阶至少在五个世纪前就出现了伴随着法佛兰德作曲家若斯金·德斯·佩雷斯的作品。

分形音乐

“分形音乐”这个术语对不同的人有不同的含义。对一些人来说,它仅仅意味着“来自分形图像的音乐”然而,将图像映射到音乐声音并不能确保这些映射的声音也具有分形属性。这有点像罗伯特·弗罗斯特的一首诗翻译成粤语时要押韵。

伯努瓦·曼德尔布罗一直有一种强烈的直觉,音乐是可测量的分形。这就是我们将使用这个术语的含义。分形几何首先是几何学,它的名字意味着测量。至少,真正分形的音乐必须具有某种可测量的尺度特性。

我们将在以后的文章中探讨更广泛的分形音乐主题。这里,我们将研究一个我们称之为旋律音程缩放的属性。它描述了旋律中单个音符之间的距离分布。例如,旋律音程缩放可以在约翰·塞巴斯蒂安·巴赫的一些作品中找到。

实验

这是一个带有独奏的简短作品的动画,使用下面描述的算法生成:

分形构图“布雷克 8”的动画

通过观察独奏音符之间的垂直间距分布,您可以对音程缩放有一个直观的感觉。总的来说,小型跳跃多,中型跳跃少。这些都被很少的大跳跃打断。计数和大小之间的这种关系可以用幂律精确地描述。

一点数学知识

考虑一个异质系列不同大小的旋律音程,{ s,s,s …}。我们统计每种尺寸的区间数{ N ₁, N ₂, N ₃,…},其中 Nₖ 对应尺寸的区间数 sₖ 。如果当我们绘制一个 Ns 的双对数图时,这些点落在一条直线上,则该序列具有旋律音程标度。这条线的斜率可以解释为元素集的维度d——在本例中是区间。这是因为取幂律关系的对数将其转换为线性方程:

如果我们让 y =Log[N]和 x =Log[1/ s ,那么我们得到的就是一条线的方程,y=MX+b,其中 d = mb =0。关于这种类型的分析及其在巴赫大提琴组曲中的应用的详细描述,请参见1

该算法

这意味着我们可以:

  1. 选择维度和间隔大小范围。
  2. 使用上面的公式,计算我们需要多少个尺寸区间来满足我们选择的尺寸所定义的幂律。
  3. 基于此计算创建间隔分布,其中每种大小的间隔一半为正,另一半为负。
  4. 随机排序该分布以确保异质性。
  5. 累积产生的音程序列来产生我们的旋律。

以下是步骤 1 到 3 的伪代码:

**// Assignments:2^(-1/12) ← sizeRatio
dimension ← fractal dimension of set  /* dimension > 0 */
scalingRange ← number of levels of scaling in set  /* scalingRange ≥ 3 */
largeCount ← number of the largest elements in set// Implementation:countRatio := Exp[dimension*Log[1/sizeRatio]]sizes := Table[1/ratio^k, (k, 1, scalingRange)]counts := 
 Table[largeCount*countRatio^(k-1), (k, scalingRange, 1, -1)]  /* k counts down from scalingRange to 1 */data := Log[Transpose[sizes, counts]]Fit[data, (1, x), x]  /* perform linear regression, if desired, to confirm dimension */melodicIntervalSet := Join[data,-1*data]**

我们假设在您选择的语言中,步骤 4 和 5 是熟悉的操作。在将上述算法付诸实践时,还需要解决当累计的间隔总和超过标准 MIDI 范围(0 到 127)时的边界问题。

****警告:这个过程产生的旋律会变得非常怪异。然而,有了适当的参数和正确的上下文,结果可能是令人惊讶的音乐。

对于上面的构图, Brecker 8 ,这里是音程的分布,对于一个 k 半音的音程,每个音程的大小等于 2^( k /12】:

数值 2^( k /12)来源于西方音乐常用的等音律音阶的物理学。以下是数据图表:

Brecker 8 的间隔计数与间隔大小的关系图。(图片由作者提供)

虽然此示例源于关注改变一组旋律音程的维度的效果,但是也可以通过算法结合其他幂律(例如,与持续时间或动态相关的幂律)来给出更人性化的感觉(参见示例[4])。

我发现这件作品特别吸引人的是,一个随机过程可以创造出一些东西,尽管如此,由于它所作用的元素和它出现的背景的潜在分布,这些东西是“有意义的”。至少在音乐中,随机性不需要听起来那么随机。

参考

巴赫大提琴组曲中的音程音阶

[2] 音乐的分形几何

[3] 曼德尔-巴赫之旅:音乐和视觉分形的联姻

【4】100 秒的圆周率

如果你喜欢这篇文章,你可以考虑成为 Medium 会员,这样就可以无限制地访问 Medium 上的所有内容:

**https://medium.com/@harlan.j.brothers/membership **

基于分而治之 CRNN 的音乐流派分类

原文:https://towardsdatascience.com/music-genre-classification-using-a-divide-conquer-crnn-2ff1cf49859f

一种适用于小数据集的有效方法

图片作者。

音乐流派分类中的 C(R)NNs

自从 CNN 在 2012 年开始在图像处理领域爆发以来,这些网络很快被应用于音乐流派分类,并取得了巨大的成功!今天,在所谓的频谱图上训练 CNN 已经成为最先进的技术,取代了几乎所有以前使用的基于手工制作的特征、MFCCs 和/或支持向量机(SVM)的方法。

最近几年,已经开始显示将 LSTMs 或 GRUs 之类的循环层添加到经典 CNN 架构中会产生更好的分类结果。对此的直观解释是,卷积层计算出什么和什么时候,而递归层发现什么和什么时候之间有意义的关系。这种架构被称为卷积递归神经网络(CRNN)

分治分类

AI 生成的图像(稳定扩散 1.5)。提示:“一群互相争斗的人,这样他们就不会联合起来对抗一个人”,分而治之的军事定义。图片作者。

当我第一次了解《分而治之》时,我很困惑,因为我是在军事背景下听说的。军方的定义是

“使一群人意见相左,互相争斗,这样他们就不会联合起来反对一个人”(【www.merriam-webster.com】)

然而,这几乎是与我们的目的相反的意思!参见表 1 ,其中列出了计算机科学和音乐流派分类中 divide &征服背后的三步过程。

表 1 —计算机科学和音乐流派分类中的分而治之。

虽然定义不一样,但它们确实有很多重叠,而且我个人认为“分治”这个术语在这两种情况下都非常合适。在音乐流派分类中,该术语最早由董(2018)使用(据我所知)。然而,其他研究人员如 Nasrullah 和赵(2019)也使用了这种方法-只是没有使用相同的名称。

通常,流派分类仅仅基于一个 30 秒的片段。这部分是因为常用的音乐数据源如 GTZANFMA 数据集或 Spotify Web API 提供了这样长度的曲目。然而,应用分治法有三大优势:

1.更多数据

假设您有完整长度的音轨,那么获取一个 30 秒的片段并丢弃剩余的 3-4 分钟的音轨是非常低效的。使用分治法,可以使用大多数音频信号。此外,通过允许切片之间的重叠,甚至可以绘制更多的片段。事实上,我认为这是一种自然的、相当无缝的数据扩充形式。

例如,与每个音轨绘制一个 30 秒的片段相比,如果您绘制一个 3 秒的片段并与 1 秒重叠,您可以从 3 分钟的音轨中获得超过 80 倍的训练数据。即使你只有 30 秒的音轨,你也可以从每个音轨中获得 14 个 3 秒的片段和 1 秒的重叠。

2.低维数据

一个 30 秒的片段产生了一个相当大的声谱图。在常用参数下,一个谱图可以有(1290 x 120)的形状,即超过 15 万个数据点。自然,一个 3 秒的片段,同样的参数会产生一个只有15k 个数据点的~(129 x 120)声谱图。根据您使用的机器学习模型和架构,这可以显著降低模型的复杂性。

边注:如果你不知道什么是频谱图,或者它们为什么以及如何用于音频分类,我推荐这篇由实验作家撰写的文章,它给出了一个漂亮而直观的解释。

3.与音频输入长度无关

如果你想在现实世界中应用你训练过的模型,你会遇到各种长度的轨迹。你不必费力去寻找合适的 30 秒片段,而是开始画 3 秒的片段,直到覆盖整个音轨。如果输入的音轨长度不到 30 秒呢?你的模型是否足够健壮来处理 10 秒的广告词,这些广告词被零填充以达到 30 秒?分而治之,这不是问题。

不足之处

使用分治法有三个主要缺点。首先,它增加了额外的处理步骤来分割音频文件并执行完整音轨的聚合预测。此外,基于片段的方法需要轨道式的训练-验证-测试分离,以避免相互关联的训练、验证和测试数据集。

幸运的是,这两个步骤都已经包含在我的单标签音频处理管道 SLAPP 中,在 GitHub 上可以免费获得。

最后,单个片段的预测通常使用某种多数投票进行汇总。这样,你的模型就完全忘记了任何超过 3 秒钟的音乐关系。除非您为聚合过程开发另一个复杂的元分类器。

何时使用分而治之

如果你有大量的数据,并且不需要更多的数据,或者如果你想分析在较长时间内展开的音乐结构,你可能不想使用分治法。但是,如果您的数据有限,并且希望用它来构建一个健壮而灵活的分类器,请考虑这种令人兴奋而有效的方法!

利用 SLAPP 进行数据处理

AI 生成的图像(稳定扩散 1.5)。提示:“功能强大的音乐处理机器的概念艺术”。图片作者。

什么是 SLAPP?

本文也作为我新开发的 单标签音频处理流水线(SLAPP) 的展示。该工具自动化了单标签音频分类任务的整个数据处理工作流程。这包括将轨道分割成片段,计算光谱图,执行轨道方向的列车验证测试分割,等等。

由于在家用电脑上处理音频数据需要很长时间,SLAPP 允许你在任何时候关闭电脑并重新加载你的进度,而不会有任何明显的时间或数据损失。在为我的学士论文开发了这个管道之后,我在几个分类任务上尝试了一下,从中获得了很大的乐趣和成功。

GitHub 上查看 SLAPP,亲自试用

使用 SLAPP 处理 GTZAN

GTZAN 是一个众所周知的公开可用的流派分类数据集,以来自 10 个不同流派的 100 首 30 秒长的曲目为特色。这个数据集非常适合我们的目的,因为它背后有大量的研究,而且它只有有限的数据。

为了使用 SLAPP,你需要把你的 MP3(不是 WAV)文件放在一个文件夹结构中,就像图 1 中的所示。

图 1 —使用 SLAPP 所需的文件夹结构示例。图片作者。

因为 GTZAN 就是这样的文件夹结构(耶!),我们现在需要做的就是将 WAV 文件转换成 MP3 文件,我们就可以开始了。我建议您通过遍历目录,并使用

from pydub import AudioSegmentAudioSegment.from_wav("/input/file.wav").export("/output/file.mp3", format="mp3")

接下来,您需要做的就是通过导航到您想要的目录并使用以下命令来克隆 SLAPP 存储库:

**git clone** https://github.com/MaxHilsdorf/single_label_audio_processing_pipeline

确保您的系统满足使用 SLAPP 的所有要求;例如,Python 库 PydubLibrosa 以及音频编解码器 FFmpeg (在资源库中有详细描述)。

现在,在 SLAPP 中,我们打开“pipeline_parameters.json”并像这样设置参数:

这样,我们从 GTZAN 内的每个 30 秒的轨道中提取多达 14 个 3 秒的片段,允许 1 秒的重叠。通过运行“build_dataset.py”构建数据集,然后通过运行“process_dataset.py”对数据集进行归一化和混洗。这就是你要做的一切。SLAPP 会帮你处理剩下的事情。

验证和测试各占 10%,您将获得 11,186 个训练谱图和 1,386 个验证和测试数据的谱图。每个谱图的形状为(130 x 100)。

旁注:如果你不只是阅读,而是实际编码,这个处理步骤会花一些时间,因为有很多计算步骤。在此过程中的任何时候,您都可以停止脚本并重新启动它们,从您停止的地方继续。如果你在 SLAPP 上遇到任何技术问题,请随时告诉我,因为这是我的第一个软件版本,可能会有很多错误,或者我的文档可能不够。

构建和培训 CRNN

AI 生成的图像(稳定扩散 1.5)。提示:“音符的绘画建造了一个巨大的宗教寺庙”。图片作者。

模型结构

使用 Keras 库,我构建了这样一个 CRNN:

这种 CRNN 架构背后的思想是使用卷积层+最大池将频率维度压缩为标量值,同时将时间维度保持为向量。这允许我们将门控递归单元(GRU)应用于由卷积块提取的特征。据我所知,这种类型的建筑是由 Choi 等人(2017)提出的,并已被 Nasrullah 和 Zhao (2018)以及 Bisharad 和 Laskar (2019)等多项研究采用。

有关所用架构的详细概述,请参见图 2 。

图 2 —所用 CRNN 模型的架构。

在开发阶段,我尝试了该架构的不同变体,并比较了这些模型之间的验证损失。这种特殊的模型仅用大约 40 万个参数就取得了的最高结果。

边注:在这种情况下,SLAPP 会给你一个形状的训练数据张量(11186,100,130)。然而,为了训练这个特定的 CRNN,输入需要被整形为(11186,130,100,1),有效地交换时间轴和频率轴(numpy.swapaxes)并增加另一个维度(numpy.expand_dims)。

模特培训

我用 Adam optimizer 训练模型,学习率为 0.0001,分类交叉熵损失,批量为 32。此外,我使用了 Keras 提供的 EarlyStopping、ReduceLROnPlateau 和 ModelCheckpoint 回调来确保训练顺利进行,并且只保存最好的模型。

正如您在图 3 中看到的,训练指标达到了接近 90%的精度和召回分数,而验证精度和召回分数收敛在 62%和 73%之间。鉴于我们只处理 3 秒钟的音频和 10 个平衡的类(通过随机猜测→ 10%的准确性),这已经相当令人印象深刻了。总体而言,本次训练运行中的最佳模型实现了 67.32% 的验证准确率。

图 3-CRNN 训练运行的训练和验证指标。图片作者。

为了获得更稳定的结果,开始了 10 次训练运行并进行评估,以获得感兴趣的指标的平均分数和标准偏差。请记住,本文的目的不是提供复杂的统计评估,而是使用一个示例来展示分治分类的有效性。

片段与基于轨迹的分类

AI 生成的图像(稳定扩散 1.5)。提示:“陪审团严格审查一个有趣案件的照片,黑暗的气氛”。图片作者。

如何计算基于轨迹的预测?

对于基于音轨的预测,我将测试数据集中的每个音轨作为 MP3 文件加载。测试轨道名称由 SLAPP 自动写入您的数据目录中的“train_val_test_dict.json”。接下来,我使用的过程如下:

  1. 将音轨分割成尽可能多的片段(在这种情况下,3 秒的片段有 2 秒的重叠)。
  2. 将每个片段转换成适合训练模型的输入形状的 mel 光谱图
  3. 获取每个片段的原始类预测。
  4. 对所有片段中每个类的原始分数求和。
  5. 您的全局预测是具有最高得分总和的班级。

由于这对于初学者来说可能会变得相当复杂,我将为您提供另一个模块来自动化这个基于轨迹的预测过程:请在这里找到该模块及其文档

分类结果

我对上面描述的模型进行了十次训练和评估,并对准确性得分进行了平均,以消除该过程的一些随机性。

参见图 4 获取准确度分数的直观概述。我们注意到的第一件事是,该模型在验证和测试数据上的表现几乎和一样好,这是一个好迹象。此外,所有的标准偏差都很小,这告诉我们,我们真的可以在平均精度的差异。

很容易看出,基于音轨的分类优于基于片段的分类,平均相差约 12.2 个百分点。虽然为了简单起见,我们不打算做任何统计测试,但不可否认的是,在这种情况下,通过 divide&convert 进行的基于轨迹的分类要优于

图 4 —基于片段和基于跟踪的分类的准确性和标准偏差(10 次测试)。图片作者。

如果我们使用完整的 30 秒片段会怎么样?

到目前为止,我们已经表明(在这个例子中)基于音轨的分治分类优于 3 秒单片段分类。然而,如果我们在“原始”GTZAN 中的 1000 个 30 秒音频片段上构建 CRNN,我们可能会获得更高的准确性。

这是可能的,所以我试了一下。我基于 30 秒的切片构建了另一个类似架构的 CRNN(尽管由于数据较少,只有一半的参数),并对其进行了 10 次训练,以平均获得的准确度分数。这种方法在的平均准确率仅为 55.8% (SD=0.043),远远低于任何 3 秒模型

结论

AI 生成的图像(稳定扩散 1.5)。提示:“两幅真正深刻对话的音乐作品,抽象”。图片作者。

分而治之确实征服了

本文展示了分而治之的方法如何有助于为音频分类问题建立一个强大的分类器,即使没有太多的训练数据。数据处理管道 SLAPP 使得为单标签音频分类任务构建分而治之分类器所需的处理步骤变得非常容易。

3 秒比 30 秒好

为什么 30 秒的切片比 3 秒的切片更糟糕,尽管它们包含更多的信息?通过绘制 3 秒切片来增加数据集大小似乎有助于模型提取有意义的特征,而在处理由巨大光谱图组成的非常小的数据集时,它似乎会迷失方向。

可以说,如果使用其他数据格式,如 MFCCs 或更小维度的手工制作的功能,该模型将更容易处理 30 秒的音频文件。然而,本文表明,如果使用分治法,几乎任何音频数据集都可以用声谱图数据进行分类。

SLAPP +分而治之的其他应用

如果你想在另一个类似的问题上尝试所提出的方法,这里有一些想法可能会启发你的下一个数据科学项目:

  1. 基于语音数据的性别识别
  2. 基于语音数据的口音识别
  3. 音乐情感分类器
  4. 使用来自机器传感器的音频数据构建分类器

非常感谢你对我的工作感兴趣!如果有什么对你不起作用,或者你在理解或应用这些概念时有困难,请告诉我。

参考

1比沙拉德,D. &拉斯卡尔,R. H. (2019) 。基于卷积递归神经网络结构的音乐类型识别。在:专家系统 36,4。

【2】崔、k;法泽卡斯;桑德勒,M. &曹,K. (2017) 。用于音乐分类的卷积递归神经网络。In: 国际声学、语音、信号处理会议 2017

【3】董,M. (2018) 。卷积神经网络在音乐流派分类中达到人类水平的准确性。参加:认知计算神经科学会议,2018 年 9 月 5 日至 8 日,宾夕法尼亚州费城

【4】纳斯鲁拉,Z. &赵,Y. (2019) 。音乐艺术家使用深度特征对音频、文本和图像进行分类。In: arXiv ,DOI:10.48550/arXiv . 1707.04916

2020 年计算机视觉必读论文更新

原文:https://towardsdatascience.com/must-read-papers-in-computer-vision-for-the-2020s-3943d5339ba4

计算机视觉中深度学习技术的前沿

计算机视觉社区目前正在发生什么?如果你像我一样是一个狂热的计算机视觉爱好者,这里有几篇我近年来最喜欢的论文,我相信它们将对未来产生巨大的影响。

免责声明:这些论文是我认为的“基础”论文。有许多论文延伸到它们之外,具有很大的见解,我将逐渐在这里添加链接以供参考。

屏蔽的自动编码器是可扩展的视觉学习器

图一。屏蔽的自动编码器管道。从原始纸张中检索的图像。

如果你跟踪深度学习技术有一段时间了,你可能听说过 BERT,这是一种自我监督的技术,用于语言中,你可以屏蔽部分句子,进行预训练,以改善学习隐式表示。然而,在学习更好的特征提取方面,同样的技术不能转移到 CNN 上——直到最近引入了视觉变形金刚 vit。

对于 transformer 架构,He 等人研究了屏蔽是否适用,结果令人振奋:一个屏蔽的自动编码器伴随 ViTs 被证明是一种有效的预处理技术,用于分类和重建等下游任务。

【论文链接:【https://arxiv.org/abs/2006.11239】

相关论文:

21 世纪 20 年代的通信网

图二。ConvNeXt 与变压器的比较。从原始纸张中检索的图像。

另一方面,尽管 vit 在许多视觉任务的论文中表现出了优越性,但有一项工作在分析卷积网络(ConvNet)的基础方面表现突出。Liu 等人专注于“现代化”卷积网络,引入经验证明对 vit 有用的元素,并表明 ConvNet 确实可以在 ImageNet 等大型数据集上实现类似于 vit 的结果。

论文链接:【https://arxiv.org/abs/2201.03545】https://arxiv.org/abs/2201.03545

相关论文:

NeRF:将场景表示为用于视图合成的神经辐射场

图 3。NeRF 的管道。从原始纸张中检索的图像。

移动到三维空间,在重建和新颖的视图合成方面也有惊人的突破。Mildenhall 等人提出使用全连接网络来表示 3D 形状。也就是说,给定一些坐标和视角,网络输出特定空间的相应颜色。这允许以高质量和精确的质量生成未知角度的物体的新图像/视图。

论文链接:https://arxiv.org/abs/2201.03545

相关论文:

去噪扩散概率模型

图 4。去噪扩散模型正反过程。从原始纸张中检索的图像。

当讨论生成网络时,人们通常会想到 gan 或 VAEs。令人惊讶的是,扩散模型是近年来优秀的模型家族。简而言之,扩散模型在其正向过程中逐渐向图像添加噪声,同时学习反向过程。因此,逆向过程成为生成模型,其中图像可以从纯随机噪声中“提取”,类似于模型被训练的数据分布。

论文链接:https://arxiv.org/abs/2006.11239】

相关论文:

感谢您坚持到现在🙏 我会在计算机视觉/深度学习的不同领域发布更多内容,所以 加入并订阅 如果你有兴趣了解更多!**

我用数据创造故事的三步法

原文:https://towardsdatascience.com/my-3-step-method-to-create-a-story-with-data-74f6fd9fb7e7

产生相关和可行见解的技巧

Pexels 的 cottonbro 拍摄的照片

作为一名数据专业人员,数据故事讲述是一项需要培养的重要软技能,但没有太多信息可以指导您从哪里开始。当我第一次成为数据分析师时,我不知道如何处理这个问题。随着时间的推移,它变得越来越容易,在做了许多演示之后,我想分享一下我用数据创作故事的三步法。

1.假装你拥有这家公司

成功的数据故事的一个要素是确保这些见解与受众相关。每位首席执行官都想发展自己的公司,增加收入,但最好的方法是什么呢?他们如何解决阻碍增长的问题?假装这是你的事情将帮助你评估你的故事中的数据是否提供了任何新的见解,将提高业务绩效或解决一个紧迫的问题。

例如,让我们假设你拥有一家科技创业公司。你需要增加新客户来发展公司。这是你作为首席执行官的最高目标。如果数据分析师展示了与客户保持率和流失率相关的见解,会怎么样?这是不相关的,因为它没有提到如何发展公司。

作为数据分析师,您仍然可以显示客户保持率和流失率,但了解新客户是一个目标,您可以通过来源进行细分,以显示客户来自哪里。假设细分数据显示 50%的客户来自付费广告,他们拥有最高的转化率和最低的流失率。有了这种认识,你的故事可以建议公司增加付费营销支出,以吸引更多新客户。你的故事现在是相关的和可行的,因为它解决了首席执行官关心的如何增加新客户的问题。

这怎么翻译成故事:

目标 —公司希望增加新客户以实现增长。

数据 — 50%的客户来自付费广告,他们的转化率最高,流失率最低。

可操作的洞察力 —增加付费营销支出以吸引更多新客户。由于他们的流失率最低,公司将在他们的有生之年充分利用他们来收回收购成本。该公司还可以创造内容,从有机搜索中吸引更多的客户,从而降低长期收购成本。

2.假设你是利益相关者

如果公司目标和你的利益相关者不一样怎么办?一个常见的公司目标可能是增加收入,但产品经理可能只关注用户对产品的体验。在这种情况下,与利益相关者讨论他们的目标和难点,以决定如何用与他们相关的数据创建一个故事。

例如,我曾经被要求寻找提高免费用户保留率的见解。我发现用户使用某个特定功能的次数越多,免费用户的留存率就越高。我问自己,如果我是产品经理,这些信息会有帮助吗?答案是否定的,因为它不具有可操作性。

然而,作为一个产品经理,如果我知道用户在注册的第一周内必须使用这个功能至少 3 次,以显示保留率的提高,那么我可以做些什么。这些是我作为产品分析师在我的故事中加入的相关见解。产品经理可以使用这些信息来进行入职更改,以鼓励新用户尝试该功能,并与营销部门合作,在新用户入职电子邮件中强化该功能的使用,以提高保留率。

这怎么翻译成故事:

问题——产品经理需要洞察力来提高免费用户保留率。

数据—与所有免费用户的整体保留率相比,在注册的第一周内至少使用一个特定功能 3 次的免费用户在保留率方面有所提高。

可操作的见解 —修改产品中的新用户入职信息,鼓励使用该功能,并与营销部门合作,在新用户入职电子邮件中强化该信息。

3.要求复审

作为一名数据分析师,我经常包含太多不需要的数据来表达我的观点。为了弥补这一点,我总结了我的故事的主要发现和建议。然后,我与同事或我的经理一起回顾演示文稿,以确认我在演示文稿中总结的发现,以及是否有任何可以修改的地方来阐明我的观点。

我发现这一步非常有助于识别太复杂的数据和需要澄清的模糊见解。如果你没有人要求回顾,我建议离开一天,早上用新鲜的眼光重新审视。

外卖食品

数据讲故事不一定很难。使用这三个步骤的方法,用与你的受众相关且可操作的数据创建一个故事。

概括一下:

  1. 假装你拥有这家公司。如果您获得了这些见解,您能利用它们来推动业务增长或解决您最关心的问题吗?
  2. 假设你是利益相关者。如果你得到了这些见解,它们对你实现目标有用吗?
  3. 求点评。你的同事或经理是否有和你想的一样的收获?有什么不清楚的数据或见解吗?

你可能也会喜欢…

我从 Tableau 专业工作中得到的 5 个最好的教训

原文:https://towardsdatascience.com/my-5-lessons-from-working-with-tableau-professionally-b55d826b8e21

在这篇文章中,我将讲述我从 Tableau 专业工作中学到的 5 个最好的经验

Unsplash 上的absolute vision拍摄

(你也可以在 我的网站 上找到这篇文章以及其他文章和更多有趣的东西)

1。理论与实践

我学到的第一个教训是,你在“理论”中学到的东西和你在日常工作中真正用到的东西之间有着天壤之别。

这是整篇文章的一个潜在主题,但是我将在这里给出一些我发现的最令人震惊的事情。

关于理论,我的意思是你会做些什么来通过认证,学习在线课程,阅读普通文章或参与社区项目。

(这可能会随着 Tableau 认证的变化而变化)

理论侧重于用预先准备好的数据创建奇特的图表

Adeolu EletuUnsplash 上拍摄的照片

当我自学学习 Tableau 时,我在网上找到的许多理论都是围绕着能够在 Tableau Desktop 的画布上工作而建立的(创建很酷的图表,并在某种程度上使用计算字段和参数)。

它不太关注构建仪表板,甚至更少关注 Tableau 管理、Tableau 服务器、Tableau 在线或数据处理。

还有一些很棒的社区项目,比如 Make Over Monday、Tableau Tip Tuesday 等等,参与这些项目也很有趣。

但是,正如大多数在线课程一样,这些课程也大多侧重于在画布上工作。

我认为这种理论焦点的主要原因是,它们是围绕免费的 Tableau Public 建立的,这更容易关注每个人都可以轻松访问的东西,而不是更难获得的管理性东西。

在实践中,你必须对画面和围绕画面的过程有更全面的了解

奥利·戴尔在 Unsplash 上的照片

当然,这取决于你将具体用什么来工作。

但无论你是商业智能顾问、数据分析师还是其他工作,你都需要有一个更全面的方法来处理不同的问题。

很有可能你将不得不与数据治理、一起工作,也就是管理你的数据的安全性和完整性。(您将如何发布报表并连接到数据源,谁将看到您的报表。)

除此之外,你还将与如何将数据输入到你的报告中一起工作,要么自己做准备,要么与你组织中从事系统架构或数据库管理的人密切接触。

2.业务需求应该放在首位

活动创建者Unsplash 上拍摄的照片

始终与利益相关者保持密切沟通

在从事商业智能和数据分析工作时,这是一件非常重要的事情。

你希望你的报告能让尽可能多的人在日常工作中使用。你想产生影响。

产生影响的最佳方式是围绕业务需求构建报告。

如果你与利益相关者密切沟通来构建你的报告,并且该报告确实对他们有所影响,他们会更多地使用该报告。

如果没有人使用这份报告,那还有什么意义呢?

你可以花一周的时间来构建一个包含大量参数和效果的精美信息图,但是可能没有人会使用它。

或者,您可以花三天时间收集您的业务所需的信息和正确数据,并将其显示在一个表格、一些过滤器和一个向下钻取的条形图中,每个人都使用它。

3.创建可重复使用的报告

Anton Maksimov 5642.su 在 Unsplash 上拍摄的照片

你不必重新发明轮子

我将重用我在以前的两篇文章中写过的一个技巧。

您不必每次创建新报告时都从头开始。

至少,创建可以在其他项目中重用的报告有两个积极的方面。

  • 你会更快进入项目的后期阶段

通过使用以前报告中可重用的部分,您可以更多地关注业务方面(提示 2 ),而不是如何设计报告中的所有内容。

  • 您可以更轻松地进一步开发报告(并可复制到其他报告)

如果您在创建报告时没有使用太多的遗留,您将有可能在以后的阶段更容易地进一步开发报告。

如果你在你的一个报告中这样做了,你也可以带着这些知识在将来开发其他的报告。

4.创建性能报告

舒佳官方Unsplash 上的照片

不要过度复杂的视觉化

来自 Data Revelations 的 Steve Wexler 是不要让你的可视化过于复杂的最著名的演讲者之一。

在商业环境中,用户体验是构建报告最重要的因素之一。

这意味着在你想让你的报告有多“高级”和你想让它们有多高的性能之间有一个重要的平衡。

您可以使用许多不同的参数和计算方法,以提供更好的用户体验,但这也可能会降低您的报告速度,反而会提供更差的用户体验。

当你拥有的数据可能比你在家里处理一些小数据集时要多得多的时候,你现在确实需要重新思考如何构建报告。

将计算和数据源筛选器向上游移动

另一方面,数据进入报告之前的工作也非常重要。

如果您可以在 Tableau 中处理数据之前限制数据并执行大量计算,您可能会有更多的回旋空间,还可以通过自定义过滤器、更多的仪表板操作等来增强用户体验。

总而言之,当你在真实的商业环境和大量数据中开始使用 Tableau 时,与在在线课程或社区环境中相比,性能将是一个更重要的因素

5.完美需要时间

照片由戴恩·托普金Unsplash 上拍摄

你想立刻拥有完美的一切…

当我第一次开始制作 Tableau 的时候,我很喜欢参与制作 Over Monday。

我认为这是一个很好的方式来习惯 Tableau 中不同的可视化技术,并且从其他更了解这个工具的人那里得到反馈。

对 Make Over Monday 的一个简短总结是,您应该在短时间内使用特定数据集创建(重新创建)可视化。

当我开始的时候,我真的很难知道我什么时候完成了可视化,因为我总是想在截止日期之前改进它。

…但重申在工作中是一件积极的事情!

在“现实生活”中,你会学到重复是一件好事,在第一次反馈会议之前不重复是应该的。

正如我前面提到的,与利益相关者的沟通是关键。
但是,当你开始着手下一个项目时,你几乎不可能确切知道它会有什么样的结果。

  • 你到底要处理哪些数据?
  • 提议的仪表板能与您拥有的不同维度一起工作吗?
  • 如果用新的数据源扩展这个项目,将来还会有更多的人对它感兴趣吗?

因为所有这些在项目开始时都是未知的,所以当你开始一个新项目时,重复是非常方便的。

更多会议=满足业务需求的更大机会

把它想成这样。你开始这个项目,最后期限是 10 周。你决定每三周举行一次反馈会议。

10 周后你会得到非常有用的反馈,就像 Andy 和 Eva 在《Make Over Monday》中的反馈一样,但你会得到三次反馈,而不是 10 周后只得到一次反馈,尽管不同角度的项目取决于会议期间的项目。

如果你从一开始就花 10 周时间在项目上,因为你想在反馈会议之前完成一个项目,那会怎么样?

结论

我在 Tableau 工作的五个专业建议是:

  1. 理论与实践:与您在在线课程和社区项目中看到的相比,您将更少地使用预先准备的数据,更多地使用数据治理,并思考如何在实践中引入数据。
  2. 业务需求应该放在第一位:在与利益相关者的交流中创建业务需求的报告,而不仅仅是创建你认为有趣的报告。
  3. 创建可重用的报告:使用早期报告的模板,以便能够更多地关注业务方面和引入数据,而不是仪表板的设计。
  4. 创建高性能的报告:不要因为可以而进行过度复杂的可视化,尝试将计算向上游移动,同时过滤数据。当你专业地使用 Tableau 时,性能是一个非常重要的因素。
  5. 完美需要时间:你想立刻让一切都变得完美…..但是在工作中反复是一件积极的事情。在产品感觉“完成”之前,不要犹豫创建原型并与利益相关者开会,这最终将是有益的。

感谢您的阅读

非常感谢你阅读我的博客!

在下面留下你对内容的看法,或者想讨论一下你从 Tableau 中学到的最好的一课是什么。

我很想听听其他人是如何看待这些事情的。

如果你喜欢阅读我的故事,并愿意支持我成为一名作家,可以考虑使用这个链接、 注册成为一名媒体会员,我可以赚取一小笔佣金,不需要你额外付费。

问题或询问

如果您有任何问题或建议,请联系我

领英:www.linkedin.com/in/elias-nordlinder
邮箱:Elias.Nordlinder@gmail.com
网页:eliasnordlinder.com

祝大家本周愉快
//伊利亚

我写技术文章的 6 个步骤

原文:https://towardsdatascience.com/my-6-step-process-for-writing-technical-articles-9d2f22026a5f

我写数据科学文章的过程、工具和技巧

克里斯汀·休姆在 Unsplash 上拍摄的照片

我发现每篇新文章的空白页都有点令人生畏。有这么多工作要做。你需要做研究,起草,编辑和添加图形。在某些情况下,你需要学习一些全新的东西。每个作者都会有某种过程来帮助处理这种工作量。

我的过程始于每篇文章的通用清单。一步一步地分解文章使得整个工作量看起来更容易管理。完成每一步也给我成就感,并帮助我保持动力。最后一个好处是,将某些任务分开让我成为了一个更有效率的作家。

我已经写了近 3 年的数据科学文章。在此期间,我发表了 33 篇文章,总浏览量超过 23 万。我最近还出现在《走向数据科学》的作者聚焦中。我投入了大量的工作来完善我的写作过程。想分享给大家,希望能对你的写作有所帮助。

这个过程已经发展成了您在下面看到的更加明确的步骤。我将带您详细了解 6 个步骤中的每个步骤。在右边,你会找到我用来帮助完成每一步的工具。我会讨论这些,同时,我会给你一些有助于我写作的建议。这些包括我如何写得更快,提出新的想法,让我的文章在移动设备上看起来不错。

6 步写作过程(来源:作者)

0)创意产生

在进入这个过程之前,我需要决定写些什么。随着时间的推移,我的想法来源发生了变化。在获得计算机科学硕士学位几个月后,我开始写作。最初,我在学位期间做的项目是一个文章的春天。艰苦的工作已经完成了。我只需要把它改编成一个好故事。

我之前一篇文章的例子是 深度神经网络语言识别 它是基于我为机器学习课程做的一个项目。分析的重点是预测一段文字的语言。这是使用以字符三元模型作为模型特征的深度学习来完成的。有一篇关于这一分析的文章,但是它假设在 ML 和 NLP 方面有一定水平的专业知识。

中等文章不只是给专家看的。在写文章的时候,我把分析改编成了编码教程。我花时间解释了一些基本的 NLP 概念,如文本清理和停用词。我还试图解释用于分析的代码。希望即使是经验很少的人也能理解这个教程。

回想起来,我认为我在大学做的工作是开始写作的最佳地方。我已经对我所学的课题了如指掌。写作是重温这些项目并向世界展示我所学到的东西的好方法。随着项目的枯竭,我需要寻找新的想法来源。我开始学习新的数据科学来写作。我发现写作是学习新事物的好方法。

当学习一个新的技术概念时,我的最终目标是写一篇关于它的文章。同时,我总是试图给我所写的领域增加一些价值。我需要将代码应用到新的数据集,创建新的可视化或提供一些其他独特的见解。为此,我需要很好地理解技术概念。这样,写作为我的学习提供了一些结构。

我发现写作有你写的文章之外的好处。我从写作中获得的知识和沟通技巧都让我在工作中受益匪浅。最近,我发现我在工作中获得的经验有益于我的写作。我所获得的实践经验自然为我所写的领域增加了价值。

比如我的文章, [分析机器学习中的公平性](http://Analysing Fairness in Machine Learning (with Python)) 我有幸和一个算法公平性的专家团队一起工作。关于这个话题有很多研究。与团队合作向我展示了什么是最有用和最实用的应用。在写这篇文章的时候,我试图尽可能多地传递这些知识。

(来源: flaticon )

随着时间的推移,创意的来源可能会发生变化,但有一点是不变的。一旦我开始写作,我总是会有新的相关想法。抓住一个话题总是会引出新的问题。对一个领域有了更好的了解,我也会知道如何增值。这意味着我通常会开始写一篇文章,即使我不是 100%确信它会增加价值。大海里有很多想法。我就一直写,让他们来。

1)研究和编码

一旦我有了想法,它会分成两类。要么是研究文章,要么是编码教程。 什么是 MLOps? 是一篇研究文章的例子。这些是非技术性的,旨在讨论概念。上面提到的语言识别文章就是一个编码教程的例子。这些旨在向你展示如何应用概念

这第一步将取决于文章的类型。研究文章包括阅读学术论文和书籍。我用媒体编辑器做笔记 app。在我做研究的时候,我会抄写一些文字。我会用引号把其他作者的作品分开。目标是收集相关的段落,稍后我会把它们提炼成我自己的话。

我如何将其他作者的作品分开的例子…

我的大部分编码教程都是 Python 的,我用的是 Jupyter 笔记本。最初,会有很多实验。我想解释如何应用一个概念,但同时,我想讲一个好故事。我将尝试不同的数据集,以找到一个能最好地解释一个概念的数据集。如果我找不到好的数据集,我将生成一个具有特定属性的数据集。我也将尝试相同结果的不同视觉化。我这样做是为了找出哪些是最有趣的,并清楚地解释这个概念。

我也对结果的顺序进行了思考。比如在 用 Python 介绍 SHAP我解释了 5 个不同的 SHAP 剧情。情节的顺序是有目的的。一些情节的弱点被前面的情节解决了。这提高了文章的流畅性。最后,笔记本中的结果将与最终文章的顺序相同。这使得下一步,创建一个大纲,更容易。

2)概述

一旦我对研究和代码的数量感到满意,我就创建文章的大纲。这是文章的骨架。它将包含所有的标题和副标题。它还将包含解释这一概念所需的所有图表。到大纲结束的时候,我会确切地知道我想写什么。这使得下一步,写草稿,尽可能快地进行。

对于研究文章,我将对第一步中复制的所有文本进行重新排序,以适应大纲。在研究阶段,我会从学术论文或书籍中复制整篇文章。我将这些段落移至正确的小标题,并删除任何不必要的文字。我也会开始在每一段上面加注释。这些是关于如何用我自己的话把它们表达出来并把来源联系在一起的想法。

对于编码文章,我将包括来自分析的任何数字。这些一般直接来自笔记本。也就是说,它们是使用 Python 包(如 matplotlib 或 plotly)创建的。我还会在每个图表上方添加简短的注释。这些概括了我每段要写的内容。

如果我想解释代码片段,我会使用 GitHub Gists 来嵌入它们。下面,您可以从语言识别文章中看到代码。这个代码被用来训练一个神经网络。比起将代码直接嵌入到介质中,我更喜欢使用这些。我喜欢不同的颜色用于组件,使其更容易阅读。

对于研究和代码文章,我将添加一些额外的图形。如果我认为它们会使一个概念更容易理解,或者如果它们会使文章读起来更有趣,我会添加这些。有了好的图形,我可以把一段详细的文字简化成“见图 X”。为了创建这些图形,我一般使用 谷歌幻灯片 。我不是平面设计师,但它能完成工作。你不需要花哨的工具来讲述一个好故事。

用谷歌幻灯片创建的图形(来源:作者)

到提纲阶段结束,我想对自己要写的东西有一个清晰的想法。虽然,我的轮廓从来都不完美。当我真正开始写作时,它通常会改变。我只是希望它包含足够的细节,使草案阶段尽可能顺利。正如我下面所讨论的,在写草稿的时候,我会努力进入一种流畅的状态。大纲应尽可能对此有所帮助。

3)草稿

提纲做好了,是时候继续写草稿了。这是文章的核心。老实说,这是写作中最无聊的部分。我希望我脑子里的想法能出现在纸上。作为一种妥协,我试图尽可能快地在页面上获得尽可能多的单词。我不担心拼写、语法或句子结构。这是在编辑阶段的后期。

真正的目标是尝试进入一种心流状态。软件开发人员可能对这个概念很熟悉。当你 100%专注于任务时。单词/代码会毫不费力地出现在页面上。我发现进入心流可以大大提高我写文章的速度。

流动状态的定义(来源:作者)

进入心流很难。这就是为什么我试图有一个好的轮廓。我不应该考虑太多我想写的东西。我尽量不去添加数字或查找我忘记研究的东西,以免打断我的写作。话虽如此,在你动笔之前,很难把你需要的东西都写在提纲里。

通常在选秀的时候,我会意识到缺少了一些东西。我可能需要做更多的研究,或者我可能会想到一个更好的数字来表达我的观点。为了流量,我就一直走下去。我为缺失的数字和图形留下占位符。一旦我完成了初稿,我就可以回到研究/编码阶段。

除了一个好的大纲,我一直在试验进入心流的最佳条件。我发现一些有用的东西是写作前的锻炼或冥想。这让我头脑清醒,可以集中注意力。我写作的常规和时间也很重要。对我自己来说,我发现最好的时间是运动后的上午 10 点到 12 点。在这两个小时里保持高度集中胜过在一天的其他时间里试图缓慢地写作。

写作前我也尽量避免分心。这包括社交媒体、电子邮件甚至 WhatsApp 消息。任何能让我走神的东西。如果我在一个嘈杂的环境中,我发现听白噪声很有用。这可能是过度的,但是保护流动状态是重要的。一旦你分心,你可能会失去它,很难再回到它。

4)编辑

我的草稿就像一大块大理石。现在是时候凿出里面的雕像了。在编辑阶段,我想提高可读性。我对句子进行了重新措辞和排序,使文章尽可能流畅。我尽量缩短句子,用更简单的词。我也删除任何重复。我的目标是用最简单的方式和尽可能少的词语解释一些事情。

如果我觉得我需要一些关于一篇文章的反馈,我会在编辑的时候试着去得到。这通常意味着将文章发送给家人或朋友。我问他们是否发现有什么难以阅读的东西,或者他们是否认为有什么遗漏。你的写作很难得到好的反馈。我发现人们太好了,或者没有足够的时间进行彻底的批评。

作为替代,我喜欢使用 海明威编辑器 。它使用机器学习来评估你的作品的可读性。如下所示,它还会指出你写作中可以改进的部分。我倾向于关注红色的“难以阅读”的句子。我将通过改写或分解句子来简化这些问题。

海明威编辑器分级(来源:海明威 app )

另一个有用的编辑工具是 语法上的 。我用的是免费版,可以很好地检查你的拼写和标点符号。具体来说,我使用的是 chrome 扩展。这允许我在媒体编辑器中使用语法。您可能希望在绘制阶段关闭扩展。我发现当我应该写作的时候,它诱使我改正语法。

总的来说,我尽量把起草和编辑阶段分开。我总是有一个编辑阶段。然而,在看了下面的视频后,我把更多的精力放在了分离步骤上。Ayodeji 说,为了提高他的写作速度,他戴着不同的帽子-一个是写作,一个是编辑。如果他试图同时做这两件事,写文章所需的总时间就会增加。

我的写作也有过类似的经历。实际的写作很无聊,完美是一种干扰。如果我花太多的时间去尝试表达事物,我会失去兴趣。相反,我试着进入心流,在那发生之前把我所有的想法都写在纸上。我可以稍后回来整理这些想法。我发现写草稿和编辑之间的休息也有利于编辑过程。我经常想到应该添加到文章中的东西或者更好的表达方式。

5)波兰

写作的最后阶段是润色文章。我想让我的大理石雕像发光。我添加了一些收尾工作,并试图找出我在编辑过程中漏掉的任何小错误。这些是语法学家没有发现的语法错误。

我可以一遍又一遍地读我自己写的东西而不发现这些错误。这就是我使用文本到语音转换的原因。我让我的设备在我阅读的时候读出我写的东西。当我听到语法错误时,它们就突出来了。大多数设备都内置了这一功能。在 macOS 上,您可以通过高亮显示文本并按下 控制+ESC来激活文本到语音转换。

我最不喜欢做的事情就是添加一些图像。这些与用谷歌幻灯片创建的图形不一样。它们无助于解释概念。它们只是为了让文章更有趣,并打破文本的连续段落。我通常用 flaticon 来表示这些。在本文中,我甚至使用了其中的一些图标。

(来源: flaticon )

6)职位

一旦我对最终的文章感到满意,就该发表了。我从不直接向媒体发表文章。相反,我试图把我的文章发表在一个出版物上。我通常和一起发表关于数据科学的文章。通过他们现有的受众和社交媒体,这些文章获得了更多的浏览量。

我也创造了自己的文章流量。我活跃在 T witter 上,我会在那里分享它。我总是试图创造一些对话,并获得反馈。比如下面这条推文。有几次我根据 Twitter 上的评论修改了一篇文章。在某些情况下,对话会产生全新的文章想法。

(来源:作者)

一些最后的提示

针对移动设备进行优化

媒体上 68%的流量来自移动设备。我不确定我的观众的确切比例。不管是什么,我还是想确保我的文章在手机上看起来不错。在我意识到我的一些文章很难阅读之前,我从来没有这样做过。

你可以在下面看到我的意思。这是我的文章 用 Python 介绍 SHAP中的一个图。这个人物有透明的背景。在媒体编辑器中(第二张图片),它看起来很好。不过手机 app 背景比较暗。这使得标签难以阅读(第一张图片)。

移动网站与桌面网站上的图像(来源:作者)

为了确保我不会遇到这样的问题,我在发布文章之前会在手机应用程序上查看。为此,我与自己分享了草稿链接。然后我在我的移动设备上打开这个链接。我确保在 Medium app 中打开。这是因为中型移动网站仍然有白色背景。

理解你的动机

弄清楚你写的动机。写的理由有很多。你可能想建立声誉,提高沟通技巧或赚钱。我在下面的文章中讨论了我的动机。理解写作的好处帮助我保持动力。我写作的具体原因也有助于我的方法。

当人们谈论成为一名成功的作家时,一个普遍的感觉是数量胜于质量。这意味着写更多的文章,而不是专注于使它们完美。我认为这个建议假设每个人都有同样的动机。那就是靠写作赚钱。

你可能想把写作作为学习新事物的一种方式。在这种情况下,不必着急。慢慢来。如果你想在某个领域建立声誉,在一篇文章上投入更多的努力是有意义的。一篇高质量的文章比许多低质量的文章能给你更好的声誉。最终,你想写的理由将决定你成功的原因。

要耐心

在你成功之前,需要时间和大量的努力。如果你用阅读你文章的人数来定义成功的话。我已经写了两年多了。我现在才开始看到好处的实现。关键是要持之以恒,不断努力提高你的写作水平。

也就是说,我觉得我才刚刚开始我的写作之旅。我一直在努力改进我的流程。因此,如果你知道任何工具技巧,请在评论中告诉我。

我希望这篇文章对你有帮助!你可以成为我的 推荐会员 来支持我。你可以访问 medium 上的所有文章,我可以得到你的部分费用。

https://conorosullyds.medium.com/membership

你可以在|Twitter|YouTube|时事通讯上找到我——注册免费参加 Python SHAP 课程

图像来源

所有图片都是我自己的或从www.flaticon.com获得。在后者的情况下,我拥有他们的保费计划中定义的“完全许可”。

作为数据科学家,我最大的错误是

原文:https://towardsdatascience.com/my-biggest-mistakes-as-a-data-scientist-so-far-d81277653c52

野外的数据科学和机器学习

作为数据科学家,我最大的错误是

我作为一名数据科学家所犯的错误,以及学到的宝贵经验

杰米·霍顿在 Unsplash 上的照片

我的错误

自从我获得第一份“正式的”数据科学工作以来,感觉已经过了很长时间。一路走来,我犯了太多的错误,吸取了太多的教训。我的目标是完全坦诚,这样我就可以对工作给出一个现实的看法,并向你保证犯错误是可以的,我们都会犯错误。这里是我犯的最大的错误(到目前为止),我希望你也能从中吸取教训。

假设机器学习就是一切

我认为对于我们许多人来说,我们对数据科学的第一次探索是通过机器学习。如果你和我一样,ML 可能是你在数据科学职业生涯中成功的一部分。我认为 MOOCs 对此负有部分责任,因为人们倾向于从零开始理解 ML 算法,并用玩具数据构建 ML 管道。老实说,我完全理解为什么,ML 是数据科学家工具箱中最强大的工具之一,由于所有的科幻电影,它也非常有市场。我从我采访的大多数初级数据科学家那里得到的问题是,“我什么时候会做机器学习?”。你可能会问,这种程度的热情有什么问题?

现在有一个难以下咽的药丸:在 FAANG 公司之外,大多数公司根本没有准备好全面的机器学习,即使在这些公司中,也有不同程度的 ML 成熟度。因此,如果你进入了一家不太成熟的公司,你该怎么办?

首先,将你的思维模式从构建机器学习模型转移一点,从尝试理解你正在处理的业务、他们的痛点、客户计划和目标开始。这并不容易,但是你很快就会发现在 ML 之外的其他领域,数据科学技能集可能是非常有价值的。

作为一名数据科学家,我做过的一些有影响力的项目都与建模和统计相关。这些通常支持帮助企业做出关键战略决策的一次性分析。我做过的一些非 ml 项目的例子包括定价优化、使用队列模型的网络模拟、使用逻辑回归的贷款结算分析、价格需求弹性模型和投资组合洞察仪表板。

作为一名优秀的数据科学家,在 ML 之外还有很多事情可以做。

照片由瓦尔瓦拉格拉博娃Unsplash 拍摄

未能推动你的工作方向

当我第一次开始的时候,我为此挣扎了很久。在任何一个新的角色或组织中,你都可能会感到迷失方向。你并不真正了解政治,你会遇到很多新的人,每个人似乎都有自己的议程,不幸的事情真的不胜枚举。

我回想起一个特殊的情况,我的一个渴望成名的同事代表我联系了一位投资组合经理。我不知道他们谈了些什么,但我的收件箱里收到了一封很长的电子邮件,列出了经理想要的一些财务报告。对我来说,这是一个尴尬的时刻,邮件的语气表明,已经在我不知情的情况下批准了这项工作。当然,报告是我们可以做的事情,但我马上发现,以这种手动方式构建这些报告不是对我们时间的最佳利用,除此之外,还有一个为 BAU 报告服务的分析团队。我暂时同意提交报告,部分原因是想尽早给人留下好印象。研究这个报告任务占用了我大量的时间,让我偏离了我原本要做的工作。我应该拒绝并让利益相关者去找我们的报告团队。

这里的教训是要记住你被雇用是有原因的,你是专家,你需要推动你如何与你的利益相关者互动。你有责任确定哪些项目将是最有影响力的,因此也是对你时间的最佳利用。你不必对收件箱里的每一个请求都答应。记住,每个人都有自己的议程,人们很乐意请你为他们做一点工作,只是因为这很乏味,你很快就会发现自己拥有这项工作。不要事事都答应。

试图煮沸海洋

当我被授予数据科学主管的职位时,我感到肩上责任重大。我相信,我需要通过交付最不可思议的东西来证明自己配得上这个职位,这些东西将为银行节省数百万美元。我的心绝对是好的,但是我太天真了。我收到的一个问题陈述是关于客户保留的,我立即开始研究提升模型、倾向模型和强化学习,试图建立一个一体化、自学式的 ML 解决方案来解决客户流失问题。我的有经验的读者可能会看到这种方法的问题。这将是复杂和昂贵的,你可能没有足够的数据。这都是真的,但只是在浪费了几天时间研究和撰写了一种方法之后,我才得出这个结论。

很久以前,我的一位经理曾经警告我们不要试图煮沸海洋。他会用浓重的西班牙口音说“约翰,我们不是要煮海洋”。每当他这么说的时候,我都会后退一步,重新评估我正在做的工作,并问自己“我付出的努力得到的回报值得吗?”有时候我们的思想比问题更复杂。在客户保持示例中,所需要的只是一次性的分析,以便企业能够更好地了解客户流失。

将复杂性误认为有效性是您成为昂贵且低效的数据科学团队的必经之路。

没有抓住风景

我敢打赌,我的一些读者已经犯了这个错误。不幸的是,我们大多数人都是通过直接经验了解到这一点的。当我说“把握前景”时,我不是在说理解业务或其产品,这是你从事的任何工作角色的基本标准。我说的是了解公司在数据科学和机器学习方面的成熟度。

我所经历的严格的申请过程让我认为这家公司比实际情况领先很多。只是在我加入之后,我才发现这家公司比我预期的要落后得多。如果你没有做好准备,在一家公司的早期阶段确实会面临一些尴尬的挑战,这些挑战会让你不知所措。除了获得合适的工具等显而易见的技术问题之外,您可能还需要教育您的利益相关者什么是数据科学或机器学习。

照片由丹妮拉·奎瓦斯Unsplash 上拍摄

这里的教训是,当你加入一家新公司时,你应该在面试阶段问正确的问题。我写了一篇博文,给了你一些问题,你可以问这些问题来更好地理解这里的风景。

</5-questions-you-need-to-ask-employers-at-data-science-interviews-a9ea10c45d6>

陷入杂草中

我是那种喜欢把手弄脏的人。我希望我参与的每个项目都有我的指纹,无论是构建原型、数据可视化还是其他任何技术性质的项目。这一部分来自于恐惧和焦虑。我花了多年的努力来获得我的技能,我担心我会失去我不用的东西。最重要的是,我真的不想仅仅成为一名人事经理。

我领导着一个数据科学团队,这意味着我的职责不仅仅是技术交付。我需要能够看到更大的画面,管理利益相关者,制定战略和方向,获得预算,并进行沟通。所有这些对于我们所做的任何工作的成功都至关重要。如果我太拘泥于细节,我学到的其他重要的事情也会受到影响。

好消息是,技术上还是可以参与的,但是一定要给自己设定适当的界限。最近,我可能会构建一个 PoC 来指导交付的技术方向,但我会让我的初级数据科学家来进一步开发它。

最重要的是要明白每个人都会犯错,对犯错的恐惧永远不应该阻止你去尝试。勇往直前,勇敢犯错误,如果你从中学习,你将继续成长为你的目标,成为有能力的数据科学家或机器学习工程师。

⭐️ 我喜欢通过分享我在野外的数据科学经验来帮助人们。如果你还不是会员,可以考虑订阅 Medium,从我这里获得更多有用的内容。

https://johnadeojo.medium.com/membership

我的持续集成花费了太多时间。我如何修理它?

原文:https://towardsdatascience.com/my-continuous-integration-takes-too-much-time-how-do-i-fix-it-704c5a674191

缩短 CI 运行时间的方法。衡量、优化、利用隐藏的资源,等等。

xkcd 作者混音的漫画。授予了显式使用权限。

我们都经历过这种情况:发送另一个 git push 到 pull 请求,并在 30、40、60 分钟内等待所有触发的 CI 检查完成。同时进行多任务处理或阅读一些 r/编程。太烦人了(不得不一心多用,也就是说)。我们能做得更好吗?我们来调查一下。

下面是一个典型的 CI 作业管道:

  1. 启动执行程序。根据配置项的不同,它可以是容器、虚拟机或云实例。
  2. 获取检查过的源代码。一个git clone
  3. 有效负载的设置:安装依赖项、编译器、linters 等。
  4. 建造。对于像 Python 这样的解释语言,这可以是项目包的一个pip install
  5. 有效负载:运行单元测试,验证,确保代码风格等。
  6. 提交工件。

我们可以在每一个阶段进行优化。此外,还有一个元优化,我将在最后描述。

邪恶的执行者

启动执行程序是所有阶段中最可疑的阶段。用户的杠杆很少。需要注意的一件重要事情是,硬件可能会在不同的运行之间发生变化。比如云 GitHub 动作在不同的 CPU 上运行;有些缺少 AVX2 这样的流行指令集。IOPS 也非常。我不建议滥用 GHA,但有可能重新触发几次乔布斯,以赢得最快的机器。

开源构建工程师经常忽略执行器经常拥有多个内核。特拉维斯有两个GHA 有两个天蓝色管道有两个。我见过在几个线程中进行make -j2或执行单元测试的 CI 脚本。

容器化的执行器通常比传统的虚拟机启动更快,所以如果你的 CI SaaS 提供选择,不要在不必要的时候选择虚拟机。

清晰的代码获取

快速获取项目代码的黄金法则是尽可能少地下载。一些配置项抽象了这个阶段,尽管每个配置至少允许设置克隆深度。我迄今为止最好的一次投篮是

git clone --single-branch --branch <sha> --no-tags --depth 1

—通过跳过除我们正在构建的提交所引用的对象之外的所有 Git 对象,减少网络传输。GitHub Actions 在git fetch期间进行等效的优化:

git init .
git remote add origin [https://github.com/...](https://github.com/...)
git -c protocol.version=2 fetch --no-tags --prune --progress --no-recurse-submodules --depth=1 origin <sha>

更快的设置

加速设置的普遍建议是缓存所有内容。

  • 缓存已安装的包/模块等。(Python,Ruby,Node,Golang)。
  • 我不熟悉 Java 和 C#,但我打赌它们也有一些值得坚持的东西。
  • 缓存 docker 层并利用 BuildX 特性。

我知道两种缓存 docker 层的方法:简单的和终极的。简单的方法提炼出

  1. docker load layers.tar
  2. docker build --cache-from <image>
  3. docker save <layers> -o layers.tar

这有两个问题:它们很慢,而且容易出现每次构建后layers.tar增长的滚雪球效应,所以<layers>应该小心选择。BuildX 消除了对docker load / store的需求,因此速度更快。

最终的方法是从一个定制的容器注册中心拉取和推入。然而,人们仍然不得不偶尔从旧的陈旧层中删除注册表。

更智能地构建

增量编译(C/C++,Rust)。但是请记住变化的 CPU——如果没有固定所需的 CPU 属性,您可能会被抓到。

利用两个 CPU 内核(参见“邪恶的执行器”)。

用 Python ( pip install -e .)中的开发安装节省一些时间。

避免一次性构建所有内容。例如,分离出独立的包并将它们卸载到设置阶段。

“内爆”有效载荷

使用增量测试运行程序,根据差异选择要执行的测试。缺点:测量测试覆盖率变得更加困难。另一个缺点是:Python 没有什么好处,在每个模块中导入半个世界是很常见的。

在多线程中执行单元测试。IO 绑定的测试套件应该过量使用线程,例如,我在两个内核上启动了四个线程,取得了巨大的成功。

以下内容有些显而易见,但值得一提:不要硬编码sleep() -s。首先,因为总会发生硬编码值不够的情况,其次,因为这纯粹是浪费资源。如果你真的需要睡觉,把 tiny sleep() -s 和出口支票一起绕成一个圈。

如果您的 CI 是按时间而不是按并行执行者来计费的,或者您的项目是开源的,那么您可以将单元测试分散到几个任务中,这样就不用在一个任务中执行 100%的测试,而是启动 5 个任务,每个任务执行 20%的测试。

给定作业时间公式 C + k * W,其中 C 是常数因子(引导、获取、设置),k 是线性因子,W 是已执行单元测试的量,如果将 W 分成 N 个大小相等的部分,CI 时间将减少到 C + k * W / N, 并且您将为 C * N + k * W 付费。增加 N 直到计费开销增加到可比值 C * N ~ k * W 才有意义,因此 N ~ k * W / C。例如:100%的测试需要 40 分钟,C 是 2 分钟。 N ~ 20。新的 CI 时间为 4 分钟。新的计费时间是 80 分钟。然而,一些统计水下石头建议较低的 N,进行到下一节。

元考虑

总有一个关键 CI 作业最后完成。下图应该看起来很熟悉:

总有一个关键 CI 作业最后完成。图片作者。

令人惊讶的是,仅仅优化平均作业运行时间是不够的。如果你能额外减少标准差,那将是最好的。以下是一些臭名昭著的例子:

作业运行时间的高标准偏差对总体 CI 持续时间的不利影响。作者图片。

夸张地说,假设我们有 20 个独立的 CI 作业,每个作业 90%的概率需要 5 分钟,10%的概率需要 10 分钟。所有工作在 5 分钟内完成的几率正在消失 0.9 ⁰≈12%.所以我们将在剩余的 88%中等待 10 分钟,即使平均作业运行时间只有 50.9+100.1=5.5 分钟。随着独立竞争情报工作数量的增加,挫败感也在增加。这种情况类似于大数据处理:数据中总有一个棘手的边缘怪癖会让你的 Spark 工作崩溃,让你从头开始。下面有一个奇特的理论,但我不会用数学公式来淹没读者。

我对保持低标准差的建议是:

  • 限制对 web APIs 的请求。
  • 依赖尽可能少的第三方。例如,重定向 pulls 到私有容器注册表,而不是探索公共容器注册表的速率限制。
  • 比起磁盘 IO,更喜欢内存缓存。
  • 处处设置严格的截止日期。
  • 监控指标。

最后一点让我特别感兴趣,因为我的公司正在开发一个产品来计算和分析这些指标。所以我想出了以下三个:占有率,临界占有率,不平衡。

占有

占用率指标是作业运行时间总和与作业数量和最长作业运行时间的乘积之比。该比率始终介于 0 和 1 之间。0 表示绝对的资源利用效率低下,1 表示理想的效率。

占用率计算示例。作者图片。

入住率反映了 CI 套件整体的“密集”程度。我们(数百名)客户的平均 CI 占用率(第 95 百分位)为 0.58

临界占有率

关键占用率指标与常规占用率非常相似,只是我们丢弃了非关键类型的作业。如果至少有一个作业完成了父配置项套件中的最后一个作业,我们称该作业的类型为关键。根据我们的三个例子,关键的工作类型是不同环境中的单元测试。此外,docker 构建在第二个示例中也很关键。

临界占用率计算示例。作者图片。

关键占用试图排除快速,轻量级的工作,如林挺或建筑文件,只留下那些影响整体持续时间。这可能是对标准差严重性更公平的估计。

我们客户的平均临界入住率(第 95 百分位)为 0.61

不平衡

最后一个指标是最简单的:不平衡是最长和第二长的作业运行时间之差。

不平衡计算的例子。作者图片。

不平衡反映了通过优化关键作业来减少总体 CI 套件持续时间的机会有多少。如果不平衡值只有几秒钟,就很难通过“微优化”来加速 CI 反之,如果值几分钟,游戏就得不偿失了。

我们的客户平均不平衡(第 95 百分位)为3 分 48 秒

警告

上面的 CI 图只是一个简化的模型。实际上,可能会有作业在其他作业完成时开始、隐式依赖 Dag 等。幸运的是,绝大多数不会配置如此复杂的场景。

像其他指标一样,占用率和不平衡可能是无意义和无用的。入住率可能在 0.4 左右..0.7,没有可操作的原因,或者当有几个相同的关键作业时,不平衡可能保持接近于零。它们就像软技能:有时有效,有时无效。

摘要

我将一个典型的 CI 工作建模为一系列的多个阶段:引导执行器、获取代码、设置环境、构建、执行有效负载、提交工件。然后,我为这些阶段提出了一些优化来减少运行时间。最后,我描述了“元”CI 优化,它降低了运行时间的标准偏差,并提出了三个相关的指标:占用率、临界占用率和不平衡。

像往常一样,我将感谢任何反馈和更正。请在媒体上关注我,以获得我下一篇帖子的通知。我写的是应用于软件开发工件的 ML/DS,Python,PostgreSQL。

另请阅读:我们如何优化 Python API 服务器代码 100 倍我们如何优化 PostgreSQL 查询 100 倍

如果你是一个渴望建立持续改进软件开发文化的工程领导者,请查看我们的 SaaS 产品网站。雅典人衡量 GitHub 上任何 CI 的占有率和朋友。

我的数据科学求职

原文:https://towardsdatascience.com/my-data-science-job-search-6deb4117e7b5

哪些有效,哪些无效,以及实用技巧

埃里克·普劳泽特在 Unsplash 上拍摄的照片

经过十年的高等教育、实习和工作之旅,我经历了系统生物学、清洁能源和电子显微镜,最终找到了自己的职业定位:可再生能源领域的数据科学家。你可能和我一样,一个差点不小心沦落到这里的人,没有受过正规教育(虽然我做了几年数据科学家后确实回到了学校)。去年夏天毕业后,我有一次寻找数据科学工作的有趣而深刻的经历,我想我应该分享一些我尝试过的事情,希望你能从中收集到一些对你有帮助的东西。这绝不是让你找到理想工作的“银弹”解决方案,而只是我在一个不断发展和竞争激烈的行业中的一些想法。

找到你的位置

有两个“啊哈!”我找工作的瞬间。第一个时刻幸运地发生在一周之内。数据科学是一个如此多才多艺的工具,以至于你几乎可以找到任何类型的公司想要雇佣某人。但是你想为一家大的科技公司工作吗?你的激情是在营销上吗?或者也许你和我一样喜欢解决工程问题。当然,我申请了几份只有“数据科学”头衔的工作,但是很多都是在我不感兴趣的公司。我的背景不一致,我的激情也不一致,他们能看出来。我没有收到他们任何一个人的回电。这里的建议是决定你想在数据科学职业生涯中做什么,找到你的激情,并申请这些类型的工作。

在搜索接近尾声时,工作邀请开始到来,但最令人兴奋的是一位雇主,他从第一次面试就告诉我,“在我们收到的数百名求职者中,你是我们最兴奋的人!”这并不是因为我有最令人惊讶的简历,而是因为这是一个真正的利基行业(半导体设备制造),我有理由着迷。

简历提示

第二个“啊哈”时刻发生在几周前,当时我申请了很多工作,但没有得到多少回应。我修改了我的简历,突然我一周内得到了两倍的面试机会,而申请的工作却更少了。以下是我做的一些具体的事情:

  • 我创建了一个专业网站,在那里我放了一份职业陈述,我的完整简历,以及一些我参与和撰写的项目和文章的链接。你可以在这里看到。
  • 在我的简历上,我在顶部加入了我的专业网站、LinkedIn 个人资料和 Github 的链接。
  • 关键词改造!嘿,你的机器学习/数据科学职位的雇主最有可能使用 ML 来整理简历。让他们知道你能胜任 ML 的最好方法是什么?认识到他们可能在进行关键词搜索,在你的简历中充斥那些特殊的关键词,比如“数据科学”、“github”、“AWS”,或者你拥有的任何其他技能。
  • 修剪脂肪。我总是在简历中保留一个名为“额外技能和兴趣”的小部分,在那里我会提到一些爱好。虽然这对于偶尔采访我的马拉松选手或音乐家同伴来说很棒,但它只是占用了宝贵的空间,对关键词搜索没有任何贡献。所以把绒毛拿出来。如果你被录用了,你会有足够的时间来联系你的爱好。如果可能的话,保持你的简历简洁。

我不确定这些变化中的哪一个突然引起了这么多雇主的注意。也许有一天当我在面试的时候,我会让你知道。但是这次简历修改把平均每周 50 份工作申请和 2-3 次面试变成了 20-30 份申请和 5-8 次面试。

追踪你的进度

如果没有跟踪我的申请和面试,我不会意识到这些“啊哈”时刻。每个人都有自己的体系;我会分享我的,但对你来说,做一些对你有用的事情很重要。

在我分享我的跟踪系统的细节之前,我想讲一个我找工作的快速经验。在我找工作的前几周,我在学校,没有认真找工作。我申请了很多我认为的工作,却一次面试机会都没有,这让我很沮丧。嗯,我没有跟踪我的申请,当我开始时,发现我申请了不到 10 家公司!现在,你们中的一些人只需要申请 10 个就能找到工作,但这真的只是一个数字游戏。通过跟踪你的应用程序,你可以看到什么是有效的,什么是无效的,如果你认为自己比实际做得更好或更差,给自己一些现实。

我个人在 Excel 电子表格中记录了所有东西。我有一个关于公司名称、职位名称和我在哪里找到这个职位的专栏(LinkedIn,实际上是公司网站,等等。).我有一栏是我申请的日期,5 栏是我面试的日期(我经历的最多的面试是 6 次),然后是“兴奋度”栏和“备注”栏。因此,当我申请一份工作时,我会记录基本信息,以便他们联系我进行面试时我可以追踪到它,给它一个 1-5 分的“兴奋度”,并写下我申请的日期。如果我有面试,我会记录日期和任何笔记。然后我写了一些 Excel 公式来跟踪我申请了多少份工作,我平均对那些工作有多兴奋,看看平均有多少份工作导致了一次面试,并做出曲线图。如果你对一个模板感兴趣,你可以在这里下载它

示例工作申请电子表格的屏幕截图。作者的电子表格和截图。

最后,我建立了一个积分系统。我没有说“本周我将申请 20 份工作”,而是制定了一个目标来获得 X 分。对于我填写的每一份申请,我都会得到与兴奋度相对应的分数(这激励我申请你更感兴趣的公司,这些公司往往会做出更积极的回应),然后第一次面试 6 分,第二次面试 8 分,之后的任何面试 10 分。这个系统帮助我在面试时继续申请工作;如果我有很多面试,我会平静地接受这样一个事实,那就是我那个星期提交的申请没有那么多,而如果我没有很多面试,我可以提交很多申请。这个系统需要一些迭代来开发,所以继续下去,根据需要继续修改它!

学习适销对路的技能

在我找工作的时候,雇主们喜欢听到的一件事就是我在继续学习技能。比如我之前用过云计算资源,但不是专门用 AWS。嗯,我碰巧住在西雅图,在地铁上看到一些西雅图 AWS 技能中心免费课程的广告,所以我报名参加了一些。网上有很多材料,你甚至可以把代码片段或项目发布到你的 Github 上给雇主看。这一点是要承认,如果你只是整天申请工作,你可能会筋疲力尽。通过从事富有成效的项目和技能来改变现状,你不仅会在找工作时找到更多的满足感和多样性,还会获得脱颖而出的技能。练习一些编码题也是个不错的主意。我个人的经验是,大约四分之一的面试有编码部分,但这可能是独一无二的,因为我更多地是在寻找重视化学工程方面而不是编码方面的工作。

“给人留下深刻印象的着装”问题

如果你从事数据科学或机器学习,你可能是在申请远程工作。你的采访都是远程的。你可能会读到很多帖子,说你应该如何穿着才能给人留下深刻印象。这可能是个不错的建议,但我的经验是这没关系。我参加过一些面试,在这些面试中,我穿了一件有纽扣的衬衫和领带,在另外一些面试中,我只穿了一件漂亮的 polo 衫。很明显,你不想看起来像个邋遢鬼,但我收到了我盛装打扮的公司和我只穿了一件 polo 衫的公司的工作邀请。

感恩日记

每个人都明白:找工作很难。如果你正在读这篇文章,你可能是那些没有得到一切的人之一。你的求职可能令人沮丧。在我找工作的时候,我当然去过那里很多次。

几周后,我决定每天写下一件我感激的事情。有时这是一次很好的面试,有时这是一份让我兴奋的新工作,有时这是一句半开玩笑的话,比如“我很感激这一家公司没有给我一份工作,因为现在我意识到,它可能不适合我!”有些日子可能超级难找到值得感恩的事,但总会有。这样做有助于我在漫长的过程中保持清醒,如果你从这篇文章中学到了什么,我希望就是这个。

利用你的关系!

我会第一个承认我不是告诉你怎么做的最佳人选。但是我会用我大学时代的一个好朋友和同学作为例子。他已经毕业,在将近一年的时间里,他很难找到一份工程方面的工作。他决定给 AIChE(美国化学工程学会)的主席打电话,告诉他自己的故事。他不仅能够建立联系,而且能够利用这种联系找到他的第一份工作,并从那时起拥有了一份伟大的职业生涯。

所以利用你的教授,利用你的朋友。这可能很可怕,你可能会觉得你在占用他们的时间,但我相信你会发现他们不仅愿意,而且渴望帮你找到工作!这也可能有助于寻找本地工作——全国甚至世界各地的每个人都可以申请任何偏远的职位,所以可能很难脱颖而出;如果公司想找一个本地人来管理办公室,竞争范围会小得多。

这就是我的全部,而且对我很有效。我们都是不同的,有不同的背景和职业目标,所以这可能有用,也可能没用。如果是的话,我很乐意在 LinkedIn 上联系。一如既往,欢迎关注我的【T2 走向数据科学】,查看我关于数据科学案例研究的定期帖子。

我参加认证分析专家考试的经历

原文:https://towardsdatascience.com/my-experience-from-the-certified-analytics-professional-test-232f34a2d148

我参加认证分析专家(CAP)考试的一个小故事

斯科特·格雷厄姆Unsplash 上拍照

免责声明 : 认证分析专家(CAP)通知提供的认证。虽然我是 INFORMS 的普通成员,但我与 INFORMS 没有任何其他联系。另外,请注意,如下所述,我在 2021 年注册了 CAP。从那时起,事情可能已经发生了变化。

介绍

一段时间以来,我一直在寻求获得数据分析证书。理想的证书应该:

  1. 进行在线考试,
  2. 与证明我已经完成课程的证书不同,
  3. 不是特定于技术、供应商或工具的。

要求(2)排除了 Coursera 之类的证书。不管在线课程有多好,我想要的是不同于在线课程的证书。我也想在短时间内获得一些东西,因此从一些大学获得一年(或更长时间)的课程也是不可能的。更不用说讨论这些项目的价格了。

要求(3)排除了 Azure 数据科学家协会、Hortonworks 的 HDP 认证开发人员认证、Qlik Sense 业务分析师认证等认证。不要误会我的意思,我不看不起以上任何一个。事实上,希望在未来,我会试着得到其中的一个(或更多)。那就是在给定的时间里,我想优先考虑一些不同的事情。

因此,在 2021 年年中,当我的雇主提出支付认证费用时,我决定接受 CAP。

关于认证分析专家认证计划

如 CAP 候选人手册所述,认证分析专家(CAP)是由 INFORMS 提供的认证计划。INFORMS 建立了一个流程来识别“有效提供(分析)服务所需的知识、技能和能力”。该过程产生了一个候选人测试的以下域列表:

  • 商业问题框架
  • 分析问题框架
  • 数据
  • 方法选择
  • 模型结构
  • 部署
  • 模型生命周期管理

申请 CAP

应该注意的是,为了能够参加考试,必须满足某些要求。一个人应该接受 CAP 的道德准则,有教育证书和工作经验。详情可在 CAP 的网页上找到。于是,我创建了一个账号,提交了学历证明,提供了雇主的联系方式(后来我才知道确实联系到了我的雇主)。过了几天,我收到一封邮件,说我有资格参加 CAP,并支付了考试费。现在,我所要做的就是准备考试,然后通过考试。

为 CAP 学习

我接着下载了学习指南。快速地看了一眼,然后让它安静地躺在一个文件夹里。我的意图是在暑假期间为考试而学习。

在我休假的前几周,我打印了 CAP 的学习指南(我担心我是守旧派,如果可能的话,我更喜欢从硬拷贝学习)。我也在网上搜索了一些相关的课程。通知提供一些,但我在寻找更便宜的东西。Udemy,一个经济在线课程的普通来源,什么都没有。LinkedIn 有两门课程,我很幸运地被免费提供了一门。我看了它,但对我来说它似乎太排序,因为它在一两个小时内覆盖了几乎一半的领域。(课程很好,但在我看来,它主要可以作为复习或进一步阅读的指南)。

得知我的暑假学习计划进展不顺利并不奇怪。事实上,暑假后我计划为 CAP 学习到圣诞节,并在我的圣诞假期参加考试。

长话短说,到圣诞节时,我已经读完了 CAP 的学习指南。一方面是想尽快结束考试,另一方面是担心会失去考试机会(注意:我从来没有收到通知说要在某个特定日期之前参加考试,所以这种担心可能是没有道理的),我决定在新年后马上预订考试日期。我有点乐观,认为我有机会。这是因为当我在学习指南中测试自己时,我可以回答大多数问题。

注:我看来,上限网站进行了重大检修。现在有一些免费的网络研讨会可以帮助准备考试。

参加考试

在预约考试时,人们被要求下载一个特定的程序并在网站上注册。注册包括在键盘上敲几下句子,然后拍照。我在我的工作电脑上做了这个。然后,我决定改用我的个人电脑,因为我对考试程序与我的工作电脑上的安全软件的交互方式感到不舒服。

事实证明,这是一个正确的决定,因为即使在我的个人电脑上,我也必须关闭杀毒程序,才能让考试软件正常工作。不幸的是,我使用不同的键盘输入的内容没有被识别。谢天谢地,技术支持可以重置我的帐户,重新注册后,我准备好了。我还应该补充一点,似乎在整个考试过程中,他/她的摄像机都是活动的,他/她受到监视。事实上,当我的视频停止时,我就被打断了。

结果—最终想法

在我完成测试后,我被告知结果。我成功了!我还得到了每个独立领域的性能分析。

总之,我相信 CAP 完成了它的设计目标。尽管准备不足,我还是设法通过了考试,因为我必须依靠我的经验。此外,按领域划分的分数反映了我的工作经验。特别是,我在我工作较多的领域得分较高。它清楚地显示了我的优势和我需要学习的地方。至于对我的职业生涯有多大帮助,现在说还为时过早。我将不得不更新这个帖子。

我在谷歌、Meta、亚马逊的面试经历

原文:https://towardsdatascience.com/my-experience-interviewing-with-google-meta-amazon-2f0c8c9a2772

观点|与大技术的工程访谈

我学到了什么,以及你如何才能得到你的工作机会

杰斯温·托马斯在 Unsplash 上的照片

2021 年和 2022 年上半年是科技工作者的神奇时期。
疫情的经济复苏,“大辞职”和强劲的股市为软件工程师创造了几十年来最好的就业市场,疯狂的薪酬和令人敬畏的福利,如完全远程工作和无限的带薪休假。

尽管我喜欢 Glovo 后端工程师的工作,但我决定回到工作岗位,重新开始面试,我意识到这种难以置信的时期不会永远持续下去,我必须抓住这个机会。

尽管我只有不到两年的工作经验,也没有学位,但我还是经历了几个大公司的招聘过程,比如谷歌、Meta、亚马逊、Adyen、Skyscanner 和 Atlassian,并且收到了很多不错的邀请;以下是这次经历的总结。

随着许多科技公司经历一轮又一轮的裁员,伟大的辞职期似乎已经远去。照片由 Clem OnojeghuoUnsplash 上拍摄

面试非 FAANG 公司

像谷歌或 Meta 这样的公司因其极具挑战性的面试而臭名昭著,但不要以为其他顶级公司的面试过程很容易,我认为 Atlassian 和 Skyscanner 的系统设计是我经历过的最具挑战性的步骤。

Adyen 是一家总部设在阿姆斯特丹的金融科技公司,以高薪酬和强大的工程文化而自豪,它是我面试的第一家公司,在开始大多数其他流程的几个月前,当我还没有准备好任何角色时,一位招聘人员就联系了我。
我面试了一名专注于数据角色的后端工程师,并通过了带回家的面试,面试包括一个普通的算法问题、一个 SQL 问题和一个要求我构建整个算法交易项目的最终任务。

虽然我在带回家的面试中表现得足够好,但我在现场面试中缺乏准备,很快我就收到了拒绝。

Atlassian 是负责开发吉拉、Confluence 和 Trello 等超级流行工具的组织,也是最值得工作的科技公司之一。
我通过推荐申请,一位招聘人员联系了我,他告诉我,由于缺乏足够的经验,我将被降级为初级。

我开始证明自己配得上一个中层职位,在算法轮次中,我表现得如此出色,以至于面试官极力要求我被考虑担任中层职位。

所以我被保留给非初级候选人的系统设计面试录取了,但我真的很挣扎,因为面试官是无情的,他不接受任何模棱两可的答案,并强迫我真正加强我的分析;回想起来,这是一次很棒的经历。

我不确定我做得够好,但谢天谢地,我得到了积极的反馈;尽管如此,我还是决定不从事行为部分,因为当时我能申请的唯一角色是在波兰,而我有兴趣在阿姆斯特丹或伦敦工作。

Skyscanner 是最受欢迎的航班搜索引擎之一,也是我经常使用的一款产品,我非常乐意接受他们的采访,我非常感激这个过程中的每一步。

该公司的一个特点是,你需要编写代码的管道的唯一部分是预筛选过程,该过程的其余部分非常专注于文化适应和系统设计,有两个行为和三个系统回合。

他们的面试总体来说相当困难,招聘人员向我承认,并不是小组中的每个人都同意给我及格分数,但是唉,他们还是决定给我这份工作!

这是我今年开始面试以来的第一份工作,它是巴塞罗那中级后端工程师中的佼佼者,诚然非常慷慨,但我决定拒绝它,而选择 FAANG。

Arthur Osipyan 在 Unsplash 上拍摄的照片

谷歌

谷歌的招聘过程从头到尾都是一片混乱。有趣的是,我通过网站 levels.fyi 的在线论坛和一个很棒的介绍电话联系到了该来源,并被转到加利福尼亚州森尼维尔的一个初级职位的面试,而没有经过在线评估。

事情开始变得奇怪了。我被介绍给一个招聘人员,从我的来源,并提交了我的虚拟现场可用性,直到我被告知这是一个错误,早期职业角色的来源需要提交一张票,等待系统匹配我,而不是自己找到一个招聘人员。

她照做了,我等了好几个星期,在请求每周更新的同时,我也在等着票的进展。

该消息来源声称自己无能为力,甚至无法查看机票的状态以及它为何停滞不前,最终最有可能的原因是我缺乏三年的经验和大学学位。

最后,她告诉我,她会将我的情况上诉给她的上司,但从那以后我再也没有收到任何答复,我将这次经历标记为拒绝并继续前进。

马頔·索罗明在 Unsplash 上拍摄的照片

梅塔的面试过程也很特别;我有在该公司工作的联系人,当我完成准备工作后正准备申请时,该公司在 2 月份冻结了几乎所有的招聘,我认为错过了机会,至少在 2022 年。

幸运的是,仍然有一些非常专业的职位空缺,如机器学习和生产工程师,加上一些非常有趣的发展机会,如“发现生产工程计划”。

生产工程师的角色介于软件、站点可靠性和平台工程之间,主要致力于开发可扩展至数十亿用户的可靠、高效的系统。

面试流程由通常流程的缩短版组成,只有四个步骤,包括在线预筛选、算法面试、系统设计/操作系统回合和最终行为部分。

我为这些面试做了疯狂的准备,就像许多其他有抱负的软件工程师一样,自从我开始写代码以来,为 FAANG 公司工作一直是我的梦想,我决定不会错过这个机会。

谢天谢地,面试相当容易,除了操作系统面试,我不得不在一个周末学习整个现代操作系统手册。

最终我得到了一份工作!它包括一份为期一年的合同,在加入一个全职团队之前,我将在伦敦办事处接受为期六个月的生产工程培训。

问题是,尽管我的薪水相当于一名中级工程师,但在我完成合同并获得新的全职工作之前,薪酬不包括任何股票,这取决于我的表现达到预期水平。我试图通过谈判来解决缺乏公平的问题,但这个角色根本没有谈判的余地。

这有点令人失望,但我意识到,包括福利在内,我将获得比现在多一倍的收入,所以最终我愉快地签下了合同,没有回头。

照片由克里斯蒂安·威迪格Unsplash 上拍摄

亚马孙

让我们解决房间里的大象,亚马逊作为雇主有一个可怕的名声,滥用和管理不善据称毒害了从仓库工人到工程人员的整个组织。

我自己过去在亚马逊面试时有过负面的经历,很多在那里工作的人也告诉我,那里的条件可能很糟糕。鉴于这一前提,我认为亚马逊是很好的面试实践,也是我在其他谈判中用作筹码的潜在还价。

我申请了巴塞罗那的一个中层职位,由于日程安排非常缓慢,整个过程从开始到结束花了几个月的时间,但我总体上很喜欢面试,并与面试官一起工作,特别是那些位于巴塞罗那的面试官。

关于亚马逊的另一个常见说法是,任何人都可以加入该公司,虽然他们的招聘人员确实非常积极地向每个人发送空缺职位的邮件,但面试过程非常严格,总体而言,这可能是我职业生涯中迄今为止最困难的选择。

我经历了在线评估、电话筛选,然后是由四轮组成的虚拟现场,第一轮是系统设计,其他是算法问题。

亚马逊现场的特殊性在于,每个面试官都会问你很多基于亚马逊领导原则的行为问题。这些都不是简单的问题,需要大量的准备和快速的思考才能令人满意地回答。

最后的反馈是积极的,我以优异的成绩通过了技术部分,但我对领导原则的回答显示我缺乏成为亚马逊中层所需的经验,因此,团队决定将我的职位降级为初级,但仍然给我这个职位。

这个报价仍然很高,甚至可以和 Skyscanner 的中级报价相媲美,超过了我对一个巴塞罗那的大三学生的期望。

也就是说,当时我已经接受了 Meta,继续做下去对我来说更有意义,但我离开时对雨林公司有了更好的看法。

伊斯雷尔·安德拉德在 Unsplash 拍摄的照片

外卖食品

申请和准备面试就像一份全职工作一样要求严格,有时我确实感到不知所措,处理我的工作,有时连续三天六轮。

理想的情况是同时获得多个报价,并在谈判过程中以此作为杠杆;虽然 Skyscanner 和 Meta 的报价很接近,但我仍然没有机会谈判,因为这两个报价都是最高的。

另一个有趣的收获是,一旦你进入面试阶段,你的课程和正规教育几乎无关紧要,没有人在乎我没有学位,尽管我在理论上有足够的经验,但我被亚马逊降级,被 Atlassian 升级,因为我能够在面试中证明自己。

一旦你迈出了第一步,你的知识和技能就是最重要的,不管你有多少年的经验、学位或训练营

我还想提倡练习的重要性,如果你在面试时惊慌失措,最好的准备也可能不够。

出于这个原因,我主张只要有机会就要经常练习面试,即使你对这家公司不感兴趣,你也应该考虑经历这个过程,这样当去你梦想中的公司面试的时候,你就不会因缺乏经验而痛苦。另外,你永远不知道你的最佳报价会从哪里来。

最后但并非最不重要的一点是,在久而久之越来越流行的软件开发中,沟通是一项至关重要的技能。

面试官很清楚这一点,他们会像关注面试内容一样关注面试的形式。我不能夸大清晰、令人信服地表达自己的重要性。

感谢你到目前为止的阅读,让我知道你是否对我写的其他文章感兴趣,比如如何准备技术面试,或者如何作为一名自学成才的工程师脱颖而出,或者看看我以前的帖子:

我感谢任何问题、评论和建议,无论是在这里还是在 Linkedin 上。

我在这个行业的第一份机器学习工作——这是我学到的东西

原文:https://towardsdatascience.com/my-first-machine-learning-job-in-the-industry-here-is-what-i-have-learned-49b1633b8eb1

在过去的几个月里,我作为一名机器学习顾问,为一家令人惊叹的医疗人工智能初创公司工作

马文·迈耶在 Unsplash 上的照片

在花了一年时间构建我的数据科学简历之后,从写 medium 到 Kaggle 竞赛等等,我终于在大约 6 个月前找到了我的第一份机器学习咨询工作。我一直喜欢做机器学习项目,但我并不 100%确定我会把它作为一份全职工作来享受(然而,我做到了!).我不能说这是我所期望的,这也是我写这个故事的主要原因,来和你分享这美妙的经历。

首先,获得这份工作比预期的要困难得多。我的固定全职工作是软件工程师。获得一份全职软件工程工作也不是那么容易,但根据我的经验,这比获得一份机器学习/ DS 工作要简单得多。原因很简单:

  • 与 ML/DS 工作相比,获得经典软件工程工作有更多的资源
  • 软件工程有更多的机会
  • 软件工程的学习曲线(当然在我看来)更容易,因为数据科学在某种程度上是建立在典型的编程技能之上的。
  • 获得 ML/DS 工作似乎比软件工程需要更多的关系/参考

无论如何,让我们来看看最重要的部分,这是我在过去 6 个月里学到的:

  1. ML 项目很难定义和细化

ML 项目生命周期与典型的软件工程生命周期有很多相似之处。该公司与客户开了几次会,讨论客户的要求,提炼这些要求,估计完成这些要求需要多长时间和多少钱,最后给客户一个报价。

根据我的经验,提炼 ML 需求比典型的软件工程需求要复杂得多

这可能并不总是如此,请随时把你的经历写在下面。细化需求的很大一部分是了解这个需求是否可行,甚至是可行的。在机器学习的世界里,这很难讲清楚,因为这些需求更独特,不像典型的软件工程需求那样多。

例如,如果我正在为一个客户构建一个 web 应用程序,他们要求我构建一个多因素身份验证系统,即使这个需求对我来说是全新的,我也可以简单地在网上查找并评估它有多困难和复杂。然而,在 ML 的世界里,一个客户可能会要求你建立一个模型来评估一个初创公司的天使投资机会的质量。你要做的第一件事是将这些需求分成几个部分,如所需的数据,你将如何收集数据,你将建立什么模型,等等。但是,如果你以前没有这样做过,或者你只是没有太多的经验,你将不会在网上找到像其他典型的软件工程例子那样多的这样做的例子,这使得很难估计需求的复杂性。

2。开始过度设计你的模型是非常容易的!

照片由约书亚·富勒Unsplash 拍摄

机器学习非常令人兴奋,也非常新颖。作为一名热情的数据科学家,在解决问题时,我总是发现自己没完没了地阅读关于新模型的论文,以解决我正在处理的特定问题。虽然我通常没有那么好奇,并且我想尽可能务实地解决问题,但在解决 ML 问题时,这是很难保持的。这是因为通常没有太多的资源来比较这些模型,这取决于您阅读论文并对它们进行评估。但是,我得到的教训是,你必须在某处划清界限。

例如,如果有 15 种不同的模型可以解决这个问题,根据快速浏览/快速研究挑选出 5 种。然后仔细阅读和评估这 5 个模型,而不是对 15 个模型进行这一过程。把这个放在坚果壳里:

学会如何驯服你的好奇心!

不要误解我,好奇心不是一件坏事。但是,太多的时间有时会妨碍实际工作和按时完成任务!

3。不仅仅是你在努力细化需求,客户也在努力

尽管这在许多软件工程项目中可能是正确的,但我认为在 ML 项目中更是如此。客户发现很难给出具体的要求,因为他们仍然在探索和学习这个领域。他们也不完全确定可能性和特定需求是否可行,因此他们不想简单地给出任何需求,浪费他们的时间和金钱。

我认为这又回到了这个客户决定开始一个项目的最初原因。在软件工程中,典型的 web 应用程序有许多共同的特性,所以客户通常会准备好他们想要的东西。然而,在 ML 世界中,ML 项目之间的差异相当大。此外,可供比较和借鉴的例子也相当少。

在这种情况下,一个很好的行动是在 ML 项目中与你的客户进行更密切的沟通。

这意味着获得定期和持续的反馈,以确保您处于正确的轨道上,并致力于正确的需求。例如,如果你认为在一个软件工程项目中,一个有规律的两周的会议就足够了,那么在一个 ML 项目中,应该是每周一次。这有助于与你的客户建立更密切的关系,在大多数情况下,他们会非常感激。

结论

我可以继续谈论很多其他的小问题,但我认为这是最重要的三个。谁知道呢,在接下来的几个月里,我可能会想出新的点子。我还意识到,在漫长的简历制作过程中获得第一份工作是非常令人满意的!虽然我在两年前获得第一份软件工程实习时经历了同样的过程,但这更令人满意,因为构建数据科学简历需要更多的努力。很多人可能会认为这两份简历在很大程度上是相互关联的,我在某种程度上同意这一点。请在评论中告诉我你的想法。

如果您想定期收到关于人工智能和机器学习的最新论文的论文评论、高质量的 ML 教程等,请添加您的电子邮件此处 &订阅!

我最有用的数据科学技巧和诀窍

原文:https://towardsdatascience.com/my-most-useful-data-science-tips-and-tricks-da3c0c7930c7

分享一些关于数据科学建模和数据处理的有用提示

(图片由 Pixabay 上的 PublicDomainPictures 提供)

介绍

对于局外人来说,数据科学可能是一个令人困惑的隧道,夹在一个全是计算和假设检验的领域和一个全是计算机编程的领域之间。我们明白这一点,不要担心——我们也害怕。说到这里,对于那些可能对数据科学或计算机编程完全陌生的人,我在这里有一点阅读,可能会在 2021 年开始使用数据科学时提供一些更准确的信息(去年没有太大变化。)

[## 2021 年如何闯入数据科学(8 步)

towardsdatascience.com](/how-to-break-into-data-science-in-2021-8-steps-87cb02a4a1f4)

无论如何,在这些年的巫术实践中,我遇到了一些小趣闻,我想与大家分享。这些是我现在第一次做的典型的事情,因为当你去做的时候,注意这些方面比回头去修复它们容易得多。当它们对模型的设计至关重要时,情况尤其如此,因此这些提示中的许多对于防止对模型或其训练数据进行戏剧性的重新设计可能是至关重要的。

№1:注意类型

当探索来自对给定特性的下一次观察的整个数据和知识世界时,很容易忘记自己完全是在编程。作为数据科学家,总有一些人可能更喜欢数据科学技能集的某些部分。例如,一些数据科学家可能喜欢建模和构建神经网络,但讨厌数据处理。

有一件事会使事情变得容易得多,特别是当涉及到使用具有类型系统变化的语言时,那就是不断地监控你的类型,或者使用一个允许你这样做的数据管理框架。例如,我们可以使用 astype()将一个系列转换为一种数据类型(将系列中的值转换为一种数据类型),并且我们可以通过以下方式获得当前数据类型:

df.dtypes
df.astype(Int64)

在 OddFrames.jl for Julia 中,我们也使用了 dtype()函数,但是语法略有不同。我们还使用可变版本 dtype!()以便将此类型转换为我们的数组。我们也可以通过为一个类型设置一个索引来做到这一点。:

od.dtype(:column)
od.dtype!(:column, Int64)

在 DataFrames.jl 中,对于我们的最后一个例子,我们可以通过

eltype.(eachcol(df))

如果你使用的是动态类型的语言,如果你从事数据科学,你很可能就是这样,那么你可能需要关注那些类型特别弱的语言中的数据。当类型是弱类型而不是强类型时,如果您想了解更多关于弱类型或隐式类型的信息,我这里有一整篇文章:

所有这些弱/强或隐式/显式意味着编译器将基于其编译或解释算法显式或隐式地控制类型。换句话说,有些数据可能会在您不知道的情况下改变类型。

№2:花更多时间测试

我能提供的另一个很好的建议,尤其是在建模的时候,是做更多的测试。测试是确定两个值之间有多大关联的唯一可靠方法。

测试你将在模型中使用的特性,观察排列,考虑每个特性的重要性。所有这些都可以形成一个更好的模型,并且可以减少花在绞尽脑汁想为什么这个模型不起作用上的时间。现在一头扎进笔记本,让你面前的数据符合你的模型,这可能令人兴奋,但耐心是一种美德。当涉及到数据科学时尤其如此,因为在测试和处理数据时,您的发现过程通常会指导您的整个项目。

№3:花更多时间处理

可能我最不喜欢的数据科学技能是数据处理。尽管探索数据很有趣,处理数据也很轻松,但也可能令人沮丧——尤其是在管理框架不够完善,或者数据没有正确保存或查询的情况下。是的,这是真的,我们都有这样一个文件,它将我们的整个文件读入一个数据帧的一个观察值中。那是糟糕的时代,但是不管处理数据和数据工程对某些人来说有多无聊,它都是数据科学过程中至关重要的一部分。

拥有完全干净的数据可以消除数据中的一些差异。很多时候,一组特定的连续样本的范围实际上根本不能反映整个特征的性质…用不太通俗的话来说,我们称之为异常值,但我的观点是,将列命名为正确的名称、特征工程、特征压缩、分解,所有这些对于在给定模型上获得最佳精度都至关重要。也就是说,大量的时间,甚至可能是永恒的时间,可以用于处理一个数据集,所以明智的做法是先了解一下什么时候有一些好的特性,什么时候没有。

№4:去掉无关紧要的功能!

我对数据科学的第四个有用的建议是去掉无关紧要的特性。在我们的训练数据集中,有一些因素会对每一次观察产生显著的影响。也就是说,无论 n 是二十还是两亿,每个观察值对最终成为回报的结果都很重要。

无关紧要的特征是不重要的特征。有些人可能认为将所有的特性放入模型中是有意义的,因为模型可能会辨别出哪些特性的权重更大或更小,事实就是如此。然而,当试图获得更高的验证准确性时,以错误的方式将模型放在数据之上会导致很多麻烦。

总的来说,拥有太多的要素而没有足够的数据来正确连接权重会导致过度拟合现象。许多建模都归结于平衡偏差,这样模型才适合于适量的数据,而不会使模型过拟合或欠拟合。为了更好地完成这项工作,通常会使用分解,但是在大多数情况下,抛弃无关紧要的特性可能比进一步处理它更好。如果您想阅读更多关于分解的内容,我有一篇关于奇异值分解的文章,这可能是对分解及其用途的很好的介绍:

[## 深入奇异值分解

towardsdatascience.com](/deep-in-singular-value-decomposition-98cfd9532241)

№5:从基线开始工作

数据科学家的一个良好实践是始终从基线精度预测开始。这有几个主要原因,第一个原因是,它为我们提供了一个良好的起点,让我们知道如何实现准确性。同样,它会给我们一个很好的想法,关于我们可能要处理的特性的深度。例如,如果我在二进制预测中使用多数类基线,并看到准确率约为 50 %,我可能会指出我的数据框中只有两个类别。不用说,这可能表明一种布尔特征类型,以及一种理想的一次性编码方法。所有这些信息都可以从简单的基线中提取出来。

使用基线的另一个原因是看你是否真的需要一个模型来预测这个特征。在某些情况下,运行一个模型可能根本没有意义,比如你预测一个反应,是或不是。

y = [0, 1, 1, 1, 1, 1, 1, 1, 1, 1]

该双数组表示响应,0 表示否,1 表示是。我们的 n,观察总数,这里是 11。在这 10 个值中,只有一个是零。在本例中,这代表了 10%的数据。因此,如果我们在这里不使用模型,只是每次都猜这个值是 1,我们在 90%的情况下都是正确的。在某些情况下,这个数字可能会高得多。为不需要模型进行预测的特征拟合模型是没有用的。

№6:使用验证集

一些真正有助于改进模型的建议是使用验证集以及测试和训练集对两个不同的数据样本进行预测,这对于数据科学来说无疑是一种很好的实践。验证集给出了一个基本目标,以及我们模型的一个相当精确的演示,虽然它可能不会过拟合。

在建模时,验证集有助于完成许多不同的事情。首先,它缩小了样本大小,这将意味着特性的重要性,以及特性的数量都将在建模时更能代表特性本身。除了常规的训练和测试集之外,使用验证集的唯一缺点是,由于没有足够的数据来填充神经元的权重,您的模型可能会不足。

№7:争论时,选择好的功能

我想分享的最后一个关于数据科学的惊人技巧来自于数据科学过程的早期,那就是数据争论。在我看来,数据争论可能是现代数据科学过程中最困难的步骤之一。有了许多不同的东西,例如预测建模和矩阵数学,我们能够使用依赖关系来加载 Python 中的快速 C 脚本,并且大多数对 Python 来说太慢或者编程工作量大的东西都被导入并用作依赖关系。

然而,数据争论就不是这样了,因为数据争论通常需要某种定制的脚本,或者保存数据然后加载的方法。也就是说,争论数据可能非常困难。当争论数据时,我的建议是以尽可能干净的格式争论数据。如果可能,在构建保存数据的结构时,扫描缺失或荒谬的值。先发制人地完成所有这些是一个好主意,因为这将保持数据本身的干净,并使您未来处理数据的工作变得容易得多。就像在常规处理阶段一样,这里的一点点数据工程可以节省后期大量的时间!

结论

数据科学有如此多的来龙去脉,很容易理解为什么它对一些可能有兴趣加入该领域的人来说是势不可挡的。然而,按部就班地看待事物,并着眼于每个单独的方面,对于建立足够强大的知识基础以促进伟大的数据科学技能会有极大的帮助。

这篇文章包含了我多年来作为一名数据科学家所学到的七件事。我来自一个更传统的计算机科学/软件工程背景,这其中的一些部分花了很多时间去学习,只是因为从来没有人告诉过我。正是因为这个原因,我现在将这些话传达给你,所以我希望这些小技巧在你的数据科学之旅中派上用场。感谢您阅读我的文章,祝您有愉快的一天!

我在 Julia 中创造性地处理大数据的新大脑方法

原文:https://towardsdatascience.com/my-new-big-brain-way-to-handle-big-data-creatively-in-julia-57be77fc6a04

我想出了为了应用表达式来压缩内存中的大量数据,让我们来看看吧

(图片由 Pixabay 上的 DamianNiolet 拍摄)

简介——记忆问题

随着更新的编程语言 Julia 速度的提高,似乎一切皆有可能。当然,有些事情是无法做到的,无论人们试图在 Julia 中采用什么方法,我的意思是很难想象这种语言有像 FORTRAN 这样的数字能力。然而,这种语言因为如此高级而变得非常非常接近,这真的令人印象深刻。

我现在发现我的硬件限制已经发生了巨大的变化。过去的情况是,虽然我有一个支持 CUDA 的显卡和一个相对较好的 8 核处理器,但像 Python 这样的语言确实没有利用这一点。通常,当处理相当大的数据集时,Python 的核心问题就开始出现了。首先,Python 并没有真正考虑到处理器/显卡并行计算。怎么可能呢?在最初设计这种语言的时候,最好的图形卡可能是由 VOODOO 制作的,像 OpenGL 这样的图形 API 还处于起步阶段。这是使用像 Julia 这样的现代语言的好处的一部分,他们能够在头脑中计划语言的设计。甚至仅仅使用处理器的并行计算对于 Python 语言来说也是相对较新的。

考虑到这一点,我现在发现,当涉及到数据和建模时,最大的瓶颈是内存,至少对于我的系统是这样,我想其他人也是这样。当然,其中一些肯定与我的计算机的规格有关,我也将与您分享我的硬件描述符:

(图片由作者提供,我也非常确定我的处理器上的 8 个核心更像 AMD 推土机/piledriver 核心,它们是虚拟的,而有 4 个物理核心。)

在 2022 年,8g 内存是一个相当低的数量,但通常这不是一个很大的障碍,直到它可能成为其他人的一个大障碍。事实上,朱莉娅宠坏了我们。我知道我可以通过一些东西传递 5000 万个观察结果,没有问题,没有评论,也没有来自我的处理器 Julia 的顾虑,没问题。然而,我经常碰到的是记忆的极限。

也就是说,我想探索一些将整个特性的观察结果分解成某种“规范形式”的想法,并且我开始研究这些主题。我在保存记忆的方法方面的发现非常酷,所以我认为这可能是一个有趣的阅读,看看我学到了什么,另外还有一个我自己想出的非常好的主意。所有这些代码都是我的项目 OddFrames.jl 的一部分,odd frames . JL 只是 Dataframes.jl 的一个替代品,具有更多的功能,我几乎准备好发布这个包了。如果你想了解更多,或者浏览这个包,这里有一个 Github 页面的链接:

https://github.com/ChifiSource/OddFrames.jl

代数表达式

当我探索像这样的科学应用程序的数据压缩概念时,我遇到了惰性数组的概念。我当然知道懒惰类型的存在,但并没有真正意识到这种类型的含义和能力。我还写了一篇关于这些类型的数组的文章,以及为什么它们如此酷,但在涉及到通用数据科学应用时也有一些问题。以下是我最初文章的链接:

自从那篇文章之后,我进一步发展了惰性数组的实现。构造函数现在是基于函数参数的。Julia 有一个惰性数组包,但是构造函数语法并不是我想要的,这更像是个人的事情,但是我最终还是在我的实现中添加了一些新函数,使得这个选择更有意义。此外,我还将随后的所有代码复制到一个笔记本中,这样我的数据科学读者就可以更容易地接触和测试这些代码,如果您愿意的话,Github 上有这样一个笔记本:

https://medium.com/r/?URL = https % 3A % 2F % 2f github . com % 2 femmettgb % 2 femmetts-DS-NoteBooks % 2f blob % 2f master % 2f Julia % 2f gebraic % 2520 arrays . ipynb

现在,相对来说,这些阵列相当健壮。我还希望这些类型能够适合另一种类型,这一点我们将在后面讨论(这可能是这个实现真正酷的原因。)新函数包括迭代能力、添加到计算表达式的能力,以及通过 compute()方法按索引进行计算的能力——现在由 getindex 调用。这个项目的代码都可以在下面的 OddFrames.jl 库中找到。让我们看看我的这个类型的实现,现在称为代数数组,在代码中是什么样子的。

mutable struct AlgebraicArray
    **f**::Function
    **n**::Int64
    **calls**::Vector{Pair{Function, Tuple}}
    function AlgebraicArray(f::Function, n::Int64)
        new(f, n, [])
    end
end

构造函数非常简单,并不需要太多的基类型。在我看来,这更像是一个大纲——我们可以用它作为模板来构建一些非常独特的类型,我们将在后面看到。我之所以将其描述为模板,是因为在大多数情况下,测量值需要保持连续的线性,才能从中获益。在大多数情况下,对真实世界的数据做类似的事情将需要系数,这违背了整个目的-因为存储表达式和东西最终只会占用更多的内存,而系数占用相同的内存量。这是一个相当有趣的问题。

这个结构有三个字段,上面用粗体突出显示,第一个是函数 f。这个函数只是我们使用的任何类型的数据的生成器。每当调用另一个方法 compute()时,首先调用它。最后一个字段是 calls 字段,它是数组中的一系列对,表示函数调用和附加的位置参数。需要注意的重要一点是,这些函数是在迭代计算每个值时调用的,不是整个数组。同样,这些应该是值可变的,所以值应该在所有表达式中放在第一位。下面是生成这些对的实际函数:

function **add_algebra(aa::Symbol, args::Any)**
    aa = eval(aa)
    if length(args) > 1
        farguments = Tuple(eval(args[length(args)]))
        **push!(aa.calls, eval(args[1]) => farguments)**
    else
        **push!(aa.calls, eval(args[1]) => [])**
    end
end

记住不要强调这里的细节,只要记住我们的输入和输出。我们这里的输出是突变,通过推!aa 的,作为一个符号提供给我们。我还没有在一个模块的作用域而不仅仅是一个主作用域中测试这个特性,因为这个特性还在构建中,所以对 aa 的评估可能会导致该符号不存在,在这种情况下,我只需要使用一点内省技巧。顺便说一句,你可以在我写的关于这个主题的文章中读到如何用元编程来做这件事:

该函数提供了我们的数组作为一个符号,和参数,这应该是另一个符号子集。这几乎就是为我们生成每个单独调用的方法调用。接下来,我们将看看与此绑定的宏,也就是@代数!宏观。

**macro algebraic!(exp::Expr)**
    args = copy(exp.args)
    aa = exp.args[2]
    deleteat!(args, 2)
    **add_algebra(aa, args)**
**end**

这里非常简单,宏只分离我们提供给方法的第一个参数,它应该是我们的代数数组,然后把它和其他参数一起传递给 add_algebra。下一个函数有点不相关,但非常简单,我们将看到所有这些是如何立即结束的。此方法只是基本生成器,它使用 generator 函数生成值,直到数组的长度:

function generate(aa::AlgebraicArray)
    **[aa.f(n) for n in 1:aa.n]**
end

这个函数也有接受整数、范围甚至 bitarray 的绑定——尽管我在让 bitarray 工作时遇到了一些麻烦,尽管我认为这很容易,因为在这个数组上有比一个索引类型更重要的东西要处理。

function generate(aa::AlgebraicArray, range::UnitRange)
    if range[2] > aa.n
        throw(BoundsError(string("Invalid algebraic index, ", string(range[2],
                        " on algebraic expression of length ", string(aa.n)))))
    end
    **[aa.f(n) for n in range]**
end**function generate(aa::AlgebraicArray, index::Integer)**
    if index > aa.n
        throw(BoundsError(string("Invalid algebraic index, ", string(range[2],
                        " on algebraic expression of length ", string(aa.n)))))
    end
    **aa.f(index)[1]**
end

由于生成器函数通常会返回一个值数组,这就是我们用 1 来索引它的原因。这些调用之间唯一真正改变的是函数调用的最后一部分。我们要看的最后一个函数是计算函数:

function compute(aa::AlgebraicArray)
    **gen = generate(aa)**
    for call in **aa.calls**
        **gen = [call[1](val, call[2]...) for val in gen]**
    end
    **return(gen)**
end

这个函数将所有其他函数包装成一个,首先它生成我们的基值,然后将 aa.calls 中的所有后续调用应用于其中的每个值。OddFrames.jl、bitarray、range 和 index 中的所有常规索引也有一个绑定。

function compute(aa::AlgebraicArray, **r::Any**) # <- range, bitarray
    gen = generate(aa, r)
    for call in aa.calls
        gen = [call[1](val, call[2]...) for val in gen]
    end
    return(gen)
end

由于 generate 能够为我们指明哪个是哪个,并且这两个函数都将返回一个具有多个元素的数组,所以我在这里传递 Any 以避免必须编写两次该函数,因为除了 generate 中的调用之外,所有调用都是相同的。这与整数略有不同,因为我们不需要遍历 gen 中的值,如果我们试图迭代一个整数,我们会得到一个 MethodError。我相信你可以假设这个函数和一个单一指数的函数之间的细微差别。最后,还有一个函数的调度调用,然后是带有 compute()的代数数组。有趣的是,一个随机的行内注释告诉你这个函数实际上是在什么上下文中使用的:

function compute(f::Function, aa::AlgebraicArray) **# compute(aa) do _**
    gen = generate(aa)
    for call in aa.calls
        gen = [call[1](gen, call[2]...)]
    end
    return(f(gen))
end

这允许我们加载数组而不将它保存在内存中的语法。例如,让我们看一个均值函数,它将计算这种类型的均值。考虑一下,将整个值加载到内存中,尤其是在全局范围内,可能会很成问题。例如,如果我们想使用来自车床. jl 的 mean()函数,我们当然可以这样做

mu = mean(compute(aa))

然而,这将所有这些加载到内存中,并且都是在 mean 的上下文中。结果可能会被缓存,我们并不真正控制内存中代数数组的状态,我们把这一切都交给了垃圾收集器。如果我们做了

mu = compute(aa) do array
   mean(array)
end

然后我们从一个全新的、临时的、匿名的函数作用域中获取这个值。此外,我们可以一次应用更多的操作,而不必为每个操作计算 aa,或者将 aa 加载到我们的全局环境中。在像这样的高级动态语言中,一旦数据被全局定义,唯一的处理方法就是隐式的。在朱莉娅时代,我们已经删除了!(),很容易看出为什么这样的函数现在被弃用了。请记住,在我们的环境中管理这些值的内存状态的最佳方式是,首先不要将它们放入环境中,而是只私下处理这些值。

当然,在这种类型上仍然存在 getindex()的绑定。同样,iterate()也有一个绑定,它只是将生成的数组的值除以 1。当它离成为常规的可迭代数组只有一两步之遥时,这是一种使简单迭代工作的简便方法。因为 compute 和 generate 函数已经可以通过 index 进行计算或生成,所以索引就像通过 compute()函数绑定任何传递的索引一样简单。虽然这造成了 MethodErrors 的缺点,有点令人困惑,但由于 OddFrames 中的索引在扩展基本 Julia 类型索引的程度上是通用的,这在整个包中是一致的,因此在这里假设参数很容易。

function iterate(aa::AlgebraicArray)
    ret = Iterators.partition(compute(aa), 1)
endgetindex(aa::AlgebraicArray, i::Any) = compute(aa, i)

履行

现在我们已经看完了我的代数数组,让我们看看它在类型中的实现。让我们来看看一种新的 OddFrame 类型,代数编码帧:

mutable struct AlgebraicOddFrame <: AbstractAlgebraOddFrame
        labels::Array{Symbol}
        **columns::Vector{AlgebraicArray}**
        types::Array{Type}
        head::Function
        dtype::Function
        not::Function
        only::Function
        drop!::Function
        dtype!::Function
        merge!::Function
        only!::Function
        **compute::Function**
        # Super
        function AlgebraicOddFrame(labels::Vector{Symbol},
                columns::Vector{AlgebraicArray},
                types::AbstractArray)
                length_check(columns)
                name_check(labels)
                head, dtype, not, only = member_immutables(labels, columns,
                                                                types)
                drop!, dtype!, merge!, only! = member_mutables(labels,
                columns, types)
compute() = OddFrame([label[i] => compute(columns[i]) for i in enumerate(labels)])
 **compute(;at = 1) = [label[at] => compute(columns[at])]
                compute(r;at = 1) = [label[at] => compute(columns[at], r)]**
**compute(r = 1:columns[1].n) = OddFrame([label[i] => compute(columns[i], r) for i in enumerate(labels)])**
                new(labels, columns, types, head, dtype, not, only, drop!,
                dtype!, merge!, only!, compute)
        end

我知道代码很多。

这里我们真正需要注意的是附加函数 compute()和列的新数据类型。我想我可能会删除这里除了 compute 之外的所有成员函数。这是因为在这个上下文中,我可能会为 compute()函数绑定一个类似的函数。换句话说,由于 compute()返回一个常规的 OddFrame,我认为只使用

aod.compute() do od
   od.whatever()end

也就是说,head()等函数除外。我可能会将 compute()定义导出到一个类似的 algebra_members()函数,该函数也可能会为此类事情提供绑定。真的,当涉及到 head 这样的函数时,只需要做微小的改变,可能不是对函数本身,因为通常这些只调用索引。

虽然我们可以通过它的构造函数直接调用 OddFrame 的代数版本,但我认为真正的力量将来自于根本不这样做。相反,对于这种类型的一些有趣的构造函数,我有两个命题想法,第一个使用机器学习。你看,问题在于,像这样的大量数据很难浓缩成一个单一的数学表达式。正如我前面提到的,通常这方面的任何事情都需要使用某种系数,这完全消除了试图将值存储在更少数据中的目的。

MLOddFrame

这时候我突然想到。整个数据科学领域都是关于预测事物的,虽然数学表达式(如用于代数编码框架类型的函数)是精确的,但在许多情况下,只预测重复的值可能是有意义的。这让我想到了我的第一个真正高级的实现,MLOddFrame。到目前为止,这已经写入 OddFrames.jl 包中,尽管它实际上并不做任何事情,而且我还为它写了一个构造函数——这是一个奇怪的选择。相反,我希望有另一个完整的包,一个扩展,这是唯一的 MLOddFrame。这使得管理预编译时间变得容易得多,因为不是每个人都想在预编译数据管理包的同时预编译机器学习包。

同样,我们真的不需要定义任何类型。我们可以使用闭包函数来维护任何类型的名称定义,在实际计算 OddFrame 时,我们可能会用到这些定义。有趣的是,我实际上写了一整篇关于这个概念的文章,其中我谈到了这个令人不安的细节。如果你对这样一篇文章感兴趣,这里也有一个链接:

这是一个一般的想法,因为在 OddFrames 中已经有了对一个代数编码帧的绑定,我们将只在一个不同的别名下为一个代数编码帧创建一个构造函数。在这个构造函数中,我们允许传递一个参数来决定要训练多少数据,以及我们想要使用什么模型。如果我们使用 Lathe.jl,我们可以通过这些参数传递整个管道。

function MLOddFrame(catmodel::Any, conmodel::Any; split = .25)end

当然,需要有一个特征类型的模型,我也有可能在这种情况下使用框架组类型,因为我们可以说——评估准确性,并决定我们是否应该使用模型来预测数据。在某些情况下,我们可能不想这样做。可能应该有一个参数来决定是否这样做,或者可能有一个完全独立的构造函数,因为它会极大地改变这个方法的返回。这将数据的维度降低到预测它的权重,这肯定会大大降低。

那么第二部分就像知道数据总长度一样简单,这将取决于我们正在构建的上下文。最后,最后一部分只是一个函数,它的定义将在这个函数中进行,保留函数中所有已定义的名称,比如我们的模型。最后,将该函数与 n 一起返回到一个新的代数编码框架中。

StreamOddFrame

StreamOddFrame 将遵循 MLOddFrame 中相同的原则,它是基本 OddFrame 的扩展,并使用闭包函数来保存名称。唯一的区别是,StreamOddFrame 将有一个直接从数据文件中读取的函数。例如,我们有一个包含超过 500,000,000 百万个观察值的 CSV 文件,我们的函数根据需要将每一行读入 OddFrame。此外,我确信类似这样的事情也可以用其他流来完成,比如 HTTP 流,这可以打开使用套接字创建 OddFrames 的远程客户端实例的可能性。

优点,缺点

这里最明显的优势就是我之前提到的,内存。这里的工作是有效地将大部分(如果不是全部)数据保留在内存之外,特别是在全局范围内,但仍然允许您访问所有数据,就像数据在内存中和全局范围内一样。这样做的明显缺点和问题是计算。然而,有了这个交换,我认为用 Julia 写的这个肯定有它的应用。

此外,我们可以序列化这样的数据,并引用其他地方的所有计算,这一事实非常有价值。有时,您可以计算没有终端超时的东西,这肯定会杀死您的 Jupyter 内核,但显然 Jupyter 内核的便利性很高,作为数据科学家有非常好的理由使用它。最后,一旦这个包发布,引用远程计算肯定是我将作为扩展写入的内容。也就是说,虽然这有一些缺点,但我认为能够在任何地方引用这种计算的能力通常可以一起减轻或消除这些缺点。

MLOddFrames 的另一个显著缺点是使用 ML 预测数据可能会产生不准确的结果。同样,可能应该有某种方式来查看、过滤和调整特性是否以物理方式表示,作为其自身的样本或整体,或 MLOddFrame。幸运的是,当我实际编写所有这些内容时,OddFrames 支持在单个类型中包含多个框架,并且有自己方便的方法来处理这些类型。

结论

因此,我有一套将基于代数/惰性表达式的数组实际应用于现实世界数据的概念。虽然这是我相信我将要追求的两个想法,但我确信这类事情还有更大的潜力。例如,我们甚至可以有一个框架,在评估时通过请求来填充。因为核心思想是一个函数,它可以是一个闭包函数,实际上可能性是无限的。

感谢您阅读本文,我希望您发现其中的一些概念和想法令人兴奋。目前,我正致力于使 OddFrames.jl 接口作为一个整体,包括代数编码框架,更加健壮。我很快就要发布这个包了,这非常令人兴奋!不用说,一旦我真的完成了,这将是非常有用的。我真的为这个项目的结果感到兴奋,但主要是我只是兴奋地在我自己的项目中使用它的能力,一旦它是稳定的。

我的预测建模和学习“分步”过程技术

原文:https://towardsdatascience.com/my-predictive-modeling-and-learning-step-process-technique-f0521ee76d90

概述了我将数据转化为机器学习模型的不同步骤

(图片由 geraltPixabay 上提供)

介绍

从表面价值来看,预测建模和数据科学可能会令人望而生畏。当谈到预测建模时,为了有效,可能需要学习很多词汇和基础知识。随之而来的是一些统计知识,数据处理,还有很多。那么,一个实质性的问题是,我们刚刚讨论的所有内容需要协同工作,以创建一个单一的结果、一个准确度分数及其关联模型。如果不调查返回后数据可能有什么问题,所有这些事情也很少或没有任何推论。

数据科学领域提供的所有这些知识的复杂性和多样性肯定不会马上下载到某人的大脑中。人们不可能列出一堆方法名,然后期望有人马上永远记住它们。也就是说,一个人学习数据科学的任何部分或任何东西的一个很好的方法是,通过将每个大的任务分成小的任务来完成。今天,我想把我通常用数据和建模完成的任务进行划分,然后一步一步地安排它们,并解释我通常如何完成每个目标的细节。

第一步:数据争论

数据科学过程的第一站是让我们争论数据。“争吵”只是一个词,在这种情况下意味着收集。在这一步中,正如标题所暗示的,我们从各种来源收集数据。尽管一个重要的预防措施是确保您的数据是您的软件实际上可以读取的格式,但是所有这些数据通常都不是非常组织良好的。

数据争论通常非常简单,但肯定有一些细微差别和需要了解的事情可以节省大量时间和精力。我的第一条建议是,只争论你需要的数据,并且总是将数据输出为某种非专有的传统数据格式,比如。比如 CSV。这将允许你的项目中的数据是可复制的,如果你想拒绝或接受无效假设并做其他科学的事情,这肯定是合适的。在某些情况下,甚至可能不需要这一步,因为您可能正在处理已经存在于这些类型的文件中的数据集。

一般来说,每当我们争论数据时,我们的目标并不是用任何方法来保持数据的干净,而是仅仅将数据转换成某种可读的输入,然后我们可以对其进行处理。然而,您可以节省一些时间,潜在的存储空间,或潜在的内存,在这个过程中花一点额外的时间来清理您的数据。虽然这并不是每个人都做的事情,也不是必须的,但是它可以为你节省很多时间,包括整个下一步。

对于使用 Python 的人来说,这一步我要做的是数据收集的方法,比如 requests 模块或 ScraPy。如果您完全是新手,我建议您跳过这一部分,下载一个. CSV 或。网上的 JSON 文件。

第二步:预处理

数据的初始预处理不要太多。如果有像“日期”、“姓名”、“id”或类似的完全没有用的特性,那么最好也把它们去掉。您使用的功能越少,您需要执行的步骤就越少。然而,您拥有的具有统计学意义的特性越多,您就越有可能从您的项目中获得一个优秀的模型。

可能在预处理阶段需要做的最大的事情是从数据帧中删除丢失的值。如果我们在其他任何时候这样做,很可能我们所有的函数在遇到缺失值时都会返回错误,同样,在我们分割我们的特征后,我们会有多个名字充满了这样的缺失值。

对于 Python,您可能希望熟悉 Pandas 的 df.drop()函数和 df.dropna()函数。这两个函数可以分别用于删除列和丢失值的观察值。

第三步:分析

我通常采取的第三步是分析。既然数据至少能够被查看而不抛出错误,我们应该深入研究每个特性。如果我们的数据在头脑中已经有了一个目标,这是通常的情况,那么尝试分析可能更相关的特征——或者非常有效地证明与您的目标更相关。找出其中的古怪之处,找出平均值,最常见的值,有多少个类别,诸如此类的东西。在分析完所有特征后,您应该有一些您认为与值非常相关的特征,这将有助于下一步。

对于 Python 程序员来说,在分析任何数据之前熟悉 numpy 和 scipy.stats 可能是个好主意。通过 matplotlib、seaborn、plot.ly 等实现可视化。也是快速了解更多特性的好方法。在此期间,继续进行并拟合基线模型也是一个好主意。一个基线模型将会让你更容易知道这个特性有多难生产。更重要的是,它会给你一个坚实的起点。

步骤 4:特征选择

下一步是特征选择。特征选择可能是整个建模过程中最重要的步骤之一。这是因为这些特征是建立一个能成功预测你的目标的模型的绝对关键。不良要素会导致不良预测,因此要素选择和要素处理可能是最难的两个部分,也是对模型影响最大的两个部分。

在此期间,从数据分析中得到的测试可以用来提供最有价值的特性。这也是您可以设计功能的地方。为什么您应该设计功能?工程特征是降低输入数据维数的一种好方法。如果你有两个特征,例如我们正在研究松果,我们有三个特征,宽度,高度和深度。我们可以将它们一起乘以体积,体积可能会比单独的特征更好地累积这些特征的强度。我的意思是这样想。

一个高 10 厘米,宽 6 米的松果有多大?相比之下松果的体积是 60 立方厘米。第二个是我们可以立即评估和比较的一个值。这样的例子在机器学习中比比皆是,特征选择很重要,因为它创造了这些价值。所有这些通常都是手工完成的,或者通过索引来完成。过滤值可以通过 pd 完成。DataFrame[BitArray]。获取数据帧上 BitArray 的索引只会删除基于条件的值。您还可以在这里使用映射函数将掩码映射到值。掩码只需要返回 0、1 或真/假值。

步骤 5:特征处理

我的数据科学过程的下一步是特征处理。特性通常需要编码、标准化和类似的重要步骤。在某些情况下,如果我们没有对输入数据进行处理,模型将无法预测出我们的输入数据。

在 Python 中,你可能想看看 SkLearn,以及 Tensorflow 中的一些其他工具,用于批处理和诸如此类的事情。编码器和定标器可能是这些操作最流行的选择,但实际上你的处理器可以是任何东西。通常,这些对象以某种管道包装器或 Python 文件的形式聚集在一起,因为我们通常会序列化该模型并自动进行特征处理。这是在一些测试中投入更多精力的另一个好理由。我们还需要再做一部分特征处理,因为这些线有些模糊。现在我们最有可能使用 train_test_split()方法进行一次 test/train/val 分割。这种子抽样随机观察,然后将它们分成两组不同的相同特征。我们之所以要在处理完数据后再这样做,而不是在

第六步:建模

可能感觉最大和最吸引人的一步是建模。在建模步骤中,我们将把这些数据仔细整合到输入数据中。然后,这些数据将作为输入提供给我们的机器学习模型。根据您的模型,超参数可能也需要在过程的这一部分进行调整。

这部分相对简单,因为库的输入通常被映射到两个简单的位置参数。确保尺寸正确,并将特征发送到模型中。得到一个预测,并在你的验证集上检查它,然后回去看看是否还可以做更多的事情来获得更好的准确性。最终,通过足够的工作,您将获得一个相当有信心的模型,可以准备好进行流水线操作。

第七步:流水线作业

最后一步是把你的东西用管道连接起来。您将希望在此管道中包含任何用于预处理的方法。重要的是处理是相同的,以便最终模型的输入保持相同的格式,因此每个特征集的输出也保持相同。

在大多数 ML 模块内部,通常有一个相当健壮的流水线接口。在 SkLearn 的例子中,您可能会在第一对模型中使用它,您可以使用 Pipeline 构造函数来创建一个新的管道。

结论

数据科学过程可能看起来令人生畏。仅仅浏览这些标题可能会让人不知所措。然而,用一种系统的方法一步一步地做事情,就像你在计算中经常做的那样,将极大地帮助你创建一个有效的工作模型。我希望这个模型能够成功地展示出一个好的模型需要做些什么。这项技术的伟大之处在于,当涉及到声明式编程时,将事情分解成步骤是非常有效的。话虽如此,但我认为它可以应用于生活和软件中不断学习不同的东西。祝你好运利用这项技术,我知道它一定会在这个应用程序中派上用场!感谢您的阅读!

我的技术写作之旅

原文:https://towardsdatascience.com/my-technical-writing-journey-14ea1ca18e79

一切都从问自己一个问题开始

照片由扬尼克·普尔弗unsplash 上拍摄

两年前,我在媒体上开始了我的第一个技术博客。老实说,我不敢相信我已经坚持这个习惯这么长时间了。作为一种欣赏的方式,我想我可以写一个回顾,与任何也想加入的人分享我的旅程。

在写这篇博客的时候,我已经发表了 28 篇文章,获得了 685,000+的浏览量和 1,000+的关注者。这些只是数字,对不同的人有不同的意义。这个博客绝不是教你如何在媒体上传播(我也不知道)。这是从问自己一个问题到养成写博客习惯的旅程。

我的出发点是什么?

我在一周内发表了我的前三篇文章。就写作而言,那是我最有收获的一周。都是从当时没有好的完整的资料来回答我的问题的挫败感开始的。为了找到出路,我从网上收集了一些作品,并经历了试错的过程。最终,我得到了我想要的。这个问题与工作有关,所以我把我的发现写在了一个笔记本上,并把它展示给了我的同事。事实证明这对每个人来说都是一个有趣的话题。

当我完成我的演讲时,我决定在互联网上与更多的观众分享。那是 2020 年 5 月,在家工作的情况也让我更想表达自己一点。同一天,我修改了笔记,完成了我的第一个官方技术博客:了解如何在 Python 中使用 NamedTuple 和 data class。我不得不说,我对接下来几天的观点感到惊讶,这是好的方面。我不知道分配算法是如何工作的。直到今天我还不知道。但这无疑给了我在互联网上创造更多有趣内容的信心。

的视图了解如何在 Python 中使用 NamedTuple 和 data class2020 年 5 月

随着我写的博客越来越多,事实告诉我,并不是每个博客都会这样。毫无疑问,我很幸运有一个好的开始。

创意从何而来?

每个内容创作者都需要一个缪斯。对我来说,每个想法都是从一个问题开始的。这些问题 90%源于我的日常工作。有些问题非常小众,比如“为什么当我调用函数 F 时,framework X 会引发异常 E?”,或者可以非常笼统,如“Python 中的继承是如何工作的?”。不管是哪种,都是困扰我的问题。如果我写了它,我应该是第一个得到好处的人,因为我的博客解决了我自己的问题。

我的第一个写作技巧是找到一个困扰你的问题。作为一名工程师,我们的日常生活应该充满了问题。我们离不开 StackOverflow:)。如果可以,那就找一份新工作,因为它不再是一份有挑战性的工作。之所以会发现一个和你很接近的问题,是因为你知道你和其他和你一样的人想要得到解决的这个问题的核心是什么。你会对你的听众表现出充分的同情心。在一天结束时,评估你工作的方法是问你自己最初的问题是否得到了回答。

拥有这样一种问题驱动的心态不仅让你的内容更加脚踏实地,而且这也是一个很好的学习机会。有时我们在向人们解释事情时会卡住。很有可能你还没有掌握这个题目。因此,告诉人们事物是如何在口语或写作中运作的,是提高你对某些主题的知识的一个很好的方法。

我通常做的是找到一个让我烦恼同时又吸引我的问题。这是一种又恨又爱的关系。你期待完成后的成就感,因为你的博客会让未来许多人的生活变得更容易。如果情况只有厌恶,那就不要写了。比如我绝对不会写什么为什么只能在 Linux 上安装包,而不能在 macOS 上。😃)我只想尽快摆脱它。

说到这里,我想以展示著名的邓宁-克鲁格效应来结束这一节。基本意思是“一知半解是件危险的事情。”当你在学习一个全新的事物时,你会很兴奋,会有很多问题。不要急着写“什么是阿帕奇卡夫卡?”或者“API 是如何工作的?”因为你很可能正站在“愚笨山的顶峰”。当你在那里时,你的产出往往是肤浅的。在我看来,写作的理想位置是“绝望的山谷”或“启蒙的斜坡”,因为它有助于你到达“可持续发展的高原”。

当我们处于“可持续发展的高原”时,我们还应该继续写作吗?我不知道。我想说,这是大多数人一生都在追求的一个假想点。只要我们在路上,我们就应该继续写作。

邓宁-克鲁格效应(来源:维基百科

如何将一个想法转化为博客?

将想法转化为博客的两种不同模式(由高创建)

下一步是将你最初的想法扩展或缩小到一个实用的范围。例如,我的 NamedTuple 和 Dataclass 博客以一个问题开始:就对象创建时间而言,哪个类的性能更好?这是一个非常小众的问题。我不需要很多话来回答。对于这种类似 StackOverflow 的问题,您需要扩展范围,思考在这个上下文中还有哪些有趣的地方。在研究过程中,我发现了许多其他有趣的点。所以我在对象创建、属性访问、不变性等方面做了更多的比较。最后,读者将对 Python 中的这两个概念有一个大致的了解,并帮助他们选择一个。其他类似的例子还有 Python 中的 Understand slotsUnderstand zip()——Python 中隐藏的瑰宝都是从一些小问题开始的,比如“我如何以优雅的方式将两个列表合并成对?”。

另一种方法是当你有一个广泛的想法时,缩小你原来的范围。你写的是博文,不是书。不要制定过于雄心勃勃的目标。否则,你要么让文章流于表面,不能创造太多价值,要么文章太长,读不下去。我喜欢的是找到题目的独特切入点。例如,在文章如何用 Python 编写用户友好的命令行界面中,我重点介绍了如何让您的 CLI 应用程序更加用户友好。在 5 使用财务数据的 Python 技巧中,我将 Python 技巧仅与财务数据联系起来。这样,你总是有一个明确的目标读者群。

另一个普遍的建议是在开始叙述之前做一个想法转储。这样你就不会忘记你要写什么,并且确保文章的大小合适。

如何留住你的观众?

好的,我有一个想法,我也知道我想谈什么。接下来,让我们想想如何保持你的观众的注意力,这样他们就不会太快失去注意力。根据我自己的经验,这里有几个建议。如果你有其他好的建议,请告诉我。

明确你的目标群体

一般来说,有两种类型的读者阅读你的科技博客。

1)浏览社交媒体并被标题吸引的读者。恭喜,你在标题上做得很好。他们打开页面,阅读介绍,并快速滚动页面,看看是否有任何有趣的内容。他们可能会上瘾,或者把它放在书签里以备后用,或者干脆把它扔掉。他们的行为是不可预测的,因此留住他们的策略也是模糊的。我唯一的策略是总是有一个清晰简洁的标题,这样在他们阅读之前,他们会有一些期望。当他们有期望时,他们属于第二类。

2)另一个群体是带着问题来的读者。他们谷歌了一下“python 命名的元组和数据类”,在 StackOverflow 之后他们就找到了我的文章。他们打开页面的目的是了解 NamedTuple 和 Dataclass。这些人是我的目标读者,我希望他们的问题在关闭页面之前得到回答。

统计数据还显示,谷歌是我最大的流量来源,这证明大多数人来这里是有目的的。

我的 2 篇文章的流量来源(作者:高 T5)

全面了解所有经验等级

当有需要时,让我们用尽可能清晰和详细的信息来满足它,而不要对读者的背景知识做任何假设。几乎我所有的博客都是从一点基础知识开始,然后才开始写代码。我不提供太多大块的代码。但是我喜欢详细描述每一个命令,它是做什么的,为什么它是这样工作的。这些细节为读者提供了提高技能所需的信息。我经常问自己,我的文章是否包含太多的隆隆声或明显的事情,但每当我看到类似“xxx 不清楚”的评论时,我知道我应该解释得更清楚。

中国有句谚语:给一个人一条鱼,你可以喂他一天。教一个人钓鱼,你可以喂他一辈子。

不要把你的技术博客写得太专业

人们(包括我在内)有时更喜欢阅读科技博客而不是 StackOverflow 的原因是,我们希望有更多的背景,听到好的故事。即使它是一个科技博客,你仍然可以讲一个关于它的好故事。有什么问题?为什么这对我们来说是个问题?怎么才能解决呢?你能举一些例子来支持你的说法吗?对读者有什么建议?这有助于非工程师理解你的观点,他们可能会将你的文章推荐给他们的工程师同事,因为他们认为这可能对他们有用。

我喜欢用图表和例子来解释事情。一张图表不仅能表达成千上万的单词,还能在阅读文章时创造一种精神上的休息。例子总是能很好地向人们展示你所说的是实实在在的东西,而不是火箭科学。

你是怎么保持节奏的?

许多跑步者努力了几个月,但一越过终点线,他们就停止了训练。当你所有的努力都集中在写一篇文章上时,当你完成后,还有什么能推动你前进呢?《原子习惯》这本书告诉我,发展一个长期的系统比拥有一个目标驱动的心态更具可持续性。

真正的长期思考是没有目标的思考。它不是关于任何单一的成就。这是一个不断完善和不断改进的循环。最终,你对这个过程的承诺将决定你的进步。—原子习惯

你的写作目标应该像“每月发表 X 篇文章”而不是“发表 10 篇文章”那样与你的持续进步相关联。

来自互联网、朋友和同事的积极反馈是让你继续前进的另一个因素。尝试在社交媒体上展示你的作品,并鼓励人们给你评论。当你获得更多的参与度时,你会更愿意开始下一个博客。相信我!

像往常一样,我希望你会发现这篇文章很有用,并兴奋地开始你的第一个科技博客。😃)

参考

这些材料可以提高你的技术写作技巧:

https://www.digitalocean.com/community/tutorials/digitalocean-s-technical-writing-guidelines https://developers.google.com/tech-writing

我的三种离群点检测方法

原文:https://towardsdatascience.com/my-three-go-to-outlier-detection-methods-49d74dc3fc29

异常值检测至关重要,尤其是对于数据质量评估

来自像素的免费使用照片

介绍

离群点检测在许多不同的方面都是至关重要的。如果一家公司想要了解异常/非典型的客户行为,它需要首先识别这样的客户。离群点检测技术在这种情况下发挥了作用。离群点检测对于检查数据集的数据质量也非常有用。在这里,我们来看看常用于检测异常值的三种主流方法。

IQR /标准差方法

Outliers are defined as:Points that are < mean/median - n * IQR/Std or > mean/median + n * IQR/Std

IQR 代表四分位距。要理解这个概念,首先需要知道四分位数是什么意思。在统计学中,四分位数是将数据分成四份的值,因此自然会有四个四分位数。1它们中的每一个通常被表示为 Q1、Q2、Q3 和 Q4。四分位范围(IQR)是数据集的中间五十,可表示为 Q3- Q1。[2]它经常被用来代替标准偏差(Std)来衡量分布和变化,因为后者更不稳定,对异常值更敏感。

使用 IQR 和标准偏差检测异常值的方法非常简单,位于 n 倍 IQR 或标准偏差定义的特定范围之外的数据点可以被视为异常值。但是,请注意,这种方法对于单变量类型的数据是有效的。

记住高斯分布的一个特性。偏离平均值 3 个标准偏差之外的点仅占分布的 1%。这意味着,与大多数其他点相比,构成 1%分布的那些点是非典型的,并且可能是异常值。当然,现实世界中的数据很少是完美的高斯分布,但是这个更大的概念仍然成立,即远离平均值或中值的点很可能是异常值。

没有用于设置阈值 n 设置规则。这取决于异常值检测的目的以及用户希望异常值检测是保守的还是全面的。因此,在一些测试数据上修改阈值将是一个好主意。

k 表示聚类

k 表示聚类是数据科学领域中使用的最经典、最传统的聚类方法之一。在这里,我不会深入讨论聚类算法本身是如何工作的。请回顾一篇解释这种算法如何工作的文章。[3]

尽管是一个聚类算法,它也可以用于离群点检测!

一种方法是将簇的数量设置为 K = 1。然后,质心将成为数据中所有点的平均值。然后,计算所有点的欧几里德距离。根据要标记为异常值的点的数量,可以选择距离质心最远的前 n 个点。k 意味着可以通过 Python 的 scikit-learn 库轻松实现集群。参考下面的示例代码,假设存储在变量 df 中的数据有两个数字列 V1 和 V2。(如果包括分类变量,记得对变量进行编码)。

**import** pandas **as** pd
**import** numpy **as** np
**from** sklearn.cluster **import** KMeans### Assume you already read in data in pandas in the variable called df (with two numerical columns V1 and V2)X = df.to_numpy() # change the dataframe to numpy matrixkmeans **=** KMeans(n_clusters**=**1)
kmeans**.**fit(X)
distances **=** kmeans**.**transform(X) # apply kmeans on data# If you want to flag 50 points as outliers, grab indexes flagged as outliers
sorted_idx **=** np**.**argsort(distances**.**ravel())[::**-**1][:50]

另一种方法是使用 k > 1 个聚类,并标记最小大小的聚类中的所有点。当然,您必须首先通过使用 elbow 方法或剪影评分方法来确定最佳聚类数,我不会在这里详细介绍这两种方法。

隔离森林

使用尽可能少的技术术语的隔离森林是一种算法,它不使用随机森林进行预测,而是如其名称所示“隔离”点。

主要思想如下-它试图“通过随机选择一个特征,然后随机选择所选特征的最大值和最小值之间的分割值来隔离观察值。”[4]我们把这种隔离过程称为“分割”。然后,每一轮划分可以被认为是“一棵随机树”,然后所有这些树的集合将是“随机森林”。分离一个样本所需的分裂数就是“树的深度”或“从根到终端节点的路径长度”。然后,在随机森林中的所有树上对该路径长度进行平均,并成为算法的度量。该算法假设使用相对较少数量的分区更容易隔离离群点(因此平均而言隔离在树的较浅深度)。

请参考再次使用 scikit 学习包的隔离森林的 Python 实现示例。

**import** pandas **as** pd
**import** numpy **as** np
**from** sklearn.ensemble **import** IsolationForestdata = df[['V1','V2','V3']] # assume there is data with three numerical columns V1, V2, and V3min_max_scaler = preprocessing.StandardScaler()data_scaled = min_max_scaler.fit_transform(data)*# train isolation forest
outliers_fraction = 0.2 # you can set how much would be flagged as outliers*model =  IsolationForest(contamination = outliers_fraction)model.fit(data)*# add the anomaly flags to the data*data['anomaly'] = pd.Series(model.predict(data_scaled))# Flagged outlier points are labelled as -1 and non-outlier points are labelled as 1 and so relabel them as binary outcomes (1 and 0) data['anomaly'] = data['anomaly'].map( {1: 0, -1: 1} )

还有其他多种异常检测算法,包括 DBSCAN、局部异常因子(LOF)等。我希望在其他一些帖子中讨论这个问题!如果你感兴趣,请关注我并订阅我的帖子: )

参考

1统计学如何,什么是四分位数?定义

[2]统计学如何,四分位距(IQR):它是什么以及如何找到它

[3] S .耶尔德勒姆,走向数据科学,K-均值聚类—解释(2020)

[4] Python Scikit 学习文档

关于作者

数据科学家。在密歇根大学刑事司法行政记录系统(CJARS)经济学实验室担任副研究员。Spotify 前数据科学实习生。Inc .(纽约市)。即将入学的信息学博士生。他喜欢运动,健身,烹饪美味的亚洲食物,看 kdramas 和制作/表演音乐,最重要的是崇拜耶稣基督。结账他的 网站

我对 2022 年人工智能的五大预测

原文:https://towardsdatascience.com/my-top-5-predictions-for-ai-in-2022-b5745646899

DeepMind、特斯拉、OpenAI 等等。

照片由 solarsevenShutterstock 上拍摄

2021 年对于人工智能来说是令人惊叹的一年。

伦理比以往任何时候都更处于人工智能研究的中心。我们对语言模型带来的伤害风险有了更好的理解——公司不断改进语言模型,使它们不仅更大,而且更聪明更高效,多模态系统更加常见(例如谷歌的 MUM 和 OpenAI 的 DALL E ,现实世界的人工智能正在向前跨越和向后跨越。总而言之,人工智能保持甚至加快了我们在过去十年中看到的进步步伐。

2022 年还会继续这个方向。人工智能社区将带来新的有希望的发展和令人印象深刻的突破,其中一些我们可以预见。我列出了 2022 年将会发生或不会发生的 5 大最有影响力的人工智能事件和发展。(其他更不可预测的里程碑肯定会发生,但我们会感到惊讶。)

1.探索构建语言模型的新方法

“越大越好”的趋势将在 2022 年消失。

语言模型是现在人工智能的事情。计算机视觉在过去十年的最初几年很流行,但从 2017 年起,语言已经成为最先进的人工智能机构和组织的关注焦点和利润来源。你将很难找到一家世界级的人工智能公司或初创公司没有声称自己在人工智能语言市场的份额。

DeepMind,作为可以说是全球排名第一的人工智能公司,这些年出奇地安静。不再是了。

几周前,DeepMind 发表了三篇关于语言模型的论文。这条新闻被害羞地报道了——我们已经对语言模特的揭露形成了一种宽容,因为在过去的四年里,媒体一直在不间断地喂养她们。然而,科学不会发生在头条新闻中,而是发生在实验中,DeepMind 的结果一点也不害羞。

第一篇论文以 Gopher 为特色,这是一个 2800 亿参数的神经网络,在 124 个任务中的 100 个任务中表现出惊人的性能,超过了 GPT-3、J1-Jumbo 和 MT-NLG(之前的前 3 名模型,我们有性能数据)。Gopher 是一种密集的大型语言模型(LLM ),构建方式与其他语言模型相同。尽管性能有所提高,但系统几乎没有任何创新。它被以同样的方式训练,工作方式相似但更好,并且也从事有毒的行为。

第三篇论文引起了我的注意。 RETRO (检索增强型变压器)是一个 70 亿参数的模型,功率与 GPT-3 和 J1-Jumbo(大 25 倍)相当。该模型的计算能力比 GPT-3 少 10 倍,并使用检索机制,允许它实时访问大型数据库,无需像以前的模型那样将所有知识保存在内存中。(很快会有关于复古的深度文章!)

在这个意义上,复古更像人类。我们不会把所有的知识都保存在记忆里;我们看书,在网上搜索,寻找新的信息。DeepMind 押注于脱离越来越大的语言模型趋势。从长远来看,这是不可持续的——人工智能的碳足迹不断增加,高昂的成本使得除了少数几个价值数十亿美元的公司之外,这项技术是负担不起的,如果增长率在未来几年保持不变,人工智能硬件将很难训练快速变化的模型。

需要像 RETRO 的知识检索这样的新颖技术。

另一种产生有希望结果的方法是稀疏语言模型。稀疏性在于只使用模型参数的一部分来进行计算——不同的部分或“专家”被有条件地激活。这与密集模型(GPT-3 和地鼠)形成对比,在密集模型中,所有参数都有助于处理所有输入。开关变压器(1.7T)武道 2.0 (1.75T),或者 M6 (10T)都是稀疏模型的例子。

我们还将看到人工智能芯片利用稀疏性来跟上语言模型的发展。人工智能硬件初创公司 Tenstorrent 是这种方法的显著支持者。他们设计更智能的芯片,更有效地利用他们的计算能力,而不是制造更大、更昂贵的芯片。

我们正在见证 4 年来模型越做越大的趋势的最后几天。我们可能永远看不到 100 万亿参数模型。越大并不总是越好,软件和硬件公司都开始注意到这一点。设计和构建语言模型的新方法将是 2022 年人工智能的主要趋势。

2.OpenAI 将发布 GPT 4 号——我们会大吃一惊

我们不期待 OpenAI 为 2022 年预留了什么。

几个月前,我发表了一篇题为“ GPT-4 将拥有 100 万亿个参数——是 GPT-3 的 500 倍。”我不再袖手旁观那个标题。考虑到我所说的关于语言模型的新方法,我相信 OpenAI 也将采取一个新的方向,并且不再仅仅为了它而制造更大的模型。GPT 3 号的大小是 GPT 2 号的 100 倍,但是 GPT 4 号的大小将和 GPT 3 号差不多——尽管它的工作方式不同。

当我写前面提到的文章时,我认为——和大多数人一样——open ai 将继续从 GPT、GPT-2 和 GPT-3 到 GPT-4 的扩展趋势。脑波强化系统公司的首席执行官安德鲁·费尔德曼在宣传他们最新的人工智能芯片 WSE-2 时,暗示open AI 将把他们的芯片用于 GPT 家族的下一个版本——100T 参数模型。但 OpenAI 的首席执行官山姆·奥特曼否认了这些传言。在一个私人 Q & A (我不会给出细节,因为奥特曼要求助手不要透露信息)中,他说 GPT-4 不会有 100T 参数,并将是一个纯文本模型(没有多模态)。他还说,它将包括尚未在《GPT 3》中看到的新功能,但没有具体说明是哪些。

OpenAI 的研究人员显然正在努力提高效率和功率,同时保持大小不变(就像 DeepMind 对 RETRO 所做的那样),完全摆脱了缩放假设。检索技术、条件处理和更高效的转换器架构是一些已知的大型模型的替代方法,但是 OpenAI 还可以尝试许多其他的东西。

我们得等 GPT 4 号出来。很快就会有惊喜了。

3.特斯拉不会制造自主人形机器人

埃隆·马斯克是一个表演者,特斯拉是他的旗舰,我们是他的观众,世界是他的游乐场。

2021 年特斯拉人工智能日埃隆·马斯克以他特有的古怪风格承诺,特斯拉将在“明年某个时候”推出一款自主人形机器人的原型——他们称之为擎天柱。他们计划将(仍不完善的)自动驾驶技术转换成人形。马斯克认为特斯拉非常适合从事这个项目,因为它非常专注于自主性、超级计算和现实世界的人工智能。

擎天柱的主要目的是“消除危险、重复和无聊的任务。”马斯克说,人们将能够使用自然语言来指挥它。你可以说:“拿起那个螺栓,用那个扳手把它固定在汽车上,”或者“去商店给我买下列杂货。”然而,马斯克没有意识到——或者也许他意识到了,只是把擎天柱作为另一个宣传噱头——制造一个人形自主机器人比制造一辆自动驾驶汽车要困难得多——特斯拉仍然远远没有实现这一壮举。

人类是多感官的,因为我们在一个多模态的世界中进化。自动驾驶汽车只有视觉,具有讽刺意味的是,这使它们看不到其他任何东西——声音、纹理、压力、温度、气味、味道……人类将不同的感知通道集成到现实的单一表示中,然后我们用它来导航世界并做出决策。Optimus 需要触觉和触觉传感器、本体感受能力以及对其身体内部的精确描述——这些都不存在于自动驾驶技术中。

最重要的是,Optimus 需要一个系统来选择和过滤重要的感知信息——因为它对手头的任务有用,因为它威胁到生命,或者因为任何其他原因介于两者之间。在语言模型中,注意力机制很容易,但当涉及到组合和选择感知并决定要注意哪个时,技术远远不能接近人类的水平。

一旦 Optimus 决定了哪些感知是重要的,它就需要处理信息,计划后续行动,决定哪些是紧急的,哪些不是,并考虑到世界和自身不断变化的性质来采取行动。自动驾驶汽车只有两个目标:遵循交通规则从 A 地前往 B 地,避开路上的一切。

自主机器人也需要像我们一样了解周围的世界。如果天空中有云,擎天柱需要推断可能会下雨。如果下雨,地板可能太湿了。如果地板是湿的,擎天柱可能会滑下来摔倒。这个推理链对我们来说是直接的,但对人工智能来说却远非显而易见。

自 20 世纪 50 年代人工智能的概念提出以来,多感官感知、注意力、规划、决策、常识和因果推理一直是人工智能无法企及的。我们还没有采取微小的步骤将它们灌输给机器人。

埃隆·马斯克不会在一年内让它成为可能。

4.正在进行的反对人工智能语言偏见的斗争

人工智能伦理学在过去几年里已经成为一个热门领域。2022 年,我们将看到公司对此更加认真,甚至将努力置于利润之上——尽管可能是因为他们已经计算出最终在经济上是值得的。

随着语言模型变得越来越大,越来越强大,它们也增加了潜在的危害。公司已经实现了减少模型偏差和从数据集继承的毒性的技术。一些方法,如管理和过滤数据,在训练之前应用,而另一些方法,如微调模型以改善它们的行为,监控应用程序发布,或定义和执行严格的用例指导方针,在下游更好地实现。

但是,尽管做出了努力,没有一个语言模型能免于成为这些缺陷的牺牲品。

道德专家一再报告称,公司在减少风险行为方面做得不够。他们批评公司将利润作为优化的目标,其他一切——包括对人的影响——都取决于此。专家承认,大型语言模型的问题是固有的,很难消除。这就是为什么艾米丽·m·本德和蒂姆尼特·格布鲁等人建议研究人员也考虑“超越更大的语言模型”的其他方向

DeepMind 的第二篇论文(我之前没有提到)包含了对语言模型带来的道德和社会风险的严格分类。他们列出了风险的结构,分析了风险的来源,定义了潜在的缓解方法,并推荐了未来的工作方向。他们认识到,要将伤害风险降低到公开使用这些模型对所有类型和种类的人都足够安全的程度,还有很多工作要做。

大型语言模型不会很快停止开发。偏见和毒性不会很快从这些模型中 100%消除。在未来的几年里,我们将会看到两股力量,一股是以发展技术为主要目标的力量,另一股是以让世界变得更美好为主要目标的力量,推动和推动双方达成妥协。我不相信我们会完全消除人工智能系统中的有害行为,但我希望人工智能社区最终会意识到 LLM 的能力有限,越建越大不是前进的方向。

5.自动驾驶汽车仍然不会自动驾驶

世界上还没有一家公司造出自动驾驶汽车。

特斯拉引入了“完全自动驾驶”和“自动驾驶”的概念,以吸引客户认为他们已经接近拥有可以自动驾驶的汽车,但这不是真的。2020 年,德国当局对这种行为采取了行动,禁止特斯拉出于营销目的使用这种误导性语言。

自动驾驶汽车还远未成为现实。特斯拉 Autopilot 帮助汽车导航常见场景,但未能解决人类会立即知道最佳行动的边缘情况——这导致了事故差点事故错误

我很清楚,完全自动驾驶不会很快准备好。但并不是所有人都同意。

自 2015 年以来,埃隆·马斯克一直承诺完全自主。在英伟达的开幕主题演讲中,GTC·马斯克说:“我不认为我们需要担心自动驾驶汽车,因为这是一种狭义的人工智能。这并不是我认为很难的事情。[……]我几乎把它视为一个已经解决的问题。”在那之后,马斯克已经承诺每两年提供完全的自动驾驶能力。

2016 年,他认为将在 2017 年做好准备:“我对从洛杉矶到纽约的完全自主示范驾驶的目标感觉非常好。”2018 年:“我认为明年我们将实现完全自动驾驶,作为一种通用的解决方案。”2020 年,他说完全自动驾驶将在今年年底“功能完善”。

他每次都错了。

2021 年,他在推特上承认了挑战的难度。

其他公司正在押注自动驾驶。他们没有特斯拉那么受欢迎,也没有追随它的脚步。 WaymoCruise 在路上行驶的汽车更少,以换取采取更安全和更高科技的方法。大多数专家都同意自动驾驶仅仅依靠视觉是不可能的——激光雷达和其他测绘技术将是更上一层楼所必需的。然而,尽管他们做出了努力,但在完全自主的竞赛中,他们远远落后于特斯拉。

特斯拉似乎比其竞争对手走得更快,原因有二:首先,他们采用了纯视觉方法;只是神经网络在做它们最擅长的事情,处理汽车周围摄像头捕捉到的大量数据。然而,完全不清楚——尽管马斯克——自主性问题可以通过这种方式解决。其次,特斯拉更擅长销售汽车,而不是制造汽车。不是每个人都喜欢埃隆·马斯克和特斯拉,但喜欢的人都是死忠粉丝。特斯拉汽车充斥道路

特斯拉又一年将无法达到预期,要么是因为技术尚未准备好,要么是因为它的方法存在固有缺陷。其他公司也不会制造自动驾驶汽车,因为安全意味着进展缓慢——尽管在这种情况下,这是值得的。

特斯拉感觉自己是自动驾驶技术的领导者,但它只是一个声音更大的参与者。马斯克是逃避未兑现承诺的大师。无人驾驶汽车比大多数人认为的还要遥远——马斯克不在其中。

如果你喜欢这篇文章,可以考虑订阅我的免费周报 【明天的想法】 !每周都有关于人工智能和技术的新闻、研究和见解!

您也可以直接支持我的工作,使用我的推荐链接 这里 成为中级会员,获得无限权限!😃

我对数据科学的理解正在向一个新的方向发展

原文:https://towardsdatascience.com/my-understanding-of-data-science-is-evolving-to-a-new-direction-5863f815dac5

意见

我不知道对此作何感想

丹·克里斯蒂安·pădureț在 Unsplash 上拍摄的照片

2019 年,数据科学开始成为我生活中的一件事。从那以后,我一直充满激情和好奇心地学习数据科学,几乎每天都有新的发现。

数据一直是数据科学的中心,这是意料之中的常态。数据科学就是从数据中提取信息、见解或价值。

利用数据创造商业价值的潜力显著增加,这推动了对数据、工具和技能的巨大需求。由于所有这些转变,工具的数量激增。

在我开始数据科学之旅时,我对数据科学家的理解是一个能够理解数据告诉我们什么的人。数据科学家超越表面现象,提取隐藏的信息。

到目前为止,我觉得成为一名数据科学家与你使用工具的能力更有关系。不要误解我。你阅读、理解和理解数据的能力仍然至关重要。然而,你在某些工具上的技能已经凸显出来。

我对数据科学的理解越来越像你在数据科学生态系统中使用工具的能力。我不知道对此作何感想。

显然,我们不能忽视工具。使用软件工具和软件包来清理、处理和分析数据总是势在必行的,因为我们可能要处理大量的数据。然而,工具的数量和复杂性一直在增加。因此,数据科学家花费大量时间学习使用这些工具。

这有什么不好?

首先,使用更高效更快捷的工具没有错。时间和计算能力是重要的资源,所以如果有一种工具有可能节省我们一些时间或计算,忽略它是不明智的。

但是,这不应该导致您忽略更重要的东西:数据。

掌握好手头的数据比任何其他工具都有用。例如,有几个超参数调整工具,可以帮助您找到最佳的超参数值,并可能实现对模型的改进。

另一方面,通过掌握数据的结构和属性并包含一些业务上下文来派生一个新特性,与超参数调优相比,您可能会获得显著的改进。

我必须学习所有的工具吗?

绝对不行!然而,数据科学不是一个单独从业者的工作。你是团队的一员,所以团队使用的工具是你需要学习的。考虑到数据科学生态系统中的高流动率,您可能会频繁更换公司,这可能意味着需要学习新的工具。

对于大公司来说,情况可能略有不同

如果你在一家大型公司担任数据科学家,你的任务可能会局限于一个较窄的范围。在这种情况下,你不必处理或学习这么多工具,因为你专注于一个特定的任务。

我认为这只对有限的几家公司有效。大量的数据科学家工作需要你参与数据科学或机器学习工作流程的多个步骤。

我想解释的是,作为一名数据科学家,还是作为一名数据科学工具专家。你不能完全忽略其中任何一个。然而,我强烈建议不要失去重点,不要忘记数据科学到底是什么。

如果你成为一个工具专家,你可以执行你被告知要做的任务。这绝对是一项宝贵的技能。除此之外,如果你帮助用数据做决策,你成为一名杰出的数据科学家的机会就会大大增加。

了解你的数据!

你可以成为 媒介会员 解锁我的全部写作权限,外加其余媒介。如果你已经是了,别忘了订阅https://sonery.medium.com/subscribe如果你想在我发表新文章时收到电子邮件。

感谢您的阅读。如果您有任何反馈,请告诉我。

MySQL 到 DynamoDB:使用 Kafka 在 AWS 上构建流数据管道

原文:https://towardsdatascience.com/mysql-to-dynamodb-build-a-streaming-data-pipeline-on-aws-using-kafka-c2cf0b6e35b6

通过 MSK 连接使用变更数据捕获在 Aurora MySQL 和 DynamoDB 之间同步数据

这是博客系列的第二部分,它提供了 Kafka 和 Kafka Connect 的数据管道的逐步演示。出于演示目的,我将使用 AWS,但是这些概念适用于任何等效的选项(例如,在 Docker 中本地运行这些选项)。

达丽娅·朱姆Unsplash 上拍摄的照片

这一部分将展示变更数据捕获的作用,它让您可以跟踪数据库表中的行级变更,以响应创建、更新和删除操作。例如,在 MySQL 中,这些变更数据事件通过 MySQL 二进制日志(binlog) 公开。在第 1 部分中,我们在数据管道的源部分使用了 Datagen 连接器——它帮助我们为 MSK 主题生成模拟数据,并使事情变得简单。

我们将使用 Aurora MySQL 作为数据源,并通过Debezium connector for MySQL利用其变更数据捕获功能,从 Aurora MySQL 中的表中实时提取数据,并将其推送到 MSK 主题。然后,我们将像以前一样继续使用 DynamoDB 接收器连接器。

如果你刚接触Debezium

它是一个分布式平台,构建在不同数据库中可用的变更数据捕获功能之上。它提供了一组 Kafka Connect 连接器,这些连接器利用数据库表中的行级更改(使用 CDC)并将它们转换成事件流。这些被发送到 Kafka,并可用于所有下游应用程序。

这是这篇博文中提出的解决方案的高级示意图。

高层架构(图片由作者提供)

我假设您是从第 1 部分开始学习的,在第 1 部分中,已经介绍了本教程所需的基础设施和服务的创建过程。如果您还没有,请参考第 1 部分章节中的准备基础设施组件和服务章节

数据管道第 1 部分:Aurora MySQL 到 MSK

让我们首先创建管道的前半部分,将 Aurora MySQL 表中的数据同步到 MSK 的一个主题。

在本节中,您将:

  • 下载 Debezium 连接器产品
  • 在 MSK 创建自定义插件
  • 将 Debezium 源连接器部署到 MSK 连接

最后,数据管道的前半部分将准备就绪!

创建自定义插件和连接器

将 Debezium 连接器上传到亚马逊 S3

登录 Kafka 客户端 EC2 实例并运行以下命令:

sudo -u ec2-user -i
mkdir debezium && cd debeziumwget https://repo1.maven.org/maven2/io/debezium/debezium-connector-mysql/1.9.0.Final/debezium-connector-mysql-1.9.0.Final-plugin.tar.gz
tar xzf debezium-connector-mysql-1.9.0.Final-plugin.tar.gzcd debezium-connector-mysql
zip -9 ../debezium-connector-mysql-1.9.0.Final-plugin.zip *cd ..
aws s3 cp ./debezium-connector-mysql-1.9.0.Final-plugin.zip s3://msk-lab-<ENTER_YOUR_AWS_ACCOUNT_ID>-plugins-bucket/

创建自定义插件

有关如何创建 MSK 连接插件的分步说明,请参考官方文档中的 使用 AWS 管理控制台 创建自定义插件。

创建定制插件时,确保选择您在上一步上传到S3的 Debezium 连接器 zip 文件。

创建 Debezium 源连接器

关于如何创建 MSK 连接连接器的逐步说明,请参考官方文档中的 创建连接器

要创建连接器:

  1. 选择您刚刚创建的插件。
  2. 输入连接器名称,并选择 MSK 集群和 IAM 身份验证
  3. 您可以在连接器配置部分输入下面提供的内容。确保根据您的设置替换以下配置:
  • database.history.kafka.bootstrap.servers -输入 MSK 集群端点
  • database.hostname -输入 Aurora RDS MySQL 端点

保持其余配置不变

connector.class=io.debezium.connector.mysql.MySqlConnector
database.user=master
database.server.id=123456
tasks.max=1
database.history.kafka.topic=dbhistory.salesdb
database.history.kafka.bootstrap.servers=<ENTER MSK CLUSTER ENDPOINT>
database.server.name=salesdb
database.port=3306
include.schema.changes=true
database.hostname=<ENTER RDS MySQL ENDPOINT>
database.password=S3cretPwd99
database.include.list=salesdb
value.converter.schemas.enable=false
key.converter.schemas.enable=false
key.converter=org.apache.kafka.connect.storage.StringConverter
value.converter=org.apache.kafka.connect.json.JsonConverter
database.history.consumer.security.protocol=SASL_SSL
database.history.consumer.sasl.mechanism=AWS_MSK_IAM
database.history.consumer.sasl.jaas.config=software.amazon.msk.auth.iam.IAMLoginModule required;
database.history.consumer.sasl.client.callback.handler.class=software.amazon.msk.auth.iam.IAMClientCallbackHandler
database.history.producer.security.protocol=SASL_SSL
database.history.producer.sasl.mechanism=AWS_MSK_IAM
database.history.producer.sasl.jaas.config=software.amazon.msk.auth.iam.IAMLoginModule required;
database.history.producer.sasl.client.callback.handler.class=software.amazon.msk.auth.iam.IAMClientCallbackHandler
transforms=unwrap
transforms.unwrap.type=io.debezium.transforms.ExtractNewRecordState
  1. 访问权限下,为连接器选择正确的 IAM 角色(名称中带有AuroraConnectorIAMRole的角色)
  2. 点击下一个的进入安全选项——保持不变
  3. 点击下一个。对于日志交付,选择交付至亚马逊云观察日志。找到并选择/msk-connect-demo-cwlog-group
  4. 点击下一步 —在最后一页,向下滚动并点击创建连接器以启动流程并等待连接器启动。

完成后,连接器转换到运行状态,继续以下步骤。

测试管道

我们想要确认来自salesdb数据库中的SALES_ORDER表的记录是否已经被推送到 MSK 主题。为此,从 EC2 主机运行 Kafka CLI 消费程序。

注意题目名称*salesdb.salesdb.SALES_ORDER*——这是根据 Debezium 约定

sudo -u ec2-user -iexport MSK_BOOTSTRAP_ADDRESS=<ENTER MSK CLUSTER ENDPOINT>/home/ec2-user/kafka/bin/kafka-console-consumer.sh --bootstrap-server $MSK_BOOTSTRAP_ADDRESS --consumer.config /home/ec2-user/kafka/config/client-config.properties --from-beginning --topic salesdb.salesdb.SALES_ORDER | jq --color-output .

在另一个终端中,使用 MySQL 客户端连接到 Aurora 数据库并插入几条记录:

sudo -u ec2-user -iexport RDS_AURORA_ENDPOINT=<ENTER RDS MySQL ENDPOINT>mysql -f -u master -h $RDS_AURORA_ENDPOINT  --password=S3cretPwd99USE salesdb;select * from SALES_ORDER limit 5;INSERT INTO SALES_ORDER (ORDER_ID, SITE_ID, ORDER_DATE, SHIP_MODE) VALUES (29001, 2568, now(), 'STANDARD');
INSERT INTO SALES_ORDER (ORDER_ID, SITE_ID, ORDER_DATE, SHIP_MODE) VALUES (29002, 1649, now(), 'ONE-DAY');
INSERT INTO SALES_ORDER (ORDER_ID, SITE_ID, ORDER_DATE, SHIP_MODE) VALUES (29003, 3861, now(), 'TWO-DAY');
INSERT INTO SALES_ORDER (ORDER_ID, SITE_ID, ORDER_DATE, SHIP_MODE) VALUES (29004, 2568, now(), 'STANDARD');
INSERT INTO SALES_ORDER (ORDER_ID, SITE_ID, ORDER_DATE, SHIP_MODE) VALUES (29005, 1649, now(), 'ONE-DAY');
INSERT INTO SALES_ORDER (ORDER_ID, SITE_ID, ORDER_DATE, SHIP_MODE) VALUES (29006, 3861, now(), 'TWO-DAY');

如果一切都设置正确,您应该在消费者终端中看到记录。

{
  "ORDER_ID": 29001,
  "SITE_ID": 2568,
  "ORDER_DATE": 1655279536000,
  "SHIP_MODE": "STANDARD"
}
{
  "ORDER_ID": 29002,
  "SITE_ID": 1649,
  "ORDER_DATE": 1655279536000,
  "SHIP_MODE": "ONE-DAY"
}
{
  "ORDER_ID": 29003,
  "SITE_ID": 3861,
  "ORDER_DATE": 1655279563000,
  "SHIP_MODE": "TWO-DAY"
}
...

压缩变更事件有效负载的秘密

请注意变更数据捕获事件负载有多紧凑。这是因为我们将连接器配置为使用 Kafka 单消息转换 (SMT)的io.debezium.transforms.ExtractNewRecordState。默认情况下,Debezium 的变更事件结构非常复杂——除了变更事件,它还包括元数据,如模式、源数据库信息等。它看起来像这样:

{
  "before": null,
  "after": {
    "ORDER_ID": 29003,
    "SITE_ID": 3861,
    "ORDER_DATE": 1655279563000,
    "SHIP_MODE": "TWO-DAY"
  },
  "source": {
    "version": "1.9.0.Final",
    "connector": "mysql",
    "name": "salesdb",
    "ts_ms": 1634569283000,
    "snapshot": "false",
    "db": "salesdb",
    "sequence": null,
    "table": "SALES_ORDER",
    "server_id": 1733046080,
    "gtid": null,
    "file": "mysql-bin-changelog.000003",
    "pos": 43275145,
    "row": 0,
    "thread": null,
    "query": null
  },
  "op": "c",
  "ts_ms": 1655279563000,
  "transaction": null
...

多亏了 Kafka SMT(使用transforms.unwrap.type=io.debezium.transforms.ExtractNewRecordState指定),我们可以有效地flatten事件有效负载,并根据我们的需求定制它。

详见 Debezium 文档中的 新记录状态提取

数据管道第 2 部分:MSK 到 DynamoDB

现在,我们可以将注意力转移到管道的后半部分,在 DynamoDB Sink 连接器的帮助下,该部分负责将数据从 MSK 主题传递到 DynamoDB 表。

如果 DynamoDB 表不存在,连接器会自动为您创建一个,但它使用默认设置,即在配置模式下创建一个表,其中包含10读取容量单元(rcu)和10写入容量单元(wcu)。

但是您的用例可能需要一个配置。例如,为了处理大量数据,您可能想要配置自动缩放,或者更好的是,为您的表激活按需模式

这正是我们要做的。

在继续之前,创建一个 DynamoDB 表

使用以下设置:

  • 表名— kafka_salesdb.salesdb.SALES_ORDER(不要而要改变表名)
  • 分区键— ORDER_ID(数字)
  • 范围键— SITE_ID(数字)
  • 容量模式—按需

就这样,你可以走了!

创建自定义插件和连接器

有关如何创建 MSK 连接插件的分步说明,请参考官方文档中的 使用 AWS 管理控制台 创建自定义插件。

创建自定义插件时,确保选择您在上一步上传到S3的 DynamoDB 连接器 zip 文件。

关于如何创建 MSK 连接连接器的逐步说明,请参考官方文档中的 创建连接器

要创建连接器:

  1. 选择您刚刚创建的插件。
  2. 输入连接器名称,并选择 MSK 集群和 IAM 身份验证
  3. 您可以在连接器配置部分输入下面提供的内容。确保根据您的设置替换以下配置:
  • topics属性使用正确的主题名称(在本例中我们使用salesdb.salesdb.SALES_ORDER,因为这是 Debezium source connector 采用的主题名称格式)
  • 对于confluent.topic.bootstrap.servers,输入 MSK 集群端点
  • 对于aws.dynamodb.endpointaws.dynamodb.region,输入您创建 DynamoDB 表的地区,例如us-east-1

保持其余配置不变

connector.class=io.confluent.connect.aws.dynamodb.DynamoDbSinkConnector
tasks.max=2
aws.dynamodb.region=<ENTER AWS REGION e.g. us-east-1>
aws.dynamodb.endpoint=https://dynamodb.<ENTER AWS REGION>.amazonaws.com
topics=salesdb.salesdb.SALES_ORDER
value.converter.schemas.enable=false
key.converter=org.apache.kafka.connect.storage.StringConverter
value.converter=org.apache.kafka.connect.json.JsonConverter
table.name.format=kafka_${topic}
confluent.topic.bootstrap.servers=<ENTER MSK CLUSTER ENDPOINT>
confluent.topic.security.protocol=SASL_SSL
confluent.topic.sasl.mechanism=AWS_MSK_IAM
confluent.topic.sasl.jaas.config=software.amazon.msk.auth.iam.IAMLoginModule required;
confluent.topic.sasl.client.callback.handler.class=software.amazon.msk.auth.iam.IAMClientCallbackHandler
aws.dynamodb.pk.hash=value.ORDER_ID
aws.dynamodb.pk.sort=value.SITE_ID
  1. 访问权限下,为连接器选择正确的 IAM 角色(名称中带有DynamoDBConnectorIAMRole的角色)
  2. 点击下一步进入安全选项——保持不变
  3. 点击下一个的。对于日志交付,选择交付到亚马逊云观察日志。找到并选择/msk-connect-demo-cwlog-group
  4. 点击下一个 —在最后一页,向下滚动并点击创建连接器以启动该过程并等待连接器启动。

完成后,连接器转换到运行状态,继续以下步骤。

选择 DynamoDB 主键

在上述配置中,我们将aws.dynamodb.pk.hashaws.dynamodb.pk.sort分别设置为value.ORDER_IDvalue.SITE_ID。这意味着 Kafka 主题事件有效负载中的ORDER_ID字段将被用作分区键,而SITE_ID的值将被指定为范围键(根据您的需求,您也可以将aws.dynamodb.pk.sort留空)。

测试端到端管道

作为初始加载过程的一部分,连接器确保 Kafka topic 中的所有现有记录都被持久化到连接器配置中指定的 DynamoDB 表中。在这种情况下,您应该在 DynamoDB 中看到不止29000条记录(根据SALES_ORDER表),并且您可以运行查询来浏览数据。

为了继续测试端到端管道,您可以在SALES_ORDER表中插入更多数据,并确认它们通过 Debezium source 连接器同步到 Kafka,并通过 sink 连接器同步到 DynamoDB。

删除资源

完成后,删除您创建的资源。

  • 删除 S3 桶的内容(msk-lab-<YOUR ACCOUNT_ID>-plugins-bucket)
  • 删除云形成堆栈
  • 删除 DynamoDB 表
  • 删除 MSK 连接连接器、插件和自定义配置

结论和总结

变更数据捕获是一个强大的工具,但我们需要一种方法来利用这些事件日志,并使其对依赖于该数据的其他服务可用。在这一部分中,您看到了我们如何利用这一功能,使用 Kafka Connect 在 MySQL 和 DynamoDB 之间建立流数据管道。

这是本系列的一个总结,以后还会有更多(当然!).快乐大厦!

信息几何的神秘世界

原文:https://towardsdatascience.com/mystical-world-of-information-geometry-16b4637d89e8

信息论和机器学习的几何故事

该图像是由作者使用人工智能工具绘制的

在高中的时候,我们有些人对几何是又爱又恨,尤其是坐标和 3D 几何。更有甚者,微积分和几何是不被认可的。然后是信息技术的繁荣,随之而来的是机器学习、人工智能和数据科学的热潮。这个原因激发了许多人去更深入地挖掘一些数学和信息几何是其中之一。信息几何可以用在统计流形学习中,统计流形学习最近被证明是在高维数据集上进行无监督学习的有用工具。它还可以计算两个概率度量之间的距离,应用于模式匹配、为神经网络的训练构建替代损失函数、信任传播网络和优化问题。

信息几何是一种利用几何探索信息世界的数学工具。信息几何也被称为费歇尔几何,其原因将在本文的后面部分显而易见。

信息几何学是用几何学研究决策,可能还包括模式匹配、模型拟合等。但是为什么是几何方法呢?几何允许在一个无坐标的框架中研究不变性,提供了一个直观思考的工具,并允许我们研究等方差。例如,三角形的质心在仿射变换下是等变的。

让我们讨论一些基本原理来理解信息几何中有什么。

基本原则

为了理解微分几何,进而理解信息几何,我们需要了解什么是流形。在我之前关于流形对齐的文章中,我讨论了什么是流形,但我将在下面复制它:

n 维流形是具有极限、连续性和正确性的最一般的数学空间,并且允许与 n 维欧几里得空间存在连续的反函数。流形局部类似欧几里得空间,但它们可能不是欧几里得空间。本质上,流形是欧几里得空间的推广。

拓扑空间

考虑一个空间 X 由一组点 x∈ X 和一组称为每个点的邻域 N(x)X 的子集定义。我们有以下属性:

  1. 如果 Ux、x∈ U ,以及 V⊂XU⊂V 的邻域,那么 V 也是 x 的邻域。
  2. x 的两个邻域的交集也是 x 的一个邻域。
  3. x 的任意邻域 U 包含 x 的邻域 V ,这样 U 就是 V 中所有点的邻域。

任何满足上述性质的空间 X 都可称为拓扑空间。

同胚现象

流形对齐中,我讨论了同态,它可以公理化地表示为:

考虑到f:XY是两个拓扑空间之间的函数,那么 XY 同胚如果 f 连续,双射,并且 f 的逆也连续。考虑一个流形𝓜,对于所有点 x 𝓜,如果 Ux 的邻域,并且对于一个整数 n 使得 U 与ℝⁿ同胚,那么小的 n 是该流形的维数。

图表

由函数κ:uκ(u)表示的同胚称为图,其中 U 可能是𝓜.的开集有许多方法可以构建一个图表来定义𝓜.这些图表的集合被称为地图集。这个想法如图 1 所示。数学上,图谱将由等式 1 定义。图表的一个具体例子是坐标系统,它可以是映射流形上的点的函数。

图一。流形和图表(图由作者创建)

等式 1。一本地图册

此时,很容易定义一个可微流形:其转移映射(或函数)是无限可微的流形。

这就是抽象数学中的流形。它对统计学和数据科学的理解呢?记住,我们在统计学中处理概率。这导致了统计流形的概念。在统计流形中,每个点 p ∈ 𝓜对应于一个域𝓧.上的概率分布人们可以用一个由正态分布族形成的流形的具体例子来考虑这一点。

流形上的向量和切线:如何在弯曲空间上定义它们

在普通几何中,向量是连接两点的直线,但在弯曲空间中,这可能不是真的。曲线空间上的向量被定义为流形上特定点处曲线的切线。如果 u 是一个沿曲线变化的参数,那么一条曲线可能被定义为 x(u) ,往往去掉 u 部分,简单写成 x 。弯曲空间中的向量变成

等式 2。弯曲空间中的向量

其被局部定义在点 p 处,其中 u = 0 。注意,向量本身不在流形上,但它有欧几里得概念。像图表一样,在点 p 可以有许多可能的切线。是的,如果你只是考虑一个 2D 平面,这是令人难以置信的,但即使在像球体一样的三维空间中,在一个点上也可能有多条切线,那么我们就可以谈论球体上一个点的切面(图 2)。类似地,我们可以讨论流形在点 p 的切空间。

图二。有切面的球体(作者画的)

如果我们从一个图表转到另一个图表,这与从笛卡尔坐标到极坐标的坐标转换是一样的。假设有一个变换函数 ϕx 从一个图表变换到另一个图表,那么可以写成x′= ϕ(x).

双空间

向量空间 V 的对偶空间 V* 是包含 V 的所有线性泛函的空间,即所有映射t:vf,其中 FV 的向量空间所在的域。因此,对偶空间包含了从 VF 的所有线性映射。为了更好地理解双重空间,我在网上搜索,无意中发现了下面这个例子:

想象一个二维实向量空间。定义一个函数,它接受任何向量,并只返回其 x 坐标值。定义另一个函数,它接受任何向量,只返回它的 y 坐标值。让我们调用第一个函数 f1 和第二个函数 f2 。让我们想得更多一点。把这两个函数当作向量。特别是,把它们想象成有趣的向量空间中的基本向量。你可以把它们加在一起:把 f1 + f2 想象成一个函数,它接受任何向量并返回x-坐标和y-坐标之和。你可以把它们乘以数字:把 7 * f1 想象成一个函数,它接受任何一个向量,并返回乘以 7 的 x 坐标值。你可以组成任何你喜欢的线性组合:把 3.5 * f1 -5 * f2 想成一个取任意向量的函数,返回数字 3.5 乘以 x 坐标减去 5 乘以 y 坐标。这就是双重空间的作用。

张量

流形上的张量是最普遍的。它们可以被认为是数学上的多线性野兽,吃切空间及其对偶空间的向量,吐出实数。供给张量的来自切空间及其对偶空间的向量总数称为张量的秩。来自对偶空间的这些数给出了所谓的逆变秩,来自正切空间的这些数被称为协变秩

我将跳过对它们的正式讨论,因为老实说,我自己也不理解它们——现在还不理解。但本质上,流形是几何构造,张量是对应的代数构造。

公制的

度量是一个张量场,它在流形上的每一点的切空间上诱导一个内积。任何协方差秩为 2 的张量场都可以用来定义度量。一些资料称之为黎曼 Metric⁷.

现在,在一长串错综复杂的术语之后,我们来看看信息几何中有用的东西。

信息几何学

信息几何是统计学和微分几何交叉的一个数学分支,它从几何的角度研究概率分布。

我们首先需要看一下信息度量,也称为 Fisher 信息度量。

费希尔信息度量

如果我们希望在点 θ* 找到合适的度量张量,其中θ对应于分布族 p(x|θ) 中的一个,那么我们需要考虑 p(x|θ) 和它的无穷小扰动 p(x|θ + dθ)之间的距离概念。那么相对差值由等式 3 给出。当然

等式 3。 θ处扰动的相对差值。*

相对距离取决于随机变量 x 。如果你计算正确,那么δ的期望值,即𝔼(δ= 0。方差呢?原来方差是非零的。我们可以定义=𝔼[δ。根据第一个原理,对于公制𝓕,在 θ* 和 θ** 之间的无穷小位移的长度由 dl = 𝓕 dθ**给出。求解为dl=𝔼[δ]=𝓕dθ* dθ* **给出

等式 4。费希尔信息度量

也就是我们所说的费希尔信息度量(FIM) 。FIM 测量随机变量 X 的一次观察平均携带了多少关于参数 θ 的信息,如果xp(x∣θ)。还有另一种方法可以达到等式 4 的 1/2 倍,我建议读者参考参考文献 1。但是,如果它让你兴奋,量子信息科学的蓬勃发展有费雪信息度量的应用。关于这一点,我将很快有一个新的系列文章。如果你想得到通知,订阅!另一个应用是贝叶斯推理中无信息先验的设计。也许我现在走得太远了。

我将用最后一件事来结束这篇文章,费希尔信息矩阵𝓘,当我们处理多个参数时,它是𝓕的矩阵版本,可以用在类似于梯度下降的优化中,更新规则为

等式 5。微分流形语言中的优化更新规则。

其中 η 为学习参数, ∇J 为标量场 J 的散度。

在本文中,我试图提供信息几何和相关术语的简要概述。为了清楚起见,本文省略了大量的工作,我鼓励读者仔细阅读我在下面提供的参考资料。

这有帮助吗? 给我买杯咖啡

爱我的文字?加入我的 邮箱列表

想了解更多 STEM 相关话题?加入 中等

参考文献

  1. http://www . robots . ox . AC . uk/~ lsgs/posts/2019-09-27-info-geom . html*
  2. https://math . stack exchange . com/questions/240491/what-a-covector-and-what-is-it-used-for
  3. https://qr.ae/pv34JS
  4. https://frank Nielsen . github . io/SPIG-莱舒克斯 2020/geom stats-spigl 2020 . pdf*
  5. https://www.cmu.edu/biolphys/deserno/pdf/diff_geom.pdf
  6. https://math . UCR . edu/home/baez/information/information _ geometry _ 1 . html*
  7. https://mathworld.wolfram.com/RiemannianMetric.html

注:结尾带*的链接已在http://web.archive.org/存档。

一些相关的话题

打破神话:进入数据科学行业不需要的 3 样东西

原文:https://towardsdatascience.com/myth-busting-3-things-you-dont-need-to-break-into-the-data-science-industry-8f77d8c4c4ed

你需要的是什么

由作者编辑—原创自 Freepik

我知道发现数据科学和坠入爱河的感觉。这是一个令人兴奋和诱人的领域,即使你没有技能,你也决心成为一名数据科学家。

可悲的是,伴随着大肆宣传而来的还有错误信息。有人说做个认证,另一个认为没用。招聘人员要求提供世界上所有的技能,这让事情变得更糟,如果没有人愿意给他们机会,初学者甚至更难理解他们如何才能获得经验。有些人为了在美国攻读硕士而负债累累,希望一旦开始工作就能偿还。

神话压倒了你,让你偏离了旅程,让你沮丧、困顿、破产。

我知道,我的朋友,因为几年前我也经历过同样的事情。我理解你的感受,这也是我写这些文章的唯一原因,希望至少能帮助到你们中的一些人。

然而,请记住这一点:进入数据科学的途径有很多,我只向您展示其中一种。我写了进入数据科学的路线图,我的学习和在人工智能初创公司工作的经历,如果你想转行要知道的事情,以及我职业生涯早期最大的错误

本文将看到充斥着数据爱好者的常见误区。你可能也听过,现在是我们揭穿他们的时候了。

1.你不需要有 DS/CS 背景就能在数据科学方面做得很好

当数据科学爱好者来自不同的背景或想要从不同的领域转换时,这是他们最常见的担忧之一。

你认为他们总是喜欢有 DS/CS 背景的人。你认为会有一种无意识的对你的偏见。你觉得你可能不得不从头开始,即使你在不同的行业有多年的经验。

听着,我理解你所有的担忧——如果有 12 年以上零售经验的人有这种感觉,如果有博士学位的人有这种感觉,你的恐惧是自然的。但这是一个神话。

我不能代表所有公司发言,但我已经联系了无数来自物理、经济、电子、数学、统计、机电一体化、新闻、营销和其他领域的数据科学家。我有来自 CS 背景的同事,他们有着和我一样的能力。

猜猜我们都有什么共同点?

您需要的是:

数据科学技能。就这么简单。

没有相关的数据科学技能,你无法通过任何面试。面试官不在乎你的背景;他们更想知道你的技能。当你有技能并能展示出来的时候,没有人会拒绝你(稍后会有更多相关内容)。)

以下是我给你的一些建议:

我进行过招聘面试,虽然我想知道你的背景,但我只关注你解决数据科学问题的能力。如果有的话,你的非 DS/CS 背景对我来说是有吸引力的,因为它向我表明你为获得技能付出了额外的努力。

如果有的话,我想让你加入我的团队——明白吗?

2.成为数据科学家不需要硕士学位

让我在这里说实话——我相信这个神话已经很久很久了。

我曾经用 GRE,托福,什么的来准备“MS in US”的梦想。我害怕负债去支付一个我负担不起的学位,谢天谢地,这阻止了我,很久以后,我的导师(他也是谷歌的工程师)打破了这个神话。

他是这样说的:

“我从一所知名大学获得了硕士学位,但我的团队(谷歌)中有些人没有硕士学位,但他们的表现同样出色,甚至更好。硕士学位是历史上证明你专业技能的方式之一——还有其他方式。只要你能展示你的专业技能,你就会做得很好。”

我的第一份数据科学工作是大一新生,之前没有工作经验。我不相信我能把它收入囊中——直到我做到了。面试问题很棘手。然而,我刚刚结束了吴恩达的课程,所以所有的概念在我的脑海里都是新鲜的。

然后他们给了我一个带回家的任务,让我在几天后向整个团队展示我的解决方案。我做的报告让我得到了这份工作。我讲述了我以前是如何解决类似问题的,以及我面临的挑战。

你可以有一个硕士学位但仍然被拒绝,也可以没有硕士学位但仍然被录用——明白吗?

您需要的是:

你需要有一个展示你技能的项目组合。很自然,他们会对你的工作感到好奇,而你最终会在大约 50%的面试中谈论这个话题。

有许多方法可以解决这个问题。我是这样做的:

  • 在 GitHub 上公开构建项目。瞄准一个简单的项目,然后不断改进。
  • 免费使用 GitHub 页面创建一个简单的投资组合网站
  • 将所有相关链接添加到你的简历中。

这样做会极大地提高你被雇佣的机会。你不需要硕士;你需要一个投资组合。

3.你不需要工作描述中列出的 100%的技能来获得面试

塞巴斯蒂安·拉米雷斯 在 FastAPI 需要 4 年的经验,他在 1.5 年前创建了 FastAPI。PyCaret 库的创建者 Moez Ali 必须拥有超过 3 年的 PyCaret 经验,而 py caret 库是他一年前创建的。

如果不是《在数据科学领域建立职业生涯》这本书的作者,我可能不会勇敢地申请大多数工作。

事情是这样的:大多数职位描述是招聘经理的愿望清单,他们不想错过理想的候选人。他们知道理想的人几乎不存在并且对那些不完全符合他们要求的人完全没有意见。

经验法则是,只要你达到招聘者广告中职位要求的 60%,就可以申请。但是有更好的方法。

您需要的是:

你需要公开分享你的工作,这样招聘人员就能看到你并联系到你。

我现在虔诚地这样做。如果我在创作,我会公开创作。我在 LinkedIn 上分享我面临的挑战和学到的东西。

为什么选择 LinkedIn?这是每个招聘人员寻找人才的平台。

结果呢?平均来说,我会有 3-4 个招聘人员主动联系我进行面试。

当我听到新手说他们连面试都进不去,更别说被录用了,我感到很难过。我不是来吹牛的,只是告诉你我做了什么,收获了什么结果。

总结想法

我知道你为什么一直读到这里。打破神话很重要,尤其是当你心中有很多不确定性的时候。

但是猜猜什么更重要?将知识转化为行动的能力。

这是我敦促你采取行动的部分——因为这才是最重要的。

  • 在线获取相关的数据科学技能。有了我推荐的所有课程,而且大部分是免费的,你可以在家学习,你没有任何借口。你来自什么背景一点都不重要。
  • 创建一个展示你技能的项目组合。使用本指南开始。这是你在 50%的面试中最终会谈到的内容。
  • 公开分享你的作品。它打开了人际关系的大门,让你获得真正的推荐,甚至迫使招聘人员主动联系你。你不就是这样找到我的吗?

请记住,没有人说这将是容易的。如果是的话,闯入数据科学没什么大不了的。获得数据科学领域的第一份工作是最难的。从那以后只会越来越好。把它作为你的动力怎么样?

要获得更多关于进入数据科学、真实体验和学习的有用见解,请考虑 加入我的电子邮件好友私人列表

如果你很看重这类文章,想支持我这个作家,可以考虑 报名成为中会员 。每月 5 美元,你可以无限制地阅读媒体上的故事。

https://arunnthevapalan.medium.com/membership

N-BEATS:基于神经基扩展的时间序列预测

原文:https://towardsdatascience.com/n-beats-time-series-forecasting-with-neural-basis-expansion-af09ea39f538

零触发时间序列预测的深度学习模型

用 DALLE 1创建

有一件事让时间序列预测变得特别。

这是数据科学中唯一深度学习和变形金刚没有明显优于其他模型的领域。

让我们使用久负盛名的 Makridakis M 竞赛作为基准——一系列展示时间序列预测领域最新进展的大规模挑战。

在被称为 M4 的第四次迭代中,获胜的解决方案是ES-RNN【2】,这是优步开发的混合 LSTM &指数平滑模型。有趣的是,6 个(57 个中的)纯 ML 模型表现很差,它们几乎没有超过竞争基线。

一年后,情况发生了变化。 Elemental AI (与 Yoshua Bengio 共同创立)发表了N-BEATS【3】 一个纯深度学习模型,比 M4 获奖的 ES-RNN 模型高出 3%。但是还有更多。

在本文中,我们深入描述:

  1. N-BEATS的架构,模型如何工作,为什么如此强大。
  2. 如何产生可解释的预测。
  3. N 拍 N 拍如何实现无与伦比的零拍迁移学习。
  4. 为什么 ARIMA 不能原生支持迁移学习。

让我们开始吧。

如果你对时间序列预测感兴趣,可以查看我收集的最佳深度学习模型和教程。

什么是 N-BEATS

N-BEATS 是一种快速、可解释的 DL 模型,它使用完全连接层的双残差堆栈来重新创建统计模型的机制。

N-BEATS 代表NeuralBasisEx expansionA分析为TimeS 这家公司是由 Yoshua Bengio 联合创立的,后来被 T42 的 ServiceNow 收购。

N-BEATS 是一个有趣的预测模型,因为:

  • 这是第一个超越所有成熟的统计方法的纯深度学习模型。
  • 它提供了可解释的预测。
  • 它为时序上的迁移学习奠定了基础。

大约在那个时候,亚马逊发布了其新颖的时间序列模型,被称为DeepAR【4】虽然 DeepAR 包含深度学习组件,但该模型也采用了一些统计概念(最大似然估计)。

n 节拍—概述

让我们简单讨论一下 N-BEATS 的几个关键特质:

  • 多时间序列支持 : N 拍可以在多个时间序列上训练,每个时间序列代表一个不同的分布。
  • 快速训练:模型不包含任何递归或自我注意层——因此,更快的训练&稳定的梯度流。
  • 多时段预测:模型产生多步预测。
  • 可解释性:作者开发了 2 个模型版本, 通用 版本,以及 可解释性 版本。可解释版本可以输出关于趋势和季节性的可解释预测。
  • 零触发迁移学习:该模型可以将其知识转移到其他时间序列数据集,取得惊人的成功。

注:element ai 最初的 N-BEATS 实现仅适用于单变量时间序列。Darts 库发布了支持多元时间序列和概率输出的更新版本。在本文中,我们将重点放在原始版本上。

N-BEATS —通用架构

N-BEATS 架构很深,但非常简单。图 1 显示顶层视图:

图 1:N-BEATS 的顶层架构(来源)

注意 3 件事:

  1. (蓝色)——基本加工单元。
  2. (橙色)——积木的集合。
  3. 最终型号 (黄色)——一叠叠的集合。

模型中的每个神经网络层只是一个密集(全连接)层。

让我们从第一个组件开始,即基本块:

1.基本块

假设H是预测范围。在 N 拍中,回看窗口是视界H的倍数。

图 2 显示了基本块的架构:

图 2: 基本块架构(来源)

让我们来看看引擎盖下。

图 3 中,我们使用来自电力数据集【5】的纸张基准参数:

图 3: 基本块内的所有操作(图片由作者提供)

让我们看看这里会发生什么:

  • 模型回顾3 days = 72 hours = 3 horizons来预测未来 24 小时的用电量。
  • 该块接收回看窗口输入。
  • 然后输入通过一个 4 层神经网络。
  • 该计算的结果被导向 2 个输出。这里密集层密集层 5 估计θ参数(θ^bθ^f),称为展开系数
  • 然后,使用层变换g^bg^f将这些参数线性投影到新的空间,以产生预测 信号。这个过程叫做“神经基础扩展”。

那么反向预测和预测向量是如何有用的呢?

给定g^bg^f变换,反向预测信号是能够最佳预测预测信号的最佳近似向量。当g^bg^f采取特定的形式时,反向预测和预测向量就变成了可解释的(后面会详细说明)。

2.堆栈

为了提高神经扩展过程的效率,作者将许多块堆叠在一起。该结构显示在图 4:

图 4: 积木堆(左)和积木堆(右)——(来源)

只有第一个块接收原始序列输入。下游块接收来自前一块的回播信号**x_l+1** (其中l是块索引,即l_1是堆栈中的第一块)。

图 5: 块内部的操作(图片由作者提供)

在每个堆栈内,反向预测和预测信号被组织成两个分支:这种拓扑被称为双重残差堆栈,并且可以由以下等式描述为:

在每个块中,模型从输入**x**_l中移除已经很好地近似的那部分反向预测信号**x̂**_l。换句话说:

每个模块的模型学习最佳地逼近输入信号的一部分,并发送其余部分以由下游模块逼近。

由于每个模块仅模拟输入信号的一部分,最终预测是来自所有模块的所有预测* ŷ信号的总和。*

最后,堆叠也堆叠起来(图 4,右)。这种架构选择进一步增加了模型的深度,并增强了其学习复杂时间序列的能力。

我们已经看到了通用版本的 N-BEATS 是如何工作的。接下来,我们将描述可解释的版本。

N-BEATS 和 ARIMA 有关系吗?

如果你熟悉 ARIMA ,你可能会注意到与 N 节拍方法的一些相似之处。**

ARIMA 使用 Box-Jenkins 方法建模,这是一个迭代过程。具体来说:

  1. 首先,我们猜测 AR()MA() 函数(pq参数)的顺序。
  2. 之后,我们使用例如最大似然估计来估计这些参数的系数。
  3. 然后,我们验证模型的条件是否成立。例如,模型的残差应该是正态和独立的。如果没有,我们返回到步骤 1 并重复该过程。这一次,我们在之前的基础上增加了新的pq度。

换句话说,在 Box-Jenkins 的每一步中,我们都向模型中添加了更多的信息。每次迭代都会根据模型残差创建更好的输入表示。

因此,我们可以得出结论:

在 N-BEATS 中,每个连续的块仅模拟由前一个块重建的反向预测的残余误差,然后根据该误差更新预测。在拟合 ARIMA 模型时,这一过程模仿了 Box-Jenkins 方法。

这两种方法的主要区别在于残差的目标函数。 ARIMA 关注残差的质量,而 N-BEATS 使用任意损失函数。

另外,我们不用手动调整任何带有 N 拍的方程——基变换会通过反向传播自动优化。使用 ARIMA 然而,我们大量使用自相关和偏自相关图来猜测AR()MA()函数的顺序。

注意:根据编程语言和库的不同,一些 ARIMA 库实现了略有不同的 Box-Jenkins 方法。在这里,我们记录了教科书的实现。

n-BEATS——可解释的架构

通过一些改变, N-BEATS 模型变得可以解释了:这些改变是:

  • 我们只使用 2 个堆栈,趋势堆栈和季节性堆栈。通用架构至少使用 30 个。****
  • 趋势和季节性堆栈都包含 3 个块。在通用架构中,每个堆栈有一个块。
  • g^bg^f基础层权重在堆栈级别共享。**

基本思想是g^bg^f基采取特定的形式。让我们更详细地描述它们。

趋势块

我们的目标是将g^bg^f函数重构为单调的,在预测窗口中缓慢变化。

给定一个时间向量t=[0,1,2,…,*H*−2,*H*−1] ( H是地平线)、来自前一层的θsθ和多项式次数p,趋势模型被定义为:

换句话说,我们使用图 3* (通用块)的架构,用上面的操作交换最后一个线性层。结果如图 6 所示:***

图 6: 趋势块(图片由作者提供)

本文中没有描述逆向预测方程,但是它们可以很容易地从项目的实现中推导出来。此外,趋势和季节性块(图 6* 和图 7 )采用来自官方项目报告的参数:***

***interpretable.seasonality_layer_size = 2048
interpretable.seasonality_blocks = 3
interpretable.seasonality_layers = 4
interpretable.trend_layer_size = 256
interpretable.degree_of_polynomial = 3
interpretable.trend_blocks = 3
interpretable.trend_layers = 4
interpretable.num_of_harmonics = 1***

季节性障碍

类似地,我们用适当的g^bg^f函数替换最后一层,以捕捉季节性。傅立叶级数是一个很好的选择:

然后,架构变成:

图 7: 季节性区块(图片由作者提供)

我们再次强调,在所有可解释的堆栈中,堆栈中的g^bg^f权重是共享的。

实验结果

最后,作者在 3 个流行的时间序列数据集上测试了 N-BEATS 的性能——M3[6]、M4[7]和旅游业[8]。

实验装置

作者将所有模型分为特定的类别,并将 N-BEATS 与每个类别的最佳模型进行比较。例如, DL/TS hybrid 是 M4 获奖的 ES-RNN 车型。

由于所有这些数据集都用于数据科学竞赛,因此所有参与者都依赖集成来实现最佳性能。因此, N-BEATS 的作者依靠集合来进行比较。他们使用了三种变体: N-BEATS-G (通用)、N-BEATS-I* (可解释)和N-BEATS-I+G(N-BEATS-G 和 N-BEATS-I 所有模型的集合)。***

最重要的是,他们创造了 6 个不同的模型,每个地平线和变化都有回顾窗口2H, 3H .. 7H。关于组件配置的更多细节,请查看原始文件。总之,作者集合了 180 个模型来报告测试集的最终结果。

结果

所有数据集的结果如图 8:* 所示***

图 8: 在 M3、M4 和旅游数据集上的实验结果(来源

结果相当可观。

在所有数据集上, N-BEATS 优于其他实现,其中 N-BEATS-I+G 最为成功。

请注意,在每个数据集中,竞赛使用 MAPE、斯马普和 OWA 指标(越低越好)。这些指标在时间序列竞赛中很受欢迎。

注意:与其他方法相反,N-BEATS 不需要任何手工制作的特征工程或输入缩放。因此,N-BEATS 在不同的时间序列任务中更容易使用。

零射击迁移学习

N-BEATS 的主要贡献是能够在时间序列上成功实现迁移学习。

预赛

迁移学习是一个更通用的术语——它指的是一个模型如何在不同的数据集之间转移其知识。这已经建立在计算机视觉或 NLP 任务上:我们可以下载一个预训练的模型,并通过微调将其调整到我们的数据集。

元学习(或少量学习)是指模型只需少量训练/微调就能适应我们的数据集。最好的场景是 零触发学习 ,其中模型不在目标数据集上训练。

零命中率学习是模型使用看不见的数据进行预测的能力,而无需对它们进行专门训练。这种学习方法更好地反映了人类的感知。

此外,这种向元学习的新范式转变已经被最新的人工智能研究所接受,如 open AICLIP【9】*Whisper【10】就是其中的几个。*******

零拍 N 拍

yo shua beng io**(N-BEATS的合著者)在之前的工作中已经建立了预测任务迁移学习的理论基础【11】。

N-BEATS 的作者发表了一篇后续论文[12],其中总结了大部分工作,包括时间序列预测模型应该满足哪些要求才能执行有效的迁移学习。

让我们来关注一下的 N-BEATS。

作者指出 N-BEATS 的元学习能力取决于两个过程:I)内部学习过程和外部学习过程。它们如图 9 所示:********

图 9:N-BEATS 中的元学习过程(来源,作者编辑)

内部循环发生在每个模块内部,重点是学习特定任务的特征。

外部循环发生在堆栈级别。在这里,模型学习所有任务的全局特征。

换句话说,内环学习局部时间特性,而外环学习所有时间序列的更长相关性。

然而,这回避了下面的部分:

为什么 ARIMA 不适合迁移学习?

如果一个既定的范例规定了一个预测模型应该满足哪些标准才适合迁移学习,那么为什么 ARIMA 不是呢?

为了回答这个问题,我们将再次关注[12]描述的两个学习过程

创建 ARIMA 模型时,有两个挑战:

  1. 参数估计:使用最大似然法等统计技术估计参数。这是内循环。****
  2. 模型公式:这定义了自回归方程的形式。例如,如果我们的模型有一点趋势,没有季节性,正常残差,我们可以决定高斯 ETS 可能会做这项工作。这是外循环。****

请注意,统计模型只通过了第一个标准,即内部循环。

一旦我们选择了模型,参数估计部分就很简单了。但是,模型制定部分需要人工干预。因此,关于统计方法,外环的作用没有实现。****

因此,我们得出结论:

N-BEATS 用可学习的参数估计策略取代了经典统计模型的模型参数估计的预定义规则集。这种策略允许 N-BEATS 在多个看不见的时间序列上很好地概括。

N 拍的零拍学习结果

在本次实验分析中,作者用一些新模型丰富了之前基准(图 8* )的结果:***

  • N 拍-M4: 作者在 M4 上建立了一个预训练的 N 拍模型。
  • 迪帕尔-M4:M4 上的一款经过预训练的迪帕尔型号也加入了型号库。**

总结果如图 10* 所示:***

图 10: 零拍车型对比(来源)

同样,结果非常有趣。

  • zero-shotN-BEATS-M4在 M4 和旅游数据集上的表现优于所有其他模型(包括获胜者),尽管它没有在这些数据集上进行训练。
  • 零距离射击迪帕尔-M4* 似乎表现不佳。这是意料之中的,因为 DeepAR 不适合迁移学习。***
  • 在每个数据集中,与定制训练的 N 拍相比,零拍 N 拍模型表现非常好。如果能看到在 Whisper 等其他零炮模型中发现的有效稳健性与总体稳健性的关系图,那将会很有意思。

结束语

N-BEATS 是一个突破性的深度学习预测模型,对时间序列领域产生了持久的影响。

在本文中,我们描述了 N-BEATS 的两个主要优势:首先,它是一个产生 SOTA 结果的强大模型。其次, N-BEATS 为实施零投迁移学习建立了一个定义明确的框架。据我所知,这是第一个成功实现这一点的模型。

在我们的下一篇文章中,我们将介绍一个使用 N-BEATS、的编程教程,并描述一篇更新的论文,名为 N-HiTS

感谢您的阅读!

我每个月写一篇有影响力的 AI 论文的深度分析。
保持连接!

参考

1根据 DALLE 创作,文字提示“通过空间传输的霓虹正弦信号,概念艺术”,给 rg

[2] Slawek Smyl 等著M4 预测竞赛:引入全新混合 ES-RNN 模型

[3] Boris O .等著 N-BEATS:可解释性时间序列预测的神经基础展开分析 ICLR (2020)

[4] D. Salinas 等人 DeepAR:用自回归递归网络进行概率预测 《国际预测杂志》(2019)。

[5]electricityloaddiagrams 2011 2014数据集由 UCI,CC BY 4.0。

[6]https://forecasters.org/resources/time-series-data/ M3 数据集,公共领域

[7]https://forecasters.org/resources/time-series-data/ M4 数据集,公共领域

[8]旅游数据集https://www.kaggle.com/c/tourism1,公共领域

[9]亚历克·拉德福德等 从自然语言监督中学习可转移的视觉模型(2021 年 2 月)**

[10]亚历克·拉德福德等人通过大规模弱监督的鲁棒语音识别(2022 年 9 月)**

[11] Yoshua Bengio 等人学习一个突触学习规则

[12] Boris O .等人 元学习框架及其在零炮时间序列预测中的应用

N-BEATS Unleashed:使用 Python 中的神经基础扩展分析进行深度预测

原文:https://towardsdatascience.com/n-beats-unleashed-deep-forecasting-using-neural-basis-expansion-analysis-in-python-343dd6307010

深度预测

N-BEATS Unleashed:使用 Python 中的神经基础扩展分析进行深度预测

端到端示例:具有复杂季节性的多变量时间序列的神经预测

分形数学几何——数码艺术家在像素上的自由图像

上周,我开始撰写一系列关于深度预测的文章。我们讨论的第一种方法是变压器。

今天,让我们为我们的武器库中添加另一个神经网络预测器: N-BEATS,时间序列的神经基础扩展分析

N-BEATS 是Oreshkin 等人在 2019 年的一篇文章中首次描述的一种神经网络类型作者报告称,N-BEATS 的表现超过了 M4 预测竞赛获胜者 3%。M4 获奖者是递归神经网络和 Holt-Winters 指数平滑的混合体——而 N-BEATS 实现了一种“纯粹的”深度神经架构。

我们将使用 N-BEATS 的 PyTorch 实现,通过 Darts 多预测库,我在上周的 Transformer 示例中使用了相同的包。Darts 将 PyTorch 的预测相关类与其他几个包的预测相关类结合在一起。通过在一个全面的时间序列库中包装多种方法,Darts 便于在预测方法、预处理和评估任务之间切换。

为了与变压器进行比较,我们将再次预测西班牙的每小时电价。时间序列展示了三种季节性模式:小时、工作日和月份。30 多个外生变量影响着价格水平。这两个方面将价格转化为一个时间序列问题,这个问题非常复杂,以至于深度预测要优于经典方法。

1.N 拍的概念

关于 N-BEATS 的开创性论文将其重点“放在使用深度学习解决单变量时间序列点预测问题上。“我们将超越最初的范围:整合协变量,生成概率预测而不是点估计。Darts 通过将源数据展平为一维序列,使原始 N-BEATS 架构适应多元时间序列。

【1905.10437.pdf(arxiv.org)【N-BEATS】,奥列什金等人(2020)

该模型由一系列堆栈组成,每个堆栈组合多个块。这些模块通过预测和反向预测链接连接前馈网络。块“移除信号的一部分…它可以很好地近似。然后,该模块将注意力集中在剩余误差上,这是前面的模块无法解决的。每个块生成一个局部预测,其重点是时间序列的局部特征。堆栈汇总其包含的块的部分预测,然后将结果移交给下一个堆栈。堆栈的目的是通过“回顾”来识别整个时间轴上的非局部模式最后,将部分预测拼凑成模型级别的全局预测。

N-BEATS 的超参数为:

  • 输入层输出层(常量 INLEN 和 N_FC)的大小应足以为源数据中的每个特征分配一个节点。输入语块的长度不能小于季节性的顺序,否则学习过程会发现更难组合这些语块。为了有效使用内存,请将它们设置为 2 的幂。
  • 堆栈中的块数(块数)。
  • 栈的每个块中每个全连接宽度:其节点计数(LWIDTH)。
  • 批量大小定义了模型在更新其矩阵权重之前将处理的观察数量。为了有效地将它与系统的内存结构对齐,将其设置为 2 的幂。非常大的批量可能会误导单方向的梯度下降——模型可能会陷入次优最小值。较小的批量将导致梯度下降在不同方向反弹,并可能导致较低的精度,但它们也有助于防止模型过度拟合。最常见的建议是选择初始批量为 32。因为我们的数据集有每天 24 小时的频率,所以我将批处理大小设置为可以处理 24 个时间步长的下一个二进制上限:32。
  • 时期告诉模型它应该运行多少个训练周期。在每个时期,模型将处理整个训练集,进行一次向前和一次向后传递。

考虑到一些过度简化,这些超参数的乘积定义了模型的张量大小。较大的参数值会使它与系统的内存限制相冲突,并导致处理时间成倍增长。而小参数值可能不足以反映源数据中的复杂模式。

正如我们在 Transformer 示例中所做的那样,我们将使用分位数回归得出一个概率预测。这是我们可以在所有深度预测模型中使用的一个选项。神经网络的损失函数可以公式化为分位数损失函数。然后,分位数回归不仅会计算每个时间步的中心预测值(也称为点估计值),还会绘制不确定性带。像 1%/99%或 10%/90%这样的分位数对表示预测值可以变化的范围,高于或低于中心值。

关于概率性预测而非确定性预测的更多背景信息,请参考本文:

2.属国

除了用于数据争论和探索的经典包,如 pandas、numpy 和 matplotlib,我们还引入了 seaborn 的绘图功能。

小的 missingno 包是可选的:missingno 有助于可视化空值是如何分布在行和列上的。但是,您可以选择不安装它,并删除它的代码行,而不会影响预测脚本的其余部分。

最后,我们需要 conda-install 或 pip-install Darts 包及其 PyTorch 选项,它涵盖了我们可以通过 Darts 实例化的所有神经网络变体:Darts/readme . MD at master unit 8co/Darts GitHub

最好为飞镖创造一个新的虚拟环境。这是我们在安装大的软件包之前应该一直遵循的实践,特别是那些具有非 Python 依赖性的软件包。PyTorch 和 Prophet 依靠 C++。

Darts 至少需要 Python 3.7,目前还不支持 3.10。我在 3.9 中也看到过由 Prophet 选项导致的安装 hickups,所以我选择了 Python 3.7 conda 环境(2021 年 12 月)。

3.设置

Jupyter 笔记本的设置单元列出了将控制 N-BEATS 模型的超参数。它为脚本的其他方面添加了许多额外的控制设置。

在最顶端,我们可以看到负载参数。如果用户设置为 True,脚本将不会(重新)训练模型,但是会将以前保存的 tar 文件从当前工作目录加载到内存中。该脚本将根据这个加载的模型创建预测值,并跳过耗时的培训课程。LOAD 选项对于将预训练的模型转移到不同的机器上也很有用,例如生产服务器。

RAND 参数接受随机种子,这将使结果可重复。

N_SAMPLES 建议模型在一个时间步长内绘制 N 个随机预测值,而不是只计算一个点估计值。对于概率预测,N_SAMPLES 必须设置为大于 1。样本数量不会显著增加处理时间。分位数损失函数以低计算成本生成样本。

N-JOBS 限制了脚本可以并行请求的处理器内核数量。如果设置为-1,所有核心都将可用于模型。

列表分位数通知模型应该计算预测分位数的百分比。

分割设置应该为培训保留的实际观察的百分比份额。

FIGSIZE 建议使用默认的绘图尺寸。

4.时间序列

该数据集可从 Kaggle 下载,获得 CC0 公共域名许可:每小时能源需求生成和天气| Kaggle

每小时能源需求生成和天气| Kaggle ,作者截图

源文件由两个 csv 文件组成:

  • 能源文件记录了西班牙每小时的电价,单位为欧元/百万瓦时(€/兆瓦时);按来源分类的发电量(煤、天然气、风力等。)在 MWh 和以 MWh 为单位的能量需求(“负载”)。
  • 天气文件提供了西班牙五个主要城市的每小时记录。

我们将我们的分析限制在观测数据的最后一年,即 2018 年,并预测接近年底时约 870 个时间步长的每小时电价。

能源数据集中的大部分栏目都按能源类型(如风力、煤炭或太阳能发电厂)报告发电量,单位为兆瓦时。

作者图片

N-BEATS 笔记本包含了数据处理和预处理的完整代码。你可以在上周的教程中找到这些步骤的详细解释和它们的笔记本单元:“变形金刚释放

价格图显示了一条具有垂直波峰和波谷的曲线。它们的幅度各不相同,这意味着我们面临不止一种季节性。价格水平没有呈现强劲的上升或下降趋势。它趋于回到€ 60/MWh 左右的水平。

作者图片

天气数据集包括五个西班牙大城市及其每小时的天气记录。

我们将把能源数据和天气数据结合在一个数据帧中。

作者图片

相关矩阵将使我们能够辨别众多因素对电价的影响。

作者图片

该热图强调了许多特征彼此高度相关,尤其是城市温度。我们将把那些至少对价格水平有适度影响的因素分离出来。这种功能集的减少是我在第一篇文章中描述的一个练习: Transformer Unleashed

接下来,我们应该调查时间序列的季节性。

当我们按月份和工作日对价格进行分组时,得到的数据透视表与热图相结合,可视化了两种季节性模式:月与月之间;还有工作日和周末的区别。

作者图片

第二个数据透视表及其热图揭示了某一天电价的每小时季节性模式。白天和夜间的时间变化,以及工作日和周末的时间变化,显示了和每月季节性一样的对比。

作者图片

我们得出结论,N-BEATS 模型必须学习三种季节模式:

  • 每小时的价格波动
  • 工作日与周末
  • 寒冷和温暖的月份

5.飞镖中的时间序列对象

Darts 对时间序列对象进行操作,我们需要将包含源数据的 pandas 序列(或 numpy 数组)转换成时间序列对象。

首先,我们从数据框架的价格列创建一个单变量目标时间序列 ts_P。

作者图片

然后,我们将数据帧的特征列分组到协变量的多元时间序列中:ts_covF。

接下来,我们在训练集和测试集之间拆分价格时间序列。

如果数据集包含不同量值的值,神经网络的梯度下降可能被误导。我们实例化 Darts 的 scaler 类,并在将 Scaler 应用于训练集和测试集之前,使其适合 price 时间序列的训练集。

然后,我们采取类似的步骤来准备特征协变量。

作者图片

Darts 的函数datetime _ attribute _ time series()使我们能够将小时、工作日和月分离为协变量。

请注意,第 2 行将我们在实际观测中发现的时间步长增加了 48 小时。这样,我们已经为实际观测结束后的样本外预测准备了时间序列。

第 3 行到第 5 行将日期时间协变量“堆叠”在一起,放在一个多变量时间序列对象中。

第 7 行包含一个标记西班牙公共假日的函数,因此增加了一个有用的第四个协变量。

第 12 行在训练集和测试集之间划分日期时间协变量,然后第 16 到 20 行调整它们的值。

concatenate() 函数合并多变量时间序列对象中的特征协变量和日期时间协变量。 slice_intersect() 函数确保组件在时间轴的同一段上延伸,否则如果我们试图组合长度不等的时间序列,就会看到一条错误消息。

6.训练 N-BEATS 模型

我们使用笔记本顶部的设置单元中定义的超参数来配置 N 拍模型。

参数似然=分位数回归(分位数)建议模型在每个时间步从分位数损失函数中抽取样本。这将产生分位数的概率预测,而不仅仅是点估计。

下面的训练单元评估我们在笔记本顶部设置的负载常数。如果 LOAD 设置为 True,脚本将不会(重新)训练 N-BEATS 模型,而是会加载一个以前保存在 tar 文件中的模型,该模型现在应该位于当前工作目录中。这个保存并重新加载的模型保留了它的训练状态——我们可以从中提取预测值,而无需进行另一个耗时的训练练习。

如果 LOAD 设置为 False,N-BEATS 会忠实地开始训练。完成后,它的状态会自动保存在一个 tar 文件中,以使其可重用——或者使其可传输给同事、客户或生产服务器。

作者图片

7.测试

predict() 函数将测试集的长度作为其预测范围。我们提供协变量的时间序列(基于时间和特征)作为第二个参数。

第 13 到 18 行初始化了我们将收集预测结果的变量。

第 37 行的 list comprehension 为列表分位数中的每个百分比调用辅助函数 predQ()

该函数通过分位数回归在第 23 行获得分位数。每个预测分位数代表其自身的单变量时间序列。第 24 行逆变换这些分位数值,使它们与原始电价兼容。然后,第 25 到 27 行在结果数据帧 dfY 的新列中收集这一系列分位数值。

我们将 50%分位数——中位数或中心预测序列——映射到专用变量 ts_q50。然后,我们将这个预测中值与实际序列进行比较,以获得准确性指标 MAPE 和 RMSE。

作者图片

以 6.57 的 MAPE 衡量,预测质量并不算糟糕。但是这比我们从变形金刚实验中得到的要差。转换器生成了一个 MAPE 为 4.57 的预测。

N-BEATS 模型似乎在 12 月下旬受到向下偏差的困扰,此时蓝色预测曲线的倾角始终比实际观测值更深。N-BEATS 是否对“假日”协变量赋予了过多的权重,而 12 月假日的价格与其他假日的价格有所不同?

作者图片

训练集不包括 12 月的前一个月,这可能为 N-BEATS 识别特定于 12 月的模式提供了更全面的基础。

我们也可以开始一个调整过程。Darts 为 N-BEATS 和其他深度预测器提供了网格搜索—穷举或随机采样—请参见本文中的 Python 示例:

但是 N-BEATS 的处理时间有点令人沮丧。它在时间序列上咬了 1 个多小时。超参数搜索会增加计算量。因此,我经受住了诱惑,试图通过隔夜网格搜索将 MAPE 降低 1%或 2%。

8.样本外预测

8.1 一步到位的模型和多个样本步骤

假设我们想要预测测试集之后 12 个小时的每小时价格。要生成样本外预测,我们需要提供覆盖扩大预测范围的协变量。

日期时间协变量—小时、工作日、月和假日—已经扩展到 1 月 1 日。请记住,当我们创建它们时,在模型训练之前,我们在实际观察结束后增加了 48 小时。

但是我们还需要为特征协变量得出未来值。天气预报可以很容易地为我们提供未来 12 小时的温度和风速。对于电力需求和发电量,我们必须推导出单变量预测——要么使用 SARIMA 等经典模型;或者我们也可以对这些时间序列应用 N 拍(没有协变量)。

在本练习中,我们假设已经获得了特征协变量的预测值。我们简单地复制 12 月 31 日之前 24 小时内记录的值,并假设 1 月 1 日的天气、需求和发电将遵循与 12 月 31 日相同的每小时模式。

然后我们将这些扩展的协变量输入到第 18 行的 predict()函数中。

在我们提供了协变量之后,样本外预测的脚本几乎与测试阶段的脚本相同。我们得到一个结果数据帧 dfY,它包含了测试集的预测,但是现在扩展了 12 行到 1 月 1 日 10:00。

作者图片

作者图片

8.2 样本外预测限于输出长度

模型设置采用一个可选参数 output_chunk_length ,默认设置为 1,我们在参数 N_FC 中保持不变。因此,我们训练了一个提前一步的预测模型。

如果我们将 output_chunk_length 提高到 12,样本外预测可能会覆盖 12 个小时,而不需要我们提供未来的协变量作为输入。

让我们通过预测测试集结束后一个小时的电价来尝试一下,而不是预测样本外的 12 个小时。因为我们已经在 output_chunk_length 为 1 的基础上训练了模型,所以我们可以预测下一个小时的,而无需进行另一次训练练习。

我们现在将协变量限制在可用的实际源数据:cov_t。

作者图片

正如预期的那样,我们收到了实际观测结束后第一个小时的预报。

自然,我们不能将 output_chunk_length 提高到任意的水平。更高的级别将乘以(取幂)张量的大小、内存使用量和处理时间。输出大小也不能超过 input_chunk_length。因此,不提供未来协变量的样本外预测只适用于相对较短的时间窗,受输入图层大小的限制。

9.结论

深度预测器可以处理受外生变量影响的时间序列,这些时间序列表现出多阶季节性,这种组合超出了大多数经典预测方法的能力。

N-BEATS 实现了 6.5%的 MAPE(在任何调整工作之前)。它花费的训练时间是上周 Transformer 示例的两倍多。它选定了一个比变压器高 2%点的 MAPE。如果我没有看到更高的变压器精度,我仍然认为 6.5%的 MAPE 是一个可接受的初始结果,我不会放弃。我认为这是一个中间结果,我们可能可以提高一两个等级。测试集的图显示了 12 月下旬的一些偏差,这是调整模型的起点。

N-BEATS Jupyter 笔记本可以在 GitHub 上下载,与 Transformer 笔记本在同一个存储库中:h3ik 0th/ES _ energy _ Transformer:Transformer deep forecast(github.com)

https://medium.com/@h3ik0.th/membership https://medium.com/@h3ik0.th/temporal-fusion-transformer-unleashed-deep-forecasting-of-multivariate-time-series-in-python-674fa393821b

n-学生学习

原文:https://towardsdatascience.com/n-student-learning-fc4e452ad006

有助于对抗过度拟合和模型不确定性的架构。

过拟合决策边界。

过拟合是机器学习领域的一个基本问题,在用噪声数据进行训练的情况下尤为重要。当我们扩展数据集时,由于仔细的人类标记的不可行性,噪声量自然增加。

在下面的文章中,我们将介绍 N-Student Learning 背后的主要思想,这是一种多网络架构,可以应用于任何网络,以减少过度拟合的影响。该设置还允许对网络如何学习对数据中的任何噪声或不确定性建模进行精确控制。

除特别注明外,所有图片均为作者所有。

问题

过度拟合

监督分类的目标是对输入空间和标签空间之间的关系进行建模,通常给定输入-标签对的训练数据集(例如,学习将动物图像分类到正确的类别)。

具体来说,标签是 one-hot ,这意味着每个输入的标签都是一个 100%正确的类别,而不是多个类别的概率分布。

n 学生学习解决了过拟合的问题,特别是在监督分类领域。过度拟合可以粗略地描述为一种现象,在这种现象中,机器学习模型使用训练数据的异常来学习解决给定的任务,这些训练数据不能推广到看不见的数据。在最坏的情况下,过度拟合意味着以泛化为代价记住训练集的标签,这是对深度神经网络训练的严重关注(其中参数的绝对数量允许它们记住甚至随机的标签)。

非常适合与过度适合的决策界限

在上图中,我们看到了一个过度拟合的例子,使用了从两个类中抽取的 20 个点的小数据集。该数据集中的类被很好地分开,但是包含一些异常值。

虚线是决策边界,将模型预测为蓝色的区域与模型预测为红色的区域分开。正如我们所看到的,过拟合决策边界“记忆”了一些异常值,创建了输入空间的一些部分,这些部分将很难推广到新的示例。

过度拟合对网络的损害程度取决于数据的质量。如果我们有一个完美的训练集,那么精确地拟合数据就不是问题了。令人担忧的是,我们训练集中的标签是不完美的——在某种程度上有噪音。

两种形式的标签噪声

我想首先区分两种可能会影响具有独热标签的数据集的标签噪声的基本来源:错误随机性

最简单的噪声类型是当标签中有错误时。一个例子是对猫和狗的高分辨率图像进行分类的任务。想象一下,一个人类标签员被用来生成训练集标签,他们有时会意外地选择不正确的标签,从而给数据集带来错误。我指定高分辨率的原因是为了清楚地表明,给定任何图像,它是狗还是猫都是完全明确的,因此标记过程中的任何噪声都可以直接归因于标记过程中的误差。在训练集中记忆这些种类的错误将对模型推广到看不见的数据有明显的负面影响。

由随机性引起的标签噪声是一种更加微妙的标签噪声形式,它发生在问题域本身具有随机性的时候。一个很好的例子是利用图像诊断黑色素瘤的问题。给定一个皮肤镜图像,并不总是能够肯定地说这是否是一个黑色素瘤病例。很多时候,对图像的最佳预测可能是患者有 80%的可能性患有黑色素瘤,因为 20%的时间,同一图像可能实际上显示良性黑色素瘤模仿者。然而,我们没有这些柏拉图式的概率——我们的一次性数据集将皮肤镜图像与特定患者是否被诊断患有黑色素瘤的二元信号配对。最佳的 80%可能性和我们收到的 100%有把握的标签之间的差距产生了这个“随机噪声差距”。当像这样的任务被定义时,我们隐含地希望网络输出对患者被诊断患有黑色素瘤的可能性的准确估计,而不仅仅是最可能的答案。这种类型的随机噪声是网络可能过度适应的另一种形式的噪声。

基于误差的噪声和随机噪声之间的共性是,给定来自输入域的特定示例,生成的标签可能不总是相同的。

在错误的情况下,人类标签员可能在一种情况下正确地标记了猫,但在另一种情况下却意外地选择了狗。

在随机情况下,完全相同类型的皮肤镜图像可能会导致一个患者出现黑色素瘤,但不会导致另一个患者出现。

在没有任何领域知识的情况下,特别是在没有数据集具有何种噪声的先验知识的情况下,我声称这两种形式的噪声是无法区分的。

gaussians 的乐趣——可视化错误和随机性

让我们通过下面的蓝色和黄色点的玩具数据集来看看这意味着什么。

蓝黄玩具数据集

我们注意到,如果一个点在最左侧,它几乎肯定是蓝色的,如果这个点在最右侧,它几乎肯定是黄色的。然而,当我们接近中间时,正确的类变得更加模糊。

对数据的一个似乎合理的解释是,平面上实际上有两个非常不相交的类,但数据集的标签中存在错误(特别是在两个类变得更加相似的中间区域)。在这种情况下,两个类之间的“最佳”决策边界可能如下所示:

最佳决策边界,假设噪声是由于误差造成的。

另一个看似合理的解释是,这些数据基本上是随机的——给定飞机上的一个位置,它可能属于任何一类。在这种情况下,最佳决策边界将在中间具有一种类别梯度。正如您可能已经猜到的,这些点实际上是从两个高斯分布中提取的,因此我们可以通过简单地计算从给定位置的每个高斯分布中提取一个点的概率来生成一个最佳的“软”决策边界。

最佳决策边界,假设噪声是由于随机性造成的。

在这两个高斯模型的背景下,我之前的说法是,这两种解释都是合理的,如果不事先知道数据集中存在哪种噪声,我们就无法知道中间重叠的数据点是由标注过程中的错误还是随机性引起的。

注意:你可能仍然认为仅仅从数据集来看,由于重叠,很明显中间的点是随机的。如果我要生成这些相距更远的高斯分布,使得这些类除了少数异常值之外是完全不相交的,我怀疑随机性质会远不那么明显。

2-学生学习

建筑

简而言之,N 学生学习范式涉及多网络架构的训练,其中每个“学生网络”从数据集的不同子集学习。在整个训练过程中,每个学生继续在自己的子集上进行训练,但一些标签被来自其他网络的伪标签所取代。

伪标注是使用网络输出为数据生成新标注的过程。现在,让我们假设伪标签是通过取网络分配最高概率的类来生成的。这些被称为“硬伪标签”,我们将在后面展示,基于所选择的伪标签方法,我们可以控制学生是学习将数据中的噪声建模为错误还是随机。

让我们继续描述一个简单的双学生设置

  1. 将训练数据集分成两半
    —d₁和 D₂
  2. 并行训练两个网络
    –n₁和 N₂
  3. 通过在各自的数据集上训练每个网络来预热网络
    –n₁在 D₁训练
    –n₂在 D₂训练
  4. 在每个时期,在每个网络各自的数据集上训练每个网络,用另一个网络生成的伪标签替换一部分样本。
    —{N₂'s 标签上的 N₁列车开往 d₁}

关键的见解是,每个网络只在自己的数据集上训练,所以它不能过度适应它为其生成伪标签的数据集。网络 1 的数据集 2 的伪标签在 D₂.中应该没有任何依赖于实例的噪声 D₁.的 N₂'s 伪标签也是如此

这种设置的有效性依赖于机器学习算法在过度拟合之前进行概括的想法。通过对没有过度拟合的伪标签进行训练,网络可以收敛到对训练数据中的噪声更鲁棒的解决方案。

正如您可能想象的那样,有许多方法可以将这种 2 学生设置推广到更多学生和不同的数据集分割,但我们不会在这里深入讨论。

与高斯人相处更有趣——控制不确定性

最后,我们将通过返回到可靠的蓝色和黄色数据集来看到所有这些。为了激励我们对伪标签方法的选择,让我们考虑如何从一个已经泛化但还没有过度拟合的网络的输出中生成伪标签。(我们假设使用交叉熵损失,其鼓励网络的输出分布代表训练数据中的标签分布。)

如果我们对该问题的先验知识告诉我们,真正的标注是一次性标注(数据集中的噪声是由错误引起的),那么我们会希望网络生成一次性标注,忽略可能来自数据集中的错误的其他类的任何小概率。我们可以通过使用硬伪标签来做到这一点。

如果我们认为问题从根本上来说是随机的,那么我们会想要与网络输出的分布相匹配的伪标签。我们可以通过将网络输出(在类上的分布)作为伪标签,或者通过从网络输出中随机采样来选择一个独热标签来做到这一点。我们分别称这些为“软伪标签”和“随机伪标签”。(它们对于无限大的批量大小是等价的,但是随机伪标签在实践中表现得更好。)

现在让我们看看当我们使用硬的和随机的伪标记方法训练两个 2 学生设置时会发生什么。针对时期 10、200 和 400(训练迭代次数)示出了网络的软决策边界,并且正常训练的相同网络将被用作基线(没有 2 学生设置)。

我们看到的是,基线网络过度适应数据集中的噪声,从而创建了一个复杂的决策边界,该边界容纳了其参数所能处理的尽可能多的噪声。

相比之下,硬伪标记网络在中间绘制了一个清晰的决策边界。如果我们假设中间的模糊数据点是噪声的结果,这正是我们想要的。

随机伪标记网络输出分层的决策边界。如果我们的假设是数据不容易出错,并且模糊性来自随机性,那么这个决策边界很好地代表了蓝色和黄色类别的条件概率。

在许多方面,这个数字概括了我的论文工作。给定一个没有领域知识的数据集,就不可能确定标注中的噪声是由标注错误还是随机效应引起的。然而,我们通常对数据集和可能存在的噪声类型有很强的先验知识。通过选择适当的伪标记方法,我们可以精确地控制模型从数据中学习哪种分布。

n-学生学习是我在布兰代斯大学计算机科学本科论文的一部分。

这篇文章是一年多的学习和研究的成果。我的希望是,任何偶然发现这一点的人都能够带着新的见解离开,不管他们之前在机器学习方面有多少经验。

对于那些希望深入研究的人来说,这里有一个到论文本身的链接。

整篇论文深入讨论了这里提出的主题,并证明了 N 学生学习在各种基准噪声标签数据集上的表现优于许多最先进的方法。

如果您对这项工作有任何疑问,或者对如何以更清晰、更易懂的方式解释这些概念有任何建议,请联系我们。

钉牢数据展示:求职者指南

原文:https://towardsdatascience.com/nailing-the-data-presentation-a-guide-for-job-seekers-5e5722cc14d8

如何花时间准备一个引人注目的、与业务相关的、精心设计的项目,让你在报价列表中名列前茅。

在开始目前的职位之前,我在大型数据驱动型公司参加了 10-15 次面试。许多面试官并不期望有一个技术报告,但惊喜地发现我准备了一个。以下是我在面试中向潜在雇主展示个人数据项目时的反馈和观察。

告诉我一个你参与的数据(科学、工程、分析)项目

对“告诉我你做过的一个项目”的最佳回答是,“实际上,让我给你演示一下。”长期以来,在各行各业都被认为是一种软技能,能够自信、连贯、简洁地向个人利益相关者和小团体展示你的工作是一种扼杀雇主价值的 T2 技能。事实上,随着越来越多的公司放弃技术测试,陈述可以成为检验候选人知识的有效方式。然而,对许多申请者来说,准备一份高质量的技术报告是一项艰巨的任务。

超越竞争对手

任何程度的演示技巧都可以提高团队和组织工作流程的效率。不管你是否意识到,工作面试包括一定程度的表达技巧,即使不需要正式的表达。虽然传统观点认为最好的面试是对话式,但你的工作是推销你自己、你的知识和你的学习能力,尤其是当涉及到新的技术堆栈和方法时。然而,区分影响个人或团队成果的演示是很重要的。求职面试的目标是在竞争中脱颖而出。相反,当向你的团队或领导介绍时,最好是详细描述所有团队成员的贡献,以避免显得自私或被贴上“不是团队成员”的标签

Unsplash 上由 Austin Distel 拍摄的照片

显示(在 GitHub 文件夹中)不要说

作为数据专业人员,除了拥有编程技能市场价值相当高,我们在求职中的另一个优势是展示技术和领域知识的能力。与面试销售职位的人不同,技术工人可以创建工作样本,展示他们干净利落地编写代码、思考问题和获得新技能的能力。随着 Github 被贴上‘开发者的游乐场’的标签,很有可能你不仅可以使用这个平台来展示你的作品,你还可能吸引未来雇主的目光。

罗曼·辛克维奇Unsplash 上拍摄的照片

在面试之前,确保你清理了你的 GitHub。如果你像我一样,有几个文件夹存放不同的课程、个人项目和其他杂项代码,考虑将你的工作整合到一个存储库中,并将其命名为' professional portfolio '或类似的名称。确保每个项目都有一份自述。作为一个旁注,来自一位前教育家,请确保你的项目的任何描述都是经过校对的,并且简明地描述了你的工作。至少对我来说,自述文件中的语法正确性可以和写得好的代码一样漂亮。

润色您的 GitHub 页面和网页

您可以并且应该利用 GitHub 的 Pages 特性,使用 yaml 文件和一些基本的 HTML 函数轻松显示您的代码。如果你想增加一点个性化,那么绝对要多做一点,创建你自己的网站。除了在你的简历或申请中包含一个链接,这可以帮助面试官以一种用户友好的方式查看你的工作,而不是从零散的评论和不清楚的文档中解读你的代码。在另一个平台上上传和显示你的代码也可以在另一方面有所帮助:如果你的演示失败了,它可以提供一个备份计划,不管是虚拟的还是其他的。如果由于某种原因,你不能在虚拟面试中分享你的屏幕,你可以邀请面试官在他们的屏幕上调出你的工作。他们可能会对你的适应性和你创建自我营销平台的主动性印象深刻。

伊利亚·巴甫洛夫在 Unsplash 上拍摄的照片

选择主题:不要犯我的错误

在选择演讲主题时,我鼓励你考虑它与你面试的领域、公司或职位的相关性。例如,为一个数据科学家的职位提供一个简单的探索性数据分析是没有意义的;面试官可能会希望你展示你写的 ML 脚本。我建议你不要选择学校项目。大多数大学、技术课程和训练营倾向于依赖相同的数据集,面试官可能已经看过几百次了,比如 iris 数据集。

希拉·斯威兹Unsplash 上拍摄的照片

你也要避免潜在的争议或冒犯性话题,即使你认为你的工作不会引发任何负面情绪。我把我关于成丨人丨电丨影女演员死亡的作品展示给一个潜在的雇主,完全犯了这个错误。

https://zachl-quinn.medium.com/tragedy-porn-a-visual-data-story-240732f6fafd

虽然我最终收到了一份工作邀请(我礼貌地拒绝了),但招聘人员提到,这个主题让团队有点不高兴。我能说什么呢?一些你在得到工作前学到的教训。

读一本书…然后提及它

在从事数据工作之前,我在广播行业工作,所以我有相当好的表达能力。如果你没有接受过同样的培训,或者正在寻求提高你的技术演示技巧,特别是在分享一个数据驱动的项目时,我强烈建议你拿起一本关于数据故事的书。我最喜欢的两本书是科尔·努斯鲍默·克纳弗里克的《用数据讲故事》和内森·尤的《T2:设计、可视化和统计的流动数据可视化指南》。这两本书的卖点在于,它们迫使你思考项目的逻辑基础,而不必过多担心编码或技术方法。

马克西姆·霍普曼在 Unsplash 上的照片

具体来说,Knaflic 提出了一个框架来讲述推动技术演示的三分钟数据故事。在面试前阅读一本以数据为导向的书的一个隐藏优势是,你可以在会议中(有机地)提到它。例如,在面试一个数据工程职位之前,当面试官提到有许多被遗忘的视图需要优化时,我读了 SQL 的艺术并提到了我对查询优化的兴趣。

整合技术堆栈

演示时,尽最大努力将你的工作浓缩成几张幻灯片,或者更好的是,一个交互式仪表盘(如果申请的是数据分析师的工作)。如果你的演示包含了公司当前技术的方方面面,你的面试官会留下深刻的印象。在你准备演讲之前,再看一下职位描述。描述中有提到他们使用 Spark 还是 Hadoop 吗?考虑在您的管道中实现这些。就像你应该仔细阅读你申请的公司的背景一样,你应该尽你所能去了解他们的范围。当你得到这份工作时,这也不会结束。也许我是一个超额完成任务的人,但在收到录取通知书后,我给我现在的领导发了电子邮件,询问我应该在第一天之前复习哪些技术。付出努力,你很可能会得到回报(或者至少给自己一个更好的机会)。

演示:讲一个故事

不要让你的面试官被 PowerPoint 搞得死亡,要确保你的陈述与你面试的行业或公司相关。还记得前面提到的三分钟数据故事这样的演示框架吗?这意味着做你的研究,以明确业务问题,以及“为什么”你提出的解决方案。如果你要参加一个数据分析师面试,利用像 Tableau 的 Stories 功能这样的工具,或者考虑使用一个幻灯片应用程序,以一种更优雅的方式展示你的发现。预测诸如“这些数据是从哪里来的?”这样的问题也会有所帮助您可能还会收到这样的问题:“还有哪些数据集可能与这个用例相关?”记住练习,最重要的是,慢下来。带你的观众踏上旅程,而不是冲刺。

一个构思良好且执行良好的数据项目会模仿你所申请的公司或业务领域的想法、工作流程和技术。所以花点时间把它做好。

完成最后一轮数据科学面试

原文:https://towardsdatascience.com/nailing-your-final-round-data-science-interview-3e07f758ab25

如果你以技术面试的方式进行最后的面试,不要期待同样的结果

来源:Shutterstock

告诉我这听起来是否熟悉:你在第一轮与人力资源代表有过愉快的经历。你喜欢在与现有团队成员的访谈中谈论工作。技术评估呢?比方说,你可能投入了过多的时间和精力,但这就是你。最后,你进入了最后一轮,希望这只是一种形式,但是他们会问,“第一类错误和第二类错误有什么区别?”

如果你和我一样,你可能会想,“啊,我讨厌这个问题!”不要误解我的意思,我并不是不明白其中的区别或者不知道如何应用它们,但是统计学家就不能为它们想出更直观的术语和定义吗?不过没关系,你在之前的面试中摸索过类似的问题,他们也理解了。这肯定会是一样的。但是之后你会收到一封可怕的邮件:

感谢您抽出时间与我们的团队会面。很高兴能更多地了解你的技能和成就。不幸的是,我们的团队没有选择你做进一步的考虑。

大多数专业技术人员在进入管理岗位之前不会遇到这个障碍。然而,数据科学家在他们的公司中有着独特的地位,跨越了高级统计和商业战略的世界。此外,数据科学项目的爆炸式流行在市场上创造了初级数据科学家的过剩。因此,公司对于雇佣谁来担任这些令人垂涎的职位变得越来越挑剔。

这体现在一个常见的观察中,即新数据科学家发现自己在面试过程的不同阶段多次回答相同的问题。看似重复,毫无意义,但实际上,评价标准每次都在变。在这些情况下,精神错乱可能被定义为一遍又一遍地做同样的事情,并期望得到 同样的结果。

不同的评分标准

想象你回到学校,写一篇关于内战的文章。暂时假装你是一个专家,拥有丰富的知识,这将是一个有趣而有知识性的阅读。你交上去,不出意外,你得了 A+!在学年的晚些时候,你在另一个班级收到了同样的作业,所以你决定交同样的论文。只是这一次,你最终得到了 b。这种差距怎么解释呢?第一节课是历史,另一节是英语。

当你进入最后一轮数据科学面试时,要知道公司已经审查了你的技术知识。面试你的人知道这一点。通常,那个人与日常的技术工作相距甚远,他们甚至没有资格评估你的技术专长。那么,他们为什么要问这样一个尖锐的技术问题呢?他们试图评估的不是你的知识;是你的风度。

在你的最后一次面试中,你如何给出答案比答案本身更重要。这并不意味着你可以简单地编造或给出错误的答案。这只是意味着你不必担心技术上的精确性。如果我们回到文章的例子,事实的准确性类似于技术面试。相比之下,你的期末面试就像评估文章的语法和衔接方面。但是如果你声称奥巴马在 1776 年领导南方取得了胜利,甚至你的英语老师也会惩罚你!

这种平衡技术知识和业务知识的能力是数据科学家与其他技术专业人员的区别。在传统的开发环境中,产品经理定义输出,工程经理分配和计划工作,工程师和设计师按照设定的时间表按规格制造产品。在数据科学中,你并不总是知道最终的输出会是什么。您的工作甚至可能旨在告知产品经理哪些功能应该优先考虑!

当你的工作成果与那些群体的直觉相矛盾时会发生什么?假设你的工作没有缺陷,你是否有能力自信地捍卫你的成果,让企业领导人采取正确的行动?如果答案是否定的,那么你所有的工作都是徒劳的。这就是你的最终面试官想要评估的。如果没有人买你卖的东西,你是对是错都没有关系。

拥有正确的心态

将您的数据科学工作视为您的产品。产品卖不出去再好也没用。当您适应新角色时,您将发现新的见解来推动业务决策,但非技术利益相关者通常会做出这些决策。如果他们不相信或不理解你的推荐,他们也不会接受。他们会问一些尖锐的技术性很强的问题,但他们不会寻求技术性的回答。你如何处理这些情况是你的最终面试官想要弄清楚的。

在这些情况下,你的目标是展示你有能力:

  1. 在压力下保持冷静。
  2. 用简单易懂的术语解释复杂的想法。
  3. 将数据科学概念与业务案例联系起来。

考虑到这一点,让我们回到最初的问题:“1 型和 2 型错误有什么区别?”如果我们少关注问题的技术方面,多关注以上三点,答案会是什么样的呢?

首先,你应该准备根据你要面试的工作量身定制这个答案。例如,如果您要进行欺诈检测,一个答案可能是这样的:

“第一类和第二类错误描述了统计模型是如何出错的。没有一个模型是完美的,降低一种错误的风险会增加另一种错误的风险。例如,如果我们正在构建检测欺诈的模型,有时模型可能会在没有欺诈活动时预测到欺诈活动。或者,当欺诈活动出现时,模型可能无法检测到它。每种情况都有不同的后果;在这种情况下,错过欺诈活动会更糟。因此,我们需要调整我们的模型,将这种风险降至最低。”

让我们把它分解一下。首先,我们避免所有假阳性和假阴性的技术术语,也不提及精确度或召回。为什么?因为非技术涉众并不关心,它所做的只是引入混乱,所以不要去那里。相反,最好把重点放在你能想到的最直白的、一行的概念描述上,并说明它为什么重要。使用与你的听众相关的现实生活中的例子是最好的方法。使用与你申请的工作直接相关的例子更好,因为这表明你可以直接将技术术语与商业案例联系起来。

心态的转变

我相信你已经在想,“好吧,听起来很棒。但是我怎么能在飞行中得出这样的答案呢?”你可以做一些对你下次面试有帮助的事情,我们稍后会讲到。但是首先,你必须知道这是一项你必须发展和练习的技能。如果你能在面试中做到这一点,但在开始工作时却不能做到这一点,那你就不会走得很远。因此,如果你养成一个习惯,总是从最简单的答案开始,并评估这个答案是否满足你的听众,那将是最好的。

在上面的例子中,这个答案甚至可以满足技术面试,这取决于个人。所以问他们,“这是否回答了你的问题,或者你想要更具体的细节?”有些人想听你给出细节;别人只是想知道你可以。正确猜测他们的喜好不是你的工作。让他们告诉。这种方法的一大好处是,如果他们确实想获得更多的技术知识,它允许你有时间整理你的想法。与其在思考时陷入尴尬的短暂沉默(这只会让你更加挣扎),不如让自己进入一个更自然的对话。这为我们提供了三件你可以做的事情,让你在下一次,也是最后一次面试中获得成功!

  1. 阐明你所听到的。你不必等到第一次回答后才阐明你的答案已经足够。澄清你对问题的理解是正确的,这总是一个很好的做法。例如,“我可以用几种方式来回答这个问题。你更感兴趣的是我对这些术语的技术理解,还是我将如何在实践中应用它们?”但这里是最关键的部分:他们如何回答并不重要!他们可以说他们更喜欢其中一个,或者两个都喜欢。你所做的只是给自己一点时间来组织你的想法。一旦你给出了你的答案,澄清这个答案足以给你自己提供另一个时刻,以防他们想要更多的细节。
  2. 提前准备例子。在你开始第一次技术面试之前,你应该对你在新职位上将会遇到什么样的问题有一个大致的概念。最起码你懂行业。向非技术人员解释时,请选择您最难理解的前 3 个概念。写下你对每个概念的理解,看看你是否能把它缩减成 1-2 个句子,这样你就能记住并适时重复。然后在网上搜索这个概念如何应用于你所申请的行业的例子,做同样的事情:用你自己的话把它概括成 1-2 句话。一旦你记下了这些,你应该能够将这些例子应用到你在面试中可能会遇到的其他概念中去。
  3. 练习,练习,练习。重要的是要记住,仅仅因为某件事对你有意义,并不意味着其他人会以同样的方式理解它。一旦你准备好了你的例子,问问你的家人和朋友他们是否能帮助你。不一定是典型的角色扮演(我一直讨厌那些!),但只要让他们知道你正在努力更好地向那些没有相同技术背景的人解释技术概念。如果对他们有意义,你知道你已经得到了。如果没有,他们可以给你反馈,告诉你他们在哪里感到困惑,这样你就可以改进。我的妻子和我总是这样做,因为我们在不同的领域工作,这有助于我们理顺我们的思维。记住这句谚语,“如果你不能向一个六岁的孩子解释,那你就理解得不够好。”——阿尔伯特·爱因斯坦(据称是)

请记住,作为一名数据科学家,您的主要职责是通知那些将做出决策并采取行动的人。每隔一段时间,你的工作就会在别人 20 年的经验面前飞起来。你需要展示你有能力以专业和有说服力的方式坚持自己的立场。如果你能记下这一点,你不仅会在下一次面试中胜出,还会给你的技术非技术同事留下即时而持久的印象。

像许多其他数据科学概念一样,这是您需要掌握的技能。除非你要为 FAANG 公司从事 AI/ML 研究,否则这将比你的神经网络反向传播知识更有价值!

PyTorch 中基于 BERT 的命名实体识别

原文:https://towardsdatascience.com/named-entity-recognition-with-bert-in-pytorch-a454405e0b6a

如何利用定制数据的预训练 BERT 模型来预测文本中每个单词的实体

照片由 Aaron BurdenUnsplash

当涉及到处理 NLP 问题时,BERT 经常作为一个机器学习模型出现,我们可以依靠它的性能。事实上,它已经预先训练了超过 2500 万个单词,它从一系列单词中学习信息的双向性质使它成为一个强大的模型。

我以前写过如何利用 BERT 进行文本分类,在本文中,我们将更多地关注如何使用 BERT 进行命名实体识别(NER)任务。

什么是 NER?

NER 是自然语言处理中的一项任务,用于识别和提取句子或文本中有意义的信息(或者我们可以称之为实体)。一个实体可以是一个单词,甚至是指同一类别的一组词。

作为一个例子,让我们说我们下面的句子,我们想从这个句子中提取关于一个人的名字的信息。

NER 任务的第一步是检测实体。这可以是指同一类别的一个单词或一组单词。举个例子:

  • ➡️由一个单词组成的实体
  • 由两个词组成的实体,但它们指的是同一个范畴。

为了确保我们的 BERT 模型知道实体可以是单个单词或一组单词,我们需要通过所谓的内部-外部-开始(IOB)标记在我们的训练数据上提供关于实体的开始和结束的信息。我们将在本文后面的数据集上看到更多这方面的内容。

检测到实体后,NER 任务的下一步是对检测到的实体进行分类。实体的类别可以是任何东西,这取决于我们的用例。以下是实体类别的示例:

  • 人物:邦德、詹姆斯·邦德、山姆、安娜、弗兰克、莱昂纳多·迪卡普里奥
  • 地点:纽约、维也纳、慕尼黑、伦敦
  • 组织:谷歌、苹果、斯坦福大学、德意志银行
  • 地点:中央公园,勃兰登堡门,时代广场

在我们的 BERT 模型的训练过程中,这些实体基本上是我们的数据的标签,我们将在下面的部分中详细讨论。

NER 的伯特

如前所述,BERT 是一个基于变形金刚的机器学习模型,如果我们想解决 NLP 相关的任务,它会非常方便。

如果您还不熟悉 BERT,我建议您在阅读本文之前先阅读我以前的一篇关于 BERT 文本分类的文章。在那里,您可以找到关于 BERT 实际上是什么、模型期望哪种输入数据以及您将从模型获得的输出的信息。

用于文本分类的 BERT 和 NER 问题之间的区别在于我们如何设置模型的输出。对于一个文本分类问题,我们只使用从特殊的【CLS】标记输出的嵌入向量,正如你在下面的可视化中看到的:

作者图片

同时,如果我们想要使用 BERT 来完成 NER 任务,我们需要使用来自所有令牌的嵌入向量输出,正如您在下面的可视化中所看到的:

作者图片

通过使用从所有标记输出的嵌入向量,我们可以在标记级别对文本进行分类。这正是我们想要的,因为我们希望我们的伯特模型预测每个令牌的实体。现在事不宜迟,我们去实现吧。

关于数据集

我们将在本文中使用的数据集是 CoNLL-2003 数据集,这是一个专门用于 NER 任务的数据集。你可以通过下面的链接下载 Kaggle 上的数据。

https://www.kaggle.com/datasets/rajnathpatel/ner-data

这个数据集是在开放数据库 1.0 版许可下分发的,因此我们可以出于自己的目的自由共享和使用这个数据集。现在让我们看看数据集是什么样子的。

正如我们在上面看到的,我们有一个由文本和标签组成的数据框架。标签对应于文本中每个单词的实体类别。

总共有 9 个实体类别,它们是:

  • geo为地理实体
  • org为组织实体
  • per为人身实体
  • gpe对于地缘政治实体
  • tim为时间指示实体
  • art为工件实体
  • eve为事件实体
  • nat为自然现象实体
  • 如果单词不属于任何实体,则分配O

让我们来看看数据集中可用的唯一标签:

您可能会注意到,每个实体类别前面都有字母IB。这对应于前面提到的 IOB 标记。I表示 中间B表示 开始 。让我们来看看下面的句子,以便更好地理解 IOB 标记的概念。

  • “凯文”有B-pers的标签,因为它是一个人实体的开始
  • “杜兰特”有I-pers的标签,因为它是一个人实体的延续
  • ‘布鲁克林’有B-org因为它是一个组织实体的开始
  • “网络”有I-org的标签,因为它是一个组织实体的延续
  • 其他单词被分配O标签,因为它们不属于任何实体

数据预处理

当然,在我们能够使用 BERT 模型对标记的实体进行分类之前,我们需要首先进行数据预处理,这包括两个部分:标记化和调整标签以匹配标记化。先从标记化说起。

标记化

使用 BERT 可以很容易地实现标记化,因为我们可以使用带有 HuggingFace 的预训练 BERT 基础模型中的BertTokenizerFast类。

为了举例说明 BERT tokenizer 是如何工作的,让我们来看看数据集中的一个文本:

BertTokenizerFast标记上面的文本非常简单:

当从上面的BertTokenizerFast类调用tokenizer方法时,我们提供了几个参数:

  • padding:用一个特殊的【PAD】标记将序列填充到我们指定的最大长度。BERT 模型的最大序列长度为 512。
  • max_length:序列的最大长度。
  • truncation:这是一个布尔值。如果我们将该值设置为 True,那么将不会使用超过最大长度的令牌。
  • return_tensors:返回的张量类型,取决于我们使用的机器学习框架。既然我们使用 PyTorch,那么我们使用pt

下面是标记化过程的输出:

如您所见,我们从标记化过程中获得的输出是一个字典,其中包含三个变量:

  • input_ids:序列中记号的 id 表示。在 BERT 中,id 101 为特殊【CLS】令牌保留,id 102 为特殊【SEP】令牌保留,id 0 为【PAD】令牌保留。
  • token_type_ids:标识令牌所属的序列。因为每个文本只有一个序列,所以token_type_ids的所有值都是 0。
  • attention_mask:识别令牌是真令牌还是填充符。如果它是一个真实的令牌,则该值为 1,如果它是一个【PAD】令牌,则该值为 0。

从上面的input_ids中,我们可以用下面的decode方法将 ids 解码回原始序列:

在实现了decode方法之后,我们恢复了原来的序列,增加了来自 BERT 的特殊标记,例如序列开头的【CLS】标记,序列结尾的【SEP】标记,以及一堆【PAD】标记,以满足所需的最大长度 512。

在这个标记化过程之后,我们需要进行下一步,即调整每个标记的标签。

标记化后调整标签

这是我们在标记化过程之后需要做的一个非常重要的步骤。这是因为在标记化过程之后,序列的长度不再匹配原始标签的长度。

BERT 记号赋予器使用所谓的词块记号赋予器,它是一个子词记号赋予器。这意味着 BERT tokenizer 可能会将一个单词拆分成一个或多个有意义的子单词。

例如,假设我们有以下序列:

上面的序列总共有 13 个标记,因此它也有 13 个标签。然而,在 BERT 标记化之后,我们得到以下结果:

在令牌化过程之后,我们需要解决两个问题:

  • 增加了来自 BERT 的特殊令牌,如【CLS】【SEP】【PAD】
  • 一些记号被分裂成子单词的事实。

作为子词标记化,词块标记化将生僻字拆分成它们的子词,例如上面示例中的' Geir '和' Haarde '。这种子词标记化有助于 BERT 模型学习相关词的语义。

这种单词片段标记化和添加来自 BERT 的特殊标记的结果是标记化后的序列长度不再匹配初始标签的长度。

从上面的例子中,现在在标记化之后,序列中总共有 512 个标记,而标签的长度仍然与之前相同。此外,序列中的第一个标记不再是单词' Prime ,而是新添加的【CLS】标记,因此我们也需要移动我们的标签。

为了解决这个问题,我们需要调整标签,使其与标记化后的序列具有相同的长度。为此,我们可以利用来自标记化结果的word_ids方法,如下所示:

从上面的代码片段中可以看出,每个被拆分的令牌共享同一个word_ids,其中来自 BERT 的特殊令牌如【CLS】【SEP】【PAD】都没有特定的word_ids

这些word_ids将非常有助于通过应用以下两种方法中的任何一种来调整标签的长度:

  1. 我们只为每个分裂令牌的第一个子词提供一个标签。子词的延续将简单地用“-100”作为标签。所有没有word_ids的代币也将标上“-100”。
  2. 我们在属于同一单词的所有子单词中提供相同的标签。所有没有word_ids的令牌都将被标记为“-100”。

下面代码片段中的函数将执行上面定义的步骤。

如果要应用第一种方法,将label_all_tokens设置为假。如果您想应用第二种方法,请将label_all_tokens设置为 True,如下面的代码片段所示:

在本文的其余部分,我们将实现第一种方法,在这种方法中,我们将只为每个令牌中的第一个子词提供一个标签,并将label_all_tokens设置为 False。

数据集类

在我们为 NER 任务训练 BERT 模型之前,我们需要创建一个数据集类来批量生成和获取数据。

在上面的代码片段中,我们用__init__函数中的tokenizer变量调用BertTokenizerFast类来标记我们的输入文本,用align_label函数在标记过程后调整我们的标签。

接下来,让我们将数据随机分为训练、变异和测试。但是,请注意,数据总数是 47959。因此,为了演示和加快培训过程,我将只选取其中的 1000 个。当然,你可以将所有的数据用于模型训练。

模型结构

在本文中,我们将使用来自 HuggingFace 的预训练 BERT 基本模型。既然我们要在标记级别对文本进行分类,那么我们需要使用BertForTokenClassification类。

BertForTokenClassification class 是一个模型,它包装了 BERT 模型,并在 BERT 模型之上添加了线性层,这些层将充当令牌级分类器。

在上面的代码片段中,首先,我们实例化模型,并将每个令牌分类器的输出设置为数据集上唯一实体的数量,在我们的示例中是 17。

接下来,我们将为训练循环定义一个函数。

训练循环

我们的 BERT 模型的训练循环是标准 PyTorch 训练循环,增加了一些内容,如下所示:

在上面的训练循环中,我只训练了 5 个时期的模型,然后使用 SGD 作为优化器。每批的损耗计算已经由BertForTokenClassification级负责。

在训练循环的每个时期,我们还需要做一个重要的步骤。在模型预测之后,我们需要忽略所有标签为'-100 '的标记,正如您在第 36、37、62 和 63 行中看到的。

以下是我们训练 5 个时期的 BERT 模型后的训练输出示例:

当然,当您训练自己的 BERT 模型时,您将看到的输出可能会有所不同,因为在训练过程中存在随机性。

您可以做很多事情来提高我们模型的性能。如果你注意到了,我们有一个数据不平衡的问题,因为有很多带有“O”标签的令牌。例如,我们可以通过在训练过程中应用类权重来改进我们的模型。

此外,您可以尝试不同的优化器,如权重衰减正则化的 Adam 优化器。

根据测试数据评估模型

既然我们已经训练了我们的模型,我们可以用下面的代码片段来评估它在看不见的测试数据上的性能。

在我的例子中,经过训练的模型在测试集上达到了平均 92.22%的准确率。当然,您可以将指标更改为 F1 得分、精确度或召回率。

或者,我们可以使用经过训练的模型来预测文本或句子中每个单词的实体,代码如下:

如果一切都完美,那么我们的模型将能够相当好地预测一个看不见的句子的每个单词的实体,正如你在上面看到的。

结论

在这篇文章中,我们实现了命名实体识别(NER)任务的伯特。这意味着我们已经训练了 BERT 模型来在标记级别预测定制文本或定制句子的 IOB 标记。

我希望这篇文章能帮助你开始使用 NER 任务的 BERT。你可以在 这本笔记本 中找到本文实现的所有代码。

深度学习命名实体识别(BERT)——基本指南

原文:https://towardsdatascience.com/named-entity-recognition-with-deep-learning-bert-the-essential-guide-274c6965e2d

从数据准备到 NER 任务的模型训练——以及如何给自己的句子加标签

照片由亚伦·伯顿Unsplash 拍摄

更新:您现在可以了解如何使用 Streamlit 部署该型号!

如今,NLP 已经成为深度学习的代名词。

但是,深度学习并不是每个 NLP 任务的“灵丹妙药”。例如,在句子分类任务中,简单的线性分类器可以相当好地工作。尤其是如果你有一个小的训练数据集。

然而,一些 NLP 任务随着深度学习而蓬勃发展。其中一个这样的任务就是 命名实体识别 — NER:

NER 是识别 并将命名实体归类到预定义的实体类别中的过程。

例如,在句子中:

尼克生活在希腊的 T21,是一名数据科学家。

我们有两个实体:

  • 尼克,也就是一个的‘人’。
  • 希腊,即的一个‘地点’。

因此,根据上面的句子,分类器应该能够定位这两个术语('尼克','希腊'【T34),并正确地将它们分别分类为'和'地点'。

在本教程中,我们将建立一个 NER 模型,使用 HugginFace 变形金刚

让我们开始吧!

加载数据

我们将使用已经包含在 HugginFace 数据集库中的wnut _ 17【1】数据集。

浏览数据集

该数据集侧重于在新兴讨论的背景下识别不寻常的、以前未见过的实体。它包含 5690 文件,分为培训套、vali d 套和测试套。文本句子被标记成单词。让我们加载数据集:

wnut = load_dataset(“wnut_17”)

我们得到以下结果:

接下来,我们打印ner_tags ——我们模型的预定义实体:

每个ner_tag描述一个实体。可以是以下之一:公司创意作品团体地点人物、产品。

每个ner_tag前面的字母表示实体的令牌位置:

  • B - 表示一个实体的开始。
  • I - 表示一个令牌包含在同一个实体内部(例如" York" 令牌是" New York "实体的一部分)。
  • 0 表示该令牌不对应任何实体。

我们还创建了id2tag 字典,它将每个标签映射到它的ner_tag——这将在以后派上用场。

重组培训和验证数据集

我们的数据集没有那么大。请记住,变形金刚需要大量数据来利用其卓越的性能。

为了解决这个问题,我们将训练验证数据集连接成单个训练数据集。出于验证目的,测试数据集将保持原样:

培训示例

让我们打印数据集中的第三个训练示例。我们将在整个教程中使用该示例作为参考:

' Pxleyes' 令牌被归类为B-corporation(一个公司的开始)。其余的记号是不相关的——它们不代表任何实体。

预处理

接下来,我们标记我们的数据。与其他用例相反, NER 任务的令牌化需要特殊处理。

我们将使用 HugginFace 库中的bert-base-uncased模型和标记器。

Transformer 模型大多使用基于子词的记号化器。

在标记化过程中,一些单词可能会被拆分成两个或多个单词。这是一种标准的做法,因为罕见的单词可以分解成有意义的符号。例如, BERT 模型默认实现了字节对编码(BPE)令牌化。

让我们对我们的样本训练示例进行符号化,看看这是如何工作的:

这是最初的培训示例:

这就是 BERT 的标记器如何标记训练示例:

请注意,有两个重要问题:

  • 添加特殊标记[CLS][SEP]
  • 令牌“pxl eyes”被拆分成 3 个子令牌:p##xley##es

换句话说,标记化造成了输入和标签之间的不匹配。因此,我们按照以下方式重新排列令牌和标签:

  1. 每个单词标记被映射到其对应的ner_tag
  2. 我们将标签-100分配给特殊记号[CLS][SEP],因此损失函数忽略它们。默认情况下,PyTorch 在损耗计算过程中忽略-100 值。
  3. 对于子词,我们只标记给定词的第一个标记。因此,我们将-100分配给同一个单词的其他子发音。

比如令牌px eyes被标注为1 ( B-corporation)。它被标记为[‘p’, ‘##xley’, ‘##es’],标记对齐后,标签应变成[1, -100, -100]

我们在tokenize_and_align_labels()函数中实现该功能:

就是这样!让我们调用我们的自定义标记化函数:

下表准确显示了我们的样本训练示例的标记化输出:

微调模型

我们现在准备建立我们的深度学习模型。

我们加载bert-base-uncased预训练模型,并使用我们的数据对其进行微调。

但是首先,我们应该训练一个简单的分类器作为基线模型。

基线模型

对于基线分类器来说,最明显的选择是用整个训练数据集中最频繁出现的实体来标记每个标记—O实体:

如果我们用它所属的句子的最频繁的标签来标记每个记号,基线分类器变得不那么简单:

因此,我们使用第二个模型作为基线。

用于命名实体识别的 BERT

数据整理器将训练样本分批在一起,同时应用填充以使它们大小相同。排序器不仅填充输入,还填充标签:

关于评估,由于我们的数据集是不平衡的,我们不能只依靠准确性

因此,我们还将测量精度召回。这里,我们将加载包含在数据集库中的[seqeval](https://github.com/chakki-works/seqeval)指标。该指标通常用于词性标注NER 任务。

让我们将它应用到我们的参考培训示例中,看看它是如何工作的:

注意:记住,损失函数在训练时会忽略所有标记有-100的记号。我们的评估功能也应该考虑这些信息。

因此,compute_metrics 函数的定义稍有不同——我们通过忽略所有标有-100的内容来计算 精度召回f1 分数、精度 :

最后,我们实例化Trainer类来微调我们的模型。注意提前停止回调的用法:

这些是我们的培训指标:

与基线模型相比,该模型实现了更好的验证准确性。此外,如果我们使用更大的模型,或者让模型训练更多的时期而不应用提前停止回调,我们可以获得更好的f1-score

测试集评估

对于我们的测试集,我们使用与之前相同的方法。

seqeval度量还输出每个类的度量:

locationperson实体得分最高,而group得分最低。

获得预测

最后,我们创建一个对我们自己的句子执行实体识别的函数:

让我们试几个例子:

模型已经成功标记了这两个国家!看一看美国:

  • 联队被正确标记为B-location
  • 状态被正确标记为I-location

苹果再次被正确地标记为公司。此外,我们的模型正确地识别了苹果产品。

结束语

命名实体识别 是一项基本的自然语言处理任务,有着众多的实际应用。

尽管 HugginFace 库已经为这个过程创建了一个超级友好的 API,但是仍然有一些混乱的地方。

我希望这篇教程对他们有所启发。这篇文章的源代码可以在这里找到

感谢您的阅读!

  • 订阅我的简讯
  • 在 Linkedin 上关注我!
  • 加入 Medium!(附属链接)

参考

1.WNUT 2017 (WNUT 2017 新兴和稀有实体认可),许可证:CC BY 4.0,来源

具有部分注释数据的命名实体识别

原文:https://towardsdatascience.com/named-entity-recognition-with-partially-annotated-data-ec679d42fceb

利用你的字典和规则快速建立一个 NER 模型

照片由皮斯特亨Unsplash 上拍摄

命名实体识别(NER)是信息提取的一个子任务,它识别文档中的实体并将它们分类到预定义的类别中,如人名、组织、位置等。NER 广泛应用于自然语言处理领域,如信息抽取、信息检索、问题回答等。

对于大型和高质量的手动注释数据集,这个问题可以通过微调预先训练的大型语言模型(如 BERT 和 RoBERTa)来解决。然而,准备如此大的和完全注释的数据集是昂贵的,并且在许多情况下是不现实的,例如低资源语言。

另一方面,部分带注释的数据集可以通过知识库、地名词典、正则表达式等轻松创建。在这些数据集中,大多数被识别的实体是正确的(高精度),但不是所有的实体都被识别(低召回)。如果我们能够充分利用这些数据集,我们应该能够减少注释成本,并加快模型开发。

从部分批注的数据集训练 NER 模型。图片来自资源库(麻省理工学院许可)。

然而,部分注释的数据集有一个问题:缺少实体,数据集可能不包含一些实体。这个问题会漏掉一些实体来注释。因此,在这种数据集上训练的标准模型无法识别这些缺失的实体。导致模型性能变差,尤其是召回率低。

为了解决这个问题,在 2021 年 TACL 中提出了预期实体比率(EER)损失1。这种损失促使实体在整个数据集中出现的百分比在某个范围内。通过结合这种损失,它将试图尽可能多地预测实体,即使当标签丢失时。

在接下来的几节中,我将向您展示如何使用 EER 从部分带注释的数据集中训练模型。为此,我将使用 spacy-partial-tagger ,这是一个使用 EER 实现模型的 Python 库。如果你想详细了解 EER,请看报纸。

https://github.com/doccano/spacy-partial-tagger

让我们开始实施。

快速入门

首先你需要安装spacy-partial-tagger。如果您使用 M1 Mac,安装fugashi可能会有问题。在这种情况下,请在安装前尝试使用brew install mecab

pip install spacy-partial-tagger

接下来,将数据集准备为 spaCy 二进制格式文件。本库期望标记化是基于字符的。关于格式的更多细节,见本页。在下面的示例中,spaCy 的 EntityRuler 和一个小字典用于创建部分带注释的数据集:

为了训练模型,使用spacy train命令。它只需要一个包含所有设置和超参数的[config.cfg](https://spacy.io/usage/training#config)配置文件。您可以选择在命令行上用覆盖设置。下面是一个spact-partial-tagger’s配置文件的例子:

最后,运行train命令:

python **-**m spacy train config**.**cfg \
       **--**output**=./model** \
       **--**paths**.**train corpus**/**train**.**spacy \
       **--**paths**.**dev corpus**/**valid**.**spacy \
       **--**gpu**-**id 0 \
       **--**training**.**patience 1000

就是这样。如果你想知道详细情况,请查看库中的笔记本

实验

在这一节中,我将在 dictionary match 创建的部分带注释的数据集上运行实验并展示 EER 模型的性能。由于字典大小在真实场景中是有限的,所以我将测试字典大小在 20~100%之间变化时的性能。

我在电子商务领域使用 DSNER 数据集进行命名实体识别( Unlicense license )。它包含“品牌”、“材料”、“模型”、“产品”和“规格”作为实体类。至于字典,我用的是已经发表在前期研究【2】(同许可)的一本。这个字典中存储的实体数量是 927。至于预训练模型,我选择了 HFL 出版的hfl/chinese-bert-wwm-ext

我们来看看以下四款车型的表现:

  • hfl/chinese-bert-wwm-ext全监督数据(full supervised)训练
  • hfl/chinese-bert-wwm-ext对部分注释的数据进行训练(hfl/chinese-bert-wwm-ext
  • hfl/chinese-bert-wwm-ext +针对部分注释数据训练的 EER 模型(+ EER)
  • 字典匹配

结果

结果如下。在这种情况下,我们可以看到在部分注释的数据集上训练的hfl/chinese-bert-wwm-ext模型的性能优于字典匹配的性能。我们还看到,使用 EER 可以提高性能,尤其是当字典很小时。这是因为使用 EER 可以提高召回率。结果还表明,其性能几乎与完全监督的性能相当。

不同字典大小下的性能(图片由作者提供)

最后的话

在本文中,我将向您展示如何使用 EER 从部分带注释的数据集中训练命名实体识别模型。实验结果表明,EER 对于电子商务数据集和词典组合的有效性,尤其是在词典规模较小时。我认为 EER 并不是在所有情况下都有效,但是如果你对此感兴趣,请尝试spacy-partial-tagger

参考

  1. 托马斯·埃夫兰和迈克尔·柯林斯。2021.通过预期实体比率损失进行部分监督的命名实体识别。计算语言学协会汇刊,9:1320–1335。
  2. 杨耀生、、、何正秋和。2018.远程监督 NER 进行部分标注学习和强化学习。《第 27 届国际计算语言学会议论文集》,第 2159-2169 页,美国新墨西哥州圣达菲。计算语言学协会。

用 Spacy 和强大的 roBERTa 进行命名实体识别

原文:https://towardsdatascience.com/named-entity-recognition-with-spacy-and-the-mighty-roberta-97d879f981

命名实体识别(NER),文本数据挖掘的一个重要方面

作者图片

介绍

当阅读文本时,人类可以自然地识别命名的实体,如日期、货币、位置、人名、医疗代码、品牌等。然后,该步骤可能与从大文本数据中进一步提取信息相关,以便更好地回答以下问题:

  • 新闻中的 covid19 变体趋势名称是什么?
  • 在给定的工作描述中提到了哪些工具,熟练程度如何?
  • 等等。

本文的目标是使用 spaCy transformers 中的 spaCy 和 roBERTa 来自动识别和提取前面介绍中定义的实体。

命名实体识别与提取

如果你喜欢,可以在 YouTube 上观看完整视频:

实验数据

我们将执行两个不同文本数据的实体提取,一个短文本和一个更长的文本(分别来自 CNN 和 Etoro )最后,比较传统 spaCy 和 roBERTa 的性能。

  • 短文 : 艾米·施耐德,一位来自加州奥克兰的工程经理,成为《危险边缘》节目的第一位女性和第四位主持人。“在周五的比赛中赢得超过 100 万美元的奖金”。
  • 较长正文:对消费者来说无疑是好消息,对投资者来说也是好消息。苹果公司最近的业绩涵盖了截至 2016 年 1 2 月 31 日的三个月,该公司首席财务官卢卡·马埃斯特里宣布:“我们在本季度通过股票回购和股息向投资者返还了近 150 亿美元。”季度股息为每股 57 美分,与前三个季度的股息相同,高于此前四个季度的每股 52 美分。苹果公司生意兴隆。1 月 31 日,苹果首席执行官蒂姆·库克(Tim Cook)在谈到 2016 年最后三个月时说:“我们很高兴地宣布,我们的假日季度业绩创造了苹果有史以来最高的季度收入,并打破了多项纪录。我们卖出了比以往更多的 iPhone,并创造了 iPhone、服务、Mac 和 Apple Watch 的历史收入记录

先决条件

这是关于安装分析所需的所有库。

提取和可视化命名实体的步骤

第一个函数print _ entities用于从给定的文本和管道中提取命名实体(在我们的例子中是传统的 spaCy 或 spaCy transformer)。它的工作原理如下:

  • pipeline是我们从spacy.load指令中得到的语言对象。它包含处理text所需的所有组件和数据
  • pipeline(text)运行pipeline并返回已处理的document
  • 命名实体是通过迭代document.ents对象获得的。
  • 然后在每次迭代中,我们可以从.text访问实体文本,从.label_访问实体标签

第二个函数visualize _ entities执行displacy.render函数来产生所有实体的良好可视化。

既然我们已经安装了所有的库并定义了命名实体提取函数,我们就可以继续进行分析了。

传统空间

这是一个面向工业级自然语言处理的开源库,已经在 OntoNotes 5.0 语料库上进行了训练。

分析短文

print_entities(nlp_sm, short_text)

图 1:实体文本和短文本的相应标签(图片由作者提供)

visualize_entities(nlp_sm, short_text)

图 2:实体文本的可视化和短文本的相应标签(图片由作者提供)

传统空间成功地将 CNN 识别为一个组织(ORG),Amy Schneider 识别为一个人,Oakland 和 California 识别为地理政治实体(GEP)等。你可以在文件实体名称注释部分找到实体代码定义的详尽列表。

分析长文

print_entities(nlp_sm, long_text)

图 3:实体文本和较长文本的相应标签(图片由作者提供)

visualize_entities(nlp_sm, short_text)

图 4:实体文本的可视化和较长文本的相应标签(图片由作者提供)

观察:对于短文本,结果看起来很完美,因为所有的实体都被正确识别了。但是,对于第二个文本,模型犯了以下错误

  • Apple Watch 误认为是Organization(组织)而不是产品。
  • 缺少识别 iPhoneMac。

让我们看看罗伯塔模型在同样的文本上是如何表现的。

太空变形金刚—罗伯塔

这是来自 2019 年 spaCy 推出的spacy-transformers库。它旨在通过将 spacy 连接到 HuggingFace 的变压器模型来为 spaCy 管道供电。

分析短文

print_entities(roberta_nlp, short_text)

图 5:实体文本和长文本的相应标签(图片由作者提供)

visualize_entities(roberta_nlp, short_text)

图 6:实体文本的可视化和长文本的相应标签(图片由作者提供)

到目前为止,一切都很好,与短文本上传统空间的结果相比没有什么变化。

分析长文

print_entities(roberta_nlp, long_text)

图 6:实体文本和较长文本的相应标签(图片由作者提供)

visualize_entities(roberta_nlp, long_text)

图 7:实体文本的可视化和较长文本的相应标签(图片由作者提供)

下面的汇总表包含传统 spacy 和 roBERTa 对长文本的结果。我们可以清楚地看到,罗伯塔的表现优于传统空间。罗伯塔只失败过一次,就是没有认出麦克。另一方面,传统空间无法识别 iPhone 和 Mac,但也错误地识别了 Apple Watch。

图 8:实体标识对照表。罗伯塔胜过传统空间(图片由作者提供)

我希望这篇文章是有用的。通过从下面的链接运行我的 GitHub 中的代码,自己尝试一下。在 YouTube上关注我以获得更多互动会话!

额外资源

GitHub

英语空间

太空变形金刚

再见🏃🏾

自然语言推理综述

原文:https://towardsdatascience.com/natural-language-inference-an-overview-57c0eecf6517

基准和模型

瓦伦丁·维萨摄于 Pexels

什么和为什么?

自然语言推理(NLI)的任务是确定是否给定的“假设”逻辑上遵循“前提”。通俗地说,你需要了解假设是否成立,而前提是你对该学科的唯一了解。

你为什么要读这个?我假设你对 NLI 一无所知,并承诺让你了解该领域的最新发展(2022 年 4 月)。对于一篇文章来说,这是一个相当大胆的承诺,不是吗?

问题陈述

自然语言推理也被称为识别文本蕴涵(RTE ),是确定给定的“假设”和“前提”在逻辑上是否相互遵循(蕴涵)或不遵循(矛盾)或不确定(中性)的任务。

你可以认为 NLI 根据前提将假设分为三类:蕴涵,矛盾,或中性。还有一个相关的问题:事实核查。事实核查问题与 NLI 非常相似。唯一不同的是,你没有前提。因此,事实核查包括两个问题:搜索问题和 NLI。在这篇文章中,我将集中讨论 NLI 问题。

基准

当开始一个新游戏时,第一步是学习游戏规则。在机器学习中,基准是研究人员遵循的事实规则。

SNLI

  • 网站
  • 论文
  • 基准
  • 示例:570k
  • 前提类型:句子。
  • 标签:蕴涵,中立,矛盾。

这是 NLI 标杆管理的经典之作,所以这个标杆被广泛使用,被尊重,坦白地说,已经过时了。
SNLI 数据集基于来自 Flickr30k 语料库的图像标题,其中图像标题被用作前提。该假设由土耳其机械工人按照以下说明手动创建:

  1. 蕴涵:写一个替代的标题,这个标题绝对是对照片的准确描述;
  2. 中性:写一个可能是照片的准确描述的备选标题;
  3. 矛盾:写一个替代说明,这是对照片的错误描述。

SNLI 有两个明显的缺点:

  • 前提是限于简短的照片描述,因此不包含时间推理,信仰,或模态。
  • 简单而简短的前提要求简单而简短的假设,因此在模型可以轻松达到人类准确性水平的情况下,基准测试不够具有挑战性。

MultiNLI

MultiNLI (MNLI)模仿 SNLI 数据集模式,但涵盖了一系列口语和书面语文本。因此,MNLI 可以与 SNLI 结合使用,并提供十种不同风格的文本。

MNLI 的前提来源于十个来源或体裁(基于开放的美国国家语料库):

  1. 面对面:两人对话的夏洛特叙事和对话集的转录;
  2. 政府:来自公共领域政府网站的报告、演讲、信件和新闻稿;
  3. 信件:来自印第安纳跨文化交流中心
    慈善募捐演讲的信件;
  4. 9/11:美国国家恐怖袭击委员会的公开报告;
  5. OUP:由牛津大学出版社出版的关于纺织业和儿童发展的五部非虚构作品;
  6. 石板:来自石板杂志档案的流行文化文章;
  7. 电话:从宾夕法尼亚大学语言数据联盟总机语料库中转录的双方电话对话;
  8. 旅行:Berlitz 出版社出版的旅行指南;
  9. 逐字记录:逐字记录档案中为非专业人员提供的关于语言学的帖子;
  10. 小说:几部 1912 年至 2010 年间创作的免费当代小说作品。

假设创建过程如下:向一名众包工作人员展示前提,并要求他写出三个新句子:

  1. 蕴涵:前提为真时必然为真或适当的事物;
  2. 矛盾:前提为真时必然为假或不恰当的矛盾;
  3. 中立:两种情况都不适用。

该数据集的一个重要特征是,十个流派中只有五个出现在训练集中,使得其他五个流派对于模型来说是不可见的。这些看不见的体裁可以用来评估模型对看不见的文本来源的概括程度。

超强力胶水

  • 网站
  • 论文
  • 基准
  • 基准
  • 示例:RTE: 3k,CB: <1k
  • 前提类型:句子
  • 标签:RTE:蕴涵,not _ 蕴涵;CB:包含,不确定,未知

SuperGLUE 是衡量 NLP 模型在三个任务中的性能的十个基准的集合:

  1. 自然语言推理
  2. 问题回答
  3. 共指消解

基于在这些任务中的表现,SuperGLUE 旨在提供一个单一的分数,该分数概括了模型在自然语言理解方面的能力。SuperGLUE 是一个非常流行的 GLUE 基准的扩展,具有更复杂的任务。

强力胶有两个 NLI 基准:RTE 和 CB。

RTE,即识别文本蕴涵,来自于每年的文本蕴涵比赛。RTE 包含 RTE1、RTE2、RTE3 和 RTE5 数据集。数据本身来自维基百科和新闻文章。RTE 的一个显著特点是标注的是二类分类而不是三类分类,所以没有中性标签。

CB,或 CommitmentBank,是一个短文本的语料库,其中至少有一个句子包含嵌入子句。每一个嵌入的条款都有注释,说明撰写该文本的人对该条款真实性的承诺程度。由此产生的任务是从华尔街日报、英国国家语料库的小说和 Switchboard 中抽取的例子的三类文本蕴涵。每个例子由一个包含嵌入子句的前提组成,相应的假设是该子句的提取。

发热

  • 网站
  • 论文
  • 基准
  • 示例:185k
  • 前提类型:维基百科网址+句子编号
  • 标签:支持、反驳、NotEnoughInfo

注意:这个数据集称假设为“主张”。

这个数据集不同于 SNLI 和 MNLI,因为它支持 NLI 和事实检查问题。数据集提供的不是前提,而是维基百科页面的 URL,可以从中提取前提。该数据集还提供了页面上的前提句数量,以支持纯 NLI 用例。

这些声明是人工生成的,根据维基百科页面的介绍部分进行人工验证,并标记为支持、反驳或无信息。

这些说法是通过转述维基百科的事实,并以各种方式对其进行变异而产生的,其中一些方式是改变意思。对于每一个主张,在不知道该主张是从哪里产生的情况下,注释者从维基百科中选择句子形式的证据来证明该主张的标注。

数据集的一个警告是,它没有为 NotEnoughInfo 标签提供前提的维基百科 URL。因此,如果您想将数据集用于 NLI 用例,您将需要自己搜索前提。

维基-事实核查

  • 网站
  • 论文
  • 基准:在有代码的论文上没有基准
  • 示例:160k
  • 前提类型:证据文档
  • 标签:蕴涵、矛盾、中性

与 SNLI、MNLI 和 FEVER 不同,该数据集由从维基百科引文中提取的真实说法组成。这些前提或证据文件是索赔中引用的文件。此外,数据集为每个索赔提供了上下文,这在不明确的情况下会有所帮助。对真实世界主张的强调确实给 NLI 提出了一个更具挑战性的任务。

这个数据集的缺点是质量:主张和前提是从维基百科自动提取的,有时毫无意义。

安利

  • 网站
  • 论文
  • 基准测试
  • 示例:第 1 轮— 19k,第 2 轮— 47k,第 3 轮— 103k
  • 前提类型:句子
  • 标签:蕴涵、矛盾、中性

安立是迄今为止最先进的 NLI 基准。ANLI 的收集过程与其他数据集非常不同,因为它采用了一种称为“人与模型在回路中的蕴涵训练”(HAMLET)的技术。

HAMLET 将数据收集过程分成几轮,每轮包括以下步骤:

  1. 训练一个 SOTA 模型。
  2. 人类注释者被给予上下文(前提)和期望的目标标签,并被要求生成一个假设,该假设将欺骗模型对标签进行错误分类
  3. 如果模型对示例进行了错误分类,则该示例将被展示给两个人类验证者以确保它是正确的。如果他们不同意,第三个人验证打破平局。
  4. 该示例被添加到这一轮的训练集中,并将用于为下一轮训练模型。

每一轮,模型和数据集的复杂性都在增加:

  1. 第一轮。型号:BERT-大号。数据集:SNLI + MNLI。上下文:维基百科+ HotpotQA。
  2. 第二轮。模特:罗伯塔模特的集合。数据集:SNLI+ MNLI +发烧+第 1 轮数据。上下文:新的来自维基百科+ HotpotQA。
  3. 第三轮。模特:罗伯塔模特的集合。数据集:SNLI + MNLI +发烧+第一轮数据+第二轮数据。上下文:各种来源,包括口语文本和更长的上下文。

在每一轮中,使用的 NLI 模型更强,背景更长,更难理解。因此,对手注释者必须在每一轮都拿出更多的例子,才能设法欺骗模型。例如,在第一轮中,注释者在模型被愚弄之前平均做了 3.4 次尝试,而在第三轮中,他们平均需要 6.4 次尝试。

ANLI 数据集被设计为比其他数据集更难收集,并提供更长的真实环境。此外,ANLI 通过增加新的回合提供了一种扩展机制,以便基准可以与 SOTA 模型一起发展。

SOTA 模型

这是你们期待已久的部分——模特!不出所料,源自 BERT 的架构在列表中名列前茅。

德伯塔

这是一个基于 Transformer 的架构,也是目前大多数强力胶任务上的 SOTA 模型:NLI (RTE,CommitmentBank),常识推理(ReCoRD),问题回答(COPA,MultiRC)。

德伯塔:

  1. 引入分散注意力机制,其中每个单词使用两个向量来表示,这两个向量对单词的内容和位置进行编码。单词间的注意力权重是使用关于单词内容和相对位置的解纠缠矩阵来计算的。
  2. 使用增强的屏蔽解码器将绝对位置结合到解码层中,以在模型预训练中预测屏蔽的记号。
  3. 采用一种新的虚拟对抗训练方法进行微调,以提高模型的泛化能力。

罗伯塔

这是一个基于变压器的架构。有趣的是,它只是调整了 BERT 训练过程的几个超参数,并实现了一个新的 SOTA 模型,击败了许多 BERT 修改。这项工作对 BERT 之后发布的模型所展示的性能改进的来源提出了质疑。

罗伯塔:

  1. 用更多数据的更大批量来训练模型更长时间
  2. 移除下一句预测目标
  3. 较长序列的训练
  4. 动态改变应用于训练数据的掩蔽模式

和德伯塔一起玩

我决定在 MNLI 数据集上重现 DeBERTa 结果。由于计算限制,选择了 DeBERTa 基础模型。MNLI 有两部分测试数据集:匹配和不匹配的流派。简而言之,匹配的流派出现在列车分裂,而不匹配的流派没有。根据 DeBERTa 的论文,DeBERTa 基本模型应该在匹配的 MNLI-m 测试数据集上产生 88.8%的准确度,在不匹配的 MNLI-mm 测试数据集上产生 88.5%的准确度:

罗伯塔和德贝塔的表现比较。

获得 DeBERTa 预训练权重的最简单方法是使用 HuggingFace 库。我用过微软的基于 deberta 的型号(T1 ),但也有更新的基于 T2 的型号(T3)。为了使用这些预先训练好的权重,你需要使用来自句子转换库的 CrossEncoder 类来加载它们。

MNLI 数据集可从 HuggingFace 数据集库获得,我们应该对 MNLI-m 使用 validation_matched 拆分,对 MNLI-mm 使用 validation_mismatched 拆分。

在 MNLI 上再现 DeBERTa 性能时,有两个注意事项:

  1. 首先,数据集和训练模型中的标签编码不同。这就是为什么如果你马上在 MNLI-m 上给模型打分,你只会得到 31%左右的准确率。
  2. 第二,训练好的模型对句序比较敏感:前提先行,假设次之。

数据集的标签编码:

  • 0 —蕴涵
  • 1-中性
  • 2 —矛盾

已训练模型的标签编码:

  • 0 —矛盾
  • 1-蕴涵
  • 2-中性

当应用上述修复时,该模型在 MNLI-m 数据集上产生了 88.29%的准确度分数。我认为其原因是来自 HuggingFace 的 deberta-base 权重根本没有在 MNLI 数据集上训练过。这也解释了为什么在我的测试中,匹配和不匹配的体裁之间的差异如此之小:88.29%匹配和 88.11%不匹配,而论文报告的匹配和不匹配比例分别为 88.8%和 88.5%。该模型应该在匹配的流派上进行训练,并在这些流派上表现得更好。然而,该模型没有在任何流派上进行训练,因此它以相同的方式在匹配和不匹配的情况下执行,并且在这两种情况下,性能都比论文中报道的差。

让我们来看看重现结果的工作流程。我假设实验是在配有 GPU 的 Colab 笔记本上运行的。

首先,我们需要安装所需的软件包:

!pip install transformers sentence-transformers datasets sentencepiece

接下来,我们加载数据集:

使用 HuggingFace 数据集库加载 MNLI 数据集

请记住,模型和数据集中的标签编码是不兼容的,我们需要定义一个映射函数:

定义一个将模型标签转换为数据集标签的函数

并使用 CrossEncoder 加载模型:

使用 sentence_transformers 库加载预训练的模型权重

最后,我们可以在 validation_matched split 上对模型进行评分,以实现 88.29%的匹配流派:

在 validation_matched 上对模型评分

并在validation _ mismatchedsplit 上对模型进行评分,在不匹配流派上达到 88.11%;

对模型进行评分验证 _ 不匹配

你可以在这里找到一个完整的 Colab 笔记本

结论

我们回顾了 NLI 最常用的数据集和模型。我们还在 MNLI 上复制了 DeBERTa base 的结果。

如果你需要快速解决一个 NLI 任务,使用德伯塔模型。这是 SOTA,可以通过 HuggingFace 获得。如果你需要一个基准,我更喜欢使用 ANLI 或 MNLI。

这几天我特别喜欢 ML 的一点就是变形金刚无处不在。因此,我们审查的模型也适用于 NLI 以外的许多应用任务。反过来也可以——许多基于 transformer 的模型在 NLI 任务上进行了基准测试,以显示与以前的架构相比的性能提升。

自然语言推理是一项重要的任务,它使我们开发出能够实际理解句子之间依赖关系的模型。到目前为止,我们已经介绍了太多关于 NLI 的内容,但是您已经有足够的基础来探索其他模型并理解基准之间的差异。

祝你在 ML 的道路上好运,记得和社区分享你的旅程!

用 Python 实现司法判决的自然语言处理

原文:https://towardsdatascience.com/natural-language-process-for-judicial-sentences-with-python-102064f24372

https://pixabay.com/

第 3 部分:TF-IDF 分析

在本系列的上一篇文章中,我们使用简单的描述性统计数据更深入地研究了我们的数据集。在本文中,我们将使用术语频率(TF) —逆密集频率(IDF)技术对术语的重要性进行更深入的分析。这种技术用于度量一个单词对于文档集合中的一个文档的重要性。

TF-IDF

这种方法背后的思想是找到一种方法来确定一个单词对一个文档的重要性,不仅仅是通过计算它的出现次数,还要考虑集合中其他文档的上下文。

事实上,假设我们有 100 篇关于计算机视觉的科学论文,让我们考虑一个专注于特定卷积神经网络 ResNet 的文档 A。在这个文档中,ResNet 和相关术语的出现频率会很高,这些术语对于这个特定的文档非常重要。然而,也有高频的其他术语,如“图像”、“神经网络”、“像素”,所有术语都与计算机视觉领域相关。然而,这些最后的术语将出现在所有的文档中,所以它们不会出现在我们关注的文档 A 中。

因此,我们说,对于文档 A,术语“ResNet”和“image”将具有高频率。对于文档 A,我们如何告诉模型“ResNet”比“image”更重要?答案在 IDF 分量中,它只不过是一个用来补偿 TF 的权重,计算如下:

所以它是这个词出现的文档数的对数。如果该术语出现在所有文档中(如术语“图像”),IDF 权重将等于 0,因此该术语将被标记为不重要。每当文档中的术语频繁出现(高 TF)但在集合中的许多文档中不出现(IDF 低)时,TF-IDF 得分增加。

所以最后,TF-IDF 的分数是:

产生加权的术语频率,其能够测量术语对于文档集合中的文档的重要性。

为了继续计算这个度量,我将对我的文档进行矢量化处理,使文档中的每个术语都根据其 TF-IDF 得分得到一个权重。具有较高权重分数的术语被认为更重要。

文档矢量化

文档矢量化是自然语言处理任务中文本数据预处理的重要步骤。事实上,通过这种实践,原始文本可以被我们的模型解释为数字表示。出于 TF-IDF 分析的目的,我们将使用术语-文档矩阵的表示。

其想法是创建一个矩阵,其维度分别是文档和单个术语。每个单元格都填充了相应文档中特定术语的频率。

我们来举个例子:

现在让我们进入代码。

用 Python 计算 TF-IDF

我将首先尝试对词干进行矢量化,然后尝试一种使用引理的不太激进的方法(在本系列的第 1 部分中计算)。此外,由于标记为的条目少于 5000 个,我将首先对整个数据集进行分析(以便遵守最少 5000 个单词的限制)。然后,我将对较小的标签文章数据集执行相同的分析,这样我也可以显示每个术语的类别。

所以让我们从最重要的 100 个术语开始(用词干和词条表示):

#first thing first, let's create a df_factor where the labels are one-hot encoded. from sklearn.preprocessing import MultiLabelBinarizermlb = MultiLabelBinarizer()df_factor = df.join(pd.DataFrame(mlb.fit_transform(df['category']),
                          columns=mlb.classes_,
                          index=df.index))#representing Stems: analyzing the most important 100 termssns.set_context("paper")
sns.set()
fig, ax = plt.subplots(figsize = (16, 9))
ax.set_title("Stems TF-IDF (size scaling on TF)", fontsize = 15)
ax.set_ylabel("idf")
ax.set_xlabel("tfidf")
ax.set_xbound(lower = 0, upper = 1000)tfidf_vectorizer = TfidfVectorizer(analyzer = 'word', min_df = 0.2, max_df = 0.6, 
                                       stop_words = 'english', sublinear_tf = True)X = tfidf_vectorizer.fit_transform(df_factor.Stems)count_vectorizer = CountVectorizer(analyzer='word', min_df = 0.2, 
                                       max_df = 0.6, stop_words = 'english')X2 = count_vectorizer.fit_transform(df_factor.Stems)word_counts = X2.toarray()word_tfidf = X.toarray()word_tfidf[word_tfidf < 0.2] = 0df_tfidf = pd.DataFrame(data = {'word': count_vectorizer.get_feature_names(), 
                            'tf': word_counts.sum(axis = 0), 
                            'idf': tfidf_vectorizer.idf_,
                            'tfidf': word_tfidf.sum(axis = 0)})df_tfidf.sort_values(["tfidf", 'tf', 'idf'], ascending = False, inplace = True)
df_tfidf.reset_index(drop = True, inplace = True)

ax.scatter(df_tfidf.tfidf[:100], df_tfidf.idf[:100], s = df_tfidf.tf * 0.14, 
            cmap = "Blues", alpha = 0.7, edgecolors = "black", linewidths = 1.2) #picking the most important 100 termsfor index, text in enumerate(df_tfidf.word[:100].tolist()):
        ax.annotate(text, (df_tfidf.tfidf.tolist()[index], df_tfidf.idf.tolist()[index]), 
                    (df_tfidf.tfidf.tolist()[index] + 0, df_tfidf.idf.tolist()[index] + 0.11))

box = ax.get_position()
ax.set_position([box.x0, box.y0, box.width * 0.8, box.height])

#representing Lemmas: analyzing the most important 100 termssns.set_context("paper")
sns.set()
fig, ax = plt.subplots(figsize = (16, 9))
ax.set_title("Lemmas TF-IDF (size scaling on TF)", fontsize = 15)
ax.set_ylabel("idf")
ax.set_xlabel("tfidf")
ax.set_xbound(lower = 0, upper = 1000)tfidf_vectorizer = TfidfVectorizer(analyzer = 'word', min_df = 0.2, max_df = 0.6, 
                                       stop_words = 'english', sublinear_tf = True)X = tfidf_vectorizer.fit_transform(df_factor.Lemmas)count_vectorizer = CountVectorizer(analyzer='word', min_df = 0.2, 
                                       max_df = 0.6, stop_words = 'english')X2 = count_vectorizer.fit_transform(df_factor.Lemmas)word_counts = X2.toarray()word_tfidf = X.toarray()word_tfidf[word_tfidf < 0.2] = 0df_tfidf = pd.DataFrame(data = {'word': count_vectorizer.get_feature_names(), 
                            'tf': word_counts.sum(axis = 0), 
                            'idf': tfidf_vectorizer.idf_,
                            'tfidf': word_tfidf.sum(axis = 0)})df_tfidf.sort_values(["tfidf", 'tf', 'idf'], ascending = False, inplace = True)
df_tfidf.reset_index(drop = True, inplace = True)

ax.scatter(df_tfidf.tfidf[:100], df_tfidf.idf[:100], s = df_tfidf.tf * 0.14, 
            cmap = "Blues", alpha = 0.7, edgecolors = "black", linewidths = 1.2)for index, text in enumerate(df_tfidf.word[:100].tolist()):
        ax.annotate(text, (df_tfidf.tfidf.tolist()[index], df_tfidf.idf.tolist()[index]), 
                    (df_tfidf.tfidf.tolist()[index] + 0, df_tfidf.idf.tolist()[index] + 0.11))

box = ax.get_position()
ax.set_position([box.x0, box.y0, box.width * 0.8, box.height])

现在让我们只使用带标签的文章,并使用类别作为进一步的变量(颜色=类别)。更具体地说,我们为每个类别选择了前两个最重要的词:

sns.set_context("talk")
sns.set()
fig, ax = plt.subplots(figsize = (16, 9))
ax.set_title("Stem TF-IDF (size scaling on TF)", fontsize = 15)
ax.set_ylabel("idf")
ax.set_xlabel("tfidf")
ax.set_xbound(lower = 0, upper = 1000)
z=categories_listcmap = plt.get_cmap('tab20b')
colors = [cmap(i) for i in np.linspace(0, 1, len(z))]
for category, color in zip(z, colors):
    tfidf_vectorizer = TfidfVectorizer(analyzer = 'word', min_df = 0.2, max_df = 0.6, 
                                       stop_words = 'english', sublinear_tf = True)X = tfidf_vectorizer.fit_transform(df_factor[df_factor[category] == 1].Stems)count_vectorizer = CountVectorizer(analyzer='word', min_df = 0.2, 
                                       max_df = 0.6, stop_words = 'english')X2 = count_vectorizer.fit_transform(df_factor[df_factor[category] == 1].Stems)word_counts = X2.toarray()word_tfidf = X.toarray()word_tfidf[word_tfidf < 0.2] = 0df_tfidf = pd.DataFrame(data = {'word': count_vectorizer.get_feature_names(), 
                            'tf': word_counts.sum(axis = 0), 
                            'idf': tfidf_vectorizer.idf_,
                            'tfidf': word_tfidf.sum(axis = 0)})df_tfidf.sort_values(["tfidf", 'tf', 'idf'], ascending = False, inplace = True)
    df_tfidf.reset_index(drop = True, inplace = True)

    ax.scatter(df_tfidf.tfidf[:2], df_tfidf.idf[:2], s = df_tfidf.tf * 0.14, 
            cmap = "Blues", alpha = 0.7, edgecolors = "black", linewidths = 1.2, color = color, label = category)for index, text in enumerate(df_tfidf.word[:2].tolist()):
        ax.annotate(text, (df_tfidf.tfidf.tolist()[index], df_tfidf.idf.tolist()[index]), 
                    (df_tfidf.tfidf.tolist()[index] + 0, df_tfidf.idf.tolist()[index] + 0.11))

box = ax.get_position()
ax.set_position([box.x0, box.y0, box.width * 0.8, box.height])
ax.legend(title = "Category", frameon = True, ncol = 2, fancybox = True, title_fontsize = 15,
          loc = 'center left', bbox_to_anchor = (1, 0.5), labelspacing = 2.5, borderpad = 2);

#let's do the same also for Lemmassns.set_context("talk")
sns.set()
fig, ax = plt.subplots(figsize = (16, 9))
ax.set_title("Lemmas TF-IDF (size scaling on TF)", fontsize = 15)
ax.set_ylabel("idf")
ax.set_xlabel("tfidf")
ax.set_xbound(lower = 0, upper = 1000)
z=categories_listcmap = plt.get_cmap('tab20b')
colors = [cmap(i) for i in np.linspace(0, 1, len(z))]for category, color in zip(z, colors):
    tfidf_vectorizer = TfidfVectorizer(analyzer = 'word', min_df = 0.2, max_df = 0.6, 
                                       stop_words = 'english', sublinear_tf = True)X = tfidf_vectorizer.fit_transform(df_factor[df_factor[category] == 1].Lemmas)count_vectorizer = CountVectorizer(analyzer='word', min_df = 0.2, 
                                       max_df = 0.6, stop_words = 'english')X2 = count_vectorizer.fit_transform(df_factor[df_factor[category] == 1].Lemmas)word_counts = X2.toarray()word_tfidf = X.toarray()word_tfidf[word_tfidf < 0.2] = 0df_tfidf = pd.DataFrame(data = {'word': count_vectorizer.get_feature_names(), 
                            'tf': word_counts.sum(axis = 0), 
                            'idf': tfidf_vectorizer.idf_,
                            'tfidf': word_tfidf.sum(axis = 0)})df_tfidf.sort_values(["tfidf", 'tf', 'idf'], ascending = False, inplace = True)
    df_tfidf.reset_index(drop = True, inplace = True)

    ax.scatter(df_tfidf.tfidf[:3], df_tfidf.idf[:3], s = df_tfidf.tf * 0.14, 
            cmap = "Blues", alpha = 0.7, edgecolors = "black", linewidths = 1.2, color = color, label = category)for index, text in enumerate(df_tfidf.word[:3].tolist()):
        ax.annotate(text, (df_tfidf.tfidf.tolist()[index], df_tfidf.idf.tolist()[index]), 
                    (df_tfidf.tfidf.tolist()[index] + 0, df_tfidf.idf.tolist()[index] + 0.11))

box = ax.get_position()
ax.set_position([box.x0, box.y0, box.width * 0.8, box.height])
ax.legend(title = "Category", frameon = True, ncol = 2, fancybox = True, title_fontsize = 15,
          loc = 'center left', bbox_to_anchor = (1, 0.5), labelspacing = 2.5, borderpad = 2);

TF-IDF 分析是 NLP 分析中的一个强大工具,并且在我们的场景中是有用的,例如,对于司法判决中的文本挖掘和搜索引擎,它是给定用户查询对文档的相关性进行评分和排序的中心工具

在下一篇文章中,我们将继续进行文本分析和潜在主题分析,敬请关注!😃

参考

用 Python 实现司法判决的自然语言处理

原文:https://towardsdatascience.com/natural-language-process-for-judicial-sentences-with-python-73525bfc6b1e

https://pixabay.com/

第 5 部分:用 LDA 进行主题建模

主题建模是一种无监督的机器学习方法,其目的是识别一组文档中的趋势并对识别这些趋势的单词进行分组。

最终的目标是每个主题都有一个单词分布。事实上,由于它是一种无监督的技术,我们得出了一般的聚类(如“主题 1”、“主题 2”、“主题 N”)而不是标签,因此我们需要某种“意义”来定义标签(并在进一步的监督任务中使用它们)。也就是说,如果与主题 1 最相关的 3 个词是“医疗”、“保健”和“保险”,则属于该群集的可能标签可能是“卫生系统”。

为此,我将使用潜在的狄利克雷分配(LDA),这是一种主题建模技术,它依赖于这样的假设,即每个文档中的每个单词都来自一个主题,并且该主题是从主题的每个文档分布中选择的。更具体地说,我们所讨论的文档中主题的分布和主题中单词的分布就是狄利克雷分布。

让我们用 Python 初始化模型:

df_factor = pd.read_pickle('data/df_factor.pkl')

#this time I will use Lemmas as input

df_factor["Lemmas_string"] = df_factor.Lemmas.apply(str)
instances = df_factor.Lemmas.apply(str.split) 

dictionary = Dictionary(instances) 

dictionary.filter_extremes(no_below=100, no_above=0.1) #this filter_extreme is keeping the dictionaries either happening
                                                        #less than 100 times or more than 10% of the data

#initializing the lda corpus

ldacorpus = [dictionary.doc2bow(text) for text in instances]
tfidfmodel = TfidfModel(ldacorpus)

model_corpus = tfidfmodel[ldacorpus]

len(model_corpus)

Output: 13087

现在出现了一个问题:我如何决定需要多少主题来聚类我的文档集?

这个想法是,我们希望一个主题中的单词彼此连贯,所以我们评估主题最佳数量的方法是给每个分段分配一个所谓的连贯分数

有几种方法可以计算一致性分数,在本文中,有两个指标我可以决定:

  • CV Coherence score →它基于标准化的点态互信息(你可以在这里阅读更多关于 PMI 的信息
  • UMass Coherence score →它衡量两个单词在文档中同时出现的频率。
#these scores are needed to identify the number of topic which is the most consistent.

coherence_values = []

dev_size = 10000
eval_size = len(model_corpus) - dev_size

for num_topics in range(5, 16):
    model = LdaMulticore(corpus=model_corpus[:dev_size], 
                         id2word=dictionary, 
                         num_topics=num_topics)

    coherencemodel_umass = CoherenceModel(model=model, 
                                          texts=instances[dev_size:dev_size+eval_size], 
                                          dictionary=dictionary, 
                                          coherence='u_mass')

    coherencemodel_cv = CoherenceModel(model=model, 
                                       texts=instances[dev_size:dev_size+eval_size], 
                                       dictionary=dictionary, 
                                       coherence='c_v')

    umass_score = coherencemodel_umass.get_coherence()
    cv_score = coherencemodel_cv.get_coherence()

    print(num_topics, umass_score, cv_score)
    coherence_values.append((num_topics, umass_score, cv_score))

#pickling results
with open('data/coherence_values.pkl', 'wb') as f:
       pickle.dump(coherence_values, f)

让我们想象一下:

%matplotlib inline
import matplotlib.pyplot as plt
import seaborn as sns
sns.set_context('poster') # use large font

scores = pd.DataFrame(coherence_values, columns=['num_topics', 'UMass', 'CV'])
fig, ax = plt.subplots(nrows=1, ncols=2, figsize=(20, 10))
scores.plot.line(x='num_topics', y='UMass', ax=ax[0], xticks=range(5,21))
scores.plot.line(x='num_topics', y='CV', ax=ax[1], xticks=range(5,21))

coherence_values[3]
(8, -5.123179828224228, 0.30521192070246617)

对于两种测量方法——UMass 和 CV——我们都想要最高值。因此,我选择 num_topics = 8,因为它在 cv 和 UMass 上都有很高的分数(后者不是最高的,但排名第二)。

 num_topics = 8 # number of topics

# find chunksize to make about 200 updates
num_passes = 10
chunk_size = len(model_corpus) * num_passes/200

model = LdaMulticore(num_topics = num_topics, 
                     corpus = model_corpus,  
                     id2word = dictionary, 
                     workers = min(10, multiprocessing.cpu_count()-1), 
                     passes = num_passes, 
                     chunksize = chunk_size, 
                     alpha = 0.1
                    ) 

一位训练了模型,让我们看看与每个主题相关的前 5 个词:

import re
num_topics = 8
# transform the data into topic distros
topic_corpus = model[model_corpus]

# get the topic descriptors
topic_sep = re.compile(r"0\.[0-9]{3}\*") 
model_topics = [(topic_no, re.sub(topic_sep, '', model_topic).split(' + ')) for topic_no, model_topic in
                model.print_topics(num_topics=num_topics, num_words=5)]

descriptors = []
for i, m in model_topics:
    print(i+1, ", ".join(m[:5]))
    descriptors.append(", ".join(m[:2]).replace('"', ''))

通读这些主题,已经可以看到单词正在引导一个标签。也就是说,主题 8 可以是“气候变化”或“环境污染”。

基于标记数据的 LDA 主题建模

现在,我将在 single_data 数据集上执行 LDA,以便我们也可以可视化跨主题的类别分布。

df = pd.read_pickle('data/df.pkl')
df=df.rename(columns={'topic':'category'})

df_factor = pd.read_pickle('data/df_factor.pkl')

# This dataset will have a column "category" that will report this category.
categories = list(set([i for  l in df['category'].to_list() for i in l]))

data_single = df_factor.copy()[df_factor[categories].sum(axis = 1) == 1]

#len(categories)
# replacing 1 with correspondent category
for column in categories:
    data_single[column] = data_single[column].apply(lambda x: column if x == 1 else "")

data_single["Category"] = data_single[categories].apply(lambda x: "".join(x), axis = 1) # joing labels with whitespace
data_single.head()

#repeating the same steps as before, but this time using a shrunken version of the 
#dataset (only those records with 1 label)

data_single["Lemmas_string"] = data_single.Lemmas.apply(str)
instances = data_single.Lemmas.apply(str.split) 

dictionary = Dictionary(instances) 

dictionary.filter_extremes(no_below=100, no_above=0.1) #this filter_extreme is keeping the dictionaries either happening
                                                        #less than 100 times or more than 10% of the data

ldacorpus = [dictionary.doc2bow(text) for text in instances] #transforms the corpus in IDs
tfidfmodel = TfidfModel(ldacorpus) #computing the tdidf of the corpus based on the document 
model_corpus = tfidfmodel[ldacorpus]

现在让我们训练模型:

num_topics = 8 #derived from the coherence scores

num_passes = 10
chunk_size = len(model_corpus) * num_passes/200

start = time.time() 

model = LdaMulticore(num_topics=num_topics, 
                     corpus = model_corpus, 
                     id2word=dictionary, 
                     workers=min(10, multiprocessing.cpu_count()-1), 
                     passes=num_passes, 
                     chunksize=chunk_size, 
                     alpha = 0.5 
                    ) 

让我们再看一遍与主题相关的单词:

topic_corpus = model[model_corpus]
#get the topic descriptors
topic_sep = re.compile(r"0\.[0-9]{3}\*") 
model_topics = [(topic_no, re.sub(topic_sep, '', model_topic).split(' + ')) for topic_no, model_topic in
                model.print_topics(num_topics=num_topics, num_words=5)]

descriptors = []
for i, m in model_topics:
    print(i+1, ", ".join(m[:5]))
    descriptors.append(", ".join(m[:2]).replace('"', ''))

# get a list of all the topic scores for each document
scores = [[t[1] for t in topic_corpus[entry]] for entry in range(len(topic_corpus))]
topic_distros = pd.DataFrame(data=scores, columns=descriptors)
topic_distros["Category"] = data_single.Category

最后,让我们看一下主题在不同类别中的分布情况:

import matplotlib.pyplot as plt # make graphs
import seaborn as sns # make prettier graphs

sns.set_context('poster') # use large font

fig, ax = plt.subplots(figsize=(50, 20)) #set graph size
#aggregate topics by categories
aggregate_by_category = topic_distros.groupby(topic_distros.Category).mean()
#plot the graph
aggregate_by_category[descriptors].plot.bar(ax=ax)
#to move the legend out
#plt.legend(loc='bottom left', bbox_to_anchor=(1.0, 0.5))

结论

对我们的司法判决运行 LDA 可以降低搜索和检查趋势的复杂性。事实上,我们有 42 个类别(参见第 2 部分了解数据集的描述性统计数据)和仅仅 8 个主题。我们还可以看到,有一些主题(如主题 3 和 5)在所有类别中不断出现,这意味着,无论司法判决的类别如何,这些判决的内容都可能涵盖这些主题。

在下一篇文章中,我们将使用我们的文档集来执行文档嵌入,这是一种矢量化技术,允许我们的语料库成为 ML 模型的输入。所以敬请期待下一部分!

参考

用 Python 实现司法判决的自然语言处理

原文:https://towardsdatascience.com/natural-language-process-for-judicial-sentences-with-python-9462c15c2a64

https://pixabay.com/

第 10 部分:预测

我们到了这一系列文章的最后一部分,在这里我将使用 ML 模型对司法判决进行分类。为此,我将使用已经标记的记录作为训练集来执行监督学习任务,以便将模型应用于那些没有标记的记录。理想情况下,这种模型可以极大地帮助在记录到达官员手中时自动对记录进行分类。

我将深入探究技术:

  • 最频繁基线
  • 逻辑回归
  • 支持向量分类器
  • 带有 Keras 的深度神经网络

让我们首先导入所需的库:

# Libraries we will use in this section
from sklearn.metrics import precision_score, classification_report, f1_score
from sklearn.model_selection import cross_val_score
from sklearn.feature_selection import SelectKBest, chi2

from sklearn.multiclass import OneVsRestClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.svm import LinearSVC

from scipy.sparse import random

import sklearn as skl
skl.warnings.filterwarnings("ignore")

为了有一个基准来比较我们的模型结果,我将首先使用最频繁的基线作为所有文档的预测,这样我们就有了与之相关的性能指标。这个想法是,如果一个模型的性能比最频繁的基线差,它就不值得。

最频繁标签基线

在将 df_factor 分成训练集和测试集之前,我将删除没有主题的行。为此,我将创建一个遮罩并将其应用于 df。

#I will split first the dataset into a test set (20%) and a temporary set (80%). 
#Then, I will split the latter into train set (80% of temporary set) 
#and development set (20% of temporary set)

from sklearn.model_selection import train_test_split

tmp, test = train_test_split(df_factor_label, test_size=0.2, random_state=123) #for replicability
train, dev = train_test_split(tmp, test_size=0.2, random_state=123) #for replicability

vectorizer_logit = TfidfVectorizer(ngram_range = (2,6), min_df = 0.001, max_df = 0.75, stop_words = 'english')

X_train = vectorizer_logit.fit_transform(train.Lemmas)

#we cannot refit the vectorizer
X_dev = vectorizer_logit.transform(dev.Lemmas)
X_test = vectorizer_logit.transform(test.Lemmas)

y_train = train.drop(["titles", "date", "text", "category", "component", "Tokens", "Lemmas", "Stems"], axis = 1)
y_dev = dev.drop(["titles", "date", "text", "category", "component", "Tokens", "Lemmas", "Stems"], axis = 1)
y_test = test.drop(["titles", "date", "text", "category", "component", "Tokens", "Lemmas", "Stems"], axis = 1)

从描述性统计部分,我们知道最常见的类别是“税”。因此,我们将只预测所有观测值的税收作为基线模型。让我们检索该类别的索引。作为一个参考指标,我将主要依靠微观平均 f1 分数,其中微观停留在“计算总的真阳性、假阴性和假阳性。”事实上,如果我们考虑宏观平均指标,我们不会考虑数据不平衡的事实。

ind = y_train.columns.get_loc("Tax")
#print(ind)

most_frequent = np.zeros(42)
most_frequent[ind] = 1
#print(most_frequent)

most_frequent_prediction = [most_frequent for i in range(y_dev.shape[0])]
most_frequent_prediction = pd.DataFrame(most_frequent_prediction, columns = y_dev.columns)

print(classification_report(y_dev, most_frequent_prediction, target_names =  y_dev.columns))

逻辑回归基线

logit = OneVsRestClassifier(LogisticRegression())
logit.fit(X_train, y_train)

让我们将交叉验证应用到培训中(如果你想了解更多关于交叉验证的知识,你可以在这里阅读我以前的文章):

# estimating the (test) F1 score via cross validation
for k in [2, 5, 10]:
    cv = cross_val_score(OneVsRestClassifier(LogisticRegression()), X_train, y = y_train, cv = k, n_jobs = -1, scoring = "f1_micro")
    fold_size = X_train.shape[0]/k

    print("F1 with {} folds for bag-of-words is {}".format(k, cv.mean()))
    print("Training on {} instances/fold, testing on {}".format(round(fold_size*(k-1)), round(fold_size)))
    print()

y_pred_logit_baseline = logit.predict(X_dev)
print(classification_report(y_dev, y_pred_logit_baseline, target_names = y_dev.columns))

尽管“幼稚”,这个模型在最频繁基线的开发集中表现得更好。我们的微观 F1 得分为 54%,低于 16%。

现在,假设我们决定使用这个模型。它在测试数据上的表现如何?

y_pred_logit_baseline_test = logit.predict(X_test)
print(classification_report(y_test, y_pred_logit_baseline_test, target_names = y_test.columns))

正规化强度

现在我将改进我的逻辑模型。

每当我们训练一个模型时,我们都必须考虑方差偏差的权衡和过度拟合的风险:事实上,增加参数的数量总是会导致训练误差的减少,但不会导致测试误差的减少,因为(平方)偏差会减少,但方差会增加。因此,在训练模型时,我们需要考虑当我们增加参数数量时增加损失函数的惩罚项。在逻辑回归中,我们使用参数 c。这个想法是,降低 c 将加强𝜆调节器。的确,c 对 1/𝜆.来说是奇怪的因此,𝜆越高,惩罚项越高,模型的参数就越少。

注:想了解更多关于正规化的内容,可以在这里阅读我之前的文章

from sklearn.metrics import f1_score
best_c = None
best_performance = 0.0

for c in [20, 10, 5, 2, 0.5, 0.1, 0.05, 0.01]:
    print(c)
    classifier_c = OneVsRestClassifier(LogisticRegression(n_jobs=-1, multi_class='auto', solver='lbfgs', 
                                             class_weight='balanced',
                                             C=c
                                     ))
    classifier_c.fit(X_train, y_train)
    predictions_c = classifier_c.predict(X_dev)
    score = f1_score(y_dev, predictions_c, average='micro')
    if score > best_performance:
        best_performance = score
        best_c = c
        print("New best performance: {}".format(score))

    #print(classification_report(y_dev, predictions_c, target_names = y_dev.columns)) 

从上面的结果,我们可以说,最好的模型输出等于 81%的微 f1 分数。让我们看看它在测试集上的表现:

功能选择

让我们把特征的数量减少到 4500 个。我会把这个模型和之前 c 的最佳值的结果结合起来。

from sklearn.feature_selection import SelectKBest, chi2

selector = SelectKBest(chi2, k=4500).fit(X_train, y_train)

X_train_sel = selector.transform(X_train)
X_dev_sel = selector.transform(X_dev)
X_test_sel = selector.transform(X_test)

classifier_sel = OneVsRestClassifier(LogisticRegression(n_jobs=-1, multi_class='auto', solver='lbfgs', 
                                    class_weight='balanced', C = best_c))
classifier_sel.fit(X_train_sel, y_train)

predictions_sel = classifier_sel.predict(X_dev_sel)
print(classification_report(y_dev, predictions_sel, target_names = y_dev.columns))

看来特征选择并没有带来提升(现在微 f1 评分更低,72%)。

#let's also evaluate the model in the test set
predictions_sel_test = classifier_sel.predict(X_test_sel)
print(classification_report(y_test, predictions_sel_test, target_names = y_test.columns))

降维

在这种情况下,我们不在特征中进行选择,而是创建新的低维特征,作为原始特征的线性组合。

from sklearn.decomposition import TruncatedSVD

best_performance=0.0
best_k = None

for k in [300, 500, 1000, 1500,2000, 2500]:
    print(k)
    svd = TruncatedSVD(n_components=k)

    X_train_dim = svd.fit_transform(X_train_sel)
    X_dev_dim = svd.transform(X_dev_sel)
    X_test_dim = svd.transform(X_test_sel)

    classifier_dim = OneVsRestClassifier(LogisticRegression(n_jobs=-1, multi_class='auto', solver='lbfgs', 
                                        class_weight='balanced', C = best_c)) #still including the parameter c
    classifier_dim.fit(X_train_dim, y_train)
    predictions_dim = classifier_dim.predict(X_dev_dim)
    score = f1_score(y_dev, predictions_dim, average='micro')
    if score > best_performance:
        best_performance = score
        best_k = k
        print("New best performance: {}".format(score))

    #print(classification_report(y_dev, predictions_dim, target_names = y_dev.columns))
    print()

它导致模型的最佳性能等于 72%。

支持向量分类器

对于我的第二个分类模型,我决定使用一个支持向量分类器(一对一对全部),像以前一样使用调优参数 C。

svc_vectorizer = TfidfVectorizer(ngram_range=(1, 2), max_df = 0.25, stop_words = 'english')

tmp, test = train_test_split(df_factor_label, test_size=0.2, random_state=123) #for replicability
train, dev = train_test_split(tmp, test_size=0.2, random_state=123) #for replicability

X_train_svc = svc_vectorizer.fit_transform(train.text)
X_dev_svc = svc_vectorizer.transform(dev.text)
X_test_svc = svc_vectorizer.transform(test.text)

y_train = train.drop(["titles", "date", "text", "category", "component", "Tokens", "Lemmas", "Stems"], axis = 1)
y_dev = dev.drop(["titles", "date", "text", "category", "component", "Tokens", "Lemmas", "Stems"], axis = 1)
y_test = test.drop(["titles", "date", "text", "category", "component", "Tokens", "Lemmas", "Stems"], axis = 1)

best_c = None #same ratio as above
best_f1_score = 0.0
for c in [5, 1.0, 0.5]:
    print(c)
    svc_clf = OneVsRestClassifier(LinearSVC(C = c, class_weight = 'balanced')).fit(X_train_svc, y_train)
    cv_reg = cross_val_score(svc_clf, X_train_svc, y = y_train, cv = 5, n_jobs = -1, scoring = "f1_micro")

    new_predictions_regularized = svc_clf.predict(X_dev_svc)
    f1 = f1_score(y_dev, new_predictions_regularized, average='micro')
    print("5-CV on train at C={}: {}".format(c, cv_reg.mean()))
    print(classification_report(y_dev, new_predictions_regularized, target_names = y_dev.columns))
    print()    
    if f1 > best_f1_score:
        best_f1_score = f1
        best_c = c
        #print("New best performance: {}".format(f1))

在这种情况下,最佳性能是 C=1 的那个,微 f1=0.84。

因此,让我们回顾一下所有上述模型的指标(在开发集中):

因此,在所有模型中,我假设最好的是 SVC:这是我要与两个基线进行比较的模型。但是在进入引导部分之前,让我们看看它在测试集上的性能。

lsvc = OneVsRestClassifier(LinearSVC(C = best_c, class_weight = "balanced"))
lsvc.fit(X_train_svc, y_train)

y_pred_svc = lsvc.predict(X_test_svc)
print(classification_report(y_test, y_pred_svc, target_names = y_test.columns))

#finally, let's store the dev predictions to be used in the next section.
best_preds = lsvc.predict(X_dev_svc)

Keras 神经网络

Keras 是一个开源软件库,为 ann(人工神经网络)提供了 Python 接口。它可以运行在 TensorFlow、微软认知工具包、Theano 或 PlaidML 之上。

Keras 的开发是为了让研究人员和开发人员更容易原型化、构建和实验深度学习模型。它具有用户友好的界面,允许您轻松创建和训练各种类型的深度学习模型,包括卷积神经网络(CNN)、递归神经网络(RNNs)和长短期记忆(LSTM)网络。

Keras 的设计是灵活和模块化的,因此您可以轻松地配置、组合和微调您创建的模型。它还具有各种预训练的模型,可以用于一系列任务,如图像分类、自然语言处理和时间序列预测。

为此,我将使用 Keras 构建一个深度神经网络来预测与司法判决相关的标签。

为了简单起见,我将只考虑那些具有单个标签的记录,这样任务将归结为一个多类任务,而不是多类、多标签任务。

import tensorflow as tf
from keras.models import Sequential
from keras.layers import InputLayer, Input
from keras.layers import Reshape, MaxPooling2D
from keras.layers import Conv2D, Dense, Flatten, Activation, Dropout
from keras.optimizers import SGD, RMSprop, Adagrad #want to try different optimizers

#downloading df
df_factor = pd.read_pickle('data/df_factor.pkl')

#creating a df containing only records with one label
m=[len(df_factor.category[i])==1 for i in range(len(df_factor))]
df_factor_single_label=df_factor[m]

import pandas as pd
from sklearn.model_selection import train_test_split

train, test = train_test_split(df_factor_single_label, test_size=0.2, random_state = 123) #here, we don't need a dev set
                                                                                        #since it can be specified direclty
                                                                                        #during training
#for this purpose, I will use the TfIdf vecotrizer.

vectorizer_nn = TfidfVectorizer(ngram_range = (1, 2), min_df = 0.001, max_df = 0.25, stop_words = 'english')

#let's also store the full dataset into a X_nn variable, so that we will be able to plot the training history.

X_nn = vectorizer_nn.fit_transform(df_factor_single_label.text)
X_train_nn = vectorizer_nn.fit_transform(train.text)
X_test_nn = vectorizer_nn.transform(test.text)

y_train = train.drop(["titles", "date", "text", "category", "component", "Tokens", "Lemmas", "Stems"], axis = 1)
y_test = test.drop(["titles", "date", "text", "category", "component", "Tokens", "Lemmas", "Stems"], axis = 1)
y = df_factor_single_label.drop(["titles", "date", "text", "category", "component", "Tokens", "Lemmas", "Stems"], axis = 1)

现在让我们初始化模型:

model = Sequential()
model.add(Dense(3000, activation='relu', input_dim = X_train_nn.shape[1]))
model.add(Dropout(0.1))
model.add(Dense(600, activation='relu'))
model.add(Dropout(0.1))
model.add(Dense(200, activation='relu'))
model.add(Dense(y_train.shape[1], activation='softmax'))

sgd = SGD(lr=0.01, decay=1e-6, momentum=0.9, nesterov=True)
#rms = RMSprop(learning_rate=0.001, rho=0.9)
#adam = Adagrad(learning_rate=0.01)
model.compile(loss='categorical_crossentropy',
              optimizer='adam',
              metrics=['accuracy'])

history = model.fit(X_train_nn, y_train, epochs = 5, batch_size = 100, verbose = 1, validation_split=0.2)

#score = model.evaluate(X_test_nn, y_test, batch_size = 100)
#score

我们最终得到了一个验证/开发准确率为 91.23%的模型,还不错!

from keras.models import load_model

model.save('Keras Models/NN_labels.h5')  # creates a HDF5 file 'NN_labels.h5'

%matplotlib inline
import pandas as pd
import seaborn

df = pd.DataFrame(history.history)
df[['val_accuracy', 'accuracy']].plot.line()
df[['val_loss', 'loss']].plot.line()

#downloading model
from keras.models import load_model

model = load_model('Keras Models/NN_labels.h5')

#using it to predict on new, never-seen-before data.

loss, accuracy = model.evaluate(X_test_nn, y_test, batch_size = 100)

print("test loss: ", loss)
print("test accuracy: ", accuracy)

测试精度相当令人满意(91.3%),因此我相信这个模型能够正确地标记新文章。

有了这个模型,我有信心在新文章被插入到数据库中时自动标记它们。这可能是实现能够解决原始任务(即多类、多标签分类问题)的解决方案的起点。这个模型的一个可能的改进是:训练一组不同的分类器(可以是具有不同结构的神经网络,或者不同的模型,如逻辑回归、SVC 等等)。).然后使用它们来预测文章的测试序列,并且每当模型输出不同的标签时,将它们都归因于该观察/文本。也就是说,可以使用逻辑回归、SVC 和 NN 来预测新文章标签,如果它们都返回不同的结果,则将所有三个标签归属于该文章。

结论

我们到了关于司法判决的 NLP 系列文章的最后一部分。如果尽管我写的东西很无聊,你还是设法来了😃谢谢大家!这对我太重要了。

我总是感谢任何建设性的反馈,所以请随时通过 Medium 或 Linkedin 联系我。

下一篇文章再见!

参考

用 Python 实现司法判决的自然语言处理

原文:https://towardsdatascience.com/natural-language-process-for-judicial-sentences-with-python-a0ec2792b70

https://pixabay.com/

第四部分:矩阵分解的潜在语义

在本文中,我将执行潜在语义(或主题)分析,这是一种通过产生与文档和术语相关的新“概念”(也称为语义或主题)来分析文档和相关术语之间关系的技术。这种方法建立在分布假设的基础上,根据该假设“具有相似分布的语言术语具有相似的含义”。因此,意义相近的术语很可能出现在相似的文本中。

潜在语义分析包括两个主要步骤:

  1. 创建一个 Word 文档矩阵(你可以在这里阅读更多关于文档矢量化的内容
  2. 降低矩阵的维度,以产生新的变量(这将是我们的语义或主题)。

我会进行两种矩阵分解技术:奇异值分解非负矩阵分解。他们都依赖于一个假设,即在高维特征空间中,实际上可能需要更少的维度来解释数据中的变化(你可以在这里阅读更多关于维度减少的概念→https://towardsdatascience . com/PCA-intrinsic vectors-and-enforcern-1 f 968 BC 6777 a)。

这两种方法的主要区别在于,SVD 将文档 X 的矢量化矩阵分解成 3 个低维矩阵,而 NMF 只用 2 个矩阵就完成了分解。

让我们用 Python 来实现它们。

二维分析

#create the original matrix X (term-document matrix), vectorized with tf-idf weights.

documents = df_factor.Tokens.apply(str).tolist()
tfidf_vectorizer = TfidfVectorizer(ngram_range=(1,2), stop_words='english', analyzer='word', 
                                   min_df=0.001, max_df=0.5, sublinear_tf=True, use_idf=True)
X = tfidf_vectorizer.fit_transform(documents)

from sklearn.decomposition import TruncatedSVD #svd

# set number of latent components
k = 10

svd = TruncatedSVD(n_components=k)
%time U = svd.fit_transform(X)
S = svd.singular_values_
V = svd.components_

from sklearn.decomposition import NMF #nmf

nmf = NMF(n_components=k, init='nndsvd', random_state=0)

%time W = nmf.fit_transform(X)
H = nmf.components_

print("SVD matrices shapes: ", U.shape, S.shape, V.shape)
print("NMF matrices shapes: ",W.shape, H.shape)

Wall time: 2.5 s
Wall time: 11.4 s
SVD matrices shapes:  (13087, 10) (10,) (10, 40808)
NMF matrices shapes:  (13087, 10) (10, 40808)

import numpy as np
def show_topics(A, vocabulary, topn=5):
    """
    find the top N words for each of the latent dimensions (=rows) in a
    """
    topic_words = ([[vocabulary[i] for i in np.argsort(t)[:-topn-1:-1]]
                    for t in A])
    return [', '.join(t) for t in topic_words] 

现在,让我们打印矢量化文档矩阵中的顶级术语,用 TF-IDF 分数进行加权(您可以在前面的部分这里中阅读关于该分数的更多信息)。

#SVD
terms = tfidf_vectorizer.get_feature_names()

sorted(show_topics(V, terms))
['antitrust, antitrust division, bid, rigging, bid rigging',
 'child, criminal division, safe childhood, project safe, childhood',
 'child, safe childhood, project safe, childhood, exploitation',
 'epa, environmental, clean, environment, natural',
 'injunction, customers, complaint, preparing, preparers',
 'medicare, hhs, health, health care, care',
 'osc, ina, immigration, citizenship, discrimination provision',
 'rights, civil rights, rights division, discrimination, employment',
 'tax, fraud, false, prison, irs',
 'tax, returns, irs, tax returns, tax division']
#NMF
sorted(show_topics(H, terms))
['antitrust, antitrust division, bid, rigging, bid rigging',
 'child, safe childhood, project safe, childhood, exploitation',
 'epa, environmental, clean, environment, natural',
 'false claims, claims act, claims, civil division, health',
 'fbi, indictment, police, security, law',
 'medicare, hhs, health, health care, care',
 'osc, ina, employment, citizenship, anti discrimination',
 'rights, civil rights, rights division, civil, discrimination',
 'tax, irs, tax division, returns, tax returns',
 'tax, returns, customers, injunction, tax returns']

现在让我们画出低维项矩阵。

#Initializing a plotting function

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import colors
import seaborn as sns

sns.set_context('notebook')

def plot_vectors(vectors, V, title='VIZ', labels=None, dimensions=3):
    """
    plot the vectors in 2 or 3 dimensions. 
    If labels are supplied, use them to color the data accordingly
    """
    # set up graph
    fig = plt.figure(figsize=(10,10))

    # create data frame
    df = pd.DataFrame(data={'x':vectors[:,0], 'y': vectors[:,1]})
    # add labels, if supplied
    if labels is not None:
        df['label'] = labels
    else:
        df['label'] = [''] * len(df)

    # assign colors to labels
    cm = plt.get_cmap('tab20b') # choose the color palette
    n_labels = len(df.label.unique())
    label_colors = [cm(1\. * i/n_labels) for i in range(n_labels)]
    cMap = colors.ListedColormap(label_colors)

    # plot in 3 dimensions
    if dimensions == 3:
        # add z-axis information
        df['z'] = vectors[:,2]
        # define plot
        ax = fig.add_subplot(111, projection='3d')
        frame1 = plt.gca() 
        # remove axis ticks
        frame1.axes.xaxis.set_ticklabels([])
        frame1.axes.yaxis.set_ticklabels([])
        frame1.axes.zaxis.set_ticklabels([])

        # plot each label as scatter plot in its own color
        for l, label in enumerate(df.label.unique()):
            df2 = df[df.label == label]
            color_values = [label_colors[l]] * len(df2)
            ax.scatter(df2['x'], df2['y'], df2['z'], 
                       c=color_values, 
                       cmap=cMap, 
                       edgecolor=None, 
                       label=label, 
                       alpha=0.4, 
                       s=100)

        topics = sorted(show_topics(V.components_, tfidf_vectorizer.get_feature_names()))
        print(topics)
        frame1.axes.set_xlabel(topics[0])
        frame1.axes.set_ylabel(topics[1])
        frame1.axes.set_zlabel(topics[2])

    # plot in 2 dimensions
    elif dimensions == 2:
        ax = fig.add_subplot(111)
        frame1 = plt.gca() 
        frame1.axes.xaxis.set_ticklabels([])
        frame1.axes.yaxis.set_ticklabels([])

        for l, label in enumerate(df.label.unique()):
            df2 = df[df.label == label]
            color_values = [label_colors[l]] * len(df2)
            ax.scatter(df2['x'], df2['y'], 
                       c=color_values, 
                       cmap=cMap, 
                       edgecolor=None, 
                       label=label, 
                       alpha=0.4, 
                       s=100)
        topics = sorted(show_topics(V.components_, tfidf_vectorizer.get_feature_names()))
        print(topics)
        frame1.axes.set_xlabel(topics[0])
        frame1.axes.set_ylabel(topics[1])

    else:
        raise NotImplementedError()
    plt.legend(ncol = 5, loc = "upper left", frameon = True, fancybox = True)
    ax.legend(frameon = True, ncol = 2, fancybox = True, title_fontsize = 15,
              loc = 'center left', bbox_to_anchor = (1, 0.5), labelspacing = 2.5, borderpad = 2)
    plt.title(title)
#     plt.legend()
    plt.show() 
# now let's perform the same computations with 2 and 3 dimensions, so that we can visualize them. Let's start with 2 dims.

low_dim_svd = TruncatedSVD(n_components = 2)
low_dim_U = low_dim_svd.fit_transform(X)
sorted(show_topics(low_dim_svd.components_, tfidf_vectorizer.get_feature_names()))
['medicare, hhs, health, health care, care', 'tax, fraud, false, prison, irs']
low_dim_nmf = NMF(n_components=2, init='nndsvd')
low_dim_W = low_dim_nmf.fit_transform(X)
sorted(show_topics(low_dim_nmf.components_, tfidf_vectorizer.get_feature_names()))
['medicare, health, health care, care, hhs',
 'tax, irs, returns, prison, tax division']

这两种方法的结果似乎是一致的。让我们来看看策划:

plot_vectors(low_dim_U, low_dim_svd, title = 'SVD 2d', dimensions=2)

plot_vectors(low_dim_W, low_dim_nmf, title = 'NMF 2d', dimensions=2)

现在我想进行同样的分析,但是也要考虑我的文章的标签(或类别)。

#creating a df with records with only one label
data_single = df_factor.copy()[df_factor[categories].sum(axis = 1) == 1]

documents = data_single.text.apply(str).tolist()
tfidf_vectorizer = TfidfVectorizer(ngram_range=(1,2), stop_words='english', analyzer='word', 
                                   min_df=0.001, max_df=0.5, sublinear_tf=True, use_idf=True)
X = tfidf_vectorizer.fit_transform(documents)

from sklearn.decomposition import TruncatedSVD # this also works with sparse matrices
labels = [i[0] for i in data_single.category] #each component is a list, I want a list of elements not a list of lists
# set number of latent components
k = 10

svd = TruncatedSVD(n_components=k)
%time U = svd.fit_transform(X)
S = svd.singular_values_
V = svd.components_

from sklearn.decomposition import NMF

nmf = NMF(n_components=k, init='nndsvd', random_state=0)

%time W = nmf.fit_transform(X)
H = nmf.components_

对于这种分析,我将只依靠 NMF 方法。事实上,我注意到用 SVD 提取的主题非常相似:

low_dim_svd = TruncatedSVD(n_components = 2)
low_dim_U = low_dim_svd.fit_transform(X)
sorted(show_topics(low_dim_svd.components_, tfidf_vectorizer.get_feature_names()))

['tax, irs, tax division, fraud, returns',
 'tax, returns, tax division, irs, tax returns']

为了让潜在的主题覆盖尽可能多的信息,我不希望两个或更多的组件带来相同的信息,因此是多余的。

low_dim_nmf = NMF(n_components=2, init='nndsvd')
low_dim_W = low_dim_nmf.fit_transform(X)
plot_vectors(low_dim_W, low_dim_nmf, labels = labels, title = 'NMF 2d', dimensions=2)

三维分析

现在让我们做同样的三维。同样,在这种情况下,我将只依靠 NMF 方法。事实上,在查看 SVD 方法的第二个和第三个潜在主题(或组件)时,我注意到它们非常相似:

['medicare, hhs, health, health care, care',
 'tax, fraud, false, prison, irs',
 'tax, returns, irs, tax returns, tax division']

因此,由于上面解释的同样的原因,我将不再继续 SVD 方法。

因此,让我们继续创建 3D 矩阵并绘制结果:

low_dim_nmf = NMF(n_components=3, init='nndsvd')
low_dim_W = low_dim_nmf.fit_transform(X)

plot_vectors(low_dim_W, low_dim_nmf, title = 'NMF 3d', dimensions=3)

同样通过 3d 分析,我想通过考虑我的文章的标签(或类别)来为我的图添加信息。

 low_dim_nmf = NMF(n_components=3, init='nndsvd')
low_dim_W = low_dim_nmf.fit_transform(X)
sorted(show_topics(low_dim_nmf.components_, tfidf_vectorizer.get_feature_names()))
plot_vectors(low_dim_W, low_dim_nmf, labels = labels, title = 'NMF 3d', dimensions=3)

结论

从上面的分析来看,我们的方法似乎能够将属于具有相同标签的文档的单词聚类到相同的潜在主题中。这可能有助于搜索引擎或自动司法判决分类,并且通常有助于在司法知识库中导航。

在下一篇文章中,将继续处理主题和语义,所以请继续关注第 5 部分!

参考

用 Python 实现司法判决的自然语言处理

原文:https://towardsdatascience.com/natural-language-process-for-judicial-sentences-with-python-ae5ea52b540d

https://pixabay.com/

第 7 部分:聚类分析

在这篇文章中,我将对我们的司法判决进行一些进一步的分析和视觉呈现。我将重点介绍聚类分析,这是一种无监督的学习技术,即使我们对它了解不多,它也非常有助于发现数据中的模式。在 NLP 中,它可以有多种应用:

  • 识别推文中对话的共同趋势
  • 对来自一个国家不同地区的文本进行分组(每个地区有不同的方言)
  • 根据电影涉及的类型对电影评论进行分组

诸如此类。无监督学习的好处是并不总是清楚聚类的含义:这取决于用户根据分析的结果来推导。例如,在我们的研究中,分组可能反映不同的干预领域,或者可能确定宣布判决的司法区域。

为了进行聚类分析,我将使用 K-means 算法。

该算法的第一步是在我们未标记的观测值中,创建随机定位的 c 新观测值,称为“质心”。质心的数量将代表簇的数量(我们将在后面看到如何确定这个数量)。现在,将开始一个迭代过程,由两个步骤组成:

  • 首先,对于每个质心,该算法找到离该质心最近的点(根据通常计算为欧几里德距离的距离),并将它们分配到其类别中;
  • 其次,对于每个类别(由一个质心表示),该算法计算属于该类别的所有点的平均值。该计算的输出将是该类的新质心。

每次重复该过程时,最初与一个质心分类在一起的一些观察结果可能被重定向到另一个质心。此外,在几次重复之后,质心位置的变化应该越来越不重要,因为初始随机质心与真实质心收敛。当质心位置不再变化时,该过程结束。

作者图片

如果你有兴趣深入研究聚类无监督技术,你可以在这里阅读我以前的文章。

现在让我们用 Python 实现它。

让我们开始定义一个函数,稍后绘制我们的聚类结果。

#initializing a plotting function

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import colors
import seaborn as sns

sns.set_context('notebook')

def plot_vectors(vectors, title='VIZ', labels=None, dimensions=3):
    """
    plot the vectors in 2 or 3 dimensions. 
    If labels are supplied, use them to color the data accordingly
    """
    # set up graph
    fig = plt.figure(figsize=(10,10))

    # create data frame
    df = pd.DataFrame(data={'x':vectors[:,0], 'y': vectors[:,1]})
    # add labels, if supplied
    if labels is not None:
        df['label'] = labels
    else:
        df['label'] = [''] * len(df)

    # assign colors to labels
    cm = plt.get_cmap('afmhot') # choose the color palette
    n_labels = len(df.label.unique())
    label_colors = [cm(1\. * i/n_labels) for i in range(n_labels)]
    cMap = colors.ListedColormap(label_colors)

    # plot in 3 dimensions
    if dimensions == 3:
        # add z-axis information
        df['z'] = vectors[:,2]
        # define plot
        ax = fig.add_subplot(111, projection='3d')
        frame1 = plt.gca() 
        # remove axis ticks
        frame1.axes.xaxis.set_ticklabels([])
        frame1.axes.yaxis.set_ticklabels([])
        frame1.axes.zaxis.set_ticklabels([])

        # plot each label as scatter plot in its own color
        for l, label in enumerate(df.label.unique()):
            df2 = df[df.label == label]
            color_values = [label_colors[l]] * len(df2)
            ax.scatter(df2['x'], df2['y'], df2['z'], 
                       c=color_values, 
                       cmap=cMap, 
                       edgecolor=None, 
                       label=label, 
                       alpha=0.4, 
                       s=100)

    # plot in 2 dimensions
    elif dimensions == 2:
        ax = fig.add_subplot(111)
        frame1 = plt.gca() 
        frame1.axes.xaxis.set_ticklabels([])
        frame1.axes.yaxis.set_ticklabels([])

        for l, label in enumerate(df.label.unique()):
            df2 = df[df.label == label]
            color_values = [label_colors[l]] * len(df2)
            ax.scatter(df2['x'], df2['y'], 
                       c=color_values, 
                       cmap=cMap, 
                       edgecolor=None, 
                       label=label, 
                       alpha=0.4, 
                       s=100)

    else:
        raise NotImplementedError()

    plt.title(title)
#     plt.legend()
    plt.show()

那我们就准备数据吧。对于我的文档的数字表示,我将使用 TF-IDF 矩阵表示(您可以在第 3 部分中阅读更多关于该技术的内容)。

#downloading data
df_factor = pd.read_pickle('data/df_factor.pkl')

documents = df_factor.text.apply(str).tolist()
tfidf_vectorizer = TfidfVectorizer(ngram_range=(1,2), stop_words='english', analyzer='word', 
                                   min_df=0.001, max_df=0.5, sublinear_tf=True, use_idf=True)
X = tfidf_vectorizer.fit_transform(documents)

现在让我们进入集群步骤。我将首先用任意数量的质心训练 K-means 模型,然后我将介绍识别它们的适当数量的技术。

from sklearn.cluster import KMeans, AgglomerativeClustering

k = 16 #just to try, not the optimal number of clusters

# reduce the dimensionality of the input, to speed up clustering
X2 = TruncatedSVD(n_components=300).fit_transform(X)

agg = AgglomerativeClustering(n_clusters=k)

agg_ids = agg.fit_predict(X2)

centroids = np.array([X2[agg_ids == c].mean(axis=0) for c in range(k)])
#print(centroids.shape)

让我们训练模型并使其适合我们的 TF-IDF 截断矩阵:

km = KMeans(n_clusters=k, 
            n_jobs=-1, 
            init=centroids)

km.fit(X2)

plot_vectors(X2, labels=km.labels_)

作者图片

现在,为了进行更有意义的分析,我想确定聚类(或质心)的最佳数量。为此,我将使用轮廓得分技术。

对于每个数据点或观察值以及每个固定数量的聚类,该分数由两部分组成:

  • 该观测值与同一聚类内所有其他观测值之间的平均距离(表示为 a
  • 该观测值与下一个最近簇的所有其他观测值之间的平均距离(表示为 b

如公式所示,分子越大,用该数量的质心完成的聚类越多。

这个想法是,我们训练一个增量 K-means 模型,其质心数量不断增加,我们为每个质心计算轮廓得分。导致具有最高轮廓分数的模型的质心的数量被假定为最佳数量。

在我的分析中,我训练了 10 个质心在 15 到 25 之间的模型。

from sklearn.metrics import silhouette_score

silhouettes = []

K = range(15, 26)

for c in K:
    agg_clustering = AgglomerativeClustering(n_clusters=c)

    agg_cluster_ids = agg_clustering.fit_predict(X2)
    score = silhouette_score(X2, agg_cluster_ids)
    silhouettes.append(score)
    print(c, score)

fig, ax = plt.subplots(figsize=(20,10))
plt.plot(K, silhouettes, 'bx-')
plt.xlabel('k')
plt.ylabel('Silhouette score')
plt.title('Silhouette Method For Optimal k')
plt.show()

作者图片

从上面的图中,似乎最一致的聚类数是 k=24,因为它具有最高的分数。如果添加更多的集群,它可能会更高,但是,我不想让我的数据过多,所以我将坚持这个数字。

from sklearn.cluster import KMeans, AgglomerativeClustering

k = 24

# reduce the dimensionality of the input, to speed up clustering
X2 = TruncatedSVD(n_components=300).fit_transform(X)

agg = AgglomerativeClustering(n_clusters=k)
agg_ids = agg.fit_predict(X2)

centroids = np.array([X2[agg_ids == c].mean(axis=0) for c in range(k)])
print(centroids.shape)

km = KMeans(n_clusters=k, 
            n_jobs=-1, 
            init=centroids)

# fit it on the full 300-dimensional data set
km.fit(X2)

plot_vectors(X2, labels=km.labels_)

作者图片

如上所述,没有预先向用户给出聚类的含义。我们有责任进一步研究模型发现的模式,并将它们与我们拥有的进一步信息联系起来。

希望你喜欢阅读!敬请期待下一部分:)

参考

用 Python 实现司法判决的自然语言处理

原文:https://towardsdatascience.com/natural-language-process-for-judicial-sentences-with-python-bb60a6d3cc0b

https://pixabay.com/

第 6 部分:文档嵌入

在本系列的最新文章中,我们经常提到,为了用作数学模型的输入(许多 NLP 应用程序的最终目标),文本数据需要以某种方式转换成数字表示。我们已经看到了其中的一些,如 TF-IDF 矩阵或词袋。然而,在捕捉单词或文档的含义时,这些技术并不“有用”。

这就是文档嵌入进入游戏的时候。文档嵌入的一般思想是以这样一种方式用向量表示文档,即一旦在向量空间中表示,向量之间的数学距离就表示它们所表示的文档之间的相似性。

在深入研究文档嵌入及其在我们分析中的应用之前,我们首先需要介绍它的起源,可以追溯到单词嵌入的更细粒度的概念。

单词嵌入

使用单词嵌入技术,我们将向量定义为单词的数字表示,这样,具有相似含义的单词具有相似的向量表示。

这个想法是,我们需要用数字来表示单词的意思,这样我们就可以进行如下活动:

  • ML 模型训练
  • 单词所在语料库中潜在模式的可视化
  • 预测所分析单词周围的单词

和许多其他人。

一种流行的单词嵌入技术是 Word2Vec,在 2013 年的这篇论文中介绍。Word2Vec 旨在将单词的含义(包括同义词、类比等)捕获到矢量表示中。这背后的思想是,例如,在向量空间中,“巴黎”的数字表示应该比“伦敦”的数字表示更接近“法国”的数字表示。

Word2Vec 基于两种算法:

  • 连续单词包(CBOW)→它使用滑动窗口预测给定上下文的单词,即前一个和下一个单词。
  • Skip-gram →它以相反的方向工作,使用一个单词作为输入来预测它周围的上下文。尽管它比 CBOW 慢,但在处理生僻字时效果更好。

来源:https://arxiv.org/pdf/1301.3781.pdf

单词嵌入对于许多上下文来说肯定是有用的,但是,这可能还不够。事实上,可能有一些与单词相关的含义不仅严格依赖于它们的上一个或下一个单词,而且依赖于它们所引用的整个文档。也就是说,Word2Vec 算法可能很难捕捉到这样一个事实,即两个同音异义词在不同的文档中可能有非常不同的含义(取决于它们的上下文)。

这就是引入文档嵌入的原因。对于文档嵌入,我们有一个单词嵌入的离散近似,它有助于捕捉整个上下文,并旨在给出更有意义的向量表示。最流行的算法之一是 Doc2Vec,由 Quoc Le 和 Tomas Mikolov 于 2014 年在这篇文章中介绍。

文档嵌入

Doc2Vec 模型用于创建一组单词的矢量化表示,这些单词作为一个整体,而不是单个单词。

至于 Word2Vec,Doc2Vec 算法有两种变体:

  • 分布式内存模型(DM)→类似于 Word2Vec 算法中的 CBOW 变体,DM 模型在其单词输入中还结合了一个段落矩阵,该矩阵可以被认为是另一个单词。“分布式记忆”这个名称表明了这样一个事实,即段落矩阵充当记忆单个单词不能捕捉的当前上下文中缺少的内容的记忆。

来源:https://arxiv.org/pdf/1405.4053v2.pdf

  • 分布式单词包(DBOW)→类似于 Word2Vec 的 skip-gram 变体,但它不是使用单个单词来预测其周围的上下文,而是使用段落矩阵(与上面解释的概念相同)。

来源:https://arxiv.org/pdf/1405.4053v2.pdf

用 Python 实现

我将使用gensim中可用的Doc2Vec模块。更具体地说,我将使用 DBOW 变体。

另外,出于可视化的目的,我将通过 t-SNE 算法来表示嵌入的文档。

因此,让我们开始创建文档矩阵 X,我们将在其上训练 Doc2Vec 算法。

df_factor = pd.read_pickle('data/df_factor.pkl')

from sklearn.feature_extraction.text import HashingVectorizer, TfidfVectorizer
documents = df_factor.Tokens.apply(str).tolist()
# hash vectorizer instance
hvec = HashingVectorizer(lowercase=False, analyzer=lambda l:l, n_features=2**12)

# features matrix X
X = hvec.fit_transform(documents)

让我们导入所有模块并训练我们的算法:

from gensim.models import Doc2Vec
from gensim.models.doc2vec import FAST_VERSION
from gensim.models.doc2vec import TaggedDocument

corpus = []
for docid, document in enumerate(documents):
    corpus.append(TaggedDocument(document.split(), tags=["{0:0>4}".format(docid)]))

d2v_model = Doc2Vec(size=300,window=5,hs=0,sample=0.000001,negative=5, min_count=10, 
                    workers=-1, iter=5000, dm=0, dbow_words=1)

d2v_model.build_vocab(corpus)

d2v_model.train(corpus, total_examples=d2v_model.corpus_count, epochs=d2v_model.epochs)

让我们检索与文档“0001”最相似的 5 个文档:

target_doc = '0001'
# retrieve the 5 most similar documents
d2v_model.docvecs.most_similar(target_doc, topn=5)

每个数组代表(target_doc,相似性得分)。

现在我还想可视化我的矢量化文档。为此,我将使用T-分布式随机邻居嵌入(t-SNE) 算法,这是一种非线性降维技术(在这里阅读关于降维的更多信息)。

#t-SNE embedding algorithm

from sklearn.model_selection import train_test_split

# test set size of 20% of the data and the random seed 123 for replicability
X_train, X_test = train_test_split(X.toarray(), test_size=0.2, random_state=123)

print("X_train size:", len(X_train))
print("X_test size:", len(X_test), "\n")

from sklearn.manifold import TSNE

tsne = TSNE(verbose=1, perplexity=5)
X_embedded = tsne.fit_transform(X_train)

让我们导入可视化软件包来绘制缩减的维度:

from matplotlib import pyplot as plt
import seaborn as sns

# sns settings
sns.set(rc={'figure.figsize':(15,15)})

# colors
palette = sns.color_palette("bright", 1)

# plot
sns.scatterplot(X_embedded[:,0], X_embedded[:,1], palette=palette)

plt.title("t-SNE Press Articles")
# plt.savefig("plots/t-sne_covid19.png")
plt.show()

在这张图片的边缘有一些有趣的集群,这意味着这些文档之间可能有一些共同的讨论趋势。用潜在主题分析来扩展这种分析也可能是有用的,以查看那些聚类是否实际上代表潜在主题。

在下一篇文章中,我们将进一步研究低维数据,以执行聚类分析。我们还将通过命名实体来查看文章的词汇组成。

所以请继续关注第 7 部分!

参考

用 Python 实现司法判决的自然语言处理

原文:https://towardsdatascience.com/natural-language-process-for-judicial-sentences-with-python-e6a01e30a675

https://pixabay.com/

第 9 部分:无监督情感分析

情感分析是一种自然语言处理技术,涉及使用机器学习算法从文本数据中识别和提取主观信息。它通常用于确定一篇文章的整体情绪,无论是积极的、消极的还是中性的。这对于各种应用程序都很有用,例如分析客户反馈、检测社交媒体帖子的情绪或识别电影评论的情绪。情感分析算法通常使用自然语言处理技术和机器学习算法的组合来处理和分析文本数据,并且可以被训练来识别各种类型的情感。

所用模型的介绍

在我们的场景中,我想分析文章的情感是否取决于它们的类别。由于文章没有与其情感对应的标签,我将使用一个名为 VADER 的预训练模型进行无监督分析,该模型可从 NLTK Python 库中获得。第一个单元格可能需要一段时间,所以您可以直接跳到突出显示的 markdown 开始运行代码并可视化结果。

VADER 属于一种依赖于情感相关词汇的情感分析。因此,该模型是在具有相关分数(取决于它是正/+ve、中性还是负/-ve)的词典语料库上训练的。

import nltk
from nltk.sentiment.vader import SentimentIntensityAnalyzer
nltk.download('vader_lexicon')
sia = SentimentIntensityAnalyzer()

[….]

[….]

一旦输入一个完整的句子,该模型使用函数“polarity_score”返回 4 个分数:前三个,+ve,neutral 和-ve,表示属于这些分类的内容的范围。第四个是复合分数,在-1 和 1 之间归一化的词汇等级的总量。

raw_text = "this cake looks delicious, I would love to eat it"
sia.polarity_scores(raw_text)

司法判决的情感分析

现在让我们将 VADER 模式用于我们的司法判决:

 import nltk
from nltk.sentiment.vader import SentimentIntensityAnalyzer 
df_sentiment=df.copy()

sia = SentimentIntensityAnalyzer()
sentiment=[]
for i in range(len(df)):
     ss = sia.polarity_scores(df.Lemmas[i])
     sentiment.append(ss)

import pickle
pickle.dump(sentiment, open("data/sentiment.pkl", 'wb'))

让我们用复合分数向数据集添加一列:

#now we can add a column to our dataset to establish whether the release was positive, neutral or negative.
compound=[sentiment[i]['compound'] for i in range(len(sentiment))]
df_sentiment['compound']=compound
df_sentiment.head()

现在我在联想:

  • 化合物大于 0.05 的任何句子的正标签
  • 任何复合值低于-0.05 的句子的否定标记
  • 任何含有-0.05 到 0.05 之间的复合词的句子的中性标签
#let's attribute a label 
sent=[]
for i in compound:
    if i<-0.05:
        sent.append('negative')
    elif i>0.05:
        sent.append('positive')
    else:
        sent.append('neutral')

df_sentiment['sentiment']=sent

#storing results
df_sentiment.to_pickle('data/df_sentiment.pkl')
df_sentiment.head()

可视化结果

由于我想在一个低维空间中可视化标记的句子,我将从 TF-IDF 矩阵开始应用奇异值分解(您可以在本系列的前几部分中了解关于 SVD 和 TF-IDF 的更多信息):

#Now let's visualize it via Singular Value Decomposition

documents = df_sentiment.Lemmas.apply(str).tolist()
tfidf_vectorizer = TfidfVectorizer(ngram_range=(1,2), stop_words='english', analyzer='word', min_df=0.001, max_df=0.5, sublinear_tf=True, use_idf=True)
X = tfidf_vectorizer.fit_transform(documents)
print(X.shape)

k = 2
svd = TruncatedSVD(n_components=k)
U = svd.fit_transform(X)
S = svd.singular_values_
V = svd.components_

print(U.shape, S.shape, V.shape)
(13087, 28314)
(13087, 2) (2,) (2, 28314)

现在让我们导入可视化库并初始化一个绘图函数:

import seaborn as sns
import matplotlib.pyplot as plt
from matplotlib import colors
def show_topics(A, vocabulary, topn = 5):
    """
    find the top N words for each of the latent dimensions (=rows) in a
    """
    topic_words = ([[vocabulary[i] for i in np.argsort(t)[:-topn-1:-1]]
                    for t in A])
    return [', '.join(t) for t in topic_words]

def plot_vectors(vectors, title = 'VIZ', labels = None, dimensions = 3, low_dim = None):
    sns.set_context('poster')
    """
    plot the vectors in 2 or 3 dimensions. If supplied, color them according to the labels
    """
    # set up graph
    fig = plt.figure(figsize = (12, 12))

    # create data frame
    df = pd.DataFrame(data = {'x': vectors[:, 0], 'y': vectors[:, 1]})
    # add labels, if supplied
    if labels is not None:
        df['label'] = labels
    else:
        df['label'] = [''] * len(df)

    # assign colors to labels
    cm = plt.get_cmap('tab20b') # choose the color palette
    n_labels = len(df.label.unique())
    label_colors = [cm(1\. * i/n_labels) for i in range(n_labels)]
    cMap = colors.ListedColormap(label_colors)

    # plot in 3 dimensions
    if dimensions == 3:
        sns.set_style("white")
        # add z-axis information
        df['z'] = vectors[:,2]
        # define plot
        ax = fig.add_subplot(111, projection='3d')
        frame1 = plt.gca() 
        # remove axis ticks
        frame1.axes.xaxis.set_ticklabels([])
        frame1.axes.yaxis.set_ticklabels([])
        frame1.axes.zaxis.set_ticklabels([])

        if low_dim != None:
            labels = sorted(show_topics(low_dim_svd.components_, tfidf_vectorizer.get_feature_names()))
            frame1.axes.set_xlabel(labels[0])
            frame1.axes.set_ylabel(labels[1])
            frame1.axes.set_zlabel(labels[2])

        # plot each label as scatter plot in its own color
        for l, label in enumerate(df.label.unique()):
            df2 = df[df.label == label]
            ax.scatter(df2['x'], df2['y'], df2['z'], c = label_colors[l], cmap = cMap, edgecolor = None, label = label, alpha = 0.5, s = 100)

    # plot in 2 dimensions
    elif dimensions == 2:
        sns.set()
        ax = fig.add_subplot(111)
        frame1 = plt.gca() 
        frame1.axes.xaxis.set_ticklabels([])
        frame1.axes.yaxis.set_ticklabels([])

        if low_dim != None:
            labels = sorted(show_topics(low_dim_svd.components_, tfidf_vectorizer.get_feature_names()))
            frame1.axes.set_xlabel(labels[0])
            frame1.axes.set_ylabel(labels[1])

        for l, label in enumerate(df.label.unique()):
            df2 = df[df.label == label]
            ax.scatter(df2['x'], df2['y'], c = label_colors[l], cmap = cMap, edgecolor = None, label = label, alpha = 0.5, s = 100)

    else:
        raise NotImplementedError()

    plt.legend(ncol = 5, loc = "upper left", frameon = True, fancybox = True)
    ax.legend(frameon = True, ncol = 2, fancybox = True, title_fontsize = 15,
          loc = 'center left', bbox_to_anchor = (1, 0.5), labelspacing = 2.5, borderpad = 2);
    plt.title(title)
    plt.show()

让我们画出结果:

low_dim_svd = TruncatedSVD(n_components = 3)
categories = df_sentiment.sentiment

low_dim_U = low_dim_svd.fit_transform(X)

plot_vectors(low_dim_U, title = "SVD", labels = categories, dimensions = 3, low_dim = low_dim_svd)

现在让我们想象一下,对于每个类别,它的文章的总体情绪是什么。

categories = list(set([i for  l in df_sentiment['category'].to_list() for i in l]))
categories_list=[]
for k in range(len(categories)):
    counter=0
    categories_list.append(categories[k])

l = [[] for i in range(len(categories_list))]

for category in range(len(categories_list)):
    for i in range(len(df_sentiment)):
        if categories_list[category] in df_sentiment.category[i]:
            l[category].append(df_sentiment['compound'][i])

comp_df=pd.DataFrame(list(zip(categories_list, l)), columns=['category', 'compounds'])

pos=[]
neg=[]
for i in range(len(comp_df)):
    pos_c=0
    neg_c=0
    for j in comp_df.compounds[i]:
        if j>=0:
            pos_c+=1
        else:
            neg_c+=1
    pos.append(pos_c)
    neg.append(neg_c)

comp_df['positive_count']=pos
comp_df['negative_count']=neg
comp_df.head()

让我们画出结果:

fig = plt.figure(figsize=(20,15))
ax = plt.subplot(111)
ax.bar(comp_df.category, [-i for i in neg], width=1, color='r')
ax.bar(comp_df.category, pos, width=1, color='g')
plt.xticks(rotation='vertical')
plt.show()

显然,税收似乎是最消极的话题。但是,长的负棒线主要是因为税收是最常见的类别。我们感兴趣的是给定类别的负面程度的相对度量。

带着这个目的,让我们计算并绘制负对正的比值比。

odds = []
for i in range(len(comp_df)):
    odds.append(comp_df['negative_count'][i]/comp_df['positive_count'][i])

comp_df['odds'] = odds
comp_df.head()

colors = []
for i in odds:
    if i>1:
        colors.append('b')
    else:
        colors.append('gray')
fig = plt.figure(figsize=(20,15))
ax = plt.subplot(111)
ax.bar(comp_df.category, odds, width=1, color=colors)
ax.axhline(1, color="red", ls = '--')
plt.xticks(rotation='vertical')
plt.show()

结论

由于比值比,我们可以看到负面文章是正面文章次数最多的类别是身份盗窃。相反,与上面猜测的不同,税收类别显示出较低的优势比,这意味着这两种情绪没有那么不平衡。

总体而言,司法新闻稿的总体情绪似乎是负面的,只有少数类别的优势比低于 1(由红色虚线表示),这意味着正面文章多于负面文章。

参考

用 Python 实现司法判决的自然语言处理

原文:https://towardsdatascience.com/natural-language-process-for-judicial-sentences-with-python-ed21d9be9ca9

https://pixabay.com/

第 8 部分:命名实体和网络表示

正如我们在以前的文章中看到的,NLP 处理包含大量信息的大量文本数据。我们看到了许多降低复杂性和从数据中提取相关信息的技术,从最简单的标记化/词汇化/词干化TF-IDF 分析单词/文档嵌入

分析这些文本数据的另一个重要方面是识别命名实体。

命名实体可以定义为具有适当标识的对象。例如,一个人是一个命名的实体,还有一个国家、一个组织等等。

为了从原始文本中提取命名实体,需要命名实体识别(NER)算法。NER 算法可以属于不同的类别,从监督学习模型(之前已经标记的实体之间的多类分类)到基于深度学习的方法再到统计模型。后者是我们将要使用的,因为它是 Python 库的 paCy 所使用的。

spaCy 的 NER 提供了 18 个命名实体,我们可以通过导入所需的库并列出这些实体来初步了解它们:

!pip install spacy
import spacy
from spacy import displacy

NER = spacy.load("en_core_web_sm")

ner_lst = NER.pipe_labels['ner']

print(len(ner_lst))
print(ner_lst)

让我们看一个带有原始文本的 NER 应用程序的例子(我使用了比尔盖茨的维基百科页面)。

raw_text="William Henry Gates III (born October 28, 1955) is an American business magnate and philanthropist. He is a co-founder of Microsoft, along with his late childhood friend Paul Allen. During his career at Microsoft, Gates held the positions of chairman, chief executive officer (CEO), president and chief software architect, while also being the largest individual shareholder until May 2014\. He was a major entrepreneur of the microcomputer revolution of the 1970s and 1980s."

text1= NER(raw_text)
for word in text1.ents:
    print(word.text,word.label_)

对于每个命名实体,我们还可以看到 spaCy 提供的定义。也就是说,让我们看看“组织”的定义。

spacy.explain("ORG")

最后,我们还可以使用 spaCy 特性中的一个有趣的视觉效果:

displacy.render(text1,style="ent",jupyter=True)

现在让我们跳到 NER 对我们的司法判决的分析。

命名实体

想看文章的词汇构成。由于“tax”类别是最常见的类别(正如我们在本系列的第 2 部分中发现的那样),我将对该类别进行分析,但是同样的推理可以扩展到所有其余的类别。

#It might take a while, so skip this cell and the following one 
m = ['Tax' in df['category'][i] for i in range(len(df))]
df_tax = df[m]
df_tax = df_tax.reset_index(drop=True)
#df_tax.head()

tax_entities=[entity.label_ for i in range(len(df_tax)) for entity in nlp(df['Tokens'][i]).ents]
#saving results
with open('data/tax_entities.pkl', 'wb') as f:
    pickle.dump(tax_entities, f)

让我们初始化一个包含与命名实体相关的 NER 标签的字典。

#initializing a dictionary
counts = dict()
for i in tax_entities:
    counts[i] = counts.get(i, 0) + 1

d = dict()
for category in categories:
    m = [category in df['category'][i] for i in range(len(df))]
    tmp = df[m]
    tmp = tmp.reset_index(drop=True)
    entities=[entity.label_ for i in range(len(tmp)) for entity in nlp(df['Tokens'][i]).ents]
    counts = dict()
    for i in entities:
        counts[i] = counts.get(i, 0) + 1
    d[category]=counts

#saving results
import pickle
with open('data/named_entities_dict.pkl', 'wb') as f:
    pickle.dump(d, f)

最后,让我们将字典转换成熊猫数据框架并绘制结果:

#run this cell to download the result
import pickle

with open('data/tax_entities.pkl', 'rb') as f:
    tax_entities = pickle.load(f)
with open('data/named_entities_dict.pkl', 'rb') as f:
    d = pickle.load(f)
#plotting results
import pandas as pd
import matplotlib.pyplot as plt
df_dict = pd.DataFrame(d)
df_dict.T.plot(kind="bar", stacked=True, figsize=(20,10))
plt.title('Named entities composition by category')
plt.legend(bbox_to_anchor=(1.05, 1.0), loc='upper left')
plt.show() 

就命名实体而言,似乎所有类别都有类似的组成,最常见的是(每个类别)个人、组织和地缘政治实体。

多亏了 NER,

网络表示

我想在这部分做的最后一个视觉分析是用一个网络框架来表现我们的文本。

网络是表示跨单元动态关系的强大数学模型。在这种情况下,单元(或节点)将是文章,我想检查它们之间的相似性。为此,我将根据链接的节点(即文章)之间的相似性来加权每条边。为了有一个可解释的可视化,我将只使用整个数据集的前 10 篇文章。

tmp = df.head(10)
tokens = []
lemma = []
pos = []
parsed_doc = [] 
col_to_parse = 'text'
for doc in nlp.pipe(tmp[col_to_parse].astype('unicode').values, batch_size=50,
                        n_threads=3):
    if doc.is_parsed:
        parsed_doc.append(doc)
        tokens.append([n.text for n in doc])
        lemma.append([n.lemma_ for n in doc])
        pos.append([n.pos_ for n in doc])
    else:
        # We want to make sure that the lists of parsed results have the
        # same number of entries of the original Dataframe, so add some blanks in case the parse fails
        tokens.append(None)
        lemma.append(None)
        pos.append(None)

tmp['parsed_doc'] = parsed_doc
tmp['comment_tokens'] = tokens
tmp['comment_lemma'] = lemma
tmp['pos_pos'] = pos

现在让我们导入这个分析的主库, NetworkX —一个 Python 包,用于创建和分析网络的结构和交互。

使用 NetworkX 提供的工具,我将为每个节点分配一个与其标题对应的标签:

import warnings
warnings.filterwarnings("ignore")
import networkx as nx
G = nx.Graph()
for i in range(10):
    G.add_node(i, text = tmp['parsed_doc'][i], title=tmp['titles'][i])
for i in range(len(tmp['parsed_doc'])):        # sure, it's inefficient, but it will do
    for j in range(len(tmp['parsed_doc'])):
        if i != j:
            if not (G.has_edge(i,j)):
                sim = tmp['parsed_doc'][i].similarity(tmp['parsed_doc'][j])
                G.add_edge(i, j, weight = sim)
labels = {}
for i in G.nodes():
    labels[i]=G.node[i]['title']

labels

现在让我们来看看结果:

plt.rcParams['figure.figsize'] = [16, 9]  # a better aspect ratio for labelled nodes
from math import sqrt
count = G.number_of_nodes()
equilibrium = 10 / sqrt(count)    # default for this is 1/sqrt(n), but this will 'blow out' the layout for better visibility
pos = nx.fruchterman_reingold_layout(G, k=equilibrium, iterations=300)
edgewidth = [d['weight'] for (u,v,d) in G.edges(data=True)] #weighting edges by the similarity of the nodes they link

nx.draw(G, pos, font_size=3, node_size=50, edge_color='gray', with_labels=False)
for p in pos:  # raise positions of the labels, relative to the nodes
    pos[p][1] -= 0.03
nx.draw_networkx_edges(G, pos, width=edgewidth)
nx.draw_networkx_labels(G,pos, font_size=10, font_color='k', labels = labels)

plt.show()

如果你想更深入地研究网络科学和背后的数学,我建议阅读我以前的文章这里

通过命名实体和网络分析,我们丰富了可以从原始文本中检索的信息。在下一篇文章中,我们将继续使用无监督学习技术,通过无监督情感分析,不仅检测文本的含义,还检测与每个句子相关的情绪或情感。

所以请继续关注第 9 部分!

参考

用 Python 实现司法判决的自然语言处理

原文:https://towardsdatascience.com/natural-language-process-for-judicial-sentences-with-python-part-1-bdc01a4d7f04

https://pixabay.com/

第 1 部分:数据预处理

自然语言处理(NPL)是人工智能的一个领域,其目的是找到计算方法来解释人类语言的口语或书面语。NLP 的思想超越了可以由 ML 算法或深度学习 NNs 进行的简单分类任务。事实上,NLP 是关于解释:你想训练你的模型不仅仅是检测频繁出现的单词,统计它们或者消除一些嘈杂的标点符号;你希望它告诉你谈话的情绪是积极的还是消极的,电子邮件的内容是纯粹的宣传还是重要的事情,过去几年关于惊悚小说的评论是好是坏。

在这一系列文章中,我决定对与美国司法判决相关的新闻稿进行分析。这是一个历史数据集,包含了来自 https://www.justice.gov/news 司法部(DOJ)网站的 13087 篇新闻稿,我在 Kaggle 上找到了 json 格式的(https://www . ka ggle . com/jben Cina/Department-of-Justice-2009 2018-press-releases)。在这些行中,有 4688 行标有新闻稿的类别。

本研究的目标有两个主要研究问题:

  • 如果您需要检索详细信息,拥有一个标记良好的知识库是至关重要的。新闻档案每天都在更新,手动标注每篇文章可能会非常耗时。我的问题是:有没有可能实现一个预测算法,自动将新文章分类到现有类别中?
  • 能否推断出文章的情绪,并将其与所属的类别联系起来?为此,我将对我的文章进行无监督的情感分析,对于每个类别,显示有多少文章被归类为正面或负面。

为了回答这些问题,我将我的研究分为 9 个部分:

  • 第 1 部分:数据预处理
  • 第 2 部分:描述性统计
  • 第 3 部分:TF-IDF 分析
  • 第四部分:矩阵分解的潜在主题
  • 第 5 部分:用 LDA 进行主题建模
  • 第 6 部分:文档嵌入
  • 第 7 部分:聚类分析
  • 第 8 部分:命名实体和网络表示
  • 第 9 部分:无监督情感分析
  • 第 10 部分:用逻辑回归、SVC 和 Keras 神经网络进行预测分析

在本系列的第一部分中,我将介绍数据预处理活动,其中我们还将执行标记化、词干化和词汇化。

  • 记号化→将给定文本分解成称为记号的单元的过程。在这个项目中,令牌将是单个单词,标点符号将被丢弃
  • 词干提取→提取单词的词根或词干的过程。
  • 词汇化→根据上下文将单词转换成有意义的基本形式或词根形式的过程。

注意:词汇化在自然语言处理和 NLU(自然语言理解)中非常重要,因为它比词干提取更准确。现实世界应用的一些例子是聊天机器人和情感分析。这种技术的缺点是词汇化算法比词干化算法慢得多。

为什么我们的数据框中需要这些进一步的元素?因为这些将是我们训练高效 NLP 模型的输入。例如,想象一个情感分析模型:你如何告诉模型“微笑”和“微笑”指的是同一个概念?

因此,让我们从导入必要的库开始(这些库对下一章也很有用):

#text preprocessing
import json
import nltk
import en_core_web_sm
import spacy
nlp = spacy.load("en_core_web_sm")
from nltk import SnowballStemmer
stemmer = SnowballStemmer("english")
from nltk.collocations import BigramCollocationFinder, BigramAssocMeasures
from nltk.corpus import stopwords
from gensim.models.doc2vec import TaggedDocument
from gensim.models import LdaMulticore, TfidfModel, Doc2Vec, CoherenceModel
from gensim.corpora import Dictionary#machine learning, models and statistics
from sklearn.decomposition import TruncatedSVD, NMF
from sklearn.feature_extraction.text import TfidfVectorizer, CountVectorizer
from sklearn.preprocessing import MultiLabelBinarizer
from sklearn.metrics import silhouette_score
from sklearn.cluster import KMeans, AgglomerativeClustering
from sklearn.model_selection import train_test_split
from sklearn.metrics import precision_score, classification_report, f1_score
from sklearn.model_selection import cross_val_score
from sklearn.feature_selection import SelectKBest, chi2
from sklearn.decomposition import TruncatedSVD
from sklearn.multiclass import OneVsRestClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.svm import LinearSVC
from scipy.sparse import random#utilities
import numpy as np
import pandas as pd
import pickle
import unidecode
import datetime
import os
import warnings
from math import sqrt
from collections import defaultdict, Counter
import json
import re
import sys
import random
import time #to know how long training took
import multiprocessing 
from IPython.display import Markdown, display#keras
import tensorflow as tf
from keras.models import Sequential
from keras.layers import InputLayer, Input
from keras.layers import Reshape, MaxPooling2D
from keras.layers import Conv2D, Dense, Flatten, Activation, Dropout
from keras.optimizers import SGD, RMSprop, Adagrad
from keras.models import load_model
from keras.preprocessing.sequence import pad_sequences
from keras.models import Model
from keras.layers import Embedding
from keras.layers import Bidirectional, LSTM
from keras.utils import to_categorical#visualizationfrom matplotlib import colors
from mpl_toolkits.mplot3d import Axes3D
import seaborn as sns
import matplotlib.pyplot as plt

对于文本 NLP 包,我将使用 Python 库 SpacyNLTK

现在,我将下载 json 数据并将其转换成 pandas 数据帧,并执行上述预处理任务。

#reading json files=[]
with open('combined.json') as f:
    for i in f.readlines():
        s.append(i)

res = [json.loads(s[i]) for i in range(len(s))]
contents = []
titles = []
date = []
topic = []
components = []
for i in range(len(res)):
    titles.append(res[i]['title'])
    contents.append(res[i]['contents'])
    date.append(res[i]['date'])
    topic.append(res[i]['topics'])
    components.append(res[i]['components'])#storing result into a pandas dfimport pandas as pd
df = pd.DataFrame(list(zip(titles, date, contents, topic, components)), 
               columns =['titles', 'date', 'text', 'topic', 'component'])

我注意到文本中充满了\xa0 符号,这是表示空格的 Unicode。更具体地说,这个符号代表“不间断空格”。幸运的是,Python 中有一个很好的包,可以通过 pip 安装,就是“unidecode”。一旦导入,函数unidecode()将获取 Unicode 数据,并尝试用 ASCII 字符表示它。

import unidecode
for i in range(len(df)):
    df['text'][i] = unidecode.unidecode(df['text'][i])#now let's convert the timestamp format of the "date" column to pandas datetime64.
import datetimefor i in range(len(df)):
    res = datetime.datetime.strptime(df['date'][i][0:10],"%Y-%m-%d")
    df['date'][i] = pd.to_datetime(res)

我还想知道有多少记录有标签,因为它将是我们分类预测分析的目标变量。

#counting how many articles are missing the category
counter=0
for i in range(len(df)):
    if len(df['category'][i])==0:
        counter+=1

print('Number of non-labeled articles: {}'.format(counter))
print('Number of labeled articles: {}'.format(len(df)-counter))
if len(df)-counter>=2000 and len(df)>=5000:
    print('Dataset convalidated:-)')Number of non-labeled articles: 8399
Number of labeled articles: 4688
Dataset convalidated:-)

所以我们有超过 2000 个文档被贴上标签,这正是我们要找的。现在,我们不需要它们,因为预测分析将是最后一部分。

现在是时候对我们的文本进行预处理了。我将使用上面提到的记号、词条和词干创建另外三列。当我们需要对单词和文档进行矢量化时,这些将非常有用。

df["Tokens"] = [" ".join([token.text.replace("'ll", "will").replace("'ve", "have").replace("'s", "is").replace("'m", "am") for sentence in nlp(speech).sents for token in sentence]) 
         for speech in df.text]df["Lemmas"] = [" ".join([token.lemma_ if token.lemma_ != "-PRON-" else token.text.lower() 
            for sentence in nlp(speech).sents for token in sentence if token.pos_ in {"NOUN", "VERB", "ADJ", "ADV", "X"} 
                               and token.is_stop == False]) for speech in df.text]punctuation = set("""!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~""")
stemmer = SnowballStemmer("english")

df["Stems"] = [" ".join([stemmer.stem(token.text) for tokenized_sentence in nlp(tokenized_speech).sents 
                         for token in tokenized_sentence if token.text not in punctuation and token.is_stop == False])
                   for tokenized_speech in df.text]#saving the df into pickle format
df.to_pickle('data/df.pkl')

在预处理的最后,熊猫数据帧的最终外观将如下所示:

#reading pickle dataset
df = pd.read_pickle('data/df.pkl')#to avoid terminology confusion, I'm renaming the topic column to "category": indeed, I'll refer to topic while performing 
#topic modeling like LDA, hence topic!=category.df=df.rename(columns={'topic':'category'})
df.head()

在下一篇文章中,我们将更深入地研究这个数据集的描述性统计,并开始检索相关信息,请继续关注下一章!

参考

用 Python 实现司法判决的自然语言处理

原文:https://towardsdatascience.com/natural-language-process-for-judicial-sentences-with-python-part-2-964b0e12dd4a

https://pixabay.com/

第 2 部分:描述性统计

在这一系列文章中,我将对司法判决进行一系列的 NLP 分析,目的是解决两个研究问题:

  • 如果您需要检索详细信息,拥有一个标签良好的知识库是至关重要的。新闻档案每天都在更新,手动标注每篇文章可能会非常耗时。我的问题是:有没有可能实现一个预测算法,自动将新文章分类到现有类别中?
  • 能否推断出文章的情绪,并将其与所属的类别联系起来?为此,我将对我的文章进行无监督的情感分析,对于每个类别,显示有多少文章被归类为正面或负面。

在本系列的第 1 部分中,我们介绍了数据集和一些预处理活动,如标记化、词干化和词汇化。

现在让我们开始从我们的数据中获得一些有意义的见解和可视化表示。

类别的频率

现在我感兴趣的是调查哪个是类别出现的频率。在预测任务中也需要这些信息:事实上,在测试预测模型时,有一个评估基准是很有用的,它可以被设置为最频繁的基线预测,也就是说,将最频繁的类别分配给所有记录作为标签。

让我们用类别频率创建一个熊猫数据框架:

#retrieving datadf = pd.read_pickle('data/df.pkl')
df=df.rename(columns={'topic':'category'})#creating a list of categories from the available ones
categories = list(set([i for  l in df['category'].to_list() for i in l]))#creating a df categories_list=[]
count_list=[]
for k in range(len(categories)):
    counter=0
    categories_list.append(categories[k])
    for i in range(len(df)):
        if categories[k] in df['category'][i]:
            counter+=1
    count_list.append(counter) 

counts = pd.DataFrame({"Category": categories_list, "Count": count_list}).sort_values(by = "Count", ascending = False).reset_index(drop=True)
counts.head(5)

import seaborn as sns
import matplotlib.pyplot as plt
sns.set_context("paper")fig, axis = plt.subplots(figsize = (16, 8))
sns.barplot(counts.Category, counts.Count, ax = axis)
axis.set_title("Category counts")for item in axis.get_xticklabels():
    item.set_rotation(90)
    item.set_fontsize(10)
    itemfig.show()

我们可以看到,到目前为止,最常见的类别是税收:它几乎是第二常见类别的两倍。

我最初想放弃那些少于 20 条记录的类别。然而,鉴于我分析的目标,我决定保留它们。事实上,我的最终目标是创建一个自动系统,能够在新文章插入数据库时对其进行标记,这样搜索它们就更容易了(在研究问题部分有更好的解释)。因此,我更喜欢保留所有已经提到的类别,由于数据库的不断输入,过一会儿记录会越来越多。

文章长度的分布

在这一节中,我分析了文章长度在类别中的分布。我很想知道是否有些类别会比其他类别导致更长的文章。

sns.set_context("talk")
fig, axis = plt.subplots(nrows = 1, ncols = 1, figsize = (11, 6))for category in categories_list:
    mask = [category in df['category'][i] for i in range(len(df))]
    x = [len(df[mask].reset_index(drop=True)['text'][i]) for i in range(len(df[mask]))]
    sns.distplot(x, label = category, hist = False, ax = axis)plt.legend(bbox_to_anchor=(1.05, 1.0), loc='upper left')
plt.title("Distributions of articles' lengths across various categories")

似乎所有的分布都趋向于在 3k 和 5k 字长之间达到它们的最大值,加上几乎所有的分布都非常窄。只有少数种类,如类足类动物,相对于其他种类,其分布范围要广得多。

地理分析

在这里,我将从文本中提取地缘政治实体,并查看哪些是美国“最热”的地区。我说的“热门”是指他们参与犯罪活动的次数。为了这个分析,我决定检查那些参与贩毒的人,但同样的推理适用于任何其他类别。

#let's create a list of geopolitical named entities. This will take a while. Skip to the next cell to directly download the results
import pickle
sites = [entity.text for i in range(len(df)) for entity in nlp(df['text'][i]).ents if (entity.label_=='GPE') & 
     ('Drug Trafficking' in df['category'][i]) & (entity.text!='U.S.')]with open("data/sites.txt", "wb") as fp:   #Pickling
    pickle.dump(sites, fp)#investigate most common locations
from collections import Counter
c = Counter(sites)
c.most_common(6)

注意:位置“美国”被解析为一个地理政治实体,但是为了本节的目的,我们应该只考虑单个州。

现在让我们绘制这些数据:

#downloading necessary packages
#! pip install --user basemap
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.basemap import Basemapimport os
#os.environ['PROJ_LIB'] = r"C:\Users\valen\Anaconda3\pkgs\proj4-5.2.0-ha925a31_1\Library\share"os.environ['PROJ_LIB'] = r"...\Anaconda3\pkgs\proj4-5.2.0-ha925a31_1\Library\share"plt.figure(figsize=(20,10))
m = Basemap(projection='robin',lon_0=0,resolution='c')
m.fillcontinents(color='white',lake_color='#85A6D9')
m.drawcoastlines(color='#6D5F47', linewidth=.4)
m.drawcountries(color='#6D5F47', linewidth=.4)#the following information have been seached on google, they are not present in the datasetlats = [23.634501,31, 36.778259,  27.994402]
lngs = [-102.552784,-100, -119.417931, -81.760254]
populations = [93,76, 75, 67]
x,y = m(lngs,lats)s_populations = [p*5 for p in populations]m.scatter(
    x,
    y,
    s=s_populations, #size
    c='red', #color
    marker='o', #symbol
    alpha=0.25, #transparency
    zorder = 2, #plotting order
    )

趋势类别随时间的演变

最后,在这一节中,我想考察一段时间内最常见的类别。为了做到这一点,我按年份汇总了我的数据集,并简单地计算了每个类别出现的次数。

sns.set_context("paper")
fig, axis = plt.subplots(nrows = 1, ncols = 1, figsize = (11, 6))for category in categories_list:
    mask = [category in df['category'][i] for i in range(len(df))]
    new_df=df[mask].reset_index(drop=True)
    gap_df=new_df.resample('Y', on='date').count()['category']

    sns.lineplot(x=gap_df.index, y=gap_df, label=category)

plt.xticks(rotation=15)
plt.legend(bbox_to_anchor=(1.05, 1.0), loc='upper left')
plt.title('Evolution of trending categories across time')

从上面的图表中我们可以看到一些标签是从某一年开始使用的。即民权或身份盗窃从 2016 年才开始使用。

显然,在 2015 年之前,最常见的标签(税)只有 1 条。

在下一篇文章中,我们将讨论 TF-IDF 分析:术语频率(TF)和逆文档频率(IDF)。

请继续关注第 3 部分!

参考

自然语言处理:获取概观的 PDF 处理功能

原文:https://towardsdatascience.com/natural-language-processing-pdf-processing-function-for-obtaining-a-general-overview-6fa63e81fbf1

今天,许多用于自然语言处理(NLP)的文档。pdf 格式。将 pdf 读入 Python,虽然不是极其困难,但也不是键入PD . read _ pdf(' file _ name . pdf ')那么简单。今天,我将为您提供代码,它不仅允许您将. pdf 文件读入 Python,还允许您创建一个函数,利用正则表达式来查找文档的元数据。

照片由 Dmitry RatushnyUnsplash 上拍摄

Python 库: PyPDF2

今天将要讨论的主要 Python 库是 PyPDF2。你可以在这里找到 PyPDF2 的文档。PyPDF2 是一个 Python 库,允许分析和操作。通过 Python 实现 pdf 格式文件。该库不仅可以从 PDF 中提取文本,还可以提取 PDf 的元数据,这是我们今天要探讨的功能。

建立

首先,您需要安装 PyPDF2

pip install PyPDF2

安装 PyPDF2 后,导入库。其他也需要导入的库有 Pandasre

import PyPDF2
import pandas as pd
import re

接下来,下面的代码将把 PDF 从它的文件路径作为它的输入,并把它转换成 Python 中可读的形式。将所有这些代码放在一个函数中是非常容易的(我已经这样做了),但是我决定将它打出来,逐行解释代码。对于以下所有代码,我使用了 Alex Krizhevsky、Ilya Sutskever 和 Geoffrey E. Hinton 的论文“使用深度卷积神经网络进行图像分类”(在这里找到论文)。

#First, create an opener which accepts a PDF file path
opener = open(pdf_file_path,'rb')
#Second, read the opened file
pdf_file_reader = PyPDF2.PdfReader(opener)

上面两行代码将允许你用 Python 打开和读取一个 pdf 文件,接下来让我们创建提取器函数!

PDF 信息提取器功能

这个函数的输入将是先前创建的 reader 对象。该函数接受转换后的 PDF,找到元数据,并输出一个字典,其中每个键都链接到某个元数据属性及其值,由找到。PyPDF2 中的 documentInfo() 方法。

def pdf_info(read_pdf):
    pdf_info_dict = {}
    pdf_info = {}
    for key,value in read_pdf.metadata.items():
      pdf_info_dict[re.sub('/',"",key)] = value
      return pdf_info_dict

使用正则表达式来移除与每个元数据属性相关联的“/”(例如“/Author”→“Author”)。接下来,让我们将列表转换为一个系列和一个数据框!pdf _ to _ list _ series _ and _ df()函数接受一个字典,并将返回该字典的一个序列和数据帧。

def pdf_list_to_series_and_df(pdf_info_dict):
    pdf_series = pd.Series(pdf_info_dict)

    key_list = []
    val_list = []
    for key, val in pdf_info_dict.items():
      key_list.append(key)
      val_list.append(val)
    pdf_df = pd.Series.to_frame(pdf_series)
    pdf_df = pd.DataFrame({"Attribute" : key_list, "Informaton" : val_list}, index=key_list)
    return pdf_series, pdf_df

使用 AlexNet pdf 运行上述代码产生了以下系列和数据帧:

图片:PDF 系列(图片来自作者)

图片:PDf 的数据框(图片来自作者)

另外,请注意我们无法完全看到标题。我们只需调用系列和数据框的“标题”属性,就会列出完整的标题。

图片:访问 PDF 的标题(图片来自作者)

就是这样!Today 提供了一种快速获取 PDF 元数据并将其转换为字典、系列和数据框的方法。这在您研究和使用 PDF 文档时非常有用。此外,如果您想为某人提供感兴趣的文档的一些快速背景信息,而不必浪费时间自己搜索细节,此功能将允许您有效地优化该任务。感谢阅读!

如果你喜欢今天的阅读,请关注我,让我知道你是否还有其他想让我探讨的话题!另外,在LinkedIn上加我,或者随时联系!感谢阅读!

使用 Tensorflow 从自然语言到 SQL

原文:https://towardsdatascience.com/natural-language-to-sql-from-scratch-with-tensorflow-adf0d41df0ca

使用 Tensorflow 训练机器学习模型从自然语言问题/指令翻译为 SQL 查询的端到端教程。

简介

在这篇博文中,我们将关注一个有趣的任务:将自然语言翻译成 SQL。对此的学术术语是数据库自然语言接口(NLIDB)。尽管 NLIDB 仍然是一个活跃的研究领域,但是为一个简单的表建立模型实际上非常简单。我们将为一个包含 3 列的雇员表做这件事:姓名、性别和薪水(如图 1 所示)。在这篇博文结束时,您将学习如何从一串自然语言输入(如show me the names of the employees whose income is higher than 50000)到 SQL 查询输出select e.name from employee e where e.salary > 50000

图 1:雇员表

概述

在本节中,我们将从较高的层面解释核心思想。

这个任务的核心是一个机器翻译问题。虽然这可能很吸引人,只是扔在一个序列到序列的机器翻译模型,直接从输入的自然语言到输出的 SQL 查询,它在实践中表现不佳。主要原因是模型可能会遇到列值的词汇外(OOV)标记。尽管模型可以在某种程度上容忍其他次要的未知单词,但 OOV 对于列值是致命的。想象一下,在上面的例子中,我们有一个不同的薪水值,它来自训练数据集没有覆盖的简介——60000、70000、80000,你能想到的——总会有一个超出词汇表的薪水数字。name 列也是如此。OOV 令牌将被映射到一个[UNK]符号,并被提供给翻译模型。因此,模型无法在 SQL 输出中重建准确的实际列值。

处理这种情况的典型方法是通过一个称为架构链接的过程来运行原始输入,该过程识别并缓存列值,并用模型在训练期间看到的占位符来替换它们。例如,在模式链接之后,简介中的输入示例将变成show me the names of the employees whose income is higher than [salary_1]。在训练过程中,模型被赋予类似select e.name from employee e where e.salary > [salary_1]的标签输出。因此,该模型实际上正在学习如何从列值替换的自然语言输入转换为列值替换的 SQL 输出。最后一步是通过查找模式链接步骤缓存的相应列值来填充占位符。

现在有了高层次的理解,让我们深入到具体的步骤。

数据采集

我们将从数据采集开始。为了完全控制端到端的过程,也为了让它更有趣,我们将从头开始生成训练数据。这个数据生成步骤的预期结果是一个列值替换的自然语言句子和列值替换的 SQL 查询对的列表。虽然你可以继续手工制作一万个训练对,但这很乏味,而且可能不包括模型在推理过程中会遇到的大量语言变化。

相反,我们只手动创建几十个训练对模板来引导数据生成,然后对可能的列名和条件进行采样来实例化训练对。模板可能如下所示:

Show me [cols] of employees whose [cond_cols] order by [comp_cols] in descending order
select [cols] from employee where [cond_cols] order by [comp_cols] desc

然后,我们可以对雇员列(姓名、性别、薪水)的组合进行采样,以填充[cols]部分,对预定义条件(例如,性别= [gender_1]、薪水> [salary_1]和薪水< [salary_2])的组合进行采样,以填充[cond_cols],对可比列的组合进行采样(在本例中只有薪水)以填充[comp_cols]部分。显然,我们需要预先为自然语言语句和 SQL 查询定义[cols][cond_cols][comp_cols]的可能内容。这通常被称为指定领域的本体。

通过实例化具体的训练对,我们可以很容易地从几十个训练对增加到几百个训练对。接下来,我们需要扩充训练对,以包含更多的语言变体。我们可以通过用释义替换自然语言句子中的短语来做到这一点。基本上,对于句子中的每一个单词或多个单词的短语,我们通过将其改为释义来创建一个新的句子。SQL 查询保持不变,因为我们关注的是自然语言句子中的语言变化。然后,新句子和原始 SQL 查询对被添加到训练数据集中。我们从释义数据库中获取释义。关于如何使用释义数据库进行自然语言增强的具体代码,请参见这篇博文。这使得我们可以从数百个训练对增加到数千个训练对,这足以完成这项任务。

自然语言到 SQL 模型

现在我们有了成千上万个替换了列值的自然语言和 SQL 查询对,我们可以构建我们的翻译模型了。我们使用一个序列到序列的模型,注意机制在这篇的博文中有详细描述。架构的 RNN 部分如图 2 所示。

图 2: RNN 建筑

不过,我们对架构的嵌入部分做了一个改动。来自机器翻译博客文章的原始翻译模型仅仅从零开始学习输入单词嵌入。在这个模型中,我们仍然要学习输入单词嵌入,但除此之外,我们将加载一个固定的预训练单词嵌入层,并将这两个嵌入层的输出连接起来,以提供给模型的其余部分。参见图 3 中的图解。

图 3:自然语言嵌入架构

基本原理是我们可能没有足够的训练数据来表示自然语言输入的语言变化。所以我们想要一个预先训练好的单词嵌入层。但与此同时,我们希望保持我们自己从零开始训练的嵌入,以捕捉这个特定领域中的细微差别。下面是加载固定的预训练单词嵌入层的代码片段。

加载预先训练的嵌入

一旦我们有了这些,我们将在机器翻译博客文章中使用类似的代码进行机器翻译。唯一的区别是,如上所述,我们将把固定嵌入层的输出与自然语言嵌入层连接起来,并将其馈送到递归神经网络层(而不仅仅是从自然语言嵌入层获得输出)。完整的模型代码请参见下面的代码片段。关于如何创建模型初始化参数nl_text_processorsql_text_processor和训练循环代码,请随意参考机器翻译博文

翻译模型代码

我们对模型进行了 10 个时期的训练,并用一些自然语言输入进行了试验:

翻译培训

Input: show me the name of the employee whose salary is higher than [salary_1]
Output: select e.name from employee e where e.salary > [salary_1]Input: get me employee with name [name_1]
Output: select * from employee e where e.name like '%[name_1]%'Input: which employee has the highest salary
Output: select * , e.salary from employee e order by e.salary desc limit 1

模式链接模型

现在,剩下的唯一任务是链接列值。给目标一个自然语言句子,如show me the names of the employees whose income is higher than 50000,模式链接应该输出show me the names of the employees whose income is higher than [salary_1],它可以被翻译模型使用。同时,我们应该记住[salary_1]是 50000。当我们从翻译模型select e.name from employee e where e.salary > [salary_1]获得 SQL 输出时,我们将写回实际的列值 50000,以创建最终的可执行 SQL 查询select e.name from employee e where e.salary > 50000

有许多方法可以将原始输入链接到模式。有些使用基于规则的语法检测来识别潜在的列值。其他方法扫描不同大小的输入范围,并确定它们与列值的相似程度。相似性可以通过计算 span 的单词嵌入和列的集合嵌入之间的欧几里德距离来测量。可以通过对列值的代表性样本的所有单词嵌入求和来预先计算列的聚合嵌入。

我们将在这里探索一种新的方法。我们将使用相同的机器翻译方法来预测/生成任何给定输入的输出掩码。让我们将预定义的占位符映射到一个整数,其余的非列值映射到零。

[name_1]: 1, [salary_1]: 2, [salary_2]: 3, [gender_1]: 4, others: 0

然后,让我们通过用随机值替换翻译训练对中的占位符来生成链接训练数据的模式。有关更多详细信息,请参见以下代码片段:

模式链接数据生成

我们有如下链接训练数据对的模式。我们可以将训练数据输入到与之前完全相同的机器翻译模型架构中。

All employees with name John Joe and salary 50000 and gender male 0      0       0    0    1   1   0     0     2    0     0     4

我们对它进行了 10 个纪元的训练,并尝试了几个输入示例。

图式链接训练

# [name_1]: 1, [salary_1]: 2, [salary_2]: 3, [gender_1]: 4, others: 0Input: name of the employee whose salary is higher than 50000
Output: 0    0  0     0       0      0    0    0     0    2Input: get me employee whose name is John Joe
Output: 0   0     0      0     0   0   1   1Input: show me employees whose salary is between 450000 and 650000
Output: 0    0     0       0      0    0    0       2    0     3

现在,我们只需要使用输出作为掩码来标记原始输入中的占位符。请注意,对于像 name 这样的多单词值,我们需要将它们折叠成一个占位符。

至此,我们已经完成了从自然语言输入到可执行 SQL 输出所需的所有步骤。

推荐论文

  • 用于数据库的端到端神经自然语言接口(链接)。
  • 数据库自然语言接口的神经方法:综述。
  • 辅助任务(链接)的 Zero-shot Text-to-SQL 学习。
  • 一个可迁移学习的数据库自然语言接口(链接)。
  • 面向跨域数据库中复杂文本转 SQL 的中间表示(链接)。
  • PPDB:释义数据库(链接)。
  • 从用户反馈中学习神经语义解析器(链接)。
  • 近期数据库自然语言接口的比较调查(链接)。

解释强化学习中的自然策略梯度

原文:https://towardsdatascience.com/natural-policy-gradients-in-reinforcement-learning-explained-2265864cf43c

传统的政策梯度方法存在固有的缺陷。自然梯度收敛得更快更好,形成了当代强化学习算法的基础。

自然策略梯度在统计流形上推进策略,确保相同黎曼距离的一致更新。[罗伯特·卢克曼在 Unsplash 上的照片]

策略梯度算法是现代强化学习的基础。其思想是,通过简单地跟随目标函数的梯度(即偏导数的向量),我们最终会达到最优。这是一个聪明的方法:( I)直接优化政策(而不是学习间接价值函数),( ii)让奖励函数引导搜索。然而,政策梯度有的根本缺陷。本文解释了自然渐变的概念,揭示了传统渐变的缺点以及如何弥补它们。

虽然自然梯度的受欢迎程度已经被 TRPOPPO 等算法超越,但掌握它们的基本原理对于理解这些当代 RL 算法至关重要。自然政策梯度部署不同的思维方式,仅仅观察损失函数并不总是清晰的。

然而,对自然梯度的完整讨论是相当技术性的,需要许多冗长的推导。为了保持这篇文章的简洁,我主要关注于推理和直觉,为更深入的推导提供外部参考。此外,假设对传统(普通)策略梯度和加强算法有扎实的理解。

一阶政策梯度的问题

在传统的策略梯度方法中,梯度∇只给出了权重更新方向。它没有告诉我们在这个方向上要走多远。导数定义在一个无穷小的区间上,这意味着梯度只在局部有效,在函数的另一部分可能完全不同。

因此,我们在采样(使用当前策略)和更新(基于采样数据)之间迭代。每个新策略卷展都允许重新计算梯度并更新策略权重θ。

行为由步长α控制。这产生了以下众所周知的策略梯度更新函数:

传统的策略梯度更新函数,基于目标函数梯度∇_θJ(θ和步长α更新策略权重θ

更新过程中可能会出现两个常见问题:

  • 超调:更新错过了回报高峰,落在了次优政策区域。
  • 下冲:在梯度方向上采取不必要的小步导致收敛缓慢。

在监督学习问题中,由于数据是固定的,超调并不是太大的问题。如果我们超过了,我们可以纠正下一个时代。但是,如果 RL 更新导致不良策略,未来的样本批次可能不会提供太多有意义的信息。有点戏剧性:我们可能永远无法从一次糟糕的更新中恢复过来。非常小的学习率可能会解决这个问题,但会导致收敛缓慢。

超调的例子。如果进入梯度方向的步长太大(左),更新可能会错过奖励峰值,并落在低梯度的次优区域(右)。【作者图片】

更新后,我们降落在一个平坦的,次优的地区。低梯度仅引起小的权重更新,并且将需要多次迭代才能再次逃逸。

这里一个有趣的观察是,当我们应该执行一个谨慎的更新时,我们执行了一个大的更新,反之亦然。正如我们将在后面看到的,自然渐变正好相反。

让我们做一个思维实验。为了进行适当大小的权重更新,我们可能会决定给参数变化设置一个上限。假设我们在参数空间中定义一个最大距离作为约束。我们可以这样定义这个问题:

一种权重更新方案,限制了旧参数和更新参数之间的欧几里德距离。

其中| |δθ| |表示更新前后参数之间的欧几里德距离。

这听起来很合理,因为它应该避免超调,同时也没有必要限制更新大小。不幸的是,它并不像你预期的那样工作。例如,假设我们的策略是由θ_1=μ和θ_2=σ参数化的高斯控制,并且我们设置了上限ϵ=1.下图中的两个更新都满足约束!

正态分布对的比较。左边有μ_1=0,μ_2=1,σ_1=σ_2=0.3。右边有μ_1=0,μ_2=1,σ_1=σ_2=3.0。虽然两对之间的欧几里德距离是 1,但是很明显右边的一对比左边的一对更相似。[图片由作者提供]

在这两种情况下,欧几里德距离都是 1:sqrt[(1–0)+(0.3–0.3)]和 sqrt[(1–0)+(3–3)]。然而,对分布(即随机策略)的影响是完全不同的。

问题是给参数空间封顶并不能有效地给我们操作的统计流形封顶。请注意,政策是概率分布,改变概率会改变预期回报。这是我们优化并想要控制的流形。

我喜欢把统计流形想象成分布的“家族”。例如,正态分布的集合族(由μ和σ参数化)构成了一个流形。另一个例子是将神经网络视为输出值的分布。改变随机策略可以被视为在定义目标函数的流形上移动(由参数θ占据)。

参数 cap 仅在统计流形是线性的情况下才起作用,但这种情况很少发生。为了防止策略本身在更新期间改变太多,我们必须考虑分布对参数变化有多敏感。传统的策略梯度算法没有考虑这种曲率。要做到这一点,我们需要进入二阶导数的领域,这正是自然政策梯度所做的。

缩小政策之间的差异

我们发现,感兴趣的是分布(即由θ参数化的策略)之间的差异,而不是参数 θ和θ_old 本身之间的差异。幸运的是,存在多种距离来计算两个概率分布之间的差。本文将使用文献中最常见的 KL 散度。从技术上来说,它不是一种度量(因为它是不对称的),但可以这样认为(对于小的差异,它是近似对称的):

策略π和π_old 之间的 Kullback-Leibner 散度(也称为“相对熵”)。它描述了两个概率分布之间的距离。

在之前显示的正态分布示例中,KL 散度分别为 0.81661 和 0.023481[公式通过维基百科 ]

在这一点上,引入 KL 散度和费希尔信息矩阵之间的联系是很好的(后面我们会看到为什么)。费希尔信息矩阵是描述统计流形的曲率的黎曼度量,即流形对边缘参数变化的灵敏度。矩阵可以被视为对考虑曲率的距离的校正——想象一下在地球仪上而不是在平坦的地球上测量距离。

如果我们局部地定义 KL 散度,即δθ= 0,结果证明两者是等价的。在这种情况下,零阶和一阶导数变成 0,可以被删除。二阶导数的矩阵由 Hessian 矩阵表示,在这种情况下,它相当于 Fisher 信息矩阵:

局部地,KL 散度等价于 Fisher 矩阵。这一结果有助于实际应用。

这个结果对于实际的实现将是至关重要的,但是现在让我们先确定一下。

如果 Fisher 矩阵是一个单位矩阵,那么流形上的距离就是欧氏距离。在这种情况下,传统政策梯度和自然政策梯度是等价的。实际上,这种情况很少见。

与之前类似,我们对允许的更新变更设置了一个**约束。然而,这一次,我们将其应用于策略的 KL 散度,而不是参数空间的欧几里德距离。调整后的问题如下:**

一个权重更新方案,它限制了新旧策略之间的 KL 差异。注意,这个方案考虑的是分布之间的差异,而不是参数。

通过求解这个表达式,我们确保在参数空间中执行大的更新,同时确保策略本身不会改变太多。然而,计算 KL 散度需要评估所有的状态-动作对,所以我们需要一些简化来处理实际的 RL 问题。

拉格朗日松弛和泰勒展开

对于接下来的部分,可以在卡耐基梅隆的演讲幻灯片中找到一个精彩而详细的推导(作者 Katerina Fragkiadaki)。为了保持对直觉的关注,我只强调最突出的结果。

我们将找出这个问题的解决方法。首先,我们使用拉格朗日松弛法将散度约束转化为罚函数,得到一个更容易求解的表达式:

通过执行 Langrangian 松弛,我们得到一个惩罚而不是约束大的政策变化的表达式。这个表达式比较好解。

鉴于典型的 RL 问题太大,无法计算所有状态和动作的散度 D_KL,我们必须求助于近似方法。利用泰勒展开——根据导数来近似函数——我们可以基于通过部署策略π_θ获得的样本轨迹来逼近 KL 散度。

上述拉格朗日松弛的泰勒展开看起来如下(为了便于标记,考虑θ=θ_ old+δθ):

逼近最佳权重更新方案的泰勒展开。展开取损失的一阶展开和 KL 散度的二阶展开。

简而言之,损失项 J(θ)用一阶泰勒展开(即梯度 w.r.t. θ)来近似,类似于传统的政策梯度(本质上是局部线性化)。KL 散度用二阶泰勒展开来近似。当局部逼近 KL 散度(即δθ= 0)时,零阶和一阶差评估为 0,因此我们可以消除它们。这里感兴趣的是二阶导数。

为什么损失项 J(θ)没有二阶展开?首先,第二项相对于散度来说可以忽略不计。第二,费希尔矩阵是正定的,但是当也混合在损失项的海森中时,这可能不成立。

为了使表达式不那么吓人,我们可以(I)用 Fisher 信息矩阵代替二阶 KL 导数,以及(ii)删除所有不依赖于δθ的项。这给我们留下了一个稍微友好的表达:

权重更新方案的简化泰勒展开,代入费希尔矩阵,并删除不依赖于δθ的项

撇开符号紧凑性不谈,为什么要用费希尔矩阵代替二阶导数呢?事实证明等价是非常方便的。海森矩阵是一个|θ|⋅ |θ|矩阵,每个元素都是二阶导数。完整的计算可能相当麻烦。然而,对于费希尔矩阵,我们有一个替代表达式,它是梯度的外积。无论如何,由于我们已经需要传统策略梯度的这些值,因此计算开销大大减少:

费希尔信息矩阵可以表示为政策梯度的外积。该表达式在局部等价于 Hessian 矩阵,但在计算上更有效地生成。

因此,如果我们能够像我们习惯的那样计算梯度,我们就拥有了执行权重更新所需的所有信息。还要注意,期望意味着我们可以使用样本。

需要了解的信息相当多,所以让我们简要回顾一下到目前为止我们已经完成的工作:

  • 为了防止政策偏离太远,我们对新旧政策之间的 KL 差异进行了限制。
  • 使用拉格朗日松弛,我们将约束转化为惩罚,给我们一个单一的(无约束的)表达式。
  • 由于我们不能基于样本直接计算 KL 散度,我们使用泰勒展开作为权重更新方案的近似。
  • 对于小的参数变化,使用费希尔信息矩阵来近似计算 KL 散度,对此我们有一个现成的表达式。
  • 整个近似是一个局部结果,假设θ=θ_old。因此,整个原理只适用于小的政策变化。

现在,让我们看看如何解决这个问题。

求解 KL 约束问题

是时候回到拉格朗日松弛法的泰勒展开式了。我们如何求解这个表达式,即找到最优权重更新δθ?

权重更新方案的简化泰勒展开可以使用拉格朗日方法来解决

嗯,我们可以通过将梯度 w . r . t .δθ设置为零来找到所需的更新(朗格方法)。求解表达式(现在转换为最小化问题,假设θ=θ_old)得出:

通过将导数 w . r . t .δθ设置为 0 来求解松弛泰勒展开式

该解决方案可以被重新安排以找到权重更新δθ:

重新安排解决方案允许表达最佳权重更新

注意-1/λ是一个常数,可以被吸收到学习速率α中。事实上,α可以通过分析推导出来。从最初的约束,我们知道 KL 散度应该至多是ϵ.对于固定的学习率α,我们不能保证α F(θ)^-1 ∇_θJ(θ)≤ϵ.从代数上来说,我们可以推断出一个动态学习速率α(t11 ),它确保(再次近似地)更新的大小等于ϵ.遵守这一约束会产生以下学习率:

动态学习速率α确保权重更新的 KL-散度(通过近似)不超过散度阈值ϵ

最后,从重新排列中,我们提取 自然策略梯度,这是针对流形的曲率校正的梯度:

自然策略梯度 w.r.t .目标函数是标准梯度乘以逆 Fisher 矩阵,说明黎曼空间的曲率

这个自然梯度在距离限制内给出了黎曼空间中最陡的下降方向,而不是传统假设的欧几里得空间。注意,与传统的策略梯度相比,唯一的区别是 与逆 Fisher 矩阵相乘!事实上,如果费雪矩阵是一个单位矩阵——实际上很少是——传统的和自然的政策梯度是等价的。

最终的权重更新方案如下所示

自然政策梯度的权重更新方案。动态学习率确保每次更新同等地改变分布。

这种方案的强大之处在于,它总是以相同的幅度改变策略,而不管分布的表示。

最终结果在两个方面不同于传统的政策梯度:

  • 考虑到政策对局部变化的敏感性,梯度由逆向 Fisher 矩阵进行“修正”。由于矩阵是倒置的,在陡坡处(高灵敏度)更新趋于谨慎,而在平坦表面处(低灵敏度)更新趋于较大。传统的梯度方法(错误地)假设更新之间的欧几里德距离。
  • 更新权重/步长α具有适应梯度和局部灵敏度的动态表达式,确保ϵ量级的策略改变,而不管参数化。在传统方法中,α是一个可能不适合的可调参数,通常设置为某个标准值,如 0.1 或 0.01。

尽管背后的机制相当不同,但在表面上,传统政策梯度方法和自然政策梯度方法惊人地相似。

自然策略梯度算法的完整概要总结如下。注意,在实践中,我们总是对梯度和 Fisher 矩阵使用样本估计。

自然政策梯度算法,来自柏克莱的深度 RL 课程,作者约书亚·阿奇姆

自然渐变的问题

自然梯度克服了传统方法的基本缺陷,考虑了目标函数定义的流形如何随参数更新而变化。具体来说,自然梯度允许逃离高原,谨慎地接近回报高峰。从理论上讲,自然政策梯度应该比传统政策梯度收敛得更快更好。

在它们最纯粹的形式中,自然梯度算法通常是不实用的。这有许多原因。

首先,泰勒展开提供了高达二阶的局部近似。由于这个原因,估计的 Hessian 可能不是正定的。在实践中,自然梯度法在数字上是脆弱的,并不总是产生稳定的结果。大量的数学推导可能看起来令人信服,但泰勒展开、样本近似和严格的局部有效性(假设θ = θ_old)会极大地影响现实世界的性能。

第二,费希尔信息矩阵占据了|θ|⋅|θ|空间。考虑一个有 10 万个参数的神经网络,你可以想象笔记本电脑上的 100 亿个矩阵不会飞。此外,计算矩阵的逆矩阵是 O(N)复杂度的运算,这相当繁琐。因此,对于深度 RL 方法,自然策略梯度通常会超过内存和计算限制

最后,我们习惯于与复杂的一阶随机梯度优化器——如 ADAM,它也考虑二阶效应——在广泛的问题上提供出色的结果。二阶优化方法(即自然梯度算法)没有利用这些优化器。

诸如共轭梯度和克罗内克因子化近似曲率(K-FAC)的方法可以(部分地)解决上述问题。在实践中,诸如信赖域策略优化( TRPO )和特别是邻近策略优化( PPO )的方法已经在流行度上超过了自然梯度,尽管它们植根于相同的数学基础。

*

结束语

当对比自然政策梯度和传统政策梯度时,差异看起来相当有限。最后,我们只在我们所熟悉的梯度上增加了一个倒置的费希尔矩阵——考虑了局部敏感性。尽管如此,我们优化的方式是非常不同的,考虑策略距离而不是参数距离。通过确保策略在更新权重时不会偏离太远,我们可以执行更稳定和一致的更新。

自然策略梯度伴随着一系列数字挑战,尤其是在处理大规模优化时(例如,具有大量参数的神经网络)。此外,在理论基础上进行了大量的近似和简化;修行可能更不守规矩。对于现实世界的实现,最近的策略优化现在通常是首选的。

尽管如此,对自然梯度的理解对于那些希望了解强化学习最新技术的人来说是非常重要的。

喜欢这篇文章?你可能也会喜欢以下的 RL 作品:

**

进一步阅读

对于自然政策梯度的起源,我建议阅读 Amari (1998 年)和 Kokade (2001 年)的基础论文,以及 Martens (2020 年)最近的反思。

讲座幻灯片而言,我发现以下几张特别有帮助。

最后,以下帖子从不同角度提供了很棒的解释。

  • 克里斯蒂娅(2018)。自然梯度下降。[ 链接
  • 自然渐变。【链接
  • 扬·彼得斯(2010 年)。政策梯度方法。Scholarpedia,5(11):3698。【链接
  • OpenAI (2018)。信任区域策略优化。[ 链接***

2022 年导航 MLOps

原文:https://towardsdatascience.com/navigating-mlops-dc2a242ef7ed

生产中的数据科学:经验、工具和框架

劳拉·奥克尔在 Unsplash 上的照片

MLOps 已经在机器学习、数据科学、软件工程和(云)基础设施的交叉领域确立了自己的独立地位。在这篇文章中,我想看看机器学习/数据科学在生产中的现代方法、经验教训和实践经验。

数据环境变化很快

我刚开始做机器学习的时候,2014 年我还是大数据工程师的时候,大部分是在大数据的背景下应用的。机器学习或数据科学并不新鲜,但随着 Hadoop 中的 MapReduce 和后来的内存引擎(如 Apache Spark)将机器学习的能力与分布式计算和海量(web)数据的能力联系起来。我们已经看到了许多重大转变,从(本地)Hadoop 生态系统的兴衰开始(并非最后是因为巨大的管理成本),以及云中数据处理的持续趋势,我们可以肯定只有一件事是一致的:变化。

不仅基础设施变了,框架和方法也变了。从 TensorFlow 到 Pytorch,从 CRISP-DM 到 Agile,从 SOAP 到无服务器。很难跟上潮流。

MLOps Buzz

如果你看看人工智能初创公司和咨询公司做出的承诺,以及真正适用的承诺,两者之间存在很大差距。虽然他们中的一些人几乎没有应用任何人工智能,但他们中的大多数人都因为坏的、丢失的或无用的数据而失败。然而,近年来许多公司已经开始成功地应用数据科学,并且有许多模型等待投入生产。这带来了新一波的 MLOps 创业公司,他们现在再次承诺提供一个适合所有人的解决方案。

没有人能统治他们所有人

当从定制的 MySQL 数据仓库迁移到云并评估许多不同的产品时,只有一个结论:没有适用于所有东西的平台,其中大多数都在为非常狭窄的用例工作。对于 MLOps 也是如此。因此,不要认为没有 ML 基础设施团队就可以将模型投入生产。

但是当然,有一些优秀的工具可以解决特定的用例。而且你不需要在投产前建立完整的基础设施,你也不应该。但是您需要相应地扩展您的基础架构。

因此,我将在这里详细说明,并直接说出供应商或开源框架的名称,而不涉及其中任何一个。

语言

只是一个简短的提醒,每一个代码都应该遵循软件工程的原则。这意味着测试 CI/CD、项目结构和编码最佳实践。我在这里不关注这个,因为它与软件工程没有什么不同。

计算机编程语言

当然,我推荐使用 pandas、scikit-learn、seaborn、tensorflow、pytorch、 huggingface 、jupyter 的 Python 堆栈——凡是你能想到的。然而,从 MLOps 的角度来看,更重要的是该堆栈的部署。如果你有轻量级的脚本,我建议使用 AWS Lambda,如果它变得更高级,我更喜欢运行云服务器(如 AWS EC2)并在 Docker 中隔离一切。将地形用于基础设施是明智之举。我也推荐使用 anaconda,因为他们的库是有管理的,对于专业人员来说非常便宜。

稀有

有时你可能需要生产 R 脚本。在这种情况下,要么选择托管服务,要么使用 Docker 来隔离环境。不要在裸机服务器上安装 R 并试图在那里运行生产脚本,你会经历地狱,因为 R 肯定不是生产级语言。并了解 R 库的安全含义。

Java 语言(一种计算机语言,尤用于创建网站)

如果你身边有 Java,就像许多公司一样,使用 java ml 框架可能更容易。在许多情况下,将您的模型直接集成到您的软件环境中是非常有意义的。但是你需要会用 Java 编码的人,或者需要会“移植”代码的工程师。虽然我个人不喜欢 WEKA 并会避免 deeplearning4j ,但我真的很喜欢使用 Smile-ML

框架和产品

实验跟踪

周围有很多不同的工具,但我个人喜欢 Neptune.ai 。他们称自己为 ML-Ops 的元数据存储库,这相当准确。你可以免费使用它们,甚至在一个小团队中,并跟踪你的实验和保存你的模型,支持许多不同的框架,如 scikit-learn,TensorFlow,Pytorch,R…

部署脚本与部署深度学习模型

如果你部署深度学习模型,我真的会建议你使用模型服务器。可以是 Tritontensorflow serve ,或者你喜欢的任何东西,但是要用模型服务器!如果你没有定制层,使用 ONNX 作为格式,如果你有定制层,使用框架的模型服务器来避免不必要的工作。这里我写的更详细,重点是 TensorFlow。

如果您为批量预测部署脚本,那么简单的方法就是在 Docker 中运行它们。对于 API,你可以像之前说的那样在 Docker 容器中运行无服务器产品,如 AWS LambdaFastAPI

模特培训

有了 terraform,你还可以在 GPU 实例(AWS、GCP、Azure)上的云中自动进行模型训练。然而,我个人喜欢在我的开发机器上有一个强大的 GPU 来快速试验东西和评估模型。在本地有一个 GPU,这对于原型来说要快得多。还要注意,有特定的 GPU 实例用于推理。

管弦乐编曲

对于编排来说, AirFlowPrefect 似乎是不错的选择,但是 Prefect 要求你在每台服务器上安装一个代理(管理员通常不喜欢这样),在 AirFlow 中你可以使用 SSH。

监视

根据我的经验,监控是高度定制的,但我个人喜欢的是用于时间序列监控的 Kibana ,它在付费版本中提供了开箱即用的异常检测。一般来说,监控模型预测(计数、分布)、训练结果和特征特性之类的东西是明智的。NeptuneAI 还涵盖了与模型训练相关的指标。

SQL 和矢量数据库

我很喜欢使用 MySQL 或 PostgeSQL 这样的纯 SQL 数据库。然而,在某种程度上,使用数据仓库是明智的,因为在 2022 年没有人会构建 Hadoop 集群,所以你最好看看像 Snowflake 这样的云数据仓库,它提供了很多高级功能,特别是针对 ML 和 DS ( Snowpark )。

但还有更多,如果你使用一些相似性搜索引擎(如视觉相似性),你可能要调查向量数据库。直截了当的方法是使用 HNSWlib (支持包括 Java 在内的许多语言)或惹恼来自 Spotify 的,其中的索引只是一个文件。但是还有更高级的选项,比如 Milvus.io

项目管理

不要用 Scrum,用看板,但这是我的看法。详见此处

推理设置

最后,让我们快速讨论一下推理设置。通常,很多模型不需要提供实时预测。如果不需要,引入额外的堆栈是没有意义的。以我的经验来看,有三种推理模式。批量预测实时预测在线学习系统,其中最后一个是一个例外。我从未真正将在线学习系统投入生产,所以我不能真正谈论它,但至少我可以说这是最复杂的场景,因为它是完全自主的,你需要大量的监控来确保模型不会失败,就像微软向我们展示的

与批处理相比,实时推理增加了许多复杂性。您需要确保应用所有的软件工程实践来构建一个可靠的、可伸缩的系统。这就是为什么我建议使用模型服务器,因为它们是为这种用例而设计的。

批处理要简单得多,因为如果它失败了,可以很容易地重复,你不需要考虑延迟,通常你只需要输入->输出,不需要太多的网络参与。

我希望这能给你一些启发,帮助你理解 MLOps 是什么。一如既往,这里有很多观点,有其他经历也没关系,但我很高兴在评论中听到你的。

NBA 选秀分析:使用机器学习预测 NBA 的成功

原文:https://towardsdatascience.com/nba-draft-analysis-using-machine-learning-to-project-nba-success-a1c6bf576d19

利用机器学习预测 NBA 成功的尝试。

埃德加·恰帕罗在 Unsplash 上拍摄的照片

介绍

众所周知,建立一个成功的体育特许经营最关键的部分之一是尽可能获得最有天赋的球员。对于 NBA 球队来说,获得这种天赋最可靠的方法是在 NBA 选秀中进行选择。每年一次,NBA 球队和他们的球迷充满了希望,他们可以带来一个大学明星,他可以将他们的成功转化为职业水平。

然而,在现实中,NBA 选秀是一门非常不精确的科学,经常导致有很高期望的球员无法实现他们的期望。作为一名 NBA 选秀的狂热粉丝,我想确定是否有可能利用一名选秀候选人的大学生涯来帮助评估他们在 NBA 的未来时的决策过程。对我来说,这似乎是数据分析和运动的一个很好的交集,也是一个获得新技能的机会。

我知道我的篮球统计一站式商店将是篮球参考。在做了一些研究之后,我发现了 Python 的 BeautifulSoup 库,我知道它能很好地为我工作,能够以有效的方式从 Basketball-Reference 获取数据。

:本文使用的所有代码都可以在 GitHub 上找到,整理在这个笔记本中。

收集数据

如前所述,我从篮球参考中获得了这个项目的 NBA 数据。我还需要 NCAA 篮球统计数据来进行分析,这些数据是我从体育参考大学篮球中获得的。为了获得这些数据,我使用了 Python 库 Beautiful Soup,它允许从 HTML 和 XML 文件中提取数据。我的第一步是从过去的 NBA 选秀中获取数据。我最初选择看过去的 20 次 NBA 选秀,主要是因为 NBA 比赛的变化是多么迅速,并希望在当前的 NBA 格局中预测成功。

我得到这个数据的页面就是上面的。本页面的网址为https://www . basketball-reference . com/draft/NBA _ { draft _ year }。html 。为了获得几个 NBA 选秀的数据集,我需要利用一个 for 循环来遍历年份列表,并更新多个 NBA 选秀的 URL。一旦完成,我就可以获得过去 20 次 NBA 选秀中所有球员的数据集以及他们的 NBA 职业统计数据。

我的下一个目标是获得大学统计数据,这些数据将与这些数据中每个适用的球员相关联。在此之前,我选择了查看所有入选球员的比赛分布,因为我想看看典型的 NBA 职业生涯长度是什么样的。

在创建可视化之前,我删除了没有参加过任何 NBA 比赛的球员以及没有上过大学的球员。有很多原因可以解释为什么被选中的球员可能从未在 NBA 打过一分钟,这可能包括海外球员被选中并选择留在海外职业联赛,甚至在一些不幸的情况下严重受伤。

很明显,NBA 比赛的分布是右偏的,这是有道理的,因为有一个不断轮换的有希望的 NBA 球员名单年复一年地进入联盟。NBA 球员只持续 2-3 个赛季的情况比勒布朗·詹姆斯这样职业生涯跨越近 20 年的人要常见得多。为了确保在我即将进行的分析中有一个像样的 NBA 表现基线,我决定删除任何没有在 NBA 打过至少 82 场比赛(相当于一个完整赛季)的球员。

在做了这些更改之后,我按照前面类似的过程来获取数据中剩余球员的大学统计数据。

个人球员学院统计链接如下:https://www . sports-reference . com/cbb/players/{ player _ first _ name }-{ player _ last _ name }-1 . html。我获得的大学球员的数据集包括 NBA 选秀的职业大学统计数据。

我考虑了 4 个指标来作为回归模型的目标变量,以表明 NBA 职业生涯的成功水平;WS、WS/48、VORP 和 BPM。对其中每一项的快速描述:

目标选择

WS — Basketball Reference 将胜率定义为“试图将团队成功的功劳分配给团队中个人的球员统计数据。”获胜份额的计算包括将进攻和防守获胜份额记入玩家的贷方。这包括计算球员相对于联盟平均水平的边际进攻和防守影响,以制定一个指标,量化球员在整个 NBA 职业生涯中对胜利的影响。

WS/48 —与 Win Shares 相同的计算方法,根据上场时间进行标准化调整。因为赢的份额是用统计数据计算的,他们更喜欢在球场上花更多时间的球员。这一统计数据使得所有球员在上场时间相同的情况下发挥的作用更加公平。

VORP——根据篮球参考, VORP 被定义为“一名球员在替补水平(-2.0)以上贡献的每 100 场球队财产的分的盒子得分估计,转化为一支平均球队,并按比例分配给 82 场比赛的赛季。乘以 2.70 以转换为胜于替换。

BPM——再次参考篮球参考,“框加/减,2.0 版(BPM)是一个基于篮球框得分的指标,评估一名篮球运动员在球场上对球队的贡献。它只基于传统篮球框得分中的信息,不包括逐场比赛数据或非传统框得分数据(如扣篮或偏转)。”

我决定利用 WS 作为回归模型中目标变量的最终选择。这些指标都不能完美概括一名 NBA 球员在球场上的影响力,但是当我们看一看 NBA 职业领袖获奖份额列表时,我们可以看到这个指标无疑指向了正确的方向。在这个指标中表现最好的是那些名人堂成员和球员,如果他们今天退役的话。

观察 WS 在数据中的分布,我们可以看到它是右偏的。这非常有意义,因为赢球份额是一个在球员整个职业生涯中累积的统计数据。

在决定了我的目标变量之后,我继续准备我将使用的数据,以便为机器学习模型清理数据。这包括处理大学统计数据中任何缺失的值,并删除任何不必要的功能。

在回归模型中训练数据之前,我选择执行特征选择,以便确定和过滤哪些大学统计数据将具有 NBA 胜利份额的最强信息增益。完成这个过程后,将作为解释性特征保存在我的模型中的大学统计数据包括:

  • 比赛开始次数——球员在整个大学生涯中开始的比赛次数(比赛开始时在球场上)。
  • 每场比赛的投篮次数——每场比赛的总投篮次数。
  • 每场比赛投中 3 分——每场比赛投中 3 分的次数。
  • 三分球命中率——三分球投篮次数除以投篮次数。
  • 每场比赛的罚球次数——每场比赛的罚球次数。
  • 每场比赛的进攻篮板——队友投篮不中后每场比赛的篮板数。
  • 每场防守篮板——在对方球员失误后每场比赛的篮板数。
  • 每场比赛的总篮板数——每场比赛进攻篮板和防守篮板的总和
  • 场均助攻——每场比赛中队友直接传球投篮的次数。
  • 每场比赛的阻挡次数——每场比赛中,球员挡出(或阻挡)对方球员投篮的次数。
  • 每场比赛失误次数——每场比赛中球员失去对对方球队的控球权的次数。
  • 每场比赛的得分——每场比赛的总得分。

训练/测试数据确定

因为 Win Shares 是一个累积的统计数据,所以我在选择预测模型的训练集时必须非常小心。这些选秀中的许多球员仍然有许多 NBA 比赛要打,特别是那些从 2010 年 NBA 选秀开始的球员,他们的球员可能刚刚进入他们的最佳 NBA 岁月。正因为如此,我选择限制我的模型的训练数据,以包括 2000-2009 年 NBA 选秀的球员,同时对 2015-2019 年 NBA 选秀进行测试。

我对此的推理是,2000-09 年选秀的球员将退出联盟/在这一点上走下坡路,因此他们的胜率将不再迅速增加,这将是最近 NBA 选秀的情况。我利用随机森林回归模型作为预测模型。该模型的预测高于这些 NBA 球员的实际获胜份额,这是意料之中的,因为这些球员仍在职业生涯的前 7 年内,并有望积累更多的获胜份额。为了说明这一点,我选择用一个主观的过程来分析我的模型的结果,而不是严格地看任何准确性指标。

下面,我比较了 NBA 选秀前 15 名球员的预测胜率和前 15 名球员的实际胜率。

回归结果

交互式图表(将鼠标悬停在数据点上以查看玩家姓名和价值)

从视觉上,我们可以看到模型对胜利份额的预测与这些 NBA 选秀的当前地位之间的差异。如前所述,这是意料之中的,因为赢球份额是在球员的整个 NBA 职业生涯中累积的。我将不得不等待根据平均绝对误差或平均绝对百分比误差来评估我的模型,直到这些选秀班的球员退出他们的 NBA 职业生涯。

2015 NBA 选秀

我的模型对这个选秀班的预测结果是一个大杂烩。2015 年选秀中的第一名是卡尔-安东尼·唐斯,他肯定会是一个重新起草的共识前三名,他在职业生涯中迄今为止一直带领这个选秀班赢得份额。

但是,在我的模型中,他在预测的 Win 份额中不在前 15 名之内(排名第 19 位),这是一个相当大的差异。Towns 已经超过了我的模型对他职业生涯的预测 30 %,他还有很长的职业生涯要走,这增加了这种差异。

尽管如此,我的模型确实预测了一些球员,他们将被认为超过了他们原来的选秀位置,包括德文·布克,德隆·赖特,凯文·鲁尼,朗德-霍利斯·杰弗逊,诺曼·鲍威尔,泰尔斯·琼斯,迈尔斯·特纳和鲍比·波蒂斯。

特别是德文·布克,他是第 13 顺位,在我的模型中表现非常好,他在上赛季带领菲尼克斯太阳队进入 NBA 总决赛时,在 NBA 中也支持了这一点。如果给 NBA 总经理一个重来的机会,这些球员很可能会比他们最初的选秀之夜更早被选中。

我的模型预测的最大失误应该是贾利尔·奥卡福、斯坦利·约翰逊、特雷·莱尔斯和温斯洛法官,他们在职业生涯中迄今为止都只不过是边缘轮换球员。

体育作家重新起草比较:

https://fadeawayworld.net/nba/re-drafting-the-2015-nba-draft-class-karl-anthony-towns-devin-booker-kristaps-porzingis

2016 NBA 选秀

我的模型有最初的第一选择,本·西蒙斯,有最高的预测胜率,如果他现在不坐在这个 NBA 赛季,他最有可能在他的职业生涯中证明这个预测。

然而,球员的第二名、第四名和第五名将不得不被归类为失误,因为马奎斯·克里斯、帕特里克·麦考和克里斯·邓恩与选秀中的前五名选手相去甚远。与他们在选秀之夜实际被选中的地方相比,像卡里斯·勒维特、多曼塔斯·萨博尼斯和德章泰·穆雷这样的球员在模型中的表现都很好。

其他城市如泰勒·尤利斯、德扬泰·戴维斯、以赛亚·怀特海德、斯卡尔·拉比西埃和亨利·埃伦森都没能进入前 15 名。与我的模型的差异还包括没有杰伦·布朗,贾马尔·穆雷,帕斯卡尔·西亚卡姆在预测的前 15 名 WS 中,因为他们都被证明是 NBA 全明星级别的球员。

体育作家重新起草比较:

https://www.yardbarker.com/nba/articles/redrafting_the_2016_nba_draft/s1__33314207

2017 NBA 选秀

我的模型,就像大多数 NBA 球探和前台一样,预测马克尔·富尔茨将是 2017 年 NBA 选秀班的最佳球员。不幸的是,像球探和前台一样,这个模型没有考虑到富尔茨在他仍然年轻的 NBA 职业生涯中一直困扰他的伤病。

从我的模型来看,下四个最高玩家的预测赢股,Tony Bradley、Dennis Smith Jr .、Semi Ojeleye 和 T.J. Leaf,在该选秀类的实际赢股方面远未达到最高。朗佐·鲍尔在预测获胜份额方面排在第 8 位,作为今年在芝加哥公牛队爆发的人,看起来是一个很好的赌注。

对于今年的选秀班来说,有几个球员在模型中表现出色,但他们在 NBA 职业生涯中迄今为止一直受到伤病的困扰。除了前面提到的马克尔·富尔茨,这还包括扎克·科林斯和哈里·贾尔斯。最重要的是,我的模型没有识别出这个选秀级别的几个最大的抢断,包括贾勒特·阿伦,巴姆·阿德巴约和多诺万·米切尔。

体育作家重新起草比较:

https://bleacherreport.com/articles/2890076-re-drafting-the-2017-nba-draft-class

2018 NBA 选秀

对于 2018 年的 NBA 选秀,我的模型预测了许多球员,他们在职业生涯中已经超越了他们的 NBA 选秀,然而这些球员的顺序仍然有很大的争议。

这个球员名单将包括贾里德范德比尔特,德安东尼梅尔顿,动摇米尔顿和德文特格雷厄姆。另一个因伤未能达到预期的球员也在我的模型中表现出色,马文·巴格利三世(Marvin Bagley III),他是选秀之夜的 2 号选择。或者,像小温德尔·卡特、沙伊·吉尔吉斯·亚历山大、小迈克尔·波特、科林·塞克斯顿、莫·邦巴和迈尔斯·布里奇斯这样的球员在他们的 NBA 职业生涯中仍然很年轻,但没有人会质疑他们应该在这个选秀班的前 15 名中获得一席之地。

体育作家重新起草比较:

https://hoopshabit.com/2021/08/18/nba-draft-luka-doncic-rises-2018-redraft/

2019 NBA 选秀

2019 年的 NBA 选秀仍然非常新鲜,球员在职业生涯胜利份额方面有足够的空间,但我对我的模型的预测相对满意,这些预测基于迄今为止预测胜利份额前 15 名球员的 NBA 表现。锡安·威廉姆森和贾·莫兰特在他们年轻的 NBA 职业生涯的第二个赛季就被评为全明星,而大流士·加兰将在本赛季加入他们。泰勒·赫罗,马蒂斯·蒂布尔,布兰登·克拉克,乔丹·普尔和 P.J .华盛顿都在职业生涯中击败了他们的选秀选择,并期待在未来的许多赛季中证明他们是选秀日抢断。

我的模型预测的失误将不得不包括泰杰罗姆,卡姆雷德和布鲁诺费尔南多,他们迄今为止不会被认为是 NBA 选秀的前 15 名球员。

体育作家重新起草比较:

https://bleacherreport.com/articles/2931305-2019-nba-re-draft-where-do-zion-ja-morant-and-the-rest-of-the-class-land

结论

我的分析给我的一个关键收获是,NBA 选秀是一门非常不精确的科学,在预测大学毕业生的 NBA 未来时,需要结合数字分析和传统的球员侦察才能获得最佳的准确性。

虽然我的模型正确地预测了相当多的球员,他们现在会被认为是 NBA 选秀中的“抢断者”(与他们在 NBA 的表现相比,他们在选秀中的表现晚于他们应该被选中的球员),但如果你跟随我的模型去开球,你也会以一些选秀“失败者”(被过早选中的球员)告终,并错过了在现实生活中表现比我的模型预测的好得多的球员。

我在这个项目中遇到的一些问题可以在以后的项目中解决,这些问题包括:

  • 为模型提供更多的训练数据——我可以包括 2000 年以前 NBA 选秀的数据,但是,我觉得 NBA 在过去的二十年里变化太大了。为了获得对当今 NBA 真实的预测,我选择省略一些我认为会混淆模型预测的数据。但是,在这样做的时候,我限制了模型可用的训练数据量。
  • 纳入国际球员——将选秀前在海外比赛的球员纳入预测也是有益的,方法是将他们纳入该模型的数据集中,或者创建一个单独的模型。今天,国际球员约占 NBA 的 25%,在选秀预测中错过这些球员就排除了一批有才华的球员,包括卫冕联盟 MVP 尼古拉·约基奇。
  • 赢得份额可能不是衡量个人成功的最佳指标。篮球是一项团队运动,因此衡量个人表现并不简单。一些非常有天赋的球员可能会被选入糟糕的情况/球队,在这种情况下,他们将不会获得与他们的天赋水平相当的预期胜利份额。在大多数情况下,这将在球员的整个职业生涯中随着他们在球场上的情况变化而平衡,但仍可能导致模型结果的差异。

总之,我了解到,是的,人们可以利用大学篮球统计数据来预测 NBA 选秀的成功前景。然而,仅仅基于大学表现的预测并不理想,因为有许多无法量化的方面会影响他们的 NBA 职业生涯,如球员无形资产和伤病,这些都无法用大学统计数据来解释。

更多内容请看plain English . io。报名参加我们的 免费周报 。在我们的 社区 获得独家获得写作机会和建议。

NDCG 不是你需要的全部

原文:https://towardsdatascience.com/ndcg-is-not-all-you-need-24eb6d2f1227

行业笔记

NDCG 不是你需要的全部

recSys 与 RecList 的行为测试

简介

电子商务系统(RSs)就在我们身边,帮助我们在当代生活的选择悖论中导航:我应该听的下一首歌是什么?我应该看的下一部电影是什么?或者更微妙地说,我应该消费的下一条新闻是什么?

RSs 代表了最普遍的机器学习系统类型之一,以及它在使数十亿人的数字生活个性化方面的所有力量,不断提醒我们作为用户、从业者和立法者的责任:通过鼓励你阅读一个关于好莱坞名人与气候变化的故事,我们使你更有可能花时间了解布拉德·皮特的生活,以及它所包含的一切。

因为即使是一个糟糕的预测也可能导致巨大的声誉损失或侵蚀我们的信任,构建更健壮的 RSs 是一个重要的话题,如果我们不能更好地测试现有的系统,我们当然不会构建一个更健壮的系统。

不幸的是,RSs 往往会“无声无息地失败”,如下例所示,摘自 LinkedIn 上一篇关于奇点的帖子(虽然还没那么接近):

每个人都有自己的关于离线评估中没有发现的错误的恐怖故事。[截图来自作者的 LinkedIn feed ]

这意味着,在一个典型的周期中,一个 ML 系统用一些数据进行训练,通过一些准确性指标在一个保留的集合上进行基准测试,如果选择的 KPI 是可接受的,则在野外部署。由于这些系统本质上都是随机的——在许多情况下,是非常复杂的非线性对象——ML 和软件开发之间的直接类比就失效了:测试集的高准确性本身并不能防止像上面这样不合理的推论。

这篇博文中,我们介绍了 RecList,这是一个新的开源库,用于在现实世界的推荐系统中扩展行为测试。虽然学术介绍以研究论文的形式提供,但我们希望在这里提供一个更实用的视角,面向现代 RSs 的所有利益相关者:ML 工程、产品经理,当然还有最终用户。

克隆回购,系好安全带,请 Github 上一个明星支持我们!

什么是行为测试?

对人工智能进行“行为测试”的想法并不新鲜:正如 NLP 爱好者可能已经注意到的那样, RecList 正在向清单致敬,微软从 2020 年开始领导建立 NLP 的行为测试。在传统测试中,我们有“逐点”估计(例如, NDCG平均倒数排名),它量化了一个保留数据集的泛化能力。相反,行为测试是输入-输出对:通过比较模型的输出和期望的结果来评估模型。例如,在情感分析中,我们希望在交换同义词时预测保持一致:

如果一个模型预测第一次评论的积极情绪,我们通过检查当把“棒极了”换成“棒极了”时预测是否改变来测试它的稳健性。【图片由作者提供】

在构建“你可能喜欢的相似事物”传送带时,我们希望产品在所有相关维度上都相似,因为向浏览低价 polos 的购物者推荐贵 10 倍的商品会导致次优体验:

相似产品的推荐应该尊重用户在各个方面的意图。例如,在这种情况下,基于视觉特征的基于内容的推荐器可能会“无声无息地失败”。[图片由作者提供]

清单的基本观点并不是“行为测试很酷”(咄!),而是说:

  • 行为测试可以以一种黑盒的方式运行,将测试从模型的内部工作中分离出来:因此我们可以在没有源代码的情况下比较模型,只要它们公开了一个标准的预测接口;
  • 建立一对一的行为测试是昂贵的,但是我们可以通过结合领域知识、ML 技巧和可扩展库来帮助扩展它们。

如果你愿意,RecList 是一个“推荐系统的清单”。

隐士背后的原则

我们确定了三个特定于建议的关键原则来启发我们的图书馆:

不变性质 : 互补和相似项满足不同的逻辑关系。虽然相似的物品可以互换,但互补的物品可能有一个自然的顺序:向购买昂贵电视的购物者推荐 HDMI 电缆是一个好主意,但向购买电缆的人推荐电视是一个糟糕的主意。我们通过将预测与元数据相结合来操作这些见解:例如,我们可以使用价格和分类法来检查不对称约束。

补充项目通常具有隐含的自然排序,这可能永远不会在标准的保留数据集上进行测试,只会在生产中变得明显。【图片来自作者等人的原论文】

不是所有的错误都一样糟糕 : 如果一部电影推荐的地面真相项目是“当哈利遇见莎莉”,漫无目的的度量将无法区分一个暗示的模型终结者和一个提议的模型你已经收到邮件。换句话说,虽然两者都是“错误的”,但它们在同一方面都不是错误的:一个是合理的错误,另一个是可怕的建议,对用户体验具有相当大的破坏性。

当根据“ 【大病号 ”推荐前 3 部电影时,模特 A 和 B 的命中率相同,然而,旋转木马中的“错误”电影并没有提供相同的体验:A 的建议比 B 的建议差得多。[图片由作者提供]

并非所有的输入都是相同的 : 如果我们关心的用户子集更快乐,我们可以容忍整体准确度的小幅下降;例如,如果我在营销活动中推广最新的耐克鞋,那么耐克产品页面上的购物体验应该特别有条理。由于商品消费通常是幂律,相对于高频商品的边际改善可能会产生 KPI 改善,为利基市场“隐藏”严重的退化:如果电影推荐者在漫威大片上变得更好,但对意大利用户来说变得很糟糕,我们应该能够注意到,并进行相关测试,以做出有原则和明确的权衡。除了一些横向用例(例如冷启动项目),最有趣的片段通常是上下文相关的,这是我们图书馆的一个重要指导原则。

即使蓝绿推荐人整体 HR 相当(见我们的 论文 ),在不同品牌上的表现也大相径庭。【图片来自作者等人的原论文】

足够的话:隐士入门

一个重新列表——我们使用斜体来表示 Python 抽象,而不是作为一个整体的包——仅仅是测试的集合。任何继承了正确抽象的类都可以包含任意多的测试,通过 Pythonic 风格的 decorators 指定(让人想起 Metaflow 中的流)。例如,下面的类只有一个测试,一个来自我们库的例行程序,运行定量检查(覆盖率,受欢迎程度偏差等)。):

一个简单的 RecList,其中一个测试重用了提供的标准库。

由于行为测试通常会随着用例(补充建议不同于类似项目)和数据集而变化,因此 RecList 的一个实例会类似于 CoveoCartRecList ,即针对电子商务研究的 Coveo 数据集的补充建议的一组测试。

由于采用了模块化设计,开始使用新的列表非常快:该库附带了许多预制的测试,您可以将这些测试与任意代码混合使用以进行自定义检查,这使得一致地重用业务逻辑变得非常容易。

了解如何使用隐士的最简单方法是浏览一个现成的例子,或者在你的浏览器中运行提供的 Colab 笔记本:

运行一个现成的 RecList 电商推荐 (例子上 电影 歌曲 也可用)

让我们来分解一下:

  • 第 1–3 行包含从库中导入的内容
  • 第 8 行正在获取 Coveo 数据集。提示:您可以在这里用您的数据集交换您的私有数据。
  • 第 12–13 行从库中实例化一个基线模型,并用训练分割对其进行训练。提示:您可以训练自己的模型(或者带一个预训练的模型)并在此处替换基线。只要你的模型围绕着 RecModel 抽象,下游的一切都会正常工作。
  • 第 17–20 行实例化 RecList ,在这种情况下,这是一个为该数据集和用例设计的现成列表。正如所料,该类将目标数据集和要测试的模型作为输入。提示:您可以在这里指定您自己的测试集合。
  • 22 线运行行为测试。

就是这样!挑选和组合像乐高积木一样的抽象概念——无论是重复使用组件还是自己创造新的积木——都有无限的可能性:

  • 挑选一个用例(类似的产品,互补的,等等。);
  • 挑选一个数据集;
  • 训练一个模型;
  • 建造/修改/选择一个隐士:

Reclist 的逻辑流程。在每一步中,你可以选择一个现成的组件,从头开始创建自己的组件,或者修改现有的组件。【图片由作者提供】

当您运行任何 RecList 时,库会将结果转储到一个版本化的本地文件夹中;还提供了一个小型 web 应用程序来可视化和比较运行情况:

一个小烧瓶应用程序使结果更易于阅读:查看repo了解详情。[图片由作者提供]

我应该如何使用 RecList?

虽然我们开发RecList 是出于将 RSs 扩展到许多行业的数百个组织所涉及的非常实际的需要,但我们也相信这种方法可以广泛应用于错误分析和新研究模型的全面评估。特别是,我们鼓励您思考两个主要的使用案例:

  1. 作为研究的实验工具:在开发一个新模型时,你可能想将它与定量的和行为的基线进行比较,以更好地了解错误模式及其相对优势和劣势。Reclist 允许你轻松地做到这一点,并带有现成的流行研究数据集的连接器;
  2. 作为生产的 CI/CD 检查:在训练一个模型之后,您通常在一个测试集上运行一个定量的基准,以确保在部署之前达到一定的准确性水平。使用 RecList,您还可以用行为测试来补充您的管道,并决定当结果与预期不符时,应该发出哪个标志/做什么。

RecList 是一个免费的库,我们希望在今年对其进行扩展和改进:我们的 alpha 版本已经支持常见用例的即插即用测试,但我们正在积极寻找反馈、采用和新的贡献者(请参见最后的帮助部分!);这也意味着:在我们迭代的时候,API 仍然很不稳定,但是我们有希望很快发布一个测试版。

我们认为,随着我们对推荐系统的理解的提高,行为测试需要不断发展,它们的局限性、能力和范围也会发生变化。

通过开源 RecList,我们希望帮助该领域超越“排行榜追逐”,并为从业者提供更好的分析、调试和决策工具。

你能帮上什么忙?

如果你喜欢这个帖子,请考虑帮忙!怎么会?一些想法,按努力的升序排列:

  • 1 分钟:给 Github 上的隐士加一颗星支持我们!
  • 1 小时:阅读论文,自述,运行教程:你能给我们什么反馈?
  • 1 周:与您的团队讨论:您将如何在您的公司使用它?您可以用它运行什么 POC?
  • 更多:积极为项目做贡献:取得联系!

奖励:在社区内发展

虽然我们非常精通产业与学术界的合作,但《隐士是我们第一部来源于社区的学术作品。阿尔法版本是由五个人带着爱带给你的——T4·帕特里克、费德里科克洛伊布莱恩我自己——在五个城市——蒙特利尔、米兰、旧金山、首尔、纽约——跨越四个时区。在作者之间总共 5(5–1)/2 =10个可能的面对面匹配中,只有 1 个实际发生过(如果你能猜出是哪一个,你将赢得一个免费版本的 RecList!).

虽然现在说这种模式对我们团队的未来会有多成功还为时过早,但我们的“扩展思维”开发到目前为止已经超出了我们的预期。

致谢

我们要感谢 Andrea Polonioli、Ciro Greco 和 Jean-Francis Roy(以及其他许多人)对这个项目的一贯支持。此外,感谢 Matthew Tamsett、Jonathan Davies、Kevin No 对本作品初稿的详细反馈。

最后但同样重要的是,感谢芯片,因为它创造了惊人的不和谐,启动了隐士社区。

最近邻丢失的图像显示

原文:https://towardsdatascience.com/nearest-neighbor-missing-visuals-revealed-63060215c2b3

如何用最先进的视觉效果分析和解释 KNN 结果

KNN 视觉效果(图片由作者提供)

无监督的 K-最近邻(KNN)算法可能是最简单的机器学习算法。然而,简单的算法并不意味着分析结果也同样简单。根据我的研究,并没有很多分析 KNN 算法结果的方法。在本文中,我将向您展示如何分析和理解无监督 KNN 算法的结果。

我将使用汽车数据集。这里显示了一个样本数据集。这些数据包括汽车的构造、不同的技术特征,如燃料类型、长度、宽度、车门数量等。以及汽车的价格。该数据大约有 25 个字段,其中大约有 15 个数值字段。

汽车样本数据(图片由作者提供)。

数据分为两部分——训练和评分。训练数据集用于训练 KNN 模型。然后,使用该模型来寻找评分数据集中数据的最近邻。

这里的图片将有助于理解 KNN 的结果。

可视化最近邻居的网络图

可视化最近邻居的一个优雅方法是使用网络图。评分数据集中的数据是一个中心节点,并与其最近的邻居相关联。

网络图(图片由作者提供)

此外,可以添加悬停工具提示来查看节点背后的细节。这样可以很好地理解分数数据中特定记录的最近邻。

悬停工具提示(作者图片)

使用图表分析的力量

由于网络图基于图形分析,您还可以分析邻居之间的连接方式。这有助于找到一个社区的邻居以及孤立的邻居。

最近邻输出的图形分析(图片由作者提供)

将最近邻算法与图形分析相结合是了解整体结果的强大工具。

使用 PCA 和聚光灯了解邻居兼容性

在现实生活中,一个人可以有好邻居,也可以有坏邻居!同样,KNN 可以识别最近邻,但是,这并不意味着最近邻总是相似或相容的。

我们可以使用 PCA 和聚光灯技术来验证这种“邻域兼容性”,如下所示。我们对所有训练和评分数据使用 PCA 来将数据减少到二维。降维数据用散点图绘制。然后,我们可以使用聚光灯技术来突出显示评分数据集中特定记录的最近邻。关于聚光灯技术的更多信息,请参见我的文章这里

分数记录 2 的最近邻分析(图片由作者提供)

上面显示的是得分记录 2 的所有最近邻。你会观察到所有的点都彼此相对靠近。这意味着最近的邻居彼此相对兼容,因为它们具有或多或少相同的特征。更进一步,检查显示大部分的车都是尼桑,这也证明了我们的观察。

现在让我们对得分记录 6 进行同样的分析,如下所示。

分数记录 6 的最近邻分析(图片由作者提供)

你会发现邻居彼此相距较远。这意味着最近的邻居彼此不太兼容。观察圆点后面的汽车,我们可以看到它是奥迪、沃尔沃和大众旅行车的混合体。因此,即使这些点被归类为最近邻,这些车也是互不相同的。

结论

概括起来

  • 网络图和图形分析是可视化 KNN 无监督算法结果的绝佳方式
  • 使用主成分分析和聚焦技术,可以分析最近邻的兼容性

观看演示并亲自尝试

你可以访问我的网站,使 KNN 分析以及其他没有编码的分析:【https://experiencedatascience.com】T2

这是我的 Youtube 频道上的一步一步的教程和演示。您将能够使用零编码根据您的数据定制演示。

Youtube 视频链接(作者图片)

订阅,以便在我发布新故事时随时获得通知。

https://pranay-dave9.medium.com/subscribe

你也可以通过我的推荐链接加入 Medium 。谢谢你。

https://pranay-dave9.medium.com/membership

数据源引用

数据来自https://archive.ics.uci.edu/ml/datasets/automobile

Dua d .和 Graff c .(2019 年)。UCI 机器学习知识库[http://archive . ics . UCI . edu/ml]。加州欧文:加州大学信息与计算机科学学院。

最近邻是 KNN、光学、DBSCAN、HDBSCAN 和 SMOTE 的基础

原文:https://towardsdatascience.com/nearest-neighbors-is-the-foundation-for-knn-optics-dbscan-hdbscan-smote-eeb10ea956e9

最近邻之旅及其衍生算法

Frogly Neighbors,(蛙趣屋居民),Alexas_Fotos, Pixabay

有各种算法是建立在其他算法的基础上的。下面的文章关注最近邻(NN ),其他模型在其概念或代码的基础上利用和创新了它。

无监督的最近邻居

让我们从基础模型开始,即在 Scikit 中实现的无监督最近邻。此算法用于判断训练数据中的实例是否与您要测量的点 k 最近。它是用于计算神经网络的不同算法的接口,如 BallTree、KDTree。如下图所示,它继承了 KNeighborsMixin、RadiusNeighborsMixin、NeighborsBase。

邻居分类器类实现。

最近邻的“算法”参数。

k-最近邻

继续使用每个数据科学家都会遇到的用于分类的 K 个最近邻,并使用上述方法,根据其 K 个近邻来确定一个未见过的样本是否属于某个类别。

如下面的代码所示,KNN 继承了相同的基类“KNeighborsMixin,&radiusboresmixin”,但是顺序不同;在文档中,它在内部使用了 fit()的 NeighborsMixin 和 predict()的 neighborsmixin 实现。

KNeighborsClassifier 类的实现,KNN,Scikit-learn。

KNN 的“算法”参数。

基于密度的噪声应用空间聚类

继续讨论 DBSCAN ,这是一种基于密度的聚类算法。简单来说,DBSCAN 寻找高密度样本的集群。我们可以在这里找到 DBSCAN 的代码,在文档中已经可以看到 DBSCAN 的内部算法指向了 NN 模块。

深入挖掘,我们看到 DBSCAN 的代码在内部使用了‘nearest neighbors’模块,如下面快照中的 DBSCAN 的 fit()函数代码和文档所示。

最近邻用法,DBSCAN,Scikit-learn。

DBSCAN 的“算法”参数。

光学

Optics 与 DBSCAN 密切相关,同样,它发现高密度区域并从中扩展集群,但是,它使用基于半径的集群层次结构,Scikit 建议在更大的数据集上使用它。这种光学实现在所有点上使用 k-最近邻搜索

在报告内部,我们看到 Optics 的代码在内部依赖于“最近邻居”模块及其算法,如 Optics compute_optics_graph()函数代码和下面的文档快照所示。

最近邻用法,compute_optics_graph(),optics,Scikit-learn。

光学“算法”参数。

HDBSCAN

HDBSCAN 通过将 DBSCAN 转换为层次聚类算法来扩展它,然后在其上使用平面聚类提取。

我们可以在 API 引用代码中看到一些指向 KNN 的线索

类中的 K 参数 **hdbscan.robust_single_linkage_.RobustSingleLinkage()**

深入到代码中我们可以看到,在下面的代码中,函数 _rsl_prims_balltree 实际上是基于 balltree 的,而 _rsl_prims_kdtree 是基于 kdtree 的,它们是 Scikit-learn 中用来计算 NN 的算法。

KDTree,prims_balltree,HDBSCAN

BallTree,prims_balltree,HDBSCAN

重击

合成少数过采样算法(SMOTE)的不平衡学习实现,它不直接使用 Scikit-learn NN 类实现,但使用 NN 概念。我们可以浏览一下代码和文档,立即发现 generate_samples() 函数中的 k_neighbors 参数。

k_neighbors 参数,SMOTE,不平衡-学习。

摘要

最近邻算法,由 Evelyn FixJoseph Hodges et 开发。艾尔。在 1951 年,后来由 Thomas Cover 在 1967 年扩展,对上述所有实现都至关重要。我们还可以看到,Scikit-learn 的各种算法实现从头到尾都在重用这些代码,而且其他各种包也使用了 NN 算法,以便为我们带来更高级的模型。

我真诚地希望这篇综述能让你理解所有这些算法之间的联系和关系,希望能帮助你用众多的算法方法解决更多的问题。

1修复,伊芙琳;约瑟夫·霍奇斯(1951)。歧视性分析。非参数判别:一致性性质 (PDF)(报告)。德克萨斯州伦道夫机场美国空军航空医学院。

[2] 盖,托马斯·m .;彼得·哈特(1967)。“最近邻模式分类” (PDF)。 IEEE 汇刊于

Ori Cohen 博士拥有计算机科学博士学位,主要研究机器学习。他是 ML & DL 纲要StateOfMLOps.com的作者,对 AIOps & MLOps 领域很感兴趣。他是 Justt.ai 的数据科学高级总监。

具有核心 PySpark 的时序聚合

原文:https://towardsdatascience.com/neat-time-series-aggregations-with-core-pyspark-4a739953076a

使用核心 PySpark 的移动窗口聚合策略和使用 Plotly 的可视化

https://media . springer nature . com/lw 660/springer-CMS/rest/v1/img/19125576/v3/4 by 3?as=jpg

有**吨的时间序列指标,其中许多指标有相同的预处理步骤和用例。为了限制冗余,我将关注三个不同用例的简洁指标:

  • 使用滚动 Z 值检测异常值
  • 滚动相关矩阵
  • 指数移动平均线趋势检测

在第一部分中,给出了滚动窗口和采样周期的定义。这些公式为如何开始创建这些移动窗口度量提供了有用的上下文。也可以随意跳到情节和代码:)

正如所有与数据有关的事情一样,有不止一种方式来烘焙您的数据。这里有一点关于我的背景和编程风格:我来自一个使用大量 SQL 的工作环境,我的聚合策略反映了这一点。因此,我没有使用 PySpark 表达式或内置库,也就是说,如果没有它们我也能完成任务。

本文中的代码在本地 PySpark 环境中进行了测试,并连接到 S3 数据源。这里是一篇关于如何在 Windows 10 上设置这个测试环境的简短教程文章。

所有的方程式、图表和图形都是我创造的。所有的绘图代码都可以在这里找到。我在所有的情节中使用了 Plotly。

移动窗口和采样周期

时间序列指标通常受某个时间窗口的限制。可以有不同的粒度或采样周期来计算度量,即 10 分钟、1 小时、1 天等。我将使用的描述本文剩余部分中的度量的语法显示在等式(0)中。

P: (1xM)矩阵
K:采样周期(10 分钟、1 小时等)
M:回顾窗口的大小
N:跨时间序列的当前整数索引
n:跨时间序列的整数索引
t:时间段内的整数索引

例如,如果采样周期(K)为 10 分钟:

t=0,n=1,1 * K = 10 分钟
t=1,n=2,2 * K = 20 分钟
t=2,n=3,3 * K = 30 分钟

https://gist . github . com/freedomtowin/6 f1 ff 6 CBF 2 a 76d 08d 8 CB 90685 EC 9075 f

时间块 t 代表一个大小为 m 的时间窗口。在聚合每个时间窗口后,时间索引 N 递增 1。度量在 t 上聚合,N 跟踪每个窗口的全局时间索引。**

使用滚动 Z 分数的异常值检测

滚动 z 分数阈值可用于检测时间序列中的大跳跃或间隙。这有许多潜在的应用,例如,为大的异常值创建警报系统。

z 分数或标准分数表示平均值的标准偏差数。如果你是统计学新手,你可以在这里阅读更多关于这个指标的信息。这个指标可以被认为是移动平均线附近的一个置信区间。

将需要简单移动平均和简单移动标准差来创建 z 得分,分别如下面的等式(1)和(2)所示。

上面的公式显示了这两个指标是如何在 M-1 大小的滚动窗口 t 的时间步长 N 上定义的。最后一个时间步长 M 从聚合时间窗口中排除,因为目标是将当前时间序列值与前一个窗口的平均值和标准差进行比较。如果将当前时间步长包括在内,标准偏差可能会大大增加,该指标将无法检测时间序列中的大变化。

在我们执行这些滚动窗口计算之后,可以计算 z 得分上限和下限,分别如等式(3)和(4)所示。

在这些等式中,z 得分上限和下限分别设置为平均值上下的两个标准差。假设正态分布,经验法则表明我们期望 99.7%的数据在标准偏差范围内。

下图显示了 ATVI 每小时股价的 10 小时移动平均线的 z 值上限和下限。

https://gist . github . com/freedomtowin/6 f1 ff 6 CBF 2 a 76d 08d 8 CB 90685 EC 9075 f

阴影绿色区域表示移动平均线周围的置信区间,黄线表示时间序列,绿线表示时间序列的移动平均线。在某一点上,在置信区间之外有一个急剧的下降,这表明有一个大的异常值。标准偏差随后在 N=130 左右大幅增加。在时间序列跨越置信区间的点上,变化不是很显著。对于这个特定的时间序列,可以使用更大的 z 分数阈值。

注意:数据集经过预处理,包含工作日上午 9 点到下午 4 点之间的每小时数据。为了便于绘图,这些值是相对于指数而不是时间绘制的。这消除了图中的一些不连续性。

PySpark 代码:

首先,创建一个分级索引来执行自连接,以便为每个时间序列(即股票符号)获得一个按时间排序的历史窗口。然后,对于视图(a)中的每个值,在视图(b)中聚合移动平均值和移动标准偏差。从这些度量中,可以计算出 z 得分阈值的上限和下限。

滚动相关矩阵

滚动相关矩阵有助于将相关的时间序列组合在一起,帮助找到不同的、不相关的时间序列,或者检测相关模式的变化。我对你的挑战是在不使用内置 PySpark 函数的情况下创建这个;)

归一化协方差,或皮尔逊相关系数,描述了两个变量之间的相关性。该指标限制在-1 和 1 之间,这是在时间序列对之间比较该指标时的一个有用属性。我们可以通过滚动时间窗口计算这种相关性。

滚动相关性也可以用指数移动平均函数来平滑。平滑滚动相关的另一个术语是“实现的相关”

其中, r 是我们的时间序列数据集中 I 列和 j 列的相关性,μ是简单移动平均值,σ是简单移动标准差。

我将使用我可靠的股票数据集来绘制 DoorDash 的小时股价与其他股票(如 ATVI、DIS、NVDA 和 WMT)的滚动相关性。

https://gist . github . com/freedomtowin/6 f1 ff 6 CBF 2 a 76d 08d 8 CB 90685 EC 9075 f

与其他时间序列相比,DASH 和 NVDA 之间的相关性在更长的时间段内似乎是一致的。同时持有这两只股票可能会增加投资组合的风险。

注意:在上图中,当任一时间序列缺少当前时间步长 n 的数据时,就会出现缺失数据点。例如,DIS 缺少 2021 年 8 月至 2021 年 11 月之间的数据。但是,关联窗口将跳过空值,过去 14 小时没有丢失的值将用于聚合。

PySpark 代码:

第一步是计算数据透视表,按时间分区,按时间序列 id、股票代码分组。该数据透视表采用时间序列的平均值, close ,但是由于数据集被预处理为每小时一个值,因此最小值、最大值、第一个值或最后一个值也可以作为聚合。

对于每对时间序列,在计算滚动相关性之前,将删除任一时间序列中具有空值的行

下一步是创建不重复的时间序列对或组合。从每个组合中删除具有空值的行。最后,我们需要计算每对时间序列之间的相关性。幸运的是,PySpark 中有一个内置函数可以计算两列之间的相关性。滚动相关性然后被左连接回数据透视表。

使用指数移动平均线进行趋势检测

差分指数移动平均线可用于检测时间序列的速度或趋势。这种方法可以在差分时间序列上第二次应用,以检测加速度,或趋势的变化率。

指数移动平均线(EMA)可以看作是一个过滤器,它更重视聚合窗口内的最新值。EMA 有两个主要参数,即 alpha 衰减率和窗口大小。时间窗口越大或者 alpha 越低,均线移动的越慢。换句话说,移动平均将比实际时间序列落后更多。

本节使用的趋势检测方法通常称为 MACD 。然而,我将用新的,在我看来更容易理解的术语来解释这个算法。

通常,α通常被选择为等于 2/(1+M),其中 M 是窗口大小。α的这个值对应于平均加权影响,或“质心”,对应于简单移动平均(SMA)的回顾窗口的中心。换句话说,对于高度自相关的信号(比如股票价格),均线会落后于实际价格大约一半的时间。详情请见Robert Nau 的预测笔记

据我所知和谷歌搜索,如果不使用用户定义的函数或 UDF,在 PySpark 中实现递归方法(如 EMAs)是不可能的。幸运的是,有一个扩展的级数表示,可以用来计算核心 PySpark 中的 EMAs。等式(6)以扩展的符号显示了指数移动平均公式(EMA)。

通过取快速移动(时间窗较短)和慢速移动(时间窗较长)的均线之差,可以估算出趋势。直观上,这可以被认为是最近价格和以前价格之间的斜率或离散导数。这些均线之间的差异可以称为趋势的速度

趋势变化的速度也可以用 1)T4 速度(T5)和 2)均线(T8)来估计。

**信号速度之差可以认为是趋势加速度速度和信号之间的收敛代表趋势方向的反转。这种背离代表着一种越来越强的趋势。

https://gist . github . com/freedomtowin/6 f1 ff 6 CBF 2 a 76d 08d 8 CB 90685 EC 9075 f

顶部的图显示了时间序列(黄色)、慢速均线(蓝色)和快速均线(红色)。底部曲线显示了加速度(直方图)、趋势速度(红色)和信号(蓝色)。该图显示了短时间间隔内趋势的一致性。简单地说,可以设置加速度的某个安全阈值来检测强烈的上升和下降。

注意:这个图看起来有点起伏,因为我选择 K=3 作为采样周期。

PySpark 代码:

创建了一个单独的函数来计算大小为 m 的特定窗口的 EMA。在该函数中,创建了一个分级指数来执行自连接,以便为每个分区(即股票符号)获得按时间排序的历史窗口。历史窗口的指数和当前指数之间的差异用于为 EMA 公式的扩展序列表示创建 alpha 权重,如等式 8 所示。最后,对于视图(a)中的每个值,汇总视图(b)中的 EMA。

我们可以用均线函数来计算快速移动的均线,慢速移动的均线,快慢均线之差(速度),以及速度的均线(信号)。

结论

简而言之,本文展示了我如何在 PySpark 和 SQL 中聚合时间序列。我知道时间序列窗口和方程的正式定义使得这篇文章更难阅读。不过我喜欢彻底:)我也,大概,本来可以把这篇文章分成三篇。不过,希望 PySpark 和 Plotly 代码对一些新的数据科学家/工程师有用。

以下是一些您可以尝试的数据挑战:

  1. 在异常值检测算法中,使用指数移动平均代替简单移动平均。
  2. 编写自己的相关函数,而不是使用内置的 PySpark 相关函数。探索实施类似的指标,例如,协整%20of%20time%20series%20variables.&text=Formally%2C%20if%20(X%2CY,Y%2C%20and%20Z%20are%20cointegrated.)。
  3. 使用指数移动平均值创建平滑移动窗口相关矩阵。
  4. 针对 M1、M2 和 M3 的多个参数,描述趋势检测方法的有效性。

这是所有的乡亲。

速度需求:优化脸书-预言家拟合方法,运行速度提高 20 倍

原文:https://towardsdatascience.com/need-for-speed-optimizing-facebook-prophet-fit-method-to-run-20x-faster-166cd258f456

简化贝叶斯模型

这是优化 Prophet 内部机制系列文章的第二部分。第一部分不是理解这部分的先决条件,但推荐。

自从我发布了第一部,先知套装的下载量达到了 3000 万。这是最常见的时间序列预测软件包之一。尽管不是最准确的算法,但它开箱即用,效果相对较好,但仍允许微调其超参数,并添加额外的回归变量(即,可能与您的目标相关的任何数据,如油价、天气),这使它成为许多用例的良好基线算法。

但是它的速度仍然是一个问题。在第一部分中,我们看到预测可能需要整整一秒钟,但我们设法将这个时间减少到了 15 毫秒左右。尽管如此,根据变化点的数量和是否使用季节性,拟合一个项目需要 50-100 毫秒。这些运行时将 Prophet 的可扩展性限制在相对较小的数据集,因为每个项目都需要单独进行调整。但它不必如此;就像预测一样 Prophet 的拟合方法可以通过几个技巧进行显著优化。

但在我们继续之前,一个免责声明:在第一部分的结尾,我提供了可以取代预言家的不确定性预测的代码;在这篇文章中,我将展示如何优化拟合,但编写完整的功能代码,包括 Prophet 可以接受的所有参数,需要更多的工作。但是在这个过程中,我们将探索时间序列建模中的新思想,更好地理解幕后的预言家,甚至学习一点贝叶斯统计。

先知入门

Prophet 模型是一个加法回归模型,有三个主要部分:

趋势、季节性、节假日(我们可以添加其他回归变量,因为它们的功能是相同的)

先知模型组件(图片由作者提供)

我们将从关注趋势因素开始,随着我们对它的优化,我们将看到添加其他项并不是一个挑战。我们将局限于趋势是线性的情况。

预言家用变点拟合线性趋势(图片由作者提供)

如上所述,Prophet 为数据拟合了线性斜率,但为斜率创建了变点。当预测未来时,仅使用最终斜率,但变点的数量和大小会影响不确定性区间(见第一部分)。

变点数由用户设置(默认为 25),并且均匀分布在数据中,不包括最后的 20%。先知没有“找到”变点;它创造了许多变化点,并让拟合过程使用正确的。

由预言者放置潜在的改变点(图片由作者提供)

在上图中,垂直线代表由 Prophet 创建的变点,但是正如您所看到的,它们中的大多数不会导致斜率发生有意义的变化。Prophet 使用贝叶斯模型来查找数据的最佳参数(截距、当前初始斜率和斜率之间的差值)。由于贝叶斯模型的作用,大多数斜率之间的差值为 0。

贝叶斯先验和正则化

无需深究贝叶斯建模的美妙之处,这里有一些基本的东西:当使用贝叶斯方法估计参数时,你必须提供一个“先验”,它代表你在看到数据之前相信你的参数会在哪里。贝叶斯模型采用这个先验和数据,并返回“后验”——这些参数值最可能位于何处的更新信念。

对于 Prophet 中的斜率增量,使用的先验是拉普拉斯,如下所示:

平均值为 0 和不同标准差的拉普拉斯分布(图片由作者提供)

换句话说,Prophet 告诉模型,它应该假设两个连续斜率之间的差值可能在零左右,除非数据高度表明斜率发生变化,否则没有差值。Prophet 创建了许多潜在的变化点,但在拟合它们的参数时,它确保通过使用以零为中心的先验,大多数变化点将被忽略。这个想法就像在损失函数中添加一个正则项——它促使优化器倾向于小值。

修改拉普拉斯的比例会改变其对结果的影响:较小的标准偏差意味着我们确定大多数增量必须接近零,而较大的标准偏差意味着非零值也是可能的,因此模型不会将增量推向零。同样,就像调整线性回归中正则化的强度一样。在 Prophet 中,您可以使用 changepoint_prior_scale 参数调整拉普拉斯先验标准差。如下所示,它可以显著地影响模型实际使用的变点的数量。

拉普拉斯先验对变点拟合的影响(作者图片)

Prophet 使用 pystan,一个贝叶斯建模库,来寻找最佳增量。但是贝叶斯建模需要非常长的时间来拟合(几十毫秒!).如果我们可以用简单的线性回归来代替它,我们可以在不牺牲性能的情况下减少运行时间,增加算法的可伸缩性。

模拟坡度

为了用线性回归模型代替 pystan,我们需要找到一种使用特征来表示斜率和斜率变化的方法。

为了模拟单个斜率,我们可以创建一个线性要素,其值为 1,2,3,4,5,…,n,对应于手头的时间序列,其中 n 是时间序列的长度。如果我们在线性回归中使用这个特性,很容易想象它能完成什么。

作为线性回归特征的简单线性趋势(图片由作者提供)

仅使用线性趋势拟合的时间序列(作者图片)

线性回归找到最佳系数——代表这个斜率应该有多陡;回归也可以产生一个负系数的向下斜率。我们可以很容易地将这个特性添加到其他表示季节性和额外回归量的特性中。

但这是一个单一的斜坡。我们能创建一个特征来表示坡度变化吗?这个怎么样:

线性趋势变点特征(图片由作者提供)

新的特征值是 0,0,0,0,…,0,1,2,3,4…这可能会让你想起 Relu activation 函数,但这里是作为一个特性,而不是作为一个函数。

其系数表示变点前后斜率之间的差值。加上初始坡度特征,您可以创建此形状:

寻找线性趋势和变点特征的系数的线性回归。每个特征乘以其系数(左)。他们的总和(右)。图片作者。

我们可以创建任意数量的这些变点特征,允许回归在每个变点采用新的斜率。

“梳状”特征-固定间隔的变点特征和基于特征的拟合(图片由作者提供)

我们称之为“梳子”特征。我们可以通过训练 Prophet 并使用其训练的增量作为这些特征的系数来确认这些特征表示与 Prophet 相同的增量。

给了我们:

将预言者的 delta 系数与梳状特征一起使用,得到与预言者相同的结果(图片由作者提供)

梳状特征乘以 Prophet 找到的增量,然后相加得到完全相同的预测。我们已经表明,我们可以使用“梳子”将 Prophet 转换成线性模型。

但这不是我们想要的——我们想要自己训练那些 deltas 系数,没有 pystan。我们能使用线性回归模拟拉普拉斯先验吗?

没有先知的训练先知

回想一下,之前的要点是将系数推向 0。这可能会让你想起 L1 正则化——惩罚系数的绝对值——这不是没有原因的:可以证明具有拉普拉斯先验的贝叶斯模型的最大后验(MAP)估计等价于具有 L1 正则化的线性回归(也称为 Lasso 回归)。拉普拉斯先验是同一概念的贝叶斯解释。贝叶斯主义者不考虑成本函数,而是考虑先验知识。

因此,我们可以在梳状特征上训练回归,并得到与 Prophet 完全相同的结果。正如我提到的——标准差决定了模型将系数“推向”零的力度,就像 L1 阿尔法一样,但相反:标准差越大意味着正则化程度越低,而阿尔法值越大意味着正则化程度越高(系数越高,则“成本”或“损失”越大)。我们所需要的是将拉普拉斯先验的标准偏差(在 changepoint_prior_scale 中给出)转换成等效的 L1α的公式。

从 b(拉普拉斯标准)到 L1 正则化参数的转换公式(图片由作者提供)

其中 b 是拉普拉斯的标准偏差,sigma 是模型预测误差的标准偏差。

等等,什么?在我们训练一个模型之前,如何使用误差的标准差?我们不能,但我们可以估计一下。让我们假设它是 0.3,用这个转换运行套索:

Prophet 和 comb-Lasso 训练与估计 sigma 的比较(图片由作者提供)

不坏,但不完全是;我们的阿尔法不符合拉普拉斯先验,因为我们的西格玛估计是错的。但现在我们有了一个预测,我们可以更好地估计误差,并重新计算正确的阿尔法。我们可以迭代地重复这个过程:创建一个模型,估计误差的标准偏差,并重新计算 alpha。

通过使用 changepoint_prior_scale,我们将得到以下比较结果:

Prophet 和 Lasso 结果的比较,给定 prior_scale 中的更改(图片由作者提供)

正如你所看到的,我们可以创建一个几乎相同的符合先知,使用梳子的特点和这种转换从拉普拉斯标准。您可以对先前的 std 使用不同的值进行实验,并看到 Prophet 和 Lasso 都会相应地更改其正则化并保持相似。但是训练几个套索难道不比训练一个先知慢吗?没有。

%%timeit
m = Prophet(n_changepoints=15, changepoint_prior_scale=changepoint_prior_scale, growth='linear', uncertainty_samples=None, yearly_seasonality=False)
m.fit(ds)

输出:

60.2 ms ± 658 µs per loop

Prophet 需要 60 毫秒来拟合时间序列。我们的迭代套索呢?

%%timeit
lr = lasso_by_laplace_prior(feat, ds.y / scale, changepoint_prior_scale)

输出:

11.2 ms ± 239 µs per loop

即使 5-10 个套索也比 pystan 的优化器快得多。事实上,如果你分析套索拟合,你会发现每次拟合花费的大部分时间都花在预处理 X 和 y 上(例如归一化它们)。目前,预处理发生在每次迭代中,但它可以放在循环之外,只发生一次,这将减少套索训练时间至少 50%。

如果我们不是特别关心模仿 Prophet,而只是想对时间序列建模,我们可以简单地使用 comb 特性,添加季节性特性和其他回归变量,并设置一个单一的 alpha。毕竟,先验的 alpha 和标准差都是任意值(可以使用交叉验证进行优化),为什么我们要关心找到精确匹配呢?我想不出任何原因,除了它给了我们一个学习机会,让我们了解预测、正则化和时间序列建模。所以,假设我们想要精确地模仿 Prophet,但是更快,还缺少什么?

特定特征正则化

如果你仔细观察上面的例子,你会注意到,与 Prophet 相比,Lasso 的初始斜率没有那么陡。原因是 Prophet 仅将拉普拉斯先验(或 L1 正则化)置于斜率之间的变化δ上,而不是初始斜率上。我们的套索模型调整了所有的特征,包括初始坡度,所以它不能完美地模仿它。

在我们继续之前,我想指出先知的建模在某种意义上有点奇怪。举以下例子:

完全随机的数据符合线性趋势(图片由作者提供)

这个时间序列的值是完全随机产生的,但是你可以发现一些斜率完全是由于偶然。但是如果我们想要预测这个随机序列的未来,使用斜率会导致过度拟合,而最好的预测就是这些值的平均值。先相信根本没有斜率是有意义的,只有在斜率非常明显的情况下才相信没有斜率,这不太可能随机发生。

Prophet 实际上在初始斜率上放置了一个先验(在任何贝叶斯模型中,技术上你都必须这样做)——先验是高斯分布,而不是拉普拉斯分布。

std=1 的拉普拉斯和高斯分布(图片由作者提供)

高斯曲线比拉普拉斯曲线更平坦,因此它对零的限制更少,Prophet 用于高斯先验的标准差是 5。由于数据的最大值为 1,初始斜率通常低于 5。换句话说,Prophet 在技术上在初始斜率上放置了一个先验,但它是一个“无信息先验”——它几乎没有限制其系数的值。正如我们刚刚看到的,不调整初始斜率可能会有问题。

但是让我们继续效仿 Prophet:如果 L1 正则化等价于拉普拉斯先验,那么什么等价于高斯先验?你猜对了——你可以证明L2(惩罚平方系数)将产生与高斯先验相同的地图。因此,我们需要对不同的特征使用不同的正则化参数来仿真 Prophet。遗憾的是,sklearn 不允许这样做-相同的正则化值必须应用于所有要素。我们将解决这个问题,但在此之前,让我们谈谈季节性和 Prophet 中的其他组件。

季节性和额外回归量

Prophet 中的季节性是使用正弦波特征建模的。Prophet 自动创建一组不同频率的正弦波来代表每周、每月和每年的季节性。Pystan 找到正弦波的最佳系数,使它们的总和最符合数据。作为傅立叶级数,这些正弦波非常灵活,适合大多数季节性形状。

使用正弦波特征训练时间序列(图片由作者提供)

他们的先验/正规化呢?Prophet 对季节性要素使用高斯先验,您可以使用 Prophet 中的 seasonality _ prior _ scale 参数设置其比例。默认的等级是 10,这也意味着——几乎没有规律可言。节假日和额外回归量被视为相同-默认为宽高斯先验。

把所有的放在一起

我们基本上完成了,我们所需要的是编写一个带有自定义损失函数的线性回归,该函数可以针对不同的要素采用不同的正则化参数-L2 用于季节性和初始坡度,L1 用于梳状要素。正则化损失为:

成本函数的正则化部分(图片由作者提供)

其中:

  • σ是误差的标准偏差
  • b 是变点先验标度(变点的拉普拉斯先验标准)
  • s 是季节性先验标度(季节性的高斯先验标准差)
  • β_fs 是第一个斜率的系数(线性上升特征)
  • β_cp 是梳状特征的系数
  • β_s 是季节系数

正则化损失被加到平方误差损失上以获得代价函数。

然后,我们使用 Adam 等梯度下降优化器来最小化成本。我们可以将 sigma 估计合并到流程中,而不是像 Lasso 那样迭代运行这个模型:优化器每 k 次迭代重新估计 sigma。

请注意,梳状和季节性正弦特征仅取决于时间序列的长度。这意味着除非添加额外的回归变量,否则该模型中的所有特征都不是特定于目标的。我们可以为整个数据集创建一次 X。

我创建了一个 PyTorch 实现,它可以采用 L1 和 L2 正则化参数或拉普拉斯和高斯先验的标准偏差每个特征。因为只优化 20-30 个参数不是 PyTorch 的强项,所以这种实现采用包含多个项目的整个数据集,并同时但单独地估计每个项目的系数,这对于每个项目来说要快得多。用于将该模型的结果与原始预言家的结果进行比较的代码和示例脚本可以在这里找到。

这里有几个适合的例子:

Prophet 预测和 PyTorch 实现的拉普拉斯模拟特定特征正则化的比较(图片由作者提供)

该实现和原始 Prophet 之间的平均 Pearson 相关是 0.995。由于实现中的微小差异(我不会深入讨论,但它们可以通过更多的工作来减少),以及梯度下降优化器的随机特性,它仍然有点偏离。无论如何,没有理由认为新的实现不太准确。

我们的模型模拟了贝叶斯模型,使用简单的线性回归,每个特征正则化。但是 Prophet 的运行时间大约是每个项目 60 毫秒,PyTorch 实现的运行时间大约是每个项目 3 毫秒。可以做更多的事情来减少运行时间,当损失函数收敛时提前停止,并学习速率调度程序,但这超出了我们的范围。

结论和要点

在本系列的第一部分优化预测代码中,我提供了可以轻松替换 Prophet 原始预测代码的代码。但是对于 fit 来说,情况要复杂得多:一个完整的实现需要能够添加额外的回归量和假日,并自动检测是否使用每周、每月和每年的季节性,这可以从 Prophet 复制过来。产品化之前还需要做更多的工作,但都是技术性的。如果有人经常使用 Prophet 承担这个项目是值得的。

那么,这个练习的目的是什么?

首先,“梳状”特征作为一种创造性的建模斜率变点的方法可以被其他时间序列模型借鉴。

其次,我希望这种解决问题的形式,在研究 Prophet 的具体机制时,贝叶斯回归和正则化的贝叶斯解释是有启发性的,所学到的经验教训可以帮助解决其他问题。

最后,这足以证明我的座右铭:任何模型,可以是线性回归应该是线性回归。线性回归是优雅的,令人难以置信的快,并且尽可能的容易解释。许多数据科学家经常急于使用更复杂的模型,忽略了线性回归的力量,而线性回归通常也能达到同样的效果。在这种情况下,只需要一点创造性的特性工程就可以将 Prophet 简化为线性回归模型,并显著提高其运行时间。

我们将何去何从?

进一步研究的一个领域是斜率变化点的自动检测。一个好的斜率变化检测算法可以同时提高精度和运行时间,而不是创建大量的变点并依靠正则化来避免过度拟合。

在本系列的第一篇文章中,我们发现拟合过程需要 60-100 毫秒,而预测可能需要整整一秒钟;然后,我们将预测减少到大约 10 毫秒。进一步优化预测方法没有多大意义,因为大部分时间都花在了拟合上。

现在 fit 减少到大约 3 毫秒,它再次成为消耗大部分运行时间的预测方法。在本系列的第 3 部分中,我们将回到我们在第 1 部分中开发的矢量化预测方法,看看我们如何在几微秒内实现类似的结果,完成 fit & predict 的 1000 倍运行时改进。

posted @ 2024-10-18 09:32  绝不原创的飞龙  阅读(327)  评论(0)    收藏  举报