TowardsDataScience-博客中文翻译-2021-七十九-

TowardsDataScience 博客中文翻译 2021(七十九)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

SimRank:从零开始的相似性分析解释和 Python 实现

原文:https://towardsdatascience.com/simrank-similarity-analysis-1d8d5a18766a?source=collection_archive---------19-----------------------

网站的相似性度量

斯凯勒·史密斯在 Unsplash 上拍摄的照片

相似性度量是各种领域都需要解决的问题。SimRank 是一种直观而通用的相似性度量方法。它适用于任何具有对象对对象关系的领域,根据对象与其他对象的关系来度量对象的相似性。

SimRank 的关键是

如果两个对象被相似的对象引用,则认为它们是相似的。

我们将简要介绍该算法,并从头开始演练 Python 实现。

算法

请随意查看评论良好的源代码。这真的有助于理解整个算法。

https://github.com/chonyy/PageRank-HITS-SimRank

算法步骤如下所列

  • 初始化以下每对节点的 SimRank
if(node1 == node2):
  SimRank(node1, node2) = 1
else:
  SimRank(node1, node2) = 0
  • 对于每次迭代,更新图中每对节点的 SimRank
  • 如果两个节点相同,则 SimRank(a,b) = 1
  • 如果其中一个节点没有邻居,则 SimRank(a,b) = 0
  • 否则,新的 SimRank 遵循以下等式

  • 我们根据上一次迭代的 SimRank 计算新的 SimRank(递归定义,但迭代计算)

Python 实现

初始化 SimRank

我们在相似类构造函数中初始化 SimRank。请注意,初始值存储在 old_sim 中。我们保留 new_sim 以保存下一次迭代中更新的 SimRank。

初始化规则就像我们上面提到的一样。如果二者相同,则 SimRank = 1,否则 SimRank = 0。

SimRank 一次迭代

这是 SimRank 的主函数。我们迭代图中的每一对节点,并更新 SimRank。在获得所有新的 SimRank 值之后,我们用当前迭代中的值替换旧值。

计算新的 SimRank

  • 如果两个节点相同,值= 1
  • 如果其中一个节点没有邻居,值= 0
  • SimRank_sum =所有相邻对的 SimRank 值之和(SimRank 值来自前一次迭代)
  • 用衰减因子计算标度
  • 用 SimRank_sum 和 scale 计算新的 SimRank

更新 SimRank

将计算出的新 SimRank 值赋给 new_sim。

替换 SimRank

用当前迭代的值替换上一次迭代的值。

https://gist.github.com/9395c9aea14e4086ce2611809084f9a0

结果分析

让我们在回购中的 数据集 上测试我们的实现。我们在所有结果中设置 decay_factor = 0.9。

graph_1.txt

图片由 Chonyy 提供。

结果

结果遵循节点值的顺序,即行上的1, 2, 3, 4, 5, 6和列上的1, 2, 3, 4, 5, 6。从矩阵中,我们可以看到对角线上总是全是 1。两个相同节点的 SimRank 始终为 1。

回想一下上面解释的 SimRank 方程,在计算中,有没有共同的双亲关系很大。从图中,我们可以看到没有一对节点有共同的父节点。因此,所有的 SimRank 都是 0。

graph_2.txt

图片由 Chonyy 提供。

结果

同样,没有一个节点有共同的父节点。所以 SimRank 都是 0。

graph_3.txt

图片由 Chonyy 提供。

结果

从结果矩阵中,有一个有趣的观察。

SimRank[1][3] = SimRank[3][1]

让我们来看看 SimRank 的配对吧!= 0.(节点 1,节点 3),(节点 2,节点 4)都有一个共同的父节点,这使得它们的 SimRank 不等于零。

graph_4.txt

图片由 Chonyy 提供。

结果

让我们看看 pair(节点 6,节点 4),(节点 7,节点 4)。这两对节点中的公共节点是节点 4。节点 4 有两个父节点,节点 1 和节点 5。

  • 节点 1 是节点 7 的唯一邻居
  • 节点 5 是节点 6 的唯一邻居

可以看到,这种关系使得他们得到了相同的 SimRank = 0.695。

IBM.txt

图片由 Chonyy 提供。

结果

结果遵循节点值顺序,分别为行和列中的2076, 2564, 4785, 5016, 5793, 6338, 6395, 9484, 9994

您可能想知道为什么(node4785,node5016)有一个单个和公共的父节点,但是 SimRank 不是 1。这就是为什么我们需要衰减因子,来区分高度相似和完全相同。

计算性能

趋同;聚集

现在我们都知道,经过足够的迭代,SimRank 将总是收敛到一个特定的值。为什么我们不画出来看看它收敛的有多快?

在 graph_4.txt 上测试收敛性

图片由 Chonyy 提供。

这里有两个重要的观察。第一个是就像我们预期的那样,值从 0 开始,逐渐增加到某个值,停止变化。第二个是曲线比我们从 HITS 或者 PageRank 算法得到的图更平滑。

请注意,完成计算可能不总是只需要这几次迭代。例如,如果我们在 repo 中的 graph_6 上测试这个 SimRank 算法,它具有 1228 个节点和 5220 条边,即使 500 次迭代也不足以使 SimRank 收敛。并且由于大量的边,计算会花费很长时间。

边数

图片由 Chonyy 提供。

我们用不同数量的总边运行 100 次迭代,以便发现总边和计算时间之间的关系。可以看出,边数对计算时间的推断比线性增加得更快。所以边太多的话计算会特别慢。

请注意,它不是一条完美的曲线的原因是边彼此链接的方式也会稍微影响计算时间。

源代码

https://github.com/chonyy/PageRank-HITS-SimRank

相关职位

https://chonyy.medium.com/hits-algorithm-link-analysis-explanation-and-python-implementation-61f0762fd7cf https://chonyy.medium.com/pagerank-3c568a7d2332

使用 SimPy 在 Python 中模拟真实事件

原文:https://towardsdatascience.com/simulate-real-life-events-in-python-using-simpy-e6d9152a102f?source=collection_archive---------5-----------------------

实践教程

模拟一家有饥饿顾客和有限食物供应的餐馆

动机

作为一名餐厅经理,你想估算一下你的餐厅明天需要准备多少食物。你知道每天有多少顾客来你的餐厅,以及为一个顾客服务的平均时间。然而,将所有这些变量放入一个计算中是具有挑战性的。

如果能用 Python 模拟这个事件岂不是很棒?

作者图片

这就是 SimPy 派上用场的时候。

什么是简单?

SimPy 是一个 Python 库,可以让你模拟现实生活中的事件。它可以对客户、车辆或代理等活动组件进行建模。

要安装 SimPy,请键入:

pip install simpy

开始—模拟服务员

在 SimPy 中,客户或车辆等活动组件的行为用流程建模。这些进程存在于环境中。它们通过事件与环境和彼此互动。

为了更好地理解这些概念,我们试着对一个在餐厅(一个环境)服务的服务员(一个流程)进行建模。服务员做的一些基本事情是:

  • 接受顾客的订单
  • 给厨师下命令
  • 向顾客提供食物

作者 GIF

在这里,服务员是一个过程。服务员住在一个env里。当前模拟时间为env.nowenv.timeout(duration)方法模拟完成一项活动所需的时间。

输出:

作者图片

酷!我们能够模拟服务员做的一些基本事情。上面的输出是有意义的,因为它需要:

  • 5s 接受顾客的订单(take_order_duration = 5
  • 给厨师下命令(give_order_duration = 2)
  • 5s 为顾客提供食物(serve_order_duration = 5)

请注意,我们没有看到“31 开始为顾客提供食物”,因为我们只运行了30 s 的模拟。

资源—模拟客户

我们可以使用与上面相同的逻辑来模拟客户。但是,由于餐厅中可以服务的顾客数量有限,我们也将对容量有限的餐厅进行建模。

这可以使用simpy.Resource类来完成。我们将使用simpy.Resource(env, capacity=2)将客户数量限制为 2。这意味着只有当餐厅里的顾客少于两个时,新顾客才能进入餐厅。

作者图片

餐馆 1

现在,我们可以指定每个活动的持续时间,并模拟 5 个客户。请注意,客户的顺序和他们的数量是不相关的。

输出:

作者图片

酷!从输出中,我们可以看到:

  • 新顾客在随机时间到来
  • 如果餐馆里已经有两个顾客,新顾客需要等到一个现有顾客吃完

很酷,不是吗?

把所有东西放在一起——模拟一家食物供应有限的餐馆

作为一名经理,你认为每种食物 10 件就足够养活你的顾客了。让我们通过应用到目前为止所学的知识来检验这个假设。

创建一家餐厅

作者图片

我们将模拟一家餐厅:

  • 只接受外卖订单
  • 只有一名工作人员
  • 顾客排队等候点菜单上的食物。顾客订购的商品越多,顾客需要等待的时间就越长。

这家餐厅的特点是:

  • staff:能力为 1 的资源(员工一次只能服务一个客户)
  • foods:餐厅提供的食物选择
  • available:每种食物的项目数量
  • run_out:每种食物用完时的事件
  • when_run_out:各种食物用完的时间
  • rejected_customers:因食物选择耗尽而离开队伍的顾客数量

创建客户

我们还想创建一个排队等候的客户。客户将:

  • 如果剩下的食物不够,就离开
  • 如果还有足够的食物,就点餐

如果顾客点了某一种食物后没有食物剩下,我们会宣布所选的食物已经用完了。

上面代码中的两个新概念是:

  • restaurant.staff.request:请求使用资源。在这种情况下,客户向工作人员请求服务。
  • restaurant.run_out[food].succeed : run_out是可以成功也可以失败的事件。在这种情况下,如果事件run_out成功,它表示餐馆用完了食物选项。

在模拟达到时间限制之前,会有新的客户。

运行模拟

现在我们已经创建了顾客和餐馆,让我们运行模拟。当餐厅没有食物可供选择时,我们会打印信息。

输出:

作者图片

酷!想象一下 SimPy 里的 1 秒在现实生活中就是 1 分钟。如果模拟运行 240 分钟(4 小时),并且每个食物选择有 10 个项目,那么餐馆将在不到 240 分钟内用完每个食物选择。

还有总共 22 名顾客饿着肚子离开餐厅。因此,如果我们想在 240 分钟内为所有顾客提供食物,我们可能需要为每种食物选择准备 10 种以上的食物。

这个例子怎么改进?

注意,上面的例子只是一个真实餐馆的简化版本。为了使这个例子更接近真实的餐馆事件,您可能需要添加更多的交互。

您还可以多次运行模拟,以更好地估计一种食物用完之前的分钟数。

查看 SimPy 文档中的示例,获取其他灵感。

结论

恭喜你!您刚刚学习了如何使用 SimPy 来模拟现实生活中的事件。希望这能给你动力去模拟你感兴趣的事件。

本文的源代码可以在这里找到:

https://github.com/khuyentran1401/Data-science/tree/master/applications/simpy_examples

我喜欢写一些基本的数据科学概念,并尝试不同的算法和数据科学工具。你可以通过 LinkedInTwitter 与我联系。

如果你想查看我写的所有文章的代码,请点击这里。在 Medium 上关注我,了解我的最新数据科学文章,例如:

用 Python 模拟你的交易策略

原文:https://towardsdatascience.com/simulate-your-trading-strategy-with-python-5a8a556c470e?source=collection_archive---------10-----------------------

股票市场中美元成本平均法的回测频率

马修·斯特恩在 Unsplash 上的照片

W 无论你是新投资者还是现有投资者,你都可能听说过“平均成本”这个术语,它指的是一种投资策略,通过将投资总额分成几个周期来减轻情绪压力和投资风险。

早在 2012 年,Vanguard 等机构就有研究比较了平均成本投资(DCA)和一次性投资(LSI)的历史表现。它得出的结论是,平均而言,即使在调整股票/债券投资组合的较高波动性后,LSI 的表现也有大约三分之二的时间优于 DCA。一次性投资让投资者更早地接触市场,如果市场长期呈上升趋势,这尤其有益。

然而,心理学在投资中起着至关重要的作用。通常情况下,投资决策是出于恐惧或贪婪。有很多关于人们因为 FOMO 或 YOLO 而赚钱或赔钱的故事。

我非常喜欢摩根·豪斯尔的《金钱心理学》这本书。在书中他指出

应对失败的诀窍是安排好你的财务生活,让这里的一次糟糕投资和那里的一个失败财务目标不会把你击垮,这样你就可以继续玩下去,直到胜算对你有利。

因此,在本文中,我将继续探讨美元成本平均化的思想,通过调整交易频率来了解资产的风险和回报可能如何相应地变化。

来自《走向数据科学》编辑的提示: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们并不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语

假设和方法

我通常对比较每周(5 天)和每月(20 天)的投资区间感兴趣。

数据集是通过 Python 包 yahoofinancials 拉取的。回溯测试是通过随机抽样 2016 年以来 5 年期间的历史价格来构建投资组合。

在开始模拟之前,以下是模型中嵌入的一些假设:

  • 相同的起始时间和相同的总投资金额
  • 假设部分股票可以收盘价买入和交易(使用收盘价的原因将在下面解释)。这也可以针对任何其他价格进行调整)
  • 假设零经纪费
  • 每周交易选择一个随机的价格点(而不是每 5 天)进行投资,每 5 个价格点按时间顺序捆绑在一起。这同样适用于月度方法
  • 在每次交易中,投资额是固定的,取决于交易频率,即每月投资额高于每周投资额
  • %回报是通过比较迄今为止的投资总额与当前投资组合价值来计算的

历史价格的探索性数据分析

在任何建模或模拟之前,重要的一步是实际查看我们正在分析的数据。此外,为了简化模拟过程,我将只考虑开盘价或收盘价。

如果你在推特上,你可能会看到这条推文:

为了测试上面的陈述,我选择了包括纳斯达克上市的前 100 家公司的 QQQ ,作为例子。这里我比较了当天开市和收盘的差异(左),以及收盘和下一次开市的价差分布(右)。

QQQ 开盘和收盘时的价格(左);收盘价和下一个开盘价之间的差价分布(右)

有趣的是,对于 QQQ 来说,价差分布类似于现实生活中的正态分布,这让我有信心使用下面的测试进行假设检验:

空:关闭和下一次打开之间的差值为 0

替代方案:关闭和下一次打开之间的差异> 0

*# add difference between next open price and close as a new column* historical_price["diff"] = historical_price["open"].shift(-1) - historical_price["close"]*# hypothesis test on if the difference between historical open and close was > 0* scipy.stats.ttest_1samp(historical_price["diff"], 0, alternative='greater')

测试结果返回小于 5%的 p 值,表明对于所选股票,第二天的开盘价大于前一天的收盘价在统计学上是显著的。

模拟成本平均法

在这一节中,我将介绍模拟不同交易频率的美元平均成本的方法,并比较每周(5 天)和每月(20 天)的风险和回报。

算法不是每隔 n 天进行投资,而是在指定的交易区间内随机选择一个可用的日期和价格,以模仿个人在研究期间持续投资固定金额。

基于随机选取的定价点,算法然后计算每次交易的number_of_units、资产的pricestotal_dollar_amount invested_to_date以及% return

随机选股返回的典型数据帧

您还可以选择需要运行模拟的次数,以获得预期的投资组合回报并构建置信区间。

下面是从 2016 年至今在 QQQ 上总共投资 5 万美元的模拟回报,其中value_w代表以 5 天为间隔投资,value_m代表以 20 天为间隔投资。

根据该图,看起来每月的总回报值与每周的总回报值不同。再次应用假设检验,通过分别构建 10,000 个投资组合,看两个模拟值是否不同。双边测试返回的 p 值为 0,这表明这两种交易频率的收益率不同。

风险通常与回报密切相关。在这里,风险分析通过查看回报的差异而得到简化。 Levene 测试用于等方差测试。返回的 p 值是 0.976,表明每周或每月交易的收益方差没有显著差异。

# perform Levene test for equal variances
scipy.stats.levene(result_w["return"], result_m["return"])

我们可以通过查看多种资产及其在过去五年中的模拟表现来进一步扩展分析。在这个例子中,我选择了 QQQTSLAGMEBTC-美元WMTARKK

对于上面的每个报价器,我使用两种交易策略进行模拟,分别构建 10,000 个投资组合,并计算回报率百分比绝对差异的 95%置信区间。

我们可以看到所有的月策略在模拟下都优于周策略。比特币和 TSLA 等波动性资产的回报率通常更高,置信区间更宽。相比之下, WMT 等蓝筹股和 QQQ 等指数 ETF 的置信区间极窄,但增量正收益也相对较低。

此外,考虑到波动性,基于模拟结果,周交易策略和月交易策略的收益方差没有显著差异。

2020 年似乎是股票非凡的一年。为了提供一个完整的画面,在一个相当动荡的宏观环境下比较两种交易策略的表现也是值得的。

以下是 QQQTSLAGMEMSFTWMT间谍在 2007 年至 2012 年期间的表现差异,期间市场受到全球金融危机以及随后的欧洲主权债务危机的冲击。

在所选的报价器中, TSLA 是唯一一个月交易严重跑输周交易的资产,而且收益的方差也不同。其余的收益没有差别,也显示出相似的风险状况。

结论

如果市场的长期趋势是向上的,模拟结果表明,投资金额较高的月交易区间优于周交易区间。在我们的模拟投资组合中,它产生了具有统计意义的更好回报,并具有类似的波动性(也称为风险)。

在一个资产不断升值的市场中,早配置比晚配置更多的资金来获得风险敞口是合乎逻辑的。然而,结果可能会因你投资的资产而异,风险资产的预期回报波动更大。

如果有兴趣,你可以在这个资源库中找到以上所有的作品。

免责声明

本文仅供参考,不应作为法律、商业、投资或税务建议。对任何证券或数字资产的引用仅用于说明目的,并不构成投资建议或提供投资咨询服务。

帖子修改后原发于此:https://www . chuxinhuang . com/blog/trading-strategy-simulation-with-python/

用 VQE 模拟分子能量的量子计算

原文:https://towardsdatascience.com/simulated-quantum-computation-of-molecular-energies-using-vqe-c717f8c86b94?source=collection_archive---------22-----------------------

用变分量子本征解算器计算哈密顿量的基态能量

图 1:元素周期表(图片由 Elchinator 提供,来自 Pixabay ,来源https://pix abay . com/illustrations/Periodic-system-chemistry-science-3962844/)

如今,许多物理问题都可以用量子计算来解决。其中一个应用是量子化学,它模拟分子来寻找基态能量。原子的基态是指电子处于最低能级的未激发状态。计算基态能量对于多体物理、分子动力学、凝聚态物理以及其他各种学科都是极其重要的。举例来说,了解凝聚态物质中系统的基态是很有趣的,因为它告诉我们系统在低温下的行为,在低温下量子效应通常是最强的。比如在室温下考虑金属,相关的温标就是费米温度,可能是几百开尔文。所以室温金属可以被认为是处于基态,顶部有一些激发。因此计算基态能量是一项重要的任务。

由于其有限的计算能力,较大分子的模拟为经典计算带来了障碍。这就是量子计算机可以提供有效替代方案的地方。作为有史以来最伟大的思想家之一,理查德·费曼说:

自然不是经典的,该死的。而如果你想做一个自然的模拟,最好是量子力学的。

因此,与经典计算机相比,量子计算机是模拟哈密顿量来寻找基态能量的完美工具。这是使用称为变分量子本征解算器(VQE)的算法完成的。因此,变分量子本征解的研究在量子计算中至关重要。在本文中,我将尝试用最少的数学来解释这种方法。让我们来看看 VQE 的概念,并探索它的工作原理。

变分量子本征解算器

VQE 是一种混合量子经典算法,用于计算量子系统的基态。VQE 使用变分法达到所考虑系统的最低能量状态(基态)。简而言之,在该算法中,量子计算机用于创建分子的波函数 Ansatz,并使用哈密顿量计算期望值。经典优化器用于优化量子电路中的变分参数。用于此目的的哈密顿量“H”总是一个埃尔米特矩阵。这意味着 H 的共轭转置总是等于它自己。谱定理表明埃尔米特矩阵的特征值必须是实数。由于任何可测的量必须是实数,厄米矩阵适合于描述量子系统的哈密顿量。为了理解这种算法的操作,我们需要理解数学中的一些基本概念。

矩阵 A 的特征向量对于应用于向量空间的变换 A 是不变的。这意味着,在对向量空间应用矩阵运算之后,特定特征向量的方向保持不变,但是向量的大小可能会变化。从下图中可以更清楚地理解这个概念,

图 2:特征向量的可视化(来源https://gfycat.com/fluffyminiaturebackswimmer

从上图可以看出,即使在向量空间上应用了变换 A,用黄色表示的两个向量的方向也没有改变。这些被称为变换 A 的特征向量,因为变换的唯一效果是拉伸或压缩幅度。与此相反,粉红色的向量从它的初始方向被撞掉了。这就是矢量变化的跨度。因此,它不能被认为是这个变换 a 的特征向量。

每个 2D 变换不一定都有特征值。一些变换可能没有任何特征向量。例如,90⁰逆时针旋转 2D 矢量空间的变换可能没有任何特征矢量,因为它改变了所有可能矢量的跨度。因此,这种变换的本征值是虚的,表示在 2D 平面中没有本征向量可以用于这种变换。

使用相同的逻辑,矩阵向量乘法和标量向量乘法的等式给出为:

H|ψᵢ> = λᵢ |ψᵢ>

其中|ψᵢ>是特征向量,λ是对应特征向量的特征能量或特征值。我们特别感兴趣的是最小本征能级,

H|ψ𝔤> = E𝔤 |ψ𝔤>

其中,E𝔤 = λ𝔤 =最小(λ)

因此我们对 E𝔤感兴趣,它是与哈密顿量相关的最小本征值。VQE 将可能的量子态空间参数化,并优化参数以找到具有最小能量的参数集。因此,为了优化参数,需要迭代改变 Ansatz。这是通过变分 Ansatz 中的通用门的线性变换来实现的。|ψ>上参数的迭代优化旨在产生期望值,

∞λ𝔤,这是我们对基态的期望值

利用 VQE 发现基态的算法

为了计算基态能量,最重要的是系统的哈密顿量。Pennylane 提供了各种用于处理和计算分子哈密顿量的内置库。为了计算泡利 X、Y 和 Z 门中的哈密顿量,需要复杂的计算。但是使用 pennylane molecular_hamiltonian()函数可以省去这些计算。该函数采用如下输入参数:

  1. 符号和坐标:感兴趣的分子的几何结构可以从各种数据库下载,如 NIST 化学网络图书、化学蜘蛛和智能社交网络。使用函数 qchem . read _ structure(GEOMETRY _ FILE)从该几何图形中,我们可以计算出该结构的符号和坐标。
  2. 电荷:要求出基态能量的分子并不总是带中性电荷。根据增加或减少的电子数量,原子上的电荷会发生变化。这可以通过使用电荷参数来解决。
  3. 活性电子和轨道:活性电子和轨道的数量可以通过调整这个特定的参数来改变。如果没有明确说明,所有的电子和轨道都被认为是活跃的。
  4. 映射:为了解决量子器件上的问题,有必要将费米子哈密顿量转换成量子比特哈密顿量(泡利门)。使用映射选择此转换方法。

这些参数可以馈入内置函数 qchem.molecular_hamiltonian(),该函数为我们提供泡利 X、Y 和 Z 基中所需的哈密顿量。

对于本文,我认为它是一个简单的哈密顿量,只需要两根线(量子位)。下面给出了计算基态能量的哈密顿量,

(32.7658547225) [I0]
+ (-2.1433035249352805) [X1]
+ (-2.1433035249352805) [Z0 X1]
+ (17.015854722500002) [Z1]
+ (3.913118960624632) [X0 Z1]
+ (-23.359145277499998) [Z0 Z1]
+ (-3.913118960624632) [X0]
+ (-26.859145277499998) [Z0]

其中 X 和 Z 是应用于各个量子位的泡利门。

找到哈密顿量后,我们要设计一个 Ansatz U(α),这是一个关键的任务。Ansatz 应该相当复杂,以便完美地完成计算,并且不要太复杂以至于参数的优化需要足够长的时间。对于这个特定的问题,Ansatz 是使用下面的用户定义函数设计的。

def variational_ansatz(params, wires): n_qubits = len(wires)
    n_rotations = len(params)
    #print(wires," ",n_qubits," ",n_rotations,"\n")

    if n_rotations > 1:
        n_layers = n_rotations // n_qubits
        n_extra_rots = n_rotations - n_layers * n_qubits # Alternating layers of unitary rotations on every qubit                   .       # followed by a ring cascade of CNOTs. for layer_idx in range(n_layers):
            layer_params = params[layer_idx * n_qubits : layer_idx *   .           n_qubits + n_qubits, :]
            qml.broadcast(qml.Rot, wires, pattern="single",               .           parameters=layer_params)
            qml.broadcast(qml.CNOT, wires, pattern="ring") extra_params = params[-n_extra_rots:, :]
        extra_wires = wires[: n_qubits - 1 - n_extra_rots : -1]
        qml.broadcast(qml.Rot, extra_wires, pattern="single",               .       parameters=extra_params)
    else:
        # For 1-qubit case, just a single rotation to the qubit
        qml.Rot(*params[0], wires=wires[0])

该算法使用 Adam 优化器进行参数优化,学习率为 0.1。迭代次数选择为 250 次迭代。这些超参数可以根据应用和基态能量的收敛进行调整。最后,在 250 次迭代之后,为了可视化的目的,在 Ansatz 中使用优化的参数集。具有优化参数的 Ansatz 如下所示,

图 3:具有给定哈密顿量的优化参数的 Ansatz 结构

总结一下这个算法,

图 4:VQE 流程图

结论

  1. 该算法需要大约 15 秒来执行。输出如下所示:

图 5:算法的输出

状态能量减少的可视化如下所示,

图 6:使用 VQE 可视化哈密顿量的减少能量

该图清楚地显示了该态的能量从-0.6258 Ha 开始减少并达到其基态,其在 -2.0457 Ha 附近。

对于这个问题,Ansatz 是精心选择的,哈密顿量不太复杂,优化器没有陷入局部最小值。但在某些情况下,对于复杂的哈密顿量和 Ansatz 函数,优化器可能会陷入局部最小值。为了避免这种情况,超参数调整起着重要的作用。

完整的代码可以在 Github 上找到。

参考文献

  1. https://pennylane.ai/qml/demos/tutorial_vqe.html
  2. https://grove-docs.readthedocs.io/en/latest/vqe.html
  3. https://qi skit . org/textbook/ch-applications/vqe-molecules . html
  4. https://grove-docs.readthedocs.io/en/latest/vqe.html
  5. https://youtu.be/PFDu9oVAE-g

更多关于量子计算和机器学习的故事,请关注我的媒体。还有,看看我的 GithubLinkedin

用 Qiskit 模拟量子计算机

原文:https://towardsdatascience.com/simulating-a-quantum-computer-with-qiskit-46eb73f78394?source=collection_archive---------30-----------------------

Qiskit 量子模拟器-如何使用它们以及它们的用途

量子机器学习要不要入门?看看 动手用 Python 学习量子机器。

在量子计算中,我们使用量子物理的基本性质来执行计算:叠加和纠缠。

叠加是指量子系统存在于量子态|0⟩和|1⟩.的复杂线性组合中的量子现象纠缠是量子粒子之间极强的关联。即使相隔很远,纠缠的粒子仍然保持完美的关联。

仅仅是我们能够利用这些现象进行计算这一事实就令人震惊。但是,这种类型的计算使我们能够解决看似无法解决的问题的事实是不可思议的。

令人惊讶的是,我们甚至不需要量子计算机。我们可以用一个日常笔记本来模拟它们。

假设我们有下面的量子电路。

作者图片

我们在|0⟩基态的量子位上应用哈达玛门,把它放入|+⟩.态在这种状态下,我们有 50%的几率将量子位测量为 0,有 50%的几率将其测量为 1。

下面的清单在 Qiskit——IBM 的量子计算 SDK——中描述了这个电路。

Qiskit 提供了Aer包。它为模拟量子电路提供了不同的后端。先说第一个,那个qasm_simulator

一旦我们用qasm_simulator后端(或任何其他后端)执行了我们的量子电路(qc,我们就可以使用job.result()方法获得结果。我们可以将这个结果转换成数字(get_counts()),然后输入到最终分布的直方图中。

尽管我们的量子电路产生了一个量子位,我们以 50%的几率将其测量为 0,但结果显示了略有不同的分布。

作者图片

这是因为qasm_simulator根据经验检索测量计数。shots参数告诉 Qiskit 运行电路多少次并获得测量结果。因此,结果并不完全准确。但是拍摄次数越多,结果就越准确。

qasm_simulator只考虑我们测量量子位时使用的经典位。按照量子力学,我们看不到一个量子位所处的真实状态。我们只能反复测量才能得出它的状态。

当然,知道量子位的确切状态会有所帮助——尤其是在量子电路的开发过程中。

幸运的是,Qiskit 提供了另一个后端,即statevector_simulator。这个模拟器计算一个量子位的确切状态。当我们使用这个模拟器时,去除所有的测量值是很重要的,因为测量会破坏量子叠加,不可避免地导致系统可能处于一个确定的状态。但是我们对坍缩前的量子叠加感兴趣。

下面的代码描述了更新的量子电路。注意,statevector_simulator不接受 shots 参数。

类似于qasm_simulator,我们可以从执行结果中得到计数。

作者图片

结果显示了精确的测量概率。

但是它是怎么做到的呢?

为了理解经典计算机如何计算量子电路,我们需要考虑量子态(这里是|0⟩)和量子算符(这里是 T9 h T10)到底是什么。

在量子力学中,我们使用看起来像|0⟩.的狄拉克符号这不是什么奇特的东西,只是一个简单的向量。

量子算符通常用大写字母表示,比如 H 。但它们也没什么特别的。量子算符是一个矩阵。

最后,量子电路一点也不花哨。就是矩阵乘法。是的。整个电路将矩阵 h 与向量|0⟩相乘,并产生另一个向量,如下式所示。

结果向量表示测量幅度,其平方是测量概率。1/sqrt(2)的平方是 1/2。

从结果对象中,我们也可以使用get_statevector()方法获得结果向量。输出是状态向量的数组。在我们的例子中,它是:

array([0.70710678+0.j, 0.70710678+0.j])

注:1/2 的平方根约为 0.707 左右。

statevector_simulator后端计算给定量子系统的状态。这是检查叠加态量子位的理想方法。

我们的量子电路由一个单一的算子组成——哈达玛门。比方说,我们用一系列的门来代替。例如,我们可以应用三个门,H-Z-H(在这篇文章中了解更多关于 HZH 序列的信息)。

作者图片

当我们运行电路时,我们看到结果为 1(如果量子位处于|0⟩).态如果你想从数学上验证这个结果,我们需要将三个矩阵相乘。

即使对于小矩阵,它们的乘法也很麻烦。你需要做相当多的乘法,你必须确保不要混淆行和列。

所以,我一直用电脑做矩阵乘法。有无数的计算器可以做到这一点。然而,最方便的是在 Qiskit 内部。就是这个unitary_simulator

这个模拟器执行电路一次,返回电路本身的最终变换矩阵。不管我们的量子电路看起来像什么,最终,它是一个矩阵。在输入状态下运行电路就是将转换矩阵与状态向量相乘。同样,当我们使用这个模拟器时,我们的电路不应该包含任何测量。

以下代码输出 H-Z-H 电路的矩阵。正如我们所看到的,它类似于非门的矩阵。因此,毫不奇怪,当应用于|0⟩态的量子位时,结果是|1⟩态的量子位,我们总是测量为 1。

array([
  [ 2.22044605e-16+6.1232340e-17j,  1.00000000e+00-1.8369702e-16j],      
  [ 1.00000000e+00-6.1232340e-17j, -2.22044605e-16+6.1232340e-17j]
])

如果量子计算就是矩阵乘法,有什么了不起?

矩阵乘法没什么花哨的。但问题是,这需要大量的计算。矩阵越大,所需乘法运算的次数(指数)就越高。因此,矩阵乘法花费的时间越多。我们可以用传统的计算机乘任意大小的矩阵。唯一的问题是,对于大型(假设是巨大的)矩阵来说,它(几乎)需要花费很长时间。大多数经典计算机无法(有效地)处理大于 30x30 的矩阵。

这就是量子计算机发挥作用的地方。他们在一个步骤中乘以许多矩阵。矩阵的大小无关紧要。因此,它们能够执行经典计算机无法执行的计算(在合理的时间内)。

结论

量子计算机是令人着迷的设备,尤其是当我们想到它们的硬件以及它们如何利用量子力学来执行计算时。这远远超出了大多数人(包括我)所能理解的范围。

但是量子计算并没有那么复杂。就是矩阵乘法,本质上就是。在小范围内,经典计算机在矩阵乘法方面做得相当好(而且准确)。因此,经典计算机可以模拟量子计算机做的事情。

但是随着矩阵规模的增加,经典计算机已经达到了极限。然后,没有模拟器会帮助你了。

量子机器学习要不要入门?看看 动手用 Python 学习量子机器

在这里免费获得前三章。

用 Python 中的 SimPy 模拟酿造操作

原文:https://towardsdatascience.com/simulating-brewing-operations-with-simpy-in-python-5b67da3783aa?source=collection_archive---------13-----------------------

python 中的离散事件模拟教程

罗伯特·北原惠子·桑塔纳在 Unsplash 上拍摄的照片

工业工程师使用离散事件模拟来评估生产车间的替代方案,但模拟还有更多用途。有了 Python 的 SimPy 包——也有其他语言版本——我们可以模拟个人项目,而无需昂贵的软件许可。SimPy 的 ReadtheDocs 文档很好;我建议从头到尾多看一遍。但是这篇文章是为那些喜欢额外的学习例子的人准备的。

SimPy 简介及示例

SimPy 使用带有 yield 而不是 return 的生成器函数,以便存储模拟中的最新步骤。关于生成器函数的介绍,我推荐凯文·康拉德的 youtube 教程

这个初始模型利用 SimPy 框架来启动酿造过程,然后清洗酿造罐。后续的模型会更复杂。

在导入 simpy 之前,如果您还没有安装 SimPy,您可能需要安装它。要安装 SimPy,您可以使用 pip 或 SimPy 文档中编写的替代方法。

pip install simpy

输出:

Starting to brew a lager at time 0
Finished brewing the lager at time 30
Starting to brew a lager at time 35
Finished brewing the lager at time 65
Starting to brew a lager at time 70
Finished brewing the lager at time 100

在这种情况下,模型在时间单位达到 101 时终止。一旦处理所有酿造过程的循环终止,下一个模型将终止。

在 SimPy 中使用资源和建模流程

现在我有 3 个同样大小的啤酒缸。我仍然在酿造淡啤酒,但是我想在第 0、5、11 和 20 天开始一个新罐,如果这些天有可用的罐的话。我在第 12-18 天休假,所以我不想在那几天开始酝酿过程。

在这个模型中,贮藏啤酒的酿造过程时间仍然假定为 30 天,尽管我意识到酿造过程时间实际上是可变的。我们将在下一个模型中添加处理时间的概率分布。

目前,这个模型的焦点是资源的类型,称为资源(而不是容器资源或商店资源)。

  • 这个 brewery_tanks 资源将作为这个模型中的流程。
  • 资源被请求,然后在它们不再被使用后释放
  • 在使用中,资源的计数将显示有多少资源的容量单位在使用中。

输出:

Decide to start brewing a lager at time 0
	Number of tanks available: 3
	Started brewing at time 0
	Number of tanks now in use: 1
Decide to start brewing a lager at time 5
	Number of tanks available: 2
	Started brewing at time 5
	Number of tanks now in use: 2
Decide to start brewing a lager at time 11
	Number of tanks available: 1
	Started brewing at time 11
	Number of tanks now in use: 3
Decide to start brewing a lager at time 20
	Number of tanks available: 0
Finished brewing a lager at time 30
Finished brewing a lager at time 35
There are 1 tanks available at time 35
	Started brewing at time 35
	Number of tanks now in use: 3
There are 1 tanks available at time 40
Finished brewing a lager at time 41
There are 2 tanks available at time 46
Finished brewing a lager at time 65
There are 3 tanks available at time 70

我选择了一个很好的时间去度假,因为当时没有任何可用的坦克。事实上,直到时间 35 都没有可用的坦克。

如您所见,仿真模型的输出值得思考。文档中的一个章节叫做 monitoring 讨论了保存和监控模型输出的更高级的方法。但是对于这个介绍和许多其他场景,print 语句就足够了。

随机模拟

虽然可以用数学方法计算确定性模拟结果,而不是利用模拟模型,但模拟对于讲述故事和自动化数据处理非常有用。随机模型将为更接近真实情况的建模场景提供更多的见解。

我阅读了多种描述酿造啤酒的资源,并分享了典型的酿造时间表是 4-8 周。酵母会做酵母做的事情,但不总是按照你的计划。让我们假设一个均值=6 周,标准差=1 周的正态分布。一旦啤酒冷却(我们假设这是过程的一部分),然后我们将啤酒并储存在库存中,比率为每个啤酒厂罐 100 箱。

为了跟踪库存,这个模型中引入了另一种称为容器的资源。虽然容器类似于资源,因为它们都具有容量参数,但是容器也具有初始的级别参数,并且容器在给定时间的级别将揭示容器存储的量。

容器的级别可以随着一个 put 而增加,或者随着一个 get 而减少,这实现了与资源不同的建模能力。

输出:

Number of tanks available at time 0: 3
Started brewing at time 0 and 1 tanks now in use
Number of tanks available at time 5: 2
Started brewing at time 5 and 2 tanks now in use
Number of tanks available at time 11: 1
Started brewing at time 11 and 3 tanks now in use
Number of tanks available at time 20: 0
Finished brewing a lager at time 38.30356066389249
At time 43.30356066389249 there are now:
	1 tanks available
	100 cases of cans available for sale
Started brewing at time 43.30356066389249 and 3 tanks now in use
Finished brewing a lager at time 44.85515309121678
At time 49.85515309121678 there are now:
	1 tanks available
	200 cases of cans available for sale
Finished brewing a lager at time 53.146254345432425
At time 58.146254345432425 there are now:
	2 tanks available
	300 cases of cans available for sale
Finished brewing a lager at time 85.11565243779629
At time 90.11565243779629 there are now:
	3 tanks available
	400 cases of cans available for sale

处理订单

现在您已经了解了如何使用两种类型的资源,即资源和容器,下一个模型将分享一种处理订单和销售库存的方法。虽然这是一个处理订单的简单方法,但是更复杂的方法可能需要使用任何简单事件的 AllOf。

输出:

Before order processing, cases available: 100
After order of 1, cases available: 99
After order of 10, cases available: 89
After order of 5, cases available: 84
After order processing, cases available: 84

我希望本教程对你有所帮助。如果你对第二部分啤酒厂模拟有什么建议,或者想在下一个 SimPy 模型上合作,我很乐意阅读你的想法!

用气流模拟连续学习模型

原文:https://towardsdatascience.com/simulating-continuous-learning-models-with-airflow-93d852718a78?source=collection_archive---------22-----------------------

配置工作流管理工具,在 13 分钟内定期重新创建模型

照片由来自 Pexels阿尼·周拍摄

C 持续学习是系统自适应学习外部世界的过程。在机器学习的背景下,模型通过自主和增量开发不断地学习更多关于它被训练的主题领域的知识。这使模型能够学习更复杂的逻辑、知识和功能,从而有助于做出更好的预测。

持续学习对于更新模型尤其重要。假设你被一家生产哑铃的制造公司聘为数据科学家,比如鹦鹉螺公司。你的目标是预测下个季度的收入和销售额。从 2017 年 3 月到 2020 年 3 月,您的模型在收入预测上的准确率为 95%。但是,从 2020 年 3 月到 2021 年 1 月,你的模型的准确率是 40%。发生了什么事?

2020 年 3 月—2021 年 1 月发生的重大事件是冠状病毒。检疫条例迫使人们呆在室内,不要频繁外出。不能再去健身房的健身爱好者不得不建立他们的内部健身房,以跟上他们的锻炼养生。鹦鹉螺公司是哑铃和家用健身设备的领先公司,因此在疫情期间销售额飙升。在这种情况下,你在 2017 年创建的模型将严重低估 2020 年的预测销售额。这是一个很好的例子,说明为什么一个模型需要根据最近的和新出现的当前数据进行重新训练。世界的发展速度令人难以置信,3 年前的数据见解可能不适用于不断发展的数据。

您可能已经意识到需要创建 3 个独立的模型:一个在疫情之前(2020 年 3 月之前)接受数据培训,一个在疫情期间(2020 年 3 月-2021 年 5 月)接受数据培训,一个在疫情之后(2021 年 5 月至今)接受数据培训。但是,您可能没有足够的时间按照特定的时间段清晰地分割数据集。因为您可以假设疫情的开始和结束日期(例如,假设疫情的结束时间是疫苗推出的时间),所以您可以准确地知道在哪里分割数据集。

随着时间的推移,各种数据集可能会发生微妙的变化,您需要应用一种持续学习的方法,以便您的模型能够随着新信息的出现而学习。这些数据既反映了收集数据的动态环境,也反映了提供数据的形式和格式。作为后一种发展的例子,考虑一个图像分类器,它确定 1000 页 pdf 中的一页是文本还是表单。随着时间的推移,表单的格式会发生变化,原始图像分类器会将其识别为文本。因为您不知道表单更改的确切时间,所以您需要根据所有当前数据训练一个模型。此外,图像分类器必须在较新的表单数据上重新训练,同时跟踪较旧的表单数据。

一个模型需要一个持续的学习方法来基于更新的数据进行再训练和更新。也就是说,用数学方法实现深度学习模型和递归神经网络的这种方法可能非常棘手。许多数据科学家不具备在公司期限内构建这种定制模型的知识。但是,有一种方法可以使用工作流管理工具(如 Airflow )来模拟持续学习。在预定的基础上,Airflow 可以获取新的训练数据,并使用以前的和新的数据重建模型。然后,数据科学家可以构建简单的模型(无论是他们自己构建的简单 PyTorch 神经网络,还是他们利用现有的 Python 机器学习库,如 scikit-learn),并专注于持续更新训练数据。

本文将通过一个例子来说明如何对时间序列模型应用连续学习。我们的模型将使用 ARIMA 分析两只股票的历史股票市场数据:Fastly 和 Nautilus。

免责声明:这不是投资建议。话虽如此,我确实持有这两只股票。所以我倾向于提升他们,这样我就能从中受益。在花你的血汗钱之前做你的研究。

另外,在你决定用 ARIMA 预测股市之前,请多读一些时间序列方面的书。当数据集中存在可识别的模式时,时间序列模型是有效的。我没有花时间去分析 ARIMA 是否适合预测 Fastly 和 Nautilus 的股票历史数据。本文从数据工程的角度讨论构建持续学习架构。ARIMA 和时间序列被用来表明,这种架构可以用于各种类型的模型(时间序列,分类,神经网络)。分析这种时间序列模型的预测指标超出了本文的范围。

为什么是气流?

Airflow 是一个用 Python 编写的开源工作流管理工具。它已经成为行业标准,并且很容易建立。在处理复杂和动态的工作流时,气流也有缺点,最好使用不同的工作流工具,如 Prefect。对于本教程,我们将创建一个简单的工作流。因此,气流是完美的。

我们将在 AWS EC2 上设置气流。如果你愿意,你也可以利用 AWS MWAA(亚马逊管理的 Apache Airflow 工作流)。但是,请记住,对于一些开发人员的预算来说,AWS MWAA 可能很昂贵。例如,我每天只需支付 27 美元就可以让它正常运行,其他 AWS 弹性云服务(例如 NAT 网关)每月支付 80 美元。

术语

  • AWS MWAA-亚马逊管理的 Apache Airflow 工作流。处理气流的所有设置和配置,但维护费用昂贵。
  • AWS EC2 —运行应用程序的虚拟服务器。
  • AWS S3——存放腌制模型的桶。
  • ARIMA——自回归综合移动平均。它是一类模型,捕捉时间序列数据中一组不同的标准时间结构。

推荐阅读

本教程假设您对气流和 Dag 有基本的了解。如果没有,请阅读气流:如何以及何时使用

要理解气流的架构,请阅读Apache 气流架构概述

本教程使用 ARIMA 进行时间序列分析。这个主题超出了本文的范围。如果你想更多的了解时间序列和 ARIMA,我推荐Arima 简介:非季节模型

体系结构

我使用 Cloudcraft 创建的气流架构图

辅导的

在本教程中,我们将在 Linux Ubuntu Server 20.04 LTS 上安装 Airflow。我们可以使用类型 t2.medium 创建一个 AWS EC2 映像。要阅读更多关于 AWS EC2 的内容,我推荐亚马逊的教程这里

Airflow 需要运行一个 web 服务器和一个调度程序,因此它需要比自由层提供的内存更多的内存。t2.medium 有足够的资源来安装和运行 Airflow。

我们还使用 Ubuntu,因为它是为数不多的适合机器学习的 Linux 操作系统之一。其他 Linux 操作系统(包括 AWS Linux)将不会有一些机器学习库所需的确切包和依赖项,包括脸书先知。他们有这些包的替代品,但如果脸书先知不能正确编译它们也没关系。脸书预言家需要一个被称为 C++14 的 C++标准来编译它所有的依赖项。Ubuntu(和 Mac OS)支持 C++14,但 CentOS 或 AWS Linux 不支持。Ubuntu 和 MacOS 对于下载未来的机器学习包并正确编译非常有用。

创建 EC2 实例

登录 AWS 控制台并导航到 EC2 >实例>启动实例。配置您的实例,使其与以下内容匹配。

注意:我删除了安全组中 Source 下的 IP 地址。但是为了保护您的实例以确保没有其他人可以访问它,请确保您选择了我的 IP 而不是 Anywhere)

编辑:除了这些安全组,还应该添加 HTTP,TCP,端口 8080。默认情况下,该端口由 airflow webserver 使用。如果您想使用不同的 airflow 服务器端口,您应该更改端口。

启动实例时,它会询问您从哪里下载. pem 文件。将此文件命名为 airflow_stock.pem,并将其存储在您选择的文件夹中。

等待 EC2 实例运行,然后在本地终端上调用这个命令。

ssh -i "airflow_stock.pem" ubuntu@ec2-3-131-153-136.us-east-2.compute.amazonaws.com

SSH 是一个安全的 Shell 协议,允许您在本地命令行上登录到云实例。它需要一个. pem 文件来验证您是否可以登录到服务器。

注意:如果你得到一个未受保护的私钥错误,这意味着你的。pem 是大家看得见的。您只需要调整. pem 的权限。在执行之前运行此命令

chmod 400 airflow_stock.pem

有关此错误的更多信息,请参见本文这里的

安装 Pip 和气流

假设你有一个干净的 Ubuntu 实例,你需要安装 pip 和 apache-airflow。要安装 pip,请运行以下命令

sudo apt update
sudo apt install python3-pip

我们将在教程中使用 Python 3。要安装 apache-airflow,请运行以下命令

pip3 install apache-airflow

下一步是创建文件作为气流项目的一部分。

创建文件夹结构

您可以创建以下文件,或者从 git repo 中获取。

该文件夹有 4 个组件:一个用于添加气流环境变量的 bash 脚本(add_airflow_env_variables.sh),一个用于安装 pip 包的需求文本(requirements.txt),一个启动气流脚本(start_airflow.sh),一个停止气流脚本(stop_airflow.sh),以及一个 dags 文件夹,该文件夹提取股票数据并从所有数据中重建 ARIMA 时间序列模型。

Visual Studio 代码中气流文件夹结构的屏幕截图

add _ airflow _ env _ variables . sh包含了我们在运行 air flow 之前需要设置的所有环境变量。现在,它只列出了 AIRFLOW_HOME,我们存储 AIRFLOW 的日志/输出/数据库的路径。我在 ec2 实例中克隆了这个 repo,所以我使用了 github repo 的文件路径。

这个 requirements.txt 文件包含了我们需要的所有 pip 包。

statsmodels
pandas
pandas_datareader
boto3
awscli

要安装 requirements.txt 中的所有包,只需运行

pip3 install -r requirements.txt

start_airflow.sh 包含在 EC2 服务器上启动 airflow 的所有命令。

stop_airflow.sh 包含停止当前在 EC2 服务器上运行的 air flow web 服务器和调度程序的所有命令。

stock_data_dag.py 将包含触发工作流的逻辑。工作流分为两个功能:提取和加载。

Extract(get _ stock _ data)将从 TIINGO 获取股票从 2015 年 1 月 1 日到触发日的所有历史价格。对于本教程来说,这个平台已经足够了。然而,在生产环境中,平台将要求只获取当天的数据,并追加到存储在 S3 中的以前的历史数据。

Load(store _ arima _ model _ in _ s3)从历史股票价格中创建 ARIMA 模型,并将腌制文件存储在 S3 桶中。我们可以获取那些根据新的训练数据重新训练过的新模型。

注意:您需要创建一个 TIINGO 帐户来获取 get_stock_data 中使用的 API 键(第 25 行)。

将 python 函数 get_stock_datastore _ ARIMA _ model _ in _ S3视为任务。为了在 DAG 上调用这些任务,我们需要使用操作符。操作符是 DAG 的主要构件,用于执行某些功能/脚本/API/查询等。在本例中,我们创建了 4 个不同的 Python 运算符:

  • 获取 NLS 股票数据
  • 商店 _ NLS _ ARIMA _ 模型 _in_s3
  • 获取 _ FSLY _ 股票 _ 数据
  • 存储 _FSLY_arima_model_in_s3

工作流时间表的格式为 crontab:0 17 * * 1–5。这意味着该工作流将在每个工作日的下午 5 点被触发。

stock_data_dag 为每只股票列出了两个不同的任务:get_stock_data 和 store_stock_arima_model 到 s3。 get_stock_data 获取自 2015 年 1 月 1 日以来的 TIINGO 历史股票数据,并将其存储在 JSON 中。 store_stock_arima_model 获取股票 JSON,获取所有日期的调整后收盘价,根据这些价格构建 arima 模型,并将 ARIMA 模型存储在名为stock-model的 S3 存储桶中。然后我们可以从 S3 下载模型,用它们来预测第二天的价格。

注意:这种架构不仅仅适用于 ARIMA 车型。各种其他 skikit-learn 模型和算法,如 XGBoost、RandomForest、NaiveBayes、LinearRegression、LogisticRegression 和 SVM,都可以从这种持续学习方法中受益。您可以简单地重用 stock_data_dag.py 文件的store _ ARIMA _ model _ in _ S3函数,或者直接编辑 python DAG 并将第 44 行更改为您想要的模型。您甚至可以添加自己的神经网络逻辑来代替 skikit-learn 模型,也就是说,您必须重新考虑您的回归/分类模型是基于哪些数据进行训练的。您可能需要更改 get_stock_data 来为您的模型获取不同的数据集。无论您试图解决什么样的业务问题,这种气流持续学习架构都可以互换,以适用于各种用例。

我们现在有了本教程计算所需的所有文件。下一步是执行某些脚本,并为运行配置气流。

创建 AWS S3 时段

我们想创建一个 S3 桶来存储我们的模型。导航到 S3 并创建一个名为stock-model的新存储桶。然后,我们将配置 EC2 aws 凭证,以便我们可以使用 boto3 来存储进入 S3 的气流的模型。

配置 AWSCLI

我们想把我们的 ARIMA 模型存放在一个 S3 桶里。为此,我们需要在 EC2 实例上配置本地 AWS 设置,以便它能够识别所需的 AWS 帐户。

首先,导航到 IAM 管理控制台的“安全身份证明”页面: IAM 管理控制台(Amazon.com)

IAM 的安全身份证明页

如果您还没有这样做,向下滚动访问键,并点击创建新的访问键。这将生成一个 excel 文件,您可以下载该文件来获得您的访问密钥和秘密访问密钥。不要与任何人共享此文件。如果您这样做,您的 AWS 帐户将面临黑客攻击和可能的超额收费的风险。

在 EC2 上,键入

aws configure

AWS 将提示您输入访问密钥和秘密访问密钥。将 excel 文件中的值放在那里。您可以在默认区域名称和默认输出上按 enter 键。

AWS 凭证现在设置在 EC2 上,因此您可以创建 boto3 会话。

配置气流

首先,添加环境变量。因为我们有一个脚本,您可以简单地调用以下命令。

source add_airflow_env_variables.sh
bash add_airflow_env_variables.sh

如果您想检查是否添加了环境变量 AIRFLOW_HOME,请在命令行中运行env

下一步是创建用于存储 dag 运行的气流数据库。如果这是您第一次创建 airflow 数据库,请在 AIRFLOW_HOME 路径中运行下面的命令。

airflow db init

您还需要添加一个用户,因为 airflow 会提示用户登录。以下命令摘自本地运行气流文档中的示例。

airflow users create \
    --username admin \
    --firstname Peter \
    --lastname Parker \
    --role Admin \
    --email spiderman@superhero.org

它会提示您输入自己选择的密码。

接下来,使用以下命令安装 requirements.txt 中的所有包

pip3 install -r requirements.txt

最后,运行以下命令来启动 daemon 中的 airflow webserver 和调度程序。

bash start_airflow.sh

这将运行气流调度和网络服务器没有任何问题。要检查 UI 是否工作,请导航到此 url。

http://ec2–11–111–111–11 . us-east-1 . compute . Amazon AWS . com:8080

ec2–11–111–111–11.us-east-1.compute.amazonaws.com 是一个虚构的 Ec2 公共 IPv4 地址。您应该能够在 EC2 -> Instances 控制台上的 Details 选项卡中为您的 EC2 实例获取它。确保您使用 http (不是 https)和端口 8080(如果您设置气流使用该端口,否则使用您配置的任何端口)。

您应该会看到一个登录页面弹出。输入用于在 Airflow 中创建用户帐户的用户凭据(用户名:admin,密码:您键入的任何内容)。现在,您应该可以看到列出的所有气流 DAG,包括我们之前创建的 stock_data DAG。

气流中所有 Dag 的列表,启用了 stock_data。

单击股票数据 dag。然后,您可以在图形视图中看到这个 dag 的 UI。

股票数据的 DAG 视图

在类型的右上角(在“时间表”下),您会看到一个“播放”按钮。点击该按钮手动触发 DAG。如果一切正常,您应该会看到所有操作员都成功通过绿色认证。如果有错误(失败,等待重试),单击操作符检查日志。

成功的 DAG 运行

现在,让我们导航到 S3 桶stock-model,看看我们的模型是否已经被添加。

添加了模型的库存模型桶

如果让 EC2 整夜运行,并且气流调度程序和 web 服务器不中断,您将看到这两个模型最后一次修改是在 7 月 23 日下午 5:00 UTC-05:00 左右。

注意:确保完成后终止 EC2

结论

本教程解释了如何使用工作流工具构建持续学习架构,以在当前和新的训练数据上重建 ARIMA 模型。我们学习了如何利用气流来创建这种持续学习架构,以预测两种不同股票的收盘价:Nautilus 和 Fastly。我们学习了如何在亚马逊 EC2 上部署气流,并在 S3 上存储新建立的模型。

持续学习不需要从头开始创建循环神经网络。这是通过使用流行的数据工程工具来创建持续学习模型的一种更简单的替代方法。仍在学习错综复杂的神经网络的入门级数据科学家可以依靠这一解决方案来创建一个健壮、可持续的架构,用于模型“学习”更新的数据。此外,这种架构可以在各种不同的算法上复制:时间序列、线性回归和分类算法。虽然读取和训练数据模型的逻辑需要改变,但总体架构应该适应各种业务问题。

Github Repo:【github.com】Data-Science-Projects/Medium/air flow _ CL/air flow at master hd2zm/Data-Science-Projects(

参考文献:

1.Matthew Kish 鹦鹉螺公司报告称,由于疫情推动了家庭锻炼趋势,销售额增长了 94%

  1. Apache Airflow —开源工作流管理工具

  2. PyTorch —开源机器学习框架

感谢阅读!如果你想阅读更多我的作品,请查看我的目录。

如果你不是一个中等收入的会员,但对订阅《走向数据科学》感兴趣,只是为了阅读类似的教程和文章,单击此处注册成为会员。注册这个链接意味着我可以通过向你推荐 Medium 来获得报酬。

模拟曼卡拉:当我把这个游戏推到极限时会发生什么?

原文:https://towardsdatascience.com/simulating-mancala-what-happens-when-i-push-this-game-to-its-limits-28d9c0a58616?source=collection_archive---------13-----------------------

我用 MATLAB 模拟了 10 万个曼卡拉游戏,看看会发生什么。结果如下:

图片由作者提供

简介:

由于曼卡拉是世界上最古老的游戏之一,所以选择这款游戏作为我的下一个棋盘游戏 MATLAB 项目是有意义的。曼卡拉是一个零和游戏,意思是一个人的收获直接导致另一个人的损失。这使得一次模拟几十万个游戏变得非常容易。

曼卡拉规则:

这是一个非常简单的游戏。每个玩家有 6 个坑和一个商店,棋盘上总共有 12 个坑和 2 个商店。每个坑开始时有 4 粒种子。目标是在您的商店中获得最多的种子。每回合,一名玩家从他们的 6 个坑中选择一个,取出所有的种子。逆时针方向移动,玩家从手中丢下一粒种子到下一个坑或仓库,直到手中的种子用完。

图片由pexels.com提供

还有一些特殊的规则需要注意:

  • 如果你的最后一粒种子在你的店里用完了,你可以再去一次。
  • 如果你的最后一粒种子落在你这边的一个完全空的坑里,你可以直接收集棋盘上的所有种子。

当一方完全空了,游戏结束,另一方从他们的坑里收集剩余的种子,放入他们的仓库。商店被计算出来,种子最多的人获胜。

编码 Mancala:

这种模拟的工作原理是让两个玩家互相随机移动,直到一个人获胜。随机选取一个有种子的坑,这些种子沿着棋盘逆时针分布(见下文)。

while gameover == 0 % while nobody has wonwhile p1_turn == 0 % while still player 1 turnif sum(layout(1:6)) ~= 0 % while player 1 still has seeds in pitsmoves = moves + 1; % plus 1 for total moveswhile good_pick1 == 0 % loop to find a pit with seedsp1 = randi(6); % picks random pitamount = layout(p1); % finds amount of seeds in that pitif amount > 0 % if pit has seedsbreak % breaks out of loop (found a good pit)endend

在每场比赛结束时,感兴趣的数据点被记录下来,并一直进行到所有 100,000 次模拟完成。

move_data(repeats) = moves; % adds moves to move datasetp1_data(repeats) = mean(p1_moves); % adds average move to player 1 move datasetp2_data(repeats) = mean(p2_moves) - 7; % adds average move to player 1 move datasetscore1_data(repeats) = layout(7); % adds player 1 score to player 1 score datasetscore2_data(repeats) = layout(14); % adds player 2 score to player 2 score dataset

结果是:

一个标准的曼卡拉游戏是什么样子的?完成一局游戏平均需要多少回合?最短的游戏是什么?所有这些问题都可以通过 10 万次曼卡拉模拟来回答。

曼卡拉(每坑 4 粒种子)的平均游戏需要 41.42 步才能完成一局。找到的最长的游戏是 77 步,最短的是 11 步(见下面的柱状图)。

图片由作者提供

最短可能的游戏发生在一个玩家很快用完他们所有的种子,然后游戏结束,另一个玩家收集剩下的种子。

每个玩家的获胜概率是多少?先走对你的胜算有影响吗?嗯,开始时每个坑有 4 粒种子,先走的玩家有 48.624%的机会获胜,而另一个玩家有 44.604%的机会获胜(剩下的 6.772%的机会是平局)。

开始游戏时有没有一个最好的第一坑可以选择,以增加自己的胜算?如果你是第一个出手的玩家,选择 6 号坑(最右边的坑)将使你平均每局获得 24.9033 粒种子,比第二高的坑平均多 0.3 粒种子。第二个玩家也一样。第 6 号坑将让他们平均每场比赛获得 23.9039 粒种子。

将曼卡拉推向极限:

开始时每个坑有 100 个种子的游戏是什么样子的?1000 呢?这对胜负概率有什么影响?

我对下面显示的每个初始种子数量(1-10、15、20、50 和 100)运行了 100,000 次模拟,以查看获胜百分比如何变化。

图片由作者提供

随着首发种子数量的增加,比赛平局的几率下降。这是有道理的,因为随着种子数量的增加,商店最终拥有准确数量种子的几率越来越低。

这里值得注意的是获胜概率。第一次行动的玩家和第二次行动的玩家之间的差异似乎变小了,这意味着随着更多种子的加入,游戏变得更加公平。当每个坑有 10 粒种子时,这种差异又跳回来重新开始。这意味着,如果你想要最公平的比赛,每个坑 9 个种子是理想的设置(100 个种子也有一点点差别,但这种游戏将永远持续下去)。

我还想看看开始种子的数量如何影响完成游戏的总平均移动。

图片由作者提供

每场比赛的平均移动不是线性增加的,而是以对数的方式增加的。我将其扩展到 1000 个种子和 5000 个种子,找到了一个 R 值为 0.9715 的最佳拟合方程(如下所示)。

图片由作者提供

假设你从 3 个或更多的种子开始,最佳拟合方程可以用来找出一个游戏在任何初始种子数量下大概要走多少步。

结论:

当您以不同的方式调整初始设置时,Mancala 会出现一些有趣的模式。先走一步可以大大增加你获胜的机会,如果你想降低比赛以平局结束的几率,就多加些种子。观察棋盘游戏的极限以及游戏在这些时刻的反应是非常有趣的。

如果您对这类内容感兴趣,我推荐您阅读我的其他一些关于用 MATLAB 模拟棋盘游戏的文章:

模拟垄断:使用 MATLAB 寻找最佳属性

原文:https://towardsdatascience.com/simulating-monopoly-finding-the-best-properties-using-matlab-130fe557b1ae?source=collection_archive---------14-----------------------

我用 MATLAB 模拟了 1000 万次大富翁游戏来寻找最好的属性

图片由作者提供

简介:

“大富翁”有 40 个空间和 28 种不同的属性,起初看起来完全是随机的,但在游戏过程中,某些属性可能对你更有益。我想通过 MATLAB 模拟 10,000,000 次掷骰子来找出这些属性。

工作原理:

我用随机数发生器模拟两个骰子滚动,然后将两个值相加。然后,我将该金额添加到玩家的当前位置,以获得他们的新位置。我还有一个变量来跟踪一行中的 doubles 数量。如果玩家连续掷出 3 个双打,他们会因为超速被送进监狱。

die1 = randi(6); % roll first diedie2 = randi(6); % roll second dieif die1 == die2 % if same valuedoubles = doubles + 1; % increase number of doubles in a rowelsedoubles = 0; % set doubles to 0 if different dice valuesend

接下来的代码是所有的特殊情况。例如,如果位置加起来大于 40,它将返回到起点作为参考,因为棋盘是一个循环。位置值 43 将变为 3。此外,如果玩家降落在位置 31(“去监狱”空间),他们将被发送到位置 11(监狱)。

if doubles < 3 % if less than 3 doubles in a rowposition = position + die1 + die2; % finds new positionif position > 40 % if above 40position = position - 40; % reverts position back to starting position as a referencearound = around + 1; % indicates the player went around the boardendif position == 31 % if player lands on the "go to jail" spacelands(position) = lands(position) + 1; % indicates player landed on position 31position = 11; % sends player to jailend

其他特殊情况的例子是公益金空间和机会空间。16 张社区公益金卡中,只有 1 张改变了玩家的位置(坐牢)。在 16 张机会卡中,有 9 张会改变玩家的位置:

  • 进监狱
  • 前进到木板路
  • 前进到伊利诺斯大街
  • 前进到圣查尔斯广场
  • 前进到雷丁铁路
  • 前进到最近的铁路
  • 前进到最近的设施
  • 继续前进
  • 后退 3 格

在每一轮结束后,玩家结束的空间被记录在所有 40 个点的数组中,然后每个点被模拟的总量(1000 万)除。这给了我们所有 40 个空间着陆的百分比可能性。

结果:

掷骰子 10,000,000 次后,每个空格落在上面的可能性如下所示:

图片由作者提供

下面显示的是上面条形图中的前 20 个电路板空间:

图片由作者提供

虽然一个属性被登陆的次数确实会影响它给玩家带来的利益,但我们也需要考虑购买该属性的成本,以及玩家每次登陆时会付给你多少钱。

上面显示的概率也可以描述为玩家在每次掷骰子后落在该地产上的机会。我们可以利用它,以及每处房产的租金,来计算如果你拥有该房产,你在每个玩家的掷骰子中将会赚到的预期金额。

例如,Ventnor Ave(如上所示)在每一次掷骰中有 2.691%的几率被击中。Ventnor Ave 没有房子的租金是 22 美元,这意味着每一次掷骰子的结果是所有者预期的 0.59202 美元。

每一卷的收入并不是比较房产的最佳指标,因为一个比 Ventnor 更贵的房产应该产生更多的收入,但事实并非总是如此。如果我们将购买房产的初始成本除以每卷的收入,我们可以找到一笔房产需要多少卷才能达到收支平衡。例如,Ventnor 的购买成本为 260 美元,因此所有者应该预计在 439.18 卷后可以收回所有的钱。不需要太多投资就能达到收支平衡的房产会被认为是更好的投资,因为你可以在其他玩家之前开始盈利。

我们还需要考虑在地产上建造房屋和酒店会如何影响盈亏平衡点。下面是一个垄断委员会上的每个属性的细分,给出了关于房屋和酒店的情况(不包括铁路和公用事业)。每个单元格中的值表示平均收回在该资产上花费的资金所需的滚动量。

图片由作者提供

正如你所看到的,从长远来看,有些房产几乎肯定会让你赔钱。购买地中海大道而不在上面建造任何房屋,在整个游戏过程中肯定会让你损失金钱。这不会是一个显著的数额(只需 60 美元购买),但它仍然不是最好的购买。

所有没有房子的房产都需要非常长的时间才能达到收支平衡,这就是为什么买房子和酒店如此重要。

图片由作者提供

上面的图显示了如何为你拥有的每一个垄断权购买至少 3 个房子,可以大大减少回本所需的滚动次数。即使是开始时是可怕投资的棕色房产(上面的棕色线),在有 3 个或更多房子的游戏中也可以开始盈利。

值得注意的另一点是,随着房产变得越来越贵,3 套房子、4 套房子和一家酒店之间的变化越来越小。以浅蓝色和黄色属性为例:

作者提供的图像(每个单元格中的值代表平均收回在该属性颜色上花费的钱所需的卷数)

淡蓝色的房产在 3 至 4 套房子之间,以及 4 套房子和一家酒店之间的价格有相当大的下降。黄色属性显示最右边的 3 列之间几乎没有变化。这意味着花在 3 套房子上的钱和花在 4 套房子上的钱一样多,但是收支平衡后的收益会更大(对于更贵的房产)。

但是铁路和公用事业呢?

下面显示了铁路,以及您拥有的铁路的所有不同排列。每个单元格中的值表示平均收回在一处或多处房产上花费的钱所需的滚动量。

图片由作者提供

对于仅仅一条铁路的所有权,最好拥有 B&O,因为它会登陆更多。如果你想拥有 2 个,那就去雷丁和 B&O 组合吧。对于 3,寻找阅读,宾夕法尼亚州和 B&O。总的来说,铁路的最佳投资要么获得 3 或全部 4,只是因为人们开始在他们的财产上建造房屋,你的 1 或 2 铁路将不会比游戏早期赚得更多。

至于公用设施:

图片由作者提供

在游戏早期拥有公用事业可能是最好的举措之一。与其他没有房子的房产相比,这两处房产的盈亏平衡点最低。然而,就像铁路一样,你不能在上面建房子,所以随着游戏的进行,这些变得越来越没用。考虑到其他 22 处房产中有 21 处房产的盈亏平衡点低于 3 处房产,即使同时拥有这两处房产,它们的排名仍然垫底。

结论:

橙色的属性是游戏中最好的点。橘子园位于最好的 5 处房产内,有 3 栋房子,是酒店的前 3 名。orange properties New York Ave 和 Tennessee Ave 在最受欢迎的景点中排名第三和第五。

图片由作者提供

木板路和公园广场(深蓝色)是棘手的。Boardwalk 在盈亏平衡的滚动次数方面一直名列前茅,而 Park Place 总是垫底。与橙子相比,这种垄断是一种高风险、高回报的选择。

绝对最差的酒店群体肯定是格林一家。这三家酒店的盈亏平衡营业额都排在后 5 名。就登陆的可能性而言,它们也低于平均水平。

图片由作者提供

只有当你拥有 3 个或者全部 4 个的时候,铁路才会在游戏接近尾声的时候变得有利。如果你能早点得到它们,并开始收支平衡,那么这是一个伟大的举动。公用事业是最好的财产,直到人们开始建造房屋。仅仅依靠公用事业或铁路是无法取胜的,所以不要把所有的精力和金钱都投入其中。

总的来说,这个模拟表明,有一些策略可以应用到你的下一个垄断游戏中。如果你对这种类型的内容感兴趣,我推荐阅读我的其他棋盘游戏文章,我教了一台机器如何玩 Connect 4 ,以及最好和最差的卡坦棋盘设置

用 Python 模拟多智能体群集事件

原文:https://towardsdatascience.com/simulating-multi-agent-swarming-events-in-python-ee133143944d?source=collection_archive---------13-----------------------

以鸟类迁徙为例模拟多主体强化学习分析中的群集现象

成群结队在自然界频繁发生:从一群候鸟到一群执行特定任务的无人机。因此,能够模拟群集如何工作将使您能够构建自己的多智能体强化学习算法,以密切模仿现实世界的场景。为了让你对我们今天正在构建的东西有所了解,下面的 GIF 将是我们使用候鸟为例的群集模拟模型的输出。事不宜迟,我们开始吧!

作者 GIF

注:可以在帖子末尾找到完整代码。

动机

为什么模拟在多智能体强化学习(RL)问题中很重要?这主要是因为模拟是 RL 中的探索性数据分析(EDA)方法之一,它允许我们可视化系统中不同代理的相互关系。在这篇文章中,我们将讨论群集事件,因为它们是多智能体强化学习的许多例子中的一些,我在下面编译了它们在现实生活中是如何行动的!

  1. 成群的鸟
  2. 无人机群
  3. 鱼群
  4. 还有很多其他的!

群集算法:Viscek 模型

我们将实现 Viscek 模型(1995 ),作为我们的多智能体群集基线算法,在此基础上已经构建了许多更高级的实现。

想象你正在试图模拟 300 只鸟的群集( N =300),其中每只鸟的索引为 i=0…( N -1)。一只鸟只有在半径为 R 的圆内与另一只鸟相互作用时,才会改变方向和速度矢量。

交互发生的半径(来源: Viscek )

让我们从导入所需的 Python 库并初始化上述常量开始。

import matplotlib.pyplot as plt
import numpy as npN = 300 # number of birds
R = 0.5 # radius of effect# Preparing the drawing
L = 5   # size of drawing boxfig = plt.figure(figsize=(6,6), dpi=96)
ax = plt.gca()

对于每个时间步, n ,每只鸟沿 x 轴和 y 轴( r )的位置更新如下:

每个时间步长的位置更新(来源: Viscek

这是用 Python 实现的。

Nt = 200 # number of time steps
dt = 0.1 # time step# bird positions (initial)
x = np.random.rand(N,1)*L
y = np.random.rand(N,1)*L

v 为速度恒定的速度矢量, v0 ,角度θ(读作:theta) 我们还将引入一个角度扰动(读作: eta )来模拟鸟类运动的自然随机性。

时间步长 n 时 x 和 y 方向的速度矢量计算(来源: Viscek

速度向量在下面的 Python 中初始化。

v0 = 0.5 # constant speed
eta = 0.6 # random fluctuation in angle (in radians)
theta = 2 * np.pi * np.random.rand(N,1)# bird velocities (initial)
vx = v0 * np.cos(theta)
vy = v0 * np.sin(theta)

现在我们将进入主 for 循环。对于每个时间步长,位置r更新为:

# Update position
x += vx*dt
y += vy*dt

并且通过找到 R 内的(1) 平均角度,(2) 加上随机扰动以产生新的θ,以及(3)更新新的速度向量v 来更新v:

(1)R 内的平均角度

mean_theta = theta# Mean angle within R
for b in range(N):
    neighbors = (x-x[b])**2+(y-y[b])**2 < R**2
    sx = np.sum(np.cos(theta[neighbors]))
    sy = np.sum(np.sin(theta[neighbors]))
    mean_theta[b] = np.arctan2(sy, sx)

(2)给速度角增加一个随机扰动。随机项是通过从[-0.5,0.5]内的正态分布中采样,将其乘以预定义的η,并将该扰动项与计算的平均θ相加而生成的。

正态分布(作者图片)

# Adding a random angle perturbation
theta = mean_theta + eta*(np.random.rand(N,1)-0.5)

(3)用新的θ更新速度矢量:

# Updating velocity
vx = v0 * np.cos(theta)
vy = v0 * np.sin(theta)

总的来说, r 和 v 的更新将在 dt * Nt 个时间步长内完成。

最后,现在一切都准备好了,让我们画出更新的样子来结束吧!

plt.cla()
plt.quiver(x,y,vx,vy,color='r')
ax.set(xlim=(0, L), ylim=(0, L))
ax.set_aspect('equal')
plt.pause(0.001)

如果一切正常,你会在屏幕上看到这个消息。恭喜你!您可以试验并更改一些参数,看看您的模拟模型如何变化。

蜂拥事件(图片由作者提供)

如果你已经到达终点,那就做得很好。你可以在这里找到完整的代码:

离别的思绪

模拟是强化学习中探索性数据分析的一个步骤。一旦您对现实世界的数学行为有了更清晰的认识,您将能够设计更鲁棒的 RL 算法。如果你喜欢这篇文章,我会做更多真实世界的模拟,作为我的多代理 RL EDA 系列的一部分。敬请关注!

https://tinyurl.com/2npw2fnz】订阅我的电子邮件简讯:https://tinyurl.com/2npw2fnz我定期在那里用通俗易懂的英语和漂亮的可视化总结 AI 研究论文。

最后,您可以在这里找到更多关于 RL 和仿真主题的资源:

** https://github.com/AndreasKuhnle/SimRLFab https://deepsense.ai/what-is-reinforcement-learning-the-complete-guide/ https://www.guru99.com/reinforcement-learning-tutorial.html **

用 SimPy 在 Python 中模拟和可视化真实事件

原文:https://towardsdatascience.com/simulating-real-life-events-in-python-with-simpy-619ffcdbf81f?source=collection_archive---------2-----------------------

我们将介绍活动行业完整模型的开发过程,并展示三种不同的可视化结果的方法(包括 AR/VR)

离散事件仿真(DES)已成为专门产品的领域,如 Simulink 8[1]和 MatLab/Simulink [2]。然而,在用 Python 进行分析的时候(过去我会使用 MatLab ),我很想测试 Python 是否也有 DES 的答案。

DES 是一种使用统计函数对现实生活事件建模的方法,通常用于医疗保健、制造、物流和其他领域的队列和资源使用[3]。最终目标是获得关键的运营指标,如资源使用和平均等待时间,以便评估和优化各种实际配置。SIMUL8 有一个描述如何模拟急诊室等待时间的视频[4],MathWorks 有许多教育视频来提供该主题的概述[5],此外还有一个关于汽车制造的案例研究[6]。SimPy [7]库支持在 Python 中描述和运行 des 模型。与 SIMUL8 等软件包不同,SimPy 不是一个用于构建、执行和报告模拟的完整图形环境;但是,它确实提供了执行模拟和输出数据以进行可视化和分析的基本组件。

本文将首先介绍一个场景,并展示如何在 SimPy 中实现它。然后,它将查看可视化结果的三种不同方法:Python 原生解决方案(使用 Matplotlib [8]和 Tkinter [9])、基于 HTML5 canvas 的方法和交互式 AR/VR 可视化。最后,我们将使用 SimPy 模型来评估替代配置。

场景

在我们的演示中,我将使用我以前工作中的一个例子:一个活动的入场队列。然而,遵循类似模式的其他示例可能是在杂货店或接受在线订单的餐馆、电影院、药店或火车站排队。

我们将模拟一个完全由公共交通服务的入口:定期会有一辆公共汽车让几个顾客下车,然后他们需要在进入活动前扫描他们的门票。一些参观者会有他们预先购买的徽章或门票,而其他人则需要先去售票亭购买门票。更复杂的是,当参观者走近售票亭时,他们会成群结队(模拟家庭/团体购票);但是,每个人都需要单独扫描门票。

下面描述了该场景的高级布局。

图片作者和 Matheus Ximenes

为了模拟这种情况,我们需要决定如何使用概率分布来表示这些不同的事件。在我们的实施中所做的假设包括:

  • 平均每三分钟就有一辆公共汽车到达。我们将使用λ为 1/3 的指数分布来表示这一点
  • 每辆巴士将包含 100 +/- 30 名游客,使用正态分布确定(μ = 100,σ = 30)
  • 游客将使用正态分布(μ = 2.25,σ = 0.5)组成 2.25+/–0.5 人的群体。我们将把它四舍五入到最接近的整数
  • 我们假设 40%的访问者需要在销售亭购买门票,另外 40%的访问者将带着已经在线购买的门票到达,20%的访问者将带着员工凭证到达
  • 参观者平均需要一分钟走出巴士,走到卖家摊位(正常,μ = 1,σ = 0.25),再花半分钟从卖家走到扫描仪(正常,μ = 0.5,σ = 0.1)。对于那些跳过卖家的人(预先购买的门票或佩戴徽章的员工),我们假设平均步行时间为 1.5 分钟(正常,μ = 1.5,σ = 0.35)
  • 游客到达时会选择最短的队伍,每一队都有一个卖家或扫描仪
  • 销售需要 1 +/- 0.2 分钟完成(正常,μ = 1,σ = 0.2)
  • 完成一次扫描需要 0.05 +/- 0.01 分钟(正常,μ = 0.05,σ = 0.01)

记住这一点,让我们从输出开始,并从那里向后工作。

作者图片

左手边的图表代表每分钟到达的游客数量,右手边的图表代表在该时刻离开队列的游客在被接待之前需要等待的平均时间。

简单模拟设置

包含完整可运行源代码的存储库可以在https://github.com/dattivo/gate-simulation找到,下面的片段摘自 simpy example.py 文件。在本节中,我们将逐步完成简单的特定设置;但是,请注意,为了将重点放在 SimPy 的 DES 特性上,省略了连接到 Tkinter 进行可视化的部分。

首先,让我们从模拟的参数开始。分析起来最有趣的变量是销售者行数( SELLER_LINES )和每行的销售者数( SELLERS_PER_LINE )以及它们对于扫描器的等价物( SCANNER_LINESSCANNERS_PER_LINE )。此外,请注意两种可能的队列/卖家配置之间的区别:尽管最普遍的配置是拥有多个不同的队列,访问者将选择并停留,直到他们得到服务,但在零售业中看到多个卖家排队也变得更加主流(例如,在一般商品大盒子零售商的快速结账队列)。

配置完成后,让我们开始 SimPy 过程,首先创建一个“环境”、所有队列(资源),并运行模拟(在本例中,直到第 60 分钟)。

请注意,我们正在创建一个 RealtimeEnvironment ,它旨在近乎实时地运行一个模拟,特别是为了在它运行时将它可视化。随着环境的建立,我们生成我们的销售者和扫描器线路资源(队列),然后我们将依次传递给我们的总线到达的“主事件”。 env.process() 命令将开始流程,如下图的 bus_arrival() 函数所述。此函数是顶级事件,所有其他事件都从该事件调度。它模拟每隔 BUS_ARRIVAL_MEAN 分钟到达一辆公交车,车上有 BUS_OCCUPANCY_MEAN 人,然后相应地触发销售和扫描过程。

由于这是顶级事件函数,我们看到该函数中的所有工作都发生在一个无限的 while 循环中。在循环中,我们用 env.timeout() 来“放弃”我们的等待时间。SimPy 广泛使用了生成器函数,这些函数将返回生成值的迭代器。关于 Python 生成器的更多信息可以在[10]中找到。

在循环的最后,我们将分派两个事件中的一个,这取决于我们是直接进入扫描仪,还是随机决定该组需要先购买门票。请注意,我们不会屈服于这些过程,因为这将指示 SimPy 按顺序完成这些操作中的每一个;取而代之的是,所有下车的游客将同时排队。

请注意,使用了 people_ids 列表,以便为每个人分配一个唯一的 ID 用于可视化目的。我们使用 people_ids 列表作为待处理人员的队列;随着游客被分派到他们的目的地,他们从 people_ids 队列中被移除。

purchasing_customer() 函数模拟三个关键事件:走向队列、排队等候,然后将控制传递给 scanning_customer() 事件(与 bus_arrival() 调用的函数相同,用于那些绕过销售者直接进入扫描器的事件)。这个函数根据选择时最短的线来选择它的线。

最后,我们需要实现 scanning_customer() 的行为。这与 purchasing_customer() 函数非常相似,但有一个关键区别:尽管游客可能会成群结队地到达和步行,但每个人都必须单独扫描他们的门票。因此,您将看到对每个被扫描的客户重复扫描超时。

我们将步行持续时间和标准偏差传递给 scanning_customer() 函数,因为这些值会根据访问者是直接走到扫描仪前还是先在卖家处停留而变化。

使用 Tkinter(原生 Python UI)可视化数据

为了可视化数据,我们添加了一些全局列表和字典来跟踪关键指标。例如,到达字典按分钟跟踪到达人数,而 seller_waits 和 scan_waits 字典将模拟的分钟映射到在这些分钟内退出队列的人的等待时间列表。还有一个 event_log 列表,我们将在下一节的 HTML5 Canvas 动画中使用。当关键事件发生时(例如,一个访问者退出队列),调用 simpy example.py 文件中 ANALYTICAL_GLOBALS 标题下的函数来保持这些字典和列表最新。

我们使用一个辅助的 SimPy 事件向 UI 发送一个 tick 事件,以便更新时钟、更新当前的平均等待时间并重新绘制 Matplotlib 图表。完整的代码可以在 GitHub 储存库中找到(https://GitHub . com/dattivo/gate-simulation/blob/master/simpy % 20 example . py);但是,下面的代码片段提供了如何从 SimPy 发送这些更新的框架视图。

使用标准 Tkinter 逻辑来表示用户进出销售者和扫描者队列的可视化。我们创建了 QueueGraphics 类来抽象销售者和扫描者队列的公共部分。这个类中的方法被编码到上一节中描述的 SimPy 事件函数中,以更新画布(例如, sellers.add_to_line(1) ,其中 1 是卖方编号,以及sellers . remove _ from _ line(1))。作为未来的工作,我们可以在流程的关键点使用事件处理程序,这样 SimPy 模拟逻辑就不会与特定于该分析的 UI 逻辑紧密耦合。

使用 HTML5 画布制作数据动画

作为另一种可视化方式,我们希望从 SimPy 模拟中导出事件,并将它们放入一个简单的 HTML5 web 应用程序中,以便在 2D 画布上可视化场景。我们通过在简单事件发生时添加一个 event_log 列表来实现这一点。特别是,公交车到达、步行到销售者、在销售者队伍中等待、购买门票、步行到扫描仪、在扫描仪队伍中等待和扫描门票事件都被记录为单独的字典,然后在模拟结束时导出到 JSON。你可以在这里看到一些示例输出:https://github . com/dattivo/gate-simulation/tree/master/output

我们开发了一个快速的概念验证来展示如何将这些事件转化成 2D 动画,你可以在 https://dattivo.github.io/gate-simulation/visualize.html 进行实验。你可以在https://github . com/dattivo/gate-simulation/blob/master/visualize . ts中看到动画逻辑的源代码。

作者图片

这种可视化得益于动画,然而,出于实际目的,基于 Python 的 Tkinter 接口组装起来更快,Matplotlib 图形(可以说是该模拟最重要的部分)在 Python 中设置起来也更平滑和更熟悉。也就是说,让行为生动起来是有价值的,尤其是在向非技术利益相关者传达结果的时候。

使用虚拟现实制作数据动画

让画布动画更进一步, Matheus Ximenes 和我一起工作,使用 HTML5 画布也在使用的相同 JSON 模拟数据,将以下 AR/VR 3-D 可视化放在一起。我们使用我们已经熟悉的 React [11]和 A-FRAME [12]实现了这一点,A-FRAME 令人惊讶地易于使用和学习。

你可以在 https://www.dattivo.com/3d-visualization/的亲自测试模拟

分析卖方/扫描仪队列配置备选方案

虽然这个例子是为了演示如何创建和可视化 SimPy 模拟,但是我们仍然可以展示几个例子来说明平均等待时间如何依赖于队列的配置。

让我们从上面动画中演示的案例开始:六个销售人员和四个扫描仪,每行一个销售人员和一个扫描仪(6/4)。60 分钟后,我们看到卖家的平均等待时间为 1.8 分钟,而扫描仪的平均等待时间为 0.1 分钟。从下面的图表中,我们看到销售者的时间在大约 6 分钟的等待时达到峰值。

我们可以看到,卖家们都是一致备份的(虽然 3.3 分钟可能不会太不合理);所以,让我们看看,如果我们再增加四个卖家,总数达到 10 个,会发生什么。

正如预期的那样,卖家的平均等待时间减少到了 0.7 分钟,最长等待时间减少到了 3 分钟多一点。

现在,让我们说,通过降低在线门票的价格,我们能够将持票到达的人数提高 35%。最初,我们假设所有访客中有 40%需要购买门票,40%已经在线预购,20%是凭凭证进入的工作人员和供应商。因此,随着 35%以上的人带着票到达,我们将需要购买的人数减少到 26%。让我们用初始的 6/4 配置来模拟一下。

在这种情况下,卖家的平均等待时间减少到 1.0 分钟,最长等待时间超过 4 分钟。在这种情况下,增加 35%的在线销售额与增加更多的卖家队列有相似的效果;如果等待时间是我们最想减少的指标,那么在这一点上,我们可以考虑这两个选项中哪一个具有更强的业务案例。

结论和未来工作

Python 可用的数学和分析工具的广度令人生畏,SimPy 完善了这些功能,还包括离散事件模拟。与 SIMUL8 等商业打包工具相比,Python 方法确实留给编程更多的空间。对于快速分析来说,组装模拟逻辑和从头构建 UI 和度量支持可能是笨拙的;然而,它确实提供了很大的灵活性,对于已经熟悉 Python 的人来说应该相对简单。如上所述,SimPy 提供的 DES 逻辑产生了清晰易读的代码。

如前所述,Tkinter 可视化是三种演示方法中最直接的一种,尤其是在包含 Matplotlib 支持的情况下。HTML5 canvas 和 AR/VR 方法可以方便地将可共享的交互式可视化放在一起;然而,他们的发展并不平凡。

比较队列配置时需要考虑的一个重要改进是销售者/扫描者利用率。减少排队时间只是分析的一个组成部分,因为在得出最佳解决方案时,还应该考虑销售者和扫描仪空闲时间的百分比。此外,如果有人看到队列太长而选择不进入,添加一个概率也会很有趣。

参考

[1]https://www.simul8.com/

[2]https://www . mathworks . com/solutions/discrete-event-simulation . html

[3]https://en.wikipedia.org/wiki/Discrete-event_simulation

https://www.simul8.com/videos/

[5]https://www . mathworks . com/videos/series/understanding-discrete-event-simulation . html

[6]https://www . mathworks . com/company/newslettes/articles/optimizing-automotive-manufacturing-processes-with-discrete-event-simulation . html

https://simpy.readthedocs.io/en/latest/

https://matplotlib.org/

[9]https://docs.python.org/3/library/tkinter.html

https://wiki.python.org/moin/Generators

[11]https://reactjs.org/

[12]https://aframe.io/

(本文改编自之前发表的两篇文章:https://dattivo . com/simulating-real-life-events-in-python-with-simpy/https://dattivo . com/3d-visualization-of-a-simulated-real-life-events-in-virtual-reality/)

在线性建模中模拟多重共线性的影响

原文:https://towardsdatascience.com/simulating-the-effect-of-multicollinearity-in-linear-modelling-using-r-purrr-parallel-computing-756a365896d?source=collection_archive---------48-----------------------

使用 R、purrr 和并行计算

在数据科学中,使用线性回归有时会被低估。作为 t 检验和方差分析等非常基本的统计概念的概括,它与统计学领域有着很深的联系,可以作为解释数据方差的强大工具。由于线性模型只有参数是线性的,所以也可以描述多项式甚至一些乘法关系。

但是,由于参数的性质,线性回归也更容易受到极值和数据中多重共线性的影响,我们希望通过模拟对后者进行更详细的分析。在许多其他问题中,多重共线性可能产生不稳健的模型(交错系数),并可能破坏统计显著性。

在运行模拟之前,我们将研究多重共线性对简单线性模型的影响。我们将使用来自 Stat Trek 的数据集,其中包含学生的考试成绩和一些其他表现指标(如智商或平均绩点):

我们观察到所有变量之间的高度相关性,尤其是iqgpa相关性非常强:

GGally::ggpairs(data)

成对测量图—图片由作者完成

如果我们使用iqstudy_hours拟合一个简单的模型来解释测试分数,我们可以看到它们的影响在统计上是显著的(p 值约为 0.03,总体显著性):

simple_model <- lm(test_score ~ iq + study_hours, data = data)
sqrt(mean((simple_model$residuals)^2))
## [1] 3.242157
summary(simple_model)
## 
## Call:
## lm(formula = test_score ~ iq + study_hours, data = data)
## 
## Residuals:
##    Min     1Q Median     3Q    Max 
## -6.341 -1.130 -0.191  1.450  5.542 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)  
## (Intercept)  23.1561    15.9672   1.450   0.1903  
## iq            0.5094     0.1808   2.818   0.0259 *
## study_hours   0.4671     0.1720   2.717   0.0299 *
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 3.875 on 7 degrees of freedom
## Multiple R-squared:  0.9053, Adjusted R-squared:  0.8782 
## F-statistic: 33.45 on 2 and 7 DF,  p-value: 0.0002617

如果我们将gpa加入到模型中,我们预计会得到一个更小的(样本内)误差,但截距和斜率的估计值会有很大不同。另外,iqgpa不再重要。这是由于它们的共线性,在这里有很好的描述

# estimates and p values very different
full_model <- lm(test_score ~ iq + study_hours + gpa, data = data)sqrt(mean((full_model$residuals)^2))
## [1] 3.061232summary(full_model)
## 
## Call:
## lm(formula = test_score ~ iq + study_hours + gpa, data = data)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -6.3146 -1.2184 -0.4266  1.5516  5.6358 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)  
## (Intercept) 50.30669   35.70317   1.409   0.2085  
## iq           0.05875    0.55872   0.105   0.9197  
## study_hours  0.48876    0.17719   2.758   0.0329 *
## gpa          7.37578    8.63161   0.855   0.4256  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 3.952 on 6 degrees of freedom
## Multiple R-squared:  0.9155, Adjusted R-squared:  0.8733 
## F-statistic: 21.68 on 3 and 6 DF,  p-value: 0.001275BIC(full_model, simple_model)
##              df      BIC
## full_model    5 62.26804
## simple_model  4 61.11389

在正常的预测建模场景中,我们将通过使用正则化来自动进行特征选择,从而在保持良好的可预测性的同时,提高我们模型的泛化能力和鲁棒性,从而尝试避免这种情况。在经典统计学中,我们不仅关心可预测性(低误差),还关心变量的统计显著性(p 值)和因果关系。

这里的传统方法是使用方差膨胀因子从等式中移除变量。我们不仅仅着眼于预测因子的成对相关性,而是试图着眼于预测因子的线性相关性:我们本质上试图通过所有其他预测因子来估计一个预测因子,然后方差膨胀因子作为未解释方差的倒数给出(即1 / (1 - R.sq.))。如果预测值可以由其他预测值线性表示,则该值较高,否则该值较低。根据经验,如果它高于 4,则该预测值可能会被丢弃。

vif_gpa_model <- lm(gpa ~ iq + study_hours, data = data)
vif_gpa_factor <- 1 / (1 - summary(vif_gpa_model)$r.squared)
vif_gpa_factor
## [1] 19.65826

我们可以很容易地对所有的预测值都这样做,并决定去掉高膨胀系数的特征。

car::vif(full_model)
##          iq study_hours         gpa 
##   22.643553    2.517786   19.658264

领域知识可能会告诉我们,GPA 是智商的结果,而不是智商的结果,因此将智商作为一个独立变量可能比使用代理变量 GPA 更合理。

模拟 1:用 purrr 运行“许多模型”

为了了解数据中多重共线性的有害影响,让我们模拟一下当我们重复拟合具有不同特征的线性回归时,模型系数可能会发生什么情况。

为了做到这一点,我们将使用purrr,它帮助我们在数据帧列表的顶部以矢量化的方式运行我们自己的函数,并因此在数据和/或模型上运行(推荐阅读:R 中的用于数据科学以及官方文档和purrr)。

为了理解我们的模拟,让我们首先模拟从简单的线性关系y = 5 + 3 * x中提取一些虚拟数据,并多次拟合线性模型。我们创建了一些辅助函数来绘制可重复的样本并拟合模型:

generate_dummy_data <- function(data, seed = NULL) {
  set.seed(seed = seed)
  data <- dplyr::tibble(x = rnorm(100, 50, 10),
                        y = 5 + 3 * x + rnorm(100, 0, 0.1))
  return(data)
}generate_dummy_model <- function(data) {
  model <- lm(y ~ x, data = data)
  return(model)
}

我们现在可以多次运行该模拟,并使用purrr::map提取模型系数(截距和斜率):

我们看到估计值非常稳定,接近真实值。

dummy_simulation %>%
  tidyr::unnest(coefs) %>% 
  ggplot(aes(x = estimate)) + 
  geom_histogram(bins = 30) + 
  facet_wrap(~ term, scales = "free") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

非常简单的线性关系的估计分布—图片由作者完成

现在,让我们通过使用自举样本,将此应用于我们的测试分数数据:

我们不仅要看一个模型,还要比较两个模型:一个是使用所有特征的完整模型(即存在多重共线性),另一个是使用简化特征集的模型:

让我们绘制两个模型中截距和斜率的估计值(使用ggplot2theme_set(theme_linedraw()))。我们看到截距的巨大差异,还有iq-系数:

model_coefs <- 
  simulations %>% 
  dplyr::select(name, coefs) %>% 
  tidyr::unnest(coefs)model_coefs %>% 
  ggplot(aes(x = estimate, color = name)) + 
  geom_freqpoly(bins = 30) + 
  facet_wrap( ~ term, scales = "free")

完整和简单模型的系数分布——作者完成的图像

p 值看起来也不是很有希望,模型很难获得关于iqgpa相关性的信心,p 值没有真正集中在低端:

model_coefs %>% 
  ggplot(aes(x = p_value, color = name)) + 
  geom_freqpoly(bins = 30) + 
  facet_wrap( ~ term) +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

完整和简单模型的 p 值分布—图片由作者制作

模拟 2:并行模型评估

让我们在一个稍大的数据集上运行另一个模拟,在这里我们额外计算一些测试数据误差度量。

我们将以稍微不同的方式执行模拟:我们将并行评估不同的模型,并且由于我们只对系数估计和误差度量感兴趣,我们将避免从工人传输太多数据,并且不存储训练数据和模型对象本身。

让我们来看一下鱼市场数据集,它由不同种类的不同鱼类尺寸(长、宽、高)及其重量组成。测量值再次高度相关。我们将尝试预测权重,并在此过程中使用一些简单的领域知识。让我们去除一些明显的异常值,即测量误差。

url <- "[https://tinyurl.com/fish-market-dataset](https://tinyurl.com/fish-market-dataset)"
raw_data <- readr::read_csv(url)
## 
## ── Column specification ────────────────────────────────────────────────────────
## cols(
##   Species = col_character(),
##   Weight = col_double(),
##   Length1 = col_double(),
##   Length2 = col_double(),
##   Length3 = col_double(),
##   Height = col_double(),
##   Width = col_double()
## )data <- 
  raw_data %>% 
  dplyr::rename_all(tolower) %>% 
  dplyr::mutate(species = tolower(species)) %>% 
  dplyr::filter(weight > 0)dplyr::sample_n(data, 5)
## # A tibble: 5 x 7
##   species weight length1 length2 length3 height width
##   <chr>    <dbl>   <dbl>   <dbl>   <dbl>  <dbl> <dbl>
## 1 perch    700      34.5    37      39.4  10.8   6.26
## 2 smelt      8.7    10.8    11.3    12.6   1.98  1.29
## 3 perch    820      37.1    40      42.5  11.1   6.63
## 4 bream    700      30.4    33      38.3  14.9   5.29
## 5 perch     78      16.8    18.7    19.4   5.20  3.12GGally::ggpairs(data) +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

所有测量的混沌对图—图片由作者完成

data %>% 
  ggplot(aes(x = length1, y = weight)) + 
  geom_point() + 
  geom_smooth(method = "loess")
## `geom_smooth()` using formula 'y ~ x'

长度 1 与重量—图片由作者制作

由于重量与体积成比例,我们可能会找到类似于weight ~ length^3的关系,因此我们可以拟合log(weight) ~ log(length),这将导致log(weight) ~ a + b * log(length)的估计值a, b。因此,在求幂运算weight ~ exp(a) * length^b之后,我们期望b大约为 3,而a为某个比例因子:

simple_log_log_model <- lm(log(weight) ~ log(length1), data = data)summary(simple_log_log_model)
## 
## Call:
## lm(formula = log(weight) ~ log(length1), data = data)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -0.90870 -0.07280  0.07773  0.26639  0.50636 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  -4.62769    0.23481  -19.71   <2e-16 ***
## log(length1)  3.14394    0.07296   43.09   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.3704 on 156 degrees of freedom
## Multiple R-squared:  0.9225, Adjusted R-squared:  0.922 
## F-statistic:  1857 on 1 and 156 DF,  p-value: < 2.2e-16

很公平,b在 3.14 左右。添加heightwidth可能会再次提高模型性能。在我们开始之前,让我们快速构建另一个没有对数变换的简单模型,通过查看残差图来了解由于非线性关系和不断增加的方差而导致的不良拟合。

naive_model <- lm(weight ~ length1 + width + height + species, 
                  data = data)par(mfrow=c(2,2))
plot(naive_model)

朴素模型的残差诊断—由作者完成的图像

# plot residuals against all features
data %>% 
  dplyr::mutate(residual = naive_model$residuals) %>% 
  dplyr::select(-species) %>% 
  # convert to wide to long for plotting
  tidyr::gather(key, value, -residual) %>% 
  ggplot(aes(x = value, y = residual)) +
  geom_point() +
  facet_wrap(~ key, scales = "free")

朴素模型的剩余图——作者完成的图像

现在回到双对数模型,我们假设它会表现得更好。

log_log_model <- lm(log(weight) ~ log(length1) + log(width) + log(height), data = data)par(mfrow=c(2,2))
plot(log_log_model)

对数模型的残差诊断—图片由作者完成

data %>% 
  dplyr::mutate(residual = log_log_model$residuals) %>% 
  dplyr::select(-species) %>% 
  tidyr::gather(key, value, -residual) %>% 
  ggplot(aes(x = value, y = residual)) +
  geom_point() +
  facet_wrap(~ key, scales = "free")

对数-对数模型的残差图—图片由作者完成

我们在残差中没有看到趋势或严重的漏斗效应。一般来说,当我们预期 x%的增加将导致 y%的响应增加时,双对数模型是有意义的

我们使用caret::dummyVars对我们的species特性进行一键编码(在lm中,我们也可以使用因子,但是那些不能与glmnet一起工作):

one_hot_encoder <- caret::dummyVars( ~ ., data = data)
data <- dplyr::as_tibble(predict(one_hot_encoder, newdata = data))

我们将实际对我们的特征进行对数变换,而不是使用 R 公式:

numeric_features <- c("length1", "length2", "length3", "height", "width")data <- 
  data %>% 
  dplyr::mutate_at(numeric_features, log) %>% 
  dplyr::rename_at(numeric_features, function(x) paste0(x, "_log"))

x <- data %>% dplyr::select(-weight)
y <- data %>% dplyr::pull(weight)generate_data <- function(seed=NULL, test_size=1/4) {
  n <- nrow(x)
  set.seed(seed)
  train_idx <- sample(1:n, size = (1-test_size) * n)

  return(list(x_train = x[train_idx,],
              x_test = x[-train_idx,],
              y_train = y[train_idx],
              y_test = y[-train_idx]))
}

在正常的线性模型之上,我们还将使用glmnet包来拟合正则化的线性模型(山脊、套索和弹性网)。我们创建了一个包装器函数,可以用来传递种子和带有模型参数的命名列表。然后,函数本身将使用上面定义的函数根据种子生成数据。

让我们用一些参数来测试我们的函数:

params <- list(features = c("length1_log", "height_log", "width_log"))fit_glmnet(seed = 1, params = params)
## $coefs
## # A tibble: 4 x 2
##   term        estimate
##   <chr>          <dbl>
## 1 (Intercept)   -1.95 
## 2 length1_log    1.50 
## 3 height_log     0.652
## 4 width_log      0.876
## 
## $rmse
## [1] 26.5608

用相同的种子和参数再次拟合将产生相同的估计:

fit_glmnet(seed = 1, params = params)
## $coefs
## # A tibble: 4 x 2
##   term        estimate
##   <chr>          <dbl>
## 1 (Intercept)   -1.95 
## 2 length1_log    1.50 
## 3 height_log     0.652
## 4 width_log      0.876
## 
## $rmse
## [1] 26.5608

我们现在可以使用purrr::map轻松运行多次模拟,并使用purrr::map_dbl提取错误。

simulations <- purrr::map(1:1000, fit_glmnet)
rmse <- purrr::map_dbl(simulations, "rmse")
hist(rmse)

模拟的误差分布—图片由作者完成

现在让我们用不同的参数组合来运行模拟。我们将把我们的参数添加到数据帧中,以便能够在模型和种子上按行并行运行模拟:

params <- list(ridge = list(lambda = 0.1, alpha = 0),
               lasso = list(lambda = 0.01, alpha = 1),
               elastic_net = list(lambda = 0.01, alpha = 0.5),
               full = list(lambda = 0),
               simple = list(features = c("length1_log",    
                                          "width_log", 
                                          "height_log"),
                             lambda = 0))params_df <- dplyr::tibble(name=factor(names(params)),
                           params=params)params_df
## # A tibble: 5 x 2
##   name        params          
##   <fct>       <named list>    
## 1 ridge       <named list [2]>
## 2 lasso       <named list [2]>
## 3 elastic_net <named list [2]>
## 4 full        <named list [1]>
## 5 simple      <named list [2]>

我们现在将添加和取消种子嵌套:

n_simulations <- 1000
n_workers <- 10simulations <- 
  params_df %>% 
  dplyr::mutate(seed = list(1:n_simulations)) %>% 
  tidyr::unnest(seed)head(simulations)
## # A tibble: 6 x 3
##   name  params            seed
##   <fct> <named list>     <int>
## 1 ridge <named list [2]>     1
## 2 ridge <named list [2]>     2
## 3 ridge <named list [2]>     3
## 4 ridge <named list [2]>     4
## 5 ridge <named list [2]>     5
## 6 ridge <named list [2]>     6

使用该数据帧,我们现在可以使用furrr::future_map2作为purrr::map2的并行版本,按行并行运行模拟。我们通过将furrr::furrr_options(seed = NULL)传递给函数调用来通知furrr我们已经设置了种子。

tictoc::tic()future::plan(future::multisession, workers = n_workers)simulations <- 
  simulations %>% 
  dplyr::mutate(simulation = furrr::future_map2(.x = seed, 
                                                .y = params, 
                                                .f = fit_glmnet, 
                                                .options = furrr::furrr_options(seed = NULL))) %>% 
  dplyr::mutate(rmse = purrr::map_dbl(simulation, "rmse"),
                coefs = purrr::map(simulation, "coefs"))tictoc::toc()
## 6.596 sec elapsed

让我们看看不同模型的误差分布:

simulations %>% 
  ggplot(aes(x = rmse, color = name)) +
  geom_boxplot()

不同模型的误差—图片由作者制作

我们看到,完整模型表现最好,但正则化模型的表现不会差太多。

simulations %>% 
  dplyr::group_by(name) %>% 
  dplyr::summarise(rmse_q90 = quantile(rmse, probs = 0.9),
                   rmse = median(rmse)) %>% 
  dplyr::ungroup() %>% 
  dplyr::mutate(perc = rmse / min(rmse)) %>% 
  dplyr::arrange(perc)
## # A tibble: 5 x 4
##   name        rmse_q90  rmse  perc
##   <fct>          <dbl> <dbl> <dbl>
## 1 full            72.5  53.4  1   
## 2 ridge           74.4  53.6  1.00
## 3 lasso           76.5  53.9  1.01
## 4 elastic_net     74.1  54.2  1.02
## 5 simple          83.9  61.0  1.14

尽管不同模型的系数变化很大,但不同模拟的系数也不同,尤其是完整模型:

model_coefs <- 
  simulations %>% 
  dplyr::select(name, coefs) %>% 
  tidyr::unnest(coefs)model_coefs %>%   
  ggplot(aes(x = estimate, color = name)) + 
  geom_freqpoly(bins = 30) +
  facet_wrap(~ term, scales = "free", ncol = 2) +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

不同模型的系数估计—图片由作者完成。

结论

我们看到了多重共线性如何给线性回归中的参数估计带来麻烦,例如在参数的高方差中寻找表达式或缺乏统计显著性。与此同时,我们看到,这并不一定会导致更差的模型性能,如均方根误差等测试误差指标。

我们还学习了一些处理这些问题的经典技术,例如查看方差膨胀因子、进行简单的特征选择或应用正则化。还有许多其他有用的技术,如降维、逐步或基于树的特征选择。

此外,许多其他更复杂的机器学习模型对这些问题更稳健,因为它们在拟合过程中结合了信息增益和正则化,或者使用不同的数据表示,这些数据表示不太容易受到单次观察和数据内相关性的影响。例如,像树模型这样的非参数候选模型,有时还有像神经网络这样的“极端参数”模型。

但是,由于线性回归允许我们从数据中得出强有力的见解和结论,并且通常作为一个简单的基准模型是有用的,所以理解它的统计主干和它的一些缺点是非常有帮助的。

最初发表于【https://blog.telsemeyer.com】

模拟 NBA:2021–22 赛季结果

原文:https://towardsdatascience.com/simulating-the-nba-2021-22-season-results-a0884b13690a?source=collection_archive---------26-----------------------

我用蒙特卡罗方法模拟了 1230 场比赛和季后赛。你的队会赢几次?

图片由作者提供

简介:

随着 NBA 2021-22 季前赛的开始,我开始模拟每支球队的 82 场比赛,加上季后赛,以找出联盟最好和最差的表现。

NBA 编码:

类似于我的 NFL 赛季模拟,所有 30 支球队都有一个评分。今年的范围从 1304(克利夫兰骑士队)开始,到 1752(密尔沃基雄鹿队),其他球队在两者之间。

每个队赢得比赛的机会是由这个等级决定的。以赛季揭幕战为例:主场作战的雄鹿(1752)对篮网(1717)。

图片由作者提供

利用这些等式,雄鹿有 55.20%的胜算,而篮网有 44.80%的胜算。

一个随机数生成器决定获胜者,然后根据他们是否处于劣势来调整两个评级。如果雄鹿击败了网队,那么他们的排名会比网队击败雄鹿时增加的少,这纯粹是因为雄鹿有望获胜。这使得球队在整个赛季中获得动力。这方面的代码如下所示:

for i = 1:length(home_schedule)Ha = 1/(1+10^((team_data(home_schedule(i),1) - team_data(away_schedule(i),1) + 2.5) * -1/400)); % approx home team chance of winningAa = 1/(1+10^((-1 * team_data(home_schedule(i),1) + team_data(away_schedule(i),1)) * -1/400)); % approx away team chance of winningT = Ha + Aa - 1; % chance of a tieH = Ha - T/2; % adjusted home team winning chancesA = Aa - T/2; % adjusted away team winning chancesteam_data(home_schedule(i),1) = team_data(home_schedule(i),1) + 20 * (HW - H); % adjusts home team's ranking based on resultteam_data(away_schedule(i),1) = team_data(away_schedule(i),1) + 20 * (AW - A); % adjusts away team's ranking based on resultend

这种模式适用于所有 1230 场比赛,以决定季后赛的赛程。NBA 季后赛由来自每个联盟的 10 支球队组成。每个小组的前 6 名将自动进入第一轮,而 7-10 号种子将在一个小范围内决出第 7 和第 8 号种子。一旦两个联盟的所有 8 支球队都确定下来,季后赛就像一个标准的括号一样开始,两个联盟总决赛的获胜者将在冠军赛上相遇。每场比赛都是 7 场系列赛中的一场,最高分的种子将获得第 1、2、5 和 7 场比赛的主场比赛(如果必要的话)。

常规赛记录、季后赛记录和季后赛机会的所有数据都被记录下来,因为赛季重复了 50,000 次。

结果是:

下面显示了所有 30 支球队最有可能的常规赛记录,以及 50,000 个模拟赛季后的统计数据。最小和最大列代表该队在一个赛季中最少和最多的胜利。10%、50%和 90%是所有 50,000 次模拟的百分位数。

图片由作者提供

迈阿密热火队预计将取得 43 胜 39 负的战绩,有 10%的几率他们会赢不到 33 场比赛,有 10%的几率他们会赢超过 55 场比赛。这使得他们有 80%的机会赢得 33 到 55 场比赛。

每支球队的季后赛机会如下所示:

图片由作者提供

第一列代表那支球队进入前 10 名的机会,这意味着他们进入了季后赛。下一列是一个队进入第一轮比赛的机会(前 8)。

只有一支球队在所有 50,000 场比赛中没有赢得一个总冠军,那就是克利夫兰骑士队。奥兰多魔术队和俄克拉荷马雷霆队都只获得过一次总冠军。

NBA 总决赛夺冠热门前 3 名分别是密尔沃基雄鹿队(16.150%)、洛杉矶快船队(12.158%)、菲尼克斯太阳队(11.802%)。

我也对一支球队在 NBA 历史上拥有最好或最差赛季的几率感兴趣。至少有一支球队打破赛季胜利纪录的几率为 6.44%,其中密尔沃基雄鹿队占这些赛季的 34.70%。接下来两个可能的球队是洛杉矶快船(14.20%)和菲尼克斯太阳(13.77%)。

至少有一支球队打破赛季亏损纪录的几率为 12.08%,其中克利夫兰骑士队占这些赛季的 40.31%。接下来两个可能的球队是奥兰多魔术队(19.20%)和俄克拉荷马雷霆队(16.55%)。

在所有 50,000 个模拟赛季中,只有一支球队有机会打破赛季输赢记录。金州勇士队有 0.026%的几率输掉 NBA 历史上最多的比赛,但有 0.031%的几率赢得 NBA 历史上最多的比赛。

结论:

这个 NBA 赛季模拟显示,密尔沃基雄鹿队显然是今年 NBA 总决赛的热门球队(16.15%),即使西部联盟占据了大多数冠军(54.05%)。

一支球队经历 NBA 历史上最糟糕赛季的几率几乎是一支球队经历 NBA 历史上最好赛季的两倍。

在所有 50,000 次模拟中,只有一支球队赢得了零冠军,那就是克利夫兰骑士队,预计战绩为 17 胜 65 负。

如果你对这类内容感兴趣,我推荐你阅读我的另一篇关于职业体育模拟的文章,NFL 模拟

用 Python 模拟交通流

原文:https://towardsdatascience.com/simulating-traffic-flow-in-python-ee1eab4dd20f?source=collection_archive---------0-----------------------

实践教程

实现微观交通模型

约翰·马特丘克在 Unsplash 上的照片

虽然交通并不总是顺畅,但汽车无缝穿越十字路口,在交通信号灯前转弯和停车,看起来会非常壮观。这种沉思让我想到了交通流量对人类文明的重要性。

在这之后,我内心的书呆子忍不住想了一个模拟交通流量的方法。我花了几周时间做一个关于交通流量的本科项目。我深入研究了不同的模拟技术,最终选择了一种。

在这篇文章中,我将解释为什么流量模拟是重要的,比较不同的方法可能建模流量,并提出我的模拟(连同源代码)。

为什么要模拟交通流?

模拟交通的主要原因是在没有真实世界的情况下生成数据。您可以使用软件上运行的模型来预测交通流量,而不是测试如何在现实世界中管理交通系统或使用传感器收集数据的新想法。这有助于加速交通系统的优化和数据收集。模拟是真实世界测试的一种更便宜、更快速的替代方式。

训练机器学习模型需要庞大的数据集,收集和处理这些数据集可能很困难,成本也很高。通过模拟交通按程序生成数据可以很容易地适应所需数据的确切类型。

建模

为了分析和优化交通系统,我们首先要对交通系统进行数学建模。这种模型应根据输入参数(路网几何形状、每分钟车辆数、车速等)真实地表现交通流量。

微观模型(左)宏观模型(右)。图片作者。

交通系统模型通常分为三类,这取决于它们运行的级别:

  • 微观模型:分别代表每一辆车,试图复制驾驶员的行为。
  • 宏观模型:用交通密度(每公里车辆数)和交通流量(每分钟车辆数)来描述车辆整体的运动。它们通常类似于流体流动。
  • 介观模型:是结合了微观和宏观模型特点的混合模型;他们将流量建模为车辆的“数据包”。

在本文中,我将使用一个微观模型。

微观模型

微观驾驶员模型描述了单个驾驶员/车辆的行为。因此,它必须是一个多智能体系统,也就是说,每辆车都使用来自其环境的输入来独立运行。

图片作者。

在微观模型中,每辆车都有编号 i 。第 i 辆跟随第( i-1 )辆。对于第 i 辆车,我们将用 xᵢ 表示其在道路上的位置, vᵢ 表示其速度, lᵢ 表示其长度。每辆车都是如此。

我们将用 sᵢ 表示保险杠到保险杠的距离,用δvᵢ表示第 i 辆车与其前车(车号 i-1 )之间的速度差。

智能驱动模型(IDM)

2000 年,Treiber、Hennecke 和 Helbing 开发了一种被称为智能驾驶模型的模型。它将第 i 辆车的加速度描述为其变量和其前方车辆变量的函数。动力学方程定义为:

在我解释这个模型背后的直觉之前,我应该解释一些符号代表什么。

我们已经讲过 sᵢvᵢ、t31】δvᵢ。其他参数是:

  • s₀ᵢ :车辆 ii-1 之间的最小期望距离。
  • v₀ᵢ : 是车辆的最大期望速度 i.
  • δ : 是加速指数,它控制加速的“平滑度”。
  • Tᵢ : 是第辆车驾驶员的反应时间。
  • aᵢ : 为车辆的最大加速度一、
  • bᵢ : 是车辆 i. 的舒适减速度
  • s* :车辆 ii-1 之间的实际所需距离。

首先,我们将查看 s* ,它是一个距离,由三项组成。

图片作者。

  • s₀ᵢ : 前面说过,是最小期望距离。
  • vᵢTᵢ : 是反应时间安全距离。它是驾驶员做出反应(刹车)之前车辆行驶的距离。
    既然速度是距离随时间的变化,那么距离就是速度乘以时间。

  • (vᵢδvᵢ)/√(2aᵢbᵢ):这是一个有点复杂的术语。这是基于速度差的安全距离。它代表车辆减速(不撞上前方车辆),不过度制动(减速度应小于 bᵢ )所需的距离。

智能驱动模型如何工作

假设车辆沿直线行驶,并遵循以下等式:

为了更好地理解这个方程,我们可以把它分成两部分。我们有一个自由道路加速度和一个交互加速度

自由道路加速度是自由道路上的加速度,也就是前方没有车辆的空旷道路。如果我们将加速度绘制成速度 vᵢ 的函数,我们得到 :

加速度是速度的函数。图片作者。

我们注意到,当车辆静止时( vᵢ=0 ),加速度最大。当车速接近最大速度 v₀ᵢ 时,加速度变为 0。这表明自由道路加速将使车辆加速至最大速度。

如果我们为不同的 δ、值绘制 v-a 图,我们会注意到,它控制着驾驶员在接近最大速度时减速的速度。这又控制加速/减速的平滑度

加速度是速度的函数。图片作者。

互动加速度与前方车辆互动相关。为了更好地理解它是如何工作的,让我们考虑以下情况:

  • 在自由路面上(sᵢ > > s*): 当前方车辆很远,也就是距离 sᵢ 是主导期望距离 s* 时,交互加速度几乎为 0。
    这意味着车辆将受到自由道路加速度的控制。

  • 在高接近速率(δvᵢ): )下,当速度差很大时,交互加速度试图通过使用分子中的(vᵢδvᵢ)项制动或减速来补偿,但太难了。这是通过分母 4bᵢsᵢ 实现的。(老实说,我不知道它如何将减速精确地限制在 bᵢ

  • 在小距离差时(sᵢ < < 1 和δvᵢ≈0): )加速度变成简单的排斥力。

交通道路网络模型

有向图的例子。图表(左)集合(右)

我们需要建立一个道路网的模型。为此,我们将使用一个有向图 G=(V,E) 。其中:

  • V 是顶点(或节点)的集合。
  • E 是代表道路的边的集合。

每辆车都有一条由多条道路(边)组成的路径。我们将对同一条道路(同一条边)上的车辆应用智能驾驶员模型。当一辆车到达路的尽头时,我们把它从那条路上移走,并把它附加到下一条路上。

在模拟中,我们不会保留一组节点(数组),相反,每条道路都将由其起始和结束节点的值明确定义。

随机车辆生成器

为了将车辆添加到我们的模拟中,我们有两个选项:

  • 通过创建一个新的Vehicle类实例并将其添加到车辆列表中,将每辆车手动添加到模拟中。
  • 根据预定义的概率随机添加车辆。

对于第二个选项,我们必须定义一个随机车辆生成器。

随机车辆生成器由两个约束条件定义:

  • 车辆生成率(τ): (每分钟车辆数)描述平均每分钟应添加多少车辆到模拟中。
  • 车辆配置列表(L): 包含车辆配置和概率的元组列表。L = [(p₁,V₁),(p₂,V₂),(p₃,V₃),…]

随机车辆生成器以概率 pᵢ 生成车辆 Vᵢ

交通灯

图片作者。

交通灯位于顶点,由两个区域表征:

  • 减速区:以一个减速距离 和一个减速系数为特征,是一个车辆使用减速系数降低其最大速度的区域。

  • 停车区:停车距离为特征,是车辆停车的区域。这是通过以下动力学方程使用阻尼力实现的:

模拟

我们将采用面向对象的方法。每辆车和每条路都将被定义为一个类别。

我们将在许多即将到来的课程中重复使用下面的__init__函数。它通过一个函数set_default_config设置当前类的默认配置。需要一个字典,并将字典中的每个属性设置为当前类实例的属性。这样我们就不用担心更新不同类的__init__功能,也不用担心以后的变化。

我们将创建一个Road类:

在屏幕上绘制道路时,我们需要道路的length及其角度的余弦和正弦值。

模拟

还有一个Simulation班。我添加了一些方法来给模拟添加道路。

我们必须在屏幕上实时显示我们的模拟。为此,我们将使用pygame。我将创建一个期望一个Simulation类作为参数的Window类。

我定义了多个绘图函数来帮助绘制基本形状。

loop方法创建一个pygame窗口,并在每一帧调用draw方法和loop参数。当我们的模拟需要每帧更新时,这将变得有用。

我将名为trafficSimulator的文件夹中的每个文件与一个导入所有类名的__init__.py文件组合在一起。

每当一个新的类被定义时,它应该被导入到这个文件中

trafficSimulator文件夹放在我们的项目文件夹中,我们就可以使用这个模块了。

模拟测试。图片作者。

车辆

现在,我们必须增加车辆。

我们将使用泰勒级数来近似本文建模部分讨论的动力学方程的解。

无限微分函数f的泰勒级数展开式为:

▲x代替a,用x+▲x代替x,我们得到:

用位置x替换f:

作为近似,我们将在位置的 2 阶停止,因为加速度是最高阶导数。我们得到方程 (2) :

对于速度,我们将用v代替x:

我们将停在 1 阶,因为我们的最高阶导数是加速度(1 阶是速度)。方程式 (2) :

在每次迭代(或帧)中,使用 IDM 公式计算加速度后,我们将使用以下两个公式更新位置和速度:

在代码中是这样的:

因为这只是一个近似值,所以速度有时会变成负值(但是模型不允许这样)。当速度为负时,不稳定性出现,位置和速度发散到负无穷大。

为了克服这个问题,每当我们预测一个负速度时,我们将它设置为零,并从那里计算出:

在代码中,这是按如下方式实现的:

为了计算 IDM 加速度,我们将前导车辆表示为lead,并计算当lead不是None时的交互项(表示为alpha)。

如果车辆停止(例如在红绿灯处),我们将使用阻尼方程:

然后,我们在一个Vehicle类中的一个update方法中将所有东西组合在一起:

Road类中,我们将添加一个deque(双端队列)来跟踪车辆。队列是存储车辆的更好的数据结构,因为队列中的第一辆车是道路上最远的一辆车,它是可以从队列中移除的第一辆车。要从deque中移除第一个项目,我们可以使用self.vehicles.popleft()

我们将在Road类中添加一个update方法:

Simulation类中的update方法:

回到Window类,我添加了一个run方法来实时更新模拟:

现在,我们将手动添加车辆:

车辆在移动!图片作者。

车辆发电机

一个VehicleGenerator有一个包含(odds, vehicle)的元组数组。

元组的第一个元素是在同一个元组中生成车辆的权重(不是概率)。我使用权重是因为它们更容易处理,因为我们可以只使用整数。

例如,如果我们有 3 辆车,重量分别为132。这与1/63/62/66=1+3+2.相对应

为了实现这一点,我们使用以下算法

  • 生成一个介于 1 和所有权重之和之间的数字。
  • r 为非负时:
    遍历所有可能的车辆,并在每次迭代中减去其权重。
  • 归还最后使用的车辆。

如果我们有权重: W₁W₂W₃ 。该算法将第一辆车分配到 1W₁ 之间的号码,第二辆车分配到 W₁W₁+W₂ 之间的号码,第三辆车分配到 W₁+W₂+W₃ 之间的号码。

至于何时添加车辆,每当生成器添加车辆时,名为last_added_time的属性被更新为当前时间。当当前时间与last_added_time之间的持续时间大于车辆生成周期时,增加一辆车辆。

添加车辆的周期是60/vehicle_rate,因为vehicle_rate每分钟车辆60是 1 分钟或 60 秒。

我们还必须检查道路是否还有空间来容纳即将到来的车辆。我们通过检查道路上最后一辆车之间的距离以及即将到来的车辆的长度和安全距离之和来做到这一点。

最后,我们应该从Simulationupdate方法中调用update方法来生成update车辆。

车辆正在产卵!图片作者。

红绿灯

交通信号的默认属性是:

self.cycle是一个元组数组,包含self.roads中设置的每条道路的状态(True表示绿色,False表示红色)。

在默认配置中,(False, True)表示第一组道路是红色,第二组是绿色。(True, False)则相反。

使用这种方法是因为它易于扩展。我们创造的交通灯包括两条以上的道路,交通灯有单独的右转和左转信号,甚至有多个交叉路口的同步交通信号。

交通信号的update功能应该是可定制的。其默认行为是对称的固定时间循环。

我们需要将这些方法添加到Road类中:

而这个,在Roadupdate方法中。

并用Simulationupdate方法检查交通灯状态:

停下来。图片作者。

曲线

在现实世界中,道路是有曲线的。虽然从技术上讲,我们可以通过手写大量道路的坐标来近似一条曲线,从而在这个模拟中创建曲线,但我们也可以在程序上做同样的事情。

为此,我将使用贝塞尔曲线

我创建了一个curve.py文件,其中包含了帮助创建曲线并通过道路索引引用它们的函数。

测试:

优美的曲线。图片作者。

例子

这些示例的代码可以在本文底部链接的 Github 资源库中找到。

高速公路上

高速公路上。图片作者。

双向交叉路口

双向交叉路口。图片作者。

迂回的

迂回。图片作者。

分叉菱形立交

分叉菱形立交。图片作者。

限制

虽然我们可以修改Simulation类来存储关于我们稍后可以使用的模拟的数据,但是如果数据收集过程更加精简的话会更好。

这个模拟还欠缺很多。弯道的实施是糟糕和低效的,并导致车辆和交通信号之间的相互作用的问题。

虽然有些人可能认为智能司机模型有点矫枉过正,但拥有一个能够复制真实世界现象的模型很重要,如交通波(又名幽灵交通蛇)和司机反应时间的影响。出于这个原因,我选择使用智能驱动程序模型。但是对于准确性和极端真实性不重要的模拟,如视频游戏,IDM 可以由更简单的基于逻辑的模型代替。

完全依赖基于模拟的数据会增加过度拟合的风险。您的 ML 模型可以针对只存在于模拟中而不存在于真实世界中的零食进行优化。

结论

仿真是数据科学和机器学习的重要组成部分。有时,从现实世界中收集数据要么是不可能的,要么是昂贵的。生成数据有助于以更好的价格建立庞大的数据集。模拟也有助于填补真实世界数据的空白。在某些情况下,真实世界的数据集缺乏可能对开发的模型至关重要的边缘案例。

这个模拟是我参与的一个大学项目的一部分。目的是优化城市十字路口的交通信号。我做这个模拟是为了测试和验证我的优化方法。

我从来没有想过要发表这篇文章,直到我在看特斯拉的 AI day,在这篇文章中,他们谈到了他们如何使用模拟来为边缘案例生成数据。

源代码和贡献

这里有一个到 Github 库 的链接,包含本文中的所有代码,包括例子。

如果您对代码有任何疑问或问题,请随时联系我,或者在 GitHub 上提交请求或问题。

数据科学模拟

原文:https://towardsdatascience.com/simulation-fbfc3227b021?source=collection_archive---------21-----------------------

有三个代码示例

https://www . free pik . com/free-vector/people-using-VR-glasses-illustration _ 11790695 . htm # page = 1&query = simulation&position = 17

在本文中,我们将看三个模拟示例。

  1. 统计理论 101
  2. 蒙蒂·霍尔问题
  3. 航线优化

介绍

在高中和研究生院的时候,我总是纠结于与以下相关的课程:

  1. 证明
  2. 统计理论

通过实践,我最终理解了这些课程中的信息。现在,我作为一名实践数据科学家进入了现实世界,显然这些课程会为我提供日常工作和生活所需的丰富知识…我不会走那么远!

说实话,我确实在这些课程中学到了很多。但实际上,我有了一种新的方法来回答这些课上提出的很多问题。其实这几天几乎每个问题我都用同样的方法回答!解决方法是 模拟

让我们看一个统计理论导论课上的例子。

统计理论 101

假设你的任务是找出将一枚硬币抛 50 次,恰好得到 20 条反面的概率?

你知道这是一个二项分布吗?如果你这样做了,很可能你很快就计算出这些几率是 4.2%。我个人在statisticshelper.com用在线计算器找到了答案,上面显示的是概率密度函数(以下所有公式/解都是从上述网站截取的图片):

这张图片显示了我们的问题设置中每个字母代表的含义。

https://www.google.com/imgres?imgurl=http%3A%2F%2Fwww.mathnstuff.com%2Fmath%2Fspoken%2Fhere%2F2class%2F90%2Fbinom1.gif&imgrefurl=http%3A%2F%2Fwww.mathnstuff.com%2Fmath%2Fspoken%2Fhere%2F2class%2F90%2Fbinom3.htm&tbnid=brz1spRymlKULM&vet=12ahUKEwiD8qLE_OPyAhVMS60KHaCgCucQMygGegUIARDPAQ..i&docid=yDZN7C9J9IpfGM&w=412&h=205&q=binomial distribution formula&ved=2ahUKEwiD8qLE_OPyAhVMS60KHaCgCucQMygGegUIARDPAQ

如果硬币被操纵了,它 80%的时候都是正面朝上。现在,20 条尾巴的几率是多少?如果你对上面的公式感到满意,你会发现答案是 0.06%。

但是如果你不知道这是二项分布呢?或者如果那个公式对你没有意义呢?或者如果问题不同呢?

如果问题是第一条尾巴在第五次翻转时翻转并且前四条都是正面的几率有多大呢?第一条尾巴出现在第 100 次翻转时,前 99 条是正面的可能性有多大?

现在,你需要知道几何分布。🤦‍♂️

你在第 100 次空翻中完成第 5 次空翻的几率有多大?而另外 4 次空翻可以分散在前 99 次空翻中的任何地方。几率有多大?

现在你需要了解负二项分布。️️🤦‍♀️.这已经失控了!

模拟示例 1:二项式分布

如果你不理解上面的任何解决方案,没关系。这就是这个帖子的力量。你不需要!

除了了解所有这些统计分布,您还可以模拟结果。让我们重温一下第一个问题:假设你的任务是找出将一枚硬币抛 50 次,恰好得到 20 条反面的概率?

我们可以用上面的方法模拟一次翻转。random.random()以相等的概率返回 0 和 1 之间的值。所以默认情况下,coin_flip()模拟公平的硬币投掷,但是通过更新默认的probability值也允许不公平的硬币。

现在我们可以模拟 50 次抛硬币,看看结果。

作者图片

您的结果可能会有所不同,因为这是随机的,我们没有设置种子。你可以看到在第一组 50 次抛硬币中,我们得到了 26 个正面和 24 个反面。问题是我们多久才能得到 20 条尾巴。我们现在可以模拟这个过程 10,000 次,看看 20 条尾巴出现的频率。

运行这段代码,我得到了大约 4.2%的结果!🎉 🎊

您可以通过将probability更改为 0.2 来轻松进行修改。我的模拟结果提供了 0.0005,这与理论结果并不完全相同。然而,在现实世界中,这是一个非常合理的值。

作者图片

模拟示例 2:蒙蒂霍尔问题

如果你对三门蒙蒂霍尔问题不熟悉,大卫伯顿的这篇文章可以帮助你加快速度。你也可以在 UCSD 网站这里自己试题。总的来说,有三扇门。一扇门后面有辆车,另外两扇门是哑弹。你选择了一扇门,然后给你看了一个哑弹。此时,您可以将您的选择更新到另一个门,或者继续使用您最初选择的门。

对我来说,在看到一扇坏门后换门以增加选择汽车的几率从来都不是直觉。然而,我们可以模拟这个问题,以证明更新我们的选择是正确的决定。

下面的代码将迭代一个游戏,其中我们选择用门的变化来更新我们最初的选择。

我们现在可以运行这个场景的 10,000 次迭代,并跟踪我们赢得汽车的频率。这可能会有一些变化,但我的模拟运行提供了几乎准确的理论解决方案,其中 66%的情况下我们会选择更换门,而只有 33%的情况下我们会选择保留原来的门。

模拟示例 3:航线问题

作为最后一个模拟示例,假设您为一家航空公司工作。想象一下,我们知道一架飞机有 100 个座位,每个买了票的乘客有 10%的几率不会出现。另外,每张票的利润是 75 美元。如果你超售了飞机,你会为每个因没有座位而需要转到新航班的乘客赔钱。这些超售席位的损失是 25 美元。

想象一下,无论乘客是否出现,你都会获得利润。

如果你卖出 100 张票,你会赚 7500 美元。

如果你卖出 101 张票,每个人都来了,你就赚了

75 美元 X100-25 美元 x1 = 7,475 美元

但是,如果至少有 1 人缺席,您将获得:

75 美元 x101 = 7 575 美元

为了实现利润最大化,应该出售的最佳座位数是多少?

我们可以使用下面的函数计算收入,默认情况下销售 100 张门票,这应该总是提供 7,500 美元的收入。

然后,我们可以模拟多次运行calculated_revenue函数,但是销售不同数量的门票,以了解最佳数量。假设我们在每一个可能售出的门票数量上运行 10,000 次迭代。然后我们看看每张售出的票的可能收入。下面的代码执行模拟。

然后我们可以画出可能的收入值。

作者图片

如果我们想要最大可能的收入,我们可能会选择增加风险,直到卖出 130 张以上的门票。然而,潜在的不利因素导致售出的座位数非常高时的预期利润低于我们在座位数低得多时的预期利润。取决于我们的风险承受能力,没有正确的答案。该图提供了做出明智决策所需的信息。

使用平均值(在大多数传统课程中建议的最优值)提供了以下图表和售出 115 个座位的解决方案。

作者图片

结论

我们的模拟解决方案之旅到此结束。下次你遇到问题时,如果你不确定正确答案,记得你可以模拟各种可能性!然后利用结果做出正确的决定!

基于数据块的单节点分布式深度学习

原文:https://towardsdatascience.com/single-node-and-distributed-deep-learning-on-databricks-2ab69797f812?source=collection_archive---------31-----------------------

理解大数据

卢克·范·德·维尔登& 里克·琼格里乌斯

Databricks 是一个分析生态系统,现在可以在大多数主要的云提供商 Google、AWS 和 Azure 上使用。数据块集群计算使用分布式 Spark 引擎。最近推出的单节点 Spark 集群不支持分布式计算,为什么?

单节点数据块集群

在多节点集群上,带有 PySpark 的 Python 解释器在驱动节点上运行以收集结果,而工作节点执行 JVM jar 文件或Python UDF。运行单节点集群的选项于 2020 年 10 月推出,其动机如下。

标准数据块 Spark 集群由一个驱动节点和一个或多个工作节点组成。这些集群最少需要两个节点——一个驱动程序和一个工作程序——来运行 Spark SQL 查询、从增量表中读取数据或执行其他 Spark 操作。然而,对于许多机器学习模型训练或轻量级数据工作负载,多节点集群是不必要的。

单节点集群可以运行 Databricks 机器学习运行时,该运行时经过精心策划,开箱即用。您可以选择 GPU 或 CPU 运行时,并将其部署在各种规模的虚拟机上。非常适合训练我们的机器学习模型。

熊猫数据帧

在单节点集群上,Spark 命令以本地模式运行。单节点集群仍然可以使用 Spark 数据源和相关的云认证/授权支持,比如 Azure 上的 ADLS 认证直通。除了数据提取和加载,Spark 功能在单节点集群上并不那么有用。为了训练机器学习模型,我们经常从 Spark 转换到 Pandas 数据帧。自从 Spark 2.3-2.4 以来,熊猫之间的转换已经通过箭头加速。有时,我们甚至在数据砖块上立即将拼花地板(数据湖)转换成熊猫,见下文。

# azure data lake parquetdf = spark.read.parquet("abfss://<storage>/path").toPandas()

Azure 用例

让我们讨论一下在计算管道中使用单节点集群。我们希望训练多个 DeepAR 模型,每个模型在我们的数据集中有不同的时间序列。这是令人尴尬的并行,因为每个模型都可以在内存中有一个时间序列的单个 CPU 虚拟机上训练。每个培训工作相当于运行一个 Databricks 笔记本,它导入一个特定的时间序列和一个具有单元测试功能的 Python 包。我们有数百个时间序列,并希望旋转数十个单节点来执行我们的训练工作和注册我们的模型。我们使用 Azure Data Factory 来安排我们的 Databricks 笔记本和启动集群。

DataFactory-Databricks 架构,图片由作者提供

Azure 数据工厂的并行性

DataFactory 管道可以并行运行 Databricks 笔记本,并等待它们完成,然后继续进行管道的下一个活动。ForEach 操作符为序列中的元素(例如数据湖拼花路径)启动一个记事本。它可以按批量顺序或并行运行。请参见下面数据工厂 JSON 导出中的“isSequential”和“batchCount”。我们启动了 20 个运行相同数据块的单节点笔记本活动来处理我们的时间序列。数据工厂通过数据块链接服务管理这些集群资源。

{
    "name": "train_each_time_series",
    "type": "ForEach",
    "dependsOn": [
        {
            "activity": "generate_time_series_paths",
            "dependencyConditions": [
                "Succeeded"
            ]
        }
    ],
    "typeProperties": {
        "items": {
            "value": "[@json](http://twitter.com/json)('[{\"time_series\": \"some/path\"}]')",
            "type": "Expression"
        },
        "isSequential": false,
        "batchCount": 20,
        "activities": [
            {
                "name": "train_a_time_series",
                "description": "Train a time series DeepAR",
                "type": "DatabricksNotebook",
                "dependsOn": [],
                "userProperties": [],
                "typeProperties": {
                    "notebookPath": "/our_model/train_model"
                },
                "linkedServiceName": {
                    "referenceName": "[parameters('db_pool')]",
                    "type": "LinkedServiceReference"
                }
            }
        ]
    }
}

数据块实例池

要从 DataFactory 运行 Databricks 笔记本,我们需要在 DataFactory 中创建一个链接服务。启动笔记本活动时,有三种方法可以配置链接服务来选择或创建 Databricks 集群。

  1. 互动集群是一个预先存在的运行集群,我们可以选择我们的笔记本活动。
  2. 为启动的每个笔记本活动创建作业群。作业集群在笔记本活动期间存在。这会导致集群创建和终止的等待时间很长(两者都需要几十分钟)。
  3. 数据块实例池包含可配置数量的就绪虚拟机实例,这些实例会等待被包含到集群中。空闲和运行之间的延迟是几分钟。从 2019 年末开始支持,实例池允许作业之间的快速周转时间。

我们在数据块中创建了一个实例池,其中包含我们选择的 20 个 CPU 密集型虚拟机。当我们在 DataFactory 中创建数据块链接服务时,我们选择这个实例池,并提供数据块工作区的访问令牌。当笔记本活动完成时,其集群虚拟机实例被释放回池中。ForEach 操作符最多可以并行处理 50 个活动,我们根据实例池的大小来限制这个数量。如果我们的资源请求大于数据块实例池大小,DataFactory 管道将失败。如果我们想在单节点上并行处理 20 个笔记本活动,我们需要一个包含 20 个实例的池。在这种情况下,如果资源不可用,DataFactory-Databricks 组合不支持排队。

待续

关于我们使用 Python SDK 的 Databricks 笔记本和数据工厂管道的 CICD 工作流的后续博客将进一步深入这个用例。在下一节中,我们想知道如果我们不使用 Spark 进行模型训练,多节点数据块集群有什么用。

分布式深度学习

当我们在具有足够内存和 GPU/CPU 资源的单节点上运行 Databricks 笔记本时,我们已经看到了单节点 Databricks 集群对于机器学习的价值。让我们回溯并考虑分布式计算。多节点数据块集群只对 Spark 计算有用吗?我们能否在多节点数据块集群之上搭载一个不同的分布式计算框架?

Databricks Horovod 转轮

Horovod ( 开源优步)是一个使用 MPI 和 NCCL 的分布式深度学习框架,支持 TensorFlow、Keras、PyTorch 和 Apache MXNet。Databricks 的 Spark-Deep-Learning 通过机器学习运行时支持 data bricks 集群上的 Horovod。它提供了一个 HorovodRunner 来对 Spark 任务中的多个工人运行 Python 深度学习。Spark 任务在 worker 节点(spark executor)上的现有 SparkContext 中运行,并涉及 worker 节点上的 Python 运行时,它与共存的 JVM 通信。

使用分布式数据

通常,分布式 Horovod 优化器使用梯度下降计算,通过 MPI 在节点之间进行通信。数据提取和预处理在 worker 节点上的 Python 解释器中完成。坚持培训成果;DBFS 上的一个检查点目录被一个工作人员用来存储模型检查点,参见下面的简明代码.)

import horovod.torch as hvdfrom sparkdl import HorovodRunnerlog_dir = "/dbfs/ml/horovod_pytorch"def train_hvd(learning_rate): hvd.init() train_dataset = get_data_for_worker(rank=hvd.rank())
        train_loader = torch.utils.data.DataLoader(
            train_dataset,
            batch_size=batch_size,
            sampler=train_sampler,
        ) optimizer = optim.SGD(
        model.parameters(),
        lr=learning_rate,
        momentum=momentum,
    ) # hvd.DistributedOptimizer handles the distributed optimization
    optimizer = hvd.DistributedOptimizer(
        optimizer, named_parameters=model.named_parameters()
    ) # all workers start with the same initial condition
    hvd.broadcast_parameters(
        model.state_dict(), root_rank=0
    ) for epoch in range(1, num_epochs + 1):
        train_epoch(
            model, device, train_loader, optimizer, epoch
        ) # save model checkpoints only from one worker
        if hvd.rank() == 0:
            save_checkpoint(
                log_dir, model, optimizer, epoch
            )# initialize a runner with two workers
hr = HorovodRunner(np=2)
# launch our training function across the workers
hr.run(train_hvd, learning_rate=0.001)

在为 Horovod runner 编写 Python 代码时,我们不能使用 Spark SQL 功能从类似 HDFS 的文件系统中查询数据。只有 Spark driver 节点可以协调该功能并收集其结果。但是,也有从类似 HDFS 的数据湖存储中读取的选项。

佩塔斯托姆

Petastorm(也是优步的开源)符合我们的需求。该库允许在深度学习模型的单节点和分布式训练期间使用数据湖存储的拼花文件。它还可以使用内存中的 Spark 数据帧。Petastorm 将物化的 Spark 数据帧转换为 PyTorch 和 Tensorflow 的不同数据加载器。预处理转换可以作为 TransformSpec 添加到 Petastorm 转换器中,它作用于 Pandas 数据帧。

我们可以使用 Petastorm 进行分布式深度学习,如果我们将 Petastorm 转换器中的' shard_count '配置为 Horovod runners 的数量,并让 Horovod 根据工人的等级选择合适的 shard(py torch 示例)。我们还会根据 Horovod 工作线程的数量小心地划分底层的 parquet 文件或内存中的数据帧。这太棒了!HDFS 式文件系统上的分布式数据可用于深度学习模型的分布式训练。

通过流媒体实现未来协同效应

我们已经讨论了 Horovod(深度学习)与 Databricks spark cluster 和 Petastorm 的集成,用于数据加载。如何改进 Spark 和 Horovod 集成?我们希望每个 worker 节点上的 worker JVM 和 Python 解释器之间的数据传输更加容易。Arrow 只是一项技术,并且已经得到支持。一个解决方案方向可能是火花结构化流

结构化流抽象是基于一个连续的数据框架,随着事件从流源进入而增长(f.i. Kafka)。操作以微批处理或连续方式执行,有三种输出格式(追加、更新、完成)。PySpark API 落后于 Java/Scala API ,不支持有状态或无状态的任意操作。性能限制可能是不支持任意 Python 操作的原因,也是 API 关注 Spark SQL 聚合的原因。

愿望列表:任意 PySpark 流操作

在训练期间,我们的 DL 模型表示我们希望在每个训练步骤中更新的状态。传递给 HorovodRunner 的 Python UDF 是对一批数据的有状态操作。例如,在数据集中的每个图像上训练 DNN。我们可以通过 Spark 结构化流将 HDFS 或卡夫卡的每幅图像传输到 Python 进程,正好赶上培训,就像在在线学习一样。下面的非功能性代码让我们对它的外观有了一些了解(我们忽略了事件窗口逻辑)。我们将训练数据流与模型权重流相结合,并对模型进行增量训练,将更新后的模型权重添加到模型权重流中,用于下一个处理步骤。

# Pseudo code not functional
schema = ...
train_data = spark.readStream.format("kafka").option(
    "subscribe", "train_data"
)model_weights = spark.readStream.format("kafka").option(
    "subscribe", "model_weights"
)train_data.join(
    model_weights,
    expr("train_data.model_id == model_weights.model_id"),
).filter(col("train_data.rank") == hvd.rank).groupByKey(
    model_id
).mapGroupsWithState(
    train_model
).writeStream.format(
    "kafka"
).option(
    "topic", "model_weights"
).outputMode(
    "append"
).start()

包裹

这个博客围绕着数据块集群的非 Spark 中心的使用。我们从在单节点集群上并行运行深度学习训练转移到在多节点集群上运行分布式训练。我们可以使用分布式数据进行模型训练。此外,我们还提出了缩小 Spark/Databricks 集群的数据密集型和计算密集型用途之间差距的方法。在后续博客中,我们将更详细地讨论我们如何通过 Databricks 笔记本和数据工厂管道来接近 CICD。

最初发布于https://codebeez . nl

奇异值分解

原文:https://towardsdatascience.com/singular-value-decomposition-158469b433ad?source=collection_archive---------19-----------------------

Python 中的解释、推导和应用

来自 Pexels 的 Nicole Avagliano 的照片

介绍

对数据科学、机器学习和人工智能至关重要的线性代数经常被忽视,因为大多数入门课程未能展示整体情况。从从业者的角度来看,像特征分解和奇异值分解(SVD)这样的概念非常重要;它们是包括主成分分析(PCA)和潜在语义分析(LSA)在内的降维技术的核心。本文旨在展示 SVD,方法是结合有形的 Python 代码,温和地介绍所需的数学知识。

奇异值分解

矩阵乘法

首先,让我们考虑下面的向量, x ,作为两个基向量 ij 的和。

由作者生成的图像

我们可以很容易地使用 matplotlib 和 Python 来可视化这个向量…

由作者生成的图像

矢量相对简单,它们有方向和大小。在这个例子中,我们从原点画出向量 x 。然而,这个相同的向量可以在ℝ的任何地方等价地绘制。

现在让我们考虑一个矩阵和矩阵乘法。数学上…

由作者生成的图像

使用标准矩阵乘法…

由作者生成的图像

在 Python 中…

此外,我们可以画出矩阵和向量的乘积…

由作者生成的图像

我们可以看到矩阵乘法( Ax )改变了我们原始向量 x 的方向和大小。

E 本质上,矩阵旋转并且拉伸我们的原始向量。

这可以通过在同一张图上绘制两个向量来更有效地看出…

由作者生成的图像

更具体地说,我们可以看到原始向量 x 被旋转了一个角度 θ ,并被拉伸了一个标量 α 。分解成旋转拉伸的概念可以推广。

然而,在我们继续之前,代替我们的原始向量, x ,现在让我们考虑向量的集合作为矩阵 V.

由作者生成的图像

在这种情况下,我们的向量集合包含原始基向量( ij ),它们相加产生 x

维度一般可以扩展到mxn—使用2x2辅助可视化。

这种旋转和拉伸的概念也可以在 AV 中看到…

由作者生成的图像

旋转矩阵

这是负责旋转原矢量的矩阵 θ

由作者生成的图像

重要的是要注意旋转矩阵是酉变换。酉变换的一个特殊性质是它们的逆是(平凡的)复共轭转置。

旋转很容易可视化,尤其是在二维空间中。然而,该矩阵可以被修改以实现更高维度的旋转,同时指定特定的旋转轴。

拉伸矩阵

这是负责拉伸原向量由 α 的矩阵。

由作者生成的图像

向更高维度的扩展是非常明显的——通过简单地对角化相关维度空间中的拉伸参数。

简化奇异值分解

这个难题的所有部分现在对我们来说都是可用的。我们知道,乘积 AV ,包含一个旋转和一个拉伸。为了进一步推广这一观点,我们还将考虑…

  • dim(A)=mxn
  • dim(U _ hat)=mxn
  • dim(σ_ hat)=nxn
  • dim(V )=nx*n

数学上…

由作者生成的图像

如果我们将右边两边乘以 V 的倒数…

在这种情况下,我们将把 V 的倒数称为 V*

由作者生成的图像

由作者生成的图像

由作者生成的图像

这就是约化奇异值分解

奇异值分解

我们可以通过向旋转矩阵添加 m-n 列,向拉伸矩阵添加 m-n 行来实现这一点。利用这个想法,我们将 U_hatσ_ hat重新定义为 Uσ

  • dim(A)=mxn
  • dim(U)=mxm
  • dim(σ)=mxn
  • dim(V )=nx*n

由作者生成的图像

这被正式称为奇异值分解。其中σ包含拉伸元素,奇异值,降序排列。这种分解的主要好处是它适用于任何矩形或正方形矩阵。

解析奇异值分解

如前所述,特征分解与奇异值分解密切相关。我们可以把上面已经得到的转化为初等线性代数中的经典特征值问题来寻找奇异值分解。

由作者生成的图像

由作者生成的图像

回想一下 U 是酉变换…

由作者生成的图像

由作者生成的图像

将两边乘以矢量集合V

由作者生成的图像

由作者生成的图像

由作者生成的图像

回想一下特征值问题…

由作者生成的图像

我们可以在这个问题的上下文中匹配以下元素…

  • 甲→ A^TA
  • x → V
  • λ→σ

通过从 AA^T.开始,可以采用相同的程序找到 U

Python 中的奇异值分解

Python 使得使用 numpy 找到矩阵的奇异值分解变得非常容易。

*array([[ 2.,  3.],
       [-2.,  4.]])*

在上面的代码片段中,我们找到了矩阵 A 的奇异值分解,也展示了原始矩阵的 SVD 重构。

在其分解形式中,我们还可以通过线性变换原始基向量集合 V 来可视化奇异值分解的元素。

由作者生成的图像

由作者生成的图像

图像压缩中的应用

也许奇异值分解最直观的例子之一来自图像压缩。首先,我们将读入一幅图像,并找到奇异值分解。接下来,我们将把秩降低到包含奇异值的矩阵的任意三层(σ)。最后,我们将重建降秩后的图像。

在图像中阅读

注意:确保你的 cwd 有一个图像,这样你就可以跟随

照片由 Aaron BurdenUnsplash 上拍摄——情节由作者生成

移除颜色通道

在奇异值分解之前,我们要移除与这张图片相关的颜色通道…

Aaron BurdenUnsplash 上的照片——作者生成的情节

奇异值分解

现在我们可以计算代表这个图像的矩阵的奇异值分解。我们将像以前一样使用 numpy,并使用 matplotlib 可视化这个分解的组件…

Aaron BurdenUnsplash 上的照片——作者生成的情节

为了更好地理解奇异值,我们可以按降序绘制前 50 个值…

由作者生成的图像

降级σ

现在让我们计算对角矩阵 S 的秩(σ)

*480*

我们可以选择保留哪些奇异值,并将其余的设置为零以降低秩——因此,我们可以通过取分解和绘图的乘积来查看对原始图像的影响…

10 个奇异值

Aaron BurdenUnsplash 上的照片——作者生成的情节

25 个奇异值

图片由 Aaron BurdenUnsplash 上拍摄——由作者生成的情节

50 个奇异值

Aaron BurdenUnsplash 上的照片——作者生成的情节

结论

本文介绍了奇异值分解及其应用。首先,我们对矩阵乘法有了一个基本的了解,并将运算分解为一个旋转和一个原始向量(或向量集合)的拉伸。然后我们通过解析地和计算地寻找奇异值分解来推广旋转和拉伸的概念;同时示出了分解对基向量集合的影响。最后,我们通过降低含有奇异值的矩阵的秩(σ)给出了奇异值分解在图像压缩中的应用。

用于机器学习数据预处理的六种数据类型转换函数

原文:https://towardsdatascience.com/six-datatype-transformer-functions-for-data-pre-processing-for-machine-learning-eb9abcce68cd?source=collection_archive---------26-----------------------

包括所有 Python 代码,提供 pandas 链接,并扩展 py teacher 包。

来源:弗兰基·查马基在 Unsplash 上

数据预处理简介:数据类型转换

在您作为数据工程师或数据科学家的工作中,您将花费大量时间对数据进行预处理,以完成对您的传销的实际训练,然后从您的传销进行准确预测。

在将数据输入到机器学习模型(MLm)之前,数据类型转换和其他变形可能会带来一些好处。

将数据更改为更好地供传销使用的过程称为数据预处理。

我主要关注机器学习的数据类型转换,以及如何使用函数链创建 pandas 函数语句,并将这些函数放置在 py teacher 包中。

我发现编写和读取函数链比编写 sklearn 管道要容易得多。

我认为链管道(函数式编程)比 sklearn 管道更容易复习和理解。

数据预处理的需要

如果你喂你的机器学习模型苹果,在通过喂它橘子来重新训练机器学习模型之前,预处理输入数据。—匿名

在第一个项目中,我们在数据预处理代码开发上花费了相当多的时间。以至于在第二个项目开始时,项目管理已经将数据清理分为三类:相同的、小于 33%的不同代码变更和大于 33%的不同代码变更。

注意 :这些是在实际编码之前对工作需求评估的指导方针。

第二个项目有一个数据预处理功能包,几乎不需要修改。其中一些我已经包含在开源包 py toolter 中,这些“用于机器学习的预处理实用函数”是这篇博客文章的主题和文档。

这些类别定义了两个重要的成就:

  • 作为一个团队,随着数据预处理需求的确定,我们能够消除大量的“重新编码”
  • 我们的开发时间估计变得更加精确。

我发现 15 个数据预处理函数中不到 1 个属于第三类,更耗费开发人员的时间,超过 33%的不同代码更改。我的概念到生产的时间是我们前两个项目的三分之一。我现在花了大部分时间来寻找一种足够好的机器学习解决方案。

熊猫是什么?

如果你正在学习或者更好地使用 Python,那么就采用 Pandas 进行强大的数据操作。

什么是 py 看门人?

py gate er是一个数据预处理函数的 Python 包。它开始是 R 的看门人包的一个端口。py teacher 通过将 py teacher 的数据预处理功能作为函数运算符添加到 pandas 来支持 pandas 包。

函数式编程——用链接创建可读管道

链接可以使它非常容易理解,因为操作是从左到右执行的。这和你阅读(英语和许多其他语言)的方式是一样的。

有些人更喜欢链而不是用 sklearn 的管道。你会注意到我们的链接在语法上与 sklearn 的管道链接略有不同。

注意 :您必须按照相同的顺序对所有数据应用数据预处理动作。所有数据意味着你的训练、测试、验证和预测数据。

我选择将这些函数添加到 py teacher 中,因为它支持链接,并且作为 R 中的一个端口,它非常接近 R 函数式编程风格的外观。从现在开始,我使用这两种调用方法。

toDataFrame(arr, columns=['a','b']).toCategory(verbose=False).dtypes

在这种情况下,我链接了toDataFrame输出的 DataFame 实例的两个方法.toCategory().dtypes

在这篇博客文章中,我会做更多的解释并展示更多的例子。

数据类型转换器

1.to_DataFrame

因为 py gate er 函数和方法只消耗 pandas 数据帧,所以我们需要转换各种 python 数据类型。这个功能就是janitor.mlsd.todatatype.toDataFrame()

janitor.mlsd.util.toDataFrame(X: any, 
    columns: List = [], 
    verbose: bool = True) 
    -> pd.DataFrame:Parameters:
    X: 
        Attempt to convert python datatype argument into 
        pandas dataframe. labels: [] (default)    
        The column names to  be used for new DataFrame. 
        If a number of column names given is less than a number 
        of column names needed, then they will be generared 
        as c_0...c_(n-1), where n is the number of missing
        column names.*verbose:
        True: output
        False: silent (default)

Raises:
    1\. ValueError will result of unknown argument type.
    2\. ValueError will result if labels is not a string or list of strings.

Returns:  pd.DataFrame*from pandas_summary import DataFrameSummary as DFS
from janitor.mlsd.todatatype import toDataFramedisplay(DFS(toDataFrame([1,2,3,4,5],verbose=False)).summary())

输出= >

使用显示的数据帧摘要(DFS(toda frame([1,2,3,4,5]))。摘要()

请注意,在将列表转换为 DataFrame 时,创建了列名c_0

尝试将verbose=True作为默认设置。:

display(DFS(toDataFrame([1,2,3,4,5])).summary())

输出有什么不同?

:这篇博客文章中讨论的 py guardian 的所有添加都在 *janitor.mlsd* 子包中。 *mlsd* 机器学习结构化 dat的缩写

:函数 *toDataFrame* 是 py teacher 规则“第一个参数是数据帧”的例外,因为它将一维或二维列表、元组、csr_matrix、numpy 数组的一维 pandas 系列转换为 pandas 数据帧。

一个好的做法是在管道的开始制作类型为DataFrame的数据集。函数所做的更改会保持 DataFrame 实例的状态(inplace=True)。在您的实验运行的整个管道中,就地将会发生,以最大化完成速度和最小化内存使用。

你也有janitor.mlsd.util.isDataFrame(X) -> bool来检查对象是否是一个pandas.DataFrame

如果不需要 py gate 的服务,比如日志记录,可以使用方法pandas.DataFrame。我会建议你不要这样做,但是你有选择。

如果您需要引用原始数据帧,那么您可以执行类似以下操作:

*<dataFrame_name>_original = <dataFrame_name>.copy()*

但是,保留原始副本会消耗一些计算机内存。

我有 68GB(千兆字节)的快速内存,如果大小大于 1 GB,我不会保留原始数据帧。

我预计内存和硬件速度将会提高,但数据集的大小将会增长得更快。

几乎总是从本地文件或通过 URL 寻址的非本地文件输入原件,因此,您可能不需要在计算机内存中保留副本。

由您决定是否保留原始数据集的副本。我只在 EDA(探索性数据分析)期间保留一份。生产中不需要。

您可以通过以下方式查看任何对象的大小(以字节为单位):

*from sys import getsizeof
getsizeof(pd.DataFrame())=> 24*

上面显示了一个大小为 24 字节的空数据帧。

2.二进制值到整数

除了树之外,MLM 和其他函数一样,需要将True/Falsevalue_1/value_0,的布尔值(只有两个值的特性)或任何二进制值特性转换为整数1/0才能正常工作。

*binary_value_to_integer(
    oX: pd.DataFrame, inplace: bool = True, verbose: bool = True
) -> pd.DataFrame: *Parameters:
           oX: dataset

        Keywords:
            inplace:
                True: mutate X, return X
                False: do no change X, return df-stats

            verbose:
                True: output (default)
                False: silent

        Returns:
            pd.DataFrame**

注意 :为了安全起见,任何传销都要换算成整数 *1/0*

函数boolean_to_integer转换所有二进制值特征。因为它不影响非二进制值特性,所以可以在任何数据帧上调用它。

*from janitor.mlsd.todatatype import binary_value_to_integertoDataFrame([10,100,10,100],verbose=False).binary_value_to_integer()*

toDataFrame 的输出([10,100,10,100],verbose=False)。二进制值到整数()

尝试非二进制值功能。发生了什么事?

3:分类

janitor.msld.todatatype.toCaterory将熊猫数据帧的任何布尔、对象或整数特征类型转换为类别特征类型。

例外情况是连续(浮点或日期时间)值,它返回未转换的值。

*def toCategory(
    oX: pd.DataFrame,
    boolean: bool = True,
    integer: bool = True,
    object_: str = True,
    inplace: bool = True,
    verbose: bool = True
) -> pd.DataFrame:
 *Parameters:
            oX: pd.DataFrame

        Keywords:

            boolean: bool Default: True
                If ``True`` will convert to ``category`` type.

            integer: Default: True
                If ``True`` will convert to ``category`` type.

            object_: Default: True
                If ``True`` will convert to ``category`` type.

            verbose: Default: True
                True: output
                False: silent

            inplace: 
                True: (default) replace 1st argument with new dataframe
                False:  (boolean)change unplace the dataframe X

        Returns: pd.DataFrame**

如果要将连续或日期时间类型转换为类别,请在 py toolter to category 之前使用 ContinuoustoCategory 或 DatetimetoComponents。

注意:py gate 数据清理步骤(如删除 Null 和 NA 值)需要在此步骤之前完成。py teacher 具有完成改变空值和 NA 值的功能。关于 py gate 清洁工的未来博客文章的主题。**

*from janitor.mlsd.todatatype import toCategoryinternet_traffic.toCategory().dtypes*

internet_traffic.toCategory()的输出。数据类型

byte特性没有改变,这正是我们想要的,因为它是 float 类型的。

date特征被改变了,这正是我们想要的,因为它是 object 类型的。

*internet_traffic.dtypes*

internet_traffic.dtypes 的输出

date功能更改为日期时间类型。

*internet_traffic_s = pd.to_datetime(internet_traffic['date'])
toCategory(internet_traffic_s).dtypes*

toCategory 的输出(internet_traffic_s)。数据类型

date特性没有改变,这正是我们想要的,因为它的类型是 datetime。

4.toContinuousCategory

janitor.msld.todatatype.toContinuousCategory通过将熊猫数据帧的任何连续浮点或整数值分组到类别箱中,将它们转换为类别值。

*toContinuousCategory(
    oX: pd.DataFrame,
    features: list = [],
    integer: bool = True,
    float_: bool = True,
    quantile: bool = True,
    nbin: int = 10,
    inplace: bool = True,
    verbose: bool = True,
) -> pd.DataFrame:*"""
Transforms any float, continuous integer values of
 a pandas dataframe to category values.

Parameters:
    X: pd.DataFrame

Keywords:

    features:  [] (default)
        The column  names to  be transform from continuous to category.

    int_: True (default)
        set integer=False if not continuous and not to transform into category.

    float_: True (default)
        set floaty=False if not continuous and not to transform into category.

    quantile: True use quantile bin. (default)
        quantile is simular to v/(maxy-miny), works on any scale.
        False, use fixed-width bin. miny,maxy arguments are ignored.

    nbin: 10 (default)
        Alternately ``nbins`` can be integer for number of bins.
        Or it can be
        array of quantiles, e.g. [0, .25, .5, .75, 1.]
        or array of fixed-width bin boundaries i.e. [0., 4., 10, 100].

    verbose: 
       True: output (default)
       False: silent

    inplace: True (default)
        True: replace 1st argument with resulting dataframe
        False:  (boolean)change unplace the dataframe X

Returns: pd.DataFrame

Raises:
    TypeError('" requires boolean type.")**

宁滨,也称为量化,用于将连续的数字特征(np.number类型)转换为category类型。

每个容器代表一系列连续的数值。

宁滨数据的具体策略有固定宽度(quantile=False)和自适应宁滨(quantile = True)。

无论输入数据集的类型如何,它都作为DataFrame返回。如果要设置功能名称,在此功能之前调用toDataFrame

固定宽度宁滨仅适用于无缩放的基于树的模型数据集,如random forest、xgboost、lightgbm、catboost 等。非基于树模型,例如线性、对数、SVM、神经网络等。,不会准确地工作,因为它们依赖于值的大小。

线性宁滨的统计问题。

宁滨增加 I 型和 II 型误差;(简单的证明是,随着箱数趋近于无穷大,那么信息损失趋近于零)。此外,改变容器数量将改变容器分布形状,除非分布均匀平坦

分位数宁滨用于单一数据集。

如果你有一个训练和测试数据集,将一个连续特征转换成一个基于百分位数(分位数的类别特征是错误的

分位数基于数据集,训练和测试是不同的,除非它们各自的分布形状相同。

如果训练和测试数据集通过了 t 检验,则分布在统计上没有差异。然而,使用分位数宁滨仍然会导致脆弱的转换。

特征之间的非线性甚至非单调关系

如果你需要线性宁滨,而不是分位数,使用quantile=False

分位数-宁滨。

尽管有上述警告,您的用例可能需要分位数宁滨。
在这种情况下,将分位数宁滨应用于合并的训练和测试数据集,然后再将它们分开。

基于分位数的宁滨是用于自适应宁滨的一个相当好的策略。分位数是将要素的连续值分布划分为离散的连续箱或区间的特定值或分界点。因此,q-分位数将数值属性划分为 q 个相等(百分比-宽度)的分区。

这些箱变成分类特征。你可能想这样做的年龄,体重,温度,工资,价格等特征。

分位数的众所周知的例子包括 2-分位数,中位数,它将数据分布分为两个相等(百分比宽度)的二进制数,4-分位数,标准四分位数,四个相等的二进制数(百分比宽度),以及 10-分位数,十分位数,十个相等宽度(百分比宽度)的二进制数。

您应该注意到,新要素是由分位数宁滨附加 q 产生的,而新要素是由固定宽度宁滨附加 w 产生的

将连续浮动要素转换为固定宽度的分类箱,保留原始列并添加箱列。

*from janitor.mlsd.todatatype import toContinuousCategoryinternet_traffic.toContinuousCategory(integer=False).head()*

tinternet _ traffic . tocontinuouscategory 的输出(integer=False)。头部()

5.toColumnNamesFixedLen

janitor.msld.todatatype.toColumnNamesFixedLen将所有列截断到给定的长度,并附加给定的分隔符和重复列的索引,第一个不同的列名除外。

如果列长度较短,则列长度保持不变。

*toColumnNamesFixedLen(
    oX: pd.DataFrame,
    column_length: int = 3,
    column_separator: str = "_",
    inplace: bool = True,
    verbose: bool = True,
) -> pd.DataFrame:
    *"""
    Truncate column name to a specific length.  If column length is
    shorter, then column length left as is.

    This method mutates the original DataFrame.

    Method chaining will truncate all columns to a given length and append
    a given separator character with the index of duplicate columns, except
    for the first distinct column name.

    Parameters:
        X: dataset

    Keywords:

        column_length: 3 (default)
            Character length for which to truncate all columns.
            The column separator value and number for duplicate 
            column name does not contribute to total string length.
            If all columns string lenths are truncated to 10
            characters, the first distinct column will be 10 
            characters and the remaining columns are
            12 characters with a column separator of one
            character).

        column_separator: "_" (default)
            The separator to append plus incremental Int to create
            unique column names. Care should be taken in choosing
            non-default str so as to create legal pandas column
            names.

        verbose: True (default)
            True: output
            False: silent

        inplace: True  (default)
            True: replace 1st argument with resulting dataframe
            False:  (boolean)change unplace the dataframe X

    Returns: A pandas DataFrame (pd.DataFrame) with truncated column lengths.*from janitor.mlsd.todatatype import toColumnNamesFixedLendf = toDataFrame([[1],[2],[3],[4]])
df.columns = [
 "long_name",
 "another_long_name",
 "another_longer_name",
 "data"]
df.toColumnNamesFixedLen(column_length=5)*

输出

6.today time 组件

为什么要将日期时间分成整数部分?

出于多种原因,所有这些都会产生更好的预测能力,您可以将一个datetime分解成int个组件。您可以更进一步,将datetime int组件转换成分类类型的组件。

你可能知道这是怎么回事了。如果我有一些非分类特征,比如一列datetimes值,我需要一个函数将日期时间转换成分类特征。

janitor.msld.todatatype.toDatetimeComponents将日期时间功能转换为时间组件分类功能。

*def toDatetimeComponents(
        oX: pd.DataFrame,
        drop: bool = True,
        components: list = [],
        prefix: bool = True,
        inplace: bool = True,
        verbose: bool = True,
) -> pd.DataFrame:
    #    import pdb; pdb.set_trace() # debugging starts here
    *"""
    Parameters:
        X: dataset

    Keywords:
        drop: True (default)
            If True then the datetime feature/column 
            is removed.

        components: [] (default) which results in all components
            list of column(feature) names of
            datetime components created.

            One or more of : 
            [Year', 'Month', 'Week', 'Day','Dayofweek'
            , 'Dayofyear','Elapsed','Is_month_end'
            , 'Is_month_start', 'Is_quarter_end'
            , 'Is_quarter_start', 'Is_year_end', 'Is_year_start']

        prefix: True (default)
            If True then the feature is the prefix of the created 
            datetime component fetures. The posfix is
            _<component> to create the new
            feature column <feature>_<component>.

            if False only first _PREFIX_LENGTH_ characters 
            of feature string is used to
            create the new feature name/column
            featurename[0:2]>_<component>.

        verbose: 
            True: output (default)
            False: silent

        inplace: True (default)
            True: replace 1st argument with resulting dataframe
            False:  (boolean)change unplace the dataframe X

    Returns: pd.DataFrame  transformed into datetime 
             feature components**

您可以看到datetimeComponents提供了哪些日期时间组件。我们的版本基于 fast.ai 包中的一个函数。

*from janitor.mlsd.todatatype import toDatetimeComponentsdisplay(date_df.dtypes)
display(date_df.shape)
display(date_df.toDatetimeComponents(verbose=False,inplace=True).shape)
date_df.head()*

当创建了十三个日期时间组件列时,输出为十四列宽。输出分为三个图像,因为 DataFame 有十四列。

头部输出(1)

中间输出(2)

尾部输出(3)

datetimeComponents将每个日期时间值转换成另外十三个int成分。然后它将这些int类型转换成category类型。

如果 X[[dt_features]]不是日期时间类型(如object类型),那么不会试图将 X[[dt_features]]强制转换为datetime类型。**

最好将原始数据字段作为datetime而不是object读取/输入。您可以使用以下方法将 dataframe 列转换为 Datetime:

*X[feature] = pd.datetime(X[feature])*

我们可以进行自动转换,但我们发现一些较大的数据集(> 1 亿行)转换太慢。使用数据帧列作为datetime而不是object来启动管道要快得多。

摘要

对于生产中的 MLa,输入数据集中出现的新要素非常常见。输入数据中出现的新特征通常被称为数据漂移T21。这是未来博客文章的主题。**

todatypes函数调用签名总结如下:

*#1
janitor.mlsd.util.toDataFrame(X: any, 
    columns: List = [], 
    verbose: bool = True) 
    -> pd.DataFrame:#2
binary_value_to_integer(
    oX: pd.DataFrame, inplace: bool = True, verbose: bool = True
) -> pd.DataFrame:#3
def toCategory(
    oX: pd.DataFrame,
    boolean: bool = True,
    integer: bool = True,
    object_: str = True,
    inplace: bool = True,
    verbose: bool = True
) -> pd.DataFrame:#4
def toContinuousCategory(
    oX: pd.DataFrame,
    features: list = [],
    integer: bool = True,
    float_: bool = True,
    quantile: bool = True,
    nbin: int = 10,
    inplace: bool = True,
    verbose: bool = True,
) -> pd.DataFrame:#5
toColumnNamesFixedLen(
    oX: pd.DataFrame,
    column_length: int = 3,
    column_separator: str = "_",
    inplace: bool = True,
    verbose: bool = True,
) -> pd.DataFrame:#6 
toDatetimeComponents(
        oX: pd.DataFrame,
        drop: bool = True,
        components: list = [],
        prefix: bool = True,
        inplace: bool = True,
        verbose: bool = True,
) -> pd.DataFrame:* 

这篇博客文章的代码在这里给出在这里给出

请告诉我们你是否知道其他信息,我们可以补充。田野在滚雪球。

良好可视化的六个准则

原文:https://towardsdatascience.com/six-guidelines-for-good-visualizations-7c1831cda29f?source=collection_archive---------27-----------------------

DataViz 传奇作品的要点。

由作者设计

在我讨论六个指导原则之前,我发现这六个原则对于创建良好的可视化效果非常有效。有些是从经验/观察中学到的,有些是通过数据可视化先驱的教导学到的。

"好的设计很像清晰的视觉思维."
——爱德华·塔夫特

这不是一个详尽的列表,而是数据可视化领域的一个很好的起点。将要讨论的六个准则是:

  1. 高数据密度
  2. 有意义的标签
  3. 命令
  4. 突出
  5. 小倍数/少即是多
  6. 参考/指导线

让我们开始吧。

高数据密度

创建视觉效果时,始终努力实现高数据密度。那么,高数据密度意味着什么呢?高数据密度可以解释为数据对油墨 的比率。向可视化添加更多的墨水不一定会增加价值,但有时会分散注意力。下图显示了相同的数据,但格式不同。左图是条形图,右图是散点图。通过控制墨水的用量,同样的信息得以传达,但干扰更少。

“卓越的图形能够在最短的时间内,用最少的笔墨,在最小的空间里,向观众传达最多的思想。”— 爱德华·塔夫特

(图片由作者提供)

另一种减少图表中墨水量的方法是在图表中使用白色背景。

有意义的标签

标签和数据有助于创建出色的可视化效果。没有数据标签或轴标签的图表是没有意义的,因为它无法传达正确的信息。如前所述,x 轴上的国家名称重叠,因此不可读。这可以通过垂直旋转文本来解决,如下图所示。文本在 x 轴上垂直对齐的问题会分散观众的注意力,并从主要故事中转移焦点,因为人类的思维更喜欢阅读水平方向的文本。最好的选择是翻转 x 轴和 y 轴,就像右侧的图表一样。这将水平定位标签,并且它们也不会彼此重叠。

还应该记住,在标记记号时不要使用速记符号,特别是对于分类数据,还要确保文本标签的方向便于阅读。

(图片由作者提供)

命令

为了从数据中获得更好的洞察力,最好在显示时对数据进行排序。在处理分类数据时,默认情况下,ggplot 按字母顺序排列数据。这可以让你的图形看起来更混乱。这里的焦点应该是正确显示的数据,而不是分类变量的顺序。按升序或降序排列数据将传达正确的信息,而不会导致对数据的误解。下图清楚地展示了排列数据有助于回答查看数据时可能出现的问题,例如:

哪个国家的人均寿命最高?

或者

哪个国家的人均寿命最低?

(图片由作者提供)

同样,数据也可以绘制成散点图,回答上述问题,更美观。

(图片由作者提供)

突出

在某些情况下,突出显示数据可以增加更多的价值,而不是让它保持原样。以英国的预期寿命数据为例,如下图所示。尽管数据显示出上升趋势,这意味着这些年来整体预期寿命在提高,但在这一过程中也可以看到一些亮点。为了回答这些问题,我们可以突出显示它们并给它们贴上标签,如下所示。在下面的图表中,1915 年前后的短暂现象被标记出来,说明了英国人预期寿命突然下降的原因。

(图片由作者提供)

添加某些数据标签可以帮助我们理解异常值或极值。向这些类型的数据点添加标签有助于发送正确的信息,即为什么这些数据点没有被排除而是被标记。下图显示了公民的预期寿命或生活质量与国家人均国内生产总值之间的关系。最右边的标记点是人均 GPD 最高的国家,左边的标记点是预期寿命最低的国家。

(图片由作者提供)

小倍数/少即是多

当处理大量数据时,尤其是时间序列,动画时间序列是一个伟大的选择,以传达大量的信息。这只有在数据以数字格式呈现时才有可能。在打印格式中,这是不可能的,因此创建原始数据的子集更有意义。通过沿公共轴排列图表,很容易进行比较,而不是创建不同的图表。通过比较这些图表,西班牙流感的影响在 20 世纪 20 年代的欧洲国家清晰可见。

(图片由作者提供)

创建小的多个图表而不是动画有其利弊。对于人类的思维来说,分析同时存在的信息要比分析运动中的信息容易得多。

参考/指导线

在处理连续数据集时,直观的指导总是很有用的。一般情况下,数据拟合是在处理连续数据时进行的。数据拟合可以是线性、样条或高阶多项式。这些数据拟合传达了两件事:趋势和变量之间的关系。

下图传达了以下信息:趋势是积极的(线的斜率是积极的),两个变量之间的关系是线性的。

(图片由作者提供)

完整代码:

关键要点

这里讨论了增强数据可视化的六个准则,它们很容易实现。关键要点是:

  1. 尝试在图表画布中用更多的空白空间绘制数据。
  2. 标注必要的文本并突出显示图表的必要区域。
  3. 处理分类数据时,按升序/降序排列数据。
  4. 将复杂的数据分成多个图。
  5. 尽可能添加趋势线,以帮助更好地理解数据。

数据来源:

  1. https://ourworldindata.org/life-expectancy
  2. https://ourworldindata . org/grapher/预期寿命与人均国内生产总值

参考资料:

  1. 定量信息的可视化展示——爱德华·塔夫特
  2. 如何糟糕地显示数据—霍华德·韦纳
  3. ggplot2:用于数据分析的优雅图形— Hadley Wickham
  4. 用数据讲故事——科尔·努斯鲍默·克纳弗里克

对我在数据可视化方面的更多工作感兴趣,请查看这些博客帖子:

* https://medium.com/geekculture/how-to-map-data-with-r-8333110dff5b

链接到 Github 库

可以在 LinkedIn Twitter*上与我连线,跟随我的数据科学和数据可视化之旅。**

关于数据科学伦理的六个问题

原文:https://towardsdatascience.com/six-questions-about-data-science-ethics-4bae9be67cca?source=collection_archive---------26-----------------------

“数据科学”这个词从未出现在亚里士多德的《尼各马科伦理学》中。(图片: Couleur / Pixabay )

在脸书崛起的那些年里,马克·扎克伯格的座右铭是著名的,“快速行动,打破常规”从那以后,很多事情都被打破了。尽管高科技给我们的生活带来了各种好处,但公司对快速增长的唯一关注意味着,对其产品可能如何危害社会的道德考虑只是事后的想法,是在损害已经造成之后要道歉的事情。

从医学到法律,专业领域已经发展了几个世纪的道德标准。数据科学领域还没有这么多时间。但考虑到我最近完成了一个数据科学训练营,并正在寻找我在该领域的第一个职位,我意识到我可能必须与许多道德问题进行斗争,这些问题现在才成为该行业的焦点。

我对关于数据科学道德的文章和课程进行了一些研究,并提出了一些我认为需要探索的更大的(通常是重叠的)问题,希望数据科学家至少有一定程度的标准和共识来回答这些问题。

1.数据的来源有偏差吗?

数据必须从某个来源收集,数据科学家需要记住数据是如何收集的以及数据来自哪里。偏见可能是固有的。例如,在一项名为街道颠簸的倡议中,波士顿居民曾经为波士顿市政府收集了一个众包坑洞数据集。但考虑到低收入人群比富裕人群更不容易拥有手机、汽车和了解科技趋势,众包数据更有利于富裕公民和他们的社区,因此这些社区更有利于街道维修。

从 Twitter 到抖音,再到 Instagram,每个社交媒体平台都有自己的人口统计数据,反映了成员的兴趣和倾向。因此,从平台上搜集的任何数据都会反映这些社区的偏见。

2.隐私得到保护了吗?

隐私,或者说缺乏隐私,在科技界已经是一个根深蒂固的问题。世界各地的政府都通过了相关法律。无数的应用程序和服务在注册时需要一些隐私政策协议,但这些冗长而难以理解的协议通常更多是针对服务的律师而不是用户。

收集的数据有多安全?如果泄露会造成什么损失?仅仅因为一家公司可以收获大量数据,就意味着它应该这样做吗?在 2016 年选举的一个丑闻中,当用户填写一个小调查时,剑桥分析公司收集了脸书的数据,所有这些用户的朋友的数据也被收集了,即使他们没有参与调查。

在共享数据时,如何真正做到匿名?仅仅删除姓名、地址和其他明显的识别信息通常是不够的。

3.数据属于谁,公司还是用户?

科技公司在收集数据方面投入了大量精力,因此他们理所当然地希望拥有这些数据。但是,当有人想要一张不讨好或有损自己的照片时,该怎么办呢?如果一家公司有关于某人的不准确数据,这个人如何发现并纠正它?

也有这样的情况,数据是关于某人的,但他们并没有直接参与其中。假设一家餐馆心怀不满的前员工在一个点评网站上用一星点评抨击这家餐馆。然后她让她的三十个朋友做同样的事情。如果评论是恶意的,餐馆老板如何让网站删除评论?

4.算法是如何影响个人生活的?

假设一家公司使用其现有员工的数据来创建一个算法,以预测新申请人的成功可能性。现在,假设这家公司有一种性别歧视的工作文化,在这种文化下,女性很难发展壮大。很可能任何算法都会预测女性申请者的成功率很低。人们可以在大学招生过程中为代表性不足的群体想到类似的类比。本质上,算法可以强制执行不公平的现状。

一个算法也将是盲目的,对现实世界的情况不宽容。假设在另一个大学招生场景中,一名学生被暂停学业,然后决定改变他的方式,并从那时起在学校努力学习。一种算法可能会基于一次停课拒绝该学生。举一个现实生活中的例子,2014 年澳大利亚的人质危机意味着身处险境的人需要迅速逃离。当他们加载他们的优步应用程序时,激增的价格已经自动达到正常价格的四倍。

虽然这些例子可能被归结为粗心或缺乏想象力,但作为一个明显不道德的例子,脸书曾经做过一个实验,他们操纵用户的反馈,以了解正面帖子的反馈是否会导致用户自己发布正面帖子,反之亦然。(事实证明确实如此。)所以,脸书在操纵人们的情绪,故意让许多用户感觉不好。与传统的心理实验不同,用户从未明确表示同意参与。

5.算法是如何影响社会的?

文化评论家抱怨说,电影和音乐网站使用的推荐系统会让人们被困在相同的流派中。人们不太可能尝试他们舒适区之外的事情;当他们可以变得开明时,他们变得狭隘。与此同时,未成名的艺术家很难引起人们的注意,而注意力却流向了已经成名的艺术家。

更严重的是,社会批评家指责 YouTube 等网站上的推荐算法在政治上让人们变得激进。这些算法旨在让用户参与并点击一个又一个视频,这可能会让他们陷入多巴胺驱动的越来越极端的内容的兔子洞。很容易将极端的网络内容与新闻中经常出现的各种暴力行为区分开来。

鉴于信用评分历史上依赖于一个人的财务信息,社交媒体数据已经进入组合。支持者表示,这种做法允许更细微的评分,但其他人担心这可能导致社会隔离和歧视。

6.数据是被操纵的还是骗人的?

为什么到目前为止提到的许多负面影响可能反映了无意的偏见,有时还有更邪恶的事情在发生。2021 年初,时任纽约州州长的安德鲁·科莫(Andrew Cuomo)卷入了一桩丑闻,有报道称,他指示顾问公开声称养老院居民的新冠肺炎死亡人数远低于实际发生的人数。报道的统计数据只统计了发生在家中的死亡,没有统计病人在家中感染疾病但后来死于医院的死亡人数。

在更南边的佛罗里达州,一位名叫丽贝卡·琼斯的数据科学家与州长罗恩·德桑蒂斯不和;她说,她被迫从一个受欢迎的卫生部仪表板上删除关键的新冠肺炎数据。她在 2020 年 5 月被解雇,世仇不断升级。那年 12 月,警察突袭了她的家,她于 2021 年 1 月入狱。去年五月,佛罗里达州监察长办公室授予她 T2 告密者保护。

即使数据是准确的,它的呈现也可能被操纵来欺骗。如果你仔细观察 2020 年 4 月出现在福克斯新闻频道的每日新增病例的新冠肺炎图表,你会注意到 y 轴从 30 开始,而不是零,沿着 y 轴的增量是对数的,而不是线性的,这具有最小化峰值的图形效果。大多数人看了这张图表后会认为新病例的增长速度没有实际那么快。

从我收集的问题列表来看,很明显,数据科学家需要了解他们的工作如何影响社会。(当然,他们中的许多人已经对像我这样的问题进行了很多思考。)我还认为,这个领域需要专业组织来概述标准,在数据科学家中传播这些标准,并对公众进行相关教育。这一职业将增加公众和媒体的信任,并在社会和政治争议中赢得信誉。

对于医疗专业人士,美国医学协会出版每月一期的 AMA 伦理杂志。美国律师协会代表全世界受迫害的法律专业人士大声疾呼。

谁在为丽贝卡·琼斯说话?

数据工作的六项技能——不全是技术性的

原文:https://towardsdatascience.com/six-skills-for-data-work-its-not-all-technical-65519e667111?source=collection_archive---------36-----------------------

是的,也许有一天你会和另一个人谈论数据。(Pxhere)

超越编码,变得有用和有用

人们沉迷于参加什么训练营,最热门的 ML 算法,以及使用哪个 SaaS 产品。但是数据科学/分析的技术细节只是工作的一部分。

我会列出至少这六项技能:

  1. 后端编码。这就是用 Python、SQL 和其他语言处理数据管道,即数据工程。相当严肃的编码;CS 学位推荐。
  2. 分析编码。分析的最大部分是数据准备:把它整理成正确的形状,清理,并使事情一致。还可以通过代码指定分析和可视化。编程是轻的;经常是 R 或者 Python。GUI 工具也许也能完成这项工作。
  3. 统计。实际上知道运行什么分析,以及如何解释结果。范围很广,从基本的汇总统计到非常复杂的建模,再到尖端的 ML。这将决定需要多少正式背景,从一些统计学课程到博士学位。
  4. 业务&领域知识。以对客户有意义的方式提出问题,并获得他们可以采取行动的结果。更基本的是,你需要简单地理解你的数据——盲目地把每个变量都扔进某个自动 ML 不会给你带来多少。这些大部分都是在工作中获得的。
  5. 沟通。你通常需要告诉别人你在做什么,或者有什么结果。当你和非技术人员交谈时,这是最难也是最重要的。它包括能够抑制你的行话,用简单的语言解释事情。但是写作、设计和视觉化都会发挥作用。
  6. 怀疑的好奇心。我认为提出想法、探索、进行外部研究、学习新技能和尝试新事物的能力是数据工作最大的个性特征。它必须受到怀疑主义的影响:知道数据不能告诉你什么,以及你的探索应该如何组织。

我的最后一项可能会引起争议:它更像是一种性格特征或一种方法。但是它可以随着时间的推移和经验的积累而发展。我认为好奇心对独立尤为重要——每个人都希望员工独立。如果你不能提出自己的想法,那么你必须依靠别人,比如你的老板或同事,来指导你前进的每一步。学校也不教好奇心——通常相反,所以你会遇到需要详细指导才能完成任何事情的聪明人。

人们经常强调技术技能,但实际上这些技能现在很容易得到。软技能相对较少——因为倾向于技术的人经常忽略它们。一个大的团队有可能负担得起只在几个领域表现出色的专家,包括纯技术领域。但是如果你也能发展你的软技能,会有更多的机会:在更小的团队,更小的公司,以及更多种类的角色。无论你发现自己在哪里,如果你能把数据放在一个更大的背景下,你会更有用:通过与人交谈,保持好奇,并使数据相关。

成功的人工智能治理的六个阶段

原文:https://towardsdatascience.com/six-stage-gates-to-a-successful-ai-governance-14ab0787a380?source=collection_archive---------23-----------------------

走向责任 AI(第四部)

决策、文件和 RACI 矩阵

来源:照片由勒内·迪安达Unsplash 上拍摄

负责任地使用 AI 应该从详细评估 AI 带来的关键风险开始[1],然后是对应该遵循的原则的良好理解[2],然后是从自上而下和端到端的角度对 AI 进行治理[3]。我们在之前的文章中已经讨论过这些。在本文中,我们将重点关注第一道防线,深入探讨价值界定、价值发现、价值交付和价值管理的九步数据科学流程[4],并强调治理的维度。

概观

G 考虑到对治理的关注,我们希望在九个步骤的过程中回答关于 的关键问题,即谁基于什么理由做出什么决策 。虽然我们在这个过程中有九个步骤,但我们只考虑在这个过程中做出关键决策的那些点。下图显示了我们的九个步骤流程、九个步骤的关键结果以及我们做出重大决策的六个阶段关口。这些决定是:

  1. 拥有一个 AI 解决方案到底值不值得?
  2. 我们如何设计(建造、购买或租赁)人工智能解决方案?
  3. 模型符合我们的期望吗?
  4. 我们将模型部署到生产中吗?
  5. 该模式是否已准备好过渡到“常规业务”运营?
  6. 模型应该继续保持原样、重新培训、重新设计还是退役?

图 1:九个步骤和六个阶段(来源:普华永道分析)

阶段门 1:是否值得拥有一个人工智能解决方案?

这个阶段关口发生在项目的价值范围阶段的业务和数据理解的第一步。这无疑是最关键的一步,因为它决定了我们是否想继续拥有一个人工智能解决方案。要做出这一决定,我们应该了解五个关键领域:

  • 业务影响:首先,是围绕正在考虑的业务目标或用例。我们正在使哪些业务活动、决策或行动更加高效或有效?这个解决方案是一个全新流程或系统的机会吗?对部署该解决方案的组织有什么业务影响—时间节约、成本节约、收入增加、更好的体验等。?
  • 数据访问和使用:其次,是了解哪些数据是可用的、可访问的,并且可以在道德上用于开发 AI 解决方案。诸如我们有足够的数据来建立模型吗?这些数据是否被恰当地标注或者可以被注释?有多少历史数据可用?数据的动态性如何,或者数据更改的频率和显著程度如何?
  • 车型使用语境:第三,是了解车型投产后会如何使用。这应该回答这样的问题,谁是这个模型的用户?他们的业务或技术理解水平如何?人与模型之间的交互是什么,例如,人在回路中,人在回路中,人在回路外?
  • 社会影响&危害评估:第四,理解人工智能解决方案更广泛的社会和道德含义。有多少人会受到影响?它是否侵犯了人权,如丧失个人自由或隐私,或对环境产生不利影响?它会造成身体、情感、心理或经济上的伤害吗?它有可能误导或操纵观点吗?
  • 风险评估:第五,确定 AI 解决方案的风险等级。风险等级分为 1-3 级,在某些情况下为 1-5 级。风险分层基于人工智能解决方案造成的危害的严重性和频率,以及该解决方案的潜在社会影响。这种评估应该回答这样的问题:人工智能解决方案会带来什么样的风险?解决方案的风险和暴露(财务或非财务)的可能性有多大?需要考虑哪些潜在的风险缓解策略和控制类别?

这一步涉及许多利益相关者。我们使用以下 RACI(负责-问责-咨询-知情)矩阵来描述这些利益相关者的角色。下面的阶段门卡中记录了决策、文件和 RACI 矩阵的摘要。

图 2: Stage Gate 1 —拥有一个人工智能解决方案是否值得?(来源:普华永道分析)

商业影响——就人工智能解决方案对组织的价值及其社会影响或潜在风险而言,将决定负责任的商业赞助商的资历。经济影响越大或潜在风险越大,就越需要商业道德委员会的批准。

业务规范、社会影响和风险评估文档是产品负责人应该制作的关键文档。负责人,在这种情况下,是业务发起人,做出进行/不进行决策,以进一步评估解决方案的技术可行性。为了权衡继续开发的利弊,发现应用程序的风险和潜在危害以及潜在好处是很重要的。

阶段门 2:我们如何设计(建造、购买或租赁)一个人工智能解决方案?

这个阶段关口发生在项目的价值范围阶段的解决方案设计的第二步。步骤 1 中的业务规范在这个步骤中得到进一步的细化,我们开始探索所需的数据和模型。这一步还决定了人工智能解决方案是否应该建立,或者可以从分析和人工智能供应商或云平台提供商的大型生态系统中购买或租赁。这一步骤中需要考虑的主要方面是:

  • 成功和接受标准:来自上一步的业务规范应该被细化,以阐明正在被自动化的关键活动或者将要被 AI 增强的决策[5]。这也应该考虑模型将如何被使用以及什么数据将是模型的一部分。基于这些,模型的性能标准(例如,准确性、特异性、假阳性率等。),应该就模型的可解释性、可解释性、安全性、隐私性、安全性和健壮性达成一致。我们广义地称之为成功标准。这些也应该转化为一套应该满足的高级测试和验证程序。我们广义地称之为验收标准
  • 构建 vs 购买 vs 租赁分析:使用上述构件,组织可以确定所需的解决方案是否已经存在,是否可以作为托管解决方案租赁。这里的关键考虑是通用模型满足业务需求的程度,成功和已经同意的接受标准。做出推断所需的数据的安全性和机密性以及数据存储的位置也必须发挥重要作用。如果解决方案不能租用,但可以购买,则应进行类似的尽职调查,以确保其符合业务要求、成功的验收标准,而且经济高效。使用专有数据对模型进行的任何再训练都应纳入考虑范围。最后,如果我们正在构建模型,那么应该为如何在培训阶段构建模型以及如何最终部署和使用模型开发一个整体解决方案设计。例如,如果一个系统的可解释性对于成功的标准非常重要,那么就需要一个更简单的模型结构或者一个添加了可解释的人工智能元素的解决方案。
  • (初步)数据集数据表:在数据科学社区中,越来越常见的实践之一是数据集数据表的概念[6]。数据表不仅捕获数据集内的关键数据项及其定义,还捕获收集数据的动机、组成、收集过程、所需的预处理、清理和标记、推荐的用途、数据的共享和分发、数据的维护和停用。虽然在流程的这一阶段,数据手册可能并不全面,但包含其中一些要素的初步数据手册对于做出明智的建模决策非常有价值。
  • (初步)模型卡:与数据集的数据表类似,用于模型报告的模型卡[8]也在数据科学社区得到广泛应用。模型卡提供了正在构建的模型的规范,包括模型的细节、正在使用的算法、模型的预期用途、伦理考虑(如偏见和公平性或可解释性要求)、培训、验证和测试数据。在流程的早期阶段,模型卡将是初步的,将在后续阶段进行完善。模型规格应考虑到上一步中讨论的社会影响和风险评估。
  • 解决方案设计:解决方案架构的概念是软件开发中广为接受的实践。随着模型从独立模型转移到模型工厂[8],我们可以用一些额外的维度来增强这个传统的解决方案架构。捕捉不同模型如何相互作用的模型架构;最终用户如何标记或验证数据;如何构建数据管道来收集、清理、处理和提供模型应该是整体解决方案设计的一部分。
  • 详细的开发计划:通过以上所有的分析,可以建立一个合理的实验计划和实验的初始成本。项目或冲刺计划应该确定人工智能解决方案的培训、测试和开发的关键角色、职责和时间安排。注意我们在别处的讨论[9],模型的项目范围,尤其是以前没有构建过的新模型,受到许多陷阱的影响,在计划价值交付时应该考虑这些陷阱。

此阶段的阶段门卡如下所示。

图 3:Stage Gate 2——我们如何设计(建造、购买或租赁)人工智能解决方案?(来源:普华永道分析)

成功和验收标准将由业务领域专家和数据科学团队共同决定。构建 vs 购买 vs 租赁决策应由产品所有者与业务发起人共同做出。数据表将由数据所有者和数据科学家共同交付,而模型卡将由数据科学家领导。软件解决方案架构师需要与机器学习或模型架构师和数据架构师合作,以完成整体解决方案设计文档。

最终决定是继续构建、购买或租赁人工智能解决方案,还是放弃整个努力,由产品所有者和业务发起人共同做出。本步骤和上一步骤中详述的所有文档都将作为流程的输入。

阶段 3:模型符合我们的期望吗?

这个阶段关口发生在项目的价值发现阶段的末尾。当我们走到这个阶段关口的时候,模型的训练已经通过一个数据提取、预处理、建模、测试的迭代循环完成了。在此阶段关口做出的关键决策是继续进行模型部署还是推迟/放弃模型开发。这一步骤中需要考虑的主要方面是:

  • 成功和验收测试结果:成功和验收标准中描述的模型详细测试应在此阶段关口之前进行。结果应总结模型的性能、模型的可解释性和可解释性、模型的公平性、安全性、控制性、安全性、隐私性、健壮性和可再现性。验收标准中规定或建议的所有验收测试都应进行和评估。理想情况下,模型应该满足在前一阶段关口中商定的成功和验收标准。但是,在某些情况下,可能会决定放宽验收门槛,并批准下一阶段的模型。这只会在特殊情况下发生,应上报给业务发起人和产品负责人考虑。
  • 数据表、模型卡和更新的解决方案设计:当模型培训完成时,可以更新前一阶段关口的关键文档。应该突出对训练模型有用的数据集和数据项。应记录选择的相关模型、使用的超参数、执行的实验以及提供最佳结果的模型集合。对整个模型架构、数据管道和解决方案架构的任何更改也应该更新。
  • 集成和部署需求:一旦模型被批准部署,就可以起草模型部署的规范。它应包括生产中的模型推理是批处理模式还是实时模式,在过渡期间如何验证模型,以及模型需要再培训的频率(例如,过渡期间模型的培训是连续的还是定期的)。性能调优和负载测试需求也应该在需求中详细说明。此外,应详细说明该模型如何与其余应用系统集成(例如,用作 docker 容器或 API 调用,或将拥有自己的独立前端)。
  • 变更管理计划:规划最终部署的最后一步是变更管理计划,包括任何流程变更、AI 解决方案的验证、人员的培训和重新部署。

此阶段的阶段门卡如下所示。

图 4:阶段 3——模型符合我们的期望吗?(来源:普华永道分析)

成功和验收测试结果将由数据科学家和数据工程师准备,并将由二线模型验证人员和道德团队进行验证。更新的数据表将由数据工程师负责,而更新的模型卡将由数据科学家负责。解决方案架构师和 ML 工程师以及模型操作人员将负责集成和部署需求。变更管理计划将由业务团队和产品负责人负责。

阶段 4:我们将模型部署到生产中吗?

只有在前一阶段关卡已经批准部署模型时,才会出现此阶段关卡。这个阶段关口发生在项目的价值交付阶段的模型部署步骤的末尾。当我们来到这个 stage gate 时,模型就可以部署了。在此阶段关口结束时做出的关键决策是发布部署以供生产使用。

  • 集成&部署测试结果:根据前期指定的集成和部署需求,模型可以作为 API 端点,或者拥有自己的 web 接口,或者作为具有 UI 的独立应用程序交付。集成测试应该在这个阶段执行。此外,性能测试、负载平衡——特别是如果模型将在生产中进行训练,以及模型使用偏差测试、与最终用户的可解释性测试、健壮性测试和对抗性攻击测试都应该在这个阶段执行。
  • 流程清单:基于已部署模型的交互模式(例如,人在回路中、人在回路上、人在回路外)的所有流程变更都应执行。如果需要的话,模型验证的流程流应该具有适当的例外、升级和批准标准。模型操作、数据操作和 SecOps 流程应在此阶段到位。
  • 培训清单:任何新角色(如模型操作、数据操作等)或对现有角色的更改都应记录在案,并明确责任和活动。如果需要对大量用户(即临时用户或高级用户)进行新角色或角色变化的培训,则应在部署之前进行。

此阶段的阶段门卡如下所示。

图 5:阶段 4——我们将模型部署到生产中吗?(来源:普华永道分析)

集成和部署测试结果将由数据科学家、ML 工程师和软件工程师准备,并将由二线模型验证人员和道德团队进行验证。流程清单和培训清单将由不同的 XOP(模型操作、数据操作、安全操作)小组负责。培训将由业务团队和变革管理专家负责。

阶段 5:模型是否准备好过渡到“照常营业”操作?

这个阶段关口发生在项目的价值交付阶段的末尾。尽管该模型已投入生产,但在转换为 BAU(“常规业务”)模型之前,应密切监控该模型的运营。

  • 模型监控结果:传统的软件部署通常不需要这个阶段关口,因为软件的工作不敏感或者不会随着新数据而改变。然而,在模型的情况下,有可能出现一些“漂移”[11] —数据漂移、特征漂移、概念漂移等。在部署模型的最初几个月,应该对此进行密切监控。密切观察的周期取决于模型的使用频率和数据的到达率。
  • BAU 过渡检查表:对模型进行严密监控后,如果模型仍值得生产使用,则应将其过渡到运营团队。确保分配和培训适当的操作人员是将模型过渡到 BAU 的一个关键方面。在此阶段,应建立并商定持续监控标准(即性能、偏差、可解释性等)、干预阈值和测试频率。

此阶段的阶段门卡如下所示。

图 6:阶段 5——模型是否准备好过渡到业务照常运行?(来源:普华永道分析)

模型监控结果将由不同的操作角色(模型操作、数据操作)在数据科学家的协助下准备。BAU 过渡将是业务团队和运营部门的共同责任。

阶段 6:模型应该继续保持原样,重新培训,重新设计,还是退役?

价值管理阶段的评估和检入步骤中,此阶段关口会定期出现。评估频率将在过渡步骤或价值交付阶段结束时确定。在每次评估中要做出的决定是继续模型而不做任何改变,重新训练模型,重新设计,或者淘汰模型。为了做出这一决定,应检查以下方面:

  • BAU 监控结果:前一阶段的所有模型监控结果都应包含在 BAU 监控中。此外,应通过比较本期和前期的结果/原始模型部署和训练结果来评估模型衰减。任何衰退或不稳定都可能导致模型的重新训练或重新设计。
  • ROI 价值:除了模型性能,业务还应该包括模型的 ROI 或价值评估。这应考虑模型的益处(例如,效率收益、有效性、增强体验、成本节约、收入收益),并确定将模型投入生产的益处是否超过维护模型的成本。这将决定该型号是否应该淘汰,或者用不同的或更新的型号替换。

此阶段的阶段门卡如下所示。

图 7:阶段入口 6——模型应该继续保持原样、重新培训、重新设计还是退役?(来源:普华永道分析)

在数据科学家和业务团队的协助下,不同的操作角色(模型操作、数据操作)将准备 BAU 监控结果。ROI 价值分析将由与数据科学家和模型操作人员合作的业务团队负责。重新培训、重新设计或淘汰模型的最终决定应由产品所有者做出。

作为 9 步模型开发和管理的一部分的 6 个阶段关口是关键的里程碑,是任何软件 2.0 管理过程不可或缺的一部分。我们在这里概述的规范的细节和深度可以根据模型的规模、范围、复杂性和社会影响进行调整。在后续文章中,我们将讨论规范以及如何根据模型的风险分层调整这些规范。

作者:

参考文献

[1] 人工智能风险的五种观点:理解人工智能的阴暗面(迈向负责任的人工智能——第一部分)

[2] 企业负责任人工智能的十大原则(迈向负责任人工智能——第二部分)

[3] 负责任地使用人工智能的自顶向下和端到端治理(迈向负责任的人工智能——第 3 部分)

[4] 模型生命周期:从理念到价值

[5] 开发以人为中心的人工智能的十种人类能力和四种智能

[6] 数据集数据表

[7] 模型卡

[8] 车型进化:从独立车型到车型工厂

【9】将模型误认为软件的后果

[10] 端点 vs API

[11] 关于数据漂移的初级读本

通过测试和扫描强化模型服务 API 的六种方法

原文:https://towardsdatascience.com/six-ways-to-harden-your-model-serving-api-with-tests-scans-b006b6137b19?source=collection_archive---------35-----------------------

去生产?利用这些免费资源强化您的 API!

你好,朋友们!我回来了!抱歉中断了两个月。我似乎总是在夏天失去发布新东西的动力,但现在我有了一台全新的 Mac Mini 来支持我的工作,我带着一些新鲜的新内容回来了。

作为一名财富 50 强公司的机器学习工程师,我的主要职责是将机器学习模型部署到生产中,供多个企业级系统使用。大多数部署都是以模型服务 API 的形式出现的。API 接收一组数据,基于该数据生成一个推理,并将推理结果返回给调用者。

考虑到这些服务模型的 API 会接触到很多人,确保这些 API 以一种我们可以尝试保证 100%正常运行的方式得到加强是非常重要的。为此,我们试图通过执行大量不同的测试和安全扫描来强化我们的 API,这些测试和扫描表明我们在部署之前认真考虑了所有风险因素。此外,这些测试/扫描结果被保存到一个公共的存储库中,以防任何审计人员有兴趣看到这些 API 的“测试证据”。

当然,有许多不同的工具和供应商解决方案来支持这项工作,但是对于这篇文章,我将重点关注免费资源。如果你是一家大型企业公司,我可能会鼓励你研究一些供应商的解决方案,但是这篇文章至少会提供一个在创建模型服务 API 时应该考虑的不同因素的入门。

在我们进入我们的资源之前,让我们先快速看一下我为支持这篇文章而创建的虚拟模型 API。如果你想跟随我的代码,请在 GitHub 找到它。

API 概述

对于这个项目,我针对流行的公共 Iris 数据集创建了一个非常简单的逻辑回归模型。因为我不一定关心为我们的项目创建一个花哨的预测模型,所以您会发现我没有做任何类似特殊功能工程或任何超参数调整的事情。在我下面的训练脚本中,你可以看到我正在做的唯一的事情是用 Scikit-Learn 加载数据集,从数据集分离目标y值,将训练数据集馈送到 Scikit-Learn LogisticRegression模型,并将序列化的模型保存到名为iris_model.pkl的 pickle 文件中。

# Importing the required Python libraries
import numpy as np
import pandas as pd
import pickle
from sklearn import datasets
from sklearn.linear_model import LogisticRegression# Loading the iris dataset from Scikit-Learn
iris = datasets.load_iris()# Converting the iris dataset into a Pandas DataFrame
df_iris = pd.DataFrame(data = np.c_[iris['data'], iris['target']],
            columns = iris['feature_names'] + ['target'])# Separating the training dataset (X) from the predictor value (y)
X = df_iris.drop(columns = ['target'])
y = df_iris[['target']]# Instantiating a Logistic Regression (LR) model
lr_model = LogisticRegression()# Fitting the dataset to the LR model
lr_model.fit(X, y)# Saving the model to a serialized .pkl file
pkl_filename = "../models/iris_model.pkl"
with open(pkl_filename, 'wb') as file:
    pickle.dump(lr_model, file)

创建好模型后,我就可以创建 API 了。作为下面脚本的一部分,您可以看到我正在使用 FastAPI 框架创建具有两个端点的模型服务 API。第一个默认端点只是返回一个 JSON 消息,声明Hello friends!。第二个端点/predict是实际接收数据并从我们的模型返回推理结果的端点。同样,我们在这里做一些非常基础的东西。你实际的 API 可能会比这个复杂得多,但是我们在这里保持事情简单。

import pandas as pd
import pickle
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse## API INSTANTIATION
## ----------------------------------------------------------------
# Instantiating FastAPI
api = FastAPI()# Loading in model from serialized .pkl file
pkl_filename = "../models/iris_model.pkl"
with open(pkl_filename, 'rb') as file:
    lr_model = pickle.load(file)## API ENDPOINTS
## ----------------------------------------------------------------
# Defining a test root path and message
[@api](http://twitter.com/api).get('/')
def root():
    msg = {'message': 'Hello friends!'}
    return JSONResponse(content = msg, status_code = 200)# Defining the prediction endpoint
[@api](http://twitter.com/api).post('/predict')
async def predict(request: Request): # Getting the JSON from the body of the request
    input_data = await request.json() # Converting JSON to Pandas DataFrame
    input_df = pd.DataFrame([input_data]) # Getting the prediction from the Logistic Regression model
    pred = lr_model.predict(input_df)[0]return JSONResponse(content = pred, status_code = 200)

最后,如果您想自己测试这个 API,我创建了一些示例 JSON 数据,可以很好地满足这些目的。看起来是这样的:

{"sepal_length":5.1,"sepal_width":3.5,"petal_length":1.4,"petal_width":0.2}

这就是这个 API 的大部分内容!既然我们已经为这篇文章的剩余部分打下了基础,现在让我们开始进入所有可以像这样强化 API 的方法。

1.使用 Pytest 进行单元测试

原因之一,这可能是你最熟悉的一个。即使您不习惯强化 API,您也很可能已经为自己的健全性检查创建了一个单元测试。出于我们的目的,我们将使用pytest和 FastAPI 测试客户端。安装pytest非常容易。您只需运行以下命令:

pip3 install pytest

现在,为了在 CLI 中实际使用pytest,我们将运行一个小的 bash 脚本,该脚本执行我们的测试文件并将输出写到reports目录中。看起来是这样的:

pytest --log-file=reports/unit_test_report.txt unit_testing/

好的,这很简单…但是它怎么知道要测试什么呢?注意在上面命令的末尾,我们让pytest遍历unit_testing目录下的所有文件。在我的unit_testing目录中,我有一个 Python 脚本,它既设置单元测试客户端,又运行单元测试。完整的脚本如下所示:

import json
from fastapi.testclient import TestClient
from container.api import api## PYTEST SETUP
## --------------------------------------------------------------------------------------------------------------------
# Instantiating the test client from our container's API
client = TestClient(api)# Loading test JSON file
with open('test_json/test_data.json', 'rb') as file:
    test_json = json.load(file)## UNIT TEST CASES
## --------------------------------------------------------------------------------------------------------------------
# Creating a unit test for the basic root path
def test_root_message():
    response = client.get("/")
    assert response.status_code == 200
    assert response.json() == {'message': 'Hello friends!'}# Creating a unit test for the prediction endpoint
def test_predict():
    response = client.post('/predict', json = test_json)
    assert response.status_code == 200

在这里,你会注意到两个不同的部分。在“Pytest Setup”一节中,我们实际上是使用之前创建的同一个 FastAPI 脚本来实例化 FastAPI 测试客户端,然后加载少量的样本数据。在第二部分中,我们使用 FastAPI 测试客户端和样本数据来运行一些带有assert语句的测试用例。注意,为了通过单元测试,这些assert语句具有需要满足的特定条件。如果这些测试用例中的任何一个失败了,Pytest 将会适当地记录下来,并简要解释失败的原因。在下面的截图中,看看当我有意改变单元测试 look 为“Hello friend!”而不是恰当的“你好朋友!”

作者截图

尽早设置单元测试是非常好的,因为您可以设置一个 CI/CD 管道,在您对 API 进行修改的任何时候都可以运行这个测试。如果你犯了一个错误,单元测试会发现它并让你知道问题。单元测试无疑让我避免了许多粗心的错误,所以一定要为您的模型服务 API 编写单元测试!

2.使用蝗虫进行性能测试

由于 API 通常每天被调用数千次,如果不是数百万次的话,您肯定希望确保您的模型服务 API 能够适当地处理负载。为此,我们有一个名为蝗虫的工具来帮助我们。现在,我不打算在这个曾经的上花太多时间,因为我已经写了一个完整的帖子,更详细地介绍了蝗虫。如果你想了解更多,我肯定会鼓励你去看看。这里我要注意的唯一一件事是,post 主要关注在 UI 模型中使用 Locust。在他们所谓的“无头”模式下,完全可以在没有用户界面的情况下运行它。下面是该命令的样子:

locust — locustfile performance_testing/locustfile.py — headless — users 15 — spawn-rate 5 — run-time 30s — only-summary — csv reports/performance_test

在运行该命令时,Locust 将执行性能测试,然后将结果作为 4 个单独的 CSV 文件的一部分写出。老实说,我希望有一种方法可以将它作为一个单独的报告导出,但是我认为有总比没有好!同样,我们不打算深入讨论蝗虫命令是如何工作的,所以如果你想了解更多,我鼓励你看看我以前的帖子。

3.安全的依赖性扫描

这个和下一个是齐头并进的,但是我们将从这个开始,因为它是两个中“不太健壮”的。当容器化您的模型服务 API 时,您肯定需要执行一个pip install来安装您需要的所有 Python 库来支持您的工作。依赖项扫描将查看requirements.txt文件中依赖项的所有固定版本,并让您知道哪些类型的漏洞与相应的 Python 库相关联。

出于我们的目的,我们将使用一个名为 Safety 的免费工具。安全性非常简单易用。要安装安全,您只需运行以下命令:

pip install safety

实际执行安全命令也同样简单。在下面的命令中,你会看到它直接指向我的requirements.txt文件,然后适当地保存一个完整的输出报告。

safety check -r ../dependencies/requirements.txt --full-report -o reports/dependency_scan_report.txt

如果你不知道,有一个由美国国土安全部支持的名为 CVE 的开源数据库。 CVE 数据库包含所有已知和常见的安全漏洞,包括一些如何修复适当的 CVE 的选项。现在,我正在努力将我的依赖关系与一些最新的 Python 库联系起来,但是只是为了演示当安全发现一些问题时它是如何工作的,让我们看看当我用一个非常旧版本的 Uvicorn 扫描一个requirements.txt文件时会发生什么。(准确的说是 uvicon 0 . 0 . 4):

作者截图

如你所见,这个旧版本的 Uvicorn 有两个安全问题。它提供了一个很好的描述,也包括适当的 CVE 数。如果您想了解更多关于该漏洞的信息,您可以使用 CVE 号码进行搜索。例如,第一个漏洞记录了 CVE-2020–7694。如果你去这个链接,除了 Safety 提供的相对简短的描述,你还可以了解更多关于这个漏洞的信息。

这就是安全,但在继续之前,我觉得我应该提到使用供应商解决方案的价值高于安全,特别是如果你为一家大公司工作。Safety 只查询政府的 CVE 数据库,该数据库并不总是与最新信息保持同步。供应商解决方案通常包含他们自己的数据库以及这些额外的漏洞问题,因此,如果您的公司特别关注安全性,我可能会建议您使用这些解决方案中的一种。

4.用 Docker 扫描集装箱(Snyk)

依赖扫描很棒,但是它的问题是,如果你要走容器化的路线(你可能会这样),它不能得到与你使用的基本映像相关的所有东西。最近,Docker 与一家名为 Snyk 的第三方公司合作,将一个容器扫描工具直接嵌入 Docker CLI。它非常容易使用,并自动捆绑了最新版本的 Docker。唯一的“问题”是,你需要一个 Docker Hub 帐户,但也可以自由创建。如果您以前没有这样做过,您可以在这里创建一个 Docker Hub 帐户

要在本地使用容器扫描,您需要做两件事。首先,你需要登录 Docker Hub。为此,请运行以下命令:

docker login

运行该命令将启动一些其他提示,要求您输入各自的 Docker 用户名和密码信息。当然,您需要做的下一件事是构建容器!出于我的目的,我通过如下方式构建我的容器,使事情变得非常简单:

docker build -t iris-api:dev .

构建好容器并登录 Docker 后,我们就可以执行容器扫描了!该命令如下所示:

docker scan iris-api:dev | tee reports/container_scan_results.txt

现在,你可能想知道tee在这里做什么。我们所有的其他工具都提供了某种输出结果的机制,但是docker scan命令没有任何内置选项来导出结果。相反,输出仅直接显示在 CLI 中。tee允许我们从命令行界面获取输出,并保存到文本文件中。如果你想在其他场合使用tee,你完全可以。无论如何不仅限于docker scan。以下是我的扫描结果:

作者截图

如您所见,它引发了相当多的漏洞,绝对超过了我们的依赖扫描。这是因为我使用的是python:3.9-slim-buster,Snyk 暴露了许多与 Debian OS 相关的漏洞。我实话实说:我不知道我是否同意所有这些发现,所以慎用。我认为 Snyk 是一家较新的公司,所以我乐观地认为他们会改进他们的流程,以正确反映适当的漏洞!

5.用 Bandit 进行静态代码扫描

我们在列表上的最后一次安全扫描着眼于您的 Python 代码的潜在漏洞,我们在一个名为 Bandit 的工具的支持下完成这项工作。对于这次扫描,我们将指向包含所有 Python 文件的目录,它将指出从纯 Python 的角度来看我们应该小心的事情。在我的例子中,我的所有 Python 文件(包括带有 FastAPI 的文件)都在一个名为container的目录中,因此我们将使用以下命令让 Bandit 扫描那里:

bandit --format=txt --output=reports/static_scan_report.txt -r ../container/

在我们的场景中,Python 代码非常简单,所以扫描并不明显。如果你好奇,下面是我的特定扫描的输出结果:

作者截图

如您所见,它正确地指出,我应该对导入 pickle 文件保持谨慎,因为有人将恶意软件打包成 pickle 文件的一部分并不罕见。在我们的例子中,我们知道我们构建了 pickle 文件,所以我们可以看着它说,“谢谢 Bandit,但是我很好!”

6.用 Pylint 编码棉绒

这最后一个是…我要说可选。(一会儿你就知道为什么了。)如果你回想起你的大学时代,你可能会想起你的教授让你用一种特殊的格式写论文,如 MLA 或 APA 格式。如果你不知道的话,编码界有一个非常相似的东西,叫做 PEP-8。Pylint 专门检查您的 Python 代码,以确保它遵循 PEP-8 标准以及其他一些标准。安装 Pylint 非常容易。只需运行以下命令:

pip install pylint

为了针对您的代码运行 linter,您需要做的就是以如下方式使用pylint:

pylint ../container/ --reports=y --output=reports/linter_report.txt

该命令的输出将提供关于您如何改进的完整描述,并且还会提供……战栗…您的表现如何,满分为 10 分。(越高越好。)现在,让我们看看我的分数如何:

作者截图

是的,你没看错。我得了满分 10 分的 3.00 分。请允许我发表一点小小的意见…亲爱的读者,你认为我的代码有那么糟糕吗?我因为提供有力的评论和把事情分成“部分”而臭名昭著,所以我接受乌姆里奇的说法,我只得到区区 3.00 分。但是,嘿,我在大学里也和教授们在 APA 格式上争论过,所以这是我的天性,在这些能力上与众不同。😂尽管如此,我觉得如果您的组织真的关心遵循 PEP-8 指南,这是值得一提的。

这篇文章到此结束!回来的感觉真好。希望你喜欢这个,并期待在不久的将来会有更多精彩的内容。我已经在 SageMaker,Terraform 上发了帖子,还有更多正在制作中。会很好玩的!感谢阅读!

规模很重要——是吗?

原文:https://towardsdatascience.com/size-matters-or-does-it-98bbd613313f?source=collection_archive---------63-----------------------

随着数据量的增加,ML 模型的性能如何提高?什么时候贴标签的成本大于收益?

Ravi Sharma 在 Unsplash 上拍摄的照片

情感分析是一项艰巨的任务。我们从一堆像推文或评论这样的文档开始,将它们翻译成数字,并试图根据某种 ML 算法来确定该消息是否具有积极或消极的意义。

一方面,拥有更多数据应该会提高我们模型的质量。另一方面,给数据贴标签会很昂贵(或者至少很烦人)。因此,我们希望有足够多的数据来训练模型,同时密切关注标签的成本。

但是模型的准确性如何随着更大的数据集而提高呢?在这篇博文中,我们将看到模型的准确性和特定用例中的数据量之间的关系。

在下文中,我们使用数据集 对 Kaggle 的金融推文 进行情绪分析,该数据集由超过 4800 条推文及其标签(“正面”、“负面”和“中性”)组成。这相当大量的推文允许我们将数据分成合理大小的训练和测试数据集。该数据集是不平衡的,有 2879 条中立的,1363 条正面的和 604 条负面的推文。

训练数据集的大小将如何影响模型在测试数据上的“样本外”性能?尺寸重要吗?

为了找到这个问题的答案,我们首先从整个数据集中随机抽取 2400 条推文,这些推文构成了训练数据。剩下的数据是测试数据,将用于评估模型的性能。

为了简单起见,我们使用推文的 tf-idf 表示,训练逻辑回归模型,并通过 10 重交叉验证来调整正则化强度 C 。我们对不同数量的数据重复这一过程。(在 this GitHub Repo 中可以找到具有不同表示和模型的情绪分析)

所以事不宜迟,让我们深入到代码(基于上面提到的情感分析)。首先,我们需要加载所需的包和数据,进行一些预处理,并将数据分成训练和测试数据:

接下来,我们为 tf-idf 表示和 10 重交叉验证定义一些辅助函数:

最后,我们通过 10 重交叉验证选择正则化参数 C ,并使用各自的参数对从 n = 300n = 2400不同数据量的模型进行训练:

结果如图 1 所示。

图 1:作为训练数据量的函数的逆正则化强度 C(红色)和准确度(蓝色);x 轴:训练数据量;y 轴:C(红色),精度(蓝色);作者图片

此外,以下是 n = 300n = 2400的混淆矩阵:

n = 300, LogReg with C = 0.4
Confusion Matrix:
[[ 203  377  139]
 [ 144 1222   52]
 [  55  118  113]]Estimated accuracy:0.635n = 2400, LogReg with C = 0.9
Confusion Matrix:
[[ 399  246   74]
 [ 186 1148   84]
 [  35   68  183]]

Estimated accuracy:0.714

测试数据集的精确度随着数据量 n 的增加而增加。不足为奇的是,准确性在开始时提高很快,然后随着数据量的增加而降低。正则化参数没有明确的趋势,尽管我们预计它会随着 C 正则化强度的倒数而增长,并且我们可能预计正则化强度会随着数据量的增加而减弱,这在结果中有所反映。

如前所述,数据集是不平衡的,大约 60%是中立的,28%是积极的,12%是消极的。毫不奇怪,基于 300 条推文的模型通过偏好中性推文来实现其准确性。尽管准确率“仅”从 63.5%提高到 71.4%,但定性差异甚至更大,因为基于 2400 条推文的模型平衡了其预测。根据具体的使用情况,另一个指标,如精确度、召回率或 f1 分数,可能更适合捕捉定性差异。

结论

在给定的场景中,大量的训练数据提高了模型的准确性和质量。尽管精确度的提高看起来很小,但是模型的质量却有了显著的提高。所以,至少在这种情况下,尺寸确实很重要。

当然,这一发现不能推广到其他场景和其他数据集,但它给出了一种直觉,即较大的数据集导致(在一定程度上)质量的提高,这可能不会反映在准确性上。给定的例子进一步表明,模型质量的改进增加了成本,并且在应用中可能是不需要的。对数据和模型选择进行适当的预处理(为了简单起见,这里省略了)可能对改进模型更有效。

Spark 中的数据失真?加盐来补偿

原文:https://towardsdatascience.com/skewed-data-in-spark-add-salt-to-compensate-16d44404088b?source=collection_archive---------2-----------------------

用 SALT 技术处理偏斜数据的分步指南

图片:@ tangerinenwtUnsplash

如果您使用 Apache Spark 已经有一段时间了,那么您一定看到过以下错误:java.lang.OutOfMemoryError: Java heap space

内存不足(OOM)错误是阻止 Spark 作业成功完成的最常见错误之一。不均匀分布的键,称为数据倾斜,通常会导致这个问题。

有许多方法可以解决 Spark 中的内存不足问题。蛮力的方法是给执行程序增加更多的内存,并希望它能起作用。Spark 也有许多调整参数来重新平衡内存。倾斜的数据是一个数据集问题。除了优化火花参数,用户通常有责任使用数据本身来解决问题。本文将使用 SALT 来解决数据倾斜问题。

了解数据不对称

在 Spark 中,大范围转换涉及分区之间的数据洗牌。混洗是将数据从映射器传输到缩减器的过程。

shuffle 是 Spark 重新分配数据的机制,以便在分区之间对数据进行不同的分组。— Spark 文档

作者图片

洗牌是一个昂贵的操作。如左图所示,混排通常不是 1:1 复制,因为哈希键通常用于确定数据如何分组以及复制到哪里。这个过程通常意味着数据通过无数的执行器和机器被复制。如果您有一个比其他键大得多的键,这个“热键”会导致数据倾斜。

数据偏斜与 Spark 无关,这是数据集的本质。例如,如果我们执行一些需要按城市细分的销售分析。纽约、芝加哥、旧金山等人口较多的城市更有可能出现数据倾斜问题。

下一个问题变成了,

我们如何平衡密钥分布,使某些密钥不会成为热点?

这个问题的答案是使现有的键略有不同,这样它们可以均匀地处理。一种选择是找到另一个字段,将其作为组合键添加,或者散列整个键集。同样,这只有在我们选择的新字段使组合键均匀分布的情况下才有效。如果这不起作用,你需要一些随机性来帮助,这就引入了盐。

盐是什么?

在密码学中,salt 是随机数据,用作散列数据的单向函数的附加输入,是密码或短语— 维基百科

密码学中的 SALT 思想在不知道数据集的任何上下文的情况下给密钥引入了随机性。这个想法是,对于一个给定的热键,如果它与不同的随机数组合,我们最终会注意到在一个分区中处理给定键的所有数据。SALT 的一个显著优点是它与任何键都无关,并且您不必担心一些上下文相似的键再次具有相同的值。

在 Spark 中,SALT 是一种添加随机值来均匀推送 Spark 分区数据的技术。对于像 join 操作这样需要洗牌的大范围转换,采用这种方法通常是好的。

下图显示了 SALT 将如何改变密钥分布。键 1(浅绿色)是导致单个分区中数据倾斜的热键。在应用 SALT 之后,原始密钥被分成 3 个部分,并驱动新密钥洗牌到与以前不同的分区。在这种情况下,键 1 进入 3 个不同的分区,原始分区可以在这 3 个分区中并行处理。

作者图片

如何在火花中使用盐

在 Spark 中使用盐的过程可以分解为:

  1. 添加一个新字段,并用随机数填充它。
  2. 将这个新字段和现有的键组合成一个组合键,执行任何转换。
  3. 处理完成后,合并最终结果。

我们可以写一行 spark 代码,如下所示:

最终想法

今天,大多数数据处理不会自动抵消高度倾斜的数据,而是由用户应用特定的逻辑来平衡数据。SALT 是在不知道数据上下文的情况下解决扭曲数据问题的通用方法。我希望这是有助于解决数据失真问题的信息。

希望这个故事对你有帮助。本文是我的工程&数据科学系列的部分,目前包括以下内容:

Chengzhi Zhao

赵承志

数据工程和数据科学故事

View list47 stories

你也可以 订阅我的新文章 或者成为 推荐媒介会员 可以无限制访问媒介上的所有故事。

如果有问题/评论,请不要犹豫,写下这个故事的评论或者通过 LinkedinTwitter 直接联系我。

获得数据/业务分析师职位所需的技能和项目 2021

原文:https://towardsdatascience.com/skills-and-projects-you-need-to-get-a-data-business-analyst-position-2021-d5c21c88e3bd?source=collection_archive---------12-----------------------

新毕业生成为数据/业务分析师指南的第 2 部分

https://elia nag . medium . com/skills-and-projects-you-need-to-get-a-data-business-analyst-position-2021-d 5c 21 c 88 E3 BD

内容

项目
SQL
网站

项目

你可能听说过很多关于项目的重要性。

项目对于申请成为数据分析师或商业分析师的应届毕业生来说尤为重要,因为它们展示了你在课堂之外可能用不到的技能的熟练程度

尽管我可能想把我的数据结构课程列为经验,因为我在这期间使用了 Java,但是看在上帝的份上,请不要把任何计算机科学实验室或课程放在你的简历上。将实验室放在互联网上不仅可能直接违反你学校的荣誉准则,而且看起来也很业余。例外的情况是,你可以在简历中学位下面的课程重点部分列出课程。

什么是好的项目?

  • 酷,不寻常的数据集
  • 清晰、可回答的问题
  • 用来回答这个问题的可视化和统计技术
  • 对你问题的某种形式的回答

互联网上有一些疯狂的天才在做疯狂而复杂的数据科学项目。请记住,这些人中的大多数都有硕士学位、博士学位和丰富的工作经验。请放心,你不一定要成为这些人中的一员才能找到工作。

你会注意到我没有提到任何机器学习技术。这是因为你不需要知道机器学习就能得到一份入门级的数据分析师工作。如果你幸运的话,他们会让你在工作的时候玩机器学习。与你一起工作的数据科学家将完成大部分实际的机器学习。

您使用的分析技术不一定是疯狂的,至少对于数据分析师或业务分析师来说是如此。事实上,你甚至不需要包括任何种类的机器学习技术。只要对数据进行一些漂亮的可视化处理,就能告诉你一些当你看电子表格时并不明显的东西。

也就是说,如果你想使用机器学习,那就去做吧!机器学习很有趣,有很多方法可以将它集成到你的项目中。

如何找到数据集?

数据集的来源数不胜数。卡格尔和 data.gov 是很好的一对。大多数数据集都可以下载。csv 文件,这是一种在大多数语言和应用程序中易于使用的文件格式。

如果你对机器学习感兴趣,你应该知道一些经典数据集。你可以在 UCI 机器学习库找到它们。

请记住:尽量不要在你的项目中使用任何 UCI 数据集。它们被极其普遍地使用,这意味着人们已经把它们分析死了。它们主要用于在学术论文中比较机器学习技术。但是作为一个机器学习的人,你应该了解他们。

我的建议是:

我会考虑您想要处理哪种数据,而不是查看现有的数据集。在你有了一个想法之后,专门搜索一个数据集。您将找到不常用的数据集,并最终得到一个更有趣的项目。

自问:

  • 我好奇的是什么?我对什么有热情?
  • 有什么事情是我已经知道一点点而其他人不知道的?
  • 我希望主要处理定量数据还是定性 e 数据?
  • 我想通过这个项目证明我知道或者学到了什么技能?

如果你有编程技能,你应该可以随意从网上刮自己的数据!没有那么难,有很多教程会告诉你具体怎么做。

确保数据的质量——在接触数据之前,寻找丢失数据与总数据的比率,以及数据的总行数。一点点缺失数据没问题,但是大量缺失数据就是问题了。如果您发现一个少于 1000 行的数据集,它可能无法告诉您任何有趣或有用的信息。

我怎么会有问题?

如果你不在乎问题的答案,你就不会完成或做好你的项目。所以挑一个你真正感兴趣的问题吧!

还有——如果花了一段时间才想出一个问题,也不要难过!我认为,与直觉相反的是,提出一个问题通常是整个项目中最困难的部分。

这就是为什么我建议不要使用 Kaggle 作为个人数据项目的资源,除非你是数据科学家工作的铁杆粉丝。Kaggle 通常会给你定义问题,基本上就是说 Kaggle 给你做最难的部分。

幸运的是,因为你刚刚完成了整个本科学位,你可能有一些提出好问题的经验!

想想你最后一次在学校没有得到教授太多指导的情况下想出一个项目,或者根据你在课堂上读到的论文写一篇论文陈述。帮助你构思好的论点和可完成的项目的技巧就是你现在用来提出问题的技巧。

例题

这个项目可以是任何东西,从你曾经有过的一个随机的想法到你已经花了几年思考的东西。有一天,我在想人们在网上抱怨什么。我自学了一些相关的自然语言处理技术,并在一周内完成了一个项目。你可以在这里看到它。

另一个例子:我知道我想做一个与政治有关的项目,因为我在大学主修政治,我想自学如何使用 Tableau。所以我搜索了一个数据集,在 openelections.net 找到了一些很酷的,最终有了这个项目

关于项目的最终想法

  • 不要害怕去做你见过其他人做过的事情。如果它展示了你的技能,并且你用不同于其他人的方式去做,你可以得出和其他人一样的结论。你未来的雇主更关心你如何思考和解决问题,而不是一个独特的结论。
  • 尝试至少有 3 个不同的项目来展示你的技能,最好是不同项目中的不同技能。
  • 如果你在学校做了一个与数据科学相关的项目,而且完全是你自己的想法,你可以随意把它列在简历的项目部分。例如,我的课程推荐器项目是我最初为一门人工智能课做的。

SQL——SQL 有什么问题,我如何才能精通它?

SQL 是一项技能,许多(但不是全部)招聘数据分析师和业务分析师的雇主希望你很好地了解这项技能,并可能在面试过程中测试你。

SQL 是一种逻辑编程语言,这基本上意味着每一个查询都是一个伪英语的逻辑语句,确切地告诉计算机你想要的数据子集。一般来说,你不会迭代任何东西。这不同于许多编程语言,如 Python、Java 和 c。

任何在大学学过离散数学的人都应该知道足够多的集合论来理解和编写 SQL 逻辑。如果你没有学过离散数学或者对集合论不太熟悉,我建议你在开始编写 SQL 查询之前先去上一堂基础集合论的在线课程。这会让你的生活轻松很多。

顺便说一句,除非你真的想,否则你不应该在你的项目中包含任何 SQL。如果潜在雇主真的在乎你懂 SQL,他们会给你一个定时测试。老实说,我有很多考试焦虑症,所以我没有完全通过那些考试。考虑到这一点,本节的各个方面是我对您需要精通 SQL 的最佳猜测,而不是实际操作经验。

那么,我该如何学习并擅长 SQL 呢?

有大量版本的 SQL,它们的语法和关键字都略有不同。MySQL 是最常见的,所以我建议重点学习它。

有很多资源可供学习和掌握 SQL——code academy、W3Schools 等等。我真的不喜欢其中一个,但你可能会喜欢。

为了实践,我发现 LeetCode 真的很有用。LeetCode 主要被那些试图练习软件工程面试的人使用,但是他们有大量的 SQL 问题,这些问题和我在实际测试中看到的非常相似。练习“简单”和“中等”的问题,直到你能在 5-10 分钟内连续正确地回答它们。扔几个“硬”来挑战自己。一定要给自己计时——这可能会让你一开始感觉不好,但你会进步,这会让你对自己的熟练程度有更多的了解。

LeetCode 对有限的问题是免费的,或者每月 35 美元就可以访问所有的问题。坦率地说,如果你真的认真练习,我认为 35 美元是值得的。

如果你不想付费,网上有很多免费资源可以练习。如果你想练习一个真实的版本,尤其是如果你想练习创建和删除表和行,你也可以免费下载 MySQL 到你的计算机上。

这里有一个真实的 SQL 面试问题的例子,你可以看一下。

需要了解的特定关键词/功能:

  • 连接(所有连接,以及如何快速熟练地使用它们)
  • 撤退
  • 分组依据
  • 以...排序
  • 哪里和拥有的区别
  • 划分
  • UNION 和 UNION ALL 的区别

这篇文章这篇文章在这方面也有一些很好的建议。

公平地说,我真的不是告诉你如何擅长 SQL 的最佳人选,但如果我比实际工作晚一个月找到工作,我会这么做。

网站(全球资讯网的主机站)

数据分析师工作需要网站吗?不。有帮助吗?绝对的。

网站优势:

  • 这向潜在雇主展示了你对视觉设计的眼光,以及你能很好地沟通
  • 允许您控制自己在 internet 上的显示方式
  • 在网站上突出和总结项目比在 GitHub 或 Medium 上更容易
  • 如果你有博客的话,我可以主持你的博客

你可以把你的网站想象成你的 LinkedIn 页面的一个更好的版本——我建议从你的 LinkedIn 页面链接到它。与 LinkedIn 页面相比,个人网站的主要优势在于你可以根据自己的喜好对其进行精确定制。

例如,请随时查看我的网站。如果你想找到其他人,只需搜索“数据科学投资组合”或给我发消息,我可以给你发送一些我保存的投资组合网站的链接。

你的网站会给商务人士留下特别深刻的印象。如果你在面试过程中走得足够远,你将不可避免地与商界人士进行面试,所以给他们留下深刻印象很重要。它也给你一个简单的方法让人们检查你所有的工作,而你不必给他们发送多个链接。

然而,要知道,你的招聘经理可能不会把它看作是你技能的任何标志,即使商务人士会这样认为。

我应该在我的网站上放什么?

  • 一个包含你想让任何潜在雇主了解你的第一件事的信息的登陆页面
  • 指向您想要突出显示的所有网站的链接
  • 关于我部分
  • 项目,包括摘要和相关链接
  • 博客(可选)——博客是谈论你的项目的一个很好的方式,所以我推荐一个,在你的个人网站和媒体上交叉发布是完全道德的
  • 简历(可选)

我应该怎么做?

用 WordPress 就行了。没关系,即使你有一个 CS 学位,在 WordPress 中也能成功。拥有一个好看的网站比硬编码的 HTML 更重要。

如果可以,请使用自定义域。理想情况下,是你的名和姓后面跟着“.com”(即我的是 elianagrosof.com)。如果这不可行,你可以发挥创造力,尽可能接近你的真实姓名。

你需要购买域名(通常每年 20 美元),并可能支付某种托管服务,这也应该不贵。

我应该在哪里链接到我的网站?

  • 在你的 LinkedIn 页面上
  • 在你的简历上(同时附上你的 LinkedIn 页面和 GitHub 的链接)
  • 在一些工作申请表上“投资组合”和“其他”的链接位置

— — — —

成为数据/业务分析师的新毕业生指南

第一部分:如何成为商业分析师 2021 年
第二部分:获得数据/商业分析师职位所需的技能和项目
第三部分:如何建立关系网并使用 LinkedIn 找工作
第四部分:如何利用专业团体找商业分析师工作 2021 年
第五部分:如何在求职过程中保持理智

图的跳格神经网络

原文:https://towardsdatascience.com/skip-gram-neural-network-for-graphs-83b8f308bf87?source=collection_archive---------21-----------------------

本文将深入探讨节点嵌入的更多细节。如果您缺乏对节点嵌入的直觉和理解,可以查看以前的这篇文章,其中讨论了节点嵌入的直觉。但是如果你准备好了。我们可以开始了。

布拉登·科拉姆在 Unsplash 拍摄的照片

在关于节点嵌入的一级解释中,我解释了为什么我们需要嵌入,这样我们就有了向量形式的图形数据。而且,我说…

嵌入应该捕获图形拓扑、节点之间的关系和进一步的信息。我们如何理解这一点,以便有一个明确的程序?

在上一篇文章中,我建议…

解决这个问题的一个可能的捷径是尝试形成嵌入,使得两个节点的节点嵌入在某种意义上是相似的,如果它们碰巧在网络中具有某种相似性的话。

好的,为了更正式地建立这一点,让我们回顾一下我们已经谈到的内容。我们说我们有或者想要一些嵌入,但是我们仅有的是来自网络的节点。因此,我们需要一个变换或函数。嵌入环境中的函数称为编码器。

f 为从节点 v 到嵌入 z 的映射编码器,其中 z维嵌入中的向量, v 为图 中的顶点

**

作者通过编码器从节点到嵌入空间的映射

从我的经验来看,这个函数 f 最初是很难掌握的。尤其是当 deepwalk 或 node2vec 的随机行走方法是您听到的第一个嵌入策略时。在这两种使用随机行走的方法中, f 只是查找嵌入矩阵中的一行或一列,因为每个节点 v 由一行或一列表示。

下一个——相似性之谜

在前一篇文章和本文的介绍中,我提到了应该捕捉的相似性。相似性帮助我们建立嵌入,使得嵌入反映真实世界网络的信息。让我们更正式地总结一下。

解码器是从两个节点enc(v)enc(v)的嵌入映射到相似性得分的函数:

解码器

一种可能的解码器是表示余弦距离的点积。

如果两个向量指向相同的方向,则点积为 1;如果两个向量相互垂直,则点积为 0;如果两个向量指向相反的方向,则点积为-1。

然而,其他距离度量/解码器也允许计算相似性值。

当我第一次看到 Jure Leskovec 关于节点嵌入的演讲时,我根本没有意识到这是 node2vec 或 deepwalk 的一部分。据我所知,解码器在这两种方法的算法中都是非常隐含的,但后面会有更多介绍。并且解码器仍然允许评估两个节点表示之间的嵌入空间中的相似性值。

网络中节点的相似性

定义相似性有很多种可能。下面给出了一个可能的选择。

相似性可能意味着两个节点…

  • 是有联系的
  • 共享邻居
  • 具有相似的“结构角色”
  • 在随机漫步中同时出现

让我们多关注一下随机漫步

为什么是随机漫步?

Andrew Gook 在 Unsplash 上拍摄的照片

但是为什么随机漫步和同现有助于描述相似性呢?

这样看,如果一个随机游走者以很高的概率从节点 u 移动到节点 v ,那么 uv 在某种意义上是相似的,即它们属于网络的相同邻域或结构。

为什么随机漫步是个好主意?很有效率。你可以很容易地将一次随机漫步与许多步行者并行。此外,您不需要考虑可能包含数百万个节点的网络的所有可能对,但它很好地描述了邻近性。

但更重要的是..

谷歌研究人员(Mikolov 等人)引入 word2vec 的自然语言处理工作启发了纽约石溪大学计算机科学系的研究小组尝试类似的东西。句子可以被来自随机漫步的节点序列模仿,他们提出了一种叫做 deepwalk 的算法。

节点序列模仿跳格模型所需的句子

也许你仍然有点怀疑为什么这应该工作。deepwalk 的作者们的动机如下:

如果连通图的度分布遵循幂律(无标度),我们观察到顶点在短随机行走中出现的频率也将遵循幂律分布。自然语言中的词频遵循类似的分布,语言建模技术解释了这种分布行为。为了强调这种相似性,我们在图 2 中展示了两种不同的幂律分布。— Perozzi 等人,2014 年,深度行走

Deepwalk,Perrozzi 等人,2014 年

Deep walk 反过来启发了斯坦福大学的研究人员来改进这种算法,他们的结果被称为 node2vec,这是他们在 2014 年发表 deepwalk 两年后发表的。

word2vec 是怎么导致 deepwalk 的?

谷歌的团队基于一种叫做 skip-gram 的模型成功实现了单词嵌入。他们看一篇文章,提取一个单词的上下文,这个单词基本上是句子。并且跳格模型应该基于每个单词来预测该单词的上下文。例如,哪些单词很可能出现在 house 旁边?门、地板、厨房等。

我们建立了这个跳格模型,随后我们看到它如何转化为 deepwalk。

首先我们需要整篇文章中的一个句子。

接下来,应用一个窗口并在文本上移动,以便创建训练样本。在下图中,我选择了大小为二的窗口。这意味着我们感兴趣的单词前面有两个单词,后面有两个单词(突出显示为深蓝色)。作为窗口一部分的单词的邻域是淡蓝色的。一个词的邻域中的词的集合也称为语料库。

自然语言处理的跳格模型的训练样本,作者

现在我们有了训练样本,我们需要一个模型。让我们假设我们训练这一对(“例子”,“傻”)。

我们的输入是一个独热编码向量,其长度等于词汇 V 的长度。值为 1 的条目位于我们对单词“example”进行编码的位置。然后我们将这个向量乘以一个输入权重矩阵。但是基本上,将一个矩阵乘以一个独热编码的向量会导致选择一个列或行向量。矩阵 的这一列或一行 W_{in} 就是我们要找的嵌入向量(用蓝色竖线表示)。

这个向量,我们现在称之为 h 随后乘以第二个权重矩阵 W_{out} 。产品被传递到非线性激活函数中。在这种情况下,它是 softmax,因为我们希望对输出单词的概率分布进行建模。在我们的例子中,输入单词是“example ”,我们计算词汇表中每个单词在“example”语料库中找到它的概率。这一过程受到监督。这意味着我们希望输入“示例”给出“愚蠢”的概率为 1,因为这是我们的数据所建议的。我们在“例”的邻域发现了“傻”。

跳跃图中的表征学习

如果嵌入向量 h 和权重矩阵 W_{out} 不向 softmax 提供导致 1 的输入,我们需要根据它们的贡献来改变这两者。这种比例变化是通过随机梯度下降实现的。

单词“silly”的预测和真实概率= 1 之间的差异导致单词“example”的嵌入向量的变化。使得“例子”更好地预测“愚蠢”。

将这些知识转化为图表..

回到我们的主要兴趣图嵌入,我们可以得出某些类比。我已经指出,纽约的研究人员将一个语料库或一个句子视为随机游走序列或邻域。表格的其余部分补充了其他类比。

行动中的漫步者

这个动画展示了一个随机漫步人如何探索网络。结果,存储了一系列节点。这个序列模仿我们的句子,能够使用跳格模型。

随机漫步机

Deepwalk 中的方法

在 deepwalk 出版物中,他们完全遵循了这一类比,并将 Google 研究人员在单词嵌入领域的工作用于图形嵌入。真好!我喜欢看到不同的团队互相激励。所以,不要羞于看到其他领域的研究人员正在试图解决的问题。也许你从他们那里获得了灵感,从而实现了突破。

让我们一步一步地检查算法。在嵌入的随机初始化和随机选择节点之后。我们将进行一次随机漫步。随后,我们将使用一个窗口在序列上滑动。在我们的例子中,窗口大小是 2。这会生成成对的节点。

下一步,生成的节点对将被传递到 skip-gram 模型中。该图显示了训练一对( v_5v_8 )的示例

按作者列出的网络跳转程序

在我们的例子中,嵌入 h 连同权重矩阵 W_{out} 没有正确地用于预测节点 v_8 在节点 v_5 的邻域中。这就是为什么我们需要用梯度下降来适应它们,以获得更精确的值。

在许多随机行走和梯度下降迭代之后,学习过程将停止。矩阵 W_{in} 包含每个节点 v_i 的向量 h_i ,表示嵌入。这种嵌入可以用于下游的机器学习任务。

摘要

简要回顾前一篇文章,解释为什么我们依靠节点相似性来形成节点嵌入。我们看到了随机漫步是如何提供一个解决方案,用计算效率来表达节点相似性的。这是将自然语言处理中的知识应用于网络的结果。我们修改了 word2vec 的工作方式以及它与 deepwalk 的关系。

[1] Perozzi 等人,deepwalk,2014 年

相关著作

*https://yvesboutellier.medium.com/node-embeddings-for-beginners-554ab1625d98 *

增长黑客:从 BigQuery (SQL)发送松弛警报

原文:https://towardsdatascience.com/slack-alerts-from-a-sql-query-on-bigquery-f626b767304c?source=collection_archive---------8-----------------------

如何根据 SQL 查询的结果发送时差警报?

根据我们数据仓库中的数据发送松弛警报是我最喜欢的增长技巧之一。在过去 2 年多的时间里,它帮助我们的团队检测准备流失的用户,回收被遗弃的购物车,或者检测高质量的用户。而且反应要快!

图片由 apassingstrangerPixabay 上拍摄

首先,我们一直使用 AirFlow 来执行 SQL,并根据结果向 Slack 发送警报。对于每个警报,我们必须创建一个新的 DAG。这对于 2 或 3 个警报来说是没问题的,但是随着我们的成长,这种方法变得混乱了。气流开始被警报 Dag 淹没,我们需要一种更快的方法来设置新的警报。

因此,我们创造了一个更好的系统,我将在本文中分享!😁

作者图片

它只需要一个主表

现在,根据 SQL 查询的结果建立一个新的时差警报只需要主表中的一行。

我们只需要插入要执行的 SQL 代码、CRON 触发器、要发送给 Slack 的格式化消息以及可选的 intro 消息。

作者截图

介绍消息(如果有)将首先发送。将为 SQL 查询产生的每一行发送格式化的消息。

作者图片

那么,让我们来深入研究一下这个系统吧!

体系结构

作者模式

该架构使用经典的生产者/消费者模式。它包括:

  • 云调度器:启动系统。它定期调用我们的生产者,由 CRON 定义。
  • 主表:包含要触发的警报的所有信息。托管在 BigQuery 上。
  • Producer: 查询主表,查找应该触发的警报,并将其推送到发布/订阅主题。
  • 消费者:订阅发布/订阅以了解要执行的警报。执行 SQL 查询,并在必要时向 Slack 发送预警。
  • Pub/sub: 一个分离生产者和消费者的简单话题。

主表

主表包含所有关于预警的信息:
要执行什么 SQL?什么时候执行?把结果送到哪里?等等。

我决定在 BigQuery 上托管主表,但是您可以使用任何其他 SGBD。甚至 Google Sheet 也能工作。

我们的表具有以下结构:

  • 名称:警报的名称。我们将使用它作为一个 ID,这样我们的生产者可以告诉我们的消费者执行哪个警报。*
  • 描述:描述,用于提供信息。
  • cron: CRON 表达式,用于确定给定警报的执行频率。
  • last_execution: 记录一个警报的上次执行时间,这样我们就知道下一次什么时候执行它
  • sql: 要执行的 sql 代码。该 SQL 的结果将包含作为警报发送的数据
  • intro_msg: 可选地,我们可以决定用一条介绍消息来介绍我们的提醒。
  • row_msg: 在 Slack 中显示数据的格式化方式。
  • 通道:我们希望在其中发送警报的空闲通道。

** BigQuery 不支持自动递增的 id 或唯一约束。因此,我们将通过名称来引用警报。确保你没有重复的。如果使用另一个 SGBDR,使用自动递增的整数作为 ID 可能更好。*

要创建主表,可以使用以下指令:

CREATE OR REPLACE TABLE [your_schema].master (
    name STRING,
    description STRING,
    cron STRING,
    last_execution datetime,
    sql STRING,
    intro_msg STRING,
    row_msg STRING,
    channel STRING
);

示例:基于中等帖子发送每日警报

假设您想要在每天早上 7:00 执行 SQL 查询的,并将格式化的结果发送到名为# medium-alerts的通道。

## Number of posts by category published yesterday on medium
SELECT category, count(post_id) as nb_posts
FROM medium
WHERE publishing_date = DATE_ADD(CURRENT_DATE(), INTERVAL -1 DAY)
GROUP BY category

对于这种情况,执行将产生以下结果:

作者截图

根据期望的输出格式,每行:
{nb_posts} where published in {category}

因此,主表需要包含以下记录:

因此,我们的时差警报将如下所示:

作者图片

生产者和消费者

GCP 服务帐户

首先,我们必须创建一个有权限运行我们系统的服务帐户。尽管有多种解决方案来处理这个问题,但让我们简化一下,创建一个包含这个项目所需的所有权限的服务帐户。

进入 GCP IAM (身份和访问管理),导航至您的服务账户。创建一个新的服务帐户,并为其命名。

添加所需的权限。首先,您需要以下内容:

Cloud Functions Invoker
Pub/Sub Publisher
BigQuery Data Viewer
BigQuery Job User

因为您的消费者必须写入主表来更新最后的执行日期,所以您也可以授予BigQuery Data Editor权限。但是如果这样做,您的服务帐户将拥有对 BigQuery 中所有内容的写权限。为了加强安全性,我们可以在您的主表上直接在表级别应用这些权限。

为此,请转到 BigQuery ,打开您的主表,并点击 Share
授予您的服务帐户BigQuery Data Editor角色。

作者截图

发布/订阅

现在,您将不得不创建一个发布/订阅主题来分离您的生产者和您的消费者。只需转到 Pub/Sub,创建一个主题,并给它一个名称。

作者截图

生产者

我们的生产者将运行主表,并根据 CRON 表达式寻找要触发的警报。

作者截图

要设置您的生产者,只需进入 GCP 云功能,并创建一个新功能。

命名它(我已经选择称它为 BigqueryAlertProducer),选择 HTTP 作为你的触发类型,选择要求认证来触发你的函数。

使用之前创建的运行时服务帐户

然后,使用以下代码创建您的函数:

返回一个计数不是必须的,但是它可能有助于调试。不要忘记替换[您的项目]和[您的模式]。

get_alerts_to_execute设置为进入终点。
添加到 requirement.txt 中的以下库:

google-cloud-bigquery
google-cloud-pubsub
croniter

消费者

我们的消费者将订阅 Pub/Sub,并执行通过主题收到的每个警报。为此,它必须从 BigQuery(主表)中读取 SQL 查询,执行它,如果需要,将格式化的结果发送到 Slack。最后,将警报的最后执行日期更新到主表中。

作者截图

再次,创建一个新的云函数。

命名它,选择 Cloud Pub/Sub 作为您的触发器类型,并选择之前创建的主题。

使用与生产者相同的运行时服务帐户

然后,使用以下代码创建您的函数:

不要忘记替换您的 _PROJECT 和 _SCHEMA。

注意,我们必须将 SLACK_BOT_TOKEN 设置为环境变量。在下一步中,我们将创建机器人并获取令牌。然后,您必须编辑您的函数,将令牌添加到您的运行时环境变量中。

trigger_pubsub设置为入口端点。
添加到 requirement.txt 中的以下库:

google-cloud-bigquery
google-cloud-pubsub
croniter
slack_sdk

调度程序

最后,让我们设置一个调度程序,它将每隔 X 分钟触发我们的生产者。

转到云调度程序,并创建一个新作业。给它一个名称和描述。然后,你必须通过一个 CRON 表达式来设置执行频率。比如用*/10 * * * *每 10mn 发射一次。这个网站可能对定义你的 CRON 表达式有很大的帮助。

作者截图

配置执行部分,选择 HTTP 作为您的目标类型,选择 POST 作为您的 HTTP 方法,并复制/粘贴您的生产者的 URL

为了调用生产者云函数,您需要对 HTTP 请求进行身份验证。设置以下标题:

Content-Type: application/octet-stream
User-Agent: Google-Cloud-Scheduler

认证头中,选择Add OIDC token。并选择您之前创建的服务帐户。

作者截图

Slack 应用

这是最后一步!为了在空闲时发送警报,我们需要创建一个应用程序。

进入https://api.slack.com/apps,点击创建新应用
选择从头开始创建,给它一个名字并选择你的工作空间。

在应用程序基本信息页面中,您可以随意个性化您的机器人外观🤖

然后,使用左侧菜单导航至 OAuth &权限。在 Bot Token 作用域中,您需要添加两个作用域:channel:readchat:write

作者截图

向上滚动到您的工作区的 OAuth 令牌部分,并点击安装到工作区。按照流程安装您的应用程序。

作者截图

完成后,回到同一个部分,在这里你可以访问你的应用程序的 Bot 用户 OAuth 令牌。从xoxb-...开始。

这是您需要向您的消费者提供 Slack 访问权的令牌。复制它,编辑您的消费者函数,并将您的令牌添加到运行时环境变量中。

现在,您只需将您的应用程序添加到您想要发送警报的 Slack 频道。

就是这样!🎉🎉

结论

现在,您已经有了一个功能齐全的松弛预警系统,它基于您将在数据库中定义的任何自定义查询。尽管我们在本文中使用了 BigQuery,但是您可以将完全相同的系统用于任何数据库或任何云提供商。将架构翻译成 AWS lambda 和 SQS 并不困难。

最后,正如我之前所说的:基于 SQL 的松弛警报是我最喜欢的基于数据的增长技巧之一。能够检测您的数据或重要用户行为的趋势,并通过专用渠道提供给您的团队,帮助我们防止客户流失,转化更多客户,等等💪

像专家一样分割 NumPy 数组

原文:https://towardsdatascience.com/slicing-numpy-arrays-like-a-ninja-e4910670ceb0?source=collection_archive---------0-----------------------

终极 numpy 数组索引和切片指南

丹尼尔·林肯在 Unsplash 上的照片

介绍

分割 numpy 数组就像削水果一样。你切掉一部分,保留其余部分。numpy 忍者—

读完这篇文章后,你应该能够像切黄油一样切开数组。我将从解释如何从 numpy 数组中选择一个元素开始。然后我会告诉你如何分割一个一维数组。我将建立更多的维度,直到你能够分割任意维数的数组。在文章的最后,我想向您展示混合单一索引和切片减少了切片结果的维数。

选择单个元素

一维数组

一维 numpy 数组的索引从 0 开始。这意味着第一个元素的索引为 0。第二个元素的索引为 1,第三个元素的索引为 2,依此类推。

(图片由作者提供)

比方说,我想打印数字 7(这是第二个元素)。我通过索引方括号中的数组“arr”得到它。

二维数组

为了从二维数组中获取一个元素,我必须提供两个索引。

(图片由作者提供)

第一个索引总是沿着被最少括号包围的轴。

例如,要从这个数组中得到数字 5,必须先用第一个索引,然后用第二个索引来索引这个数组。

(图片由作者提供)

如果考虑按列组织的 2D 数组,可以认为第一个索引选择行,第二个索引选择列。

(图片由作者提供)

三维或多维数组

对于三维或多维数组,必须为每个维度提供一个索引。

(图片由作者提供)

第一个索引总是选择最外面的方括号内的元素。通过这种逻辑,您可以计算出任意数组中索引的顺序。

假设我想从这个数组中获取数字 10。在思想上,从整个数组开始,选择包含你想要的数字的部分,扔掉其余的。

(图片由作者提供)

所以要打印数组中的数字 10,我会使用索引 1,2,0。

(图片由作者提供)

用负指数倒数

您可以提供负索引以在数组中倒计数。最后一个索引的索引是-1,倒数第二个元素的索引是-2,依此类推。

一维的

(图片由作者提供)

二维的

(图片由作者提供)

立体的

(图片由作者提供)

结合正负指标

您总是可以结合正索引和负索引来选择数组中的元素。例如,如果我想打印以下数组中的数字 5,我可以用-2 作为第一个索引,用 2 作为第二个索引。

(图片由作者提供)

选择数组的一部分(=切片)

只选择数组的一部分称为切片

一维数组

(图片由作者提供)

为了分割一维数组,我提供了一个由分号(:)分隔的开始和结束数字。然后,范围开始于结束号前的起始号

(图片由作者提供)

当我想从开始直到索引为 3 的元素获得整个数组时,我可以写:print(arr[0:4])。然而,这与以下情况相同:

(图片由作者提供)

类似地,为了从索引 1 一直到数组的末尾,我可以在不提供切片结束的情况下编写它。

(图片由作者提供)

为了打印完整的数组,我只使用冒号进行索引。

(图片由作者提供)

对于一维数组,以这种方式打印整个数组是没有意义的,而是使用 print(arr)直接打印数组。然而,当你分割一个数组时,它被复制。当您使用没有切片的原始数组时,情况并非如此。对于像打印这样的应用程序来说,这种差异无关紧要,但是对于修改数组的其他情况,这就很重要了。

二维数组

当我对 2D 数组进行切片时,我认为结果是所选行(第一个索引)和列(第二个索引)的交集。

(图片由作者提供)

当我只给 2D 数组的第一个索引时,它会返回一整行。例如,print(arr[1,])将与 print(arr[1])相同。

(图片由作者提供)

三维或多维数组

三维或多维数组的切片遵循与 2D 数组相同的逻辑,但是我无法想象它的行和列。

(图片由作者提供)

我想以下面的方式分割 3D 或更多维的数组。我从最外面的括号开始,根据第一个索引从头到尾进行选择。然后我再深入一层括号,根据第二个索引从头到尾进行选择。

(图片由作者提供)

负索引切片

当使用负索引进行切片时,切片从开始处开始,并在结束之前的一个元素处结束,就像使用正索引一样。例如,如果我想打印(arr[-4:-1])最后一个要打印的元素的索引为-2。

(图片由作者提供)

组合切片和单一索引

请注意,当您同时使用单个索引和切片时,您会损失维度。更具体地说,您在使用单一索引的地方失去了维度。让我们考虑下面的例子:

(图片由作者提供)

第三维度会降低。这是有意义的,因为只有一个元素不需要最内部段周围的括号,numpy 将删除它们。得到的数组将是 2D。

(图片由作者提供)

你总是可以用负指数作为开始,用正指数作为结束。但是,请确保起始索引处的元素必须位于结束索引处的元素的左侧。否则你会得到一个空数组。

摘要

读完这篇文章后,你应该知道的要点应该是。

  • 数组的索引从 0 开始。
  • 如果你想从一个数组中得到一个数字,你必须给出和数组维数一样多的索引。
  • 通过给定由冒号(:)分隔的开始和结束索引来分割数组。你将得到从开始索引到结束索引前一个元素的元素
  • 要从开始切片,您只需不指定开始。要从末端切片,只需不指定末端即可。如果您想要一个维度中的每个元素,您既不需要开始也不需要结束,只需要写一个冒号(😃
  • 对 2D 数组进行切片时,您可以将结果想象为所选行和所选列的交集。

想联系和支持我?

领英
https://www.linkedin.com/in/vincent-m%C3%BCller-6b3542214/
脸书
https://www.facebook.com/profile.php?id=100072095823739
推特
https://twitter.com/Vincent02770108
中等
https://medium.com/@Vincent.Mueller
成为中等会员并支持我(你的部分会员费直接归我)
https://medium.com/@Vincent.Mueller/membership

相关故事

其他故事

使用 Python 的 Matplotlib 绘制坡度图

原文:https://towardsdatascience.com/slope-charts-with-pythons-matplotlib-2c3456c137b8?source=collection_archive---------11-----------------------

如何绘制这个简单的图表来显示变化和层次

斜率图有一种简单明了的格式,可以毫不费力地说明变化和排列变量,它不仅仅是一种过分美化的折线图。

它们已经在数据可视化中赢得了一席之地,可以成为您的报告和仪表板的重要补充。

斜率表—图片由作者提供

在本文中,我们将探索如何用 Matplotlib 绘制它们,了解设计它们的不同方法,以及它们与其他数据可视化的比较。

斜率表—图片由作者提供

入门指南

我们将在下面的例子中使用的数据是来自联合国的人均 GDP。我用过滤器只下载了 2018 年和 2019 年的数据。

import pandas as pd
import matplotlib.pyplot as pltdf = pd.read_csv('../data/UNdata_gdp.csv')
df.head()

让我们从绘制一个国家的单线图开始。

temp = df[df['Country or Area'] == 'Switzerland']
plt.plot(temp.Year, temp.Value)plt.show()

简单折线图—图片由作者提供

我们需要为一系列国家重复这一过程,以绘制其他线条。我们还可以开始准备可视化布局,方法是添加一个具有自定义大小的图形,固定 x 刻度,并增加 x 轴上的空间以添加标签。

线条起点和终点的标签是斜率表的一个显著特征;它们可以显示不同的信息来满足我们可视化的目的。

countries = ['Ireland', 'Norway',
             'Switzerland', 'Cayman Islands',
             'China, Macao Special Administrative Region']fig, ax = plt.subplots(1, figsize=(10,10))for i in countries:
    temp = df[df['Country or Area'] == i]
    plt.plot(temp.Year, temp.Value)

plt.xlim(2017.5,2019.5)
plt.xticks([2018, 2019])plt.show()

矩形中的一些线条——作者图片

酷,它开始看起来更像一个斜率图,但我们还没有到那一步。

让我们添加标签并格式化 y 刻度。

countries = ['Ireland', 'Norway',
             'Switzerland', 'Cayman Islands',
             'China, Macao Special Administrative Region']fig, ax = plt.subplots(1, figsize=(10,10))for i in countries:
    # get a single country from the list
    temp = df[df['Country or Area'] == i]
    # plot the lines
    plt.plot(temp.Year, temp.Value, marker='o', markersize=5)
    # replace large labels
    if i == 'China, Macao Special Administrative Region':
        i = 'Macao (China)'
    # end label
    plt.text(temp.Year.values[0]+0.02, temp.Value.values[0], i)
    # start label
    plt.text(temp.Year.values[1]-0.02, temp.Value.values[1], i, ha='right')

# x limits, x ticks, and y label 
plt.xlim(2017.5,2019.5)
plt.xticks([2018, 2019])# get y ticks, replace 1,000 with k, and draw the ticks
yticks = plt.yticks()[0]
plt.yticks(yticks, ['{}k'.format(i/1000) for i in yticks])plt.show()

斜率表—图片由作者提供

就是这样!我们有一张斜率表。

带有国家/地区名称的标签使得跟踪信息变得非常容易,比传说更容易理解。它还对变化前后的变量进行排序。

定制的

在探索其他应用之前,让我们为斜率表添加更多的细节。

我们将删除书脊,添加网格线,y 轴标签,标题,并改变一些颜色。

countries = ['Ireland', 'Norway',
             'Switzerland', 'Cayman Islands',
             'China, Macao Special Administrative Region']colors = ['#769465', '#69BDE0', '#E06D5E', '#2F7694', '#94E069']fig, ax = plt.subplots(1, figsize=(10,10), facecolor='darkgrey')
ax.set_facecolor('darkgrey')
for i, v in enumerate(countries):
    # get a single country from the list
    temp = df[df['Country or Area'] == v]
    # plot the lines
    plt.plot(temp.Year, temp.Value, 
             color=colors[i], lw=2.5, 
             marker='o', markersize=5)
    # replace large labels
    if v == 'China, Macao Special Administrative Region':
        v = 'Macao (China)'
    # end label
    plt.text(temp.Year.values[0]+0.02, temp.Value.values[0], v)
    # start label
    plt.text(temp.Year.values[1]-0.02, temp.Value.values[1], v, ha='right')

# x limits, x ticks, and y label 
plt.xlim(2017.5,2019.5)
plt.xticks([2018, 2019])
plt.ylabel('USD')
# get y ticks, replace 1,000 with k, and draw the ticks
yticks = plt.yticks()[0]
plt.yticks(yticks[1:-1], ['{}k'.format(i/1000) for i in yticks[1:-1]])# grid
ax.xaxis.grid(color='black', linestyle='solid', which='both', alpha=0.9)
ax.yaxis.grid(color='black', linestyle='dashed', which='both', alpha=0.1)# remove spines
ax.spines['right'].set_visible(False)
ax.spines['left'].set_visible(False)
ax.spines['bottom'].set_visible(False)
ax.spines['top'].set_visible(False)plt.title('GDP Per Capta\n', loc='left', fontsize=20)
plt.show()

斜率表—图片由作者提供

标签通常用于两个主要目的,命名线或显示值。

有时,知道确切的值对我们的 viz 是必不可少的,我们可能需要牺牲名称来显示它们。

countries = ['Ireland', 'Norway',
             'Switzerland', 'Cayman Islands',
             'China, Macao Special Administrative Region']colors = ['#769465', '#69BDE0', '#E06D5E', '#2F7694', '#94E069']fig, ax = plt.subplots(1, figsize=(10,10), facecolor='darkgrey')
ax.set_facecolor('darkgrey')
for i, v in enumerate(countries):
    temp = df[df['Country or Area'] == v]
    plt.plot(temp.Year, temp.Value, color=colors[i], lw=2.5)

    plt.text(temp.Year.values[0]+0.02, 
             temp.Value.values[0], 
             '{:,.2f}'.format(temp.Value.values[0]))

    plt.text(temp.Year.values[1]-0.02, 
             temp.Value.values[1], 
             '{:,.2f}'.format(temp.Value.values[1]), va='center', ha='right')plt.xlim(2017.5,2019.5)
plt.xticks([2018, 2019])
yticks = plt.yticks()[0]
plt.yticks([])# grid
ax.xaxis.grid(color='black', linestyle='solid', which='both', alpha=0.9)# remove spines
ax.spines['right'].set_visible(False)
ax.spines['left'].set_visible(False)
ax.spines['bottom'].set_visible(False)
ax.spines['top'].set_visible(False)plt.title('GDP Per Capta\n', loc='left', fontsize=20)for i, v in enumerate(countries):
    if v == 'China, Macao Special Administrative Region':
        countries[i] = 'Macao (China)'plt.legend(countries, loc='upper left', frameon=False)
plt.show()

斜率表—图片由作者提供

仍然很容易理解每行代表什么,但是当我们使用图例而不是标签时,排名就不那么明确了。

在某些情况下,我们也许能够鱼和熊掌兼得,但这取决于数据。也就是说——如果这些行的间距足够大,你就可以把名字写在它们的上面或下面。

colors = ['#87B38D', '#477998', '#291F1E', '#BC9CB0', '#A3333D']countries = ['Germany', 'Canada',
             'Spain', 'Italy', 'France']fig, ax = plt.subplots(1, figsize=(10,10))for i, v in enumerate(countries):
    temp = df[df['Country or Area'] == v]
    plt.plot(temp.Year, temp.Value, color=colors[i], lw=2.5)

    plt.text(temp.Year.values[0]+0.02, 
             temp.Value.values[0], 
             '{:,.2f}'.format(temp.Value.values[0]))

    plt.text(temp.Year.values[1]-0.02, 
             temp.Value.values[1], 
             '{:,.2f}'.format(temp.Value.values[1]), 
             va='center', ha='right')

    correction = 0
    if v == 'Canada': correction = 500
    plt.text(2018.5, 
             temp.Value.values[1] - correction, 
             v, color=colors[i],
             va='center', ha='center', fontsize=14)plt.xlim(2017.5,2019.5)
plt.xticks([2018, 2019])
plt.yticks([])# grid
ax.xaxis.grid(color='black', linestyle='solid', 
              which='both', alpha=0.9)# remove spines
ax.spines['right'].set_visible(False)
ax.spines['left'].set_visible(False)
ax.spines['bottom'].set_visible(False)
ax.spines['top'].set_visible(False)plt.title('GDP Per Capta\n', loc='left', fontsize=20)plt.show()

斜率表—图片由作者提供

斜率表通常很简单,有几种不同的方法,它们可能需要一些调整来适应你的解决方案,但总的来说,你可以用很少的元素得到一个有意义的图表。

斜率表—图片由作者提供

其他方法

毫无疑问,您可以将变化可视化,并使用其他数据可视化显示相同的信息。

条形图—作者提供的图片

箭头图—作者图片

使用条形图和箭头图,您可以按初始值、终值或变化量/百分比对数据进行排序。你需要多个图表来显示前后的排名,但这是不一样的。

没有什么能像斜率表那样自然地显示排名。

即使变量的顺序对您的可视化并不重要,但斜率表在传达变化和为您的报告和仪表板增加多样性方面做得很好。

您可以在这里找到本文中使用的所有可视化代码。

感谢阅读我的文章。我希望你喜欢它。

更多 Python DataViz 教程

Spark 中渐变维度类型 2

原文:https://towardsdatascience.com/slowly-changing-dimension-type-2-in-spark-7d5865ac915b?source=collection_archive---------7-----------------------

资料照片由尤金·日夫奇克Unsplash 拍摄

理解大数据

介绍什么是渐变维度类型 2 以及如何用 Apache Spark 创建它

介绍

如果这不是你第一次阅读我的帖子,你知道在我进入大数据世界之前,我是一名传统的 DWH/BI 开发人员。使用数据湖,我们的工作方式与我以前不同。所以过了一段时间后,我决定回到“我的根源”,写了一篇数据仓库指南的博文。现在我将再次回到这个话题,解释渐变维度(SCD ),尤其是关于 Type 2,并提供一些关于如何在 Apache Spark 中实现它的代码,以及与关系数据库相比的一些关键差异。

什么是 SCD?

如果您是数据仓库领域的新手,它可能听起来像某种疾病的缩写,但实际上,它是一个能够跟踪事物如何随时间变化的表。

SCD 有多种类型。即:

  • 那些只保留原始数据的—不可变的
  • 那些只保留当前值的
  • 具有当前值和先前值等。

如果你想了解更多,金博尔集团有一个关于不同类型 SCD 的很好的设计技巧。尽管就我所知,这取决于你使用的是什么资源,类型可能有不同的含义:在一个上下文中,它在另一个上下文中意味着一件事——方式不同的方法。

那么我说的 2 型是什么呢?

想象一下我们有产品维度。我通过 dbdiagram.io 工具创建了所有表格插图!

产品维度。作者图片

我们可能想看看产品的名称或价格如何影响我们的销售。当然,你可能会认为我在胡说八道,因为产品价格对销售有着巨大的影响,而产品名称可能与此毫无关系。但与我裸露;这只是一个展示 SCD 类型 2 如何工作的例子。

因此,想象一下,如果我们每天都在构建我们的维度,并且我们注意到一些信息发生了变化,但是因为我们只是加载快照值,所以我们看不到变化的内容和原因。因此,在这种情况下,当该行有效时,我们添加生效日期。

具有生效日期的产品维度。作者图片

好了,现在发生的情况是,我们的表中没有主键,因为我们现在将有多个 product_id。为了解决这个问题,我们可以创建一个代理键,通过 join on 键和事实发生的日期,我们可以轻松地将它添加到事实表中。

具有代理键的产品维度。作者图片

但是如果我们的一个产品因为某种原因被删除了会怎么样呢?是的,我们应该通过添加一个标志 is_deleted 来确定它是否被删除。

但是等等,现在要获得当前值以进行快速分析,我们必须在 end_date 扫描所有表(如果它是 null 或者设置为 2999–12–31)?是的,这听起来也不太有效,所以让我们添加另一个标志,是有效的。

因此,我们的终端 SCD 类型 2 应该看起来像:

最终 SCD。作者图片

Apache Spark 中的实现

你可能会想,我们在 RDBMS 中有 MERGE 命令,我们可以很容易地做到这一点。是的,您可以,但是您最有可能操作的是镶木地板(或任何其他类型的文件)。或者,如果你足够幸运,你的公司有像 Apache 胡迪/Delta/Apache Iceberg这样的表格格式,这样你就可以在那里利用类似的特性。

但是让我们假设您没有任何其他选择,只能用最简单的步骤创建 SCD。

声明我的代码与我之前解释的不同之处:

  1. 在我的代码中,我没有使用 is_valid 标志;我将传递 end _ date = 2999–12–31,它将标识当前有效的行。
  2. 此外,我还添加了 open_reason 和 close_reason 来标识一行被打开和关闭的原因。
  3. 我也不会为一个表添加一个代理键来进行 PK

先决条件

如果一行发生了变化,我们需要的第一件事是更容易识别——get _ values _ hash 方法。我更喜欢 MD5,在使用 MS SQL stack 构建数据保险库模型时,我广泛使用了 MD5,但这次我有点懒,没有找到它,而是使用了一个简单的散列。

哈希值方法

我们还需要确保当前快照中的列在 SCD 中没有相同的名称,以避免重复的列错误:

列重命名方法

我们还需要一个分割器来识别哪些行应该被检查更改——只检查打开的行。我们根本不应该碰历史记录。

从现有 SCD 打开和关闭行拆分器。

新行

所以新行非常简单;我们添加 SCD 列,如 is_valid、start_date、close_date、open_reason、close_reason。我还添加了一些特定的处理程序。如果某些数据到达较晚或有特定的列,我们应该使用它作为开放日期,而不是当前日期。

删除的行

我们从现有的 SCD 中取出打开的行,并只在删除的行上创建它们的副本。关闭 SCD 中已打开的文件,并向新打开的文件添加相关数据(is_deleted flag = 1,end _ date = 2999–12–31 ),并将其合并回一个数据帧。

处理删除的行

已更新或未更改的行

所以这个有点复杂,主要是因为这个,我们有那些先决条件的方法。

首先,我们识别没有改变的记录,并把它们放在一边;他们会像以前一样。

第二,我们通过比较记录值的散列来处理更改的记录。为了避免两次读取相同的源,我使用了一个作弊方法,结果证明在较小的表中效率非常高(我还没有在较大的表上试过)=我添加了一个包含两个值(0,1)的数组列,稍后我将展开它,有条件地处理这两种情况。因此,在旧行上添加日期更改,打开包含所有相关元数据的新行,并联合所有三个数据框(无更改、来自 SCD 的更新更改、来自快照的新值)。

所以整个过程看起来是这样的:

摘要

在本文中,您已经简要介绍了 SCD type 2,并且知道如果您的表存储在 parquet 文件中(不使用任何表格式),如何使用 Apache Spark 创建它。

值得一提的是,代码并非完美无瑕。

  • 添加代理键以便在事实数据表中使用
  • 我假设删除该行后,它将不再出现,因此在这种情况下,代码将不会以正确的方式工作。
  • 添加 is_valid 标志,不处理与 bool/int 相比过滤效率不高的日期。
  • 添加一些缓存/广播/洗牌分区选项,用一些参数提高性能。

你可以在我的 GitHub 中找到我开发时使用的一些测试的完整代码。

智能手机位置数据代表谁?

原文:https://towardsdatascience.com/small-districts-big-data-who-does-geo-referenced-mobility-data-represent-78212ca004f6?source=collection_archive---------58-----------------------

实践教程

脸书数据的快速验证

这项工作已经完全使用公开数据完成,并与 Kai Kaiser 合著。所有错误和遗漏都是作者的。

在危机情况下,政府和国际救济组织传统上依赖行政报告和调查数据。这些数据被用来为自然灾害或公共卫生危机等冲击的出现、应对和恢复提供信息。然而,尽管可以仔细收集传统数据并专门用于衡量灾害的各种社会经济层面,但它面临着局限性。它可能不常见,实施成本高,易受滞后影响,并且在更高的空间粒度级别上可能不具有代表性。此外,许多发展中国家的系统根本没有能力在面对意料之外的发展时衡量和迅速提供这些数据。

智能手机设备的激增导致了数据收集模式的转变

包含 GPS 技术的设备使得以前所未有的规模和频率测量人类运动成为可能。越来越多的科技公司,如脸书谷歌苹果,以及应用集成或地图提供商,如 UnacastCuebiqMapbox ,已经将这些数据资产提供给人道主义社区,作为“数据为善”运动的一部分——由新冠肺炎加速。虽然不是专门为风险管理而构建的,但从位置数据中获得的数据集具有很高的空间精度,并且经常更新(通常是每天或甚至每 8 小时更新一次)。此外,它们往往由同一提供者同时为几个国家提供,这种方法和衡量的一致性在传统数据中并不常见。

对于依赖传统流动性指标的决策者来说,这可能会改变游戏规则。例如,一些数据集可以在网格块或像素级别获得,通常尺寸约为 1 公里 x 1 公里,并且可以根据感兴趣的区域任意向上聚合。这使得能够将精细的移动数据映射到越来越小的感兴趣的镜头,例如越南的选区/社区、巴基斯坦的工会理事会或美国的人口普查区。

成败的问题是:这些数据代表谁?

如果携带智能手机或基于 GPS 的设备被要求包含在这些数据中,不可避免地,这些数据将倾向于支付能力。此外,在低收入国家,如果只有一名家庭成员拥有智能手机(相比之下,美国有三分之一的家庭拥有三部或更多智能手机),数据也可能倾向于“养家糊口的人”,因此也存在性别差异。对基于全球定位系统的数据的代表性如何进行频繁、可访问的分析仍然至关重要,特别是如果要将其应用于风险缓解政策。

全球可用的地理空间层(人口统计、性别和建筑足迹)的可用性使得有可能快速量化代表性,并对这些数据进行比较性国家验证。

深入了解脸书移动数据

鉴于移动性数据在公共政策中的大量可能应用,我们采用了一种快速且可复制的评估方法,为不同地方背景下的移动性数据设定基准。下面应用这种方法来了解脸书痕迹在当地居民以及流动人口(如游客或流离失所者)中的代表性。我们选择脸书数据是因为它有一个庞大的用户群体,已经在用它来回答政策问题。

讨论了脸书为移动性分析提供的两个主要产品。首先,移动范围图测量行政单位级别的移动性。该数据集可证明有助于快速评估灾害影响。其次,脸书灾难地图提供了脸书用户的数量,而不是行政边界级别,而是 Bing 瓦片,大小从 600 米 x600 米开始。这些磁贴可以根据感兴趣的区域任意向上聚合,例如在移动范围地图的情况下,移动范围地图是根据脸书用户在给定时间段内访问的 Bing 磁贴数量构建的聚合指标

图一。一个散点图,展示了脸书和谷歌在越南(R 平方= 0.77)、印尼(R 平方= 0.60)和菲律宾(R 平方= 0.79)的数据之间的密切关系。作者准备的图表。

使用运动范围图,可以进行快速验证步骤。我们发现停留指标,或“脸书用户全天停留在一个磁贴上的比率”,与谷歌社区移动地图提供的居民访问数据高度相关。图 1 展示了三个太平洋国家的一致性:越南、印度尼西亚和菲律宾。

就全球覆盖范围而言,谷歌和脸书的移动数据在大多数国家往往是相互关联的,如图 2 所示。这表明了科技公司提供的数据集在表现上的一致性,并激发了对这些数据集如何串联使用的进一步研究。

虽然这些数据不一定包括没有携带智能手机的人,但它确实表明了一个有希望的结果:在各个平台上,两个主要移动数据集——脸书移动范围地图和谷歌社区移动报告——的空间分布高度相关。这是利用这些数据集为人道主义工作建立信心的重要一步。

图二。当全天停留在一个单幅图块中的用户数量(脸书移动范围地图指标)与访问住宅位置的百分比变化(由 Google COVID 社区移动地图提供)进行比较时,我们发现在大多数国家中存在中度到非常强的相关性。图片作者。

感兴趣的用户如何访问这些数据?

运动范围地图是一个开放数据集,在地区级别可用。随着越来越多的移动产品本着数据为善的精神提供给人道主义界,开展快速验证工作以使这些数据可操作将成为基础。

实现负责任的人工智能的一小步

原文:https://towardsdatascience.com/small-steps-to-actually-achieving-responsible-ai-69f998f9eefb?source=collection_archive---------29-----------------------

克里斯蒂安·朱德雷在 Unsplash 上拍摄的照片

负责任的人工智能似乎很难实现。在那一点上我支持你。它带来了如此多的挑战,以至于在试图完成任何事情的时候都很容易迷失方向并感到沮丧。但是,正如他们所说,千里之行始于第一步,我相信我们可以采取一些小步骤,以现实的方式实际实现负责任的人工智能。

这一战略的关键是强调从部分解决方案开始,这些解决方案一开始可能并不完美,但提供了必要的肥沃土壤,有助于克服这一领域的实践者经常遇到的惰性,这是由于将原则付诸实践涉及大量变量(包括技术和组织变量)。

让我们带着部分解决方案来到谈判桌前

如果我们要实现一个更健康、负责任的人工智能生态系统,那么在道德、安全和包容性问题上的问题识别是必不可少的。AI Now、Data & Society、Partnership on AI、Markkula Centre for Applied Ethics、Algorithm Watch 等组织在该领域做了大量开创性的工作。

事实上,正如我在 2021 年初的一篇早期文章中敦促的那样,建立人工智能伦理方面的公民能力,这是蒙特利尔人工智能伦理研究所工作的核心原则之一,将使更多人能够有效地为问题识别阶段做出贡献。

然而,我们需要认识到,问题识别只能让我们到此为止。

这是因为我们意识到,我们可以付出很多努力来确保我们能够尽可能多地识别我们试图解决的问题的方方面面,但其中有一些隐藏的层面,只有当我们开始应用一些解决方案来解决这些问题时,它们才会浮出水面。这是因为这些是社会技术问题。它们在性质上相当复杂,并且在问题的人和技术因素之间有相互作用。此外,一旦我们开始应用解决方案或缓解策略,我们就会看到这些方法是如何与问题的原因相互作用并重塑问题的,这构成了在没有实际付诸实践和反复实践的情况下无法收集的见解。

牢记这一点,我相信部分解决方案可以提供一个具体的前进方向,因为它具有创建、实施的障碍更低,以及在整合不同利益相关方反馈的能力方面的灵活性。

不是说我们没有在一定程度上做到这一点,我们做到了!但是,考虑到我们做好事的道德义务和意图,我们经常发现自己在试图拿出完美的解决方案,然后再尝试将某个东西放到世界上进行测试和迭代。

为什么是部分解决方案?

让我们花点时间来看一下为什么这种部分解决方案是一种很好的方式,可以在今天就开始解决问题,而不是将问题推迟到未来不确定的时间。

  1. 它们创建起来更快
  2. 它们更加灵活
  3. 他们仍然有邀请其他人做出贡献的空间
  4. 人们更愿意倾听我们的提议

1.它们创建起来更快

我相信你听说过这句谚语,完美是完成任何事情的敌人。当我们谈论人工智能伦理并提出可以帮助我们解决该领域一些挑战的解决方案时,情况似乎确实如此。

部分解决方案为我们提供了一个很好的折衷方案:它们必须承认它们是正在进行的工作,因此不可能是完美的,这为我们提供了一个机会,让我们可以快速地将它们与其他利益相关者共享。

想想看,当你和你的同事不得不选择一个地方吃午餐时,我们可以继续兜圈子,或者你可以建议去麦当劳,然后突然每个人都开始围绕几个选择,以免他们最终都吃了非常不健康的食物。

速度的另一个好处是,考虑到它们(潜在的)前期投资低,你可以更容易地从你的团队那里获得支持,在第一时间进行试验。此外,因为他们没有花太多的精力去创造,你也更开放(希望如此!)在人们对解决方案提供反馈的同时做出改变,而不要太专注于一个方向(这本身就表现为沉没成本谬误)。

最后,正如数据科学中的笔记本电脑如何让我们快速试验代码,而不是总是必须运行整个管道,在创建解决方案、将它们发布到世界上以及寻求利益相关者的反馈以迭代解决方案方面的快速试验可以作为一个伟大的过程,使你能够更有意义地解决构建道德、安全和包容性人工智能系统中的问题。

2.它们更加灵活

你在某件事情上投入得越多,它就变得越(潜在地)僵化,因为可能的选择范围变得越小。我们还开始限制解决方案可以采用的不同方向的数量,这在路径决定论的思想中有所体现。虽然果断是好的,尤其是当你不断努力让别人考虑一些事情时,硬币的另一面是具体化某些设计选择,这些选择使解决方案永远远离某些可能性。正如我们将在下一节中看到的,如果解决方案要取得成功,这使得整合其他人的想法变得特别困难,而您也需要这些人的认同。

尤其是在尝试使用多学科方法来解决问题时,灵活性是关键,因为每个领域都有自己解决问题的意识形态和方法,如果我们过于固执己见,可能会使解决方案难以适应来自不同背景的其他人的反馈。

此外,如果您在实现解决方案时遇到了意想不到的问题,部分解决方案允许您根据收集的反馈快速迭代,从而使您变得更加有效。

3.他们仍然有邀请其他人做出贡献的空间

如果你从这篇文章中吸取了什么,那就是:部分解决方案的重要性在于它们留下了足够的空间(或者应该留下足够的空间!)这样其他人在分享他们的想法时会感到受欢迎和舒适,并知道这些想法有被采纳的空间。

当组织内部的激励不一致时,不同的利益相关者可能会有不同的目标。当很多人在一个解决方案的不同方面工作时,这就是事实。当涉及到负责任的人工智能时,承认这一现实并做出调整,允许包含来自其他可能以不同方式处理问题的人的想法,对于解决问题的社会技术性质是至关重要的。

多方利益相关者的协商和参与要求这种方法取得成功。不要仅仅认为这是你为了能够吸收他人的想法而必须做出的一次性妥协。把这想象成一个重复的互动场景,你会带着不同的问题和不同的想法回到谈判桌前,也许是同一个小组,当别人看到你过去是如何通融的时候,你如何创造空间的记忆也会帮助你。

4.人们更愿意倾听我们的提议

这是一个非常重要的考虑,当涉及到真正开放和困难的问题,如解决人工智能伦理。

带着一个部分的解决方案来到谈判桌前也是对你的一种谦卑的承认,你知道这个解决方案是不完整的,你重视并征求他人的反馈,这样你就可以一起构建一些更有意义的东西,最终有助于更全面地解决问题。

尽管一些关注点可能更倾向于技术元素,但最终,我们拥有与您正在努力创建的解决方案的每个方面都不可分割地联系在一起的人,这意味着获得对人类如何以合作的方式协商和达成解决方案的直观(或其他)理解。我相信你已经听过很多次关于将人工智能开发的讨论重新架构为一种合作努力而不是“军备竞赛”的论点,这种动态也适用于此!我们试图提出解决方案,帮助我们解决我们面临的问题,而不是相互竞争,为提出“正确”的解决方案插上胜利的旗帜。

事实上,如果你遇到这种动态,这可能是一个信号,表明需要首先进行更大的对话,将讨论框定在合作的动态中,以便你可以更好地实现你和他们的目标。

我被卖了,但是这不需要其他人也走几步吗?

百分之百!如果没有其他人愿意让步,我们肯定无法做出改变!我的行动呼吁是,如果我们带着植根于实用主义的解决方案来到谈判桌前,我们就有更好的机会真正实现变革,而不是停滞不前。绝对主义只能带领我们到此为止,我们需要愿意采取渐进的步骤,以期实现我们的目标。

我希望有更好的消息,能够一下子做出大胆的改变,但有些改变是从小步开始的,有可能体现为我们最终希望在世界上诞生的突破性变化。通过蒙特利尔人工智能伦理研究所的一些教育工作,我们已经能够让社区了解他们的背景和经验,并帮助他们的社区找到改善我们生态系统状态的解决方案。

你可以在 蒙特利尔人工智能伦理研究所**人工智能伦理简介 中阅读更多来自团队的想法和见解。**

****你可以在https://ATG-abhishek . github . io了解更多我的作品

迈向更直观、更美观的图形的一小步

原文:https://towardsdatascience.com/small-steps-towards-more-intuitive-and-aesthetic-graphics-723e658ecd0?source=collection_archive---------60-----------------------

微小的图形修改和可视化决策如何能够产生更清晰、更具信息性的图形

作者图片

数据可视化领域始于 18 世纪晚期 William Playfair 对基本图形设计的开发,并且已经发展到每年发布数万亿统计图形图像的程度。在当前的信息时代,随着数据集变得越来越大、越来越复杂,良好的数据可视化技能变得越来越重要。

不考虑目标用途,最有效的设计数据图形是传达预期信息的最简单的图形。因此,将数据可视化视为一门科学和一门艺术是很重要的,因为有效的量化图形必须简单而准确地传达数据,同时具有美感。

有很多关于如何对图形进行一般改进的教程,比如为了清晰起见而处理字体大小、标记大小、标签、图例和颜色。今天可用的许多编程语言和可视化软件对于生成定量图形来说都是极好的,但是这些工具可以通过根据被可视化的数据实现简单的改变来更好地使用。没有多少人会考虑绘制的数据类型如何影响可视化决策,从而为读者提供更清晰、更丰富的图形。

让图表更直观

有几种不同的方法可以改善图形的外观。可视化决策,如选择线的颜色,可能看起来不太重要,但在时间-温度图中,将较高的温度改为红线,将较低的温度改为蓝线,可能会使读者更容易理解该图。

以下是四个不同数据集上的四个好的和差的可视化例子,这些数据集的维度不断增加。下表总结了用作示例的数字和变量的类型。

注意,具有更高维度的数据集更复杂,并且通常由更多绘图参数组成,用于可视化附加变量。

一维图形

各大洲的国内生产总值数据被用来生成 1D 案例的柱状图。下图比较了左边差的图形和右边好的图形。

各大洲的国内生产总值数据绘制在左侧的穷人图和右侧的风格图中。作者图片

为改善量化图的外观所做的具体修改包括按升序对数据进行排序,对每个洲进行颜色编码,并在每个条形的末尾添加数据标签。

二维图形

2D 图是一个合成的时间-温度线图,显示了一个城市全年的月温度。下图比较了左边差的图形和右边好的图形。

北美某城市的合成温度数据绘制在左侧的劣质图和右侧的样式图中。作者图片

为改善定量图形的外观而进行的修改包括更改线条颜色以使其更加直观(红色表示高点,蓝色表示低点),移动轴和图例以减少空白,将月份数字更改为月份名称,以及为每个点添加数据标签。

三维图形

3D 图是一个合成散点图,显示了健身房会员的身高、体重和体脂百分比。下图比较了左边差的图形和右边好的图形。

合成的身高、体重和体脂百分比数据绘制在左侧的穷人图和右侧的风格图中。作者图片

为改善定量图形的外观所做的修改包括将线条改为灰度背景,以更好地突出代表区域,移动轴以减少空白,将体重和身高的单位从 kg 和 cm 改为 lbs 和 ft,使用增加了颜色范围的颜色条,以及设置颜色条值的限制。

四维图形

4D 图是一个散点图,显示了大约 54,000 颗钻石的价格、重量、颜色和净度。下图比较了左边差的图形和右边好的图形。

钻石的重量、价格、颜色和净度数据绘制在左侧的穷人图和右侧的风格图中。作者图片

为改善定量图形的外观而进行的修改包括:缩放数据以更好地显示具有不同属性的钻石,移动轴以减少空白,将轴更改为对数刻度,因为数据更呈对数正态分布,在轴上添加均匀间隔的刻度,根据透明度对数据进行分类,以使较大的点绘制在较小的点下面, 用黑边包围每个点以增加与其他数据点的视觉对比,添加额外的注释以帮助读者识别哪个钻石颜色和净度值更好,以及使用具有更大颜色范围的颜色条。

摘要

这里介绍的四个示例比较了使用默认设置生成的图形和改进外观并创建更清晰、信息量更大的图的图形样式。虽然具体的改进取决于所绘制的数据类型,但是可以进行一些一般性的更改来改善图形的外观,例如:

  • 使用更多人更容易理解的单位
  • 不同的字体和标记大小
  • 整理数据
  • 更改默认标签
  • 轴限制,测井轴,颜色限制
  • 对适当的变量使用直观的颜色

尽管可以采取更多的措施来改善图形的外观,但这些措施通常是针对被可视化数据的维度和性质的。花些时间思考可视化的数据总是值得的,因为可以采取几个步骤来修改图形,以生成更清晰、信息更丰富的图。

R 中丢失数据的智能处理

原文:https://towardsdatascience.com/smart-handling-of-missing-data-in-r-6425f8a559f2?source=collection_archive---------3-----------------------

实践教程

缺失数据无处不在——学习如何总结、可视化和估算它们,同时关注统计不确定性。

罗斯·斯奈登Unsplash 上拍摄的照片

当值应该被报告但不可用时,我们最终得到缺失的值。在现实生活的数据中,缺失值几乎是自动出现的——就像没有人能够真正摆脱的阴影。想想调查中的无回应、数据收集过程中的技术问题或连接不同来源的数据——令人恼火的是,我们只有完整案例的数据相当稀缺。但是你为什么要关心它呢?简单地忽略丢失的数据会有什么问题呢?

本文将向您展示为什么缺失数据需要特殊处理,以及为什么这是值得的。

  • 了解可视化技术,以检测缺失数据中的有趣模式。
  • 了解为什么均值插补或列表删除不一定总是最佳选择。
  • 通过 r 中的链式方程(小鼠)执行多重插补。
  • 评估插补质量,以说明统计不确定性,并使您的分析更加稳健。

缺失数据的问题

一些分析(如线性回归)需要完整的观测值,但在普通的统计软件中,当向系统输入包含缺失值的数据时,你不会得到错误。相反,不完整的案例会被自动删除,这通常是悄悄发生的。但是,如果您计划在同一个数据集上测试不同的模型,它们之间的统计比较将是不合适的,因为您不能保证模型是基于相同的观察。此外,通过完全丢弃观察值,我们不仅会失去统计能力,甚至可能会得到有偏差的结果——丢弃的观察值可能会提供有关感兴趣问题的关键信息,因此简单地忽略它们将是一种遗憾。

例如,假设您被咨询来评估组织 z 中的心理工作条件。为此,您在开始采访利益相关者之前创建了一个员工调查。因为沮丧的员工通常会跳过不愉快但至关重要的问题,丢失数据几乎是不可避免的。因为这些价值观肯定会影响员工的整体满意度,所以我们应该关注它们。在这种情况下,数据不是随机缺失的,或者至少不是完全随机缺失的,因为缺失取决于员工满意度本身。有点太复杂了?好吧,让我们慢慢来:哪种类型的缺失数据存在,它如何影响数据分析?

  • 非随机缺失(MNAR): 数据集中缺失值的位置取决于缺失值本身。在我们的例子中,员工倾向于报告积极或中立的回答,并在不同意的情况下跳过每个问题。例如,个人可能会系统地将以下项目留空,因为他们对给领导留下差评感到不舒服:“我会毫不犹豫地向我的领导报告不愉快的问题。”尽管如此,删除这些观察结果会将结果扭曲为积极的,因为它会让人觉得每个人都很好,组织不需要任何干预。在这种情况下,缺失的存在是有趣的,因为它告诉我们一些正在研究的现象——我们绝对不应该忽视它们。
  • 随机缺失(MAR): 数据集中缺失值的位置取决于其他一些观察到的数据。在员工调查的另一种情况下,一些受访者并不完全确定,可能觉得无法用数字来准确回答他们的想法和行为。最终,他们很少回答被问到的问题。因此,反应多少取决于另一个未被观察到的机制:在这种情况下,个人需要更精确的自我报告。删除这些回答将是一个遗憾,因为它们可以从以前的回答中预测出来,甚至可能引入偏差:事实上,数据集将只包括自发回答这些问题的人的回答,我们不知道这将如何影响整体结果。因此,不建议在分析中忽略缺失的数据。
  • 完全随机缺失(MCAR): 数据集中缺失值的位置完全是随机的,它们不依赖于任何其他数据。在第三种情况下,当调查平台上突然出现技术问题时,所有员工同时填写调查。因此,每个人都需要提前停止,因此在调查快结束时,所有参与者都会出现缺失值。数据的成对删除会使你难以分析数据集中最后的变量,但至少它可能不会给结果带来任何特定的偏差。根据经验,当数据包含不依赖于观察值或未观察值的少于 5%的随机缺失时,完全案例分析也可能是一种可接受的方法(Graham,2009)。

您不知道数据集中的值是否随机丢失?如果数据是随机丢失的,有一些统计测试,但是考虑到你需要一些关于丢失值的假设,以及你期望它们在哪里,无止境的测试似乎有点麻烦。更有意义的做法可能是直观地探索数据,并注意潜在的与方法相关的偏差,以防你马上没有强有力的想法。如果你对现实生活中的缺失数据问题感兴趣,我强烈推荐科勒、波尔和卡斯滕森(2017) 的一篇论文:作者展示了在大规模教育学生评估中对无应答的不同处理如何影响能力分数等重要结果。

用 naniar 探索缺失的数据——获得鸟瞰图

我们将使用的数据是来自美国国家健康和营养检查研究的调查数据——它包含 10000 个健康相关结果的观察数据,这些数据是在 20 世纪 60 年代早期收集的,同时还有一些人口统计变量(年龄、收入等)。).通过安装 Randall Pruim (2016)的 NHANES 包,可在 R 中使用。

首先,我们加载数据集,并通过从原始指数中随机采样将样本大小减少到 500 个观察值——您可能会使用较小的数据集,我们将使绘制变得稍微容易一些。我假设您的计算机上已经安装了 dplyr。

现在,我们将通过 Nicholas Tierney 和他的同事们(2020 年)制作的非常简洁的 naniar 包来粗略地了解一下失踪的情况…

结果表明,数据集中确实存在缺失数据,约占 18%的值(n = 1165)。除了“年龄”变量,每个变量都有大量的缺失值。请注意,由于我们抽取的是随机样本,所以每次运行脚本时,结果可能会有所不同。

通过查看每个变量的缺失摘要,我们注意到,在数据集中的所有变量中,尤其是“PhysActiveDays”变量的缺失量最高。该变量代表“参与者在典型的一周中进行中度或剧烈活动的天数。”众所周知,体育活动会产生如此大的影响,这一变量似乎是一个有趣的生活方式变量,可能是各种健康结果的潜在预测因素。请记住,值得注意的是,该数据集中缺失值的数量超过了记录值的数量。

我们可以将这种总结转换成如下的可视化表示:

为了更好地理解数据是否是随机缺失的,我们将可视化所有变量中缺失值的位置。

我们可以看到缺失值“聚集”在哪里,这似乎与我们之前对每个变量缺失值的概述相匹配。情节的左边部分特别有趣:一些参与者没有回答一排与心理健康相关的问题(例如“LitteInterest”和“Depression”)——这些问题可能已经超出了他们个人的舒适区(但这只是我的一个假设)。

现在,缺失值的出现与其他变量的缺失有关吗?让我们找出答案。

该结果与观察结果一致,即在大量情况下,某些遗漏碰巧发生在某些变量上(例如,遗漏工作、教育、兴趣和抑郁信息,以及缺少体育活动日的记录)。因此,在我看来,数据并不是完全随机丢失的。我们已经知道,如果数据是 MAR 或 MNAR,输入缺失值是明智的。

如果您有关于某个变量中缺失值对目标变量的影响的具体假设,您可以像这样测试它:

出于某种原因,你会认为身体质量指数缺失价值观的比例会随着人们对做事的兴趣程度而有所不同。

结果表明,我们不能拒绝零假设,从而假设每个兴趣水平的身体质量指数缺失率没有差异。请注意,从概念上讲,这没有任何意义,但我不想让它远离你。😉

好的——在开始插补之前,让我们先检查一件事:阅读 NHANES 数据集的文档,我们可以看到一些变量没有记录到 9 岁或 12 岁以下的儿童(例如,自我报告的健康状况或参与者在过去一个月中身体状况不佳的天数)。为了找出年龄如何影响数据集中缺失值的存在,我们可以创建一个热图,表示按年龄细分的每个变量的缺失密度。

事实上,对于 0-9 岁的个体来说,许多变量中有更多的缺失值。在某些情况下,这也适用于青少年(10-19 岁)的人口统计变量和抑郁相关变量,但我们现在不会触及这一点。然而,在我们的统计分析中排除儿童似乎是合理的,以减少我们结果中的偏差。

案例研究—调查心血管健康

杰西·奥里科Unsplash 上拍摄的照片

假设您对心血管健康感兴趣,因为您开展了一项旨在促进心血管疾病预防的干预计划——在没有关于患者身体状况的任何进一步信息的情况下,您想知道是否有一些可能与心血管健康相关的常见参数。你可以肯定,一个人生活越活跃,你就越不可能观察到血压异常升高(Whelton et al .,2002)。类似地,身体质量指数(身体质量指数)也可能与心血管健康相关,因为肥胖个体经常经历高血压,而较瘦的人的血压往往较低(例如,Bogers,2007;Hadaegh 等人,2012)。您决定在这个大型数据集上测试您的假设,但是您必须注意缺失的值,以确定是否值得专门针对那些有患心血管疾病风险的个体。

为了便于计算,您使用动脉血压中值(MAP)作为目标变量,这是一个有效的参数(Kundu,Biswas & Das,2017 年),代表血管系统中的血压平均值,不考虑收缩压和舒张压的波动。

下面是我们如何根据收缩压和舒张压计算每个人的 MAP(Psychrembel,2004 ):

MAP =舒张压+ 1/3(收缩压-舒张压)

估算缺失数据—填空

在进入我喜欢的插补技术之前,让我们先了解一下各种各样的插补技术,例如均值插补、最大似然插补、热卡插补和 k 近邻插补。即使它们确实有些用处,它们也有一个共同的缺点:它们没有考虑到统计上的不确定性,而这种不确定性自然与输入缺失值有关。我们如何知道我们是否准确地预测了可能被记录的值?此外,我们估算的质量如何影响我们的统计模型?

理解好的就意味着理解坏的——如果我们简单地用它们各自的平均值代替所有的连续变量,数据会发生什么?让我们看看。

我们可以看到红色的估算值和蓝色的自然值——估算值似乎形成了一种“十字”,看起来有些不自然。这是因为与记录值不同,均值估算值不包括自然方差。因此,这些值不太“分散”,并且在技术上可以最小化我们的线性回归中的标准误差。我们会认为我们的估计比现实生活中的更准确。

因此,我们需要一个好的替代方案——链式方程多重插补(MICE)是我最喜欢的方法,因为它非常灵活,可以同时处理不同的变量类型(例如,连续变量、二元变量、有序变量等)。).对于每个包含缺失值的变量,我们可以使用数据中的剩余信息来创建一个模型,该模型可以预测记录了哪些内容来填补空白——当使用统计软件时,这完全是在后台悄悄发生的。为了说明插补中的统计不确定性,MICE 程序经过几轮,并计算每轮缺失值的替代值。顾名思义,我们因此多次填充缺失值,并创建几个完整的数据集,然后将结果汇集在一起,以获得更真实的结果。如果你对链式方程多重插补的更多细节感兴趣,我推荐你阅读这篇由 Azur 和他的同事(2011)写得很好的论文。当你已经知道随机森林优于简单决策树的时候,这个想法可能会让你想起什么?

让我们通过使用 Stef van Buuren (2020)的精彩的 mice 包来实现 R 中的 MICE 过程。

install.packages("mice")
library(mice)

为了对每一个包含缺失值的目标变量做出好的预测,我们保存至少与它有些相关(r > 0.25)的变量。

然后,我们运行实际插补程序 10 次,设置一个种子,选择一种方法,并在我们的原始数据集上使用预测矩阵。根据您选择的回合数,计算可能需要一段时间…

现在,我们对 10 个估算数据集的每一个进行回归分析,并最终汇总结果。为了了解统计不确定性,我们将在汇总结果的回归总结中包含 95%的置信区间。

正如预期的那样,我们可以看到身体质量指数以及身体活动的程度显著地预测了我们的 NHANES 子样本中的平均血压( p < .001 )。身体质量指数的回归估计值约为 0.41,这意味着每增加一个单位,我们预计平均动脉压将增加 0.41 毫米汞柱。考虑到置信区间,当从我们的不同插补周期中抽取回归系数时,身体质量指数和血压之间的关系似乎始终是正的(95% CI [0.25,0.56])。然而,对于身体活动的程度,我们的置信区间包括积极和消极的估计(95% CI [- 1.07,0.44]),这应该使我们持怀疑态度。这意味着,根据每一轮的插补质量,我们会得到不同的结果,从而会以不同的方式解释脉搏和身体质量指数之间的关系。假设你只有一轮(简单的插补),那么你就没有机会评估你的系数估计值的可靠性。因此,我们很大程度上受益于多次输入缺失值并汇集结果!

请注意,我们不知道血压和身体质量指数之间的关系是否是因果关系,但假设这种轻微的联系似乎并不牵强——即使这种联系可能受到健康生活方式的调节(例如,频繁的体育活动,适当的营养等)。).

此外,我们将创建一个带状图来评估插补质量——红点是否自然符合报告值?

低身体质量指数值似乎有更多的估算值,这是由更高密度的缺失值造成的(您可以从平均估算散点图中猜到)。除此之外,估算值很好地分散在数据云中,似乎在各轮估算中没有实质性的不同。

我们完成了——现在我们可以使用混合插补来完善我们的数据集,这样就不会有遗漏。

奖励回合——通过交叉验证评估模型的稳健性

现在我们有了一个简单的回归模型,可以预测平均动脉血压。一种常见的情况是,我们希望实际利用我们的知识,预测新的参与者样本中未知的血压。但是对于这项工作来说,它真的足够准确吗?

我们首先将数据分成测试数据和训练数据,然后只在一部分数据上训练算法。

与我们之前的分析一致,只有身体质量指数与血压显著相关。R 平方值表明我们的模型只能解释大约 5%的血压变化。

我们仍然试图使用该模型在算法从未见过的数据集(测试数据集)内实际预测血压。最后,我们将评估模型的准确性。

输出给我们的 RMSE 值为 11.83,这意味着平均而言,预测值与实际值相差约 12 个血压单位。假定正常的 MAP 值在 65 和 110 毫米汞柱之间,大约 12 毫米汞柱的偏差可能会使接近正常值(例如 62 毫米汞柱)的值向并发症和心力衰竭的临界值移动。学者提出,在平均动脉压为 50 毫米汞柱的情况下,即使 1 分钟,外科手术期间的死亡风险也会增加 5% (Maheshwari 等人,2018)。在这种情况下,我们糟糕的估计精度表明我们的模型不能代替真实数据(例如,实际记录的血压)。此外,错误率达 14%,与高质量的算法(谷歌、脸书等)相比,这仍然相当高。)达到 95%以上的准确率。对我来说,这个模型似乎并没有真正击中目标,但是嘿——它仍然有助于得到粗略的估计。在牢记这些限制的时候,开始就不错了!

了解你的拼图

处理丢失数据的最好办法是什么都不要有。

—格特鲁德·玛丽·考克斯

…但仍然无处不在。所以,掌握一些如何处理思念的诀窍绝对是值得的。你已经学会了如何总结、可视化和估算缺失数据,以符合后续分析。通过这种方式,你不仅知道你的拼图缺少哪些部分,而且你有看到更大画面的技术技能。

参考文献

[1] J. W. Graham,缺失数据分析:在现实世界中发挥作用。(2009 年),心理学年度评论60 ,549–576

[2]c . k hler,S. Pohl & C. H. Carstensen,(2017),处理大规模认知评估中的无反应项目:缺失数据方法对估计解释关系的影响教育测量杂志,54(4),397–419

[3] R. Pruim, NHANES:数据来自美国国家健康和营养检查研究 (2016), R 包

[4] N. Tierney,D. Cook,m .麦克拜恩,C. Fay,M. O'Hara-Wild 和 J. Hester, Naniar:缺失数据的数据结构、摘要和可视化 (2019), R 包

[5] S. P. Whelton,A. Chin,X. Xin 和 J. He,有氧运动对血压的影响:随机对照试验的荟萃分析 (2002),内科年报,136(7),493–503

[6] R. P. Bogers,W. J. Bemelmans,R. T. Hoogenveen,H. C. Boshuizen,M. Woodward,P. Knekt… & M. J. Shipley,超重与冠心病风险增加的相关性部分独立于血压和胆固醇水平:对包括 300 000 多人的 21 项队列研究的荟萃分析 (2007),《内科学文献》,167(16)

[7] F. Hadaegh,G. Shafiee,M. Hatami 和 F. Azizi,收缩压和舒张压、平均动脉压和脉压用于预测中东人口的心血管事件和死亡率 (2012),血压,21(1),12–18

[8] R. N. Kundu,S. Biswas & M. Das (2017),平均动脉压分类:对血压相关风险协变量进行统计解释的更好工具心脏病学和血管病学:国际期刊,1–7

[9] W. Psychrembel,mittler arterieller Druck(2004)。klini sches wrter buch。260. Aufl。慕尼黑德·格里特

[10] M. J. Azur、E. A. Stuart、C. Frangakis 和 P. J. Leaf,链式方程的多重插补:它是什么,如何工作? (2011),《国际精神病学研究方法杂志》,20(1),40–49

[11]s . v . buu ren & k . Groothuis-Oudshoorn(2010),小鼠:R统计软件杂志,1–68

[12] K. Maheshwari,S. Khanna,G. R. Bajracharya,N. Makarova,Q. Riter,S. Raza,… & D. I. Sessler,一项非心脏手术期间连续无创血压监测的随机试验 (2018),麻醉和镇痛127 (2),424

用于人工智能筛查的基于智能手机的视网膜照相装置

原文:https://towardsdatascience.com/smartphone-based-retinal-camera-rig-for-ai-screening-4f7944d5a513?source=collection_archive---------35-----------------------

用于人工智能视网膜成像的智能手机系统的博客和描述

作者图片

最近,我为我的组织 Drishti 完成了一个项目,该项目为孟加拉国的农村地区进行基于人工智能的糖尿病视网膜病变筛查。为了在没有全成本视网膜相机的情况下进行视网膜扫描,我们建造了一个可以容纳智能手机的钻机来拍摄这些图像。这些图像然后可以上传到我们研究支持的人工智能算法,以提供即时和准确的初步筛选。这个项目展示了软件和硬件之间的和谐,因为我们能够有效地将我们的人工智能算法与低成本的视网膜相机装备相结合。希望你喜欢这篇文章,如果你有任何想法或需要改进的地方,请告诉我。

软件

我们 CNN 的模型示意图(图片由作者提供)

我们的算法是执行糖尿病视网膜病变筛查的 CNN。算法本身将实用性置于新颖性之上。基础网络是 DenseNet-121,最后一层替换为 5 节点全连接层。我们预测糖尿病视网膜病变分为 5 个不同的阶段。

模型架构细节表。m 代表小批量。(图片由作者提供)

为了训练 CNN,我们使用了开源的 APTOS 2019 失明检测眼底图像数据集(APTOS)。在孟加拉人的眼睛上,该算法达到了 93%以上的准确率,考虑到它训练的图像来自一个单独的数据集,这是非常令人印象深刻的。结果证实了在孟加拉国的真实临床环境中使用该算法的潜力。

我们的算法在两个孟加拉人眼睛数据集上的结果(图片由作者提供)

关于孟加拉人眼睛的方法细节和结果的论文可在此处获得。

五金器具

视网膜摄像系统(图片由作者提供)

我们构建了我们的系统来容纳带有视网膜附件的智能手机。这个视频演示了如何使用它。框架由 PVC 管制成,所有附件都是 CAD 设计和 3D 打印的。患者将他们的下巴放在下巴托上,并直视视网膜照相机。然后,技术人员会用 iPhone 拍摄照片,并上传到 Drishti 的网络服务器上,以获得即时预测。

该系统可以很容易地拆卸,以便可以在不同的诊所之间转移。这些部件是用拉链绑在一起的,所以需要时可以拆开。我们的装配指南是这里是

这个建筑的成本不到 20 美元,花了大约 2 个月的时间规划、设计和购买材料。视网膜附件本身是必须购买的,但是其余的部件都是低价的。整个系统运行良好,我们目前正在将该系统的副本部署到我们正在开始试运行的两家诊所。

我们目前正在开发 v2,它为可调节组件增加了翼形螺钉,以提高适应性。我们的 CAD 可在这里获得。如果你点击 STL 文件,你将能够与 3D 渲染的设计进行交互。

这是与萨吉夫·沙阿合作完成的。他协助开发和设计 CAD 零件,并 3D 打印材料。他还帮助组装了第一个原型,并撰写了指导性文件。

感谢您阅读这篇关于我们项目细节的短文/博文。如果你有任何想法或想法,请告诉我!

用于活动识别的智能手机(第一部分)

原文:https://towardsdatascience.com/smartphone-for-activity-recognition-228b9e75c306?source=collection_archive---------20-----------------------

R 中的数据科学

你的手机知道你在做什么,即使没有麦克风或摄像头

塞尔温·范·哈伦在 Unsplash 上的照片

**Table of Contents**· [Library](#24eb)
· [Dataset](#ef5d)
· [Data Cleaning](#15aa)
· [Exploratory Data Analysis](#097c)
· [Cross-validation](#265a)
· [Metrics](#2b5b)
· [Modeling](#6ca2)
  ∘ [Naive Bayes](#8b68)
  ∘ [Decision Tree](#b02b)
  ∘ [k-Nearest Neighbors](#f14a)
  ∘ [Random Forest](#ebbe)
· [Conclusion](#3122)

现代智能手机配备了加速度计和陀螺仪等传感器,以提供高级功能和更好的用户体验。智能手机中的加速度计用于检测手机的方向。陀螺仪通过跟踪旋转或扭曲,为加速度计提供的信息增加了一个额外的维度。

已经进行了利用这些传感器的研究,例如在估计路面不平度条件中。然而,我们正在做的将更类似于哈佛大学的这项研究。具体来说,这个项目是建立一个模型,根据智能手机的测量准确预测人类的活动,如行走、上楼、下楼、坐着、站着或躺着。

图书馆

我们将使用 R 语言。这是一些要导入的库。

library(dplyr)          # data wrangling
library(ggplot2)        # visualization
library(Rtsne)          # EDA
library(caret)          # machine learning functions
library(MLmetrics)      # machine learning metrics
library(e1071)          # naive bayes
library(rpart)          # decision tree
library(rattle)         # tree visualization
library(class)          # k-NN
library(randomForest)   # random forest

资料组

用于训练该模型的数据集是从 30 名在腰部佩戴智能手机进行不同活动的人那里收集的,并在智能手机传感器的帮助下进行记录。该实验被录像以手动标记数据。要查看更多详情,请参考此链接

我们来读一下数据集。

uci_har <- read.csv("UCI HAR.csv")
dim(uci_har)#> [1] 10299   563

如上所示,该数据集包含 563 个要素和 10299 个观测值。太多了!目前,我们不需要完全理解测量的含义。简而言之,我们有这些:

  1. subject特征表示进行实验的受试者的标识符。有 30 个唯一的 id,每个 id 对应 30 个人中的一个。
  2. Activity特征表示受试者正在进行的活动,包括:行走、上楼、下楼、坐、站、躺
  3. 具有时域和频域变量的 561 特征向量。

根据问题陈述,Activity将是我们的目标特性。

数据清理

首先,将subjectActivity特征转换成因子,其他的转换成数值。

uci_har <- uci_har %>% 
  mutate_at(c('subject', 'Activity'), as.factor) %>% 
  mutate_at(vars(-subject, -Activity), as.numeric)lvl <- levels(uci_har$Activity)
lvl#> [1] "LAYING"             "SITTING"            "STANDING"           "WALKING"            "WALKING_DOWNSTAIRS" "WALKING_UPSTAIRS"

让我们检查是否有任何重复的观察值或丢失的值。

cat("Number of duplicated rows:", sum(duplicated(uci_har)))#> Number of duplicated rows: 0cat("Number of missing values:", sum(is.na(uci_har)))#> Number of missing values: 0

太好了!它们都不存在。现在让我们检查数据不平衡。

ggplot(uci_har %>% 
         group_by(subject, Activity) %>% 
         count(name = 'activity_count'), 
       aes(x = subject, y = activity_count, fill = Activity)) + 
  geom_bar(stat = 'identity')

就每个受试者的活动计数而言,没有显著差异。因此,所有目标类都是平衡的。

探索性数据分析

我们可以把Activity分为两类:静止活动(如躺、坐或站)和运动活动(如走路、下楼或上楼)。我们来看看物体加速度信号三轴震级均值的分布(phew,真拗口!我真正的意思是tBodyAccMagmean

ggplot(uci_har,
       aes(x = tBodyAccMagmean, group = Activity, fill = Activity)) +
  geom_density(alpha = .5) + 
  annotate('text', x = -.8, y = 25, label = "Stationary activities") + 
  annotate('text', x = -.0, y = 5, label = "Moving activities")

ggplot(uci_har,
       aes(y = Activity, x = tBodyAccMagmean, group = Activity, fill = Activity)) +
  geom_boxplot(show.legend = FALSE)

我们可以看到两组之间的明显区别:

  1. 与移动活动相比,静止活动具有非常小的身体运动。
  2. 如果tBodyAccMagmean > -0.5,那么活动大概会是要么走路,要么上楼,要么下楼。
  3. 如果tBodyAccMagmean < -0.5,那么活动最有可能是躺、站、坐。

现在,在手机定位方面,做爱和其他活动也应该有所区别。与其他活动不同,躺着时,人们倾向于将手机水平放在腰间。那么,让我们通过比较 X、Y、Z 轴之间的角度与每个轴上重力加速度信号的平均值(angleXgravityMeanangleYgravityMeanangleZgravityMean)来看看这个假设是否成立。

for (coor in c('angleXgravityMean', 'angleYgravityMean', 'angleZgravityMean')) {
  print(
    ggplot(uci_har,
           aes_string(y = 'Activity', x = coor, group = 'Activity', fill = 'Activity')) + 
      geom_boxplot(show.legend = FALSE)
  )
}

很明显:

  1. 躺着时的手机方向明显不同于做其他活动时的手机方向。
  2. 如果angleXgravityMean >为 0,则该活动极有可能是铺设,否则为其他活动。
  3. 如果angleYgravityMean < -0.25 或者angleZgravityMean < -0.25,那么该活动将可能是铺设,其他活动则不然。
  4. 我们可以仅通过使用angleXgravityMean,或者可能通过 X 轴的其他重力相关特征,以最小的误差预测铺设活动。

最后,我们可以对数据集执行 t-SNE 来减少它的维度,以便可视化,希望每个Activity将被分组到不同的区域。t-SNE 是一种无监督的非线性技术,主要用于数据探索和可视化高维数据。基本上,t-SNE 所做的是给我们一种感觉或直觉,让我们知道数据是如何在高维空间中排列的。在本文中,我们不会深入研究 SNE 霸王龙。我们将通过在 5、10 和 20 的困惑中做灵敏度来执行 t-SNE,以确保降维后获得的值确实被分组到不同的Activity中。

for (perp in c(5, 10, 20)) {
  tsne <- Rtsne(uci_har %>% select(-c(subject, Activity)), perplexity = perp)
  tsne_plot <- data.frame(x = tsne$Y[,1], y = tsne$Y[,2], Activity = uci_har$Activity)
  print(
    ggplot(tsne_plot) + 
      geom_point(aes(x = x, y = y, color = Activity))
  )
}

可以看出,除了站和坐,所有的活动都可以很容易地分开。这是有道理的,因为站着和坐着在手机方向上没有太大的区别。

交叉验证

对于这个问题,我们不能应用正常的 k 倍交叉验证。回想一下,我们的目标是基于手机传感器预测人类活动。这意味着当一个新的看不见的subject进来时,我们不知道他们用手机的行为。如果我们通过随机选择观察值来使用 k-fold 交叉验证,那么相同的subject有可能同时出现在训练和测试数据集中,这表明存在数据泄漏。为了避免这种情况,在交叉验证期间,我们分割数据集,使得训练和测试数据集中的subject不相交。

set.seed(2072) # for reproducibility
subject_id <- unique(uci_har$subject)
folds <- sample(1:5, 30, replace = TRUE)
d <- data.frame(col1 = c(subject_id), col2 = c(folds))
uci_har$folds <- d$col2[match(uci_har$subject, d$col1)]
uci_har <- uci_har %>% 
  mutate(folds = as.factor(folds)) %>% 
  select(-subject)

请注意,在为每个观察结果创建折叠后,我们丢弃了subject特征,因为在分析中不再需要它。最后,我们还可以在下面看到,数据在褶皱之间均匀分布,因此没有出现不平衡数据。

ggplot(uci_har %>%
         group_by(folds, Activity) %>%
         count(name = 'activity_count'),
       aes(x = folds, y = activity_count, fill = Activity)) +
  geom_bar(stat = 'identity')

韵律学

在考虑以下原因后,我们使用精确度来量化模型的性能:

  1. 根据问题陈述,我们只对同等准确地预测每个类感兴趣,而不是偏爱其中一个,因此怀疑召回和精度度量的目的。
  2. 这个问题是一个多类分类,其准确性比 ROC-AUC 度量更容易解释。

建模

首先,作为健全性检查,让我们看看数据集的维度,以及每个要素中的最大值和最小值。

dim(uci_har)#> [1] 10299   563max(apply(uci_har %>% select(-c(Activity, folds)), 1, max))#> [1] 1min(apply(uci_har %>% select(-c(Activity, folds)), 1, max))#> [1] 0.795525min(apply(uci_har %>% select(-c(Activity, folds)), 1, min))#> [1] -1max(apply(uci_har %>% select(-c(Activity, folds)), 1, min))#> [1] -0.9960928

所有特征的最小值都接近-1,而最大值的范围在 0.8 到 1 之间。我们不会进行任何标准化,因为前面提到的范围被认为很小,更重要的是,我们不想丢失太多关于特征之间相关性的信息。

数据集有 563 个要素,其中两个将在建模时被丢弃。它们是Activity(因为这是目标变量)和folds(因为这不会向数据集添加任何信息)。

我们将提出四个模型来实现问题陈述的最佳解决方案:朴素贝叶斯、决策树、k-最近邻和随机森林。为了简化,下面是一个交叉验证所有模型的函数。在此函数中,我们迭代之前已构建的交叉验证的每个折叠,并在每次迭代中执行以下操作:

  1. 创建X_trainy_trainX_testy_test,分别为训练的预测变量、训练的目标变量、测试的预测变量、测试的目标变量。
  2. 建立模型,预测输出为y_pred
  3. 通过比较y_predy_test计算模型精度。
  4. 然后对所有折叠的精度结果进行平均,产生一个数字来比较所有模型。

注意,对于随机森林模型,我们也使用交叉验证精度而不是 OOB 误差,这样与其他模型的比较就是苹果对苹果。但是,稍后我们会单独处理随机森林模型,以强调超参数调整的重要性。

crossvalidate <- function(data, k, model_name,
                          tuning = FALSE, mtry = NULL, nodesize = NULL) {
  # 'data' is the training set with the 'folds' column
  # 'k' is the number of folds we have
  # 'model_name' is a string describing the model being used
  # 'tuning' is a mode in which this function will operate, tuning = TRUE means we are doing hyperparameter tuning
  # 'mtry' and 'nodesize' are used only in Random Forest hyperparameter tuning

  # initialize empty lists for recording performances
  acc_train <- c()
  acc_test <- c()
  y_preds <- c()
  y_tests <- c()
  models <- c()

  # one iteration per fold
  for (fold in 1:k) {

    # create training set for this iteration
    # subset all the datapoints where folds does not match the current fold
    training_set <- data %>% filter(folds != fold)
    X_train <- training_set %>% select(-c(Activity, folds))
    y_train <- training_set$Activity

    # create test set for this iteration
    # subset all the datapoints where folds matches the current fold
    testing_set <- data %>% filter(folds == fold)
    X_test <- testing_set %>% select(-c(Activity, folds))
    y_test <- testing_set$Activity

    # train & predict
    switch(model_name,
      nb = {
        model <- naiveBayes(x = X_train, y = y_train, laplace = 1)
        y_pred <- predict(model, X_test, type = 'class')
        y_pred_train <- predict(model, X_train, type = 'class')
      },
      dt = {
        model <- rpart(formula = Activity ~ ., data = training_set %>% select(-folds), method = 'class')
        y_pred <- predict(model, X_test, type = 'class')
        y_pred_train <- predict(model, X_train, type = 'class')
      },
      knn = {
        k <- round(sqrt(nrow(training_set)))
        y_pred <- knn(train = X_train, test = X_test, cl = y_train, k = k)
        y_pred_train <- knn(train = X_train, test = X_train, cl = y_train, k = k)
      },
      rf = {
        if (tuning == FALSE) {
          model <- randomForest(x = X_train, y = y_train, xtest = X_test, ytest = y_test)
        } else {
          model <- randomForest(x = X_train, y = y_train, xtest = X_test, ytest = y_test,
                                mtry = mtry, nodesize = nodesize)
        }
        y_pred <- model$test$predicted
        y_pred_train <- model$predicted
      },
      {
        print("Model is not recognized. Try to input 'nb', 'dt', 'knn', or 'rf'.")
        return()
      }
    )

    # populate corresponding lists
    acc_train[fold] <- Accuracy(y_pred_train, y_train)
    acc_test[fold] <- Accuracy(y_pred, y_test)
    y_preds <- append(y_preds, y_pred)
    y_tests <- append(y_tests, y_test)
    models <- c(models, list(model))
  }

  # convert back to factor
  y_preds <- factor(y_preds, labels = lvl)
  y_tests <- factor(y_tests, labels = lvl)

  # get the accuracy between the predicted and the observed
  cm <- confusionMatrix(y_preds, y_tests)
  cm_table <- cm$table
  acc <- cm$overall['Accuracy']

  # return the results
  if (model_name == 'knn') {
    return(list('cm' = cm_table, 'acc' = acc, 'acc_train' = acc_train, 'acc_test' = acc_test))
  } else {
    return(list('cm' = cm_table, 'acc' = acc, 'acc_train' = acc_train, 'acc_test' = acc_test, 'models' = models))
  }
}

现在我们准备好了。

朴素贝叶斯

nb <- crossvalidate(uci_har, 5, 'nb')
cat("Naive Bayes Accuracy:", nb$acc)#> Naive Bayes Accuracy: 0.7258957

朴素贝叶斯模型给出的结果很差,准确率为 73%。这主要是由于模型的基本假设,即每个预测因子都是相互独立的,而在我们的数据集中并非如此。例如,我们有以下模型没有捕捉到的一些预测值之间的高度相关性。

set.seed(3)
col <- c(sample(names(uci_har), 6))
GGally::ggcorr(uci_har[, col], hjust = 1, layout.exp = 3, label = T)

让我们看看下面的混淆矩阵。

nb$cm#>                     Reference
#> Prediction           LAYING SITTING STANDING WALKING WALKING_DOWNSTAIRS WALKING_UPSTAIRS
#>   LAYING               1623      14        4       0                  0                0
#>   SITTING               286    1550     1188       0                  0                0
#>   STANDING                0     187      677       0                  0                0
#>   WALKING                 2       0        1    1181                 93               40
#>   WALKING_DOWNSTAIRS      4       0        0     234               1065              124
#>   WALKING_UPSTAIRS       29      26       36     307                248             1380

朴素贝叶斯模型仍然在几个微不足道的不同活动之间混淆,比如躺下和坐着(大约 300 个错误的预测)。这个模型也很难区分上楼和静止的活动(大约 90 个错误的预测)。

最后,正如下面可以看到的,基于预测训练和测试数据集所得到的准确性,我们可以看到该模型已经相当不错,没有对训练数据集欠拟合或过拟合(除了第四个折叠)。因此,我们不能过多地依靠权衡偏差和方差来提高模型性能。

print(nb$acc_train)#> [1] 0.7349193 0.7647131 0.7280039 0.7635290 0.7612536print(nb$acc_test)#> [1] 0.7475728 0.7491702 0.7276636 0.6863137 0.7169533

决策图表

dt <- crossvalidate(uci_har, 5, 'dt')
cat("Decision Tree Accuracy:", dt$acc)#> Decision Tree Accuracy: 0.8599864

决策树模型给出了更好的结果,准确率为 86%。通过注意 t-SNE 结果,我们可以看出为什么我们的数据集是高度可分的(除了坐着和站着的活动)。基于树的模型可以利用这一特性。为了让大家了解决策树是如何工作的,请观察下面的 5 个树形图,每个图代表一个交叉验证文件夹。

for (model in dt$models) {
  fancyRpartPlot(model, sub = NULL)
}

人们可以很容易地看到,tGravityAccminXtGravityAccmeanX在根之后的第一次分割中成为一个关键特征。如果该特征小于某个阈值,则模型可以完美地预测相应的活动正在进行,这占所有训练数据集观察的 19%。这与我们的 EDA 结果是一致的,即仅通过观察 X 轴的一个重力相关特征就可以区分铺设活动。

在第二次分割中,基于身体加速度信号,模型可以将坐着和站着与移动活动完全分开(坐着和站着占 36%,而所有移动活动占训练数据集观察的 45%)。这一发现证实了我们先前的分析,即静止和运动活动可以相当容易地分开。

然后,在第三次分解到树叶时,模特们分开了坐着和站着,同样都是移动的活动,有一些误差。这意味着坐着和站着,同样都是移动的活动,对于模特来说很难区分。为了看清楚这一点,这里有一个混淆矩阵。

dt$cm#>                     Reference
#> Prediction           LAYING SITTING STANDING WALKING WALKING_DOWNSTAIRS WALKING_UPSTAIRS
#>   LAYING               1942      15        0       0                  0                0
#>   SITTING                 2    1564      334       0                  0                0
#>   STANDING                0     196     1571       8                  0                0
#>   WALKING                 0       0        1    1549                151              296
#>   WALKING_DOWNSTAIRS      0       1        0      39               1149              166
#>   WALKING_UPSTAIRS        0       1        0     126                106             1082

此表中的一个小注意事项:与朴素贝叶斯模型不同,决策树模型不会错误地预测上楼时的平稳活动。事实上,与朴素贝叶斯模型中的 91 个预测相比,在这种情况下只有一个预测是错误的。

接下来,我们来看看哪些变量是最重要的。下表再次证实了 X 轴的重力相关特征(tGravityAccminXangleXgravityMean)对我们模型的重要性。

for (model in dt$models) {
  var_imp <- varImp(model)
  var_imp <- var_imp %>% slice_max(Overall, n = 10)
  print(var_imp)
}#>                             Overall
#> tGravityAccminX            1647.338
#> tGravityAccmeanX           1522.782
#> tGravityAccenergyX         1521.581
#> angleXgravityMean          1521.581
#> tGravityAccmaxX            1505.993
#> fBodyAccJerkbandsEnergy116 1374.982
#> fBodyAccJerkbandsEnergy124 1374.982
#> fBodyAccmadX               1374.982
#> fBodyAccmeanX              1374.982
#> tBodyAccJerkmadX           1374.178
#>                        Overall
#> angleXgravityMean     1494.691
#> tGravityAccenergyX    1494.691
#> tGravityAccmeanX      1494.691
#> tGravityAccminX       1494.691
#> tGravityAccmaxX       1483.883
#> fBodyAccJerkentropyX  1376.017
#> fBodyAccmadX          1376.017
#> fBodyAccmeanX         1376.017
#> tBodyAccJerkmadX      1376.017
#> tBodyAccJerkMagenergy 1376.017
#>                      Overall
#> angleXgravityMean   1504.419
#> tGravityAccenergyX  1504.419
#> tGravityAccmeanX    1504.419
#> tGravityAccminX     1504.419
#> tGravityAccmaxX     1488.823
#> fBodyAccJerkenergyX 1370.566
#> fBodyAccmadX        1370.566
#> fBodyAccmeanX       1370.566
#> tBodyAccJerkenergyX 1370.566
#> tBodyAccJerkstdX    1370.566
#>                      Overall
#> tGravityAccminX     1528.936
#> tGravityAccmeanX    1527.734
#> angleXgravityMean   1526.532
#> tGravityAccenergyX  1526.532
#> tGravityAccmaxX     1508.549
#> tBodyAccJerkenergyX 1387.769
#> tBodyAccJerkmadX    1387.769
#> tBodyAccJerkstdX    1387.769
#> tBodyAccmaxX        1387.769
#> tBodyAccstdX        1387.769
#>                             Overall
#> tGravityAccminX            1531.881
#> tGravityAccmeanX           1530.679
#> angleXgravityMean          1529.478
#> tGravityAccenergyX         1529.478
#> tGravityAccmaxX            1512.692
#> fBodyAccJerkbandsEnergy116 1379.583
#> fBodyAccJerkbandsEnergy124 1379.583
#> fBodyAccmadX               1379.583
#> fBodyAccmeanX              1379.583
#> tBodyAccJerkmadX           1378.776

通常情况下,决策树模型倾向于过度适应训练数据集,因为它们可以很容易地适应数据中的噪声。幸运的是,这不是我们的情况,因为正如下面可以看到的,除了第四次折叠,训练和测试数据集的精度都很接近。因此,树木修剪是不必要的。

print(dt$acc_train)#> [1] 0.8897925 0.8959707 0.8811845 0.8897192 0.8691917print(dt$acc_test)#> [1] 0.8660194 0.8914177 0.8643096 0.7902098 0.8855037

k-最近邻

由于 k-NN 是一种基于距离的模型,因此数据集必须在建模之前进行归一化,以便模型可以平等地对待每个要素。换句话说,如果某个特征与其他特征相比具有相对较大的值,那么它将在选择数据点的邻居时对模型产生主要影响。但是在我们的例子中,我们没有对数据集进行任何规范化,原因已经在建模部分的开头解释过了。

knn <- crossvalidate(uci_har, 5, 'knn')
cat("k-Nearest Neighbors Accuracy:", knn$acc)#> k-Nearest Neighbors Accuracy: 0.8925138

k-NN 模型给出了比决策树模型更好的结果,准确率为 89%。同样,这是由于我们的数据集是高度可分离的,因此 k-NN 算法可以很容易地对每个活动进行分组。

knn$cm#>                     Reference
#> Prediction           LAYING SITTING STANDING WALKING WALKING_DOWNSTAIRS WALKING_UPSTAIRS
#>   LAYING               1926      13        0       0                  0                0
#>   SITTING                 5    1369      245       0                  0                0
#>   STANDING                5     390     1659       0                  0                0
#>   WALKING                 1       0        1    1658                 98               90
#>   WALKING_DOWNSTAIRS      0       0        0      35               1178               52
#>   WALKING_UPSTAIRS        7       5        1      29                130             1402

然而,k-NN 模型在区分坐着和站着活动方面不如决策树模型。此外,一些静止的活动被预测为上楼。另一方面,与决策树模型相比,更多的移动活动被正确预测。

通过在训练和测试数据集上比较准确性,可以看出该模型恰到好处,具有低偏差和方差。

print(knn$acc_train)#> [1] 0.9299672 0.9269841 0.9332196 0.9308184 0.9335673print(knn$acc_test)#> [1] 0.9169903 0.9184448 0.8781653 0.8516484 0.8958231

随机森林

rf <- crossvalidate(uci_har, 5, 'rf')
cat("Random Forest Accuracy:", rf$acc)#> Random Forest Accuracy: 0.9336829

与其他模型相比,随机森林模型给出了迄今为止最好的结果,准确率高达 93%。特别是,随机森林几乎总是比决策树好,原因如下:

  1. 随机森林是许多决策树模型的集合。它基于许多决策树的多数投票,因此倾向于减少单个决策树预测的错误。
  2. 随机森林执行 bootstrap 聚合,生成一系列模型,将弱学习者转换为强学习者,从而克服一个决策树的过拟合问题。

然而,这些优点并非没有缺点:随机森林模型比决策树训练更慢,更难解释。

现在让我们看看混淆矩阵。

rf$cm#>                     Reference
#> Prediction           LAYING SITTING STANDING WALKING WALKING_DOWNSTAIRS WALKING_UPSTAIRS
#>   LAYING               1942      15        0       0                  0                0
#>   SITTING                 0    1626      189       0                  0                0
#>   STANDING                0     135     1717       0                  0                0
#>   WALKING                 0       0        0    1585                 18               37
#>   WALKING_DOWNSTAIRS      0       0        0      27               1293               54
#>   WALKING_UPSTAIRS        2       1        0     110                 95             1453

随机森林模型仍然很难弄清楚哪个是坐着的,哪个是站着的,哪个是从移动活动中来的。然而,除了这些错误之外,这个模型只错分了 18 个其他的观察值,这是所有观察值的一小部分。

根据下面的变量重要性表,我们看到随机森林模型更喜欢重力相关的特征,而不是身体相关的特征。

for (model in rf$models) {
  var_imp <- varImp(model)
  var_imp <- var_imp %>% slice_max(Overall, n = 10)
  print(var_imp)
}#>                     Overall
#> tGravityAccmeanX   233.5234
#> tGravityAccminX    212.9809
#> angleXgravityMean  190.1192
#> tGravityAccmaxX    185.7354
#> angleYgravityMean  168.6782
#> tGravityAccenergyX 158.2211
#> tGravityAccminY    152.9756
#> tGravityAccmaxY    149.8530
#> tGravityAccmeanY   128.6168
#> tGravityAccenergyY 115.0822
#>                      Overall
#> tGravityAccmeanX   215.90652
#> tGravityAccminX    199.06699
#> tGravityAccenergyX 187.14571
#> tGravityAccmaxX    174.64894
#> angleXgravityMean  170.14726
#> tGravityAccmaxY    148.36554
#> angleYgravityMean  147.43523
#> tGravityAccmeanY   136.34275
#> tGravityAccminY    132.14115
#> tGravityAccenergyY  83.03708
#>                     Overall
#> angleXgravityMean  211.0124
#> tGravityAccminX    193.4731
#> tGravityAccmaxX    183.6834
#> tGravityAccenergyX 178.2531
#> tGravityAccmaxY    175.3123
#> tGravityAccmeanX   170.4459
#> tGravityAccmeanY   166.4416
#> tGravityAccminY    164.2081
#> angleYgravityMean  159.2264
#> tGravityAccenergyY 113.4814
#>                     Overall
#> tGravityAccmaxX    214.2470
#> tGravityAccminX    201.6110
#> tGravityAccenergyX 198.3143
#> angleXgravityMean  191.6710
#> tGravityAccmeanY   185.8804
#> tGravityAccmeanX   182.7646
#> tGravityAccmaxY    179.4252
#> angleYgravityMean  172.8559
#> tGravityAccminY    171.5347
#> tGravityAccenergyY 102.5362
#>                      Overall
#> tGravityAccmeanX   208.67569
#> angleXgravityMean  202.43801
#> tGravityAccminX    192.91251
#> tGravityAccenergyX 185.74270
#> tGravityAccmaxX    158.31243
#> tGravityAccmaxY    148.26482
#> angleYgravityMean  145.74691
#> tGravityAccmeanY   142.97585
#> tGravityAccminY    126.27075
#> tGravityAccenergyY  95.61133

最后,从下面的训练和测试精度来看,很明显,该模型在训练和测试数据集上表现得非常好,尽管在第三次和第四次折叠中,测试精度仍然略低于 91%。我们已经知道,如果我们从决策树模型切换到随机森林(由于装袋和随机特征选择),过度拟合的趋势应该会减少。然而,泛化误差不会变为零。随着添加更多的树,泛化误差的方差将趋近于零,但偏差不会!

print(rf$acc_train)#> [1] 0.9815512 0.9805861 0.9837923 0.9865011 0.9842691print(rf$acc_test)#> [1] 0.9631068 0.9720247 0.9096990 0.8971029 0.9248157

我们可以通过超参数调整来进一步改进模型。预计通过修剪树木,我们可以交易一些偏差,降低两个值。这是通过增加随机森林模型中的nodesize参数来实现的。参考 R 文档,nodesize是终端节点的最小尺寸。将该数值设置得越大,生成的树就越小(因此花费的时间也就越少)。对于分类问题,默认值为 1,这往往会使模型过度适应数据中的噪声。

除了nodesize,我们还将调优mtry(在每次分割时随机抽样作为候选变量的变量数量,在我们的例子中默认为 sqrt(561) = 23)。我们将通过改变nodesizec(3,5,7)mtryc(11,16)来进行网格搜索。

# establish a list of possible values for nodesize and mtry
nodesize <- c(3, 5, 7)
mtry <- c(11, 16)# create a data frame containing all combinations 
hyper_grid <- expand.grid(mtry = mtry, nodesize = nodesize)# initialize empty vectors to store the results
rf_acc <- c()
rf_acc_train <- c()
rf_acc_test <- c()# loop over the rows of hyper_grid
for (i in 1:nrow(hyper_grid)) {
  # cross validate
  rf_tuning <- crossvalidate(uci_har, 5, 'rf', 
                             tuning = TRUE, mtry = hyper_grid$mtry[i], nodesize = hyper_grid$nodesize[i])
  # store the results
  rf_acc[i] <- rf_tuning$acc
  rf_acc_train <- c(rf_acc_train, list(rf_tuning$acc_train))
  rf_acc_test <- c(rf_acc_test, list(rf_tuning$acc_test))
}# identify optimal set of hyperparameters based on accuracy
opt_i <- which.max(rf_acc)
print(hyper_grid[opt_i,])#>   mtry nodesize
#> 5   11        7

找到的最佳超参数是nodesize的 7 和mtry的 11。有了这些,准确率稍微提高了一点,接近 94%,如下图所示。此外,所有的测试精度在褶皱之间更加一致,并且具有 91%以上的值。

print(rf_acc[opt_i])#> [1] 0.9370813print(rf_acc_train[opt_i])#> [[1]]
#> [1] 0.9815512 0.9788767 0.9820863 0.9849343 0.9831801print(rf_acc_test[opt_i])#> [[1]]
#> [1] 0.9684466 0.9687055 0.9182991 0.9130869 0.9154791

结论

rbind("Naive Bayes" = nb$acc, "Decision Tree" = dt$acc, "k-Nearest Neighbors" = knn$acc, "Random Forest" = max(rf_acc))#>                      Accuracy
#> Naive Bayes         0.7258957
#> Decision Tree       0.8599864
#> k-Nearest Neighbors 0.8925138
#> Random Forest       0.9370813

根据上面的准确度表,Random Forest 显然是最佳模型。Random Forest 能够根据人们的手机行为识别人类活动,准确率高达 94%。另一方面,随机森林运行缓慢,因为默认情况下它是 500 个决策树的集合。当然,我们也可以尝试更简单的模型,如 One-vs-Rest 逻辑回归,或目前业界非常标准的 boosting 模型,如 XGBoost 或 LightGBM,然后比较结果。

🔥你好!如果你喜欢这个故事,想支持我这个作家,可以考虑 成为会员 。每月只需 5 美元,你就可以无限制地阅读媒体上的所有报道。如果你注册使用我的链接,我会赚一小笔佣金。

🔖想了解更多关于经典机器学习模型如何工作以及如何优化其参数的信息?或者 MLOps 大型项目的例子?有史以来最优秀的文章呢?继续阅读:

Albers Uzila

艾伯斯·乌兹拉

从零开始的机器学习

View list8 storiesAlbers Uzila

艾伯斯·乌兹拉

高级优化方法

View list7 storiesAlbers Uzila

艾伯斯·乌兹拉

MLOps 大型项目

View list6 storiesAlbers Uzila

艾伯斯·乌兹拉

我最好的故事

View list24 storiesAlbers Uzila

艾伯斯·乌兹拉

R 中的数据科学

View list7 stories

用于活动识别的智能手机(第二部分)

原文:https://towardsdatascience.com/smartphone-for-activity-recognition-part-2-67c1d779a46e?source=collection_archive---------41-----------------------

R 中的数据科学

处理维数灾难

格蕾丝·玛德琳在 Unsplash 上的照片

**Table of Contents**· [Problem Statement 1](#bd40)
· [Library](#1f50)
· [Dataset](#cb25)
· [Data Cleaning](#5b6c)
· [Principal Component Analysis](#d06c)
· [Cross-Validation](#f06c)
· [Modeling](#d278)
· [Problem Statement 2](#daf9)
· [Clustering](#8f03)
· [Conclusion](#c624)

B 在阅读这篇文章之前,我们鼓励你先阅读上一篇文章,以便对我们将要做的事情有一点了解。本文研究两个问题:降维和聚类。尽情享受吧!

问题陈述 1

在上一篇文章中,我们在人类活动识别数据集上执行分类。我们知道这个数据集有如此多的特征(准确地说是 561 个),其中一些特征彼此之间有很强的相关性。使用该数据集,随机森林模型可以以高达 94%的准确度对人类活动进行分类。然而,这样做需要很长时间。第二个候选模型是 k-NN 模型,准确率为 89%,其次是决策树模型,准确率为 86%,朴素贝叶斯模型,准确率为 73%。这让我们思考,

我们可以用计算时间换取一些准确性吗?或者,我们真的可以进一步提高某些模型的性能吗?

这个想法是减少数据集的维度,使大部分信息仍然保留。当然,这也可能会降低模型的性能,但好消息是模型可以运行得更快。在之前的文章中,我们实际上已经使用 t-SNE 进行了降维,以创建一个可视化,试图看到每个观察在高维空间中的相对位置。然而,t-SNE 是一种非确定性算法,因此我们在三个不同的值中改变了困惑度,以确保结果不是随机的。此外,t-SNE 试图只保留数据的本地结构,而不能保留其差异。

在本文中,我们将介绍一种称为主成分分析(PCA)的新算法,作为 t-SNE 降维的替代算法。PCA 是一种确定性算法,保留了数据的全局结构,通过 PCA,我们可以使用特征值来决定保留多少方差。

图书馆

像往常一样,我们将使用 R. Load 加载所需的库。

library(tidymodels)     # collection of best packages
library(caret)          # machine learning functions
library(MLmetrics)      # machine learning metrics
library(e1071)          # naive bayes
library(rpart)          # decision tree
library(randomForest)   # random forest
library(class)          # kNN
library(FactoMineR)     # PCA algorithm
library(factoextra)     # PCA and clustering visualization

资料组

我们来读一下数据集。关于数据集的详细解释可以在之前的文章中找到。

uci_har <- read.csv("UCI HAR.csv")
dim(uci_har)#> [1] 10299   563

如上所示,该数据集包含 563 个要素和 10299 个观测值。太多了!

数据清理

将每个特征转换为相应的类型。

uci_har <- uci_har %>% 
  mutate_at(c('subject', 'Activity'), as.factor) %>% 
  mutate_at(vars(-subject, -Activity), as.numeric)

lvl <- levels(uci_har$Activity)
lvl#> [1] "LAYING"             "SITTING"            "STANDING"           "WALKING"            "WALKING_DOWNSTAIRS" "WALKING_UPSTAIRS"

有六个活动作为目标变量:WALKINGWALKING_UPSTAIRSWALKING_DOWNSTAIRSSITTINGSTANDINGLAYING。我们不会做任何进一步的数据清理,因为在上一篇文章中已经知道数据集可以被认为是干净的。

主成分分析

PCA 算法使用称为主成分(PC)的新维度来总结初始特征的信息/方差。我们的数据集适合于主成分分析,因为它有很多特征,其中有很多是相关的。所以我们现在就开始 PCA 吧。下面的柱状图表明,数据集的方差很大程度上可以用几个主要成分来解释。为了分析的目的,我们将只取 10 个主成分。

principal_component <- prcomp(x = uci_har %>% select(-c(subject, Activity)), scale. = TRUE, rank. = 10)
plot(principal_component)

图片由作者

下面是用数字表示的主成分及其摘要。

head(as.data.frame(principal_component$x))#>        PC1       PC2        PC3        PC4      PC5        PC6        PC7         PC8          PC9        PC10
#> 1 16.38018 -1.994986 -3.4155244  0.6498264 7.824682 -2.7718293  2.2981737 -5.22746872 -1.335465100  3.75994762
#> 2 15.58142 -1.182536  0.3211912 -2.7479498 4.729307 -1.5887459 -0.3340349 -1.62109884 -0.006348803 -0.07202641
#> 3 15.42324 -2.243058  1.2377235 -4.0026866 4.402521 -1.0350383 -0.1297623 -1.27912309  0.190738822  0.78085331
#> 4 15.64705 -3.762700  1.2752210 -2.8065268 3.238953 -0.7434877  0.3260538 -1.74289880  0.912173701  1.59466696
#> 5 15.84155 -4.438682  1.8081439 -3.1603532 3.331010 -0.9115065 -0.8618895 -0.09012166  0.521603541 -1.01580251
#> 6 15.64401 -4.619950  2.1287441 -2.7722016 2.462343 -0.8805438 -1.1847999  1.40402104  0.692842819 -1.48599448summary(principal_component)#> Importance of first k=10 (out of 561) components:
#>                            PC1     PC2     PC3     PC4     PC5     PC6     PC7     PC8     PC9    PC10
#> Standard deviation     16.8713 5.91623 3.88655 3.70953 3.25529 3.02525 2.81701 2.61208 2.35101 2.30763
#> Proportion of Variance  0.5074 0.06239 0.02693 0.02453 0.01889 0.01631 0.01415 0.01216 0.00985 0.00949
#> Cumulative Proportion   0.5074 0.56977 0.59670 0.62123 0.64012 0.65643 0.67058 0.68274 0.69259 **0.70208**

我们可以看到,这 10 个主成分捕获了原始数据集 70%的方差。请注意,每个主成分都是相互垂直的,因此它们彼此不相关,如下所示。

GGally::ggcorr(data = principal_component$x, label = TRUE)

图片作者作者

最后,请记住,这些主成分是原始数据集中每个要素的信息的顶点。因此,它们是不可解释的。然而,我们可以使用特征值信息近似返回原始特征,即使结果与原始数据集不完全相同。

我们可以把前两个主要成分画出来,以获得一些见解。让我们再次执行 PCA。

uci_har_pca <- PCA(X = uci_har %>% select(-subject),
                   scale.unit = TRUE,
                   quali.sup = 562,
                   graph = FALSE,
                   ncp = 10)

head(uci_har_pca$ind$coord)#>       Dim.1    Dim.2      Dim.3     Dim.4    Dim.5     Dim.6      Dim.7       Dim.8        Dim.9     Dim.10
#> 1 -16.38098 1.995083  3.4156902  0.649858 7.825062 2.7719639  2.2982853  5.22772253  1.335529939 -3.7601302
#> 2 -15.58217 1.182594 -0.3212068 -2.748083 4.729536 1.5888231 -0.3340511  1.62117755  0.006349111  0.0720299
#> 3 -15.42399 2.243166 -1.2377836 -4.002881 4.402735 1.0350886 -0.1297686  1.27918520 -0.190748083 -0.7808912
#> 4 -15.64781 3.762882 -1.2752829 -2.806663 3.239111 0.7435238  0.3260696  1.74298342 -0.912217989 -1.5947444
#> 5 -15.84232 4.438897 -1.8082316 -3.160507 3.331172 0.9115508 -0.8619313  0.09012604 -0.521628866  1.0158518
#> 6 -15.64477 4.620174 -2.1288474 -2.772336 2.462463 0.8805866 -1.1848574 -1.40408921 -0.692876458  1.4860666

然后,选择前两个主成分并绘图。

plot.PCA(x = uci_har_pca,
         choix = "ind",
         invisible = "quali",
         select = "contrib 10",
         habillage = "Activity")

图片作者作者

这里有几样东西需要打开:

  1. 维度 1 解释了原始数据集 51%的方差,而维度 2 解释了其中的 6%。
  2. Dim 1 明确区分静止活动(如LAYINGSITTINGSTANDING)和运动活动(如WALKINGWALKING_DOWNSTAIRSWALKING_UPSTAIRS)。尺寸 1 小于 0 表示静止活动,尺寸 1 大于 0 表示移动活动。
  3. WALKING_DOWNSTAIRS类别有几个异常值。或许有些人的确有不同风格的WALKING_DOWNSTAIRS
  4. SITTINGSTANDING活动堆叠在一起,很难分类。

接下来,我们可以看到有许多与身体相关的特征对 Dim 1 有贡献,还有一些与重力相关的特征对 Dim 2 有贡献。

dim <- dimdesc(uci_har_pca)
head(as.data.frame(dim$Dim.1$quanti))#>                     correlation p.value
#> fBodyAccsma           0.9889282       0
#> fBodyAccJerksma       0.9882851       0
#> fBodyGyrosma          0.9878669       0
#> tBodyAccJerksma       0.9877968       0
#> tBodyAccJerkMagsma    0.9868902       0
#> tBodyAccJerkMagmean   0.9868902       0head(as.data.frame(dim$Dim.2$quanti))#>                        correlation p.value
#> fBodyAccmeanFreqZ        0.7359410       0
#> tBodyGyroMagarCoeff1     0.7133977       0
#> fBodyAccMagmeanFreq      0.7080972       0
#> tGravityAccarCoeffZ1     0.7075568       0
#> tGravityAccMagarCoeff1   0.7068318       0
#> tBodyAccMagarCoeff1      0.7068318       0

交叉验证

由于之前,我们通过分割数据集进行 5 重交叉验证,使得训练和测试数据集中的subject不相交。

set.seed(2072) # for reproducibility
subject_id <- unique(uci_har$subject)
folds <- sample(1:5, 30, replace = TRUE)
d <- data.frame(col1 = c(subject_id), col2 = c(folds))
uci_har$folds <- d$col2[match(uci_har$subject, d$col1)]
uci_har <- uci_har %>% 
  mutate(folds = as.factor(folds)) %>% 
  select(-subject)

请注意,在为每个观察结果创建折叠后,我们丢弃了subject特征,因为在分析中不再需要它。最后,我们还可以在下面看到,数据在褶皱之间均匀分布,因此没有出现不平衡数据。

ggplot(uci_har %>%
         group_by(folds, Activity) %>%
         count(name = 'activity_count'),
       aes(x = folds, y = activity_count, fill = Activity)) +
  geom_bar(stat = 'identity')

图片作者作者

建模

现在,使用五重交叉验证执行建模步骤。下面的函数是由上一篇文章中的crossvalidate函数编辑而来。唯一不同的是,这一次我们:

  1. 使用step_nzv功能删除任何方差接近零的特征,
  2. 使用step_centerstep_scale功能对数据进行居中和缩放(因为这是 PCA 所必需的),以及
  3. 使用step_pca功能合并 PCA。

上面提到的四个函数都来自tidymodels库。将这些函数拟合到train数据集,然后使用拟合过程中发现的参数对test数据集进行转换。我们将采用几个主要成分来解释至少 90%的数据集累积方差。

crossvalidate <- function(data, k, model_name) {
  # 'data' is the training set with the 'folds' column
  # 'k' is the number of folds we have
  # 'model_name' is a string describing the model being used

  # initialize empty lists for recording performances
  acc_train <- c()
  acc_test <- c()
  y_preds <- c()
  y_tests <- c()
  models <- c()

  # one iteration per fold
  for (fold in 1:k) {

    # create training set for this iteration
    # subset all the datapoints where folds does not match the current fold
    train <- data %>% filter(folds != fold)
    y_train <- train$Activity

    # create test set for this iteration
    # subset all the datapoints where folds matches the current fold
    test <- data %>% filter(folds == fold)
    y_test <- test$Activity

    # create PCA pipeline
    rec <- recipe(Activity~., data = train) %>%
       step_rm(folds) %>% 
       step_nzv(all_predictors()) %>%
       step_center(all_numeric()) %>%
       step_scale(all_numeric()) %>%
       step_pca(all_numeric(), threshold = 0.90) %>%
       prep()

    # perform PCA pipeline
    X_train_dt <- juice(rec)
    X_test_dt <- bake(rec, test)
    X_train <- X_train_dt %>% select(-Activity)
    X_test <- X_test_dt %>% select(-Activity)

    print(glue::glue("Fold {fold} dataset reduced to {ncol(X_train)} dimensions."))

    # train & predict
    switch(model_name,
      nb = {
        model <- naiveBayes(x = X_train, y = y_train, laplace = 1)
        y_pred <- predict(model, X_test, type = 'class')
        y_pred_train <- predict(model, X_train, type = 'class')
        models <- c(models, list(model))
      },
      dt = {
        model <- rpart(formula = Activity ~ ., data = X_train_dt, method = 'class')
        y_pred <- predict(model, X_test_dt, type = 'class')
        y_pred_train <- predict(model, X_train_dt, type = 'class')
        models <- c(models, list(model))
      },
      knn = {
        k <- round(sqrt(nrow(X_train)))
        y_pred <- knn(train = X_train, test = X_test, cl = y_train, k = k)
        y_pred_train <- knn(train = X_train, test = X_train, cl = y_train, k = k)
      },
      rf = {
        model <- randomForest(x = X_train, y = y_train, xtest = X_test, ytest = y_test)
        y_pred <- model$test$predicted
        y_pred_train <- model$predicted
        models <- c(models, list(model))
      },
      {
        print("Model is not recognized. Try to input 'nb', 'dt', 'knn', or 'rf'.")
        return()
      }
    )

    # populate corresponding lists
    acc_train[fold] <- Accuracy(y_pred_train, y_train)
    acc_test[fold] <- Accuracy(y_pred, y_test)
    y_preds <- append(y_preds, y_pred)
    y_tests <- append(y_tests, y_test)
  }

  # convert back to factor
  y_preds <- factor(y_preds, labels = lvl)
  y_tests <- factor(y_tests, labels = lvl)

  # get the accuracy between the predicted and the observed
  cm <- confusionMatrix(y_preds, y_tests)
  cm_table <- cm$table
  acc <- cm$overall['Accuracy']

  # return the results
  if (model_name == 'knn') {
    return(list('cm' = cm_table, 'acc' = acc, 'acc_train' = acc_train, 'acc_test' = acc_test))
  } else {
    return(list('cm' = cm_table, 'acc' = acc, 'acc_train' = acc_train, 'acc_test' = acc_test, 'models' = models))
  }
}

在几个型号上运行该功能。

nb <- crossvalidate(uci_har, 5, 'nb')#> Fold 1 dataset reduced to 65 dimensions.
#> Fold 2 dataset reduced to 65 dimensions.
#> Fold 3 dataset reduced to 64 dimensions.
#> Fold 4 dataset reduced to 64 dimensions.
#> Fold 5 dataset reduced to 65 dimensions.cat("Naive Bayes Accuracy:", nb$acc)#> Naive Bayes Accuracy: 0.8010486dt <- crossvalidate(uci_har, 5, 'dt')#> Fold 1 dataset reduced to 65 dimensions.
#> Fold 2 dataset reduced to 65 dimensions.
#> Fold 3 dataset reduced to 64 dimensions.
#> Fold 4 dataset reduced to 64 dimensions.
#> Fold 5 dataset reduced to 65 dimensions.cat("Decision Tree Accuracy:", dt$acc)#> Decision Tree Accuracy: 0.7174483knn <- crossvalidate(uci_har, 5, 'knn')#> Fold 1 dataset reduced to 65 dimensions.
#> Fold 2 dataset reduced to 65 dimensions.
#> Fold 3 dataset reduced to 64 dimensions.
#> Fold 4 dataset reduced to 64 dimensions.
#> Fold 5 dataset reduced to 65 dimensions.cat("k-Nearest Neighbors Accuracy:", knn$acc)#> k-Nearest Neighbors Accuracy: 0.8750364rf <- crossvalidate(uci_har, 5, 'rf')#> Fold 1 dataset reduced to 65 dimensions.
#> Fold 2 dataset reduced to 65 dimensions.
#> Fold 3 dataset reduced to 64 dimensions.
#> Fold 4 dataset reduced to 64 dimensions.
#> Fold 5 dataset reduced to 65 dimensions.cat("Random Forest Accuracy:", rf$acc)#> Random Forest Accuracy: 0.8714438

我们不打算详细分析这些模型,因为这已经在上一篇文章中完成了。这里需要注意的一点是,PCA 算法仅用大约 65 个特征就可以捕获 90%的原始数据集方差。这比原来的 561 功能小了 860%!这带来了一些后果,有些是好的,有些是坏的:

  1. 朴素贝叶斯的准确率从 73%提高到 80%。这是因为 PCA 得到的特征在向量空间中是垂直的,因此彼此不相关,这完全符合朴素贝叶斯假设。
  2. 决策树、随机森林等基于树的模型越来越差。决策树准确率从 86%下降到 72%,随机森林准确率从 94%下降到 87%。这可能是由于主成分的模糊区域造成的,这使得基于树的模型难以在类分布之间设置决策边界。这个模糊区域来自于这样一个事实,即每个主成分都是所有原始特征信息的某种组合。
  3. k-NN 模型受影响没那么大。准确性从 89%到 88%只有微小的下降,这是正常的,因为我们只捕获了原始数据集方差的 90%。

问题陈述 2

假设我们不知道目标变量是什么样子,让我们完全放弃Activity

uci_har <- uci_har %>% select(-c(Activity, folds))

现在,我们的任务是将数据集中的每个观察结果分成几个类别。我们如何做到这一点?我们做了多少个集群?

使聚集

聚类分析包括寻找对象组,使得一个组中的对象彼此相似(或相关),而与其他组中的对象不同(或无关)。我们将使用 k-means 聚类对观察值进行分类。基本上,k-means 是一种基于质心的聚类算法,其工作原理如下。

  1. 随机初始化:随机放置 k 个质心
  2. 分类指定:根据到质心的距离,将每个观测值指定到最近的分类。
  3. 质心更新:将质心移动到同一个聚类的平均观测值。
  4. 重复步骤 2 和 3,直到达到收敛。

在实现 k-means 聚类时,有 2 个超参数必须由用户确定:质心的数量 k ,以及所使用的距离度量(通常是欧几里德距离曼哈顿距离)。在本文中,我们将使用欧几里德距离公式

其中 x = ( x₁x₂ ,…, xₙy = ( y₁y₂ ,…, yₙ )为维度为 n 的观测值。因此,我们只需要确定 k

由于聚类分析包括查找对象组,使得一个组中的对象彼此相似,因此好的聚类是平方和(WSS)最小的聚类,也就是说,从每个观察值到其所属聚类的质心的二次距离的和最小。此外,由于一个组中的对象需要与其他组中的对象不同,所以好的聚类也具有较大的平方和(BSS)。这里,BSS 只是从每个质心到全局平均值的二次距离之和,由每个聚类中的观察值数量加权。很容易看出,较高的 k 对应于较低的 WSS。然而, k 必须从商业的角度来确定,或者更客观地使用所谓的肘方法。数学上,WSS 和盲源分离公式如下

在哪里

uci_har_scaled <- scale(uci_har)
fviz_nbclust(x = uci_har_scaled, FUNcluster = kmeans, method = "wss")

图片由作者

肘方法说,我们应该选择 k,在那里增加它不会导致 WSS 更明显的减少。因此从上面的图中,我们选择 k = 2。由于 k-means 聚类是一种不确定的算法(由于质心的随机初始化),我们将运行 k-means 5 次,然后取 WSS 最低的一个。

set.seed(42) # for reproducibility
result <- c()
for (i in 1:5) {
  uci_har_km <- kmeans(x = uci_har_scaled, centers = 2)
  wss <- uci_har_km$tot.withinss
  bss <- uci_har_km$betweenss
  print(glue::glue("Trial {i} WSS: {wss} \t BSS: {bss}"))
  result <- c(result, list(uci_har_km))
}#> Trial 1 WSS: 3272538.83119699   BSS: 2504639.16880301
#> Trial 2 WSS: 3272538.83119699   BSS: 2504639.16880301
#> Trial 3 WSS: 3272538.83119699   BSS: 2504639.16880301
#> Trial 4 WSS: 3272538.83119699   BSS: 2504639.16880301
#> Trial 5 WSS: 3272538.83119699   BSS: 2504639.16880301

由于 BSS 和 WSS 对每个试验给出相同的数字,聚类过程很可能对所有试验给出相同的结果。我们可以选择任何一个试验,所以就选第五个吧。我们可以将这种聚类形象化如下。

fviz_cluster(object = uci_har_km, data = uci_har_scaled, labelsize = 0)

图片作者作者

我们可以很容易地看到,所获得的两个集群最有可能是集群 2 的静止活动和集群 1 的移动活动,它们在大约 0 处被 Dim 1 分开。

结论

PCA 是一种降维技术,对于处理多维数据非常有用,原因有两个:可视化和简化。在本文中,我们看到在将人类活动识别数据集拟合到模型之前对其进行 PCA 给出了不同的结果:一些模型变得更好,一些变得更差,一些没有受到太大影响。我们还看到,在不知道目标变量的情况下对数据集进行聚类,可以很好地区分静止和移动活动。

🔥你好!如果你喜欢这个故事,想支持我这个作家,可以考虑 成为会员 。每月只需 5 美元,你就可以无限制地阅读媒体上的所有报道。如果你注册使用我的链接,我会赚一小笔佣金。

🔖想了解更多关于经典机器学习模型如何工作以及如何优化其参数的信息?或者 MLOps 大型项目的例子?有史以来最优秀的文章呢?继续阅读:

Albers Uzila

艾伯斯·乌兹拉

从零开始的机器学习

View list8 storiesAlbers Uzila

艾伯斯·乌兹拉

高级优化方法

View list7 storiesAlbers Uzila

艾伯斯·乌兹拉

MLOps 大型项目

View list6 storiesAlbers Uzila

艾伯斯·乌兹拉

我最好的故事

View list24 storiesAlbers Uzila

艾伯斯·乌兹拉

R 中的数据科学

View list7 stories

用领域数据存储打破孤岛

原文:https://towardsdatascience.com/smashing-silos-with-domain-data-stores-4c37d37fd838?source=collection_archive---------28-----------------------

在我的其他帖子中,你了解了 ABN·AMRO 如何在数据网格风格的架构中提供数据。在这篇博文中,你将了解如何打破大数据的垄断。

数据驱动的决策转变

自从数据仓库成为商品以来,情况发生了很大变化。分布式系统越来越受欢迎,数据越来越大,越来越多样化,新的数据库设计层出不穷,云的出现将计算和存储分离开来,以提高可扩展性和弹性。将这些趋势与从集中式到面向领域的数据所有权的转变结合起来,您将立即理解改变数据密集型应用程序的设计方式的重要性。

在我们的数据架构中,我们明确区分了直接数据消费和新数据创建。在数据分发架构中,正如您可以在这里阅读一样,我们已经定位了读取数据存储(RDS)来捕获并向消费者重复提供大量不可变数据。在这种模式中,读取数据,但不创建新数据。消费应用程序或用户直接使用 rds 作为他们的数据源,并可能基于相似数据元素之间的映射执行一些轻量级集成。这种模型的最大好处是它不需要数据工程团队创建和维护新的数据模型。您不需要将数据提取、转换和加载到新的数据库中。转变是即时发生的,但这些结果并不需要一个永久的新家。这种方法对于数据探索、轻量级报告和不需要复杂数据转换的简单分析模型特别有用。

然而,问题是消费者的需求可能超过 rds 所能提供的。在某些情况下,显然需要创建新的数据:例如,复杂的业务逻辑之后是生成新的业务洞察力的分析模型。为了保存这些见解供以后分析,您需要将这些信息保存在某个地方,例如,保存在数据库中。另一种情况是需要处理的数据量超过了 RDS 平台的处理能力。在这种情况下,数据处理量;例如,对历史数据的访问是如此的密集,以至于您有理由逐步将数据转移到一个新的位置,对其进行处理,并对其进行预优化以供以后使用。另一种情况是当多个 rds 需要组合和协调时。这通常需要协调许多任务并将数据整合在一起。让用户一直等到所有这些任务都完成,会对用户体验产生负面影响。这些暗示将我们带到了数据消费的第二种模式:创建域数据存储(DDSs)

域数据存储

我们希望更仔细地管理新创建的数据,同时提高灵活性。这就是 DDS 的定位。这种类型的应用程序的作用是集中处理数据,存储新创建的数据,并为消费者的用例提供便利。为了释放价值,我们设计了一个新的架构,其中包括一个数据工程团队的平台。让我们看看侧面,评价一下特点。

图 1 由 Piethein Strengholt 制作:DDS 参考架构

我们设想的是一个生态系统,允许快速交付新的数据驱动的决策用例。它在总体上促进了数据工程和密集处理,同时保持控制并且不会看到技术的扩散。我们预见到从通用(企业)数据集成到业务特定数据创建的转变;从集成专家到社区建设和无缝协作的转变;以及从僵化的数据模型向更灵活的或“轻模式”方法的转变。

从高层次来看,生态系统类似于上面的图 1;一个完全托管的平台,允许快速接收、转换和使用数据。在底部,您可以看到托管基础架构,其主要目标是隐藏所有数据工程团队的复杂性。有一些可重用的功能以自助方式支持数据工程团队。其中包括可重用和托管的数据库技术、中央监控和日志记录、沿袭、身份和访问管理、流程编排、CI/CD、数据和模式版本控制、基于批处理、API 和事件的接收模式、与商业智能和高级分析功能的集成,等等。使用团队拓扑方法管理基础平台:中央平台团队管理底层平台,同时支持所有其他团队。主要目的是简化所有服务,管理和保护平台,从而减少数据工程团队的开销。

在顶部,您可以看到 DDSs,其中的数据由数据工程团队管理。这些领域团队关注数据产品、客户旅程或业务用例。DDSs 的界限也决定了数据责任。这些包括数据质量、所有权、集成和分发、元数据注册、建模和安全性。稍后我将回到粒度和域边界。

对于功能需求,我们确保业务目标和目的是明确的、详细的和完整的。理解它们是您的解决方案的基础,并要求您澄清以下标准:需要解决哪些业务问题、需要哪些数据源、哪些解决方案需要可操作、必须实时或离线执行哪些数据处理、完整性和要求是什么,以及哪些结果可以被其他域重用。

对于非功能性需求,我们选择提供多少和什么类型的数据存储技术。您可以考虑一组通用的可重用数据库技术或数据存储和模式,以确保利用每个数据存储的优势。例如,任务关键型和过渡型应用程序可能只允许使用强一致性模型,或者商业智能和报告可能只允许使用提供快速 SQL 访问的存储。

不同的数据存储在内部管理和组织它们的数据。一种常见的组织方式是(逻辑上或物理上)分离摄入、清洗、管理、协调、服务等关注点。在我们的域数据存储中,我们鼓励使用具有不同存储技术的不同区域,例如文件夹、存储桶、数据库等。区域还允许我们组合用途,因此商店可以同时用于促进运营和分析。对于所有商店和区域,范围必须非常明确。

图 2 由 Piethein Strengholt 提供:分层 DDS 方法

对于数据模型,我们鼓励从僵化的数据模型转向更“轻模式”的方法。然而,任何建筑风格都是允许的。如果团队接受读模式或者更喜欢直接构建简单的维度模型,我们鼓励他们这样做。也可以应用 Kimball 或数据仓库建模。这完全取决于用例的需求和规模,这就把我带到了下一个主题。

域数据存储粒度

当我们从企业数据仓库过渡到更细粒度的 DDS 设计时,我们需要考虑粒度并对数据进行逻辑分段。确定逻辑 DDS 边界的范围、大小和位置非常困难,在域之间分发数据时会带来挑战。通常,边界是面向主题的,并与业务能力相一致。当定义一个域的逻辑边界时,为了简化数据建模活动和域内的内部数据分布,将它分解成子域是有价值的。

重要的任务是仔细考虑 DDS 的逻辑角色。这也涵盖了业务粒度技术粒度:

  • 业务粒度从业务关注点的自顶向下分解开始:分析最高级别的功能上下文、范围(即“边界上下文”)和活动。这些必须被划分成更小的“区域”、用例以及业务目标。这项工作需要良好的业务知识和专业知识,如何有效地划分业务流程,领域,职能等。最佳实践是使用业务功能作为参考模型,研究通用术语(无处不在的语言)和重叠的数据需求。
  • 技术粒度是针对特定目标执行的,例如:可重用性、灵活性(易于适应频繁的功能更改)、性能、安全性和可伸缩性。平衡的关键在于做出正确的权衡。一个业务领域可能使用相同的数据,但是如果技术需求相互冲突,那么最好将关注点分开。例如,如果一个特定的业务任务需要集中汇总数据,而另一个业务任务只需要快速选择单个记录,那么最好将关注点分开。这同样适用于灵活性。一个用例可能需要每天修改,另一个用例必须保持稳定至少一个季度。同样,您应该考虑分离关注点。因此,我们以这样的方式分解 DDS,即在 DDS 边界内允许实例。

当一个域更大并且由几个子域组成时,在内部组织数据的过程会变得更加复杂。这个视图中的 DDS 更加抽象:实例和区域可以在多个子域之间共享,而区域可以是独占的。让我试着用一个例子来具体说明这一点。对于大型域,您可以围绕一个 DDS 的所有不同区域绘制一个边界。例如,在这个 DDS 中,前两个区域可以在多个子域之间共享。因此,清理、纠正和建立历史数据通常是为所有子域执行的。对于转换来说,故事变得更加复杂,因为数据需要特定于一个子域或用例。因此,可以有共享的管道和专用于一个用例的管道。包括所有管道在内的整个数据链都属于一个整体,因此可以被视为一个巨大的 DDS 实施方案。正如您刚刚了解到的,在这个庞大的 DDS 实现中,您会看到不同的边界:所有子域通用的边界和特定的边界。

当域比较大,或者子域需要通用的——可重复的——集成逻辑时,分解域尤其重要。在这种情况下,拥有一个通用的子域会有所帮助,它以一种允许其他子域标准化并从中受益的方式提供集成逻辑。一个基本规则是保持子域间的共享模型较小,并始终与通用语言保持一致。对于重叠部分,我们使用不同于领域驱动设计的模式。

想象三个数据需求重叠的说明性用例。不同的集成和分布模式可以在不同的团队中应用。让我们探索一下您可以应用哪些不同的方法。

如果复制的相关成本优先于可重用性,那么可以使用分离方式模式。当需要高灵活性和敏捷性时,这种模式通常是一种选择。从建模的角度来看,当很少或没有共同点时,这也是一种选择。

图 3 由 Piethein Strengholt 制作:DDS 集成模式

当重叠很大时,团队可以使用伙伴关系模式来适应所有各方的共享开发需求。所有团队都必须愿意合作,并尊重彼此的需求。每个人都需要做出重大承诺,因为每个人都不能随意改变共同的逻辑。在这种方法中,数据工程团队既是数据消费者也是提供者:他们捕获、提取数据并将其加载到数据存储中,然后重新发布或分发。

图 4 由 Piethein Strengholt 表示:DDS 集成模式

如果一个团队很强大,并且愿意掌握下游消费者的数据和需求,那么可以使用客户-供应商模式。这种模式的缺点是相互冲突的关注点,迫使下游团队协商可交付成果和安排优先级。

图 5 由 Piethein Strengholt 制作:DDS 集成模式

一个从众模式可以用来让所有的当事人完全符合所有的要求。当集成工作极其复杂,不允许任何其他方拥有控制权,或者使用供应商包时,这种模式也是一种选择。

图 6 由 Piethein Strengholt 表示:DDS 集成模式

结论

我们在本章中构建的架构有助于您了解我们如何大规模管理数据密集型应用程序。您已经看到,为了实现更快的价值,使用域边界分解数据是很重要的。通过打破孤岛并将 DDS 之间的依赖性保持在最低限度,我们让团队保持专注。

我们在这篇博文中讨论的架构有助于我们大规模管理数据。如果你想了解更多,我推荐你去看看《规模化数据管理https://learning.oreilly.com/library/view/data-management-at/9781492054771/》一书。

R 中的平滑样条

原文:https://towardsdatascience.com/smoothing-splines-in-r-and-python-f6b50d4a7f59?source=collection_archive---------17-----------------------

线性模型的扩展

这篇文章讨论了关于平滑样条的基础知识和 r。

安娜斯塔西娅·马莱在 Unsplash 上拍摄的照片

O 关于有很多方法可以解决这个问题,但是有一个捷径可以避免在建模中考虑这些问题。这是平滑样条

平滑样条的概念

平滑样条曲线将 X 的每个唯一值作为一个结,而不是请求一系列预先选择的结。等等!是不是意味着它最多给原模型增加了 N ( N =观察次数)额外的自由度?是不是太合身了?

不要惊慌。为了解决过度拟合问题,平滑样条在其 损失函数 中引入了 惩罚项 。惩罚项鼓励更平滑的线并惩罚变化。

具体地,平滑样条的损失函数等于残差平方和( RSS )和平滑项的和,

光滑样条的损失函数。(图片来自 James,Gareth 等人《统计学习导论》。第 112 卷。纽约:施普林格,2013 年。)

其中 g 是模型函数,λ是非负调谐参数,g′′是二阶导数的平方。

我们可以看到上面损失函数中的第一项简单来说就是 RSS 。第二项,即 惩罚项 ,鼓励更平滑的拟合。为什么二阶导数的和与拟合的平滑度相关联?

众所周知,点 A 的一阶导数测量了函数在 A 的斜率。并且在 A 处的二阶导数测量在 A 处的斜率变化。然后,我们再次查看损失函数方程,我们可以很容易地理解,一个更摆动的曲线将导致一个更大的平滑项。因为我们的目标是最小化整个损失函数,所以我们更喜欢较小的平滑项,这对应于更平滑的线。很简单,不是吗?

这个平滑样条的想法让你想起什么了吗?是的,我肯定你还记得 套索 回归在损失函数中使用惩罚项来缩小线性模型的系数。类似于 套索 回归,光滑样条的损失函数既有“偏差项,也有“方差项。RSS 测量模型的偏差,平滑项测量模型的方差。

看到了吗?在统计建模中,偏差-方差权衡无处不在。我们想要的只是在它们之间找到一个平衡点,最好地“描述”真实的数据。

平滑样条曲线中的参数调整

您可能已经注意到,通过平滑样条曲线中的这些设置,平滑参数 λ 在平衡偏差和方差方面起着非常重要的作用。

如果 λ 超级巨大,那么它对拟合函数的粗糙程度惩罚很大,那么这个模型就是一条直线,因为一条直线的二阶导数为零。

如果 λ 为 0,则完全不惩罚粗糙度,那么模型是完美的过拟合,并且跟随每个训练点。

一种更直接的方式来可视化大λ对小λ

图示了更大和更小的λ(图片来自https://stats . stack exchange . com/questions/41369/optimal-choice-of-smooth-spline-parameter)

红色虚线表示较大的λ,蓝色/黑色曲线表示较小的λ。您可以看到曲线粗糙度的差异。

即使我们避免选择节点的数量和位置,我们仍然需要调整平滑样条中的平滑参数 λ。

谢天谢地,通过交叉验证可以简单解决。对应于最小测试集误差的λ是要使用的最终平滑参数。

对于大多数模型来说,留一交叉验证( LOOCV )听起来比 k 重交叉验证更耗费时间和资源,因为它需要(N - k)次额外的参数调整迭代。

然而,LOOCV 是平滑样条曲线中平滑参数调整的最简单方法。原因是 LOOCV RSS 的总和可以仅使用一个原始数据拟合所有数据来计算。这一招并不适用于所有型号。如果你对为什么会这样的理论部分感兴趣,可以参考经典著作,ESL IIby特雷弗·哈斯蒂罗伯特·蒂布拉尼,以及杰罗姆·弗里德曼

光滑样条的实现

好了,了解了平滑样条的基本概念后,让我展示一下在 R 中的实现。

幸运的是,已经有了用于平滑样条的内置函数,所以 r 中的标准实现非常简单。我将只展示基本实现,但是您可能需要在真实情况下专门修改代码。

R 中,我们将使用示例数据集 工资 中的 ISLR 。执行平滑样条的 R 包是 样条 。我们将使用 函数 smooth.spline()

library(splines)
library(ISLR)
attach(Wage)
spl_mod <- smooth.spline(age, wage, cv= TRUE) 

其中年龄工资分别表示自变量和因变量为 年龄工资 (变量名称)。这里 cv 表示该函数执行交叉验证以自动选择最佳平滑参数。

就是这样。

我希望这篇短文能帮助你更好地理解非线性数据建模中的样条。如果你对我关于数据和建模的文章感兴趣,请订阅我的 Medium 账号。谢谢大家!

威尔·斯图尔特在 Unsplash 上拍摄的照片

参考资料:

https://stats.stackexchange.com/questions/41369/optimal-choice-of-smooth-spline-parameter

詹姆斯、加雷斯等人统计学习导论。第 112 卷。纽约:施普林格,2013 年。

重击

原文:https://towardsdatascience.com/smote-fdce2f605729?source=collection_archive---------0-----------------------

实践教程

你需要知道的关于 SMOTE 的一切。提供 Python 中 SMOTE 的完整示例演练。

SMOTE:不平衡数据的强大解决方案。由埃琳娜·莫日维洛Unsplash 上拍摄的照片。

在这篇文章中,你将学到你需要知道的关于 SMOTE 的一切。SMOTE 是一种机器学习技术,可以解决使用不平衡数据集时出现的问题。不平衡数据集在实践中经常出现,掌握处理这类数据所需的工具至关重要。

SMOTE:不平衡数据的强大解决方案

SMOTE 代表合成少数过采样技术。该方法是在《人工智能研究杂志》2002 年的一篇论文中提出的。SMOTE 是分类问题中处理不平衡数据的一种改进方法。

何时使用 SMOTE?

首先,让我们回顾一下什么是不平衡数据以及何时出现不平衡数据。

不平衡数据是在分类变量的不同可能值上观察到的频率非常不同的数据。基本上,某种类型的观测很多,另一种类型的观测很少。

当您有不平衡的数据时,SMOTE 是一个解决方案。

作为一个例子,想象一个关于山地运动新产品销售的数据集。为了简单起见,我们假设网站销售给两种类型的客户:滑雪者和登山者。

对于每个游客,我们还记录游客是否购买了新的山地产品。假设我们想要制作一个分类模型,允许我们使用客户数据来预测访问者是否会购买新产品。

大多数电子商务购物者并不购买:通常,许多人是来看产品的,只有一小部分访问者真正购买了东西。我们的数据集将是不平衡的,因为我们有一个庞大的非买家数量和一个非常小的买家数量。

以下模式代表了我们的示例情况:

击打。购买我们新山地产品的阶层不平衡。图片作者。

为什么不平衡的数据是一个问题?

在数据示例中,您可以看到我们有 30 次网站访问。其中 20 人是滑雪者,10 人是登山者。目标是建立一个机器学习模型,可以预测访问者是否会购买。

这个例子只有一个独立变量:游客是滑雪者还是登山者。作为一个思维实验,让我们考虑两个非常简单的模型:

  • 使用变量“滑雪者对登山者”的模型
  • 不使用变量“滑雪者对登山者”的模型

我想避免在这里深入探讨不同的机器学习算法,但让我们从逻辑分析的角度来看,使用自变量预测买家是否有用。

10%的登山者购买,而只有 5%的滑雪者购买。基于这一数据,我们可以说登山者比滑雪者更有可能购买。然而,这并没有而不是帮助模型决定预测访问者“购买”或“不购买”。

在处理不平衡数据时,精确度是一个糟糕的机器学习指标。

为了将 30 个人分成买家/非买家,模特在这里唯一能做的事情就是预测每个人“不买”。滑雪者更有可能不买而不是买。登山者也更有可能不买。预测所有人“不买”是这里唯一的选择。

这里棘手的是,预测每个人“不买”的模型在 30 个案例中有 28 个是正确的。这转换成 30 分中的 28 分的准确率,也就是 93%!使用不平衡的数据,我们刚刚制作了一个看起来非常准确的模型,但实际上它是无用的!

欠采样

在深入 SMOTE 的细节之前,我们先来看看几个 简单的直观的抵消职业不平衡的方法**!

抵消类别不平衡最直接的方法是欠采样。欠采样意味着你丢弃了太频繁出现的类的一些数据点

欠采样的缺点是你会丢失很多有价值的数据

对于 mountain 网站的例子,我们有两个选项:“购买”和“不购买”。我们有 28 个非买家和 2 个买家。如果我们进行欠采样,我们会从数据集中随机删除大量非买家。

欠采样的优点在于,它是一种非常直接的技术来减少类不平衡。但是,我们需要删除大量的数据,这是一个巨大的缺点。

击打。欠采样:删除出现频率较高的类。图片作者。

在给出的例子中,欠采样绝对不是一个好主意,因为我们最终几乎没有数据。当有大量数据时,欠采样可能是有效的,并且类别不平衡不是很大。在一个有 40%买家和 60%非买家的例子中,欠采样不会删除这么多数据,因此它可能是有效的。

过采样

解决不平衡数据的另一个简单方法是过采样。过采样与欠采样相反。过采样意味着复制数据集中最少出现的数据。然后将这些副本添加到数据集中。

让我们把这个应用到山地运动网站的例子中。我们的产品有 2 个买家,28 个非买家。如果我们进行过采样,我们可以将购买者复制 16 次,获得一个包含 28 个购买者和 28 个非购买者的数据集。

过采样的缺点是会产生许多重复的数据点。

这样做的好处是你不必删除数据点,所以你没有删除有价值的信息。另一方面,您正在创建不真实的数据,因此您可能会在模型中引入错误的信息。

显然,在我们的山地运动例子中,我们甚至没有足够的数据点来考虑过采样。我们最终会得到许多相同的数据点,这对于任何机器学习算法来说肯定是有问题的。

击打。过采样:重复出现频率较低的类。图片作者。

然而,在不太极端的情况下,应用随机过采样实际上可能是有效的。进行此操作时,在非过采样数据集上评估您的机器学习模型的预测性能非常重要。毕竟,您的样本外预测将基于非过采样数据进行,因此这就是您应该如何衡量模型性能的方法。

数据扩充

数据扩充是一种非常类似于过采样的方法。然而,数据扩充增加了一个扭曲:你将向复制的数据点添加小的扰动,而不是在不太存在的类中精确复制观察值

这种小干扰取决于你所拥有的数据类型。该方法常用于图像处理模型,如物体检测图像分割,在其中您可以简单地扭曲、旋转和拉伸输入图像,以获得相似但不同的图像。

在表格数据中,您可以考虑向值中添加小的随机噪声,使它们与原始值略有不同。您也可以根据原始数据创建合成数据。

击打。数据扩充:复制和干扰不太频繁的类的出现。图片作者。

SMOTE 算法

SMOTE 是一种算法,通过基于原始数据点创建合成数据点来执行数据扩充。SMOTE 可以被视为过采样的高级版本,或者是数据扩充的特定算法。SMOTE 的优势在于,你是而不是生成副本,而是创建与原始数据点略有不同的合成数据点。

SMOTE 是过采样的改进替代方案

SMOTE 算法的工作原理如下:

  • 你从少数民族中随机抽取一个样本。
  • 对于本示例中的观察值,您将确定 k 个最近邻。
  • 然后,您将选择其中一个相邻点,并确定当前数据点和所选相邻点之间的矢量。
  • 将向量乘以 0 到 1 之间的一个随机数。
  • 要获得合成数据点,将它添加到当前数据点。

这个操作实际上非常类似于在它的邻居的方向上稍微移动数据点。这样,你可以确保你的合成数据点不是一个现有数据点的精确拷贝,同时确保它也不会与你的少数类中的已知观察值相差太多。

关于算法的更多细节,你可以在这里查看介绍 SMOTE 的论文

SMOTE 影响精确度和召回率

在之前展示的山地运动示例中,我们已经查看了模型的整体准确性。准确性衡量的是预测正确的百分比。在分类问题中,我们通常希望比这更进一步,并考虑每一类的预测性能。

在二进制分类中,混淆矩阵是一种机器学习度量,它显示了:

  • 真阳性(模型正确预测为真)
  • 假阳性(模型错误地预测为真)
  • 真否定 (模型正确预测假)
  • 假阴性(模型错误地预测为假)

在这个背景下,我们也来谈谈精度 vs .召回。精确度是指模型在识别阳性病例方面的成功程度。回忆意味着模型在识别数据中的所有阳性病例方面有多成功。

真正的积极和真正的消极都是正确的预测:拥有许多这样的预测是最理想的情况。假阳性和假阴性都是错误的预测:它们都很少也是理想的情况。然而在许多情况下,我们可能更喜欢假阳性而不是假阴性。

当机器学习用于自动化业务流程时,假阴性(被预测为阴性的阳性)将不会出现在任何地方,并且可能永远不会被检测到,而假阳性(被错误预测为阳性的阴性)通常会在许多企业已经实施的后续人工检查中被很容易地过滤掉。

在许多商业案例中,假阳性比假阴性问题更少。

一个明显的例子是 T2 对冠状病毒 T3 的检测。想象一下,病人做了一个测试,他们得到了一个假阴性:他们会出去传染给其他人。另一方面,如果他们是假阳性,他们将不得不呆在家里:不理想,但至少他们不会形成公共健康危害。

当我们有一个强烈的类不平衡时,我们在一个类中有很少的案例,导致模型几乎从不预测该类。使用 SMOTE,我们可以调整模型以减少假阴性,代价是增加假阳性。使用 SMOTE 的结果一般是提高召回,代价是降低精度。这意味着我们将添加更多少数类的预测:其中一些是正确的(提高召回率),但一些是错误的(降低精确度)。

SMOTE 以较低的精度为代价提高了召回率

例如,一个始终预测购买者的模型在回忆方面将是好的,因为它确实识别了所有积极的情况。然而,就精确度而言,这将是糟糕的。整体模型精度也可能下降,但这不是问题:精度不应用作不平衡数据的度量。

现在让我们转到用 Python 实现 SMOTE 的代码。

用蟒蛇皮击打

我们将在本例中使用的数据集是一个模拟数据集,有点类似于之前使用的示例。以下代码将直接从 GitHub 存储库中将数据导入 Python:

击打。导入数据。

如果你不熟悉 Github,可以在这里查看一下这个 的 GitHub 短教程

一旦您导入了这些数据,它将是一个如下所示的数据框。数据包含四个自变量和一个因变量(“购买”)。我们希望建立一个分类模型,根据其他四列中的信息预测访问者是否会购买。

击打。数据集的前五行。图片作者。

由于本文的目标是介绍 SMOTE 作为一种解决类不平衡的解决方案,我们应该做的第一件事是检查数据中的这种不平衡。下面的代码创建了一个条形图,显示购买者和非购买者的阶级分布。

击打。创建一个条形图来显示类别分布。

使用此代码,您将获得下图:

击打。原始数据中的类分布。图片作者。

我们清楚地看到,相对于少数买家,有许多非买家。

分层抽样

在本文中,我们将创建一个训练/测试分割,以在模型训练中未包括的数据集上对我们的机器学习模型的性能进行基准测试。如果你不熟悉训练/测试方法,我建议你看看这篇关于机器学习项目的整体流程的文章。

处理不平衡数据时,对训练/测试分割使用分层抽样。

在平衡数据的情况下,我们可以简单地通过将 30%的数据随机分配给一个测试集来生成一个训练/测试集。但是,在不平衡数据中,应该避免这种情况。测试集中几乎没有少数类案例,这是一个很大的风险。

分层抽样就是解决这个问题的办法。分层采样将在训练和测试数据集上强制使用与原始数据相同的类平衡。您可以使用 scikitlearn 的 train_test_split 进行分层采样,如下所示:

击打。创建分层列车测试分割。

现在让我们用一个图来验证训练中的类分布是否与原始数据中的相同。这是在建模之前必须完成的一项重要检查。

击打。检验训练数据中的类别分布。

您将获得下图。由于我们在 scikitlearn 的 train_test_split 函数中使用了分层抽样,它确认了类分布是平等的。

击打。训练数据中类别分布的条形图。图片作者。

为了绝对安全,让我们也检查一下测试数据中的分层是否顺利。您可以使用以下代码来实现这一点:

击打。为测试数据创建相同的图表。

除了使用测试数据作为输入之外,这段代码将创建与前面相同的条形图。您将获得下图:

击打。验证测试数据中的类别分布。图片作者。

一切看起来都很好:训练数据和测试数据都具有与原始数据相同的类分布。我们现在可以继续制作一个机器学习模型来预测哪个网站的访问者最终会购买一些东西。

我们将使用的机器学习模型是逻辑回归。逻辑回归是我们可以用于分类的最简单的模型之一。这通常是您在处理分类问题时应该尝试的第一个模型。

让我们首先在原始数据上构建一个逻辑回归模型,这样我们就有了使用 SMOTE 时的性能基准。您可以使用下列程式码将逻辑回归拟合到定型资料,并在测试资料上建立预测。

击打。对训练数据进行逻辑回归拟合,并对测试集进行预测。

为了评估这个模型的预测性能,让我们从查看混淆矩阵开始。混淆矩阵一边显示预测结果,另一边显示实际结果。

如果你有两个以上的结果,混淆矩阵会给你每一个预测类和实际类组合的准确细节。当你只有两个结果类别时,这转化为真阳性、真阴性、假阳性和假阴性。

您可以使用以下代码获得混淆矩阵:

击打。检查混淆矩阵。

输出可能略有不同,因为随机的训练/测试分割将导致精确测试集中的差异。但是,在我的例子中,获得的输出如下:

  • 真阴性:281
  • 误报:4
  • 假阴性:12
  • 真阳性:3

我们将使用的第二个分类指标是 scikitlearn 的分类报告。它是一个非常有用的工具,可以提取关于我们模型的许多度量。您可以通过以下方式获得它:

击打。获取分类报告。

获得的报告如下所示。值得关注的是每个类别的精确度(0 =非买家,1 =买家)和每个类别的召回率。

击打。这是我们对原始数据的分类报告。图片作者。

用 SMOTE 重新进行逻辑回归

下一步,我们将使用混淆矩阵和分类报告作为基准。我们现在将应用 SMOTE 来减少类别不平衡,并比较应用 SMOTE 前后的相同指标。

imblearn 包非常适合 Python 中的 SMOTE

第一步是使用 imblearn 包中的 SMOTE 函数来创建 X 和 y 的重采样数据集。可按如下方式完成:

击打。使用 imblearn 包中的 SMOTE 对 X 和 Y 数据集进行拟合和重采样。

让我们验证一下这对我们的阶级不平衡有什么影响。下面的代码将产生与我们之前创建的相同的条形图:

击打。应用 SMOTE 后检查班级平衡。

您将获得下图:

击打。显示应用 SMOTE 后的类分布。图片作者。

这张图表清楚地表明,我们现在有了前所未有的大量买家。这些都是由 SMOTE 创建的合成数据点

现在,让我们重做模型,研究 SMOTE 对我们的分类指标的影响。您可以使用以下代码重做模型:

击打。在用 SMOTE 重新采样的数据上改装模型。

我们现在重做我们在之前的模型中所做的指标。这将允许我们比较两者,并估计 SMOTE 的影响。您可以获得如下混淆矩阵:

击打。检查使用 SMOTE 重新采样的数据的混淆矩阵。

您将获得以下结果(由于随机创建训练/测试集,这些结果可能略有不同):

  • 真实负数:265(本来是 281,所以 T2 用 SMOTE 减少了 T3)
  • 误报:20(是 4,所以这个随着重击增加)
  • 假阴性:2(是 12,所以这个用 SMOTE 减少了
  • 真阳性:13(是 3,所以这有增加与 SMOTE)

这表明 SMOTE 导致了整体准确性的降低(整体正确率降低)。然而,由于 SMOTE 的帮助,我们确实成功地大幅增加了真阳性(正确识别的买家)的数量。

这证实了,如前所述,当您想要将错误转移到假阳性而不是假阴性时,SMOTE 非常有用。在很多商业案例中,误报比漏报问题小。

让我们也生成分类报告。这可以通过以下方式完成:

击打。显示了使用 SMOTE 重新采样的数据的分类报告。

获得的分类报告如下所示:

击打。这是使用 SMOTE 重新采样的数据的分类报告。图片作者。

将此分类报告与之前的分类报告进行比较时,我们可以观察到一些变化:

  • 非购买者的回忆从 0.99 下降到 0.93: 还有更多我们没有成功找到的非购买者
  • 买家召回率从 0.4 上升到 0.87: 我们成功确定了更多买家
  • 买家的精确度从 0.77 下降到 0.39: **正确地识别更多买家的成本是,我们现在也错误地识别更多买家(识别访客为买家,而他们实际上不是买家)**

这证实了一个结论,即我们现在能够更好地找到买家,代价是也错误地将更多非买家归类为买家。

结论

在这篇文章中,你已经发现了作为分类问题中不平衡数据的解决方案的 SMOTE 算法。SMOTE 是过采样的智能替代方案:它不是创建少数类的副本,而是创建与原始数据点相对相似的合成数据点

使用 SMOTE,您的模型将开始检测更多的少数类案例,这将导致召回率增加,但精度下降。决定这是否是想要的行为总是取决于你的商业案例。

您还看到了如何用 Python 实现 SMOTE。使用 imblearn 包中的 SMOTE 函数并对网站销售数据进行逻辑回归,您已经确认 SMOTE 以较低的精度为代价实现了少数民族类别的较高召回率。

我希望这篇文章对你有用。感谢阅读!

SMOTE:表格数据的合成数据扩充

原文:https://towardsdatascience.com/smote-synthetic-data-augmentation-for-tabular-data-1ce28090debc?source=collection_archive---------10-----------------------

SMOTE 及其一些变体如 Borderline-SMOTE 和 ADASYN 的探讨

图一。SMOTE、Borderline-SMOTE 和 ADASYN 表示法|作者图片|图标取自 freepick

当阶级之间没有平衡的分配时,就会出现阶级不平衡的问题。解决这种问题的直觉是向少数类添加更多的数据,以在类之间产生平衡。然而,在真实的机器学习系统中,由于问题的性质,很难获得更多的数据。因此,已经提出了各种备选方案来解决表格数据中的类别不平衡问题,例如过采样、欠采样和合成数据的生成。在这篇博客中,我们将重点关注从 SMOTE [ 2 ]算法及其变体生成的合成表格数据,为此,这篇博客将分为以下几部分:

  • 什么是 SMOTE?
  • 临界撞击
  • 阿达辛
  • 练习中的击打

什么是 SMOTE?

合成少数过采样技术 ( SMOTE 由 Nitesh V. Chawla 等人引入。到。2002 年 2SMOTE 是一种过采样技术,主要用于生成合成表格数据。 SMOTE 的总体思路是生成少数类的每个样本与其“ k ”最近邻之间的合成数据。也就是说,对于少数类样本中的每一个,定位其“ k ”个最近邻居(默认情况下 k = 5 ),然后在该样本和其每个邻居生成的点对之间,生成新的合成数据。在图 2 中,您可以看到对 SMOTE 实现的可视化描述。

图二。SMOTE 视觉描述|作者图片

如图 2 (b)所示,考虑到最近的 3 个邻居( x2x3x4 ),应用 SMOTEx1 生成合成数据,生成合成数据 s1s2s3

虽然 SMOTE [ 2 是一种允许生成合成表格数据的技术,但是这种算法本身具有一些局限性。 SMOTE 仅适用于连续数据(也就是说,它不是为生成分类合成数据而设计的),另一方面,生成的合成数据是线性依赖的,这会导致生成的数据存在偏差,从而产生过度拟合的模型。为此,基于 SMOTE 的替代方案已经被提出,旨在改进原始 SMOTE 技术的局限性。

让我们来看看一些最相关的变化!

临界 SMOTE

Borderline-SMOTE 是韩晖等人推出的 SMOTE 的变体。在。2005 年 3 。与最初的 SMOTE 技术不同, Borderline-SMOTE 专注于通过仅考虑构成划分一个类与另一个类的边界的样本来生成合成数据。也就是说,边界平滑检测哪些样本在类空间的边界上,并将平滑技术应用于这些样本。在图 3 中,您可以看到对边界平滑的可视化描述。

图 3。边缘平滑视觉描述|作者图片

如前图所示,被视为生成合成样本的少数类样本是边界线的一部分。边界线-SMOTE 的替代方案是 SVM-SMOTE ,它使用支持向量机来确定边界线。

阿达辛

自适应合成 ( ADASYN )是由何海波等人提出的。艾尔。2008 年 4ADASYN 是一种基于 SMOTE 算法生成合成数据的技术。 ADASYNSMOTE 之间的区别在于 ADASYN 实现了一种方法,该方法检测在由多数类支配的空间中发现的少数类样本,这是为了在少数类的较低密度区域中生成样本。也就是说, ADASYN 关注那些因为处于低密度区域而难以分类的少数类样本。在图 4 中,您可以看到对 ADASYN 的可视化描述。

图 4。ADASYN 视觉描述|作者图片

从上图中可以看出,被视为生成合成样本的样本是那些位于低密度区域的样本。一个替代 ADASYN 的方法是 K-Means-SMOTE ,它基于在少数类中发现的每个聚类的密度生成合成样本。

在实践中受到打击

在本节中,我们将看到使用 python 库不平衡学习【1】SMOTE2】实现及其变体(Borderline-SMOTE[3】和ADASYN4)。为了对这些技术进行比较,将使用 scikit-learn 框架的模块 make_classification 生成一个不平衡数据集。稍后,将显示与每个算法相对应的可视化效果,以及在准确度精度召回f1-得分指标下对每个模型的评估。因此,让我们从数据集的生成开始。

代码片段 1。数据生成

代码片段 1 生成了一个 2000 年的样本数据集,其中只有 2 个要素和 2 个类,多数类占数据集的 95%,少数类仅占 5%。生成的数据集的可视化如图 5 所示。

图 5。阶级失衡|作者图片

得益于不平衡学习库, SMOTEBorderline-SMOTEADASYN 的实现相对简单。我们只需要扩展每个所需的过采样算法,并为每个算法定义一些参数。每个算法的实现如代码片段 2 所示。

代码片段 2。SMOTE、Borderline-SMOTE 和 ADASYN

值得一提的是,对于该示例,定义了一些固定参数,例如要考虑的“k”个最近邻居的情况,以及确定样本何时处于危险的邻居数量(对于边界-SMOTE 的情况)。这些超参数将取决于数据集的大小、类别不平衡比率、少数类别中的样本数量等。

图 6、7 和 8 分别显示了 SMOTEBorderline-SMOTEADASYN 算法的实现的可视化。

图 6。SMOTE |作者图片

图 7。作者的边界-SMOTE |图像

图 8。ADASYN |作者图片

培训和评估方法基于使用分层 K-fold 交叉验证技术的逻辑回归的实施,以保持每个 fold 中班级的平衡。“ dummy ”模型是指带有不平衡数据的分类器的实现。获得的结果如表 1 所示,其中每个评价指标是在交叉验证过程中获得的平均值。

表 1。结果比较

给定为该示例生成的数据集的特征,发现具有最佳结果的模型是基于 Borderline-SMOTE 的模型。

结论

在这篇博客中,我们看到 SMOTE 是一种基于过采样的技术,用于生成合成表格数据。同样地, Borderline-SMOTEADASYN 算法是对原始 SMOTE 算法的改进。

考虑到用于解决类别不平衡问题的各种技术,建议尝试各种替代方案并选择最适合您具体问题的方案,始终考虑超参数的调整。

参考

[1] 不平衡学习

[2] SMOTE:合成少数过采样技术

[3] Borderline-SMOTE:不平衡数据集学习中一种新的过采样方法

[4] ADASYN:用于不平衡学习的自适应合成采样方法

具有策略梯度的 Snake 深度强化学习

原文:https://towardsdatascience.com/snake-with-policy-gradients-deep-reinforcement-learning-5e6e921db054?source=collection_archive---------18-----------------------

策略梯度深度强化学习在蛇游戏中的应用

作者 GIF

介绍

策略梯度方法用于实现深度强化学习研究领域的最新成果。在这篇文章中,我将介绍最简单的策略梯度方法,即“强化”算法,并向您展示它在学习玩贪吃蛇游戏的任务中表现如何。我将介绍以下主题:

  • 对我的设置和术语的简单介绍
  • 增强算法如何工作的解释
  • 玩蛇的算法结果
  • 贪吃蛇游戏的 Python 代码及加固算法

概念

状态

状态𝑠(𝑡)是座席在某一时间点𝑡.的状态在我们的例子中,蛇的状态包含以下信息:

  • 在一键编码中,苹果相对于蛇的方向
  • 蛇当前移动的方向,采用一键编码
  • 如果在蛇头(0 或 1)的正上方有一个障碍物(墙或蛇的身体)(直接意味着距离为 1)
  • 如果蛇头正下方有障碍物(0 或 1)
  • 如果蛇头的左边有障碍物(0 或 1)
  • 如果蛇头右侧有障碍物(0 或 1)

在一键编码中,我们将整数表示为二进制向量。我们有四个方向:上(0),下(1),左(2)和右(3)。一键编码中的相同方向:

  • 【1,0,0,0】为上升
  • 【0,1,0,0】为羽绒
  • 【0,0,1,0】为左
  • 【0,0,0,1】为右

我们使用一次性编码,因为我们使用神经网络来估计策略函数,并且当输入在 0 和 1 之间变化时,它有利于训练。

整个状态被组织在一个列向量中。例如:苹果在蛇的上方,蛇在向下移动。蛇尾就在蛇的左边。状态向量 s(t)看起来像这样:

作者图片

注意,有了这个状态向量,蛇只能感知到左侧、右侧、上方和下方的障碍物。它不能感知与其位置成对角线的障碍物。

行动

蛇有四种可能的动作:

  • 提升
  • 下移
  • 向左移动
  • 向右移动

这意味着蛇也可以移动到自己的尾巴里:

如果蛇在这种情况下向上移动,它就会死去。(图片由作者提供)

报酬

奖励以下列方式给予:

  • 吃苹果: +100
  • 垂死挣扎: -10
  • 向苹果靠近: +1
  • 远离苹果: -1

政策

该政策由𝜋.表示它输出给定状态𝑠(𝑡和参数 𝜃 的情况下,我们的 snake 代理采取行动的概率。在我们的例子中,参数是普通深度神经网络的权重。为了选择一个行动,我们用𝜋.给出的概率从一个离散的概率分布中取样意味着我们从来没有一个确定性的政策,以一定的概率选择次优的行动。这也确保了对代理的充分探索。学习过程应该减少不良行为的概率,增加想要的行为的概率。

经过 100 次培训后,该政策会是什么样子

归来

代理的目标是在一集里获得最大的总回报。一集就是一场比赛。在每一个时间步𝑡,我们希望最大化未来的预期总回报。起初这可能有点令人困惑。为什么不调整我们的政策,使我们的蛇在过去获得的回报最大化呢?让我们这样来看:如果我们在一个特定的时间点,我们应该执行行动,这将我们置于在未来获得最多回报的位置。

该策略在一集结束时更新。这是蛇死亡的时候。对于每一个时间步,我们知道蛇在下一个时间步得到的奖励是什么。所以我们可以定义返回 𝐺.它是未来奖励的贴现或未贴现的总和。在我们的例子中,我们选择引入贴现因子𝛾,因为遥远未来的回报具有不确定性。在普通政策梯度中,𝛾通常为 0.99。𝛾值为 1 意味着它是不贴现的,伽马值越低意味着我们的代理人越关注眼前的回报。

返回功能(图片由作者提供)

时间步长𝑇是这一集的最后一个时间步长。在我们的例子中,这意味着蛇死亡的时间。

损失

我们使用梯度下降来训练我们的神经网络/策略函数。损失可以通过以下方式计算:

我们神经网络的损失函数(图片由作者提供)

损失是相当直观的。我们希望最大限度地提高采取高回报𝐺.行动的可能性损失前面有一个负号,这意味着我们做梯度上升。当我们针对这个损失对策略进行梯度下降时,这意味着我们在一个时间步长上增加了概率输出,如果在这个时间步长上有高回报的话。此处的对数来自于这样一个事实,即对和求偏导数比对乘法求偏导数更容易,对数将乘积转换为和。但是对数不会改变方程的平均值。

梯度上升

权重以下列方式更新。这与用于更新神经网络权重的算法相同,但它是梯度上升而不是下降。下面的公式是通过获取损失函数相对于我们的神经网络的权重的偏导数而获得的。在实践中,我们使用自动签名的框架,如 tensorflow 或 pytorch,因此只需要知道损失函数。

作者图片

这里,𝜆是网络的学习率。

伪代码

完整的算法如下面的伪代码所示。我们可以看到神经网络的权重在每一集之后都会更新

最终加固算法(图片由作者提供)

结果

10 集

代理人还没有意识到如何玩这个游戏。

作者 GIF

100 集

代理人已经学会了玩这个游戏,但是仍然有一些概率代理人采取了一个次优的行动,并最终杀死了自己。

作者 GIF

552 集

代理玩游戏玩得很好,并取得了很高的分数。

作者 GIF

1000 集

这个代理人设法打破了以前的高分记录。但是分数的差异非常大。

作者 GIF

在这张 GIF 图中,这条蛇以一种它几乎无法避免的方式将自己包裹在尾巴里,杀死了自己,因为它没有意识到自己的尾巴与头部位置成对角线。

在实际场景中蛇看不到的陷阱(图片由作者提供)

如果我们在状态向量中加入对角变量的输入特性,这个问题就可以解决。

2852 集

2852 集之后,特工达到了整个 9000 集训练的最高分。下面的 GIF 向我们展示了贪吃蛇游戏中最难解决的问题。当蛇长得很长时,它可以用它的大身体围成一个大圈,这样它就无法逃脱了。

作者 GIF

总体学习曲线

下面的情节显示了蛇的分数(以吃掉的苹果为单位)与过去的剧集/游戏数的对比。请注意,蛇的表现差异如此之大,以至于我不得不用移动平均值 4 来平滑曲线,以便正确地看到趋势。

蛇分数(吃的苹果)与情节(图片由作者提供)

我们可以看到,蛇的性能在训练开始时增长非常快,但随后开始收敛到平均 25 左右。这个限制是由于蛇接收的信息不完整。

结论和展望

通过简单的强化算法,我们的代理可以获得大约 25 的很高的平均分数和 51 的高分,这并不坏,特别是与 Q 学习所获得的结果相比。但以下问题仍有待解决:

  1. 蛇没有意识到斜对着它头部的障碍物,因此把自己困在尾巴里。

2.这条蛇没有意识到它尾巴的其余部分,因此把自己困在自己尾巴的大圈里。

这两个问题都可以通过给蛇更多的空间信息来解决。第一个问题可以简单地通过让蛇接近其头部对角线 1 个像素的障碍物来解决。第二个问题可以通过给蛇整个操场作为输入,并用卷积神经网络( CNN )代替香草深度神经网络来解决。

然而,给蛇提供更多的信息需要注意。我们需要更多的训练时间让蛇达到同样的平均分。我们甚至冒着蛇根本不能学习的风险,因为当使用神经网络作为我们策略函数的近似时,不能保证收敛到好的分数。

参考

萨顿和巴尔托,《强化学习》

Youtube:大卫·西尔弗,“第七讲:政策梯度方法”

Icalems 博客,“萨顿&巴尔托总结第 13 章——政策梯度方法”

密码

算法

相关文章

https://medium.com/@Vincent.Mueller/einstein-index-notation-d62d48795378

想联系支持我?

领英
https://www.linkedin.com/in/vincent-m%C3%BCller-6b3542214/
脸书
https://www.facebook.com/profile.php?id=100072095823739
推特
https://twitter.com/Vincent02770108
中等
https://medium.com/@Vincent.Mueller
成为中等会员并支持我(你的部分会员费直接归我)
https://medium.com/@Vincent.Mueller/membership

飞机上的蛇

原文:https://towardsdatascience.com/snakes-on-a-plane-75b5b95612e0?source=collection_archive---------29-----------------------

使用 Python 包分发系统运送您的 Snakemake 管道

图片由 pch.vector / Freepik 提供

Snakemake 是最流行的数据科学工作流管理包之一,但是它缺乏分发您创建的工作流(管道)的功能。在这篇文章中,我将演示如何通过使用 Python 包分发系统来添加这样的功能。使用这种分配系统有以下好处:

  • 在可安装包中将工作流分发给无法访问您的基础架构的其他数据科学家
  • 向管道添加版本控制
  • 减少 Snakemake 的样板文件(例如必需的参数—-cores)

遵循这些指导方针,安装和运行 snakemake 工作流将像执行以下命令一样简单:

步骤 0:先决条件

要构建一个包,需要(可发现的)Python 3 安装。最简单的方法是创建一个包含 Python 3 的 Conda 环境。在这篇文章中,我在 Unix 系统上使用了 Python 3.9。要继续学习,需要一些使用 Conda 和 Snakemake 的经验。

步骤 1:创建包装材料

图片来自 burst.shopify.com

对于这个例子,我们将创建一个 Python 包(称为 snakepack ),它使用一个简单的内部 Snakemake 管道将所有的.txt文件从输入目录复制到输出目录。

为了模拟真实情况,我们将创建两个单独的文件夹,一个包含包文件,另一个包含用户用来运行包的输入和配置文件。

包文件夹结构

这个结构将包含创建实际包的所有文件。根文件夹包含构建包所需的文件。snakepack 子文件夹包含实际的模块。snakepack 子文件夹中是 snakemake 文件夹,其中包含管道文件。

这种结构可以例如通过运行

这些文件一直是空的,直到我们以后把它们填满。

用户文件

这些文件夹包含将用于演示管道工作的文件。它们可以位于任何位置,但在本例中,它们将位于个人(~)文件夹中:

这种结构可以例如通过运行

第二步:锻造管子进行包装

图片来自 burst.shopify.com

让我们从在 Snakemake 文件夹中创建 snakemake 管道开始,看看它在没有包包装的情况下是否工作。因为我们希望我们的包的用户能够配置管道,所以我们将使用 Snakemake 的配置文件来定义输入和输出目录,并将它们传递给 Snakemake。

Snakefile

在我们的snakepack_files文件夹中(见上文),我们可以填写配置文件:

config.yaml

测试

现在可以通过命令行从snakemake文件夹运行该管道

第三步:打包

图片来自 burst.shopify.com

我们将围绕现有的 snakemake 管道创建一个包包装器,它允许我们创建一个包含包并可以分发的.whl文件。使用上述文件夹/文件结构填写这些文件:

copyfiles.py

因为我们希望能够在安装包之后从命令行调用管道,所以这个文件充当一个包装器,它接受命令行参数并使用它们来启动 snakemake 管道。一个优点是,我们可以控制用户设置哪些参数,以及我们自动固定或确定哪些参数。

在这种情况下,我们可以区分以下参数:

  • 用户定义的:配置文件的位置
  • 修正:snake 文件在包中的位置
  • 自动确定:可用 CPU 的数量

copyfiles.py

requirements.txt

该文件包含 Python 包的要求。在这种情况下,snakemake 是一个需求。我们还包括了曼巴,因为这优化了蛇制造的使用。

requirements.txt

当您稍后安装 snakepack 包时,这些需求将被自动下载和安装。

setup.py

setup.py文件将为 python 包工具提供如何创建包的指导。

setup.py

setup命令指示包构建器这是包 snakepack 的 0.0.1 版本。find_packages()调用确保 python 模块将包含在轮子中。install_requires定义了我们从requirements.txt读入的需求。最后,我们将include_package_data设置为True,并指向包含在包中的 snakemake 目录。

测试

一旦创建了上述文件,您就可以测试您的包是否可以在开发模式下安装,并且可以运行该模块。

这将运行管道并复制文件。之后删除复制的文件。

第四步:起跳!

图片来自 burst.shopify.com

如果第 3 步成功,您就可以构建实际的包了:

这将在名为dist/的新子文件夹中创建一个 wheel ( .whl)文件。这个轮子可以和别人分享。

测试

让我们试试它是否安装并运行:

结论

这篇文章使用了一个非常简单的 snakemake 管道来展示它可以通过包含在一个定制的 Python 包中来进行分发。如果您经常使用 Snakemake,并且希望轻松地共享和版本控制您的管道,那么您可以使用更复杂的管道来扩展所提供的框架,并根据您的需要进行定制。

来源

在野外潜泳——谷歌、英特尔、苹果和 IBM 的监管不力

原文:https://towardsdatascience.com/snorkel-in-the-wild-weak-supervision-at-google-intel-apple-and-ibm-2e0d77637ee0?source=collection_archive---------10-----------------------

行业笔记

工业中程序数据标记的综合。

雷蒙·克拉文斯Unsplash 上拍摄的照片

对未标记的数据感到沮丧和冷漠?尝试浮潜来治愈你的症状!浮潜是一种承诺给你应得的标签的技术。斯坦福大学在这一领域的研究导致了他们的浮潜人工智能初创公司,该公司在 2021 年 4 月筹集了 3500 万美元的 B 轮融资。

对于许多 ML 问题,有大量的数据,但很少被标注。例如,你从哪里获得一条推文是否提到两家公司的合并或者一个产品列表是否包含非法产品的标签?解决这一问题的传统方法是人工标记,这既昂贵又缓慢。一个更快、更可扩展、有时更高效的解决方案是使用通气管。TLDR 是浮潜让你用代码生成标签,代码比人快。

浮潜虽然刺激,但不是魔术。通过研究,我对这个行业如何使用浮潜、何时合适以及它的局限性有了一个平衡的看法。这篇文章综合了来自谷歌、苹果、英特尔和 IBM 的关于浮潜的论文,提供了这项技术的细致入微的观点。

我们会掩护

  1. 浮潜概述
  2. 在实践中生成标记函数
  3. 生成模式的语用考量
  4. 什么时候浮潜,什么时候给人贴标签

在高空浮潜

如果你已经知道通气管是如何工作的,可以跳过这一部分。

想象一下,你经常问你的三个朋友,汉娜,魏和贾森,预测一部电影是否会火,他们的猜测比机会更好。你问过他们复仇者联盟电影,寄生虫之类的。随着时间的推移,你会知道谁会投类似的票,每个人拒绝投票的频率,甚至每个人的准确度。你利用这一知识将你朋友的投票与加权平均相结合,得到一个比任何一个人都更好的估计。简而言之就是潜水。

图 1:通气管管道。作者改编自[1]。通气管的主要部分是标签功能和生成模型。

图 1 显示了带有两个关键概念(标记功能和生成模型)的通气管管道,并进行编号。浮潜发生在离线过程中,以标记训练数据,这些数据可用于为您的 ML 任务训练判别模型。

标签功能(LFs)

图 1 中显示的标记函数与上例中的 Hannah、Wei 和 Jason 相似。LFs 是段代码,当给定一个数据点时,它们可以输出一个估计的标签或者放弃。对于二进制分类,可能的输出是{-1,0,1},其中 0 表示放弃。

最简单的 LFs 匹配未标记输入数据中用户定义的模式,但更高级的 LFs 利用外部信号。例如,假设任务是识别称赞员工的客户电子邮件。LF 可以:

  1. 使用 Python 库来检测电子邮件正文是否包含人名。
  2. 如果姓名存在,请将该姓名与内部员工目录中的姓名进行匹配。
  3. 在名字的三个单词内寻找像“感谢”这样的短语。

如果三个条件都满足,输出 1 ,否则输出 0 弃权。LF 不会尽善尽美,但它为手头的任务提供了一些信号。因为 LFs 很吵,所以被称为“弱监管”的一种形式。

标注函数可能相关、冲突,并且覆盖数据集的不同部分。在将 LF 应用于每个未标记的数据点之后,输出是包含每个点的 LF 标记的标记矩阵。下一个组件,生成模型。负责将这些嘈杂的投票组合成一个标签。

生成模型

回想一下我们的电影例子,生成模型就是你,因为你综合了你朋友的数据。在没有基本事实标签的情况下,生成模型可以学习 LFs 的准确性和相关性,并且将它们组合以近似基本事实。输出是概率标签(在 0 和 1 之间),你可以用它来训练一个判别模型。如果下游模型支持“噪声感知”损失函数,您可以直接使用概率标签,或者您可以将标签阈值设置为 0 或 1。在[1]和[2]中有一大堆数学描述了生成模型如何以及为什么工作。

注意:通气管目前只适用于分类问题(二元和多类)。

等等,我不再需要标签了?

不,你仍然有。浮潜减少但并没有消除对高质量标记数据集的需求。通常,你需要一个开发集来生产 LFs,一个测试集来测试最终模型的性能。浮潜的主要优势是你的大型训练集不需要手动标记(假设你正在进行监督 ML)。

在实践中生成标记函数

写 LFs 并不总是容易的。这一节描述了不同的公司如何提出 LFs 来标记他们的数据集,这可以为你的问题提供思路。

使用组织资源的劳动力市场

图 2:图片来自[3]。谷歌 LFs 涉及的组织资源。

Google 的 LFs 大量使用组织资源,这些资源是来自 Google 其他服务或知识库的信号来源[3,4]。

谷歌如何使用组织资源的一些例子是:

  • 内部命名实体识别模型。对于分类内容是否提及名人的任务,LF 使用该模型来检测文本中是否存在“人”实体。如果没有“人”,输出写着“没有名人”的标签。
  • 汇总统计。对于内容审核任务,统计数据是用户因违反策略的行为而被报告的次数。
  • 可以操作文本和图像的主题模型。尽管过于粗粒度以至于无法检测到积极的类别,但是这些模型有助于标记消极的类别。

作者描述了两类组织资源:

  • Servable:作为特性对离线 LFs 和在线模型都可用。
  • 不可维护:仅适用于离线通气管管道。这些资源查询起来很慢或者很贵,就像内部知识图一样。

虽然不可观测信号仅用于标记,但它们提高了仅在可观测特征上训练的鉴别模型的性能。作者认为这是迁移学习的一种形式。在这些情况下,您可以使用昂贵的、不可服务的信号来改进您的模型,而不会使您的在线服务系统复杂化或变慢!

无代码 LFs

有时候,生成 LFs 需要一定程度的领域知识,但是那些有领域知识的人不知道如何编码。为了解决这个问题,一些公司开发了无代码接口来为 LFs 获取领域知识。

英特尔为产品分析师制作了一个基于电子表格的界面来表达规则[5]。任务是对一条推文是否提到与客户相关的业务场景进行分类。例如,客户是否与另一家公司建立了合作关系。

图 3:来自[5]的表格。领域专家表达 LF 规则的基于电子表格的界面。

在如图 3 所示的界面中,领域专家输入关键字或正则表达式以及“极性”,表明它是支持还是反对业务场景。在上面的例子中,“伙伴关系”和“高兴”都是伙伴关系场景的标志,但是看到“几个月前”表示负面的标签。该接口捕获领域知识,开发人员可以选择如何在 LFs 中表达这些知识。

为了给聊天机器人训练标注数据, IBM 构建了一个搜索界面,让用户找到符合某种意图的聊天日志[7]。构建聊天机器人涉及到检测用户的意图,比如他们是否在询问账单、试图安排约会等等。

图 4:图片来自[7]。搜索界面,查找属于某个用户意图的聊天记录。

IBM 的系统提示用户查询与“schedule”这样的意图相匹配的日志。用户输入类似“安排会议时间”的查询。系统将前 N 个结果(设置为 100)定义为邻域,但仅向用户显示 k 个结果(设置为 10)。这 10 个结果是从邻域的顶部、中部和底部采样的。然后,要求用户标记 10 个结果,每个结果是否属于意图。如果超过 60%的样本具有相同的标签,则邻域中的所有样本(所有 100 个样本)都被赋予该标签。每个邻域被认为是一个 LF。这种方法以更多噪声为代价增加了弱标签的数量。

苹果开发了 ui 来帮助工程师识别需要更多或更好弱标签的数据片段[6]。他们所有的任务都是基于文本的。不幸的是,这篇论文并没有详细介绍 ui 或者特定的 ML 任务。

请注意,这三个无代码 LF 生成示例是对文本数据进行操作的。与图像或视频等其他非结构化数据相比,设计操作文本的高级界面更容易。这类接口仍然是一个活跃的研究领域。

阶级不平衡

类别不平衡是考虑使用通气管的一个很好的动机,因为手动标记随机样本是昂贵的。如果你的正类占数据的 1%,标注 100 个随机样本只会给你一个正的标签!

一些论文讨论了类不平衡问题,其中正类的范围从数据集的 0.05%到约 10%。在某些情况下,通气管适用于处理不平衡。

对于特定的问题,编写以高精度识别正类的 LFs 比负类更容易。举个例子,识别明显形式的仇恨言论并不困难(包含亵渎、提及某些话题等)。).但是,没有脏话就意味着内容不是仇恨言论吗?你如何发现边缘病例?

如果没有标记负类和边界示例的 LFs,您将只能用明显的正示例标记数据集的一小部分。您的最终模型可能具有高精度,但是将具有低召回率,并且可能过度适合某些类型的正类。

标签传播

Google 通过使用一种叫做标签传播的图形算法来解决这个问题。他们有大量人类标记的数据和未标记的数据,并使用该技术将信息从标记的数据转移到未标记的数据[4]——如图 5 所示。

图 5:图片来自[8]。标签传播的可视化表示。

在论文[4]中,每个未标记的数据点得到一个分数,该分数是其邻居的标记的加权组合。权重是未标记点和标记点之间的相似性,其中相似性是 Jaccard 相似性或距离度量。如果分数超过预定阈值,LFs 则输出某个标签。这项技术有助于创建高召回率的 LFs 来补充高精度的 LFs。

对于某些任务,标签传播输出上的 LFs 显著改进了生成模型的标签。标签传播是不可服务资源的一个例子,它可以提高终端模型的性能。

合成数据集

英特尔为每个类别生成了平衡的合成数据集,以处理其多类别问题的类别不平衡,正类别的比率为 0.05%。对于 C 类,他们认为属于“非 C”的例子是反面例子,以及不属于任何类的例子。

自动低频生成

编写 LFs 可能需要领域知识,但是如果您的工程师没有这方面的专业知识怎么办?来自工程师的一个可能的答案是“让我们用代码创建 LFs”,这就是他们在 Google 的第二篇论文中所做的[4]。

他们的大多数特征是分类的,并且他们使用频繁模式挖掘来识别在一个类中比另一个类中出现更频繁的特征组合。模式是特征值(X 和 Y)的合取,类似于领域专家倾向于生成的东西。只有当一个模式在开发集上满足一定的精度和召回阈值时,它才被用于 LF。模式挖掘发生在单个特征而不是多个特征上,以减少 LFs 之间的相关性。

这种技术减少了部署通气管的时间,因为它不需要领域专家手写 LFs。此外,没有专业领域知识的工程师也可以部署管道。

低频考虑因素

一般来说,您的 LFs 集应该具有高精度、高召回率和高覆盖率(覆盖大部分数据)。我还没有找到最低精度、召回率和覆盖率的具体阈值,这可能取决于您的数据集。不管怎样,如果质量很重要。生成模型估计 LFs 的准确性,Google 使用这些估计来修复或删除低质量的 LFs。

假设使用高质量的 LFs,越来越多种类繁多的 LFs 通常会带来更好的性能。

在创建 LFs 时,请考虑以下问题:

  1. 你可以在你的劳动力市场中使用什么样的组织/外部资源?考虑一下那些来自 HuggingFace 或亚马逊 Sagemaker 的预训练模特。
  2. 你能开发一个让非编码者指定 LFs 的接口吗?
  3. 您的 LFs 是否遗漏了数据集的重要部分,如某些类型的正类、负类和边界情况?

生成模式的语用考量

生成式模型什么时候值得?

LFs 是强大的,因为它们可以给我们的数据加上弱标签。但是,什么时候我们需要生成模型来组合它们呢?新的建模管道产生了维护开销,我们必须评估成本和收益。

您应该将生成模型的结果与弱标签的简单平均值或弱标签的多数投票进行比较(图 6)。

图 6:根据简单基线评估通气管标签。作者图解。

来自浮潜 Drybell 论文[3]的表格比较了来自创成式模型的标签与来自弱标签的未加权平均值的标签(图 7)。他们在每组标签上训练了一个判别模型,并将其性能与在人类创建的标签上训练的模型进行了比较。单元格中的 100%表示与根据手动标记的数据训练的模型性能相当。

图 7:来自[3]的表格。比较生成模型标签上的区别模型性能与弱标签的平均值。分数是相对于在手工标记的数据集上训练的模型而言的。

生成模型是主题分类任务的明显胜利。但是,对于产品分类任务,该模型仅提供了对“同等权重”的轻微提升。

原始的浮潜论文[1]讨论了随着标签密度的增加,生成模型相对于多数投票的性能。标签密度是分配给每个数据点的弱标签的平均数(记住,LFs 可以弃权)。生成模型似乎为中等标签密度的体系提供了优势,并且类似于针对低和高标签密度的多数投票。原因是,当大多数示例只有一个弱标签(低标签密度)时,生成模型无法对标签重新加权。当存在高标签密度,并且你的 LFs 比机会更好时,随着标签密度的增加,多数投票快速收敛到最优解。

一个简单的平均值可能就足够解决你的问题了,你可能不需要生成模型。

生成模型的可伸缩性如何?

原始版本的通气管使用了一种不太可扩展的采样方法。当前的开源版本使用了一种矩阵补全方法,这种方法被认为更具可扩展性,因为它随着逻辑函数的数量而不是数据集的大小而扩展[9]。

Google 在 Tensorflow 中实现了一个比原始版本更高效的版本,并且可以以分布式方式运行。如果你有非常大的标签矩阵,看看开源版本是否能满足你的需求。如果没有,请参见[3]了解更多关于 Tensorflow 实现的详细信息。

我们可以使用生成模型来服务吗?

生成模型输出概率。什么时候我们应该使用它作为生产模型,而不是根据概率标签训练的判别模型?

以下是一些注意事项:

  • 生成模型和判别模型在测试集上表现更好吗?
  • 有没有你所有的 LFs 都弃权的例子?在这些情况下,生成模型也将弃权,这可能是不可接受的。判别模型可以学习输入数据和概率标签之间的相关性-提供比生成模型更多的覆盖范围。
  • 生成模型对 LFs 的输出进行操作。你所有的 LFs 在生产中都是可服务的吗?它们满足您的服务 SLA 吗?
  • 如果您有调用其他服务、执行复杂操作等的复杂 LFs。,将其置于在线生产环境中的部署成本是多少?也许更简单的方法是将 LFs 放在离线管道中,并部署一个可以在易于计算的特性上运行的模型。

什么时候浮潜,什么时候给人贴标签?

通常情况下,浮潜生成的标签与人工生成的标签相比较,浮潜被认为更好,因为它:

  • 伸缩性更好。代码比人快,有时候人类的标注管道慢到不符合解决方案要求。
  • 产生更好的模型(有时)。
  • 更敏捷。可以响应标签定义的变化。例如,业务决定将电动滑板车纳入“运动器材”类别,产品分类模型需要更新标签。有了通气管,开发人员可以修改现有的 LFs 并触发通气管管道来获得更新的标签。有了人的标签,适应会更慢。

这些都是选择通气管的好理由,但重要的是要知道通常有一个“交叉点”——人类标签的例子数量,在这个数量上,用人类标签训练的模型优于用通气管标签训练的模型。

图 7:来自[4]的表格,作者添加的绿色圆圈。五种不同分类任务的交叉点。

从图 7 可以看出,不同分类任务的交叉点从 4K 到 750K 人工生成的标签不等[4]。没有收集到足够的人类标签,你不会知道交叉点,但尝试找出答案是有用的。可能门槛低,人标是比浮潜更好的解决方案。或者,也许你选择使用通气管,直到你有足够的标签到达交叉点。

在英特尔的论文[5]中,在三分之二的分类任务中,基于规则加上人工标记的管道比 plus 做得更好。作者说,人在回路管道是不可行的,因为它强加了 10 天的评分潜伏期,而且维护费用昂贵。因此,他们将通气管模型与全监督模型进行比较。无论如何,速度较慢但性能更好的人工标记管道可能更好地满足您的业务需求。

最后,对于标签较差的设置,有比通气管和人工注释器更多的选择。主动学习和半监督学习是可以探索的其他方法。

最后

高质量的训练数据是至关重要的资产,因为对 ML 的另外两个要求,计算资源和建模工具,正在商品化。尤金·卫的这篇出色的文章认为,抖音的成功是因为其高质量的标签,而不是它的模型。

潜泳可以帮助你有计划地提高你的关键资产,但也有警告。首先,您仍然需要有代表性的、不带偏见的带有“黄金”标签的开发和测试数据集。为了帮助评估浮潜是否适合你:

  • 确定 LF 方法对您的问题是否有意义。参见上面的 LF 注意事项。
  • 将创成式模型标签与更简单的基线进行比较。
  • 决定你是否需要一个有区别的终端模型。
  • 最后,将通气管管道与带有人工贴标机的简单管道进行比较。

参考

[1] Ratner,Alexander,等,“浮潜:在弱监督下的快速训练数据创建”VLDB 基金会会议录,第 11 卷,第 3 期,2017 年,第 269-82 页。Crossref,doi:10.14778/3157794.3157797。

[2]克里斯托弗·雷;丹尼尔·塞尔萨姆;吴,森;德·萨·克里斯托弗;亚历山大·拉特纳(2016 年 5 月 25 日)。“数据编程:快速创建大型训练集”。arXiv:1605.07723 v3

[3]巴赫、斯蒂芬·h .等人,《通气管干铃》2019 数据管理国际会议论文集,2019。交叉引用,doi:10.1145/32994036。

[4] Suri,Sahaana 等人,“利用组织资源使模型适应新的数据形式。”VLDB 基金会会议录,第 13 卷,第 12 期,2020 年,第 3396–410 页。Crossref ,doi:10.14778/341588 . 34153535346

[5] Bringer,Eran 等,“鱼鹰:没有代码的不平衡提取问题的弱监督”第三届端到端机器学习数据管理国际研讨会论文集— DEEM'19 ,2019。Crossref ,doi:10.1145/33232533246

[6] Christopher Ré,Feng Niu,Pallavi Gudipati,Charles Srisuwananukorn:《Overton:一个用于监控和改进机器学习产品的数据系统》,2019 年;http://arxiv.org/abs/1909.05372

[7] Mallinar,Neil,等人,“在弱监督下引导对话代理”人工智能 AAAI 会议论文集,2019 年第 33 卷,第 9528–33 页。交叉引用,doi:10.1609/aaai . v33i 01.33019528。

[8]李约瑟,马克。" Neo4j 中的图形算法:标签传播."Dzone.Com,2019 年 3 月 8 日,dzone . com/articles/graph-algorithms-in-neo4j-label-propagation。

[9]潜水队。"介绍新的通气管。"通气管,2019 年 8 月 14 日,www . snoop . org/blog/hello-world-v-0-9 #升级-贴标-管道。

雪花 S3 集成

原文:https://towardsdatascience.com/snowflake-s3-integration-911bd4b03739?source=collection_archive---------16-----------------------

凯利·西克玛在 Unsplash 上的照片

访问存储在 S3 的文件,作为雪花中的常规表

雪花集成对象使我们能够从雪花与外部系统连接。在我的上一篇博客中,我们讲述了如何创建 API 集成来调用 AWS Lambda。在这个博客中,我们将看到如何集成 AWS S3,以便我们可以访问存储的数据,并在雪花中查询它。

作者图片

实施细节

AWS IAM 角色

为了让雪花能够与我们的 AWS 帐户对话,我们需要添加交叉帐户角色。该角色将由雪花帐户中的 IAM 身份承担,并在我们的 AWS 帐户中执行操作。

  • 在您的 AWS 帐户中创建一个跨帐户 IAM 角色。放一个虚拟账户,我们稍后会更新信任政策。
  • 将一个内联策略附加到上面创建的角色。
Version: '2012-10-17'
Statement:
- Effect: Allow
  Action:
  - s3:GetObject
  - s3:GetObjectVersion
  Resource: arn:aws:s3:::my-snowflake-data-bucket/dev/gold/*
- Effect: Allow
  Action: s3:ListBucket
  Resource: arn:aws:s3:::my-snowflake-data-bucket

上述策略允许角色在前缀下访问 S3 存储桶。出于安全考虑,只添加雪花所需的最低访问权限。

雪花集成对象

  • 创建一个存储集成对象
create or replace storage integration my_s3_int_01
  type = external_stage
  storage_provider = s3
  enabled = true
  storage_aws_role_arn = '<IAM_ROLE_ARN>'
  storage_allowed_locations = ('s3://my-snowflake-data-bucket/dev/gold/');

上述命令在雪花中运行时会创建一个 S3 集成对象。此对象在雪花帐户中创建 IAM 标识。AWS 帐户中的所有 API 操作将由该用户通过承担我们在 storage_aws_role_arn 中提到的角色来执行。

如上所述,集成对象创建用户和外部身份。我们需要这些来增加 AWS 和雪花帐户之间的信任关系。为此,我们描述刚刚创建的集成对象,并记下存储 _ AWS _ IAM _ 用户 _ARN & 存储 _ AWS _ 外部 _ID 的值

describe integration my_s3_int_01;

输出:

作者图片

一旦我们有了用户和外部身份,我们就更新在第一步中创建的角色的信任关系。在条件下,在主体&存储 _ AWS _ 外部 _ID 中添加存储 _ AWS _ IAM _ 用户 _ARN 的值

Version: '2012-10-17'
Statement:
- Effect: Allow
  Principal:
    AWS: "<STORAGE_AWS_IAM_USER_ARN>"
  Action: sts:AssumeRole
  Condition:
    ForAnyValue:StringLike:
      sts:ExternalId:
      - "<STORAGE_AWS_EXTERNAL_ID>"

我们需要将存储在 S3 存储桶中的文件中的任何特殊格式告诉雪花。这是唯一需要我们雪花提供的文件格式是不够的。

  • (可选)创建文件格式
CREATE OR REPLACE FILE FORMAT my_csv_format_01
  TYPE = CSV
  FIELD_OPTIONALLY_ENCLOSED_BY='"'

雪花外部阶段

一旦我们完成了访问和格式设置,我们就创建了 stage。Stage 存储外部文件的元数据,在我们的例子中是 s3。这用于查找需要加载到雪花表格中的数据。我们已经创建了一个简单的阶段,您也可以看看其他选项,如加密。

  • 创建外部阶段
create  or replace stage my_s3_stage_01
  storage_integration = my_s3_int_01
  url = 's3://my-snowflake-data-bucket/dev/gold/'
  file_format = my_csv_format_01;

上面的命令创建了雪花和 S3 文件前缀之间的映射。它还告诉雪花使用一种文件格式,适合存储在 S3 的数据。

  • 创建外部表
create or replace external table ext_ccfinput_test_01
  with location = @my_s3_stage_01/
  auto_refresh = true
  file_format = (type = CSV)
  pattern='.*ganalytics.*[.]csv';describe stage my_s3_stage_01;

类似于 stage 存储在哪里可以找到数据的信息,外部表存储文件级元数据,比如文件名、版本标识符和相关属性。这增加了一个抽象,允许我们像在雪花中一样查询数据。

检查结果

现在我们准备在雪花中查询来自 S3 的数据。我们在创建的表上发出 select 语句。

select t.$1, t.$2, t.$3, t.$4, t.$5, t.$6 from @my_s3_stage_01 as t;

瞧,我们得到了与 s3 数据文件内容一致的结果。

在这篇博客中,我们看到了如何从 snowflake 访问和查询存储在 S3 的数据。

雪花快乐!!

那么……人工智能到底是如何被用来探测新冠肺炎的呢?

原文:https://towardsdatascience.com/so-how-exactly-is-ai-being-used-to-detect-covid-19-d0dff155881f?source=collection_archive---------26-----------------------

D 揭秘深度学习背后的数学&卷积神经网络(CNN)

人工智能已经成为近年来热烈讨论的话题,它有可能彻底改变我们生活的方方面面,从医疗保健、教育,甚至加强边境安全。当谈到人工智能时,我们许多人都会想到机器学习,这是人工智能的一个子集,它允许计算机系统随着经验和数据的增加而改进【1】。出现了许多关于使用机器学习来帮助对抗新冠肺炎的报告,包括从业者如何通过分析胸部 x 光片使用深度学习来检测和诊断新冠肺炎。

深度学习如何用于使用胸部 X 射线诊断新冠肺炎的概述(图片由作者提供)

我相信我们很多人都对这种算法背后的数学很好奇——数学是如何影响这些算法的,以及如何操纵数学系统产生如此惊人的结果,堪比探测新冠肺炎?

虽然围绕深度学习的数学术语,如“梯度下降”、“反向传播”、“矩阵乘法”等乍一看可能听起来很吓人,但不要担心!我们将通过首先解释每个深度学习任务的目标并引入包括权重初始化、激活函数、通过梯度下降和反向传播最小化损失以允许神经网络学习的概念来分解这些术语。

然后,我们将涵盖卷积神经网络(CNN)的细节——深入研究各个层(卷积层、池层和全连接层),并将它们结合在一起,最终导致成功的新冠肺炎诊断。

如果数学符号让你害怕,不要担心。你不需要理解每一行是什么意思;只要跟着文本走,希望在这篇文章结束时,你会更好地了解这些看似不同的数学概念是如何结合在一起驱动深度学习并实现新冠肺炎的检测的!

AI,ML & DL 的比较。图片取自https://commons.wikimedia.org/wiki/File:AI-ML-DL.svg

深度学习的数学概述

深度学习的目标是近似某个函数 f* 其中y = f (X)将一个输入 X 映射到一个类别 y 。我们通常将输入定义为向量x∈ℝ,其中向量的每个条目 xᵢ 代表一个特定的特征。例如,图像的特征通常是图像中像素的值。训练时,神经网络定义了一个映射y = f(XW)并且学习参数 W 的值,该值导致 f* 的最佳近似。输出 y 将为 0 或 1,代表患者是新冠肺炎阴性(0)还是阳性(1), y ∈ {0,1}。*

为了学习这种映射,我们首先通过神经元传播信息,神经元是神经网络的基本构件。这些输入 x₁x₂ ,… xₙ 中的每一个都将用相应的权重 w₁w₂ ,… wₙ 其中w∈ℝXW 的点积然后通过非线性激活函数 g ,产生预测输出 ŷ 。除了 X 之外,还有一个偏置项 w₀ ,它允许我们将激活功能向左或向右移动,而与输入无关。

把这些放在一起,

作者图片

一个等价的概括表示是

作者图片

因此,深度学习的目标是找到权重的最优集合,这将产生映射y = f(X;W ) 最接近 f* 。

投入和权重产生产出 ŷ.图片取自https://cs.gmu.edu/~kosecka/cs747/03_linear.pdf

激活功能

我们应用非线性激活函数,因为xw不在 0 和 1 之间。因此,我们使用 sigmoid 函数将实数转换为 0 到 1 之间的标量输出。缩放后, y 将被定义为:

作者图片

乙状结肠激活功能。图片取自https://commons . wikimedia . org/wiki/File:Sigmoid-function-2 . SVG

激活函数的另一个目的是将非线性引入网络。这一点很重要,因为在现实生活中,我们遇到的大多数数据都是非线性的,也就是说,它们不能用一条直线分开。因此,激活函数允许 Xy 之间的非线性映射,其更好地逼近 f 。*

激活函数将非线性引入网络。亚历山大·阿米尼和艾娃·索莱马尼。S191: 深度学习介绍

一些常用的激活函数包括 sigmoid、tanh、ReLU,它们都用于将非线性引入网络。

常用的激活函数。图片取自 Sze,Vivienne 等人的《深度神经网络的有效处理:教程和调查》IEEE 会议录 105.12(2017):2295–2329。

最小化损失

在为特定输入 x ⁽ᶦ⁾和**t30】wt32】计算出 y 之后,如何找到最优映射 f ?这就是“学习”部分的由来。首先,当网络出错时,我们必须告诉它。这是通过量化误差(称为损失)来完成的,通过采用预测 y 并将其与真实答案f (X)进行比较。经验损失,J(W),测量由 n 个样本组成的整个数据集的平均损失。***

作者图片

因此,“学习”的目标是找到最佳权重 W ,这将产生最小的损失,从而得到最接近的 f

作者图片

梯度下降

在训练开始时,我们首先随机初始化权重。为了向最小损失移动,我们计算相对于每个重量的损失梯度,以理解最大上升的方向。有了这些信息,我们就可以向梯度的相反方向迈出一步,向损耗较低的点移动。然后我们重复这个过程,直到我们收敛到局部最小值。

梯度下降算法。亚历山大·阿米尼和艾娃·索莱马尼
麻省理工学院。S191: 深度学习介绍

图解梯度下降。图片取自https://commons . wikimedia . org/wiki/File:Gradient _ descent . gif

实际上,神经网络将包括一个以上的神经元。正是多个神经元的组合使得神经网络如此强大。

那么我们如何通过多个神经元计算梯度呢?通过使用链式法则的反向传播!

反向传播

以两个神经元依次排列的神经网络为例, xz₁ 分别对应权重 w₁w₂

两层神经网络。亚历山大·阿米尼和艾娃·索莱马尼
麻省理工学院 6。S191: 深度学习介绍

如果我们稍微改变 w₂ ,就会改变 ŷ 的输出。为了计算梯度,我们应用链式法则:

作者图片

如果我们改为改变 w₁,那么我们将执行以下操作:

作者图片

对于更深层次的神经网络,我们随后从输出到输入连续应用链式法则来计算梯度,并量化权重的变化如何影响损失。

计算完梯度后,我们如何决定在梯度的反方向上走多大的一步?那是由学习率 n 决定的。设置学习率在训练神经网络中是极其重要的,因为小的学习率会收敛得非常慢,并且可能陷入错误的局部极小值,而大的学习率可能过冲并且永远不会收敛。因此,我们不使用固定的学习速率,而是使用自适应学习速率,该自适应学习速率根据包括学习进行得多快、特定权重的大小等因素来“适应”损失情况。

学习率对训练的影响。图片摘自 Krittanawong,Chayakrit 等人的《心血管医学的深度学习:实用入门》。欧洲心脏杂志40.25(2019):2058–2073。

卷积神经网络

我们现在已经完成了对神经网络中如何进行训练的概述。接下来,让我们将注意力转移到我们如何准确地使用深度学习从 X 射线胸部扫描中诊断新冠肺炎。

当处理像 X 射线扫描这样的图像数据时,将输入的 X 视为大小为 b x h 的矩阵会更好,因为这就是一幅图像如何呈现给计算机的——作为一个二维数字矩阵。如果我们将 X 作为像素值的向量(一维)传递,我们将丢失原始图片中所有可用的空间信息。因此,我们将神经网络的原始架构调整为卷积神经网络(CNN ),其保留空间结构作为输入。这是通过将输入图像的小块连接到每个神经元,使得每个神经元仅连接到输入的一个区域来实现的。输入图像经历几个卷积层,这些卷积层首先包括卷积运算,接着是激活函数,最后是汇集运算,所有这些将在下面详细描述。

卷积神经网络概述(图片由作者提供)

卷积层

首先,将一个内核滑过尺寸为 b x h 的输入图像。内核是大小为 bₖ x hₖ (其中bₖt22】b 和hₖ<t26】h 的矩阵,其中包含权重w,从而大小为 bₖ x hₖ 的内核将具有bₖx 我们将这个相同的内核应用到输入中的每个 bₖ x hₖ 面片,从左上角开始,通过滑动窗口移动到下一个面片。**

2x2 内核(粉红色)滑过 4x4 输入图像(浅绿色),产生 3x3 输出图像(深绿色)。图片取自https://commons . wikimedia . org/wiki/File:Valid-padding-convolution . gif

内核从图像中提取特征,这些特征可以通知网络关于患者的诊断。这些特征是通过卷积运算提取的,卷积运算包括将滤波器中的每个像素与输入图像的 bₖ x hₖ 片进行逐元素相乘。然后,我们对每个滑动动作的所有值求和,以获得该层的输出。经过卷积的图像被称为特征图。

与大小为 4×3 的输入图像和大小为 2×2 的内核进行卷积运算,以产生大小为 3×2 的特征图。图片来自印第安纳州古德费勒、纽约州本吉奥和亚利桑那州库维尔(2016)。深度学习。麻省理工出版社。

与大小为 6x6 的输入图像和大小为 3x3 的内核进行卷积运算,以产生大小为 6x6 的特征图。图片取自https://commons . wikimedia . org/wiki/File:2D _ 卷积 _ 动画. gif

不同的核包含不同的权重集,因此使用多个核,可以从图像中提取不同的特征。如果将 k 个核应用于图像,那么得到的特征图的大小将是 b x h x k 。这些特征可以包括锐边、曲线、纹理等等。神经网络之所以如此强大,是因为这些特征不是人类硬编码的。相反,通过反向传播的训练过程来减少损失,神经网络找到每个核的最佳权重,并因此提取对诊断患者最重要的特征。

汇集层

在卷积操作之后,特征图然后通过激活函数。利用非线性输出,特征图然后经历汇集,这减小了图像的大小。这背后的动机是使神经网络能够学习对输入的小平移不变的特征。这一点很重要,因为不是所有的 X 射线扫描都可以在相同的方向上进行。拍摄 X 射线时患者位置的微小差异,甚至不同扫描仪之间存在的微小差异都可能导致扫描结果略有不同。因此,池允许神经网络对这些微小的差异保持不变,从而允许它更适用于更大范围的扫描。这是通过只保留图像特定部分中的最大值来实现的,称为 maxpooling。以尺寸为 4×4 的输入图像为例。在图像的每个 2x2 小块中,最大值(粗体)将构成新的特征图,如下所示:

作者图片

这允许不变性,因为如果图像被稍微平移,最大值将保持近似恒定。

例如,在下图中,尽管所有像素都向右移动了一个单位,使得新图像与初始图像相差四个值,但 maxpooling 后生成的图层仅相差一个值(斜体)。

作者图片

全连接层

在执行 maxpooling 之后,最后一个阶段是将特征图连接到由多个神经元组成的全连接层。卷积层(卷积、激活、汇集)提供从图像中提取的有意义且不变的特征,最终的全连接层学习非线性函数,该函数将提取的特征映射到 y ,即患者的诊断。经过多个卷积层后的最终特征图,比如大小为 b1 x h1 x k ,将被展平为一维特征向量I∈ℝˡˣʰˡˣ,类似于 X I 会有自己的一组对应的权重,我们计算 IW 的点积,后面跟着一个非线性激活函数,产生最终的预测 y**

卷积神经网络的架构(图片由作者提供)

通过这种改进的架构,模型然后利用梯度下降进行学习,并且通过该过程,我们将有希望获得以高精度对 X 射线扫描进行分类的模型。

结论

所以你有它!我已经为你提供了这些新冠肺炎分类系统背后的数学的高层次概述。希望你已经学了一点,并会继续深入探索这些概念!

在此期间,让我们都保持安全和健康~

参考

【1】古德费勒,I .,本吉奥,y .&库维尔,A. (2016)。深度学习。麻省理工出版社。

<#_ftnref2>【陈海燕】【金东光】&【林】【2020】。深度学习结核病分类器对新冠肺炎胸片的可推广性:旧算法的新招?。胸部影像杂志35 (4),W102-W104。

所以现在你已经有了你喜欢的可穿戴设备。但是你如何从它的数据中获得全部价值呢?

原文:https://towardsdatascience.com/so-now-youve-got-your-fancy-wearable-device-but-how-do-you-get-full-value-from-its-data-e98b27f6647c?source=collection_archive---------20-----------------------

健康的未来是关于你和你的数据:捕捉健康和健康数据的智能传感器就在我们周围。它们不仅日夜跟踪我们的心率,还能给我们提供最准确的睡眠数据。这些数据有助于发现你什么时候感觉不舒服,甚至在你感觉不舒服之前。听起来很棒!但是你怎么能接触到它呢?从中获取更多价值的途径有哪些?

基于订阅的数据模型的兴起

随着第三代智能戒指的发布,Oura 推出了每月订阅计划,这是一个付费选项,可以获得见解、个性化建议、指导音频会议、科学支持的教育内容等。

这种向订阅模式的转变似乎已经成为可穿戴设备市场的一种常态:Fitbit Premium 和 Whoop 已经推出了额外优惠的月度订阅。为什么设备用户要为这样的个性化见解付费?为什么我们不能免费得到它们?

付费订阅存在的一个可能原因是,此类可穿戴设备制造商的投资者和风投对 MRR(每月经常性收入)着迷,这应该会推高公司的估值。用户似乎并不介意为此付费。

我们每个人都会产生如此多的数据。我们如何从中受益?图片来源:Rawpixel.com

大数据缺失的 V:价值

以下四个概念传统上用于提供对大数据的技术理解:量、准确性、多样性和速度(所谓的“4-v”)。卷是指数据量。准确性描述了数据的准确性。多样性是指用于构建特定数据模型的数据集的数量。速度是指获取数据和数据在数据库中流动的速度。

这四个 v 确实有助于我们了解大数据企业环境的复杂性。像谷歌或脸书这样的大型实体和各种研究机构可能需要收集数百万用户的大量数据,以确定特定的模式或趋势。这些庞大而复杂的数据集有助于为团体级或社会级应用创造价值。

为我个人创造价值怎么样?

当涉及到我的行为时,我可能对其他人的行为模式不感兴趣,就像我对自己的日常事务感兴趣一样。对于我的个人健康和幸福来说,这可能更是如此。

购买智能手表或智能戒指可能是人们开始更好地了解日常活动、心率和睡眠的第一步。可穿戴设备制造商能够迅速发现市场机遇:因此,我们支付了订阅计划,以从我的数据中释放价值。

为什么我们需要为我们的数据付费?

您可能想知道…为什么我要为您刚刚购买的智能设备所产生的数据的洞察力付费?我不是拥有这些数据的人吗?

劳动理论

几个世纪以来,哲学家们一直在争论财产权的正当性。例如,约翰·洛克认为你的劳动成果应该属于你。所以,如果你在土壤里播种,浇水,你应该有权收获。或者,如果你选择了原料——比如生金和蓝宝石——并创造了一种新的权利,你的劳动使你有权成为那枚戒指的主人。

人格理论

另一种“性格理论”支持这样一种观点,即我们应该是我们数据的主人,因为我们(我们的性格)和我们产生的数据之间有一种情感上的紧密联系。

激励(成本和收益)方法

所谓的“激励”理论假设我们生活在由拥有不同能力、天赋和资源的个体组成的社区中。因此,至关重要的是要有一个奖励个人创造力的激励体系。因此,创造新的有社会价值的产品的人应该知道他们的独创性会得到回报。作为回报,他们可以从自己的作品中获得一些所有权。这就是版权和专利权是如何出现的:一部小说或某种化学化合物的创造者获得一个有限的时间垄断权,以决定这种小说或化学物质将如何被使用,谁可以获得,以及在什么条件下(报酬)。

我们可以看到类似的逻辑是如何应用于每月支付墙背后的数据的:设备制造商想出了一个想法,即设备中的哪些见解可能是你最感兴趣的,并创建了一个应用程序来将它们传递给你。如果你想要它,你必须为这种访问支付“公平”的补偿。

根据 GDPR 和 CCPA,您的数据访问权限如何?

根据欧盟的 GDPR 和加州的 CCPA,消费者对他们的数据拥有一些权利。更具体地说,消费者可以要求企业向他们提供其数据的副本,甚至要求删除关于消费者的所有数据。此外,在加州,消费者可以要求企业不要出售他们的个人数据。

作为消费者,你可以要求公司向你发送个人数据的副本。然而,在实践中,这一过程远非用户友好且效率不高。一般来说,企业可能需要长达 30 天的时间来满足这一要求(尽管许多数据巨头已经知道如何在几秒或几小时内做到这一点)。如果你幸运的话,你会得到一种没有多大意义的数据格式,除非你接受过一些软件开发人员的教育。

事实上,最近的一些研究表明,企业并不真正关心遵守这种数据可移植性要求,因为没有对这种违反行为实施制裁。此外,缺乏行业范围的技术标准来使这种数据请求统一和方便用户。

因此,你必须支付每月订阅费才能从智能可穿戴设备中获得一些见解,这一事实可以被视为一种市场驱动的解决方案,以平衡设备制造商的投资和消费者对个性化见解的预期意愿。

可穿戴设备数据市场的下一个重大转变:整合多个来源的数据

如果你有多台智能设备呢?如果你家里有一个 iWatch、Oura Ring 和其他智能传感器,如 Fitbit 秤,会怎么样?有没有办法把所有这些设备的数据结合起来?

其他数据源呢,比如你在谷歌地图上的位置数据或者你的信用卡支付记录?你最喜欢的平台上的其他在线历史记录呢,比如网飞或 Twitch?公共数据呢(天气预报、高速公路交通、IMBD)?

结合不同来源的数据将是可穿戴设备市场最大的创新突破。Rawpixel.com

我们看到可穿戴设备的精确度正变得越来越精确。各种知名品牌和市场新进入者报告称设备精确度达到 95%以上,这样的精确度水平可以使他们有资格与医疗设备相提并论。

这种智能健康和健康跟踪设备的制造商看到的最大机会是结合不同来源的数据。这可以识别影响某些健康状况的周围因素:例如,什么导致体温升高?血液中的葡萄糖水平和一个人的日常活动有什么联系吗?

为了向个人消费者提供易于使用、真正强大的应用程序,帮助他们更好地了解个人的日常健康和健康,硬件制造商需要解决许多技术问题。必须有一个数据层,允许统一各种独立设备和传感器捕获的数据。显然,这不是一家公司能够完成的任务;相反,这是一个生态系统。

许多公司和风投看到了这种用户持有数据的机会,因此我们可以预计,未来几年可穿戴设备领域将出现一些令人兴奋的事情。

那么,成为一名数据科学家意味着什么?

原文:https://towardsdatascience.com/so-what-does-being-a-data-scientist-mean-737e66c74e03?source=collection_archive---------30-----------------------

每个数据科学家在每个项目中要完成的 6 项基本任务。

本·怀特在 Unsplash 上的照片

当你考虑一个新的职业时,你通常会做很多关于职业道路的研究,以及进入这个职业并取得成功需要什么。在生活中,有清晰直接的职业道路。你知道你应该学什么,你需要练习什么,一旦你找到工作你将做什么。例如,如果你想成为一名数学老师,你需要提高你的数学和沟通技巧。

你有一个清晰的路线图,你需要做什么来达到你的最终目标。这种清晰会对你需要投入多少时间和精力来获得成功的职业生涯产生很大的影响。也就是说,获得一个清晰的、一步一步的特定职业道路路线图并不总是容易的。当你考虑的职业道路是在技术领域时,这一点尤其准确。

总的来说,技术领域——尤其是数据科学——非常广阔,有许多差异很大的部门和子领域。这就是为什么在科技领域导航不像在其他领域导航那么简单。但是今天,让我们把重点放在作为职业道路的数据科学上。数据科学是一个总括术语,通常用于描述与数据有关的各种职业道路。

但是,在数据科学中,你可以从事不同的领域和专业。由于这种多样性,试图清楚地了解作为一名数据科学家实际需要做些什么常常令人困惑。我和其他数据科学家被问及“那么,作为一名数据科学家,你实际上在做什么?”的次数可以证明这一点。

在本文中,我将尝试解释成为一名数据科学家的一些方法。基本上,我将回顾一下大多数数据科学家日常工作中的一些任务。

№1:收集数据

在每个数据科学项目的开始,数据科学家需要收集和收集数据,通常来自不同的资源-来自网络或数据库-以分析和应用算法。有时,数据科学家不会自己收集数据,但会从客户或公司那里准备好数据以供分析。这个过程是作为数据科学家的第一步,你需要掌握的一个基本和必要的技能。这主要意味着在一段时间内收集关于特定主题的信息。

</5-online-data-science-courses-you-can-finish-in-1-day-5b60f353fa8e>

№2:清理数据

一旦我们有了数据,在我们继续分析它之前,我们将需要对它执行一些预处理,即清理数据。当从不同的资源收集数据时,它通常包含不正确的、损坏的或重复的条目。这些值可能会在结果中导致一些重大错误。这就是为什么去掉它们会得到更好、更准确的结果。数据清理过程中有多个步骤,例如修复结构错误、删除重复项以及填充缺失数据。

№3:分析数据

在我们以一种干净的、结构化的形式获得数据之后,我们可以进入下一步,数据分析和模式发现。这一步通常包含不同形式的可视化,并应用统计或/和逻辑技术来尝试和找出数据是否包含任何模式或异常,这可能有助于我们确定下一步应用的最佳算法或模型,以获得准确的结果。

№4:对数据应用模型

现在,我们终于进入了每个数据科学项目的有趣部分,也是您感觉到您的项目所属的特定专业的步骤。您将需要使用从上一步获得的信息来决定要应用的好模型,并且您的模型可以根据您的应用和您想要的结果而变化。

</10-newsletters-you-need-to-subscribe-to-as-a-data-scientist-d12c7f33fbab>

№5:解读结果

这一步与前一步齐头并进;基本上,你会有一些潜在的候选模型,然后将它们应用到你的数据中并检查结果。选择正确的模型对你将要获得的结果有很大的影响。但是,一旦您获得这些结果,您将需要理解它们,并使用它们来预测未来的数据或做出重要的商业决策。

№6:传达调查结果

您现在可能会想,应用于任何数据科学项目的步骤可能看起来很简单,但它们实际上需要大量的思考、分析和实践。在所有这些努力之后,你需要向你的主管或客户交付并传达你的结果。因此,数据科学家需要掌握的另一项基本技能是科学交流和可视化。这是非常关键的一步,因为它可以决定你项目的成败。如果你的沟通技巧不好,你就不会展示你的努力或有效地分享你的成果。

最后的想法

一些职业道路有明确的工作描述。如果你问某人他们是做什么的,并说他们是数学老师或外科医生,通常不会问出他们真正的意思。但是,如果你说你是一名数据科学家,那么 90%的时候,你会被问到这是什么意思,或者你作为一名数据科学家做什么。

</5-new-data-science-books-that-you-should-consider-reading-c90aec1d5b0d>

这种模糊背后的原因是术语数据科学家的普遍性,它用于描述不同的角色和工作描述。简单来说,数据科学家是一个处理数据并试图利用数据做出更好的商业决策的人。尽管数据科学中有不同的角色,从分析师到机器学习专家或大数据专家,但几乎所有的数据科学家都使用数据来更好地了解世界。

在这篇文章中,我试图简单地用文字说明数据科学家每天实际在做什么,以及成为一名成功的数据科学家需要什么。因此,下次有人问你,“作为一名数据科学家,你是做什么的”时,你会有一个很好的回答方法。

你想学数据科学?

原文:https://towardsdatascience.com/so-you-wanna-data-science-eb9bd4b048b8?source=collection_archive---------21-----------------------

新手指南,有兴趣了解当今最热门的技术话题之一

大卫·普帕扎在 Unsplash 上的照片

你在那里!是的,你!我听说你想看看镇上所有酷孩子都在谈论的一些新东西——一些数据科学。你可能听说过一些,这些数据奇才用黑盒来解开宇宙的秘密。你可能想知道的唯一问题是你从哪里开始?

就在这里。当我们完成这篇短文时,我们将设置和配置一个本地 Python 虚拟环境,准备好进行您可以处理的所有数据科学工作(至少对于入门来说)。任何新尝试的第一步都是了解你将要工作的环境,或者视情况而定,虚拟环境,但我们稍后会回到这一点。首先让我们来看看最可怕的努力是从计算机的终端开始的。

终点站

终端、命令行、外壳;这些小窗口允许我们以任何有意义的方式向计算机编写和发送指令,对于那些不太熟悉它们的人来说,这些小窗口可能看起来很可怕,然而,只要有一点耐心和练习,不需要很长时间就可以完成基本操作。所有主要的操作系统都会有一个终端程序。(的确,有些操作系统只是一个终端)对于那些完全不熟悉终端的人来说,你可以在这里阅读一个速成班

我将在我的 MacBook Pro 上使用一个名为 iTerm2 的终端程序。我还使用了 Powerlevel10k ,这是一个 ZSH 主题,以其灵活性和定制选项而闻名。对于新手来说,定制你的终端可能是一个令人生畏的过程,所以如果你是新手,就不要担心那些花里胡哨的东西。您可以使用操作系统附带的基本终端程序。

作者照片

包管理器

对于刚接触终端环境的人来说,这听起来可能是一个令人畏惧的话题,但我保证这里不会太糟糕。可以把包管理器想象成一个可以让你访问其他程序的程序。在 MacOS 上,这将是自制的。你可以从他们的主页找到如何安装它的说明。

安装完成后,需要安装 PyEnv,这是一个安装不同版本 python 的软件工具。最好将 PyEnv 想象成一个 Python 版本管理器,允许您在系统上同时安装不同的版本。这很重要,因为在开发和编写代码时,不同的项目将需要不同版本的 Python。

brew install pyenv

这会让很多事情发生。如果你注意的话,你会看到当家酿啤酒打开酒桶,倒酒,以及其他各种恶作剧时,你的终端上会闪过很多饮酒的引用,直到聚会最终结束,你会再次看着你的光标。通过以下测试确保 pyenv 安装正确:

pyenv

假设到目前为止一切顺利,您应该会看到下面的输出,其中提供了有关使用 pyenv 的信息。

作者图片

如果这是你第一次使用终端,恭喜你!现在,您已经在命令行中安装了第一个软件工具!PyEnv 很棒,因为它允许用户(也就是你)在同一个系统上安装和管理不同版本的 python。通常从这里开始,您将继续安装 python 版本,但我们现在可以跳过这一部分,因为如果您的系统上还没有安装最新版本,我们的下一个工具将安装最新版本。

现在让我们继续安装另一个包 pipenv,这是一个帮助我们创建 python 环境的工具!

brew install pipenv 

我们现在准备创建我们的第一个 python 环境,并做一些我们一直听说的数据科学。

Python 环境

当我第一次学习如何用 Python 编程时,最大的挑战之一是学习如何处理这些被称为虚拟环境的东西。教程经常会掩盖这个话题,如果他们提到的话,我发现这很容易引起更多的混乱。虚拟环境实际上是一个非常简单的概念。它们只是一个软件创建的虚拟环境,允许您为您正在处理的每个 python 项目安装不同的 python 包。简单吧?不要担心,对于那些对 Python 完全陌生的人来说,有更简单的方法来考虑这个问题,他们可能不能 100%确定我们在谈论什么。

可以把 Python 虚拟环境想象成一个盒子,你可以把你的项目放在里面,这样无论你在盒子里面安装什么软件包,它们都不会干扰盒子外面的任何东西。这使得我们可以在不同的机器上同时处理不同的项目,这些项目具有不同的软件版本需求,只需要在我们的终端上输入几行代码就可以从一个环境切换到另一个环境。让我们开始做第一个吧。

使用 pipenv 创建虚拟环境

使用 pipenv 创建虚拟环境是一个非常简单的过程。按照以下命令导航到要在其中创建项目的目录,并创建一个虚拟环境:

cd Your-Project-Folder
pipenv install

同样,您会在您的终端窗口上看到几行文本弹出,片刻之后,您会看到消息,告诉您可以使用pipenv shell激活虚拟环境,这将激活您的终端环境,并将改变您的终端的外观,如下所示:

作者图片

这里我想指出的重要的一点是我的终端行中屏幕右侧的额外文本。这让我知道,当我键入命令时,我是在我的虚拟环境中键入它们,而不是在我的通用系统中。现在,如果您还记得,我已经将我的终端设计得非常漂亮,您的终端在这一点上看起来可能会有所不同,虚拟环境名称位于左侧的括号中。

您可能还会注意到位于项目目录中的几个新文件。具体来说就是PipfilePipefile.lock。现在不要太担心这些,因为这些文件用于跟踪在虚拟环境中安装了哪些包(及其版本)。

Jupyter 笔记本:数据科学家最喜欢的工具

好吧,这可能不是每个数据科学家最喜欢的工具,但绝对是最重要的工具之一。为了安装它,我们像将任何和所有软件包安装到我们的虚拟环境中一样安装它:

pipenv install jupyter

现在,这将需要几秒钟的时间来完成,但一旦完成,您可以启动服务器并使用以下命令打开 Jupyter 笔记本页面:

jupyter notebook

作者图片

有我们提到的那两个文件。要开始一个新的笔记本,只需点击右上角的“新建”下拉菜单项,并选择“Python 3”作为您的内核。你将会看到一个新的标签打开了一个名为untitled.ipynb的新笔记本,你现在可以在里面做各种各样的数据科学!

作者图片

结论

Jupyter 笔记本功能极其强大,能够处理太多的事情,在这里无法一一介绍,但是我们可以很快了解它是什么,以及如何开始使用它们。

你的新笔记本将从一个单元格开始,这是一个可以用来编写代码或 markdown 的模块,这是一种为更容易地为互联网编写格式化文本而设计的标记语言。在上图中,我用第一个单元格写了一个简短的祝贺信息,用第二个单元格写了每个程序员都知道和喜欢的通用的第一行代码。您可以看到,该代码的输出显示在它被写入的单元格下方的空间中,一个新的空白单元格准备好写入新代码或进行标记。

如果你想看看 Jupyter 笔记本被用于各种科学工作的例子,他们的 Github repo 上的这个集合有许多很棒的笔记本,展示了这个简单工具是多么强大。如果你想了解更多关于 Jupyter 笔记本电脑的信息,走向数据科学这里有一篇关于它们的精彩文章。

希望这篇介绍对您有所帮助。数据科学的世界非常大,在您了解它之前,事情可能会变得复杂,本教程只是触及了所涉及的技术和应用的皮毛。随着时间、耐心,最重要的是实践和实验,事情最终会变得更有意义。从这个最初的起点,数据科学家可以走向许多方向,但本文中介绍的工具将有助于您进一步了解我们的数据世界。

所以你想成为一名数据工程师

原文:https://towardsdatascience.com/so-you-want-to-be-a-data-engineer-c1c92dcf3234?source=collection_archive---------21-----------------------

办公时间

你更好地理解架构和业务分析

许多人错误地认为优秀的数据技能造就了优秀的数据工程师。表面上看有道理。毕竟,数据工程师将来自多个来源的原始数据转换成可消费的包。因此,数据技能。虽然数据工程师需要各种技术技能,但它们更像是先决条件,而不是竞争优势。数据工程师需要掌握整个业务用例,并设计完整的数据解决方案。

为了更好地理解这一点,让我给你讲一个关于苏的故事。苏的故事始于一家名为 Krispy Crabcakes 的连锁餐厅,该餐厅在全国拥有数百家门店。他们想增加销售额,所以他们开始电子邮件营销。顾客在店内信息亭注册免费优惠券,优惠券大约每周通过电子邮件发送。作为老派,他们不信任云或 SaaS。不,他们什么都在家里做。

它管理电子邮件列表和邮件服务器。

网站开发人员创建追踪电子邮件中的图片,看看有多少人打开了它们。

优惠券在使用时在销售点系统中扫描。

在今天的环境下效率不是很高,但几个月来一直运行良好。销售额上升,大量优惠券被兑换。管理层询问电子邮件营销进展如何,IT 人员说:“太好了!”当然,这对管理层来说还不够好,他们问发了多少封邮件,打开了多少封,兑换了多少优惠券?合理的问题,如果你问我,但有一个问题。从来没有人想过衡量这些活动。他们只是被告知发一堆优惠券。现在,他们陷入了困境。数据工程师苏出场了。

Sue 知道她需要报告电子邮件活动的指标。她甚至没有和任何管理这项技术的人说过话,因为她现在不关心这个。她的目标是了解电子邮件营销是如何影响业务的。所以,在与三四个人交谈后,她终于在脑海中有了一幅画面。她也理解管理层希望看到的指标。

在考虑如何进行项目的之前,她很早就发现了项目的为什么什么。事实上,她甚至用一些注释画了一个简单的图表。该图并不真正遵循任何行业标准,但它完成了工作,并为她提供了进一步讨论的参考点。她用简单易懂的图片和一些要点记录了业务流程。

由作者提供

如果你问我,我会说这是一个很好的开始。Sue 现在了解了业务流程,因此她可以开始设计系统。她也知道出现问题时该找谁,但她做了什么?在左侧,有简短的要点,解释了 Krispy Crabcakes 使用的业务流程和管理层需要的三个指标。

右边是流程中涉及的实体之间的简单关系图。箭头显示了实体之间的关系,用符号表示可能找到的相关实体的数量。例如,优惠券可以兑换 0 次或 1 次。一个活动将至少有一个收件人,但没有最大金额。通过命名这些实体,每个人都可以使用相同的语言来讨论这个过程。

现在 Sue 完全理解了业务流程,是时候与 it 部门交流,看看使用了什么样的技术。她找到 IT 经理,要了一份架构图,以便更好地了解电子邮件营销活动是如何实施的。正如所料,没有这样的图表存在,更糟糕的是,没有一个人了解整个过程。苏已经这样做了很长时间,所以她并不感到惊讶。她也不担心。

经过一番挖掘,喝了许多咖啡,喝了一点酒,苏找到了拼图的所有碎片。她将自己的业务知识与从 IT 和 web 开发中学到的一切结合起来,构建当前技术的架构图。同样,她没有遵循任何特定的图表标准。事实上,她经常只是为正在使用的技术贴上标识。她在各种组件之间画出带有动作的箭头,以显示活动的流程。因为一切从顾客开始,她把他们放在中间。

你能跟上图表吗?花几分钟弄清楚流程。

由作者提供

根据 Sue 了解到的信息,她找到了四个数据源。虽然以前没有人知道如何跟踪发送的电子邮件,但她了解了邮件服务器的日志记录。所有电子邮件都记录了重要的元数据,以符合内部审计要求。幸运的是,这些日志包含的信息刚好足以识别每个商店为特定活动发送的电子邮件。

由作者提供

现在苏在一个很棒的地方。她了解企业如何利用电子邮件营销来提高销售额。她还拼凑了支持一切的技术环境的所有细节。她可以开始考虑解决问题了。她是登录数据库还是打开文本编辑器?不,当然不是。她现在需要设计解决方案的架构。

虽然她更愿意使用云服务或至少 Kubernetes 来管理事情,但 Crispy Crabcakes 更喜欢内部技术,并提供 Linux 服务器来运行脚本和调度 CRON 作业。没关系。Sue 是一名数据工程师,而不是技术传播者。她使用最合适的工具设计了一个优雅的解决方案。

请注意,她的新图表只包含了前一个图表中的数据源。她的解决方案不与 IT 基础设施中的许多组件交互,所以她只添加这个特定项目所需的内容。

由作者提供

Sue 还创建了一个数据库图来显示数据仓库的样子。她有一个二维的事实表。随着时间的推移,Sue 预计企业会要求更多的指标和报告。她从一个易于扩展的星型模式开始,但不会增加当前解决方案的复杂性。

由作者提供

最后,做完这一切后,Sue 开始实施她的解决方案。达到这一步似乎需要做很多工作,但事实并非如此。她花了一天左右的时间编写脚本,并开始安排收集数据的工作。几天之内,她已经可以从浏览器中查看生产报告管理。一切正常。因为她了解业务流程,所以她已经在推荐更好的度量标准和方法,以使反馈循环进入系统。她能做到这一点,是因为她懂架构,懂商业分析。事实上,脆皮蟹饼已经希望她监督更好地跟踪活动和个人电子邮件。

数据工程师需要掌握整个业务用例,并设计完整的数据解决方案。在完全理解我们支持的业务流程之前,我们经常跳到 SQL 或 Python。另一方面,Sue 在设计解决方案之前会花时间了解业务和当前技术。这使她能够为管理层和 IT 部门提供有价值的见解。这让她能够设计出既适合今天又足够灵活的解决方案。如果你想成为一名数据工程师,就像苏一样。

所以你想成为一名数据科学家?

原文:https://towardsdatascience.com/so-you-want-to-become-a-data-scientist-c47e196b5ab5?source=collection_archive---------27-----------------------

这份清单会让你成为数据超级英雄!

在 LinkedIn 上,我经常会被问到应该如何进入数据科学领域。这当然是一个非常有效的问题,但不太容易回答,因为这取决于你现在在哪里。为了以最概括的方式回答这个问题,我决定在这方面多花一点时间,写这篇指南。

网上有很多课程声称你完成后会成为一名数据科学家。在比较这些课程时,他们主要关注机器学习部分。这当然是看起来最酷的部分,创建机器学习模型。在课程中讨论的这些学校示例例如,MNIST 分类器给出了可能性的想法,然而,当一家公司想要一名数据科学家时,它们并没有反映出实际需要什么。

在家工作(用我的佳能 RP 拍摄)。

基础——数学和统计

作为一名数据科学家,出类拔萃的重要因素是什么?根据我的经验,一个数据科学家应该有扎实的数学和统计学背景。这并不意味着数据科学家应该是一个完全成熟的数学家,而是应该能够熟练地阅读方程,并且知道大学微积分 101 课程中的典型关系。比如知道像 log(x ʸ ) = y log(x) 这样的对数规则。你需要了解多少数学知识?越多越好,但至少我会说微积分 101 和线性代数 101。如果没有数学背景,你可能会活下来,但是你会很难理解比如交叉熵损失函数。需要的另一个基础是统计学,这甚至更重要。数据科学家的核心业务是数据争论,你会不断地问自己一些问题,比如“这个特性重要吗”、“我有足够的数据吗”或者“这个发现有意义吗”。几乎任何你想回答的问题都要用到你的统计学知识。这可能并不总是可见的,但统计数据有助于您在调查数据时将重要的要素与不太重要的要素组织起来。和数学一样,你知道的统计学越多越好,但至少知道描述统计学和推断统计学的基础。顾名思义,描述统计学是一种对信息集合进行描述或量化的方法。你可能还记得一些,比如均值和中值。当我们想从一个样本中推断出一个总体的特性时,我们使用推断统计学。有许多很好的在线资源可以帮助你快速掌握基础知识。这里有几个我喜欢的:

你需要有一些基本的统计学知识来理解这个笑话。(来源: Reddit 科学迷因)

核心技能:编程

到目前为止,我们只触及了基础。有了这些知识,学习数据科学会顺利很多。下一个技能是每个数据科学家的面包和黄油:编程。虽然我从来不敢把自己比作软件工程师,但我确实花了大部分时间在交互式开发环境(IDE)中编写代码来与数据交互。虽然您可以用任何编程语言来完成这项工作,但有两种语言是最常用的: PythonR 。关于哪一个更好,存在着无休止的争论,我想说这没多大关系。如果你是一个完全的新手,我总是推荐 Python,因为我认为它是一种更干净(可读性更好)的语言,并且相对容易学习。Python 是非常通用的,也可以用于许多其他事情,包括构建应用程序或网站。R 并没有错,如果你觉得 R 更吸引你,那就随意投资于这项技能。一个数据科学家应该至少精通一门编程语言。

一个数据科学家应该至少精通一门编程语言。

非常熟练的意思是,如果给你一个算法问题,例如快速排序或 fizz-buzz,你可以从头开始写解决方案(意思是没有堆栈交换)。你可以在算法上得到一些帮助,但是编程逻辑应该很容易写出来。编程是一项非常依赖你的经验的技能。你可以做一门课程帮助你入门,但是真正的理解来自于用 Python(或者 R)解决问题。关于 Python,我也建议在使用额外的包之前先掌握基本语言。熟悉语法、编程逻辑和标准库的一些组件。不要在第一周立即投入 Tensorflow 或 PyTorch。要开始使用 Python,这里有一些优秀的资源(包括我创建的一个):

所需工具:Numpy、Pandas 和一个可视化库

好了,现在我们有了坚实的基础,我们可以开始分析数据了。这意味着我们需要自己开发使用 Python(或 R)加载和操作数据的方法。我们将创建许多工具来解决特定的问题,但是创建工具来解决广泛的问题并不是一件容易的事情。幸运的是,一个巨大的开源社区为我们提供了很多方法,甚至是完整的算法。这些包由一大群志愿者维护,经过优化、验证和功能完善。从头开始编写这些方法是没有意义的(除非是为了学习)。作为一名数据科学家,你应该掌握的第一个包是 Numpy 。Numpy 扩展了 Python,为科学计算提供了许多方法和数据结构,最著名的可能是 n 维数组数据结构。在本质上,Numpy 绑定到方法的编译实现,因此执行起来几乎与其他语言一样(Python 本身有一些开销,但是 Numpy 本身是编译的)。Numpy 是巨大的,你可以假设如果你需要一个数学运算,它可能已经实现了。如果你有扎实的数学基础,你会从 Numpy 中受益匪浅。网上有很多很棒的教程,但是我强烈推荐巴勃罗·卡塞雷斯的教程。如果你喜欢视频,懒惰的程序员的课程也很不错。熟悉 Numpy 文档很重要。不仅您可能会经常使用它,而且许多其他软件包也有类似的格式。通常,文档本身提供了一个很好的指南。

成为熊猫忍者!(使用imgflip.com生成的迷因)

还有一个包是每个数据科学家都应该掌握的:熊猫(R 中的 dplyr )。熊猫是用和你的数据说话的方式,这应该感觉很自然。Pandas 得名于计量经济学术语“面板数据”,而
引入了数据结构来处理表格数据。它可以比作一个电子表格,其中的数据是由行和列构成的。Pandas 是在 Numpy 的基础上构建的,因此操作起来很快,而且经过了彻底的测试。这个包使得将数据加载到 Python 中变得非常容易。在支持(有时需要额外的软件包)许多格式,如:CVS,SQL,HDF5,XLS/XLSX,JSON,拼花,剪贴板,等等。

一旦数据被导入,Pandas 就会成为数据争论的瑞士军刀。这是一个数据科学家的核心活动。寻找丢失的值并输入它们,分析值的范围并检查这些值是否有意义。例如,如果有一个代表客户年龄的列,那么它的上限可能会超过 100(最长寿的人大约是 120 岁)。数据经过验证后,数据科学家通常会汇总数据。如果原始数据集是一个购买清单,我们可能希望汇总到每月的销售额。一旦你学会了熊猫的数据语言,这种操作对熊猫来说是极其容易的。作为一名数据科学家,你应该花时间问一些关于数据集的问题(实际上是回答这些问题)。熊猫给你找到答案的工具。你对熊猫了解得越多,找到这些答案的效率就越高。这是我会对任何申请数据科学职位的人进行的评估。这里有一些精彩的演讲和资源。成为熊猫忍者!

发现洞见是探索性数据分析(EDA)的第一步。第二步是用图来形象化你的发现。在 Python 中有许多用于绘图的包,使用哪一个取决于你。最常见的是 Matplotlib ,这是一个成熟的绘图库,用于创建出版物质量等级可视化。它很容易使用,我肯定会建议有一个基本的了解 Matplotlib。开源的好处是有很多选择。我最喜欢的一个是 Seaborn ,它是在 Matplotlib 的基础上构建的,并且很好地集成了 Pandas。另一个可以尝试的绘图库是 Altair ,它可以超级容易地创建交互式绘图。你学习哪一个系统并不重要,重要的是你能够可视化你的结果。

获得经验:竞赛和项目

大多数之前提到的熊猫教程已经展示了一些分析数据和回答各种问题的方法。但是这些问题(或者一般的方法)适用于你的问题吗?当然,这取决于你正在解决的问题,但一般来说,学会问正确的问题只能通过经验来实现。你可以通过分析别人做的项目获得经验,或者更好的是,自己做项目。有许多著名的数据集可以帮助您入门。每个数据科学家可能都知道的一个是 泰坦尼克号数据集 。这个数据集是为 Kaggle 教程竞赛创建的,目标是预测哪些乘客在悲剧事件中幸存。数据集很小,但在建模开始之前有许多问题需要解决。它有需要估算的缺失值,但是应该使用哪些值呢?中位数?刻薄?我们应该在全球范围内估算数值,还是应该在亚组中解决这个问题,即首先将数据分为一类、二类和三类?了解这些问题的答案的最好方法是通过经验和看别人的作品。

互联网让你觉得机器学习算法是关于做数据科学家的核心,我觉得不是。

泰坦尼克号比赛的目标是创建一个模型,即使用机器学习来预测乘客幸存的可能性。直到现在,我们还没有谈到任何模型。互联网让你觉得机器学习算法是关于做数据科学家的核心,我觉得不是。当然,你应该知道各种各样的算法,但是要成为所有算法的专家几乎是不可能的。不要学习机器学习算法,而是学习处理数据科学问题。每个问题都会以一个机器学习算法结束,这是你学习这个算法的时刻。你在工作中学习算法。在做了几个项目或比赛后,你会发现到目前为止,你大部分的时间都花在了数据争论和特性工程上(80%以上的时间)。我的建议是参加几个(教程)竞赛,尝试最常用的算法:逻辑回归、线性回归和随机森林。把梯度提升、深度学习模型等更高级的算法留到以后再说。这些算法有点像黑盒,因此更难掌握,但是当你习惯于更传统的算法时,也可以随意尝试这些算法。

千万不要在测试台上训练。(使用imgflip.com生成的迷因)

获得经验的最好方法是做项目或参加比赛。比赛是最容易开始的方式。数据科学竞赛最好的平台之一是 Kaggle 。Kaggle 是一个惊人的资源,因为他们不仅有比赛,还有一个讨论平台,甚至还有微型课程。当你是 Kaggle 的新手时,它会把你送到他们的教程比赛,名为:“泰坦尼克号——从灾难中学习的机器”。辅导比赛不同于常规比赛,因为没有截止日期。如果你在云中工作得很好,你不需要安装任何东西,你可以在在线 IDE 中完成所有的分析。下载数据集和离线工作是相当容易的,但是你需要自己管理 Python 环境(教程)。泰坦尼克号竞赛是一个成熟的教程,带你完成整个过程,直到你有一个最终的模型。这是一个很好的起点,从这里你可以很容易地尝试你的新改进。许多人在平台上分享他们的笔记本,这是向他人学习的最佳方式。同样,我认为改进模型的最大收获是在特征工程方面。数据争论是数据科学家的超能力。浏览其他 Kagglers 的笔记本,并查看他们创建的功能。如此小的数据集中隐藏了如此多的特征,真是令人惊讶。

数据争论是数据科学家的超能力。

下面是两个强烈推荐的辅导比赛的链接,但也可以随时查看正在进行的常规比赛。如果你准备测试一个新算法,第一站(在 Google 之后)是 sci-kit-learn 包。它已经实现了许多算法。算法和统计概念的另一个很好的来源是 Joshua Starmer 的视频。他的视频很有趣,对于掌握复杂算法非常有用。

高级主题

有很多高级的话题,我们只是简单的提了一下或者根本没提。深度学习、计算机视觉、生成性对抗网络或变形模型等主题是该领域的主要进展。如果您对数据科学感兴趣,您可能听说过这项技术,甚至可能在媒体上读到过。虽然这些主题的知识有助于获得一份数据科学的工作,但你选择正确的机会非常渺茫。有太多的东西需要成为所有领域的专家。我的建议是选择一个你最喜欢的,并研究这些。所有的主题都很受欢迎,教程的数量也多得让人应接不暇。这是你为了好玩而做的一部分,因为在你的第一份数据科学工作中,你很有可能会做些别的事情。老实说,我看到的最多的是线性回归和逻辑回归,偶尔还有基于树的模型。我的第一份工作也是如此,最先进的模型是逻辑回归。逻辑回归没有错,美妙之处在于这些模型非常容易解释,这一点正变得非常重要(理应如此)。你应该关注这些高级话题吗?肯定不是!如果你刚刚开始,把重点放在数据角力部分和几个传统模型上。成为这方面的专家。在你的旅途中,你的好奇心会驱使你去阅读一些先进的东西。一年后,你会对外面有什么有一个很好的想法。在这一年里,你可能已经参加了一些聚会,人们在那里展示他们的项目,或者组织一次关于 NLP 或 T2 和 YOLO 的辅导。几乎在任何一个大城市,志愿者们都会组织聚会,这是结识外地朋友的好方法。最后,保持消息灵通的一个好方法是(数字化地)参加会议,如 PyDataPyCon 。这些活动有一些高质量的演讲(通常在几个月后才公开),你可以与该领域的专家互动。有可能的话去参加这些活动。提问!你可能会因为问了一个愚蠢的问题而感到害怕,但是没有这样的事情。不问一个你心里想的问题更糟糕。

问问题的人是一时的傻瓜,不问问题的人是一辈子的傻瓜。—孔子。

和前一节一样,我将列出一些建议,告诉你如何掌握这一节。然而,这一次,这主要是为了娱乐,并不是一个详尽的清单。

最后的想法和清单

有许多方法可以进入数据科学,这个概述只是我坚持的路线图。请记住,这个过程并不容易,但如果你喜欢与数据打交道,即解决数据之谜,这肯定是可行的。正如您在本指南中所看到的,没有明确的终点(总是有更多东西要学)。有一个较低的门槛,需要在工作中表现良好。有两个重要的技能我还没有提到:将业务问题转化为数据问题和沟通技能。独立学习翻译商业问题有点困难,但你可能会在第一份工作中从同事那里学到这一点。请注意,这是数据科学家的一项重要技能。最后一个还没有提到的技巧是沟通技巧。在你得到你的发现后,你必须把它传达给客户或你的同事。这可以是需要令人信服的报告、图表或演示。这是一种需要训练的技能,会随着练习而提高。一个很好的改进方法就是开始写博客或者在聚会上展示你的项目!

我将所有必需的技能浓缩到一份数据科学家清单中。这些技能可以分为三组:基础、编程和特定工作。这就像一个自我评估,你可以在不足(💩),好(🙂),以及超级英雄(🦸).擅长前 8 项技能绝对足以在数据科学领域取得成功。如果你是熊猫里的超级英雄,你可以认为自己已经是专家了。最后两项技能你可以在你的第一份工作中学到,但要意识到这两项技能很重要。

数据科学家清单。做一个超级英雄!

在我们结束之前,最后一个建议。不要等到你有了一份完整的超级英雄清单再去申请工作。几样商品就可以了,然后开始申请。当然,会有拒绝,但是在申请过程中获得经验也很重要。展示你的动机,不要过度推销,你可能会比你想象的更早被邀请。

到此,我们就结束了这个指南。这些都是基于我的经验和我对数据科学家角色的看法。欢迎在 LinkedIn 上发表评论或联系我。

所以,你想建立一个强化学习库

原文:https://towardsdatascience.com/so-you-want-to-build-a-reinforcement-learning-library-658d51ddbcd?source=collection_archive---------35-----------------------

RL 算法编码指南

Avel Chuklanov 在 Unsplash 上拍摄的照片

我最近决定建立自己的强化学习库,目标是为我的博士学位建立快速创意原型。如果你很好奇, 这里有一个链接 ,虽然它肯定还没有完全完成😅

现在我自己已经经历了这种折磨,我感到受到了启发,要对我所犯的一些错误提出警告,并希望有助于防止一些深夜调试复杂的 PyTorch 代码!

1.从简单开始!

这是迄今为止我能给出的最重要的建议。当我第一次了解强化学习时,我陷入了寻找最新、最先进的算法的探索中,我可以用它来最终掌握每一部视频。这让我了解了 【阶段性政策梯度】(PPG) ,我决心从这里开始。自然地,当它不起作用时,我意识到算法有这么多不同的活动部分,几乎不可能缩小到底是哪个部分出了问题。

在 RL 世界中,许多算法都是建立在现有工作的基础上的(例如,A2C → PPO → PPG),同样的,你应该首先实现最简单的算法,然后构建更复杂的算法,这样每个阶段的调试就更少了。例如,最后,我决定放弃 PPG,只采用简单的深度 Q 网络(DQN)算法。这是更容易测试,只有真正担心的评论家网络,现在我可以更有信心,组成 DQN 算法的组件按预期工作。现在,当我用 DQN 构建一个有重叠组件的更复杂的算法时,我知道如果有什么东西不工作,我就不需要重新访问它们了。

2.先学习理论,再尝试实现

一般来说,RL 算法可能比标准的监督或非监督方法更复杂,具有复杂的损失函数和大量的超参数调整。这意味着它们往往不能在每种环境下都准确地开箱即用,如果没有算法如何工作的理论知识,您可能会认为您一定只是编写了错误的代码,而实际上问题可能是超参数的计算错误,或者只是在特定环境下使用了错误的算法。对理论的良好理解有助于更快地进行诊断,并凭直觉找到错误所在。

3.不要害怕一开始就使用快速脚本

编写好的软件有许多原则和指南。不幸的是,它们通常需要大量的思考和时间来正确地实现。当涉及到编码 RL 算法时,最重要的事情是它按照预期工作,如果它不工作,那么不管代码有多好,它都是不可用的!一个好的策略是首先确保底层功能是健全的,然后再考虑如何恰当地设计它。Jupyter 笔记本是这种用例的一个很好的工具,在获取代码并在 Python 模块中形式化之前,可以在笔记本上快速设计和测试算法。总的来说,这种方法可以节省您的时间,并增强您对代码库的信心。

现在你知道了,在实现你自己的 RL 算法时,三个技巧可以节省你的时间和压力。

如果您觉得这篇文章有用,请考虑:

所以,你想建立 ML 模型…

原文:https://towardsdatascience.com/so-you-want-to-build-ml-models-how-strong-are-your-data-jujitsu-skills-13fb3777c0ef?source=collection_archive---------44-----------------------

你的数据柔术有多强?

照片由 Josefina Di BattistaUnsplash 上拍摄

几周前,我主持了一系列圆桌会议,与来自 Atomico 的投资组合公司的一群工程领导者进行讨论。这些讨论的主题是数据,特别是人工智能公司中的数据挑战。无可否认,这是一个广泛而开放的话题,但也是一个重要的话题。

构建人工智能的一个重要部分,或者更具体地说,机器学习(ML)模型,是以数据为中心的。ML 模型主要涉及获取数据、清理数据、转换数据、可视化数据,并最终使用数据来构建模型。Anaconda 最近的一项调查显示,花费在数据操作(或争论)上的时间约占 ML/data 科学家总时间的 65%。这些数字在其他调查中也是一致的。处理数据是 ML/data 科学家花费时间的重要部分。

在接下来的几篇博文中,我将回顾一系列相关的主题。这一系列帖子将关注人工智能公司面临的整体数据挑战,并提供一个简单的框架来帮助引导对话。这一系列的文章将涵盖指导我们讨论的总体框架,以及对构建 ML 模型的 3 个主要数据活动的深入研究。

我们在讨论中使用的(简单)框架以 3 个数据活动为中心,如下图所示。这些足够广泛,涵盖了 ML 产品或团队将从事的主要数据活动。值得注意的是,本讨论的范围仅限于模型构建或培训,而不是模型部署到生产的整个生命周期。后者将在以后的文章中讨论。这些帖子的第一个系列将涵盖数据收集步骤,这是下面“框架”中概述的第一步。

ML 模型是数据优化算法。模型试图在一系列数据(训练数据集)上拟合一条曲线,试图最小化或最大化一个函数,在 ML 术语中通常称为成本函数。线性回归算法就是一个很好的例子。它执行优化过程以找到一组权重,该组权重最小化训练数据集上的平方和误差。更多关于这个话题和 ML 模型如何“学习”的信息,可以在我之前的一篇文章中找到。

因此,构建人工智能产品,更具体地说是 ML 模型,需要大量的数据就不足为奇了。你不仅需要一个大型数据集来建立初始模型,而且你将不断需要更多的数据来微调你的模型,以确保它们能够对新的输入进行归纳——以解决人工智能的长尾问题

因此,数据收集问题可以分为两个不同的阶段。第一阶段是获取构建初始模型所需的数据。我称之为自举问题。有许多方法可以获得引导模型所需的初始数据集。第二阶段是获取数据,不断训练和改进你的模型。模特就像花园,需要不断的修饰。

有许多数据源可用于引导阶段。你可以在网上找到免费的或购买的数据。例如, KaggleAWS 一样托管各种数据集。一旦有了运行的模型,另一个数据源就开始发挥作用了。从模型中排出的数据成为训练数据的来源。一个特别有用的数据是生产模型的输入数据。这些数据可用于训练新版本的模型。

数据收集阶段的两个阶段都面临两个主要挑战:地面实况调查,或标记和数据绘图。一个例子可能有助于说明这两个问题。

假设我们正在构建一个模型来识别猫的图片。我们出去获取一个包含数千张猫和其他动物(比如狗)图像的数据集。如果我们幸运的话,我们获得的数据集将已经被预先标记。这意味着有猫的图片会被清楚地标记出来,同样,没有猫的图片也会被清楚地标记出来。在我们的数据集的例子中,图像文件名标识图像是猫还是狗,如下所示。

不幸的是,明确标记的数据集是例外,而不是规范。此外,标签可能会变得非常复杂,这取决于模型的性质。考虑这样的情况,我们想要扩展我们的猫识别模型,以不仅识别猫(即分类),而且还突出猫在图像中的位置(即定位)。

突然之间,我们的训练数据集不足以识别图像是否是猫的图像。我们的训练数据缺少指示图像中猫的位置的边界框。我们必须丰富我们的训练数据,以包括猫图像周围的边界框,以帮助我们的模型对猫的图像进行分类和定位。这通常是一个手动过程,需要人工标记这些数据。这就是标签工具发挥作用的地方,比如 V7HiveLabelboxAWS SageMaker GroundTruthcentaulabs等等

让我们给这个故事再加一个转折。我们的模型现在能够对猫进行分类和定位。但是,业务现在要求我们通过添加新功能来增强它。我们的模型应该区分小猫和成年猫。它还应该把猫分为暹罗猫、波斯猫、孟加拉猫、美国短毛猫、土耳其安哥拉猫或其他种类。我们的训练数据集现在需要再次增强。我们不能再依赖文件名中的“cat”字符串来帮助我们对图像进行分类,我们知道必须向我们的训练数据集添加更多的元数据。

我们的猫识别模型正在风靡全球。因此,业务需要我们扩展到亚洲,并要求我们扩大模型,以对亚洲国家常见的猫进行分类。我们最初的训练数据集严重偏向于西方国家常见的猫。我们能够获得额外的数据来帮助我们的模型对亚洲猫进行归纳,但这些数据与我们现有的训练数据集不同。我们获得的数据由成千上万的 JPG 图像和一个 CSV 文件组成。该文件包含每个图像的元数据,如下所示。

观察这个训练数据集的结构如何与我们最初的大不相同。我们的初始数据集在图像文件名中对图像中猫的存在进行编码,而我们的亚洲数据集依赖于 CSV 文件对图像进行分类。我们现在必须采用我们的训练管道来“理解”这两种数据模型,或者将它们合并成一个公共模型。我们现在突然考虑 ETL、Airflow,并将我们的各种训练数据集转换成一个通用的分类法。

此外,我们从亚洲数据集获得的图像与我们之前获得的图像大小不同。我们现在必须将这两个集合协调成一些标准的图像大小,我们的 ML 模型将在其上进行训练。我们还注意到,这个数据集中的一些图像不适用于我们的领域——猫。有数千份包含单细胞原生动物图像的文件。这些很可能被错误地包含在我们获得的亚洲数据集中。提示:始终目视检查您的数据!

不知不觉中,需要有限磁盘和计算能力的简单任务变成了复杂的数据转换、映射、标记和可视化检查网络。我们最终构建的堆栈可能类似于下图,这不是没有道理的。请注意,存储和培训层将在本系列的下一篇文章中讨论。这些也会给我们的堆栈增加不同的需求和工具。

由于问题的本质,我在本文中强调的挑战显得过于简单:识别猫的图像。然而,当构建“真实”模型时,这些挑战会变得相当复杂。在数据难以获取、缺乏标准导致数据集之间痛苦的逻辑示意图和语义映射的领域,这种方法格外困难。在医疗保健等数据分布具有长尾特征的领域,这一挑战会进一步加剧;一个我非常熟悉的领域。

最初发表于【https://karimfanous.substack.com】

所以你想做机器学习但是不知道从哪里开始

原文:https://towardsdatascience.com/so-you-want-to-do-machine-learning-but-dont-know-where-to-start-3fba1529bbcd?source=collection_archive---------19-----------------------

提示:不要从数学开始

图片由 安德里亚·皮亚卡迪奥 来自 像素

我经常收到电子邮件,询问申请 ML 的建议。

我最喜欢的电子邮件来自那些已经有一个他们正在努力解决的具体问题的人。然而,他们通常没有 ML 或编码方面的经验。

非软件工程师开始应用机器学习最快的方法是什么?

这是我的建议。

学习 Python 的基础知识

你不需要成为代码忍者。但是你确实需要写代码。

从概念上理解 ML 比编码更重要。所以现在先学习基础知识。

如果你以前从来没有写过 Python,那么去 Codecademy 看看下面的内容。

  • 如果和否则
  • 列表和词典
  • 字符串、整数和浮点
  • 功能
  • 导入库
  • 安装软件包

就是这样。这还不够,但是当你需要更多的知识时,你可以重温。

太多的人因为没有准备好而错过了建造东西的机会。永远不要优先考虑“通过”在线编码课程,而不是应用你已经学到的东西。我们忘记了我们不适用的东西。

就拿那些因为这个原因而多次(在最终成为软件开发人员之前)失败的人来说吧。

为 ML 设置您的机器

建立一个本地环境太糟糕了。

对于开发人员来说,在开始一项新工作时,花 1-2 天的时间来设置他们的机器并不罕见。

幸运的是,这不会很难。

  1. 安装 Python
  2. 设置一个虚拟环境
  3. 安装并启动 Jupyter notebooks (带 pip),一个伟大的ML 和数据科学 IDE

不要跳过设置虚拟环境。当您不可避免地安装不兼容的软件包版本时,这将为您省去麻烦。您需要做的只是关闭虚拟环境并创建一个新环境。

亲提示。不要用蟒蛇。如果你不知道你在做什么,在你的机器上设置难以逆转的默认值是很容易的。

安装这些库

你需要一个软件包管理器来安装软件包。使用 pip

然后在您的虚拟环境中安装以下软件。

  • Numpy(包含有用的数据类型,如数组)
  • Pandas(允许以表格格式导入和查看数据)
  • Sklearn(包括您的 ML 型号)

从这些开始。然后根据需要安装其他的。

有很多很棒的库,但是如果你是初学者,保持简单。

知道(有监督的)机器学习的含义

机器学习是做什么的?

ML 模型学习特征(输入)和标签(输出)之间的模糊关系。

示例:

你想根据房子的特点来预测它的价格。

特征:房间数量,平方英尺,位置,邻里收入…
标签:价格

虽然这是一个简单的例子,但是要理解大多数 ML 模型在孤立的情况下做一些非常简单的事情。

现实世界中有用的“AI”通常是 ML、软件工程和深层领域知识的结合。

准备您的数据

这是机器学习最具挑战性和最耗时的方面之一。

每个案例都不一样,所以很难提供通用的建议。

但这里有几个你可能想思考的话题。

  1. 编码输入。机器学习模型只接受数值输入。在将图像和文本提供给模型之前,必须将它们转换成数字列表。
  2. 缩放数据。当不同的特征具有不同的尺度时,一些算法会变得混乱。
  3. 将复杂的特征拆分成更简单的特征。对于一个模型来说,学习 5 个独立的布尔特征通常比学习一个复杂的特征更容易。
  4. 删除丢失特征或标签的数据。否则,这可能会让你的管道崩溃。
  5. 剔除异常值。根据算法的不同,异常值可能会误导您的模型。
  6. 将数据拆分成训练集和测试集。避免在你的测试和训练集中包含相同的例子。否则,您不会知道模型是否可以概括,或者只是在评估期间学习了训练集中的示例。

太多了。老实说,这可能是 6 篇不同文章的主题。但我想让你们注意到这些,因为这是可以花费大量时间和进行改进的地方。

信用: imgflip

现在做最低限度的工作,在你建立了一些基线结果后再回来改进它。

理解回归 VS 分类问题

在监督学习领域,有两种类型的问题,分类和回归。

鸣谢: Sklearn

分类:

在分类问题中,您试图预测离散数量的标签中的一个。

在预测一个实例属于哪个组时使用此选项。

例子:

  1. 是猫、狗还是公鸡的形象?
  2. 这个学生会被麻省理工学院录取吗?
  3. 这部小说是科幻小说,还是非科幻小说?

回归:

在回归问题中,你试图预测一个连续的量。

当可能的输出落在一个范围内时,使用此选项。根据经验,预测某物的货币价值总是一个回归问题。

  1. 明天下雨的可能性。
  2. 一幅画的美元价值。
  3. 候选人 X 赢得选举的信心水平。

在选择模型之前,了解您的问题属于哪一类非常重要。

然后选择一个适合你的问题类型的模型。

让事情尽快运转起来

没有人第一次就能得到正确的 ML 管道。

特别是作为一个初学者,在你尝试之前,你对什么有效什么无效几乎没有什么直觉。天下没有免费的午餐。

所以尽快让你的管道工作起来,让你的模型做出预测。

写马虎的代码是可以的,选择最简单的模型,忘记去掉标点和停用词。这可以在第二次或第三次重写时完成,此时您将跟踪这些更改对性能的改善程度。

这有助于你确定一个模型是否比随机猜测更有效。

评估结果并建立基线

既然您的管道输出了一个结果,那么您必须用度量来评估它。

如果你的问题是一个分类,使用 Sklearn 的分类报告输出精度,召回,f1,以及对每个类的支持。

如果你的问题是一个回归,深入研究像 MSE 、RMSE 和 MAE 这样的误差度量。

没有两个问题可以用同样的方式来评价。永远要考虑上下文。

现在,请注意您的绩效指标以及您的管道的当前状态。如果您的模型在这一点上表现不佳,也不要气馁。

这是你的底线。从现在开始,你做的每一件事都有超越它的目标。

是时候迭代一下,看看我们是否能跑赢基线。

找到导师并获得反馈

现在是时候去找一个导师,询问如何改进的反馈了。

你已经付出了努力,并且有工作可以证明。

如果你伸出援手,人们会更愿意帮助你。如果你在这一点之前伸出手,你可能会被忽略。

虽然你可以(也应该)尝试迭代自己。你是初学者,ML 领域广阔。很可能,你不知道你不知道的。一个简单的建议就能让你的成绩直线上升。

我个人曾在 LinkedIn 上向数百人寻求项目帮助。大多数人会忽视你。有些不会。当你得到建议时,立即应用并报告结果。

永远不要低估导师。

不要从数学开始

很多人会说“从数学开始”。别理他们。

ML 的基础是数学。这对于发展对特定模型如何工作的直觉是非常有价值的。但是在建造任何东西之前学习线性代数会让你落后几个月。

等有了一些实践经验再来数学。

结论

我希望这对你寻求应用机器学习有所帮助。

我强调快速移动和建造东西,因为这是一个布满兔子洞的领域,你可能要花上几年时间才能下去。许多人工智能工程师确实把整个职业生涯都花在了一个领域,比如搜索、自然语言处理或图像分类。

所以特别是在开始的时候,你必须小心不要偏离主题。

拓展足够的广度来完成工作。然后在需要的地方增加深度。

你有很多工作要做。走吧。

所以你想学机器学习?学慢一点。

原文:https://towardsdatascience.com/so-you-want-to-learn-machine-learning-learn-slower-daee3e40ba34?source=collection_archive---------6-----------------------

意见

急什么?

蒂姆·高Unsplash 上拍摄的照片

我最喜欢的文章之一是彼得·诺维格的《十年自学编程》。这篇文章的概要是学习慢一点,比如,建立好的技能,像编程这样有价值的技能需要时间。

你会看到教授快速学习或快速阅读的课程,或者在 24 小时内做 X 运动,然后在 6 周内变得健康。很明显,现代世界喜欢速度,但速度是一种营销策略。

我一直在农场度过时光,如果你深入大自然足够长的时间,你会发现大自然母亲不戴手表。一切都以自己的速度运行。以它需要的速度前进。

所以知道这一点,知道现代世界热爱速度。让自己与众不同的一个好方法是创建自己的时间表。

用你的生物作为时钟。

如果你想学点什么,挑战自己,是的,但是让它按照需要的速度前进。如果某件事变得无聊,就放弃它。

学校做了一件可怕的工作,让学习感觉你必须学这个,你必须学那个。同时说服你学习一切,让你觉得自己什么都不知道。如果不难,那你就做得不对。

教育系统承诺在高中毕业时给你你想要的饼干。

但是等等,还有一块饼干。

那是在大学结束的时候。

哦,你本科毕业了?

太美了。我们为你准备了另一块饼干。研究生学习怎么样,硕士学位?最后你会得到那个漂亮的饼干,我保证!

哦,你完成了你的硕士学位?

现在我们给你一个博士学位,只是多一点,多一点学习。然后你最终会觉得你学到了一些东西。

我大学头两年没及格。所以我不是谈论这种概念的最佳人选。这也不是反对大学,他们有他们的位置,这是反对匆忙学习的论点。

每次我学到一些东西,因为我不得不学,就像,我被强行灌输了一些过于繁琐的课程(因为现在大学太贵了,他们不得不给你增加额外的材料,而这些材料是不必要的,因为你必须觉得你的钱花得值),我都忘记了。试图学习我必须学习的东西让我问了一个问题“考试内容是什么?”而不是“我好奇什么?”

如今很多书也是如此,300 页的废话,10 页的好东西。本应作为博客文章留下的内容现在变成了《纽约时报》的畅销书。

然而,我学到的东西是因为我想要去学的,或者我读过的书是因为我喜欢它的封面,而且前 10 页没有无聊到掉眼泪,我记得就像一种怀旧的味道。

所以我反对快速阅读和快速学习这样的概念的理由是双重的。

第一,大自然其实并不在乎时间。违背自然很少会有好结果。

第二,如果某样东西学起来很有趣,如果某样东西读起来很有趣,如果我真的很喜欢一本书,我不希望它结束。我不想加快速度。

你曾经读过一本真正的好书,读到最后 10 章,最后 10 页,然后把它延长吗?也许你开始一晚上只看一页。延长是因为你不想让它结束。

如果你在学习新的东西,你会在早上兴奋地起床,兴奋地继续把点点滴滴联系起来。

路尽头的饼干很好吃,但我更喜欢跟着面包屑的痕迹走。

回忆一下小时候学骑自行车或者小时候学几乎任何东西,你会玩,你不会试着催促事情。

我不记得问过任何人“我要多久才能学会骑自行车?”

我收到许多这种性质的问题。

学习编码需要多长时间?

我怎么学机器学习更快?

我能理解这些问题从何而来,因为我也曾经是这种心态。

但是每次我试图催促一些事情,比如,试图学得比它能学的更快,它都会导致可以避免的不愉快。

也许这是必要的。有必要经历学习的痛苦,努力比你学得更快,找到你的极限,并从那里调整。痛苦是一位了不起的老师。

有些人喜欢研磨。不快乐的折磨,除非你投入工作,投入努力,直到他们濒临崩溃。那不是我的风格。

我的风格是:如果某样东西值得学习,我想享受它。

当一个孩子学骑自行车时,摔倒并不是一件消极的事情,它是一个信息,是通往下一个的路上的一个点。世界上最好的感觉之一。当你学骑自行车的时候,你摔倒了一百次,这使得第一次骑自行车如此愉快。

这就是我学习或创造的目的。

我不想加快这个过程。我想神奇地偶然发现一个时刻,一个点连接到另一个点,学习变成了一场美丽的舞蹈。好奇心驱使下的舞蹈总是更有趣。

有人说“你一定要跳舞”有多过瘾?

就像有人说“你必须学会这个!”

不,谢谢你。我没有时间去学任何东西。

你也不用坚持。

互联网已经发展成为一个奇妙的地方,在那里你不必坚持传统的学习方式(尽管真正的传统学习方式是修修补补,下小赌注,记住所有的自然都必须用一个工具:错误)。你可以创造自己的道路。你想怎么做都行。

如果你对某件事不感兴趣,或者某件事不有趣,那就去做别的事情,或者想办法让它变得有趣。

别忘了。如果你想做什么就做什么,如果你有创造自己道路的自由,你现在也有责任继续创造它。

急什么?

学慢一点。

让它变得有趣。

你可以在 YouTube 上看到这个片段的视频版本。

所以你要获得分析学硕士学位

原文:https://towardsdatascience.com/so-youre-getting-a-masters-in-analytics-d81fb9b4595e?source=collection_archive---------6-----------------------

我对有志于加入这些项目的数据科学家和分析师的建议是

阿德里安·卡萨诺瓦在 Unsplash 上的照片

新学年即将到来,我收到了一些进入数据科学或分析项目的新生的请求,请他们谈谈我在研究生院的经历。我于 2016 年 6 月从芝加哥大学毕业,获得了分析学硕士学位。这是一次神奇的经历,帮助我进入了一个新的职业生涯,但肯定有一些事情我希望我知道或者会做得不同。

我试图记录人们问的不同问题,并将它们汇总成一个列表。一些警告:

  • 我的程序、作业和项目主要是在 r。
  • 这是假设你参加这个项目是为了寻找一份新的工作或职业。

以下是我对加入这些项目的有抱负的数据科学家/分析师的建议,以及我将如何让已经很棒的经历变得更好。

1.学习 Git,把所有东西都放在 Github(或等效物)上

任何课程都不需要 Git 和 Github(至少当我在这个项目中的时候),我真的很后悔没有和我的同学一起使用它。

  • Github 是向潜在雇主展示你技能的好方法。
  • 这是一个比我们所做的更好的协作工具(通过电子邮件互相发送代码变更)。
  • 这也是一段很好的历史,所以你记得你做过的事情(我已经忘记了)。
  • 在 Github 的项目中有家庭作业也比在你的桌面上有无数的文件夹要整洁得多(我就是这么做的)。
  • Git[hub]也是一项很好的技能,因为许多数据分析/科学团队经常使用它!
  • 也没有什么比一张填满的贡献图更好的了。

来源:“事情变得相当严重”迷因。【knowyourmeme.com/memes/things-are-getting-pretty-serious】T4,字面媒体,2012 年

2.给每堂课留下一个你可以展示给潜在雇主的例子

这些节目进行得很快。我是一名兼职学生,度过了我的甜蜜时光,尽管如此,我仍觉得自己在向终点冲刺。由于优先权的竞争,很容易匆忙完成家庭作业和项目。不要把注意力放在完成上,记住这样做的目的是进入一个新的职业/角色/人生道路,你所做的一切都是你可以展示的新技能的例子。所以:记录你的工作。要清楚背后的意图以及你是如何思考的。弄得像样点。与上面的建议相关,将潜在雇主链接到你的 Github。

当然,我知道不是所有的家庭作业都适用于此——但是很多作业都适用,尤其是你的期末项目。你会在简历上写下你可以制作带有数据可视化的仪表盘吗?你必须有一个例子来支持这一点,并且能够展示这个项目如何说明你可以给你未来的公司带来的价值。

来源:img lip。https://i.imgflip.com/155vlt.gif

3.多样化你的编程语言

我的课程主要是 R 语言,只有一门课程需要 Python。在很短的时间内,我学会并会使用 Python——课程结束后,我在差不多相同的时间内失去了我的技能!由于没有训练和保持我在 R 以外的语言中的编程技能,它们很快就被搁置起来,最终被遗忘了。

来源:“你知道我不会说西班牙语”迷因。作者创建的图像。makeameme.org。

当我完成学业申请工作时,我不得不向潜在的雇主承认我的 R 技能更强。这最终成功了,但它确实让我没有机会去任何 Python 商店。为了复制我在 Python 中所做的工作,我不得不刷新自己的语言基础——考虑到我在课堂上付出的努力,这是一个遗憾。

4.仔细选择你的顶点项目和顾问

当到了顶点时间,选项被列在一个网站上,我们被告知要组成一个小组,选择一个项目,并挑选一名顾问。这似乎是唯一的变量,但实际上还有许多其他因素影响着项目的难度和复杂性。他们直接影响到一个人是否能在他们想要的时候毕业,所以这是一个需要非常认真对待的决定。

  • 研究问题:研究问题是否由顶点发起人定义(明确)?否则,大量时间将用于研究问题的开发、编辑、重写和最终批准。
  • 数据:将一个项目添加到顶点列表中并不意味着当您想要开始您的项目时,这些数据将随时可供您使用。几个学生选择了一个项目,他们认为顶点赞助商会立即发送数据,而不是必须等待一刻钟。)之前,他们实际上收到任何东西,可以开始分析。提问:数据是否可用,如果不可用,将如何收集?你需要签署保密协议吗?谁负责这项工作?数据将如何以何种格式传输给您?获取数据需要多长时间?在顶石课程中,您是否需要更新或刷新它?一旦有了数据,你会细化你的研究问题吗?
  • 赞助商可用性/互动:一些赞助商希望参与流程的每一步。有些赞助商一个季度见一次。每种方法都有优点和缺点,但一般来说,你需要一个提供明确指导和期望的赞助商,而不是微观管理。试着找出你的赞助商,这样你就知道如何最好地与他们合作。你们将长期在一起!
  • 顾问可用性/互动:与赞助商类似,顶点顾问对合作方式的期望也各不相同。请注意,一些顾问正在处理繁重的工作——另一份工作,课程,同时还有几个顶点项目——因此你必须考虑如何获得他们的时间和注意力。如果你需要帮助或者正在努力赶上最后期限,这可能会令人沮丧。尽早决定您的期望——我的团队与我们的顾问建立了每周一次的电话会议和每两周一次的面对面会议。
  • 顾问专长:在你试图做的特定分析中,有些顾问更有经验或洞察力。我的顶点项目与调查对象打交道——幸运的是,我的顾问是一名市场研究员!然而,有时指导老师的专业与你的分析方法不同,你的小组将不得不自己做更多的研究来满足资助者的需求。
  • 组可用性/交互:在事件的突然转变中,顶点集团的一个成员退出并在另一个州开始新的工作。这让他们的团队陷入混乱,因为他们试图找出如何管理项目的其余部分。虽然我为找到工作的人感到高兴(不管怎么说,这是最重要的),但他们本可以通过公开和诚实来帮助他们的团队。问问你潜在的队友:如果给你一份新工作会怎么样?你的毕业时间表是固定的还是灵活的?你还学了多少其他课程,你能为这个项目贡献多少时间?除了这些问题之外,还要弄清楚每个人打算如何互动,并从一开始就设定明确的期望,这样整个团队就能一起走向成功。

5.跟踪并给出如何改进计划的建议

我是我的分析项目的第二批学生,尽管已经过去了几年,但大多数数据科学课程都相对较新。在哪些应该是必修课,哪些应该是选修课,以什么顺序上课等方面,仍有一些问题需要解决。例如,我们被推进了 R 的世界,因为我们的作业必须在 R 中完成,尽管我们中的许多人以前甚至从未安装过一个软件包!我们向管理部门提出建议,他们应该创建一个涵盖基础知识的短期课程,现在它提供给任何可能从中受益的学生。

来源:Giphy 上的“帮助我帮助你”meme。https://giphy.com/explore/help-me-help-you

当你完成你的计划时,一切似乎都进行得很快。除非你有意识地思考如何改善你的经历以及你的后来者的经历,否则细节会被遗忘。

6.了解你喜欢这个过程的什么

随着您在分析或数据科学项目中的进步,您将了解数据工作的细微差别和复杂性。这是一个迷人的旅程,需要项目管理、客户互动和理解、技术专长、反馈循环和反复试验。这些都很棒,也很有必要,但是会有你更喜欢的部分。我一直喜欢创作美观的作品,并且知道我会喜欢开发数据可视化。我不知道喜欢伴随数据争论而来的创造力和智力难题。有些人的感觉正好相反,这是他们最不喜欢的数据科学程序。但是他们可能比我更喜欢收集业务需求。

你应该知道如何成功地完成数据科学方法论,但研究生院也是一个机会,了解哪一部分让你兴奋。由于数据分析的巨大世界,这将有助于你尽可能地专注于能给你的工作带来快乐的角色。

资料来源:tenor.com。https://tenor . com/view/star-trek-the-next-generation-data-play-cat-gif-3992194

结论

这些数据科学/分析学位的目的是作为达到目的的一种手段,但我完成硕士学位已经两年了,我仍在反思这段经历。一路上,发生了很多变化和改进。我的分析项目影响了我的思维方式、职业轨迹和工作之外的行为。对此我心存感激,因为我也意识到学习没有止境。

本文原载于 2018 年 8 月 4 日 % > %梦想。在推特上和我保持联系!

药物开发的社会图表

原文:https://towardsdatascience.com/social-graphs-for-drug-development-e45faa68dc0e?source=collection_archive---------63-----------------------

通过 unsplash 的图像权利

早在二月份,当我们还能安全地聚在一起的时候,Grakn 实验室的第一次全球用户会议, Grakn Cosmos 在伦敦举行;和https://www.linkedin.com/in/paul-agapow/?originalSubdomain=uk【阿斯利康】的健康信息学主管保罗·阿加泼,讲述了他的团队在建立社交图谱以减少临床试验招募时的时间和财政资源方面的工作。

这是第一步,让我们发展专业知识去探索,看看我们能去哪里——我们是有问题要解决的人。

死亡之谷

别担心,会有一个幸福的结局

药物开发是一个极其困难、漫长和昂贵的过程。这不是新消息,通常这个过程被类比为走进一个赌场,把 100k 放在桌子上,然后把它全押在黑色 27 上。

这是因为当一家制药公司着手生产一种已经通过所有临床试验阶段的新药时,它是 10 年和大约 20 亿美元的成果。

许多制药公司在内部存储了药物蛋白质相互作用集,即成千上万的候选分子,它们能够与人体的某个部分相互作用。这是许多药物开发项目的起点。然后,根据哪些产品可以交付,哪些产品可以合理生产,对这些产品进行筛选。甚至在把药物放入人体之前,你可能会有大约 250 个。

从这 250 个进入临床试验的第一阶段——在那里检查安全性和有效性——你可能会在监管审查前减少到 5 个。最后,你拿出一种新药。

从 10000 个可能的候选人,减少到 1 个。在这期间,我们有 20 亿美元和大约 10 年的时间。这就是我们所说的“死亡之谷”。

当你为一种药物付费时,你并没有为这种药物的开发付费,而是为所有这些失败付费。像阿斯利康这样的公司正在考虑他们为临床试验招募人员的方式,作为一个可能的优化领域。

但是首先,简要概述一下临床试验过程

第 1 阶段:观察少量患者服用药物的安全性。在第一阶段的试验中,通常会有数十人参与。

第二阶段:重新评估安全性——对人类是否安全——并查看预期补救措施的功效。在第二阶段,该药物在 100 名患者身上进行了测试。

第三阶段:最后的安全性和有效性检查——这种药物会意外杀死你吗?它有什么好处吗——同时询问它是否比目前生产的任何药物都更有效?

临床试验受益于大量的受试者。一般来说,科目越多,统计能力越强。然而,由于每个受试者的成本在 1-1 万美元之间,60%的临床试验达不到招募目标,这是该过程中较为困难的方面之一。

招聘是怎么运作的?

传统上,招聘是通过社会关系完成的:个人关系、宣传册、招聘网站、医院周围的广告等。如今,随着招募目标的增加和药物的投入,这种做法已经行不通了。

使用联合电子健康记录(EHR ),如 Trinetx,您可以获得来自不同地区的大量患者数据。不利的一面是,这仍然只是全部潜在受试者的一个子集,通常缺乏鉴定所需的细节。在下面的幻灯片中,Paul 提供了一些取消潜在受试者资格的方法。

临床试验招募中的困难——允许使用的幻灯片

你会注意到,许多这种失格可以通过问一些社交问题来减轻。制药公司最不擅长问的软问题,这可能有助于减少获得完整试验受试者组所需的时间和资源。

与临床试验招募相关的问题—经许可使用的幻灯片

真实世界的证据

当组织和团队面对这一现实时,存在未被充分利用的、廉价的、在数量和范围上不断增长的数据。这种数据通常无法通过任何其他方式获得,它代表了个人和/或群体的更真实的情况。在生命科学领域的某些圈子里,包括 Paul 在内,这些数据被称为“真实世界证据”或“真实世界数据”。

我们谈论的是来自健康追踪器和可穿戴设备的数据、电子医疗记录和调查数据、医院和药房数据、社交媒体和消费者数据。杂乱的数据,不是来自临床对照试验的数据,也就是说,任何不适合放入表格的数据。

这种数据越来越多地被认为是以前不可用或不可识别的信号的驱动因素。除了这种“新”数据便宜之外,它还使制药公司能够覆盖以前无法覆盖的人群。此外,这些数据涵盖了出于道德考虑公司不允许问的问题。

这些“世界的信号”为临床试验提供了一个扩展的范围,其中主要涉及的人口部分是不典型的。保罗注意到,参加试验的人群通常是年轻、受过大学教育的白人男性。

因此需要“真实世界的证据”——但是它将如何帮助减少成本和时间呢?

解决方案

让我们从我们所拥有的开始:

  • 新近可访问的“真实世界的证据”,来自不同来源的社会数据——杂乱的数据
  • 识别和发现新患者群的机会
  • 更快、更准确地获得资格的机会,避免在不合格的科目上花费时间和金钱

保罗和他在阿斯利康的团队想出的解决方案是一个试验从业者的社会图表。保罗称之为“肿瘤医生的脸书”。这张图表将通过他们的合作、出版物和工作场所把他们联系起来。他们可以用这些不同来源的“真实世界证据”来注释图表,并询问关于这些从业者的问题。

该项目被称为 OPSIN——肿瘤从业者社会互动网络,作为“简化和激励临床试验”的更大努力的一部分。

非常有趣的是,在 2008 年,一个团队提出了一个类似的概念,使用“手动”图形数据库,从互联网上搜集任何数据,让他们能够找到关键的意见领袖。

今天,有了如此丰富的信息和技术平台来解决这个问题。

在图表领域,人们对制药越来越感兴趣。特别是在迄今为止的生物化学和生物实体的知识图表中。

此外,他们可以将现在可用的外部信息与他们所有的内部数据相结合,也许更重要的是与多年的考验和磨难相结合。

构建社交图谱

从他们的前辈那里学习,有一些问题贯穿那些早期的努力。关键是它们不是为可持续发展而构建的——这些早期图表不仅仅是为一次性项目而构建的。Paul 指出,这些通常是由使用 R 的人构建的,或者是一起编写 python 脚本;手工构建自己的图形数据库。

这些都不适合操作,它们需要插入到其他系统中,以便整个组织的团队能够利用它。这就是 Grakn 介入的地方。

Grakn 是一个复杂数据的数据库,来自 Grakn 实验室的团队。知道阿斯利康的团队将会对边、起源边以及关于边的陈述感兴趣,他们开始意识到他们真正谈论的是超图。Grakn 作为一个超图数据库,使这变得容易得多。

模型的基础

对于热爱形式模式的生物信息学家来说,他们经常迫不及待地想把他们内部庞大的本体转储进去,而不考虑这是否是适合这种情况的正确模型。Grakn 实验室的一些团队鼓励 Paul 不要从他们现有的本体开始,而是从他们想要问的一系列问题开始。从上面的思路出发,我们将在下面讨论生物信息学家可以提出的几个问题。

然而,由于他们确实需要一个模式,以下是他们做出的一些决定:

  • 从简单开始,不要从一开始就对整个世界建模
  • 使用你想问的问题作为引出你的模型的潜在概念的一种方式——通常当我们大声或在纸上清晰地表达问题时,我们能够更清楚地看到事情
  • 从人和机构开始,通过合作和出版等关系将他们联系起来
  • 给每个关系一个时间元素作为属性
  • 将范围保持在 5 年内——人和关系会发生变化,5 年对我们的目标来说已经足够了

我们可以问的问题

我们已经在 y 站点使用 X 了,还能考虑谁呢?他们认识谁?

朋友的朋友的问题对于超图来说是微不足道的。通过协作追踪到另一个,你可以回答这些问题,并根据连接数、最近时间或其他时间过滤器对连接进行排名。

附近有从业者吗?在同一个城市有没有我们可以参考试验地点的患者?

虽然很难表达“附近”的概念,但在应用层有其他方法可以解决这个问题——在撰写本文时,他们还不需要这样做。Grakn 的推理引擎允许在查询时推断传递关系。

有哪些从业者社区?这些社区的关键人物是谁?

集群上的集群,识别图中的中心概念和它们之间的联系也是很平常的事情。

结论

听保罗的故事,我学到了什么?嗯,即使制药界变得越来越数据驱动,仍然有许多软问题要问,这给临床试验过程带来了巨大的价值。有大量的数据可以用来解决这些问题。

这是一个人的企业,我们会有一些人的问题……

保罗还花时间感谢了这支球队,我不能忘记他们:Domingo Salazar,Maja Malkowska,Li 和 Gabi Feldberg。

特别感谢保罗和他在阿斯利康的团队分享他们的故事和智慧之言。这显然是一个优化的领域,这些项目有助于推动整个行业向前发展

你可以在 Grakn Labs YouTube 频道点击找到完整的演示文稿。

VADER 的社交媒体情感分析

原文:https://towardsdatascience.com/social-media-sentiment-analysis-with-vader-c29d0c96fa90?source=collection_archive---------8-----------------------

测量细微文本数据的情感。

普拉蒂克卡蒂亚尔Unsplash

对于数据科学,我们需要不同的工具来处理各种各样的数据集。在我们深入研究情感分析的不同方法之前,重要的是要注意它是自然语言处理中的一种技术。通常被称为 NLP ,是研究计算机如何理解人类语言的研究。虽然这是一个在数据科学家中很受欢迎的专业,但它并不是行业的专属。

使用文本数据会带来一组独特的问题和其他类型的数据集所没有的解决方案。通常,文本数据比其他数据类型需要更多的清理预处理。然而,也有独特的探索性数据分析技术,我们可以应用于文本数据,如单词云、可视化最常见的单词等等。在这篇博客中,我们将关注情感分析。

情绪分析

情感分析是对文本文档有多少积极、消极和固执己见的分析。例如,这种技术通常用于评论数据,以了解顾客对公司产品的感受。

文本数据的问题在于它反映了人类的语言。它是多样的并且不断进化,因此,机器学习必须不断约束基于新词汇的模型。例如,社交媒体文本极其微妙,众所周知,机器学习算法很难“理解”。

有许多不同的库可以帮助我们进行情感分析,但我们将关注一个对肮脏的社交媒体数据特别有效的库, VADER

VADER 代表用于情感推理的 Valance Aware 字典,它是一种情感分析工具,对人类文本中情感的极性和强度都很敏感。这个词典是一个基于规则的系统,专门针对社交媒体数据进行训练。你可以在这里查看工具的 GitHub 库。

TextBlob

为了理解这个工具的有效性,我们可以先看看 TextBlob 在 Twitter 数据上的表现。出于这个博客的目的,我将展示我最近的项目 Twitter 仇恨言论检测 中的例子,其中的文本数据已经被清理

请注意,由于该项目的性质,本博客中的以下可视化内容包含未经审查的、露骨的和冒犯性的语言。

对于大多数具有“普通”文本的自然语言处理项目,如书籍、新闻文章、电影评论等。我们通常可以使用 TextBlob 。 TextBlob 是一个库,它提供了一个简单的 API 来处理文本数据,包括词性数据、名词短语提取、标记化、分类等任务。

对于情感分析,TextBlob 是唯一的,因为除了极性 得分,它还生成主观性得分。如果我们从单独一行中的每条 tweet 的数据帧开始,我们可以创建一个简单的 lambda 函数来将这些方法应用到 tweet。

pol = **lambda** x: TextBlob(x).sentiment.polaritysub = **lambda** x: TextBlob(x).sentiment.subjectivity

之后,我们可以使用一个groupby函数来查看每个标签、Hate SpeechNot Hate Speech的平均极性和主观性得分。

TextBlob 情感得分

TextBlob 的极性得分在从- 1 (最负)到 1 (最正)的范围内测量。非仇恨言论文本(标签 0)似乎相对中性,平均得分为 0.006,而仇恨言论文本(标签 1)也可以被视为中性,得分为-0.071。从他们的主观性来看,这两个标签似乎都有相似的意见水平。为了更深入的了解,请随意查看 GitHub 上的这个分析的 Jupyter 笔记本

最终,这些分数看起来而不是代表这个数据集中的推文,其中的文本范围从仇恨言论到攻击性语言。因此,我们可以看到 TextBlob 很难分析 Twitter 数据。让我们看看 VADER 能对这种肮脏、无意义的社交媒体数据做些什么。

应用 VADER

现在,我们终于可以去 VADER 了。VADER 的独特之处在于它分解了每个文档的极性分数,给出了一个中性复合的分数。与 TextBlob 类似,我们可以使用简单的 lambda 函数将该方法应用于整个数据帧,以生成这些极性得分。

pol = **lambda** x: analyser.polarity_scores(x)

该函数将分数作为字典返回,因此在其他几行代码之后,我们可以创建一个 dataframe,其中包含单独列中的每个分数。同样,你可以在 Jupyter 笔记本上查看完整的代码。这是我们最后得到的结果。

VADER 情绪得分

对于一些groupby函数,这里是整个数据集的平均分数,由标签分隔。

解读 VADER 的极性得分

正、负和中性分数代表属于这些类别的文本的比例。因此,仇恨言论推文平均有 8%是正面的,61%是中性的,30%是负面的。另一方面,没有仇恨言论的推文平均有 10%是正面的,65%是中性的,25%是负面的。这种分类对于理解数据集中情绪的 T21 范围更有帮助。这表明两个语料库是相似的,但仇恨言论标签的负面推文略多,平均。有趣的是,这两个类别中的大多数推文都被认为是非常中立的,但至少我们有一个清晰的分类。

复合极性得分

最值得注意的是,该库提供了一个复合极性得分,这是一个计算所有词典评级总和的度量,并且将它们在-1 和 1 之间归一化。其中-1 表示非常负,1 表示非常正。这个评分似乎更可靠,因为它包含了这个语料库的整体情绪。这两个类别都包含负面的、攻击性的语言。但是我们可以从上面的分数中看到,已经被归类为仇恨言论的推文尤其负面

下一步将是可视化所有这些分数的分布!你可以查看笔记本,了解正面、中性和负面分数的分布情况。但这里是复合极性分数的分布。

VADER 化合物极性得分分布,按标签

从这张图表中,我们可以看到被归类为仇恨言论的推文特别负面,正如我们已经怀疑的那样。它进一步强调了这些标签之间的边际差异。

在所有这些之后,很明显 VADER在社交媒体数据上执行情感分析的高级库。总的来说,自然语言处理是机器学习中一个高度实验性的领域,它完全是关于找到能够适应你的数据集的正确包。在这种情况下,对于 Twitter 仇恨言论数据,我们需要一个针对肮脏、细微差别的文本数据进行训练的词典。下次当你处理社交媒体数据时,请随意尝试 VADER!

社交媒体的使用正在扼杀你作为数据科学家的职业生涯

原文:https://towardsdatascience.com/social-media-use-is-killing-your-career-as-a-data-scientist-be6fa0fc16d1?source=collection_archive---------3-----------------------

多长时间的深度专注对数据科学家来说至关重要

在我们的互联网时代,我们通过邮件聊天和社交媒体与地球上的每个人实时联系。回复一封电子邮件,检查你的媒体文章有多少浏览量,在 twitter 上发布 doomscroll,提醒你查看对你正在撰写的报告的评论。所有这些都需要我们的关注,让我们远离我们正在做的工作。在他的书《深度工作》中,卡尔·纽波特解释说,所有这些注意力需求使得人们很难留出不间断的深度专注时间来工作(深度工作)。需要深入的工作来解决你在研究中面临的困难挑战,没有深入的工作会严重限制你的进步。

凯利·西克玛Unsplash 上拍摄的照片

对于我和我的博士同事来说,在从事新的或困难的项目时,一个常见的问题是在最初几周明显缺乏进展,随后几天疯狂的生产力。后来回想起来,最初几周缺乏具体进展是骗人的。当刚刚开始一个新的主题时,它仍然是如此之大和陌生,以至于你的大脑很难理解它。随着时间的推移,对主题的更好理解类似于 zip 算法:它压缩信息。当信息被充分压缩时,它会立刻适应你的思维空间。当你达到这个门槛时,生产力的日子就开始了,许多有形的结果就产生了。

以我的经验来看,zip 算法是由一件事推动的:长时间不间断的深度专注时间,也就是深度工作。然而,社交媒体、即时通讯和电子邮件与我们的个人和职业生活的深度融合不断冲击着这些专注的时间段。当你不断受到干扰时,进入并保持深度工作是非常困难的。任何分心都会让我们脱离深层工作,分心后可能需要 15 分钟才能回到深层工作。

但是,我们如何摆脱网络干扰的束缚呢?卡尔·纽波特在他《深度工作》一书中提供了许多可靠的建议。对于所有的细微差别和深度,我强烈建议你阅读这本书,但以下几点确实引起了我的注意:

  • 积极主动地安排不受干扰的深度专注时间,在此期间排除一切干扰。禁用电子邮件、即时消息和社交媒体,搬到一个安静的地方,让你的同事知道你没空。你需要做的任何事情都可以让你真正进入一种深度、专注和不受干扰的精神状态。
  • 用电子邮件、即时消息和其他分散注意力的方式批量处理你的互动。例如,在每天开始和结束时用 30 分钟回复邮件,并在那批时间之外禁用它。我也强烈建议你关掉智能手机上的任何通知声音或蜂鸣声。选择你想与这些工具交互的时间,而不是让工具替你选择。
  • 变得更加适应无聊。与其在没事的时候拿着你的智能手机,不如让自己感到无聊,活在当下。通过活在当下,你可以训练你的思维专注,这与你不断被社交媒体等上的大量信息分散注意力时形成的分心心态形成对比。

我是谁?

我叫 Paul Hiemstra,是荷兰的一名教师和数据科学家。我是科学家和软件工程师的混合体,对与数据科学相关的一切都有广泛的兴趣。你可以在 medium 上关注我,或者在 LinkedIn 上关注我。

如果你喜欢这篇文章,你可能也会喜欢我的其他一些文章:

社会网络分析:社区检测

原文:https://towardsdatascience.com/social-network-analysis-community-detection-2b19e836c76c?source=collection_archive---------16-----------------------

使用 R 从 Yelp API 中提取用户数据并创建网络图

在线社交平台使世界各地的人们能够相互交流,并与他们有共同兴趣的人建立关系。这可以在现实生活中观察到——自然地,我们倾向于与和我们相似的人发展和维持关系。具有相似兴趣的人倾向于相互吸引,并在社区中联系在一起——彼此拥有相似特征的人群或团体。由于人们倾向于与类似的人聚集在一起,我们可以使用社区检测来识别具有高学历(联系)的用户,并查看他们在网络中能走多远。

用户数据提取— 由于我们只对用户数据感兴趣,我们将只提取以下变量:

  • User_id — Yelp 用户 ID;这是制作节点和边所需要的
  • 姓名—用户的名字
  • 审核计数—用户撰写的审核数量
  • Yelp 自—用户加入 Yelp 的日期
  • 朋友—按用户 id 包含用户所有朋友的列表
  • 粉丝—用户拥有的粉丝数量
  • 精英—用户拥有精英身份的年数
  • 平均星级—用户对所有已写评论的平均评分

Yelp 数据非常大,所以从 json 文件中提取数据需要很长时间。

library(jsonlite)
library(dplyr)
library(stringr)
library(tidyr)
library(tidyjson)
library(tidyverse)
library(igraph)user_out <- lapply(readLines("yelp_academic_dataset_user.json"), fromJSON)#parse through each user's data and only obtain columns we are interested inyelp_parse <- function(x) {

  parse_list <- list(user_id = x$user_id, 
                     name = x$name, 
                     review_count = x$review_count, 
                     yelping_since = x$yelping_since, 
                     elite = x$elite, 
                     friends = x$friends, 
                     fans = x$fans, 
                     average_stars = x$average_stars)

  parse_list <- lapply(parse_list, FUN = function(x) ifelse(is.null(x), "", x))

  df <- data_frame(user_id=parse_list$user_id,
                   name=parse_list$name, 
                   review_count = parse_list$review_count, 
                   yelping_since=parse_list$yelping_since, 
                   elite = parse_list$elite, 
                   friends = parse_list$friends, 
                   fans = parse_list$fans, 
                   average_stars = parse_list$average_stars)
  df
}user_list <- lapply(user_out, FUN = yelp_parse)#should now have a dictionary with only keys and values we want#now, make this into a table
user_df <- data.frame(matrix(unlist(user_list), nrow=length(user_list), byrow=TRUE),stringsAsFactors=FALSE)

网络图— 让我们用 r 中的 igraph 包制作两张对比 2005 年和 2015 年加入的用户的图,10 年后会有什么不同?

制作网络图需要非常长的时间,所以我们将我们的子集限制在 100k 个节点,并创建最大度数的用户的子图。

#remove users with no friends
sample <- subset(user_df, friends != "None")#make a subset; we only need to retain data of users with some social activity
sub <- subset(sample, year == 2005 & review_count >= 2 & no_of_friends >= 2)#make links (nodes and edges)
sample_friends <- sub %>% select(user_id, friends)
sample_users <- strsplit(sample_friends$friends, split = ",")
sample_dat <- data.frame(user_id = rep(sample_friends$user_id, sapply(sample_users, length)), friends = unlist(sample_users))#network is still too big, take a random sample of 100k nodes
samp_net <- sample_n(sample_dat, 100000)#make network 
network <- graph.data.frame(samp_net)
network_s <- simplify(network)net_deg <- degree(network_s)
all_degree <- degree(network, mode = 'all')#graph user with max degrees
sub_all <- subcomponent(network_s, which(all_degree == max(all_degree)), 'all')
g_sub <- induced_subgraph(network_s, sub_all)#communities
graph.com <- fastgreedy.community(as.undirected(g_sub))
V(g_sub)$color <- graph.com$membership + 1#create pdf graph for high resolution (try zooming in!)
pdf("communities2005.pdf", 10,10)
plot(g_sub, 
     vertex.color = V(g_sub)$color,
     vertex.size = 1,
     vertex.label = NA,
     vertex.frame.color = adjustcolor("#41424c", alpha.f = 0.25),
     edge.arrow.size = 0.1,
     edge.color = adjustcolor("#41424c", alpha.f = 0.20),
     edge.width = 1.5,
     edge.arrow.mode=0,
     layout=layout_with_lgl,
     asp = 0.9,
     dpi=300
) 
dev.off()

于 2005 年(左)和 2015 年(右)加入的用户社区。

左边的图表更加密集,这可能是因为在平台上存在时间更长的 Yelpers 有更多的时间在 Yelp 上建立自己的声誉,并在社区中建立自己的地位。另一方面,2015 年加入的 Yelpers,社区密度小,边少→连接少。来自 2015 年社区的一个有趣的见解是,橙色圆点的密集区域集中在网络底部附近,这意味着有一个拥有类似特征的大型用户社区。

从我们的社区子图中,我们可以发现小团体:

#cliques/communities
cliques <- max_cliques(g_sub)
cliqueBP <- matrix(c(rep(paste0("cl", seq_along(cliques)), sapply(cliques, length)), names(unlist(cliques))), ncol=2, )#shows which clique users belong to
bp <- graph_from_edgelist(cliqueBP, directed = F)#to graph cliques, use the same code as above, but replace g_sub with bp

这个看起来很酷,但是还没有太大用处。我们肯定能看到集群!

我们可以利用这一点来深入了解一个流行的 Yelper 的网络,以可视化他们的势力范围。

#show largest clique from subgraph of user with the max number of degrees to identify bridgepdf("cliques2015.pdf", 10,10)
plot(bp, 
     vertex.size = all_degree,
     vertex.color = adjustcolor("lightgoldenrod3", alpha.f = 0.6),
     edge.color = adjustcolor("#41424c", alpha.f = 0.3),
     vertex.frame.color = adjustcolor("#41424c", alpha.f = 0.3),
     vertex.label = ifelse(V(bp)$name %in% top_d1$user_id, top_d1$name, NA),
     vertex.label.color = "darkred",
     layout=layout_with_lgl,
     asp = 0.8,
     dpi = 300)
dev.off()

显示了集团相对于其他集团的位置,这就是为什么我们看到他们的一些名字出现不止一次。名字是子集中朋友数量最多的用户的名字。

每个节点(用户)的大小表示他们的连接(好友数量)。每条边(链接)显示节点(用户的朋友)之间的连接。米歇尔和布莱恩特出现两次,因为这表明他们的影响力出现在不止一个群体中。

在上面的小团体中,我们发现 Paige 具有最高的中间中心性— 一个节点(用户)充当两个节点之间的桥梁的次数的度量。

蓝色节点连接两个黄色节点的网络示例。第一个网络(左)具有最高的介数中心性。

桥梁很重要,因为它们连接了社会网络中的两个不同群体;他们有助于弥合不同社区之间的差距!桥梁的一个例子是能够与技术和非技术团队成员交流和解释数据的人。另一个例子是当你的朋友向你介绍他们的新朋友时,你的朋友充当了桥梁。

  • 我们可以使用中间中心性来找到有很大的影响力,并看看他们的影响力可以在不同的社区之间传播多远。
  • 我们还可以将此应用于其他社交媒体数据,以识别潜在的影响者。理想情况下,我们应该挑选能够影响不同群体的影响者,这就是中间中心性派上用场的地方!

参考文献:

《社会网络中的社区检测》电线在 JSM 2017。

王,亚历克斯,迈克尔张,和 Il-Horn 汉恩。《社会推动:在线产品评级中朋友社会影响力的准实验研究》。"

图形算法

igraph R 包装手册

如果你有任何问题或想要我写的 R 代码,请随时联系我在 m.delacruz@uci.edu

社会网络分析:从图论到 Python 应用

原文:https://towardsdatascience.com/social-network-analysis-from-theory-to-applications-with-python-d12e9a34c2c7?source=collection_archive---------0-----------------------

社会网络分析是通过使用网络和图论来调查社会结构的过程。本文向数据科学家介绍了社交网络理论,并简要介绍了图论和信息传播。它深入到 Python 代码中,使用 NetworkX 从真实数据集构建和暗示社交网络。

文章大纲

(本文是来自 Pycon 2019 的一次演讲的文字版。你可以观看下面的视频查看 github 代码库

PyCon 2019 talk —社交网络分析

https://github.com/dimgold/pycon_social_networkx

网络理论

我们将首先简要介绍网络的基本组成部分:节点和边。

示例网络

节点(例子中的 A、B、C、D、E)通常代表网络中的实体,可以持有自身属性(如重量、大小、位置等任何属性)和基于网络的属性(如 -邻居数或 -节点所属的连通分量等)。).

代表节点之间的连接,也可能包含属性(如代表连接强度的权重、不对称关系情况下的方向或时间,如果适用)。

这两个基本元素可以描述多种现象,如社会联系、虚拟路由网络、物理电网络、道路网络、生物关系网络以及许多其他关系。

真实世界的网络

现实世界的网络,尤其是社交网络,具有独特的结构,这种结构通常不同于随机的数学网络:

来源:黄,钟源等,“小世界网络模型中局部信息对社会模拟的影响”j .阿提夫。社会主义者社会主义者 Simul。2005 年第 8 期

  • 小世界 现象声称,真实的网络在任何相连的网络成员之间往往有非常短的路径(就跳数而言)。这适用于真实和虚拟的社交网络(六次握手理论),也适用于物理网络,如机场或网络交通路线的电力。
  • 具有幂律度分布的 无标度网络具有一个偏斜的群体,该群体具有少数高度连接的节点(例如社会影响)和大量松散连接的节点。
  • 同向性 是个体与相似的他人产生联想和联结的倾向,从而导致邻里之间具有相似的属性。

中心性度量

高度集中的节点在网络中起着关键作用,充当不同网络动态的枢纽。然而中心性的定义和重要性可能因情况而异,并且可能指不同的中心性测量:

  • —节点的邻居数量
  • 特征向量/ PageRank —邻居迭代圆
  • 接近度 —对所有节点的接近程度
  • 介数 —通过节点的最短路径的数量

各种中心性测量的说明。资料来源——阿罗约.."发现社交网络中的关键人物."计算社会网络分析 (2010 )

不同的度量在不同的场景中是有用的,例如网页排名(页面排名)、临界点检测(中间性)、交通枢纽(接近度)和其他应用。

构建网络

只要我们能够描述节点之间的关系,就可以从各种数据集构建网络。在下面的例子中,我们将使用Pythonnetworkx包构建并可视化 Eurovision 2018 投票网络(基于官方数据)

我们将从 excel 文件中读取数据到一个 熊猫 数据帧中,以获得投票的表格表示。由于每行代表每个国家的所有选票,我们将融化数据集,以确保每行代表两个国家(节点)之间的一张选票()。

然后,我们将使用 networkx 从我们作为熊猫数据帧的边列表中构建一个有向图。最后,我们将尝试使用泛型方法来使可视化,如下面的代码所示:

从 excel 文件加载欧洲电视网

nx.draw_networkx(G)欧洲电视网 2018 投票网络结果

形象化

不幸的是内置的 绘制 的方法产生了一个非常难以理解的图形。这种方法试图绘制一个高度连通的图形,但是由于没有有用的“提示”,它无法从数据中获得很多意义。我们将通过划分和征服剧情的不同视觉方面,利用我们对实体的先验知识来增强图形:

  • 位置— 每个国家根据其地理位置进行分配
  • 每个国家都被它的国旗和国旗颜色所识别
  • 大小 —节点和边的大小代表点数

最后,我们将分部分绘制网络组件:

新的数据可读性更强,让我们对投票有了一个简要的了解。作为一般的旁注,绘制网络图通常很困难,需要在呈现的数据量和传达的信息之间进行深思熟虑的权衡。(可以尝试探索其他网络可视化工具如 GephiPyvisGraphChi )。

信息流

信息扩散过程可能类似于疾病的病毒传播,遵循从一个个体跳到他的社会邻居的传染动力学。两个流行的基本模型经常被用来描述这个过程:

线性阈值定义了一个基于阈值的行为,其中影响从节点的多个邻居累积,只有当累积的影响超过某个阈值时才被激活。这种行为对于电影推荐来说是典型的,你的一个朋友的建议可能最终会说服你去看一部电影。

线性阈值激活函数。来源:独立级联和线性阈值模型。 P. Shakarian,A . Bhatnagar,A . Aleali,E . Shaabani,R . Guo——社会网络中的扩散,2015 年

独立级联模型中,节点的每个活动邻居都有概率和独立的机会激活该节点。这类似于病毒的传播,例如在新冠肺炎,每个社会互动都可能引发感染。

信息流示例

为了说明信息传播过程,我们将使用基于《权力的游戏》中角色的剑网。该网络是基于“冰与火之歌”中的共同出现而构建的。

《权力的游戏》网络创作流程示意图。

依靠独立的级联模型,我们将试图追踪谣言传播的动态,这在这个节目中很常见。

来源:https://www.pinterest.com/pin/753508581387517221/

剧透预警!假设琼恩·雪诺在过程开始时一无所知,而他的两个忠实朋友布兰·史塔克山姆威尔·塔利知道一个关于他生活的非常重要的秘密。让我们看看在独立级联模式下谣言是如何传播的:

独立级联过程模拟代码

谣言在 t=1 到达乔恩,在接下来的时间里传播到他的邻居,并迅速传遍整个网络,最终成为众所周知的事情:

《权力的游戏》网络上的独立级联扩散

这种动力学高度依赖于模型参数,模型参数可以将扩散过程驱动到不同的模式。

影响力最大化

来源:https://quizlet.com/398701664/flashcards

影响最大化问题描述了一种营销(但不仅仅是)设置,其中营销者的目标是选择网络中有限的一组节点(播种组),以便将影响自然地传播到尽可能多的节点。例如,考虑邀请有限数量的有影响力的人参加一个著名的产品发布会,以便将消息传播给他们网络中的其他人。

这种影响者可以用许多技术来识别,比如使用我们上面提到的中心性度量。以下是权力的游戏网络中最核心的节点,根据不同的衡量标准:

《权力的游戏》网络中的中心性度量

正如我们所看到的,一些角色在不同的措施中重新出现,并且也因其在节目中的社会影响力而闻名。

通过模拟大多数中心节点的选择,我们观察到选择网络的单个节点可以实现大约 50%的网络覆盖——这就是社交影响者可能有多重要。

网络通过不同的方法和预算影响覆盖范围

另一方面,影响力最大化很难。事实上,这被认为是一个 NP 难问题。开发了许多试探法来在有效的计算中寻找最佳播种集。尝试一种强力方法来寻找我们网络中的最佳播种夫妇,结果花费了 41 分钟,实现了 56%的覆盖率(通过选择劳勃·拜拉席恩卓戈·卡奥)——这是一个用中心启发式很难实现的结果。

总结

网络分析是各种领域的复杂而有用的工具,特别是在快速增长的社交网络中。这种分析的应用包括营销 i 影响力最大化、欺诈检测推荐系统。有多种工具和技术可应用于网络数据集,但需要明智地选择它们,并考虑问题和网络的独特属性。

如果您希望在您的学术研究中引用该资源,请使用以下格式:

戈登伯格,德米特里。"社会网络分析:从图论到 python 的应用."PyCon 2019 —第三届以色列国家 Python 大会,以色列,2019 年。arXiv 预印本 arXiv:2102.10014 (2021)。

有用的资源

代码和数据:

论文:

让我们保持联系!

通过邮件Linkedin 向我提出问题和想法。

对我在 Booking.com 的工作感兴趣吗?查看我们的Booking.com 数据科学博客。

https://booking.ai

具有 Genshin 影响的社会网络分析

原文:https://towardsdatascience.com/social-network-analysis-with-genshin-impact-df49da8c10c3?source=collection_archive---------16-----------------------

python 中的社会网络分析,应用于 Genshin Impact。

Omar FloresUnsplash 拍摄的照片

0.什么和为什么——背景和目的

Genshin 是一款开放世界的动作角色扮演游戏。除了被评为 2020 年 Google Play 最佳游戏之一之外,它也是 2021 年上半年最卖座的手机游戏之一。与游戏打包在一起的是传说,玩家和其他角色在故事线中穿插。

游戏中不同角色相互提及的方式激发了对 Genshin Impact 角色的社交网络的分析,以回答以下问题:

1.分析前:数据来源和范围

  • gen shin Impact fandom wiki手动整理数据
  • 只有可玩角色被考虑用于分析,因为他们往往是大多数玩家熟悉的角色
  • 如果 A 在 A 的故事任务或台词中“提到”了 B,那么 A 就被认为与 B 有联系
  • 注:本次探险的数据收集于 2021 年 7 月 28 日,目前的旗帜(最新人物)是上里绚香。目前没有发布的闪电角色不包括在内(这是在知道闪电的任务线之前写的)
  • 用于分析的数据和完整代码可在这里找到。为了简洁起见,在本文中可能会修改或省略代码。
  • 用于分析的库是 NetworkX (导入为nx)

2.Genshin 社交网络——指导

定向社交网络意味着角色之间的联系是定向的,可能不是相互的。

构建有向社会网络

图一。Genshin 定向社交网络

  • 角色根据他们所属的国家被涂上颜色:绿色代表蒙德斯塔特,橙色代表李越,蓝色代表斯涅日纳亚,紫色代表闪电,灰色代表不属于任何一个国家的旅行者(玩家)

网络观测

  • 有许多关系不是对等的(单向的):

一个例子:Kadehara Kazuha → Kamisato 绚香,但不存在相反的关系,这表明 Kazuha 提到并因此暗示知道绚香,但绚香不知道 Kazuha。同理对于中力→ Venti。

  • 有些联系是通往其他国家的重要桥梁:

李越和斯涅日那亚之间的唯一纽带是中坜和塔尔塔利亚之间的纽带。北斗与 Kazuha 的联系是李越和闪电之间唯一的联系。

这可能会预测玩家将如何被介绍到更新的地区——通过让玩家首先与这些桥接角色(例如,Kazuha,塔尔塔利亚)互动。

  • 蒙德都特的角色似乎比李越的角色更紧密(联系更多),这可能意味着蒙德都特的角色比李越的角色更熟悉对方。

网络中的角色重要性

为了理解一个角色在这个网络中有多重要,或者说有多中心,使用了中心性度量。中心性度量决定了一个角色在我们的网络中有多重要。每个中心性度量之间的差异是如何解释“重要性”。

有向图有 3 个可以应用的中心性度量:入度和出度中心性以及页面等级中心性。

度内中心性

指的是有多少人在给某个特定的节点“送领带”。在我们的网络中,它指的是有多少人提到了某个特定人物,是衡量某个特定人物受欢迎程度的一种方式。更高的 in-degree 意味着一个角色被其他人提及很多,推断更受欢迎。

图二。具有最高程度中心性的前 10 个字符的排名

  • 平均程度中心性是 7——每个字符有大约 7 个其他字符提到它们。
  • 最受欢迎的角色大多来自蒙德都特,吉恩、丽莎、凯娅和克利的数量几乎是平均水平的两倍。
  • 了解了故事情节,这个结果就说得通了。前五名最受欢迎的角色都是蒙德斯泰特管理机构的成员,经常一起出现。

外度中心性

与 in-degree centralityt 相反,指的是一个特定的字符直接向多少个字符发送联系。在我们的网络中,这是一种衡量一个特定角色认识多少字符的方法。

图三。具有最高出度中心性的前 10 个字符的排名

  • 平均出度中心性是 7——每个字符提到大约 7 个其他字符
  • 最具社会性的价值观在这里并没有像在学位上那样表现出巨大的差异
  • 有趣的是,网络的平均(包括小数)入度和出度几乎完全相同,但入度和出度计数的分布不同。

图 4。人物内外向中心性的分布

  • 出度表示更接近正态分布的分布

页面排名中心性

为影响力增加了一个额外的考虑因素——比提到一个角色的角色数量(入度)更重要的是有多少有影响力的纽带即将到来。较高的页面等级中心性值表明一个字符与更有影响力的字符相连接,并且具有超出其直接联系的更广范围。

图五。具有最高页面等级中心性的前 10 个字符的等级

  • 这个列表中的大多数角色之前都被列为具有最高程度中心性值的角色,所以他们不仅受欢迎,还知道其他知名角色

3.Genshin 社交网络——无向

为了进一步分析网络,有必要将有向图转换成无向图。此过程中的一个主要考虑因素是单向(不对称)关系的处理。无向平局意味着平局是相互的(双向的),这在我们的上下文中意味着两个角色互相认识(足以提及对方)。因此,在我们的无向网络中去掉所有的单向联系,只考虑角色之间的相互联系,会更有意义。

设置无向网络

图六。Genshin 无向社会网络

网络观测

  • 要求连接是双向的会导致图中出现与其他字符无关的孤立字符(可怜的旅行者)
  • 与有向网络相比,这个网络看起来更稀疏

隔离物不太可能是这个网络的中心(主要是因为它们不是这个网络的一部分),它们将被移除。

图 7。无隔离的无向社会网络

网络中的角色重要性

如果网络是无向的,就可以使用度中心性之外的中心性度量:接近度、中间性、特征向量中心性。

程度中心性 将重要性放在一个字符直接连接到多少个字符上。衡量一个角色在网络中的重要性的简单方法——如果他们认识很多其他角色,那么这个角色是重要的,但是仍然是与稍后解释的其他重要性度量进行比较的良好基础。

图 8。具有最高程度中心性的前 10 个字符的排序

  • 迪吕克之前排在第一位,有很高的程度中心性,一旦相互关系加强,他就不再在名单上了,这表明尽管他被许多角色提到,但他似乎并不认识他们(奇怪的是,很符合角色的特点……)

接近中心性 通过测量一个字符在其他字符之间的最短路径中的次数,将重要性放在该字符间接连接到多少个字符上。这里更重要的角色是可以通过最短路径到达更多角色的角色——能够以最快的速度将信息传播给网络中的大多数角色。

图九。具有最高接近中心性的前 10 个角色的排名

  • 这个列表中的大多数字符不具有最高程度的中心性(知道许多其他字符),但是能够最快地到达最多数量的其他字符
  • 回头看一下无向网络(图 7),具有较高接近中心性值的角色是蒙德斯塔特和李越之间的纽带——迪奥娜、香玲、阿尔比多、邢丘、尤拉、殷飞,这解释了它们的高度重要性,因为这些角色具有将信息传播给他们自己国家以外的更广泛受众的联系

中间中心性
强调网络中的信息经纪人,即“介于”想要联系对方的角色对之间的角色。具有较高“中间性”的角色允许这些角色对信息流有更多的控制,这也是它们之所以重要的原因。

图九。具有最高介数中心性的前 10 位字符排序

  • 请注意,与亲密度中心性相比,中间度中心性会产生不同的字符排序列表
  • 同样,具有较高中间中心性值的字符是那些连接蒙德都特和李越的字符——Diona、香菱、Albedo、Xingqiu、Eula、殷飞,表明它们是两个国家之间重要的信息经纪人
  • 当考虑无向图(图 7)时,胡涛是奇奇与网络其余部分的唯一纽带,这意味着奇奇依赖胡涛来获得信息或向网络其余部分传播信息
  • 上述观点也适用于塔尔塔利亚和中利——塔尔塔利亚依赖中利在更广泛的网络中访问和分发信息

特征向量中心性 将重要性定义为与网络中其他有影响力的人物有联系——与关系良好的人物有联系。更重要的是与更好连接的字符的联系,而不是简单地将每个联系视为相等(程度中心性就是这样)。

图 10。具有最高特征向量中心性的前 10 个字符的排序

  • 这个排名看起来与上面的度中心性非常相似,除了这个列表只包含来自 Mondstadt 的字符,这意味着这些字符不仅仅有很多联系,这些联系也往往具有更高的质量(对于在网络中具有更高影响力的字符)
  • 类似地,等级中心性排名高但特征向量中心性排名低的角色(例如宁光)有很多联系,但这些联系是与离权力中心有一定距离的角色

结论:谁是谁?

谁是特伊维特最受欢迎的人?

  • 最高程度的中心性(有向网络)
  • 最高中心度(无向网络)
  • 难以否认的是,很多人物都认识琼,足以谈论她
  • 在故事情节中,她也是玩家在遇到她之前最先听说的角色之一

谁是 Teyvat 中最重要的人物?

卡耶

  • 这不太容易回答,什么被认为是重要的可能是主观的,需要更全面地看待每个节点在社交网络中的作用

图 11。前 10 个字符的平均排名

  • 一个简单的方法是平均每个角色在各种无向网络中心性中的排名,这表明 Jean 是最重要的
  • 然而,Kaeya 在社交网络中有更有利的位置,因为他在所有应用的角色重要性测量中排名前 10,并且不仅受欢迎,而且是可以控制社交网络中信息流的重要角色

泰勒:博士

在 Teyvat,你应该认识 Jean,但是如果你需要信息,去找 Kaeya。

作为一名独立创始人,试推出一款人工智能/人工智能产品

原文:https://towardsdatascience.com/soft-launching-an-ai-ml-product-as-a-solo-founder-87ee81bbe6f6?source=collection_archive---------16-----------------------

Pro 提示: 3D 打印创业公司可以自己制作 swag。图片作者。

深入了解我如何构建 Print Nanny,它使用计算机视觉来自动检测 3D 打印故障。

技术深度探讨我如何构建打印保姆,它使用计算机视觉自动检测 3D 打印故障。我将介绍每个开发阶段:从最小可行原型扩大规模以满足客户需求。

作为单枪匹马的创始人,推出一款人工智能/人工智能驱动的产品是一场高风险的赌博。下面是我如何通过定义一个成功的 ML 战略并在每个阶段利用正确的谷歌云平台服务来充分利用我有限的时间。

去年春天我过生日的时候,我给自己买了每个女孩都需要的东西:一个熔融细丝制造系统(又名 3D 打印机)。我组装了打印机,小心地运行校准程序,并拿出我的卡尺来检查测试打印。

我的大多数照片都完美无瑕!虽然偶尔会出现印刷失败的情况。我设置了 OctoPrint ,这是一个 3D 打印机的开源 web 接口。我经常偷看直播镜头,想知道这是否是新父母对高科技婴儿监视器的一点感受。

肯定有更好的方法来自动监控打印机的健康状况吧?

每个女孩都需要好的卡尺和熔丝沉积系统。除了几个明显的例外,我的第一批照片大多完美无瑕。作者图片。

机器学习策略

开发 AI/ML 支持的产品是一个昂贵的命题——在实现任何投资回报之前,有很高的失败风险。

根据 Rackspace 在 2021 年 1 月进行的一项全球研究, 87%的数据科学项目从未投入生产。

为了克服困难,接受调查的公司在机器学习项目上平均花费了 106 万美元。作为一个单独的创始人,我不准备为我的想法花费超过一百万美元!

幸运的是,我是谷歌开发专家项目的一员——这个项目里全是喜欢与世界分享知识的人(T2)。进入这个项目时,我有了一个好主意,如何通过将“无差别的繁重工作”外包给合适的云服务,快速而廉价地构建一个原型。

是什么让机器学习成为一个高风险的赌注?

在 1,870 名 Rackspace 研究参与者中,以下是 AI/ML 项目中报告的当前使用、未来计划和失败原因的总结。

挑战和障碍中有几个共同的主题是显而易见的:

  • 所有数据:数据质量差,数据不可访问,缺乏数据管理,无法以有意义的方式构建/集成数据。
  • 专业知识和熟练人才供不应求。好消息是:你可以在前进的过程中发展技能和直觉。我在这篇文章中涉及的所有内容都可以在没有高级学位的情况下实现!
  • 缺乏支持人工智能/人工智能的基础设施。我将向您展示如何从零开始逐步构建数据和 ML 基础设施。
  • 衡量 AI/ML 商业价值的挑战。

我将向您展示我如何通过在 ML 驱动的产品的每个成熟阶段利用正确的技术和产品选择来实现快速迭代计划,从而战胜困难(不到 1:10 的成功机会)。

我还将展示我是如何做到这一点的而没有牺牲大多数公司花费数百万试图实现的科学严谨性和真实世界的结果质量——所有这些都只是成本的一小部分!

让我们开始吧。🤓

51%的公司在探索 AI /试图将 AI/ML 投入生产。来源: 组织在 AI 和机器学习方面成功了吗?

数据是挑战和障碍的统一主题。来源: 组织在 AI 和机器学习方面是否成功?

机器学习之旅的顶级挑战。来源: 组织在 AI 和机器学习方面是否成功?

58%的受访者表示对人工智能的了解“相当多”,42%的受访者表示“很多”来源: 组织在 AI 和机器学习方面是否成功?

定义问题和机会

有什么问题?3D 打印机太不可靠了。

那是因为 3D 打印机技术还没有成熟!几年前,这项技术还是业余爱好者的领域。在致力于更可靠的制造过程之前,工业应用仅限于快速原型制作。

如今,3D 打印在小规模制造业中得到更多应用。当现有的制造业供应线因为新冠肺炎疫情而消失时,这种趋势加速了。

一个不可靠的工具对于一个业余爱好者来说是一个麻烦,但是对于一个小规模的制造企业来说却是一个潜在的责任

在下一节中,我将概述这个领域中的问题,并强调用 AI/ML 产品解决这些问题的机会。

图片来源:Prusa 3D Print Farm for Nerf Mods(左) Creality 推出传送带 3D 打印机 (右)

是什么让 3D 打印机变得如此不可靠?

  1. 打印工作需要小时(有时才能完成,随时可能失败。需要几乎不间断的人工监控。
  2. 缺乏可靠的工业制造过程的闭环控制
  3. 3D 打印最常见的形式是将材料 加热到 190℃—220℃熔化。如果不注意,会有火灾危险!
  4. 人为失误是一大因素!3D 打印机读取的指令是使用一种称为“切片器”的应用程序,根据手动配置的设置创建的培养对正确环境的直觉需要时间和耐心。

即使在我决定设计一个故障检测器的原型之后,战略思路也没有就此结束。我确定了额外的价值主张,通过快速模拟、描述性文案和调查进行测试。

来源:基于挤压的微流体设备 3D 打印

问题:网络不靠谱!😱

大多数小规模制造商的经营方式是:

  1. 家(地下室、车库、储藏室)
  2. 仓库或工业空间

在世界上的许多地方,来自多个摄像头的持续上传流会使互联网连接饱和——维持这种状态可能是不可能的,也是不经济的。

为了确保 Print Nanny 离线时仍能工作,我在原型中优先考虑了设备上的推断。这让我可以用零模型服务基础设施来测试这个想法,并利用我在小型设备的计算机视觉方面的研究。

我不想让印刷保姆加入这个俱乐部。

问题:即使是失败也不可靠🤪

没有两个 3D 打印失败看起来是一样的!这有许多原因,包括:

  1. 打印机硬件是手工组装的。相同的打印机型号可以产生截然不同的结果!打印统一批次需要专家校准。
  2. 大部分 3D 打印材料都是吸湿(吸水),导致批次变化和缺陷像天气一样易变!🌧️
  3. 许多设置,用于将 3D 模型分割成 X-Y-Z 运动指令。选择正确的设置需要反复试验才能达到最佳效果。

我的机器学习策略需要考虑到快速迭代持续改进来建立客户信任——如果第一个原型不太好也没关系,只要随后的原型显示出改进。

无论如何,为了保持敏捷,我需要避免让我背上特定类别的机器学习技术债务的策略:

改变任何事情都会改变一切(CACE)

CACE 是在机器学习系统中隐藏的技术债务中提出的一个原理,指的是机器学习结果在一个系统中的纠缠。

例如,考虑一个在模型中使用特征 x1,…xn 的系统。如果我们改变 x1 值的输入分布,其余 n-1 特性的重要性、权重或用途都可能改变。无论是以批量方式对模型进行完全再训练,还是允许以在线方式进行调整,都是如此。添加新特征 xn+1 会导致类似的变化,移除任何特征 xj 也会导致类似的变化。没有任何输入是真正独立的。

郑最近对状态 ML 抽象和数据库技术的状态做了一个引人注目的比较[17],指出在机器学习文献中没有任何东西可以接近关系数据库作为基本抽象的成功。

机器学习系统中隐藏的技术债务

我将在下一节解释我如何避开 CACE 和其他一些常见的陷阱,比如不稳定的数据依赖。我还会反思那些节省我时间的抽象概念和那些浪费时间的抽象概念。

图片来源: 3D 打印的错误正在激发一种新的小故障艺术

塑造道路的原型

作为开发可靠产品和机器学习策略的一部分,我“将原型缩小到滑板。”我用这句话来描述从 A 点(问题)到 B 点(客户想去的地方)最简单的运输方式。

我见过机器学习项目因买入/构建完全成型的解决方案而失败,就像下图中的汽车。不仅在早期迭代中收到的反馈是无用的,而且在全面生产运行之前取消也是一个风险。

图片来源:mlsdev.com

最低超赞产品

我没有构建一个全功能的 web 应用程序,而是将原型开发为一个用于 OctoPrint插件。OctoPrint 为 3D 打印机、网络摄像头控制和蓬勃发展的社区提供了一个网络界面。

我将最小的令人敬畏的产品浓缩为以下内容:

  1. 训练模型检测以下物体标签:
  2. 通过 OctoPrint 插件将预测器代码部署到 Raspberry Pi
  3. 计算健康得分趋势
  4. 自动停止不健康的打印作业。
  5. 提供关于打印保姆决策的反馈👍👎

安装打印保姆作为 OctoPrint 插件(左)。打印失败警告(右)。作者图片。

检测标签:打印,筏,喷嘴,意大利面条,粘附。图片作者。

原始数据集

获取高质量的标签数据是任何机器学习项目最难的启动成本!以我为例,发烧友们经常上传延时视频到 YouTube。

解锁的额外效率:我想出了如何使用 TensorFlow.js 模型(从 AutoML Vision 导出)在虚拟对象标记工具中自动建议边界框

我在自动图像注释中解释了如何用很少的预算做到这一点。 🧠

与手工绘制每个方框相比,调整指导模型的建议将每小时标记的图像数量增加了 10 倍

为云 AutoML 准备数据

我经常在原型阶段使用 Google AutoML 产品。

这些产品面向对机器学习知识有限的人,所以有经验的数据科学家可能不熟悉这条产品线。

如果他们完全有能力自己训练一个机器学习模型,为什么有人会为 AutoML 付费?

这就是为什么我倾向于对每个原型都使用 AutoML:

  • 前期固定成本,这比“雇佣自己”要便宜得多

这是我花了多少钱训练打印保姆的基线模型。

我花了两年多的时间。人工智能域名租赁(149 美元)给你一个成本锚。

  • 投资回报很容易实现!根据我的经验,算法/模型性能只是数据产品成功的众多促成因素之一。在投入专家 ML 资源之前发现其他因素是挑选获胜项目的关键部分。
  • 快速结果 —我在 24 小时内就有了一个生产就绪的模型,并针对边缘设备或移动设备的性能进行了优化和量化。

我的初始数据集中的标签分布(左)。Google AutoML 提供了一个 GUI 来探索带标签的数据(右图)。作者图片。

除了云 AutoML 愿景,Google 还为以下领域提供 AutoML 服务:

表格 一组具有自动化特征工程的建模技术(线性、梯度提升树、神经网络、集成)。

翻译— 训练自定义翻译模型。

视频智能— 通过标签对视频帧和片段进行分类。

自然语言

  • 分类—预测类别/标签
  • 实体提取—从发票、餐馆菜单、税务文档、名片、简历和其他结构化文档中提取数据。
  • 情感分析——识别流行的情感观点。

利用 AutoML 自然语言分类发现奖学金(2018 年收购)。图片作者。

训练基线模型

Cloud AutoML Vision Edge 训练了一个针对边缘/移动设备优化的张量流模型。在引擎盖下,AutoML 的架构和参数搜索使用强化学习来找到速度和准确性之间的理想权衡。

查看 MnasNet:实现移动机器学习模型的自动化设计MnasNet:移动平台感知神经架构搜索如果您想了解更多关于云 AutoML 视觉的内部工作方式!

Google AutoML Vision Edge 在神经网络架构搜索的强化学习策略中加入了速度/精度偏好(左)。评估基线模型(右)。作者图片。

基线模型指标

您可以通过 API 获取 AutoML 模型评估指标,这是一种将候选模型与基线进行比较的简便方法。检查这个要点看我的完整例子。

from google.cloud import automl
from google.protobuf.json_format import MessageToDict
import pandas as pd

project_id = "your-project-id"
model_id = "your-automl-model-id"

# Initialize AutoMl API Client
client = automl.AutoMlClient()

# Get the full path of the model
model_full_id = client.model_path(project_id, "us-central1", model_id)

# Get all evaluation metrics for model
eval_metrics = client.list_model_evaluations(parent=model_full_id, filter="")

# Deserialize from protobuf to dict
eval_metrics = [MessageToDict(e._pb) for e in eval_metrics ]

# Initialize a Pandas DataFrame
df = pd.DataFrame(eval_metrics)

设计可改进的结果

你可能还记得,我的基线模型在置信度和 IoU 阈值为 0.5 时的召回率为 75%。换句话说,我的模型未能识别测试集中大约 1/4 的对象。真实世界的表现更差!😬

幸运的是,将基线模型训练交给 AutoML 让我有时间深入思考持续模型改进的制胜策略。经过最初的头脑风暴,我将我的选择减少到只有两个(非常不同的)策略。

选项#1 —二元分类器

第一种选择是训练一个二进制分类器来预测打印机在任何单个时间点是否失败:预测{失败,不失败}。

警报决策基于预测的置信度得分。🔔

选项#2 —时间序列集合

第二个选项训练一个多标签物体检测器检测阳性、阴性和神经标签的混合,如{打印、喷嘴、气泡}。

然后,根据每个检测到的对象的置信度来计算加权健康分数

最后,在健康分数的时间序列视图上拟合一个多项式(趋势线)

警报的决定是基于多项式斜率的方向和距截距的距离。🔔

二进制分类被认为是计算机视觉的“hello world”,有大量使用 MNIST 数据集的例子(对手写数字进行分类)。我个人最喜欢的是 fashion mnist ,你可以在 Colab 笔记本上探索。

无法抗拒一些好的科学,我假设大多数数据科学家和机器学习工程师会选择首先实现一个二元分类器。

我正在开发一个全新的数据产品。为了克服最初的生产高峰,我想马上部署一个模型,并开始根据真实世界的数据测量/改进性能。

如果我正在为快速迭代和改进进行优化,我应该采用哪种方法?

具有复杂结果的二元分类器可能难以解释和说明(左)。决策集合或堆叠模型能够对每个组件的学习/编码信息进行内省。(右)作者图片。

大多数**数据科学家和机器学习工程师投票支持选项 1!

** 等待同行评审和决定的研究🤣

违背群众的智慧:为什么我选择实施选项 2 作为我获胜的机器学习策略的一部分?

选择 1:改变任何事都会改变一切(CACE)

概括地说,二进制分类器预测打印机在任何单个时间点是否失败:预测{失败,不失败}。

该模型学习编码一个复杂的结果,该结果在训练集中有许多决定因素(以及更多模型尚未看到的因素)。

为了改进这个模型,我只能使用几个杠杆:

  • 样品/标签重量
  • 优化器超参数,如学习率
  • 添加合成和/或增强数据

改变以上中的任何一个都会改变所有的决策结果!

选项 2 —全面了解数据

第二种选择有更多可移动的部分,但也有更多的机会内省数据和每个建模的结果。

这些组件是:

  1. 多标签对象检测器(MnasNet 或 MobileNet +单次检测器)。
  2. 健康得分,检测机置信度的加权和。
  3. 健康评分时间序列的拟合多项式(趋势线)。

这种方法不是回答一个复杂的问题,而是从一系列简单问题中构建一个算法:

  • 这个图像框中有哪些对象?
  • 这些物品放在哪里?
  • 与中性或阳性标签相比,“缺陷”标签的可信度如何?该指标是打印健康状况的一个代理。
  • 失败的打印工作的不归路点在哪里?
  • 健康分数保持不变吗?增加?递减?这种变化开始发生的速度(斜率)有多快,什么时候开始的?(y 轴截距)。
  • 采样频率如何影响集合的准确性?我可以不通过网络发送更少的图像帧吗?

每个组成部分都可以被独立地解释、研究和改进——这样做有助于我获得对数据的整体理解,并对与我的业务相关的问题得出额外的结论。

这些额外的信息对我的使命是无价的,我的使命是持续改进建立对我的模型结果的信任

作者图片。检测置信度由标签分解(左)。Heath score 时间序列(中)。累积健康评分系列的多项式拟合(右)。

部署原型

经过不到两周的开发,Print Nanny 的第一个原型发布了,并在几天内就在全球范围内被采用。🤯

我使用了以下工具、技术堆栈和服务来部署概念验证:

提示:学习一个 web 应用程序框架将使你能够在你的目标受众面前测试你的想法。Daniel Feldroy 的两勺 DjangoAudrey fold Roy 是一本关于 Django 框架的实用指南。💜

为下一阶段开绿灯

两周后(花了几百美元),我能够把我的原型放在观众面前,并开始收集反馈。几个虚荣指标:

  • 37k 登录页面点击率
  • 2k 个已关闭的测试版注册
  • 发出 200 份邀请(第一批)
  • 平均 100 分。每日活跃用户——哇!

除了核心故障检测系统,我还测试了非常粗糙的模型,以了解更多与我的观众产生共鸣的功能。

优化结果,增加价值

下一部分重点关注通过提炼模型结果和处理边缘案例,建立对机器学习结果的信任

为持续改进而构建

两种反馈机制用于标记学习机会:

  1. 当检测到故障时:“打印保姆做了一个好的呼叫吗?”👍 👎
  2. 任何视频都可以标记,以便进行额外审查🚩

在这个阶段,我将标记的视频发送到一个队列中,进行手动注释并合并到一个版本化的训练数据集中。

我跟踪关于数据集整体的聚合统计数据标记的示例。

  • 均值、中值、众数、RGB 通道的标准偏差
  • 主观亮度(也称相对亮度
  • 平均精度相对于联合上的交叉点,每个标签的突破,以及小尺寸盒子(总面积的 1/10)与大盒子(总面积的 1/4)之间的平均精度。
    地图 iou = 0.5,地图 iou = 0.75,地图小/大

这让我了解我的模型在某些照明条件、灯丝颜色和边界框大小下是否表现不佳——按标签细分。

限制感兴趣的区域

在我部署了原型后不久,我就从我的故障检测系统的灵活性中认识到了价值。当我开发模型时,我没有考虑到大多数 3D 打印机都是用 3D 打印的组件制造的。🤦‍♀️

我添加了选择感兴趣的区域的功能,并从健康分数计算中排除该区域之外的对象。

“你没有错,但你也绝对不对。”图片作者。

摄取遥测数据

我是如何从的设备上预测的版本化数据集整齐地组织在谷歌云存储中的?

起初,我用 REST API 处理遥测事件。事实证明,对于不可靠连接上的连续事件流,REST 并没有那么好。幸运的是,有一个为物联网用例设计的协议,叫做 MQTT。

我使用云物联网核心管理树莓 Pi 设备身份并使用 MQTT 消息协议建立双向 设备通信

MQTT 描述了三种服务质量(QoS)级别:

  1. 消息最多传递一次— QoS 0
  2. 消息至少传递一次— QoS 1
  3. 消息只传送一次— QoS 2

:云物联网不支持 QoS 2

除了消除管理 MQTT 基础设施的复杂性(如负载平衡和水平扩展),云物联网核心还提供了更多价值:

  • 设备注册表(设备元数据和指纹的数据库)。
  • 使用 HMAC 签名的 JSON Web 令牌(JWT)身份验证。
  • MQTT 消息自动重新发布到发布/订阅主题。

注册一个运行 OctoPrint 的 Raspberry Pi(左)。创建密钥对和 CloudIoT 设备(中间)。CloudIoT 的设备注册表(右图)。作者图片。

在设备遥测消息到达云发布/订阅后,集成到许多其他云组件和服务是可能的。

数据管道和数据湖

Print Nanny 的数据管道是用 Apache Beam 编写的,支持编写流和批处理作业。Beam 是高级编程模型,用 Java (best)、Python (getting there)、Go (alpha)实现 SDK。Beam API 用于构建并行任务的可移植图。

光束有许多执行引擎(称为转轮)。云数据流是一个具有自动水平自动扩展功能的托管运行器。我在本地使用 Beam 的捆绑 DirectRunner 开发管道,然后推送一个容器映像用于云数据流。

我可以轻松地写一整篇关于开始使用 Apache Beam 的博客文章(甚至是系列文章)!如果你有兴趣阅读更多关于使用 TensorFlow Extended (TFX)Apache Beam 编写机器学习管道的信息,请在下面发表评论。

管道流入数据湖。

我的第一个管道(左)相当大——没关系!最后,我将根据需要把它分解成更常见的组件。

该管道的主要功能:

  • 从发布/订阅中读取设备遥测数据
  • 利用设备校准和其他元数据对数据流进行开窗和丰富
  • 包装 TF 唱片和拼花桌
  • 将原始输入和窗口视图写入 Google 云存储
  • 维护数据流的个窗口视图的聚合度量。

右边是一个更简单的管道,它呈现。jpg 文件转换成. mp4 视频。

一个大型 Apache Beam 应用程序将窗口化的数据视图接收到 Google 云存储中,在 Google Cloud DataFlow 的作业图中查看(左)。一个较小的应用程序从 JPEG 帧渲染视频。(对)。作者图片。

附加 AutoML 模型培训

当按比例放大一个原型时,我试图从我的客户的角度了解性能改进的质量影响。没有这个锚,很容易陷入前期优化的陷阱。

根据我的经验,改进绩效指标是容易的部分,难的是理解:

  • 绩效指标/度量标准是实际价值或感知价值的良好代表吗?
  • 如果不是…为什么这个指标不能反映真实世界的价值?我能制定和研究一个更有表现力的度量标准吗?
  • 我什么时候会看到投入时间的收益递减?

我在这里再次利用了 Cloud AutoML Vision,这一次是在来自 beta cohort 和 YouTube 的混合数据集上进行训练。

在这个阶段,我手动分析了数据的片段,以了解系统是否在某些打印机型号材料类型上表现不佳。****

首选灯丝类型(左)。首选 3D 打印机品牌(右)。图片作者。

训练物体探测器

终于!这是真正的机器学习开始的地方,对吗?

来源:机器学习系统中隐藏的技术债务

张量流模型园

我是从 TensorFlow 的模型园开始的。该报告包含许多最新架构的参考实现,以及运行培训工作的一些最佳实践。

回购根据稳定性和支持程度分为几个集合:

  • 官方。由 TensorFlow 官方维护、支持并保持最新的 TensorFlow 2 APIs,针对可读性和性能进行了优化
  • 研究。由研究人员 TensorFlow 1 和 2 实施和支持。
  • 社区。外部 Github 库的精选列表。

参考消息: TensorFlow 的官方vision 的框架正在进行升级!

下面的例子引用了对象检测 API,它是 research 集合中的一个框架。

对象检测 API

TensorFlow 2 检测模型 Zoo 是预训练模型的集合(COCO2017 数据集)。权重是一个有用的初始化点,即使您的问题超出了 COCO 的范围

我对于使用树莓派设备上推断的首选架构是一个 MobileNet 特征提取器/主干,带有一个单发探测头。其中使用的 ops 与 TensorFlow Lite 运行时兼容。

如果你想看实际例子,请查阅快速入门指南

对象检测 API 使用 protobuf 文件来配置训练和评估(如下图)。除了超参数之外,配置还允许您指定数据扩充操作(下面突出显示)。TFRecords 应作为输入。

图片作者。对象检测 API 培训管道配置示例

使用 MLFlow 记录参数、指标和工件

你的实验和实验笔记看起来像这一堆乱七八糟的笔记本吗?这是我将 AnyNet 从脸书研究中心的模型动物园移植到 TensorFlow 2 / Keras 时,我的实验跟踪看起来的样子。

图片作者。笔记本上是一堆乱七八糟的截图、代码片段、指标、手动实验跟踪。

我开始使用 MLFlow 来记录模型超参数、代码版本、度量,并在中央存储库中存储训练工件。它改变了我的生活!

如果你想深入了解我的实验跟踪和训练计算机视觉模型的工作流程,请在下面发表评论!

感谢您的阅读!🌻

建立一个成功的机器学习系统没有放之四海而皆准的指南——不是所有对我有用的东西都保证对你有用。

我的希望是,通过解释我的决策过程,我展示了这些基本技能在哪里支持一个成功的人工智能/人工智能产品战略。

  • 数据架构—与数据的移动、转换、存储和可用性相关的一切
  • 软件工程
  • 基础设施和云工程
  • 统计建模

你对封闭打印保姆测试版感兴趣吗?

点击此处申请测试版邀请

寻找更多针对 Raspberry Pi 和其他小型设备的机器学习实践示例?注册我的时事通讯以接收新的教程并直接深入您的收件箱。

最初发布于bitsy . ai

谷歌通过提供谷歌云信用来支持这项工作

数据科学中的软技能:让我们来讨论一下

原文:https://towardsdatascience.com/soft-skills-in-data-science-lets-talk-about-that-9d0db96ad2d7?source=collection_archive---------12-----------------------

作为一名数据科学家,不仅仅需要技术技能。让我们来看看哪些软技能可以帮助你成为一名伟大的数据科学家

Pixabay

我们都看到了就业公告板,我们都看到了所需技术技能的厄运清单。我在 Spark 见过要求 15 年经验的工作(2012 年才公开发布)。但即使是理性的人也有巨大的期望,所以掌握这些技能可能会令人生畏。许多人,无论是有抱负的数据科学家还是有经验的人,都花时间学习复杂的建模技术…不是说它们不重要,但是,值得注意的是,它们不是一切。

如果不是技术要求,还有什么?

所以,有一只神秘的野兽在成为伟大的数据科学家的过程中起到了关键作用,不…不是深度学习,是软技能!软技能是普通数据科学家与独角兽之间的区别。那些能够理解并帮助利益相关者定义,最重要的是执行清晰、有意义的项目的人。

掌握这些技能中的一部分将不可避免地帮助你成为任何组织中的杰出候选人。值得注意的是,这篇文章包含了我自己的发现和与我在数据科学领域尊敬的人的经历。

谅解

我认识的一些最优秀的数据科学家可能不是最专业的,也不是拥有最著名大学的最佳学位,但他们能够高效、有效地将自己的知识应用于业务问题。

那么,他们是怎么做到的呢?这真的很简单,他们花时间听,再听,最后,再听一些。他们从利益相关者或客户那里获取信息,然后问关键问题…为什么?一般来说,利益相关者都知道自己想要什么,无论是 KPI 仪表板、下一季度销售预测还是其他。这可能简单或容易,但如果我们只是按照别人的要求去做,我们就不会做出最好的作品,更重要的是,不会产生有持久影响的结果。我们需要批判性地理解问题空间,并应用我们的技术经验以尽可能好的方式解决所述问题。如果不倾听和问一些棘手的问题,我们就不会知道最佳解决方案是什么。

有时,利益相关者可能会觉得被问到“为什么”这个问题令人不快,但这是从情感角度理解问题如何影响他们的工作,以及潜在的解决方案如何减轻负担或为最终用户提供价值的最简单方法。“为什么”允许我们深入问题空间,并与我们的利益相关者一起定义一个可能以更有效的方式实现其最终目标的解决方案。

好了,我们知道问题是什么,以及真正的原因;为什么它是一个问题,但是我们如何想出一个解决方案?

产品/行业理解

“知识不等于理解”:了解产品领域是很好的第一步,知道产品有具体的功能,知道客户以特定的方式做某事是另一步。但是,真正理解产品的工作原理,理解客户为什么会有特定的问题是关键。

数据科学家应该努力尽可能全面地了解他们的产品和客户,因为如果他们不这样做,就像在瀑布上游泳一样。你不是鲑鱼!

一旦你头脑中有了僵化的思维方式,有时你就无法改变它,即使你想改变。

你必须强迫一种新的思维方式;这通常是通过走出你的舒适区,并采取新的方法迎面而来!

对行业的理解不是你一夜之间就能改变的,但是你可以改变你的习惯,在这个领域实现更快的发展。与利益相关者合作,站在他们的立场上,与客户交流。

商务商务商务

是的,我们并不都在典型的商业环境中工作,但是如果你在这样的环境中工作,适当的商业头脑会对获得利益相关者的信任和支持大有帮助。

我在各种形式和规模的企业中工作过,从小规模的初创企业到全球巨头,但所有企业都有非数据人员,你需要在整个过程中与他们牵手。这不是一件消极的事情,但它会带着某人踏上一段他们原本会忽略的旅程。

你如何带一个人踏上这段旅程?讲故事!用你的作品创作一个故事有很多方法,我过去用过的一个好方法是从采访技巧中提炼出来的。

受访者使用 STAR 技术来解释一个项目或叙述,从问题到解决方案。

情况

设定场景,并给出项目的必要细节。

工作

描述您的解决方案涵盖了与问题相关的哪些内容。

行动

问题是如何解决的,有没有与结果无关的有趣发现?

结果

分享你的行动取得的成果。

秘密:回顾展

能够定义项目过程中什么进展顺利,什么进展不顺利是关键,尤其是如果你不想在将来犯同样的错误!

这种方法是定义一个故事的非常简单和基本的方法,然而,还有许多更好的方法。

Ling Wong 的这篇文章非常详细地讨论了如何在讲故事时创造叙事性和清晰性:

https://www.gokantaloupe.com/blog/best-techniques-for-data-driven-storytelling

最后,如果你想知道更多关于 STAR 方法的信息,看看这个。是的,这是从面试的角度来看,但你得到了漂移。

https://www.themuse.com/advice/star-interview-method

作为一名团队成员

成为一名团队成员意味着适应你周围的环境。不是每个人都有你的视野或知识范围,但这是值得注意的。你没有他们的!

倾听你的团队,向他们学习,分享你的知识。做到这一点的一个好方法是定期分享你的工作,即使你不是在解决同一个问题。这样做将有助于找出你推理中的漏洞,它们不是用来打击你的,而是让你振作起来,帮助你巩固与利益相关者的信任。对你的团队做同样的事情将有助于建立一个良好的团队纽带,并在项目可能有点停滞不前时推动团队前进。

其次,适应新环境是这一过程的关键部分。适应性将使你和你的团队专注于项目最重要的方面,同时牢记总体目标和范围。重要的是不要用同样的方法来解决问题,因为上次它就奏效了。可能有新或更简单的方法来处理大大改进的结果。对新想法敞开心扉,快速有效地适应需求的变化。请记住,利益相关者并不总是数据人员,他们不知道从数据科学的角度来看,范围的微小变化会产生巨大的影响。最好以务实的态度对待这些变化,并设身处地为利益相关者着想。

好奇心

最后,好奇!提出问题,寻找新的领域或数据盲点。与你的利益相关者坐在一起,真正了解商业环境,最重要的是,学习!不仅仅是技术技能,还要学习客户和利益相关者如何以及为什么做他们所做的事情。

我没有特殊的才能。我只是非常好奇。(阿尔伯特·爱因斯坦)

如果你使用上面列出的一些方法,你将成为一名进步很大的数据科学家。是的,在编程、统计或机器学习方面,你已经花了数年时间磨练自己的技能,但还是要花些时间了解你的客户。

神入

我知道,我知道,我说过最后一节是最后一部分,但换位思考是成为伟大的数据科学家的一个重要因素。许多人认为这一部分是理所当然的,许多人很擅长,许多人不是。设身处地为利益相关者着想,真正理解他们是如何解决问题、克服障碍和克服期限的。作为一名数据科学家,这将有助于您保持积极性,因为您将了解您的新解决方案将如何真正惠及并改善利益相关方的日常工作。

许多人都有这些技能,但是记住,你们是一个团队,和其他人一起解决困难的问题。在团队中一起开发解决方案,你将能够为你周围的人创造一些难以置信的价值。

我仍在致力于我的职业生涯,提高我的技能,并理解成为一名优秀的数据科学家意味着什么(我不确定我是否还在那里!).我还有很长的路要走,我相信这需要时间,但一步一步来,你可以成为一名伟大的数据科学家。

如果有一点可以带走…试着对你想帮助的人敞开心扉。

Python 中的 Softmax 回归:多类分类

原文:https://towardsdatascience.com/softmax-regression-in-python-multi-class-classification-3cb560d90cb2?source=collection_archive---------1-----------------------

从零开始的机器学习:第 7 部分

作者图片

在本文中,我们将研究用于多类分类问题的 Softmax 回归,并在 MNIST 手写数字识别数据集上实现它。

首先,我们将基于逻辑回归来理解 Softmax 函数,然后我们将查看交叉熵损失一键编码,并对其进行编码。最后,我们将对训练函数(**fit**)进行编码,看看我们的精度。我们还将绘制我们的预测。

我们将使用 Python NumPy 和 Matplotlib 来完成这一切。我们将看到如何在训练集和测试集中获得 91% 的

逻辑回归概述

逻辑回归模型;作者图片

正如我们在上面看到的,在逻辑回归模型中,我们取了一个大小为n(特征)的向量x(它仅代表m中的一个例子),并取了一个与weights的点积,然后加上一个bias。我们就叫它z(线性部分)也就是w.X + b。之后,我们应用逻辑回归的 sigmoid 激活函数来计算y_hat

y_hat = sigmoid(z) = sigmoid(w.X + b)

X到直线部分的每条边代表一个weight,直线部分的每一圈都有一个bias

逻辑回归用于二进制分类,这意味着有 2 个类别( 01 ),并且由于 sigmoid 函数,我们得到 0 和 1 之间的输出(y_hat)。我们将逻辑模型的这个输出(y_hat)解释为y为 1 的概率,那么y为 0 的概率就变成了(1-y_hat)

问你一个问题——上图中的w是什么形状?

Softmax 回归

现在,我们为自己设定一个目标— 来识别图像中的数字。

我们将使用 MNIST 手写数据集作为理解 Softmax 回归的激励性示例。它有 10 个等级,每个等级代表从 09 的一个数字。让我们先看看数据集。

keras.datasets加载 MNIST 数据集并绘图。此外,拆分训练集和测试集。它在训练集中有 60,000 个示例,在测试集中有 10,000 个示例。

#Loading
**from keras.datasets import mnist****(train_X, train_y), (test_X, test_y) = mnist.load_data()**#Plotting**fig = plt.figure(figsize=(10,7))****for i in range(15):  
    ax = fig.add_subplot(3, 5, i+1)
    ax.imshow(train_X[i], cmap=plt.get_cmap('gray'))
    ax.set_title('Label (y): {y}'.format(y=train_y[i]))
    plt.axis('off')**

MNIST 数据;作者图片

我们的数据有数字图像和代表数字的标签。每幅图像都是灰度,大小为 28x28 像素

第一个问题— 我们如何将图像作为输入?

每个图像都可以表示为一个三维矩阵。如果我们有一个尺寸为 28x28 的彩色图像,我们就有 28 x 28 x 3 个数字来表示该图像(RGB(红绿蓝)通道的 3 )。

但是我们有灰度图像,这意味着每个图像只有一个通道。所以,每张图片都是 28x28 像素。为了将图像输入到模型中,我们将把这个 28x28 展平成一个长度为 784 (28*28)的向量,这些 784 数字只是代表数据集中每个示例(图像)的**n** 特征的像素。

**X_train = train_X.reshape(60000,28*28)
X_test = test_X.reshape(10000,28*28)**

X_train 是一个具有 60,000 行和 784 列的矩阵。

m = 6 万;n = 784

一键编码

现在,我们的y是一个像这样的向量—

**train_y**
>> array([5, 0, 4, ..., 5, 6, 8], dtype=uint8)

这是一个带有标签的 NumPy 数组。我们不能把它用于我们的模型,我们必须以某种方式把它修改成 0 和 1,这就是我们所说的一键编码。我们希望我们的y看起来像这样(一个大小为 60,000 x 10 的矩阵)—

y 的一热表示;作者图片

行代表第 I 个例子,第 I 列告诉我们标签。例如,对于第一个示例,有一个 1,其中列名为 5,其余的为零。因此,第一个示例的标签是 5,其他示例也是如此。对于每个示例,只有一列的值为 1.0,其余的值为零。

让我们编写一个函数来一次性编码我们的标签—

**c** =班级人数

**def one_hot(y, c):**

    # y--> label/ground truth.
    # c--> Number of classes.

    # A zero matrix of size (m, c)
    **y_hot = np.zeros((len(y), c))**

    # Putting 1 for column where the label is,
    # Using multidimensional indexing.
    **y_hot[np.arange(len(y)), y] = 1**

  **  return y_hot**

引用—NumPy 中的多维索引

Softmax 函数

在使用 Softmax 回归进行多类分类时,我们有一个约束,即我们的模型将只预测**c** 类中的一个类。对于我们的数据,这意味着模型将只预测图像中的一个数字(从 0 到 9)。

我们将逻辑模型的输出解释为概率。类似地,我们希望将多类分类模型的输出解释为概率分布。因此,我们希望我们的模型输出一个大小为**c** 的向量,向量中的每个值代表每个类的概率。换句话说,向量中的第值代表我们的预测成为第类的概率。由于它们都是概率,所以它们的和等于 1。

为了符合上述假设,我们使用 softmax 函数。

的 softmax 定义为—

Softmax 函数;作者图片

其中,**z** 是直线部分。例如**z1 = w1.X + b1**,对其他人也是如此。

y_hat = softmax(w.X + b)

对于我们的数据,**c**(类的数量)=10。

让我们借助下面的模型图来理解 Softmax 函数和 Softmax 回归。

Softmax 回归模型;作者图片

  1. 首先,我们将我们的 28x28 图像展平成一个长度为 784 的向量,在上图中用x表示。
  2. 其次,我们计算每个类的线性部分→ zc = wc.X + bc,其中zc是第类的线性部分,而wc是第类的权重集。bc是对第 c 类的偏向。
  3. 第三,我们使用上面的公式计算每个类的 soft maxzc。组合所有的类,我们得到一个大小为c的向量,它们的和等于 1。而哪个类的值(概率)最高,就成为我们的预测。

请注意,每个类别都有不同的权重集和不同的偏差。

还要注意,上图显示的 **x** 只是一个例子。

每个zc 都是一个(1,1)矩阵。但是因为我们有了m的例子,我们将把zc(第 c 个类的z)表示为一个大小为m的向量。现在,为了组合所有的**z** 类,我们将把它们并排放在一起,这将给出一个大小矩阵(mc)。

类似地,对于每个类,weights的集合是一个大小为n的向量。因此,为了组合所有的类,我们将把它们并排放在一起,这将给出一个大小矩阵(nc)。此外,我们对每个类都有一个偏差,组合偏差将是一个大小为c的向量。

结合 softmax 的所有类给出了一个大小为c的向量。并且结合所有的m例子给出一个大小的矩阵(mc)。

形状—

  1. X →(m,n)
  2. y →(m,c)【一个热编码】
  3. w →(n,c)
  4. b→大小为 c 的向量

让我们为 softmax 函数编写代码。请参见注释(#)。

**def softmax(z):**

    # z--> linear part.

    # subtracting the max of z for numerical stability.
    **exp = np.exp(z - np.max(z))**

    # Calculating softmax for all examples.
    **for i in range(len(z)):
        exp[i] /= np.sum(exp[i])**

 **return exp**

reference—数值稳定的 softmax

reference—soft max的导数

交叉熵损失

对于每个参数机器学习算法,我们需要一个损失函数,我们希望最小化该函数(找到其全局最小值),以确定最佳参数(wb),这将帮助我们做出最佳预测。

对于 softmax 回归,我们使用交叉熵(CE)损失—

CE 损耗;作者图片

参考如何计算损失的导数。

参考— 与 Softmax 交叉熵损失的导数

reference—soft max 损失函数的导数

在代码中,损失看起来像这样—

**loss = -np.mean(np.log(y_hat[np.arange(len(y)), y]))**

再次使用多维索引—NumPy 中的多维索引

注意y在损失函数中不是一键编码的。

培养

  1. 初始化参数— wb
  2. 使用梯度下降法找到最佳的wb
  3. softmax(w.X + b)预测。
**def fit(X, y, lr, c, epochs):**

    # X --> Input.
    # y --> true/target value.
    # lr --> Learning rate.
    # c --> Number of classes.
    # epochs --> Number of iterations.

    # m-> number of training examples
    # n-> number of features 
    **m, n = X.shape**

    # Initializing weights and bias randomly.
    **w = np.random.random((n, c))
    b = np.random.random(c)** # Empty list to store losses.
  **  losses = []**

    # Training loop.
    **for epoch in range(epochs):**

        # Calculating hypothesis/prediction.
        **z = X@w + b
        y_hat = softmax(z)**

        # One-hot encoding y.
       **y_hot = one_hot(y, c)** 
        # Calculating the gradient of loss w.r.t w and b.
       ** w_grad = (1/m)*np.dot(X.T, (y_hat - y_hot)) 
        b_grad = (1/m)*np.sum(y_hat - y_hot)**

        # Updating the parameters.
       ** w = w - lr*w_grad
        b = b - lr*b_grad**

        # Calculating loss and appending it in the list.
       ** loss = -np.mean(np.log(y_hat[np.arange(len(y)), y]))
        losses.append(loss)** # Printing out the loss at every 100th iteration.
        **if epoch%100==0:
            print('Epoch {epoch}==> Loss = {loss}'
                  .format(epoch=epoch, loss=loss))** ** return w, b, losses**

现在让我们训练我们的 MNIST 数据—

请参见注释(#)。

# Flattening the image.
**X_train = train_X.reshape(60000,28*28)**# Normalizing. 
**X_train = X_train/255**# Training
**w, b, l = fit(X_train, train_y, lr=0.9, c=10, epochs=1000)
>>** Epoch 0==> Loss = 4.765269749988195
Epoch 100==> Loss = 0.41732772667703605
Epoch 200==> Loss = 0.36146764856576774
Epoch 300==> Loss = 0.3371995534802398
Epoch 400==> Loss = 0.32295154931574305
Epoch 500==> Loss = 0.31331228168677683
Epoch 600==> Loss = 0.3062124554422963
Epoch 700==> Loss = 0.3006810669534496
Epoch 800==> Loss = 0.29619875396394774
Epoch 900==> Loss = 0.29246033264255616

我们可以看到,每次迭代后损失都在下降,这意味着我们做得很好。

预测

请参见注释(#)。

**def predict(X, w, b):**

    # X --> Input.
    # w --> weights.
    # b --> bias.

    # Predicting
  **  z = X@w + b
    y_hat = softmax(z)**

    # Returning the class with highest probability.
    **return np.argmax(y_hat, axis=1)**

计量精度

**def accuracy(y, y_hat):
    return np.sum(y==y_hat)/len(y)**

让我们在训练和测试集上计算我们的模型的准确性。

# Accuracy for training set.
**train_preds = predict(X_train, w, b)
accuracy(train_y, train_preds)
>> 0.9187666**# Accuracy for test set.# Flattening and normalizing.
**X_test = test_X.reshape(10000,28*28)
X_test = X_test/255****test_preds = predict(X_test, w, b)
accuracy(test_y, test_preds)
>> 0.9173**

我们的模型在训练集上的准确率为 91.8%,在测试集上的准确率为 91.7%。

不错。

绘制预测

**fig = plt.figure(figsize=(15,10))****for i in range(40):  
    ax = fig.add_subplot(5, 8, i+1)
    ax.imshow(test_X[i], cmap=plt.get_cmap('gray'))

    ax.set_title('y: {y}/ y_hat: {y_hat}'
                 .format(y=test_y[i], y_hat=test_preds)
    plt.axis('off')**

预测;作者图片

上图显示了模型对训练集中 40 个示例的预测。我们可以看到其中有一些是预测错误的,比如上图中用红色圈出的那个。

感谢阅读。对于问题、评论、顾虑,请在回复部分进行讨论。更多的 ML 从零开始即将推出。

看看从零开始学习的机器系列—

软件 3.0——提示将如何改变游戏规则

原文:https://towardsdatascience.com/software-3-0-how-prompting-will-change-the-rules-of-the-game-a982fbfe1e0?source=collection_archive---------19-----------------------

人工智能

GPT-3 点燃了一场新的编程革命的火焰。

亚历山大·辛恩在 Unsplash 上的照片

2017 年Andrej Karpathy——人工智能领域的大腕之一,现任特斯拉人工智能总监——发表了一篇帖子,标题很简单:“软件 2.0。”由于卡帕西的受欢迎程度和他为之辩护的争议性观点,这篇帖子迅速走红。

他认为,神经网络的破坏比大多数人想象的更深,因为它们在许多方面取代了传统的编码。他声称神经网络不仅仅是一种简单的分类技术,而是一种全新的编程范式。

你可能同意或不同意他的观点,但不可否认的是,深度学习已经走得很远了。去年,随着 GPT-3 的出现,出现了另一种编程范式。科技博客作者 Gwern Branwen 称之为的即时编程将改变游戏规则。进入软件 3.0。

软件 3.0:提示编程

OpenAI 的 GPT-3 在 2020 年以其惊人的表现震惊了人工智能世界。它可以按照英文说明编写代码或吉他乐谱。它可以保持对话或者写诗。它可以思考未来或生命的意义。所有这些都没有经过训练。GPT-3 是一个多任务元学习者。

它最具创新性的特点之一是我们可以与它交流的方式。我们可以用自然语言写一个带有任务示例的文本(提示),它会“理解”我们想要的。例如,带有提示:“我爱你→ Te quiero。我有很多工作→ Tengo mucho trabajo。GPT-3 是有史以来最好的人工智能系统→ _____" GPT-3 会知道我想要一个英语-西班牙语翻译。

Gwern 认为,提示可以更好地理解为一种新的编程范式:

"【提示】是使用 DL 【深度学习】模型的一种相当不同的方式,最好把它看作一种新的编程,其中提示现在是一个“程序”,它对 GPT-3 进行编程以做新的事情。"

每次我们提示系统做一个任务时,我们都在创建一个稍微不同的版本;一架特制的 GPT-3。我们正在对它进行“编程”,让它完成一项其他版本的 GPT 3 号不知道如何完成的任务。

然而,如果我们给它编程不好,它将很难完成任务。一些人批评 GPT-3 缺乏逻辑和常识性推理能力,但格文认为,糟糕的提示是 GPT-3 失败的原因。为了展示科学的严谨性,他重复了测试,改变了提示,直到他给 GPT-3 号足够的“编程”来学习任务,证明批评者错了。

他证明了良好的提示是揭示 GPT-3 真正潜力的关键。像其他形式的编程一样,提示可以通过良好的实践来改进和标准化。具有讽刺意味的是,人们在系统中发现的局限性往往是他们缺乏技能的结果。

如果我们想从一个系统中获得特定的行为,我们必须学会与它交流。打个比方,如果你用 Python 或 C 语言编码,你必须知道语法。否则,你写的任何程序都会失败。然后你会责怪计算机,得出结论说计算机不能完成这项任务。

有两个理由可以将提示视为一种编程范式:

  • 首先,提示允许用户从系统中生成一个非常具体的行为,这就是编码的定义。给定一个输入,你希望程序按照一组指令以特定的方式运行。
  • 第二,有好有坏的提示。我们可以定义最佳实践并标准化方法。在传统的编码中,我们必须学习语言语法、变量操作、方法等。对于神经网络,我们必须学会从数据集中去除偏见和噪声。提示并没有什么不同。

如果提示编程可以理解为软件 3.0 ,这又把传统编码(软件 1.0)和神经网络(软件 2.0)留在了哪里?提示会取代这些编程范例吗?程序员必须将提示作为一项新技能添加到他们的工具包中吗?它会改变我们与机器互动的方式吗?

它既不会取代编码,也不会取代神经网络

去年,Frederik Bussler 为面向数据科学的写了一篇受欢迎的文章,认为 GPT-3 可能是走向我们所知的编码终结的另一个垫脚石(以及无代码和 autoML 等趋势)。

即使 GPT-3 能生成好的代码,它的能力也将极大地依赖于我们用明确定义的提示来传达我们的意图的能力。使用 GPT-3 不太可能取代我们对某些事情编程的简单性。就像用大锤砸坚果一样。

神经网络也是如此。Karpathy 认为,软件 2.0 将取代软件 1.0 的某些方面,这在某种程度上是正确的(一些问题很难通过显式编写程序来解决,相反,为神经网络收集数据以学习我们想要生成的特定行为非常容易),但对于许多问题来说,硬编码仍然是首选方法。

软件 3.0 将被应用来执行一些任务,但是它将覆盖的问题空间不会 100%包含软件 1.0 或 2.0 的问题空间。将会有重叠,但是每一个范例最终都会停留在它们比其他范例表现得更有效率的领域。他们会找到稳定的和谐。这些软件范例并不相互竞争,而是试图在完美的共生关系中找到自己的位置。

最终,它们都可能是实现真正人工智能的必要组成部分。我们,人类,是由生物软件组成的,在某些方面对应于每一种范式。让我们看看如何理解这个类比。

软件 1.0、2.0 和 3.0 —了解全貌

“[软件 2.0]的未来是光明的,因为越来越清楚的是,当我们开发 AGI 时,它肯定会用软件 2.0 来编写。”

安德烈·卡帕西

我不同意卡帕西的预测。 AGI(人工通用智能)将从三种编程范式(或许还有其他尚未发现的范式)的结合中出现。理解原因的最好方法是与真正智慧的唯一实例:我们做一个类比。

软件 1.0

在 21 世纪初神经网络成名之前,所有代码都是手写的。如果你想让一个系统有一个特定的行为,你必须写下它在每种情况下必须遵循的每条指令。硬编码的程序完全按照程序来做。没有黑盒,就没有学习。

这正是进化创造遗传密码时的想法。当我们出生时,我们并不是一张白板,我们带着一套根植于大脑中的硬连线模块来到这个世界,因为我们的基因是这样说的。那是我们的生物软件 1.0。我的基因编码表明我有两只眼睛、两只胳膊和两条腿,我是金色的,有一双棕绿色的眼睛。

软件 2.0

卡帕西说,软件 2.0 涉及数据集的管理和神经网络架构的设计。我们定义程序的结构和我们要喂给它的“食物”,但不是内容。是神经网络本身“编写”了它的内部“程序”(权重),来学习我们想要的行为。

生物软件 2.0 是我们成长过程中的经历和感知。我们生来就是一个半空的结构,等待着被灌输各种各样的物理和社会信息。我们可能天生就有两只胳膊,但我们是否会学习西班牙语或英语,我们何时会学会走路,或者我们会在多大程度上学会理解世界是如何运转的,都不是硬编码在我们的基因中的。它取决于环境和我们与世界的互动,这是我们用来优化内在程序的数据集。

软件 3.0

提示允许我们将 GPT 3 号设定为“特殊化”从某种意义上来说,当我们提示 GPT-3 时,我们正在指导它去做一个系统的其他实例还没有学会的任务。每个 GPT-3 实例都有学习任务的潜力,但只有当我们提示它时,它才会发生。

生物软件 3.0 将是我们成年后经历的特定学习:我们被教授的任务、我们阅读的书籍、我们从事的工作、我们发展的能力和技能。可以说,我们所有人都有类似的潜力来学习这些,但我们每个人都选择学习一些技能而不是其他技能,或者获得一些知识而忽略其他知识。这些选择改变了我们的大脑,就像提示改变了 GPT 3 号的能力。

值得注意的是,这个类比虽然具有说明性,但却是对现实的简化。我们的生物软件实际上在各个层面上相互作用。我们的基因取决于环境激活与否。我们学习工程或医学的能力取决于我们是否拥有先天的能力。然而,通过这个类比,我们意识到 AGI 很可能需要各种各样的软件范例。

它需要传统的编码来定义基本的、不可改变的结构元素。它需要神经网络(或其他深度学习框架)从外部和内部数据中学习。它需要提示才能直接与我们互动并获得专业知识。

正如 Karpathy 所说,AGI 不会建立在软件 2.0 中,相反,它会从每个范例中获取所需的东西。AGI 将会有一个人工基因组,将会感知世界,并将成为一个元学习者。它需要一个身体来进行实验,并与世界积极互动。这正是发展机器人领域试图通过结合人工智能、机器人技术和认知科学所做的事情。

声明:本文为个人观点。人工软件和生物软件之间的类比在某些时候可能有些牵强。我欢迎任何辩论!

推荐阅读

数据科学家的软件工程最佳实践

原文:https://towardsdatascience.com/software-engineering-best-practices-for-data-scientists-4c199ede6e03?source=collection_archive---------5-----------------------

如何弥合数据科学和软件工程之间差距的速成课程

照片由来自 Pexels 的 Joonas kri inen 拍摄

直到我开始自己编写数据分析代码,我才真正理解为什么人们会抱怨数据科学家产生的代码。

我接受过软件开发方面的教育,我觉得我很好地掌握了编码最佳实践,并且我对自己编写干净代码的能力很有信心。然后是我写第一份数据分析的时候了。

你猜对了。

缺少函数、不清楚的变量名、杂乱无章的代码、没有一点单元测试的迹象,以及严重缺乏风格,在我让代码工作后,留给我的是相当于狗的早餐的东西。

最后,我终于明白了那些工程师在抱怨什么。

数据科学不是一个自然源自计算机科学的领域,它反映在数据科学家拥有的各种背景中。许多数据科学家甚至没有计算机科学学位,因为他们通常来自其他不相关的领域,包括数学、科学、工程、商业、医学等。

因此,难怪数据科学家不总是拥有最干净的代码。

我不是说数据科学家需要能够编写整个复杂的库。相反,数据科学家应该能够产生干净的代码,这些代码可以更新、调试,并在工程部门很少骂人的情况下转移到生产环境中。

编写干净代码的重要性也不仅仅是为了他人。在许多公司,预算不足以聘请数据科学家软件工程师,这意味着数据科学家通常负责创建生产就绪的代码。因此,对于维护和开发干净的、易于使用的和可重用的代码来说,一个人的理智应该算作一个因素。

许多人会认为好的代码是主观的。然而,通常有四个大家都同意的标准,它们定义了使用最佳实践编写的代码:

  1. 好的代码应该是高效的——这意味着即使没有速度和效率,也要从 Python 代码中挤出一点点速度和效率。
  2. 好的代码是可维护的——这意味着您可以维护代码,并且其他人可以很容易地理解和维护您的代码。
  3. 好的代码是可读的,结构良好的——这意味着任何人都应该能够看到你的代码,理解你想要完成什么,而不必太努力。
  4. 好的代码是可靠的——干净的代码是好的代码,好的代码是可靠的,不容易出现错误或随机故障。

查看这些最佳实践,您可以实施它们来达到上述四个标准。

使用描述性的变量名。

我经常看到数据分析代码使用变量名编写,比如 x 或 y,或者任何数量的数学变量的英文措辞。

虽然这在写出数学公式时是可行的,但当你在别人的代码中阅读时就不那么清晰了。

谈到数据科学,我喜欢确切地说我的变量是什么。例如:

null_hypothesis
standard_deviation_paired_differences_population
degrees_of_freedom
observed_frequency
test_statistic_chi_squared

任何上过统计学课程的人都能够理解我的变量是什么,以及它们在我的方程中的作用。在具体计算或使用时,这些基本变量可以变得更具描述性。

诀窍是在情况允许的情况下尽量描述。当然,会有计算如此复杂的情况,以至于编写这样的描述性变量没有意义。然而,在那之前,试着描述一下。

使用函数。

函数,不考虑你的编程方法(面向对象的,函数式的,等等)。),对于保持代码的干净、简洁、易读和干爽是至关重要的(我将在后面谈到)。

对于非计算机科学毕业的数据科学家来说,函数并不总是直观的,因为没有它们代码也能正确运行。

然而,最好的程序员据说是最懒的程序员。为什么?因为他们编写最少的代码,而通常编写最干净的代码来产生解决方案。他们尽可能少地工作以使他们的代码工作,并且他们经常产生最简洁的解决方案。这涉及到函数的使用。

以下是一些关于如何编写保持代码整洁的优秀函数的提示:

  • 该函数应该只做一件事。不是 10。不是 20。只有一件事。
  • 功能应该很小。有些人认为函数包含的代码不应该超过 20 行,但这是一个任意的数字。尽你最大的努力保持你的功能简明扼要。
  • 函数应该这样编写,这样你就可以自顶向下地阅读它们并理解它们的逻辑——就像你如何阅读一本书一样。
  • 尽量保持函数参数的数量最少(最好少于 3 个)。如果你需要三个以上的函数参数,问问你自己这个函数是否只完成一个任务。
  • 确保函数中的逻辑被正确缩进,并且整个函数的代码被正确阻塞。这将有助于其他人看到函数内部的代码,以及函数开始和停止的位置。
  • 使用描述性名称。

使用注释并编写适当的支持文档。

当你正在构建改变生活的模型时,很容易忽略这一步,但事实是,如果没有其他人可以使用它们,那么它们真的能改变生活吗?

我个人评论的经验法则是:

  • 在代码文件的顶部写一条注释,简要描述代码的目标。
  • 在每个函数的顶部写一个注释,描述它的输入、输出和它执行的逻辑。
  • 在我不完全理解的任何逻辑的顶部写一个评论,这样我可以更好地组织我的想法。这有助于调试和代码重构。

当涉及到编写适当的支持文档时,您的文档不必比自述文件更复杂。最基本的,好的文档应该包括:

  • 代码的目标。
  • 关于如何安装和使用代码的说明。
  • 用长格式解释代码中棘手的部分,花时间逐行准确地描述代码做了什么,甚至可能是为什么你选择这样写代码。
  • 屏幕截图有助于调试和故障排除。
  • 指向外部支持文档的有用链接,这些文档可以进一步描述和解释您的部分逻辑或代码。

使用一致的编码风格,并熟悉您使用的语言的语法约定。

我犯了一个错误,对于一种给定的语言,我并不总是遵循正确的惯例。

当我在大学里主要学习用 C#编码后,我对它的惯例变得熟悉和舒适,然后我继续非常懒散地将那些相同的惯例应用到我遇到的每一种其他语言。

不要像我一样。相反,花时间学习每种新语言的语法约定,并强迫自己正确使用这些约定。这不仅能帮助你沉浸在语言中,还能帮助你写出更简洁的代码,并帮助你与使用相同语言的其他开发者交流。

下面是一个完成相同功能的代码示例,它使用两种不同的语言和相应的约定编写(请记住,这种格式不会导致缩进):

C#:

// This is a comment in C#. I am going to write some code that prints out a value if the inputted number is correct.int year = 0;
Console.Write("\nEnter the year that C# became a language: ");
year = Convert.ToInt32(Console.ReadLine());if(year == 2000) 
{
Console.Write("That is correct!");
}
else 
{
Console.Write("This is incorrect.");
}

Python:

# This is a comment in Python. This code is going to do the same job that the code above just did.year = int(input("Enter the year that Python became a language: "))if year == 1989:
print ("That is correct!")
else:
print ("That is incorrect.")

这段愚蠢的代码实际上并没有做太多事情,但是它让您了解了每种语言的约定有多么不同。

使用图书馆。

使用预先存在的库可以节省大量时间,尤其是在编码数据分析时。Python 充满了可以处理数据科学家可能抛出的每个请求的库。这些库不仅已经为您编写好了,而且已经调试好,可以投入生产了。

查看这篇概述了顶级数据科学库及其能力的文章。我在这里总结了一下,重点放在我发现最有用的七个库上:

NumPy

  • 基本数组操作:加、乘、切片、展平、整形、索引。
  • 先进的数组操作:堆栈数组,分为部分。
  • 线性代数。

熊猫

  • 索引、操作、重命名、排序和合并数据框。
  • 数据框中列的基本 CRUD(更新、添加和删除)功能。
  • 输入丢失的文件并处理丢失的数据。
  • 创建直方图或箱线图。

Matplotlib

  • 所有数据可视化,包括折线图、散点图、条形图、直方图、饼图、柱状图和光谱图等等。

张量流

  • 语音和声音识别。
  • 情感分析。
  • 人脸识别。
  • 时间序列。
  • 视频检测。

海生的

  • 确定变量之间的相关性。
  • 分析单变量或双变量分布。
  • 绘制因变量的线性回归模型。

SciPy

  • 普通科学计算,包括线性代数、插值、统计、微积分和常微分方程。

sci kit-学习

  • 用于分类、聚类、回归、维度、模型选择和预处理的监督和非监督学习算法。

使用好的 IDE。

Jupyter 并不打算编写生产就绪的代码。

虽然有很多方法可以让 Jupyter 笔记本创建生产就绪代码,但我发现在数据探索和分析的早期阶段使用它更容易,然后在适当的 IDE 中开发生产级代码。

ide 附带了一些简单的工具来帮助保持代码的整洁,比如代码林挺、自动格式化、语法高亮、查找功能和错误捕捉。此外,有大量的 ide 扩展可以帮助您节省编写代码的时间。

您可以查看的一些 ide 包括:

保持你的代码干燥。

DRY(不要重复自己)是一种软件工程最佳实践,旨在保持您的代码干净、简洁、切中要点。

目标是不重复任何代码。这意味着,如果你注意到你一遍又一遍地写着相同的代码行,你需要把这些代码变成一个你只写一次的函数。然后可以多次调用该函数。

如果你在保持代码干燥方面有困难,就使用我最喜欢的规则:

  1. 如果这是第一次,就编码。
  2. 如果这是第二次,那就抄一遍。
  3. 如果这是第三次,就把它做成一个函数或者一个类。

编写单元测试。

单元测试是一种软件测试,包括测试代码中的单个组件。编写单元测试是为了确保每段代码都能按预期执行。编写测试是为了测试一小部分执行特定功能的代码,以确保它准确地完成工作。被测试的“单元”可以是单独的功能、过程或整个对象。

单元测试的一个很好的例子是测试一个函数在接收到不同的参数类型时是如何响应的。这个测试的结果将确保您已经为每一个可能发生的事情做好了计划,并且函数不会在以后抛出任何无法解释的错误。

查看这篇由微软撰写的关于如何编写单元测试的文章:

https://docs.microsoft.com/en-us/dotnet/core/testing/unit-testing-best-practices

虽然本文只描述了如何用 C#编写单元测试,但是这些经验和例子适用于任何语言。

不要沉迷于一行解决方案。

如果你想给你的朋友留下深刻印象,或者如果你想在媒体上写一篇病毒式的文章,一行程序是很好的解决方案。谁不想用一行代码炒股呢?

虽然使用一行代码创建解决方案令人印象深刻,但在调试和重构代码时,您也将自己暴露在一个充满伤害的世界中。

相反,尽量保持每行一两个函数调用,并确保任何阅读您代码的人都容易理解您的逻辑。如果你不能用简单的术语解释一行代码在做什么,你可能已经把这一行弄得太复杂了。

必须指出的是,开发人员编写的代码行数并不能很好地表明他们的能力或实力——不要听任何人告诉你的其他情况。

最后的想法。

本文强调了数据科学家可以实施的最重要的最佳实践,以使他们的代码可以投入生产。

你的代码是你的责任,所以花一些时间来确保你写的代码是有效的、简单的和容易理解的是有意义的。虽然有很多其他的技巧和窍门来编写干净的代码,但是上面列出的是你可以从今天开始应用的最简单的一些。

软件工程:有效入门的技巧

原文:https://towardsdatascience.com/software-engineering-tips-for-effectively-on-boarding-33b090f1115d?source=collection_archive---------16-----------------------

如何加入一个团队并成为一名成功的新软件工程师

安德鲁·戴维在 Unsplash 上的照片

作为一名软件工程师加入一个新团队可能会令人生畏。在这么短的时间内有这么多东西要学。压力可能会很大,甚至在您开始之前,功能发布的最后期限就要到了。在许多不同的技术团队工作过之后,我在入职期间犯了许多错误。我写这篇文章是为了有效地帮助新工程师,避免我犯的错误。有时新工程师会专注于尽可能快地构建东西,但是很多时候我们会忘记创建软件的社会/人的因素。

请记住,代码审查通常是由人(有时是机器)进行的。因此,在使用键盘之前,了解你的团队、文化和动态是一个好主意。开始为开发工作做准备同样重要。我整理了一份软件工程的软件和技术方面的清单,可以帮助你更有效地上船。我将首先从软件方面开始,然后讨论技术方面。

N 注:每个部分将有一个名称,或者是第一天、第一周、第一个月、或者是第一年,并按优先级组织。

柔软的

AksonUnsplash 上拍照

认识到你作为工程师的缺点——第一周

没有人是完美的,了解自己的弱点很重要。我有时缺乏对细节的关注,并且很难优先安排我的时间。清楚简洁地陈述这些缺点可以帮助我在日常生活中解决这些缺陷。但是仅仅承认是不够的,我们还应该练习减少这些弱点。知道我有时缺乏对细节的关注,在提交 PR 之前,我会考虑边缘情况两三次检查我的代码。如果没有高级工程师推荐的修复(这种情况很少发生),我会很满意。然而,这些都是职业生涯的目标,我不指望一夜之间就能解决。这需要持续的努力和耐心。

根据技术市场趋势获得正确的体验—第一个月

通常,在面试过程中,你已经与招聘经理确定了一个大致的范围,并了解你将从事什么项目,但无论如何,你都应该积极倡导与市场趋势一致的项目。例如,我不想写 COBOL 代码,因为这不会推进我的职业生涯。我感兴趣的未来工作都不需要 COBOL 编程语言知识。因此,我花在编写 COBOL 上的任何时间都不一定与我的长期目标一致。最终,在这些新岗位上获得的经验必须为你的未来做好准备。因此,重要的是要考虑你预期的未来技术,如云计算、Docker 容器、Terraform、Python 等。然后提倡用这些技术做项目。

另一方面,你不能期望只研究你感兴趣的技术。我总是把我和我的雇主的关系看作一个维恩图,那里有我的兴趣,他们的兴趣,希望有很多重叠。如果我想使用 Python,我的雇主也想使用,那么这就代表了利益的重叠。但是如果我的雇主想让我使用 windows 系统,而我想使用 Linux 系统,那么这就不是我们共同利益的一部分。应该有重要的共同兴趣,如果没有,那么你应该找一份新工作,或者和你的经理谈谈让项目更符合你的兴趣。

了解你的团队—第一个月

软件工程是一项团队运动,因此了解你的团队至关重要。我参与的几乎每个重大项目都需要协作。有时预先存在的代码对于维护或支持很重要。希望,如果代码没有被注释,并且你不理解逻辑,那么你需要安排一次与创作工程师的会议。也许这位创作工程师最近有了孩子,只能在上午晚些时候或下午早些时候进行同步。我并不提倡在初次见面时问私人问题,但了解同事的个人情况可能很重要。

了解你同事的动机和兴趣也很有用。例如,我的同事喜欢编写单元测试,而其他人则喜欢自动化平凡的任务。下一次,我要么知道该向谁征求另一个意见,要么接受审查。此外,你也应该把你的兴趣告诉你的同事。展示你感兴趣的专业知识会带来更多类似的项目。这样,表达你的兴趣以及理解你同事的兴趣是很重要的。

寻找导师(技术和商业/职业)——第一个月/第一年

当开始一个新职位时,我建议与你的经理或团队领导交谈寻找一位技术和业务导师。技术顾问可以帮助您浏览堆栈,理解架构图、部署流程等。这个人通常在你的团队中,但是根据你的组织,他们也可能在另一个姐妹或兄弟团队中。商业导师会帮助你理解更大的图景和更广泛的商业目标。理想情况下,这个人在工程组织内,但也了解公司领域。这个人可能是董事、产品经理、高级工程经理等。软件最终会解决业务问题,理解你的代码能完成什么是很重要的。

应该先找技术导师,再找商业/职业导师。我会组织一个宽松的双月节奏,最多 30 到 45 分钟。我认为这些会议是职业治疗。取决于我在和谁说话,我可能会发泄不同的事情。我可能会和我的技术导师讨论某个特定项目的一些挫折。当与我的商业/职业导师交谈时,我会头脑风暴项目来推销和更具体地思考我的长期目标。

理解分配给你的项目背后更广泛的动机——第一年

理解一个项目背后的更广泛的动机是有益的,原因有很多。当你面试未来的职位时,招聘人员或招聘经理通常会问你做过的项目,了解你工作背后的更广泛的目的会显示出你的深思熟虑。换句话说,你不想表现得像一架执行命令的无人机。例如,“我编写了一个脚本来测试我们内部 API 的限制”,而不是说“为了更好地了解内部用户体验并减少创建计算实例时不必要的限制,我们编写了一些脚本来测试创建此类资源的限制”。

如果适用,您还可以围绕您的测试和可能的解决方案添加一些财务或[服务级别指标(SLIs)](https://www.atlassian.com/incident-management/kpis/sla-vs-slo-vs-sli#:~:text=An SLI (service level indicator,actual measurement of your uptime.) :“在测试我们的 API 后,我们注意到由于节流,从创建请求到创建资源有 15 分钟的延迟。”记住,时间就是金钱。如果某件事花费了过多的时间,那么这就代表了成本。围绕更大的画面有背景将帮助你讲述一个更好的故事,也理解你工作背后的目的。如果成就显著,这些也可以成为你简历中的亮点。

技术

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

设置您的本地环境—第一天

优先考虑设置您的本地环境对于高效的未来开发工作至关重要。过去,我会推动设置我的本地环境,因为我认为我会在需要时这样做。但是在计算工作成本和启动项目时,花费最初的时间来设置您的本地机器将会节省大量的时间。如果有人比你稍早加入团队,利用他们的背景知识。

让你的团队告诉你什么是重要的,什么是次要的。我还鼓励团队中的其他成员使用相同的集成开发环境(IDE)。基本上,我会模仿他们在本地机器上配置的任何东西,即使你不一定适应。我犯了一个错误,在我的团队中没有人熟悉的时候,我使用了 Jupyter 笔记本,并且在推送和审查代码时出现了问题。

创建一个简单的拉动式请求(PR)来理解流程——第一周

适应新的团队开发环境的良好第一步是创建一个琐碎的 PR 。在这里,添加一个评论或改变代码格式或一些简单的东西来证明一个公关。除了提交代码之外,还要研究如何检查语法,是否有本地测试环境,以及可能如何将这些更改部署到生产环境中。这些只是一些值得问的问题。了解代码是如何被审查的,以及合并到 master 需要什么批准也是至关重要的。

第一周收集资源列表(合流、闲置渠道、主题专家(SME)联系信息、IT 人员)

您所面临的问题很有可能是您的团队、组织或网络中的其他开发人员在某个时候所面临的问题。这些知识通常以不同的方式记录下来。有时,团队喜欢在一个松散的小组中以消息的形式发布他们的学习成果,他们创建汇合页面或将笔记保存在他们的本地机器上。不管问题是如何解决的,收集一个资源列表来帮助解决您可能面临的问题是很重要的。例如,当我学会如何在 Slack 中搜索错误消息时,它为解决问题打开了一个全新的世界。了解可用的资源以及如何利用这些资源对于有效地解决这个问题是绝对重要的。您应该假设其他人已经看到了您正在看到的错误。

学习服务—第一个月

无论您是在创建一个新的服务还是支持一个已存在的服务,理解这个服务通常需要一生的时间。一个简单的架构图可以引入一系列关于服务的利弊和目的的问题。服务密集。服务是一个有许多引用的关于解决特定问题的最佳方法的论点。它也是一种具有独特解剖结构的生物,必须理解这种结构才能得到改善和维护。

虽然我可能会写一整篇关于如何研究服务的文章,但我会尝试总结一下有组织的工作包括什么。首先,了解服务将如何被使用。如果服务或 MVP 已经建立,尝试自己使用服务。真正考验服务的局限性。如果是 API,尝试输入错误的值,看看服务如何响应。显然,要确保有一个可以进行测试的安全环境。

第二,与负责服务的产品经理或技术主管交谈,了解未来的改进和目标。如果有关于当前性能的指标,详细说明 5xx%、429%和延迟指标,可能会更有帮助。然而,讨论服务当前的缺陷以及未来的目标将帮助您更好地把握期望和目标。

第三,获得服务的架构图,并彻底研究它。涉及的各种组件是什么?这些组件的优缺点是什么?给定当前和未来的目标,一个更好的架构图看起来像什么?我甚至会建议你自己画草图来测试你的知识。更进一步,你可以询问技术负责人或任何设计你正在构建和支持的服务的人。

第四,从高层次到低层次查看服务。首先写几个句子来描述这项服务。接下来,抽象地参考架构提供更详细的描述。然后描述该工作流程的技术细节。如果有 API,那么详细说明这些 API 如何协同工作来解决更广泛的问题。您还可以详细描述这些 API 如何抽象地工作,然后再详细描述。最后,您可以获得代码库并从本质上描述功能。我们的目标是理解每一行代码,但有时在我们的有生之年这是不可能的。做到这一点需要时间,所以不要在加入后的第一天甚至第二个月就执着于理解每一行。

结论

在作为一名软件工程师加入一个新团队之前,我写了这篇文章来整理我的思路。鉴于我的技术背景和经验,我知道我刚加入时犯的错误。考虑到全球疫情,我在偏远地区,这一事实加剧了这些错误。我想创建一个排序列表,以更有效地帮助新工程师,避免我犯的错误。将这个列表组织成软件和技术领域可能会帮助新的工程师认识到这两者对帮助他们的团队的重要性。然而,这个列表并不全面。我错过了什么吗?请在下面评论。

子堆栈:

我最近创建了一个子栈来学习如何用 Python 编程和解决 LeetCode 问题。

软件工程 vs 机器学习

原文:https://towardsdatascience.com/software-engineering-vs-machine-learning-916fd393bf74?source=collection_archive---------38-----------------------

软件工程将工程原理应用于软件开发问题

无论你是刚刚进入就业市场,还是仅仅在考虑职业生涯的下一步,计算机科学领域都有点像迷宫,充满了曲折。似乎随着科技行业的持续发展,所有的职业都凭空出现了,或者说曾经属于某个职业的职责已经变成了另一个职业的常规期望。

今天,我们将看看软件工程(一个更古老、更传统的技术职业)和机器学习(这个词最近似乎挂在每个人的嘴边)领域。我们将根据它们的传统界限来定义每个领域,确定它们之间的关键差异,然后最终查看这两个领域相交和重叠的地方。

什么是软件工程?

用最简单的术语来说,软件工程将工程原理应用于软件开发的问题。这涉及前端开发、后端开发、数据库、基础设施等工作。

作为一个完美的多面手,软件工程师开发了一个编程语言的工作库,并且知道何时以及如何在适当的时候应用每种语言。一个软件工程师可能在一个项目中使用 Java,然后在另一个项目中使用 Golang,然后再次使用 SQL 实现一个数据库。

这意味着软件工程师的价值不仅取决于他们掌握的语言数量,还取决于他们使用这些语言实现目标的能力。

一般来说,软件工程师根据软件开发生命周期(或 SDLC)开发软件,这是一个实现和精炼软件的连续循环过程。SDLC 的常见变体包括软件开发的敏捷和瀑布过程。

什么是机器学习?

简单地说,机器学习寻求利用计算机的处理能力,在大量数据中寻找不明显的模式。

机器学习专家致力于开发机器学习算法,要么是受监督的(机器被“告知”要通过训练数据集寻找什么),要么是不受监督的(机器在数据中寻找模式,而没有被具体告知要寻找什么)。在“深度”学习的情况下,机器学习专业人员部署由不同算法“神经元”组成的分层神经网络来处理大量数据。

实际上,今天大多数机器学习专业人员使用 Python,特别是 scikit-learn、PyTorch、Tensorflow 和 Keras 库。这些库不断更新和改进,因此机器学习随着时间的推移变得越来越容易,以至于 scikit-learn 库可以在短短十行代码中部署一个全功能的机器学习算法。

这意味着机器学习专业人员的工作是确定什么样的算法(逻辑回归、随机森林、K 近邻等。)最适合于手边的情况,并且清除数据,使得算法可以以最小的失真运行。

为机器学习面试而练习?通过阅读 机器学习面试问题,以正确的方式专注于正确的主题,节省时间。

主要差异

既然我们对什么是软件工程和机器学习有了更清晰的理解,我们可以观察它们之间的一些关键差异。

一般来说,软件工程是一个比机器学习更广泛的领域,涉及更多种技能、语言和过程。

此外,软件工程涉及比机器学习更直接的方法。软件工程师负责给定应用程序从概念阶段到代码实现的整个过程,然后还负责在软件的整个生命周期中更新和调试软件。

如果你喜欢从头到尾都有一个游戏计划,而不是即兴创作,那么软件工程可能比机器学习更适合你。

相比之下,机器学习专家的大部分工作都是由机器学习算法完成的;他们为算法设置参数以产生结果,但他们不参与在大量数据中手动识别模式。

因此,机器学习专业人员可能需要与数据科学家合作,解释机器学习算法发现的模式,以便该算法不会像某种“黑匣子”一样,识别任何人都无法有意义地解释的模式。

在这里,我们遇到了软件工程和机器学习之间的一个关键区别。由于机器学习算法在理论上可以检测到任何个人都无法检测到的模式,因此它可以从数据集获得对外部人类观察者来说永远不明显的洞察力。

这是一把双刃剑。一方面,机器学习之所以有用,恰恰是因为它可以检测到这些模式。另一方面,如果我们不能确定为什么机器学习算法返回这样的结果,我们就无法知道如何有效地利用这些见解来推动我们的业务决策、产品选择或决策矩阵。

重叠

虽然看起来这两个领域没有任何共同之处,但实际上它们有共同的交叉点。

如果你是一家使用机器学习来驱动其业务决策的公司的软件工程师,你很可能最终会在生产期间将机器学习代码发布到应用程序中。

如果你是一名机器学习专家,当涉及到将你的机器学习算法集成到生产中时,你也会收到软件工程师的输入。

此外,给定机器学习算法的结果可用于驱动软件开发生命周期中的变化,意味着软件工程师只能受益于对其机器学习对手的了解。

最后,还有一个横跨软件工程和机器学习的职位:机器学习工程师。机器学习工程师通过充当建模机器学习专家和必须在生产中实现其算法的软件工程师之间的联络人,弥合了数据科学和软件工程之间的差距。

如今的就业市场对机器学习工程师的需求很高,因为这份工作需要对机器学习有中到深度的理解,以及该角色所需的更一般的编程知识,而很少有程序员符合要求。

在那些这样做的公司中,大多数都被大公司吸引,这些公司跟上了机器学习的现代趋势,并为员工提供了最多的机会,因此较小的企业在这场机器学习革命中被落在了后面。

如果你对机器学习和将机器学习算法集成到生产中感兴趣,你可能很适合机器学习工程师的角色。为了检查你是否在正确的轨道上,看看这个机器学习面试问题:

提前键入搜索

你如何为网飞的预输入搜索建立推荐算法?

给你个提示:

让我们首先考虑一个简单的用例。假设我们在一部电影的开头键入单词“hello”。

如果我们输入 h-e-l-l-o,那么一个合适的建议可能是一部像《阳光你好》这样的电影或者一部名为《你好》的西班牙电影。

练习 面试查询上这道面试题。

结论

现在,您对软件工程和机器学习之间的异同有了更多的了解,您可以采取下一步来推进您的计算机科学职业生涯。一个很好的起点是面试查询,我们在那里提供 SQL、统计学和机器学习的课程,以及来自谷歌、脸书、网飞等公司真实面试的大量问题,让你提高自己的技能。无论你为你的技术职业选择什么方向,面试查询都有你准备下一次面试所需的资源!

软件工程师 vs 数据科学家

原文:https://towardsdatascience.com/software-engineering-xor-data-science-72abbc868b8b?source=collection_archive---------20-----------------------

我在担任软件工程师和数据科学家期间看到/感受到的简单对比,以及我所面临挑战的解决方案

我很确定许多读者会质疑或思考他们在职业生涯中做了什么。此外,他们还比较他们的项目、开发实践、经理,甚至同事。因为我已经完成了 6 年的工作,并且有机会从事软件工程和数据科学项目开发,所以我想写一篇博客来提及相似性、差异和本质。

la sphère ( 来源)

在帖子的以下部分, SWDS 分别代表软件数据科学

项目管理

正如你可能猜到的,就我所做的来说,阶段业务目标需求计划技术设计实现维护/监控对于这两种类型的项目来说是相同或相似的。项目经理或产品负责人,根据公司的规模或方面,在多次会议后最终确定业务目标,定义功能和技术需求,与产品和技术团队一起进行评估,然后开发应用程序。最后,对发布的应用程序进行监控,随后开发所需的更改/功能。

应用程序开发阶段

数据分析

有了好的计划和聪明设计的干净实现,好的软件就可以发布了。对于 DS 项目,我想在需求和计划阶段之间增加一个数据分析部分。在这个阶段,首先,定义假设,检查和验证现有或可能的数据源,并完成假设的验证。在这个过程的任何一步,我都看到了项目可以被停止或修改。就像在生活中一样,除非我们有任何依据,否则一旦我们的假设动摇了,就放弃它是最好的决定之一。

在数据源验证中,一个低质量的数据可能维持一个普通的软件,但是它对于 DS 项目是至关重要的。如果公司想要从 DS 应用程序的输出中做出一些决策,那么对于第一次迭代,模型的准确性必须是可接受的或者可以忽略的。创建一个基础模型将有助于观察新提出的模型是好是坏。即使该基础模型具有合理的准确性,也可以使用,但是为了在进一步的开发中胜过原始模型,复杂性会增加。尽管准确性很高,但数据质量必须得到验证。因此,数据工程师和数据科学家或 ML 工程师之间的联合对于数据应用程序来说是非常重要的。因为对于数据应用程序,训练数据和预测数据必须相同。例如,如果有批量预测,则必须在预测之前验证数据迁移。否则,预测和训练部分将会不同,这可能会给公司带来一些灾难性的结论

技术设计

使用清晰的软件设计(如六角形架构)可以为软件和 DS 项目添加新功能或改变技术堆栈。在开发之前,数据源、框架甚至编程语言都可能发生变化。例如,如果我们想为支付流程实现一个响应时间非常短的应用程序,可以实现缓存来保存这些重要的信息,这样我们就可以减少数据库的工作量。

对于 DS 项目,除了模型训练、模型选择和预测阶段,应用流程与 SW 项目相似。因此,在技术设计中,可以定义 MLOps 的复杂性。最初,从手动处理 M 模型训练、M 模型选择模型部署的最简单方式开始,然后这些步骤可以自动化。除了 MLOps,算法类型和模型的复杂性是技术设计的另一个重要因素

规划

如果所有的验证都完成了,需求也最终确定了,我们就可以开始规划部分了。由于团队开始更好地了解动态和开发环境,正如我所观察到的,这个阶段会变得更好。任务可以尽可能细化,每个团队成员可以给出更好的、相似的冲刺点,整体的预估和规划可以最终确定。

在软件方面,对 DevOps 操作可能有一些低估。服务器上的操作以及应用程序或数据源之间的权限可能会持续更长时间。新的应用程序可能需要从现有的应用程序调用端点,或者可能需要在响应中添加新的字段。因此,这些沟通应该在计划之前进行,以便做出更好的估计。

在数据方面,软件方面的细节通常是相似的。此外,特征工程模型训练模型选择步骤的规划可能被高估,这也是正常的,因为它们是与数据相关的过程。更好的数据分析和从简单的方法开始可能有助于更好的规划除此之外,可以为模型分配一些更简单的阈值,以便在这些步骤中超越它们。例如,如果分类器的 F1 值高于 X 值,我们可以在那里停下来,并保持兴奋,以进行进一步的模型训练。

发展

对双方来说,我最喜欢的开发部分是最后一步之前的最后一步,也就是监控。服务的实现、处理单元集成功能测试以及为应用程序准备服务器是这两个应用程序的共同阶段。

在软件应用程序中,对非特殊应用程序进行测试比在 DS 应用程序中进行测试要容易一些。如果我们使用一个复杂的模型,测试人工智能模型的结果并不是一个好的方法。然而,可以为这些模型实现模拟,因为它可以为应用程序中的外部服务实现。

特征工程、模型训练和模型选择是区别于软件项目的阶段。正如在上一节课中提到的,可能有一些二维的阈值。一个维度是停止成功标准,另一个维度是时间标准。由于这些步骤是当今最愿意和最大肆宣传的发展之一,这些发展和研究应限于提高生产率。如果这种新的应用带来一些成功,并且它需要更好的准确性,这三个阶段可以继续进行进一步的开发。

监视

终于到了双方收获果实的时候了。使用从应用程序日志中提取信息的监控工具,如 SplunkDatadog (我只为他们工作过)具有重要的作用。在一个新版本发布后,它将是第一个验证新版本是否无缝工作的地方。通过编写良好的日志,可以提取软件的度量,如请求数量、响应时间和错误类型。在开始 SW 和 DS 项目的操作之前,在接收请求并将其转发到所需服务的控制器类中进行数据检查也是很好的,除非 UI 端不做任何检查。正则表达式验证和数据类型检查可以从性能和安全性两方面保存应用程序。一个简单的验证可能会阻止一个简单的 SQL 注入,它甚至可以拯救公司的未来。

在 DS 方面,除了软件指标之外,还应该有进一步的监控。模型特征和模型预测的分布是至关重要的。如果任何数据列的分布发生变化,可能会发生数据转移,需要进行新的模型训练。如果可以在短时间内验证预测结果,则必须监控这些结果,并在精确度低于或高于给定阈值时警告开发人员。

常见做法

在结束之前,我想提一下对这两种类型的项目都有帮助的一些常见实践。

来源

  • 拥有渊博的商业领域知识:对双方来说都是不可或缺的。为了获得这些知识,我们必须了解客户的需求。这种知识可以提供一些好处,例如在软件项目中实现重要的边缘案例,以及在 DS 项目中提取重要的特性。它还有助于编写更好的测试,为应用程序准备更好的环境
  • 使用 OKRs 进行项目输出:为了在应用程序中给出更好的商业价值,最好使用商业语言。我们应该将目标从“将响应时间缩短 x 倍”转变为“将入职流程加快 y 倍”或“将欺诈率降低 x 倍”转变为“将支付转换率提高 y 倍”。这些转换将有助于不同部门的团队更好地沟通,并减少技术和非技术团队之间沟通的技术术语。
  • 开发实践的应用:干净的编码,设计一个伟大的服务架构,结对编程,以及评论是我记忆中最常见的。对于维护代码来说,质量是不可避免的。即使对于一个笔记本文件,另一个人也应该尽可能地理解这个脚本,以理解和实现新的特性。结对编程和复习对于提高生产率和减少被忽视的错误也很重要。
  • 优秀的文档:在规划和设计阶段,在开始实施之前,拥有可被视为法律文件的优秀文档也很重要。它可以用作开发的来源,并包含公司中其他技术和非技术成员的简要说明和流程图。

结论

我试图提及我在从事软件和 DS 项目时所面临的问题。希望其中一些能对部分读者有用。这将有助于一些我曾经受益过的人,我现在也正在受益。正如上面提到的,更好的计划、更好的可见的交流、更好的分析、高效的开发和更好的监控阶段通常会驱使我在更好的开发中工作。

在说再见之前,我想提一下,这不是一篇博客文章,我想以超过 6 年的工作经验给人们提供超越极限的建议,我也有点偏向数据项目,因为我已经在数据项目工作了 3 年多。这些是我面临的唯一挑战,我只想分享我的经历。这些推论可能对其他一些开发人员有用。

在说再见之前,我想对这篇文章的第一个读者说谢谢穆斯塔法·基拉克

向我问好或问我任何问题:

领英:https://www.linkedin.com/in/ktoprakucar/

github:https://github.com/ktoprakucar

电子邮件:toprakucar@gmail.com

参考

Python 中的实体编码

原文:https://towardsdatascience.com/solid-coding-in-python-1281392a6a94?source=collection_archive---------0-----------------------

可靠编码的原则是由 Robert C. Martin 发明的首字母缩略词,它代表五种不同的编码惯例。

如果您遵循这些原则,您可以通过处理代码的结构和逻辑一致性来提高代码的可靠性。

Unsplash 上拍摄的 ThisisEngineering RAEng

坚实的原则是:

  • 单一责任原则
  • 开闭原则(OCP)
  • 利斯科夫替代原理(LSP)
  • 接口隔离原则(ISP)
  • 依赖性倒置原则

这五个原则不是一个特定的有序列表(做这个,然后那个,等等),而是几十年来发展的最佳实践的集合。它们被聚集成一个首字母缩略词,作为记忆的载体,类似于计算机科学中的其他,例如: : 不要重复自己亲亲 : 保持小而简;作为智慧的积累。附带说明一下,这个缩写词是在这五个原则结合在一起多年后产生的。

一般来说,坚实的原则是每个代码开发人员的基本学习步骤,但通常被那些不认为代码的最高质量是他们绝对优先考虑的人所忽略。

然而,作为一名数据科学家,我认为遵循这些原则是有益的。具体来说,它提高了可测试性,减少了技术债务,以及当客户/股东的新需求出现时实现变更所需的时间。

在下面的文章中,我想探索这五个原则,并提供一些 Python 中的例子。通常,坚实的原则被应用在面向对象设计的上下文中(例如:Python 的类),但是我相信它们不管什么级别都是有效的,我想把这里的例子和解释保留到“高级初学者”的级别,监督正式定义。

1)单一责任原则

“一个类应该有且只有一个更改原因”

换句话说,你的代码的每个组件(通常是一个类,但也是一个函数)都应该有一个且只有一个职责。因此,应该只有一个改变它的理由。

你经常会看到一段代码同时处理整个过程。即在返回结果之前加载数据、修改数据并绘制数据的函数。

让我们举一个更简单的例子,我们有一个数 L = [n1,n2,…,nx]的列表,我们对这个列表计算一些数学函数。例如,计算平均值、中间值等。

一个糟糕的方法是让一个函数完成所有的工作:

为了更符合 SRP,我们应该做的第一件事是将函数 math_operations 分成个原子函数!因此,当一个功能的职责不能再分成更多的子部分时。

第二步是创建一个单独的函数(或类),一般命名为“main”。这将在一步一步的过程中一个接一个地调用所有其他函数。

现在,您只有一个理由来更改与“main”相关的每个函数。

这个简单动作的结果是:

  1. 更容易定位错误。执行过程中的任何错误都会指出代码的一小部分,从而加速调试阶段。
  2. 代码的任何部分都可以在代码的其他部分重用。
  3. 此外,经常被忽视的是,为代码的每个功能创建测试更容易。关于测试的补充说明:你应该在实际编写脚本之前编写测试。但是,这经常被忽略,而倾向于创建一些好的结果显示给涉众。

相对于第一个代码示例,这已经是一个很大的改进了。但是,创建一个“main”并调用具有单一职责的函数并没有完全实现 SR 原则。

的确,我们的“主要”有许多理由需要改变。这个类实际上很脆弱,很难维护。

为了解决这个问题,让我们引入下一个原则:

2)开闭原则(OCP)

"软件实体…应该对扩展开放,但对修改关闭"

换句话说:你不需要修改你已经写好的代码来适应新的功能,只需要简单地添加你现在需要的。

这并不意味着当代码前提需要修改时,您不能更改您的代码,但是如果您需要添加类似于现有功能的新功能,您不应该要求更改代码的其他部分。

为了澄清这一点,让我们参考前面看到的例子。如果我们想添加新的功能,例如计算中位数,我们应该创建一个新的方法函数,并将其调用添加到“main”中。那将会增加一个扩展修改主。

我们可以通过把我们写的所有函数变成一个类的子类来解决这个问题。在这个例子中,我用一个抽象方法“get_operation”创建了一个名为“Operations”的抽象类。(抽象类一般是高级话题。如果不知道什么是抽象类,即使没有也可以运行下面的代码)。

现在,所有旧的函数,Now 类都被 _ _ subclass _ _()方法调用。这将找到从 operations 继承的所有类,并操作所有子类中存在的函数“Operations”。

如果现在我们想要添加一个新的操作,例如:median,我们只需要添加一个继承自类“Operations”的类“Median”。_ _ subclass _ _()将立即选择新形成的子类,并且不需要修改代码的任何其他部分。

结果是一个非常灵活的类,只需要最少的时间来维护。

3)利斯科夫替代原理(LSP)

“使用指向基类的指针或引用的函数必须能够在不知道的情况下使用派生类的对象”

或者,这可以表达为“派生类必须可替换它们的基类”。

用(也许)更简单的话来说,如果一个子类重新定义了一个父类中也存在的函数,客户端用户应该不会注意到行为、的任何差异,它是基类的替代
例如,如果您正在使用一个函数,而您的同事更改了基类,您应该不会注意到您正在使用的函数有任何不同。

在所有坚实的原理中,这是最难理解和解释的。对于这一原则,没有标准的“模板式”解决方案,也很难提供“标准范例”来展示。

用最简单的方式,我可以这样说,这个原则可以总结为:
如果在一个子类中,你重定义了一个函数,它也存在于基类中,那么这两个函数应该具有相同的行为。然而,这并不意味着它们必须强制相等,而是用户应该期望在给定相同输入的情况下得到相同的类型的结果。
在 ocp.py 示例中,“operation”方法出现在子类和基类中,最终用户应该可以从这两个类中获得相同的行为。

这一原则的结果是,我们将以一致的方式编写代码,最终用户只需要了解我们的代码是如何工作的。

附录:

(可以跳到下一个原理)。

LSP 的结果是:子类中新的重新定义的函数应该是有效的,并且可以在父类中使用相同函数的任何地方使用。

这不是典型的情况,事实上我们人类通常是根据集合论来思考的。具有定义一个概念的类和扩展第一个概念的子类,具有异常或不同的行为。

例如,基类“哺乳动物”中的子类“鸭嘴兽”会有例外,即这些哺乳动物产卵。LSP 告诉我们,它将创建一个名为“give_birth”的函数,这个函数将对鸭嘴兽亚纲和狗亚纲有不同的行为。因此,我们应该有一个比哺乳动物更抽象的基类来适应这一点。如果这听起来很混乱,不要担心,LSP 的后一方面的应用很少被完全实现,并且很少离开理论教科书。

4)接口隔离原则(ISP)

"许多客户端专用接口比一个通用接口更好"

在类的竞争中,考虑一个接口,所有的方法和属性暴露,因此,用户可以与之交互的一切都属于那个类。

从这个意义上来说,is 原则告诉我们,一个类应该只具有所需的接口(SRP ),并避免那些不起作用或者没有理由成为该类一部分的方法。

这个问题主要出现在子类从基类继承它不需要的方法的时候。

让我们看一个例子:

对于这个例子,我们得到了抽象类“哺乳动物”,它有两个抽象方法:“行走”和“游泳”。这两个元素将属于子类“人类”,而只有“游泳”将属于子类“鲸鱼”。

事实上,如果我们运行这段代码,我们可以:

子类 whale 仍然可以调用方法“walk ”,但是它不应该调用,我们必须避免它。

ISP 建议的方式是创建更多的 客户端专用接口 而不是一个通用接口。因此,我们的代码示例变成了:

现在,每个子类只继承它需要的,避免调用脱离上下文的(错误的)子方法。这可能会产生一个难以捕捉的错误。

这个原则与其他原则紧密相连,具体来说,它告诉我们保持子类的内容没有对该子类无用的元素。这有一个最终目标,那就是保持我们的班级整洁,减少错误。

5)依存倒置原则(DIP)

抽象不应该依赖于细节。细节应该取决于抽象。高层模块不应该依赖低层模块。两者都应该依赖于抽象

因此,抽象(例如,如上所述的接口)不应该依赖于低级方法,而应该依赖于第三方接口。

为了更好地解释这个概念,我倾向于认为是一种信息流。

想象一下,你有一个程序,它接收一组特定的信息(文件、格式等),你写了一个脚本来处理它。如果信息发生变化,会发生什么?你将不得不重写你的剧本并调整新的格式。失去了与旧文件的追溯兼容性。

然而,您可以通过创建第三个抽象来解决这个问题,该抽象将信息作为输入,并将其传递给其他人。
这基本上也是 API 的用途。

(图片由作者提供)

这个原则的有趣设计概念是,它是我们通常会做的相反的方法。

考虑到这一点,我们将从项目的结尾开始,其中我们的代码独立于输入的内容,并且它不容易受到变化的影响,也不受我们的直接控制。

我希望你能在你的编码中找到这些有用的概念,我知道它们适合我。此外,我留下了一些我用来理解这些原则的材料。

使用 Sklearn、XGBoost 和 PySpark 在 Python 中进行回归

原文:https://towardsdatascience.com/solution-of-a-regression-problem-with-machine-learning-in-python-using-sklearn-and-xgboost-and-ea19afdfc067?source=collection_archive---------13-----------------------

机器学习通常用于解决回归问题。更具体地说,将回归算法应用于多维数据框架是一种常用于测量一个(或多个)自变量(预测值)和多个因变量(响应值)线性相关程度的方法。

图片来自 unsplash.com

如果你正准备开始建立你的机器学习模型,那么我希望你已经完成了探索性数据分析(EDA ),并且现在你已经熟悉了你的数据集。如果你不知道,我建议你看一下我写的关于 EDA 的故事,这是为了解决我在这里将要介绍的同样的问题。这个故事将带你回到分析数据,然后跳到机器学习部分,我将在这里介绍。

在上面的故事中,我们使用了一个 Fitbit 数据集

基于 EDA,发现所采取的步骤和卡路里在某种程度上是线性相关的,并且它们一起可以指示全因死亡率的较低风险。更有趣的是,在我们的数据中,有一个数据集还没有被使用,那就是重量和身体质量指数测井。

这些数据具有独特的性质,因为它们不一定是机器生成的,此后它们被用作“标签”。简而言之,用户正在使用他们的 Fitbit 收集有关他们活动的数据,并且偶尔会记录一些身体信息,如体重、脂肪和身体质量指数。这为监督学习问题创造了一个最佳场景,例如,我们可以使用 Fitbit 活动数据来预测用户的身体质量指数。然后,这种预测可以用于评估用户的健康状况。此外,访问与上述监督学习问题非常相关的一些其他个人信息,如身高、年龄和性别,会更加有用。

然而,出于本练习的目的,weightLogInfo _ mergeddata frame 的数据长度不足以训练一个人工智能模型,并且我们不会处理用户的个人信息。因此,我们将问题简化为卡路里预测,而卡路里被用作评估用户健康状况的主要变量。

我们将实现三种不同的机器学习算法,即:多元线性回归、随机森林和极端梯度助推器。然后,我们将宣布获胜者,并在 PySpark 中实现获胜者算法,以适应潜在的“大数据”问题。

预处理

我们希望以这样一种方式分割数据,即训练集占总数据集的 60%,验证集和测试集各占 20%。此后,为了便于学习过程,我们将数据的范围定在 0 到 1 之间。

我们希望根据每次预测平均值的模型的性能来建立基线分数。基线有助于评估我们要训练的模型是否真正从数据中“学习”了什么。

Lasso 回归算法表明,通过在模型中包含所有要素,我们可能不会面临任何重大的多重共线性问题。

多元线性回归器

随机森林回归量

极端梯度助推器

我们使用了三种不同的模型,它们(幸运地)似乎都优于基线,因此结果是有意义的。

交叉验证

首先,我们将进行交叉验证比较…

超参数调谐

为了提高我们的随机森林模型的准确性,我们将使用 RandomizedSearchCV 调整超参数

首先,我们将设置调整随机森林的范围或可能性…

…然后我们调整模型

然后,我们将对极端梯度助推器重复同样的过程…

最终模型评估

现在,是时候评估模型了…

aaaaaa 获胜者是… 随机森林

交叉验证和超参数微调有助于提高准确性。

通过在更大的跨度上选择更小的估计量区间,模型的性能可能会得到改善。总的来说,随机森林似乎给出了最好的结果,准确率为 80.5%。

PySpark 中的随机森林回归器

为了构建前面的模型,我们使用了 Scikit-learn 和 XGBoost。虽然这些库在相对方便的数据集上工作得很好(比如我们正在处理的数据集),但它们是处理“大数据”的高效计算选择。

假设我们的数据集可能被进一步扩展,我们只为性能最好的模型实现了上述原型的一个更加可扩展的应用程序。为了简单起见,我们将直接使用以前在 Pandas 中创建的数据帧。

数据框需要一些“按摩”才能适合训练…

既然 PySpark 数据框架已经准备好了,我们就可以创建特性了…

现在数据已经准备好进行训练了…

在 Spark 环境中获得了(几乎)相同的结果,而在超参数调整之前,先前的随机森林模型的准确度是 77.3%,现在获得了 77%。

如果你喜欢我的故事,并想跳到有代码和完整数据集的笔记本,我已经在我个人的 git 上的 repo 中发布了它。

给回购打个星:)

如果你在数据科学和/或人工智能项目上需要任何帮助,请不要犹豫,通过 Linkedinmidasanalytics.ai 联系我

表格数据机器学习的过拟合解决方案

原文:https://towardsdatascience.com/solutions-against-overfitting-for-machine-learning-on-tabular-data-857c080651fd?source=collection_archive---------9-----------------------

展示 5 个解决方案,你可以用它们来抵消表格数据和经典机器学习中的过度拟合

针对表格数据和经典机器学习的过拟合解决方案。照片由梅姆Unsplash 上拍摄。

在这篇文章中,我将概述适用于表格数据和经典机器学习的防止过度拟合的解决方案。

当比较经典的机器学习和深度学习时,后者通常被认为更复杂。对于过度拟合的问题,情况正好相反:许多工具和技巧可以很容易地避免深度神经网络的过度拟合,而当涉及到经典的机器学习时,你会发现自己处于黑暗之中。

什么是过度拟合?

过拟合是机器学习中常见的问题。让我先用一个关于人类学习的直观例子来说明。

过拟合:模型已经记住了训练数据,但对学习过程没有概括的理解。

当人们在学校、为考试或只是一般地学习东西时,总有那些学生有很大的背诵能力:他们能记住许多事实并在考试中取得成功。其他学生付出的努力要少得多,因为他们对事情有深刻的理解,因此他们不需要用心去学习。

死记硬背是不好的学习

有些考试会要求你简单地记住事实信息。了解事实在生活中也会非常有用。然而,在机器学习中, 事实学习没有任何价值 :如果你需要存储事实,你可以简单地为这些数据实现一个数据库。

学会“理解”是好的学习

机器学习模型不是学习事实,但它的学习是基于 变量 之间的学习关系。在更简单的机器学习模型中,这些关系通常是线性的,而在更复杂的模型中,它们可能是非线性的。

死记硬背考试成绩不是真正的学习。照片由 Nguyen Dang Hoang NhuUnsplash 上拍摄

过度拟合定义

有了这个想法,我们可以定义过度拟合。当模型在训练数据上表现良好时,它就是过度拟合,但在测试数据上表现不佳。

过拟合:模型已经记住了训练数据,但对学习过程没有概括的理解。

这与前面的例子有关:一个学生已经记住了过去考试的所有答案,但无法正确回答他以前没有见过的考试问题。

如何检测过度拟合?

训练测试分割以检测过度拟合

检测过度拟合的最简单方法是查看训练数据和测试数据之间的性能。如果您的模型在训练数据上表现很好,而在测试数据上表现很差,则您的模型可能过度拟合。

高训练性能和低测试性能指向过度拟合。图片作者。

可以使用交叉验证来检测过度拟合吗?

交叉验证是训练/测试分割的常用替代方法。这个想法是通过在数据的一部分上多次拟合模型,同时在数据的另一部分上进行测试,来避免创建训练/测试分割。最后,数据的所有部分都用于训练和测试。

交叉验证分数是在整个流程中完成的所有测试评估的平均分数。

交叉验证分数仅基于测试数据,因此这是一种很好的方法,可以用一个指标来代替单独的训练和测试指标。我们可以说交叉验证相当于模型的测试分数。

使用交叉验证分数不会帮助您检测过度拟合,因为它不会向您显示培训绩效。所以这不是一种检测过度拟合的方法,但是交叉验证分数可以告诉你你的测试表现很差(没有原因)

为什么过拟合在深度神经网络上更容易解决?

如果说过度拟合在任何情况下都很容易解决,那就太过分了。然而,深度学习工具箱有很多工具可用于解决过度拟合。对于经典的机器学习来说,情况就不是这样了。

解决神经网络的过拟合问题

原因可能是神经网络是如此强大的学习算法,以至于很可能发生过拟合。建立一个根据训练数据进行学习的神经网络相对容易,但神经网络的真正任务是抵消过度拟合。这可能就是为什么有很多工具存在的原因。

用于检测神经网络中过拟合的著名图形。图片作者。

解决经典机器学习的过拟合问题

在经典的机器学习中,算法通常不太强大,但过拟合也可能发生!

你也可以计算经典机器学习的学习曲线,尽管这是一种不太标准的方法。您可以针对越来越多的数据点重新调整模型,并获得与上述相同类型的图形。然而,这需要多次改装,可能有点乏味。

什么是防止过度拟合的解决方案?

现在让我们继续,发现你可以对机器学习管道进行的一些改进,这些改进将 减少你的训练和测试性能 之间的差距。

实际上,这甚至意味着降低列车性能以提高测试性能。

如果你最终发现你不能再缩小这个差距,那么在一个训练和测试性能差距很大的模型上工作将是浪费时间。

最好是先缩小差距,一旦两者之间的差距缩小,您就可以继续尝试一起提高训练和测试性能。

应对过度拟合的解决方案

防止过度拟合的解决方案有:

  1. 简化模型
  2. 正规化
  3. 数据扩充
  4. 交叉验证的超参数调整
  5. 集合模型

让我们更详细地发现它们,并看看它们如何应用于表格数据和经典的机器学习模型。

解决方案 1:针对过度拟合简化模型

减少过度拟合的第一个解决方案是降低模型的复杂性。

针对表格数据和经典机器学习的过拟合解决方案。照片由 Prateek KatyalUnsplash 上拍摄

一个模型的性能越好,就越有可能过度拟合。毕竟,学习能力差的模型永远无法记住训练数据集。想象一个线性回归,它学习系数来拟合变量之间的线性关系:它只能学习线性趋势。

一个模型越有性能,就越有可能过度拟合。

如果您观察到过度拟合,让您的模型不那么复杂是个好主意。这可以通过切换到性能较低的模型来实现。一旦您缩小了训练和测试性能之间的差距,您就可以重新迭代并尝试再次提高整体性能。

解决方案 2:调整以防止过度拟合

正则化有助于模型在两个精度相同的模型之间进行选择。如果您使用复杂模型获得了与简单模型相同的精度,那么您应该选择更简单的模型。

这个原则通常被称为奥卡姆剃刀接吻原则等等。

正则化的目标是改变成本函数,以便使模型复杂度成为额外的成本。

例如,让我们看看下图:

如何在两款性能完美的机型中做出选择?图片作者。

方差较低的模型似乎更可靠,但两个模型都精确地通过了每个数据点:两者都得了满分。

正则化的目标是改变成本函数,以便使模型复杂性成为额外的成本。

一些经典的机器学习模型是专门为正则化而开发的:

这些正则化方法各不相同,因为它们使用不同的函数添加到成本函数中。Lasso 使用 L1 规范,Ridge 使用 L2 规范,ElasticNet 同时使用 L1 和 L2 规范。 你可以看看这篇文章,深入了解这些规范之间的区别。

不同的距离度量用于不同的正则化方法。

将正则化添加到成本函数仅对于某些模型是可能的。不幸的是,对于其他模型来说,这不太容易实现。但是, 对于没有正则化的模型,可以通过交叉验证 (方案 4)的超参数调优获得模型蕴涵。

解决方案 3:表格数据的数据扩充

数据扩充是一项在图像处理的机器学习中经常使用的任务。在影像数据中,通过向训练数据集中添加稍加变换的影像来扩充数据是很容易的。例如:

  • 扭转形象
  • 拉伸图像
  • 更多

对于图像上的数据增强,存在许多功能,例如在 Tensorflow 的数据增强层中。

在经典机器学习中,数据扩充不太明显。你不能只是翻转或拉伸一行表格数据。

对表格数据进行数据扩充的一个很棒的方法就是 SMOTE

SMOTE 是一种对表格数据进行数据扩充的算法

SMOTE 是一种通过基于原始数据点创建合成数据点来执行数据扩充的算法。因此,这是唯一完全适用于表格数据的方法之一。

如果过度拟合是由于数据中没有足够的样本,这种方法尤其有效。

解决方案 4:针对过度拟合进行交叉验证的超参数调整

超参数调整是调整模型的超参数以提高模型性能的任务。

最简单的方法是使用网格搜索:测试超参数值的多种组合,并使用交叉验证评估它们的性能。

使用这个,您可以找到产生最佳交叉验证分数的超参数组合。如上所述,交叉验证分数可能代表您在培训/测试环境中的测试分数。

在这种情况下,拆分数据的一个好方法是:

  • 输入交叉验证的训练数据集(内部训练/验证分割):用于超参数调整
  • 在调优和最终模型定义结束之前,不会使用测试数据集。您将使用这个测试集进行最终的性能评估:它应该代表您在模型部署之后观察到的性能。

由于这种方法优化了交叉验证分数(与测试分数相当),因此很可能产生具有较少过度拟合(较高测试分数)的解决方案。

解决方案 5:防止过度拟合的集合模型

根据随机森林的发明者,随机森林不会过度适应。在实践中,这是很有争议的(例如见这里这里)。

也许说随机森林永远不会过度适配有点过了。然而,可以肯定地说,合奏可以在避免过度拟合方面提供很大的帮助。

一个简单的模型可能是非常错误的。很多简单模型的平均值,大概是误差较小的。

尽管一个模型可能非常错误,但它比大量模型的平均值非常错误的可能性要小得多。这就是集成学习的思想。

集合模型是将多个弱学习者放在一起的艺术。新的预测是所有那些弱学习者的平均预测。随机森林是集成学习的一个很好的例子,但是在这个家族中存在更多的模型。

集成建模就像机器学习的团队工作。照片由 NeONBRANDUnsplash 上拍摄。

如果您正在处理一个表现不佳的模型,您甚至可以通过将多个模型分组到一个集合包装器中,将它制作成一个集合。这方面的例子有:

  • 投票分类器:让多个分类模型进行投票,最常预测的类别将被保留。
  • 投票回归器:它做同样的事情,但是取所有单个模型的平均预测,并将其转化为数字目标的预测。
  • 堆叠分类器:建立一个额外的分类模型,使用每个单独模型的预测作为输入,并预测最终目标。
  • 堆叠回归变量:建立一个额外的回归模型,使用每个单独模型的预测作为最终回归模型的输入,最终回归模型将所有预测组合成一个最终预测。

结论

在本文中,您首先看到了如何在表格数据和经典机器学习中检测过度拟合。你已经看到了 5 种应对过度拟合的主要解决方案:

  1. 简化模型
  2. 正规化
  3. 数据扩充
  4. 交叉验证的超参数调整
  5. 集合模型

我希望这篇文章已经给了你发现过度拟合的知识,以及改进你的模型的资源!

感谢您的阅读,请不要犹豫,继续关注更多数学、统计和数据科学内容!

用机器学习解决现实世界的客户流失问题(包括详细的案例研究)

原文:https://towardsdatascience.com/solve-a-real-world-churn-problem-with-machine-learning-including-a-detailed-case-study-a41dac0150b9?source=collection_archive---------15-----------------------

照片由马太·亨利发自突发

使用 R & Tidyverse

流失定义

客户流失是大多数公司面临的主要问题。失去客户需要获得新的客户来替代他们。根据领域的不同,这可能比留住现有客户要贵 10 倍左右。

当客户停止使用你公司的产品或服务时,他/她被认为是流失。这对于合同设置来说很容易定义,因为当客户未能续签合同时,就被认为是流失客户。但在非合同环境中,没有明确的规则来定义客户流失。在大多数情况下,具有扩展领域知识的业务用户与数据科学家/数据分析师一起工作,来定义在特定问题中什么被认为是变动。例如,在一个零售组织中,当一个客户在过去 4 个月中没有购买任何东西时,团队可以将他定义为流失客户。

客户流失管理的好处

主要好处是通过获得更高的保留率和客户满意度来增加收入。另一个好处是通过有针对性的营销活动优化营销支出&重新分配营销预算。

流失率

您可以通过将我们在特定时期(比如一个季度或一年)失去的客户数量除以该时期开始时我们拥有的客户数量来计算流失率。

例如,如果我们这个季度开始时有 400 个客户,结束时有 380 个,我们的流失率是 5%,因为我们失去了 5%或 20 个客户。

我们的流失问题

我们的案例研究是一家银行,该银行希望开发一个客户流失模型来预测客户流失的可能性。银行业已经成为发达国家的主要产业之一。技术进步和银行数量的增加提高了竞争水平。银行依靠多种策略努力在这个竞争激烈的市场中生存。

提出了三个主要战略来增加收入:

  • 获取新客户
  • 向现有客户追加销售(说服客户购买额外的或更贵的东西)
  • 增加客户保持期

在我们的案例中,我们专注于最后一个策略,即增加客户的保留期。原始数据集是公共的,可以在 Kaggle 找到

导入库

首先,我们加载所有的库。

library(tidyverse) 
library(ggthemes) 
library(correlationfunnel) 
library(knitr) 
library(caret) 
library(recipes) 
library(yardstick)
library(DT) # Set the black & white theme for all plots 
theme_set(theme_bw())

加载数据集

我们使用 read_csv()函数(来自 readr 库)来读取 r 中的 csv 文件。

bank <- read_csv(file = "/Users/manos/OneDrive/Projects/R/All_Projects/Churn/data/Churn_Modelling.csv") bank <- 
bank %>% mutate(Churn = if_else(Exited == 0, "No", "Yes"), 
                HasCrCard = if_else(HasCrCard == 0, "No", "Yes"), 
                IsActiveMember = if_else(IsActiveMember == 0, "No", "Yes")) %>% 
select(- RowNumber, -CustomerId, - Surname, -Exited)

检查数据集

使用 glimpse()函数检查数据集。

bank %>% glimpse()## Rows: 10,000 
## Columns: 11 
## $ CreditScore <dbl> 619, 608, 502, 699, 850, 645, 822, 376, 501, 684, 528... 
## $ Geography <chr> "France", "Spain", "France", "France", "Spain", "Spai... 
## $ Gender <chr> "Female", "Female", "Female", "Female", "Female", "Ma... 
## $ Age <dbl> 42, 41, 42, 39, 43, 44, 50, 29, 44, 27, 31, 24, 34, 2... 
## $ Tenure <dbl> 2, 1, 8, 1, 2, 8, 7, 4, 4, 2, 6, 3, 10, 5, 7, 3, 1, 9... 
## $ Balance <dbl> 0.00, 83807.86, 159660.80, 0.00, 125510.82, 113755.78... 
## $ NumOfProducts <dbl> 1, 1, 3, 2, 1, 2, 2, 4, 2, 1, 2, 2, 2, 2, 2, 2, 1, 2,... 
## $ HasCrCard <chr> "Yes", "No", "Yes", "No", "Yes", "Yes", "Yes", "Yes",... 
## $ IsActiveMember <chr> "Yes", "Yes", "No", "No", "Yes", "No", "Yes", "No", "... 
## $ EstimatedSalary <dbl> 101348.88, 112542.58, 113931.57, 93826.63, 79084.10, ... 
## $ Churn <chr> "Yes", "No", "Yes", "No", "No", "Yes", "No", "Yes", "...

下面是数据集所有变量的详细描述

  • 信用评分 |信用评分是一个介于 300-850 之间的数字,用来描述消费者的信誉。分数越高,借款人对潜在的贷款人来说就越好
  • 地理位置 |客户所在国家
  • 性别 |客户性别
  • 年龄 |客户年龄
  • 任期 |客户在银行工作的年数
  • 余额 |客户当前余额
  • NumOfProducts |客户正在使用的银行产品数量
  • HasCrCard |是否有信用卡
  • IsActiveMember |客户是否为活跃会员
  • 预计工资 |客户的预计工资
  • 搅动 |客户是否搅动

检查缺少的值

任何数据分析中最常见的问题之一是发现和处理缺失数据。

bank %>% 
map_df(~ sum(is.na(.))) %>% 
gather() %>% 
arrange(desc(value))## 
# A tibble: 11 x 2 
## key value 
## <chr> <int> 
## 1 CreditScore 0 
## 2 Geography 0 
## 3 Gender 0 
## 4 Age 0 
## 5 Tenure 0 
## 6 Balance 0 
## 7 NumOfProducts 0 
## 8 HasCrCard 0 
## 9 IsActiveMember 0 
## 10 EstimatedSalary 0 
## 11 Churn 0

没有任何丢失的值。

检查分类/文本变量的级别

现在我们要检查所有分类变量的级别。

bank %>% summarise_if(is.character, n_distinct) %>% t()## [,1] 
## Geography 3 
## Gender 2 
## HasCrCard 2 
## IsActiveMember 2 
## Churn 2

看起来所有的字符变量都是具有几个级别(2-3)的分类变量。

分类变量分布

现在我们要检查有关流失的分类变量的分布。

bank %>% 
select_if(is.character) %>% 
gather(key = key, value = value, - Churn, factor_key = T) %>% ggplot(aes( x = value, fill = Churn)) + geom_bar() + facet_wrap(~key, scales = 'free') + 
scale_x_discrete(labels = abbreviate) + 
labs( title = 'Distribution of categorical variables in relation to churn', x = '') + 
scale_fill_economist()

作者图片

主要发现是:

  • 来自德国的顾客更有可能流失
  • 男性客户流失的可能性略低
  • 活跃成员不太可能流失

数值变量分布

检查与客户流失相关的数字变量的分布。

bank %>% 
select_if(function(col) is.numeric(col) | all(col == .$Churn)) %>%
gather(key = "Variable", value = "Value", -Churn) %>% ggplot(aes(Value, fill = Churn)) + 
geom_histogram(bins = 20) + 
facet_wrap(~ Variable, scales = "free") + 
labs( title = "Numerical variables histograms", x = "" ) + scale_fill_economist()

作者图片

年龄越大,流失的可能性就越大

相关漏斗

相关性是理解变量之间关系的一个非常重要的度量。软件包correlation funnel生成一个图表,帮助我们了解所有变量(分类&数值)与流失的关系。

首先,它创建每个类别变量的二元变量和每个数值变量的 4 个箱(基于分位数)。它从最相关到最不相关的所有变量开始绘制。

# Create correlation Funnel 
bank %>% 
drop_na() %>% 
binarize() %>% 
correlate(Churn__Yes) %>% 
plot_correlation_funnel()

作者图片

看起来年龄、产品数量、地理位置和会员活动都很重要。平衡和性别不那么重要。信用评分、任期、预计工资和信用卡似乎对客户流失不重要,因为几乎所有类别的相关性都接近于零。

建模

现在,我们可以继续开发一个机器学习模型,预测所有客户的流失概率。

分割数据集

首先,我们将数据集分为两部分,训练和测试数据集。我们将使用训练数据集来训练我们的模型&测试数据集来检查模型的性能。

# Split the dataset in two parts 
set.seed(1) 
inTrain = createDataPartition(bank$Churn, p = .70)[[1]] # Assign the 70% of observations to training data 
training <- bank[inTrain,] # Assign the remaining 30 % of observations to testing data 
testing <- bank[-inTrain,]

为建模准备数据配方

这里,我们使用 recipes 包将相同的预处理步骤应用于训练和测试数据。

# Create the recipe object 
recipe_obj <- recipe(Churn ~ ., data = training) %>% step_zv(all_predictors()) %>% # check any zero variance features step_center(all_numeric()) %>% 
step_scale(all_numeric()) %>% # scale the numeric features 
prep()

根据配方处理数据

train_data <- bake(recipe_obj, training) 
test_data <- bake(recipe_obj, testing)

为建模设置列车控制

我们使用 TrainControl()函数在模型训练期间指定一些参数,例如重采样方法、K-Fold 中的折叠数等。

train_ctr <- trainControl(method = 'cv', number = 10, classProbs = TRUE, summaryFunction = twoClassSummary)

开发多种机器学习模型

开发一个逻辑回归模型

Logistic_model <- train(Churn ~ ., data = train_data, method = 'glm', family = 'binomial', trControl = train_ctr, metric = 'ROC')

开发一个随机森林模型

rf_model <- train(Churn ~ ., data = train_data, method = 'rf', trControl = train_ctr, tuneLength = 5, metric = 'ROC')

开发一个 XGBoost 模型

xgb_model <- train(Churn ~ ., data = train_data, method = 'xgbTree', trControl = train_ctr, tuneLength = 5, metric = 'ROC')

模型比较

在这一步,我们将比较模型的准确性。

model_list <- resamples(list(Logistic = Logistic_model, Random_forest = rf_model, XgBoost = xgb_model)) summary(model_list)## 
## Call: 
## summary.resamples(object = model_list) 
## 
## Models: Logistic, Random_forest, XgBoost 
## Number of resamples: 10 
## 
## ROC 
## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's 
## Logistic 0.7310692 0.7629073 0.7713738 0.7685092 0.7793850 0.7893184 0 
## Random_forest 0.8257989 0.8573118 0.8669290 0.8653269 0.8735644 0.8900139 0 
## XgBoost 0.8445059 0.8651163 0.8716812 0.8717819 0.8818719 0.8984382 0 
## 
## Sens 
## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's 
## Logistic 0.9408602 0.9573794 0.9605027 0.9612578 0.9654857 0.9820789 0 
## Random_forest 0.9425494 0.9546864 0.9631957 0.9608965 0.9681900 0.9802513 0 
## XgBoost 0.9498208 0.9609515 0.9641577 0.9648430 0.9677275 0.9784946 0 
## 
## Spec 
## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's 
## Logistic 0.1818182 0.2126465 0.2245395 0.2307003 0.2376761 0.3076923 0 
## Random_forest 0.3732394 0.4534128 0.4825175 0.4803211 0.5131119 0.5594406 0 
## XgBoost 0.4265734 0.4797843 0.4964789 0.4902098 0.5034965 0.5524476 0

基于 ROC (AUC 值)的最佳模型是 XgBoost 。最佳模型的 AUC 值(逻辑回归的平均值)为 0.87 。一般来说,AUC 值为 0.7 的模型被认为是有用的,当然这取决于问题的背景。

模型评估

# Predictions on test data 
pred_logistic <- predict(Logistic_model, newdata = test_data, type = 'prob') pred_rf <- predict(rf_model, newdata = test_data, type = 'prob') pred_xgb <- predict(xgb_model, newdata = test_data, type = 'prob') evaluation_tbl <- tibble(true_class = test_data$Churn, logistic_churn = pred_logistic$Yes, rf_churn = pred_rf$Yes, xgb_churn = pred_xgb$Yes) evaluation_tbl## # A tibble: 2,999 x 4 
## true_class logistic_churn rf_churn xgb_churn 
## <fct> <dbl> <dbl> <dbl> 
## 1 Yes 0.125 0.276 0.297 
## 2 No 0.234 0.178 0.0503 
## 3 Yes 0.289 0.758 0.997 
## 4 No 0.112 0.158 0.0631 
## 5 No 0.0600 0.002 0.0139 
## 6 No 0.160 0.006 0.0324 
## 7 No 0.0991 0.002 0.0349 
## 8 No 0.0669 0.042 0.0267 
## 9 No 0.232 0.164 0.246 ## 10 No 0.0311 0.056 0.0181 
## # ... with 2,989 more rows

受试者工作特征曲线

ROC 曲线或受试者操作特征曲线是说明二元分类器系统在其辨别阈值变化时的诊断能力的图。

即模型在不同阈值下的预测能力

# set the second level as the positive class options(yardstick.event_first = FALSE) # creating data for ploting ROC curve 
roc_curve_logistic <- 
roc_curve(evaluation_tbl, true_class, logistic_churn) %>% mutate(model = 'logistic') 
roc_curve_rf <- roc_curve(evaluation_tbl, true_class, rf_churn) %>% mutate(model = 'RF') roc_curve_xgb <- roc_curve(evaluation_tbl, true_class, xgb_churn) %>% mutate(model = 'XGB') # combine all the roc curve data roc_curve_combine_tbl <- Reduce(rbind, list(roc_curve_logistic, roc_curve_rf, roc_curve_xgb)) head(roc_curve_combine_tbl,10)## # A tibble: 10 x 4 ## .threshold specificity sensitivity model ## <dbl> <dbl> <dbl> <chr> ## 1 -Inf 0 1 logistic ## 2 0.0115 0 1 logistic ## 3 0.0118 0.000419 1 logistic ## 4 0.0134 0.000838 1 logistic ## 5 0.0137 0.00126 1 logistic ## 6 0.0141 0.00168 1 logistic ## 7 0.0144 0.00209 1 logistic ## 8 0.0149 0.00251 1 logistic ## 9 0.0152 0.00293 1 logistic ## 10 0.0154 0.00293 0.998 logistic# Plot ROC curves roc_curve_combine_tbl %>% ggplot(aes(x = 1- specificity, y = sensitivity, color = model))+ geom_line(size = 1)+ geom_abline(linetype = 'dashed')+ scale_color_tableau()+ labs(title = 'ROC curve Comparison', x = '1 - Specificity', y = 'Sensitivity')

作者图片

AUC 值越大,模型精确度越好。

特征重要性

理解模型是如何工作的很重要,尤其是在客户流失模型的情况下。一种常用的度量是全局特征重要性。它衡量每个变量对客户流失预测的贡献。

这种重要性是为数据集中的每个属性显式计算的,允许对属性进行排序和相互比较。对于单个决策树,重要性通过每个属性分割点改进性能度量的量来计算,通过节点负责的观察的数量来加权。然后,对模型中所有决策树的特征重要性进行平均。

ggplot(varImp(xgb_model)) + 
labs(title = "Feature Importance for XGBoost churn model")

作者图片

看起来前 2 个最重要的特征是:
年龄
产品数量

预测新客户的流失

当我们想要对客户进行预测时,我们可以遵循以下流程。

new_data <- read_csv(file = "/Users/manos/OneDrive/Projects/R/All_Projects/Churn/data/new_data_bank.csv") new_data_recipe <- bake(recipe_obj, new_data) new_dat_pred <- predict(Logistic_model, newdata = new_data_recipe, type = 'prob') %>% 
select(Yes) %>% 
rename(churn_prob = Yes) %>% 
bind_cols(new_data) %>% 
mutate(churn_group = ntile(churn_prob, n = 10)) %>% select(churn_prob, churn_group, everything()) %>% 
mutate(churn_prob = round(churn_prob, 2)) new_dat_pred %>% select(churn_prob, churn_group, Age, NumOfProducts, Geography, HasCrCard, Tenure) %>% head(10) %>% 
kable() %>% 
kableExtra::kable_styling()

作者图片

创建流失风险排名

虽然我们开发了一个模型,可以很好地预测客户是否会流失,但该模型的输出概率在业务环境中并不充分。我们需要一些能够被所有利益相关者理解和容易使用的度量标准,并消除例如向非技术利益相关者解释阈值的复杂性。

因此,一个流失风险排名会比一个实际概率更有用。因此,我们将概率变量分解为 10 个流失风险桶。现在客户的流失风险从 1(最低概率)到 10(最高概率)

防止流失的策略

最初的策略是为不同的客户流失风险群体开发不同的销售方案(或营销活动)。

例如,属于流失风险组 10 和 9 的客户比示例 1 和 2 具有明显更高的流失风险。因此,向他们提供更多的东西来留住他们是至关重要的。

https://www.manosantoniou.com】最初发表于https://www.manosantoniou.com/post/solve-a-real-world-churn-problem/

用 FICO Xpress 在 Python 中求解线性规划

原文:https://towardsdatascience.com/solve-linear-programming-in-python-with-fico-xpress-599f204fb34a?source=collection_archive---------13-----------------------

为什么我们有 LP?因为我们希望利用手头的资源每次都做到最好

安妮·斯普拉特在 Unsplash 上的照片

介绍

数学优化是应用数学的一个分支。拥有这个分支的最简单的原因是我们需要在标准下找到最优解。在现实生活中,这个最优解往往是利润最大化或成本最小化。标准涉及限制当前情况的有限资源和约束。给定这些标准,我们应该做什么来实现最佳解决方案?我们使用公式和不等式来描述情况,并定义一个函数来衡量结果。这样就建立了一个数学模型。

数学优化的一个特例是线性规划(LP)。“线性”表示数学模型中的所有公式都是线性形式,即没有平方函数、平方根函数等。

例子

一家公司使用两种原材料生产两种油漆,一种用于室内,一种用于室外。下表显示了每种原材料中每种油漆的成分,以及每种原材料每天的供应情况。两种油漆的利润也列了出来。

而且因为市场的原因,每天室内涂料的需求量不能超过室外涂料 1 吨以上。该公司还知道,室内涂料的最大需求量是每天 2 吨。

问题是,在所有这些限制下,我们如何能使利润最大化。因此,我们需要知道每天生产多少吨室外涂料和室内涂料。

LP 中的 4 个常见要求

对于每个 LP 问题,有 4 个共同的要求。下面我将使用上面的例子展示所有 4 个。

  1. 如前所述,LP 问题寻求实现最优解,最大化或最小化某个数量,也就是目标函数。在示例中,我们希望实现利润最大化。换句话说,我们想知道外部和内部油漆的日产量,以便 5(外部油漆的日产量,以吨计)+ 4(内部油漆的日产量,以吨计)最大化。如果我们将 X1 定义为室外油漆的日产量(以吨计),将 X2 定义为室内油漆的日产量(以吨计),那么目标函数就是 5X1+4X2,我们希望最大化这个函数。
  2. 必须有一个或多个约束来限制追求目标的程度。我们称之为约束。在这个例子中,有四个约束。第一个和第二个是原材料 M1 和 M2 的最大日供应量。第三个是内外墙涂料的市场界限。最后一个是室内涂料的最大需求。我们可以将这些限制转化为不平等。
  3. 在 LP 问题中,必须有可供选择的行动方案。这意味着我们可以选择任何可变结果的组合来获得最优解。在这个例子中,没有限制生产室外或室内油漆需要多少吨 M1 或 M2。
  4. 目标和约束必须用线性方程或不等式来表示。在上面的例子中,它们都是线性的。

4 个限制因素

在大多数 LP 问题中,也有一个常见的假设,即变量不能是负的。这个假设甚至经常被认为是 LP 问题的第五个要求。在这个例子中,我们还将两个变量限制为非负。

用 FICO Xpress 求解线性规划

我们在 python 中需要做的事情和我们刚刚做的事情类似。我们首先定义变量。然后我们定义目标函数和这 4 个约束。最后,我们请求 Xpress 解决 LP 问题并提供解决方案。

import xpress as xp

FICO Xpress 提供了一个社区许可证,对于这个演示来说已经足够了。

# Define the LP problem
model = xp.problem(name='Paints')

这一步创建了一个名为 Paints 的空问题。

# Define variables
x1 = xp.var(name='exterior')
x2 = xp.var(name='interior')
model.addVariable(x1,x2)

在帮助文档中,var有多个参数,如下所示

xpress.var(name='', lb=0, ub=xpress.infinity, threshold=-xpress.infinity, vartype=xpress.continuous)

您可以为每个变量指定一个名称,以便清楚地展示。lbub用于设定下限和上限。如前所述,非负性经常需要 Xpress 将缺省值lb指定为 0。另一个重要的论点是vartype。这可以进一步将变量限制为不同的变量类型,包括二进制、整数等。此链接进一步解释了var Xpress 类型。

# Define constraints 
constr1 = 6*x1+ 4*x2 <= 24
constr2 = x1+x2 <=6 
constr3 = x2-x1 <=1 
constr4 = x2<=2

现在我们可以直接构建这 4 个约束。你所需要做的就是像在公式表单中那样键入它们。每个变量的类型都是xpress.constraint

xpress.constraint (constraint=None, body=None, lb=-xpress.infinity, ub=xpress.infinity, sense=None, rhs=None, name='')

在上面,我们包括了约束的完整形式。另一种方法是把它分解成bodylbub

# Same as constr1
xp.constraint(body = 6*x1+ 4*x2,ub = 24)

一旦我们定义了所有的约束,我们就可以将所有的变量和约束输入到模型中。您可以逐个添加约束,也可以使用约束列表/元组/数组一起输入它们。

problem.addVariable(v1, v2, ...)

Argument:
v1,v2...: 

  Variables or list/tuples/array of variables created with the
  xpress.var constructor or the
  xpress.vars function.bproblem.addConstraint(c1, c2, ...)

Argument:
c1,c2...: 

  Constraints or list/tuples/array of constraints created with the xpress.constraint() call.

所以我们现在将它们添加到模型中。

# Add variables
model.addVariable([x1,x2])# Add constraints 
model.addConstraint([constr1,constr2,constr3,constr4])

最后一部分是目标函数。

model.setObjective(5*x1+4*x2,sense=xp.maximize)

你可以看到setObjective只有两个参数。一个是expr,一个是sense.

problem.setObjective(expr, sense=xpress.minimize)

Arguments:
expr: 
 A linear or quadratic function of the variables
that were added to the problem prior to this call. An error will be
returned if any variable in the linear or quadratic part of the
objective was not added to the problem via
addVariable.
sense: 
 Either xpress.minimize or xpress.maximize.

sense用来定义我们是否要最大化或最小化目标函数。

最后可以请 Xpress 来求解模型。

model.solve()FICO Xpress v8.13.1, Community, solve started 18:15:59, Dec 5, 2021
Heap usage: 331KB (peak 331KB, 285KB system)
Maximizing LP Paints using up to 4 threads, with these control settings:
OUTPUTLOG = 1
Original problem has:
         4 rows            2 cols            7 elements
Presolved problem has:
         3 rows            2 cols            6 elements
Presolve finished in 0 seconds
Heap usage: 332KB (peak 344KB, 287KB system)

Coefficient range                    original                 solved        
  Coefficients   [min,max] : [ 1.00e+00,  6.00e+00] / [ 5.00e-01,  1.50e+00]
  RHS and bounds [min,max] : [ 1.00e+00,  2.40e+01] / [ 1.00e+00,  6.00e+00]
  Objective      [min,max] : [ 4.00e+00,  5.00e+00] / [ 4.00e+00,  5.00e+00]
Autoscaling applied standard scaling

   Its         Obj Value      S   Ninf  Nneg   Sum Dual Inf  Time
     0         28.000053      D      2     0        .000000     0
     2         21.000000      D      0     0        .000000     0
Uncrunching matrix
Optimal solution found
Dual solved problem
  2 simplex iterations in 0.00 seconds at time 0

Final objective                       : 2.100000000000000e+01
  Max primal violation      (abs/rel) :       0.0 /       0.0
  Max dual violation        (abs/rel) :       0.0 /       0.0
  Max complementarity viol. (abs/rel) :       0.0 /       0.0

发现 obj 值= 21 的最优解!

所以我们可以看到最优解。

model.getSolution()
[3.0, 1.5]

所以我们每天应该生产 3 吨外部油漆和 1.5 吨内部油漆。

结论

线性规划常用于资源分配,求解线性规划的方法很多。在这里我向你展示使用 Python 来解决。希望你喜欢这篇文章,下次再见。

使用线性编程解决数独(Python-PuLP)

原文:https://towardsdatascience.com/solve-sudoku-using-linear-programming-python-pulp-b41b29f479f3?source=collection_archive---------2-----------------------

利用线性规划的概念解决数独难题

数独+线性规划(图片作者编译。数独图片— 维基

数独是一种典型的涉及到笔、纸和思维共同作用的游戏。当你想让笔和大脑休息一下时,可以用数独解算器来解决。在这里,我们将尝试创建一个这样的数独求解器。

本文旨在利用线性规划的概念来解决数独难题。我们将从刷新线性编程背后的一些基本理论开始,然后使用 PuLP 包在 Python 中实现相同的内容。

在这里,我们将尝试解决两种类型的数独难题,常规数独和对角线数独,它们在数独网格的对角线上有额外的规则。随着我们的进展会有更多的报道。

什么是线性规划?

线性规划是一个约束优化模型,它有 3 个主要部分:目标函数、决策变量和约束。

目标函数: 目标函数是一个线性函数,其值需要根据我们试图解决的问题最小化或最大化。

决策变量: 这些是决定最终输出的输出或目标变量。

约束: 约束是对决策变量的约束或限制。

线性规划的目标是在应用所有约束的情况下,为给定的目标函数找到一组最优的决策变量。该解决方案可以是可行的解决方案或最优的解决方案。

可行解: 满足所有约束的线性规划的解。

最优解: 以最佳目标函数值(最大化或最小化目标函数)

线性规划和数独

数独的线性规划(图片由作者提供)

以数独为例,我们需要确定一个可行的解决方案来解决它。

用 Python 中的线性规划求解数独

这里我们将使用 Python 中的 PuLP 包来解决这个线性编程问题。

解决数独问题的步骤:

步骤 1: 定义线性规划问题
步骤 2: 设置目标函数
步骤 3: 定义决策变量
步骤 4: 设置约束
步骤 5: 解决数独难题
步骤 6: 检查是否找到最优结果

步骤 1:定义线性规划问题

第二步:设置目标函数

通常在线性规划中,我们有一个目标函数,我们试图最大化或最小化。但是在这种情况下,我们没有任何目标函数。这更像是一个可行性问题,也就是说,如果满足约束条件,那么数独就解决了。

因此,我们在这里设置一个虚拟目标函数为 0。

步骤 3:定义目标变量或决策变量

数独网格由 81 个单元格组成(9x9 网格)。每个单元格可以取 1 到 9 之间的值。如果我们为每个值创建一组布尔决策变量,那么我们总共会得到 729 个变量(9x9x9)。

由于每个单元格只能有一个关联值,因此单元格的 9 个变量中只有一个布尔变量可以设置为 true,其余的应为 false。正如您将在下一节中看到的,这是通过约束来确保的。

步骤 4:设置约束

数独的一般约束

需要将数独的规则设置为约束来解决这个问题。
鉴于数独是一个 9x9 的格子,游戏规则在这里提一下:

约束 1: 每个单元格都应该填充一个介于 1 和 9 之间的值

约束 2: 每一行应该包含一次从 1 到 9 的所有数字

约束 3: 每一列都应该包含一次从 1 到 9 的所有数字

约束 4: 从左上角开始,每个 3x3 网格应该包含 1 到 9 的每个数字一次

对角数独的附加约束

还有另一个版本的数独,称为对角线数独,除了上面提到的通用规则,还应该满足以下约束。

约束 5: 每条对角线应该包含 1 到 9 中的每个数字一次

注意,这个约束需要应用于两条对角线(左上角到右下角和右上角到左下角)。

初始化输入数独难题的约束

既然游戏规则已经通过约束设置好了,下一步就是初始化拼图中的已知值。这是从我们试图解决的数独难题中提取的。填充预先填充的单元格作为 LP 问题的约束。

至此,解决数独游戏所需的所有约束都已设置好,我们已经准备好解决它了。

第五步:解决数独难题

一旦设置了目标函数、决策变量和约束,解决难题就像对问题变量调用 solve 方法一样简单。

步骤 6:检查是否找到最佳结果

一旦问题得到解决,我们就可以通过检查状态标志来检查算法是否已经确定了最优解。

如果结果是“最优的”,那么线性规划算法已经确定了具有给定约束的解决方案。如果找不到解决方案,它将返回“不可行”状态。

时间来解决

将上面的代码包装成一个函数,这样它就可以解决作为输入传递的任何数独。下面给出了几个例子,其中正常数独和对角线数独是用这个求解器求解的。

数独 1-普通数独

输入

输入——普通数独(图片由作者提供)

输出

解出的数独——正常(图片由作者提供)

数独 2 —对角线数独

投入

输入——对角线数独(图片由作者提供)

输出

解出的数独——对角线(图片由作者提供)

源代码

参考 git repo 获得完整的代码: Sudoku_Solver_LP

结论

这是一个非常简单的方法,说明如何使用 Python 中提供的线性编程包(即 PuLP)来解决数独难题。

还有进一步增强的空间,如从图像中读取数独网格或通过网络摄像头读取网格的视频处理。

稍后会有更多,希望你喜欢这篇文章!!

使用开源数据科学堆栈解决您的 MLOps 问题

原文:https://towardsdatascience.com/solve-your-mlops-problems-with-an-open-source-data-science-stack-c76a484c92c8?source=collection_archive---------24-----------------------

奥马尔·弗洛雷斯在 Unsplash 上拍摄的照片

TL;速度三角形定位法(dead reckoning)

数据科学家面临挑战,需要工具来克服这些挑战。最好使用开源、同类最佳的模块化解决方案。从问题解决的角度考虑这些挑战也是一个好主意,而不是“给我最棒的工具”的方法。我提供了一个常见问题和这些问题的 OSS 解决方案的列表,并对这些问题何时变好/变坏进行了评论。跳到列表

一个熟悉的问题

如果你是一名从事机器学习项目的数据科学家,你可能已经注意到了工作中反复出现的一个问题。

  1. 你有很多问题,似乎有很多工具适合他们,但每个人都声称他们可以做任何事情,而且很难知道每个任务使用什么工具。更糟糕的是,您已经尝试了其中的一些工具,并感到失望。因为它们每个都有一个学习曲线,尝试另一个很可怕,可能会在你最终需要内部构建的东西上浪费更多时间。
  2. 快速行动至关重要因为你需要向你组织的其他人展示这种新的人工智能事物实际上可以转化为商业价值,并且你希望专注于你最擅长的事情——理解你的数据,转换它,或者将其转化为商业见解,或者为你的产品/公司培训 ML 驱动的组件。
  3. 你知道你是团队的一员——你的公司将成为一艘火箭船,你很快就会有一个庞大的团队(如果你还没有的话),你已经需要与组织中的其他团队合作,比如领域专家、软件工程师和开发人员。
  4. 最后,您意识到这一领域瞬息万变,因此选择一家可能过时的供应商,或者不支持您将来会使用的格式/工具是有问题的。但是,另一方面,分析麻痹也好不到哪里去(回到第 2 点)。

这家伙绝对不会将模型交付生产。廷杰律师事务所Unsplash 拍摄的照片

我很高兴地告诉你,这个问题是可以解决的。您不必在购买可定制性较差、可能意味着您没有应对所面临挑战的最佳解决方案的端到端平台和构建可能完全符合您需求的内部解决方案之间做出选择,但这意味着您不再是一名数据科学家(恭喜您,您现在是一名软件工程师,这是晋升还是降级取决于您)。

这个黑客使用开源软件创建了一个开源数据科学栈。由于 ML 世界快速变化的本质,对于数据科学家面临的许多问题,有数百种开源解决方案,这些解决方案不会遇到上述相同的问题,我将在下面做出一些假设:

假设以下情况:

  1. 最佳解决方案被定义为针对给定问题的最佳解决方案,它能够与您使用的其他最佳解决方案相对容易地集成。这意味着,任何要求您放弃其他与问题无关的工具来使用它的出色解决方案都不算数。
  2. 你不需要为了工具而工具——你有你想要解决的问题,或者你想要改进的流程,你需要工具来帮助你完成这些事情。这个假设很重要。否则,您将要求一切,这意味着您将最终使用一个端到端平台,当您需要解决更具体的挑战时,该平台可能会崩溃。
  3. 数据科学家需要与开发人员不同的工具 —你应该立志不要重新发明轮子(例如,开发人员的自动化工具可能非常适合机器学习工作负载),但处理代码并不是数据科学的全部。您至少需要考虑如何处理数据、模型、管道和实验。具体来说,在将被推广到生产的 ML 项目中,再现性需要被适当地解决。

现在,符合上述假设的开源堆栈以下列方式解决了这些挑战:

  1. 知道使用哪些工具是这篇文章的目的。此外,通过为您的问题选择易于集成的单点解决方案,您可以避免失望——如果该工具不能解决您的问题,那么端到端平台可能也不能。
  2. 您可以快速行动因为一旦出现问题,您会选择一个符合假设的工具,即它解决了一个实际问题,并且是您的问题的最佳解决方案,同时与您的堆栈的其余部分集成。这意味着您花在选择工具上的时间更少,将它与堆栈的其余部分集成的时间也更少。
  3. 在团队中工作已经解决了,因为你将从寻找已经在组织的其他部分采用的工具开始。如果没有一个解决您的问题,这意味着您应该看看外面,通过选择与您的堆栈的其他部分集成的最佳解决方案,您将使其他人也很容易采用它们。这可能会大大缩短您的生产时间。
  4. 该领域的快速变化特性得到了解决,因为如果您根据上述假设选择工具,您的平台就是完全模块化的。用新工具替换旧工具应该只需要最少的集成和时间。

通过选择开放堆栈(即开源工具),您可以获得两个额外的好处:

  1. 如果出现特殊需求,可以根据您的需求修改工具。
  2. 你从与你职位相同的人那里获得社区支持——使用工具和经历挑战。这也意味着评估工具的缺陷要容易得多(只需进入他们的问题页面)。
  • 当然,使用开源并不等同于避免锁定。比起晦涩难懂的或新的标准,你应该总是更喜欢被广泛支持的通用格式。

我上面写的一切都是现实的理想表现。在现实生活中,事情要复杂得多,还有其他的考虑因素在起作用。然而,使用这种思考过程和假设将会节省你的时间、空间和潜在的悲伤和金钱。

本文的其余部分将是我在过去几年中听到的数据科学家提出的问题,以及一些可用于将它们整合到您的堆栈中的开源工具。我会试着在每一个上面添加一些注释,以便在它更适合/不太适合这个任务的时候使用。

10 个 MLOps 问题以及如何使用开放式堆栈解决这些问题:

1.问题:您有不断变化的数据,并且您想要时间旅行/灾难恢复/治理:

您的数据是非结构化的:

  • https://github.com/iterative/dvc DVC——可能是这个列表中最轻量级的解决方案。如果你想最快上手,这是你的选择。
  • pachyderm—https://github.com/pachyderm/pachyderm—pachyderm 专注于 K8s 之上的管道,提供数据版本控制作为其管道系统的副产品。如果您在 K8s 之上工作,并且正在寻找一个重载管道解决方案,这可能适合您。
  • LakeFS—https://github.com/treeverse/lakeFS—您的整个数据湖的版本。这对于非常大的规模是很好的,并且对于大规模流有很多优化,但是可能需要您的组织采用它

您的数据被结构化或存储在您可以查询的数据库中(在这种情况下,您需要同意更改您的数据库):

注意:这三种结构化数据解决方案都不如非结构化数据解决方案轻便,因为它们需要将数据转换成专用的数据格式或数据库。这对你来说可能是一个交易破坏者。

这个问题经常出现,但通常,提问者会询问数据版本化的解决方案。本着解决问题的精神,而不是为了添加工具而添加工具,回答两个主要问题很重要:

数据版本控制将完成什么任务:

  • 您希望能够使用不同版本的数据来比较模型的行为吗?
  • 您是否需要一个解决方案,以便在出现错误时恢复到早期版本。?
  • 您是否在寻找一种解决方案来了解哪些数据在哪里使用?
  • 您是否在寻找一种方法来添加/修改数据,而不破坏下游的消费项目?

您正在管理什么类型的数据:

  • 它是否存储在数据库中,并且您想要管理整个数据库的版本?
  • 它是否存储在数据库中,但您希望只管理查询到特定项目中的数据?
  • 是像图像、文本或音频这样的非结构化数据吗?

每个问题的不同答案可能需要不同的工具。

2.问题:您不确定 ML 项目使用什么结构:

考虑哪种结构对您的项目有意义可能会令人沮丧。你应该把你的数据和模型放在同一个文件夹里吗?在这方面还没有做很多工作,但 Cookiecutter 数据科学似乎是一个反复出现的名字,所以当你有疑问时,你可以使用它,它可能会满足你的需求(同时减轻你的精神负担)。

3.问题:您不确定如何跟踪您的实验元数据*:

这可能不是一个问题——但是人们一直在问这个问题,所以我也可以解决这个问题。有这么多的解决方案,所以选择的悖论在这里很强烈,但幸运的是,你可以阅读我们的另一篇帖子,关于选择哪个实验跟踪解决方案最适合你

这里需要注意一些事情:

  • 你应该尽可能多地记录,同时保持一种区分“垃圾”实验和“好”实验的方法(标签是一种很好的方法)。
  • 避免完全删除不好的实验。他们可以教你很多好的实验。最好将它们放在一个单独的视图中。
  • 最好使用通用格式,而不是专有或晦涩的格式。这将让您轻松地分析您的实验元数据。

4.问题:您已经有了一个模型,并且您认为您可以通过微调它的超参数来获得额外的几个百分点:

注意:在大多数情况下,超参数调优并不像描述的那样有效。通常,努力提高数据质量是一个更好的主意。

不过,这里有两个很棒的工具:

5.问题:你需要一个免费的 GPU(我们不都是吗):

照片由 Nana DuaUnsplash 上拍摄

6.问题:您希望尽可能多地实现自动化。在更改参数/代码/数据时,您决定从模型的训练开始,或者更一般地说,从管道的运行开始:

它必须与您组织中的开发人员正在使用的工具一起工作吗?

您能否为您的 ML 管道创建专用服务:

7.问题:您想要通过 API 访问您的模型:

另一个经常出现的问题是“您如何将您的模型部署到生产中?”。虽然这是一个需要完成的重要任务,但它通常是非常具体的。很难找到一个放之四海而皆准的解决方案,您需要注意以下主题:

  1. 我只需要部署模型吗?或者,我是否需要一个部署整个数据处理管道的解决方案,否则将永远不会生成正确的模型输入?
  2. 我是在使用 Scikit-Learn Pipelines 这样的标准框架,还是开发了定制的数据处理步骤/模型?
  3. 我的部署系统有“工程”约束吗?它应该支持每小时数百万的请求吗?它的延迟是否应该低于某个阈值?在有压力的情况下,它应该是可伸缩的吗?

这些问题会引导你做出权衡。对系统的约束越多,需要的前期工程工作就越多,但是如果您只需要部署一个“简单”的模型推理 API,下面的方法可能会对您有所帮助:

  • 谢顿—https://github.com/SeldonIO/seldon-core或 KFServing—https://github.com/kubeflow/kfserving—这两者都与 K8s 有关,所以这可能是一个主要的考虑因素。Seldon 集成到 KFServing 中,但你也可以把它作为一个独立的项目,它可能更成熟。支持标准模型格式,并允许您在需要时定义自定义包装器。
  • BentoML——为模型提供包装器(创建一个 docker 容器),但不提供容器本身的编排。在这个意义上,它可以和谢顿一起使用。与谢顿相比,他更固执己见,因此可能不太灵活。
  • cortex—https://github.com/cortexlabs/cortex—开始是一项 ML 部署服务,从那以后开始关注更广泛的“部署、管理和扩展无服务器容器”。它目前只支持 AWS,但是已经被广泛采用,并且支持多种类型的 API(实时、批处理、异步),这对于某些应用程序来说可能是至关重要的。

8.问题:您希望更好地了解模型的输出,以增加您对其性能的信任:

这对您来说可能是一个严重的问题,尤其是如果您的项目/组织需要确定性的和可解释的预测来推广到生产中(例如,当您不能冒险让您的模型有偏差时)。这是一个活跃的开发领域,您很可能需要为您的工作开发一些定制的可视化,特别是如果它不是一个通用领域(人脸识别或文本翻译)。然而,有一些框架可以让你的生活更轻松:

记住,在许多情况下,最好的解决方案是使用更简单的模型,这也是一个好主意。许多公司选择基于树的模型,因为它们本质上比神经网络更容易解释,同时在性能上不会损失太多。记住这不是一个非此即彼的情况也是一个好主意。您可以使用以上所有选项,并在需要时为您的仪表板提取细节。

9.问题:您想向某人(可能是内部利益相关者)展示您的工作演示,但没有学习 HTML、CSS 和 JS:

这一个是相当不言自明的。

10.问题:你已经有了一个生产中的模型,你担心它可能会出问题,所以你需要一些方法来确保预测是有意义的:

针对这个问题的开源解决方案相对较少,下面的解决方案虽然很早,但可能对您的需求有用。

  • ml watcher—https://github.com/anodot/MLWatcher—更专注于实时监控,特别是概念漂移、数据异常和模型度量。
  • 显然——https://github.com/evidentlyai/evidently——一个基于报告的监控系统,它分析模型并为特定主题(如数据漂移和模型性能)提供交互式报告。据我所知,它们不提供任何实时功能,这意味着如果这对您很重要,您将需要手动设置。

摘要

这是一个诚实的尝试,以减轻一些分析瘫痪,以及“购买者”在选择 ML 工具栈时可能有的懊悔。我坚信,随着团队更好地理解他们想要解决的问题,以及他们愿意做出的权衡,开放堆栈将会出现。

通过选择模块化的开源工具,并解决您面临的实际问题,您将自己置于最佳位置,以利用已经为社区开发的自动化和最佳实践,同时仍然保留升级、更改和向堆栈添加新组件的能力。

如果您有其他通过合并最佳开源解决方案解决的问题,请告诉我,我会将它添加到文章中。

解决了!Power BI 中本地 SQL 的查询折叠

原文:https://towardsdatascience.com/solved-query-folding-for-native-sql-in-power-bi-c94ebc604d1d?source=collection_archive---------9-----------------------

在 Power BI 中编写原生 SQL 查询会破坏查询折叠,对吗?嗯,不再是了

作者图片

谈到技术(尤其适用于 Power BI ),唯一不变的是——变化!而且,当我说 Power BI 的变化时,我并不仅仅指每个月都会有大量新功能的定期更新。现有的功能也在不断改进和升级,所以很容易发生这样的情况,几个月前不支持的东西,或者您必须执行不同的工作区来找到解决方案,现在默认工作。

其中一个很酷的新功能是可以为手写的 SQL 查询实现查询折叠!正如我之前在这个由三部分组成的系列文章中所写的,经验法则是:一旦您决定编写一个自定义 SQL 来将数据导入 Power BI,您就要对所有后续步骤的查询折叠说“再见”了。

首先什么是查询折叠?

在我们直接开始之前,让我先解释一下什么是查询折叠。

用最简单的话来说: 如果 Power Query 引擎能够收集您的所有转换,并生成一条将在源端(大多数情况下是 SQL 数据库)执行的 SQL 语句,我们称之为查询折叠!

这样,通过将转换和计算推到数据源端,在大多数情况下,您将获得数据刷新过程的性能优势。我不打算通过不同的场景来重申查询折叠何时可行或不可行——我强烈建议您阅读我已经提到的文章—并参考微软的官方文档。

在本文中,当您决定使用原生 SQL 查询作为 Power BI 数据集的源时,我将重点关注一个特定的场景。

Power BI 中的原生 SQL 查询是什么?

当您将数据导入 Power BI 时,需要做出的第一个决定是:您是否希望从 SQL 数据库中“按原样”获取数据,然后使用 Power Query editor 应用必要的转换…或者,您是否希望编写自己的 SQL 代码来获取数据。

如果您选择编写自定义 SQL,在 Power Query 编辑器中应用的所有后续转换步骤都不会折叠,即使您正在应用一些基本转换,如过滤或重命名列,这些步骤在“正常”情况下会折叠。

这个说法应该没错吧?嗯,不再是了:)

作者图片

在上图中,我正在从 Contoso 示例数据库中的 FactOnlineSales 表中导入所有行和列。该表包含大约 1260 万行。

打破查询折叠的“传统”方式

其中一列是销售额。现在,假设我只想保留销售额值大于 400 的那些行。我将打开 Power Query 编辑器并添加这个转换步骤:

作者图片

在我应用了这个步骤之后,如果我右键单击它,我可以看到 View Native Query 选项是灰色的,这意味着我的查询可能不会折叠。在这种情况下,这是正确的,所以让我们检查一下这个查询在 Power BI 中加载需要多长时间。

刷新数据花费了大约 160 秒,但下图显示了后台发生的情况:

作者图片

基本上,Power Query 引擎必须从底层 SQL Server 数据库的 FactOnlineSales 表中提取所有数据,然后应用我们指定的过滤条件!最后,大约有 210 万条记录满足我们的销售额标准。因此,无论我们在第一条语句(编写定制 SQL)之后应用什么转换步骤,查询都不会折叠!

神奇的 M 功能来拯救

现在让我们尝试另一种方法。灵感来自Chris Webb 的这篇很棒的博文,我只是在下面的例子中稍微调整了一下:

作者图片

您可能注意到了,我不会在启动窗口中编写 SQL 语句。我将把它留空,并从 Contoso 数据库中导入所有数据库对象(所有表、视图和表值函数):

作者图片

除了使用 300 多种内置的 Power Query 转换之一,您还可以编写自己的 M 代码来应用于数据。因此,如果我右键单击最后一步,我可以选择在后插入一步:

作者图片

这个自定义步骤允许我在公式栏中手动输入 M 公式,因此我将输入以下 M 代码:

= Value.NativeQuery(Contoso,"SELECT * FROM FactOnlineSales",null,[EnableFolding=true])

“神奇”发生在值内。NativeQuery() 函数。你可以在这里阅读关于这个函数及其参数的更多信息。我们可以省略第三和第四个参数,查询仍然可以工作,但是通过提供第四个参数 EnableFolding ,并将其标记为 TRUE,我们显式地指示 Power Query 引擎启用查询折叠!

现在让我们应用与上一个案例完全相同的过滤器—只保留销售额值大于 400 的那些行。如果右键单击最后一步,应该会启用查看本地查询选项。当我点击它时,我看到我的查询现在折叠了,因为过滤条件被很好地翻译成 SQL where 子句!

作者图片

最后,让我们检查一下这种变通方法是否对数据刷新过程有影响:

作者图片

这一次,通过将过滤器推送到 SQL 数据库,Power Query 只检索我们需要的那些行。这显然影响了数据刷新处理时间,因为它花费了大约 20 秒

加盟还是不行!

我尝试对此进行扩展,并在原生查询中组合多个表:

= Value.NativeQuery(Contoso,"SELECT * FROM FactOnlineSales AS fco INNER JOIN DimCustomer AS c ON c.customerKey = fco.customerKey",null,[EnableFolding=true])

但是,我收到了以下错误:

作者图片

结论

默认情况下,编写定制的 SQL 代码将数据导入 Power BI 仍然会破坏查询折叠。但是,正如您所看到的,有一种简便的方法可以“强制”Power Query 引擎利用查询折叠,即使在您决定使用原生 SQL 查询选项的情况下也是如此。

老实说,这种技巧的用例数量有限,因为您不能像在开始数据导入窗口中以“传统”方式编写 SQL 代码那样,在一条 SQL 语句中组合多个数据库对象。不过,使用定制 SQL 进行数据转换的首选方式是在源数据库中创建一个视图。视图将包含整个转换逻辑,然后您可以在 Power BI 中“按原样”导入它。数据库视图是可折叠的对象,这意味着如果对视图应用可折叠的转换步骤,您仍然可以从查询折叠中受益!

然而,很高兴看到事情正在向前发展,让我们希望在不久的将来,本地 SQL 查询的查询折叠将默认启用。

感谢阅读!

用极小极大解一个文字游戏

原文:https://towardsdatascience.com/solving-a-word-game-with-minimax-68695c5e963?source=collection_archive---------10-----------------------

有没有一个字母表示参与人 1 总是赢?

目录

[The Game](#e96a) [Introducing Minimax](#3275)
[Applying Minimax](#8a50)
[Improvements](#9455)
[Conclusion](#4c6f)

游戏

一个简单的双人文字游戏可以这样描述:

玩家 1 首先从一个至少 4 个字母长的单词开始,例如“medium”。他弹奏这个单词的第一个字母,即 m。然后,玩家 2 选择下一个字母,这个字母代表他知道的以“m”开头的单词。然后,两位玩家继续轮流选择字母,同时尽量不选择导致单词形成的字母(只计算至少有 4 个字母的单词)。

例如,如果当前的字母串或前缀是“medi ”,玩家 1 不会选择字母“a ”,因为“media”是一个有效的单词,这会导致他输。

唯一的规则是,每个玩家在游戏的任何时候都必须有最后的发言权(如果受到挑战,必须提供)。否则你可以随便玩一个字母,游戏永远不会结束。

一个典型的博弈(参与人 2 输了)可能是这样的:

Player 1: m
Player 2: e
Player 1: d
Player 2: i
Player 1: u
Player 2: m

这个简单的文字游戏和你的朋友一起玩很有趣。我鼓励你在继续阅读之前先自己尝试一下这个游戏。

介绍 Minimax

在玩了几轮游戏后,你可能会开始想,如果我们可以提出一种算法,告诉我们在给定当前前缀的情况下,最佳的下一个字母是什么呢?更好的是,如果它能告诉我们参与人 1 可以选择什么字母,这样他们就能一直赢,那么我们就可以宣布这个游戏解决了!

正如这篇文章的标题所暗示的,事实上有一种算法我们可以使用:minimax。

Minimax 是一个简单而强大的算法,旨在帮助人工智能、博弈论和统计学中的决策。它最初用于零和游戏,如国际象棋,但已被推广到帮助决策,即使有不确定性。

解释极小极大的最好方法是使用一个树形图,显示一个游戏可能有的所有可能的游戏状态。

一般极大极小图(图片由作者提供)

从上面的树形图可以看出,作为价值函数实现的极大极小算法的目标是为特定的游戏状态赋值。在这种情况下,所有状态的值都是相对于我们想要赢得的玩家而言的。值较高的州意味着玩家更有可能获胜,而值较低的州意味着对手更有可能获胜。此外,轮到玩家的州标为蓝色,而对手的州标为红色。

为了指定一个特定状态的值(在这种情况下,我们最终想要指定一个值给状态 A),算法必须递归地考虑所有后续的游戏状态。对于蓝色状态,由于轮到玩家,我们将选择导致具有最高值的状态的移动,这就是为什么它们被标记为“最大”状态。例如,状态 A 将选择右边的移动,因为与值为 2 的状态 B 相比,它将导致值为 3 的状态 C。相反,作为对手的州,红州将选择导致具有最低值的州的移动。因此,它们被称为“最小”状态。例如,状态 C 将选择左边的移动,因为它导致具有较低值 3 的状态 D,而不是具有值 4 的状态 E。

这样,我们经历了一系列最大化和最小化的步骤(这就是为什么这个算法被称为 minimax!)将这些值分配给不同的状态。最终,当算法到达终点状态,即底部的状态时,算法将结束。

那么一旦所有状态的值都被赋值,状态 A 的最优移动就是导致具有最高值的状态(状态 C)的移动,我们就完成了!

文字游戏的极大极小图(图片由作者提供)

对于我们的文字游戏,终端状态只能有两个可能的值——如果玩家赢了,1;如果玩家输了,0,这简化了我们的图表(上面的那个)。本文的剩余部分将研究这个算法的实现,以及我们如何进一步改进这个算法来加速我们的代码。

应用最小值最大值

有了极大极小算法如何工作的高级概述,让我们直接进入实际的实现。

设置字典

首先,我们需要一本字典作为我们的单词库。我们将使用nltk包,你将不得不pip install它和 unix 单词表,可以通过代码下载或者从这里下载。

请注意,字典中包含了一些与我们无关的单词,例如代词和少于 4 个字母的单词。因此,我们将使用 python 方便的filter函数对字典进行快速处理,去掉这些单词。

此外,我们将把我们的字典存储在一个 trie 数据结构中,因为这将允许我们比简单的列表更快地搜索单词。具体来说,我们将使用谷歌实现的开源pygtrie模块,你也将不得不pip install

本文不会深入研究 trie 如何工作或者具体实现的细节,但是你可以在这里找到模块的文档。我们需要知道的是,为了在CharTrie对象中存储一个单词,我们

完美!

助手功能

我们还需要两个在以后会有用的辅助函数。

search接受一个前缀,并在返回以该前缀开头的所有可能单词的列表以及下一个可能字母的列表之前搜索我们拥有的字典。

当玩家(玩家 1 或 2)获胜时,get_score返回1,否则返回0 。这很容易通过xor (^)函数比较字长和玩家号的奇偶来计算。

价值函数

接下来,我们将实现上面提到的价值函数。尽管我们可以使用一些技巧来加速代码,但它实际上实现起来非常简单。

这是一个递归函数,它的基本情况是当游戏状态是终端时,即当前缀是一个我们可以通过检查是否有任何下一个可能的字母来测试的单词时。然后,根据是否轮到玩家,我们或者找到所有可能的下一个状态的最大值或最小值,即前缀+每个可能的下一个字母。

把它放在一起

最后,我们实现了一个main函数,该函数将要求用户输入起始前缀,然后计算接下来可以玩的所有可能字母的值,并在可以保证获胜的地方打印这些字母(如果有的话)。

在这个过程中,我们还实现了一些输入检查,比如确保我们可以用提供的前缀组成一个单词,以及检查前缀是否已经是一个单词,或者是否只有一个可能的单词可以用这个前缀组成。

我们做到了!现在你知道如何打败你的朋友了。

丰富

如果你真的试着运行这段代码,你会注意到它并不是特别快,只需要 9.2 秒。

在下一节中,我们将下面探讨不同的方法来加速这个过程到不到一秒钟:

  • 阿尔法-贝塔剪枝
  • 删除不必要的单词
  • 评估启发式

阿尔法-贝塔剪枝

Alpha-beta 修剪是一种技术,我们可以通过不评估不可能影响游戏状态的最终值的移动来减少评估的游戏状态的数量。

阿尔法-贝塔剪枝图(图片由作者提供)

例如,参考上图,一旦我们评估了状态 G,我们知道状态 H 的最大值是-3,因此它永远不会被值至少为 7 的状态 F 选择。因此,没有必要进一步评估状态 I。这将减少我们必须做的计算的数量,因为我们不必评估所有划掉的状态。

文字游戏的 Alpha-beta 剪枝图(图片由作者提供)

在我们的文字游戏算法中实现 alpha-beta 剪枝相当简单(我们只需添加 4 行!)因为只要“最大”游戏状态已经找到一个可以保证获胜的移动,该游戏状态将肯定具有值 1,并且没有必要考虑任何其他移动或状态。

要做到这一点,我们所要做的就是检查最大状态的当前值是否为 1(或者最小状态的当前值为 0),在这种情况下,我们将返回 1 并停止检查剩余的游戏状态。

从上面可以看出,这实际上允许我们减少了相当多的评估游戏状态的数量,这相当于将我们的运行时间减少了 75%到 2.3s!

删除不必要的单词

我们可以做的另一个改进是减少我们必须搜索的单词数量。字典中有相当多的单词是不可能通过游戏得到的,因为它们包含另一个单词作为前缀。

例如,我们永远不会达到“媒介”,因为“媒介”这个词将首先结束游戏。

最初,我尝试了一个简单的算法,遍历字典中的每个单词,搜索包含它作为前缀的所有其他单词,但我很快意识到,如果有超过 20 万个单词,这个算法将永远不会完成运行。

因此,我想到了一个更好的方法,利用递归来过滤掉我们不需要的单词。本质上,我们正在做的是通过逐个考虑字母的组合来建立我们的字典,直到我们找到一个单词,这时我们将停止向字符串添加字母。

我们可以看到,该算法(运行时间为 18 秒)将我们字典中的单词量减少了 60%,相当于运行时间减少了 52%,仅为 1.1 秒!

评估启发式

最后,我们可以通过使用启发式算法来加速价值函数,以快速评估游戏状态的价值,而不必考虑后续状态。

关键的观察是,如果剩余的可能单词都以同一个人结束,即单词长度的奇偶性都相同,那么对于该游戏状态只有一个可能的分数。

例如,如果当前前缀是“medi ”,唯一可能的单词是“media”和“medical ”,那么我们知道玩家 2 将会赢,这实际上是一个终端游戏状态。

实现也相当简单——除了检查是否只剩下一个可能的单词,我们还检查剩余单词的单词长度的奇偶性是否相同。

这给了我们 0.9s 的最终运行时间,比原始算法提高了 90%!

结论

我希望使用一个简单的文字游戏(与更复杂的游戏如国际象棋相比)能帮助你更好地理解 minimax 是如何工作的。我们还展示了只需几行额外的代码就能显著提高性能。

您可以在这里使用代码来更好地感受实现。

一些最后的想法

这篇文章假设的一件事是两个玩家都是最优的。然而,虽然你可能有小字典的大脑容量(多亏了这种算法),但你的人类对手可能没有。

虽然这实际上不会影响你是否会赢得这个特定的文字游戏(你可以想想为什么),但对于更复杂的游戏,或者那些有随机因素的游戏,这可能是一个需要考虑的重要因素。在这种情况下,你可能会把 expectimax 看作是我们当前 minimax 算法的扩展。

最后,你们中的一些人可能会意识到,解决这个文字游戏基本上就是解决一个图形问题。虽然本文的主要焦点是用一个简单的文字游戏来说明极大极小算法,但是我邀请任何人提出一个更好的算法来解决这个游戏!

用人工智能解决网络疾病

原文:https://towardsdatascience.com/solving-cybersickness-with-ai-d5d7bb711467?source=collection_archive---------30-----------------------

几何人工智能技术如何被用来减轻网瘾

360 VR 体验可以把你运送到世界上的任何地方。[图片由 Minh PhamUnsplash 上拍摄]

今天的 360°虚拟现实(VR)体验具有巨大的潜力,可以让你被运送到世界上的任何地方。它们具有照片般的真实感,而且相对容易和便宜——只需要一个现成的 360°相机——但它们缺乏沉浸感和交互性。至关重要的是,这种交互性的缺乏也会导致许多用户产生网瘾。

3 自由度与 6 自由度

虽然 360 度虚拟现实体验可以让你环顾四周,但在当前的技术下,你无法在虚拟环境中四处移动。例如,你不能走来走去或向前探身去仔细观察某样东西。

由于 360°内容是从给定位置的摄像机捕捉的,因此在 VR 中观看体验时只有一个视点可用,因此不可能自由移动以享受不同的视图。无论你在真实的物理世界中如何移动,你在虚拟世界中仍然是冻结的。

在当前的 360 度虚拟现实体验中,你被冻结了,无法在虚拟世界中移动。[图片由马特·福斯特Unsplash 上拍摄]

换句话说,目前的 360 VR 体验支持所谓的 3 自由度(3 DOF)旋转运动,在这种运动中,你可以环顾四周(即旋转你的头部),但你不能移动。为了让你在虚拟环境中四处移动,必须支持 6 自由度运动。6 自由度运动包括来自旋转运动(环视)的 3 自由度和来自位置运动(走动)的 3 自由度。

最新的独立虚拟现实头戴设备,如 Oculus Quest 2 ,现在正在打开虚拟现实市场,使消费者和企业用户更容易获得虚拟现实,不再需要高端计算机来为有线虚拟现实设备供电。这些独立设备现在也在硬件层面支持 6 DOF。然而,内容仍然是 3 自由度的,这导致了最新硬件(6 自由度支持)和内容(仅 3 自由度支持)的能力之间的脱节。这意味着今天无法向用户提供 6 DOF 360 VR 体验。

视觉-前庭冲突

虽然今天的 360 VR 体验缺乏对 6 自由度运动的支持,限制了沉浸感和真实感,但这也引发了一个更大的问题:网络运动病。

在日常生活中,我们身体的感觉系统协调运作。当身体的不同感觉系统发生冲突时,就会产生网络晕动病。

视觉-前庭冲突是网瘾最常见的原因。在虚拟现实中,没有人会保持头部完全僵硬,因此一些身体运动是不可避免的。当你在身体上移动你的平衡感时,你的前庭系统会经历那个动作。但是如果你在虚拟环境中看不到自己在场景中移动,你的视觉系统就不会经历同样的运动。视觉和前庭系统的断开导致感觉冲突,这种感觉冲突被体验为恶心。

要了解更多关于网络病的信息,请听听虚拟现实播客的【T4 之声】,肯特·拜采访了杜克大学的兼职教授杰森·杰拉德。杰森解释了网瘾的各种原因,强调视觉-前庭冲突是最重要的。

当你移动时,你的平衡感(前庭系统)与你在 VR 中看到的(视觉系统)不匹配,在 360 VR 中就会产生视觉-前庭冲突。[图片由 Deniz AltindasUnsplash 上拍摄]

用人工智能合成虚拟现实中的运动

为了消除虚拟现实中的视觉-前庭冲突,有必要恢复身体的视觉和前庭系统之间的和谐。如果你的视觉系统在 VR 中体验运动,以匹配前庭系统体验的身体运动,这是可以实现的。问题是,当拍摄 360°镜头时,相机在每个时刻只捕捉到场景的一个视点。

使用人工智能(AI)可以通过合成拍摄 360°镜头时从未捕捉到的场景的新视点来解决这个问题。当你四处走动时,通过动态渲染这些新颖的观点,你会获得在虚拟世界中移动的感觉。从技术上来说,使用人工智能在 360 VR 环境中合成运动通过提供 6 自由度运动来增强体验——从某种意义上来说,是对 3 自由度 360°内容进行 6 自由度支持。

我们在为 360°照片和视频内容开发几何人工智能技术的初创公司 Kagenova 开发了这样一个系统。我们的 copernic360 系统通过两个子系统协同工作来实现这一效果:一个基于后端 AI 的云处理系统;和前端查看器系统。

基于人工智能的云系统处理 360 VR 视频或照片内容,以恢复表示场景的 3D 几何形状的估计。查看器系统使用由 AI 系统计算的几何图形以及原始的 360°视频或照片内容来渲染场景的 3D 纹理表示。然后,用户能够以完整的 6 DOF 运动在重建的场景中四处移动,其中,根据场景中的新的合成视点的位置,渲染这些视点并提供给用户。

科学研究

虽然直觉上很清楚,消除视觉-前庭冲突将减轻网瘾,但科学地测试这一点仍然很重要。最近,伦敦皇家霍洛威大学的一组前庭神经科学专家对这一假设进行了测试。

他们设计了一个实验,以高度可控的方式对许多用户进行 A-B 测试。用户体验了使用标准 360 VR 技术的 3 自由度 360 VR 场景,以及 Kagenova 的 copernic360 提供的 6 自由度场景。在这两种情况下,都测量了一些网瘾指标,包括定性指标(如用户的口头反馈)和定量指标(如心率)。

科学研究表明,人工智能技术大大减少了虚拟现实中的网瘾。[图片由 Bermix 工作室Unsplash 上拍摄]

这项研究的结果刚刚发表在一篇提交给多感官研究的学术文章中(预印本在此提供),并刊登在新科学家上。该研究表明,使用 Kagenova 的 copernic360 6 DOF 技术时,恶心等网络疾病的症状明显减少。

"结果证实,使用哥白尼 360 能非常有效地减少晕机."Elisa Ferré博士,科学研究的领导者

外卖食品

虽然 360 VR 可以将你带到世界上的任何地方,享受虚拟娱乐、旅游、文化或教育体验,但今天的技术仅支持 3 自由度运动,你无法在虚拟世界中四处移动。这限制了现实主义,并可能导致网络病。幸运的是,最近开发的几何人工智能技术专门为 360 张照片和视频量身定制,在卡格诺娃的哥白尼 360 技术中实现,可以合成 6 个自由度,让你在虚拟世界中四处移动,减轻网络病,大大增强真实感。

参考

[1] Iqra Arshad,Paulo De Mello,Martin Ender,Jason D. McEwen,Elisa R. Ferré,在 360 度虚拟现实中减少网络疾病 (2021), arXiv:2103.03898

[2] Oliver J. Cobb,Christopher G. R. Wallis,Augustine N. Mavor-Parker,Augustin Marignier,Matthew A. Price,Mayeul d'Avezac,Jason D. McEwen ,高效广义球形 CNN(2021),ICLR, arXiv:2010.11661

[3] Jason D. McEwen,Christopher G. R. Wallis,Augustine N. Mavor-Parker,可缩放和旋转等变球形 CNN 的球上散射网络 (2021), arXiv:2102.02828

[4] Matthew Sparkes, AI 可以阻止一些人在使用 VR 头戴设备时产生的网瘾,《新科学家》,https://www . New Scientist . com/article/2271020-AI-can-stop-the-cyber disease-some-people-get-when-use-VR-headsets/# ixzz 6 onyarkim

[5]哥白尼 360,走进 360 VR 内容【https://www.kagenova.com/products/copernic360/】,

[6] fourpiAI,几何 AI 为球面数据https://www.kagenova.com/products/fourpiAI/

解决数据的“最后一英里”问题

原文:https://towardsdatascience.com/solving-datas-last-mile-problem-dbde5ce3825?source=collection_archive---------24-----------------------

行业秘诀

什么是反向 ETL?好奇的人想知道

图片由爱丽丝·多诺万·劳斯Unsplash 上提供。

现代数据团队拥有所有合适的解决方案来确保数据被接收、存储、转换和加载到他们的数据仓库中,但是在 “最后一英里”会发生什么呢? “换句话说,数据分析师和工程师如何确保转换后的可操作数据确实可供访问和使用?

Tejas Mano har,high touch的联合创始人,也是 部门 的前技术负责人,我解释了反向 ETL 和数据可观察性可以帮助团队在信任您的数据产品方面走得更远。

现在是上午 9 点——你已经喝了第二杯咖啡,你最喜欢的 Spotify 播放列表在背景中大声播放,你刚刚在今天早上第三次刷新了团队的“营销分析”仪表盘,只是为了确保数据在你的 CMO 每周 All Hands 之前得到验证。世界上一切(似乎)都是对的。

然后,就在您进入最佳状态时,您听到了世界各地传来的懈怠声:“为什么 Salesforce 没有更新最新数据?”

如果这种情况听起来很熟悉,你并不孤单。在 2021 年,公司在数据上押下重注,以推动决策并为其数字产品提供支持,然而高达68%的数据 经常由于数据在仓库中转换后发生的问题而未被使用。

很多时候,你的 Looker 或 Tableau 仪表盘上的数字与你的运营系统(例如,你的 CMO 的 Salesforce 报告)中所代表的数字之间存在脱节,这降低了你的利益相关者的速度,并侵蚀了他们对数据的信任。我们称这种数据为“最后一英里问题”,这是现代企业普遍存在的现实。

图片由作者提供。

好在有更好的办法: 反向 ETL 。通过与 数据可观察性 合作,这套新的数据工具可以帮助数据团队在最重要的时候释放可访问、可靠数据的潜力。

什么是反向 ETL?

如果像 Fivetran 和 Stitch 这样的传统 ETL 和 ELT 解决方案使公司能够将数据接收到他们的数据仓库中进行转换和建模,那么反向 ETL 则正好相反:它使公司能够将转换后的数据从他们的云数据仓库转移到运营业务工具中。这是一种新的方法,通过让业务团队能够在他们每天都在使用的 SaaS 工具中直接访问和处理转换后的数据,使数据变得可操作,并解决分析中的“最后一英里”问题。

反向 ETL 管道可以定制,但是像许多数据工程挑战一样,它们需要大量的资源来设计、构建和维护。反向 ETL 工具使得拥有较少数据工程资源的团队可以只使用 SQL 来设计和构建管道。

同时,反向 ETL 加强了数据仓库作为事实来源的作用,同时通过将数据从仪表板和报告中取出,放入销售、营销和客户成功团队已经在使用的工具中,使数据访问民主化。

图片由 Tejas Manohar 提供。

反向 ETL 支持运营分析

通过使数据访问民主化并使数据更容易访问,反向 ETL 正在推动一种被称为运营分析的新范式,即在业务团队的日常工作流程中,将数据团队的见解反馈给业务团队,以便他们可以做出更多基于数据的决策。反向 ETL 通过确保数据在下游 SaaS 工具中是可访问和可操作的,来“操作化”BI 工具中支持报告的相同数据。

运营和分析团队越来越多地利用这种新方法将转换后的数据从他们的云数据仓库传输到他们的 CRM(如 Salesforce)、营销自动化工具、广告平台、客户支持和票务系统,当然还有 Slack。这使得分析师和商业智能团队更容易访问正在收集和存储在仓库中的大量客户数据,同时确保数据工程师在向其利益相关者提供可访问、可操作的数据时覆盖他们的基础。

当然,随着越来越多的数据被生成(并变得可操作),这就引出了一个关键问题:公司能信任他们的数据吗?

数据停机的风险

每当公司增加对数据的收集和使用,数据停机https://www.montecarlodata.com/the-rise-of-data-downtime/——数据丢失、不准确或其他错误的情况——的风险也会增加。当直接的客户体验受到威胁时,管道破裂、延迟进口和下游影响都变得更加紧迫。考虑可能的情况:

  • 如果过时的数据是客户支持通信的动力,团队就有在客户生命周期的敏感时刻发送无关信息的风险。
  • 如果推动您的销售序列的数据(如免费试用的完成)丢失或延迟,则不会及时发送消息,机会可能会错过
  • 如果将客户数据发送到广告平台的渠道中断,广告支出可能会很快偏离轨道——导致收入损失或更高的客户获取成本

除了数据质量差的具体业务影响之外,如果停机经常发生,内部团队可能会失去对数据的信任。对于致力于建立数据驱动文化的公司来说,这种信任是一种珍贵但不稳定的商品。

这就是为什么投资反向 ETL 的公司不应该跳过 现代数据栈 的最后一层:数据可观察性。

什么是数据可观测性?

数据可观察性将 DevOps 和应用程序可观察性的原则应用于数据,使用监控和警报来检测以下支柱的数据质量问题:

图片由巴尔·摩西提供。

  • ****新鲜度:数据是最近的吗?最后一次生成是什么时候?包含/省略了哪些上游数据?
  • ****分布:数据是否在可接受的范围内?它的格式是否正确和完整?
  • ****卷:所有预期的数据都到了吗?
  • ****模式:什么是模式,它是如何变化的?谁做了这些改变,原因是什么?
  • ****沿袭:给定资产影响的上游来源和下游资产是什么?谁是生成这些数据的人,谁是决策的依据?

简而言之,数据可观察性有助于数据团队确保他们的管道和资产是准确和可信的。通过 数据可观察性平台 进行监控和警报有助于确保当事件发生时,负责的数据团队将第一个知道,并可以与业务团队进行干预,以防止不可靠数据的下游影响。

提供值得信赖的数据,并保护数据工程资源

对于超负荷工作的数据工程团队来说,反向 ETL 和数据可观察性通过民主化数据访问节省了宝贵的时间和资源,同时确保了可靠性,并提供了对如何以及何时使用数据的可见性。

反向 ETL 为开发人员提供变更日志和实时调试器,以增强对运营数据流的可见性。除了数据可观察性的自动化沿袭和在整个数据生命周期中对数据资产和管道的端到端监控,这两种工具还提高了对整个组织中数据访问和交互方式的可见性和理解。

一个实时调试器,突出显示与同步的每行数据相关联的 API 调用。图片由 Tejas Manohar 提供。

您的数据工具不应该是一个黑盒,这就是为什么领先的反向 ETL 工具提供了一个实时调试器,用于理解它们代表您所做的更改和 API 调用。借助逆向 ETL,业务团队可以直接使用他们喜欢的工具访问数据,快速处理客户洞察和事件,并设计自动化数据驱动流程的工作流。反向 ETL 工具充当数据和业务团队之间的粘合剂:数据团队拥有数据模型,然后业务团队可以像点击式受众生成器一样使用 ui 来定义他们工具中需要的数据,而无需了解 SQL。这将数据工程师从一次性任务中解放出来,同时使业务团队能够自助服务他们的数据。

反向 ETL 解决方案允许营销人员在没有 SQL 的情况下直观地过滤数据模型。图片由 Tejas Manohar 提供。

有了数据可观察性,这些团队可以相信驱动他们客户体验的数据是可靠、准确和最新的。通过自动、端到端地覆盖您的整个堆栈,数据可观察性通过跨数据健康的五个关键支柱(新鲜度、分布、卷、模式和沿袭)在管道的每个阶段监控和警告您的数据问题,补充了传统测试。

现代数据可观察性工具甚至可以通过将关键数据资产的所有上下文信息、历史信息和统计信息集中在一个统一的平台上,帮助您实时解决根本原因数据问题,以免它们影响到 BI 仪表盘和 SaaS 解决方案中的业务用户。

数据可观察性为数据团队提供了一个完整的、端到端的数据管道健康和可靠性视图。图片由巴尔·摩西提供。

例如,数据停机的一个常见原因是数据的新鲜度,即数据异常过时。这种事件可能是由多种原因造成的,包括作业卡在队列中、超时、合作伙伴没有及时提供数据集、错误或意外的计划更改从 DAG 中删除了作业。

通过获取数据资产的历史快照,数据可观察性为您提供了确定“为什么”的必要方法在破损的数据管道背后,即使问题本身与数据本身无关。此外,许多数据可观测性解决方案所提供的谱系赋予了跨职能团队(即,数据工程师、数据分析师、分析工程师、数据科学家等)。)协作解决数据问题的能力,以免它们成为更大的业务问题。

字段级沿袭提供了对数据管道如何、为什么以及在哪里中断的洞察。图片由巴尔·摩西提供。

虽然反向 ETL 和数据可观察性都不能将您从利益相关者向您发出的所有清晨 pings 中拯救出来,但采取主动的端到端数据访问和信任方法肯定可以帮助您导航数据之旅的“最后一英里”——不管有没有完美的播放列表。

对学习更感兴趣?伸出手去 巴尔 或者 特哈斯

解决 MTV 的《非诚勿扰》:有可能永远不输吗?

原文:https://towardsdatascience.com/solving-mtvs-are-you-the-one-is-it-possible-to-never-lose-992488277099?source=collection_archive---------9-----------------------

照片由奥泽尔·戈麦斯Unsplash 上拍摄

爱情的游戏还是逻辑的游戏?

有时看一个有很多戏剧性的电视真人秀节目是非常放松的。“你就是那个人吗,”是其中一个节目。这个节目有一个有趣的额外部分:它引发我找到解决这个游戏的完美方法!找到解决方案有多难?平均需要多少回合?如果你玩很多游戏,你多久输一次?让我们把戏剧抛在脑后,试着只用逻辑和逻辑来解决这个游戏!

游戏解说

当然你们都知道这个电视节目或者已经解决了这个游戏!但如果你没有,这里有一个游戏解释。

“你就是那个人吗,”是一场 10 个男人和 10 个女人试图找到他们的完美伴侣的表演。这 10 对完美的情侣是由配对算法决定的。只有一个解决方案是正确的。在一起生活时,参与者试图在最多 10 轮中找到解决方案。如果参与者在出局前找到所有的完美匹配,他们将赢得一大笔钱。

每一轮,参与者可以送一对情侣到真相亭。在真相亭,这对夫妇发现他们是否是一个完美的匹配。随后,举行配对仪式,由男人或女人选择他们的伴侣。他们用光束找出多少匹配是正确的。每一束代表一个完美的匹配。如果他们有 10 场完美的比赛,他们就赢了这场比赛。如果他们没有找到一个完美的配对(除了他们在真理亭发现的那个),就会停电。然后他们会失去一半的奖金。

在一个有 10 只雄性和 10 只雌性的游戏中,配对的可能方式总共有 10 种!= = 10 * 9 * 8 * 7 * 6 * 5 * 4 * 3 * 2 * 1 = = 362.88 万
听起来没那么容易吧?!

照片由 Kimson DoanUnsplash 上拍摄

游戏模拟

现在问题清楚了,剩下唯一要做的就是编程,让计算机给我们的问题答案。我们将尝试两种方法来解决这个问题。第一种方法是从仍有可能配对的夫妇中随机选择配对。这有点类似于大多数参与者的玩法,这就是这种方法的有趣之处。这个方法给了我们一个基线分数。第二个使用了一种叫做 minimax 的算法。

首先,我们导入一些包。然后我们创建两个函数:一个在开始时给出所有可能的匹配(或可能的答案),另一个我们可以用来从可能的答案中提取可能的配对(为真理亭选择一对)。在开始的时候,可能的情侣数量等于一男一女所有可能的组合,所以有 10*10 = 100 对可能的情侣。如前所述,可能的匹配数等于 3,628,800。

在这个实验中,情侣的数量等于 10 对,就像大多数“非诚勿扰”中一样季节。如果您想试验不同数量的情侣,您可以在这里选择不同的值。

一个函数创建所有可能的配对,另一个函数从所有可能的答案中提取可能的配对。我们只需要创建一次可能的答案,就可以在每场比赛中重复使用。

在玩游戏时,我们将从可能的答案中删除我们从真相展台获得的信息以及配对过程中正确配对的数量。

在真相亭,一对夫妇可以是完美的一对。如果是这样的话,我们只保留这一对的答案。如果这对夫妇不是完美的匹配,我们删除这对夫妇的所有答案。

对于比赛仪式,我们选择一个可能的答案。这个答案包含十对情侣。仪式的回应是一个 0 到 10 之间的数字。这个数字对应于完全匹配的数目。如果我们找到 3 个完美匹配,我们只保留与我们选择的答案有 3 对相同的答案。

方法 1:随机选择

下一个要点显示了玩“非诚勿扰”游戏的代码。首先从所有可能的答案中随机选择一个;这个目标代表了我们需要找出的完美匹配。出于分析的目的,我们保存一些数据,比如真理摊位的结果和匹配仪式期间完美匹配的数量。当然,最后的结果是:电脑赢了还是输了?

计算机得到 10 个回合来计算出目标。首先从可能的情侣中随机挑选一对送到真相亭。它将真相亭的结果与可能的答案进行比较,并删除所有与真相亭结果不匹配的可能答案。然后,它从可能的答案中选择一个随机匹配,并使用完美匹配的数量来删除所有不满足该条件的可能答案。可能性的数量以这种方式迅速减少!

运行一个游戏的代码。

然后我们需要一小段代码来运行这些函数。让我们玩 1000 季的《非诚勿扰》。你的猜测是什么?电脑会赢几个?

玩“非诚勿扰”的代码 100 次。

游戏玩了 100 次,胜负数看起来是这样的:

在 100 场游戏中,当我们为真相展台随机猜对一对情侣,为配对仪式随机猜对一个答案时,我们赢了 83 次,输了 17 次。这是大部分选手选择的战术,也没那么差!图片由 autor 提供。

好吧,83%的机会赢得 100 万美元,比任何彩票都高,太好了!是否有可能进一步提高这一基线分数?在游戏过程中,我们随机选择了一对夫妇作为真相展台,随机选择了一个配对仪式的队列。完全没有优化。让我们继续方法 2。

剧透警告:是的,我们当然可以提高这个分数!

方法 2:最小最大乘以 2

在第一种方法中,我们随机(从可能的配对中)挑选我们的真理亭夫妇和我们的配对。我们没有考虑一个重要的事情,那就是在选择一对夫妇或配对后剩下的可能答案的数量。如果我们选择一对给我们留下最低数量的剩余可能答案,我们可以改善可能答案的减少。同样的原理也适用于比赛。

我们如何计算还剩下多少个可能的答案?与配对相比,这对夫妇来说是一个不同的过程。让我们开始吧。

用极小极大法 为真相展台挑选情侣对于情侣,我们可以统计每对情侣在所有可能答案中出现的次数。有两种可能:这对夫妇是完美的一对,或者这对夫妇不是完美的一对。如果这对夫妇是完美的匹配,我们只保留这对夫妇存在的可能答案。如果这对夫妇不是完美的匹配,我们就剩下可能的答案减去这对夫妇可能的答案。

举个例子:我们还有 50 个可能的答案。一对夫妇在可能的答案中出现 10 次。另一对在可能的答案中出现了 27 次。对于第一对夫妇,我们将保留 10 个(如果这对夫妇完全匹配)或 40 个(不匹配,50 减去 10)可能的答案,这两个值的最大值是 40。对于第二对夫妇,我们将保留 27 或 23(50-27)个可能的答案,最多 27 个。在这种情况下,第二对夫妇是更好的选择,因为 27 和 40 的最小值是 27。

每轮我们选择剩余答案中最大值最低的一对。这就解释了 minimax 这个名字。

完全匹配,是还是不是?照片由萨蒂亚万·纳林哈特Unsplash 拍摄

用最小最大值选择最佳匹配
选择留给我们最少可能答案的匹配有点复杂。当真相亭给我们一个是或否的答案时,配对仪式的回答在 0 到 10 之间。因此,在这种情况下,我们从一个可能的答案开始,然后计算所有可能答案的剩余数量(从 0 到 10)。然后我们选择最大值(就像对夫妇一样)。对所有可能的答案重复这些步骤,并选择具有最低值的可能答案。

举个例子,如果我们和 5 对情侣一起玩这个游戏:我们选择一个可能的答案。有 10 个其他答案有 0 个(响应)共同配对,20 个答案有 1 个共同配对,25 个答案有 2 个共同配对,23 个答案有 3 个共同配对,8 个答案有 4 个共同配对。剩余答案的最大可能数量是 25,因此对于这个答案,我们选择 25。对所有其他可能的答案重复这些计算,并选择最大值最低的一个。

当你有大量可能的答案时,计算最佳匹配是相当昂贵的。记住我们从 3.628.800 个可能的答案开始!正因为如此,我在开始时取 100 场比赛的样本,并取该样本的最小值。当可能答案的数量减少时,我抽取 1000 个匹配的样本,或者我计算它们(如果可能答案的数量小于 1000)。

用代码实现:

它需要更多的时间来运行。同样,我们模拟了 100 个游戏。等了…又等…这是赢和输的图表:

使用 minimax 时的结果。是的,你的眼睛没有说谎,我们总能赢!图片作者。

哇!满分 100 分!这是一个逻辑的游戏,不涉及感情!

额外收获:比较这两种方法

胜负已经很清楚了,我们肯定应该采用方法 2,并根据极大极小算法选择情侣和配对。但是其他统计数据呢?

平均回合数方法使用 随机法平均耗时 8.53 回合,而极小极大法仅需 7.94 回合。分布图看起来像这样:

用了多少圈?Minimax 使用较少的回合,它在 6 回合中完成的次数比 10 回合多!图片作者。

可能答案和配对的平均减少 还有关于每一轮之后剩下的可能答案数量的数据。让我们通过取每轮 100 场比赛的平均值来比较这两种方法。用随机方法剩下的平均可能答案除以极大极小方法剩下的平均可能答案,我们得到以下数字。表格和图表告诉我们,在最后几轮中,minimax 的剩余可能答案的数量比随机猜测的剩余可能答案的数量少 5 倍以上!在最后两轮比赛中,这个数字甚至更高:几乎是 9 倍!

随机方法和极大极小方法每轮的平均剩余可能答案数。图片作者。

完美的真相展台匹配的数量 根据极大极小法,我们把情侣送到真相展台会得到更多的完美匹配吗?我们的目标不是得到一个完美的匹配,我们只想尽可能快地减少可能答案的数量。

这些图再次向我们展示了极大极小者获胜。平均有 2.8 个完美匹配。随机法只有 1.63。这是合理的。Minimax 将总是选择出现在最接近 50%的可能答案中的一对。如果是这样的话,你有 50%的几率,这对夫妻确实是天造地设的一对。凭随机猜测,情况并非如此。

真理亭中完美匹配的数量(每场游戏最多 10 个)。图片作者。

停电次数 还记得停电也有可能吗?当你在配对仪式中没有完美的配对时,就会发生这种情况。参与者损失一部分奖金,所以想避免这个!

停电次数:再一次,minimax 赢了!图片作者。

结论

戏剧和数学在这个电视节目中是完美的搭配!这部剧很棒,因为如果你玩得聪明,它总能有一个美好的结局🤓。这个帖子证明了只有两种可能的结果:找到爱情赢钱或者只是赢钱。万一没找到爱情,可以拿着奖金继续狂欢!

干杯!🥂

菠萝供应公司Unsplash 上拍摄的照片

如果你有任何关于改进这段代码的建议,让它变得更快或者用不同的方式解决它,我很乐意听到你的意见。感谢阅读!

有关系的

别忘了 订阅 如果你想在我发表新文章时收到电子邮件。

用有限差分法数值求解非线性微分方程

原文:https://towardsdatascience.com/solving-non-linear-differential-equations-numerically-using-the-finite-difference-method-1532d0863755?source=collection_archive---------21-----------------------

在这篇文章中,我们将看到如何使用有限差分法来解决非线性微分方程数值。我们将在考虑空气阻力的情况下练习摆方程,并用 Python 求解。

我们会从零开始找到摆的微分方程,然后求解。在我们开始之前,我们需要一些关于极坐标的背景知识。

极坐标

你已经知道著名的笛卡尔坐标,这可能是日常生活中使用最多的坐标。然而,在某些情况下,用笛卡尔坐标描述一个物体的位置是不实际的。例如,当一个物体在圆周运动时,正弦和余弦函数会到处出现,所以通常用我们称之为极坐标来描述物体的位置是一个更好的主意。

极坐标由两个变量描述,半径 ρ 和角度 θ 。我们将单位向量附加到每个变量上:

  • 是一个单位矢量,始终指向与矢量 OM 相同的方向。
  • 是垂直于 的单位矢量。

我们现在的目标是在极坐标中表达一个物体的 位置速度加速度 。为此,我们需要表达极坐标单位向量和笛卡尔单位向量之间的关系。

笛卡尔坐标到极坐标

极坐标到笛卡尔坐标

好吧!让我们用极坐标来表示 位置速度加速度

位置

这个很简单。这就是使用极坐标的全部意义!

极坐标中的位置

速度

我们只是简单的区分了位置和时间的关系。我们将假设 ρ 为常数,只有 θ 随时间变化。

极坐标中的速度

加速

我们对速度对时间求导。

极坐标中的加速度

好吧!我们现在可以解决我们的问题:钟摆。

钟摆方程

为了找到角度 θ 所满足的方程,我们会用到牛顿第二运动定律,或者用法语叫动力学的根本原理

牛顿第二运动定律

施加在一个系统上的所有力的总和等于它的质量乘以它的加速度。我们来列举一下摆所受的所有力 用极坐标 表示。

重量

重力引起的物体重量是施加在物体上的力的一种。它的公式是众所周知的,并将在我们的坐标系中表示为:

重量

其中 m (kg) 是物体的质量, g (m/s ) 是重力加速度的值——在地球上大约是 9.81。

绳索张力

绳子施加一个张力,沿着绳子的方向拉着钟摆。

绳索张力

其中 R (N) 为绳索拉力,单位为牛顿。

空气阻力

当然,空气对钟摆施加了一个摩擦力,这个摩擦力会使钟摆在某一点停止摆动。小空气阻力通常被建模为与速度矢量相反且与速度矢量的范数成比例的力。

空气阻力

其中 k (kg/s) 为运动中物体特有的摩擦系数, L (m) 为摆绳的长度。

牛顿第二运动定律

我们现在可以应用牛顿第二运动定律:

牛顿第二运动定律应用于单摆

然后将结果投影到两个轴上:

推断

重新排列第二个等式的各项,我们得到:

钟摆方程

求解这个二阶非线性微分方程是 非常复杂的 。这就是有限差分法非常方便的地方。归结起来就是两行 Python!让我们看看怎么做。

差分法

该方法包括使用变化率以非常小的步长数值逼近导数。

导数——变化率

这就是衍生品的定义。在数值上,如果我们知道 f ,我们可以取一个小数字—例如 0.0001 —并计算给定x 的上述公式,这将给我们一个f’(x)的近似值。**

有限差分法就是利用这一事实将微分方程转化为普通方程。

在我们的例子中,我们首先用变化率来表示相对于θ】

我去掉了极限,写下 dt 让我们知道这应该是一个无穷小的值——实际上,只是一个很小的数。我们现在将把这个方程代入摆方程。

好吧!我们设法将时间 t+dt 的角速度表示为相对于时间 t 的角度和角速度。换句话说,如果例如 dt=0.001 并且如果你知道【θ(0)【θ’(0)(这些都是系统的初始条件),那么你就可以计算出θ’(0.001)!如果我们还可以计算 θ(0.001) ,那么递归就完成了,我们可以从已知的初始条件开始计算任意【t】【θ(t),θ'(t)】

好在有一个计算 θ(t+dt) 的方法:

好吧!有了这个方程,我们还可以计算出在时间 t+dt 的角度,给定在时间 t 的角速度。

利用这两个方程,我们现在可以计算任意时间步长的角度θ!

的确,给定【θ(0),θ'(0)】就可以计算出【θ(dt),θ'(dt)]。** 给定【θ(dt),θ'(dt)】可以计算【θ(2dt),θ'(2dt)】,以此类推。**

让我们在 Python 程序中使用所有这些!

Python 代码

我们使用找到的公式迭代计算【θ’(t),并将结果放在两个单独的列表中。希望代码是可以理解的,但是如果你有任何问题,请随意发表评论。

运行该代码将产生以下图形。

两个快乐的观察:

  • 当角度为 0 时,角速度似乎达到了极值,这很合理,因为这是钟摆积累了所有惯性的地方,因为它在上升,所以速度要慢下来。
  • 当角度达到极值时,角速度似乎达到零,这是有道理的,因为这是钟摆减速并准备向另一个方向运动的时候。

稍微摆弄一下代码,比如你可能想把初速度设置为

注意角度在下降前是如何不断增加的。发生的情况是,初速度足够高,使得钟摆在进入通常的振荡之前旋转了一整圈!

最后一件事…你可以尝试增加dt,看看这对模拟有什么影响。希望你已经明白,更小的 dt 意味着更精确的结果。让我们看看对于 N=3,2,1 会发生什么。

实际上,我很惊讶地看到,对于每秒 3 个点(甚至 2 个点),我们仍然能够得到解的大致形状。N=1 是另一个故事…

结论

在这篇文章中,我们已经看到了如何使用有限差分方法来解决微分方程(即使是非线性的),我们把它应用到一个实际的例子:摆。这种技术也适用于偏微分方程,一个众所周知的例子是热方程。

用量子能量解决数独

原文:https://towardsdatascience.com/solving-sudoku-with-a-quantum-power-up-5bb4f64f3944?source=collection_archive---------30-----------------------

神秘的量子计算机承诺比经典计算机更快。

今天我们将使用量子 Annealer 来解决周日休息时间的数独问题。

9x9 数独游戏本来源:https://pix abay . com/photos/optics-glass-glass-Sudoku-pencil-4691414/

更好的是:我们将使用数独游戏来测试最新公开的量子 Annealer 架构。这样,我们就会知道投资这项新技术的时机到了。

B 但是什么是量子退火机呢?

我不想解释几个人已经做了什么,并且可以做得更好。因此,我将在底部为您提供一些精彩解释的链接,现在只需陈述我们需要的基线假设:

  • 量子退火器可以用来解决离散的组合优化问题(数独是其中的一个子集)
  • 通过使用量子力学的魔力,量子退火机理论上可以比经典计算机快(很多)
  • Quantum Annealer 制造商 D-Wave 提供了文档和一个公开可用的 API ( D-Wave Leap ),我们可以用它来解决我们的问题。

如何制定解决一个数独竞猜的目标?

数独游戏可以简单地表述为包含数字和一组简单规则的矩阵。

假设我们有一个 9x9 的数独游戏,将棋盘表示为一个矩阵,将矩阵限制为 9 行 9 列,并且只包含从 1 到 9 的数字。

那么我们可以将数独规则定义为:

  • 每一行都需要包含从 1 到 9 的所有数字
  • 每一列都需要包含从 1 到 9 的所有数字
  • 每个方块需要包含从 1 到 9 的所有数字

这些基本上是我们需要的所有规则。很简单,是吧?如果我们现在确保所有这些规则都成立,在输入数字时,我们将得到一个有效的数独解决方案!

有哪些(数学)约束?

顶级编码器挑战系列中,已经针对非量子优化硬件(称为数字退火机)解决了制定约束的问题,因此我们已经处理了耗时的冷启动问题

在我展示约束之前,我想介绍一下我们通常如何看待电路板的一个小变化。因为量子退火器可以解决的数学公式是二进制的,所以我们需要限制代表我们的板的矩阵也是二进制的。我们可以通过为一个单元格中的每个数字引入一个维度来实现这一点(在我们的例子中是 9)。看看下面的图片,以便更好地理解。

摘自https://tc3-Japan . github . io/DA _ tutorial/tutorial-1-sudoku . htmlTC3 K . K(www.tc3.co.jp)

现在让我们快速看一下我们需要的约束:

1.一个单元格只能有一个数字。

第一个约束是我们之前介绍的每个可能数字的维度的结果。“单元”是包含所有行、列组合的元组的集合。约束 2–4 只是我们在一开始定义的数独规则。

数独游戏的约束

第五个约束是确保算法不会开始改变已经给定的数字。当然,这对人类来说是显而易见的,但是计算机需要更明确地知道什么该做,什么不该做。以下等式的和在一个称为“提示”的集合上迭代,该集合包含所有初始给定的单元格。

5.作为“提示”给出的初始数字不能更改

然而,这些约束不具有量子退火器可以解决的格式。还没有。我们需要将约束重新表述为一个曲波(二次无约束二元优化问题),以便量子退火器解决它。曲波基本上期望一组线性和二次项来表示问题。

二次无约束二元优化(请参见https://docs.dwavesys.com/docs/latest/c_qpu_0.html?曲波)highlight =曲波对曲波进行更深入的解释。

将我们的约束重新表述为曲波的一个方法是引入一个所谓的惩罚项。通过一个惩罚项,我们惩罚“错误”的行为,所以我们强制执行算法,给我们最好的解决方案。

让我们先看看这些公式是怎样的,然后再谈一点。

数独游戏的 QUBOs

上面的大多数等式使用的是 1 的平方距离,因为我们必须确保每个数字在每行和每列只能选择一次。这样,当总和之下的相加数字等于 1 时,达到最佳解,即最小值,在我们的情况下为 0(因为平方)。将约束重新表述为罚函数的逻辑适用于所有项,其中最优解是最低的可能值。

如果我们将所有的量子集合在一起,以获得量子退火器的最终目标函数,我们最终得到这个方程:

数独(曲波)目标函数

这个目标函数导致了一个最终矩阵,该矩阵具有一个正确的数独游戏作为最优解。最终,Quantum Annealer 所做的一切都是离散优化:在未知的离散问题空间中找到一组最优值(解)。

在这一点上,我们可以使用我们的曲波和 D-Wave Leap,来解决数独板,这提供了对量子退火器的免费访问:具有新 Pegasus 架构D-Wave Advantage

量子退火炉的局限性

事实证明,量子退火机的硬件对于解决整个 9x9 数独曲波来说还太小。我不可能找到将曲波的嵌入到 D-Wave 硬件图中的,这是采样发生前的必要步骤。

然而,对于一个简化版的 4x4 数独游戏,我可以找到一个有效的嵌入,因此我将使用这个更小尺寸的电路板进行以下性能测量。

Quantum Annealer 性能指标评测

除了时间,即进行计算所需的,另一个广泛使用的度量标准被称为成功概率
由于 Quantum Annealer 以启发式方式工作,因此无法保证有效的结果。成功概率就是所有计算中正确解的比率。
根据问题的复杂程度,成功概率可能会很快降低,这意味着量子退火器偶尔会找到最佳解决方案,而在其他时候会以(稍微)更差的解决方案告终。为了进行比较,我们在经典计算机上使用了一个模拟退火算法。模拟退火与量子退火器有许多共同的属性。

量子退火和模拟退火的采样时间比较

即使有了这个实验,经典算法和量子算法之间的最大差异已经很明显了。虽然量子退火器需要大致恒定的时间(每次采样 20 秒)来达成解决方案,但经典算法所需的时间可能会有所不同。此外,量子退火器似乎无论如何都比我家用电脑的 CPU 快。****

量子退火和模拟退火成功概率的比较

成功概率描绘了一幅不同的画面。虽然模拟退火几乎总能找到精确的解决方案,但量子退火机仅在大约 1%的采样运行中找到精确的解决方案。这正是我们在计算解决方案时需要采样的原因——尤其是对于量子硬件。
尽管 1%听起来是一个非常糟糕的比率,但如果一次采样(比如说 1000 次运行)需要持续几秒钟的时间,这就足够了,而且我们通常只需要一个有效的解决方案。

后记

AdvantageQuantum Annealer 的分析表明,D-Wave 使向其系统提交问题变得非常简单。尽管硬件还不足以解决大问题,但已经有很多有趣的应用,当问题的规模减小时,可以对它们进行研究。此外,可能有可能使用一种 混合方法 即:将问题分解成容易计算的经典计算部分,只将较难的计算部分外包给量子退火程序。

我希望我的小实验能够向你展示花一点时间投入到计算机科学、数学和物理这个非常有趣的交叉领域的乐趣。

Y ou 可以在 GitHub 上找到所有创建的代码

有用资源:
【1】https://research.aimultiple.com/quantum-annealing/
【2】http://www . cs . CMU . edu/AFS/cs . CMU . edu/project/learn-43/lib/photoz/。g/web/glossary/comb . html
【3】https://en.wikipedia.org/wiki/Quantum_annealing
【4】https://docs.ocean.dwavesys.com/en/stable/
【5】https://cloud.dwavesys.com/leap/
【6】https://www.youtube.com/watch?v=zvfkXjzzYOo

用 Google 或 tools 解决多背包问题

原文:https://towardsdatascience.com/solving-the-multiple-knapsack-problem-with-google-or-tools-e961dfc8288e?source=collection_archive---------14-----------------------

使用 Python 中的线性编程解决多约束多背包问题的分步演练。

照片由 S & B 冯兰森Unsplash 上拍摄

介绍

背包问题是一个在线性规划中使用的玩具问题,供用户学习如何制定一个等式,将各种重量的物品最佳地打包到背包中。每个项目通常也有一个相关的值。目标是优化背包的价值,同时不超过包的重量限制。今天的问题从基本的背包问题扩展到具有多种约束的多背包问题。提供的代码和演练与在 Goole 或工具上找到的类似,然而,今天解决的问题有更多的限制。

问题

你和 4 科学家们正在研究不同元素组合的放射性,为地球寻找一种新的清洁能源。虽然元素和混合物中的放射性水平很低,但你决定要徒步走到一个偏远的实验室,以确保平民的安全。你们每个人都有 1 个元素包装袋,能够装下 50 磅【lbs】的重量和* 50 立方英寸 。另外,您和您的团队只能接触到最大放射性为 25 拉德 的物品,因此, 每个包最多只能有 5 个 拉德 以确保任何人的包都不会有过多的辐射而伤害携带者。每个元素都有一定的价值水平 【实用工具】 ,团队的目标是挑选最佳的物品组合,最大限度地发挥团队的效用,同时仍然安全地包装 5 个袋子。这 5 个袋子应该如何与给定的物品包装在一起? (“包”和“背包”这两个词可以互换)*

作者图片:每个项目的参数。

设置问题

首先,导入工具(可能需要进行 pip 安装!)

*import ortools
from ortools.linear_solver import pywraplp*

我们正在解一个线性程序,因此需要从 ortools.linear_solver 导入 pywraplp接下来,创建求解器。

*solver = solver = pywraplp.Solver.CreateSolver('SCIP')*

集合/参数

现在将为该问题定义集合和参数。

设定

项目数量= N = {1,2…19}

行李数量= M = {1,2,3,4,5}

参数(数据)

作者图片

将上表中的值代入 python,得到以下参数:

*data = {}
values = [48, 30, 42, 36, 22, 43, 18, 24, 36, 29, 30, 25, 19, 41, 34, 32, 27, 24, 18]
weights = [10, 30, 12, 22, 12, 20, 9, 9, 18, 20, 25, 18, 7, 16, 24, 21, 21, 32, 9]
volume = [15, 20, 18, 20, 5, 12, 7, 7, 24, 30, 25, 20, 5, 25, 19, 24, 19, 14, 30]
rad = [3, 1, 2, 3, 1, 2, 0, 2, 2, 1, 2, 3, 4, 3, 2, 3, 1, 1, 3]assert len(values) == len(weights) == len(volume) == len(rad)
data['values'] = values
data['weights'] = weights
data['volume'] = volume
data['rad'] = rad
data['items'] = list(range(len(weights)))
data['num_items'] = len(values)
number_bags = 5 #All have the same capacity of 50 pounds
data['bag_capacities'] = [50, 50, 50, 50, 50] #pounds
data['bag_volume'] = [50,50,50,50,50] #while this equals bag_capacities, I made it its own variable in case
data['rad_capacities'] = [5,5,5,5,5]
#I wanted to change the values at a later data
data['bags'] = list(range(number_bags))assert len(data['bag_capacities']) == number_bags
assert len(data['bag_capacities']) == len(data['bag_volume']) == len(data['rad_capacities'])
print("Values: ",*data['values'])
print('Weights:',*data['weights'])
print('Volume:',*data['volume'])
print('Radiation Levels:', *data['rad'])
print("Number of Items:", data['num_items'])
print("Number of Knapsacks:" , number_bags)
print('Knapsack Capacities: 50 Pounds, 50 cubic inches, 5 Levels of Radiation')*

这里的断言是作为一种健全性检查,确保您没有遗漏任何输入值。运行此命令将输出以下内容:

作者图片

决策变量

一般来说,决策值首先被概述,然而,它们必须在数据被输入和变量被设置之后被编码。对于这个问题,我们的决策变量是选择一个项目(1)还是不选择(0)。

作者图片

在 python 中:

*x = {}
for i in data['items']:
    for j in data['bags']:
        x[(i,j)] = solver.IntVar(0,1,'x_%i_%i' % (i, j))*

假设

当建立一个线性规划时,包含你所做的主要假设总是很重要的。对于这个问题,主要假设是一个人不能携带超过 1 个背包。此外,一件物品不能移动到另一个包中,也不能放在多个包中。

限制

对于这个问题,每个包的重量、体积和辐射量是我们的约束条件。此外,一件物品只能放在 5 个袋子中的一个。

项目约束

作者图片

纵观所有包,一件物品只能放在一个包里。

重量约束

作者图片

请记住,每个袋子的重量不能超过 50 磅。

音量限制

作者图片

记住,每个袋子的体积不能超过 50 立方英寸

辐射约束

作者图片

请记住,每个袋子中的辐射不能超过 5 拉德

*#Constraint for an item being placed in 1 knapsack
for i in data['items']:
    solver.Add(sum(x[i,j] for j in data['bags'])<=1)#Knapsack Capacity Constraint
for j in data['bags']:
    solver.Add(sum(x[(i,j)]*data['weights'][i] 
                  for i in data['items']) <= data['bag_capacities'][j])#Volume Constraint
for j in data['bags']:
    solver.Add(sum(x[(i,j)]*data['volume'][i]
                  for i in data['items']) <= data['bag_volume'][j])
#Radiation Constraint
for j in data['bags']:
    solver.Add(sum(x[(i,j)]*data['rad'][i]
                  for i in data['items']) <= data['rad_capacities'[j])*

目标函数

这个问题表明我们想要找到最好的方法来最大化我们团队的效用。如下面的目标函数所示,这相当于将放入袋子中的所有物品值相加。

作者图片

*#objective function
objective = solver.Objective()
for i in data['items']:
    for j in data['bags']:
        objective.SetCoefficient(x[(i,j)], data['values'][i])
objective.SetMaximization()*

求解!

*solv = solver.Solve()
if solv == pywraplp.Solver.OPTIMAL:
    print('Total Packed Value:', objective.Value())
    total_weight = 0
    for j in data['bags']:
        bag_value = 0
        bag_weight = 0
        bag_volume= 0
        bag_rad = 0
        print('\n','Bag', j+1 , '\n')
        for i in data['items']:
            if x[i,j].solution_value()>0:
                print('Item:', i , 
                      'value',data['values'][i],
                      'Weight', data['weights'][i], 
                      'Volume',data['volume'][i],
                      'Radiation', data['rad'][i]
                     )
                bag_value += data['values'][i]
                bag_weight += data['weights'][i]
                bag_volume += data['volume'][i]
                bag_rad += data['rad'][i]
        print('Packed Knapsack Value: ', bag_value)
        print('Packed Knapsack Weight: ', bag_weight)
        print('Packed Knapsack Volume: ', bag_volume)
        print('Pack Knapsack Radiation: ', bag_rad)
else:
    print("There is no optimal solution")*

最终输出:

作者图片

作者图片

如您所见,所有的约束都已满足,我们的最终效用值为 433.0 utils

结论

今天我们学习了如何解决有多个约束的多重背包问题。当处理可以线性框架化的问题时,google.ortools 是一个极好的资源,它提供了各种不同的解决方案。你可以在你的日常生活中为你的下一次大旅行做些调整,确保你以最适合你需求的方式打包行李!

请在LinkedIn上加我或者随时联系!这个代码的仓库可以在* 这里找到 。感谢阅读!*

用 Python 解决纽约时报拼字比赛难题

原文:https://towardsdatascience.com/solving-the-new-york-times-spelling-bee-puzzle-in-python-511bcb5ea65e?source=collection_archive---------22-----------------------

使用内置函数和数据类型

德米特里·格里戈列夫在 Unsplash 上拍摄的照片

介绍

我对谜题很着迷,我现在痴迷于纽约时报拼字比赛。在这个日常谜题中,玩家会得到一个由七个字母组成的“蜂巢”。他们的任务是创造单词,这些单词可能只有包含这些字母,而必须包含蜂巢中心的字母(称为“中心字母”)。有效单词也必须至少有四个字母长。例如,在下面的模仿游戏中,“accolade”是一个有效的单词,但是“load”(没有中间字母)和“cap”(太短)不是。

作者的图像,旨在模仿拼字游戏的结构。不是 NYT 出版的。

我们的目标是想出尽可能多的有效单词:随着你的字数增加,你的表现排名也会上升,排名范围从“初学者”到“天才”。为了追求那种珍贵的天才身份,我几个晚上都失眠了。

今天,我决定依靠 Python 而不是英语来解决这个难题,本文将解释我是如何做到的。

这不是作弊吗?

这项任务将一个字谜转化成了一个逻辑谜题,从某种意义上说,仍然有一些“解决”要做,我不认为这是作弊。考虑到这个逻辑难题只需要解决一次就可以应用到任何未来的字谜中,然而,如果我只是在明天的拼字比赛挑战中运行这个程序,我无疑是在作弊。

那会剥夺解决拼字比赛的所有乐趣,所以如果我将来使用这个程序,只有在我穷尽了我的词汇中的每个有效单词,并且仍然在凌晨 2 点起床追逐天才排名之后。本着良好的体育精神,我想请我的读者仅将本文中的信息用于教育目的。

创建英语单词列表

解决拼字比赛的程序需要有某种方法来识别一个单词是否存在于英语中。为此,我从这个 GitHub 库下载了“words.txt”文件。然后,我在文件中创建了一个单词列表,代码结构如下:

' word_file '中的单词在结尾包含不必要的字符,'/n ',表示新的一行。这些字符通过字符串索引被删除,为了保持一致,单词在被附加到“单词列表”之前被改为小写该列表总共包含大约 25.5k 个单词,这远非详尽,但大概包括了足够我们使用的单词。

定义求解函数

下面的函数返回一个潜在有效单词的列表,给定一个谜题的约束条件,不导入任何依赖关系:

该函数将不可接受的字母定义为字母表中的所有字母,这些字母在它作为参数的字母列表中是而不是。可接受单词的列表通过迭代“单词列表”并识别满足以下条件的单词来填充:

  • 作为参数提供的中心字母必须在单词中。
  • 该单词必须不在可接受单词列表中(以避免在最终列表中出现重复)。
  • 单词字符串的长度必须大于 3。
  • 该单词不得包含不可接受字母列表中的任何字母。

如果满足所有这些条件,该单词将被追加到可接受单词列表中。该函数在遍历“单词列表”后将列表作为输出返回

测试功能

现在让我们在一组实际的拼写比赛难题约束上测试这个函数。下面,我们将谜题的有效字母定义为单个字符串的列表,中间的字母定义为独立的单个字符串。

给定此输入,函数返回以下单词列表:

['alai ',' alan ',' alfa ',' alia ',' alicia ',' allan ',' annal ',' annual ',' calculi ',' calculi ',' call ',' call ',' calla ',' canal ',' cilia ',' clan ',' cliff ',' clanic ',' clin ',' cull ',' face ',' fail ',' fall ',' fanatic ',' faunal ',' fanal ',' subsidy ',' fill ',' final ',' final ',' finial ',' flail ',' flail ',' flail ','

应该注意的是,并非所有上述单词都是拼字游戏难题的有效条目。大部分谜题只接受相对常见的单词,包含标点符号的单词无效。晦涩难懂的词和那些可能是专有名词的词,包括“艾丽西娅”、“加州大学洛杉矶分校”和“乌兰”,都不被接受。尽管如此,这个列表还是让我今天的谜题得分超过了“天才”的门槛。

你自己试试!

想在另一组约束上测试函数吗?这个求解器的完整代码,以及包含英文单词的文本文件,可以在这个 GitHub 库中找到。只要记住:蜜蜂一种好的运动,试着先自己解决这个难题。

求解概率 Deutsch 和 Jozsa 量子算法

原文:https://towardsdatascience.com/solving-the-probabilistic-deutsch-and-jozsa-quantum-algorithm-90c39a97013e?source=collection_archive---------38-----------------------

总结了一系列关于著名的 Deutsch 和 Jozsa 量子算法的文章

量子机器学习要不要入门?看看 动手量子机器学习用 Python

之前的文章中,我们开发了一个概率版本的 Deutsch 和 Jozsa 的量子算法。

作者图片

Deutsch 和 Jozsa 的原始算法告诉你一个给定的函数是产生恒定输出(总是相同)还是平衡输出(0 和 1 的数量相等)。但是它假设平衡函数在特定的边界内是平衡的。例如,如果边界是两个(如在 Deutsch 的第一个算法中),那么该算法假设一个游程的结果是 0,另一个游程的结果是 1。

当观察现实世界的概率系统时,比如扔硬币,这个假设显得不切实际。你可以连续掷三次或更多次硬币,结果都一样。当然,你可能会怀疑这枚硬币是否公平。每次掷硬币产生相同的结果,得到公平硬币的可能性就会降低。然而,你不能断定这枚硬币肯定被骗了。

我们的概率版本去除了这个假设。当硬币确实是公平的时,算法产生输出 0。如果硬币可能被欺骗,算法产生两个输出,0 和 1。输出为 1 的概率代表硬币是公平的概率。

然而,算法还不完整。虽然它对公平硬币和欺骗硬币产生不同的输出,但是这些输出是不明确的。如果你有一个公平的硬币,那么算法总是返回 0。我们在 100%的情况下测量为 0。因此,测量 0 的概率代表拥有公平硬币的概率— 100%。但是当你有一枚被骗的硬币时,测得的概率 1 代表有一枚公平硬币的概率。

这意味着,当我们在 25%的情况下测量 1 时,获得公平硬币的机会是 25%。当公平硬币的机会几乎为零时,这就成了问题。然后,我们会几乎总是测量 0。这意味着一枚被骗的硬币。但是,如果我们总是测量 0,这将意味着一个公平的硬币。

所以,我们需要调整我们的量子电路。在开始之前,让我们先简单看一下第一个版本。

下图描绘了三次平视投掷的电路。

作者图片

相比之下,下一条赛道显示了两次正面朝上投掷和一次反面朝上投掷的赛道。两个电路之间的唯一区别是代表各自 toss 的甲骨文部分。

作者图片

该电路有五个部分。首先,我们将代表投掷的量子位放入叠加态。第二,一位神谕将哈达玛门应用在代表朝上投掷的量子位元上。此外,它在代表正面投掷的量子位上应用受控非门。

第三,我们将辅助量子位带回|0⟩状态,一个三重受控非门计算出所有量子位都代表正面投掷的公平硬币的概率。第四,一系列的 Z 和 Hadamard 门,然后是另一个三重控制的 NOT 门,计算所有量子位都代表朝上投掷的公平硬币的概率。

第五,我们测量辅助量子位。

如果一些量子位代表正面朝上,而另一些代表反面朝上,那么三控制非门都不适用,因此,辅助量子位停留在|0⟩.状态

这就是我们想要改变的。它不应该留在|0⟩.它应该变成|1⟩。

这听起来不太难吧?然而,问题是,我们决不能干扰三个受控非门中的任何一个。这是一个真正的问题。

让我们从简单的开始。如果我们想要|1⟩态的辅助量子位,为什么不在三个受控非门之前去掉辅助量子位上的非门呢?

当我们有一枚公平的硬币时,这种方法非常有效。然后,辅助量子位处于|1⟩.状态

作者图片

但是,当所有三次投掷都相同时,例如正面朝上,如下图所示,那么我们以错误的概率将辅助量子位测量为 1。而不是 12.5%,现在是 87.5%。

请注意,不准确是由于我们使用的模拟的经验性质。

作者图片

显然,解决方案必须考虑代表投掷的量子位元。只有当三次投掷都处于相同的状态时,我们才必须在辅助量子位上应用非门。

因此,当所有三个量子位都代表正面投掷时,它们都处于|−⟩状态,因为我们在甲骨文中应用的受控非门对它们执行相位反冲。我们可以在他们每个人身上安装哈达玛门,把他们带进|1⟩.然后,我们应用另一个三控制非门。接下来的哈达玛门把他们带回了|−⟩.

作者图片

当所有三个量子位都代表正面投掷(表示被骗的硬币)时,这种方法非常有效。现在,测量 1 的概率代表公平硬币的概率。

作者图片

但是一旦掷硬币时,它就失败了。然后,我们只在大约 50%的时候,不把量子位测量为 1。

作者图片

这是因为我们新增加的三控制非门也适用于这种情况。当你看电路时,你会看到我们在一个量子位上应用了三个哈达玛门,代表一次抛尾。由于哈达玛门会自行回复,前两者会互相抵消。但是第三个哈达玛门将量子位放入|+⟩.状态我们也知道这种状态为:

这个州是基础州|0⟩和|1⟩.的组合因此,三控制非门适用于|1⟩部分。

好吧,也许我们需要应用不同的门序列?我们可以试试。但不会成功。问题出在我们的先知身上。当我们把代表正面朝上的量子位放入|−⟩状态时,我们把代表反面朝上的量子位放入|1⟩.状态

这两种状态相互正交。看看布洛赫球会有所帮助。

作者图片

我们故意让代表正面朝上和反面朝上投掷的量子位元互相正交。它允许我们安排量子位的状态,使三控制非门按照我们想要的方式工作。我们使用一个三重控制-不考虑正面朝上的投掷,一个考虑反面朝上的投掷。

诀窍是将代表正确一侧(正面或反面)的量子位置于一种状态,在这种状态下,它有 50%的概率被测量为 1。结果,每一个三重受控非门只把辅助量子位的 1/2 放入|1⟩.状态这是在三次投掷结果相同的情况下,公平硬币的总体概率。但是一旦单个量子位代表另一面,三重控制不再适用,因为我们已经把相应的量子位放入|0⟩.态

我们将量子位表示为彼此正交的两边,因为|0⟩状态与量子位状态正交,量子位状态表现出 50%的概率来测量量子位为 1(例如|+⟩).状态

因此,虽然正交性有助于我们在一个计算中取得成功,但它会阻止我们进行另一个计算。我们不能轻易地重新排列量子位指向相反的方向。这就需要我们考虑每一种正面朝上和反面朝上投掷的特定组合。这将非常麻烦。

相反,我们增加另一个量子位来代表每一次投掷。在我们的神谕中,我们应用了将量子比特放在相反方向的门。例如,我们把代表正面的量子位留在|0⟩,把代表反面的量子位留在|1⟩.

然后,我们应用另一个三重控制——如果所有三个量子位都代表正面朝上或反面朝上,就不是这样。所以我们用拥有公平币的概率来衡量辅助量子位为 1。

下图描绘了代表一枚(据称是被骗的)硬币的最终电路,该硬币连续三次产生正面朝上。

作者图片

结果表明,通过将量子位测量为 1 来表示公平硬币的概率约为 0.125。

作者图片

下图显示了代表两次正面朝上投掷和一次反面朝上投掷的电路。

作者图片

我们总是将这个电路测量为 1——表明拥有一枚公平硬币的确定性。

作者图片

结论

在这篇文章中,我们完成了 Deutsch 和 Jozsa 量子算法的改进版本。

这个最后的电路通过测量辅助量子位 1 来产生似是而非的结果,1 代表一个公平的硬币。

此外,这篇文章阐述了解决给定问题的不同方法——包括那些没有解决问题的方法。通常,我们关注的是有效的事情。但我相信,看到失败也有一定的价值。它说明了解决问题的整个过程,这个过程通常不像我们希望的那样简单。看到那些不起作用的方法可能会防止你犯同样的“错误”和浪费时间去做一些不起作用的事情。

量子机器学习要不要入门?看看 动手量子机器学习用 Python

在这里免费获得前三章。

SOM:你不知道你需要的数据科学工具

原文:https://towardsdatascience.com/som-the-data-science-tool-you-didnt-know-you-need-98e7e1460112?source=collection_archive---------25-----------------------

Som 是一种产生输入空间的低维表示的人工神经网络。

蒂姆·斯卡佐Unsplash 上拍摄的照片

1982 年,芬兰教授 Teuvo Kohonen 在题为“拓扑正确特征图的自组织形成”的出版物中描述了一种新算法,该算法旨在提供训练数据集样本在较小空间(通常为二维)中的表示,目的是保留初始拓扑并便于其解释。

在很短的时间内,这种被朋友们称为 SOM 的方法变得越来越成熟,这也归功于后来的研究提供了更先进和更强大的算法。

这种类型的算法属于人工神经网络类,对于现代数据科学家来说无疑是一种不可或缺的工具,因为它提供了降维、聚类和数据可视化的强大组合。

用 R 生成的图像,带有彩虹和等离子体调色板

它是如何工作的

在 SOM 中,训练数据集的统计单元由称为码本的 k 维向量来近似,我们将在后面看到,码本的功能与 k-means 方法中的质心的功能非常相似,不同之处在于它们被排列在平面上,以便产生规则的模式。

每个码本与矩形网格的一个单元相关联,以便允许输入空间的二维表示。

地图的构建有四个明确定义的阶段:

  • 建立
  • 初始化
  • 培养
  • 地图评估

建立

在设置阶段,为构建地图固定了一些基本特征,如下所列:

  • 大小:地图的大小主要根据要绘制的统计单元的数量和要获得的分辨率来选择。简而言之,你必须靠自己的努力,不断尝试,直到达到满意的结果。
  • 细胞形状:细胞可以是正方形或六边形。虽然这看起来无关紧要,但这个选择可以改变地图的拓扑结构,这是因为从 4 边到 6 边,或者相反,周围的环境会增加或减少。
  • 地图形状:地图可以是平面的,也可以是环形的。第一种类型可以使地图在边上不规则,同时仍然保持容易解释,而第二种类型不存在结构异常,但可能更难以解释。

初始化

在初始化阶段,一个 k 维向量(初始码本)被分配给网格的每个单元,它可以通过两种不同的方式选择:

  • 随机生成向量。
  • 从数据集中随机抽取。

这两个标准是等价的,因为它们几乎总是导致相同的结果。

培养

在跨时代进行的训练阶段,地图越来越精确地自我组织,直到它找到自己的清晰和定义良好的拓扑结构。

在每个时期中,表示统计单元的向量被呈现给算法,其通过欧几里德距离与最相似的码本相关联。获胜的码本通过一个函数来更新,该函数考虑了与该单元的距离,对于与时期和距离本身成反比的函数进行加权,该函数被称为邻域函数。

以这种方式,最接近统计单元的码本被更新为更类似于它。而那些更远的人经历难以察觉的变化,还考虑到随着年龄的增加,更新将变得不那么显著,因此地图越来越稳定。

当码本的更新将是零或者在任何情况下都是察觉不到的时,训练被终止。

以下是细胞在训练过程中如何更新的一个非常基本的例子:

展示其工作原理的短片

地图质量指标

两个指标主要用于评估我们的 SOM 是否是一个好地图:

  • 量化误差:它是统计单元和与之相关的码本之间距离的平均值。评估地图与初始数据的吻合程度非常有用。
  • 地形误差:它是每个小区的码本和相邻小区的码本之间距离的平均值。它是平滑度的度量,用于评估地图的规则性。

解释

SOM 的解释是通过按照预先定义的色度标度给地图着色来进行的。

一旦变量被固定,每个单元格就根据固定组件的值进行着色。单元格越暗,变量的值越高,反之亦然。

可以对所有的 k 个变量进行这种操作,因此可以对图谱进行详尽的解释,只要想想可以识别的聚类就知道了。

使用示例

为了清楚起见,我提供了一个如何使用 Kohonen 地图的例子。

这些地图参考了去年一次大学考试的试卷。在这项工作中,利用联合国提供的发展和性别指标对亚洲国家进行了分析。

在本文中,我使用 SOM 来查找彼此相似的国家,并给出比其他聚类算法(如 k-means)更详细的表示。

下面我将总结一个非常简短的论文节选,我用颜色标出了性别不平等指数(GII)、人类发展指数(HDI)和性别发展指数(GDI)。

用 R 生成的图像

但要强调高级官员会议的直接作用。事实上,乍看之下,它们已经给了我们三条信息:

  • 相关性:从色差中,我们立即观察到图 2 和图 3 中描述的变量之间的良好相关性,而这两者与图 1 中的变量呈负相关。
  • 集群:概述了一些重要的集群,例如在左上角的单元格中可以看到由五个国家组成的集群。
  • 可变性:如果色差有许多阴影,这意味着感兴趣的变量有相当大的可变性。

这个例子只是一个小小的尝试,展示了一个制作精良的地图能做些什么,尤其是当统计单位的数量呈指数级增长时。

最后

从给出的关于它们如何工作的简要描述中,很明显 SOM 不仅是一个非常丰富多彩和令人愉快的工具,在其他稍微无菌的统计方法中也是一个非常强大的工具,用于阅读和理解我们的数据。

在我们的分析工具中使用它当然是一个优势,因为它不经常使用,它可以提供不同的观点,并使非专家(他们可能正在阅读我们的报告)因其易于解释和迷人的美学而充满热情。

我强烈建议您深化之前所描述的内容,因为 SOM 的世界并没有就此结束,相反,它已经并将会产生越来越有趣的影响!

使用 GitHub Copilot 和 Python 的一些实验

原文:https://towardsdatascience.com/some-experiments-using-github-copilot-with-python-90f8065fb72e?source=collection_archive---------3-----------------------

我最近拿到了 VS 代码的 GitHub Copilot 扩展,这太棒了(有点吓人)

https://unsplash.com/photos/UT8LMo-wlyk

查看我最近关于用 GitHub Copilot 编写测试的文章

https://www.realpythonproject.com/writing-better-tests-with-ai-and-github-copilot/

获取口袋妖怪数据的函数

作者 GIF

  • 也能够自动完成我的评论。最初,我从未打算将它保存到 json 文件中。
  • 添加了一些行内代码注释
  • 使用外部库(请求)发出请求。使用 Json 保存数据。
  • 自己选择了一个合适的文件名
  • 能够找到数据来源,令人惊讶的是有人的 GitHub repo

压缩和解压缩文件的函数

作者 GIF

  • 能够导入所有必要的库。虽然它确实进口了 shutil,也没用过。
  • 使用 zipfile 库来解压缩/zip
  • 对于第二个函数,它没有导入 zipfile 库。
  • 能够使用正确的参数

构建 tictactoe 游戏

作者 GIF

  • 生成了 64 行代码
  • 能够为各种目的编写函数
  • 知道 tictactoe 板的获胜组合
  • 增加了错误处理
  • 增加了打印语句和接受用户输入的能力
  • 检查游戏结果的逻辑

尽管它编写了所有的子功能,但它从未调用它们来实际构建一个可玩的游戏

秘密价格

作者 GIF

  • 添加了一个参数
  • 使用加密 api 来获取数据
  • 能够返回正确的价格列

构建一个 streamlit 应用程序来显示 Github Repos

作者 GIF

为此,我不得不写多条评论,这实际上就像是我在和副驾驶配对编程。然而,大部分代码是由 Copilot 生成的。

  • 能够从 github api 获取数据
  • 由于我提到了流行,它根据“明星”对回购进行了排序。令人难以置信的是,它是如何将“明星”与流行联系起来的。
  • 它能够使用外部库 streamlit (streamlit 用于构建 web 应用程序)
  • 它还添加了要在 web 应用程序中显示的标题和文本
  • 它重用了以前创建的函数
  • 对于大多数部分,它也是自动完成我的评论

一般观察

  • 变量名和函数名非常容易理解
  • 添加了相关的内联代码注释
  • 能够使用外部库
  • 能够从各种数据源获取数据
  • 代码的格式很简洁,有适当的缩进和换行符
  • 我花了相当多的时间(尝试不同的评论)让它实际使用 streamlit 并构建一个简单的应用程序。最后,我导入了这个库,它开始使用这个库生成代码。然而,有时它也能够自己生成代码
  • 当试图获得口袋妖怪/加密数据时,它经常提出使用美丽的汤来刮数据的建议。网络抓取并不总是最好的选择,在某些情况下,你甚至可能会违反一些法律。

有些奇怪

  • 有时候表现得很奇怪。例如,有时生成的代码包含一些其他用户的本地文件路径,例如“用户/项目/……”
  • 我试图获得一个名为 api_key 的变量的建议,它实际上建议了一个带有随机键的字符串。当然,这实际上可能是随机的,但的确很奇怪。
  • 有时它会产生可接受的代码。当我试图为 streamlit 生成代码时,它一遍又一遍地生成同样的两行代码
  • 它有时会暗示一些不必要的进口
  • 出于某种原因,它继续生成使用 Dash 的代码,尽管我特别提到了 streamlit

我的观点和几个问题

我的观点是我自己的

它肯定会提高软件开发人员的生产力。然而,我没有看到它取代一个软件开发人员。Copilot 经常生成无意义且重复的代码。它有时也没有导入必要的库。它基本上就像类固醇上的风筝或泰宁。

我能想到的一个很好的类比是谷歌翻译。它已经存在很多年了,但它并没有取代对真正翻译的需求。你可以在几秒钟内把一篇文章从英语翻译成日语。然而,你仍然需要一个精通两种语言的人来确保译文语法正确,传达与原文相同的信息。

我能想到的另一个问题是——谁将对代码负责?如果我用 GitHub 生成了一些代码,后来因为某种原因被人起诉了。我能把责任推到 GitHub 身上吗?GitHub 很可能会让用户同意一些条款和条件,以防止他们被起诉。所以我们实际上需要有经验的人来确保生成的代码可以安全使用。

虽然 GitHub Copilot 对新项目很好,但我不确定它在处理现有代码库时是否同样有用。在现有的代码库中,它必须遵循现有的编码风格,并且能够重用已经编写的代码。我没有尝试过与 Copilot 合作一个已经存在的项目,所以我不能发表太多意见

假设随着越来越多的用户使用它,它会不断地学习,它将如何区分“好”代码与“坏”或“意大利面条代码”。

结论

GitHub copilot 可以保证的一件事是,它将使编码和软件在总体上更容易获得💯

你对副驾驶有什么看法?你尝试过什么酷的东西吗?你认为它将如何影响软件/数据科学行业?让我在评论中知道发布保存草稿恢复新的更改

资源

更多关于 GitHub Copilot 的文章

Alberto Romero 撰写的这篇文章更深入地探讨了 GitHub Copilot

Bex T. 撰写的这篇文章也很值得一读

我在六月份发现的一些关于数据可视化的有趣文章和资源

原文:https://towardsdatascience.com/some-interesting-articles-and-resources-on-data-visualisation-that-i-discovered-in-june-c00b58d15946?source=collection_archive---------29-----------------------

数据科学讨论

从网上收集的一些关于数据可视化的最新有趣文章和资源的摘要。

图片由克里斯·马汀发自 Pixabay

数据科学家的工作是很刺激,但同时又是很累,因为它要求与时俱进。

始终掌握最新技术。因此,数据科学家必须阅读并通知自己。

在本文中,我指出了一些关于数据可视化的文章和资源,我发现它们非常有趣,您可以从中获得灵感。

1.探索性数据分析

EDA 是数据分析前的第一步。它允许理解数据和发现一些初步的见解。

1.1 自动绘图仪

Autoplotter 是一个用于探索性数据分析(EDA)的 Python 包。可以通过pip包管理器安装。一旦安装,你可以运行它作为一个独立的应用程序。Autplotter 允许加载数据集,并通过最常见的 Python 库(如seaborn)绘制数据集。

运行后,主应用程序如下图所示:

作者图片

左侧有一个菜单,您可以从中选择图。您可以在多种类型的图表和多种类型的 Python 库之间进行选择:

作者图片

所选图形显得相当幼稚,如下图:

作者图片

此外,Python 源代码无法下载。无论如何,这个库的目的是提供一个初步的 EDA,这可能是非常有用的。

要了解更多细节,您可以查看完整的库文档:

https://pypi.org/project/autoplotter/

1.2 可观察汇总表

如果你想快速浏览你的数据集,Observable 的工作人员提出了新的实验性的SummaryTable函数,它可以对类似 CSV 的数据进行操作。

给定一个 CSV 数据集,该函数允许快速可视化每个列的分布和一些统计数据,例如缺失值的百分比平均值中值标准偏差。通过一行代码,您可以构建以下可视化:

作者图片

有关更多信息,您可以通过以下链接阅读实验性SummaryTable功能的官方文档:

https://observablehq.com/@observablehq/summary-table

1.3 学到的第一课

不要花时间构建 EDA 图。您可以利用预构建的工具,这可能会加快您对数据的理解。

2.数据显示

数据呈现涉及如何将数据呈现给最终受众。我发现了一些有趣的文章,可能有助于数据呈现。

2.1 Excel 中有趣的图表库

该资源包含许多有趣的模型,包含 Excel 中的不同图表。每个模型都有评论,可以免费下载。该图库中包含的图表展示了良好的编码知识并不是构建良好图表的必要条件。

例如,一个非常有趣的图表是美国政府的收入和支出。该图显示了带有一些注释的时间序列。作者还解释了如何建立这样一个图表。

对我来说,非常令人惊讶的是 Excel 允许构建非常强大的可视化,其中还包括注释和许多细节。

以下链接提供了 Excel 图表的完整图库:

https://simplexct.com/charts-in-excel

2.2 一些数据讲述(红色)图表

数据科学家初学者的主要困难之一是以正确的方式可视化数据。图表应该把观众的注意力集中在某件事情上。

Cole Nussbaumer Knaflic 是一位非常受欢迎的数据讲述影响者,他提出了一些有趣的图表,从中你可以获得绘制演示图表的想法。所有展示的图表都是用红色绘制的,在她的文章中,科尔解释了她为什么在图表中使用这种颜色。

图表做得非常好,并带有丰富的注释。其中最有趣的一张图表是系列的第六张。该图包含一些注释和预测,如虚线所示。

全文可通过以下链接获得:

https://community.storytellingwithdata.com/challenges/june-2021-reading-into-red/only-six-red-graphs

2.3 R 中的条形图动画

当你展示一个图表时,一个令人印象深刻的方面是动画。例如,一个动画可以展示数据如何随着时间的推移而演变。 阿比纳夫·马拉斯提出了一个关于如何通过ggplot包用 R 语言制作条形图的教程。

作者提出了两种制作动画的策略:

  • 保持轴不变,让条形随着时间增长
  • 带值的轴随着时间的推移自动调整。

有关更多详细信息,您可以通过以下链接阅读全文:

2.4 在 DataWrapper 条形图中显示置信区间

当您基于已经执行的一些预测构建数据演示时,显示预测的置信区间非常重要。

DataWrapper 可以帮助你。数据包装器是一个非常流行的在线工具,它允许创建图表。该项目已经扩展到包括条形图中的置信区间。

例如,您可以构建如下所示的条形图:

作者图片

有关如何在 DataWrapper 上显示置信区间的更多详细信息,您可以阅读完整的文章:

https://blog.datawrapper.de/confidence-intervals-value-markers-bar-charts/

2.5 第二个教训

请在你的观想中使用注释和动画!此外,如果你的观想包含一个预测,你应该增加置信区间。注释、动画和置信区间有助于观众理解图表的内容,并将注意力集中到你想要传达的内容上!

3.旗帜

当你建立一个可视化,选择正确的调色板是非常重要的。

3.1 从图像中构建调色板

Google Art & Culture 提出了一个非常有趣的在线工具来从图像中构建调色板。首先,你必须上传一个图像,然后你选择适合你的调色板。

例如,我可以在工具中上传以下图像:

图片由克里斯·马汀来自 Pixabay

该工具建议使用以下调色板:

作者图片

除了调色板之外,在线工具还提供一些相似的图像,即具有相同调色板的图像。

您可以通过以下链接亲自试用该工具:

https://artsexperiments.withgoogle.com/artpalette/colors/bbb6b4-3362bf-6b6260-a93c3e-243c7e

3.2 如何选择最佳的调色板

Elena V Kazakova 提出了一篇关于如何选择最佳调色板来吸引观众的有趣文章。我不想破坏这篇文章,但有些亮点可以做:

  • 通过颜色,你可以影响观众的情绪
  • 你应该选择和谐的颜色。

文章还调查了色彩心理学研究的其他方面。我建议您通过以下链接阅读全文:

[## 色彩的力量

towardsdatascience.com](/the-power-of-color-be3bbe9f6bc1)

3.3 第三个教训

当你建立观想时,不要忘记处理颜色。请不要为你的图表使用默认颜色,而是为你的观众选择正确的调色板。

4.另一个有趣的阅读

在这一部分,我将继续我在六月份发现的其他有趣的文章。

4.1 Javascript 中的甘特图

甘特图是说明项目进度的条形图。有许多工具可以用来制作甘特图。无论如何,如果你想从头开始构建图表, Alfrick Opidi 解释了如何使用anychart Javascript 库构建甘特图。

对于现有的工具,您必须手动构建图表,而anychart库允许自动加载您的数据。因此,在开始设置图表之后,就可以加载数据了。

您可以通过以下链接阅读全文:

摘要

在这篇文章中,我推荐了一些关于数据可视化的有趣文章和资源,这些都是我在 6 月份发现的。

总的来说,在六月份,我学到了以下关于数据可视化的东西:

  • 有许多数据可视化工具:其中一些需要编码知识,另一些则不需要。你应该选择你最熟悉的工具;
  • 不要花时间去构建一个令人印象深刻的初步图表,因为你的最终图表会改变。相反,利用一个预建的工具,它可以帮助你理解你的数据;
  • 当你构建数据表示时,使用注释、动画和置信区间,如果你的图包含一个预测。此外,你应该注意颜色

相关文章

机器学习会议综述过程中的若干问题

原文:https://towardsdatascience.com/some-issues-in-the-review-process-of-machine-learning-conferences-2c19c1eef42f?source=collection_archive---------19-----------------------

意见

以及我们能做些什么来解决它们

乔舒亚·沃罗尼耶基Unsplash 上拍摄的照片

这是怎么回事?

T 今天,我想谈谈机器学习会议 的 同行评议过程,我想强调一些可能破坏新知识创造的问题。**

机器学习社区最近的成功导致提交给会议的论文数量急剧增加。这种增加使得影响这些会议目前审查进程的一些问题更加突出。审查过程有几个问题可能会破坏科学研究的本质,即完全客观、非政治化、无偏见和无不当行为(如抄袭、欺骗、不当影响和其他不当行为)。

在最近的一份预印本中,我强调了(https://arxiv.org/abs/2106.00810)审稿人招募、违反双盲流程、欺诈行为、数字评分偏差和附录现象(即在论文附录部分发布结果变得越来越普遍的事实)的问题。对于这些问题中的每一个,我都提供了简短的描述和可能的解决方案,目的是提高机器学习社区对这些问题的认识。

介绍

由于深度学习(DL)[1] 的成功,机器学习(ML)在过去十年中受到了极大的关注。这种兴趣导致提交给 NeurIPS、ICML、ICLR 等会议的论文数量急剧增加。相比之下,2014 年至 2020 年期间,NeurIPS 提交的论文数量平均每年增加 34%【2,3】。

每年在主要机器学习会议上提交的论文数量(i 作者图片,d 此处下载数据:https://we.tl/t-UvCS7dfIs4)。

然而,尽管有大量的工作,这些会议的接受率多年来大致保持不变,或略有下降[2,3] 。这一点似乎表明,这些会议与提交的数量成比例。然而,审查过程中有几个问题可能会破坏科学研究的性质,即完全客观、非政治化、无偏见和无不当行为。

部分主要机器学习会议的录取率加权平均值:EMNLP、CVPR、NeurIPS、ICML、IJCAI、AAAI(我是作者的法师,在这里找数据https://we.tl/t-UvCS7dfIs4)。

那么,有什么问题呢?

基于这些年来接受率大致保持不变的数字,人们可能会认为 ML 会议已经很好地适应了 ML 社区所做的越来越多的工作。然而,如果这些会议多年来未能保持相同的质量标准,则之前的陈述是错误的。

关于质量标准,我们指的是一个审查过程,该过程可以通过符合科学研究价值观(我们在引言中提到的价值观)的合格能力来创造科学知识。然而,正如我们将在接下来的段落中指出的,有些问题并不是由于提交的论文数量较多而引起的。由于大量的工作,其中一些变得更加明显。事实上,许多研究人员最近提出了对当前审核流程的担忧,其中一些人主张对审核系统进行深刻的变革[4–10]。

现在,我提出了审查过程中的一些主要道德问题,没有任何优先顺序。这些问题是:

  • 评审人员招聘和评审经验不足
  • 违反双盲法
  • 投标系统中的欺诈行为
  • 数值评定中的偏差
  • 阑尾现象

招聘审查人员,缺乏审查经验

Clem OnojeghuoUnsplash 上拍摄的照片

能够招募到评审员在扩大会议规模方面起着很大的作用。会议,还有期刊,依靠社区来繁荣。

然而,会议不雇佣也不支付审稿人(即使相同的审稿人可能需要付费才能发表论文/参加会议)。因此,应该限制每位评审者要评审的论文数量。

如果提交的论文数量增加,而审查过程的时间长度保持不变,那么很自然地,提交的论文数量增加后,审查人员也必须相应增加。此外,我们还需要考虑一些评审员可能退休(即不想再评审)的情况:这加剧了每年需要额外评审员的影响。因此,正如我们刚刚看到的,提交论文数量的增加需要新审稿人数量的相应增加。

然而,正如[6–8]中所指出的,合格评审员的数量增长速度要慢得多。在[8]中,ICML 董事会主席约翰·兰福德说

有重要证据表明,在数年的指数增长下,机器学习中的论文审阅过程正在崩溃。

应对这种急剧增长的会议减轻了成为评论者的一些要求。在 NeurIPS 2016 中,通过要求作者提名至少一名愿意成为审稿人的作者来招募 36%的审稿人【6,9】。然而,很大一部分(10 个中有 7 个)新评审者由博士生组成,即初级研究人员,他们可能不具备客观评估新科学研究的知识或经验【6,9】。有时候,新来的审稿人甚至可能是硕士生。最近,ICML 2020 向公众征集自我提名的评审员[6]。

总的来说,我们可以得出这样的结论:这种行为增加了低质量评审的可能性,其结果是在评审过程中产生了一种随意感【10】。

不幸的是,为缺乏合格的审查人员想出一个补救办法并不容易。一项长期行动是在学者课程中纳入一门关于审核流程的必修课。许多大学都要求学生互相评估,部分做到了这一点,但不幸的是,博士生并不总是这样。[4]中最近提出了一个类似的建议,其中建议在复习中包括导师。事实上,最近在 NeurIPS 2020 上举办的研讨会似乎表明了指导学者产生高质量评论的有效性。****

违反双盲法

大多数会议,如果不是全部的话,允许作者在 arXiv 等公共网站上发布预印本。有时作者甚至会在公共存储库(例如 GitHub)上发布他们算法的代码。因此,这些行动打破了双盲审查过程。

安特·汉默斯米特在 Unsplash 上的照片

打破双盲性质导致审查过程中的偏倚。在[12]中,他们发现了录取率和 arXiv 上发表的高声誉论文之间正相关的统计证据,上的声誉是基于谷歌学术引用的数量。

此外,总是在[12]中,他们指出不太自信的评论者可能倾向于给知名作者高分,给不太知名的作者低分。虽然他们的分析没有显示出因果关系,但这表明确实存在正相关。如果像 arXiv 这样的网站允许作者匿名发表他们的作品,这个问题可以得到部分解决,正如[12]** 中所提议的。在极端的情况下,人们也可以禁止预印本的出版。这项禁令可能会降低新知识的传播速度。然而,这将导致一个纯粹的双盲审查过程,有助于减少偏见。**

投标系统中的欺诈行为

杰弗逊·桑托斯在 Unsplash 上拍摄的照片

如今,大规模的 ML 会议采用竞价系统,让评论者选择他们想要评论的论文。

然而,竞价系统可能会导致评审员之间达成协议(交换安排),目的是提供正面或负面的评审。同样,审稿人也可能为了自己的利益,试图故意拒绝自己不喜欢的论文。

不幸的是,这种行为在学术界并不罕见。2019 年 6 月 13 日,其论文即将在凤凰城 ISCA 2019 发表的博士生陈在佛罗里达大学的校园大楼内上吊自杀[13,14]。这名学生留下了一张纸条,说他拒绝继续从事学术欺诈行为,并指责他的导师李涛博士[14]。

ACM-IEEE 联合调查委员会已经启动了一项调查,确定“没有证据表明论文审查过程中存在不当行为。”[14].然而,正如在[13]中指出的,向辉的笔记本电脑包含了不止一个会议的大部分提交内容,以及去匿名的评论和讨论,这一事实令人不安和麻烦。在[13]中,他们总是指出“另一个 ACM SIG 社区存在串通问题,其中调查人员发现一组 PC 成员和作者串通投标和推送彼此的论文,违反了通常的利益冲突规则”[13]。

在【15】中,Langford 提出了鱼雷审查的问题,即

如果一个研究方向是有争议的,几百个评论者中只有 2-3 个人反对,这 2-3 个人可以竞标论文,给它糟糕的评论,并阻止发表。无限期地重复下去,这就给了一个群体中 2 或 3 个最保守的成员扼杀新的研究方向的权力,潜在地大大阻碍了整个群体的进步。

总是在[15]兰福德指出,鱼雷检阅可能不是幻想。有时人们倾向于对他们想要拒绝的论文进行投标,“理论上拒绝比可能接受更容易”。

另一个问题是理性欺骗【17】,审稿人倾向于拒绝与他们自己的作品竞争的论文

在[16]中,他们分析了人们在竞争性同行评审中的策略行为,认为“竞争激励评审者采取策略行为,这降低了评价的公平性和裁判之间的共识。”

所有这些行为似乎都是人类本性的一部分。作为人类,我们倾向于优先考虑我们的生存机会。因此,有人可能会争辩说,人类天生倾向于个人利益而不是集体利益,这导致了有组织的个人小团体中恶意模式的产生。为了解决这些问题,会议可能在审查过程中采用随机化。

随机选择审稿人将有助于减少恶意审稿人获得他/她想要审阅的论文的可能性。然而,随机化可能会导致审查过程的质量下降,因为作者将无法对他们有能力的论文进行投标。该解决方案在[11]中进行了进一步的研究,作者提出了一种随机化审查过程的算法。

数值评定中的偏差

约书亚·戈尔德在 Unsplash 上拍摄的照片

审查者在他们的审查中必须为他们审查的每篇论文提供一个数字评级,以及一个置信度得分。

众所周知,人们很难将观点映射到一个数字上【18】。

此外,正如【20】中指出的,数字评级的另一个问题是选择偏差【19】。

选择偏差(Selection bias)是指导致平均评分有正向倾斜趋势的现象。

这个想法是“用户可能会选择已经排名靠前的实体来表达意见和评级”[20]。这与之前讨论的观察结果一致,即接受率和高声誉的预印本之间存在正相关[12]。

在评审过程中,评审者在反驳期间可能会受到其他评审的态度/评论的影响。这也被称为从众偏差,这是一种遵从群体意见的愿望[21–23],即使存在压倒性的相反证据[23],这种愿望也会持续存在。因此,不太自信的评审者可能被诱导去遵从更自信的评审者的意见。

如【18】中所建议的,评审者可能会被要求进行比较或排名,而不是提供数字评级。为了解决从众偏差的问题,可以使用让多组评审员评审同一篇论文的想法,其中每组评审员彼此不了解。然后,评审可能会被随机化,区域主席将根据这些随机化的评审做出决定。

阑尾现象

照片由 Keagan HenmanUnsplash 拍摄

最后,但同样重要的是,ML 会议中的一个关键问题是结果的主要部分出现在附录中。

这就是我所说的阑尾现象。

会议通常是快速传播研究成果的场所,但论文数量的急剧增加导致了一场军备竞赛,作者倾向于以滥用附录为代价撰写更全面的论文。

这可能会导致评审者要求更多的工作,因为全面性的标准不断提高。此外,众所周知,大多数审查者倾向于不审查补充材料。事实上,大多数结果没有得到审查,这对知识的传播和 ML 社区的发展构成了巨大的威胁。

一个快速有效的解决办法是限制提交给会议的页数或者限制附录的页数。作者应该能够在有限的页数内陈述他们的结果。如果这是不可能的,那么作者应该考虑将论文提交给期刊。

也许曼梯·里社区对诉讼给予了太多的信任。

结论

不幸的是,ML 社区是高度分散的,由几个具有不同背景、使用不同方法的子社区组成。这可能是研究人员经常不能互相理解的原因。正如[24]中所指出的,由于背景的差异,论文经常被分配给彼此不理解的人,增加了审查过程是随机的这种感觉。然而,我们必须联合起来解决这个问题。

显然,所有这些问题都应该得到比这里更多的篇幅。这篇文章的目标是强调 ML 社区目前面临的一些问题,并提高研究者对这些问题的认识。

感谢您的阅读!

参考

  1. 印第安纳州古德费勒、纽约州本吉奥、库维尔和纽约州本吉奥,2016 年。深度学习(第 1 卷№2)。剑桥:麻省理工学院出版社。
  2. Charrez D,2019。NeurIPS 2019 年统计。网址:https://medium . com/@ dcharrezt/neur IPS-2019-stats-c 91346d 31 c8 f(2021 年 3 月 29 日访问)。
  3. 主要 AI 会议接受率统计,Lixin4ever。Github,2021。网址:https://github.com/lixin4ever/Conference-Acceptance-Rate(2021 年 3 月 29 日访问)。
  4. 2021 年在大型 ML 会议(如 ICML、NeurIPS)上改进审查流程的想法。https://docs . Google . com/document/d/1j 7 Mn 2 zkquszwj _ ezxdxbp 3 z _ JQtrSeUa-CQ 0 gotauyw/edit(2021 年 3 月 29 日访问)。
  5. 麦库克,2006 年。同行评议坏了吗?投稿数量增加,审稿人负担过重,作者对顶级期刊的流程提出了一个又一个的投诉。同行评议怎么了?。《科学家》,第 20 卷第 2 期,第 26-35 页。
  6. Stelmakh,I .,Shah,N.B .,Singh,a .和 Daumé III,h .,2020 年。新手审稿人实验,解决大型会议中合格审稿人不足的问题。arXiv 预印本 arXiv:2011.15050
  7. d .斯卡利,Snoek,j .和 a .威尔特施科,2018。避免同行评审过程中的公地悲剧。arXiv 预印本 arXiv:1901.06246
  8. j . Langford(2018 年)。当泡沫破裂时…https://hunch.net/?p=9604328(2021 年 3 月 30 日访问)。
  9. n . b . shah,b . tabi bian,b . Muandet,k . Guyon,I .和 Von Luxburg,u .,2018。NIPS 2016 审核流程的设计和分析。机器学习研究杂志。
  10. Tran,d .,Valtchanov,a .,Ganapathy,k .,Feng,r .,Slud,e .,Goldblum,m .和 Goldstein,t .,2020 年。Open Review 的公开评论:机器学习会议评论过程的批判性分析。arXiv 预印本 arXiv:2010.05137。
  11. Jecmen,s .,Zhang,h .,Liu,r .,Shah,N.B .,Conitzer,v .和 Fang,f .,2020。通过随机分配评审员来减少同行评审中的操纵。arXiv 预印本 arXiv:2006.16437
  12. 巴拉德瓦赫、图尔平、加尔格和安德森,2020 年。在双盲审查期间通过 arXiv 提交对作者进行去匿名化。arXiv 预印本 arXiv:2007.00177
  13. T.维贾伊库马尔。ACM/IEEE 计算机体系结构会议中潜在的有组织欺诈。https://medium . com/@ tnvijayk/potential-organized-fraud-in-acmieee-computer-architecture conferences-CCD 61169370d,2020 年(2021 年 3 月 30 日访问)。
  14. 向辉五世,2020。证据对 IEEE/ACM 的调查提出了质疑。https://huixiang voice . medium . com/evidence-put-questions-on-the-IEEE-acms investigation-991 a6 d 50802 a(2021 年 3 月 30 日访问)。
  15. 约翰·兰福德。投标问题,2008 年。https://hunch.net/?p=407(2021 年 3 月 30 日访问)。
  16. Balietti,s .,Goldstone,R.L .和 Helbing,d .,2016 年。艺术展览游戏中的同行评议与竞争。美国国家科学院学报,113(30),第 8414-8419 页。
  17. 巴罗加基金会,2014 年。通过抑制同行评议中的“理性欺骗”维护科学交流的完整性。韩国医学杂志,29(11),第 1450 页
  18. Sparling,e . I . & Sen,S. (2011 年)。评分:难度如何?。第五届 acm 推荐系统会议录。RecSys '11,(第 149-156 页)。纽约:ACM。
  19. n .达尔维、r .库马尔、b .庞(2013 年)。准“正常”活动:关于平均评级的分布。第七届网络日志和社交媒体国际 AAAI 会议论文集,(第 110-119 页)。
  20. Centeno,r .,Hermoso,r .和 Fasli,m .,2015 年。数字评分的不准确性:处理社会网络中的偏见意见。信息系统前沿,17(4),第 809-825 页。
  21. 高、埃格尔、库兹涅佐夫、古列维奇和姚蜜,2019 年。我的反驳重要吗?来自一个重要的 NLP 会议的见解。arXiv 预印本 arXiv:1903.11367
  22. Buechel,b .,Hellmann,t .和 Kl ner,s .,2015 年。整合下的意见动力与智慧。经济动态与控制杂志,第 52 期,第 240-257 页。
  23. 所罗门·E·阿希。1951.群体压力对修改和扭曲判断的影响。团体、领导和男人,第 177-190 页。
  24. Sagun,Levent 等人,“深度学习中的科学与工程的研讨会后报告,NeurIPS 2019,温哥华。”arXiv 预印本 arXiv:2007.13483 (2020)。

一些鲜为人知的数据科学库

原文:https://towardsdatascience.com/some-lesser-known-data-science-libraries-18314b1ea43d?source=collection_archive---------23-----------------------

以及如何在 python 代码中使用它们…

图片由皮沙贝格哈特 G. 拍摄

简介

你从事数据科学已经有一段时间了吗?现阶段你一定很了解熊猫、scikit-learn seaborn、matplotlib。

如果你想扩展你的视野,学习一些不太出名但同样有用的库,你就在一个好地方。在本文中,我将向您展示一些鲜为人知的 python 中的数据科学家库。

让我们开始吧。

不平衡学习

如果你过去一直在构建一些有监督的机器学习模型,你会知道目标变量中的类别不平衡可能是一个大问题。这是因为在少数类中没有足够的样本供算法学习模式。

一种解决方案是创建一些合成样本,通过使用例如 SMOTE(合成少数过采样技术)来扩充用于学习的少数类。

幸运的是不平衡学习库将帮助你在任何不平衡数据集上实现这一技术。

您可以通过在终端中运行以下命令来安装不平衡学习库。

pip install imbalanced-learn

为了演示平衡数据集,我们将从 sci-kit 学习库中下载一个乳腺癌数据集。

from sklearn.datasets import load_breast_cancer
import pandas as pddata = load_breast_cancer()
df = pd.DataFrame(data.data, columns=[data.feature_names])
df[‘target’] = data[‘target’]
df.head()

现在让我们看看目标变量的分布。

df.target.value_counts()

该数据集确实是均匀分布的,尽管它并没有严重失衡:我们有 357 名乳腺癌患者和 212 名健康患者。

让我们看看我们是否能使它更平衡一点。我们将使用 SMOTE 对 0 类进行过采样。

from imblearn.over_sampling import SMOTEoversample = SMOTE()
X_oversample, y_oversample = oversample.fit_resample(data.data, data.target)
pd.Series(y_oversample).value_counts()

如你所见,数据集现在完全平衡了。每个类有 357 个实例。由于我们的操作,创建了 145 个人工实例。

统计模型

这是另一个特别为构建统计模型而设计的伟大的库。我通常用它来拟合线性回归

它真的很容易使用,你马上就可以得到许多关于模型的信息,如 R2 BIC,AIC,系数,以及它们相应的 p 值。当使用 scikit-learn 中的线性回归时,此信息更难获取。

让我们看看如何使用这个库来拟合线性回归模型。让我们先下载一个波士顿房价数据集。

from sklearn.datasets import load_boston
import pandas as pddata = load_boston()
df = pd.DataFrame(data.data, columns=[data.feature_names])
df[‘target’] = data[‘target’]
df.head()

上面是我们数据集的前五行。有十三个特征,我们可以看到目标变量是一个连续的数字。这是一个完美的回归数据集。

现在让我们使用 pip 安装统计模型库

pip install statsmodels

现在,我们可以使用下面的代码尝试将线性回归模型拟合到我们的数据中。

import statsmodels.api as sm
X = sm.add_constant(df.drop(columns=[‘target’])) # adding a constantmodel = sm.OLS(df.target, X).fit()
predictions = model.predict(X)print_model = model.summary()
print(print_model)

如您所见,我们刚刚将线性回归模型拟合到该数据集,并打印了该模型的详细摘要。您可以非常轻松地阅读所有重要信息,必要时重新调整您的功能,然后重新运行模型。

我发现与 scikit-learn 版本相比,使用 stats 模型进行回归更容易,因为我需要的所有信息都在这个简短的报告中提供了。

缺失编号

另一个有用的库是 missingno。它帮助您可视化缺失值的分布。

您可能习惯于使用 isnull()函数检查 pandas 中缺失的值。这有助于您获得每一列中缺失值的数量,但不能帮助您确定它们在哪里。这正是 missingo 变得有用的时候。

您可以使用以下命令安装该库:

pip install missingno

现在让我们演示如何使用 missingo 来可视化丢失的数据。为了做到这一点,我们将从 Kaggle 下载预期寿命数据集

然后可以使用 read_csv()函数加载数据集,然后从 missingno 库中调用 matrix()函数。

import pandas as pd
import missingno as msnodf = pd.read_csv(‘Life Expectancy Data.csv’)
msno.matrix(df)

因此,您可以看到丢失值的位置。如果您怀疑丢失的值在某个特定的位置或遵循某个特定的模式,这将非常有用。

总结

在本教程中,您已经学习了如何使用一些鲜为人知的数据科学库。具体来说,您学到了:

  • 如何使用 SMOTE 技术和不平衡学习库平衡数据集
  • 如何使用统计模型、进行线性回归
  • 以及如何使用 missingno 可视化缺失值。

我希望你能在实践中运用你在实践中所学到的东西。编码快乐!

最初发表于 aboutdatablog.com: 一些鲜为人知的数据科学库以及如何用 python 代码使用2020 年 8 月 15 日。

PS:我正在 Medium 和https://www.aboutdatablog.com/上撰写深入浅出地解释基本数据科学概念的文章。你可以订阅我的* 邮件列表 *每次我写新文章都会收到通知。如果你还不是中等会员,你可以在这里加入**

下面还有一些你可能喜欢的帖子

** </9-things-you-did-not-know-about-jupyter-notebook-d0d995a8efb3> **

Python 3.9 中的一些重要函数

原文:https://towardsdatascience.com/some-magnificent-functions-in-python-3-9-705c2970795b?source=collection_archive---------8-----------------------

Python 编程语言中我最喜欢的一些函数。

(src =https://pixabay.com/images/id-3597101/

介绍

Python 编程语言本身就包含了许多奇妙的函数和工具,以及标准库中一些令人敬畏的功能。这使得 Python 本身成为一种非常通用的语言,而不需要像其他语言那样依赖于依赖关系。在过去,我已经讨论了一些我最喜欢的 Python 模块,它们包含在标准库中,如果您想阅读我这样做的一些文章,您可以在这里查看我关于这个主题的上一篇文章:

</15-more-surprisingly-useful-python-base-modules-6ff1ee89b018>

然而,尽管这些工具很有用,实际的语言本身仍然有更多的乐趣。Python 编程语言中包含了许多非常有用的函数,这些函数在许多不同的场景中都非常有用。对于数据科学家来说尤其如此,因为许多这些操作在迭代观察列表上工作得非常好。

笔记本

№1:地图()

map 函数将给定的函数应用于 iterable 的每个元素。这对于快速有效地处理列表非常有用,甚至不需要编写 for 循环。我们将在下面的列表中使用 map()。

lst = [5, 10, 15, 20]

接下来,我们必须创建一个函数,我们希望每个元素都通过它。需要记住的一件重要事情是,我们将只能接受一个没有默认值的位置参数,否则映射将不起作用!

def addone(x):
    return(x + 1)

在我的例子中,我只是简单地给列表中的每个元素添加一个,但是如果你愿意的话,这些函数肯定会变得非常复杂。这里最大的障碍肯定是无法添加参数。但是,您总是可以从映射函数内部调用提取的方法来获取您可能需要的数据。除此之外,您将依赖于函数中设置的默认值。现在,我们将使用以下内容创建新地图:

lstpone = map(addone, lst)

您会注意到这个新对象的类型很可能是 map 类。为了改变这一点,我们可以简单地将一个相应的类型投射到我们的地图上,比如 list:

lstpone = list(map(addone, lst))
print(lstpone)

(图片由作者提供)

很容易看出,在 Python 中处理数据时,这样的东西可能会派上用场。同样,编写一个提取的 map()函数比用一些嵌套的 for 循环来执行算术要干净和简洁得多。我写了一篇关于抽取的整洁的文章,以及为什么作为程序员它是如此伟大的实践,如果你对这样一个概念感兴趣,它当然值得一读,如果你愿意,你可以在这里查看这个主题的更详细的内容:

[## 更多的方法意味着更好的代码

towardsdatascience.com](/more-methods-means-better-code-1d3b237f6cf2)

№2:zip()函数

Python 附带的另一个非常酷的函数是 zip()函数。大多数 Python 程序员可能都有过使用这个函数的经历,但是对于那些以前没有使用过这个强大函数的人来说,它确实值得一提。zip 函数从多个可迭代对象中创建一个新的迭代器,并允许在同一个循环中同时迭代不同列表中的多个元素。不用说,这对于比较两个组或者对特征执行某种程度的算术运算是非常有用的。

我们将在本例中使用以下列表:

lst1 = [23, 28, 43, 23, 23, 94, 32]lst2 = [34, 58, 109, 72, 33, 2, 90]lst3 = []

我们将得到每个匹配元素之间的平均值列表,方法是将这两个元素压缩在一起,然后在将新的平均值添加到空列表 lst3 之前计算每个周期的平均值。然而,首先我们需要写一个函数来计算我们的平均值:

def mu(x): return(sum(x) / len(x))

现在我们将使用 zip()函数编写一个简单的 for 循环,如下所示:

for one, two in zip(lst1, lst2): lst3.append(mu([one, two]))
print(lst3)

(图片由作者提供)

很容易理解为什么这样的东西对处理数据很有价值。跨多个特性进行迭代是一种您很容易发现自己所处的场景,所以这个解决方案肯定是一个很好的想法。我正好有一篇关于 Python 的 zip()函数的更深入的文章,当然值得一读。如果你以前从未看过,你可以在这里查看:

[## 关于 Python 中的 Zip,您需要了解的一切

towardsdatascience.com](/everything-you-need-to-know-about-zip-in-python-5da1416f3626)

№3:超级()

super()函数是 Python 编程语言基础中另一个非常有用的特性。这个函数比其他两个函数更加 Pythonic 化,并且允许子类引用它们的父类作为“super”这很有用的第一个原因是,我们可以避免显式使用基类,换句话说,我们可以控制是引用父类还是子类。另一个派上用场的地方是当我们处理多个子类和大量继承时。让我们考虑下面的类:

class Noodle(object): def __init__(self, name): print(name)

这个类继承自 object,Python 中的所有其他类也是如此。现在让我们为这种新的面条类型创建一个子类:

class Spaghetti(Noodle): def __init__(self): pass

在第一个例子中,我们只在这个类的初始化函数中使用 pass。因为这个类是意大利面条,我们不想把它标记为意大利面条,但是我们仍然希望它在初始化时打印出意大利面条。为了在这种情况下不做任何工作就打印出它的名字,我们可以在初始化函数中调用超级函数,同时调用父函数的初始化。

super().__init__("Spaghetti")

注意,现在两个类都将在初始化时打印它们的名字,如果没有这个调用就不会这样。当然,这并不是唯一的单继承情况,在这种情况下需要注意这个有用的函数。这个功能在很多不同的场景下肯定能派上用场,所以绝对值得熟悉一下!

№4:哈希()

在 Python 中,hash()函数用于返回对象的哈希值。哇,这是一个相对容易使用的方法,我认为它值得包括在内,因为有时看起来很有趣。哈希是一个有符号的整数,用于表示给定值。该函数将一个对象作为参数,例如字符串“Hello”:

print(hash(“ Hello”))-7944160827638463773

№5:格式()

format 方法可用于返回由格式说明符控制的对象的格式化表示形式。这些方法调用对象的内部 format()函数。这个方法有两个参数,第一个是我们想要格式化的值,第二个是格式规范。有关说明符的完整列表,您可以查看此页面:

总之,在这个例子中,我将把一个有符号的整数转换成二进制数:

print(format(1392391237, "b"))
1010010111111100011010001000101

我知道我之前说过有符号整数,但是这个整数要么没有符号,要么没有符号。我们可以通过将它转换为一个列表,然后将长度除以 8 来判断这一点:

print(len(list(format(1392391237, "b"))) / 8)3.875

当然,如果这是数字的真正二进制数,它将被 4 整除,所以我认为 Python 的格式只是省略了二进制数的符号部分,这是值得注意的—这很奇怪。

№6:枚举()

对于 Python 中的循环来说,一个非常有用的工具是 enumerate()函数。这个函数可用于在一个简单的调用中轻松地向循环添加计数器。这对于标注字典,调用索引,各种各样的事情都很有用!并允许您这样做,而无需在循环之外创建计数器并添加计数器。我们可以用它来完成与 zip 循环基本相同的事情,例如:

for i, w in enumerate(lst1): lst2[i] += w

这将修改我们正在枚举的索引,并添加到我们的 lst1 列表中的当前迭代中。

print(lst2)[57, 86, 152, 95, 56, 96, 122]

№7:内存视图()

你有没有好奇过计算机实际上是如何存储数据的?您可以通过查看 Python 中的 memoryview()函数来了解更多信息!这将允许我们查看代表 Python 对象中字符的字节缓冲区。考虑以下字符串:

lstmem = memoryview(bytearray(lst2, "utf-8"))print(list(lstmem))[104, 101, 108, 108, 111, 33]

这些是放入列表中的字符串中字符的单独表示。这听起来可能没什么用,但它可以用来制作浮点编码器。然而,你总是可以转换成 float 类型。不管怎样,我认为这是一个非常有趣和酷的方法!

结论

在过去几年中,Python 已经成为数据科学工作的重要资产,这是有充分理由的。虽然 Python 确实有它的缺点,但它有一个强大的生态系统,一个古老的代码库,甚至还有一个奇妙的标准库。这些是一些非常简单和众所周知的 Python 函数,但是我认为特别是对于初学者,或者那些想要更多地了解这些函数的人,这篇文章肯定会派上用场。非常感谢您阅读我的文章!

关于数据科学的一些哲学思考

原文:https://towardsdatascience.com/some-philosophical-thoughts-on-data-science-c33659ec63f6?source=collection_archive---------27-----------------------

意见

我们是否应该考虑制定一些与数据使用相关的法规?

卢卡斯·布拉塞克在 Unsplash 上的照片

在一个数据变得越来越重要的世界,我们是否应该思考一些与数据使用相关的法规的必要性?换句话说:数据科学是否应该受到某种方式的监管?

在这篇文章中,我想分享我对应用于一些特定领域的数据科学和机器学习的想法,并思考在这些领域规范数据使用的可能需求。

众所周知,工具在某种程度上是“中性”的;我的意思是:工具就是工具;没有好坏之分。工具的使用总是有好有坏。我的意思是,数据科学没有好坏之分:数据的使用可以被定义为好坏。当然,“好”或“坏”是和一些个人观点有关的东西;所以,问题是:应该有一些实体必须定义(作为法律?)数据的一些使用是“好的”,而另一些是“坏的”?

让我们看一些实际的例子…

1)数据科学在工业和制造工程中的应用

数据科学和机器学习在工业领域的使用越来越广泛,应用越来越多。考虑到我们可以准确预测何时停止机器进行维护,而不会延迟制造过程(准确的维护日期是预测的),这有点像魔法。

但我认为,当我们与人类打交道时,麻烦总是会到来。让我们来看一个特殊的例子。

作为一名工艺和制造工程师,我的一项日常工作是监控生产 KPIs 当然,我是用 python 和一些数据科学技术来做的。

我所做的非常简单:当某个产品被制造出来时,我控制这个特定产品的实际生产时间是否与理论生产时间一致。事实是,操作人员可能更快也可能更慢,但是,平均来说,我们只是对生产时间是否一致感兴趣(否则,这意味着存在制造问题),而不考虑在特定时刻工作的操作人员。

但是,如果在某些行业中,有人想寻找速度较慢的经营者来解雇他们,那该怎么办呢?是的,当然,这里有一些隐私问题(至少在意大利是这样的):比如,你不能提供带有操作人员姓名的数据,这样他们就可以因为工作缓慢而受到“惩罚”。但是当然,有时不需要用名字来表示数据;名字可以在会议之外说…

所以,问题是:我们如何在不影响人为因素的情况下处理生产数据?是否有必要在工业流程中规范数据科学(以及数据的使用)?如果是,我们如何做到这一点?

2)数据在法庭上的使用

众所周知,审判以事实为依据;当然,在法庭上,事实必须有证据和数据支持。

你必须知道,我和我的妻子热爱英语,为了保持我们在使用英语方面的训练,我们用英语看电视连续剧和电影。那几天我们在看电视剧《公牛》。是的,它不是一个新的;但是,如果你不知道我在说什么,这些故事有数据背景:杰森·布尔医生(主角)是一名心理学家,为律师或需要律师的人担任顾问。他的工作很简单:他必须为他的客户找到最好的陪审员。

但是他如何找到最好的陪审员呢?使用一些数据科学技术。实际上,他可以从陪审员那里找到数据(在某种程度上由软件支持),并可以确定哪些陪审员最适合他的客户。

你知道,在法庭上,重要的是要赢,而律师的工作就是说服陪审员和法官。但如果将来真的有人能选出最好的陪审员(和最好的法官)呢?)基于他们的行为,他们的习惯,以及他们对客户的信念?如果由机器学习算法选择的陪审员和法官来做出死刑判决会怎么样?谁会赢:算法还是人类行为?

3)机器学习和社交媒体

我最近读了杰伦·拉尼尔的书《立即删除你的社交媒体账户的十个理由》,这本书读起来令人难以置信(我真的建议你读一读)。

事实很简单,因为机器学习以非常简单的方式工作;“机器”向你学习,所以:

  • YouTube 为你推荐音乐(和视频)(“你也可以喜欢这个视频”)
  • 亚马逊会建议你买什么(“其他人也买了……”)
  • 脸书和其他社交媒体根据你最喜欢的东西向你展示东西(新闻、视频、照片等);这意味着你只能看到现实的一部分。

机器学习算法创建了一个你的模型,以便社交媒体向你展示你喜欢的东西:只是现实的一部分。这意味着,例如,如果你有一个政治观点,你会看到与这个观点一致的新闻,而你几乎不会改变它;换句话说,软件和算法(“社交媒体”)将强化你的想法、你的偏见、你的整个世界,你可能会保持同一个人,这基本上与人类生活的进化相反。

但是如果我想在 YouTube 上有一个类似“不要给我推荐其他视频:我喜欢不同类型的音乐,所以你真的不能给我贴标签”的按钮呢?为了“打破算法”,有时我会看一些完全不同的视频(是的,我亲爱的算法:我的女儿们用我的 YouTube 账户看儿童视频;看电视的不是我!)

总之:与数据使用相关的人类未来会是怎样的?未来世界的中心是人类还是数据(和商业?)会?

如果我们考虑一下制造业,我们知道自动化正在取代人类,在我看来,这甚至对重复性任务有好处……但数据科学会带来使用更快机器人的需求吗?

最终…在一个数据驱动的世界里,人类的未来会是怎样的?最慢的操作员会被解雇吗?最慢的机器人会被更快的机器人取代吗?
数据驱动软件会根据陪审员的行为来选择法庭中的陪审员,以便让一方成为最喜欢的一方吗?是不是特别需要被算法贴上“摇滚”爱好者的标签(YouTube 告诉我之前我就知道了!)?

我认为,现在是时候反思这些话题,并了解是否有必要以某种方式和在某些情况下规范数据的使用。

需要 Python 和数据科学方面的内容来开始或促进你的职业生涯吗?下面是我的一些文章,可以帮到你:

蟒蛇:

数据科学:

考虑成为会员:你可以免费支持我和其他像我一样的作家。点击 这里成为会员。

关于 SQL DDL、SQL DML、一致性和性能指数的一些提示

原文:https://towardsdatascience.com/some-tips-on-sql-ddl-sql-dml-costintency-and-performance-index-1364230b8a70?source=collection_archive---------36-----------------------

开始掌握 SQL 并构建自己的数据模型

吉列尔莫·费拉在 Unsplash 上拍摄的照片

当有人想进入数据分析领域时,最大的错误之一是低估了 SQL 的能力,以及在您的数据专业职业生涯中掌握这一技能的重要性。在本文中,我将重点介绍一些能够真正帮助您提高对 SQL 的理解的技巧。这篇文章将分为以下几个主题:

  • SQL 数据定义语言
  • SQL 数据操作语言
  • 约束的一致性和索引的性能

如果你是 SQL 新手,建议你先从基础做起,再看这篇文章。在网上,有许多关于 SQL 基础知识的资源,但是如果你愿意,你可以通过阅读以下三篇文章来跟随我的 SQL 教程:

所以,如果你准备好了,让我们开始吧!

第一部分:SQL 数据定义语言

对于数据定义语言,我指的是当您想要创建、更改、删除或截断一个表时所必需的所有方面。换句话说,创建和修改表结构所需的一切。

首先,需要识别数字、整数、小数、文本、日期/时间等不同的数据类型。我将在本文中考虑的数据库管理系统是 Postgres,所以如果你想安装它,请查看本文。

关于 Postgres 中的数字数据类型,我们有一堆不同的数字类型,比如 SMALLINT、INTEGER、BIGINT、DECIMAL、Numeric、REAL、DOUBLE PRECISION、SMALLSERIAL、SERIAL 和 BIGSERIAL。现在你可能会问自己它们之间的区别是什么,以及如何才能理解最适合你的情况。

不要担心,这些数据类型很多都是相似的,现在我将向您展示。

SMALLINT、INTEGER、BIGINT 之间的唯一区别是可以存储的数字的给定范围。低于您可以为每种类型存储的数量的维度。

SMALLINT:-32768 到+32767

整数:-2147483648 到+2147483647

BIGINT:-9223372036854775808 到+9223372036854775807

因此,如果我需要存储一个大于 32K 或小于-32K 的数,我会更喜欢使用 INTEGER 或 BIGINT。

另一个有趣的类型是以下三种类型:

小型串行:1 到 32,767

序列号:1 至 2,147,483,647

大序列:1 到 9,223,372,036,854,775,807

正如你所注意到的,它们都没有负值,它们从 1 开始。关于序列类型的另一个重要考虑是,每次在表中添加一个新行时,Postgres 默认会通过递增上一个序列来自动添加下一个序列。

假设您有这三个表 test_1、test_2 和 test_3。

CREATE TABLE **test_1** (
"id" SMALLINT,
"Test_Number" SMALLINT,
"First_Name" VARCHAR(30),
"Last_Name" VARCHAR(30));CREATE TABLE **test_2** (
"id" INT,
"Test_Number" INT,
First_Name" VARCHAR(30),
"Last_Name" VARCHAR(30));CREATE TABLE **test_3** (
"id" SERIAL,
"Test_Number" INT,
"First_Name" VARCHAR(30),
"Last_Name" VARCHAR(30));

现在我想插入这些值:60000,'英里','戴维斯'。我们将逐步看到每个表的不同输出。对于第一个表:

INSERT INTO test_1 VALUES (1, 60000, ‘Miles’, ‘Davis’); 

出局:

ERROR: smallint out of range

正如您注意到的,我们收到了一个错误,因为我将 SMALLINT 设置为表 test_1 的数据类型。

相反,如果我们再次尝试,对于第二个表 test_2,输出将是正确的,因为列“Test_Number”的数据类型现在是 INT,其范围比以前更大。

INSERT INTO test_2 VALUES (1, 60000, ‘Miles’, ‘Davis’);

假设我们已经在表中插入了值(6000,' Miles '和' Davis '),然后我们想插入第二行的值(70000,' John '和' Coltrane '),并在默认情况下获得等于 1 和 2 的“id”。为了获得这个结果,我需要使用表 test_3,其中列“id”的数据类型是 SERIAL。

INSERT INTO test_3 (“test_number”,”first_name”,”last_name”) 
VALUES (60000, ‘Miles’, ‘Davis’), (70000, 'John', 'Coltrane');

输出:

作者图片

对于 VARCHAR 和 TEXT,您需要知道 VARCHAR 通常在您想要像前面的示例那样设置长度时使用,在前面的示例中,我将 first_name 和 last_name 的长度固定为等于 30 ,但是我也可以使用不对插入到列中的值设置特定长度的 TEXT 类型。无论如何,请记住,TEXT 相当于没有特定长度的 VARCHAR。

另一个有趣的数据类型是日期。在 Postgres 中,您应该知道有一些技术可以帮助您检查 Postgres 服务器的时区,以及更改时区。下面的两个命令可以帮助你做到这一点:

SHOW TIMEZONE;
SET TIMEZONE = "Europe/Rome"

要显示当前时间戳、日期或时间,您可以使用以下命令:

SELECT CURRENT_TIMESTAMP;SELECT CURRENT_DATE;SELECT CURRENT_TIME;

有时我们不想删除一个表,但我们只需要将值删除到表中,以便添加不同的值。因此,TRUNCATE 命令可以帮助我们。

TRUNCATE TABLE test_3 RESTART IDENTITY

您可以注意到,我添加了 RESTART IDENTITY,因为我希望表 test_3 的列“id”从 1 而不是从 3 重新开始。

在描述中添加一些注释可以帮助其他人更好地理解特定列的含义。所以:

COMMENT ON COLUMN test_3.”test_number” 
IS ‘describe the number of tests in one year’;

要查看注释,请使用以下命令:

**\d+ test_3**

出局:

作者图片

在上面的输出中,你可以在左边看到我的评论。

第二部分:SQL 数据操作语言

在这一部分中,我们将看到一些关于数据操作的提示,以便添加、修改和删除数据。

有时从另一个表中插入数据很有用,因此可以将数据从一个表迁移到另一个表。在前面的例子中,我创建了一个包含一些值的表 test_3。现在,我想将表 test_3 中“名字”和“姓氏”列的值转移到名为 test_4 的新表中。

CREATE TABLE test_4 (
first_name VARCHAR(30),
last_name VARCHAR(30));

所以这个表 test_4 是空的,我想插入 test_3 中的值。过程很简单:

INSERT INTO test_4 ("first_name", "last_name) 
SELECT “first_name”, ”last_name” FROM test_3;

出局:

作者图片

我想在这个新表中添加一列。所以:

ALTER TABLE test_4 ADD COLUMN “song” VARCHAR;

另一个有用的技能是能够随时更新您的表。为此,我可以使用更新。

UPDATE test_4 SET “song”=’Blue in Gree’ WHERE “last_name”=’Davis’;UPDATE test_4 SET “song”=’In A Sentimental Mood’ 
WHERE “last_name”=’Coltrane’;TABLE test_4;

出局:

作者图片

我想删除第二行,添加不同的值,并改变表格。所以:

DELETE FROM test_4 WHERE “last_name”=’Coltrane’;INSERT INTO test_4 VALUES (‘Duke', 'Ellington’, ‘In A Sentimental Mood’);ALTER TABLE test_4 ADD COLUMN "song_date" DATE;UPDATE test_4 SET "song_date"='1959-03-02' 
WHERE "last_name"='Davis';UPDATE test_4 SET "song_date"='1935-04-30' 
WHERE "last_name"='Ellington';INSERT INTO test_4 
VALUES ('Paul', 'Desmond','Take Five', '1959-07-01');

出局:

作者图片

现在我比较一个音程和另一个音程。比如我想知道哪首歌有 70 多年。但在此之前,最好检查一下当前日期和歌曲日期之间的差异是否给了我们一个时间间隔。所以:

SELECT **pg_typeof**(CURRENT_TIMESTAMP — “song_date”) FROM test_4;

出局:

作者图片

SELECT “song”,”song_date”, 
(CURRENT_TIMESTAMP — “song_date”) > INTERVAL ’70 years’ FROM test_4;

出局:

作者图片

你可以看到第二首歌是 70 多年前的。

第三部分:约束一致性和指标性能

一致性在关系数据库中非常重要,您需要知道一些约束条件,比如在一列中只添加正数,在一列中只包含唯一值,或者构建一个引用特定外键的主键。

第一个约束是独特的。这个约束允许我们在一列或一组列中固定唯一的值。

下面我将向你展示使用 UNIQUE 的不同方法。

--FIRST example--
CREATE TABLE table_name (
"id" SERIAL,
"full_name" VARCHAR(30) UNIQUE);--SECOND example---
CREATE TABLE table_name ( 
“id” SERIAL,
"full_name" VARCHAR(30),
UNIQUE ("full_name"));--THIRD example--
ALTER TABLE table_name ADD UNIQUE ("full_name")

在第三个示例中,您必须记住,如果表中没有重复的值或者表是空的,则可以以唯一的形式更改列。

一种特殊类型的唯一约束是主键。使用主键,我们可以确保特定的列将只获得唯一的值,而不是空值。

--FIRST example--
CREATE TABLE table_name_1 (
"id" SERIAL PRIMARY KEY,
"full_name" VARCHAR(30) UNIQUE);--SECOND example--
CREATE TABLE table_name_1 (
"id" SERIAL, 
"full_name" VARCHAR(30),
PRIMARY KEY ("id"),
UNIQUE ("full_name"));--THIRD example--
ALTER TABLE table_name_1 ADD PRIMARY KEY ("id")

另一个键是外键,它将一列中的值限制为出现在另一列中的值。下面是一些例子。

--FIRST example--
CREATE TABLE table_name_2 (
"id_2" INTEGER REFERENCES teble_name_1 ("id"),
"full_name" VARCHAR(30) UNIQUE);--SECOND example--
CREATE TABLE table_name_2 (
"id_2" INTEGER,
"full_name" VARCHAR(30),
FOREIGN KEY ("id_2") REFERENCES table_name_1 ("id"),
UNIQUE ("full_name"));--THIRD EXAMPLE--
ALTER TABLE table_name_2 ADD FOREIGN KEY ("id_2") 
REFERENCES table_name_1 ("id"); 

最后,我们可以看看 INDEX,了解它在某些情况下如何帮助我们减少查询的处理时间。

因此,要知道我们的查询需要多长时间才能产生输出,我们需要使用以下命令:

\timing on

下面列出了创建索引的不同方式:

CREATE INDEX ON table_name (“col_1”);
CREATE INDEX ON table_name (“col_1”, “col_2);
CREATE UNIQUE INDEX ON table_name ("col_1");

但是,您应该知道,有时 Postgres 更喜欢使用序列扫描,即使您已经设置了索引。发生这种情况是因为 Postgres 搜索最佳处理时间,在某些情况下,例如当记录的数量不是很大时,Postgres 可以选择顺序扫描,而不是您设置的索引。

结论

通过这篇文章,我认为您已经很好地了解了数据操作、数据定义、与约束的一致性以及如何使用索引。现在轮到您创建自己的数据模型,并尝试应用我们已经看到的步骤。祝你好运!

感谢你阅读这篇文章。您还可以通过其他方式与我保持联系并关注我的工作:

有人将是医学院的最后一名:人工智能在医疗保健中的案例

原文:https://towardsdatascience.com/somebody-will-be-last-at-medical-school-the-case-for-ai-in-healthcare-fe49f5ce6f8f?source=collection_archive---------31-----------------------

说到护理,人情味是不可替代的。在人工智能的帮助下,这可能会更频繁地发生。

图片由 Piqsels 提供

2017 年,疾病预防控制中心估计美国急诊室的平均等待时间约为 40 分钟,超过 2200 万次就诊需要患者等待一个多小时。虽然去急诊室从来都不好玩——长时间的等待、沮丧和疲惫成为常态——但问题的一部分源于这样一个事实,即并非所有情况都是真正的紧急情况。撇开全球疫情不谈,医疗保健只是需要大量的时间和资源。它还与相关人员的数量、质量和速度成比例。

2018 年,FDA 批准了首个人工智能增强的糖尿病视网膜病变诊断系统,这一举动可能是未来更大变化的前奏。由于机器软件中内置的智能和能力,该系统可以由任何操作员操作,无需专门且难以找到的专家。

将更多的人工智能嵌入机器对设备制造商来说非常有意义。将更多人工智能集成到他们的产品中所增强的分析能力和商业优势,使他们对寻求提高吞吐量、收入和改善患者体验的医疗保健行业具有巨大的吸引力。但这对我们所有患者也是有意义的,他们可以从更快、更便宜、更容易获得的诊断中受益。毫不奇怪,根据许多估计,人工智能将越来越多地被医疗保健所采用,特别是在诊断、治疗、患者家庭护理以及消耗资源和让医疗保健从业者从事单调乏味任务的管理活动方面。

例如,《国家癌症研究所杂志》的一项研究已经证明,人工智能驱动的乳腺癌检测可以与人类放射科医生竞争。这为扩大人工智能在更多疾病中的部署打开了大门,以实现更快、更准确的患者结果。

但这种人工智能驱动的效率提升不需要局限于医院。在疫情肆虐的世界,AI 克服了传统机器视觉的低效,还帮助压力下的制药商检测疫苗小瓶,确保小瓶中没有外来污染物(例如,箔片颗粒、灰尘),并且过程符合严格的监管标准。

类似地,深度学习(人工智能的一个分支,目前广泛用于取代工厂中的传统机器视觉应用)软件可以用于检查试管支架,以确保存在正确数量的试管,每个插槽都被填满,并最终防止人类操作员进行代价高昂的返工。最后,密封和瓶盖对齐可以通过人工智能软件自动检查,以确保产品保护,避免污染和浪费。

我的一个好朋友,一位医学博士,在我们波士顿大学攻读人工智能博士学位期间,我和他一起度过了许多不眠之夜,他曾经对我说,“听着,在医学院里,总会有人需要排在最后!”除了令人担忧的讽刺之外,这种说法为人工智能提供了一个强有力的理由。当涉及到我们的健康时,从药物的研究和制造到检查和治疗的管理,人们希望最好的机器、过程和产品到位。这与汽车等其他行业的情况没有什么不同,这些行业已经为自动驾驶汽车实施了非常强大和智能的人工智能。当我们的安全受到威胁时,我们希望使用最好的技术来完成任务。

在过去的几年里,人工智能取代医疗工作者的潜力不仅被夸大了,而且被误报了。无论是帮助制造质量最好、最安全的药物,还是减少我们在急诊室的等待时间,当我们需要所有可以获得的帮助时,人工智能都在这里推动我们的医疗保健工作。

当进入急诊室时,没有人能取代人类的智慧、热情和同情心。在人工智能的帮助下,人类可能最终有时间做到这一点。

本文原载 此处

每个数据科学家都应该知道:偏差-方差权衡推广是错误的

原文:https://towardsdatascience.com/something-every-data-scientist-should-know-but-probably-doesnt-the-bias-variance-trade-off-25d97a17329d?source=collection_archive---------15-----------------------

高级数据科学家的基础知识

一项突破性的相对较新的发现颠覆了传统统计学,并对数据科学从业者和统计顾问产生了相关影响

丹尼尔·瓦格斯Unsplash 上拍摄

介绍

数据科学是一个迷人的领域。首席执行官们对其承诺的对收入的影响很着迷,而从业者对创新的快速步伐很感兴趣。已经有太多的东西要知道了,而且似乎每年都有更多的东西要学。

这篇文章将注意力吸引到一个相对新颖的想法,这个想法可能对大多数数据科学家和少数统计学家来说是有争议的: 偏差-方差权衡一般化并不一般化,只适用于非常具体的场景事实上,在撰写本文时,对于几乎所有已知模型的具体现实场景,包括线性回归,偏差-方差权衡已经被经验证明是错误的!

显然,这对于有经验的深度学习实践者或那些热衷于跟踪相关文献扩展体的几千人来说并不奇怪。本文总结了十几篇最突出和“最近”(从 2018 年到 2020 年 12 月)的关于~70+ [2]的双重下降的研究论文。根据什么是总结,鉴于数百页的研究被总结,这篇文章掩盖了许多细节,并解释了足够多的主要观点,以便有一个高层次的理解。

这一突破性发现的术语是“双下降”现象,这个想法有几个名字:双下降、深度双下降、双下降现象/曲线/风险曲线。至少有两种类型的双重下降:基于模型的基于样本的。本文对前者的讨论比后者更深入,尽管后者在我对线性模型双重下降的讨论中有所涉及。

我写这篇文章是为了让更多的读者能够理解。为此,我将使用不同的术语重述相同的观点,以帮助我的听众熟悉术语。对于有相关背景的读者,跳过背景部分:)

背景

统计学家、数据科学家和深度学习实践者都知道偏倚-方差权衡的经典统计学概念:

斯坦福大学的 Trevor Hastie、Robert Tibshirani 和 Jerome Friedman 的基础教材《统计学习的要素》(Elements of Statistical Learning,简称“ESL”)中的偏差-方差图截图。

偏差-方差权衡意味着模型(即方程)“应该平衡欠拟合和过拟合:足够丰富以表达数据中的潜在结构,足够简单以避免拟合虚假模式”[1]。左图 2.11 是一个典型的偏差-方差图,说明了模型复杂度的增加对训练数据和测试数据预测误差的不同影响。

线性模型由黄色平面表示,显示了给定两个输入变量“受教育年限”和“资历”的“收入”预测。我们看到预测并不完美,正如红点和黄色平面之间的距离所证明的。来源:文中截图,《统计学习入门》(ISL)。

更复杂的模型预测的值更接近实际值,或者显示的错误分类情况更少。换句话说,该模型显示出较少的偏差。左图 2.4 是一个众所周知的可视化图,展示了一个基于两个变量预测收入的线性模型:受教育年限和资历。

模型的拟合度或预测数据集因变量的能力会随着模型复杂性的增加而增加。增加模型复杂性的示例包括添加更多项、非线性/多项式项(例如,x、x 等。)或用于多元线性回归的逐步/分段常数,增加神经网络的训练时期/训练时间的数量,或增加决策节点(“深度”)的数量,并增加决策树的每个决策节点处的叶子数量,以及在随机森林的情况下增加树的数量。

很容易看到这个线性模型完全适合一个数据集,即零偏差,这是一个不同红点集的收入的较差估计值。来源:文中截图,《统计学习入门》(ISL)。

方差是在函数的近似值(即模型或方程)中观察到多少变化的度量。当使用不同的训练数据集进行函数近似时,更灵活的模型通常会看到更大的可变性,因为它们更容易改变以适应不同的数据集。换句话说,方差是指当使用一个或多个不同的训练数据集[3,5]构建时,函数(将一组输入与一组输出相关联的方程)的变化量。

2018 年末:双下降现象

偏差-方差权衡是关于插值和概化之间关系的陈述。传统的统计知识表明,将模型复杂性增加到超过插值消失训练误差的程度,是过度拟合的一个方法,并且是一个泛化能力差的模型;这意味着它在不同的、不可见的数据集上表现不佳[3,4]。然而,机器学习社区的子集定期训练模型,以完美地拟合训练数据集,从而零训练错误,并且这些模型继续在看不见的测试数据上表现良好。这种经验上的矛盾激发了对神经网络“神秘”属性的狂热兴奋,并激发了对解释的需求[1a]。

从 2018 年 2 月开始,伯克林、米哈伊尔等人写了一系列文章寻求做到这一点[1a、1b、1c、1d]。2018 年 12 月[1a],贝尔金等人。艾尔。形式化了一个经验观察:偏差-方差权衡只在不同的情况下成立,并创造了术语“双重下降”来描述偏差-方差权衡不成立的现象。该主题系列论文中第一篇论文的图 1 有助于建立直觉:

Belkin 等人的图 1。艾尔。[1a]

这些贝尔金等人的作品。艾尔。刺激了几十个后续出版物,这些出版物在多个数据集和多个模型类型中正式证实了双重下降现象。

基础文本“ESL”(统计学习的要素)可能是向当今大多数从业者教授偏差-方差概念的最主要负责人,其作者之一特雷弗·哈斯蒂(Trevor Hastie)在 2019 年 3 月与他和他的合作者的结果证实了双重下降现象[4],最近几周(2020 年 12 月 7 日)进行了多次修订。重点是,这是令人兴奋的东西,有很多人关注新的发展。

它是如何工作的?

没有人知道。

完全理解深度神经网络中模型式双重下降背后的机制仍然是一个重要的开放性问题。然而,即使对于线性模型,也会出现模拟的模型式双重下降。最近的一系列理论著作分析了这一背景(Bartlett et al .(2019);Muthukumar 等人(2019 年);贝尔金等人(2019);梅&蒙塔纳里(2019);Hastie 等人(2019 年))。我们认为类似的机制可能在深度神经网络中起作用" (Nakkiran,Preetum,et al .,arxiv,2019 年 12 月,[8a])。

纳克兰等人。艾尔。[8a]假设双重下降的潜在解释。

  1. 被训练到内插阈值的模型,即具有与观测值 n 大约相同数量的参数 p 的模型,只产生一个完全符合训练数据的模型。这种插值模型对训练集中的噪声非常敏感,并且易受模型规格错误(即差的函数逼近)的影响。
  2. 过度参数化模型确保了许多插值模型。回顾一下,内插模型是实现接近零训练误差的模型。一个过度参数化的模型是:给定一个真实的模型给定一个真实的模型 Y=X+ϵ,我们可以尝试以下两个模型来使用x解释/预测y:

第二个模型参数化过度。在这个玩具示例中,过参数化模型的θ1 和θ2 可以有多个值,这可以产生多个精确预测 y 的插值模型。随机梯度下降能够找到最能“记忆”或“吸收”数据中噪声的模型,使其对新数据集具有鲁棒性。换句话说,具有比观察值更多的模型参数意味着存在多个特征子集,这将允许将过参数化模型拟合到训练数据和随机梯度下降。用统计学的说法,一个过参数化的模型,p>n 没有唯一的最小二乘目标也没有唯一的极小值

那么为什么会出现双下降呢?有一些假设,我在上面描述了两个,但是还没有人知道。

结论

双重下降是一种稳健的现象,它在广泛的神经网络体系结构、随机森林、集成方法甚至流行数据集和合成数据集的线性回归中得到了证明。

双下降可以简单地描述为观察两次下降的测试误差曲线。模型式双下降是一种测试误差曲线,它观察到随着模型容量/复杂性/灵活性的增加而出现的两次下降。样本双下降是一种测试误差曲线,随着训练数据集中观察次数的增加,观察到两次下降。

双重下降的情况可能发生在:

  1. 第一层固定权重的两层神经网络【1a】。也称为非线性参数模型,称为随机傅立叶特征(RFF)。演示用 0-1&平方损失函数用黑色&白色/彩色图像分类数据集: MNIST CIFAR-10SVHN,用 bootstrap 重采样,以及英文语音识别数据集, TIMIT
    使用和不使用正则化进行演示:SVHN 实验使用岭正则化实现插值附近的数值稳定性,MNIST 实验没有正则化【1a,9】。
  2. 具有 RReLU 激活和 SGD 的全连接神经网络,用于 ERM 优化【1a】 RReLU =随机化修正线性单元[9],SGD =随机梯度下降,ERM =经验风险优化。
  3. ResNet - 18 (在 100 万+图片 ImageNet 数据集上训练的 18 层深度的卷积神经网络,有 1K+对象类别)[8a]。

图片来源:Nakkiran et。艾尔。[8a]

4。随机森林 [1a]。图 11 来自贝尔金等人。艾尔。演示了随着“模型容量”的增加而增加的双下降,在这种情况下,这意味着增加单个林中的最大树数和林中每棵树的最大叶子数。**

资料来源:贝尔金、米哈伊尔等人

5.由多个随机森林模型组成的集合模型【1a】。下图 12 来自贝尔金等人。艾尔。[1a]显示了增加的“模型容量”,即增加集合中的森林数量和每个森林中的树木数量。

资料来源:贝尔金、米哈伊尔等人

以上 4 点是“模型式双重下降”的例子,基本上,增加模型容量/复杂性/灵活性可以证明传统的偏差-方差权衡,然后测试误差第二次下降。事实证明,这是另一个启示,更多的数据可能会损害模型的性能!这导致了样本双下降的以下情况:

6.线性模型。纳克兰等人。艾尔。显示[8b]对于过度参数化的模型,可能出现样本双下降。下面的图 1 来自 Nakkiran 等人。艾尔。[8b]显示了在[0,2000]个样本上训练了 1,000 个特征的多元线性模型的测试均方误差(MSE)。对于“Num ”,该模型参数化过度。样品”不到 1000 个。随着训练数据集的规模从 0 增加到 900 个观察值,我们看到测试 MSE 下降,正如预期的那样,因为“数据越多越好”然而,当训练集中的样本数量从大约 900 个观察值增加到 1000 个观察值时,测试 MSE 增加。什么?更多的数据导致更严重的样本外测试误差?! 然后,当训练数据集从 1,000 个观察值增加到约 1,100 个观察值时,测试误差再次急剧减小,然后恢复测试 MSE 的先前改进速度。两点:1。在过度参数化的模型中,更多的数据是有害的,2。当改变训练数据集大小时,观察到测试误差的双重下降。****

这是一个很好的结果,因为这是一个非常简单的实验,使用的是大多数人都熟悉的模型,而且应该很容易复制(如果获得 1K 的掌声,我会尝试分享我的代码:)。

这个情节有两个重要的启示。(1)随着训练集中样本数量的增加,测试 MSE 实际上会变得更差,如[~900,1,000]所示。这非常令人惊讶。(2)随着数据的增加,测试误差表现出预期的下降,在某一点达到峰值,然后又惊人地下降,提供了双重下降现象的另一个例子,具体地说是“样本双重下降”图片来源:Nakkiran et。艾尔。[8b]

是的,我的头也疼。但是相当令人兴奋和兴奋的东西,对不对?

关于作者

安德鲁·杨是 Neustar 的 R&D 数据科学家经理。例如,Neustar 是一家信息服务公司,从航空、银行、政府、营销、社交媒体和电信等领域的数百家公司获取结构化和非结构化的文本和图片数据。Neustar 将这些数据成分结合起来,然后向企业客户出售具有附加值的成品“菜肴”,用于咨询、网络安全、欺诈检测和营销等目的。在这种情况下,Young 先生是 R&D 一个小型数据科学团队的实践首席架构师,负责构建、优化和维护一个为所有产品和服务提供信息的系统,该系统为 Neustar 带来了超过 10 亿美元的年收入(在 LinkedIn 上关注他,了解数据科学的最新趋势!)

更多热门文章:

参考

[1a] Belkin,Mikhail 等人,“调和现代机器学习实践和偏差-方差权衡” arXiv 预印本 arXiv:1812.11118 (2018)。https://arxiv.org/abs/1812.11118

[1b]贝尔金、米哈伊尔、马思远和苏米克·曼达尔。"为了理解深度学习,我们需要理解核心学习." arXiv 预印本 arXiv:1802.01396 (2018)。https://arxiv.org/abs/1802.01396

[1c]贝尔金、米哈伊尔、亚历山大·拉赫林和亚历山大·b·齐巴科夫。“数据插值与统计最优性相矛盾吗?."第 22 届人工智能与统计国际会议。PMLR,2019。https://arxiv.org/abs/1806.09471**

[1d]贝尔金、米哈伊尔、丹尼尔·许和许骥。"弱特征的两种双重下降模型." SIAM 数据科学数学杂志2.4(2020):1167–1180。https://arxiv.org/abs/1903.07571

[2]邓、泽玉、阿布拉·卡姆蒙和克里斯特斯·索兰普利迪斯。“高维二元线性分类的双下降模型。” arXiv 预印本 arXiv:1911.05822 (2019)。https://arxiv.org/abs/1911.05822

[3]哈斯蒂、特雷弗、罗伯特·蒂布拉尼和 J. H .弗里德曼。统计学习的要素:数据挖掘、推理和预测。第二版。纽约:斯普林格,2009 年。打印。https://web.stanford.edu/~hastie/ElemStatLearn/

[4] Hastie,Trevor 等,“高维无脊最小二乘插值中的惊喜” arXiv 预印本 arXiv:1903.08560 (2019)。https://arxiv.org/abs/1903.08560

[5]詹姆斯、加雷思、丹妮拉·威滕、特雷弗·哈斯蒂和罗伯特·蒂布拉尼。统计学习介绍:在 R 中的应用。, 2017.打印。https://statlearning.com/

[6]汤姆·米切尔。卡内基梅隆大学。机器学习讲义。https://www . cs . CMU . edu/~ guest rin/Class/10701-S05/slides/NNet-cross validation-2-2-2005 . pdf

[7] Muthukumar,Vidya 等,“回归中噪声数据的无害插值”信息理论选定领域 IEEE 期刊 (2020)。https://arxiv.org/abs/1903.09139

[8a] Nakkiran,Preetum 等人,“深度双重下降:更大的模型和更多的数据带来的伤害” arXiv 预印本 arXiv:1912.02292 (2019)。https://arxiv.org/abs/1912.02292

[8b] Nakkiran,Preetum。"更多的数据会损害线性回归:样本双下降." arXiv 预印本 arXiv:1912.07242 (2019)。https://arxiv.org/abs/1912.07242

[9]徐,冰,等.“卷积网络中校正激活的经验评估” arXiv 预印本 arXiv:1505.00853 (2015)。https://arxiv.org/pdf/1505.00853.pdf

进一步阅读

其他人对此现象感到震惊(我只是浏览了他们的帖子,但阅读其他观点可能会有所帮助)

https://news.ycombinator.com/item?id=21730020

[11]https://www . less wrong . com/posts/frv 7 ryoqtvsuqbxut/understanding-deep-double-descent

[12]https://medium . com/@ lighton io/beyond-over fitting-and-beyond-silicon-the-double-descent-curve-18b6d 9810 e 1b

[13]https://medium . com/@ LightOnIO/recover-the-double-descent-curve-with-an-opu-21df 319142 aa

密码

[14]https://github.com/lightonai/double-descent-curve

发音:通过声音讲述数据故事

原文:https://towardsdatascience.com/sonification-telling-data-stories-through-sound-fbd1f98d0071?source=collection_archive---------23-----------------------

4 个独特的例子展示了音乐和声音在数据科学中的应用

Unsplash 上由Sao tusar拍摄的照片

当您考虑通过数据讲述一个故事时,例如回答一个常见的问题或突然出现在您脑海中的事情,或者从分析的角度解决一个问题,您可能会转向 matplotlib 和 seaborn 等软件包进行可视化。通常,条形图和散点图是直观检测有趣模式的好方法,也是开始新项目或数据集的最佳工具之一。但是声音呢?声音可以用来传达关于数据的故事吗?它可以揭示我们用视觉图表看不到的关系吗?最近,我一直在探索这个话题,在这个故事中,我想与你分享一些最好的例子。

响亮的数字播客:

【链接:】https://www.loudnumbers.net/

这个播客由 Miriam Quick 和 Duncan Geere 主持,讨论了他们共同创建的数据发音项目。通常情况下,剧集是围绕他们创作的特定声音/作品组织的,从艺术和节目两方面解释其背后的过程。他们在每集的结尾播放这首曲子,一旦听众知道每个声音代表什么,他们就能从中获得更多。例如:

  • 《穷途末路》,8 月 2 日出版

在这一集里,他们讨论了人类活动对昆虫数量的影响,通过对一位科学家的故事进行声音处理,这位科学家在他每年的研究旅行中跟踪了击中他挡风玻璃的昆虫数量。米丽娅姆和邓肯解释了较大和较小的音符是如何代表较小和较大的昆虫的,以及每年是如何被庄严和黑暗的锣声所标志的。如果你听这首歌,你会听到这些声音逐渐减弱,直到消失。

我认为以音频格式呈现数据可能比视觉图形更能吸引观众的情绪。音乐本身是情绪化的,听音乐的同时记住它是由昆虫、法律或酒精类型的真实数据集组成的(所有这些都是播客片段),这是一种独特的体验。因此,用它来尝试讲述一个故事对气候变化等原因有着巨大的影响,超出了视觉图表的一般方法。

作者通常还会回顾他们是如何创作歌曲的,从数据集到音乐,正如他们所说,这个过程相当简单。它主要涉及清理数据的 Google Sheets,以及处理声音的基于 Ruby 的编码环境 SonicPi。

《纽约时报》:“冠状病毒平息了城市噪音。听听剩下的。”

链接:https://www . nytimes . com/interactive/2020/05/22/upshot/coronavirus-quiet-city-noise . html

这篇文章讲述了在新冠肺炎疫情期间采取的措施对城市噪音水平的影响。美好的视觉化,也包括两个典型的城市噪音在同一天,相隔一年的音轨。这些效果增加了对图表的额外理解,有趣的是看到一个关于声音的时间图,并辅以它所代表的城市的实际声音。这种声音将图表与真实的城市联系起来,使它对阅读它的人来说更加真实。我鼓励你看看上面的链接!

《纽约时报》:“一秒九发子弹:拉斯韦加斯

枪手如何装备步枪以更快开火”

链接:https://www . nytimes . com/interactive/2017/10/02/us/Vegas-guns . html

这是《纽约时报》的另一篇文章,它使用声音来交流数据信息。这篇特别的文章着眼于 2017 年拉斯维加斯枪击案,并描述了枪手如何改变他的武器,使其像自动武器一样工作。文章开头的视觉效果比较了三种不同武器发射子弹的声音。点击后,你可以听到“哔哔声”的每一枪发射,你可以比较不同类型的武器。

这种伴随着声音的视觉效果是情感力量的一个例子,加入这些声音效果可以给故事带来情感力量。虽然选择的效果听起来不像火器开火,但听起来仍然很震撼,比看图表本身更震撼。

NASA:数据发音:一种新的声音宇宙三重奏

链接:https://www . NASA . gov/mission _ pages/Chandra/news/data-sonification-a-new-cosmic-triad-of-sound . html

在 NASA 的这篇文章中,你可以听到当一条垂直线从左到右穿过时宇宙物体发出的声音。对于每个星系,你可以听到更尖锐、几乎闪烁的声音,而对于图片中间的蓝色和红色气体,你可以听到更深沉、更响亮的声音。这个项目暗示了另一种方式,即发音可以提高我们理解数据的方式,即通过帮助我们听模式,我们可能无法观察到,而只看数据。

所以,你有它!这些是一些关于数据声音化的例子,以及它们在塑造我们对数据的理解以及通过唤起人们的情感来帮助人们更多地关注重要原因方面有多么重要。

感谢您的阅读!

抱歉,数据湖不是“遗产”

原文:https://towardsdatascience.com/sorry-data-lakes-are-not-legacy-625bc70b4090?source=collection_archive---------19-----------------------

图片作者:托马斯·斯派塞

为什么“数据湖已死”的说法对现代数据架构对话没有什么帮助

去年在一篇关于数据湖的帖子中,我们讨论了围绕湖架构、策略和分析的各种 FUD 。快进一年;数据湖现在似乎被明确地认为是一种“遗留”数据架构。

在 2020 年现代数据堆栈会议期间,他们表示数据湖在现代数据架构中没有一席之地:

“在我看来,数据湖不是现代数据堆栈的一部分。弗雷泽说:“数据湖是遗产。“人们采用数据湖有组织[和]准政治原因。但采用数据湖不再有技术上的原因。”

事实上,这是有争议的,可能是一种挑衅的企图。然而,这并不是第一次将数据湖视为过时的数据架构模型。

公平地说,像“数据湖是遗产”这样的豪言壮语在行业中使用得太频繁了。例如,您是否知道 Dremio 数据湖正处于使数据仓库过时的边缘,至少根据文章“dre mio 刚刚使数据仓库过时了吗?”。

如果您正试图用现代数据架构绘制一条前进的道路,这些类型的语句会造成不必要的混淆。作为一个寻求为你的团队和公司开发解决方案的人,过滤噪音可能是一个挑战。

在这篇文章中,我们将从另一个角度来看待数据湖不是遗留问题。我们将把数据仓库过时的话题留到以后再说。

为什么供应商声称数据湖是“遗留物”

声称数据湖不应该是现代数据堆栈的一部分的理由是什么?有几个主题;

  1. 计算和存储:数据湖在架构上和技术上都存在缺陷,因为数据仓库产品的进步提供了一个独立的“计算和存储”解决方案
  2. 技术债务:团队继承了数据湖,或者出于政治原因而非技术原因创建了数据湖。它们是由“组织高层的某个人”创造的,他们拥抱了市场体系,发明了一个带有数据湖的图表,并宣布胜利
  3. 成本:从历史上看,数据湖因成本较低而被采用,但这种成本效益已不复存在

我们将反驳每一个错误的主题。

1.计算和存储

在 2020 年数据堆栈大会上,有人提出现代数据架构应该利用“通过独立的计算和存储解决方案实现大规模并行处理和列存储技术。”这个没什么争议。计算和存储的分离带来了多种运营、技术和财务优势。

不幸的是,计算和存储被框定为失败的数据湖,这是不准确的。

为了说明这一点,我们需要深入了解计算和存储概念。一些重要的区别,特别是考虑到像计算和存储这样的技术概念,通常表现为定价策略。

计算

按需计算活动(如查询引擎)允许数据计算能力在需求增加时扩大,在需求减少时缩小。计算服务类似于按需查询引擎,并为数据湖提供主要的 SQL 查询功能。

与传统的数据仓库系统不同,计算资源可以独立扩展。例如,对人 X 的查询不会对人 y 的查询产生实质性影响。

按需计算显著减少了闲置的系统资源。大多数供应商的定价模型都将计算成本与使用紧密联系在一起。这意味着您只需为活跃的计算操作付费,在高效使用时可带来有利的成本经济性。为什么高效?不幸的是,计算成本往往变化很大,这使得预测预算极具挑战性。

您的成本将取决于用户执行的操作的类型、数量、频率和复杂性。在编写 SQL 查询时,人 X 的效率可能比人 Y 低得多,这意味着对于类似的结果,成本可能要高得多。

储存;储备

存储是数据以特定格式组织(或不组织)、持久、持久、可访问和驻留的地方。

存储成本通常是固定的倍数,具有高度可预测性,并且可以根据需要进行扩展。例如,AWS 和谷歌云存储标准定价为每月每 GB 0.023 美元。

1 TB 的成本约为 23 美元。对于 10 TB,成本约为 230 美元。一些其他因素会影响存储成本,但这里的想法是,成本允许团队轻松预测一段时间内的成本。

最重要的一点是,独立的计算和存储不是 lake 的缺陷,而是其主要的架构和战略优势之一。

数据湖技术栈中的紧耦合与松耦合

有人在会议上说,计算和存储的分离是数据仓库优于数据湖的一个关键优势。

“将计算与存储分离的数据仓库具有数据湖的所有优势,甚至更多”

从技术角度来看,计算和存储是松散耦合的体系结构。因此,这对于仓库来说是一个好处。然而,受益的不仅仅是仓库。

从设计上来说,任何现代数据架构都依赖于松散耦合的计算和存储分离来提供高效、可扩展和灵活的解决方案。与数据湖相比,数据仓库供应商正在引入独立的计算和存储这一事实并不算创新;它正在实现与数据湖的对等。

仓库中独立计算和存储的发展使它们符合生产性数据湖通过按需 SQL 查询服务所采用的架构。

在一篇名为的文章中——何时采用数据湖——何时不采用,对数据湖的挖苦是它们不能轻松或按需扩展计算;

一些解决方案架构师提出了数据湖,以在传统数据仓库中“将计算与存储分离”。但是他们忽略了一点:您希望能够轻松地按需扩展计算。数据湖不会给你这个;您需要的是一个数据仓库,它可以在您需要时随时提供和暂停容量。

数据湖不会扩展计算资源,因为没有可扩展的计算资源。作为对数据湖的批评,这是一个失误。那么,如何查询数据湖呢?数据湖依赖于良好抽象的计算(即查询服务),像亚马逊红移光谱、亚马逊雅典娜、脸书普雷斯托、阿哈纳等。

根据设计,这些查询服务按需扩展计算资源,以查询数据湖的内容。因此,lake 不需要计算服务,因为它在一个松散耦合的联邦模型中运行,其中计算(查询)服务附属于 lake。

那么,紧密耦合和松散耦合的计算和存储之间有什么区别呢?

松耦合和紧耦合—技术对产品对定价

BigQuery 和 Snowflake 是会议期间提到的两个产品,它们被强调为数据仓库领域的领导者,以分离计算和存储。

当您将数据推送到 BigQuery 时,数据驻留在 BigQuery 存储系统中。当数据驻留在雪花存储系统中时,该数据被绑定到雪花。因此,BigQuery 和 Snowflake 中的静态数据(通常)绑定到各自的存储系统。在这些情况下,计算和存储范式在产品环境中紧密耦合。

虽然在每个产品中,计算和存储内部在技术上是分离的,但实际的最终状态在产品上下文中是相互绑定的。这与 Oracle 或 MySQL 没有什么不同,在 Oracle 或 MySQL 中,计算和存储在每个供应商的产品中都有内在的联系。由于计算和存储在每个供应商的产品中都有内在的联系,因此雪花不能直接查询 BigQuery 本机存储内容,就像 BigQuery 不能查询本机雪花存储一样。

图片作者:托马斯·斯派塞

虽然产品可能在产品级别紧密耦合,但 BigQuery 和 Snowflake 体现了独立计算和存储作为定价创新的优势。

因此,紧密耦合的计算和存储产品模型反映在松散耦合的定价模型中。

现代数据湖体系结构期望计算资源由外部 SQL 查询服务提供。下图强调了良好抽象的数据湖如何独立地服务于不同的产品计算服务。Presto、Athena、Snowflake 和 BigQuery 都可以作为数据湖的计算层:

图片作者:托马斯·斯派塞

很容易想象不同的品牌、部门或合作伙伴组织如何在更复杂的组织中利用核心数据湖基础设施上的首选查询服务。

当我们提到 Google BigQuerySnowflake 通常在本地存储系统上工作时,它们提供独立于那些内部存储系统的计算服务。

例如,雪花允许你通过外部表查询你的数据湖。Google 也支持同样的概念查询外部数据源

Google 通过对 BigQuery Omni 的大量投资进一步扩展了这种良好抽象的查询模型。AWS 通过频谱按需计算查询服务类似地扩展了红移。

这些混合模式反映了对灵活性的需求。扩展查询服务不是支持“遗留”数据湖的功能,但供应商认识到他们在复杂的生态系统中运营,面临着来自数据科学、分析、运营和报告领域快速创新和转型的持续压力。

2.技术债务:架构

团队继承大量技术债务并不罕见,尽管口头(或未言明的)目标是尽可能少地承担债务。

根据一般经验,您越是选择特定于供应商的技术和解决方案,您招致额外技术债务的风险就越大。这可能是一个可以接受的妥协。

团队经常用短期速度换取长期成本。假设管理层随机地强迫一个数据湖“市场体系结构”,招致债务的风险增加。然而,管理也可以这样说,“嘿,你必须使用雪花,因为我的朋友杰德说它很棒”或“我在 BigQuery 上看到一篇博客帖子是有史以来最好的东西,我们正在使用它。”我们都知道发生这种事情的案例。

尽管有相反的说法,数据湖本质上并不比仓库承载更多的技术债务。做得好的数据湖可以通过用同类最佳的解决方案恰当地抽象数据架构的各个方面,帮助降低招致技术债务的风险。例如,数据湖可以提供采用不同计算服务的速度和灵活性,最大限度地降低供应商锁定风险,并降低转换成本。

例如,在 2020 年现代数据栈会议上,有人指出,人们不应该担心数据仓库空间中的供应商锁定。为什么?如果你依赖像 Fivetran 这样的供应商,它将降低仓库锁定的风险。奇怪的是,这种对供应商锁定的回答实际上是在促进供应商锁定。如果您需要从使用 Fivetran 转向使用其他产品,该怎么办?

显然,供应商在让你在操作上和技术上被锁定在一个产品上有既得利益。作为一种商业策略,这是可以的,但对于任何试图创建松散耦合、良好抽象的现代数据架构的公司来说,这肯定不是一个强有力的反驳。

在某些情况下,供应商锁定和由此产生的技术债务可能是一种可行的折衷方案。然而,考虑到行业的变化速度,这种妥协的好处可能是短暂的,很快就会消失。

例如,Athena 和 Presto 提供了联邦查询,为跨关系、非关系、对象和定制数据源中存储的数据运行 SQL 查询提供了按需计算服务。联邦查询反映了一种分布式、松散耦合的方法,这种方法在加速数据消耗的同时最大限度地减少了技术负担。

如果可以通过 Presto、Athena 或 Redshift 直接就地查询数据,为什么要将数据从 MySQL 移动到 Snowflake?当然,Fivetran 将数据从 MySQL 转移到 Snowflake 的服务需求已经消除或显著减少。

下面是 PrestoDB 如何描述联邦查询允许用户做什么:

Presto 允许查询数据所在的位置,包括 Hive、Cassandra、关系数据库甚至专有数据存储。一个 Presto 查询可以组合来自多个来源的数据,允许跨整个组织进行分析。

Ahana 的首席执行官史蒂夫·米回应了这种观点,“Presto 是开放分析栈的一部分——开源、开放格式、开放接口、开放云。”

使用联邦查询服务最小化 ELT 和 ETL 的想法反映了一个良好抽象的计算服务层。AWS、Google、Microsoft 和许多其他公司正在迅速地在他们的产品中采用分布式查询引擎模型。这是每个人的正确方法吗,可能不是。

鉴于数据仓库、查询引擎和数据分析市场不断加速的变化,最小化风险、锁定和技术债务应该是严肃的数据架构策略的核心部分。

3.成本:技术架构还是定价模型?

成本这个话题可能很棘手。通常,会围绕总拥有成本进行分析。例如,与产品相关的成本是什么?运营?人?培训?

为了简单起见,我们将围绕计算和存储缩小供应商产品和服务的成本。

每个供应商都有不同的计算资源定价策略。Google、AWS、Snowflake 的主要卖点之一是承诺按需服务、无前期成本和低运营费用。

虽然这是典型的情况,但计算定价的另一面是很难预测。您现在知道您当前和未来的状态用例将如何影响 6 个月、12 个月或 24 个月之后的成本吗?因此,计算机定价的可变性会导致意外的成本。这些成本常常成为与首席财务官进行重大运营和财务对话的目标。

计算成本随时间增长。图片作者:托马斯·斯派塞

大查询和雪花

在会议期间,雪花和谷歌 BigQuery 都被认为是这种新的可变定价模式的领导者。这两种服务都提供了将计算和存储分开的定价模式。例如,典型的 Google BigQuery 的存储成本约为每 GB 0.020 美元,查询中扫描的每 TB 数据成本为 5.00 美元。

在计算方面,雪花成本比 BigQuery 稍微复杂一些;随着雪花仓库规模的增加(计算资源的规模越大),成本的增加就越多。下面我们来看看雪花和亚马逊雅典娜的用例。

成本:雪花示例

以下是雪花的计算和存储定价方案。在我们的假设场景中,我们将从雪花的最小仓库大小开始:X-Small。随着规模的扩大,仓库的成本也会增加。我们将在雪花中拥有大约 1 TB 的数据。

假设我们每周七天,每小时运行 ETL 过程 10 分钟。从周一到周五,您还可以让分析师每天 3 小时从 Tableau 连接到 Snowflake。计算成本大约为每月 620 美元,而存储成本不到 100 美元。如果您的用例将 ETL 时间从 10 分钟变为 20 分钟,那么您的计算成本是 862 美元,而您的存储成本通常保持不变。

现在,假设您的处理工作负载发生了变化,并且您注意到了 X-Small 的性能瓶颈。考虑到工作负载,您决定从 X-Small 扩展到 Medium。每月 10 分钟 ETL 过程的计算成本从 620 美元到 1496 美元不等,20 分钟 ETL 过程的计算成本从 862 美元到 2470 美元不等。

在本例中,计算成本增加了,而存储成本保持不变。到目前为止,一切顺利。这无疑是定价上的创新。按需购买资源,无需添加或更改存储容量,这提供了灵活性并降低了操作复杂性。

那么有什么问题呢?由于计算和存储紧密结合,您的体系结构会受到供应商定价模式的限制。如果 Snowflake 增加了额外服务的成本,那么被不断上涨的成本“套牢”的可能性是一个真正的头等财务考虑因素。

在某个时候,比如说几年后,雪花决定它必须专注于扩展产品以满足股东对实现盈利的期望。不是不合理的期望。然而,这种新的盈利动力将如何影响定价?

让我们说,在推动盈利能力之前,你的每月雪花运行率是 2000 美元。雪花改变了你用例的成本基础,增加了 20%。因此,在一年的时间里,雪花将因计算服务获得额外的 5000 美元收入。

这里的要点是,从长远来看,定价创新并不总是有利于客户。易于实施和前期简单性必然会对总拥有成本产生影响。

成本:亚马逊雅典娜示例

Amazon Athena 为数据湖提供按需查询服务。雅典娜的定价?扫描每 TB 数据的费用为 5.00 美元。

如果平均每天运行 100 个查询,每个查询扫描 25 GB 的数据将花费大约 370.00 美元。(每月 3,042 次查询 x 0.0244140625 TB x 5.00 美元= 371.34 美元)。

在一些案例中,我们看到 Athena 与 Tableau Hyper 等工具结合使用,提供了极具吸引力的性价比价值主张。Tableau Hyper 使用一种内存数据引擎技术支持来自湖泊的数据,该技术旨在对大型或复杂数据集进行快速数据摄取和分析查询处理

使用 Hyper 可以确保 Athena Tableau 的查询数量显著减少。因此,减少了 Athena 收取的相关 Tableau 查询的成本。

Athena/Tableau 配对中详述的相同好处也适用于雪花或 BigQuery。

这里的要点是,现代数据体系结构应该从探索模式、试验、定义优先级和基于合理标准做出客观决策开始。

拥抱现代数据湖设计模式

麦肯锡对数据湖做了如下描述:

“…lakes 不仅确保了技术堆栈的灵活性,还确保了业务能力的灵活性。”

“数据”市场的加速变化并没有减弱。技术、运营和定价方面的快速创新需要合理的数据架构。因此,对现代数据架构 的任何合理评估都必须 考虑每个选择的影响(积极或消极),包括使用或不使用数据湖。

数据湖并不是所有用例的正确解决方案。对于某种类型的团队来说,缺少紧密耦合的计算资源可能是一个真实、实际的考虑因素。像 BigQuery 或 Snowflake 这样的解决方案提供了操作和技术上的简单性。幸运的是,这些产品还支持松耦合的查询服务。如果团队发现自己将来需要利用数据湖,这种折衷可能是有用的。

现代数据架构中 lake 的目的是提供一个重视灵活性、开放标准、最小化供应商锁定和服务联合的解决方案。不管您喜欢的架构或平台是什么,花时间定义一个架构来权衡每个选择的利弊是值得的。

抱歉,学数据科学的第一年,你无法脱颖而出

原文:https://towardsdatascience.com/sorry-you-cant-stand-out-in-your-first-year-of-learning-data-science-6889af48150f?source=collection_archive---------19-----------------------

意见

这没关系。以下是最终脱颖而出的方法。

做黄色郁金香就可以了。你最终会成为红色的那个。(鲁珀特·布里顿在 Unsplash 上拍照)

你是否觉得,这些天来,每个其他学生都想进入数据科学领域?

LinkedIn 上的每个人看起来都在学习同样的吴恩达机器学习课程吗?

在数据科学爱好者的海洋中,你被招募为数据科学家的希望破灭了吗?

如果你点了头,不要担心。你并不孤单。有时,我的学员会问我同样的问题:“如果其他人都遵循你的道路并取得成功,那么我如何才能脱颖而出?”

听着,给谎言裹上糖衣卖给你我什么都得不到。你可能会为这篇文章鼓掌,但我不能接受。我想说实话,即使这会变得很苛刻,但会给你一个正确的心态。

你还在吗?很好。别告诉我我没警告过你。

本文旨在详细说明如何在数据科学中脱颖而出,但简短的回答是:你不可能很早就脱颖而出,也没人指望你能做到。如果你在游戏中呆的时间足够长,你就会赢,因为其他人都会退出。

让我解释一下。

人们并不指望你早早脱颖而出

2017 年,开始学数据科学的时候,没人认识我。

我在宿舍里浏览 Coursera 和 YouTube 视频上的一堆课程。我会狂看吴恩达和每一个谈论成为机器学习工程师所需技能的 YouTuber。

那是我的学习阶段。我最不想做的事就是脱颖而出。

我宣扬进入数据科学的 3 步过程。学习、创造、分享。直到我开始创作和分享数据科学内容,人们才开始注意到我。我继续前进,得到了多份工作邀请,自由职业的机会和一群忠实的读者来阅读我写的东西。

现在我不打算吹嘘这些了。我只是透明的给你看过程和结果。当你开始学习时,没有人会注意到你。没人在乎你是在读吴恩达大学还是在读 T2 大学的纳米学位。没事。

没有招聘人员会寻找出类拔萃的数据科学学习者。他们希望学习者学习所有的基础知识,并有在现实世界项目中工作的经验。因此,让我重新措辞来回答你的问题。即使你是一名出色的数据科学学习者,也没人会找你。

这没关系。请相信我。

它适用于生活中的任何事情。想想吧。

这个原则并不是数据科学特有的。我在人生的各个阶段都经历过。

当我开始在媒体上写作时,我没有观众。我只是在公共场合练习,随着写得越来越多,我的技巧也在不断提高。当我在 LinkedIn 上发帖时,除了我亲爱的朋友,最初没有人注意到。这些天我开始自由写作——我的客户不多。很正常。

我想不出我生命中有哪一章让我瞬间脱颖而出。可以吗?我想是的。那你为什么会期望这么早就在数据科学领域脱颖而出呢?

尼古拉斯·科尔写了一篇优美的文章,概述了第一年是如何的艰难,列举了他的学校、工作和个人生活的各个方面。我非常同意。

学习数据科学的第一年将是最艰难的一年。我遇到过无数 6 个月内退出数据科学,回到软件工程的爱好者。

我没意见。

不是每个人都必须成为数据科学家。

但是如果你真的想要,那就继续学习,不断成长。高层的竞争很少。请也相信我。

如何最终脱颖而出成为一名初级数据科学家

首先,我们总结一下目前为止我们所了解的内容。

  • 当你学习数据科学时,没人指望你会出类拔萃。
  • 只有当你在数据科学领域创造和分享时,你才会被注意到。
  • 你不断学习所有的基础知识,每天都在提高你的技能。
  • 许多爱好者在一年内退出,从长远来看,你很少有竞争。

如果你理解了以上内容,掌握了所有的基本技能,那么现在你就处于脱颖而出的有利位置。我可以一开始就告诉你,但是你会在掌握所有基础知识之前就匆忙去实现它。

脱颖而出的唯一方法就是做别人不愿意做的事情。这是一个简单的想法,但也是最难实现的。它消耗了大量的时间,但仍然永远有回报。

我工作中的第一个项目是从 Yelp 和猫途鹰等多个网站搜集餐馆相关数据。我清理了所有杂乱的数据,用有用的可视化工具构建了一个 web 应用程序,并把它提交给了我的 CEO。我不需要告诉你首席执行官是否被打动了——你很聪明。

我的一个朋友去了超市,录下了货架上杂货的片段。手动给每张图片贴上标签,并建立了一个系统,在商品即将脱销时自动发出警报。当他申请工作时,他激活了这个系统。我不需要告诉你他骗了多少人——你很聪明。

我可以举很多例子,但你是个聪明人。你明白了。投入精力和时间去做别人不愿意做的事情(只有在你建立了基本技能之后。)以下是你可以做的一些想法:

最后,我不可能什么都教你;脱颖而出的一部分是提出自己的想法。我祝你一切顺利。

要获得更多关于进入数据科学、真实体验和学习的有用见解,请考虑 加入我的电子邮件好友私人列表

抱歉,你很优秀,但是我们把工作给了别人

原文:https://towardsdatascience.com/sorry-you-were-excellent-but-we-gave-the-job-to-someone-else-d0c47ef3547b?source=collection_archive---------33-----------------------

在当前的经济形势下,满分不再能让你得到一份数据科学的工作。原因如下。

Unsplash

我已经积极找工作大约 3 个月了,经过 8 次最终面试、20 多次技术面试和几十次首轮面试,我逐渐意识到当前就业市场的一个奇怪现象:在最终面试中表现出色并不能保证获得数据科学工作。

作为背景,我在加州湾区有大约两年的数据科学家工作经验。我在分析、A/B 测试实验、建模和客户沟通方面有丰富的经验。这些经历引起了招聘人员的足够兴趣,他们回复了我的工作申请,并安排了技术面试。

而这些技术访谈相对保持不变(SQL、ML、产品问题、带回家的东西等。),颠倒过来的是挡在你和工作机会之间的最后面试。

让我解释一下。

在我对湾区一家生物技术公司的最后一次面试中,我被告知我是数据科学家职位的 7 名最终候选人之一。这是一个惊人的 14%的成功率,这是闻所未闻的。两年前,当我第一次申请数据科学职位时,最后一轮意味着你或其他人排队等候。不是八个申请人都等着在最后一轮竞争同一个位置。

这意味着,在 5 个小时紧张、充满压力的提问中,哪怕是一丁点儿的错误都可能导致别人偷走你的提议。我曾经在期末考试中遇到过这种情况,当时我用 3 步而不是 2 步解决了一个 SQL 问题,这意味着我被拒绝了。那是一次疯狂的经历。

即使你完美地完成了最后面试的技术部分,你仍然可能得不到那份工作。这是一封来自招聘人员的邮件,在我问了拒绝的原因后,他回复了我:

嗨,哈比昌,

你在面试中的技术部分确实令人印象深刻。团队认为你在沟通方面非常出色,非常了解项目经理需要什么以及如何解决这些棘手问题。不前进的主要原因与我们公司的现状有关。我们认为,在正确的指导下,您可以成为数据科学团队的重要资产,但目前我们没有提供这种指导的方法。这更多的是适合度和你面试表现的问题。

这实际上是我第三次收到这种信息。三家不同的公司对我最后一次面试的回应是,虽然我在技术上有能力,但也有同样有能力、有更多相关经验的人(“更匹配的候选人”)。这些经历使其他候选人具备了该行业的领域知识,因此在被聘用时,这些人可以马上开始工作。相比之下,我需要在入职期间接受培训,比如了解什么指标对公司来说是重要的,目前什么产品特性是流行的,他们的关系数据库是什么样子的,等等。

第一次听到这个消息时,我大吃一惊。毕竟,数据科学不应该是领域不可知的吗?该领域的工具可以应用于不同的领域,但仍然可以在公司的底线上带来类似的好处?(即 SaaS 应用于 B2C 公司的客户流失建模)

但这是当今拥挤的数据科学就业市场的现实。在沟通技巧之上,在技术能力之上,在所有你自己的职业经历之上,都有一个模糊但决定性的词“适合”。适合,或匹配,或利基或任何你想叫它,是新的女孩在街上。她现在是你能否得到这份工作的最终决定者。

这不是拒绝的陈述,而是接受的陈述。我承认,就业市场充满了同样具有竞争力的候选人,如果每个人在技术上表现出色,领域经验仍然是区分候选人的唯一其他变量。如果是别的时代,就不会是这种情况,但我们生活在一个非常时期。

如果你想看其他谈论类似经历的帖子,下面有一些链接:

按列或行对 NumPy 数组排序

原文:https://towardsdatascience.com/sort-numpy-arrays-by-columns-or-rows-18dfd2ef28c4?source=collection_archive---------26-----------------------

使用 NumPy 的内置函数来简化排序

Alex BlockUnsplash 上拍照

NumPy 是许多 Python 程序和分析中使用的基本模块,因为它非常有效地进行数值计算。然而,对于 NumPy 的新手来说,一开始可能很难理解。具体来说,理解数组索引和排序很快变得复杂。幸运的是,NumPy 有一些内置函数,使得执行基本的排序操作非常简单。

NumPy 数组可以使用argsort()函数按单列、单行或多列或多行排序。argsort函数返回一个索引列表,该列表将对数组中的值进行升序排序。argsort函数的kind参数使得对多行或多列的数组进行排序成为可能。本文将使用简单的示例和代码,对单行和单行进行排序,对多列进行排序。

创建一个 NumPy 随机整数数组

首先,导入numpy模块。

现在创建一个随机整数数组。在这里,我创建了一个 5 行 4 列的数组。数组中的值是随机的,所以如果使用相同的代码,得到的值将与这里显示的不同。

a = np.random.randit(100, size=(5, 4)) output: 
[[44 47 64 67] 
[67 9 83 21] 
[36 87 70 88] 
[88 12 58 65] 
[39 87 46 88]]

按列对 NumPy 数组排序

为了按照特定的列对数组进行排序,我们将使用numpy argsort()函数。argsort()返回将导致排序数组的数组索引( source )。让我们快速看一下argsort返回什么,然后如何使用argsort获得一个排序后的数组。

首先,在我们创建的数组的第一列上调用argsort。您应该会得到类似这样的结果。其中argsort结果给出了从最小到最大值的索引。

a[:, 0].argsort() output: 
[2 4 0 1 3]

输出显示第一列中的最小值位于位置 2(第三个值)。这是正确的,数组第一列中的最小值是 36,这是第一列中的第三个值(位置 2)。

为了对数组进行排序,我们现在需要使用来自argsort结果的索引来对数组中的行进行重新排序。这是一个简单的过程,只用一行代码就可以完成。我们将简单地使用argsort结果作为行索引,并将结果数组赋回给a,如下所示。

a = a[a[:, 0].argsort()] output:
[[36 87 70 88] 
[39 87 46 88] 
[44 47 64 67] 
[67 9 83 21] 
[88 12 58 65]]

如您所见,现在根据第一列将行从最少到最多排序。要对不同的列进行排序,只需更改列索引。例如,我们可以用a[a[:, 1].argsort()]对第 2 列进行排序。

按行对 NumPy 数组排序

NumPy 数组也可以按行值排序。这与使用列进行排序的方式相同。我们只需要改变索引位置。例如,假设我们创建的数组在第一列进行排序,然后根据第一行中的值对列进行排序。

为此,只需将索引(0)移动到行位置,并将argsort结果移动到列位置。下面的代码显示了演示和结果。

a = a[:, a[0, :].argsort()] output: 
[[36 70 87 88] 
[39 46 87 88] 
[44 64 47 67] 
[67 83 9 21] 
[88 58 12 65]]

多列排序

有时需要对多列进行排序。这方面的一个例子是在单独的列中包含年、月、日和值的数据。NumPy 的argsort可以使用kind参数处理多个列的排序。

让我们首先创建一个包含 4 列的数组,分别代表年、月、日和值。

b = np.array([
[2020, 1, 1, 98], 
[2020, 2, 1, 99], 
[2021, 3, 6, 43], 
[2020, 2, 1, 54], 
[2021, 1, 1, 54], 
[2020, 1, 2, 74], 
[2021, 1, 3, 87], 
[2021, 3, 9, 23]])

现在我们将使用argsort对列进行排序,从最低优先级开始。这意味着如果我们想按年排序,然后按月排序,然后按日排序,我们需要先按日排序,然后按月排序,再按年排序。对于除第一个排序之外的所有排序,我们需要将kind指定为‘merge sort ’,这将保持之前的排序上下文。下面的代码块演示了这一点。

b = b[b[:, 2].argsort()] # sort by day 
b = b[b[:, 1].argsort(kind='mergesort')] # sort by month 
b = b[b[:, 0].argsort(kind='mergesort')] # sort by year output:
[[2020 1 1 98] 
[2020 1 2 74] 
[2020 2 1 99] 
[2020 2 1 54] 
[2021 1 1 54] 
[2021 1 3 87] 
[2021 3 6 43] 
[2021 3 9 23]]

如您所见,该过程成功地基于三列进行了排序。

结论

NumPy 是一个基本的 Python 包。因为它是用编译语言构建的,所以它可以大大提高 Python 程序在 Python 列表和元组上的速度(如果合适的话)。缺点是 NumPy 可能有一个陡峭的学习曲线。一旦你能够理解数组造型、索引和排序,你就已经是一个熟练的 NumPy 用户了。

最初发表于https://opensourceoptions.com

Spark 中的 sort()与 orderBy()

原文:https://towardsdatascience.com/sort-vs-orderby-in-spark-8a912475390?source=collection_archive---------6-----------------------

Apache Spark 中 sort()和 orderBy()有什么区别

米凯尔·克里斯滕森在 Unsplash 上拍摄的照片

介绍

对 Spark 数据帧进行排序可能是最常用的操作之一。您可以使用sort()orderBy()内置函数对至少一列的特定数据帧进行升序或降序排序。尽管这两个函数都应该对 Spark 数据帧中的数据进行排序,但它们有一个显著的区别。

在今天的文章中,我们将讨论sort()orderBy()的区别,并了解何时使用其中一个。

首先,让我们创建一个示例数据框架,它将在整篇文章中用来演示sort()orderBy()是如何工作的。注意,我将使用 Python API,但是我们今天讨论的内容也适用于 Scala。

df = spark.createDataFrame(
   [
      ('Andrew', 'Johnson', 'Engineering', 'UK', 34),
      ('Maria', 'Brown', 'Finance', 'US', 41),
      ('Michael', 'Stevenson', 'Sales', 'US', 31),
      ('Mark', 'Anderson', 'Engineering', 'Ireland', 28),
      ('Jen', 'White', 'Engineering', 'UK', 29)
  ],
  ['first_name', 'last_name', 'department', 'country', 'age']
)
df.show(truncate=False)+----------+---------+-----------+--------+---+
|first_name|last_name| department||country|age|
+----------+---------+-----------+--------+---+
|    Andrew|  Johnson|Engineering|      UK| 34|
|     Maria|    Brown|    Finance|      US| 41|
|   Michael|Stevenson|      Sales|      US| 31|
|      Mark| Anderson|Engineering| Ireland| 28|
|       Jen|    White|Engineering|      UK| 29|
+----------+---------+-----------+--------+---+

排序()

现在,我们可以使用sort()方法根据雇员的国家和年龄对数据帧df进行升序排序。

df.sort('country', 'age').show(truncate=False)+----------+---------+-----------+--------+---+
|first_name|last_name| department||country|age|
+----------+---------+-----------+--------+---+
|      Mark| Anderson|Engineering| Ireland| 28|
|       Jen|    White|Engineering|      UK| 29|
|    Andrew|  Johnson|Engineering|      UK| 34|
|   Michael|Stevenson|      Sales|      US| 31|
|     Maria|    Brown|    Finance|      US| 41|
+----------+---------+-----------+--------+---+

但是请注意,sort()方法将对每个分区中的记录进行排序,然后返回最终输出,这意味着输出数据的顺序无法保证,因为数据是在分区级别上排序的,但是您的数据帧可能有数千个分区分布在集群中。由于数据没有被收集到一个单独的执行器中,所以sort()方法是有效的,因此当排序对您的用例不重要时更合适。

例如,如果雇员JenAndrew存储在不同的分区上,那么最终的顺序可能是错误的,例如:

+----------+---------+-----------+--------+---+
|first_name|last_name| department||country|age|
+----------+---------+-----------+--------+---+
|      Mark| Anderson|Engineering| Ireland| 28|
|    Andrew|  Johnson|Engineering|      UK| 34|
|       Jen|    White|Engineering|      UK| 29|
|   Michael|Stevenson|      Sales|      US| 31|
|     Maria|    Brown|    Finance|      US| 41|
+----------+---------+-----------+--------+---+

orderBy()

同样,我们可以使用orderBy()根据雇员的国家和年龄以升序排列数据框:

df.orderBy('country', 'age').show(truncate=False)+----------+---------+-----------+--------+---+
|first_name|last_name| department||country|age|
+----------+---------+-----------+--------+---+
|      Mark| Anderson|Engineering| Ireland| 28|
|       Jen|    White|Engineering|      UK| 29|
|    Andrew|  Johnson|Engineering|      UK| 34|
|   Michael|Stevenson|      Sales|      US| 31|
|     Maria|    Brown|    Finance|      US| 41|
+----------+---------+-----------+--------+---+

sort()不同,**orderBy()** 功能保证输出中的总顺序。发生这种情况是因为数据将被收集到单个执行器中以便进行排序。这意味着orderBy()sort()效率更低。

最后一句话

sort()orderBy()功能均可用于对至少一列的火花数据帧进行排序,排序顺序为任意顺序,即升序或降序。

sort()orderBy()更有效,因为数据在每个分区上单独排序,这就是为什么输出数据的顺序不能保证。

另一方面,orderBy()将所有数据收集到一个执行器中,然后对它们进行排序。这意味着输出数据的顺序得到保证,但这可能是一个非常昂贵的操作。

因此,如果您需要对顺序不太重要的数据帧进行排序,同时您希望它尽可能快,那么sort()更合适。如果顺序很重要,确保使用orderBy()来保证输出数据根据用户参数排序。

你可能也喜欢

每个数据科学家都应该知道的排序算法

原文:https://towardsdatascience.com/sorting-algorithms-every-data-scientist-should-know-9c4ff592f28c?source=collection_archive---------44-----------------------

使用这些聪明的排序方法更好地排序您的数据!

(src =https://pixabay.com/images/id-1312384/

介绍

数据很难保持有序。数据科学家有几种技术可以帮助他们组织数据,其中最流行的一种技术是排序。排序是一种基于某些设定参数对观察值进行重新排序的技术。该参数可以是连续值、分类的,或者甚至是特征或工程特征的组合。此外,时间也常用于对观察值进行排序。

不用说,这使得排序成为数据分析领域中非常重要的一项工作。在某些情况下,很难处理没有排序的数据,并且有许多算法可能需要归入排序类别,例如搜索栏。此外,排序在机器学习和分析中非常常用。将排序算法应用于统计分析的一个很好的例子是 Wilcox 秩和检验。该测试根据观察值在数据集中的连续排名对其进行重新排序。在重申了排序算法的重要性之后,我想谈谈一些不同的排序算法、它们的性能以及它们在不同类型数据上的应用。

№1:冒泡排序

一种相当基本的排序方法是冒泡排序法。这种技术是通过比较前两个值来实现的,如果一个值应该在另一个值之前,那么它将与前一个值进行交换。接下来,我们比较接下来的两个值,做同样的事情,如果前一个值是错的,我们继续切换。这肯定会首先获得最高值,但在我们到达切换的第一次迭代结束后,我们需要返回并重新开始,直到最终没有切换,这意味着我们的数据都已排序。

很容易理解为什么这是低效的。我们可能会在每个值上循环移动一个值。这个事件可能会发生几次。在许多情况下,它需要为每个单独的观察传递一次我们的值。一千次观察意味着一千次操作执行一千次,以此类推。该算法将要执行的运算次数随着观察次数(n)的平方而增加。衡量该算法性能的一个很好的方法是使用大 O 符号。正如我所说的,这个算法有一个指数增长,所以它的计算时间随着 n 的增加而增加,这意味着这个算法的大 O 符号是 O(n)。不用说,当观测值很多时,你可能不想使用这种算法,因为它会让你的计算机计算起来困难得多。在整篇文章中,我将使用大 O 符号来衡量这些排序算法的性能,因此,如果您想进一步了解它,我确实写了一篇文章,您可以在这里阅读:

[## 为什么你的循环这么慢

towardsdatascience.com](/why-your-loops-are-so-slow-166ec67db47d)

№2:插入排序

我想看的下一种排序算法是插入排序。这种算法以与冒泡排序非常相似的方式开始,首先交换前两个观察值,然后交换第二个观察值。然而,当插入排序跳回到前两个并进行比较时,区别就来了。这些项继续向起始项移动,一边移动一边检查,直到您最终到达列表的起始处,并且您知道它已经被排序了。如果你有一手按顺序排列的牌,这可能是你会使用的算法,甚至没有意识到这一点。

正如您可能想象的那样,这种排序方法通常比冒泡排序更快。这是因为我们不需要每次遍历列表时都用一个操作对其进行整体排序。也就是说,该算法的大 O 符号仍然是 O(n),这意味着该算法仍然具有与冒泡排序相同的性能损失,只是在不同的时间段内。也就是说,虽然它对于处理较小的数据集肯定是有用的,但在对大量值进行排序时,这很可能不是最佳选择。

№3:快速排序

快速排序因一个非常重要的原因而得名,与列表中的其他方法相比,它的速度相当快。这项技术包括选择一个中心点,通常是数据的中间点,然后比较该中心点两侧的观察值。小一点的会去一端,大一点的会去另一端。在那之后,你在两边的中心选择一个新的支点,以同样的方式排序,直到最后,数据被排序。

在我介绍的所有其他排序算法中,这种排序算法的初始计算成本总是最小的。然而,这个算法的大 O 符号仍然可以归入 O(n)类,尽管这不是一个保证。当然,一个算法的效率总是依赖于它的输入,所以在这方面肯定会有增强或损害算法的变化。然而,该算法的平均性能是 O(n log n)。这意味着它通常是具有陡峭斜率的线性,然而,当涉及到处理更多的观测值时,它可能会陷入困境。

№4:桶排序

桶排序是一种算法,它首先将部分观察值放入“桶”中。然后每个存储桶被单独排序,通常使用快速排序方法,然后这些存储桶被递归排序。这确实将快速排序的“分而治之”方法提升到了一个全新的水平,但是进行多层划分使得处理单个组件变得非常容易,从而可以轻松地对所有组件进行排序。这也提供了巨大的性能优势,最差的性能可能是 O(k)。通常,这个算法更多地落入 O(n+k ),这是好得多的,并使这个算法当然脱颖而出。

№5:基数排序

我想谈的最后一个排序算法是基数排序。这种算法建立在典型的桶排序的基础上,但是有一个关键的区别。基数排序是一种非比较排序算法。这意味着列表中的观察值从不直接比较。相反,这些元素使用它们的基数分布到桶中。基数是基于唯一位数的数字位置系统。正如人们可能想象的那样,这种算法与桶排序具有相似的性能,但是去掉了快速排序功能。因此,该算法更加一致,并且通常落入 O(nk)中,这是相当快的。

结论

在计算机科学中,有许多不同的排序算法服务于许多不同的目的。当然,在处理数据观察时,排序尤其关键,因此数据科学家肯定可以从了解不同的排序方法并利用其应用优势中受益。算法的选择几乎肯定会随着应用的不同而改变,因此了解不同的算法及其应用对于更有效地对数据进行排序至关重要。非常感谢你看我的文章!

排序算法—使用 Python

原文:https://towardsdatascience.com/sorting-algorithms-with-python-4ec7081d78a1?source=collection_archive---------4-----------------------

实验复习为什么什么和如何排序算法

Unsplash 上的 Max Panamá 拍摄的照片

带来秩序的艺术

排序意味着将项目按特定的顺序排列。该特定顺序由元素的比较属性决定。在整数的情况下,我们说较小的数字先出现,较大的数字后出现。以特定顺序排列项目可以改进元素的搜索。因此,排序在计算机科学中有大量的应用。

在这篇博客中,我们将介绍常见的排序算法。我们将看到每一个在 python 中的实现。为了比较它们的运行时间,我在排序数组上使用了 Leetcode 问题。该问题的约束条件如下。

约束:

1 <= nums.length <= 50000
-50000 <= nums[i] <= 50000

我用所有常见的排序算法解决了这个问题。下面是执行结果。

作者图片

注: TLE 超过时限。

基于比较

冒泡排序
选择排序
插入排序
快速排序
壳/山排序
堆排序
合并排序

不基于比较

计数排序
桶排序
基数排序

冒泡排序

最简单的排序算法。对列表进行迭代,在每次迭代中,它成对地比较元素,并不断交换它们,以使较大的元素移向列表的末尾。

非递归
稳定
到位
O(n)

def bubbleSort(array):
    swapped = False
    for i in range(len(array)-1,0,-1):
        for j in range(i):
            if array[j]>array[j+1]:
                array[j], array[j+1] = array[j+1], array[j]
                swapped= True
        if swapped:
            swapped=False
        else:
            break
    return array

选择排序

在这个算法中,我们创建了两个列表段,一个已排序,另一个未排序。我们不断地从未排序的列表段中删除最小的元素,并将其附加到排序的列表段中。我们不交换中间元素。因此,该算法以最少的交换次数对数组进行排序。

非递归
不稳定
到位
O(n)

def selectionSort(array):
    for i in range(len(array)-1):
        min_idx = i
        for idx in range(i + 1, len(array)):
            if array[idx] < array[min_idx]:
                min_idx = idx
        array[i], array[min_idx] = array[min_idx], array[i]
    return array

插入排序

像选择排序一样,在这个算法中,我们将列表分为排序和未排序部分。然后,我们遍历未排序的段,并将该段中的元素插入到排序列表中的正确位置。

非递归
稳定
到位
O(n)

def insertionSort(array):
    for i in range(1, len(array)):
        key = array[i]
        j = i-1
        while array[j] > key and j >= 0:
            array[j+1] = array[j]
            j -= 1
        array[j+1] = key
    return array

外壳排序

外壳排序是对插入排序的优化。这是通过以固定的递减间隔对所有元素重复执行插入排序来实现的。最后一次迭代的间隔是 1。这里它变成了一个常规的插入排序,它保证了数组将被排序。但要注意的一点是,当我们这样做的时候,数组几乎已经排序了,因此迭代非常快。

非递归
稳定
到位
0(n)也取决于区间选择

def shellSort(array):
    n = len(array)
    k = int(math.log2(n))
    interval = 2**k -1
    while interval > 0:
        for i in range(interval, n):
            temp = array[i]
            j = i
            while j >= interval and array[j - interval] > temp:
                array[j] = array[j - interval]
                j -= interval
            array[j] = temp
        k -= 1
        interval = 2**k -1
    return array

堆排序

像前面两个算法一样,我们创建了两个列表段,一个是已排序的,一个是未排序的。在这里,我们使用堆数据结构来有效地从列表的未排序段中获取 max 元素。heapify 方法使用递归来获取顶部的 max 元素。

非递归
不稳定
在位
O(nlogn)

def heapify(array, n, i):
    largest = i
    l = 2 * i + 1
    r = 2 * i + 2

    if l < n and array[i] < array[l]:
        largest = l
    if r < n and array[largest] < array[r]:
        largest = r

    if largest != i:
        array[i], array[largest] = array[largest], array[i]
        heapify(array, n, largest)

def heapSort(array):
    n = len(array)
    for i in range(n//2, -1, -1):
        heapify(array, n, i)
    for i in range(n-1, 0, -1):
        array[i], array[0] = array[0], array[i]
        heapify(array, i, 0)
    return array

合并排序

这是一个分治算法。在这个算法中,我们将一个列表分成两半,并继续将列表分成两半,直到它只有一个元素。然后我们合并排序后的列表。我们一直这样做,直到得到一个包含未排序输入列表中所有元素的排序列表。

递归
稳定
需要额外空间
O(nlogn)

def mergeSort(nums):
    if len(nums)==1:
        return nums
    mid = (len(nums)-1) // 2
    lst1 = mergeSort(nums[:mid+1])
    lst2 = mergeSort(nums[mid+1:])
    result = merge(lst1, lst2)
    return resultdef merge(lst1, lst2):
    lst = []
    i = 0
    j = 0
    while(i<=len(lst1)-1 and j<=len(lst2)-1):
        if lst1[i]<lst2[j]:
            lst.append(lst1[i])
            i+=1
        else:
            lst.append(lst2[j])
            j+=1
    if i>len(lst1)-1:
        while(j<=len(lst2)-1):
            lst.append(lst2[j])
            j+=1
    else:
        while(i<=len(lst1)-1):
            lst.append(lst1[i])
            i+=1
    return lst

快速排序

在这个算法中,我们围绕一个 pivot 元素对列表进行分区,围绕 pivot 对值进行排序。在我的解决方案中,我使用列表中的最后一个元素作为 pivot 值。当 pivot 值将列表分成几乎相等的两半时,可以获得最佳性能。

递归
到位
不稳定
O(nlogn)

def quickSort(array):
    if len(array)> 1:
        pivot=array.pop()
        grtr_lst, equal_lst, smlr_lst = [], [pivot], []
        for item in array:
            if item == pivot:
                equal_lst.append(item)
            elif item > pivot:
                grtr_lst.append(item)
            else:
                smlr_lst.append(item)
        return (quickSort(smlr_lst) + equal_lst + quickSort(grtr_lst))
    else:
        return array

计数排序

该算法不在元素之间进行比较。我们使用整数的数学属性来排序。我们计算一个数字出现的次数,并将计数存储在数组中,数组中的索引映射到键值。

非递归
到位,但需要额外的空间
稳定
O(n)

def sortArray(self, nums: List[int]) -> List[int]:
    i_lower_bound , upper_bound = min(nums), max(nums)
    lower_bound = i_lower_bound
    if i_lower_bound < 0:
        lb = abs(i_lower_bound)
        nums = [item + lb for item in nums]
        lower_bound , upper_bound = min(nums), max(nums)

    counter_nums = [0]*(upper_bound-lower_bound+1)
    for item in nums:
        counter_nums[item-lower_bound] += 1
    pos = 0
    for idx, item in enumerate(counter_nums):
        num = idx + lower_bound
        for i in range(item):
            nums[pos] = num
            pos += 1
    if i_lower_bound < 0:
        lb = abs(i_lower_bound)
        nums = [item - lb for item in nums]
    return nums

我还想提一下基数排序,它可以用计数排序/桶排序作为子例程来实现。请参见下面的参考链接。

https://medium.com/nerd-for-tech/counting-sort-radix-sort-ccd9f77a00a2

将所有这些放在一起:

在尝试了所有这些不同的算法之后,出于好奇,我用默认的排序方法提交了我的解决方案。它的运行时间是 152 毫秒,非常快。Python 的默认排序使用 Tim 排序,这是合并排序和插入排序的组合。实现将在另一篇文章中讨论。

发现了一个惊人的播放列表,其中用舞蹈演示了排序算法。你可能想看。值得花时间。

礼貌:https://www.youtube.com/channel/UCIqiLefbVHsOAXDAxQJH7Xw

通过我们的小实验,我们了解了不同的算法、它们的运行时间和内存消耗。我们现在知道了内存、CPU 时间和算法稳定性等参数。我们需要根据给定的问题来评估这些参数,以确定算法。我们还可以将这些基本排序算法组合成更强大的算法,如 Tim Sort。

分类愉快!!

不良数据的来源及其应对措施

原文:https://towardsdatascience.com/sources-of-bad-data-and-what-to-do-about-it-ef06f3c10cc9?source=collection_archive---------36-----------------------

好的数据是模型训练成功的前提。请继续阅读,发现可能出现坏数据的棘手地方。

照片由陈嘉里Unsplash 上拍摄

在任何机器学习问题中,拥有好的数据和拥有好的模型一样重要。或者,正如关于坏数据的一句名言所说——垃圾进,垃圾出。在这篇文章中,我们探讨了一些常见的,但没有很好地认识到的坏数据的来源。

顶部编码和底部编码

当数据集用相同的值替换非常高或非常低的数字时,会发生顶部编码和底部编码。这有时用于保护数据集中人员的身份。例如,考虑一个公开可用的数据集,其中一部分数据是收入。收入超过 10 亿美元的人并不多,这意味着这些条目如果不变,很可能只根据收入数字就能匹配到确切的人。为了防止这种情况并保持匿名性,我们可以执行顶层编码,并将数据集中所有高于一个大数字(比如 1 亿)的收入设置为同一个数字。当然,这意味着任何建立在这个数据集中的收入数据之上的模型都是不准确的。没有办法获得顶部编码/底部编码数字的真实值,所以我们能做的最好的事情是根据其他未修改的值来估计它们的真实值。例如,您可以对非编码收入值训练一个线性模型,然后使用该模型来推断真正的编码值。

代理报告

当调查中的一个成员为调查中的其他成员回答问题时,就会出现代理报告。一个常见的例子是家庭调查,户主为家庭中的每个人回答问题。代理报告的问题是,回答调查的人可能会为他所代表的人提供空白或不准确的答案。因此,如果数据集包含代理报告的值,就很难说是否可以信任这些数据。确保代理报告值准确的一种方法是将它们与非代理值进行比较。

选择偏差

一般来说,当数据集背后的样本不是随机的时,就会出现选择偏差问题,这会引入偏差。发生这种情况有各种各样的原因。选择偏差的一个常见原因是倾向于回答调查的人有某些特殊的特征——例如做更多的公民和政治活动。那么,如果正在讨论的调查是关于人们做多少政治活动,它将偏向于更多的活动。选择偏差的另一个例子发生在医学领域,当医生要求志愿者测试一种新疗法的有效性时。自愿接受这些治疗的人可能并不代表一般人群,因为他们可能更懂医学,更会照顾自己,更年轻,等等。

选择偏差的问题在于,有时它是不可避免的,尤其是当你的调查依赖于征求志愿者的时候。在您的数据集中,也没有很好的方法来减轻这种影响——我们没有统计技巧来解除数据集的偏倚。我们能做的最好的事情就是设计调查来避免选择偏差,并且对有偏差的调查的结论有所保留。

插补偏差

许多数据集有空值。处理这些空白的一种方法是根据一些公式来填充或估算这些值。当估算值的百分比很大时,从数据中得出的结论可能是可疑的。例如,我们可能想要测量一组中每个人的收入随时间的差异。这需要在两个不同的时间进行两次收入调查。让我们假设其中一个调查的一些值是通过取非估算值的样本平均值来估算的。然后,对于每个有估算数据点的人,我们不是在衡量那个人的收入差异——我们实际上是在衡量一个数据点和其他所有人的平均值之间的差异,这没有任何意义。

处理估算值的另一种方法是将其从数据集中删除。不幸的是,这可能会使数据产生偏差。处理可能的估算数据的最好方法是找到实际数据。这有时是可能的,例如,如果您是政府雇员,并且可以访问具有类似数据的其他数据集。第三种解决方案是重新加权数据,使估算值没有非估算值重要。

在某些情况下,估算数据不是一个大问题。例如,如果我们要做的只是找出一个数据集的平均值,那么使用样本平均值计算的估算数据应该不会改变平均值太多。此外,对于具有很少异常值和低方差的“表现良好”的数据集,估算数据的影响较小。

在这篇文章中,我们看了一些坏数据的来源。在某些情况下,我们已经讨论了减少坏数据影响的方法。在其他情况下(比如志愿者调查中出现的选择偏差),我们已经看到我们无能为力。无论如何,重要的是要记住这些坏数据的可能来源,并相应地判断来自坏数据集的结论。

计算机算法中的空间和时间复杂性

原文:https://towardsdatascience.com/space-and-time-complexity-in-computer-algorithms-a7fffe9e4683?source=collection_archive---------0-----------------------

计算机科学中的时空权衡让你的生活更轻松

阿里安·达尔维什在 Unsplash 上拍摄的照片

在这篇文章中,我将讨论由 Juris Hartmanis 和 Richard E. Stearns 开发的计算复杂性来分析算法的难度。

我们都知道,人类天性渴望寻找一种有效的方式来组合他们的日常任务。创新和技术背后的主导思维过程是通过提供解决人们可能遇到的问题的方法来使人们的生活更加轻松。同样的事情也发生在计算机科学和数字产品领域。我们编写的算法效率高,占用内存少,性能更好。

时间复杂度是算法执行每组指令所花费的时间。当一个简单的问题可以用不同的方法解决时,选择最有效的算法总是更好。

空间复杂度通常指算法消耗的内存量。它由两个不同的空间组成;辅助空格输入空格

时间因素通常比空间因素更重要。

注意:—在计算机编程中,你可以使用 256MB 来解决一个特定的问题。如果创建一个大于 10⁸的数组,将会出现错误。此外,您不能创建大小为 10⁶的数组,因为分配给函数的最大空间是 4MB。我们必须定义一个全局来解决这个问题。

虽然它们看起来微不足道,但这些因素在决定一个计算机程序如何被开发、设计以及它如何给用户的生活增加价值方面是至关重要的。记住,时间就是金钱。

创建一个好的算法需要什么?

作者图片

一个好的算法是在执行过程中花费较少的时间并节省空间的算法。理想情况下,我们必须在空间和时间之间找到一个中间点,但我们可以满足于平均水平。让我们看看一个简单的算法,找出两个数字的总和。

**Step #01:** Start.**Step #02:** Create two variables (a & b).**Step #03:** Store integer values in ‘a’ and ‘b.’ -> **Input****Step #04:** Create a variable named ‘Sum.’**Step #05:** Store the sum of ‘a’ and ‘b’ in a variable named ‘Sum’ -> **Output****Step #06:** End.

它就像看起来一样简单。

注: —如果你是一个游戏玩家,你会知道游戏的平均大小(在硬盘中)与日俱增,加载时间也在缩短。同样,在网站中,加载时间显著减少,其服务器的存储空间与日俱增。因此,正如我们之前讨论的,就空间&时间而言,时间在任何软件的开发阶段都起着至关重要的作用。

根据尼尔·帕特尔进行的研究

" 47%的消费者希望网站加载时间不超过两秒钟."

你可以在网上找到很多复杂的算法。这可能是为什么几乎所有的大公司都在研究和编写这些复杂的指令集上投入大量资金的原因。

以下是在算法的长期使用中起重要作用的因素:

  1. 效率——我们已经讨论过在创建一个好的算法时效率有多重要。是效率减少了计算时间,产生了快速输出。
  2. 有限性 —算法必须在完成指定数量的步骤后终止。否则,它将使用更多的内存空间,这被认为是一个不好的做法。如果无限循环或递归继续下去,可能会出现堆栈溢出和越界情况。
  3. 正确性 —一个好的算法应该产生正确的结果,而不管所提供的输入的大小。

时间复杂度有多重要?

时间复杂度与输入大小密切相关。随着输入大小的增加,运行时间(即算法运行所需的时间)也会增加。

举例:考虑一个排序算法。

假设我们有一组名为 A = {10,5,21,6,9}的数字,

有许多算法可以对给定的数字进行排序。但并不是所有的都有效率。为了找出最有效的方法,我们必须对每个算法进行计算分析。

Leonardo Galler 和 Matteo Kimura 对 LAMFO 做了一项关于“排序算法的最精细的研究

LAMFO 基准

以下是图表中的一些重要观察结果

  • 这个测试展示了五种最常用的排序算法:快速排序、插入排序、冒泡排序、外壳排序和堆排序。
  • 用于执行该任务的编程语言是 Python,输入的大小范围从 2500 到 50000。
  • 结果是:“尽管列表很长,外壳排序和堆排序算法执行得很好,另一方面,我们发现插入排序和冒泡排序算法要差得多,大大增加了计算时间。请参见上图中的结果。”
  • 在对任何算法进行分析之前,我们必须检查它是否稳定。理解我们的数据是进行成功分析的最重要部分。

渐近符号介绍(变得简单)

如果你不是来自计算机科学背景,你可能会发现这个概念比通常要复杂一点。别担心!我掩护你。

那么,什么是渐近符号呢?

简单地说,它告诉我们一个算法与另一个算法相比有多好。

我们不能直接并排比较两种算法。这在很大程度上取决于我们用于比较的工具和硬件,例如操作系统、CPU 型号、处理器代等。即使我们为运行在同一个系统上的两个算法计算时间和空间,它们的时间和空间复杂度也可能受到系统环境微妙变化的影响。

因此,我们用 渐近分析 来比较空间和时间复杂度。它根据两种算法在输入大小的增量或减量方面的性能变化来分析这两种算法。

主要有三种渐近符号:

  1. 大-哦(O)符号。
  2. 大ω符号。
  3. 大 Theta(θ)符号—广泛使用。

Big-Oh/ Big-O (O)符号

1894 年,Paul Bachmann 引入了 big-O 符号。他在关于逼近函数的讨论中轻率地引入了这个符号。

**From the definition:** O(g(n)) = {f(n): there exist positive constants c and n0 such that 0 <= f(n) <= c*g(n) for all n >=n0}

这里‘n’给出了上限值。如果一个函数是 O(n),那么它也是 O(n),O(n)。

这是渐近分析中最常用的符号。它定义了一个函数的上限,即算法所用的最大时间或算法的最坏情况时间复杂度。换句话说,它给出了相应输入的最大输出值(big-O)。

作者麦迪逊·斯坦克维奇

大ω(ω)符号:

**From the definition:** The function f (n) is Ω(g(n)) if there exist positive numbers c and N, such that f (n) ≥ cg(n) for all n ≥ N.

它定义了函数的下限,即算法所用的最小时间。它给出了相应输入的最小输出值(大ω)。

图由黑客地球

大θ符号:

**From the definition:** f(n) is Θ(g(n)) if there exist positive numbers c1, c2, and N such that c1g(n) ≤ f (n) ≤ c2g(n) for all n ≥ N

它定义了函数的下限和上限,即,对于给定的输入值,它存在于两个、大多数和至少边界处。

图由黑客地球

注:— Big-O 以≤定义,ω以≥定义;=包含在两个不等式中。它提出了一种限制可能的下限和上限的方法。这个限制是通过大θ符号实现的。

渐近分析中的最佳情况、最差情况和平均情况:

作者图片

  1. 最佳情况:它定义为允许算法在最短时间内完成语句执行的条件。在这种情况下,执行时间充当算法时间复杂度的下限。
  2. 平均情况:在平均情况下,我们得到每个可能的输入组合的运行时间之和,然后取平均值。在这种情况下,执行时间充当算法时间复杂度的下限和上限。
  3. 最坏情况:它定义为允许一个算法在最大时间内完成语句执行的条件。在这种情况下,执行时间充当算法时间复杂度的上限。

刚开始编程的时候,空间和时间复杂度的概念一直是我的敲门砖。所以今天,我想就这两个因素如何显著影响算法进行一个简单的讨论。

参考

谢谢你读了这个故事,再见。如果您对此有任何想法、反馈或建议,请随时发表评论!

如果你喜欢我的作品,想要支持我,那么请使用这个链接注册成为一名媒体会员,每月只需 5 美元,你的一部分费用将支持我们作家,让我们不断创造惊人的内容。或者,你可以给我买一杯☕️咖啡。如果你这样做了,万分感谢。🙂

空间划分和 KD 树

原文:https://towardsdatascience.com/space-partitioning-and-kd-trees-7b0e12b368d0?source=collection_archive---------15-----------------------

实践教程

加速建模和最近邻搜索

在疫情开始的时候,我给写了一篇帖子,讨论用 Python 构建流行病学模型,着眼于制作简单的动画视觉效果——这是《华盛顿邮报》和 3Blue1Brown 在他们关于疫情的热门帖子中使用的图形的快速版本。

一个玩具流行病学模型,红点代表受感染的个体

当时,我的重点主要是使用 Matplotlob 的动画库来构建视觉效果,因此我很快拼凑出的用于制作模型的代码并不是很好。我的意思是,模型本身并不特别有趣 3Blue1Brown 的格兰特·桑德森(Grant Sanderson)做了大量脑力劳动,用不同的参数和模型设置进行实验,以收集真正的见解,我只是揭开了一些技术方面的帷幕——但代码效率低下,如果我试图扩大模型的规模,这将成为一个真正的问题。

正如你所想象的,流行病学模型中最重要的一步可能是建立疾病传播的模型。对于我们建模的每个时间段,我们的模型需要确定当前未感染的脆弱人群是否会从当前已感染的人群感染疾病。在我们上次建立的简单模型中,这包括检查在给定的传播半径内是否有任何受感染的个体,这就是挑战所在。计算机不一定有直观的方式来判断哪些人可能彼此靠近,特别是当我们对四处移动的人进行建模时,计算机所能做的就是选择两个人并检查他们之间的距离。为了保证它没有错过任何可能的成对个体,它可能需要检查许多对个体,这些个体实际上没有接近到足以发生传播。

保证捕捉到每一个可能的配对的最简单的方法是对每一个个体进行循环,这是可行的,但是随着模型规模的增长,最终需要大量的操作,因为随着模型中个体数量的增长,需要检查的配对数量也呈指数增长。尽管检查一对个体之间的距离可能相对较快,但如果您的模型中有成千上万的个体,要检查的配对数量将会非常多,无论如何该过程可能会花费大量时间。

眼前的问题是,我们如何减少这些操作的数量,而不冒错过任何可能的传输的风险。我将探索一种涉及空间划分的策略。事实证明,这不仅与我们在此讨论的建模类型相关,还与各种其他计算领域相关,包括计算机图形学和一些数据科学算法,如“最近邻”类型的模型,该模型涉及搜索训练数据中的点以找到离新点最近的点的类似任务。我们将从一个简单但仍然相当有效的空间分区方法开始,讨论它的一些局限性,然后探索一种略有不同的方法,空间分区树,以及如何在这个和其他数据科学应用程序中使用它们。

简单的空间隔断

为了激励我们正在做的事情,让我们想象一下手头的任务。回想一下,我们一开始试图模拟一种假设的疾病从受感染的个体传播到脆弱的个体。我们可以这样想象我们模型的初始阶段,红点代表受感染的个体,蓝点代表未受感染的个体:

简单 SIR 模型的开始

受感染的个体有一定的概率将疾病传播给在一定半径内的任何当前易受感染的个体。我们可以把可能的感染范围加入到我们的可视化中:

每一个被感染的个体都代表着对某一半径范围内的易感者的威胁

在这个模型的初始阶段,一些易受感染的个体与所有受感染的个体安全地保持距离,尽管有些没有。我们可以非常清楚地看到这一点,但是我们如何让计算机,它不能使用我们的视觉直觉,来确认一个易感个体是否靠近一个受感染的个体?计算机唯一能做的就是计算两个人之间的距离,并检查这个距离是否短到足以在感染半径之内。计算机需要将任何给定的个体与群体中的所有其他个体进行核对吗?

你可能会想,虽然不检查一堆距离是不可能的,但是有些配对我们应该可以不用计算就能扔掉,比如两个人明显不在一起。也许我们可以将图表分成象限,例如,像这样:

将个人分成象限

我们还没有完全弄清楚谁可能靠近谁,但至少我们可以说,左上角的人不太可能靠近右下角的人。这样,我们只需要对照同一象限中的其他个体来检查每个个体,而不是对照模型中的所有其他个体。您可能会发现这种策略的一个挑战,即一些个体靠近象限的边缘,如果两个不同象限中的个体都靠近这两个象限之间的边界,则有可能在它们之间获得传播。我们可以通过将这些边缘案例分配到两个象限来克服这一点——一个是他们实际所在的象限,另一个是他们离得足够近,可以想象他们可能会与其中的个人进行交互的象限。您也可以将这看作是将四个正方形中的每一个都扩展一点,这样它们就占据了图形的四分之一多一点,并且在它们的边缘有一点重叠:

“象限”略大于图表的四分之一

这是建模空间的一个简单、统一的网格划分,我们可以用它来大大提高建模的速度。现在,对于我们建模的每个时间步,首先我们忽略所有的个体,并将他们分配到一个象限(或者如果他们在边界附近,分配到多个象限)。然后当我们检查可能的传播时,计算机只需要检查每个象限内个体之间的距离。我们在建模过程中增加了另一个步骤,即确定每个人处于哪个象限,但我们在建模的传输检查步骤中减少了更多的计算量。

即使是这种最简单的网格分区也能显著提高速度。当我在之前构建的生成视觉效果的模型上实现了这样一个简单的分区时,我将所需的时间减少了一半以上。在一定程度上,增加我们划分空间的扇区数量可以进一步改善这个时间。

当然,这种策略也有一些缺点。第一个是所有这种空间分区策略所固有的,即它们需要更多的内存,因为现在除了我们的模型在本地内存中需要的任何其他内容之外,我们还需要存储某种表或目录来跟踪每个人所在的扇区。我们基本上是以牺牲内存使用为代价来换取模型的速度。对于这些小玩具流行病学模型,这是一个很小的问题。对于一个更大、更复杂的模型或计算机图形应用程序,内存可能是一种紧张的资源,这可能是一个重要的考虑因素。

第二个问题与这种空间划分方法更为相关,即这种性质的统一网格不一定是划分空间的有效方式。考虑一下在我们的示例中,如果您增加分区的数量来创建一个 6×6 的网格,会发生什么情况:

被分成 36 个扇区空间

一方面,这看起来甚至比象限图更好——在任何给定的部门中只有少数几个个体,因此您需要检查的个体组合的数量应该总是相当低的。但是这种划分在其他方面是低效的。由于个体在空间中的随机放置,一些扇区具有比其他扇区相对更多的个体,而一些扇区完全是空的。如果我们在 3d 空间中有一个模型,或者如果我们在具有更多变量的数据集上执行分区,这种低效率在具有更多维度的情况下会成为一个更大的问题。变量越多,数据集就越稀疏,像这样的统一网格可能就越不合理。

KD 树

我们可以克服这种低效率的方法之一,同时引入一些其他的好处,就是使用一种更智能的方法来划分空间,这种方法可能不会给我们一个统一的网格。一种常见的方法是 KD 树算法。该算法首先在数据集中选择一个轴,找到沿该轴所有点的中值,然后沿该轴创建一个拆分。在我们的例子中,假设我们从 x 轴开始。我们找到中间的 x 值,并沿着它放入一条分割线:

我们 KD 树中的第一个部分

您会注意到这条线在图表中心的右侧一点,随机生成的点恰好在图表的右侧更密集一点,因此中间 x 值在图表中心的上方一点。现在,对于这条线任一侧的每个区域,我们切换到 y 轴,并使用区域内点的中间 y 值,以相同的方式再次划分空间:

第二个 KD 分区,这次是沿着 y 轴

我们可以以这种方式继续,交换轴并细分每个空间。下面是下一层划分的样子:

沿着 x 轴的另一个分割

下一个是:

这种空间的最终划分可能比我们简单、统一的网格看起来更杂乱。但是这种方法有两个主要的好处。首先,生成的分区是“平衡的”——我们创建的每个子区域内都有大致相等数量的点,随着数据集中维度数量的增加,这一特性变得越来越有用。第二是这个算法很自然的创建了一个树形数据结构。KD 树算法创建的并不是真正的网格,而是一系列嵌套的阈值。在我们划分的第一步中,x 值高于阈值的点进入右侧区域,而 x 值低于阈值的点进入左侧区域。在每个后续区域中,在 y 方向上有一个新的阈值,依此类推。对于计算机来说,树形数据结构比网格数据结构更容易完成某些任务。这种树结构就是为什么 KD 树对于像 KNN 模型这样的搜索算法如此有用。让我们看看它们是如何工作的。

KNN 的 KD 树

让我们考虑一个稍微不同的用例。假设我们有一些点的集合,我们用 KD 树将它们分开:

新数据集的 KD 树分区

现在,我们引入一个新的点,一个目标点,我们将尝试估算它的值。这就是,用红色标出的:

新点离哪个点最近?

计算机面临的问题是,原始数据集中的哪些点最接近新的目标点。与我们之前的建模练习一样,我们希望避免计算机需要检查所有原始点和目标点之间的距离。KD 分区的树形特性帮助我们有效地缩小搜索范围。我们可以简单地沿着树开始,看看我们的新目标点在哪里。首先,沿着 x 轴有一个分区;目标点位于它的右侧:

我们沿着树向下走到第一个分区的右边

接下来,沿 y 轴的一个分区,新点位于该分区下方:

向下到第二个分区的左侧

另一个 x 轴分区:

以此类推:

找到目标点在树中的位置不需要检查它和许多点之间的距离,只需要将其 x 和 y 坐标与四个或五个阈值进行比较。因此,该树被证明是缩小可能的点以找到最近邻居的非常有用的工具。

当然,这有点复杂,如果目标点落在其中一个阈值附近,最近的邻居点可能落在树的不同分支上的阈值的另一侧。这确实意味着,当在树中搜索最近的邻居时,您可能需要偶尔遍历树来检查边界另一侧的区域。然而,在这些情况下,该树也可以帮助您快速排除整个区域/子树;区域内的任何点都不会比阈值更接近目标点,因此,如果区域之间的分界线比当前区域内最近的迄今识别的邻居离目标更远,我们可以排除相邻区域内的所有点。

当你适应一个模型时,这实质上是一个 KNN 实现,比如常用的 sklearn 包中的那个。该实现使用类似 KD 树的东西来划分训练数据。当使用模型来预测新点的值时,模型的任务实际上是以尽可能省时的方式在训练数据中搜索离每个新点最近的点,这是使用树来完成的。

球树

搜索算法使用另一种常见的空间划分树,它以稍微不同的方式达到类似的目的。这是球树算法,将点分组为嵌套的“球”或球体。它是这样工作的:首先,确定所有点的质心。接下来,找到离这个质心最远的点,这将是我们图中的第一个“孩子”。质心和这个点之间的距离是第一个球的“半径”,这个球集中没有一个点离质心的距离超过这个半径。集合中离第一个孩子最远的点是我们的第二个孩子。从那里开始,每个更远的点根据哪个更近与第一个或第二个孩子分组。使用上面的数据,我们可以像这样可视化第一个分组:

球树算法的第一分区,其中两个孩子被标记为星形

然后,我们可以迭代地继续这个过程,通过找到质心并将子节点定义到以下组中,将每个球分成另外两个球:

进一步的细分

再说一遍:

当然,我们可以通过进一步细分来继续这个过程。这些嵌套的分组看起来更像重叠的圆圈,但是为了让视觉效果更清晰,我在每个分组周围画了更紧密的边界形状。与 KD 树策略一样,这创建了一个树结构,尽管它是不平衡的。与 KD 树中区域之间的分界线的距离给了我们一种方法来测试和检查我们是否需要在一个区域内搜索一个可能的邻居,或者取消整个区域的资格,在球树中不同球体的半径和到质心的距离也是如此。

用 Python 可视化数据的 5 种非常规方法

原文:https://towardsdatascience.com/spaceborn-visualizations-69058ad6c2df?source=collection_archive---------34-----------------------

应用于 UFO 数据集的“太空出生”可视化

来自 Unsplash

虽然条形图、直方图、散点图、折线图和箱线图是显示数据并从中发现模式的广泛而有效的工具,但还有其他图形,虽然不太流行,但对于创建优秀的可视化效果仍然非常有用。在本文中,我们将探讨以下几个问题:

1。茎图
2。字云
3。树形图
4。文氏图
5。蜂群剧情

为了让我们的实验更有趣,我们将把它们应用到另一种不太为人所知的物体上:那些不明飞行物🛸.为此,我们将使用 Kaggle 数据集1969 年至 2019 年在北美报道的 UFO 目击事件

首先,我们将导入数据集并进行一些必要的清理。省份缩写是根据美国加拿大对应的维基百科页面整理出来的。

**import** pandas **as** pd
**import** numpy **as** np 

df = pd.read_csv('nuforc_reports.csv')

print('Number of UFO sightings:', len(df), '\n')
print(df.columns.tolist())**Output:**Number of UFO sightings: 88125 

['summary', 'city', 'state', 'date_time', 'shape', 'duration', 'stats', 'report_link', 'text', 'posted', 'city_latitude', 'city_longitude']

数据清洗:

*# Leaving only the necessary columns*
df = df[['city', 'state', 'date_time', 'shape', 'text']]

*# Removing rows with missing values*
df = df.dropna(axis=0).reset_index(drop=True)

*# Fixing an abbreviation duplication issue*
df['state'] = df['state'].apply(**lambda** x: 'QC' **if** x=='QB' **else** x)

*# Creating a list of Canadian provinces*
canada = ['ON', 'QC', 'AB', 'BC', 'NB', 'MB',
          'NS', 'SK', 'NT', 'NL', 'YT', 'PE']  

*# Creating new columns: `country`, `year`, `month`, and `time`*
df['country'] = df['state'].apply(\
                  **lambda** x: 'Canada' **if** x **in** canada **else** 'USA')
df['year'] = df['date_time'].apply(**lambda** x: x[:4]).astype(int)
df['month'] = df['date_time'].apply(**lambda** x: x[5:7]).astype(int)
df['month'] = df['month'].replace({1: 'Jan', 2: 'Feb', 3: 'Mar', 
                                   4: 'Apr', 5: 'May', 6: 'Jun',
                                   7: 'Jul', 8: 'Aug', 9: 'Sep', 
                                   10: 'Oct', 11: 'Nov', 12: 'Dec'})
df['time'] = df['date_time'].apply(**lambda** x: x[-8:-6]).astype(int)

*# Dropping an already used column*
df = df.drop(['date_time'], axis=1)

*# Dropping duplicated rows*
df = df.drop_duplicates().reset_index(drop=True)

print('Number of UFO sightings after data cleaning:', len(df), '\n')
print(df.columns.tolist(), '\n')
print(df.head(3))**Output:**Number of UFO sightings after data cleaning: 79507 

['city', 'state', 'shape', 'text', 'country', 'year', 'month', 'time']          city  state     shape                                    
0     Chester     VA     light 
1  Rocky Hill     CT    circle
2      Ottawa     ON  teardrop

                                      text country  year  month time
0 My wife was driving southeast on a fa...     USA  2019    Dec   18
1 I think that I may caught a UFO on th...     USA  2019    Mar   18
2 I was driving towards the intersectio...  Canada  2019    Apr    2

现在我们有一个清理过的数据集,其中包括 1969 年至 2019 年发生在美国和加拿大的 79,507 起 UFO 目击事件。结果显示,其中绝大多数(96%)与美国有关:

round(df['country'].value_counts(normalize=True)*100)**Output:**USA       96.0
Canada     4.0
Name: country, dtype: float64

让我们最终开始我们的飞碟学实验。

1.茎图

stem 图代表一种修改过的条形图。事实上,它是条形图(特别是那些有很多条的,或者有相似长度的条的)和饼图的很好的替代品,因为它有助于最大化图表的数据-墨水比率,使其更可读和更容易理解。

为了创建一个茎图,我们可以使用stem()函数,或者hlines()vlines()函数。stem()功能在从基线到 y 的每个 x 位置绘制垂直线,并在那里放置一个标记。

我们将开始创建一个基本的每月 UFO 事件的柱状图,只添加一些常见的 matplotlib 定制。对于经典(水平)stem 图,我们可以使用stem()vlines(),结果是一样的。

**import** matplotlib.pyplot **as** plt
**import** seaborn **as** sns

*# Creating a series object for UFO occurences by month, in %*
months = df['month'].value_counts(normalize=True)\
           [['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 
             'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']]*100

*# Defining a function for creating and customizing a figure in matplotlib (will be used for the next 3 plots)*
**def** **create_customized_fig**():
    fig, ax = plt.subplots(figsize=(12,6))
    plt.title('UFO occurences by month, %', fontsize=27)
    plt.ylim(0,15)
    plt.xticks(fontsize=20)
    plt.yticks(fontsize=20)
    ax.tick_params(bottom=False)
    sns.despine()
    **return** ' '

*# PLOTTING*
create_customized_fig()

*# Creating a stem plot*
plt.stem(months.index, months) 

*# ALTERNATIVE WAY TO CREATE A STEM PLOT*
*# plt.vlines(x=months.index, ymin=0, ymax=months)*
*# plt.plot(months.index, months, 'o')*

plt.show()

作者图片

我们看到,美国和加拿大的大多数 UFO 目击事件都与夏秋季节有关,7 月份最多,约为 12%,而在冬春季节活动要少得多,2 月份最少,为 5%。

调整茎图有几个可选参数:

  • linefmt–定义垂直线属性(颜色或线条样式)的字符串。线条可以是实线('-')、虚线('--')、点划线('-.')、虚线(':'),或者根本没有线条。
  • markerfmt–定义杆头标记属性的字符串:'o''*''D''v''s''x'等。,包括没有标记的' '
  • basefmt–定义基线属性的字符串。
  • bottom–基线的 y 位置。

让我们将它们应用到我们的情节中:

*# Creating and customizing a figure in matplotlib*
create_customized_fig()*# Creating and customizing a stem plot*
plt.stem(months.index, months, 
         linefmt='C2:',   *# line color and style*
         markerfmt='D',   
         basefmt=' ') plt.show()

作者图片

还有一些其他属性,如linewidthmarkersize,没有包含在stem()函数的标准关键字参数中。为了调优它们,我们必须创建markerlinestemlinesbaseline对象:

*# Creating and customizing a figure in matplotlib*
create_customized_fig()*# Creating `markerline`, `stemlines`, and `baseline` objects*
*# with the same properties as in the code above*
markerline, stemlines, baseline = plt.stem(months.index, months, 
                                           linefmt='C2:', 
                                           markerfmt='D', 
                                           basefmt=' ') *# Advanced stem plot customization*
plt.setp(markerline, markersize=10)      
plt.setp(stemlines, 'linewidth', 5)      
markerline.set_markerfacecolor('yellow') plt.show()

作者图片

最后,我们可以考虑创建一个竖干图。然而,在这种情况下,我们不能再使用stem()函数,因为它只画垂直线。相反,我们可以将hlines()plot()结合使用。除了必要的参数yxminxmax外,我们还可以调整可选参数colorlinestyle ( 'solid''dashed''dashdot''dotted')。此外,我们在plot()函数本身中有很多选项可以调整,包括颜色、标记和线条。

让我们为 UFO 形状频率分布创建一个垂直茎图,以检查一些形状是否比其他形状更常见:

*# Creating a series of shapes and their frequencies 
# in ascending order*
shapes = df['shape'].value_counts(normalize=True,
                                  ascending=True)*100fig, ax = plt.subplots(figsize=(12,9))*# Creating a vertical stem plot*
plt.hlines(y=shapes.index, 
           xmin=0, xmax=shapes, 
           color='slateblue',
           linestyle='dotted', linewidth=5)
plt.plot(shapes, shapes.index, 
         '*', ms=17, 
         c='darkorange')plt.title('UFO shapes by sighting frequency, %', fontsize=29)
plt.xlim(0,25)
plt.yticks(fontsize=20)
plt.xticks(fontsize=20)
ax.tick_params()
sns.despine()
plt.show()

作者图片

我们看到不明飞行物,根据他们的目击者,可以采取各种各样令人难以置信的形式,包括钻石,雪茄,人字形,泪珠,和十字架。然而,最常见的形式(22%)被描述为光。

在这里,垂直干图看起来是一个更好的选择,因为形状的名称相当长,在水平图中它们会垂直翻转,降低了它们的可读性。

提醒一下,为了创建水平茎图,我们可以使用类似的函数vlines()来代替stem()。除了“镜像”的必要参数xyminymax外,所有参数与hlines()相同。

有了 stem 剧情定制就够了。让我们了解一下我们的朋友外星人的其他情况。

2.词云

词云是一种文本数据可视化,其中每个词的大小表示其频率。使用它,我们可以在任何文本中找到最重要的单词。

我们来分析一下美国目击者给出的所有目击 UFO 的描述。为此,我们将安装并导入wordcloud库(安装: pip install wordcloud),并创建一个基本图形:

**from** wordcloud **import** WordCloud, STOPWORDS*# Gathering sighting descriptions from all American witnesses*
text = ''
**for** t **in** df[df['country']=='USA'].loc[:, 'text']:
    text += ' ' + tfig = plt.subplots(figsize=(10,10)) *# Creating a basic word cloud*
wordcloud = WordCloud(width=1000, height=1000, 
                      collocations=False).generate(text)plt.title('USA collective description of UFO', fontsize=27)
plt.imshow(wordcloud)
plt.axis('off')
plt.show()*# Saving the word cloud*
wordcloud.to_file('wordcloud_usa.png')

作者图片

最常见的词是,其次是工艺。在出现频率最高的词中,有一些低信息量的词,如等。我们可以假设美国目击者大多观察到白色或红色的明亮的飞行器物体,在天空中移动并发光。

在上面的单词 cloud 中,我们使用了以下参数:

  • widthheight——字云画布的宽度和高度。
  • collocations–是否包含两个单词的搭配。我们将它设置为False以避免结果图中的单词重复。

要向我们的词云添加更多高级功能,我们可以使用以下参数:

  • colormap–matplotlib 颜色图,用于为每个单词绘制颜色。
  • background_color–文字云背景色。
  • stopwords–从分析中排除的单词。该库已经有内置的STOPWORDS列表,其中包含一些低信息量的单词,如 hownotthe 等。这个列表可以用用户单词列表来补充,或者用它来替换。
  • prefer_horizontal–尝试水平拟合与垂直拟合的次数之比。如果该参数小于 1,算法将尝试旋转不适合的单词。
  • include_numbers–是否包含数字作为短语(默认为False)。
  • random_state–用于总是复制相同云的种子号。
  • min_word_length–一个单词必须包含最少数量的字母。
  • max_words–单词云中显示的最大单词数。
  • min_font_sizemax_font_size–用于显示文字的最大和最小字体大小。

有了这些新的信息,让我们创建一个更好的词云。我们将为单词和背景颜色添加一个颜色图,将最大单词数从 200(默认)减少到 100,只考虑 3+字母的单词(以避免出现像 uPD 这样的单词),允许更多的垂直单词(0.85 而不是默认的 0.9),从分析中排除一些低信息量的单词,并确保单词云的可复制性。

然而,这一次,我们很想知道加拿大人对 UFO 的集体看法:

*# Gathering sighting descriptions from all Canadian witnesses*
text = ''
for t in df[df['country']=='Canada'].loc[:, 'text']:
    text += ' ' + t*# Creating a user stopword list*
stopwords = ['one', 'two', 'first', 'second', 'saw', 'see', 'seen',
             'looked', 'looking', 'look', 'went', 'minute', 'back', 
             'noticed', 'north', 'south', 'east', 'west', 'nuforc',
             'appeared', 'shape', 'side', 'witness', 'sighting', 
             'going', 'note', 'around', 'direction', approximately',
             'still', 'away', 'across', 'seemed', 'time']fig = plt.subplots(figsize=(10,10)) *# Creating and customizing a word cloud*
wordcloud = WordCloud(width=1000, height=1000, 
                      collocations=False,
                      colormap='cool',
                      background_color='yellow',
                      stopwords=STOPWORDS.update(stopwords), 
                      prefer_horizontal=0.85,
                      random_state=100,
                      max_words=100,
                      min_word_length=3).generate(text)plt.title('Canadian collective description of UFO', fontsize=27)
plt.imshow(wordcloud)
plt.axis('off')
plt.show()*# Saving the word cloud*
wordcloud.to_file('wordcloud_canada.png')

作者图片

加拿大人给出的描述似乎与美国人的描述颇为相似,只是增加了一些其他的常用词:橙色飞机夜晚分钟秒钟飞行速度声音。我们可以假设加拿大人目击了明亮的白色、红色或橙色的飞行器物体,大部分是在晚上,在空中移动/飞行,发出光,可能还有声音。起初,这些物体看起来像星星、飞机或云,整个过程持续了几秒钟到几分钟。

加拿大和美国的集合描述之间的差异可以通过在停用词表中添加更多的词来部分解释。或者,也许,“加拿大”外星人真的更橙色,更像飞机或云,更吵闹😀

3.树形图

树形图是一组嵌套矩形的分层数据的可视化,其中每个矩形的面积与相应数据的值成比例。换句话说,树状图显示了整个数据的组成,是饼状图的一个很好的替代品。

让我们来看看美国哪些州是 UFO 特别喜欢去的地方。我们将安装并导入squarify库(安装 : pip install squarify,并创建一个基本的树形图:

**import** squarify*# Extract the data*
states = df[df['country']=='USA'].loc[:, 'state'].value_counts()fig = plt.subplots(figsize=(12,6))*# Creating a treemap*
squarify.plot(sizes=states.values, label=states.index)plt.title('UFO sighting frequencies by state, the USA', fontsize=27)
plt.axis('off')
plt.show()

作者图片

看起来加州是美国真正的外星基地!紧随其后的是佛罗里达州、华盛顿州和得克萨斯州,而哥伦比亚特区和波多黎各地区很少被不明飞行物访问。

上面使用的参数sizeslabel代表squarify的数字输入和相应的标签文本。可以调整的其他参数:

  • color–矩形颜色的用户列表,
  • alpha–调节色彩强度的参数,
  • pad–是否画出中间有小间隙的矩形,
  • text_kwargs–关键字参数字典(colorfontsizefontweight等)。)来调整标签文本属性。

让我们检查在什么时候看到的外星人最多/最少,同时练习可选参数:

**import** matplotlib*# Extracting the data*
hours = df['time'].value_counts()*# Creating a list of colors from 2 matplotlib colormaps 
# `Set3` and `tab20`*
cmap1 = matplotlib.cm.Set3
cmap2 = matplotlib.cm.tab20
colors = []
**for** i **in** range(len(hours.index)):
    colors.append(cmap1(i))
    **if** cmap2(i) **not** **in** colors:
        colors.append(cmap2(i))

fig = plt.subplots(figsize=(12,6))*# Creating and customizing a treemap*
squarify.plot(sizes=hours.values, label=hours.index,
              color=colors, alpha=0.8, 
              pad=True,
              text_kwargs={'color': 'indigo',
                           'fontsize': 20, 
                           'fontweight': 'bold'})plt.title('UFO sighting frequencies by hour', fontsize=27)
plt.axis('off')
plt.show()

作者图片

我们数据集中的受访者大多在 20:00 到 23:00 的时间范围内观察到 UFO,或者更一般地,从 19:00 到午夜。最不容易发生不明飞行物的时间是 07:00-09:00。然而,这并不一定意味着一天中的某些时段“缺乏外星人”,相反,可以更务实地解释:通常人们在晚上下班后有自由时间,而在早上,大多数人都要去工作,有点太沉浸在他们的想法中,以至于没有注意到他们周围有趣的现象。

4.文氏图

维恩图显示了几个数据集之间的关系,其中每个组显示为一个面积加权的圆圈,圆圈的重叠部分(如果有)表示相应数据集之间的交集及其大小。在 Python 中,我们可以使用matplotlib-venn库为 2 或 3 个数据集创建维恩图。对于第一种情况,软件包提供了venn2venn2_circles功能,对于第二种情况提供了–venn3venn3_circles功能。

让我们在 UFO 数据集的两个子集上练习这个工具。例如,我们希望提取过去 5 年(在我们的数据集上下文中是指从 2015 年到 2019 年,含 2015 年和 2019 年)在北美发生的所有十字形和雪茄形 UFO 目击事件(为简单起见,我们从现在起将它们称为十字形和雪茄形)的数据,并检查是否有一些城市在此期间观察到了这两种形状。让我们安装并导入matplotlib-venn库(安装: pip install matplotlib-venn),为十字架和雪茄创建一个基本的维恩图:

from matplotlib_venn import **# Creating the subsets for crosses and cigars*
crosses = df[(df['shape']=='cross')&\
             (df['year']>=2015)&(df['year']<=2019)].loc[:, 'city']
cigars = df[(df['shape']=='cigar')&\
            (df['year']>=2015)&(df['year']<=2019)].loc[:, 'city']fig = plt.subplots(figsize=(12,8))*# Creating a Venn diagram*
venn2(subsets=[set(crosses), set(cigars)], 
      set_labels=['Crosses', 'Cigars'])plt.title('Crosses and cigars by number of cities, 2015-2019', 
          fontsize=27)
plt.show()

作者图片

从 2015 年到 2019 年(含),北美有 18 个城市注册了十字架和雪茄。在 79 个城市中,只观察到十字(从这两个形状),在 469 个城市中,只观察到雪茄。

现在,我们将从我们的系列中添加一个更奇特的 UFO 形状——钻石——并对维恩图进行一些定制。前面,我们已经使用了一个不言自明的可选参数set_labels。此外,我们可以增加venn2()venn3()功能:

  • set_colors–圆的颜色列表,将根据该列表计算交叉点的颜色。
  • alpha–调节色彩强度的参数,默认为 0.4。

其他两个功能-venn2_circles()venn3_circles()-用于使用参数coloralphalinestyle(或ls)和linewidth(或lw)调整圆的周长。

*# Creating a subset for diamonds*
diamonds = df[(df['shape']=='diamond')&\
              (df['year']>=2015)&(df['year']<=2019)].loc[:, 'city']*# Creating a list of subsets*
subsets=[set(crosses), set(cigars), set(diamonds)]fig = plt.subplots(figsize=(15,10))*# Creating a Venn diagram for the 3 subsets*
venn3(subsets=subsets, 
      set_labels=['Crosses', 'Cigars', 'Diamonds'],
      set_colors=['magenta', 'dodgerblue', 'gold'],
      alpha=0.3)*# Customizing the circumferences of the circles* 
venn3_circles(subsets=subsets,
              color='darkviolet', alpha=0.9, 
              ls='dotted', lw=4)plt.title('Crosses, cigars, and diamonds \nby number of cities, 2015-2019', fontsize=26)
plt.show()

作者图片

因此,在感兴趣的时间段内,北美有 6 个城市注册了所有 3 种形状,66 个城市只注册了雪茄和钻石,260 个城市只注册了钻石,等等。让我们检查这 6 个城市所有 3 种形状的共同点:

print(set(crosses) & set(cigars) & set(diamonds))**Output:**{'Albuquerque', 'Rochester', 'Staten Island', 'Lakewood', 'Savannah', 'New York'}

他们都位于美国。

维恩图可以通过get_patch_by_id()方法进一步美化。它允许我们通过 id 选择任何一个图区域,并改变它的颜色(set_color())、透明度(set_alpha())、改变文本(set_text())和调整字体大小(set_fontsize())。对于双圆维恩图,id 的可能值为'10''01''11',对于三圆维恩图,id 的可能值为'100''010''001''110''101''011''111'。这些值背后的逻辑如下:

  • 数字的数量反映了圆圈的数量,
  • 每个数字按照它们的赋值顺序代表一个数据集(子集),
  • 1 表示该区域中数据集的存在,而 0–不存在

例如,'101'与第一个和第三个数据集所在的区域相关,而第二个数据集在三圆图中不存在,即与第一个和第三个圆的交点(不包括第二个)相关。在我们的示例中,它是十字-菱形交叉,相当于在感兴趣的时间段内只观察到这两种形状的 9 个城市。

让我们试着改变文氏图中相交区域的颜色,并在只代表一个形状的区域中添加短文本而不是数字。此外,为了让它更有趣,让它不只是一个无聊的文本,而是一些反映每个形状的 ASCII 艺术符号:

fig = plt.subplots(figsize=(15,10))*# Assigning the Venn diagram to a variable*
v = venn3(subsets=subsets, 
          set_labels=['Crosses', 'Cigars', 'Diamonds'],
          set_colors=['magenta', 'dodgerblue', 'gold'],
          alpha=0.3)*# Changing the color of the intersection zones*
v.get_patch_by_id('111').set_color('white')
v.get_patch_by_id('110').set_color('lightgrey')
v.get_patch_by_id('101').set_color('lightgrey')
v.get_patch_by_id('011').set_color('lightgrey')*# Changing text and font size*
v.get_label_by_id('100').set_text('✠')
v.get_label_by_id('100').set_fontsize(25)
v.get_label_by_id('010').set_text('(̅_̅_̅_̅(̅_̅_̅_̅_̅_̅_̅_̅_̅̅_̅()~~~')
v.get_label_by_id('010').set_fontsize(9)
v.get_label_by_id('001').set_text('♛')
v.get_label_by_id('001').set_fontsize(35)*# Customizing the circumferences of the circles*
venn3_circles(subsets=subsets,
              color='darkviolet', alpha=0.9, 
              ls='dotted', lw=4)plt.title('Crosses, cigars, and diamonds \nby number of cities, 2015-2019', fontsize=26)
plt.show()

作者图片

最后,可以单独调整任何圆,将venn3_circles()方法的结果赋给一个变量,然后通过索引引用这些圆(在三圆维恩图的情况下为012)。这里使用的方法是不言自明的,类似于上面讨论的方法:set_color()set_edgecolor()set_alpha()set_ls()set_lw()

让我们强调一下钻石的圆圈(每个人都喜欢钻石!🙂💎)

*##### PREVIOUS CODE #####
*  
fig = plt.subplots(figsize=(15,10)) *# Assigning the Venn diagram to a variable* 
v = venn3(subsets=subsets, 
          set_labels=['Crosses', 'Cigars', 'Diamonds'],
          set_colors=['magenta', 'dodgerblue', 'gold'],
          alpha=0.3) *# Changing the color of the intersection zones* v.get_patch_by_id('111').set_color('white') v.get_patch_by_id('110').set_color('lightgrey') v.get_patch_by_id('101').set_color('lightgrey') v.get_patch_by_id('011').set_color('lightgrey') *# Changing text and font size* 
v.get_label_by_id('100').set_text('✠') v.get_label_by_id('100').set_fontsize(25) v.get_label_by_id('010').set_text('(̅_̅_̅_̅(̅_̅_̅_̅_̅_̅_̅_̅_̅̅_̅()~~~') 
v.get_label_by_id('010').set_fontsize(9) v.get_label_by_id('001').set_text('♛') v.get_label_by_id('001').set_fontsize(35) *##### NEW CODE #####* *# Assigning the Venn diagram circles to a variable* 
c = venn3_circles(subsets=subsets,
                  color='darkviolet', alpha=0.9,
                  ls='dotted', lw=4) *# Changing the circle for diamonds by index* 
c[2].set_color('gold') 
c[2].set_edgecolor('darkgoldenrod') 
c[2].set_alpha(0.6) 
c[2].set_ls('dashed') 
c[2].set_lw(6) plt.title('Crosses, cigars, and diamonds \nby number of cities, 2015-2019', fontsize=26) 
plt.show()

作者图片

5.群体图

虽然其更著名的“相对”箱线图在显示总体分布统计数据方面很棒,而不太为人所知的 violin 图描述了一个或几个类别的数据分布,但被低估的 swarm 图提供了关于数据集的一些附加信息。也就是说,它给了我们一个概念:

  • 样本大小,
  • 一个数值变量在一个或多个类别中的总体分布,
  • 各个观察值在分布中的确切位置。

群图中的点沿着分类轴以相互靠近但不重叠的方式进行调整。因此,该图仅在数据点数量相对较少的情况下工作良好,而对于较大的样本,violin 图更合适(对于它们,正好相反,需要足够数量的数据点以避免误导估计)。此外,正如我们很快会看到的,群体图有利于区分来自不同组的单个数据点(最佳不超过 3 组)。

虫群图可以很好地替代或补充盒图或小提琴图。

让我们从 UFO 数据集中提取几个相对较小的子集,为它们创建群体图,并将它们与盒子图和小提琴图进行比较。特别地,我们可以从美国和加拿大选择一个州,提取两个州的所有圆锥形或圆柱形 UFO 目击事件,并观察相应的数据点分布(从 1969 年到 2019 年)。从我们的树状图实验中,我们记得美国最大数量的 UFO 目击事件发生在加利福尼亚。现在让我们来看看加拿大的领导者:

df[df['country']=='Canada'].loc[:, 'state'].value_counts()[:3]**Output:**ON    1363
BC     451
AB     369
Name: state, dtype: int64

因此,我们将选择美国的加利福尼亚和加拿大的安大略作为我们进一步绘图的候选。首先,让我们提取数据并为其创建基本的蜂群图,叠加在相应的箱线图上进行比较:

*# Extracting the data for cylinders and cones 
# from California and Ontario*
CA_ON_cyl_con = df[((df['state']=='CA')|(df['state']=='ON'))&\
                   (df['shape']=='cylinder')|(df['shape']=='cone'))]fig = plt.subplots(figsize=(12,7))
sns.set_theme(style='white')*# Creating swarm plots*
sns.swarmplot(data=CA_ON_cyl_con, 
              x='year', y='state', 
              palette=['deeppink', 'blue'])*# Creating box plots*
sns.boxplot(data=CA_ON_cyl_con, 
            x='year', y='state', 
            palette=['palegreen', 'lemonchiffon'])plt.title('Cylinders and cones in California and Ontario',  
          fontsize=29)
plt.xlabel('Years', fontsize=18)
plt.ylabel('States', fontsize=18)
sns.despine()
plt.show()

作者图片

我们可以在这里作出以下观察:

  • 由于所讨论的数值变量(year)是一个整数,所以数据点是对齐的。
  • 这两个子集在样本量上有很大不同。在蜂群图上可以清楚地看到这一点,而箱形图隐藏了这一信息。
  • 加州的子集严重左倾,包含许多离群值。
  • 没有一个箱线图给我们一个关于底层数据分布的概念。在加利福尼亚子集的情况下,群图显示,有许多圆锥形或圆柱形 UFO 与分布的第三个四分位数以及最近的 2019 年有关。
  • 我们肯定应该在我们的“愿望清单”中加入区分每个子集的圆柱和圆锥的可能性。

因此,我们的下一步将是:

  • 为了从可视化中排除异常值并在 x 轴上放大它,
  • hue参数添加到群体图,能够显示第二个分类变量(shape)。
fig = plt.subplots(figsize=(12,7))*# Creating swarm plots*
sns.swarmplot(data=CA_ON_cyl_con, 
              x='year', y='state', 
              palette=['deeppink', 'blue'], 
              hue='shape')*# Creating box plots*
sns.boxplot(data=CA_ON_cyl_con, 
            x='year', y='state', 
            palette=['palegreen', 'lemonchiffon'])plt.title('Cylinders and cones in California and Ontario', 
          fontsize=29)
plt.xlim(1997,2020)
plt.xlabel('Years', fontsize=18)
plt.ylabel('States', fontsize=18)
plt.legend(loc='upper left', frameon=False, fontsize=15)
sns.despine()
plt.show()

作者图片

现在两个群体图显示,这两个子集的 UFO 占主导地位的大部分是圆柱。对于加利福尼亚州的子集,我们可以区分圆柱形/圆锥形不明飞行物特别频繁出现的年份:2008 年,2015 年和 2019 年。此外,在 2015 年,我们观察到了一次意想不到的球果繁荣,尽管它们总体上要罕见得多。

现在让我们把箱线图分开,比较每个子集的 swarm 和 violin 图。不过,这一次,我们将使用下面的一些参数对虫群图进行更多的自定义:

  • orderhue_order–绘制分类变量的顺序。如果我们创建一个类似上面的虫群盒混合图(或虫群小提琴),我们必须将这个顺序也应用到第二种类型的图。
  • dodge–将其分配给True将沿分类轴分离不同色调等级的条带(如果适用)。
  • markercoloralphasizeedgecolorlinewidth–标记样式('o'默认)、颜色、透明度、半径(5 默认)、边缘颜色('gray'默认)、边缘宽度(0 默认)。
  • cmap–色彩映射表名称。
fig = plt.subplots(figsize=(12,7))*# Creating and customizing swarm plots*
sns.swarmplot(data=CA_ON_cyl_con, 
              x='year', y='state', 
              palette=['deeppink', 'blue'], 
              hue='shape',
              marker='D',              
              size = 8,
              edgecolor='green',
              linewidth = 0.8)*# Creating violin plots*
sns.violinplot(data=CA_ON_cyl_con, 
               x='year', y='state', 
               palette=['palegreen', 'lemonchiffon'])plt.title('Cylinders and cones in California and Ontario', fontsize=29)
plt.xlim(1997,2020)
plt.xlabel('Years', fontsize=18)
plt.ylabel('States', fontsize=18)
plt.legend(loc='upper left', frameon=False, fontsize=15)
sns.despine()
plt.show()

作者图片

在这里我们可以做出如下观察:

  • 与盒图一样,小提琴图不能反映两个子集的样本大小。
  • 小提琴图没有区分圆柱和圆锥。

我们可以通过创建分组的 violin 图来解决最后一个问题(使用参数splithue)。然而,考虑到我们的子集已经相当小,分割它们以创建分组的 violin 图将导致每个部分的样本大小和数据密度进一步减少,使这些图更不具有代表性。因此,在这种情况下,群体图看起来是一个更好的选择。

结论

总之,我们已经探索了五种很少使用的绘图类型,它们的应用情况、限制、替代、定制方式以及分析结果图的方法。此外,我们已经调查了一点神秘的不明飞行物世界。

如果碰巧,现在有一些外星生物正在阅读这篇文章,那么我要感谢他们不时地造访我们的星球。请下次也来我的国家,也许我能更好地想象你👽🎨。

亲爱的读者,谢谢你的关注。我希望你喜欢我的文章,并找到对你有用的东西。

如果你喜欢这篇文章,你也可以发现下面这些有趣的:

**https://betterprogramming.pub/read-your-horoscope-in-python-91ca561910e1 https://medium.com/geekculture/creating-a-waterfall-chart-in-python-dc7bcddecb45 **

空间:如何在公共场合展示你的 ML Web 应用程序演示

原文:https://towardsdatascience.com/spaces-how-to-showcase-your-ml-web-app-demo-in-public-3a701772959?source=collection_archive---------23-----------------------

使用 Spaces、Streamlit 和 Transformers 构建和部署一个简单的机器翻译 web 应用程序

Unsplash 上由 Alvan Nee 拍摄的照片

我认为每个人都同意,能够构建有意义的项目是我们需要在我们的投资组合中拥有的先决条件,以便在我们想要进入数据科学角色时脱颖而出。

幸运的是,现在有很多库、框架和平台,让我们更容易部署和向其他人展示我们的项目和投资组合。空格就是其中之一。

在这篇文章中,我将向您展示在 Spaces 上部署和展示您的 ML web 应用程序演示是多么容易。具体来说,我们将创建一个简单的机器翻译应用程序,使用预训练的变形金刚模型并简化它,然后最终在 Spaces 上部署该应用程序。

下面是我们将在本文中构建的机器翻译应用程序的示例。

作者图片

当然,你可以自己玩这个简单的 ML 应用程序 这里

很有趣,对吧?其他人可以通过访问您的共享空间链接来玩您的 ML 应用程序。在本文中,我将一步一步地指导您如何在 Spaces 上构建这个简单的 ML 应用程序。

但是在我们深入研究实现之前,让我们简单地讨论一下空间。

什么是空间?

Spaces 是拥抱脸创建的一个平台,让你可以直接在你的个人资料上部署机器学习应用程序。要创建应用程序本身,您有两个选择:要么使用 Streamlit,要么使用 Gradio。

Spaces 将存储 git repo 中的所有源代码以及数据集和模型,因此您可以使用 git-lfs 保存像预训练模型一样的大文件。对于每个环境,您还将获得 16 GB 的 RAM 和 8 个 CPU 内核。

虽然 Spaces 是由 Hugging Face 创建的,但是您可以使用现有的各种包和库创建任何符合您兴趣的 ML 应用程序。因此,在构建自己的 ML 应用程序时,您可以尽可能地发挥创造力。

现在,让我们首先创建构建机器翻译的代码。

用变形金刚构建机器翻译

为了创建机器翻译,我们不需要从头开始构建和训练模型。相反,我们将使用一个预先训练好的变形金刚模型。在加载模型后,我们可以开始用英语编写我们的输入文本,然后模型会将其翻译成德语或法语。

作者图片

我们将要使用的预训练模型是 T5 模型,它是Text-To-TextTtransferTtransformers 的缩写。我们要用变形金刚库加载这个模型,所以确保你先安装它。

pip install transformers

接下来,我们可以用下面的代码加载预先训练好的模型。

T5 使我们能够完成不同的 NLP 任务,如文本翻译、文本摘要、文本分类,甚至回归任务。由于它的多功能性,我们需要添加一个前缀来告诉 T5 模型应该执行哪些任务。

因为我们希望模型翻译文本,所以我们需要添加一个前缀,如下所示:

translate English to {target_language}

其中 T5 型号支持的target_language是德国、法国和罗马尼亚。

除了加载模型之外,请注意,在上面的代码中,我们还从 T5 模型中加载了一个预训练的标记器。

这个标记器负责将我们的输入文本转换成 T5 模型期望的输入的所有必要转换。使用这个记号赋予器,我们的输入文本将被转换成一系列记号,然后每个记号将被转换成它的 id 表示。

然后,来自记号赋予器的输出将被馈入 T5 模型。下面是一个例子,说明我们如何提供输入文本,由预先训练的分词器进行分词。

这是我们从上面的output变量中得到的:

  • input_ids —这是我们输入文本中每个单词的 id 表示。
  • attention_mask —这是一个二进制掩码,告诉 T5 哪个单词是真实单词,哪个只是填充。注意力屏蔽是我们 T5 模型的可选输入。

接下来,我们应该调用模型,用generate()函数预测 tokenizer 的输出,如下所示:

最后,我们需要将 T5 模型的预测解码回文本表示,如下所示:

这就是所有的步骤!很简单吧?

T5 模型一次只能翻译一个句子,所以如果我们有一个由几个句子组成的文本,我们需要将文本分割成一个句子列表。我们可以通过 NLTK 库中的sent_tokenize()函数轻松做到这一点。然后,我们用 T5 分词器对列表中的每个句子进行分词,如上所示。

下面是在我们将 NLTK 集成到机器翻译中之后,使用预训练的 T5 模型构建机器翻译的完整代码实现。

接下来,让我们用 Streamlit 创建我们的应用程序。

使用 Streamlit 构建 ML 应用程序

Spaces 目前支持两个 SDK 或库,我们可以选择来构建应用程序:要么使用 Streamlit,要么使用 Gradio。在本文中,我们将使用 Streamlit 来实现这一目的。如果你以前没有用过 Streamlit,那么我推荐你去看看,因为它真的很简单易学。使用 Streamlit,您不必处理前端开发和路由。

要使用 Streamlit,请确保首先 pip 安装它(如果您尚未安装)。

pip install streamlit

作为第一步,让我们定义一个函数来加载我们预先训练的 T5 模型,然后在函数定义上方放置一个缓存装饰器st.cache()

缓存装饰器在这里非常重要,因为我们不希望我们的应用程序在每次用户使用我们的应用程序时都持续运行函数来加载我们的 T5 模型。

之前我们已经用预训练的 T5 模型建立了我们的机器翻译,但是你可能会注意到一切都是硬编码的。到目前为止,我们对输入文本和目标语言进行了硬编码。

使用 Streamlit,我们需要让用户自己输入文本。此外,如果他们可以选择输入文本应该翻译成哪种语言,是德语还是法语,那就更好了。

现在让我们创建一个交互式特性,让用户在侧边栏中选择目标语言。由于用户只能选择德语或法语,因此选择框将是实现这一目的的完美小部件。我们可以使用 Streamlit 中的st.selectbox来创建一个选择框小部件。

为了使应用程序不那么空洞,我们可以用st.title为我们的应用程序创建一个标题,并用st.write创建普通文本来解释应用程序。你可以随意添加任何你想要的东西,尽你所能发挥你的创造力。点击 查看 Streamlit 上提供的完整 API。

现在我们需要创建另一个交互式特性,让用户键入他们想要翻译的输入文本。要做到这一点,来自 Streamlit 的st.text_area小部件将是完美的选择。

最后,用户的输入将按照我们在上一节中实现的相同步骤进行处理。

下面是使用 Streamlit 构建我们的机器翻译应用程序的完整代码实现。

我们把上面的脚本保存为app.py。现在,让我们尝试运行 web 应用程序。

要在您的电脑上运行 streamlit 应用程序,您需要打开终端或 anaconda 提示符。然后,在终端中,转到保存app.py的目录。在目录中,您可以键入以下内容:

streamlit run app.py

运行上面的命令后,应该会弹出一个新窗口,显示如下截图所示的应用程序。

作者图片

请注意,到目前为止,我们已经成功地用 Streamlit 构建了我们的 web 应用程序,但是我们只能在我们的计算机上本地运行它。现在是我们公开展示我们的 web 应用程序的时候了,这样其他人就可以玩它了。

这就是我们需要空间的地方。

部署带空格的 ML 应用程序

正如我在文章开头提到的,在 Spaces 上部署您的 ML 应用程序非常容易。我们所需要的先决条件是一个拥抱脸帐户。

如果你还没有账户,你可以直接在拥抱脸网站上注册一个新的。接下来,您需要提供您的电子邮件地址和密码来完成注册。

如果一切顺利,那么在您登录到您的新帐户后,您将看到以下页面。

作者图片

要创建新空间,您可以转到导航栏右上角的个人资料图片,然后选择“新空间”。

作者图片

接下来,您可以为您的 ML 应用程序创建名称,选择构建 web 应用程序的库,并选择您的应用程序是公开还是仅私有。因为我们使用 Streamlit 来构建我们的 web 应用程序,所以您需要选择 Streamlit 作为 SDK,如下所示。最后,点击创建空间

作者图片

现在,您应该会看到您的共享空间的登录页面,如下所示。

作者图片

这个页面实际上是一个 git repo,为了在我们的空间上部署我们的机器翻译 web 应用程序,我们需要添加两个附加文件:

  • requirements.txt —这是一个文本文件,包含我们构建应用程序所需的所有依赖项或库。
  • app.py —这是我们在上一节刚刚创建的 Streamlit 脚本。

将这两个文件添加到我们的空间页面非常简单。首先,我们来创建requirements.txt

为此,我们需要点击'文件和版本选项卡,然后点击'添加文件并选择'创建新文件'。

作者图片

接下来,将文件命名为requirements.txt,然后列出运行我们的应用程序所需的所有库。在这个简单的机器翻译应用程序中,我们需要 Transformers、Streamlit、PyTorch 和 NLTK,所以我们必须将它们包含在requirements.txt中。最后,点击提交新文件

作者图片

到目前为止我们已经创建了requirements.txt。现在我们需要创建app.py作为最后一步。创建该文件有两个选项:

  • 您可以创建一个新文件,将该文件命名为app.py,然后将我们实现的所有代码粘贴到该文件中,以使用上面的 Streamlit 构建应用程序。为此,点击“添加文件”,然后选择“创建新文件”,步骤与上面的requirements.txt相同。
  • 或者,您可以将我们在上一节中实现的app.py脚本直接上传到这个“文件和版本”选项卡中。为此,单击“添加文件,然后选择“上传文件”。

在下面的截图中,我们用第一个选项创建了app.py。所以我们将这个文件命名为app.py,并将我们之前实现的所有代码粘贴到这个文件中。之后,点击“提交新文件”。

作者 Imae

就是这样!如果您点击“应用”选项卡,您将看到我们的简单机器翻译应用现已上线,其他人也可以访问。您可以通过共享共享空间的链接向其他人展示您的 ML 应用程序。

作者图片

结论

在本文中,现在您知道在 Spaces 上向其他人展示您的机器学习项目是多么容易和简单。

有一些库和平台使我们能够展示我们的创造力和技能,以执行端到端的机器学习项目,从数据收集开始,直到模型部署。

特别是 Spaces,你可以构建和部署任何符合你兴趣的机器学习应用程序,它不一定是关于机器翻译的。我希望这篇文章对你建立和扩展你的投资组合项目有所帮助。

如果你想更深入地探索太空,你可以阅读它的文档。

像往常一样,你可以在这里 找到本文中实现的源代码。

SMS 消息中的垃圾邮件检测

原文:https://towardsdatascience.com/spam-detection-in-sms-messages-3322e03300f5?source=collection_archive---------13-----------------------

真实短信垃圾邮件检测综述

介绍

米卡·鲍梅斯特在 Unsplash 上的照片

本文中的每一项都是在一个跨越多个国家的大型社交应用的背景下考虑的。用户可以向任何号码发送一定数量的免费短信,在互联网连接非常有限、移动数据费用非常高的地区,这被证明是一项非常好的服务。

这当然吸引了骗子和欺诈者的注意,他们看到了非常便宜地吸引许多人关注他们有问题的业务的机会,这导致许多人试图滥用该平台。

该模型和相关程序是针对这种情况开发的,以保持应用程序中用户体验的质量。

本文中描述的模型和程序是在 XpertAI 内与 Filippo Monari 合作完成的。

Discalimer:出于隐私原因,本文中显示的示例是原始示例的修改版本,保留了感兴趣的属性,以便分析垃圾邮件检测。

什么是垃圾邮件,为什么要阻止垃圾邮件

垃圾邮件是以电子方式发送的不请自来且不受欢迎的消息,其内容可能是恶意的。垃圾电子邮件通过互联网发送/接收,而垃圾短信通常通过移动网络传输。我们将发送垃圾邮件的用户称为“垃圾邮件制造者”。用户发送 SMS 消息通常非常便宜(如果不是免费的话),这使得它对不正当的利用很有吸引力。用户通常认为 SMS 是比其他来源(例如电子邮件)更安全、更值得信赖的通信形式,这一事实进一步加剧了这种情况。

垃圾消息对用户的危险是多方面的:不受欢迎的广告、私人信息的暴露、成为欺诈或金融阴谋的受害者、被引诱进入恶意软件和网络钓鱼网站、无意中接触到不适当的内容等。对于网络运营商来说,垃圾短信导致运营成本增加。

在所研究的案例中,垃圾邮件是用户的烦恼,因此不利于服务质量,在这个过程中损害了品牌。这可能会导致投诉,收视率低,甚至失去用户,更不用说用户被骗。

与垃圾邮件的区别

下表总结了电子邮件和短信中垃圾邮件的主要区别。

垃圾短信与垃圾邮件

垃圾邮件发送者的行为

垃圾邮件发送者试图通过发送不同数量的垃圾邮件来测试运营商的反垃圾邮件基础设施,以确定是否存在数量障碍。对于他们来说,使用多个号码发送消息是非常常见的,这就排除了号码阻止作为防止垃圾邮件的策略。这种情况需要某种基于内容的过滤,这种过滤不仅依赖于音量,还依赖于 SMS 本身的内容。

服务中垃圾邮件过滤的状态

在项目开始时,针对垃圾邮件发送者的行动仅包括禁止在发送 SMS 的数量方面超过固定的每日和每小时阈值的用户。当时没有基于内容的过滤,也没有任何基于用户元数据的考虑。这是一个基于规则的系统,很容易绕过,利用的数据很少。

困难

  • 没有公开的垃圾短信大数据集。即使有,也不能指望在这些数据集上的训练会在我们的环境中转化为良好的表现。因此,除了从流入系统的真实数据构建自定义数据集之外,别无选择。
  • 缺少将 SMS 日志转换为结构化的干净数据集的管道。
  • 该应用程序在许多国家和语言中可用,增加了另一层复杂性。
  • 最终模型必须部署并集成到应用程序的当前基础设施中,采取必要的预防措施,以避免导致高成本和消息传递延迟。
  • 标签的主观标准:屏蔽宗教宣传可以吗,即使它不是为了欺诈或骗局?如果这个消息被传播给成千上万的用户呢?
  • 消息不明确:人们甚至很难区分真正的消息和垃圾邮件。

例句

以下是基于真实邮件的示例(并非所有邮件都是垃圾邮件):

-你的包裹正在等待递送📥bit.ly/xxxxxxx

你的手指有多快?立即测试!->https://play.google.com/store/apps/details?idxxxxxxx)

-晚上好,先生,恢复愉快。我今天打了你的电话,但是你没有接。请把钱转到我的账户上。

-我用 PayTree 送你到€ 25 日注册与我的链接索赔,然后得到€30 万赠送基金:https://palmpay.site/QXAfLgKsPhKA

-2021 年 4 月 22 日,您的手机支付账户收到了来自约翰·卡彭特(+1)111111111 的 150 美元。发件人:交易 ID: 2901380912。为了成功提款,Ecobank 将联系您进行更多查询。

-关注此链接加入我的 WhatsApp 群:http://unnoficial _ WhatsApp . com/download

-请检查并发送给我,我的成绩 100 级第三年。姓名:约翰-保罗吉莉安 Jambaya 电子邮件:【johnpaulGillianJ@gmail.com】T2Jamb 注册号:49851407SA 公制号码。YU/14/6337 密码:2352246811678。我会付钱给你。

数据收集、提取和解析

照片由西格蒙德Unsplash 上拍摄

在这里,我们介绍了所采取的措施和部署的系统,以便从负责向最终用户发送 SMS 的运营商提供的非结构化日志中构建合适的数据集。每次发送短信都会产生大量数据,但这些数据无法以原始形式使用。

从它们的 S3 存储中收集日志,解密并解析,以便从 SMS 消息中分离日志的元数据。在此阶段,消息被分类为发送接收OTP失败。 OTP 代表一次性密码,由 app 发送进行注册登录,失败表示发送时报错。不同于垃圾邮件,OTP 还遭受其他类型的欺诈,这可能是另一篇文章的主题。

之后,执行另一个解析步骤,以提取消息体、元信息(如发送者的电话号码)、区分群组和对等消息,并将实际消息体与自动生成的内容(如链接到应用下载的页脚或指向服务主页的超链接)分开。接下来是从 UTF-16_LE 编码中解码 SMS 字符串,这被发现比其他可用的替代方法效果更好。

流水线的最后处理步骤包括使用快速文本模型来检测消息的语言以及作为所选语言的样本的概率分数。

最后,处理后的消息存储在另一个 S3 存储桶中,该存储桶连接到 Athena 表(通过使用 Crawler 每天更新),这些表提供了一种浏览解析后的消息和在数据集上运行 SQL 查询的便捷方式。由于数据量巨大(数百万条消息),解析步骤是在运行 Spark 应用程序的集群上执行的。

下图描述了已部署的 ETL 管道,用于收集和处理构建数据集所需的日常日志:

用于创建消息数据集的管道

建模

迈克尔·泽兹奇Unsplash 上拍摄的照片

垃圾邮件检测问题可以用几种方法解决,从非常简单的(如一袋单词)到非常复杂的(完整的 NLP 模型)。
我们决定采用一种中间方法,对邮件的许多特征进行分析,并输入到机器学习算法中,以发现有助于区分垃圾邮件和非垃圾邮件的潜在模式。

问题类型

垃圾邮件分类历来被认为是一个二元分类问题。这就是我们的方法揭示其最原始的方面:我们放弃了二元分类问题,转而支持回归问题,其目标是预测消息的垃圾邮件概率。

稍后,这些垃圾邮件概率将与一个阈值进行比较,这将导致以下操作:

  • 允许消息
  • 允许消息并存储警告计数器
  • 阻止消息
  • 阻止消息和用户

型号类型

选择的模型架构是 XGBoost ,一个基于梯度增强技术的优化分布式梯度增强库。它被设计成高效的灵活的便携的,被认为是只有数字特征的表格数据的最佳执行者之一。

XGBoost 是一个集合模型,由一系列弱学习者组成(至少比随机猜测要好),这些弱学习者作为一个“委员会”一起工作,比单独提供更好的预测。由于新的学习者被顺序地添加到组中,以关注前一个学习者的最大错误,这些相对于简单的“投票”得到了改进。这些弱学习器通常是决策树,其工作方式是沿着选定的特征在某些值上分割数据集。

即使该问题现在是目标在 0 和 1 之间的回归问题,我们仍然使用输出概率的二元分类的 二元:逻辑 损失来训练模型。

贴标程序

手动标记数千条消息是不可行的,更不用说数百万条了。很明显,在项目开始时,产生一个具有正确标签的精心策划的数据集将是需要克服的最大障碍,事实也的确如此。

为了解决这个问题,我们想出了一个基于非常简单的原则的方法:垃圾邮件经常以高频率的突发发送。这源于这样一个事实,即垃圾邮件发送者希望让他们的内容,无论是欺诈,骗局,宣传或其他任何东西。这个想法是从一个有序的消息列表开始,其排序参数我们称之为垃圾邮件分数。这将表示对消息发送次数的一些计算,并且有许多可能的选择。最简单的方法是简单地对给定消息的出现次数进行计数,但是通过考虑例如:多少不同的用户在多少不同的日子里发送相同的消息,连续消息之间的时间差等等,这可以变得更复杂(不一定更好)。
最后,每条消息都会有一个关联的垃圾邮件分数,用于确定垃圾邮件概率。

从分数到概率

一旦我们有了有序的消息列表,我们需要将垃圾邮件分数转换成垃圾邮件概率。这些概率的范围从 0 到 1,并作为我们的监督学习问题的标签。

注意:我们非正式地使用了术语概率,因为垃圾邮件概率并不代表邮件是垃圾邮件的实际概率,除非模型经过校准,但事实并非如此。

将垃圾邮件分数从 0 转换到+∞的最直接方法是对这些值应用位移的 sigmoid 曲线。这将确保我们的值始终位于[0,1]范围内。

更详细地说,过程如下。由于垃圾邮件分数的范围非常大,我们首先取对数来获得日志垃圾邮件分数。由于有一个唯一的 sigmoid 通过 2 个不同的点,一旦我们选择了对 (q1,p1) (q2,p2) ,我们就已经确定了将垃圾邮件分数转换为垃圾邮件概率的 sigmoid 函数。这些对代表(百分比,概率)超过日志垃圾邮件分数。

使用这些参数,可以拟合以下形式的 S 形曲线:

通过确定 ab 的值,使 s 形线通过之前选择的线对。

注意,当 x 趋于无穷大时,S 趋于 1,对于所有 x,S(x) > 0。

最后,日志垃圾邮件分数通过 sigmoid 函数传递,以获得用于训练的每封邮件的垃圾邮件概率。

在我们的例子中,我们选择(0.75,0.05)和(0.99,0.90)来拟合乙状结肠。这意味着我们已经决定,所有邮件中较低的 75%的垃圾邮件概率最多为 5%,而最低的 99%的邮件的垃圾邮件概率为 90%或更低。

尽管它们不在实际建模范围内,但这 4 个值可以被视为管道的超参数,因为它们有助于标签的获取,从而强烈影响训练的有效性。
为这些参数选择错误的值会将所有垃圾邮件概率聚集在一个值周围,这甚至可能导致无法获得任何有意义的训练。不幸的是,设置这些参数高度依赖于数据,因为不同的数据集会有不同数量的垃圾邮件。

分数分布

以下是垃圾邮件日志分数按特定百分比的分布情况:

下图显示了左侧 y 轴上的垃圾邮件日志分数计数。请注意 y 轴上的对数刻度,值 0(对应于发送一次的消息)是目前最常见的值。还显示了指示垃圾邮件日志分数百分比的垂直线。

日志垃圾邮件分数在训练集上的分布

接下来,显示垃圾邮件概率。图中显示了用于拟合 sigmoid 函数的点,以及一些示例消息和它们被分配的垃圾邮件分数。

基于通过 sigmoid 函数传递的消息及其对应的指定垃圾邮件概率的示例

标签的改进

即使前面描述的标记选择是明智的,但在某些情况下,邮件频率本身并不是垃圾邮件得分的良好衡量标准。这些情况包括垃圾邮件,它们遵循相同的模式,但可能在 URL 的数字或字符上有细微的变化。
此外,诸如“
早安”等非常常见的消息也将被赋予较高的垃圾邮件分值。考虑到最小消息长度,这个问题在很大程度上得到了缓解。
最后,合法的广播消息(如群体事件或公告)会被发送几次,这将赋予它们中等的垃圾邮件概率。然而,这可能是一个合理的标签,因为它可以帮助模型学习为大量不纯粹是垃圾邮件的消息分配中间概率。

为了改进一些标签,我们引入了一些非常简单的硬编码规则,用于我们绝对确定某个消息是否是垃圾邮件的情况。在前一种情况下,我们人为地增加消息发送次数的值,以便能够重用当前的管道,而在后一种情况下,假设消息已经发送了一次。

把问题当作倒退来处理的后果

重要的是要注意,当决定放弃二进制分类问题,而是将其重新定义为输出垃圾邮件概率的回归问题时,我们在结果中引入了一个基础结构,即消息不仅仅是垃圾邮件/垃圾邮件,而是有一个垃圾邮件等级来反映消息的垃圾程度。
这种结构在其他分类问题中可能毫无意义,在某些情况下甚至会恶化性能。然而,在我们的问题中,模型基于预测的垃圾邮件概率产生的额外“排序”实际上是一种优势,因为它可以在分类中提供一些麻烦。
换句话说,接收到中等垃圾邮件概率的消息可以反映该消息的模糊性,并且可以被保存以供人类稍后检查,而明显是垃圾邮件/ham 的消息将累积到 1/0,并且实际概率值变得有些不相关。

语言

由于短信来自一个多国消息应用程序,这些消息的语言当然是多种多样的。英语和法语是第一和第二广泛使用的语言,但也发现了数十种其他语言,包括阿拉伯语、汉语和非洲当地语言。
输入到模型中的一些特征对语言也有很强的依赖性。这意味着,为了使用它们,我们需要一个管道来计算任何语言的这些特征(每种语言的数据量最小,以使模型学习任何有意义的东西),以将问题限制在一种语言上,在我们的例子中是英语。鉴于垃圾邮件绝大多数是用英文写的,可能是因为垃圾邮件发送者试图扩大受众范围,因此只分析英文邮件似乎是明智的选择。我们使用 60%作为接受语言检测的最低置信度。
尽管如此,语言不可知模型也是一种选择,其中仅考虑独立于文本语言的特征,尽管由于许多特征的损失,预测能力可能降低。
必须澄清的是,短信的语言是根据消息的
干净版本进行评估的。在这个上下文中,clean 意味着一个标记化的,消息的小写版本,其中一些特征被替换为占位符(参见建模部分末尾的一些示例)。

消息长度

大多数真实消息很短,而大多数垃圾消息明显长于平均长度。这是因为垃圾邮件需要包含某种方法,以 URL、电子邮件、电话号码的形式引诱消息的接收者。因此,通过仅考虑超过特定长度的消息来节省训练和推断时间的资源是有意义的。

最小消息长度的合理范围是 30 到 50 个字符,因为垃圾消息中的 URL、电话号码和电子邮件占据了许多字符。这可能因问题和生成消息的上下文而异。垃圾邮件发送者在注意到对策被应用后缩短和缩写他们的消息是很常见的。这可能需要修改最小消息长度,需要在现在更大的数据集上重新训练模型。

特色工程

术语特征指的是所考虑的 SMS 消息的特征、质量和/或属性。在许多情况下,一些特征值是很少出现的概念,因此只提取它们的计数,而不是实际内容。由于可处理内容的稀缺性,特征工程在垃圾短信检测中起着非常重要的作用。

做出的第一个决定是删除原始(未处理)长度少于 40 个字符的任何消息。接下来,随着特征被提取,内容逐渐被清除,直到获得消息的最终干净的“标记化”版本,其中仅留下字符。
对于手头的问题,使用了以下特征:

  • 垃圾词(见下一节)
  • 消息的一般 特征
    —长度
    —清理前后的字数及其比率
    —平均字长
  • 特定概念的出现次数及占总字数的比例。
    —电子邮件地址
    —通用网址
    —时间
    —工作日/月份
    —电话号码
    —宗教词汇和内容
  • 特定字符
    —大写字符
    —货币符号
    —数字
    —标点符号(星号、感叹号/问号、标签等)的出现次数及占字符总数的比例。)
    —消息中不同字符的数量

还使用了其他不常用的功能。

需要注意的是,在模型的训练阶段,并不是所有这些功能都会用到。根据结果和新数据的引入,较新的模型迭代可能具有不同的特性。

垃圾词汇

这组单词被选择来为每个消息生成与它们相关联的特征。
这些功能包括:

  • 邮件中的垃圾邮件字数
  • 独特的垃圾词
  • 垃圾词与非垃圾词的比率
  • 统计垃圾邮件单词出现次数的个人特征

起初,垃圾邮件单词是根据它们在垃圾邮件中出现的次数来选择的。这当然会导致一些正常的单词被认为是垃圾单词,而且从结果来看,这需要付出不值得的努力。因此,基于对最常见的垃圾邮件消息和这些单词可能携带的信息的简单观察,为模型选择了一组 20 个手动选择的单词。这些垃圾词的例子有金钱免费等。

重要提示:尽管名称如此,这些词并不意味着垃圾邮件的嫌疑。模型使用它们来创建与每个单词相关联的特征。

模型评估

每个监督训练模型将测量通过比较真实和预测标签获得的损失。通常,垃圾邮件检测是一个分类问题,因此用于评估模型性能的指标是常见的指标,如准确度、召回率、精确度、f1 值。在我们的例子中,我们可以依赖模型用于训练的损失函数,但它不是人类可以解释的。

因此,由于缺乏完全验证的标签,模型的评估是通过在训练的任何阶段都没有使用的测试集上人工检查模型的预测来完成的。该集合包含特别选择的消息。

在每个评估步骤都进行了定性考虑,通过改变训练参数的特征集、调试流水线中的步骤和改进标记,模型的最终责任得到了纠正或显著降低。

其他需要考虑的事情

运行时间和成本

每次调用 Lambda(AWS 定价)并运行模型时,将模型作为 AWS Lambda 运行都会产生成本。计费金额基于使用的内存和运行时间。部署的模型非常轻量级和快速,每次运行使用不到 1000Mb,占用不到 10ms。对于未处理的消息,即那些因为太短而没有通过模型的消息,官方或非英语)运行时间大约为 2 毫秒。作为参考,在一天中有 80k 条未处理的消息和 27k 条消息通过模型,总成本大约为四分之一美元。

硬编码规则

在有些情况下,垃圾邮件发送者的活动量如此之大,以至于必须在短时间内交付快速而完整的解决方案。这意味着需要做出妥协,一个纯粹的、正统的 ML 解决方案必须给更多的 ah-hoc 方法留有余地。例如,在一个小时内看到数以千计的消息以前所未见的模式流入后,简单地阻止前 20 个字符与该消息中看到的字符完全相同的消息的硬编码解决方案似乎是一个合理的临时解决方案。稍后将有时间将这些消息添加到训练集,重新训练和重新部署。

固定消息

相当一部分消息是由应用程序本身生成的,作为对所提供内容的推广。此时的一个选项是将这些消息包括在模型的训练中,并给它们分配 0 垃圾邮件概率。然而,我们已经选择将这些消息从训练中完全排除,并且它们也不会在推断时被模型评估。因为它们包含预先已知的字符串模式,所以在将输入提供给模型之前设置一个简单的过滤器可以防止对它们进行评估,这对交付时间几乎没有影响。

更新型号

几乎可以保证,在模型部署之后,反对垃圾邮件的斗争不会结束。无论有多少单元测试和集成测试,当模型被部署在真实的环境中时,错误和意外的行为几乎肯定会出现。这些错误包括破坏模型(例如,不正确的消息解析、错误的字符串编码、错误的数据格式输入数据等。)到最无害的(例如,正则表达式中未考虑的边缘情况、时区差异)。

垃圾邮件发送者报复

垃圾邮件发送者可以非常坚定地让他们的“内容”被理解,一旦他们发现我们意识到他们的企图,他们就会改变他们的行为。
他们将测试系统以确定适当的检查和阻止,以便绕过它们,如果系统依赖记忆或固定规则,则稍微修改他们的消息,测试消息长度和消息量的阈值等。

以下是一个非常活跃且持续的垃圾邮件发送者的示例:

-请将您的姓氏发送到tommadeira128@gmail.com以获得在您的姓氏上发现的无人认领的资金。
-请将您的姓氏发送到tommadeira128777@gmail.com领取您的无人认领基金
-在您的姓氏上发现了一笔无人认领的基金将您的姓氏发送到tommasmad173@gmail.com进行索赔和验证,以及有关该基金的更多查询。
-tommasmaderira25@gmail.com进行核实和索赔
-将您的姓氏发送到tommadeira128128@gmail.com4 索赔
-请将您的姓氏 2tommade25@gmail.com发送给一笔在您的姓氏上发现的无人认领的资金
-Snd ur 1 name 2tommadeira777@gmail.com4 ur 检测到的遗产
-姓氏 2tommadeira128@gmail.com4 一笔在您的姓氏上发现的无人认领的资金
-您的姓氏 2

垃圾邮件发送者的报复需要定期更新模型,以包括新的垃圾邮件模式,希望使发送垃圾邮件变得如此困难,以至于垃圾邮件发送者投入的时间回报不再有利可图。

部署

一旦模型被训练完成,它就被打包成一个 docker 映像。这个图像然后被上传到 AWS,并作为 Lambda 函数在它自己的容器中运行。这种类型的无服务器部署具有许多优势,因为它非常易于扩展和更新,并且允许随时随地调用该模型,而无需担心服务器和基础架构。

对于我们的应用程序,每当消息请求到达 XMPP 消息服务时,就会调用该模型。如果消息的内容超过了最小长度,那么它将被发送到模型,该模型将确定消息的语言并对其内容运行垃圾邮件预测器。然后,该模型将返回一个 json 文件,其输出包括语言、时间戳、调试特性(如果需要)以及垃圾邮件概率。

总结

下图总结了训练垃圾邮件模型及其在真实环境中的集成所涉及的步骤。

训练和开发垃圾邮件检测系统的工作流程

空格之间的虚线表示来自训练和预测管道的交互。

  1. 真实数据将为模型的未来迭代提供信息
  2. 在训练阶段定义的特征提取器必须在真实环境中完全相同。否则,模型要么抛出不兼容错误,要么产生意想不到的结果。
  3. 在训练之后,一个令人满意的模型被部署到一个真实的环境中。首先,在影子模式下测试一段时间,在不中断服务的情况下获得评估指标。推理时间也在这个阶段被测试。一段时间后,模型被完全引入到生产环境中。
  4. 引入模型后获得的结果应该被记录和保存,以获得性能测量。根据这些,我们可以决定你重新培训和重新部署。预计当新的 SMS 分布偏离训练集中的分布时,性能会随着时间而恶化。

结果

卢克·切瑟Unsplash 上拍摄的照片

总的来说,有可能实现一个模型能够相当好地识别垃圾邮件,而不惩罚正常的消息。该模型似乎正在推广训练集中没有的模式。简单的垃圾邮件案例很容易被检测到,在这些案例中,相对于之前已经看到的垃圾邮件,只有一个数字或一个单词发生了变化。

还有一个非常有趣的消息排序,其中非明确垃圾邮件的消息被分配中间概率。通过仅在这些不明确的消息上进行人工标记,这使得为模型的下一次迭代产生更好的标签变得容易。
此外,几乎没有误报,这意味着正常邮件几乎不会被阻止。

基于垃圾邮件概率的后续行动

一旦模型返回垃圾邮件概率,就可以对发件人和邮件采取行动。
一种选择是在模型之外包含一些硬编码的逻辑(比如当前基于消息频率的逻辑)。

例如:

  1. 设置预定义的百分位数 Q1 和 Q2,并获得对应于这些百分位数的垃圾邮件概率的阈值 T1 和 T2。这些值将根据目标是 召回 (最大限度地检测垃圾邮件)还是(最大限度地提高正确标记为垃圾邮件的邮件的百分比)而变化,并且应该由最好具有领域知识的产品所有者来决定。**
  2. 跟踪和记录垃圾邮件概率超过 T1 和 T2 的消息、消息的发送者和接收者。
  3. 如果消息超过 T1,有几个选项:

app 推广——暂时什么都不做

—在发送消息之前用确认按钮提示用户,以避免出现多条消息

**—直接阻止消息,而不通知用户

4。如果消息是关于 T2 的,考虑与前一种情况相似(但更严格)的选项。

5。对通过 T1 和 T2 发送消息的用户进行计数**

N 注:最好说分位数而不是实际阈值,因为重新训练模型会改变某个分位数所在的阈值。因此,分位数提供了一种跨时间和不同模型迭代对消息进行分类的更稳定的方式。

使用垃圾邮件概率

在模型被训练的上下文中解释模型的结果是很重要的。如建模部分所述,标签是通过根据一些手动选择的标准拟合一个 s 形曲线而获得的。因此,结果应该用那个标准来理解。0.1 的垃圾邮件概率可能看起来很低,但在我们的情况下,它高于所有邮件的 70%。事实上,只有 10%的邮件被分配了高于 0.5 的垃圾邮件概率。

对垃圾邮件概率采取的任何行动都应该考虑这个训练参数。一种方法是考虑基于给定百分比的垃圾邮件概率而不是固定数字来设置阈值。例如,将阈值设置为只有 1%的邮件超过的垃圾邮件概率值。这当然需要了解真实邮件(测试集)中垃圾邮件分数的分布情况,但是如果这些值来自相同的来源,并且发生在不久之前,那么用来自训练集的值来近似它是合理的。

在任何情况下,当决定如何解释垃圾邮件概率时(例如当选择阈值时),建议考虑在评估阶段使用的选定消息。

例题

下面是一些邮件解析、特征提取和垃圾邮件概率预测的例子。仅显示了一小部分功能。

例 1:垃圾 app 推广

示例 2:应用推广消息、解析和特征提取

示例 2:正常消息

示例 2:普通消息、解析和特征提取

示例 3:带有表情符号的普通消息

示例 3:带有表情符号的普通消息、解析和特征提取

例 4:可疑企业的垃圾邮件。

示例 4:垃圾消息、解析和特征提取

可以看出,消息 1 和 4 最有可能是垃圾邮件,它们被给予高垃圾邮件概率,尽管消息 4 的概率可能更高。这可能是该模型第一次发现这种模式,但它仍然检测到一些与垃圾邮件相关的模式,因此垃圾邮件的概率几乎高于所有邮件的 90%。
根据为训练选择的参数,消息 2 和 3 被正确地分配了非常低的垃圾邮件概率。

结论

外卖

  • 基于频率的自动标注允许合理地创建标注数据集。
  • 垃圾邮件检测可以被重新构建为回归问题,并且添加的垃圾邮件概率结构提供了更精细的分类。
  • 该模型成功地检测出在训练集上看不到的新垃圾邮件模式。
  • 标签越好,结果越好。
  • 垃圾邮件概率不应该在真空中解释,而应该在问题和分析数据集的上下文中解释。
  • 在云上运行这个模型是非常便宜的,因为它速度快,内存使用少。

可能的改进

任何机器学习项目都有改进的空间,这个项目也不例外。这些是一些可能的预测影响。

在当前框架内

  1. 改进标签。这是迄今为止最重要的事情。数据的质量与预测的质量高度相关。一些可能性是:
    —在特征提取之后使用消息聚类将相似的消息分组在一起,并且手动标记该组的代表。然后,该标签将共享给组中的所有数据点。
    —想出更好的标签试探法。
    —利用外部数据,如发件人、承运人、位置等。,识别垃圾邮件发送者并将其所有邮件标记为垃圾邮件。
    —如果有助于训练,请使用其他来源的垃圾邮件数据(小心)。
    —更多手动贴标。

2.尝试不同的模型架构并调整超参数。

3.模型校准,以便垃圾邮件概率实际上代表一个数学定义的概率

当前框架外

包括除了 SMS 内容之外的信息可以提供辅助变量,以帮助决定在 SMS 运行通过模型之后对其采取何种动作。用户元数据,如应用程序的使用期限、消息频率、以前被阻止的消息数等,应该是对仅根据短信内容计算的垃圾邮件概率的很大补充。

垃圾邮件概率只是其中一个组成部分的集成框架将通过减少误报数量(从仅在一定数量的警告后阻止)来增强模型的实用性,不评估来自已知合法用户的消息,并及时阻止重复的垃圾邮件发送者使用该应用程序。

KNN 垃圾邮件分类器—从零开始(Python)

原文:https://towardsdatascience.com/spam-email-classifier-with-knn-from-scratch-python-6e68eeb50a9e?source=collection_archive---------4-----------------------

使用 Python 实现 KNN 算法对垃圾邮件进行分类的分步指南

马特·里德利在 Unsplash 上的照片

什么是 KNN?

KNN 是一种非常简单的监督学习算法。然而,与传统的监督学习算法(如多项式朴素贝叶斯算法)不同,KNN 没有独立的训练阶段,也没有基于训练模型预测测试数据标签的阶段。相反,实时地将每个测试数据项的特征与每个训练数据项的特征进行比较,然后选择 K 个最接近的训练数据项,并且将其中最频繁的类别给予测试数据项。

在电子邮件分类(垃圾邮件或 ham)的上下文中,要比较的特征是每封电子邮件中单词的频率。欧几里德距离用于确定两个电子邮件之间的相似性;距离越小,越相似。算法中使用的欧几里德距离公式如下:

作者图片

一旦计算了测试电子邮件和每个训练电子邮件之间的欧几里德距离,就以升序(从最近到最远)对距离进行排序,并且选择 K 个最近的相邻电子邮件。如果大部分是垃圾邮件,则测试电子邮件被标记为垃圾邮件,否则,它被标记为垃圾邮件。

作者图片

在上面的例子中,K = 5;我们正在将我们想要分类的电子邮件与最近的 5 个邻居进行比较。在这种情况下,5 封电子邮件中有 3 封被分类为 ham(非垃圾邮件),2 封被分类为垃圾邮件。因此,未知的电子邮件将被赋予多数人的类别:火腿。现在我们已经看到了 KNN 是如何工作的,让我们继续使用代码实现分类器!

履行

为了快速了解我们将用 Python 编写什么,编写伪代码总是一个好的做法:

1\. Load the spam and ham emails2\. Remove common punctuation and symbols3\. Lowercase all letters4\. Remove stopwords (very common words like pronouns, articles, etc.)5\. Split emails into training email and testing emails6\. For each test email, calculate the similarity between it and all training emails
    6.1\. For each word that exists in either test email or training email, count its frequency in both emails
    6.2\. calculate the euclidean distance between both emails to determine similarity7\. Sort the emails in ascending order of euclidean distance8\. Select the k nearest neighbors (shortest distance)9\. Assign the class which is most frequent in the selected k nearest neighbours to the new email

数据集

垃圾邮件和 ham(普通电子邮件)的电子邮件数据集是从“安然-垃圾邮件数据集”中获得的。可以在 Enron2 下的http://NLP . cs . aueb . gr/software _ and _ datasets/Enron-Spam/index . html找到。我们使用的数据集包含 5857 封电子邮件。每封电子邮件都存储在一个文本文件中,文本文件被划分并存储在两个文件夹中,ham 文件夹和 spam 文件夹。这意味着电子邮件已经被标记。每个文本文件都将被程序加载,每封邮件都将被读取并存储为一个字符串变量。字符串中的每个不同的单词都将被视为一个特征。

使用的库

import os
import string
from nltk.corpus import stopwords
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
import numpy as np

os 打开并读取文件的库。
字符串为标点符号列表
停用词包含停用词列表。
train_test_split 将数据拆分成训练和测试数据。
accuracy_score 计算算法的准确度。
numpy 允许高级数组操作。

加载数据

def load_data():
    print("Loading data...")

    ham_files_location = os.listdir("dataset/ham")
    spam_files_location = os.listdir("dataset/spam")
    data = []

返回一个文件夹中所有文件名的列表。这用于检索每个 ham 和 spam 文件夹中文本文件的所有文件名,并将它们分别存储在 ham_files_location 和 spam_files_location 中。数据是存储每个电子邮件文本及其相应标签的列表。

 # Load ham email
    for file_path in ham_files_location:
        f = open("dataset/ham/" + file_path, "r")
        text = str(f.read())
        data.append([text, "ham"])

    # Load spam email
    for file_path in spam_files_location:
        f = open("dataset/spam/" + file_path, "r")
        text = str(f.read())
        data.append([text, "spam"])

我们遍历 ham 文本文件名列表,使用 open()打开一个文件,然后使用 str(f.read())将电子邮件文本作为一个字符串读取并存储在变量文本中。由文本构成的列表和相应的标签“ham”被附加到列表数据。

 data = np.array(data)

    print("flag 1: loaded data")
    return data

列表数据被转换成一个 numpy 数组,以便以后更好地操作该数组。然后返回数据。

数据预处理

# Preprocessing data: noise removaldef preprocess_data(data):
    print("Preprocessing data...")

    punc = string.punctuation           # Punctuation list
    sw = stopwords.words('english')     # Stopwords list

punc 保存一个标点和符号列表
sw 保存一个 nltk.corpus 库中的停用词列表

 for record in data:
        # Remove common punctuation and symbols
        for item in punc:
            record[0] = record[0].replace(item, "")

对于 data 中的每条记录,对于 punc 中的每项(符号或标点),用空字符串替换该项,以从记录0中删除该项。

 # Lowercase all letters and remove stopwords 
        splittedWords = record[0].split()
        newText = ""
        for word in splittedWords:
            if word not in sw:
                word = word.lower()
                newText = newText + " " + word        record[0] = newText

    print("flag 2: preprocessed data")        
    return data

对电子邮件文本记录[0]使用 split()方法返回电子邮件中所有单词的列表。遍历单词列表,如果单词不在停用词列表中,将其设置为小写,并将该单词添加到 newText。新文本将包含电子邮件,但没有停用词。newText 被分配回记录[0]。预处理完每个记录[0]后,返回干净的数据。

预处理电子邮件之前和之后

将数据分成训练集和测试集

数据集分为训练集(73%)和测试集(27%)。

# Splitting original dataset into training dataset and test datasetdef split_data(data):
    print("Splitting data...")

    features = data[:, 0]   # array containing all email text bodies
    labels = data[:, 1]     # array containing corresponding labels
    print(labels)
    training_data, test_data, training_labels, test_labels =\
        train_test_split(features, labels, test_size = 0.27, random_state = 42)

    print("flag 3: splitted data")
    return training_data, test_data, training_labels, test_labels

首先,有必要将电子邮件文本放在一个自己的数组中,并将标签放在另一个自己的数组中。因此,电子邮件文本存储在特征中,标签存储在标签中。然后使用 train_test_split 方法将数据拆分为 training_data、test_data、training_labels 和 test_labels。随机状态被设置为 42,以确保出于测试目的获得相同的随机洗牌输出。拆分后,将返回 training_data、test_data、training_labels 和 test_labels。

KNN 算法

get_count()函数

def get_count(text):
    wordCounts = dict()
    for word in text.split():
        if word in wordCounts:
            wordCounts[word] += 1
        else:
            wordCounts[word] = 1

    return wordCounts

该函数获取一个电子邮件文本,并使用 split()将其拆分。统计电子邮件中每个单词的出现频率,并保存在 wordCounts 中,这是字典数据类型。然后返回字典字数。

欧几里得 _ 差分()函数

def euclidean_difference(test_WordCounts, training_WordCounts):
    total = 0

这个函数接受一个测试邮件的字数字典 test_WordCounts 和另一个训练邮件的字典 training_wordCounts。total 存储测试和训练电子邮件中单词频率的平方差之和。

 for word in test_WordCounts:
        if word in test_WordCounts and word in training_WordCounts:
            total += (test_WordCounts[word] - training_WordCounts[word])**2

首先,我们迭代测试电子邮件字典中的单词。对于每个单词,有三种情况。第一种情况是它同时存在于测试邮件和培训邮件中。在这种情况下,total 会随着单词在测试电子邮件和培训电子邮件中出现频率的平方差而增加。

 del training_WordCounts[word]

然后,从训练电子邮件字典中删除常用单词,以加速下一个 for 循环

 else:
            total += test_WordCounts[word]**2

第二种情况是这个词只出现在测试邮件中。在这种情况下,没有必要找出差异(因为它的频率在训练邮件中是 0),所以我们只需将单词的平方频率加到总数中。

 for word in training_WordCounts:
            total += training_WordCounts[word]**2

最后一种情况是这个词只在培训邮件里。由于我们在前面的 for 循环中删除了所有常用单词,因此我们只需循环遍历训练电子邮件字典,并将每个单词的频率平方添加到 total 中。

 return total**0.5

最后,total 的平方根(每个单词的频率的平方差的和的平方根)作为 double 返回。这是欧几里德距离计算函数的结尾。

get_class()函数

def get_class(selected_Kvalues):
    spam_count = 0
    ham_count = 0

该函数接受所选的 K 个最近邻居的列表,以确定当前测试电子邮件的类别。spam_count 和 ham_count 分别存储每个“spam”标签和“ham”标签在 K 个选定的最近邻居中的出现频率。

 for value in selected_Kvalues:
        if value[0] == "spam":
            spam_count += 1
        else:
            ham_count += 1

使用 for 循环,对于 K 个选定值中的每个值,如果标签值[0]等于“spam ”,则 spam_count 递增 1。否则,ham_count 递增 1。

 if spam_count > ham_count:
        return "spam"
    else:
        return "ham"

在 for 循环之后,如果 spam_count 大于 ham_count,则表示当前测试邮件有更大的倾向是垃圾邮件,因此返回一个字符串“spam”作为预测标签。否则,字符串“ham”将作为预测标签返回。

knn_classifier()函数

def knn_classifier(training_data, training_labels, test_data, K, tsize):
    print("Running KNN Classifier...")

    result = []
    counter = 1

这是 KNN 分类器函数。它接收训练邮件、训练标签、测试数据、K 值,以及原始 27%测试邮件中要测试的测试邮件的数量。结果是包含预测标签的列表。计数器将仅用于显示目的,以指示程序运行时的进度。

 # word counts for training email
    training_WordCounts = [] 
    for training_text in training_data:
            training_WordCounts.append(get_count(training_text))

由于训练集是恒定的,我们可以一劳永逸地统计每封训练邮件中的词频。因此,对于训练数据中的每个电子邮件文本,使用 get_count()获得其词频字典。然后,将该词典添加到要存储的 training_WordCounts 列表中。

 for test_text in test_data:
        similarity = [] # List of euclidean distances
        test_WordCounts = get_count(test_text)  # word counts for test email

现在,对于测试数据中的每封测试邮件,都执行以下操作。声明了空列表相似性。它将存储当前测试电子邮件和每个培训电子邮件之间的欧几里德距离。然后,使用 get_count()获得测试邮件的词频词典。

 # Getting euclidean difference 
        for index in range(len(training_data)):
            euclidean_diff =\
                euclidean_difference(test_WordCounts, training_WordCounts[index])
            similarity.append([training_labels[index], euclidean_diff])

因为我们已经有了所有训练邮件和当前测试邮件的词频词典。我们可以继续使用迭代 x 次的 for 循环来计算当前测试电子邮件和每个训练电子邮件之间的欧几里德距离,其中 x 等于训练数据集的大小。在每次迭代之后,计算的欧几里德距离连同训练电子邮件的相应标签一起被附加到相似性列表。

 # Sort list in ascending order based on euclidean difference
        similarity = sorted(similarity, key = lambda i:i[1])

在存储了所有欧几里得距离之后。我们基于第二列,即基于欧几里德距离(从最近到最远),以升序对相似性列表进行排序。

 # Select K nearest neighbours
        selected_Kvalues = [] 
        for i in range(K):
            selected_Kvalues.append(similarity[i])

现在,由于相似性列表已经排序,我们可以使用简单的 for 循环轻松地将最近的 K 个邻居添加到 selected_Kvalues 列表中。

 # Predicting the class of email
        result.append(get_class(selected_Kvalues))

最后,在进入下一封测试邮件之前。我们使用 get_class()来确定当前测试电子邮件的类别。现在,我们已经到了一次迭代的末尾,下一次迭代可以开始分类下一封测试邮件了。

 return result

一旦所有的测试邮件都被分类,并且 for 循环已经到达它的末端,那么包含预测标签列表的结果列表将被返回。

main()函数

def main(K):
    data = load_data()
    data = preprocess_data(data)
    training_data, test_data, training_labels, test_labels = split_data(data)

这是程序开始运行的主要功能。这是所有东西放在一起的地方。主函数接受 K 值。首先,使用 load_data()加载所有电子邮件,然后存储在 data 中。然后使用 preprocess_data()对电子邮件进行预处理,并再次存储在数据中。然后使用 split_data()将数据拆分为 training_data、test_data、training_labels 和 test_labels。

 tsize = len(test_data)

tsize 指定了测试电子邮件的数量(在原始的 27%测试数据中)来预测它们的标签。目前,tsize 被设置为等于整个测试电子邮件集。

 result = knn_classifier(training_data, training_labels, test_data[:tsize], K, tsize) 
    accuracy = accuracy_score(test_labels[:tsize], result)

现在,调用 knn_classifier()函数来预测测试电子邮件的标签。返回的预测标签列表存储在结果中。之后,使用 sklearn 库中的 accuracy_score()方法计算准确度。此方法将实际标签列表 test_labels 与预测标签列表结果进行比较。

 print("training data size\t: " + str(len(training_data)))
    print("test data size\t\t: " + str(len(test_data)))
    print("K value\t\t\t\t: " + str(K))
    print("Samples tested\t\t: " + str(tsize))
    print("% accuracy\t\t\t: " + str(accuracy * 100))
    print("Number correct\t\t: " + str(int(accuracy * tsize)))
    print("Number wrong\t\t: " + str(int((1 - accuracy) * tsize)))

这些行显示运行的详细信息,如训练数据大小、测试数据大小、K 值、测试的样本数量、百分比准确度、正确识别的电子邮件数量以及错误识别的电子邮件数量。

main(11)

最后,这一行通过调用 main 函数启动程序,并赋予它 K 值(在本例中是 11)。

产出和结论

输出

这是上面解释的所有代码的最终输出。可以看出,使用 KNN 算法将电子邮件分类为垃圾邮件和垃圾邮件,K 值为 11,测试数据大小为 1582,其准确率为 76.7%。虽然不是最好的,但还是令人满意的。需要注意的一个缺点是,对 1582 封邮件进行分类需要很长时间。这主要是由于高时间复杂度,这是在计算测试电子邮件和训练电子邮件之间的欧几里德差时三个嵌套 for 循环的结果。

你可以在我的 Github 库这里找到完整的源代码。

Spark 3.2:流数据的会话窗口特性

原文:https://towardsdatascience.com/spark-3-2-session-windowing-feature-for-streaming-data-e404d92e267?source=collection_archive---------9-----------------------

Spark 3.2 的大数据实施

在 Spark 3.2 上用 PySpark 实现会话窗口

来源

最新版本的 Spark 3.2 发布于 2021 年 10 月 13 日[ 1 ]。除了在不同主题上的改进之外,

现有的用于流数据处理的窗口框架仅提供翻转和滑动窗口,如 Spark 技术文档[ 2 ]中所强调的。在术语中,有一个额外的窗口操作,称为会话窗口。

与其他两种窗口方法不同,会话窗口没有固定的窗口开始和结束时间。会话窗口的创建取决于定义的会话周期,该周期可以是静态的或动态的。

在会话窗口的静态版本中,特定时间段内发生的事件被视为一个会话窗口。当会话窗口在指定时间段内没有收集事件时,它将停止。

在会话窗口的动态版本中,周期可能会因事件而异。

对于每种窗口方法,您可以在接下来的章节中找到 PySpark 语言的相应实现。对于开发平台和编程语言,将分别使用 Google Colab 和 PySpark。

Java 安装

在安装 Spark 3.2 之前,最初,我们需要安装 Java 的匹配版本。使用下面的脚本,您可以设置 Java 8 版本。

!apt-get install openjdk-8-jdk-headless -qq > /dev/null

火花 3.2。安装

完成适当版本的 Java 安装后,就可以开始 Spark 3.2 的安装了。以下脚本将帮助您设置 Spark 3.2。版本[ 3 ]。

!wget -q [https://archive.apache.org/dist/spark/spark-3.2.0/spark-3.2.0-bin-hadoop2.7.tgz](https://archive.apache.org/dist/spark/spark-3.2.0/spark-3.2.0-bin-hadoop2.7.tgz)

当安装成功结束时,您需要在以下脚本的帮助下提取 spark 文件。

!tar xf spark-3.2.0-bin-hadoop2.7.tgz

Java 和 Spark 3.2 的安装。完了。下一步是为它们分配环境变量。如果你使用 Google Colab,你可以像下面这样设置路径环境。

import osos.environ["JAVA_HOME"] = "/usr/lib/jvm/java-8-openjdk-amd64"
os.environ["SPARK_HOME"] = "/content/spark-3.2.0-bin-hadoop2.7"

出于测试目的,您可以像下面的脚本一样运行 findspark 库。

import findspark
findspark.init()

窗口函数的类型

随着最新的更新,Spark 3.2 包含三种独特类型的窗口功能,如翻滚滑动、会话。为了能够单独测试它们,应使用最新的 spark 版本创建 Spark 会话。

以下脚本使用 Google Colab 环境中的所有本地核心创建一个会话。

from pyspark.sql import SparkSessionspark = SparkSession.builder.master("local[*]").getOrCreate()

为了测试 Spark 的版本,。版本功能可以为 spark 会话执行。

spark.version

火花会话初始化

为了能够应用窗口函数,需要一个 spark 会话和一个样本数据帧。示例 spark 会话可以初始化为下面的代码片段。

from pyspark.sql import SparkSessionspark = SparkSession.builder.master("local[*]").getOrCreate()
spark.version

示例数据可以用下面的脚本生成。

# Sample Data is generated for windowing exampleswindowingData = (("12", "2019-01-02 15:30:00"),("12",  "2019-01-02 15:30:30"),
("12",  "2019-01-02 15:31:00"),
("12",  "2019-01-02 15:31:50"),
("12",  "2019-01-02 15:31:55"),
("16",  "2019-01-02 15:33:00"),
("16",  "2019-01-02 15:35:20"),
("16",  "2019-01-02 15:37:00"),
("20",  "2019-01-02 15:30:30"),
("20",  "2019-01-02 15:31:00"),
("20",  "2019-01-02 15:31:50"),
("20",  "2019-01-02 15:31:55"),
("20",  "2019-01-02 15:33:00"),
("20",  "2019-01-02 15:35:20"),
("20",  "2019-01-02 15:37:00"),
("20",  "2019-01-02 15:40:00"),
("20",  "2019-01-02 15:45:00"),
("20",  "2019-01-02 15:46:00"),
("20",  "2019-01-02 15:47:30"),
("20",  "2019-01-02 15:48:00"),
("20",  "2019-01-02 15:48:10"),
("20",  "2019-01-02 15:48:20"),
("20",  "2019-01-02 15:48:30"),
("20",  "2019-01-02 15:50:00"),
("20",  "2019-01-02 15:53:00"),
("20",  "2019-01-02 15:54:30"),
("20",  "2019-01-02 15:55:00"),
("22",  "2019-01-02 15:50:30"),
("22",  "2019-01-02 15:52:00"),
("22",  "2019-01-02 15:50:30"),
("22",  "2019-01-02 15:52:00"),
("22",  "2019-01-02 15:50:30"),
("22",  "2019-01-02 15:52:00"))columns = ["eventId", "timeReceived"]windowing_df = spark.createDataFrame(data = windowingData, schema = columns)windowing_df.printSchema()windowing_df.show(truncate=False)

样本数据帧的输出(归作者所有)

翻滚窗口

滚动窗口可以被表示为一组相等划分的、相邻的时间段,而没有任何交叉间隔。输入的数据可以服从于单独的窗口。

from pyspark.sql.functions import *tumblingWindows = windowing_df.withWatermark("timeReceived", "10 minutes").groupBy("eventId", window("timeReceived", "10 minutes")).count()tumblingWindows.show(truncate = False)

翻滚窗口的输出(归作者所有)

在时间轴上观察时,窗口可以被视为一个序列,一个接一个以静态方式排列,如下图所示。

时间轴上的滚动窗口(归作者所有)

推拉窗

当时间跨度包含比窗口范围更短的间隔时,滑动窗口可以具有交叉的时间段。在这种情况下,可以在多个窗口中找到带有时间戳的项目。

from pyspark.sql.functions import *slidingWindows = windowing_df.withWatermark("timeReceived", "10 minutes").groupBy("eventId", window("timeReceived", "10 minutes", "5 minutes")).count()slidingWindows.show(truncate = False)

滑动窗口的输出(归作者所有)

当在时间线上观察时,当与具有作为滚动窗口的静态方式的主滑动窗口的长度相比时,根据时间范围的长度,窗口可以重叠或不重叠。

时间轴上的滑动窗口(归作者所有)

会话窗口

通过相对于输入项目类型的窗口大小,会话窗口可以包含动态长度的性质。

会话窗口从单个数据点开始,如果在间隔期内已经收集了即将到来的元素,则该窗口会变宽。

当最后一个项目被接受时,如果在间隔期内没有项目被确认,则会话窗口结束。此功能允许用户在选定时间段内没有高级事件注入时对事件进行聚类,该时间段在官方文章中也描述为非活动期[ 4 ]。

在现实世界中,会话窗口的逻辑作为超时事件运行。在某个特定的时间间隔内,您应该执行一个活动,否则,现有的会话会在足够长的时间过去后关闭。在记录了额外活动的情况下,可以延长会话周期。

对于会话窗口的具体用法,可以描述为用即将到来的事件创建一个会话窗口,在超时周期内持续收集的事件将被添加到当前会话中。

from pyspark.sql.functions import *sessionWindows = windowing_df.withWatermark("timeReceived", "10 minutes").groupBy("eventId", session_window("timeReceived", "5 minutes")).count()sessionWindows.show(truncate = False)

会话窗口的输出(归作者所有)

获得的每个事件都能够增加会话超时的范围。有了这个特性,与其他两种窗口方法相比,会话窗口的时间周期可以被认为不是静态的。

对于上面添加的特定示例,可以观察到 id 为 20 的事件,会话窗口流在指定时间段后关闭,并在新元素被确认时再次开始。

时间轴上的会话窗口(归作者所有)

会话窗口的动态间隔期

会话窗口功能有一个额外的特性,称为动态间隙持续时间,如 Databricks 博客文章[ 4 中所述。当被请求时,会话的周期可以具有各种值。具有重叠行为的会话可以被分组到单个会话中。此会话的总持续时间将等于交叉事件持续时间的总和。

您可以按事件 Id、会话窗口对数据进行分组,并通过为最新的 Spark 3.2 [ 5 ]编程指南中描述的 session_window() 函数插入两个值,使用以下脚本计算其出现次数。

windowedCountsDF = windowing_df.withWatermark("timeReceived", "10 minutes").groupBy(windowing_df.eventId, **session_window**(windowing_df.timeReceived, \
when(windowing_df.eventId == "20", "10 seconds").when(windowing_df.eventId == "12","30 seconds").otherwise("10 minutes"))).count()windowedCountsDF.show(100, truncate = False)

会话窗口的动态间隔期的输出(归作者所有)

结论

在文献中,存在三种主要的窗口功能。具有静态方式的是翻滚滑动窗口,这在以前版本的 Spark 3.2 中已经可用。Spark 3.2 [ 6 ]的最新版本现在提供了动态的会话 windows 功能。

你可以在 Google Colab 环境中找到完整的 PySpark 脚本代码。

非常感谢您的提问和评论!非常感谢您的阅读!

参考

  1. 火花释放 3.2.0
  2. 结构化流媒体节目指南
  3. 高级火花流

火花 3。嵌套字段不再嵌套

原文:https://towardsdatascience.com/spark-3-nested-fields-not-so-nested-anymore-9b8d34b00b95?source=collection_archive---------11-----------------------

关于嵌套字段的重要更改

照片由 Mrg SimonUnsplash 上拍摄

Spark 3.1.1 发布带来了很多新东西!

例如,查看为这个版本完成的所有与 Kubernetes 相关的任务。

正如你可能从这篇文章的标题中想象的那样,我们不会谈论 Kubernetes,而是嵌套字段

如果您使用 Spark 的时间足够长,那么您肯定会做一些使用深度嵌套字段的噩梦。

让我们从头开始。

什么是嵌套字段?

嵌套字段是包含其他字段或对象的字段。

例如:

如何在 Spark 中管理嵌套字段?

在 Spark 中,处理它们总是有点问题。

假设 id=1 的人已经把他的地址从“詹姆斯街”改成了“圣彼得街”,现在你需要更新它。

因为在 Spark 中不能直接修改嵌套字段,所以需要重新构建整个结构,这样才不会丢失其他字段。

大概是这样的:

df
  .withColumn("personal_info",
  *struct*(
    *lit*("**St Peter**"),
    *col*("personal_info.phone_number"),
    *col*("personal_info.age")))

嗯,好吧,但是我现在如何在 Spark 中管理嵌套字段呢?

嗯,在这个新的 3.1.1 版本中,引入了一个名为with field的新方法。

让我们打个招呼😁

从 Spark 3.1.1 开始,我们将能够像这样改变嵌套字段:

*df
  .withColumn("personal_info",
    'personal_info.withField("address", *lit*("**St Peter**")))*

好多了,对吧?我们不需要重建整个对象,我们只需要引用我们想要改变的字段就可以了!

让我们看一个更复杂的例子。

我们的新输入 JSON 如下(为了简单起见,我避免了一些括号)

我将打印 Spark 模式,让输入数据更加清晰。

*root
 |-- items: struct (nullable = true)
 |    |-- item: struct (nullable = true)
 |    |    |-- batters: struct (nullable = true)
 |    |    |    |-- batter: struct (nullable = true)
 |    |    |    |    |-- id: string (nullable = true)
 |    |    |    |    |-- topping: struct (nullable = true)
 |    |    |    |    |    |-- id: string (nullable = true)
 |    |    |    |    |    |-- type: string (nullable = true)
 |    |    |    |    |-- type: string (nullable = true)
 |    |    |-- id: string (nullable = true)
 |    |    |-- name: string (nullable = true)
 |    |    |-- ppu: double (nullable = true)
 |    |    |-- type: string (nullable = true)*

现在我们需要从置顶字段中更改 id ,该字段嵌套很深,我们需要导航:

物品- >物品- >面糊- >面糊- >浇头- > id

来达到这个值。

在 Spark 3.1.1 之前,这将是一个真正的难题,但现在就像这样简单:

*df
  .withColumn("items",
    *col*("items")
      .withField("item.batters.batter.topping.id", *lit*(**12**)))*

如果我们打印结果,我们可以看到新的 id 不再是 5001 而是 12 :

*+----------------------------------------------------------+
|items                                                     |
+----------------------------------------------------------+
|{{{{1001, {**12**, None}, Regular}}, 0001, Cake, 0.55, donut}}|
+----------------------------------------------------------+*

就是这样!酷吧?

…那么删除嵌套字段呢?

好消息!我们也可以放弃他们。让我们删除刚刚更改过的字段,为此,我们需要调用 dropFields:

*df
  .withColumn("items",
    *col*("items")
      .dropFields("item.batters.batter.topping.id"))*

让我们再次打印输出:

*+------------------------------------------------------+
|items                                                 |
+------------------------------------------------------+
|{{{{1001, {None}, Regular}}, 0001, Cake, 0.55, donut}}|
+------------------------------------------------------+*

还有成功! id 不见了。

在这一点上,你可能会问…

我可以同时修改一列和删除一列吗?

答案是肯定的!

*df
 .withColumn("items",
   *col*("items")
     .withField("item.batters.batter.topping.id", *lit*(12))
     .dropFields("item.batters.batter.topping.type"))*

如果我们打印结果和模式,我们可以看到 id 是 12,而 topping.type 不见了:

*+----------------------------------------------------+
|items                                               |
+----------------------------------------------------+
|{{{{1001, {12}, Regular}}, 0001, Cake, 0.55, donut}}|
+----------------------------------------------------+root
 |-- items: struct (nullable = true)
 |    |-- item: struct (nullable = true)
 |    |    |-- batters: struct (nullable = true)
 |    |    |    |-- batter: struct (nullable = true)
 |    |    |    |    |-- id: string (nullable = true)
 |    |    |    |    |-- topping: struct (nullable = true)
 |    |    |    |    |    |-- id: integer (nullable = false)
 |    |    |    |    |-- type: string (nullable = true)
 |    |    |-- id: string (nullable = true)
 |    |    |-- name: string (nullable = true)
 |    |    |-- ppu: double (nullable = true)
 |    |    |-- type: string (nullable = true)*

结论

Spark 发展很快,以前不可能的事情现在可能会发生,跟上时代总是好的,不仅是 Spark,而是每项技术。

毫无疑问,对于所有使用深度嵌套字段的人来说,这将改变游戏规则。

把它提高一个档次

原文:https://towardsdatascience.com/spark-it-up-a-notch-70d8864a896e?source=collection_archive---------20-----------------------

Apache Spark 的基本细节

我花了大约一年的时间来学习和实现与 Spark 相关的各种微妙之处。在这个系列中,从这篇文章开始,我将尝试记录我在 Spark 中遇到的不同场景,并解释它的用例。

照片由杰兹·蒂姆斯Unsplash 上拍摄

什么是火花?

Apache Spark 是一个用于大规模数据处理的开源统一分析引擎。Spark 提供了一个接口,通过隐式数据并行和容错对整个集群进行编程。

Note : Spark is not a programming language。它是一个通用的分布式数据处理引擎。Spark 是用函数式编程语言 Scala 编写的。幸运的是,Spark 有一个很棒的 Python 集成,叫做 PySpark(这是我最常用的),它让我可以以任何我想要的方式与框架交互。

为什么是火花?

Spark 是许多大数据相关问题的一站式解决方案。Spark 的一些常见用例有:

  1. 批处理(ETL)
  2. 实时,几乎是实时。
  3. ML 和深度学习

运行 spark(在集群模式下)最简单的方法是在现有的 Hadoop 集群上使用 AWS EMR,使用 YARN 作为资源管理器。

是什么让 spark 如此特别?

这是一个简单的三重回答。

  • 速度
  • 易用性
  • 统一的引擎

传统 Hadoop map-reduce 和 Spark 的差异

对于那些传统上从事 map-reduce 或 Hadoop 的人来说,你可能想知道为什么是 Spark?从速度到书写的容易程度,Spark 都有很大的优势。一些明显的差异是:

Spark 和分布式计算术语

大多数技术都伴随着过多的术语和术语,T2 也不例外。首先:

  • 分区数据
  • RDDS
  • 数据帧
  • 容错
  • 谱系图
  • 懒惰评估
  • 在内存计算中
  • 转换
  • 行动
  • 火花工作

分区数据、RDD 和数据帧

分区只不过是存储在集群中一个节点上的一个原子数据块。分区的存在主要是为了促进 Apache Spark 中的并行性。Apache Spark 中的 RDDS 是分区的集合。RDD 是 Apache Spark 的基本数据结构和构建块。

RDDS 是:

  • 弹性分布式数据集
  • 不可变的对象集合
  • 逻辑分区

词源:

  • 弹性:由于它们的不变性和谱系图(DAG)
  • 分布式:由于内置分区
  • 数据:哦,他们确实保存了一些数据!!

关于 RDD,需要注意的一点是它没有模式。它们不是存储在列结构或表格中。数据只是一行一行地存储在其中,并以类似于列表的方式显示(类似于行列表—例如-->[row(…)])

举例说明一个Data-frames have schema:

>>>from pyspark.sql.types import StringType,StructField,StructType,IntegerType>>>food_schema = StructType([StructField("FoodName",StringType(),True),
StructField("Cost",IntegerType(),True)])>>>food = [("Tiramisu",10),("Doritos",5),("Merlot",15)]
>>>rdd = spark.sparkContext.parallelize(food)
>>>df = sqlContext.createDataFrame(rdd,food_schema)>>> df.printSchema()
root
|-- FoodName: string (nullable = true)
|-- Cost: integer (nullable = true)>>> df.show()
+--------+----+
|FoodName|Cost|
+--------+----+
|Tiramisu|  10|
| Doritos|   5|
|  Merlot|  15|
+--------+----+

举例说明一个RDDs don’t have schema is a actually a list:

>>> food = [("Tiramisu",10),("Doritos",5),("Merlot",15)]
>>> rdd = spark.sparkContext.parallelize(food)
>>> food_collect = rdd.collect()>>> food_collect.printSchema()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute 'printSchema'>>> food_collect
[('Tiramisu', 10), ('Doritos', 5), ('Merlot', 15)]

容错和谱系图

顾名思义,它基本上是一种 Apache Spark 能够容忍一定数量错误的机制。这意味着系统必须能够在出现故障时优雅地继续正常工作,而不必束手就擒。故障可能是任何事情,如节点故障或任何网络干扰。

Apache Spark 中的容错围绕着 RDDs 的概念。这种自我恢复的特性是 Apache Spark 的决定性优势之一。对于这个 Apache Spark 使用了谱系图/DdirectedAcyclicG图。这不过是构成程序本身的一系列逻辑步骤;这就是我所说的“逻辑执行计划”。因此,如果一个节点崩溃,集群管理器会找出是哪个节点——并获得关于该节点在谱系图中应该做什么的所有信息,并将它分配给另一个节点,以便在同一时间同一地点继续处理。这个新节点将在 RDD 的同一个分区上运行。由于这个新节点,没有数据丢失

DAG 看起来像这样:

DAG 可以在 AWS EMR 控制台上的火花历史服务器中找到

懒惰评估

懒惰在一般情况下有负面含义,但在 Spark 中没有

顾名思义,可以肯定地说 Spark 可能很懒,但效率极高。除非触发一个动作,否则它不会开始执行。转换本质上是懒惰的——Spark 跟踪对哪个记录调用了什么转换(使用 DAG ),并且只有在对数据调用了某个操作时才会执行它们(例如,打印数据集的前 5 行)。因此,Spark 确保除非绝对需要,否则不会加载和处理数据。

照片由伊莫·威格曼Unsplash 上拍摄

这类似于老师决定在课堂上提问。集体不回答也是班里的规定,只有被特别指着问的时候才回答。现在假设老师问一群学生什么是**5 times 9**。如果**Apache Spark** 是那个班的学生,只有当老师说**Apache Spark, what is the answer**——注意**what is the answer**——相当于**action**时,他/她才会用大脑计算**5 times 9**

>>>from pyspark.sql.types import StringType,StructField,StructType,IntegerType>>>food_schema = StructType([StructField("FoodName",StringType(),True),
StructField("Cost",IntegerType(),True)])>>>food = [("Tiramisu",10),("Doritos",5),("Merlot",15)]
>>>rdd = spark.sparkContext.parallelize(food)
>>>df = sqlContext.createDataFrame(rdd,food_schema)>>> df.show() 

注意 : df.show() —下面是动作。当您第一次执行这段代码时,您会注意到需要几毫秒才能得到一个答案;这说明它被懒散地评价了。

转换

火花变换基本上是在 RDD 上执行的一个函数或一组函数,以获得新的 RDD。注意,转换返回新的 rdd,因为 rdd 是不可变的。转换本质上是懒惰的,这意味着只有当一个【动作】被调用时,转换才会得到【执行】。两个最基本的转换是:map()和 filter()

**two types**的变换:

  • 窄转换计算单个分区中的记录所需的所有元素都驻留在父 RDD 的single partition中。这意味着分区的子集可以用来计算我们想要的任何结果。《出埃及记》map(),mapPartition(),flatMap(),filter(),union()

看待这个问题的一种方式是——不管出于什么原因,如果变换过滤函数可以是:

  1. 显示物品在Coriander的记录
  2. 给我看看所有价格高于 5 美元的项目
  3. 显示所有食物名称以字母s结尾的项目
  4. 显示所有的VegetarianVegan选项。

现在,如果注意上面的每个过滤函数,它们中的每一个都可以应用于每个分区(123),而不依赖于其他分区,并且结果数据集将是预期的。这些被称为转换。

下面是几个例子来说明:

创建数据:

>>> food_schema = StructType([StructField("FoodName",StringType(),True),
StructField("Cost",IntegerType(),True),
StructField("Store",StringType(),True),
StructField("Type",StringType(),True)])>>> food = [
("Tiramisu",10,"Safeway","Vegetarian"),
("Aquafaba",6,"Safeway","Vegan"),
("Horizon Milk",4,"Safeway","Vegetarian"),
("Butter Wine",15,"Costco","Vegan"),
("Doritos",5,"Costco","Vegetarian"),
("Kirkland Merlot",35,"Costco","Vegetarian"),
("Coriander",1,"Trader Joe's","Vegan"),
("Ravioli",6,"rader Joe's","Vegetarian"),
("Chicken Breast",10,"rader Joe's","Non-Vegetarian")]>>> rdd = spark.sparkContext.parallelize(food)
>>> df = sqlContext.createDataFrame(rdd,food_schema)
>>> df.show()+---------------+----+------------+--------------+
|       FoodName|Cost|       Store|          Type|
+---------------+----+------------+--------------+
|       Tiramisu|  10|     Safeway|    Vegetarian|
|       Aquafaba|   6|     Safeway|         Vegan|
|   Horizon Milk|   4|     Safeway|    Vegetarian|
|    Butter Wine|  15|      Costco|         Vegan|
|        Doritos|   5|      Costco|    Vegetarian|
|Kirkland Merlot|  35|      Costco|    Vegetarian|
|      Coriander|   1|Trader Joe's|         Vegan|
|        Ravioli|   6| rader Joe's|    Vegetarian|
| Chicken Breast|  10| rader Joe's|Non-Vegetarian|
+---------------+----+------------+--------------+

过滤数据:

  1. 显示物品所在的记录Coriander
>>> df.filter(df["FoodName"]=="Coriander").show()
+---------+----+------------+-----+
| FoodName|Cost|       Store| Type|
+---------+----+------------+-----+
|Coriander|   1|Trader Joe's|Vegan|
+---------+----+------------+-----+

2.给我看看所有价格高于 5 美元的项目

>>> df.filter(df["Cost"]>=5).show()
+---------------+----+-----------+--------------+
|       FoodName|Cost|      Store|          Type|
+---------------+----+-----------+--------------+
|       Tiramisu|  10|    Safeway|    Vegetarian|
|       Aquafaba|   6|    Safeway|         Vegan|
|    Butter Wine|  15|     Costco|         Vegan|
|        Doritos|   5|     Costco|    Vegetarian|
|Kirkland Merlot|  35|     Costco|    Vegetarian|
|        Ravioli|   6|rader Joe's|    Vegetarian|
| Chicken Breast|  10|rader Joe's|Non-Vegetarian|
+---------------+----+-----------+--------------+

3.显示所有的VegetarianVegan选项。

>>> df.filter((df.Type == "Vegetarian") | (df.Type == "Vegan")).show()
+---------------+----+------------+----------+
|       FoodName|Cost|       Store|      Type|
+---------------+----+------------+----------+
|       Tiramisu|  10|     Safeway|Vegetarian|
|       Aquafaba|   6|     Safeway|     Vegan|
|   Horizon Milk|   4|     Safeway|Vegetarian|
|    Butter Wine|  15|      Costco|     Vegan|
|        Doritos|   5|      Costco|Vegetarian|
|Kirkland Merlot|  35|      Costco|Vegetarian|
|      Coriander|   1|Trader Joe's|     Vegan|
|        Ravioli|   6| rader Joe's|Vegetarian|
+---------------+----+------------+----------+

4.显示所有食物名称以字母s结尾的项目

>>> df.where(df.FoodName.like("%s")).show()
+--------+----+------+----------+
|FoodName|Cost| Store|      Type|
+--------+----+------+----------+
| Doritos|   5|Costco|Vegetarian|
+--------+----+------+----------+
  • 宽转换计算单个分区中的记录所需的所有元素可以驻留在父 RDD 的many partitions中。这意味着分区的子集不能用于计算我们想要的任何结果。这意味着将在分区之间进行数据移动以执行更广泛的转换——因为数据是围绕着它移动的,所以也称为混洗转换。

《出埃及记》groupByKey()、aggregateByKey()、aggregate()、join()、repartition()

举与上面相同的例子:

  1. 假设我们需要找出 a VegetarianVeganNon Vegetarian花费的总成本。
  2. 或者,我们希望按商店顺序查找商品及其价格。

总量:

>>> from pyspark.sql import functions as F
>>> df.groupBy("Type").agg(F.sum("Cost").alias("Total cost spent")).show()
+--------------+----------------+
|          Type|Total cost spent|
+--------------+----------------+
|Non-Vegetarian|              10|
|    Vegetarian|              60|
|         Vegan|              22|
+--------------+----------------+

窗口功能:

>>> df.select("Store","FoodName","Cost",F.rank().over(Window.partitionBy("Store").orderBy("Cost")).alias("Cost ranking by store")).show()
+------------+---------------+----+---------------------+
|       Store|       FoodName|Cost|Cost ranking by store|
+------------+---------------+----+---------------------+
|      Costco|        Doritos|   5|                    1|
|      Costco|    Butter Wine|  15|                    2|
|      Costco|Kirkland Merlot|  35|                    3|
|Trader Joe's|      Coriander|   1|                    1|
| rader Joe's|        Ravioli|   6|                    1|
| rader Joe's| Chicken Breast|  10|                    2|
|     Safeway|   Horizon Milk|   4|                    1|
|     Safeway|       Aquafaba|   6|                    2|
|     Safeway|       Tiramisu|  10|                    3|
+------------+---------------+----+---------------------+

注意:由于 **shuffling**,与窄变换相比,宽变换是昂贵的操作

行动

如上所述,transformations导致了new RDD的形成。现在actions相反,do not。一个动作触发了一个执行——这意味着它启动了 Spark 的laze evaluation还记得老师问全班一个问题的例子吗?(如果没有访问部分懒评 )**

动作是负责将数据从执行器发送到驱动程序的动作。执行器是负责执行任务的节点,驱动程序是维护和协调任务执行的 JVM 进程。

《出埃及记》count(),collect(),take(n),top(),foreach()

结论

我希望这篇文章能让您对 Spark 有所了解。在我看来,Spark 玩起来超级好玩和酷。今天我就讲到这里,并期待接下来的帖子,包括示例、用例以及火花调优

再见。

posted @ 2024-10-17 11:35  绝不原创的飞龙  阅读(206)  评论(0)    收藏  举报