TowardsDataScience-博客中文翻译-2021-十五-

龙哥盟 / 2024-10-22 / 原文

TowardsDataScience 博客中文翻译 2021(十五)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

用自然语言处理分析印度诗歌

原文:https://towardsdatascience.com/analyzing-hindu-verses-with-nlp-25d72986703b?source=collection_archive---------30-----------------------

使用 Python 对“毗湿奴”和“魔鬼”斯洛卡进行分类

布达扬·巴德汉在 Unsplash 上拍摄的照片

“文本分类”是一种机器学习技术,用于分析文本,然后根据模式或结构对其进行组织或分类。文本分类在人工智能领域有很多 应用 比如新闻文章分析、仇恨言论识别、性别分类等。在这篇文章中,我使用“文本分类”和使用 Python 的自然语言处理(NLP)来分析印度教经文并对它们进行分类。在我们更深入地研究 Python 的技术方面之前,让我们快速地看看我们将处理哪些数据。

“Sahasranama”——字面上有 1000 个名字(其中“ sahasra ”的意思是 1000,而“ nama ”的意思是名字)——是一首赞美印度教神的圣歌。https://en.wikipedia.org/wiki/Lalita_Sahasranama【颂难近母女神】和 毗湿奴 【颂摩诃毗湿奴】就是这样两个颂难近母女神和摩诃毗湿奴的诗句,各有 1000 个不同的名字。我从以下链接中获得了用于我们分析的数据:'Lalitha Sahasranama'和'Vishnu Sahasranama',并对其进行了清理,删除了“om”和“namah”,并将数据保存为 文本文件

导入库:

第一步是导入我们分析所需的库。从下面的代码中可以看出,我们使用了 3 个不同的库。

*#Import required librariesfrom nltk.classify import accuracy #For accuracy predictor
import random #For randomly shuffling the names
import nltk #Natural Language toolkit*

NLTK —自然语言工具包

NLTK 准确性——用于显示我们预测的准确性

Random —用于为我们的训练集和测试集随机排列名称

加载数据和标记:

在“读取”模式下使用 open() 功能,然后使用 readlines() 功能将文件的内容分配给一个列表。

读取数据并标记它

下一步是将标签' Devi '和' Vishnu '分配给被读取的相应诗句。这个标签后面会用来给诗句分类。

标签分配的输出 Jupyter 笔记本的屏幕截图

random . shuffle()函数用于随机打乱列表,使数据均匀分布在两个列表中。

特征提取:

定义特征提取的函数

我们定义一个函数来从列表中传递的诗句中提取特征。定义的函数提取最后 2 个字符来开发模型。

从诗歌列表中提取特征 Jupyter 笔记本截图

使用朴素贝叶斯分类器创建模型:

图像专家:走向数据科学— 朴素贝叶斯分类器。什么是量词?|作者 Rohith Gandhi |走向数据科学

朴素贝叶斯分类器基于贝叶斯定理,该定理用于通过假设第二个事件 B 已经发生来找出事件 A 发生的概率。

第一步是将数据分为训练集(前 1000 节)和测试集(后 1000 节)。

检查最具信息性的特征和模型准确性

nltk . classify . accuracy()函数用于显示使用训练集或测试集作为参数的预测结果的准确性。

show _ most _ informational _ features(n)函数基于特征提取突出显示可用诗句的最具信息性的 n 个特征。

分类器精度和“显示大多数信息特征”的输出如下所示。这个分类器有将近 97%的准确率。我们也看到,与神摩诃毗瑟奴相对应的诗句通常以“ya”结尾,而与女神难近母相对应的诗句通常以“ai”作为最后两个字符。

我们的数据集最具信息性的特征 Jupyter 笔记本截图

检查分类输出:

检查分类输出 Jupyter 笔记本的屏幕截图

让我们检查分类输出,并将其与之前分配给诗句的原始标签进行比较。为此,我们创建一个空列表,然后使用(classifier . classify(name _ features(name)))函数比较模型的输出,并将其与之前最初分配的标签进行比较。如果我们有兴趣查看那些不匹配的,我们可以简单地替换“If”条件来查找输出,其中 model_output!=神。这里显示了一个正确分类输出的示例。

检查模型输出与标签

整个代码可以通过 Github 库 下载。

参考资料:

  1. 分类(bgu.ac.il) —使用 NLTK 进行性别分类的优秀范例
  2. Python |使用 NLTK 的性别识别— GeeksforGeeks
  3. 朴素贝叶斯分类器。什么是量词?|作者 Rohith Gandhi |走向数据科学
  4. nltk.classify 包— NLTK 3.6.2 文档
  5. Python 编程教程
  6. Python 的自然语言工具包(NLTK)教程第 3 部分|作者:Ishan Dixit | Medium

使用 QGIS 和 PostGIS 函数分析孟买的出租房屋

原文:https://towardsdatascience.com/analyzing-houses-for-rent-in-mumbai-using-qgis-and-postgis-functions-7383e4223d0d?source=collection_archive---------23-----------------------

基于 QGIS 的空间数据分析

作者图片

介绍

孟买是印度人口最多的大都市之一,人口持续增长,超过 2000 万。它也被认为是印度的经济首都,因为这里有大量的工业和组织总部。因此,来自全国各地的人们带着各种各样的学习和工作前景来到孟买,这使得房地产行业繁荣起来,出租或租赁的公寓价格飞涨。

在这个项目中,我们将尝试使用 PostGIS 和 QGIS 分析 2020 年孟买出租或出售的公寓,并根据一些预定义的条件得出结论。

动机

几个月前,我的表弟试图在市区内找到一套合适的公寓出售。她几乎没有条件将搜索范围从整个孟买缩小到一些特定的地点。条件如下。

  1. 她想了解这些房产的价格,以便决定她的预算。
  2. 公寓应该在孟买国际机场的 5 公里半径内(因为她在机场工作)。
  3. 公寓应该靠近海滩,面朝大海。
  4. 最后,她还想找到去孟买海军造船厂的最快路线,因为我的 BIL 在那里工作。

这激起了我的兴趣,我参与了这个项目,并为他们找到了一套符合她大部分标准的好公寓。

履行

从分析开始,我从 Kaggle 导入了孟买房价数据集。该数据集包含位于孟买市的各种公寓的详细信息。该数据集中可用列的描述如下。

  • 面积:房屋的建筑面积
  • 浴室数量:可用的浴室数量
  • 卧室数量:可用卧室的数量
  • 城市:物业所在的城市
  • desc:属性的文本描述
  • 开发商名称:房地产开发商的名称
  • floor_count:建筑物的总层数
  • floor_num:物业所在的楼层
  • 家具:家具状态
  • id:唯一的 ID
  • id_string:用于抓取特定 HTML 页面元素的唯一 id 字符串。
  • 纬度:位置的纬度
  • 地点:财产所在的地点
  • 经度:位置的经度。
  • post_date:酒店在网站上发布的日期。
  • 发帖人姓名:发帖人姓名
  • 价格:财产的价格
  • 项目:住宅区的名称
  • 标题:网站上物业广告的标题
  • 交易:财产交易的类型
  • 类型:住宅区的类型
  • URL:单个属性的 URL
  • user_type:发布广告的用户的类型

我已经将数据下载为 CSV 文件,然后用 python 编写了一个小程序来清理数据,并将其加载到 PostgreSQL 的一个表中。在这里可以找到笔记本。

我发现这些列中很少有空值,所以为了简化分析,我考虑删除这些列。在数据清理过程之后,现在需要将数据帧转换为 GeoPandas 数据帧,以便在 PostGIS 中表示空间数据。这里使用的 CRS 是 EPSG:4326。然后将数据加载到 PostgreSQL 数据库中,表名为 mumbai_house_price_raw

分析

我们将执行以下分析,然后在分析过程中得出结论。

  1. 在地图上可视化整个数据集
  2. 按价格区间分析公寓
  3. 寻找机场半径 5 公里内的地方
  4. 选择面向大海的公寓
  5. 距离海军船坞的距离

在地图上可视化整个数据集

现在我们已经在 PostgreSQL 表中有了整个数据集,我们可以很容易地将其导入 QGIS,并可视化孟买的各种房屋。首先,让我们将 OpenStreetMap 导入 QGIS 作为背景层。

转到 Web → QuickMapServices → OSM → OSM 标准。

图层→添加图层→添加 Postgis 图层。选择 PostGIS 中可用的表的名称,然后单击添加。

PostgreSQL 表中的所有数据点现在都将加载到 QGIS 图层中,并且这些点将绘制在地图上。为了更好的理解,我还提供了孟买机场和海军船坞的准确位置。

按价格区间分析公寓

如上图所示,地图上显示了超过 30000 套公寓。然而,看看这些公寓的价格范围如何因地区而异会很有意思。基本上,我们试图回答的是当地是否对房价有影响。

为此,我们将房价分成如下 3 个部分。

  • 低于 20K
  • 20K 到 40K
  • 40K 以上

我将在 PostgreSQL 表中添加一列,并更新关于 bin 的相关信息。

现在,我们需要刷新已经在 QGIS 中获取的图层。这将带来我们刚刚创建的新列' house_price_category 。一旦新列位于图层中,下一步就是基于此类别在地图上显示点。

右键单击图层→属性→符号系统→分类→值= house_price_category →颜色渐变=为 3 个类别创建手动颜色渐变→分类。

这将把地图上的所有点分类到三个箱中,如下所示。

从上面的分析中,我们现在对在孟买市找房子的预算有了一个概念。虽然很少房子的价格低于 40K(绿色和蓝色,T1),但大多数房子的价格高于 40K(T2 红色,T3)。为了验证上述观察结果,我使用 SQL 做了一个快速检查,以找到每个类别中的公寓数量。

经核实,在 5 万套以上的公寓中,大约有 2 万多套。所以,如果我们保持高预算,租一套更好的公寓的可能性更大。

寻找机场半径 5 公里以内的地方

孟买机场位置:Lon: 72.874374,Lat: 19.096713

  1. 在 PostgreSQL 中创建一个查询,该查询将过滤机场 5 公里半径内的建筑物名称→创建并将其存储在视图中。
  2. 将上一步创建的视图提取到 QGIS 中,并绘制公寓图。

以下是距机场 5 公里范围内所有公寓的平面图。

选择面朝大海的公寓

在这里,我们需要找到面朝大海的公寓。这是一个棘手的部分,因为我们需要某种基准来决定公寓是靠近还是远离海岸,以及公寓是否面向西方。对于那些靠近海滩但面朝其他方向的公寓,应该排除在分析之外。

我们可以从 OSM 获取海岸线。为此,我们需要安装快速浏览插件,然后选择矢量→快速浏览→快速浏览。在快速查询页签中,提供关键字为自然、值为海岸线,选择画布范围点击运行查询

这是在 QGIS 中作为孟买海岸线的一部分导入的。我们可以导出这条海岸线,并将其保存为一个 SQL 转储,然后加载到 PostgreSQL 表中。你可以看到部分海岸线用黑色突出显示。

既然海岸线已经准备好了,我们可以在海岸的一边创建一个 1000 米的缓冲区,它会显示缓冲区内的公寓。

转到数据库管理器,编写一个简单的 SQL 查询,如下所示,根据海岸线几何数据获取缓冲区。将其作为新图层导入到 QGIS 中。

对于面朝大海的公寓,他们需要面朝西方。“ desc ”列包含该信息,我们将创建一个视图,使用 REGEX 过滤匹配单词‘T14’朝西’或‘T16’朝西’的记录。将此新视图导入 QGIS 并绘制输出。

以下是最终输出。

在这里,正如你所看到的,我们有一些朝西的公寓和 1000 米的海岸线缓冲区。位于缓冲区的公寓是我们的兴趣点,因为它们都在海滩的 1000 米范围内,并且都朝西。所以这些公寓可以通过实地参观来进一步调查。

最终分析-距海军船坞的距离

经过实地考察,他们决定敲定位于 Juhu Tara 路的 2BHK 公寓,详情如下。

现在,我们将设法找到最快和最短的路线乘车到海军船坞。

海军造船厂位置:经度:72.832096,纬度:18.926257

为此,我们将使用名为 ORS 工具的开放路线服务插件。

一旦插件安装完毕,下一步就是在openrouteservice.org注册并获得一个 API 密钥。

如上图所示,我们选择了起点作为家,终点作为海军船坞交汇处。上面显示了驾驶汽车时最快的路线,距离约为 19.5 公里。

或者,我们也可以进行最短路径分析,看看最快路径和最短路径是否相同。仅通过改变参数重复上述过程。

如上图所示,最短路径约为 19.2 公里,在某些点上与最快路径不同。

结论

到目前为止,在这个项目中,我们已经广泛利用 QGIS 和 PostGIS 功能来分析孟买市的房屋,并得出了购买或租赁符合我们要求的房屋的结论。我们还计算了从公寓到海军船坞的最快和最短的路线,并了解到两条路线之间有一些小的差异。

QGIS 所有图层

上面是我们在这个项目中分析过的所有图层的示意图,下面会提到图例。

  • 小圆点(红/绿/蓝)——所有可供出售的公寓。
  • 绿色大圆圈——机场 5 公里半径内的公寓。
  • 带黑点的绿色大圆圈——机场半径 5 公里内的公寓,面朝西。
  • 黑线-该区域的海岸线。
  • 橙色斑块——海岸线的缓冲区,距离海岸线 1000 米。
  • 蓝线——从公寓到海军船坞的最快路线。
  • 绿线——从公寓到海军船坞的最短路线。

该项目及其源代码可以在 https://github.com/aveek22/cs621-spatial-db 的找到。

用 R 分析墨尔本房价

原文:https://towardsdatascience.com/analyzing-melbourne-house-prices-with-r-5c13218f242d?source=collection_archive---------11-----------------------

数据分析综合实践指南。

哈姆扎·扎伊迪在 Unsplash 上的照片

有几个库和包为数据科学家、分析师或任何对数据感兴趣的人提供了执行高效数据分析的功能。它们中的大多数都有很好的文档记录,所以您可以很容易地找到一个函数是做什么的。

然而,学习这些库的最好方法是通过实践。仅仅知道一个函数做什么是不够的。我们应该能够在正确的时间和地点回忆和使用它们。因此,我强烈建议练习学习一个包或库。

在本文中,我们将使用 R 包探索并深入了解 Kaggle 上可用的墨尔本房屋数据集。

对于数据分析和操作,我们将使用 r 的 data.table 包。

> library(data.table)> house_prices <- fread("Downloads/melb_data.csv")> head(house_prices)

(图片由作者提供)

该数据集包含墨尔本房屋的几个属性及其价格。因为这个数据集的焦点是价格,所以最好先了解一下价格列。

> house_prices[, summary(Price)] Min.  1st Qu.  Median     Mean  3rd Qu.     Max. 
  85000  650000   903000  1075684  1330000  9000000

我们使用 price 列上的 summary 函数来获得基本统计数据的概述。平均房价约 107 万。

我们知道一般的平均房价。我们可能需要比较不同地区的房价。这是一个按任务分组的方法,可以通过添加用于分组的列名来轻松完成。

> house_prices[, mean(Price), by = Regionname]

(图片由作者提供)

聚合列表示为“V1 ”,这并不能提供很多信息。我们可以为聚合列指定一个名称,只需稍微改变一下语法。让我们也计算一下每个地区的房屋数量以及平均房价。

> house_prices[, .(avg_price = mean(Price), number_of_houses = .N), by = Regionname]

(图片由作者提供)

数据集中有 3 种类型的房屋,h 代表住宅,u 代表单元住宅,t 代表联排住宅。data.table 包还允许过滤以及聚合和分组。

例如,我们可以计算北部大都市地区每种房屋类型的平均土地面积。

> house_prices[Regionname == 'Northern Metropolitan', .(avg_size = mean(Landsize)), by = Type] Type  avg_size
1:    h  619.2491
2:    u  495.0265
3:    t  317.3257

我觉得还是详细阐述一下 data.table 库使用的语法结构比较好。

datatable[ filter/order , select/aggregate/update , by ]

第一个逗号之前的表达式用于排序或过滤。在前面的示例中,我们根据 region name 列过滤了观察值(即行)。

第二部分用于汇总、选择或更新。正如您在示例中看到的,我们已经计算了这一部分的平均价格。最后一部分是根据给定列中的类别对行进行分组。

语法结构提供了执行数据操作、选择和转换的有效方式。例如,我们可以创建一个子集,其中只包含价格低于 100 万英镑的房屋的地址、房间数量和价格。

> subset = house_prices[Price < 1000000, .(Address, Rooms, Price)]
> head(subset)

(图片由作者提供)

我们还可以在选择列的同时执行数据转换或聚合。例如,我们可以在子集中以百万为单位表示价格。

> subset = house_prices[
     Price < 1000000, 
     .(Address, Rooms, Price_million = (Price/1000000))
]
> head(subset)

(图片由作者提供)

如果我们使用“keyby”参数而不是“by”参数,那么结果集将根据类别的升序进行排序。考虑下面的例子。

> house_prices[
      ,.(avg_distance = mean(Distance), avg_price = mean(Price)) 
      ,keyby =.(Type, Regionname)
]

(图片由作者提供)

正如我们所看到的,type 和 region name 列中的类别是按升序排序的。

data.table 包允许链式操作,这进一步简化了更复杂操作的语法。回想一下前面的例子,我们计算了每种类型和地区名称组合的房屋的平均距离和价格。

我们可能希望根据聚合值(如平均价格)对组进行排序。下面是我们如何完成这项任务。

> house_prices[
      ,.(avg_distance = mean(Distance), avg_price = mean(Price)) 
      ,by =.(Type, Regionname)
][order(avg_price)]

(图片由作者提供)

默认情况下,order 函数按升序对值进行排序。我们可以通过在列名前面添加一个减号(例如 order(-avg_price))来将其更改为降序。

结论

我们已经做了几个例子来演示 data.table 包用于数据分析和操作的简单而有效的方法。

能够将过滤、选择、转换和聚合组合到一个方括号中,这使得操作更加直观。

在 data.table 包中还有更多的内容要介绍,但是我们在本文中介绍的内容为高效的数据分析提供了一组不错的操作。

感谢您的阅读。如果您有任何反馈,请告诉我。

使用仪表板分析 ML 模型

原文:https://towardsdatascience.com/analyzing-ml-model-using-dashboard-f69e17a942f9?source=collection_archive---------14-----------------------

显然用于创建机器学习模型仪表板

来源:作者

解释机器学习模型是一个困难的过程,因为通常大多数模型都是一个黑盒,我们并不真正知道模型内部发生了什么。创建不同类型的可视化有助于理解模型是如何执行的,但是很少有库可以用来解释模型是如何工作的。

一个模型的性能可以基于不同的度量标准,如分类报告、MAE、MSE 等。类似地,有不同的可视化显示数据的漂移,这意味着特征分布的变化,我们也可以在目标变量中找到这种漂移。

显然是一个开源 Python 库,用于创建交互式可视报告、仪表盘和 JSON 配置文件,有助于在验证和预测期间分析机器学习模型。它可以创建 6 种不同类型的报告,涉及数据漂移、分类或回归的模型性能等。

在本文中,我们将探索并创建交互式报告/仪表板。

让我们开始吧…

安装所需的库

我们将从使用 pip 安装开始。下面给出的命令可以做到这一点。

!pip install evidently

导入所需的库

在这一步中,我们将导入创建 ML 模型所需的库。我们还将导入一些库,这些库将用于创建一个仪表板来分析模型性能。此外,我们将导入熊猫来加载数据集。

import pandas as pd
import numpy as np
from sklearn.ensemble import RandomForestRegressor
from evidently.dashboard import Dashboard
from evidently.tabs import RegressionPerformanceTab
from evidently.model_profile import Profile
from evidently.profile_sections import RegressionPerformanceProfileSection

加载数据集

在这一步中,我们将加载数据,并将其分为参考数据和预测数据。

raw_data = pd.read_csv('/content/day.csv', header = 0, sep = ',', parse_dates=['dteday'])ref_data = raw_data[:120]
prod_data = raw_data[120:150]ref_data.head()

数据集(来源:作者)

创建模型

在这一步中,我们将创建机器学习模型,对于这个特定的数据集,我们使用随机森林回归模型。

target = 'cnt'
datetime = 'dteday'
numerical_features = ['mnth', 'temp', 'atemp', 'hum', 'windspeed']
categorical_features = ['season', 'holiday', 'weekday', 'workingday', 'weathersit',]
features = numerical_features + categorical_featuresmodel = RandomForestRegressor(random_state = 0)model.fit(ref_data[features], ref_data[target])ref_data['prediction']  = model.predict(ref_data[features])
prod_data['prediction'] = model.predict(prod_data[features])

创建仪表板

在此步骤中,我们将创建仪表板,用于解释模型性能和分析模型的不同属性,如 MAE、MAPE、误差分布等。

column_mapping = {}
column_mapping['target'] = target
column_mapping['prediction'] = 'prediction'
column_mapping['datetime'] = datetime
column_mapping['numerical_features'] = numerical_features
column_mapping['categorical_features'] = categorical_featuresdashboard = Dashboard(tabs=[RegressionPerformanceTab])dashboard .calculate(ref_data, prod_data, column_mapping=column_mapping)dashboard.save('bike_sharing_demand_model_perfomance.html')

仪表板(来源:作者)

在上面的图片中,我们可以清楚地看到显示模型性能的报告,您可以下载我们使用上面的代码为本文创建的 HTML 报告。HTML 报告是可共享的,可以在不同的浏览器中打开。

继续尝试使用不同的数据集和机器学习模型仪表板。如果您发现任何困难,请在回复部分告诉我。

本文是与 Piyush Ingale 合作完成的。

在你走之前

感谢 的阅读!如果你想与我取得联系,请随时通过 hmix13@gmail.com 联系我或我的 LinkedIn 个人资料 。可以查看我的Github简介针对不同的数据科学项目和包教程。还有,随意探索 我的简介 ,阅读我写过的与数据科学相关的不同文章。

分析音乐品味

原文:https://towardsdatascience.com/analyzing-music-taste-64202f602bcd?source=collection_archive---------24-----------------------

使用 Spotify API 的音乐属性来评估我的音乐品味,并预测我是否会喜欢一首歌

阿列克谢·鲁班在 Unsplash 上的照片

我是一个音乐迷,大约 8 年来,我一直专门使用 Spotify 的平台来听音乐和播客,发现新音乐,并与朋友合作播放列表。Spotify 也有一个强大的 API,可以提供 Spotify 上 5000 多万首歌曲的大量信息,包括独特的音频属性——捕捉歌曲各种分类方式的数字特征。更多关于音频属性的信息可以在这里找到。

Spotify 使用这些音频功能以及一整套复杂的模型来识别您喜欢的新音乐的良好匹配(为什么您的 Discover Weekly 播放列表可以如此有效地向您显示您将重复播放一整周的下一首歌曲)。在这篇文章中,我分析了我的流媒体历史的特征,并使用机器学习方法来尝试和预测我是否会喜欢我输入到模型中的歌曲。

我从 Spotify 请求您的数据时收到的 zip 文件中获得了. JSON 格式的流媒体历史记录。如果您想为您的帐户获取相同的信息,请从桌面登录您的帐户,单击“隐私设置”,然后单击“下载您的数据”下的“请求”

我的流历史数据帧的前几行。图片作者。

使用 Spotify 的 API,我获得了数据帧中每首歌曲的音频属性。

在删除了一些不太有用的返回列后,我们剩下的是:

具有音频功能的数据帧。图片作者。

我听了多少音乐?

print(‘Streaming history shows a total of %s listened minutes’ % format((df[‘msPlayed’].sum()*.001)/60))>>> Streaming history shows a total of 30602.300566666665 listened minutes

根据这个列表,在 12/6/19 到 12/6/20 之间大约有 30.6k 分钟(这和我的“2020 Wrapped”总结告诉我的差不多)。

我很好奇每首歌的节奏(每分钟节拍数)有多准确。对于我列表中最慢的两首和最快的两首歌曲,我也用软件录制了 BPM。看起来 API 中的一些录制的节奏实际上是我对节奏分类的细分,因为四首歌中有三首是我录制的 BPM 的两倍:

探索性数据分析

在深入 EDA 之前,我生成了两个数据集,一个“好”和一个“坏”播放列表,用于训练一个模型来分类我喜欢和不喜欢的音乐。虽然使用具有 15,800 个数据点的流媒体历史数据集是理想的,但数据将是高度不对称的,因为我没有长度相等的播放列表来播放我不喜欢的歌曲。相反,我在我的账户上使用了不同的播放列表——202 首歌曲。我为我不喜欢的歌曲生成了一个相当于 200 首歌曲的播放列表。这不是一个有趣的任务,但浏览“热门电台歌曲”让它很快过去。感谢 Spotify!

深入研究 EDA,让我们首先绘制每个音频特性的频率分布。

虽然一些要素的分布显示出明显的重叠,但我们现在将保留它们,因为包含所有要素的数据集和维度很小。我对节奏的分布感到非常惊讶——我不期望看到明显的区别。跨 3 个数据集比较的每个特征的平均值绘制如下(在最小最大缩放速度和响度特征之后)。

图片作者。

在一些特征中,例如效价和可跳舞性,好/坏之间的平均值相当接近。对于某些功能,我的“好”播放列表准确地代表了流历史的样本,但对于其他功能,流历史更接近于“坏”所有歌曲在工具性、语言性和生动性方面的价值都相当低。

我将好的/坏的数据集连接起来,并添加了一个“目标”列,用于监督学习。最后一个要查看的 EDA 图是一个相关矩阵,用于查看每个特性之间的相关程度,以及这些特性与目标的相关程度。

响度和能量是正相关的,而能量和声音是负相关的。不幸的是,这些特性和识别好与坏的目标之间没有太多的关联,所以我怀疑用这些数据生成的模型有多大用处。

监督学习

我尝试了各种监督学习方法,利用网格搜索来优化每种方法的超参数。

对数据训练 K-最近邻模型。

对于 kNN,最佳超参数如下:

{'metric': 'manhattan', 'n_neighbors': 23, 'weights': 'distance'}

然后,我用调整后的超参数重新拟合分类器。

除了 kNN,我还测试了 SVM、逻辑回归和随机森林分类器。随机森林分类器似乎给出了所有分类器中最高的准确度。

图片作者。

然而,即使是最好的结果也只有大约 84%的准确率——不是很好。只有 400 首歌曲用于训练模型,一些特征的频率分布明显重叠,我怀疑我能达到的模型精度。没有额外的特征选择来改进模型,因为使用所有特征作为输入获得了最高的精度。

后续步骤

我需要更多的数据。所以我联系了一些朋友,其中一个同意把他的流媒体数据发给我。他的音乐品味足够多样,但与我的截然不同。完美!这个策略将试图训练一个模型来辨别我的音乐品味和我朋友的音乐品味之间的区别,而不是“好”和“坏”之间的区别。

我按照上面相同的步骤将 json 字典转换成包含音频特性的数据帧。Joe 在他的流历史中有更少的独特歌曲,所以我通过从我的流历史中随机抽样独特的条目来确保类分布对于目标变量是相等的。

然后,我重新拟合模型,将我的流历史与新的流历史数据集进行比较。

相关矩阵仍然不是最佳的。没有一个特征与用于比较两组流历史数据点的目标变量高度相关。图片作者。

尽管更新的相关性矩阵缺乏增加的相关性,但当输入 300%以上的数据时,每个模型的交叉验证分数表现得更好(与手动选择“不喜欢”的歌曲样本相比,通过使用我朋友乔的流媒体历史,样本的多样性更大)。

图片作者。

通过在 XGBoost 模型上调整超参数,我能够达到大约 90%的准确率。

为了进一步测试模型的性能,我从 Joe 和我在 Spotify 上的“每周发现”播放列表中抓取了歌曲,这是一个包含 30 首歌曲的播放列表,每周都会更新你没有听过的新歌,Spotify 根据他们的推荐算法认为你会喜欢这些新歌。使用此播放列表中的歌曲可以确保我们不会意外地让模型根据之前在流媒体历史中看到的数据进行预测。

结果…令人失望。

用于预测乔和我的流历史之间的分类的混淆矩阵结果。图片作者。

当将优化后的模型应用于这个新数据时,我无法实现高于 38-55%的分类准确率。从上面的图表来看,该模型有大量的误报,并且它过度分类了更可能是乔的播放列表的歌曲。

其他模型产生了相同的结果——从主数据集中保留的测试数据具有相当高的准确性,但在尝试对 discover weekly 歌曲进行分类时,类似的预测具有大约 50%的准确性。

我还尝试过采样我的数据来改变类别分布,以便为“我的音乐”类别保留更高百分比的歌曲我还尝试重新拟合 XGBoost 模型,添加 early_stopping_rounds 参数(在 20 轮后停止),以确保模型不会过度拟合训练数据。

在训练 XGBoost 模型时绘制对数损失和分类误差,以确定一个合适的训练时期,并在该时期尽早停止。我用了 20 的值。

虽然模型的表现不如我希望的那样好,但我也想检查特征的重要性——模型说什么特征对我的音乐品味最重要?

图片作者。

响度、速度、效价和能量似乎是预测的最重要特征,而乐器性是最不重要的。

包扎

我仍在熟悉机器学习实践,我可能错过了数据处理或特征工程步骤。然而,我对《发现周刊》预测准确性低的假设并不是建模问题的结果,而是方法和用于该方法的数据量的结果。我最终采用的方法是——尝试对属于某个人的播放列表的歌曲进行分类,这可能是一项具有挑战性的任务,尤其是考虑到我必须处理的数据点数量有限。

关于我将从我的朋友 Joe 那里接收到的数据,我曾错误地做出一个假设,即数据集将具有相似数量的歌曲,并且歌曲多样性的广度将大致相同。在我的流媒体历史中,我有 16260 首歌曲,其中 27%是独特的。Joe 的流媒体历史记录仅包含 2109 首歌曲(但独立实例的百分比非常相似,为 30%!).如果 Joe 的流媒体历史也有至少 4000 首独特的歌曲,那么该模型可能会表现得更好。

还要记住的是,流媒体历史记录包括没有从头到尾播放的歌曲,因此如果您正在播放喜爱的播放列表,并且在一两秒钟后跳过了一首歌曲,它仍然会显示在流媒体历史记录中。也许你不喜欢那首歌,所以模特的表现会受到影响。

作为快速检查,看看这个事实对唯一条目的百分比有多大影响,我在过滤了大于 30000 的“msPlayed”后再次计算了这个数字,假设如果你听一首歌至少 30 秒,你可能会听完整首歌。结果与我所期望的相反。我的流媒体历史记录中歌曲的变化从 27%下降到 20%。乔保持稳定,仅从 30%下降到 29%。我想我听的音乐种类没有我想象的那么多!

最终,如果我有办法为“坏”歌曲生成大量数据点,我会很乐意回到这个项目。我认为我的第一个想法是根据我喜欢或不喜欢的歌曲对模型进行分类,这可能是实现具有令人满意的性能的模型的更好方法,但是即使花费在管理 200 首糟糕歌曲的播放列表上的时间也是非常长的,更不用说数量级更大的播放列表了。从互联网上的其他地方获取这些数据也是不可能的,因为好的和坏的音乐对听众来说是主观的。Spotify 在一个大规模矩阵中拥有数亿个数据点,他们用这些数据点来推荐音乐。我敢肯定,当您有能力查看如此大规模的大数据时,本项目中使用的音频属性功能会提供更多见解。

感谢阅读!如果您对如何提高 discover weekly 数据的模型性能有什么见解,请随时联系我们。这个项目的代码可以在我的 github 这里找到。

另外,我发现了一篇文章,其中的分析与我执行的分析非常相似,这让我感觉比作者无法在原始数据集之外的数据上实现高性能模型要好一些。那篇文章可以在这里找到。

用机器学习分析学校用电

原文:https://towardsdatascience.com/analyzing-power-usage-in-schools-with-machine-learning-ba07f089d547?source=collection_archive---------44-----------------------

人工智能在学校项目中的发现

今年,我与温布尔登高中的一组教师合作,开发并推出了我们的 AI in Schools 计划的第一个迭代,通过分析真实数据,向高中生教授数据科学和机器学习技能。

https://nwheeler443.medium.com/the-ai-in-schools-program-896f7000f31f

我们关注了气候变化问题,并特别关注了学校的用电情况。女孩们使用三个数据源来调查学校的用电情况:

  • 学校能源使用数据是作为能源火花计划的一部分收集的。
  • 女孩们还对 Arduinos 进行了编程,以收集学校周围战略位置的光线、运动和温度数据。
  • 我们从整个伦敦的家庭获取智能电表数据作为“大数据”集,以补充和比较我们的学校用电数据。

我们的发现

我们使用 Kaggle 分析数据,因此您可以自己用笔记本这里、这里和这里探索代码和数据。

1。学校的能源使用量很高,甚至在学校关闭时也是如此

学校的基准能耗约为 20 千瓦/小时,最高日能耗通常达到约 120 千瓦/小时。相比之下,伦敦典型家庭的最高能耗不到 5 千瓦/小时。因此,让学生有机会设计干预措施来减少学校的能耗,可能会比鼓励学生改变自己家中的能耗产生更大的影响。

我们还可以看到,在 2020 年,当学生呆在家里时,能源使用会明显下降。

三年来学校的能源使用情况

三个伦敦家庭的能源使用

2。能源使用显示出明显的时间趋势。

当我们绘制几个月来学校的能源使用情况时,我们可以看到能源使用的每日趋势,在上课时间达到峰值,在放学时间下降。我们还可以看到能源使用的每周趋势在周末下降,尤其是在周日。也有固定的假期,学生可以休息一下,一周的能量消耗会大幅下降。

3。脸书的 Prophet 时间序列建模工具捕捉到了其中的一些趋势

我们尝试了脸书的 Prophet 工具,该工具在用户很少输入的情况下对时间序列数据进行建模,这意味着初学者可以快速采用它,而无需学习时间序列建模的数学基础来建立模型。下面是真实的能源使用数据(蓝线)和来自训练模型的预测(红线)。

我们可以看到,该模型学习了每日振荡趋势,并学习了周末的能源使用下降。它没有意识到的是,周末的能源使用差异要小得多,所以它重复了同样的振荡趋势,但平均值更低。这导致周六夜间的能源使用预测为负值,而周末的能源使用预测过高。

该模型还没有学会的另一件事是如何预测学校假期。因此,该模型对节假日和学期时间的峰值能源使用进行平均,导致学期时间的能源使用预测不足,节假日的能源使用预测过高。

4。包含额外的元数据可改善预测

学生们使用 Arduinos 收集了整个学校的光线、温度和运动数据。每个组都有来自一个位置的一种数据类型。当这个数据被包含在模型中时,能源使用预测随着时间的推移会有所改善。

在两天的期限内,原始模型(左)和包含运动作为协变量的模型(右)的预测。

5.所有数据来源都有缺点

我们拥有的最大数据源是一个关于伦敦电力使用的公开数据集,但这个使用数据看起来与我们在学校看到的模式非常不同。学校用电高峰在工作日,而家庭用电高峰在上班前后。同样,家庭在周末用更多的电,学校在工作日用更多的电。

根据伦敦家庭数据训练的模型预测的能源使用(红色)与真实的学校能源使用(蓝色)

第二大数据源是学校的智能电表数据,这对预测未来的能源使用要有用得多。但是,学校放假和停课不会在可预测的时间间隔内发生,也不会以 Prophet 精心设计的方式进行建模,因此预测通常会连续几周出错。

根据学校能源使用数据训练的模型预测的能源使用(红色)与实际的学校能源使用(蓝色)

最小的数据集是 Arduinos 收集的数据。运动数据与电力使用特别相关,但我们只有几周的数据用于建模,其中一些时间是在期中休息期间,所以我们只能对未来几天进行预测(见发现 4)。

运动数据(蓝色)与程序持续期间的能耗(红色)的关系图

摘要

检查学校能源使用的真实数据提供了有用的见解,可以导致干预措施,以减少学校的能源消耗。例如,当学校不使用时,将整个学校的供暖设置为下降,可能会导致假期和未来封锁期间的消费下降。

这些数据还让学生们有机会培养自己的编程、数学、批判性思维和项目管理技能。学生们收集的数据部分涵盖了一个期中假期,这意味着峰值用电量低于平时。这让学生们有机会看到算法所犯的错误,并试图找出为什么某一周的用电量被大大高估了。

随着该项目在温布尔登高中的继续,我们计划从 Arduinos 中收集更多的数据,随着时间的推移,这将有望导致更好的模型。这是关于构建人工智能以做出有价值的预测的一个很好的教训——提前计划并尽早开始收集数据是值得的。但收集正确的数据也很重要,因为大量不相关或误导性的数据可能比少量相关数据更糟糕。

使用高级分析基准模型调查价格趋势

原文:https://towardsdatascience.com/analyzing-price-trends-with-advanced-analytics-benchmarking-model-1aa57928c1b8?source=collection_archive---------31-----------------------

利用 ML 深入了解房地产价格趋势…看看我们是否能胜过线性回归模型。

作者照片

1.介绍

在我的上一篇文章“去年房地产价格如何变化”
中,我描述了处理非同质商品(如房地产)价格数据的主要挑战,通过我们的分析,样本构成各不相同。

我建议至少阅读前一篇文章的介绍,它将为你提供一些关于我们正在试图解决的问题的背景。

简单回顾一下,我们的目标是消除样本变化对分析时间段的影响,这样我们就可以自信地回答这个问题:不管样本变化如何,价格本身变化了多少。在使用两个更简单的解决方案完成这项任务后:商业智能分析和统计测试,我们可以继续利用高级分析并比较这些方法的结果。

这篇文章的代码可以在 GitHub 上获得(笔记本的第 6 部分& 7)。

2.高级分析方法

作者图片

这种方法依赖于利用机器学习来精确建模我们在某个时间点的价格环境——在我们的例子中,华沙房地产价格为 2020 年第二季度,然后将其作为基准,与模型未看到的时间段(在我们的例子中为 2020 年第四季度)的数据进行比较。为了更容易理解整个过程,我准备了一个简单的 5 步图如下。

价格基准管道

作者图片

  1. 我们从想要比较的时期创建一个基线数据集。我们将这些数据分为训练和测试数据,以确保我们不会过度拟合——这是我们在这种方法中需要避免的主要风险。
  2. 我们使用训练集来训练我们的模型,其目的是复制我们基线数据的定价环境。
  3. 我们使用我们的测试集来评估我们的模型是否可以作为基线数据的可靠定价基准。首先,我们需要避免过度拟合,并确保我们的模型没有高估或低估的趋势。
  4. 一旦我们对第 1-3 点感到满意,并可以假设我们可以可靠地模拟基线数据的价格,我们就可以将我们想要比较和估计的 2020 年第 4 季度到 2020 年第 2 季度房地产价格的数据输入到这个模型中。
  5. 最后一步是比较比较数据集中的实际价格——在我们的例子中,是 2020 年第四季度的价格与 2020 年第二季度同一房产的基准价格。如果我们努力地执行前面的步骤,从基准的平均变化将会给我们一个在两个被分析的时期之间的价格趋势的相当精确的视图。

3.可靠的基准模型清单

我们的方法在很大程度上依赖于这样的假设,即我们能够从基准期对定价环境进行建模,并将其转移到比较期。如果我们在第 1-3 阶段不小心,继续使用一个模型,这个模型不能完成这个任务,我们也不能完成这个基本假设,基准测试的结果可能会非常误导人。

此外,一旦我们过了阶段 3,我们就没有进一步的方法来验证我们的模型是否可靠。在继续基准测试流程之前,我们需要进行三项关键的健全性检查:

3.1 分析平均绝对百分比误差和平均百分比误差

首先,我们需要确保我们的模型能够很好地预测价格。我建议使用两个关键 KPI:

  • 平均绝对百分比误差-告诉我们模型预测值与实际值的差距。在房地产的例子中,任何低于 10%的都很好,阈值取决于商品和价格的波动性。但是,如果您试图评估几个百分点的变化,并且您的平均模型误差为 20%+则您可能没有足够的特征来为价格建模。
  • 平均百分比误差—告诉我们我们的模型是否有高估或低估的趋势。这个值应该尽可能接近 0%,这意味着平均价格预测与实际价格一致,这是基准中的一个理想特征。

测试集性能

在我们的模型中,我们的 mean_percentage_error 在 1%左右,这已经足够好了,因为我们没有明显的高估或低估倾向。平均绝对百分比误差为 9%,这也是可以接受的。

3.2 不惜一切代价避免过度配合

过度拟合总是不好的,但是如果我们想用我们的模型作为价格基准,过度拟合会产生更坏的影响。通过这种方法,我们不再专注于对单个样本的完美预测,而是依赖于获得正确的关键价格驱动因素。

当我为我的模型使用神经网络时,通过结合退出和批量标准化来避免过度拟合。我还在 160 个周期后停止了训练,尽管为了避免过度适应的风险,在以后的周期中可能会有所改善。

3.3 误差分布应接近平均值= 0 的正态分布

测试集上的误差分布

第 3.3 点。与前两个任务有很强的联系——如果你答对了,它应该被确认为一个均匀的误差分布。如果您的误差分布严重偏斜或不以 0 为中心,这意味着您的模型在整个数据范围内没有稳定的性能。

这可能与过度拟合训练集有关,这通常会导致 mean_percentage_error 远离 0。这是一个大问题,因为如果它甚至不能在基线数据集上获得正确的平均预测,就很难依赖这个基准作为可靠的定价表示。

如果分布不均匀,这意味着您可能会遗漏一些关键特征,推动具有特定特征的房地产价格,从而导致该数据子集的持续高估或低估。

4.结果分析

一旦我们确信我们的模型在基线 2020 年第二季度数据集上足够好,我们就可以用它来预测 2020 年第四季度的房地产价格与半年前相比会是什么样子。

2020 年第二季度-第四季度对比

我们可以看到,对于我们的基准测试集,我们模型的预测价格仅为 77 PLN/m2,比实际值低 0.46%,这是一个令人满意的性能,但在对我们的比较数据集进行基准测试时,我们还应该考虑这一轻微的低估。

我们 2020 年第四季度数据集的平均价格基准比 2020 年第二季度高出 161 波兰兹罗提/平方米,这表明我们样本的构成变为质量稍高的公寓——即使在 2020 年第二季度的基线期,它们的平均价格也将高于基线期的样本。

当我们将 2020 年第四季度每平方米的平均实际价格与基线期基准价格进行比较时,差异增加了近 3%。在减去 0.5 个百分点后,我们可以估算出最终价格上涨 2.5%或 255 波兰兹罗提/平方米。

如果我们回到上一篇文章,我们可以看到 a 用一种简单得多的基于统计的方法和线性模型得出了非常相似的结果(2.5%,270 PLN/m2)。那么我们在复杂的神经网络模型上的所有工作都是不必要的吗?

恰恰相反,用更简单的线性回归方法得到类似的结果,更容易验证和理解,而复杂的 ML 模型可以让我们对后者更有信心,并利用其灵活性来增加我们分析的深度。

5.利用 ML 模型的灵活性

线性回归模型可以很好地分析整个样本的主要趋势和价格驱动因素,但我不建议用它来准确预测单个结果。

另一方面,一旦我们完成了我们的基准模型清单,并对我们的基准预测有信心,我们就可以用它来评估价格在更细粒度数据上的表现。

2020 年 Q2 nad 第四季度的价格与基准分布

让我们从基准分布的变化分析开始,我们可以看到 2020Q4 的红色直方图在整个数值范围内稍微向右移动。这表明价格上涨是大多数价格的总体趋势。

映射价格变化

按位置汇总基准结果可用于绘制地理价格趋势图。在上面的地图上,我们可以看到华沙的大部分地区,尤其是中心地区是稳定的。另一方面,我们可以看到郊区和最大的公园和绿地附近的房价大幅上涨近 10%,这可能表明这一特征开始在买家决策中发挥重要作用。

其他使用案例

基于不限于时间段的各种特征,使用 ML 模型的基准的类似方法可以用于比较不同的数据集。

在修改我们用来划分基线和比较数据集的内容后,我们可以使用相同的原则来估计 A/B 测试对更广泛的客户群中的一个群体的治疗效果,或者比较最终报价和有效报价之间的价格。如果我们在训练模型时很勤奋,我们可以将可靠的结果与复杂模型的灵活性结合起来。

6.摘要

我希望这两篇关于分析非统一商品价格趋势的不同方法的文章表明,这一挑战比听起来要复杂得多。

我想总结一下关于方法选择的指导方针:

  • 如果你正在对过去进行广泛的分析,商业智能方法可能是最有效的。这一步对于更复杂的方法也是一个很好的起点,并且最容易向更广泛的受众解释。
  • 如果您希望对您的结果更有信心,并确保样本变化不会导致误导趋势,那么统计方法是对上一步的良好安全检查。
  • 如果您想深入研究定价问题并成功完成前两种方法,您可能可以安全地使用本文中描述的最复杂的方法。

我建议不要从高级分析方法开始,而不使用更简单的方法进行至少一次简单的分析,原因有两个:

  • 基于 ML 的方法是最难解释的,并且有许多可能导致误导结果的陷阱。如果您在前面的步骤中了解了数据和关键驱动因素,那么避免它们会容易得多
  • ML 模型的灵活性是一把双刃剑,正如 R.Coase 所说*“如果你折磨数据足够长的时间,它会承认任何事情”*并且相信我 ML 模型可以是一台令人印象深刻的数据折磨机器。如果您想要用于分段的时间变量在线性模型中不重要,则应用更复杂的变量只会提供任何重要的结果…如果它们实际上是误导性的或由过度拟合引起的。

感谢您的宝贵时间!我希望所描述的方法能帮助你进行价格分析。

使用 Python 的 LUX 库分析生产数据

原文:https://towardsdatascience.com/analyzing-production-data-with-pythons-lux-library-34812267d109?source=collection_archive---------17-----------------------

仅使用交互式可视化技术执行预测性维护任务

乔纳森·博尔巴在 Unsplash 上的照片

作为数据科学家,我们都知道我们将大部分时间花在探索性的数据分析、数据清理和预处理上。通常,开发机器学习、做所有的训练、验证和测试工作都是次要的。一些最大似然模型比其他模型表现得更好,你可以调整一些参数,最终得到更好或更差的结果。但是通常,你会很快找到一个能很好地处理数据的模型。

因此,问题是如何加快最初的探索性数据分析步骤。由于这一步涉及大量的交互式工作,一个可能的解决方案是使用所谓的无代码或低代码库。使用这些库可以让你快速检查你的数据集,大大减少甚至不需要编码。换句话说,您可以完全专注于分析数据集的创造性工作。
在 Python 内部,几个低代码/无代码库已经上市,而在本文中,我们将重点关注 Lux 。

Lux 宣传自己是一个 Python 库,可以在处理 Pandas 数据帧的同时实现可视化和数据分析过程的自动化。因此,Lux 会自动建议突出显示底层数据集中有趣的趋势和模式。

使用案例

网上已经有一些使用 Lux 进行数据分析的教程。尽管如此,大多数教程都是针对较小的数据集,重点是对 Lux 进行一般性介绍。因此,在本文中,我们将尝试对来自生产领域的大量数据进行深入分析。

该数据集已于 2020 年在 AI4I 会议上在本文中介绍,随后被上传到 UCI 机器学习知识库。它是免费的,可以通过这个链接下载。
总之,数据集描述了由 10 000 个数据点和 14 个特征组成的生产工厂的模拟。对于我们的任务,我们对以下各列感兴趣:

  • 类型:此栏由字母 L、M 和 H 组成。L 代表低(所有产品的 50%),M 代表中(30%),H 代表高(20%)产品质量变体。
  • 空气温度[K]:由随机游走产生,大约 300 开尔文
  • 过程温度[K]:通过随机游走生成,并加上空气温度加 10 开尔文
  • 转速[rpm]:使用 2860 W 的功率计算,并叠加正态分布噪声
  • 扭矩[Nm]:40Nm 左右的正态分布值
  • 工具磨损[分钟]:对于质量变量,H/M/L 5/3/2 分钟的工具磨损加到过程中使用的工具上。
  • 机器故障:表明机器是否出现故障的标签。

数据集包含更多信息,例如给出机器故障原因的详细信息。但是因为我们不想搞清楚这一点,所以其他列将被忽略。总之,用例的目标可以总结如下

通过使用 Lux 库的交互式可视化,找出机器故障的一些可能原因

对其他图书馆的快速调查

正如介绍中提到的,Python 中有几个低代码/无代码库。以下是对其他一些例子的快速回顾:

  • Bamboolib :是一个在熊猫数据框架上工作的 GUI,可以很容易地集成到 Jupyter 笔记本中。可视化和分析数据集是相当交互式的。不幸的是,它不是开源的,免费版只能让你使用有限的功能。
  • 米托:给你的 Jupyter 笔记本添加某种 Excel 电子表格。您在电子表格中所做的更改会被翻译成 python 代码。这对于市民数据科学家来说尤其有趣,因为他们可以在熟悉的环境中进行数据分析
  • PyCaret :这不是探索性的数据分析,而是提供了一个低代码的 ML 库。它特别适合数据科学新手,因为您可以用很少的代码行解决完整的 ML 任务

设置 Lux

让我们开始吧。首先,我们需要安装所需的库 Lux 和 Pandas。要安装这两个库,只需在命令提示符下使用 PyPI 和以下命令,并键入

pip install lux-api
pip install pandas

此外,要激活 LUX 作为 Jupyter 笔记本扩展,请在命令提示符下键入以下两个命令。注意,也许你需要重启 Jupyter 内核

jupyter nbextension install --py luxwidget
jupyter nbextension enable --py luxwidget

接下来,在 Jupyter 笔记本中导入这两个库,并从 UCI 存储库中加载原始数据集。加载完。csv 文件,你应该会看到一个按钮写着“切换熊猫/力士”。当然还有你的数据框的摘录。

作者图片

首先,我们删除完成任务不需要的列。此外,我们需要重命名剩余的列。如果列名中包含方括号,Lux 表示分布图有困难。因此,需要用圆括号替换它们。注意,本文是用 Lux 0 . 4 . 0 版编写的。也许这个问题在未来的版本中不会继续存在。

最后,用 Lux 检查结果(在 Jupyter 笔记本中,只需单击切换按钮)。情节应该是这样的:

作者图片

让我们看看选项卡后面有什么:

  • 相关性:有 10 个散点图显示不同过程值的行为。空气温度和过程温度显示出或多或少的线性行为。最后一幅图显示了扭矩和合理速度之间的非线性行为。
  • 分布:以直方图的形式显示每个过程变量的分布。
  • 发生:Flux 自动将定量变量和分类变量彼此分开。在数据帧中有两个分类属性,即机器故障和类型。此外,我们移动条形图,您可以获得以下信息:9661 次生产正常运行,339 次出现机器故障

一大特点:意图属性

到目前为止,这并没有给人留下深刻的印象,但是这个库提供了一个很好的特性,叫做意图属性。可以选择一个(甚至几个)将在图中突出显示的列。

在我们的例子中,只需一行代码,就可以检查机器在不同质量类型下的表现。因此,只需输入

作者图片

那挺有意思的。尤其是第二个情节。在“H”高,“L”低和“M”中等质量的三种类型中,现在 Lux 还标记是否有机器故障。通过点击“过滤器”标签,甚至有一个更详细的看法。通过悬停在条形图上(要做到这一点,下载 jupyter 笔记本),Lux 只给我们记录的数量,但是计算故障率是相当直接的。结果是:

  • 男:83/2914 = 0.028
  • l:235/5765 = 0.04
  • h:21/982 = 0.021

因此:低品质产品的机器故障率最高。最后,让我们看看是否有可能找出导致机器故障的原因。请记住,这是我们最初想要实现的实际目标。让我们将 intent 的属性设置为“机器故障”并检查结果。

作者图片

Lux 告诉我们,如果机器出现故障,过程变量的平均值会如何变化。

检查结果,两个变量突出,即“工具磨损”和“扭矩”。如果机器出现故障,这两个变量都有较高的值。
让我们进一步研究扭矩。因此,标记该地块,并点击右上角的放大镜。

作者图片

现在,我们得到了所有变量的散点图,如果有一个机器故障或没有额外的标记。滚动到“扭矩”而不是“转速”,可以让我们清楚地了解情况。如果机器在低扭矩和高转速下运行,或者在高扭矩和低转速下运行,则存在机器故障。

最后,选择这个散点图并点击放大镜。同样,结果也很有趣。作为附加信息,数据分为三种产品质量。通过比较这些图,我们看到,转速大于 2.500、扭矩大于 60 NM 的产品很少能生产出高质量的产品。因此,高质量的产品很少导致机器故障。

作者图片

结论

本文将介绍 Lux 库,并评估它是否可以分析生产数据。请记住,这篇文章只是触及了库提供的可能性的表面。

对我来说,有几件事很有趣:

  • 令人印象深刻的是只需要几行代码。此外,理解它们并不复杂
  • 令人印象深刻的是,几乎不需要统计知识。你需要对散点图和直方图有一个基本的了解,但仅此而已。
  • 不需要 ML/AI 来寻找机器故障的原因之一。请记住,这是分析生产数据时的常见结果。用一些领域知识仔细查看生产数据通常足以获得对数据的新见解。
  • 最后,最令人印象深刻的是浏览数据集是多么的快速和直观。通过不必编写代码,可以专注于主要任务,分析过程数据并获得新的见解。

如果你有兴趣,你可以通过点击这个链接下载 Jupyter 笔记本和使用过的数据集

用 Apache Spark 3.0 分析堆栈溢出数据集

原文:https://towardsdatascience.com/analyzing-stack-overflow-dataset-with-apache-spark-3-0-39786c141829?source=collection_archive---------19-----------------------

在 Amazon EMR 上使用 Spark SQL 和 Jupyter 进行端到端数据分析

从 Pixabay 下载的图像

数据科学家和分析师经常选择 Python library Pandas 进行数据探索和转换,这是很常见的,因为它的 API 漂亮、丰富且用户友好。Pandas 库提供了 DataFrame 实现,当数据量适合单台机器的内存时,这真的非常方便。对于更大的数据集,这可能不再是最佳选择,而 Apache Spark 等分布式系统的强大功能变得非常有用。事实上,Apache Spark 成为了大数据环境中数据分析的标准。

在本文中,我们将看到一步一步地分析特定数据集的方法,即来自堆栈溢出数据库的转储。本文的目标并不是通过一些具体的结论进行详尽的分析,而是展示如何使用 Apache Spark 和 AWS 堆栈(EMR、S3、EC2)进行这样的分析。我们将在这里展示所有的步骤,从从堆栈交换存档下载数据,上传到 S3,数据的基本预处理,到使用 Spark 的最终分析,包括使用 Matplotlib 和 Pandas 绘制的一些漂亮的图表。

在这里,我们将看到使用 Spark 和 Python 生态系统对大数据集进行数据分析的一种非常常见的模式。该模式有三个步骤,第一,用 Spark 读取数据,第二,做一些减少数据大小的处理——这可能是一些过滤、聚合,甚至是数据的采样,最后将减少的数据集转换成 Pandas 数据框架,并在 Pandas 中继续分析,这允许您使用 Matplotlib 在幕后绘制图表。

我们将在本文中使用的所有代码也可以在我的 GitHub 库中找到,所以请随意查看。

本文假设读者对 AWS 有一些基本的了解,比如在那里有一个帐户,能够生成 EC2 密钥对,等等。**此外,让我在这里强调,这些服务,如 EC2、EMR 和 S3,需要信用卡信息,亚马逊将向您收取使用费用。**因此,请确保在完成后终止 EC2 实例和 EMR 集群,并在不再需要时从 S3 中删除数据(包括日志、笔记本和其他可能在此过程中创建的文件)。

EC2 和集群设置的步骤将通过 AWS 文档的相关链接进行简要描述,AWS 文档非常广泛,其他资源也可以在网上找到。让我们看看我们将执行的所有步骤:

  1. 从 Stack Exchange 归档文件下载数据转储(这是一个 7z 压缩的 XML 文件)
  2. 解压缩下载的文件
  3. 将文件上传到 S3(AWS 上的分布式对象存储)
  4. 将 XML 文件转换为 Apache Parquet 格式(再次在 S3 上保存 Parquet)
  5. 分析数据集

对于步骤 1–3,我们将使用一个带有更大磁盘的 EC2 实例。对于第 4 步和第 5 步,我们将使用 Spark 3.0 和 JupyterLab 在 AWS 上部署一个 EMR 集群。

我们将使用 Spark 执行的分析本身独立于运行 Spark 的云,因此,如果您不想使用 AWS,但您在其他地方运行 Spark,并且您可以将数据存储到您的存储系统,您仍然可以使用我们将在下面使用的查询。

数据集

栈溢出很受程序员欢迎,你可以在那里提问,也可以回答其他用户的问题。转储本身可以在栈交换档案中找到,并且可以在知识共享许可 cc-by-sa 4.0 下获得(更多信息参见细节)。

数据由更多的文件组成,分别是帖子用户评论徽章投票标签历史记录、帖子链接。在本文中,我们将只使用压缩 XML 中有 16.9 GB 的 Posts 数据集(截止到 2021 年 12 月)。解压缩后,大小增加到 85.6 GB。要查看各个表及其列的含义,我建议查看 Stack Exchange 上的这篇帖子,这篇帖子很好地描述了这一点。我们将处理的帖子包含所有问题及其答案的列表,我们可以根据 _Post_type_id 来区分它们,其中值 1 代表问题,值 2 代表答案。

数据下载和解压缩

为了从 Stack Exchange 归档文件中下载数据,我们将使用 EC2 实例 t2.large 和 200 GB 的磁盘空间。为了做到这一点,你需要登录到 AWS 控制台,进入 EC2 实例,点击启动实例并选择操作系统,例如,Ubuntu Server 18.04 LTS。接下来,您需要指定实例类型,使用 t2.large 很好,但是其他选项可能也不错。然后单击配置实例详细信息,并在添加存储选项卡中添加一个磁盘卷。您需要指定磁盘的大小,我选择 200 GB,但是更小的磁盘应该也可以。只需记住,解压缩后数据集将有近 90 GB。我在这里添加了一些与描述的步骤相关的截图:

作者创造的形象。

作者创造的形象。

此外,AWS 会建议您设置一些安全组来限制可以使用该实例的 IP 地址,所以您可以随意这样做,并查看 AWS 文档。最后,要启动实例,您需要有一个 EC2 密钥对,如果您还没有,您可以继续并生成它。同样,要生成新的密钥对,请参见 AWS 文档。在实例运行之后,您可以从终端使用您的密钥(您需要在下载密钥的同一个文件夹中)对它进行 ssh:

ssh -i key.pem ubuntu@<the ip address of your instance>

然后,在控制台上安装 pip3p7zip (第一个是 Python3 的包管理系统,我们将用它来安装 2 个 Python 包,第二个是用来解压 7z 文件的):

sudo apt update
sudo apt install python3-pip
sudo apt install p7zip-full

接下来安装 Python 包请求boto3 ,我们将使用它们进行数据下载和 S3 上传:

pip3 install boto3
pip3 install requests

然后使用 vi data_download.py 创建一个新文件,并在其中复制以下 Python 代码(在 vi 编辑器中,您需要通过按 i 键切换到插入模式):

url = 'https://archive.org/download/stackexchange/stackoverflow.com-Posts.7z'local_filename =  'posts.7z'
with requests.get(url, stream=True) as r:
  r.raise_for_status()
  with open(local_filename, 'wb') as f:
    for chunk in r.iter_content(chunk_size=8192):
      if chunk:
        f.write(chunk)

然后保存(按 esc 键并使用 :wq 命令)并使用python 3 data _ download . py运行。数据下载本身可能需要一段时间,因为如上所述,该文件有近 17 GB。接下来使用以下命令解压缩文件

7z x /home/ubuntu/posts.7z

之后,创建另一个 python 文件 *vi upload_to_s3.py,*并将数据上传到 s3 的代码复制到那里:

from boto3.session import *
import osaccess_key = '...'
secret_key = '...'
bucket = '...'
s3_output_prefix = '...'session = Session(
  aws_access_key_id=access_key, 
  aws_secret_access_key=secret_key
)
s3s = session.resource('s3').Bucket(bucket)local_input_prefix = '/home/ubuntu'
file_name = 'Posts.xml'input_path = os.path.join(local_input_prefix, file_name)
output_path = os.path.join(s3_output_prefix, file_name)s3s.upload_file(input_path, output_path)

正如您在代码中看到的,您需要填写 S3 的凭证( access_keysecret_key )、您的 bucket 的名称和 s3_output_prefix ,这是文件上传的最终目的地。如果没有 access_keysecret_key ,可以生成(参见文档)。如果您没有存储桶,您可以创建它。

让我也提一下,还有其他方法如何访问堆栈溢出数据,即有一个公共的 API 允许您查询数据库,还有一个 Python 包装器构建在其上。

数据预处理

从存档中下载数据并存储在 S3 上后,我们现在将做一些基本的预处理,为分析查询准备数据。更具体地说,我们将把格式从 XML 转换为 Apache Parquet,并将列重命名/重新键入为更方便的格式。

正在启动 EMR 群集

对于这个预处理步骤,以及实际的数据分析,我们将使用 Spark 3.0 和 JupyterHub 启动一个 EMR 集群。要启动集群,请转到 EMR(在 AWS 服务中)并点击创建集群。之后,你就需要指定一堆设置了。转到高级选项,在那里您可以选择 EMR 版本并检查集群上将安装哪些应用程序。这里我们使用了 emr-6.1.0 ,我们将需要的关键组件是 Hadoop、Spark、JupyterHub 和 Livy(另见所附截图):

作者创造的形象。

在下一步中,您可以配置您的群集将有多少个节点,您可以使用默认设置,但是对于一些查询,您将不得不等待一段时间—它不会有超级响应。换句话说,拥有一个更大的集群会导致更快的查询,但是集群的成本也会更大。在下一步中,您可以为群集选择一个有意义的名称,并取消选中终止保护。最后,在点击创建集群之后,它会将您带到一个页面,您会看到您的集群正在启动。接下来,您可以点击笔记本并创建一个笔记本,您可以从中运行 Spark 查询。为笔记本选择一个名称,并选择该笔记本将连接到的已创建群集。几秒钟后,您可以点击打开 JupyterLab 按钮,打开 Jupyter:

作者创造的形象。

在 JupyterLab 中,你会看到你可以打开使用的笔记本。除此之外,您还可以使用菜单中的上传按钮来上传其他笔记本。

对于数据预处理和分析,您可以将我的存储库克隆到您的本地机器上,并上传这两个笔记本:

  • stack overflow-Data-Analysis/Data-prepare/Data-prepare . ipynb
  • stack overflow-Data-Analysis/Data-Analysis/Posts-General-Analysis . ipynb

在笔记本本身,确保你使用的是正确的内核,你可以在右上角看到(也可以看上面的截图)。出于我们的目的,我们将需要 PySpark 内核。

转换为拼花地板格式

让 EMR 集群运行 Spark 和 Jupyter,我们就可以开始处理数据了。我们要做的第一步是将格式从原始的 XML 转换成 Apache Parquet,这对于 Spark 中的分析查询来说要方便得多。为了读取 Spark SQL 中的 XML,我们将使用 spark-xml 包,它允许我们指定格式 xml 并将数据读入数据帧

作为这个 ETL 过程的一部分,我们还将所有列重命名为 snake-case 样式,并将 creation_date 列转换为 TimestampType 。将数据转换为拼花后,大小从 85.6 GB 减少到 30 GB,这是因为拼花压缩,也因为我们没有在最终的拼花中包括所有列。这里我们可以考虑的另一个步骤是按照创建年份甚至可能是从创建日期中导出的创建月份来划分数据。如果我们只想分析数据的某一部分,也许是最后一段时间等等,这将是很方便的。

数据分析

最后,我们将进入探索数据并可能揭示其一些有趣属性的部分。我们将回答关于数据集的 5 个分析问题:

1.计算计数

让我们计算以下有趣的计数:

  • 我们有多少问题
  • 有多少个答案
  • 有多少问题已经接受了答案
  • 有多少用户提问或回答了一个问题

在上面的代码中,我们首先读取了所有的帖子,但是只选择了在进一步的分析中需要的特定列,并且我们缓存了它们,这将非常方便,因为我们将在所有的查询中引用数据集。然后,我们根据 post_type_id 将帖子分成两个数据帧,因为值 1 代表问题,值 2 代表答案。帖子总数为 53 949 886,其中 21 641 802 是问题,32 199 928 是回答(数据集中还有其他类型的帖子)。当过滤 accepted_answer_id 不为空的问题时,我们得到了具有可接受答案的问题的数量,它是 11 138 924。对 user_id 列上的数据集进行重复数据删除,我们得到提问或回答问题的用户总数为 5 404 321。

2.计算响应时间

我们在这里将响应时间定义为从提出问题到得到被接受的答案所经过的时间。在下面的代码中,您可以看到我们需要将问题与答案连接起来,这样我们就可以将问题的创建日期与其被接受的答案的创建日期进行比较:

当根据响应时间对数据进行排序时,我们可以看到最快的可接受答案出现在一秒钟内,您可能会想,为什么有人能如此快速地回答一个问题。我明确地检查了其中的一些问题,发现这些问题是由发布问题的同一个用户回答的,所以显然他/她知道答案,并将其与问题一起发布。

作者创造的形象。

将响应时间从几秒钟转换为几个小时,并进行汇总,我们可以显示在问题发布后的每个小时内有多少问题得到了回答。

这里我们将时间限制为 24 小时,以查看问题发布后第一天内的行为。正如您在图表中看到的,大多数问题都在第一个小时内得到了回答:

作者创造的形象。

3.查看问题/答案的数量如何随着时间的推移而变化

观察每个时间单位的问题和答案的数量如何随着时间的推移而变化是非常有趣的。要使用 Spark 计算聚合,我们可以使用 window() 函数进行分组,它有两个参数,第一个是具有时间含义的列的名称,第二个是我们希望对时间维度进行分组的时间段。这里我们选择时间单位为一周。在聚合本身中,我们可以使用 when 条件计算答案和问题,如下所示:

按窗口分组创建了一个具有两个结构字段的结构类型*,开始、结束,因此在这里为了绘图,我们使用了开始*字段,它是每周开始的日期。

作者创造的形象。

4.计算标签的数量

每个问题都有一些代表一些主题的标签—在堆栈溢出数据集中,这些通常是用户询问的一些技术。然而,标签是以字符串的形式存储的,格式为*<tag 1><tag 2><…>*,所以为了分析它们,将这个字符串转换成这些标签的数组并去掉尖括号是很有用的。为此,我们将首先使用 split 函数拆分字符串,之后,我们将使用高阶函数 TRANSFORM 使用regexp _ replace删除数组中每个元素的尖括号。为了计算所有标签的总非重复计数,我们将最终 展开 该数组并对其进行重复数据删除:

计数的结果显示 61 662 个不同的标签。要查看哪些标签被使用的次数最多,我们只需按标签对展开的数据集进行分组,计算每个标签的数量,并按计算出的频率进行排序。这就产生了这个列表,从这个列表中可以看出使用最多的标签是 javascript:

作者创造的形象。

5.查看一些特定标签的流行程度

将 tags 列转换为一个数组后,我们现在可以过滤这个数组并研究特定的标记。这里我们将看看人们用标签 apache-sparkapache-spark-sql 问了多少问题。为此,我们可以使用函数 array_contains ,该函数有两个参数,第一个参数是数组的列,第二个参数是一个特定的元素,我们希望确定它是否包含在数组中。该函数返回,因此我们可以在过滤器内部直接使用它。像这样过滤问题后,我们可以根据创建日期按周对它们进行分组。最后,我们将其转换为熊猫数据帧并绘制成图:

最终的曲线图显示,在 Stack Overflow 上,人们在 2014 年开始对 Spark 感兴趣,每周被问及最多的问题是在 2017 年和 2018 年:

作者创造的形象。

结论

在本文中,我们看到了如何使用 Spark 来分析公开可用的数据集,但是,我们首先需要下载、解压缩并存储在某个分布式存储系统中,这样我们就可以使用 Spark 有效地访问它。

堆栈溢出数据集包含大量有趣的信息,我这样说不仅仅是指实际答案形式的内容,还包括问题、答案、评论和用户之间一些有趣的关系。在本文中,我们展示的只是冰山一角,我们已经看到了这个数据集上的一些基本查询,在未来的一些帖子中,我们将回到它并应用一些更高级的分析工具,如使用 Spark 的机器学习和图形处理来揭示数据的更多属性。

如何分析 NDVI 趋势

原文:https://towardsdatascience.com/analyzing-the-annual-maximum-ndvi-trend-using-time-series-landsat-images-a95a686c0c2c?source=collection_archive---------16-----------------------

利用时间序列陆地卫星图像分析年最大 NDVI 趋势

由 Unsplash 上的hkon grim stad拍摄的照片

归一化差异植被指数(NDVI) 是一个简单的图形指示,可用于分析遥感数据,通常来自空间平台,以确定被观察的目标是否有活着的绿色植被。

归一化差异植被指数的卫星数据可用于量化生态系统生产力的变化模式(NDVI)。另一方面,利用 NDVI 时间序列对趋势的估计会因所研究的卫星数据集、相关的时空分辨率和所使用的统计方法而有很大差异。我们分析了各种趋势估计方法的有效性,并表明性能随着 NDVI 时间序列的年际变化的增加而降低。

ArcGIS

环境系统研究所维护着 ArcGIS,这是一个用于处理地图和地理信息的地理信息系统。它用于创建和利用地图、收集地理数据、分析地图信息、分发和查找地理信息、在各种应用程序中使用地图和地理信息,以及管理数据库中的地理数据。

ArcGIS 的产品/服务(作者创建)

在这个场景中,我使用了 Arcpy.ia 和 ArcGis API for Python。arcpy.ia 是一个用于管理和处理影像和栅格数据的 Python 模块。该模块还提供了 ArcGIS Image Analyst 扩展模块提供的所有地理处理方法,以及用于实现栅格处理工作流自动化的增强功能和类。

事不宜迟,让我们开始吧。我将在解释代码时描述这个过程。首先,我将所需的模块导入到我的工作流中。

import osimport matplotlib.pyplot as pltimport arcpyarcpy.CheckOutExtension("ImageAnalyst")

根据时间序列 Landsat 图像创建栅格集合

获取文件夹中所有陆地卫星场景的列表。

我建立了我的数据文件夹。我从美国地质调查局收集了这些数据,你可以在美国地质调查局注册获得类似的数据。我还创建了四个数组供将来使用。

美国地质调查局网站

folder = r"\\landsat\\LE70820552011359EDC00"raster_lists = []acquisition_time = []names = []quality_bands = []

在下面的代码中,我从源文件夹中检索所有 tif 文件,并将它们添加到栅格数据集中。我还包括每个数组的获取时间和文件名。但是,在添加文件名之前,我用. xml 对其进行了更改。

for directory in sorted(os.listdir(folder)):for file in os.listdir(folder+"\\"+directory):if file.endswith("xml") and len(file.split(".")) == 2:raster_lists.append(folder+"\\"+directory+"\\"+file)time = file.split("_")[3]acquisition_time.append(time[:4]+"-"+time[4:6]+"-"+time[6:])names.append(file.replace(".xml", ""))quality_bands.append(folder+"\\"+directory+"\\"+file.replace(".xml", "") + "_pixel_qa.tif")

创建栅格集合

为了生成栅格集合,我使用了 raster collection 方法。RasterCollection 对象可以轻松地对一组栅格进行排序和过滤,并为后续处理和分析准备集合。RasterCollection 对象有六种方法可用于为集合栅格中匹配波段上的每个像素生成统计数据(最大值、最小值、中值、平均值、多数和总和)。

rc = arcpy.ia.RasterCollection(raster_lists, {"Varieble": "Landsat7", names, "STDTime": acquisition_time, "Quantity": quality_bands})

过滤器

我们使用的数据集涵盖了 18 年的数据。所以我只用了 10 年的数据。我们可以通过使用 filterByCalanderRange 函数来实现这一点。

filtered_rc = rc.filterByCalenderRange(calender_field = "YEAR", start = 1997, end = 2007)

可视化栅格集合

不,我们可以查看栅格集合。

filtered_rc

结果如下。如我们所见,它显示变量、名称等等。

结果(作者创建)

项目中的可视光栅

下面的代码从集合的最终条目中检索栅格。我使用 arcpy.ia.Render 函数来呈现结果,并使用 exportImage 方法将其显示为图像。

要改进符号系统,请使用渲染功能来调整栅格对象的外观。当在 Jupyter 笔记本上工作时,数据显示是一个重要的优势,这个功能非常有用。该函数使用指定的渲染规则或颜色映射生成一个光栅对象。必须至少给出一个呈现规则或颜色映射。

first_item = filtered_rc[0]['Raster']rendered_raster = arcpy.ia.Render(first_item_raster, rendering_rule = {"bands": [3, 2, 1], "min": 500, "max": 1000, colormap='NDVI'})rendered_raster.exportImage(width = 500)

这是结果图。正如我们所看到的,这张图片有很多云。因此,我们必须清理这个形象。

结果图像(作者创建)

预处理

在预处理阶段,我将处理之前获得的图像,去除云、阴影、水和雪。

在下面的代码中,我首先检索栅格和每个栅格的质量。然后,我保存需要从栅格中移除的像素值。接下来,我使用 arcpy.ia.Apply 和 bandRaster 来应用于每个栅格。bandRaster 将屏蔽指定 RGB 波段中的像素。然后,我分配一个已清理的评定者,并将其分配给 return。

def clean(item):raster - item["Raster"]qa_raster = arcpy.Raster(item["Quantity"])values_tb = (qa_raster != 68) & (qa_raster != 72) & (qa_raster != 80) & (qa_raster != 96) & (qa_raster != 132) & (qa_raster != 136) & (qa_raster != 112) & (qa_raster != 144) & (qa_raster != 160) & (qa_raster != 176)masked_clean_band_list = []for bandRaster in raster.getRasterBands([1, 2, 3, 4, 5, 6]):masked_clean_band = arcpy.ia.Apply((values_tb, bandRaster, 0), "Local", args = {"Operation": "StdTime"})masked_clean_band_list.append(masked_clean_band)masked_clean_raster = arcpy.ia.CompositeBand(masked_clean_band_list)return {"raster": masked_clean_raster, "Name" : item["Name"], "StdTime" : item["StdTime"]}cleaned_rc = filtered_rc.map(clean)

设想

使用这段代码,我在集合的第二个项目中检索栅格。

first_item_raster = cleaned_rc[0]['Raster']rendered_raster = arcpy.ia.Render(first_item_raster, rendering_rule = {"bands": [3, 2, 1], "min": 500, "max": 1000, colormap='NDVI'})renderes_raster.exportImage(width = 500)

如下图所示,整个水域都被遮罩掉了。

结果图像(作者创建)

计算 NDVI

我定义了一种称为 clacNDVI 的方法来计算 NDVI,并使用 arcpy.ia.NDVI 从项目栅格和两个波段值创建 NDVI 栅格。最后,我返回项目、名称和标准时间等参数。

def clacNDVI(item):ndvi = arcpy.ia.NDVI(item['Raster'], nir_band_id = 4, red_band = 3)return {"raster": ndvi, "Name": item["Name"], "StdTime": item["StdTime"]}ndvi_rc = cleaned_rc.map(calcNDVI)

当我们调用这个函数时,我们得到了 NDVI 栅格。

设想

让我们用和我之前一样的方式来可视化栅格。

first_item_raster = ndvi_rc[0]['Raster']rendered_raster = arcpy.ia.Render(first_item_raster, rendering_rule = {"bands": [3, 2, 1], "min": 0, "max": 1.0, colormap='NDVI'})rendred_raster.exportImage(width = 500)

这与上一个栅格相同。然而,它现在显示 NDVI。

结果图像(作者创建)

每年获得最大 NDVI

我使用下面的代码转换了多维栅格集合。此方法返回一个多维栅格数据集,栅格集合中的每个项目代表多维栅格中的一个切片。

anual_max_ndvi_mdraster = arcpy.ia.Aggregate(ndvi_mdraster, demension_name = "StdTime", raster_function = "MaxIgnoreNoData", aggregate_definition = {"interval": "yearly"})

我使用 arcpy.ia.Aggregate 函数来确定每年的最大 NDVI。此方法基于提供的多维栅格生成栅格对象。默认情况下,将为与所选维度链接的所有变量计算聚合。在本例中,我使用 STDTime 作为维度时间,使用 MaxIgnoreNoData 作为栅格函数,其时间间隔为一年。

生成趋势

现在,我对年度最大多维栅格应用线性趋势。线性趋势线是估计基本线性关系的最佳拟合直线。线性趋势表示以一致的速度增加或减少的变化速度。

max_ndvi_linear_trend = arcpy.ia.Apply(Annual_max_ndvi_mdraster, "TrendAnalysis", raster_function_arguments = {"DimensionName": "StdTime", "RegressionType": 0})

设想

我们可以使用下面的代码检索趋势栅格的斜率。getRasterBands 为多波段栅格数据集中提供的每个波段返回一个栅格对象。在这种情况下,我使用 1 作为栅格波段。

slope - max_ndvi_linear_trend.getRaterBands(1)

再变换

为了修改负像素值,我使用了重映射函数。重映射功能允许您更改或重分类栅格数据像素值。这可以通过提供映射到输出像素值的像素值范围或者通过使用表格将像素值映射到输出像素值来实现。因此,输出将是一个布尔映射。

increase_decrease_trend_map = arcpy.ia.Remap(slope, input_ranges=[-10000, 0, 0, 10000], output_values=[-1, 1])

提供;给予

我用不同颜色的渲染函数进行渲染(绿色和灰色)。

rendered_raster = arcpy.ia.Render(increase_decrease_trend_map, colormap = {"values":[0, 1], "colors": ["#B0C4DE", "green", "gray"]})rendered_raster.exportImage(width = 500)

当我们看这张图片时,我们可以看到绿色区域的 NDVI 在增加。图像上的灰点代表 NDVI 减少的区域。

结果图像(作者创建)

让我们回顾一下

NDVI 是使用 GIS 工具对影像执行的逐像素数学计算。它是通过测量红光和近红外光的吸收和反射来确定的植物健康指标。NDVI 可用于研究世界各地的土地,使其既适用于重点实地研究,也适用于大陆或全球范围的植被监测。ArcGIS 是一个用于处理地图和地理信息的地理信息系统,由环境系统研究所维护。ArcGIS 可用于计算 NDVI。

参考

什么是 NDVI(归一化差异植被指数)?

ArcGis 文档

https://www.linkedin.com/in/rkavishwara/

GPT-3 在搜索引擎优化方面有多好?

原文:https://towardsdatascience.com/analyzing-the-seo-score-of-an-article-gpt-3-wrote-59108dd105af?source=collection_archive---------34-----------------------

使用自动化人工智能生成的内容有哪些实际和更广泛的社会关注?

附身摄影在 Unsplash 上拍照

机器人正在写文章。机器人正在编写代码。活着是多么令人兴奋的时刻啊!

GPT 3 号现在风靡一时。这是一个生成性的预训练深度学习模型,用于生成类似人类的文本。它是由专门从事人工智能研究的实验室 OpenAI 创建的。

作为一名作家、数字营销人员和一名有抱负的数据科学家,我对这种模式可能有助于创造的可能性感到兴奋。所以,我测试了这个模型。

我想给你看一下 Semrush 的 SEO 写作工具对一篇文章的评估结果,由 GPT-3 生成。然后,我们将讨论这种模式进入主流阶段的意义——它的局限性,以及对社会和地球的潜在影响。

一个机器人写了这篇文章

最近,OpenAI 宣布用户可以请求访问其用户友好的 GPT-3 API——一种“机器学习工具集”——以帮助 OpenAI“探索这项新技术的优势和局限性”。

由于我对它的访问是待定的,我使用了一个服务,已经在他们的应用程序中应用了该模型,简称为ai。

虽然 shortly.ai 被誉为市场上最成熟的作家,但它目前相当昂贵,所以我不会以任何方式支持购买。至少现在还不行,在 OpenAI 发布 alpha 之前。

开始很容易。你会被提示选择一个文章类型(故事或博客/文章),然后写一个标题和几个句子来提示模型开始写作。

然后,瞧!GPT 3 号不到 3 分钟就完成了我的工作。

查看它为您自己写的示例文章。

https://lazarinastoy.medium.com/10-ways-to-increase-your-productivity-c4154b765b9a

现在,让我们开始分析。

评价

Semrush 的 SEO 写作助手是一款针对内容优化的工具。它对你输入的目标关键词使用谷歌搜索结果中前 10 名竞争对手的分析。它提供的建议可以提高它所分析的文本的 SEO 友好性、可读性和语调。它可以作为谷歌文档的附加组件。

以下是它对这篇人工智能文章的评论。

6/10——一般。哎哟。我们来看看为什么。

作者谷歌文档的屏幕截图(图片由作者提供)

可读性

可读性分数是最低的之一。原因是这篇课文是为一个五岁的孩子写的。十分清楚,平心而论!句子简单易懂,这不一定总是坏事,但好的写作需要一些变化。

很明显,这里少了点什么。事实是,书面内容要想真正成功,需要与观众建立联系。这往往是由作者的轶事,甚至随机切线,个人经验分享,批判性讨论,思想。

目前,这篇文章最多只能变成一个维基百科。这是一个想法转储,一个粗略的草稿,需要一些关键的编辑迭代才能发表到任何值得尊敬的出版物。

搜索引擎优化部分——作者谷歌文档的屏幕截图(图片由作者提供)

搜索引擎优化

SEO 部分包括关键词和链接推荐,以及 alt 标签检查。

因为我们的文章没有任何图像,所以没有丢失 alt-tag 警告,但是,我们被提醒,有图像的文章对读者更有吸引力。

也没有添加链接。这是我在阅读这篇文章时没有立即注意到的,但是,我认为它非常重要。GPT 3 写体面,逻辑的内容。然而,却找不到认可其观点的地方。

再一次,我们回到彻底编辑的需要——最好是如批判性地评估所做的声明,在此过程中使用各种权威来源。

最后,使用的相关关键词并不多。当然,其中一些是不相关的,但是提供的目标关键字(即生产力)是一个相当广泛的目标。

如果输入的句子为主题提供了更多的上下文,应该如何处理,以及重要的主要和次要关键字,这可能是模型可以做得更好的事情。

语调部分——作者谷歌文档的屏幕截图(图片由作者提供)

语音语调

现在,谈谈语气。语调是信息呈现的方式,包括用词、顺序、节奏和语速的选择。

塞姆拉什用五分制给语调打分:

  • 很随意(“这是有史以来最愚蠢的建议。”)
  • 有点漫不经心(“那其实并不聪明。”)
  • 中立(“没有人要求这个建议。”)
  • 有点正式(“不需要推荐信。”)
  • 非常正式()“给定的推荐是不请自来且不受欢迎的,将不会被接受。”).

Semrush 说,语音评分使用机器学习算法,基于科学证明的研究和数以千计的人类评分文本。

生成的文本有些正式,但完全不一致。

独创性

并不奇怪,但文字是 100%原创。

原创部分——作者谷歌文档的截屏(图片由作者提供)

GPT-3 模型使用自然语言处理来理解不同的文本语料库,它已经通过许多不同的数据集进行了预训练。这允许自然语言理解,而不需要时间密集的数据标记。它生成的任何文本都是原始的和新的。

判决

印象深刻,印象深刻,印象深刻。尽管如此,在将这个模型应用到现实世界的任何地方之前,人类是绝对必要的。

通过人类辅助的机器学习,可以实现内在的人类元素,如情感和细微的视觉线索,以改善模型的输出。单独使用机器学习算法是行不通的,至少对于长格式内容来说是这样。还没有。

对广泛实施 GPT 协议-3 的广泛影响和担忧

首先,你可能想知道——谷歌对这种生成内容的程序化方法有什么看法?

是的——假设他们现在反对,你可能是对的。在写这篇文章的时候,谷歌的质量指南说:

自动生成(或“自动生成”)的内容是以编程方式生成的内容。如果谷歌的目的是操纵搜索排名,而不是帮助用户,谷歌可能会对这些内容采取行动。这些包括但不限于:(…)

通过自动化过程生成的文本,如马尔可夫链(…)

虽然,他们说在未来的某个时候,可能会对这些指导方针进行修订。

更令我担心的是社会对 GPT-3 模型更广泛实施的担忧,这在布朗和开放人工智能实验室的学者们的研究中得到了强调。

该研究的作者强调了该模型的一些令人关注的应用,目的是支持研究和立法工作以降低风险:

  • 恶意滥用— 将 GPT-3 用于错误信息、垃圾邮件、网络钓鱼、滥用法律和政府程序、欺诈性学术论文写作和社会工程伪装
  • 认可和维持关于种族、偏见和代表性的现有刻板印象 —当出现在训练数据中时,这些会导致模型生成刻板印象或偏见内容
  • 保持对职场男性的刻板印象——作者报告称,他们测试的 388 种职业中,有 83%更有可能被 GPT-3 的男性识别符跟随
  • 强化关于种族的现有刻板印象 —当被提示谈论种族时,当讨论“黑人”时,检测到的模型情绪一直很低(亚洲人——高;白色—中性)
  • 强化现有的关于宗教的刻板印象 —因为模型与宗教术语相关联,表明当被提示谈论宗教时,这些术语有时在世界上是如何呈现的,如暴力、恐怖主义和恐怖主义等词与伊斯兰教比与其他宗教更频繁地同时出现。

作者还强调了训练模型所需的能源使用,以及这种模型的广泛发展对地球及其资源的潜在影响。

外卖

GPT 3 号是令人兴奋的,崭新的,闪亮的;然而,它远非完美。

仍然非常需要人类来帮助创建公平、健壮、批判性、深刻和吸引人的美丽内容。

虽然这种模式的好处大于成本,但至少在测试中,广泛使用 GPT-3 的影响在为被歪曲和代表不足的社区争取平等和正义的斗争中可能是有害的。

这种主流模式的实施存在着深刻的悖论,这在对这些局限性的讨论中表现得尤为明显。与任何机器学习模型一样,GPT-3 是在现有的文本片段上训练的。很明显,虽然这很了不起,但对于我们所坚持的好的写作标准来说,这仍然是易错的。如果广泛实施(没有编辑过程),它将导致质量更差的文本和偏见的永久化,这将最终扼杀其发展。

相反,似乎需要的是并肩努力改进这一模式,并帮助它与时俱进。怎么会?通过写得更好,通过编辑它的输出,通过报道它的缺陷。这似乎是我们人类能够帮助研究人员动态推进这个模型的唯一方法。

如果您确实想继续使用 GPT-3 来帮助自己生成内容,请在发布内容之前查看以下方便的清单:

  1. 语言是不是太简单了? —如果需要,添加更复杂的句子。
  2. 用的字是基本的吗? —尽可能替换为高级单词。
  3. 添加图像,并插入 alt-tag 以方便使用。
  4. 批判性地评估自始至终提出的主张— 尽可能提供权威来源的链接。
  5. 使用的相关关键词是否足够?这篇文章是否连贯地阐述了主题? —如果需要,添加更多相关关键字。使用answerthepublic.com等工具添加紧迫问题的答案。
  6. 写这篇文章的语调是否一致? —你的内容的最佳基调将取决于你的用户、你的信息和你的品牌。
  7. 文字读起来像你或你的品牌会说的话吗? —根据需要,添加、删除、编辑适当的部分以定制您的文本。
  8. 生成的文本是有偏见的,还是支持现有的关于性别、种族或其他代表性问题的刻板印象? —意识到模型的局限性可以帮助你剔除固有的有害偏见。

用 Neo4j 图形数据科学分析数字游牧民的旅游日志

原文:https://towardsdatascience.com/analyzing-travel-logs-of-digital-nomads-with-neo4j-graph-data-science-b7a13d6ba544?source=collection_archive---------12-----------------------

使用 Neo4j Graph 数据科学平台分析数字游牧民的出行模式,预测新的可能路线。

几天前,我偶然发现了由皮特·里维斯在他的游牧者网站上制作的旅行日志的网络可视化。NomadList 是一个建立基础设施的网站,帮助数字游牧民生活在世界上的任何地方,并与志同道合的人联系。所以我在 Twitter 上问 Pieter 他是否可以分享旅行日志的底层数据,你瞧,就在第二天,他公开了聚合的旅行日志数据。对我来说,唯一合适的下一步是将这些数据插入 Neo4j 并进行试验。这篇博文将展示我的一些发现,并向您展示如何使用新的链接预测管道来预测图中的新连接。

如果你想跟随这篇文章中的例子,我推荐在 Neo4j 沙箱中使用一个空白项目。它是 Neo4j 数据库的免费云实例,预装了 APOC 和图形数据科学插件。

图形模型

图形模型模式。图片由作者提供。

图模型由城市和旅行日志组成,城市用节点表示,旅行日志用关系描述。旅行日志已经聚集在底层数据中,所以我们只知道从一个城市到另一个城市旅行了多少次。不幸的是,数据中没有时间成分,所以我们无法比较大流行前后的旅行或一个人在那个城市呆了多长时间。我们所知道的是,所有的旅行都发生在 2014 年至 2021 年之间的某个地方,数据可用于每个大洲的前 50 个城市。在更技术性的层面上,我们正在处理一个有向加权图。

图形导入

如果您想继续,请打开您的 Neo4j 沙箱实例,并在我们进行的过程中复制 Cypher 查询。如果你想用的话,我还准备了所有的代码在 Jupyter 笔记本里。

首先,我们将在城市节点上定义一个唯一属性约束。唯一属性约束确保属性值对于特定标签的所有节点都是唯一的,而且还会自动为该属性添加索引,以便更快地导入和查询。

CREATE CONSTRAINT ON (c:City) ASSERT c.id IS UNIQUE;

接下来,我们将从流浪者网站导入旅行日志。在网站上可以方便地获得 JSON 格式的文件。我们可以使用 apoc.load.json 过程从页面中检索数据。我们还将使用 apoc.periodic.iterate 过程进行批处理。在文档中阅读更多关于如何批量交易的信息。

CALL apoc.periodic.iterate('
  CALL apoc.load.json("https://nomadlist.com/graph.json")
  YIELD value
  WITH value, [x in keys(value) WHERE x <> "README" | x] AS keys
  UNWIND keys AS source_city
  WITH source_city, value
  RETURN source_city, value
','
  MERGE (s:City{name:source_city})
  WITH value[source_city] as destinations, s
  WHERE destinations <> []
  WITH destinations, keys(destinations) as destination_cities, s
  UNWIND destination_cities AS destination_city
  MERGE (t:City{name:destination_city})
  MERGE (s)-[r:TRAVEL_TO]->(t)
  SET r.weight = destinations[destination_city]', 
  {batchSize:10})

城市之间的距离在原始数据集中不可用。你可以使用 APOC 空间程序来检索 GPS 位置。为了避免向 OSM 服务器发送垃圾邮件,我准备了一个包含所有城市位置的 CSV 文件,并将其存储在 GitHub 上。就像从互联网上获取 JSON 文件一样,你也可以从任何网页上获取 CSV 文件。运行以下查询来导入城市的位置。

LOAD CSV WITH HEADERS FROM "https://raw.githubusercontent.com/tomasonjo/blog-datasets/main/nomad/nomad_cities_location.csv" as row
MATCH (c:City)
WHERE c.name = row.city
SET c.location = point({latitude:toFloat(row.latitude), longitude:toFloat(row.longitude)})

在我们进入分析之前,最后一件事是根据城市的位置信息计算城市之间的距离。

MATCH (s:City)-[r:TRAVEL_TO]->(t:City)
WITH r,distance(s.location, t.location) / 1000 AS distanceInKm
SET r.distance = distanceInKm

探索性图形分析

既然数据集已经导入,您可以通过在 Neo4j 浏览器中运行以下 Cypher 查询来检查样本子图。

MATCH p=()-[r:TRAVEL_TO]->()
RETURN p LIMIT 25

结果

旅行日志的示例子图。作者图片

只要一些节点和关系出现在屏幕上,不要担心没有得到相同的可视化。让我们首先检查我们的图表中表示了多少个旅行日志。记住,在我们得到数据之前,数据已经被聚合了,所以我们需要查看所有旅行关系的 weight 属性的总和。

MATCH p=()-[r:TRAVEL_TO]->()
RETURN sum(r.weight) as all_travels

在我的例子中,我总共得到了 26657 个旅行日志。你可能会得到一个更高的数字,因为我相信数据不是静态的,会定期更新。现在是时候检查哪些是最受欢迎的目的地。对于那些喜欢技术术语的人,我们将研究具有最高加权入度的节点。

MATCH (c:City)<-[r:TRAVEL_TO]-()
RETURN c.name as city, sum(r.weight) as travels
ORDER BY travels DESC
LIMIT 10

结果

令人惊讶的是,伦敦排在第一位。我从来没有想象过,然而在我看来,曼谷位居第二很有意义。如果知道人们只是经过伦敦到达他们的最终目的地,或者他们实际上会在那里停留更长时间,这将是很有趣的。我们缺乏这方面的信息,所以只能假设。总而言之,泰国和西欧似乎是数字游牧民的热门目的地。此外,新加坡、纽约和旧金山也包括在内。

我还对人们平均喜欢旅行多远感兴趣。我们将距离属性添加到关系中,以便我们可以粗略地检查它们的分布。我准备了一个 Cypher 查询,它创建了四个条块,并计算该距离内的行程除以所有行程的比率。

MATCH ()-[r:TRAVEL_TO]->()
RETURN sum(CASE WHEN r.distance < 500 THEN r.weight END) 
          / toFloat(sum(r.weight)) AS within_500,
sum(CASE WHEN 500 < r.distance < 1000 THEN r.weight END) 
          / toFloat(sum(r.weight)) AS within_1000,
sum(CASE WHEN 1000 < r.distance < 2000 THEN r.weight END) 
          / toFloat(sum(r.weight)) AS within_2000,
sum(CASE WHEN 2000 < r.distance  THEN r.weight END) 
          / toFloat(sum(r.weight)) AS rest

结果

几乎 50%的记录行程不到 1000 公里。作为一个欧洲人,我想象 1000 公里的旅程是一夜火车的距离,就我所知,对于美国人来说,这几乎就像是通勤上班。即使是 2000 公里的旅程,平均也只有两个小时的飞行时间。对于其余 30%的旅行,我想它们最有可能是跨洲旅行。我的假设是,数字游牧民喜欢跨洲旅行,然后通过从一个城市到另一个城市来探索那个大陆。当然,这只是我的假设,我也可能是错的。

图形数据科学

现在,我们将利用 Neo4j 图形数据科学库中可用的图形算法进行进一步分析。我们将从投影内存中的图形开始,它将被用作执行图形算法的基础。如果你想了解更多关于图形数据科学库和它是如何工作的,我会推荐免费的图形算法介绍课程。

CALL gds.graph.create('nomad', 'City', 'TRAVEL_TO', {relationshipProperties:'weight'});

几乎所有的图分析都是从弱连通分量算法开始的。它用于查找图中不相连的部分或节点岛。

样本图中的可视化弱连通分量。图片由作者提供。

要获取 WCC 结果的高级统计信息,可以执行以下查询:

CALL gds.wcc.stats('nomad')
YIELD componentCount, componentDistribution;

结果

WCC 算法发现了 38 个断开的组件,最大的有 295 个成员。根据百分位值判断,似乎有一个单一的超级组件有 295 个成员,然后有 37 个组件只有一个成员。要检查与世界其他地方没有任何联系的城市,我们可以使用下面的 Cypher 查询:

MATCH (c:City)
WHERE NOT (c)--()
RETURN c.name as city
LIMIT 10;

结果

虽然这些城市在游牧数据集中被提及,但它们与外部世界没有任何联系。

中间中心性

接下来,我们将看看中间中心性。中间中心性用于识别节点集群之间的桥或连接。它的计算方法是找出所有节点对之间的最短路径,然后查看节点出现在这些最短路径上的次数。

漫威字符数据集上的中间中心性。BC 较高的节点用红色表示。图片由作者提供。

在漫威的例子中,您可以注意到字符 Beast 是网络中心和右侧之间的唯一桥梁,因此具有较高的介数中心性得分。您可以在 nomadlist 投影内存图中执行介数中心性,如下所示:

CALL gds.betweenness.stream('nomad')
YIELD nodeId, score
RETURN gds.util.asNode(nodeId).name AS city, score
ORDER BY score DESC LIMIT 10

结果

一个重要的免责声明是,在这个例子中,中间中心性没有考虑关系权重。如果你要环游世界,遵循典型的数字游牧旅行模式,你可能会在某个时间点到达这些城市。例如,如果你在欧洲和亚洲之间旅行,你很有可能迟早会到达迪拜。我之前已经提到过,我很想知道游牧民族是在伦敦呆得更久,还是只是为了穿越大陆而去。同样,你可以把这些结果解释为纽约和墨西哥是进入或离开北美的入口点。

网络可视化

现在,我们将为这篇博文创建一个可爱的特征图像。我只是部分开玩笑,但是对于像这样的小数据集,开发良好的网络可视化是有意义的,因为一个优秀的可视化胜过千言万语。

我们将使用 PageRank 分数来确定节点的大小。在 PageRank 的上下文中,每个关系都可以解释为信任投票。在我们的游牧者旅行日志的例子中,最受欢迎的游牧目的地应该有最高的分数。这是因为 PageRank 算法既考虑了传入连接的数量,也考虑了这些连接来自哪里。

CALL gds.pageRank.write('nomad', {relationshipWeightProperty:'weight', writeProperty:'pagerank'});

为了确定可视化中节点的颜色,我们将使用 Louvain 模块化算法。Louvain 模块化算法是一种社区检测或聚类算法,它将密集连接的节点分组在一起。

CALL gds.louvain.write('nomad', {relationshipWeightProperty:'weight', writeProperty:'louvain'});

如果您使用 Neo4j 沙盒或桌面,您可以打开 Neo4j Bloom 并重新创建以下可视化。如果您想了解 Neo4j Bloom 中基于规则的网络可视化的更多信息,请查看文档。

游牧网络可视化。节点基于它们所属的社区被着色,并且它们的大小由 PageRank 算法确定。图片由作者提供。

节点群很好地与大陆对齐。例如,橙色社区代表北美的节点,而紫色社区代表欧洲的节点。这些结果可以解释为,大多数旅行是洲际旅行,因为更多联系紧密的城市聚集在同一个社区。节点的大小决定了该大陆最受欢迎的城市。例如,最受欢迎的亚洲目的地是曼谷、吉隆坡、新加坡、清迈和仓谷。有了 Bloom,你可以专注于一个单一的社区,并探索其中的旅行模式。

亚洲社区的网络可视化。图片由作者提供。

我还包括了北美社区,因为这些网络视觉效果很悦目。

北美社区的网络可视化。图片由作者提供。

完成网络分析后,应该删除投影图以释放内存。

CALL gds.graph.drop('nomad')

链路预测流水线

Neo4j 图形数据科学库 1.7 版本中增加了链接预测管道。链接预测是一种技术,它做的事情和你想象的完全一样。它用于预测网络中尚不存在的新连接。您可以将它视为一个二进制分类模型,它指示节点对之间是否可能存在新的链接。从推荐系统到药物再利用,链接预测有多种应用。首先,您必须定义一个新的链接预测管道。

CALL gds.alpha.ml.pipeline.linkPrediction.create('lp-pipeline');

作为链接预测管道的一部分,您可以添加执行其他图算法的中间步骤。您希望运行图形算法,其输出将用作链接预测分类器特征。在本例中,我们将使用 FastRP 节点嵌入作为链路预测模型的输入特征。你可以在我的朋友 Clair Sullivan 的这篇优秀文章中读到更多关于 FastRP 算法的内容。

CALL gds.alpha.ml.pipeline.linkPrediction.addNodeProperty(
  'lp-pipeline', 
  'fastRPExtended', {
    mutateProperty: 'embedding',
    featureProperties:['pagerank'],
    embeddingDimension: 128,
    randomSeed: 42
});

这里,我们已经定义了链路预测流水线应该通过执行 FastRP 算法开始,该算法将输出网络中所有节点的嵌入。您还可以在此步骤的配置中微调算法超参数。接下来,我们需要定义如何组合链接的源节点和目标节点特征,以生成链接预测模型的输入。请记住,Neo4j GDS 中的链接预测模型是一个二元分类模型,它使用了逻辑回归。无论网络中是否存在连接,输出都是 1 或 0,并且通过考虑源和目标节点特征来组合输入特征。目前,管道有三种不同的方式组合节点功能:

  • L2
  • 哈达马德
  • 余弦

您可以查看文档以获取更多关于将节点特征组合为链接预测模型输入的信息。在本例中,我们将使用余弦组合器来组合成对节点的 FastRP 节点嵌入。

CALL gds.alpha.ml.pipeline.linkPrediction.addFeature('lp-pipeline', 'cosine', {
  nodeProperties: ['embedding']
}) YIELD featureSteps;

现在是时候定义训练测试划分了。我们可以定义测试列车分流比。流水线使用 k-fold 交叉验证训练技术。关于训练测试分割的一个好处是我们不必开发手动数据分割。

CALL gds.alpha.ml.pipeline.linkPrediction.configureSplit(
 'lp-pipeline', {  
   testFraction: 0.3,
   trainFraction: 0.6,
   validationFolds: 7})
YIELD splitConfig;

在训练模型之前,我们必须设置的最后一个配置是链路预测模型超参数。您可以提供模型超参数的许多变体。在本例中,我们将提供三种不同的超参数组合,模型将在训练过程中自动选择提供最佳结果的组合。

CALL gds.alpha.ml.pipeline.linkPrediction.configureParams(
  'lp-pipeline',  
    [{tolerance: 0.001, maxEpochs: 500},    
     {penalty:0.001, tolerance: 0.01,  maxEpochs: 500},      
     {penalty:0.01, tolerance: 0.01, maxEpochs: 500}])
YIELD parameterSpace;

同样,在开始之前,我们需要投影内存中的图形。与之前的微妙区别在于,这里我们将关系投射为无向的。目前,链接预测管道仅支持预测无向关系。

CALL gds.graph.create('lp-graph', 
  'City', 
  {TRAVEL_TO:{orientation:'UNDIRECTED'}}, 
  {nodeProperties:['pagerank']});

最后,我们开始训练我们的链接预测模型。

CALL gds.alpha.ml.pipeline.linkPrediction.train('lp-graph', 
  {pipeline: 'lp-pipeline',
   modelName: 'lp-model',
   randomSeed: 42})
YIELD modelInfo
RETURN  modelInfo.bestParameters AS winningModel,  modelInfo.metrics.AUCPR.outerTrain AS trainGraphScore,  modelInfo.metrics.AUCPR.test AS testGraphScore;

您可以使用 mutate 模式生成分类结果,并将预测的关系存储回投影图。

CALL gds.alpha.ml.pipeline.linkPrediction.predict.mutate('lp-graph', 
  {modelName: 'lp-model',  
   mutateRelationshipType: 'TRAVEL_PREDICTED',
   topN: 20,
   threshold: 0.45})
YIELD relationshipsWritten;

最后一步,您可以使用下面的 Cypher 查询对结果进行流式处理和检查。

CALL gds.graph.streamRelationshipProperty('lp-graph', 
  'probability', 
  ['TRAVEL_PREDICTED'])
YIELD  sourceNodeId, targetNodeId, propertyValue
WHERE sourceNodeId < targetNodeId
WITH  gds.util.asNode(sourceNodeId).name as city1, gds.util.asNode(targetNodeId).name as city2, propertyValue AS probability
ORDER BY probability DESC
RETURN city1, city2
LIMIT 10;

链接预测结果

我们的链路预测主要预测新的本地连接。例如,它预测了葡萄牙的马德拉和埃里希拉或摩洛哥的丹吉尔和阿加迪尔之间的新航线。同样,它预测了柬埔寨、越南和泰国等邻国之间的新联系。唯一一个看起来有点不靠谱的预测是连接几内亚的科纳克里和加拿大的阿伯茨福德。

结论

我希望我让您对网络分析以及如何在机器学习工作流中结合网络特征来预测新链接感到兴奋。如果你感兴趣,你可以跳到 Neo4j 沙盒上,按照这个教程或者选择任何其他沙盒项目开始。如果你在游牧数据上发现了任何很酷的见解,或者你对下一步你希望看到的数据分析有任何反馈,请告诉我。

和往常一样,代码可以在 GitHub 上获得。

分析 Twitter 对话

原文:https://towardsdatascience.com/analyzing-twitter-conversations-4ce28080732e?source=collection_archive---------22-----------------------

使用新的 Twitter API v2 端点获得关于某个主题的可行见解

普里西拉·杜·普里兹在 Unsplash 上的照片

近乎实时地了解人们在谈论什么是非常强大的。这类信息可以为活动、品牌战略和产品创新提供信息。收集这些数据的标准方法是走出去,找到消费者,向他们提出有针对性的问题。如果你的问题不完美,这种方法可能会很昂贵、耗时,而且完全不相关。

Twitter 提供了一种收集未经过滤的数据的方式,这些数据是关于人们或公司如何谈论特定话题的。然而,“未经过滤”的部分可能是试图从原始 twitter 数据中提取洞察力的最大挑战。这个数据的一个缺点是它只是拼图的一小部分。API 将只提取包含与您的查询匹配的文本的推文,因此它不能提取所有相关的内容。如今,Twitter 被更多地用于对话,最初的推文可能会引发下面的讨论。我想找出一种方法来绘制出这种类型的信息,并获得给定主题的完整图片。

收集

在这个项目中,我想深入了解围绕园艺的对话。Twitter 的免费 API 允许你回溯 7 天,所以这是我的出发点。我用他们 API 的新 v2 版本做了一个 7 天的搜索,寻找任何提到“园艺”的推文,大约有 22,000 条!

v2 端点的新特性是对话 ID。这让我们可以围绕这个话题展开全面的讨论,而不仅仅是一次性的推文。为了获得之前获得的 Tweet 对象,我们将:

  1. 获取我们保存的所有没有回复的推文(会话的初始节点)
  2. 对于其中的每一个,获取具有相同对话 ID 的所有 Tweets
  3. 确保我们的数据库中不存在该推文,然后保存。

以下是大致的想法:

主题聚类

在收集了这些推文之后,我的第一个想法是使用 NLP 将它们归类到子主题中。在对最适合 tweet 数据的模型进行了一些研究之后,我决定使用潜在的 Dirichlet 分配(LDA) Mallet 模型。为了运行这个,我清理了所有的 Tweet 文本,包括 URL、表情符号、标签和用户标签。

为了简单起见,我选择了 20 个主题。下面是按热门话题排序的输出。

作者图片

这个输出非常有趣,但是它只能让我们到此为止。我希望能够更深入地研究这个列表。例如,我想知道每个主题下有哪些推文,以便更好地了解它们真正在谈论什么。

主题散点图

为了能够直观地看到这一点,我决定创建一个交互式散景散点图。

下面是我用来生成图的基本代码。我正在获取推文数据,这样我就可以将鼠标悬停在每个节点上,以了解它是什么推文。此外,由于我们在这里处理的数据量很大,我希望能够区分更重要的推文。为此,我使用该推文收到的收藏数量来确定大小。

作者图片

现在我们越来越近了!使用这个图表,我可以放大集群并阅读最受欢迎的推文,以获得关于主题的真正好主意。然而,这仍然不是全部情况。这些聚类只是随机推文聚类的代表,并不能让我们了解围绕该主题的对话是如何形成的。

对话网络图

对话本质上是更大主题中的迷你网络。在这种情况下,它们通过我们之前收集的对话 ID 连接起来。我们可以通过将 Tweet 连接到它们的“回复”Tweet 对象来确定一切是如何链接在一起的。我将坚持使用散景,并根据这个标准创建一个网络图。

散点图中不同大小的节点非常有帮助,因此我将在此图中坚持使用。我注意到的一件事是,由于 Tweet 收藏夹的差异很大,比例有点失调,所以我将大小限制为收藏夹的平方根,从 5 到 150。也很难确定对话的开始推文,因此,如果数据库中没有保存“回复”,我会将其标记为红色。

为了创建网络图,我为我保存的每条推文添加了一个节点,然后通过“回复”字段将推文连接起来。最后,为了更容易可视化,我将删除所有没有连接的节点。如果我们只想绘制出最大的对话,我们也可以删除任何连接少于一定数量的网络。完成此操作的基本步骤如下。

输出是围绕“园艺”的对话的一个很酷的可视化,不管推文是否提到园艺。这让我们对正在发生的事情有了一个全面的了解。从这里我们可以放大到具体的对话,看看用户在说什么

全图(作者图片)

放大版(图片由作者提供)

我们现在有了一个围绕真实话题的对话地图,它可以提供对一个主题的无限见解。这对于研究博客主题、进行市场研究甚至发现趋势是很有用的。通过调整节点大小,我们可以快速看到哪种类型的推文和内容产生了最多的点击和最多的对话。

用交互式可视化分析巴塞罗那的失业率

原文:https://towardsdatascience.com/analyzing-unemployment-rates-in-barcelona-with-interactive-visualizations-f69f70c03caf?source=collection_archive---------32-----------------------

新冠肺炎和社会不平等对巴塞罗纳失业率的影响概述。

爱德华多·伊格莱西亚斯水彩画(经作者授权在此发表)

2008 年的金融危机对西班牙经济产生了重大影响,特别是在失业方面。然而,从 2012 年开始,西班牙经历了缓慢但持续的经济复苏。冠状病毒疫情已经结束了似乎不可阻挡的增长,再次增加了整个国家的失业率。

这篇文章分析了巴塞罗那所有街区过去 8 年的失业率。该研究旨在应对与失业率的时间和地理分布相关的多个方面。电晕危机对失业有多大影响?哪些社区受危机影响最严重?失业率较高的地区在哪里?在失业方面是否存在性别平等?还有更多问题…继续读❤️

巴塞罗那开放数据

本文中使用的所有数据集都是从巴塞罗那开放数据中心获得的:

  • 巴塞罗那市 16 至 64 岁人口中登记失业的比重
  • 登记失业按巴塞罗纳市的持续时间分类
  • 巴塞罗纳市的登记失业人数
  • 巴塞罗那市每户平均税收

https://opendata-ajuntament.barcelona.cat/en/

按街区分列的失业率的时间演变

我们从分析巴塞罗那不同地区失业率的演变开始这篇文章。下面的交互式可视化显示了从 2012 年 1 月到 2020 年 12 月失业率的演变。每条线代表巴塞罗那的一个街区,总共 72 个(按字母顺序排列)。默认情况下,除了 Baró de Viver 之外,看不到任何线条;然而,我们可以很容易地激活/停用图线,只需点击图例上它们各自的名称。

当我们与可视化交互时,我们会注意到一些有趣的见解。自 2012 年(我们没有之前的数据)以来,我们可以观察到几乎所有社区的失业率都呈整体下降趋势。当冠状病毒危机再次重创巴塞罗那经济时,这座城市仍在从 2008 年的金融危机中恢复。在许多社区,我们可以注意到从 2020 年 2 月到 2020 年 6 月失业率的快速增长。然而,从那时起,我们观察到一个慢得多的增量。

自 2012 年以来,我们可以观察到巴塞罗那所有社区的失业率持续回升。这种复苏在 2020 年 2 月被电晕危机打破。

在与上面的可视化互动时,你可能会注意到的另一个方面是不同社区失业率的不同演变。尽管所有社区的总体趋势几乎相同,但我们可以观察到,在失业率较高的地区,就业的季节性差异更大(主要与旅游业和圣诞节有关)。下图显示了高失业率地区失业率的变化。正如你所观察到的,从四月到五月到七月到八月通常会有一个急剧的下降,然后在新年前后会有一个相当不明显的下降。

失业率较高地区的失业变化

然而,在失业率较低的社区,情况却大不相同。这些地区没有季节性变化。这部分是因为这些领域的就业类型需要更高的教育背景,因此更稳定。但是大量的熟练劳动力并不是这些地区拥有更稳定就业的唯一原因。我们不要忘记,这些也是巴塞罗那人均收入较高的街区,我们稍后会看到。

失业率较低地区的失业变化

我们观察到,在失业率较高的地区,失业率有较大的季节性变化

按街区划分的失业率空间分布

处理地理空间数据时,直观地查看地图上的数据通常很有用。在这种特殊情况下,choropleth 图是最佳选择。 **Choropleth 图提供了一种简单的方法,通过使用阴影区域来可视化一个变量在一个区域内的变化。**这些区域通常是行政区划,如国家、城市、州,或者在本例中是街区。

下面的地图显示了巴塞罗那每年的失业率分布(从 2012 年到 2020 年,每年一张地图)。在绘制地图之前,我们已经计算了每个街区的年失业率,因为提供 108 张 choropleth 地图(每月一张)没有任何意义。正如我们将在下面看到的,失业率的分布在过去几年中几乎没有变化所以提供年度数据变得更有意义。

在设计视觉效果时,颜色的使用是我们应该牢记在心的关键要素之一。颜色有其相关的含义,因此正确使用颜色以避免误解非常重要。在下面的地图上,我们选择了发散的配色方案。该方案是为强调数据中的低值和高值的情况而设计的。在这种特殊情况下,RdYlGr 方案(之前已翻转)应用于 choropleth 地图,以高亮显示失业率较低(用绿色表示)和较高(用红色表示)的邻域。

失业率较高的地区位于港口附近和城市的内北侧。

当我们与可视化交互时,我们会注意到一些有趣的见解。**失业率较高的地区位于港口附近和城市的内北侧。**尽管从 2012 年到 2020 年,这些街区的失业率有所下降,但这些地区的失业率一直是全市最高的。此外,我们可以观察到,失业率在巴塞罗那的地理分布并不均匀,因为受影响最严重的地区的失业率是失业率较低的街区的 4 倍左右。

失业率在整个城市的分布并不均匀,因为受影响最严重的社区的失业率比失业率较低的社区高出约 4 倍。

新冠肺炎对失业率的影响

冠状病毒爆发严重影响了全球经济;特别是,西班牙在 2020 年裁员超过 50 万人。这是有道理的,因为西班牙的经济主要基于旅游业,而没有像其他欧洲国家那样强大的生产部门。然而,使用以前的可视化方法,无法计算出哪些地区在失业方面受到了电晕危机的最严重影响。下面的可视化旨在解决这个问题。它显示了按降序排列的相邻地区失业率的相对变化(仅显示前 10 名)。使用以下公式计算相对变化百分比。

百分比变化=((2020 年 12 月的比率-2019 年 12 月的比率)/2019 年 12 月的比率)* 100

不出所料,巴塞罗那的所有街区去年都面临着失业率的上升,从 17.11(埃尔邦帕斯托尔)到 65.5(埃尔拉瓦尔)不等。在科罗纳危机期间,共有 6 个区的失业率相对变化超过了 40%:埃尔拉瓦尔、埃尔波布尔-塞、拉丰德拉瓜特拉、拉德雷塔德莱辛普、拉巴塞洛内塔和圣佩雷、圣卡塔林纳和拉里贝拉。然而,如下所示,在电晕危机开始前的失业率和一年后失业率的相对变化之间没有关系,这意味着失业率较高的地区没有比失业率较低的社区遭受更大的相对增长。

由于电晕危机,巴塞罗那的所有街区都面临着失业率的上升。

2019 年 12 月的失业率与冠状病毒疫情(按街区)导致的失业增量之间没有关系。

**埃尔拉瓦尔是受科罗纳危机影响最严重的社区,失业率甚至达到了 2012 年的水平。**下图显示了电晕危机期间失业率的相对变化,这次使用的是 choropleth 地图。正如你所看到的,受影响最严重的地区位于港口附近;然而,如前所述,我们无法观察到 2019 年 12 月至 2020 年 12 月失业率的相对变化与 2019 年 12 月电晕危机开始前的失业率之间的关系(如上散点图所示)。

就失业而言,埃尔拉瓦尔社区受电晕危机的影响最大

按持续时间划分的失业人数(所有社区)

巴塞罗那公开数据还提供了按持续时间和地区分列的失业人数信息。下图显示了 2013 年 1 月至 2020 年 12 月期间所有社区的失业人数(聚合值)。可以看出,失业人数(三组持续时间)随着时间的推移不断减少,直到爆发电晕危机。从那一刻起,长达 6 个月的失业人数迅速增加,直到 2020 年 5 月,这表明许多工人在危机开始时被解雇。从 6 月份开始,我们观察到失业人数在 6 个月内急剧下降,这意味着没有额外的员工被解雇。然而,在疫情电晕期间被解雇的工人在危机期间肯定没有找到工作,因为长期失业(6 个月以上)的人数自危机开始以来一直没有停止增长。

疫情在危机期间被解雇的工人在危机期间肯定没有找到工作,因为自危机开始以来,长期失业(六个月以上)的人数一直没有停止增长。

按性别分列的失业人数

性别平等必须是任何民主国家议程的焦点。虽然在过去的几十年里,西班牙妇女的角色确实发生了彻底的变化,但仍有许多工作要做💜。

下图显示了所有街区的男女失业人数(合计值)。正如你所观察到的,2020 年 12 月,巴塞罗那大约有 5 万名失业女性和 4.4 万名失业男性。我们注意到,从 2013 年到科罗纳危机开始,男女失业人数都在稳步下降;然而,男性失业率下降得更快。就就业而言,男性比女性从经济增长中获益更多。

如果您对某个特定的地区感兴趣,您可以使用位于左上角的下拉列表进行选择。在与可视化互动一段时间后,你会注意到,在几乎所有的街区,失业女性的数量都超过了男性。正如我所说的,仍然有工作要做😉。

几乎在所有社区,失业女性的数量都超过了男性。

为了恰当地显示地区之间的差异,我们将使用一个 choropleth 图。下图显示了 2020 年 12 月按街区划分的男女失业人数的差异。粉红色区域代表失业女性人数多于男性人数的社区。正如你所看到的,还有更多地区的失业妇女人数更多,尽管在其中一些地区,这种差异并不显著。El Raval 显然是一个例外,因为男性人数明显多于女性。

失业与收入

在结束这篇文章之前,我们要分析一下巴塞罗那市各个区的家庭平均收入和失业率之间的关系。如前所示,巴塞罗那市有些区的失业率是其他区的 4 倍左右,正如你所料,收入也是如此。正如你在下面观察到的,收入和失业之间有很强的负相关关系,这意味着高家庭收入的社区也有低失业率。在下面的图中,气泡的大小代表 16 至 64 岁之间的人口,动画(播放和停止按钮)让我们可以直观地看到 2015 年和 2018 年之间这种关系的演变(这是我们从巴塞罗那开放数据中获得每户家庭收入信息的唯一年份)。

收入和失业之间有很强的负相关关系,这意味着高家庭收入的社区失业率也低。

上图显示,即使在巴塞罗那这样繁荣发达的城市,社会不平等现象依然存在。从这个意义上说,重要的是,政府要尽一切努力尽量减少这些不平等,确保即使是来自低收入地区的人也有平等的生活机会,当然包括工作权。

注:所有的可视化效果都是由作者创作的

感谢阅读💜

阿曼达·伊格莱西亚斯

PyOD 异常检测

原文:https://towardsdatascience.com/anamoly-detection-with-pyod-fea90f0b4b42?source=collection_archive---------18-----------------------

这个 Python 库(PyOD)使用算法使异常值检测更加容易和全面

鲁珀特·布里顿通过 Unsplash 拍摄的照片

上周,我和我的熨斗学校——数据科学全日制沉浸式项目同学们进入了我们训练营旅程的科学计算和定量方法阶段,我们接受了在短时间内吸收大量统计知识的挑战。虽然这肯定不是我们最轻松的一周,但通过每次演示、计算和可视化,统计和机器学习的效用之美变得越来越明显。

有一件事特别引起了我的注意,那就是一个简单的三变量计算的重要性:Z 分数。对于那些不熟悉的人,Z 分数的计算如下:

z = (x-μ)/σ

z ”表示一个数据点离总体平均值有多远,以标准偏差为单位。在这个计算中, x 是一个实验数据点, μ 是平均值, σ 是标准差。很简单,对吧?在外行看来,这只是一个数字。

但是当我得知它在统计测试中服务于多少不同的应用时,我感到很惊讶。事实证明,z 分数在数据分析和概率计算中非常有用。此外,它还是检测数据集中异常的各种不同技术的重要组成部分。

检测数据中的异常

有各种不同的技术和方法来检测数据集中的异常,主要是因为有大量不同的方法可以做到这一点。到目前为止,我们已经了解了一些,例如:

  • 使用像 Matplotlib 和 Seaborn 这样的数据可视化工具来绘制数据集和评估数据分布中的异常值,
  • 使用 Pandas 执行无缝数据清理并生成描述性统计数据
  • 假设检验策略,如 Z 和 P 检验,利用人工计算的描述性统计

我们在短短几周内学会了这些策略和其他策略,这让我想知道:在最大、最混乱的数据集中…在数百种(如果不是更多)不同概率分布类型的世界中…这些不可能是检测异常值的唯一方法。如果有许多其他不同的方法来检测数据中的异常,是否有一种简单的方法来选择您想要使用的技术并将其应用到您的数据中?毕竟,异常值检测不仅在数据科学中非常重要,而且在大多数其他数学或计算学科中也非常重要。这让我偶然发现了一个迷人的 Python 库:Python PyOD 模块!

PyOD —更简单的异常测试

你看看那个!有一个全面的、可扩展的 Python 工具包用于检测异常值/异常(注意:这两个术语通常可以互换使用),称为 PyOD。你能猜出这个名字代表什么吗?!(提示:可能与“Python”和“离群点检测”有关)。

PyOD 的完整文档可以在这里找到。

PyOD 文档中的第三句话立刻给我留下了深刻的印象:“PyOD 包括 30 多种检测算法。”Welp,这回答了我之前的问题——很明显,在不同类型的数据集中,有多种不同的技术来检测异常值。如果不使用 PyOD 工具包,其中的许多工作将会非常耗时和复杂!

为什么

离群点检测包存在于许多其他编程语言中,如 Java 和 R,PyOD 开发团队认识到 Python 中缺少离群点检测的专用工具包。Python 的单一算法工具,如 PyNomaly,仅限于一种算法。或者,多用途框架,如广为人知的 scikit-learn,并没有专门针对异常值检测的问题。

PyOD 的开发人员创建了一个全面的库,它专门用于可扩展的异常值检测,可以根据被评估的数据类型选择所需的方法。

它是如何工作的

PyOD 可以与 Python 2 和 3 一起工作,并且还依赖于非常常用的库 NumPy、SciPi 和 scikit-learn 来执行其最基本的功能。对于某些附加功能,如深度学习模型和运行基准,其他依赖项(如 Pandas、Matplotlib 或 TensorFlow)是可选的导入。

PyOD 通过一系列易于使用的统一 API 提供了对离群点检测算法集合的访问。每个 API 都带有完整详细的指导文档和附属示例,以直观地展示如何开始实现每种不同类型的算法。下面是我从 PyOD 文档中提取的一个非常简单的例子,它用 5 行代码演示了异常值检测的 PyOD 实现:

使用 PyOD 只需 5 行代码即可检测异常值

在上面的例子中,COPOD(Copula-Based Outlier Detection)被选为这个计算的异常检测算法。正如您所看到的,PyOD 内置功能使您可以通过简单的导入轻松选择其库中包含的 30 多种算法之一,并在几秒钟内应用复杂的概率函数。幸运的是,这些算法在文档中附有详细的分步示例,引导您完成其功能的示例应用。

更复杂的 PyOD 算法应用程序的例子也可以直接在他们的文档中找到。例如,他们包括了一个笔记本,对同一个数据集实现了 12 个不同的异常值检测模型,允许进行多用途的比较统计,否则在 Python 中是不容易访问的。看看下面附带的可视化结果。PyOD 功能内置了这种清晰有序的可视化效果。

via PyOD 文档

改变游戏

毫无疑问,PyOD 在机器学习社区享有盛誉。它使访问重要的统计功能变得更加容易,而且功能更加多样。在 Github 上很容易获得这些文档,这些例子使得复杂的机器学习算法技术对像我这样好奇的学生来说不那么可怕了。我迫不及待地想继续玩 PyOD 工具包,探索无限的可能性!

dbt 项目剖析

原文:https://towardsdatascience.com/anatomy-of-a-dbt-project-50e810abc695?source=collection_archive---------4-----------------------

一些基本概念和项目结构可以帮助您开始 dbt 项目。

照片由 Unsplash 上的谷仓图片拍摄

对于非技术背景的分析师来说,一开始使用 dbt 可能会令人生畏。编写代码来配置一切需要一些时间来适应,尤其是如果您习惯于使用 UI 来设置一切。

如果您考虑为您的团队使用 dbt,或者刚刚开始使用 dbt 的团队,这篇文章有望帮助您了解项目结构和一些基本概念。我们将浏览下图中的大多数文件和文件夹。你可以在这里进行回购备案。

图片来自 dbt-init 项目。

profiles.yml

该文件包含 dbt 将用来连接到数据仓库的数据库连接。如果您在本地设置了 dbt,那么您只需要担心这个文件。

由于该文件可能包含敏感信息,如项目名称和数据库凭据,因此它不在您的 dbt 项目中。默认情况下,文件创建在文件夹:~/.dbt/中。

如果您从头开始您的项目,运行dbt init将为您生成这个文件。如果没有,你可能需要在本地创建一个.dbt文件夹和一个profiles.yml文件。如果您不确定如何设置该文件,请遵循 dbt 文档或联系回购所有者。

如果您在本地处理多个项目,不同的项目名称(在dbt_project.yml文件中配置)将允许您为其他项目设置不同的概要文件。

除了配置数据库连接,您还可以配置目标。目标是如何为不同的环境提供不同的配置。例如,当在本地开发时,您和您的团队可能希望在单独的数据集/数据库上工作。但是当部署到生产环境时,最好将所有表放在一个数据集/数据库中。默认目标是dev

dbt_project.yml

该文件是项目的主配置文件。如果您正在创建您的项目,请更改项目名称和概要文件名称(最好是相同的值)。此外,用新的项目名称替换models部分的my_new_project

除非在模型级别被覆盖,否则此项目中的所有对象都将继承此处配置的设置。例如,您可以配置在dbt_project.yml文件中将所有模型创建为表格。但是您可以进入其中一个模型并覆盖该设置来创建一个视图。

在哪里配置什么取决于您项目设置,但是遵循一个好的原则是 DRY(不要重复)。对于应用于整个项目或特定文件夹的设置,请在此文件中定义。对于仅与一个型号相关的选项,请在那里进行配置。

关于设置的完整列表,你可以在这个文件中配置,查看dbt_project.yml 文档。

模型

models文件夹包含项目中的所有数据模型。在该文件夹中,您可以创建任何想要的文件夹结构。dbt 风格指南中给出的推荐结构如下:

├── dbt_project.yml
└── models
    ├── marts
    |   └── core
    |       ├── intermediate
    |       |   ├── intermediate.yml
    |       |   ├── customers__unioned.sql
    |       |   ├── customers__grouped.sql
    |       └── core.yml
    |       └── core.docs
    |       └── dim_customers.sql
    |       └── fct_orders.sql
    └── staging
        └── stripe
            ├── base
            |   ├── base__stripe_invoices.sql
            ├── src_stripe.yml
            ├── src_stripe.docs
            ├── stg_stripe.yml
            ├── stg_stripe__customers.sql
            └── stg_stripe__invoices.sql

这里,models下面有martsstaging文件夹。不同的数据源在staging下会有单独的文件夹(例如stripe)。用例或部门在marts下有不同的文件夹(例如coremarketing)。

注意上面例子中的.yml.doc文件。它们是您为模型定义元数据和文档的地方。您可以在dbt_project.yml文件中包含所有内容,但是在这里定义它们要干净得多。

这种结构允许您清晰地组织对象,并轻松地应用批量设置。

数据

该文件夹包含将由 dbt 加载到数据库的所有手动数据。要将该文件夹中的.csv文件加载到数据库,您必须运行dbt seed命令。

因为这是一个受版本控制的存储库,所以不要将大文件或包含敏感信息的文件放在这里。定期更改的文件也不适合这种方法。适合dbt seed的例子有年度预算、状态映射、类别映射等。

宏指令

dbt 中的宏类似于 excel 中的函数。您可以在macros文件夹中定义自定义函数,或者覆盖默认宏和宏包。宏和 Jinja 模板一起,给了你许多 SQL 所没有的功能。

例如,您可以对不同的目标使用控制结构(if 语句和 For 循环)或不同的行为。您还可以抽象出复杂的 SQL 逻辑,使您的代码更具可读性。

-- This is hard to read
(amount / 100)::numeric(16, 2) as amount_usd-- This is much easier
{{ cents_to_dollars('amount') }} as amount_usd

packages.yml

dbt 的一个优点是,您可以轻松地使用其他人制作的包。这样做可以节省你大量的时间,你也不必重新发明轮子。你可以在这里找到大多数 dbt 包。

要使用这些包,你需要在packages.yml文件中定义它们。只需将如下内容添加到文件中。

packages:
  - package: dbt-labs/dbt_utils
    version: 0.7.3

在使用这些包之前,您必须运行dbt deps来安装这些依赖项。之后,您可以使用 cod 中的包中的任何宏。

快照

快照是一个 dbt 特性,用于捕获表在特定时间的状态。快照文件夹包含项目的所有快照模型,它必须与模型文件夹分开。

其用例是为不支持变更数据捕获(CDC)的源构建渐变维度(SCD)表。例如,每次订单状态改变时,您的系统都会用新信息覆盖它。在这种情况下,我们无法知道这个秩序有什么历史地位。

Snapshot 提供了一个解决方案。如果您每天都捕获订单表,您可以保留订单状态随时间变化的历史记录。对于每个记录,dbt 将确定它是否是最新的,并更新旧记录的有效期。

图片来自 dbt 文档

点击阅读更多关于 dbt 快照功能的信息。

试验

dbt 项目中的大多数测试都是在models下的.yml文件中定义的。这些是使用预制或定制宏的测试。这些测试可以应用于模型或列。

图片来自 dbt 文档

在上面的例子中,当你运行dbt test时,dbt 会检查orer_id是否为uniquenot_null,状态是否在定义的值内,并且customer_id的每条记录都可以链接到customers表中的一条记录。

但是,有时这些测试是不够的,您需要编写自定义的测试。在这种情况下,您可以将那些定制测试存储在tests文件夹中。dbt 将评估 SQL 语句。如果没有行返回,测试将通过,如果至少有一行或多行返回,测试将失败。

结论

在本文中,我们已经介绍了典型 dbt 项目的一些基本概念和项目结构。我希望这对你来说是一个迷人的新旅程的开始。

快乐的 learning✌️

一种量子机器学习算法的剖析

原文:https://towardsdatascience.com/anatomy-of-a-quantum-machine-learning-algorithm-24d97dfd388d?source=collection_archive---------45-----------------------

什么是变分量子经典算法,我们为什么需要它?

本帖是本书的一部分: 动手用 Python 学习量子机器

作者弗兰克·齐克特的图片

柯克:“斯波克先生,你在你的时间重返程序中考虑到鲸鱼和水的可变质量了吗?”

斯波克:“斯科特先生不能给我确切的数字,上将,所以……我会猜测一下。”

柯克:“一猜?你,史巴克?这太不寻常了。”

斯波克:对麦考伊说:“我认为他不明白。”

不,斯波克。他的意思是,他觉得你的猜测比大多数其他人的事实更安全。”

斯波克:“那你是说……这是一种赞美?”

麦科伊:“是的。”

斯波克:“啊。然后我会尽我所能做出最好的猜测。”

不,你不需要猜什么是变分量子经典算法。但是你马上就会明白这个介绍是关于什么的。

量子计算是一种不同形式的计算。一种可以改变解决问题的复杂性,使问题变得容易处理的形式。也许,你听说过 Shor 的因式分解算法。该算法将寻找给定数的(质数)因子的复杂度从指数复杂度降低到亚指数复杂度。

如果这听起来没有压倒性的,让我们重新措辞。现代密码学建立在不可能分解一个大数的假设之上。对于经典计算机来说,这是不可能的,因为找到它的复杂性呈指数级增长。

Shor 证明了量子计算机可以在合理的时间内分解一个大数。一台能够运行肖尔算法的量子计算机会像纸牌搭的房子一样瓦解现代密码学。

机器学习面临的挑战堪比因式分解问题。我们为我们的模型寻找一组参数来预测未来。但是有很多可能的参数组合。经典地计算它们实在是太复杂了。

指数级和亚指数级复杂性之间的差异不可低估。例如,你的智能手机能够在几秒钟内将 800 位数相乘。这是一个亚指数问题。

相比之下,这类数字的因式分解在一台超级计算机上需要大约 2000 年。因为这是一个指数级增长的问题。

作者弗兰克·齐克特的图片

只有一个问题。这些算法需要数百万个量子位(qubits)。IBM 和谷歌的旗舰量子计算机目前有大约 50 个量子比特。而这些量子位甚至没有那么好用。

量子计算机需要非常精确。他们需要保持连续的量子态。任何微小的波动都可能毁掉一次计算。但今天的量子计算机噪音太大,最大计算时间以毫秒而不是秒计量。

我们近期预期的量子处理器仍然相对较小且噪音较大。我们生活在一个被称为“嘈杂的中间尺度量子”的时代——T4。

在这个时代,我们无法使用百万量子比特。在这个时代,我们不可能几个小时甚至几天都在运行算法。

当前的 NISQ 设备时代需要一套不同的算法、工具和策略。

例如,变分量子经典算法已经成为考虑近期量子设备量子算法的一种流行方式。在这些算法中,经典计算机对它们从量子计算机上运行某些难以计算的计算中获得的信息执行整体机器学习任务。

量子算法基于经典算法提供的一组参数产生信息。因此被称为参数化量子电路(PQCs)

它们相对较小,寿命较短,因此适用于 NISQ 设备。

变分混合量子经典算法由三部分组成:

  • 我们在经典计算机上预处理数据来计算量子态。
  • 我们在量子电路中计算我们的解,并测量代表结果的量子位元。
  • 我们对测量值进行后处理,并将其转换为预测值。

下图描述了这种算法的结构。

作者弗兰克·齐克特的图片

通常,PQC 包含模型——或者模型的一部分。但它的参数,如神经网络中的权重或贝叶斯网络中的概率,都被排除在外。每次我们运行模型时,它们都会被输入到模型中。

这种结构允许我们永久地存储参数(或者至少比几秒钟更长)。同时,我们可以利用量子电路来降低计算的复杂性。

让我们来看看这样一个算法的运行情况。假设我们的模型是一个有两个节点的量子贝叶斯网络。

作者弗兰克·齐克特的图片

我们有两个二元变量AB。我们知道他们不是独立的。A是父节点。B是我们试图估计其条件概率的子节点。

我们有一些数据,但有一项不完整。我们遗漏了一个值。

作者弗兰克·齐克特的图片

在贝叶斯网络中,我们一直在处理概率。我们为什么不用一个概率分布代替一个特定值来填充缺失的数据点呢?

等等!如果这个分布是我们首先要计算的,那么我们如何用概率分布来填充这个值呢?让我们做点不平凡的事。斯波克。

我们猜测一个分布。我们用它来计算给定 a 的对数似然得分和 B 的下一个分布。

我们在用分布填充缺失数据和估计新的概率分布之间迭代。

下面是这个算法的实现。

我们从从 Qiskit 导入库开始,Qiskit 是用于量子计算的 IBM SDK(第 1–4 行)。然后,我们初始化我们的数据(第 6–8 行)。

我们使用两个方便的函数。首先,prob_to_angle-函数(第 11-12 行)获取一个概率,并将其转换成我们可以在量子位变换门中使用的旋转角度。

log-likelihood-函数计算模型产生观察数据的可能性(第 15–28 行)。我们使它适应我们的具体情况。它期望data是一个元组列表,每个元组有两个条目。此外,它需要我们模型的参数。这些是 A 和 B ( prob_a_b)、A 和 not B ( prob_a_nb)、not A 和 B ( prob_na_b)、not A 和 not B ( prob_na_nb)的概率。

我们为列表中的每个元组调用函数get_prob,并返回所有结果的总和。这个函数get_prob获取一个数据point(元组)并评估它的组合。它只是返回相应概率的对数。例如,如果 A 和 B 的值都是1,则返回log(prob_a_b)—A 和 B 的概率。

如果不能识别组合,则返回prob_na_bprob_na_nb之和的对数。这是当我们错过 b 的值时的情况。我们的数据中只有一个案例((0, None)),其 A 的值是0。因此,我们知道它不包含 a,但是我们不确定 b。

下一个代码清单描述了一个量子贝叶斯网络

它从声明两个列表开始。第一个list_a(第 4 行)包含我们数据中的所有项目,其中 A 的值为1表示 A 为真。第二个list_na(第 5 行)包含所有 A 的值为0代表非 A 的项目,我们用这些列表计算四个组合(A∧ B,A∧ B,A∧B,A∧B)的概率。

我们从 A 的边际概率(第 8 行)开始。这是我们的数据中 A 为真的项目数(1,长度list_a)除以我们的数据中的项目总数(data的长度)。我们让位置 0 的量子位代表这个概率(第 10-12 行)。

接下来,我们将 A 为假的情况分为 B 为真的情况和 B 也为假的情况(第 13–17 行)。首先,我们通过在位置 0(第 13 行)的量子位上应用非门来“激活”A 为假的状态。当 b 为真时,受控的 RY 门将位置 1 的量子位设置为|1⟩状态(第 14 行)。我们通过将 A 为假而 B 为真的项目数除以 A 为假的项目数来计算概率(第 15 行)。当然,在 a 为真的情况下,我们需要将量子位转换回|1⟩态(第 17 行)。

最后,我们把 A 为真的情况分成 B 也为真的情况和 B 为假的情况。我们应用另一个受控的 RY 门。旋转角度表示假设 A 也为真,B 为真的概率(第 21 行)。

下图用图形描绘了这个量子电路。

作者弗兰克·齐克特的图片

所有这些概率和旋转角度的计算描述了 PQC 的预处理步骤。我们在经典计算机上准备数据以产生有效的量子态。

让我们运行这个量子电路。

作者弗兰克·齐克特的图片

四种不同状态的测量概率代表 A 或 A 和 B 或 B 的可能组合的概率。

一旦我们运行了电路,我们需要对它进行后处理。

让我们将结果输入到我们之前创建的log_likelihood函数中。我们为此创建了另一个便利函数。

该功能eval_qbn(第 1 行)以qbn-功能为模型。但是我们也可以插入任何其他模型,只要它接受给定格式的数据集并返回我们从 Qiskit 获得的结果。第二个参数prepare_data是一个处理丢失数据点的函数。我们简单地输入数据,并期待我们输入模型的数据集(第 2 行)。

该函数返回给定模型的对数似然得分(第 3 行)。因此,我们提供了从量子电路得到的概率测量(第 4-7 行)。注意,我们从量子电路中得到的状态是从右边(位置 0 的量子位代表 A)读到左边(位置 1 的量子位代表 B)。

现在,让我们把这些放在一起。

我们用 P(B| A)=0.5 初始化我们的分布,并运行 PQC。

这似乎是一个相当不错的猜测。对数似然得分为 9.476。

但是我们不会就此止步。模型告诉我们 P(B| A)=0.3 的新值。让我们用这个值来运行我们的模型。

我们的模型改进了。我们得到了 9.452 的对数似然得分和缺失数据点的新分布。

显然,我们可以在用分布填充缺失数据和估计新的概率分布之间迭代。

结论

这个迭代过程是一般过程的一个例子,称为期望最大化(EM)算法。

先不累,就迭代,直到分数收敛。不过,很难判断新兴市场何时已经收敛。有时候,模型只是在很长一段时间内变得稍微好一点。一旦你认为这个过程完成了,分数就会突然增加。没办法分辨。

这个小程序展示了我们如何有效地实现一个变分量子经典算法。在预处理步骤中,我们将经典数据转换成量子态。然后,我们在量子计算机(或者模拟器——如果你没有量子计算机的话)上运行参数化的量子电路。最后,在后处理步骤中,我们评估量子电路的结果以更新我们的模型并重新开始。

通过将模型保持在量子电路之外,我们可以将它存储更长的时间,并将量子电路限制到最小。这可能允许我们甚至在我们目前生活的 NISQ 时代使用它。

本帖是本书的一部分: 用 Python 动手做量子机器学习

在这里免费获得前三章。

增强现实中人脸跟踪的基础知识

原文:https://towardsdatascience.com/anchors-and-facetracking-in-ar-for-dummies-7a297ffbf7fe?source=collection_archive---------21-----------------------

图片由 Unsplash 的 John Noonan 提供

锚点、实体、混合形状、系数和表达式

一点历史

苹果最新的 iPads 和 iPhones 配备了激光雷达扫描仪,正如你们许多人所知,它代表光探测和测距。它是一个使用红外光从场景中收集深度信息的传感器。在激光雷达之前,我们使用无线电波来实现这一目的……无线电探测和测距(雷达)。它非常适合更短的距离,而且更加精确。激光雷达工作在更远的距离,但在雾或雨不是很准确。

为什么我要说激光雷达?因为它是消费设备中增强现实的下一次飞跃。对于一个四处移动并理解真实世界的机器人来说,它需要理解它的周围环境以及它相对于该空间中其他事物的位置和方向。

你的手机是一个没有四肢的机器人。它是一个大脑和一些为大脑提供信息的传感器,以便更好地了解它的环境。激光雷达是一种增强其视觉的传感器。

我们人类通过利用双眼之间的视角差异来获得深度感。在计算机上同时保持实时帧速率是一项极其昂贵的计算。所以现在我们不得不使用激光雷达传感器和一只眼睛/相机来凑合。

我们已经取得了相当大的进步,从仅仅能够检测水平面到在带有激光雷达的消费设备上相当精确地检测深度。由于我们现在可以用 AR 做更多的事情,我们可以开始利用增加的功能来构建更复杂的体验。

为此,我们需要框架来抽象出底层系统的复杂性,并为程序员、艺术家和设计师提供额外的功能,以便在更短的时间内构建 AR 体验,并减少麻烦。

RealityKit 就是这样一个框架,它的姐妹 iOS 应用程序 Reality Composer 来自 App Store,是该框架的可视化包装器,RealityKit 的拖放功能的启用者,以及隐藏底层框架复杂性的迷你内容创建平台。

为什么真人秀作曲家可能成为下一个“iMovie”

iOS 系统上的每个底层操作系统特性都有相应的编程框架。例如用于绘制 UI 的 UIKit、用于 Sprite 动画的 SpriteKit、用于用 iCloud 编程的 CloudKit、用于编程增强现实(AR)体验的 ARKit 等。

RealityKit 框架使用 ARKit 框架来创建 AR 体验。ARKit 框架处理来自 IMU、视觉系统和激光雷达的数据,以在空间中定位自己,并找出周围其他事物的位置。Reality kit 使用这些信息将虚拟对象带入 AR 空间。

RealityKit 还使某些样板任务变得更容易,如在构建共享 AR 体验的同时处理网络上的连接管理。虽然这不是 RealityKit 的出名之处。它主要是因为提供了一个平台来编程骨骼动画、真实的阴影、照明、反射等。这些能力通过一个名为 Reality Composer 的应用程序带给艺术家和设计师。

你会觉得这些东西在虚幻引擎,Unity,Maya,3DSMax,Houdini 等里面处理的比较好。然后可以将它们作为 USDZ 文件导入。苹果为什么需要开发 3D 内容创作平台?也许他们有计划在这个市场上竞争,并在 Mac 上提供端到端的解决方案来创建和消费 AR 体验。这是我的推测。

Reality Composer 是一个用于 AR 的 DCC(数字内容创作)平台。它既可以在 Mac 上运行,也可以在 iPhone 和 iPad 上运行。我在我的笔记本电脑上使用它,我发现它有直观的动作控制。

连按两次以重设世界,捏合以放大和缩小。放下一个手指,用另一个手指旋转,等等。在 iPhone 或 iPad 上,您可以直接在 AR 中设计 3D 内容。虽然现在这可能看起来像是一个噱头,但将来当 AR 平台支持更复杂的设计时,这一点的用处将显而易见。

我们在虚拟现实中达到了一个类似的里程碑,Epic Games 允许虚幻引擎在虚拟世界中运行编辑器。你可以戴着虚拟现实耳机,用你的 Vive 控制器抓住东西并移动它们。

这不是设计关卡的最佳方式,因为长时间在虚拟现实头盔中工作的人在后勤方面存在固有的问题。但是有一点是肯定的——如果它能大规模工作,它将减少迭代时间。

在桌面应用程序上设计一些东西,然后进行构建并在设备上部署构建,以检查您在 VR 中的工作,这是许多不必要的往返。在你的 iPad 或 iPhone 上设计东西并不是一种不舒服或陌生的体验。

因此,iOS 设备上的 Reality Composer 可能有很好的增长潜力,可以作为未来为 AR 创建内容的首选应用程序。即使您要在 Mac 上进行设计,Reality Composer 也支持快速查看功能。

从应用程序中导出场景,通过电子邮件发送给自己或保存在 iCloud 上,并在 iPhone 上打开 Reality Composer 场景。它会向你展示你的设计在现实中的样子。无需在 XCode 项目中导入场景,构建并在设备上部署它来检查您的工作。

你也可以在 Safari 中嵌入场景。Safari 有 AR 的快速查看功能。你可以用你的 iPhone 去 apple.com 看看,在 AR 里浏览他们的每一件产品。使用快速查看功能,直接从 Safari 查看工作区中的 Mac Pro 或 Mac Book。

Reality Composer 现在完全集成到 XCode 中。

对我来说,这是如何将这些部分设计成形成一个端到端的管道,为苹果生态系统中的 AR 创建内容的一瞥。

随着最新的传感器和框架的出现,让我们继续跟踪,看看它们是如何用于跟踪人脸的。

AR 中的锚和实体

锚点有助于跟踪已识别的对象。当对象被 AR 系统识别时,锚被放置在对象上。它记录了物体在世界空间中的方向和位置。

随着相机四处移动,识别的对象相对于相机移动,AR 系统试图跟踪移动,以便锚可以每帧更新。

锚由一个名为 didUpdateFrame 的会话委托函数(回调函数的别称)访问。其中,这个 updateFrame 函数接收一个 ARFrame

ARFrame 包含关于摄像机、锚、场景、跟踪、绘图状态、地理跟踪状态、分割(检查人)等信息。大多数 AR 应用程序都需要访问 ARFrame 中的数据。它以应用程序帧速率的频率可用。

任何东西都可以放在锚上。宇宙飞船、小球或图像的 3D 模型。

一旦它们被锚定,AR 跟踪系统就会跟踪它们,这样当你四处移动时,它们就可以在世界的同一个地方被维护。就像你在现实世界中四处走动时会看到一棵树一样。

这些放置在锚点的对象在 AR 中被称为实体

当 ARKit 检测到一张脸时,它会在被检测为鼻子的区域的正后方放置一个锚。下图中,黄点是大概的位置。这个锚叫做 ARFaceAnchor 。当会话在前置摄像头输入中检测到唯一的人脸时,它会自动将一个 ARFaceAnchor 对象添加到其锚点列表中。

作者图片

它是如何检测人脸的?面部跟踪系统是由一串记录眼睛、眼球、眉毛、下巴等运动的数字定义的。这些数字被称为系数。这些系数定义了面拓扑。然后,该拓扑用于创建一个名为 ARFaceGeometry 对象的几何体。

该几何模型可用于将自定义对象“附加”到 AR 面上。导出系数和重建面网格几何体也比导出网格更容易,因为网格中顶点的数量远远大于系数的数量。

系数是浮点数,其值的范围从 0.0 到 1.0。系数的不同值记录了不同的面部表情。代表特定表达式的这些系数的集合称为混合形状。因此这些系数被称为混合形状系数。

这些系数使得 3D 角色的面部动画成为可能。从系数开始,让我们看看面网格几何本身。

人脸网格几何是人脸跟踪系统的重要组成部分。您可以在锚点位置放置一个面罩。演员在剧院里用于表演训练的那种面具。该遮罩在 Reality Composer 中作为 3D 网格随时可用。ARKit 将通过在用户移动他们的脸时跟踪底层锚来跟踪面具。

或者你可以使用这种几何图形作为叠加内容的基础,如化妆或酷太阳镜,就像 Snapchat 所做的那样。Snap 使用了这些相同的原则和基本概念,只是他们没有使用 ARkit。他们开发了自己的 AR 系统。

让我们花一点时间来讨论一下跟踪,这样您就可以充分理解使这一切成为可能所需的各种底层技术。

AR 中的跟踪

追踪并不像听起来那么容易。当你四处移动时,你可能会在离物体几米远的地方徘徊,它可能不会在你的相机视野内。但是如果你决定转身回来,你会希望它停留在你最后看到它的地方。

这些都是计算量很大的计算,所以需要一些捷径来找到实时帧速率和准确性之间的平衡。因此,对象的位置可能会随着时间的推移而改变,您可能会发现锚点的位置与上次看到时略有不同。这种差异被称为漂移。

不同的系统有不同程度的漂移。像 Hololens 和 Magic Leap 这样的头戴式 AR 设备有专用的硬件和更多的资源来执行空间映射(用几何图形/三角形近似相机看到的世界)和跟踪。因此,这些设备往往比手持 AR 设备更准确,如 iPad、iPhone 和越来越好的 Androids。

在 iOS 设备上引入激光雷达是帮助空间制图的一大飞跃,因为激光雷达用精确的深度信息增强了相机数据,所以系统不需要处理来自相机馈送的数据来估计深度。

最后

我们讨论了激光雷达及其重要性。我们谈到了 Reality Composer 及其潜力,然后浅尝辄止地研究了跟踪、锚点,尤其是面部跟踪。

希望它有助于作为进入这些有趣的话题的跳板。如果你有任何问题,请在下面留言,我会尽力回答。此外,Apple 的开发人员文档是一个很好的资源,可以找到您可以自己尝试的编码示例。

机器学习中 11 个榜样的轶事

原文:https://towardsdatascience.com/anecdotes-from-11-role-models-in-machine-learning-d01bc0d65dcd?source=collection_archive---------14-----------------------

创建良好数据所需的技能也是优秀领导所需的技能

我最近写了一本书,我希望在我被介绍到机器学习时就存在:人在回路中的机器学习:以人为中心的人工智能的主动学习和注释。大多数机器学习模型都是由人类注释的数据指导的,但大多数机器学习书籍和课程都专注于算法。你经常可以用好的数据和简单的算法得到最先进的结果,但是你很少用坏数据从最好的算法得到最先进的结果。因此,如果你需要首先深入机器学习的一个领域,你可以认为数据方面更重要。

除了这本书的技术重点,它还介绍了 11 位机器学习专家的轶事。每个人都分享了一个关于他们在现实世界中建立和评估机器学习模型时遇到的数据相关问题的轶事。他们的故事更广泛地告诉了我们一些关于机器学习领导力的重要事情,每个轶事都与运行成功的数据科学项目有关。

机器学习领域的 11 位专家在《人在回路中的机器学习》中作了专题介绍。(所有图片均经每位专家许可使用,并在下面以他们各自的轶事重复)

这些专家是根据两个标准挑选的:他们都创建了成功的机器学习公司,他们都直接在机器学习的数据方面工作。对于考虑从事机器学习职业的人来说,他们都是很好的榜样:阿扬娜·霍华德、丹妮拉·布拉加、艾琳娜·格雷瓦尔、伊娜丝·蒙塔尼、詹妮弗·普伦德基、李嘉、基兰·斯奈德、丽莎·布雷登-哈德、马修·霍尼巴尔、彼得·斯科莫罗如果你正处于职业生涯的早期,正在努力为你的模型创建良好的数据,那么我希望你能理解书中的许多趣闻,分享如下:

艾安娜·霍华德

“父母是完美的主题专家”

关于人的模型对于数据中没有表示的人来说很少是准确的。有许多人口统计偏见会导致人们的代表性不足,如能力、年龄、种族和性别。此外,还经常存在交叉偏见:如果人们在多个人口统计中的代表性不足,那么有时这些人口统计的交集不仅仅是各个部分的总和。即使您有数据,也可能很难找到有适当经验的注释者来正确地注释它。

在为有特殊需求的孩子制造机器人时,我发现没有足够的数据来检测儿童的情绪,检测来自代表性不足的种族的人的情绪,以及检测自闭症患者的情绪。没有身临其境体验的人往往很难识别这些孩子的情绪,这限制了谁可以提供训练数据来说明孩子何时真正快乐或沮丧。甚至一些训练有素的儿童医生在处理能力、年龄和/或种族的交叉性时也难以准确地注释数据。幸运的是,我们发现孩子自己的父母是他们情绪的最佳判断者,所以我们为父母创建了快速接受/拒绝模型对孩子情绪的预测的界面。这使我们能够获得尽可能多的培训数据,同时最大限度地减少家长提供反馈所需的时间和技术知识。这些孩子的父母被证明是完美的主题专家来调整我们的系统以满足他们孩子的需求。

简历: Ayanna Howard 是俄亥俄州立大学工程学院的院长。她之前是佐治亚理工学院交互计算学院的主席;Zyrobotics 的联合创始人,该公司为有特殊需求的儿童制作治疗和教育产品;曾在美国宇航局工作;拥有南加州大学的博士学位。

丹妮拉·布拉加

“关于采购语言的自白”

在我们公司,我们以付出额外的努力来确保获得最佳数据而自豪,这有时会导致令人捧腹的情况。对于文本和语音数据,最困难的问题通常是找到流利的说话者。在机器学习中,找到具有合适资格和说合适语言的人是最困难和最容易被忽视的问题之一。

最近,我们为一个有特定语言要求的客户做了一个大项目集合。在几次为一种稀有语言寻找合适人选的尝试失败后,我们的一个人去了一个教堂,他知道在那里他会找到符合要求的人。当他为我们的委托人找到他需要的人时,他意外地在忏悔时间出现了。牧师认为他是为此而来,所以,和往常一样,他做了完整的忏悔,包括关于语言的来源。

*简历:*丹妮拉·布拉加(Daniela Braga)是 DefinedCrowd 的创始人兼首席执行官,该公司为机器学习创建训练数据,包括 60 多种语言的文本和语音数据。

埃琳娜·格雷瓦尔

“合成控制:在没有评估数据的情况下评估模型”

如果您正在部署一个不能运行 A/B 测试的应用程序,您如何衡量您的模型的成功呢?综合控制方法是一种可以在这种情况下使用的技术:找到与部署模型的位置在功能上最接近的现有数据,并将该数据用作控制组。

我第一次了解合成控制是在学习教育政策分析的时候。当一所学校尝试一些新的方法来改善学生的学习环境时,不能指望他们只改善一半学生的生活,这样另一半就可以成为统计控制组。取而代之的是,教育研究人员可能会创建一个“综合控制组”,由在学生人口统计和表现方面最为相似的学校组成。我采用了这种策略,当我在 Airbnb 领导数据科学时,我们在那里应用了这种策略。例如,当 Airbnb 在一个新的城市/市场推出产品或政策变化,而无法进行实验时,我们会创建一个最相似城市/市场的合成控制组。然后,我们可以将模型的影响与参与度、收入、用户评级和搜索相关性等指标的综合控制进行比较。综合控制允许我们采取数据驱动的方法来衡量我们的模型的影响,即使我们没有评估数据。

简介:埃琳娜·格雷瓦尔是 D ata 2 the People 咨询公司的创始人兼首席执行官,该公司利用数据科学来支持那些旨在对世界产生积极影响的政治候选人。Elena 之前领导 Airbnb 的 200 多人数据科学团队,拥有斯坦福大学的教育学博士学位。

伊内斯·蒙塔尼

“好的界面给你质量,而不仅仅是数量”

当我和人们谈论可用于注释的接口时,他们的反应通常是“何必呢?收集注释并不十分昂贵,所以即使你的工具快了一倍,它仍然没有那么大的价值。”这个观点是有问题的。首先,许多项目需要主题专家的支持,如律师、医生或工程师,他们将做大量的注释工作。更重要的是,即使你给员工的报酬不多,你仍然关心他们的工作,如果你让他们失败,他们就不会给你好的工作。糟糕的注释过程经常迫使工作人员在例子、注释模式和界面之间切换焦点。这需要积极的集中注意力,而且很快就会筋疲力尽。

在我开始从事人工智能工作之前,我从事网络编程,所以注释和可视化工具是我开始考虑的第一批人工智能软件。我对游戏中的“隐形”界面特别感兴趣,它让你思考做什么,而不是怎么做。但游戏化并不是让任务变得像“游戏”一样“有趣”:它是让界面尽可能无缝和沉浸,以给他们最好的机会来做好任务。这将创造更好的数据,并对创造数据的人更加尊重。

*简历:*伊内斯·蒙塔尼是《爆炸》的联合创始人。她是 spaCy 的核心开发者,也是 Prodigy 的首席开发者。

詹妮弗·普伦斯基

“并非所有数据都是平等的”

如果你关心你的营养,你不会去超市,从货架上随机选择商品。你可能最终通过吃超市货架上的随机物品来获得你需要的营养,但是,在这个过程中你会吃很多垃圾食品。我认为很奇怪的是,在机器学习中,人们仍然认为“随机抽样超市”比弄清楚他们需要什么并在那里集中精力更好。

我建立的第一个主动学习系统是必然的。我正在构建机器学习系统,以帮助一家大型零售店确保当有人在网站上搜索时,出现正确的产品组合。几乎就在一夜之间,一家公司的重组意味着我的人工标签预算减少了一半,而我们需要贴标签的库存增加了 10 倍。因此,我的标签团队每件商品的预算只有我们之前的 5%。

我创建了我的第一个主动学习框架来发现哪一个是最重要的 5%。结果比预算更大的随机抽样要好。从那以后,我在我的大部分项目中使用了主动学习,因为不是所有的数据都是相等的!

*生物:*詹妮弗·普伦斯基是 Alectio 的首席执行官,为机器学习寻找正确的数据。她曾在 Atlassian、Figure Eight 和沃尔玛等公司领导数据科学团队。

李嘉

“学术和现实世界数据标注的区别”

与学术研究相比,在现实世界中部署机器学习要困难得多,主要区别在于数据。现实世界的数据是杂乱的,由于制度障碍,通常很难获取。在干净、不变的数据集上进行研究是很好的,但当你将这些模型带入现实世界时,很难预测它们的表现如何。

当我在帮助构建 ImageNet 时,我们不必担心我们在现实世界中可能遇到的每一个可能的图像类。我们可以将数据限制在 WordNet 层次结构中概念子集的图像上。在现实世界中,我们没有这种奢侈。比如,我们无法收集大量与罕见疾病相关的医学影像。标记这样的图像还需要领域专业知识,这带来了更多的挑战。现实世界的系统需要人工智能技术专家和领域专家密切合作,以激发研究,提供数据和分析,并开发算法来解决问题。

*简历:*李嘉是使用机器学习的医疗保健公司 Dawnlight 的首席执行官和联合创始人。她曾领导过谷歌、Snap 和雅虎的研究部门。,拥有斯坦福大学博士学位。

基兰·斯奈德

“您早期的数据决策仍然很重要”

您在机器学习项目早期做出的决策会影响您在未来许多年中正在构建的产品。对于数据决策来说尤其如此:你的特征编码策略、标注本体和源数据将会产生长期的影响。

在我研究生毕业后的第一份工作中,我负责构建允许微软软件在全球数十种不同语言中运行的基础设施。这包括做出基本的决定,比如决定一种语言中字符的字母顺序,这在当时对许多语言来说是不存在的。当 2004 年的海啸摧毁了印度洋周围的国家时,这对斯里兰卡说僧伽罗语的人来说是一个迫在眉睫的问题:没有简单的方法来支持对失踪人员的搜索,因为僧伽罗语还没有标准化的编码。我们为僧伽罗人提供支持的时间从几个月缩短到几天,这样我们就可以帮助失踪人员服务,与母语人士一起尽快找到解决方案。我们当时决定的编码被 Unicode 采纳为僧伽罗语的官方编码,并将永远编码该语言。你不会总是在如此关键的时间线上工作,但是你应该从一开始就考虑你的产品决策的长期影响。

基兰是广泛使用的增强写作平台 Textio 的首席执行官和联合创始人。Kieran 之前在微软和亚马逊担任产品领导职务,拥有宾夕法尼亚大学的语言学博士学位。

丽莎·布雷登-哈德

“注释偏差不是闹着玩的”

数据科学家通常低估了收集高质量高度主观数据所需的努力。当您试图在没有可靠的基础事实数据的情况下注释数据时,人类对相关性任务的认同并不容易,并且只有在强有力地传达了目标、指导方针和质量控制措施的情况下,让人类注释者参与才是成功的。这在跨语言和文化工作时尤其重要。

我曾经收到一个来自美国个人助理公司的韩国敲门笑话的请求,该公司正在向韩国扩张。向产品经理解释为什么这不可行,并为他们的应用程序找到文化上合适的内容,这不是一个快速的对话:它解开了许多假设的知识。即使在说韩语的人中,创建和评估笑话的注释者也需要来自与目标客户相同的人口统计数据。这是一个例子,说明了为什么减少偏见的策略会触及数据管道的每个部分,从指导方针到针对最合适的注释劳动力的补偿策略:注释偏见不是玩笑!

*简历:*丽莎·布雷登-哈德是圣克拉拉大学全球社会福利研究所的导师。她是巴特勒希尔集团的创始人兼首席执行官,该集团是最大、最成功的注释公司之一;在此之前,我是 IBM 的一名程序员,并在普渡大学和 NYU 大学获得了计算机科学学位。

马修·洪尼巴尔

“考虑注释项目的总成本”

它有助于与注释您数据的人直接交流,就像您组织中的任何其他人一样。不可避免的是,您的一些指令在实践中是行不通的,您需要与您的注释者密切合作来完善它们。在投入生产后很长一段时间内,您也可能会不断完善指令并添加注释。如果你不花时间考虑完善说明和丢弃错误标签的项目,那么很容易以外包解决方案告终,这种解决方案在纸面上看起来很便宜,但实际上很昂贵。

2009 年,我参与了悉尼大学和澳大利亚一家主要新闻出版商的联合项目,该项目要求命名实体识别、命名实体链接和事件链接。虽然当时学术界越来越多地使用众包工作者,但我们建立了一个小型的注释者团队,并直接签约。从长远来看,这最终要便宜得多,特别是对于更复杂的“实体链接”和“事件链接”任务,在这些任务中,众包工作人员很难完成,而我们的注释者通过与我们直接工作和交流得到了帮助。

简历:马修·洪尼巴尔(Matthew Honnibal)是 spaCy NLP 图书馆的创建者,也是 Explosion 的联合创始人。自 2005 年以来,他一直致力于 NLP 研究。

彼得·斯科莫洛赫

“阳光是最好的消毒剂”

你需要深入查看真实数据,才能准确知道要建立什么模型。除了高级图表和聚合统计数据,我建议数据科学家定期检查大量随机选择的粒度数据,让这些示例“冲刷你”。就像高管每周查看公司层面的图表,网络工程师查看系统日志中的统计数据一样,数据科学家应该对他们的数据及其变化有一种直觉。

当我构建 LinkedIn 的技能推荐功能时,我构建了一个简单的 web 界面,其中有一个“随机”按钮,可以显示个人推荐示例以及相应的模型输入,这样我就可以快速查看数据,并直观地了解可能最成功的算法和注释策略。这是确保您发现潜在问题并获得高质量输入数据的最佳方式,这是至关重要的:您正在照亮您的数据,阳光是最好的消毒剂。

*简历:*彼得·斯科莫罗奇是 SkipFlag(被 WorkDay 收购)的前首席执行官,曾在领英的团队中担任首席数据科学家,发明了这个“数据科学家”的头衔。

拉达·拉马斯瓦米·巴苏

“人类洞察力和可扩展的机器学习等于生产人工智能”

人工智能的结果在很大程度上取决于投入其中的训练数据的质量。一个小的 UI 改进就像一个魔术棒,当结合定义良好的质量控制过程应用于数百万个数据点时,可以产生巨大的效率。先进的劳动力是关键因素:培训和专业化提高了质量,来自专家劳动力的洞察力可以与领域专家一起为模型设计提供信息。最好的模型是由机器和人类智能之间的建设性和持续的伙伴关系创建的。

我们最近接了一个项目,需要在机器人冠状动脉旁路移植术或 CABG 视频中对各种解剖结构进行像素级注释。我们的注释团队不是解剖学或生理学方面的专家,因此我们实施了临床知识教学课程,以增强 3D 空间推理和精确注释方面的现有核心技能,该课程由一名训练有素的外科医生担任解决方案架构师。我们客户的成果是我们客户的成功培训和评估数据。对我们来说,结果是看到来自资源不足背景的人热烈讨论人工智能的一些最先进的用途,因为他们很快成为医学图像分析中最重要步骤之一的专家。

简历: Radha Basu 是 iMerit 的创始人兼首席执行官。iMerit 利用技术和人工智能劳动力为全球客户创造先进的技术工人,其中 50%是来自服务水平低下社区的妇女和青年。Radha 之前曾在惠普工作,担任 SupportSoft public 的首席执行官,并在圣克拉拉大学创建了“节俭创新实验室”。

机器学习的领导技能

创建好的数据比创建好的算法需要更广泛的技能。创建良好的培训数据所需的许多技能也是良好的领导技能,我书中提到的专家就是例证:

Radha 是硅谷任何行业中最成功的领导者之一,已经将一家公司上市,现在是一家盈利的人工智能公司的创始人兼首席执行官,该公司雇用了数千人。我特别喜欢她的轶事表明外包注释者可以成为领域专家,作为他们工作的结果,他们的职业潜力在增长。

Peter 鼓励数据科学家始终关注数据,表明即使对于公司的领导者来说,理解您正在处理的数据也是非常重要的。

Matthew 的轶事强调了注释本身并不是创建好数据的唯一成本,这一点经常被只使用匿名众包工人的人忽略,这在学术界很常见,但在行业中很少见。

Lisa 强调了查看数据的重要性,但是如果没有正确的文化背景来理解数据,就不可能完全理解数据。这突出了好的领导意味着让比你更有知识的人来完成他们的任务。

基兰的轶事是理解创建数据的人的文化背景的另一个很好的例子,在这种情况下,需要特定语言的知识来支持时间紧迫的灾难响应工作。

贾关于学术和现实世界数据之间差异的轶事强调了大多数人在学术机器学习程序中学习的狭窄技能集往往不适用于现实世界的情况。

Jennifer 还强调了许多现实情况的实际现实:您的时间和预算有限,所以当您仍然需要发布人们会使用的产品时,如何选择正确的数据?

Ines 的职业生涯始于对 web 界面良好用户体验的思考,她强调良好的界面设计对于良好的数据注释工具是多么重要,不管是谁在注释数据。

Elena 强调了现实世界模型的另一个实际现实:当您甚至不能运行 A/B 测试,更不用说使用保留的评估数据时,如何评估模型更改的成功?

丹妮拉的故事谈到了与一个按照他们自己的方式提供语言数据的社区的会面,并提供了一些轻松的东西来提醒我们不要太把自己当回事。

Ayanna 举了一个我最喜欢的例子,说明决定谁可以标记数据是多么重要:有特殊需求的孩子的父母/监护人可能是唯一准确和道德的注释者来理解和编码孩子的情绪。

即使在专注于算法的学术界,研究人员也明白数据的重要性。斯坦福人工智能实验室主任克里斯托弗·d·曼宁在书的前言中分享了这一点:

“获得正确的数据和正确的注释比采用更先进的机器学习算法更有价值,这是工业中机器学习从业者的公开秘密。”

荣誉奖

我认识的其他许多人也有资格成为专家——在职业生涯中从事机器学习数据方面工作的公司创始人——但这本书的时间安排和有限的章节意味着只能包括这么多专家。如果有更多的时间,其他的榜样可能包括阿莉奥娜·梅德丽安、阿曼·奈马特、方成、希拉里·梅森、李建阳、约翰·阿克瑞德、马克·西尔斯和莫妮卡·罗加蒂。我还想到了十几个人,包括那些不符合我在书中使用的标准,但仍然是榜样的人。也感谢 Emmanuel Ameisen 给我灵感,邀请专家为我的书做专题介绍。我是在他为他的书构建机器学习驱动的应用这样做之后得到这个想法的。

机器学习中的角色模型

对于刚接触机器学习的人来说,可能很难确定有哪些职业道路。就像大部分课程侧重于算法一样,大部分机器学习领军人物的榜单侧重于算法研究者。本文中专家背景的多样性表明,有许多可能的职业道路通往机器学习领域的领导者,他们具有教育、语言学、UI 开发、物理和计算机科学以外的许多其他领域的背景。所以,如果你从事机器学习的数据端工作,没有计算机科学背景,你不应该觉得自己是个局外人。研究机器学习中与数据相关的问题是成功职业生涯的必要条件,也是通往领导力的常见道路。

我在这里分享所有的故事,这样你就不必买本书来学习这些专家轶事。如果你真的买了这本书,我会把所有作者的收入捐献给更好的数据集,特别是低资源语言和健康与灾难响应的倡议,这样你就能为慈善事业做出贡献。虽然这不是选择标准的一部分,但所有的专家都致力于对世界产生明显积极影响的应用,所以我很高兴在我的书中给予这 11 个优秀领导的榜样更多的认可!

罗伯特君主
@WWRob

2021 年 9 月 21 日

基于暹罗网络和均值嵌入的动物识别

原文:https://towardsdatascience.com/animal-recognition-with-siamese-networks-and-mean-embeddings-13d3f27eff93?source=collection_archive---------24-----------------------

基于机器学习的物体识别

暹罗网络能打败标准分类方法吗?

嵌入投影。作者图片

当听到暹罗网络时,你可能会想到“人脸识别”。这是这类网络最常见的用途。我们试图做些别的事情:只根据俯视摄像机镜头来识别动物。

图 1:来自数据集的帧,来源: PSRG 数据集

如果你对“介绍”不感兴趣,那么跳到暹罗网络培训。

为什么是暹罗网?

你可能知道,连体网络广泛用于识别任务。与标准分类相比,它的主要优势在于我们可以使用一张我们希望能够识别的物体的图片,这很有效。当然,这并不简单,但我们的想法是不要在每次我们想要添加另一个类(人或动物或其他东西)时重新训练整个网络。暹罗网络的基本结构是这样的:

*图 2:简单的连体网络结构,*作者图片

网络的第一部分很简单,它只是一个基本的特征提取器(ResNet 或任何其他类似 CNN 的网络)。更重要的是最后的损失函数。目前,大多数 SOTAs 都在使用三重损失,但基本思想是最大化不同嵌入(来自 CNN 的编码图像)之间的距离。这只是对暹罗网络的一个简单总结。我真的鼓励你去读读原文和关于三重态损失的那位,以便更好地理解。

识别是如何工作的?

在现实生活中,我们通常使用来自相同设备、在相同条件下拍摄的图像,等等。通常情况下,相机是在安检门前或某个我们可以确定每张照片或多或少相似的地方。但在试图识别农场里的动物时,情况并非如此。我们的研究涉及多种不同的摄像机和照明条件(其中一些是夜间视频)。因此,我们不得不使用两个网络,而不是一个。第一是发现目标,第二是识别目标。

检测和裁剪

检测非常简单,不需要很多工作(除了用于训练的注释)。我们采用了带有 ResNet50 主干的标准 SSD FPN 网络,并在我们的数据集上对其进行了重新训练。

表 1:地图得分

我们不打算关注检测模型,因为它只是我们必须做的额外工作。你可以在我们的 GH Wiki 上阅读更多关于训练的内容。

暹罗网络培训

神奇的事情发生了:)正如我之前提到的,我们使用三重态损失作为我们的损失函数。在我们的实验中,我们使用了三种不同的基线网络,然后在其上附加自定义层。这些网络是:

  • ResNet101 连体网络结构
  • EfficientNetB5 连体网络结构
  • MobileNetV2 连体网络结构

我们没有像人们通常所做的那样,只是在最后一层上附加我们的自定义层。我们的想法是将它们添加到中间的某个地方,以使用更细粒度的功能,而不是高级功能。

  • ResNet101 —连接到层 4 的输出
  • EfficientNetB5 —连接到第三层的输出
  • MobileNetV2 —连接到层 10 的输出

现在,当我们完成网络结构时,我们必须讨论培训过程以及如何准备数据。我们使用 TensorFlow 和他们的三重态损失法,这种方法更容易使用,但也要求我们以特定的方式准备批次。这个特定的损失函数从给定的批次中选择正样本和负样本。因为有 16 种不同的动物,我们必须创建一个批量**,每个类别至少有 2 个样本**。这使得的最小批量为 32 (我们已经使用了 64)。第二件事是,对于每个时期,我们必须手动调整批次,以确保每个批次中有最小数量的类示例。每次手动混洗,按类混洗图像,然后将混洗的数据集(每个类的数据集)合并为一个,并按给定的批量大小分割它们。

图 3。数据集批处理过程,按作者排序的图像

图 3 展示了我刚刚描述的内容的更简单版本。请注意,每个批处理至少有两个输入属于同一类。通常,我们不会使用批量大小 10,但它可以作为一个例子。

我们的培训脚本可在 GitHub 上获得。

平均嵌入法

通常,我们将一幅图像与某个锚点(真实的主体图像)进行比较。因为同样的条件。多视角和闪电条件是行不通的。我们可能会尝试将该图像与同一主题的多个图像进行比较,这可能会导致您的脚本变慢,因为您必须检查每个条件。我们的想法是使用已经生成的嵌入,并为每个类创建平均嵌入。

*图 4:样本嵌入空间。每个点代表图像的嵌入向量,每种颜色被分配到不同的类,*图像由作者

我们可以在 TensorFlow 嵌入投影仪的帮助下,使用 UMAP 在 3D 空间中可视化 64D 嵌入。你已经注意到不同的类(颜色)是如何聚集在一起的。图 4 只是一个例子,但是我们必须为我们训练过的每一个连体网络设想这样的空间。

*图 6:列车紧急制动系统。基于 MobileNetV2 暹罗网络的空间,*图片由作者提供

基于 MobileNetV2 的网络产生了最好的结果,因此我们将从它开始。如果我们比较图 6 和图 5,聚类甚至更容易与一个“离群值”聚类(右下角)分开。该额外的聚类具有所有离群值,这些离群值由于最小化损失函数而与其他聚类分离(不幸的是,有时模型更容易牺牲一些示例来最小化损失)。这将很快导致一个问题,但现在,我们可以使用当前的嵌入。

*图 7:列车平均 Emb。基于 MobileNetV2 暹罗网络的空间,*图片由作者提供

图 7 示出了嵌入空间的不同版本。在这种情况下,我们已经计算了给定类中所有示例的平均位置。不再有“离群值”聚类,每个类在空间中只有一个点(准确地说是向量)。利用整个类别平均值,我们可以计算从测试图像到该类别的距离,并将最接近的一个作为预测类别。

*图 8:猪分类的混淆矩阵(基于 MobileNetV2 的暹罗网络),*作者图片

这种方法的 F1-得分0.91 ,这使我们远远高于我们以前使用的分类器(F1 ~ 0.6)。

平均值的问题

只看分数本身是不够的,因为有一个小细节我们可能会错过。您可能还记得图 6 中的“异常值”集群。该异常值也存在于测试数据集中,并且因为我们基于到最近均值嵌入的距离对示例进行分类,所以导致将所有异常值分类为“Robert”(Robert 的均值是最接近异常值的一个,查看图 8 和 Robert 的列)。我们可以通过添加另一个称为“离群值”的平均值来解决这个问题,但是我们必须定义如何将示例分配给“离群值”类(我们可以使用集群化度量)。

另一个网络基地

正如我说过的,MobileNetV2 基础版是最适合我们的问题的。我们尝试了不同的方法,其中 ResNet101 是一个非常有趣的方法( F1 = 0.54 关于使用均值嵌入方法的分类)。

*图 9:列车紧急制动。空间为 ResNet101 基地连体网络,*图片由作者提供

在这种情况下,我们的嵌入看起来更像图 4。结果比 MobileNetV2 差,但与基本分类器处于相似的水平。

*图 10:猪分类的混淆矩阵(ResNet101 base Siamese Network),*作者图片

一个更有趣的例子是用 EfficientNetB5 base 训练的网络。这个网络是最难训练的(与 0.06 MobileNetV2 和 0.01 ResNet101 相比,训练损失为 0.25)。将训练样本投射到嵌入空间给出了一个令人惊讶的结果。

*图 11:列车紧急制动。高效网络空间 B5 基础连体网络,*图片由作者提供

如你所见,我们每个班有不止一个集群。数据本身是聚类的,但是当计算每个类的平均值时,最终向量不在聚类的任何位置附近。除此之外,大部分都非常靠近空间的中心。这导致分类分数非常低( F1 = 0.32 )。

*图 12:猪分类的混淆矩阵(基于 EfficientNetB5 的暹罗网络),*作者图片

大多数示例被分配到一个类,而这个类恰好是离空间中心最远的:)即使带有 EfficientNet base 的版本没有按预期工作,它也让我们了解了该方法如何在不同类型的网络中工作。

为什么这种方法更好?

  • 生产中的计算量更少,你只需计算一次类的平均值,然后将你的例子与所有的平均值进行比较。
  • 你可以很容易地添加一个新的类,只需从该类中计算一堆图像,并计算另一个均值嵌入
  • 通过一些额外的工作,您可能会有一个“离群值”类。

更多的动物呢?

这个网络与 64D 空间一起工作,当处理更复杂的数据集(成千上万的主题)时,增加更多的维度可能是有益的。

整个系统是如何运作的?

这是一个困难的部分。我提到过系统的第一部分是一个检测网络。Siamese 网络必须处理来自该检测的裁剪图像,而不是裁剪良好的训练/测试数据集。

*图 13:检测输出,*作者图片

*图 14:裁剪后的检测输出(输出中只有 5 个元素),*作者的图像

图 13 示出了来自检测网络的样本输出。您可以清楚地看到,一些边界框覆盖了不止一种动物。图 14 示出了仅使用来自检测网络的边界框定义的一些裁剪图像。然后将裁剪后的图像发送到暹罗网络,并与均值嵌入进行比较。一些裁剪的图像很容易分类(即左上),但是一些图像(即右上)在同一图像上有多个动物。这可能导致网络输出基于我们想要的不同动物的特征的嵌入,并最终对结果进行错误分类。我们使用一些自定义的启发式方法来处理这样的情况,但是你应该意识到这个问题。

结论

使用均值嵌入对具有暹罗网络的动物进行分类恰好非常有效。我们必须小心一些边缘情况(如上所述),但至少其中一些可以用一些启发法来解决。如果你对这个项目感兴趣(或者只是想看看代码),我们已经在 GitHub 资源库上发布了所有内容。

项目的主要目标是跟踪和识别视频中的动物,这是一个快速的样本结果。

视频 1:跟踪样本

参考资料:

  • 格雷戈里·科赫。*“用于一次性图像识别的连体神经网络”。*2015https://www.cs.utoronto.ca/~gkoch/files/msc-thesis.pdf
  • 利兰·麦金尼斯,约翰·希利和詹姆斯·梅尔维尔。 UMAP:一致流形逼近和降维投影。 2020 年https://arxiv.org/abs/1802.03426
  • Stefan Schneider 等人用于动物个体再识别的相似性学习网络——超越人类观察者的能力。2019 年 2 月。
  • 张量流。嵌入式投影仪—网络访问 2021 年 1 月https://projector.tensorflow.org/
  • 谭明星和郭诉乐。EfficientNet:重新思考卷积神经网络的模型缩放。【https://arxiv.org/abs/1905.11946】2020 年T21
  • Mark Sandler 等人 *MobileNetV2:反向残差和线性瓶颈。*2019【https://arxiv.org/abs/1801.04381 2019
  • 明凯何等*用于图像识别的深度残差学习。*2015https://arxiv.org/abs/1512.03385
  • 感知系统研究小组——内布拉斯加大学http://psrg.unl.edu/Projects/Details/12-Animal-Tracking
  • 南 Schneider,G. Taylor,S. Linquist 和 S. Kremer 用于动物个体再识别的相似性学习网络——超越人类观察者的能力2020https://arxiv.org/abs/1902.09324
  • Elad Hoffer,Nir Ailon 使用三重网络的深度度量学习2014https://arxiv.org/abs/1412.6622

原载于https://erdem . pl

用 Gephi 制作动态图形动画

原文:https://towardsdatascience.com/animate-dynamic-graphs-with-gephi-d6bd9faf5aec?source=collection_archive---------15-----------------------

社会网络图中时间动态行为动画的数据可视化教程。

一段时间内 Twitter 影响者转发行为的 Gephi 可视化。图片作者。

谈到分析社交网络,我以前的文章主要是关于自然语言处理(NLP),或者更具体地说是阿拉伯语 NLP。然而,推文不仅仅是文本数据,它们代表了 Twitter 用户之间的网络联系。增加网络分析,允许社交媒体数据的内容和行为之间的综合;因此,将网络和文本数据结合起来,可以对社交媒体网络有更细致入微的理解。

我的 Python 学习之旅是出于需要而开始的,我的目标是制作一个 Twitter 网络图,而编码似乎是解决方案。因此,我的第一个脚本是与熊猫的一场令人流泪的战斗,这一切都是为了创建一个简单的 csv,它将被流行的开源图形可视化软件 Gephi 接受。几个月后,阅读我最早的代码提醒了我的毅力,改进第一个脚本是一堂谦卑的课。

最近,我在与世界银行的研究人员合作进行黎巴嫩调查时,使用了这个改进的脚本。在地区知识和专业技能的指导下,我正在分析一个特定的 Twitter 标签“#لبنان_ينتفض".”数据是在很长一段时间内收集的;追踪始于 2019 年十月革命的第一次抗议,收集持续到 2020 年 8 月贝鲁特爆炸,结束于 2020 年 11 月。

作为调查的一部分,我观察了 Twitter 影响者 retweet 网络,有人建议我专注于选定的感兴趣时段,所以我最初的方法是创建静态快照。然而,数据的时间性质促使我去想象转发行为的时间动态。多亏了我的第一个脚本,我能够只用最基本的 Python 和 Gephi 创建几个类似上面例子的动画图形。接下来,我通过使用 CloudApp 创建截屏 gif,使我的时间动态 Twitter 网络动画可以访问。

在这个简短的数据可视化教程中,我将概述创建网络数据动画图所需的步骤和最少的代码。我将使用 Twitter 作为例子,但是同样的过程也可以用于其他社交媒体网络。由于本教程侧重于数据可视化,我将跳过对数据收集过程的解释。相反,我与我的初学者 Twitter 抓取脚本分享了一个要点,它足以为本教程的目的收集数据。

Gephi 有几个选项可以从数据库加载网络数据,或者作为图形文件类型,比如。然而,对于动态图,最简单的选择是将正确标记和格式化的电子表格中的数据加载到 Gephi 中。在网络图术语中,“节点”代表单个 Twitter 用户,“边”代表用户之间的转发连接。我从节点和边 csv 文件开始,用 Python 中的 networkx 从原始的未处理 Twitter 数据创建。这篇优秀的媒体文章解释了如何开始可视化 Twitter 网络,包括如何用 networkx 创建节点和边。

下面的代码片段展示了如何从 nodes 和 edges csv 文件中加载 Twitter 数据,以便可以用 Python 正确地标记和格式化它们。

加载节点和边 csv 文件的 Python 代码片段。

一旦加载了原始 Twitter 数据,我首先处理节点,然后是边,并将它们都保存为 csv 文件。

Gephi 需要一个节点电子表格,其中第一列专门命名为“Id ”,包含 Twitter 用户 Id,第二列应该是“Label ”,包含 Twitter 用户屏幕名称。所有其他列表示节点属性,并且是可选的。在下面的代码示例中,我包含了一个关于 Louvain 集群(由在 networkx 中实现的 Louvain 社区检测算法确定)和 Twitter 用户追随者计数的列。处理节点的最后一步是将节点数据帧保存为 csv 格式,以便以后可以将其导入 Gephi。

为 Gephi 处理节点的 Python 代码片段。

接下来,对于 edges 电子表格,与节点类似,Gephi 希望在导入过程中特别标记和排序列。前两个必需的列是“Source”和“Target”,代表参与转发的 Twitter 用户对。第三列应该是“Type”,在这个 Twitter 示例中是“directed ”,因为我们处理的是转发。第四列应该是“Label”,在本例中是一个简单的索引。第五列是最重要的,它应该被命名为“Timeset ”,包含转发的创建时间——特别是以 iso 格式。“时间集”列是时间变量,将用于在 Gephi 中制作网络图动画。最后一列“Weight”是可选的,Gephi 默认为“1”。最后,可以将 edges 数据帧保存为 csv 格式,以便导入 Gephi。

为 Gephi 处理边的 Python 代码片段。

既然 nodes 和 edges 电子表格已经被格式化和标记,它们就可以通过“Import spreadsheet”菜单选项导入到 Gephi 中了。从节点开始,下面的截图显示了第一个导入菜单,其中分隔符应该是“逗号”,导入选项应该是“节点表”,编码应该是“UTF-8”。

节点的 Gephi 电子表格导入屏幕。图片作者。

下一步是选择要导入的列,并为可选属性列分配数据类型。确保在时间表示的下拉菜单中选择“时间戳”,这对我们稍后导入带时间戳的边很重要。

节点导入过程通过将节点添加到新的 Gephi 工作空间来完成。

接下来,添加边电子表格中的边。下面截图中的边导入过程类似于节点导入过程;唯一的区别是选择“边表”的导入选项。

用于 edges 的 Gephi 电子表格导入屏幕。图片作者。

下面的屏幕截图显示了第二个导入屏幕,其中我们将时间表示指定为“Timestamps”,并将“Timeset”列的数据类型指定为“TimestampSet”。

边导入过程的最后一步是将边附加到导入节点时创建的现有工作空间中。在 Gephi 的数据实验室中,edges 表应该是可见的,如下图所示,“时间戳”将以 iso 格式显示。

gephi edges 表截图。图片作者。

按照我通常的程序,我在导入的网络图上工作,通过应用强制定向算法来布局节点,并挑选哪些属性用于节点的着色和大小调整。在下图所示的“概览”窗口中,设计的网络图下方有一个宽栏,显示“启用时间线”选项。

Gephi 推特转发网络图截图。图片作者。

只需选择“启用时间线”,它会显示一个打勾的数字时间条,如下所示。左下角的设置轮允许设置时间格式,我从中选择“日期时间”。

Gephi 调整时间格式设置截图。图片作者。

剩下的就是用光标选择时间间隔窗口的大小,然后按 play。

使用光标调整时间窗口的大小,并拖动到所需的开始位置。按播放开始播放动画。

正如我之前提到的,我使用 CloudApp 创建了动画网络的截屏 gif,然后与我的合著者分享。就是这样!使用一点 Python 代码,很容易修改网络数据,以便可以在 Gephi 中动画化。

我希望这个教程是有帮助的,如果是的话,考虑在下面留下评论,这样我就知道有人对这个话题和/或这种风格的帖子感兴趣。欢迎所有问题和评论,欢迎随时在 Linkedin 上与我联系。

Python 中时间序列数据的动画条形图

原文:https://towardsdatascience.com/animated-bar-plot-in-python-for-time-series-data-8809dbdf9bc?source=collection_archive---------52-----------------------

用 python 实现一个动画赛车条形图

图片由 janjf93 来自 Pixabay

由于大量开源库的存在,ython 在很短的时间内就获得了流行,但在创建动态情节方面仍有不足。探索性数据分析是端到端机器学习项目的重要组成部分之一。

数据集的 EDA 需要开发用于数据可视化的图。有各种各样的库,如 seaborn、matplotlib、ggplot 等,可以为数据可视化创建很好的图形。当涉及到动态情节或动画情节时,这些库无法开发动画情节。bar-chart-race 是一个开源 python 库,可以为时间序列数据集创建动态或动画图。

安装:

bar_chart_race 库的 Pip 和 Conda 安装:

**pip install bar_chart_race
conda install -c conda-forge bar_chart_race**

用法:

bar_chart_race 库是一个开源库,为时序数据集创建动画条形图。在实施动画条形图之前,需要遵循一系列快速入门指南:

  • 数据集的每一行都应该包含一个时间段。
  • 日期时间列应重置为数据框的索引(可选)。
  • 数据集的列应该是条形图的类别。
  • 每一列都应该包含特定类别的特定值。

下面提到的样本数据集是实施柱状图所需数据框格式的正确示例。

(图片由作者提供),样本数据集

以上样本数据集是印度各邦的**新冠肺炎病例数。**数据集的列是指印度各邦,值是特定日期的新冠肺炎病例数。数据集的索引被重置为日期列。

bar _ chart _ race 函数的语法:

**bcr.bar_chart_race(
df, filename=None, orientation='h', sort='desc', n_bars=None, fixed_order=False, fixed_max=False, steps_per_period=10, period_length=500, interpolate_period=False, label_bars=True, bar_size=0.95, period_label=True, period_fmt=None, period_summary_func=None, perpendicular_bar_func=None, figsize=(6, 3.5), cmap=None, title=None, title_size=None, bar_label_size=7, tick_label_size=7, shared_fontdict=None, scale='linear', writer=None, fig=None, dpi=144, bar_kwargs=None, filter_column_colors=False
)**

covid 数据和结果的函数实现:

下面是为 covid 时间序列数据实现动画条形图的 python 代码。其余所有参数都保持默认,可以更改这些参数来改变绘图的显示方式。

(作者 GIF),动画条形图

要了解如何使用 PivotTable.js 创建交互式数据透视表,请阅读下面的文章:

结论:

动画条形图是比静态条形图更好的可视化技术。在本文中,我们讨论了如何为时间序列数据集绘制动画或动态条形图。

Matplotlib 动画也可以用来开发动画条形图,但它需要更多的代码来实现。bar_chart_race 库构建在 matplotlib 库之上。

参考资料:

[1]条形图-比赛文档:https://pypi.org/project/bar-chart-race/

动画数学分析

原文:https://towardsdatascience.com/animated-mathematical-analysis-907179ce9c9e?source=collection_archive---------13-----------------------

使用 Manim 创建数学动画可视化

马尼姆(来源:作者)

数据可视化有助于理解数据中无法用肉眼看到的隐藏模式。它确定不同数据点之间的关联、不同特征之间的相关性以及目标变量。有多个 Python 库可用于创建可视化效果。

动画可视化使它对实际查看它的用户更有吸引力和更容易理解。动画有助于显示数据是如何分布、关联、相关等的。像 Matplotlib、Plotly 等几个 Python 库提供了可视化的动画。

在本文中,我们将讨论 Manim,这是一个开源的 python 库,致力于创建解释性的数学视频/动画。它通过动画的方式帮助理解不同的数学概念。

让我们开始吧…

安装所需的库

我们将从使用 pip 安装一个 Manim 开始。manim 需要几个依赖项。下面给出的命令将安装所有依赖项和 Manim。

!sudo apt update
!sudo apt install libcairo2-dev ffmpeg \
    texlive texlive-latex-extra texlive-fonts-extra \
    texlive-latex-recommended texlive-science \
    tipa libpango1.0-dev
!pip install manim
!pip install IPython --upgrade

导入所需的库

在这一步,我们将导入所需的库和函数来使用 Manim 创建数学可视化。

import numpy as np
from manim import *

创建可视化

在这一步,我们将开始创建不同的可视化。让我们从一些基本的可视化开始。

  1. 圆到方
%%manim -qm -v WARNING SquareToCircleclass SquareToCircle(Scene):
    def construct(self):
        # Creating shapes
        circle = Circle()
        square = Square()#Showing shapes
        self.play(Create(square))
        self.play(Transform(square, circle))
        self.play(FadeOut(square))

动画(来源:作者)

2。创建图表

%%manim -qm -v WARNING CreateGraphclass CreateGraph(Scene):
    def construct(self):
        axes = Axes(
            x_range=[-3, 3],
            y_range=[-5, 5],
            axis_config={"color": BLUE},
        )# Create Graph
        graph = axes.get_graph(lambda x: x**2, color=WHITE)
        graph_label = axes.get_graph_label(graph, label='x^{2}')graph2 = axes.get_graph(lambda x: x**3, color=WHITE)
        graph_label2 = axes.get_graph_label(graph2, label='x^{3}')# Display graph
        self.play(Create(axes), Create(graph), Write(graph_label))
        self.wait(1)
        self.play(Transform(graph, graph2), Transform(graph_label, graph_label2))
        self.wait(1)

图表(来源:作者)

3。布尔运算解释

%%manim -qm -v WARNING BooleanOperationsclass BooleanOperations(Scene):
    def construct(self):
        ellipse1 = Ellipse(
            width=4.0, height=5.0, fill_opacity=0.5, color=BLUE, stroke_width=10
        ).move_to(LEFT)
        ellipse2 = ellipse1.copy().set_color(color=RED).move_to(RIGHT)
        bool_ops_text = MarkupText("<u>Boolean Operation</u>").next_to(ellipse1, UP * 3)
        ellipse_group = Group(bool_ops_text, ellipse1, ellipse2).move_to(LEFT * 3)
        self.play(FadeIn(ellipse_group))i = Intersection(ellipse1, ellipse2, color=GREEN, fill_opacity=0.5)
        self.play(i.animate.scale(0.25).move_to(RIGHT * 5 + UP * 2.5))
        intersection_text = Text("Intersection", font_size=23).next_to(i, UP)
        self.play(FadeIn(intersection_text))u = Union(ellipse1, ellipse2, color=ORANGE, fill_opacity=0.5)
        union_text = Text("Union", font_size=23)
        self.play(u.animate.scale(0.3).next_to(i, DOWN, buff=union_text.height * 3))
        union_text.next_to(u, UP)
        self.play(FadeIn(union_text))e = Exclusion(ellipse1, ellipse2, color=YELLOW, fill_opacity=0.5)
        exclusion_text = Text("Exclusion", font_size=23)
        self.play(e.animate.scale(0.3).next_to(u, DOWN, buff=exclusion_text.height * 3.5))
        exclusion_text.next_to(e, UP)
        self.play(FadeIn(exclusion_text))d = Difference(ellipse1, ellipse2, color=PINK, fill_opacity=0.5)
        difference_text = Text("Difference", font_size=23)
        self.play(d.animate.scale(0.3).next_to(u, LEFT, buff=difference_text.height * 3.5))
        difference_text.next_to(d, UP)
        self.play(FadeIn(difference_text))

布尔运算(来源:作者)

所有这些可视化都需要大量代码,我已经将每一行代码解释为代码中的注释,您可以浏览它,并了解如何创建图形并将其可视化。

让我们创建一些更高级的可视化。

4。带痕迹的点

%%manim -qm -v WARNING PointWithTraceclass PointWithTrace(Scene):
    def construct(self):
        path = VMobject()
        dot = Dot()
        path.set_points_as_corners([dot.get_center(), dot.get_center()])
        def update_path(path):
            previous_path = path.copy()
            previous_path.add_points_as_corners([dot.get_center()])
            path.become(previous_path)
        path.add_updater(update_path)
        self.add(path, dot)
        self.play(Rotating(dot, radians=PI, about_point=RIGHT, run_time=2))
        self.wait()
        self.play(dot.animate.shift(UP))
        self.play(dot.animate.shift(LEFT))
        self.wait()

带痕迹的点(来源:作者)

5。图形摄像机

%%manim -qm -v WARNING FollowingGraphCameraclass FollowingGraphCamera(MovingCameraScene):
    def construct(self):
        self.camera.frame.save_state()# create the axes and the curve
        ax = Axes(x_range=[-1, 10], y_range=[-1, 10])
        graph = ax.plot(lambda x: np.sin(x), color=BLUE, x_range=[0, 3 * PI])# create dots based on the graph
        moving_dot = Dot(ax.i2gp(graph.t_min, graph), color=ORANGE)
        dot_1 = Dot(ax.i2gp(graph.t_min, graph))
        dot_2 = Dot(ax.i2gp(graph.t_max, graph))self.add(ax, graph, dot_1, dot_2, moving_dot)
        self.play(self.camera.frame.animate.scale(0.5).move_to(moving_dot))def update_curve(mob):
            mob.move_to(moving_dot.get_center())self.camera.frame.add_updater(update_curve)
        self.play(MoveAlongPath(moving_dot, graph, rate_func=linear))
        self.camera.frame.remove_updater(update_curve)self.play(Restore(self.camera.frame))

图表相机(来源:作者)

继续尝试不同的可视化。如果您发现任何困难,请在回复部分告诉我。

本文是与 Piyush Ingale 合作完成的。

在你走之前

感谢 的阅读!如果你想与我取得联系,请随时通过 hmix13@gmail.com 联系我或我的 LinkedIn 个人资料 。可以查看我的Github简介针对不同的数据科学项目和包教程。还有,随意探索 我的简介 ,阅读我写过的与数据科学相关的不同文章。

R 中的动画时间序列图

原文:https://towardsdatascience.com/animated-time-series-plots-in-r-3777fa738456?source=collection_archive---------27-----------------------

制作时间序列动画的快速指南

几周前,我发表了一篇分析 2020 年南非人口流动趋势的文章。那篇文章中包括一组动画时间序列图,描绘了一段时间内的移动趋势,描绘了以新冠肺炎锁定为标志的一年中的移动情况。在本文中,我将为我的分析提供一个代码演练,重点是创建如下所示的动画时间序列图。

居住和工作地点的移动趋势|作者图表

我们将使用的数据可以在谷歌的新冠肺炎社区移动报告页面上找到,并且已经包含在我的 GitHub 存储库中。一旦。csv 文件已经加载到 R 中,我们需要拆分出国家级数据。除了这些图之外,该分析还包括一个比较表,显示了每个锁定期内移动性的平均变化。这是通过首先删除对应于南非公共假日的数据点实现的,这些数据点可能是异常值。接下来,将剩余的数据分成每个警报级别,最后,计算每个警报级别的平均值。

计算每个警报级别中移动性的平均变化|数据集的第 9 至 14 列代表每个移动性类别

绘制图表需要几个软件包:

  • ggplot2:用于制作我们的地块
  • ggeasy:用于更方便地访问一些 ggplot2 命令
  • ggpubr:用于格式化我们的 ggplot2 图,并使它们准备好发布
  • gganimate:用于激活我们的 ggplot2 图
  • gifski:用于将我们的动画 ggplot2 图转换为 GIF 格式
  • transformr:用于平滑地激活图中的时间序列路径
  • 刻度:用于在图的 y 轴上显示百分比标签

图中设置的另一个元素是为每个警报级别设置标记,我们将这些标记绘制为带有文本标签的垂直线:

除了最后一次,使用过 ggplot2 的人应该对这个情节的代码很熟悉。我们选择我们的国家数据集,将我们的审美映射为 x 轴上的日期和 y 轴上的移动性百分比变化,在同一轴上添加另一个时间序列,添加轴标签,为我们的线设置颜色,并包括我们的垂直线以划分警报级别。最后一行(下面代码片段中的第 15 行)是我们制作情节动画所需的全部内容。我们提供给 transition_reveal 函数的参数与我们图表中的 x 轴美学相同,因为我们想要创建一个逐渐揭示的时间序列。

生成居住和工作地点的移动趋势的代码图如上所示

最后,我们使用 animate 函数将绘图渲染为 GIF,并使用 anim_save (默认情况下保存最后渲染的图形)将我们的动画时间序列绘图保存为 GIF。该文件将被保存到与我们正在处理的 R 文件相同的目录中。

将动画绘制为 GIF 格式,并保存。gif 文件

其余的图以同样的方式生成,完整的代码可以在这里的库中找到。

用于对象检测的注释器

原文:https://towardsdatascience.com/annotator-for-object-detection-950fd799b651?source=collection_archive---------18-----------------------

一个为计算机视觉生成带注释图像的工具

在计算机视觉中,目标检测是一个有着众多应用的有趣领域。像任何其他受监督的机器学习任务一样,我们也需要为这个任务添加注释或标签。然而,对我们许多人来说,图像的注释可能是一项乏味耗时的任务(懒人?).当我写我的上一篇文章时,我强烈地感受到了这一点,链接如下。

所以大问题来了;

我们怎样才能自动生成带有期望背景的带注释的图像?

作者截图

嗯,答案很简单,使用现有的对象,并叠加在一些背景上!互联网上有几个博客会告诉你如何去做。但对我来说,很难找到一个合适的工具,可以为我做一切,这样我就可以专注于主要问题,对象检测。

所以我介绍,对象注释器。它是做什么的,又是如何做到的?这篇文章就是关于这种用途的。

准备对象图像

找到物体的图像比找到注释要容易得多。这是因为捕捉物体图像很容易,而且更省力。一个很好的水果图片来源是 Kaggle 中的水果 360 数据集。

作者截图(【https://www.kaggle.com/moltean/fruits】T4)

该数据集包含来自水果的 100×100 个图像,带有关于水果是什么的标签。

决定背景

这一步是非常具体的任务。例如,如果您要在火车站检测行人,您最好使用特定车站的照片。或者你房间(或者超市)的照片,如果你在房间里找水果的话。这很容易做到,而且不需要太多的努力。

照片由上的水印图案和组成

使用对象注释生成器

您可以从下面的 GitHub 存储库中下载注释器。

https://github.com/anuradhawick/Object-Annotation-Maker

存储库的自述文件中提供了运行说明。

按如下方式组织您的图像;

data
-Fruits
--Banana
---img-1.jpg
---img-2.jpg
--Apple
---img-1.jpg
---img-2.jpg
...
-Backgronds
--bg-1.jpg
--bg-2.jpg
--bg-3.jpg

该工具可以为您完成以下任务;

  • 重新缩放图像 —如果你想缩小图像尺寸,这很有帮助。例如,用于训练的最常见图像大小是 224x224。所以你需要有大约 80x80 大小的对象。该选项可通过使用参数--resize-images 80,80进行设置。您可以同时提供高度和宽度,或者只提供缩放尺寸的高度(—-resize-images 80)。
  • 重新调整背景——这与上一步类似。通常,背景图像会非常大。使用参数--resize-backgrounds 224,224可以很容易地重新调整它们。
  • 每个对象的图像数量 —这决定了特定对象生成的图像数量。以--n-images 10为例。
  • 图像中的对象数量 —目前该工具支持在背景上叠加同一图像的多个实例。例如,厨房图像中的两个苹果。可以通过参数--n-objects 2,5来实现。您可以为层代提供单个值或一系列值。

该程序还将线程种子的数量作为多线程和随机生成的输入。输入图像可以作为通配符列表给出。例如,如果您要从 Fruit 260 数据库中挑选香蕉和苹果,您的命令将如下所示。为了清楚起见,我省略了上面的选项。

python3 Maker.py -i "fruit260/Training/Banana/*.jpg" "fruit260/Training/Apple/*.jpg" -b "data/Backgrounds/8.jpg"

这使得选择多个文件夹变得非常容易。

请注意,图像必须来自带有标签的文件夹。例如,在名为香蕉的文件夹中总是有香蕉图片。您可以在不同的目录中有几个这样的文件夹。

您需要指定一个输出文件夹来保存图像。对于上面的数据集,您提到的输出目录中会有以下结构。

output
-tmp (used for temporary data)
-images
--banana
---img-0-banana-1.jpg
---img-0-banana-2.jpg
--apple
---img-0-apple-1.jpg
---img-0-apple-1.jpg
-annotations
--banana
---img-0-banana-1.xml
---img-0-banana-2.xml
--apple
---img-0-apple-1.xml
---img-0-apple-1.xml

该程序将自动生成 VOC 格式的 XML 用于训练。现在,您可以使用这些图像来训练您的机器学习模型。编码快乐!

PyCaret 2.3.5 来了!了解新内容

原文:https://towardsdatascience.com/announcement-pycaret-2-3-5-is-here-learn-whats-new-7f0256c71797?source=collection_archive---------17-----------------------

关于 PyCaret 最新版本中添加的新功能

(图片由作者提供)PyCaret 2.3.5 中的新特性

🚀介绍

PyCaret 是一个用 Python 编写的开源、低代码的机器学习库,可以自动化机器学习工作流。这是一个端到端的机器学习和模型管理工具,可以成倍地加快实验周期,提高您的工作效率。想了解更多关于 PyCaret 的信息,可以查看官方网站或者 GitHub 。

本文展示了最近发布的 PyCaret 2.3.5 中添加的新功能的使用。

🤖新模型:DummyClassifier 和 DummyRegressor

pycaret.classificationpycaret.regression模块的模型动物园中增加了 DummyClassifier 和 DummyRegressor。当您运行compare_models时,它将使用简单的规则训练一个虚拟模型(分类器或回归器),结果将显示在排行榜上以供比较。

**# load dataset** from pycaret.datasets import get_data
data = get_data('juice')**# init setup** from pycaret.classification import *
s = setup(data, target = 'Purchase', session_id = 123)**# model training & selection**
best = compare_models()

(图片由作者提供)compare _ models 函数的输出

**# load dataset** from pycaret.datasets import get_data
data = get_data('boston')**# init setup** from pycaret.regression import *
s = setup(data, target = 'medv', session_id = 123)**# model training & selection**
best = compare_models()

(图片由作者提供)compare _ models 函数的输出

您也可以在create_model功能中使用该模型。

**# train dummy regressor**
dummy = create_model('dummy', strategy = 'quantile', quantile = 0.5)

(图片由作者提供)create _ model 函数的输出

虚拟模型(分类器或回归器)是与其他(真实)回归器进行比较的简单基线。不要用它来解决实际问题。

📈自定义概率截止值

PyCaret 的create_model compare_models ensemble_model blend_models等所有训练函数中都引入了一个新参数probability_threshold。默认情况下,所有能够预测概率的分类器都使用 0.5 作为截止阈值。

这个新参数将允许用户传递一个介于 0 和 1 之间的浮点数来设置一个自定义的概率阈值。当使用probability_threshold时,底层函数返回的对象是模型对象的包装器,这意味着当您在predict_model函数中传递它时,它将尊重阈值,并将使用它们在传递的数据上生成硬标签。

**# load dataset** from pycaret.datasets import get_data
data = get_data('juice')**# init setup** from pycaret.classification import *
s = setup(data, target = 'Purchase', session_id = 123)**# model training**
lr = create_model('lr')
lr_30 = create_model('lr', probability_threshold = 0.3)

(图片由作者提供)左侧- LR 没有概率阈值,使用默认的 0.5 |右侧-LR,概率阈值= 0.3。

您可以编写这样一个简单的循环来优化概率截止值:

**# train 10 models at diff thresholds**recalls = []for i in np.arange(0,1,0.1):
   model = create_model('lr', probability_threshold = i, verbose=False)
   recalls.append(pull()['Recall']['Mean'])**# plot it**
import pandas as pd
df = pd.DataFrame()
df['threshold'], df['recall'] = np.arange(0,1,0.1), recalls
df.set_index('threshold').plot()

(图片由作者提供)不同概率下的回忆 _ 阈值(x 轴是阈值,y 轴是回忆)

您还可以简单地构建一个ipywidgets仪表板来测试不同模型的不同概率阈值。

from ipywidgets import interact
import ipywidgets as widgetsdef f(x):
   create_model('lr', probability_threshold = x, verbose = False)
   return pull()interact(f, x=widgets.FloatSlider(min = 0.01, max = 1.0, step = 0.01, value = 0.5));

(图片由作者提供)-当您更改滑块的值时,模型将重新训练,并且 CV 结果将实时更新。

本公告中显示的所有代码示例都在这个 Google Colab 笔记本中。

重要链接

⭐ 教程py caret 新?查看我们的官方笔记本!
📋社区创建的示例笔记本。
📙博客投稿人的教程和文章。
📚文档py caret 的详细 API 文档
📺视频教程我们的视频教程来自各种赛事。
📢讨论有疑问?与社区和贡献者互动。
🛠️ 变更日志变更和版本历史。
🌳路线图 PyCaret 的软件和社区开发计划。

作者:

我写的是关于 PyCaret 及其在现实世界中的用例,如果你想自动得到通知,你可以在 Medium 、 LinkedIn 和 Twitter 上关注我。

宣布盛宴 0.10

原文:https://towardsdatascience.com/announcing-feast-0-10-feast-1d635762896b?source=collection_archive---------26-----------------------

我们如何显著简化了管理功能商店的流程。

由 Unsplash 上的 Pietro Jeng 拍摄

更简单的功能存储

今天,我们宣布盛宴 0.10,这是我们实现轻量级功能商店愿景的一个重要里程碑。

Feast 是一个开源的特性商店,可以帮助你在产品中提供特性。它通过从批处理数据构建训练数据集来防止要素泄漏,自动执行在线要素存储中加载和提供要素的过程,并确保生产中的模型具有一致的要素数据视图。

有了 Feast 0.10,我们极大地简化了管理功能商店的过程。这一新版本允许您:

  • 从您的笔记本电脑运行一个最小的本地功能存储
  • 在 30 秒内将生产就绪的功能存储部署到云环境中
  • 在没有 Kubernetes、Spark 或自我管理基础设施的情况下运营功能商店

我们认为 Feast 0.10 是生产特性的最简单和最快的方式。让我们开始吧!

特色商店面临的挑战

在我们之前的文章中,一个盛宴的状态,我们分享了构建一个所有 ML 团队都可以访问的特色商店的愿景。从那以后,我们一直在努力实现这一愿景,为 AWS、Azure 和本地部署提供支持。

在过去的几个月里,我们看到人们对盛宴的兴趣激增。ML 团队越来越多地承担构建生产 ML 系统的任务,许多人正在寻找开源工具来帮助他们以结构化的方式操作他们的特征数据。然而,这些团队中的许多人仍然负担不起运营自己的功能商店的费用:

“特色店是大基础设施!”

传统观点认为,特色商店应该作为平台来建设和运营。许多人有这种想法并不奇怪。功能存储需要访问计算层、离线和在线数据库,并需要与生产系统直接交互。

这种以基础设施为中心的方法意味着运营自己的功能库是一项艰巨的任务。许多团队根本没有资源来部署和管理特性库。取而代之的是,ML 团队被迫拼凑他们自己的定制脚本,或者在等待工程支持的时候推迟他们的项目。

走向更简单的功能存储

我们对 Feast 的愿景是提供一个特性库,单个数据科学家可以为单个 ML 项目部署该特性库,但也可以扩展到大型平台团队使用。

在 Feast 0.10 中,我们已经将所有基础设施设为可选。这意味着没有火花,没有 Kubernetes,没有 API,除非你需要它们。如果您刚刚起步,我们不会要求您部署和管理平台。

此外,我们将软件的核心整合到一个 Python 框架中。该框架允许团队定义特性,并基于这些定义向本地或云环境声明性地提供特性存储。如果您刚刚开始使用特性存储,您只需要管理一个 Git 存储库并运行 Feast CLI 或 SDK,仅此而已。

Feast 0.10 引入了一流的本地模式:不是通过 Docker 容器安装,而是通过 pip。它允许用户完全从笔记本开始一个最小的特性库,允许针对样本数据进行快速开发,并针对他们在生产中使用的相同 ML 框架进行测试。

最后,我们还开始增加对托管服务的一流支持。Feast 0.10 提供了对 GCP 的原生支持,更多的提供者还在路上。运行 Feast at scale 的平台团队可以获得两全其美的优势:通过利用无服务器技术,功能商店可以扩展到生产工作负载,如果需要,可以灵活地将整个系统部署到 Kubernetes。

新体验

如今,机器学习团队越来越多地承担构建模型以在线预测的任务。这些团队还坐拥 BigQuery、Snowflake 和 Redshift 等仓库中的大量特征数据。将这些功能用于模型训练是很自然的,但是很难以低延迟在线提供这些功能。

Feast 使您可以轻松地将要素数据用于生产。在本例中,我们将向您展示如何根据 BigQuery 数据训练模型,然后使用 Firestore 在生产中以低延迟提供相同的功能。

1.创建特征库

安装 Feast 现在就像:

pip install feast

我们将基于 GCP 模板搭建一个特征库:

feast init driver_features -t gcp

特征库由一个feature _ store . YAML和一组特征定义组成。

driver_features/
└── feature_store.yaml
└── driver_features.py

feature _ store . YAML文件包含设置功能存储所需的基础设施配置。字段用于唯一标识功能存储,是功能定义的真实来源,而指定了我们的功能存储将运行的环境。

project: driver_features
registry: gs://driver-fs/
provider: gcp

特征库还包含基于 Python 的特征定义,如driver _ features . py。该文件包含一个实体和一个特征视图。它们共同描述了 BigQuery 中可用于模型训练或服务的一系列特性。

driver = Entity(
    name="driver_id",
    join_key="driver_id",
    value_type=ValueType.INT64,
)driver_hourly_stats_fv = FeatureView(
    name="driver_hourly_stats",
    entities=["driver_id"],
    ttl=timedelta(weeks=1),
    features=[
        Feature(name="conv_rate", dtype=ValueType.FLOAT),
        Feature(name="acc_rate", dtype=ValueType.FLOAT),
        Feature(name="avg_daily_trips", dtype=ValueType.INT64),
    ],
    input=BigQuerySource(
        table_ref="feast-oss.demo_data.driver_hourly_stats",
        event_timestamp_column="datetime",
        created_timestamp_column="created",
    ),
)

2.设置功能存储

接下来,我们运行 应用 在 GCP 建立我们的特色商店。

feast apply

运行 Feast apply 将向 GCS 特性注册中心注册我们的特性定义,并为读写特性准备我们的基础设施。Apply 可以幂等运行,并且意味着当特性定义改变时从 CI 执行。

此时,我们还没有移动任何数据。我们只在对象存储注册表(GCS)中存储了我们的特性定义元数据,而 Feast 已经配置了我们的基础设施(在本例中是 Firestore)。

3.构建训练数据集

Feast 能够从我们现有的特征数据中构建训练数据集,包括 BigQuery 中上游表中的静态数据。既然我们已经向 Feast 注册了我们的特征定义,我们就能够构建一个训练数据集了。

# Connect to the feature registry
fs = FeatureStore(
   RepoConfig(
       registry="gs://driver-fs/",
       project="driver_features"
   )
)# Load our driver events table.  This dataframe will be enriched with features from BigQuery
driver_events = pd.read_csv("driver_events.csv")# Build a training dataset from features in BigQuery
training_df = fs.get_historical_features(
   feature_refs=[
       "driver_hourly_stats:conv_rate",
       "driver_hourly_stats:acc_rate"
   ],
   entity_df=driver_events
).to_df()# Train a model, and ship it into production
model = ml.fit(training_data)

上面的代码片段将以正确的时间点方式将用户提供的数据帧 driver_events 加入到我们的 driver_stats BigQuery 表中。Feast 能够使用功能表的时间属性(事件时间戳)从任意数量的功能表或视图中重建特定时间点的功能视图。

4.将功能加载到在线商店

此时,我们已经训练好了我们的模型,并准备好为它服务。但是,我们的在线功能商店不包含任何数据。为了将特征加载到特征库中,我们从命令行运行 物化-增量

# Load all features from source up to yesterday’s date
feast materialize-incremental 2021-04-14

Feast 提供了将离线商店的特性加载到在线商店的具体化命令。默认的 GCP 提供程序从 BigQuery 导出特性,并使用内存中的进程将它们直接写入 Firestore。大规模运行的团队可能希望通过使用不同的提供商配置来利用基于云的摄取。

5.低延迟读取功能

既然我们的在线商店已经填充了最新的要素数据,我们的 ML 模型服务就有可能读取在线要素进行预测。

# Connect to the feature store
fs = feast.FeatureStore(
  RepoConfig(registry="gs://driver-fs/", project="driver_features")
)# Query Firestore for online feature values
online_features = fs.get_online_features(
   feature_refs=[
       "driver_hourly_stats:conv_rate",
       "driver_hourly_stats:acc_rate"
   ],
   entity_rows=[{"driver_id": 1001}, {"driver_id": 1002}],
).to_dict()# Make a prediction
model.predict(online_features)

6.好了

此时,您可以安排一个盛宴物化作业,并设置我们的 CI 管道来随着特性定义的变化更新我们的基础设施。您的生产系统应该如下所示:

下一步是什么

我们对 Feast 的愿景是构建一个简单而可扩展的特性库。在 0.10 中,我们提供了本地工作流、基础架构可插拔性,并消除了所有基础架构开销。但是我们才刚刚开始这个旅程,还有很多工作要做。

在接下来的几个月里,我们将致力于让尽可能多的团队可以使用 Feast。这意味着增加对更多数据源、流和云提供商的支持,也意味着与我们的用户密切合作,解锁新的运营 ML 用例及集成。

Feast 是一个社区驱动的项目,这意味着可扩展性始终是我们关注的重点。我们希望让您添加新数据存储、计算层或为新堆栈带来盛宴变得超级简单。我们已经看到团队在预发布期间开始向 0.10 的社区提供者发展,我们欢迎社区在这方面的贡献。

接下来的几个月将是盛宴项目的关键时期。请继续关注更多新闻,我们希望您今天就开始使用 Feast 0.10!

开始

  • ✨:如果你是盛宴的新手,试试我们的快速入门,或者通过我们的文档了解更多关于盛宴的知识。
  • 👋加入我们的 Slack 打个招呼吧!Slack 是你与 Feast 维护者取得联系的最佳论坛,我们喜欢听到试用 0.10 Feast 的团队的反馈。
  • 📢注册参加apply()—ML 数据工程会议,在这里我们将演示 0.10 并讨论AWS的未来发展。
  • 🔥对于希望继续使用 Spark 运行 Kubernetes 盛宴的团队,请查看我们的安装指南和舵图。

🛠️想着为盛宴做贡献?在 GitHub 上查看我们的代码!

原载于 2021 年 4 月 15 日https://feast . dev

宣布 nb2 媒体

原文:https://towardsdatascience.com/announcing-nb2medium-d123e2a8d52c?source=collection_archive---------36-----------------------

从 Jupyter 笔记本更快地发布

在撰写 Medium 博客时,数据科学家和开发人员经常发现自己在复制和粘贴代码块,将图像和表格导入 Medium 编辑器。这可能是一个相当乏味的过程,最重要的是它阻碍了真正的目标:写作。

在这篇文章中,我将展示nb2medium,这是我在过去几周的业余时间开发的一个包**,它可以让用户直接从 Jupyter 笔记本上传中等大小的草稿**。在这里,我将带您了解nb2medium附带的功能。

TLDR

nb2medium 代表了一个简单但足够的框架,可以将 Jupyter 笔记本上传到 medium。

nb2medium最突出的特点是:

  • 使用nbdev编写,因此易于维护
  • Medium API 的简单 pythonic 实现
  • 利用nbconvert和定制的预处理程序将笔记本转换成减价文件
  • 支持上传带有简单单元格内标签的块作为 GitHub gists
  • 支持隐藏单元源、单元输出或单元
  • 附带 Jupyter 扩展CLI 以便于使用

文档、 GitHub 回购、示例笔记本

文章概述

我将讲述如何安装和设置nb2medium,然后我将介绍它的功能。

安装

pip install nb2medium

默认情况下应该启用该扩展,但如果没有启用,请通过运行以下命令手动启用它:

jupyter notebook install nb2medium --py
jupyter notebook enable nb2medium --py

如果您想只为当前用户激活扩展,请在这些命令中添加--user
如果您只想在当前虚拟环境中激活扩展,请在这些命令中添加--sys-prefix

设置

您将需要一个媒体集成令牌,以便能够将您的文章上传到媒体。如果你想上传一些代码块作为 gists,你也需要一个 GitHub 令牌。这两个标记应该都可以作为环境变量使用,因此我们建议您将这两行代码添加到您的 shell 配置文件中(~/.bashrc~/.zshrc是最常见的):

export MEDIUM_TOKEN=<your-medium-token>
export GITHUB_TOKEN=<your-github-token>

您的媒体设置页面 获取媒体集成令牌通过跟随 GitHub 的文档 获取 GitHub 令牌

如何使用

您可以选择使用nb2medium作为命令行工具,直接从 python 或者使用 Jupyter 笔记本扩展按钮或菜单。

注意: nb2medium上传 Jupyter 笔记本,笔记本在渲染前不会被执行。

Jupyter 笔记本上的

您可以选择使用文件菜单下的nb2medium或顶部工具栏上的按钮:

从 CLI

从 shell (bash,zsh,sh):

nb2medium "My article" path/to/notebook.ipynb

使用nb2medium --help查看所有不同的选项

来自 python

图像、代码单元格和表格

形象

图像通常来自本地文件、在线网站或者是绘图的结果。nb2medium能处理这三种情况。如果图像对应于减价单元格中的本地文件(例如![](path/to/image.png))

本地图像被自动检测,然后被上传到媒体端点,并且到图像的路径被替换为新生成的图像 URL。如果图像是绘图的结果,例如:

图像的结果被上传到介质端点(不写入内存),相应的图被图的 URL 替换。

如果图片已经在网上,没有什么改变,因为媒体可以在加载文章时直接从互联网上访问它

将单元格编码为 GitHub gists

默认情况下,代码单元格在呈现时没有语法突出显示或任何花哨的东西,如:

import pandas as pd
pd.DataFrame({'a': [1,2,3], 'b': [2,4,6]})

但是,如果 GitHub 令牌可用,并且用户在单元格中包含以下标头:

# %gist gistname: pandas.py
import pandas as pd
pd.DataFrame({'a': [1,2,3], 'b': [2,4,6]})

代码块将作为私有 gist 上传到用户的 GitHub(默认情况下——可以更改为公共的),相应的代码单元将由 gist URL 替换。

您在本文中看到的 gists 就是使用该功能上传的

#%gist标签接受以下“参数”:

  • gistname:带有文件扩展名的脚本名称(对于正确的语法高亮显示至关重要,例如 script.py)
  • description:[可选]描述要点的字符串(不需要引号)
  • public:【可选-默认为假】gist 是否应该公开
  • upload:【可选-默认来源】是否将单元的来源、输出或两者作为 gists 上传(例如upload: bothupload: source)

隐藏单元格

隐藏单元格的源代码(即代码)、单元格的输出(计算代码的结果)或整个单元格通常很方便。为此,用户可以在相关单元格的开头放置以下标题。

  • 要隐藏单元格的来源:

  • 要隐藏单元格的输出:

  • 要完全隐藏单元格(源和输出):

*注意:*所有标签(%hide-*%gist)都不是按照组合的想法设计的,因此这种用法尚未经过测试。一般来说,不需要这种行为。

注 2: 在本文中很难显示隐藏标签的一些功能,因为我需要显示nb2medium语法。我只想说#%hide标签如你所料的那样工作。

桌子

Medium 对 HTML 和 Markdown 表都没有很好的支持。我首选的现有表选项还是 gists。如果一个单元格输出一个 pandas 数据帧,并且您使用#%gist选项并将upload:标志的值设置为bothoutputnb2medium将检测您的表格,并将其作为 CSV 文件上传到您的 GitHub gist repo。

示例标题:#%gist gistname: pandas.py upload: both

示例输出(下面 2 个 gists):

证明文件

这些文档可以在 https://lucharo.github.io/nb2medium[的](https://lucharo.github.io/nb2medium)获得,并从`nbdev`笔记本上自动呈现,因此它们总是与包源代码保持同步。

贡献和结论

如果您发现了一个 bug 或者想到了一个增强功能,请随时提出问题或者提交请求。如果您想为像这样的开源项目做贡献,请特别关注标签/标记help needed的问题。我打算积极维护这个包,让它成为 Jupyter 用户上传文章的主流方式。

这篇文章确实是在 Jupyter 笔记本上写的,并作为草稿上传到 Medium。我根据自己的需要和偏好制作了这个包,我总是喜欢谨慎发布,这就是为什么我只提供了将笔记本作为草稿提交的选项,而不是直接发布。

📢宣布 PyCaret 的新时间序列模块

原文:https://towardsdatascience.com/announcing-pycarets-new-time-series-module-b6e724d4636c?source=collection_archive---------2-----------------------

(图片由作者提供)PyCaret 的新时间序列模块

🚪简介

PyCaret 是一个用 Python 编写的开源、低代码的机器学习库,可以自动化机器学习工作流。这是一个端到端的机器学习和模型管理工具,可以成倍地加快实验周期,提高您的工作效率。

与其他开源机器学习库相比,PyCaret 是一个替代的低代码库,可以用来用几行代码替换数百行代码。这使得实验快速有效。PyCaret 本质上是几个机器学习库和框架的 Python 包装器,比如 scikit-learn、XGBoost、LightGBM、CatBoost、spaCy、Optuna、Hyperopt、Ray 等等。

PyCaret 的设计和简单性受到了公民数据科学家这一新兴角色的启发,这是 Gartner 首先使用的术语。公民数据科学家是超级用户,他们可以执行简单和中等复杂的分析任务,这些任务在以前需要更多的技术专业知识。

PyCaret 时序模块

PyCaret 新的时间序列模块现已推出测试版。保持 PyCaret 的简单性,它与现有的 API 一致,并附带了许多功能。统计测试、模型训练和选择(30 多种算法)、模型分析、自动化超参数调整、实验记录、云部署等等。所有这些只需要几行代码(就像 pycaret 的其他模块一样)。如果你想试一试,看看官方的快速入门笔记本。

您可以使用 pip 来安装这个库。如果您在同一个环境中安装了 PyCaret,由于依赖冲突,您必须为pycaret-ts-alpha创建一个单独的环境。pycaret-ts-alpha将在下一个主要版本中与主要的 pycaret 包合并

pip install pycaret-ts-alpha

➡️工作流程示例

PyCaret 的时序模块中的工作流程真的很简单。它从setup函数开始,在这里您定义预测范围fh和数量folds。您也可以将fold_strategy定义为expandingsliding

设置完成后,著名的compare_models函数训练并评估了从 ARIMA 到 XGboost (TBATS、FBProphet、ETS 等等)的 30+算法。

plot_model功能可在训练前或训练后使用。在训练前使用时,它使用 plotly 接口收集了大量的时序 EDA 图。当与模型一起使用时,plot_model处理模型残差,并可用于访问模型拟合。

最后,predict_model用于生成预测。

📊加载数据

import pandas as pd
from pycaret.datasets import get_data
data = get_data('pycaret_downloads')
data['Date'] = pd.to_datetime(data['Date'])
data = data.groupby('Date').sum()
data = data.asfreq('D')
data.head()

(图片由作者提供)

# plot the data
data.plot()

(图片由作者提供)pycaret_downloads 的时间序列图

这个时间序列是每天从 pip 下载 PyCaret 库的次数。

⚙️初始化设置

**# with functional API** from pycaret.time_series import *
setup(data, fh = 7, fold = 3, session_id = 123)**# with new object-oriented API** from pycaret.internal.pycaret_experiment import TimeSeriesExperiment
exp = TimeSeriesExperiment()
exp.setup(data, fh = 7, fold = 3, session_id = 123)

(图片由作者提供)设置功能的输出

📐统计测试

check_stats()

(图片由作者提供)check_stats 函数的输出

📈探索性数据分析

**# functional API**
plot_model(plot = 'ts')**# object-oriented API** exp.plot_model(plot = 'ts')

(图片由作者提供)

**# cross-validation plot** plot_model(plot = 'cv')

(图片由作者提供)

**# ACF plot** plot_model(plot = 'acf')

**# Diagnostics plot** plot_model(plot = 'diagnostics')

**# Decomposition plot**
plot_model(plot = 'decomp_stl')

✈️模特培训和选拔

**# functional API** best = compare_models()**# object-oriented API** best = exp.compare_models()

(图片由作者提供)compare_models 函数的输出

create_model在时序模块中,其工作方式与在其他模块中一样。

**# create fbprophet model** prophet = create_model('prophet')
print(prophet)

(图片由作者提供)create_model 函数的输出

(作者提供的图片)打印功能的输出

tune_model也没什么不同。

tuned_prophet = tune_model(prophet)
print(tuned_prophet)

(图片由作者提供)tune_model 函数的输出

(作者提供的图片)打印功能的输出

plot_model(best, plot = 'forecast')

(图片由作者提供)

**# forecast in unknown future** plot_model(best, plot = 'forecast', data_kwargs = {'fh' : 30})

(图片由作者提供)

# in-sample plot
plot_model(best, plot = 'insample')

# residuals plot
plot_model(best, plot = 'residuals')

# diagnostics plot
plot_model(best, plot = 'diagnostics')

🚀部署

**# finalize model** final_best = finalize_model(best)**# generate predictions** predict_model(final_best, fh = 90)

(图片由作者提供)

**# save the model** save_model(final_best, 'my_best_model')

(图片由作者提供)

该模块仍处于测试阶段。我们每天都在增加新的功能,并且每周发布 pip。请确保创建一个单独的 python 环境,以避免与主 pycaret 发生依赖冲突。这个模块的最终版本将在下一个主要版本中与主要的 pycaret 合并。

📚时间序列文档
❓ 时间序列常见问题
🚀特性和路线图

开发者:
尼希尔·古普塔(主角)安东尼·鲍姆 萨提亚·帕特奈克 米盖尔·特雷霍·马鲁夫 克里希南·S·G

使用 Python 中的这个轻量级工作流自动化库,您可以实现的目标是无限的。如果你觉得这很有用,请不要忘记给我们 GitHub 库上的⭐️。

想了解更多关于 PyCaret 的信息,请在 LinkedIn 和 Youtube 上关注我们。

加入我们的休闲频道。邀请链接此处。

重要链接

⭐ 教程py caret 新手?查看我们的官方笔记本!
📋社区创建的示例笔记本。
📙博客投稿人的教程和文章。
📚文档py caret 的详细 API 文档
📺视频教程我们的视频教程来自各种赛事。
📢讨论有问题吗?与社区和贡献者互动。
🛠️ 变更日志变更和版本历史。
🌳路线图 PyCaret 的软件和社区开发计划。

作者:

我写的是 PyCaret 及其在现实世界中的用例,如果你想自动得到通知,你可以在 Medium 、 LinkedIn 和 Twitter 上关注我。

宣布 Streamlit 应用的主题化!🎨

原文:https://towardsdatascience.com/announcing-theming-for-streamlit-apps-834ff831ac17?source=collection_archive---------31-----------------------

尝试新的黑暗模式和自定义主题功能

我们喜欢认为 Streamlit 应用程序开箱后就很漂亮(希望你也是!),但直到今天,改变外观和感觉来反映你的个人风格或公司的视觉品牌还不容易。Streamlit Theming 现在允许您轻松地更改应用程序中的配色方案和字体,而无需接触任何 HTML 或 CSS。

此外,为了便于访问和用户友好,我们还让应用程序查看者能够在亮暗主题之间切换。

如果你想直接进入,看看这个示例应用!

引入黑暗模式!

在我们进入自定义主题之前,我们先来谈谈黑暗模式。

根据大众需求,我们现在为应用程序查看者提供定制他们想要如何体验您的应用程序的能力。这对于在黑暗环境中使用应用程序的人,或者具有辅助功能需求、希望覆盖应用程序自定义主题的人非常有用。

要在各种主题之间切换,请进入右上角的菜单并选择设置。你会看到一个重新设计的设置对话框,让应用程序用户在不同的主题选项之间进行选择:

  • 灯光模式:这是你已经知道并喜爱的 Streamlit 主题
  • 黑暗模式:这是 Streamlit 新的黑暗主题。你也会喜欢这个的!
  • 使用系统设置 : Streamlit 会自动拾取你的操作系统主题(亮或暗),并随你的 OS 改变颜色(注意:可能不适用于某些浏览器和 Linux 发行版)。
  • 自定义主题(仅在 app 作者提供时可见):使用 app 作者提供的主题。如果提供,这是默认设置。否则,“使用系统设置”是默认设置。

创建自定义主题

主题通过 Streamlit 的配置系统设置。你可以直接在那里设置主题,或者你可以使用我们新奇的主题编辑器界面来构建一个主题,然后把它复制到你的配置中。

使用主题编辑器创建主题

应用程序开发人员只需进入菜单→设置→编辑活动主题,即可创建自定义主题。

:主题编辑器菜单仅在local开发中可用。如果您已经使用 Streamlit 共享部署了您的应用,“编辑活动主题”按钮将不再显示在“设置”菜单中。

直接使用config.toml创建主题

自定义主题也可以在配置文件中定义:./.streamlit/config.toml

[theme]部分,可以定义颜色变量来创建自定义主题

[theme]

# Primary accent for interactive elements
primaryColor = '#7792E3'

# Background color for the main content area
backgroundColor = '#273346'

# Background color for sidebar and most interactive widgets
secondaryBackgroundColor = '#B9F1C0'

# Color used for almost all text
textColor = '#FFFFFF'

# Font family for all text in the app, except code blocks
# Accepted values (serif | sans serif | monospace) 
# Default: "sans serif"
font = "sans serif"

(:一次只能设置一个自定义主题)

来自配置文件的实时更新

您知道当您更改其源代码时,您的 Streamlit 应用程序是如何实时更新的吗?我们现在也这样做,当你更新应用的配置文件!这样,直接在配置文件中编辑主题将使你的应用程序立即显示新的主题。

每种颜色设置的作用

Streamlit 的主题系统遵循一种将颜色和字体应用于应用程序的全球方法。颜色样式有语义名称和一种简单的颜色规范方法。

  • 原色:交互元素的强调色,如st.radio,按钮边框等。默认情况下,这是粉红色的。
  • 背景颜色:这是你的应用程序主体的背景颜色。
  • **文字颜色:**漂亮不言自明!这是应用程序的文本颜色。
  • **次要背景色:**用作st.sidebar和几个小工具的背景。

背景色和第二背景色在某种程度上是“互补的”,因为它们用于放置在基本元素“上面”的元素,以促进一致性和可及的对比度。这意味着st.number_input小部件在主体中定义时将使用Secondary background color作为背景色,但在侧边栏中定义时使用Background color。下图说明了这一点:

主题和自定义组件

如果你是一个组件作者,我们可以让你很容易地阅读你的 JS 和 CSS 代码中的主题颜色。要使用这项新功能,请安装或更新最新版本的streamlit-component-lib

npm install streamlit-component-lib

一旦这个包被更新,一个组件的背景和文本颜色将自动改变以匹配活动主题的颜色。附加的主题属性以两种等效的方式向组件公开:主题对象或主题样式。

作为一个反应道具

通过theme道具传递给组件的对象具有以下形状:

{
    "primaryColor": "someColor1"
    "backgroundColor": "someColor3",
    "secondaryBackgroundColor": "someColor4",
    "textColor": "someColor5",
    "font": "someFont",
}

作为 CSS 变量

--primary-color
--background-color
--secondary-background-color
--text-color
--font

如果你不熟悉 CSS 变量,TLDR 版本是你可以这样使用它们:

.mySelector {
  color: var(--text-color);
}

向组件公开主题属性的两种方法包含相同的信息,因此使用哪一种取决于个人偏好。让定制组件与主题一起工作的例子可以在这里找到。

找到你最喜欢的新主题

这是我们的贴图演示,有一个受“宁静光”启发的主题:

[theme]

primaryColor="#6eb52f"
backgroundColor="#f0f0f5"
secondaryBackgroundColor="#e0e0ef"
textColor="#262730"
font="sans serif"

以下是一个受“日晒”启发的主题示例:

[theme]

primaryColor="#d33682"
backgroundColor="#002b36"
secondaryBackgroundColor="#586e75"
textColor="#fafafa"
font="sans serif"

尝试主题化,让我们知道你的想法!

要尝试主题化,只需像往常一样升级到 Streamlit 的最新版本:

$ pip install streamlit --upgrade

我们迫不及待地想看到这个社区将要构建的又酷又神奇的主题。如果你创建了一个很酷的自定义主题,我们很乐意看到它,所以当你在 Twitter 、 LinkedIn 或我们的论坛上分享时,请标记@streamlit

资源

  • 文档
  • Github
  • 变更日志
  • 论坛
  • 样本 app

宣布 YOLTv4:改进的卫星图像目标探测

原文:https://towardsdatascience.com/announcing-yoltv4-improved-satellite-imagery-object-detection-f5091e913fad?source=collection_archive---------10-----------------------

将高级目标探测扩展到任意大的卫星图像

前言:虽然 CosmiQ 作品 (及其相关博客:The DownLinQ)不幸被关闭,但在地理空间分析领域仍有许多工作要做。因此,这篇博客详细描述了 IQT 在业余时间独立完成的https://www.iqt.org工作。

在之前的一些博客[如 1 、 2 、 3 ]和学术论文[如 4 、 5 、 6 ]中,我们已经展示了采用 YOLO 探测卫星图像中的物体的惊人功效。回想一下,YOLO 是一个领先的深度学习对象检测框架,旨在检测图像中的对象。YOLO 最大限度地利用了几千像素大小的图像,这对于处理超过 1 亿像素的大规模卫星图像来说太小了。因此,我们构建了 YOLT (并用 SIMRDWN 扩展了 YOLT)来优化这个任意大尺寸卫星图像的目标检测框架。

我们现在很高兴地宣布,一个更快、性能更好的改进版本 YOLTv4 已经发布。代码在 github.com/avanetten/yoltv4 的开源。在下面的小节中,我们将详细介绍这个新的存储库,并提供示例结果。

1。简介

YOLTv4 旨在快速检测航空或卫星图像中任意大图像的对象,这些图像远远超过深度学习对象检测框架通常摄取的约 600×600 像素大小。这个库建立在 AlexeyAB 的 YOLOv4 实现的令人印象深刻的工作之上,与 YOLOv3(在 SIMRDWN 中实现)相比,它提高了速度和检测性能。我们用 YOLOv4 代替 YOLOv5,因为 YOLOv4 得到了 YOLO 原创者的认可,而 YOLOv5 没有;此外,YOLOv4 似乎具有优越的性能。

因此,YOLTv4 将令人印象深刻的 YOLOv4 主干与 SIMRDWN 的预处理和后处理脚本结合起来,创建了一个改进的、轻量级的替代 SIMRDWN。回想一下,SIMRDWN 允许用户从众多主干中进行选择(例如,YOLO、固态硬盘、fast-RCNN 等。).虽然这种灵活性非常有用,但它确实扩大了代码库;此外,多项研究[ 5 , 6 ]表明,YOLO 骨干网在卫星图像目标检测方面优于其他方法。因此,在 YOLTv4 中,我们选择构建一个更简单的代码库,并且只关注 YOLOv4 的主干。

下面,我们提供了如何使用开源稀有面数据集的例子。

2。运行 YOLTv4

在本节中,我们将演示如何使用稀疏飞机数据集来训练和测试 YOLTv4,以定位飞机。

2.1。安装

YOLTv4 构建为在支持 GPU 的机器上的 docker 容器中执行。docker 命令使用 CUDA 9.2、python 3.6 和 conda 创建 Ubuntu 16.04 映像。

  1. 克隆这个库(例如克隆到 /yoltv4/ )。
  2. 将模型重量下载到 yoltv4/darknet/weights(如见:https://github . com/AlexeyAB/dark net/releases/download/dark net _ yolo _ v3 _ optimal/yolov 4 . conv . 137)。
  3. 安装 nvidia-docker 。
  4. 构建 docker 文件。nvidia-docker build -t yoltv4_image /yoltv4/docker
  5. 旋转对接容器(选项见对接文件)。nvidia-docker run -it -v /yoltv4:/yoltv4 -ti — ipc=host — name yoltv4_gpu0 yoltv4_image
  6. 编译 Darknet C 程序。先在/yoltv 4/darknet/Makefile中设置 GPU=1 CUDNN=1,CUDNN_HALF=1,OPENCV=1,然后:
    cd /yoltv4/darknet make

2.2。列车

A .准备数据

  1. 制作 YOLO 图像和标签(详见yoltv 4/notebooks/prep _ data . ipynb*)。*
  2. 创建一个列出训练图像的 txt 文件。
  3. 创建文件 obj.names 文件,每个所需的对象名称各占一行。
  4. 在包含必要文件的目录 yoltv4/darknet/data 中创建文件 obj.data。例如/yoltv 4/darknet/data/rare planes _ train . data:* classes = 30
    train =/local _ data/cosm IQ/wdata/rare planes/train/txt
    valid =/local _ data/cosm IQ/wdata/rare planes/train/txt/valid . txt
    names =/yoltv 4/darknet/data/rare planes . name
    backup = backup*
  5. 准备配置文件。
    参见此处的说明,或者调整
    /yoltv 4/darknet/CFG/yoltv 4 _ rare planes . CFG*。*

B .执行培训

  1. 执行。
    cd /yoltv4/darknet time ./darknet detector train data/rareplanes_train.data cfg/yoltv4_rareplanes.cfg weights/yolov4.conv.137 -dont_show -mjpeg_port 8090 -map
  2. 审查进度(见下图 1):

图一。YOLTv4 训练进度(保存为/yolt v4/darknet/chart _ yolt v4 _ rare planes . png*)*

2.3。测试

A .准备数据

  1. 制作切片图像(详见yoltv 4/notebooks/prep _ data . ipynb*)。*
  2. 创建一个列出训练图像的 txt 文件。
  3. 在包含必要文件的目录 yoltv4/darknet/data 中创建文件 obj.data。例如/yoltv 4/darknet/data/rare planes _ test . data:*
    classes = 30
    train =
    valid =/local _ data/cosm IQ/wdata/rare planes/test/txt/test . txt
    names =/yoltv 4/darknet/data/rare planes . name
    backup = backup/*

B .执行测试

  1. 执行(在特斯拉 P100 上以每秒 80 帧以上的速度进行):
    cd /yoltv4/darknet
  2. time ./darknet detector valid data/rareplanes_test.data cfg/yoltv4_rareplanes.cfg backup/ yoltv4_rareplanes_best.weights
  3. 后处理检测,将图像拼接在一起,并制作绘图:
*time python /yoltv4/yoltv4/post_process.py \
 — pred_dir=/yoltv4/darknet/results/rareplanes_preds_v0/orig_txt/ \
 — raw_im_dir=/local_data/cosmiq/wdata/rareplanes/test/images/ \
 — sliced_im_dir= /local_data/cosmiq/wdata/rareplanes/test/yoltv4/images_slice/ \
 — out_dir= /yoltv4/darknet/results/rareplanes_preds_v0 \
 — detection_thresh=0.25 \
 — slice_size=416 \
 — n_plots=8*

3。输出

输出将类似于下图。

图二。从开源的稀有飞机数据集中检测到飞机。

4。结论

YOLTv4 是高性能和相当迅速的。在单个特斯拉 P100 GPU 上,即使在最高分辨率的 30 厘米卫星图像上,推理也能以每秒约 2 平方公里的速度进行。以这种速度,一个简单的 4-gpu 集群可以近乎实时地处理整个 WorldView-3 收集。所以,请随意旋转代码来寻找汽车、船只、基础设施项目、飞机、建筑,或者任何你感兴趣的东西。

新加坡节约的再生能源——一个数据分析项目

原文:https://towardsdatascience.com/annual-recycled-energy-saved-in-singapore-2d6bad49bfb2?source=collection_archive---------14-----------------------

了解新加坡每年通过回收塑料、纸张、玻璃、黑色金属和有色金属节约了多少能源。

图片由作者|元素由宏向量、 Freepik

介绍

由于政府担心不断增加的垃圾处理量,英格波尔成为了一个零垃圾国家的新里程碑。按照目前的速度,到 2035 年,三宝垃圾填埋场将会耗尽空间,这对新加坡人来说是一个令人担忧的情况。更糟糕的是,新加坡用于建设新焚烧厂或垃圾填埋场的土地有限。政府希望通过分享联合回收努力每年节省的总能量来激励公民。

https://www.nea.gov.sg/our-services/waste-management

我们将使用回收统计数据来计算从 2003 年到 2020 年每年节约的能源,基于五种废物类型:塑料、纸张、玻璃、黑色金属和有色金属。

密码

加载数据集

我们将使用 Plotly 进行可视化,使用 Pandas 进行数据分析。我们的数据集分为两个时间线,从 2003 年到 2017 年和 2018 年到 2020 年。2020 年的数据是通过 NEA 网站手动添加的。您可以在 Greentumble 上找到每种材料类型的额外能量数据。

2018 年至 2020 年的废物数据

经过初步探索,我们发现两个数据集中的列名不同,因此我们将对 2018 年至 2020 年的数据集进行重命名,并将千吨值转换为公吨值。

让我们将回收率添加到我们的数据框架中,因为我们将使用它进行进一步的分析。

节能数据集

我们可以看到节能数据集的表格格式不正确。让我们把它转换成可读的格式。

  1. 移项
  2. 删除前两列和第一行
  3. 重置索引
  4. 重命名列

我们可以看到,我们有三栏,材料节能原油节能

2003 年至 2017 年的废物数据

我们将限制我们的列,以匹配 2018 年至 2020 年数据集的列,以便它们可以很容易地连接。

数据分析

我们将堆叠两个数据集,因为它们有相似的列。最终数据集包含 2003 年至 2020 年的样本。

让我们看一下每年产生的废物总量和回收的废物总量。

到 2017 年,垃圾收集量迅速增加,此后,由于政府干预,我们可以看到垃圾收集量的下降。由于新冠肺炎疫情的影响,去年 NEA 的垃圾收集量急剧下降。

通过分析数据集中的分类废物类型,我们可以清楚地看到同一类别有不同的名称。我们来做一些文字处理,尽量做出和cleaned _ energy _ saved . CSV中提到的素材类似的类别。

通过使用简单的字符串替换,我们已经规范化了我们的类别,这样我们就可以在 waste_type 上合并我们的数据。

为了检查我们是否成功地规范化了类别,让我们合并关于 waste_typematerial 的两个数据集。

我们需要通过删除“Kwh”和“kWh”将节省的能源从字符串转换为整数。

正如我们所看到的,节省的能量被成功地转换成一个整数,这样我们就可以用它来计算每年节省的总能量。

我们需要通过将total_waste_recycled_tonneenergy_saved相乘来创建新的特征total_energy_saved

形象化

让我们计算每种废物的平均回收量。黑色金属和有色金属的总体回收率较高,塑料的回收率最低。

我们必须使用箱线图来检查最终数据中的异常值和模式。我们发现在 2018 年有一个异常,为了找出它,我们必须检查我们的数据集。

经过查看 2018 年回收的废物总量,我们发现黑色金属产生的废物总量为 126900 吨,而回收的废物总量为 126000 吨。正如我们所知,黑色金属的平均回收率为 90%以上,但它显示的是 10%,这很奇怪,所以我回到了网站上的原始数据,并发现了错误。我们可以在 PDF 中看到,整个零都不见了。

让我们更新该值并再次检查盒图,正如我们所看到的,它现在看起来很完美。这个故事的寓意是,总是回去检查数据是否有错误。

总节能量的箱线图到处都是,因为一些材料每公吨产生更高的能量千瓦时。

我们更多地与数据互动,并在多级散点图中寻找模式。

正如我们所见,由于政府采取措施控制废物的产生,在过去几年中,从纸张和塑料中节省的总能量已经显著减少。

每年节约的能源

现在是时候计算从 2003 年到 2020 年每年节省的能源了,基于五种废物类型,塑料,纸张,玻璃,黑色金属和有色金属。

  • 每年分组依据
  • 总结和提取节省的总能量
  • 将其转换成熊猫数据帧
  • total_energy_saved从浮点数转换为整数

我们现在将通过使年度节能量可读

  • 将 kWh 转换为 GWh
  • 四舍五入到小数点后两位。
  • 添加 GWh 后缀
  • 显示过去五年的数据

最后的想法

我们已经清理了数据,并确保它可以与其他数据集合并。我们还学习了如何检测数据集中的异常并创建新功能。这个项目很简单,但是它教会了我们很多关于数据清理和数据可视化的东西。由于 Covid 19,垃圾收集量已经停止,因此 2020 年节省的回收能源相对较少。政府的举措也影响了整体垃圾产量,因为人们正在从塑料转向更环保的材料。与其他废物类型相比,纸张和有色金属材料产生的能量最高,我们看到这两种材料的能量都在减少。

这篇文章是初学者友好的,详细解释了数据清理和可视化。我还在下面分享了我的项目代码,这样你就可以克隆并开始与项目文件交互。

代码可从以下网址获得:

深度笔记

https://deepnote.com/project/Singapore-Recycled-Energy-Xh_wbptwTn2l-I1GQ-O1Vw/%2FAnnual-Recycled-Energy-Saved-in-Singapore%2FREADME.md

开源代码库

https://github.com/kingabzpro/Annual-Recycled-Energy-Saved-in-Singapore

卡格尔

https://www.kaggle.com/kingabzpro/singapore-recycling-and-waste-management

学习资源

DataCamp 课程 : 使用 Python 的数据分析师

https://www.goodreads.com/book/show/23594979-data-analytics-made-accessible https://github.com/WillKoehrsen/Data-Analysis/tree/master/medium

异常检测备忘单

原文:https://towardsdatascience.com/anomaly-detection-cheat-sheet-5502fc4f6bea?source=collection_archive---------24-----------------------

在 6 分钟内你应该知道的异常检测

异常检测。作者图片

在本文中,您将找到一份完整的异常检测备忘单。在六分钟内,你将能够知道它是什么,并刷新主要算法的记忆。

异常检测(也称为异常值检测)是检测异常实例的任务,这些异常实例与正常情况非常不同。这些实例被称为异常(或异常值),而正常实例被称为内联者

Scikit Learn 的异常检测算法。图像来源

异常检测在各种各样的应用中是有用的,最重要的是:

  • 数据清理 —在训练另一个模型之前从数据集中移除离群值
  • 直接异常检测 *任务:*欺诈检测,检测制造中的缺陷产品等。

您可能已经注意到,使用异常检测算法也可以解决不平衡分类的一些问题。但是你需要理解其中的区别——这是两种完全不同的方法。

分类的情况下,我们希望了解什么样的异常(正面例子)看起来像,以便在未来检测类似的实例。在异常检测的情况下,未来的异常可能看起来与我们之前见过的任何例子都完全不同。因为我们的数据集是未标记的,我们只能怀疑正常的数据点看起来如何,并将任何其他实例视为异常。

例如,垃圾邮件检测任务可以被认为是分类任务(当我们有足够的垃圾邮件来了解垃圾邮件应该是什么样子时),也可以被认为是异常检测任务(当我们必须了解正常电子邮件看起来是什么样子并将任何其他电子邮件视为垃圾邮件时)。

一个密切相关的任务是**新颖性检测,**但在这种情况下,算法被假定为在干净的数据集上训练(没有离群值)。当需要识别一个新实例是否是离群点时,它被广泛用于在线学习。

另一个相关的任务是密度估计。这是估计数据集生成的过程的概率密度函数的任务。基于密度概念(高斯混合模型DBSCAN )的聚类算法通常可以解决这个问题,并且还可以帮助进行异常检测和数据分析。

统计方法

检测异常值最简单的方法是尝试很久以前开发的统计方法。其中最受欢迎的一种被称为异常值检测的 Tukey 方法(或四分位数间距(IQR) )。

它的本质是计算百分位数和四分位数之间的范围。位于Q1-1.5 * IQR之前和 Q3 + 1.5IQR* 之后的数据点被视为异常值。下面你可以看到使用人身高数据集的例子来说明这种方法。低于 54.95 英寸(139 厘米)和高于 77.75 英寸(197 厘米)的身高被视为异常值。

以身高为例的 Tukey 异常值检测方法。作者图片

这种方法和其他统计方法(用于检测异常值的 z-score 方法等)。)通常用于数据清理。

聚类和降维算法

另一种简单、直观且通常有效的异常检测方法是使用一些聚类算法解决密度估计任务,如高斯混合模型DBSCAN 。然后,位于具有较低密度水平的区域中的任何实例都可以被认为是异常,我们只需要设置一些密度阈值。

此外,可以使用任何具有inverse_transform()方法的降维算法。这是因为异常的重建误差总是比正常情况下的大得多。

隔离森林和 SVM

一些监督学习算法也可以用于异常检测,最流行的两个是隔离森林SVM 。这些算法更适合于新奇检测,但通常也适用于异常检测。

隔离森林算法构建了一个随机森林,其中每棵决策树都是随机生长的。每走一步,这片森林就会隔离越来越多的点,直到所有的点都被隔离。由于异常的位置远离通常的数据点,它们通常比正常情况下被隔离的步骤少。该算法对于高维数据表现良好,但是需要比 SVM 更大的数据集。

SVM (在我们的例子中是一级 SVM )也被广泛用于异常检测。感谢内核技巧内核化的 SVM 可以构建一个有效的“极限超平面”,它将正常点与离群点分开。像任何 SVM 修正一样,它可以很好地处理高维或稀疏数据,但只适用于中小型数据集。

局部异常因素

局部异常因子( LOF )算法基于异常位于低密度区域的假设。然而,它不是仅仅设置一个密度阈值(就像我们对 DBSCAN 所做的那样),而是将某个点的密度与其最近邻居的密度进行比较。如果这个点的密度比它的邻居低得多(这意味着它离他们很远),它就被认为是一个异常点。

该算法既可以用于异常检测,也可以用于异常检测。由于它的计算简单且质量好,所以经常被使用。

通过 Scikit 学习局部异常值因子。图像来源

最小协方差行列式

最小协方差行列式( MCD 或其变形 Fast-MCD )对于异常值检测非常有用,特别是对于数据清理。它假设内点是从单个高斯分布生成的,而离群点不是从这个分布生成的。由于许多数据具有正态分布(或者可以简化为正态分布),因此该算法通常表现良好。它是在EllipticEnvelope sklearn 类中实现的。

如何选择一种异常检测算法?

如果您需要清理数据集,您应该首先尝试经典的统计方法,如用于异常值检测的 Tukey 方法。然后用 Fast-MCD ,如果你知道你的数据分布是高斯的。

如果你做异常检测不是为了数据清洗,首先尝试简单快速的**局部离群因子。**如果效果不好(或者出于某种原因需要分离超平面)——根据你的任务和数据集尝试其他算法:

  • 稀疏高维数据的一类 SVM 或密集高维数据的隔离林
  • 高斯混合模型如果你可以假设数据是由几个高斯分布混合生成的
  • 诸如此类。

异常检测算法选择。作者图片

本文是以下内容的一部分:

您可能还对以下内容感兴趣:

感谢您的阅读!

  • 我希望这些材料对你有用。在 Medium 上关注我可以获得更多类似的文章。
  • 如果您有任何问题或意见,我将很高兴得到任何反馈。在评论中问我,或者通过 LinkedIn 或 Twitter 联系我。
  • 为了支持我作为一名作家,并获得数以千计的其他媒体文章,使用我的推荐链接获得媒体会员资格(不收取额外费用)。

异常检测——如何区分性能好坏

原文:https://towardsdatascience.com/anomaly-detection-how-to-tell-good-performance-from-bad-b57116d71a10?source=collection_archive---------4-----------------------

行业笔记

通常很难评估性能和量化工具对业务的潜在影响。

照片由阮当黄虎在 Unsplash 上拍摄

摘要

  • 除了准确性之外,假阳性假阴性率是评估绩效的合理、直观的方法
  • **并非所有的异常检测器都是相同的:**异常检测器之间的性能分数可能会有很大的不同,这些异常检测器针对业务指标对相同的真实时间序列数据进行操作
  • 在我们的测试数据中, **Avora 的异常检测器实现了比脸书 Kats 更好的性能,具有明显更低的假阳性&阴性率,**但相当的准确性
  • 通过超参数调整,甚至可以实现更低的假阳性/假阴性率,而不会降低精确度

介绍

世界各地的每家企业都有越来越多的数据可用于分析绩效和做出数据驱动的决策。然而,相当多的公司发现他们自己有太多的数据,不可能被人跟踪和分析。因此,人工智能驱动的商业智能工具,特别是异常检测,在商业成功中发挥着越来越重要的作用。

商业智能并不缺少产品和解决方案,但是通常很难评估性能和量化工具对业务的潜在影响。难以评估的原因包括:

  1. 缺乏与嘈杂的真实业务绩效数据相关的绩效比较数据集
  2. 绩效是用复杂的科学指标来描述的,这些指标不容易转化到商业世界中。

在 Avora 中,我们使用基于业务数据的真实生活时间序列创建了一个评估管道,以根据著名的脸书·卡茨异常检测器对 Avora 的性能进行基准测试,该检测器与流行的脸书先知包紧密相关。

直观地衡量和解释绩效

除了准确性,评估异常检测解决方案时最常用的指标是 F1精度召回。人们可以按以下方式考虑这些指标:

  • 回忆用于回答以下问题:*真正的异常被识别的比例是多少?*计算如下:

  • 精度回答问题:识别出的异常有多大比例是真异常?

  • F1 得分使用调和平均值,通过结合召回率和精确度来确定异常检测模型的整体性能

例如:

您正在将销售指标作为 KPI 之一进行跟踪。您会收到一个通知,告知您已经识别出 10 个异常。您检查图表并确认10 个日期中只有 6 个确实是异常。然而,您也注意到还有其他 9 个日期的销售指标表现异常,您会认为它们是异常。因此,现在您有 6 个正确识别的异常(真阳性),4 个错误识别的异常(假阳性)和 9 个遗漏的异常(假阴性)。

在这种情况下,度量值将是:

  • 回忆:6 / (6 + 9) = 0.4
  • 精度:6 / (6 + 4) = 0.6
  • F1 得分:2 * (0.4 * 0.6) / (0.4 + 0.6) = 0.48

假阳性和假阴性率

在决定异常检测系统时,关注指标很重要:误报率和漏报率。

假阳性率帮助您了解平均有多少次,您的检测器会发出“狼来了”的警报,并标记实际上不是真正异常的数据点。

在上例中,假阳性率为 0.4 或 40% —系统识别出 10 个异常,其中只有 6 个是真异常。这意味着 40%检测到的异常事实上根本不是异常。

选择假阳性率最低的系统。如果假阳性率太高,用户将关闭系统,因为它更令人分心而不是有用。

假阴性率显示平均有多少异常被检测机遗漏。

在示例中的假阴性率是 9/15 = 0.6 或 60% 。该系统确定了 6 个真正的异常,但遗漏了 9 个。这意味着系统遗漏了数据中 60%的异常。

选择假阴性率最低的系统。如果假阴性率太高,你将会错过许多关键的异常,最终你会对系统失去信任。

方法学

为了进行性能比较,我们创建了一个旨在提供客观、公正评估的系统。

  1. 来自真实生活例子的时间序列数据被收集和匿名化。
  2. 在性能评估之前,异常被手动标记——这被存储为基于分析师评估的地面真实数据集,独立于任一算法的结果。
  3. 地面真实数据集被一条管道使用,该管道仅在 Avora 和 KATS 异常检测算法完成标记后执行评估。

绩效评估设置和阶段

结果和分析

下面你可以看到 Avora 和 Kats 在 19 个匿名数据集上的结果,跨越多个商业领域。这些结果代表了算法在生产环境中的表现。

Kats 和 Avora 算法的关键指标结果

所用每个数据集的 Kats 和 Avora 性能指标

取所有数据集的平均值,Avora 的异常检测算法在每个指标(准确度、精确度、召回率、F1)上都比脸书·KATS 获得了更高的分数。同样值得一提的是,对于 KATS,精确度和召回率有更大比例的极端得分(0 或 1),这取决于数据集。

虽然令人鼓舞,但这些测量突出了需要改进的地方,特别是在精度方面(Avora 算法的平均值为 0.51)。

通过检查假阳性和假阴性率,我们看到 KATS 没有检测到这些数据集集合中大约 80%的异常(如地面实况中所标记的),这可能会导致用户质疑在现实生活的业务数据上部署当前形式的 Kats 算法的有效性。

Kats 和 Avora 的假阳性和假阴性率

时间序列和异常比较

我们将更详细地介绍两个数据集的结果:数据集 ID 85693 和数据集 ID 2258。与 Avora 不同,脸书·KATS 的输出不提供检测包络,仅将特定数据点标记为异常。

在线销售-数据集 ID 2258

下图显示了数据集 ID 2258 的手动标注、阿维拉异常检测标注和脸书 KATS 异常检测标注的输出。在这种情况下,提供地面实况的分析师确定了 2019-20 年的高峰和 2020 年夏季的显著下降。

Avora 的算法已经成功地将峰值和夏季下降识别为异常。除此之外,它在 2019 年深秋出现了较小的下降,以及其他较小的峰值和下降。一些人可能会认为,算法标记的那些额外异常中的一些(例如,2019 年 11 月)可以被视为地面真相中的异常。

脸书·KATS算法只成功地发现了峰值,没有将任何长期的下降识别为异常——这是一个问题,因为销售或收入的突然下降对任何企业来说都是非常重要的。

数据集 ID 2258 的手动标注、Avora 输出和 Kats 输出

API 网关错误-数据集 ID 85693

事实上,分析师将 2021 年 4 月的一次飙升和 2020 年 11 月/12 月的两次短期下跌称为异常。Avora 异常检测公司设法找到了所有的异常区域。

另一方面,Kats 只设法捡起了尖峰本身,并在 2021 年 4 月尖峰之前引入了许多假阳性——我们解释说 KATS 对较小幅度的变化不太敏感,这可能对用户仍然有用。

数据集 ID 85693 的手动标注、Avora 输出和 Kats 输出

进一步研究—降低假阳性/假阴性率

这种比较是使用每种算法的默认参数进行的——不可能找到允许 Kats 改变异常检测灵敏度的参数。

Avora 的异常检测系统允许用户修改输入参数以改善结果,这是用户应该采取的自然步骤。通过搜索参数空间,我们观察到,如果输入参数针对单个数据集进行优化,结果可以得到显著改善。

数据集 ID2258 的手动标记和优化的 Avora 算法输出

例如,优化数据集 ID 2258(在线销售)的参数显著改善了所有指标的结果— 误报率几乎减半:

  • 精度:默认— 0.464,优化— 0.722 (越高越好)
  • 回忆:默认— 0.812,优化— 0.813 (越高越好)
  • F1 得分:默认— 0.591,优化— 0.764 (越高越好)
  • 精度:默认— 0.979,优化— 0.981 (越高越好)
  • 假阳性率:默认— 0.536,优化— 0.278 (27.8%) (越低越好)
  • 假阴性率:默认— 0.188,优化— 0.187 (18.7%) (越低越好)

Avora 异常检测方法的灵活性允许每个数据集根据每个用户所需的灵敏度进行定制,使用超参数优化,确保较低的假阳性和假阴性率。

摘要

  • 除了准确性之外,假阳性和假阴性率是评估性能的良好、直观的方法。
  • **并非所有的异常检测器都是相同的:**不同的异常检测器之间的得分可能会有很大的不同,这些异常检测器对测量业务绩效的相同的真实时间序列数据进行操作。
  • 在我们的测试数据集合中,Avora 的异常检测提供了比脸书 Kats 更好的性能,同时具有更低的误报率和漏报率。
  • 通过超参数调整,可以显著改善假阳性和假阴性率,而不会降低准确性。

机器学习评分模型中的异常检测

原文:https://towardsdatascience.com/anomaly-detection-in-a-machine-learning-scoring-model-205a942518e3?source=collection_archive---------23-----------------------

时间序列异常检测的统计技术

掷骰子将是本文所用方法的一个很好的类比。乔纳森·格林纳韦在 Unsplash 上拍摄的照片

在我最近的默认评分数据科学项目中,我希望有一个自动工具可以警告我,特别是在开发阶段,当我的模型预测不连贯时,无论是因为数据处理中出现了问题,还是因为模型需要重新训练。

主要的困难是分数的急剧上升或下降可能是不一致的,也可能是合理的,这意味着分数的变化是观测数据实际和显著变化的结果。

在不知道模型输出分布的情况下,做出这种区分是复杂的,尽管 SHAP 或 LIME 等可解释性工具有助于区分,但这意味着要逐一检查每个预测。

一个在 XGBoost 模型上计算的 SHAP 值的例子,该模型是根据来自“住房信贷违约风险”Kaggle 竞赛的数据训练的。图片作者。

我们将在这里使用的关键原则是中心极限定理,利用它我们将研究从我们的模型预测中导出的正态或伪正态分布。由于这一点,我们将能够用一种完全自动化的方法来推断一个预测序列是一致的还是异常的。

掷骰子的类比

让我们把我们的模型看作一个骰子滚动。我们不知道骰子是否被装载,这意味着我们不知道它的概率分布。

然而,如果我们掷骰子 1000 次并平均结果,我们将得到一个稳定的值,如果我们重复这个过程,它不会改变太多。
例如,下面是我们通过对空载骰子进行 1000 次投掷的 3000 次迭代得到的图表:

图片作者。

这里,即使我们不知道骰子是空载的,我们也知道掷骰子的平均值具有正态分布,其中 68%、95%和 99.7%的值位于平均值的一个、两个和三个标准差内。

如果骰子突然变化并被加载,它将立即可见:

图片作者。

这里,掷骰子的结果是 6:掷出 6 的概率是 30%,其他值是 14%。如果骰子没有变,那么出现这种结果的概率极低,因此我们可以有把握地假设骰子被改变了。

回到模型

我对我的模型所做的是一样的。我取了之前每个月预测的平均分数,看看最新的预测是否与它们一致。

即使一些观察值合理地显著增加或减少了它们的分数,所有分数的平均值应该总体上遵循正态分布。这就是我们可以看到的:

图片作者。

如果一系列预测的平均值在平均值的两个标准差之外,我们可以将其视为异常并仔细分析。

该方法也可应用于月度预测的标准差。

计数异常

如果我们的骰子是 3 的话会怎么样?它会自然地将 1000 个滚动系列的平均值移向 3,但该值不会离平均值太远而被视为异常:

骰子的位置有利于 3。图片作者。

这里,根据掷骰子的平均值看不出异常,但是如果我们在 1000 次掷骰子中数出 3 的次数,就可以看出异常:

图片作者。

同样,三的数量遵循正态分布,装载骰子的结果明显异常。

在模型中,我们可以通过计算观察值的数量来应用这一点,这些观察值的得分有显著的增加或减少。
如果一半的观察值增加了 0.3,而另一半减少了 0.3,那么就预测值的平均值而言,结果是不可见的,但通过这种方法可以看到。

应用于我的模型,我得到了以下图表:

图片作者。

图片作者。

在这里,我保留了一份我的模型,几个月来根本没有训练它。我们可以从直方图和折线图中看到,该模型在 2021 年 6 月之后出现了退化的迹象。以前的数据分析表明,模型只需每年重新训练一次,但这种异常分析表明,这应该在 4 个月后发生。

处理异常情况

在多种原因中,异常可能发生,因为:
-数据处理管道
中存在异常-模型不稳定或必须重新训练
-存在外部因素。例如,如果出现经济危机,大多数观察值的默认分数将会急剧上升,因为它们的健康状况已经下降。反常不一定意味着不连贯。

在我的情况下,数据处理管道还没有最终确定,即使模型的重新训练频率已经被推断出来,我也想在当前数据上监控它。
因此,基于 CLT 的异常检测主要针对前两种情况。

支持物联网的智能电池管理系统中的异常检测

原文:https://towardsdatascience.com/anomaly-detection-in-iot-enabled-smart-battery-management-systems-4ae2419c6074?source=collection_archive---------12-----------------------

了解电子移动世界中数据工程和机器学习的使用

库姆潘电气在un plash上拍摄的照片

我们生活在一个电动移动的世界里。从全球范围来看,电动汽车和两轮驱动汽车的使用正在急剧上升。电动移动设备依靠昂贵的可充电锂离子电池供电。这些电池是对抗化石燃料不良影响(如污染和成本上升)的不可或缺的组成部分。但是锂离子技术也有一些缺点,比如:

  • 温度升高和短路引起的火灾和爆炸
  • 充电过度/不足导致的使用寿命缩短
  • 由于老化,性能随着时间的推移而下降

全球锂离子电池市场规模预计在几年内将翻一番以上——从 2020 年的 440 亿美元到 2025 年的 940 亿美元

目前,与物理电池的采购及其维护相关的成本是采用该技术的一个主要障碍。几家锂离子电池制造公司现在正在开发智能电池管理系统,以帮助监控和跟踪锂离子电池的健康状况。电池管理系统有几个目标:

  1. 预防性分析监控层 —持续监控与火灾、爆炸相关的威胁
  2. 预测分析分析层 —预测故障,防止电池损坏,从而延长电池寿命

最近,我有机会为一家生产电动代步车电池的公司创建了这样一个智能电池管理系统。以下是该解决方案的高级架构:

作者图片 Azure Cloud 上智能电池管理系统的高级架构

  • 每个电池组都配有一个测量电池电压、电流和温度的电子物联网传感器
  • 使用 Wi-Fi 或蓝牙将传感器数据发送到 Azure 云(Azure 事件中枢或 Azure 物联网中枢)
  • 来自 Azure 事件中枢/Azure 物联网中枢的流数据使用 Azure 流分析进行计算
  • Azure Stream Analytics 作业执行异常检测。如果检测到异常,将生成警报并使用 Azure 通知中枢发送给用户

在接下来的部分中,我将向您展示我们如何使用 Azure 提供的基于机器学习的操作符—anomaly detection _ spike andipanomaly detection _ change point来执行异常检测。

AnomalyDetection _ SpikeAndDip 能够检测时间序列事件中的临时异常。

AnomalyDetection_ChangePoint 能够检测时间序列事件流中的持续异常

在这次演示中,我使用了 48V 25Ah 的锂离子电池。以下是电池的技术规格。

图片 bu 作者—电池规格

对于这种电池,以下是推荐的水印。

上限电压— 54.6 V— 任何更高的电压都可能导致爆炸或火灾
下限电压— 39 V — 任何更低的电压都会影响电池的健康

电池管理系统的一个关键功能是监控这些关键值是否在推荐范围内。出于演示的目的,我将集中在高点电压。毕竟谁想要爆炸或火灾。

现在让我们开始实现。假设你已经有一个 Azure 帐户,请按照下面的步骤继续操作:

  • 开云壳https://portal.azure.com/#cloudshell/
  • 调用下面的命令来克隆代码库
Azure> git clone [https://github.com/mkukreja1/blogs](https://github.com/mkukreja1/blogs)
Cloning into ‘blogs’…
remote: Enumerating objects: 149, done.
remote: Counting objects: 100% (149/149), done.
remote: Compressing objects: 100% (90/90), done.
remote: Total 149 (delta 54), reused 135 (delta 40), pack-reused 0
Receiving objects: 100% (149/149), 1.67 MiB | 41.71 MiB/s, done.
Resolving deltas: 100% (54/54), done.
  • 现在让我们创建一个 Azure 事件中心。您可以根据自己的喜好编辑资源组名称位置的名称。
RESOURCEGROUPNAME="training_rg"
LOCATION="eastus"
TOPIC="iotbattery"
EVENTHUB_NAMESPACE="iotbatteryspace"
EVENTHUB_NAME="iotbatteryhub"
EVENT_SUBSCRIPTION="iotbatteryevent"az provider register --namespace Microsoft.EventGrid
az provider show --namespace Microsoft.EventGrid --query "registrationState"
az eventgrid topic create --name $TOPIC -l $LOCATION -g $RESOURCEGROUPNAMEaz eventhubs namespace create --name $EVENTHUB_NAMESPACE --resource-group $RESOURCEGROUPNAME
az eventhubs eventhub create --name $EVENTHUB_NAME --namespace-name $EVENTHUB_NAMESPACE --resource-group $RESOURCEGROUPNAME
  • 要验证新创建的 Azure Event Hub,请导航到主页 > 所有资源 > iotbatteryspace。在左侧菜单中点击活动中心。现在点击新创建的事件中心 iotbatteryhub。

作者图片—新创建的 Azure 事件中心

  • 点击过程数据,然后点击浏览。你现在应该在一个查询编辑器窗口中。我们需要创建一个共享访问策略来预览 Azure 事件中心中的数据。点击创建

按作者分类的图像-查询编辑器窗口

  • 此时,我们已经准备好向新创建的 Azure event hub 发送一些事件。由于您没有实际的物联网传感器,我们可以使用下面的命令来模拟发送到 Azure 事件中心的五节电池的 100 个事件。在云外壳中调用下面的命令。
RESOURCEGROUPNAME="training_rg"
TOPIC="iotbattery"
EVENTHUB_NAMESPACE="iotbatteryspace"
EVENTHUB_NAME="iotbatteryhub"
EVENTHUB_CONN_STR=$(az eventhubs namespace authorization-rule keys list --resource-group $RESOURCEGROUPNAME --namespace-name $EVENTHUB_NAMESPACE --name RootManageSharedAccessKey --query "primaryConnectionString" --output tsv)
for VARIABLE in {1..100}
do
 echo "Sending event $VARIABLE"
 python ~/blogs/eventhubs/iot/send_battery_events_normal_reading.py --EVENTHUB_NAME $EVENTHUB_NAME --EVENTHUB_CONN_STR $EVENTHUB_CONN_STR
done
  • 我们现在可以验证事件数据是否出现在 Azure 事件中心。回到 Azure 门户查询编辑器。在输入预览页签中,按刷新。现在,您应该在查询窗口中看到新发送的事件。

按作者分类的图像-事件数据

  • 检查从物联网事件收到的读数。在查询编辑器中调用下面的 SQL。
SELECT data.Timestamp, data."Battery Pack", data."Terminal Voltage", data."Charging Current", data."Discharging Current",data."SoC", data."Charge Capacity", data."Charging Power", data."Discharging Power", data."Cycle Count"
FROM [iotbatteryhub]

作者图片-活动阅读

  • 我们现在准备实施异常检测。记住,我们要监控电池电压的尖峰电压,因为它们可能是危险的。调用下面的 SQL 来检查收到了多少个峰值。
WITH batteryAnomalyDetection AS
(
    SELECT
        data.Timestamp AS time,
        CAST(data."Terminal Voltage" AS float) AS Voltage,
        AnomalyDetection_SpikeAndDip(CAST(data."Terminal Voltage" AS   float), 70, 90, 'spikes')
        OVER(LIMIT DURATION(second, 120)) AS Spikes
    FROM 
    [iotbatteryhub]
)
SELECT
    time,
    Voltage,
    CAST(GetRecordPropertyValue(Spikes, 'Score') AS float) AS
    SpikeAndDipScore,
    CAST(GetRecordPropertyValue(Spikes, 'IsAnomaly') AS bigint) AS
    IsSpikeAnomaly
INTO battery
FROM batteryAnomalyDetection
WHERE CAST(GetRecordPropertyValue(Spikes, 'IsAnomaly') AS bigint)=1

作者图片—70%置信度下模型测试的电压峰值

在上面的 SQL 中,我们使用 70%的置信度检查了异常。anomaly detection _ spikeandip(CAST(data。“端电压”为浮动), 70 ,90,“尖峰”)

上图向我们展示了机器学习操作员将 17 个事件标记为尖峰。

  • 让我们重新测试这个模型,但是这次要有 90%的置信度。
WITH batteryAnomalyDetection AS
(
    SELECT
        data.Timestamp AS time,
        CAST(data."Terminal Voltage" AS float) AS Voltage,
        AnomalyDetection_SpikeAndDip(CAST(data."Terminal Voltage" AS   float), 90, 95, 'spikes')
        OVER(LIMIT DURATION(second, 120)) AS Spikes
    FROM 
    [iotbatteryhub]
)
SELECT
    time,
    Voltage,
    CAST(GetRecordPropertyValue(Spikes, 'Score') AS float) AS
    SpikeAndDipScore,
    CAST(GetRecordPropertyValue(Spikes, 'IsAnomaly') AS bigint) AS
    IsSpikeAnomaly
INTO battery
FROM batteryAnomalyDetection
WHERE CAST(GetRecordPropertyValue(Spikes, 'IsAnomaly') AS bigint)=1

作者图片—90%置信度下模型测试的电压峰值

上图向我们展示了机器学习操作员将 15 个事件标记为尖峰。

对于预防性分析,我们可以实现为每个接收到的尖峰发送警报的代码。使用 Azure 函数可以轻松做到这一点。

  • 现在让我们做一些更有趣的事情。我们现在将此转换为**流分析工作。**将该查询转换为作业的好处是可以无人值守地收集数据。收集的数据将有助于我们进行进一步的分析,这些分析可用于仪表板和决策制定。
WITH batteryAnomalyDetection AS
(
    SELECT
        data.Timestamp AS time,
        CAST(data."Terminal Voltage" AS float) AS Voltage,
        AnomalyDetection_SpikeAndDip(CAST(data."Terminal Voltage" AS   float), 90, 95, 'spikes')
        OVER(LIMIT DURATION(second, 120)) AS Spikes
    FROM 
    [iotbatteryhub]
)
SELECT
    time,
    Voltage,
    CAST(GetRecordPropertyValue(Spikes, 'Score') AS float) AS
    SpikeAndDipScore,
    CAST(GetRecordPropertyValue(Spikes, 'IsAnomaly') AS bigint) AS
    IsSpikeAnomaly
INTO battery
FROM batteryAnomalyDetection

点击创建流分析作业。填写如下详细信息,并点击创建

按作者分类的图像-新的流分析作业参数

  • 你现在会在一个类似这样的窗口中。

按作者分类的图像-新的流分析作业

我们需要一个用于数据收集的输出。点击 +添加,选择 Azure Blob 存储/ADLS Gen2。

按作者分类的图像-流分析作业输出

  • 既然已经创建了作业的输出,我们就可以开始作业了。点击启动

按作者分类的图像-启动流分析作业

  • 收到“流式作业成功启动”消息后,向事件中心发送更多事件。在云外壳中调用下面的命令。
RESOURCEGROUPNAME="training_rg"
TOPIC="iotbattery"
EVENTHUB_NAMESPACE="iotbatteryspace"
EVENTHUB_NAME="iotbatteryhub"
EVENTHUB_CONN_STR=$(az eventhubs namespace authorization-rule keys list --resource-group $RESOURCEGROUPNAME --namespace-name $EVENTHUB_NAMESPACE --name RootManageSharedAccessKey --query "primaryConnectionString" --output tsv)
for VARIABLE in {1..100}
do
 echo "Sending event $VARIABLE"
 python ~/blogs/eventhubs/iot/send_battery_events_normal_reading.py --EVENTHUB_NAME $EVENTHUB_NAME --EVENTHUB_CONN_STR $EVENTHUB_CONN_STR
done
  • 如果一切正常,流分析作业将以 JSON 格式将事件输出到下面的存储中。导航到主页 > 所有资源 > 培训工作室 > 电池

按作者分类的图像—存储上的事件数据

  • 但是我们如何对事件数据进行分析呢?为此,我们可以使用 Azure Synapse。在 cloud shell 中调用下面的命令来创建一个新的 Synape 工作区。
STORAGEACCOUNTNAME="traininglakehouse"
BATTERYDATALAYER="battery"
SYSNAPSEWORKSPACENAME="batterytracking"
RESOURCEGROUPNAME="training_rg"
LOCATION="eastus"
SQLUSER="sqladminuser"
SQLPASSWORD="*********"az synapse workspace create \
  --name $SYSNAPSEWORKSPACENAME \
  --resource-group $RESOURCEGROUPNAME \
  --storage-account $STORAGEACCOUNTNAME \
  --file-system $BATTERYDATALAYER \
  --sql-admin-login-user $SQLUSER \
  --sql-admin-login-password $SQLPASSWORD \
  --location $LOCATIONaz synapse workspace firewall-rule create --name allowAll --workspace-name $SYSNAPSEWORKSPACENAME --resource-group $RESOURCEGROUPNAME --start-ip-address 0.0.0.0 --end-ip-address 255.255.255.255
  • 现在导航到主页 > 所有资源 > 电池追踪,点击打开 Synapse 工作室部分的打开

作者图片—打开 Synapse studio

  • 在 Synapse workspace 窗口中,使用左侧窗格中的菜单点击数据

按作者分类的图像—创建新的 SQL 数据库

输入如下详细信息,点击创建

按作者排序的图像—在无服务器池中创建新的 SQL 数据库

  • 创建数据库后,打开一个新的 SQL 脚本窗口。

按作者排序的图像—打开一个新的 SQL 脚本窗口

  • 将下面的 SQL 粘贴到新打开的 SQL 窗口中。该 SQL 将创建电池事件数据的视图。
CREATE view battery
AS
SELECT JSON_VALUE(doc, '$.time') AS time, JSON_VALUE(doc, '$.BatteryPack') AS BatteryPack, JSON_VALUE(doc, '$.Voltage') AS Voltage, JSON_VALUE(doc, '$.SpikeAndDipScore') AS SpikeAndDipScore, JSON_VALUE(doc, '$.IsSpikeAnomaly') AS IsSpikeAnomaly
FROM openrowset(
   BULK 'https://traininglakehouse.blob.core.windows.net/battery/',
   FORMAT= 'csv',
   FIELDTERMINATOR='0x0b',
   FIELDQUOTE= '0x0b'
) WITH(doc nvarchar(max)) as row
GO
SELECT CONVERT(TIME,DATEADD(s,cast(time as int),'19700101 0:00:00')) as time, BatteryPack, Voltage, IsSpikeAnomaly FROM battery
GO
  • 如您所见,我们现在可以在事件数据流入存储时对其进行虚拟化。下面的图表向我们展示了一段时间内事件的峰值。该信息对于预测分析至关重要。

作者提供的图片—虚拟化电池事件

我希望这篇文章是有帮助的。这个主题在由 Datafence Cloud Academy 提供的 Azure 数据分析课程中有更详细的介绍。课程是周末自己在网上教的。

制造业中的异常检测,第 1 部分:简介

原文:https://towardsdatascience.com/anomaly-detection-in-manufacturing-part-1-an-introduction-8c29f70fc68b?source=collection_archive---------3-----------------------

行业笔记,用 PYTHON 制造数据科学

在制造业工作?用数据科学和机器学习推动您的业务

安特·罗泽茨基在 Unsplash 上的照片

一个关键电机出现故障。数百万美元的收入损失。最糟糕的是,这件事发生在我的眼皮底下。

事实上,失败并不归咎于任何一个人——这是一个系统故障。但是在这个过程中还是有遗漏的“信号”,就像几周前有“峰值”的温度探测器。不幸的是,我没有配备识别这些问题的工具。大量的数据不断地被收集,但要找出一个偏差就像大海捞针一样。

您可能也有过类似的经历,设备故障给企业带来了巨大的损失,无论是在金钱方面还是在人力方面。状态监控是测量机器参数的过程,如温度、振动、压力等。—以便检测和预防故障。然而,过去实施的状态监控无法管理当今世界的海量数据。

可用于状态监控的几个参数。(图片由作者提供)

为了找到这些“大海捞针”,并提高生产率,传统的状态监控必须与数据科学和机器学习相结合。

幸运的是,数据的可用性以及从理论到应用的清晰界限,为在工业环境中使用数据科学和机器学习技术提供了一个令人信服的案例。[1]麦肯锡的一项研究估计,流程制造商适当使用数据驱动技术“通常可以减少 30%到 50%的机器停机时间,延长 20%到 40%的机器寿命”。

最终,正是在这里,在传统工业、数据科学和机器学习的交汇处,我们释放出难以置信的价值。

这个由三部分组成的系列将探索数据科学和机器学习在制造业中的应用。特别是,我们将学习在金属加工过程中,使用变分自动编码器(VAE)检测异常。尽管这种应用是特定于制造的,但是只要异常检测有用,就可以使用这些原理。

在第 1 部分(本文),我们将回顾什么是异常检测。我们还将学习加州大学伯克利分校的铣削数据集,并做一些探索性的数据分析——这是重要的第一步。

在第 2 部分中,我们将介绍变分自动编码器的理论。然后,我们将使用 TensorFlow 构建和训练 VAEs 所有代码都已提供,因此您可以轻松地跟随。

最后,在第 3 部分中,我们将使用经过训练的 VAEs 进行异常检测。你会明白潜在空间是如何用于异常检测的。此外,我们将制作一些独特的数据可视化,以便更好地理解结果。

在本系列结束时,我希望您对异常检测以及如何在生产环境中使用它有一个新的认识。也许您会受到启发,在这个迷人的领域中锻炼您的数据科学技能?

让我们从理解什么是异常检测开始,看看自动编码器如何用于异常检测。

异常检测和自动编码器

道格拉斯·霍金斯给出了异常现象的经典定义:“一个[异常现象]是一个与其他观测结果相差如此之大的观测结果,以至于让人怀疑它是由不同的机制产生的。”[2]有时,异常被清楚地识别出来,数据科学家可以使用直接的方法将它们挑选出来。然而在现实中,数据中的噪声使得异常检测变得困难。区分噪声和异常成为主要挑战,如下所示。

正常数据、噪声和异常之间的连续体。挑战在于区分数据中的噪声和异常。(图片由作者提供,灵感来自异常值分析中的 Charu C. Aggarwal)

有许多方法可以执行异常检测。我强烈推荐 Aggarwal 的《离群值分析》,这本书有很好的概述。

异常检测的一种方法是使用自动编码器。如下图所示,自动编码器学习重建它们的输入。然而,重建永远不会是完美的。将数据输入与自动编码器所接受的训练非常不同的自动编码器会产生较大的重建误差。馈送相似的数据将产生较低的重建误差。

自动编码器学习重构其输入。这里,一个简单的自动编码器有一个输入层、一个输出层和一个隐藏层。隐藏单元通常被称为编码,或潜在变量。隐藏单元在潜在空间。(图片由作者提供)

重建误差的大小可以作为数据异常程度的代表。然后可以设置阈值,由此产生高于阈值的重建误差的数据被认为是异常。这被称为输入空间异常检测。

本质上,自动编码器的能力在于它能够以自我监督的方式学习。Yann Lecun 在他的图灵奖演讲中描述了自我监督学习的力量:自我监督学习允许模型“在不为特定任务训练的情况下了解世界。”这使得大量数据可以用于模型的训练,这些数据在监督学习技术中是不可用的。

自我监督学习的能力使其在制造和工业环境中的使用具有吸引力,在这些环境中,许多数据没有被正确标记,和/或标记成本太高。使用自动编码器进行异常检测是自我监督学习的一个实例。

金属加工数据集简介

我们将进一步探索自我监督学习、异常检测和自动编码器的概念,因为我们构建了一个变化的自动编码器来检测金属加工过程中工具的异常。

我们将使用的金属加工数据集或铣削数据集来自加州大学伯克利分校。这些数据存放在美国宇航局预测卓越中心的网页上,可以免费获取。在接下来的章节中,我们将回顾什么是铣削,然后探究数据。

什么是铣削?

运行中的铣削工具。(照片由丹尼尔·史密斯在 Unsplash 上拍摄)

在铣削中,旋转刀具,如上图所示,在沿着工件移动时去除材料。大多数情况下,铣削是在金属上进行的——这是金属加工。

下图演示了端面铣削程序。刀具在旋转的同时向前推进。当刀具旋转时,刀具将“咬入”金属并将其移除。

铣削刀具上有几个刀具刀片。当刀具旋转并被向前推动时,刀片切入金属。(图片由作者修改,公共领域)

随着时间的推移,刀具刀片会磨损。具体来说,刀具的侧面磨损,如下所示。在 UC Berkeley 铣削数据集中,侧面磨损( VB )是从一次切削到另一次切削测量的。该 VB 值将用于标记目的。

刀具刀片的侧面磨损(透视图和正视图)。 VB 是侧面磨损的量度。(图片由作者提供)

数据探索

注意:我不会涵盖数据探索的所有代码——跟随 Colab 笔记本查看全部内容。

处理任何新的数据科学问题时,数据探索都是重要的一步。因此,在开始任何类型的模型构建之前,我们需要熟悉加州大学伯克利分校的铣削数据集。

从哪里开始?第一步是理解数据的结构。数据是如何存储的?在数据库里?在一个数组里?元数据(如标签和时间戳)在哪里?

数据结构

加州大学伯克利分校铣削数据集包含在一个结构化的 MATLAB 数组中。我们可以使用 scipy.io 模块和 loadmat 函数加载.mat文件。

# load the data from the matlab file
m = sio.loadmat(folder_raw_data / 'mill.mat',struct_as_record=True)

数据存储在字典中。只有'mill'键包含有用的信息。

# store the 'mill' data in a separate np array
data = m['mill']

我们可以看到data阵列是由什么组成的。

# store the field names in the data np array in a tuple, l
l = data.dtype.names
print('List of the field names:\n', l)>>> List of the field names:
>>> ('case', 'run', 'VB', 'time', 'DOC', 'feed', 'material', 'smcAC', 'smcDC', 'vib_table', 'vib_spindle', 'AE_table', 'AE_spindle')

元数据和标签

加州大学伯克利分校铣削数据集包含的文档强调了重要的元数据。该数据集由 16 个铣削刀具切削金属的案例组成。创建数据时使用了六个切割参数:

  • 金属类型(铸铁或钢,在数据集中分别标记为 1 或 2)
  • 切割深度(0.75 毫米或 1.5 毫米)
  • 进给速度(0.25 毫米/转或 0.5 毫米/转)

16 种情况中的每一种都是切削参数的组合(例如,情况一的切削深度为 1.5 毫米,进给速度为 0.5 毫米/转,并且是在铸铁上进行的)。

案例由从新工具到退化或磨损工具的单个切割组成。在所有 16 个案例中有 167 个切口(在文档中称为“运行”)。许多切口都伴随着一定程度的侧面磨损 (VB )。我们稍后将使用它来标记切口是健康的、退化的还是磨损的。

最后,在每次切割期间收集六个信号:

  • 主轴和工作台发出的声发射 (AE)信号。
  • 来自主轴和工作台的振动。
  • 主轴电机的交流/DC 电流。

信号以 250 Hz 采集,每个切割有 9000 个采样点,总信号长度为 36 秒。

我们将从 numpy 数组中提取元数据和标签,并将其存储为 pandas 数据帧——我们将此数据帧称为df_labels,因为它包含我们感兴趣的标签信息。我们是这样创建数据帧的:

在上表中,从df_labels.head()可以看出,并非所有切口都标有侧面磨损( VB )值。稍后,我们将设置工具健康状况的类别—健康、降级或磨损(故障)。对于没有侧面磨损( VB )值的切削,我们可以基于具有磨损值的附近切削合理地估计刀具健康类别。

数据可视化

可视化一个新的数据集是一种很好的方式来掌握正在发生的事情,并检测任何问题。我也喜欢数据可视化,所以我们将使用 seaborn 和 Matplotlib 创建一个漂亮的图形。

在这个数据集中只有 167 次切割,这不是一个很大的数量。我们可以目视检查每个切口以发现异常。幸运的是,我已经为你做到了…下面是一个亮点。

首先,我们来看一个相当“正常”的剪切——167 号剪切。

但是,如果您查看所有切割,您会发现切割 18 和 95(索引 17 和 94)很奇怪,在我们开始构建异常检测模型之前,需要丢弃它们。

这是第 18 个镜头:

这是第 95 次切割:

最后,我们将创建一个图表,清晰地显示所有六个信号(声发射、振动和电流)。

切 146 号。(图片由作者提供)

结论

数据科学和机器学习非常适合制造业环境。为此,我们回顾了使用自动编码器进行异常检测的概念。这种自我监督的学习方法在制造环境中有助于检测和预防机器故障。

在这篇文章中,我们还解释了什么是金属加工——在铣削的背景下——并探索了加州大学伯克利分校铣削数据集。在第 2 部分中,我们将构建一个变化的自动编码器,并根据铣削数据对其进行训练。

参考

[1]经济学家。(2020).企业发现人工智能很难被采用。《经济学家》,ISSN 0013–0613。

[2]霍金斯博士(1980 年)。 离群值识别 (第 11 卷)。伦敦:查普曼与霍尔。

本文原载于tvhahn.com。此外,这项工作是对发表在 IJHM 研究的补充。官方的 GitHub 回购是 这里的

除非另有说明,本帖及其内容由作者以CC BY-SA 4.0授权。

制造业中的异常检测,第 2 部分:构建可变自动编码器

原文:https://towardsdatascience.com/anomaly-detection-in-manufacturing-part-2-building-a-variational-autoencoder-248abce07349?source=collection_archive---------10-----------------------

用 PYTHON 制造数据科学

机器故障?使用可变自动编码器来检测和防止它们

照片由丹尼尔·史密斯在 Unsplash 上拍摄

在之前的文章(本系列的第 1 部分)中,我们讨论了如何使用自动编码器进行异常检测。我们还探索了加州大学伯克利分校铣削数据集。接下来,我们将使用自动编码器的变体——变分自动编码器(VAE)——对铣削数据集进行异常检测。

在这篇文章中,我们将看到 VAE 与传统自动编码器的相似和不同之处。然后,我们将实现一个 VAE,并在铣削数据上训练它。在下一篇第 3 部分的中,我们将检查 VAE 的异常检测性能。

变分自动编码器

variable auto encoder 于 2013 年推出,目前广泛用于机器学习应用。[1]VAE 不同于传统的自动编码器,因为 VAE 既是概率性的又是生成性的。那是什么意思?VAE 生成的输出部分是随机的(即使在训练之后),也可以生成与被训练数据相似的新数据。

VAE 在线有很好的解释——我会带你去 Alfredo Canziani 的深度学习课程(下面的视频来自 YouTube)。不管怎样,我试图解释一下。

在高层次上,VAE 具有与传统自动编码器相似的结构。然而,编码器学习不同的编码;即,VAE 学习均值编码、和标准差编码、。然后,VAE 从高斯分布中随机采样,具有由编码器产生的相同的平均值和标准偏差,以生成潜在变量 z 。这些潜在变量被“解码”以重建输入。

下图展示了如何使用 VAE 重建信号。

一种变化的自动编码器架构(上图),以及一个通过 VAE 的数据样本示例(下图)。数据在编码器中被压缩以产生平均值和标准偏差编码。然后,通过添加高斯噪声,从均值和标准差编码中创建编码。解码器使用编码(或潜在变量)来重建输入。(图片由作者提供,灵感来自奥雷连·杰龙)

在训练期间,VAE 努力最小化其重建损失(在我们的情况下,我们使用二进制交叉熵),同时,使用潜在损失强制高斯结构。这种结构是通过 Kullback-Leibler (KL)散度实现的,在最初的 VAE 论文中对损失进行了详细的推导。[1]潜在损失如下:*

其中 K 是潜在变量的数量,而 β 是由 Higgens 等人[2]引入的可调超参数

VAE 学习嵌入在编码中的因子,这些因子可用于生成新数据。作为这些因素的一个例子,VAE 可以被训练来识别图像中的形状。一个因素可能编码了形状有多尖的信息,而另一个因素可能看它有多圆。然而,在 VAE 中,这些因素经常在编码中纠缠在一起(潜在变量)。

将超参数β(β)调整为大于 1 的值,可以使因子“解开”,使得每个编码一次仅代表一个因子。因此,可以获得模型的更大的可解释性。具有可调β的 VAE 有时被称为解纠缠-变分-自动编码器,或者简称为 β -VAE。为了简单起见,我们仍然称 β -VAE 为 VAE。

数据准备

在继续之前,我们需要准备数据。最终,我们将使用 VAE 来检测“异常”工具状况,这对应于工具磨损或失效的时间。但是首先我们需要标记数据。

如上一篇文章所示,每次切削都有一个相关的侧面磨损量, VB ,在切削结束时测量。我们将根据刀具的磨损程度将每个切削标记为健康、退化或失败——这些是刀具健康类别。以下是模式:

我创建了一个数据准备类,它获取原始的 Matlab 文件,一个带标签的 CSV(每个切口都标有相关的侧面磨损),并输出训练/验证/和测试数据。然而,我想在课上强调一个重要的功能。也就是create_tensor函数。

注意:为了简洁,我不会覆盖所有的代码——按照 Colab 笔记本中的步骤,训练一些模型。

create_tensor函数获取一个单独的片段,将其分解成块,并放入一个数组中。它使用一个固定大小的窗口(window_size变量)将切割信号分割成块,然后沿着信号“滑动”窗口。窗口“滑动”预定的量,由stride变量设置。

我们将采用 165 个切割中的每一个(记住,原始 167 个切割中的两个被破坏),并应用 64 的窗口大小和 64 的步幅(窗口之间没有重叠)。

我目视检查了每个切割,并选择了“稳定”切割区域出现的时间,通常是信号开始收集后的五秒钟左右,信号收集结束前的几秒钟。该信息存储在“labels _ with _ tool _ class . CSV”文件中。

使用data_prep.py (参见 github repo )和一些 Python 魔术,我们可以创建训练/验证/测试数据集。脚本看起来是这样的:

数据的最终分布如下所示。注意数据有多不平衡(即相对较少的“失败”样本)?这是制造/工业数据中的常见问题,也是使用自监督方法的另一个原因。

对于异常检测,通常只对“正常”数据训练自动编码器。我们将做同样的事情,在健康数据(0 类)上训练我们的 VAE。但是,检查异常检测的性能将使用所有数据来完成。换句话说,我们将在“瘦”数据集上训练我们的 VAE,但在“完整”数据集上测试。

构建模型

我们现在明白了什么是变分自动编码器,以及数据是如何准备的。是时候建造了!

我们的 VAEs 将由卷积层、批量标准化层和最大池层组成。下图显示了我们的一个 VAE 模型可能的样子。

VAE 使用的示例模型架构。编码器的输入是铣削数据样本,对于(64,6)的输入形状,窗口大小为 64。有 3 个卷积层,滤波器大小为 17,编码大小为 18。(图片由作者提供)

我不会讨论这个模型的所有细节。但是,这里有一些要点:

  • 我使用时间卷积网络作为卷积层的基础。实现来自 Philippe Remy——感谢 Philippe!你可以在这里找到他的 github 回购。
  • Aurélien Geron 的书“用 Scikit-Learn、Keras 和 TensorFlow 实践机器学习”很棒。特别是,他关于 VAEs 的部分非常有用,我在这里使用了他的一些方法。有一本 Jupyter 的笔记本,摘自他的书中关于他的 github 的章节,非常有用。谢谢奥雷连恩![3]
  • 正如 Geron 所建议的,我使用了四舍五入的精确度来衡量模型在训练过程中的表现。

模型函数大概是这样的:

训练模型

是时候开始训练一些模特了。为了选择超参数,我们将使用随机搜索。

为什么要随机搜索?嗯,它实现起来相当简单,而且与网格搜索相比,已经显示出产生良好的结果。[4] Scikit-learn 有一些实现随机搜索的好方法——我们将使用[ParameterSampler](https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.ParameterSampler.html)方法。

我们将训练一堆不同的阀门,都有不同的参数。在每个 VAE 都经过训练(训练以最小化重建损失)并保存模型后,我们将浏览 VAE 模型,并查看它在异常检测中的表现(我们将在第 3 部分中讨论)。下面是随机搜索训练过程的示意图:

随机搜索训练过程有三个步骤。首先,随机选择超参数。其次,用这些参数训练 VAE。第三,检查经过训练的 VAE 的异常检测性能。(图片由作者提供)

在实践中,当我运行这个实验时,我在 Google Colab 上训练了大约 1000 个 VAE 模型(免费 GPU 耶!).在训练完所有 1000 个模型后,我将它们转移到我的本地计算机上,使用一个功能不太强大的 GPU,然后检查这些模型的异常检测性能。Colab GPUs 的持续使用是有限的,因此以这种方式最大限度地利用它们上的 GPU 是有意义的。

您可以在 Colab 笔记本中查看完整的训练循环。试着训练一些模特!

结论

在这篇文章中,我们了解了 VAE 与传统自动编码器的相似和不同之处。然后,我们准备铣削数据,为超参数选择创建随机搜索,并开始训练模型。

在的下一篇文章,第 3 部分中,我们将评估经过训练的 vae,看看它们是如何用于异常检测的。我们将使用精度-召回曲线来询问模型性能。最后,我们将创建一些漂亮的图形来可视化结果(我的最爱!).

参考

[1]金马博士和韦林博士(2013 年)。自动编码变分贝叶斯。 arXiv 预印本 arXiv:1312.6114

[2]希金斯、伊琳娜等人(2016)。" beta-vae:用约束变分框架学习基本视觉概念。”

[3]盖伦,奥雷连恩。(2019).使用 Scikit-Learn、Keras 和 TensorFlow 进行机器实践学习:构建智能系统的概念、工具和技术。奥莱利传媒。

[4]j .伯格斯特拉和 y .本吉奥(2012 年)。超参数优化的随机搜索。 机器学习研究杂志13 (2)。

本文原载于 tvhahn.comhttps://www.tvhahn.com/posts/building-vae/。此外,这项工作是对发表在 IJHM* 研究的补充。官方的 GitHub 回购是 这里的 ***

除非另有说明,本帖及其内容由作者授权于CC BY-SA 4.0**

**** 更正:在最初的文章中,我在潜在损失函数中有一个错误(尽管代码很好)。我已经对它进行了修改,以匹配 Geron 中的内容。此外,记谱法与阿尔弗雷多·坎齐亚尼不同。请参考他的视频——非常好!***

制造业中的异常检测,第 3 部分:可视化结果

原文:https://towardsdatascience.com/anomaly-detection-in-manufacturing-part-3-visualize-the-results-a2afb5f61d2f?source=collection_archive---------12-----------------------

用 PYTHON 制造数据科学

创建漂亮的数据可视化,并查看异常检测模型的执行情况

照片由丹尼尔·史密斯在 Unsplash 上拍摄

在现代制造环境中,数据的可用性是巨大的,但是常常缺乏利用数据的能力。幸运的是,数据科学和机器学习的工具可以提供帮助,反过来,可以释放出难以置信的价值。在本系列中,我们一直在探索这些工具在金属加工过程中检测故障的应用。

在之前的文章中,我们构建并训练了可变自动编码器来重构铣床信号。下图中的步骤 1 和 2 显示了这一点。在本帖中,我们将通过检查一个经过训练的 VAE 模型的异常检测性能来演示随机搜索循环的最后一步(步骤 3)——我们将看看我们的异常检测模型是否能够真正检测到磨损的工具。

使用重建误差(输入空间异常检测)和测量样本之间的 KL-散度差(潜在空间异常检测)来执行异常检测。我们将看到这是如何做到的,并通过一些漂亮的数据可视化来深入研究结果。最后,我将建议一些潜在的领域进行进一步的探索。

随机搜索训练过程有三个步骤。首先,随机选择超参数。其次,用这些参数训练 VAE。第三,检查经过训练的 VAE 的异常检测性能。在这篇文章中,我们将着眼于第三步。(图片由作者提供)

背景

输入空间异常检测

我们的变型自动编码器已经过“健康”刀具磨损数据的培训。因此,如果我们向训练过的 VAEs 提供不健康的数据,或者仅仅是不正常的数据,我们将产生很大的重建误差。可以对该重建误差设置阈值,由此产生高于阈值的重建误差的数据被认为是异常。这是输入空间异常检测。

注意:为了简洁,我不会在这篇文章中涵盖所有代码——打开 Colab 笔记本进行交互体验,并查看所有代码。

我们将使用均方误差(MSE)来测量重建误差。因为重构是对所有六个信号的重构,所以我们可以计算每个单独信号(mse函数)以及所有六个组合信号(mse_total函数)的 MSE。这两个函数如下所示:

重建值(recon)是通过将窗口切割信号(也称为子切割)输入到训练过的 VAE 中产生的,如下:recon = model.predict(X, batch_size=64).

重构概率是输入空间异常检测的另一种方法。).安和赵在他们 2015 年的论文中介绍了这种方法。[1]

我对重建概率方法不太熟悉,但是詹姆斯·麦卡弗里在他的博客上有一个很好的解释(并且用 PyTorch 实现)。他说:“重建概率异常检测的想法是计算第二个概率分布,然后用它来计算输入项来自该分布的可能性。具有低重建概率的数据项不可能来自该分布,因此在某种程度上是异常的。”

我们不会使用重建概率来进行异常检测,但实现起来会很有趣。也许你可以试一试?

潜在空间异常检测

异常检测也可以使用潜在空间中的均值和标准差编码来执行,这就是我们将要做的。下面是一般的方法:

  • 使用 KL-divergence,测量数据样本之间熵的相对差异。可以在这个相对差异上设置阈值,指示数据样本何时异常。

Adam Lineberry 在他的博客上有一个用 PyTorch 实现的 KL-divergence 异常检测的很好的例子。下面是我们将使用的 KL-divergence 函数(通过 Keras 和 TensorFlow 实现):

其中mu是平均值(),而log_var是方差的对数(log σ)。方差的对数用于训练 VAE,因为它比方差更稳定。

为了生成 KL-散度分数,我们使用以下函数:

评估指标

在我们计算了重建误差或 KL-divergence 分数之后,我们准备设置一个决策阈值。任何高于阈值的值都将是异常的(可能是磨损的工具),而任何低于阈值的值都将是正常的(健康的工具)。

为了全面评估一个模型的性能,我们必须考虑一系列潜在的决策阈值。两种常见的方法是接收器工作特性 (ROC)和精确召回曲线。ROC 曲线描绘了真阳性率对假阳性率。精确度-召回曲线,顾名思义,描绘了精确度与召回率的关系。测量曲线下的面积提供了一个比较不同模型的好方法。

我们将使用精确召回曲线下面积(PR-AUC)来评估模型性能。与 ROC-AUC 相比,PR-AUC 在不平衡数据上表现良好。[2,3]下图解释了什么是精确度和召回率,以及精确度-召回率曲线是如何构建的。

通过改变异常检测模型中的决策阈值来创建精确召回曲线。(图片由作者提供)

最终,模型性能的评估及其决策阈值的设置是特定于应用的。例如,制造商可能将预防工具故障置于频繁更换工具之上。因此,他们可以设置低阈值来检测更多的工具故障(更高的召回率),但代价是有更多的假阳性(更低的精度)。

分析最佳模型

现在,一些“背景”信息已经涵盖,我们可以开始分析训练好的 VAE 模型。您必须计算每个模型的性能指标(PR-AUC 分数),看看哪一个是最好的。但是为了这个帖子,我已经训练了一堆模型,选出了最上面的一个(基于 PR-AUC 评分)。

以下是顶配车型的参数:

计算 PR-AUC 分数

让我们看看顶级模型在训练/验证/测试集上的 PR-AUC 分数。但是首先,我们需要加载数据和包。

get_results函数接受一个模型,并给出该模型在训练、验证和测试集上的性能。它还返回给定迭代次数的精度、召回、真阳性和假阳性(称为grid_iterations)。因为 VAE 的输出是部分随机的(random ),所以您也可以运行一些搜索(search_iterations),然后取所有搜索的平均值。

最后,下面是我们得出的结果:

看上表:潜在空间异常检测优于输入空间异常检测。这并不奇怪。包含在潜在空间中的信息更具表现力,因此更有可能识别切割之间的差异。

精确回忆曲线

我们希望将模型的性能可视化。让我们为潜在空间中的异常检测模型绘制精确召回曲线和 ROC 曲线。

上图中的虚线表示“无技能模型”在进行异常检测时将获得的结果,也就是说,如果模型为数据集中的每个子部分随机分配一个类别(正常或异常)。该随机模型由 ROC 图中的斜线和 PR-AUC 图中的水平线表示,设定精度为 0.073(测试集中失败子切割的百分比)。

比较精确-召回曲线和 ROC 曲线:ROC 曲线给出了对模型性能更乐观的看法;这是 0.883 的曲线下面积。然而,精确召回曲线下面积没有那么高,值为 0.450。

为什么曲线下面积值不同?这是因为我们的数据严重失衡。这就是为什么您想要使用 PR-AUC 而不是 ROC-AUC 指标的确切原因。PR-AUC 将在处理不平衡数据时提供模型性能的更真实视图。

潜在空间的小提琴情节

小提琴图是一种可视化决策边界和查看样本错误分类的有效方法。你说,什么是小提琴的情节?我们来造一个吧!

这是 will 将用来创建情节的violin_plot函数。它需要经过训练的编码器、子切割(X)、标签(y)和一个示例阈值。

我们需要加载编码器。

…还有剧情!

不错吧。您可以在 violin 图中看到不同的阈值会如何错误分类不同数量的数据点。想象红色虚线,代表决策阈值,在图上向左或向右移动。这是异常检测的固有问题——从异常中分离噪声。

比较不同切割参数的结果

铣削数据集中有六个切削参数:

  • 金属类型(铸铁或钢)
  • 切割深度(0.75 毫米或 1.5 毫米)
  • 进给速度(0.25 毫米/转或 0.5 毫米/转)

我们可以看到我们选择的异常检测模型是否在一组参数上比另一组参数更好地检测失败的工具。我们将通过一次向模型中输入一种类型的参数并观察结果来做到这一点。例如,我们将进给用铸铁制成的切口。那我们就转向钢铁。等等。等等。

我已经跳过了一个好的代码块(见 Jupyter 笔记本),因为它是重复的。但是,一旦我们为每个独特的切削参数创建了结果数据帧,我们就可以将它们组合成一个简洁的条形图。

下面是组合每组结果的代码:

现在我们可以制作漂亮的条形图了。

显然,这个“最佳”模型发现一些切削参数比其他参数更有用。某些切削参数可以产生携带更多信息和/或具有更高信噪比的信号。

在训练过程中,该模型还可以对某些参数产生优于其他参数的偏好。偏好可以是模型构建方式(例如,β参数或编码大小)以及模型训练方式的函数。

我怀疑可能存在具有不同参数偏好的模型配置,例如铸铁而不是钢。一组模型可能会产生更好的结果。这将是进一步研究的一个有趣的领域!

趋势 KL-Divergence 分数

KL-divergence 分数可以按顺序进行趋势分析,以了解我们的异常检测模型是如何工作的。这是我最喜欢的图表——它很漂亮,给人很好的洞察力。

注意:您也可以对输入空间重构误差进行趋势分析,但我们不会在这里这样做。

让我们快速探索一下,看看这些趋势会是什么样子。我们需要一个函数来对子切割进行顺序排序:

现在做一个趋势图。

我们现在有了创建 KL-divergence 分数随时间变化趋势图所需的所有信息。

我们将趋势案例 13,这是在钢上进行,速度很慢,是一个浅切口。

看起来不错!这个模型产生了一个清晰的趋势。然而,正如我们在上一节中所看到的,我们的异常检测模型在某些切削条件下识别刀具异常(失效/不健康/磨损)时确实有些困难。

让我们看看另一个例子——案例 11。

通过“降级”区域,您可以看到趋势是如何增加的,但是当它到达红色的“失败”区域时,趋势会迅速下降。为什么?呃,我也不知道具体是什么。趋势末端的样本可能更类似于健康样本…我很想听听你的想法。

还有更多的分析可以做…我会留给你。如果你发现什么有趣的东西,请告诉我!

进一步的想法

在这个由三部分组成的系列中,我们所做的是使用 VAE 在工业数据集上构建一种异常检测方法。我毫不怀疑这些方法可以大大改进,其他有趣的领域也可以探索。

我希望一些勤奋的研究人员或学生可以利用这项工作作为跳板,或灵感,做一些真正有趣的事情!以下是我有兴趣进一步做的一些事情:

  • 正如我上面提到的,模型集合可能会产生更好的结果。
  • VAE 中的β使它成为一个不纠缠的变分自动编码器。看看编码如何随不同的切削参数而变化,以及编码是否代表独特的特征,这将是很有趣的。
  • 我在 VAE 中使用了 TCN,但我认为一个常规的卷积神经网络,加上膨胀,也会工作得很好(这是我的直觉)。这将使模型训练更简单。
  • 如果我要重新开始,我会整合更多的模型测试。这些模型测试(如单元测试)将对照不同的切割参数检查模型的性能。这将更容易发现哪些模型在切削参数上具有良好的通用性。

结论

在这篇文章中,我们通过几个可视化来探索我们训练过的 VAE 模型的性能。我们发现使用 KL 散度的潜在空间比输入空间的异常检测更有效。

在制造领域使用数据科学和机器学习工具有很强的商业案例。此外,这里演示的原则可以跨许多使用异常检测的领域使用。

我希望你喜欢这个系列,也许,学到了一些新的东西!

参考

[1]安,j .,&赵树声(2015)。使用重构概率的基于变分自动编码器的异常检测。工业工程专题讲座2 (1),1–18。

[2]j .戴维斯和 m .戈德里奇(2006 年 6 月)。精确召回与 ROC 曲线的关系。在第 23 届机器学习国际会议论文集(第 233–240 页)。

[3]t .斋藤和 m .雷姆斯迈尔(2015 年)。在不平衡数据集上评估二元分类器时,精确召回图比 ROC 图提供的信息更多。 PloS one10 (3),e0118432。

本文原载于【tvhahn.com】。此外,这项工作是对发表在 IJHM 研究的补充。官方的 GitHub 回购是 这里的

除非另有说明,本帖及其内容由作者授权于CC BY-SA 4.0

Python 中的异常检测——第二部分:多元无监督方法和代码

原文:https://towardsdatascience.com/anomaly-detection-in-python-part-2-multivariate-unsupervised-methods-and-code-b311a63f298b?source=collection_archive---------6-----------------------

关于如何为业务分析执行异常检测的指南或关于多元数据的机器学习管道以及相关 Python 代码。

在我之前的文章(https://medium . com/analytics-vid hya/Anomaly-detection-in-python-part-1-basics-code-and-standard-algorithms-37d 022 CDB CFF)中,我们讨论了异常检测的基础知识、问题的类型以及使用的方法的类型。我们讨论了执行异常检测的 EDA、单变量和多变量方法,以及每种方法的一个示例。我们讨论了为什么多元异常值检测是一个困难的问题,需要专门的技术。我们还讨论了使用 fastcd 的马氏距离方法来检测多元异常值。

在本文中,我们将讨论另外两种广泛使用的方法来执行多元无监督异常检测。我们将讨论:

  1. 隔离林
  2. OC-SVM(一级 SVM)

异常检测的一般思路

异常检测是一种识别数据中异常或有趣事件的工具。**但是,在移除异常之前,从领域/业务角度分析检测到的异常非常重要。**每种方法都有自己的异常定义。多种方法可能经常无法确定哪些点是异常的。从领域/业务角度验证结果是我们的责任。

有时,检测到的异常代表数据中的“欠采样”状态。在这种情况下,我们不应该删除它们,而是应该在该体系中收集更多的数据。

多元异常的简要概述

**当各种特征的值合在一起看起来异常时,即使单个特征没有异常值,也会出现多元异常。**例如,假设我们有 2 个特征:
1。这是汽车里程表的读数,单位是英里/小时。它通常在 0-50 之间。②
。每分钟转数:这是汽车车轮的每分钟转数。它通常在 0-650 之间。

现在,想象一下奥多的时速是 0 英里。我们知道这是可能的——而且汽车没有移动。比方说,另一个场合,转速读数 600。我们知道汽车在移动。分开来看,我们知道上述读数并不异常——因为它们代表了汽车完全正常的运行模式。然而,让我们想象一下在同一时间里程表读数为 0 英里/小时,而转速读数为 600 英里/小时。这是不可能的——他们有冲突。rpm 表示汽车在移动,odo 表示汽车是静止的。这是一个多元异常值的例子。

检测多元异常值包括同时检查所有特征的值,并验证这些特征的值组合是否异常。 正如我们所理解的,当我们拥有大量的特征(比如数百个)时,手动操作变得非常困难。或者,我们可以使用专门的算法来识别它们。让我们来看看它们。

用于异常检测的数据

我们希望用多元方法来检测 2 个主要的异常值——按作者分类的图像

隔离森林

众所周知,隔离林适用于高维数据。隔离林是“隔离树”的集合。在下面的论文中详细讨论:https://cs . nju . edu . cn/Zhou zh/Zhou zh . files/publication/ICD m08 b . pdf?q =隔离-森林。
*隔离树是一种二叉树,通过将数据划分成盒(称为节点)来存储数据。*要理解为什么隔离林是异常检测器,重要的是要理解隔离树是如何构建的。以下是计算隔离树的步骤:

  1. 从数据中随机选择一个特征。让我们称随机特征为 f
  2. 从特征 f 中选择一个随机值。我们将使用这个随机值作为阈值。让我们称之为 t.
  3. f < t 所在的数据点存放在节点 1, f ≥ t 所在的数据点存放在节点 2。
  4. 对节点 1 和节点 2 重复步骤 1-3。
  5. 当树完全长大或满足终止标准时终止。

为了简单起见,让我们从隔离树如何处理单变量数据开始。我们稍后将探讨多元示例。下图显示了一维数据的机制:

为没有异常值的数据构建的隔离树。选择一个随机阈值将数据分成子节点。然后对每个子节点重复这个过程。—作者图片

重要的是要记住,要分割的特征和阈值是随机选择的,如上图所示。由于以上示例为单因素,因此我们仅随机选择阈值。

现在,假设上面的单变量数据有异常。在这种情况下,异常点将远离其他数据点。隔离林能够在分裂过程中很早就隔离出异常,因为 用于分裂的随机阈值很有可能位于离群值和数据之间的空白区域,如果空白区域足够大的话。因此,异常具有较短的路径长度。毕竟,分割点(阈值)是随机选择的。因此,空白区域越大,随机选择的分裂点就越有可能位于该空白区域

让我们看看存在异常时隔离树的外观。

隔离树“隔离”出第一个拆分中的异常。由于异常与其他数据之间的空间较大,因此很可能在该区域出现随机分裂。—作者图像

正如我们所看到的,由于异常和其他数据之间的空间很大,很可能在这个空白区域会出现随机分裂。

现在,让我们看看如果有多元数据,情况会是怎样。正如我们将看到的,隔离树的工作方式与我们上面看到的非常相似——它们在隔离其他点之前先隔离异常。

异常在裂口 2 处被隔离。—作者图像

请注意:树木也可以生长:

  1. 直到每个叶节点中只有一个数据点。或
  2. 直到达到关于叶节点中数据点的最小数量的终止标准。

我在这里只展示了前几个部分来说明。正如我们所看到的,隔离树将数据划分为“框”。其特点是比包含正常数据点的方框更早地隔离包含异常的区域。

我们可以将隔离树的概念扩展到一个隔离林,它是多个隔离树的集合。以下是隔离林的工作原理:

  1. 从整个要素集或随机选择的要素集子集构建隔离树。
  2. 构建 n 这样的隔离树。
  3. 计算每个数据点的异常分数。**异常得分为所有隔离树平均路径长度的非线性函数。**路径长度相当于隔离树为隔离一个点而进行的拆分次数。平均路径长度越短,该点成为异常的可能性就越大(正如我们在图中前面看到的)。

即使对于具有数百个维度的数据,隔离林也能很好地工作。

skearn 的隔离林有 4 个重要输入:

***n _ estimators:***被训练的隔离树数。 ***max _ samples:***用于训练每棵树的数据点数。 沾染: 异常数据点的分数。例如,如果我们怀疑 5%的数据是异常的,我们将污染度设置为 0.05 ***max _ features:***用于训练每棵树的特征数量(这与随机森林相反,在随机森林中,我们为每次分割决定随机的特征子集)。

它有两个重要的方法:

decision_function(X): 返回一个分数——这样负分数越多的例子就越反常。
预测(X): 对于异常点返回-1,对于正常点返回+1。输出为异常的点的数量取决于拟合模型时设置的污染值。

让我们根据上述数据训练一个隔离林(我们将污染设置为 0.01):

# Create Artificial Data with Multivariate Outliers
d1 = np.random.multivariate_normal(mean = np.array([-.5, 0]),
                               cov = np.array([[1, 0], [0, 1]]), size = 100)d2 = np.random.multivariate_normal(mean = np.array([15, 10]),
                               cov = np.array([[1, 0.3], [.3, 1]]), size = 100)outliers = np.array([[0, 10],[0, 9.5]])
d = pd.DataFrame(np.concatenate([d1, d2, outliers], axis = 0), columns = ['Var 1', 'Var 2'])################### Train Isolation Forest #################
model  =  ensemble.IsolationForest(n_estimators=50, max_samples=500, contamination=.01, max_features=2, 
                         bootstrap=False, n_jobs=1, random_state=1, verbose=0, warm_start=False).fit(d)# Get Anomaly Scores and Predictions
anomaly_score = model.decision_function(d)
predictions = model.predict(d)######### Visualize Anomaly scores and Anomaly Status ########plt.figure(figsize = (10, 6), dpi = 150)
s = plt.scatter(d['Var 1'], d['Var 2'], c = anomaly_score, cmap = 'coolwarm')
plt.colorbar(s, label = 'More Negative = More Anomalous')
plt.xlabel('Var 1', fontsize = 16)
plt.ylabel('Var 2', fontsize = 16)
plt.grid()# To Plot Predictions
plt.figure(figsize = (10, 6), dpi = 150)
s = plt.scatter(d['Var 1'], d['Var 2'], c = predictions, cmap = 'coolwarm')
plt.colorbar(s, label = 'More Negative = More Anomalous')
plt.xlabel('Var 1', fontsize = 16)
plt.ylabel('Var 2', fontsize = 16)
plt.grid()
plt.title('Contamination = 0.01', weight = 'bold')

左图:决策函数的输出-负值越大,意味着异常越强。右图:异常的最高“污染”分数。—作者图片

如我们所见,这两个点被检测为强异常值。

因此,可以使用两种方法来确定一个点是否异常:

  1. 使用预测函数:如果模型预测为-1,则将该点标记为异常点。对于这种方法,小心设置污染很重要。
  2. 分析决策函数输出分布,并基于视觉检查设置阈值,异常点将落在该阈值以下。我们还可以对决策函数输出应用单变量异常检测算法;这是一种非常常见的方法,我们将多变量问题转化为单变量问题,通过计算异常分数,然后对分数使用单变量算法。该方法不依赖于我们对污染因子的选择。

但是,让我们看看如果我们设置不同的污染值并使用方法 1 会发生什么。

选择方法 1 预测时污染对隔离林产量的影响。—作者图片

随着污染值的增加,模型将更多的数据标记为异常。

如果我们不知道正确的污染怎么办?

在这种情况下,我们分析 decision_function()的输出。有两种方法可以做到这一点:

  1. 对隔离林决策函数输出应用单变量异常检测算法(类似于 tukey 的方法——我们在上一篇文章中讨论过)。这是一种标准方法——我们使用多元算法计算“异常分数”(这里是决策函数输出);然后,为了选择这些异常分数中的哪些对应于异常值,我们对分数应用单变量异常检测算法。
  2. 绘制直方图,手动选择阈值。

让我们看看将 Tukey 的方法应用于我们的隔离林给出的决策函数输出的结果:

应用于隔离林决策函数输出的 Tukey 方法选择了 6 个异常值。我们可以看到两个极端的异常值——这确实是我们试图检测的异常。—作者图片

我们看到两个明显的异常值,它们是左边的两个极端点。这些实际上也是我们想要检测的两个主要异常值。然而,我们看到另外 4 个点被标记为异常值。为了选择适当的异常,需要进行领域/业务分析。

让我们讨论一下隔离林的一些优点:

  1. 可扩展性— 隔离林可以在训练和预测期间使用并行处理—因为所有隔离树都是并行训练的—彼此独立。
  2. 可解释性— 隔离森林中的单棵树可以被可视化,以给出使数据点成为异常值的确切规则。这些规则可能具有很大的业务/领域重要性。但是,对于大数据来说,这可能会变得很困难。
  3. 灵活性— 它们可以捕捉非常复杂的异常值,并且不要求数据属于特定的分布。如果我们还记得,前一篇文章中讨论的使用 FastMCD 的 Mahalanobis 距离方法假设干净数据属于多元正态分布。隔离森林不做这样的假设。

根据经验,我注意到严重异常值和轻微异常值的决策函数值通常很接近。正如我们在这里看到的,我们有 2 个明显的异常值。然而,他们的决策函数输出接近于其他一些点的决策函数输出。这与 Mahalanobis 距离方法形成对比,在 Mahalanobis 距离方法中,对于相同的问题,区别非常明显。总体而言,隔离林检测到的异常值是合理的,对决策函数使用单变量方法的方法也产生了合理的结果。

一类——支持向量机(OC-SVM)

OC-SVM 是一种多元方法,属于一类分类方法家族。在下面的论文中详细讨论:https://www . Microsoft . com/en-us/research/WP-content/uploads/2016/02/tr-99-87 . pdf

我们决定一小部分数据,比如说ν(读作 Nu ),我们怀疑它是数据中出现的异常数量的上限。**然后,OC-SVM 试图找到一个包含高数据密度区域的边界,最多排除一小部分ν个数据点。**可以看出,问题变量空间中的线性边界对于大多数问题来说过于简单。由于 SVM 本质上是线性分类器,我们需要借助核方法来建立具有非线性边界的灵活模型。

OC-SVM 试图寻找一个超平面将数据从原点分开,而不是寻找一个决策边界来分隔两个类。想法是将数据映射到内核特征空间,并使用内核特征空间中的线性分类器以最大余量将其从原点分离。这相当于在我们的原始问题空间中使用非线性边界。

**让我们简单讨论一下 OC-SVM 中内核的使用。**SVM 是一个线性模型。这使得它可以制定非常简单的决策规则。提高 SVM 容量的一种方法是根据数据创建多项式要素,然后在变换后的要素空间中使用线性 SVM。SVM 仍然在多项式特征空间中找到线性边界,但是当映射回我们的原始问题变量空间时,判定边界(在多项式特征空间中是线性的)看起来是非线性的。但是我们必须显式计算多项式特征——如果我们一开始就有大量特征,这会占用大量内存。**内核允许我们在非常高维的特征空间中拟合简单模型(如线性 SVM ),而无需显式计算高维特征。**最广泛使用的内核之一是 **RBF 内核。**要记住的重要一点是,SVM 总是在核心特征空间中拟合线性模型——即使决策边界在原始问题变量空间中看起来是非线性的。

以下是关于 OC-SVM 的一些一般要点:

  1. 一般来说,使用 OC-SVM 时,建议使用非线性内核。RBF 核被广泛使用。
  2. 用 RBF 核来调整 OC-SVM 的超参数是:γ,ν
  3. 可以使用 predict()和 decision_function()方法进行预测。我们将在后面详细讨论它们。

Gamma 是特定于 RBF 核的参数,它控制相邻点对决策边界的影响。较大的 Gamma 值允许相邻点对决策边界有较大的影响,较小的 Gamma 值允许相邻点和远处点对决策边界都有影响。

让我们讨论使用不同的γ值的效果。较大的 Gamma 值会导致模型具有较大的方差-这会以概化为代价。让我们改变伽玛,看看对模型的影响。

def plot_anomaly2(data, predicted, ax):
    data2 = data.copy()
    data2['Predicted'] = predicted

    normal = data2.loc[data2['Predicted'] == 1, :]
    anomalies = data2.loc[data2['Predicted'] == -1, :]

    # Make Scatterplot
    column1 = data.columns[0]
    column2 = data.columns[1]

    anomalies.plot.scatter(column1, column2, color = 'tomato', fontsize = 14,  sharex = False, ax=ax)
    normal.plot.scatter(column1, column2, color = 'grey', fontsize = 14,  sharex = False, ax = ax)#plt.grid(linestyle = '--')

    plt.xlabel(column1, fontsize = 14, weight = 'bold')
    plt.ylabel(column2, fontsize = 14, weight = 'bold')
    return ax# Create Fake data to classify 
x_fake  =  pd.DataFrame(np.random.uniform(-5, 19, (35000, 2)), columns = ['Var 1', 'Var 2'])# Visualize effect of changing Gamma
gammas = [.00005, .005, .01, .025, .05, .1,.3, .6, .9, 2, 5, 10]
fig, axes = plt.subplots(2, 6, figsize = (25, 6), tight_layout = True)
for i, ax in zip(range(len(gammas)), axes.flatten()):
    gamma = gammas[i]
    model = svm.OneClassSVM(kernel='rbf', degree=5, gamma=gamma, coef0=0.0, tol=0.001, nu=0.01, 
                        shrinking=True, cache_size=200, verbose=False, max_iter=- 1).fit(all_data)model_predictions = model.predict(x_fake)
    #x_fake['Predictions'] = model_predictionsax = plot_anomaly2(x_fake, model_predictions,ax)
    ax.scatter(all_data.iloc[:, 0], all_data.iloc[:, 1], color = 'k', s = 10)
    ax.set_title('Gamma: {}'.format(np.around(gamma,6)), weight = 'bold', fontsize = 14) 

下图中的蓝色区域是指 SVM 奥组委预测为“正常”的区域。红点被检测为异常。

数据点的异常状态与作者提供的伽马图像的关系

我们观察到以下情况:

  1. 当伽马射线极低或极高时,我们看到 OC-SVM 至少会漏掉一个主要的异常现象。
  2. 对于 0.005 到 0.1 范围内的中伽马值,OC-SVM 识别出两种主要异常。

选择 Gamma 的一种方法是让 sklearn 选择 Gamma。我们通过选择 gamma = 'scale '来实现。下面是当我们设置 gamma = 'scale '时发生的情况:

Gamma = 'scale '检测 2 个异常值。nu 设定为 0.05。—作者图片

如前所述,在 OC-SVMs 中,使用线性决策边界将数据从核空间中的原点分离出来。允许一小部分(高达ν)数据落在线性决策边界的错误一侧。基本上,我们希望所有的内点在决策边界的一边,所有的外点在决策边界的另一边。OC-SVM 的 decision_function 方法输出一个点到决策边界的符号距离。对于异常值,该距离取负值,对于正常点(内部点),该距离取正值。

让我们像前面一样对 decision_function 输出应用 tukey 的方法。

在 Tukey 方法中,k = 1.5 的 2 个点被确定为异常。这里,tukey 的方法被应用于 OCSVM 决策函数输出。—作者图片

幸运的是,tukey 的方法发现了数据中的两个主要异常。然而,在一般情况下,它可以识别额外的或较小的异常。Tukey 方法识别的异常依赖于我们的“k”值(在前一篇文章中讨论过),这个值是可以调整的。有时将 k 视为 ML 管道中的超参数是有用的,这可以通过领域分析或优化来最终确定。

结论

到目前为止,我们已经讨论了执行异常检测的无监督方法。我们讨论了用于执行多元异常检测的隔离森林和 OC-SVM 方法。这种方法的优点之一是它们不要求数据属于特定的分布。

OC-SVM 是一种可用于无监督和半监督异常检测的方法。在接下来的文章中,我们将讨论执行异常检测的半监督和监督方法。它们包括使用 PCA、自动编码器、OC-SVM 和不平衡分类方法来执行异常检测。

如果您有任何反馈,请随时告诉我,并查看我以前关于异常检测的介绍性文章,其中我们讨论了不同类型的异常检测问题和方法(https://medium . com/analytics-vid hya/Anomaly-detection-in-python-part-1-basics-code-and-standard-algorithms-37d 022 cdbcff)。

伺服驱动器的异常检测

原文:https://towardsdatascience.com/anomaly-detection-on-servo-drives-f711f3e6022f?source=collection_archive---------16-----------------------

行业笔记

可靠地预测伺服故障以减少停机时间

介绍

给定伺服反馈信息,如扭矩、速度、加速度和功率,可以预测伺服系统出现问题的可能性。通过了解伺服系统可能出现故障的时间,可以减少停机时间,并防止故障伺服系统对系统造成潜在损害。通过将简单的高斯概率密度函数应用于一组训练的特征,可以用很少的计算成本来确定异常伺服的分类。

背景

单个特征 x 的各种平均值和方差值的高斯分布如下所示:

各种高斯分布,图片由维基百科提供

该图说明了方差如何影响沿特征轴的概率密度函数大小和覆盖范围。较高的方差值表示数据更加分散,较低的方差值表示数据的值接近,方差为零表示所有数据都相同。

理论

模拟上述曲线的高斯概率密度函数如下所示:

等式 1:高斯概率密度函数,图片来自维基百科

该函数表示给定特征值 x. 处的幅度。在这种情况下,均值(μ)和方差(σ)值均在评估该函数之前确定。使用以下通用形式确定平均值:

特征均值方程,作者图片

使用以下公式确定差异:

特征方差方程,作者图片

计算将用于训练和评估模型的每个特征的平均值和方差。使用所使用的编程环境尽可能地对计算进行矢量化是很重要的。

该函数将输出高斯概率密度,对于包含各种要素的不同数据集,高斯概率密度会有显著变化。输出值需要被映射到表示伺服状态分类(异常或非异常)的二进制值。这将通过为概率密度函数指定阈值来完成。

当确定异常的阈值时,可以使用迭代方法来决定一个值,并观察模型对于各种特征值的行为。所选阈值取决于模型标记异常数据集的灵敏度。如果存在交叉验证或测试数据集,则可以评估精确度、召回率、F1 分数和综合 F-1 分数,以确定模型何时表现最佳。

一个好的起始值是平均值的 2-3 倍标准差。最大概率密度可以通过将等式 1 上的指数值驱动为 0 并用矢量化的学习方差值来评估该等式来确定。类似地,2 个标准偏差下的密度可以通过在μ+2σ下评估等式 1 来确定。

特征选择

通过组合两个伺服反馈值来确定特征,这两个伺服反馈值在理论上应该彼此成比例。几个特征对的例子可以包括:

  • 输入功率和输出扭矩
  • 输入功率和输出速度
  • 输入功率和加速度
  • 输入扭矩和输出速度

通过将一个除以另一个,可以将两个反馈变量组合成一个单一特征:

功能 1 由功率和扭矩决定,图片由作者提供

这允许模型对异常值进行分类,如果使用标准反馈值作为两个独立的特征,则异常值可以被分类为非异常。

对于以下所示的示例,使用了由扭矩和速度组成的单一特征:

使用的特征,例如,作者的图像

使用数据采样功能,在伺服轴上运行各种 g 代码命令时,可以保存扭矩和速度值。使用各种伺服参数尽可能多地采样数据非常重要。在这种情况下,伺服系统的最大速度和加速度分别在 0.1-200.0 和 10.0-500.0 用户单位之间变化。大约有 50,000 个训练样本,分布如下图所示:

特征 1 的直方图,按作者分类的图像

请注意,数据类似于高斯分布,如果不是这种情况,则必须应用转换来使其高斯化。在某些情况下,这可以通过对数据集取对数来实现。

训练数据的平均值、方差和标准差如下所示:

分布的均值、方差和标准差,按作者分类的图像

这些值是模型学习并用于应用于伺服反馈变量的未来值的内容。

使用等式 1,为每个训练数据点计算概率密度,并且产生以下曲线:

训练数据的高斯概率密度,图片由作者提供

程序

为了查看运行中的模型,伺服机构将被配置为执行简单恒速移动的 3D 机架的 X 轴。

PLC 将在每个周期(8 毫秒)采样扭矩和速度值,并评估伺服系统是否异常。从模型输出的高斯概率密度和二元分类将与每次扫描采样的数据一起保存。

虽然每 1000 次的异常输出可能会出现一次假阳性,但预计模型在恒速运行时输出“非异常”。因此,需要实现额外的逻辑来确定何时以及如何对模型的输出采取行动。例如,在后面的 1,000 个样本中异常出现的次数可以用来触发一个事件,以通知管理员检查伺服系统。

为了模拟异常,3D 机架上的 X 轴将受到未知来源的外部阻力(不要在家里尝试)。这种模拟异常可能与向伺服机构提供机械阻力的受损轴承相关,并且这反映在所提供扭矩的输出速度恶化上。当伺服系统受到这些外力时,预计模型输出“异常”。

测试

对于上述测试程序,高斯概率密度作为时间的函数绘制如下:

单次加载的概率密度,图片由作者提供

请注意,在 6 秒标记附近,震级迅速下降;这是伺服机构经受外部阻力的准确时间,并且持续到外部阻力被移除时的 14 秒标记。

由阈值 0.001 指定的二元分类输出绘制如下:

单次加载的二进制分类,按作者分类的图像

二元分类反映了上面的高斯概率密度函数图中显示的内容。由于严格的阈值,6–14s 范围内的一些值不被归类为异常。

生成了第二组图来证明外部阻力/异常的重复加载和卸载。请注意,第二个测试使用的是用不同数据训练的模型,因此具有不同的权重。概率密度绘制如下:

重复加载和卸载的概率密度,图片由作者提供

最初,密度值以总体水平趋势分散。这是因为扭矩值会稍微改变,以保持伺服系统的恒定速度,这是预期的行为。一旦在大约 4.0、8.0 和 12.0 秒的标记处施加载荷,概率密度下降到零。通过观察剧情,很明显伺服发生了一些问题。

下面绘制的是上面绘制的概率密度的二元分类,阈值为 0.0026,这是平均值的 3 个标准偏差。

重复加载和卸载的二进制分类,图片由作者提供

该图将证实在上述概率密度图上看到的情况,因为它是概率密度的函数。

结论

实现这种用于异常检测的轻量级高斯概率密度模型可以很好地指示伺服何时需要维护。通过在 PLC 上运行该模型作为后台进程,可以防止不必要的停机时间和对系统的潜在损害。

对这个实现感兴趣吗?联系我。

参考

[1] Ng,a .异常检测[讲座]。机器学习。 Coursera。【https://www.coursera.org/learn/machine-learning

[2]维基百科贡献者。(2021 年 10 月 12 日)。正态分布。在维基百科,免费百科。2021 年 10 月 20 日 03:24 从https://en.wikipedia.org/w/index.php?检索 title =正态分布& oldid=1049556144

[3]列别德津斯基,P. (2021 年 6 月 24 日)。用于评估对象检测模型的单个数字度量。中等。2021 年 11 月 10 日检索,来自https://towardsdatascience . com/a-single-number-metric-for-evaluating-object-detection-models-c97f 4a 98616d。

使用自动编码器的异常检测

原文:https://towardsdatascience.com/anomaly-detection-using-autoencoders-5b032178a1ea?source=collection_archive---------1-----------------------

使用 TensorFlow 中的自动编码器执行欺诈检测

了解什么是自动编码器,它们如何工作,它们的用法,最后实现用于异常检测的自动编码器。

AutoEncoder 是一种生成式无监督深度学习算法,用于使用神经网络重建高维输入数据,该神经网络在中间具有狭窄的瓶颈层,该瓶颈层包含输入数据的潜在表示。

来源:https://lilian Weng . github . io/lil-log/2018/08/12/from-auto encoder-to-beta-vae . html

Autoencoder 由编码器和解码器组成。

  • 编码器网络:接受高维输入数据,并将其转化为潜在的低维数据。编码器网络的输入大小大于其输出大小。
  • 解码器网络:解码器网络从编码器的输出接收输入。解码器的目标是重建输入数据。解码器网络的输出大小大于其输入大小。

自动编码器接受高维输入数据,将其压缩到瓶颈隐藏层的潜在空间表示;解码器将数据的潜在表示作为输入,以重构原始输入数据。

自动编码器的使用

  • **降维。**编码器将输入编码到隐藏层,降低线性和非线性数据的维数;因此,它比 PCA 更强大。
  • 推荐引擎
  • 异常检测:作为训练的一部分,自动编码器试图最小化重建误差。通过检查重建损失的大小来检测异常。
  • 图像去噪:被破坏的图像可以恢复到原来的版本。
  • 图像识别:堆叠式自动编码器通过学习图像的不同特征来进行图像识别。
  • 图像生成:变分自动编码器(VAE),一种自动编码器,用于生成图像。

在这里阅读关于不同类型的自动编码器https://medium.com/datadriveninvestor/deep-learning-different-types-of-autoencoders-41d4fa5f7570

使用自动编码器的异常检测

按照以下步骤检测高维数据集中的异常。您也可以将此应用于不平衡的数据集。

  • 在训练期间,只向编码器输入正常的交易。瓶颈层将学习正常输入数据的潜在表示。
  • 解码器将使用瓶颈层输出来重建原始输入数据的正常事务。
  • 欺诈交易不同于正常交易。自动编码器将难以重建欺诈性交易,因此重建误差将会很高。
  • 您可以根据重建错误的指定阈值,将新交易标记为欺诈交易。

使用自动编码器实现异常检测

这里使用的数据集是来自 Kaggle 的信用卡欺诈检测。

导入所需库

**import pandas as pd
import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split****from sklearn.preprocessing import StandardScaler
from sklearn.metrics import confusion_matrix, recall_score, accuracy_score, precision_score****RANDOM_SEED = 2021 
TEST_PCT = 0.3
LABELS = ["Normal","Fraud"]**

读取数据集

我已经从 Kaggle 下载了数据,并存储在本地目录中。

**dataset = pd.read_csv("creditcard.csv")**

探索性数据分析

#check for any  nullvalues 
**print("Any nulls in the dataset ",dataset.isnull().values.any() )
print('-------')**
**print("No. of unique labels ", len(dataset['Class'].unique()))
print("Label values ",dataset.Class.unique())**#0 is for normal credit card transaction
#1 is for fraudulent credit card transaction
**print('-------')
print("Break down of the Normal and Fraud Transactions")
print(pd.value_counts(dataset['Class'], sort = True) )**

可视化数据集

绘制数据集中正常交易和欺诈交易的数量。

#Visualizing the imbalanced dataset
**count_classes = pd.value_counts(dataset['Class'], sort = True)
count_classes.plot(kind = 'bar', rot=0)
plt.xticks(range(len(dataset['Class'].unique())), dataset.Class.unique())
plt.title("Frequency by observation number")
plt.xlabel("Class")
plt.ylabel("Number of Observations");**

可视化正常交易和欺诈交易的金额。

# Save the normal and fradulent transactions in separate dataframe
**normal_dataset = dataset[dataset.Class == 0] 
fraud_dataset = dataset[dataset.Class == 1]**#Visualize transactionamounts for normal and fraudulent transactions
**bins = np.linspace(200, 2500, 100)
plt.hist(normal_dataset.Amount, bins=bins, alpha=1, density=True, label='Normal')
plt.hist(fraud_dataset.Amount, bins=bins, alpha=0.5, density=True, label='Fraud')
plt.legend(loc='upper right')**
**plt.title("Transaction amount vs Percentage of transactions")
plt.xlabel("Transaction amount (USD)")
plt.ylabel("Percentage of transactions");
plt.show()**

创建训练和测试数据集

检查数据集

时间和金额是不缩放的列,因此仅对金额和时间列应用 StandardScaler。对 0 和 1 之间的值进行归一化对于数据集来说效果不太好。

**sc=StandardScaler()
dataset['Time'] = sc.fit_transform(dataset['Time'].values.reshape(-1, 1))
dataset['Amount'] = sc.fit_transform(dataset['Amount'].values.reshape(-1, 1))**

数据集中的最后一列是我们的目标变量。

**raw_data = dataset.values**
# The last element contains if the transaction is normal which is represented by a 0 and if fraud then 1
**labels = raw_data[:, -1]**# The other data points are the electrocadriogram data
**data = raw_data[:, 0:-1]****train_data, test_data, train_labels, test_labels = train_test_split(
    data, labels, test_size=0.2, random_state=2021
)**

标准化数据,使其值介于 0 和 1 之间

**min_val = tf.reduce_min(train_data)
max_val = tf.reduce_max(train_data)****train_data = (train_data - min_val) / (max_val - min_val)
test_data = (test_data - min_val) / (max_val - min_val)****train_data = tf.cast(train_data, tf.float32)
test_data = tf.cast(test_data, tf.float32)**

仅使用普通交易来训练自动编码器。

正常数据在目标变量中的值为 0。使用目标变量创建正常和欺诈数据集。

**train_labels = train_labels.astype(bool)
test_labels = test_labels.astype(bool)**#creating normal and fraud datasets **normal_train_data = train_data[~train_labels]
normal_test_data = test_data[~test_labels]****fraud_train_data = train_data[train_labels]
fraud_test_data = test_data[test_labels]
print(" No. of records in Fraud Train Data=",len(fraud_train_data))
print(" No. of records in Normal Train data=",len(normal_train_data))
print(" No. of records in Fraud Test Data=",len(fraud_test_data))
print(" No. of records in Normal Test data=",len(normal_test_data))**

设置训练参数值

nb_epoch = 50
batch_size = 64
input_dim = normal_train_data.shape[1] #num of columns, 30
encoding_dim = 14
hidden_dim_1 = int(encoding_dim / 2) #
hidden_dim_2=4  
learning_rate = 1e-7 

创建自动编码器

自动编码器的架构如下所示。

作者图片

#input Layer
**input_layer = tf.keras.layers.Input(shape=(input_dim, ))**#Encoder**encoder = tf.keras.layers.Dense(encoding_dim, activation="tanh",                                activity_regularizer=tf.keras.regularizers.l2(learning_rate))(input_layer)
encoder=tf.keras.layers.Dropout(0.2)(encoder)
encoder = tf.keras.layers.Dense(hidden_dim_1, activation='relu')(encoder)
encoder = tf.keras.layers.Dense(hidden_dim_2, activation=tf.nn.leaky_relu)(encoder)**# Decoder
**decoder = tf.keras.layers.Dense(hidden_dim_1, activation='relu')(encoder)
decoder=tf.keras.layers.Dropout(0.2)(decoder)
decoder = tf.keras.layers.Dense(encoding_dim, activation='relu')(decoder)
decoder = tf.keras.layers.Dense(input_dim, activation='tanh')(decoder)**#Autoencoder
**autoencoder = tf.keras.Model(inputs=input_layer, outputs=decoder)
autoencoder.summary()**

定义检查点和提前停止的回调

**cp = tf.keras.callbacks.ModelCheckpoint(filepath="autoencoder_fraud.h5",
                               mode='min', monitor='val_loss', verbose=2, save_best_only=True)
# define our early stopping
early_stop = tf.keras.callbacks.EarlyStopping(
    monitor='val_loss',
    min_delta=0.0001,
    patience=10,
    verbose=1, 
    mode='min',
    restore_best_weights=True**

编译自动编码器

**autoencoder.compile(metrics=['accuracy'],
                    loss='mean_squared_error',
                    optimizer='adam')**

训练自动编码器

**history = autoencoder.fit(normal_train_data, normal_train_data,
                    epochs=nb_epoch,
                    batch_size=batch_size,
                    shuffle=True,
                    validation_data=(test_data, test_data),
                    verbose=1,
                    callbacks=[cp, early_stop]
                    ).history**

剧情训练和测试损失

**plt.plot(history['loss'], linewidth=2, label='Train')
plt.plot(history['val_loss'], linewidth=2, label='Test')
plt.legend(loc='upper right')
plt.title('Model loss')
plt.ylabel('Loss')
plt.xlabel('Epoch')
#plt.ylim(ymin=0.70,ymax=1)
plt.show()**

检测测试数据的异常

异常是重建损失较高的数据点

计算测试数据的重建损失,预测测试数据,并计算测试数据和重建的测试数据之间的均方误差。

**test_x_predictions = autoencoder.predict(test_data)
mse = np.mean(np.power(test_data - test_x_predictions, 2), axis=1)
error_df = pd.DataFrame({'Reconstruction_error': mse,
                        'True_class': test_labels})**

绘制测试数据点和它们各自的重建误差设置了阈值,以便可视化是否需要调整阈值。

**threshold_fixed = 50
groups = error_df.groupby('True_class')
fig, ax = plt.subplots()****for name, group in groups:
    ax.plot(group.index, group.Reconstruction_error, marker='o', ms=3.5, linestyle='',
            label= "Fraud" if name == 1 else "Normal")
ax.hlines(threshold_fixed, ax.get_xlim()[0], ax.get_xlim()[1], colors="r", zorder=100, label='Threshold')
ax.legend()
plt.title("Reconstruction error for normal and fraud data")
plt.ylabel("Reconstruction error")
plt.xlabel("Data point index")
plt.show();**

将异常检测为重建损失大于固定阈值的点。这里我们看到阈值为 52 是好的。

评估异常检测的性能

**threshold_fixed =52
pred_y = [1 if e > threshold_fixed else 0 for e in error_df.Reconstruction_error.values]
error_df['pred'] =pred_y
conf_matrix = confusion_matrix(error_df.True_class, pred_y)****plt.figure(figsize=(4, 4))
sns.heatmap(conf_matrix, xticklabels=LABELS, yticklabels=LABELS, annot=True, fmt="d");
plt.title("Confusion matrix")
plt.ylabel('True class')
plt.xlabel('Predicted class')
plt.show()**# print Accuracy, precision and recall
**print(" Accuracy: ",accuracy_score(error_df['True_class'], error_df['pred']))
print(" Recall: ",recall_score(error_df['True_class'], error_df['pred']))
print(" Precision: ",precision_score(error_df['True_class'], error_df['pred']))**

由于我们的数据集是高度不平衡的,我们看到了高准确度,但低召回率和精确度。

要进一步提高精确度和召回率,需要添加更多相关的功能、不同的 autoencoder 架构、不同的超参数或不同的算法。

结论:

当我们有一个不平衡的数据集时,Autoencoder 可以用作异常检测算法,我们有很多好的例子,但只有少数异常。自动编码器被训练以最小化重建误差。当我们在正常数据或良好数据上训练自动编码器时,我们可以假设异常将比良好或正常数据具有更高的重建误差。

参考文献:

**https://blog.keras.io/building-autoencoders-in-keras.html

【https://www.deeplearningbook.org/contents/autoencoders.html

https://www . tensor flow . org/tutorials/generative/auto encoder # third _ example _ anomaly _ detection**

异常检测:为什么您的数据团队对此不感兴趣

原文:https://towardsdatascience.com/anomaly-detection-why-your-data-team-is-just-not-that-into-it-580663498d0a?source=collection_archive---------14-----------------------

引入更主动的数据质量方法:数据可靠性生命周期。

图片由 Georg Arthur Pflueger 在 Unsplash 上提供。

交付可靠的数据产品不必如此痛苦。

以下是一些最优秀的数据团队在实现主动、迭代的数据信任模型时,转向开发运维及站点可靠性工程寻求灵感的原因和方式。 数据可靠性生命周期 介绍。

想象一下,你是一名汽车修理工。

一辆轿车开进你的车库,发动机发出劈啪声。

“怎么了?”你问,从你的桌子上抬起你的眼睛。

司机摇下车窗。“我的车出了点问题,”他们回答。

很有描述性,你想,擦擦额头的汗。你的讽刺让你窃笑。

"我的车出了点问题。"他们重复,这次没有收缩。

几个小时后,你发现这辆车的火花塞松了。当然,他们缺乏信息并不是世界末日,但是想象一下,如果他们积极主动地说:

"我的引擎无法启动,我的车无法加速,我的电池也一直没电。"记住,你是个机械师。:)

你会问,这和数据有什么关系?嗯,表面上看,不多。但是在构建更可靠的数据系统时,我们可以从我们友好的机械师那里学到一两件事。

数据质量:反应式方法

如今,大多数数据团队采用某种 异常检测 措施来解决数据质量问题。异常检测非常适合希望确定数据健康的关键支柱(即,容量、新鲜度、模式和分布)何时未能满足组织的生产预期的组织。此外,当端到端实现时(例如跨您的数据仓库、lake、ETL 和 BI 工具),异常检测对企业来说非常有价值,而不是只存在于您的数据平台的一两层中。

太好了,你知道你的数据坏了。现在怎么办?

正如大多数数据团队所了解的那样,当涉及到建立洞察力驱动的组织所需的信任、责任和透明度时,仅靠异常检测是不够的。

最近,我和一家财富 500 强软件公司的分析副总裁一起喝(虚拟)咖啡,他几乎完美地总结了这个问题。

“我想要与影响相关的东西,这样我就可以对它们采取行动,”他说。“异常检测作为起点是必要的,但我们需要做更多的工作来了解根本原因并评估影响。知道有问题是很好的,但是真的很难理解如何处理它。相反,我们需要确切地了解是什么出了问题,谁受到了它的影响,它出问题的原因和位置,以及根本原因可能是什么。我对异常检测一点也不感兴趣

显然,我们需要一种更好的方法来了解并主动改善数据的健康状况。幸运的是,现代数据团队只需要看看我们的 DevOps 和现场可靠性工程同行,就可以在 DevOps 生命周期中获得灵感。

DevOps 的生命周期是什么?

图片由 萨万阿吉特 Shutterstock由作者购买用于 标准商业许可

开发人员运营(DevOps) 团队已经成为大多数工程组织不可或缺的组成部分,打破了软件和基础设施团队之间的孤岛。为了促进代码及其底层架构的快速开发,DevOps 团队应用了一个反馈循环,称为 DevOps 生命周期,它帮助团队可靠地交付与大规模业务目标一致的功能。

DevOps 生命周期包含 8 个不同的连续阶段,包括:

  • 规划:与产品和业务团队合作,理解软件的目标和 SLA。
  • 开发:写软件。
  • 集成:将软件与您现有的应用程序集成。
  • 测试:测试你的软件。
  • 发布:将你的软件发布到测试环境中。
  • 部署:将您的软件部署到生产环境中。
  • 操作:运行软件,根据需要进行调整。
  • 监控:监控软件中的问题并发出警报。

如此循环往复。虽然我们的许多数据技术和框架(即数据测试、数据 SLA、分布式架构等。)已经适应了由我们的软件工程同行设定的标准和最佳实践,我们被动处理数据质量的倾向阻止了我们以有意义和可伸缩的方式推动业务的变化。

数据可靠性生命周期简介

就像 DevOps 应用一个连续的反馈循环来改进软件一样,我认为现在是我们利用同样的数据勤奋的时候了。

数据可靠性生命周期是一种持续、主动改善数据健康的全组织方法,它通过将 DevOps 的最佳实践应用于数据管道来消除数据停机。

图片由巴尔·摩西提供。

该框架允许数据团队:

  1. 率先了解生产中的数据质量问题。
  2. 充分理解问题的影响。
  3. 完全了解数据是从哪里泄露的。
  4. 采取措施解决问题。
  5. 收集经验,这样随着时间的推移,你可以防止问题再次发生。

方法如下:

发现

图片由巴尔·摩西提供。

在您测试了您的数据并将其应用到生产中之后,最重要的是监控和警告您的管道中的异常,换句话说,如果和当数据问题发生时,检测。异常检测是该策略的一个重要部分,尽管主要是被动的。

针对新鲜度、数量和基于分发的问题的自动监控和警报将有助于跟踪您的数据可靠性SLA 和 SLIs ,当您不符合这些要求时,请确保您是第一个知道的人。随着数据管道变得越来越复杂,数据必然会在某一点或另一点中断,能够缩短检测的时间 (TTD)对于数据驱动的企业来说至关重要。

解决

数据可靠性生命周期的第二个阶段是解决,需要您评估损坏的数据对更大的数据生态系统和相应的数据产品的影响,并将问题向下游传达给需要知道的人。理想情况下,整个影响分析过程应该是集中的,并对相应的警报进行适当的分组和标记。

你的团队不仅需要了解事件发生的时间和方式,还需要了解事件的严重性。破坏 CEO 仪表盘的表格不值得像跟踪内部事件页面浏览量的表格一样被关注。对他们一视同仁的成本对你的团队的生产力和整个企业都是有害的。

在循环的这一部分,可以使用端到端的沿袭和统计分析来理解手头问题的根本原因。随着团队越来越擅长理解、分类和响应事件,他们可以减少解决常见或预期数据事件的https://www.montecarlodata.com/data-quality-you-are-measuring-it-wrong/#:~:text=Time-to-resolution%20(TTR)(TTR)时间。

预防

设置数据治理仪表板(如 Looker 中的这个)可以帮助数据团队更好地了解数据问题和随时间的变化,以便他们可以采取主动措施来防止未来的事件。图片由作者提供。

数据可靠性生命周期的预防阶段处理您先前的知识和有关数据管道的历史信息,并将其转化为可操作的预防性步骤。例如,并不是你的工程师公司对现有数据集进行的每个模式更改都意味着你的管道着火了;事实上,模式更新通常意味着发展和进步。然而,如果没有适当的上下文,数据可靠性生命周期的检测阶段(换句话说,异常检测)将无法挑选这些队列。

你对病因了解得越具体越好。让我们回顾一下我们的汽车修理工——听起来很傻,对吗?但是就像说“我的车坏了,你能修好它吗?”给你可怜的机械师一点工作燃料(双关语),告诉一个数据工程师“我的财务报告中有空值,你能修复它吗?”几乎没有帮助。如果没有与损坏报告的根本原因相关的所有详细信息,可能需要几个小时甚至几天的时间来了解问题的原因,并相应地防止未来的事件。

通过应用数据可靠性生命周期,团队应该能够显示日志、元数据和关于数据的查询,以衡量甚至预测数据健康状况。更高级的生命周期还应该能够自动调整和更新测试和监视器,以匹配不断发展的业务逻辑,进而减少 数据停机

数据质量的未来是前瞻性的

机械师和软件工程师并不是唯一受益于对他们的工艺采取积极的、迭代的方法的人。

通过应用 DevOps 的类似原则,数据团队可以更好地协作来识别、解决甚至从一开始就防止数据质量问题的发生。在接下来的几个月里,我很高兴看到这种方法如何发展,以及数据系统如何成熟,变得更具弹性和可靠性。

下次你见到你的机械师时:谢谢她。你不知道她经历了什么!

有兴趣了解更多关于数据可靠性生命周期的信息吗?伸出手去 巴尔摩西 或者剩下的 蒙特卡洛团队

基于极值分析的异常检测

原文:https://towardsdatascience.com/anomaly-detection-with-extreme-value-analysis-b11ad19b601f?source=collection_archive---------15-----------------------

使用极值理论解释异常检测结果

伊恩·西蒙兹在 Unsplash 上拍摄的照片

极值分析(EVA)是一种统计方法,主要用于估计与之前观察到的事件相比最为罕见的事件的概率。应用领域一般是工程、气象、水文、金融和海洋学。事实上,这是一种存在于不同年代的方法,用于处理参考分布的极端偏差。

我们能做的是更进一步。我们在异常检测的应用中评估我们的深度学习模型期间使用 EVA。我们并不试图重新发明任何东西,我们只是使用极值理论来为我们的监督方法的结果提供额外的解释。我们的方法与算法无关,可以很容易地推广或修改,以适应每个建模管道。

在这篇文章中,我们开发了一个基于深度学习结构的时间序列预测应用程序。在正确验证之后,我们通过 EVA 来检验预测的可靠性。我们检查在选定的控制周期内产生的残差,以确定它们有多“极端”,以及它们在未来会有多“频繁”。生成关于这些方面的统计数据和置信度允许我们指出异常情况,即不由我们的深度学习框架建模的情况,在未来会如何揭示。

数据

我们使用来自温哥华开放数据目录的数据集。在 Kaggle 上可以很容易地访问它,它报告了从 2003 年到 2017 年在加拿大城市登记的超过 5 亿份犯罪记录。我们只关注日常犯罪的汇总系列。如下图所示,这个系列看起来噪音很大。

每日汇总犯罪的原始系列(图片由作者提供)

然而,在每周和每月的水平上可以检测到某种形式的季节性。

犯罪系列的季节性水平(图片由作者提供)

模型

我们使用这些信息建立一个预测模型,根据过去的观察结果预测未来几天的犯罪。针对这种任务,我们开发了 Seq2Seq LSTM 自动编码器。它的结构非常适合对我们处理的数据进行建模,因为我们可以将原始时间信号与时间嵌入的创建相结合。编码器被输入数字输入序列(目标历史序列)加上历史工作日和月份的数字嵌入。解码器接收解码器输出加上未来工作日和月份的数字嵌入,以产生 7 天预测。

通过 keras-hypetune一种仅基于 NumPy 的 keras 超参数调整的简单框架,利用网格搜索方法优化验证集上的参数,从而计算训练过程。

下面描述了我们的验证集生成的预测,以及为每个未来时滞计算的相应性能。我们的参考基线由最后一次有效观察的虚拟重复构成。

不同时间范围的现实与预测(图片由作者提供)

在不同时间范围内计算的 MSE(图片由作者提供)

极值分析

在这一点上,我们的模型的一个优化版本已经被训练好并可以使用了。我们现在要做的是运用 EVA 的一些典型技术来解释这些性能。在我们的实验中,术语“验证集”用于以更一般的方式定义“控制组”,即用于操作调整和 EVA 应用的时间间隔。

我们感兴趣的第一个要素是我们的模型在验证集上生成的残差。我们认为残差的绝对值是极值。这种选择是合理的,因为它们代表了我们的预测模型表现不佳的未知情况。换句话说,我们的模型倾向于错误的情况还没有被研究,因此被标记为异常事件。异常的程度是通过与现实和预测的距离来衡量的。EVA 的标准方法包括将高于固定阈值的所有观察值(峰值超过阈值)识别为异常/极端,或者构建将原始序列分成块的最大值序列(块最大值方法)。方法的选择与领域相关,可以产生不同的结果。当然,从模型残差中导出极值可以给我们信心来处理平稳序列,这是 EVA 的先决条件。

左起:应用于残差序列的块极大值法;在每个区块中检测到的一系列最大值点(图片由作者提供)

第二步是建立极值模型。我们希望分配这些值来自的参考分布。根据之前对如何选择异常/极值所做的选择,我们有不同的候选分布可供选择。对于我们的工作,我们选择块最大值方法,块的长度为 30 天。通过这种方法,我们有信心认为极端事件来自广义极值(GEV)或 Gumble 分布。该选择可以通过执行最大似然估计进行数学计算,从中我们选择具有最佳参数的最佳分布。

异常/极端情况的经验分布和估计分布(图片由作者提供)

有了这些零件,我们就可以开始下一步了。我们只需要选择一些时间步长,技术上称为返回周期,在此基础上计算相对返回值。对于每个返回周期,我们可以预期至少会看到一个实例超过估计的返回值。给定 t 作为一个重现期,一个预期的返回值( E) 意味着在接下来的 t *30 天内,我们可以预期看到一些值以等于 1/t 的概率超过 E 。在我们的具体研究案例中,返回值是我们在未来可能会超过的残差,即我们可以归类为异常的意外事件。

在下面的图中,我们可以显示未来的模型化返回值,以及我们的验证集的观察到的最大值。附表更正式地总结了我们的发现。例如,第一行表示,在接下来的 30 天内,我们有望(以相对较高的概率)获得超过我们模型预测值 21 个绝对点的观测值。同样,最后一行表示,我们可以预期(以相对较低的概率)在接下来的 3000 天内,我们的模型预测会有超过 53 个绝对点的观测值。使用自举统计来计算置信区间。

t+1 时残差的返回值图。红色表示每个区块中检测到的一系列最大值点。蓝色表示预期返回值和相对引导区间(图片由作者提供)

t+1 时残差的返回值表(图片由作者提供)

所解释的程序适用于一步预测的残差,但同样可以计算其他预测范围。

t+7 时残差的返回值图。红色表示每个区块中检测到的一系列最大值点。蓝色表示预期返回值和相对引导区间(图片由作者提供)

t+7 时残差的返回值表(图片由作者提供)

摘要

在这篇文章中,我们提出了一个时间序列预测任务。首先,我们为多步预测建立了一个 Seq2Seq 模型。其次,我们试图结合极值理论中的一些技巧对我们的模型作进一步的解释。我们利用这种组合来解释正常数据流中可能出现的异常,这是我们的模型无法识别的。

查看我的 GITHUB 回购

保持联系: Linkedin

Python 中基于 GESD 的异常检测

原文:https://towardsdatascience.com/anomaly-detection-with-generalized-extreme-studentized-deviate-in-python-f350075900e2?source=collection_archive---------7-----------------------

理解 GESD 并用 Python 实现它

来源: Unsplash

异常检测在各种各样的用例中扮演着非常重要的角色。通常,异常数据可能与某种问题或罕见事件有关,例如银行欺诈、医疗问题、结构缺陷、设备故障等。这种联系使得挑选出哪些数据点可以被视为异常变得非常有趣,因为从业务角度来看,识别这些事件通常非常有趣。

此外,许多统计技术对异常值的存在很敏感。例如,平均值和标准偏差的简单计算可能会被一个非常不准确的数据点扭曲。检查异常值应该是任何数据分析的常规部分,并且应该检查潜在的异常值以查看它们是否可能是错误的

什么是异常检测?

异常检测是发现数据集异常值的任何过程;那些不属于这里的东西。这些异常可能指向不寻常的网络流量,发现某个传感器出了故障,或者只是在分析之前识别出要清理的数据。

有各种技术可用于根据不同的方案或方法识别异常,如图形(箱线图、散点图)、基于距离的方案(最近邻法、聚类算法)、统计方法(GESD、基于四分位数的技术)等。每种方案都有其优点和缺点,其功效取决于使用案例。

在本文中,我们将重点关注 GESD(广义极端学生化偏差),并用 python 实现一个简单的示例,以更好地理解它的工作原理。在我开始之前,我想承认这篇文章在很大程度上受到了我的探索性数据分析教授 Edward McFowland 和 Bhavesh Bhatt 在 YouTube 上的机器学习和统计教程的启发。

什么是 GESD?

GESD 是一种简单的统计方法,用于检测符合近似正态分布的单变量数据集中的一个或多个异常值。统计方法假设常规数据遵循某种统计模型,不遵循该模型的数据是异常值。

GESD 克服了格拉布斯检验和蒂特金-摩尔检验的主要限制,即异常值的可疑数量 k 必须精确指定。如果没有正确指定 k ,这可能会扭曲这些测试的结论。GESD 检验只要求指定异常值可疑数量的上限。

给定上限 r ,广义 ESD 测试本质上执行 r 单独的测试:一个异常值的测试,两个异常值的测试,等等,直到 r 异常值。

广义 ESD 测试是针对以下假设定义的:

H0:数据集中没有异常值

高可用性:数据集中有多达 r 个异常值

我们的测试统计由下面的公式给出:

GESD 的检验统计

这里,x_bar 和σ分别表示样本均值和样本标准差。

在 GESD,我们移除最大化| xix_bar 的观察,然后用 n-1 个观察重新计算上述统计。我们重复这个过程,直到 r 个观察值被去除。这导致 r 统计数据 R1,R2 ……,Rr。通过编码示例,该过程将变得更加清晰。

对应于 r 测试统计,计算以下 r 临界值:

临界值计算

其中 tp,ν 是从具有 ν 自由度的tt分配的 100 p 百分点

我们的显著性水平将由α表示。

通过找到最大的 i 来确定异常值的数量,使得 Ri > λi

Rosner 的模拟研究表明,这个临界值近似值对于 n ≥ 25 非常精确,对于 n≥15 相当精确。

请注意,尽管广义 ESD 本质上是按顺序应用的格拉布斯测试,但还是有一些重要的区别:

  • 广义 ESD 测试根据测试的异常值数量对临界值进行适当的调整,而连续应用 Grubbs 测试则不会。
  • 如果存在明显的掩蔽,连续应用格拉布斯检验可能会过早停止。

现在为了巩固理论部分,让我们用 Python 实现 GESD,以了解它实际上是如何工作的:

由于这是一个例子,我们将首先模拟我们的数据。这里我们创建 100 个 0 到 1 之间的随机值。我们的数据散点图如下所示。

**

现在,我们会故意在数据中放一些异常值进行识别。

**

有异常值的数据

现在,我们将创建单独的函数来计算测试统计数据和临界值

计算测试统计的函数:

计算临界值的函数:

该函数将所有内容汇集在一起,并执行 r 检验来确定异常值的数量。对于每次迭代,我们使用上面的函数来计算使|Xix _ bar|最大化的检验统计量,然后计算相应的临界值,然后在下一次迭代中从我们的数据中移除观察值。

对我们的数据调用具有 7 个异常值上限且显著性水平为 5%的函数会产生以下结果:

我们可以看到执行了 7 项测试。通过找到最大的 i 来确定异常值的数量,使得RiλI。在本例中,测试统计值大于临界值(在 5 %的水平)的最大异常值数是 3。因此,我们得出结论,在这个数据集中有 3 个异常值。

下面是完整的代码:

这就是 GESD 在 python 中的实现。我希望这篇文章将有助于您实现这个简单而有效的方法来发现数据中的异常值。更多项目请查看我的 Github 简介。

参考文献:

bmc| 机器学习的异常检测:Jonathan Johnson 的介绍

*https://datascience904.wordpress.com/2019/10/14/anomaly-detection-using-pca/

https://www . ITL . NIST . gov/div 898/handbook/EDA/section 3/EDA 35h 3 . htm*

异常和欺诈检测

原文:https://towardsdatascience.com/anomaly-fraud-detection-a-quick-overview-28641ec49ec1?source=collection_archive---------16-----------------------

可用方法的快速概述

这真的很反常。—Ibrahim Boran在 Unsplash 上拍摄的照片

异常检测、欺诈检测和异常检测是人工智能领域常见的术语。虽然有不同的术语,并在脑海中暗示不同的图像,但它们都归结为同一个数学问题,简而言之就是,在许多条目中检测似乎不属于那里的条目的过程。

例如,作为异常检测的一个用例,信用卡/借记卡欺诈检测是检查传入的交易请求是否与用户以前的档案和行为相符的过程。以此为例:乔是一个勤奋的人,在纽约附近的一家工厂工作。每天他从当地的咖啡馆买一杯咖啡,去上班,买午餐,在回家的路上,他有时会去买些杂货。他用自己的卡支付账单,偶尔会在休闲、餐馆、电影院等地方花钱。

照片由rupixen.com在 Unsplash 上拍摄

一天,一个交易请求被发送到 Joe 的银行帐户,要求他在德克萨斯州奥斯汀附近的一家必胜客支付 30 美元。不知道这是 Joe 在度假还是他的卡丢失了,这看起来像是异常交易吗?是的。如果有人开始在“持卡人不在场”的基础上用乔的账户支付 10 美元的账单,例如在线支付,该怎么办?银行机构想要停止这些交易,并通过短信或电子邮件向 Joe 核实这些交易。

这些都是肉眼可以识别的异常交易的明显例子。但是正如每一个统计问题一样,总有不平凡的情况。我们如何探测它们是我想谈论的问题。

请注意,异常检测问题没有确定的答案,答案是随机的,并且总是取决于我们查看数据的角度。

方法

我将解决这个问题的数学方法分为三类:容易解释的统计方法,有点解释的经典机器学习方法,以及黑盒深度学习方法。

特征工程

特征工程的过程建立了我们观察数据的框架。它定义了我们认为什么是重要的,它是我们将直觉引入模型的过程。这一步在很大程度上取决于手头的问题,但我将针对我们之前讨论过的银行账户示例深入探讨这个问题。什么可以帮助我们确定乔是乔还是他的卡或在线凭证丢失了?这里有一个提示性的列表:

  1. **这一天花掉的钱的总数:**看着这个,并将其与乔每天通常的总数进行比较,模型将了解乔通常的行为。
  2. 当日交易笔数
  3. 金额
  4. 一天中的某个小时
  5. 位置

引用贝森等人的这本书。艾尔。,

欺诈性账户活动可能涉及在短时间内尽可能多地消费。

这些是一些例子,需要银行机构从他们关于 Joe 的数据库的不同表中得到。当然,每个问题还有许多其他可能的特征,试着为你的情况找到它们。

特征工程是重要的一步—照片由 airfocus 在 Unsplash 上拍摄

统计方法

这个名字可能有点误导,因为我们要做的一切都是统计方法,对吗?但是在这里,我将重点放在简单的统计数据上,这些数据可以在 5 分钟内解释给利益相关者,他们可能不理解复杂的方法。这些方法的一个缺点是它们不能处理分类数据,如一天中的小时特征。因此,为了实现它们,我建议将它们分别应用于每个类别。所以我们会比较日间交易和隔夜交易。

  1. 修改后的 Z 值:

Z-score 背后有一个非常简单的想法,这个数据点距离别人的均值有多少标准差?它越高,数据点越异常。这个定义有局限性,它假设数据是正态分布的,容易出现异常值,如果 Joe 决定偶尔比平时多花一点钱,就会触发异常值。因此,我们将目光转向修改后的 Z-score,这也是由伊格莱维奇和霍格林推荐的。

修改后的 Z 分数使用中位数绝对偏差,定义如下:

修改的 Z 分数

作者建议将修改后的 Z 值为 3.5 或更高的点标记为异常。

2.四分位距:

正如在箱线图可视化中看到的,一个范围内的数据分布可以用四分位数来可视化,这里有一个很好的描述。

在这种方法中,第一个和第三个四分位数之间的点是正常点,它们之外的点将被标记为异常点。例如,您可以将其修改为“整数”范围,其中您可以使用第 n 个和第 m 个整数来标记数据,而不是四分位数。

3.柱状图箱:

直方图作为一种著名的数据可视化方法,也可以用于离群点检测。通过计算每个样本的箱并获取直方图,我们可以将异常点标记为异常。这在某种程度上与 Z 得分指标相关。

机器学习方法

与上面描述的方法相比,ML 方法要复杂得多,有点复杂,并且能够处理分类数据(通过预处理方法,例如一键编码,例如 Pandas 中的 get_dummies )。

  1. k 近邻

kNN 是一种广泛使用的 ML 算法,其背后的基本逻辑如下:“相似的观察值彼此接近,而离群值通常是孤立的观察值”。利用这一点,我们可以检测出一个高维特征空间中最“孤独”的点。在 sklearn 库中有很大的涵盖。通过计算每个数据点与其他数据点的平均距离,我们可以设置一个阈值,将它们中的一定比例归类为异常,甚至可以对这些距离运行 Z 评分,并找到异常值。

2.一级 SVM

支持向量机是 ML 工具包中的强大武器。简而言之,它们是特征空间中的超平面,将点分成不同的类。在异常检测的上下文中,一类支持向量机学习什么是“正常的”,并基于此检测异常值和异常数据。这里的是一篇关于其背后的数学的彻底而完整的文章。sklearn 的 SVM 工具包中有的一类支持向量机。

3.数据库扫描

DBSCAN 是一种无监督算法,可以检测空间中密集的区域,并将低密度区域中的数据点标记为异常。这是一种广泛使用的聚类方法,有两个超参数需要调整:Epsilon 和 min_samples,如 sklearn 的实现中所定义。

Epsilon 是数据点相互之间应该有多接近才能成为一个聚类的一部分的度量,min_samples 是一个聚类中的最小点数。

  1. LOF(局部异常因素)

虽然与 kNN 和 DBSCAN 的逻辑相似,但 LOF 为每个数据点分配了一个指标(LOF ),正常点的得分在 1 到 1.5 之间,而异常点的得分更高。在 sklearn 中也有。

4.隔离森林

隔离森林或 iForest 是一种非常强大的,可能是大数据中最好的方法,用于异常检测的工具。它很容易扩展,这里有一个很好的解释。这是大数据异常检测的必由之路。

随机森林!—Jay Mantri在 Unsplash 上拍摄的照片

深度学习方法

最后,在花哨的人工智能领域,我们最终得到了黑盒,它们出于我们不知道的原因表现良好,并根据我们无法解释的原因来判断数据。最著名的 DL 异常检测方法是使用自动编码器网络。

自动编码器

自动编码器是由两个,实际上是三个部分组成的网络:编码器,潜在空间,解码器。用数学术语来说,自动编码器学习数据集上的恒等函数(简单地说: f(x)=x )。假设我输入了一大组我认为没有欺诈性的 Joe 的交易。网络训练取事务 a编码潜在,低维空间,然后解码回基数等于输入空间的空间。例如,具有 100 个特征的输入将被减少到具有 30 个特征的潜在空间(这是编码),然后返回到 100 个特征的表示。神经网络训练最小化输入和输出之间的差异。

来自:https://towards data science . com/applied-deep-learning-part-3-auto encoders-1c 083 af 4d 798

所以它基本上学会了回馈它收到的任何东西。现在,在训练阶段之后,如果我们显示它的正常入口,网络将能够以低误差重建它,因为它与我们训练网络所用的相似。但是如果我输入一个稍微不同的条目呢?重构误差会更高,这意味着网络还不能很好地重构它。我们可以基于这个度量来决定一个数据点是否异常。

这是对著名的、可用的异常检测方法的快速回顾。我将在以后更深入地研究其中的一些,并提供一个在样本数据集中检测欺诈的实际例子。如果你有什么想法,请告诉我。

用 hashlib 匿名化 Pandas DataFrame 列中的敏感数据

原文:https://towardsdatascience.com/anonymise-sensitive-data-in-a-pandas-dataframe-column-with-hashlib-8e7ef397d91f?source=collection_archive---------6-----------------------

停止在您的数据框架中共享个人身份信息

马库斯·斯皮斯克在 Unsplash 上的照片

数据科学家遇到的一个常见情况是与其他人共享数据。但是,如果这些数据包含电子邮件地址、客户 id 或电话号码等个人身份信息(PII ),您该怎么办呢?

一个简单的解决方案是在共享数据之前删除这些字段。然而,你的分析可能依赖于 PII 的数据。例如,电子商务交易数据集中的客户 id 是了解哪个客户购买了哪个产品所必需的。

相反,你可以使用散列法来匿名化数据中的 PII 字段。

哈希是什么?

哈希是将明文字符串转换为固定长度的唯一字符串的单向过程。哈希过程有两个重要特征:

  1. 将散列字符串转换成其原始形式是非常困难的
  2. 相同的明文字符串将产生相同的哈希输出

出于这些原因,开发人员会将您的散列密码存储在网站的数据库中。

一个使用 hashlib 的简单例子

haslib 是 Python 中的内置模块,包含了很多流行的哈希算法。在我们的教程中,我们将使用 SHA-256,它是 SHA-2 算法家族的一部分。

在将我们的字符串(在本例中为电子邮件地址)转换为哈希值之前,我们必须首先使用 UTF-8 编码将其转换为字节:

import hashlib# Encode our string using UTF-8 default 
stringToHash = 'example@email.com'.encode()

我们现在可以使用 SHA-256 散列它:

# Hash using SHA-256 and print
print('Email (SHA-256): ', hashlib.sha256(stringToHash).hexdigest())

输出:

Email (SHA-256): 36e96648c5410d00a7da7206c01237139f950bed21d8c729aae019dbe07964e7

就是这样!我们的假电子邮件地址已被成功散列。

使用 pandas 和 hashlib 的完整示例

既然我们可以将 hashlib 应用于单个字符串,那么将这个例子扩展到 pandas 数据帧就相当简单了。我们将使用信用卡客户数据,这些数据可以在 Kaggle 上获得,最初是由Analyttica treasury hunt LEAPS提供的。

**场景:**你需要共享一个信用卡客户列表。您希望保留字段“CLIENTNUM ”,因为一个客户可以拥有多张信用卡,并且您希望能够唯一地标识它们。

import pandas as pd# Read only select columns using pandas
df = pd.read_csv('data/BankChurners.csv', usecols=['CLIENTNUM', 'Customer_Age', 'Gender', 'Attrition_Flag', 'Total_Trans_Amt'])df.head()

作者图片

在将我们的“CLIENTNUM”列转换为 string 数据类型之后,我们可以使用 pandas .apply()散列该列中的所有字符串:

# Convert column to string
df['CLIENTNUM'] = df['CLIENTNUM'].astype(str)# Apply hashing function to the column
df['CLIENTNUM_HASH'] = df['CLIENTNUM'].apply(
    lambda x: 
        hashlib.sha256(x.encode()).hexdigest()
)

祝你在尝试破解我们新创建的专栏时好运。

作者图片

结论

完成本教程后,你应该对哈希算法有一个基本的了解。我们看到了如何使用 hashlib 散列一个字符串,以及如何将它应用于 pandas DataFrame 列来匿名敏感信息。

你有什么问题吗? 推我 或者在 LinkedIn 上加我。

你可以在 GitHub 上找到这篇文章中用到的所有代码。

匿名数据无用:事实还是虚构?

原文:https://towardsdatascience.com/anonymized-data-is-useless-fact-or-fiction-b8ab087d5c1e?source=collection_archive---------31-----------------------

思想和理论

挖掘非结构化数据

来源:https://unsplash.com/photos/qm1zk-2oWD4

“匿名化什么时候有用?”是一个棘手的问题,因为答案高度依赖于数据类型和任务。匿名化数据集正被用于众多领域的学术研究、工业研究和现实产品,由于数据的高度敏感性和实用性,临床研究往往处于领先地位。2016 年 NIST 的一个演讲提到了匿名数据有用的其他几个用例,包括:

  • 改进方向和交通数据的驾驶解决方案。
  • 坑洞警报。
  • 公布教育记录。
  • 提交给联邦航空管理局的自愿安全报告。

虽然已经对结构化数据匿名化的适当方法进行了多年的研究(特别是在医疗领域),但对非结构化数据匿名化的研究才刚刚开始。在这篇文章中,我们将深入研究语音、图像/视频和文本匿名化领域。

演讲

对于语音,匿名化意味着:

(1)使说话者的声音不可识别(例如,使用在使用 X 向量和神经波形模型的说话者匿名化中提出的方法)和

(2)通过发出哔哔声或替换它们(即,假名化)从语音中去除直接和准标识符。

如果您还没有阅读“揭秘去标识化”或“数据匿名化:来自一位前怀疑者的观点”的话,请快速提醒您,直接标识符是直接标识个人的实体(全名、确切位置、社会保险号等)。)并且准标识符是当组合在一起时能够以指数可能性识别个体的实体(年龄、近似位置、口语等)。).

如果你对语音技术和隐私感兴趣,可以看看语音隐私倡议和 ISCA 语音通信安全和隐私特别兴趣小组,该小组聚集了来自各种背景(从信号处理到法律)的专业人士来讨论语音技术中的隐私问题。

图像和视频

鉴于可识别信息的多样性,图像和视频中的匿名化是一项复杂的任务。虽然完全并适当地模糊照片中的整个人体可能会对某些受限的使用情况产生影响,但背包上的姓名标签、不同的午餐盒、背景中的房子等仍然存在重新识别的风险。然而,这些媒体的匿名化往往只是意味着删除或替换人脸,这意味着它仅限于身体的特定部位,而不是将重新识别的风险降低到几乎为零(例如,人脸匿名化——例如,参见 CIAGAN:有条件身份匿名化生成对抗网络)。这是一个开始,但考虑到像 Palantir Technologies 这样的公司可以通过纹身来识别人,移除或替换身体的一部分通常只能称为编辑,而不是匿名化。

也就是说,有许多机器学习任务使用没有个人数据的图像和视频,或者其中的个人数据是多余的,可以在不损害任务的情况下删除/替换,包括:

  • 农业气象学
  • 动物图像分类
  • 卫星图像理解
  • 车辆计数

就拿车辆计数 GitHub repo 中提供的车辆计数的例子来说吧。

图片来源:https://github.com/ahmetozlu/vehicle_counting_tensorflow(麻省理工学院许可)

很明显,在这项任务中,车牌和人脸都不起作用。如果我们担心独特的汽车颜色太明显,即使是黑白视频也可以做得很好,就像在边缘计数车辆一样(例如,在数据到达任何服务器之前,直接在摄像机上)。

以另一张图片为例:

来源:https://unsplash.com/photos/EJnhyLTbPH8

你能从图像中发现什么?

  • 房屋数量
  • 农田类型
  • 减轻
  • 天气

有许多关于地形的可用信息,也有许多类似的图像可用于确定生态系统的健康状况、作物中是否有杂草生长等。更不用说许多匿名视频可以作为自动驾驶汽车的部分训练集。

文本

最后,让我们考虑一下文本匿名化。已经有一些关于文本的重新识别风险分数的初步研究,包括我们在《数据保护和隐私杂志》上发表的题为关于非结构化数据去识别的推理的研究(如果你很难访问该论文,请给我发电子邮件)。虽然出于数据发布目的的适当匿名化需要专家检查数据并计算重新识别的风险,但我们可以说,自动编辑文本在通过数据最小化(即,将您收集的个人数据量减少到仅必要的数量)大大提高数据安全性方面发挥了巨大作用。请注意,已经对统计和基于规则的系统自动去识别医学文本语料库的有效性进行了测试(这些研究中的三项在此处进行了总结)。这些测试必须重新进行,以说明统计自然语言处理系统在过去三年中的巨大改进。

有趣的是,让我给你举一个简单的例子,说明一封匿名邮件可以携带多少信息:

“你好[姓名],

抱歉,它已经在我的垃圾邮件中结束了!

我明天[时间]有预约,但是[时间]可以。届时,我将发送更新的邀请。如果这对你不起作用,请告诉我。

谢谢你,

[姓名]"

知道是谁写的吗?除非你是收件人或作者,否则不可能知道。

但是你能从这封邮件中收集到什么有用的信息呢?

  • 一个电话被重新安排到明天
  • 发送者很有礼貌(说请和谢谢)
  • 收件人的前一封电子邮件本不应该出现在垃圾邮件文件夹中,但却出现了!

比如说,电子邮件服务提供商可以用这些信息做什么?嗯,如果他们能确保这个收件人的邮件再也不会出现在垃圾邮件文件夹里,那就太好了。

我有很多这样的例子。从能够识别一个人对特定产品的感觉,到确定谈话中涉及的话题,以及确定消费者在聊天或电话中的情绪。

匿名数据是

社区需要时间和大量的研究来更好地理解数据匿名和有用的意义。正如在差分隐私和匿名化领域一样,在密码学领域也可以感受到迭代技术并理解其局限性的过程。我们不再使用 DES 加密数据,而是使用 AES。很有可能,在未来十年,我们将不得不更多地依赖基于网格的加密技术,而不是 RSA。当我们发现一项技术的局限性时,我们不会把婴儿和洗澡水一起倒掉,而是希望更深入地了解哪里出了问题,对它进行创新,并使它更强大、更有用、更容易使用。

致谢 感谢 John Stocks 和 Pieter Luitjens 对本文早期草稿的反馈。

另一篇先验的文章,但有一点小小的变化…

原文:https://towardsdatascience.com/another-apriori-article-but-with-a-small-twist-ebfac2d639b?source=collection_archive---------30-----------------------

你的老板想知道做一次产品推广的估计财务影响,给了你一大堆交易数据。现在怎么办?

由斯蒂芬·道森在 Unsplash 上拍摄的照片

已经有很多关于先验细节的好文章了。我将主要关注一个现实的商业场景,它可能围绕着先验和市场篮分析的应用出现。

但是对 Apriori 算法做一个快速和高水平的复习是没有坏处的。

买了 X 的也买了 Y.

Apriori 是一种关联规则挖掘算法,它发现哪些项目倾向于与哪些其他项目相匹配。想象一个超市,薯条和蘸酱通常在过道上相邻放置(甚至穿过商店,让你走得更远,可能会冲动购买更多)。

在进行购物篮分析时,Apriori 算法的输出中有三个关键指标需要查看,即支持度、信心度和提升度。

支持 - >既有 A 又有 b 的部分交易

置信度 - >给定 A 出现的次数,A & B 一起买入的频率。

解除 - >表示统治随机几率的强弱。例如,3.2 的提升意味着如果客户购买 A,他们购买 b 的可能性会增加 3.2 倍。

回到你老板要求的场景…

在这个场景中,您有一个包含零售业务交易数据的大文件。你的老板说市场部想通过赠送糖果棒来促销贺卡。您的任务是验证假设并估计财务影响。

我觉得贺卡利润丰厚,我也喜欢糖果。不管怎样,回到场景…

取一个峰值,我们可以看到有 459,258 条记录,最终代表 200,000 笔交易。

项目频率图(来自 R 中的 arulesViz 库)(图片由作者提供)

我这里主要关注的是 有什么影响? “在真实的商业场景中。在这一点上,我将忽略 EDA,以及很多先验部分本身,因为它在其他文章中已经讨论过了,但不是我的主要关注点。

浏览完数据后,你会发现这并不是一个太糟糕的想法。我们看到,几乎 30%的情况下,购买糖果棒的同时还会购买贺卡(信心)。我们还发现,购买贺卡的人购买糖果棒的可能性是其他人的 1.74 倍()。

LHS 和 RHS 分别是“左手侧”和“右手侧”。LHS 是倾向于导致购买 RHS 产品的产品。(图片由作者提供)

按事务数据集中的项目汇总统计信息。(图片由作者提供)

给定按产品的汇总统计数据并浏览原始数据,可以有把握地说这里可能发生两种情况中的一种(可能是组合)。

  1. 这家公司的交易中有相当一部分是大宗销售。
  2. 来自杂项事务的数据中有噪声。
    -例如,一家商店的系统停机了一天,他们输入了一个汇总交易来平衡库存。

这个数据集对于一个简单的例子来说已经足够好了,所以让我们看看糖果和贺卡。

项目摘要表的子集,查看我们感兴趣的内容。(图片由作者提供)

假设

让我们假设:

  • 贺卡的利润非常可观,1 美元买 5 美元卖。
  • 糖果有不错的利润,0.67 美元买进,1 美元卖出。
  • 该数据代表 1 年的交易量。

可以肯定地说,以这样的速度,我们想推动贺卡的销售,可以用糖果棒的销售作为激励。

我们在汇总表中看到,平均贺卡交易有 2.53 张贺卡。

现在最大的问题是:

如果我们能通过“至少购买 3 张贺卡,可获得一个免费糖果棒”的促销活动,将平均贺卡数量从 2.53 张提高到 3 张… 会有什么影响?

平均数 3.0 减去目前的平均数 2.531,得出每笔交易贺卡平均净增加 0.469 张。

我们可以用这个增量,或者说净平均增长,乘以利润来估算贺卡业务的毛利增长。很简单,每张卡 0.469 * 4 美元的利润等于 1.876 美元。

现在我们有进展了。如果我们将平均值提高到 3,我们可以预期每笔交易会再增加 1.876 美元。但是等等,糖果不是免费的(记住,它们的价格是 0.67 英镑),所以我们需要把它考虑进去。这使我们每笔交易的净利润增加了 1.206 美元。

因此,如果我们将其应用于贺卡交易总数(40,292),我们会看到 预计的 净利润增长为 $48,608!

旁注:

当进行这类分析时,如果企业中有人在寻找一个数字,设定期望值很重要。任何时候我被要求计算出一些变化的影响,我总是把它框定为一个估计,并列出导致那个估计的我的假设。大多数时候都明白没有水晶球,但也有可能有人认为有。或者更有可能的是,他们知道这是一个估计,但他们不知道你可能做的任何假设。

因此,起初,这个场景可能看起来令人生畏,尤其是对于初级分析师(没关系!).但是,如果我们把问题分解成更小的块,就会变得容易得多。

结论

感谢您花时间阅读本文。我希望您从中获得了一些有用的东西,可以应用到现实世界的场景中。如果您有任何问题或反馈,请随时发表评论。

如果你喜欢这篇文章,你可能也会喜欢我关于使用 PuLP 解决一个简单优化问题的文章。

https://python.plainenglish.io/python-pulp-optimization-a-simple-logistics-example-4a8f713ba807

继续学习!

你应该知道的另一个 JupyterLab 扩展

原文:https://towardsdatascience.com/another-jupyterlab-extension-you-should-know-about-146371cf0f32?source=collection_archive---------13-----------------------

米托是一个 JupyterLab 扩展,可以轻松地使用 Excel 探索和转换数据集……而且是免费的。

本杰明·戴维斯在 Unsplash 上拍摄的照片

随着所有新的 JupyterLab 扩展的出现,成为数据科学社区的一员真是一个激动人心的时刻。他们通过减少乏味的工作使数据科学变得更加有趣。

我记得在过去,我们必须依赖 numpy 和 matplotlib 作为我们在 Python 中进行探索性数据分析的主要工具。幸运的是,那些日子已经一去不复返了。

您将会明白我所说的“早就过去了”是什么意思,JupyterLab 扩展是本文的主题。

如果你错过了其他关于米托的文章:

  • 米托——生成 Python 的电子表格

认识米托

加载了米托的虹膜数据集(图片由作者制作)

米托是一个免费的 JupyterLab 扩展,可以轻松地使用 Excel 来探索和转换数据集。

米托是我们等待多年的熊猫延伸

当你启动米托时,它显示一个熊猫数据框架的电子表格视图。只需几次点击,您就可以执行任何 CRUD 操作。

CRUD 代表创建、读取、更新、删除

如何开始米托?

劳拉·加里利奥在 Unsplash 上拍摄的照片

使用米托加载数据并显示电子表格视图非常简单:

import mitosheet
import pandas as pdurl = '[https://raw.githubusercontent.com/mwaskom/seaborn-data/master/iris.csv'](https://raw.githubusercontent.com/mwaskom/seaborn-data/master/iris.csv')
iris = pd.read_csv(url)mitosheet.sheet(iris)

米托打开了一个功能强大的电子表格查看器,可以对数据进行过滤、排序和编辑。

按花瓣宽度排序(图片由作者制作)

而且它不仅仅停留在基本的编辑功能上…

数据透视表

只需点击几下,米托就可以创建一个数据透视表。它支持许多常见的聚合,如总和、中值、平均值、计数、唯一等。

带米托的旋转虹膜表( Gif 作者制作)

什么是数据透视表?(来自维基百科)

数据透视表是一个由分组值组成的表,它将一个更大的表中的各个项目聚合在一个或多个离散的类别中。

米托最令人印象深刻的特征是什么?

如果旋转表没有给你留下足够的印象,让你尝试一下米托,我很有信心下面的功能会。

电子表格公式

用米托计算公式(图片由作者提供)

动态公式是 Excel 的杀手锏。对于那些不熟悉编程的人来说,Excel 可以轻松地创建复杂的电子表格。

如果我告诉你**米托以一种“Excel 方式”**支持动态公式会怎么样?这个特性真的让我很惊讶,因为米托背后的团队花了很多开发时间来实现它。

公式引用显示支持的公式(图片由作者制作)

看看下面的 GIF,看看米托的求和公式:

用米托计算虹膜数据集上的求和公式( GIF 作者制作)

可视化数据

由活动创作者在 Unsplash 上拍摄的照片

我们,数据科学家,欣赏简化数据可视化的工具。

起初,pandas 从使用准系统 matplotlib(一个用于数据可视化的强大 python 包)实现了巨大的飞跃。

然后是 seaborn 和plottly,它们可以用 Python 制作出令人惊叹的可视化效果,只需几个命令……这又是一次巨大的飞跃。

…然后是米托,它可以不用写一行代码就能把你的数据可视化。

米托支持条形图、箱线图、直方图和散点图。

在下面的 GIF 中,我做了一个柱状图,x 轴是萼片宽度,y 轴是物种。

用米托可视化数据( GIF 作者制作)

自动代码生成

Joshua Aragon 在 Unsplash 拍摄的照片

米托将你的每个操作转换成熊猫代码,然后你可以和你的同事分享。

此功能的主要目的是在另一个数据集上重复分析。这就像一个熊猫的宏。

对于经验不足的数据科学家来说,这也是一个很好的功能,因为他们可以学习“熊猫方式”进行数据分析。

我点击了一下,米托产生了下面的代码片段:

米托动态自动生成代码(图片由作者制作)

如何安装米托?

米托需要 Python 3.6 或更高版本。

首先,你需要下载米托的安装程序:

python -m pip install mitoinstaller

然后要安装它,只需运行:

python -m mitoinstaller install

如果你有一些安装错误,看看米托的常见安装问题。

结论

由大卫·穆林斯在 Unsplash 上拍摄的照片

JupyterLab 的扩展生态系统已经走了这么远,这让我感到惊讶。最初的扩展笨重,容易出错,难以安装。

时代变了,JupyterLab 的扩展也日趋成熟。米托是这种趋势的一个很好的例子。

我带米托去试驾,几个小时后,我没有看到性能下降(或一些奇怪的错误)。

我将把米托加入我的数据科学工具箱。我计划用它进行初步的探索性数据分析——感受数据。一遍又一遍地输入同一套命令变得单调乏味。

如果你想了解更多关于米托的知识,它有写得很好的文档(和许多教程),这对于这样的扩展来说总是一个好兆头。

在你走之前

如果你喜欢看这些故事,为什么不成为 中等付费会员 ?每月 5 美元,你可以无限制地阅读 10000 个故事和作家。**如果你用我的链接注册,我会赚一小笔佣金。

照片由普里西拉·杜·普里兹在 Unsplash 上拍摄

使用 Python 进行 ANOVA、T-test 和其他统计测试

原文:https://towardsdatascience.com/anova-t-test-and-other-statistical-tests-with-python-e7a36a2fdc0c?source=collection_archive---------5-----------------------

主要分析https://en.wikipedia.org/wiki/Statistical_hypothesis_testing(ANOVA、T 检验、MANOVA 等)。)及其特点,在 Python 中应用。

gyphy.com上疯狂的前 GIF

你的老板有没有让你做一些统计测试,而你不知道从哪里开始?😱

你是不是已经疯了,认为你必须阅读大量的统计书籍,而这些书籍你到目前为止一直能够避免阅读?

这是一个快速指南,将帮助您了解基础知识,选择适合您的测试,以及如何用 Python 实现它们,并提供示例和库。😎

什么是统计测试

统计测试用于假设检验。一般来说,它们可以用于:

  • 确定输入变量是否与输出(目标)变量有显著的统计关系。
  • 估计两组或多组之间的差异。

统计检验的工作原理很简单:他们假设一种特定的情况发生,然后估计这种假设错误的可能性有多大。

统计测试假设一个零假设。零假设对应于所考虑的组(集)之间没有关系或差异的假设。然后,他们确定观察到的数据是在零假设预测值的范围之外还是之内。

还有多重统计测试**,根据要面对的问题和可用数据的特点而有所不同。现在我们将详细介绍它们,然后在 Python 中应用其中一些。**

统计测试的工作原理

统计测试的输出值被定义(具有巨大的想象力😆)一个 测试统计量 —一个描述测试中输入和输出变量之间的关系与原假设有多大差异的数字。

但这不是我们感兴趣的结果。

让我们明白零假设是否成立的是p-值 (概率值)。p-值估计如果零假设为真,您将看到测试统计报告的差异的可能性有多大。

要说 p 值是否显著,我们需要一个称为显著性水平的显著性阈值。这个阈值通常设置为 0.05,但我们不会进入这种无聊的数学家讨论。😉

如果 p 值低于阈值(意味着小于),那么您可以推断出输入和目标变量之间的统计显著关系**。**

否则,那么你就可以推断出预测值和结果变量之间没有统计上的显著关系**。**

让我们举一个简单的例子。我在两组之间进行 T 检验。如果 T 检验的相应 p 值为 0.03,则意味着有统计学意义的关系。只有 3%的概率零假设是正确的(并且结果是随机的)。因此,我们拒绝零假设,接受替代假设。

决定哪种测试适合我们

我们已经说过,统计检验有很多,每一种都只能应用于特定的场景。要确定使用哪种统计检验,您需要知道:

  • 您正在处理的变量类型(分类变量、数量变量等。)

您的数据是否满足某些 假设 (观测值的独立性、方差的同质性、数据的正态性)

统计测试分为两大类:

  • 参数化
  • 非参数化

前者是最“强大”的,因此是推荐使用的,但它们必须尊重上述假设。让我们来详细看看它们:

  1. 观察值的独立性:单个观察值(变量的每个条目)相互独立(例如,对单个患者重复相同的测试会产生非独立的测量值,即重复测量值)。
  2. 数据的正态性:数据服从正态分布。只有定量数据才需要这种假设。(更多详情,另见此处)
  3. 方差的同质性:在所有的组中,被比较的每个组中的方差(即,分数围绕平均值的分布或“分布”)是相似的。如果一组的差异比其他组大得多,这将降低测试识别差异的“能力”。

如果您的数据不满足观测值独立性的假设,您可以使用考虑这种情况的测试(即重复测量测试)。

相反,如果您的数据不满足正态性或方差齐性假设,您可以执行 非参数统计测试 ,这样您就可以在没有这两个假设的情况下进行比较。

指导选择正确统计检验的流程图。【作者图片】

现在我们知道了在各种测试中进行选择的标准,您可以使用下面的流程图来选择适合您的测试。现在无聊的部分结束了**,我们可以看一些代码了。😝**

此外,还有其他的统计测试来代替那些被提议的测试,它们也有同样的功能。为了避免文章太长,它们被省略了。

用于统计测试的 Python 库

收集主要统计测试的最著名和受支持的 python 库有:

  • stats model:一个 Python 模块,为许多不同的统计模型的估计,以及进行统计测试和统计数据探索提供了类和函数。
  • Pingouin:一个用 Python 3 编写的开源统计包,主要基于 Pandas 和 NumPy。
  • Scipy:基于 Python 的数学、科学和工程开源软件生态系统。

测试假设

至于独立性假设,这个必须是你先验知道的,没有办法从数据外推。对于另外两个假设;我们可以用 Scipy (数据可以在这里下载);

from scipy import stats
import pandas as pd# import the data
df= pd.read_csv("Iris_Data.csv")
setosa = df[(df['species'] == 'Iris-setosa')]
versicolor = df[(df['species'] == 'Iris-versicolor')]# homogeneity
stats.levene(setosa['sepal_width'], versicolor['sepal_width'])# Shapiro-Wilk test for normality
stats.shapiro(setosa['sepal_width'])
stats.shapiro(versicolor['sepal_width'])

*****输出:*leven result(statistic = 0.66,pvalue=0.417)

检验不显著(巨大的 p 值),意味着方差是同质的,我们可以继续。

输出: (0.968,0.204)(0.974,0.337)

两个正态性检验都不显著,所以两个变量都没有违反假设。两次测试都很成功。至于独立性,我们可以假设它先验地知道数据。我们可以按计划进行。

T 型测试

为了进行独立 t 检验,我们可以使用 stats.ttest_ind() 方法:

stats.ttest_ind(setosa['sepal_width'], versicolor['sepal_width'])

*****输出:*Ttest _ ind result(statistic = 9.282,pvalue=4.362e-15)

独立 t 检验结果显著(p 值非常非常小)!因此,我们可以拒绝零假设来支持替代假设。

如果您想使用非参数版本,只需将 stats.ttest_ind 替换为 stats.wilcoxon.

方差分析

**为了应用 ANOVA,我们依赖 **Pingouin。我们使用库中包含的数据集:

import pingouin as pg# Read an example dataset
df = pg.read_dataset('mixed_anova')

# Run the ANOVA
aov = pg.anova(data=df, dv='Scores', between='Group', detailed=True)
print(aov)

ANOVA 的结果[来自 Pingouin ]

如我们所见,我们的 p 值低于阈值,因此各组之间存在显著差异!不幸的是,有两个以上的群体,我们无法知道他们中的哪一个是不同的。为了找出答案,你需要成对地应用 T 检验。这可以通过方法 pingouin.pairwise_ttests 来完成。

如果不能确保独立性,使用重复测量 ANOVA** 😗*

pg.rm_anova(data=df, dv='Scores', within='Time', subject='Subject', detailed=True)

重复测量 ANOVA 的结果[来自 Pingouin

马诺娃

在这个例子中,我们回到使用初始数据集。我们将使用宽度和长度列作为因变量。此外,物种列被用作自变量。

MANOVA 目前仅由 Statsmodel 库实现。这个库的主要特性之一是它使用 R-style 公式向模型传递参数。

from statsmodels.multivariate.manova import MANOVAmaov = MANOVA.from_formula('Sepal_Length + Sepal_Width + \
                            Petal_Length + Petal_Width  ~ Species', data=df)print(maov.mv_test())

结果来自马诺娃[图片由作者提供]

这种情况下要考虑的 p 值是相对于输出变量(物种)的 Wilks' lambda 值。正如我们所看到的,即使在这种情况下,它也是重要的。

我们可以认为这个简短的统计测试指南已经完成了。我希望它有助于澄清概念并避免不必要的麻烦。😄

再见,弗朗西斯科

用 Python 进行 ANOVA 测试

原文:https://towardsdatascience.com/anova-test-with-python-cfbf4013328b?source=collection_archive---------1-----------------------

执行 ANOVA 测试的完全初学者指南(带代码!)

照片由乔纳森派在 Unsplash 上拍摄

在之前的文章中,我已经讨论了如何进行单样本假设检验和双样本假设检验。那么,如果我们想比较几个人口平均数呢?在本文中,我将介绍方差分析(ANOVA ),它涉及多个未知μ的比较。

单向方差分析

这是一个测试,其中一个特定的因素有两个以上的群体或利益水平。例如,设μ为毕业生的真实平均年薪
单因素利益=研究对象
假设我们有 6 类研究对象,Factor levels = athematics and Statistics, Economics and Finance, Environmental Sciences, Political Science, Social Sciences and Biology.
因此,在影响毕业生年薪均值方面,有 6 个层次或群体的**single factor**

单向方差分析的基本思想是从每组中抽取独立的随机样本,然后计算每组的样本均值。之后,将组间样本均值的变化与组内的变化进行比较。最后,根据测试统计数据决定各组的平均值是否相等。

平方和

在单向 ANOVA 表中:
可变性的总量来自两个可能的来源,即:
1。各组间的差异**,称为处理 (TR)
2。组内的差,称为误差 (E)**

由于治疗( SSTR )的平方和以及由于误差( SSE )的平方和列在单因素方差分析表中。SSTR 和上证的总和等于平方和的总和( SSTO )。

就像 SS 一样,d.f. (SSTO) = d.f. (SSTR) + d.f .(上证)

均方误差(毫秒)

均方差是平方和除以其方差。这些均方差都是方差,将用于所有群体均值相等的假设检验。

单向方差分析假设检验的假设

  • 从人群中随机选择样本数据,并随机分配给每个治疗组。因此,每个观察都是独立于任何其他观察的——随机性和独立性。
  • 常态。假设每个抽样组中的值来自正态分布的总体。我们可以用正态概率图或者 Q-Q 图来检验正态性。
  • 方差齐性。所有的 c 组方差都相等,即σ₁ = σ₂ = σ₃ = … = σ𝒸。根据经验,如果最大样本标准偏差与最小样本标准偏差的比小于 2 ,我们认为满足等标准偏差假设。

单向 ANOVA 检验的简单概述:

两个以上均值的差异检验
H₀: μ₁= μ₂ = μ₃ = … = μ𝒸
H₁:并非所有的μᵢ's 都相等,其中 i = 1,2,3,…,c.
显著性水平= α

最后,单因素方差分析表如下所示:

假设一个 students.csv 数据集包含 8239 行,每行代表一个特定的学生,还有 16 列( stud.id、姓名、性别、年龄、身高、体重、宗教、nc.score、学期、专业、辅修、score1、score2、online.tutorial、已毕业、薪水),每一列对应于与该特定学生相关的一个特性。在 5%的显著性水平上,不同研究主题的毕业生的平均年薪有显著差异吗?有 6 个不同的研究主题。

数据探索和准备

从给定的数据集中,我们首先需要筛选出已毕业的学生,并进行随机抽样。在我们的例子中,我们将使用 Python 中的函数random.sample从数据集中随机抽取 500 名学生。之后,我们将我们的数据集归入两个感兴趣的变量,分类变量major和数值变量salary

正态假设检验

在我们进行假设检验之前,我们检查是否满足单向 ANOVA 假设检验的假设。样本是随机和独立的样本。现在,我们通过绘制每个分组变量的正态概率图( Q-Q 图)来检验正态假设。

图 1:每个分组变量的 Q-Q 图

如果是正态分布,Q-Q 图显示的是一个很大程度上的直线模式。从上图中,我们可以假设每组的数据大致落在一条直线上。

方差假设的同质性检查

最大与最小样本标准偏差之比为 1.67。这小于阈值 2。因此,我们得出结论,这些假设已经实现。

假设检验

按照假设检验的五步流程:
H₀: μ₁= μ₂ = μ₃ = … = μ₆
H₁:根据 f 检验统计,并非所有的薪酬均值都相等
α = 0.05
:

结论:我们有足够的证据表明,在 5%的显著性水平上,不同学科的毕业生的平均工资并不相同。

推荐阅读

https://levelup.gitconnected.com/how-to-perform-one-sample-hypothesis-tests-with-python-308eae8789fc https://levelup.gitconnected.com/two-sample-hypothesis-tests-with-python-43e1b8c52306

参考

[1]“单因素方差分析 SOGA 地球科学系。”【在线】。可用:https://www . geo . fu-Berlin . de/en/v/soga/Basics-of-statistics/ANOVA/One-way-ANOVA/index . html

[2]“单因素方差分析假设检验 SOGA 地球科学系。”【在线】。可用:https://www . geo . fu-Berlin . de/en/v/soga/Basics-of-statistics/ANOVA/One-way-ANOVA-Hypothesis-Test/index . html

方差分析和 Bonferroni 校正

原文:https://towardsdatascience.com/anova-vs-bonferroni-correction-c8573936a64e?source=collection_archive---------4-----------------------

防范 1 类错误

来源:照片由 mohamed_hassan 拍摄,来自 Pixabay

进行假设检验会带来获得 1 型或 2 型错误的风险。

  • 第一类错误:拒绝一个真正的零假设
  • 第二类错误:接受错误的零假设

当分析不同的组时,单向 ANOVA 可以告诉我们这些组之间是否存在统计上的显著差异。

然而,它不能告诉我们哪个群体不同于另一个群体。此外,当一次执行多个假设检验时,获得类型 1 错误的概率增加。

在统计学中,这被称为家族错误率,它衡量在任何特定假设检验中出现第一类错误的概率。

其计算方法如下:

1 — (1-α)^n

其中:

α =给定假设检验的显著性水平

n =测试总数

例如,如果我们使用 0.05 的显著性水平,并进行三次假设检验,则出现 1 类错误的概率增加到 14.26%,即1-(1–0.05)= 0.1426

为了防止这种 1 型错误(并且同时在每组之间进行成对 t 检验),使用 Bonferroni 校正,从而调整显著性水平以降低犯 1 型错误的概率。然而,这个测试的一个缺点是,犯第二类错误的概率也会增加。

酒店收入:分析分销渠道的平均每日价格

对于这个例子,让我们考虑一个酒店,该酒店已经收集了关于其每个顾客的平均每日价格的数据,即,顾客在该酒店住宿每天支付的平均价格。

酒店也有关于每个顾客的分销渠道的信息,即公司直接TA/TO 。分析的目的是确定这三组中每一组的 ADR 均值之间的差异。原始数据来源于 Antonio、Almeida 和 Nunes (2019 年),如下所述,从每个分销渠道随机选择 100 个样本。

单向方差分析

让我们从在 r 中进行单向方差分析开始。

> model <- aov(ADR ~ DistributionChannel, data = data)
> summary(model)
                     Df  Sum Sq Mean Sq F value   Pr(>F)    
DistributionChannel   2  236636  118318   33.33 8.76e-14 ***
Residuals           297 1054469    3550                     
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

分析结果时,我们可以看到 p 值非常显著,几乎为零。这意味着我们拒绝每组之间不存在显著差异的无效假设。

然而,我们可以看到 ANOVA 测试仅仅表明三个分销渠道之间存在差异,它并没有告诉我们任何关于这种差异的本质。

在进行成对 p 检验之前,这里有一个箱线图,说明三组之间的差异:

来源:RStudio 输出

从外观上看,我们可以看到直接和 TA/TO 分销渠道的平均 ADR 高于公司渠道,ADR 的离差也明显更大。

带 Bonferroni 校正的成对 t 检验

然而,我们希望使用带有 Bonferroni 校正的成对 t 检验对此进行更详细的分析。

> pairwise.t.test(data$ADR, data$DistributionChannel, p.adjust.method="bonferroni")Pairwise comparisons using t tests with pooled SDdata:  data$ADR and data$DistributionChannel Corporate Direct
Direct 4.6e-11   -     
TA/TO  2.4e-11   1P value adjustment method: bonferroni

当查看调整后的 p 值时,我们可以看到公司与直接、公司与 TA/TO 之间的差异非常显著,因为 p 值接近于零。鉴于 Bonferroni 校正已被用于预防 1 型错误,我们可以更有信心拒绝组间无显著差异的零假设。

也就是说,我们可以看到在直接组和 TA/TO 组之间存在 p 值 1,这意味着我们不能拒绝这两个组之间没有显著差异的零假设。

结论

这是对成对 t 检验的简短介绍,特别是使用 Bonferroni 校正来防止 1 型错误。您已经看到:

  • 使用单向方差分析的局限性
  • 如何计算家庭误差率
  • 如何使用 Bonferroni 校正进行成对 t 检验并解释结果

非常感谢您的宝贵时间,非常感谢您的任何问题或反馈。

参考

  • 安东尼奥,阿尔梅达,努内斯(2019)。酒店预订需求数据集
  • 统计学:如何在 R 中进行 Bonferroni 校正
  • 统计学:家族误差率是多少?

免责声明:本文是在“原样”的基础上编写的,没有任何担保。它旨在提供数据科学概念的概述,不应被解释为专业建议。本文中的发现和解释是作者的发现和解释,不被本文中提到的任何第三方认可或隶属于任何第三方。作者与本文提及的任何第三方无任何关系。

回答关于人工智能的 10 个最常见的问题

原文:https://towardsdatascience.com/answering-10-most-commonly-asked-questions-about-artificial-intelligence-f847e63b5837?source=collection_archive---------2-----------------------

提供人工智能问题的解决方案,澄清误解,并讨论关于人工智能的各种概念

附身摄影在 Unsplash 上拍照

人工智能是我们这个世纪的主要话题之一。人工智能的能力和无限潜力引发了一些有趣的对话和辩论。

人工智能的普及为人工智能带来了许多新的视角。从对学习人工智能越来越有热情的爱好者,到期待探索该领域的野心家,或者只是想批评它的其他人。但是,不管你躺在哪个光谱上,都有几个问题贯穿你的头脑。

在本文中,我们期待回答一些关于人工智能的最常见问题。目的是回答这两个方面,包括所有的实际问题,并澄清个人对这个问题的任何疑问。让我们简单看一下今天这篇文章中我们将试图解决的各种问题。

  1. 什么是人工智能?
  2. AI 到底有多厉害?
  3. AI 会抢你们的饭碗吗?
  4. AI 能接管世界吗?
  5. AI 有哪些优点?
  6. 人工智能有哪些弊端?
  7. 我们离实现 AGI 还有多远?
  8. AI 有哪些应用?
  9. 开始学习 AI 需要成为天才吗?
  10. 如何入门 AI?

你可以随意跳到你最想得到答案的问题。但是,如果您不想错过任何特定的要点,强烈建议您阅读整篇文章。现在,让我们期待回答关于 AI 最常被问到的十个问题。

1.什么是人工智能?

人工智能是指开发的软件或特定模型可以独立执行复杂的任务,而不需要人类的任何帮助。人工智能更正式的定义可以表述如下:

“能够执行通常需要人类智能的任务的计算机系统的理论和开发,例如视觉感知、语音识别、决策和语言间的翻译。”

人工智能是一个庞大的研究领域,由各种子领域组成,包括机器学习、深度学习、神经网络、计算机视觉、自然语言处理等等。

人工智能被认为是未来的技术,可以解决各种领域的众多问题,从机器人,医学科学,物流和运输,金融,以及工业中更多的公用事业服务。

我强烈推荐从下面提供的链接中查看下面这篇关于揭开人工智能神秘面纱的文章,以评估对该领域更高级的理解。

2.AI 到底有多厉害?

为了回答这个问题,让我们简单地看一下人工智能的复杂历史,尤其是神经网络领域。人工智能的概念和它无限的能力在几十年前就有了。人工智能被认为是为现存问题提供解决方案的下一件大事。

随着时间的推移,人们意识到人工智能并不像想象的那么简单。执行复杂的活动几乎是不可能的,特别是在那个时期的现有技术下。

最初,人工智能并不像人们认为的那样成功,因为缺乏数据和技术资源来承受执行复杂排列和组合的能力。

然而,在我们当前的时代,我们已经得出结论,人工智能是创造一个革命性世界的潜在未来。人工智能甚至可以解决某些复杂的任务,相对而言,这些任务可能会花费人类更多的时间。

问题的简单答案—“AI 到底有多厉害?” 在现代依赖于从事程序计算的研究人员的能力。开发人员的技能有助于使人工智能模型足够好,以尽快解决一个特别复杂的任务。

随着图形处理单元(GPU)的兴起,你可以帮助更快地计算人工智能模型,并开发创新的东西。就目前而言,人工智能非常强大,可以高效、有效地解决分配给它的一系列任务。然而,它还没有达到顶峰,我们离那个点还有几年的时间。

3.AI 会抢你们的饭碗吗?

人工智能是当今发展最快的领域。据财富报道,统计数据显示,在过去的 4 年里,人工智能专家的雇佣量增长了 74%。人工智能被认为是当代人最热门的工作。

对熟练人工智能专家的需求正以前所未有的速度增长。对机器学习、深度学习、计算机视觉、统计学和自然语言处理等人工智能子领域专家的需求和空缺职位每天都在激增。

自然产生的问题是,人工智能最终会强大到有能力偷走我们所有的工作吗?

这种说 AI 未来会抢你饭碗的说法,在我看来几乎可以认为是神话。这是一个假设的场景,人工智能将取代所有人类活动,并接管现代世界的大部分任务,因为它们不容易出现人为错误,并且更有效地执行特定任务。

在工业革命时期,当机器被引进的时候,类似的事情被陈述。显然,它没有偷走工作机会。相反,它为人类控制它们的更多工作铺平了道路。人类是智慧生物。因此,人工智能将简化人类工作的复杂性,但实际上不会夺走你的工作!

4.AI 能接管世界吗?

菲利普·卡岑伯格在 Unsplash 上拍摄的照片

科幻(Sci-fi)电影改变了一些人对人工智能的认知。他们想象用人工智能编程的机器人将变得如此强大,以至于它们最终将摧毁它们的创造者,并摧毁整个世界,从而导致一种新的人工智能控制的物种的发展。

一个耐人寻味的故事情节,但不真实,因为它将在不久的将来真实地发生!

不可否认,人工智能已经走过了漫长的道路,并发展成为现代世界的一个独特特征。尽管人工智能取得了进步,但在工作或开发阶段,大多数任务仍然是在某种人工监督下完成的。

人工智能也仅限于被编程来完成的特定任务。一个有趣的例子是自动驾驶汽车,人工智能控制汽车,并将其驾驶到用户选择的预期目的地。然而,人工智能只限于精确地驾驶汽车,没有其他外部任务。

因此,对于科幻电影中展示的人工智能接管世界来说,我们至少还需要几十年才能实现这样的结果。但是,只是为了保持对这种猜测的好奇心,它可能最终在未来成为可能!虽然目前,这只是虚构的。

5.AI 有哪些优点?

由于人工智能在现代被大肆宣传,它有许多优点。

除了之前讨论过的人工智能创造的大量工作机会,它还有其他优点,例如完成人类需要执行的循环或重复任务,而没有人类容易出错的缺点。

人工智能,类似于计算机程序,不会疲劳,因此有能力整天工作在一个特定的任务上,直到达到预期的结果。

它们有能力在一系列问题上进行比人类速度更快的计算,并得到精确的结果。他们也有大量的现实生活中的应用程序,使我们的日常生活更加简单。人工智能的优点是不言自明和无限的。

6.人工智能有哪些弊端?

从零开始构建人工智能模型有时会非常耗时,而且会耗费大量资源。如果你正在寻求在没有 GPU 的普通计算机上开发深度学习模型,你的替代方案将是切换到云平台,因为模型的构建过程在你的 PC 上是不可持续的。

除了消耗大量的时间和资源,人工智能模型的部署在某些情况下也可能相当昂贵。此外,在特殊情况下,人工智能模型出现故障时的维护成本可能很难处理和解决。

人工智能要考虑的另一个显著缺点是缺乏使用人工一般智能来完成更高级和智能的任务。我们受限于人工狭义智能。ANI 对于执行许多任务是有益的和必要的,但是它不像 AGI 那样有效。这一点引导我们进入下一个问题。

7.我们离实现 AGI 还有多远?

安德鲁·乔治在 Unsplash 上的照片

人工通用智能是一个耐人寻味的概念。AGI 是指人工智能程序可以计算、评估和处理多项任务,并具有人类水平的完整性和智能。

尽管不断的研究工作和技术进步,人工通用智能领域尚未取得丰硕成果。关于这个概念的实验和研究正在不断地被评估,希望在不久的将来会有更好的结果。

根据专家的说法,人工智能尚未成为可能和实现的主要原因是因为复制人脑几乎是不可能的。

虽然神经网络在执行分配给它们的特定复杂任务方面表现出色,但不幸的是,它们不能代替人脑。

8.AI 有哪些应用?

自然界中的人工智能有着广泛的应用。这些包括你从一天开始到一天结束的旅程。当你通常用智能手机开始一天的工作时,你可以利用智能面锁的人工智能功能或其他指纹人工智能措施来解锁你的手机。

然后你决定用谷歌搜索一些东西,你会看到自动完成和自动更正的人工智能特性,它利用了序列到序列建模的技术。除了智能手机,人工智能还有大量其他应用,包括垃圾邮件检测、聊天机器人、对象字符识别等等。

人工智能还在许多其他领域得到应用,例如机器人、医学、物流和运输、金融以及工业中更多的公用事业服务。

要查看数据科学和人工智能的十个令人敬畏的现实世界应用,我强烈建议通过以下链接查看我以前的一篇文章。

</10-awesome-real-world-applications-of-data-science-and-ai-6fddf3f34d74>

9.开始学习 AI 需要成为天才吗?

这个问题有一个简单的答案— “不,你没有!”

人工智能是一个神话般的领域,包含许多壮观的子领域。如果你对以下主题提供的各种有趣的概念特别感兴趣,投资你的宝贵时间来获得人工智能主题的进一步知识是完全值得的。

虽然从头开始学习人工智能有时在开始时可能很难,但随着你投入更多时间学习大量与人工智能相关的概念,它会变得更加有趣和酷。你将接触到数学、编程、机器学习等等,这将扩大你的知识面。

即使你发现人工智能领域不适合你的特殊兴趣,只要你对人工智能的众多主题有所了解,那也完全没问题。

接触人工智能的最大好处在于,你从以下主题中获得的知识可以部分或全部用于各种软件应用和工作。

10.如何入门 AI?

好吧!所以到这一点,你有希望对人工智能的各种功能着迷,并且你很兴奋地寻找一个开始使用人工智能的好地方。

人工智能是一个广阔的领域。但是,不用担心!有大量有价值的资源和生产资料,你可以利用它们来产生最好的结果。你可以通过分析和研究互联网上的资料获得广阔的知识领域。

Stack overflow、Data stack exchange 和 GitHub 等网站是一些最受欢迎的网站,它们可以提供您在运行或安装程序或相应代码块时遇到的问题或错误的深入解决方案和答案。

我建议查看本文结论部分提供的第一个链接,以了解更多关于“了解更多数据科学和机器学习的 10 个最佳免费网站”的信息这应该是一个很好的起点,可以用来分析各种观点,同时从资源中获得大量知识。

我还强烈推荐阅读我的一篇文章,这篇 23 分钟的文章介绍了如何在 12 个月内掌握数据科学,包括 12 个基本步骤,每个月一个步骤。您可以通过下面提供的链接访问这篇文章。

</12-steps-for-beginner-to-pro-in-data-science-in-12-months-c6f6ba01f96e>

结论:

美国宇航局在 Unsplash 拍摄的照片

在这篇文章中,我们涵盖了大多数关于人工智能的常见问题,同时试图为众多人工智能主题提供可持续的信息和解决方案。我们还旨在澄清误解和讨论各种关于人工智能的概念。

读完这篇文章后,我希望所有的观众对人工智能和众多关于人工智能的话题有一个清晰的了解。获得对人工智能的现实感知,对于理解未来世界革命性的变化具有重要意义。

因此,了解复杂的细节并对人工智能的主题有所了解以避免错误的观念和其他误解是至关重要的。此外,理解人工智能并致力于在人工智能领域获得未来是一种令人欣慰的经历。这是一个崭新的领域,前面有许多新的机会和发现。

如果你对这篇文章中提到的各点有任何疑问,请在下面的评论中告诉我。我会尽快给你回复。

看看我的其他一些文章,你可能会喜欢读!

</10-best-free-websites-to-learn-more-about-data-science-and-machine-learning-f2c6d7387b8d> </11-crucial-mistakes-to-avoid-as-a-data-scientist-71c29aef028> </8-revolutionary-artificial-intelligence-technologies-of-the-modern-era-f8f22a4127d0>

谢谢你们坚持到最后。我希望你们喜欢阅读这篇文章。我希望你们都有美好的一天!

回答人工智能中的因果问题

原文:https://towardsdatascience.com/answering-causal-questions-in-ai-87c9b53e3a72?source=collection_archive---------26-----------------------

介绍一些最常用的技术,这些技术可用于从数据中查询信息以进行可解释的推断。

里卡多·佩拉蒂在 Unsplash 上拍摄的照片

介绍

试图发现因果关系的两种主要技术是图形方法(如知识图和贝叶斯信念网络)和可解释的人工智能。这两种方法实际上构成了因果关系层次中关联层次的基础(图 1),使我们能够回答这样的问题:什么不同的属性组成了一个实体,不同的组成部分是如何相互关联的?

如果你有兴趣了解更多关于因果关系如何在机器学习中使用的信息,更多信息可以在我以前的文章中找到:机器学习中的因果推理。

图 1:因果关系层级(图片由作者提供)。

知识图表

知识图是一种图形技术,通常用于从大量数据中简明地存储和检索相关信息。知识图目前广泛应用于从搜索引擎、电子商务网站和社交网络查询信息等应用中。在我们之前概述的推荐系统案例研究之后,知识图最近被应用于因果关系(Yikun Xian 等人。艾尔。[1]),以便生成基于因果推理的推荐。

作为一个简单的例子,让我们考虑一下,如果我们使用搜索引擎来找出伦纳德·尼莫伊是谁(在《星际迷航》中扮演斯波克的演员),会发生什么。一旦输入我们的查询,搜索引擎将自动构建一个类似于图 2 所示的知识图,以此作为我们搜索查询的起点,然后从它开始扩展以获取任何相关信息。

图 2:简单的知识图。图片转载自[2]。

知识图最有前途的应用之一是创建能够从因果关系中学习的机器学习模型。知识图卷积网络(KGCN),代表了这一领域的第一个成功的应用[3]。事实上,图卷积网络旨在创建知识图的矢量(嵌入式)表示,然后可以输入到机器学习模型中,以生成推理路径并为模型预测提供证据[4]。KGCN 可以潜在地用于监督或非监督任务(例如,多类分类和聚类)。

贝叶斯信念网络

贝叶斯信念网络是一种概率模型,它利用简化的假设来可靠地定义不同元素之间的联系,并有效地计算它们的概率关系。通过分析不同元素之间的相互作用,我们最终可以利用这些类型的模型来发现因果关系。在贝叶斯网络中,节点代表变量,而边报告不同元素之间的概率连接。图 3 给出了一个三变量贝叶斯信任网络的简单例子。

图 3:贝叶斯信念网络(图片由作者提供)。

贝叶斯信念网络,能够表达条件依赖和独立变量的联系。这些类型的网络还遵循马尔可夫条件5。最后,使用贝叶斯概率方法(等式 1),我们能够基于新收集的证据迭代地更新连接概率。

等式 1:贝叶斯法则(图片由作者提供)。

DeepMind 等公司目前的研究重点是使用贝叶斯信念网络作为起点,以创建因果贝叶斯网络(CBN) [6]。因果贝叶斯网络现在被用于可视地识别和定量地测量数据集中的不公平模式(数据中的元素可以导致偏向特定子类别的机器学习模型)。此外,研究还证明了使用因果贝叶斯网络的可能性,以便不仅识别数据,而且识别机器学习模型本身是否偏向特定类别[7]。

可解释的人工智能

当今机器学习的主要权衡之一是模型性能与复杂性。事实上,与传统的线性分类器和回归技术相比,复杂的深度学习架构通常能够在各种各样的任务中表现得更好。这种权衡在 2016 年的出版物《我为什么要相信你》中有深入的分析。作者 Ribiero 等人。艾尔。[8]并引领了人工智能关注可解释性的新趋势。

复杂且更精确的模型如今被称为黑盒。这些类型的模型工作过程更难理解,它们不能估计每个特征的重要性以及它们之间的关系。黑盒模型的一些例子是神经网络和集成模型。

另一方面,更简单、更不精确的模型,如决策树和线性回归,反而被视为白盒,更容易解释。可以用来估计模型可解释性的两个主要指标是其响应函数的线性和单调性[9]。

模型代理人

为了使模型更容易解释,一种可能的方法是创建替代版本(近似版本)。这可以在本地或全球范围内进行。

  • 全局代理模型:在这种情况下,我们创建原始非线性模型的线性和单调近似,它对任何可能的输入都有效。如果原始模型是高度非线性的,那么创建一个全局代理可能会导致糟糕的性能。
  • 本地代理模型:通常在试图逼近高度非线性模型时实现。事实上,使用这种方法,我们可以将原始特征空间分成不同的线性子部分。对于这些部分中的每一个,然后可以创建线性模型等效近似(例如,使用决策树和线性模型)。局部代理模型通常也被称为局部可解释模型不可知模型(LIME)。

使用输入要素和原始模型预测(而不是地面实况标签)来训练代理模型。

在图 4 中,提供了一个简单的示例,显示了在使用标准黑盒模型或模型替代技术时,回归任务的拟合曲线可能的样子。

图 4:模型代理示例(图片由作者提供)。

为了使模型更易于解释,可以使用的一些替代方法有:特征重要性、沙普利附加解释(SHAP)、部分相关图(PDP)和基于梯度/注意力的方法。

人工智能中的偏差

创建可解释的和基于因果关系的机器学习模型的另一个重要原因是识别和防止任何可能形式的偏见(例如,对任何特定阶层的不公平歧视)。事实上,偏差可能来自训练数据集本身(例如,我们有限的数据量可能无法正确表示真实的人口分布)或模型构成(例如,我们的模型可能不合理地偏好一个类而不是其他类)。可能的偏差类型有:交互偏差、潜在偏差和选择偏差。

联系人

如果你想了解我最新的文章和项目,请通过媒体关注我,并订阅我的邮件列表。以下是我的一些联系人详细信息:

  • 领英
  • 个人博客
  • 个人网站
  • 中型型材
  • GitHub
  • 卡格尔

文献学

[1]强化知识图推理的可解释推荐,Yikun Xian 等。艾尔。访问日期:https://arxiv.org/pdf/1906.05237.pdf2020 年 7 月。

[2]知识图的关系机器学习综述。马克西米利安·尼克尔、凯文·墨菲、沃尔克·特雷普、叶夫根尼·加布利洛维奇。访问日期:https://arxiv.org/pdf/1503.00759.pdf2020 年 7 月。

[3] KGCNs:用 TensorFlow 对知识图进行机器学习。詹姆斯·弗莱彻,格拉克实验室的。已访问:https://blog . grakn . ai/kg cns-machine-learning-over-knowledge-graphs-with-tensor flow-a1d 3328 b 8 f 022020 年 7 月。

[4]知识图和因果关系。Gianmario Spacagna,实用数据科学学院。已访问:https://datasciencevademecum . com/2019/12/19/knowledge-graphs-and-causality/2020 年 7 月。

[5]因果推理导论。彼得·斯皮尔特斯,卡内基梅隆大学哲学系。已访问:http://www . jmlr . org/papers/volume 11/spirtes 10a/spirtes 10a . pdf2020 年 7 月。

[6]因果贝叶斯网络:一种支持更公平机器学习的灵活工具。西尔维亚·基亚帕和威廉·艾萨克。访问日期:https://deepmind.com/blog/article/Causal_Bayesian_Networks2020 年 7 月。

[7]路径特定的反事实公平。西尔维亚·基亚帕和托马斯·p·s·吉拉姆。访问日期:https://arxiv.org/pdf/1802.08139.pdf2020 年 7 月。

[8]“我为什么要相信你?”解释任何分类器的预测。马尔科·图利奥·里贝罗,华盛顿大学。艾尔。访问:【https://arxiv.org/pdf/1602.04938.pdf】T22020 年 7 月。

[9]黑盒与白盒模型。Lars Hulstaert,走向数据科学。已访问:https://towards data science . com/machine-learning-interprebility-techniques-662 c 723454 F32020 年 7 月。

回答数据科学指标变化面试问题—终极指南

原文:https://towardsdatascience.com/answering-the-data-science-metric-change-interview-question-the-ultimate-guide-5e18d62d0dc6?source=collection_archive---------11-----------------------

办公时间

数据科学访谈系列闯入 FAANG

目录

  1. 动机
  2. 思考这个问题的简单方法
  3. 那么你实际上如何回答这个问题
  4. 这是什么意思?
    一、提问澄清问题
    二、提问。举几个高层假设
    III。深入挖掘诊断问题—热带地区 ⅳ。提出解决方案
    五、总结
  5. 应用框架
  6. 介绍数据科学面试准备系列

动机

最常见的数据科学(和产品管理)面试问题之一是诊断指标变化的原因。

让我们暂时离开面试环境,想想为什么要问这个问题。指标在任何组织中都至关重要。他们告诉你:

  • 你是否在推进你的使命
  • 如何决定是否推出新产品或新功能
  • 如何优先关注什么

实际上,您所做的每个决定都是由其对指标的影响决定的。

因此,如果指标发生变化(无论是正的还是负的),你自然会想知道为什么

思考这个问题的更简单的方法

你拥有一家书店,每天都有大约九到十个人来买一本书。

一个周六的早晨,你醒来,骑车去书店,把招牌从“关门”换成“开门”,在等待第一个顾客的时候开始整理书架。

Artem Beliaikin 在 Unsplash 上的照片

一个小时过去了,然后两个小时,然后五个小时。你开始奇怪为什么没人来。

也许镇上有一家新书店要接管你的生意?也许有一个你忘了的全国性节日?也许镇上正在举行一个大型活动,你的潜在客户会蜂拥而至?你是 假设

你拿出手机,开始搜索:

  • 今天公休?
  • 一家新书店开张了?
  • 在我附近发生的事件?

你正在进行诊断。

果不其然,你发现有一个事件正在发生——有一个新的书展,现在每年都会举办一次。你知道你对此无能为力。很难与全市范围的书展竞争,而且这也是毫无意义的,因为它每年只会举办一次。但是,至少现在你知道为什么今天没有人来,当明年这个时候同样的事情发生时,你就知道是怎么回事了。你有一个 的解决方案。

很直观吧?这不仅是你用来解决度量变化问题的相同框架,而且也是你解决问题时需要的相同心流和好奇心心态:

你关心的事情已经改变了。

你自然想明白发生了什么。

你有一些高层次的猜测。

你测试你的假设,看看是否有正确的。

一旦你确定了变化的来源,你就决定了行动的方向。

那么这个问题怎么回答呢?

一、澄清

二。假设

三世。诊断

四。求解

五、总结

那是什么意思?

一、提出澄清性问题

什么都不要假设——剖析问题的每个字。如果有些事情含糊不清,问后续。

活跃用户数下降 15%。

  • 主动 是什么意思?登录的人?点击特定按钮的人?
  • 时间线是什么?是过去一天下跌 15%吗?月份?一周?

上周点击率增加了 10%。

  • 点击率是一个比率,即每次展示的点击数。所以**分解分子和分母。**分子(即点击量)增加了吗?或者说,分母有没有减少(即印象数)?

二。给出几个高层次的假设

在你开始探索可能的原因之前。花点时间假设几个嫌疑人——这会让你和面试官有所准备。一种方法是把它分解成内部和外部原因。

内部

  • 我们的日志记录中的一个错误——所以实际上什么都没有改变,但是我们的系统报告了错误的数据
  • 我们的用户喜欢(或讨厌)的最近发布的功能更新

外部

  • 用户行为的改变
  • 竞争对手采取的行动

三世。深入挖掘以诊断问题

你的诊断由两部分组成,你想探索什么你计划如何探索。

为了引导您的头脑风暴,您可以利用热带框架,该框架分为:

  1. 时间
  2. 地区
  3. 其他内部产品
  4. 平台
  5. 行业&竞争对手
  6. 自相残杀
  7. 分割

萨米·威廉姆斯在 Unsplash 上拍摄的照片

1.时间

随着时间的推移,这一指标是如何变化的?

探索什么

  • 过去几周(或几个月)我们的表现如何?如果我们看到上周有 10%的增长,那么前几周的百分比变化也是 10%吗?在这种情况下,10%可能是很正常的。还是变化更小?更高?
  • 这种变化 是季节性的 吗?每年这个时候我们都会看到同样的峰值吗?WhatsApp 在节日期间发送的消息 中会出现 峰值吗?
  • 变化是突然的还是渐进的?我们是否看到了一夜之间的突然上涨或下跌?或者,随着时间的推移,这一指标已经逐渐朝着这个方向发展了吗?如果产品使用量一夜之间增加了 50%,我们的记录系统会不会有问题?
  • 一天或一周中是否有特定的时间这种变化更明显?

如何探索它

  • 你可以做一个纵向分析——这是一个有趣的说法,你想看看这个指标的历史数据,看看它是如何随时间变化的

2.地区

这一指标在不同地区有何变化?

探索什么

  • 这种变化是集中在一个特定的地区,还是我们看到了一个类似的全面变化?

如何探索它

  • 一个简单的细分分析(在这种情况下,该指标在美国、印度和巴西等地的表现如何)将告诉您需要了解的内容!

3。其他功能/产品

如果我们有多个功能或产品线,它们之间有什么变化吗?

探索什么

  • 这种变化是针对某个产品的,还是整个公司的?这一指标在我们的其他产品中有何变化?如果脸书上的筹款功能被越来越多的人使用,那么脸书旗下的 Instagram 上的“刷起来捐赠”功能也会有类似的增长吗?
  • 除了正在讨论的指标之外,是否还有其他指标也发生了变化?如果花在优步上的时间在减少,司机取消订单的数量是否也在下降(这意味着人们花在应用上的时间减少了,因为他们有了更可靠的体验)?

如何探索它

  • 检查正在跟踪的其他指标,并查看该指标在其他产品和功能中的状态。

4。平台

这一指标在不同的平台(即使用的操作系统)上有何变化?

探索什么

  • 移动 vs 桌面?
  • Mac vs Windows?
  • 安卓 vs iOS?

如何探索它

  • 再次分段!这次是靠平台!

5。行业&竞争对手

我们的竞争对手也看到了同样的变化吗?他们正经历着相反的情况吗?

探索什么

  • 当我们开始走下坡路时,是否出现了新的竞争对手或类别?club house 登场后,听苹果播客的用户数是否下降了?
  • 竞争对手最近是否改变了他们的产品?
  • 品类整体是否在下降?

如何探索它

  • 确定指标变化的时间,并查看是否有新的竞争对手在大约同一时间进入该领域(或者现有竞争对手发布了新产品)?

6。自相残杀

如果一种产品的指标在下降,是因为我们提供的另一种产品正在蚕食用户参与度吗?或者,如果有问题的指标在增加,我们是否会从其他产品中分食?

探索什么

  • 面对我们的下滑,我们提供的其他产品或功能是否有所增长,反之亦然?
  • 我们是否发布了一个新功能,将用户从我们的旧功能中吸引过来?如果是这样,我们能否将新特性的发布完全归因于我们正在讨论的特性的度量的下降?当脸书发布 reactions 时,帖子的评论数量下降了,是因为人们发现按 react 按钮比写评论更容易吗?

如何探索它

  • 分析其他产品和功能的关键指标,以确定我们感兴趣的指标的下降是否与另一个产品的指标的上升一致。
  • 新功能的用户同时也是原始功能的用户的比例是多少?
  • 自从问题出现以来,转换的用户中对原始功能的参与度显著下降的用户比例是多少?
  • 新功能使用量的增加是否等于原有功能的下降?

7。分割

指标的变化可能由特定的细分市场驱动。还有什么方法可以分解这个指标?

探索什么

此指标如何因用户类型而异:

  • 年龄、性别、教育程度
  • 超级用户与普通用户
  • 新用户与现有用户

该指标如何随着产品的不同属性而变化:

  • 如果观看 YouTube 视频的时间在下降,是跨较长的视频还是较短的剪辑?是只针对 DIY 视频还是面试教程内容?开始看视频的人数是一样的,但是他们中的大部分人在中途停止观看了吗?

如何探索它

  • 分段!分段!分段!

四。提出潜在的解决方案

1.时间

  • 如果变化是季节性的,那么不一定有什么你需要“解决”的。但是,你可以利用这一点。亚马逊的销售额可能会在黑色星期五激增,所以他们会希望确保他们有适当的基础设施,这样 网站就不会崩溃 。他们也可以看看是否有某些类型的产品是受欢迎的购买,并相应地增加他们的库存。
  • 如果出现突然下降,可能是日志中的错误,或者是最近推出的新功能或更新造成了问题,您可能需要回滚。
  • 如果有逐渐下降,这可能表明用户行为的变化。如果因为人们更喜欢听播客,花在听音乐上的时间正在减少,那么 Spotify 可能希望将更多的内容库存放在播客上。

2.地区

  • 国家/地区可能会有新实施的法规影响您的产品指标。您需要做进一步的研究,以评估这些法规的影响和潜在的变通办法。 优步于 2019 年在伦敦被暂时禁赛 原因是多次安全故障导致一系列诉讼和法庭案件。
  • 当地流行的事件也可能是潜在的解释。虽然这些可能不是需要“解决”的领域,但却是可以利用的机会。科切拉季节意味着南加州 Airbnb 预订量的激增,这得益于飙升的价格。

3。其他产品/功能

  • 如果我们的其他功能和产品出现了度量变化,这可能是一个更大的问题,我们应该与多个团队一起解决,可能需要一个公共关系顾问。 Elon + Twitter。

4。站台

  • 如果我们的指标在特定平台(例如 iOS)上发生了积极的变化,并且与我们发布的(iOS)更新相吻合,我们会希望进行回顾,以确定该更新的哪些方面是有利的,这样我们就可以加倍努力。或者,如果指标变化是负的,我们可能想要重新考虑甚至回滚更新。
  • 如果变化是由于平台体验的变化**(例如,应用商店的位置、评级)**我们可能需要向我们的营销团队寻求建议,因为这是一个漏斗顶端的问题
  • 如果用户对特定平台表现出强烈的偏好,我们希望确保首选平台的体验符合标准。我们还需要确保我们特定平台的盈利战略能够顺应潮流。脸书的广告模式最初只局限于桌面应用,随着移动应用成为首选平台,它不得不进行扩展。

5。行业

  • 如果这一类别正在整体上发生变化,我们应该开始关注应用程序的大规模变化。什么 柯达 该做的都做了。
  • 如果有新的竞争对手抢走了我们的市场份额,我们可以从重新激活用户开始。我们可能还想进行用户研究,以了解我们的产品与竞争对手的产品之间的差距

6。自相残杀

  • 同类相食未必是坏事。我们需要通过确定新功能是否更好地符合业务目标,来确定用户对我们功能的兴趣是否有所改变。
  • 同类相食也可能是用户行为改变的指示,但它是用户行为改变的指示。在这种情况下,我们可能要考虑我们的核心指标是否需要随着用户行为的改变而改变。如果用户更关心观看 Instagram 故事,而不是参与 Instagram feed,我们可能希望优化留存率(因为故事的短暂性更有可能促使用户不断回到平台),而不是花在应用上的时间。
  • 我们还可以考虑将这两种特性结合起来,创建一个更加统一的平台。

7。分割

  • 如果新用户和现有用户之间的指标不同,那么可能存在过度拥挤效应。 Reddit 论坛可能会达到临界质量,新用户会感到失落,比现有用户更不愿意参与,导致每位用户的参与度下降
  • 如果用户在漏斗的某些部分下降,那么这个漏斗阶段的体验可能会被打破。虽然同样数量的人开始在亚马逊上购物车,但如果支付验证系统不起作用,购买量可能会下降。

五、总结

简要重申:

最初的问题陈述,即我们试图确定指标变化的原因

变化的原因

您提议的解决方案

应用框架

在我的下一篇文章中,我将向你展示如何将这个框架应用于一个特定的问题(包括一个模拟面试的视频!)

数据科学面试准备系列介绍

有很多数据科学面试准备指南。事实上,这些指南正是我能够在脸书找到目前工作和之前在 Adobe 工作的原因。然而,它们都没有像我希望的那样深入 T2。

这就是为什么我正在创建我自己的数据科学面试准备系列。随着每篇文章我都围绕机器学习、产品商业意识、统计概率和 SQL 深入面试问题。

我还将分享我旅途中的个人故事,包括我如何进入数据科学,我的工资,我如何管理我的财务以及我最喜欢的学习和灵感资源。

如果您有具体的问题想要回答,或者您正在纠结的话题,请在这里联系我,我会尽快回复您!

回答费米问题:AI 是我们伟大的过滤器吗?

原文:https://towardsdatascience.com/answering-the-fermi-question-is-ai-our-great-filter-56bd7f8fb44d?source=collection_archive---------19-----------------------

播客

安德斯·桑德伯格在 TDS 播客

要选择章节,请访问 Youtube 视频这里。

编者按:这一集是我们关于数据科学和机器学习新兴问题的播客系列的一部分由 Jeremie Harris 主持。除了主持播客,Jeremie 还帮助运营一家名为sharpes minds的数据科学导师初创公司。可以听下面的播客:

苹果 | 谷歌 | SPOTIFY | 其他

几十年来,我们的宇宙中似乎没有外星生命,这一直是科学界猜测和争议的一个来源。如果我们假设在一个给定的星球上,智慧生命可能进化的可能性微乎其微,那么几乎不可能想象宇宙中没有外星文明。他们在哪里?

这就是安德斯·桑德伯格所说的“费米问题”:鉴于宇宙深不可测的大小,我们怎么会没有看到外星生命的迹象?安德斯是牛津大学人类未来研究所的研究员,他试图预测当我们接近一个技术无限的未来时,人类将不得不面对的伦理、哲学和实践问题。这项工作在很大程度上专注于超智能人工智能及其可能带来的生存风险。作为这项工作的一部分,他非常详细地研究了费米问题,以及它对生命的稀缺性和人类物种的价值意味着什么。

我们的谈话涉及了很多领域,以下是我最喜欢的一些观点:

  • 费米问题争论的核心是德雷克方程。德雷克方程是一个公式,用于估计我们应该能够在我们的宇宙中观察到的外星文明的数量,以及各种参数,例如生命在给定星球上进化的概率,似乎维持生命的恒星和行星形成的速度,等等。这些参数中的大多数都有相当大的不确定性——有些跨越几十个数量级——安德斯认为,对费米问题的大多数分析都没有考虑到这种不确定性,导致关于宇宙中存在其他形式的智慧生命的可能性的结论非常不正确。
  • 费米问题对人类的未来有着重要的影响。如果事实证明,可探测的智慧生命在宇宙中应该很普遍,那么我们在那里看不到它一定有什么原因。一个看似合理的原因是,所有文明都达到了某种技术或其他门槛,在这个门槛上,它们会可靠地自我毁灭。如果是这样的话,我们最好小心,因为那个伟大的过滤器可能就在前方。但如果事实并非如此——如果事实证明人类在宇宙中是孤独的,因为生命确实如此不寻常——那么地球上的生命就变得更加珍贵和有价值,成为宇宙中唯一的亮点。
  • 费米问题给了我们很好的理由去关注前方的路。这使得我们变得非常重要,善于预测可能具有破坏性潜力的技术发展。其中一个候选人是通用人工智能,一些人预测这种智能可能会带来技术 奇点。考虑到这一点,安德斯花了大量时间研究不同的人工智能发展预测策略。
  • 对即将到来的通用人工智能持怀疑态度的人使用的一个常见论点是基于这样的观察,即当前的人工智能系统在每次操作的基础上远不如人脑节能。安德斯指出,专注于能效并不真正有意义,因为这只是我们可以想象用来评估人工智能系统潜力的一个维度。在另一种意义上,人们可能会认为,一个仅经过几天训练就在图像分类方面超过典型的五岁儿童的机器学习模型已经能够在一个性能轴上表现出超人的性能。同样,当前的人工智能系统能够吸收更多的数据,并比人类更快地做出预测和推理,因为它们的物理结构不像人脑那样笨重和笨拙。在这场辩论的背景下,不可能总是将苹果与苹果进行比较,这应该是在对人工智能的未来做出任何预测时相当谦逊的原因。

你可以在推特上关注安德斯或者在推特上关注我。

播客中引用的链接:

  • 没有明确提到,但是如果你想了解更多关于他的工作,那么安德斯在人类未来研究所网站上的页面值得一看。

章节:

  • 0:00 介绍
  • 1:34 安德斯的背景
  • 8:45 问题解决流程
  • 16:06 宇宙优化
  • 17:07 生物进化
  • 24:55 费米问题
  • 33:47 矮星
  • 42:10 人工智能和机器学习设计
  • 42:54 哲学家和人工智能系统
  • 57:19 人类行为和自由意志
  • 1:05:36 总结

请查看下面的文字记录:

Jeremie (00:00:00):
大家好。我叫杰瑞米。欢迎回到迈向数据科学播客。我对今天的节目非常兴奋,因为我可以和安德斯·桑德伯格交谈。安德斯是我长期以来一直想与之交谈的人,因为他的研究专注于一些有趣的主题,他用一种非常有趣的多学科策略来研究这些主题。他是一名研究员、科学辩论家、未来学家和超人类主义者。他拥有斯德哥尔摩大学的计算神经科学学位。他目前还是牛津大学人类未来研究所的高级研究员。我会说安德斯是我遇到的关于存在风险和高级人工智能系统将迫使我们回答的难题的最有趣的思想家之一。他有办法将来自机器学习、伦理学、形而上学和宇宙学等不同领域的知识无缝融合在一起。

Jeremie (00:00:47):
和他说话是一种享受。这也让人们意识到,当你拉远到足以看到人类如何融入深邃的宇宙图景时,这些不同的领域变得多么紧密相关。我真的很喜欢这次谈话。它涵盖了一些有趣的主题,这些主题都以一些意想不到和令人惊讶的方式触及了人工智能发展的未来。这些问题包括,为什么我们在宇宙中实际上可能是孤独的,人类大脑的能量效率是否表明,总体而言,这可能比看起来更难组合起来,以及人工智能是否会有意识。所以这绝对是一场爆炸。我希望你和我一样喜欢这次谈话,事不宜迟,我要闪开,让这一集开始了。好吧。安德斯,非常感谢你参加我的播客。

安德斯(00:01:32):
谢谢你邀请我。

杰瑞米(00:01:34):
你能来我非常高兴。我在推特上跟踪你的时间比我想承认的要长。我的意思是,有很多你正在研究的非常有趣的东西,那么多与人工智能相关的话题,人工智能的未来,人类的未来。我想从一个有点传记性质的问题开始。我想知道你是如何来到这个领域的。是什么驱使你来到这里?

安德斯(00:01:56):
我在 20 世纪 70 年代斯德哥尔摩北部的一个郊区长大,非常无聊。所以我读了所有我能在当地分馆找到的科幻小说,然后有一天我意识到,实际上我想把它变成现实,我该怎么做呢?我也许应该读读科学书籍。所以当我从那里开始,然后我去了市立图书馆,然后是主图书馆,然后是大学图书馆。所以我就这样结束了,但我一直想让未来成真。如果我不能写关于它的小说,也许我可以调查它,写论文,发明东西,或者弄清楚我们应该关注或避免什么。

Jeremie (00:02:35):
在这个过程中,你有没有对什么样的科幻小说最有可能产生看法?我还想说,科幻作家通常会犯什么样的错误?因为那有点…我不知道,我想知道你是否发现了那里的趋势。

安德斯(00:02:53):
所以问题是科幻小说往往想写好故事,而现实通常是一个非常糟糕的故事。现实的情节事无可救药。我的意思是,只要看看今年或任何平均年份,你会说,“是啊,这是如此不均衡,这没有意义。”所以真实的故事,当然要尽量讲得通。他们试图讲述一个能引起我们共鸣的故事。当然,问题是,世界上的许多事物都与我们人类的情感无关,尤其是当你涉足科技领域的时候。这意味着许多最好的故事实际上根本没有非常仔细地处理科学和技术。他们更喜欢人类故事,这意味着如果你想思考你的未来,在许多情况下,你可能会想去科幻小说,实际上不如小说有价值,但更擅长思考想法。

安德斯(00:03:54):
但同样,你有一个权衡。许多最酷的想法实际上可能并不十分合理。是的,我觉得真正包含有趣事物种子的科幻小说除了小说之外,还充满了小创意。他们试图描述在一个环境中发生的事情的相互作用,不仅仅是惊人的技术,还包括它是如何失败的,或者孩子们是如何滥用技术的,以及如何对抗所有人的叫喊,离开我的草坪,或者现在我们在说,离开我的增强现实和过滤器。在这一点上,你开始看到对非平凡效应的兴趣。艾萨克·阿斯莫斯正在谈论电梯原理。如果你展示华盛顿天际线的照片,对不起,不是华盛顿,如果你展示纽约天际线的照片给过去一个世纪的人看,他们应该能发现一定有电梯之类的东西,否则摩天大楼就没有意义了。

安德斯(00:04:53):
这是一种登上所有楼梯的工具。也许他们会错了,他们会说,"是的,所有的富人当然都住在离底层很近的地方,而穷人必须建造高海拔的公寓。"但他们将被迫意识到,必须有像电梯这样的东西来理解这幅画。我认为这也是科幻小说最有用的地方。它让你意识到这些电梯的一些原则。例如,对我来说,思考大量先进技术的可维护性是一个有趣的问题。你如何修理太空电梯?如果你建造[听不清 00:05:33],需要付出多大的努力来保护它,使它不被破坏?当你制造一个人工智能时,保持它的理智和功能需要多少额外的工作?

Jeremie (00:05:46):
有意思。这几乎…这很有趣,这让我想起了创业公司的一个原则,那就是你应该总是致力于解决看起来无聊的问题,有些事情是人们不工作的领域,也是像 Stripe 这样的公司的领域…很多人不知道 Stripe 是什么。是一家支付处理公司。他们干脏活。他们负责互联网的管道工程。这似乎是类似的想法之一。你认为会有类似的影响吗,会有一点点反过来,人们可能会遇到一个遵守电梯原理的想法,这是一种理性的前瞻性预测,但它的含义是如此深刻地反直觉,以至于人们几乎本能地反对它。那也是你见过的吗?

安德斯(00:06:32):
哦,是的。一直都是。事实上,回到经典科幻小说作家阿瑟·c·克拉克在他的书《未来概况》中,他谈到了想象力的失败和神经的失败。相当多的人,尤其是学者,缺乏想象力。他们无法想象事情会有很大不同。我记得有人告诉一位纳米技术教授说,自我复制的机器是绝对不可能的,但当我指出细菌又如何呢?是的,但是我们不能在这里建。所以从他的角度来看,自我复制的机器是绝对不可能的,因为他不能把它放在他的项目中,他不想从他能做的事情中发现实际存在的阻力。但是这样你就没有勇气了。你可以想象一些事情,但你不想坚持到底,因为后果是如此巨大,如此怪异,好吧,这听起来很疯狂。我只是不想谈论。

Anders (00:07:29):
同样,纳米技术也有这个问题,因为 Eric Drexler 最初的观点表明,如果你获得原子级的精确制造,并且可以扩大规模,世界就会变得完全不同。这让人们对所有的技术感兴趣。但不幸的是,这个领域后来被像那位教授这样的人接管了,他们想研究正常的东西。所以我们最终得到了很多精彩的固态科学,但是你可以实际做一些事情来真正改变世界,这并不是我们在实验室里做的事情。所以这可能是疯狂的言论。同样的事情也发生了,当然,对于许多其他领域,我们已经在太空中看到了。克拉克写的就是这个。许多人都在批评早期的太空先锋,并给出各种合理的解释,为什么你永远也不能建造一个可以在太空中飞行的火箭。

Anders (00:08:24):
当然,问题是他们在想最简单的方法,然后他们可以证明那是行不通的。他们没有考虑如果有人真的有动力去做,并且花了一些时间去做一个好的设计,他们能做什么。这就是为什么当戈达德发射火箭的时候,人们在嘘火箭。

Jeremie (00:08:45):
你认为……因为我经常陷入思考的一个范式是具体问题及其解决方案之间的区别,然后是解决问题的过程。我总是觉得在我们平凡的日常生活中很容易遇到特殊的挑战,比如太空旅行,看着它说,“哇,耶。”就像你说的,那真的很难。我无法想象自己能解决这个问题。从某种意义上来说,这就是我们所说的,这就像是,同样的事情让我们说,“嗯,如果我管理那个国家,它会运行得很好。”我们可以想象,我们可以将我们的意愿印在问题上,以某种方式反映如果人类共同努力会发生什么,而不是说,我们是某种集体结构,某种超级有机体,通过怪异的市场力量,人际互动和所有这些东西的混合,我们是某种集体努力解决这个问题的机器学习算法。我是说,你觉得这是个准确的框架吗?

安德斯(00:09:45):
我想是的。这还取决于你试图解决的问题的类型。所以如果你有一个自上而下的方法,你需要一个天才的管理人员来做。我们已经看到了两个很好的例子,曼哈顿计划的极地项目。在这两种情况下,都有一个相当明确的目标,基本的物理原理大部分都被理解了,但并非完全理解。然后你让人们同时致力于困难的科学和工程问题,但你也碰巧让几个管理天才管理整个项目。莱斯利·格罗夫斯将军可能是整个 20 世纪在管理人员和完成任务方面最优秀的人之一。但你也有像互联网这样的庞大项目,它们有机地成长,充满了内部矛盾和混乱。我的一个小嗜好是收集文件,作为互联网即将灭亡的预兆。

Anders (00:10:46):
他们中的许多人从 98 年开始就指出,如果这种趋势持续到 9 月份,这将不会奏效。它应该是在七月写的。当然,有一天解决方案得到了实施,并在 9 月份推出,没有问题。互联网有很多问题,人们疯狂地修补它们。这里我们有很多自下而上的解决方案。不是所有的都是完美的。如果我们在 70 年代早期以不同的方式实现邮件系统,我们就可以避免垃圾邮件,但是没有人能够想象电子邮件会在计算机部门之外使用。肯定不是数百万人,包括那些有点自私的人,没有人想到邀请一位经济学家指出,“看,如果发送一封额外邮件的边际成本是零,你将会收到无限多的邮件。”

耶雷米(00:11:34):
对。是啊。而且看起来好像…在某种程度上,这确实让我想到了我们在预测人工智能将如何解决问题方面遇到的各种问题。如果我看一个计算机视觉模型,我一般无法提前猜测算法会在图像中寻找什么样的特征来区分飞机和鸭子,潜艇之类的东西。同样的,我们似乎总是被集体想出这些疯狂的解决方案的方式所震惊。有一件事可能与你在深层时间和费米悖论方面的一些工作有关,这让我想知道的一件事是,宇宙中原子的集合真的只是…只是随着时间的推移而变得混乱的一些大优化算法的参数吗?我是说,我很想和这个想法互动。首先,也许我们可以在一分钟内深入费米的东西。

安德斯(00:12:34):
从某种意义上来说,我认为这是完全正确的,是的,我们正在进行巨大的优化。我们在最小化自由能,一些吉布斯自由能,或者海尔曼自由能,不管是什么。我永远也不能把他们分开。所以在某种意义上,哦,是的,原子和粒子,试图通过最小化熵来达到能量最小的约束。这就是事情开始变得非常奇怪和有趣的地方。因为出于某种原因,宇宙开始时有一个非常平坦的时空。但是当然,非常高的温度和很多混乱的原子。当时空膨胀,温度下降时,原子开始以各种不同的方式结合在一起。因为它们会因为重力而聚集在一起,你会得到很多非常重要的模式,包括一些开始融合并开始产生能量的模式。

安德斯(00:13:26):
现在你得到了能量流,事情变得更加复杂,但在超级大宇宙中,你可以说宇宙的历史基本上是我们将大量熵移入宇宙的引力场,但聚集了物质。这为许多非平凡的非平衡过程提供了动力,这些过程具有非常低的熵。他们中的许多人后来被证明是在为其他事情进行优化。有一个非平衡热力学的大领域,我不是很理解。但似乎在很多情况下,如果你有一个火焰,持续不断地供给气体,它会倾向于最大化或最小化熵产生,这取决于约束。

Anders (00:14:10):
你会发现很多奇怪的优化开始在各处发生。所以对于像我们这样的分子物质生物来说,例如,晶体真的感觉很奇怪,因为它们与我们在自然界中发现的大多数其他岩石如此不同,因为它们试图最小化晶格能和表面能,并变成这些非常精确的精确定位,与正常的岩石非常不同,正常的岩石当然也充满了不同种类的晶体。

安德斯(00:14:36):
当然,我们拥有洛里所说的八个周期信息晶体。他在我们真正知道遗传密码是什么之前就假设了它们,但是一定有某种分子,当以常规方式放置时,包含了建造世界和太阳的信息。他们不知道这是哪种分子。他们推测了一下。他大部分都错了,但是他把 DNA 描述成一种非周期性晶体是对的。进化最酷的一点是,你有一个优化过程。你试图最大化你的适应度,或者至少你的基因,他们希望到目前为止我们可以希望我们有很多后代基因。所以在这方面非常成功的生物,它们将基因传播开来。那么现在你得到的是为其生态位而优化的东西。这是一个局部优化。这些利基中的许多被证明是短暂的或者只是简单的愚蠢,或者只是在一个天体撞击的时候被厄运摧毁。

安德斯(00:15:31):
但最终的结果是,来自环境的大量非平凡信息被转化为遗传信息。我们的身体充满了适应环境的能力。这种情况已经发生了几十亿代,细胞和有机体已经学到了很多东西,通常是通过艰苦的学习。现在它编码了更瘦的基因。当然,类似地,其中一些基因编码的大脑在本质上做着同样的事情,但速度要快得多。现在我们甚至有了累积培养。所以我们正在以更快的速度进行。

Jeremie (00:16:06):
我的意思是,这些抽象层次似乎一直在堆积,在某种程度上,我的意思是,这让我感到好奇,因为进化生物学总是被框定在这种范式中,我们说,什么是物种遗传适应度的优化,它们传播基因的能力,本质上就像通过时间传播遗传信息一样。但有时,从你试图优化个体数量或这些基因拷贝数量的角度来看,总是有些不清楚实际上应该优化什么。随着我们越来越接近许多人认为的技术奇点,这似乎也将打破许多假设,因为智力可能不是遗传的。它不包含在基因中。因此,无论宇宙是为了什么而优化,它都不一定仅仅是遗传适应性。好像是什么东西,但我不知道是什么。你知道那会是什么吗?

安德斯(00:17:07):
所以如果你能和进化对话,进化会说,“哦,是的,作为一个物种,你一定非常非常优秀和成功。”看看它们吧,这么大的哺乳动物,在世界各地都很常见。是的,非常好。当然,除了很多人真的不擅长繁殖。我是说,我们为什么要制造避孕药?为什么老人不捐精呢?就包容性健身而言,这是你应该做的。取而代之的是,有些人有宗教思想,他们决定我要在这个修道院过独身生活,思考神圣的思想。我们会想出很多比抚养孩子更有趣的事情。也许有一天我们真的会把自己上传到软件里。从生物进化的角度来看,这确实是一个糟糕的想法,但这是一直在发生的事情,因为生物进化,它创造了各种各样的东西,以试图优化适应度,但除此之外,它并不关心这些东西做什么。

安德斯(00:18:10):
比如说,性是长期提高健康和能力水平的好方法,因为你们可以分享有用的基因。当然,你需要一个激励系统。所以动物实际上开始做爱。所以突然之间,你得到了更多的乐趣和资金,从进化的角度来看,这只是工具性的,但从价值的角度来看,这是一件好事。大脑,嗯,他们真的必须协调凡人的行动,避免被吃掉,但你可以用一些来想象事情,做更多的事情。似乎在宇宙深处,可能只是在做自由能最小化,但这导致了超级不平凡的效应。所以当你玩人工生命模拟,一个细胞自动机的时候,你经常会得到奇妙的涌现现象,这些现象在很多方面都非常鼓舞人心。

安德斯(00:19:01):
哦,我只是加入了一些简单的规则,去掉了很多复杂的东西。但是,如果你花足够的时间进行这种模拟,你经常会感到有点无聊,因为你确实会变得复杂,但这种复杂性在大多数时候都是相同的[听不清 00:19:14]生活中一次又一次。过一会儿你会看到那些图案。你会得到一些真正奇怪的东西。你通常要自己设计,从外面放进去。他们在 90 年代进行的许多人造光模拟发现,你有一个小的生态系统,但它们从未变得更复杂,这与我们自己的生态系统和我们自己的社会非常不同。它们似乎有变得更加复杂的趋势。

安德斯(00:19:39):
这可能是我们遗漏了一些关于现实或进化的非常基本的东西,或者可能只是你需要一个足够大的世界才能让它发生。有点像 2000 年代的神经网络革命表明,到目前为止,我们使用的数据太少,计算机太小,训练太少。当你把它放大几个数量级的时候,真正令人惊奇的新事物发生了,这是我们在 20 世纪 90 年代甚至无法想象的。

耶雷米(00:20:07):
令人惊讶的是,在这些奇怪的影响、有性生殖、生物进化等等发生之前,这个世界与你想象中的世界是多么的不同。这在某种程度上凸显了这些过程有多奇怪,当然也提出了一个问题,即它们在宇宙层面上有多普遍。我认为这可能与一些关于风险的存在主义问题有关,对吗?因为当人们经常谈论为什么我们仰望夜空,却没有在那里看到其他外星文明?这是否意味着一个伟大的过滤器或一些可能仍然在我们前面的东西?你在这个话题上做了很多工作。我想问你一个问题,你对费米悖论有什么看法?你能介绍一下,简单描述一下,然后看看你能从那里得到什么吗?

安德斯(00:21:02):
是的。所以费米悖论并不是一个真正的悖论,有些人会指出它甚至不是费米的,但我喜欢称它为费米问题。回到 20 世纪 50 年代,曼哈顿计划的人正在吃午餐,谈论原子火箭,谈论现在解决宇宙问题是多么容易,那时原子的力量已经被释放,然后费米显然只是问,“那么大家都在哪里?”这是一个非常好的问题,因为如果很容易穿越太空的沟壑并在整个宇宙定居,我们应该会看到很多反外星人的例子,因为宇宙真的很大很古老。如果可以的话,在你出现在每个地方之前,它不需要很长时间就能在星星之间传播。所以空旷的天空成了一个真正的问题,因为如果你对技术有点乐观,这似乎会引起注意。

安德斯(00:21:58):
这就是为什么人们说这是一个悖论。我们假设互联网会在很多地点和时间出现。你乘以一些合理的智慧出现的概率,然后你应该得到一个数字。如果你有点乐观,你会得到一个很大的数字,但这似乎并不合适。现在,你可能会争辩说,也许宇宙中很少有地方可以进化出智慧和生命。所以有一些人说,地球是非常独特的,但很难让它变得超级独特,独特到你可以放心地假设其他地方没有生命。所以肯定还有别的原因。当然,在这个等式的某个地方,你将各种因素相乘,一定有一个因素小到足以让宇宙变得相当空。

安德斯(00:22:47):
这就是伟大的过滤因素。可能是生命超级稀有。在这种情况下,嗯,我们很幸运我们存在,现在我们有一个远大的未来在我们面前,或者它可能是聪明是罕见的,或者它可能是聪明是常见的,但它不会存活很长时间。当然,这是一种可怕的伟大过滤器,让我们在人类未来研究所工作,思考这些问题,因为这似乎是少数几条关于我们机会的真正独立的信息之一,无论全球风险来自于阅读报纸和思考关于生物技术和流行病的最新消息。他们试图理解其他问题,但这里我们有一些似乎是所有可能文明的平均值。现在,这里真正有趣的事情是,当然,如果我们生活在一个趋向复杂的宇宙中,情况会变得更糟。

安德斯(00:23:41):
如果你认为宇宙是中性的,或者对生命是有害的,好吧。很空。如果你认为宇宙真的试图获得生命,你告诉我们,你有一个更大的问题。同样值得注意的是,过去许多人绝对相信每个环境都有自己的居民。对许多人来说,在古代和现代早期,其他星球上有人居住的想法几乎是不言而喻的。[听不清 00:24:09]实际上他说上帝创造这些星球却不把人放在上面是有点疯狂。

安德斯(00:24:16):
顺便提一下,这也意味着你不需要太在意人类的末日。我的朋友托马斯·莫伊尼汉写了一本很好的书,《X-Risk 》,讲述了思考人类灭绝的历史。他指出,直到最近,人们还没有把它看得很重,因为如果我们灭绝了,那么,其他人就会出现。宇宙就是这样。但是如果你认为我们几乎是孤独的或者完全是孤独的,如果我们的火花熄灭了,就只有黑暗,这使得存在的威胁更加糟糕。所以这是我们非常想理解费米问题的另一个原因。

杰里米(00:24:55):
好的,是的。我完全理解你对费米问题的执着,我也同意。我的意思是,费米悖论的框架总是看起来…这并不是说它看起来很天真,但它似乎反映了一种偏见,当你看着,我猜,著名的德雷克方程,它列出了所有的因素,你乘在一起得到潜在的外星文明的数量。你已经在你的作品中指出了这一点,这似乎是作者偏见的一贯反映。因此,如果你在分析这个等式,你可以根据你如何调整这些因素,得出几乎任何你想要的答案。你介意解释一下吗?

安德斯(00:25:40):
你可以无意识地。你很容易把你认为合理的东西放进去,然后你会得到一个合理的答案。如果我最后得到了我认为不合理的答案,通常,我会回头或多或少有意识地捏造事实,这样我就会得到我认为合理的答案。如果你认真思考的话,这当然是非常危险的。

耶雷米(00:26:00):
实际上,就这一点而言,这意味着预测本身存在巨大的不确定性。在研究这种不确定性并解释它时,我们在学术上或研究上做得好吗?

安德斯(00:26:12):
我有一篇论文,我认为我们在这方面做得不好。因为我认为人们通常会把德雷克方程的因子排列起来。他们承认这个我们不知道。事实上,我们对类地行星上存在生命的可能性一无所知,但我们只能说百万分之一的可能性。让我们承认编造了这些数字,然后他们把所有的数字相乘,他们得到一个数字,他们承认,当然,这是非常不确定的,因为我编造了一些数字,然后他们就不管了。因为对他们来说,承认你对某事不确定,这就是如何处理不确定性。但这当然是不理智的。如果我不确定有多少人住在旧金山,我应该陈述一个范围,我认为我 90%或 95%确定它在里面。

安德斯(00:27:03):
我不能只说,“好吧,我认为是 500 万人,”然后不提这个范围有多广。现在有趣的部分是,如果你真的用德雷克方程,试着放入适当的范围,如果你有均匀的概率分布,那就更好了,所以你只放入你的知识水平和不确定性,你会得到一系列的答案,但事实证明,鉴于目前的知识水平,你会得到一个相当大的分布。所以我们相当清楚银河系中的恒星数量和它们形成的速率。我们有一个想法,好吧,类地行星是一毛钱或一打。他们有很多人。我们对一个星球上出现生命的可能性有大约 100 个数量级的不确定性。

安德斯(00:27:49):
有可能是在第一个模式出现后的 10 分钟内发生的。这可能是通过或多或少的热力学奇迹发生的,每 10 的 100 次方个行星或类似的东西。我们真的不知道。所以当你把它放进去,你会得到非常宽的不确定性分布。即使你平均来说相当乐观,所以你认为平均来说,银河系中可能有 10 个文明,你也有很大的可能性相信我们在可观测的宇宙中确实是孤独的。如果你知道空空如也的天空中的一些东西不是那么有问题,我可能仍然有一些希望,我是对的,我们在那里有更多的空间,但是空空如也并不可怕。如果我只是输入数字,那么我最后会说,“哦,这里应该是 100 个文明。为什么我们看不到他们?”

耶雷米(00:28:44):
是啊。不,这很有趣,尤其是因为你把所有这些概率相乘,对吗?那么,如果说你只需要在这些参数中的一个非常非常悲观的范围内,就能真正摧毁任何希望,或者至少显著降低我们拥有行星邻居的可能性,这是对这个论点的一个很好的炮轰吗?

安德斯(00:29:11):
是的,没错。重要的是要认识到,许多职业宇航员会说,“是的,但当他们使用德雷克方程时,人们不会那么愚蠢。”然后,当然,我立即开始挥舞一堆发表的同行评议论文,这或多或少是一回事。许多人对超级不可能的事情同样过于自信。所以有些人认为生活是不太可能的。我们实际上不得不在我们的文本中做一点改变,不要给创造提供任何帮助,这是神圣的说法,但显然只有上帝才能给我们的星球增加生命,但这并不是不合理的,这可能需要一系列非常不可能的事件来导致这种复杂性。在写那篇论文之前,我没有想到的一件事是,很多生命最终可能会拥有一个可能是蹩脚的遗传编码系统。

安德斯(00:30:04):
它允许它们繁殖,但进化非常缓慢。所以我们需要有机会变成任何有趣的东西,直到恒星燃烧起来。现在我们这种生命只花了几十亿年就从原始粘状物发展到人们写关于原始粘状物的论文。但可能宇宙中大多数生命实际上都是原始的粘性物质,直到它变干并死亡。所以我并不认为这是一个难点,但是我意识到,哦,这实际上是有可能的。有点让人不安。

耶雷米(00:30:40):
是的。实际上很有趣,你提到了神创论者的观点,还有一些东西…我的意思是,我来自量子力学时代的世界,我在一个叫做量子力学解释的领域工作。在这种情况下,我们讨论了很多多节诗。我一直发现多节的一个令人信服的论点是,如果我们真的在宇宙中是孤独的,那将是一个多么可疑的情况。这将意味着,如果可观测的宇宙是所有实际存在的,并且恰好有一个智慧生命的样本,那么这意味着,比方说,生命在一个给定的星球上进化的概率几乎恰好是宇宙中有多少个星球中的一个。

Jeremie (00:31:37):
这真是令人难以置信……不会比这大 10 倍,否则我们会有 10 个邻居的利益。比那小不了十亿倍。它正好或者非常粗略地正好是这个数量级。然后,我的意思是,我想,你可能会指出一些宗教叙事,这可能是这类事情的另一种解释,你觉得这是一个令人信服的论点,证明在可观测的宇宙之外还有一个更广阔的宇宙,也许是一个多宇宙,诸如此类的东西?

安德斯(00:32:06):
我认为多节恐惧是许多人对它们的退缩,因为它看起来像,哦,等等,科学不是应该处理可测试的东西吗?这听起来非常不可测试,但似乎有很多不同理论的相当强大的预测。所以这不仅仅是你可以宣称,量子力学自然地导致了我们球体的数量。那也是我自己的观点。但是话说回来,人们可以花一整天的时间来争论量子力学的解释。但是也没有很好的理由认为可观测的宇宙就是宇宙的全部。的确,时空似乎是平的,这意味着最简单的答案是,哦,是的,它是无限大的。你不能说,“嗯,它有某种紧密的拓扑结构,但我们没有看到这方面的证据。”

安德斯(00:32:53):
然后你需要添加额外的复杂功能来使其工作。当然,你还有膨胀理论,它说,实际上可能还有其他领域,等等。所以你会得到几乎所有地方都支持的多宇宙理论。这意味着几乎所有的事情都很容易解释,因为在某个地方这是必然会发生的。真正的问题当然是,为什么世界没有变得更奇怪。为什么我们会发现自己在 G 星周围的一个看起来相当正常的行星上?即使这样也可能有点奇怪,因为毕竟黄矮星与红矮星相比并不常见,红矮星无处不在,而且它们发光的时间也更长。所以有点奇怪的是,它们在宇宙参考书中相对较早,而不是在中间的某个地方绕着一颗小红矮星转。

安德斯(00:33:47):
在另一篇论文中,我认为这可能实际上是暗示这些矮星周围不太适合居住。通常,我对这些行星的可居住性持乐观态度,但也许这些耀斑真的侵蚀了大气层,或者大陆漂移停止得足够早,以至于它们失去了碳循环,然后气候变得混乱。所以实际上,在这个参考例子中,大部分生命倾向于提前出现。

Jeremie (00:34:15):
这实际上在某种程度上非常违背直觉。我认为,这也凸显了不确定性的程度。我的意思是,如果你相当看好这些…对不起,是白矮星,还是?

安德斯(00:34:30):
红矮星。

Jeremie (00:34:31):
对不起,红矮星。如果你真的因为各种各样的其他原因看好它们,但这里我们绕着一些不同的恒星,我的意思是,红矮星之间的相对频率有什么不同,红矮星应该是绕着我们太阳转的恒星,如果它们确实更适合或同样适合居住,或者像-

安德斯(00:34:53):
基本上,我认为每颗黄星对应 30 到 50 颗红矮星。当你看到最小的,最小的,最暗的,这些数字会飙升。它们真的无处不在,而且持续时间也很长。太阳只会再持续 50 亿年。然后它会变成一颗红巨星,褪去外层,或者变成一颗相当无趣的白矮星。对于当时生活在太阳系中的人们来说,这是非常悲哀的,但与此同时,我们周围的许多红矮星,它们将快乐地继续运行一万亿年。现在离我们几光年远的燃烧恒星,当外层太阳已经变成白矮星,但也冷却成一个相当无聊的几乎是黑矮星时,它仍然可以发光。它仍然会发光。它将会熔化几乎所有的消防栓。如果它周围有一颗行星,那么在未来它可能会有和现在一样的温度。

Jeremie (00:35:59):
因此,出于各种原因,我肯定这是一个不好的术语,但如果你把宇宙中红矮星所有可能的生命维持年数的总和加起来,它应该比我们的太阳或类似太阳的恒星的生命维持年数的总和大得多。

安德斯(00:36:19):
它应该完全占据主导地位。我不记得确切的数字是多少了。我前阵子算了一下,红矮星周围的生物圈年比黄矮星多很多。

Jeremie (00:36:30):
生物圈年。好吧。是啊。很高兴知道有更好的说法。太好了。令人着迷。好吧。所以我们是如何走到这一步的还有各种各样的疑问。那么很明显不确定性的范围是难以置信的。令人着迷的是,我们甚至可以对此进行推理,坦率地说,我很惊讶地阅读了你的作品,你能够从我们父母的情况中提取出如此多的信息,仅仅是因为你在这里寻找更多的方向。随着宇宙的进化继续,显然,地平线上的一件大事,我们在这里必须期待的最大的相变可能是像高级人工智能,人工通用智能,超级智能以及所有需要的东西的出现。

Jeremie (00:37:12):
你在人工智能的能耗和人脑方面做了一些比较。看看人们提出的论点,即人工智能可能需要更长的时间才能发展,因为出于能量的原因,你认为这可能是一个糟糕的论点。我很想听听你对整个思想体系的阐述。

Anders (00:37:36):
现在,如果你想运行一个大型的机器学习模型,你将会启动你的数据中心,你的电费将会达到千瓦甚至兆瓦级。这并不便宜,但同时,当然,即使是最聪明的人脑也需要 20 到 25 瓦的能量。那是一个相当暗的灯泡。嗯,那是一个相当暗的白炽灯泡。如果你使用发光二极管,它实际上相当亮,但它仍然没有那么多能量。这真的很奇怪,因为大脑中的神经元通过一种 Rube Goldberg 机制传输信息,基本上是离子泵将细胞膜不同侧的钾离子和钠离子分开,当一个通道打开时,它们流过,它产生一些电势打开通道,你得到以大约音速机电传播的小波。

安德斯(00:38:37):
这有点傻,但效果很好。现在,这里有趣的部分是,如果你能在那个规模上做正常的电子设备,你可能会更节能,但是大脑仍然比现在的计算机更节能。所以有些人试图用这个作为论据,说,看,为了通过人工智能,你需要巨大的能量,显然你做不到。现在的问题是,我们是在比较苹果和橘子吗?我认为这就是正在发生的事情,因为当一个婴儿开始学习语言时,这与我们是否训练一种模拟数据中心的大型语言并不完全相同。这个婴儿在它所在的房间里说话很厉害。它从广播和电视中听到一些东西。它被语言所包围,但是一个婴儿在一两年内听到单词总量,并不是天文数字。

安德斯(00:39:36):
我不知道他们到底有多少百万个单词,但这并不多。现在,把它和现代语言模型做比较,现代语言模型做得很好。你基本上给他们所有的维基百科和 Reddit,互联网的大块,卑尔根的项目,所有翻译的联合国文本,基本上是你能得到的尽可能多的文本。这是一个巨大的数量,但他们也在数据中心接受了大约一周的培训后才学会。当你观察正在进行的过程时,它们似乎也非常不同。机器学习过程使用随机梯度下降,而婴儿大脑中发生的事情似乎更像是更重的学习。所以我的观点是,这不能作为比较。我们不能用他们使用当前计算机的能量来说他们什么时候能达到人类的智能。特别是因为算法的改进经常意味着我们可以伪造摩尔定律的额外十年。

安德斯(00:40:38):
如果你看看国际象棋计算机的表现,你会发现,在过去,当有人提出更好的解决问题的方法时,收视率会突然大幅上升。因此,这意味着实际上使用能量作为估计人工智能反弹的方法是行不通的。现在,它有另一面。我确实认为能量、智力和信息很重要,我们可以用它们来限制文明的发展。因为文明在某种意义上是信息处理。我喜欢指出,即使坠入爱河至少是信息处理操作的一部分,也许爱情的关键部分并不是所有的信息,也许有某种不可言喻的特质,或者实际上真正重要的东西。但是你肯定记得你爱上了谁,那就是信息储存。

安德斯(00:41:31):
你最好做点什么。这是一种需要传递给肌肉的信息,所以你要说话。所以这里有趣的部分是我们可以用信息和能量的物理学来说一点关于高级文明和他们能做什么和不能做什么。我们可以看着能量源说,“是的,宇宙中没有足够的能量来进行这种竞争。”这给了我们一些思考极端到遥远未来可能存在的极端先进文明的方法,但它并没有告诉我们 10 年或 100 年后我们是否会获得 AI。

杰雷米(00:42:10):
在预测方面,显然任何与人工智能有关的预测,以及我们什么时候会有变革性的人工智能都有巨大的特技棒,你有没有个人偏好,比如说,你的个人特技棒看起来会是什么样子,比如说,人工智能出现的 80%或 90%的置信区间,比如说,可以设计比人类更好的机器学习系统?因为我认为,出于各种原因,这可能是一个很好的客观基准。

Anders (00:42:43):
所以可能是 80%的置信区间。我可能会比大多数同事开始得晚。我认为,如果我们看到人工智能在 2050 年甚至 2040 年之前做出好的机器学习设计,我不会感到惊讶。大约的终点甚至可能是 21 世纪左右。我有一个相当-

耶雷米(00:43:08):
哇,太棒了。

安德斯(00:43:09):
是的。所以我有一个非常宽泛的估计,我想如果有人引用我的话,我可能会做得更宽泛。现在,当然,从安全的角度来看,我想指出的是,我们最好工作得好像它将很快发生一样,因为我们必须在它到来之前做好我们的功课,制造安全和价值一致的人工智能。即使它在 10 年、15 年、20 年后到来,那实际上对于解决一些非常基本的问题来说也是相当短的时间。如果要花上一个世纪,那么,它可能仍然是一个很难解决的问题。毕竟,2500 年来,哲学一直试图解决人类的价值取向问题,但收效甚微。

Jeremie (00:43:54):
实际上,我认为这是一个非常有趣的领域。如果有的话,你认为哲学家们在过去的 2500 年里一直致力于解决的问题的价值线和我们必须用人工智能系统解决的问题的价值之间有什么不同,以确保它们反映了。他们做我们想做的,实际上我们也知道我们想让他们做什么,这是这两个部分中的一部分。

安德斯(00:44:28):
很长一段时间以来,哲学和伦理学的有趣之处在于,它只是假设我们需要关心的唯一思维模式是类人思维。中世纪有一些关于天使伦理的有趣论述,但一般来说,这不被认为是一个大问题,因为我们知道天使无论如何都会表现自己。问题是他们的行为是出于自愿,还是仅仅因为我们被设定了这样做的程序。但是我们大多数的假设总是对的,但是每个人的想法都有点相似。关于类人思维的假设相当深奥,而且有点问题,因为人类思维是特殊的。这在动物研究中并不是什么有趣的发现,但是,是的,看起来黑猩猩实际上有一种公平感。当他们看到另一只黑猩猩因为在实验室里做了一些工作而获得超额报酬时,他们会感到非常不安。

安德斯(00:45:25):
这不仅仅是嫉妒。他们还意识到,等一下,为什么我要求得到一个香蕉,而黑猩猩得到了两个香蕉?所以你可以看到我们可能会说的一些元素,道德情感。我不认为黑猩猩真的在思考道德或公平或类似的东西,但我们确实需要这些先决条件,让我们在社会群体中工作。然后很多东西会变得精炼,因为我们有大的大脑,开始抽象地把它想成普遍的原则。但这背后是大脑中奖励功能的特殊设计。我们有一个特殊类型的动机系统,直到罗伯特最近,哲学家们只是假设它都是这样的。我认为现在正在发生的很酷的事情是人工智能正在将另一个引入伦理学。这相当具有挑战性。

安德斯(00:46:23):
当然,一些哲学家一直在思考动物权利和动物遭受的苦难等问题,但动物从来都不是你需要关心的道德代理人。如果你试图教你的猫道德,它不会工作得很好。但是理论上你可以试着用人工智能做到这一点。人们开始意识到这些系统可能与人类非常非常不同。事实上,从人类中心主义到非人类中心主义的伦理学,我认为是一个巨大的挑战,不仅仅是为了让有道德的人工智能安全,也是为了哲学。我认为这两个领域互相交流也是非常健康的。所以,一般来说,超越人类,是很有帮助的。

耶雷米(00:47:08):
是的。这是一个非常有趣的问题,至于我们是否以及在多大程度上把人工智能系统算作这些需要考虑的因素,我们必须以一种考虑它们的需求、想要和愿望的方式来做会计。我想,这也有点像是,你不能把它从围绕意识和主观经验的问题中分离出来,因为如果这些是没有任何感情的宗教黑盒,无论它们看起来有多真实,或者无论它们看起来有多动机,如果它们在内心深处真的没有感情,我不知道,因为机器没有感情,那么我们可能会浪费我们的时间和精力来优化它们的偏好。你对我们如何探索这个有什么想法吗?我的意思是,这似乎明显与意识的难题有关,这不是一件容易回答的事情,但你对此有什么想法?

安德斯(00:48:00):
我是一个糟糕的心灵哲学家。我不知道如何真正解决这个问题,但我认为有时谨慎是无关紧要的。所以当尼克·博斯特罗姆的书《超智能》出版时,这位伟大的哲学家头脑写了一篇有点粗略的评论,说,看,机器没有意识,所以伦理不可能是一个问题,这有点奇怪,因为一辆车可以在你没有意识的情况下碾过你。无意识机器可能相当危险。尼克的观点当然是,非常对,我们最好制造安全的机器不管它们是否意识到它们是安全的,这无关紧要。现在我们可能想设计一些机器,让它们成为我们需要关心的道德病人。我们也可能有很好的理由不想这么做。

安德斯(00:48:46):
乔安娜·布赖森写了一篇题为《机器应该成为我们的奴隶或者机器人应该成为我们的奴隶》的论文,这个标题当然比论文更进一步,但她说得很有道理。出于很多目的,你不想要一些你不得不关心的东西。她认为制造大量我们需要关心的机器是一个相当愚蠢的步骤。我还认为,如果你能制造一台被迫关心的机器,即使只是作为一个艺术项目,也一定会有人去做。所以真正的问题是,我们能区分出我们需要非常关心的系统吗?我认为这将变得非常棘手,因为通常我们倾向于使用直觉,比如,好吧,我跟它说话,它会给出合理的反应。好吧,我相信有人在附近。

安德斯(00:49:37):
但是我们知道你一定会爱上聊天机器人。令人尴尬的是,欺骗人类认为那里有人是很容易的,因为我们很好地假设了安全和抱歉,如果有东西似乎有思想,我们可能应该假设它是一个类似人类的思想,这也是为什么我们把类似人类的思想投射到动物和自然现象上,并且可能建立宗教来解释是谁如此愤怒以至于可爱地撞上了那棵树?它需要一个合适的外星人代理。问题是,当然可能有一些系统根本不是代理,这个比喻真的失败了。当你想到像谷歌搜索引擎这样的东西时,那是一种存在吗?不,不是真的。搜索引擎的边界非常繁琐,事实上它甚至不像人脑那样运作。

安德斯(00:50:33):
我们可能最终会生活在一个有很多非常重要、强大的系统的世界,这些系统做了很多聪明的事情,但与我们有些不同,因此我们需要新的道德分类。删除一个真正好的搜索代理可能是一件坏事,但不是因为它对搜索代理不好,而是它可能像一件艺术品,或者它可能有其他重要的价值或目标。所以在动物伦理工作样本中有一些想法,动物有生命项目,我们不应该干涉它们。你可以想象机器人有项目,即使我们可能非常无意识。正常的强调会说,“是啊,无意识的东西其实没有任何道德权利。”但是如果你进入环境伦理学,你会发现生物中心主义者和生态中心主义者说实际上生态系统可能有价值。

安德斯(00:51:22):
如果你努力改造地球,你会发现至少有几个哲学家会说,“哦,没有生命的星球环境是有价值的,所以如果我们引入生命也没有用。”所以他们可能会非常愿意说,“是的,也许这些机器人中的一些应该有某种形式的权利,所以我们应该尊重他们,即使他们仍然只是在数字和比特之间变戏法,没有任何内部经验。”这当然远远超出了我们通常的处理方式,以及我们作为一个社会应该如何安排我们自己的事务,以及人们与我们周围事物的关系,这将是相当具有挑战性的。

耶雷米(00:51:58):
这让我想起了之前我们谈论人类超级有机体的那段对话,或者说,一个星球上所有人类的集合是一个连贯的有机体。从这个角度来看,我们对这个超级有机体有道德责任吗?我的意思是,我们基本上充当它的细胞,谁说它没有合法的有意识的主观体验。当然,也许如果你疯狂地缩小,你可以看着地球,看着它随着时间的推移而演变,然后说,哦,那是一个逐渐苏醒的星球,有事情正在发生。这整个生态系统值得某种独立于系统中个体实体的道德地位。我想这里的问题也和你进入组合学一样,因为你可以很容易地说,加拿大是一个有道德的实体吗?无论如何,你对那种近乎逆向还原论的立场有什么想法吗?

安德斯(00:52:58):
是的。所以埃里克·罢工给了牛虻一个非常有趣的哲学。他写了一篇非常有趣的论文,关于如何宣称美国是有意识的?他认为,根据一些相当常识性的假设,并不难让读者得出这样的结论:是的,也许美国是有意识的,而且可以推测加拿大也是有意识的,但联合国或世界经济体系呢?组合学不一定那么疯狂。毕竟,我们通常有内部大脑半球,它们之间的带宽有限,我们大脑中的不同模块实际上并不完全了解其他模块中的信息。有时你会得到这些奇怪的脱节的体验。我记得有一天晚上回家,发现我挂在衣架上的那件外套看起来真的像是潜伏着一个邪恶的角色。

安德斯(00:53:54):
然后我跳了起来,因为我大脑的另一部分已经注意到黑暗中潜伏着一个邪恶的角色。我大脑的不同部分几乎在同一时间得出了不同的结论,我对两者都有点意识。现在,同样的事情很可能发生在超级生物身上。在某种意义上,谷歌可以是一个超级有机体的意识,它也是美国和世界经济的一部分。它们在某种程度上还被它们之间有限的信息流所分隔,但你有时可以说,在这种情况下,将这部分相当独立地对待是有意义的。就像我们现在可能会说,我们人类倾向于彼此相当独立,但你也可以谈论特定的人类群体。你可以说,科学界已经决定,以下关于世界的陈述似乎暂时是正确的。但是在那个社区里有很多科学家甚至没有听说过这个消息。

Jeremie (00:54:54):
意识可能会变成这样一个分形问题,这很有趣。这有点有助于思考终极泛灵论。我的意思是,如果我大脑中的每一个神经元都是有意识的,我身体中的每一个细胞都是独立有意识的,那会有什么结果呢?每个细胞内的每个细胞器都是有意识的吗?我能一直往下走直到每个怪癖和轻子都被意识到吗,我是说,每个粒子?

安德斯(00:55:20):
然后我们可能不得不担心不幸的轻子和怪癖。也许他们是这个宇宙中真正的道德问题,你们中的许多人正在遭受痛苦,我们真的不需要帮助穷人摆脱怪癖。

耶雷米(00:55:35):
谁知道他们会有自己的游说团。现在,实际上-

安德斯(00:55:38):
这里有一件重要的事情,意识可能是一件简单的事情,你可以扩展它以涵盖一切,这并不是不合理的。然后我们碰巧是那种也写论文、谈论和思考有意识的 OBX,这对于岩石来说可能不是真的。但是我们的大脑还有其他不平凡的属性,当你进一步武断时,这些属性可能不会继续存在。在近海,我们都说英语,但如果我检查你的大脑,我不会发现一个说英语的神经元。这是一个具有某种属性的系统。这当然是我对塞尔的中国室的第四个实验的回应。我认为这是完全正确的,一些系统有你在部件中找不到的属性,当你有足够的水分子在一起时,水的湿度就会发生。它不完全是水分子的一部分。

安德斯(00:56:34):
我们头脑中许多与道德相关的部分可能存在于某些层面,而不存在于其他层面。当你观察超级有机体时,这当然会很有趣。也许美国确实在这个意义上说英语,但让它成为我们谈论国家或文明的美德。我们当然可以谈论人们的美德。我不能谈论一个身体部位或一个神经元的优点,但可以说那个人是一个顽强的人,一个勇敢的人。那是一件懦弱的事情。也许我们可以说这实际上是一个勇敢的文明,或者这可能是有意义的。甚至可能存在文明尺度上的美德,而这些美德在人类个体尺度上是不存在的。

耶雷米(00:57:19):
因为我发现令人困惑或有点难以调和的事情之一是,从智力上来说,我认为这是有道理的,但直觉上我仍然在与之斗争的是,如果人类的超级有机体真的是有意识的,我的意思是,它的行为是完全确定的。似乎人类超级有机体所做的任何事情都只是组成它的所有个体人类行为的功能或产物,每个人都感觉他们有自由意志。所以这种感觉就像没有空间让那个超级有机体做出不同的决定,这似乎暗示着,哦,它不可能真的是一个有意识的自由意志的实体,但话说回来,我想我们对自己也是如此。我们也没有那种自由度。

Anders (00:58:09):
我认为这里的解决方案是人们对自由意志假设过多。我们希望它是一种自由的形式,但我认为它与物理学甚至逻辑学都不相容。但是实际上,自由意志是对大多数人行为的一个有用的描述。如果我在很多情况下都在犯罪,好吧,我已经做出了决定,这是基于我当时的了解和感受,这可能不是一个坏主意。也许我甚至可以证明导致我犯罪的神经放电,但这实际上并不能很好地解释我为什么这么做。自由意志在人与人之间是非常有用的。归属于大群体可能用处不大。

安德斯(00:58:57):
我们说民主党有自由意志。嗯,在某种程度上,是的,决策是在党的层面上做出的,但我们可能会将其归因于个人,但你也可以说这实际上是一种突发现象,因为当人们相互交谈时,有时会有一个集体决策,但没有人真正支持,但确实仍然做出了决定。所以我认为重要的是尝试去了解解释在什么层面起作用?所以通常,当我们谈论自由意志时,它是关于可预测性的。当你说某人是机器人时,这意味着你有一个输入如何产生输出的相当好的模型。当然,制作一个极其难以预测的简单程序是相当琐碎的,但是大多数时候我们并不认为随机行为真的有趣。我们认为适当的行为是难以预测的,这也是我们认为的自由意志。但是我们在很多系统中都遇到过这种情况。

安德斯(00:59:56):
只是在大多数情况下,我们不会直接与生物圈或整个文明互动。我们与人或组织打交道,但他们的自由意志一点也不令人难以置信。我们可能会说,嗯,脸书在某种程度上有设立隐私箱的自由意志,你可能会说,嗯,这取决于扎克伯格说什么,但实际上有股东,有内部结构。所以比那要复杂一点。还有其他组织在决定做什么方面可能更自由。自由意志只是假设我们能为我们采取或不采取的行动责备他们多少。

Jeremie (01:00:33):
对。我想这是它自己的一种独立的道德范畴,我们可能需要弄清楚。我的意思是,你认为这些事情是问题吗,这些问题是我们需要回答的问题,以便能够安全地应对即将到来的技术转型,或者我们可以通过,比如说,其中的一部分,或者你对此有什么想法?

Anders (01:00:53):
我想我们可能需要一个子集。这可能会很可怕,因为我认为如果我们能解决所有这些深层的问题,那就太好了,但其中一些问题可能会非常复杂。可能有些事情没有一个通用的答案,但是我认为好的治疗学确实能帮你很大的忙。例如,如果你想一想政府中的权力划分,这种平衡行为对于避免某些失败模式非常有用。我们通过各种方式了解到这一点。如果你想建立组织,这样我们就不会有太多的内讧,有各种各样的工具可以做到这一点,建立责任规则,让人们行为检点。我们这里有很多不同的选择。因此,如果你创建新的机构,你实际上可以考虑,好吧,这可能会失败,然后我们可以建立保障措施?

安德斯(01:01:51):
随着时间的推移,我们发明了它们。回过头来看看政治机构的历史或壁垒是很迷人的,因为在很多情况下,就像研究软件和研究密码原语一样,人们不得不发明委员会。人们不得不在不同的时间点发明各种形式的选票和选举机制。还有一些不好的方法,我们正在尝试,但大多数都被遗忘了。好的现在在工具箱里,我们可以把它们放进去。我们可能需要发明更多的方法,因为我们有两个新的问题,事情在不同的规模上。这也是技术转变如此可怕的原因之一。这不仅仅是因为我们需要考虑周围可能存在的人工智能,而且我们已经有了自己的公司和其他超级有机体,它们已经证明了这一点,是的,这一点也不容易控制。

安德斯(01:02:43):
我们还发现了一些很酷的机制,比如市场力量和声誉。甚至当父母告诉孩子如何表现和不表现,并给出各种关于做错事的孩子会发生什么的神话,这是一种编程形式,我们可以借用其中的一些想法。它按照我们想要的方式进行教学,或者机器人如何成长。因此,当然,我们将不得不进行测试,并可能找到自动生成更好工具的好方法。所以这里有很多工作要做。最酷的是,这将会打破哲学、编程、经济学和许多其他领域之间的界限。这里需要做很多跨学科的工作。我们可以窃取不同学科中最酷、最好的部分,并把它们构建成全新的学科。

耶雷米(01:03:32):
是的,为了能够对具有巨大不确定性的时间表做出估计,为了能够得到一些合理的东西,你不得不学习、了解和理解所有这些不同的领域,这令人难以置信。这只是一个绝对迷人的过去和未来真正深入时间的盛大旅行。我真的很感谢你的时间安德斯。事实上,我想对所有正在听的人说,如果你对安德斯的工作感兴趣,请看看他的推特,因为这真的是很好的东西。Anders,你还有其他资源推荐给大家看看吗?

安德斯(01:04:04):
在某种意义上,我会说是维基百科。我认为维基百科是一件有趣的事情,不仅仅是作为人类知识的宝库,也是我们有时可以团结一致的证明。看一些成功的例子是很有趣的,在这些例子中,人们实际上共同收集信息,解决各种争端和问题,并创造出有价值的东西。如果你有那种典型的《星际迷航》,旧的系列剧集,外星人要审判人类,维基百科将是我要指出的事情之一,看,这个展览我们没那么坏。有趣的是,人们一直试图制作类似维基百科的资源,但大多数都失败了。我们也可以从中学习。最终,我认为我们将拥有某种艺术和科学来制作这些伟大的共享资源,这些资源实际上结合在一起并带来很多好处。

安德斯(01:05:00):
目前,我们只是在网上尝试了几十年。所以我们仍然不知道如何定期做,但我认为我们会变得更好。因此,这就是为什么我真的建议看看像互联网档案馆,维基百科和一些声誉系统,似乎像在股票交易所工作。这些本身就是惊人的财富,不仅仅是因为它们是在证券交易所回答的很酷的问题,或者[听不清 01:05:29]有助于我们的集体记忆,而且我们可以在我们的集体系统中建立全新的成长工具。

Jeremie (01:05:39):
是啊。希望他们能继续成长。希望它们能帮助我们保持超级生物的一致性。

安德斯(01:05:45):
我希望在一万亿年后,维基百科上会有关于这一切的条目。

杰里米(01:05:51):
非常感谢安德斯。真的很感激。谢谢你的时间。

安德斯(01:05:55):
谢谢。这太有趣了。

用机器学习工具预测野火

原文:https://towardsdatascience.com/anticipating-wildfires-with-machine-learning-tools-5eb43b450d36?source=collection_archive---------45-----------------------

本阔在 Unsplash 上拍照

使用数据发现理想的野火抑制资源布局

从我的中学科学教室的窗户望出去,似乎地狱已经来到了科罗拉多州博尔德的郊区。烟雾几乎遮住了太阳,直升机和低空飞行的飞机来回嗡嗡作响,为四英里峡谷大火运送水和阻燃剂,这打乱了教学计划。这场大火将会烧毁 6181 英亩土地,摧毁 168 所房屋,并造成大约 2 . 17 亿美元的财产损失。

不幸的是,那场火灾如今远非异常。2020 年,美国西部的野火造成了高达 130 亿美元的财产损失,更不用说失去的生命和无数被迫重建的家庭的心痛。

野火不是不可阻挡的力量——是人类的努力阻止了四英里峡谷大火,以及其他许多威胁到我们社区的火灾。灭火最重要的工具之一是了解最危险的区域在哪里,这样我们就可以预先部署消防资源以尽快到达。

在 Metis 提供的数据科学训练营之后,我受到启发,创建了一个分类工具,可以帮助人类更好地部署消防资源。我想用我的技能来解决这个多年来一直是我生活一部分的问题。

科学背景

为了理解这个问题,我们不得不转向研究野火地理分布的领域。当我去大学获得环境科学学位时,我学到了这个领域的基础知识,当谈到理解火灾风险时,有三个主要的考虑因素:燃料、气候和地形。

燃料是一个非常明显的例子。在有大量枯木燃烧的地区,火灾更有可能发生,并且可能比其他地区发展得更快。

气候也很晴朗。在湿度低于正常水平或温度高于正常水平的地区,火灾发生的可能性更大,强度也更大。

地形是一个更有趣的变量。上坡时,火灾往往蔓延得更快,在丘陵地区灭火也更困难,这意味着这里的火灾比平坦、更容易到达的地区风险更高。

我想为所有这些特征建立代理,这样我就可以在估计火灾风险时将它们考虑在内。

方法

我在 Kaggle 上找到了一个包含 188 万起地理参照的美国野火的数据集。这将作为我的目标功能。当然,这需要大量的重新格式化,因为它没有提供任何一个没有发生过野火的县的数据,只有现有野火的记录。此外,东部各州的数据有限,迫使我将分析范围缩小到美国西部。我使用 SQLite 导入了这些数据。

至于我的预测器特性,我从多个来源检索数据。我能够从 NOAA 收集气候数据,从 USDA 收集地形数据,从美国林务局收集燃料数据。将这些联系在一起后,我对我在美国西部研究的变量有了一个很好的了解。

我在迭代过程中使用了各种模型,但是我发现的最强大的模型是 scikit-learn 包中的 Extra Trees 分类器。该算法是随机森林的一种变体,旨在通过随机改变每个决策树中每个节点的截止点来进一步减少方差。关于额外树算法的更多信息,我推荐这篇文章。

我希望构建的这个分类器工具最重要的一个方面是它的灵活性。我希望用户能够提高或降低阈值,当它来到什么样的火灾规模,他们想查明。这是因为不同的灭火工具有不同的可用性,因此最适合不同的严重程度。在部署消防队时,检查所有预计会发生的小型、中型和大型火灾是有意义的。然而,当分配更大、更昂贵的资源,如飞机和直升机时,用户应该能够将结果缩小到更小、更严重的火灾范围。

因此,我需要内置一个动态工具来防止阶级失衡。当用户仅寻找最大的火灾时,这些火灾可能仅包括模型被训练的观察总数的百分之几。为了保持性能,我需要确保有足够的来自正面类的例子供模型使用。通过在模型的每次预测运行期间检查不平衡,我能够保持比在查看非常大的火灾时更高的性能。

一旦我确信已经充分调优了我的模型的超参数,并在不同的截止点进行了测试,我就继续进行部署。我希望这个工具对理论用户有用,所以我决定使用 Streamlit 在线托管它。

Streamlit 是一个伟大而简单的工具,允许在 web 浏览器上托管 python 代码。我强烈推荐它给那些想轻松部署模型的人。如果你想找到更多关于如何实现简化部署的信息,我推荐他们的优秀指南。

结果

这里我们可以看到一个已部署工具的示例。用户可以选择在即将到来的火灾季节预计燃烧的县的给定百分比的分界点,然后查看预计达到或超过该阈值的红色县的结果。

下面,度量向用户显示了工具的准确度和精确度。精确度从 0.88 到 0.83 不等,而回忆变化更大,从 0.93 到 0.29 不等。度量标准取决于用户选择的截止点。随着阈值火大小的增加,精确度提高,回忆变差。

结论

这个工具超出了我的预期,当与基本的气候和地形测量结合使用时,一个有很多洞和间隙的静态数据集可以完成什么。当涉及到火灾范围和严重程度时,我很想看看这个模型如何处理更细粒度的空间信息和更多的数据。

如果你想看看这个项目的精髓,就去我的 GitHub 吧,在那里你可以看到这个项目道路上的每一个弯道。感谢您的阅读,我希望您离开时能对自然的力量有一个新的认识,以及我们使用这些工具理解它的能力。

反模式和公司中过时的思维模式

原文:https://towardsdatascience.com/antipatterns-and-obsolete-mindset-in-corporations-74540f128bc0?source=collection_archive---------26-----------------------

为什么很多数据项目会失败甚至无法启动?

Gian D. 在 Unsplash 上拍摄的照片

许多人将数据科学计划和人工智能的先进应用视为一场引领文化变革的运动。但是这种转变的结果是今天已经存在的数字社会。生活在一起自然会受到这些数字创新的影响,有针对性的数据分析甚至可以颠覆整个行业。在快速变化的时代,那些害怕变化的人也在大声疾呼。与此同时,由于数据隐私不足而可能出现的数据滥用甚至不是最大的社会问题。许多人害怕在数字社会中失去一席之地。在创新中成长起来的数字原住民比那些在某个时候相信即使没有深入的技术技能也能拥有职业未来的人更具适应性。[1]

在这种背景下,数据科学家有时也有打破公司旧思维模式的任务。他们必须将变化视为一个机会,以便将自己置于从数据中产生新知识的位置。

矛盾的是,因为安全考虑而拒绝改变会导致完全相反的结果。那些跟不上时代的人冒着生命危险。没有“大到不能倒”这种东西。教授主导的课程,如成功的关键商业技能,用网飞和百视达这样的案例研究来说明,领先公司可能会因为错误的战略而很快被赶下台。[2]

乔恩·泰森在 Unsplash 上的照片

公司中的反模式

反模式是导致与期望状态相反的行为模式。公司中的沟通努力和官僚作风随着员工数量的增加而增加。他们用自己的公司政治来阻碍自己管理有价值的数据项目。众所周知,大公司不如小公司灵活。缺乏灵活性使得公司很难对新趋势做出反应。当新技术出现时,咨询公司的顾问似乎只是在复制标准的通用幻灯片。

经验可能不是每个人都有,但在许多数据科学团体的网络中,不满意在更大的公司工作的人越来越多。

在下文中,我描述了一些可能导致数据项目失败的心态。它们都有一个共同点:它们可以通过领导和建立一种替代的企业文化来改变。

领域专长的贬值

有时,公司会有一种误解,认为数据科学是一个纯粹的技术话题。输入数据,运行算法,得到结果,每个人都很高兴。

我们假设一家公司雇佣了一个顶尖的数据团队。管理层期待新员工在如何通过数据分析为公司带来新价值方面创造奇迹。数据科学家得到他们想要的一切:顶级设备和访问他们需要的所有数据。毕竟这种做法也是本书推荐的。

在数据科学家旁边的办公室里,坐着一位领域专家。这位所谓的领域专家近年来因指出各种可能让一些想法逐渐消失的细节而惹恼了同事。他没有被提名为项目组成员,因为人们觉得他持续的怀疑只会让其他项目成员感到不安。不给这些专家他所需要的关注是一个很大的错误。

领域专家应该总是参与进来。越过用户工作就像在未知的领域盲目飞行。坠毁的风险很高,因为即使是最稳定的飞机也无法在无地图的岩石上生存。

悲观

在企业中,有许多影响者喜欢对数据项目持悲观态度。数据科学或大数据是法律顾问、安全专家、记者等末日预言者的天堂。有无数的陷阱和问题可以指出。语句开始如下。不管怎样,这是行不通的,因为…

  • 一切都太不确定了。
  • 数据可能会丢失。
  • 一切都是炒作,反正又会过去的。
  • 顾客和员工根本不想要改变。
  • 管理层没有足够的能力来迎接挑战。

恐惧文化

我们经常在学校学到恐惧文化。犯错的人受到惩罚,上级利用他们的权力地位来维护他们的权威。员工受到恐吓,并被期望遵循指示,而不是做出独立的决定。

这种心态对任何创意环境都是毒药。生活在这种文化中的公司将无法招募到向数据驱动型企业转变所必需的人才。所谓的高潜力通常可以选择他们的雇主,相应地很少人会进入“石器时代”的工作文化。有恐惧文化的公司也会伤害自己,因为好员工会再次离开这样的公司。

“一直都是这样”

经历过许多变化的人也知道,并不是所有的员工都能从变化中受益。年纪较大的员工尤其担心他们会因变革而失败。如果变革没有得到很好的沟通,员工权益团体可能会提出抗议。在大多数情况下,论点是公司的现状是好的,变化会危及它。

很多时候,管理者被理解为只管理资源的人。但是行政管理是不够的。要克服对变化的恐惧,领导力是必不可少的。领导者必须向员工传达,每个人都将从变革中受益。

变革对于公司的长期生存至关重要。你能冒的最大风险是抵制总体变化。

结论

数据驱动的应用正在改变我们的工作方式。拒绝接受这一趋势的后果就好像一家公司在 20 世纪 90 年代拒绝互联网一样。对于一个成功的数据战略,公司的心态必须是正确的。如果没有愿景和改变的勇气,即使有最好的工具和最聪明的数据科学家,计划也会失败。

参考

[1] Papp,Weidinger,Meir-Huber,Ortner,Langs,Wazir,数据科学(2019),HANSER

[2]m . c .康纳,《成功的关键商业技能》(2013),教学公司

任何人都可以使用机器学习构建生产就绪的 SaaS 应用程序|将 ML 带入生活

原文:https://towardsdatascience.com/anyone-can-build-production-ready-saas-application-with-machine-learning-bring-ml-to-life-af04efec1d37?source=collection_archive---------17-----------------------

使用所有必要的工具和框架,通过机器学习构建您的 SaaS 应用程序。这比你想象的要容易!

在我的职业生涯中,我构建了不同的软件即服务(SaaS)应用程序。我最近的一个 SaaS 产品是 社交形象 AI 。作为 #buildinpublic 的一部分,我想向 case 展示我用于该产品的技术堆栈。它是各种技术栈的组合,包括机器学习。在我的不同产品中多次尝试后,我想出了这个堆栈。在这篇文章中,我将谈论我所使用的技术堆栈。所以这可以帮助你创造你的产品。

从 socialimageai.com 创建的图像

你从哪里开始?这是你以前没有做过任何产品时通常会遇到的第一个问题。最近无代码平台的激增正在改变整个格局。然而,在尝试了不同的无代码产品后,我觉得没有一个能满足我的所有需求,而且它也没有市场上销售的那么灵活。

当然,在你生产任何产品之前,你需要对你计划生产的产品有一个概念。我将带你通过人工智能的社会形象镜头。下面是开发 web 应用程序的不同部分。

  1. 前端
  2. 前端部署
  3. 后端
  4. 后端部署
  5. 支付网关
  6. 证明
  7. 模型培训/调整包
  8. 模型部署
  9. 将模型结果集成到应用程序中

这可能看起来很多。但是大部分都是非常标准的建筑。我将一个一个地走过它。

前端(反应)

作为 python 的拥护者,我尝试过不同的前端框架,比如 Flask、HTML、PHP、Django 等。毫无疑问,reactjs 是最好的。作为一名 ML 工程师,你可能会想多学一门语言?因为您精通 python,所以您希望用 python 构建所有东西,并使用像 Django 或 Flask 这样的框架。但是这些框架只在一定程度上适用于 web 应用程序。

React 前端文件夹结构

react 的一个相当大的优势是社区的力量。许多非代码平台可以使用 react 为内部页面创建不同的组件,并将它们包含在您的应用程序中。此外,大量不同的模板可用于启动您的前端。所以不需要从头开始。

前端部署

一个常见的问题是如何部署。但在我看来,最具挑战性的问题是如何使用 CI/CD 以更便宜的方式部署它。您可能想要可扩展的服务和最少的停机时间。

在尝试了 render、Heroku 和其他服务之后,我觉得托管您的应用程序的最佳和最简单的方式是通过 AWS Amplify。是的,使用 AWS 可能有点奇怪,这项服务已经存在一段时间了。但是用 AWS amplify 部署一个网站实在是太容易了。

AWS 放大器

尤其是有了 react,vuejs,gatsby,或者任何其他主要的 java script 应用都可以轻松托管。一些优点是

  1. 轻松添加免费 SSL 证书
  2. 内置 CI/CD 管道。从您的代码库中推送
  3. 添加开发分支
  4. 创建拉式请求

最好的是,它是无服务器的,定价几乎等于静态网站托管。因此,今天就为您的 SaaS 应用程序试用 aws amplify 吧。

后端

如果您从事过任何机器学习(ML)应用程序,您可能会对 python 很熟悉。当您为服务器端选择 python 时,您有一些优势。您可以高效地为您的 ML 应用程序执行任何数据转换。此外,你有一些选择

  1. 无服务器
    • AWS 圣杯
      -使用 Lambda 的无服务器
  2. 服务器
    -烧瓶
    • Django
    • FastAPI

一旦为服务器端应用选择了 python,就需要决定是使用 FastAPI 还是 Flask。FastAPI 是一个快速发展的框架,有很多好处。不过 Flask 还是比较成熟的,有了合适的文件夹结构就可以轻松维护。如何构建 Flask 应用程序也很重要。您可以将所有的密钥和配置分组到一个文件中,以便于更新。

烧瓶文件夹结构

后端部署

与前端类似,我们需要在可伸缩的环境中部署后端。说到后端,有很多选择。

  1. 使用 Lambda 的无服务器部署
  2. AWS 弹性豆茎展开
  3. 渲染或 Heroku 部署

当然,无服务器的优势在于维护成本低,服务器成本低。然而,在大多数情况下,对于一个应用程序,我们可能需要一个服务器;否则,它需要一些时间来设置通过无服务器。

Elastic beanstalk 可用于以可伸缩的方式部署您的应用程序。所有你需要的是一个配置文件,并做 eb 初始化。该命令将引导您完成要执行的步骤。使用 beanstalk 的一个优点和缺点是使用负载平衡器。这是增加成本的一个原因,但它可以防止或处理应用程序中任何看不见的峰值。我们可以在没有负载均衡器的情况下使用它,然后我们就错过了免费证书的好处,并涉及到其他配置。

弹性豆茎的另一种方法是使用 EC2 或 AWS Lightsail 机器并执行您的配置。这里我们需要一个 apache web 服务器,并使用 LetsEncrypt 创建一个证书。配置并不庞大;然而,它涉及一些设置工作。

Render 或 Heroku 选项托管 flask 应用程序更便宜。它还提供免费的 SSL 证书。然而,如果您的整个堆栈是 AWS 或任何云提供商,我会建议使用相同的提供商来减少一些延迟。

支付网关

我们中的许多人需要一个支付网关,我们中的大多数人都知道 stripe。Stripe 是您可以使用的最佳支付网关。直接用条纹的话便宜。最重要的是,当您没有任何身份验证时,它更容易集成。如果你有多个计划,就有点复杂了。

条带订阅

几个服务包装了 stripe,并提供了一个极其简单的集成选项,如 Gumroad ,或者查看这篇文章了解更多细节。

证明

如果你想开发一个带有不同支付选项的简化登录选项的产品,认证曾经是一个很大的难题。然而,最近有大量的服务简化了这种方法。使用 AWS Amplify 的一个好处是,您可以通过一行命令行为托管 UI 添加身份验证。

添加身份验证

当你使用 AWS Amplify 时,它会在后台使用 AWS Cognito 服务。不过来自 AWS 的登录设计并不是很棒。所以我们可能需要为我们的应用程序定制它。如果是 react 前端,我们需要更新 react 代码。社交图像 AI 在后端使用 Cognito 使用基于 react 的登录屏幕。如果你是新手,Cognito 几乎是免费的(高达 50k MAUs)。其他选项有使用 okta、Firebase Auth、Auth0 等。大多数认证服务将满足基本需求。由于通过 amplify 添加 Cognito 更加简单,我们将使用它作为身份验证提供者。

模型训练/调谐包

模型可能是应用程序的大脑。通常你需要训练或者微调一个模型。最近几天,大多数模特都不是从零开始训练的。微调的话,我一般更倾向于 AWS sagemaker 笔记本,导入之前的 NLP/vision 模型。我是 PyTorch 的忠实粉丝,所以我的大部分模特都在 PyTorch。

假设您要微调一个 BERT 模型。以下是清单

  1. 你有数据集可以微调吗?
  2. 数据集是否足够大,可以进行微调?
  3. 可以下载预先训练好的 BERT 模型吗?
  4. 你能创建微调代码和转换吗?
  5. 你能成功地微调模型吗?

如果你需要一些微调方面的帮助,请查看文章。

车型部署

你不可能拥有一个没有连接的大脑。在这一部分,大脑与身体相连。在本节中,我们将部署模型。同样,sagemaker 是部署模型的可伸缩方式。下面是部署的清单

  1. 你对模特的表现满意吗?
  2. 模型有多大?
  3. 模型的可接受延迟是多少?
  4. 推理需要 GPU 的吗?
  5. 您希望模型作为端点还是批处理?
  6. 如何在应用程序中使用模型?
  7. 你有多种型号吗?
  8. 如果有多个模型,如何在单个服务器上部署它们?

有时,根据型号的不同,这些步骤可能需要很长时间。由于重试次数不同。这里有一篇文章展示了如何在 AWS sagemaker 中部署一个模型。

多模式部署

如果模型训练代码是按照 sagemaker 函数构建的,那么部署它将会更容易。否则,需要创建模型部署结构,然后进行部署。

将模型结果整合到应用中

如果您的服务器端是 python 框架(FastAPI 或 Flask),并且在 AWS 中,调用该模型可能很容易。下面是一个调用总结模型的示例代码。

正在获取 Sagemaker 数据

如果模型会被 Python 之外的人消费,那么最好集成 API gateway 和 lambda 函数。作为第一步,您将在 lambda 函数中编写类似的代码,然后将从 API 网关调用 lambda 函数。

下一步是什么?

现在想想你的想法,开始创造你的产品。如果你想详细了解,请订阅以下书籍/教程。

从教程中学习—从 socialimageai.com 创建

目前我正在写一本书和视频教程,里面有从头开始创建 SaaS 产品所需的所有代码。它将引导您通过机器学习来构建产品的构建模块。如果你有兴趣,请点击 订阅此处 获取相关通知。

获取代码

请订阅我的 简讯 获取我的文章和其他更新的免费工作代码。

服用类固醇的阿帕奇气流 2.0——EKS·库伯内特和 MWAA

原文:https://towardsdatascience.com/apache-airflow-2-0-on-steroids-eks-kubernetes-mwaa-ec341d52fe2d?source=collection_archive---------13-----------------------

这篇文章将告诉我们如何在 EKS 自动气象站上运行气流 2.0,以及它在新 MWAA 上的好处。它还将把它与原生的 Kubernetes executor 联系起来,并告诉我们它与它有什么不同,类似的还有 Fargate vs EKS 的节点管理 EC2。

上个月,2021 年 5 月,AWS 发布了针对 Apache Airflow (MWAA)的 AWS 托管工作流的更新,其中包括 Airflow 2.0,这让我想到,当 AWS 以最新的稳定版本提供托管服务,价格并不昂贵,更不用说我们可以从 AWS 获得的支持(以前 MWAA 有一个旧的 Airflow 版本,不支持云形成)时,为什么现在有人要自己管理 Airflow(典型的云困境)。但我担心自己处于尚未经过实战检验的技术的前沿。另一方面,AWS 几个月前发布了一个关于 EKS 气流的版本,我们都知道 K8s 的可扩展性,所以我想尝试一下。我将站在技术的前沿,但至少它不会是一个黑匣子。

假设我们都知道 Kubernetes 的组成部分以及什么是舵轮图,我将这篇文章分成两个子主题,即:

  1. 在 EKS 和 MWAA 部署 air flow 2.0 &
  2. 添加 Dag、变量、插件、连接等

来源:pixabay

在 EKS 和 MWAA 部署 Airflow 2.0

简言之,K8s 中的节点组可以使用 AWS Fargate 或节点管理的 EC2 来创建。两者都可以横向扩展,但主要区别是,Fargate AWS 将管理工作节点扩展及其 AMI 配置,而在节点管理中,我们必须同时做这两件事,但它的好处是相当可观的。由于 Fargate 不支持 daemonsets,使用受管节点组,我们不必将 Dynatrace、Splunk 或 Datadog 等监控服务的代理放在 sidecar 容器中,因此更加可靠。此外,Fargate 不缓存容器图像,因此启动时间会更长。根据我的经验,Fargate 更适合基于气流的工作流,其中大多数是批处理,而节点管理更适合与流相关的模式。

先决条件

  • 在您的角色需要访问的地方,设置了一个基于 Linux 的环境。我正在使用 Ubuntu windows 应用程序。

创建集群

在创建集群之前,我们需要安装 kubectl 和 eksctl。eksctl 将创建和管理集群,而 kubectl 将与集群 API 服务器通信,以检索所需的节点、pod 和名称空间。幸运的是,二进制文件对他们两个都是可用的。

curl -o kubectl [https://amazon-eks.s3.us-west-2.amazonaws.com/1.20.4/2021-04-12/bin/linux/amd64/kubectl](https://amazon-eks.s3.us-west-2.amazonaws.com/1.20.4/2021-04-12/bin/linux/amd64/kubectl)chmod +x ./kubectlmkdir -p $HOME/bin && cp ./kubectl $HOME/bin/kubectl && export PATH=$PATH:$HOME/bin

对于 eksctl 来说,

curl — silent — location “[https://github.com/weaveworks/eksctl/releases/latest/download/eksctl_$(uname](https://github.com/weaveworks/eksctl/releases/latest/download/eksctl_$(uname) -s)_amd64.tar.gz” | tar xz -C /tmpsudo mv /tmp/eksctl /usr/local/bin

现在让我们使用 Fargate 创建一个 k8 集群,命令如下。这大约需要 15–20 分钟,用户可以查看有关将要创建的资源(Vpc、子网等)的云信息。

eksctl create cluster \
 — name test-cluster \
 — region ap-southeast-2 \
 — fargate

它会显示如下内容:


【】EKS 集群
*ap-southeast-2* 【地区】中的“测试集群”准备就绪

接下来,让我们创建一个节点管理的集群,区别就在这里。我们需要首先在本地创建将在 EC2 连接中使用的 pub 私有密钥对。

aws ec2 create-key-pair — region us-west-2 — key-name myKeyPaireksctl create cluster \
 — name eksctl-airflow-cluster \
 — region ap-southeast-2 \
 — with-oidc \
 — ssh-access \
 — ssh-public-key <your-key> \
 — managed

您可以在 CFN 中查看有关创建的节点组的信息。

作者图片

通过运行以下命令,我们可以确认安装,它应该会显示群集中存在的节点:

kubectl get nodes -o wide

接下来,我们需要更新 kubeconfig,以便无论何时运行 kubectl,它都应该指向正确的集群。

aws eks — region ap-southeast-2 update-kubeconfig — name eksctl-airflow-cluster

接下来,创建名称空间,以便我们可以在其中部署气流。它只是一种抽象,将相关资源保存在一个地方,就像一个堆栈一样。一个集群可以有许多可以相互通信的名称空间。

kubectl create namespace airflow

在命名空间中创建气流之前,我们需要安装 helm

$ curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3
$ chmod 700 get_helm.sh
$ ./get_helm.sh

下面将创建气流资源,完成后你可以在 localhost:8080 查看。我们可以给任何你想要的版本名。

helm repo add airflow-stable [https://airflow-helm.github.io/charts](https://airflow-helm.github.io/charts)
helm repo updatehelm install RELEASE_NAME airflow-stable/airflow 
 — namespace airflow \
 — version 8.3.0

你可以在 https://github.com/airflow-helm/charts/releases 找到对应于最新气流发布的其他图表版本。

在检查 GUI 之前,我们需要使用以下命令将 8080 端口从 pod 转发到本地主机:

export POD_NAME=$(kubectl get pods — namespace NAMESPACE -l “component=web,app=airflow” -o jsonpath=”{.items[0].metadata.name}”)
kubectl port-forward — namespace NAMESPACE $POD_NAME 8080:8080

添加 Dag、变量、插件、连接等

现在有趣的部分来了,舵图给出了 values.yml,其中提到了所有气流配置、变量、dagbag 文件夹等,这有助于更新实际气流。因此,现在剩下的唯一事情就是给它所需的值,并使用下面的命令进行更新:

helm upgrade airflow airflow-stable/airflow — namespace airflow — version 8.3.0 — values values.yml — debug

下面是 values.yml 的一部分,我们可以在其中指定需要安装的 python 模块

airflow:
    extraPipPackages: 
        — “pandas”
        — “awscli”

类似地,下面是 values.yml 的一部分,其中指定了我们在部署 efs 后获得的持久卷和声明。

airflow:
    extraVolumeMounts:
        — name: airflow-efs-dag
          mountPath: /opt/efs/

    extraVolumes:
        — name: airflow-efs-dag
          persistentVolumeClaim:
              claimName: efs-storage-claim

下面是它的大袋子:

dags:
    path: /opt/efs/dags

有趣的是, MWAA 也有类似的设置,它要求我们在云信息模板中提及所有这些细节。为了添加一个气流变量,MWAA 使用了环境变量,为了更新 env,我们需要在 CFN 中提到它。由于 AWS SSM 是 MWAA 的后端,我们可以指定包含 conn_string 的连接的键名,MWAA 将使用键名选择它并将其部署在 Airflow 中。至于 DAG、插件文件夹和 requirements.txt,我们需要给它一个 s3 文件夹,但问题是它们都应该在同一个桶中。对任何刚开始的人来说都不是瓶颈。MWAA 在后端使用 RDS 或 Aurora,但它不会让用户随意摆弄。它只使用芹菜执行器,并根据不同的机器类型提供三种类型的集群。总之,它只能支持 1000 个 Dag 运行。因此,MWAA 对中小企业有利,但对大公司不利。在撰写本文时,使用 MWAA 的一个缺点是,即使您通过 CFN 更新气流变量,调度程序也会在部署时停止,并需要大约 10-20 分钟才能运行。因此,需要考虑一个适当的发布策略,即每当有新的构建要部署时,不运行或运行较少的 Dag。EKS 也需要几分钟来完成头盔升级,但这比 MWAA 更透明,因为它提供了日志。

此外,对于 EKS,我们需要自己集成 EFS 和 RDS,这应该不是什么大问题,然后任何 CICD 都可以将更新的 Dag 从代码库推送到 EFS。

气流舵的 values . yml:https://github . com/air flow-helm/charts/blob/main/charts/air flow/values . YAML

EKS &库伯内特的遗嘱执行人

在 Airflow 中使用 k8 executor 时,每个任务都在 pod 上运行,而 airflow 可以在单独的机器上运行。然而,在 EKS,airflow 的所有部分,即 airflow db、调度程序等都在单独的 pod 中运行,因此具有更高的 HA 和可靠性。

结论

如果您已经完成了,那么恭喜您,您已经学会了如何使用 fargate 和节点管理 ec2 在 EKS 上部署 Airflow 2.0。我们还学习了如何定制 values.yml,它将具有气流配置和其他规定,如指定变量、连接等。在我看来,一切都将在不久的将来成为 kubernetes,我们拥有将存储与计算分离的优势,可以随时利用潜在的无限量计算。这完全取决于您在理解和实现数据工程前景的旅程中处于哪个阶段。

Apache air flow——编写 Dag 的新方法

原文:https://towardsdatascience.com/apache-airflow-a-new-way-to-write-dags-240a93c52e1a?source=collection_archive---------16-----------------------

扩展数据管道的 ETL 框架

作者图片

问候数据从业者。
现在是 2021 年,数据工程是新的数据科学。
在数据工程领域花费的时间和投资
都有所增加。

您有多少次构建模型或执行分析时才意识到您使用的数据有问题?—如果你已经在这个行业工作了一段时间,这种情况会很常见。

为了消除我们的疑虑,我们经常需要咨询数据工程师来验证我们数据的完整性。如果我们自己是数据工程师,我们将不得不钻研数据管道来理解我们数据的 ETL。

随着数据行业的成熟,数据管道和基础设施越来越受到重视。这就是今天文章的内容。

阿帕奇气流

Apache Airflow 是我们用来管理数据管道的最流行的工作流管理系统之一。crontabs 被用作管道调度程序的时代已经一去不复返了。

Apache Airflow 是批处理管道的最佳解决方案之一。如果您的公司非常重视数据,那么采用气流可以为未来的项目带来巨大的好处。像脸书Airbnb 这样的科技巨头在他们的日常数据任务中使用 Apache Airflow。

先决条件

这是一篇关于在 Apache Airflow 上构建框架的文章。
因此,我们假设你已经了解气流的基本概念,比如——

  • 熟练的技艺
  • 经营者

如果你不熟悉,可以通读一下我做的关于 Apache 气流基础的综合指南。

不言而喻,你了解基本的 Python

gif 作者reactiongifs.com

框架

凯利·西克玛在 Unsplash 上拍摄的照片

首先,你可能会问— 为什么我们甚至需要一个框架?

有了框架,我们就可以建立结构更好的管道。更容易维护刻度。更不用说它还符合业务规则编程惯例

当你开始拥有大量的管道时,框架可以节省你部署管道的时间,让你可以集中精力为管道开发代码。

这是阿帕奇气流中的管道是如何部署的—

作者图片

  1. 在 Airflow 的 DAG 文件夹中创建了一个 DAG.py 文件,其中包含操作符的导入、DAG 配置(如时间表和 DAG 名称),并定义了任务的依赖关系和顺序。
  2. 操作员是在气流中的操作员文件夹中创建的。它们包含具有执行任务逻辑的 Python 类。它们在 DAG.py 文件中被调用。

这是一个示例 DAG 和运算符—

DAG:

操作员:

看起来很整洁。然而,当管道中有多个任务时,事情就变得混乱了。每个任务需要不同的操作员,它们都需要不同的变量。

你的 DAG.py 文件很快就会被一行行不可读的代码淹没。
文件路径、SQL 查询,甚至随机变量,当它们开始堆积起来时,读者会感到困惑。

让我们使用一个框架来部署我们的管道。

作者图片

  1. 不是. py 文件,而是为每个 DAG 创建一个文件夹。文件夹里面,一个**。创建 py 文件**、 **task_config.json、**和 job_config.json 。的。py 文件是一个调用 create_dag_operator.py 的单行程序。
  2. create_dag_operator.py 包含了设计我们管道的任务流的所有逻辑。它还从作业和任务配置文件中提取所有操作员所需的变量。在任务流构建期间,它从这里导入所有其他定制操作符。

下面是管道是如何与框架一起部署的—

任务配置:

作业配置:

DAG:

操作员:

从这里,你可以清楚地看到 CreateDagOperator 做了所有繁重的工作。观察 DAG 文件有多简单。

配置文件是简单的 python 字典。作业配置主要由 DAG 配置组成,如名称、计划和开始日期。任务配置由任务配置和实例变量组成,比如任务名称和任何需要通过被调用的操作符传递的内容。

在 **CreateDagOperator 中,**定义了任务流依赖、DAG 创建和任务定义。所有必需的变量都从配置文件中提取出来,并在这里进行实例化。

对于你的团队成员来说,这个框架让事情变得整洁和简单。他们确切地知道他们必须在配置文件中输入什么,同时只在 CreateDagOperator 中开发逻辑。

当然,这只是一个基本管道的例子。对于特殊的作业和定制的任务流,只需在 CreateDagOperator 中用创建一个新的静态方法,并在 DAG 文件中调用它。

这是框架下你的文件夹结构应该是什么样子的—

作者图片

在框架上扩展,您可以在 sample_dag 文件夹中创建文件夹用于不同的目的。例如,如果您的管道以某种方式涉及。csv 文件,您可以创建一个名为“resources”的文件夹,并将您的。csv 文件放在那里。然后,您可以从 CreateDagOperator 中调用它。然后,您可以将配置文件放在名为“config”的文件夹中。

这提供了一种更加系统化的方式来组织你的文件
我今天在这里提供的是框架的基本结构
完全取决于你在上面展开,这就是乐趣发挥的地方,不是吗?

Gif by Memecandy

这里有一个文件夹结构的实际例子—

https://github.com/nickefy/Apache-Airflow---A-New-Way-to-Write-DAGs

摘要

Jason Strull 在 Unsplash 上拍摄的照片

如果你已经走了这么远,恭喜你。你一定对数据非常认真。

你应该担心——数据将很快成为我们经济的燃料。

在这篇文章中,你学到了—

  • 阿帕奇气流的基本结构
  • 在 Apache Airflow 上构建一个基本框架

像往常一样,我引用一句话作为结束。

每个公司的未来都有大数据,每个公司最终都会从事数据业务。——托马斯·h·达文波特 ,美国学者兼作者

订阅我的时事通讯,保持联系。

也可以通过 我的链接 注册一个中等会员来支持我。你将能够从我和其他不可思议的作家那里读到无限量的故事!

我正在撰写更多关于数据行业的故事、文章和指南。你绝对可以期待更多这样的帖子。与此同时,你可以随时查看我的其他 文章 来暂时填补你对数据的渴望。

感谢 的阅读!如果你想和我联系,请随时通过 nickmydata@gmail.com 联系我或者我的 LinkedIn 个人资料 。你也可以在我的Github中查看之前写的代码。

集装箱化数据管道的阿帕奇气流

原文:https://towardsdatascience.com/apache-airflow-for-containerized-data-pipelines-4d7a3c385bd?source=collection_archive---------14-----------------------

在 Airflow 上使用不同版本的 Python 运行任务时是否会遇到问题?在本文中,我将解释如何解决这个问题。

弗兰克·麦肯纳在 Unsplash 上拍摄的照片

介绍

您可能以前听说过 Apache Airflow,或者您现在正在使用它来调度您的数据管道。而且,根据您要运行的内容,您的方法是为它使用一个操作符,您使用 SparkSubmitOperator 调度 Spark 作业,并使用 BashOperator 在 Airflow 正在运行的机器上运行一些东西。当您需要运行 Python 代码时,事情就变得有趣了。

您可以再次查阅文档,找到著名且有用的 PythonOperator。很直白。您只需将想要运行的 Python 代码放在那里,它将根据您的 DAG 的配置运行。您只需要确保您的代码运行在 Python 版本的 Airflow 上,并且您的库安装在系统上。

您的工作流使用 Python 的次数越多,您使用 PythonOperator 的次数就越多。您很高兴,因为您能够将 Python 知识用于所有数据科学任务。Airflow 允许您在其中安装 Python 模块,因此您可以手头上有所有您喜欢的库来做非凡的事情。

在您或您团队中的某个人想要使用新版本的 Python 或新的库之前,所有这一切都运行良好,此时您已经有许多任务在运行旧版本且不兼容的库,您有两个选择:

  • 更新库并更新所有有问题的任务。
  • 根据所有现有任务使用库的过时版本。

让我们更深入地了解这些选项,并分析第一个选项。您已经有 30 个任务在使用旧版本的库,因此您和您的团队可以抽出时间,用这些更改来更新库和所有这些作业。这不是一个简单的任务,但你们都做到了。最后,这是值得的,因为您有机会使用库的所有新特性,并且您的代码库是与最新趋势同步的。但是这是不可伸缩的,当你有 100 个任务在运行,你需要做同样的事情,会发生什么呢?或者仅仅是您的旧任务出于某种原因需要旧版本的库?

现在我们来分析第二种选择,这种选择似乎不那么痛苦。你用一个过时版本的库开发你的任务,然后就结束了。这并不理想,但也许你知道如何用过时的版本完成工作。但同样,这是不可扩展的,一两年后,你继续用一个过时的版本开发,你将面临各种问题,其中之一是你将需要一些新的东西,在这一点上用过时的版本库很难完成,而且你做不到。这真的会挫伤积极性,并且会影响到你和你的团队。最终,你的代码库将不会是一个你可以创新和尝试新事物的快乐地方。

现在让我们想象一个更糟糕的场景,当我们需要更新系统的 Python 版本或者我们的一个库与 Airflow 本身不兼容时。这是一个主要的问题,而且非常普遍,尤其是当团队在成长的时候。

那么,我们能做些什么来解决这个问题呢?

使用容器

我们之前问题的解决方案是使用 Docker 容器。

气流调度集装箱化任务—作者图片

想象一下,您可以创建一个 DAG,其中每个任务都在一个 Docker 容器中运行,由于容器的原理,您需要安装任何版本的库或您想要用于它的语言。

这将使您能够自主地使用新技术创建任务,而无需承担更新已经运行的任务的负担。

但是我如何才能做到这一点呢?

让我们探讨几个选项。

在 Kubernetes 上安排作业

KubernetesPodOperator 的气流调度任务—图片由作者提供

如果你不知道 Kubernetes 是什么,你可以去这里。

通过这种方法,我们在 Kubernetes 中以 pod 的形式推出了 Docker 容器。这些吊舱是短暂的,这意味着一旦任务完成,吊舱被摧毁,它不会使用系统的容量。

因此,每个任务都是一个 Docker 容器,每个容器都作为一个 Pod 在 Kubernetes 上启动。Airflow 将日志从 Kubernetes 拉到任务的 UI 中,因此任务在哪里运行对您来说是透明的,最后您可以继续正常使用 Airflow。

为此,我们使用了 KubernetesPodOperator 。

[KubernetesPodOperator](https://airflow.apache.org/docs/apache-airflow-providers-cncf-kubernetes/stable/_api/airflow/providers/cncf/kubernetes/operators/kubernetes_pod/index.html#airflow.providers.cncf.kubernetes.operators.kubernetes_pod.KubernetesPodOperator)使用 Kubernetes API 在 Kubernetes 集群中启动一个 pod。通过提供一个图像 URL 和一个带有可选参数的命令,操作员使用 Kube Python 客户机来生成一个 Kubernetes API 请求,该请求可以动态启动这些单独的 pod。用户可以使用config_file参数指定一个 kubeconfig 文件,否则操作员将默认为~/.kube/config

这是我发现的更通用的解决方案,可以在主要的云提供商甚至本地安排容器化的任务。Kubernetes 是作为 Azure、AWS、Google Cloud、Linode 等平台上的服务提供的。可能是库伯内特公司的解决方案。

在 GKE 上安排工作(Google Kubernetes 引擎)

GKE 上的气流调度任务——图片由作者提供

如果你在谷歌云上,你计划使用的 Kubernetes 集群是托管版本(GKE)。为了方便起见,合适的操作员是gkestartpoperator。你可以使用 KubernetesPodOperator,但最终需要更多的工作来设置所有的环境,如果你在谷歌云上,我认为这是推荐的方法。

ECS(弹性集装箱服务)上的作业调度

ECS 上的气流调度任务—图片由作者提供

现在让我们假设你在 AWS 上,他们提供了一个叫做弹性库本内特服务或 EKS 的解决方案。如果你要在那里启动任务,你可以使用 KubernetesPodOperator。

但是 AWS 提供了另一种容器编排服务,称为弹性容器服务或 ECS,正如你可能已经猜到的,其中有一个操作符来启动容器化的任务,它被称为 ECSOperator 。

在 ECS 上,没有 pod,这个概念有点不同,在那里它们被称为任务。您将在 ECS 任务中安排气流集装箱化任务。我知道,这听起来令人困惑,但原理和豆荚是一样的,它们是短暂的。因此,一旦气流任务完成,它就不会消耗资源。

考虑

  • 当您在群集中运行任务时,请确保您的群集拥有所有可用的权限和凭据。如果集群与 Airflow 运行的集群不同,并且您正在迁移任务,这是您需要考虑的问题。
  • 运行任务所需的额外 Kubernetes 或 ECS 群集的成本不容小觑。你一定要考虑到这一点。
  • 一个是我们之前用来在 Kubernetes 上运行任务的 KubernetesPodOperator,另一个是 Kubernetes Executor,最后一个是使用 Kubernetes 来运行 Airflow 本身。请确保你不要混淆这些,有时网上有很多信息可能会使这种情况发生。
  • 您不一定需要在 Kubernetes 或 ECS 上运行 Airflow 本身,您可以在外部集群中调度任务。

结论

这不是银弹,因为在所有的技术决策中,总是有一个权衡。是的,容器允许你有更好的隔离,但也给你更多的操作复杂性。如果您想让这种方法为您工作,您需要有一个 CI/CD 管道,这样部署一个任务的新版本就不会成为负担。

尽管不受我们可以用来完成任务的库或语言的限制很酷。这通常是人们所忽略的,但是能够试验和尝试新事物对团队的士气是一个主要的优势。

资源

  • KubernetesPodOperator
  • GKEStartPodOperator
  • ECSOperator

Python 中的 Apache Kafka:如何与生产者和消费者共享数据流

原文:https://towardsdatascience.com/apache-kafka-in-python-how-to-stream-data-with-producers-and-consumers-307e84ca8bdb?source=collection_archive---------18-----------------------

从头开始编写 Kafka 生产者和消费者的流聊天数据。

照片由努贝尔森·费尔南德斯在 Unsplash 拍摄

在大数据的世界里,可靠的流媒体平台是必不可少的。阿帕奇卡夫卡是一条路要走。

今天的文章将向您展示如何使用 Python 与 Kafka 生产者和消费者合作。你应该通过 Docker 配置 Zookeeper 和 Kafka。如果不是这样,请在继续之前阅读这篇文章或观看这段视频。

不想看书?坐下来看:

本文涵盖以下主题:

  • 环境设置
  • 用 Python 创建假数据
  • 用 Python 写卡夫卡制作人
  • 用 Python 编写 Kafka 消费者
  • 测试

你可以在 GitHub 上找到源代码。

环境设置

首先打开一个新的终端窗口并连接到 Kafka shell。您应该已经运行了 Zookeeper 和 Kafka 容器,如果不是这样,请启动它们:

docker exec -it kafka /bin/sh
cd /opt/kafka_<version>/bin
ls

以下是您应该看到的内容:

图 1——打开卡夫卡的外壳(图片由作者提供)

接下来,您应该创建一个主题来存储 Python 生成的消息。以下是如何创建一个名为messages的主题,然后通过列出所有 Kafka 主题来验证它是如何创建的:

图 2——创建一个卡夫卡主题(作者图片)

这就是你在卡夫卡外壳里要做的一切。您现在可以通过在控制台中键入exit来离开它。下一步是安装 Python 包来使用 Kafka。它叫做kafka-python,你可以用下面的命令安装它:

python3 -m pip install kafka-python

安装过程应该是这样的:

图 3 —为 Kafka 安装 Python 库(图片由作者提供)

快到了!最后一步是创建一个文件夹来存储 Python 脚本和脚本文件本身。我在桌面上创建了一个kafka-python-code文件夹,但是你可以把你的放在任何地方:

cd Desktop
mkdir kafka-python-code && cd kafka-python-codetouch data_generator.py
touch producer.py
touch consumer.py

这就是你开始编码所需要的一切!

用 Python 创建假数据

如果你想了解卡夫卡是如何工作的,你需要一种方法来确保恒定的数据流。这就是为什么您将创建一个生成虚假用户消息数据的文件。

文件data_generator.pygenerate_message()函数将有这个任务。它将选择一个随机的用户 ID 和收件人 ID——只是一个从 1 到 100 的随机数,确保它们不相同——并以 32 个字符的随机字符串形式创建一条消息。

因为我们将发送编码为 JSON 的消息,所以这三个都以 Python 字典的形式返回。

下面是完整的代码:

import random 
import string user_ids = list(range(1, 101))
recipient_ids = list(range(1, 101)) def generate_message() -> dict:
    random_user_id = random.choice(user_ids) # Copy the recipients array
    recipient_ids_copy = recipient_ids.copy() # User can't send message to himself
    recipient_ids_copy.remove(random_user_id)
    random_recipient_id = random.choice(recipient_ids_copy) # Generate a random message
    message = ''.join(random.choice(string.ascii_letters) for i in range(32)) return {
        'user_id': random_user_id,
        'recipient_id': random_recipient_id,
        'message': message
    }

这个功能很简单,但是它能很好地满足我们的需求。接下来,我们用 Python 写一个卡夫卡制作人。

用 Python 写卡夫卡制作人

这是有趣的事情开始的地方。现在您将看到如何用kafka-python库编写一个生产者代码。打开producer.py文件,你就可以开始了。

去往卡夫卡的消息需要以某种方式序列化。既然我们以 Python 字典的形式获得它们,那么唯一合理的选择就是 JSON。您将编写一个助手serializer()函数,将它看到的任何东西都转换成 JSON 并编码为utf-8

卡夫卡制作人需要知道卡夫卡在哪里运行。如果您在配置阶段没有更改端口,那么您的端口可能在localhost:9092上。此外,KafkaProducer类需要知道值将如何被序列化。你知道这两个问题的答案。

现在到了您将生成消息并将它们发送到messages主题的部分。您将创建一个无限循环,因此消息会一直发送,直到您停止 Python 脚本。在循环中,您将生成消息,打印带有时间戳的消息,并将其发送到 Kafka 主题。

为了避免控制台中的消息数量过多,最好在消息发送后设置一些睡眠时间。Python 将休眠随机的秒数,范围在 1 到 10 秒之间。

这就是卡夫卡制作人的全部。以下是完整的源代码:

import time 
import json 
import random 
from datetime import datetime
from data_generator import generate_message
from kafka import KafkaProducer # Messages will be serialized as JSON 
def serializer(message):
    return json.dumps(message).encode('utf-8') # Kafka Producer
producer = KafkaProducer(
    bootstrap_servers=['localhost:9092'],
    value_serializer=serializer
) if __name__ == '__main__':
    # Infinite loop - runs until you kill the program
    while True:
        # Generate a message
        dummy_message = generate_message()

        # Send it to our 'messages' topic
        print(f'Producing message @ {datetime.now()} | Message = {str(dummy_message)}')
        producer.send('messages', dummy_message)

        # Sleep for a random number of seconds
        time_to_sleep = random.randint(1, 11)
        time.sleep(time_to_sleep)

*简单吧?*我们仍然需要编写消费者代码,所以我们接下来就这么做吧。

用 Python 写卡夫卡式的消费者

卡夫卡的消费者将更容易编码出来。当消费者启动时,您将获得来自messages主题的所有消息,并将它们打印出来。当然,您并不局限于打印消息——您可以做任何您想做的事情——但是让我们把事情简单化。

auto_offset_reset参数将确保最早的消息首先显示。

以下是完整的源代码:

import json 
from kafka import KafkaConsumer if __name__ == '__main__':
    # Kafka Consumer 
    consumer = KafkaConsumer(
        'messages',
        bootstrap_servers='localhost:9092',
        auto_offset_reset='earliest'
    )
    for message in consumer:
        print(json.loads(message.value))

这就是你要做的一切!接下来让我们对它们进行测试。

测试

如果可以的话,并排打开两个终端窗口。这在 Mac 上很容易,因为你可以按照你认为合适的方式排列标签。在两个选项卡中打开 Python 脚本所在的文件夹:

图 4 —测试 Python 消费者和生产者(1)(图片由作者提供)

您将希望首先启动消费者,因为您不想错过任何消息。所有旧邮件将首先被打印,您将看到新邮件在生成时被打印出来。使用以下命令启动消费者:

python3 consumer.py

现在,在另一个选项卡中使用以下命令来启动生成器:

python3 producer.py

您将立即看到一条打印出来的消息:

图 5 —测试 Python 消费者和生产者(2)(图片由作者提供)

请记住,生成器在发送下一条消息之前会随机休眠几秒钟。最好让两个窗口都运行一分钟左右,只是为了验证一切都正常工作。

以下是我的机器上的结果:

图片 6 —测试 Python 消费者和生产者(3)(图片由作者提供)

一切都像广告宣传的那样有效!接下来让我们总结一下。

离别赠言

这就是你的第一个基于 Python 的 Apache Kafka 消费者和生产者代码。当然,这是一个虚拟的例子,但是不管您要做什么样的代码更改,原则都是一样的。

如果有些事情对你不起作用,参考视频,因为这样更容易跟进。

对于整个 Apache Kafka 迷你文章/视频系列来说都是如此——我希望它足够容易理解,并且您在这个过程中学到了一些有用的东西。感谢阅读。

喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。

https://medium.com/@radecicdario/membership

保持联系

  • 在媒体上关注我,了解更多类似的故事
  • 注册我的简讯
  • 在 LinkedIn 上连接

原载于 2021 年 9 月 27 日https://betterdatascience.comT22。

Apache Spark 3.1 发布:Kubernetes 上的 Spark 现在已经正式发布

原文:https://towardsdatascience.com/apache-spark-3-1-release-spark-on-kubernetes-is-now-generally-available-65cccbf1436b?source=collection_archive---------16-----------------------

随着 2021 年 3 月发布的 Apache Spark 3.1,Kubernetes 项目上的 Spark 现已正式宣布为生产就绪,并普遍可用。这是自 Spark 2.3(2018 年 2 月)添加对 Spark-on-Kubernetes 的初始支持以来,3 年来快速增长的社区贡献和项目采用的成就。在本文中,我们将回顾 Spark 3.1 的主要特性,特别关注对 Spark-on-Kubernetes 的改进。

相关资源:

  • 关于使用 Kubernetes 作为 Spark(而不是 YARN)的资源管理器的介绍,请看在 Kubernetes 上运行 Spark 的 优点&缺点
  • 有关在 Kubernetes 上成功使用 Spark 的技术指南,请查看 设置、管理&监控 Kubernetes 上的 Spark
  • 关于数据机制的背景知识,请查看 Kubernetes 上的Spark Made Easy:Data Mechanics 如何改进开源版本

Spark-on-Kubernetes 之旅:从 2.3 中的 beta 支持到成为 3.1 中的新标准

随着 2018 年初 Spark 2.3 的发布,Kubernetes 成为了 Spark 的新调度程序(除了 YARN、Mesos 和 Standalone mode 之外),这要归功于 RedHat、Palantir、谷歌、彭博和 Lyft 等少数几家带头开展该项目的大公司。这种最初的支持是实验性的——缺乏特性,并且存在稳定性和性能问题。

从那时起,社区支持蓬勃发展,许多大大小小的公司都被 Kubernetes 的好处所吸引:

  1. 本地集装箱化。使用 Docker 来打包你的依赖项(和 Spark 本身)——查看我们为 Spark 优化的 Docker 图片。
  2. 高效的资源共享和更快的应用启动时间。
  3. 丰富的开源生态系统减少了云提供商和供应商的束缚

这个项目的主要特点是——从 2.4 中的 PySpark & R 支持、客户端模式和卷挂载等基本要求,到动态分配(3.0)和更好的节点关闭处理(3.1)等强大的优化。在过去的 3 年中,总共有超过 500 个补丁(改进和错误修复)被贡献出来,使得 Kubernetes 上的 spark 更加稳定和高效。

Kubernetes 上 Spark 改进的时间线从 2018 年的 Spark 2.3,到 2021 年 3 月最新的 Spark 3.1。图片作者。

因此,Kubernetes 越来越被认为是 2021 年新 Spark 项目的标准资源管理器,我们可以从开源Spark-on-Kubernetes operator项目的受欢迎程度,或者主要供应商采用 Kubernetes 而不是 Hadoop YARN 的声明中看出这一点。

有了 Spark 3.1,Spark-on-Kubernetes 项目现在被认为是普遍可用和生产就绪的。在这个最新版本中,超过 70 个错误修复和性能改进被贡献给了这个项目。现在,让我们深入了解最具影响力的功能,也是我们的客户热切期待的功能。

更好地处理节点关闭——优雅的执行器退役(新的 Spark 3.1 特性)

这个功能( SPARK-20624 )是由 Holden Karau 实现的,目前只适用于 Kubernetes 和单机部署。它被称为“更好地处理节点关闭”,尽管“优雅的执行器退役”是它的另一个好名字。

这个特性使得 Spark 在使用 spot 节点(也就是 GCP 上的可抢占节点)时更加健壮和高效。它确保在 spot 中断发生之前,移动 shuffle 和缓存数据,以便 Spark 应用程序可以在影响最小的情况下继续运行。在此功能之前,当发生定点清除时,随机文件会丢失,因此需要重新计算(通过重新运行可能非常长的任务)。此功能不需要设置外部 shuffle 服务(这需要昂贵的存储节点按需运行,并且与 Kubernetes 兼容)。这个用一张图更好描述。

Spark 的新功能的一个例子,它可以预测当场死亡并优雅地终止执行者,而不会丢失宝贵的数据!图片作者。

这个功能有什么作用?

  1. 将要离开的执行程序被列入黑名单 Spark 驱动程序不会在它上面安排新的任务。当前在其上运行的 Spark 任务不会被强制中断,但是如果它们失败(由于执行器死亡),这些任务将在另一个执行器上重试(与今天相同),并且它们的失败不会计入最大失败次数(新)。
  2. 执行程序中的随机文件和缓存数据会迁移到另一个执行程序中。如果没有其他执行程序(例如,我们正在删除那里唯一的执行程序),您可以配置一个对象存储(如 S3)作为后备存储。
  3. 一旦完成,执行者就死了,Spark 应用程序可以不受影响地继续运行!

这个什么时候生效?

  • 当您使用 spot/preemptable 节点时,云提供商(aws、gcp、azure)现在会提前 60-120 秒通知您。Spark 现在可以利用这个时间段来保存我们宝贵的洗牌文件了!当云提供商实例因其他原因关闭时,如 ec2 维护事件,同样的机制也适用。
  • 当一个 Kubernetes 节点被清空(例如为了维护)或者 Spark executor pod 被驱逐(例如被一个更高优先级的 pod 抢占)时。
  • 当执行程序作为动态分配的一部分被删除时,在缩减期间,因为执行程序空闲。在这种情况下,缓存和随机文件也将被保留。

如何打开它?

  • 配置标志。需要开启的 4 个主要 Spark 配置分别是Spark . dissolution . enabledSpark . storage . dissolution . rddblocks . enabledSpark . storage . dissolution . shuffle blocks . enabledSpark . storage . dissolution . enabled
    我建议直接参考源代码来查看其他可用的配置。
  • 云提供商警告节点即将离开(例如由于定点清除)的能力需要特定的集成。我们建议查看一下 AWS 、 GCP 和 Azure 的 NodeTerminationHandler 项目。如果你是数据力学的客户,请注意我们正在为你做这项工作。

Kubernetes 上 Spark 的新卷选项

从 Spark 2.4 开始,在 Kubernetes 上使用 Spark 时,可以挂载 3 种类型的卷:

  1. 一个 emptyDir :共享一个 pod 生命周期的初始空目录。这对于临时存储很有用。这可以由节点的磁盘、SSD 或网络存储提供支持。
  2. A hostpath :将一个目录从底层节点挂载到您的 pod。
  3. 静态预创建的 PersistentVolumeClaim 。这是 Kubernetes 对各种类型的持久存储的抽象,比如 AWS EBS 、 Azure Disk ,或者 GCP 的持久磁盘。PersistentVolumeClaim 必须由用户提前创建,并且其生命周期与 pod 无关。

Spark 3.1 支持两个新选项——NFS 和动态创建的 PersistentVolumeClaims。

NFS 是一个可以同时由许多 pod 共享的卷,并且可以预先填充数据。因此,这是一种跨 Spark 应用程序,或者跨给定 Spark 应用程序中的驱动程序和执行器交换数据、代码和配置的方式。Kubernetes 不运行 NFS 服务器,你可以自己运行,或者使用云服务(比如 AWS EFS ,GCP Filestore ,或者 Azure Files )。

一旦您创建了 NFS 共享,现在使用 Spark 3.1 只需使用 Spark 配置就可以很容易地将其安装到您的 Spark 应用程序中:

spark . kubernetes . driver . volumes . NFS . my share . mount . path =/shared
spark . kubernetes . driver . volumes . NFS . my share . mount . readonly = false
spark . kubernetes . driver . volumes . NFS . my share . options . server = NFS . example . com
spark . kubernetes . driver . volumes . NFS . my share . options . path =/storage/shared

NFS(网络文件系统)是在所有 Spark 应用程序之间共享数据的热门选择。它现在可以在 Kubernetes 上运行。图片作者。

第二个新选项是动态 PVC,这是一种更加用户友好的使用持久卷的方式。以前,您必须预先创建 PVC,然后装载它们。如果你使用动态分配,你不知道在你的应用程序执行过程中会产生多少执行者,所以这很难做到。您还必须自己清理未使用的持久卷,或者接受存储资源的浪费。

有了 Spark 3.1,一切都是动态和自动化的。当您提交 Spark 应用程序时(或者当您在动态分配期间请求新的执行器时),Kubernetes 中会动态创建 PersistentVolumeClaims,这将自动提供您所请求的存储类的新 PersistentVolumes(例如 AWS EBS 、 Azure Disk 或 GCP 的持久性磁盘)。删除 pod 后,关联的资源会自动销毁。

其他 Spark 3.1 特性:PySpark UX、阶段级调度、性能提升

除了 Kubernetes go GA 上的 Spark 之外,Spark 3.1 还带来了许多显著的功能。在这里我们将集中讨论几个主要的问题。

PySpark 开发者 UX 在 Spark 3.1 中获得了两大改进:

  • PySpark 文档已经接受了完全的重新设计,这使得它更加 pythonic 化和用户友好。看看吧!
  • 类型提示支持现在意味着您应该在 IDE 中免费获得代码完成和静态错误检测。

下面是 Apache Spark 3.1 中 PySpark 用户可以使用的 IDE 自动完成功能的示例。来源:https://github.com/zero323/pyspark-stubs

Spark History Server 可以在应用程序完成后呈现 Spark UI,它现在会显示您运行的结构化流查询的统计信息。

阶段级调度( SPARK-27495 )仅适用于启用动态分配时的 YARN 和 Kubernetes 部署。它允许您在代码中控制在一个阶段的粒度上请求的执行器资源的数量和类型。具体来说,您可以配置您的应用程序,在应用程序的第一阶段使用带有 CPU 的执行器(比如说,执行 ETL 和准备数据),然后在应用程序的第二阶段使用 GPU(比如说,训练 ML 模型)。

在性能方面,Spark 3.1 改进了混洗散列连接的性能,并在子表达式消除和 catalyst 优化器中添加了新规则。对于 PySpark 用户来说,内存中的列格式 Apache Arrow 版本 2.0.0 现在与 Spark 捆绑在一起(而不是 1.0.2),这应该会使您的应用程序更快,特别是如果您倾向于在 Spark 和 Pandas 数据帧之间转换数据。好消息是,您将免费从这些性能改进中受益,无需任何代码或配置更改。

结论

Spark 3.1 是 Apache Spark 的一个激动人心的版本,庆祝了社区多年来对 Spark-on-Kubernetes 集成的支持,标志着它的支持已经普遍可用并可以生产。这对于我们 Data Mechanics 来说并不意外,因为我们一直在帮助客户迁移到 Kubernetes,并在开发人员体验和性能/成本降低方面不断取得巨大成果。

除了即将正式上市的 Kubernetes 上的 Spark 之外,优雅的退役功能还实现了将计算资源从存储中完全分离出来的愿景,并通过 Spark 使经济高效的 spot 实例的使用更加稳定。我们对该功能的初步测试非常有希望——我们将很快发布一个我们的大客户使用该功能的故事,敬请关注!

本文原 发表于数据力学博客

Apache Spark 3.2 版本—Spark-on-Kubernetes 的新特性

原文:https://towardsdatascience.com/apache-spark-3-2-release-whats-new-for-spark-on-kubernetes-cb234e680fc0?source=collection_archive---------16-----------------------

主要功能解释:PersistentVolumeClaim 重用(k8s 专用)、考拉、Hadoop 3.3、RocksDB 等等!

Apache Spark 3.2 为每个人带来了性能和稳定性的改进,以及一个令人兴奋的新特性,使 Spark-on-Kubernetes 在 spot 节点上表现出色!来源: Unsplash 。

Apache Spark 3.2 于 2021 年 10 月发布(参见发布说明),现在面向数据机制客户,以及任何希望在 Kubernetes 上运行 Spark(或简单地在 Docker 上运行 Spark)的人,因为我们更新了 Spark 的优化 Docker 映像的 DockerHub 库。

在本文中,我们将带您浏览 Apache Spark 的新特性,这些新特性让我们兴奋不已。我们将首先看一下有利于整个 Spark 社区的总体改进,然后关注一个针对 Spark-on-Kubernetes 用户的激动人心的新开发。

主要特性—总体改进

Hadoop 3.3.1 —性能改进

Spark 3.2 现在默认使用 Hadoop 3.3.1 (而不是之前的 Hadoop 3.2.0)。你可能认为这不适用于你(特别是如果你在 Kubernetes 上运行 Spark),但实际上即使你不在 Hadoop 基础设施上运行,Spark 也使用 Hadoop 库。特别是,Spark 在从对象存储中读取数据时会使用 Hadoop 库(比如 S3 ),所以这一点非常重要。

Hadoop 3.3.1 版本为 S3A 连接器( HADOOP-17400 )带来了显著的性能提升。这些改进调优了 s3 清单的性能,并删除了许多对 S3 API 的不必要调用,因此降低了被抑制的风险,并且总体上提高了 Spark 从 S3 读取时的性能。

另一个值得注意的变化是“ Magic S3 提交器”现在更易于使用,甚至被推荐为默认设置,因为我们现在可以受益于 S3 自 2020 年 12 月以来在全球范围内支持强写后读一致性的事实。Spark 3.2 和 Hadoop 3.3.1 版本修复了一些错误,并显著提高了提交器的性能。

你现在可以通过打开单个 Spark 配置标志(而不是多个)来启用 magic committer,其中<桶>是你写入数据的 S3 桶:" Spark . Hadoop . fs . s3a . bucket .<桶>. committer . magic . enabled ":" true "。

‍If,你不知道什么是 S3 提交器,我们推荐你阅读这个文档,它有点过时,但是对 S3 提交问题做了很好的介绍。还要注意的是,Hadoop 维护者正在开发一个类似的提交器,为 Google Cloud 和 Azure 的对象存储进行了优化。

自适应查询执行—性能改进

Spark 开发团队一直在寻找提高 Spark SQL 查询优化器效率的方法。查询优化器负责选择适当的连接方法、任务执行顺序,并根据从底层数据中获得的各种统计信息决定连接顺序策略。

自适应查询执行是这些优化技术之一,最初发布于 Spark 3.0。在 Spark 3.2 中,默认情况下启用自适应查询执行(您不再需要配置标志来启用它),并且与其他查询优化技术(如动态分区修剪)兼容,使其更加强大。

AQE 允许查询优化器在某些阶段完成时收集数据,根据从查询执行中收集的新统计信息重新构建查询计划,并在应用程序执行过程中对查询计划应用这些更改。

自适应查询执行在 Spark SQL 查询运行时对其进行优化。

AQE 通过应用以下策略来优化查询执行:

  • 动态合并混洗分区
  • 动态切换连接策略(动态选择使用广播连接或混合散列连接,而不是较慢的排序-合并连接)
  • 动态优化偏斜连接

不同的配置标志让你调整这些优化的行为,更多细节请参考文档。这些配置标志是在数据力学平台上为您自动设置的。

数据源 V2 聚合下推-性能改进

当 Spark 从任何存储中读取数据时(对象存储中的 parquet 文件、数据仓库、HDFS、SQL 数据库……),它使用一个实现特定 API 的库。随着 2018 年 Spark 2.3 的发布,发布了新的 API(名为 DataSource V2),主要的常用数据连接器(阅读器和写入器)都移植到了它上面。基本 API (V1)只允许 Spark 以分布式方式读/写数据。新的 API (V2)在数据源层实现了很多优化,比如通过在数据源“向下”推送过滤器来减少数据读取量。

有了 Spark 3.2,您现在可以从包含聚合过滤器或选择聚合列的查询上的谓词下推中受益。举一个具体而简单的例子,让 Spark 计算一个拼花文件中的行数现在会快得多,因为 Spark 可以直接从文件的元数据中读取它,而不是扫描它(参见 SPARK-35511 )。

支持的聚合函数包括计数、求和、最小值、最大值、平均值。通过聚合下推,Spark 可以将聚合谓词应用于数据源,从而减少通过网络读取和混合的文件数量。如果查询中包含多个聚合,并且数据源支持这些聚合,Spark 将下推所有聚合。

请注意,如果您选择一个聚合和一个维度列,该聚合将不会被下推到数据源。还要注意的是,当使用 parquet 或 Delta 作为数据源时,这种优化效果最明显。

考拉——熊猫用户的 PySpark 改进

Koalas 是流行的 Pandas 库的 Spark 实现,作为 PySpark 的首选转换库,它越来越受欢迎。在 Spark 3.2 中,考拉现在将默认与 Spark 捆绑在一起,它不需要作为附加库安装。我们的目标是让 Spark 更容易被习惯在单台机器上运行纯 python 代码的人访问,事实上,现在使用 PySpark 运行相同的代码比使用纯 Pandas 要快得多,即使您不运行 Spark 的分布式功能(这要感谢 Spark SQL 中内置的许多优化)。

Api 变更

以前,如果您想要在熊猫、考拉或 Spark 功能之间切换,您需要应用转换函数来获得适当的 DataFrame 对象,从而允许您利用特定的转换。随着 Spark 3.2 的发布,Pandas 现在可以在 Spark 上本地运行,并且包含在 PySpark API 中。

星火 3.2 之前进口熊猫。图片作者。

上面的单元格描述了使用 Pandas 函数、创建 Pandas 数据帧和转换为 Spark 数据帧的工作流程。这种操作效率特别低,因为在开始 Spark 操作之前,您会将整个“test.csv”收集到您的驱动程序中,从而放弃了 Spark 的一个关键优势,即分布式处理。此外,代码本身变得乏味,因为我们需要在库之间切换,以便利用我们的应用程序所需的转换和功能。

从 Spark 3.2+导入熊猫。图片作者。

使用 Spark 3.2,您可以直接从 PySpark API 导入 read_csv 函数,并接收 Spark 数据帧,这样就不需要在两个库之间来回转换了。此外,read_csv 函数现在是使用 Spark 作为后端实现的,这意味着在内存中读取和处理 csv 时,您将受益于并行化。这种实现不仅允许开发人员生成更干净的 PySpark 应用程序,还可以消除混淆,即哪些操作只在 Spark 驱动程序上执行,哪些操作分布在执行器上。

形象化

标准的 Python Pandas 实现打包了 matplotlib 作为默认的绘图库。在 3.2 版本中,PySpark Pandas 默认使用。Plotly 提供了许多增强功能,例如对交互式放大和缩小图形的本机支持,以及使用 Spark 重新计算绘图视图。如果选择切换回 matplotlib,可以在 Spark 配置中指定 PySpark 绘图库。

RocksDB —火花流改进

标准的 Spark 流配置通过在内存中存储持久值来支持有状态操作,如流聚合或连接。对于一些具有高基数的流工作负载,在内存中存储状态可能不够,导致应用程序将数据溢出到磁盘并影响性能。

Spark 现在支持 RocksDB ,这是一个用于高性能状态管理的持久性键值存储。与传统的内存解决方案相比,RocksDB 在查找性能和延迟方面都有显著提升。要使用 RocksDB 运行 Spark 应用程序,请添加以下配置设置:

" spark . SQL . streaming . statestore . provider class ":" org . Apache . spark . SQL . execution . streaming . state . rocksdbstatestoreprovider "

Kubernetes 上的火花改进

Spark UI 中的执行器失败原因

新的执行程序失败原因列位于右侧。图片作者。

Spark UI 现在在它的执行者页面上增加了一个新的列,给你一个 Spark 执行者死亡的原因。这一改进适用于所有 Spark 用户,而不仅仅是 Spark-on-Kubernetes。对于 Spark-on-Kubernetes 用户来说,一个阻止错误代码传播的 bug 已经修复,并且提供了一些额外的见解,例如将 Docker 退出代码翻译成人类可以理解的东西。

火花 PMC·霍尔登·卡劳添加了新的有趣的错误信息(来源: Github

执行程序重启时 Kubernetes 持久性卷声明重用

在 3.1 版本中,Spark 引入了在 Kubernetes 工作负载上动态生成、装载和删除持久卷声明 (PVCs)的能力,这些工作负载基本上是装载到 Spark pods 中的卷,由 Kubernetes 管理,并由物理云卷(如 AWS 上的 EBS、GCP 上的持久磁盘或 Azure 磁盘)提供支持。如果你想在我们的平台上做这件事,请看我们的文档。

如果由于定点清除或失败(例如 JVM 耗尽内存)而丢失了一个执行器,那么持久化卷将在执行器 pod 终止的同时丢失,从而迫使 Spark 应用程序重新计算丢失的工作(洗牌文件)。

Spark 3.2 增加了 PVC 重用和洗牌恢复来处理这种确切的场景( SPARK-35593 )。如果执行程序或节点在运行时丢失,Spark 将保留最初与丢失的执行程序相关联的持久卷声明,从 Kubernetes 请求一个新的 pod,并将现有的卷附加到它上面,以便宝贵的 shuffle 文件不会丢失,Spark 可以恢复已经完成的工作,而不是重新计算它。

演示 Exec-2 如何恢复存储在与 Exec-1 关联的 PVC 中的随机文件的动画 GIF。图片作者。

该特性将极大地提高 Spark-on-Kubernetes 在处理偶发执行器故障时的健壮性和效率,并有助于使用现场/可抢占节点,与传统的按需节点相比,可节省 60%以上的成本。这是 Spark-on-k8s 用户的一大胜利!

要启用动态 PVC 重用,请将以下设置添加到应用程序的 Spark 配置中。请注意,前两个标志是 Spark 3.2 中的新标志,其他标志来自 Spark 3.1 中的动态 PVC 特性

结论

Apache Spark 3.2 为 Spark 在所有用例中带来了显著的性能提升——从 Spark SQL 用户(AQE),到熊猫开发者(考拉),以及流用户(RocksDB)。这也是 Spark-on-Kubernetes 项目(从 Spark 3.1 开始正式发布)增加稳定性和成熟度的另一个版本,因为 PVC 重用以一种健壮的方式解决了在应用程序运行时丢失 Spark 执行器的问题。

当您在我们的平台上将工作负载升级到 Spark 3.2 时,我们期待展示这些影响。如果您不是客户,但对迁移到 Spark-on-Kubernetes 感兴趣,请向我们预订演示!

Apache Spark Monitoring:如何使用 Spark API 和开源库来获得应用程序更好的数据可观察性

原文:https://towardsdatascience.com/apache-spark-monitoring-how-to-use-spark-api-open-source-libraries-to-get-better-data-9f48074a4fc9?source=collection_archive---------27-----------------------

Arie Wubben 在 Unsplash 上拍摄的照片

了解如何使用监听器 API 和数据质量库为 Apache Spark 获得不同级别的数据可观察性。

Spark 对于现代数据堆栈至关重要。因此,对于您的 Spark 环境来说,拥有正确的可观察性水平是极其重要的。监控 Spark 有很多选项,包括为 Spark 和 Spark SQL 指标提供预配置仪表板的 SaaS 程序。如果这还不够呢?

典型的 Spark 应用程序设置,无论是自托管还是托管解决方案,都包括一些用于集群健康监控的操作仪表板。尽管这些控制面板非常有用,但它们只为我们带来了基础架构概述,而不是与数据相关的实际指标。是的,当 CPU 使用率增加或集群内存不足时,我们可以假设应用程序可能有问题,但当源改变了模式或来自另一个部门的数据被破坏时,这没有帮助。工程师面临的大多数问题都是由数据引起的,而不是由底层基础设施引起的,因此他们不得不花费大量时间重现问题或者像侦探一样摆弄文件和桶。这正是实际应用程序监控可以提供帮助的地方。

每种情况都需要不同级别的可见性,数据工程师需要具备比执行指标更深入的能力。否则,您可能会花费大量时间在 Spark 中调试数据质量问题。

在本指南中,您将了解如何为 Spark 获得高级和低级的数据可观察性。对于高层,您将使用 Spark 的内部系统,如监听器 API 和查询执行监听器。对于底层,您将学习如何使用库来跟踪数据质量度量。

学会这两种方法后,你可以选择哪一种最适合你要解决的问题。

监视 Apache Spark 的低级方法

火花监听器

这是一种非常古老且可靠的获取指标的方法。实际上, Spark UI 利用完全相同的机制来可视化指标。Spark 监听器 API 允许开发人员跟踪 Spark 在应用程序执行期间发出的事件。这些事件通常是应用程序开始/结束、作业开始/结束、阶段开始/结束等。你可以在 Spark JavaDoc 中找到完整的列表。它很容易配置,也很容易使用 Spark 监听器来获取指标。在执行每个操作之后,Spark 将调用 Spark Listener 并将一些元数据信息传递给它的方法。这将包括像执行时间,记录读/写,字节读/写和其他。

这种非常基本和低级的数据质量监控将检查记录的数量和大小。假设您有一些每天都在运行的作业,并对传入的数据集执行一些转换/分析。您可以编写一个侦听器来检查从输入中读取了多少记录,并将其与前一天的结果进行比较。当差异显著时,我们可以假设数据源可能有问题。

然而,这种方法需要编写内部监控解决方案。度量值应该存储在某个地方,应该配置警报机制。当应用程序代码将改变时,所有的度量键也将改变,人们应该正确地处理它。

然而,即使是一个简单的 Spark 监听器也可以对您的数据提供一些见解。

这里有一个这样的火花监听器的例子:

您可以通过几种方式将 Spark 监听器添加到您的应用程序中:

以编程方式添加它:

或者通过 spark-submit/spark cluster 驱动程序选项传递它:

Spark 查询执行监听器

这是另一种开箱即用的火花监测机制。查询执行监听器允许开发人员订阅查询完成事件,而不是关注非常低级的指标。它提供了关于执行的查询的更高层次的元数据,如逻辑和物理计划,以及执行度量。

您可以获得像查询读/写的记录这样的指标,但是这次是针对整个查询而不是特定的任务/作业/阶段进行聚合的。

还可以从计划中提取非常有用的信息,如数据位置和模式。您可以提取和存储模式以及数据帧维度,并将其与之前的运行进行比较,在出现问题时触发警报。

然而,从计划中提取数据可能很复杂,因为您被迫使用低级别的 Spark API。

此外,实施指标存储和警报机制的所有运营负担仍然存在。你从 Spark 中得到的只是元数据。开发人员有责任利用它。

下面是一个简单的查询执行监听器示例,它打印计划和度量:

查询执行侦听器可以通过编程方式或配置方式添加:

通过 spark-提交:

实现低级别的监控可能是非常繁重的工作,然而,“系统”监控方式有一个巨大的好处:它不会引入计算开销。因为元数据是由 Spark 内部发出和记录的,所以它不会对查询执行时间造成任何损失。

使用监听器进行监控可以让您完全避免接触应用程序代码!当您想要跟踪现有和遗留应用程序上的数据,但没有预算进行更改时,这将带来巨大的好处。只需编写一个侦听器,通过 spark 配置传递它,并获得您的数据的图片。

监视 Apache Spark 的高级方法

人工数据质量检查

通过手动验证,您可以大大增强对传入数据的信心。假设我们期望输入数据源中有一定数量的记录,并且该数量通常不应该低于 x。我们可以写一些非常简单的东西,比如:

这里的可能性是无限的。我们可以比较计数、非空值计数、推断模式等。

使用数据质量库

由于许多质量检查或多或少都是琐碎的,比如确保数据帧具有正确的形状和内容,社区为此类检查开发了方便的库。其中一个图书馆是 Deequ 。它为大多数情况提供了丰富的领域特定语言(DSL)。看看这个。此外,它还具有一些高级功能,如分析列的能力——计算最小值/最大值/平均值/百分位数、计算直方图、检测异常等等。

考虑 Deequ 文档中的以下示例:

你可以看到我们有一大堆的支票包装在一个漂亮的随时可用的 DSL 中。

更重要的是,Deequ 提供了存储检查结果和自动运行与以前运行的比较的能力。这可以通过利用度量库来完成。用户可以编写自己的实现,并将 Deequ 无缝集成到现有的监控基础设施中。

虽然高级别的应用程序质量检查比低级别的方法灵活得多,但是它们也有一个很大的缺点:性能损失。由于每个计算都会产生火花操作,因此在某些情况下,开销会非常大,尤其是在大型数据集上。每个“计数”和“位置”都可以导致全面扫描。Spark 内部将尽力优化执行计划,但您应该考虑这些影响,并确保数据分析不会损害您的性能。

结论

我们已经回顾了几种监测 Spark 应用程序数据质量的方法。低级方法利用 Spark 事件监听器 API,并提供对低级指标(如记录读/写、逻辑/物理计划)的访问,可用于构建趋势,确保数据管道产生正确的结果,并在不修改任何代码的情况下获得现有应用的概览。像手工检查数据或使用数据质量库这样的高级方法要方便得多,但是也有一些缺点,比如性能损失。

正如在任何实际情况中一样,根据您的应用程序类型,这两种方法总会有折衷和更好的方案。明智地使用它。

在 Databand ,我们利用这两种方式提供一套全面的选项来跟踪 Spark 应用。虽然在我们的核心中,我们使用 Spark 监听器来构建指标趋势和数据谱系,但我们也为 Deequ 提供了方便的指标存储,以及跟踪单个手动计算的指标的能力。

Apache Spark —多部分系列:Spark 架构

原文:https://towardsdatascience.com/apache-spark-multi-part-series-spark-architecture-461d81e24010?source=collection_archive---------4-----------------------

理解大数据

Spark 架构是最初学习 Spark 时最难掌握的元素之一。我认为其中一个主要原因是有大量的信息,但没有什么能让我们深入了解 Spark 生态系统的方方面面。这很可能是因为,它很复杂!有这么多奇妙的资源,但并不是所有的都是直观的或容易理解的。

我希望这个系列的这一部分能够帮助那些对这个主题知之甚少的人从基础开始理解 Spark Architecture 是如何构建的。我们还将研究如何为我们的 Spark 系统提供工作,以及系统如何以最有效的方式消耗和完成这些工作。

正如我所承诺的,这部分会更重一点。所以系好安全带,这将会很有趣!

物理火花等级:

为了理解 Spark 程序是如何工作的,我们需要理解 Spark 系统是如何一砖一瓦地构建起来的(参见我在那里所做的)。有许多不同的方法来建立一个 Spark 系统,但是对于这个系列的这一部分,我们将讨论一个最流行的方法来建立它。

一般来说,Spark 系统是由许多独立的机器组成的,它们为了一个共同的目标一起工作,这被称为集群分布式系统。要让这样的系统工作,我们需要一台机器来整体管理集群。该机器通常被标记为驱动节点

驱动节点

Spark 驱动程序用于协调整个 Spark 集群,这意味着它将管理分布在集群中的工作,以及在整个集群生命周期中哪些机器是可用的。

逐步驱动程序节点(由 Luke Thorp 创建)

  1. 驱动程序节点像任何其他机器一样,它有 CPU、内存、磁盘和缓存等硬件,但是,这些硬件组件用于托管 Spark 程序和管理更广泛的集群。驱动因素是用户之间的链接,以及完成提交给群集的任何工作所需的物理计算。
  2. 由于 Spark 是用 Scala 编写的,所以要记住集群中的任何机器都需要运行一个 JVM (Java 虚拟机),这样 Spark 才能与主机上的硬件一起工作。
  3. Spark 程序在这个 JVM 内部运行,用于创建 SparkContext,这是用户访问 Spark 集群的访问点。

驱动程序包含 DAG(有向无环图)调度器、任务调度器、后端调度器和块管理器。这些驱动程序组件负责将用户代码翻译成在集群上执行的 Spark 作业。隐藏在驱动程序节点中的是集群管理器,它负责获取 Spark 集群上的资源并将它们分配给 Spark 作业。这些资源以工作节点的形式出现。

工作节点

工作者节点形成集群的*【分布式】*部分。这些节点有各种形状和大小,加入集群时不一定总是相同的,它们的大小和性能可以不同。但是,当进一步调查性能瓶颈时,拥有性能相同的工作节点可能会有好处。所以,这是需要记住的事情。

Worker Node Step by Step(由 Luke Thorp 创建)

  1. 工作节点通常是独立的机器,其硬件与任何其他节点一样。例如 CPU、存储器、磁盘和高速缓存。
  2. 与驱动程序一样,为了让 Spark 在 worker 节点上运行,我们需要确保系统安装了兼容版本的 Java,以便代码能够以正确且有意义的方式进行解释。
  3. 当我们在工作节点上安装 Spark 时,我们的驱动节点将能够利用其集群管理功能来映射工作节点上可用的硬件。
    集群管理器将跟踪*“插槽”*的数量,这实际上是设备上可用内核的数量。这些插槽被分类为可用的计算卡盘,并且可以由驱动程序节点向它们提供要完成的任务。有一定数量的可用内存被分成两部分,存储内存和工作内存。默认比例是 50:50,但是可以在 Spark 配置中更改。
    每个工作人员还会附带一些磁盘。但是我知道你要说什么,Spark 工作在内存,不是磁盘!然而,Spark 仍然需要磁盘来分配 shuffle 分区(将在本文后面进一步讨论),它还需要空间来持久存储到磁盘和溢出到磁盘。
  4. 现在我们有了驱动节点和工作者节点是如何构造的细节,我们需要知道它们是如何交互和处理工作的。
    为此,我们将创建一个极简版本的图表来显示一个 worker 节点及其可用的工作插槽。

最小化物理结构

既然我们知道了驱动程序和工作程序是如何独立构造的,我们就可以研究它们在集群环境中是如何内在联系的。如上所述,我们将简化 worker 节点的视图,以便它只显示对下一节重要的元素。(他们还有内存和磁盘!)

查看驱动程序和工作节点,我们可以看到驱动程序节点是集群中节点之间通信的中心。

驱动节点和工作节点架构(由 Luke Thorp 创建)

工作节点能够相互通信和传递数据,但是对于工作和任务,驱动节点只负责向工作节点提供要完成的工作。

在上面的例子中,我们有一个包含一个驱动程序和三个工作节点的集群。每个节点都有四个可用的计算插槽,因此,该集群将能够同时完成十二项任务。值得一提的是,Sparks new photon engine 是一款 Spark 兼容的矢量查询引擎,旨在满足和利用最新的 CPU 架构。这支持更快的数据并行处理,甚至在任务执行期间读取数据时实时提高性能。

Spark 运行时架构

Spark 运行时架构正是它在 tin 上所说的,即代码运行时集群发生了什么。“代码正在运行”可能是错误的阶段。Spark 既有热切的评价,也有慵懒的评价。星火行动渴望;然而,转换天生懒惰。

如上所述,转换是懒惰的,这实质上意味着当我们对数据调用一些操作时,它不会立即执行。Spark 维护被调用操作的记录。这些操作可以包括连接和过滤。

动作是急切的,这意味着当一个动作被调用时,该行代码一运行,集群就开始计算预期的结果。当操作运行时,它们会产生一个非 RDD(弹性分布式数据集)组件。

转换类型:

有两种类型的转换是理解 Spark 如何以分布式方式管理数据和计算的关键,它们是。转换从彼此创建 rdd。

狭窄和宽阔的变换(由卢克·索普创造)

  • 由一个输入分区定义的窄转换将产生一个输出分区。这方面的一个例子是过滤器:因为我们可以有一个数据帧,我们可以将其过滤成一个更小的数据集,而不需要了解任何其他工作节点上保存的任何数据。
  • 大范围转换(混洗)是由工作节点需要通过网络传输(混洗)数据以完成所需任务这一事实定义的。这方面的一个例子是连接:因为我们可能需要从集群中收集数据,以完全正确地完成两个数据集的连接。

扩展的广泛转换(由卢克·索普创建)

宽转换可以采取多种形式,并且 n 个输入分区可能不总是产生 n 个输出分区。如上所述,连接被归类为宽转换。所以在上面的例子中,我们有两个 RDD,它们有不同数量的分区连接在一起形成一个新的 RDD。

运行时间

当用户通过他们喜欢的方法向驱动程序提交代码时,驱动程序隐式地将包含转换(过滤器、连接、分组、联合等)和动作(计数、写入等)的代码转换成未解析的逻辑计划。

在提交阶段,驱动程序参考逻辑计划目录以确保所有代码符合所需的参数,在这完成之后,未解决的逻辑计划被转换成逻辑计划。流程现在执行优化,比如计划转换和行动,这个优化器更好地被称为 catalyst 优化器。

Spark SQL 的核心是 Catalyst optimizer,它以一种新颖的方式利用高级编程语言功能(例如 Scala 的模式匹配和准引号)来构建可扩展的查询优化器。Catalyst 基于 Scala 中的函数式编程结构,其设计有两个主要目的:

-轻松将新的优化技术和特性添加到 Spark SQL 中

-使外部开发人员能够扩展优化器(例如,添加数据源特定规则、支持新数据类型等)。)

https://databricks.com/glossary/catalyst-optimizer

Apache Spark Catalyst 优化器:data bricks(【https://databricks.com/glossary/catalyst-optimizer】T2

在本系列的下一部分,我将深入研究 catalyst optimiser,所以不要太担心这一部分。

一旦优化完成,逻辑计划被转换成多个物理执行计划,这些计划被汇集到成本模型中,分析每个计划并应用成本值来运行每个执行计划。完成成本最低的计划将被选为最终产出。此选定的执行计划将包含具有多个阶段的作业。

物理执行计划(由卢克·索普创建)

阶段有小的物理任务,这些任务被打包发送到 Spark 集群。在分配这些任务之前,驱动程序与集群管理器进行对话,以协商资源。

集群任务分配(由 Luke Thorp 创建)

一旦完成这项工作并分配了资源,任务就被分配给有空闲时间的工作节点(执行者),驱动程序监控进度。这是以尽可能最有效的方式完成的,同时牢记当前可用的资源和集群结构。在上面的例子中很简单,我们有三个任务分配给三个工作节点。任务可以以上述方式分配,即每个工作节点一个任务,或者它们可以被发送到一个节点,并且由于 Spark 的并行特性而仍然异步运行。但是,当我们的任务多于可用的计算槽时,或者当我们的任务数与集群中的节点数相比不可整除时,复杂性就会增加。幸运的是,Spark 自己处理这个问题,并管理任务分配。

值得一提的是,尽管 Spark 可以高效地分配任务,但它并不总是能够解释数据,这意味着我们可能会遇到数据偏差,这会导致任务持续时间的巨大时间差。然而,这完全取决于底层数据湖的结构。如果它是使用 Delta Engine (Photon)构建的,我们可以利用查询执行的实时改进,以及鼓励更好的混排大小和最佳连接类型的改进。

Spark UI(用户界面)允许用户交互整个工作负载流程,这意味着用户可以深入每个部分。例如,有针对作业和阶段的选项卡。通过进入一个阶段,用户可以查看与特定任务相关的指标,这种方法可用于了解为什么特定作业可能比预期需要更长时间才能完成。

云与本地

我相信您已经猜到了,Spark 集群通常构建在云环境中,使用云服务提供商提供的流行服务。这是由于这些服务的可扩展性、可靠性和成本效益。然而,您可能没有意识到,如果您想使用 Spark 配置,可以在本地机器上运行 Spark。

到目前为止,最简单的修补方法是注册 Databricks 的社区版。这允许您创建一个小型但功能相当强大的单个工作节点集群,能够运行所有的数据块代码示例和笔记本。如果你还没有做,那就去做吧!有一些限制,群集在两个小时的空闲时间后会超时,但是免费计算就是免费计算!

最后

星火架构很难。理解物理元素以及核心运行时代码如何转化为正在转换的数据并在集群中移动需要时间。当我们完成这个系列时,我们将继续参考这一部分,以确保我们都将基本原理锁定在我们的灰质中。

在本系列中,我将探讨我们能够更深入 Spark 的方法,以便我们能够理解 JVM 结构的基本框架,以及优化器如何对我们未解决的逻辑计划起作用。

值得注意的是,在接下来的部分中,我们将构建一个 Spark 集群,使用一个 Raspberry Pi 作为驱动节点,使用多个 Raspberry Pi Zero 作为工作节点。我只需要等待项目被交付… Covid 交付时间很慢!

如果我错过了本节的任何内容,或者如果还有什么不清楚的地方,请让我知道,这样我就可以改进这个系列,更不用说我的 Spark 知识了!你可以在 LinkedIn 上找到我:

系列部分:

简介

  1. 什么是阿帕奇 Spark
  2. 星火建筑

Windows 上的 Apache Spark:Docker 方法

原文:https://towardsdatascience.com/apache-spark-on-windows-a-docker-approach-4dd05d8a7147?source=collection_archive---------5-----------------------

如何用 Docker for Windows 以最少的工作量建立一个 Apache Spark 开发环境

卡斯帕·卡米尔·鲁宾在 Unsplash 上的照片

最近,我被分配到一个项目中,在这个项目中,整个客户数据库都在 Apache Spark / Hadoop 中。作为我所有项目的标准,我首先在公司笔记本电脑上准备开发环境,这是 Windows 作为标准操作系统提供的。正如许多人已经知道的,在 Windows 笔记本电脑上准备开发环境有时会很痛苦,如果笔记本电脑是公司的,那就更痛苦了(由于系统管理员、公司 VPN 等施加的限制)。).

为 Apache Spark / Hadoop 创建开发环境也是如此。在 Windows 上安装 Spark 极其复杂。需要安装几个依赖项(Java SDK,Python,Winutils,Log4j),需要配置服务,需要正确设置环境变量。鉴于此,我决定将 Docker 作为我所有开发环境的首选。

现在,Docker 就是我的“一环 / 一工具”(参考《魔戒》):

“在魔多的土地(窗户)阴影所在的地方。一枚戒指统治他们所有人,一枚戒指寻找他们,一枚戒指带来他们所有人,并在黑暗中束缚他们;在魔多的阴影之地"(托尔金

夏尔——照片由格雷戈&路易斯·努内斯在 Unsplash 上拍摄

如果 Docker 不是你的选择,有几篇文章可以解释这个问题

  • [在 Windows 10 上安装 Apache PySpark](http://Installing Apache PySpark on Windows 10)
  • Windows 上的 Apache Spark 安装
  • Windows 上的 PySpark 入门

为什么是 Docker?

  1. Windows 上不需要安装任何库或应用,只需要 Docker。每周安装软件和库时,无需请求技术支持人员的许可。(他们会爱你的,相信我)
  2. Windows 将始终以最大潜力运行(不会有无数服务在登录时启动)
  3. 拥有不同的项目环境,包括软件版本。例如:一个项目可以使用 Apache Spark 2 和 Scala,另一个项目可以使用 Apache Spark 3 和 pyspark,不会有任何冲突。
  4. 社区做出来的现成形象有几个(postgres,spark,jupyters 等。),使得开发设置更快。

这些只是 Docker 的一些优势,还有其他的,你可以在 Docker 官方页面 上了解更多。

说了这么多,让我们言归正传,设置我们的 Apache Spark 环境。

为 Windows 安装 Docker

你可以按照 开始指南 下载 Docker for Windows 并按照说明在你的机器上安装 Docker。如果你的 Windows 是家庭版,你可以按照 的说明在 Windows 上安装 Docker 桌面家庭版。

当安装完成后,你可以重新启动你的机器(记得保存这篇文章在收藏夹,以备份从重启)。

如果您在此时或稍后运行任何错误,请查看微软故障排除指南https://docs.microsoft.com/en-us/visualstudio/containers/troubleshooting-docker-errors?view=vs-2019

你可以从开始菜单启动 Docker,过一会儿你会在系统托盘上看到这个图标:

鲸鱼码头图标

你可以右击图标,选择仪表盘**。在仪表板上,您可以点击配置按钮(右上方的发动机图标)。您将看到以下屏幕:**

Docker 仪表板(图片由作者提供)

我喜欢做的一件事是取消选择选项:

  • 登录后启动 docker 桌面。

这样 docker 就不会从 windows 启动,我可以只在需要的时候通过开始菜单启动它。但是这是个人的选择。

检查对接器安装

首先,我们需要确保我们的 docker 安装工作正常。打开一个 Powershell(或 WSL 终端),我强烈推荐令人惊叹的 Windows 终端 ,这是一个 Windows ( 类 Unix)终端,它有很多帮助我们开发人员的功能(标签、自动完成、主题和其他很酷的功能),并键入以下内容:

~$ docker run hello-world 

如果你看到这样的东西:

Docker hello-world(图片由作者提供)

你的 docker 安装是 ok

Jupyter 和 Apache Spark

正如我前面所说,docker 最酷的特性之一依赖于社区图片。几乎所有的需求都有许多预制的图像可供下载,只需很少或不需要配置就可以使用。花点时间探索一下 Docker Hub ,自己看吧。

Jupyter 开发人员一直在做一项惊人的工作,积极地为数据科学家和研究人员维护一些图像,项目页面可以在这里找到https://jupyter-docker-stacks.readthedocs.io/en/latest/index.html。一些图像是:****

  1. jupyter/r-notebook 包括来自 R 生态系统的流行软件包。
  2. jupyter/scipy-notebook 包括来自科学 Python 生态系统的流行包。
  3. jupyter/tensor flow-notebook包括流行的 Python 深度学习库。
  4. jupyter/pyspark-notebook 包含对 Apache Spark 的 Python 支持。
  5. jupyter/all-spark-notebook 包括 Python、R 和 Scala 对 Apache Spark 的支持。

和许多其他人。

对于我们的 Apache Spark 环境,我们选择了jupyter/pyspark-notebook,因为我们不需要 R 和 Scala 支持。

要创建新容器,您可以转到终端并键入以下内容:

****~$ docker run -p 8888:8888 -e JUPYTER_ENABLE_LAB=yes --name pyspark jupyter/pyspark-notebook****

如果本地主机上还没有jupyter/py spark-notebook映像,这个命令会从 Docker Hub 中提取它。

然后,它启动一个运行 Jupyter 笔记本服务器的 name= pyspark 容器,并在主机端口 8888 上公开服务器。

您可以指示启动脚本在启动笔记本服务器之前定制容器环境。您可以通过向 docker run 命令传递参数(-e 标志)来实现这一点。所有可用变量的列表可在 docker-stacks docs 中找到。

服务器日志出现在终端中,并包含笔记本服务器的 URL。您可以导航到该 URL,创建一个新的 python 笔记本并粘贴以下代码:

瞧啊。我们用最少的努力创造了我们的 Apache Spark 环境。您可以打开一个终端,使用 condapip 安装软件包,并按照您的意愿管理您的软件包和依赖项。完成后,您可以按下 ctrl+C 并停止容器。

数据持久性

如果你想启动你的容器并保存你的数据,你不能再次运行" docker run "命令,这将创建一个新的默认容器,那么我们需要做什么呢?

您可以在终端中键入:

****~$ docker ps -a****

Docker 容器列表(图片由作者提供)

这将列出所有可用的容器,要启动之前创建的容器,请键入:

****~$ docker start -a pyspark****

其中 -a 是一个标志,告诉 docker 将控制台输出绑定到终端,pyspark 是容器的名称。要了解更多关于 docker start 选项的信息,您可以访问Docker docs

结论

在本文中,我们可以看到 docker 如何加快开发生命周期,并帮助我们减轻使用 Windows 作为主要开发操作系统的一些缺点。微软在 WSL、docker 和其他开发人员和工程师工具方面做得很好,甚至通过 Docker 和 WSL 支持 GPU 处理。未来大有可为。

感谢您阅读这篇文章,希望这篇小指南能够帮助您,并给任何决定使用 Docker for Windows 作为您的“ One ring ”的人提供宝贵的见解。

请随时在评论区提问或提供反馈。

Apache Spark 性能提升

原文:https://towardsdatascience.com/apache-spark-performance-boosting-e072a3ec1179?source=collection_archive---------0-----------------------

关于 Pyspark 性能技巧的综合指南

Apache Spark 是一个通用的分布式数据处理平台,专门用于大数据应用。它成为处理大数据的事实标准。根据其分布式和内存中的工作原理,默认情况下它应该执行得很快。然而,在现实生活中并非总是如此。这篇文章是我上一篇文章的后续,我的上一篇文章是关于设置配置参数来优化 Spark 中的内存和 CPU 分配。在这里,我将提到在 Pyspark 中开发时一些有用的编码实现,以提高工作持续时间、内存和 CPU 使用率方面的性能。

照片由在 Unsplash 上拍摄的 Caspi

1 —通过广播加入

连接两个表是 Spark 中的主要事务之一。它主要需要洗牌,由于节点之间的数据移动,洗牌的成本很高。如果其中一个表足够小,则可能不需要任何洗牌操作。通过将小表广播到集群中的每个节点,可以简单地避免混乱。

假设您正在处理一个力场数据集,并且有一个名为 df_work_order 的数据框,其中包含力场团队处理的工作指令。此外,您还有另一个数据框,其中包含现场工作队的城市信息。虽然 df_work_order 中有超过 100M 行和许多列,但是 df_city 数据帧中大约有 100 条记录。要将城市信息添加到 df_work_order 数据帧中,广播小表就可以了。

df_work_order = df_work_order.join(broadcast(df_city), on=[‘TEAM_NO’], how=’inner’)

广播表的最大大小为 8GB 。Spark 还在内部维护了一个表大小的阈值,以自动应用广播连接。可以使用spark . SQL . autobroadcastjointhreshold配置阈值,默认为 10MB

2 —用窗口替换联接&聚合

常见的模式是对特定的列执行聚合,并将结果作为新的特性/列保留在原始表中。正如所料,该操作由一个聚合和一个连接组成。作为一个更优化的选项,可以使用 窗口 类来执行任务。我认为这是一种常见的模式,值得一提。两种方法的简单基准和 DAG(有向无环图)表示可以在这里找到。

# first approachdf_agg = df.groupBy('city', 'team').agg(F.mean('job').alias('job_mean'))df = df.join(df_agg, on=['city', 'team'], how='inner')# second approachfrom pyspark.sql.window import Windowwindow_spec = Window.partitionBy(df['city'], df['team'])
df = df.withColumn('job_mean', F.mean(col('job')).over(window_spec))

3 —最小化洗牌

Spark 操作符通常是流水线式的,在并行进程中执行。然而,一次洗牌打破了这一管道。它们是物化点的种类,并在管道中触发一个新的阶段。在每个阶段结束时,所有中间结果都被具体化,并被下一个阶段使用。在每个阶段中,任务以并行方式运行。

原则上,混洗是数据在网络上的物理移动,并被写入磁盘,导致网络、磁盘 I/O 和数据序列化,从而使混洗成为一项成本高昂的操作。换句话说,它是有原因的数据重新分配。在 Spark 中,这些原因是像加入分组、减少、重新分配不同这样的变换。这些是非常常见的转换。因此,对于 Spark 应用程序来说,洗牌几乎是不可避免的。然而,减少洗牌是我们的责任。当先前的转换已经根据相同的分割器对数据进行了分区时,Spark 知道如何避免洗牌。

为了减少网络 I/O 在 shuffle 的情况下,可以创建具有更少机器并且每个机器具有更大资源的集群。然而,这完全是一个设计决策,不应该只考虑最小化洗牌。

  • 从洗牌点加入

正如我之前提到的,join 是需要 shuffle 的常见操作之一。因为这是一个非常常见的转换,而且 join 中的洗牌可能是可以避免的,所以我想在一个单独的部分中讨论它。Spark 为 joins 提供了三种不同的算法—SortMergeJoinShuffleHashJoinBroadcastHashJoin 。从 2.3 版开始, SortMergeJoin 是默认的连接算法。使用 *BroadcastHashJoin,*可以获得最佳性能,但是,它对数据帧的大小有非常严格的限制。

洗牌或许可以避免,但当然要有所取舍。大多数情况下,通过对同样需要洗牌的数据应用其他转换,可以消除连接过程中的洗牌。重点是你额外创造了多少洗牌,作为回报,你将阻止多少洗牌。此外,每次洗牌的数据量是另一个应该考虑的重要因素——一次大洗牌还是两次小洗牌?所有这些问题的答案都不是直截了当的,如果是,这将是 Spark 的默认行为。这实际上取决于您正在处理的数据。

根据经验,如果在连接中,第一个表的每个分区最多被第二个表的一个分区使用,就没有必要进行洗牌。但是,如果第一个表的每个分区可能被第二个表的多个分区在连接中使用,那么就需要进行洗牌。以这种方式,我们可以在实现连接之前,通过对相同键值的两个表进行重新分区或分桶来避免洗牌。请记住,这些操作也需要洗牌。

加入 Spark 期间洗牌

不避免混洗但减轻混洗中的数据量的典型例子可以是一个大数据帧和一个中等数据帧的连接。如果一个中等大小的数据帧不够小而不能广播,但是它的密钥集足够小,我们可以广播中等大小数据帧的密钥集来过滤大数据帧。通过这种方式,如果我们能够从大规模数据中过滤出大量数据,我们可能会实现大幅减少数据量。

list_to_broadcast = df_medium.select('id').rdd.flatMap(lambda x: x).collect()
df_reduced = df_large.filter(df_large['id'].isin(list_to_broadcast))
df_join = df_reduced.join(df_medium, on=['id'], how='inner')
  • 铲斗移动

分桶是另一种数据组织技术,它用相同的桶值对数据进行分组。**它类似于分区,但是分区为每个分区创建一个目录,而分桶通过桶值上的散列将数据分布在固定数量的桶上。**关于存储的信息存储在 metastore 中。它可以在有或没有分区的情况下使用。一个重要的要点是分区应该只用于值数量有限的列;当唯一值的数量很大时,bucketing 也能很好地工作。通常在聚合和连接中用作键的列是分桶的合适候选列。

通过在混洗所需的操作之前对数据帧中的方便列应用桶化,我们可以避免多次可能的昂贵混洗。分桶通过在执行排序-合并连接之前对数据进行排序和混排来提高性能。在连接中,表的两边有相同数量的桶是很重要的。

要使用它,需要指定桶的数量和键列。不用说,我们应该对数据有深入的了解,以决定正确的桶数。一般来说,通过连接组,不同的变换受益于

df = df.bucketBy(32, ‘key’).sortBy(‘value’)

任何情况下多洗牌都是好的?

可能会出现两种不同的情况。第一个是关于通过应用额外的洗牌来增加应用的并行度。如果应用程序由于低水平的并行性而无法利用集群中的所有内核,则可以应用重新分区来增加分区数量。这样,通过额外的洗牌,应用程序的整体性能可能会更好。

其次,当在大量分区上聚合时,在合并所有结果的驱动程序中,计算会很快成为单个线程的瓶颈。为了减轻驱动程序的负载,可以执行一轮额外的分布式聚合,通过聚合操作将数据集划分为更少的分区。在将结果发送到驱动程序进行最后一轮聚合之前,每个分区中的值会并行合并。这样,驱动程序中的计算负荷将会减轻。

4 —正确缓存

仅仅因为您可以在内存中缓存数据帧,您就不应该本能地这样做。请记住,执行内存和存储内存共享一个统一的区域。越多不必要的缓存,就越有可能将溢出到磁盘上,从而影响性能。这样,重新计算可能比增加内存压力所付出的代价更快。Spark 中有几个存储级别,可能会根据序列化、内存和数据大小因素进行相应的设置。

如果一个数据帧将在后面的步骤中反复使用,那么在开始时缓存它以避免重复的转换负载将是合理的。这是使用缓存的理想情况。

我经常观察到的一个误用缓存的情况是在从 Cassandra 或 Parquet 这样的数据源读取数据后立即缓存数据帧。在这种情况下,整个数据被缓存,而不检查所有数据是否相关。例如,在从 parquet 读取的情况下,Spark 将只读取元数据来获取计数,因此它不需要扫描整个数据集。对于过滤查询,它将使用列修剪,只扫描相关的列。另一方面,当从缓存中读取数据时,Spark 将读取整个数据集。

需要注意的是,如果您在数据帧上应用哪怕是一个小的事务,比如添加一个带有列的新列*,它将不再存储在缓存中。您可以使用测向存储级别检查数据帧的状态*

5 —打破沿袭—检查点

检查点 截断执行计划,将检查点数据帧保存到磁盘上的临时位置,并重新加载回来,这在除 Spark 之外的任何地方都是多余的。然而,在 Spark 中,它作为一个性能提升因素出现。关键在于,每次在数据框上应用变换或执行查询时,查询计划都会增长。Spark 保存了在数据帧上运行 explain 命令时可以看到的应用于数据帧的所有变换历史。当查询计划开始变大时,性能会急剧下降,从而产生瓶颈。

以这种方式,检查点有助于刷新查询计划和具体化数据。它非常适合包含迭代算法和扩展新数据框架以执行不同类型分析的场景。更确切地说,在对数据帧进行检查点操作后,您不需要重新计算之前应用于数据帧的所有变换,它会永远存储在磁盘上。注意,即使在 sparkContext 被销毁之后,Spark 也不会清理检查点数据,清理工作需要由应用程序来管理。通过检查数据帧的状态来调试数据流水线也是检查点的一个很好的特性。

为了提高性能,缓存也是一种类似目的的替代方法。与检查点相比,它显然需要更多的内存。在缓存检查点之间有一个很好的比较,以及什么时候更喜欢它们中的一个。这里可以看一下。

还有一个意见这里,数据流水线中检查点放在哪里。在创建 Spark 会话时,可以定义存储数据的位置。

# query plan without checkpointdf = df.filter(df['city'] == 'Ankara')
df = df.join(df1, on = ['job_id'], how='inner')
df.explain()# query plan with checkpointdf = df.filter(df['city'] == 'Ankara').checkpoint()
df = df.join(df1, on = ['job_id'], how=’inner’)
df.explain()

6 —避免使用 UDF

乍一看,用户定义函数(UDF)是以函数方式解决问题的非常有用的材料,它们确实如此。然而,Pyspark 的成本非常高。它们一次操作一行,因此序列化和调用开销很大。换句话说,它们使数据在 executor JVM 和 Python 解释器之间移动,导致了巨大的序列化成本。此外,在调用一个 Python UDF 之后,Spark 会忘记之前数据是如何分布的。因此,与 Java 或 Scala 中的 UDF 实现相比,在 Pyspark 中使用UDF不可避免地会降低性能。

从这个意义上来说,在 Pyspark 中开发时,避免不必要的使用 UDF 是一个很好的实践。内置的 Spark SQL 函数可以满足大部分需求。在 Pyspark 中使用UDF之前,重新思考是很重要的。如果你仍然要使用UDF,考虑使用pandasUDF构建在 Apache Arrow 之上。它承诺完全用 Python 定义低开销、高性能的UDF的能力,从 2.3 版本开始就支持了。作为缓解由UDF引起的性能瓶颈的另一种选择,用 Java 或 Scala 实现的UDF也可以从 PySpark 中调用。

为了更清楚UDF的不必要用法,看一下下面的例子,用UDF计算 z-score 没有任何意义。

# Unnecessary usage of UDFs
z_score_udf = F.udf(lambda x, m, s: (x — m) / s, DoubleType())
 df = df.withColumn('z_score',z_score_udf('completed_job',
 'mean_completed_job', 'std_completed_job'))# A better approach
df = df.withColumn('z_score',
 F.round(((F.col('completed_job') — F.col('mean_completed_job')) /
 F.col('std_completed_job')),2))

7 —处理倾斜数据— 加盐&重新分配

整个阶段的工作持续时间直接取决于任务的最长运行时间。如果你在 Spark 上花了足够多的时间,你很可能会遇到这样一种情况:最后一个任务需要几分钟,而这个阶段的其余任务比如说 199 个任务在几毫秒内执行。它是数据沿分区分布不均匀的结果,即数据偏斜问题。这个问题可能发生在火花应用的中间阶段。此外,如果数据高度倾斜,甚至可能导致数据从内存溢出到磁盘。为了观察数据在分区之间的分布,可以使用 glom 函数。此外,借助 Spark UI 的执行器页面中筛选出的任务执行时间和任务处理的数据量信息,也可以检测出数据的不均匀分布。

*partition_number = df.rdd.getNumPartitions()
data_distribution = df.rdd.glom().map(len).collect()*

Spark 3.0 版本有一个很好的特性 自适应查询执行 ,它可以自动平衡分区间的偏斜。除此之外,还有两个独立的解决方法来解决分区间数据分布的偏斜加盐重新分区

  • 腌制

加盐 一个数据集基本上就是给数据添加随机化,帮助它更均匀的分布。额外的处理成本是在分区间均匀分布数据的回报,因此性能会提高。在聚合和联接中,具有相同键的所有记录都位于同一个分区中。因此,如果其中一个键比其他键有更多的记录,那么该键的分区就有更多的记录需要处理。 S alting 技术仅应用于倾斜的键,从这个意义上说,随机值被添加到键中。然后,获得*<key 1+random _ salting _ value>*,如果是 join 操作,则将这个创建的新键值与另一个表中复制的对应键值进行匹配。

为了澄清这一点,请看下面的例子,其中键列是 join 中的城市信息,而键列的分布在表中是高度倾斜的。为了均匀分布数据,我们将从 1 到 5 的随机值附加到较大的连接表的键值的末尾,并通过从 1 到 5 展开一个数组在较小的表中组成一个新列。

Spark 中的 Salting 示例

# Adding random values to one side of the join
df_big = df_big.withColumn('city', F.concat(df['city'], F.lit('_'), F.lit(F.floor(F.rand(seed=17) * 5) + 1)))# Exploding corresponding values in other table to match the new values of initial table
df_medium = df_medium.withColumn('city_exploded', F.explode(F.array([F.lit(i) for i in range(1,6)])))
df_medium = df_medium.withColumn('city_exploded', F.concat(df_medium['city'], F.lit('_'), df_medium['city_exploded'])). \
            drop('city').withColumnRenamed('city_exploded', 'city')# joining
df_join = df_big.join(df_medium, on=['city'], how='inner')
  • 重新分配

重新分区执行完全洗牌,创建新分区,并提高应用程序中的并行级别。更多的分区将有助于处理数据偏斜问题,其额外成本是如上所述的全部数据的混洗。然而,向查询计划添加一个 shuffle 可能会消除另外两个 shuffle,从而加快运行速度。重新分区也可能由特定的列执行。如果在下面的步骤中这些列上存在多个连接或聚合,这将非常有用。

另一种方法是 *coalesce,*不同于 repartition 用于通过洗牌增加或减少分区号,它用于减少分区号而不洗牌。联合可能无法解决数据分布的不平衡问题。

# only set partition number
df = df.repartition(1000)
*# only partition accroding to colums*  
*df = df.repartition(['col_1', 'col_2', 'col_3'])
# reparition number and columns together
df.repartition(1000, ['col_1', 'col_2', 'col_3'])*

除了数据偏斜,我强烈推荐看一看这篇文章,它给出了关于重新分配的使用案例,并解释了幕后的细节。

配置输入格式以创建更多的分割并将输入数据以较小的块大小写出到 HDFS 是增加分区数量的其他技术。

8 —利用适当的文件格式—拼花地板

Apache Parquet 是一种列存储格式,旨在只选择被查询的列,跳过其余的列。它通过 Spark 提供了最快的读取性能。 Parquet 将数据排列成列,将相关值放在彼此靠近的位置,以优化查询性能,最大限度地减少 I/O,并促进压缩。此外,它实现了列修剪和谓词下推(基于统计的过滤器),这是一个简单的过程,当查询一个巨大的表时,只选择所需的数据进行处理。它可以防止在内存中加载不必要的数据部分,并减少网络使用。

拼花格式—列修剪和谓词下推

关键是从数据源中只获取相关的数据,而不管您使用什么类型的数据源,并简单地防止全表扫描。它不是 Spark 的直接问题,而是直接影响 Spark 应用程序的性能。

例如,如果您使用 Cassandra ,读取直接分区会非常有效。然而,它有时会变得棘手。假设,Cassandra 表是按日期列分区的,并且您有兴趣读取最近 15 天的数据。在这种情况下,简单地用等于操作符一个接一个地读取 day,然后将它们联合在一起,比用过滤器 > date_current-15 读取要高效得多。

# day_from is the starting point of date info and sequential 15 days are queried. 
dfs =list()
for i in range(15):
    day_i = day_from + timedelta(days=i)
    df = self.sc_session \
                .read \
                .format('org.apache.spark.sql.cassandra') \
                .options(table=self.table, keyspace=self.keyspace) \
                .load()
    df = df.filter(F.col('PARTITION_KEY_COLUMN') == day_i) # rather than > day_i
    dfs.append(df)
df_complete = reduce(DataFrame.union, dfs)  # union is a kind of narrow transformation which does not require shuffling

9 —使用带有 pyArrow 的托潘达斯

作为官方定义, Apache Arrow 是一个跨语言的内存数据开发平台。它为平面和层次数据指定了一种标准化的语言无关的列内存格式。更清楚地说, Apache Arrow 是跨语言平台之间的桥梁,它有助于读取 Spark 数据帧,然后将数据帧写入 Apache Cassandra,而不会遭受巨大的低效序列化和反序列化性能。

Apache PyArrow 是 Arrow 的 Python 实现。它提供了一个 Python API,将 Arrow 的功能与 Python 环境结合在一起,包括领先的库,如熊猫numpy 。在 Spark 中,数据只要在 JVM 中,处理速度都非常快。然而,由于 Python 中丰富的数据处理库等原因,Pyspark 开发人员可能会在 Python 环境和 JVM 之间转移数据。从这个意义上来说,在从 pandas 数据帧移动到 Spark 数据帧时使用 PyArrow ,或者反之亦然,都会带来巨大的性能提升。

要使用 PyArrow ,首先要通过 pipconda 安装。之后,在配置中启用它就足够了。剩下的都是一样的,编码没有变化。在 Pyspark 应用中使用 pyArrow 以及在 pandas 和 spark 数据帧之间的转换过程中发生了什么在这里解释得非常清楚。

pip install pyarrowspark.conf.set(“spark.sql.execution.arrow.enabled”, “true”)

外卖食品

  • 当不需要返回精确的行数时,不要使用 count() 。为了检查数据帧是否为空,考虑到性能问题, len(df.head(1)) > 0 会更准确。
  • 不要在你的产品代码中使用 show()
  • 使用 df.explain() 来深入了解 Spark(物理规划的最终版本)中数据帧的内部表示是一个很好的实践。
  • 在连接之前,总是通过过滤不相关的数据(行/列)来尽量减小数据大小。
  • 在线/离线监控 Spark 应用。它可能会给你任何关于不平衡的数据分区的线索,作业在哪里被阻塞,以及查询计划。Spark UI 的替代产品可能是 Ganglia 。
  • 基本上,避免使用循环。
  • 关注内置功能,而不是定制解决方案。
  • 确保 join 操作中的键列不包含空值。
  • 将较大的数据集放在左边的连接中。
  • 请记住,Spark 是以懒惰评估逻辑运行的。因此,在调用动作之前,不会触发任何东西。这可能会导致无意义的错误代码。
  • 如果您的其余代码不需要缓存中的数据,请将其取消持久化。
  • 完成应用程序后,关闭/停止 Spark 会话。
  • 在 Spark 3.0 中,考虑到版本升级,通过 自适应查询执行 解决了性能问题,实现了重大改进。
  • 对于数据操作,更喜欢数据帧而不是 rdd。
  • 一般来说,大于大约 20 KiB 的任务可能值得优化。
  • 一般来说,建议您的集群中每个 CPU 内核执行 2-3 个任务
  • 每个分区有一个在 128MB 内的块来实现并行性总是好的。
  • CsvJson 数据文件格式可提供高写入性能,但读取速度较慢,另一方面,拼花文件格式非常快,可提供最佳读取性能,但在写入操作方面比其他提及的文件格式慢。
  • 物理计划是自下而上读取的,而 DAG 是自上而下读取的。
  • 交换的意思是阶段之间发生了洗牌,基本上是性能下降。
  • 过多的阶段数可能是性能问题的迹象。
  • 垃圾收集(GC) 是另一个可能导致性能问题的关键因素。从 Spark UI 的执行者标签中查看。在任何与 GC 相关的情况下,通常都可以使用 Java GC 选项。
  • 序列化在任何分布式应用程序的性能中也扮演着重要的角色。将对象序列化的速度较慢或消耗大量字节的格式将大大降低计算速度。对于基于 Scala/Java 的 Spark 应用,强烈推荐使用 Kryo 序列化。在 Pyspark 中,支持 Marshal 和 Pickle 序列化器, MarshalSerializerPickleSerializer 快,但支持的数据类型少。
  • 注意,如果您更喜欢在 docker 环境中使用 Spark,您可能会体验到性能损失。在我们的项目中,我们观察到,在 docker 环境中,Spark 应用程序使用相同的配置指标需要更长的时间。

有用的链接

  • https://towards data science . com/the-art-of-joining-in-spark-dcbd 33d 693 c
  • https://medium . com/@ brajendragouda/5-key-factors-to-keep-in-mind-while-optimization-Apache-spark-in-AWS-part-2-c 0197276623 c
  • https://medium . com/teads-engineering/spark-performance-tuning-from-the-trench-7 cbde 521 cf 60
  • https://medium . com/tblx-insider/how-we-reduced-our-Apache-spark-cluster-cost-using-best-practices-ac1f 176379 AC
  • https://medium . com/Expedia-group-tech/part-3-efficient-executor-configuration-for-Apache-spark-b 4602929262
  • https://towards data science . com/about-joins-in-spark-3-0-1 E0 ea 083 ea 86
  • https://towards data science . com/be-in-charge-of-query-execution-in-spark-SQL-c 83 D1 e 16 b 9 b 8
  • https://ch-naba run . medium . com/Apache-spark-optimization-techniques-54864d 4 FDC 0 c
  • https://changhsinlee.com/pyspark-dataframe-basics/
  • https://robertovitillo.com/spark-best-practices/
  • 【https://luminousmen.com/post/spark-tips-partition-tuning

Apache Spark:在应用程序中的并发作业之间公平共享

原文:https://towardsdatascience.com/apache-spark-sharing-fairly-between-concurrent-jobs-d1caba6e77c2?source=collection_archive---------12-----------------------

确保在应用程序中的并发作业之间平等分配资源,而不管它们的大小

Krzysztof Maksimiuk 在 Unsplash 上的照片

注意:在本帖中,短语“spark job”和“job”用于指代 spark 操作,如保存、收集、计数等。,短语“并发作业”是指在一个应用程序中同时运行的多个并行 spark 动作。

在我的 上一篇文章 中,我们讨论了通过 Scala Futures 或 Parallel Collections 并发运行 Spark 作业来提升单调的 Apache Spark 应用程序,这将应用程序时间减少到了四分之一。(如果你之前没看过,现在看就值得了。)

然而,可能存在这样一种情况,即仅在 spark 作业级别实现并发性不足以优化应用程序的性能。例如,一个大型作业消耗所有可用的 spark 资源,并将其他并行作业推入等待状态,直到前者的任务没有利用所有资源。发生这种情况是因为 spark 在应用程序中的默认调度选项是 FIFO(先进先出),这确保了第一个作业优先获得所有可用的 Spark 资源,直到其阶段有任务运行。在大多数这样的场景中,我们绝不会希望 spark 应用程序只被一个长作业卡住,同时,我们希望所有 spark 作业,无论是短作业还是长作业,都能公平地共享资源。

为了满足我们的需求,Apache Spark 提供了一个完美的解决方案,通过它我们可以将默认的调度选项更改为 FAIR,这样任务之间的任务就会以循环方式执行。这意味着所有的工作都获得了同等份额的星火资源。通过使用公平调度,我将我的应用程序的持续时间减少了 27 %,因此,我强烈推荐它用于任何同时运行多个并行 spark 作业的 spark 应用程序。

利用公平调度的先决条件:在 Spark 应用程序中,多个任务从不同的线程提交。如果你不知道怎么做,请看这里的。

此时,我们准备探索如何在具有并发作业的 Spark 应用程序中实现公平调度。

首先创建“ fairscheduler.xml ”,使用您选择的池名和调度模式 FAIR。这里,我们需要明确地提到它的调度模式,因为默认情况下,它是每个池的 FIFO。你可以在这里了解更多。

https://gist . github . com/hariviapak/06310 a6f 62 fe2b 59 ea 37 b 8 D1 cc 051726

其次,我们需要配置 spark 应用程序,以便在创建 spark 会话或提交 Spark 应用程序时使用公平调度。

最后,我们需要将" fairscheduler.xml "中定义的池设置为 spark 上下文的本地属性。

https://gist . github . com/hariviapak/d3ba 23 ea 12082 e 6 f 36 df 124 e 5 f 6 bea 8 a

通过 Spark UI 确认公平调度

确认公平调度是否成功实施是非常重要的,因为在执行上述步骤时稍有差错,就可能使您回到起点。此外,

  1. 仅将" spark.scheduler.mode "设置为" FAIR "是不够的,因为任务的阶段仍在调度模式为 FIFO 的默认池中运行。
  2. 这就是我们用公平调度模式创建自己的池的原因。
  3. 并将该池设置为 spark context 的本地属性。

为了确认我们的工作,我们可以使用 Spark UI。

  1. 检查“Jobs”页面,确认调度模式是公平的,但是,如前所述,它不能确保池级别的调度也是公平的。
  2. 因此,转到“阶段”页面,检查正在运行或已完成阶段的池名称。在我们的例子中,应该是“ mypool ”。

下面的片段将帮助你更清楚地理解它。

Spark UI 的工作网页

Spark UI 的 Stages 网页

总之,公平调度是一个必须具备的特性,如果一个应用程序包含并发运行的大小 spark 任务。通过这种方式,我们可以在资源利用方面显著提高应用程序的性能,并最终节省成本。

我希望,你喜欢这篇文章。我很乐意在评论区听到你的建议或反馈。

参考文献

  • Apache Spark 官方文档|https://spark.apache.org/docs/latest/job-scheduling.html
  • 通过运行多个并行作业提升 Apache Spark 应用Hari Viapak Garg

从人工智能算法到全功能 API

原文:https://towardsdatascience.com/api-flask-fastapi-http-postman-e1673d672596?source=collection_archive---------15-----------------------

如何简单高效地将你的 AI 算法公开为 API

图片由来自 Pexels 的 Brett Sayles 拍摄

作为一名数据科学家,当与其他开发人员一起从事一个复杂的项目时,你经常需要将你的人工智能算法打包到我们称为API的东西中,后端可以调用它来协调你的应用程序。使用 API 有几个好处,可以使您的预测更高效、更省时。

在这篇文章中,我们将通过深入研究RESTfulAPI 的定义,然后我们将通过模块FlaskFastAPI使用 python 创建一个 API。最后,我们将看到如何通过Curls或软件Postman使用HTTP协议与它通信。

目录

摘要如下:

  1. API & RESTful API
  2. HTTP 协议& CURL &邮递员
  3. 数据科学算法
  4. 烧瓶
  5. FastAPI

API & RESTful API

一个 API,作为一个应用程序的接口,是一个计算机工具,它允许将你的代码打包成一个服务,这个服务可以简单有效地进行交流。
这可以被视为将您的开发转化为一个黑盒的一个步骤,黑盒带有预定义的通信代码,允许您作为provider随时向团队中的clientsconsumers前端和后端开发人员公开。

作者图片

有许多免费的 API(天气、航班搜索、足球……)可以在 RapidAPI 上探索。
原料药有多种类型:

  • 公共开放API:无限制
  • 私有内部API:在同一公司内使用
  • 合作伙伴API:需要许可证才能访问

如果 API 使两个应用程序能够通信, Web 服务 API,对于它们来说,使给定网络上的两台机器之间的交互成为可能。这是一个使用万维网上的url来提供对其服务的访问的系统。
web 服务 API 有很多种,比如 SOAPJSON-RPCXML-RPC ,...等等。在本文中,我们将主要关注另一种类型的 REST 。与其他协议形式的 web 服务 API 不同,REST 是一套五大架构原则,使得 RESTful web 服务变得轻便、高效、可伸缩且易于使用:

  • 客户机-服务器架构:当客户机与服务器通信时,它要么接受请求并发送响应,要么拒绝请求并通知服务器
  • 无状态:不在服务器上存储任何信息。这也意味着客户端应该确保所有需要的数据都在请求中
  • 可缓存性:在客户端实现,以便在发送旧请求时返回更快的响应
  • 分层系统:覆盖附加层的能力,例如,安全层或负载均衡器,而不影响客户机-服务器交换
  • 统一接口:简单来说,就是使用URIs,为Un formRe sourceI标识器,来公开仓库的结构。结合 HTTP 方法,它允许与服务器进行有效的XMLJSON交换。

作者图片

HTTP 协议& CURL & Postman

HTTP 协议

一旦你创建了你的 web 服务 API,你将需要与它通信,这就是 HTTP 开始发挥作用的时候。HTTP,是一种网络通信协议,用于在网络上交换数据。它旨在促进网络服务器和网络导航器(如 Google Chrome 和 Safari)之间的通信。它是一个无状态的协议,遵循客户端-服务器架构,很容易与 RESTful APIs 集成。

作者图片

下面是一些最常用的协议方法:

  • POST :在服务器上创建一个资源
  • GET :访问服务器上的资源
  • 上传:更新服务器上的一个资源
  • 删除:删除服务器上的一个资源

卷曲

通常,在虚拟机上工作时,您只能访问一个command line interface,因为没有graphical interface,因此也没有导航器。
CURL ,forClientURLRequestLlibrary,之前名为 httpget ,是一个命令行工具,用于获取和发送资源到连接到某个网络的服务器。它支持许多协议,包括 HTTP。有许多 curl 选项,但是在本文中,我们将专注于一个专门针对 RESTful API 的选项:

  • -X :确定与服务器通信时使用的 HTTP 方法

邮递员

Postman 是一个简化 API 开发和测试的软件或平台。
通过其用户友好的界面,它提供了一种非常简单的发送请求的方式:

  • 选择 HTTP 方法
  • 输入 API 正在监听的 URL端口
  • 选择参数,这将自动更新 HTTP 请求
  • 向 API 发送请求

可以在页面的主体部分的底部看到响应。

作者图片

数据科学算法

为了便于说明,我们将考虑鸢尾数据集,其中 ML 任务包括使用四个变量(萼片长度、萼片宽度、花瓣长度和花瓣宽度)将鸢尾分为三类(Setosa、Versicolour 和 Virginica)。

iris 数据集将从 Sklearn 下载,我们将使用随机森林分类器进行训练。

import numpy as np
import pandas as pd

from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix
from sklearn.metrics import accuracy_score
from sklearn.datasets import load_iris
from matplotlib import pyplot as plt
import joblib

%matplotlib inline
WEIGHTS_DIR="weights/"

iris = load_iris()

df=pd.DataFrame(iris.data, columns=iris.feature_names)
df["species"]=iris.target

X = df[iris.feature_names]
y = df['species']

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.20, random_state=42)

clf = RandomForestClassifier(max_depth=2, random_state=42)
clf.fit(X_train, y_train)
y_pred = clf.predict(X_test)

print("Confusion matrix: \n", confusion_matrix(y_test, y_pred))
print("Accuracy score: ", accuracy_score(y_test, y_pred, normalize=True) )

joblib.dump(clf, WEIGHTS_DIR+"clf_iris.joblib")

经过训练,我们得到以下结果:

>>> Confusion matrix:
    array([[10,  0,  0],
        [ 0,  9,  0],
        [ 0,  0, 11]])
>>> Accuracy score: 1.0

在以下段落中,我们将对以下数据集进行预测:

Flask 是一个 python 模块,用于创建 API 并在给定的网络上公开它们的服务。可以使用以下命令行安装它:

pip install flask

在下面的代码中,我将创建一个 flask API,它使用之前训练的模型来预测虹膜的类别,给定四个变量作为输入。

初始化 API

#%%
from flask import Flask, request, jsonify
import pandas as pd
import joblib
import jsonWEIGHTS_DIR = "weights/"
FLASK_API = Flask(__name__)

加载模型

def get_iris_model():
    loaded_clf = joblib.load(WEIGHTS_DIR + "clf_iris.joblib")
    return loaded_clfloaded_clf = get_iris_model()def str_to_float_list(arg):
    arg = arg.split(",")
    arg = [float(x) for x in arg]
    return arg

路由

当创建一个 API 时, Routes 用于公开它的功能和服务。在 flask 中,使用 decorators 添加了。

-predict _ class _ postman 我们创建路线,通过该路线我们将进行预测。这条路径返回一个 json 响应,其中包含每组变量的相应类。当使用 Postman 时,我们使用请求args 参数提取变量。

#%%Postman
def get_params_postman(request):
    sep_length = str_to_float_list(request.args.get("sepLen"))
    sep_width = str_to_float_list(request.args.get("sepWid"))
    pet_length = str_to_float_list(request.args.get("petLen"))
    pet_width = str_to_float_list(request.args.get("petWid")) return (sep_length, sep_width, pet_length, pet_width)@FLASK_API.route("/predict_class_postman", methods=["GET", "POST"])
def predict_class_postman():
    (sep_length, sep_width, pet_length, pet_width) = get_params_postman(request)
    new_row = pd.DataFrame(
        {
            "sepal length (cm)": [float(x) for x in sep_length],
            "sepal width (cm)": [float(x) for x in sep_width],
            "petal length (cm)": [float(x) for x in pet_length],
            "petal width (cm)": [float(x) for x in pet_width],
        }
    )
    y_pred = list(loaded_clf.predict(new_row))
    y_pred = [str(x) for x in y_pred] response = {"y_pred": ",".join(y_pred)}
    return jsonify(response)

-predict _ class _ curl 我们这次创建另一条路由来与 CURL 命令通信。我们使用发送的请求的方法 form.get 从命令行提取变量。

#%%CURL
def get_params_curl(request):
    request_input = request.form.get("input")
    request_input = json.loads(request_input) sep_length = str_to_float_list(request_input["sepLen"])
    sep_width = str_to_float_list(request_input["sepWid"])
    pet_length = str_to_float_list(request_input["petLen"])
    pet_width = str_to_float_list(request_input["petWid"]) return (sep_length, sep_width, pet_length, pet_width) @FLASK_API.route("/predict_class_curl", methods=["GET", "POST"])
def predict_class_curl():
    (sep_length, sep_width, pet_length, pet_width) = get_params_curl(request)
    new_row = pd.DataFrame(
        {
            "sepal length (cm)": [float(x) for x in sep_length],
            "sepal width (cm)": [float(x) for x in sep_width],
            "petal length (cm)": [float(x) for x in pet_length],
            "petal width (cm)": [float(x) for x in pet_width],
        }
    )
    y_pred = list(loaded_clf.predict(new_row))
    y_pred = [str(x) for x in y_pred] response = {"y_pred": ",".join(y_pred)}
    return jsonify(response)

启动服务

一旦我们定义了上面的所有元素,我们通过添加以下代码来启动 API 的服务:

#%%
if __name__ == "__main__":
    FLASK_API.debug = True
    FLASK_API.run(host="0.0.0.0", port="8080")
  • debug mode有助于即时可视化变化
  • 我们可以选择暴露 API 的URLport:

要启动 API,请键入:

python flask_api.py

其中flask_api.py是托管上面开发的所有代码的文件。

我们得到以下响应:

>>> * Serving Flask app "flask_api" (lazy loading)
>>> * Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
>>> * Debug mode: on
>>> * Running on [http://0.0.0.0:8080/](http://0.0.0.0:8080/) (Press CTRL+C to quit)
>>> * Restarting with fsevents reloader
>>> * Debugger is active!
>>> * Debugger PIN: 514-546-929

请求&响应

+邮递员
给定了HTTP request上的邮递员:

localhost:8080/predict_class_postman?sepLen=1,5&sepWid=2,6&petLen=3,7&petWid=4,8

答复如下:

{
  "y_pred": "1,2"
}

作者图片

+ CURL
我们使用 CURL 启动以下命令行来与 API 通信:

curl -F "input={\"sepLen\":\"1,5\",\"sepWid\":\"2,6\",\"petLen\":\"3,7\",\"petWid\":\"4,8\"}" -X POST "http://0.0.0.0:8080/predict_class_curl"

正如所料,我们得到了相同的结果:

{
  "y_pred": "1,2"
}

作者图片

**HTTP 代码:**如果请求正确,API 返回 HTTP 代码 200 。还存在其他代码,如客户端错误的 4xx 和服务器错误的 5xx

作者图片

你可以在我的 GitHub 资源库中找到 flask API 的代码。

FastAPI

FastAPI 是另一个支持 API 开发的 python 模块。
可以使用命令行进行安装:

pip install fastapi

它与 Flask 非常相似,但速度更快,有一些细微的变化:

  • 使用request.query_params提取postman中的查询参数
  • 使用eval(input)获得curls中的形状参数,其中input: str = Form(...)
#%%
import pandas as pd
import joblib
import json
from fastapi import FastAPI, Form, Request
import uvicorn

WEIGHTS_DIR = "weights/"
FASTAPI_API = FastAPI()

#%%
def get_iris_model():
    loaded_clf = joblib.load(WEIGHTS_DIR + "clf_iris.joblib")
    return loaded_clf

def str_to_float_list(arg):
    arg = arg.split(",")
    arg = [float(x) for x in arg]
    return arg

loaded_clf = get_iris_model()

#%%Postman
def get_params_postman(query_params):
    sep_length = str_to_float_list(query_params["sepLen"])
    sep_width = str_to_float_list(query_params["sepWid"])
    pet_length = str_to_float_list(query_params["petLen"])
    pet_width = str_to_float_list(query_params["petWid"])
    return (sep_length, sep_width, pet_length, pet_width)

@FASTAPI_API.post("/predict_class_postman")
def predict_class_postman(request: Request):
    query_params = dict(request.query_params)
    (sep_length, sep_width, pet_length, pet_width) = get_params_postman(query_params)
    new_row = pd.DataFrame(
        {
            "sepal length (cm)": [float(x) for x in sep_length],
            "sepal width (cm)": [float(x) for x in sep_width],
            "petal length (cm)": [float(x) for x in pet_length],
            "petal width (cm)": [float(x) for x in pet_width],
        }
    )
    y_pred = list(loaded_clf.predict(new_row))
    y_pred = [str(x) for x in y_pred]

    response = {"y_pred": ",".join(y_pred)}
    return response

#%%CURL
def get_params_curls(input_var):
    sep_length = str_to_float_list(input_var["sepLen"])
    sep_width = str_to_float_list(input_var["sepWid"])
    pet_length = str_to_float_list(input_var["petLen"])
    pet_width = str_to_float_list(input_var["petWid"])
    return (sep_length, sep_width, pet_length, pet_width)

@FASTAPI_API.post("/predict_class_curl")
def predict_class_curl(input: str = Form(...)):
    input_var = eval(input)
    (sep_length, sep_width, pet_length, pet_width) = get_params_curls(input_var)
    new_row = pd.DataFrame(
        {
            "sepal length (cm)": [float(x) for x in sep_length],
            "sepal width (cm)": [float(x) for x in sep_width],
            "petal length (cm)": [float(x) for x in pet_length],
            "petal width (cm)": [float(x) for x in pet_width],
        }
    )
    y_pred = list(loaded_clf.predict(new_row))
    y_pred = [str(x) for x in y_pred]

    response = {"y_pred": ",".join(y_pred)}
    return response

#%%
if __name__ == "__main__":
    uvicorn.run(FASTAPI_API, host="0.0.0.0", port=8080)

FastAPI 是使用uvicon运行的。这是一个闪电般快速的 ASGI 服务器实现,基于 uvloop 和 httptools,其中 uvloop 是 asyncio 事件循环的基于 Cython 的替代,比默认事件循环快 2-4 倍。
可以使用以下命令行安装 Uvicorn:

pip install uvicorn

要启动 API,请键入:

python fastapi_api.py

其中fastapi_api.py是托管上面开发的所有代码的文件。

我们得到以下响应:

>>> INFO:     Started server process [50003]
>>> INFO:     Waiting for application startup.
>>> INFO:     Application startup complete.
>>> INFO:     Uvicorn running on [http://0.0.0.0:8080](http://0.0.0.0:8080) (Press CTRL+C to quit)

请求和回应

+ Postman
给定对 Postman 的 HTTP 请求:

localhost:8080/predict_class_postman?sepLen=1,5&sepWid=2,6&petLen=3,7&petWid=4,8

答复如下:

{
  "y_pred": "1,2"
}

作者图片

+ CURL
我们使用 CURL 启动以下命令行与 API 进行通信:

curl -F "input={\"sepLen\":\"1,5\",\"sepWid\":\"2,6\",\"petLen\":\"3,7\",\"petWid\":\"4,8\"}" -X POST "http://0.0.0.0:8080/predict_class_curl"

正如所料,我们用一个 200 HTTP 代码得到了相同的结果:

{
  "y_pred": "1,2"
}

作者图片

你可以在我的 GitHub 资源库中找到 FastAPI API 的代码。

结论

API 是非常强大的工具,允许您向服务公开您的工作,并促进与服务的通信。当在一个开发团队中工作时,掌握这些技术对于项目的进展变得至关重要。

API 与 R 的交互

原文:https://towardsdatascience.com/api-interaction-with-r-46bf1d4f4fe6?source=collection_archive---------28-----------------------

实践教程

演示与 NHL API 交互的插图

这个文档是一个小插图,展示了如何从一个 API 中检索数据。为了演示,我将与 NHL API 进行交互。我将构建一些函数来与一些端点进行交互,并探索一些我可以检索的数据。

需要注意的是,其中一些函数返回团队级别的数据。一些 API 使用特许 ID 号,而一些使用最近的团队 ID 来选择特定团队的端点。因此,如果您使用我的任何功能,我建议您提供完整的团队名称(例如"Montréal Canadiens")。我的函数会把它们解码成合适的 ID 号。

这篇文章很长,所以如果需要编写函数与 API 交互的例子,只需阅读前半部分。

我在文章的底部提供了一个到 github pages 版本的链接,它提供了一个到 github repo 的链接。

要求

为了使用与 NHL API 交互的函数,我使用了以下包:

  • [tidyverse](https://www.tidyverse.org/):大量有用的数据操作和可视化特性
  • [jsonlite](https://cran.r-project.org/web/packages/jsonlite/) : API 交互

除了这些包之外,我还在文档的其余部分使用了以下包:

  • [cowplot](https://cran.r-project.org/web/packages/cowplot/index.html):针对ggplot2的额外功能
  • [imager](https://cran.r-project.org/web/packages/imager/):载入图像
  • [broom](https://cran.r-project.org/web/packages/broom/vignettes/broom.html):整理回归输出进行显示
  • 以友好的方式显示表格

API 交互功能

在这里,我定义了与 NHL 记录 API 和 NHL 统计 API 交互的函数,以及一些辅助函数。

convertToNumeric

我创建了这个助手函数来将包含存储为character值的数字数据的列转换为数字数据类型。我遇到了一个问题,我的 API 调用将一些数字数据作为character数据返回,我需要一种方法来处理这个问题,而不只是根据需要调用as.numeric

convertToNumeric **<-** **function**(vec){
  *###*
  *# This function will convert the input vector to a numeric vector    
  # if it is able to. Otherwise, it just returns the vector.*
  *###*

  *# If any of the values in vec return NA when trying to convert to 
  # numeric, set output to the unchanged input.*
  **if** (**any**(**is.na**(suppressWarnings(**as.numeric**(vec))) **==** **TRUE**)){
    output **<-** vec
  }
  *# Otherwise, convert vec to a numeric vector.*
  **else** {
    output **<-** **as.numeric**(vec)
  }
  *# Return output.*
  **return**(output)
}

franchise

我编写这个函数是为了与 NHL Records API 的franchise端点进行交互。它返回一个data.frame,包含 NHL 历史上每个球队的第一个和最后一个赛季的球队和当前球队 Id 号、球队的名称和缩写。随着地点的改变,一些球队已经改变了球队的名字。它需要一个参数。team,可以是"all",球队全称(如"New Jersey Devils"),也可以是球队 Id (如23新泽西魔鬼队)。

franchise **<-** **function**(team**=**"all"){
  *###*
  *# This functions returns a data.frame with metadata on NHL teams. 
  # It can also return those columns for a single team if a 
  # franchise ID or name is passed.*
  *###*

  *# Get the franchise data from the franchises endpoint.*
  outputAPI **<-** fromJSON(
      "https://records.nhl.com/site/api/franchise"
      )

  *# Select only the data.frame from the JSON output.*
  output **<-** outputAPI**$**data

  *# If team does not equal "all", check if it is a franchise ID or 
  # team name.*
  **if** (team **!=** "all"){

    *# If team is in the id column, subset output for just that row.*
    **if** (team **%in%** output**$**id){
      output **<-** output **%>%**
        filter(id **==** team)
    }
    *# If team is in the fullName column, subset output for just that 
    # row.*
    **else** **if** (team **%in%** output**$**fullName){
      output **<-** output **%>%**
        filter(fullName **==** team)
    }
    *# Otherwise, throw an informative error.*
    **else** {
      message **<-** paste("ERROR: Argument for team was not found in ",      
                       "either the fullName or id columns. Try ",  
                       "franchise('all') to find the franchise ", 
                       "you're looking for.")
      stop(message)
    }
  }
  *# Do nothing if the team value equals "all".*
  **else** {
  }

  *# Convert any columns that should be numeric to numeric, while 
  # suppressing messages.*
  output **<-** suppressMessages(
                as.data.frame(lapply(output, convertToNumeric)))

  *# Return the output data.frame.*
  **return**(output)
}

teamTotals

我编写这个函数是为了与 NHL 记录 API 的franchise-team-totals端点进行交互。它返回了球队整个历史上常规赛和季后赛的大量统计数据。它需要一个参数。team,可以是"all",球队全称(如"New Jersey Devils"),也可以是球队 Id (如1新泽西魔鬼队)。

teamTotals **<-** **function**(team**=**"all"){
  *###*
  *# This function returns total stats for every franchise (ex 
  # roadTies, roadWins, etc) unless a specific team Id or full team 
  # name is passed. Then it returns that data for the specific team.    
  # The output is a data.frame.*
  *###*

  *# Get the franchise data from the franchises endpoint.*
  outputAPI **<-** fromJSON(
    "https://records.nhl.com/site/api/franchise-team-totals"
    )

  *# Select only the data.frame from the JSON output.*
  output **<-** outputAPI**$**data

  *# If team does not equal "all", check if it is a team ID or team 
  # name.*
  **if** (team **!=** "all"){

    *# If team is in the teamId column, subset output for just that 
    # row.*
    **if** (team **%in%** output**$**teamId){
      output **<-** output **%>%**
        filter(teamId **==** team)
    }
    *# If team is in the teamName column, subset output for just that 
    # row.*
    **else** **if** (team **%in%** output**$**teamName){
      output **<-** output **%>%**
        filter(teamName **==** team)
    }
    *# Otherwise, warn the user and return the entire dataframe.*
    **else** {
      message **<-** paste("WARNING: Argument for team was not found ",
                       "in either the teamName or franchiseId ", 
                       "columns. Returning all franchises.")
      warning(message)
    }
  }
  *# Do nothing if the team value equals "all".*
  **else** {
  }

  *# Convert any columns that should be numeric to numeric values.*
  output **<-** suppressMessages(
                as.data.frame(lapply(output, convertToNumeric)))

  *# Return the output data.frame.*
  **return**(output)
}

findId

这是一个帮助功能,用于查找最新的球队 Id 或球队 Id,以获得完整的球队名称(例如findId("Boston Bruins", "team")findId("Boston Bruins", "franchise"))。它用于为 API 端点找到合适的 Id 号。

findId **<-** **function**(teamName, idType){

  *# Call the teamTotals function with the team name so we can look 
  # up the appropriate Id from it.*
  outputAPI **<-** teamTotals(teamName)

  *# Retrieve the franchise Id if that is what the idType is.*
  **if** (idType **==** "franchise"){
    output **<-** outputAPI[1,]**$**franchiseId
  }
  *# Retrieve the team Id if that is what the idType is.*
  **else** **if** (idType **==** "team"){
    output **<-** outputAPI[1,]**$**teamId
  }
  *# Any other argument throws an error.*
  **else** {
    stop(paste("ERROR: Invalid idType argument! Should be ", 
               "'franchise' or 'team'!"))
  }
  *# Return the appropriate Id.*
  **return**(output)
}

seasonRecords

seasonRecords函数返回单个专营权的记录统计数据。比如一个赛季进球最多的,以及他们进球的赛季。它需要一个参数。team,可以是球队全称(如"New Jersey Devils"),也可以是球队 Id (如23新泽西魔鬼队)。

seasonRecords **<-** **function**(team){
  *###*
  *# This functions returns a data.frame with the season records for 
  # a variety of stats for a single team.*
  *###*

  *# If team is a "character" type, try to look up the franchise id.*
  **if** (typeof(team) **==** "character"){
    teamId **=** findId(team, "franchise")
  }
  *# If team is an integer, set teamId equal to team.*
  **else** **if** ((typeof(team) **==** "double") **&** (team **%%** 1 **==** 0)){
    teamId **=** team
  }
  *# Otherwise, throw an error.*
  **else** {
    message **<-** paste("ERROR: Please pass a franchise id (integer) ", 
                     "or a full team name (e.g. 'Boston Bruins').")
    stop(message)
  }

  *# Set the base url, endpoint, and combine them with teamId for the 
  # full url.*
  baseURL **<-** "https://records.nhl.com/site/api/"
  endpoint **<-** "franchise-season-records?cayenneExp=franchiseId="
  fullURL **<-** paste0(baseURL, endpoint, teamId)

  *# Get the API output.*
  outputAPI **<-** fromJSON(fullURL)
  *# Select only the data from the API output.*
  output **<-** outputAPI**$**data

  *# Convert any columns that should be numeric to numeric format.*
  output **<-** suppressMessages(
                as.data.frame(lapply(output, convertToNumeric)))

  *# Return the output from the request.*
  **return**(output)
}

goalieRecords

goalieRecords函数返回为一个球队效力的所有守门员的统计数据。比如一场比赛最多的扑救,比赛日期。它需要一个参数。team,可以是球队全称(如"New Jersey Devils"),也可以是球队 Id (如23新泽西魔鬼队)。

goalieRecords **<-** **function**(team){
  *###*
  *# This functions returns a data.frame with the goalie records for 
  # a team.*
  *###*

  *# If team is a "character" type, try to look up the franchise id.*
  **if** (typeof(team) **==** "character"){
    teamId **=** findId(team, "franchise")
  }
  *# If team is an integer, set teamId equal to team.*
  **else** **if** ((typeof(team) **==** "double") **&** (team **%%** 1 **==** 0)){
    teamId **=** team
  }
  *# Otherwise, throw an error.*
  **else** {
    message **<-** paste("ERROR: Please pass a franchise id (integer) ",         
                     "or a full team name (e.g. 'Boston Bruins').")
    stop(message)
  }

  *# Set the base url, endpoint, and combine them with teamId for the 
  # full url.*
  baseURL **<-** "https://records.nhl.com/site/api/"
  endpoint **<-** "franchise-goalie-records?cayenneExp=franchiseId="
  fullURL **<-** paste0(baseURL, endpoint, teamId)

  *# Get the API output.*
  outputAPI **<-** fromJSON(fullURL)
  *# Select only the data from the JSON output.*
  output **<-** outputAPI**$**data

  *# Convert any columns that should be numeric to numeric format.*
  output **<-** suppressMessages(
                as.data.frame(lapply(output, convertToNumeric)))

  *# Return the output from the request.*
  **return**(output)
}

skaterRecords

skaterRecords函数返回所有为一个球队效力的非守门员球员的数据。例如,他们在一个赛季中被罚的时间最多,以及这个赛季发生的时间。它需要一个参数。team,可以是球队全称(如"New Jersey Devils"),也可以是球队 Id (如23新泽西魔鬼队)。

skaterRecords **<-** **function**(team){
  *###*
  *# This functions returns a data.frame with the skater records for 
  # a team.*
  *###*

  *# If team is a "character" type, try to look up the franchise id.*
  **if** (typeof(team) **==** "character"){
    teamId **=** findId(team, "franchise")
  }
  *# If team is an integer, set teamId equal to team.*
  **else** **if** ((typeof(team) **==** "double") **&** (team **%%** 1 **==** 0)){
    teamId **=** team
  }
  *# Otherwise, throw an error.*
  **else** {
    message **<-** paste("ERROR: Please pass a franchise id (integer) ",     
                     "or a full team name (e.g. 'Boston Bruins').")
    stop(message)
  }

  *# Set the base url, endpoint, and combine them with teamId for the 
  # full url.*
  baseURL **<-** "https://records.nhl.com/site/api/"
  endpoint **<-** "franchise-skater-records?cayenneExp=franchiseId="
  fullURL **<-** paste0(baseURL, endpoint, teamId)

  *# Get the API output.*
  outputAPI **<-** fromJSON(fullURL)
  *# Select only the data from the JSON output.*
  output **<-** outputAPI**$**data

  *# Convert any columns that should be numeric to numeric format.*
  output **<-** suppressMessages(
                as.data.frame(lapply(output, convertToNumeric)))

  *# Return the output from the request.*
  **return**(output)
}

franchiseDetail

这个函数获取特许经营权的信息,比如他们的退休号码。它需要一个参数。team,可以是球队全称(如"New Jersey Devils"),也可以是最近的球队 Id (如1新泽西魔鬼队)。

franchiseDetail **<-** **function**(team){
  *###*
  *# This functions returns a data.frame with the data for a team.*
  *###*

  *# If team is a "character" type, try to look up the team id.*
  **if** (typeof(team) **==** "character"){
    teamId **=** findId(team, "team")
  }
  *# If team is an integer, set teamId equal to team.*
  **else** **if** ((typeof(team) **==** "double") **&** (team **%%** 1 **==** 0)){
    teamId **=** team
  }
  *# Otherwise, throw an error.*
  **else** {
    message **<-** paste("ERROR: Please pass a team id (integer) or ",
                     "a full team name (e.g. 'Boston Bruins').")
    stop(message)
  }

  *# Set the base url, endpoint, and combine them with teamId for the 
  # full url.*
  baseURL **<-** "https://records.nhl.com/site/api/"
  endpoint **<-** "franchise-detail?cayenneExp=mostRecentTeamId="
  fullURL **<-** paste0(baseURL, endpoint, teamId)

  *# Get the API output.*
  outputAPI **<-** fromJSON(fullURL)
  *# Select only the data from the JSON output.*
  output **<-** outputAPI**$**data

  *# If the output has no rows after becoming a data.frame, then the 
  # team is now a different team under an active franchise. Then we   
  # need to re-run the query with the appropriate team Id.*
  **if** (nrow(as.data.frame(output)) **==** 0){

    *# Find the most recent team Id for this deprecated team that is 
    # in the history of the active franchise.*
    teamId **<-** franchise(findId(team, "franchise"))**$**mostRecentTeamId

    *# Combine the URL components with teamId for the full url.*
    fullURL **<-** paste0(baseURL, endpoint, teamId)

    *# Get the API output.*
    outputAPI **<-** fromJSON(fullURL)
    *# Select only the data from the JSON output.*
    output **<-** outputAPI**$**data
    }

  *# Convert any columns that should be numeric to numeric format.*
  output **<-** suppressMessages(
                as.data.frame(lapply(output, convertToNumeric)))

  *# Return the output from the request.*
  **return**(output)
}

seasonStats

这个函数返回一个包含当前赛季统计数据的data.frame。它需要一个参数。team,可以是"all",现役球队的全称(如"New Jersey Devils"),也可以是最近的球队 Id (如1新泽西魔鬼队)。

seasonStats **<-** **function**(team**=**"all", raw**=FALSE**){
  *###*
  *# Returns the current seasons stats for all teams or just one. If 
  # raw is FALSE, it returns only the stats. If raw is TRUE, it just 
  # returns the API output for the user to parse.*
  *###*

  *# If raw is not a valid input, throw an error.*
  **if** ((raw **!=** **TRUE**) **&** (raw **!=** **FALSE**)){
    stop("ERROR: Argument for raw must equal TRUE or FALSE!")
  }
  *# Otherwise do nothing.*
  **else** {
  }

  *# If team equals "all" the spot where ID goes in the URL will be 
  # blank.*
  **if** (team **==** "all"){
    teamId **=** ""
  }
  *# If team is a "character" type, try to look up the team id.*
  **else** **if** (typeof(team) **==** "character"){
    teamId **=** findId(team, "team")
  }
  *# If team is an integer, set teamId equal to team.*
  **else** **if** ((typeof(team) **==** "double") **&** (team **%%** 1 **==** 0)){
    teamId **=** team
  }
  *# Otherwise, throw an error.*
  **else** {
    message **<-** paste("ERROR: Please pass a team id (integer) or ",
                     "a full team name (e.g. 'Boston Bruins').")
    stop(message)
  }

  *# Paste together the endpoint URL from its components.*
  baseURL **<-** "https://statsapi.web.nhl.com/api/v1/teams/"
  modifier **<-** "?expand=team.stats"
  fullURL **<-** paste0(baseURL, teamId, modifier)
  *# Get the data from the endpoint.*
  outputAPI **<-** fromJSON(fullURL, flatten**=TRUE**)

  *# If the user doesn't want every team's data, execute this chunk.*
  **if** (team **!=** "all"){

    *# If raw is FALSE, give back only the stats.*
    **if** (raw **==** **FALSE**){
      *# Navigate through the columns and list indices to select the 
      # stats only.*
      teamStats **<-** outputAPI**$**teams**$**teamStats[[1]]**$**splits[[1]][1,]
      *# Convert any columns that should be numeric to numeric.*
      teamStats **<-** suppressMessages(
                       as.data.frame(lapply(teamStats,                                 
                                            convertToNumeric)))
    }
    *# If the user wants the raw API output, give it to them so they 
    # can parse it themselves.*
    **else** {
      teamStats **<-** outputAPI**$**teams
    }
  }
  *# Otherwise, return them the data for all teams.*
  **else** {

    *# If raw is FALSE, give back only the stats.*
    **if** (raw **==** **FALSE**){

      *# Get the teamStats list where each element is a data.frame.*
      output **<-** outputAPI**$**teams**$**teamStats
      *# Count the number of teams. The last element is NULL.*
      num_teams **=** **length**(output) **-** 1
      *# Make a variable to hold just the stats, starting with the 
      # first team.*
      teamStats **<-** output[[1]]**$**splits[[1]][1,]

      *# Loop through the 2nd to the last team in the list.*
      **for** (i **in** seq(2, num_teams)){
        *# Select only the first row of the seasons stats for the 
        # team.*
        stats **<-** output[[i]]**$**splits[[1]][1,]
        *# Add the row to teamStats.*
        teamStats **<-** rbind(teamStats, stats)
      }
    *# Convert any columns that should be numeric to numeric format.*
    teamStats **<-** suppressMessages(
                     as.data.frame(lapply(teamStats,                                 
                                          convertToNumeric)))
    }
    *# If the user wants the raw API output, give it to them so they 
    # can parse it themselves.*
    **else** {
      teamStats **<-** outputAPI**$**teams
    }
  }

  *# Return teamStats.*
  **return**(teamStats)
}

nhlAPI

这个函数是上面所有其他函数的包装函数。您只需传递想要使用的函数名,比如"seasonStats",以及该函数的任何附加参数。

nhlAPI **<-** **function**(func, ...){
  *###*
  *# This function is a wrapper for the other functions. It takes in 
  # the name of the function to use as a character and any 
  # additional arguments for that function.*
  *###*

  *# Find and call the appropriate function using conditional logic.*
  **if** (func **==** "franchise"){
    output **<-** franchise(...)
  }
  **else** **if** (func **==** "teamTotals"){
    output **<-** teamTotals(...)
  }
  **else** **if** (func **==** "seasonRecords"){
    output **<-** seasonRecords(...)
  }
  **else** **if** (func **==** "goalieRecords"){
    output **<-** goalieRecords(...)
  }
  **else** **if** (func **==** "skaterRecords"){
    output **<-** skaterRecords(...)
  }
  **else** **if** (func **==** "franchiseDetail"){
    output **<-** franchiseDetail(...)
  }
  **else** **if** (func **==** "seasonStats"){
    output **<-** seasonStats(...)
  }
  **else** {
    stop("ERROR: Argument for func is not valid!")
  }

  *# Return the output from the appropriate function.*
  **return**(output)
}

数据探索

现在我们可以与 NHL API 的一些端点进行交互,让我们从它们那里获取一些数据。

首先,让我们通过调用nhlAPI("seasonStats")来获取所有球队的当前赛季统计数据。

*# Get the current season stats for all of the teams in the NHL.*
currentSeason **<-** nhlAPI("seasonStats")

我感兴趣的两个变量是场均投篮次数和投篮命中率。我对这两个数据与球队胜率的关系很感兴趣。这个变量不存在,我需要计算一下。我把这个定义为赢的次数除以比赛的总次数。

*# Add a column for the win percentage.*
currentSeason **<-** currentSeason **%>%**
  mutate(winPercentage **=** stat.wins **/** stat.gamesPlayed)

我的猜测是,这两者与胜率正相关。高投篮命中率意味着你很有可能在投篮时得分。每场比赛的高投篮次数可能意味着你比其他球队控制球更多,你有更多的得分机会,除非你投篮命中率高。这两个数据似乎都是进攻力量的伟大代表。

下面我绘制了每场比赛的胜率和投篮命中率。我还添加了一条回归线。正如所料,两者都与胜率正相关。

*# Create a scatter plot of win pct vs. shots per game.*
plot1 **<-** ggplot(currentSeason, aes(stat.shotsPerGame,
                                   winPercentage,
                                   color**=**winPercentage)) **+** 
  *# Add a scatter plot layer and adjust the size and opaqueness of 
  # points.*
  geom_point(size**=**4, alpha**=**0.75) **+** 
  *# Add a color gradient for winPercentage.*
  scale_color_gradient(low**=**"blue", high**=**"red") **+** 
  *# Remove the legend because it takes up space.*
  theme(legend.position**=**"none") **+** 
  *# Add a black regression line.*
  geom_smooth(method**=**lm, formula**=**y**~**x, color**=**"black") **+** 
  *# Add labels to the axes.*
  scale_x_continuous("Shots per Game") **+** 
  scale_y_continuous("Win Percentage") **+** 
  *# Add a title.*
  ggtitle("Win Pct. vs. Shots per Game")*# Create a scatter plot of win pct vs. shooting pct.*
plot2 **<-** ggplot(currentSeason, aes(stat.shootingPctg,
                                   winPercentage,
                                   color**=**winPercentage)) **+** 
  *# Add a scatter plot layer and adjust the size and opaqueness of 
  # points.*
  geom_point(size**=**4, alpha**=**0.75) **+** 
  *# Add a color gradient for winPercentage.*
  scale_color_gradient(low**=**"blue", high**=**"red") **+** 
  *# Remove the legend because it takes up space.*
  theme(legend.position**=**"none") **+** 
  *# Add a black regression line.*
  geom_smooth(method**=**lm, formula**=**y**~**x, color**=**"black") **+** 
  *# Add labels to the axes.*
  scale_x_continuous("Shooting Percentage") **+** 
  scale_y_continuous("Win Percentage") **+** 
  *# Add a title.*
  ggtitle("Win Pct. vs. Shooting Pct.")*# Plot them side-by-side.*
plot_grid(plot1, plot2, ncol**=**2)

作者图片

现在让我们看看投篮命中率和场均投篮次数。我为获胜百分比添加了一个颜色渐变。

*# Create a scatter plot of shooting pct vs. shots per game.*
plot3 **<-** ggplot(currentSeason, aes(stat.shotsPerGame,
                                   stat.shootingPctg,
                                   color**=**winPercentage)) **+** 
  *# Add a scatter plot layer and adjust the size and opaqueness of 
  # points.*
  geom_point(size**=**4, alpha**=**0.75) **+** 
  *# Add a color gradient for winPercentage with an improved label.*
  scale_color_gradient(low**=**"blue", high**=**"red", name**=**"Win Pct.") **+** 
  *# Add labels to the axes.*
  scale_x_continuous("Shots per Game") **+** 
  scale_y_continuous("Shooting Percentage") **+** 
  *# Add a title.*
  ggtitle("Shooting Pct. vs. Shots per Game") *# Show the plot.*
plot3

作者图片

这两个变量之间似乎没有明确的关系,而我认为可能有。让我们看看另一个端点的数据。

让我们来看看所有参加过 NHL 的球队的历史数据。请注意,其中一些球队属于同一支球队。我将属于同一支球队的球队视为独立的球队(例如科罗拉多洛矶队和新泽西魔鬼队)。我调用了nhlAPI("teamTotals")来获取这个数据。

*# Get some stats for the total history of a team.*
teamTotalStats **<-** nhlAPI("teamTotals")

首先,让我们看看非活动团队与活动团队的数量。

teamStatus **<-** teamTotalStats **%>%**
  *# Filter for regular season stats.*
  filter(gameTypeId **==** 2) **%>%**
  *# Create a column that tells whether a team is active or not.*
  mutate(currentlyActive **=** ifelse(**is.na**(lastSeasonId), 
                                  "Active", "Inactive")) **%>%**
  *# Select the teamName and activity status columns.*
  select(teamName, currentlyActive)*# Count the number of active and inactive teams.*
numActive **<-** **sum**(teamStatus **==**"Active")
numInactive **<-** **sum**(teamStatus **==**"Inactive")

有 26 个不活跃的团队和 31 个活跃的团队(只是提醒一下,我把转移到其他地方的团队视为不活跃的)。

不是所有的球队都有相同的任期,我想调整一些统计数据,以便能够在相同的基础上比较球队。我对各队的点球时间和记录特别感兴趣。

为了在相同的基础上得到数字,当比赛次数如此不同时,我计算每场比赛的罚分钟数,总罚分钟数除以总比赛次数。我再次计算获胜百分比,即获胜次数除以游戏次数。

我还对季后赛或常规赛如何影响罚球时间感兴趣,因为我认为季后赛的重要性可能会导致罚球行为的变化。

此外,我还创建了一个列,recordType,用于指示一个团队是否有输赢记录。如果赢的百分比大于 0.5,他们被分类为有赢的记录,否则为输的记录。

teamTotalStats **<-** teamTotalStats **%>%**
  *# Add columns for penalty minutes per game, win percentage, a text*
  *# representation of the game type, and whether a team has a losing 
  # or winning record for the game type.*
  mutate(penaltyMinutesPerGame **=** penaltyMinutes **/** gamesPlayed,
         winPercentage **=** wins **/** gamesPlayed,
         gameType **=** ifelse(gameTypeId **==** 2, "Regular Season", 
                           "Playoffs"),
         recordType **=** ifelse(wins **>** losses, "Winning Record", 
                             "Losing Record"))

下表显示了按游戏类型统计的有输赢记录的活跃队伍的数量。

*# Filter for active teams by looking for missing values in 
# lastSeasonId.*
activeTeams **<-** teamTotalStats **%>%**
  filter((**is.na**(lastSeasonId) **==** **TRUE**))*# Display a table of the game types by record types for active 
# teams.*
knitr**::**kable(table(activeTeams**$**gameType, activeTeams**$**recordType),
             caption**=**paste("Counts of Franchise Record Types by ", 
                           "Game Type for Active Teams"))

现役球队按游戏类型统计的特许经营记录类型(图片由作者提供)

下面是不活跃团队的相同表格。从这些表格中可以清楚地看出,不活跃的队伍比活跃的队伍要差得多。他们可能很难吸引观众和销售商品,因为他们太差了。这可能是它们不再存在的原因。

*# Filter for only inactive teams by looking for rows where 
# lastSeasonId is not missing.*
inactiveTeams **<-** teamTotalStats **%>%**
  filter((**is.na**(lastSeasonId) **==** **FALSE**))*# Count the number of inactive teams using the number of teams with 
# regular season games.*
numInactiveTeams **<-** **dim**(filter(inactiveTeams, gameTypeId **==** 2))[1]*# Count the number of inactive teams that made it to the playoffs, 
# which is not all of the inactive teams.*
numInactiveTeamsInPlayoffs **<-** **dim**(
    filter(inactiveTeams, gameTypeId **==** 3))[1]*# Count the number of inactive teams who did not make the playoffs.*
numDidntMakePlayoffs **<-** numInactiveTeams**-**numInactiveTeamsInPlayoffs*# Create an index for the last row in inactive teams.*
currentEndRow **<-** nrow(inactiveTeams)*# Add as many empty rows to inactiveTeams as teams not making the 
# playoffs.*
inactiveTeams[currentEndRow**+**seq(numDidntMakePlayoffs),] **<-** **NA***# Teams without playoff data do not have rows for that game type. 
# I'm going to add the proper number of losing records to the 
# dataframe to account for the missing rows for the playoffs.*
inactiveTeams[currentEndRow**+**seq(numDidntMakePlayoffs), 
              "recordType"] **<-** "Losing Record"*# To make the table work, I need to make the gameType of these rows 
# "Playoffs".*
inactiveTeams[currentEndRow**+**seq(numDidntMakePlayoffs), 
              "gameType"] **<-** "Playoffs"*# Display a table of the game types by record types for inactive 
# teams.*
knitr**::**kable(
    table(inactiveTeams**$**gameType, 
          inactiveTeams**$**recordType),
          caption**=**paste("Counts of Franchise Record Types by ", 
                        "Game Type for Inactive Teams"))

非活跃球队按游戏类型统计的特许经营记录类型(图片由作者提供)

现在,我将在数据集中保留非活动的和活动的团队。现在让我们根据游戏类型得到一个胜率的数字总结。

*# Create a table of summary stats for win percentage by game type.*
winPercSumm **<-** teamTotalStats **%>%** 
  *# Select the gameType and winPercentage columns.*
  select(gameType, winPercentage) **%>%**
  *# Group by game type.*
  group_by(gameType) **%>%**
  *# Get summary statistics for winPercentage.*
  summarize("Min." **=** **min**(winPercentage),
            "1st Quartile" **=** quantile(winPercentage, 0.25, 
                                      na.rm**=TRUE**),
            "Median" **=** quantile(winPercentage, 0.5, na.rm**=TRUE**),
            "Mean" **=** mean(winPercentage, na.rm**=TRUE**),
            "3rd Quartile" **=** quantile(winPercentage, 0.75, 
                                      na.rm**=TRUE**),
            "Max" **=** **max**(winPercentage),
            "Std. Dev." **=** sd(winPercentage, na.rm**=TRUE**)
            )*# Display a table of the summary stats.*
knitr**::**kable(winPercSumm, 
             caption**=paste(**"Summary Statistics for Win Percentage ", 
                           "by Game Type"),
             digits**=**2)

按游戏类型统计的获胜百分比摘要(按作者分类的图片)

根据统计数据,分布看起来没有太大的不同,尽管季后赛的胜率可能会更加多变。至少有一支球队从未赢得过季后赛。让我们用箱线图来形象化这些分布。

*# Make a box plot of franchise win percentage by game type.*
plot4 **<-** ggplot(teamTotalStats, 
               aes(gameType,
                   winPercentage,
                   color**=**gameType)) **+**
  *# Add the box plot layer.*
  geom_boxplot() **+** 
  *# Jitter the points to add a little more info to the boxplot.*
  geom_jitter() **+** 
  *# Add labels to the axes.*
  scale_x_discrete("Game Type") **+** 
  scale_y_continuous("Win Percentage") **+**
  *# Add a title.*
  ggtitle("Franchise Win Percentage by Game Type") **+** 
  *# Remove the legend because it isn't needed.*
  theme(legend.position**=**"none")*# Display the plot.*
plot4

作者图片

季后赛和常规赛的胜率差距肯定是存在的。即使他们的趋势接近,常规赛的胜率分布也更加紧密。这可能是因为常规赛比季后赛多。

现在让我们来看一个按比赛类型划分的每场比赛罚分钟数的数字总结。季后赛似乎有更高的中心倾向,在每场比赛的罚球时间上更易变。

*# Create a table of summary stats for penalty minutes per game by 
# game type.*
penMinSumm **<-** teamTotalStats **%>%** 
  *# Select the gameType and penaltyMinutesPerGame columns.*
  select(gameType, penaltyMinutesPerGame) **%>%**
  *# Group by game type.*
  group_by(gameType) **%>%**
  *# Get summary statistics for penaltyMinutesPerGame.*
  summarize("Min." **=** **min**(penaltyMinutesPerGame),
            "1st Quartile" **=** quantile(penaltyMinutesPerGame, 0.25),
            "Median" **=** quantile(penaltyMinutesPerGame, 0.5),
            "Mean" **=** mean(penaltyMinutesPerGame),
            "3rd Quartile" **=** quantile(penaltyMinutesPerGame, 0.75),
            "Max" **=** **max**(penaltyMinutesPerGame),
            "Std. Dev." **=** sd(penaltyMinutesPerGame)
            )*# Display a table of the summary stats.*
knitr**::**kable(penMinSumm, 
             caption**=**paste("Summary Statistics for Penalty ",   
                           "Minutes per Game by Game Type"),
             digits**=**2)

按比赛类型统计每场比赛的罚分时间(图片由作者提供)

下面的柱状图提供了一种替代箱线图的方法,用于可视化每场比赛的罚分钟数分布。很明显,每场季后赛的罚分钟数远远超过了常规赛。

*# Make a histogram of penalty minutes per game by game type.*
plot5 **<-** ggplot(teamTotalStats,aes(penaltyMinutesPerGame, 
                                   y**=**..density..,
                                   fill**=**gameType)) **+** 
  *# Add a semi-transparent histogram with 10 bins for regular season 
  # games.*
  geom_histogram(data**=**subset(teamTotalStats,
                             gameType **==** 'Regular Season'),
                 bins**=**10, alpha **=** 0.5) **+**
  *# Add a semi-transparent histogram with 10 bins for playoff games.*
  geom_histogram(data**=**subset(teamTotalStats,gameType **==** 'Playoffs'),
                 bins**=**10, alpha **=** 0.5) **+** 
  *# Add a better legend label.*
  guides(fill**=**guide_legend(title**=**"Game Type")) **+**
  *# Add labels to the axes.*
  scale_x_continuous("Penalty Minutes per Game") **+** 
  scale_y_continuous("Density") **+**
  *# Add a title.*
  ggtitle("Histogram of Penalty Minutes per Game by Game Type")*# Display the plot.*
plot5

作者图片

我很好奇哪支球队每场比赛在禁区里呆的时间最长。我筛选了现役球队和常规赛比赛,并为每支球队和他们每场比赛的罚球时间制作了一个条形图。我将条形图中的条形从表现最差到表现最好进行了排序。

*# Create a column with the triCode for each team ordered by most 
# penalty time per game to least.*
mostPenaltyMinsRegSeason **<-** teamTotalStats **%>%**
  *# Filter for active teams and regular season games.*
  filter(**is.na**(lastSeasonId) **&** (gameTypeId **==** 2)) **%>%**
  *# Sort from most penalty minutes per game to the least.*
  arrange(desc(penaltyMinutesPerGame)) **%>%**
  *# select the triCode column.*
  select(triCode)*# Create a bar chart for the penalty mins per regular season game by 
# active teams.*
plot6 **<-** teamTotalStats **%>%**
  *# Filter for active teams and their regular season stats.*
  filter(**is.na**(lastSeasonId) **&** (gameTypeId **==** 2)) **%>%**
  *# Create a column that is a sorted factor of triCode.*
  mutate(sortedTriCode **=** factor(
      triCode, levels**=**mostPenaltyMinsRegSeason[["triCode"]],
      ordered**=TRUE**))  **%>%**
  *# Create a bar chart with a fill gradient for 
  # penaltyMinutesPerGame.*
  ggplot(aes(sortedTriCode, penaltyMinutesPerGame, 
             fill**=**penaltyMinutesPerGame)) **+**
  geom_col() **+** 
  *# Rotate the x-axis labls 90 degrees and remove the legend.*
  theme(axis.text.x**=**element_text(angle**=**90), legend.position**=**"none")**+**
  *# Change the fill gradient to go from blue to red.*
  scale_fill_gradient(low**=**"blue", high**=**"red") **+**
  *# Set the axes labels.*
  scale_x_discrete("Team") **+** 
  scale_y_continuous("Penalty Minutes per Game") **+**
  *# Add a title.*
  ggtitle("Penalty Minutes per Regular Season Game by Team")*# Display the plot.*
plot6

作者图片

看起来费城飞人队和费城体育迷一样吵闹。我又制作了同样的柱状图,但这次是为了季后赛。我保留了常规赛最粗暴的顺序。如果常规赛和季后赛的点球有相关性,我们预计顺序不会有太大变化。

*# Create a bar chart for the penalty mins per playoff game by active* 
*# teams.*
plot7 **<-** teamTotalStats **%>%**
  *# Filter for active teams and their playoff stats.*
  filter(**is.na**(lastSeasonId) **&** (gameTypeId **==** 3)) **%>%**
  *# Create a column that is a sorted factor of triCode.*
  mutate(sortedTriCode **=** factor(
     triCode,
     levels**=**mostPenaltyMinsRegSeason[["triCode"]], ordered**=TRUE**))**%>%**
  *# Create a bar chart with a fill gradient for 
  # penaltyMinutesPerGame.*
  ggplot(aes(sortedTriCode, penaltyMinutesPerGame, 
             fill**=**penaltyMinutesPerGame)) **+**
  geom_col() **+** 
  *# Rotate the x-axis labls 90 degrees and remove the legend.*
  theme(axis.text.x**=**element_text(angle**=**90), legend.position**=**"none")**+**
  *# Change the fill gradient to go from blue to red.*
  scale_fill_gradient(low**=**"blue", high**=**"red") **+**
  *# Set the axes labels.*
  scale_x_discrete("Team") **+** 
  scale_y_continuous("Penalty Minutes per Game") **+**
  *# Add a title.*
  ggtitle("Penalty Minutes per Playoff Game by Team")*# Display the plot.*
plot7

作者图片

顺序变化不大,所以常规赛点球时间和季后赛点球时间是有关联的。让我们来看看季后赛和常规赛每场比赛的罚球时间的散点图。

*# Create a scatter plot of playoff penalty time per game vs. regular 
# season.*
plot8 **<-** teamTotalStats **%>%**
  *# Filter for active teams.*
  filter(**is.na**(lastSeasonId)) **%>%**
  *# Select triCode, gameType, and penaltyMinutesPer Game.*
  select(triCode, gameType, penaltyMinutesPerGame) **%>%**
  *# Spread penaltyMinutesPerGame by gameType.*
  spread(gameType, penaltyMinutesPerGame) **%>%**
  *# Create a scatter plot with a regression line.*
  ggplot(aes(`Regular Season`, Playoffs)) **+**
  *# Add a scatter plot layer and adjust the size and opaqueness of 
  # points.*
  geom_point(alpha**=**0.75,  color**=**"blue") **+** 
  *# Add a red regression line.*
  geom_smooth(method**=**lm, formula**=**y**~**x, color**=**"red") **+**
  *# Set the axes labels.*
  scale_x_continuous("Regular Season Penalty Min. per Game") **+**
  scale_y_continuous("Playoffs Penalty Min. per Game") **+** 
  *# Add a title.*
  ggtitle(paste("Playoff vs. Regular Season Penalty Min. Per ",
                "Game (Active Teams)"))*# Display the plot.*
plot8

作者图片

虽然正相关并不十分令人惊讶,但我没想到相关性会如此紧密。

我想知道每场比赛的罚分时间和胜率有什么关系。让我们来了解一下!

我创建了一个散点图,根据游戏类型来显示每场比赛的胜率和罚分。我尝试了一下回归线。我首先从一条黄土回归线开始,看到了一个倒置的 U 型线。我决定用一条二次回归线来绘制它们,以使事情更顺利。

plot9 **<-** teamTotalStats **%>%**
  *# Select the triCode, gameType, penaltyMinutesPerGame, and 
  # winPercentage columns.*
  select(triCode, gameType, penaltyMinutesPerGame, winPercentage)**%>%**
  *# Create a scatter plot of winPercentage vs. 
  # penaltyMinutesPerGame, coloring by game type.*
  ggplot(aes(penaltyMinutesPerGame, winPercentage, color**=**gameType))**+**
  *# Add a scatter plot layer and adjust the size.*
  geom_point(size**=**2) **+** 
  *# Add a quadratic regression line.*
  geom_smooth(method**=**"lm", formula**=**"y~poly(x, 2)") **+** 
  *# Set the axes labels.*
  scale_x_continuous("Penalty Minutes per Game") **+** 
  scale_y_continuous("Win Percentage") **+** 
  *# The legend isn't needed, so remove it.*
  theme(legend.position**=**"none") **+** 
  *# Add a title*
  ggtitle(paste("Win Percentage vs. Penalty Minutes per Game by ",   
                "Game Type")) **+** 
  *# Break out the plots by game type.*
  facet_wrap(**~**gameType)*# Display the plot.*
plot9

作者图片

正如你所看到的,每场比赛的胜率和罚分钟数之间的关系并不完全清晰,但似乎确实存在二次关系。在常规赛中,就胜率而言,15 分钟似乎是最佳的罚球时间。对于季后赛,它似乎在 17 分钟左右。

在这一点上,相关性并不意味着因果关系是不言而喻的。有可能存在一种罚点球的策略,即使这给了对方一个强力进攻的机会,就像篮球一样。或者它可能只是一个最佳侵略性游戏风格的副产品。不管怎样,我想测试这种关系是否有统计学意义。让我们用回归更正式地测试一下二次关系。

*# Create a model regressing win percentage on penalty minutes per 
# game.*
winPercMod **<-** lm(winPercentage **~** poly(penaltyMinutesPerGame, 2),
                 data**=**teamTotalStats)*# Get the percentage of variance explained.*
winVarExplainedPerc **<-** **round**(100*****summary(winPercMod)[[8]],1)*# Create a table of the regression coefficients.*
tidywinPercModSumm **<-** winPercMod **%>%**
  *# Pass the model through the tidy() function.*
  tidy()*# Rename the variables for improved printing.*
tidywinPercModSumm[1, "term"] **=** "Intercept"
tidywinPercModSumm[2, "term"] **=** "Penalty Min. per Game"
tidywinPercModSumm[3, "term"] **=** "(Penalty Min. per Game)^2"*# Pass the tidied model output to a table and format it.*
knitr**::**kable(
  tidywinPercModSumm,
  caption**=**paste("Coefficient summary of Win Perc. Regressed on",
                "Penalty Min. per Game with Quadratic Term"),
  col.names **=** **c**("Variable", "Est. Coef.", "SE", "t", "P(|t| > 0)"),
  digits**=c**(0, 2, 3, 2, 3)
)

Win Perc 系数汇总。在罚分上倒退。每场比赛有二次项(图片由作者提供)

给定回归中二次项的 t 统计量,二次关系似乎是合理的。该模型解释了成功百分比中 23.8%的差异,因此在预测模型中仍有相当数量的差异需要考虑。尽管如此,这仍然是一个有趣的发现!

总结

为了总结我在这篇短文中所做的一切,我构建了与 NHL API 的一些端点进行交互的函数,检索了一些数据,并使用表格、数字摘要和数据可视化对其进行了研究。我发现了一些不足为奇的事情,比如场均投篮次数和投篮命中率与胜率相关。我还发现了一些令人惊讶的事情,即每场比赛的罚球时间与胜率成二次关系。

最重要的是,我希望我的代码有助于您与 API 进行交互!

NHL-API-vignetteJK clem维护。

附加 CSV 文件

原文:https://towardsdatascience.com/appending-csv-files-d31a08153e0c?source=collection_archive---------28-----------------------

使用 Power BI、R、Power Shell 或 CMD 完成此操作

丹尼斯·强森在 Unsplash 拍摄的照片

你曾经不得不合并多个 CSV 文件吗?我相信这是每个数据分析师或数据科学家在某个时候必须完成的任务,而且确实是一项奇怪的任务。他们当初为什么要分开?连接到源不是更好吗?有时候最好的解决方案并不总是可行的。

如果你发现自己在打开 Excel、复制文件和处理 10 个不同的工作簿,那么这篇文章就是为你准备的。我将向您展示附加文件的不同方式。你不仅可以节省时间,还可以避免犯错误。相信我,在我发现权力 BI 和 r 之前,我曾经这样做过。

如果你不必这样做,并且对 Power BI 和 R 非常熟悉,那么我相信你会发现一些有命令行和 Power Shell 的有趣脚本。

让我们开始吧。

我这里有 6 个图像文件,每个文件有 29 列。

图片来自作者

这些都位于一个文件夹中。我试图得到的最终结果是一个更大的文件,将所有 6 个合并成一个。这里的数字无关紧要,可以是 6,也可以是 100。流程还是一样的:)

我们为什么要合并这些?原因各不相同。有时使用大表进行简单分析会更容易。有时可能是因为数据类型问题,文件无法加载。在任何情况下,都有不同的工具来附加这些文件。

Power BI — Power 查询文件夹组合

作者图片

有了 Power BI,您只需进入 Power BI,使用“连接到文件夹”即可连接所有内容。

给你 Power Query 中的所有文件供你组合和加载。

作者图片

有时 Power BI 可能会发现数据错误,无法加载这些文件。这里有一篇文章讲述了一些错误以及如何修复它们。

由于列错误,Power BI 将无法加载所有这些文件。有时一个单元格可能有字母,但 Power BI 将该列归类为数字。这将导致错误。

作为一个快速提示,在加载文件时删除“更改类型”步骤。它造成的错误似乎多于它的用处。如果我去掉这一步,您可以看到 Power BI 能够加载所有内容。

PRO :不需要代码,只需要点击。简单的导航和 GUI。允许您使用表格函数和自定义函数通过表格和函数进行其他类型的分析。

缺点:速度慢,可能无法工作,如果有数据类型错误,需要排除故障。如果您有大量的文件,并且在等待之后发现一个类型错误,您会感到沮丧。

R- VROOM 联合收割机

作者图片

在这里,我可以使用包调用 VROOM,获取文件夹中的所有文件名,并将其全部合并。

在 R Studio 中键入

library(vroom)setwd("Your folder directory")Files = dir_ls(glob = "*.csv")Files_Combined = vroom(Files)

这里的脚本说要设置你的工作目录,使用 dir_ls ,过滤任何以“.”结尾的内容。csv "并将其保存到一个变量调用文件中。然后 vroom() 读取“文件”并赋给结束变量调用 Files_Combined。

这里——它对我不太适用,因为其中一个文件有 150 列。

作者图片

要纠正这一点,您可以使用地图。如果您不熟悉 R,map 会对所提供的变量应用一个函数。在这种情况下,它是文件—包含我们想要加载的 CSV 文件的名称。这里的函数是 vroom() ,这是加载函数。

library(vroom)setwd("Your folder directory")Files = dir_ls(glob = "*csv")Files_Combined = map(Files, ~vroom(.x))

这里所有的文件都将被加载,但是是列表格式的。然后,您可以通过子设置选择您想要的文件。您可以使用 Files_Combined[1:3]或使用名称 Files _ Combined[" your _ file _ name "]来引用实际文件本身。

:非常快!也真的很喜欢它的声音:)能够在 5 秒钟内加载列表格式的所有文件。

CON :假设您有相同数量的列标题。在运行 vroom 之前,我必须做一些调整,看看哪个文件有额外的列。如果数据框中的一列为数字,另一列为字符,则无法绑定行。

PowerShell —获取内容|添加内容

作者图片

下面是将这些文件合并成一个 CSV 文档的脚本。你可以在启动菜单中输入“PowerShell ISE ”, PowerShell 就会被加载。

会出现一个蓝屏——别担心这不是死亡蓝屏。:)

你可以在这里输入。

cd "Your folder directory"Get-ChildItem -Filter *csv | Get-Content | Add-Content "Combined.csv"

Get-child item将列出该文件夹中的所有文件。如果你愿意,你甚至可以使用 -Recurse ,它会列出相关子文件夹中的所有文件。 Get-Content 取每个 csv 的内容,Add-Content将内容添加到一个名为" combined.csv "的新文件中。

俏皮:)

作者图片

***亲:*可靠。不会引发类型错误。它只是做你让它做的事。

反对意见:很好,没问题。很高兴看到它没有抛出任何错误!可能是一件好事也可能是一件坏事,额外的列没有被追加到新文件中。

命令行—复制和粘贴

作者图片

这个是我最喜欢的。只需在启动菜单中输入 CMD,就会出现一个黑屏。别担心,这只是命令提示符。

*cd "your folder directory"copy *csv combined.csv*

这一行简单的代码会将您创建的文件夹中的所有 CSV 文件复制到一个名为“combined.csv”的新文件中。

这里说的是复制所有以“csv”结尾的内容,并将它们放入一个名为“combined.csv”的文档中。

最棒的是,你可以在组合之前添加 grep 来过滤你想要的值。

*grep "your_item" *csv >> item.csv*

在这里,您从该文件夹中的所有 CSV 文件中“grep”您的项目,并将其导出到一个名为“item.csv”的新文件中

我确实喜欢这个:)

想象一下,你必须在 200 个文件中搜索一个项目,比如说一个账号?有 CMD 的 grep 是你的朋友。

***亲:*非常快,没有问题。3 秒内复制了所有内容。

***缺点:*如果你不知道你在读什么,那就有点神秘了。

结论

TL:DR??—我不是在评判。

作者图片

有不同的工具可用于附加 CSV。

最头痛的事情,至少对我来说,是处理类型问题。PowerShell 和 CMD 都可以跳过所有这些,为您提供一个分析表。

如果你只是想添加文件,做一些过滤和处理一个合并的文档,使用 PowerShell 或 CMD。之后,您可以使用 Power BI 或 R 进行分析,而不用担心数据格式问题。我还没有尝试过 CSVKIT,但当我这样做时,我一定会让你知道。

曾经有人问我:“你为什么要费心写作?反正你一篇文章挣不到 5 美元”。这是个很好的问题。事实是,我从别人那里学到了很多。我认为分享知识很重要。没有人能在没有他人帮助的情况下取得今天的成就。我们都相互依赖,不管是直接的还是间接的。

谢谢!祝您的数据之旅安全顺利:)

苹果的 M1 Pro 和 M1 Max 超过谷歌 Colab 高达 54%

原文:https://towardsdatascience.com/apples-m1-pro-and-m1-max-outperform-google-colab-by-up-to-54-cbf13c8b6357?source=collection_archive---------1-----------------------

2021 苹果 M1 Pro 和 M1 Max 机器学习速度测试对比

标志性的绿色充电灯又回来了。作者照片。

我使用的主键盘连接在 MacBook Pro 上。我在 MacBook Pro 上打这个。

作为一个技术呆子,当苹果发布了几款升级了硬件的新 MacBook Pro:M1 Pro、M1 Max 芯片和重新设计等等,我决定,我最好测试一下。

对于上下文,我制作关于机器学习的视频,编写机器学习代码,教授机器学习。

这就是本文的重点。

将苹果的 M1、M1 Pro 和 M1 Max 芯片与其他几款芯片进行对比。

怎么会?

四项测试:

  1. Final Cut Pro 导出 —各种 MacBook Pro 导出 4 小时长的 TensorFlow 教学视频(我制作编码教育视频)和 10 分钟长的故事视频(使用 H.264 和 ProRes 编码)的速度有多快?
  2. CreateML 图像分类机器学习模型创建 —各种 MacBook Pro 用 CreateML 把一万张图像变成一个图像分类模型能有多快?
  3. CIFAR10 TinyVGG 图像分类与 tensor flow(via**tensorflow-macos**)——多亏了tensorflow-metal,你现在可以利用你的 MacBook 的内部 GPU 来加速机器学习模型训练。这个和小一点的模型配起来怎么样?
  4. Food101 EfficientNetB0 用 **tensorflow-macos**进行特征提取——我很少从零开始训练机器学习模型。那么,新的 M1 Pro 和 M1 Max 芯片如何利用 TensorFlow 代码进行迁移学习呢?

本文严格关注性能。对于设计、输入、输出、电池寿命,还有很多其他资源。

硬件规格

我目前使用基于英特尔的 16 英寸 MacBook Pro 作为我的主机(几乎总是插着电源),2020 年的 13 英寸 M1 MacBook Pro 作为随身携带的选项。

为了训练更大的机器学习模型,我使用谷歌 Colab、谷歌云 GPU 或 SSH(通过互联网连接)连接到一台配备泰坦 RTX GPU 的专用深度学习 PC。

对于 TensorFlow 代码测试,我包括了与 Google Colab 和泰坦 RTX GPU 的比较。

这里的规格主要是基于英特尔的 MacBook Pro、M1、M1 Pro 和 M1 Max。

测试中 Mac 机型的不同硬件规格。

在每项测试中,所有的 MacBook Pro 都运行 macOS Monterey 12.0.1,并接通了电源。

实验 1: Final Cut Pro 导出(小视频和大视频)

我制作 YouTube 视频和教授机器学习的教育视频。

所以我用的机器必须能快速渲染和导出。这是我购买 2019 年 16 英寸 MacBook Pro 规格的主要原因之一,这样我就可以毫无延迟地编辑视频。

此外,M1 专业和 M1 最大芯片是针对专业人士。他们中的许多人编辑视频的质量比我高得多(目前)。

对于每个视频,我都将其导出为 H.264 编码(更高的压缩率,更多的 GPU 密集型)和 ProRes 编码(更低的压缩率,更少的 CPU 和 GPU 密集型)。

这是因为苹果公司声明,新的 M1 芯片有专门的 ProRes 引擎。

大型视频

实验详情:

  • 视频: 学习 TensorFlow 进行深度学习第二部分
  • 时长: 4 小时
  • **风格:**拼接在一起的 30 倍屏幕录像短片(每部约 10 分钟)
  • 质量: 1080p

有趣的是,当使用 H.264 编码时,新的 M1s (Pro 和 Max)占用的时间是基本 M1 的两倍,是基于英特尔的 Mac 的近四倍。

还应该注意的是,在使用 H.264 编码时,M1 Pro(约 89%空闲)和 M1 Max(约 68%空闲)上都有大量硬件处于空闲状态。

在导出到 H.264 编码时,M1 Pro 和 M1 Max 上的大量硬件被闲置。我不确定这是什么原因造成的。但这确实解释了为什么与其他 MAC 相比,它们的出口时间如此之长。

可能需要更新软件才能充分利用新芯片的全部功能?

不过,对于 ProRes 编码来说,情况就不同了。

ProRes 编码是较新的 M1 芯片真正闪光的地方。

与 H.264 编码相比,在 M1 Pro 和 M1 Max 上导出到 ProRes 时,会占用大量 CPU 资源。或许这是来自专用的 ProRes 内核?

导出到 ProRes 编码时,在 M1 Max 和 M1 Pro 上可以看到大量的 CPU 使用。**注:**所有屏幕记录均与结果跟踪分开进行。

然而,尽管 ProRes 编码导出比 H.264 快得多,但文件大小的差异使它对许多人来说几乎不可用。

将大视频导出到 H.264 时,文件大小为 7GB,导出到 ProRes 时,文件大小为 167GB。

我不知道你的网速,但这种视频需要三四天才能上传给我。

小视频

较小的视频产生了非常接近的结果。

实验详情:

  • 视频: 我如何学习机器学习每周 5 天
  • 长度: 10 分钟
  • **风格:**画外音,视频剪辑编辑成语音
  • 质量: 1080p

所有的出口时间都非常相似。

但同样,当导出到 H.264 编码时,M1 Pro 和 M1 Max 芯片上有大量硬件闲置。

当使用 ProRes 编码时,M1 Pro 和 M1 Max 再次大放异彩。检查活动监视器显示了大量的 CPU 使用率,M1 Pro 的使用率为 350-450%,M1 Max 的使用率为 300-500%。

实验 2: CreateML 图像分类

CreateML 是苹果公司制作的机器学习 app,自带 Xcode(苹果公司制作 iOS 和 macOS 应用的软件)。

它提供了一种将数据转化为机器学习模型的简单方法。

我之所以尝试它,是因为我和我哥哥一直在用它制作 Nutrify (一款拍摄食物照片并了解食物的应用程序)上的原型模型。

它不仅适用于我们的用例,还能生成针对苹果设备优化的训练模型。

实验详情:

  • **数据:**所有 Food101 类的随机 10%子集(~7.5k 训练图像,~2.5k 测试图像)
  • 训练: 25 个历元,所有数据扩充设置开启
  • 模型: CreateML powered 模型(苹果没有告诉你用的是什么架构)

再一次,M1 Pro 和 M1 Max 之间没有太大的区别。尽管它们都比其他芯片表现出色。

在培训期间,检查 M1 专业版和 M1 最大版的活动监视器显示,在进程名称“MLRecipeExcecutionService”下有大量的 CPU 使用。

在 CreateML 应用程序中训练模型的过程中,CPU 使用率飙升至 500%以上。GPU 根本用不上多少。也许这是 16 核神经引擎在起作用?截图摄于 M1 专业版。

值得注意的是,在训练或特征提取期间,GPU 根本没有得到多少使用。

这让我相信 CreateML 使用 16 核神经引擎来加速训练。然而,这并没有得到 100%的证实,因为活动监视器并没有透露神经引擎何时启动。

现在 CreateML 也没有透露使用什么样的模型。从性能来看,我猜它至少会使用预训练的 ResNet50 型号或 EfficientNetB2 以上或类似型号。

实验三:带 TensorFlow 代码的 CIFAR10 TinyVGG 模型

CreateML 工作起来很棒,但有时你会想要制作自己的机器学习模型。

为此,您可能最终会使用 TensorFlow 这样的框架。

我教 TensorFlow 几乎每天都要编码。所以我很期待看到新机器在这里的表现。

对于所有自定义 TensorFlow 测试,所有机器都使用相同的数据集和相同的环境设置运行相同的代码。

唯一的区别是谷歌 Colab 和英伟达巨头 RTX 与每台 MAC 电脑之间的区别。

每台 Mac 都运行了[tensorflow-macos](https://developer.apple.com/metal/tensorflow-plugin/)(Mac 的 TensorFlow)和 [tensorflow-metal](https://developer.apple.com/metal/tensorflow-plugin/) 的组合,用于 GPU 加速。而 Google Colab 和 Nvidia TITAN RTX 使用的是标准 TensorFlow。

你可以在 GitHub 上看到所有实验和 TensorFlow 在 Mac 上设置的代码。

**注意:**我目前不知道有任何 PyTorch 等同于tensorflow-metal来加速 Mac GPUs 上的 PyTorch 代码。目前,PyTorch 只能在 Mac CPU 上运行。

首次 TensorFlow 实验详情:

  • **数据:**来自 TensorFlow 数据集的 cifar 10(32x 32 幅图像,10 个类别,50,000 次训练,10,000 次测试)
  • 型号: TinyVGG(来自 CNN Explainer 网站)
  • 训练: 10 个时期,批量 32
  • 代码: 在 GitHub 上看到

*对于 TensorFlow 实验,我使用谷歌 Colab 的免费版本,这是谷歌提供的一个非常棒的产品,可以使用连接到 GPU 的 Jupyter 笔记本,只需很少的设置。免费版本曾经提供更快的图形处理器(英伟达 P100,T4),然而,自从我获得其中一个已经有一段时间了。你可以通过 Colab Pro 获得更快的 GPU,但在我居住的地方(澳大利亚)还没有。

泰坦 RTX 胜过其他机器是有道理的。这是一个专门为机器学习和数据科学而构建的 GPU。

就每个时期的时间而言,所有 MAC 的性能或多或少在相同的范围内。M1 马克斯和平原 M1 机器运行代码在类似的时间。

使用较小的数据集(CIFAR10,32x32 图像大小)和使用 TensorFlow 代码制作的模型架构(TinyVGG)测试的不同设备的比较。

检查每台 M1 MAC 电脑的活动监视器发现,在培训期间使用了大量的 GPU。这要归功于苹果的[tensorflow-metal](https://developer.apple.com/metal/tensorflow-plugin/) PluggableDevice ,一个利用苹果金属 GPU 框架加速 TensorFlow 的软件包。

多亏了tensorflow-metal,在所有的 MAC 上,训练期间活动监视器中显示了大量的 GPU 使用情况。截图来自 M1 专业版。

实验 4:使用 TensorFlow 代码进行 Food101 EfficientNetB0 特征提取

最后的机器学习实验规模更大了。使用比实验 3 更多更大的图像。

当训练机器学习模型时,获得奇妙结果的最好方法之一是使用迁移学习。

迁移学习利用一个模型在类似数据集上学习到的权重(模式),并允许您将它们用于自己的数据集。

对于实验 4,我在 Food101 数据集上使用了 EfficientNetB0 模型的迁移学习。

实验详情:

  • 数据: Food101 来自 TensorFlow 数据集(224x224 幅图像,101 个类别,~75k 训练,~25k 测试)
  • 型号: EfficientNetB0 在 ImageNet 上预训练,顶层被替换(特征提取)
  • 训练: 5 个时期,批量 32
  • 代码: 在 GitHub 上看到

*在 16 英寸 MacBook Pro 上运行的代码使用了 SGD 优化器,而不是 Adam 优化器,因为在基于英特尔的 Mac 上运行的 *tensorflow-macos* 有一个未解决的问题,我找不到修复方法。

事实证明,对于更大规模的模型和数据集,更新的 M1 Pro 和 M1 Max 芯片比谷歌 Colab 的免费产品(K80 GPU)更快。M1 Max 甚至与泰坦 RTX 相差不远。

对我来说,最突出的是 M1 Pro 和 M1 Max 在放大到更大的实验时的性能。

实验 3 在所有 MAC 上看到了类似的结果,但当数据量增加时(图像大小和图像数量),M1 Pro 和 M1 Max 远远领先于其他 MAC。

M1 Pro 和 M1 Max 甚至超过了配备专用 Nvidia GPU 的谷歌 Colab(M1 Pro 快约 1.5 倍,M1 Max 快约 2 倍)。

这意味着你可以在本地机器上进行机器学习实验,比用在线的 Colab 笔记本更快。给你在本地跑步的所有好处。不过,Google Colab 确实给了你通过链接分享笔记本的神奇能力。

当然,泰坦 RTX 表现最好,但 M1 Max 也不甘落后,这对于便携式设备来说是非常令人印象深刻的。

活动监视器显示所有设备都使用了大量 GPU。

多亏了tensorflow-metal,所有 MAC 的 GPU 使用率在培训期间都很高。不过,看到神经引擎在这里发挥作用会很酷。CreateML 上的训练非常快。我想知道这是否可以用于定制代码。截图来自 M1 麦克斯。

应该买哪个?

在过去的一年里,我每天都在使用一台 13 英寸的 M1 MacBook Pro。我将它用于小规模的机器学习实验、视频编辑和谷歌 Colab 浏览器。

然而,我现在正在考虑用 M1 Pro 升级到 14 英寸的 MacBook Pro,并在需要时在泰坦 RTX 或云上升级之前在本地做一切事情(不再有谷歌 Colab)。

从测试来看,很明显 13 英寸的 M1 MacBook Pro(尽管这里没有测试,M1 MacBook Air 的性能应该仍然接近 M1 MacBook Pro 的结果)仍然是一款不可思议的笔记本电脑。

虽然如果你有预算,M1 专业版会给你一个明显的性能提升。

至于 M1 Max,我的测试很难证明它和 M1 Pro 之间 1100 美元的价格差距。对我来说,我宁愿把钱花在额外的本地存储、RAM 或专用 GPU 上。

简而言之,这些是我的建议:

  • 13 英寸的 M1 MacBook Pro/MacBook Air——对于初学机器学习和数据科学的人来说,这仍然是一款非常棒的笔记本电脑。
  • 14 英寸 M1 专业版 MacBook Pro —来自 M1 的显著性能提升,如果你喜欢新设计或有预算,这是值得的。
  • 14 英寸 M1 Max MacBook Pro——M1 Pro 的显著性能提升只有在大规模模型训练中,许多其他性能基准达到了与 M1 Pro 相同的水平。如果您发现自己经常编辑多个 4K 视频流,这可能是一个选项。

结论

我想看看苹果的专用芯片在什么地方,什么时候被使用。

例如,当神经引擎开始发挥作用时,看起来会很酷。比如在使用 CreateML 时(所有 M1 机器都在这里快速通过训练,就像没有明天一样)。

或者更好的是,如何编写代码来利用神经引擎本身?

去年,我说16 英寸 MacBook Pro 配 M2 怎么样,苹果送了一台 M1 Max。或许 M2 仍在以 Mac mini 或新款 iMac 的形式出现。

如果有什么是明确的,专用于某些目的的芯片似乎是趋势。性能优势不言自明。

资源

  • 在 GitHub 上查看所有用于测试和评估的代码。
  • 看我关于如何为机器学习和数据科学设置你的新 M1 机器的视频。
  • 在 YouTube 上观看这篇博文的视频版本。

苹果的 neural hash——它是如何工作的,以及它可能如何受到损害

原文:https://towardsdatascience.com/apples-neuralhash-how-it-works-and-ways-to-break-it-577d1edc9838?source=collection_archive---------5-----------------------

人工智能校准和安全

技术指南、其漏洞和可能的缓解措施

苹果公司最近宣布了各种措施来打击儿童性虐待材料的传播(CSAM)。

接下来,iOS 和 iPadOS 将使用加密技术的新应用来帮助限制 CSAM 病毒在网上的传播,同时为用户隐私进行设计。CSAM 检测将帮助苹果公司向执法部门提供关于 iCloud 照片中 CSAM 收藏的有价值的信息。
—苹果声明宣布这些措施

其中包括使用一种被称为 NeuralHash 的新技术扫描 CSAM 的 iCloud 照片。

作者图片,灵感来自苹果关于 CSAM 探测的技术总结。这里列出的散列并不是真正的 CSAM 散列。这不包括细节,如致盲步骤。有关详细的图表,请参考技术总结。

苹果在扫描我的照片?!?

“扫描”可能是一个错误的词。实际上,苹果打算在不看你照片的情况下检查 CSAM。

照片由 Immo Wegmann 在 Unsplash 上拍摄

考虑一下指纹的概念——每个人都有其独特之处。它识别一个人,但它本身并不能告诉你这个人的任何信息。在计算机科学中,一个散列函数帮助我们计算一个散列,它的作用就像一个文件的指纹。散列对于文件来说(有点)是唯一的,但是它本身并不能告诉我们关于文件内容的任何事情。

如果两个文件产生相同的散列,它们很可能是同一个文件。就像如果两组指纹匹配,它们很可能来自同一个人。

苹果打算存储一个与已知 CSAM 相对应的哈希数据库。然后,通过散列你所有的 iCloud 照片,并将你的散列与 CSAM 散列数据库进行比较,苹果可以在不看你的 iCloud 照片的情况下识别匹配。

但是说起来容易做起来难。传统的哈希函数(MD5、SHA256 等。)对文件中的变化非常敏感。例如,这两个图像具有完全不同的 MD5 散列,尽管它们看起来相同(右边的一个在宽度上短了 2 个像素)。

(原 Doge meme ,MD5:53 facff 91 EC 83 f 60 a 88235 ab 628590 bb |()作者裁剪的图片,MD5:da 25273 f 33 C4 EC 95 f 71984075079 BD 16

对变化超级敏感通常是哈希函数的一个有用特性,因为这使得判断文件是否被篡改变得非常容易。

但是如果我们用它来探测 CSAM,这就成了一个巨大的限制。想象一下,如果上面左边的图像是被禁止的图像。苹果可能会存储 MD5 哈希 53f…0bb 。但是仅仅通过稍微裁剪图像,我们仍然得到看起来相同的图像(在右边),但是具有完全不同的 MD5 散列值 da2…d16 ,这将会逃避检测。

更普遍的情况是,即使苹果有一个已知 CSAM 的数据库,CSAM 经销商也可以旋转、裁剪或调整图像大小来逃避检测。苹果将不得不存储无限数量的散列,以考虑旋转、裁剪或调整图像大小的无限方式。想象一下,当人们减肥、理发或剪指甲时,他们的指纹会发生变化吗?

为了进行检测,即使图像被轻微修改,哈希也必须相同。介绍… 神经哈希

好吧,跟我说说这个神经哈希

NeuralHash 是一种哈希算法,对输入图像的微小变化不敏感。

(原 Doge meme ,neural hash:11 d9 b 097 AC 960 BD 2c 6c 131 fa |()作者裁剪的图片,neural hash:11 d9 b 097 AC 960 BD 2c 6c 131 fa

对于之前由于不明显的作物而具有非常不同的 MD5 散列的同一对图像,我们得到相同的神经散列。
(本文中所有的 NeuralHashes 都是用这个浏览器内演示计算出来的。)

(原 Doge meme ,neural hash:11 d9 b 097 AC 960 BD 2c 6c 131 fa |()图片由作者翻转,neural hash:20 D8 f 097 AC 960 ad 2c 7c 231 Fe

即使在图像被翻转的情况下,神经哈希的重要部分仍然相同:
左神经哈希:11d9b097 AC 960bd2c6c131fa
右神经哈希:20d8f097 AC 960

(原 Doge meme ,neural hash:11 d9 b 097 AC 960 BD 2c 6c 131 fa |()图片由作者编辑,neural hash:11 d9 B0 b7a 8120 BD 286 C1 B1 Fe

下面是另一个例子,我们将一些单词叠加到图像上,但得到的 NeuralHash 仍然是相似的:
左 neural hash:11d9b09
7ac960bd 2c6 C131fa
右 neural hash:11d9b0b
7a812

在不深入细节的情况下,NeuralHash 算法使用卷积神经网络(CNN)来计算哈希。在训练过程中,CNN 会看到成对的图像。正对由彼此简单变换(旋转、裁剪、调整大小)的图像组成,例如上面的图像对。负像对包含完全不同的图像。CNN 被训练成将正对映射到相同的散列,将负对映射到不同的散列。这样,它学会忽略应用于图像的小变换。

而你说可以破?

大多数人工智能和人工智能的研究人员和学生可能听说过对抗性攻击,其中神经网络通过向图像添加噪声来欺骗。

作者展示对抗性攻击如何欺骗人工智能模型的图片,其灵感来自图 1 中的典型熊猫示例,解释并利用了Goodfellow 等人的对抗性示例,2014 。

例如,神经网络最初可能会将我们的 Doge 图像标记为“dingo”。但是我们可以在同一张照片上添加少量的噪声,现在神经网络将这张照片标记为“暹罗猫”。

这些攻击在人工智能社区中已经广为人知好几年了,研究人员之间一直在开发对抗性攻击和防御。

在 NeuralHash 算法中,同样的攻击可以很容易地应用于 CNN。8 月中旬,Reddit 用户 AsuharietYgvar 发布了关于如何导出苹果 NeuralHash 模型副本的 指令。几个 脚本 出现演示成功的攻击仅仅几个小时后。

对 NeuralHash 模型的对抗性攻击有两种形式。第一种类型的攻击——让我们称之为“不同的图像,相同的哈希”——将噪声添加到源图像,以便得到的神经哈希与目标图像相同。

一个哈希碰撞对的例子,由作者从 Doge ( )和 Grumpy Cat ( )模因中生成。两张图像都有相同的 neural hash 11 d9b 097 AC 960 BD 2c 6c 131 fa,在给暴躁猫图像添加噪声之后。

在上面的例子中,噪声被添加到 Grump 猫图像中,因此结果具有与 Doge 图像完全相同的 NeuralHash。这也被称为哈希冲突——从不同的图像中计算出相同的哈希。这是有问题的,因为这意味着有人可以在无害的图像中添加噪声,使它们对应于 CSAM 神经哈希,然后伪装成常规图像,在随机网站上分发它们。这些伪造的 CSAM 图像会在苹果的服务器上引起假警报,导致人们被错误地标记。同样的攻击也可以用来将 CSAM 伪装成普通的图像。

另一种攻击——让我们称之为“相同的图像,不同的哈希”——增加了一点点噪声,但极大地改变了神经哈希。

(原 Doge meme ,neural hash:11 d9 b 097 AC 960 BD 2c 6c 131 fa |()图片由作者生成,neural hash:F8 D1 b 897 a 45 E0 BF 2 f 7 E1 b 0 Fe

在这个例子中,即使两幅图像看起来很相似(我们在右边添加了一些浅黄绿色斑点的噪声),但神经哈希却完全不同。该噪音是特制的,用于攻击为 NeuralHash 供电的底层 CNN。与之前的例子相比,我们在图像中添加了一些单词,但是神经哈希仍然基本相似。

通过这种攻击,CSAM 的经销商可以通过在他们的图像中添加一点点噪声并极大地改变结果的神经哈希值来逃避检测。如上所示,这可能是少量的噪声,不会显著降低图像质量。

不要给我带来问题,给我带来解决方案

照片由里里在 Unsplash 上拍摄

避免这些攻击的一个方法是永远不要在用户设备上存储 CNN。没有 CNN 的权重,这些攻击将更加困难。然而,这也意味着苹果在他们的服务器上运行哈希算法,这反过来意味着我们所有的 iCloud 照片都将被共享。

或者,苹果可以在许多 CNN 型号上运行 NeuralHash,而不是只有一个型号。使用许多模型增加了产生这些攻击的难度,因为攻击必须同时欺骗所有的模型。然而,这增加了必须在设备上完成的计算,这对于用户来说可能是不期望的。

另一个可能的解决方案是在哈希之前对图像进行一些预处理,例如将它改为黑白,增加对比度或生成多个随机裁剪和旋转。这是有帮助的,因为攻击有时是脆弱的,并且可能被充分的预处理所否定。

NeuralHash 算法是一项新技术,其漏洞的程度可能还没有被完全探索。虽然我对苹果抗击 CSAM 的意图感到振奋,但我会鼓励研究人员继续研究该系统的潜在弱点,以期加以改进。希望这是对 NeuralHash 技术的一个很好的概述!尝试运行算法这里!

苹果新的 M1 Pro 和 M1 Max MacBooks

原文:https://towardsdatascience.com/apples-new-m1-pro-and-m1-max-macbooks-940e104ad6e1?source=collection_archive---------6-----------------------

使用它们进行机器学习时,要做好学习的准备

由 Ales Nesetril 在 Unsplash 上拍摄的照片

规格确实令人生畏:多达 32 个 GPU 核心和多达 16 个 CPU 核心。再加上 64 GB 的 RAM,您就可以轻松应对任何工作负载了。还有,别忘了设计。嗯,看来苹果又来了。

但是,他们真的这样做了吗?让我们再深入一点。就在一年前,M1 芯片亮相,这是苹果公司的第一个定制的片上系统(SoC)。那时候最多可以装 16GB 的主存。并且,在 MacBook Pro 中,这个芯片有 8 个 CPU 和 8 个 GPU 核心。与最新的模型相比,这听起来很小,不是吗?

答案是:看情况。我经常在工作中使用 M1 的 MacBook Pro,我发现唯一的限制是小内存。如果让我再选这台机器,32g 甚至 64 GB 都有选项,我肯定会选。

但是,即使有这些令人印象深刻的规格,我们也不要忘记我们必须为道路提供动力。对我们来说,这主要意味着进行机器学习任务,如训练网络或探索数据集。这就是麻烦的开始。

是的,M1 芯片既快又节能。但是通常情况下,从 pip 安装 python 包是很麻烦的。安装 TensorFlow 是而不是运行那么简单

当我尝试这样做时,我花了三天时间让系统启动并运行。尽管苹果提供了一个专用的 TensorFlow 版本,但要让它运行起来还是很有挑战性。

令人欣慰的是,当他们宣布支持 TensorFlow 的 PluggableDevice 机制时,这一点得到了改善,该机制旨在改善对不同加速器 (GPU、TPU、定制 SOC 等)的支持。随着该版本的发布,事情变得更加顺利——至少在让框架运行方面。

一年后,当我试图从 pip 本地安装包时,仍然经常遇到麻烦。是的,使用 Anaconda 使事情变得更容易。但是,一旦您准备部署您的脚本,您可能希望冻结需求并在远程环境中使用相同的设置。然后,您必须处理两个需求文件:一个来自 Anaconda,另一个来自 pip。

那么,在所有这些感叹之后,有什么解决办法呢?**第一个也是最简单的解决方案:**使用 Linux 机器,配备久经考验的 AMD 或 Intel CPU,并与专用 GPU 配对。

**第二种解决方法:**拥抱学习。是的,我花了三天时间让 TensorFlow 开始工作。是的,我绕了很长的路去安装想要的包。但是,由于我被 M1 芯片卡住了,我没有其他选择,只能经历这一切。

我学到了很多东西。我自己钻研编译包。我开始使用蟒蛇。我深入研究了皮普。我在 GitHub 上滚动了几个小时,了解到写简洁问题的重要性。

所以,我会推荐 M1 的机器学习芯片吗?可以,但是你要做好学习的准备。不像即插即用那么简单。但这也不是火箭科学。

编辑:自从我写了这篇文章,我有机会测试新的 MacBooks,包括“小”的 14 英寸和“大”的 16 英寸。引起我注意的是,大版本比 14 英寸和去年的 13 英寸厚得多。它也更重(官方规格说明)。鉴于此,我更喜欢我的 13 英寸版本,因为它的便携性和重量。

如果可以的话,我建议你在花两千或更多钱之前,先去当地的商店体验一下。这样,你会更好地注意到我指的是什么。

申请和简历技巧让你在数据世界找到理想的工作

原文:https://towardsdatascience.com/application-and-resume-tricks-to-land-your-dream-job-in-the-data-world-c2a1e481651f?source=collection_archive---------26-----------------------

斯科特·格雷厄姆在 Unsplash 上拍照

我是如何采访方、独角兽以及顶级咨询公司的

我知道招聘有多么困难和令人生畏,尤其是在像数据科学这样竞争激烈的领域。回到 2018 年,我正试图从金融世界向数据世界转型;尽管我毕业于一所以量化严谨著称的学校,并在一家顶级资产管理公司担任定量分析师,但我申请的任何一家公司都没有给我回音。

GIF 由 GIPHY

我知道,只要公司愿意给我面试机会,让我展示自己,我就有资格胜任这些工作,但我们都知道,获得面试邀请是整个过程中最难的部分。事实上,申请人跟踪系统(ATS)的拒绝率,在人类看你的简历之前,是惊人的 75%https://blogs.wsj.com/experts/2016/05/24/how-job-seekers-can-get-around-flaws-in-hiring-software/。当时我怎么也想不到,仅仅两年后,在另一轮招聘中,我就获得了方的四家公司中的三家、硅谷的无数独角兽和顶级数据科学咨询公司的面试邀请。

我参与过招聘过程的两个方面(我在过去的多个职位中帮助过招聘),我希望我能帮助阐明如何在数据世界中驾驭招聘过程。本文是系列文章中的第一篇,将集中在这个过程中至关重要的第一步——申请和简历;我将在以后的文章中深入探讨面试准备和其他步骤,敬请关注。

GIF by GIPHY

首先,让我们谈谈在线申请和网络(以及为什么你应该在线申请)

我在 Medium 上读过无数关于“你不应该再在网上申请工作”的文章,这些文章告诉我们“网络是绕过臭名昭著的 ATS 的方法”;因此,我接下来要说的可能不是最受欢迎的观点,但如果你像我一样是一个内向的人,非常害怕强迫社交,这可能会让你的一天变得更好:你绝对应该在线提交申请,很多很多。原因如下:

人脉总是有帮助的,但是 ROI(投资回报)取决于情况:我并不反对人脉。事实上,如果你的目标是更好地了解公司、文化或收集任何其他信息,社交网络是你最好的选择(对于像我这样的内向者来说,这可能是次要的)。但是如果你的目标是得到招聘者的推荐或介绍,这样你就可以“在 ATS 中导航”,你可能会失望。根据我的经验,你通过人际关系网成功获得推荐的机会会因你交往的人对你的了解程度而有很大不同。或者更准确地说,他/她有多愿意/能够担保你的能力。

作者图片

稍微了解一下转诊流程,稍微换位思考一下,就不难理解这背后的原因了。在大公司,每天都有巨大的内部推荐量涌入;因此,他们由一个标准化的系统处理,推荐人必须回答诸如“你为什么推荐这个人”或“你以前和这个人共事过吗?”。在小公司,内部推荐会直接传达给招聘人员,不可避免地,招聘人员会问推荐人类似的问题。

当系统处理推荐时,您会遇到与 ATS 相同的问题;招聘人员只会看排名靠前的推荐人。如果让你来设计排名模型,你会把哪一个排在最前面?上面写着“是的,我和这个人一起工作过”以及一份详细的资格清单?或者上面写着“不,我以前没有和这个人共事过”以及对过去经历的一般性描述。当推荐人被直接推荐给招聘人员时,你希望你的推荐人如何陈述“我推荐这个人是因为他们在 LinkedIn 上给我发了冷消息,而我对他们一无所知”的情况?

诚然,大部分工作都是由内部推荐的人来完成的;事实上,他们中的一些人甚至在发布到网上之前就被填满了。所以,如果你有一个前同事或前同学在你感兴趣的公司工作,你一定会得到推荐。否则,我宁愿用 ATS 碰碰运气。

这是扩大和增加漏斗顶部的最简单方法。如果你思考招聘流程的漏斗(如下所示),有两个变量你可以改变以获得更多的聘用;转换率和漏斗顶部的数量。转换率可以通过改进简历或面试技巧来提高,而提高申请数量的最快方法是通过在线申请。和体积问题;即使有一份出色的简历和强大的关系网,你也永远不会有 100%的转化率(另外,你理想的情况是想要多个工作机会;但是我们将在后面的文章中涉及到这一点)。不要因为你正在向推荐人努力而放慢你的招聘进程:这两者并不相互排斥。如果你最终从一个关系密切的人那里得到了热情的推荐,那么直接申请一家公司并不会对你的机会产生负面影响。

作者图片

公司系统里有你的简历总是好的。没有多少人知道这一点,但在网上发布工作之前,许多招聘人员会先在公司的数据库中搜索过去的求职者。我不止一次收到电子邮件并与招聘人员交谈,大意是“我在我们的数据库中找到了你一年前的简历,我认为你可能非常适合我们的 XXX 职位”,而我:

GIF 由 GIPHY

所以,如果你决定进行在线申请,你需要做些什么来从所有其他在线申请人中脱颖而出呢?改进你的简历可能是最安全的赌注。我不是说检查拼写或语法错误(尽管这也很重要),但更重要的是,你需要注意你使用的单词。让我来给你解释一下为什么不管一家公司是否在使用 ATS,在简历中使用正确的(关键的)单词是获得最佳结果的关键。

如果你逃不出 ATS,就打吧。

知己知彼知己知彼;百战不殆——孙子

说到底,ATS 是基于模型的,可能不是超级复杂的模型。既然你是一个有抱负的数据科学家,问问你自己,如果让你构建一个 ATS,你会如何构建?

在高层次上,我会抓取简历的内容,将结果与职位描述进行匹配,分配一个匹配分数,并设置一个截止值。

我和我新造的 ATS (GIF 来自 GIPHY

我敢肯定这比那要复杂得多,但你已经明白了要点。所以“打败”它真的没那么难。

  1. ***仔细阅读职位描述:*我假设此时,你已经决定了 在数据世界 追求哪个角色。但是公司有时会互换职位名称,所以阅读职位描述不仅能帮助你完成我将要提到的下一步,还能提供一个现实检查,看看你将要申请的工作是否真的是你想象的那样。
  2. 为你的简历量身定做:我的意思是真正地玩“单词匹配”游戏。如果工作描述提到 SQL,确保“SQL”出现在你简历的某个地方。如果这个模型不那么笨,它甚至可以挑选出与目标单词在同一个“簇”中的单词;如果工作描述中写着“贝叶斯统计知识”,而你的课程中有“统计学”、“数学”或“概率”,这肯定会加强你的案例。
  3. 我不会坐在这里假装,如果我只是把我的简历修改一千遍,而没有加上麦肯锡,我也能取得同样的成功。你当然想贴近真实,做到有阶级;但是在你的简历上尽可能多写一些品牌名称。在我的简历上有麦肯锡之前,我在学校赢得的由 Citadel 赞助的 datathon 在 LinkedIn 上引起了很多招聘人员的注意。如果你参加过由著名公司赞助的 Kaggle 竞赛或 Udacity 纳米学位,不要害羞,在描述中包括公司名称。

一个额外的小提示,如果你在申请一两天后收到普通拒绝,你几乎肯定会被 ATS 踢出局;人类工作没那么快。

让你的简历更容易被人浏览。

人类只是有情感的复杂机器,对吧?所以适用于 ATS 的规则也适用于阅读你简历的人。优秀的招聘人员肯定知道他们招聘的领域,知道如何发现该领域的人才,但这并不意味着他们知道这份工作的每一个细节。更不用说他们每天有成千上万份简历要处理,所以如果你没有“信号”词,他们肯定不会花大量时间试图理解你为一个项目做了什么。如果他们找的是“AB 测试经验”,如果你的简历上只有“进行了营销实验,并分析了结果的统计意义”,那你真是得寸进尺。

如果你认为你已经做了你能做的一切,并且相信你已经掌握了所有需要的技能,但是仍然没有回音,有两种可能:

  1. 你没有合适的经历(至少在招聘人员的书上是这样的)。尽管我从事的金融项目与现在非常相似,但科技招聘人员并不明显。我并不惊讶。毕竟,对于以前没有从事过金融工作的人来说,无论我在简历中多少次提到“R”和“SQL”,都很难揣摩“定量分析师”与“数据科学家”做着类似的工作。如果你怀疑是这种情况,那就把目光放在奖品上,但要绕道走。知道咨询公司招聘的人背景更加多样化,我“绕道”成为了一名数据科学顾问,后来证明这是我职业生涯中迄今为止做出的最佳决定(我将写另一篇文章,讲述为什么我认为数据科学咨询是数据职业生涯的一个很好的起点)。在我的简历上写“数据科学顾问”打开了许多几年前关闭的大门。
  2. 只是运气不好。说真的,招聘就像生活中的其他事情一样,运气也是一个因素。在这种情况下,你能做的就是坚持不懈,不断提高自己的技能。我曾经申请过硅谷最大的公司之一(你可能会认为他们已经想好了招聘流程),但被拒绝了,几周后,一位招聘人员找到了我,申请的是完全相同的职位,而我:

GIF 由 GIPHY

用 Python 库实现自然语言处理算法在 IT 监控中的应用

原文:https://towardsdatascience.com/application-of-algorithms-for-natural-language-processing-in-it-monitoring-with-python-libraries-57eddc45c4ac?source=collection_archive---------30-----------------------

当应用于监控 IT 基础设施和业务流程的系统时,NLP 算法可用于解决文本分类问题和创建各种对话系统。本文将简要描述在 Monq 平台的 AIOps 微服务中使用的 NLP 方法。

1.介绍

纯粹从理论上讲,所有自然语言处理(NLP)算法的最终目标都是创造能够理解人类语言的人工智能(AI),此外,在“实现意义”(文本分析)和“做出有意义的陈述”(文本合成)的意义上“理解”。虽然这个目标仍然非常遥远——为了识别一种活的语言,有必要给 AI 智能体所有关于他周围世界的大量知识,以及与它交互的能力,即创建一个“真正会思考”的智能体。所以就目前而言,实际上,自然语言处理可以被认为是从文本数据中提取一些有用信息的各种算法方法。

有相当广泛的实际任务需要自然语言处理:

  • 外语文本的机器翻译,
  • 文本的自动注释(摘要),
  • 按类别(垃圾邮件/非垃圾邮件、新闻分类、情感分析等)对文本进行分类。),
  • 对话系统(聊天机器人、问答系统),
  • 识别命名实体(找到人、公司、地点等的正确名称。正文中)。

当应用于监控 IT 基础设施和业务流程的系统时,NLP 算法可用于解决文本分类问题和创建各种对话系统。本文将简要描述用于混合 IT 监控的 Monq 平台的 AIOps 微服务中使用的自然语言处理方法,特别是用于分析流入系统的事件和日志。

图片作者。

2.将文本转换为数字矢量表示

数学算法与数字一起工作,因此,为了使用数学仪器处理和分析文本数据,您必须首先将单词和句子转换为数字向量,并且最好保留语义关系和词序,即:

  • 数字向量应该反映文本的内容和结构,
  • 意义相似的单词和句子应该具有相似的矢量表示值。

目前,有两种主要的方法用于将文本,更准确地说是一组文本(或者,在 NLP 术语中,文档的语料库)转换成向量表示:

  • 主题建模——用于在文档语料库中寻找隐藏(潜在)主题的几种类型的统计模型:潜在语义分析(PLSA),潜在狄利克雷放置(LDA),
  • 基于分布假设的单词上下文表示的各种模型:神经网络模型 Word2Vec、GloVe、Doc2Vec、fastText 和其他一些模型。

在这些算法结束时获得的向量表示使得比较文本、搜索它们之间的相似文本、对文本进行分类和聚类等变得容易。

大约 10-15 年前,只有专家才能参与自然语言处理项目,因为这需要数学、机器学习和语言学领域的专业知识。现在,开发者可以使用许多现成的工具和库来解决 NLP 问题。特别是,在 Python 语言中,用于训练各种模型的相当广泛的 NLP 能力由两个模块的组合提供: nltk 和gensim—Monq 平台的 NLP 功能基于它们(但不仅仅是它们)。

3.用于模型训练的文本数据预处理

预处理文本数据是构建各种 NLP 模型过程中的一个重要步骤——在这里,GIGO(“垃圾输入,垃圾输出”)的原则比其他任何地方都更正确。文本预处理的主要阶段包括标记化方法、规范化方法(词干化或词条化)和停用词的去除。通常,这还包括提取通常共现的短语(在 NLP 术语中——n-grams 或搭配)和编译令牌字典的方法,但我们将它们区分为单独的阶段。

记号化是将文本分割成文本单元,记号,可以是单个单词或短语,也可以是整个句子。在这种情况下,文档是属于一个语义单元(例如,句子、段落或段落)的标记的集合,而语料库是文档的一般集合。在标记化的过程中,文本是:

  • 分解成句子,
  • 去掉标点符号,
  • 转换成小写,
  • 分成记号(最常见的是单词,但有时是音节或字母组合)。

规范化是将单词还原为其标准的形态形式,为此,要么使用词干化,将单词还原为词干形式(“argu”、“fishing”、“fish”),要么使用词汇化,将单词解析为其规范的词典形式(“is”、“be”、“written”、“write”)。对于俄语来说,词汇化更可取,通常,您必须使用两种不同的算法对单词进行词汇化——分别用于俄语(在 Python 中,您可以使用 pymorphy2 模块来实现这一点)和英语。

从文本块中移除停用词是从文本中清除不提供任何有用信息的词。这些通常包括常用词、代词和功能词类(介词、冠词、连词)。在 Python 中,nltk 模块本身有不同语言的停用词列表,在一个特殊的停用词模块中提供了更大的停用词集合——为了完整起见,可以组合不同的停用词列表。名字和父名也经常被添加到停用词列表中。

下面是一个相对完整的 Python 代码示例,用于预处理一组俄语和英语文本(输出是标记化文档的列表):

import re
from nltk.tokenize.punkt import PunktSentenceTokenizer
sentTokenizer = PunktSentenceTokenizer()
from nltk.tokenize import RegexpTokenizer
tokenizer = RegexpTokenizer(r’\w+’)from nltk.stem.wordnet import WordNetLemmatizer
lemmatizer = WordNetLemmatizer()
import pymorphy2
morph = pymorphy2.MorphAnalyzer()import langid
from nltk.corpus import stopwords
from stop_words import get_stop_wordslangid.set_languages([‘en’,’ru’])
stopWordsEn=set().union(get_stop_words(‘en’), stopwords.words(‘english’))
stopWordsRu=set().union(get_stop_words(‘ru’), stopwords.words(‘russian’))
stopWords=list(set().union(stopWordsEn, stopWordsRu))
stopWords.sort()textCollection=[‘The main stages of text preprocessing include tokenization methods, normalization methods, and removal of stop words.', 'Text tokenization is the splitting of text into text units. During the tokenization process, the text is first divided into sentences.’, ‘Tokenization in Python is the most primary step in any natural language processing program. ‘, ‘We have imported re library and used “\w+” for picking up words from the expression.’]textCollTokens = []
for text in textCollection: ## loop over the collection of texts
 sentList = [sent for sent in sentTokenizer.tokenize(text)]
 tokens = [word for sent in sentList for word in tokenizer.tokenize(sent.lower())]
 lemmedTokens=[]
 for token in tokens:
 if langid.classify(token)[0]==’en’:
 lemmedTokens.append(lemmatizer.lemmatize(token))
 elif langid.classify(token)[0]==’ru’:
 lemmedTokens.append(morph.parse(token)[0].normal_form)
 goodTokens = [token for token in lemmedTokens if not token in stopWords]
 textCollTokens.append(goodTokens)

4.n 元文法的提取和记号字典的编译

提取文本语料库中的稳定短语(n-gram 或搭配,例如,“纽约”、“中央银行”等)。)并且在 NLP 模型中将它们用作单个标记是提高这种模型质量的相当标准的方式。基于给定集合中单词共现的各种统计的计算,有几种算法用于确定文本集合中的搭配。但我们不会深入分析这些算法的利弊,而只是简单地使用 gensim 模块提供的提取二元模型和三元模型的方法:

from gensim.models.phrases import Phrases, Phraserbigrams = Phrases(textCollTokens, min_count=1, threshold=5) ## finding bigrams in the collection
trigrams = Phrases(bigrams[textCollTokens], min_count=2, threshold=5) ## finding trigrams
bigramPhraser = Phraser(bigrams) ## setting up parser for bigrams
trigramPhraser = Phraser(trigrams) ## parser for trigrams
docCollTexts=[]
for doc in textCollTokens:
 docCollTexts.append(trigramPhraser[bigramPhraser[doc]])

文本数据预处理的最后阶段是为给定的文本集合编译一个标记字典(考虑所有找到的 n 元语法)。通常,为了进入字典,令牌必须满足一些额外的标准——过滤令牌以减少“噪音”和“背景”——在我们的例子中(下面是代码示例):

  • 令牌必须在整个集合中出现至少一定次数(参数 no_below ),
  • 该标记不应该比在来自集合的一半文本中更频繁地被发现(参数 no_above )。
from gensim import corporatextCollDictionary = corpora.Dictionary(docCollTexts)
textCollDictionary.filter_extremes(no_below=1, no_above=0.5, keep_n=None)

因此,经过预处理后,文本集合被转换成一个标记化(包括 n 元语法)的文档列表,此外,我们还有一个给定文本主体的标记字典,以及“训练有素”的二元和三元语法分析器。最后三个对象必须保存在磁盘上以备后用,因为它们是正在构建的 NLP 模型的重要组成部分(为了方便起见,令牌字典也保存为文本文件):

bigramPhraser.save(‘bigramPhraser.pkl’)
trigramPhraser.save(‘trigramPhraser.pkl’)
textCollDictionary.save(‘textCollDictionary.pkl’)
textCollDictionary.save_as_text(‘textCollDictionary.txt’)

5.基于潜在狄利克雷分配的主题建模

如上所述,将文本集合转换成向量表示的方法之一是主题建模。在我们的平台中,我们为此使用了潜在的狄利克雷分配方法(LDA)。我们不会详细描述 LDA 内部的文本处理机制(带描述的原始文章此处),但我们将尝试概述该方法的基本原理:

  • 集合中的每个文档都由一组潜在的(隐藏的)主题描述,
  • 主题是具有特定权重或概率(给定字典的单词集上的多项式概率分布)的单词集,
  • 文档是由潜在主题集生成的随机独立单词样本(单词包),
  • 建立描述在特定文档和整个集合中出现的单词频率的矩阵,
  • 使用吉布斯采样——一种根据某种联合分布(特别是狄利克雷分布)生成一组变量的随机样本的算法,确定这样的词到主题和主题到文档的矩阵,其最“正确”地再现给定的文本语料库,
  • 作为集合中每个文档的输出,LDA 算法定义一个主题向量,其值是相应文本中每个潜在主题的相对权重。

下图说明了该方法的本质,相对而言,该图可以在对俄罗斯童话文本语料库运行 LDA 方法之后获得。在输出端,LDA 算法将“渔夫和鱼的故事”与主题向量 T = (0.35,0.5,0.15)进行匹配,其中 0.35 是主题 1 的权重,0.5 是主题 2 的权重,0.15 是主题 3 的权重。应该注意,LDA 方法不以任何方式解释主题,也不为每个主题提供任何通用名称——主题只是由索引编号的单词集合。有时,给定主题中具有最大权重的一个或多个单词被视为主题的简单“有意义”标识符。

图片作者。

使用 gensim 模块,用 Python 训练 LDA 模型非常容易:

from gensim import models
import numpy as np

text corpus =[textcolldictionary . doc 2 bow(doc)for doc in docCollTexts]

nTopics=4ldamodel=models.ldamodel.LdaModel(textCorpus, id2word=textCollDictionary, num_topics=nTopics, passes=10)
ldamodel.save(‘ldaModel’)textTopicsMtx=np.zeros(shape=(len(textCorpus),nTopics),dtype=float)
for k in range(len(textCorpus)): ## make the matrix of docs to topic vectors
 for tpcId,tpcProb in ldamodel.get_document_topics(textCorpus[k]):
 textTopicsMtx[k,tpcId]=tpcProb

在这个例子中,我们训练了一个 LDA 模型,将其保存到磁盘,并且还创建了语料库中所有文档的主题向量矩阵(文本集合的主题表示矩阵),该矩阵可以在将来用于解决文本的分类和聚类的具体问题。对于我们的简单示例,主题矩阵看起来是这样的:

图片作者。

Python 中有几个模块可以可视化主题本身及其组成。以下是使用 wordcloud 和 matplotlib 模块可视化 LDA 结果的代码示例:

import matplotlib.pyplot as plt
from wordcloud import WordCloudcloud = WordCloud(background_color=’white’, width=2500, height=1800, max_words=5, colormap=’tab10',prefer_horizontal=1.0)
topics = ldamodel.show_topics(num_topics=nTopics, num_words=5, formatted=False)fig, ax = plt.subplots(1, 4, figsize=(8, 3), sharex=True, sharey=True)
for i, ax in enumerate(ax.flatten()):
 fig.add_subplot(ax)topicWords = dict(topics[i][1])
 cloud.generate_from_frequencies(topicWords, max_font_size=300)
 plt.gca().imshow(cloud)
 plt.gca().set_title(‘Topic ‘ + str(i), fontdict=dict(size=16))
 plt.gca().axis(‘off’)
plt.subplots_adjust(wspace=0, hspace=0)
plt.axis(‘off’)
plt.show()

结果,对于我们的示例,我们得到了下面的图片:

图片作者。

因为在给定的例子中,文本集合只是一组独立的句子,所以主题分析实际上为每个句子(文档)挑选出一个独立的主题,尽管它将英语中的句子归属于一个主题。

原则上,LDA 算法没有很多调整参数。主要参数是潜在主题的数量,它必须预先确定,并且为该参数选择最佳值是一项非常重要的任务。在我们的实践中,我们受以下简单规则的指导:我们获取在给定文本语料库上构建的标记字典的大小,并将其除以 10 到 20 之间的数(单个主题中的平均字数)——所得值作为 LDA 算法的输入给出。

6.Word2Vec 和 Doc2Vec 模型中单词的上下文表示

LDA 过程结束时文本集合的主题向量矩阵构成了文本语料库的全向量表示的第一部分,第二部分由语义向量或上下文表示形成。

语义向量的概念是基于分布假设:一个词的意义不是它由什么声音和字母组成,而是它最常出现在什么词中,即一个词的意义不是存储在它内部的某个地方,而是集中在其可能的上下文中。基本上,一个词的语义向量显示了它出现在其他词旁边的频率。

语义向量的最简单版本是一个单词在一个上下文中使用频率的矩阵,即彼此之间的距离不超过 n 个单词(通常使用 n=10 ),例如,如果您拿一期《读者文摘》杂志,并计算不同的单词对在特定大小的相同上下文窗口中出现的频率,您会得到一个表格,其中一部分可能如下所示:

图片作者。

此表中的每一行数字都是第一列中单词的语义向量(上下文表示),在《读者文摘》杂志的文本语料库中定义。

如果我们在整个语言的文本语料库(例如,这个 one )上执行这样的过程,那么这种方法的两个显著缺点立即变得明显:

  • 该表太大了,因为它的大小是由成千上万的字典的大小决定的,
  • 该表将主要由零填充。

自上世纪 60 年代以来,已经提出了各种技术来降低词的共现矩阵的维数(奇异值分解、主成分分析、各种类型的过滤),但是没有观察到显著的突破。突破发生在 2013 年,当时谷歌的一组研究人员提出使用神经网络架构 Word2Vec 来获得语义向量。还是那句话,我们不会详细描述 Word2Vec 神经网络是如何工作的(原文可以取此处,以更通俗易懂的形式此处),但我们会局限于主要方面:

  • 神经网络在大量的文档集合上被训练,以预测哪些单词最有可能被发现彼此相邻(并且神经网络绝不是深度的——只有两层),
  • 两种学习模式,CBOW(连续单词包,更快)和 skip-gram(对较少使用的单词更准确),
  • 输出向量的维数是几百(典型地, n=300 ),
  • 在训练了从输入层到神经元隐藏层的权重矩阵之后,自动给出所有单词的期望语义向量。

Word2Vec 方法的进一步发展是 Doc2Vec 神经网络架构,它为整个句子和段落定义语义向量。基本上,附加的抽象标记被任意地插入到每个文档的标记序列的开始,并且被用于神经网络的训练。训练完成后,这个抽象记号对应的语义向量包含了整个文档的广义含义。尽管这个过程看起来像是“用耳朵玩的把戏”,但实际上,来自 Doc2Vec 的语义向量改善了 NLP 模型的特性(当然,并不总是如此)。

我们再次使用 gensim 模块中 Doc2Vec 算法的实现:

从 gensim.models 导入 Doc2Vec

d2vSize=5
d2vCorpus= [models.doc2vec.TaggedDocument(text,[k]) for k,text in enumerate(docCollTexts)]
d2vModel=Doc2Vec(vector_size=d2vSize, min_count=1, epochs=10, dm=1)
d2vModel.build_vocab(d2vCorpus)
d2vModel.train(d2vCorpus, total_examples=d2vModel.corpus_count, epochs=d2vModel.epochs)
d2vModel.save(‘doc2vecModel’)textD2vMtx=np.zeros(shape=(len(textCorpus), d2vSize),dtype=float)
for docId in range(len(d2vCorpus)):
 doc2vector=d2vModel.infer_vector(d2vCorpus[docId].words)
 textD2vMtx[docId,:]=doc2vector

在这里,我们训练一个 Doc2Vec 模型,将其保存到磁盘,然后,使用训练好的模型,创建所有文档的语义向量矩阵。在我们的简单示例中,这个矩阵是这样的:

图片作者。

查看该矩阵,很难解释其内容,尤其是与主题矩阵相比,主题矩阵中的所有内容或多或少都很清楚。但是解释的复杂性是神经网络模型的特征,主要是它们应该改进结果。

结合作为 LDA 和 Doc2Vec 算法的工作结果而计算的矩阵,我们获得了文档集合的全向量表示的矩阵(在我们的简单示例中,矩阵大小为 4x9 )。此时,可以认为将文本数据转换成数字向量的任务已经完成,并且得到的矩阵可以进一步用于构建文本分类和聚类的 NLP 模型。

7.摘要

在本文中,我们分析了使用几个 Python 库处理文本数据并将它们转换成数字向量的例子。在下一篇文章中,我们将描述一个使用 LDA 和 Doc2Vec 方法解决混合 IT 监控平台 Monq 中主事件自动聚类问题的具体例子。

低密度聚乙烯综合生命周期评估和供应链分析的应用

原文:https://towardsdatascience.com/application-of-an-integrated-life-cycle-assessment-and-supply-chain-analytics-on-ldpe-6aba41555921?source=collection_archive---------40-----------------------

行业笔记

作者:Debanjana Chakraborty @ Chakraborty _ Debanjana,Suparno Bhatta @iamsuparno 和 Poovammal E 博士,印度钦奈 603203

S 可持续产品是指以最优成本获得的清洁绿色产品。在市场主导的时代,快速的商业发展集中于廉价商品的快速生产。这造成了自然资源过度消耗的激增,代价高昂,从而产生了有害的污染物。意识到后果后,各国开始采取措施消除市场对气候和自然环境的负面影响。

开发一种能够专注于在环境质量和全球市场产品供应链之间建立平衡的模式,已经成为一件至关重要的事情(Bhatta 等人,2021 年)。这可以通过开发一个融合了供应链管理分析和生命周期评估(LCA)的模型来实现。

生命周期评估(IS014040 和 ISO4044)是指测试和评估产品的环境和生态质量的方法。整个过程包括原材料提取阶段、制造和加工阶段、运输阶段以及最终的处置和再利用阶段。

供应链管理(SCM)要求组织数据、人员、事件、组织和资源(Bhatta et al .,2021)。它们强调改善货物流动的功能、计划、组织和控制。整个过程包括仓储、供应商、分销商、市场、制造商和零售商物流。

现有模型的缺点:

在生命周期评估和供应链管理的单个模型中,人们注意到在信息技术、数据生成和管理方面存在严重不足。(达文波特等人,2001;Hazen 等人,2014 年)。他们未能产生一个准确的结果去测试身体。这是因为缺乏对关键决策领域的了解,而这些领域是了解产品类型、质量和在任何特定行业使用时的可持续性所必需的(Fornasiero 等人,2017 年)。独立模型缺乏逻辑和技术流程来确定利润率、消费和排放模式(Brondi & Carpanzano,2011 年)。先前模型中使用的数据集大多是聚合的,而不管它们是否特定于位置。(Nuasaen 等人,2014;Park & Seo,2006;Moriguchi 和 Terazono,2000 年)。

目标:

已经进行了许多研究来开发 LCA 和 SCM 的单个模型,但是很少关注将这两个方面结合起来,以提供从原产地到消费的高质量订单履行。这些技术的结合将建立一种操作效率,从而提高模型的生产率。该整合模型将通过关注生产支出的增长和减少,帮助企业跟踪其供求关系。同时,它还将强调减少有害的环境排放和对自然资源的过度消耗。我们之前提出了一个架构(Bhatta 等人,2021 年),将在这项研究中实施,以检查模型的效率。

一次性塑料

整个测试将在主要消耗天然气和石油以及排放有害温室气体的产品(一次性塑料)上进行。该模型应强调数据维度的完整性、一致性、准确性和及时性、相关性、价值性、数量性、可访问性和声誉性(王,1996;王等,1995;王,1998)。

履行

LCA 和 SCM 集成模型的体系结构图

该模型是一个集成开发,首先确定满足客户需求所必需的原材料。然后在 Google Colab 中使用 Python 执行 LCA 和 SCM 分析。在生命周期评价阶段,对每种原材料的环境质量进行测试,以获得加工、制造和运输阶段消耗的能量。这让我们了解了制造阶段可用的不可再生资源的稀缺程度,以及使用这些资源时所消耗的能源量。它还让我们了解了因生产一次性塑料而产生的温室气体和非温室气体排放量。同时还对产品的供求数据进行分析。这可以通过进入计划阶段来获得,在计划阶段,可以获得制造和销售一次性塑料的净支出和总支出。损益是根据净支出、总支出、运营成本和额外的第三方成本预测的。数据集准备就绪后,使用 Scikit learn 执行数据挖掘。

UML 图

首先,我们导入数据集,并将其安装在与 Google Colab 相同的驱动器上。

将数据集装载到 google collab

然后在 python 中设置访问数据集的路径。

添加数据集的路径

然后,我们导入 NumPy 库来转换 CSV 格式的数据,并以表格格式分析数据。

导入 NumPy 库以将数据集转换为数据数组

使用特征提取获得所有需要的输入,并设置预测的目标值。

设置输入和输出的数据值

训练和测试数据被分成 70:30 以获得最佳结果。

训练和测试数据集

然后我们在导入 Scikit learn 后进行 Huber、Ridge 和线性回归。

用于导入 Huber(左)、岭(中)和线性(右)回归的代码段

检查模型分数以获得挖掘技术的准确性。

Huber(左)、岭(中)和线性回归(右)的模型得分

对于图形可视化,将从目标结果获得的实际值和预测值与平滑线图进行比较。LCA 和 SCM 阶段都是使用回归技术进行的。

数据集收集

理解该模型的可行性是至关重要的。分钟数据集具有现成的数据集,可用于在集成的 LCA-SCM 模型上进行测试,因此为此测试过程创建了一个新的数据集。

每个 LCA 和 SCM 部分的收集方法是不同的。一次性塑料的生产由低密度聚乙烯完成。使用这种塑料的数据是因为它们目前在全球市场上需求量最大。低密度聚乙烯在我们生活的各个方面都有广泛的用途。他们也是造成全球变暖的温室气体的最大排放者。我们从第一个报告期获得了提交给英国政府的六个月(2015 年 10 月-2016 年 4 月)一次性载体的数据集。该数据( Lca-SCM 塑料数据库-完整数据库,未注明)包括制造塑料的净支出、销售塑料的总支出、政府征收的增值税。

我们将 SCM 数据与自行创建的 LCA 数据集相结合。使用“一次性塑料的数量”和一个单位生产或排放的数值来计算数值,该数值基于 2011 年 8 月关于九种塑料树脂和四种聚氨酯前体从摇篮到大门生命周期清单的技术报告(Feraldi 等人,2013 年)。生命周期评价的数据集侧重于“从摇篮到大门”阶段。该数据包括加工过程中使用的不可再生资源和消耗的能源的价值。它还包括在一次性塑料的制造、加工和运输阶段排放到大气中的各种温室和非温室污染物。

要获取数据集,请将此出版物的作者发邮件给

回归技术

我们使用线性回归,因为它有助于预测公司的净利润/亏损,这取决于制造用原材料的销售情况。当我们使用一个因变量和一个自变量进行预测分析时,它会给出准确的结果。因为这有利于预测效果,也有利于确定预测者的强度。我们还尝试使用更多的回归技术来检查哪一个更适合我们。使用 Huber 回归,因为它对异常值是稳健的。与标准的最小二乘法相比,它使用了一种特殊的损失函数。对于损失的小残差,似乎通常拥有最小量的平方惩罚,但是对于大残差,它的惩罚更小,并且它线性地而不是二次地激增。因此,它适用于含有大量异常值的数据集。

然后利用岭回归分析多重共线性严重的多重相关数据。当多重共线性出现时,最小二乘法估计值是无偏的,但它们的方差很大,这意味着它将远离真值。它通过给回归估计增加一定程度的偏差来减少质量误差。

结果和讨论

我们希望设计一个模型,可以预测产品的环境质量和可持续成本之间的趋势。在这个结果中,我们试图找出一次性塑料制造过程中的能源消耗率、销售系数和有害污染物排放量。在这项研究中,采用了线性、Huber 和岭回归技术进行比较研究,以了解在这种情况下哪种方法在准确性方面效果最好。在比较研究中,实际值和真实值通过图表计算得出。实际曲线代表基于独立变量的曲线的当前状态。预测曲线表示与当前值相比,曲线可能如何变化的未来预测。

在所有三种回归技术中,模型得分几乎在0.9995–1.0之间。这显示了三种技术的精确度。所有三种回归技术为我们提供了几乎相似的结果,只有微小的偏差。然而,在三个 Huber 回归中,几乎所有代码的模型得分为 1.0,显示出最高的准确性。在这里,我们将只显示 Huber 回归的图形和数据头。

销售和推断:

我们试图找出英国公司每年用低密度聚乙烯制造一次性塑料的总支出。在数据集中,我们获得了净支出、总支出和增值税的值。

根据计算:总收入=净收入+增值税(总收入> >净收入)

**总收入:**公司生产一次性塑料的平均支出。

**净收益:**制造商制造塑料的平均支出。

**增值税:**政府对一次性塑料征收的税

存在增值税时使用线性回归进行销售预测的示例代码

存在增值税时使用 Huber 回归进行销售预测的示例代码

下图说明:

含增值税和不含增值税的一次性塑料的销售增长

  1. 在我们的图表中,我们注意到预测总值略低于实际总值。制造塑料的未来开支将会减少。这意味着英国公司将开始减少这些塑料的使用和生产,从而遵守英国公司签署的反塑料协议。公司还没有减少一次性塑料的使用。一次性塑料未来产量的大幅下降将有助于在很大程度上减少销售。公司还没有减少一次性塑料的使用。
  2. 上述四张图中的实际总支出和预测总支出保持不变。这表明政府实施的机制还没有发挥作用。未来各公司的塑料年供应量和销售量将继续增长。

不含增值税的销售额前 7 名的负责人(左),含增值税的销售额前 7 名的负责人(右)

3.比较上图中的两个数据集,以了解预测总支出的微小变化。数据头中预测总量的微小变化肉眼几乎无法分辨。有增值税的数据集显示塑料的预测支出较少,而没有增值税的数据集显示一次性塑料的未来销售和消费呈线性增长。从而显示政府在控制一次性塑料生产中的作用。英国政府实施的严格机制将迫使这些公司自动减少塑料的生产和消费。

能源和资源消耗:

低密度聚乙烯的化学结构

一次性塑料由低密度聚乙烯制成。它们由原油或天然气加工而成。在塑料制造过程中,需要大量不可再生资源来提取原材料,将这些原材料加工成低密度聚乙烯,然后从工厂运输到公司。加工、运输和材料阶段的能源消耗有助于我们了解公司每年消耗的不可再生资源的数量。

过程运输和材料阶段

材料阶段:

下图说明:(添加数字)

Huber 回归中的实际(左)材料资源与所有不可再生能源以及预测(右)材料资源与所有不可再生能源

  1. 上述数字显示了对日益枯竭的自然资源的过度消耗。大量的天然气被加工和消耗以制备低密度聚乙烯。石油在能源消耗总量中位居第二。剩余资源(煤、核能等)消耗的能量非常少。
  2. 在这两个图表中,所有资源的能源消耗继续急剧增加。这描绘了公司过度消耗不可再生资源的有害习惯。

流程阶段

下图说明:

Huber 回归中的实际工艺资源与所有不可再生能源(左)和预测工艺资源与所有不可再生能源(右)

3.在上图中,我们可以看到天然气消耗最大的能量来制备低密度聚乙烯。与物质相类似,石油排在总能耗的第二位。其他气体的曲线几乎已经融合在图表中,因为它们消耗的能量非常少,肉眼几乎看不见。

4.加工阶段的消耗曲线类似于材料阶段。这告诉我们公司如何继续过度消耗资源

运输阶段:

下图说明了:

Huber 回归中的实际运输资源与所有不可再生能源(左)和预测运输资源与所有不可再生能源(右)

5.天然气和石油再次占据运输阶段能源消耗的前两位。然而,石油和天然气的能耗曲线存在巨大差异。这表明与石油相比,天然气的消耗量要大得多。加工和制造阶段的情况并非如此。

合并资源:

下面给出的数据标题图代表了不可再生资源消耗的能源与工艺、运输和材料资源的实际值和预测值之间的比较。

使用岭回归进行流程、运输和材料预测的示例代码,以了解过度消耗的自然资源

下图说明了:

组合流程、运输和材料阶段的前 9 个值的标题

6.在塑料制造过程中(加工阶段),不可再生资源的能源消耗最大。原材料运输(运输阶段)排在第二位,提取原材料制造塑料排在第三位

7.英国的公司显示出生产更多塑料的未来趋势。这是显而易见的,因为预测过程值大于实际过程值。减少产量的唯一方法是提高每种产品的税率。这将减少需求,从而减少石油和天然气的过度使用。

8.实际运输量和预测运输量的趋势相反。这与过程阶段相反。这表明未来交通运输阶段的能源消耗率将会下降。

9.与生产和运输阶段的值相比,材料阶段的预测值和实际值的能耗几乎可以忽略不计。

能源消耗越大,自然资源的损耗就越大。因此,影响了地球的生态因素。它还增加了导致全球变暖的二氧化碳和甲烷的排放率。

温室气体和非温室气体排放推断:

以前我们已经注意到不可再生资源是如何被过度消耗来制造一次性塑料的。下图和图 15 中的饼图分析了该阶段排放的温室气体和非温室气体。

温室气体:

下图分析了提取、制造和运输一次性塑料时预计会排放的温室气体量。

利用 Huber 回归分析温室污染率

温室气体:二氧化碳、甲烷、一氧化二氮、溴甲烷、氯甲烷、三氯乙烷、氯仿、四氯化碳、CFC13、HCFC 22。

下图说明:

各阶段预计的温室气体排放量(单位:1000 千克)

  1. 二氧化碳(78.67%)和甲烷(20.88%)是产生最多的污染物,其次是其他污染物。这是因为石油和天然气资源被过度消耗以制造一次性塑料。在运输阶段,大量的煤以及石油和天然气也被用作燃料。它们是截留甲烷气体的主要来源。当这些资源被过度消耗时,致命的甲烷和二氧化碳被释放到空气中。在运输阶段,大量的煤以及石油和天然气也被用作燃料。这会释放有害的污染物。这两种污染物都吸收热量,并且不允许它们从地球表面逸出。从而使空气变得非常热,增加了气候临界点的危险。这将对地球的气候产生毁灭性的影响。
  2. 甲烷是一种更致命的温室污染物,因为它在大气中停留的时间比二氧化碳更长。在所有三个阶段中曲线的急剧上升会导致许多健康问题,如视力问题、记忆力丧失、恶心、呕吐、面部潮红和头痛。
  3. 其余的污染物加在一起只占总污染的 0.44 %。因此,控制甲烷和二氧化碳的比例,地球的温度就会得到控制。

下面是一个类似的饼状图,用来比较加工、运输和材料阶段排放的非温室气体水平。这些气体不是全球变暖的罪魁祸首,但它们是导致致命健康问题的空气污染的罪魁祸首。

非温室气体:颗粒物(PM 未指明,2.5,10)、挥发性有机化合物(VOC)、非化石二氧化碳、氮氧化物、一氧化碳和二氧化硫。所有三个阶段都显示出相同的趋势。

使用线性回归技术的非温室气体排放率

下图说明:

预测的各阶段非温室气体排放量(单位:1000 千克)

  1. 在该过程中产生大约 43.5%的二氧化硫。这是一个令人担忧的问题,因为如此高的百分比破坏了生态系统,也是文物建筑恶化的原因。它们还会导致人类呼吸急促和胸闷。
  2. 氮氧化物占非温室气体排放总量的 27%。它们破坏植被,影响动物和人类。这种污染物会引起眼睛刺激、肺部问题和呼吸问题。它们与大量排放的二氧化硫结合产生酸雨。酸雨与建筑物和雕像发生化学反应,溶解化学物质,从而使它们变得脆弱。
  3. 与其他污染物相比,一氧化碳和挥发性有机化合物的产生率为 12%,但当一氧化碳含量超过 40%时,这些污染物就会对人类和动物的生命产生致命影响。
  4. 与其他污染物相比,微粒物质(未指明,10 和 2.5)的排放量非常小。这些主要是微米和纳米微粒,是室内污染的主要来源。它们对人类健康有重大影响。这些是极小颗粒和液滴的复杂混合物。这些污染物在本质上是致命的,当大量排放时甚至会导致死亡。

二氧化碳化石排放几乎可以忽略不计。然而,与其他污染物相比,它们的致命性较低,如果在高浓度下产生,会导致昏迷或死亡。

结论

所有三种回归技术都经过了准确性测试。给出最佳精度的模型是 Huber,其次是线性,然后是岭型。这是因为它对数据集中出现的异常值或数据异常不太敏感。但是,在此过程中,某些净值、总值和增值税值不可用,因此默认情况下假定它们为 0。长期以来,完整数据的可获得性一直是一个问题,这项研究也反映了这一点。由于公司的保密原因,这些数据中的大部分被隐藏或未被探索。在大多数图表中,所有的曲线都不清晰可见。这是因为我们的数据集包含大范围的值,当累积在一起时,我们很难通过肉眼看到结果中的微小差异。此过程中使用的数据独立于任何特定位置,因此如果考虑任何特定区域,结果可能会有所不同。使用回归技术的整个过程提供了非常高效的结果。读者也可以尝试聚类或分类技术来获得可持续的结果。

参考资料:

*Bhatta,s .,Chakraborty,d .,& Poovammal,E. (2021)。使用整合的 LCA-SCM 模型得出可持续产品的最佳成本。 9 (2),1193–1199 年。【https://doi.org/10.17762/itii.v9i2.472 *

c .布朗迪和 e .卡潘萨诺(2011 年)。基于 LCA 的生产系统模拟的模块化框架。《CIRP 制造科技杂志》4 (3),305–312 页。https://doi.org/10.1016/j.cirpj.2011.06.006

达文波特,T. H .,哈里斯,J. G .,德龙,D. W .,,雅各布森,A. L. (2001)。从数据到知识再到结果。加州管理评论43 (2),117。http://search.ebscohost.com/login.aspx?direct=true&db = buh&AN = 4372997&site = ehost-live

费阿尔迪、凯许曼、哈夫和拉豪格(2013 年)。美国废轮胎处理方案的比较生命周期分析;

材料回收和轮胎衍生燃料燃烧。国际生命周期评价杂志18 (3),613–625。https://doi.org/10.1007/s11367-012-0514-8

*Fornasiero、c . Brondi 和 d . Collatina(2017 年)。提出一个整合的 LCA-SCM 模型来评估定制策略的可持续性。国际计算机集成制造杂志00 (00),1–14。【https://doi.org/10.1080/0951192X.2016.1268716 *

Hazen,B. T .,Boone,C. A .,Ezell,J. D .,& Jones-Farmer,L. A. (2014 年)。供应链管理中数据科学、预测分析和大数据的数据质量:问题介绍及研究和应用建议。国际生产经济学杂志154 ,72–80。https://doi.org/10.1016/j.ijpe.2014.04.018

lca-scm 塑料数据库-全数据库。(未注明)。

Moriguchi,y .,& Terazono,A. (2000 年)。大气排放空间差异影响评估的简化模式。国际生命周期评价杂志5 (5),281–286。https://doi.org/10.1007/BF02977580

Nuasaen,s .,Opaprakasit,p .,& Tangboriboonrat,P. (2014 年)。壳聚糖功能化的中空乳胶粒子用于去除室内空气中的甲醛。碳水化合物聚合物101 (1),179–187。https://doi.org/10.1016/j.carbpol.2013.09.059

Park,J. H .,,Seo,K. K. (2006 年)。一种基于知识的近似生命周期评估系统,用于评估协同设计环境中产品设计备选方案的环境影响。高等工程信息学20 (2),147–154。https://doi.org/10.1016/j.aei.2005.09.003

王瑞英(1996)。数据质量对数据消费者意味着什么。管理信息系统杂志12 (4),5–34。https://doi.org/10.1080/07421222.1996.11518099

*王瑞英(1998)。全面数据质量管理的产品视角。ACM 的通信41 (2)、58–65。【https://doi.org/10.1145/269012.269022 *

*王瑞英、斯托里、冯维国、费斯、陈平(1995)。数据质量研究的分析框架。 IEEE 知识与数据工程汇刊7 (4),623–640。【https://doi.org/10.1109/69.404034 *

特征选择技术在回归问题中的应用

原文:https://towardsdatascience.com/application-of-feature-selection-techniques-in-a-regression-problem-4278e2efd503?source=collection_archive---------18-----------------------

维多利亚诺·伊斯基耶多在 Unsplash 上的照片

Python 中的要素选择和优化示例

在与您的数据管理员多次通话,通过各种渠道和数据丰富平台获取最有用的数据后,您作为一名数据科学家完成了数据收集。现在你有一个庞大的数据集可以浏览。为了优化计算时间、性能和可解释性之间的权衡,您只需要包括与您的机器学习模型最相关的列。此时,功能选择开始发挥作用。

为什么我们需要特征选择?

主要原因是在将数据提供给模型之前减少数据集中的字段数量。为什么需要这样做?需要降低模型的复杂性,这可能会导致过度拟合。我们的数据应该符合信号,而不是不相关的预测者产生的噪音。复杂性的降低也增加了我们模型的可解释性,这在与其他利益相关者交流结果时是必不可少的。最后,它减少了内存和计算需求,这是针对生产中的模型进行优化的一个方面。

最常见的特征选择方法

过滤方法仅包括与目标变量相关性分数高的模型最相关的特征。这是非常简单和计算友好的,因为使用一个相关措施,一个分数计算所有的预测。具有最高分数的特征被过滤以在模型中使用。一个警告是,在线性模型中总是存在共线性的风险。过滤方法只检查与目标变量的相关性,这就是为什么预测值之间的任何相关性都需要在预处理的后期处理。

包装器方法在每次迭代中用特性子集训练模型,并检查它们对性能的影响。然后利用不同的子集训练另一个模型。在每一步中,来自先前模型的学习被考虑来挑选产生更好结果的不同子集。迭代继续进行,直到选择出最佳特征。这种方法计算量很大,因为在每次迭代中,都用新的特征子集来训练新的模型。

嵌入式方法利用在建模过程中支持特征选择的算法。这种方法减少了预处理时间,因为训练和特征选择是一起完成的。最好的例子可以是 Lasso 和岭回归或者决策树。

下面我将为过滤器包装器嵌入方法应用一些例子。前三个将是皮尔逊相关、单变量特征选择和方差阈值来表示过滤方法,然后是包装方法的递归特征消除,最后是嵌入方法的 Lasso。

方法 1:皮尔逊相关法

皮尔逊相关系数是介于-1 和+1 之间的一种度量。如果系数越来越接近一端,那么相关性就越高。最好的可视化方法是利用sns.heatmap()。在下图的情况下,单元格越亮,特征之间的相关性越高。

为了找到模型中最有用的列,可以从相关矩阵中筛选出目标变量。由于我对相关性的大小比对其方向更感兴趣,所以我检查了矩阵的绝对值。

# creating dataframe including the correlations with SalePrice
df_corr = abs(df_train.corr()).sort_values(by='SalePrice', ascending=False)[['SalePrice']]# threshold of 0.4 is selected to filter the features
df_corr[df_corr['SalePrice']>0.4]

挑选与目标变量最相关的列很容易,但是我们需要解决任何线性模型中的共线性问题。为了解决这个问题,我建立了一个共线性检测器:

def colinear_detector(df, target_col):
  """
  aim: detect any colinearity
  inputs: all the columns
  output: colinear pairs
  """
  # dropping the target columns
  df_x = df.drop(columns=target_col) # initiating an empty dictionary 
  colinear_dict = {}

  # sorting the most correlated column for each column
  for column in  df_x.columns.tolist():
    df_corre = abs(df_x.corr()).sort_values(by=column, ascending=False)[[column]]

    # if score is higher than 0.8, add them to a new list
    # every predictor's correlation with itself is 1 so drop the first
    colinear_cols = df_corre[df_corre[column]>0.8].index.tolist()[1:]
    # add a list of correlated columns for each column
    colinear_dict[column]= colinear_cols

  # return the correlation dictionary
  return colinear_dict

输出将是一个字典,其中键是具有共线性的列,值是相应键的所有共线性列的列表。

方法 2:单变量特征选择

单变量特征选择计算特征和目标变量之间的相互作用,并对每个特征分别进行计算。对于每个特征,计算 p 值,表明该特征是否对目标变量有显著影响。由于我们正在处理一个回归问题,我将利用 sklearn 的f_regression。在幕后,它分别训练每个特征的简单线性回归模型来预测目标变量。每个模型的 F 值解释了该特征预测目标变量变化的程度。在f_regression的例子中,一个简单的线性回归模型的 F 值变成了它被训练的特征的 F 值。该分数随后用于确定该特征相对于其他特征与目标变量的相关性。

与目标变量高度相关的特征

与目标变量相关性差的特征

下面,我挑选了 25 个得分最高的列来包含在我的模型中。

from sklearn.feature_selection import f_regression
# assigning f and p values to 2 separate arrays
f_val, p_val = f_regression(X_train,y_train)

# creating a dictionary from the arrays
feature_dict={'features':X_train.columns.tolist(),
              'f_score':f_val.tolist()}

# creating a sorted dataframe from the dictionary
feature_df = pd.DataFrame(feature_dict).sort_values(by='f_scores', ascending=False).reset_index(drop=True)

# printing 25 features with the highest scores
feature_df.iloc[:25,:]['columns'].tolist()

方法 3:方差阈值

在训练数据集中,仅采用一个值的要素对模型的整体性能没有贡献。不管有没有,性能都是一样的,因为该特性的方差为零。换句话说,如果方差很低,该特征就不能提供关于目标变量的有意义的见解。这就是为什么它不会增加模型的预测能力。由于一个特征的方差很好地表明了它的预测能力,VarianceThreshold函数消除了低于给定方差的所有特征。

下面是一个箱线图,显示了 6 个不同特征的中心趋势。与前五个特征相比,最后一个特征的方差为零,因为 99%的值都是 0。换句话说,它对目标变量几乎没有预测能力,因为它的值从不改变。这就是为什么将它添加到我们的模型中不会提高性能。

下面我更喜欢使用阈值水平 0.25,因为默认值不会删除我的训练数据中的任何列。请注意,一旦数据被标准化,这个过程是有用的,这样所有的差异都在一个相似的范围内。

from sklearn.feature_selection import VarianceThresholdvar_selector=VarianceThreshold(threshold=0.25)
df_var = var_selector.fit_transform(X_train)

方法 4:递归特征消除

递归特征消除方法试图找到包含在模型中的最优特征子集。在这种方法中,形成特征的子集,并且在每次迭代中,丢弃表现最差的特征。因此,在达到最佳子集之前,子集在每一步中都变得越来越少。

为了利用递归特征消除,我从 sklearn 中选择了RFECV函数。在 RFE 函数中启动并加载一个模型,其中step=5意味着每一步将消除 5 个特征,而cv=3用于 3 重交叉验证。

from sklearn import linear_model
from sklearn.feature_selection import RFECV

# initiating a linear model
model = linear_model.LinearRegression()

# feeding the model to RFECV
rfe = RFECV(estimator=model, step=5, cv=3)

# fitting to the trained rfe model
rfe_fit = rfe.fit(X_train,y_train)

# zipping the column names and their rankings according to RFECV
rfe_dict = dict(zip(np.array(X_train.columns),rfe_fit.ranking_))

# picking the features ranked 1st in the above dictionary
selected = {}
for key, value in rfe_dict.items():
  if value==1:
    selected[key]=value
print(list(selected.keys()))

方法 5:套索回归

虽然 Lasso 是一种正则化技术,但它也可用于特征选择,因为它强制不相关特征的系数变为零。如果正则化的强度通过增加α而增加,则成本函数优先考虑最小化系数,而不是最小化误差平方和,这将迫使系数更快地达到零。

套索回归的目标函数

在应用正则化之前对数据进行归一化是非常重要的,因为否则要素及其系数将不会以类似的方式受到正则化项的影响。下面,我在建模前使用了StandardScaler

from sklearn.linear_model import Lasso
from sklearn.preprocessing import StandardScaler
from sklearn.feature_selection import SelectFromModel
from sklearn.pipeline import Pipeline
from sklearn.model_selection import GridSearchCV# creating a pipeline to scale and model
pipeline = Pipeline([('sc',StandardScaler()),('model',Lasso())])# gridsearch over the pipeline to find the best alpha for lasso
# scoring is picked as mean squared error
lassocv = GridSearchCV(pipeline,
                      {'model__alpha':np.arange(0.1,10,0.2)},
                      cv = 5, scoring="neg_root_mean_squared_error"
                      )# fitting to the model
lassocv.fit(X_train,y_train)

# dataframe of variables, coefficients and absolute coefficients 
coefs = pd.DataFrame({'variable':X.columns,                            'coef':lassocv.best_estimator_.named_steps['model'].coef_,                            'abs_coef':np.abs(lassocv.best_estimator_.named_steps['model'].coef_)})

# sorting based on the absolute of the coefficients
coefs.sort_values('abs_coef', inplace=True, ascending=False)

结论

以上,我试图总结最常见的特征选择技术;过滤器包装器嵌入方法及其在一个回归问题中的应用。重要的是要记住,上述方法中的一种并不比另一种优越,因为每个应用程序的用例及优先级都不同。关键的一点是,在大多数情况下,我们不需要引入模型过度拟合的复杂程度。我们需要选择对目标变量具有最高预测能力的特征。

感谢阅读到最后,你可以在这里找到我的笔记本。如有任何问题或意见,请联系。

[1] Tracers,什么是数据浓缩?,追踪者博客

[2] Sklearn 文档, sklearn.feature_selection。RFECV

[3] J. Brownlee,递归特征消除(RFE)用于 Python 中的特征选择,机器学习掌握

GPT-3 在问答中的应用

原文:https://towardsdatascience.com/application-of-gpt-3-in-q-a-769239a01f6d?source=collection_archive---------4-----------------------

人工智能时代的技术支持

杰利·范·李斯特在 Unsplash 上拍摄的照片

计算机程序将句子翻译成内部表示的能力,从而可以对用户提出的问题产生有效的答案,这种能力被称为自然语言的机器理解。 OpenAI 的预训练生成变压器 3 (GPT-3)就是这样一个自回归语言模型,它将自然语言处理(NLP)的能力革命性地提升到了一个新的水平。

本文探讨了如何有效地使用 GPT-3 来更好地理解用户的问题,并以良好的准确性将它们连接到庞大内容库中的相关部分,如用户指南和技术文档。

GPT-3 概述

GPT-3 已经被训练了 1750 亿个参数,比任何以前的非稀疏语言模型多 10 倍。它可以生成质量如此之高的书面文本,以至于经常很难与人写的文本区分开来。GPT-3 与其他语言模型(如 BERT)的区别在于,对于所有任务,GPT-3 都是在没有任何梯度更新或微调的情况下应用的,任务和少量演示完全通过与模型的文本交互来指定。GPT-3 在许多自然语言处理数据集上取得了很好的性能,包括翻译和问答[1]。GPT-3 的加权预训练数据集的 60%来自由 4100 亿字节对编码的令牌组成的通用爬行的过滤版本。其他来源是来自 WebText2 的 190 亿个标记,占加权总数的 22%,来自 Books1 的 120 亿个标记占 8%,来自 Books2 的 550 亿个标记占 8%,来自 Wikipedia 的 30 亿个标记占 3%。GPT-3 接受了数千亿单词的训练,能够用 CSS、SQL、Python 等语言编码。由于 GPT-3 的训练数据包罗万象,它不需要为不同的语言任务进行进一步的训练[2]。目前,OpenAI 已经发布了基于 invite only 的 GPT-3 APIbeta 版

GPT 3 号建筑

GPT-3 的架构由两个主要部分组成,编码器和解码器。编码器将句子中的前一个单词作为输入,并将其转换为向量表示,然后通过注意过程来生成下一个单词预测。解码器将前一个单词及其矢量表示作为输入,并输出给定这些输入的可能单词的概率分布。GPT-3 API 提供了一个通用的“文本输入,文本输出”接口,几乎可以用于任何语言任务。另一方面,大多数其他语言 API 都是为单一目的设计的,比如情感分类或命名实体识别。要使用该 API,只需向它提供一个文本提示(基于文本的输入或指令),它将返回一个文本补全,尝试适应您提供的上下文或模式。它可以通过写一个你想让它做什么的总结或者几个例子来编程。它的性能很大程度上取决于任务的复杂性。如果你有一个更具体的例子,你可能会期待一个更可靠的答案。随着 GPT-3 训练数据在 2019 年 10 月截止,API 可能不了解当前事件。

礼遇:OpenAI

GPT-3 核心组件

GPT-3 API 包含三个核心组件:提示、完成和令牌。“提示”是给 API 的输入文本。“完成”是 API 根据提示生成的文本。例如,如果您给 API 提示“Python 是 a”,它将返回“Python 是一种很棒的编程语言,因为它非常简单易学。”很有可能。默认情况下,API 是随机的,这意味着每次您调用它时,您可能会得到稍微不同的完成,即使提示保持不变。“令牌”可以被认为是单词的片段。API 在处理文本之前将其转换成标记。文本提示和 API 生成完成的标记组合必须少于 2048 个标记,即大约 1500 个单词。

**GPT-3 API 提供了不同的语言模型/引擎,它们在功能上有所不同。突出的典型有:达芬奇、居里、巴贝奇、阿达。**达芬奇被认为是最有能力的模特,阿达是最快的。OpenAI 建议使用 davinci 进行用例实验,并尝试其他生产模型,因为它们具有成本效益。达芬奇主要用于创意和分析输出。由于 davinci 的功能增加,它需要更多的计算资源。因此,它的每次 API 调用成本更高,并且不如其他引擎快。居里速度很快,擅长语言翻译、分类和总结。巴贝奇擅长语义搜索分类。Ada 是解析文本和简单分类的首选。

回答 API 端点

Answers 是一个专用的问答端点,适用于需要基于文档源、用户指南和技术文档生成高精度文本的应用程序。给定一个问题、一组文档和一些示例,API 基于这组文档中的信息生成问题的答案。您可以输入多达 200 个文件的列表,也可以预先上传不超过 1GB 的文件。通过 OpenAI 支持的 Python 绑定,您可以通过任何语言的 HTTP 请求与 API 进行交互。API 使用 API 密钥进行身份验证。

**Answers 端点对一组文档进行语义搜索。**如果您提交一个查询,例如一个自然语言问题或一个句子,返回的文档将根据它们与查询的语义相关程度进行评分和分级。“文档”可以是一组段落或几页。例如,如果您提供一个文档列表[“苹果是红色的”、“胡萝卜是橙色的”、“玫瑰是粉红色的”]并查询“哪一种是水果”,您将为每个文档获得不同的相似性得分。相似性得分越高,文档与查询在语义上就越相似。在这个例子中,“苹果是红色的”将与查询最相似。可以使用“Documents”参数将文档作为 API 请求的一部分进行传递。您可以使用“文件”参数来上传文件。文档作为文件预先上传,离线处理和索引一次。当使用保存的文件时,利用两步搜索过程来找到回答问题的最佳上下文。第一步使用传统的关键字搜索将所提供文件中的文档缩小到最多“max_rerank”(参数)个文档。然后,通过语义搜索分数对所选文档进行重新排序。

礼遇:OpenAI

下面是一个通过提供文档列表来获取答案的 Python 代码片段示例:

import os
import openai
openai.api_key =os.getenv("OPENAI_API_KEY")
doc_list= ["When you deploy Oracle Analytics Cloud, you complete some initial setup steps, and then Oracle takes care of most service management, patching, backup and restore, and other maintenance tasks. You determine the size of your service when you set up the service and you can increase or decrease capacity if your requirements change. Oracle Analytics Cloud offers two sizing options, you can specify the number of Oracle Compute Units (OCPUs) you want to deploy or how many people you expect to use the service."]
response = openai.Answer.create(
 search_model="davinci",
 model="curie",
 question="**What are the sizing options offered by Oracle Analytics Cloud?**",
 documents=doc_list,
examples_context="Larry Page and Sergey Brin.",
examples=[["Who founded Google?"]],
 max_tokens=30,
 stop=["\n", "<|endoftext|>"],
)
 print(response)

回应是:

{
  "answers": [
    "**You can specify the number of Oracle Compute Units (OCPUs) you want to deploy or how many people you expect to use the service.**"
  ],
  "completion": "cmpl-2xHoUMBCfmt0MyYvLso3n4PFtpEFM",
  "model": "curie:2020-05-03",
  "object": "answer",
  "search_model": "davinci",
  "selected_documents": [
    {
      "document": 0,
      "text": "When you deploy Oracle Analytics Cloud, you complete some initial setup steps, and then Oracle takes care of most service management, patching, backup and restore, and other maintenance tasks. You determine the size of your service when you set up the service and you can increase or decrease capacity if your requirements change. Oracle Analytics Cloud offers two sizing options, you can specify the number of Oracle Compute Units (OCPUs) you want to deploy or how many people you expect to use the service. "
    } ] }

用例

客户/技术支持 —支持主管可以使用此功能快速提供服务请求的相关答案。

开发者文档 —开发者没有耐心在技术指南的链接列表中寻找答案。该功能可以解析文档,并为他们的查询提供准确的答案,而不是指向相关页面。

上下文相关的帮助 —在软件中提供上下文相关的帮助是很常见的。但大多数时候用户会有次要问题,现在为此提供聊天机器人很常见。然而,聊天机器人提供的答案有限。因此,Answers API 可以有效地用来准确地回答用户的问题,而不是提供相关技术文档的链接。

示例答案应用程序

结论

著名的计算机科学家 Geoffrey Hinton 说过,“你所需要的是大量的数据和关于正确答案的大量信息,你将能够训练一个大的神经网络来做你想做的事情。”这正是 OpenAI 的 GPT-3 能够做到的。通过清楚地理解上下文来给用户提供正确的答案,将建立有益的用户体验。无需微调,GPT-3 可以高精度地执行搜索和回答问题的任务。这将彻底改变技术/客户支持领域。

参考

[1]语言模型是很少出手的学习者,OpenAI,https://arxiv.org/pdf/2005.14165.pdf

[2]https://en.wikipedia.org/wiki/GPT-3

冯·米塞斯的随机性公理在由数字序列描述的非平稳系统动力学预测中的应用

原文:https://towardsdatascience.com/application-of-the-von-mises-axiom-of-randomness-on-the-forecasts-concerning-the-dynamics-of-a-9693ce3d763e?source=collection_archive---------34-----------------------

TT 在 Pixabay 上的照片

使用冯·米塞斯的随机性公理作为一种关于预测非平稳系统发展的分析方法。

摘要:在本文中,我们将使用一个数字序列来描述一个非平稳系统的动力学,其中项的值在系统的自由度的数量内变化。这个数字序列允许我们使用冯·米塞斯的随机性公理作为一种分析方法,这种分析方法涉及从非平稳系统的演化预测中获得的结果。这个公理的含义如下:当我们理解一个数字序列的模式时,我们获得的结果是对下一个序列号的预测,它不能随机再现。在实践中,如果结果是通过随机算法或通过实现系统中存在的模式的认知算法获得的,则该公理定义了能够理解的统计方法。这种方法对于分析非平稳系统特别有用,非平稳系统的特点是产生非独立的结果,因此没有统计意义。非平稳系统最重要的例子是金融市场,因此,这种方法的主要应用是分析交易策略。

简介

为了应用冯·米塞斯的随机性公理作为一种分析方法,必须面对的第一个问题是能够用一个数字序列来描述一个系统的演化。为了这个目的,我们使用一个序列,其中项的值可以在系统的自由度的数量内变化。然后我们还引入了一个由一系列递增整数表示的时序级数。有了这两个序列,我们可以描述一个描述我们正在研究的系统演化的动力学。这样,我们可以应用冯·米塞斯的随机性公理,它定义了随机序列的统计特征。公理如下:“一个序列被定义为随机的本质要求在于,完全不存在任何可以被成功应用来改善关于下一个数的预测的规则”

这个公理告诉我们,当我们理解一个数字序列的模式时,我们可以获得结果,作为对下一个序列号的预测,它不能随机再现。知道我们的数字序列的值代表系统的动态,我们已经找到了一种方法来评估结果。因此,如果随机获得相同或更好结果的概率非常小(即,随着策略使用次数的增加而趋于零),这意味着预测方法使用系统中存在的规则。因此,我们可以使用发展的方法,以预测系统的演变;这种方法是研究非平稳系统的基础。事实上,如果我们处于非平稳状态,结果可能是非独立的,其结果是统计值的减少。为了用简单的方式解释这样一个复杂的话题,我创造了一个悖论[1],两个玩家在一个游戏中互相挑战,其中通过改变游戏规则,有可能从静止状态进入非静止状态。其后果是,开始时的数据是独立的,然后一旦发生变化,它们就变得相互依赖,因此对统计目的没有用处。

因此,在这些条件下获得的大量数据在统计学上对理解产生数据的方法是否是随机的没有用处。在这种情况下,预测算法的性能不再是可靠的数据;因此,我们需要找到一个新的统计指标,以评估结果。事实上,在金融领域经常发生这样的情况,一种策略尽管通过执行大量操作获得了出色的结果,但突然停止工作,产生了巨大的损失,以这种方式证明这是一种完全随机的策略。关于金融市场的非平稳性有很多研究;关于这个话题的一些文章有[2]、[3]、[4]、[5]。

通过数字序列描述非平稳系统的动态

我们首先考虑一个以两个自由度为特征的非平稳系统。我们做出这个选择有两个原因:第一,为了简化处理,第二,在有两个自由度的系统中,有金融市场,它代表了我们将要展示的方法的主要应用。

然后,我们将这个系统与一系列在 1 和 2 之间变化的值联系起来。在实践中,当在这个系统上,确定性过程的作用方向发生变化时,我们得到的结果是,继承的值相对于前一项发生变化。当我们谈到“确定性过程”时,我们指的是作用于系统的确定性和非随机力。我们还引入了这样的简化,即确定性过程作为一个恒定的力,在两个自由度的方向上强度没有变化;这允许我们用一个单独的数字序列来描述系统的动态特性。

最后,我们向系统添加一个时间度量;我们可以通过使用一系列递增的整数来做到这一点,这样,我的序列中的每个值都明确地对应于我的时间序列中的一个值。

得到的结果如下:我们有两个序列,一个描述作用在系统上的力的方向 F ,另一个描述时间的进程 T :

分析这两个序列,我们可以注意到作用在系统上的力(确定性过程)改变方向三次,第一次在时间 T = 8 ,第二次在时间T = 13**,第三次在时间 T = 20 。图 1 显示了由两个序列描述的动力学;图表中还包括了时间进程。**

图 1:由两个序列描述的系统动力学。

应用冯·米塞斯的随机性公理作为一种关于预测算法所得结果的分析方法

在这一点上,我们将应用冯米塞斯的随机性公理来评估预测算法所获得的结果。根据本文引言中报告和分析的公理,如果我们已经开发了一种非随机方法来实现系统中存在的模式,我们将能够预测继任期限相对于前一个期限的变化。

这个陈述是至关重要的,因为我们说只有关于系统变化的预测才是有用的,以便理解所使用的方法是否是随机的。事实上,如果我们在时间 8 和 12 对连续的两个项目进行两次预测,打赌连续的值是 2,我们获得两次成功。这两个结果是相互独立的吗?这个问题是基本的,因为只有当他们是独立的,才是有用的统计目的,以评估预测方法。

为了回答这个问题,我们必须使用我们报告的复合概率公式:

P(E1∩E2)= P(E1 | E2)P(E2)= P(E2 | E1)P(E1)(1)

两个事件 (E1,E2) 发生的概率,等于 E1 发生P(E1) 的概率,乘以给定 E1 发生 P(E2 | E1) 的条件概率 E2

看到公式(1)我们明白,条件概率 P (E2 | E1) 的正确计算取决于事件是否独立。事实上,在独立事件的情况下 P(E2 | E1) = P(E2) ,相反在事件完全相互依赖的情况下 P (E2 | E1) = 1 或 0。在我们的例子中,概率 P(E1) = 1/8 。事实上,以随机方式打赌在前 8 个时间位置内序列的值等于 2 的获胜概率是 1/8,因为在前 7 个位置中序列的值为 1,只有在第 8 个位置中序列的值为 2。在实践中,我们可以看到这个值,就像从一个装有八个弹珠的袋子中随机抽取一个值为 2 的弹珠的概率,其中七个值为 1,一个值为 2。因此, P (E1) 表示使用随机策略获得相同结果的概率。这样,我们就能从一系列有关系统动力学的数据中推导出一个概率。

现在我们试着计算 P (E2) 的值;研究从第九个位置到第十二个位置的时间间隔,我们看到系统保持不变。因此,在此时间间隔内,随机下注值 2 并获胜的概率等于 1。这是因为所有的位置都有值 2,所以随机策略以 100%的概率获得相同的结果。在这种情况下,我们可以将第二次下注视为完全依赖于第一次下注。这意味着,从统计学的角度来看,我们可以将这两次下注视为单次下注。

现在,让我们尝试将第二个赌注从第十二个位置转移到第二十个位置,在这种情况下,我们将有 P(E2) = 5/7 。事实上,在从第九个位置到第二十个位置的时间间隔中,有七个值等于 1,五个值等于 2。由于第二次预测中的这一变化,复合概率将等于:

P(E1∩E2)= P(E1)P(E2 | E1)= P(E1)P(E2)= 1/8∙5/7 = 0.089

在这种情况下,这两个事件是相互独立的,因此它们对于统计目的是有用的,以便理解所使用的方法是否可以预测系统的演化。

从这两个例子中,我们可以推导出以下结论:当我们想要研究一个确定性过程时,每当它决定系统中的一个变化时,我们就有可能对系统的演化执行一个预测,其采用随机策略的成功概率被证明是微小的 1。这涉及到复合概率的降低,根据冯·米塞斯的随机性公理,复合概率的含义是表明作用于系统的确定性过程的存在。

在实践中,每当确定性过程改变系统状态时,我们可以通过预测来检测它。因此,为了以低误差检测确定性过程,有必要在系统中产生统计上显著数量的变化。对此我们的预测方法进行了大量的独立预测。因此,每一次预测都必须关注系统的单一变化。

这种方法对于预测可能不独立的非平稳系统至关重要。因此,计算复合概率是非常重要的,复合概率是以随机方式获得相同结果的概率。我们还记得,在复合概率(1)中,有条件概率的计算,如果事件是相互依赖或相互独立的,则考虑条件概率。其实这个方法的目标是抛弃所有非独立预测,其贡献是让我高估了算法的预测技巧。这种错误评估的后果可能会导致我们(例如在金融领域)承担过度的风险。

结论

对结果的分析,涉及一个非平稳系统的发展,代表了应用数学中尚未解决的最重要的问题之一。在这篇文章中,我建议使用冯米塞斯的随机性公理作为评估在这些条件下获得的数据的方法。如前所述,这个公理定义了一个统计特征,当我们有意识地操作时,这个统计特征假定了一个明确定义的行为。在实践中,由非随机预测算法生成的结果不能随机再现,该算法实现了关于所考虑系统的知识。因此,随着策略使用次数的增加,获得相同或更好结果的概率随机趋于零。这样,我们就把注意力从非平稳条件下可能由非独立预测产生的结果值转移到与其非随机行为相关的统计特征上。

为了应用这种方法,我们定义了一个简单的数学模型,它的任务是通过一个数字序列来描述一个系统的动力学。使用这个数学模型,我们已经展示了如何从预测算法获得的一系列结果中计算复合概率。然后,通过分析一些例子,我们得出了一些重要的考虑因素。

特别是,我们已经看到,当系统发生变化时,我们有可能做出成功概率小于 1 的预测。这样做的结果是复合概率的减少,根据冯米塞斯的随机性公理,复合概率的意义是表明系统中模式的存在。

因此,如果我们想以较低的误差检测它,我们必须对系统的每一个单独的变化作出统计上显著数量的独立预测。这样,预测算法证明能够预测未来的系统演变。因此,这一特征成为评估在非稳定系统(如金融市场)上运行的策略的风险的基础。事实上,正确的风险评估必须始终考虑算法对系统未来发展的预测技巧。

在下一篇文章中,我们将在一个非平稳系统的动态中引入一个纯粹的随机成分。这样,确定性过程将被用概率密度函数描述的随机过程所取代。为了在金融领域应用这种方法,这一步是必不可少的。

参考文献

[1] Berdondini Andrea,《职业交易者的悖论》,(2018 年 11 月 20 日)。可在 https://ssrn.com/abstract=3287968[SSRN](https://ssrn.com/abstract=3287968)买到。

[2]j . Barkley Rosser Jr .‘重新考虑遍历性和基本不确定性’。载于:《后凯恩斯经济学杂志》第 38 期(2015 年第 3 期),第 331–354 页。doi:10.1080/01603477.2015 . 1070271(1)。

[3]苏厄尔先生。“有效市场假说的历史”。伦敦大学学院计算机科学系技术报告,2011 年 1 月。

[4] O .彼得斯,m .盖尔曼。“使用动力学评估赌博”,《混沌:非线性科学跨学科期刊》,2016 年;26 (2): 023103

[5] Berdondini,Andrea,“来自经济物理学的方法论描述作为财务战略的验证技术”,(2017 年 5 月 1 日)。可在 https://ssrn.com/abstract=3184781[SSRN](https://ssrn.com/abstract=3184781)买到。

ROC 曲线不同部分的应用

原文:https://towardsdatascience.com/applications-of-different-parts-of-an-roc-curve-b534b1aafb68?source=collection_archive---------6-----------------------

理解 ROC 曲线不同部分的重要性,探索 ML 应用的 AUC 变量

作者图片

介绍

受试者工作特性( ROC )曲线是诊断二元分类器能力的最常用图形工具之一,独立于固有的分类算法。几十年来,ROC 分析已经用于许多领域,包括医学、放射学、生物统计学、自然灾害预测、气象学、模型性能评估和其他领域,并且越来越多地用于机器学习和数据挖掘研究[1]。如果你是一名数据科学家,你可能每天都在使用它。

ROC 曲线是通过绘制真阳性率(TPR)对假阳性率(FPR ),基于不同模型分数设置下的二元结果而创建的。理想的分类器将在非常低的 FPR 下给出非常高的 TPR 值(即,它将正确地识别阳性而不会错误地标记阴性)。但是,理想的模型很少存在,因此,尝试不同模型和机器学习实验的目标是确定一个最佳模型,该模型将给出最佳的成本(FPR)/收益(TPR)权衡。ROC 分析有助于实现这一目标。

ROC 曲线下的总面积(AUC)

通常,(并且在“几乎所有”情况下),我们使用 ROC 曲线下的总面积( AUC )作为评估标准来找到最佳分类器。AUC 也是大多数 Kaggle 比赛中使用的评估标准。这是因为 AUC 是一个单一的数字,它提供了各种阈值设置下整体模型性能的平均信息。关于 AUC 的一个有趣的性质是,它独立于类别分布,即无论类别分布是 10%/90%还是 50%/50%,基于 AUC 的模型的排名将是相同的。(注意我提到的“有趣”,并不总是好的属性。有用性取决于手头的问题。在某些情况下,这个属性可能不好,数据科学家转而使用精确-召回曲线下的面积。但是让我们把这个放在一边)。AUC 在大多数情况下是信息性的和有用的,但是有许多现实世界的问题,在这些问题中,它未能提供对模型能力的正确关注,而在投入生产之前需要理解该模型的能力。在许多实际应用中(下面讨论的例子),ROC 曲线的主要部分几乎没有用,但 AUC 总结了整个曲线,给予所有区域同等的重要性,包括不相关的区域。

应对上述 AUC 挑战的常用解决方案是确定一个 FPR(或 TPR)截止点,并找到在该截止点最大化 TPR(或最小化 FPR)的模型。但是在任一轴上固定截止值需要业务方面的具体要求,并且在输出值上具有更高的方差。这不是一个理想的解决方案,它忽略了 ROC 曲线的大部分操作范围。也很难查看各种指标的网格来比较模型,例如 TPR @ 1% FPR、2%FPR、10% FPR、20% FPR 等。

简而言之,我们认为完全 AUC 在某些情况下存在问题,查看固定的截断点集并不是这些问题的理想解决方案。建议的解决方案介于两者之间,即,我们不使用所有可能的 FPR/TPR 值,而是在一组重要的点上进行聚合。这可以通过 ROC 曲线下的部分面积来实现。

在剩下的帖子中,让我们来了解一下文献中发现的部分面积度量的不同变体,如何计算每个变体以及何时使用它们。但首先,让我们从一些 AUC 未能强调部分面积指标需求的例子开始。

完全 AUC 问题示例

  1. 当类别分布高度不平衡时(例如 0.1%/99.9%,用例:欺诈检测、癌症检测等。),不同的分类器即使被正确排序也会在 AUC 值上表现出非常小的差异,使得模型比较变得困难。例如,您可能正在比较像 0 这样的数字。AUC 中 9967 vs. 0.9961 并且认为对比的两个模型相当相似。实际上,这两个模型在用于实际决策时可能会有显著的差异。
  2. 如果只有 1 个肯定和 999 个否定,即使是一个像样的模型也能够通过错误分类一小部分否定(FPR,假设 20%)来捕捉 100%的肯定(即 1)。在这种情况下,比较高 FPR 范围(例如> 20%)的面积是没有用的,因为**所有模型在高 FPR 区域都达到了 100%的 TPR。**但是 AUC 仍然使用高 FPR 无用区域。在这种情况下,理想情况下,我们希望找到一种模型,它可以通过错误分类非常低%的否定来识别很大%的肯定。
  3. 在临床实践中,具有高 FPR 的诊断测试导致显著的经济费用,因为很大比例的非患病候选人将耗尽医学治疗的稀缺资源。此外,在诊断致命疾病时,未能正确识别严重患病受试者(低 TPR)将导致严重的伦理后果。因此, FPR 和 TPR 需要同时分别维持在低和高水平;以便从 AUC 中排除不经济和不道德的区域。[2]
  4. 在使用乳房 x 光片检测良性与恶性癌症时,识别恶性癌症比对良性疾病进行不必要的活检更重要。即高 TPR ≥ 0.9 是感兴趣区域。完整的 AUC 可能不会显示不同模型之间的显著差异,即使它们在感兴趣的区域有所不同。
  5. AUC 警告的另一个经典例子是两个分类器的比较,这两个分类器具有彼此交叉的非常不同的 ROC 曲线。一个具有高 TPR,另一个具有高真阴性率(TNR),但两者具有相同的 AUC。在这种情况下,AUC 无法选择更好的分类器。[6]

**边注:**顺便说一句,这里有一个有趣的琐事,以及 ROC 曲线中“”这个词的由来。ROC 在二战期间首次被用于分析雷达信号,更具体地说,被美国陆军用于从他们的雷达信号中探测日本飞机。通过观察屏幕,雷达接收机操作员必须手动决定屏幕上的一个光点是代表敌机还是某种噪音。他们做出这些区分的能力被称为受试者操作特征。

ROC 曲线(图片作者

ROC 曲线的不同部分(图片作者

左边的 ROC 曲线表示被训练来分类二元结果的样本分类器的性能。该分类器的 AUC 是 0.969。

注:AUC 范围从 0 到 1,在 0.5 之间对称。即 AUC 为 0.5 是最差的分类器。AUC 在 0 和 0.5 之间的分类器预测与被评估的类别相反的类别。

现在,我们将了解如何使用同一 ROC 曲线的不同区域(如左侧突出显示的区域)来计算不同的部分指标。

ROC 曲线下部分面积的变量

局部区域度量并不新鲜。我最后引用的一些论文发表于 80 年代。我写这篇文章的目的是重温它们,并引起您对处理实际生产问题时可能遇到的常见场景的注意,尤其是在数据科学竞赛的世界之外。

1。暂停

定义:低于某一 FPR 的 ROC 曲线下归一化面积。[4]

pAUCcn @ <10% FPR. Green area divided by red area. (A/B) (图片由作者提供)

**优点:**关注 ROC 曲线最左边部分对高 TPR 的需求;对高度不平衡的数据最重要的区域。

缺点: pAUCn 缺乏 AUC 在 0.5 左右所享有的对称性。pAUCn 没有固定的下限,因为下限随着所选择的 FPR 阈值而变化。

2.矿泉

**定义:**主对角线以上 ROC 曲线下标准化部分面积。[7]

sPA @ <10% FPR. 0.5 * (1 + A/(A+B))(图片由作者提供)

**优点:**关注 ROC 曲线最左边部分对高 TPR 的需求。对于随机分类器,其被缩放以给出 0.5 的值,即,给出固定的下限,而不考虑 FPR 截止。 缺点: sPA 也缺乏对称性,0.5 以下没有意义。其下限不为 0。

3.个人事故保险(Personal Accident Insurance 的缩写)

**定义:**局部面积指数。特定 TPR 以上 ROC 曲线下的面积。换句话说,TPR 临界值和 1 之间的所有 FPRs 值的 TPR 平均值。[3]

PAI @ >70% TPR。绿色区域除以红色区域。(A/B) (图片由作者提供)

**优点:**关注高 TPR 需求,不考虑 FPR。在一些临床实践中,如早期癌症检测,如果高 FPR 意味着更好的捕获率,是否可以容忍。

**缺点:**下限不是固定的,并且随着 TPR 截止值而变化。相对于其他部分面积度量,缺少大约 0:0.5 和 0.5:1 的对称性。

4.tpAUCn

**定义:**ROC 曲线下低于一定 FPR、高于一定 TPR 的双向部分区域。[2]

tpAUCn @ <10% FPR & > 70% TPR。绿色区域除以红色区域。(A/B) (图片由作者提供)

**优点:**允许同时控制 TPR 和 FPR。许多用例需要高 TPR 和低 FPR。例如使用昂贵测试仪器的致命疾病的诊断。

**反对意见:**用户应该了解 TPR 和 FPR 临界值。

5.pAUCcn

**定义:**ROC 曲线下一致的部分面积。ROC 曲线下垂直部分面积(pAUCn)和 ROC 曲线下水平部分面积(pAUCxn)之和的一半。[5]

pAUCcn @ >10% FPR & <20% FPR. Green area divided by red area (图片由作者提供)

**优点:**比例与 sPA 相似,给随机分类器 0.5 分。与其他局部面积测量不同,这种方法认为 AUC 与平均 TPR、平均 FPR 和 c 统计有类似的关系。

**缺点:**其下界不为 0。

【后作者注:关于 pAUCcn 利弊的知识需要根据 ROC 曲线下一致部分面积作者的最新论文进行更新。基于新的归一化方法,cons 是无效的,并且具有类似于 AUC 的对称性。我将很快做出必要的改变。】

6.HAUC

**定义:**Half-AUC,AUC 分两半测量,当 TPR > TNR 或 TPR < TNR 时。当已知需要高灵敏度(TPR)还是高特异性(TNR)时,用于比较分类器。两半之和等于完全 AUC。[6]

HAUCsp: A+0.5C,HAUCse:B+0.5c . HAUCsp+HAUCse = AUC(图片由作者提供)*

**优点:**不需要就 FPR 或 TPR 停产做出决定。临界值是从 ROC 曲线中 FPR = 1 - TPR 的点自动得出的。唯一需要决定的是你需要高 TPR 还是高 TNR。范围从 0 到 0.5,0.25 随机。

**缺点:**当截止点已知且不同于 FPR = 1 - TPR 时,没有用。

比较研究

我用一个合成数据的比较研究来说明部分 AUC 的有用性。可能还有其他几个例子我没有在这篇文章中演示(长话短说)。这些例子包括:A)具有高 AUC 但在低 FPRs 下表现不同的两个分类器的比较,B)具有相同 AUC 但在高 TPRs 下表现不同的分类器,C)具有相同 AUC 但灵敏度或特异性水平不同的交叉 ROC 曲线,以及更多…

两个分类器具有相似的高 AUC,但在低 FPR 时性能不同

在下面的例子中,我使用 sklearn 的 make_classification 方法准备了一个合成数据集。为了强调标签分布高度不平衡的数据场景,我将标签比率保持为 99%/1%。然后我训练了两个分类器,一个用 LogisticRegression (LR)训练,另一个用 CatBoostClassifier (CB)训练。

线性和对数标度的 ROC 曲线。对数标度强调低 FPR 时的差异。(图片作者

我们看到 LR 和 CB 的 AUC 非常接近(99.78%对 99.96%)。事实上,线性标度 ROC 曲线看起来非常相似。有人可能会认为 CB 的排名很好,但并不比 LR 好多少。但是在对数标度上看到相同的曲线(右侧)强调了在低 FPRs 下的差异(<1%). Let’s see what would a partial area give.

Emphasis on partial area under curve below certain FPR. FPR at log scale. (图片由作者提供)

在 1% FPR 的截止值处的 pAUCn 显示了 LR 和 CB 之间的相当大的性能差异(89.9%对 97.9%),表明 CB 做得更好。

这种情况的一个例子(除了前面提到的癌症检测)是训练一个分类器,用于从在线认证中检测账户泄露。设想一个模型,该模型检测登录是来自攻击者(受损的)还是来自原始帐户持有人。考虑到在线活动的巨大规模,实际上只有极小比例的登录会受到影响(例如万分之一)。您可以使用该模型将高风险登录放入人工调查队列中。由于很大的不平衡,在 FPR 低至 1%的情况下,您将调查 100 个良好的登录,以努力找到 1 个折衷方案。由于调查中产生的费用,您的业务需求可能会限制您只调查所有登录中的 X%,从业务角度来看,将 X% FPR 以上的区域视为无用。

包含创建图和计算部分指标的代码的笔记本

https://github.com/groverpr/Machine-Learning/blob/master/notebooks/11_Applications_of_different_parts_of_an_ROC_curve.ipynb

结论

AUC 通常被用作一个汇总指标,根据 TPR 和 FPR 在不同阈值下的准确性,指示二元检验的总体表现。更简单地说,它是所有可能的 FPR 上的平均 TPR 和所有可能的 TPR 上的平均 FPR(本质上是对称的)。AUC 在大多数情况下是信息性的和有用的,但是存在许多真实世界的问题,其中 ROC 曲线的许多部分是不感兴趣的,并且 AUC 未能聚焦于重要的区域。有时,您感兴趣的区域低于某个 FPR,有时高于某个 TPR,或者其他时候同时受 FPR 和 TPR 的限制。有时特异性(TNR,1-FPR)比敏感性(TPR)更重要,有时反之亦然。在所有这些情况下,部分 AUC 测量做了正确的事情,集中在 ROC 曲线的重要区域。这篇文章提供了一些文献中的局部面积测量,帮助我们将它们可视化,并总结了它们的优缺点。

复习学分: 什卡·古普塔

参考文献

我上面提到的度量标准基本上是从过去写的几篇关于部分 AUC 的论文中挑选出来的。

  1. 维基百科:https://en . Wikipedia . org/wiki/Receiver _ operating _ character istic
  2. 杨 H,陆 K,吕 X,胡 f .双向偏 AUC 及其性质。
  3. 姜 Y,梅斯策,西川 RM。高灵敏度诊断试验用的接收机工作特性局部面积指数。
  4. 沃尔特·SD。汇总 ROC 曲线下的部分面积。
  5. Carrington 等人。机器学习算法评估中不平衡数据的新的协调部分 AUC 和部分 c 统计。
  6. 布拉德利美联社。用于评价敏感或特异分类器的半 AUC。
  7. 马等利用 ROC 曲线下部分面积评价诊断性能。

时频表示在语音处理中的一些应用

原文:https://towardsdatascience.com/applications-of-time-frequency-representations-in-speech-processing-cfa28ec1f251?source=collection_archive---------21-----------------------

从计算机视觉到 Mel 声谱图的音频处理

频谱图无疑是用于分析的音频信号的最合适的表示。它不仅符合我们通过频率分解对声音的理解,还允许我们使用二维分析架构。鉴于过去十年计算机视觉的巨大进步,将声音表现为图像的可能性打开了许多选择。本文将分析计算机视觉技术应用于音频处理的相似性和局限性。重点放在语音识别和语音合成上。

“我们是否正在走向一个人工智能能够在各方面超越我们的未来?那么答案是肯定的。”埃隆·马斯克——谱图由作者生成。

计算机视觉是当前机器学习中最热门的研究领域之一。然而,音频处理已经能够在视觉分析中突破性的深度学习技术的浪潮中冲浪。事实上,音频信号可以很容易地表示为二维数据。这种形式被证明更适合信息提取。顾名思义,时频表示通过时间和频率的离散化来显示音频信号。因此,大多数操纵图像的神经网络结构可以用于音频处理。就像人类大脑如何激发神经网络一样,Mel-Spectrogram 旨在显示我们如何听到信号。这种特定的时间-频率表示考虑了我们听觉的非线性尺度。本文重点介绍 Mel-Spectrogram 和深度学习技术在改善语音处理方面的一些应用。我们将看到如何生成特定文本的类似人类的语音,以及计算机如何理解我们正在说的话。这样的任务使用像序列对序列(听、听和拼)、连接主义时间分类和因果卷积这样的体系结构。最后,我们将讨论计算机视觉和机器听觉之间相似性的局限性。

如何可视化声音

声音信号或波形是由气压的变化产生的。麦克风和扬声器通过振动膜与声波接触。我们很快就明白声音是一维数据。在其最自然的形式中,音频信号被表示为随时间变化的强度。然而,这并不能很好地表达信号中包含的信息。事实上,频率是构成声音的关键特征。信息片段被编码在频率和振幅随时间的演变中。时间-频率(TF)表示是对声音进行分析其嵌入数据的适当表示。最常见的 TF 表示是光谱图。声谱图由像素组成,这些像素描述了与特定时间步长的频率范围相关的振幅。时间位置在 x 轴上,而频率仓在 y 轴上。像素越亮,相关频率的能量越高。频谱图不仅是可视化音频信号的好方法,而且易于计算。多年来,已经开发和优化了多种技术,傅立叶变换是最常见的。

由于声谱图的二维性质,计算机视觉技术可以应用于音频处理。有许多应用:音频分类、音频分离和分段、音乐流派分类和标记、音乐生成、音乐转录、语音识别、语音合成和语音识别。此类应用不胜枚举,但在本文中,我们将重点讨论后两种应用。一种特定类型的声谱图,或者更具体地说是一种标度,特别适合于语音处理。人类听到频率的方式被称为音高。人类的听觉频谱并不平坦。我们不能线性地感知频率。低频比高频更容易辨别。Mal 标度考虑了我们听觉的对数反应。该标度基于经验公式,使得声谱图 y 轴上的距离与我们在不同声音中感知的距离频率相匹配。此外,人类对声音振幅的感知也不是线性的。我们再一次听到响度的对数。分贝标度考虑了这一特性。Mel 谱图通过将 y 轴变形为 MEL 标度和分贝标度作为像素强度值来实现这两种表示。

语音识别

经典方法

语音识别无疑是公众最熟知的机器学习技术之一。语音助手正在成为所有平台的主流:扬声器(亚马逊 Echo)、游戏机(Kinect)、手机和智能手表(Siri、谷歌助手)等。虽然语音识别已经研究了几十年,但由于深度学习的兴起,它只是在最近才变得足够准确,可以用于现实世界的应用。

首先,让我们看看建立语音识别系统的经典方法。这种方法基于检测声谱图中的音素和它们的组合来形成基于模型的句子。如图 1 所示,三个模型组成了语音识别管道,以将语音特征***【X】***匹配到句子 Y :

  • 语言模型定义了给定单词序列在句子中出现的概率。这种模式是基于对特定语言的语法和习惯用法的研究。n 元模型非常适合有限的语音输入数据。
  • 发音模型为给定的句子生成一系列音素。这个步骤通常使用语言学专家定义的查找表来执行。
  • 声学模型最终定义了给定音素的发音。它产生与从录音中提取的语音特征 X 相比较的数据。光谱图通常用作特征集。声学模型被构建为高斯混合模型。

给定这样的模型,通过推断从分析的记录中提取的特征来执行识别。给定数据 X ,系统通过最大化其概率找到句子 Y

图一。经典语音识别管道[1]

多年来,该模型的组件一直使用深度学习进行优化。神经网络的使用大大提高了模型的整体准确性。典型的完全优化的流水线使用神经语言模型、神经网络发音模型和混合 RNN-HMM(递归神经网络-隐马尔可夫模型)声学模型。甚至使用 CNN(卷积神经网络)增强了特征提取。

然而,每个学习模型都被独立地训练来执行组件应该做的特定任务。管道成员的模型误差可能无法很好地协同工作——这一过程中的复合误差限制了性能。此外,人性化设计的特征管道直观且易于训练。尽管如此,它可能不是为任务优化的,这导致了性能限制和对更高计算资源的需求。这些原因推动了端到端模型的发展。整个识别过程被训练成一个单一的大组件。我们现在来看两个最流行的:连接主义时态分类和序列对序列。

联结主义时间分类

如果你曾经说过‘嘿,谷歌’,这个模型肯定已经分析了你的声音。Connectionist 时态分类(CTC)模型[2,3,4]目前在 Google 和百度的多个产品中使用。

同样,目标是从输入的声谱图 X 中推导出句子 Y 。但是现在,每个输出记号 {Y(t),t ∈[1,L]} 代表一个字母,每个输入记号 {X(t),t t ∈[1,T]} 是声谱图的一个子部分。请注意,输入光谱图标记可以重叠并具有任意大小。唯一的要求是T>L;输入标记必须比句子字符多。然而,由于两个主要原因,不能将输入与输出令牌直接匹配。首先,正如我们之前看到的,声音与字母无关,而是与音素相关。并且可以为不同的字母组合产生给定的音素。因此,语境对于识别产生给定音素的字母是至关重要的。其次,我们的输入记号的时间分辨率不一定与说话者给定字母(或音素)的发音持续时间相匹配。在多个连续的输出标记中会(并且应该——为了避免遗漏)推断出相同的字符。图 2 所示的 CTC 结构解决了这两个问题。

图二。连接主义时态分类结构[1]

图 3。CTC 模型对“他的朋友”发音的结果[1]

频谱图被输入双向递归神经网络。每个时间步长都考虑了它的时间邻居,这允许考虑发音上下文。预测的每一帧由最可能字符的分布函数描述, Y(t) = { s(k,t),k ∈字符图} 其中 s(k,t) 是在时间步长 t 出现字符 k 的对数概率。字符映射表可以由字母、数字、标点符号、空白字符甚至完整的单词组成。现在,我们可以通过选择 picks 字符来计算每个字符在一段时间内形成句子的概率——参见图 3。

CTC 结构经常与语法和拼写错误的单词作斗争。在 CTC 培训中集成一个简单的语言模型极大地改进了该模型[2]。

序列对序列

序列到序列(Seq2Seq)模型在语言处理中用于改变对象的表示域。这种表示可以是用于语音翻译的特定语言或者用于图像字幕的图像和文本。在我们的例子中,我们希望以文本形式表达一段语音记录。Seq2Seq 型号由两个主要组件组成,一个编码器和一个解码器。给定我们的应用领域,让我们分别称之为听者和拼写者。

图 4。LAS 模型结构[5]

listen 操作通常使用双向长短期记忆(BLSTM) RNN 来压缩来自语音频谱图的信息。卷积层也被证明在提高精度方面非常有效[7]。这一步对于优化信息的表现和消除音频中的噪声至关重要。听众提取的特征向量 h 通常比音频记录的初始持续时间小八倍。这种降低的分辨率对于加速学习和(最重要的)推理过程至关重要。注意,给定网络的循环结构,时间顺序保持不变。

然而,特征向量对于拼写者提取句子来说太长了。它不知道在哪里找到相关信息来推断与特定时间步长相关联的字符。需要注意机制来将分析集中在输入的适当时间窗口上。这个操作被添加到我们的解码器中。该模型现在被描述为一个听、听、拼(LAS)神经网络[5,6]。解码器通过聚焦于最可能感兴趣的区域,一次一个字符,顺序地运行特征表示 h 。注意,这种注意力机制很好地解决了在 CTC 中重复字符检测的问题。

使用基于注意力的 LSTM 传感器来计算注意和拼写操作。来自 y(i) 输出的字符的概率分布是解码器状态 s(i) 和注意上下文 c(i) 的函数。此外,当前状态 s(i) 还依赖于先前状态 s(i-1) ,先前最可能的字符 y(i-1) ,以及先前上下文 c(i-1) 。这种错综复杂的关系清楚地显示在图 4 中。

语音识别方法比较

图 5。[12]中比较的模型的各种测试集的 WERs (%)

图 5 显示了多个语音识别系统的单词错误率(WERs)。特别是,CTC-grapheme 模型是基于第三部分所示的第一种架构,Att。[1,2]-图层 dec。使用 LAS 技术。相关论文[12]中提供了实施细节和培训流程。

WER 是音频语音识别领域中使用的常用评估度量。它使用 Levenshtein 距离比较模型输出和地面真实情况。可以简化为 WER = (S+I+D) / N 其中 S 为替换数, D 为删除数, I 为插入数, N 为地真中的字数。性能最好的模型是使用两个解码器(或拼写器)层的 LAS。然而,考虑到额外的计算成本,使用单个拼写器的模型的准确性改进是微小的。乍一看,似乎基于 CTC 的模型甚至不可用。然而,这里给出的实现不包括语言模型。这是可以理解,因为所有模型都将受益于这种附加组件。但是正如我们前面看到的,CTC 模型的主要问题是它们的语法不正确。输出仍然是可理解的,但是从与参考文本的原始比较来看,表现很差。Graves 等人[2]通过将三元模型语言模型添加到基于 CTC 的模型中,达到了 6.7%的 WER。请注意,这种准确性很难与图 5 中的结果相比,因为它们不共享相同的训练过程。

语音合成

现在我们知道了虚拟助手是如何理解我们所说的话的,让我们看看它是如何回复的。语音合成或文本到语音(TTS)传统上使用两种主要策略构建:拼接和参数化。拼接方法将大量数据库中声音拼接在一起,生成新的可听语音。参数方法(HMM)使用由语言专家定义的表示复杂语言-听觉特征的模型。然而,这两种方法都不灵活,听起来像机器人。类似于语音识别领域,TTS 已经被神经网络增强。

最先进的深度学习方法已经显示出很好的效果。一些模型生成的语音可能会被误认为是真人的话。目前主要有两个 TTS 框架:百度研究院的 DeepVoice3 和 Google 的 Tacotron2。两种模型都使用 MEL 谱图作为管道中的中间表示。从输入文本生成声谱图,然后从元表示合成音频波。谱图生成过程总体上使用前面在语音识别部分看到的相同的 seq-2-seq 技术。此外,两个系统都使用音频合成器 WaveNet [9,10]。我们现在将关注 WaveNet 及其与应用于图像处理的深度学习技术的相似性。

波网结构

WaveNet 是给定 MEL 谱图的原始音频波形的生成神经网络。有人可能会说,一个简单的傅里叶逆变换可以更有效地完成类似的任务。不幸的是,从文本生成的频谱图目前还不足以产生听起来真实的语音。鉴于输入的 2D 性质,计算机视觉领域的深度学习技术是执行手头任务的一大灵感。自动回归模型、自动编码器和生成对抗网络(GANs)是执行图像生成的流行模型。WaveNet 实际上是基于 PixelCNN 架构,一种自动回归模型。

WaveNet 成功的关键是因果卷积(CCs)。这是一种适用于 2D 时间数据(如光谱图)的卷积。它确保模型保持输入的时间顺序。与经典卷积相反,在给定时间步长 t 发出的预测概率 p(x(t+1) | x(1),…,x(t)) 不依赖于下一个输入令牌,如图 6 所示。此外,CCs 不存在复发连接。因此,他们通常比其他 RNN 模型训练得更快。

图 6。因果卷积层堆栈的可视化[9]

然而,CCs 模型需要许多层来增加输入序列的感知场。例如,图 6 所示模型的接收场只有 5。在具有高时间分辨率的声谱图上,单个音素可以跨越数百或数千个时间步长。网络扩张解决了这个问题。这使得输入数据有很大的跳跃,所以我们有一个更好的全局视图。这个概念通过视觉类比很容易理解。这类似于扩大视野以看到整个景观,而不仅仅是照片上的一棵树。如图 6 所示,每增加一层,视野就扩大一倍。这种方法可以在不增加网络中数据分辨率的情况下产生巨大的感受野,从而保持计算效率。

图 7。膨胀因果卷积层堆栈的可视化[9]

WaveNet 训练和加速

然而,训练和推理过程的计算量非常大。它通常需要 10 到 30 个小时的个人语音训练数据。该模型必须为每秒的连续音频镜头产生 16 到 24,000 个样本。不幸的是,由于 CCs 架构是顺序生成的,因此要求逐个生成样本。这是对计算能力的巨大浪费,因为今天的显卡依赖于大量的并行计算。波形必须一次产生,利用并行计算单元来加速计算。WaveNet 的最新变体[10]从白噪声开始,并随着时间的推移对其进行改变,以将其变形为输出语音波形。变形过程在整个输入数据上是并行的。这种网络的训练需要另一个预训练模型。第二个模型,老师,是一个经典的序列波网。并行网络试图模仿教师网络的工作,同时更加高效。这可以通过在训练期间将教师放在学生前面(见图 8)来实现,这样反向传播算法就可以调整学生的参数。这种训练方法叫做概率密度蒸馏

图 8。波网概率密度蒸馏综述[10]

这种方法相对类似于 GANs 的训练:一个学生模型试图愚弄老师,而老师试图更好地区分假输入和真输入。然而,在这里,学生试图模仿而不是愚弄老师。注意,只有推理过程被加速,因为训练方法需要初始的完全训练的顺序模型。但是计算效率的提高对于推理来说是巨大的。波形生成比它的前身快一千多倍。实时生成现在在大多数硬件平台上都是绝对可行的,甚至是智能手机或者像 Google Nest 这样的家庭助手。

波网性能

评估生成模型是困难的。事实上,在给定输入的情况下,不可能将模型的输出与预期结果进行比较。根据定义,生成模型创造了新的、看不见的东西。WaveNet 也不例外。平均意见得分(MOS)是语音合成的有效验证度量。MOS 是一个描述声音样本如何作为自然人类语音传递的数字。它是通过人工评级服务获得经验值。Tacotron 2 [11]使用 WaveNet 从 seq2seq 模型生成的声谱图中合成音频,产生了很好的结果。这款机型的 MOS 为4.526±0.066,真正的专业录音达到4.582±0.053。此外,谷歌还进行了一项用户研究,让人们听 Tacotron 2 样本和专业语音解说员,并猜测哪一个更自然。你可以自己试试这个测试,在论文链接的以下网页上听听合成的样本[11]:【https://google.github.io/tacotron/publications/tacotron2】。图 9 显示了研究结果。

图 9。合成与地面真相:100 个项目的 800 个评级[11]

大多数时候,人们无法区分合成的和基础的真相样本。记录了一个微小但具有统计学意义的真实镜头趋势。这主要是因为专有名词和生僻词汇的发音错误。

计算机视觉应用于音频处理的局限性

现在,我们已经讨论了几种能够极大改善音频处理的深层架构。这些架构中的大多数最初是为了解决计算机视觉问题而开发的。然而,声音的时间-频率表示允许将声音视为 2D 数据,类似于图像。更具体地说,卷积层非常有用。然而,一些谱图特征可能对这种技术的进步产生实质性的限制。

首先,声音是透明的。视觉对象和声音事件不会类似地累积。声谱图中的黑暗区域显示没有声音,而它只是对应于图像中的黑暗物体或阴影。除了反射或透明表面,图像中的像素通常属于单个实体。相反,离散的声音事件叠加在一起成为一个独特的整体。不能假定给定时间步长的频率仓属于具有相关像素幅度的单个声音。它可以由任何数量的累积声音产生,甚至是复杂的相互作用,如相位抵消。在图片中容易分离对象的地方,分离重叠的声音并不方便。

第二,声谱图的轴与平面图像的轴不具有相同的含义。图像的轴描述了物体的投影位置。平移或旋转不会改变图片中主体的性质,只会改变它的位置。这就是 CNN 如此强大的原因。卷积层允许将对象的性质与其在图像中的位置分离开来。如果一只狗在图片的左上角或右下角,它仍然是一只狗。然而,声谱图沿 y 轴的平移会改变声音的频率。由于两个轴的不同含义,声谱图的旋转没有任何物理解释。正如我们在因果卷积的研究中所看到的,谱图的时间方面对模型的结构进行了一些修改。

第三,声音的频谱特性是非局部的。图像的相邻像素属于相同的对象或形成边界。然而,声音却不是这样。虽然声音的基频是局部的,但它的音色却不是。声音的音色是由 y 轴上的所有谐波组成的。这些谐波频率在声谱图中不是局部接近的,而是形成相同的声音,并且根据共享关系一起移动。

结论

现在,您应该对神经网络上下文中的时频表示的优势有了更好的理解。我们在这里描述的几个架构仅仅提供了可能的应用的一瞥。然而,CTC 和 seq2seq 架构封装了经典计算机视觉模型如何适应音频处理,关键区别在于频谱图的时间性质。此外,这些方法是通向更高级应用的垫脚石,如声音克隆。

毫无疑问,计算机视觉对于大多数二维数据的分析是一个巨大的启发。它对音频处理产生了重大影响,因为它允许端到端管道,而无需创建全新的架构。然而,光谱图的年代性质是图像和音频之间的一个严重障碍。虽然基于卷积的网络显著提高了精度,但这种层不适合光谱图。如 IV 所示,新技术带来了根本性的不同。计算机视觉应用于音频处理的局限性。语音相关的 2D 卷积为基础的技术公认达到他们的桃性能。剩下的误差百分比肯定是由于光谱图的不寻常的特性造成的。

感谢阅读。在 LinkedIn 上与我联系,继续讨论!

参考

[1] Chris Manning 和 Richard Socher,第 12 讲:语音处理的端到端模型 (2017),斯坦福大学

[2]格雷夫斯、亚历克斯和纳夫迪普·贾伊特利。用递归神经网络实现端到端语音识别 (2014),ICML。第 14 卷。

[3] H. Sak,A. Senior,K. Rao,O. Irsoy,A. Graves,F. Beaufays 和 J. Schalkwyk,(2015),IEEE 声学、语音和信号处理国际会议(ICASSP)

[4] Amodei,Dario,等人,深度语音 2:英语和普通话的端到端语音识别 (2015),arXiv 预印本 arXiv:1512.02595

[5]陈伟霆和纳夫迪普·贾伊特利和阔克诉勒和奥里奥尔·维尼亚尔斯案,"听、听、拼 (2015),arXiv:1508.01211

[6] Chung-Cheng Chiu 和 Tara N. Sainath 和 Yonghui Wu 和 Rohit Prabhavalkar 和 Patrick Nguyen 和和 Anjuli Kannan 和 Ron J. Weiss 和 Kanishka Rao 和 Ekaterina Gonina 和 Navdeep Jaitly 和和 Jan Chorowski 和 Michiel Bacchiani,采用序列到序列模型的最新语音识别技术 (2018),arXiv:1712.01769

[7] N. Jaitly,D. Sussillo,Q. Le,O. Vinyals,I. Sutskever 和 S. Bengio,一种神经传感器 (2016),arXiv 预印本 arXiv:1511.04868

[8]魏平和彭和安德鲁吉比安斯基和 o .阿里克和阿贾伊坎南和纳朗和乔纳森雷曼和约翰米勒,深度语音 3:利用卷积序列学习缩放文本到语音 (2018),arXiv: 1710.07654

[9]亚伦·范·登·奥尔德和桑德·迪耶曼和黑加·曾和卡伦·西蒙扬和奥里奥尔·维尼亚尔斯和亚历克斯·格雷夫斯和纳尔·卡尔奇布伦纳和安德鲁·老和科拉伊·卡武克库奥卢, WaveNet:原始音频的生成模型 (2016),arXiv: 1609.03499

[10]Aaron van den Oord and Yazhe Li and Igor Babuschkin and and Oriol viny als and Koray Kavukcuoglu and George van den dries sche and Edward Lockhart and Luis c . Cobo and Florian stim Berg and Norman and Grewe and Seb Noury and Sander die Elman and Erich Elsen and Nal Kalchbrenner and Heiga Zen and Alex Graves and Helen King and Tom Walters and Dan Belov and,平行波网:快速高保真语音合成(2010 年)

[11]和庞若明和 Ron J. Weiss 和 Mike Schuster 和 Navdeep Jaitly 和 Zongheng Yang 和和和 Skerry-Ryan 和 Rif A. Saurous 和 Yannis Agiomyrgiannakis 和 Wu Yonghui,通过调节 WaveNet 对 Mel 谱图预测进行自然 TTS 合成 (2018),arXiv: 1712.05884

[12] Rohit Prabhavalkar 和 Kanishka Rao 和 Tara Sainath 和李博和叶小开·约翰逊和纳夫迪普·贾伊特利,语音识别的序列间模型比较 n (2017),Interspeech 2017,ISCA

PyMC3 应用贝叶斯推理第 1 部分

原文:https://towardsdatascience.com/applied-bayesian-inference-pt-1-322b25093f62?source=collection_archive---------14-----------------------

实践教程

条件世界导论

毛伊岛上空的银河。作者图片

这个故事是为那些已经稍微熟悉统计学和 Python,并希望将他们的技能提升到一个新水平的人而写的。我将从哲学开始,然后尝试直接用 Python 实现概念,为您构建一个更具可操作性的指南。我已经无数次读到过这个世界,但直到我实际应用它之前,我对自己的能力没有信心,所以我恳求你找到一个你感兴趣的数据集,然后一头扎进去!

有帮助的先决条件:熟悉统计学直到假设检验,初级到中级 Python 技能。

在这个故事中,我将介绍贝叶斯思维、概率编程介绍,以及一个用 PyMC3 & ArviZ 掷硬币的例子。

思维贝叶斯

当我们听到“统计”这个词时,我们通常会更多地考虑长期运行的频率。我们的脑海中会立即浮现出这样的例子:硬币正面朝上的概率,在六面骰子上掷出 3 的概率,闪电击中的概率。举个例子:如果你想知道一枚硬币正面朝上的概率,那么你可以想象一个世界,你把一枚硬币抛了无数次,然后看看在令人难以置信的大量抛硬币或试验中,它有多少次正面朝上。这个经典的思想流派被称为频率主义者,其中概率被认为是经过多次试验后某一事件相对频率的极限。有些情况下,“长期频率”思维没有太多的逻辑意义,比如:考试得 A 的概率和*选举结果的概率。*频率主义者通过调用替代现实来解决这些问题,并说在所有这些现实中,发生的频率决定了可能性。本质上,频率主义者将参数(测试统计试图估计的)视为确定性的,或非随机的变量。

当 n 趋近于无穷大时,真实概率就接近于。作者图片

与这一学派相反的是贝叶斯,其中概率表达了对一个事件的相信程度。因为这篇文章的大部分内容将在贝叶斯世界中进行,所以让我举例来介绍这种思维方式。

这个例子来自行为心理学家丹尼尔·卡内曼和阿莫斯·特沃斯基,在思考快与慢 (Kahneman,2011)中得到推广:

“史蒂夫非常害羞和孤僻,总是乐于助人,但对人或现实世界不感兴趣。温顺整洁的灵魂。他需要秩序和结构,热爱细节。”

史蒂夫更有可能成为图书管理员还是农民?

他们发现绝大多数人选择图书管理员(90%)而不是农民(10%)。不管答案如何,丹尼尔&阿莫斯发现,人们甚至不考虑世界上图书馆员与农民的比例来做出决定,这是不合理的。即使是关于图书馆员和农民的比例,这也不是关于人们实际上知道真实的答案,而是关于思考去估计它。正如来自 3Blue1Brown 的 Grant Sanderson 如此雄辩地指出的那样,这一点可以用图形更好地说明:

作者图片

让我们说,在我们的估计中,我们认为图书管理员约占农民总数的 1/21。我们的目标是根据描述找出史蒂夫成为图书管理员的可能性。假设我们发现证据表明,在 10 名图书管理员和 200 名农民中,有 4 名图书管理员和 20 名农民符合这个描述。

作者图片

因此,考虑到我们的信念、证据和目标,我们可以宣称概率为:

作者图片

现在让我们说,在对这个话题做了更多的阅读之后,我们发现图书馆员与农民的比例并没有那么低。那么,在这种情况下,我们所要做的就是更新我们先前的信念(实质上是加宽细长的蓝色条)并重新计算。这是贝叶斯哲学的真正精髓;没有“对”或“错”的概念,因为你在用你先前的知识和新的证据不断更新你对世界的理解。

作者图片

本质上,贝叶斯主义者将参数(最有可能解释数据的分布)视为随机变量。下面是我们的例子如何映射到贝叶斯世界中的术语:

  • 先验:P(H) = 1/21。在看到证据之前我们所知道的参数值。
  • 证据(又名边际可能性):P(E) = 24。证据 E 不考虑事件的可能性有多大。
  • 可能性:P(E|H) = 4/10 = 2/5。给定假设证据有多大可能?
  • 后验:P(H|E) = 4/24 = 1/6。给定证据,假设的可能性有多大。

综上所述,我们得到:

作者图片

当我们把它简化时,我们得到:

贝叶斯定理。作者图片

顺便说一下,由于在贝叶斯世界中获得洞见的“主观”本质,它可能会被混淆,并很容易被简化为仅仅是观点。这可能是一个懒惰的概括,因为所有的统计(频率主义者和贝叶斯)都需要假设,无论在哪里做出假设,从数据到见解,你都有一个相对主观的工艺。

这说明了数据的真正价值,因为当我们收集无限量的数据(或证据)时,比如说当 N 接近无穷大时,贝叶斯结果往往与频率主义者的结果一致。因此可以推断,统计推断对于大的 N. 来说或多或少是客观的。问题是我们通常没有无限的资源(时间、能量、数据、计算等)。),所以我们不得不在现有证据较少的世界里工作。在频率主义者的世界里,这将产生具有更大方差和更大置信区间的结果。贝叶斯分析在这里可以大放异彩,因为你保留了不确定性,突出了小数据集上统计推断的不稳定性。请注意,这并不意味着贝叶斯在小数据集上比 Frequentist“更准确”。两者都需要假设才能获得洞察力,数据集越小,需要做出的假设就越多/越广泛。

概率规划

请记住,我们的目标是计算后验概率,理想情况下是基于大量数据(以有效的方式)。由于过去的斗争和计算时代的发展,我们已经开发了概率编程语言(PPL),即 PyMC3。PPL 旨在帮助在模型创建和推理之间划清界限,同时也使设计和调试更加容易。这个框架允许各种数值方法作为通用推理机来应用,以自动化整个过程的推理部分。使用相对较少的代码行,就可以以有效和合理的方式创建复杂的概率模型。为了可视化 PyMC3 结果,我们将使用 ArviZ 库,正如您将看到的,它是为了补充 PyMC3 而惊人地构建的。

让我们来看看一些最著名的推理引擎。许多这方面的知识在 用 Python 进行贝叶斯分析(马丁 2016) 中得到了令人难以置信的解释,所以我将从那里汲取灵感:

非马尔可夫方法:

  • 网格计算
  • 二次逼近
  • 变分法

马尔可夫方法:

  • 大都会黑斯廷斯
  • 哈密尔顿蒙特卡罗/无掉头采样器

由于被称为 MCMC ( 蒙特卡罗马尔可夫链)模型的马尔可夫方法是当今最流行的方法之一,所以我在这里只集中讨论这些方法。我发现学习这些方法的理论的最好方法是通过做和倒退,所以让我们先从一个简单的,人为的抛硬币的例子开始。

import pandas as pd
import numpy as np
import pymc3 as pm
import arviz as az
from arviz.plots.plot_utils import xarray_var_iter
import scipy.stats as stats
import matplotlib.pyplot as plt
%matplotlib inlinenp.random.seed = 0#the number of samples
N=15#establishing prior and getting observed data
theta_real = .5
observed_data=stats.bernoulli.rvs(p=theta_real, size=N)#the number of heads
k=observed_data.sum()print(observed_data)
print(f"There are {k} heads")[1 0 1 1 1 1 1 0 0 0 0 1 0 1 1]
There are 9 heads

上面的代码只运行 15 次概率(theta_real)为 0.5 的硬币投掷。在大多数真实的用例中,这需要估计。现在我们有了数据,让我们来构建模型:

#fit the observed data 
with pm.Model() as coin_flip:
    theta=pm.Beta('theta', alpha=1, beta=1)
    y=pm.Bernoulli('y', theta, observed=observed_data)

pm.model_to_graphviz(coin_flip)

作者图片

为了在这里阐述思维过程,这本书 黑客的贝叶斯方法:概率编程和贝叶斯推理 (Davidson-Pilon,2015) 提供了一个惊人的心理模型指南。当你构建贝叶斯模型时,你应该问自己的第一个问题是“这些数据是如何产生的?”

**1。**我们首先思考,“描述这个抛硬币数据的最佳随机变量是什么?”二项式随机变量是一个很好的候选变量,因为它可以很好地表示具有 n 次独立试验的二元决策过程。这是代码块中的第三行,代表我们的可能性。单个成功/失败实验也称为伯努利试验或伯努利实验,一系列结果称为伯努利过程;对于单次试验,即 n = 1,二项分布是伯努利分布。

**2。**接下来,我们想,“好吧,假设掷硬币是二项分布的,我需要什么来得到二项分布?”嗯,二项分布有一个参数θ(θ)

**3。**我们知道 θ 吗?是的,但是让我们假设我们没有。在大多数真实情况下,我们不会,我们需要估计它,但对于我们的例子,我们知道它是 0.5。但是我们可以引入一个天真的先验,好像我们不知道 *θ。*这是代码块中的第二行,代表我们的先验。

**4。**什么是 θ 的好分布?Beta 是好的,因为它模拟了有限范围的事物,比如 0 到 1。贝塔分布有两个决定其形状的参数,阿尔法(α)和贝塔(β)。

5。我们知道α和β可能是什么吗?不。在这一点上,我们可以继续并给这些参数分配一个分布,但一旦我们达到一个设定的无知水平,最好停止(记住我们只进行了 15 次试验作为证据);尽管我们对 θ 有一个先验的信念(“当我们接近无穷大时,它可能会趋向于 0.5”),但我们对α和β并没有什么强有力的信念。所以最好就此停止建模。

6。那么,α和β的合适值是多少呢?贝塔分布的形状因这些值而异,但首先我们将α和β指定为 1。这等同于从[0,1]开始的均匀分布。这是一个弱信息先验,因为它基本上假设没有先验知识,知道公平抛硬币头的概率是多少。本质上,该参数可以是 0 到 1 之间的任何值。

贝塔分布的概率密度函数,公共域

这种思维方式“这个数据是怎么产生的?”非常关键。本质上,我们对世界上的某个过程有一些假设,并希望对该过程建模,因此我们希望选择适当的分布组合来创建模型。如果我们假设公平抛硬币的概率代表贝塔分布,并且我们希望我们的模型描述它,那么由于共轭性,选择我们的先验分布为贝塔分布和可能性为二项式分布是一个重要的选择。如果后验分布与先验概率分布在同一概率分布族中,则先验和后验被称为共轭分布、**,先验被称为似然函数的共轭先验。本质上,如果我们使用贝塔分布作为先验,使用二项式分布作为似然,我们将得到贝塔分布作为后验。

共轭性确保了后验概率的数学易处理性,这一点很重要,因为贝叶斯统计中的一个常见问题是我们无法解析地解决后验概率。幸运的是,无论我们是否选择共轭先验,现代计算方法都使我们能够解决贝叶斯问题,但重要的是要认识到制作适当模型的价值。

现在让我们按下推理引擎按钮,看看我们得到了什么:

with coin_flip:
 step = pm.Metropolis()
 trace = pm.sample(10000, step=step, return_inferencedata=True)

默认情况下,PyMC3 将运行 Metropolis 推理引擎(Metropolis-Hastings),但是我们也可以显式地声明它。我们选择要运行的样本数量,现在让我们看看我们的模型学习数据的程度。

var_names = ["theta"]
lines = list(xarray_var_iter(trace.posterior[var_names].mean(dim=("chain", "draw"))))
az.plot_trace(trace, lines=lines);

作者图片

正如我们所看到的,后验概率的平均值接近 0.5,但确实有点偏。考虑到这只是 15 次试验,我们可以想象如果我们进行更多的试验,模型会自然地找到它的方向!对于左边的图,我们寻找的是一个相对平滑的核密度估计(KDE)图。没有那么顺利,可能是多种原因造成的(推理机的选择,数据中的观察次数,样本数等。).对于右边的图,我们要找的是没有明显的模式。我们希望右边的图类似于白噪声,没有明显的“分歧”被描述;本质上,我们想要各种蒙特卡罗马尔可夫链的良好混合。

在我们进一步分析后验分布之前,让我们看看是否能得到一个更好的 KDE 图。

with coin_flip:
 step = pm.NUTS()
 trace = pm.sample(10000, step=step, return_inferencedata=True)var_names = ["theta"]
lines = list(xarray_var_iter(trace.posterior[var_names].mean(dim=("chain", "draw"))))
az.plot_trace(trace, lines=lines);

作者图片

这样看起来好多了!但是为什么呢?我们的第一个模型和这个模型的区别在于推理机。我们首先使用了默认的 Metropolis-Hastings 推理引擎,现在我们尝试了哈密尔顿蒙特卡罗/无 U 形转弯采样器。这页相当精彩地解释了它们的区别(概念上和数学上的),我将尝试在这里总结它们的区别:

Metropolis-Hastings 使我们能够从任何概率为 p(x) 的分布中获取样本,只要我们能够计算出至少一个与其成比例的值。这在概念上类似于试图从船上测量湖底的形状,方法是随机使用一根长棍来测量与一点的距离,然后在更深的方向上反复移动,并且只在这些方向上移动。正如你所想象的,经过大量的迭代,你会对湖底的形状有一个非常精确的描述,这在贝叶斯世界中也是相似的(湖底是后验分布)。

汉密尔顿蒙特卡罗/NUTS 源于这样一种认识,即获取大量样本可能会非常昂贵(时间和计算)。这种方法基本上与 Metropolis-Hastings 相同,只是我们没有建议随机移动我们的船,而是做了一些更聪明的事情;我们沿着湖底的曲线移动小船。为了决定下一步去哪里,我们让一个球在湖底滚动,从我们现在的位置开始。我们扔一个球,让它滚动一小会儿,然后我们把船移到球所在的地方。现在,我们使用 Metropolis 标准来接受或拒绝这一步,就像我们在 Metropolis-Hastings 方法中看到的那样。熟悉用 Adam 优化的梯度下降的人可能会发现这种方法类似。

像所有事情一样,使用更聪明的哈密尔顿方法有一个权衡。HMC 的每一步都比大都会-黑斯廷斯的每一步计算起来更昂贵,但是 HMC 接受这一步的概率比大都会高得多。我强烈建议阅读之前链接的页面,深入了解这个概念,但现在让我们继续前进。

我们还可以打印出一个数据框架,显示我们的后验分布建模的关键指标:

az.summary(trace)

作者图片

默认情况下,我们可以获得相当多的信息:平均值、标准差、最高密度区间(HDI)等等。返回的数量之一是蒙特卡洛标准误差(MCSE)平均值和标准偏差。这是对采样方法引入的误差的估计。它被定义为链的标准偏差除以它们的有效样本量。该估计考虑到样本并非真正相互独立。后验分布的另一个快速质量检查是 r_hat 度量。经验法则是,您希望该值接近 1.0,因为这表示链中的收敛。通常,高于 1.05 的值是一个关注点,1.0 和 1.5 之间的值值得研究。

这里有趣的是,即使进行了 15 次试验,我们的估计仍然得到接近 0.5 的平均值,但肯定比我们想要的要高。看到 r_hat 告诉我们,我们的模型在构造中也不一定会遇到错误,所以我们的许多估计结果都是因为我们的数据很少。

为了解释高密度间隔,让我们直观地看一下:

az.plot_posterior(trace, kind='kde', ref_val=0.5);

作者图片

在贝叶斯推理中,与频率主义推理不同,我们得到值的整个分布。每次 ArviZ 计算和报告 HPD 时,默认情况下,它将使用 94%的值。在频率主义者的世界里,我们有 p 值和置信区间。在贝叶斯世界中,这被称为可信区间。这里我们可以解释为,有 94%的概率,硬币抛头的信念在 0.45 到 53 之间。这里的好处是,我们用频率主义者世界中相对混乱的术语和解释来换取贝叶斯世界中明智的估计。不利的一面是,我们失去了一个更明确的行动来换取一系列的信念。例如,在这种情况下,硬币可能会稍微偏向正面,因为大部分区间都在 0.5 以上,但也可能是 0.38。频繁主义者的世界可能会给我们提供一个更明确的答案,但一如既往:你一直在做出假设以获得洞察力。

最后,我们可以运行后验预测检验(PPC)来验证我们的模型。这里的想法是,我们比较观察数据和预测数据,找出这两组数据之间的差异。主要目标是检查自动一致性。这是通过使用来自后部的每次绘制的参数从模型生成数据来完成的。理想情况下,生成的数据和观察到的数据应该或多或少相似。即使我们把所有的建模都做对了,他们也可能会看走眼。对原因的调查可能会导致我们模型的重大改进,这是我们以前可能没有想到的。

with coin_flip:
 ppc = pm.sample_posterior_predictive(trace, var_names=[“theta”, “y”])ppc['y'].shape(20000, 15)az.plot_ppc(az.from_pymc3(posterior_predictive=ppc, model=coin_flip));

作者图片

fig, ax = plt.subplots(figsize=(10, 5))
ax.hist([y.mean() for y in ppc['y']], bins=19, alpha=0.5)
ax.axvline(observed_data.mean())
ax.axvline(0.5, c='r')
ax.set(title='Posterior Predictive Check', xlabel='Observed Data', ylabel='Frequency');

作者图片

看起来我们的贝叶斯模型能够很好地学习我们观察到的数据分布!这一点尤其值得注意,因为我们只进行了 15 项试验,坦白地说,这是相当疯狂的。像任何数据项目一样,高性能模型不仅仅是好的度量数字。我们对掷硬币的了解告诉我们,0.6 对于公平的掷硬币来说没有多大意义。

所以现在,以真正的贝叶斯方式,让我们收集更多的证据,更新我们的先验,看看会发生什么。

根据新证据更新信念

假设我们进行 1000 次相同的掷硬币试验:

#the number of samples
N_updated=1000#establishing prior and getting observed data
theta_real = .5
observed_data_updated=stats.bernoulli.rvs(p=theta_real, size=N_updated)#the number of heads
k_updated=observed_data_updated.sum()print(observed_data_updated)
print(f”There are {k_updated} heads”)[0 1 0 1 0 1 0 1 1 1 1 1 0 1 0 0 1 1 0 1 1 1 1 1 1 1 0 0 1 0 1 0 1 0 0 1 1 0 1 1 0 0 1 1 1 0 1 0 1 0 0 1 0 0 1 1 0 0 1 1 0 0 1 1 1 1 1 0 0 1 1 1 1 1 1 0 1 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 1 1 0 0 0 1 1 0 1 0 0 1 1 0 1 0 0 1 1 0 0 1 1 0 0 1 1 1 0 0 1 0 1 1 1 0 0 1 0 1 1 1 1 1 1 0 0 1 0 1 1 1 0 1 0 1 1 1 0 0 0 1 0 1 1 0 1 1 0 1 0 1 1 1 1 0 0 0 0 1 0 0 0 1 0 1 0 1 0 1 1 0 1 1 0 0 0 1 0 1 0 1 0 1 0 1 0 0 1 0 0 1 1 0 1 0 0 0 0 0 1 0 0 0 1 0 0 0 1 1 1 1 0 1 1 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 0 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 0 0 0 1 0 1 1 1 1 0 1 0 1 1 1 1 1 1 1 0 0 1 1 0 0 0 1 1 0 1 0 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 0 1 1 1 0 0 0 0 1 1 0 0 0 1 0 1 1 0 0 1 1 0 1 1 0 1 0 1 1 0 0 1 0 0 1 1 1 1 0 1 0 1 0 1 1 0 0 0 1 0 1 1 0 0 0 1 1 0 1 0 1 0 0 0 1 1 0 1 1 0 1 1 0 1 1 1 1 1 0 1 0 0 0 0 1 0 0 1 1 0 0 0 1 1 0 1 0 1 0 1 1 0 0 1 1 1 1 0 0 1 1 1 0 0 1 1 0 0 1 1 1 0 0 1 0 1 0 0 1 1 1 1 1 0 0 0 1 1 0 1 0 1 1 1 0 1 1 0 1 0 0 1 1 1 1 1 0 0 1 0 1 1 1 0 0 1 1 1 0 0 0 0 0 0 1 1 1 1 0 1 1 1 1 1 1 0 0 1 0 1 1 0 1 1 0 1 0 0 1 1 1 1 0 0 1 0 1 0 1 0 1 1 0 1 1 1 0 1 1 0 1 1 1 0 1 0 1 1 1 1 0 0 1 0 0 0 0 0 1 1 0 1 1 1 0 0 0 1 0 1 0 0 1 1 1 0 0 1 1 1 0 0 0 1 1 0 1 0 0 1 1 0 0 1 1 0 1 0 1 0 0 0 0 1 0 1 0 1 0 1 0 0 1 0 1 1 0 0 1 1 0 0 0 1 1 1 1 0 0 1 0 1 1 1 1 0 1 0 0 0 0 0 0 1 0 1 0 1 1 0 0 1 1 1 0 0 1 1 1 0 1 1 1 0 1 0 0 0 0 0 1 0 0 0 0 1 0 1 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 1 1 1 0 0 1 1 0 1 0 1 1 0 1 0 1 0 1 0 0 1 1 1 1 0 0 1 1 1 1 1 0 1 0 1 0 1 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 1 0 1 1 1 0 0 1 0 0 1 0 1 1 0 0 0 1 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 1 0 1 1 0 1 1 1 0 0 0 0 0 1 1 1 0 1 0 1 1 0 1 0 1 0 0 1 0 0 0 0 0 1 0 1 0 1 0 0 0 1 0 0 0 1 1 0 1 1 0 1 1 0 1 0 0 1 0 1 1 0 0 1 1 1 1 0 0 0 1 1 0 0 1 1 1 0 0 0 0 1 1 1 0 0 0 0 1 1 1 0 0 0 1 1 1 1 1 0 0 0 1 0 1 1 1 0 0 0 1 1 1 1 0 1 1 0 1 1 1 1 1 1 0 0 0 0 1 0 1 1 0 0 1 0 0 0 1 0 0 0 1 1 1 1 1 1 0 0 0 1 0 0 0 1 1 0 0 0 1 1 1 0 1 1 1 0 0 1 1 0 0 1 1 1 0 0 1 0 1 1 1 0 0 1 0 1 1 0 1 0 1 0 1 0 0 1]
There are 511 heads

在我们构建模型时,我们也可以更新之前的 Beta 分布,使其看起来更像一个能更好地反映掷硬币的形状。

#fit the observed data 
with pm.Model() as coin_flip_updated:
 theta=pm.Beta(‘theta’, alpha=2, beta=2)
 y=pm.Bernoulli(‘y’, theta, observed=observed_data_updated)

pm.model_to_graphviz(coin_flip_updated)

作者图片

with coin_flip_updated:
 step = pm.NUTS()
 trace_updated = pm.sample(10000, step=step, return_inferencedata=True)var_names = ["theta"]
lines = list(xarray_var_iter(trace_updated.posterior[var_names].mean(dim=("chain", "draw"))))
az.plot_trace(trace_updated, lines=lines);

作者图片

az.summary(trace_updated)

作者图片

az.plot_posterior(trace_updated, kind=’kde’, ref_val=0.5);

作者图片

with coin_flip_updated:
 ppc_updated = pm.sample_posterior_predictive(trace_updated, var_names=[‘theta’, ‘y’])az.plot_ppc(az.from_pymc3(posterior_predictive=ppc_updated, model=coin_flip_updated));

作者图片

fig, ax = plt.subplots(figsize=(10, 5))
ax.hist([y.mean() for y in ppc_updated[‘y’]], bins=19, alpha=0.5)
ax.axvline(observed_data_updated.mean())
ax.axvline(0.5, c='r')
ax.set(title=’Posterior Predictive Check’, xlabel=’Observed Data’, ylabel=’Frequency’);

作者图片

这里令人惊讶的是,我们的贝叶斯模型能够超越我们的证据(其平均值约为 0.51),并确定公平抛硬币的真实平均概率:0.5。只要收集新的证据,更新我们先前的信念,重塑我们的数据,我们就能够对掷硬币现象做出更好的估计!

数学上已经证明,如果我们想扩展逻辑以包括不确定性,我们必须使用概率和概率论。正如我们所看到的,贝叶斯定理只是概率规则的逻辑结果。因此,另一种思考贝叶斯统计的方式是在处理不确定性时作为逻辑的延伸。

这个世界还有许多令人着迷的深度,在第 2 部分中,我计划将这些应用到真实世界的数据集中!看这里:用 Python pt 应用贝叶斯推理。2

参考文献

[1]奥斯瓦尔多·马丁,用 Python 进行贝叶斯分析

[2]卡梅隆·戴维森-皮隆,黑客的概率编程和贝叶斯方法

【3】卡西·科济尔科夫,统计学:你是贝叶斯主义者还是频率主义者?

应用贝叶斯推理与 PyMC3 和 Bambi 第 3 部分

原文:https://towardsdatascience.com/applied-bayesian-inference-with-pymc3-and-bambi-pt-3-d4bfb3211509?source=collection_archive---------8-----------------------

预测 Spotify 前 200 首歌曲的流

银翼杀手 2020。作者图片

介绍

这是我的应用贝叶斯推理系列的最后一部分。在第 1 部分中,我介绍了条件世界和概率编程:

在第 2 部分中,我展示了如何通过对 Nike 和 Adidas 的鞋子建模来将模型扩展到“真实”数据。这篇文章展示了如何运行更有效的 A/B 测试和组比较:

在本部分的第 3 部分,我将通过在 PyMC3 中引入线性回归,然后将其进一步引入层次模型、广义线性模型和样本外预测,来展示贝叶斯建模为何如此不可思议。

这个故事将看看 Spotify 的数据,即 2020 年至 2021 年的前 200 首歌曲。可以在这里找到数据集:https://www . ka ggle . com/sashankpillai/Spotify-top-200-charts-2020 2021。我们将尝试基于每首歌曲的相关特征(可跳舞性、艺术家追随者等)为每首歌曲的流的后验建模。)

探索性数据分析和清理

作者图片

Dtypes 是一个很好的起点:

作者图片

这肯定需要一些清理。此外,因为它们是对象,所以很可能需要清理一些奇怪的文本才能变成数字。在这之间,我认为最大的变化是体裁栏。每首歌曲似乎符合 1 个以上的流派,这些流派以列表的形式表示。对于我们的用例,我们希望对这些列进行一次热编码。这可以很容易地用 sklearn 的 MultiLabelBinarizer 来完成。

作者图片

那应该好多了!现在我们想把列按类型分成 1 列。修复 dtype 和 2。从一个地方开始过滤。从那里,我们可以绘制流分布图,看看我们的目标是什么。

对于回归,集中或标准化你的价值是常见的做法,这里也不例外。事实上,对于 MCMC 模型来说,标准化您的值可能特别重要,因为否则采样器将很难收敛。标准化对我们来说还有另一个优势,那就是能够使用弱信息先验,因为截距总是为零,斜率大约为-1 和 1。它还允许我们用 Z 分数来表示,确定离平均值有多少标准偏差值。

0

作者图片

我们现在只处理这些数字字段;稍后的实现可以很容易地扩展到包含我们之前编码的分类列(genre)。

作者图片

这是我们在许多场景中看到的常见分布形状。这是有很多人处于“较低”值的结果,并且随着你在尾部越靠右,人数呈指数下降。有趣的是,我们发现它甚至对前 200 首歌曲都如此强大,但这将是我们在建模后要记住的一个重要部分。

首先,我们可以画出标准化的水流记录图:

作者图片

仍然不是最好的,但是好多了。我们显然有一些离群值,它们在去年经历了一些大规模的病毒式传播。

简单回归

最好的学习方法是实践,当事情很复杂时,最好从尽可能简单的开始,然后慢慢扩大规模。这里最简单的例子是线性回归,让我们看看我们想要将流建模为艺术家追随者的函数。也许我们有一些预感,艺术家的追随者对溪流有一些影响。

首先理解人际关系是一个好习惯;艺术家的追随者如何与排名前 200 的流相关联?

作者图片

几乎没有相关性,这似乎是有意义的,因为这些歌曲已经非常受欢迎——甚至可能出现反向相关性(当一首新歌进入前 200 名时,该艺术家的粉丝显著增加)。不仅在建模之前,而且在从结果中得出结论的时候,理解这些类型的见解都非常重要。现在每个人都应该明白这种相关性了!=因果关系,但相关性可能流向哪个方向?为什么呢?这些可以给你惊人的洞察力,让你知道如何修复模型中的问题,或者你想要管理什么样的数据来改进你的模型。

作者图片

这里有趣的是,我们为 beta(x 的权重,艺术家的追随者)和偏差创建一个先验,然后创建一个线性函数(alpha + betax)来建模可能性。请记住,我们试图将后验概率建模为先验的函数,但当我们这样做时,我们能够基于观察值的不确定性量化值的分布。我们希望将 mu 包装在一个确定性函数中,因为我们不希望它每次都不一样;它应该总是遵循 alpha + betax 的结果,但 alpha 和 beta 具有随机先验,允许模型扫描它们的一系列选项。

此外,我们还可以创建一些确定性的指标来非常简单地测量我们的输出。因为我们在处理回归,所以我们也可以在模型构建本身中计算 r2 值。这创造了一个非常简单的方法来确定拟合线与数据的吻合程度。

现在我们可以对数据进行采样。NUTS(不掉头采样器)是大多数用例的绝佳选择。虽然它的每一步都比 Metropolis 慢,但它能够轻松穿越对角线空间。此外,它通常需要少得多的步骤来获得对后验概率的合理近似。

作者图片

我们在轨迹中没有看到明显的差异,标准化/居中显著地帮助我们的模型为每首歌找到合适的分布。让我们看看我们的 r2,并绘制结果。

作者图片

作者图片

因此,我们的模型表现很好,但我们得到了相当低的相关性,这在看到这个散点图时应该很明显。一开始真的没有太多的相关性,这一点似乎越来越明显。不过,我们可以做很多事情来改进这个模型。首先,让我们使用 StudentT 分布作为我们的可能性,看看我们是否可以使它对异常值更稳健,同时也预测样本外。

稳健的线性回归和样本外预测

作者图片

作者图片

作者图片

作者图片

我们在拟合数据的直线方面得到了显著的改进—~0.16!从技术上讲,由于我们对数据进行了标准化,斜率和皮尔逊相关系数具有相同的值,但这是一种非常有趣的将它融入建模的方式。仍然不是流的最佳预测器,但我们可以看到这条线不太可能被数据集中的潜在异常值选中。虽然这对我们没什么好处,但我们也可以轻松地更新我们的数据,并在测试数据集上使用该模型预测后验概率。

作者图片

Mean Squared Error: 0.6710557105827462

令人惊讶的是,不仅可以很容易地找到一个测试点(每个后验分布的平均值),而且还可以找到一个值的分布来估计误差!我们现在可以利用这些技能来构建这个简单的模型,并在此基础上继续构建。

进入贝叶斯建模界面——Bambi。

多重、分层和广义线性模型

像以前一样,如果我们要加入更多的变量,我们应该首先可视化关系,并确保特征之间以及特征/预测变量之间的最小相关性。我们可以用我最喜欢的可视化工具散点图矩阵有效地做到这一点。

作者图片

作者图片

似乎“能量”和“响度”比我们想象的更相关。当试图对这些变量之间的关系建模时,这可能会导致许多问题,因此我们可以删除一个变量。

注意最后一行是 Log_Streams 值,所以我们不需要评估它与流的相关性,因为我们还没有建模。

多元线性回归

使用 Bambi,我们可以轻松地写出我们的公式,并像这样创建模型:

作者图片

我将跳过 traceplots 和 forestplots 来分析这里的数据,但请确保使用它们来确保您的模型正确地收敛到每个要素!跟踪图是直观显示 MCMC 模型输出的极好方法。

作者图片

作者图片

Mean Squared Error: 0.5720862510645325

请注意,我们在后验预测中对两条链进行采样,以模拟测试预测,因此我们采用了两个列方式的平均值,一个是两条链的平均值,另两个是每个预测的后验分布平均值(测试值)。

随着我们添加特定歌曲的更多特征(可跳性、效价、能量等等),MSE 明显更好。通过评估拟合模型摘要中的平均值,有趣的是,追随者似乎对增加 Log_Streams 的其他值有最强的影响。Danceability 和 Speechiness 等功能似乎对 Log_Streams 有负面影响,但重要的是对这些功能持保留态度,因为我不完全确定 Spotify 是如何记录这些信息的(更不用说有人可能是如何操纵它以在 Kaggle 中获得这些信息的)。

此时你可能已经意识到的一个重要问题是,艺人的歌曲变奏曲呢?我们目前正在为每首歌单独建模(非池化),但我们也可以为每个艺术家的歌曲建模(池化)。每一个问题是,你必须为它放弃一些东西。如果没有,你就失去了看到艺术家之间细微差别的能力;有了 pooled,你就失去了每首歌分发的粒度。那么我们能做什么呢?分级部分统筹来救援!

分层部分池

作者图片

这看起来复杂得多,但实际上非常简单。到目前为止,我们所学的贝叶斯推理的整个概念是,你能够烘焙先前的知识,并建立一个后验分布模型,该模型适合于随着新知识的形成而更新。到目前为止,我们只对直接先验进行了建模(例如,基于正态概率的 mu 和 sigma 先验),但是如果我们的数据具有层次性质,会怎么样呢?如果我们可以对每个艺术家的歌曲元数据的分布进行建模,这不是很好吗?简而言之,我们可以,而且很容易。这是一个层次模型,它包括你在你的先验知识上增加超先验知识;在这种情况下,我们添加了按艺术家对歌曲元数据特征的分组,并添加了超优先级以及我们的可能性。

在代码块中,我们可以通过向每个特性添加“(1|…)”来表示我们想要一个层次模型,然后简单地向值添加适当的优先级。语法可以分解如下:

  • 常见的预测因素是你的主要影响。例如,可跳舞性。
  • 群体预测因子可以通过三种不同的方式来识别:1 .(Danceability|Artist)表示我们希望允许每个艺术家拥有自己的可跳舞斜率和截距,2。(1|Artist)表示我们想要 Artist 的不同截距,而不是斜率,以及 3。(0+Danceability|Artist)表示我们只想要特定于每个艺术家的斜率,而不包括特定于艺术家的截距。

显然,我们拥有的知识越少,我们想要实现的先验就越广,就像以前一样。无论如何,这个模型表现如何?

作者图片

作者图片

不幸的是,Bambi 目前还没有功能(版本 0.6.3)来进行样本外预测,当你涉及到分类分组时,我们的模型似乎也可以学习分布。做部分池的好处是,我们不仅可以看到这方面的后验分布,还可以衡量每个艺术家的歌曲属性分布的影响。下面是一些受欢迎程度的例子:

作者图片

尽管分层建模非常吸引人,但很明显我们不能完全有效地对观察到的分布建模。这是因为它的非高斯形状。我们试图用线性模型来模拟它,这些模型假设了一个常态水平,而在现实中,我们只是看不到。此外,我们的流永远不会低于 0,所以我们的 Log_Streams 永远不会低于 0(最小的流计数可能是 1 和离散的)。我们如何对此建模?

广义线性模型

广义线性模型在 Bambi 中非常容易实现,它们是我们问题的解决方案。我们的河流不是自然分布的,因此我们应该考虑如何有效地模拟它们的真实性质。一个好的开始是我在第 1 部分中概述的指南——我们如何相信这些数据是收集的?流是一种计数方法;它们反映了一首歌曲在 Spotify 上播放的离散次数。对我们来说幸运的是,有一种流行的 GLM 类型完全符合这一点:泊松。下面是我们如何用 Bambi 实现它以及结果(注意我们现在是如何直接建模流的)。

作者图片

作者图片

作者图片

重要声明:在撰写本文时,我使用了已经标准化的 Streams 字段,该字段不适合建模为泊松分布,因为这会将离散计数数据转换为低于 0 的连续数据(大喊到 Susia 以捕捉这一点)。在我能够解决这个问题之前,请确保知道要建模的预期字段是流的原始计数。

Mean Squared Error: 0.2989611604295006

多么惊人的进步啊!但是为什么 MSE 变得如此之好呢?实际上,我敢打赌我们可以更低,但是怎么做呢?这个问题的答案就是贝叶斯机器学习的精髓。

该模型表现明显更好的原因是因为我们仔细考虑了数据及其代表的内容。我们思考了它的细微差别以及它是如何被收集的。在一个问题上投入无止境的建模技术可以提高你的度量,但是模型不会真正歌唱,直到你做了战略设计的艰苦工作。贝叶斯 ML 让我们能够非常灵活地做到这一点,并且通过稳健和精确的先验变得更加强大,这是我们做/改进的另一件事。允许你的前科有一个大致的学习空间可以创造一个真正的信息模型,能够快速收敛和有效学习。

模型比较

为了更好地总结这一点,让我们进行模型比较,以更清楚地了解每个模型的表现。

作者图片

作者图片

默认情况下,AriZ 使用留一个交叉验证。另一种选择是广泛适用的信息标准(WAIC)。由于结果是对数标度的,因此具有最高值的模型给出了更好的样本外预测拟合,这就是我们前面看到的广义线性模型。

结论

这结束了我的应用贝叶斯推理的 3 部分系列,我们已经能够在这段时间做了很多。老实说,我开始这个系列时对这个领域知之甚少,我知道我想深入了解它,所以我认为最好的方法是利用我在阅读中找到的所有知识。除了记录我的旅程,还有什么更好的方法让其他人学习呢?

我希望这个系列不仅帮助你学习这门手艺并亲自投入其中,还让你看到了机器学习的美好世界。学习贝叶斯方法给你一个更好的理解,一个强大的统计学基础可以带你走这么远。从对你的世界的不确定性建模,做出预测,以及根据新的证据更新你的知识。

这个系列的结束标志着我在这个领域设计我的品牌的正式开始。应用内容不仅可以帮助你入门,还可以帮助你理解概念,丰富你的世界。请关注更多与机器学习的艺术和科学相关的内容。

参考文献

[1]奥斯瓦尔多·马丁,用 Python 进行贝叶斯分析

[2] PyMC3,pymc 3 中的 GLM:样本外预测

[3] PyMC3,(广义)线性和分层线性模型在 PyMC3

[4] PyMC3, GLM:泊松回归

[5] PyMC3,分层部分统筹

[6] PyMC3,多级建模贝叶斯方法入门

[7] Bambi, Wald 和 Gamma 回归(澳大利亚保险索赔 2004-2005)

[8] Bambi,多层回归

[9] Bambi,与 Bambi 和 ArviZ 的逻辑回归和模型比较

[9] Bambi,具有二项式家族的分层逻辑回归

PyMC3 应用贝叶斯推理第 2 部分

原文:https://towardsdatascience.com/applied-bayesian-inference-with-python-pt-2-80bcd63b507e?source=collection_archive---------13-----------------------

在 StockX 上展示阿迪达斯 Yeezy 和耐克米色转售

纽约时报。作者图片

介绍

如果你是从第 1 部分来到这里的,那么我已经假设你至少知道 Python &统计学的基础。如果不是,从第 1 部分开始可能对你自己的学习体验更舒服。

在第 1 部分中,我展示了如何通过 Python 使用 PyMC3,通过掷硬币的例子进行贝叶斯建模。这个例子是一个很好的起点,因为它是一个大多数人都熟悉的概念,而且数据并不“混乱”。这个例子的问题是它的人为性质使你,学习者,很难真正使用这个技能。我知道,因为这正是我的感受。因此,在这一部分中,我将把 PyMC3 模型和方法应用于“真实”数据集。由于我是一个狂热的耐克粉丝,我将浏览这个 StockX 2019 数据竞赛数据集。这个数据集比这个领域的大多数专家使用的数据集都要干净得多。Kaggle 是一个很好的游乐场,可以做数据科学的有趣事情,但是现实世界中的大多数数据集都非常混乱。

为了解决这个问题,假设我们想为 Yeezys 和 Off-white 的利润率建模,以帮助我们在转售世界中找到一个更好的权衡。

与任何好的数据科学项目一样,让我们先做尽职调查,进行探索性的数据分析,并在此过程中做一些清理工作。

探索性数据分析和数据清理

import pandas as pd
import numpy as np
import pymc3 as pm
import arviz as az
from arviz.plots.plot_utils import xarray_var_iter
import matplotlib.pyplot as plt
import seaborn as sns
import xarray as xr%matplotlib inlineRANDOM_SEED = 42stockx_data = pd.read_csv('StockX-Data-Contest-2019-3.csv')#Seeing how many rows/columns we're working with
print(stockx_data.shape)(99956, 8)#Getting first glimpse at dataset
stockx_data.head(10)

作者图片

首先检查 nan 和 dtypes:

stockx_data.isna().sum()

作者图片

stockx_data.dtypes

作者图片

一开始看起来相对干净。让我们稍微清理一下数据/数据类型,然后创建一些有趣的特性。

def cleanCurrency(series):
    """
    Input: a dataframe series for currency (USD)
    Output: cleaned series as a float
    """
    #Replaces dollar sign and comma with empty string
    series = series.str.replace('$', '', regex=True)
    series = series.str.replace(',', '', regex=True)
    series = series.astype(float)
    return series#Fix the Price fields to only be numeric
stockx_data['Sale Price'] = cleanCurrency(stockx_data['Sale Price'])
stockx_data['Retail Price'] = cleanCurrency(stockx_data['Retail Price'])#Calculate Profit by subtracting Sale from Retail
stockx_data['Profit'] = stockx_data['Sale Price'] - stockx_data['Retail Price']#Cleaning Sneaker Name column by removing hyphens
stockx_data['Sneaker Name'] = stockx_data['Sneaker Name'].str.replace('-', '')#Making brand name a little more descriptive
stockx_data['Brand'] = stockx_data['Brand'].str.replace('Yeezy', 'Yeezy (Adidas)')
stockx_data['Brand'] = stockx_data['Brand'].str.replace('Off-White', 'Off-White (Nike)')#Getting only 'object' dtype columns and then stripping trailing white spaces
stockx_data_obj = stockx_data.select_dtypes(['object'])
stockx_data[stockx_data_obj.columns] = stockx_data_obj.apply(lambda x: x.str.strip())#Ensure date fields are the right dtype
stockx_data['Order Date'] = pd.to_datetime(stockx_data['Order Date'])
stockx_data['Release Date'] = pd.to_datetime(stockx_data['Release Date'])#Calculate Duration of Days from Release Date to Order Date
stockx_data['Duration (days)'] = stockx_data['Order Date'] - stockx_data['Release Date']#Univariate stats on the numeric measures
stockx_data.describe()

作者图片

首先引起我注意的是这双鞋的价格。考虑到品牌和 StockX 的目标(转售高端街头服饰),这一点得到了证实。这里的平均订单也从转售中获得了相当不错的利润,大约 60 美元。不过,标准差显示利润有很大的变化。这里需要注意的另一件有趣的事情是,在 2017-2019 年期间,Off-Whites & Yeezys 平均需要 2 个月才能在 StockX 上转售。这可能是由于在鞋子发布的最初几个月供货率低。

接下来,让我们快速了解一下日期字段的范围。

#Getting the min and max dates to understand order date range
print(stockx_data['Order Date'].min(), stockx_data['Order Date'].max())2017-09-01 00:00:00 2019-02-13 00:00:00

似乎我们有相当多的数据!将近两年的相对干净的数据。目前看起来不错,现在让我们开始可视化数据,以便更好地理解我们正在处理的内容:

#Grouping by Brand to see how the general distribution of sales are
brand_group = pd.DataFrame(stockx_data.groupby('Brand').size(), columns=['values']).reset_index().sort_values('values', ascending=False)
brand_group['values'] = brand_group['values'].astype(int)
plt.figure(figsize=(16, 8))
plt.rcParams.update({'font.size': 10})
ax = sns.barplot(x=brand_group['Brand'], y=brand_group['values'], data=brand_group, hue='Brand', palette="icefire")
ax.set(ylabel='Number of Shoes')
plt.show()

按品牌销售的鞋子总数。作者图片

这是一个很大的阶级不平衡,Yeezys 在这两年的销量似乎远远超过了白人。让我们潜得更深。

#Grouping by Sneaker Name to see how the general distribution of ratings are
sneaker_group = pd.DataFrame(stockx_data.groupby(['Brand', 'Sneaker Name']).size(), columns=['values']).reset_index().sort_values('values', ascending=False)
sneaker_group['values'] = sneaker_group['values'].astype(int)
plt.figure(figsize=(20, 8))
plt.rcParams.update({'font.size': 10})
ax = sns.barplot(x=sneaker_group['Sneaker Name'], y=sneaker_group['values'], data=sneaker_group, hue='Brand', palette="icefire")
ax.set(ylabel='Number of Shoes')
plt.xticks(rotation=90)
plt.show()

按运动鞋名称销售的鞋子总数。作者图片

按销量排名前 10 的运动鞋品牌似乎主要由 Yeezys 占据。即:

sneaker_group[:10]

作者图片

尽管买方地区(州)可能有点显而易见,但让我们确认一下假设。

#Grouping by Region to see how the general distribution of ratings are
region_group = pd.DataFrame(stockx_data.groupby([‘Buyer Region’, ‘Brand’]).size(), columns=[‘values’]).reset_index().sort_values(by=’values’, ascending=False)
region_group[‘values’] = region_group[‘values’].astype(int)
plt.figure(figsize=(20, 8))
plt.rcParams.update({‘font.size’: 10})
ax = sns.barplot(x=region_group[‘Buyer Region’], y=region_group[‘values’], data=region_group, hue=’Brand’, palette=”icefire”)
ax.set(ylabel=’Number of Shoes’)
plt.xticks(rotation=90)
plt.show()

按买家区域列出的售出鞋子总数。作者图片

像加利福尼亚和纽约这样的州在这里有很多代表。这无疑证明了这些州(和俄勒冈州)的运动鞋文化是非常强大的。

region_group[:10]

作者图片

最后,让我们更好地了解一下利润与我们目前所看到的内容之间的关系。

order_group = pd.DataFrame(stockx_data.groupby([‘Order Date’, ‘Brand’]).size(), columns=[‘values’]).reset_index().sort_values(by=’values’, ascending=False)
order_group[‘values’] = order_group[‘values’].astype(int)
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(20, 12))
plt.rcParams.update({‘font.size’: 10})
ax1 = sns.lineplot(x=order_group[‘Order Date’], y=order_group[‘values’], data=order_group, hue=’Brand’, palette=”icefire”, ax=ax1)
ax2 = sns.lineplot(x=stockx_data[‘Order Date’], y=stockx_data[‘Profit’], data=stockx_data, hue=’Brand’, palette=”icefire”, ax=ax2)
ax1.set(ylabel=’Number of Shoes’)
plt.show()

一段时间内售出的鞋子数量(上图)。一段时间内每次销售的利润(下图)。作者图片

这讲述了一个非常有趣的故事——虽然 Yeezys 在 StockX 上销售的鞋子数量上占主导地位(根据该数据集),但白人的利润数字一直较高。将散点图矩阵可视化将有助于查看是否有任何固有的模式出现在数值测量中:

sns.pairplot(stockx_data, diag_kind=’kde’, hue=’Brand’, palette=’icefire’);

作者图片

很明显,销售价格和利润会有一个完美的线性关系,但除此之外,变量之间不会出现明显的模式。与 Yeezys 相比,我们在各种散点图中看到了更多的灰白色分散和变化。最后,值得注意的是,售出的平均鞋码似乎在 10 码左右。在美国,这个数字相当大,可能意味着销售的主要是男鞋。

因为我们最终想要对利润建模,所以让我们仔细看看它们的分布。

fig, (ax1, ax2) = plt.subplots(1,2, figsize=(16, 8))ax1 = sns.histplot(stockx_data.loc[stockx_data[‘Brand’]==’Yeezy (Adidas)’,’Profit’], kde=True, color=’midnightblue’, ax=ax1)
ax2 = sns.histplot(stockx_data.loc[stockx_data[‘Brand’]==’Off-White (Nike)’,’Profit’], kde=True, color=’maroon’, ax=ax2)ax1.set_title(‘Rating Distribution for Yeezy (Adidas) Products’)
ax2.set_title(‘Rating Distribution for Off-White (Nike) Products’)plt.show()

作者图片

一定要记住天平;该数据集中的灰白色计数要少得多,但它们都遵循类似的双峰分布,大频率接近 0–500,长尾将平均值向右倾斜。从线图中可以看出,我们确实证实了米白色的平均利润更高。

#Filtering dataframe to get the respective brands only
yeezy = stockx_data[stockx_data[‘Brand’]==’Yeezy (Adidas)’]
offwhite = stockx_data[stockx_data[‘Brand’]==’Off-White (Nike)’]

我们已经对我们的数据有了一个相当不错的想法,现在是时候开始构建模型了。

建模和分析

在我们开始编码之前,重要的是要经历第 1 部分中说明的步骤:
**1。**描述这个利润数据的最佳随机变量是什么?这种分布似乎表明,有两种类型的人从 yee zys/Off-white(双峰)中获利。此外,长尾可能代表更具排他性的产品,但我们的模型会将它们解读为噪音。为了说明这一点,我们可以使用学生的 t 分布。这种分布与正态分布非常相似,除了对异常值更稳健,这对我们非常有用。这种假设反映了我们所认为的利润人群会得到我们所拥有的信息。考虑到世界上流行的高斯人是多么有效,这可能是一个很好的起点。

**2。**接下来,我们想,“好吧,假设利润是学生的 t 分布,我需要正态分布吗?”嗯,t 分布有三个参数 mu ( μ ) sigma ( σ ),nu ( v )。这些表示平均值、标度(与数据的分布相关)和 t 分布的自由度,它们决定了 t 分布的形状。

**3。**我们知道 *μ,σ,*和 v 吗?不,我们对它的最好估计是我们分布的均值和标准差。似乎每个品牌都有不同,所以我们能做的是设定一个先验来评估它们。

**4。**对于 *μ,σ,*和 v 有哪些好的分布?对于 μ ,我们可以用一个很宽的均匀分布来体现我们的无知。从本质上讲,我们认为在给定的范围内,平均值可能是相同的。仅仅通过观察分布,我们就可以得到 Yeezys 的范围是[0,500],灰白色的范围是[0,1000]。对于 σ ,我们知道标准差需要有正值,因此我们可以使用半正态分布,并通过使用宽范围[0,10]来反映我们的无知。最后,我们也不知道 v 的分布,所以我们可以遵循推荐的文献,使用另一个弱信息先验。这意味着一个平均值为 30 的指数分布。这基本上反映了我们或多或少地认为 v 应该在 30 左右,但是可以轻松地移动到更小和更大的值。

到目前为止,我们讨论的所有观点都反映了我们对两个品牌利润的人口分布的各种假设和忽视。请记住,这个过程的目标是从某个相当好的地方开始,获得新的证据,然后更新我们的先验和可能性,以获得更准确/精确的后验。

#Creating a model with respective mu, sigma, and y distributions
with pm.Model() as model:
    mu_offwhite = pm.Uniform('mu_offwhite', 0, 1000)
    sigma_offwhite = pm.HalfNormal('sigma_offwhite', sd=10)
    nu_offwhite = pm.Exponential('nu_offwhite', 1/30)
    y_offwhite = pm.StudentT('y_offwhite', mu=mu_offwhite, sd=sigma_offwhite, nu=nu_offwhite, observed=offwhite['Profit_transformed'])mu_yeezy = pm.Uniform('mu_yeezy', 0, 500)
    sigma_yeezy = pm.HalfNormal('sigma_yeezy', sd=10)
    nu_yeezy = pm.Exponential('nu_yeezy', 1/30)
    y_yeezy = pm.StudentT('y_yeezy', mu=mu_yeezy, sd=sigma_yeezy, nu=nu_yeezy, observed=yeezy['Profit_transformed'])

pm.model_to_graphviz(model)

作者图片

你可能想知道为什么我在同一个模型中有两种利润分布。这应该不会对我们的模型产生很大的影响,如果有的话,对我们后面要讨论的部分会有帮助。

with model:
    trace = pm.sample(10000, tune=2000, target_accept=.9, return_inferencedata=True, random_seed=RANDOM_SEED)

关于排除发散链故障的快速说明:当您创建自己的 MCMC 模型时,您可能会遇到后验分布收敛的问题。马尔可夫链蒙特卡罗(MCMC)使用马尔可夫链𝑞0,…,𝑞𝑁.的状态来逼近关于给定目标(后验)分布的期望问题是,只有当链变得无限长时,它们才能保证精确。这通常在计算上是不可行的,因此我们试图在不耗尽资源的情况下快速收敛到目标分布。不幸的是,这需要一个叫做几何遍历的条件才能成立。这个条件本质上使我们能够在仅利用有限次迭代时遵循 MCMC 模型的中心极限定理。从技术上讲,我们无法以简单的方式证明几何遍历性,但我们可以观察到链中的发散,这表明条件已被违反。采样中发现的 Rhat 统计和发散链是违反该条件的关键指标,如果您发现了这一点,我建议尝试以下策略:

  1. 增加“调整”参数:如果你想从你的模型中抽取“真实的”(无偏的)样本,你需要“调整”(让它收敛)这个链。默认情况下,tune 对 1000 个样本执行此操作,但增加此操作会让模型有更多时间达到收敛。
  2. 增加抽取的样本数量:马尔可夫链从随机分布开始,慢慢收敛到你的模型的分布。在调整步骤收敛后,增加绘制次数可以为模型提供更多样本供选择。
  3. 增加“target_accept”参数:这等同于降低机器学习模型中的学习速率。降低该值可使采样器确保更少的发散。

如何对推理引擎进行最佳故障诊断的理论和解释在这里得到了最好的说明。

var_names = ['mu_offwhite', 'sigma_offwhite', 'nu_offwhite', 'mu_yeezy', 'sigma_yeezy', 'nu_yeezy']
lines = list(xarray_var_iter(trace.posterior[var_names].mean(dim=("chain", "draw"))))
az.plot_trace(trace, lines=lines);

作者图片

作者图片

对我们前科的初步分析看起来不错。左边的 KDE 图平滑且相对对齐(尽管在这里和那里可以看到轻微的偏差),右边的图看起来更像白噪声,没有明显的模式。

在继续前进之前,对你的过去进行更多的分析是一个很好的习惯。请记住,从数据中获得洞察力的桥梁是通过假设,因此理解和提炼我们的先验知识非常重要!幸运的是,ArviZ 让我们的工作变得简单多了。

with model:
 prior_checks = pm.sample_prior_predictive(random_seed=RANDOM_SEED)
 idata_prior = az.from_pymc3(prior=prior_checks)

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(16,8))
ax1 = az.plot_dist(idata_prior.prior[‘mu_yeezy’], kind=’kde’, rug=True, 
 quantiles=[.25, .5, .75], color=’midnightblue’, ax=ax1)
ax2 = az.plot_dist(idata_prior.prior[‘mu_offwhite’], kind=’kde’, rug=True,
 quantiles=[.25, .5, .75], color=’maroon’, ax=ax2)ax1.set_title(‘Prior Distribution for mu_yeezy’)
ax2.set_title(‘Prior Distribution for mu_offwhite’)plt.show()

作者图片

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(16,8))ax1 = az.plot_dist(idata_prior.prior[‘sigma_yeezy’], kind=’kde’, rug=True, 
 quantiles=[.25, .5, .75], color=’midnightblue’, ax=ax1)
ax2 = az.plot_dist(idata_prior.prior[‘sigma_offwhite’], kind=’kde’, rug=True,
 quantiles=[.25, .5, .75], color=’maroon’, ax=ax2)ax1.set_title(‘Prior Distribution for sigma_yeezy’)
ax2.set_title(‘Prior Distribution for sigma_offwhite’)plt.show()

作者图片

az.summary(trace)

作者图片

这些先验分布图并不意味着与我们的利润分布相匹配;在给定数据的情况下,我们正在研究模型发现的每个品牌利润的均值和标准差的可能分布。所以对于平均的先验分布,我们确实看到平均值与我们在每个利润分布中发现的相对接近。事实上,对于 yeezy,该模型能够拾取由较大数据分布组成的较大的第一个尖峰。我们估计值的低标准偏差是一大优势。此外,查看摘要,我们可以看到 rhat 的良好数字和相当低的 MCSE 误差,这是一个好现象。最后,我们看到自由度的估计值约为 2.4,这表明我们的分布确实有较重的尾部(由于异常值),选择 Student 的 t 分布是一个很好的选择,因为它可以为我们提供更稳健的估计。

我们肯定要进行后验预测检验,看看后验分布是否成立。

with model:
 ppc = pm.sample_posterior_predictive(trace, var_names=var_names+[‘y_offwhite’, ‘y_yeezy’], random_seed=RANDOM_SEED)fig, (ax1,ax2) = plt.subplots(1,2, figsize=(10, 5))
ax1.hist([y.mean() for y in ppc['y_yeezy']], bins=19, alpha=0.5, color='midnightblue')
ax2.hist([y.mean() for y in ppc['y_offwhite']], bins=19, alpha=0.5, color='maroon')
ax1.axvline(yeezy['Profit'].mean(), color='r')
ax2.axvline(offwhite['Profit'].mean(), color='r')
for ax in [ax1, ax2]:
    ax.set(xlabel='x', ylabel='')
ax1.set_title('Posterior Predictive Check for y_yeezy')
ax2.set_title('Posterior Predictive Check for y_offwhite');

作者图片

红线代表我们观察到的各个利润分布的平均值。这里很容易说我们的模型没有像我们最初想的那样学习数据,但是让我们考虑一下我们的假设。我们第一眼看到的利润分布显示,由于离群值的长尾效应,利润分布明显向右倾斜。我们的假设是,这些异常值并不代表每个品牌的利润,如果我们想更准确地了解哪个品牌的利润率更高,我们应该构建一个对这些异常值稳健的模型。因此,我们选择学生的 t 分布作为我们的可能性。这是一个直接的结果。我们观察数据的平均值应该高于我们后验分布的平均值,因为我们明确想要一个对异常值更稳健的目标分布。

既然我们对每个利润分配都有了一个看似强大的模型,我们就可以明显地看到米色的平均利润率更高,或者进行典型的 t 检验来评估差异。对我们来说幸运的是,贝叶斯估计为我们提供了一个很好的比较群体的方法,同时也产生了对不确定性的评估。

分组比较

with model:
    diff_means = pm.Deterministic('means difference', mu_yeezy-mu_offwhite)
    diff_std = pm.Deterministic('stds difference', sigma_yeezy-sigma_offwhite)
    effect_size = pm.Deterministic('effect size (cohens d)', diff_means/np.sqrt((yeezy['Profit'].std() ** 2 + offwhite['Profit'].std() ** 2) / 2))

我们可以计算均值、标准差和效应大小的差异,然后用确定性对象来表示我们希望将采样值记录为输出的一部分。

对于效应大小,我们可以使用 Cohen 的 d 测量值来计算,d 测量值是两组平均标准差的平均值之差。

with model:
 trace = pm.sample(5000, return_inferencedata=True, random_seed=RANDOM_SEED)pm.plot_posterior(trace, var_names=['mu_yeezy', 'mu_offwhite', 'sigma_yeezy', 'sigma_offwhite'], color='#87ceeb');

作者图片

pm.plot_posterior(trace, var_names=['means difference', 'stds difference', 'effect size (cohens d)'], ref_val=0, color='#87ceeb');

作者图片

作者图片

az.summary(trace, var_names=['means difference', 'stds difference', 'effect size (cohens d)'])

作者图片

如果 Yeezy 和 Off-White 之间的利润率出现巨大差异,我们会看到什么;Yeezys 在该数据集中的销售额可能更高,但利润率比非白种人低 330 美元左右。效果大小也非常重要,可以解释为 Z 分数。所以 Yeezy 的利润率比白人低 1.3 个标准差。

结论

贝叶斯估计和建模的惊人之处在于,它为您提供了分布的稳健不确定性估计,并且您可以轻松地添加新的数据/证据来完善您的先验知识。使用 PyMC3,我们可以将这一点进一步扩展到分层建模,我们不仅可以比较品牌,还可以对每款鞋的目标分布进行建模,即使我们只有每款鞋相对少量的数据。

我们最初的目标是为 Yeezys 和 Off-white 的利润率建模,以帮助我们在转售世界中找到更好的权衡。看看我们的数据集,Yeezys 的转售数量占压倒性多数,这给人的印象是,这些将是更好的目标选择。在使用贝叶斯估计对我们的目标利润分布进行建模后,我们发现白色人种的利润率可以高出近 1.3 个标准差。作为耐克的狂热爱好者,这是一个好消息!

参考文献

[1]奥斯瓦尔多·马丁,用 Python 进行贝叶斯分析

[2]卡梅隆·戴维森-皮隆,黑客的概率编程和贝叶斯方法

[3] PyMC3,事前和事后预测检查

[4] PyMC3,贝叶斯估计取代了 T 检验

[5] PyMC3,诊断有偏差的推理与分歧

ETL 管道中的应用并发技术

原文:https://towardsdatascience.com/applied-concurrency-techniques-for-etl-pipelines-32387eb82fbc?source=collection_archive---------17-----------------------

Python 并发方法与案例场景

照片由突发

并发性意味着在重叠的时间间隔内运行程序的组成部分&并行性意味着并行运行所有部分的能力(这也取决于系统的能力,如多核和语言支持)。如果我们想让程序多任务化(并行)或表现得像多任务化(并发),我们必须理解这些概念是什么,何时以及如何应用的。今天,在这篇文章中,我将尝试解释这些技术以及它们通过一个简单的 ETL 管道的实现。

词汇:

  1. python 技术简介。
  2. 通过示例 ETL 过程实现
  3. 所有技术的执行时间表

简介:在 python 中,我们有一个强大的模块,可以在任何程序中实现并发/并行。这里,下面是三个需要实现的模块—

  • concurrent . futurers . threadpool executor
  • concurrent . futures . processpoolexecutor
  • 阿辛西奥

我们将在每个单独的管道中使用上述模块,并读取性能,但在此之前,让我们尝试了解这些模块是关于什么的?

  • 并发。期货包提供了执行器接口,可以向线程进程提交作业。一个作业可以提交给线程实现多线程( ThreadPoolExecutor 将被使用),一个作业提交给进程实现多处理,然后 ProcessPoolExecutor 将被使用。
  • 另一方面,asyncio(异步 i/o)类似于线程,但它通过单线程或事件循环来归档并发性。

现在,我们将在实施部分讨论何时以及哪个模块适合任何特定的场景——

实现 —我们将获取一个 CSV 文件,其中包含通过示例 ETL 流程中的提取功能处理的机场频率记录。转换函数将这两个值['airport_ident,' ' ***']连接到输出文件中的新列。这是一个非常简单的场景,显示了实现我们的技术(并发/并行)的 ETL 执行流程。

线程支持 i/o 绑定操作、网络 i/o、磁盘操作,多进程支持 CPU 绑定操作。平台上有很多很好的文章来了解什么是线程&进程,为什么是操作特定的!为了继续我们的讨论,我们需要了解哪个组件/模块支持哪种操作。

下面是如何使用每种技术运行它。

  1. 多线程——正如我们已经讨论过的,每个线程的操作在重叠的时间间隔内并发运行。并且这种重叠与以并行方式执行一样快。

此外,在实现多线程时,我们需要记住一些事情,比如线程间的竞争条件使程序的关键部分容易出错;在 ETL 或任何程序中,这可能会导致数据加载/处理损坏。为了避免这种竞争情况或控制任务执行的流程,我们可以使用锁或信号量。如我们的 ETL 示例所示。此外,下面的示例截图显示了线程是如何执行的,当前进入和退出执行的是哪个线程!

线程执行-作者图片

注意——在 python 的线程化中,我们还要记住一件重要的事情,那就是 GIL(global interpreter lock),它通过对一个解释器应用全局锁,使得任何程序一次只能运行一个线程。

from concurrent.futures import ThreadPoolExecutor
import pandas as pd
import time##Threading:
##- A new thread is spawned within the existing process
##- starting a thread is faster than starting a process
##- memory is shared between all threads
##- mutexes often necessary to control access to shared data
##- on GIL (Global Interpreter Lock) for all threads##semaphore locks with lock access count
semTrn = Semaphore(4)
semLd = Semaphore(4)

def extract(file):
    dtype_dict = {'id': 'category',
                  'airport_ref': 'category',
                  'airport_ident': 'category',
                  'type': 'category',
                  'description': 'category',
                  'frequency_mhz': 'float16'
                  }

    df = pd.read_csv(file, dtype=dtype_dict, low_memory=False)
    return df

def transform(df):
    ##semaphore lock
    semTrn.acquire()
    print("thread {} acquired tranform lock ".format(threading.currentThread().ident))
    ##basic transformation operation

    df['ref_code'] = df['airport_ident'].astype(str)+str('***')
    semTrn.release()
    print("thread {} released tranform lock ".format(threading.currentThread().ident))
    print("thread {} acquired load lock ".format(threading.currentThread().ident))
    semLd.acquire()
    load(df)

def load(tdf):

    tdf.to_csv('airport_freq_output.csv', mode='a', header=False, index=False)
    semLd.release()
    print("thread {} released load lock  ".format(threading.currentThread().ident))
    print("thread {} load completion ".format(threading.currentThread().ident))

def main():
    pd.set_option('mode.chained_assignment', None)
    file = 'airport_freq.csv'
    df = extract(file)
    chunk_size = int(df.shape[0] / 4)
    ##t = [0] * 4
    executor = ThreadPoolExecutor(max_workers=4)
    lst = list()
    for start in range(0, df.shape[0], chunk_size):
        df_subset = df.iloc[start:start + chunk_size]
        ##df_subset.is_copy=None
        lst.append(executor.submit(transform, df_subset))
    for future in lst:
        future.result()
    executor.shutdown()

if __name__ == "__main__":
    start = time.time()
    main()
    end = time.time() - start
    print("Execution time {} sec".format(end))

2.多处理——在这种情况下,python 提供了在每个进程中运行程序任务的能力,并且全局解释器锁的限制也得到扩展(这意味着我们可以通过多处理接口正式并行运行每个任务),但这也带来了其他特定的约定,例如每个进程比线程花费更多的创建时间。另外,每个进程都需要自己的内存执行空间;这使得成本很高,所以在用户被要求运行任何特定于 CPU 的活动之前,多处理对于 i/o 相关的任务来说是很昂贵的。下面显示了示例 ETL 实现的执行流程。

多进程执行流程—作者图片

from concurrent.futures import ProcessPoolExecutor
import pandas as pd
import time##MultiProcessing
##- A new process is started independent from the first process
##- Starting a process is slower than starting a thread
##- Memory is not shared between processes
##- Mutexes not necessary (unless threading in the new process)
##- One GIL(Global Interpreter Lock) for each processsemTrn = Semaphore(5)
semLd = Semaphore(5)
def extract(file):
    dtype_dict = {'id': 'category',
                  'airport_ref': 'category',
                  'airport_ident': 'category',
                  'type': 'category',
                  'description': 'category',
                  'frequency_mhz': 'float16'
                  }

    df = pd.read_csv(file, dtype=dtype_dict, low_memory=False)
    return df

def transform(df):
    print("process {} transform started ".format(multiprocessing.current_process().pid))
    ##basic transformation operation
    semTrn.acquire()
    df['ref_code'] = df['airport_ident'].astype(str)+str('***')
    semTrn.release()
    print("process {} transform completion ".format(multiprocessing.current_process().pid))
    semLd.acquire()
    load(df)

def load(tdf):
    print("process {} load started".format(multiprocessing.current_process().pid))
    tdf.to_csv('airport_freq_output.csv', mode='a', header=False, index=False)
    semLd.release()
    print("process {} load completion ".format(multiprocessing.current_process().pid))

def main():
    file = 'airport_freq.csv'
    df = extract(file)
    chunk_size = int(df.shape[0] / 4)
    executor = ProcessPoolExecutor(max_workers=5)
    lst = list()
    for start in range(0, df.shape[0], chunk_size):
        df_subset = df.iloc[start:start + chunk_size]
        lst.append(executor.submit(transform, df_subset))
    for future in lst:
        future.result()
    executor.shutdown()

if __name__ == "__main__":
    start = time.time()
    main()
    end = time.time() - start
    print("Execution time {} sec".format(end))

3.Asyncio —该模块为任何网络、磁盘或基于 web 的操作提供高性能。我们可以使用 asyncio 模块编写并发代码。这可以在函数中使用异步/等待条件来实现;以下 ETL 示例的执行展示了 asyncio 技术的实现。

import asyncio
import time
import pandas as pd##coroutine - wrapped version of the function to run async
##async function in python is typically called coroutine
##coroutines declared with async/await syntax
##coroutines are special functions that return coroutine objects when called

##event loop -
##the event loop is a very efficient task manager
##coordinate tasks
##run asynchronous tasks and callbacks

def extract(file):
    dtype_dict = {'id': 'category',
                  'airport_ref': 'category',
                  'airport_ident': 'category',
                  'type': 'category',
                  'description': 'category',
                  'frequency_mhz': 'float16'
                  }
    df = pd.read_csv(file, dtype=dtype_dict,low_memory=False)
    return df

async def transform(df):

    df['ref_code'] = df['airport_ident'].astype(str)+str('***')
    await load(df)

async def load(tdf):

    tdf.to_csv('airport_freq_out.csv', mode='a', header=False, index=False)
    await asyncio.sleep(0)

async def main():
    pd.set_option('mode.chained_assignment', None)
    file = 'airport_freq.csv'
    df = extract(file)
    chunk_size = int(df.shape[0] / 4)
    for start in range(0, df.shape[0], chunk_size):
        df_subset = df.iloc[start:start + chunk_size]
        x = asyncio.create_task(transform(df_subset))
        await x

start = time.time()
asyncio.run(main())
end=time.time()-start
print("execution time {} sec".format(end))

执行时间:

作者图片

几秒钟!!—作者图片

结论:从我们的样本运行来看,我们可以假设 asyncio 线程方法适合我们的场景,因为它涉及到文件的 i/o 处理。多重处理对我们的系统来说是昂贵的,尽管它涉及并行处理。

今天的帖子通过一个示例 ETL 强调了应用并发性/并行性的技术。我希望这个细节对理解这些技术并在您的实现中使用有所帮助。在以后的文章中,我将尝试在任何数据集的 groupby 和 aggregation 等操作上实现这些技术。

GitHub 地点—https://github.com/Shivakoreddi/ConcurrencyPipelines

参考:

  1. Python 文档,https://docs.python.org/3/library/asyncio.html
  2. 塞尔达尔,https://www . infoworld . com/article/3632284/python-concurrency-and-parallelism-explained . html,2021 年
  3. http://pymotw.com/2/threading/[PyMOTW](http://pymotw.com/2/threading/)

应用的距离测量;建立高等教育比较小组

原文:https://towardsdatascience.com/applied-distance-measures-building-higher-education-comparison-groups-22d4985f23b5?source=collection_archive---------40-----------------------

庞余浩在 Unsplash 上拍照

美国教育部的数据演示,如何使用距离测量来指定大学比较组

介绍

想象一个场景,一所学院或大学受到负面印刷、在线和社交媒体关注等形式的批评。假设注意力集中在学校的本科申请费用上。

机构如何应对?如果你对回应媒体关注的想法包括将这个机构与其他类似的机构进行比较,那么这篇文章是为你准备的。这个负面的媒体关注假设提供了一个应用距离测量的案例研究。机构可以使用这些距离度量来确定有意义的比较组。如果你想在任何数据中找出有意义的比较组,这篇文章也会对你有帮助。

依靠之前出版的食谱,这篇文章将距离测量应用于美国教育部的数据。这些数据包括来自 1,539 个机构的观察结果(7,000 多个观察结果的子集)。任务是找到一份具有相似特征的机构清单。特征相似的机构的列表可以作为一个比较组。然后,比较组将支持其他分析。

例如,一个机构可能会经历关于其本科大学申请费的负面报道。这种技术可以支持一种分析,这种分析将显示与一组类似的机构相比,该机构的申请费是相对高、低还是中等。

下面我将首先指出以前发表的讨论这些数据的资源,我是如何获得这些数据的,以及我是如何准备这些数据进行分析的。接下来,我还将提到以前的文章,这些文章展示了使用距离度量来寻找比较组的过程。最后,用本科申请费的例子,我将展示一个机构如何使用比较组来分析它的申请费。在结论中,我将讨论这项技术的其他应用,可能涉及其他数据。

要记住的一个警告或“诀窍”是,这一高等教育数据已经包括了预先指定的对照组的信息。照片由詹妮弗·拉图佩丽莎-安德森在 Unsplash 拍摄

警告

有一个警告或“窍门”要记住。一定要观察整个大象,这样你就不会分类错误或错过整个画面。

虽然本文旨在说明一种可以在教育机构之间建立比较组的方法,但这些数据也包括预先指定的比较组(例如,“NCES 创建的数据反馈报告比较组”)。美国教育部网站提供了关于那些已经指定的对照组的信息。本文中讨论的方法,为了这个特定的案例研究的目的,然后补充和添加到预先指定的比较组。

数据

变量

这些数据来自美国教育部。这些数据易于访问和准备分析。我以前写过一些代码示例,这些代码可以自动获取这些数据。

在本文中,我使用的数据版本包括以下变量。

unitid   : A unique identification number for each institution.
instnm   : The institution's name.
stabbr   : The abbreviation of the institution's state.
sector   : A categorical that indicates multiple factors
           such as, public, private, 2yr, 4yr, etc.
iclevel  : A categorical that indicates 2yr, 4yr, etc.
control  : A categorical that indicates public, or private.
locale   : A categorical that indicates the campus urbanicity.
instcat  : A categorical that indicates degree-granting status.
c18basic : Classification; Carnegie Commission on Higher Ed.
instsize : A categorical that indicates enrollment counts.
longitud : The campus geographic longitude.
latitude : The campus geographic latitude.
roomcap  : The residence hall room capacity.
roomamt  : The residence hall room fees.
boardamt : The residence hall meal plan fees.
rmbrdamt : Room & bard amount combined (if no roomamt or bardamt)
applfeeu : The undergraduate application fee.
applcn   : Count of undergraduate applications.
admssn   : Count of undergraduate admissions.
enrlft   : Count of full-time student enrollment.
enrlt    : Count of student enrollment.

从这些数据中我们可以计算出新的数据点。例如,我们可以通过将admssn除以applcn来估计选择性。由此得出的数字是每所学校录取的申请人比例。我们还可以通过除以enrlt / admssn来估计该校的产量,即录取的学生比例。

accept   : Estimated selectivity admssn / applcn.
yield    : Estimated admission yield enrlt / admssn.

观察结果

我进一步将数据限制到特定的观察类型。这些数据最初包括 2 年期机构、不到 2 年期机构、职业机构(例如,美容、电气、管道、油漆、农业等)的观察数据。),以及营利性机构。

对于本文中的演示,我将数据限制为包括来自 4 年非营利私人机构和 4 年公共机构的观察结果。数据也受到信息缺失的影响。去除缺失信息后,最终观测值为 1,539 个观测值。

图 1 显示了数据的摘录。

图 1,作者摘录的数据。

图 2 显示了数据的汇总。

图 2,作者呈现的数据摘要。用熊猫轮廓制作。

寻找一个比较组

为了便于演示,假设有一个虚构的机构(Wiscesota 大学)具有以下特征:

instnm                                 University of Wiscesota
stabbr                                                      IA
sector                                 Public, 4-year or above
iclevel                                     Four or more years
control                                                 Public
locale                                             City: Large
instcat      Degree-granting, primarily baccalaureate or above
c15basic      Doctoral Universities: Highest Research Activity
instsize                                      20,000 and above
longitud                                              -92.7237
latitude                                                42.773
roomcap                                                   8998
applfeeu                                                    22
applcn                                                   42596
admssn                                                   18153
enrlft                                                    7597
enrlt                                                     6303
alloncam                                                 2\. No
accept                                                0.426167
yield                                                 0.347215
roomamtX                                                  6755
boardamtX                                                 4275

使用之前出版的食谱,我们找到了如图 3 所示的对照组。

图 3,作者描绘了一个虚构的 Wiscesota 大学的对比组。先前出版的食谱说明并解释了如何找到这个比较组。根据代码中设置的参数,结果可能会有所不同。

使用比较组

我在上面提出了一个假设,在这个假设中,一个机构,Wiscesota 大学,最近经历了对其本科申请费的批评。在找到一个在经验上站得住脚的对照组后,我们接下来可以看看该费用在整个对照组的其他机构中的比较情况。

图 4 显示了每个机构的本科申请费。事实证明,Wiscesota 大学的本科申请费是所有大学中最低的。

图 4,作者描绘了一个比较组,以及虚拟的 Wiscesota 大学的比较组中每个机构的申请费。

从这个输出中我们看到,Wiscesota 大学不仅要求最低的费用,而且下一个最低的 45 美元的费用是 Wiscesota 的两倍多。最高收费是 Wiscesota 收费的近 4 倍。

这些信息对 Wiscesota 大学的专业招生人员来说很有价值,因为他们决定如何回应最近的批评。这一信息也可能会告知 Wiscesota 关于申请费的未来计划。

优势、劣势和局限性

在这篇文章中,我给出了一个高层次的技术概述,这些技术可以支持需要一个比较组的各种分析。这篇文章的一个弱点和局限性是,各种来源定期进行类似的分析,以产生库存,现成的,现成的比较组。许多高等教育分析师、数据消费者和其他人已经熟悉现成的比较组。使用家庭自制的比较组,比如用这里介绍的方法制作的比较组,有与其他更知名的比较组竞争的风险。

然而,在多个比较组之间复制分析,除了用这些方法生成的分析之外,还包括那些现成的(如美国教育部预先指定的)分析,可以支持更完整的分析。例如,如果多个潜在比较组的相同分析产生相似的结果,差异的一致性将增加发现的可信度。

此外,并不是所有的数据都有现成的比较组。在下面的结论中,我讨论了使用学生级别或员工级别的数据在这些人群中构建和分析比较组的场景。

这种技术的另一个缺点是,它可能会强化或复制现有的偏见,包括对少数民族个体(例如,非白人、非男性、非 cis、非 hetero)的制度化排斥和压迫。本文并没有解决这些或类似的技术可能会强化或重现现有偏见的问题。如果一个机构或分析师采用这些方法,就需要额外的步骤来调整和纠正这些缺点。

至少还有一个弱点和局限值得关注。本文(上文和下文)中讨论的技术,以及它的配套文章,很少提供支持因果分析的前景。为了更好地理解如何或为什么机构设置他们的申请费,如上所述,一系列的实验或准实验分析将是必要的。同样,一项实验或准实验技术对于帮助理解学生如何应对申请费的变化的分析是必要的。

结论

这篇文章应用距离测量方法在全美的高等教育机构中寻找一个比较组。在找到这个对照组之后,本文接着使用这个对照组来分析这个机构的本科申请费。

这项技术可以支持其他分析目标。例如,某个机构可能有兴趣研究其员工。该机构可能会发现其员工的平均年龄和中值年龄。但下一个合乎逻辑的问题是,平均值和中值高吗?低吗?你怎么知道什么是规范?一个答案是,你转向整个对照组的平均值和中间值。

如果你有观察个体的数据,这种技术也可以找到相似的个体。例如,继续本科生入学的主题,考虑一下 Wiscesota 的一个场景,招生主任想要寻找一种新的方法来识别有可能在该机构表现良好的学生。

https://adamrossnelson.medium.com/membership

照片由泰在 Unsplash 上抓拍

想象一下,举一份在 Wiscesota 完成第一年学业的学生名单。然后根据第一年的平均绩点对名单进行排序。之后取前十名学生。然后,招生办主任可以使用距离测量来确定下一年申请人中与那些表现优异的学生相似的学生。

另一个场景,假设院长的目标是提高产量。然后,院长可以开出一份名单,列出在学校表现良好、提前退还了注册押金的学生。再次使用距离测量,院长可以在随后的申请人中寻找申请人,以确定那些既有可能获得更高比率又在该机构表现良好的学生。

感谢阅读

感谢阅读。把你的想法和主意发给我。你可以写信只是为了说声嗨。如果你真的需要告诉我是怎么错的,我期待着尽快和你聊天。推特:@ adamrossnelson| LinkedIn:亚当·罗斯·纳尔逊 |脸书:亚当·罗斯·纳尔逊。

Python 中生产计划系统的应用模型

原文:https://towardsdatascience.com/applied-models-of-production-planning-systems-in-python-942a2c91e6ad?source=collection_archive---------6-----------------------

在 Python 中开发了生产计划系统之后,可以扩展我们的系统,以包括人员配备和材料模型,从而将这些计划作为详细的工作计划和订单报告来实施。

作者在谷歌地球上拍摄的制造工厂照片

在我们的上一篇文章Python 中制造系统的生产计划和资源管理中,我们介绍了如何获取一系列具有已知持续时间、依赖性和所需资源的制药任务,并加载一个矩阵以最小化计划中的停机时间。从这个矩阵中,我们可以生成资源利用统计数据,以及用于跟踪进度的交互式甘特图。

虽然我们项目的最后一次迭代生成了一个功能齐全的时间表,但真正的制造计划需要分配、跟踪和报告额外的信息和资源。在本文中,我们将介绍上一个项目的一些扩展,这些扩展允许我们的生产计划具有额外的覆盖范围和高级分析。

我们将讨论的主题包括:

  • 调度过程中的任务优先级
  • 操作员的任务培训要求
  • 操作员培训跟踪、装载、利用和成本核算
  • 增值任务分析
  • 逐班报告和说明
  • 消耗材料报告和计划

如果你还没有阅读前一篇文章,请点击这里来看看目前为止都报道了些什么!

我们调度模型的所有任务数据都来自一个 JSON 文件。下面的文件描述了我们的任务数据,修改后包括附加数据以帮助后期调度,以及一些附加任务。

{
  "Items": [
    {
      "ID":1,
      "Name":"Seed",
      "Description":"Start fermentation from vial of seed material.",
      "Process":"Fermentation",
      "ResourcesRequired":
      {
        "Operator":2,
        "SafetyCabinet":1
      },
      "Dependencies":[0],
      "Duration":2,
      "TrainingRequirements":["1A"],
      "ValueAdding":1,
      "Priority":1,
      "Materials":
      {
        "SeedVial":4
      }
    },
    {
      "ID":2,
      "Name":"Flasks",
      "Description":"Ferment seed material in flasks.",
      "Process":"Fermentation",
      "ResourcesRequired":
      {
        "Operator":2,
        "SafetyCabinet":1
      },
      "Dependencies":[1],
      "Duration":3,
      "TrainingRequirements":["1A"],
      "ValueAdding":1,
      "Priority":1,
      "Materials":
      {
        "Flasks":4,
        "Agar":2
      }
    },
    {
      "ID":3,
      "Name":"Small Fermentor",
      "Description":"Ferment in small fermentation vessel.",
      "Process":"Fermentation",
      "ResourcesRequired":
      {
        "Operator":2,
        "SmallFermentor":1
      },
      "Dependencies":[2],
      "Duration":3,
      "TrainingRequirements":["1B"],
      "ValueAdding":1,
      "Priority":1,
      "Materials":
      {
        "GrowthMedium":10
      }
    },
    {
      "ID":4,
      "Name":"Large Fermentor",
      "Description":"Ferment and inactivare in large fermentation vessel.",
      "Process":"Fermentation",
      "ResourcesRequired":
      {
        "Operator":2,
        "LargeFermentor":1
      },
      "Dependencies":[3],
      "Duration":4,
      "TrainingRequirements":["1B"],
      "ValueAdding":1,
      "Priority":1,
      "Materials":
      {
        "GrowthMedium":50
      }
    },
    {
      "ID":5,
      "Name":"Prepare Filters",
      "Description":"Prep purification filters for next step.",
      "Process":"Purification",
      "ResourcesRequired":
      {
        "Operator":1,
        "PurificationTank":1
      },
      "Dependencies":[3],
      "Duration":1,
      "TrainingRequirements":["2A"],
      "ValueAdding":0,
      "Priority":1,
      "Materials":
      {
        "Ethanol":20,
        "Filters":2
      }
    },
    {
      "ID":6,
      "Name":"Purification Filters",
      "Description":"Start purification in first purification assembly.",
      "Process":"Purification",
      "ResourcesRequired":
      {
        "Operator":3,
        "PurificationTank":1
      },
      "Dependencies":[4,5],
      "Duration":4,
      "TrainingRequirements":["2A"],
      "ValueAdding":1,
      "Priority":1,
      "Materials":
      {
      }
    },
    {
      "ID":7,
      "Name":"Centrifuge",
      "Description":"Separate material in centrifuges.",
      "Process":"Purification",
      "ResourcesRequired":
      {
        "Operator":2,
        "Centrifuge":2
      },
      "Dependencies":[6],
      "Duration":4,
      "TrainingRequirements":["2B"],
      "ValueAdding":1,
      "Priority":1,
      "Materials":
      {
        "Bowls":2,
        "Inserts":2
      }
    },
    {
      "ID":8,
      "Name":"Sterile Filter",
      "Description":"Start sterilization of material.",
      "Process":"Sterile Boundary",
      "ResourcesRequired":
      {
        "Operator":3,
        "SterileAssembly":1
      },
      "Dependencies":[7],
      "Duration":2,
      "TrainingRequirements":["2C"],
      "ValueAdding":1,
      "Priority":1,
      "Materials":
      {
        "SterileFilters":4,
        "SterileTubing":4
      }
    },
    {
      "ID":9,
      "Name":"Adjuvants",
      "Description":"Add adjuvants to bulk material.",
      "Process":"Sterile Boundary",
      "ResourcesRequired":
      {
        "Operator":2,
        "SterileAssembly":1
      },
      "Dependencies":[8],
      "Duration":2,
      "TrainingRequirements":["3A"],
      "ValueAdding":1,
      "Priority":1,
      "Materials":
      {
        "Aluminum":10
      }
    },
    {
      "ID":10,
      "Name":"Prepare Vials",
      "Description":"Sterilize bulk vials.",
      "Process":"Sterile Boundary",
      "ResourcesRequired":
      {
        "Operator":2,
        "VialFiller":1
      },
      "Dependencies":[8],
      "Duration":1,
      "TrainingRequirements":["3B"],
      "ValueAdding":0,
      "Priority":1,
      "Materials":
      {
        "Vials":1000
      }
    },
    {
      "ID":11,
      "Name":"Fill",
      "Description":"Fill vials with bulk material.",
      "Process":"Sterile Boundary",
      "ResourcesRequired":
      {
        "Operator":2,
        "VialFiller":1
      },
      "Dependencies":[9,10],
      "Duration":3,
      "TrainingRequirements":["3B"],
      "ValueAdding":1,
      "Priority":1,
      "Materials":
      {
        "Caps":1000
      }
    },
    {
      "ID":12,
      "Name":"Clean Vial Filler",
      "Description":"Clean vial filling machine.",
      "Process":"Sterile Boundary",
      "ResourcesRequired":
      {
        "Operator":2,
        "VialFiller":1
      },
      "Dependencies":[11],
      "Duration":2,
      "TrainingRequirements":["4A"],
      "ValueAdding":0,
      "Priority":2,
      "Materials":
      {
        "CleaningAgent":5
      }
    },
    {
      "ID":13,
      "Name":"Decontaminate",
      "Description":"Decontaminate fermentation suite.",
      "Process":"Fermentation",
      "ResourcesRequired":
      {
        "Operator":1,
        "SafetyCabinet":1,
        "SmallFermentor":1,
        "LargeFermentor":1
      },
      "Dependencies":[4],
      "Duration":2,
      "TrainingRequirements":["4A"],
      "ValueAdding":0,
      "Priority":2,
      "Materials":
      {
        "CleaningAgent":5
      }
    },
    {
      "ID":14,
      "Name":"Autoclave Waste",
      "Description":"Autoclave fermentation materials post-decontamination.",
      "Process":"Fermentation",
      "ResourcesRequired":
      {
        "Operator":1,
        "Autoclave":1
      },
      "Dependencies":[13],
      "Duration":1,
      "TrainingRequirements":["4A"],
      "ValueAdding":0,
      "Priority":2,
      "Materials":
      {
      }
    }
  ],
  "ResourceCapacities":{
      "Operator":3,
      "SafetyCabinet":1,
      "SmallFermentor":1,
      "LargeFermentor":1,
      "PurificationTank":1,
      "Centrifuge":3,
      "SterileAssembly":1,
      "VialFiller":1,
      "Autoclave":1
  },
  "Operators":[
    {
      "Name":"AA",
      "Employment":"01/01/2000",
      "Shift":1,
      "Training":["1A","1B","2A","2B","2C","3A","3B","4A"],
      "Pay":25.5
    },
    {
      "Name":"BB",
      "Employment":"01/01/2001",
      "Shift":1,
      "Training":["1A","2A","2B","2C","3A","3B","4A"],
      "Pay":25.5
    },
    {
      "Name":"CC",
      "Employment":"01/01/2002",
      "Shift":1,
      "Training":["1A","1B","2A","2B","2C","3A","3B","4A"],
      "Pay":25.5
    },
    {
      "Name":"DD",
      "Employment":"01/01/2000",
      "Shift":2,
      "Training":["1A","1B","2A","2B","2C","3A","3B","4A"],
      "Pay":28.5
    },
    {
      "Name":"EE",
      "Employment":"01/01/2001",
      "Shift":2,
      "Training":["1A","1B","2A","2B","2C","3A","3B","4A"],
      "Pay":28.5
    },
    {
      "Name":"FF",
      "Employment":"01/01/2002",
      "Shift":2,
      "Training":["1A","1B","2A","2B","2C","3A","3B","4A"],
      "Pay":28.5
    }
  ]
}

请注意,我们批处理的每个任务都包括:

  • ID —整数值
  • 名称-字符串简短描述
  • 描述—字符串详细描述
  • 流程-字符串(步骤的三个类别之一)
  • 所需资源—所需资源和整数计数的字典
  • 依赖项-依赖的 ID 整数列表
  • 持续时间—所需小时数的整数(不必是整数)
  • 培训要求—培训字符串标识符列表
  • 增值—一个整数,1 或 0,用于标识任务是否增值(1)或不增值(0)
  • 优先级—标识任务优先级的调度顺序的整数,其中较低的数字应在具有相同相关性的较高数字之前被调度
  • 物料——任务操作期间消耗的物料和数量的字典

在调度期间,我们的资源能力保持不变,但现在我们也有我们团队中六个操作员的数据,三个在第一班,三个在第二班。请注意,我们认为一个班次是 12 个小时的跨度,从午夜到中午或从中午到午夜。还要注意的是,与同行相比,我们的一些技术人员缺少培训任务,由于轮班差异,第二班的工资更高。

我们再次使用下面的包,并如下读取我们的 JSON 数据。

import pandas as pd
import datetime
import numpy as np
import plotly.figure_factory as ff
import json
import random
import plotly.express as pxwith open('tasks.json') as json_file:
    data = json.load(json_file)

然后,我们可以将数据加载到相应的数据框表中。

Tasks = pd.DataFrame.from_dict(data['Items'])
CapacitiesDict = data['ResourceCapacities']
Operators = pd.DataFrame.from_dict(data['Operators'])

任务和操作员的相应数据框

此时,我们可以实现我们的第一个目标——实现任务优先级。

Tasks['dependancyPriority']=0
for i in range(len(Tasks['Dependencies'])):
    Tasks['dependancyPriority'][i]=Tasks['Dependencies'][i][0]
Tasks = Tasks.sort_values(by=['dependancyPriority','Priority']).drop('dependancyPriority',axis=1)

上面的代码片段是一个启发,用于确保优先级更高的任务优先于次要任务。这在优先考虑增值流程步骤而不是清洁和检查任务时非常有用,因为我们可能不希望其中一个步骤阻碍我们的流程,可以在以后有可用资源时完成它。此外,现在对这个数据帧进行排序,比在我们后面的算法中添加进一步的检查要快得多。请注意,下面的任务 12 现在在日程中的位置更高,但次于任务 5。

优先排序的任务

实际安排任务的任务从我们的第一个项目开始就没有改变。下面的代码与上一篇文章中的相同。

start_date = datetime.datetime(2021, 1, 1,0,0,0)
number_of_days = 2intervals = []
for minute in range(number_of_days*96):
    interval = (start_date + datetime.timedelta(minutes = 15*minute)).isoformat()
    intervals.append(interval)headings = list(CapacitiesDict.keys())
loading = pd.DataFrame(columns = headings)
loading['index']=intervals
loading = loading.fillna(0)loadingMax = pd.DataFrame(columns = headings)
loadingMax['index']=intervals
for item in headings:
    loadingMax[item] = CapacitiesDict[item]jobsToAdd = Tasks.copy()
jobsToAdd['TimeAddedToSchedule']=jobsToAdd['Duration']*60
jobsToAdd['Start'] = start_date
jobsToAdd['End'] = start_datefor i in range(len(loading.index)): # Go through loading schedule time by time
    print(str(round(i/len(loading.index)*100,2))+'%')
    for j in range(len(jobsToAdd.index)): # Go through list of jobs, job by job
        if jobsToAdd['TimeAddedToSchedule'][j]>0: # Continue if job needs to be scheduled still
            available = True
            for resource in list(jobsToAdd['ResourcesRequired'][j].keys()): # Continue if all required resources are available
                if loading[resource][i] + jobsToAdd['ResourcesRequired'][j][resource] > loadingMax[resource][i]:
                    available=False
            if available:
                dependenciesSatisfied = True
                if jobsToAdd['Dependencies'][j][0] == 0: #Skip checking dependencies if there are none
                    pass
                else:
                    for dependency in jobsToAdd['Dependencies'][j]: # Check if a task's dependencies have been fully scheduled
                        if jobsToAdd.loc[jobsToAdd['ID'] == dependency]['TimeAddedToSchedule'].item() > 0:
                            dependenciesSatisfied = False # Check if fully scheduled
                        if jobsToAdd.loc[jobsToAdd['ID'] == dependency]['End'].item() == datetime.datetime.strptime(loading['index'][i],'%Y-%m-%dT%H:%M:%S')+ datetime.timedelta(minutes = 15):
                            dependenciesSatisfied = False # Check that dependent end time isnt after the start of this time
                if dependenciesSatisfied:
                    if jobsToAdd['TimeAddedToSchedule'][j]==jobsToAdd['Duration'][j]*60: # Set the start time
                        jobsToAdd['Start'][j]=datetime.datetime.strptime(loading['index'][i],'%Y-%m-%dT%H:%M:%S')
                    for resource in list(jobsToAdd['ResourcesRequired'][j].keys()): # Allocate Resources
                        loading[resource][i] = loading[resource][i] + jobsToAdd['ResourcesRequired'][j][resource]
                    jobsToAdd['TimeAddedToSchedule'][j] = jobsToAdd['TimeAddedToSchedule'][j]-15 # Reduce needed time
                    if jobsToAdd['TimeAddedToSchedule'][j] == 0: # Set the end time
                        jobsToAdd['End'][j]=datetime.datetime.strptime(loading['index'][i],'%Y-%m-%dT%H:%M:%S')+ datetime.timedelta(minutes = 15)

然而,现在我们也想加载我们的操作符。我们的技术人员有特定的培训要求,对于他们来说,作为一个接受过培训的人被安排执行某项任务,培训要求必须是他们名册的一个子集。这意味着,如果任务 1 要求完成培训任务“1A ”,那么要将操作员 AA 分配到他们的花名册上,就必须包括“1A”。从 Python 技术的角度来看,我们不能将列表作为集合进行比较,而是必须将它们转换成不可变的对象,比如元组。下面的代码将我们的需求和花名册初始化为元组,并为我们的操作员初始化一个加载矩阵,以便将它们分配给任务。就像我们的设备资源负载能力分析一样,一次只能给每个技术人员分配一项任务。我们还将在 jobsToAdd 表中添加一个新列,以跟踪哪些操作员被分配了哪些任务。

Tasks['TrainingRequirementsTuple'] = tuple(Tasks['TrainingRequirements'])Operators['TrainingTuple'] = tuple(Operators['Training'])operatorAssignments = pd.DataFrame(columns = list(Operators['Name']))
operatorAssignments['index']=intervals
operatorAssignments = operatorAssignments.fillna(0)

空载操作员利用率矩阵

jobsToAdd['AssignedTechnicians'] = ""
for i in range(len(jobsToAdd['ID'])):
    jobsToAdd['AssignedTechnicians'][i] = []for j in range(len(jobsToAdd.index)): 
    # Go through list of jobs, job by job
    print(str(round(j/len(jobsToAdd.index)*100,2))+'%')
    for i in range(len(operatorAssignments.index)): 
        # Go through operator assignment schedule time by time
        if datetime.datetime.strptime(operatorAssignments['index'][i],'%Y-%m-%dT%H:%M:%S') >= jobsToAdd['Start'][j] and datetime.datetime.strptime(operatorAssignments['index'][i],'%Y-%m-%dT%H:%M:%S') < jobsToAdd['End'][j]: 
            # If loading time is inbetween start and end
            if datetime.datetime.strptime(operatorAssignments['index'][i],'%Y-%m-%dT%H:%M:%S').time() < datetime.time(12):
                # If on first shift
                Operators['temp']=0
                for k in range(len(Operators['TrainingTuple'])):    
                    Operators['temp'][k] = set(jobsToAdd['TrainingRequirements'][j]).issubset(Operators['TrainingTuple'][k])
                candidates = Operators[Operators.Shift == 1]
                candidates = candidates[Operators.temp == True]
                # Get list of trained operators for tasks on shift
                count = jobsToAdd['ResourcesRequired'][j]['Operator']
                for candidate in candidates['Name']:
                    if count > 0:
                        # Only consider assigning if needed
                        if operatorAssignments[candidate][i] == 0:
                            #Only assign if not already assigned
                            operatorAssignments[candidate][i] = jobsToAdd['ID'][j]
                            count -= 1

                            if datetime.datetime.strptime(operatorAssignments['index'][i],'%Y-%m-%dT%H:%M:%S') == jobsToAdd['Start'][j] or datetime.datetime.strptime(operatorAssignments['index'][i],'%Y-%m-%dT%H:%M:%S').time() == datetime.time(0):
                                # document the assignment in the jobsToAdd dataframe 
                                jobsToAdd['AssignedTechnicians'][j].append(candidate)
            else:
                Operators['temp']=0
                for k in range(len(Operators['TrainingTuple'])):    
                    Operators['temp'][k] = set(jobsToAdd['TrainingRequirements'][j]).issubset(Operators['TrainingTuple'][k])
                candidates = Operators[Operators.Shift == 2]
                candidates = candidates[Operators.temp == True]
                count = jobsToAdd['ResourcesRequired'][j]['Operator']
                for candidate in candidates['Name']:
                    if count > 0:
                        if operatorAssignments[candidate][i] == 0:
                            operatorAssignments[candidate][i] = jobsToAdd['ID'][j]
                            count -= 1

                            if datetime.datetime.strptime(operatorAssignments['index'][i],'%Y-%m-%dT%H:%M:%S') == jobsToAdd['Start'][j] or datetime.datetime.strptime(operatorAssignments['index'][i],'%Y-%m-%dT%H:%M:%S').time() == datetime.time(12):
                                 jobsToAdd['AssignedTechnicians'][j].append(candidate) 

现在,我们所有的操作员都被指派不超过利用率,并确保在设定的时间间隔内将一项任务分配给任何给定的技术人员。让我们也将我们的负载矩阵缩写为处理的最后时间间隔。这对于作业矩阵来说比操作符要容易得多,因为我们关注的重点是不要对作业使用操作符,而是对操作符矩阵使用任何操作符。请注意,loading 跟踪分配了多少操作员,而 operator assignments 跟踪分配给操作员的任务的 ID。分配的缩写过程通过检查行的总和来搜索空行。

loading = loading[loading.Operator != 0]operatorAssignments['temp'] = ''
nointerval = operatorAssignments.drop('index',axis=1)
nointerval['sum'] = nointerval.sum(axis=1)
for i in range(len(operatorAssignments['index'])):
    if nointerval['sum'][i] > 0:
        operatorAssignments['temp'][i] = True
    else:
        operatorAssignments['temp'][i] = False
operatorAssignments = operatorAssignments[operatorAssignments['temp']==True]
operatorAssignments = operatorAssignments.drop('temp',axis=1)
operatorAssignments

我们简化的加载矩阵

在我们的 JSON 文件中,我们看到操作员 BB 缺少培训任务“1B”。在下面,我们可以检查他们是否没有被分配相关任务,操作员 CC 是否已经取代了他们的位置。

jobsToAdd 添加分配了技术人员的数据框

现在已经加载了所有的任务,我们可以再次生成甘特图。

report = jobsToAdd.drop(['TrainingRequirementsTuple','TimeAddedToSchedule'],axis=1)x = report[['Name','Start','End','Process']].copy()
x = x.rename(columns={'Name':'Task','Process':'Resource','End':'Finish'})df = []
for r in range(len(x.index)):
    df.append(dict(Task=x['Task'][r],Start=x['Start'][r],Finish=x['Finish'][r],Resource=x['Resource'][r]))r = lambda: random.randint(0,255)
colors = ['#%02X%02X%02X' % (r(),r(),r())]for i in range(len(x.Resource.unique().tolist())):              
    colors.append('#%02X%02X%02X' % (r(),r(),r()))fig = ff.create_gantt(df, colors=colors, index_col='Resource', show_colorbar=True, group_tasks=True)
fig.show()

优先级较低的任务安排在以后的甘特图

接下来,让我们检查一下我们的资源利用率。

desc = loading.describe()
descT = desc.transpose()
descT['averageUtilization'] = descT['mean']/descT['max']
desc = descT.transpose()

利用率数据帧

我们的报告数据框架现在包含了非常有用的信息:

  • 身份证明
  • 名字
  • 描述
  • 过程
  • 所需资源
  • 属国
  • 持续时间
  • 培训要求
  • 增值
  • 优先
  • 材料
  • 开始
  • 结束
  • 指派的技术人员

现在让我们开始分析我们的刷新报告。我们可以开始寻找我们的直接人工成本,并指定任务的班次。

report['LaborCost'] = 0.0
report['TotalLaborCost'] = 0.0
totalCost=0.0
for i in range(len(report['ID'])):
    cost = 0.0
    for operator in report['AssignedTechnicians'][i]:
        pay = Operators.loc[Operators['Name'] == operator]['Pay'].item()
        cost += report['Duration'][i] * pay
    report['LaborCost'][i] = cost
    totalCost += cost
    report['TotalLaborCost'][i] = totalCostreport['Shift'] = 0
for i in range(len(report['ID'])):
    if report['Start'][i].time() < datetime.time(12):
        report['Shift'][i] = 1
    else:
        report['Shift'][i] = 2

虽然这种方法不适用于在班次之间拆分的任务,但这是计算与每项任务相关的直接人工费用的快速方法。

每项任务的直接人工费用

让我们直观地表示这些成本数据。我们可以看到发酵是我们过程中最昂贵的阶段。

#Fermentation is slightly more expensive in terms of labor than purification and the sterile boundary
fig = px.pie(report, values='LaborCost', names='Process')
fig.update_layout(title_text='Labor Costs of Batch by Process')
fig.update_traces(textinfo='percent+value')
fig.show()

按流程阶段列出的批次直接人工成本

我们还可以评估我们的劳动增值了多少。

#80% of process duration is spent on value adding time
fig = px.pie(report, values='Duration', names='ValueAdding')
fig.update_layout(title_text='Value Added Manufacturing Time in Batch')
fig.update_traces(textinfo='percent+value')
fig.show()

批量增值制造时间

我们的消耗材料需求可以来自各种来源,包括 ERP 系统,如 SAP 和物料清单(BOM)。我们还可以使用我们的报告来生成我们自己的 BOM、车间需求清单或高级采购的预定订单。

对于发酵,让我们看看在给定的批次中我们将使用什么材料。

fermentationMaterials = report[report['Process']=='Fermentation']['Materials']
print('Fermenation Material Requirements:')
for i in range(len(fermentationMaterials.values)):
    for key in fermentationMaterials.values[i].keys():
        print(key+" - "+str(fermentationMaterials.values[i][key]))out:
Fermenation Material Requirements:
SeedVial - 4
Flasks - 4
Agar - 2
GrowthMedium - 10
GrowthMedium - 50
CleaningAgent - 5

让我们把同类商品组合起来,给我们的库存经理一个简明的订单。生长培养基应该显示 60,而不是 10 和 50。

fermMaterialsTotals = {}
for i in range(len(fermentationMaterials.values)):
    for key in fermentationMaterials.values[i].keys():
        if key in fermMaterialsTotals:
            fermMaterialsTotals[key] += fermentationMaterials.values[i][key]
        else:
            fermMaterialsTotals[key] = fermentationMaterials.values[i][key]print('Fermenation Material Requirements Totals:')
for key in fermMaterialsTotals:
    print(key+" - "+str(fermMaterialsTotals[key]))out:
Fermenation Material Requirements Totals:
SeedVial - 4
Flasks - 4
Agar - 2
GrowthMedium - 60
CleaningAgent - 5

那就好多了;现在我们的库存经理知道订购 60 单位的发酵培养基。

我们如何将我们的计划作为一个简明的计划与车间分享?我们的技术人员通常主要需要关注我们对这种转变的期望。让我们通过检索报告中仅适用于技术人员和第一天第一次轮班的部分来生成一个轮班记录表。

def date(element):
    return element.date()shiftNotes = report.copy()
firstDay = datetime.date(2021, 1, 1)
shiftNotes = shiftNotes[shiftNotes['Start'].apply(date) == firstDay]
shiftNotes = shiftNotes[shiftNotes['Shift'] == 1]
shiftNotes = shiftNotes[['Process','Name','Description','Start','AssignedTechnicians']]
shiftNotes

2021–01–01 第一班的换档说明

如果某个特定的技术人员正在寻找他们这一周的工作报告,以计划他们的个人时间表和财务状况,该怎么办?

我们可以使用过滤器为特定的技术人员 AA 创建一个单独的细分,如下所示。

AASchedule = pd.DataFrame(columns = ['Process','Name','Description','Start','End','Duration','Pay'])
for i in range(len(report['AssignedTechnicians'])):
    for technician in report['AssignedTechnicians'][i]:
        if technician == 'AA':
            added = {
                        'Process': report['Process'][i], 
                        'Name': report['Name'][i], 
                        'Description': report['Description'][i],
                        'Start': report['Start'][i],
                        'End': report['End'][i],
                        'Duration': report['Duration'][i],
                        'Pay': report['Duration'][i] * Operators.loc[Operators['Name'] == 'AA']['Pay'].item()
                    }
            AASchedule = AASchedule.append(added, ignore_index = True)

操作员 AA 整个星期的工作分解

我们也可以报告他们的总收入。

AASchedule['Pay'].sum()out:
459.0

我们现在已经证明,我们可以使用 Python 跟踪平均批处理运行的几乎所有属性。我们跟踪了设备的资源分配、培训要求和操作员利用率。还有许多其他方法可以让我们区分工作的优先次序,进一步优化我们的时间表。假设我们的技术团队都是新的?我们可以优先安排一个人员充足的日程表,而不是尽快结束。我们还可以安排最大限度地减少劳动力成本,因为第二次轮班工作会产生更昂贵的轮班差异。所有这些都可以在一个更健壮的脚本中实现。然而,即便如此,这种模式也能切实改善我们在制药和其他任何行业中安排生产的方式。

请继续关注 Dash 和 Plotly 主持的交互式报告中的进一步分析和高级可视化!

请让我知道您的想法,并随时通过 LinkedIn 联系我,向我提供反馈,提出问题,或者看看我们如何将这些工具和思维方式引入您的组织!点击这里查看我的其他一些关于数据分析和生产计划的文章!

如何对 Pandas 中的列应用函数

原文:https://towardsdatascience.com/apply-function-to-columns-in-pandas-60b01c41c485?source=collection_archive---------7-----------------------

讨论在对 pandas 列应用函数时何时使用 apply()或 map(),以及如何更有效地执行

照片由Meagan car science在 Unsplash 上拍摄

介绍

当涉及到数据转换时,对 pandas 列应用特定的函数是一种非常常见的方法。在今天的简短指南中,我们将讨论如何对 pandas 数据帧中的一列或多列应用预定义函数或 lambda 函数。

此外,我们将讨论如何实现最佳性能,以及为什么在对多个列应用一个方法时使用apply() 方法更好,而在只处理一个列时使用map()方法更好。

首先,让我们创建一个示例数据框架,我们将在本文中引用它来演示一些概念。

import pandas as pddf = pd.DataFrame({
    'colA': [1, 2, 3, 4, 5],
    'colB': [True, False, None, False, True],
    'colC': ['a', 'b', 'c', 'd', 'e'],
    'colD': [1.0, 2.0, 3.0, 4.0, 5.0]
})print(df)
   *colA   colB colC  colD
0     1   True    a   1.0
1     2  False    b   2.0
2     3   None    c   3.0
3     4  False    d   4.0
4     5   True    e   5.0*

此外,让我们假设我们想给列colA下的每个值加 1。这可以使用 lambda 表达式轻松完成

lambda x: x + 1

对单个列应用函数

如果您想只对一列应用函数,那么map()是最好的选择。

**df['colA'] = df['colA'].map(****lambda x: x + 1****)**print(df)
***colA*** *colB colC  colD
0* ***2*** *True    a   1.0
1* ***3*** *False    b   2.0
2* ***4*** *None    c   3.0
3* ***5*** *False    d   4.0
4* ***6*** *True    e   5.0*

您也可以使用apply()方法,但是在对单个列应用方法时,**map()** 更有效。 [**pandas.Series.map(**](https://pandas.pydata.org/docs/reference/api/pandas.Series.map.html)**)** 方法对 **Series** (即数据帧的单列)进行操作,一次操作一个单元格。另一方面,pandas.DataFrame.apply()一次对整行进行操作。因此,使用map()会更有效,因为您只需要访问特定列的值(而不是整行的值)。

将函数应用于多列

另一方面,在需要对多个列应用某个函数的场合,那么您可能应该使用[pandas.DataFrame.apply()](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)方法。例如,假设我们需要对列colAcolD应用 lambda 函数lambda x: x + 1。以下内容应该可以解决问题:

**df[['colA', 'colD']] = df[['colA', 'colD']].apply(****lambda x: x + 1****)**print(df)
***colA*** *colB colC* ***colD*** *0* ***2*** *True    a* ***2.0*** *1* ***3*** *False    b* ***3.0*** *2* ***4*** *None    c* ***4.0*** *3* ***5*** *False    d* ***5.0*** *4* ***6*** *True    e* ***6.0***

在这种情况下,map()方法不能使用,因为它应该只在Series对象上操作(即在单个列上)。apply()方法将对整行应用指定的 lambda 函数,在本例中,整行由列colAcolD中的值组成。

最后的想法

在今天的简短指南中,我们讨论了如何在 pandas 中对列应用特定的函数。此外,我们探讨了为什么在将一个方法应用于单个列时使用map()方法很重要,而apply()应该只在处理多个列时使用。

成为会员 阅读介质上的每一个故事。你的会员费直接支持我和你看的其他作家。

你可能也会喜欢

工具变量法在因果分析中的应用

原文:https://towardsdatascience.com/apply-instrumental-variables-method-in-causal-analysis-9fd55e39da7a?source=collection_archive---------27-----------------------

重点放在通过一个例子说明的实验上

在 Unsplash 上由 Aniket Deole 拍摄

动机

2021 年诺贝尔经济学奖授予约书亚·d·安格里斯特和吉多·w·因本斯,以表彰他们对因果关系分析的方法论贡献。。如果你想知道“方法论的贡献”具体是什么,并愿意更深入地挖掘科学背景,你会注意到“工具变量方法”被称为“因果关系研究的新框架”。受到这个令人兴奋的消息的启发,我想写一篇关于工具变量(IV)方法的文章,尽管我想强调 IV 框架非常复杂,不可能在一篇中型文章中涵盖所有内容。利用观察数据回答因果问题是一种非常重要的方法,但在本文中,我们将介绍这种方法在实验中的应用。该应用遵循安格里斯特(2006 年)的论文“实验犯罪学研究中的工具变量方法:什么、为什么和如何”。我用 R 语言做了复制,你可以看看底部的程序。

介绍

在我自己研究因果推理的过程中,我注意到主要有两种方法来引入工具变量:1)一种是由内生性问题引起的(维基百科:在计量经济学中,内生性广义上是指解释变量与误差项相关的情况),2)另一种是与实验中的问题相关的,当时治疗分配是随机的,但治疗提供不是随机的。在这两种情况下,工具变量框架都提供了解决方案。

在本文中,我们不会深入探讨内生性问题,因为它相当复杂,基本上是一个独立的话题。相反,我们将根据安格里斯特(2006)研究明尼阿波利斯家庭暴力实验(MDVE)的论文,重点关注 IV 方法在实验中的应用。

实验中的工具变量(四)

假设我们要分析一个随机实验,凭直觉,仪器变量可以理解为一个因果链,其中一个仪器 Z 改变实际提供的治疗 D,反过来影响结果 y,Z 必须与 D 密切相关,并且是随机分配的或“与随机分配一样好”。最后,Z 对 Y 的因果作用只通过 D 实现(是单通道)。更正式地说,我们定义:

  • 仪器:Z i
  • 治疗变量:D i
  • 结果变量:Y i

IV 因果链中的链接基于潜在结果框架中的条件预期。如果你不熟悉潜在结果模型,你可以看看我以前的文章:

https://medium.com/geekculture/an-introduction-of-randomized-experiment-aka-a-b-testing-and-potential-outcome-model-f2c93f73d426

第一阶段是从仪器 Z i 到感兴趣的因果变量(治疗变量 D i )的链接:

简化形式(也称为意向处理效应)是工具 Z i 对结果 Y i 的影响:

称为局部平均治疗效应(晚期)的因果效应由简化形式与第一阶段估计值的比率确定:

上面的方程是用总体平均值写的。在实践中,我们使用来自我们的实验数据的样本均值来估计那些。我知道在这一点上,你可能对 Z、D 和 Y 的实际含义有点困惑,因果联系是如何工作的。接下来,我们将通过一个真实的实验来应用这些变量和方程,以了解 IV 是如何工作的。

应用程序(MDVE)

在家庭暴力受害者救助项目中,参与实验的明尼阿波利斯警察局的巡逻警官被要求在警官有可能有理由相信发生了攻击的情况下,使用三种方法(在回应时随机选择)中的一种来处理家庭暴力电话:

  1. 分离:将施虐者送走八小时。
  2. 建议:调解纠纷。
  3. 逮捕。

随机化是通过一叠报告表进行的,这些报告表对三种可能的反应中的每一种都进行了随机颜色编码。官员们被要求根据便笺簿上表格的颜色采取行动。

数据

我从哈佛数据节
下载了数据和 SAS 程序,Joshua D. Angrist,2009,“复制数据:实验犯罪学研究中的工具变量方法:什么、为什么和如何”,https://doi.org/10.7910/DVN/FTZ8GN,哈佛数据节,V1

请注意,数据集不包含作为累犯率的结果变量。为了构建它,我运行了提供的 SAS 程序。然后我把数据加载到 R 中,继续进行因果分析。

那么,MDVE 中的 Z 和 D 是什么呢?

我们将 Z 定义为处理的分配,它是每个官员在实验中使用的便笺簿上随机分配的表格颜色。在分析中,我们还将建议分组并分离到同一个称为“溺爱”的桶中。当案例被指定为待处理时,Z=1,否则为 1。

实验中的官员是自愿参与的,因此应该遵守研究设计。然而,不难想象严格遵守随机化协议是不现实的。在实践中,官员们经常偏离在事件发生时根据报告表格的颜色所指定的反应。例如,在某些情况下,当随机分配是离职或建议时,官员决定进行逮捕。在现场实验中,部分和不完全的依从性并不少见。

因此,在每种情况下,我们定义为 D 的实际治疗与指定为 Z 的治疗并不相同。在分析中,我们表示 D =1 时,案件实际上是溺爱,0 时,逮捕了。

下面我复制了表 1,即安格里斯特(2006)论文中 Z 和 D 的列表。它表明,在被指派逮捕的 92 人中,91 人实际上被逮捕(服从),只有 1 人没有被逮捕。然而,在被指派提供咨询的 108 人中,有 19 人实际上被逮捕(未遵守)。建议治疗的依从率为 78% (84/108)。同样,在被随机分配隔离的 114 名嫌疑人中,26 人被逮捕,5 人被劝告。分离治疗的依从率为 73% (83/114)。简而言之,我们看到几乎每一个被指派逮捕的案件都导致了逮捕,但是许多被指派建议或分开的案件也导致了逮捕,这是 MDVE 中最常见的偏差。

表 1:指定和提供的治疗

IV 作为不完全遵守的解决方案

依从性不完善的问题是否会让您认为这个实验是失败的,因为随机分配的治疗实际上并没有进行?幸运的是我们可以用静脉注射的方法来修复它!

通过因果链应用 IV 方法,从指定的治疗开始,通过实施的治疗,最终到结果变量,我们可以估计晚期,治疗对依从者的平均因果影响。我们不知道对总是接受者的影响(例如,无论有没有实验,案例都会被娇惯,而与此相反的是,因为我们指定他们被娇惯,所以他们才会被娇惯)。我们无法得知这种仪器对那些治疗状况不受影响的人的影响。

有趣的是,在 MDVE 中,我们有单方面的依从性:随机分配到治疗组的一些人没有接受治疗,而随机分配到对照组的人没有接受治疗(记住,我们在 92 个被分配逮捕的病例中只有 1 个实际上被溺爱)。在 MDVE 中(几乎)没有总是接受者,(几乎)所有被治疗者都是从犯,因此对从犯的平均因果影响也捕捉到了对被治疗者的平均因果影响。

使用 2SLS 回归实施 IV

我们通常使用两阶段最小二乘法(2SLS)作为广义 IV 估计量。我不会在这里详细说明为什么 IV 是 2sl,但是如果你感兴趣的话,我建议你深入研究它,这将把内生性问题带回到图片中,你可能会发现它使一切变得更清楚(或者不清楚)。

下面是我用来在 MDVE 中实现 IV 的 R 代码:

在第一次回归中,我根据。提供治疗。由于治疗的非随机性,这是一种误导。在这种方法中,由于负选择偏倚,估计值 0.087 要小得多。下面等式中的第二项是选择偏差。

从第一阶段回归来看,系数= 0.786 就是分配到被溺爱时被溺爱的顺从率((84+5+5+83)/(108+114))。

第三个回归估计了意向治疗(IIT)效应,它捕捉了被分配到治疗的因果效应,并考虑到一些被分配的治疗没有被提供的事实。由于不完全依从性引起的治疗错觉,估计值 0.786 小于 IV 估计值。

IV 回归给出了对被溺爱的因果效应的最新估计,不受不顺从的影响,也不受选择偏差的影响。估计效果是 0.145。

在最后一次回归中,我用两个内生变量和两个工具变量实现了 IV/2SLS。

匹配法在因果分析中的应用

原文:https://towardsdatascience.com/apply-matching-method-in-causal-analysis-19e032144e93?source=collection_archive---------27-----------------------

用 Python 写了一个真实的例子

扬·瓦莱卡在 Unsplash 上拍摄的照片

动机

随机实验是检验因果关系的金标准。然而,由于各种各样的实际原因,我们常常不能进行实验,而必须依靠观察数据。匹配是一种用于近似实验结果以从观察数据中恢复因果效应的方法。

当使用观察数据估计因果效应时,通过获得具有相似协变量分布的治疗组和对照组,尽可能接近地重复随机实验是可取的。

—英国斯图尔特,2010 年。"因果推理的匹配方法:回顾与展望."统计。Sci。

条件独立性假设

假设我们有两个潜在的结果和一个治疗变量 Dᵢ :

我们把治疗效果写成:

随机实验解决了选择问题,因为随机分配消除了选择偏差。关键的假设是 Dᵢ 独立于潜在的结果( y₁ᵢ,y₀ᵢ ):

如果你需要一个潜在结果模型的回顾,你可以阅读下面链接的我以前的文章:

https://medium.com/geekculture/an-introduction-of-randomized-experiment-aka-a-b-testing-and-potential-outcome-model-f2c93f73d426

中情局称,根据一组观察到的特征 X ,选择偏差被消除:

考虑到 CIA,以协变量 X *ᵢ为条件,*治疗组和对照组的结果变量的比较有一个因果解释:

在另一种格式中:

应用 CIA 的一个例子是田纳西之星实验,在这个实验中,学生被分配到不同的班级。

CIA 的另一个经验应用是匹配估计:在治疗组和对照组之间比较具有相同协变量(以协变量为条件)的单位。以某种方式计算并平均这些差异。

例子

为了演示匹配是如何工作的,我使用了 Arceneaux,Kevin,Alan S. Gerber 和 Donald P. Green (2010)的论文“使用大规模选民动员实验比较实验和匹配方法”的复制材料中的数据和方法。ISPS 数据档案。您可以通过该链接下载数据。dta 或者。csv 格式和 Stata 代码。

在本文中,我按照附录中阐述的精确匹配过程,用 Python 再现了表 6 中的 OLS 和精确匹配结果。

数据

Arceneaux、Kevin、Alan S. Gerber 和 Donald P. Green (2010)使用的数据来自一项大规模现场实验,在 2002 年中期选举之前,将个人随机分配到爱荷华州和密歇根州的治疗组和对照组。

处理方法是:动员投票(GOTV)电话(数据中的变量“联系”)。治疗组接到了电话,而对照组没有。结果变量是 2002 年的投票率。匹配分析中使用的协变量包括:、【年龄】、【人员】、【新记录】、【竞争对手】、【投票 00】、【投票 98】、【县】、【st_sen】、【st_hse】、【女性】、【州】、,这些变量包括年龄、家庭规模、新登记选民、竞争阶层、2000 年投票、1998 年投票、县、州参议院选区、州众议院选区、性别和州。

匹配分析

分析的目的是估计 2002 年 GOTV 投票电话对投票率的因果关系。我们假设实验数据是观察性的,并将治疗组的个体与对照组的个体进行比较。匹配程序识别未处理的个体,与每个具有相同特征的处理过的个体相匹配。根据 CIA,希望在协变量匹配后,组间的任何剩余差异可以归因于治疗的效果。

我进行了精确的匹配程序,其中每个治疗受试者与协变量上的一个或多个未治疗受试者匹配,并丢弃未找到匹配的治疗观察。

如本文附录所述,为了计算受治疗者的平均治疗效果,使用 Abadie 和 Imbens (2004 年)提出的方法计算了与每个治疗观察相匹配的对照组观察的权重。我总结了治疗观察结果和比较观察加权结果之间的差异,然后对治疗组的许多观察结果进行平均。我还根据 Abadie 和 Imbens (2004)提出的算法计算了标准误差。

此外,按照论文中的表 6,我运行了 OLS 回归

精确匹配产生 2.8 个百分点的估计治疗效果。这意味着竞选电话每 36 个电话就增加一个选民。OLS 结果的处理效果是接触变量的系数,等于 2.7,标准误差为 0.3。

以下是我用 Python 编写的用于匹配分析的完整代码(摘自 Arceneaux、Kevin、Alan S. Gerber 和 Donald P. Green (2010)使用的 Stata 代码) :

倾向得分方法在因果推理中的应用——第二部分:K 近邻匹配

原文:https://towardsdatascience.com/apply-propensity-score-method-in-causal-inference-part-2-k-nearest-neighbor-matching-c0cd2cdb0f74?source=collection_archive---------15-----------------------

用 Python 写了一个真实的例子

照片由威利安·贾斯登·德·瓦斯康塞洛斯在 Unsplash

在我的上一篇文章中,我介绍了如何使用 logistic 回归模型估计倾向得分,并逐步进行分层匹配。综上所述,在实践中,倾向评分法通常分两步完成。首先,我们估计倾向得分。第二,我们通过使用一种匹配方法来估计治疗的效果。

在本文中,我假设我们已经使用相同的数据并按照我上一篇文章中列出的程序(链接如上)获得了治疗组和对照组的估计倾向得分。

在我深入最近邻匹配之前,下面是倾向评分估计的 Python 代码。最后,我保存了带有估计分数的数据集,以便稍后进行匹配:

k 近邻匹配

最近邻匹配是分层的替代方法,用于匹配处理单元和比较单元。它获取每个处理过的单元,并搜索具有最接近 p 值的比较单元。匹配后,我们计算每个治疗组和对照组之间的结果差异,并对多个治疗组的这些差异进行平均。

有不同类型的最近邻匹配。在本文中,我考虑 k 从 1 到 5 的范围内的 k 最近邻匹配。我使用了实现 k-最近邻算法的[**sklearn.neighbors**](https://scikit-learn.org/stable/modules/classes.html#module-sklearn.neighbors)模块。在 k 个对照单位与每个治疗单位匹配后,我计算所有变量平均值的加权差异,以 1)估计治疗效果(结果变量 re78 ),以及 2)比较治疗组和对照组之间的协变量平衡。k=1,3,5 的结果总结在下表 1 中。

表 1:不同 Ks 的 KNN 倾向评分匹配结果

假设我们已经获得了实验基准效应= $1,676,分层匹配的治疗效应= $1,632(在上面链接的前一篇文章中),当我们将它们与实验效应进行比较时,KNN 匹配似乎产生了比分层更小且更差的效应。可能的解释包括:1)即使 knn 匹配保证所有处理过的单元都找到匹配。然而,这些匹配中的一些可能相当差,因为对于一些处理过的单元,匹配的比较单元(最近邻)可能具有非常不同的 p 分数,同样 2)即使分数匹配得很好,匹配的处理单元和比较单元在我的模型中似乎并不总是很好的匹配。例如,下面是来自 k=1 NM 匹配的一个例子,其显示了处理和比较单元具有几乎相等的 p-分数,但是在其他特征上非常不同。我的观点是,倾向得分模型还不够好,如果可能的话,我应该包含更多的特征。

以下是我用于 k 近邻匹配的 Python 代码:

倾向得分方法在因果推理中的应用——第一部分:分层

原文:https://towardsdatascience.com/apply-propensity-score-methods-in-causal-inference-part-1-stratification-afce2e85730c?source=collection_archive---------16-----------------------

用 Python 一步步实现了一个例子

蒂姆·斯蒂夫在 Unsplash 上的照片

本文介绍并实现了 Dehejia 和 Wahba (1999)“非实验性研究中的因果效应:重新评估培训计划的评估”,美国统计协会杂志,第 94 卷,№448(1999 年 12 月),第 1053–1062 页。我将简要回顾一下这些理论,然后一步一步地介绍我是如何实现分层匹配的。文章末尾提供了完整的 Python 代码。

直觉

倾向得分方法的直觉是:我们尝试以 X ᵢ.估计的倾向得分为条件,而不是以协变量 X 的完整向量为条件,这在有许多治疗前变量且治疗组和对照组非常不同时会变得困难

如果您需要回顾协变量匹配,我之前的文章介绍了条件独立假设(CIA)下的协变量精确匹配:

倾向得分定理

将倾向得分 p(X ᵢ】定义为

我们有如下:假设中央情报局认为

那么这个成立:

**倾向得分匹配与协变量匹配的工作方式相同,只是我们根据得分而不是直接根据协变量进行匹配。**由倾向得分定理可知:

应用

在实践中,估算通常分两步完成。首先,我们估计倾向得分。第二,我们通过使用一种匹配方法来估计治疗的效果。

Dehejia 和 Wahba (1999 年)使用的数据来自国家支持的工作(NSW)项目,该项目旨在为长期存在“就业问题”的人提供过渡性补贴工作经验方案。合格的申请人被随机分配接受培训(治疗组)或不接受培训(对照组)。评估的目的是估计培训对收入的影响。

以下步骤中所示的分析组织如下:首先,由于该项目是作为 RCT 进行的,因此原始数据(NSW)是实验数据,用于估计作为基准的处理效果。接下来,我们使用额外的政府调查数据(CPS 和 PSID)来模拟观察数据。然后,我们估计和应用倾向得分和分层方法来估计提供培训对收入的影响使用观察数据。

1.实验基准

如果随机化处理得当,我们预计对照组和治疗组之间的预处理特征应该没有系统性差异。否则,我们可能会怀疑它们在未观察到的特征上也有所不同,这可能会导致对治疗的因果效应的有偏见的估计。

因此,检查两组之间的预处理变量是否平衡总是一个好主意。为了测试治疗前变量的协变量平衡(re78 是实现的结果变量,所有其他变量在分配到治疗组或对照组之前是预先确定的),我运行了 8 个单独的简单线性回归,并将结果报告在表 1 中。它表明对照组和治疗组的样本平均值没有差异。

表 1:使用实验数据测试对照组和治疗组之间的协变量平衡

此外,我使用实验数据来估计平均治疗效果(ate ),作为提供在职培训对培训后收入(re87)的因果影响。控制协变量后,我得到了 1676 美元的效果。我会以此为基准牢记在心。

表 2:使用实验数据估计的 ATE(平均治疗效果)

2.观测资料

Dehejia 和 Wahba (1999)使用两个调查数据样本(CPS 和 PSID 调查)作为与新南威尔士州治疗组的对照组,以构建观察数据库(CPS 代表当前人口调查,PSID 代表收入动态的小组研究)。

同样,我比较了新南威尔士州的样本均值(实验数据)和增加的比较样本(CPS 和 PSID)。在表 3 中,很明显,新南威尔士州治疗组与 CPS/PSID 组在人口统计学特征以及治疗前收入(re74 和 re75)方面有很大不同。新南威尔士州的治疗组更年轻,受教育程度更低,更有可能是非白人,并且在项目开始前收入更低。直接比较新南威尔士州治疗组和对照组是没有意义的。

表 3:新南威尔士州(实验)和对比样本的样本均值比较

3.倾向得分估计

从这一步开始,我用 PSID 作为对照组,用新南威尔士州处理的构建观察数据库。

我使用逻辑回归模型来估计倾向得分。具体来说,我研究了两个规格回归两组不同的协变量(在第二组中,我添加了年龄和教育年限的平方):

X1 = [‘age’, ‘ed’, ‘black’, ‘hisp’, ‘married’, ‘nodeg’, ‘re74’,’re75']X2 = [‘age’, ‘ed’, ‘black’, ‘hisp’, ‘married’, ‘nodeg’, ‘re74’,’re75',’age2',’ed2']

接下来,我使用交叉验证来调整参数 C,这是正则化的强度。C 值越高,意味着正则化程度越低。

best = 0.0
for strength in [0.00001,0.0001,0.001,0.01,0.1, 1, 10, 100, 1000, 10000, 100000, 1000000] :
 model = LogisticRegression(C=strength, max_iter=10000)
 curScore = cross_val_score(model,df_combine[X1], df_combine[T],scoring=’f1').mean()
 if curScore > best :
 best = curScore
 print(strength)
 print (curScore)

接下来,我运行具有最佳 C 值的逻辑回归模型,并将估计的倾向得分添加回观察集。下面,我绘制了比较组和治疗组的估计 p 值。不足为奇的是,这两个群体之间没有太多的重叠。治疗组(n=185)的平均 p 值为 0.62,而对照组(n=2490)的平均 p 值为 0.03。事实上,正如 Dehejia 和 Wahba (1999)所指出的,倾向得分法的优势之一是它戏剧性地强调了一个事实,即大多数比较单位与处理单位非常不同。

4.整理

治疗组和对照组之间的巨大差异自然导致了修整和匹配的步骤,在我的 Python 代码中,这些步骤是在同一个函数分层 _ 函数中完成的。微调的目的是找到共同的支持,使治疗组或对照组的 p 值都不会超出支持范围。调整后,我们从 PSID 对照组中剔除了大约 1300 个超出共同支持范围的观察结果,其中大部分的估计倾向得分低于治疗单位的最小估计倾向得分。调整的原因是为了防止当我们进入分层/匹配阶段时,我们最终得到处理过的单元而没有匹配的控制单元,反之亦然。

5.分层匹配

分层的基本思想是根据估计的倾向分数来定义层,然后将治疗和比较单位分组到每个层中。在每一层中,我们取治疗组和对照组之间的结果的平均值的差异,然后我们通过每一层中治疗单位的数量来计算跨层的加权平均值。治疗组和对照组的最终加权平均差异(ATT)计算如下:

在我的代码中,这是在函数*的分层 _ 函数中完成的。*我还测试了两种定义阶层的方法:

blocks1 = np.array_split(df_ps.query(“treat==1”)[“pscore”].sort_values(), nostrata)blocks2 = np.linspace(0,1,11)

假设我们需要 10 层。第一种方式将处理单元的估计倾向分数分成 10 个相同大小的块,然后将比较单元与每个块范围内的估计倾向分数进行匹配。第二种方法简单地把 1 分成 10 个等间距的区间。

记得在第三步倾向得分估计中,我测试了两个逻辑回归模型。将两个逻辑回归模型与两个 strata 定义相结合,我在输出端有四个模型,我将其命名为:M11 (reg1,strata1)、M12(reg1,strata2)、M21(reg2,strata1)和 M22(reg2,strata2)。结果如表 4 所示:

表 4:不同模型选项的加权平均差异

在这四个选项中,最终模型的选择应基于每个阶层内的协变量在处理单元和比较单元之间的平衡程度。所以我觉得 Model21 最好。 Model21 估计效果= 1632 美元(含标准差。误差=1,514),这非常接近我们的实验基准效果= 1,676 美元。

最后,正如我所承诺的,下面是我的第 1 步到第 5 步的完整 Python 代码,它也输出了汇总表 1-4:

我从 NYU 的 网站 下载了所有数据

熊猫中的 apply() vs map() vs applymap()

原文:https://towardsdatascience.com/apply-vs-map-vs-applymap-pandas-529acdf6d744?source=collection_archive---------10-----------------------

讨论 Python 和 Pandas 中 apply()、map()和 applymap()的区别

木丹在 Unsplash 上拍照

介绍

通常,我们需要对 DataFrame 的列或行应用某些函数,以便更新值或者甚至创建新的列。熊猫最常用的手术是applymapapplymap方法。

在今天的指南中,我们将探索所有三种方法,并了解它们各自的工作原理。此外,我们将讨论何时使用其中一个。

首先,让我们创建一个示例数据框架,我们将在本文中使用它来演示一些概念,这些概念将有助于我们强调apply()map()applymap()之间的区别。

import pandas as pd df = pd.DataFrame(
    [
        (1, 521, True, 10.1, 'Hello'),
        (2, 723, False, 54.2, 'Hey'),
        (3, 123, False, 33.2, 'Howdy'),
        (4, 641, True, 48.6, 'Hi'),
        (5, 467, False, 98.1, 'Hey'),
    ],
    columns=['colA', 'colB', 'colC', 'colD', 'colE']
)print(df)
 *colA  colB   colC  colD   colE
0     1   521   True  10.1  Hello
1     2   723  False  54.2    Hey
2     3   123  False  33.2  Howdy
3     4   641   True  48.6     Hi
4     5   467  False  98.1    Hey*

apply()方法

[pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)方法用于沿着 pandas 数据帧的指定轴应用一个函数。apply()方法一次对整行或整列进行操作,最适用于应用 函数而**无法矢量化的情况。**注意方法的输入必须是一个可调用的

import numpy as np**df[['colA', 'colB']] = df[['colA', 'colB']].apply(np.sqrt)**print(df)
 *colA       colB   colC  colD   colE
0  1.000000  22.825424   True  10.1  Hello
1  1.414214  26.888659  False  54.2    Hey
2  1.732051  11.090537  False  33.2  Howdy
3  2.000000  25.317978   True  48.6     Hi
4  2.236068  21.610183  False  98.1    Hey*

此外,该方法也适用于熊猫系列(见[pandas.Series.apply](https://pandas.pydata.org/docs/reference/api/pandas.Series.apply.html))。

map()方法

[pandas.Series.map](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.Series.map.html)方法只能应用于 pandas 系列对象,用于根据输入映射系列的值,该输入用于将每个值替换为从字典、函数甚至另一个Series对象中导出的指定值。

注意,该方法一次对一个元素进行操作,丢失的值将在输出中表示为NaN

**df['colE'] = df['colE'].map({'Hello': 'Good Bye', 'Hey': 'Bye'})**print(df)
 *colA  colB   colC  colD      colE
0     1   521   True  10.1  Good Bye
1     2   723  False  54.2       Bye
2     3   123  False  33.2       NaN
3     4   641   True  48.6       NaN
4     5   467  False  98.1       Bye*

applymap()方法

最后,[pandas.DataFrame.applymap](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.applymap.html)方法只能应用于 pandas DataFrame 对象,并用于在元素方面应用指定的函数**。该方法只接受调用,最适合转换多行或多列中的值。**

**df[['colA', 'colD']] = df[['colA', 'colD']].applymap(lambda x: x**2)**print(df)
 *colA  colB   colC     colD   colE
0     1   521   True   102.01  Hello
1     4   723  False  2937.64    Hey
2     9   123  False  1102.24  Howdy
3    16   641   True  2361.96     Hi
4    25   467  False  9623.61    Hey*

最后的想法

在今天的简短指南中,我们讨论了apply()map()applymap()方法在熊猫身上的作用。此外,我们展示了如何使用这些方法,并探讨了它们的主要区别。

有时,可能不清楚您是否需要使用apply()applymap()方法,因此,最好的方法是简单地测试两种方法,然后选择更有效/更快的方法。

成为会员 阅读介质上的每一个故事。你的会员费直接支持我和你看的其他作家。你也可以在媒体上看到所有的故事。

**https://gmyrianthous.medium.com/membership **

你可能也会喜欢

** ** ** ** ** **

将聚类算法应用于特征贡献

原文:https://towardsdatascience.com/applying-a-clustering-algorithm-to-feature-contribution-3c649ab0ca17?source=collection_archive---------13-----------------------

以整体的方式培养可解释性

kjpargeter 制作的背景照片—www.freepik.com(https://www.freepik.com/photos/background)

介绍

如果你已经深入研究了 xgboost 和 lightgbm 等集成算法的机器学习可解释性,你可能听说过预测贡献,甚至可能使用过 SHAP 包,它允许我们更好地理解训练过的模型。本文假设您以前使用过 Shapley values,因为它没有提供该主题的详细解释。

如果您以前没有使用过预测贡献,我们就假设这些值总结了每个特定特征如何对每个特定观察值的预测做出贡献。为了理解这些值,我们可以将它们全部相加(每次观察),加上偏差(期望值),并应用 logit 函数,从而将它们转换为实际预测概率。因此,如果您正在预测某个事件的发生(二元分类,其中 1 表示事件已发生),则来自某个要素的正贡献意味着它在向模型发出信号,以预测该特定观察事件发生的概率较高。如果你感兴趣,我邀请你阅读更多关于这个主题的内容,从 Christoph Molnar 的这个有趣的 GitHub 页面开始:

https://christophm.github.io/interpretable-ml-book/shapley.html

挑战

虽然通过使用预测贡献,我们可以看到(平均而言)特定特征如何影响总体的预测,但我们需要记住,特征不是单独起作用的。特别是当我们使用像 xgboost 和 lightgbm 这样的集合算法时,某个特征对观测值预测的贡献很大程度上取决于其他特征对同一观测值的贡献。因此,一次绘制多个特性的贡献通常很有帮助(以便确定它们之间的交互趋势)。在本文中,我想更进一步,提出对预测贡献数据使用 KMeans 等聚类算法,目的是提供一种更全面的方法来解释树集成机器学习算法决策。

预期

将预测贡献数据提供给 KMean 聚类分析算法将产生一组已识别的组,这些组描述了组内相似性,即所有功能如何协同工作,以向模型发出信号,表明它应该如何预测该组中的观察值。请务必记住,这些聚类不会直接与特征值相关联,而是与这些特征值的贡献相关联。因为 KMeans 是一种无监督的学习算法(它不是预测事件的发生,而是主要识别数据中的趋势),不同的组或聚类将具有不同的完成目标的观察百分比。一个有用的练习是查看目标值和预测值之间的每组平均差异。从这个角度来看预测有助于识别模型更难预测的观察值。

练习:

让我们应用上面讨论的所有内容。

首先:我们将检索我们的数据,转换它,并创建一个基本模型来使用。

完成此操作后,我们应该有一个大致如下所示的表:

我们将把这个数据集输入 KMeans 算法,以便在我们的预测贡献中识别趋势或聚类。

使用预测贡献的优势

  1. **标度:**所有预测贡献都是相同的标度,我们不需要在将数据提供给 KMeans 之前对其进行预处理或转换。
  2. **空值:**当一个特性值可能为空时,一个特性贡献值不会为空。因此,我们可以安全地使用 KMeans,而不必丢弃观察值或输入空数据。

让我们开始我们练习的第二步:

我们得到的数据集应该是这样的:

为了开始理解我们的数据集,我认为查看每个聚类中心的信息是公平的。

按照前面的代码,我们将得到一个类似于下面的表:

很明显,定义每个聚类的特征实际上是 RM、AGE、PTRATIO 和 LSTAT。但是,我们要记住,这里值聚类的中心是基于特性贡献的,而不是基于特性值的。让我们用 groupby 创建一个不同的表,它将允许我们查看每个集群的特征值以及每个集群中完成目标的观察值的百分比(以及它们对目标的预测)。

如果我们根据每个分类对训练数据进行分组,我们可以根据模型的目标值和预测值之间的现有差异来查看模型更容易预测哪些分类。很明显,聚类 18 包含了我们的模型最难预测的人群。

结论

在确定模型可解释性的整体趋势时,我们可以研究聚类算法以获得洞察力。聚类算法将帮助我们创建观察组,这些观察组在它们的特征值如何影响它们的预测方面彼此相似。通过查看每组的目标和预测,我们可以开始更好地了解模型发现哪些类型的观察值更容易或更难预测。感谢您的阅读。我希望这篇文章在某种程度上对你有所启发。

https://www.linkedin.com/in/anapreciado/

贝叶斯网络在新冠肺炎诊断中的应用

原文:https://towardsdatascience.com/applying-bayesian-networks-to-covid-19-diagnosis-73b06c3418d8?source=collection_archive---------20-----------------------

实践教程

高度复杂情况下的概率决策

由马塞洛·里尔在 Unsplash 上拍摄的照片

动机

新冠肺炎·疫情使我们社会中长期以来认为理所当然的部分承受了压力,这已经不是什么新闻了。医疗保健服务可能是最明显的例子之一。我们的医疗保健专业人员面对一种他们知之甚少的病毒,不得不经常在信息稀缺和资源有限的情况下做出决策。在这种背景下,数据和人工智能社区已经介入,通过利用他们最擅长的东西来为抗击新冠肺炎做出贡献:处理数据以做出明智的决策。这已经转化为呼吁代码、 Kaggle 挑战,甚至非营利企业联盟。

受到这种集体努力的启发,在这些文章中,我们展示了贝叶斯网络如何有助于决策者使用概率方法从复杂的信息中获得意义。特别是,在简要介绍了贝叶斯网络的概念之后,我们展示了如何将这一概念应用于来自新冠肺炎的 Kaggle challenge 诊断及其临床谱 *的真实世界数据集。*该数据集包含来自患者的匿名医疗数据,这些患者在巴西圣保罗的 Israelita Albert Einstein 医院接受了新冠肺炎病毒检测,该医院是该挑战的赞助商。

用组织者自己的话来说,挑战的动机是*在一个不堪重负的卫生系统的背景下,进行新型冠状病毒检测的测试可能会受到限制,测试每个病例将是不切实际的,即使只测试目标亚群,测试结果也可能会延迟。*贝叶斯网络提供的结构化知识非常适合这种情况。正如我们在这篇博客中所展示的,有了贝叶斯网络,复杂的决策变得更加容易。

贝叶斯网络简介

贝叶斯网络(BN)是一种在人工智能社区中处理不确定性的成熟技术,以至于一些人认为它们是现代人工智能的顶点。正如 Stuart Russell 教授所说的,“BN 对于人工智能和人工智能的重要性不亚于布尔电路对于计算机科学的重要性。”虽然人们可能会争论这句话的范围,但很明显,bn 在人工智能社区中相当受欢迎。但是,BNs 到底是什么?

BN 是一类概率图形模型,以其在不确定性下的推理能力而闻名。它们可以被视为概率专家系统:领域(业务)知识被建模为有向无环图(DAG)。DAG 链接或弧表示域中节点/变量之间的概率依赖性。这些依赖不一定是因果关系,但可以表示一种形式的相关性或依赖性。更正式地说,BN 是一组随机变量的联合概率分布(JPD)。它由 DAG 表示,其中节点用随机变量标识,弧线表示它们之间的概率相关性。

BNs 已经在不同行业成功实施。它们的许多应用包括风险评估(癌症、水、核安全…)、工业过程模拟、机器健康监控(故障诊断、缺陷、故障等)..)和预测性维护。

更一般地,BNs 可以用作诊断、预测(也称为预后)或对观察结果进行可能解释的工具。在诊断应用程序中,我们可以根据图表(通常是 BN 中弧线的相反方向)从结果到原因进行推理。例如,在我们的数据集中,关于血细胞计数的观察可以用来更新专家关于需要入住 ICU 的信念。一旦我们有了 BN,我们也可以在不确定的情况下开始推理,或者换句话说,通过概率推理做出假设类型的问题。这包括计算后验概率,例如 P(新冠肺炎=阳性|血小板= 3)。

回到最初的动机,让我们考虑下面的思想实验。在当前疫情期间,在医院的重症监护室(ICU)。医疗保健专业人员需要了解一个人在不久的将来是否需要重症监护,以便在压力大的时候更好地分配资源。入住 ICU 可能是因为新冠肺炎,也可能不是。一些生物指标目前与患新冠肺炎的较高概率相关,并且可以通过标准测试相对容易地识别(例如参见 Lippi 和 Plebani [2020]的这篇论文)。同样,其他不一定与新冠肺炎相关的指标也可能与增加入住 ICU 可能性的其他疾病相关。这种情况可以使用 BN 进行建模,其 DAG 结构如下图所示(该图仅用于说明):

作者提供的说明性 DAG 图像

给定 DAG 中的父节点,BN 将条件概率表(CPT)关联到每个节点。在我们的示例中,顶层的节点百日咳博德特氏菌没有任何父节点;腺病毒的父节点为百日咳博德特氏菌,其子节点为冠状病毒 NL63链球菌 A;反过来,链球菌 A 不会有任何孩子。在这种形式中,我们认为变量是离散的。每个变量的值实际上是该变量可能采取的模态(状态),例如对于 covid_19 为 0 或 1。

更重要的是,国阵将𝑃的 JPD 分解为 CPT 的产品。对于上面描述的网络,我们的 JPD 是:

P(百日咳杆菌、腺病毒、冠状病毒 NL63、冠状病毒 HKU1、…、副流感 1、肺炎嗜衣原体)= P(百日咳杆菌) x P(腺病毒|百日咳杆菌) x P(冠状病毒 NL63 |腺病毒、百日咳杆菌) x P(冠状病毒 HKU1 |冠状病毒 NL63、百日咳杆菌) x … x

可以通过两种不同的方式获得这些 CPT:我们可以通过从历史数据集中学习一个结构来使用频率主义方法,或者我们可以从我们的领域知识中得出 CPT。通常,最有效的方法是两者的结合。这是 BN 的一个特别有用的特性:在实践中,我们首先想看看数据要说什么,所以我们将使用频繁主义方法从中学习一个结构。这几乎总能给我们的数据带来新的见解,甚至挑战我们之前的假设。一旦我们有了自己的网络,如果我们知道某些链接必须存在,或者有些链接是不可能的,我们就可以应用我们的领域知识来确认或拒绝我们通过频率主义方法计算的结果。例如,在医疗保健领域,我们可能想要强加一种结构,使得住院是疾病的后果,而不是原因

BN 的另一个重要特征是我们在进行概率推理时从它们那里获得的记忆增益。这在处理大数据集时非常方便。在我们 111 列宽的数据集的情况下,我们需要存储 6347497291776 个条目来存储 JPD,它是 JPD 中所有模态大小的乘积。使用 BN,我们可以对其进行压缩编码,就像我们在上面的因式分解中看到的那样,这意味着我们现在只需要存储 817 个条目。这是一个 99.998712879%的内存增益!

图形结构还编码了非常有趣的信息,这些信息可用于获得关于数据的见解:给定 DAG 中的父节点,每个节点都有条件地独立于其非后代。基于这种条件独立性,我们还可以证明,给定一个节点的马尔可夫毯,即 X 的父节点、X 的子节点以及这些子节点的父节点的并集,该节点是条件独立于所有节点的。换句话说,要更新我们对一个变量的信念,我们只需要知道它的马尔可夫毯。

例如,在我们的示例数据集中,为了更新我们关于患者新冠肺炎检测呈阳性的概率的知识,我们将只需要关于变量甲型 H1N1 2009、乙型流感、血小板、冠状病毒 HKU1、呼吸道合胞病毒、常规病房、鼻病毒/肠道病毒的信息。

covid_19 变量的马尔可夫毯——作者的图像

BN 也有一些警告。他们的力量依赖于他们对复杂系统中概率相互作用的建模,而人类的推理能力很弱。BN 在数学上是合理的,并且在理论上有一个处理不确定性的规范方法。然而,尽管它们可以用作分类器,但它们通常不是用于分类任务的特别强大的工具。这是因为我们在处理状态(即离散变量)时丢失了信息。有正在进行的研究,以找到更有效的离散连续变量的方法,以在 BN 中使用它们,但该领域相对较新,超出了本博客的范围。

应用于真实世界的数据集

BN 的结构和参数(CPT)都可以从数据集中学习。在我们的案例中,以色列医院的数据集由 5644 个观察值组成,每个观察值对应于一个接受新冠肺炎检测的患者。除了测试结果,我们还得到了有关此人是否入住医院普通病房、半重症监护病房或重症监护病房的信息,以及各种其他健康测试的结果。请注意,在这个应用程序中,我们将把数据集视为一组具有概率交互的随机变量。

数据集的结构反映了医院的现实:没有两个病人是相同的,因此医生通常需要在入院时为每个病人开出不同类型的测试。因此,我们的数据集包含大量缺失值——在某些列中,缺失值高达总数的 99%。在数据科学领域,有几种众所周知的技术可以处理缺失值,同时保留尽可能多的信息。除了经典的均值/中值/众数插补技术之外,在使用 BNs 时,可以使用聚类和 PCA 算法来填补这些缺失值。由于我们博客的目的主要是教育,我们希望尽可能保持简单明了,我们简单地删除了 95%以上缺失值的所有列。在剩余的变量中,离散变量将用值-999 填充,而连续变量将用中间值填充。这是由 BNs 的分类性质决定的:用-999 填充离散变量有效地创建了另一个状态,而其他的则保持不变。假设我们所有的连续变量的均值和标准差都为零,用中值填充它们可以确保我们在离散它们时仍然能够捕捉到实际值的变化。

正如我们之前说过的,我们的 BN 更好地处理离散变量,所以我们需要离散化我们的连续变量。出于我们分析的目的,我们根据属于百分位存储桶的情况,将数据划分到存储桶中。我们取四个不对称有界桶。假设我们的缺失量很大,并且我们用中间值填充了值,我们最感兴趣的是分布的极端值的变化。因此,我们的第一个桶以百分点 0 和 5 为界,而我们的最后一个桶以百分点 95 和 100 为界。介于两者之间的部分按 50%分割,形成了我们的两个中间桶。

现在…我们已经准备好估算我们的第一个 BN 了!作为最初的尝试,如果我们试图仅从没有任何业务或领域知识的数据中学习结构,我们会得到以下结构:

没有业务领域限制的学习型 DAG 图片由作者提供

从领域知识的角度来看,学习到的结构可能包含一些没有意义的关系。BN 框架是灵活的,允许领域专家介入以在学习过程中引入人类知识。此外,从预测的角度考虑,我们可能希望从一些预测变量到我们的目标变量实施某些弧线。施加一些进一步的约束后,我们会得到一个略有不同的 BN,如下图所示:

包含业务域约束后的 DAG 图片由作者提供

一旦我们用我们的领域知识微调了 BN 的结构,我们就可以开始查询它关于学习过的 CPT。让我们来看看常规病房入院指标的 CPT。对于这种特殊的结构(不一定是最佳的),CPT 仅由新冠肺炎和 B 型流感诊断决定。就解释而言,我们可以说,新冠肺炎和 B 型流感检测均为阴性的患者只有 2.95%的概率需要入住医院的普通病房。

作者的规则变量图像的 CPT

请记住,我们的 BN 允许我们询问假设情景。例如,我们可以问*如果一个病人呈现出指标 A 和 B 的水平,那么她新冠肺炎检测呈阳性的概率有多大?*这个问题的答案可以在变量 covid_19 的后验中找到。让我们评估一名患者的后验概率,该患者已检测到乙型流感病毒,但未检测到冠状病毒 HKU1,并且她处于血小板变量的第一桶。患者患有新冠肺炎的概率高达 52.89%:

患有乙型流感但没有冠状病毒 HKU1 的患者感染新冠肺炎的后验概率——作者图片

值得注意的是,概率引擎只使用马尔可夫链中的节点来获得这样的信息。为了验证这一点,让我们再次使用变量 *covid_19 的马尔可夫毯。如上所示,相应的马尔科夫毯由变量甲型 H1N1 2009、乙型流感、血小板、冠状病毒 HKU1、呼吸道合胞病毒、常规病房、鼻病毒/肠道病毒组成。*对于这些变量中每一个的任何一组值,我们可以得到变量 *covid_19 的后验概率。*在此特定示例中,我们为 Marvok blanket 提供了以下随机值 { '血小板': ' 3 ',' Inf A H1N1 2009 ':'未检测到','流感 B ':'已检测到','呼吸道合胞病毒':'已检测到','冠状病毒 HKU1 ':'已检测到','鼻病毒/肠道病毒':'未检测到','常规病房':0} ,这产生了下图中的后验概率:

给定马尔可夫毯图像的 covid_19 变量的后验概率

马尔可夫链中这些值的组合意味着患者新冠肺炎检测呈阳性的概率为 56.21%。现在,让我们添加一些对马尔可夫链之外的节点的观察,看看变化。例如,让我们添加偏肺病毒甲型流感快速检测:

新冠肺炎给定参数在其马尔可夫毯之外的后验概率——作者的图像

…这对我们新冠肺炎的后验概率没有影响。请注意 BNs 的这一特性的强大之处:在一个大型且潜在复杂的网络环境中,我们只需要知道一个较小的节点子集,就可以对感兴趣的变量的假设场景进行建模。

主要外卖

在这篇博客中,我们介绍了贝叶斯网络,并强调了它们在紧急情况下与决策的相关性,以及在当前疫情环境中的具体应用。

作为图形表示,这些专家建模工具非常直观,简化了复杂系统的分析。它们的价值主要在于它们为复杂数据集揭示的网络结构和概率联系。这使得它们对于面临巨大不确定性的决策者非常有益,因为这已经在许多行业得到了证明,并且我们已经用我们的示例数据集进行了演示。

这个分析的代码可以在我们的 Github repo 中找到。

我们使用 pyAgrum 库进行分析。访问它的网站来学习更多关于用 Python 编程贝叶斯网络的知识。

作者:

  • Hamza Agli 是国际数据科学和人工智能领域的领导者,拥有人工智能和决策方面的博士学位。
  • Á·阿尔瓦罗·科拉莱斯·卡诺是一名数据科学家。Á·阿尔瓦罗拥有经济学背景,擅长广泛的计量经济学技术,包括因果推断、离散选择模型、时间序列和持续时间分析。

对 Pandas 中的数据组应用自定义函数

原文:https://towardsdatascience.com/applying-custom-functions-to-groups-of-data-in-pandas-928d7eece0aa?source=collection_archive---------0-----------------------

照片由西格蒙德在 Unsplash 拍摄

在这里,我将与您分享两种不同的方法来将自定义函数应用于 pandas 中的数据组。已经有许多现成的聚合和过滤功能可供我们使用,但这些功能并不总是能满足我们的所有需求。有时,当我想知道“每月第二个最近的观察结果是什么”或“按产品类型计算的两个加权平均值之间的差异是什么”时,我知道我需要定义一个自定义函数来完成这项工作。

样本数据(不要再说了……)

通常,我尽量不使用这个玩具数据集,因为它已经被使用了很多次,但它有一个很好的分类列,可以从中创建数据组,所以我打算使用它。对于每一个分类,在每一列中有 4 个数字数据的特征来测量花的花瓣和萼片。

聚合函数如何应用于组

通常,首轮分析可能包括检查数据集的描述性统计数据。如果您有一个正在特别查看的表,像“describe”方法这样的方法可以揭示关于列的一些关键信息,比如非空值的数量和数字特征的分布。一个稍微深一点的问题是“每组的这些描述性统计数据是什么?”。根据单个列中的相似值将数据分组在一起,并获取每个组中所有行的最大值,如下所示:

“刚毛”组的最大萼片长度(以厘米计)为 5.8。这很好,但是如果我们所关心的只是群体水平的描述性统计,那么我们也许可以给计算机编一个程序来处理所有的可能性并返回一些有趣的发现。我发现更经常出现的是需要对数据帧中的每个组应用自定义函数。接下来,我将创建一个定制函数,它不容易被 groupby 聚合函数复制。

定义自定义函数

这可能是任何事情。在这里,我感兴趣的是萼片长度与花瓣长度的比值。为什么?也许我有兴趣看看哪种花的萼片变异比花瓣变异大。谁知道呢。重点是,这是一个你想要回答的特定问题,通常你可以用最大值和最小值做一个分组,保存这些结果,得到比率。这是可能的,但是为什么不对组执行自定义函数呢?我们不局限于聚合,如果你愿意,你可以在数据帧的列中找到第二个值。

请注意,该函数使用括号符号显式命名数据帧中的列。您还可以创建应用于数据帧中所有列的自定义函数,并以我们稍后将看到的相同方式在组中使用它。

看看 Python 中的 Groupby 对象

注意,groupby 中的每个元素都是一个元组,第一个元素包含数据分组所依据的内容(可以是多列,在这种情况下是另一个元组)和分组的数据(一个 pandas dataframe)。这意味着无论在 groupby 元素上使用什么函数,都需要接受组和数据帧。

第一种方法

只需对 groupby 对象中的每个数据帧使用 apply 方法。这是最直接的方式,也最容易理解。请注意,该函数将数据帧作为唯一的参数,因此自定义函数中的任何代码都需要处理 pandas 数据帧。

data.groupby([‘target’]).apply(find_ratio)

也可以通过在每个数据帧上执行 lambda 函数来实现这一点:

data.groupby([‘target’]).apply(lambda x: find_ratio(x))

第二种方法

保存 groupby 元素,并对 groupby 对象中的每个元素执行该函数。第二个选项需要更多的工作,但可能是您在定制方面所寻找的。虽然我们之前直接将自定义函数应用于每个数据帧,但我们需要稍微重写函数,以接受 groupby 元素作为参数。但是首先,为要分组的列创建一个 groupby 对象,并为它分配一个变量名。接下来,重写函数以处理 groupby 元素中的每个 groupby。注意:groupby 对象是可迭代的(意味着 python 可以遍历它),并且包含分组级别和结果数据帧。

grouping = data.groupby([‘target’])

在循环中或使用列表理解对 groupby 元素执行函数。我选择在第一个例子中更新一个字典,在第二个例子中添加到一个列表中。每种方法都有其优点:

结论

这是将自定义函数应用于 pandas 中的数据组的两种方法。请,在这里查看完整的笔记本。

参考

https://github.com/caseywhorton/medium-blog-code https://scikit-learn.org/stable/auto_examples/datasets/plot_iris_dataset.html

将数据科学应用于寿险行业:合格精算师的观点

原文:https://towardsdatascience.com/applying-data-science-in-the-life-insurance-industry-a-perspective-from-a-qualified-actuary-698c197e1826?source=collection_archive---------5-----------------------

行业笔记

数据科学如何改变寿险精算师的传统格局(用几个用例解释)

安德鲁·尼尔在 Unsplash 上拍照

作者的背景资料

我是一名合格的精算师,在澳大利亚的人寿保险业有 10 年的从业经验。对于那些可能不熟悉精算师日常工作的人来说,从较高的层面来看,精算师的角色包括:设定死亡率和发病率产品的保险费率,计算人寿保险公司的索赔义务和相关资本要求,为人寿保险公司的股东校准经济价值,以及了解实际经验(如索赔和中止)与预期经验的偏差。

大约 3 年前,在偶然看到 AlphaGo 上的纪录片后,我踏上了自学数据科学/机器学习技术的旅程。我发现如何训练机器学习抽象概念特别令人着迷,在这种情况下,人类在围棋比赛中采取行动的直觉。

有了一定的统计学教育背景,我很快学会了一些数据科学技术,并将它们应用到我的工作中。我将在本文后面用两个用例来解释这些技术如何帮助改进精算分析。

我认为数据科学技术是传统精算技术的良好补充,传统精算技术大多基于统计数据,并且足够幸运,不必从一个完全不相关的领域跳到数据科学的潮流中。

寿险行业数据科学的现状

数据工具

在我的日常工作中(我可以代表澳大利亚大多数人寿保险精算师发言),Excel 一直是分析数据的主要工具,而 VBA 宏用于自动化一些基于 Excel 的 ETL 工作。

从编程的角度来看,大多数寿险精算师在大学期间都会接触到 R。

Excel 有它的优点和缺点,我将在后面详细讨论。

数据科学在寿险公司中的渗透

据我采访的一家咨询公司称,数据科学在澳大利亚的大多数寿险公司都处于起步阶段。“机器学习”和“数据科学”是高管们听说过的时髦词汇,但他们可能会发现很难将它们联系起来。举个例子,有一次,我听过高管们评论机器学习,好像它和相当遥远的人工通用智能一样。

对我来说,数据科学技术在寿险行业渗透率低的原因有两个:

  • 与一般保险公司(如汽车保险公司)相比,人寿保险公司收集的数据历来较少。这是因为大多数人至少拥有一辆汽车,而不一定有人寿保险。此外,与人寿保险相比,汽车保险更容易索赔。这无形中使得依赖大量训练数据(如神经网络)的机器学习模型不那么有吸引力。
  • 人寿保险公司的高管还没有看到数据科学技术如何为组织增加价值的实际用例。鉴于这是一个相对较新的发展,该组织中很少有人有能力建立并同时推广一个商业案例,以确保买入和投资。举个例子,产品团队中很少有人会意识到,我们可以应用一点网络搜集来收集竞争对手产品特性的更新,而不是通过访问每个竞争对手的网站来手动收集这些信息。

本文旨在展示数据科学技术如何为人寿保险公司增加价值,尤其是精算工作流,并根据我自己的经验提供了两(2)个用例。我有更多的用例,希望在以后的文章中分享。

数据科学技术如何帮助精算师——用例

用例 1 —自由文本索赔原因的自然语言处理

大多数人寿保险公司会记录投保人提出索赔的原因。记录的索赔原因包括:

  • 髋骨脱臼、骨折
  • 转移性黑色素瘤
  • 心脏停搏

索赔原因有时以自由文本的形式记录。这些是非结构化数据,对希望在索赔原因层面分析索赔的精算师来说是一个挑战。

上述三个索赔原因的例子分别与髋骨骨折、皮肤癌和心脏病发作具有相同的含义。然而,通过简单地让计算机在没有人类理解的情况下阅读文本,这些权利主张都将被视为独特的权利主张。

例如,计算机可能会在下面的“索赔原因”栏中识别 15 个独特的级别,而实际上精算师应该只关心一个独特的级别(肿瘤或癌症)。

表 1.1(按作者分列的表格)

在使用我将在下面描述的 NLP 技术之前,我已经在 Excel 中进行了过滤,例如,包含类似于“癌症”或“肿瘤”的词串的声明,并将它们手动分类到肿瘤类别。你可以想象这是多么的手工和费力,有时需要我一个接一个地查找诸如“胆管癌”、“精原细胞瘤”等医学术语。

此用例中的任务是使用一些自然语言处理(“NLP”)自动将索赔原因分类到预定义的类别中。汇总索赔原因的一些类别可能是事故/伤害、肿瘤、肌肉骨骼、循环系统疾病、呼吸系统疾病、精神障碍等等。这可以通过以下五(5)个步骤实现。

第一步

定义汇总索赔原因的类别后,手动将自由文本索赔原因标记到这些类别中。这是为了给 NLP 模型提供一些训练数据,但我保证这将是这项任务中最后一个费力的步骤!

实际上,在大约 6,000 件索赔的数据集中,对 366 件索赔进行了标记。就我个人而言,这并没有增加太多的努力,因为我之前已经这样做了。

第二步

对于现在带有标签的(366)索赔,请对索赔原因执行文本清理,例如删除特殊字符、删除双空格和小写字母。很容易将这个步骤扩展到包括其他选择的清理步骤,例如纠正拼写错误。

下面提供了一些用于文本清理的示例 Python 代码:

import re #remove special charactertext_remove_special_character = []for texts in raw_claims_cause:text_remove_special_character.append((re.sub(r'[^\w\s]', '', str(texts).strip())))#remove double white spacetext_remove_white_space = []for texts in raw_claims_cause:text_remove_white_space.append(" ".join(texts.split()))#lower-casingtext_lowercase = []for texts in raw_claims_cause:text_lowercase.append(texts.lower())

第三步

为索赔原因的清除文本创建单词嵌入。根据下面的 Python 代码示例,我选择了一种形式的 BERT vector 化。

from sentence_transformers import SentenceTransformer bert_input_claims_with_label = text_claims_register['text_claims_causes_cleaned'].tolist()model_claims_with_label = 
SentenceTransformer('paraphrase-mpnet-base-v2')embeddings_claims_with_label = model_claims_with_label.encode(bert_input_claims_with_label)print('Shape of vector outputs:', embeddings_claims_with_label.shape)

来自变压器的双向编码器表示(“BERT”)模型是谷歌在 2018 年开发并发布的 NLP 模型。这是一个使用维基百科和图书语料库上的文本段落进行预训练的模型(只是为了确保训练数据在语法上是正确的)。

本质上,每个自由文本的索赔原因都被 BERT“翻译”成计算机可以理解的语言,由长度为 768 的向量表示。

这个步骤的输出实际上是每个自由文本声明的向量,原因是带有标签,长度也是 768。

整体单词嵌入的形状如预期:

Shape of vector outputs: (366, 768)

第四步

对于没有标签的索赔原因,重复步骤 1-3。该步骤的输出是没有标签的每个自由文本索赔原因的有效向量(即,我们想要标记的索赔原因),长度也是 768。如果你有 n 个没有标签的索赔原因,你应该期望单词 embedding 的形状是维数(n,768)。

第五步

将不带标签的索赔原因的矢量输出与带标签的索赔原因的矢量输出进行比较。基于余弦相似度返回最接近匹配的索赔原因标签。

简而言之,由于训练数据由 366 个索赔组成,对于我们想要自动标记的每个索赔原因(即,对于下面样本代码中 similarity_df 数据帧中的每一行),存在 366 列,它们从数字上对特定索赔原因与训练数据中的每个索赔的相似程度进行评分,并且使用 pandas idxmax 方法返回给出最高分数的索赔的位置。

from sklearn.metrics.pairwise import cosine_similarity embedding_array_claims_with_label = np.array(embeddings_claims_with_label)embedding_array_claims_without_label = np.array(embeddings_claims_without_label)similarity = 
cosine_similarity(embedding_array_claims_without_label, embedding_array_claims_with_label)similarity_df = pd.DataFrame(similarity) #return position of closest match, with which the label can be looked upsimilarity_index = similarity_df.idxmax(axis = 1)

下面是一些声明的最终输出片段,它们肯定通过了“眼睛测试”。

表 1.2(按作者分列的表格)

对精算师的启示

如果索赔原因能够在这一级别进行映射和汇总,那么精算分析将会有许多影响。一些例子包括:

  • 按索赔原因调查趋势(例如,新冠肺炎疫情期间精神障碍或相关索赔等主观索赔的趋势)
  • 根据索赔原因将公司索赔经验与行业经验(由澳大利亚政府机构发布)进行比较
  • 通过职业、性别等评定因素了解最常见的索赔原因。
  • 按专业将待评估的索赔分类给合适的索赔评估员(例如,某些索赔评估员可能比其他人更擅长精神障碍索赔)

单独地,使用类似的方法,这个用例可以外推到职业类。例如,将“水管工,3 年工作经验”等职业描述映射到蓝领类别,将“律师,学位资格”映射到专业/白领类别,这样可以进一步提供精算分析。

总结—用例 1

总之,这个用例为精算师提供了一种将自由文本索赔原因数据自动分类到预定义类别中以供进一步分析的方法。最终,在伯特的帮助下,计算机能够理解人类语言。在这种情况下,计算机能够理解和比较医疗术语或索赔事件的描述,这有时可能会很混乱。

在 Excel 中手动筛选的替代方法不实用,尤其是对于大量索赔。

用例 2 —大规模数据的复杂转换

如前所述,Excel 是大多数寿险精算师的主要 ETL 工具。虽然 Excel 为用户提供了转换数据的透明度,但它在精算应用方面有许多严重的缺点:

  • Excel 有行和列的限制。更常见的情况是,即使在达到这个限制之前,考虑到对计算资源的要求,在电子表格中导航会很慢很困难。
  • Excel 不是一个可扩展的工具,因为通常很难有效地将一个工作簿中的数据与另一个工作簿中的数据连接起来,更不用说执行计算了,尤其是当数据集很大时。

这个用例所基于的任务用 Excel(实际上)是无法完成的,但是用 PySpark 通过一点数据帧操作就可以轻松完成。

为了用一个简单的例子来描述这个任务,假设一个人寿保险投保人购买了一份具有两(2)个保险的保单,一个是针对死亡事件的保险(“死亡保险”),另一个是针对预先定义的医疗状况事件的保险(“创伤保险”)。

这两种保险与不同的赔付金额相关联,通常死亡保险比创伤保险的赔付金额更多。此外,在澳大利亚的大多数情况下,支付创伤保险会减少最初购买死亡保险的金额。例如,假设投保人购买了赔付金额为 10 万美元的死亡保险和赔付金额为 5 万美元的创伤保险。如果投保人在外伤保险中索赔并获得 50,000 美元的赔付,死亡保险将降至 50,000 美元(即 100,000-50,000 美元)。

读者不需要理解技术细节,但这种在澳大利亚很常见的产品结构,为死亡保险提供了不同的死亡率假设。简而言之,如下图 2.1 所示,死亡保险的蓝色和绿色部分需要采用不同的精算假设进行建模。本质上,这需要将死亡保险“拆分”成两个部分,我们将对这两个部分的索赔成本进行不同的建模。

图表 2.1(按作者分列的图表)

实际上,这需要识别具有死亡保险和“关联的”创伤保险的投保人,并且在特定投保人的数据的该“框架”中,为死亡保险添加额外的行,并且基于创伤保险的赔付额来计算两行死亡保险的赔付额。您应该很快就会发现,用 Excel 很难做到这一点,部分原因是这种转换不是一对一的。下表 2.2 以表格形式说明了所需的转换:

图表 2.2(按作者分列的图表)

此时 PySpark 及其内置的 SQL 函数变得非常强大,有了它,所需的转换可以在以下四(4)个步骤中实现:

第一步

创建一个新列“链接金额”,该列返回投保人 ID 级别的创伤保险赔付金额,如下图 2.3 所示。

图表 2.3(按作者分列的图表)

这可以通过在 Policyholder ID 字段上创建一个窗口或“框架”来实现(在下面的示例代码中,我将它与 Policyholder_ID 互换使用):

from pyspark.sql import functions as F
from pyspark.sql import Window as Wdef compute_Linked_Amount(Policyholder_ID: List[str]) -> Column:window = W.partitionBy(*Policyholder_ID) \.rangeBetween(W.unboundedPreceding, W.unboundedFollowing) \.orderBy("Policyholder_ID")return F.when(F.col("Cover") == "Death", F.lit(0.0)).otherwise(F.col("Amount")).over(window)

第二步

创建另一个新列“拆分类型”,仅使用死亡封面行的数组数据类型指定,将这些行的值设置为【死亡补偿 1,死亡补偿 2】(即具有相同字符串的 2 乘 1 数组),如下图所示:

图表 2.4(按作者分列的图表)

第三步

将这些行中的每一行“分解”成两个相同的行,除了新列的值分别为死亡补偿 1 和死亡补偿 2。如下图所示。使用 PySpark 中的 explode_outer SQL 函数可以实现“explode”操作。

图表 2.5(作者图表)

第四步

创建最终支出金额列,用以下 WHEN OTHERWISE 语句表示。

from pyspark.sql import functions as FF.when(col("Split Type") == "Death Comp 1", \
F.col("Amount") - F.col("Linked Amount") \
.otherwise(F.col("Linked Amount")

图表 2.6(按作者分列的图表)

您应该能够看到,在删除了“最终金额”列中值为 0 的行之后,根据图表 2.2,转换就完成了。

总结—使用案例 2

总的来说,这个用例代表了一个转换,如果同一个投保人也有一个创伤保险,则需要将数据帧中对应于死亡保险的行分割成单独的行。使用 Excel 中的操作不容易做到这一点。

在使用案例中已经证明,使用 PySpark 可以轻松实现这种转换,PySpark 为用户提供了以下功能:

  • 能够在数据子集上创建窗口(例如,使用投保人 ID 字段为每个投保人)并提取该级别的信息。在本例中,这确定了投保人是否同时拥有死亡和创伤保险,如果投保人拥有,则根据用例的步骤 1 在死亡保险的行中填充创伤保险的赔付金额。
  • 能够在数组数据类型的数据帧中插入列,这使得像拆分这样的操作可以使用内置的 SQL 函数,如“explode_outer”。
  • 与 Excel 相比,转换效率更高,因为 PySpark 以其在大型数据集/数据帧中的运算速度而闻名。这是至关重要的,因为我们推断用例涵盖除死亡和创伤以外的类型,以及人寿保险公司内用于商业用途的所有投保人。

结论

对于 2021 年的寿险精算师来说,数据科学、机器学习可能仍然是一个遥远的词,但它们比人们想象的更近。就个人(和商业)而言,打破准入门槛需要一点努力,这最终是值得的。

最后,我希望你觉得这篇文章是一篇好文章,如果你有,请分享。

将设计和敏捷思维应用到您的数据科学项目中—第 1 部分

原文:https://towardsdatascience.com/applying-design-and-agile-thinking-to-your-data-science-project-part-1-ef945c558181?source=collection_archive---------22-----------------------

了解如何将设计思维应用于任何数据科学案例

罗斯·斯奈登在 Unsplash 上的照片

把世界想象成一个由数百万块碎片组成的巨大拼图。拼图仍未完成,一些碎片已经放置,但仍有空间放置新的碎片。我们想让我们的解决方案成为这个巨大拼图中的一块拼图,但是我们不知道怎么做。设计和敏捷思维帮助我们将我们的解决方案整合成一个拼图,放入这个巨大的拼图中。

创建一个数据产品可能是混乱和困难的。我的建议是,当一开始看起来似乎没有解决方案时,不要轻易放弃,而是要继续尝试构建这些部分!

遵循设计和敏捷思维作为您旅途中的向导,可以使您的解决方案的设计、实现和到达变得更加容易。

你可能听说过设计和敏捷思维方法,因为它们在许多组织中被广泛使用。本文简要介绍了这两种方法,并解释了如何在一个数据科学案例中应用设计思想

如果你对如何在一个数据科学案例中应用敏捷思维更感兴趣,请查看本文的 第二部分 ,否则,继续阅读!

设计还是敏捷思维?

差异#1

如果你对实际问题和切实可行的解决方案不清楚,那么你可以使用设计思维方法的步骤来帮助你度过难关。

如果你处在一个问题已经明确的情况下,并且你需要管理你的方式直到你按时达成解决方案,那么你可以遵循敏捷思维方法。

差异#2

设计思维用于重新定义问题,理解用户,尝试新颖的概念,并交付解决方案的最优和创新设计。

敏捷思维是一种项目管理方法,将解决方案的设计(设计思维过程的结果)转化为实际的解决方案。

类似

两者有相似之处,例如:

  • 以人为中心,优先考虑最终用户和利益相关者的需求,
  • 迭代,由多个循环组成,并在每次迭代中根据提供的反馈进行改进。
  • 聚焦选项,列举探索的可能性

这两种方法的结合可以确保高质量地解决组织内部的实际问题。

设计思维:没有数据的数据科学

在大多数情况下,大量的数据是可用的,如果我们立即开始查看数据,我们可能会在试图理解数据列和字段时迷失,并忘记最初的问题。因此,我们应该后退一步,脱离细节,尝试创建一个组织的流程及其瓶颈的整体概述。

在此阶段,我们期望的结果识别问题并设计解决方案。因为我们想要带来一个数据驱动的解决方案,所以设计应该包括数据需求的初始草案和数据产品的第一个概念。在这个阶段,我们还应该定义最终产品的用户,并让他们参与设计过程。

设计思维方法包括三个步骤:问题理解,发散,收敛,如下图所示。

设计思维,埃琳娜·斯塔马特卢素描

第一步:理解问题

这一阶段主要是为了更深入地了解组织内部的现状。

设计思维:第一步,Elena Stamatelou 的草图

A.面谈

安排与组织中不同人员的面谈,有助于我们深入了解他们的流程、沟通方式和愿景。从数据科学家的角度来看,预期的关键成果是发现组织的数据成熟度级别,这显示了数据科学在其组织中的当前角色。为了达到这种理解水平,我们为受访者列出了一系列问题。

个人问题

  1. 你的背景是什么?
  2. 你在公司的角色是什么?你在哪个部门工作?
  3. 你对数据科学了解多少?

团队问题

  1. 你是团队的一员还是管理团队?
  2. 与你密切合作的同事的角色是什么?
  3. 你的团队的职能是什么?
  4. 从规模和层级来看,团队的结构如何?
  5. 您在团队内部以及与其他部门沟通的频率如何?

商业问题

  1. 你的组织最具挑战性的问题是什么?
  2. 你的团队最具挑战性的问题是什么?

数据科学问题

  1. 您的组织中有数据科学相关项目的例子吗?如果是,这些项目是关于什么的?
  2. 您的组织中有数据科学家吗?如果是,能否举例说明它们的影响?
  3. 对于数据科学如何应对贵组织内部的挑战,您有什么想法或建议吗?

数据管道问题

  1. 您目前是否收集或拥有任何数据?
  2. 您是否执行涉及数据处理或分析的任务?
  3. 数据是如何存储和提取的?
  4. 数据的大小是多少?

技术

  1. 您使用哪种类型的控制面板进行数据可视化/分析?
  2. 您使用哪种数据库系统来存储数据?
  3. 你使用云技术吗?
  4. 对于数据分析/建模,你有什么偏好的编程语言吗?

视觉问题

  1. 您认为您的组织在未来十年内会朝着更加数据驱动的方向发展吗?
  2. 员工适应新技术的难易程度如何,比如一个新的软件或数据产品?

B.主要调查结果演示

演示应该包括对访谈结果的总结,并总结组织面临的主要挑战。

为了增加数据科学视角,定义组织的数据成熟度级别对于将数据使用推进到下一个级别至关重要。成熟度各不相同,从不使用任何数据的公司到信任数据并根据数据洞察力做出决策的公司。

下图显示了迈向数据驱动型组织的旅程。

数据成熟度级别,由 Elena Stamatelou 绘制

  1. **数据拒绝:**主动拒绝数据使用
  2. **数据冷漠:**对数据的使用缺乏兴趣
  3. **数据感知:**数据收集和历史数据的可视化,用于了解过去的事件(描述性分析)
  4. **数据通知:**基于历史数据的模型,用于未来事件预测(预测分析)
  5. **数据驱动:**基于预测的决策建议(说明性分析)

在了解了关键挑战和组织的数据成熟度后,演示文稿还应包括一些示例,说明数据科学如何帮助组织应对一些挑战并转变为更加数据驱动的组织。

第二步:发散

如果一切皆有可能呢?我们应该带着这个问题度过这个阶段,并探索每一种可能性。该阶段由两部分组成;利益相关者地图初稿和集思广益会议。

设计思维:第二步,Elena Stamatelou 的草图

A.利益相关方地图(初稿)

在问题理解阶段之后,我们对项目中涉及的各方有了很好的了解。为了组织不同利益相关者的视图以及他们的参与和影响,我们需要创建一个利益相关者地图的初稿,如下所示。

利益相关者地图,Elena Stamatelou 绘制

区分不同类型的利益相关者的两个主要变量是他们的兴趣和他们在我们项目中的影响水平,这两个变量的所有组合都是可能的。

目前,我们不需要绘制所有利益相关者的详细地图,但一个初稿就足以继续设计思维方法的下一步。

B.头脑风暴

完成利益相关者图后,我们从图的不同部分选择最具代表性的利益相关者,这样我们就有了具有不同影响和兴趣水平的利益相关者的合适组合。然后,我们邀请这些利益相关者参加我们的集思广益会议。

在头脑风暴期间,我们与不同的利益相关者创建两到三个小组。我们将会议分为两部分:挑战思维和解决方案思维。

挑战创意部分,我们询问参与者他们面临的两大挑战,将他们分组,让他们投票选出最重要的挑战。然后,对于投票最多的挑战,我们要求他们确定受其影响的利益相关者,并将他们分组。

之后,我们选择三个投票最多的挑战及其最受影响的利益相关方,并开始解决方案的构思部分。随后,我们要求参与者为密切相关的利益相关者设想理想的情况,并找出阻碍组织达到这一理想情况的瓶颈。然后,我们让他们就管理这些瓶颈的方法进行头脑风暴,并根据相似性对他们进行分组。

第三步:融合

在分散和探索了各种挑战及其可能的解决方案之后,没有添加任何限制,是时候收敛了。这里的目的是定义一个具体的用例,并向涉众提出一个数据驱动的设计概念。

设计思维:第三步,Elena Stamatelou 的草图

A.概念创造:

我们在这里的首要任务是总结集思广益会议期间产生的所有挑战和解决方案,并重点关注那些似乎对组织影响最大、负担更重的挑战和解决方案。对于每一个项目,我们都致力于定义数据需求的初稿、数据产品的概念和高级数据驱动的数据管道。

对于数据需求的初稿,我们考虑数据可用性、可能的数据收集和数据隐私问题。在那之后,我们开始制作最终数据产品的第一份草图,包含了最初的数据需求。考虑到数据需求和最终产品,我们设计了一个数据管道的概述,其中包括从数据收集到最终产品部署及其交付给利益相关方的整个流程。

在头脑风暴期间,我们对定义的关键挑战的解决方案有了一个良好的初步设计,然后安排与主要利益相关者的会议。我们利用这些会议的时间来讨论这些设计,并汇聚到一个解决用例的设计,这个用例对他们来说似乎更有趣,也更现实。

B.利益相关者地图细化

在定义了解决方案的第一个设计和一个具体的用例之后,是时候回顾利益相关者地图的第一个草案了,它是在头脑风暴会议之前创建的。

这一步的主要目的是细化不同的涉众群体并创建三个团队;数据、反馈和管理 团队。这些团队对于产品开发的共同创造过程以及实现与他们的有效协作是必不可少的。

团队,草图由 Elena Stamatelou 绘制

数据团队将包括与数据密切相关的利益相关者,如数据或业务分析师。该团队将帮助收集数据和保护数据隐私。反馈团队将由最终用户组成,他们将对原型和最终产品进行实验并提供反馈。在管理团队中,将包括层级较高的利益相关者。这个团队将作为指导委员会,对项目方向的决策有很高的影响。

C.工程计划

实施设计前的最后一步是制定项目管理计划。这是设计思维和敏捷思维方法之间的过渡步骤。

有必要回顾在设计思考过程中创建的所有信息,包括数据需求、解决方案的设计以及不同的利益相关者群体。基于这个评审,我们创建一个敏捷计划,在它的执行之后将交付数据产品。

包扎

使用设计思维作为指导,使合适解决方案的形成变得更加容易。在设计之后,实施就来了,敏捷思维可以在这个过程中做我们的向导。

阅读本文的 第二部分 ,了解如何在您的数据科学案例中应用敏捷思维。

将设计和敏捷思维应用到您的数据科学项目中—第 2 部分

原文:https://towardsdatascience.com/applying-design-and-agile-thinking-to-your-data-science-project-part-2-d11ce96275f6?source=collection_archive---------35-----------------------

了解如何将敏捷思维应用于任何数据科学案例

汉斯-彼得·高斯特在 Unsplash 上拍摄的照片

把数据驱动的解决方案想象成一块拼图。这一块需要有一个合适的形状和颜色,以适应一个更大的拼图。我们的世界就是这个拼图,还没有完成。我们想做一个合适的拼图块,然后放入拼图中,使其更加完整。换句话说,我们想给这个世界带来一个合适的解决方案。设计和带来合适的解决方案可能很复杂。设计思维做出解决方案的形状,而敏捷思维确保其实施,取得设计思维的成果。

这是文章的第 2 部分,重点是如何将敏捷思维应用到数据科学项目中。如果您对将设计思维应用于数据科学案例或了解两种方法的异同更感兴趣,请阅读本文的第 1 部分 ,否则,请继续阅读!

敏捷思维:数据科学与数据

鉴于解决方案的设计是设计思维的结果,敏捷思维方法促进了设计的实现过程,并确保了最终数据产品的及时交付。虽然在设计思维阶段,数据的使用不是必须的,但是在敏捷思维阶段,数据的使用是必须的,但不一定从一开始就需要。此时,我们期望的结果是在一个时间框架内发布数据产品。

敏捷思维是一种迭代方法,由多个阶段组成。每个阶段都将逐步建立在前一个阶段的基础上,以产品发布为最终目标。我们从我们产品的一个非常基本的轮廓开始,那就是交互式线框(第一阶段)我们不断地添加功能,每次都是最新版本。从基本的第一个版本开始,我们迅速发布了一个最小可行产品(第 2 阶段),然后是 beta 产品版本(第 3 阶段)。最后,我们在产品发布(阶段 4)期间交付我们的最终数据产品。

每个阶段由相同的五个组成部分组成;设计、预处理、建模、可视化和部署。各阶段及其基本组件在下图中显示为金字塔。每个阶段都考虑相同的方面,但是每次都在它们的基础上进行改进,使它们更加完整。

敏捷思维的生命周期,Elena Stamatelou 的草图

在每个阶段的开始,都有一些空间来重新定义我们的解决方案方法,考虑从上一阶段学到的知识。这提供了结合变化的灵活性的可能性,以及对我们最初方向的执着,这是设计思维方法的结果。在每个阶段的末尾,需要与数据产品的最终用户(反馈团队)一起计划反馈会议。该反馈提供了关于他们的需求和他们对产品当前版本的满意程度的知识。因此,这有助于定义需要在下一阶段应用的必要修改。

阶段 1:交互式线框

这个阶段包括项目启动,还没有数据。主要的可交付成果是产品大纲交互线框,这将是一个非常基本的用户界面,将使用户对我们的方向有第一印象。

敏捷思维:交互式线框(第一阶段),Elena Stamatelou 的草图

设计

我们使用设计思维方法的结果来定义解决方案的初始架构、任务以及该阶段的方向。我们还创建了用户故事的初始草图,我们将在这个阶段使用它作为指南。

预处理

**数据要求:**由于没有数据,这里的主要目的是定义数据要求。我们考虑两个相反的方向。一方面,通过想象没有关于数据可用性的限制,我们定义必要的数据来解决挑战。从另一方面来说,通过考虑现有的数据及其限制,我们想到了实现最终项目目标的方法。然后,我们得到一个包含所有必需数据的列表。

数据保护影响评估(DPIA) :在获取数据之前,我们需要准备并与组织签署协议,以确保数据保护。我们致力于 DPIA 流程,以识别并最大限度地降低可能的数据保护风险。

建模

**文献研究:**在开始任何建模方法之前,我们需要做一个文献回顾。我们的目标是创建一个包含所有可能的建模方法的列表,从最佳实践到最先进的技术

形象化

线框:牢记用户故事中描述的用户视角和需求,我们创建可视化的格式和一般结构。我们还制作了一些高层次的草图,并将它们组合成一个交互式线框,该线框将用于该阶段结束时的反馈会议。

部署

**数据基础设施了解:**我们收集有关组织基础设施的信息,如数据存储或 BI 工具。我们的目标是找出我们将使用哪些工具来部署我们的数据产品。使用员工熟悉的工具比尝试引入新工具更具成本效益,也更容易。

反馈

在这个阶段的最后,我们组织一个反馈会议,在这个会议中,我们向用户展示交互式线框,让他们使用它,并向他们提问以获得他们的反馈。

阶段 2:最低可行产品

在这里,我们的目标是为利益相关者和最终用户提供解决方案的基础,这将是最小可行产品。从我们的角度来说,我们希望收集最大数量的反馈和学习,这样我们就可以确保我们最终的工作原型能够满足用户和利益相关者的期望和需求。

敏捷思维:最小可行产品(第二阶段),Elena Stamatelou 的草图

设计

我们使用前一阶段收集的反馈来提炼用户故事,重塑我们的设计,并规划当前阶段。我们通过添加更多细节来更新初始架构

预处理

数据收集设计:有了数据需求列表,我们就设计了获取数据的方法。如果数据已经存在于他们的系统中,那么请求和接收数据是很容易的。如果数据尚不存在,我们应该定义一个数据收集流程。

数据生成:由于数据收集需要一些时间,为了节省时间,我们根据数据需求生成合成数据**、**。在这个阶段,我们使用生成的数据。

建模

**基本特征工程:**在拥有真实数据之前,我们使用合成生成的数据来创建我们认为最有影响力的基本特征。我们还清楚地定义了目标变量。

**基本建模:**使用基本特征,我们应用简单模型来预测我们的目标变量。

形象化

**模型:**使用前一阶段的线框作为起点,我们基于更新的用户故事添加数据可视化。我们的重点是制作一个用户友好的交互式模型,让用户感受到我们的方向,并为我们提供建设性的反馈。

部署

**部署要求:**根据对组织基础设施的了解,我们指定部署要求,并检查可行性和所涉及的成本。

**本地部署:**在这个阶段,我们在本地服务器上运行模型,

反馈

在这个阶段结束之前,我们会安排一次与用户的会议,通过向他们展示我们的最小可行产品,与他们分享我们的概念和预测的数据可视化。

阶段 3:测试产品版本

这个阶段的重点是交付一个 beta 产品版本,其中包括真实的模型预测,并在一个用户友好的交互式仪表盘中显示出来。在这个阶段的最后,用户和涉众将测试产品以识别 bug 和其他缺陷,以便进入产品交付阶段。

敏捷思维:Beta 产品版本(第 3 阶段),Elena Stamatelou 的草图

设计

在开始该阶段之前,我们重新考虑所有的知识,并通过将数据存储的细节添加到模型部署中来更新架构。这个阶段离实际产品更近了一步,因为我们将使用真实数据。

预处理

**数据收集:**根据数据收集设计和数据需求,我们从组织中收集数据。

**数据探索:**收到真实数据后,我们检查完整性(缺失值)和及时性(最新数据)以确保数据质量,以便我们可以进一步使用它进行建模。为了更好地理解数据的意外行为,我们寻找异常值(异常),这些异常值是错误的或非常重要的值,显示了数据的奇怪行为。

建模

**特征工程:**为了继续使用相同或相似的建模方法,我们检查所提供的数据在多大程度上可以替代合成数据。然后,我们将合成数据的特征与真实数据的特征进行匹配,并且如果我们认为来自真实数据的更多特征会改进预测结果,则添加这些特征。

**高级建模:**有了真实数据的特征,我们首先应用前一阶段的简单模型,以确保真实数据的特征可以替代合成的特征。如果简单的模型不再适用,我们会用更合适或更复杂的模型来代替它。在这里,我们还可以尝试不同的建模方法或超参数调整,并比较不同模型或同一模型变体的性能。

形象化

**原型:**从上一阶段的交互式模型过渡到原型,需要进一步改进数据可视化和仪表板的前端开发。这里的重点是制作一个可以为最终用户服务的功能性和交互性的仪表板。

部署

**云/内部部署:**根据前一阶段定义的部署需求,我们应该将解决方案从本地运行转移到我们的设备上,在组织的云或内部服务器上运行。在这一部分中,我们部署预测模型,而不是定型模型。如果一切顺利,在下一阶段,我们还将部署培训模型。

反馈

这个阶段产生了一个类似于实际产品的 Beta 产品版本,因为我们同时在前端(仪表板)和后端开发(部署)上工作。在这一点上,我们给出了在产品发布之前从用户那里获得反馈的最后机会。

第 4 阶段:产品发布

在这个阶段,我们应用最后的改进。当不需要改进时,我们通过使持续验证成为可能来部署训练和预测模型,然后推出数据产品。我们的目标是向涉众和用户交付一个功能性的、可靠的和可用的产品。

敏捷思维:产品发布(第 4 阶段),Elena Stamatelou 的草图

设计

在这个最后的设计周期中,我们从最后的反馈会议中收集反馈,并在发布之前将最后的改进添加到产品架构中。

预处理

**预处理验证:**我们从组织中收集新数据,并将其与用于测试产品版本的数据进行比较,以检测差异。

**健全性检查:**基于对新数据预处理验证的观察,我们创建健全性检查,以确保输入数据具有合适的和期望的格式。

建模

**建模验证:**使用新数据,我们验证 beta 产品版本,并在需要时应用最后的更改。

形象化

**仪表板:**根据前一阶段的原型,我们构建了一个更稳定的仪表板,在其中我们应用了最后的改进。

部署

**持续部署:**如果测试产品版本的预测模型在云或内部基础设施中顺利运行,我们可以更进一步部署我们的模型。首先,我们还部署了我们的训练模型,以便在代码更新或新数据的情况下重新运行。我们的目标是为我们的预测模型实现持续集成(CI)、持续交付(CD)和持续培训(CT)。这意味着,对于任何新的数据或代码更新,模型都会被重新训练并提供更新的预测。

包扎

终于,我们的产品可以上市了!

随着不断的集成、开发和训练,我们的预测模型会随着新的输入而改进。提供这种灵活性使我们的数据产品能够适应变化。通过将它暴露给新的和更多的数据来改进数据产品的预测。

敏捷思维通过将来自设计思维的设计概念作为输入,创建了创建数据产品的指导方针。

如果你对如何在数据科学案例中应用设计思维感兴趣,请阅读本文的 第一部分

将改善和 5S 原则应用于外部数据采集

原文:https://towardsdatascience.com/applying-kaizen-principles-to-external-data-acquisition-c7c377300a64?source=collection_archive---------39-----------------------

解决任何分析问题的关键是拥有正确的数据。数据是一种资产。有远见的组织会像寻找收入流或新客户一样积极地寻找。有了相关数据,组织可以做出更明智的决策,解决关键的业务挑战。如今,“良好的分析”意味着远远超越一些常用的算法或仪表板来展示内部数据。

希望充分利用智能革命的企业必须不断发展,超越他们的四面墙,以获取他们需要的数据。获得相关的外部数据是一种竞争优势。公司必须制定有效的策略来寻找、获取、部署和理解新的替代数据资产。外部数据获取策略有助于更快地解决复杂问题,让企业真正成为数据驱动型企业。

图片由 格尔德 转自 Pixabay

那么,如果外部数据是竞争优势的来源,企业如何才能更好地获取外部数据呢?事实证明,90 年代制造业中流行的持续改进过程同样可以应用于现代数据工程。具体有两个:Kaizen 质量框架(为什么、什么、哪里、谁、什么时候和如何)可以帮助规划你的数据采集计划;而 5S 技术提供了一个可以用来管理和组织外部数据的框架。

改善质量框架

Kaizen 对持续改进的关注集中在六个基本问题上:为什么、什么、哪里、谁、何时和如何。让我们探讨它们与外部数据采集的关系。

我们为什么需要外部数据?

关键是从定义你最终想要解决的问题开始。你要么有一个需要回答的特定问题(为此你需要新的、更好的或补充的数据)。您正在努力优化现有的解决方案(微调模型并提高预测的准确性。)

我们需要什么数据?

第一步是寻找有助于你发现真知灼见的相关数据。外部数据,如客流量、定价、公司图、技术图以及其他营销和财务属性,可以改善预测模型和业务成果。

从哪里获取数据?

寻找正确的数据本身就存在一系列挑战。有成千上万的主要和公共数据源。为您的用例搜索和评估数据可能需要一些时间。及时访问数据是组织敏捷性的关键。

谁将使用这些数据?

有各种各样的外部数据用例,这意味着用户的类型各不相同。用户有不同的需求和技能。用户范围从市场营销和销售运营成员到客户洞察团队、欺诈和风险官、业务分析师和数据科学家。了解他们的需求并提供他们可以快速评估和消费的数据,对于从您的数据投资中获得最大投资回报至关重要。

数据应该什么时候刷新?

根据您的用例及其所需的数据信号,需要确定刷新数据的频率。确保您有正确的数据入职频率,以保持您的数据是最新的、准确的和相关的。还应持续监控预测模型的性能或漂移,以查看模型是否仍按照业务预期运行。如果发生任何信号丢失(例如丢失第三方 cookies),您应该立即为您的业务连续性寻找替代数据源。

如何利用这些数据进行分析和机器学习?

在获取、准备和整合外部数据之后,下一步是评估您将如何在您的分析或机器学习平台中使用这些数据。有多种连接选项可供考虑。看看您是否可以通过导出、API 和与存储(如 S3 或雪花)的连接功能来访问数据。此外,根据您的业务需求,规划与 Google Big Query、Databricks 和 Salesforce 等平台或应用程序的连接。

5S 技巧

5S 指的是下面这些可以让你的数据程序高效有效的日语单词:

  1. Seiri(排序)—对所有项目进行排序,并删除那些不必要的项目
  2. Seiton(订单)—将所有必需的物品放在最佳位置,以实现其功能
  3. Seis(清洁)—定期清洁和检查
  4. Seiketsu(标准化)—标准化用于分类、订购和清洁的流程
  5. Shitsuke(过程)—规程和过程导向

很多时候会加上第六个 S——安全。你可能已经看到链接了。让我们看看如何将这个框架用于外部数据。在您开始购买外部数据之前,请考虑:

Seiri (Sort)消费数据准备

利用外部数据的一个主要挑战是,外部数据通常不适合消费。数据准备过程可能包括数据清理、数据转换以及正确组织数据以供使用。

Seiton(订单)数据匹配与整合

要使用外部数据来解决您的特定业务问题,您需要将它与您的内部或培训数据集相匹配。匹配或身份解析是一项复杂的活动。如果您没有合适的工具,这可能会非常耗费资源。计划如何在数据中下订单,以便可以消费。

seis(Clean)数据质量和验证

了解数据源、其数据质量、覆盖范围、差距、新近性、更新频率、风险,以及最重要的与您的业务环境的相关性至关重要。数据购买的另一个风险是缺乏投资回报。确保您知道您的数据投资将带来的回报。在投资数据产品之前了解质量和风险

Seiketsu(标准化)标准化分拣、排序和清洁流程

数据采集不是一蹴而就的事情。数据获取、评估、清理、匹配和整合方法的标准化将有助于持续有效地使用数据。

Shitsuke(工艺规程)——工艺导向、监控和改进

建立一些机制,以便您可以监控预测模型漂移的数据。使数据获取成为您整体数据管理策略的一部分,并确保在购买之前对数据的相关性、质量、覆盖范围、风险和准备情况进行评估。

安全——隐私、合规、法规

考虑隐私、安全和法规遵从性要求。GDPR 和 CCPA 等隐私法规使得外部数据管理比以往更加困难。检查外部数据的合规性,保护政策,并纳入最佳实践,以消除将数据投入使用的风险。合规数据将帮助您更快实现高级分析和 ML 目标。

分析现在是竞争绩效的基础,就像数字化转型之前的制造能力一样。用于增强制造过程的技术可以直接应用于提高组织的分析能力。具体来说,5S 和 Kaizen 等技术提供了每个组织应该提出的战略问题,以增强其外部数据获取流程。生产方式正在发生变化,但这些学科在当今以 ML 和分析为动力的组织中仍然适用。

将机器学习应用于核反应堆动力学

原文:https://towardsdatascience.com/applying-machine-learning-and-exploratory-data-analysis-to-nuclear-reactor-kinetics-d1dae16fb081?source=collection_archive---------26-----------------------

使用 Python 进行核物理、反应堆操作和预测建模的初级读本

图一。2 兆瓦特特里加反应堆满功率。来源:我(沃克·佩恩)

根据核管理委员会的数据,美国有 31 个核研究反应堆。我碰巧有操作其中一个的许可证,在这篇文章中,我将演示我如何应用机器学习和通用数据分析技术来预测脉冲功率水平并提高我们实验的可重复性。

背景

一个裂变核反应堆利用裂变原子的能量工作。当铀-235 吸收一个中子时,它有机会裂变和分裂,释放裂变产物、中子和动能。这种能量加热冷却介质,冷却介质通常通过管道输送到热交换器,然后输送到蒸汽涡轮机,从而产生电能。我的设施是 TRIGA 反应堆的所在地,它不产生任何电力——它纯粹用于研究和实验。

有趣的事实:一公斤的铀-235 比一公斤的煤多 300 万倍的能量。哦,裂变反应不会产生碳排放。(我一点也不偏心。)

与商用反应堆相比, TRIGA 反应堆在许多方面都是独一无二的,其中之一就是其执行“脉冲”的能力。燃料的高度负反应性系数意味着随着温度的升高,反应性——从而裂变链式反应的速率——降低。这意味着反应堆在功率水平方面是自我限制的,并且由于燃料的设计,在没有操作者输入的情况下,在脉冲之后会物理地自我关闭。(所以他们才会让我这样的人来操作!)

脉冲通过气动方式(使用压缩空气)从反应堆堆芯中弹出一根控制棒来工作,这导致功率水平迅速增加。以下情况发生在几毫秒的时间跨度内:

  • 控制棒垂直向上射出堆芯。
  • 功率水平从大约 50 W 增加到高达 2000 MW。
  • 在这种高功率水平下,燃料的即时负反馈效应为堆芯提供了负反应性,从而使堆芯自动关闭。

在这一点上,控制棒可能仍在离开堆芯的途中,但随后会由于重力而回落。你最终得到一个功率响应函数 P(t) ,看起来有点像下面的图 2 功率随着控制棒被弹出而增加,然后随着负反应性的增加而迅速减少。

**图二。**脉冲期间反应堆功率、反应性和能量随时间的变化。来源:m .拉夫尼卡

虽然脉冲有许多研究应用,但它特别适合在非常小的规模上模拟核爆炸发出的辐射。具体来说,我们正在观察中子和伽马射线如何在原子水平上与电子相互作用。假设你正在设计一个新的电子元件,用来控制核武器系统的某个部分。你有多大把握你的电子设备会在附近的核爆炸中幸存下来?集成电路放在放射性弹头旁边储存 10 年会受到什么损害?这种损坏将如何影响组件的功能?或者,如果你正在设计一个用于新型战斗机的 CPU 芯片呢?当暴露在一定量的辐射下时,由你的 CPU 启动的飞行控制系统会失效吗?你可以看到这些问题是多么的重要。结合这一事实,即真正的核爆炸是一个考验,并有一系列问题(更不用说被全面禁止核试验条约禁止),你很快就会同意反应堆脉冲是一项关键能力。

注意:值得说明的是,这个脉冲只产生辐射。在这种类型的实验中,没有东西被爆炸。我们只是将一个样本暴露在一个高度受控的环境中,这个环境是为完成这样的任务而设计的。

操作员可以根据控制棒的反应性“价值”选择脉冲达到的最大功率水平。对于许多核反应堆来说,这种价值是以美元为单位来衡量的,由于解释我们为什么使用这种看似怪异的单位超出了本文的范围,如果你愿意,你可以在这里阅读所有相关内容。

根据即将进行的实验,可能需要特定的脉冲功率水平。控制棒在堆芯中的位置决定了任何给定时间的功率水平。我从自己在这个反应堆的工作中编译的数据集由这些值和一些其他值组成,如下所述:

  • 日期。脉冲发生的特定日期时间格式的日期。
  • 估计反应性估计的脉冲的反应性插入量,单位为美元。这个估计是通过查阅某个控制棒的积分值找到的。例子:一个实验者将要求一个价值 2.00 美元的脉冲。操作员将根据所述控制棒的总积分值找到放置控制棒的位置。
  • 杆位置。堆芯中有四根控制棒—瞬态、垫片 1、垫片 2 和调节棒。在我的数据集中,标记为“跨,S1,S2,注册”,这些值与控制棒在堆芯中的物理位置有关。范围从 0 到 960,其中 0 完全插入内核,960 完全移除。
  • 峰值功率。单位为 MW。
  • **总能量。**以毫瓦秒计量。
  • 峰值温度。以摄氏度测量,这是仪表化燃料元件(IFE)达到的峰值温度。一些燃料棒内部嵌有热电偶,用来监测堆芯的温度。
  • 计算出的反应性。这是“真实的”反应性值,通常与估计的反应性有一定的差异。它是由反应堆控制台自动计算的。

在这篇文章的其余部分,我将分析数据集,看看我能收集到什么见解。此外,我将应用线性回归机器学习模型,根据估算的反应性棒位置,预测计算的反应性 。这个预测模型将帮助我们更准确地使用脉冲功率(在一定程度上——稍后会有更多的想法),并提高我们实验的有效性和准确性。我的最终目标是使我们的脉冲在特定样品的辐射暴露量方面更加准确和可重复。

探索性数据分析(EDA)

我提前清理了数据,所以我就不告诉你细节了。在大多数情况下,清理过程包括从数据集中移除不正确或不完整的条目。我必须识别和清理大量的手工输入错误(打字错误),以及一些丢失的数据点,我要么完全删除,要么用平均值替换。

一旦我有了干净的数据,我要做的第一件事就是导入相关的库并加载我的数据框架:

import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import numpy as npfrom sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn import metrics
from sklearn.metrics import r2_scoredf = pd.read_excel('Pulse_Data_2021_NO_NULL.xlsx', )

之后,我总是喜欢使用三个主要函数来学习我的数据的一般特征— 。头()。info() 和*。描述()。*

。head() 只显示了数据帧的前几行,我们可以看到数据的一般结构:

图 3 。df.head()输出

。info() 输出数据帧中的条目数、列名、每列中的空条目数以及每列的数据类型:

图 4 。df.info()输出

还有*。describe()* 提供数据本身的一些汇总分析—均值、标准差、四分位数等。每列的。为了可读性,我喜欢把它调换一下:

图 5 。df.describe()输出

我的 EDA 的下一步是开始绘制任何可能有意义的东西。例如,我认为观察计算的反应性和峰值功率之间的关系会很有趣,所以我做了一个简单的散点图:

图 6 。计算反应性与峰值功率的散点图。

这两个变量之间存在明显的指数关系,这与驱动该操作的反应器动力学原理一致。该图也很有用,因为它可以识别潜在的异常点——要么是反应器的怪异行为,要么更有可能是不正确的输入。从这个图表中,我发现了其他需要清理或删除的粗手指条目。

关联热图是另一种可应用于大多数数值数据集的有用工具。下面的图 7 描述了哪些列彼此密切相关,值的范围从-1 到 1。

**图七。**关联热图。

要解释此热图,请寻找高值(接近 1)。1 的对角线是热图布局的人工产物,它告诉我每个特性都与自身直接相关。有道理。S1 和 S2 的相关性为 0.99,这也是有道理的,因为这两个控制棒在脉动时几乎总是处于完全相同的垂直位置。我最感兴趣的特性是计算反应性,它与峰值温度、总能量和峰值功率高度相关。这似乎也是合乎逻辑的,因为随着反应性的增加,我预计这些特性也会增加。

由于我试图提高估计反应性值的预测准确性,我的下一步是比较它的分布与计算的反应性。为了做到这一点,我叠加了两个直方图,并做了一些格式化,使它看起来很好:

图 8 。反应性直方图。

当决定我们想要多大的脉冲时,我们通常使用离散的脉冲反应性值。图 8 清楚地反映了这一点,显示$1.50、$2.00、$2.50 和$3.00 是常见的估计值。人们可能会认为,计算出的反应性值在每个估计反应性值周围呈某种正态分布,如蓝色图表所示(尽管不严格)。

再次查看**图 8,**似乎估算的反应性略高于计算的反应性。这意味着,一般来说,如果你请求一个 2.00 美元的脉冲,你实际上得到的值会比这个值少一点。我可以通过简单地减去两列数据并求平均值来量化这一点:

图九。反应性值的平均差异。

这表明,平均而言,“真实”计算的反应性比估计的反应性低 0.16 美元。

最后,我生成了一个图表,作为反应堆利用率随时间变化的有趣代理。该反应堆于 1992 年首次启动(又名“进入临界”),从那时起每年的脉冲数图表提供了一些深入的兔子洞:

**图 10。**反应堆脉冲频率每年一次。

图 10 中可能出现的问题包括:

  • 为什么 1994-1996 年和 2013-2014 年的脉搏如此之少?
  • 是否有管理上的变化影响了在该工厂进行的实验类型?
  • 是否有任何新的国家或大学研究发展需要额外的脉冲?
  • 在 2000 年和 2020-20201 年(现在)发生了什么需要如此多脉冲的实验,为什么在这期间没有这样的实验?

令人惊讶的是,从一些小图表中可以获得如此多的洞察力。

预测建模

预测计算反应性的任务适合(双关语)线性回归模型。这被认为是有监督的机器学习模型,因为数据已经被标记(提供 x 和 y 值来训练模型)。它在技术上是一个多元回归模型(其定义包含线性回归),因为使用多个独立变量( Est_Reactivity,Trans,S1,S2,Reg )来预测因变量( Calc_Reactivity )的结果。

注:这个线性回归模型假设因变量和自变量之间的关系是**线性的。**估计反应性与计算反应性明显呈线性相关,但我们的其他独立变量(反式、S1、S2、注册)却并非如此。独立变量之间也存在明显的多重共线性,因为杆的位置(S1、S2、Reg)高度相关。由于这些原因,线性回归模型可能不是最佳选择。需要进一步探索独立变量才能确定,但多项式回归或岭回归可能是一个不错的选择。底线是理解模型的假设和限制以及它们在不同技术中的变化是很重要的。

我首先将数据分为训练集和测试集,以确保对模型进行公正的评估。然后,我实例化模型并拟合数据:

**图 11。**演示训练测试分割和模型拟合的代码。

一旦模型被拟合,我就使用测试数据将模型输出与期望值进行比较。完全直线表示完美的模型:

**图 12。**展示模型输出与真实数据的散点图。

除了一些显著的异常值,该模型在准确预测方面做得很好。根据模型的用例,可能值得进一步调查数据,以确定这些异常值来自哪里,以及如何减轻它们以提高模型的准确性。在我的情况下,这个模型就足够了。我甚至可以通过计算模型的 R 平方值来量化“拟合优度”:

图 13。 R 平方值为 0.91。

**图 14。**残差图。

r 平方是自变量中可预测的因变量方差的比例。以百分比表示,任何给定脉冲的估计反应性的 91%的变化可以用我们的输入值来解释。要点是,这是一个非常适合我们目的的模型。也就是说,我还可以再做一次检查来了解这个模型。图 14 左边是残差直方图,或任何给定数据点和最佳拟合回归线之间的距离。该图表明,随机误差在零附近呈正态分布,这很好。如果不是这样,那么我们的模型和/或数据集可能有问题。同样也有值得注意的异常值,但当用我们令人印象深刻的 R 平方分数来支持时,没什么可担心的。

**图 15。**慢动作记录的脉冲(最初为 240fps)。来源:我(沃克·佩恩)

结论(和局限性)

有了这个模型,我现在可以根据控制棒位置和棒价值等反应堆参数准确预测脉冲反应性值。可重复性对于所有类型的实验都很重要,这个模型将有助于收紧我们的脉冲值,并确保每个受辐射的组件都有相似的辐射暴露。

注意:该模型确实有一些限制,这些限制对于那些具有反应器动力学和操作知识的人来说可能是显而易见的。也就是说,模型不知道以前的操作和极其重要的裂变产物毒物的积累。随着反应堆的运行,氙-135 随着时间的推移而产生(并被烧掉),并显著影响中子吸收和反应堆行为。

编辑:在脉冲前用全新的数据测试模型后,预测的反应性与反应堆控制台计算的“真实”反应性相差不到 3%。成功!

如果你喜欢这篇文章,请随意看看我的其他文章,并在评论中告诉我你的想法。感谢阅读!

应用机器学习评估佛罗里达气候驱动的房地产风险(上)

原文:https://towardsdatascience.com/applying-machine-learning-to-assess-floridas-climate-driven-real-estate-risk-part1-60e9a8913b85?source=collection_archive---------39-----------------------

图片由作者创作,灵感来源:https://sealevelrise.org/states/florida/:自 1950 年以来,佛罗里达群岛周围的海平面已经上升了 8 英寸。它的上升速度在过去的十年里加快了,现在每三年上升一英寸。科学家们知道这一点,因为海平面每 6 分钟测量一次,使用卫星、沿海浮动浮标和验潮仪等设备来精确测量当地海平面的加速和变化

佛罗里达州短暂的气候变化沙皇,首席恢复官 Julia Nesheiwat,为该州设定了一个明确的优先事项:保护房地产市场。

图片来源: JNW21 885,CC BY-SA 4.0https://creativecommons.org/licenses/by-sa/4.0通过维基共享

Nesheiwat 2020 年 1 月的[未发表的]报告 T11 载有旨在保持佛罗里达州最重要的产业房地产的提案。

她的计划提出了更严格的建筑法规,但也提出了更具争议的措施,如向购房者披露洪水风险,提供国家资助的房屋收购,并要求对城市和县进行脆弱性研究。

“佛罗里达州的沿海社区和地区没有时间可以浪费,需要一个最高级别的合作伙伴来帮助管理和应对迫在眉睫的威胁,”Nesheiwat 写道。

麦肯锡的一项案例研究呼应了 Nesheiwat 对该州大部分地区的可怕预测,包括佛罗里达群岛,但也包括亚特兰蒂斯北部的许多沿海地区。

佛罗里达州维罗海滩的住宅。水下海堤和码头,以及海水浸泡的草坪已经变得司空见惯

“在近三个月的时间里,斯蒂尔赖特角(Florida Keys neighborhood)215 个家庭的居民被迫仔细计划他们的出游,并找到临时的变通办法来处理难闻的死水——这不是因为下雨,而是因为海平面上升——这使他们的红树林街道看起来更像运河。”— 《纽约时报》

麦肯锡报告的作者说,他们估计潮汐洪水频率的预计增加可能导致暴露的房地产在 2030 年贬值 100-300 亿美元,到 2050 年贬值 300-800 亿美元。他们说,到 2050 年,受影响房屋的平均影响预计将增加 15-35%,高于今天的 5%。

“这是一个保守的估计”,麦肯锡全球研究所高级研究员,案例研究的合著者 Mekala Krishnan 说。

根据像麦肯锡这样的报告,普遍的看法似乎是,买家会慢慢地意识到易受洪水影响的房产是糟糕的投资 ,而不是一下子放弃受灾严重的地区,如果飓风在与飓风季节重叠的秋季大潮季节袭击,这很容易发生。

"佛罗里达的抵押贷款和市场会继续运转吗?"— 克里希南说 ,前提是该邦 确实做了Nesheiwat 的气候报告中建议的那种事情。

“如果我们这样做了,我认为风险是可以控制的,”她说。但这并不意味着该州的每一个口袋都会成功。

图片由作者创作,灵感来源:https://sealevelrise.org/states/florida/

数据集和机器学习方法

有许多优秀的媒体文章概述了如何将 ML 应用于房地产估价,因此为了提供原创内容,本文的剩余部分将探索一种新的方法,使用一个前所未见的房地产数据集。这样做的目的是确认或否认南佛罗里达房地产价格已经受到海平面上升(SLR)驱动的气候风险的打击。

“我们的结果表明,佛罗里达州沿海房地产出现了脱节:从 2013 年到 2018 年,受海平面上升(SLR)影响最大的社区的房屋销量相对于受 SLR 影响较小的地区下降了 16%至 20%,尽管它们的销售价格同步增长。然而,在 2018—2020 年间,这些风险市场的相对价格最终比峰值下降了约 5%。贷款人的行为无法调和这些模式,因为我们表明,全现金和抵押贷款融资的购买都出现了类似的收缩,几乎没有证据表明贷款拒绝或证券化有所增加。”—国家经济研究局最近发表报告

我们想要预测的目标是“给定物业的物业价值变化率与附近类似物业的变化率的差异”。理论上,对于具有高度差异的属性,我们应该发现这种差异的很大一部分可以用洪水风险来解释。

为了计算目标值,我们将使用以下等式:

简而言之,我们希望预测每项资产与其相邻资产(可比资产)之间的价格变化率差异。

上述等式实际上可以用以下等式实现:

在数据集中,大约 66%的物业具有较低的洪水风险(在洪水因子评分表上小于 5 分),大约。33%有很高的洪水风险。

我们将通过构建一个 ML 模型,然后对其应用各种模型解释工具(如要素重要性和 SHAP ),来测试高洪水风险分数是财产价值变化率差异的强预测因素这一假设。

使用 EvalML AutoML 库,突出训练有素的 XGBOOST 管道的重要性。作者图片

本博客使用的所有代码和数据都可以在 github 上获得,这里

收集数据

除了标准的房地产功能,如卧室,浴室,面积等。,数据还包括:

  • 每个酒店 3 张图片(也包含在 Github repo 中)
  • 2 份公布的洪水风险评级,一份来自联邦应急管理局,一份来自第一街的洪水评分
  • 截至 2021 年 2 月(上周)的第三周,每处房产均在 realtor.com 挂牌出售,并公布了要价
  • 每处房产都附有最匹配的 Zillow 房屋价值指数 ZHVI(根据邮政编码+卧室数量进行汇总和合并),从而得出附近房屋的月度时间序列指数价格,可追溯到 1996 年
  • 此外,许多其他公共数据集,如相关地区的人口普查数据,都被附加到每个属性中

该数据集包括 1600 多处南佛罗里达房地产的记录,每处都有 650 多列,还包括洪水风险信息、人口统计信息、列表图像、列表描述、房地产上市时间、销售历史以及许多其他相关信息。

要素的推断数据类型

加载属性数据

在建立模型之前,我们需要清理原始数据。因为清理所有东西很费时间,所以我们在这里做一个“快速通过”,但是为了获得更多的训练数据,还会有额外的数据需要清理。请随时这样做,并提供一个链接!

import pandas as pd
import evalml
from evalml.preprocessing import load_dataproperty_data = pd.read_csv('../data/raw/property.csv').reset_index() 

加载人口统计数据

geo codeio提供了一些数据集,我们可以轻松地将其添加到我们的酒店数据中,以供其他研究和使用:

  • The US Census Bureau
  • Local city, county, and state datasets from OpenAddresses
  • OpenStreetMap
  • GeoNames
  • CanVecPlus by Natural Resources Canada
  • StatCan
  • Legislator information from the UnitedStates project on GitHub
demographics_data = pd.read_csv('../data/raw/demographics.csv', low_memory=False).reset_index()

合并数据

数据已经过预排序和重新索引,因此我们可以简单地根据索引 id 进行合并。

merged = pd.merge(property_data, demographics_data, on='index')

注意单词sqft包含在列Area中,因此我们将识别所有包含sqft的行,忽略不包含的行。注意:不包含sqft的行将需要在单独的工作流中进行清理。就目前而言,我们将只关注清理大部分数据,稍后我们可以回来清理那些掉队的数据。

word = 'sqft'
new_df = merged[merged["Area"].str.contains(word) == True]

其他立柱需要类似的清洁,例如BedsFloodInfoYearBuilt

word = 'bed'
new_df = new_df[new_df["Beds"].str.contains(word) == True]word = 'Year Built'
new_df = new_df[new_df["YearBuilt"].str.contains(word) == True]word = 'Flood Factor'
new_df = new_df[new_df["FloodInfo"].str.contains(word) == True]

Style栏完全是一团乱麻,因为许多/大部分列表都不包含这些信息。现在,我们将简单地放弃它,以节省解析混乱的时间。

new_df = new_df.drop('Style',axis=1)

使用特征工具加速数据清理。

Featuretools 库有一些很棒的数据清理工具,我们将使用它们来节省时间。具体来说:

  • remove_low_information_features:仅保留至少有两个唯一值且不全为空的特征
  • remove_highly_null_features:从特征矩阵中删除高于空值阈值的列。
  • remove_single_value_features:删除特征矩阵中所有值都相同的列..
  • remove_highly_correlated_features:删除特征矩阵中与另一列高度相关的列。
from featuretools.selection import remove_low_information_features, remove_highly_null_features, remove_single_value_features, remove_highly_correlated_featuresdf = new_df.copy()"""Select features that have at least 2 unique values and that are not all null"""df_t = remove_low_information_features(df)"""Removes columns from a feature matrix that have higher than a set threshold"""df_t = remove_highly_null_features(df_t)"""Removes columns in feature matrix where all the values are the same."""df_t = remove_single_value_features(df_t)"""Removes columns in feature matrix that are highly correlated with another column."""df_t = remove_highly_correlated_features(df_t)

清理洪水风险数据

Flood Risk Data是这项研究的一个突出特点,所以我们想稍微清理一下格式。

df_t[['FemaInfo','FloodFactorInfo']] = df_t.FloodInfo.str.split(' • ', expand=True) 
df_t['FloodFactorInfo'] = df_t['FloodFactorInfo'].astype(str).str.replace('/10 New','').str.replace('Flood Factor ','')
df_t['FemaInfo'] = df_t['FemaInfo'].astype(str).str.replace('FEMA Zone ','').str.replace('(est.)','')

清理数字特征

我们不能重新格式化像AreaBathsYear BuiltDays on Realtor.com这样的东西,只要它们包含文本字符,所以我们需要删除它们以便正确地格式化数据集来训练我们的模型。

df_t['Beds'] = df_t['Beds'].str.replace('bed','')
df_t['Baths'] = df_t['Baths'].str.replace('bath','')
df_t['Noise'] = df_t['Noise'].str.replace('Noise:','')
df_t['PropertyType'] = df_t['PropertyType'].str.replace('Property Type','')
df_t['DaysOnRealtor'] = df_t['DaysOnRealtor'].str.replace('Days on Realtor.com','').str.replace('Days','')
df_t['Area'] = df_t['Area'].str.replace('sqft','').str.replace(',','')
df_t['Price'] = df_t['Price'].str.replace('$','').str.replace(',','').str.replace(',','')
df_t['PricePerSQFT'] = df_t['PricePerSQFT'].astype(str).str.replace(',','')
df_t['YearBuilt'] = df_t['YearBuilt'].astype(str).str.replace('Year Built','')

拆分LastSoldAmtLastSoldYear功能

这些列一起包含在抓取的数据中,因此我们需要相应地将它们分开,以便正确地将它们格式化为模型特征。

df_t[['LastSoldAmt','LastSoldYear']] = df_t.LastSold.str.split(' in ', expand=True) 

清理LastSoldAmt

LastSoldAmt数据使用文本字符来表示千和百万,然而,为了我们的目的,我们需要用它们的数字亲属来替换它们。

df_t['LastSoldAmt'] = df_t['LastSoldAmt'].astype(str).str.replace('k','000')
df_t['LastSoldAmt'] = df_t['LastSoldAmt'].astype(str).str.replace('M','000000').str.replace('.','').str.replace('Last Sold','').str.replace('$','').str.replace('000000','0000')

删除不必要的列并保存预处理数据

df_t = df_t.drop('LastSold',axis=1)
df_t = df_t.drop('index',axis=1)
df_t = df_t.reset_index()
drop_cols = [col for col in df_t.columns if 'url' in col.lower() or ' id' in col.lower()]
X_t = df_t
X_t = X_t.drop(drop_cols,axis=1)
t_datapath = '../data/processed/preprocessed.csv'
X_t.to_csv(t_datapath,index=False)

下载属性图像供以后使用

数据集中的每个属性都有 3 个包含图片列表的 URL。虽然这些图像并不直接相关,但我们将在以后的博客文章中使用它们。现在,我们将简单地下载它们。

import requestsdef download_images(indx):
    file_name = str(indx)+'.png'
    urldata = df_t[df_t['index']==indx]
    url1 = df_t['Image_URL'].values[0]
    url2 = df_t['Image_URL1'].values[0]
    url3 = df_t['Image_URL2'].values[0]
    urls = [url1,url2,url3]
    ct=0
    for url in urls:
        response = requests.get(url)
        with open('../data/images/_'+str(ct)+'_'+file_name, "wb") as file:
            file.write(response.content)
            file.close()
        ct+=1

df_t['index'].apply(download_images)

合并 Zillow 数据

ZHVI 用户指南

Zillow 最常引用的指标之一是 ZHVI,Zillow 房屋价值指数。它告诉我们在给定的地理区域(大城市,城市,邮政编码等)典型的房屋价值。),现在和将来,针对特定的资产类型和规模。关于 ZHVI 的一般信息,请参考这个方法指南和这个轻松的视频。

我们将合并所创建的键,该键连接了区域邮政编码和zipbeds中的房产卧室数量:

zillow1beds = pd.read_csv('../data/raw/zillow1bed.csv')
zillow1beds['zipbeds'] = zillow1beds['RegionName'].astype(str)+'_'+str(1)zillow2beds = pd.read_csv('../data/raw/zillow2bed.csv')
zillow2beds['zipbeds'] = zillow2beds['RegionName'].astype(str)+'_'+str(2)zillow3beds = pd.read_csv('../data/raw/zillow3bed.csv')
zillow3beds['zipbeds'] = zillow3beds['RegionName'].astype(str)+'_'+str(3)zillow4beds = pd.read_csv('../data/raw/zillow4bed.csv')
zillow4beds['zipbeds'] = zillow4beds['RegionName'].astype(str)+'_'+str(4)zillow5beds = pd.read_csv('../data/raw/zillow5bed.csv')
zillow5beds['zipbeds'] = zillow5beds['RegionName'].astype(str)+'_'+str(5)zillowdata = pd.concat([zillow1beds, zillow2beds, zillow3beds, zillow4beds, zillow5beds])# load preprocessed data
t_datapath = '../data/processed/preprocessed.csv'target = 'Price'#set to None for production / actual use, set lower for testing
n_rows=None#set the index
index='index'X, y = load_data(t_datapath, index=index, target=target, n_rows=n_rows)y = y.reset_index().drop('index',axis=1).reset_index()[target]
X_t = X.reset_index().drop('index',axis=1).reset_index()
X_t[target]=ydf_t['LastSoldDate'] = '1/31/' + df_t['LastSoldYear'].astype(str).str[2:4]
df_t['zipbeds'] = df_t['Zip'].astype(str).str.replace('zip_','')+'_'+df_t['Beds'].astype(str)
zipbeds = list(set(df_t['zipbeds'].values))
zillowdata['zipbeds'] = zillowdata['zipbeds'].astype(str)
df_t['zipbeds'] = df_t['zipbeds'].astype(str)Number of Features
Categorical                  60
Numeric                     640Number of training examples: 1071
Targets
325000     1.49%
450000     1.21%
350000     1.21%
339000     0.93%
349900     0.84%
           ...  
245000     0.09%
5750000    0.09%
74995      0.09%
2379000    0.09%
256000     0.09%
Name: Price, Length: 567, dtype: object Columns (5,9,23,700) have mixed types.Specify dtype option on import or set low_memory=False.df_t = pd.merge(df_t, zillowdata, on='zipbeds')

计算每个属性的变化率及其comparables

在房地产中,comparable是附近具有相似特征的房产,例如相同数量的卧室。在我们的案例中,对于我们在zipbeds合并的每一处房产,我们希望训练一个模型来预测价格变化率与附近可比价格变化率之间的差异。为此,我们将找到目标资产的LastSoldDate列,并查找从该日期到现在相应的 ZHVI 变化率。

X_t = df_t.copy()time_series_cols = [col for col in X_t.columns if '/' in col and 'Percentage' not in col and 'Value' not in col and 'Margin of error' not in col and 'Metro' not in col and col != '1/31/21']l = []
for ct in range(len(X_t)):
    try:
        indx = X_t['index'].values[ct]
        last_sold_date = X_t['LastSoldDate'].values[ct]
        zillow_price = X_t[last_sold_date].values[ct]
        X_ts = X_t[X_t['index']==indx]
        X_ts['zillow_price'] = zillow_price
        X_ts['zillow_price_change'] = X_ts['1/31/21'].astype(float) - X_ts['zillow_price'].astype(float)
        X_ts['zillow_price_change_rate'] = X_ts['zillow_price_change'].astype(float) / float(2021.0 - X_ts['LastSoldYear'].astype(float))
        X_ts['zillow_price_change_percent'] = X_ts['zillow_price_change'].astype(float) / X_ts['zillow_price'].astype(float)
        l.append(X_ts)
    except: pass

df = pd.concat(l)df['last_sold_price_change'] = df['Price'].astype(float) - df['LastSoldAmt'].astype(float)
df['last_sold_price_change_percent'] = (df['Price'].astype(float) - df['LastSoldAmt'].astype(float)) / df['LastSoldAmt'].astype(float)
df['last_sold_price_change_rate'] = df['last_sold_price_change'].astype(float) / float(2021.0 - X_ts['LastSoldYear'].astype(float))
df['yearly_price_delta'] = df['last_sold_price_change_rate'].astype(float) - df['zillow_price_change_rate'].astype(float)

定义我们将训练模型预测的目标变量。

对于我们最初的博客条目,我们将使用特性yearly_price_delta_percent作为我们的目标变量。其定义如下:

df['yearly_price_delta_percent'] = df['last_sold_price_change_percent'].astype(float) -  df['zillow_price_change_percent'].astype(float)

最终清理和保存

在一些数字列中还有一些零散的文本字符,所以请删除它们,然后保存。

df = df.drop(time_series_cols, axis=1)
df =df[df['LastSoldAmt'] != 'Property TypeTownhome']
df = df[df['LastSoldAmt'] != 'Property TypeSingle Family Home']
df.to_csv('../data/processed/zillow_merged.csv',index=False)

使用 AutoML 进行基准测试

虽然原始数据集要大得多,但出于本文的目的,我们将重点关注较小的要素样本。

作者图片

在深入研究 ColorFrame 之前,有必要使用传统的表格建模算法(如 XGBOOST)获得目标数据可预测性的基线,以便我们可以比较 color frame 的性能。但是我们的目标特征是什么呢?

记住,为了计算目标值,我们使用以下等式:

简而言之,我们希望预测每项资产与其相邻资产(可比资产)之间的价格变化率。

上述等式实际上可以用以下等式实现:

国家经济研究局最近发表了一份报告,我们将用它作为规划我们的方法的起点和参考。可以肯定的是,这里的目的是从一个非常普遍的意义上看他们做了什么,而不是重复他们的研究。我们的研究将使用完全不同的方法和数据。**

佛罗里达邮政编码。图片由作者创作,灵感来自来源

假设

我们的假设是,如果确实存在与高洪水风险相关联的强大/重要的价格影响,那么公布的洪水风险评级/分数,例如第一街的洪水因素,应该是高洪水风险房产的最重要的预测特征之一。

为了测试这个假设,我们开始使用 EvalML 的 AutoML 特性构建一个基本的管道。

通过 EvalML 进行 AutoML。作者图片

特征重要性

*best_pipeline.feature_importance*

功能重要性。作者图片

我们可以获得与最终管道的每个特征相关联的重要性。

很容易看出,正如所料,第一街的洪水因子分数实际上显示在最重要的要素顶部附近!哇!

我们还可以计算和绘制管线的排列重要性。

*from evalml.model_understanding.graphs import graph_permutation_importance
graph_permutation_importance(best_pipeline, X_holdout, y_holdout, 'MAE')*

排列重要性。作者图片

解释排列的重要性

“排列特征重要性是一种模型检验技术,当数据为表格形式时,可用于任何符合的 估计器。这对于非线性或不透明的估计器尤其有用。置换特征重要性被定义为当单个特征值被随机打乱 1 时模型得分的减少。该过程打破了特征和目标之间的关系,因此模型分数的下降指示了模型对特征的依赖程度。这种技术受益于模型不可知,并且可以用特征的不同排列进行多次计算。”— sklearn

在解释排列的重要性时,顶部的值被认为是最重要的特征,底部的值最不重要,这些值表明每个特征的随机排列会降低多少模型性能(在这种情况下,使用“平均绝对误差”作为性能度量)。

在上图中,我们立即看到了 FloodFactorInfo 特性的负值。虽然这可能意味着我们在上面看到的高特征重要性只不过是随机的,但重要的是要注意排列重要性非常容易受到特征相关性的影响。

为了解决相关性干扰的可能性,让我们用布尔类型的FloodFactorHigh=True/False替换FloodFactorInfo_flood_factor_high/low特性。让我们也放弃FemaInfo功能,因为它可能会干扰FloodFactorHigh。然后,让我们进行相关性研究,以消除任何其他潜在的冲突。

使用木工数据表的 mutual_information()工具进行特征关联研究。作者图片

在上面的相关性表中,我们可以看到一些要素的相关性超过 50%,因此,要深入了解排列图中发生的情况,让我们尝试移除 City_x 和 County,这应该会移除所有大于 50%的相关性。

移除 City_x 和 County 要素后,修改了要素的重要性,以降低要素之间的相关性。作者图片

我们可以在修改后的特征重要性图表中看到,FloodFactorHigh成为 XGBOOST 模型中最重要的特征,但是排列重要性呢?

移除 City_x 和 County 要素后,修改了排列重要性,以降低要素之间的相关性。作者图片

在修改后的排列图中,我们可以看到虽然FloodFactorHigh仍然接近底部,但它不再是负的,这比之前的情况有所改善。现在,最重要的排列是Area后接Longitude.,这个结果看起来至少是合理的,但就个人偏好而言,我倾向于避免过度依赖排列,因为它对特征相关性非常敏感,在处理房地产特征时,这在很大程度上是不可避免的,尤其是在使用较小的数据样本时。

让我们转向 SHAP 图书馆,看看我们是否能获得关于我们的模型及其特性重要性的任何其他见解…

带有 TreeExplainer 的树集成示例(XGBoost/light GBM/CatBoost/scikit-learn/py spark 模型)

图片来源: SHAP

SHAP 可以解释任何机器学习模型的输出,它的超快速 C++实现支持 XGBoost,LightGBM,CatBoost,scikit-learn 和 pyspark tree 模型,所以让我们试试吧:

*import xgboost
import shap# load JS visualization code to notebook
shap.initjs()# train XGBoost model
model = xgboost.train(best_pipeline.parameters['XGBoost Regressor'], xgboost.DMatrix(X_ttrain, label=y_train.to_series()))# explain the model's predictions using SHAP
# (same syntax works for LightGBM, CatBoost, scikit-learn and spark models)
explainer = shap.TreeExplainer(model)
shap_values = explainer.shap_values(X_ttest)*

现在我们已经创建了解释器对象,让我们制作一些可解释的情节…

*# visualize the first prediction's explanation (use matplotlib=True to avoid Javascript)
shap.force_plot(explainer.expected_value, shap_values[0:1], X_ttest.iloc[0:1])*

SHAP 情节。作者图片

上面的解释显示了将模型输出从基础值(我们传递的训练数据集的平均模型输出)推送到模型输出的每个功能。将预测推高的特征显示为红色,将预测推低的特征显示为蓝色(关于力图的细节可以在这里找到:自然 BME 论文)。

*# visualize the test set predictions
shap.force_plot(explainer.expected_value, shap_values, X_ttest)*

SHAP 情节。作者图片

为了了解哪些特征对模型最重要,我们可以绘制每个样本的每个特征的 SHAP 值。下图按所有样本的 SHAP 量值总和对要素进行排序,并使用 SHAP 值显示每个要素对模型输出的影响分布。颜色代表特征值(红色高,蓝色低)。例如,这表明高FloodFactorInfo_flood_factor_high会增加目标房屋和 Zillow 房屋价值指数之间的预测价格差异。

*# summarize the effects of all the features
shap.summary_plot(shap_values, X_ttest)*

SHAP 情节。作者图片

要了解单个要素如何影响模型的输出,我们可以绘制该要素的 SHAP 值与数据集中所有示例的要素值的关系。由于 SHAP 值表示要素对模型输出变化的责任,因此下图表示随着DaysOnRealtor.com的变化,预测房价的变化。单一值DaysOnRealtor.com的垂直离差表示与其他特征的相互作用效应,在本例中为FloodFactorInfo_flood_factor_high

*shap.dependence_plot("DaysOnRealtor.com", shap_values, X_ttest, 
                     interaction_index="FloodFactorInfo_flood_factor_high"
                    )*

SHAP 情节。作者图片

关键要点

  1. ****flood factor info功能是预测给定资产与其可比资产之间价格变化率差异的最重要功能之一。这种重要性意味着佛罗里达房地产价格已经受到气候变化风险的影响。
  2. ****因为用于计算 FloodFactorInfo 特征的方法特定于每个物业,而 FEMA 洪水等级则不是,因此 FEMA 洪水等级的预测性较低是有道理的。请注意,联邦应急管理局计划大约在 2014 年发布第一个变更。50 年后的 2021 年秋天,对未来有什么想法?
  3. ****FloodFactorInfo 特征是否有其他可能的关联,可以间接解释这些结果,比如靠近海洋或运河?因为我们已经包含了纬度+经度和其他位置感知功能,所以我们认为这些功能几乎同样重要,如果是这样的话。

最后的想法

更多使用该数据集的相关帖子即将发布!留意使用 彩色框架 的额外分析,这是一种受 SuperTML……启发的新颖的计算机视觉方法

房地产投资者一定要注意这一点和相关的研究结果。在大多数情况下,投资者会根据当地的发展趋势来考虑位置、当前资产估值和潜在上涨空间等因素。但是,越来越多的精明投资者开始分析环境事件和影响,这些类型的考虑正在迅速正式成为标准,如由气候相关财务信息披露(TCFD)任务组(T8)提出的标准。重要的是要意识到这些变化和事件对房地产投资的影响。

海平面上升和野火等气候风险对住宅和商业建筑构成重大威胁,并对房地产估价产生负面影响。除了造成财产损失,气候事件还会导致昂贵的保险、维护和运营成本。在最坏的情况下,自然灾害可能导致完全的财产损失和破产的保险供应商。令人欣慰的是,在整个房地产投资行业,围绕气候风险的意识似乎有所增强。

识别气候风险的最佳实践

如果还没有,投资者和投资经理需要实施切实可行的策略来应对气候风险的潜在影响。当考虑在投资组合中增加新资产时,谨慎的做法是探索任何形式的气候风险敞口。积极主动的投资者已经在分析堆积如山的气候数据和其他可用信息,以做出最明智的决策,他们将成为明天的赢家。这里有一些要点可以帮助指导房地产投资决策:

将评估气候风险的流程正式化,作为您更广泛的投资决策的一部分。

自学缓解和适应策略(更高的海堤、暖通空调、更高的海拔等)。).

联系您的保险提供商,了解他们的未来计划。

旨在应对气候风险的研究法规。

气候风险不应该让房地产投资者感到恐惧,但在现在和可预见的未来,它们应该是一个考虑因素。

将机器学习应用于井字游戏

原文:https://towardsdatascience.com/applying-machine-learning-to-tic-tac-toe-47d954671c73?source=collection_archive---------20-----------------------

我创建了一个程序,通过一段时间的模拟来学习如何玩井字游戏

图片由作者提供

简介:

在制作了一个可以玩 Connect 4 的 MATLAB 程序后,我决定下一步是设计一个可以学习如何玩井字游戏的程序。毕竟它只是 Connect 4 的简化版,所以机器学习的时间应该会更少,并增加它的胜率。

井字游戏机 V1.0

它始于两个完全随机的井字游戏发生器互相游戏。一个游戏结束后,其中一台机器从游戏中收集所有决策数据,并将其放入数据库。如果这步棋赢了,这步棋得 3 分。如果移动导致损失,它失去 1 点。平局比赛的结果是加 1 分。在 100,000 场游戏中,每一个独特的场景都记录在它的数据库中,以及它在那之后选择的移动。在下一局游戏中,得分较高的棋更有可能被选中,而得分较低的棋则不太可能被选中。

这个想法是,在几千场比赛中,赢或平的棋会得到更多的分数,更有可能被选中。由此产生了一个包含 2000 多种独特游戏场景的数据库,每一步都有不同的权重。下面是 100,000 场比赛中数据库中独特场景的总量图。

图片由作者提供

正如你所看到的,数量收敛于值 2,423,在这一点上,机器开始看到重复出现的场景,并为 9 个可能的移动中的每一个调整权重。

但是在 10 万场比赛中,TTT-M1 的胜率如何呢?下面左边是所有模拟的总胜率,右边是前几千个游戏的放大部分。

图片由作者提供

由于 TTT-M1 和它的随机移动对手都是在对游戏一无所知的情况下开始的,所以在前 2000 场游戏中,胜率波动很大。到了某个时候,它开始下沉,形成一条平滑的线,随着时间的推移慢慢增加。这是因为机器现在已经看到了所有可能的场景,并且现在正在细化每个移动的权重。

正是在这一点上,我认为我有一个完整的项目。任何人都不可能打败一台有 10 万次井字游戏经验的机器。嗯,我想错了。

事实证明,虽然 100,000 场游戏很多,但当它与随机移动生成器对战时,它真的没有任何意义。一个真实的人是有直觉的,这是我的机器的对手没有的。因此,击败这台机器是相当容易的,或者至少打平它。这并不意味着机器在阻挡它的对手时没有学到一些技术。它肯定做出了明智的决定,只是没有考虑到一个具有良好逻辑的人类对手。

井字游戏机 V2.0

解决这个问题的方法很简单。让另一台机器在没有任何游戏逻辑的情况下启动,并让它面对我的井字游戏机器 1.0 版(TTT-M1)。使用与以前相同的代码,这台新机器(TTT-M2)将学习超过 150,000 场如何玩井字游戏,但它将学习如何对抗具有相当技能的对手。理论上,TTT-M2 应该比 TTT-M1 更聪明,因为它有成千上万场比赛来暴露它的弱点,同时还能制定新的策略。

我感兴趣的一个特殊情况是第一步决策。TTT-M1 率先行动,我预计它会集中在中间位置,这是最好的。事实上,它更喜欢角落,因为它是起点。这给第二个玩家留下了空档,导致 TTT-M1 输掉了很多对真人的比赛。我想看看 TTT-M2 是否会抓住这个弱点,如果轮到它了,就开始瞄准中间的位置。

以下是 TTT-M2 对 TTT-M1 的学习率:

图片由作者提供

正如你所看到的,胜率开始时非常低,因为它面对的是一个有一些游戏知识的对手。随着模拟的进行,它了解了 TTT-M1 的弱点,直到它收敛到 0.50 左右的胜率。考虑到它没有第一步棋,这是非常好的。

它还了解到,如果 TTT-M1 在第一步没有采取中间路线,那么它应该采取它。这证明了一个众所周知的事实,即在井字游戏中,中间点是最好的位置。

现在我有了一个像样的对手,我开发了 TTT-M3。

井字游戏机 3.0 版

我的井字游戏机器学习项目的最终演变是 TTT-M3。这台机器在 150,000 场比赛中对阵 TTT-M2,学习最佳策略,同时也在最佳数据库中找到当前的漏洞。经过 150,000 场比赛,结果如下:

图片由作者提供

这说明这个井字游戏机器学习项目的最后一代一开始非常弱。在最初的 1000 场比赛中,它的胜率徘徊在 25%左右,但后来开始腾飞,达到了 74%左右。这是对一个同样有井字游戏逻辑的对手(TTT-M2)。下面是我和 TTT-M3 博弈的一个例子(“2”是 TTT-M3 的棋,“1”是我的棋):

图片由作者提供

我马上注意到它已经发展出一种策略来引诱我选择中心点。一旦我这样做了,每次都是平局,在计算机看来,这是一个好结果。

图片由作者提供

我扮演了中间的角色,落入了圈套。然后,它设置我采取左上角。

图片由作者提供

在顶上挡住它的水平尝试后,它又把我放在最右边的一栏。

图片由作者提供

这是第二排第一列的下一步棋,决定了这场比赛的命运。

图片由作者提供

结论:

经过 3 代的进化,这个程序已经发展出一种策略来建立联系,阻止对手,并争取胜利。总的来说,这个项目是成功的,因为创建了一个程序,可以学习新的技能,并在与其他对手玩井字游戏时变得更好。

如果你对这类内容感兴趣,我有一些基于棋盘游戏和数据科学的文章:

  • 模拟垄断
  • 教机器玩 Connect 4
  • 最佳和最差卡坦板设置

强化学习在自动驾驶汽车中的应用

原文:https://towardsdatascience.com/applying-of-reinforcement-learning-for-self-driving-cars-8fd87b255b81?source=collection_archive---------7-----------------------

马尔可夫决策过程(MDP)和寻找自动驾驶发展的整体政策

Denys Nevozhai 在 Unsplash 上拍摄的照片

自动驾驶汽车的人工智能应用的一种广泛方法是监督学习方法,最重要的是,用于解决感知要求。但是自动驾驶汽车在强化学习(RL)方法中非常类似于机器人和代理。我们能用强化学习方法代替监督学习方法吗?监督方法的缺点是,从数据收集到模型部署,整个人工智能过程都存在人为偏见。

与环境互动是自动驾驶汽车最重要的任务。感知是第一步,目前是基于人工智能的,并应用了监督的方法。在这种方法中,**你需要考虑车辆是在一个开放的情境环境中行驶,你需要用现实世界中所有可能的场景和情景来训练你的模型。**场景和场景的多样性是特斯拉、Waymo、Cruise 必须通过收集越来越多的数据,并根据收集到的数据验证系统运行来解决的主要困难。如何确保自动驾驶汽车已经学习了所有可能的场景,并安全地掌握了每一种情况?

强化学习

强化学习可以解决这个问题。**RL 方法意味着代理收集环境信息,并基于定义的策略从一个状态切换到下一个状态,以最大化回报。**智能体作为自动驾驶汽车的大脑,采取哪些行动?简单来说,加速、减速和转向这三个动作是影响车辆动力学和道路安全的最关键动作。最危险的决定是转向,最不关键的决定是刹车。我们如何在强化学习过程中为作为驾驶员的代理定义一个策略和一个奖励函数?

奖励函数

为了定义奖励函数,我们可以考虑各种方面,例如功耗、所有道路使用者的安全,或者更快到达目的地且仍然安全驾驶的最佳导航方式。我们应该区分安全驾驶等短期奖励和提前到达目的地等长期奖励。一个智能体,在这种情况下,一辆自动驾驶汽车,需要监控环境,并意识到哪个新状态可以获得最大回报,例如加速、减速或转向,以及所有其他相关参数和变量。

社会不接受自动驾驶汽车过于缓慢和保守的驾驶或频繁制动。有两种应对安全关键事件的解决方案。一个是回到旧的安全状态,第二个是寻找新的“安全状态”由于不清楚较早的“安全状态”是否仍然安全,因此找到新的“安全状态”是唯一可靠的解决方案,而较旧的安全状态是所有其他可能情况中的一个解决方案。然而,如果一个代理决定回复到过去的状态,这个代理将需要一个新的奖励评估,而现有的奖励将不再有效。

挑战将是找到与其他代理活动并行执行新的奖励评估的时间间隔。奖励随时间变化,取决于情况和驾驶动态。**一般来说,在道路交通中与其他车辆保持安全距离可以获得较高的奖励值。**这个论证类似于不必要的刹车缩短了跟在自动驾驶汽车后面的汽车的距离。

环境监测

自动驾驶汽车通过车载传感器收集相关的环境信息,如道路或交通标志的类型,或者从其他车辆或基础设施远程接收这些信息。自动驾驶汽车的所有必要信息都可以测量吗?不,不是全部,至少没有驾驶风格等历史数据。如何衡量自动驾驶汽车在混合交通道路或其他自动驾驶车辆上是否与其他驾驶员配合?

在设计任何基于强化学习方法的自动驾驶智能体之前,我们必须回答这些问题。社会不会接受代理人长期不合作,我们需要衡量和控制这种行为。

使用监督学习进行环境监测似乎仍然是正确的解决方案。我们需要通过分离感知部分和决策部分来降低代理决策的复杂性。由环境传感器收集的所有数据都应被标记并与适当的策略相关联,以促进强化学习设计过程。

为代理选择策略

由于许多可能的情况,为作为自动车辆的代理选择优化的策略是复杂的。代理应该根据流量情况解决不同的问题,并根据这种情况找到适当的响应或策略。如果有多个有效策略,代理应灵活地在其他合适的策略之间切换,或者组合多个策略来计算以下可能状态的奖励。

如本论文、**中所述,找到最佳策略的一个可能解决方案是使用有经验的驾驶员的行为和相关分布作为参考,并尝试通过代理再现这种行为。**然而,本文分析了一个特殊的场景,即具有深度强化学习的类人驾驶的“静态避障”,需要讨论如何将其扩展到所有可能的场景。

另一个解决方案是分层方法,正如在这篇论文中所描述的。选择支持的机动动作,例如在车道内行驶,换到右侧车道,换到左侧车道。每个机动都有它的策略和状态,应该单独训练,并且应该有一个主策略来选择正确的机动。该解决方案描述了一个多代理 RL,在任何单个时间点只有一个代理是活动的,我们需要额外的智能在所有代理之间切换。

然而,机动动作的分离并没有解决自动驾驶的复杂性,因为我们还必须证明,所有的机动动作,包括所有的变化,都已经在设计和测试过程中考虑到了,并且机动动作之间的切换也是安全进行的。因此,这种方法不太可能提供一种完全自动驾驶的汽车,在每种情况和场景下进行竞争。

驾驶汽车是一项复杂的任务,需要最低水平的智能来确保安全驾驶,因为我们已经有了关于如何获得驾照的规定。许多参数,如车辆动力学参数如速度、加速度、位置、倾斜度等。以及环境参数影响驾驶策略。一个 操作设计域(奇数) **应该指定影响强化学习方法的参数。**多种驾驶动作只是设计领域中的一个方面,我们需要基于 ODD 考虑所有参数来训练 RL。

https://medium.com/subscribe/@bhbenam

我的推荐页面:https://medium.com/@bhbenam/membership

使用 Python

原文:https://towardsdatascience.com/applying-smote-for-class-imbalance-with-just-a-few-lines-of-code-python-cdf603e58688?source=collection_archive---------3-----------------------

用几行 python 代码实现类平衡

https://unsplash.com/photos/-9eNCP979zY

类别不平衡是一个经常出现的问题,表现在欺诈检测、入侵检测、可疑活动检测等等。在二元分类法的上下文中,出现频率较低的类称为少数类,出现频率较高的类称为多数类。你可以在这里查看我们关于同一主题的视频。

这有什么问题?

大多数机器学习模型实际上被多数类淹没了,因为它期望类在某种程度上是平衡的。这就像要求一个学生同样学好代数和三角学,但只给他 5 个已解决的三角学问题,而给他 1000 个已解决的代数问题。少数阶级的模式被埋没了。这实际上变成了从干草堆里找一根针的问题。

https://unsplash.com/photos/9Mq_Q-4gs-w

评价也是去折腾,我们更关心的是少数民族类召回而不是别的。

根据合意性使用颜色的混淆矩阵

假阳性是可以的,但假阴性是不可接受的。诈骗类作为正类。

本文的目标是实现,理论上的理解可以参考 SMOTE 的详细工作原理这里。

阶层失衡策略(来源:作者)

当然,最好是有更多的数据,但那也太理想了。在基于采样和基于采样的策略中,SMOTE 属于生成合成样本策略。

步骤 1:创建样本数据集

from sklearn.datasets import make_classification
X, y = make_classification(n_classes=2, class_sep=0.5,
weights=[0.05, 0.95], n_informative=2, n_redundant=0, flip_y=0,
n_features=2, n_clusters_per_class=1, n_samples=1000, random_state=10)

make_classification 是一个非常方便的函数,可以为您创建一些实验数据。这里重要的参数是权重,确保 95%来自一个类,5%来自另一个类。

可视化数据(图片来源:作者)

可以理解为红色阶级是多数阶级,蓝色阶级是少数阶级。

步骤 2:创建训练、测试数据集、拟合和评估模型

对基于原始不平衡数据训练的测试集模型的评估(图片来源:作者)

这里的主要问题是,当原始不平衡数据用于训练模型时,少数类的召回率非常低。

步骤 3:用合成样本创建数据集

from imblearn.over_sampling import SMOTE
sm = SMOTE(random_state=42)
X_res, y_res = sm.fit_resample(X_train, y_train)

我们可以用三行代码创建一个平衡的数据集

步骤 4:在修改的数据集上拟合和评估模型

对在修改的平衡数据上训练的测试集模型的评估(图片来源:作者)

我们可以直接看到,召回从. 21 提升到. 84。这就是三行代码的力量和魅力。

SMOTE 的工作方式是选择一对少数类观测值,然后创建一个位于连接这两个观测值的线上的合成点。选择少数点是相当自由的,最终可能会选择异常的少数点。

ADASYN、BorderLine SMOTE、KMeansSMOTE、SVMSMOTE 是选择更好的少数点的一些策略。

尾注:

类不平衡是一个很常见的问题,如果不处理,会对模型性能产生明显的影响。模型性能对于少数民族阶层尤为关键。

在这篇文章中,我们概述了如何用几行代码,可以像一个奇迹。

引用:

[1]https://www.kaggle.com/saptarsi/smote-notebook

[2]https://machine learning mastery . com/tactics-to-combat-unbalanced-classes-in-your-machine-learning-dataset/

[3]https://www.kaggle.com/qianchao/smote-with-imbalance-data

基于 SVM 的主动学习在多类数据集上的应用

原文:https://towardsdatascience.com/applying-svm-based-active-learning-on-multi-class-datasets-ba6aacdb52d1?source=collection_archive---------18-----------------------

实践教程

基于主动学习和半监督学习的多类分类问题标记策略

在新时代,大量的数据被收集和处理,以提取有价值的信息。同样,机器学习模型也在不断改进,新的方法也在不断提供。显然,基于监督学习的方法对于数据驱动的问题会产生更好的准确性。然而,他们不可或缺地需要每个样品的标签。标签信息对于设计更多的实体模型和获得更好的结果至关重要。不幸的是,这些发展中唯一缺乏的是获得正确的标签信息。给数据加标签通常是一个耗时且令人信服的过程。

主动学习背后的想法是,考虑到标记所有样本是很困难的,找到要标记的最有信息量的样本。因此,通过标记整个样本的一个小子集,模型的准确性可能会显著提高。此时,一个简单的问题出现了。如何从整个集合中选择最具信息性的样本?我提到了一种在多类数据集中决定最有信息量的样本的方法。此外,我将提到一个简单的基于半监督的方法来增加样本集中的标签数量。以及如何在同一数据集中同时利用主动学习和半监督学习。

由克里斯汀娜·帕普在 Unsplash 上拍摄的照片

为了揭示最丰富的样本,大多数研究受益于概率方法。在这篇文章中,我使用了基于 SVM 的方法,而不是典型的概率模型。众所周知,支持向量机(SVM)旨在找出 N 维空间中使不同类别点之间的距离最大化的超平面。请看图 1,这是一个只包含两个特征的数据集。SVM 基本上是看着这边的样点来决定上课的。类似地,点到超平面的距离(裕度)是样本属于该类有多强的一种度量。因此,SVM 评论说样品 34 分别属于蓝色和红色类的概率比样品 12 高。

那么,我们如何在主动学习中使用这些信息,换句话说,找出信息量最大的样本?

以简单的方式,靠近超平面的样本比远离超平面的样本更能提供信息。SVM 为离超平面最远的样本做出具有更高置信度的预测。另一方面,越靠近超平面的样本的类别就越不确定。出于这个原因,拥有更接近超平面的样本的基础事实比其他样本更有价值。

图 1 —二维空间中的 SVM 描述(图片由作者提供)

但是,对于多类分类问题,就没那么简单了。我使用https://scikit-learn.org/stable/modules/generated/sklearn.multiclass.OneVsRestClassifier.html多类策略,这是多类分类任务最常用的方法之一。原则上,每个分类器的每个类都与 OvR 中的所有其他类相匹配。因此,与其替代的 OneVsOneClassifier(OvO)相比,它的解释相对容易。

作为 OvR 的结果,您期望每个类都有一个预测。为了更清楚,假设我们有 4 个类;a、B、C 和 d。在这样的例子中,您获得 4 个不同的二元分类器结果,它们是

A vs (B,C,D);

B vs (A,C,D);

C vs (A,B,D);

D vs (A,B,C);

同样,这意味着 4 个不同的距离测量,如果你在 OvR 上应用 SVM。距离的符号简单地说明了等级;例如,如果上述示例中第一个分类器的距离(A vs (B,C,D))为正,则分类器返回 class A,如果为负,则返回 not A。

正如所料,1 个正距离测量值和 3 个负距离测量值的组合是最有把握的预测,这是图 2 中的 Sample_4 。OvR 声明样本属于作为 4 个不同分类器的结果的具有正距离值的类别。另一方面,4 个正的或 4 个负的距离测量是不确定预测的例子。在主动学习中,我们对这些最不自信的样本感兴趣。我没有遇到 Sample_5 对应的情况。最有可能的情况是,您遇到像 Sample_1 这样的场景作为最不自信的样本。在样本 _1* 之后,样本 _2 可以被定义为第二大信息量样本。它也代表了一种歧义情况。预测声明 Sample_2 可能是任何 B、C 或 D 类的成员,但不是 a。*

然后,如果您还有手动标记样品的空间,您可以考虑像 Sample_3 这样的样品。此时,首先计算两个正距离值之差的绝对值。差异越小,样本的预测就越模糊。因此,您可以选择差值较小的样本。我试图在图 2 中总结一个 4 类数据集的 OvR 组合。同样的想法可以扩展到所有多类数据集,只需稍加修改。

图 2-在 4 类数据集上应用 OVR(SVM)-距离值符号的组合(图片由作者提供)

你也可以看看下面的代码片段,它是上述方法的实现。

*# It calculates the difference of distance results of only positive 2 distance value
def posit_diff(a,b,c,d):
    lst = list([a,b,c,d])
    print(lst)
    index_lst = [lst.index(i) for i in lst if i>0]
    print(index_lst)
    if len(index_lst) != 2:
        print('Warning! Expecting only 2 positive distance values')
        return -1
    else:
        return abs(lst[index_lst[0]] - lst[index_lst[1]])MODEL = LinearSVC(penalty='l2',dual=False, multi_class='ovr', class_weight = 'balanced', random_state=1709)
ACTIVE_LEARNING_BATCH_SIZE = 100 # lets say I am looking for 100 most informative samples from the data set 
# FEATURES = [...]def active_learning(df_train_set, df_unlabelled_set):
    """
    Applying active learning to an example of 4 classes classification problem. 
    """
    ovr = OneVsRestClassifier(MODEL)
    ovr.fit(df_train_set[FEATURES], df_train_set['LABEL'])
    pred = ovr.predict(df_unlabelled_set[FEATURES])dec_func = (ovr.decision_function(df_unlabelled_set[FEATURES]))df_dec_func = pd.DataFrame(dec_func, columns = ['1','2','3','4'])
    df_dec_func = pd.concat([df_dec_func, df_unlabelled_set],axis=1)df_dec_func['positives'] = df_dec_func[['1', '2', '3', '4']].gt(0).sum(axis=1)
    df_dec_func['negatives'] = df_dec_func[['1', '2', '3', '4']].lt(0).sum(axis=1)df_dec_func_posit_0 = df_dec_func.loc[df_dec_func['positives']==0] # the most informative ones
    df_dec_func_posit_3 = df_dec_func.loc[df_dec_func['positives']==3] # the second most informative ones
    df_dec_func_posit_2 = df_dec_func.loc[df_dec_func['positives']==2] # the third most informative onesdf_dec_func_posit_2['posit_diff'] = df_dec_func_posit_2[['1','2','3','4']].apply(lambda x: posit_diff(*x), axis=1)
    df_dec_func_posit_2 = df_dec_func_posit_2.sort_values(by=['posit_diff'], ascending = True)
    df_dec_func_posit_2.reset_index(drop=True, inplace=True)
    rest_needed = (ACTIVE_LEARNING_BATCH_SIZE) - (df_dec_func_posit_0.shape[0] + df_dec_func_posit_3.shape[0])

    if rest_needed > 0:
        df_dec_func_posit_2_al = df_dec_func_posit_2.iloc[0:rest_needed,:]
        df_act_learn = pd.concat([df_dec_func_posit_0, df_dec_func_posit_3, df_dec_func_posit_2_al], axis=0)
    else:
        df_act_learn = pd.concat([df_dec_func_posit_0, df_dec_func_posit_3], axis=0)
        df_act_learn = df_act_learn.sort_values(by=['positives'], ascending=True)
        df_act_learn = df_act_learn.iloc[0:ACTIVE_LEARNING_BATCH_SIZE,:]
    df_act_learn.reset_index(drop=True, inplace=True)

    return df_act_learn*

更进一步,聚类可以应用于推断为最有信息的样本的样本集。通过从聚类的质心和边界选择样本(等于类的数量),可以确保最终集合中的变化。

半监督学习

增加数据集中标签数量的另一种方法是以自动方式标记最有把握的样本。换句话说,如果预测样本高于预定的阈值,则可以迭代地标记样本。并且阈值可以在每次迭代中增加。

假设,分类器预测一个样本属于 A 类的概率为 87%,高于阈值 85%。因此,在接下来的步骤中,它可能被评估为 A 类成员。训练集和未标记集在每次迭代结束时被更新。注意,事实上,它可能不是类 A 的成员,这是基于半监督的方法的一个非常严重的缺点。我在下面的代码片段中分享了这种方法的一个简单代码示例:

*MODEL = RandomForestClassifier(max_depth=4, n_estimators=200, class_weight='balanced', random_state=1709)
LIST_LABELLED_SAMPLES = df_train_set.shape[0]
SS_LEARNING_LABELLING_THRESHOLD = 0.80
SS_THRESHOLD_INCREASE = 0.01
MAX_SS_ITERATION = 10
# FEATURES = [...]def ss_iterations(df_train_set, df_unlabelled_set):  
    """
    It covers all steps of semi supervised learning.
    It uses a simple Ranfom Forest to fit df_train_set and predict df_unlabelled_set 
    to determine the most confident samples 
    those are predicted with a higher accuracy than SS_LEARNING_LABELLING_THRESHOLD. 
    """
    pred_threshold = SS_LEARNING_LABELLING_THRESHOLD

    ovr = OneVsRestClassifier(MODEL)
    print('Before iterations, size of labelled and unlabelled data', df_train_set.shape[0], df_unlabelled_set.shape[0])for i in range (0, MAX_SS_ITERATION):ovr.fit(df_train_set[FEATURES], df_train_set['LABEL'])
        preds = ovr.predict_proba(df_unlabelled_set[FEATURES])df_pred = pd.DataFrame({'1': preds[:, 0], '2': preds[:, 1], '3': preds[:, 2], '4': preds[:, 3]})
        df_pred_ss= pd.concat([df_unlabelled_set, df_pred], axis=1)df_pred_ss['MAX_PRED_RATIO'] = df_pred_ss[['1', '2', '3', '4']].max(axis=1)
        df_pred_ss['LABEL'] = df_pred_ss[['1', '2', '3', '4']].idxmax(axis=1)
        df_pred_ss['LABEL'] = df_pred_ss['LABEL'].astype(int)
        df_pred_ss_up = df_pred_ss[df_pred_ss['MAX_PRED_RATIO'] >= pred_threshold]
        print('The number of samples predicted with high confidence level:', df_pred_ss_up.shape)if len(df_pred_ss_up) > 0:# deleting from unlabelled set
            df_unlabelled_set.drop(index=df_pred_ss_up.index.tolist(),inplace=True)
            df_unlabelled_set.reset_index(drop=True,inplace=True)# adding to train set as if they are ground truths
            df_train_set = pd.concat([df_train_set, df_pred_ss_up[df_train_set.columns]],axis=0)
            df_train_set.reset_index(drop=True, inplace=True)print('Threshold ratio', pred_threshold)
            print('Remaining unlabelled data', df_unlabelled_set.shape[0])
            print('Total labelled data', df_train_set.shape[0])
            print('Iteration is completed', i)pred_threshold += SS_THRESHOLD_INCREASE
        else:
            print('No improvement!')
            breakdf_train_set.reset_index(drop=True,inplace=True)
    df_unlabelled_set.reset_index(drop=True,inplace=True)

    return df_train_set, df_unlabelled_set*

标签增强——主动学习 vs 半监督学习

总之,标签增加背后的原因非常简单。实际上,主动学习背后的推理与半监督学习相反。

在主动学习中,我们的目标是找到以低概率预测的信息量最大的样本。在找到它们之后,这些样本被分享给专家进行人工标记。如果您有大量未标记的数据,而标记这些数据的资源有限,那么这是一种非常有效的方法。另一方面,预测概率高(信息量最少)的样本被自动标记,就好像它们是基本事实一样,并被添加到下一次迭代的训练集中。

主动学习和半监督学习都可以顺序地应用于相同的数据集。首先,应用多个半监督迭代,直到不再有概率高于阈值的预测。然后主动学习的单次迭代可以应用于前一步骤的结果,并且推断的信息样本可以与要标记的领域专家共享。可能会重复一定数量的迭代集。

有用的链接

* *

构建实时 ML 系统的方法

原文:https://towardsdatascience.com/approaches-for-building-real-time-ml-systems-79ea0e340269?source=collection_archive---------4-----------------------

来源:https://unsplash.com/photos/p3Pj7jOYvnM

理解大数据

以毫秒为单位响应预测请求

作为 Zynga 的应用数据科学家,我已经开始着手构建和部署数据产品。随着我探索越来越多的机器学习用例,对实时机器学习(ML)系统的需求越来越多,其中系统执行特征工程和模型推理,以在几毫秒内响应预测请求。虽然我以前使用过 AWS SageMaker 等工具进行近实时的模型推理,但我只是最近才探索了为 ML 系统进行动态特征工程的选项。

广告技术是需要实时 ML 来构建在广告市场中表现良好的系统的领域之一。在广告的需求方面,实现 OpenRTB 规范的实时竞价者需要预测哪些广告印象最有可能驱动转换事件。在供应方,广告中介平台需要实时确定广告库存的投标底价,以便优化广告收入。

在实时 ML 部署中,系统会在请求发出后的几毫秒内回复请求。使用实时系统进行预测请求有两种常规工作流程:

  1. Web 请求
  2. 流式工作流

在第一种情况下,需要预测的系统或客户端向一个端点发出 HTTP 请求,该端点使用预测直接响应请求。其他协议,如 gRPC,可以用于这种类型的工作流。

第二个工作流程可以通过多种方式实现。例如,可以向 Kafka 主题发出一个请求,在那里用 Spark 流处理该请求,并将结果发布到一个单独的主题。其他流框架,如 Flink 或 GCP 数据流,可用于近实时响应预测请求。

在过去的一年里,我和 Golang 一起开发了实时 ML 系统。虽然 Python 可以用来实现这些类型的系统,但 Golang 通常能够在固定数量的机器上每秒响应更多的请求。此外,Golang 还有一些优雅的特性,可以在构建实时 ML 系统时使用 NoSQL 数据存储。对于请求量非常大的用例,为了提高效率,我选择了纯 Go 实现。

在这篇文章中,我将讨论构建实时执行特征工程和模型推理的 ML 系统的一些可用选项。我将讨论纯 Go 方法和 Python 混合方法的选择。

特征工程

在应用模型之前,通常需要将预测请求转换成可以传递给 ML 模型的特征向量。例如,对于 OpenRTB bid 请求,在预测转换事件之前,需要将 web 请求中传递的 JSON 转换为特征向量。另一个用例是检索移动游戏的用户简档,并将简档转换为特征向量,以进行终身价值(LTV)预测。在这些用例中,基于请求中的信息实时执行特征工程,或者在移动应用中使用先前存储的关于用户的信息。

特征工程需要进行重大转变,从矢量预先计算的批处理过程转变为针对每个传入请求即时进行特征工程的实时系统。在广告技术中,系统可以接收投标请求,然后在执行特征工程之前用额外的数据点(例如来自数据管理平台(DPM)的信息)来扩充该请求。所有这些都需要以最小的延迟来完成,这限制了可以使用的方法的类型。以下是我探索过的一些方法:

  1. 经常预先计算特征并存储在 NoSQL
  2. 使用 NoSQL 存储和更新特征向量
  3. 使用 NoSQL 存储配置文件,并使用自定义应用程序代码进行翻译
  4. 使用 NoSQL 存储配置文件,并使用 DSL 进行翻译
  5. 为特征工程调用远程端点

这些解决方案大多依赖于使用 NoSQL 数据存储,因为它需要快速响应请求。对于使用哪种工具,需要考虑几个权衡因素,例如要存储的数据点数量、读/写请求以及最大延迟。Redis 是入门时的一个很好的选择,大多数云平台都有托管服务。

预计算功能

这种方法最接近于传统的批处理预测工作流,在这种工作流中,同时为大量用户进行预测,并将结果缓存到应用程序数据库中,如 Redis。这实际上不是实时地执行特征工程,而是可以以这样一种方式来实现,即对于在应用中活跃的用户,特征向量被频繁地更新。例如,您可以使用 Spark 作业每 15 分钟查询一次关系数据库,使用 SQL 将用户活动转换为特征向量,并将结果存储到 NoSQL 商店。对于许多用例来说,从用户在应用程序中执行操作到该活动出现在用于模型推断的特征向量中有 15 分钟的延迟可能不是问题。然而,这对于关系数据库来说是一项繁重的工作,只有在需要创建向量的用户数量相对较少的情况下才推荐使用。这种方法不适用于系统需要在特征生成过程中使用来自预测请求的上下文的问题,并且通常应该仅用作后备。

在 NoSQL 直接存储媒介

使用 NoSQL 实时执行特征工程的另一种方法是提前确定应用所需的特征向量的形状,并在系统接收到新的跟踪事件时实时更新这些值。例如,特征向量可以具有跟踪用户已经进行的会话总数的属性,并且每当用户开始新的会话时更新该值。对于移动应用程序,具有实时回调事件的 MMP 可用于设置这种跟踪事件功能。

对于这种方法,像用户 ID 这样的键被用作 NoSQL 商店的键,值是特征向量,它可以用多种方式实现。在 Redis 中,散列可以用来实时更新计数器值。向量也可以存储为 JSON,这很简单,但通常会占用更多空间。您可以使用特定于编程语言的库进行序列化,比如 Java 的 kryo 。我通常使用的方法是 Google 协议缓冲区,以可移植的方式序列化数据。

这种方法是基本的,这使得它更容易实现,但是它有一些主要的限制。首先,您需要一个固定的特征向量定义,因为向量是在收到新数据时直接更新的,而不是即时聚合数据,我们将在后面的方法中讨论。第二个大的限制是特性必须能够增量更新,而不是在过去的事件上聚集。这意味着可以使用诸如计数器和标志之类的功能,但不可能使用诸如中位数之类的聚合或其他需要历史数据的计算。

配置文件和自定义应用程序代码

为了在可为特征工程执行的操作类型中获得更大的灵活性,一种选择是在 NoSQL 中存储用户简档,并使用存储在简档中的数据来动态执行特征工程以创建矢量。该方法使用与之前相同的过程,其中当接收到新数据时,实时更新 NoSQL 存储中的值,但是该方法不是更新特征向量,而是更新概括用户活动的用户简档。例如,简档可以存储用户在游戏中玩的过去 3 种游戏模式,或者跟踪用户最近使用了哪些加电项目。

当使用这种方法接收到预测请求时,该请求在请求中传递用户 ID,预测端点从 NoSQL 获取配置文件,然后自定义应用程序代码将该配置文件转换为特征向量。在 Golang 中,这可以实现为一个函数,该函数将 protobuf 对象作为输入,并返回一个特征向量([]float64)作为结果。这种方法通常是计算效率最高的方法,但是每次特征向量的定义改变时都需要系统的新部署。这对于只需要为少量模型提供服务并且定义很少改变的系统来说很有效,但是对于为各种模型提供预测的系统来说可能是有问题的。

配置文件和领域特定语言(DSL)

为了消除每次特征向量定义改变时部署系统的需要,我们需要系统更新特征变换的更灵活的方法。一种方法是使用数据驱动或配置驱动的方法,在这种方法中,转换被定义为数据结构或配置文件,可以在每次进行更新时由系统加载。例如,可以将系统设置为接收一个 JSON 文件,该文件定义了 OpenRTB bid 请求的哪些属性和值要进行 1-hot 编码。这使得数据科学家能够在单独的运行时(如 PySpark)中定义模型管道,而模型在 Go 中提供服务。

随着时间的推移,团队可能希望定义更复杂类型的操作来对请求数据和用户配置文件执行操作,以将这些数据点转换为特征向量。针对这个问题,我探索了小型的特定领域语言(DSL ),比如在比较值时允许≥和≤操作符,以及添加操作符来检查列表中的某项。这很快变得复杂起来,并成为系统部署的新驱动力,因为新的操作符是为您的定制 DSL 定义的。我已经开始使用谷歌的通用表达式语言 (CEL)将用户资料翻译成特征向量,而不是为每个新系统从头开始构建。该语言支持各种可以在 protobuf 对象上执行的操作,在输入对象上运行的 CEL 程序的输出是一个特征向量。当使用这种方法时,特征工程翻译被定义为 CEL 程序,它可以作为字符串传递。

特征工程终点

如果延迟不是问题,并且您希望在如何将用户配置文件转换为特征向量方面有很大的灵活性,那么您可以设置一个端点来执行该转换。这可以通过 AWS Lambda 等无服务器功能或部署到 Kubernetes 实例的 docker 映像来实现。这种方法在构建原型时很有用,但是会导致额外的云基础设施,并且具有定制应用程序代码方法的所有缺点。它仍然使用这种方法,但提供了一个选项,当更新特征工程定义时,只需要重新部署无服务器功能,而不是整个系统。

模型推理

一旦有了预测请求的特征向量,就需要在响应实时请求时应用 ML 模型。实现这一功能有多种选择,但是使用 Golang 确实会使事情变得更加复杂。以下是我探索过的方法。

  1. 推理终点
  2. 自定义应用程序代码
  3. 跨运行时算法
  4. 便携式模型格式

我曾经开发过数据产品,其中训练环境是 PySpark,运行时环境是 Golang,这使得使用标准数据科学堆栈(如 sklearn、XGBoost、SparkML 和 TensorFlow)训练的模型变得很棘手。我将介绍一些处理这些限制的方法,但是纯粹的 Go 实现仍然有一定的局限性。

推理终点

支持各种模型推理算法的最简单方法之一是使用该问题的现有解决方案,如 AWS SageMaker、BentoML 或 fast.ai。这些解决方案使用 Python 运行时为模型提供服务,并支持预测模型的标准 ML 工作台。当使用这种方法时,系统首先在当前过程中执行特征生成,调用端点来执行模型推断,然后用推断端点产生的值来响应预测请求。

这种方法适用于大多数用例,但是有一些注意事项。如果系统正在响应大量的请求,那么像 SageMaker 这样的托管解决方案会变得非常昂贵。另一个问题是延迟,调用远程进程会增加延迟,并会影响系统的 SLA,这对广告技术等领域来说是个问题。

自定义应用程序代码

如果系统需要运行的算法相对简单,例如线性模型,那么编写自定义应用程序代码来实现该模型可能是有利的。在 Golang 中,线性回归和逻辑回归相对容易实现,但是您需要定义一种格式来序列化 Python/PySpark to Go 的模型,这取决于您的培训环境。对于更复杂的算法,这种方法可能会很快导致技术债务,并最终导致错误。

这种方法非常有用的地方是当构建具有非常宽的特征向量的系统时,例如成千上万的特征。如果向量非常稀疏,那么在处理这种规模的数据时,可以使用查找表(如哈希映射)来高效地执行模型推断。

跨运行时算法

我经常为 Golang ML 系统使用的一种方法是使用在 Python 和 Golang 中都有实现的算法。LightGBM 是一个很好的 ML 算法的例子,它在 Python 中运行良好,并与叶库相匹配。在 Python 和 Go 中,库与积极支持的实现的交集很小,但有望随着时间的推移而增长。TensorFlow 确实有 Golang API,但目前还不稳定。

便携式模型格式

为了帮助解决只有少数 ML 库在 Golang 中得到适当支持的事实,我探索的另一种方法是可移植的模型格式,如 PMML 和 ONNX。PMML 是使 ML 模型可以跨不同运行时移植的最初提议,但是只有有限的几个关键 ML 框架采用了它。前提是你可以用 Python 训练一个模型,用 PMML 格式保存结果,然后在不同的运行时加载模型,进行模型推断。Golang 实现了用于推理的 PMML,例如 goscore ,但是目前还不支持流行的算法,例如 XGBoost。

ONNX 是使 ML 模型跨运行时可移植的下一代建议。它最初是为深度学习而设计的,但已经扩展到支持经典算法。理论上,您可以使用 SparkML、XGBoost、sklearn、Tensorflow 和其他工具来训练模型,并使用 ONNX 实现在任何运行时运行模型推理。目前的情况是,许多这些框架对导出到 ONNX 的支持有限,Golang 实现的领先者对经典 ML 算法的支持也有限。对 ONNX 导出和运行时的支持有望随着时间的推移而改进,使 ML 系统的纯 Go 实现成为一个可靠的选择。

结论

对于数据产品,从批量转换到实时 ML 预测需要新的方法来执行特征工程和潜在的模型推断。我发现对实时特征工程最有用的方法是在 NoSQL 数据存储中存储一个用户级概要对象,并使用自定义应用程序代码或通用表达式语言对每个请求动态执行特征工程。对于模型推理,我使用了可用于模型训练和模型服务环境的算法和实现。

让我们的团队能够构建能够在数毫秒内响应预测请求的 ML 管道,这开启了各种新的用例。

本·韦伯是 Zynga 应用科学总监。我们正在招聘!

交易数据中的异常检测方法

原文:https://towardsdatascience.com/approaching-anomaly-detection-in-transactional-data-744d132d524e?source=collection_archive---------9-----------------------

交易数据中的异常检测可能很难,但它带来了在大量数据中发现未知的好处,否则这是不可能的。

查尔斯·德鲁维奥在 Unsplash 上拍摄的照片

1 导言

通常,当人们谈论交易数据**时,他们指的是金融交易。**然而,根据维基百科,“交易数据是描述一个事件(作为交易结果的变化)的数据,通常用动词来描述。交易数据总是有一个时间维度,一个数值,并涉及一个或多个对象”。在本文中,我们将使用向服务器发出请求的数据(互联网流量数据)作为示例,但是所考虑的方法可以应用于前述事务性数据定义下的大多数数据集。

异常检测,简而言之,就是在一个生成数据的系统中,找到正常情况下不应该出现的数据点。交易数据中的异常检测有许多应用,下面是几个例子:

  • 金融交易中的欺诈检测。
  • 制造中的故障检测。
  • 计算机网络中的攻击或故障检测(本文涉及的情况)。
  • 预测性维护的建议。
  • 健康状况监控和警报。

2 数据

2.1 数据描述

我们将用作示例的数据如下所示:

具有如下所述的列:

  • 时间戳:事件发生的时间。
  • serverId: 被访问的服务器的唯一标识符。
  • ip :客户端的 IPv4 地址或 IPv6 地址。
  • timeMs :请求和服务器响应之间经过的时间,以毫秒为单位。
  • clientSSLProtocol :用于请求的 SSL 加密协议。
  • clientHTTPMethod : POST,GET,PUT,PATCH,DELETE。
  • clientRequestHTTPProtocol:HTTP 协议的版本,例如 HTTP/1、HTTP/2。
  • clientRequestPath :请求的路径,例如。/v3/article/。
  • userAgent :一个特征字符串,让服务器和网络对等点识别应用程序、操作系统、供应商和/或发出请求的用户代理的版本。

2.2 数据探索

这里我们不是在进行经典的 EDA(探索性数据分析),而是试图从几个给定的例子中理解如何使用数据。

现有功能(列)分为 3 类,并补充了使用它们的方法:

  • 实数(timeMs,…) 。这些可以直接使用(在基于树的模型的情况下)或者在输入模型之前用标准化。
  • 基数低的分类变量(clientSSLProtocol,clientHTTPScheme,clientHTTPMethod,…) 可以编码(one hot,target 等..)或者嵌入式。
  • 具有高基数和文本数据的分类变量(clientRequestPath,userAgent,… )。这些方法比较棘手,一种方法是解析文本并尝试推断基数较低的类别,或者,例如,使用文本嵌入。

2.3 特征工程

2.3.1 时间特征

时间戳可以被编码成如下特征:

  • 一小时中的分钟
  • 一天中的小时
  • 星期几
  • 等等...

为了摆脱隐式排序(即周一和周日之间的距离与其他连续日期之间的距离相同,但如果它们分别编码为 1 和 7,则该属性不成立),通常使用归一化值的正弦和余弦。

2 . 3 . 2 IP 地址的处理

可以使用字典查找将 IP 地址转换为地理位置。这个位置可以被视为一个分类变量。

此外,可以检查 IP 地址是否是众所周知的代理服务器,并将其标记为众所周知的代理服务器(这对于应用 3.1.1 中的规则很有用)

用户代理标志

userAgent 值可以被解析以提取关于特定子字符串存在的信息,如“torrent”、“iphone”、“chrome”等,然后相应的标志“uaTorrent”、“uaIphone”、“uaChrome”等被设置为 1。

然而,解析规则可能需要维护,因为相关的关键字往往会随着时间而变化。

2.3.4 clientRequestPath 标志

用户代理相同,但是搜索类似“登录”、“管理”等模式...

2.4 标签

数据集中的记录没有按照“坏的”和“好的”参与者进行标记,但是我们假设在下一节中数据集可以具有用于监督建模方法的那些标签。

3 建模

在本节中,我们回顾了几种异常流量检测器的建模方法,从最简单到最先进,考虑它们的优缺点。

3.1 检测个别异常

3.1.1 简单规则集

在这个简单的检测器中,一组规则被应用于每个单独的记录,并且在以下情况下发生警报,例如:

  • 时间> X
  • uaTorrent == 1

以及规则的逻辑组合(与、或……)

优点:

  • 很容易推理
  • 易于实现和测试
  • 警报是可以解释的
  • 一个相当复杂的规则集是最快的选择

缺点:

  • 难以维护,尤其是当规则集变得复杂时
  • 不能(快速)适应变化的行为
  • 可能会遗漏不明显的病例,因此召回率可能较低

3.1.2 记录的监督分类

这种方法假设标签对于数据是可用的,并且问题可以被构造成一个二元分类。几乎所有的分类模型都可以在这里应用,将特征(2.1)作为输入向量。这些模型可以是简单的,如逻辑回归,也可以是复杂的,如神经网络,输出一个介于 0 和 1 之间的“异常分数”。然而,树集成在这里似乎是一个最佳选择,它结合了自然的可解释性、良好的预期预测性能、无需显式编码的分类变量支持以及可接受的推理速度。如果数据量很大,后者非常重要。

优点:

  • 分类是一个众所周知的 ML 问题,有许多成功应用的例子
  • (潜在的)高检测性能(也见缺点)
  • 大多数模型都是可以解释的

缺点:

  • 需要标签,检测质量与标签质量一样好
  • 标签很可能是不平衡的(异常流量更少),这需要应用特殊措施,如多数类欠采样、少数类过采样、数据扩充、 SMOTE 等。
  • 需要用于模型(重新)训练、验证和存储的基础设施

3.1.3 无监督异常检测

高质量的标签并不总是可用的,因此无人监管的方法可能会很方便。异常检测方法/模型通常学习数据中最流行的模式,然后预测新示例的“异常性”。

优点:

  • 模型训练不需要标签

缺点:

  • 标签对于模型验证仍然很方便
  • 需要用于模型(重新)训练和存储的基础设施

3.2 使用时态数据(“行为”分析)

使用时间可以使模型不断适应潜在恶意行为者不断变化的行为,但这需要对数据进行更多的预处理。这种预处理将是在滚动时间窗口上的聚合,并且可以用类似这样的伪火花码来完成:

df
 …
 .groupBy(window(col(“timestamp”), 1.minute), col(“ip”))
 .agg(
   count(col(“*”)) as ‘num_reqs,
   avg(col(“timeMs”)) as ‘avgTimeMs,
   skew(col(“timeMs”)) as ‘skewTimeMs,
   count(when(col(“clientRequestPath”).contains(“login”)) as ‘num_logins),
 …
)

**注意:**数据按“ip”分组,但根据使用案例,按国家和/或地区组合(从 IP 地址解码)分组可能更好。

通过这个查询,我们可以将数据转换成一组时间序列,并应用下面描述的特定于时间序列的方法。

3.2.1 对比历史统计

为了增加规则“模型”的适应性,我们可以使用数据的时态特征和简单的规则集来修改规则,如下所示:

currentTimeMs > avgTimeMs + someConstant

其中“avgTimeMs”是在最后 N 个时间戳上度量的连续更新的平均值。

对于分类特征,可以使用比较当前时刻分类变量的分布和来自相同的最后 N 个时间戳的一些聚集分布。

优点:

  • 不需要模型(重新)训练,但仍然提供适应性

缺点:

  • 需要附加的预处理步骤

**注:**其他利弊同简单规则集。

3.2.2 异常检测任务的时间序列预测

该方法的工作方式如下:基于某个时间窗口,模型预测下一个值,并将预测值与实际观察值进行比较。潜在的预测方法可能会有所不同,从更传统的东西,如 SARIMA 到深度学习。概率预测由于不利因素列表中描述的因素而成为首选。

优点:

  • 是一种自我监督的方法,因此不需要标签
  • 当超出正常值的指标被返回为异常时,可以解释多变量预测的情况,可选地,与正常值的距离为异常分数,数据列用于分组依据
  • 例如,在胶子-ts 中提供的概率模型的情况下,不需要设置阈值,只需要设置百分位数,超出百分位数的观察将被视为异常

关于胶子-ts 的概率预测例子

缺点:

  • 需要用于模型(重新)训练、验证和存储的基础设施
  • 推理时间可能很长,尤其是对于概率模型

3.3 解释预测

可解释的预测对于识别检测的根本原因非常重要,这对于调试和调查攻击可能非常有用。所以不推荐像神经网络这种不易解释的模型。

就工具而言,例如 SHAP ,它是从黑盒模型中推断特性重要性的最佳软件包之一。

4 额外资源

这两个存储库是异常检测和时序预测的研究和工具的重要信息源:

https://github.com/yzhao062/anomaly-detection-resources https://github.com/cuge1995/awesome-time-series

5 结论和后续步骤

在本文中,我们回顾并总结了应用于交易数据的异常检测任务的数据预处理、特征工程和建模的几种选择。根据系统要求,选项的数量可以缩小:例如,如果推理时间应该是微秒——深度学习模型最有可能出局。可行的方案应进行离线和在线测试,并根据项目开始时选择的成功标准确定最佳方案。该指标可以是 precision (如果担心误报),或者 recall (如果错过攻击代价高昂),甚至是某种综合指标,可以是这两者与各种指标(如推理时间、训练时间、计算负载、错误成本等)的加权组合

像科学家一样对待数据科学项目

原文:https://towardsdatascience.com/approaching-data-science-projects-like-a-scientist-86dcb9e4d6dc?source=collection_archive---------22-----------------------

入门

在数据科学项目中使用科学方法

萨特西什·桑卡兰在 Unsplash 上的照片

介绍

一个科学家用 的科学方法 进行研究。科学方法一词指的是进行实验的过程,这一过程始于观察,始于提出问题、进行假设、进行实验、观察和分析、得出结论。

使用科学方法的方法不仅适用于科学家进行实验,还可以用于我们的日常生活中解决问题,通过这种方法,我们可以学习如何提出问题、收集信息、基于证据进行分析、使用逻辑得出结论,并在生活中做出更好的决定。

在本文中,我们将探索和学习什么是科学方法,以及如何在数据科学项目中使用科学方法。

什么是科学方法?

当人们开始通过显微镜进行研究时,弗朗西斯·培根爵士首先介绍了这种科学方法。培根为哲学家创造了科学方法,以确保结果的真实性,并主张哲学家在确认真理之前必须怀疑他们的主张。科学方法由一系列步骤组成,首先是观察、提出问题、形成假设、进行实验以检验假设、分析结果,以及确定假设是正确还是不正确。

科学用来获得基于观察的知识的方法,通过实验制定定律和理论、检验理论或假设

—科学方法的定义:摘自牛津参考文献 —

现在,让我们从数据科学的角度更深入地理解“科学方法”的方法。

理解科学方法

科学方法流程(图片由作者提供)

第一步:观察和理解

在开始一个机器学习项目之前,我们首先需要观察、执行一些研究并了解项目所属的业务。在这个阶段,你可能会或可能不会得到一组数据。如果没有提供数据,那么你需要调查类似的案例,进行研究,寻找现有的数据,并进行调查,以获得更好的理解。如果提供了数据,观察、探索并确定哪些是与您的用例相关的信息。

一家电信公司找我们建立一个机器学习模型,来预测和识别在接下来的一个月中可能流失的客户。—这个阶段的观察和理解是什么?

  • 理解用例、业务问题和目标/结果的大背景。
  • 与利益相关者交流,了解更多关于电信行业的信息。
  • 了解以前识别和防止客户流失的方法。
  • 浏览数据以更好地理解可用信息。
  • 观察在数据集中是否可以看到任何模式。

在观察、理解和探索之后,你会开始有问题浮现在你的脑海中,这使我们进入科学方法过程的下一步——从一个问题开始。

第二步:从一个问题开始

在这一阶段,根据前面的观察、理解和探索步骤,提出问题。列出问题是为了更好地理解哪些领域可以用当前的信息集来回答。从 7w 开始提问——“什么”、“为什么”、“如何”、“哪个”、“什么时候”、“谁”或“哪里”。

可能的问题是什么?

  • 流失率是多少?
  • 客户流失前是否有异常行为或迹象?
  • 大多数流失的客户都有类似的移动计划吗?
  • 哪些套餐流失率最低?
  • 哪个客户下个月会离开?

有了初步的问题列表和对用例的更好理解后,问题将被转化为假设。

第三步:提出假设

在机器学习项目中,假设生成就像是通过识别可能影响企业正在解决的问题的因素来进行“有根据的猜测”。开发假设是数据科学项目中的一个重要过程,因为它有助于识别信息/驱动因素,这些信息/驱动因素有助于模型更准确地预测,并了解需要收集哪些额外数据,或者如果在早期阶段没有提供数据,需要收集哪些额外数据。

例如:

  • 搅动的客户通常属于类似的移动计划。
  • 搅动的顾客远离联系较弱的城市。
  • 与在职成年人相比,老年人的流失率往往更高。

作为一名数据科学家,您必须能够进行批判性思考,并具备识别不同因素和生成相关假设的领域专业知识,以便从模型中获得成功的结果。

假设生成后的下一步是执行实验和测试假设。

步骤 4:进行实验来测试假设

在此阶段,您将根据生成的假设收集相关数据,并构建一个探索图表来支持假设,然后在训练机器学习模型时将它们作为因素/驱动因素添加进来。

例如,在制定了假设— 之后,经常翻炒的客户属于一个类似的移动计划 ,然后我们将需要查看数据集,以确定我们的假设是否得到数据的支持。这个过程可以通过将结果制成表格或构建可视化视图来实现。

构建一个图表来调查移动计划的流失率(图片由作者提供)

上图显示,计划 A 中的客户流失率最高,这符合我们的假设。因此,在模型训练数据集中具有每个客户移动计划的信息可以导致更好的预测结果。或者,如果数据不支持我们的假设,那么这表明假设不应该被接受。

对生成的每个假设重复这一步,在将它们作为模型训练的因素/驱动因素添加之前,我们测试和验证假设。

假设检验后的下一步是分析和解释结果,这也意味着评估我们的模型性能。

第五步:分析和解释结果

在此阶段,我们将根据上一步中训练的模型来评估我们的模型性能。机器学习模型通常会经历模型调整的迭代步骤,但拥有一组经过测试并得到数据支持的强大假设将会导致更好的模型性能。模型定型后,将对照验证集和维持集来验证模型性能。(如果你想了解更多关于验证集和维持集的区别——参考本文 )。)

在验证模型性能时,有几个问题需要回答,例如:

  • 该模型在识别流失客户方面有多准确?错误率是多少?
  • 有什么新的假设可以被检验并作为一个特征添加到模型中吗?

有了回答问题陈述的最终模型后,下一步是得出结论,并将结果反馈给涉众。

第六步:沟通

最后一步是能够以可理解的格式传达结果,并提供利益相关者可以采纳的建议*(例如,提供下个月可能流失的客户列表,以及营销团队如何根据这些信息采取行动。)*

以可理解的格式向观众展示你的发现的一个很好的方式是通过交互式图表或演示幻灯片的可视化。

结论

数据科学不仅仅关注不同的机器学习模型(深度学习、神经网络、聚类等。)但也要考虑到科学方法。就像进行科学实验一样,数据科学中的科学方法可以帮助我们避免得出错误的结论,并根据我们的假设建立一个有偏见的模型。随着组织在项目中采用科学方法,他们将能够理解最终导致构建更健壮的模型和做出更好的决策的数据。

感谢您阅读我的文章,如果您喜欢并愿意支持我:

  • 跟我上 🙆🏻
  • 通过我的推荐链接成为 Medium 会员🙋

参考文献&链接:

[1]https://open StAX . org/books/biology/pages/1-1-the-science-of-biology

[2]科学方法。*牛津参考。*2021 年 11 月 1 日检索,来自https://www . Oxford reference . com/view/10.1093/oi/authority . 20110803100447727

[3]https://search business analytics . tech target . com/feature/The-data-science-process-6-key-steps-on-analytics-applications

逼近 KL 散度

原文:https://towardsdatascience.com/approximating-kl-divergence-4151c8c85ddd?source=collection_archive---------26-----------------------

无偏的低方差样本估计

阿图尔·尤奇卡在 Unsplash 上的照片

在本文中,我将讨论 KL 散度的一个样本近似值。我将讨论正向和反向 KL 偏差,所以如果你不熟悉这些概念,在这里查看我关于主题 的文章 可能会有帮助。

如果你想要一些关于如何用代码实现这一点的指导(来自 open ai联合创始人 John Schulman 的评论)!!)查看我的拉动请求 此处 为稳定基线 3。

为什么一开始要近似?

  1. 无解析解:KL 散度的完整形式解析解可能未知。例如,高斯混合分布就是这种情况。
  2. 高计算复杂度:计算完整的 KL 散度经常需要对整个分布空间求和。使用不需要这样做的近似是有用的,因为它可以更快。

近似值的标准

KL 散度是“真实”分布和“预测”分布之间差异的度量。“真实”分布 p(x) 被认为是固定的,而“预测”分布 q(x) 是我们可以控制的。我们将从 q(x) 中抽取样本作为近似函数的输入,使其成为一个随机变量。如果 p(x) 是已知的,您可以从那里取样本,但有时情况并非如此,因此概括起来,我们将坚持使用 q(x)

直觉上,近似应该具有与被近似的原始度量相似的行为。我们可以用两种方法来衡量这种相似性:

  1. 偏差:理想情况下,近似值应该是无偏的;也就是说,近似值的期望值应该等于原始度量。
  2. 方差:方差为 0 的无偏近似值(确定性的)将完全等于原始度量!当然,这是不现实的,但理想情况下,方差应该尽可能低,从而增加获得更接近原始度量的值的可能性。

近似向前 KL 发散

让我们首先提醒自己完整的分析形式:

作者图片:KL 向前发散

首先,让我们考虑一下偏见的情况。我们想要:

作者图片

其中 approxKL 是我们想要得到的近似值。让我们看看下面的解决方案:

作者图片

因此:

作者图片:向前 KL 发散的无偏近似

但是这个方差仍然很高,因为它可以取负值,而实际 KL 散度不能。改善这种情况的一种方法是添加一个期望值为 0 的项,该项与上面的原始近似值负相关。我们提出以下解决方案:

作者图片

这是可行的,因为 p(x) 被假定为有效的概率分布。因此,我们可以将近似值更新为:

作者图片

其中我们可以求解 λ 来找到最小方差。不幸的是,这取决于具体情况,很难分析计算。但是,我们仍然可以通过选择值-1 来找到一个很好的折衷方案。这在任何情况下都导致一个正定的近似!如果我们将比率 p(x)/q(x) 作为自己的变量,并将其与近似值 λ=-1 相对照,我们会得到以下正定图:

作者图片

因此,前向 KL 散度近似的最终形式是:

作者图片:最终缩减方差无偏正向 KL 散度近似值

近似反向 KL 散度

我们可以采取同样的步骤来得到反向 KL 散度和正向 KL 散度的近似值!

让我们提醒自己完整的分析形式:

作者图片:反向 KL 发散

再次,我们需要首先获得一个简单的无偏近似;下面给出了一个解决方案:

作者图片

因此:

作者图片:反向 KL 发散的无偏近似

这遭受与前向 KL 散度的无偏近似完全相同的问题:这可能取负值,这导致大的方差。我们可以用和上次一样的术语来反驳这一点:

作者图片

同样,我们现在可以求解 λ 以找到最小方差,尽管这取决于具体情况。这次一个很好的折中办法是选择 λ=1 ,以 p(x)/q(x) 的比值作为自己的变量,对照这个近似值作图,我们得到如下正定图:

作者图片

因此,反向 KL 散度近似的最终形式是:

作者图片:最终缩减方差无偏反向 KL 散度近似值

下次你试图通过抽样来测量两个分布之间的差异时,请记住这些近似值!

如果您觉得这篇文章有用,请考虑:

  • 跟踪我🙌
  • 订阅我的邮件通知 永不错过上传📧
  • 使用我的媒介 推荐链接 直接支持我并获得无限量的优质文章🤗

推广的方式,我真的希望你觉得这篇文章有趣,让我知道你的想法!!

参考

http://joschu.net/blog/kl-approx.html约翰·舒尔曼的《逼近 KL 散度》

接近 Neo4j 的中间中心性分数——对速度的需求

原文:https://towardsdatascience.com/approximating-neo4js-betweenness-centrality-scores-the-need-for-speed-27371d37a559?source=collection_archive---------54-----------------------

现在,我把我的 96,000 核心超级计算机放在哪里了?

想象一下,有一天早上你醒来,你对自己说:

嗯,如果我可以计算昨天我们加载到 Neo4j 中的 100 个 mio 节点的中间中心性得分,这不是很棒吗,它代表了我们公司的电子邮件流量。天哪,从信息流的角度来看,这肯定有助于我们识别关键员工。
是的,让我们开始工作吧!

你可能会问自己的下一个问题是:“现在,我把我的 96,000 核心超级计算机放在哪里了?”除非你是 Ziyad AlGhamdi(T4)研究团队的一员,否则你会很快记起你所拥有的是 16 个内核。

许多组织经常用 1 亿甚至几十亿个节点来编制图表,这些节点代表产品网络、共同购买、电子邮件流量等。并且在这些图形上执行中心性分析的能力是必不可少的。

然而,对于许多中心性指标,如中间中心性接近中心性得分,精确算法的计算复杂性 非常昂贵
例如,Cohen 等人在*”*计算经典接近中心性中说明,在规模中,使用标准计算资源计算 2400 万个节点的网络上的接近中心性需要惊人的 44222 个小时,或者大约 5 年来计算。

*https://dl.acm.org/doi/abs/10.1145/2660460.2660465

在这篇文章中, Mark Needham 和我将说明如何使用一个 定制机器学习模型 来近似 Neo4j 中大型图的中间中心性分数。

使用 Neo4j

Neo4j 图形数据科学库有不少于 7 个中心性得分,其中有一个重要但昂贵的中间中心性。该算法应用了 Brandes 算法,可以使用以下命令从 GDS 库中调用该算法:

CALL gds.betweenness.stream(
{ nodeProjection: "Node", relationshipProjection: "EDGE" }) 
YIELD nodeId, score 
RETURN nodeId, score 
ORDER BY score DESC

使用这些 Neo4j 中心性算法,正如我们在以前的文章中所阐述的; “启动你的中心性度量引擎:Neo4j vs NetworkX——各种形式的短程加速赛… *”,*仍然是计算中心性得分的最有效方式之一,但正如所强调的,当图的大小增加时,运行时间也以指数方式增加,因此需要速度。

使用机器学习模型

近似中心性度量的另一种方法是使用机器学习方法,其中我们在一个小图上训练一个模型,然后将其应用于更大的图

假设如下:通常,许多组织的数据库中会有一个图,它或者是一个更大的图的样本,或者是这样一个更大的图的代表。

这种设置的一个例子是对等网络;Gnutella。
这些数据集可以在斯坦福 SNAP 存储库中找到,由 9 个已发布的网络组成,节点数量从 6300 个到 63000 个不等。
同样,我们的目标是预测介数中心性,在这个实验中,我们将使用最小的 Gnutella 网络进行训练,一旦我们有了训练好的模型,就可以预测该数据集中更大图形的介数中心性得分。

基本原理是,这种方法不是生成一组随机训练图,而是使用一个与测试图具有相同拓扑属性的真实世界图,并使用留一交叉验证 (LOOCV)作为模型学习映射的重采样方法。

模型架构

我们的首要任务是通过神经网络为处理“准备”图表。为此,我们使用了 NetworkX Python 包,这是一个构建和分析图的有效工具,如提取邻接矩阵等。

一旦我们对图表进行了预处理,我们就可以把它输入到我们的神经网络中。

概括地说,(人工)神经网络由人工神经元组成,这些神经元通过链接连接,而链接又以数字权重为特征。然后,一个神经网络通过反复调整这些权重来学习

我们部署的神经网络是一个简单的多层感知器(MLP),但有一个转折:模型的输入变量是一个嵌入矩阵,我们使用了戴等人建立的 Struc2Vec 节点嵌入技术

在我们的例子中,嵌入矩阵由节点嵌入组成,由向量表示。一旦这样的嵌入矩阵被编译,我们通过两个完全连接的层运行它,并使用 Keras 早期停止方法来控制历元的数量。我们可以这样设想:

图片摘自 Aqib 等人

所有代码都在 TensorFlow 2.2.3 & Keras 中,可以在下面找到:

https://colab.research.google.com/drive/1m3FBa2KgYxmkLo182wNiOzKF0C3Qbki3?usp=sharing

从 Neo4j 加载图形

我们假设图形在 Neo4j 中,并调用 Neo4j Python 驱动程序通过以下命令获取图形:

from neo4j import GraphDatabase 
import networkx as nx driver = GraphDatabase.driver 
("bolt://localhost:7687", auth=("neo4j", "<password>")) query = "MATCH (n)-[r]->(c) RETURN *"results = driver.session().run(query) G = nx.DiGraph() nodes = list(results.graph()._nodes.values()) 
for node in nodes: 
    G.add_node(node.id, labels=node._labels) rels = list(results.graph()._relationships.values()) 
for rel in rels: 
    G.add_edge(rel.start_node.id, rel.end_node.id, key=rel.id,
              type=rel.type) print(nx.info(G))

如代码片段所示:一旦我们在本地机器上加载了图,我们就调用 NetworkX 来构造一个图,从这里我们可以开始我们的工作。

首先,对于 Struc2Vec 方法,我们产生了归一化度中心性值,它与图的邻接矩阵相结合,允许每个节点由一个嵌入向量来表示。也就是说,它需要一个节点的特定信息,它的,以及这个特定节点的邻居的嵌入来构造这样一个向量。我们可以将工作流程可视化如下:

模型工作流程(图片摘自德门多卡等人)

用数学术语来说,该工作流形式化为:

其中 d 是度向量W 是权重矩阵 。然后,最后出来的香肠是一个嵌入矩阵, H ,它将为我们的神经网络提供输入。

至于神经网络,没有什么特别奇特的:我们有一个简单的输入层,接受形状(6301 x 512)的嵌入矩阵,还有一个输出层,中间有两个完全连接的层,它们创建权重矩阵,其中每一层都使用***【Relu】***激活函数。由于它是一个 单变量回归模型 ,我们将一个只有一个神经元的全连接层作为最终层。

训练模特

如前所述,在这个实验中,我们使用了不同的训练协议

我们没有生成旨在捕捉真实世界图的拓扑结构的随机训练图,而是使用一个更小的真实世界图(要么是一个样本,要么是一个简化的表示),并使用留一交叉验证 (LOOCV)方法来训练模型。

LOOCV 方法包括将观察值集合分割成元组,其中单个观察值、 (x1,y1) 用于验证集合,而剩余的观察值 {(x2,y2)…。、(x_n,y_n)} 组成训练集。

然后,将该模型拟合到 n-1 个训练观测值上,并使用其值 x 1 对排除的观测值进行预测 y 。在我们的例子中, x1 表示使用 Struc2Vec 方法获得的嵌入向量

这种方法的好处是它捕获了我们想要预测的图的精确拓扑。一旦我们对模型进行了训练,我们就可以计算任何新的、更大的图上的嵌入矩阵,为此我们想要估计节点的中间中心度

该模型的训练被证明是非常有效的,其中使用 Keras Earlystopping 回调在 30 个时期停止训练,对于 Gnutella 网络中最小的图(具有 6,301 个节点),只需要不到 10 分钟的训练(在 Google Colab 上),并且导致了较低的丢失率,如下图所示。

有多准确?

为了测量预测的归一化介数值(和节点的排序)产生可信的结果,我们使用了肯德尔τ距离度量。此指标比较两个列表的相似程度,1 分表示两个列表完全匹配。

经过 30 个纪元的训练,我们获得了 0.783 的肯德尔 Tau 分数。

考虑到我们训练模型的速度如此之快,这是一个相当高的分数,肯定比范等人通过他们的中间性深度排名器(DrBC)模型获得的结果更好,其中前 10%的 Kendall Tau 的分数范围为 0.64 到 0.77。

近似介数分数

下一步,我们可以使用这个训练好的模型来预测 Gnutella 集合中具有 62,586 个节点的最大图的介数中心性分数。

我们需要做的唯一预处理是用 Struct2Vec 计算嵌入矩阵,它将成为我们神经网络的输入变量。

该工作流只是需要从训练工作流中复制的几个步骤:我们将图加载到模型中,并再次使用 NetworkX 包来构造图,在此基础上 Struc2Vec 函数产生嵌入矩阵。有了我们的训练模型,我们只需要进行几次 TensorFlow 调用就可以得到预测:

#re-loading model:path_model = ‘/content/drive/MyDrive/Colab_files/GN08_model.h5’model = tf.keras.models.load_model(path_model)x_new = mu_all #where mu_all is the embedding matrix computed by 
                Struc2Vecy_pred = model.predict(x_new)

将预测的中间值写回 Neo4j

现在我们已经获得了预测的介数分数,我们可以将它们写回到 Neo4j 中更大的图中。这将允许我们在 Neo4j 中进行各种图形查询,例如,帮助我们识别那些关键员工。为此,我们再次调用 Neo4j Python 驱动程序,并使用我们心爱的熊猫,执行以下 Python 代码:

from neo4j import GraphDatabase 
import pandas as pd 
import os driver = GraphDatabase.driver("bolt://localhost:7687",
                              auth=("neo4j", "<password>")) cwd = os.getcwd() 
file_path = ‘y_pred’ 
save_path = os.path.join(cwd, file_to_save) 
path = os.path.join(cwd, file_path) df = pd.DataFrame(y_pred) 
df.index.names = [‘id’] 
df.columns = [‘Betweenness’] 
df[‘NodeId’] = df.index 
df_list_nodes = df.values.tolist() q =""" 
UNWIND $nodes AS line 
MATCH (f:Node {id:toInteger(line[1])}) 
Set f.Betweenness = line[0] 
Return Count(*)"""with driver.session() as session: 
    result = session.run(q, {“nodes”: df_list_nodes })

结论

在这篇文章中, Mark Needham 和我试图说明,当速度是关键,一个好的近似就足够了,那么相对简单的机器学习模型可以部署为*&play工具,通过使用 Neo4j Python 驱动程序可以扩展当前 Neo4j 图形数据科学功能。

四月版:主题建模的冒险

原文:https://towardsdatascience.com/april-edition-adventures-in-topic-modelling-7ee9081a48a0?source=collection_archive---------23-----------------------

月刊

超越情感分析

照片由苏西·黑兹伍德从派克斯拍摄

计算机非常擅长处理结构化数据,如电子表格和数据库表格。但由于人类大多使用语言和文字交流,这对计算机来说是不幸的。世界上的许多信息都是非结构化的,例如,英语或其他语言的原始文本。如何才能让计算机理解非结构化文本,并从中提取信息?

自然语言处理(NLP)是人工智能的子领域,专注于使计算机能够理解和处理人类语言。如果你是数据科学的新手,你会发现有大量的资料涵盖了各种与 NLP 相关的任务。我见过的最常见的 NLP 博客帖子都与情感分析有关。也就是说,检测一段文本表达的是积极的还是消极的情绪。但是存在更多的 NLP 问题。

我想提请大家注意主题建模,这是 NLP 中的一个领域,我最近开始对它产生了浓厚的兴趣。主题建模使用单词在文档集合中的分布来识别单词出现的潜在模式。输出是一组主题,由根据特定模式在这些文档中同时出现的单词簇组成。

为什么我觉得主题建模很有趣?因为现在比以往任何时候都更重要的是,它不仅关乎我们的感受,还关乎我们所说的内容。结合起来,情感分析和主题建模可以用来执行所谓的基于方面的情感分析,其目标是提取文本中描述的实体和对这些实体表达的情感。

对于企业来说,通过探索客户对您的服务或产品的特定部分的反应而获得的优势可以帮助支持业务用例,包括产品开发和质量控制、通信、客户支持和决策过程。这比仅仅知道你的客户是高兴还是不高兴要多得多的信息,它可以帮助支持你的业务不断发展和改进。

《走向数据科学》的编辑助理劳里·威廉姆斯

与 BERTopic 的交互式主题建模

使用 BERTopic 进行主题建模的深入指南

由马腾·格罗登斯特 — 7 分钟读完

话题造型文章与 NMF

抽取主题是发现文本之间潜在关系的一种很好的无监督数据挖掘技术。

由罗布·萨尔加多 — 12 分钟阅读

潜在狄利克雷分配(LDA)主题建模教程

这是一本实用指南,包含经过实践检验的 Python 代码。找到人们在推特上谈论的内容。

米歇尔·卡纳博士 — 5 分钟阅读

使用 Scikit 的主题建模简介-学习

探索 3 种无监督技术,从文档中提取重要主题

通过 Ng Wai Foong — 10 分钟读取

理解自然语言处理和主题建模第一部分

我们探索如何通过自然语言处理提取主题帮助我们更好地数据科学

由饶彤彤 — 8 分钟读出

使用 PyCaret 在 Power BI 中进行主题建模

在这篇文章中,我们将看到如何使用 PyCaret 在 Power BI 中实现主题建模。

通过 Moez Ali — 7 分钟读取

主题建模:超越令牌输出

关于如何给题目赋予有意义的标题的调查

由劳里·威廉姆斯 — 9 分钟阅读

话题造型与 PLSA

PLSA 或概率潜在语义分析是一种用于在概率框架下对信息建模的技术。

由 Dhruvil Karani — 5 分钟阅读

情感分析:基于方面的观点挖掘

情感分析和主题建模技术的研究。

作者劳里·威廉姆斯 — 8 分钟阅读

使用 NLTK 和 Gensim 在 Python 中进行主题建模

在本帖中,我们将学习如何识别文档中讨论的主题,这称为主题建模。

由苏珊李 — 6 分钟读完

新播客

  • 大卫·罗德曼— 经济史和通往奇点之路
  • 伊森·佩雷斯— 通过辩论让人工智能变得安全
  • 格奥尔格·诺瑟夫— 意识与人工智能
  • 人工智能:人类的终极目标?

我们也感谢最近加入我们的所有伟大的新作家:薇薇安·迪法兰斯科、莫尼卡·因德拉万、瓦盖努尼·穆罕默德、莱恩·萨德勒、肯德里克·吴、索罗什·萨法伊、亚历山德拉·苏利、甘特·拉博德、阿比·塞尼、 https://medium.com/u/6ca0fe37eac1?source=post_page-----7ee9081a48a0-------------------------------- 马哈茂德·哈穆希、阿贾伊·阿鲁纳恰拉姆、马克西姆·齐亚丁诺夫、萨贾德·舒马利、胡安·塞缪尔、塞尔希·波斯皮洛夫、费尔南多·卡里略、扬恩·莫里泽、塞巴斯蒂安·卡里诺、彭艳 https://medium.com/u/90bafa597f29?source=post_page-----7ee9081a48a0-------------------------------- 季米特里斯·戴斯,朱利安·哈茨基,埃文斯·多伊·奥坎西,普拉瓦兰·卡兰吉特,伊克巴尔·阿里,斯蒂芬·赫鲁达-拉斯姆森,迈克·卡萨勒,马哈姆·费萨尔汗, 我们邀请你看看他们的简介,看看他们的工作。

关联规则学习的 Apriori 算法——如何发现事务间的清晰联系

原文:https://towardsdatascience.com/apriori-algorithm-for-association-rule-learning-how-to-find-clear-links-between-transactions-bf7ebc22cf0a?source=collection_archive---------3-----------------------

机器学习

Python 中关系数据库的频繁项集挖掘和关联规则学习的解释和示例

关联规则学习。由 5 个项目(A,B,C,D,E)构成的所有可能的项目集。图片由作者提供。

介绍

你们中的大多数人可能已经熟悉了聚类算法,如 K-Means 、 HAC 或 DBSCAN 。然而,聚类不是发现数据点之间相似性的唯一非监督方式。您还可以使用关联规则学习技术来确定某些数据点(动作)是否更有可能一起出现。

一个简单的例子是超市购物篮分析。如果有人在买碎牛肉,这是否会让他们更有可能也买意大利面?我们可以通过使用 Apriori 算法来回答这类问题。

内容

  • Apriori 算法所属的类别
  • 关联规则学习介绍和 Apriori 算法工作原理的直观解释
  • 使用真实数据的 Apriori 算法的 Python 示例
  • 结论

Apriori 属于哪一类算法?

如前所述,Apriori 是关联规则学习算法的一部分,属于机器学习的无监督分支。

这是因为先验并不要求我们为模型提供一个目标变量。相反,该算法根据我们指定的约束来识别数据点之间的关系。

下图是交互式的,所以请点击不同的类别来放大并展示更多的👇。

机器学习算法分类。由作者创建的互动图表。

如果你喜欢数据科学和机器学习 ,请 订阅 每当我发布一个新故事时,你都会收到一封电子邮件。

关联规则学习和 Apriori 算法

关联规则学习

如简介中所述,关联规则学习是一种基于规则的机器学习方法,用于发现大型数据库中变量之间的有趣关系。我们用一个简单的超市购物篮分析来说明关联规则是如何发现的。

购物者的超市购物清单。图片由作者提供。

假设我们分析上述交易数据,找出经常购买的商品,并确定它们是否经常一起购买。为了帮助我们找到答案,我们将利用以下 4 个指标:

  • 支持
  • 信心
  • 电梯
  • 定罪

支持

我们和算法的第一步是找到经常购买的物品。这是一个基于频率的简单计算:

**Support(A)** = Transactions(A) / Total Transactions

所以在我们的例子中:

**Support(Eggs)** = 3/6 = 1/2 = 0.5
**Support(Bacon)** = 4/6 = 2/3 = 0.667
Support(Apple) = 2/6 = 1/3 = 0.333
...
**Support(Eggs&Bacon)** = 3/6 = 0.5
...

在这里,我们可以通过告诉算法我们想要探索的最小支持级别来设置我们的第一个约束,这在处理大型数据集时非常有用。我们通常希望集中计算资源来搜索经常购买的商品之间的关联,同时忽略不经常购买的商品。

为了我们的例子,让我们将最小支持设置为 0.5 ,这使得我们在这个例子的剩余部分中处理鸡蛋和熏肉。

**重要:**虽然 Support(Eggs)和 Support(Bacon)单独满足我们的最小支持约束,但理解我们还需要它们的组合(Eggs & Bacon)来通过这个约束是至关重要的。否则,我们不会有一个条目配对来创建关联规则。

信心

现在我们已经确定了经常购买的商品,让我们来计算置信度。这将告诉我们(基于我们的数据)有多大的信心,我们可以相信一个项目将被购买,因为另一个项目已经被购买。

**Confidence(A→B)** = Probability(A & B) / Support(A)Note, confidence is the same as what is also known as conditional probability in statistics:
P(B|A) = P(A & B) / P(A) *Please beware of the notation. The above two equeations are equivalent, although the notations are in different order:* ***(A→B)*** *is the same as* ***(B|A).***

因此,让我们为我们的例子计算置信度:

**Confidence(Eggs→Bacon)** = P(Eggs & Bacon) / Support(Eggs) = (3/6) / (3/6) = 1**Confidence(Bacon→Eggs)** = P(Eggs & Bacon) / Support(Bacon) = (3/6) / (2/3) = 3/4 = 0.75

以上告诉我们,无论什么时候买鸡蛋,培根也是 100%的时候买。还有,无论什么时候买培根,75%的时候都是买鸡蛋。

电梯

鉴于不同的商品以不同的频率被购买,我们如何知道鸡蛋和培根真的有很强的关联,我们如何衡量它?你会很高兴听到我们有办法使用 lift 客观地评估这一点。

计算升力的公式有多种表达方式。让我先展示一下公式是什么样子的,然后我会描述一种直观的方式让你思考。

**Lift(A→B)** = Probability(A & B) / (Support(A) * Support(B))You should be able to spot that we can simplify this formula by replacing P(A&B)/Sup(A) with Confidence(A→B). Hence, we have:**Lift(A→B)** = Confidence(A→B) / Support(B)

让我们计算相关物品的升力:

**Lift(Eggs→Bacon)** = Confidence(Eggs→Bacon) / Support(Bacon) = 1 / (2/3) = 1.5**Lift(Bacon→Eggs)** = Confidence(Bacon→Eggs) / Support(Eggs) = (3/4) / (1/2) = 1.5

这两个项目的升力等于 1.5。注意,lift>1 表示两件商品更有可能一起买,lift<1 表示两件商品更有可能分开买。最后,lift=1 意味着这两个项目之间没有关联。

理解这一点的直观方法是首先考虑鸡蛋被购买的概率:P(Eggs)=Support(Eggs)=0.5因为 6 个购物者中有 3 个购买了鸡蛋。

然后想想每当买熏肉时买鸡蛋的概率:P(Eggs|Bacon)=Confidence(Bacon->Eggs)=0.75因为买熏肉的 4 个顾客中,有 3 个也买了鸡蛋。

现在,lift 只是一个简单的度量,它告诉我们在购买培根的情况下,购买鸡蛋的概率是增加了还是减少了。由于在这种情况下购买鸡蛋的概率从 0.5 上升到 0.75,我们看到 1.5 倍的正提升(0.75/0.5=1.5)。这意味着,如果你已经把熏肉放进篮子里,你购买鸡蛋的可能性会增加 1.5 倍(即 50%)。

*看看你的超市附近有没有这两样东西。*😜

定罪

信念是衡量联想的另一种方式,尽管要理解它有点困难。它比较了 A 在没有 B 的情况下出现的概率(如果它们是独立的)和 A 在没有 B 的情况下出现的实际频率,我们先来看看通用公式:

**Conviction(A→B)** = (1 - Support(B)) / (1 - Confidence(A→B))

在我们的示例中,这将是:

**Conviction(Eggs→Bacon)** = (1 - Sup(Bacon) / (1 - Conf(Eggs→Bacon)) = (1 - 2/3) / (1 - 1) = (1/3) / 0 = infinity**Conviction(Bacon→Eggs)** = (1 - Sup(Eggs) / (1 - Conf(Bacon→Eggs)) = (1 - 1/2) / (1 - 3/4) = (1/2) / (1/4) = 2

如您所见,在计算(鸡蛋→培根)的确信度时,我们除以 0,这是因为我们没有一个鸡蛋被购买而没有培根的实例(置信度=100%)。

一般来说,对 A→B 的高信任度和对 B 的低支持度会产生高确信度。

与解除相反,定罪是一种直接的措施。因此,虽然 lift 对于(鸡蛋→培根)和(培根→鸡蛋)来说是相同的,但两者之间的信念是不同的,Conv(鸡蛋→培根)要高得多。因此,你可以使用信念来评估你的项目之间的方向关系。

最后,与 lift 类似,confidence = 1 表示项目没有关联,而 confidence > 1 表示项目之间的关系(值越高,关系越强)。

Apriori 算法

Apriori 是一种非常简单的算法,它执行以下一系列计算:

  1. 计算大小为 1 的项集的支持度。
  2. 应用最小支持阈值并删除不符合阈值的项集。
  3. 移动到大小为 2 的项目集,重复步骤 1 和 2。
  4. 继续相同的过程,直到找不到满足最小阈值的其他项目集。

为了使该过程更加直观,下面的图表说明了该算法的作用:

Apriori 算法的进程树。图片由作者提供。

如您所见,本例中的大多数项目集都被删除了,因为它们没有达到 0.5 的最小支持阈值。

重要的是要认识到,通过设置更低的最小支持阈值,我们将产生更多大小为 2 的项目集。准确地说,在最小支持阈值为 0.3 的情况下,大小为 1 的项集都不会被删除,因此大小为 2 的项集总共有 15 个(5+4+3+2+1=15)。

当我们有一个小的数据集时,这不是一个问题,但是如果你正在处理一个大的数据集,这可能成为一个瓶颈。例如,1,000 个项目可以创建多达 499,500 个项目对。因此,仔细选择你的最低支持阈值。

注意,如果你想要更多的例子,你可以参考封面图片,它显示了所有可能的项目集,这些项目集可以由 5 个单独的项目组成。

使用真实数据的 Apriori 算法的 Python 示例

现在让我们抛开理论,用 Python 对现实生活中的数据进行分析。

设置

我们将使用以下数据和库:

  • 【Kaggle 的购物篮优化数据
  • 关联规则学习的 Apriori 算法
  • 熊猫进行数据操作
  • Matplotlib 用于绘制频率分布图

让我们导入所有的库:

然后我们从 Kaggle 下载Market _ Basket _ optimization . CSV并获取数据:

数据看起来是这样的:

超市购物篮数据的片段。图片由作者提供。

探测

在我们运行关联规则分析之前,让我们先来看看项目的频率分布。

购买超市商品的频率计数片段。图片由作者提供。

我们可以看到,矿泉水(1788 英镑)是这家超市最受欢迎的商品,其次是鸡蛋(1348 英镑)、意大利面(1306 英镑)、薯条(1282 英镑)和巧克力(1230 英镑)。同时,可怜的老芦笋只被买过一次。

此外,值得注意的是,即使是最常购买的商品也只出现在略高于 6%的交易中。当设置最小支持阈值时,我们可以使用该信息作为指导。

让我们也在条形图中显示频率分布。

购买超市商品的频率分布。图片由作者提供。

运行 Apriori 算法

在我们运行算法之前,让我们把数据放入要求的格式中。这将是一个列表列表,其中所有你在熊猫数据帧中看到的“nan”都被删除了。

列表的事务列表的片段。图片由作者提供。

最后,让我们运行 Apriori 算法并保存项目集和关联规则。

该算法发现了 36 个大小为 1 的项目集和 18 个大小为 2 的项目集,它们满足 0.03 的最小支持阈值。这些显示如下。

满足最小支持度阈值约束的所有项目集。图片由作者提供。

现在让我们打印算法找到的关联规则。注意,一些集合在规则生成阶段被排除,因为它们不满足我们指定的最小置信度要求(在本例中,min_confidence=0.2)。

上面的代码打印了满足我们的约束条件的所有关联规则,这些约束条件按最高提升和信念排序:

27 关联规则产生的 Apriori 算法基于我们的约束。图片由作者提供。

正如我们所看到的,真实数据中提升最高的是碎牛肉和意大利面的组合。然而,碎牛肉的购买者更有可能同时购买意大利面条(信心:0.399,确信:1.374),而不是相反(信心:0.225,确信:1.164)。

总而言之,看起来这家超市的游客是意大利肉酱面和矿泉水的忠实粉丝。

结论

Apriori 是一种简单的算法,可以快速学习项目(数据点)之间的关联规则。虽然我已经向您介绍了它在购物篮分析中的应用,但它还有许多其他实际应用,包括生物信息学(蛋白质测序)、医学诊断(症状和疾病之间的关系)或人口普查数据分析。

在大型数据集上使用 Apriori 时需要注意的一点是最小支持阈值的选择。如果不小心的话,可能会因为大量大小为 2 的项集而很快耗尽内存。

我希望您和我一样发现了先验和关联规则学习。感谢您的阅读,如果您有任何问题或建议,请随时联系我们!

干杯!👏
索尔·多比拉斯

如果你已经花光了这个月的学习预算,下次请记得我。 我的个性化链接加入媒介是:

https://solclover.com/membership

您可能感兴趣的相关文章:

《超越销售》中的先验分析

原文:https://towardsdatascience.com/apriori-analysis-in-r-beyond-sells-ffdf56c1d95c?source=collection_archive---------46-----------------------

apriori 算法经常用于所谓的“购物篮分析”中,以确定给定商品是否与其他商品一起被更频繁地购买(就像著名的啤酒和尿布的例子)。由于这是一个相当特殊的用例,我想测试一下我是否能在一个不处理销售的数据集中发现任何模式。我从来自的蘑菇数据集开始,这里的包含了来自 Audubon Society 的北美蘑菇野外指南的蘑菇记录,仅限于蘑菇属和蘑菇属中的 23 种带菌蘑菇。潜在的假设是,我将根据他们的描述重新提取不同的物种。
顺便说一句,有一种蘑菇是众所周知的广泛传播的沙拉/比萨饼/汤/把你的菜放在这里配料:普通的蘑菇双孢蘑菇:

资料来源:达科尼,CC BY-SA 2.5,https://commons.wikimedia.org/w/index.php?curid=671026

首先,我们读入数据文件并从 agaricus-lepiota.name 文件中获取列名。为了更好地呈现表格,我必须翻转它,这样您可以看到列和行交换了,但仍然可以破译所有列名和前几行。数据集包含 22 列和 8124 行。对每个特征中的参数分布执行快速检查,我们检测到列“veil_type”只包含一个值,因此我们可以删除它,因为它不提供任何附加信息来对蘑菇进行分类。

path <- "my path on computer"
df <- read.table(file.path(path, "agaricus-lepiota.data"), sep=",")
names <- c("edible","cap_shape", "cap_surface", "cap_color", "bruises","odor","gill_attachment","gill_spacing", "gill_size","gill_color","stalk_shape","stalk_root","stalksurface_abovering","stalksurface_belowring","stalkcolor_abovering","stalkcolor_belowring","veil_type","veil_color","ring_number","ring_type","sporeprint_color","population","habitat")
colnames(df) <- names
as.data.frame(t(head(df))) #the table is flipped for demonstration purposes#check, if there are columns of constant values
for (i in 1:ncol(df)){
  if(length(table(df[,i], exclude=NULL))<2){
    print(colnames(df)[i])
  }
}
table(df$veil_type, exclude=NULL)
#veil_type has only 1 value
df <- df[ , -which(names(df) %in% c("veil_type"))]

接下来,我们加载库并将数据集转换成 arules 包中实现的 apriori 算法所期望的形式。

library(arules)
for (i in 1:ncol(df)){
  df[,i] <- paste(colnames(df)[i], df[,i], sep=".")
}
## create transactions
fungi <- as(df, "transactions")

为了对算法有一个感觉,我首先试图找到一些与蘑菇的可食用性相关的规则(尽管,正如已经提到的,这不是分析的目标。先验的优势应该是它的特征识别能力,而不考虑目标,因为它通常是普通机器学习分类算法的情况)。该规则是与特征“可食用”相关联的特征的组合,其形式为“{x,y,z} = >可食用”(给定 x,y,z,特征“可食用”跟随其后)。然而,我需要设置一个门槛,来确定我对某个规则有多自信。这可以通过改变不同的标准来实现:
支持度告诉我们,在数据集中观察到特定特征组合的频率,例如,0.1 的支持度对应于 0.1 x 8124 = 812 个样本,这意味着至少 812 个样本符合规则“x & y & z &可食用”。
规则的置信度是指给定其左侧的特征组合的频率:置信度({x,y,z} = >可食用)= x & y & z &可食用/ x & y & z。此关系回答了以下问题:在数据集的所有蘑菇样本中,符合规则“x & y & z”的,有多少也符合“x & y & z 【T24arules 包的实现算法也允许控制规则的最小和最大长度(minlen,maxlen)。让我们来看看:

library(tidyverse)
supps <- c(); confs <- c(); nums <- c()
for (supp in seq(0.3, 0.7, by=0.05)){
  for (conf in seq(0.5, 0.9, by=0.05)){
    #run apriori algorithm looping over different support and confidence levels
    rules <- apriori(fungi,parameter = list(supp = supp, conf = conf, target = "rules", minlen=2,maxlen=22))
    #limit set of rules to ==>"edible"
    rules_sub <- subset(rules, subset = rhs %in% "edible=edible.e")
    #extract results
    supps <- c(supps,supp); confs <- c(confs, conf); nums <- c(nums,rules_sub@lhs@data@Dim[2])
  }
}
#combine results to data frame
results <- data.frame(support=supps,confidence=confs, count=nums)
results %>% arrange(desc(confidence)) %>% head(.)

我们看到,0.4 x 8124 = 3249 个蘑菇样本符合两个规则,其中“可食用”条件的置信度为 0.9。还不错。让我们仔细看看这些:

#repeat modeling with pre-selected thresholds for support and confidence
rules <- apriori(fungi,parameter = list(supp = 0.4, conf = 0.9, target = "rules", minlen=2,maxlen=22))
#limit set of rules to ==>"edible"
rules_sub <- subset(rules, subset = rhs %in% "edible=edible.e")
inspect(rules_sub)

这就是了。apriori 算法已经确定了这样的规则,即如果没有气味,蘑菇是可食用的。这条规则的置信度为 0.97,这意味着 97%的无气味蘑菇是可食用的(在我们的数据集中!).> 1 的 lift (可以解读为气味和可食用性之间的一种相关性)告诉我们,这两个特征之间存在正相关关系。第二条规则更有趣一点。它告诉我们,宽鳃(鳃大小)和环上光滑的柄表面与蘑菇的可食用性有正相关。至少在 94%的宽鳃、光滑柄的伞菌标本中发现了它。
让我们用一个快速而肮脏的随机森林简单地检查一下结果,如果我们得到了鉴别可食用性的相似特征:

library(caret)
table(df$edible)
fit_control <- trainControl(method = "cv",number = 10)
set.seed(123456)
rf_fit <- train(as.factor(edible) ~ .,data = df,method = "rf",trControl = fit_control)
rf_fit
saveRDS(rf_fit, file.path(path, "fungiEdibility_RF_model.rds"))
rf_fit <- readRDS(file.path(path, "fungiEdibility_RF_model.rds"))
varImp(rf_fit)

不错!随机森林还选择气味作为区分食用蘑菇和毒蘑菇的最重要特征。但这不是我们分析的目标。我们仍在寻找数据集内的 23 个不同物种或任何有趣的模式。

因此,我再次运行该算法,现在不是寻找规则,而是寻找频繁的特征组合。我将特性集的最小长度设置为 15。

#run apriori
itemset <- apriori(fungi,parameter = list(supp = 0.1, conf = 0.98,target = "frequent", minlen=15,maxlen=22))
#get top items
top_items <- data.frame(as(items(itemset), "list"))
top_items <- cbind(as.data.frame(t(apply(top_items,2,function(x) sub("[[:alpha:]]+_?[[:alpha:]]+=", "", x)))),itemset@quality)
#spread top items into a tidy dataframe
df2 <- df[0,]
colnames(df2)
for (rownr in 1:nrow(top_items)){
  for (cols in 1:(ncol(top_items)-3)){
        feature <- substring(top_items[rownr,cols], 1, unlist(gregexpr("\\.", top_items[rownr,cols]))-1)
        type <- substring(top_items[rownr,cols], unlist(gregexpr("\\.", top_items[rownr,cols]))+1)
      for (i in 1:ncol(df2)){
        if(feature==colnames(df2)[i]){
          df2[rownr,i] <- type
        } 
    }
  }
}
#exclude columns which have never been selected by apriori 
non_na_cols <- c()
for (k in 1:ncol(df2)){
  if(sum(is.na(df2[,k]))!=nrow(df2)){
    non_na_cols <- c(non_na_cols,colnames(df2)[k])
  }
}
df2[,non_na_cols]

在这里,我再次翻转表格,以便您可以更好地阅读。我还添加了由算法识别的不同特征的选定级别的翻译。

有趣的是,该算法只识别了可食用蘑菇的模式,这可能是因为有毒蘑菇的可变性更高,而不是可食用蘑菇。我们还看到,有一个所有特征集共有的子特征集(可食用&木质&擦伤&无气味&无鳃&鳃间距紧密&鳃宽&茎尖逐渐变细&球茎根&茎表面光滑&白色面纱&一个垂环)。这个典型的特性集让我强烈地想起了按钮蘑菇。让我们看看这个描述在确定物种方面能走多远。网页提供了不同蘑菇种类的检索表。从第一页开始,我们浏览描述。由于我们没有读到任何关于我们的蘑菇生长在其他蘑菇上的东西(而是在树林中独居),问题 1 引出了问题 2,我们肯定地回答了问题 2(我们的蘑菇有鳃)。我们进一步遵循这个关键:3)不像上面那样→ 4)腮不沿着茎向下延伸(我们的腮是自由的)→去腮蘑菇。到目前为止,一切顺利。该链接将我们带到下一个页面。根据检索表,我们得到:1)有面纱,没有鱼腥味→ 2)孢子不是粉红色的→ 3)鳃不是黄色的,没有沿着茎向下延伸→ 4)孢子印痕不是橙色的→ 6)孢子印痕是深色的(棕色,黑色)。我们只在两个特征集中有这个特征,但是我假设如果偏离到淡色蘑菇是重要的,它会被算法拾取。跟着上的键,我们就到了(我在这里省略了键的读数,如果你愿意,你可以从头到尾走一遍):1 → 3 → 12 → 21 → 26 →蘑菇。太好了,算法找到了一些蘑菇的共同特征。我们在网页上读到了一个不太乐观的说法,“你不应该期望能够识别你收集的每一个蘑菇”。事实上,前两个问题已经让我们没有了答案,因为切片时的肉色特征或蒙特利柏树关联在数据集中没有得到解决。我猜奥杜邦协会野外指南的原始答案可能会对这个问题有所启发,然而不幸的是我没有获得这个资源。唯一令人欣慰的是,如果对白色瓶盖的颜色回答“是”,就会出现双孢蘑菇按钮。

那么,从这个玩具分析中,我的收获是什么呢?该算法明确地选择了所有蘑菇属蘑菇共有的特征集。然而,我会猜测,从 8124 个样本/ 23 个物种=每个物种 353 个样本开始,该算法会选择其他物种共有的特征集。事实上,对数据集进行快速主成分分析清楚地表明,可食用(绿色)和有毒(红色)样品中存在明显的聚类,很可能对应于不同的物种。

theme_set(theme_classic())
#encode factors to hot-one, as PCA works on numeric features only
dummy <- dummyVars(" ~ .", data=df, fullRank=T)
df_hotone <- data.frame(predict(dummy, newdata = df))
mypca <- prcomp(df_hotone) ;  myscores <- mypca$x[,1:3] #scores
#add PCA results to table 
df_hotone_PCA <- cbind(df_hotone,myscores)
ggplot(df_hotone_PCA, aes(PC1,PC2))+
  geom_point(aes(color=factor(edibleedible.p, levels=c("1","0"))))+
  theme(legend.position = "none")+
  labs(title="Principal component analysis on mushroom dataset",subtitle = "colored by edibility", caption = "red = poisonous; green = edible")

作者图片

这就结束了这个相当令人失望的分析。我猜想,为了破译对应于数据集不同物种的特征集,需要进一步调整 apriori 算法或转向另一种方法。肯定是有提升空间的,所以还是撸起袖子继续分析吧……另一个故事。