TowardsDataScience-博客中文翻译-2020-八十四-
TowardsDataScience 博客中文翻译 2020(八十四)
如何进行 A/B 测试
理解 Python 中用于 A/B 测试的函数
A/B 测试的功能
最近,我在 DataCamp 上学习了“客户分析&Python 中的 A/B 测试”课程。本课程涵盖了如何使用 Python 来分析客户行为和业务趋势的细节,以及如何创建、运行和分析 A/B 测试以做出主动的、数据驱动的业务决策。

https://then extweb . com/DD/2015/07/24/marketing-the-tnw-way-2-deep-dive-on-ab-testing/上的图片
幂函数
统计功效是指当零假设为假时,发现具有统计显著性结果的概率。

统计功效公式(数据营课程图片)
from scipy import statsdef get_power(n, p1, p2, cl):
alpha = 1 - cl
qu = stats.norm.ppf(1 - alpha/2)
diff = abs(p2-p1)
bp = (p1+p2) / 2
v1 = p1 * (1-p1)
v2 = p2 * (1-p2)
bv = bp * (1-bp)
power_part_one = stats.norm.cdf((n**0.5 * diff - qu * (2 * bv)**0.5) / (v1+v2) ** 0.5)
power_part_two = 1 - stats.norm.cdf((n**0.5 * diff + qu * (2 * bv)**0.5) / (v1+v2) ** 0.5)
power = power_part_one + power_part_two
return (power)
样本容量函数
get_sample_size 函数决定了在各种情况下测试组和控制组需要多少用户。
def get_sample_size(power, p1, p2, cl, max_n=1000000):
n = 1
while n <= max_n:
tmp_power = get_power(n, p1, p2, cl)
if tmp_power >= power:
return n
else:
n = n + 100
return "Increase Max N Value"
p 值函数
假设零假设是正确的,p 值是获得至少与测试期间实际观察到的结果一样极端的测试结果的概率。p 值函数接受测试和控制转换率以及每个组的大小。
def get_pvalue(con_conv, test_conv, con_size, test_size):
lift = -abs(test_conv - con_conv)
scale_one = con_conv * (1 - con_conv) * (1 / con_size)
scale_two = test_conv * (1 - test_conv) * (1 / test_size)
scale_val = (scale_one + scale_two) ** 0.5
p_value = 2 * stats.norm.cdf(lift, loc = 0, scale_val)
return p_value
置信区间函数
置信区间是我们估计值的范围,并为我们的估计过程提供背景。
def get_ci(test_conv, con_conv, test_size, con_size, ci):
sd = ((test_conv * (1 - test_conv)) / test_size + (con_conv * (1 - con_conv)) / con_size) ** 0.5
lift = test_conv - con_conv
val = stats.norm.isf((1 - ci) / 2)
lwr_bnd = lift - val * sd
upr_bnd = lift + val * sd
return((lwr_bnd, upr_bnd))
这是我之前在 DataCamp 上做的 AB 测试项目。
用曲奇猫测试手机游戏 A/B
猫和饼干
饼干猫是由触觉娱乐开发的一款非常受欢迎的手机益智游戏。这是一个经典的“连接三个”风格的益智游戏,玩家必须连接相同颜色的瓷砖,以清除董事会和赢得水平。随着玩家在游戏关卡中前进,他们偶尔会遇到一些关卡,迫使他们等待相当长的时间或进行应用内购买才能前进。除了推动应用内购买之外,这些关卡还有一个重要的目的,那就是让玩家暂时停止玩游戏,希望能够增加和延长玩家对游戏的享受。但是门应该放在哪里呢?所以让我们把数据加载进去看看吧!
import pandas as pd
df = pd.read_csv('datasets/cookie_cats.csv')
df.head()
AB 测试数据
我们拥有的数据来自 90189 名在 AB 测试运行时安装游戏的玩家。这些变量是:
userid-识别每个玩家的唯一号码。version-玩家是被放在控制组(gate_30-30 级的门)还是移动门的组(gate_40-40 级的门)。sum_gamerounds-玩家在安装后的前 14 天内玩的游戏回合数。retention_1-玩家安装后 1 天回来玩了吗?retention_7-玩家安装后 7 天回来玩了吗?
当玩家安装游戏时,他或她被随机分配到gate_30或gate_40。

df.groupby(by='version').count()
游戏回合的分配

这个测试的重点将是大门的位置如何影响玩家的保留,所以让我们绘制第一周玩家游戏回合数的分布。
%matplotlib inlineplot_df = df.groupby(by='sum_gamerounds')['userid'].count()ax = plot_df.head(100).plot(x='sum_gamerounds', y='userid')
ax.set_xlabel("Total Game Rounds")
ax.set_ylabel("Number of Players")
ax.set_title("Number of Game Rounds Players on the First Week")
总保留时间为 1 天
在上面的图中,我们可以看到一些玩家安装了游戏,但从来没有玩过,一些玩家在第一周只玩了几轮,还有一些玩家真的上瘾了。视频游戏行业的一个常见指标是 1 天留存率:玩家在安装游戏一天后回来玩游戏的百分比。1 天留存越高,越容易留住玩家,建立庞大的玩家群。
print('The 1-Day Retention Rate: {:f}' .format(df['retention_1'].mean()))The 1-Day Retention Rate: **0.445210**
AB 组保留 1 天
所以,我们理解,只有不到一半的玩家在安装游戏一天后回来。让我们来看看两个 AB 组的 1 天保留时间有何不同。
df.groupby('version')['retention_1'].mean()gate_30 # **0.448188**
gate_40 # **0.442283**
我们应该对差异有信心吗?

当门移动到级别 40 时(44.2%),与处于级别 30 时的对照(44.8%)相比,1 天保持率似乎略有下降。这里我们使用自举:我们将重复对数据集进行重新采样(替换),并计算这些样本的 1 天保留时间。1 天保留时间的变化将向我们显示保留时间的不确定性。
boot_1d = []
iterations = 500
for i in range(iterations):
boot_mean = df.sample(frac=1, replace=True).groupby(by='version')['retention_1'].mean()
boot_1d.append(boot_mean)
boot_1d = pd.DataFrame(boot_1d)
ax = boot_1d.plot.kde()
ax.set_xlabel("The Average of 1-Day Retention")
ax.set_title("The Average of 1-Day Retention for each AB group")
放大差异

上面这两个分布代表了两个 AB 组潜在的 1 天保留时间的 bootstrap 不确定性。让我们放大一下 1 天保留时间的差异。
boot_1d['diff'] = ((boot_1d['gate_30'] - boot_1d['gate_40']) / boot_1d['gate_40'] * 100)ax = boot_1d['diff'].plot.kde()
ax.set_xlabel("% Difference between gate_30 and gate_40 for 1-Day Retention by AB-group ")
差异的概率
从这个图表中,我们可以看到最有可能的%差异在 1%-2%左右,并且大部分分布在 0%以上,有利于 30 级的门。但是差异在 0%以上的概率有多大?
# Calculating the probability that 1-day retention is greater when the gate is at level 30
prob = (boot_1d['diff'] > 0).mean()('{:.1%}').format(prob) # **95.8%**
AB 组保留 7 天
让我们从计算两个 AB 组的 7 天保留时间开始。
df.groupby('version')['retention_7'].mean()gate_30 # **0.190201**
gate_40 # **0.182000**
再次引导差异

与 1 天保留期一样,我们看到当关卡处于 40 级时,7 天保留期(18.2%)略低于关卡处于 30 级时(19.0%)。这种差异也大于 1 天的保留,大概是因为更多的玩家有时间击中第一个门。但是像以前一样,让我们使用 bootstrap 分析来计算我们应该对 AB 组之间的差异有多确定。
boot_7d = []
for i in range(500):
boot_mean = df.sample(frac=0.1, replace=True).groupby('version')['retention_7'].mean()
boot_7d.append(boot_mean)
boot_7d = pd.DataFrame(boot_7d)boot_7d['diff'] = ((boot_7d['gate_30'] - boot_7d['gate_40']) / boot_7d['gate_40'] * 100)ax = boot_7d['diff'].plot.kde()
ax.set_xlabel("% Difference between gate_30 and gate_40 for 7-Day Retention by AB-group ")prob = (boot_7d['diff'] > 0).mean()# Pretty printing the probability
('{:.1%}').format(prob) # **84.4%**
结论
bootstrap 结果告诉我们,有强有力的证据表明,当 gate 处于 30 级时,7 天保留时间比处于 40 级时更长。结论是:如果我们想保持高留存率——1 天和 7 天留存率——我们应该而不是将关卡从 30 级移到 40 级。当然,我们还可以查看其他指标,比如两个 AB 组玩的游戏回合数或游戏内购买量。但是保留是最重要的指标之一。如果我们不保留我们的玩家基础,他们在游戏中花多少钱都没用。
# Should we move the gate from level 30 to level 40 ?
move_to_level_40 = False
创建这篇文章的源代码可以在我的 Github 中找到。
关于我
非常感谢您阅读我的文章!大家好,我是雪莉,目前在亚利桑那州立大学攻读商业分析硕士学位。如果您有任何问题,请随时联系我!
Email me at ***kchen122@asu.edu***and feel free to connect me on [**LinkedIn**](https://www.linkedin.com/in/kuanyinchen-shirley/)!
Twitter API 上使用 Python 的移动推送通知研究
将你的 Twitter 账户变成你的个人#笔记本

萨拉·库菲在 Unsplash 上拍摄的照片
数据现在无处不在,几乎和你的指纹一模一样。今天,技术进步和数字可访问性已经包围了我们,不幸的是,让我们的手机或笔记本电脑远离视线并不容易。
简单来说,
- 我们应该在哪里保存数据?
- 真的能满足我们的需求吗?
然后,
- 我们应该从哪里开始?
我认为数据科学是制作美味佳肴的艺术。烹饪时,您可以添加不同的配料或改变烹饪风格,以达到新的口味或增强现有配料的风味。同样,在处理数据时,您可以尝试不同的方法和材料来获得结果。就像吃饭的乐趣和快乐一样,在一天结束时,如果你的最终结果对你或你的观众来说不好吃,你可能会觉得你做的所有工作都是无用的。我们谈到的大多数数据问题都是看不见摸不着的。事实上,你会发现自己处于一个持续的说服之旅中。如你所知,关于【数据】最重要的一点是提出正确的问题,并对可能的答案进行正确的排序。
为了捕捉上面提到的味道,在开始处理数据之前,我通常会问自己以下三个问题:
- 什么最能描述我的问题,期望的结果是什么?
- 我可以使用哪些技术/方法来满足我的需求并使我的工作可持续发展?
- 我的工作有成本效益吗?
让我先描述一下我的问题:
当阅读书籍、文章或任何新闻时,我总是很难找到我写下的笔记、突出显示或下划线。过了一段时间,难免记不住所有记下的信息。因此,我想我可以开发一个工具,在特定的时间间隔为我的笔记设置提醒,从而改善我的记忆,让它们保持更长的时间。
或许市场上可以有笔记提醒应用程序。然而,我想开发自己的笔记提醒工具。
如何满足我的需求并使其可持续发展?
显然,我不想开发一个具有单一用户管理和一次性使用的静态结构的工具。对我来说,拥有一个灵活的架构至关重要,这样每个人都可以根据自己的利益修改和改进工具。
当然,也可以通过移动应用程序来实现这一功能。我确信你们当中许多成功的应用程序开发人员可以很容易地编写程序来添加这一功能。然而,我想在这里实现的是用我有的食材准备一顿美餐。对我来说,另一个关键点是通过节省时间和成本,用最少的努力获得最大的收益。
为此,我想我可以把 Twitter 当作一个记事本。
有何不可?
最终,Twitter 是一个可以根据你的目的用作个人笔记本的平台。多亏了 Twitter 的#标签机制,我甚至可以毫不费力地将我的笔记本分成若干部分。
嗯,我能让它持续吗?
是的,我的笔记本将继续存在,除非 Twitter 关闭或者我的用户 ID 从他们的数据库中删除。这样,不仅是我,其他 Twitter 用户也将能够使用我的笔记本作为信息的洪水。
学习是否划算?
是的,托管服务器是这项工作的主要成本。然而,由于我已经有了一台服务器,我不需要额外花钱。因此,我在这方面没有任何花费。
我们开始工作吧!
先来解决问题:
经济学和金融市场一直是我感兴趣的领域。我喜欢阅读很多关于这些话题的文章。因此,我的第一个动机是把我的经济学读物转换成笔记。我在推特上给自己设置的标签是# eko _ kendimenotlar(eko:经济的缩写,kendimenotlar:notestomyself)。在这个标签下,我开始分享我的阅读笔记,所以作为第一步,我开始填充我的数据仓库。
现在我需要找到一种方法来访问我在 Twitter 上的数据仓库。幸运的是,Twitter API 拯救了我。通过在 Twitter 上开一个开发者账户,我能够通过 Twitter API 访问我的账户。我使用 Tweepy 包来访问 Twitter API 和作为编程语言的 Python。事实上,我想做的很简单:
- 在 Twitter 上找到我的帐户并收集我的推文,然后用#hashtag 将它们分开。
- 定期将收集的推文发送到我的手机作为提醒。
我用 Python 无缝地编写了第一步,但在最后一部分,我确实需要找到一个解决方案:
向我的手机发送通知?
我最初计划将我的想法实现为一个移动应用程序。然而,我在这方面没有足够的经验,这将需要更多的时间和额外的努力来学习它,然后相应地实施。因此,我决定使用一种叫做 Pushbullet 的附加服务。push pullet是一家服务提供商,可以轻松地在设备之间提供信息共享,以实现更好的同步。我使用 Pushbullet 的服务将通知推送到手机上,这大大节省了我的时间。多亏了 Pushbullet Python 包,我将 push 命令集成到了我的脚本中,并消除了通过他们的移动应用程序向我的手机发送通知的问题。这是我为这项工作编写的 Python 脚本:

Twitter API 上使用 Python 的移动推送通知研究
现在,我可以在 Twitter 上找到我的 #eko_kendimenotlar 标签,并将我的笔记作为通知发送到我的手机上。然后,下一个问题出现了:我如何使它自动化?
起初,我想到了在 Windows 任务管理器或 UIPath 中实现自动化。我所要做的就是创建 myscript.bat 文件,并将其重定向到任务管理器和 UIPath 自动化机器人。然而,我不想依赖于本地,于是我想我可以在 Dreamhost 的服务器上创建一个 Cron 作业。您可以将 Cron 作业想象成一种定期运行计划任务的结构,就像任务管理器一样。您可以在互联网上找到许多可以创建 Cron 作业的服务。
当然,为了激活 Cron 作业,我首先通过 SSH(安全 Shell) 将我的 Twitter 脚本上传到我的服务器。因此,Cron 作业在我指定的时间开始在服务器上运行这个脚本。
这是结果😊

现在,我能够一天两次获得我的“推特经济笔记”🚀🚀🚀
仅用上面您看到的 26 行代码,我就能够为自己的个人使用创建一个经济高效的服务。当然,这是一个非常简单的应用,但我想在这里指出的是,强大的品牌或渠道如果有效地结合起来,将如何提供强大的解决方案。这是一个很好的案例研究,它表明您编写的代码作为独立的代码对您来说意义重大,但是,如果它得到业务模型的支持,它会产生更大的影响。
以下几点可以改进:
- 使用随机 Python 包,我将收集到的 tweets 随机发送到我的手机上。我的主要目标是在这里创建一个快速的解决方案,但如果你的笔记的时间线对你来说很重要,作为这个解决方案的替代方案,你可以对我提供的脚本进行轻微的修改,创建一个数据库,并将收集的推文按时间顺序发送到你的手机上。
- 虽然脚本中的方法具有可编辑的#hashtag 结构,但是我只能通过 SSH 在脚本中修改它。对于更动态的结构,可以创建移动应用程序,其中可以根据所需的 hashtag 构造各种查询。
- 这里,由于 Twitter API 的免费搜索方法有 7 天的限制,我继续使用 user_timeline 方法,然后过滤相应的 #hashtag 。高级或企业开发人员帐户可以跳过此选项卡,直接通过搜索方法继续。
非常感谢以下所有环境使我能够完成这项工作:

为了未来,
塞尔詹·耶尔德兹
对数正态分布的模式
使用模拟和数学建立直觉

正态分布在统计学和数据建模中很流行,因为它无处不在。现实世界充满了服从正态分布的随机变量的例子。而且,这种分布在统计学中起着核心作用(中心极限定理)。使用正态分布可以得到一个有趣的分布,称为对数正态分布。对数正态分布在金融中被广泛使用,因为股票价格被假定遵循这种分布。顾名思义,对数正态随机变量可以推导如下:

上式中的随机变量 Y 被称为遵循对数正态分布。换句话说, X 是从均值为μ、方差为σ的正态分布中采样得到的, Y 是通过使用指数函数对其进行变换得到的。在这篇文章中,我试图理解这个发行版的模式。我写这篇文章的一个原因是,这个问题迫使我更新了我对答案的错误直觉。
回答这个问题的思路可以是这样的:众数是一组数据中最频繁出现的值,或者是随机变量中最频繁出现的值。在概率术语中,Mode 表示出现概率最高的值。因为我们在这里看到的是连续的随机变量,我们可以有把握地说,众数是一个使概率密度函数(pdf)最大化的值。我们知道正态分布的 pdf 类似于图 1。


图 1(左)和图 2(右)。图 1 描述了正态分布的概率分布函数。可以观察到,分布围绕标记μ ∓ σ的中间线和虚线对称。图 2 描述了指数函数。
我们可以观察到最常见的(/可能的)值是平均值。在图 2 中,我们可以观察到指数函数,它是一个严格递增函数(因为它的导数> 0)。因此,如果我们设想从正态分布中对 X 的许多值进行采样,并将这些值插入指数函数中,我们将得到 Y 的多个值,即我们将对随机变量 Y 的值进行采样。如果 Mode 表示一个数 Y 的最可能值,那么μ不是对数正态分布的模式吗?不,这不是正确的答案。在继续读下去之前,我建议你暂停一下,试着想一想你是否能在论点中找到任何漏洞,并且大概猜出正确的数字(至少在方向上相对于μ)。
在下文中,我将用两种方法来回答这个问题。首先,我们将查看一些计算机模拟,以获得一些直觉,知道我哪里出错了(如果你已经发现了缺陷,那就太好了),然后我们将使用数学推导出这个值的解析表达式。
在深入研究以上任何一个问题之前,让我给你一个手动论证。指数函数在 x=0 时的值是 1,并且总是正的。这意味着所有的 x < 0 values are mapped to [0,1) and x≥0 are mapped to [1, ∞). In other words, if we pass the real number line through an exponential function, half of the line maps to values between [0,1) and the other half to [1, ∞). This is why an exponential function is called non-linear (compared to this, a linear function, for instance, a straight line would map the real line equally). This is exactly why the Mode of a Log-Normal distribution is not at the mean (which is the most frequent value for a Normal distribution), i.e. due to this non-linear mapping from X to Y, the collection of negative values are getting compressed in a smaller region (compared to the positive values collection), and this shifts the mode slightly to the right. Now, let’s picture this idea with computer simulations.
To keep the discussion simple, I am 都假设一个标准的正态分布,μ=0,而σ =1。模拟的程序如下,
- 选择要采样的点数,N
- 使用正态分布的 pdf,我们抽样 n 个点(也称为 x 的实现):{x₁,x₂,…
- 计算 Y = exp(X)
- 标记 x ≥ 0 的值(因为我们对研究两个区域中点的压缩感兴趣)
- 计算两组(x≥0 和 x<0)压缩的粗略度量,我称之为点密度,定义如下,

这里,X₋表示所有那些 x<0 的值的集合。为了避免混淆,这不再是随机的,因为我们在第 2 步采样时已经知道了这些值。关于点密度测量需要注意的一些事情。粗略地说,它捕获了(我前面谈到的真实线的)压缩量,因为它代表了长度区间中的平均点数(我使用 max 和 min 来计算区间长度的原因是我们正在采样随机值。所以,我们永远不知道我们是否真的会到达区间的终点。不要担心,你会在下面的模拟结果中看到这一点)。另一件要注意的事情是,这是一个粗略的度量,因为它没有考虑曲率(这可能看起来很傻,因为曲率实际上是驱动整个练习的原因)。但是,这种近似的测量方法目前是可行的。我们走吧。
我对 N = 10、100、1000、10000 重复了上述过程,结果如下所示。每个图标题描述了点数(n)和为 X₋(负 x)和 X₊(位置 x)计算的点密度值。红色和蓝色的点分别属于集合 X₋和 X₊。背景中的虚线突出显示了指数曲线。




从左到右,从上到下:不同 N = 10、100、1000、10000 值的 Y v/s X 图。每个图的顶部给出了点密度测量值。
通过这些结果,我们可以立即看到压缩。首先,注意 y 轴的刻度是如何随着采样点数 N 的增加而变化的。这是因为当我们采样的点越多,越稀少的点就越容易接近(就特定事件的频率而言)。这是我们在上面定义的点密度测量中考虑间隔长度的一个原因。其次,观察 y 轴上区间[0,1]内的值(因为这正是实直线负部分映射的地方)。随着采样点数量的增加,更多的红色点被压缩在[0,1]区域,这也在 X₋的点密度测量中突出显示,从 11.04 增加到 5065.49!最后,让我们看看采样点的分布,以及对数正态的 pdf。


左图:标准正态分布(N=10,000)中采样点的概率密度函数。右图:对数正态分布的概率密度函数。红色虚线表示这种分布的模式,大约为 0.37。
在左图中,我们可以观察到从中抽取 10,000 个样本的标准正态分布的 pdf。该直方图类似于正态分布,在 n 值较高时看起来更好。在右图中,我们可以观察到对数正态分布。我还突出显示了该图上的模式(红线),该模式在大约 0.37 处向正态分布的μ (=0)右侧移动。现在,让我们试着用分析计算来调和这个结果。
让我们从找到 Y 的 C.D.F. 开始,我们可以用它来找到 P.D.F.

在上面,我们使用对数函数的单调性。φ表示标准正态分布的 CDF。在最后一步,我们标准化 X,即将其转换为标准正态(我们在这里求解一般情况,其中均值为μ,方差为σ)。接下来,我们可以对此进行微分以找到 PDF。

如果我们看一下一个标准法线的 CDF 的方程,这个计算并没有那么难。最后,为了找到模式,我们可以最大化 PDF,这相当于为对数正态分布找到最频繁的值。

这给出了从正态分布生成的对数正态分布模式的一般表达式。让我们验证一下我们是否得到了与模拟相同的答案。

尽管估计的模式似乎与真实值略有偏差,但根据大数定律,它收敛到真实值(快速证明:如果我们对模式使用指示函数,并计算我们对模式采样的次数,则当 N → ∞时,比例将收敛到 e⁻)。
感谢你阅读这篇文章。我希望你和我一样喜欢探索这个想法。如果你对代码(用 R 写的)感兴趣,你可以在这里找到它。
解释任何机器学习模型的模型不可知方法
解释方法概述:排列特征重要性,部分依赖图,石灰,SHAP 等。

Lukasz Szmigiel 在 Unsplash 上的照片
越来越多的公司正在使用复杂的机器学习模型,比如神经网络和梯度推进机器。他们使用复杂模型的原因是因为它们优于传统模型,如决策树或逻辑回归。使用复杂模型的负面影响是你不能直接解释那些模型。你不想要一个有偏见的模型,或者一个根据陌生或不相关的知识做出选择的模型。过去的经验,如亚马逊或教师的经历,表明了解释复杂模型的重要性。了解模型做出决策的原因有积极的副作用,例如,您可以了解模型发现的新模式,并了解更多关于您的数据的信息。
对于机器学习模型的可解释性已经做了很多研究。有不同的方法来解释机器学习模型。最容易的划分是在可解释模型和模型不可知方法之间。可解释的模型是解释它们自己的模型,例如从决策树中你可以很容易地提取决策规则。模型不可知方法是可以用于任何机器学习模型的方法,从支持向量机到神经网络。在本文中,重点将放在与模型无关的方法上。还有另一篇关于可解释模型的文章。
资料组
您可以使用模型可解释性的一个领域是医疗保健。为了了解模型如何决定一个人是否患有心脏病,我们使用了克利夫兰数据库中具有以下特征的数据集:

我们将尝试用随机森林来预测目标,并用模型不可知的方法来解释这个模型。你可以在 Kaggle 上找到心脏病 UCI 数据集。
密码
这篇关于模型不可知方法的文章(以及关于可解释模型的文章)的代码可以在 GitHub 上找到。
模型不可知的方法
不幸的是,直接解释大多数机器学习模型是不可能的。对于像随机森林、梯度增强机器和神经网络这样的流行模型,你需要与模型无关的方法。目前有一些有趣的方法可用,如排列特征重要性,部分依赖图(PDP),个体条件期望(ICE)图,全局代理模型,局部可解释模型不可知解释(LIME)和 Shapley 加法解释(SHAP)。我们将深入研究这些方法,并讨论它们的优缺点。
置换特征重要性
你使用 scikit-learn⁴的特征重要性吗?这些特征的重要性基于标准的平均减少,如基尼系数(对于决策树和随机森林)。最好使用排列特征重要性。使用这种方法,重要度是基于在置换要素值时测量预测误差的增加。因此,您需要计算两次预测误差,在特征置换之前和之后。预测误差之间的差异越大,该特征越重要。
现在,我们将比较随机森林的 scikit-learn 特征重要性和置换特征重要性:

sci kit-了解随机森林的要素重要性

随机森林的置换特征重要性
哇!如果您将 scikit-learn 特征重要性中的特征顺序与置换特征重要性进行比较,一切都会被打乱!根据最后一张图片,我们应该尝试排除 chol 和 exang,因为当这些特征被置换时,模型执行得更好!对于特征 fbs、trestbps 和 age,什么都不会发生(如果我们忽略方差)。
部分相关图(PDP)
这些图有助于可视化预测目标和一个或多个特征之间的平均部分关系。通过强制所有实例具有相同的特征值来创建图。然后对这些实例进行预测,并对它们进行平均,这就给出了该特征值的平均预测值。由于可视化,大多数时候只研究一两个特征。
为了对 PDP 有一个概念,下面你可以在心脏病数据集中看到 PDP 的例子。前两个图像在 x 轴上有一个特征,在 y 轴上有心脏病的概率。在第三幅图中,您可以看到两个特征(一个在 x 轴上,一个在 y 轴上)。

一般来说,如果心率加快,患心脏病的可能性就会增加。

胸痛类型是一个分类变量。如果这个值等于 1、2 或 3,那么当这个值等于 0 时,你患心脏病的可能性更大。

该图显示了 oldpeak 和 cp 与平均预测值的关系。当胸痛类型等于 1、2 或 3 且 oldpeak 具有低值时,患心脏病的概率(> 0.63)比 cp 等于 0 且 oldpeak 具有高值(< 0.37).
In PDPs, you force all the instances to have the same feature value. The plots can be misleading if you only have a small amount of instances who have a certain feature value. It’s better to include data distributions in your plot, so you can see if the data is equally distributed.
Watch out with PDPs! It is assumed that features for which you compute the partial dependence are independent. So they shouldn’t be correlated with other features. You can also easily miss complexity of the model, because the predictions are averaged.
Individual Conditional Expectation (ICE)
A way to deal with the problem of missing complexity in the model with PDPs is to show them in combination with ICE plots⁵. ICE plots are more detailed and show how the prediction of each instance changes when a feature value is changed. In the following images, every blue line represents one instance.

Combined PDP and ICE plot for the ‘sex’ variable.
The ICE plot for the sex (female = 0, male =1) variable is shown above. The average is the thick line in the middle (same line as in the PDPs). You see that for some instances the prediction changes a lot when sex is changed to male, but for some instances the prediction almost stays the same, although it always has a negative effect to be female. On the bottom of the image you see the data distribution.

Combined PDP and ICE plot for the ‘chol’ variable.
This is interesting! The cholesterol variable shows that the pattern is more complicated than you would expect from the PDP, because the instances are spread out all over the place and often they don’t follow the pattern of the thick line. Sometimes a higher cholesterol has a (small) positive effect and sometimes the effect is negative. There are not that many instances that have a cholesterol value above 400 (check out the distribution on the bottom) so we should be careful here!
With ICE plots we solved the problem of PDPs by showing more complexity, but what about the independent features problem? That problem isn’t solved with ICE plots, because the feature you plot still needs to be uncorrelated with the other features.
Global Surrogate Models

Workflow for a global surrogate model.
Global surrogates are really easy to understand, that’s an advantage of this method. First you build a black box model on the training data with the real labels. Then you let the model predict the labels for the same data and you build an interpretable model on the data with the 预测标记)时高得多。因为代理模型是可解释的,并且建立在黑盒模型的预测之上,所以您将了解黑盒模型是如何做出预测的。
这种方法很好,因为它很直观。有一些缺点。可解释模型的性能会比黑盒模型差(否则你应该替换黑盒模型)。您需要决定对于可解释模型的性能,什么是可接受的度量。除此之外,可解释模型得出关于黑盒模型的结论,而不是关于数据的结论。
局部可解释的模型不可知解释(LIME)
当你想解释一个单独的预测时,你可以用石灰。用 LIME 训练一个局部代理模型。这个可解释的替代模型可以用来解释个体预测。您不仅可以在表格数据上使用石灰,还可以在图像或 text⁶.上使用石灰
下图直观地展示了 LIME 的工作方式。红色加号和蓝色圆点是来自不同班级的样本。粉色和蓝色区域之间的边界是黑盒模型的决策边界。如果您想解释下图中的大红色加号,您可以创建其他实例,这些实例与可解释模型的局部决策边界(虚线)非常接近。这个局部决策边界比粉红色和蓝色区域之间的边界(黑盒模型的决策边界)更容易解释。

让我们从测试集中取一个新记录:

而现在,让我们用石灰来解释这个记录的预言:

千钧一发!我们看到,对于真(心脏病= 1)的预测概率略高于假(心脏病= 0)的预测概率。在右侧,我们可以看到哪些特征对预测贡献最大。
就像其他解释方法一样,你需要小心使用石灰。同一个记录解释两次,解释可以不一样!另一个缺点是你只能解释一个实例,所以不可能解释整个黑盒模型。
沙普利加法解释(SHAP)
如果您想要一种非常好的方式来显示特征值对预测的贡献,您应该使用 SHAP。对于这种方法,沙普利值(来自博弈论)和石灰是 combined⁷.简而言之,shapley 值使用联合来查看某个特征值对最终预测的贡献。

被调查的记录和我们用于石灰的记录是一样的。预测概率等于 0.53。该值略低于基准值 0.5486。红色特征,如 cp 和 oldpeak 增加了患心脏病的可能性,而 ca 和 thal 降低了患心脏病的可能性。

如果我们将测试集中的所有样本旋转 90 度,将它们堆叠并按相似性排序,我们会得到上面的图像。

上面的汇总图显示了高或低特征值的不同 SHAP 值。如果您查看 ca 特征,您会发现当该特征的值较低时,SHAP 值较高,这意味着患心脏病的可能性较高。该图还向我们展示了最重要的特征到最不重要的特征(从上到下)。
SHAP 值有一些优势,因为我们可以用它们来进行局部和全局的解释。而且他们有很强的理论基础。开始时,它们不能处理相关的特性,但是研究表明,它们可以和相关的 features⁸.一起使用 SHAP 值的一个问题是计算速度,当您拥有许多要素时,计算时间将显著增加。
希望您可以使用这些方法来调查您的数据和模型!
[1] J. Dastin,亚马逊废除了对女性有偏见的秘密人工智能招聘工具 (2018),路透社
[2]c·奥尼尔,数学毁灭的武器 (2016),新冠
[3] A. Altmann 和 L. Toloş,排列重要性:一个修正的特征重要性度量 (2010),生物信息学
[4] T. Parr,K. Turgutlu,C. Csiszar 和 J. Howard,当心默认随机森林重要性 (2018),解释. ai
[5] A. Goldstein,A. Kapelner,J. Bleich 和 E. Pitkin,窥视黑盒内部:用个体条件期望图可视化统计学习 (2014),《计算和图形统计杂志》
[6] M. T .里贝罗,s .辛格和 C. Guestrin,“我为什么要相信你?”解释任何分类器的预测 (2016),ResearchGate
[7] S. Lundberg 和 S. Lee,解释模型预测的统一方法 (2017),NIPS
[8] K. Aas 和 M. Jullum,解释特征相关时的个体预测:更精确地逼近 Shapley 值 (2019),ArXiv
模型假设—已解释
对于商业读者
听说过模型假设吗?它们是什么?为什么他们重要?模型是现实的简化版本,对于机器学习模型来说,这没有什么不同。要创建模型,我们需要做出假设,如果这些假设没有得到验证和满足,我们可能会陷入一些麻烦。
如果这些假设没有得到验证和满足,我们可能会陷入一些麻烦。

在 Unsplash 上 Marcin Jozwiak 拍摄的照片
每个(机器学习)模型都有一套不同的假设。我们对数据、不同变量之间的关系以及我们用该数据创建的模型做出假设。这些假设其实大部分都是可以验证的。因此,你一直想做的一件事就是问一问假设是否得到了验证。有些假设仅与得出关于关系的结论的相关(例如,温度上升 1 度表明冰淇淋销量增加 4%),其他假设也与相关到预测结果(我们预测明天 x 的冰淇淋销量)。
这些假设大部分实际上都是可以验证的。
让我们看看为最简单的模型所做的假设。线性回归。
假设 1:固定回归变量
这实际上意味着,我们假设变量(输入数据)不是随机变量,而是固定的数字,如果我们重新进行实验(我们以同样的方式再次收集数据),我们会得到同样的结果。
与固定回归变量相反的是随机回归变量,通常被视为从更广泛的人群中取样的数据。如果是这种情况,那么你只能根据数据得出“有条件的”结论。这意味着你可以得出同样的结论,但只是基于这些数据。您不能在数据集之外进行概化。
结论 —如果你的数据是人群的(代表),你就是好。否则,请尝试收集代表性数据,或者仅根据您创建模型的数据得出结论。
对于商业读者 —如果你有所有客户的数据,并想预测新客户的行为,只要你的目标客户是相似类型的客户,你就没问题。如果没有,你可能会对这些新客户提供完全错误的建议或结论,并在获得他们之前失去他们。所以向询问数据集的代表性。
所以要求数据集的代表性。如果数据对总体有代表性,你就是好的。
假设 2:随机干扰,零均值
我们假设我们的模型的误差范围是随机的,并且在所有的观测中平均水平。这是你可以实际检查的东西。
结论——取所有误差项的平均值,并验证它在统计上是否明显不同于零。如果是→您可能希望调整您的型号,并且包含更多条款。
对于商业读者——你希望你的模型预测正确的事情。如果这个条件不满足,你要么总是 - 下的,要么就是高估了。例如,如果你的误差项平均为 3.5,这意味着你平均高估了 3.5。如果你预测股票价格并自动做出交易决定,这可不是一件好事。所以求误差项的平均值。
求误差项的平均值,以了解你是高估还是低估了。如果平均分在 0 左右,你就是好的。
假设 3:同质性
扰动的方差存在并且相等。这意味着我们期望模型中的误差对于所有不同的数据点来说都具有相似的大小,有时也称为方差齐性。这仅适用于我们所观察的关系在所有不同层面上都是线性的情况。
例如,如果你在研究收入和旅游支出之间的关系。低收入人群的息差将比高收入人群的息差小得多,原因很简单,因为高收入人群将在消费方面提供更多选择。结果是你的模型被“拉”向了错误的方向(因为它假设传播在任何地方都是相等的,并试图减少误差),高收入数据点对模型的影响比低收入数据点大得多。
此外,这将影响对参数的重要性做出结论的能力。
结论 —如果你想用你的模型进行同质性推断测试,如果你发现你的误差项不是均匀分布的 → 标度(你的变量 (s)之一)或者使用 WLS 。
对于商业读者 —您希望误差项具有均匀的方差,否则,您的一些数据点可能会对模型产生太大的影响,并且会干扰其余数据点的视图。这不是什么大问题,你的模型仍然会预测正确的事情。因此,如果这是你所关心的,这是一个让滑。
如果你只是想预测,让这一个溜走。如果你想推断关系,最好做出改变。
假设 4:无相关性
误差项是不相关的。如果不是这样,实际上还有改进模型的潜力。这意味着,如果误差项存在相关性,仍然有“解释”的能力。违反这一假设的结果是模型系数的偏差。这些系数从误差项中“吸收”信息。
结论——如果你想使用你的模型进行推理,测试你的误差项中的相关性,如果你发现相关性→ 添加更多的变量。
对于商业读者来说——如果你有兴趣对关系做出结论,用错误的术语来说相关性是不可行的。误差项中的相关性也告诉你有一个潜在的来改进模型并生成更好的** 预测。**
如果存在相关性,你需要改进模型,你的预测会变得更好,你的推断也会有意义。
假设 5:常数参数
您使用模型估计的参数是固定的未知数字。首先,如果他们是已知的,就不需要模型。我们之所以假设它们是固定的,是因为我们希望避免随着时间的推移而改变。这是指收集数据的时间。如果随着时间的推移有变化,我们可能需要包括两个不同的参数或只取最近的数据样本。
违规的一个例子是,如果通过询问客户向他们的养老基金支付了多少钱来收集数据,并且去年更改了年度最高金额,突然您可以再添加几千美元。在这种情况下,您的参数不是常数,您需要考虑这一点。
结论——你能有把握地说手头的数据是由相同的过程产生的,没有随着时间的推移改变吗?→那你就好。如果不是→你会想要调整你的模型,并允许 n 个新变量进入。
对于商业读者来说— 这里的关键是数据是由相同的流程产生的,数据收集是否随着时间的推移而改变?如果是这样的话,根据不同变量之间的关系得出的结论将不成立,并且对即将到来的新数据的预测实际上可能被低估或高估。
随着时间的推移,数据收集是否发生了变化?然后调整模型,否则随着新数据的到来,你可能会高估或低估你的预测。
假设 6:线性模型
不同变量之间的关系是线性关系。如果不是这种情况,你会有一个非线性的关系,你不能估计一个模型适合你的数据。因此,当您创建线性模型时,您需要假设线性。这不是一个线性关系,如果你这样对待它,你会估计许多人在 50 摄氏度的街道上。
结论——测试线性度(散点图起作用),如果关系不是线性的→ 转换你的变量或者使用不同的模型****
对于商业读者来说——这种类型的模型决定了我们试图预测的内容和进入模型的内容之间的结构。如果结构不满足(在这种情况下是线性),模型无意义。如果期望关系是线性的,可以进行逻辑思考。如果不是,并且如果测试告诉关系不是线性的→这是一个不通过并且模型需要调整以对关系以及预测做出结论。
如果模型是线性的,但是关系不是线性的,你可以忘记推断和预测。
假设 7:常态
这种假设认为误差项是正态分布的。我们希望验证这一点,因为我们希望能够对显著性进行测试,以及定义我们的置信区间。
结论— 画出你的错误术语,并验证它们是否正常。如果未正常分布→再次检查您的线性假设。
对于商业读者 —这个假设让我们可以告诉我们一些关于我们对模型中的估计值有多确定的事情。如果这个假设不成立,我们无法对关系做出结论,但是我们可以预测。****
没有这个假设,我们就不能说我们对估计的参数有多大把握。我们可以根据新数据进行预测。
需要帮助吗?通过https://aibridge.ch/contact/联系我。
灵感来自:Christiaan Heij、Paul de Boer、Philip Hans Franses、Teun Kloek 和 Herman K. van Dijk 所著的《经济计量方法及其在商业和经济学中的应用》
关于我:我是一名分析顾问,也是当地一所商学院“人工智能管理”研究的主任。我的使命是帮助组织利用人工智能创造商业价值,并创造一个数据科学家可以茁壮成长的环境。 报名参加我的 通迅 获取关于 AI 管理的新文章、见解和祭品 此处 。
模型的复杂性、准确性和可解释性
可解释的人工智能(XAI)
模型可解释性的重要性以及可解释性如何随着复杂性的增加而降低

平衡之举:模型准确性与可解释性|鸣谢:kasiastock/Shutterstock
简介
复杂的现实世界挑战需要建立复杂的模型,以最大的准确性给出预测。然而,它们最终并不具有高度的可解释性。在本文中,我们将探讨复杂性、准确性和可解释性之间的关系。
动机:
我目前是一名机器学习研究员,正在从事一个关于可解释机器学习的项目。本系列基于我对克里斯托弗·莫尔纳尔的书的实现:“可解释的机器学习”。
系列:可解释的机器学习
第 1 部分:模型的复杂性、准确性和可解释性
我们将使用真实世界的数据集来展示机器学习模型的复杂性、准确性和可解释性之间的关系。我们将尝试越来越复杂的机器学习模型,看看准确性如何提高,可解释性如何降低。
第 2 部分:拆箱“黑盒”模型
我们将使用最复杂的模型作为我们的最终模型,因为它提供了非常高的准确性,并使用“可解释的机器学习”实施模型不可知的方法来解释该模型,请参考此处:
https://medium . com/@ sajee . a/unboxing-the-black-box-models-23b 4808 a3 be 5
内容:
- 准确性与可解释性
- 复杂性和准确性
- 可解释性的重要性
- 实施——复杂性如何增加,可解释性如何降低
模型准确性与可解释性
在现实世界中,在处理任何问题时,理解模型准确性和模型可解释性之间的权衡是很重要的。业务用户希望数据科学家建立更高精度的模型,而数据科学家面临的问题是向他们解释这些模型如何做出预测。
什么更重要?? —拥有一个对未知数据给出最佳精确度的模型,或者即使精确度很低也能理解预测。下面我们比较了传统模型的准确性和可解释性。

准确性与可解释性
该图显示了一些最常用的机器学习算法以及它们的可解释性。就机器学习模型在下面如何工作而言,复杂性增加了。它可以是参数模型(线性模型)或非参数模型(K-最近邻)、简单决策树(CART)或集成模型(Bagging 方法-随机森林或 Boosting 方法-梯度 Boosting 树)。复杂的模型通常能提供更准确的预测。然而,解释它们更困难。
模型复杂性和准确性

典型的精度-复杂度权衡
任何监督机器学习算法的目标都是实现低偏差和低方差。然而,这在现实生活中是不可能的,我们需要在偏差和方差之间进行权衡。
线性回归假设线性,但实际上关系相当复杂。这些简化的假设给了很高的偏差(训练和测试误差很高),并且模型趋向于欠拟合。高偏差可以通过使用复杂函数或添加更多功能来降低。这时复杂性增加了,准确性也提高了。在某一点上,模型将变得过于复杂,并且倾向于过度拟合训练数据,即低偏差但高方差用于测试数据。像决策树这样的复杂模型往往会过度拟合。
通常有过度拟合机器学习模型的趋势,因此,为了克服这一点,我们可以使用重采样技术(交叉验证)来提高对未知数据的性能。
模型可解释性的重要性
在预测的影响较高的用例中,理解【为什么】做出某个预测是非常重要的。知道“为什么”可以帮助你更多地了解问题、数据和模型可能失败的原因。
了解可解释性的原因:
- 好奇心&学习
- 安全措施 —确保学习无误
- 调试到检测模型训练中的偏差
- 可解释性增加社会接受度
- 调试和审计机器学习模型
实施:
数据集—自行车租赁预测
自行车租赁数据集可以从 http://archive.ics.uci.edu/ml/datasets/Bike+Sharing+Dataset 的 UCI 机器学习库找到。
该数据集包含华盛顿自行车租赁公司 Capital-Bikeshare 每天租赁自行车的数量,以及天气和季节信息。
目标: 根据天气和当天的情况预测将会租出多少辆自行车。
输入变量:
- Total_count (target):租赁自行车总数,包括休闲自行车和注册自行车
- 年:年(0: 2011,1:2012)
- 月:月(1 到 12)
- 小时:小时(0 到 23)
- Temp:以摄氏度为单位的标准化温度。这些值是通过(t-t_min)/(t_max-t_min),t_min=-8,t_max=+39(仅限于小时刻度)得出的
- Atemp:归一化的感觉温度,单位为摄氏度。这些值是通过(t-t_min)/(t_max-t_min),t_min=-16,t_max=+50(仅在小时范围内)得出的
- 湿度:标准化湿度。这些值除以 100(最大值)
- 风速:归一化风速。这些值除以 67(最大值)
- 假日:一天是否是假日
- 工作日:一周中的某一天
- 工作日:如果一天既不是周末也不是假日,则为 1,否则为 0
- 季节:季节(1:冬天,2:春天,3:夏天,4:秋天)
- 天气:
- 1:晴朗,少云,部分多云,部分多云
- 2:薄雾+多云,薄雾+碎云,薄雾+少云,薄雾
- 3:小雪,小雨+雷雨+散云,小雨+散云
- 4:暴雨+冰托盘+雷雨+薄雾,雪+雾
功能:
Index(['month', 'hr', 'workingday', 'temp', 'atemp', 'humidity', 'windspeed','total_count', 'season_Fall', 'season_Spring', 'season_Summer','season_Winter', 'weather_1', 'weather_2', 'weather_3', 'weather_4','weekday_0', 'weekday_1', 'weekday_2', 'weekday_3', 'weekday_4','weekday_5', 'weekday_6', 'holiday', 'year'],dtype='object')
探索性数据分析:

骑自行车的人在一段时间内会增加
从 2011 年到 2012 年的两年间,骑自行车的次数增加了。

相关矩阵
风速和湿度略有负相关。Temp 和 atemp 携带相同的信息,因此高度正相关。因此,为了构建模型,我们可以使用 temp 或 atemp。

目标直方图:大部分时间骑自行车的次数在 20-30 次/小时左右
预处理:
删除像因果图这样的特征,因为它们与 total_count 相同。同样,对于与 temp 相同的 atemp 等要素,删除一个要素以减少多重共线性。对于分类特征,使用 OneHotEncoding 方法将其转换为更适合回归模型的格式。
模型实现:
我们将研究越来越复杂的模型,看看可解释性如何降低。
- 多元线性回归(线性,单一性)
- 决策树回归器
- 梯度推进回归器
多元线性回归:
涉及多个变量的线性回归称为“多元线性回归”或“多元线性回归”。

多元线性回归(MLR)的目标是对解释变量(自变量)和响应变量(因变量)之间的线性关系进行建模。从本质上说,多元回归是普通最小二乘(OLS)回归的延伸,涉及一个以上的解释变量。
回归带有一些在真实世界数据集不实用的假设。
- 线性
- 同方差(恒定方差)
- 独立性ˌ自立性
- 固定功能
- 多重共线性缺失
线性回归实现:

线性回归结果:
Mean Squared Error: 19592.4703292543
R score: 0.40700134640548247
Mean Absolute Error: 103.67180228987019
使用交叉验证:

解读多元线性回归:
解释线性模型更容易,我们可以研究每个变量的系数,以了解它对预测的影响以及截距的斜率。
方程的截距(Bo):
截距表示当没有任何特征有任何影响(x=0)时 y(目标)的值。
18.01100142944577
对应于 x 列的系数有助于我们理解每个特征对目标结果的影响。

这意味着“温度”每增加一个单位,自行车骑行次数就会增加 211.05 个单位。这同样适用于 rest 特征
决策树回归器:
决策树的工作原理是以贪婪的方式将数据反复分割成不同的子集。对于回归树,它们被选择来最小化所有子集内的 MSE(均方误差)或 MAE(平均绝对误差)。
CART —分类和回归树:
CART 采用一个特征,并确定哪个分界点使回归任务的 y 方差最小。方差告诉我们一个节点中的 y 值围绕其平均值分布了多少。分割基于最小化基于决策树中使用的所有子集的平均值的方差的特征。
DecisionTreeRegressor(ccp_alpha=0.0, criterion='mse', max_depth=None,max_features=None, max_leaf_nodes=15,
min_impurity_decrease=0.0, min_impurity_split=None,
min_samples_leaf=1, min_samples_split=10,
min_weight_fraction_leaf=0.0, presort='deprecated',
random_state=None, splitter='best')
决策树结果:
决策树回归结果更符合数据。
Mean Squared Error: 10880.635297455
R score: 0.6706795022162286
Mean Absolute Error: 73.76311613574498Decision tree split:
决策树比线性回归更适合模型。 R 平方值约为 0.67 。
使用交叉验证:

决策树图:

决策树回归器输出
解释决策树:
特征重要性:
特征重要性基于减少使用该特征的所有分割的最大方差的重要性。一个特征可以用于多个分割,或者根本不使用。我们可以将每个 p 特征的贡献相加,并得到每个特征对预测贡献的解释。
我们可以看到特征:hr、temp、year、workingday、season_Spring 是用来分割决策树的特征。


决策树回归器—特征重要性条形图
梯度推进回归器:
增强是一种集合技术,其中预测器不是独立产生的,而是顺序产生的。梯度提升使用决策树作为弱模型。
Boosting 是一种将弱学习者转化为强学习者的方法,通过以渐进、累加和顺序的方式训练许多模型,并在最终模型中最小化损失函数(即回归问题的平方误差)。
GBR 因其 Boosting 技术而比其他回归模型具有更高的精度。这是竞赛中最常用的回归算法。
GradientBoostingRegressor(alpha=0.9, ccp_alpha=0.0, criterion='friedman_mse',init=None, learning_rate=0.1, loss='ls', max_depth=6,max_features=None, max_leaf_nodes=None, min_impurity_decrease=0.0, min_impurity_split=None, min_samples_leaf=1, min_samples_split=2,
min_weight_fraction_leaf=0.0, n_estimators=100, n_iter_no_change=None, presort='deprecated',random_state=None, subsample=1.0, tol=0.0001,validation_fraction=0.1, verbose=0, warm_start=False)
来自 GBR 的结果如下:
Mean Squared Error: 1388.8979420780786
R score: 0.9579626971080454
Mean Absolute Error: 23.81293483364058
梯度推进回归器给出了最佳的 R2 平方值 0.957 。然而,要解释这个模型是非常困难的。
解释集合模型:
集合模型肯定属于“黑盒”模型的范畴,因为它们由许多潜在复杂的个体模型组成。
使用随机选择的特征对袋装数据按顺序训练每棵树,因此通过检查每棵树来获得对决策过程的全面理解是不可行的。
第二部分:解释梯度推进回归器模型的模型不可知方法—https://medium . com/@ sajee . a/unboxing-the-black-box-models-23b 4808 a3 be 5
参考文献:
https://christophm.github.io/interpretable-ml-book/
模型压缩:需求和重要性
了解深度学习的不同模型压缩技术的需求和优势

物联网连接的智慧城市(图片来自 Pixabay 的 Tumisu
无论你是计算机视觉新手还是专家,你都可能听说过 AlexNet 在 2012 年赢得了 ImageNet 挑战赛。那是计算机视觉历史上的转折点,因为它表明深度学习模型可以以前所未有的准确度执行被认为对计算机来说非常困难的任务。
但是你知道 AlexNet 有 6200 万个可训练参数吗?
有趣的权利。
2014 年推出的另一个流行模型 VGGNet 甚至有更多,1.38 亿个可训练参数。
那是 AlexNet 的 2 倍多。
你可能会想…我知道模型越深入,它的表现就越好。那么为什么要突出参数的数量呢?网络越深,显然参数会越多。

已知卷积神经网络(CNN)网络的复杂性和准确性。#参数和#MACCs 在上表中按百万顺序排列 [8]
当然,这些深度模型已经成为计算机视觉行业的基准。但是当你想创建一个真实世界的应用时,你会选择这些模型吗?
我想我们在这里应该问的真正问题是:你能在你的应用程序中使用这些模型吗?
暂时不要想这个问题!
在得到答案之前,让我在这里转移一下话题。(不过可以随意跳到最后。)
到 2030 年,物联网设备的数量预计将达到 1250-5000 亿台,假设其中 20%将拥有摄像头,带摄像头的物联网设备将是 130-1000 亿台的市场。[9,10,11]
物联网摄像头设备包括家庭安全摄像头(如 Amazon Ring 和 Google Nest ),当你到家时会打开门,或者如果它看到陌生人会通知你,智能车辆上的摄像头会帮助你驾驶,或者停车场的摄像头会在你进出时打开大门,等等!其中一些物联网设备已经在某种程度上使用人工智能,其他设备正在慢慢赶上。

与物联网设备连接的智能家居系统(图片来自 Pixabay 的 Gerd Altmann
许多现实世界的应用需要实时的设备处理能力。自动驾驶汽车就是一个很好的例子。为了让汽车在任何道路上安全行驶,它们必须实时观察道路,如果有人走在汽车前面,它们就必须停下来。在这种情况下,需要在设备上实时处理视觉信息并做出决策。
那么,回到之前的问题:你能在你的应用中使用这些模型吗?
如果您正在使用计算机视觉,那么您的应用很有可能需要物联网设备,从物联网设备的预测来看,您的公司很不错。
主要挑战是物联网设备资源受限;它们的内存有限,计算能力较低。模型中可训练的参数越多,它的规模就越大。深度学习模型的推理时间随着可训练参数数量的增加而增加。此外,与具有较少参数的较小网络相比,具有较高参数的模型需要更多的能量和空间。最终结果是,当模型规模很大时,很难在资源受限的设备上部署。虽然这些模型在实验室中取得了巨大的成功,但它们在许多现实应用中并不可用。
在实验室中,您拥有昂贵的高速 GPU 来获得这种级别的性能[1],但当您在现实世界中部署时,成本、功耗、散热和其他问题会阻碍“加大投入”策略。
在云上部署深度学习模型是一种选择,因为它可以提供高计算和存储可用性。然而,由于网络延迟,它的响应时间很短,这在许多实时应用程序中是不可接受的(不要让我开始谈论网络连接对整体可靠性或隐私的影响!).
简而言之,AI 需要靠近数据源进行处理,最好是在物联网设备本身上!
这给我们留下了一个选择:减小模型的尺寸。
制造能够在边缘设备的限制下运行的较小模型是一个关键的挑战。这也不会影响精度。仅仅有一个可以在资源受限的设备上运行的小模型是不够的。它应该表现良好,无论是在准确性还是推理速度方面。
那么,如何在有限的设备上安装这些模型呢?如何让它们在现实应用中可用?
这里有一些技术可以用来减少模型的大小,以便您可以将它们部署在您的物联网设备上。
修剪
修剪通过删除对性能不敏感的冗余、不重要的连接来减少参数的数量。这不仅有助于减少整个模型的大小,而且节省了计算时间和能量。

修剪(来源
优点:
- 可以在训练期间或训练后使用
- 可以改善给定架构的推理时间/模型大小与准确性的权衡[12]
- 可以应用于卷积层和全连接层
缺点:
- 一般来说,没有切换到更好的架构更有帮助[12]
- 受益于延迟的实现很少,因为 TensorFlow 只带来模型大小的好处

原始模型和修剪模型的速度和大小权衡[13]
量化
在 DNN,权重存储为 32 位浮点数。量化是通过减少位数来表示这些权重的思想。权重可以量化为 16 位、8 位、4 位甚至 1 位。通过减少使用的比特数,深度神经网络的规模可以显著减小。

二进制量化(源)
优点:
- 量化可以在训练期间和之后应用
- 可以应用于卷积层和全连接层
缺点:
- 量化的权重使得神经网络更难收敛。需要较小的学习速率来确保网络具有良好的性能。[13]
- 量化的权重使得反向传播不可行,因为梯度不能通过离散的神经元反向传播。需要近似方法来估计损失函数相对于离散神经元输入的梯度[13]
- TensorFlow 的量化感知训练本身在训练过程中不会进行任何量化。仅在训练期间收集统计数据,这些数据用于量化训练后的数据。所以我不确定以上几点是否应该被列为缺点
知识升华
在知识提炼中,大型复杂模型在大型数据集上进行训练。当这个大型模型可以对看不见的数据进行归纳并表现良好时,它就被转移到一个较小的网络中。较大的模型也称为教师模型,较小的网络也称为学生网络。

知识蒸馏(来源)
优点:
- 如果你有一个预先训练的教师网络,训练较小的(学生)网络所需的训练数据较少。
- 如果你有一个预先培训过的教师网络,较小的(学生)网络的培训会更快。
- 可以缩小网络规模,而不管教师和学生网络之间的结构差异。
缺点:
- 如果您没有经过预先培训的教师网络,可能需要更大的数据集,并且需要更多的时间来训练它。
选择性注意
选择性注意是指关注感兴趣的物体或元素,而忽略其他物体(通常是背景或其他与任务无关的物体)。它的灵感来自于人眼的生物学。我们看东西的时候,一次只关注一个或者几个物体,其他区域就模糊掉了。

选择性注意(来源)
这需要在你现有的人工智能系统的上游添加一个选择性注意力网络,或者如果它服务于你的目的,单独使用它。这取决于你试图解决的问题。
优点:
- 更快的推理
- 较小的型号(例如只有 44 KB 的人脸检测器和裁剪器!)
- 精度增益(通过将下游 AI 仅聚焦在感兴趣的区域/对象上)
缺点:
- 仅支持从头开始培训
低秩因子分解
使用矩阵/张量分解来估计信息参数。具有 m×n 维且秩为 r 的权重矩阵 A 被更小维的矩阵代替。这项技术有助于将一个大矩阵分解成更小的矩阵。

低秩因子分解(来源)
优点:
- 可以在训练期间或训练后使用
- 可以应用于卷积层和全连接层
- 当在训练期间应用时,可以减少训练时间
最棒的是,以上所有技术都是相辅相成的。它们可以原样应用,或者与一种或多种技术结合使用。使用三级流水线;修剪、量化和霍夫曼编码为了减小预训练模型的大小,在 ImageNet 数据集上训练的 VGG16 模型从 550 MB 减小到 11.3 MB。
上面讨论的大多数技术都可以应用于预先训练的模型,作为减少模型大小和提高推理速度的后处理步骤。但是它们也可以在训练期间使用。量化越来越受欢迎,现在已经被纳入机器学习框架。我们可以期待修剪很快会成为流行的框架。
在本文中,我们研究了将基于深度学习的模型部署到资源受限设备(如物联网设备)的动机,以及减少模型大小的需求,以便它们在不牺牲准确性的情况下适合。我们还讨论了一些压缩深度学习模型的现代技术的利弊。最后,我们谈到了每种技术既可以单独使用,也可以结合使用。
确保在培训后和培训期间探索适合你的模特的所有技巧,并找出最适合你的方法。
哪种模型压缩技术对你最有效? 下面留下评论。
想训练自己的 选择性注意网络 ? 点击这里 。
原载于www.xailient.com/blog。**
关于作者
Sabina Pokhrel 在xai lient工作,这是一家计算机视觉初创公司,已经建造了世界上最快的边缘优化物体探测器。
参考文献:
- https://towards data science . com/machine-learning-models-compression-and-quantization-simplified-a 302 ddf 326 f 2
- c .布西卢、r .卡鲁阿纳和 a .尼古列斯库-米齐尔(2006 年 8 月)。模型压缩。第 12 届 ACM SIGKDD 知识发现和数据挖掘国际会议论文集(第 535–541 页)。**
- 程,王,周平,张,等(2017)。深度神经网络的模型压缩和加速综述。 arXiv 预印本 arXiv:1710.09282 。
- http://mitch Gordon . me/machine/learning/2020/01/13/do-we-really-need-model-compression . html
- https://software . Intel . com/content/www/us/en/develop/articles/compression-and-acceleration-of-high-dimensional-neural-networks . html
- https://towards data science . com/the-w3h-of-Alex net-vggnet-resnet-and-inception-7 baaaecccc 96
- https://www . learnopencv . com/number-of-parameters-and-tensor-sizes-in-convolutionary-neural-network/
- Véstias 议员(2019 年)。基于可重构计算的边缘卷积神经网络综述。算法, 12 (8),154。
- https://technology . informa . com/596542/IHS-markit 称,到 2030 年,物联网设备的数量将激增至 1250 亿台
- https://www . Cisco . com/c/dam/en/us/products/parallels/se/internet-of-things/at-a-glance-c45-731471 . pdf
- 莫汉,a .,高恩,k .,卢耀辉,李伟伟,陈,X. (2017 年 5 月)。2030 年的视频物联网:一个有很多摄像头的世界。在 2017 IEEE 国际电路与系统研讨会(ISCAS) (第 1–4 页)。IEEE。
- Blalock,d .,Ortiz,J. J. G .,Frankle,j .,& Guttag,J. (2020)。神经网络剪枝是什么状态?。arXiv 预印本 arXiv:2003.03033
- 郭,于(2018)。量化神经网络方法和理论综述。 arXiv 预印本 arXiv:1808.04752 。
使用 Apache Beam 和数据流进行模型部署
使用 Apache Beam / Dataflow、BigQuery 和 Scikit-learn 快速简单地操作分析模型的选项

照片来自 Pixabay
对于一些数据科学家来说,操作数据科学模型有时可能会有压力。你的模型越复杂,生产时你面临的困难就越多。在开发客户流失分类器时,你是否后悔过将 5 个不同的模型集合在一起?别担心,阿帕奇光束来救援了。
在开始使用 Apache Beam 之前,让我们看看您有哪些选项来操作您的模型。第一个选项是在云上创建一个虚拟机(VM)来执行计算任务。很公平,但是设置和管理虚拟机将是一件令人头痛的事情,因为它需要大量的手动工作(您的云工程师不会喜欢它!).Cloud Dataproc 有助于释放管理需求,并且是另一个值得考虑的好选择,因为它提供的计算资源只在一次运行期间有效。然而,您需要花一些时间将 Python 代码转换成 PySpark 或 Scala,更不用说您可能无法完全复制您在 Python 中使用这些编程语言所做的事情。如果这些问题对你来说不是一个挑战,请查看我的同事发来的这篇精彩的帖子,但是如果是,数据流可能是一个不错的选择,因为:
- 您可以使用 Apache Beam Python SDK 编写 Python3 来创建运行在 DataflowRunner 后端的数据管道。那是什么意思?这意味着您可以编写 Python3 来构建您的管道,并直接操作您基于 Python 构建的 ML 模型,而无需将它们转换为 Scala 或 PySpark。
- Dataflow 是一项完全托管的服务,通过自动扩展员工资源来最大限度地减少延迟、处理时间和成本。
简介:什么是什么?
你可能会对“Google Cloud Dataflow”和“Apache Beam”这两个术语感到困惑,就像我第一次知道它们一样,那么这些术语是什么意思呢?Apache Beam 是一个开源的统一模型,它允许用户通过使用一个开源的 Beam SDK(Python 就是其中之一)来定义数据处理管道,从而构建一个程序。然后,管道由 Beam 管道运行器翻译,由分布式处理后端(如 Google Cloud Dataflow)执行。越来越清晰了?好,我们开始吧!
用例
假设您是一家电信公司的数据科学家,您的工作是使用您的数据工程团队准备的客户数据来预测哪些客户将会流失。为了便于说明,我将在本教程中使用常见的电信客户流失数据集。这是一个非常小的公共数据集,形状为(7043,21)。你可以下载一下,在这里快速看一下专栏描述。
理解数据后,让我们对其进行预处理,并使用 Scikit-learn 的 RandomForestClassifier 开发一个简单的模型。
预处理输入并开发分类器模型
该模型在测试集上给出了 81.17%的准确度和 AUC = 0.7023,然而这不是理想的性能。让我们假设您的涉众对此有些满意,那么您将需要转储这个模型以将其部署到生产中。
from joblib import dump
dump(clf, 'model_rf.joblib')
您的数据管道
图 1 展示了管道的组件。在高层次上,这是一个 BigQuery-to-BigQuery 流,其中你的 BigQuery (BQ)表形式的输入数据随后被云数据流处理,并被馈送到包含你的客户的 ID 和流失概率的输出 BQ 表。在处理过程中,云数据流需要云存储来保存临时和暂存文件,以及加载您开发的随机森林模型。气流组件用于调度此作业,因为您的公司可能需要每周或每月运行客户流失预测。日程安排是一个完全不同的话题,不是这篇文章的重点(我可能会在以后写另一篇关于这个话题的文章)。

图 1:通用数据管道
循序渐进的指导
设置 GCP
为了运行数据管道,您需要做一些初始设置。我假设你已经有了一个 GCP 账户,那么你需要遵循 6 个步骤来设置这里提到的数据流服务,否则你需要注册一个 300 美元的免费账户,链接。
您还需要在云存储中创建一个 bucket,其中包括您的模型(作为 joblib 或其他序列化形式)和两个文件夹:temp 和 staging。

图 2: GCS 存储桶结构
接下来,您需要在 BigQuery 中创建一个数据集和一个表。为了便于说明,我将使用去掉标签的相同训练数据集(来自上面的 Kaggle 链接),,但在实践中,您将对新的和未标记的数据运行预测。上传数据到 BQ 是非常简单的,你可以很容易地按照这个指令去做。注意,在上传之前您需要清理一下您的数据,因为TotalCharges中有非数字值。
上传前的小数据清理
对数据流进行编码
首先,每个波束管道中有 4 个关键术语:
- 流水线:每个 Beam 程序的基础部分,一个
Pipeline包含整个数据处理任务,从 I/O 到数据转换。下面的代码片段展示了一个 4 步管道图;三个关键元素I/O transform、PCollection、PTransform被包裹在Pipeline里面。
波束管道示意图
- I/O 转换:读取输入,写入输出。
3.p 集合:表示某种形式的数据,可能是分布式的。初始输入PCollection通常从某个数据集加载,通常是 JSON 格式(类似于带字典的),包括键和键值{“key1”: “key_value1”, “key2”: “key_value2”,…, “keyn”: “key_valuen”}。在上面的例子中,p是一个PCollection。
- PTransform:
PTransform是通过管道运算符(|)应用于PCollection的每个元素的变换操作。束核变换有几种类型,包括:ParDo(并行 do)GroupByKey``CoGroupByKey``Combine``Flatten``Partition。更多详情请参考官方梁文档,本帖不一一介绍。
回到我们的用例,类似于开发阶段,您需要实现两个转换步骤,包括输入格式化和预测。应用这些步骤需要由分布式处理功能以DoFn对象的形式定义的ParDo转换。下面的代码片段展示了与上面提到的两个步骤相对应的两个DoFn对象。
用于 ParDo 变换的 DoFn 类
最后,输出数据通过 WriteToBigQuery 模块写入 BigQuery。如果目标表不存在,可以通过设置create_disposition = 'CREATE_IF_NEEDED '来创建一个新表,如果目标表已经有一些数据,可以通过设置write_disposition参数来截断、追加或引发错误。
有几个强制管道选项需要在代码中或运行时设置为参数(然后您需要在代码中用ArgumentParser()定义参数)。
管道选项
运行管道
有几种不同的方式来运行管道。您可以从 Cloud Shell、本地终端或您的编程 IDE 运行它。这真的取决于你。在本教程中,我选择从本地终端运行作业。
首先,记得激活您用来编写管道代码的 Python 虚拟环境。我是通过 Anaconda 创建的,所以我需要运行:
conda activate /path/to/your/venv
如果您没有将环境变量GOOGLE_APPLICATION_CREDENTIALS设置为包含您的服务帐户密钥的 JSON 文件的路径。你需要现在就做。之后,您就可以进入包含您的作业的目录并运行它了。
cd path/to/your/main.py
python main.py
如果作业正在运行,您可以转到 Dataflow 选项卡来查看您的管道,您将能够看到如图 3 所示的管道。

图 3:云数据流管道
如果您在屏幕右侧查看您的工作信息,您将能够看到云数据流的自动缩放机制,它会根据数据量自动将员工数量从 1 增加到 4。

图 4:云数据流作业信息
这项工作通常在 15 分钟内完成(检查您的许可和启动 worker 也需要相当长的时间)。随后,您的输出就可以使用了。让我们尝试运行下面的查询,然后看看输出。
SELECT * FROM `your_project.your_dataset.your_tablename` LIMIT 1000

图 5:对最终输出的查询
关键要点
部署您的数据科学模型有时很困难,但我希望本教程对那些选择云数据流进行操作的人有用。让我总结一下我在本教程中提到的一些要点。
- Apache Beam 允许您在 Python 3 中开发数据管道,并作为后端运行程序在云数据流中执行它。
- 云数据流是一种完全托管的服务,支持资源的自动扩展。
- [最终输出 p 集合]=([初始输入 p 集合] | [第一 p 变换] | [第二 p 变换]
- 您可以通过云外壳、本地终端或 IDE(如 PyCharm)运行数据流作业。
本教程主要关注如何使用 Apache Beam 创建和运行数据管道。实际上,您可能还需要安排作业每天、每周或每月自动运行。此外,我还没有从您的服务帐户中提到 Cloud IAM 的作用。目前,我将它设置为项目的所有者,这已经足够了,我敢打赌你的 Cloud DevOps 团队在实践中很少让这种情况发生。我计划在接下来的文章中讨论这些话题。敬请期待!
感谢阅读。快乐学习。
我的 Github 库:https://github.com/nghiamab/dataflow-demo
参考
[1] Nike Nano,Apache Beam+Scikit learn(sk learn)(2019),https://medium . com/@ niklas . sven . hansson/Apache-Beam-Scikit-learn-19 F8 ad 10d 4d
[2]官方光束编程指南:【https://beam.apache.org/documentation/programming-guide/
机器学习中的模型漂移
机器学习模型应该如何以及何时被重新训练
随着时间的推移,观念、人和社会都发生了巨大的变化。曾经最先进的东西现在已经过时了;同样,现在是新鲜的想法可能几年后会被遗忘。同样,理解变化对企业至关重要。想象一下 15 年前的任何一家手机制造公司。如果他们没有升级到智能手机,他们能养活自己吗?很可能不会。虽然拥有一部普通手机在 15 年前是一种常态,但需求急剧转向智能手机。未能跟上客户行为变化步伐的公司受到的冲击最大。
随着我们进入一个由数据和分析决定的世界,机器学习模型已经成为商业决策的主要驱动力。与任何其他商业战略一样,这些模型需要随着时间的推移进行修改,其背后的技术原因是“模型漂移”。虽然大多数课程、文章和帖子定义了机器学习(ML)生命周期,以数据收集开始,以在各自环境中部署 ML 模型结束,但他们忘记了 ML 生命周期中一个非常重要的特征,即模型漂移。
它本质上意味着目标变量和自变量之间的关系随时间而变化。由于这种漂移,模型不断变得不稳定,预测随着时间不断变得错误。让我们借助简单的线性回归,从技术角度来理解它。在线性回归中,我们简单地映射自变量 x_i 来预测目标变量 y :
y =α+β_ 1 * x1+β_ 2 * x2+β_ 3 * x3+…
其中, α 为截距, β_i 对应于变量 x_i. 的系数
通常,我们假设这种映射是静态的,即我们假设系数 β_i (和截距 α )不随时间变化,并且控制目标变量 y 预测的关系对于未来数据也是有效的。这一假设并不是在所有情况下都适用。如果没有,就会对业务造成严重威胁。这是因为组织的利润在很大程度上依赖于这些模型;虽然这些模型可能代表了开发时的情况,但它们肯定不一定适用于未来。由于潜在条件的这些变化,随着时间的推移,预测将开始变得越来越不准确。

在 Unsplash 上 engin akyurt 拍摄的照片
漂移的类型
模型漂移可以分为两大类。第一种叫做‘概念漂移’。当目标变量本身的统计属性发生变化时,就会发生这种情况。显而易见,如果我们试图预测的变量的真正含义发生了变化,那么这个模型就不能很好地适应这个更新的定义。咄!
第二种也是更常见的类型是“数据漂移”。当预测值的统计属性发生变化时,就会发生这种情况。同样,如果潜在的变量在变化,这个模型注定会失败。这么明显!这种情况可能发生的一个典型例子是当数据中的模式由于季节性而改变时。不管什么样的商业模式在夏天行得通,在冬天可能就行不通了。虽然假期期间航班需求激增,但航空公司很难在淡季保持上座率。另一个例子是当个人偏好改变时,这可以与开头提到的智能手机例子相关。

如何解决这个问题?
解决这个问题的最好方法是不断地重新调整模型。基于过去的经验,可以估计漂移何时开始在模型中蔓延。在此基础上,可以主动地重新开发模型,以减轻与漂移相关的风险。
对于数据随时间变化的情况,称重数据可能是一个不错的选择。例如,基于最近的交易来决定某些参数的金融模型可以包含赋予最近的交易更大权重而赋予过去的交易更小权重的特征。这不仅确保了模型的稳健性,也有助于避免与漂移相关的潜在问题。
对抗模型漂移的更复杂的方法是对变化本身建模。开发的第一个模型保持静态,并作为基线。现在,作为最近数据中行为变化的结果,可以建立新的模型来校正该基线模型的预测。
模型应该多长时间重新训练一次?
既然我们已经看到了最常见的解决方案涉及到模型的持续重新训练,那么问题就来了,这需要多久进行一次。对此有多种解决方案,每种方案都因情况而异。
有时候,问题会自己出现。虽然等待问题发生不是最优雅的方法,但对于新模型来说,这仍然是唯一的选择,因为没有过去的历史来了解什么时候事情可能会变得任性。当问题出现时,可以对问题所在进行调查,并进行修改以避免将来出现此类问题。
在其他时候,与模型中处理的实体相关的数据观察季节性的模式。在这里,模型应该根据这些季节进行重新训练。例如,随着节日期间消费的增加,信贷机构需要有特殊的模型来处理这种模式的突然变化。
然而,检测漂移的最佳方式是持续监控。需要以连续的时间间隔监控与模型稳定性相关的指标。这个间隔可以是一周、一个月、一个季度等等。这取决于领域和业务。监控模式可以是手动的,也可以是自动脚本,只要观察到突然的异常,就会触发警报和通知。
这就把我们带到了本文的结尾。正如赫拉克利特的名言,“变化是唯一不变的”。记住这一点,准备好接受和监控这些变化的组织注定会成功。

谢谢大家!
模型演进:从独立模型到模型工厂(第 3 部分)
数据科学家来自火星,软件工程师来自金星(第三部分)
从火星上的数据科学家到金星上的软件工程师到地球上的模型工

软件、模型和数据交汇处的新兴角色
在本系列的第 1 部分中,我们检查了软件和模型之间的关键区别,在第 2 部分中,我们探索了将模型和软件混为一谈的十二个陷阱。这两篇文章都着重强调了这些问题,但没有提供任何解决方案。在接下来的两篇文章中,我们将重点提供一些解决这些差距的具体实践。
人工智能对全球经济的潜在贡献以及投资人工智能的重要性在商业和技术界得到了广泛认可。在最近的一项首席执行官调查中,超过 85%的首席执行官认为人工智能将极大地改变他们做生意的方式。虽然只有 6%的受访者承认拥有企业范围的人工智能计划,但近 20%的受访者计划在近期内部署企业范围的人工智能。在企业范围内部署模型的最大挑战之一是部署模型所花费的时间。在最近的一项调查中,近 58%的受访公司表示部署模型需要 31 天或更长时间。
模型进化
随着我们跟踪并帮助企业从构建简单的分析模型发展到嵌入大型事务性应用程序的更复杂、不断学习的模型,我们看到了三个不同的阶段。
独立模型阶段:在这个阶段,公司通常使用命令行界面和 Jupyter 笔记本在独立的基础上部署模型。这些模型通常也更简单,不与数据源或软件应用程序实时交互。公司在一个更大的职能或业务单位的单个团队中使用这些模型。此外,数据科学家通常负责确定模型的范围、设计、构建、部署和维护。这一阶段对于公司证明人工智能/人工智能模型的价值以及了解更广泛使用的挑战至关重要。
预测即服务阶段:一旦公司能够展示这些模型的价值,就需要将这些模型的预测提供给企业内的其他团队。此外,随着大公司中的多个团队开始构建他们自己的模型,出现了大量的重复工作、技能,以及架构、设计模式和工具的异构性。这导致了模型交付、部署和监控更多的工程方法。软件工程的进步,包括微服务架构、Dockers 和 Kubernetes,被用来为其他软件系统提供模型预测服务。例如,NER(命名实体识别)模型可以开发一次,然后作为 REST API 服务提供给其他 web 应用程序。
模型工厂阶段:更先进的公司正在转向工厂模型,在那里部署数百(如果不是数千)个模型。自动化 CD/CI(持续部署/持续集成)管道正在与 CL(持续学习)管道相结合。这使得公司能够自动接收数据,并定期或持续地对模型进行再培训。这允许公司拥有灵活的部署策略,允许自动或半自动的模型再培训。这解决了我们前面讨论过的部署时间和成本的一些重大挑战。自动化部署还为自动化模型的持续监控提供了机会——这对于解决我们在本系列的第 2 部分中讨论的回报实现陷阱至关重要。最后,集成的端到端模型谱系跟踪、实验和推理日志记录支持模型的工厂式操作。
随着组织沿着这三个阶段前进,他们部署的模型的数量和这些模型的复杂性增加了——本质上,模型构建、部署和监控的规模和范围增加了。第二,数据接收从批处理模式转变为流式和实时模式。这有助于增加数据的数量、种类和速度。数据版本化与模型版本化相集成,以支持模型的快速实验和再训练。第三,软件应用程序封装了传统软件(软件 1.0 代码)以及机器学习模型(软件 2.0 ),从而真正实现了解决业务问题的“智能软件”。

模型演变:从独立模型到模型工厂(来源:普华永道分析)
新兴角色
软件、模型和数据组合的规模和复杂性不断增加,新的角色不断涌现。软件开发的角色包括业务分析师、系统分析师、架构师、开发人员、测试人员、开发运营人员(DevOps)等。当您更仔细地检查时,这些角色反映了软件生命周期的范围、设计、开发、操作和维护阶段。随着机器学习模型和软件 2.0 范式的出现,我们看到了许多新的技能和角色。
数据科学家的角色出现在模型进化的独立阶段,当时需要结合三种技能——主题或领域专业知识、数学和统计、计算机科学和大数据。在 2012 年的一篇文章中,谷歌首席经济学家哈尔·瓦里安称数据科学家为 21 世纪最性感的工作。
未来 10 年最性感的工作将是统计学家。人们认为我在开玩笑,但是谁会想到计算机工程师会是 20 世纪 90 年代最性感的工作呢?
随着我们开始从模型的第一个基础阶段进入预测即服务的第二个阶段,需要有人来扩展和优化由数据科学家开发的概念验证模型。这导致了机器学习或 ML 工程师的出现。 Jeff Hale 对 ML 工程师的角色描述如下:
机器学习工程师采用科学家创建的显示预测前景的模型数据,并将它们转化为在生产中表现良好的代码。
随着越来越多的 ML 模型被开发、缩放和部署,以手工方式维护这些模型的任务变得不可行。随着组织开始走向模型工厂的第三阶段,我们看到了另一个角色的出现——机器学习或(ML)操作(或简称为 MLOps )。 MLOps 专家寻求在生产中可靠高效地部署和维护 ML 系统。
在模型工厂阶段,我们看到了数据、软件和模型的集成。机器学习管道整合了数据和代码。Cristian Breuel 在他关于 MLOps:作为工程学科的机器学习的论文中进一步阐述了这一点。
根本原因是 ML 和传统软件有一个根本的区别: ML 不仅仅是代码,它是代码加数据。ML 模型,即您最终投入生产的工件,是通过将算法应用于大量训练数据而创建的,这将影响模型在生产中的行为。至关重要的是,模型的行为还取决于它将在预测时接收的输入数据,而这是您无法提前知道的。
简而言之,有四个关键维度——软件工程、软件运营、统计和机器学习以及数据管理。数据工程师处于软件工程和数据管理的交叉点;ML 工程师处于软件工程、统计学和机器学习的交汇点。数据科学家处于统计学和机器学习、数据管理和领域专业知识的交汇点(下图中未显示)。最后,MLOps 处于软件操作、统计和 ML 以及数据管理的交叉点。

软件、统计& ML 和数据管理交叉领域的四个新兴角色(来源:普华永道分析)
这四个角色都有非常具体的技能组合。下面给出了详细的角色描述和相关技能。

四个新兴角色的主要角色和技能组合(来源:普华永道分析)
摘要
在这篇博客中,我们追踪了软件、模型和数据是如何结合在一起,创建一个解决业务问题的强大方法的演变过程。意识到软件和模型之间的差异(第一部分)允许我们认识到解决构建智能软件中的一些陷阱(第二部分)所需的独特技能。虽然它们是必要的,但它们不足以解决我们到目前为止概述的将软件、模型和数据结合在一起的所有挑战。在接下来的几篇博客中,我们将讨论与开发方法和模型生命周期相关的问题。
作者: Anand S. Rao , Joseph Voyles 和张世南
模型可解释性与 JRT 人工智能
JRT(合理、负责和透明)人工智能在解决数据驱动的业务问题时,在模型可解释性方面变得至关重要
在今天的场景中,模型可解释性变得越来越普遍,并成为人工智能模型和使用的主流。期望是理解正在发生的细节以及任何算法或模型的“如何”部分,或者关于用于解决业务用例的机器学习管道。企业需要控制人工智能模型的决策,以及它是如何制定的。这里的尝试是涵盖这个主题,考虑围绕这些概念的使用和执行,包括引用几个可以利用的库。
理解模型操作或操作层面也很重要。显然,它更多的是关于采用和使用,因为更多的模型正在被开发,有必要对多个模型、实验等进行模型管理和监控。这与我们为什么要考虑最佳模型以及为什么要考虑这个问题相关。
我们应该把重点放在任何合理、负责和透明的解决方案上。(因此简称为 JRT 人工智能的需要——合理的、负责任的和透明的人工智能)。
我想围绕以下议程要点来组织内容:
一、模型可解释性的挑战
二世。什么是可解释的 AI/ML 解决方案?
三。为什么我们需要 AI/ML 解决方案的可解释性?
四。我们真的一直需要可解释性吗?
五、框架
六。各种技术/库
七。代码示例
八。参考文献
现在,让我们深入这些维度。
i)模型可解释性的挑战:
- 什么是“可解释的”的定义-通常一些参数用于决策,为什么解决方案达到特定的结果,它最有可能犯的错误以及如何纠正这些潜在的错误。
- 竞争优势的丧失——当我们看到商业秘密被泄露,那么业务竞争优势也会在某种程度上被泄露。这个理解起来可能简单,也可能不简单。
- 无法理解解释——让我们假设我们绘制了一个复杂性对时间的图表,AI 解决方案的可解释性的复杂性增加到超过某个特定点。所以我们不能真正理解解释什么,如何解释等等。
- 有些方面是无法解释的——比如说有些决定很难解释。有时,我们基于人类的情感和非理性因素做出决定,然后通过逆向工程来解读这些决定。如果我们在大多数时候不解释我们的决定,那么就有理由争论机器如何能够做出决定?
- 各种情况下的几种折衷方案——情况可能有很多种。一个场景是很少或没有可解释性,但质量很高,另一个场景是有更多可解释性,但输出质量很低。权衡并不总是可以接受的。很明显,它会根据业务需求、目标、目的或优先级而有所不同。
需要考虑管理风险方面的某些立法和发展参考,这可能会影响使用数据科学模型、算法或解决方案的决策。可以按照以下参考阅读一些参考资料:
a)https://the hill . com/policy/technology/364482-立法者-介绍-两党-人工智能-立法
b)https://www . federal reserve . gov/supervision reg/Sr letters/Sr 1107 . htm
什么是可解释的人工智能/人工智能解决方案?
我想到的一些问题是:
我们如何建立对 ML 模型或解决方案的信任?
我们如何知道 ML 解决方案中发生了什么?
我们如何确保我们了解模型/解决方案的细节,以便我们可以期望在未来的一段实施持续时间内保持一致性?(也就是说,如果我的预测精度是 X%,那么在接下来的几个模型中,它如何能保持在 X%左右,或者与一些+-阈值限制保持一致,以及我们如何能控制它?)等等。

图片由 Kamal Mishra 提供
为什么我们需要人工智能/人工智能解决方案的可解释性?
嗯,可能是各方面的原因。
a)模型偏差、伦理、公平:模型结果是否存在任何歧视?例如,根据过去几年的历史数据提升员工,根据性别等特定因素提升员工。贷款违约问题或贷款授予决策——它是否会因某些影响目标参数的预想数据参数而受到影响?
b)特征的因果关系:一组图像上相似外观分类器的例子。我们需要更多的数据来消除因果关系吗?是否有多个背景不同的图像?例子可以是任何这样的方面。
c)监管需求:模型结果是否满足监管需求?例如,当我们考虑跨地理区域的各种法规、欧盟法规、GDPR 相关要点等等时。
d)关键领域和行业特定要求:金融、医疗保健、风险、司法等一些行业领域比其他行业更加重要。
e)调试或故障排除以及了解更多信息的能力:为什么 AI/ML 模型表现得像它正在表现的那样?
可能有许多这样的类别、指针和需求,对于这些类别、指针和需求,对 AI/ML 解决方案内部正在发生的事情的解释变得更加重要。
我们真的一直都需要可解释性吗?
这可能并不总是需要的。我们讨论了可能需要它的几个维度。我们需要理解,这些可能是基于某些特定场景而需要的:
a)它会影响我的最终客户或最终用户吗?—这很重要。如果它不影响我们的最终客户,那么只要我们在解决问题,就可能不需要解释。例如,我们希望改进一些内部流程,但这可能不会影响最终结果。假设我们希望对通话记录进行分类,因为一次通话导致了一位不满意的客户。只要我们取得了不错的业绩和成果,并且满足了最终客户的需求,我们就是好的。
b)我们还可以想出另一个不会再次影响最终客户的例子。这与解决人工智能问题的自动化引擎有关。如果问题研究得很好,我们对最终结果很有信心。对于光学字符识别情况,我们可以获得大量训练数据来训练我们的模型,并且它可以依赖于手头任务的注释的良好性能或结果。所以它并不真正需要引擎内部的可解释性方面。
五)框架:
框架可以基于范围和模型类型。

图片由 Kamal Mishra 提供
全球和当地的解释各不相同。如果我们可以基于给定的完整数据集解释因变量和自变量之间的条件交互作用,那么我们认为这是“全局解释”。如果我们能够解释因变量和自变量之间关于单一预测结果的条件交互作用,那么我们可以认为这种情况是“局部解释”。

图片由 Kamal Mishra 提供
vi)各种技术/库:
下面是一些用于 ML 模型可解释性的库的例子。还有其他方法,下面并不详尽。
a)石灰(LlocalIinterpretableMmodel-agnosticEx 解释)
SHAP(ShapelyAadditive Explanation)
Eli 5(Explain melikeIam 5)
d) PDP ( P 工艺 D 依赖 P 批次)
石灰、SHAP 和 ELi5 之间的比较如下图所示,反映了它们之间的比较以及它们通常遵循的方法。

图片由 Kamal Mishra 拍摄——对比图显示 Eli5 与 LIME 和 SHAP
vii)代码示例:
本文并不打算展示如何使用 Python 中的这些库进行编码。相反,它使用一个示例来说明实际实现的样本数据集(为了便于理解,我们将引用通用的 titanic 数据集,并使用其中一个库及其方法)以及如何解决业务需求。
场景 1:
目的是预测泰坦尼克号数据集中的幸存乘客。在执行了 EDA、特征工程和建模的常规步骤后,我们正试图借助 LIME 软件包解释提供预测的模型。对于我们的场景 1,21 岁的男性乘客乘坐乘客等级 3,从等级 Q 出发,他没有幸存,被认为是一个案例。让我们看看我们的模型如何预测他的生存。

图片由 Kamal Mishra 提供
解读一:
模型预测 421 号乘客将会死亡。(87%预测)影响最大的参数是性别(男性),这大大降低了他的生存几率。下一个参数是乘客等级 3。这也有助于减少他的生存机会。同时,如果我们注意到对“将生存”因素贡献更大的参数是诸如“年龄”的参数。任何年龄小于 22 岁的人都有更高的生存机会,因此由于该乘客年龄为 21 岁,该参数对此有一定影响。总之,从这个简单的例子中,我们可以看出哪些参数的可解释性对模型的整体决策有多大影响。
场景二:
我们将乘客 id 310 视为场景 2,并尝试以类似的方式进行分析。下面是类似场景 1 的插图和结果。

图片由 Kamal Mishra 提供
解读二:
模型非常有信心这位乘客会活下来,而且“活下来”的几率是 99%。对其起作用的最大因素是性别,该乘客是女性,随后是可以以类似方式分析和解释的其他参数。
详情请参考我的 GitHub 代码参考此处。
总结归纳,我们可以使用不同特性和库的组合,基于业务案例和数据集进行解读。我们可以看看下面的一般型号的咨询。当然,这些都是基于从业者推荐的某些经验。我们需要了解数据、业务目标和情况,以便能够推荐和使用最有效的方法,并对其进行适当的解释。解释是关键,这也是价值和影响所在。
a)需求:可解释和可预测的需求
- 观察—局部[主要使用石灰、SHAP 等,基于树的方法,基线使用广义线性混合模型];全局[通用线性混合模型基线、基于树的方法、结构化内容的 SHAP]
- 可以收集—[广义线性混合模型,使用多种算法进行基线预测]
b)需要:仅可解释性
- 结构化数据源—观察[广义线性混合模型,基于树的模型];可以收集【广义线性混合模型】
- 图像内容来源—本地[SHAP、莱姆等。;];全局[用概念激活向量测试]
c)需求:仅预测
- 准确性可能是这里的关键和最重要的方面
- 神经网络、GBMs、RFs 或 RRFs
viii)参考文献:
石灰:
a) 可解释的机器学习:让黑盒模型变得可解释的指南 —作者克里斯托夫·莫尔纳尔
b) 马可等人的论文(2016) :“我为什么要相信你?”:解释任何分类器的预测
d) 参考 Marco 使用 iris 数据集的实例—使用 iris 数据集
SHAP:
a) 论文:解释 Lundberg 和 Lee 模型预测的统一方法
b) 论文:Lundberg 等人 2019 年为树集合提供一致的个性化特征属性
关于interpret ML:ML 可解释性的统一框架——Harsha Nori 等人 2019——解释了 blackbox 和 glassbox 的有趣方面。黑盒(包括石灰,SHAP,部分依赖,敏感性分析)和玻璃盒(包括可解释的助推,线性模型,决策树,规则列表)
免责声明 :这里的帖子是来自我的经验、想法和各种来源的阅读的个人观点,不一定代表任何公司的立场、策略或观点。
什么是模型治理?
办公时间
以及为什么您应该关心…

由 Vladimir Mokry 在 Unsplash 上拍摄的照片
随着组织变得更加数据驱动,机器学习模型正在越来越多的应用中使用。尽管这些模型有用且有价值,但它们的使用代表了一种新的风险来源——即构建不良的模型可能导致糟糕的商业决策、糟糕的客户体验,甚至导致一个组织违法。
模型治理通过建立确保模型质量的过程来帮助最小化风险。
什么是模型治理?
开放风险手册将模型治理定义为:
公司或组织的内部框架,控制模型开发、验证和使用过程,分配责任和角色等。
从本质上来说,MG 框架是一组指定如何在组织内管理模型的过程。这些过程可能会为以下问题提供答案:
- 谁拥有模型的所有权?
- 一款车型是否符合相关规定?
- 模型在哪些数据上被训练过?
- 在开发的每个阶段需要什么样的验证和批准?
- 我们如何在部署后监控模型?

Silas k hler 在 Unsplash 上拍摄的照片
谁拥有模型的所有权?
爱丽丝:“我正在做一个项目,做 x。”
鲍勃:“啊,是的,在 Z:/Archive/Misc/Old stuff 中有一些代码可以做到这一点。
爱丽丝:“这里没有很多评论,你能告诉我它是如何工作的吗?”
鲍勃:“不要问我,我认为它是由克里斯开发的,他在你加入前几年就离开了……”
如果你的组织有很多人在做数据分析和模型构建,那么记录谁创建了哪些模型以及谁最终对模型的质量和性能负责是很重要的。这可以实现更好的协作、更少的重复工作、质量标准的实施、处理问题的更低风险,并避免当有人离开时“孤立的”模型在以太中丢失。
拥有一个分类良好的模型目录,加上每个案例中清晰的所有权,通过允许人们更容易地建立在其他人的工作之上,促进了协作。知道是谁开发了一个特定的模型,会让其他人更容易接受这个工作,并将其合并到他们自己的项目中。通过查看他们负责的模型,它也给了人们一个关于一个人擅长什么技术/领域的想法。
质量标准是另一个受益者。符合质量方针可以包含在某人的个人目标中。当实施新的质量标准时,明确的责任使得管理和分配所需的工作变得更加容易。
对于面向公众的模型,风险要高得多。因此,维护责任链接更为重要,这样可以快速诊断和修复问题,同时将对业务的影响降至最低。例如,如果一家在线服装零售商发现,在某些情况下,他们的产品推荐系统正在推荐用户已经购买的产品,那么每次这种情况发生时,他们都会失去潜在的销售额。与不太熟悉的人相比,快速找到该模型的创建者/所有者将使问题得到更快的处理。对于生产模型,记录第二“模型专家”是很重要的,这样如果模型所有者不知何故联系不上,仍然有人有足够的知识来帮助。
当一个“模型所有者”离开组织,或者改变角色时,将他们的模型的所有权移交给其他人是至关重要的,这样他们的工作就能继续成为资产。有一个清晰的模型所有权记录,通过给你一个他们工作的全面列表,在他们的同事之间适当地分配,使这个任务变得更容易。

一款车型是否符合相关规定?
通常,模型必须遵守与其领域相关的当地法规和法律。
2018 年,亚马逊放弃了他们的人工智能招聘工具,该工具旨在对求职者的简历进行评级和排名。原来他们的算法是惩罚包含“女性”一词的应用程序,这种偏见会违反许多国家的反歧视法律。现在,这个模型的开发者不太可能明确包括性别、种族或任何人口统计变量。然而,该模型能够找到性别的代理,使其能够利用历史性别偏见做出技术上更准确的预测。
网上超市的推荐系统可能会发现某本杂志的购买者和香烟之间有很强的联系。然而,许多国家和州会以健康为由禁止广告或“推荐”香烟,因此需要将商业规则添加到模型中,以确保它不会开始鼓励人们吸烟。
为了降低你的模特违反当地法律的风险,问自己以下问题?
- 哪些法律法规与模型的功能相关?有没有我们想要应用的更广泛的道德标准?
- 我们如何测试模型的输出,以确保符合这些法律?
- 一旦投入生产,我们将如何监控模型的输出以确保持续的符合性?

法比奥·萨索在 Unsplash 上拍摄的照片
模型在哪些数据上被训练过?
机器学习模型的输出与它的训练数据不可分割地联系在一起。如果在开发过程中发现了问题,重要的是能够追溯到它被训练的精确数据,这样您就可以重现这个问题。有时精确的数据将不再可用,但是记录训练数据的来源仍然是值得的,以便任何进一步的再训练可以尽可能接近地匹配。
这也有助于确定发生了多大的模型漂移,以及模型在历史数据上有多稳定。如果您可以在不同的日期范围内训练您的模型,并获得非常不同的结果,这表明您的模型没有捕捉到潜在的长期趋势,这将最终导致其准确性随着时间的推移而下降。(参见监控后期制作一节)。

在开发的每个阶段需要什么样的验证和批准?
当一个模型通过开发管道时,验证和批准应该被应用和记录,以确保高标准的质量。这降低了任何问题进入生产的可能性,并增加了第二层责任。
验证过程可能包括:
- 数据质量检查
- 特征工程的单元测试
- 培训/测试/验证数据集和交叉验证的最佳实践。
- 符合性测试(参见相关法规章节)
- 代码质量(格式、命名约定、位置、版本控制和注释)
- 证明文件
从根本上说,每一个投入生产的模型都应该由至少一个不直接参与其开发的合格人员进行彻底的检查和批准。这迫使开发人员证明他们的决定是正确的,并将他们的工作置于新的眼光的审视之下。

马库斯·温克勒在 Unsplash 上的照片
我们如何在部署后监控模型?
一旦部署了一个模型,您应该定期监控它的性能,以确保它的行为符合预期,并保持高标准的输出。
模型漂移是一种现象,通过这种现象,模型所依据的数据中存在的统计趋势发生了变化。例如,由于新冠肺炎疫情的影响,2019 年训练的销售预测模型可能会出现准确性下降。对模型准确性的定期监测将揭示出它何时下降到足以证明需要重新训练的程度。
对于使用生产中收集的数据不断重新训练的模型,保持定期监控尤为重要。谁会忘记微软的 Tay,在推出几个小时后,就开始大肆宣扬种族主义和性别歧视。我们只能说“实时”数据比训练数据更有风险。
为什么我应该关心模型治理?
随着组织变得更加数据驱动,机器学习模型将被用于越来越多的应用中。因此,围绕这些模型建立稳健的流程至关重要,以确保它们的质量,降低风险和有效性。
模型生命周期:从想法到价值
数据科学家来自火星,软件工程师来自金星(第四部分)
价值范围界定、发现、交付和管理

由作者根据 Youtube 视频创作帝王蝶蜕变延时 FYV 1080 高清
在本系列的第 1 部分中,我们研究了软件和模型之间的主要区别;在第 2 部分中,我们探索了用软件合并模型的十二个陷阱;在第 3 部分中,我们探讨了模型的演变。在本文中,我们经历了模型的生命周期,从最初构建模型的概念到最终交付这些模型的价值。
我们将模型的整个生命周期分为四个主要阶段——范围界定、发现、交付和管理。虽然这个模型生命周期和典型的软件生命周期有很多相似之处,但是也有很大的不同,这源于我们开始这个系列的软件和模型之间的不同。在这里,我们回顾了四个阶段和这些阶段中的九个步骤。
价值范围
这可以说是模型生命周期中最关键的阶段;这是确定在构建模型中是否有任何商业价值、模型的成功度量以及我们打算如何使用模型的阶段。范围界定阶段由两个步骤组成:
业务和数据理解
构建一个模型来自动化一项任务、做出一个决策或采取一项行动的想法可以来自各种不同的来源。它可以是来自业务的“拉力”,以提高生产率、减少时间或削减成本(所谓的自动化路径)或做出更好的决策(所谓的分析路径)。它也可以是软件或 It 组织的“拉动”,这些组织已有一个软件或正在构建一个软件,他们希望通过添加独特的个性化、优化、推荐或模型提供的许多其他特征来使其变得“智能”。
它也可以是来自自动化、分析或人工智能组织的“推动”,以利用机器学习模型或机器人的独特特征。这也可能是数据组织的一种“推动”,数据组织看到了其独特数据资产的价值,可以利用这些资产获得竞争优势。
例如,已经构建了聊天机器人并证明了其在一个业务部门中提高客户参与度的好处的自动化或人工智能团队可能希望在多个业务领域中构建类似的聊天机器人。类似地,已经建立了 NLP 模型以在一个功能(例如,财务)中提取结构化信息的分析或 AI 组可能会推动在其他功能(例如,法律)中建立类似的模型。
不管是“推动”还是“拉动”来构建模型,这里有一些企业必须寻求回答的关键问题:
- 什么是业务目标——需要提高效率或效果的业务活动、决策或行动?
- 使用现有的工具、软件、人员和过程可以实现业务目标吗?
- 我们有足够的数据来建立模型吗?如果不能,能否在一段时间内对数据进行注释和收集?
- 业务接受的理想模型性能标准是什么?
- 当满足业务标准时,将如何使用模型?
回答这些问题需要不同团队的合作,包括业务、数据、分析和软件专业人员。如果问题 2 的答案是肯定的,那么就不需要建立模型(使用 ML、NLP、计算机视觉)。如果第二个问题的答案是否定的,那么就有必要进一步探究这个问题。如果 3-5 的答案不可用,团队应在开始流程的下一步之前努力获得答案。为了真正实现 AI/ML 模型的全部价值,我们认为,在继续下一步之前,必须回答问题 1-5——即使答案可能会基于以下步骤的进一步调查而修改。
解决方案设计
一旦我们有了业务模型的规范,我们就可以继续设计解决方案。这里我们指的不仅仅是解决方案的经典定义,即人们通常通过查看 IT 堆栈或技术工具或供应商来实现业务需求的规范。对我们来说,解决方案设计应该包括数据、模型和软件如何相互作用以满足业务需求。它不仅仅是存储和计算基础架构,还应该包括应用程序如何随着环境的变化交付业务功能,包括软件的持续集成和交付、模型的持续监控和学习,以及为模型提供信息的数据管道。
传统的软件工程师和技术架构师关注构建 vs .购买 vs .租赁决策,以交付所需的业务功能。对于模型,我们还应该增加一个额外的选择组装。有两个原因可以解释为什么应该考虑装配选项。首先,模型通常会做出预测或建议,或者自动执行特定的任务。单一供应商的解决方案可能过于狭隘,无法满足企业的所有需求。因此,从同一个平台组装多个模型,或者多个供应商提供的模型,或者供应商和开源或专有模型的组合通常是必要的。第二,来自大型 ML 平台和供应商解决方案的模型在您的企业外部接受数据训练。通常,当这些模型在您自己的数据集上接受训练时,您可能会看到模型性能的提高。因此,重新训练模型并将它们组装在一起通常更好。
解决方案的设计和应该提出的关键问题取决于模式演变的阶段。对于独立模型阶段,重点更多的是从其他企业数据仓库或数据湖获取数据,提供给数据科学家,让他们展示模型性能。使用 ML 平台的开源替代方案或预打包模型,可能是确定初始可行性的快速方法。对于预测即服务阶段,我们需要担心安全性、模型需要重新训练和重新部署的频率,以及确保数据、模型和软件的可追溯性。对于一个模型工厂阶段,解决方案设计的重点要宽泛得多;解决方案设计者需要考虑具有不同专业知识和技能水平的多组人员如何构建、培训、测试、消费和监控模型。他们需要提供一个安全的“沙箱”以及一个生产基础设施,供多个团队在其中运营。围绕开源软件的使用选择正确的云 ML 平台或供应商 ML 解决方案和政策变得至关重要。
流行的 CRISP-DM 方法将业务和数据理解分为两个不同的步骤。解决方案设计阶段是我们增加的一个额外步骤。正如我们到目前为止所看到的,价值范围界定阶段是业务、IT 和数据科学团队之间的协作。一旦对业务需求和如何使用模型有了合理的说明,数据科学家和数据工程师就可以进入下一阶段。
价值发现
这个阶段已经在数据挖掘和数据科学文献中得到很好的研究。这是决定构建和部署模型是否有价值的阶段。在任何给定的情况下,数据科学团队都应该构建和评估许多模型。这些模型应作为“投资组合”进行管理,期望一部分模型能够证明企业设定的绩效标准,而其他一些模型将会失败。正如我们在之前的博客中所讨论的,如果不以实验的心态来看待这个投资组合,可能会扼杀整个 AI/ML 和数据科学的努力。
数据析取
这一步包括决定可以通知模型的内部和外部数据源,然后从这些数据源获取数据。这里要考虑的其他方面是数据的多样性(例如,结构化或非结构化数据)、数量、速度和准确性。在非结构化数据领域,我们需要考虑基于文本的数据以及它们将如何被接收(例如,来自社交媒体或静态文档的流数据)、音频数据、图像、视频和传感器数据。
与传统的数据挖掘不同,我们还需要考虑我们是否需要标记数据,以及我们如何才能促进更好的“过程中”标记。我们已经看到许多数据科学项目由于缺乏足够的标记数据而步履蹒跚。作为领域专家正在进行的过程的一部分,为数据注释创建管道可能是企业中最有价值的举措之一。虽然它可能不会立即产生价值,但它将为收集正确的标记数据奠定基础。
从不同来源提取数据的任务落在了数据工程师和/或数据科学家的肩上。数据提取越小、越本地化,越有利于数据科学家独立处理;由于数据提取涉及到企业数据仓库或事务系统,数据工程师处理这类请求会更好。随着企业沿着数据、自动化、分析和人工智能连续体开始这一旅程,他们的数据工程师应该能够处理结构化和非结构化数据。
预处理
这一步包括获取摄取的数据,对数据进行一些预处理,并为构建机器学习模型做好准备。预处理步骤取决于我们从哪里获取数据(即内部还是外部)、我们正在处理的数据类型(即文本、音频、图像等)以及我们接收数据的速度。
数据清理是预处理中的一个子步骤。所需的数据清理量取决于数据的质量。它也因数据类型而异。
例如,在文本文档中,需要经过一系列的预处理步骤,如去除标点、词干、词汇化等。,然后运行情感分析等 NLP 任务。在结构化程度更高的数据集中,清理可能采取移除 NAs(字段不可用)或输入缺失像元值的形式。
数据丰富是另一个预处理步骤,随着人们能够访问所有外部数据源和合成数据源,这一步骤越来越受欢迎。这个过程需要统计匹配两个数据集的记录,然后用附加变量丰富原始数据集。
例如,我们可以从银行获取客户数据,并围绕他们的在线行为或购买模式添加外部信息。这使得银行现在可以根据客户的渠道偏好和属性更好地开展营销活动。
探索性数据分析和特征工程也可以被认为是预处理步骤的一部分。它们提供有用的信息,说明在收集的数据中哪些数据是有用的,以及需要建立什么类型的模型。
随着这些步骤的进行,任务从数据工程师技能集转移到数据科学家技能集。拥有一个至少由两名成员组成的团队来从事数据科学项目,并且拥有数据工程和数据科学技能的混合,最好具有一定水平的领域知识,这通常是很有用的。
模型结构
这是整个发现过程中最具挑战性的阶段之一。CRISP-DM 和其他方法通常将模型构建和模型评估分为两个独立的阶段。然而,我们发现将这两者结合起来很有用,因为我们认为数据科学家应该不断地构建、测试或评估他们的模型。
可用于建立模型的技术取决于模型的目标和数据的 4 V(多样性、容量、速度、准确性)。该模型可以用于各种不同的目的,包括预测、推荐、总结等。模型评估的标准也可以很多——性能,公平性,可解释性,健壮性,安全性等等。此外,已经表明具有模型集合将产生更好的准确性,并且可能更稳健。考虑到所有这些因素,选择构建模型的技术以及利用数据集中的某些特征来构建这些模型的方式与其说是科学,不如说是艺术。此外,模型构建和评估的方式也可以参数化,通常称为超参数。鉴于这一步的广度和深度(相对于单个博客而言,它值得写一整本书),我们不会在这里探究所有的细节。
模型发现的这三个步骤是迭代的。根据我们的经验,我们已经看到数据科学家对这些步骤着迷,并努力用更好的技术、更好的数据和更好的数据工程来构建越来越好的模型。虽然这可能是一项有价值的学术努力,但它可能会被证明非常昂贵,并为企业数据科学团队敲响丧钟。我们在别处讨论过的基线模型和时间框建模冲刺是我们将在未来的博客中回头讨论的基本最佳实践。
价值传递
只有当组织认为模型发现阶段已经产生了一个满足业务所建立的模型性能标准的模型,或者确信当模型被部署并且被允许在部署后改进其性能时,它能够满足时,组织才应该进入这个阶段。随着企业在如何构建和使用模型方面的发展,这个阶段变得越来越重要。它还强调了对新技能的需求,如模型操作、ML 操作和 ML 工程师。
模型部署
在这一步中,数据科学家通常在沙盒环境中构建的模型将被部署到生产环境中。如果部署只是“部署一次就忘了”,这一步就没什么可做的了。然而,模型本身就消耗数据,而这些数据改变了久而久之。因此,最初使用历史数据构建的模型的性能开始下降。恶化的速度实际上取决于环境变化的速度和模型消耗的数据量。
例如,一个电信提供商构建了一个聊天机器人,作为处理客户查询的第一线支持;如果聊天机器人不能回答询问,聊天会话将被引导到人类代表。在经过广泛的培训和测试后部署该模型时,它具有出色的性能,可以处理 95%的查询。然而,它的性能每周都在下降,几个月后它只能处理 80%的查询。通过进一步调查,很明显,该模型需要至少每周或每月进行一次再培训,以说明新发布设备的品牌、模型和附件以及公司的最新营销活动。
模型部署涉及模块化模型代码(如软件代码)以分离出模型的参数,对参数、超参数、数据和用于训练和测试模型的开源包进行版本控制,并最终自动化模型的实验、训练、测试和回溯测试。根据再训练所需的数据,还必须考虑创建数据管道来为模型的再训练提供信息。
鉴于这一步涉及大量的软件工程和数据工程,您需要将数据科学和软件工程技能结合起来。这导致了 ML 工程师和 ML 操作作为新的角色出现,以处理正在进行的甚至连续的模型部署。
过渡和执行
当组织过渡到模型部署时,它需要确保有足够的治理来监控模型的性能及其适当的使用。大多数模型改变了流程中的工作流,或者增强了人类的决策。在这两种情况下,用户都必须重新接受培训或掌握如何使用模型的技能。人类需要对模型做出的预测或建议建立信心,同时也要警惕模型中的潜在错误。变更管理,尤其是何时信任模型以及何时使用人类判断的数据科学文化变得至关重要。
在这一步中,模型离开了数据科学家和软件工程师的“温柔和关爱”,并被具有不同数字或数据科学技能的不同业务利益相关者所使用。企业开始收获模型的价值,因此应该有收集和报告模型 ROI 的机制。
价值管理
这是这个过程的最后一个阶段,可能也是最长的一个阶段。该模型已经交付,企业正在使用该模型——可能嵌入到其他应用系统中。这一阶段需要确保正在产生的价值被捕获并持续报告给高级管理层,并且价值没有退化。
持续监控
我们已经在上面以及我们之前的博客中看到,模型的价值可能会下降。因此,我们需要持续监控模型的结果,了解与过去的任何偏差,并报告所产生的价值。在一些受监管的行业领域,如金融服务,对监控和向监管机构报告模型有严格的要求。因此,他们有广泛的过程、治理和结构来治理模型。然而,在许多不受监管的部门,很少对模型进行持续监控。当我们在未来的博客中讨论负责任的人工智能时,我们将回到这一步。
评估和登记
总有一天,模型的使用寿命会走到尽头。这可能是由于业务变化、技术变化或新数据的可用性。当我们监控和报告一个模型或模型组合的价值时,我们需要跟踪整个组合的价值。我们需要决定何时以及如何退休、重新培训或建立新的模式。这类似于你的财务顾问卖出某些表现不佳的股票,买入某些未来有增长潜力的股票,以确保你的投资组合价值得到保护或正在增长。
价值管理的这两个步骤都应该由企业来完成——尤其是财务组织,并得到业务发起人、数据科学家和软件组织的帮助。此外,这一步应该在模型生命周期开始时就计划好,而不应该在模型已经开始恶化时才想到。
有趣的是,CRISP-DM 方法止于模型部署,并不包括我们上面概述的一些额外步骤。我们的九步流程如下图所示。

九步模型生命周期(来源:普华永道分析)
迭代过程
虽然详细的九步流程是按顺序列出的,但是流程本身远不是模型开发和部署的“瀑布”方法。生命周期是迭代的,有三个特定的反馈循环。
第一个反馈回路处于价值发现阶段。流程中的三个步骤是迭代的,并且还可能导致对业务目标的重新检查。第二个反馈过程是在价值交付阶段。这个阶段可能导致返回到价值发现阶段,甚至可能触发价值范围确定阶段。最后的反馈来自价值管理流程。这可能导致回到价值交付、价值发现或价值范围界定流程。四个阶段中所有这些步骤之间的丰富的相互作用导致了模型、数据和软件的非常复杂的管理过程。
迭代模型生命周期过程和过程中涉及的不同角色在下面的图表中被捕获。

九步模型生命周期中的反馈循环(来源:普华永道分析)
尽管我们已经详细描述了模型生命周期过程及其迭代性质,但是我们还没有将它与敏捷软件开发过程进行比较。我们将在下一篇博客中讨论这个问题。
作者:作者: Anand S. Rao , Joseph Voyles 和 Shinan Zhang
模型提升——缺失的一环

安杰伊·西曼斯基
如何向非技术观众谈论机器学习的结果
数据建模成功的一半是利益相关者/受众对我们模型的理解。关键是要理解他们的期望,并在我们的演示中使用简单的语言,让非技术人员也能理解。通常,受众是希望在某种交叉销售或保留活动中使用该模型的营销和/或财务团队成员。他们通常更感兴趣的是模型能在多大程度上提高竞选绩效,而不是模型本身有多好。这是一个微妙的区别,我将在稍后解释。
作为数据科学家,我们倾向于关注模型的整体性能,并通过使用 ROC 曲线和混淆矩阵来说明模型在区分积极和消极方面有多好。然而,这并不总是最好的方法。
对模型在现实生活中如何部署的理解是关键点,它决定了应该如何评估模型。
在识别欺诈行为、建立信用记分卡或识别癌症患者时,ROC 是一种非常好的评估方法。在这些情况下,我们需要正确识别每个现有的或新的记录。
然而,在市场营销或政治环境中,大多数活动只针对我们基础中的一个狭窄群体——那些对信息最开放的群体(目标群体)。因此,当务之急是了解模型在该组中的表现。组外的表现无关紧要。所以用评估整个基数的 ROC,不一定是最好的选择。
其次,我们的观众可能不理解一个混淆矩阵或者 ROC。对他们来说,等于 0.6 或 0.75 的 AUC 是一个抽象的、无关紧要的数字。谈论特异性和敏感性是行不通的。我们必须开始用和我们的观众相同的频率广播,用他们自己的语言说话。需要调整的是我们,不是他们!请记住,你在那里不是要用你的知识给他们留下深刻印象,而是要传递一个关于你的模型的发现的明确信息。这条信息应该在几秒钟内就能被理解。为了实现这一点,我们需要比 ROC 更容易理解的东西,以及我们可以容易地对其进行估价的东西。这就是模特升降机的闪光点。

来源:ADOBE STOCK
天才的定义是化繁为简
阿尔伯特·爱因斯坦
模型提升告诉我们,通过使用我们的模型,我们可以在多大程度上改进活动。它告诉我们,在我们的选择中找到阳性(商品)的几率有多大,与随机找到它们相比如何?使用我们的模型和随机选择的成功率的比较是模型提升。
模型提升的好处在于,我们可以立即为我们的模型分配财务价值。如果我们的前十分位数中的模型提升相等,例如等于 3,我们可以说,使用我们模型的前十分位数,该活动可以产生比基于随机选择的活动多 3 倍的收入。模型提升基本上是投资回报(ROI),其中投资是我们建立的模型。
要获得模型提升,我们需要遵循以下步骤:
- 获取我们数据库中每个记录的模型概率得分。
- 使用概率分数对基础进行排序,并将其分成相等的组。十分位数似乎是最常见的选择,因此,在本文的其余部分,我将把这些组称为十分位数。
- 计算每十分位数中阳性反应的比例( 十分位数反应率 )

- 计算 平均响应率

- 计算 模型升力

为了说明这一点,让我们以一家葡萄牙银行的直接营销活动为例。数据来自 UCI 机器学习库并且在这里可用。在运行数据清理、特征减少和重采样之后,我最终得到了这个数据集。包括数据清理在内的完整代码可以在这里找到。我们的目标是找出有可能对报价做出积极回应的客户。我们的因变量是 y,其中 1=阳性反应(阳性),0 =无/阴性反应(阴性)。
数据集中的一个要素(呼叫持续时间)导致数据泄漏。通话时间可能是购买的结果,而不是购买的原因。第二,它是未知的,直到活动的数据被选择和调用。因此,它应该从选择用于建模的数据中排除。然而,由于我想专注于模型提升本身,我决定保留数据集中的所有变量。
首先,我们需要将数据分成训练样本和测试样本
df= pd.read_csv('banking_campaign.csv')X = df.iloc[:,1:]
y = df.iloc[:,0]X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0)
我选择了梯度推进建模
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.metrics import classification_reportGBM = GradientBoostingClassifier(learning_rate=0.01,
n_estimators=1000,
max_depth=6,
min_samples_split=50,
min_samples_leaf=25,
subsample=0.8,
max_features=10,
random_state=10)
GBM.fit(X_train,y_train)
predictors=list(X_train)
让我们简单看一下整体模型性能。
print('Accuracy of the GBM on test set: {:.3f}'.format(GBM.score(X_test, y_test)))
pred=GBM.predict(X_test)
print(classification_report(y_test, pred))
from sklearn.metrics import roc_auc_score
print (roc_auc_score(y_test, pred))

该模型的 ROC AUC 得分为 0.75 ,但我们忽略它。
获得模型提升的第一步是获得概率。为此,我们使用 predict_proba 。
y_pred2=GBM.predict_proba(X_test)
y_pred2 中的第一个值是概率 0(负数),第二个值是概率 1(正数)。

要计算模型提升,运行下面的代码。该代码生成一个 pandas 数据框,用于模型的最终评估和构建图形演示。
你需要做的是调用函数,指定你的测试因变量(y_test),它的预测概率(y_pred2)和你想把基数分成的组数(我决定用十分位数,所以 10)。
ModelLift=lift(y_test,y_pred2,10)
输出如下所示:

Scr_grp 代表我们的十分位数,使用概率分数进行分类和拆分。现在我们可以比较积极和消极在十分位数中的分布。你可以看到,随着我们越来越接近十分位数,阳性反应的数量越来越少。十分位数 1 中有 800 例阳性,而十分位数 10 中只有 1 例阳性!这意味着我们的模型正在做正确的工作。
现在我们可以计算响应率( resp_rate )。应答率是十分位数中阳性的比例。对于十分位数 1,它是 800/(800+436)=0.65。同样,你可以看到在十分位数组中,回复率迅速下降。
最后,我们需要计算升力。为此,我们需要测试组中阳性和阴性的总体比例,即:1376/12357 = 0.11。现在十分位数 1 的升程是 5.84 (0.65/0.11)。这意味着基于十分之一选择的活动比基于随机选择的活动成功 5.84 倍。
表格中的其他特征有:
cmltv_p_perc:累积阳性百分比
cmltv_n_perc:累积阴性百分比
cmltv_rand_p_perc:累积随机阳性百分比
cmltv_resp_rate:累积响应率
cmltv_lift:累积 lift
KS: Kolmogorov-Smirnov 测试
现在比较这两条消息:

和...相对

现在,你告诉我我们的观众会对哪条消息感到兴奋!
创建一个图表是传递信息的最好方式,但是观众应该能够在几秒钟内掌握这个想法。因此,我会避免任何更复杂的东西,比如增益图。
展示结果时,我们需要回答三个问题:
- 模型有效吗?
- 选择和模型性能有什么关系?
- 最优选择是什么?
让我们稍微调整一下输出。我将 dataframe 索引改成了更具信息性的内容,比如 Decile1 到 10,并且只选择了范围内的变量。
dec = ['Decile 1','Decile 2','Decile 3','Decile 4','Decile 5','Decile 6','Decile 7','Decile 8','Decile 9','Decile 10',]
MLift=ModelLift[['Positives','Negatives','cmltv_lift','KS']].copy()
MLift.index = (dec)
使用下面的代码,我们可以很快看到模型是如何工作的。
import cufflinks as cf
cf.go_offline()
cf.set_config_file(offline=False, world_readable=True)MLift[['Positives','Negatives']].iplot(kind='bar',yTitle='Volume',xTitle='Model decile', title='Positives & Negatives by model decile')
选择大小和性能之间的关系可以使用累积提升来确定。
MLift[['cmltv_lift']].iplot(kind='bar',color='LightSkyBlue',yTitle='Lift',xTitle='Model decile', title='Cumulative Lift', yrange=[1.11, 6])
随着目标群体的增加,我们可以期待更小的 ROI(模型提升)。例如,如果我们决定同时使用十分位数 1 和 2,ROI 将从 5.84(仅十分位数 1)下降到 2.87。尽管如此,这项活动将会比随机选择成功将近 3 倍!
使用 Kolmogorov-Smirnov 测试确定最佳选择,该测试用简单的语言评估累积阳性和累积阴性之间的差异。这个测试在我们的数据中找到一个点,在这个点上我们可以用最小的负值得到最大的正值。为了说明这一点,我曾经建立增益图,但我改变了主意,我现在认为条形图更容易阅读!
MLift[['KS']].iplot(kind='bar',color='DarkSlateGrey', yTitle='Separation',xTitle='Model decile', title='Target separation')
在我们的例子中,最好的分割是大约十分位数 2 和 3。在这一点上,我们已经得到了最大的积极因素和最小的消极因素。这是我们可以使用我们的模型实现收入最大化的点。
概括地说,模型升力优于 ROC 的 3 个主要优点是:
即使是一个普通的模型仍然可以提供一些价值,并对营销活动做出重要贡献。
模型提升可以直接转化为收入。
模型提升容易理解,比混淆矩阵更少混淆!
生产 ML 软件中的模型管理
MLflow 简介-

乌默尔·萨亚姆在 Unsplash 上拍摄的照片
为机器学习问题开发一个好的概念证明有时会很困难。您正在处理成吨成吨的数据工程层,测试许多不同的模型,直到您最终“破解了代码”,并在测试集上取得了好成绩。万岁!这是一个好消息,因为现在乐趣真正开始了,你的模型可能会帮助你的公司赚钱或省钱。如果是这样的话,你必须围绕你的模型来构建高效的软件。那是什么意思?您需要一个支持实时数据流的解决方案架构、一个可随数据流扩展的计算组件、一个真正的前端和一个良好的存储解决方案。而这些只是你的主要组件!您还需要一个监控解决方案,以防您的任何软件运行出错,还需要一个 DevOps 工具,负责测试您的新代码版本并将其发布到生产环境中。这只是你的日常数据用例,如果你试图构建人工智能软件,你可能会处理大量数据和非常密集的数据工程层,以将你的原始数据转化为可用于训练/推断机器学习模型的形式。最有可能的是,您将需要一个 ETL 管道工具来在每天的运行中编排您的数据工程。但最后一件事可能是最重要的一块拼图,你需要知道你的 ML 模型随着时间的推移在做什么!
- 今天的模型与昨天的模型相比有多好?
- 它接受了哪些功能的培训?
- 什么是最优超参数?它们会随着时间而改变吗?
- 训练和测试数据如何随时间变化?
- 哪个模型处于生产/集成状态?
- 我如何在这种设置中引入主要的模型更新?
所有这些问题都源于机器学习生命周期的复杂性。因为数据会随着时间的推移而变化,即使在生产性的 ML 环境中,我们也经常处于收集数据、探索模型、提炼模型、最后测试/评估、部署并最终监控我们的模型的循环中。

ML 生命周期
我们如何才能保持对这个不断进化的复杂循环的控制?
实际上有一个非常简单的解决方法——把一切都写下来!或者用计算机术语来说:
记录所有相关信息!
一个有趣的名字是机器学习模型管理,这是 MLOps 的重要组成部分。在软件执行过程中捕获相关信息并基于这些信息做出自动决策的持续过程。准备、训练和评估期间的指标,以及经过训练的模型、预处理管道和超参数。所有这些都被整齐地保存在一个模型数据库中,以备比较、分析和用于挑选一个特定的模型作为预测层。现在,理论上你可以使用任何普通的数据库作为后端,并编写自己的 API 来测量和存储所有这些信息。然而,在实践中,我们可以利用一些非常酷的预建框架来帮助完成这项任务。 Azure 机器学习、 Polyaxon 、 mldb.ai 只是几个例子。本文重点介绍 MLflow 。MLflow 由 D atabricks 的人员开发,并无缝集成到他们的生态系统中。由于我已经在 Databricks 上完成了所有的 ETL 和超参数调优(使用 hyperopt 框架),所以这是模型管理框架的一个简单选择。
MLflow 深潜
MLflow 是一个“机器学习生命周期的开源平台”。 Databricks 上的管理版本特别易于使用,您甚至可以通过点击下面的“新 MLflow 实验”使用用户界面创建一个空 MLflow 实验。

您不必担心设置后端数据库,一切都会自动处理。一旦实验可用,就可以在您的代码中使用 MLflow tracking API 来记录各种信息。为了快速浏览,让我们看一个例子:
Sklearn 的葡萄酒数据集
我看了一下 sklearn 的开源葡萄酒数据集。它包含来自不同意大利葡萄酒种植者的三种不同葡萄酒类型的化学分析数据。目标是创建一个分类器,使用给定的特征(如酒精、镁、颜色强度等)来预测葡萄酒的类别。
首先,您可以打开一个新的 Databricks 笔记本,并加载以下软件包(确保它们已安装):
import warnings
from sklearn import datasets
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, f1_score
import pandas as pd
import numpy as np
import mlflow
import mlflow.sklearnwarnings.filterwarnings(“ignore”)
现在,您可以将数据加载到熊猫数据框架中:
# Load wine dataset
wine = datasets.load_wine()
X = wine.data
y = wine.target#create pandas df:
Y = np.array([y]).transpose()
d = np.concatenate((X, Y), axis=1)
cols = ["Alcohol", "Malic acid", "Ash", "Alcalinity of ash", "Magnesium", "Total phenols", "Flavanoids", "Nonflavanoid phenols", "Proanthocyanins", "Color intensity", "Hue", "OD280/OD315 of diluted wines", "Proline", "class"]
data = pd.DataFrame(d, columns=cols)
设置日志记录
首先,我们需要决定在培训阶段我们想记录什么。原则上,MLflow 允许我们记录任何东西。是的,任何事!在最基本的形式中,我们可以将任何信息写入文件,并让 MLflow 将其作为工件记录。预建的日志记录功能包括:
- 运行 ID
- 日期
- 参数(例如:超参数)
- 韵律学
- 模型(训练模型)
- 工件(例如:经过训练的编码器、预处理管道等)
- 标签(例如:型号版本、型号名称、其他元信息)
在本例中,我们将坚持基本原则,记录 2 个指标、所有超参数以及训练模型,并给它一个模型版本标签。为了简单一点,我们围绕 scikit-learn 构建了一个小包装器。负责 MLflow 日志记录的 fit()方法。然后,我们可以调用包装器,传入不同的超参数,并查看性能如何变化,一切都记录到我们的 MLflow 后端。
让我们从评估函数开始,包括准确性和 F1 分数:
from sklearn.metrics import accuracy_score, f1_scoredef eval_metrics(actual, pred):
acc = accuracy_score(actual, pred)
f1 = f1_score(actual, pred, average = "weighted")
return acc, f1
然后围绕 scikit-learn 的 RandomForestClassifier 编写我们的包装器,给它两个超参数来调整,即森林中的树的数量,以及树中每个决策拆分使用的特征列的比例。
def train_winedata(data, in_n_estimators, in_max_features):
warnings.filterwarnings("ignore")
np.random.seed(40)# Split the data into training and test sets. (0.75, 0.25) split.
train, test = train_test_split(data, stratify= data["class"])# The predicted column is "class"
train_x = train.drop(["class"], axis=1)
test_x = test.drop(["class"], axis=1)
train_y = train[["class"]]
test_y = test[["class"]]
# start run
with mlflow.start_run():
rf = RandomForestClassifier(n_estimators = in_n_estimators,
max_features= in_max_features)
rf.fit(train_x, train_y)
acc, f1 = eval_metrics(test_y, rf.predict(test_x))
print(" Accuracy: %s" % acc)
print(" F1 Score: %s" % f1)
#log metrics
mlflow.log_metric("F1", f1)
mlflow.log_metric("Accuracy", acc)
#log params
mlflow.log_param("n_estimator", in_n_estimators)
mlflow.log_param("max_features", in_max_features)
# add tags
mlflow.set_tag("model_name", "Wineforest")
# save the trained model
mlflow.sklearn.log_model(rf, "model")
训练模型
在运行培训之前,我们需要告诉我们的 MLflow 客户端在哪里记录所有这些信息。我们将让它指向我们之前通过 Databricks UI 创建的空 MLflow 实验:
mlflow.set_experiment("/[Users/](mailto:Users/maxibe@emea.corpdir.net)<your user>/WineClassifier")
现在我们可以运行第一组超参数:
train_winedata(data, in_n_estimators= 1, in_max_features= 0.1)Accuracy: 0.733 F1 Score: 0.731
我们可以看到,单个树每次分裂仅使用 10%的特征就获得了相当不错的准确度和 F1 分数。
现在以 max_features 的 50%重新运行训练:
train_winedata(data, in_n_estimators= 1, in_max_features= 0.5)Accuracy: 0.933 F1 Score: 0.931
已经给了很高的分数。但是,在随机森林中包含 100 棵树可以进一步提高性能:
train_winedata(data, in_n_estimators= 100, in_max_features= 0.5)Accuracy: 0.98 F1 Score: 0.98
让我们快速浏览一下 MLflow 实验的结果:

MLflow 用户界面
我们可以看到一个漂亮的 GUI,其中包含了我们所有的登录信息。我们还可以通过点击日期深入了解单次跑步。在那里,我们发现日志模型作为一个工件,准备好被加载并用于进行预测。现在,我们可以设置我们的服务层来获取最佳模型(例如,根据 F1 分数),并自动将我们的生产环境部署到该层。当进行广泛的网格搜索时,可以探索不同超参数日志的影响,以潜在地调整超参数网格中的搜索空间。
生产级机器学习的架构设置
当构建高效的软件时,我们必须时刻牢记,一个单一的环境很可能不会成功。如果我们有一个生产性的设置在运行,并且我们想要对代码进行更改,我们至少需要一个集成环境,在那里我们可以首先测试任何更改。
随着训练数据随着时间的推移而增加和变化,我们还需要定义重新训练我们的模型、重做超参数调整甚至重新评估机器学习模型整体类型的周期。这需要以自动化的方式发生,并且已经在集成环境中成功测试的最佳模型(在固定的外部数据集上测试)需要一直部署在生产中。如果我们决定改变外部测试数据集或者代码发生重大变化,我们可能想要合并一个新的模型版本,并且只比较比该版本旧的部署模型,等等。有点复杂的过程。
MLflow 中的模型注册中心提供了一个工具,可以帮助完成这个过程。在这里,我们可以用不同的名称、版本和阶段注册来自 MLflow 后端的已训练模型。从“无”、过渡到“准备”到“生产”的阶段。

模型“winetest”的模型注册表项
记录我们之前的葡萄酒分类器,我们只需要从 MLflow 获取 model_uri 并运行:
mlflow.register_model(model_uri=model_uri, name="winetest")
让我们更仔细地看一下如何使用这个设置管理两个基本的 ML 生命周期场景:定期重新训练和调整已经部署的 PROD 模型,以及将新的或更新的模型代码部署到生产系统中。主要区别:后者需要在 INT 上经历一个测试阶段,因为代码已经改变,而前者没有。代码保持不变,只是模型的性能可能发生了变化。

使用 MLflow 的模型管理设置
想想下面的场景:目前 1.0 版中一个名为 AImodel 的模型注册为“Production”,并部署在 PROD 上。我们每 x 天对模型进行一次再培训和重新调整。一旦进行了重新训练和重新调整,我们就可以从注册表中获取模型,运行我们的训练和调整管道,将 AImodel 版的新模型性能记录到 MLflow 中。现在,我们可以通过比较新训练的 AImodel 1.0 的性能和 AImodel 1.0 在外部数据集上的所有现有性能来启动自动选择过程,并通过用我们选择的模型覆盖“生产”阶段位于模型注册表中的当前 AImodel 1.0 来部署最佳模型(由于某种度量)。现在,我们仍然在产品中使用 AImodel 1.0,只不过(希望)有更好的性能,因为它是在更新(更多)的数据上训练出来的。
这非常简单,允许在生产中不断部署最好的 AImodel 1.0。如果我们想改变 AImodel 1.0 的代码,甚至引入 MLmodel 1.0 来与 AImodel 1.0 竞争生产位置,该怎么办?我们需要利用我们的集成环境!
将代码部署到环境中是由 DevOps 管道管理的,它分别由 int 或 master Git 分支上的拉请求触发。这就给了我们 INT 或 PROD 版本的代码到各自的环境中。让我们想象一下,我们在 AImodel 1.0 的代码中包含了分布式培训。我们通过 DevOps 将 INT Git 分支上的内容部署到我们的 INT 环境中,并运行新配置的培训,再次登录 MLflow(给模型一个 INT 标记),并将模型注册为 AImodel 2.0 以及“Staging”。现在的关键概念是:我们的两个环境会根据一些标准(如上面使用性能指标所解释的)自动选择用作服务层的模型。由于我们只希望 AImodel 2.0 在 INT 上使用(因此被测试),而不是在 PROD 上,我们引入了每个环境的模型版本容差。这意味着仅允许 PROD 从≤1.0 版的型号中选择,而允许 INT 选择≤ 2.0 版(或如果我们特别想测试 2.0 版:=2.0)。在完成 INT 的测试阶段后,我们可以将模型转换到注册表中的“生产”,并将 PROD 中的模型版本容差提高到≤ 2.0(或=2.0 或 1.0≤PROD 版本≤2.0 等)。现在,新代码(新模型)已经为 PROD 环境做好了准备。因此,基本上,在 INT 的每个新的训练循环中,我们都在比较并潜在地用一个模型子集替换“Staging”中的当前模型,这是根据 MLflow 中 INT 的版本容差(例如,模型标签模型名称等)进行的。根据产品上的版本余量,使用“生产”中的模型,在产品上也会发生同样的事情。如果我们对代码进行了重大更新(比如想要使用一个完全不同的模型),我们总是可以用一个新的名字注册这个模型,并从头开始 1.0 版。
这种设置允许自动部署性能最佳的模型,同时仍然可以将新的代码和模型顺利地引入系统。然而,这仍然是一些基本的设置,因为在大多数情况下,调优和训练将异步完成,因为调优工作是非常计算密集型的,因此可能仅在例如每 10 个训练循环中发生一次。现在我们可以考虑使用不同的 MLflow 后端,一个用于调优,一个用于训练,以保持事情清晰和可管理。
总的来说,MLflow 中可用的工具允许非常灵活的模型管理设置,可以直接根据用例的需求进行定制,并满足生产级需求。我们还可以想象在我们的 MLflow 后端之上构建一个仪表板,允许高级用户跟踪性能、版本状态以及参数和功能选择,而无需打开数据块。
尝试一下,让你的机器学习应用更上一层楼:)
参考文献:
MLflow
机器学习生命周期的开源平台 MLflow 是一个开源平台,用于管理机器学习生命周期
mlflow.org](https://mlflow.org/)
MLflow 车型注册
[## 在 MLflow 模型注册表中管理 MLflow 模型的生命周期
MLflow Model Registry 是一个集中式模型存储和一个 UI 以及一组 API,使您能够管理整个生命周期…
docs.databricks.com](https://docs.databricks.com/applications/mlflow/model-registry.html?_ga=2.191901866.1832504560.1588601145-536696397.1564753281#model-registry-concepts)
模特?还是指证据权重(WoE)和信息价值(IV)?
使用巨大的数据集来一步一步地解释和实现这两个概念。这是一个在 Julia 中编码的好机会!
作为一名数据科学家,我一直很想知道某些自变量(如职业)如何影响因变量(如收入)。具体来说,在分类问题上,WoE 和 IV 可以讲一个自变量和因变量之间的故事。
这些概念主要是为了解决信用评分问题而开发的,在这种情况下,客户被贴上“好”或“坏”的标签,这是基于他们是否拖欠信用还款。他们的相关变量,如年龄,也被记录下来。
现在,我们正在将这些概念应用于巨大的数据集。
using DataFrames, CSV
df = DataFrame(CSV.File("train.csv"))
show(describe(df), allcols=true)

泰坦尼克号列车组的描述
证据权重
WoE 的公式是这样的:
对于自变量 x 的每一个类 i ,我们要求其因变量 y 属于某个类的人口的比例/百分比,该类有类 i ,后面是自然对数。

听起来很困惑?放在上下文中,我们可以把 x 作为“性”,把 y 作为“幸存”。“性”分为 2 类:“男”&“女”。要算 WoE,
1a。数一数灭亡的男性("性别" = "男性","幸存" = 0): 468
1b。将该数字除以遇难乘客总数("幸存" = 0):
468/549 = 0.85246
2a。数一数存活的雄性数量("性别" = "雄性","存活" = 1): 109
2b。将该数除以幸存乘客总数("幸存" = 0):
109/342 = 0.31871
3。将(1b)的结果除以(2b)的结果,然后取自然对数得到 WoE:ln(0.85246/0.31871)=0.98383
我们对“女性”执行相同的步骤来导出 WoE,即 -1.52988 。代码如下:
survived = by(df, :Survived, (count = :Survived => length))
sex_df = unstack(by(df, [:Sex, :Survived], (count = :Sex => length)), :Sex, :count)male_event = sex_df[(sex_df.Survived .== 1), :male] / survived[(survived.Survived .== 1), :count]
male_non_event = sex_df[(sex_df.Survived .== 0), :male] / survived[(survived.Survived .== 0), :count]
woe_male = log(male_non_event/male_event)female_event = sex_df[(sex_df.Survived .== 1), :female] / survived[(survived.Survived .== 1), :count]
female_non_event = sex_df[(sex_df.Survived .== 0), :female] / survived[(survived.Survived .== 0), :count]
woe_female = log(female_non_event/female_event)
回到公式,WoE,通过符号(+/-),揭示了在灾难性事件中男性幸存者与男性受害者的比例。然而,我们更感兴趣的是知道自变量(“性别”)和因变量(“存活”)之间的关系。
信息价值(四)
这将能够把不同的困境结合在一起,因为在同一个独立变量下。公式是这样的:

继续我们之前的示例,以下是要采取的步骤:
4a。取(1b)结果与(2b)结果之差:
0.85246-0.31871 = 0.53375
4b。将(4a)的结果与(3)的结果相乘,即为结果权重:
0.53375 * 0.98383 =0.52512
我们对“女性”执行相同的步骤,得到 0.81656 。将两个值相加,我们得到自变量“性别”的 IV = 1.34168 。代码如下:
iv_male = (male_non_event - male_event) * woe_male
iv_female = (female_non_event - female_event) * woe_female
iv = iv_male + iv_female
一旦我们有了变量的 IV,我们就可以对照这个表来查看变量的预测能力。正如你所看到的,这个表格在说“性别”这个变量好得令人难以置信!

变量预测能力表
我们在这里做了一些假设:首先,当创建解决信用违约问题的方法时,该表作为指南。在此期间,研究人员希望了解可能影响客户信用评级的变量。研究人员发现,这个表格与他们的问题陈述非常相关。然后它被用作指南。
第二,已经证明泰坦尼克号沉没后幸存的女性多于男性。事故中女性的生还几率高于男性。因此,“性别”这一变量确实是一个异常强大的预测力量也就不足为奇了。
好吧,接下来呢?
除了将概念应用于研究目的之外,WoE 的主要实际用途是用于编码,在编码中,您可以用相关的值替换类。例如,在我们的示例中,您可以用 0.98383 替换“男性”,用-1.52988 替换“女性”。原因很明显:机器学习算法主要以数字作为输入,所以我们必须在训练模型之前将字符串转换为数字。
使用 WoE 的另一个积极结果是减少了用于训练模型的输入的列数。假设您有一个包含 10 个不同类的分类变量,并且您执行了一次热编码,您将得到 10 列,其中大部分值为“0”。使用 WoE 技术,类被它们相关的 WoE 值所代替。
至于变量,它们为我们进一步深入分析自变量和因变量之间的关系提供了基础。此外,如果变量是定性类型,我们可以使用宁滨方法,然后使用 WoE 和 IV 概念来设计有意义的特征。
现在的解释就到此为止。如果有任何方法可以提高我的 Julia 编码技能,请给我留言,因为我最近才学会这门语言。干杯!
在一行代码中模拟并行性
利用多个 GPU 对大型图像进行训练

保罗·德拉古纳斯在 Unsplash 上的照片
TL;DR;
当由于内存限制而无法在单个 GPU 上运行训练时,应该使用模型并行。这种技术将模型分割到多个 GPU 上,并在不同的加速器上执行每个部分。通过这种方式,巨大的模型可以被拆分,以适应内存。
这种技术在过去实现起来相当复杂。模型的公平分割、对 GPU 的正确分配和高效执行是不容易实现的功能。
艾森 0.0.5 在一行代码中实现了模型并行,因此对于任何使用不止一个 GPU 并苦于内存问题的人来说,模型并行都是显而易见的。
更新!
主分支中的当前 Eisen 代码库(以及 eisen0.1.6 和 eisen-core0.0.5 之后的未来版本)已经更新,以进一步简化模型并行的实现方式。现在可以在没有 Eisen 工作流的情况下使用模型并行性(不要担心,您仍然可以像使用任何其他模型一样在工作流中使用它),其方式几乎与 PyTorch 中的数据并行性相同!
现在它看起来真的像一行程序!
model = eisen.utils.ModelParallel(model, split_size=2)
以前处理过体积医学数据(CT、MRI 等)。)我的研究一直受到单个 GPU 可用内存量的限制。回到 V-Net 时代,我们必须在 GTX 1080 显卡上的 8GB 显存上运行我们的 3D 卷积模型。后来,由于我可以使用高达 32GB 视频内存的更大的 GPU,我们可以提高数据分辨率并改善我们的结果。
一旦我在一台机器上有了多个 GPU,我就可以限制我的数据的分辨率,以便单个图像能够适合一个 GPU,并且我将求助于数据并行性来运行更大批量的训练。数据并行会将计算分布在多个 GPU 上,但整个模型仍需要放在一个 GPU 中。
我仍然相信,当您至少可以在单个 GPU 上运行每批单个图像的训练时,数据并行是首选。但是,如果您需要在非常高分辨率的数据上训练一个复杂的模型,在这种情况下,只有少数几个层而不是整个模型可以放在单个 GPU 上,该怎么办?在这种情况下,您需要模型并行性!
在 Tensorflow (1.x)中,启动数据并行或模型并行纯粹是一场噩梦。
在 PyTorch 中,我看到人们在一行代码中很好地实现了梯度检查点(另一种以牺牲性能为代价处理大型模型的技术),以及数据 并行性。这行代码:
model = torch.nn.DataParallel(model)
但是对于模型并行性来说,没有只包含一行代码的解决方案。PyTorch 团队有一本优秀指南,解释了如何在 PyTorch 中获得模型并行性,以及如何通过流水线执行获得出色的性能,但仅此而已。你必须重新实现。
正如我在之前的帖子中解释的那样,任何人都不应该浪费时间通过重新实现已经理解的东西来制造 bug 和问题。这就是为什么 Eisen 0.0.5 以遵循 PyTorch 建议的方式,在一行代码中为任何模型、任何应用和任何架构*实现了模型并行性。
(只要架构有多层)*
【Eisen 的模型并行在一行代码中完成。这行代码:
model = EisenAutoModelParallelModuleWrapper(
module=YourModule, # Python module
number_gpus=8, # Desired parallelism
split_size=2, # How to split batch for pipelining
input_names=['inputs'], # input names for Eisen workflow
output_names=['labels'], # output names for Eisen workflow
)# please note that this has been changed as of
# eisen > 1.6.0 and eisen-core > 0.0.5
这行代码的作用是:
- 将模型实例化为“YourModule”的实例
- 当 forward 方法被调用时(第一次),它对模型本身占用的 GPU 内存进行惰性评估
- 跨 GPU 分割图层,并将其分配给设备
- 根据需要实现在 GPU 之间移动张量的自动方式
- 自动为模型定义流水线执行策略,以便尽可能多地使用所有的 GPU
作为这种策略的结果,我们获得了(1)模型在指定 GPU 上的平衡分割,以及(2)与单个 GPU 模型相比更快的执行。实现这个特性的源代码可以在这里找到。
我整理了一个 python 笔记本,在模型并行性和单 GPU 训练之间做了一些比较。你可以在这里找到笔记本。这是来源于官方艾森教程之一。
我决定在配备 32GB Tesla V100 的 8 GPU AWS 实例 p3dn.24xlarge 上运行以下实验:
- 对于固定模型、固定数据集、固定批量:
-使用分割大小为 2 的 1、2、4 和 8 个 GPU 运行训练
-使用分割大小为 4 的 1、2、4 和 8 个 GPU 运行训练 - 对于固定的模型、固定的数据集,尝试将整个数据集放入一个批处理 :
-使用 8 个 GPU 和模型并行性运行训练
第一个实验是使用 MSD 任务 04 数据集运行的,批量大小为 32,分辨率为 64x64x64 体素,以 1 毫米立方重采样数据。你可以在笔记本里找到更多。批量大小为 32 时,1 个 GPU 上的内存使用量约为 18GB。

用一个 GPU 一个有感觉这个美好的 p3dn.24xlarge 浪费的差不多了。
我想澄清的是,在这个特定的场景中,由于我们能够在一个 GPU 上一批安装 32 个图像,因此应该使用数据并行而不是模型并行。与数据并行相比,模型并行有一些缺点。其中一些问题与内存传输开销和高效的流水线执行有关。
在这个玩具示例中,我有目的地在错误类型的工作负载上运行模型并行。事实上,只有在最大的 GPU 无法容纳批量 1 时,才应该使用模型并行。在这里,我们可以在单个 GPU 上安装 32 个图像,如果我们想一次处理 260 个(见下文)图像,我们可以求助于数据并行(这要简单得多),而不去管整个模型并行的事情。
但是为了公平比较,我们希望能够展示在一个 GPU 上运行所有东西和跨 2、4 和 8 个 GPU 运行所有东西之间的性能差异。
当我通过 Eisen 打开模型并行时,我在所有 GPU 上获得了更低的内存消耗。真的管用!!

模型并行可以利用系统中的所有 GPU,由于流水线执行,所有 GPU 都可以并行运行。现在我们的 p3dn.24xlarge 实例运行正常。
在获得了看到所有 GPU 并行运行的快感并享受了 8 Tesla V100 和 32GB 内存同时运行的感觉后,我们准备看看一些性能。这些是在 260 个图像的数据集上获得的,批次大小为 32,50 个时期(450 个批次),分割大小为 2:

第一个实验:在固定数据集上运行,固定批量大小,分割大小为 2(用于流水线)。

分割大小为 2 的单次训练的总时间(用于流水线)。
当我将分割大小增加到 4 时,为了获得更有效的流水线操作并最大化 GPU 的使用开销,情况得到了改善:

当使用更大的分割大小来“填满”GPU 的计算资源时,所有场景的情况都会得到改善。

单次训练的时间也相应增加。
此时,我想看看由于模型并行性,我还能在内存中容纳多少数据。
批量大小为 32 时,我在单个 GPU 上获得了 18GB 的内存使用量。现在,我想一次运行数据集中的所有 260 幅图像。这不一定是我们在真实用例中想要做的事情,无论如何,如上所述,在这种情况下运行数据并行比运行模型并行要好得多。但我想我想看看 GPU 和艾森如何处理批量大小为 260、分割大小为 20 和 50 的时期。下面是对比。

当将整个数据集作为一批处理并使用分割大小 20 时,运行训练的时间进一步减少。显然,通信开销和流水线仍然是一个问题。
总之,当由于图像分辨率、维度和模型架构而无法在 GPU 中容纳哪怕一个训练示例时,应该使用模型并行。然后,模型并行将在不同的 GPU 之间分割模型梯度和激活,以便进行训练。
艾森似乎能够自动处理模型并行性,因此提供了类似于拥有一个巨大的 GPU(本例中为 256 GB)进行训练的体验。
如何让您的数据科学项目经得起未来考验
入门
ML 模型选择和部署的 5 个关键要素

87%的数据科学项目从未投入生产 。这个统计数字令人震惊。然而,如果你像大多数数据科学家一样,这可能不会让你感到惊讶。非技术利益相关者努力定义业务需求。跨职能团队面临着为可复制的数据交付建立健壮管道的艰苦战斗。部署很难。机器学习模型可以拥有自己的生命。
这里列出了五个实用步骤,让您的模型能够应对模型选择和部署的挑战。如果你在过去忽略了这些关键因素,你可能会发现你的部署率飙升。您的数据产品可能依赖于正确部署本文中的技巧。
1.0 不要低估可解释性
一个可解释的模型是一个内在可解释的模型。例如,基于决策树的方法——随机森林、Adaboost、梯度树提升——提供了其底层决策逻辑的清晰视图。

在受到严格监管的刑事司法和金融领域,可解释性可能是强制性的。它也往往是一个强大的数据科学项目中被低估的元素。
除了固有的可解释性,决策树模型还有以下有用的特性:
- 容易以视觉形式描述
- 能够检测任何非线性双变量关系
- 跨多种使用案例的良好预测能力
- 提供分级的功能重要性
- 对特征预处理的要求低
- 使用
sklearn.OneHotEncoder处理分类特征 - 很好地处理异常值,不容易过度拟合
- 可用于分类或回归
由于这些原因,决策树是研究许多典型业务问题的一个可靠的初始模型。
在做出决策时,利益相关者更可能相信一个无法解释的黑盒神经网络还是一个随机森林?想想看,一个非常注重细节(或者非常无聊)的业务人员可以清楚地追踪每一个底层决策树中的逻辑,如果他们愿意的话。如果首席数据官的工作是让首席执行官免受牢狱之灾,那么这种水平的可解释性显然是一种胜利。
无法解释的模型冒着延续社会不平等的风险,比如人类和基于人工智能的抵押贷款系统对黑人家庭的系统性“划红线”——除非采取具体措施减轻对弱势群体的偏见。奥古斯特·黎塞留在像素上拍摄的照片。
除了决策树之外,可解释模型家族还包括朴素贝叶斯分类器、线性和逻辑回归以及 K 近邻(用于聚类和回归)。这些本质上可解释的模型具有额外的好处,即相对于黑盒神经网络,它们在训练和服务方面节省了大量的时间和资源,而预测性能的成本可以忽略不计。
1.1 如何选择合适的型号
无论是否以可解释性为目标,都要使用这种资源(决策树无处不在!)指导您选择型号:
via sklearn
1.2 阅读更多关于型号选择的信息
选择正确算法的系统方法。
medium.com](https://medium.com/atlas-research/model-selection-d190fb8bbdda)
2.0 生产化修剪
当然,有时候使用神经网络可能是你最好的选择。也许你正在做图像识别或自然语言处理(NLP)。也许你正在处理一个非常复杂的数据集。如果你使用神经网络,你应该考虑如何在投入生产之前削减模型。

神经网络中的大多数权重实际上是无用的。
训练后,60–90%的重量可以减轻,而不会影响表现。结果是更快的推理时间、更小的模型规模和更低的服务用户的成本。事实上,Neural Magic 团队认为,这种稀疏化可以使基于 CPU 的架构和“无硬件”人工智能复兴。
[## ICML 论文:归纳和利用激活稀疏性进行快速神经网络推理
2020 年 7 月,在机器学习国际会议上,我们提交了一篇关于最大化…
neuralmagic.com](https://neuralmagic.com/resources/icml-activation-sparsity/)
修剪包括移除神经网络中未使用的路径,保留必要的路径。 渐进量级修剪 (GMP) 已经成为一种最受欢迎的技术。一般来说,非结构化修剪——即去除特定权重而不是整个神经元——允许对稀疏化过程进行更大的控制,从而获得更好的性能。
2.1 如何在生产前修剪模型
- 以比培训中使用的最终学习率略高的学习率重新培训网络
- 在时段 1 的开始,将所有要修剪的层的所有稀疏度设置为 5%
- 每个时期迭代地移除最接近零的权重一次,直到达到指定的稀疏度
- 在降低学习率的同时,在剩余的再训练中保持稀疏度不变
2.2 阅读更多关于彩票假设的信息
让您在数据科学领域保持领先的基本理念。
towardsdatascience.com](/must-read-data-science-papers-487cce9a2020)
3.0 防止数据和模型漂移
部署后,漂移的力量将不可避免地冲击您的模型,并导致其性能随着时间的推移而下降。当模型的基础输入随着一个或多个数据特征改变时,数据漂移就会发生。当环境条件改变时,模型漂移发生,模型不再可靠地代表真实世界。

3.0a 数据漂移
数据漂移通常是数据收集过程中发生变化的结果。例如,一家制造厂的传感器可能会发生故障,在问题得到解决之前会记录几个小时的零度温度。然后,新的五月传感器可以记录摄氏温度,而不是以前的华氏温度。如果没有这些变化的背景,零值和切换到新的测量标准将对下游模型产生不利影响。
定性信息的变化也是如此。调查数据收集方法——例如,从邮寄问卷转变为固定电话调查——将对受访者的人口统计产生影响。即使问题措辞方式的微小变化也会对模型从数据集中得出纵向推论的能力产生不利影响。
数据漂移也可能是由数据集中字段定义的更改引起的。例如,制造工厂的数据所有者可以决定术语“废料”不仅指无用的材料,还指最终将被再加工成回收产品的材料。术语的这种变化也会影响模型的性能。
3.0b 模型漂移
真实世界环境的变化可能会降低模型的预测能力。
鉴于 2020 年是一个灾难性的一年,消费者行为模型通常需要被踢到路边。 Carl gold 是 Zuora 的首席数据科学家,Zuora 是一家为订阅业务提供服务的公司,通过先进的数据产品帮助他们超越分析。在最近的一次采访中,他分享了他对疫情影响的看法:
我告诉每个人更新他们的模型。现在,如果你做一个新的客户流失模型,你应该尽可能只使用 COVID 以来的数据。
只有拥有大量观察数据的消费公司才有可能做到这一点。一般来说,B2B 公司面临一个小的数据挑战。所以改装你的模型有这么多相互竞争的问题。
一旦部署,工作就不会停止。
你应该持续监控你的模型预测的准确性,因为这实际上会给你一个警告信号,如果你已经很久没有重新训练的话。
3.1 如何使您的模型对漂移具有鲁棒性
- 与数据源所有者签订数据共享协议,以接收数据漂移的提前警告
- 对照原始训练数据监控传入数据的分布——您可以使用 Kolmogorov-Smirnov (K-S)测试 或简单地比较 z 值 来实现这一点
- 监控时间序列数据集相对于前一时间段的漂移-您可能需要部署 群体稳定性指数(PSI) 指标来实现这一目的
- 定期重新培训您的模型——例如每五个月一次——或者通过在线学习,其中模型会不断获取新的培训数据,并在持续集成/持续部署过程中发布新版本。
3.2 阅读更多关于模型再培训的信息
挖掘数据流是机器学习中最重要的当代课题之一。这种情况需要适应性…
www.researchgate.net](https://www.researchgate.net/publication/321627304_Online_Ensemble_Learning_with_Abstaining_Classifiers_for_Drifting_and_Noisy_Data_Streams) 
via LinkedIn
4.0 利用正反馈循环
算法是支持数据驱动行动的强大工具。通过对成对的预测数据和实际数据进行再训练,模型的结果随着时间的推移变得越来越复杂。
数据产品 的输出在集成回数据生命周期时提供高质量的信号。把这个概念称为良性循环的艾 。哈佛商业评论称其为 洞察引擎 。

诺埃米·马卡韦-卡特茨在 Unsplash 上拍摄的照片
对数据驱动的决策及其结果的有力捕捉可以进一步丰富数据收集过程。希望很快,更多的反馈收集机会将被内置到仪表板、网络界面和其他数据产品中。反馈收集可以增强终端用户的能力,并改进洞察引擎。
4.1 如何利用正循环
- 在规划过程的开始与利益相关者交流有效的机器学习模型的巨大好处
- 从部署的模型中创建数据收集管道
- 确保元数据的准确性
4.2 阅读更多关于有效数据产品的内容
我们正在经历一场信息革命。像任何经济革命一样,它对…产生了变革性的影响
districtdatalabs.silvrback.com](https://districtdatalabs.silvrback.com/the-age-of-the-data-product)
5.0 防止负反馈循环
提醒一句:数据产品远不是一个自我维持的系统,它需要持续的监控。虽然算法反馈循环可以创建一个洞察丰富的数据集,但它也可以生成一个偏差永久化循环。有许多例子表明,机器学习工具的部署,特别是那些可解释性和可解释性有限的工具,意外地加深了社会偏见。

例如,一家数据科学承包公司创建了一个预测累犯的算法,该算法被部署在纽约、威斯康星、加利福尼亚、佛罗里达和其他司法管辖区。 ProPublica 发现该算法将现有的不平等延续到一个广为使用的反馈循环中。
尽管被告的种族被明确排除在特征集之外,但该算法使用了与种族高度相关的特征,这些特征提供了无意中有偏见的判断。这些特征也应该被消除,以减少机器学习系统的判断差异。 阅读本文 中关于这些风险的更多内容。
作为负反馈循环停滞的轻松解决方案,一位计算机科学家发明了一种随机性发生器来改变他的社交生活:
从 2015 年开始,我让电脑决定我住在哪里,做了两年多的事情。它把我送到了世界各地…
maxhawkins.me](https://maxhawkins.me/work/randomized_living)
5.1 如何避免螺旋式下降
- 从一份 清单 开始,帮助你思考你的模型的伦理含义
- 在模型训练的预处理、处理和后处理阶段,彻底地 调查偏差的潜在来源——然后补救偏差的来源
- 在文档中的保护类之间交流模型性能
5.2 阅读更多关于反偏见工具的信息
在将人工智能集成到您组织的工作流程中之前,请考虑这些工具来防止机器…
medium.com](https://medium.com/atlas-research/ethical-ai-tools-b9d276a49fea)
摘要
没有计划就是计划失败。本杰明·富兰克林在雷暴中从卧室窗户放风筝被闪电击中之前如是说。
我喜欢认为,如果他今天还活着,100 美元钞票的创始人应该已经在构建一个基于 GPU 的深度学习盒子,定期向开源项目提交 pr,并像老板一样选择和部署模型。
通过以稳健的规划流程开始下一个数据科学项目,你可以确保你的模型比 1:9 的几率投入生产。使用这些技巧来更好地选择和部署模型:
如果你想让被错误引用到数据科学文章中,只有在雷雨天气时在你的卧室窗外放风筝。
如果你喜欢这篇文章,请在 Medium 、 LinkedIn 、 YouTube 和 Twitter 上关注我,了解更多提高你的数据科学技能的想法。
你如何让你的机器学习模型适应未来?请在评论中告诉我。
培养数据科学技能的资源
数据策略最有价值培训实用指南。
towardsdatascience.com](/best-data-science-certification-4f221ac3dbe3) [## 如何在 2020 年打入数据科学
今年,我完成了一个训练营,并立即获得了一份数据科学的工作。如果我不得不回到过去,通过…
towardsdatascience.com](/new-data-science-f4eeee38d8f6) [## 作为一名数据科学家,我在 6 个月里学到了什么
我从新兵训练营开始,然后找到了我梦寐以求的工作。以下是一些关键要点。
towardsdatascience.com](/6-months-data-science-e875e69aab0a) [## 10 项被低估的 Python 技能
使用这些技巧来改进您的 Python 编码,以获得更好的 EDA、目标分析和功能,从而提升您的数据科学水平
towardsdatascience.com](/10-underrated-python-skills-dfdff5741fdf) [## 数据仓库综合指南
了解数据仓库作为分析就绪数据集的主存储库的角色。
towardsdatascience.com](/data-warehouse-68ec63eecf78)
模型选择和评估
超越火车价值测试分裂

图片来源:https://pix abay . com/illustrations/cubes-choice-one-yellow-light-2492010/
标准的建模工作流会将您的数据划分为训练集、验证集和测试集。然后,您将使您的模型适合训练数据,然后使用验证集来执行模型选择,最后,评估测试数据上的最佳选择模型,以查看它可以预期的泛化性能(模型评估)。这个流程大概是您的最佳选择,以确保您选择了正确的模型,并且一旦您将它部署到生产中,您就不会感到吃惊。
也就是说,人们不能总是奢侈地将数据放在一边以形成验证和测试集。如果你的数据很少,你宁愿把它全部用于训练。在本文中,我们将讨论选择和评估模型的方法,这些方法可以让您做到这一点——不需要验证也不需要测试集!
本文基于 Hastie,t .,Tibshirani,r .,& Friedman,J. H. (2009)的一章。统计学习的要素:数据挖掘、推理和预测。第二版。纽约:斯普林格。

关于模型误差
在比较竞争模型时,您可能会选择在新的、看不见的数据上表现最好的模型。这就是您通常使用验证集的目的:模型拟合时,验证数据是不可见的,因此选择最适合这些数据的模型是一个好策略。唉,在我们的设置中,您没有验证数据来检查不同的模型!为了了解如何解决这个问题,我们先来介绍几个误差测量方法:
- 训练误差是当你在训练模型的相同数据上运行训练模型时得到的误差。
- 测试(或推广)误差是当你在全新的、看不见的数据上运行你的模型时你得到的误差。
- 假设在对模型进行训练后,您观察到响应变量的新值与训练数据中的属性值相同。例如,假设您根据房间数量来预测房子的价格。在您的训练数据中,您有一个价值$300𝑘的房子,有 5 个房间,现在您观察一个为$350𝑘出售的房子,也有 5 个房间。模型对这些新数据产生的误差被称为样本内误差(因为特征的值与训练样本中的值相同——我同意这不是最直接的表示法)。
现在考虑这个量:样本内误差—训练误差。它通常是正的:训练误差较小,因为它基于模型被优化的完全相同的数据。但是这到底意味着什么呢?嗯,可以证明(达到预期)它认为

其中,𝑁是观察值的数量,最后一项是训练集响应与其预测值之间的协方差。这个协方差越大,我们对训练数据的模型拟合就越强(达到过拟合的程度),因此训练误差下降,增加了等式的左侧。
让我们假设(这是一个重要的假设)我们正在处理一个参数是*线性的模型。这意味着我们在谈论线性或逻辑回归模型、非线性样条或自回归模型。在这种情况下,上面的协方差项简化为 d ** σϵ,其中𝑑是模型复杂性的度量(线性回归中的要素数量,回归样条中的基函数数量),σϵ是误差方差。在替换这种简化并重新排列术语后,我们得到

这给了我们什么?如果我们可以估计最右边的项,那么我们可以将它添加到训练误差中,以获得样本内误差的估计值。而样品内误差正是我们选型所需要的!当然,它没有给我们关于模型的泛化性能的信息(这是模型评估的作用,请继续阅读)。此外,我们并不真正关心样本内错误的具体值——在部署后看到与训练数据中相同的特征值是相当罕见的。但是不同模型的样本内误差的相对大小允许我们选择最好的一个。

基于信息准则的模型选择
上述公式也被称为软糖的𝐶𝑝 :

当模型符合平方损失时,它可用于模型选择,我们只需选择具有最低𝐶𝑝.的模型
如果模型不一定符合平方损失,我们需要稍微调整 Mallows 的𝐶𝑝。回想一下,我们讨论的是参数线性的模型:想想线性或逻辑回归、回归样条或 ARIMA 模型。它们通常由最大似然估计,在高斯模型下,它保持(直到一个常数)

求解𝑒_𝑡𝑟𝑎𝑖𝑛并将其代入𝐶𝑝公式,我们得到阿凯克信息标准或 AIC :

AIC 基本上是一个惩罚的可能性。它随着模型复杂性𝑑的增加而上升,随着模型与数据的拟合度(对数似然)的增加而下降,权衡这两者。我们选择 AIC 最低的型号。最好的一点是尽管 AIC 是仅使用训练数据计算的,但最小化它在渐近上等价于最小化留一交叉验证均方误差,这对于模型选择来说非常好。关于 AIC 的更多警告(也是在时间序列预测的背景下),请查看 Rob Hyndman 的这篇精彩文章,他是 R 的forecast软件包的作者。
现在让我们看一个用 AIC 进行模型选择的 Python 例子。我们将使用来自 scikit-learn 数据集的臭名昭著的波士顿住房数据。我们来拟合两个解释房价的线性回归模型。我们将为此使用statsmodels包,因为它可以方便地为我们计算 AIC。这两个模型都将使用房间数量和房龄作为特征。模型 1 将在此基础上使用社区犯罪率,而模型 2 将使用到大型就业中心的距离。这两种型号哪个更好?
Model1 AIC: 3268.8701039911457
Model2 AIC: 3300.3758836602733
就 AIC 而言,模型 1(使用犯罪率的模型)更好,因为它的 AIC 值更低。
AIC 不是唯一的信息标准。另一个是 BIC,或贝叶斯信息准则,也称为施瓦兹准则。与 AIC 类似,BIC 也是一个被处罚的可能性,但处罚条款不同:

这种惩罚比 AIC 更倾向于惩罚更复杂的模型。让我们看看这两个房价模型在 BIC 的表现如何。
Model1 BIC: 3285.7762506682957
Model2 BIC: 3317.2820303374233
同样,模型 1 是首选,因为它得分较低的 BIC。在我们的例子中,这两个标准是一致的,但情况并不一定如此。那么我们应该根据什么标准来选择模型呢?没有放之四海而皆准的答案,但请记住以下几点:
- BIC 是渐近一致的,这意味着当呈现一组竞争模型时,它有很高的概率选择真模型(根据该模型生成数据)。
- AIC 没有一致性,但是你相信有一个真正的模型可以选择吗?
- BIC 喜欢更节俭的模式。对于小数据样本,它可能会选择过于简单的模型。另一方面,对于大样本,AIC 倾向于选择过于复杂的样本。

交叉验证的模型评估
现在,我们已经选择了包含犯罪率特性的模型,如果能知道它部署后的预期性能,那就太好了。这就是模型评估的工作——评估模型的测试误差。
一种方法是通过众所周知的交叉验证程序。我们将数据随机分成𝑘子集或褶皱,然后迭代通过它们,留下当前的褶皱,并用剩余的𝑘−1 褶皱拟合模型。然后,我们评估模型在遗漏折叠上的误差,并进行下一次迭代。这样,我们获得了𝑘误差估计。一旦取平均,它们就形成了测试误差的交叉验证估计值。很简单,对吧?但是𝑘的价值应该是什么呢?
像机器学习中的许多其他选择一样,𝑘的选择是在偏差和方差之间。走极端设置𝑘=𝑁会导致所谓的留一交叉验证。在这种情况下,每个观察构成了它自己的折叠。因此,训练集在褶皱上非常相似,事实上,它们只有一个观察结果不同。因此,测试误差的 CV 估计值可能会受到高方差的影响。另一方面,当𝑘很小的时候,我们就有高度偏见的风险。这是因为𝑘越小,构成𝑘−1 训练褶皱的观测值就越少。例如,考虑一下𝑁=100 的观察。对于𝑘=10,每个折叠有 10 个观察值,因此每次训练都基于 90 个观察值。对于𝑘=4,每次训练仅使用 75 个观察值。如果模型性能随着训练数据的减少而降低,过低的𝑘将导致高估的误差。
最终,𝑘的选择应取决于您的应用。𝑘=𝑁很少是一个好主意——运行起来计算量也很大(𝑁模型需要训练)!您最了解您的数据-如果您愿意假设(或已经证明)更少的数据不会对您的模型的性能产生太大影响,那么您可以选择 3 或 5 这样的小𝑘。但是如果你只有很少的数据,情况可能就不一样了(如果你有大数据,你可以遵循标准的训练/验证/测试分割,忽略这篇文章)。因此,稍微大一点的𝑘,比如 10,可能值得一试。还有一点需要考虑:如果你的𝑘太小,误差将会被高估,这意味着真实的测试误差可能会比你的简历告诉你的要小。如果您对交叉验证的误差估计值感到满意,您可能会对生产性能更加满意。
让我们用 10 重交叉验证来评估我们选择的模型。为此,我们将使用scikit-learn API。
Cross-validated testing MSE: 43.925463559757674

结束语
总结一下:刚刚发生了什么?如果您没有足够的数据来留出验证和测试集,您需要其他方法来进行模型选择和评估。我们已经展示了什么是信息标准以及如何将它们用于模型选择,然后如何通过交叉验证来估计所选模型的预期真实性能。
为什么不用 CV 来选模型,我听到你问。你可以的!然而,对于小数据,信息标准往往更可靠。如果你的简历会因为数据太少而有偏差,至少你会知道你选择的模型是正确的——即使它的交叉验证测试误差估计远非完美。
感谢阅读!我希望你已经学到了对你的项目有益的东西🚀
如果你喜欢这篇文章,试试我的其他文章。不能选择?从这些中选择一个:
揭秘著名的竞赛获奖算法。
towardsdatascience.com](/boost-your-grasp-on-boosting-acf239694b1) [## 线性回归中收缩法和选择法的比较
详细介绍 7 种流行的收缩和选择方法。
towardsdatascience.com](/a-comparison-of-shrinkage-and-selection-methods-for-linear-regression-ee4dd3a71f16) [## 非线性回归:基础扩展、多项式和样条
如何用多项式和样条捕捉非线性关系?
towardsdatascience.com](/non-linear-regression-basis-expansion-polynomials-splines-2d7adb2cc226) 
来源
- Hastie,Tibshirani,r .,,j . h . Friedman(2009 年)。统计学习的要素:数据挖掘、推理和预测。第二版。纽约:斯普林格。
- https://robjhyndman.com/hyndsight/aic/
文本分类中的模型选择

来源:作者图片
更新 2020–11–24:结论中增加了资源
更新 2020 年 8 月 21 日:管道现在可以用于二进制和多类分类问题。在处理模型的保存时,转换器仍然存在错误。
更新 2020–07–16:管道现在可以保存模型,修正了主函数内部的错误。添加了 Adaboost、Catboost、LightGBM、extractree 分类器
简介:
一开始,有一个简单的问题。我的经理来问我是否可以用 NLP 方法对邮件和相关文档进行分类。
听起来并不奇怪,但我会从成千上万个样本开始。问的第一件事就是用“XGBoost”,因为:“我们可以用 XGBoost 做任何事情”。有趣的工作,如果数据科学都归结于 XGBoost…
在用不同的算法、指标和可视化实现了一个笔记本之后,我的脑海中仍然有一些东西。我无法一次在不同的型号中做出选择。仅仅是因为你可能对一个模型抱有侥幸心理,而不知道如何重现良好的准确度、精确度等…
所以此时此刻,我问自己,如何做一个模特选拔?我在网上寻找,阅读帖子,文章,等等。看到实现这种东西的不同方式是非常有趣的。但是,当考虑到神经网络时,它是模糊的。这时,我脑子里有一件事,如何比较经典方法(多项朴素贝叶斯、SVM、逻辑回归、boosting …)和神经网络(浅层、深层、lstm、rnn、cnn…)。
我在这里对笔记本做一个简短的解释。欢迎评论。
如何开始?
每个项目都是从探索性数据分析(简称 EDA )开始,然后直接是预处理(文本非常脏,邮件中的签名、url、邮件头等等……)。不同的功能将出现在 Github 库中。
查看预处理是否正确的快速方法是确定最常见的 n 元文法 (uni,bi,tri… grams)。另一篇文章将会引导你走这条路。
数据
我们将对 IMDB 数据集应用模型选择的方法。如果您不熟悉 IMDB 数据集,它是一个包含电影评论(文本)的数据集,用于情感分析(二元——正面或负面)。
更多详情可在这里找到。要下载它:
$ wget http://ai.stanford.edu/~amaas/data/sentiment/aclImdb_v1.tar.gz$ tar -xzf aclImdb_v1.tar.gz
矢量化方法
One-Hot encoding(count vectorizing):
这是一种将单词替换为 0 和 1 的向量的方法,目标是获取一个语料库(重要的单词量),并为语料库中包含的每个唯一单词制作一个向量。之后,每个单词都会被投影到这个向量中,其中 0 表示不存在,1 表示存在。
| bird | cat | dog | monkey |
bird | 1 | 0 | 0 | 0 |
cat | 0 | 1 | 0 | 0 |
dog | 0 | 0 | 1 | 0 |
monkey | 0 | 0 | 0 | 1 |
相应的 python 代码:
# create a count vectorizer object
count_vect = CountVectorizer(analyzer='word', token_pattern=r'\w{1,}')
count_vect.fit(df[TEXT]) # text without stopwords# transform the training and validation data using count vectorizer object
xtrain_count = count_vect.transform(train_x)
xvalid_count = count_vect.transform(valid_x)
TF-IDF:
词频-逆文档频率是信息检索和文本挖掘中经常用到的一个权重。这个权重是一个统计量,用于评估一个词对集合或语料库中的文档有多重要(来源: tf-idf )。
在处理大量停用词时,这种方法非常有效(这种类型的词与信息无关→ 我、我的、我自己、我们、我们的、我们的、我们自己、你 …对于英语)。IDF 术语允许显示重要词和罕见词。
# word level tf-idf
tfidf_vect = TfidfVectorizer(analyzer='word', token_pattern=r'\w{1,}', max_features=10000)
tfidf_vect.fit(df[TEXT])
xtrain_tfidf = tfidf_vect.transform(train_x_sw)
xvalid_tfidf = tfidf_vect.transform(valid_x_sw)
TF-IDF n-grams :
与之前的 tf-idf 的区别在于一字, tf-idf n-grams 兼顾了 n 连词。
# ngram level tf-idf
tfidf_vect_ngram = TfidfVectorizer(analyzer='word', token_pattern=r'\w{1,}', ngram_range=(2,3), max_features=10000)
tfidf_vect_ngram.fit(df[TEXT])
xtrain_tfidf_ngram = tfidf_vect_ngram.transform(train_x_sw)
xvalid_tfidf_ngram = tfidf_vect_ngram.transform(valid_x_sw)
TF-IDF chars n-grams:
与前面的方法相同,但重点是在字符级别,该方法将重点放在 n 个连续的字符上。
# characters level tf-idf
tfidf_vect_ngram_chars = TfidfVectorizer(analyzer='char', ngram_range=(2,3), max_features=10000)
tfidf_vect_ngram_chars.fit(df[TEXT])
xtrain_tfidf_ngram_chars = tfidf_vect_ngram_chars.transform(train_x_sw)
xvalid_tfidf_ngram_chars = tfidf_vect_ngram_chars.transform(valid_x_sw)
预训练模型—快速文本
FastText 是一个开源、免费、轻量级的库,允许用户学习文本表示和文本分类器。它在标准的通用硬件上工作。模型可以缩小尺寸,甚至适合移动设备。(来源:此处)
如何加载 fastText?来自官方文件:
$ git clone https://github.com/facebookresearch/fastText.git
$ cd fastText
$ sudo pip install .
$ # or :
$ sudo python setup.py install
下载合适的型号。这里有 157 种语言的模型。要下载英文模型:
$ wget https://dl.fbaipublicfiles.com/fasttext/vectors-english/crawl-300d-2M-subword.zip& unzip crawl-300d-2M-subword.zip
下载完成后,将其加载到 python 中:
pretrained = fasttext.FastText.load_model('crawl-300d-2M-subword.bin')
单词嵌入或单词向量(WE):
将向量与单词相关联的另一种流行且强大的方法是使用密集的“单词向量”,也称为“单词嵌入”。虽然通过一位热编码获得的向量是二进制的、稀疏的(主要由零组成)和非常高维的(与词汇表中的单词数量具有相同的维数),但是“单词嵌入”是低维浮点向量(即,与稀疏向量相反的“密集”向量)。与通过一键编码获得的单词向量不同,单词嵌入是从数据中学习的。在处理非常大的词汇表时,经常会看到 256 维、512 维或 1024 维的单词嵌入。另一方面,一键编码单词通常导致 20,000 维或更高维的向量(在这种情况下捕获 20,000 个单词的词汇)。因此,单词嵌入将更多的信息打包到更少的维度中。(来源:用 Python 进行深度学习,弗朗索瓦·乔莱 2017 )
如何用整数映射一个句子:
# create a tokenizer
token = Tokenizer()
token.fit_on_texts(df[TEXT])
word_index = token.word_index# convert text to sequence of tokens and pad them to ensure equal length vectors
train_seq_x = sequence.pad_sequences(token.texts_to_sequences(train_x), maxlen=300)
valid_seq_x = sequence.pad_sequences(token.texts_to_sequences(valid_x), maxlen=300)# create token-embedding mapping
embedding_matrix = np.zeros((len(word_index) + 1, 300))
words = []
for word, i in tqdm(word_index.items()):
embedding_vector = pretrained.get_word_vector(word) #embeddings_index.get(word)
words.append(word)
if embedding_vector is not None:
embedding_matrix[i] = embedding_vector
型号选择
计算机科学中的选型是什么?具体在 AI 领域?模型选择是在不同的机器学习方法之间进行选择的过程。所以简而言之,不同的模式。
但是,我们如何比较它们呢?要做到这一点,我们需要度量标准(更多细节见此链接)。数据集将被分割成 训练 和 测试 部分(验证集将在深度学习模型中确定)。
在这个二元或多类分类模型选择中,我们使用什么样的度量标准?
为了分类,我们将使用以下术语:
- tp :真阳性预测
- tn :真阴性预测
- fp :假阳性预测
- fn :假阴性预测
这里有一个链接以了解更多细节。
- 准确度 :所有预测上的所有正预测
(tp + tn) / (tp + tn + fp + fn) - :定义为不平衡数据集在每个类上获得的平均召回率
- 精度 :精度直观上是分类器不将 tp / (tp + fp)为阴性的样本标记为阳性的能力
- 召回 (或灵敏度):召回是分类器直观地找到所有阳性样本 tp / (tp + fn)的能力
- ****f1-得分:f1 得分可以解释为精度和召回率的加权平均值- > 2 (精度召回率)/(精度+召回率)
- Cohen kappa :表示两个标注者对一个分类问题的一致程度的分数。因此,如果数值小于 0.4 就很糟糕,在 0.4 到 0.6 之间,它相当于人类,0.6 到 0.8 是很大的数值,超过 0.8 就是例外。
- 马修斯相关系数:马修斯相关系数 (MCC)用于 机器学习 作为二进制(两类) 分类 […]该系数考虑了真、假阳性和阴性,通常被视为可以使用的平衡度量 MCC 本质上是观察到的和预测的二元分类之间的相关系数;它返回 1 到+1 之间的值。系数+1 表示完美预测,0 表示不比随机预测好,1 表示预测和观察完全不一致。(来源: 维基百科 )
- 受试者操作特征曲线下面积 (ROC AUC)
- 时间拟合:训练模型所需的时间
- 时间分数:预测结果所需的时间
太好了!!我们是我们的衡量标准。下一步是什么?
交叉验证
为了能够实现模型之间的稳健比较,我们需要验证每个模型的稳健性。
下图显示了需要将全局数据集拆分为训练和测试数据。训练数据用于训练模型,测试数据用于测试模型。交叉验证是在 k-fold 中分离数据集的过程。k 是我们实现数据所需的比例数。

来源: sklearn
一般来说, k 为 5 或 10 这将取决于数据集的大小(小数据集小 k ,大数据集大 k )。**
目标是计算每个折叠的每个指标,并计算它们的平均值(平均值)和标准偏差(标准偏差)。**
在 python 中,这个过程将通过 scikit-learn 中的 cross_validate 函数来完成。
我们会对比哪些车型?
评估的型号
我们将测试机器学习模型、深度学习模型和 NLP 专门模型。
机器学习模型
- 多项式朴素贝叶斯
- 逻辑回归
- SVM (SVM)
- 随机梯度下降
- k 近邻
- 随机森林
- 梯度增强(GB)
- XGBoost(著名的)(XGB)
- adaboost 算法
- Catboost
- LigthGBM
- 树外分级机
深度学习模型
- 浅层神经网络
- 深度神经网络(和 2 个变体)
- 递归神经网络(RNN)
- 长短期记忆(LSTM)
- 卷积神经网络(CNN)
- 门控循环单元(GRU)
- CNN+LSTM
- CNN+GRU
- 双向 RNN
- 双向 LSTM
- 双向 GRU
- 递归卷积神经网络(RCNN)(和 3 个变体)
- 变形金刚(电影名)
仅此而已,30 款还不错。
这可以在这里恢复:
让我们展示一些代码
机器学习
对于经典算法,我将使用sk learn(0.23 版)中的 cross_validate() 函数来考虑多指标。下面的函数, report ,获取一个分类器, X,y 数据,以及一个自定义指标列表,并使用参数计算它们的交叉验证。它返回一个 dataframe,包含所有指标的值,以及每个指标的平均值和标准偏差( std) 。**
使用机器学习算法计算不同指标的函数
怎么用?
下面是多项式朴素贝叶斯的一个例子:
术语 if multinomial_naive_bayes 之所以存在,是因为此代码是笔记本的一部分,开头带有参数(布尔型)。所有代码都在 GitHub 和 Colab 中。
深度学习
我还没有找到深度学习的 cross_validate 这样的函数,只有关于对神经网络使用 k 倍交叉验证的帖子。这里我将分享一个用于深度学习的自定义 cross_validate 函数,其输入和输出与 report 函数相同。它将允许我们使用相同的指标,并将所有模型放在一起进行比较。
利用二元和多类分类的度量进行深度学习的 k-Folds 交叉验证
目标是将神经网络函数视为:
并在 cross_validate_NN 函数内部调用这个函数。所有的深度学习都获得了相同的实现,并将进行比较。有关不同型号的完整实现,请访问笔记本。
结果
当所有模型计算出不同的折叠和度量时,我们可以很容易地将它们与数据框架进行比较。在 IMDB 数据集上,表现较好的模型是:
这里我只展示了准确率> 80%的结果,以及准确度、精确度和召回指标的结果。
如何改善?
- 在模型字典的参数中构建一个函数,并将所有工作连接成一个循环
- 使用分布式深度学习
- 使用 TensorNetwork 加速神经网络
- 使用 GridSearch 进行超调
结论
现在,您有了一个导入管道,可以为带有大量参数的文本分类进行模型选择。所有的模型都是自动的。享受吧。
另一个关于机器学习时代模型选择的伟大资源(更多理论文章)是由 Samadrita Ghosh 在 Neptune.ai 博客上写的。
大神经网络和小数据的模型选择
高度超参数化的神经网络可以表现出很强的泛化性能,即使是在小数据集上

标题声明当然是一个大胆的主张,我怀疑你们中的许多人现在正在摇头。
根据统计学习的经典教导,这与众所周知的偏差-方差权衡相矛盾。这个理论定义了一个最佳点,如果你进一步增加模型的复杂性,泛化误差会增加(典型的 U 型测试误差曲线)。
您可能会认为这种影响对于参数数量 p 大于观察数量 n 的小数据集更为明显,但不一定如此。
在最近由 Deepmind (Bornschein,2020)发表的一篇 ICML 2020 论文中,人们可以在训练数据的更小子集上进行训练,同时保持可概括的结果,即使对于大型的过度参数化模型也是如此。如果这是真的,我们可以显著降低模型选择和超参数调整的计算开销。
思考一下这件事的含义。这可能会极大地改变我们选择最佳模型或调整超参数的方式(例如在 Kaggle 比赛中),因为我们可以在网格搜索中包括更多的模型(等等)。
这好得令人难以置信吗?我们如何证明这一点?
在我们开始之前,这里有一些要点:
- 模型选择可能只使用训练数据的子集,从而节省计算资源** ( 相对排序假设)**
- 大型过度参数化的神经网络能够惊人地推广 ( 双重下降)
- 达到最小值后,测试交叉熵倾向于随时间逐渐增加,同时测试准确度提高 ( 过度自信)。这可以通过使用温度刻度来避免。
让我们开始吧。
1.偏倚-方差权衡的经典理论述评
在我们开始之前,我会给你两个选择。如果你已经厌倦了第 100 次听到偏差-方差权衡,请阅读第 1 部分末尾的 TLDR ,然后继续阅读第 2 部分。否则,我将简要介绍理解基础知识所需的最低要求,然后再继续讨论实际的论文。
所有监督学习算法的预测误差可以分为三个(理论上的)部分,这对于理解偏差-方差权衡是必不可少的。这些是;1)偏差 2)方差 3)不可约误差(或噪声项)
不可约误差(有时称为噪声)是一个与所选模型无关的术语,永远无法减少。这是由于问题的框架不完善而产生的数据的一个方面,这意味着我们永远无法捕捉数据的真实关系——无论我们的模型有多好。
偏差一词通常是人们在提到模型(预测)误差时想到的。简而言之,它衡量“平均”模型预测和实际情况之间的差异。在这种情况下,平均可能看起来很奇怪,因为我们通常只训练一个模型。这么想吧。由于我们数据中的小扰动(随机性),即使使用相同的模型,我们也可以得到稍微不同的预测。通过平均由于这些扰动而得到的预测范围,我们得到了偏差项。高偏差是模型拟合差(拟合不足)的标志,因为它在训练集和测试集上都有很大的预测误差。
最后,方差项是指给定数据点的模型预测的可变性。这听起来可能很相似,但关键区别在于“平均值”和“数据点”。高方差意味着高泛化误差。例如,虽然模型在训练集上可能相对准确,但它在测试集上的拟合度却相当差。当训练过度参数化的神经网络时,后一种情况(高方差、低偏差)通常是最有可能的,即我们所说的过度拟合。
这些术语的实际含义意味着平衡偏差和方差(因此得名权衡),通常通过模型复杂性来控制。最终目标是获得低偏差和低方差。这是你以前可能见过的典型的 U 型测试误差曲线。

来自https://www.digitalvidya.com/blog/bias-variance-tradeoff/
好吧,我假设你对偏差-方差权衡有足够的了解,可以理解为什么最初声称过度参数化的神经网络不一定意味着高方差确实令人困惑。
TLDR;高方差、低偏差是过度拟合的标志。当模型在训练集上达到高精度,但在测试集上达到低精度时,就会发生过度拟合。这通常发生在过度参数化的神经网络中。
2.现代政体——型号越大越好!
在实践中,我们通常使用带有(例如)提前停止的验证集来优化偏差-方差权衡。有趣的是,这种方法可能是完全错误的。在过去的几年中,研究人员发现,如果你继续拟合越来越灵活的模型,你会得到所谓的双下降,即泛化误差在达到中间峰值后将再次开始下降。这一发现在 Nakkiran et al. (2019) 关于现代神经网络架构的已建立和挑战性数据集上得到了经验验证。见下图来自 OpenAI,展示了这个场景;

来自https://openai.com/blog/deep-double-descent/
- 测试误差最初下降,直到它达到(局部)最小值,然后随着复杂性的增加再次开始增加。在临界状态下,我们不断增加模型的复杂性是很重要的,因为测试误差将开始再次下降,最终达到(全局)最小值。
这些发现表明,由于双下降现象,较大的模型通常更好,这挑战了长期以来关于过度参数化神经网络的过度拟合的观点。
3.相对排序假设
已经确定大型超参数化神经网络可以很好地推广,我们想更进一步。进入相对排名假设。在我们解释假设之前,我们注意到,如果被证明为真,那么您可以为您的下一个实验对您的训练数据集的一个小子集执行模型选择和超参数调整,这样做可以节省计算资源和宝贵的训练时间。
我们将简要介绍这个假设,然后用几个实验来验证这个说法。作为文献中未包括的额外实验(据我们所知),我们将调查一个可能使相对排名假设无效的设置,即不平衡数据集。
a)理论
Bornschein (2020)的一个关键假设是;
“当在训练集的任意小的子集上训练时,过度参数化的模型架构似乎保持了它们在泛化性能方面的相对排名”。
他们称这一观察为相对排名假说。
用外行人的话说;假设我们有 10 个型号可供选择,编号从 1 到 10。我们在训练数据的 10%子集上训练我们的模型,并且发现模型 6 是最好的,其次是模型 4,然后是模型 3,等等..
排序假设假定,当我们逐渐将子集百分比从 10%增加到 100%时,我们应该获得最优模型的完全相同的排序。
如果这个假设是真的,我们可以在原始数据的一个小的子集上执行模型选择,从而获得更快收敛的额外好处。如果这还不够有争议的话,作者甚至更进一步,因为他们发现了一些实验,在这些实验中,对小数据集的训练导致了更鲁棒的模型选择(更少的方差),这当然似乎是违反直觉的,因为我们预计较小的数据集会有相对更多的噪声。
b)温度校准
训练神经网络分类器时的一个奇怪现象是,交叉熵误差有增大的趋势,而分类误差减小。这种看似违反直觉的,其实只是因为模型在预测中变得过于自信(郭等(2017) )。我们可以使用一种叫做温度缩放的东西,它可以在一个小的保留数据集上校准交叉熵估计。与经典的交叉熵相比,这产生了更一般化和性能更好的结果,特别是与过度参数化的神经网络相关。作为一个粗略的类比,你可以认为这提供了更少的关于过度拟合情况的“假阴性”。
虽然 Bornschein (2020 年)没有提供他们论文中使用的 softmax 温度校准程序的明确细节,但我们在实验中使用了以下程序:
- 我们定义了一个保持校准数据集 C,相当于训练数据的 10%。
- 我们将温度标量初始化为 1.5(如郭等人(2017 年)所述)
对于每个时期;
- 1)在我们的校准集 C 上计算交叉熵损失
- 2)在校准集上使用梯度下降优化温度标量(参见郭等人的 Github 报告(2017) )
- 3)在梯度下降期间,使用更新的温度标量来校准常规交叉熵
- 在训练 50 个时期后,我们计算校准的测试误差,该误差应该不再表现出过度自信的迹象。
现在让我们转向实验设置。
4.实验
在这篇文章中,我们将进行两个实验。一个用于验证 MNIST 数据集上的相对排名假设,一个用于评估如果我们综合地使 MNIST 不平衡,我们的结论会如何变化。Bornschein (2020)论文中的没有包括后一个实验,并且可能会使不平衡数据集的相对排序假设无效。
MNIST
我们从复制 Bornschein (2020)对 MNIST 的研究开始,然后继续进行不平衡数据集实验。这并不意味着否定论文中的任何说法,而只是为了确保我们尽可能地复制了他们的实验设置(做了一些修改)。
- 分别对训练集和校准集进行 90%/10%的拆分
- 随机抽样(根据论文,平衡子集抽样没有提供任何额外的好处)
- 50 个时代
- 具有固定学习率的 Adam[10e-4]
- 批量= 256
- 具有 3 个隐藏层和 2048 个单元的全连接 MLPs
- 没有辍学(使我们的结果太不稳定,无法纳入)
- 具有 4 层、5×5 空间核、跨距 1 和 256 个信道的简单卷积网络
- 逻辑回归
- 10 种不同的种子来显现不确定性带(原始论文中有 30 种)
作者还提到了用 tanh、batch-norm、layer-norm 等替换 ReLU 的实验。,但不清楚这些测试是否包括在他们的最终结果中。因此,我们只考虑使用上述设置的实验。
实验一:梯度下降过程中的温度标度如何影响泛化?
作为初始实验,我们希望验证为什么需要温度缩放。为此,我们分别使用 ReLU 和 3 个 2048 个单位的隐藏层来训练 MLP。我们不包括辍学者,我们培训 50 个纪元。
我们的假设是:随着时间的推移,测试交叉熵应该逐渐增加,而测试精度会逐渐降低(首先是温度缩放的动机,即模型过度自信)。
下面是这个初步实验的结果:

显然,测试熵确实在开始时下降,然后随着时间逐渐增加,同时测试精度不断提高。这是支持假设 1 的证据。Guo 等人(2017 年)的图 3 展示了对 CIFAR-100 的完全相同的影响。
注:我们对结果做了一点平滑处理(5 窗口滚动平均)使效果更明显。
实验 1 的结论:
- 如果我们持续训练大型神经网络足够长的时间,我们会开始看到过于自信的概率预测,使它们在样本外变得不那么有用。
为了补救这种影响,我们可以结合温度缩放
- a) 确保样本外的概率预测更加稳定和可靠,以及
- b) 在梯度下降过程中,通过缩放训练交叉熵来提高泛化能力。
平衡数据集
已经表明需要温度缩放,我们现在转向主要实验,即测试交叉熵如何作为我们训练数据集大小的函数而变化。我们的结果如下:

测试交叉熵作为 MNIST 训练集大小的函数
注意,我们没有获得与 Bornschein (2020)完全相同的“平滑”结果。这很可能是因为我们没有完全复制他们的实验,因为他们包括了更多不同的种子。尽管如此,我们可以得出以下结论:
- 有趣的是,相对较大的 ResNet-18 模型在训练过程中的任何时候都不会比逻辑回归拟合得更好!
- 相对排序假设得到了证实
- 超过 25000 次观察(大约是 MNIST 训练数据集的一半),明显更大的 ResNet 模型仅比相对更快的 MLP 模型略好。
不平衡数据集
我们现在将针对不平衡数据集的情况进行一个实验,该实验并不包括在实际的论文中,因为它可能是一个测试假设无效的设置。
我们采样一个类似于郭等(2019) 的人为失衡版本的。程序如下。对于数据集中的每个类,我们对原始训练和测试数据集进行 0%到 100%的二次抽样。我们使用下面的 GitHub repo 进行采样。
然后,我们选择类似于先前实验的校准数据集,即,在训练和校准之间随机 90/10%分割。
我们包括了原始 MNIST 训练数据集的类分布的可视化

MNIST 每个等级的频率计数
还有不平衡版

不平衡 MNIST 中每个类的频率计数
鉴于这种频率分布的巨大差异,你可以清楚地看到这个版本比原来的 MNIST 更加不平衡。
虽然存在大量不同的方法来克服不平衡数据集的问题(参见下面的综述论文),但我们希望研究和隔离不平衡数据集对相对排序假设的影响,即相对排序假设在不平衡数据设置中是否仍然成立?
我们使用这个综合不平衡的 MNIST 数据集再次运行我们的所有模型,并获得以下结果:

测试作为不平衡 MNIST 的训练集大小的函数的交叉熵
那么结论改变了吗?
不是真的!
这是一个非常乐观的结果,因为我们现在更有信心,在不平衡数据集的情况下,相对排序假设大多是正确的。我们认为这也可能是引用 Bornschein (2020 年)论文中关于抽样策略的原因;
“我们试验了平衡子集抽样,即确保所有子集在每类中都包含相同数量的样本。但是我们没有观察到这样做有任何可靠的改进,因此我们又回到了简单的 i.i.d .采样策略。”
平衡版本和不平衡版本之间的主要区别是更多的"跳动的"结果,这是有意义的,因为测试集中可能有在所选模型的训练期间看不到的类。
5.摘要
总结我们的发现:
- 由于相对排序假设,对于平衡和不平衡数据集,我们可以仅使用我们的训练数据的子集来执行模型选择,从而节省计算资源****
- 大型过度参数化的神经网络能够惊人地推广,甚至在小型数据集上(双重下降)
- 我们可以通过应用温度标度来避免过度自信
我希望你能够在下一次机器学习实验中应用这些发现,并且记住,越大(几乎)总是越好。
感谢您的阅读!
6.参考
[1] J. Bornschein,F. Visin 和 S. Osindero,《小数据,大决策:小数据体制中的模型选择》( 2020 年),载于机器学习国际会议(ICML)。
[2] P. Nakkiran,G. Kaplun,Y. Yang,B. T. Barak 和 I. Sutskever,深度双重下降:更大的模型和更多数据的伤害(2019),arXiv 预印本 arXiv:1912.02292。
[3]郭、普莱斯、孙和温伯格(2017 年)。关于现代神经网络的校准(2017),arXiv 预印本 arXiv:1706.04599。
[4] T. Guo,X. Zhu,Y. Wang,和 F. Chen,用于深度不平衡学习的判别样本生成(2019),载于人工智能组织国际联合会议(IJCAI)(第 2406-2412 页)。
原载于 2020 年 8 月 14 日https://holm dk . github . io。
TensorFlow 2 中从头开始的模型子分类和自定义训练循环
对 TF.Keras 中模型子类化和自定义训练循环的各种组件的简单介绍。

在本文中,我们将尝试从 TensorFlow 2 中的开始理解模型子类 API 和自定义训练循环。这可能不是一个初学者或高级介绍,但目的是获得他们都是什么粗略的直觉。本帖分为三个部分:
- tensor flow 2 中的可比建模策略
- 用模型子类化 API 建立一个初始网络
- 端到端培训,从头开始定制培训循环
因此,首先,我们将了解使用 TensorFlow 2 定义模型的几种方法以及它们之间的区别。接下来,我们将看到使用 TF 2 中引入的模型子类化 API 来构建一个复杂的神经架构是多么的可行。然后我们将实现一个定制的训练循环,从头开始端到端地训练这些子类化模型。我们还将在我们的定制训练循环中使用 Tensorboard 来跟踪每一批的模型性能。我们还将看到如何在训练后保存和加载模型。最后,我们将通过混淆矩阵和分类报告等来衡量模型的性能。
TensorFlow 2 中的可比建模策略
在中 TF。Keras 我们基本上有三种方式可以定义一个神经网络,即
- 顺序 API
- 功能 API
- 模型子类化 API
其中,顺序 API 是最容易实现的方法,但有一定的局限性。例如,使用这个 API,我们不能创建一个与另一个层共享特性信息的模型,除了它的后续层。此外,多输入和多输出也不可能实现。在这一点上,功能 API 确实很好地解决了这些问题。像 Inception 或 ResNet 这样的模型在函数式 API 中实现是可行的。但深度学习研究人员通常希望对网络和训练管道的每个细微差别有更多的控制,这正是模型子类化 API 所服务的。模型子类化是一种完全可定制的方式,以面向对象的方式实现定制设计的深度神经网络的前馈机制。
让我们使用这三个 API 创建一个非常基本的神经网络。这将是相同的神经架构,并会看到什么是实现差异。这当然不会展示全部潜力,尤其是对于功能和模型子类 API 。架构将如下所示:
Input - > Conv - > MaxPool - > BN - > Conv -> BN - > Droput - > GAP -> Dense
很简单。如上所述,让我们分别用顺序、功能和模型子类创建神经网络。
顺序 API
# declare input shape
seq_model = tf.keras.Sequential()
seq_model.add(tf.keras.Input(shape=imput_dim))
# Block 1
seq_model.add(tf.keras.layers.Conv2D(32, 3, strides=2, activation="relu"))
seq_model.add(tf.keras.layers.MaxPooling2D(3))
seq_model.add(tf.keras.layers.BatchNormalization())
# Block 2
seq_model.add(tf.keras.layers.Conv2D(64, 3, activation="relu"))
seq_model.add(tf.keras.layers.BatchNormalization())
seq_model.add(tf.keras.layers.Dropout(0.3))
# Now that we apply global max pooling.
seq_model.add(tf.keras.layers.GlobalMaxPooling2D())
# Finally, we add a classification layer.
seq_model.add(tf.keras.layers.Dense(output_dim))
功能 API
# declare input shape
input = tf.keras.Input(shape=(imput_dim))
# Block 1
x = tf.keras.layers.Conv2D(32, 3, strides=2, activation="relu")(input)
x = tf.keras.layers.MaxPooling2D(3)(x)
x = tf.keras.layers.BatchNormalization()(x)
# Block 2
x = tf.keras.layers.Conv2D(64, 3, activation="relu")(x)
x = tf.keras.layers.BatchNormalization()(x)
x = tf.keras.layers.Dropout(0.3)(x)
# Now that we apply global max pooling.
gap = tf.keras.layers.GlobalMaxPooling2D()(x)
# Finally, we add a classification layer.
output = tf.keras.layers.Dense(output_dim)(gap)
# bind all
func_model = tf.keras.Model(input, output)
模型子类 API
class ModelSubClassing(tf.keras.Model):
def __init__(self, num_classes):
super(ModelSubClassing, self).__init__()
# define all layers in init
# Layer of Block 1
self.conv1 = tf.keras.layers.Conv2D(
32, 3, strides=2, activation="relu"
)
self.max1 = tf.keras.layers.MaxPooling2D(3)
self.bn1 = tf.keras.layers.BatchNormalization()
# Layer of Block 2
self.conv2 = tf.keras.layers.Conv2D(64, 3, activation="relu")
self.bn2 = tf.keras.layers.BatchNormalization()
self.drop = tf.keras.layers.Dropout(0.3)
# GAP, followed by Classifier
self.gap = tf.keras.layers.GlobalAveragePooling2D()
self.dense = tf.keras.layers.Dense(num_classes)
def call(self, input_tensor, training=False):
# forward pass: block 1
x = self.conv1(input_tensor)
x = self.max1(x)
x = self.bn1(x)
# forward pass: block 2
x = self.conv2(x)
x = self.bn2(x)
# droput followed by gap and classifier
x = self.drop(x)
x = self.gap(x)
return self.dense(x)
在模型子类中有两个最重要的函数 init 和 调用 。基本上,我们将在 init 方法中定义所有可训练的 tf.keras 层或自定义实现的层,并在用于执行前向传播的 call 方法中根据我们的网络设计调用这些层。(反正和 PyTorch 里用来建模型的 forward 方法挺像的。)
让我们在 MNIST 数据集上运行这些模型。我们将从TF . keras . datasets加载。然而,输入图像是 28 乘 28 并且是灰度形状。我们将重复这个轴三次,这样如果需要的话,我们就可以用预训练的重量进行可行的实验。
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()
# x_train.shape, y_train.shape: (60000, 28, 28) (60000,)
# x_test.shape, y_test.shape : (10000, 28, 28) (10000,)
# train set / data
x_train = np.expand_dims(x_train, axis=-1)
x_train = np.repeat(x_train, 3, axis=-1)
x_train = x_train.astype('float32') / 255
# train set / target
y_train = tf.keras.utils.to_categorical(y_train, num_classes=10)
# validation set / data
x_test = np.expand_dims(x_test, axis=-1)
x_test = np.repeat(x_test, 3, axis=-1)
x_test = x_test.astype('float32') / 255
# validation set / target
y_test = tf.keras.utils.to_categorical(y_test, num_classes=10)
# ------------------------------------------------------------------
# compile
print('Sequential API')
seq_model.compile(
loss = tf.keras.losses.CategoricalCrossentropy(),
metrics = tf.keras.metrics.CategoricalAccuracy(),
optimizer = tf.keras.optimizers.Adam())
# fit
seq_model.fit(x_train, y_train, batch_size=128, epochs=1)
# compile
print('\nFunctional API')
func_model.compile(
loss = tf.keras.losses.CategoricalCrossentropy(),
metrics = tf.keras.metrics.CategoricalAccuracy(),
optimizer = tf.keras.optimizers.Adam())
# fit
func_model.fit(x_train, y_train, batch_size=128, epochs=1)
# compile
print('\nModel Sub-Classing API')
sub_classing_model = ModelSubClassing(10)
sub_classing_model.compile(
loss = tf.keras.losses.CategoricalCrossentropy(),
metrics = tf.keras.metrics.CategoricalAccuracy(),
optimizer = tf.keras.optimizers.Adam())
# fit
sub_classing_model.fit(x_train, y_train, batch_size=128, epochs=1);
输出
Sequential API
469/469 [==============================] - 2s 3ms/step - loss: 7.5747 - categorical_accuracy: 0.2516
Functional API
469/469 [==============================] - 2s 3ms/step - loss: 8.1335 - categorical_accuracy: 0.2368
Model Sub-Classing API
469/469 [==============================] - 2s 3ms/step - loss: 5.2695 - categorical_accuracy: 0.1731
用模型子类 API 构建一个初始网络
TF 中的核心数据结构。Keras 是层和模型类。一层封装了 状态 (权重)和从输入到输出的转换,即 调用 方法,用于定义正向传递。然而,这些层也是递归可组合的。这意味着如果我们将一个TF . keras . layers . layer实例指定为另一个TF . keras . layer的属性,外层将开始跟踪内层的权重矩阵。因此,每一层将跟踪其子层的权重,包括可训练的和不可训练的。当我们需要构建这样一个更高级别的抽象层时,就需要这样的功能。
在这一部分中,我们将通过对层和模型类进行子类化来构建一个小型的初始模型。请看下图。这是一个小盗梦空间网络 src 。如果仔细观察,我们会发现它主要由三个特殊模块组成,即:
- Conv 模块
- 盗梦模块
- 下采样模块

一个小型的盗梦空间网络, src 。
Conv 模块
从图中我们可以看到,它由一个卷积网络、一个批量归一化和一个 relu 激活组成。同样,它用 K x K 滤镜和 S x S 步距生成 C 倍特征图。现在,如果我们简单地采用顺序建模方法,效率会非常低,因为我们将在整个网络中多次重复使用该模块。因此,定义一个功能块将是足够简单有效的。但是这一次,我们会更喜欢层子类更python 化更高效。为此,我们将创建一个将继承TF . keras . layers . layer类的类对象。
class ConvModule(tf.keras.layers.Layer):
def __init__(
self, kernel_num, kernel_size, strides, padding='same'
):
super(ConvModule, self).__init__()
# conv layer
self.conv = tf.keras.layers.Conv2D(
kernel_num,
kernel_size=kernel_size,
strides=strides,
padding=padding
)
# batch norm layer
self.bn = tf.keras.layers.BatchNormalization()
def call(self, input_tensor, training=False):
x = self.conv(input_tensor)
x = self.bn(x, training=training)
x = tf.nn.relu(x)
return x
现在,我们还可以初始化该类的对象,并查看以下属性。
cm = ConvModule(96, (3,3), (1,1))
y = cm(tf.ones(shape=(2,32,32,3))) # first call to the `cm` will create weights
print("weights:", len(cm.weights))
print("trainable weights:", len(cm.trainable_weights))
# output
weights: 6
trainable weights: 4
初始模块
接下来是初始模块。根据上图,它由两个 卷积模块 组成,然后合并在一起。现在我们知道要合并,这里我们需要确保输出特征图的尺寸( 高度 和 宽度 )需要相同。
class InceptionModule(tf.keras.layers.Layer):
def __init__(self, kernel_size1x1, kernel_size3x3):
super(InceptionModule, self).__init__()
# two conv modules: they will take same input tensor
self.conv1 = ConvModule(
kernel_size1x1, kernel_size=(1,1), strides=(1,1)
)
self.conv2 = ConvModule(
kernel_size3x3, kernel_size=(3,3), strides=(1,1)
)
self.cat = tf.keras.layers.Concatenate()
def call(self, input_tensor, training=False):
x_1x1 = self.conv1(input_tensor)
x_3x3 = self.conv2(input_tensor)
x = self.cat([x_1x1, x_3x3])
return x
这里你可能会注意到,我们现在根据网络(图)对两个卷积层的确切的 内核大小 和 步长 数进行了硬编码。同样在conv module,中我们已经将 padding 设置为“same”,这样两者( self.conv1 和 self.conv2 )的特征图的尺寸将是相同的;这是将它们连接到末尾所需要的。
同样,在这个模块中,两个变量作为占位符执行,kernel _ size1x 1和kernel _ size3x 3。这是为了当然的目的。因为在整个模型的不同阶段,我们需要不同数量的特征地图。如果我们查看模型的图表,我们会看到inception module在模型的不同阶段采用不同数量的过滤器。
下采样模块
最后是下采样模块。下采样的主要直觉是,我们希望获得更相关的特征信息,这些信息高度代表模型的输入。因为它倾向于移除不需要的特征,以便模型可以集中于最相关的特征。有许多方法可以降低特征图(或输入)的维数。例如:使用 跨步 2 或使用常规 拼成 操作。池化操作有很多种,分别是: MaxPooling、AveragePooling、GlobalAveragePooling 。
从图中,我们可以看到下采样模块包含一个卷积层和一个最大池层,它们后来合并在一起。现在,如果我们仔细观察该图(右上角),我们会看到卷积层采用了一个 3 x 3 大小的滤波器,步长为 2 x 2 。而池层(此处 MaxPooling )取池大小 3 x 3 步长 2 x 2 。然而,公平地说,我们也需要确保来自它们中每一个的维度应该是相同的,以便最终合并。现在,如果我们还记得,当我们设计conv module 时,我们特意将 padding 参数的值设置为 *same* '。但在这种情况下,我们需要将其设置为有效`。
*class DownsampleModule(tf.keras.layers.Layer):
def __init__(self, kernel_size):
super(DownsampleModule, self).__init__()
# conv layer
self.conv3 = ConvModule(
kernel_size,
kernel_size=(3,3),
strides=(2,2),
padding="valid"
)
# pooling layer
self.pool = tf.keras.layers.MaxPooling2D(
pool_size=(3, 3),
strides=(2,2)
)
self.cat = tf.keras.layers.Concatenate()
def call(self, input_tensor, training=False):
# forward pass
conv_x = self.conv3(input_tensor, training=training)
pool_x = self.pool(input_tensor)
# merged
return self.cat([conv_x, pool_x])*
模型类:图层包含
一般来说,我们使用 层 类来定义内部计算模块,并将使用 模型 类来定义外部模型,实际上就是我们将要训练的对象。在我们的例子中,在一个 初始 模型中,我们定义了三个计算模块: Conv 模块、初始模块和下采样模块。这些是通过子类化 层 类创建的。所以接下来,我们将使用 模型 类来包含这些计算模块,以便创建整个 初始 网络。通常情况下, 模型 类具有与 层 相同的 API,但具有一些额外的功能。
与 层 类相同,我们将初始化 模型 类的 init 方法内的计算块如下:
*# the first conv module
self.conv_block = ConvModule(96, (3,3), (1,1))
# 2 inception module and 1 downsample module
self.inception_block1 = InceptionModule(32, 32)
self.inception_block2 = InceptionModule(32, 48)
self.downsample_block1 = DownsampleModule(80)
# 4 inception module and 1 downsample module
self.inception_block3 = InceptionModule(112, 48)
self.inception_block4 = InceptionModule(96, 64)
self.inception_block5 = InceptionModule(80, 80)
self.inception_block6 = InceptionModule(48, 96)
self.downsample_block2 = DownsampleModule(96)
# 2 inception module
self.inception_block7 = InceptionModule(176, 160)
self.inception_block8 = InceptionModule(176, 160)
# average pooling
self.avg_pool = tf.keras.layers.AveragePooling2D((7,7))*
每个计算模块的滤波器数量的数量根据模型的设计进行设置(也可在下图中看到)。在草签完所有的积木后,我们将根据设计(图)把它们连接起来。这里是使用模型子类的完整初始网络:
*class MiniInception(tf.keras.Model):
def __init__(self, num_classes=10):
super(MiniInception, self).__init__()
# the first conv module
self.conv_block = ConvModule(96, (3,3), (1,1))
# 2 inception module and 1 downsample module
self.inception_block1 = InceptionModule(32, 32)
self.inception_block2 = InceptionModule(32, 48)
self.downsample_block1 = DownsampleModule(80)
# 4 inception module and 1 downsample module
self.inception_block3 = InceptionModule(112, 48)
self.inception_block4 = InceptionModule(96, 64)
self.inception_block5 = InceptionModule(80, 80)
self.inception_block6 = InceptionModule(48, 96)
self.downsample_block2 = DownsampleModule(96)
# 2 inception module
self.inception_block7 = InceptionModule(176, 160)
self.inception_block8 = InceptionModule(176, 160)
# average pooling
self.avg_pool = tf.keras.layers.AveragePooling2D((7,7))
# model tail
self.flat = tf.keras.layers.Flatten()
self.classfier = tf.keras.layers.Dense(
num_classes, activation='softmax'
)
def call(self, input_tensor, training=False, **kwargs):
# forward pass
x = self.conv_block(input_tensor)
x = self.inception_block1(x)
x = self.inception_block2(x)
x = self.downsample_block1(x)
x = self.inception_block3(x)
x = self.inception_block4(x)
x = self.inception_block5(x)
x = self.inception_block6(x)
x = self.downsample_block2(x)
x = self.inception_block7(x)
x = self.inception_block8(x)
x = self.avg_pool(x)
x = self.flat(x)
return self.classfier(x)
def build_graph(self, raw_shape):
x = tf.keras.layers.Input(shape=raw_shape)
return Model(inputs=[x], outputs=self.call(x))*
你可能注意到了,除了 init 和 call 方法之外,我们还定义了一个自定义方法 build_graph 。我们使用它作为辅助功能,方便地绘制模型摘要信息。请查看本讨论了解更多详情。无论如何,让我们看看模型的总结。
*raw_input = (32, 32, 3)
# init model object
cm = MiniInception()
# The first call to the `cm` will create the weights
y = cm(tf.ones(shape=(0,*raw_input)))
# print summary
cm.build_graph(raw_input).summary()
# ---------------------------------------------------------------------
Layer (type) Output Shape Param #
=================================================================
input_6 (InputLayer) [(None, 32, 32, 3)] 0
_________________________________________________________________
conv_module_329 (ConvModule) (None, 32, 32, 96) 3072
_________________________________________________________________
inception_module_136 (Incept (None, 32, 32, 64) 31040
_________________________________________________________________
inception_module_137 (Incept (None, 32, 32, 80) 30096
_________________________________________________________________
downsample_module_34 (Downsa (None, 15, 15, 160) 58000
_________________________________________________________________
inception_module_138 (Incept (None, 15, 15, 160) 87840
_________________________________________________________________
inception_module_139 (Incept (None, 15, 15, 160) 108320
_________________________________________________________________
inception_module_140 (Incept (None, 15, 15, 160) 128800
_________________________________________________________________
inception_module_141 (Incept (None, 15, 15, 144) 146640
_________________________________________________________________
downsample_module_35 (Downsa (None, 7, 7, 240) 124896
_________________________________________________________________
inception_module_142 (Incept (None, 7, 7, 336) 389520
_________________________________________________________________
inception_module_143 (Incept (None, 7, 7, 336) 544656
_________________________________________________________________
average_pooling2d_17 (Averag (None, 1, 1, 336) 0
_________________________________________________________________
flatten_13 (Flatten) (None, 336) 0
_________________________________________________________________
dense_17 (Dense) (None, 10) 3370
=================================================================
Total params: 1,656,250
Trainable params: 1,652,826
Non-trainable params: 3,424*
现在,通过模型子类化构建整个 Inception 模型就完成了。
从头开始定制培训循环的端到端培训
现在我们已经建立了一个复杂的网络,是时候让它忙着学习一些东西了。我们现在可以简单地通过使用 编译 和 拟合 来轻松训练模型。但是这里我们将从头开始看一个定制的训练循环。这个功能是 TensorFlow 2 中新引入的。请注意,这个功能相对来说有点复杂,更适合深度学习研究者。
数据集
出于演示目的,我们将使用 CIFAR-10 数据集。先准备一下吧。
*(x_train, y_train), (x_test, y_test) = keras.datasets.cifar10.load_data()
print(x_train.shape, y_train.shape) # (50000, 32, 32, 3) (50000, 1)
print(x_test.shape, y_test.shape) # (10000, 32, 32, 3) (10000, 1)
# train set / data
x_train = x_train.astype('float32') / 255
# validation set / data
x_test = x_test.astype('float32') / 255
# target / class name
class_names = ['airplane', 'automobile', 'bird', 'cat', 'deer',
'dog', 'frog', 'horse', 'ship', 'truck']
plt.figure(figsize=(10,10))
for i in range(25):
plt.subplot(5,5,i+1)
plt.grid(False)
plt.imshow(x_train[i], cmap=plt.cm.binary)
plt.xlabel(class_names[y_train[i][0]])*

来自 cifar-10 的样本
这里我们将类向量(* y_train,y_test ) 转换为多类矩阵。我们还将使用 tf.data API 来获得更好、更高效的输入管道。*
*# train set / target
y_train = tf.keras.utils.to_categorical(y_train, num_classes=10)
# validation set / target
y_test = tf.keras.utils.to_categorical(y_test, num_classes=10)
# Prepare the training dataset.
train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train))
train_dataset = train_dataset.shuffle(buffer_size=1024).batch(batch_size)
# Prepare the validation dataset.
val_dataset = tf.data.Dataset.from_tensor_slices((x_test, y_test))
val_dataset = val_dataset.batch(batch_size)*
让我们快速检查一下标签转换和输入限幅后的数据形状:
*for i, (x, y) in enumerate(train_dataset):
print(x.shape, y.shape)
if i == 2:
break
for i, (x, y) in enumerate(val_dataset):
print(x.shape, y.shape)
if i == 2:
break
# output
(64, 32, 32, 3) (64, 10)
(64, 32, 32, 3) (64, 10)
(64, 32, 32, 3) (64, 10)
(64, 32, 32, 3) (64, 10)
(64, 32, 32, 3) (64, 10)
(64, 32, 32, 3) (64, 10)*
到目前为止一切顺利。我们有一个输入形状**32x32x3**和总共 10 类来分类。然而,将测试集作为验证集并不理想,但出于演示目的,我们不考虑train _ test _ split方法。现在,让我们看看 Tensorflow 2 中的自定义培训管道由哪些部分组成。
培训机制
在 TF。Keras ,我们有方便的训练和评估循环, 适合 , 评估 。但是我们也可以利用对培训和评估过程的低层次控制。在这种情况下,我们需要从头开始编写我们自己的训练和评估循环。以下是食谱:
- 我们为循环打开一个,它将迭代历元的数量。
- 对于每个历元,我们为循环打开另一个,它将批量迭代数据集( x , y )。
- 对于每一批,我们打开gradient tape()**范围。**
- 在这个范围内,我们称这个模型为正向传递,并计算损失。
- 在这个范围之外,我们检索关于损失的模型的权重的梯度。****
- 接下来,我们使用优化器基于梯度更新模型的权重。
TensorFlow 提供了 tf。GradientTape() 用于自动微分的 API,即计算计算相对于某些输入的梯度。
下面是其操作过程的简短演示。这里我们有一些输入( x )和可训练参数( w,b )。内 tf。GradientTape()** 范围,输出(【y】,基本上会是模型输出),损耗进行测量。在这个范围之外,我们检索权重参数相对于损失的梯度。**
**# x:input, w,b: trainable param - x*w + b
w = tf.Variable(tf.random.normal((3, 2)), name='w')
b = tf.Variable(tf.zeros(2, dtype=tf.float32), name='b')
x = [[1., 2., 3.]]
# Open a GradientTape to record the operations run
# during the forward pass, which enables auto-differentiation.
with tf.GradientTape(persistent=True) as tape:
y = x @ w + b # output from the forward pass (for the actual model)
# Compute the loss value for this minibatch.
loss = tf.reduce_mean(y**2)
# Calculate gradients with respect to every trainable variable
grad = tape.gradient(loss, [w, b])
grad
# output
[
<tf.Tensor: shape=(3, 2), dtype=float32, numpy=
array([[ -5.2607636, 1.4286567],
[-10.521527 , 2.8573134],
[-15.782291 , 4.28597 ]], dtype=float32)>,
<tf.Tensor: shape=(2,), dtype=float32,
numpy=array([-5.2607636, 1.4286567], dtype=float32)>
]**
现在,让我们相应地实现定制的培训方案。
**for epoch in range(epochs): # <----- start for loop, step 1
# <-------- start for loop, step 2
# Iterate over the batches of the dataset.
for step, (x_batch_train, y_batch_train) in enumerate(train_dataset):
# <-------- start gradient tape scope, step 3
# Open a GradientTape to record the operations run
# during the forward pass, which enables auto-differentiation.
with tf.GradientTape() as tape:
# Run the forward pass of the layer.
# The operations that the layer applies
# to its inputs are going to be recorded
# on the GradientTape.
logits = model(x_batch_train, training=True) <- step 4
# Compute the loss value for this minibatch.
loss_value = loss_fn(y_batch_train, logits) <- step 4
# compute the gradient of weights w.r.t. loss <-------- step 5
# Use the gradient tape to automatically retrieve
# the gradients of the trainable variables with respect to the loss.
grads = tape.gradient(loss_value, model.trainable_weights)
# update the weight based on gradient <---------- step 6
# Run one step of gradient descent by updating
# the value of the variables to minimize the loss.
optimizer.apply_gradients(zip(grads, model.trainable_weights))**
太好了。然而,我们仍然没有讨论如何添加指标来监控这个定制的训练循环。显然,我们也可以在训练循环中使用内置指标甚至自定义指标。在训练循环中添加指标相当简单,流程如下:
- 每批后调用metric . update _ state()****
- 当我们需要显示度量的当前值时,调用metric . result()****
- 调用metric . reset _ States()****
这里还有一件事要考虑。TensorFlow 2.0 中的默认运行时是急切执行。上面的训练循环正在急切地执行。但是如果我们想要进行图形编译,我们可以用@ TF . functiondecorator 将任何函数编译成静态图。这也加快了训练的速度。下面是用@ TF . functiondecorator 对训练和评估函数的设置。
**@tf.function
def train_step(x, y):
'''
input: x, y <- typically batches
return: loss value
'''
# start the scope of gradient
with tf.GradientTape() as tape:
logits = model(x, training=True) # forward pass
train_loss_value = loss_fn(y, logits) # compute loss
# compute gradient
grads = tape.gradient(train_loss_value, model.trainable_weights)
# update weights
optimizer.apply_gradients(zip(grads, model.trainable_weights))
# update metrics
train_acc_metric.update_state(y, logits)
return train_loss_value
@tf.function
def test_step(x, y):
'''
input: x, y <- typically batches
return: loss value
'''
# forward pass, no backprop, inference mode
val_logits = model(x, training=False)
# Compute the loss value
val_loss_value = loss_fn(y, val_logits)
# Update val metrics
val_acc_metric.update_state(y, val_logits)
return val_loss_value**
这里我们看到了metrics . update _ state()的用法。这些函数返回到训练循环,在这里我们将设置显示日志消息、【metric . result()、以及重置指标、metric . reset _ States()。
这是我们最不喜欢设置的东西,TensorBoard。有一些很棒的功能可以利用,如:显示每批样本+混淆矩阵,超参数调整,嵌入投影仪,模型图等。目前,我们将只关注记录 it 上的培训指标。很简单,但我们将把它集成到自定义培训循环中。所以,我们不能使用TF . keras . callbacks . tensor board而是需要使用 TensorFlow 汇总 API 。 tf.summary 模块提供了在 TensorBoard 上写入汇总数据的 API。我们希望在每个批处理操作之后写入日志记录状态,以获得更多详细信息。否则,我们可能更喜欢在每个时期结束时。让我们创建一个保存 事件 消息的目录。在工作目录中,创建 日志/训练 和 日志/测试 。下面是完整的培训管道。我们建议首先通读代码,以便了解整个培训流程。
**# Instantiate an optimizer to train the model.
optimizer = tf.keras.optimizers.Adam()
# Instantiate a loss function
loss_fn = tf.keras.losses.CategoricalCrossentropy()
# Prepare the metrics.
train_acc_metric = tf.keras.metrics.CategoricalAccuracy()
val_acc_metric = tf.keras.metrics.CategoricalAccuracy()
# tensorboard writer
train_writer = tf.summary.create_file_writer('logs/train/')
test_writer = tf.summary.create_file_writer('logs/test/')
@tf.function
def train_step(step, x, y):
'''
input: x, y <- typically batches
input: step <- batch step
return: loss value
'''
# start the scope of gradient
with tf.GradientTape() as tape:
logits = model(x, training=True) # forward pass
train_loss_value = loss_fn(y, logits) # compute loss
# compute gradient
grads = tape.gradient(train_loss_value, model.trainable_weights)
# update weights
optimizer.apply_gradients(zip(grads, model.trainable_weights))
# update metrics
train_acc_metric.update_state(y, logits)
# write training loss and accuracy to the tensorboard
with train_writer.as_default():
tf.summary.scalar('loss', train_loss_value, step=step)
tf.summary.scalar(
'accuracy', train_acc_metric.result(), step=step
)
return train_loss_value
@tf.function
def test_step(step, x, y):
'''
input: x, y <- typically batches
input: step <- batch step
return: loss value
'''
# forward pass, no backprop, inference mode
val_logits = model(x, training=False)
# Compute the loss value
val_loss_value = loss_fn(y, val_logits)
# Update val metrics
val_acc_metric.update_state(y, val_logits)
# write test loss and accuracy to the tensorboard
with test_writer.as_default():
tf.summary.scalar('val loss', val_loss_value, step=step)
tf.summary.scalar(
'val accuracy', val_acc_metric.result(), step=step
)
return val_loss_value
# custom training loop
for epoch in range(epochs):
t = time.time()
# batch training
# Iterate over the batches of the train dataset.
for train_batch_step, (x_batch_train, \
y_batch_train) in enumerate(train_dataset):
train_batch_step = tf.convert_to_tensor(
train_batch_step, dtype=tf.int64
)
train_loss_value = train_step(
train_batch_step,
x_batch_train, y_batch_train
)
# evaluation on validation set
# Run a validation loop at the end of each epoch.
for test_batch_step, (x_batch_val, \
y_batch_val) in enumerate(val_dataset):
test_batch_step = tf.convert_to_tensor(
test_batch_step, dtype=tf.int64
)
val_loss_value = test_step(
test_batch_step, x_batch_val, y_batch_val
)
template = '
ETA: {} - epoch: {} loss: {} acc: {} val loss: {} val acc: {}\n
'
print(template.format(
round((time.time() - t)/60, 2), epoch + 1,
train_loss_value, float(train_acc_metric.result()),
val_loss_value, float(val_acc_metric.result())
))
# Reset metrics at the end of each epoch
train_acc_metric.reset_states()
val_acc_metric.reset_states()**
瞧啊。我们用 RTX 2070 在本地系统中运行代码。通过启用,我们能够将批次大小增加到 256 。以下是日志输出:
**ETA: 0.78 - epoch: 1 loss: 0.7587890625 acc: 0.5794399976730347 val loss: 3.173828125 val acc: 0.10159999877214432
ETA: 0.29 - epoch: 2 loss: 0.63232421875 acc: 0.7421200275421143 val loss: 1.0126953125 val acc: 0.5756999850273132
ETA: 0.32 - epoch: 3 loss: 0.453369140625 acc: 0.8073400259017944 val loss: 0.7734375 val acc: 0.7243000268936157
ETA: 0.33 - epoch: 4 loss: 0.474365234375 acc: 0.8501200079917908 val loss: 0.64111328125 val acc: 0.7628999948501587
..
..
ETA: 0.35 - epoch: 17 loss: 0.0443115234375 acc: 0.9857199788093567 val loss: 1.8603515625 val acc: 0.7465000152587891
ETA: 0.68 - epoch: 18 loss: 0.01328277587890625 acc: 0.9839400053024292 val loss: 0.65380859375 val acc: 0.7875999808311462
ETA: 0.53 - epoch: 19 loss: 0.035552978515625 acc: 0.9851599931716919 val loss: 1.0849609375 val acc: 0.7432000041007996
ETA: 0.4 - epoch: 20 loss: 0.04217529296875 acc: 0.9877399802207947 val loss: 3.078125 val acc: 0.7224000096321106**
过度拟合!但是现在没关系。为此,我们只需要考虑一些细节,如图像增强、学习速率表等。在工作目录中,对实时 tensorboard 运行以下命令。在下面的命令中, logs 是我们手动创建的保存事件日志的文件夹名。
**tensorboard --logdir logs**
保存并加载
根据我们使用的 API,有各种各样的方法来保存 TensorFlow 模型。模型子类化中的模型 保存 和 重新加载 与顺序或功能 API 中的不一样。它需要一些特别的关注。目前有两种格式存储模型: SaveModel 和 HDF5 。来自官方文件:
HDF5 和 SavedModel 的关键区别在于,HDF5 使用对象配置来保存模型架构,而 SavedModel 保存执行图。因此,SavedModels 能够保存自定义对象,如子类模型和自定义层,而不需要原始代码。
因此,看起来 SavedModels 能够保存我们的定制子类模型。但是如果我们想要 HDF5 格式的自定义子类模型呢?根据医生的说法。我们也能做到,但是我们需要一些额外的东西。我们必须在我们的对象中定义 get_config 方法。并且在加载模型时还需要将对象传递给custom _ object参数。此参数必须是字典映射:TF . keras . models . load _ model(path,custom _ objects = { ' custom layer ':custom layer })。然而,似乎我们现在不能使用 HDF5,因为我们没有在自定义对象中使用 get_config 方法。然而,在自定义对象中定义这个函数实际上是一个很好的做法。如果需要的话,这将允许我们稍后容易地更新计算。**
但是现在,让我们保存模型并用 SavedModel 格式重新加载它。
**model.save('net', save_format='tf')**
之后,它会在工作目录下创建一个名为 net 的新文件夹。它将包含 资产 ,saved _ model . Pb, 变量 。模型架构和训练配置,包括优化器、损失和指标,存储在 saved_model.pb 中。权重保存在变量目录中。**
当保存模型及其层时, SavedModel 格式存储类名、调用函数、损失和权重(以及配置,如果实现的话)。调用函数定义模型/层的计算图。在没有模型/层配置的情况下,调用函数用于创建一个像原始模型一样存在的模型,该模型可以被训练,评估,并用于推理。稍后,为了重新加载保存的模型,我们将执行以下操作:
**new_model = tf.keras.models.load_model("net", compile=False)**
设置compile = False是可选的,我这样做是为了避免警告日志。此外,由于我们正在进行自定义循环训练,我们不需要任何编译。
到目前为止,我们已经讨论了保存整个模型(计算图和参数)。但是,如果我们只想保存训练重量并在需要时重新加载重量。是的,我们也能做到。简单地说,
**model.save_weights('net.h5')**
这将减轻我们模型的重量。现在,当需要重新加载时,有一点需要记住。在尝试加载权重之前,我们需要调用 构建 方法。它主要初始化子类模型中的层,以便可以构建计算图。例如,如果我们尝试如下:
**new_model = MiniInception()
new_model.load_weights('net.h5')
--------------------------------------
ValueError: Unable to load weights saved in HDF5 format
into a subclassed Model which has not created its variables yet.
Call the Model first, then load the weights.**
要解决这个问题,我们可以做如下工作:
**new_model = MiniInception()
new_model.build((None, *x_train.shape[1:])) # or .build((x_train.shape))
new_model.load_weights('net.h5')**
它将成功加载。这里有一篇关于在 TF 中保存和序列化模型的很棒的文章。弗朗索瓦·乔莱的作品,必读。
评估和预测
尽管不是必须的,让我们通过测量模型性能来结束。 CIFAR-10 级标号图如下: 0: 飞机, 1: 汽车, 2 : 鸟, 3 : 猫, 4 : 先找分类报告吧。
**Y_pred = model.predict(x_test, verbose=1)
y_pred = np.argmax(Y_pred, axis=1)
target_names = [
'airplane', 'automobile', 'bird', 'cat', 'deer',
'dog', 'frog', 'horse', 'ship', 'truck'
]
classification_report(
np.argmax(y_test, axis=1),
y_pred,target_names=target_names
)
#--------------------------------------------------------------------
precision recall f1-score support
airplane 0.81 0.79 0.80 1000
automobile 0.76 0.95 0.85 1000
bird 0.78 0.65 0.71 1000
cat 0.36 0.92 0.51 1000
deer 0.94 0.49 0.65 1000
dog 0.87 0.46 0.60 1000
frog 0.97 0.52 0.68 1000
horse 0.89 0.69 0.78 1000
ship 0.83 0.90 0.86 1000
truck 0.90 0.81 0.85 1000
accuracy 0.72 10000
macro avg 0.81 0.72 0.73 10000
weighted avg 0.81 0.72 0.73 10000**
接下来,多级 ROC AUC 得分:
**def multiclass_roc_auc_score(
y_test, y_pred, average="macro"
):
lb = LabelBinarizer()
lb.fit(y_test)
y_test = lb.transform(y_test)
y_pred = lb.transform(y_pred)
return roc_auc_score(y_test, y_pred, average=average)
multiclass_roc_auc_score(y_test,y_pred)
# ---------------------------------------------------
#output: 0.8436111111111112**
混淆矩阵:
**Y_pred = model.predict(x_test, verbose=2)
y_pred = np.argmax(Y_pred, axis=1)
cm = confusion_matrix(np.argmax(y_test, axis=1), y_pred)
cm = pd.DataFrame(cm, range(10),range(10))
plt.figure(figsize = (10,10))
sns.heatmap(cm, annot=True, annot_kws={"size": 12}) # font size
plt.show()**

cifar_10 混淆矩阵
****对新样本的预测/推断:
**target_names = [
'airplane', 'automobile', 'bird', 'cat', 'deer',
'dog', 'frog', 'horse', 'ship', 'truck'
]
# Give the link of the image here to test
test_image1 = image.load_img(image_file, target_size=(32,32))
test_image = image.img_to_array(test_image1)
test_image = np.expand_dims(test_image, axis =0)
prediction = model.predict(test_image)[0]
label_index = np.argmax(prediction)
target_names[label_index]**
尾注
这件事到此为止。非常感谢你阅读这篇文章,希望你们喜欢。文章有点长,这里简单总结一下。
我们先对比一下 TF。Keras 建模 API。接下来,我们使用 模型子类 API 来逐步构建一个小型初始网络。然后我们用 GradientTape 来看看 TensorFlow 2 中新引入的自定义循环训练的流程。我们还端到端地训练了子类化的 Inception 模型。最后,我们讨论自定义模型保存和重新加载,然后测量这些经过训练的模型的性能。
有趣的阅读
- py torch 中的亲笔签名和 TensorFlow 2 中的 gradient tape
- tensor flow 2[Keras]中的多输入多输出建模
- 选择损失和指标
- 在喀拉斯重装最佳关卡
- Keras 致密层 vs PyTorch 线性层
- Keras 建模 API 与顺序 API
- Keras 模型。预测比 NumPy 慢!
- 在 Keras 中实现 grad cam
- LSTM/GRU Keras 关注层
- TensorFlow 数据 API vs Numpy 数组
- 神经网络&二元分类指导
- 用 tf 进行量化感知训练。梯度胶带
- Keras 中 self.add_loss 函数的用途
使用 Python 模拟您所在地区的新冠肺炎-超越 SIR 模型
结合基于个人的模拟和概率框架的详细和准确的建模,以及如何在几个步骤中建立自己的模拟

图片由皮克斯拜的 Gerd Altmann 提供
疫情会持续多久?我们需要保持多长时间的社交距离?如果解除了呢?如果什么都没做呢?
一个月前,在医生和数据科学家的帮助下,我用 Python 创建了一个模型,以预测圣保罗大都市地区冠状病毒疫情的演变,并回答上述问题。虽然有些人对预测的情景持怀疑态度,但我现在对预测的准确性感到震惊。这个模型已经得到了改进和开源,这就是为什么我想和你分享如何使用它,为任何你想建模的地区。
关于我早期的预测
圣保罗大都市区新冠肺炎预测死亡人数

红线代表悲观的情况。蓝色的,乐观。星星,后来加上,显示官方死亡人数。预测是在 3 月 30 日计算的。
当进行预测时,我们预计死亡的确认会有一些延迟,但这还不是模型的一部分。那时,在官方统计数据出来之前,我们仍然从医院的报告中得知死亡人数。因此,我们预计官方数据会像预测的那样,向右移动几天。
该模型在说:我不知道长期会发生什么,但未来 2-3 周,任何情景看起来都一样。如果我们对疾病的建模是正确的,那么毫不奇怪我们在这三周内是正确的。在过去的 10 天里,曲线的倾斜度似乎在两种情况的中间。
用模型回答问题
当前的模型可以处理关于流行病如何表现的不确定性,并且【学习】来自观察。没有人确切知道这种疾病的传染性如何,或者人们的行为如何。图表中投影周围的阴影区域代表了这种不确定性。在学习时,模型识别哪些假设组合可以解释现实,哪些不能。
尽管如此,我们还是可以开始提问。
如果… 截止到 4 月 20 日有封锁怎么办?(在圣保罗,呆在家里只是一个建议,一些数据表明 40%的人每天不呆在家里)

封锁之后,重症监护室的入住率大约需要 24 天才能降至初始水平以下。如果在封锁前没有社交距离,这个时间可能会更长。
如果……医院外面没人戴口罩怎么办?假设病人戴上后有 40%的效果,易感者有 20%的效果,我们预计如果没有人使用,病毒的传播会更严重。

虽然不能控制疫情,但在户外使用口罩可能会减少传染,并有助于使曲线变平。
什么是… 社会距离感的效果?让我们来看看模型预期的生殖数(每个感染者感染的人数)在里约热内卢是如何表现的:

每个收入水平的每日生育数。在所有的社会阶层中,生殖数量在三月中旬显著下降,那时社会距离在里约热内卢开始。
正如所料,社交距离在减少传播方面非常有效。负担不起呆在家里的费用的人,或者甚至没有家可住的人,不能有效地隔离,正如较高的生殖数量所显示的那样。
但是等等…为什么对于收入很低的来说,再生号有更高的起点( R0 )?其他社会阶层从 3.4 开始,极低收入从 4.0 开始。这种不明显的行为有一个简单的解释:正如模型正确指出的那样,更多的人生活在一起增加了在家里传播的机会。
最后, 为什么… 死亡人数在 4 月中旬加速上升如此之快,到那时似乎下降了,然后又再次上升?(此事发生在圣保罗,解说解释了纽约等地的类似模式)
考虑到诊断和死亡确认的延迟,以及它们如何随时间变化,可以解释在官方数字中看到的不明显的行为。我们预计,由于缺乏检测能力,实际死亡人数在 4 月中旬之前的许多周都高于官方统计数字。当测试赶上时,事情似乎会在几天内失去控制。

模拟的每日确诊死亡人数(红色)和每日实际死亡人数(蓝色)。测试能力的增加导致了 4 月中旬官方数字的激增——圣保罗大都市地区的预测。
关于巴西的模拟,可以在这里找到详细的历史数据。对于世界数据来说,这个 Kaggle 数据集可以是一个好的开始。
模型是如何构建的
(如果你对 Python 建模不感兴趣,你可以跳到靠近末尾的图表,带有对纽约州的预测)
当我在圣保罗做模特时,有一件事从一开始就很清楚:社会不平等很重要。所以我知道这个模型会比之前的大多数模型更复杂。使用时间序列模型是无助的,因为在曲线上领先的国家有非常不同的社会。建立一个房室模型 (SIR,SEIR 之类的),预先定义状态,并详细说明转换的微分方程,由于要建模的许多重要方面,将是不可行的。一个基于个人的模拟正在进行。
通过使用 Simpy ,很容易对疾病的任意特征或人们的行为进行建模。不需要预先知道什么是状态转换,甚至不需要知道有多少可能的状态,只需要知道状态变量和控制它们的逻辑。
简而言之,我们随机创建一个群体,根据其亚群体的特征,我们指定一些种子感染,并让个体相互作用。所有关于人们如何互动的规则,病人会有什么结果,疾病的每个阶段会持续多长时间,所有这些都可以被明确编程。一个事件可以触发并行逻辑,彼此独立运行,具有独立的随机性。这就像编写异步代码,将在模拟时间同步运行。
Python 很棒,但是当对一种可能对一小部分人群产生影响的疾病建模时,需要模拟大量的个体。10.000 个人,内存消耗和 CPU 使用率直线上升。为了解决这个问题,每个个体都变成了 Cython 中的一个对象,代码几乎没有变化,但可以模拟 1000000 个个体,消耗大约 1GB RAM,性能大幅提升。
然后,对疾病演变和传播进行了详细建模。大多数参数来自以下两个来源:
- 【2019 年冠状病毒疾病严重程度的估计:基于模型的分析
- 流行病计算器 —包含几个疾病参数的估计值。
该模型的当前源代码可以在这里找到:
[## ArroyoAndre/covidsimulation
Covid 流行病模拟 python 和 cython 中基于个体的动态模型,带有 Simpy“所有模型都是错误的,但是…
github.com](https://github.com/ArroyoAndre/covidsimulation)
模拟一个地区的疫情
安装
首先,让我们克隆回购,建立一个虚拟环境,并安装模拟代码。(安装将下载需求,编译 Cython 代码,并将所有内容作为一个包安装)
git clone [https://github.com/ArroyoAndre/covidsimulation.git](https://github.com/ArroyoAndre/covidsimulation.git)cd covidsimulation/python3 -m venv venv. venv/bin/activate./setup.sh
然后,启动 Jupyter:
jupyter notebook
配置您的区域
在笔记本中,让我们从导入开始:
现在,我们将定义一个群体。为了真实地模拟一个地区,您可以定义几个人口(例如,城市和乡村、富人和穷人、普通人口和基本服务人员……)
我们将模拟纽约州。提供数字是为了教学目的。(例如,我发明了年龄分布和房屋大小)
我们还需要指定干预。我们将使用两种干预措施。3 月 10 日,学校停课,社交聚会被禁止,3 月 22 日,家庭订单被取消。4 月 10 日和 12 日,我们将使用诊断延迟变更干预来减少测试结果的延迟,以解决几天后报告的死亡人数激增的问题。
等等…为什么我在 10 天开始诊断 _ 延迟,然后减少到 5 天?似乎合理的是,从收集检查结果,送到实验室,测试,结果出来,到死亡报告,总共需要大约 5 天时间。根据维基百科的数据,在 4 月 10 日到 4 月 15 日期间,纽约每天大约有 700 人死亡。在 16 日和 17 日,死亡人数比之前的平均值多 3500 人,然后回到之前的趋势。所以我们可以假设有大约 5 天的死亡等待确认,最终得到确认。因此之前的 10 天延迟。
现在,我们需要将所有这些包装到一个区域的参数对象中。
由于感染人数很少,疫情发展的时间可能会有很大差异,我们将所有模拟都放在 D0 中,定义为感染人数达到阈值的日期。我们将把 D0 定义为 3 月 10 日(因为在此之前发生的事情不多)。因为我们不知道那时有多少人被感染,我们把它设为一个在 10k 到 200k 之间均匀分布的随机参数。
校准我们的模型
我们在模型中加入了两个不确定的参数。其他一些,如来自感染者的传播率,也是未知的,或者可能因地区而异,默认为随机参数。这就是为什么我们需要校准我们的模型。
通常,我们可以用来校准的偏差较小的变量是官方的死亡人数。可能会有一些系统性的漏报和系统延迟,但这比其他偏见要好。我们已经假设病例确认的平均延迟从 10 天开始,到 4 月中旬变为 5 天,所以我们应该没问题。
我们将创建一个得分函数,为模拟提供更低的损失值,以更好地匹配特定日期的官方死亡人数:
然后,我们将开始校准过程。首先,我们将使用 100,000 个个体的小群体,尝试模型内部参数值的几种组合。(为此,我使用了一个具有 32 个 CPU 内核的虚拟实例。在笔记本电脑上,可能需要一段时间……)我们将进行 1000 次试验,并保留 100 个最佳结果( p=0.1 )。
其次,我们将在 100 万人的群体中尝试这 100 个最佳,以减少分离最佳时的噪音。然后我们会保留 16 个最好的。
那些随机状态是模型中所有不确定参数联合分布的后验。通过适当的命名,可以使用一个区域的后验作为另一个区域的前验,以获得精确的后验。
(终于!)运行模拟并绘制结果
在花了这么多时间校准之后,你可能想保存目前为止找到的随机状态****。(如何使用泡菜做这件事超出了这篇文章的目的,但是鼓励你去谷歌一下)
我们现在可以进行真正的模拟,并预测未来的事情。我们将选取找到的 16 个最佳随机状态,并在 200 万个体的群体中分别应用两次。同样,我们将保留最符合我们校准数据的 16 个模拟(其中 0.5 个)。
让我们画出已确认的死亡人数,来做第一次检查:

该模型用截至 3 月 19 日的真实数据训练,预测死亡人数在 5 月初趋于稳定。请注意 4 月中旬死亡人数的正确预测。
和总死亡数,没有确认延迟,以对数标度:

我们还可以满足我们的好奇心,看看我们的随机参数学习了哪些值。

尽管对数先验范围在 1 万到 20 万之间,但我们的模型认为 3 月 10 日更有可能有大约 7 万人已经被感染。不同的先验可能导致不同的结论,除非我们可以观察到足够的数据来减少其他参数的不确定性。
可以看到实现的指标:
列出实施的指标的输出
死亡率呢?没有一个衡量标准,但是我们可以创建一个。

该中心对预测感染死亡率的估计接近 0.5%,与最近通过血清学检测对已感染人群的估计一致。60-69 岁年龄组的 IFR 可能高达 3%。
尽管这是出于教学目的的快速建模(抱歉,如果你用的是笔记本电脑),但结果却非常真实。
经验之谈
- 良好的前科是金。在许多情况下,避免统一参数,即使用不同的分布来代替它们,也可以在只能学习过去的模型和能够真正预测未来不明显行为的模型之间产生差异。请记住:
进→出
- 进行许多模拟(至少从 10k random_states 开始),并用大量人群(200 万用于演示目的)进行模拟也可以显著提高准确性。
- 向评分函数添加不同类型的度量可以使更多的输出变量更加准确。
如果你走到这一步…
这里没有探讨更多的功能,比如研究卫生措施或使用口罩的效果。也有可能模拟不同的社会距离情景,包括间歇性距离,并考虑到卫生系统的能力。
我希望你可以很好地利用这个模型,或者告诉人们你所在地区的预测和情景,或者只是了解它是如何构建的,以及如何用 Python 做更酷的事情。
邀请与您合作改进模型,为新区域建模,添加新功能,并分享您的成果。
编者按: 走向数据科学 是一份以数据科学和机器学习研究为主的中型刊物。我们不是健康专家或流行病学家,本文的观点不应被解释为专业建议。想了解更多关于疫情冠状病毒的信息,可以点击 这里 。
用于大规模分类的模型树分类器
一种层次分类的方法。
L 大规模分类问题是目标标签数量多但确定的用例,不仅在规模方面,而且在实现许多有用的机器学习算法方面都提出了挑战,否则这些算法可以方便地解决许多用例。值得仔细研究一下标签,看看它们是否可以按层次排列,或者是否有某种提供这种层次的分类法。最大似然空间中一个流行的用例是图像识别,其中目标标签的数量可能非常高,以百万计。
建立一个具有 RNN 层或神经网络集群的整体模型可能是一种选择。一些人认为一组或一系列 LSTMs 可以解决这个问题。
但是也有一些根本性的缺陷
- LSTM 神经网络和多层神经网络固有的训练和分类速度慢
-如果我们需要将每一层连接到分类法或层次结构中的某个分类级别,则神经元和层数之间不可能存在真实的相关性。
-像决策树算法这样的其他算法并不健壮,看起来更像是逻辑推导,而随机森林是一种集成算法,更多的是在黑暗中射击,偶然击中目标。
-分类标签的绝对数量使得训练算法和优化器不能收敛,尽管有大量好的训练例子。
需要一个现实的分布式模型来解决工程和算法方面的问题,并处理分类对象的规模。标签,在图像分类的情况下,在层级的叶级别上可能达到数百万。
H 分层模型树,而不是分层建模(单个整体模型),是一种稳健的方法,用于从顶层根节点的单个模型和沿层级向下的每个级别的模型集群开始,对层级的每个级别的项目进行分类。
-每个模型都有自己的算法。根模型可以是朴素贝叶斯分类器,而第二层模型可以是具有卷积的神经网络(NN/CNN)
一些模型可以采用 RNN 和 LSTMs,并且可以是其自身的微神经网络,但是模型本身将表示模型树中的一个节点。
-前提是每个模型应分类到一个指定的标签中,并且在下一个级别存在一个具有子类别并满足分类类别的模型。
过度工程化可能是这种方法的潜在负面,人们倾向于构建一个非常离散的层次结构,并且会遇到构建大量模型的开销。在层次结构的每一层需要多少个模型,它们覆盖或处理什么不同的方面,更多的是工程或功能上的考虑。

在上面的例子中,在图像识别的情况下,如果顶层模型,根模型,将照片分类为不同的类别,汽车、动物、植物/树木、风景图片(山脉和河流)等。第二层将为每个对象建立一个模型。例如,动物的 2 级模型可以将动物分类为哺乳动物、鸟类、鱼类等。或者它可以是更离散的,如野生动物、家畜、多种鸟类、不同类型的鱼等。,这取决于我们希望在这一级拥有的目标标签的数量。
但是,这种方法有一定的成本。那是贴标签。每个示例或训练数据集都应该被标记,以匹配层次结构的每个级别的至少一个模型中的一个标签。例如,如果我们要训练一个狗的图像,上面例子中的根模型只能归类为汽车、动物等抽象标签,所以狗的图像也应该被标记为动物;此外,在第二级,这个狗的形象必须被标记为哺乳动物,以训练与动物模型。如果第二级模型更离散,如野生动物、家畜等。就像上面的例子一样,狗也需要贴上家畜的标签。因此,无论哪种方式,建立一个层级都有其负面影响。
如果我们不得不处理更复杂的图片,例如,一张北极熊猎鱼的图片,这种方法可能会变得复杂。这是多标签分类的情况,其中训练示例可能沿着树流经 2 个或更多路径。
另外,图像分类可以是上下文相关的,例如,在较早的北极熊的例子中,上下文是“狩猎”。这可以被视为图像分类的另一个维度和完全不同的上下文层次树。如果分类正确,最终结果可能是这棵树上的另一个标签,即狩猎。
随后,将有一个合并方面来合并多个标签,并为图像提供一个有创意的名称,在本例中为“北极熊猎鱼”。然而,这种整合不是本文的一部分,我们仅限于“使用模型层次结构或模型树进行分类”
变量,自动质量保证和校正:
在分类期间,所讨论的对象可能沿着树的不同路径向下遍历。这里有许多选择/导出正确分类的考虑因素。
- 如果第一级分类将对象归类为哺乳动物,则子节点中的模型应该与哺乳动物及其子类对齐。
- 可以考虑在一个模型下混合两个或更多不同的子类别/标签,例如,在上述例子中的家养和野生动物。这个孩子模型将包括许多家养和野生动物的变种;在分类期间,如果第一级将“牛”分类为“家畜”,下一级模型应该能够从家畜类别中对对象进行分类,比如说牛;如果下一个级别将它归类为“猫科动物”(狮子),那么结果会变得不对齐。
- 这就是验证和反馈成为关键的地方。本例的分类应该到此为止,分类摘要应该报告哪些内容可以有把握地进行分类。
- 在一些用例中,在这个例子中,基于 1 级模型的置信度,分类可以将这个对象分类为仅仅是哺乳动物。
- 一些情况下可以采用“最一致的分类”方法,例如,级别 1 将奶牛分类为动物(从人、事物、动物等中)。),2 级分类为哺乳动物(来自哺乳动物、鸟类、爬行动物等。)、3 级家畜(来自家养、野生等。)并且第 4 级分类为狮子(从母牛、山羊、鹿、大象、狮子中)。此处,分类应在第 3 级结束。
- 用母前缀标记标签是检查分类一致性的一种方式,例如,如果哺乳动物编码为“ma”,家养和野生动物分别编码为“mado”和“mawi”。
自动校正的好处:
在每个子级别分类并拥有 2 个或更多子类别(不相关)的好处是确保我们不会将错误从顶级传播到下一级。如果级别 2 模型被训练成处理家畜的所有子类和植物的所有子类,则好的模型算法应该对牛的图像进行分类,牛被分类为级别 1 的家畜到级别 2 的牛。如果 2 级模型将奶牛分类到植物的一个子类中,那么结果是不对齐的,我们不会进一步分类,因为这是错误分类。我们致力于用这种用例扩充第 2 级和第 1 级模型,并进行错误修正。
所以如果误差传播没有出现。在一些变体中,如果 1 级模型被训练为随着时间推移而变得健壮,我们仍然可以将该示例分类到 1 级模型的输出,并将其标记为“抽象分类”,因为我们没有得到细节。
在一些变体中,1 级、2 级、3 级模型对齐,而 4 级模型不对齐,然后我们给出前 3 级分层模型与特定类别和子类别对齐的好处,并在 3 级停止分类。这还是不错的。
构建这个层次模型的关键是建立一个良好的子类别和进一步子类别的组合,这样我们就可以确定我们是在正轨上还是停了下来。
另一个方面是当我们向下传播时处理更小的数据集。随着我们向下传播,可用于针对特定标签集构建模型的数据量变得更少。但是这里有几件事需要注意。
- 我们可以采用一种分类器算法,该算法在训练样本较少的情况下仍能很好地工作。模型算法不能用较少的训练样本进行分类不是模型树分类器的问题。
- 这样的模型树分类器不受影响,因为它仍然可以向下渗透到更小的细节,并且仍然可以尝试将示例分类到抽象类别。清晰的比对逻辑/评分应注意误差传播。
结论:
这项工作的第一个方面是查看分层结构中的目标标签并构建标签树。这也将有助于可视化分类问题和理解问题空间。
第二步是针对每个示例定义或识别标签,以匹配层次结构,因为这是一种核心的监督学习方法。
步骤 3 是使不同的 ML 模型沿树向下分层,每个节点一个模型,并建立分类聚类。
步骤 4 是实现自动校正和处理错误传播,以获得准确的分类,而不一定是详细的分类。
Python 中的模型验证
应用训练/测试分割、折叠,并省略一个

Scikit-learn 是一个开源的机器学习库,提供了构建、训练和测试模型的工具。模型选择模块具有许多对模型测试和验证有用的功能。在本帖中,我们将讨论 scikit-learn 中一些重要的模型选择功能。
我们开始吧!
出于我们的目的,我们将使用葡萄酒评论数据集,该数据集可以在这里找到。
首先,让我们将数据读入熊猫数据框:
import pandas as pd
df = pd.read_csv("winemag-data-130k-v2.csv")
接下来,让我们打印前五行数据:
print(df.head())

让我们考虑基于品种、酒厂、国家和评论点预测葡萄酒价格是否比 50 美元更贵的任务。我们可以构建一个随机的森林分类器来执行这项任务。首先,让我们将分类特征转换成可由随机森林处理的分类代码:
df['country_cat'.format(i)] = df['country'].astype('category').copy()
df['country_cat'.format(i)] = df['country_cat'.format(i)].cat.codesdf['winery_cat'.format(i)] = df['winery'].astype('category').copy()
df['winery_cat'.format(i)] = df['winery_cat'.format(i)].cat.codesdf['variety_cat'.format(i)] = df['variety'].astype('category').copy()
df['variety_cat'.format(i)] = df['variety_cat'.format(i)].cat.codes
让我们也估算缺失值。我们在这里不做任何花哨的估算,但是看看用 Python 预测缺失值的,这是一种更可靠的估算方法。这里,让我们用 0 替换丢失的值:
df.fillna(0, inplace=True)
接下来,让我们根据价格值是否大于或等于$50,为它们分配二进制标签。超过 50 美元的葡萄酒有“1”的标签,否则为“0”:
df['price_class']=np.where(df['price']>=50,1,0)
现在,让我们定义我们的功能和目标:
X = np.array(df[['country_cat', 'winery_cat', 'variety_cat', 'points']])
y = np.array(df['price_class'])
训练/测试分割
我们将讨论的第一个函数是“train_test_split()”方法。这个函数将数组或矩阵分成随机训练和测试子集。让我们从 scikit-learn 导入这个函数:
from sklearn.model_selection import train_test_split
为了将我们的功能划分为培训和测试,我们执行以下操作:
X_train, X_test, y_train, y_test = train_test_split(X,y)
默认测试大小为 0.25。您还可以更改训练/测试分割的大小。例如,要将测试大小更改为 0.3,我们需要执行以下操作:
X_train, X_test, y_train, y_test = train_test_split(X,y, test_size = 0.3)
接下来,我们可以训练和测试我们的随机森林模型:
from sklearn.ensemble import RandomForestClassifier
model = RandomForestClassifier()
model.fit(X_train, y_train)
y_pred = model.predict_proba(X_test)
让我们评估接收器工作特性曲线(AUROC)下的面积:
from sklearn.metrics import roc_auc_score
from sklearn.metrics import roc_curve, auc
fpr, tpr, _ = roc_curve(y_test, y_pred)
roc_auc = auc(fpr, tpr)
并绘制 ROC:
import matplotlib.pyplot as plt
import seaborn as sns
sns.set()
plt.figure()
lw = 2
plt.plot(fpr, tpr, color='red',
lw=lw, label='ROC curve (area = %0.2f)' % roc_auc)
plt.plot([0, 1], [0, 1], color='navy', lw=lw, linestyle='--')
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('Receiver operating characteristic for Wine Price Classification')
plt.legend(loc="lower right")
plt.show()

我们还可以看看精度/召回曲线和平均精度:
precision, recall, _ = precision_recall_curve(y_test,y_pred)
average_precision = average_precision_score(y_test, y_pred)sns.set()
plt.figure()
plt.step(recall, precision, where='post')plt.xlabel('Recall')
plt.ylabel('Precision')
plt.ylim([0.0, 1.05])
plt.xlim([0.0, 1.0])
plt.title(
'Average precision score Wine Price Classification, micro-averaged over all classes: AP={0:0.2f}'
.format(average_precision)

k 倍交叉验证
接下来让我们讨论“KFold”方法。该函数提供训练和测试索引,将数据分成训练和测试集。它将数据随机或连续(默认)分成 K 个折叠,每个折叠用于一次验证,而其余 k-1 个折叠构成训练集。我们将使用折叠数的默认值,即 5。让我们初始化一个“KFold”对象,获取数据分割,并初始化预测目标值和真实目标值的数组:
from sklearn.model_selection import KFold
folds = KFold()
folds.get_n_splits(df)
y_true = []
y_pred = []
如果我们想将折叠数从 5 改为 10,我们可以这样做:
folds = KFold(n_splits=10)
接下来,让我们在 for 循环中训练和测试我们的模型,并追加结果:
for train_index, test_index in folds.split(df):
df_test = df.iloc[test_index]
df_train = df.iloc[train_index]
X_train = np.array(df_train[['country_cat', 'winery_cat', 'variety_cat', 'points']])
y_train = np.array(df_train['price_class'])
X_test = np.array(df_test[['country_cat', 'winery_cat', 'variety_cat', 'points']])
y_test = np.array(df_test['price_class'])
y_true.append(y_test)
model = RandomForestClassifier()
model.fit(X_train, y_train)
y_pred.append(model.predict_proba(X_test)[:,1])
y_pred = [item for sublist in y_pred for item in sublist]
y_true = [item for sublist in y_true for item in sublist]
接下来我们绘制 ROC 曲线:
fpr, tpr, _ = roc_curve(y_true, y_pred)
roc_auc = auc(fpr, tpr)sns.set()
plt.figure()
lw = 2
plt.plot(fpr, tpr, color='red',
lw=lw, label='ROC curve (area = %0.2f)' % roc_auc)
plt.plot([0, 1], [0, 1], color='navy', lw=lw, linestyle='--')
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('Receiver operating characteristic for Wine Price Classification (KFolds)')
plt.legend(loc="lower right")
plt.show()

精确度/召回率曲线:
precision, recall, _ = precision_recall_curve(y_true,y_pred)
average_precision = average_precision_score(y_true, y_pred)sns.set()
plt.figure()
plt.step(recall, precision, where='post')plt.xlabel('Recall')
plt.ylabel('Precision')
plt.ylim([0.0, 1.05])
plt.xlim([0.0, 1.0])
plt.title(
'Average precision score Wine Price Classification (KFolds), micro-averaged over all classes: AP={0:0.2f}'
.format(average_precision))

遗漏一个交叉验证
最后,让我们讨论一下“LeaveOneOut”方法。该功能提供训练/测试指数,以分割用于训练和测试的数据。每个样本作为单独测试使用一次,而剩余的样本用于训练。这里,我们初始化“LeaveOneOut”对象,并像以前一样获取数据拆分:
from sklearn.model_selection import LeaveOneOut
loo = LeaveOneOut()
loo.get_n_splits(df)
y_true = []
y_pred = []
我们循环遍历索引并追加结果。为清楚起见,代码被截断:
for train_index, test_index in loo.split(df):
df_test = df.iloc[test_index]
df_train = df.iloc[train_index]
...
接下来我们绘制结果:


我就讲到这里,但我鼓励您自己尝试一下这些函数。
结论
总之,在这篇文章中,我们讨论了如何使用模型选择模块中的函数来验证模型。首先,我们讨论了随机选择数据进行训练和测试的训练/测试分割函数。然后,我们讨论了“K-Fold”函数,它将数据分成连续或随机的折叠,用于训练和测试。最后,我们讨论了“LeaveOneOut”函数,该函数使用一个单独的样本进行测试,剩余的数据用于数据中每个样本的训练。我希望你觉得这篇文章有用/有趣。这篇文章中的代码可以在 GitHub 上找到。感谢您的阅读!
用 Python 建模区域性新冠肺炎疫情

我所在地区的疫情何时会结束?
下图是意大利新冠肺炎疫情的一系列数据分析和建模结果。对于每个地区,它显示了流行病结束的预期日期,以在危急情况下零死亡和零患者之前剩余的天数表示。为了理解我是如何预测这些日期以及我是如何绘制它们的,我们需要倒退几步。

在4 月 21 日发表的这篇文章中,我展示了如何使用 Python 和数据科学库来分析和建模新冠肺炎数据。在我的分析中,我使用了意大利民防局每天发布的数据集,这些数据集可以在官方 GitHub 知识库上获得。
在确定了采样方法中偏差的几个潜在来源后,我证明了在 4 月 1 日达到峰值后,每天死亡人数与重症监护病房患者人数的总和(一个我命名为deaths_ICU的变量)呈线性下降趋势。
因此,使用该衍生变量,可以创建一个线性回归模型来估计意大利严重疫情的结束时间,给出的日期是每天死亡人数与重症监护患者人数之和预计为零的日期。
如下图所示,在文章发表五天后,趋势仍在有规律地持续,模型的结果在今天(4 月 26 日)仍然有效。

衍生变量deaths_ICU随时间的演变
更仔细地看
根据该模型,考虑到整个国家,疫情的结束应该发生在 2020 年 5 月 19 日至 22 日之间。然而,我们知道,自疫情开始以来,北部地区受影响最为严重;此外,一些地区可能比其他地区恢复得更快。模型预测的日期可能是 20 个地区的平均日期。
出于这个原因,今天,我们将通过为每个意大利地区开发一个单独的模型来进一步分析。
然后,我们将加入时态信息(什么时候结束?)与空间成分(在哪里会先结束?),并创建显示疫情结束时间表的复合区域地图。
导入和清理数据
首先,让我们导入必要的图书馆和意大利民事保护局提供的新冠肺炎地区细目表
这个数据集已经包含了大约 1200 行。不幸的是,数据并没有完全按照我们需要的格式提供。事实上,由于一些体制原因,20 个地区中只有 19 个每天提供民事保护的最新情况。特伦蒂诺上阿迪杰(数据集中的第 4 区)由它的两个省分别代表(波尔扎诺自治省和特伦托自治省)。因此,数据集报告了 19 个地区和 2 个省份的数据。因此,首先要做的是从数据集中排除引用区域 4 的所有行。然后,我们创建第二个数据帧,相反,它只包含两个省份的行。
现在我们将这两个省的数据相加,以便计算整个 Trentino Alto Adige 地区的死亡人数和 ICU 患者。最后,我们将两个表连接在一起,以得到一个包含 20 个区域中每个区域的干净数据的数据框架,这最终是分析所需的格式。
将数据集分成 20 部分
假设我们要构建和训练 20 个不同的模型,每个意大利地区一个,我们还需要将主数据集分成 20 个部分。当我们这样做时,我们还计算将用于训练模型的派生变量deaths_ICU。所有 20 个数据集随后被追加到一个名为regional_df_list的列表中。
定义代数函数
下一步是定义一些稍后将在主脚本中使用的函数。这些函数将执行估计回归线和 X 轴之间的交点所需的代数运算。
建立和训练 20 个模型
我们现在已经获得了遍历每个地区的数据集、创建模型、训练模型以及估计从意大利北部到南部的疫情所需的所有信息。当我们迭代时,我们将模型的输出存储在一个名为model_outputs_df的新数据框架中。它不仅包含预测日期,还包含每个区域模型的决定系数 R。r 会给出一些关于模型性能的信息。R 为 1 表示回归预测完全符合数据,因此值越接近 1,我们就越信任我们的模型。
如果你对以下脚本的细节感到好奇,你可以在我之前的文章中了解更多。
了解模型的输出
我们快完成了!这是前 5 个地区的model_outputs_df的样子:

列model_score特别重要,它是 R 系数,告诉我们模型的表现如何,因此每个地区的预测有多可信和可靠。
映射模型的性能
现在,生活在我内心的地理空间极客终于可以得到一些满足:可视化区域模型的不同可靠性的最佳方式是在地图上显示它们,我们将使用geopandas和mapclassify库来实现这一点。意大利地区的边界可以从意大利国家统计局的网站上下载。

上图显示了模型的可靠性在不同的地区有很大的差异。在我们构建和训练的模型中,表现最好的模型是与伦巴第地区相关的模型。

由于其 R 高于 0.99,该模型完全符合数据序列,我们可以预计该估计在未来也是相当可靠的。在光谱的另一端,我们发现了拉齐奥区。

在这种情况下,决定系数 R 仅为大约 0.35。因此,预测不太可靠,并且误差幅度特别大。这主要是因为该地区的流行趋势不是线性趋势,因此该模型提供的预测不应被视为有效。
映射模型的预测
我们的模型提供的关键信息是对每个意大利地区流行病结束的预测,用变量deaths_ICU变为零之前所需的天数表示。在考虑我们的模型的可靠性之前,让我们画一张他们在全国的预测图。

将预测与模型可靠性相结合
最后一步是将区域模型的预测与它们的可靠性得分结合起来。换句话说,我们希望从最终地图中排除所有模型表现不佳的区域。为此,我们为最小可接受的model_score设置了一个任意的阈值,并且我们只在地图上显示我们的预测更可靠的区域。

结论
下表总结了我们在本练习中构建和训练的区域线性回归模型的预测。
如前所述,模型得分越高,预测就越可靠。由于这个原因,低评分模型的输出已经从这个摘要表和最终地图中排除。
考虑到地区差异和趋势,本文旨在预测意大利新冠肺炎疫情的结束。像任何建模工作一样,我们必须考虑任何新的外部因素都可能改变流行病的有效演变。模型无法考虑系统中的意外变化,如封锁限制的逐渐放松(预计在 5 月 4 日之后在意大利),或气温上升对病毒传播的影响。系统中的这些变化可能会产生影响,并改变我们观察到的线性趋势。
从更普遍的角度来看,我们的分析结果强调了意大利 COVID 疫情的巨大异质性,一些地区的恢复速度比其他地区快得多。此外,在地图上可视化这些预测让我们有了更清晰的概览,并帮助我们更好地了解疫情。
我为本文中显示的分析、模型和地图开发的所有代码都可以在我的 GitHub 库上获得。
用 Python 建模新冠肺炎疫情

由于意大利目前实行的国家封锁,这个周末我不得不呆在家里,就像世界上数十亿其他人一样。所以,我决定利用这段时间来研究意大利新冠肺炎大流行的数据,这些数据是由意大利民防部门每天发布的。
在这篇文章中,我将展示如何检查数据,在图表上表示数据,并用 Python 建模未来趋势,使用一些开源数据科学库,如Pandas、Matplotlib和Scikit-learn。
我真的很感谢民防部门在不同规模(国家、地区和省级)发布每日数据所做的努力,特别是通过他们的官方 GitHub 知识库以机器可读的格式发布这些数据。像往常一样,开放数据方法促进了科学合作,丰富了合作研究,并可以提高集体分析能力,最终为决策提供信息。
这项工作是在 macOS 上用 Python-3.7.1 开发的,它受到了我在网上找到的类似论文的启发。完整版本的代码可以在我的 GitHub 库上找到。
将提议的方法应用于与其他受 COVID 影响的国家相关的数据集将是有趣的,例如西班牙或法国。
安装必要的库
本教程中使用的模块没有附带标准的 Python 发行版。要使用这些第三方模块,我们必须安装它们。
pip install pandas matplotlib scikit-learn
or
conda install -c anaconda pandas matplotlib scikit-learn
导入数据和提取字段
首先,我们导入所有必要的库,然后我们从 Civil Protection GitHub 帐户导入最新版本的意大利 COVID 数据集,并将其存储在一个 Pandas 数据框中。然后,我们探索该表的结构,以便对变量有一个更清晰的看法,并最终确定我们应该考虑的流行病建模。
import pandas as pd
from datetime import datetime, timedelta
import matplotlib.pyplot as plt
from matplotlib.dates import DateFormatter
import matplotlib.dates as mdatesdata = pd.read_csv(“[https://raw.githubusercontent.com/pcm-dpc/COVID-19/master/dati-andamento-nazionale/dpc-covid19-ita-andamento-nazionale.csv](https://raw.githubusercontent.com/pcm-dpc/COVID-19/master/dati-andamento-nazionale/dpc-covid19-ita-andamento-nazionale.csv)")print (data.columns)
我们获得了数据帧中包含的所有字段的列表。

特别是,deceduti和tamponi分别报告了自危机开始以来记录的死亡和医学拭子(检验)的累计人数。为了计算这两个变量的每日增量,我们运行一个简单的 pandas 函数,并将输出保存到两个新字段中:diff_deceduti和diff_tamponi。然后,我们创建一个包含所有日期的列表,并将它们从string 转换成更合适的datetime格式。
data[‘diff_deceduti’] = data[‘deceduti’].diff()
data[‘diff_tamponi’] = data[‘tamponi’].diff()
dates = data[‘data’]
date_format = [pd.to_datetime(d) for d in dates]
可视化数据
我们现在准备创建我们的第一个散点图,并显示直观上看起来似乎对估计疫情进展最重要的变量:每日新增阳性病例数(nuovi_positivi)。
variable = ‘nuovi_positivi’
fig, ax = plt.subplots(figsize=(12, 5))
ax.grid()
ax.scatter(date_format,data[variable])
ax.set(xlabel=”Date”,ylabel=variable,title=variable)
date_form = DateFormatter(“%d-%m”)
ax.xaxis.set_major_formatter(date_form)
ax.xaxis.set_major_locator(mdates.DayLocator(interval = 3))
fig.savefig(variable + ‘.png’)
plt.show()

上面的图中有一个重复出现的模式,这是由每个周末后发生的异常现象给出的:似乎疫情在周一和周二放缓。这是因为在周末(以及一般的公共假日,如复活节后的星期一)处理的拭子数量要少得多。

纠正偏见
纠正这种系统性偏差的一种可能方法是计算移动平均值,这种方法通常用于通过计算整个数据集不同子集的平均值来分析时间序列,在我们的例子中是 7 天。
通过对第一个 7 天的子集进行平均来计算第一个移动平均值,然后通过向前移动到下一个固定子集来改变子集,以此类推。总的来说,移动平均线平滑了数据,减少了像周末偏差这样的异常。
rolling_average_days = 7data[‘nuovi_positivi_moving’] = data[‘nuovi_positivi’].rolling(window=rolling_average_days).mean() variable = ‘nuovi_positivi_moving’
fig, ax = plt.subplots(figsize=(12, 5))
ax.grid()
ax.scatter(date_format,data[variable])
ax.set(xlabel=”Date”,ylabel=variable,title=variable)
date_form = DateFormatter(“%d-%m”)
ax.xaxis.set_major_formatter(date_form)
ax.xaxis.set_major_locator(mdates.DayLocator(interval = 3))
fig.savefig(variable + ‘.png’)
plt.show()

上图显示了 7 天内新病例的平均趋势,掩盖了周末的异常情况。然而,每天检测的拭子数量的影响还没有完全补偿。显然,阳性病例的数量与所进行的检测数量密切相关。让我们来看看在意大利进行的日常测试的趋势(tamponi):
data['diff_tamponi_moving'] = data['tamponi'].diff().rolling(window=rolling_average_days).mean()variable = 'diff_tamponi_moving'
fig, ax = plt.subplots(figsize=(12, 5))
ax.grid()
ax.scatter(date_format,data[variable])
ax.set(xlabel="Date",ylabel=variable,title=variable)
date_form = DateFormatter("%d-%m")
ax.xaxis.set_major_formatter(date_form)
ax.xaxis.set_major_locator(mdates.DayLocator(interval = 3))
fig.savefig(variable + '.png')
plt.show()

我们的怀疑得到了证实:在我们的时间序列结束时,每日拭子的数量比开始时高出约 20 倍,因此变量nuovi_positivi正遭受这一重要偏差。为了找到一个更有代表性的趋势,我们现在计算新阳性在总的每日测试中的百分比,并检查随时间的变化。
data[‘perc_positive’] = ((data[‘nuovi_positivi_moving’])/(data[‘diff_tamponi_moving’])*100)variable = ‘perc_positive’
fig, ax = plt.subplots(figsize=(12, 5))
ax.grid()
ax.scatter(date_format,data[variable])
ax.set(xlabel=”Date”,ylabel=variable,title=variable)
date_form = DateFormatter(“%d-%m”)
ax.xaxis.set_major_formatter(date_form)
ax.xaxis.set_major_locator(mdates.DayLocator(interval = 3))
fig.savefig(variable + ‘.png’)
plt.show()

衍生变量perc_positive提供了更可靠的疫情进展表示,因为它修正了之前确定的两个系统性偏差:周末波动和测试总数。不幸的是,仍然有几个未考虑的因素破坏了这一分析的有效性。仅提及其中的一些:随着时间的推移,检测程序发生了相当大的变化,从仅检测严重症状的患者到在特定地区对整个人群进行大规模检测,并且拭子方法因地区而异。此外,关于新阳性病例的数据可以参考前几天进行的测试,从 1 天到 7 天。让事情变得更加复杂的是,一些患者也可以进行多次测试,但我们无法从数据集中检测到这一点。由于这些和其他原因,perc_positive还不是我们建模流行趋势和最终预测其演变所需的变量。
我们还应该进一步检查其他重要的领域:特别是,让我们仔细看看terapia_intensiva 和diff_deceduti(分别是重症监护和每日死亡)。
variable = ‘terapia_intensiva’
fig, ax = plt.subplots(figsize=(12, 5))
ax.grid()
ax.scatter(date_format,data[variable])
ax.set(xlabel=”Date”,ylabel=variable,title=variable)
date_form = DateFormatter(“%d-%m”)
ax.xaxis.set_major_formatter(date_form)
ax.xaxis.set_major_locator(mdates.DayLocator(interval = 3))
fig.savefig(variable + ‘.png’)
plt.show()variable = ‘diff_deceduti’
fig, ax = plt.subplots(figsize=(12, 5))
ax.grid()
ax.scatter(date_format,data[variable])
ax.set(xlabel=”Date”,ylabel=variable,title=variable)
date_form = DateFormatter(“%d-%m”)
ax.xaxis.set_major_formatter(date_form)
ax.xaxis.set_major_locator(mdates.DayLocator(interval = 3))
fig.savefig(variable + ‘.png’)
plt.show()

如上图所示,目前接受重症监护的患者数量似乎遵循一个更有规律的趋势。事实上,它指的是一个容易衡量的信息,不受抽样方法或每周波动的影响。当然,它并不完美,因为它也容易被低估,尤其是在危机最严重的时候,那时医疗系统压力很大,医院已经饱和。但是在这个关键阶段之后,它应该能很好地反映出受病毒影响最严重的患者人数。然而,我们可能还遗漏了一些东西:我们能肯定重症监护数量的减少总是与情况的改善相对应吗?事实上,由于每天死亡人数的增加,这一数字可能正在下降。

上面的图表显示,每天的死亡人数一直在增加,直到 3 月 28 日,然后它开始以较慢的速度下降。
为模型创建派生变量
本文的主要假设是重症监护和每日死亡的综合价值可以作为估计当前流行病进展和模拟未来趋势的可靠变量。让我们创建一个新的字段gravi_deceduti,计算每天死亡的重度抑郁患者的总数,并绘制结果值。
data[‘gravi_deceduti’] = data[‘diff_deceduti’] + data[‘terapia_intensiva’]variable = 'gravi_deceduti'
fig, ax = plt.subplots(figsize=(12, 5))
ax.grid()
ax.scatter(date_format,data[variable])
ax.set(xlabel="Date",ylabel=variable,title=variable)
date_form = DateFormatter("%d-%m")
ax.xaxis.set_major_formatter(date_form)
ax.xaxis.set_major_locator(mdates.DayLocator(interval = 3))
ax.axvline(datetime(2020, 4, 1), c="green", zorder=0)
fig.savefig(variable + '.png')
plt.show()

我们解释了terapia_intensiva 和diff_deceduti如何不遭受重大系统偏倚,以及后者如何在每日死亡增加的情况下补偿前者。这两个变量的组合现在可以用于模拟意大利的疫情趋势,因为这一得出的数字较少受到系统性偏差的影响。我们在图表上添加了一条绿线,以突出显示高峰日,这是下降趋势开始的点。
流行病趋势建模
我们现在可以建立一个线性回归模型,并用从 4 月 1 日开始的gravi_deceduti的数据对其进行训练。
线性回归是监督学习中最流行的经典机器学习算法之一。当协变量和响应变量之间的关系是线性的(在我们的例子中:date VS gravi_deceduti)时,这种算法相对容易实现并且工作良好。一个明显的缺点是线性回归简化了许多现实世界的问题。
下面的代码改编自安婕莉卡·洛·杜卡在 3 月 31 日所做的部分工作,她试图用阳性病例的数量来模拟疫情。
首先我们从sklearn模块导入linear_model。然后,我们从X和y中排除 4 月 1 日记录的流行高峰之前的所有数据,并用X和y拟合LinearRegression模型。最后,我们通过运行函数score()来评估模型,该函数返回预测的决定系数 R(从自变量可预测的因变量中方差的比例)。r 会给出一些关于模型的拟合优度的信息。在回归中,R 决定系数是回归预测与真实数据点接近程度的统计度量。R 为 1 表示回归预测完全符合数据,因此值越接近 1,我们就越信任我们的模型。
import numpy as np
from sklearn import linear_model
# prepare the lists for the model
X = date_format
y = data['gravi_deceduti'].tolist()[1:]
# date format is not suitable for modeling, let's transform the date into incrementals number starting from April 1st
starting_date = 37 # April 1st is the 37th day of the series
day_numbers = []
for i in range(1, len(X)):
day_numbers.append([i])
X = day_numbers
# # let's train our model only with data after the peak
X = X[starting_date:]
y = y[starting_date:]
# Instantiate Linear Regression
linear_regr = linear_model.LinearRegression()
# Train the model using the training sets
linear_regr.fit(X, y)
print ("Linear Regression Model Score: %s" % (linear_regr.score(X, y)))

预测未来趋势
既然我们已经拟合了模型并积极评估了它的 R 值,我们就可以预测gravi_deceduti未来的演化了。为了做到这一点,我们调用函数predict(),并且我们用函数max_error()跟踪模型的最大误差。使用该值,我们将创建两条线来描述容差缓冲区,包括模型预测的最小和最大误差。
# Predict future trend
from sklearn.metrics import max_error
import math
y_pred = linear_regr.predict(X)
error = max_error(y, y_pred)
这个模型现在已经可以预测接下来几天的gravi_deceduti了。我们定义了一个变量X_test,它包含了过去和未来的日子。我们还创建了变量future_days,它包含我们想要估计流行趋势的天数。然后我们将我们的模型应用到X_test。
X_test = []
future_days = 55
for i in range(starting_date, starting_date + future_days):
X_test.append([i])
y_pred_linear = linear_regr.predict(X_test)
变量y_pred_linear包含未来 55 天的预测gravi_deceduti。为了考虑模型产生的误差,我们分别定义包含y_pred + error和y_pred - error的y_pred_max和y_pred_min。
y_pred_max = []
y_pred_min = []
for i in range(0, len(y_pred_linear)):
y_pred_max.append(y_pred_linear[i] + error)
y_pred_min.append(y_pred_linear[i] - error)
绘制预测图
我们有三个输出变量准备显示在图表上:y_pred、y_pred_max和y_pred_min,分别包含预测、最大误差和最小误差。为了让情节更吸引人,我们应该将数字(由X_test变量表示)转换成日期。
# convert date of the epidemic peak into datetime format
from datetime import datetime, timedelta
date_zero = datetime.strptime(data['data'][starting_date], '%Y-%m-%dT%H:%M:%S')
# creating x_ticks for making the plot more appealing
date_prev = []
x_ticks = []
step = 5
data_curr = date_zero
x_current = starting_date
n = int(future_days / step)
for i in range(0, n):
date_prev.append(str(data_curr.day) + "/" + str(data_curr.month))
x_ticks.append(x_current)
data_curr = data_curr + timedelta(days=step)
x_current = x_current + step
现在,我们可以将已知数据与预测和误差线一起绘制出来
# plot known data
plt.grid()
plt.scatter(X, y)
# plot linear regression prediction
plt.plot(X_test, y_pred_linear, color='green', linewidth=2)
# plot maximum error
plt.plot(X_test, y_pred_max, color='red', linewidth=1, linestyle='dashed')
#plot minimum error
plt.plot(X_test, y_pred_min, color='red', linewidth=1, linestyle='dashed')
plt.xlabel('Days')
plt.xlim(starting_date, starting_date + future_days)
plt.xticks(x_ticks, date_prev)
plt.ylabel('gravi_deceduti')
plt.yscale("log")
plt.savefig("prediction.png")
plt.show()


区域趋势
上述分析是在国家基础上处理意大利新冠肺炎疫情,但众所周知,伦巴第、皮埃蒙特、威尼托和艾米利亚-罗马涅地区受到的影响比其他地区更严重。为了对此进行量化,我们可以检查由民事保护局提供的区域新冠肺炎数据集,并计算这些区域(我们称之为区域 1 )中登记的死亡比例
data = pd.read_csv("https://raw.githubusercontent.com/pcm-dpc/COVID-19/master/dati-regioni/dpc-covid19-ita-regioni.csv")
zone1_df = data[data.denominazione_regione.isin(['Piemonte','Emilia-Romagna','Veneto','Lombardia'])]
zone1_df['deceduti'].sum()
print("Zone 1 accounts for %s percent of the total deaths" % (round(zone1_df['deceduti'].sum() / data['deceduti'].sum() * 100,2)))

如果仅仅这 4 个地区(20 个地区中的)就占了总死亡人数的 80%以上,那么用一个单一的模型来预测整个国家的趋势就是一种严重的过度简化。事实上,不同地区的情况差异很大,如下图所示。

对于每个地区,应采用不同的模型,以便更好地了解该国不同地区的流行趋势。考虑到民防部门也提供省级的数据,有可能对情况进行更好的建模。
结论
我们的模型告诉我们,如果目前的趋势持续下去,新冠肺炎疫情将于 2020 年 5 月 19 日至 22 日在 T2 结束。
除了地区差异,我们还必须考虑到任何新的外部因素都可能改变疫情的有效趋势。目前的模型无法考虑系统中的意外变化,如封锁限制的逐渐放松,或温度上升对病毒传播的影响。
在我的下一篇文章中,我将分别为每个地区创建不同的模型,并将代码上传到我的 GitHub 库上。
作物保险索赔建模
作物保险概述和损失率建模
这篇文章概述了美国联邦农作物保险公司通过私人公司网络提供的农作物保险项目。我们回顾了该计划在过去二十年中的演变,并提出了一种模拟损失负债比的方法。我们表明,威布尔分布提供了一个合理的选择,以模拟 0%至 100%的责任水平之间的损失支付,这一发现与先前对保险索赔建模的研究相一致。

塞尔吉奥·索萨在 Unsplash 上的照片
一.导言
美国的农作物保险由联邦农作物保险公司(FCIC)通过一个私人公司网络提供。FCIC 由美国农业部风险管理局(RMA)管理[1]。本文介绍了美国的农作物保险项目,并概述了 2000-2020 年期间的政策、责任和索赔。我们还研究了保险损失,并提出了一种模拟这些损失的方法。
虽然本文中提供的示例和网站参考是以美国为中心的,但这里提出的想法是通用的,可以应用于所有地方。在其他地区和国家,分析师需要用适当的地区数据源替代作物保险索赔数据。
二。数据源
RMA 在其网站上提供了优秀的汇总级数据集[2]。本文中的分析主要基于作者在 2020 年 10 月从该网站下载的州/县/作物/覆盖水平数据。这些数据文件是按年份组织的,并且是以文本文件格式,可以很容易地被标准统计软件处理。
请注意,来自 RMA 网站的数据不是分散的数据。在州、县、作物和覆盖水平上进行总结。因此,我们无法在单个保单或单个索赔级别进行分析。我们确实通过《信息自由法》请求向美国农业部请求了政策层面的数据,但由于管理数据的现行法律法规,这一请求被拒绝。
三。概述
美国农作物保险计划覆盖了 100 多种农作物,全国各地的农民都可以投保。FCIC 利用几家私人公司(认可的保险提供商)来销售和服务个人保单。在过去二十年中,该计划有 110 万至 130 万份保费支付保单(见图 1。)

图 1:有保费的农作物保险单。
3.1 保险费和补贴
与这些政策相关的保费约为。在过去的几年里增加了 100 亿美元(见图 2 的上图)。附录中提供了通货膨胀调整后的保险费。
如图 2 下图所示,该计划获得了大量补贴。过去几年,补贴占保费的 60%以上。请注意,图 2 显示了联邦补贴。各州、私人和其他计划提供额外补贴,但这些补贴与联邦补贴相比相当低(补贴水平的详细情况见附录)。在我们的分析中,我们将忽略这些国家、私人和其他补贴。保费和联邦补贴之间的差额可以被认为是保单持有人获得的“净保费”。

图 2:农作物保险费和补贴。
FCIC 保险的 100 多种作物中的四种——玉米、大豆、棉花和小麦——占了该计划收到的保险费的 70%以上。在过去二十年中,其他作物的份额一直在稳步增长(见图 3。)

图 3:按商品分列的农作物保险费。
该计划在全国广泛传播;图 4 显示了 2020 年各县的保费分布情况(附录中提供了前几年的类似图表)。

图 4:各县农作物保险费(2020 年)。
3.2 损失
作为典型的保险政策,每年都有一些保单持有人提出索赔,以获得对他们当年损失的赔偿。这些损失金额(RMA 称之为“赔偿金额”)按主要作物显示在图 5 中,2020 年损失的地理分布显示在图 6 中(前一年的图在附录中提供)。

图 5:按商品分列的农作物保险损失。

图 6:各县的农作物保险损失(2020 年)。
图 7 显示了这一年的净保费的亏损情况。这个数字清楚地表明,在大多数年份里,该计划支付的损失要多于它获得的净保费。

图 7:农作物保险净保费和损失。
四。损失率建模
对于预测损失(索赔支付),总损失与总保险或总保费的比率是一个关键组成部分,因为它定义了索赔人可能从其索赔中获得的金额。使用一个比率而不是绝对数量,可以让分析师将来自多个地理区域和不同时间段的数据结合起来,使覆盖水平和通货膨胀的影响正常化。
图 8 显示了损失在保费(上图)和负债(下图)中所占份额的变化,图 9 显示了 2020 年这些比率的地理分布。过去二十年的数据表明,平均而言,损失占已付保费的 82%或负债额的 7.5%。

图 8:损失率演变。

图 9:各县的损失率(2020 年)。
对于损失比率的建模,我们认为损失与负债比率是比损失与保费比率更好的衡量标准,因为从理论上讲,损失可以达到保单承保的责任范围金额,实际上在个别情况下,保单持有人在索赔中获得 100%的责任范围。
如前所述,我们无法访问建模任务的单个保单或索赔数据。相反,我们使用聚合数据集,其中每个数据点反映了州、县、作物、保险计划名称、保险类型和交付类型的组合。例如,对于 2020 年,大约有。140,000 个数据点反映了大约。110 万份保费支付保单和大约。187,000 份可赔偿保单(即损失)。
图 10 显示了 2000-2020 年期间的损失负债比率。有大量数据点的索赔与负债比率为 0.0,反映了未提交索赔或索赔被拒绝的保单。有几个索赔负债比率为 1.0 的数据点反映了保单获得了 100%的保险金额。

图 10:损失负债比分布(2000-2020)。
考虑到总数据的特殊情况,其中 0%的责任损失结合了未提交索赔的保单和被拒绝索赔的保单,以及 100%水平的局部峰值,将标准统计分布之一拟合到数据是不合适的。解决这个问题的一种方法是只考虑损失负债比在 0 和 1 之间的数据点。图 11 所示的结果数据遵循一种更易识别的统计分布模式,类似于对数正态分布、威布尔分布或伽马分布。
在测试了一些不同的选项后,我们发现威布尔分布相当好地拟合了数据(见图 11 和图 12)。威布尔分布是经过充分研究的统计分布,广泛用于索赔建模、可靠性分析、部件寿命分析、天气预报、水文(降雨量、河流流量)等一般保险分析。
这一发现与保险索赔模型的研究是一致的。Hewitt 和 Lefkowitz [3]描述了使用五种不同的分布(伽玛、对数伽玛、对数正态、伽玛+对数伽玛和伽玛+对数正态)来拟合保险损失数据。祖阿内蒂等人【4】描述了保险索赔数据的对数正态模型的统计细节。Tiwari [5]提供了使用广义线性模型对索赔频率建模的概述。David 和 Jemna [6]展示了泊松和负二项分布如何用于汽车保险索赔建模。常等人【7】建议使用泊松分布来模拟单个台风/洪水事件的发生。

图 11:使用汇总数据的损失负债比率模型。

图 12:损失责任模型的威布尔分布拟合结果。
动词 (verb 的缩写)关闭
这篇文章概述了美国的农作物保险项目。我们已经提出了一种方法来模拟索赔覆盖率,并表明威布尔分布提供了一种合理的选择来模拟 0%和 100%之间的保单责任水平的索赔支付。虽然分析是基于聚合数据集,但我们相信结果也适用于个人保单和索赔级别的数据。
参考
联邦农作物保险公司。美国农业部-风险管理署。https://www.rma.usda.gov/FCIC/。
【2】州/县/作物业务汇总。美国农业部-风险管理署。https://www . RMA . USDA . gov/en/Information-Tools/Summary-of-Business/State-County-Crop-Summary-of-Business。2020 年 10 月接入。
[3]小查尔斯·c·休伊特和本杰明·莱夫科维茨。保险损失数据的分布拟合方法。在 1979 年 11 月的伤亡保险精算学会会议上发表的论文。https://www.casact.org/pubs/proceed/proceed79/79139.pdf
[4]祖阿内蒂、迪尼茨和莱特。保险索赔数据的对数正态模型。统计杂志。第 4 卷第 2 期,2006 年 6 月。https://www.ine.pt/revstat/pdf/rs060203.pdf。
【5】阿贾提瓦里。建模保险索赔频率。https://medium . com/swlh/modeling-insurance-claim-frequency-a 776 F3 BF 41 DC。2020 年 9 月接入。
[6]m .大卫和 d .杰姆纳。通过泊松和负二项式模型对汽车保险索赔频率进行建模。经济和商业科学年鉴 62(2):151–168。2015 年 7 月。https://content . science do . com/view/journals/ai cue/62/2/article-p151 . XML。
[7]清、许文子和苏明道。台湾洪水风险和洪水保险项目建模。农业与应用经济学协会 2008 年年会。https://ideas.repec.org/p/ags/aaea08/6141.html。
在此下载论文副本(包括附录):http://www . rockcreekanalytics . com/modeling-crop-insurance-claims/
模拟指数增长

使用对数变换、指数增长和线性回归预测冠状病毒传播
随着当前冠状病毒的爆发,我们听到了很多关于指数增长的说法。在本文中,我展示了如何理解和分析指数增长。如果你想跟随,你可以使用那些示例数据和一个简短的 Python 笔记本。
为什么是指数增长?
指数增长是一个数学函数,可用于多种情况。这个公式告诉我们某一时刻的病例数,对于冠状病毒来说,这是被感染的人数。
在指数增长的其他用例中,这个数字可能是动物数量的大小,也可能是你银行账户上的金额(如果你足够幸运有好的利率)。
使用指数增长来模拟冠状病毒爆发的原因是,流行病学家已经研究了这些类型的爆发,众所周知,流行病的第一阶段遵循指数增长。
指数增长公式
指数增长的特征在于以下公式:

指数增长函数
其中:
- x(t) 是任意给定时间 t 的病例数
- x0 是案例开始时的数量,也叫初始值
- b 是每个病人感染的人数,即增长系数
指数增长的一个简单例子:基数 2
为了更清楚地说明这一点,我将举一个假设的例子:
- 我们从 1 个感染者的初始值开始,所以 x0 = 1
- 每个病人会感染另外两个人,所以增长率 b = 2
- 我们将考察从时间 0 到时间 14 的疫情发展
我们首先需要将 a 和 b 的值代入公式,以获得特定流行病的公式:

然后我们可以用这个公式来计算从 0 到 14 的每个 t 值的 y 值。当我们这样做时,我们在每个时间点获得了以下感染人数,如下表所示。这表明,从 1 个人开始,每个人的增长因子为 2,我们在 14 天后获得了超过 16000 个案例。

如果我们想用图表来表示,我们开始看到一个图表,看起来很像我们看到的关于冠状病毒的非常惊人的曲线:

生长因子为 2 的指数生长图
找到冠状病毒的确切分子式
现在,我们知道这个图或多或少有正确的形状,但我们需要做一个额外的步骤,使我们的分析有用。我们需要通过查看疫情传播的数据,找到电晕疫情的真正增长因素。

冠状病毒数据的摘录。来源:https://covid.ourworldindata.org/data/full_data.csv
寻找生长因子的线性回归
在查看数据时,我们只有每天的病例数,而没有增长因素。从每日的经验观察中找到生长因子的最好方法是使用一个叫做线性回归的统计模型。
给定 y 和 x 的经验观察值,线性回归允许我们在下面的公式中估计 a 和 b 的最佳值。在该公式中,y 是病例数,x 是时间。但是我们需要对指数增长函数进行一些重写,因为线性回归只能估计如下所示的公式:

线性回归所需的公式类型
重写线性回归的指数公式
首先,我们需要以线性回归的形式重写公式。我们需要的工具是对数。对数允许以正确的形式重写函数:

- 我们使用感染数量的记录而不是感染数量
- 我们使用生长因子的对数,而不是生长因子****
将线性回归应用于数据
步骤 1—Python 记事本的第一步是导入数据并应用日志转换:

步骤 2 — 然后我们使用 statsmodels 库来估计线性回归函数:

步骤 3 —根据表格制作预测函数
让我们回到线性回归的公式:

线性回归所需的公式类型
statsmodels 表给出了 coef (中间) : 下 a 和 b 的值
- 值 const 是我们线性回归中 a 的值: 0.4480
- 值时间是我们的线性回归中 b 的值:0.1128
因此,我们现在可以填写线性回归函数。

请记住:
- 我们说线性回归的一个 就是的对数的初始值
- 我们说线性回归的 b 是的对数增长因子
因此,我们知道:
- 初始值的对数等于 0.4480
- 生长因子的对数是 0.1128
为了找到实际的值,我们需要通过应用指数来“解开”它们。

现在,我们可以回到指数增长的原始公式,并填入这些值,以找出冠状病毒案例的实际值:

冠状病毒流行的实际公式!
对两周以后做预测!
现在我们已经估算了这个新公式,我们可以用它来预测任何我们想要的日期。值得注意的是,这里的预测只是一个例子,说明数学和统计学如何应用于流行病学。现实生活中的流行病学家将测试除指数增长之外的不同类型的模型,并在模型验证方面做大量工作,而目前的例子还没有这样做。
一旦找到最佳模型,就可以用它来进行预测。使用我们使用指数增长曲线估计的函数,如果我们想预测数据集最后一天(第 68 天)后的 2 周,我们只需将 t= 68 放入公式中,模型就预测当天有 3355 例感染。

进一步发展的可能性:
我已经展示了如何应用线性模型来预测指数增长过程。需要采取一些预防措施:
- 线性模型只是指数增长函数的最佳估计,它有一定的误差,我们可以在进一步的研究中检验
- 指数增长函数不一定是流行病的完美代表。我已经找到了最合适的指数增长函数,但是下一步要研究的可能是逻辑增长
- 指数级的增长只会在一开始符合流行病。在某种程度上,治愈的人将不再传播病毒,当(几乎)每个人都被感染或已经被感染时,增长将停止。
在这里,你可以找到一篇关于冠状病毒的逻辑增长的文章 ,它也考虑到了疫情的最后阶段。
感谢您阅读本文。我希望你喜欢它。不要犹豫,继续关注更多!
建模功能
从线性回归到逻辑回归

Clem Onojeghuo 在 Unsplash 上拍摄的照片
**Table of Contents**[**Introduction**](#3846)1\. [Linear models](#e296)
2\. [Quadratic models](#005e)
3\. [Cubic models](#35bc)
4\. [Exponential models](#7b2b)
5\. [Logarithmic models](#ec20)
6\. [Sinusoidal models](#48d3)
7\. [Logistic models](#8108)[**Conclusion**](#9ef6)
介绍
我们将使用 Jupyter Notebook 绘制一个散点图,并建立一个从线性到逻辑的回归线模型。
线性模型

第一个是线性模型。线性模型被表示为𝑦=𝑚𝑥+𝑐.我们将使用 numpy.array 或 numpy.arange 来创建数据。如果你想了解更多关于线性关系的内容,请阅读线性关系的衡量标准。我们导入 Python 库 numpy 和 matplotlib。我们创建了一个年份和一个二氧化碳阵列。
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inlineyear=np.array([1980,1982,1984,1986,1988,1990,1992,1994,1996,1998,2000])
co2=np.array([338.7,341.1,344.4,347.2,351.5,354.2,356.4,358.9,362.6,366.6,369.4])
首先,我们使用 matplotlib 创建一个散点图。添加标题、标签、x 轴和 y 轴标签。你需要使用show()方法。您可以在没有它的情况下进行打印,但这将删除不必要的输出。
plt.scatter(year,co2,label='CO2')
plt.title("Year vs CO2")
plt.xlabel('Year')
plt.ylabel('CO2')
plt.legend()
plt.show()

x 轴上的整数
如上图所示,x 轴上有小数。在下面的代码中,我们使用前三行使它们成为整数。
from matplotlib.ticker import MaxNLocatorax = plt.figure().gca()
ax.xaxis.set_major_locator(MaxNLocator(integer=True))plt.scatter(year,co2,label='CO2')
plt.title("Year vs CO2")
plt.xlabel('Year')
plt.ylabel('CO2')
plt.legend()
plt.show()

x 轴上的整数
用numpy.polyfit和numpy.poly1d寻找线性模型
最简单的方法就是用numpy.polyfit。通过将 order 设置为 1,它将返回一个线性系数数组。在numpy.poly1d中使用它会返回一个使用系数的等式。
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import MaxNLocator
from sklearn.linear_model import LinearRegression
%matplotlib inlineax = plt.figure().gca()
ax.xaxis.set_major_locator(MaxNLocator(integer=True))year=np.array([1980,1982,1984,1986,1988,1990,1992,1994,1996,1998,2000])
co2=np.array([338.7,341.1,344.4,347.2,351.5,354.2,356.4,358.9,362.6,366.6,369.4])coef = np.polyfit(year, co2, 1)
equ = np.poly1d(coef)x_plot = np.linspace(1975,2005,100)
y_plot = equ(x_plot)
plt.plot(x_plot, y_plot, color='r')plt.scatter(year,co2,label='CO2')
plt.title("Year vs CO2")
plt.xlabel('Year')
plt.ylabel('CO2')
plt.legend()
plt.show()

散点图和线性回归线
使用 scikit-learn 查找线性模型
求回归斜率和截距的第二种方法是使用[sklearn.linear_model.LinearRegression](https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LinearRegression.html)。该类要求 x 值为一列。我们使用reshape(-1,1)修改年份数据。原始年份数据具有 1x 11 形状。您需要将年份数据调整为 11 乘 1。
year1=year.reshape((-1,1))
print(np.shape(year))
print(np.shape(year1))

我们导入[sklearn.linear_model.LinearRegression](https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LinearRegression.html),重塑年份数据,使用LinearRegression().fit()拟合我们的数据。这将返回斜率coef_和 y 轴截距intercept_。coef_返回一个数组,所以我们用reg.coef_[0]取第一项。让我们打印出我们的回归线方程。

线性方程
from sklearn.linear_model import LinearRegressionyear1=year.reshape((-1,1))reg = LinearRegression().fit(year1,co2)slope=reg.coef_[0]
intercept=reg.intercept_print(f'The equation of regression line is y={slope:.3f}x+{intercept:.3f}.')

一起
我们一起画一个散点图和我们的线性回归线。我们使用从 1975 年到 2005 年的新 x 域,取 100 个样本作为回归线,np.linspace(1975,2005,100)。然后使用 x 域、斜率和 y 截距绘制回归线。
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import MaxNLocator
from sklearn.linear_model import LinearRegression
%matplotlib inlineax = plt.figure().gca()
ax.xaxis.set_major_locator(MaxNLocator(integer=True))year=np.array([1980,1982,1984,1986,1988,1990,1992,1994,1996,1998,2000])
co2=np.array([338.7,341.1,344.4,347.2,351.5,354.2,356.4,358.9,362.6,366.6,369.4])year1=year.reshape((-1,1))reg = LinearRegression().fit(year1,co2)
slope=reg.coef_[0]
intercept=reg.intercept_plt.scatter(year,co2,label='CO2')
X_plot = np.linspace(1975,2005,100)
Y_plot = slope*X_plot+intercept
plt.plot(X_plot, Y_plot, color='r')
plt.title("Year vs CO2")
plt.xlabel('Year')
plt.ylabel('CO2')
plt.legend()
plt.show()print(f'The equation of regression line is y={slope:.3f}x+{intercept:.3f}.')

用 scipy 寻找线性模型
另一种寻找回归斜率和截距的方法是使用[scipy.stats.linregress](https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.linregress.html)。这将返回slope, intercept, rvalue, pvalue, stderr。
from scipy.stats import linregressslope, intercept, r_value, p_value, std_err = linregress(year,co2)
print(f'The equation of regression line is y={slope:.3f}x+{intercept:.3f}.')

线性回归图
为了画一条线,我们需要 x 个点。我们使用np.linspace,它是numpy.linspace,因为我们使用了import numpy as np。我们的数据是从 1975 年到 2000 年。所以我们用 1960 代表start,2005 代表stop,100 代表样本数。
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import linregress
%matplotlib inlineyear=np.array([1980,1982,1984,1986,1988,1990,1992,1994,1996,1998,2000])
co2=np.array([338.7,341.1,344.4,347.2,351.5,354.2,356.4,358.9,362.6,366.6,369.4])X_plot = np.linspace(1975,2005,100)
Y_plot = slope*X_plot+intercept
plt.plot(X_plot, Y_plot, color='r')
plt.show()

使用 scipy . Lin regression 的线性回归线
现在我们把散点图、回归线和回归方程放在一起。
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import MaxNLocator
from scipy.stats import linregress%matplotlib inlineax = plt.figure().gca()
ax.xaxis.set_major_locator(MaxNLocator(integer=True))
slope, intercept, r_value, p_value, std_err = linregress(year,co2)
X_plot = np.linspace(1975,2005,100)
Y_plot = slope*X_plot+intercept
plt.plot(X_plot, Y_plot, color='r')
plt.scatter(year,co2,label='CO2')
plt.title("Year vs CO2")
plt.xlabel('Year')
plt.ylabel('CO2')
plt.legend()
plt.show()print(f'The equation of regression line is y={slope:.3f}x+{intercept:.3f}.')

散点图和线性回归线
练习 1
使用以下数据绘制散点图和回归线。求一个线性回归方程。
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inlinetemp = np.array([55,60,65,70,75,80,85,90])
rate = np.array([45,80,92,114,141,174,202,226])
回答
你画了散点图和回归图吗?回归线应该是𝑦= 5.119𝑥—236.88。
单调关系的未知部分
towardsdatascience.com](/the-subtlety-of-spearmans-rank-correlation-coefficient-29478653bbb9)
二次模型

我们使用 Numpy 的arange来创建从 0 到 9 的 10 个整数。
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inlinetime = np.arange(10)
height = np.array([450,445,430,409,375,331,280,215,144,59])
我们来绘制上面的数据。
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inlineplt.scatter(time,height,label='Height of a ball')
plt.title("Time vs Height")
plt.xlabel('Time')
plt.ylabel('Height')
plt.legend()
plt.show()

二次回归图
numpy.polyfit拟合多项式。它需要 x,y 和拟合多项式的次数。二次为 2,三次为 3,以此类推。它返回一个数组,该数组的多项式系数为常数的高次幂。对于二次函数,它们是 a、b 和 c:

coef = np.polyfit(time, height, 2)
coef

让我们打印出二次回归线。
print(f'The equation of regression line is y=')
print(equ)

或者使用系数,回归线是:
print(f'The equation of regression line is y={coef[0]:.3f}x^2+{coef[1]:.3f}x+{coef[2]:.3f}.')

二次回归方程
我们再次使用 NumPy 的poly1d和polyfit。np.poly1d(coefficients)将使用我们的系数返回一个多项式方程。
equ = np.poly1d(coef)
我们可以找到任意 x 的值。例如,如果您想在 x=1 时找到 y 值:
equ(1)

x=1 时的 y 值
我们用这个来画回归线。我们使用numpy.linspace为 100 个样本定义从 0 到 10 的 x 值。并在equ中使用它作为 y 值。
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inlinex_plot = np.linspace(0,10,100)
y_plot = equ(x_plot)
plt.plot(x_plot, y_plot, color='r')
plt.show()

我们把它们放在一起。
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inlinetime = np.arange(10)
height = np.array([450,445,430,409,375,331,280,215,144,59])coef = np.polyfit(time, height, 2)
equ = np.poly1d(coef)x_plot = np.linspace(0,10,100)
y_plot = equ(x_plot)
plt.plot(x_plot, y_plot, color='r')plt.scatter(time,height,label='Height of a ball')
plt.title("Time vs Height")
plt.xlabel('Time')
plt.ylabel('Height')
plt.legend()
plt.show()print(f'The equation of regression line is y=')
print(equ)

练习 2
通过使用以下数据,在图表中绘制散点图和回归线。求二次回归方程。
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inlineangle = np.arange(20,80,10)
distance = np.array([371,465,511,498,439,325])
回答
你能画一条散点线和回归线吗?二次方程应该是:

二次回归练习答案
使用 Jupyter 笔记本的卡方初学者指南
towardsdatascience.com](/gentle-introduction-to-chi-square-test-for-independence-7182a7414a95)
立方模型

可以用和上面二次函数一样的方法。我们要用plyfit和poly1d。首先,我们准备数据。让我们画一个散点图。
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inlineengspeed = np.arange(9,23,2)
avespeed = np.array([6.45,7.44,8.88,9.66,10.98,12.56,15.44])plt.scatter(engspeed,avespeed,label='Speed of different boat engine')plt.title("Average speed of different boat engine")
plt.xlabel('Engine speed')
plt.ylabel('Boad speed')
plt.ylim(0,20)
plt.legend()
plt.show()

使用polyfit返回系数。对于三次函数,a、b、c 和 d 在:

立方函数
coef = np.polyfit(engspeed, avespeed, 3)
print(coef)

三次函数的系数
我们把所有的放在一起。
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inlineengspeed = np.arange(9,23,2)
avespeed = np.array([6.45,7.44,8.88,9.66,10.98,12.56,15.44])plt.scatter(engspeed,avespeed,label='Speed of different boat engine')coef = np.polyfit(engspeed, avespeed, 3)
equ = np.poly1d(coef)x_plot = np.linspace(8,25,100)
y_plot = equ(x_plot)
plt.plot(x_plot, y_plot, color='r')plt.title("Average speed of different boat engine")
plt.xlabel('Engine speed')
plt.ylabel('Boad speed')
plt.ylim(0,20)
plt.legend()
plt.show()a, b, c, d = coef
print(f'The equation of regression line is y={a:.3f}x^3+{b:.3f}x^2+{c:.3f}x+{d}.')

散点图和三次回归线
练习 3
使用以下数据绘制散点图和三次回归线。打印三次方程。
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inlinex=np.arange(1,8)
y=np.array([0,0.012,0.06,0.162,0.336,0.6,0.972])
回答
你能画出散点图和回归线吗?回归线方程应为:

系数为[3.000000000 e-03,-1.16796094e-16,-9.000000000 e-03,6.00000000e-03]。这些意味着:

第二个实际上是 0。尝试以下方法,看看两者是否都是相同的 0.3。
print(300e-03)
print(300*10**(-3))
指数模型
我们将探索三种指数模型。

让我们设置数据。画一个散点图。
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inlineday = np.arange(0,8)
weight = np.array([251,209,157,129,103,81,66,49])plt.scatter(day,weight,label='Weight change')
plt.title("Day vs Weight")
plt.xlabel('Day')
plt.ylabel('Weight')
plt.legend()
plt.show()

我们要用[scipy.optimize.curve_fit](https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.curve_fit.html)。这需要一个函数,x 和 y 值,和初始值,p0以数组的形式。找到合适的p0需要一点反复试验。你必须测试不同的价值观。我们用p0=(1, 1e-6, 1)。它返回参数的最优值和 popt 的估计协方差。
𝑎⋅𝑒^−𝑏𝑥+𝑐

我们的第一个指数函数使用 a、b 和 c。我们将首先定义一个函数。这在curve_fit方法中使用。对于平滑曲线,我们用 100 个样本使用numpy.linspace从 0 到 7 设置 x 值。
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
%matplotlib inlinedef func(x, a, b, c):
return a * np.exp(-b * x) + cpopt, pcov = curve_fit(func, day, weight, p0=[1, 1e-6, 1])x_plot=np.linspace(0,7,100)
plt.plot(x_plot, func(x_plot, *popt), 'r-')plt.scatter(day,weight,label='Day vs Weight')
plt.title("Day vs Weight a*e^-bx +c")
plt.xlabel('Day')
plt.ylabel('Weight')
plt.legend()
plt.show()# equation
a=popt[0].round(2)
b=popt[1].round(2)
c=popt[2].round(2)print(f'The equation of regression line is y={a}e^({b}x)+{c}')

散点图和指数回归线
𝑎⋅𝑒^−𝑏𝑥

第二个函数使用 a 和 b。我们相应地定义函数。
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
%matplotlib inlinedef func2(x, a, b):
return a * np.exp(-b * x)popt, pcov = curve_fit(func2, day, weight, p0=[1, 1e-6])x_plot=np.linspace(0,7,100)
plt.plot(x_plot, func2(x_plot, *popt), 'r-')plt.scatter(day,weight,label='Day vs Weight')
plt.title("Day vs Weight a*e^-bx")
plt.xlabel('Day')
plt.ylabel('Weight')
plt.legend()
plt.show()# equation
a=popt[0].round(2)
b=popt[1].round(2)print(f'The equation of regression line is y={a}e^({b}x)')

指数回归线的第二个例子
𝑎⋅𝑏^𝑥

最后一个指数函数使用 a 和 b,我们相应地修改函数。
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
%matplotlib inlinedef func3(x, a, b):
return a * b ** xpopt, pcov = curve_fit(func3, day, weight, p0=[1, 1e-6])
x_plot=np.linspace(0,7,100)
plt.plot(x_plot, func3(x_plot, *popt), 'r-')plt.scatter(day,weight,label='Day vs Weight')
plt.title("Day vs Weight a*b^x")
plt.xlabel('Day')
plt.ylabel('Weight')
plt.legend()
plt.show()# equation
a=popt[0].round(4)
b=popt[1].round(4)print(f'The equation of regression line is y={a}*{b}^x')

与 TI Nspire 结果比较
TI Nspire 的指数回归使用转换值 x 和𝑙𝑛的最小二乘拟合,将模型方程 𝑦 = 𝑎𝑏^𝑥 拟合到数据上(𝑦)。它返回不同的值。

实践
使用下列数据找出 ab^x.形式的指数函数,绘制散点图并绘制回归线。
import numpy as npweek = np.arange(1,21)
views = np.array([102365, 38716,21617,24305,9321,14148,2103,8285,5098,3777,831,1007,834,34,378,204,6,42,54,31])
回答


对数模型
半对数模型
通常,我们对指数函数使用半对数模型:

我们设置了模拟数据并绘制了散点图。
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inlinetime = np.arange(0,30,4)
bacteria = np.array([20,150,453,920,1820,9765,15487,19450])plt.scatter(time,bacteria,label='Bacteria')
plt.title("Time vs Bacteria")
plt.xlabel('time')
plt.ylabel('bacteria')
plt.legend()
plt.show()

时间对细菌图
我们将使用[numpy.log](https://docs.scipy.org/doc/numpy/reference/generated/numpy.log.html)对细菌值进行自然记录。numpy.log是自然对数。这应该显示一个线性趋势。我们需要用ln(bacteria)修改标题和 y 标签。
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inlinetime = np.arange(0,30,4)
bacteria = np.array([20,150,453,920,1820,9765,15487,19450])plt.scatter(time,np.log(bacteria),label='Bacteria')
plt.title("Time vs ln(Bacteria)")
plt.xlabel('time')
plt.ylabel('ln(bacteria)')
plt.legend()
plt.show()

我们使用在二次和三次函数中使用的numpy.polyfit。我们在numpy.polyfit()中使用1,这样它将返回一个线性回归。numpy.polyfit返回等式的所有系数。
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inlinetime = np.arange(0,30,4)
bacteria = np.array([20,150,453,920,1820,9765,15487,19450])p = np.polyfit(time, np.log(bacteria), 1)
plt.plot(time, p[0] * time + p[1], 'g--', label='Semi-log graph')plt.scatter(time,np.log(bacteria),label='Bacteria')
plt.title("Time vs Bacteria")
plt.xlabel('time')
plt.ylabel('bacteria')
plt.legend()
plt.show()print(f'The equation of regression line is y={p[0]:.3f} * x + {p[1]:.3f}')

双对数模型
双对数模型用于幂函数。

让我们设置数据并绘制散点图。
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inlinex=np.array([2,30,70,100,150])
y=np.array([4.24,16.4,25.1,30,36.7])plt.scatter(x,y,label='Log-log')
plt.title("Log-Log model")
plt.xlabel('x')
plt.ylabel('y')
plt.legend()
plt.show()

我们使用[numpy.log](https://docs.scipy.org/doc/numpy/reference/generated/numpy.log.html)获取 x 和 y 值的自然对数。我们需要将 x 和 y 标签修改为 ln(x)和 ln(y)。
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inlinex=np.array([2,30,70,100,150])
y=np.array([4.24,16.4,25.1,30,36.7])p = np.polyfit(np.log(x), np.log(y), 1)
plt.plot(np.log(x), p[0] * np.log(x) + p[1], 'r--', label='Regression line')plt.scatter(np.log(x),np.log(y),label='log-log')
plt.title("Log-log regression")
plt.xlabel('ln(x)')
plt.ylabel('ln(y)')
plt.legend()
plt.show()print(f'The equation of regression line is ln(y)={p[0]:.3f} * ln(x) + {p[1]:.3f}')

正弦模型
让我们试试正弦函数。我们设置数据并绘制散点图。既然要用scipy.optimize.curve_fit,那我们也导入一下吧。我们在指数模型中使用了它。我们建立了数据并绘制了散点图。
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
%matplotlib inlineyear=np.arange(0,24,2)
population=np.array([10.2,11.1,12,11.7,10.6,10,10.6,11.7,12,11.1,10.2,10.2])plt.scatter(year,population,label='Population')
plt.title("Year vs Population")
plt.xlabel('Year')
plt.ylabel('Population')
plt.legend()
plt.show()

我们定义一个叫做sinfunc的函数。这需要参数x, a, b, c, d。我们用[numpy.radians](https://docs.scipy.org/doc/numpy/reference/generated/numpy.radians.html)表示c。

import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
%matplotlib inlineyear=np.arange(0,24,2)
population=np.array([10.2,11.1,12,11.7,10.6,10,10.6,11.7,12,11.1,10.2,10.2])def sinfunc(x, a, b, c, d):
return a * np.sin(b * (x - np.radians(c)))+dpopt, pcov = curve_fit(sinfunc, year, population, p0=[1,0.4,1,5])
x_data = np.linspace(0, 25, num=100)plt.scatter(year,population,label='Population')
plt.plot(x_data, sinfunc(x_data, *popt), 'r-',label='Fitted function')
plt.title("Year vs Population")
plt.xlabel('Year')
plt.ylabel('Population')
plt.legend()
plt.show()a, b, c, d = poptprint(f'The equation of regression line is y={a:.3f} * sin({b:.3f}(x-{np.radians(c):.3f}))+{d:.3f}')

实践
使用下面的表格,画一个散点图并找到一个余弦回归函数。

回答

你可能有不同的系数。我用过
逻辑模型

我们收集数据并绘制散点图。我们使用plt.xlim和plt.ylim设置域从-10 到 10,范围从 0 到 250。我们将使用scipy.optimize.curve_fit进行逻辑回归。
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
%matplotlib inlinex=np.arange(0,10)
y=np.array([52,133,203,230,237,239.5,239.8,239.9,240,240])plt.scatter(x, y, label='Regression line')
plt.title("Logistic regression")
plt.xlabel('x')
plt.ylabel('y')
plt.xlim(-10,10)
plt.ylim(0,250)
plt.legend()
plt.show()

我们使用logifunc来定义我们的逻辑功能。我们用curve_fit找到popt中的函数参数。对于回归线,我们为函数设置一个新的域,x_data从-10 到 10。我们使用plt.plot绘制这条线。
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
%matplotlib inlinex=np.arange(0,10.0)
y=np.array([52,133,203,230,237,239.5,239.8,239.9,240,240])def logifunc(x,L,c,k):
return L/ (1 + c*np.exp(-k*x))popt, pcov = curve_fit(logifunc, x, y, p0=[200,1,1])
x_data = np.linspace(-10, 10, num=100)plt.scatter(x,y,label='Logistic function')
plt.plot(x_data, logifunc(x_data, *popt), 'r-',label='Fitted function')
plt.title("Logistic")
plt.xlabel('x')
plt.ylabel('y')
plt.xlim(-10,10)
plt.ylim(0,250)
plt.legend()
plt.show()

当 y 数据点为负时
有时,您的数据在 y 坐标中可能有负值。
import pandas as pddf = pd.read_csv('[http://bit.ly/2tUIZjK'](http://bit.ly/2tUIZjK'))df.head()

数据的最小值必须为零。理想情况下,乙状结肠中点也为零。但是上面的数据集两者都不满足。使用等式(1–2),并添加offset值适用于该数据集。
x=df.T.iloc[0]
y=df.T.iloc[1]def logifunc(x,L,x0,k,off):
return L / (1 + np.exp(-k*(x-x0)))+offplt.scatter(x,y,label='Logistic function')popt, pcov = curve_fit(logifunc, x, y, p0=[50,185,0.1,-222])
plt.scatter(x,y,label='Logistic function')x_data = np.linspace(170,205,num=100)
plt.plot(x_data, logifunc(x_data, *popt), 'r-',label='Fitted function')
plt.legend()plt.show()
print(popt)

结论
scipy.optimize.curve_fit对许多功能都有用。唯一的问题是在p0中找到好的初始值。有时不同的p0值会返回不同的popt。可以试试 LMFIT 。
通过 成为 的会员,可以完全访问媒体上的每一个故事。

https://blog.codewithshin.com/subscribe
参考
- https://realpython.com/linear-regression-in-python/
- https://stack overflow . com/questions/41925157/logisticregression-unknown-label-type-continuous-using-sk learn-in-python
用图形数据库对医疗保健数据建模
使用 TigerGraph 和 Synthea 创建合成医疗保健系统

弗兰基·查马基在 Unsplash 上拍摄的照片
介绍
自从从纸质记录过渡到虚拟记录以来,医院一直在堆积数据。医疗保健系统的每个接触点、每个处方、手术和免疫都记录并存储在医院的电子健康记录中(EHR)。现在已经到了医院数据多到不知如何处理的地步。更糟糕的是,这种复杂数据的过饱和使得访问和分析数据的效率非常低。
那么,这场危机的解决方案是什么?
图形数据库!
图表非常适合存储和可视化医疗保健数据。它们旨在处理高度关联的信息,如病历。如果你对图形不熟悉,看看这篇的精彩文章,它介绍了一些图论的基础知识。
我将通过一个例子展示我们如何使用一个 TigerGraph 图形数据库来表示复杂的医疗保健数据。我们开始吧!
生成数据
在一个理想的世界里,我们可以用真实的病人数据来创建这个图表;然而,有许多规则和条例使得处理患者数据相当困难。而是可以用退而求其次的东西:合成数据。
使用 Synthea ,一个开源的合成病人生成器,我们可以创建一个完整的医疗保健生态系统,其中充满了病人、医院访问、保险提供商以及你能想到的一切。如果你以前从未遇到过 Synthea,看看我写的这篇解释其工作原理的简短文章。
Synthea 的输出数据分为几个 CSV 文件,如过敏、药物、遭遇、供应商等。
有些 CSV 文件的列标题不是列数据的明显指示器。查看 Synthea Wiki 有助于了解每个文件的内容。

样本输出数据格式
实际 CSV 文件的列与文档完全对应。

护理计划的 CSV 输出示例
对于这个医疗保健系统的例子,我生成了来自美国各地的 500 名患者的样本。
创建图表模式

完整的医疗保健模式
这个图表的模式非常复杂(正如您从图片中看到的)。我这样说并不是为了恐吓你,而是为了强调我已经说过的话。
医疗保健数据很复杂!
每个患者都与医疗保健系统有如此多的交互,一个简单的模式肯定无法捕获所有可用的数据和信息。我们的模式必须和我们的数据一样详细。
我们可以使用 TigerGraph 本地语言 GSQL 来创建我们的模式。整个脚本如下所示:
图形模式的 GSQL 脚本
光是这个脚本,我就可以写一整个博客。但是,为了使你免于阅读肯定会非常无聊的读物,我将只注意一些要点。
- 每个 CSV 文件主题都成为具有适当边的顶点。
- 所有的边都是无向的,因为所有的关系都是双向的(即,一个病人有一种药物,但是该药物对应于该病人。)
- 像性别、种族和地址这样的属性可以是内部属性,但是我选择将它们分离出来以优化围绕这些属性的搜索。
- 顶点 SnomedCode 存储使用的每一个医疗代码,这也有助于优化搜索
加载数据
我们可以编写 GSQL 加载脚本来加载我们的数据。让我们看看 CarePlans CSV 文件的加载脚本示例。
GSQL 加载脚本示例
同样,让我们简单地看一下这段代码的重要部分。
- 我们首先定义用来加载数据的文件。
- 然后,我们指定哪些列对应于模式中定义的顶点 id、顶点属性或边属性。
- 最后,我们声明我们的文件有一个头,分隔符是逗号
使用相同的格式,我们可以为其余的数据文件编写类似的加载作业。
对于我们 500 名患者的样本,加载我们所有的数据会产生大约 80 万个顶点和近****200 万条边。
示例查询
关于 GSQL 查询,我不会讲太多细节。我不想把重点放在实际编写查询上,而是放在查询运行的速度上——毕竟,这个博客旨在展示图形数据库的效率。如果你感兴趣的话,我有的另一个博客,里面有很多查询示例。和往常一样, TigerGraph 文档网站是寻找更多信息的好地方。
让我们运行一个简单的查询,获取与给定患者直接相关的所有顶点和边。
示例查询
该查询返回大量信息。它基本上调用了给定患者与医疗保健系统的每个接触点。通常,这对于数据库来说是一项艰巨的工作。但是有了我们的图形数据库,信息在几毫秒内就被检索出来了!真是快得不可思议!
这个速度也适用于大于 500 名患者的数据集。在一个拥有 1 亿患者(哇,这么多数据 ) 的样本系统中,收集相同信息的时间只有几秒钟。
使用图表进行查询非常高效,并且显示出比用于查询医疗保健数据的标准技术有了巨大的改进。
进一步探索
虽然图形数据库确实是保存数据的有效且高效的方式,但它们的好处不仅限于存储。我们已经看到了在大型数据集上执行查询有多快。但是,我们也可以利用图表进行可视化。例如,使用相同的数据库和稍微不同的查询,我们可以很容易地创建这个 3D 网络图,只需要几秒钟的渲染时间。

演示三维网络图
除了看起来很酷之外,这种 3D 视觉效果非常有用。虽然相同信息的 2D 表示会很混乱,无法阅读,但这个 3D 模型提供了一种开放而清晰的方法来查看我们大量相互关联的数据。此外,虽然美学部分是由 HTML 和 JavaScript 组成的,但数据是整个可视化的关键,它存在于图形数据库和查询中。
如果你想知道我是如何实现这个可视化的,看看我的另一篇博文 这里 !
结论
与普通的关系数据库相比,图形是一个很好的选择,尤其是在表示高度关联的数据时。它们非常适合代表医疗保健网络,其中每个患者都与大量数据相关联。如果大规模实施,这项技术可以大大减轻 EHRs 的负担,并使数据的存储、分析和可视化更加有效。图表是医疗数据的未来!
我希望你喜欢这个博客,并学到一些新的东西。让我知道你的想法!
** [## 阿卡什·考尔-数据科学实习生-未来学家学院| LinkedIn
查看阿卡什·考尔在全球最大的职业社区 LinkedIn 上的个人资料。阿卡什有 5 个工作列在他们的…
www.linkedin.com](https://www.linkedin.com/in/akash-kaul-6a8063194/)**
几秒钟内建模:使用 PyCaret 作为数据科学快速决策的工具

弗兰基·查马基在 Unsplash 上的照片
我在浏览数据科学家的空闲时间时偶然发现了 Pycaret。这是一个多功能库,您可以在其中同时应用/评估/调整许多模型。
根据 PyCaret 文档:"py caret是 Python 中一个开源的低代码机器学习库,旨在减少从假设到洞察的周期时间。它使数据科学家和分析师能够高效地执行迭代的端到端数据科学实验,并且由于编码时间大大减少,他们可以更快地得出结论。”
这个库看起来非常类似于 R 中的 caret 库,但是是用 Python 实现的。
在从事数据科学项目时,通常我们需要花很长时间来理解数据(EDA 和功能工程),那么如果我们可以将花费在项目建模部分的时间减少一半会怎么样呢?
我们已经有(许多)关于泰坦尼克号数据集的帖子,所以我将只展示该库的应用。不过,如果你很好奇想了解整个 EDA/数据操纵/特征工程,可以在 Kaggle 或者 GitHub 上查看我的内核。
1.建立图书馆
使用 PyCaret 的第一步是设置环境。这个设置的大部分是自动完成的,但是你可以手动设置一些参数。例如:
- 默认分流比是 70:30,但可以通过“列车大小”进行更改
- 默认情况下,k 倍交叉验证设置为 10
- “会话 id”是我们经典的“随机状态”

setup()函数的输出
setup()的输出是一个表格,其中包含您可以设置的所有可能的选项。如你所见,有很多选择。如果你想尝试不同的方法,这是非常有用的。例如,您可以使用其中一个特征选项来查看模型是否有所改进。
有趣的是,你可以用这个函数预处理你的数据。要查看所有选项,请查看库的文档。
2.比较模型

compare_models()的输出
compare_models()功能允许您一次比较多个型号。对我来说,这是使用 PyCaret 的一大优势。在短短的一行中,你就有了许多模型之间的表格比较。此外,它返回一些指标,如准确性、AUC 和 F1。另一件很酷的事情是库如何自动突出显示最好的结果。
一旦选择了模型,您就可以创建并调整它。在接下来的例子中,我将演示这些步骤。
3.创建和调整您的模型
让我们创建并调整我们的模型,看看库是如何处理它的。


使用 10 重交叉验证的 create_model()(左)和 tune_model()(右)的输出
我们创建了模型,并用 2 行代码对其进行了调整,如上所示。然而,我注意到的一件事是,模型的调整并没有提高我的模型的平均精度。我查了文档,上面写着:
"为了调整超参数,使用了 tune_model()函数。该功能在预定义的搜索空间中自动调整模型的超参数,并使用分层交叉验证对其进行评分。”
我在下面展示了为DecisionTreeClassifier调优模型的函数,因此我们可以对它的功能有一个概念。
from sklearn.tree import DecisionTreeClassifier param_grid = {"max_depth": np.random.randint(1, (len(X_train.columns)*.85),20), "max_features": np.random.randint(3, len(X_train.columns),20), "min_samples_leaf": [2,3,4,5,6],
"criterion": ["gini", "entropy"],}
tune_model()功能将RandomizedSearchCV应用于考虑数据帧大小的模型。
4.模型性能可视化
PyCaret 的有趣之处在于它允许您轻松地评估您的模型性能。对我来说,创建这些图表有点乏味,所以有一个快速的方法来评估我的模型是完美的!有 15 种不同的地块可供选择。请参见下面的一些选项。




使用 PyCaret 可视化模型性能
与前面的步骤一样,生成这些图形的代码非常简单。我在下面的代码中展示了它:
5.混合模型
PyCaret 允许我们实现的另一种方法是混合模型。根据文献记载:blend_models()是一种集成方法,利用估计者之间的共识来生成最终预测。混合背后的想法是结合不同的机器学习算法,并在分类的情况下使用多数投票或平均预测概率来预测最终结果。
我们将尝试看看blend_models()是否会对我们的结果产生任何影响。blend_models()可以和一些预定义的模型一起使用,你可以用estimator_list通过。此外,在Classification的情况下,方法参数可用于定义“软”或“硬”,其中软使用预测的投票概率,硬使用预测的标签。为了简单起见,我就不通过车型列表了。

混合模型时的输出
我们的平均精确度从 0.8301 提高到 0.8316。没什么了不起的,但这是一点进步。
6.堆叠模型
PyCaret 还允许我们stack_models()。在文档中,它将堆叠模型定义为一种使用元学习的集成方法。叠加背后的想法是建立一个元模型,使用多个基本估计量的预测来生成最终预测。
与blend_models()不同的是,stack_models()要求通过estimator_list(),所以我选择了一些型号来尝试这种方法。

堆叠模型时的输出
如您所见,与其他方法相比,平均准确度没有提高。
7.总结
PyCaret 是一个多功能且易于使用的库。您可以用一行代码进行许多模型评估。
在比赛结束时,我使用 PyCaret 的最佳模型的平均准确度为 0.79 。然而,与任何新图书馆一样,仍有改进的余地。我将列出我在使用该库时发现的一些优点和缺点。
优点:
- 这使得项目的建模部分更加容易。您可以用一行代码创建许多不同分析。
- 在调优模型时,忘记传递参数列表。PyCaret 会自动为您完成。
- 您有许多不同的选项来评估模型,同样,只有一行代码。
- 由于它是建立在著名的机器学习库之上的,你可以很容易地将其与你的传统方法进行比较。
缺点:
- 这个库还是第一个版本,所以还不够成熟,容易出现 bug。
- 这是一个黑匣子,所以你真的看不到里面发生了什么。所以,我不会推荐新手使用。这会让学习过程变得有点肤浅。
- 在我的具体例子中,优化模型的函数并没有改进它。如果你们中的任何人对这件事有什么见解,请让我知道,这样我可以更新它。
暂时就这样了。我希望你喜欢这篇文章,如果你想查看我的一些作品,欢迎你访问我的 GitHub 或 LinkedIn 。
建模逻辑增长

冠状病毒的逻辑增长。疾控中心在 Unsplash 拍摄的照片
用 Python 中的 Scipy 对 Logistic 增长函数进行非线性最小二乘估计——以中国冠状病毒数据为例
在之前的一篇文章中,我解释了如何使用指数增长来模拟冠状病毒爆发的传播。它仅限于爆发的第一阶段,因为指数增长的最大限制是它永远不会停止增长。
在本文中,我将介绍这个模型的下一步:逻辑增长模型。如果你想跟着做,你可以在这里得到笔记本,在这里得到数据。
为什么是物流增长?
逻辑增长是一个数学函数,可用于多种情况。逻辑增长的特点是在开始阶段增长增加,但在后期,当你接近最大值时增长减少。例如,在冠状病毒的情况下,这个最大限度将是世界上的总人数,因为当每个人都生病时,增长必然会减少。
在逻辑增长的其他使用情况下,该数字可以是动物种群的大小,该动物种群呈指数增长,直到它们的环境不能为所有动物提供足够的食物,因此增长变得更慢,直到达到环境的最大容量。
使用逻辑增长来模拟冠状病毒爆发的原因是流行病学家已经研究了这些类型的爆发,并且众所周知,流行病的第一阶段遵循指数增长,并且总阶段可以用逻辑增长来模拟。

逻辑增长与指数增长
逻辑增长公式
逻辑增长的特征在于以下公式:

逻辑增长公式
其中:
- y(t) 是任意给定时间 t 的病例数
- c 是极限值,最大容量为 y
- b 必须大于 0
我还列出了这个公式的另外两个有趣的地方:
- 开始的情况数,也叫初始值是 : c / (1 + a)
- 最大增长率出现在 t = ln(a) / b 和 y(t) = c / 2
数学爱好者的逻辑增长公式的深度挖掘
对于那些想对公式有更好的数学理解的人:上面的函数实际上是从皮埃尔·弗朗索瓦·维赫斯特发现的下面的微分公式推导出来的。我会一步一步地解释:

- 绿色部分 dy / dt 表示这个公式不是针对人口规模的,而是针对人口增长的。
- 我们可以看到 y 和 c 在公式中,所以我们理解人口的增长取决于 y 的值(人口规模)和 c 的值(最大容量)
- 当 y 等于 c 时(即群体处于最大规模),y / c 将为 1。因此,蓝色部分将为 0,因此增长将为 0。
- 当 y 比 c 小很多的时候(人口远离极限)蓝色部分将几乎是 1。因此,增长由橙色部分定义。橙色部分实际上是指数增长的公式。
逻辑增长的一个简单例子
为了更清楚地说明这一点,我将举一个假设的例子:
- 最大患病人数 c ,为 1000 人
- 我们从 1 个感染者的初始值开始,所以 c / (1 + a) = 1,给 1000 / (1 + a) = 1 ,给 a = 999
- 在感染开始时,每个病人会感染另外两个人,所以增长率b = 2
- 我们将考察从时间 0 到时间 10 的疫情发展
我们首先需要将 a 和 b 的值代入公式,以获得特定流行病的公式:

在 Python 中,这看起来如下:

然后我们可以用这个公式来计算从 0 到 10 的每个 t 值的 y 值。当我们这样做时,我们在每个时间点获得了以下感染人数,如下表所示。这表明,在开始时,感染人数增长很快,但随后就慢了很多,并在最大容量时结束。

如果我们想用图表来表示,我们开始看到一个图表,看起来很像我们看到的关于冠状病毒的非常惊人的曲线:

逻辑增长图
从冠状病毒数据到逻辑增长公式
现在,我们知道这个图或多或少有正确的形状,但我们需要做一个额外的步骤,使我们的分析有用。我们需要通过查看疫情传播的数据来找到电晕疫情的真实曲线。
在这种情况下,我将采用中国的数据:由于那里的增长已经大幅下降,逻辑曲线将是一个相当好的匹配。

中国冠状病毒数据摘录。来源:https://covid.ourworldindata.org/data/full_data.csv
中国物流增长的非线性最小二乘法
在查看数据时,我们只有每天的病例数。我们也有想要应用的公式,但是我们还没有公式中参数 a、b 和 c 的正确值。
不幸的是,不可能像指数模型那样将逻辑函数改写成线性回归。因此,我们需要一个更复杂的方法:非线性最小二乘估计。
第一步 —读入数据

第二步 —定义需要安装的物流功能

第三步 —随机初始化 a、b、c
在这种情况下,我使用 np.random.exponential,但是您可以使用您喜欢的任何东西。但是请注意:您在这里的选择可能会影响步骤 5 的性能。

第四步 —设置 a、b 和 c 的上下限
所有参数的下限都是 0。我把 b 的上界设为 3,因为在第一次尝试时我把它释放了,它变得太高了。a 和 c 的上限不会对曲线拟合产生负面影响,所以我让它们的上限相对较高。

步骤 5 —使用 Scipy 曲线拟合进行非线性最小二乘估计
在这一步中,Scipy 进行非线性最小二乘优化,并给出使模型的最小二乘误差最小化的 a、b 和 c 的值。

第 6 步 —绘制拟合函数与实际数据
正如你在下面的图表中看到的,逻辑模型与实际的中国冠状病毒数据相差不远。

第 7 步 —结束
我们发现了一个逻辑函数,它与从中国观察到的冠状病毒数据非常接近。为了在现实生活中真正使用这些信息,有必要进行大量的模型验证,比较不同模型的准确性和其他性能指标,并密切关注未来趋势是否符合所选模型。
这将远远超出本文的范围,本文只是试图说明如何拟合逻辑增长曲线,但是,使用该理论,我们可以陈述一些观察结果:
- 根据这个模型,c 是 81802,这将意味着中国感染人数的最大限度将是 81802。
我们还可以根据这个模型计算最大增长率出现的时间:
- 时间上的瞬间是:t =ln(a)/b = ln(61.5)/0.1929 =第 21 天
- 而那个时刻的感染人数是 y = c / 2 = 40500
有了一个完全有效的模型,这种类型的信息可以被决策者用来估计如何采取正确的措施。
我希望你已经清楚如何适应一个逻辑模型,以及如何在不同的用例中使用它。感谢您的阅读,敬请期待更多文章!
编者按: 走向数据科学 是一份以数据科学和机器学习研究为主的中型刊物。我们不是健康专家或流行病学家,本文的观点不应被解释为专业建议。想了解更多关于疫情冠状病毒的信息,可以点击 这里 。
用物理学模拟市场崩溃
使用物理学概念识别市场崩溃的模式

格德·奥特曼德皮查拜的图片。
历史上有很多次市场崩盘(见这份名单)。重要的例子包括 1929 年的大崩盘,被称为“”(见下文):

图 1:1928 年至 1930 年间道琼斯工业平均指数的暴跌。
还有 1987 年的那个“黑色星期一”(见下):

图 2:1987 年 6 月 19 日至 1988 年 1 月 19 日的道琼斯工业平均指数(来源)。
最近,市场在冠状病毒的担忧中暴跌,这是自 2008 年金融危机以来最严重的崩盘:

图 3:最近,道琼斯工业平均指数在冠状病毒的恐惧中暴跌。
撞车是罕见事件的例子。正如这里将显示的,主要是在保罗和巴斯纳格尔之后,崩溃比通常的高斯或李维分布所能解释的更有可能发生。见下面两个分布的同步图。

图 4:高斯分布和李维分布的比较(来源)。
考虑到崩溃,市场的行为可以分为质量上不同的阶段:
- 价格波动相关性很弱的正常交易阶段
- 一个到崩溃的助跑阶段,其特征是价格波动之间的强相关性。
这种行为的变化将与多年来物理学中已知的一种现象相比较:相变。在相变中,有几个物理量发散的临界点。这些点将与市场崩溃进行比较。
崩溃的属性
正如在以前的一篇文章中详细讨论的那样(见下面的链接),股票收益的分布更类似于李维分布,而不是高斯分布。前者有厚尾,在预测崩溃和其他类型的罕见事件时更有效。直观地假设,一次碰撞采样了尾部的非常远的边缘(用极端波动来标识)。

图 5:布朗运动和李维飞行的比较(来源)。
按照 Paul 和 Baschnagel 的说法,我们将把股票价格过程 S ( t )模拟为截断的无漂移李维飞行(参见上文李维飞行和布朗运动之间的比较)。
模拟股票市场的动态
towardsdatascience.com](/statistical-properties-of-stock-prices-15145be752a2)
回想一下上面的文章,截断的李维分布是通过截断通常的李维分布的尾部得到的,使用的截断参数如下

等式 1:截断的李维分布。
其中 N 是一个归一化常数。下图显示了截断的李维分布和标准的李维分布。

图 6:截断的李维分布与标准李维分布的比较(来源)。
根据 Stanley 和 Mantegna 的说法,分布的中心部分将由 p ( l 表示,其中 l 是价格波动,由指数为 α = 1.4 的 l < 6 σ 近似,其中 σ 人们可以按照本文中的算法生成 Lévy 增量:

等式 2:无漂移的李维飞行。增量是从截断的 Lévy 分布中采样的。截止参数为δL = 6σ。
如果分布是对称的,指数 α 不等于 1,那么飞行变成:

等式 3:对称分布的 Lévy 飞行,其中 u 具有[-π/2,π/2]之间的均匀分布,v 具有平均值为 1 的指数分布。
情商。3 是对称分布的 Lévy 飞行,其中:
- u 是在[-π/2,π/2]之间均匀分布的随机变量
- v 具有平均值为 1 的指数分布。
注意截止的存在使得 p( l )的所有矩都是有限的。此外, S ( t→ ∞ )接近布朗运动。
除了最大偏移
让我们先定义某随机过程 x ( t )的最大值函数 M ( t ):

等式 4:给定间隔内样本路径 x(t)的最大值。
为了确定 M ( t )的分布,我们需要定义一个停止时间 T. 如果假设当 x ( t )第一次到达某个数字a**,时过程停止,我们可以定义:

等式 5:进程 x(t)的停止时间。
可以证明(参见 Paul 和 Baschnagel )标准布朗运动的最大值 M ( t )或最大偏移的概率密度为:

等式 6:标准布朗运动的最大值 M ( t )的概率密度。
在评估金融市场的风险时,这一结果至关重要。
我们对方程中参数的选择。如图 1 和 2 所示,极端事件的高斯行为是在大约 100 步的李维过程之后获得的。换句话说,价格波动的非高斯特征很快就消失了。
下降 人们也可以使用下降的分布来衡量极端事件。下降是价格时间序列中从相对最大值到下一个相对最小值的百分比下降。请注意,水位下降方法避免了在极端偏移分布分析中出现的固定时间范围问题(因为它对极端情况之间出现的不同时间延迟进行平均)。

图 7:提款 D 的相对数量 N(D)的线性对数图(来源)。
现在,在图 8 中,观察在 DJI 发现的两种状态。对于 D < 15% 保罗和巴斯纳格尔很好地符合一个指数。然而,两个图的比较表明,与模拟数据相比,DJI 在大约 30 %水平上的下降频率 D 大约高 10 倍。模拟数据预测提款的可能性要高得多。

图 8:DJIA 下降次数 N(D)与下降幅度 D 的对数线性图。这项分析每天进行,范围从 1900 年到 1994 年(来源)。
制度变化和相变
根据这些数据,有人推测(如引言中所述),这些罕见事件不仅仅是“正常”统计中的异常值,相反,它们表明了两种状态之间的转换:一种状态具有“正常”动态,另一种状态具有巨大的波动。

考虑图 9 中的以下两个崩溃。它们显示了 DJIA 在 1929 年和 1987 年崩盘前后的每日收盘报价。我们注意到这两个价格序列在崩盘前都是逐步上升的。这些阶跃是周期越来越小的振荡,在接近实际碰撞时消失(换句话说,随着碰撞时刻的临近,阶跃变得越来越短)。这种行为似乎是崩溃的前兆。

图 9:DJIA 接近 1929 年和 1987 年崩盘时的每日收盘报价。我们注意到,在崩溃之前,这两个系列都在逐步增加。此外,当我们接近崩溃时,步骤变得更短(来源)。
此外,价格下跌的幅度表明,它们不仅仅是“正常”价格序列的罕见统计波动。
合作性和关键性
当崩盘即将到来时,大量交易者自发地决定卖出他们的股票(有一种“合作性”),动摇了市场的流动性。需求不匹配导致价格下降,市场进入非均衡状态。似乎发生的情况是,交易员之间形成了相关性,从而引发了随后的崩盘。
在热力学和统计力学中,当系统接近所谓的系统临界点时,会发生极其相似的现象。在这篇文章中,作者建立了一个简单的模型来提供市场行为和热力学之间的联系。
Cont-Bouchaud 模型 考虑市场中平均交易量相同的 N ( t )个交易者。交易者 i 可以:
- 如果 ϕᵢ = 1 则买入
- 卖出 if ϕᵢ = -1
- 如果 ϕᵢ = 0,请等待
供求差额由下式给出:

等式 7:供给和需求之间的差异。
提出了两个假设:
- 价格变化被假定为与供需不平衡成正比(这是小δS 所服从的)。
- 比例常数衡量市场对供求差异的敏感度(所谓的市场深度)。
通常,在市场中,交易者会相互“互动”。这种相互作用以两种不同的方式发生:
- 直接接触
- 间接的,通过跟踪价格的变化。
由于这种互动导致两个交易者采取共同的策略(他们要么买入,要么卖出,要么保持不动),他们之间就产生了所谓的“纽带”。债券产生的概率是:

等式 8:债券创造概率,这对于大市场来说是个小数字。
考虑到 N(t) >>1,这是一个很小数量。参数 b 衡量交易者与同事互动的意愿。下一步是连接随机生成聚类的两个交易者,每个人对他们的活动都有不同的看法。
其中 ϕ ( c )=+1 或-1 包含活跃仓位的交易者,而ϕ(c)=0 包含非活跃仓位的交易者。参见图 10。

图 10:导致集群形成的步骤(改编自来源)。
等式中的和。7 可以重写为在 N ( c )簇上的和:

等式 9:等式中的和。7 以簇的形式书写。s 系数是聚类的大小。
其中 s ( c )是每个集群的大小。现在,我们将借用逾渗理论的一些结果,

图 11:逾渗(来源)。
根据该方程,无限程键逾渗模型的团簇尺寸概率分布为

方程式 10:无限范围键渗透模型的团簇尺寸概率分布。
这对 b 大约 1 有效。值 b =1 是逾渗阈值,有限簇具有 b < 1。当 b =1 时,一个单独的星系团出现并渗透整个系统。这是一个临界现象的例子,其中强时间相关性产生不同的动态机制。当所有的交易者都联系在一起时,就会发生渗透(平均而言)。如果b1,更多的交易者进入集群,开始控制整体行为。如果这个全跨度集群的成员决定出售,新的动力就会导致崩盘。我们必须有b1 来规避这样的场景。但是,我们必须记住,b 必须保持接近 1。否则,截断的 Lévy 行为的保存将被破坏。似乎发生的是,内在的市场动态使 b 接近 1。市场的本质应该是这样的,它将系统推向 b ≈ 1,并在“正常”行为期间将其保持在临界区域。现在,物理学充满了显示这种行为的系统。许多模型在没有任何外部控制(如温度)的情况下逐渐向动态吸引子演化,当它们接近该吸引子时,它们表现出临界行为,表现出幂律和厚尾 pdf。这里的类比是精确的,因为是系统本身的动态将它带向临界点。这叫做自组织临界。

图 12:森林火灾模型是显示自组织临界性的系统的一个例子(来源)。
因此,我们研究的模型认为股票市场是一个自组织的临界系统!
我的 Github 和个人网站 www.marcotavora.me 都有一些关于金融和其他主题的有趣资料,比如数学、数据科学和物理。看看他们!
用神经网络建模非线性动态系统

1.介绍
在 数学和科学中,非线性系统是一个系统,其中输出的变化与输入的变化不成比例。工程师、生物学家、物理学家、数学家以及许多其他科学家都对非线性问题感兴趣,因为大多数系统本质上都是非线性的。描述变量随时间变化的非线性动力系统,与简单得多的线性系统相比,可能显得混乱、不可预测或违反直觉。
我们将以工程系统为例,从统计/最大似然的角度来评估系统模型。工程系统的设计由原始设备制造商根据物理学/工程学的基本原理完成。数据科学家只获得输入和输出的集合。系统的建模——正式的系统识别——没有原始设计、领域知识和 OEM 的帮助;因此是不可能的。
那么,有没有可能根据 I/O 信息对系统建模;对于数据科学的非线性和动态系统来说也是如此吗?答案是肯定的,但即使在这种情况下,也需要一些领域专业知识来理解数据特性及其背后的数学基础。
仅基于观测数据的模型构建,没有关于其结构的任何详细信息;其特征仅在于其输入和输出,被称为黑盒模型。模型结构的元素没有物理意义。
当模型的结构和参数都完全已知时——完整的物理知识是可用的——我们就有了一个白盒模型。白盒模型可以根据先验信息构建,而不需要任何观测值。这也被称为实际系统的数字孪生或模拟。
2.什么是 NARX——非线性自回归外生建模?

图 NARX 模型的串并联架构。@版权
ARX 是自回归外生(ARX)的非线性推广,是线性黑箱系统辨识的标准技术。NARX 模型可用于建模各种各样的非线性动态系统。它是自动回归的,因为它的输出是以前值的回归。如果预测的映射函数是非线性的,则该模型称为 NARX。非线性映射可以是神经网络、高斯过程或其他。

图二。非线性映射函数。@版权
非线性映射函数被写成已知基函数的线性组合的情况将识别问题简化为线性回归。这是 NARX 模型非常受欢迎并成功应用于许多工业问题的一个原因。
对于静态系统,离散时间步长的输出仅取决于同一时刻的输入。静态网络可用于静态非线性系统建模。在动态系统中,给定时刻的输出不仅取决于当前的输入,还取决于系统先前的行为。动态系统是有记忆的系统——它们会随着时间而变化。
基于神经网络的 NARX 的架构如图 1 和图 3 所示。输入和输出都用抽头延迟线延迟。如果在输出信号路径中使用抽头延迟线,则可以构建反馈架构,其中前馈网络的输入或一些输入由网络的延迟输出组成。由此产生的网络是一个循环网络。该架构如图 3 所示。基本 NARX 网络用于多步预测。
假设目标的实际过去值是不可用的,并且预测本身被反馈到网络。因为在训练期间,我们可以访问实际的目标值,所以我们可以使用这些值来代替我们过去的预测。这有助于系统根据实际值而不是预测值进行训练。这是通过使用 NARX 网络的串并联版本实现的,如图 1 所示。该架构有两个优点。
首先是前馈网络的输入更加精确。
第二个是所得到的网络具有纯前馈结构,因此;静态反向传播可用于训练。

图 3。NARX 模型的并行体系结构。观察预测被用作延迟输入的闭环系统。@版权
3.发电厂示例
到为了让理解更具体,我们来建模一个电厂系统。数据来自 120MW 发电厂,参考 De Moor B.L.R .(版。)、 DaISy:用于系统识别的数据库,URL:http://homes.esat.kuleuven.be/~smc/daisy/,一座 120 MW 发电厂(法国桑布尔河畔)的数据。
数据采样时间为 1228.8 秒,大小为 200 个样本。输入有
1.气流
2.涡轮阀打开
3.过热器喷雾流量
4.气体阻尼器
5.气流
观察结果是
1.汽压
2.主阀杆温度
3.再热蒸汽温度
图 4 显示了电厂过程的输入传感器数据。

图 4。电厂工艺系统的输入
使用两个样本延迟、10 个隐藏神经元、Sigmoid 激活和 levengerg 损失函数来训练 NARX 网络。过去的外源输入和实际的目标观测值被用来预测当前观测值。如果没有记录实际目标观测值的过去值,网络将使用过去的预测值。但是,不建议用于工业先进过程控制的用例,因为预测会迅速偏离预期的估计。

图 5。输出预测和与观测值的比较。结果来自开环串并联架构。
结论
系统识别问题对于数据科学家来说是一个非常复杂的难题。
NARX 模型是能够以合理的精度对复杂动态系统进行建模的方法之一。
为了解决这些问题,需要用数据科学来增强领域专业知识、过程工程技能。
我们看到了一个串并联架构发电厂的建模示例。
下一步怎么样
还有哪些方法可以用来模拟非线性动态系统?将在下一篇文章中捕获它。
参考
- Luigi Piroddi 博士,“非线性模型识别”课程讲义
- 肖肯斯 1 和伦纳特·荣,非线性系统辨识,arXiv:1902.00683v1 [cs .2019 年 2 月 2 日
- https://www.youtube.com/channel/UCAhRo1cCl1r_dFMVYaxTh5Q
使用可扩展和分层的变分推理对商店价格建模
在本文中,我将使用来自 Kaggle 的 Mercari 价格建议数据,通过在 PyMC3 中实现的自动微分变分推理来预测商店价格。这个价格模型是由 Susan Li 在的这篇文章中使用 PyStan 完成的。我的一些作品受到了她的代码的启发。
我将更多地解释数据集、我执行的转换、我选择的模型以及我从中获得的准确性。请注意,本文的目的是探索不同的技术来更好地建模价格,并思考一些更好地构建数据的方法。我不会实现所有这些技术,但是我会在整篇文章中讨论它们。
价格建模的用例
每天,电子商务网站上都会发布数百个新的房源。因为电子商务网站竞争激烈,所以上市价格有很大的差异,如果上市价格不在一般客户的承受能力范围内,就非常容易失去一个客户。影响产品价格可行性的因素有很多。这包括产品的类型、状况、品牌以及免费送货等服务的可用性。在这个作业中,我们将把这些物品的价格作为这些因素的函数来建模。
预测列表的价格对电子商务公司非常有帮助,原因如下:
- 客户的价格决定会影响 Mercari 获得的在线流量,这与他们的声誉和广告利润等相关。因此,挂牌价格必须是最优的,而不是由客户随机生成的
- 知情的价格预测有助于卖家获得大量的客户关注,从而增加快速销售产品的机会,而无需等待更长的时间。
- 价格预测工具还改善了销售者和顾客的产品体验,因为价格是合理的,并且销售者不必花费大量精力来计算价格和降低价格以吸引顾客。
- 因此,价格预测工具将对卖家和客户的体验产生重大影响,同时提高 Mercari 的盈利能力和声誉。
数据
这些数据是从 Mercari 的 Kaggle Challenge 获得的,用于建立一个建议模型,根据客户提供的信息预测列表的价格。原始数据集包含大约 140 万个数据点。(梅尔卡利,2018 年)
对于每个数据点,我们都获得了以下指标:
- 条件 ID:指定产品的条件(从 1 到 5 不等)。
- 名称:列表的名称
- 类别名称:产品的一般类别。大约有 1500 个不同的类别。
- 品牌名称:品牌的名称,如果没有品牌,则为 Nan。
- 价格:客户列出的价格。
- 航运:如果航运可用
- 项目说明:列表的说明
数据清理、整合和转换
为了更好地分析数据,我们执行一些操作来合并文本数据:
- 数据中大约有 1500 个不同的类别。对所有这些类别建模是不现实的。因此,我没有使用 sklearn 的标签编码器,这是许多在线模型建议的选择。相反,我手动探索了这些数据,并将其分为 8 大类:男性、女性、电子产品、运动、美容、儿童、家居和其他。使用实际类别中的关键字,我将每个类别归类为八大类别代码之一。
**# Loading and processing the data here**
df = pd.read_csv(‘train.tsv’, sep=’\t’)
df = df[pd.notnull(df[‘category_name’])]category_codes = []**# Analysing the words in each category and classifying them as either of the 8 numerical categories**for i in df.index:
category = df['category_name'][i]
if 'Men' in category:
category_codes.append(0)
elif 'Women' in category:
category_codes.append(1)
elif 'Electronics' in category:
category_codes.append(2)
elif 'Sports' in category:
category_codes.append(3)
elif 'Beauty' in category:
category_codes.append(4)
elif 'Kids' in category:
category_codes.append(5)
elif 'Home' in category:
category_codes.append(6)
else:
category_codes.append(7)df['category_code'] = category_codes
注意,上面的方法不是最佳的,但它是一种简单有效的情感分析方法。人们可以更深入地对每个产品的 category_name 和 item_description 进行情感分析,这也将加强处理后数据的质量。例如,可以分析诸如“良好状态”或“工作正常”等词的存在,并给它们打分/评级。不同种类的单词的存在将告知每个产品的总得分,这可以成为我们数据的一个特征。
- 条件 id 和运输代码未更改,因为它们是可用于分析的分类变量。
- 我不会在此分析中使用商品名称,但也可以使用情感分析来提取相关信息,如产品的尺寸或功能,以强化其价格。
- 对于品牌,可以建立一个简单的分类器,将较大的品牌分类为“2”,较小的品牌分类为“1”,没有品牌分类为“0”。为了简化模型,我不会实现这个策略,但是我鼓励您尝试一下。
- 我们把价格转换成对数标度。这使得价格分布更加平滑,从而便于建模。这也很重要,因为价格有一个乘数。你可以在这里阅读更多相关信息。但本质上,价格是通过乘以一个因子而变化的。例如,如果可以发货,价格将乘以 1.1。在贝叶斯统计中,对这些乘法因子进行建模更加困难,因为它们在采样期间更有可能发散。因此,对数标度将乘法因子转换为加法因子,我们可以使用线性回归对其建模。也使得价格成为实数线上的连续变量。
更好地理解数据
让我们看一下价格和对数价格分布,直观地了解这种转变:

价格与实际价格的对数分布。来源:自己的工作
在上图中,我们可以看到对数价格的分布类似于更容易建模的正态分布。

按航运可用性划分的原木价格分布。
上图挺有意思的。免运费产品的平均价格实际上小于免运费产品的平均价格,但我们可以预期相反的情况,因为运费会增加价格成本。这有两个问题:
- 这些分布并不完全是正态分布,因此,执行 t 检验或比较平均值并不是比较这些分布的最佳方式。
- 这里的比较分析也可能被混淆。可能的情况是,我们的无运费产品数据包含一般较便宜的产品,或者包含在分销中占主导地位的特定类别的产品。例如,大多数免运费的产品通常更便宜(也可以很容易地运送),而更重、更大和更贵的产品通常没有免运费。因此,运费为 2 美元的 10 美元产品会被宣传为 12 美元,但更贵的产品是没有运费的产品,它们的价格(虽然不包括运费)会比包含运费的更便宜的产品高得多。
以上是数据中结构偏差的一个经典例子,这种偏差源于我们作为人类所做的选择。作为一名数据科学家,理解并解释这些源于非统计选择的偏差非常重要。没有简单的方法来纠正这种偏见。一种方法是执行个体匹配。基本上,我们会浏览数据集,挑选看起来完全相同或非常相似的记录。他们之间唯一的主要区别是免费送货的可用性。因此,我们将构建一个包含两个子集的新数据集,两个子集的产品分布非常相似,但一个没有发货,而另一个有更多发货。
您可以在文章末尾的笔记本中找到这个可视化的代码。欢迎您绘制自己的可视化图,并思考数据集中可能存在的偏见。
车型
我们先来考虑一个简单的线性回归。(虽然线性回归看起来非常简单,但它们功能强大且可扩展。因此,数据科学家更喜欢用线性回归建模的方式处理他们的数据。)

这里,c 是模型中的常数,而 a 和 b 是运输系数和条件系数。为了使它变得现实,我将使用变量池。这意味着我将为每一类产品创造不同的 a、b 和 c 值。因此,“电子产品”将有自己的一套参数,“家居装饰”也将有自己的一套参数。如果我没有对一个或多个参数进行池化,这将被称为部分池化。让我们更新等式:

这里,[8]表示参数反映了 8 个不同的参数,每个参数对应一个指定的类别。
a、b、c 的含义:
我们用 a、b 和 c 建立了一个模型,但是它们是什么意思呢?就线性回归而言,c 是截距,而 b 是条件系数和运输系数。在价格方面,“c”就像是每个类别的基础价格。此类别中的每个产品都有一个基准价格。该基线受产品状况的影响。这种影响的规模由‘b’控制。原木价格还受到运输的影响。这种影响的规模由“a”控制
建模基础设施
既然我们已经定义了模型的基本结构,我们需要构建基于这些参数的基础先验分布。我将在这里使用层次模型,因为层次提供了值的灵活性,并允许同一参数的不同值有它们的分布,而不是依赖于同一个值。
例如,每个“a”将从平均值和标准偏差的正态分布中取样,这些正态分布将从它们自己的先验中进一步取样。因此,每个“a”都有自己的正态分布,这对于满足联营假设很重要。否则,它们可能会相互关联,从而威胁到模型的有效性。

这里‘a’的超上级是 mu 和 sigma。μ是从正态分布采样的,而标准差是从 0 到 10 之间的均匀分布采样的。请注意,标准差必须为正,因此,我们不能使用正态先验,因为正态分布的支持是整个实数行。这些先验被选为未知先验,因为我们对这些系数或它们的影响知之甚少。我们将让模型找到并收敛于最佳可能值。
我们也为“b”和“c”复制相同的模型。我们将正态分布置于两者之上,并通过其超参数“mu”和“sigma”进一步参数化。最后,似然性被建模为另一个正态分布,该正态分布被置于具有噪声“sigma”的最终线性方程上。

执行推理
现在我们已经建立了我们的模型,我们想进行推理,以获得 a、b 和 c 的后验分布,它通知我们理解模型的能力。我们可以允许 MCMC 从中生成样本,或者我们可以执行变分推断,将这些分布放在我们的参数上,并优化它们的超参数值。让我们从这个分析中回顾一下我们的目标:对产品价格进行可伸缩的推断。
对 MCMC 及其缺点的思考;
Susan Li 在的这篇文章中使用 PyStan 进行了类似的分析。PyStan 是一个很棒的工具,它使用一个 NUTS 采样器来执行自动哈密尔顿蒙特卡罗采样。但是 HMC 是一个非常慢的算法。在这个数据集中,我们有大约 140 万个数据点。这使得算法的平均运行时间约为 2-3 小时。虽然有不同的方法来优化 PyStan 的效率,但它不是一个可伸缩的算法。你可以在这里了解更多关于 PyStan 的线性回归。
PyStan 的另一个重要限制是缺乏可伸缩的适应性。随着新数据的生成和处理,我们希望我们的算法能够实时更新它们的参数,以便模型每天都变得更加智能。我们也希望这一过程快速高效地进行。因此,PyStan 可能不是在线市场模式的最佳选择。你可以在这里阅读更多关于 PyStan 的信息。
自动微分变分推理
如果你不太了解 MCMC 或者变分推理(VI),你应该读一下这篇文章。总而言之,VI 对给定的参数进行分布,然后优化这些分布。
VI 使用了一种叫做 KL-Divergence 的东西,它是一种成本函数,可以帮助我们理解我们的结果应该是什么样子,以及我们想要达到的目标之间的距离。但是 VI 不是全自动的,需要微调。因此,我们将使用一个称为自动微分变分推理(ADVI)的 VI 版本,它优化了一个不同的参数(类似于 KL-Divergence,但更容易优化)。该算法使用不同的库来区分 ELBO(我们这里的度量)并找到最大值来更新参数。它使用坐标下降(沿坐标轴优化)来实现参数更新方程。我知道这是很多信息,但是我推荐你阅读这篇文章来熟悉它。
这里有一个重要的注意事项:当我们在 VI 中执行推理时,我们对定义每个模型参数(a、b 和 c)的分布的变分参数(这里是超参数)执行推理。因此,模型变量成为一个过渡参数,其分布通过对变化参数(所有 mu 和所有 sigma)的推断来确定
在 PyMC3 中建立模型
对于这个实现,我们不会从头开始构建 ADVI 算法。相反,我们将使用 PyMC3 的内置特性来构建我们的模型,并指定超先验、先验、模型和推理策略。
为了使推断更快,我们将在 PyMC3 中使用迷你批处理。迷你批次减少了训练数据集的大小。在初始设置中,参数更新将要求参数查看整个训练数据集。不幸的是,我们有 140 万个数据点要看。对于大约 25000 次迭代(我们的目标),这是一个很大的工作量。因此,迷你批次允许我们从训练数据集中取样。他们创建训练数据集的小批量(大小为“b”),并且每次更新的训练都在小批量上进行。每次迭代后,批次都会更新。
经过大量的迭代,微型批次在优化 ELBO 方面变得非常有效,因为更新方程几乎可以查看每个数据点,但不是同时查看所有数据点,这使得该过程更具可扩展性。
下面是为我们构建模型的代码。(我的灵感来自于 PyMC3 网站上提供的这个分层部分池示例,其中一些代码是从他们的笔记本中获得的。)
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns; sns.set()
import numpy as np
%matplotlib inline
%env THEANO_FLAGS=device=cpu, floatX=float32, warn_float64=ignore
import theano
import theano.tensor as tt
import pymc3 as pmcategory_code_new = df.category_code.values
shipping = df.shipping.values
condition_id = df.item_condition_id.values
log_price = df.log_price.values
n_categories = len(df.category_code.unique())**# Minibatches below. The batch size is 500**log_price_t = pm.Minibatch(log_price, 500)
shipping_t = pm.Minibatch(shipping, 500)
condition_t = pm.Minibatch(condition_id, 500)
category_code_t = pm.Minibatch(category_code_new, 500)**# Setting the hyperpriors (mu,sigma) for a,b,c**with pm.Model() as hierarchical_model:
mu_a = pm.Normal('mu_alpha', mu=0., sigma=10**2)
sigma_a = pm.Uniform('sigma_alpha', lower=0, upper=10) mu_b = pm.Normal('mu_beta', mu=0., sigma=10**2)
sigma_b = pm.Uniform('sigma_beta', lower=0, upper=10) mu_c = pm.Normal('mu_c', mu=0., sigma=10**2)
sigma_c = pm.Uniform('sigma_c', lower=0, upper=10)**# Setting the priors here. Note the shape variable.
# The shape must be equal the number of categories. (8 here)**with hierarchical_model:
a = pm.Normal('alpha',mu=mu_a,sigma=sigma_a, shape=n_categories)
b = pm.Normal('beta',mu=mu_b, sigma=sigma_b, shape=n_categories)
c = pm.Normal('c', mu=mu_c, sigma=sigma_c, shape=n_categories)**# Setting the likelihood here as another gaussian**with hierarchical_model:
**# Estimated log prices** log_price_est = c[category_code_t] + b[category_code_t] *
condition_t + a[category_code_t] * shipping_t **# Noise or standard deviation of the likelihood**
noise = pm.Uniform('eps', lower=0, upper=100) **#Likelihood function. Note that we still need to provide the
# total size of the dataset for convergence check.** lp_like = pm.Normal('lp_like', mu=log_price_est, sigma=eps,
observed=log_price_t, total_size=len(df))
优化时间!
让我们在 PyMC3 中将优化策略定义为 ADVI,并执行推理。
from pm.callbacks import CheckParametersConvergence**# Defining inference method and parameter checks**with hierarchical_model:
inference = pm.ADVI()
approx = pm.fit(100000, method=inference, callbacks=
[CheckParametersConvergence()])plt.figure(figsize=(12,6))
plt.plot(-inference.hist, label='new ADVI', alpha=.8)
plt.plot(approx.hist, label='old ADVI', alpha=.8)
plt.legend(fontsize=15)
plt.ylabel('ELBO', fontsize=15)
plt.xlabel('iteration', fontsize=15);

ELBO 在多次迭代中的收敛性。旧 ADVI 和新 ADVI 提供了对每次迭代级别的优化的深入了解。来源:自己的工作
从图中,我们可以说 ELBO 收敛得很好,并在大约 25000 次迭代时达到最小值。我们观察到最小值出现在 490.22,而最大值出现在 4706.71。你可能会问——在 10000 次迭代左右,ELBO 中的峰值是什么?这些更新违背了我们的目标。发生这种情况可能有多种原因,最有可能的是微型批次中的巨大偏差,导致参数更新偏离其轨迹。但正是随机化的美妙之处(和数学原理)帮助我们最终得到正确的结果。
变分参数的后验分布
还记得我们没有执行 MCMC 吗?是的。所以我们得到的不是样本,而是变分参数的值,它们是模型参数的超先验,然后也是模型参数的超先验。我们通过拟合模型得到的变量“近似值”将为我们提供每个参数的平均值和标准偏差。我们将获得一个均值和标准差字典,可以用参数值进行索引:
means = approx.bij.rmap(approx.mean.eval())
sds = approx.bij.rmap(approx.std.eval())a_mean = means['alpha']
a_std = sds['alpha'] **# for each model and variational parameter**
我们对变分参数的分布不太感兴趣,因为它们的先验不是动态的。我们将快速浏览一下它们的均值和标准差:
- mu_a ~ N(-0.00022505,0.7320357)
- mu_b ~ N(6.097043e-05,0.7320404)
- mu_c ~ N(0.00212902,0.7320338)
正如所料,它们以 0 为中心,因为这些管理部门的先验也以 0 为中心。标准差已经重新调整了(从最初的 10 到小得多的 0.732)。所有这些意味着分布彼此非常接近。因此,a、b 和 c 的实际值是从彼此非常接近的高斯样本中采样的。需要注意的重要一点是,虽然均值的后验概率是相似的,但基于所执行的推断,模型参数的实际后验概率可能会非常不同。
模型参数的后验分布
我们使用下面的代码来绘制后验分布的 PDF。(这里不能做直方图,因为我们没有一组样本。)
from scipy import stats**# List of categories**
category_names = ['Men','Women','Electronics','Sports','Beauty',
'Kids', 'Home', 'Other']**# Model parameters names**
model_param = ['alpha','beta','c']fig, axs = plt.subplots(nrows=3, figsize=(12, 10))**# Plotting a set of PDFs for each Model parameter**
for var, ax in zip(model_param, axs):
mu_arr = means[var]
sigma_arr = sds[var] ax.set_title(var) **# Title of the subplot** for i, (mu, sigma) in enumerate(zip(mu_arr.flatten(),
sigma_arr.flatten())):
sd3 = (-4*sigma + mu, 4*sigma + mu)
x = np.linspace(sd3[0], sd3[1], 300)
y = stats.norm(mu, sigma).pdf(x) **# PDF of the parameter**
ax.plot(x, y, label='Coefficient of {}'. format(
category_names[i]))
ax.legend()
fig.tight_layout()
以下是混合模型参数的后验分布:

让我们分析模型参数:
- 对于基准价格(c),男装的基准最高,其次是女装、家居装饰和运动。其他产品有最便宜的基线。这可能是因为较小的杂项产品没有很多价值,因为它们不属于任何主要类别。
- 对于 beta (b ),产品条件对某些类别的影响是积极的,而对其他类别则是消极的。更重要的是,它是对数线性模型中的一个系数。因此,这些系数代表实际价格的变化百分比。恶劣条件的负面影响最大的是女装,恶劣条件会导致价格下降 15%。这同样适用于男装和家居装饰。运动和童装下降了 5%。有一个电子价格的增长(约 12%)与一个坏的条件,这是违反直觉,但同样,这将需要验证匹配。
- 对于阿尔法(a),免费送货的影响很大程度上是负面的(正如我们在之前的初始分析中看到的)。男装的负面影响最小(约-18%),而电子产品的负面影响最大(约-50%)。你应该更多地考虑数据的结构偏差,以及类别是否会加强偏差。提示:大多数电子产品需要更多的运输/处理费用,因此不提供免费运输。
从上述分析中,我们可以得出结论,最违反直觉的结果出现在电子产品和其他产品类别中,而其他产品没有出现极端结果。为了更好、更准确地建模,对该数据集执行匹配是值得的。
不确定因素
从后验分布可以看出,很多分布是非常确定的(比如女装的参数)。另一方面,运动产品的参数更加不确定(标准偏差为 0.025,约为实际价格的 2.5%变化)。因此,当我们根据这些结果建立预测模型时,我们需要小心这些不确定性,并将它们纳入我们的结果。
一个简单的预测模型
从技术上来说,我们可以从我们的线性回归结果中建立一个确定性预测模型:
means = approx.bij.rmap(approx.mean.eval())def predict(shipping, condition, category):
c = means['c'][category]
b = means['beta'][category]
a = means['alpha'][category] return np.exp(c + b*condition + a*shipping)
但是 VI 没有提供预测区间,而 MCMC 提供了。要获得预测区间,我们只需构建 a、b 和 c 的置信区间。然后,我们使用这些区间来评估相同输入在 a、b 和 c 的下限/上限处的相同预测。
与 HMC 坚果取样器结果的比较
在同一个模型上,我们也可以执行 HMC 螺母抽样来获得后验分布。下面是实现这一点的代码:
with hierarchical_model:
step = pm.NUTS(scaling=approx.cov.eval(), is_cov=True)
hierarchical_trace = pm.sample(100000, step=step,
start=approx.sample()[0], progressbar=True,
tune=1000, cores=4)pm.summary(hierarchical_trace)
请注意,PyMC3 的 NUTS 采样器(主要是 summary 函数)有一些错误,因此,我没有广泛地使用它,但您仍然可以使用 MCMC 获得一些关于我们结果的收敛性的很好的见解。
剧透警告:锁链不会收敛。原因可能在于使用了微型批次,这导致链分叉,因为 MCMC 并不完全执行更新参数的迭代。它可以创建许多您想要生成的样本。因此,如果样本是基于不同的小批次生成的,这组样本可能会变得非常不同(就是这种情况)。在这种情况下,您将需要更多的迭代和链来完成收敛。
未来的步骤和改进
有许多方法可以改进目前的模式。我们讨论了一些更好地分析文本数据的方法。以下是 Mercari 在数据收集方面可以做得更好的一些事情。Mercari 还可以在收集更多准确且易于处理的数据方面做得更好。我们可以设置 5 个问题来帮助用户更好地对产品进行分类,而不是要求键入描述。这也将使我们能够收集易于分析的良好数据。例如,我们可以问这样的问题:“产品有多长时间了?”,‘产品有用吗?’、‘是翻新的还是有保修的?’,‘你买的时候花了多少钱?’。等等。
2.在构建价格预测器时,还应考虑客户的偏好。每个顾客在等待时间、预期价格、最初支付的价格、情感/社会价值(属于名人)等方面都有一定的偏好。因此,价格应该根据客户的偏好和情况进行调整。
测试笔记本
在这里随意克隆 Python 笔记本并测试不同的策略。
注意:如果你有任何问题或者你想联系,我的 LinkedIn 个人资料是这里的。
模拟摆动概率

戴戟·梅本在 Unsplash 上的照片
多年来,棒球的一个方面让我着迷,那就是击球手和投手在击球时进行的心理博弈。每个玩家都在不断地试图进入对手的大脑,猜测他们下一步可能会做什么。击球手可能会利用他对投手的了解来预测他是否会尝试用快速球挑战击球手,或是引诱他将突破球追出区域。与此同时,投手正在利用关于击球手的信息来制定一系列投球,这些投球应该会让他因三振出局而回到替补席。
正是在这些智力游戏中,我发现统计学和 sabermetrics 可以得到最有效的应用。投手或击球手拥有的相关数据越多,他们的优势就越大。在我之前的一篇文章中,我量化了投手隐藏投球的程度,并讨论了击球手如何利用这些信息来识别投手可能会投出什么(不需要垃圾桶,太空人)。然而,在这篇文章中,我想调查投手如何利用打者的信息来给自己一个优势。特别是,我将试图测量某个击球手在给定投球时挥棒的概率。
型号:
为了构建这个模型,我将使用 Python 和它的机器学习库 LightGBM,我将把它用于它的梯度增强分类工具。如果你不知道什么是梯度增强分类算法,并想知道更多,请随时查看 StatQuest 的这个优秀视频进行深入了解。现在,关于我们的模型,你需要知道的是,它本质上是一个二元分类算法,其中两个分类要么是挥杆,要么是拿杆(注意:我们不关心挥杆的结果,只关心击球手是否挥杆)。
数据:
与大多数机器学习算法一样,如果我们希望我们的模型准确有效,我们需要大量的数据。幸运的是,有一个名为 pybaseball 的方便的 Python 库,使得获取这些数据变得轻而易举。我将使用这个库来收集 StatCast 逐节数据,以便训练模型。
特性:
特征选择是构建分类模型不可或缺的一部分。这些特征是算法将考虑的不同度量,以确定击球手是否会以特定的音高挥杆。因此,我们必须纳入对结果变量影响最大的指标。StatCast 跟踪大量不同的投球指标,如旋转速度、释放点等,但只有其中一些指标对决定击球手是否挥杆有用。我将在模型中使用的最重要的功能是:
- 球场的位置
- 投球的速度
- 音高的垂直和水平变化
- 投球的计数
- 前一个投球的投球类型(除非它是击球手的第一个投球)
这些特征中的一些可能是显而易见的,但是有一些可能不是很直观。位置绝对是决定击球手挥杆与否的最重要因素;一个打者几乎不会在 3 英尺以上的区域挥棒,但经常会在中间挥出快速球。
速度和垂直/水平突破功能一举两得。首先,它们在决定击球手是否挥棒时非常重要(击球手经常挥棒打一个看起来像好球的滑球,但最终离开本垒板 6 英寸,快速球经常会冻结击球手期望的变速球)。其次,它们允许我隐式地告诉模型投掷的是什么类型的球。我不必明确地告诉模型具体的音高类型,而是算法可以根据音高的速度和移动来学习音高的行为。这在我们以后用模型做预测的时候会很有帮助。
投球的次数也是击球手决定挥杆与否的一个因素。例如,击球手通常对击球手的计数更有选择性,比如 3-0,除非完美,否则不会挥棒击球;但是当打者以 0-2 落后时,他通常会挥棒攻击任何接近好球带的地方。
最后,前一个投球的投球类型在决定击球手是否会挥棒方面起着更微妙的作用。一个刚看到曲球的击球手可能不太可能挥出随后的曲球,因为他刚看到一个曲球。
现在我们有了所有这些数据,我们终于可以开始训练模型了。
训练模型:
为了训练这个模型,我选择了红队的一垒手乔伊·沃托作为我们的击球手。我选择沃托是因为他在挥杆时极其挑剔,我想看看我们的模型是否会反映这一点。在用乔伊·沃托过去 5 个赛季的逐节数据训练模型后,我们得到了 0.795 的准确率。这意味着我们的模型能够在 79.5%的时间里正确预测沃托是否在投球。虽然并不完美,但这似乎很有希望。期望接近 100%的准确性是不现实的,因为人为误差和棒球的随机特性使得不可能绝对确定地预测。经过一点点的超参数调整,我能够获得高达 80.5%的精度分数,这对于我们的模型来说已经足够好了。
使用模型:
既然我们的模型已经训练好了,我们终于可以开始用它来做预测了。虽然知道一个特定的音高是否会被挥杆是有帮助的,但输入特定的音高来看它们是否会产生挥杆是非常低效的。即使我们这样做了,我们的模型也只有 80%的准确性,所以会有相当大的误差。相反,我们可以绕过这两个问题,在好球区周围绘制一个特定的球距图,并使用我们的模型来计算沃托在该区域每个位置的挥杆概率。

*从捕手的角度看好球带
这个图表给了我们模型的预测概率,如果乔伊·沃托在击球手的第一次投球时投出泰勒·格拉斯诺的快速球,他将挥棒击球。以这种方式使用模型的预测概率更有帮助,提供了更多的洞察力,而不是让模型简单地指示投球是否会被挥杆。该模型将预测挥杆概率为 51%的投球和挥杆概率为 98%的投球,因为两者都被挥杆,但它们显然是两种非常不同的投球。通过可视化数据,我们可以看到 Votto 以某一音高摆动的可能性,而不是简单地得到是或否的答案。
看前面的图表,我们可以看到,在球板中心上方的球路上,沃托挥出快速球的概率约为 80%,但当你到达好球带的边缘时,这种概率会迅速降低。这似乎与我们对乔伊·沃托的了解一致。他是一个非常挑剔的击球手,有着精英的眼光,所以当你远离本垒板的中心时,他的挥杆概率就会下降。这一点在击球的第一次投球时得到了进一步的强调,在这种情况下,没有必要挥动任何不容易击中的东西。基于第一个结果,我们的模型看起来是准确的,但是让我们更深入一点来确定。我们可以测试模型准确性的一种方法是通过观察 Votto 在 0-0 计数中的摆动率,看它是否与我们的模型给出的相似。

看这个我们可以看到我们的模型的情节看起来惊人的相似。这是一个好迹象,表明我们的模型是准确的。看起来确实有些细微的差异,但那很可能是因为挥杆率图表考虑了所有的球种,而不只是快速球。为了进一步测试这个模型,让我们来看看沃托在另一场比赛中的挥杆概率。

该图显示了在 0-2 计数中,沃托的挥杆概率与克莱顿·克肖的曲球。这个模型似乎又一次符合我们的预期。在 0-2 的计数中,Votto 最有可能在任何看起来可能是好球的投球上挥杆,这是由他在低于区域的投球上相对较高的挥杆概率所表明的。此外,由于落在区域顶端的曲球很少被挥击,所以当你在好球带中走得更高时,沃托的挥击概率会降低是有道理的。
应用模型:
既然我们已经看到我们的模型是相对准确的,我们可以考虑如何在现实世界中应用它
第一个也是最明显的应用是增强投手的打者侦察报告。虽然挥杆率数据是可用的,并且可以在 Baseball Savant 等网站上很好地可视化,但这些数据通常非常有限或过于笼统。如果一个打者在他的职业生涯中只面对过几次投手,那么样本量太小,不足以揭示任何有意义的见解。类似地,如果你试图对一种特定的投球类型进行归纳和建模,你就失去了特异性。这样的模型将克莱顿·克肖的缓慢循环曲球与泰勒·格拉斯诺的快速突破曲球归为一类,这损害了模型在应用于个案时的准确性。通过使用我们在这里制作的模型,您可以获得两个世界的最佳效果。即使一个打者没有面对过某个投手,他也很可能面对过几个不同的投手,他们有相似的投球。通过使用单个投手的平均投球动作/速度,该模型可以相对准确地预测击球手的行为,即使关于他们具体比赛的数据很少。这有助于制作适合特定投手的球探报告,并提供对击球手行为的洞察,否则这些行为会被忽略。
其次,这个模型也可以用于模拟,试图预测击球手或投手在比赛中的表现。使用像这样的模型来预测挥杆概率,同时使用其他算法来预测挥杆结果,可能有助于预测特定投手的击球结果。
如果你做到了这一步,感谢你的阅读,并随时给我留下任何提示和/或批评!如果你想查看这个模型的代码,这里有一个到 GitHub 库的链接。
模拟纽约市的交通事故风险
使用 convLSTM2D 深度学习模型对交通事故风险进行分类

问题陈述
仅在 2017 年,美国就有超过 3.7 万人死于道路交通事故。2010 年与交通事故相关的成本(来自 2015 年发表的一份报告)为 2420 亿美元,占当年国内生产总值的 1.6%。以运输为生的公司将受益于关于哪里更可能发生事故的信息,因此他们可以避开这些地区,降低事故风险。优步和 Lyft 等拼车公司依赖于快速安全地从一个地方到达另一个地方,以获得客户满意度。优步为其司机提供保险,承担事故成本,因此如果发生事故,面临的风险不仅仅是客户满意度下降。这个项目将集中于整个纽约市事故风险的时空预测。拼车公司在生成路线以避开事故风险增加的区域时,可以使用这样的信息作为额外的数据层。
我们将使用的数据是由纽约市提供的 NYPD 机动车碰撞数据。
数据以 CSV 格式提供,包括事故时间、地理坐标和其他信息,如事故原因、涉及的车辆、死亡人数等。数据将被清理,以确保每个条目包含有效的坐标和时间戳。在这一阶段,我们计划将数据表示为栅格,以确定一天中特定时间事故风险增加的区域。随着时间的推移而可视化的 2D 矩阵基本上是一个图像识别问题,因此我们计划开发一个卷积 LSTM 深度学习模型。像这样的方法是有先例的。
数据争论
我们决定将此作为一个分类问题,提出这样一个问题:在这一天的这个时候,这个地区发生事故的风险是否会增加。数据集包含 1.58 MM 的事故行,包括日期、时间、经度和纬度,以及涉及的车辆数量、促成因素、受伤人数等。在之前的项目中,我们对纽约市范围内发生的所有事故都很感兴趣,因此不关心是否有缺失的位置值。对于本项目,事故的位置和时间非常重要,因为我们试图建立一个模型来预测在选定的时间和日期发生事故的风险增加的位置。
通过上面的链接下载的. csv 文件中的数据被导入到熊猫数据框架中。列标题被转换为小写,标题中的空格被替换为下划线以允许链接。创建了一个新的 datetime 列,并将其设置为索引,以便使用 pandas 进行索引。loc 访问器。数据集中的每条线代表向 NYPD 报告的事故的时间和位置。最初检查数据时,日期时间数据的行数与经度和纬度的行数不匹配。更仔细的检查揭示了超过 185,000 个 NaN 实例,以及超过 900 个经度和纬度的 0 值。0 经纬度在赤道上,加纳以南。由于事故的日期和位置对这项工作至关重要,所有的 NaN 值首先被转换为 0,并与现有的 0 值一起删除。这些条目的删除导致了与经度和纬度相同数量的日期时间值。
所有事故都是用它们的纬度和经度值绘制的。这显示了多个外围点,很明显不在纽约市范围内。对经度和纬度的限制越来越严格,直到通过目视检查获得了一幅完整的纽约市地图。接下来,我们将经纬度转换为世界大地测量系统,这是将地球曲面投影到平面上的标准,就像地图一样。这一点很重要,因为地面距离测量会更精确。

并列显示坐标(绿色)和 WGS 投影(蓝色)
这种差异很小,但在地面测量中会造成几十米的误差。美国东海岸位于通用横轴墨卡托坐标系的 18T 区域。更多信息见此处。应用此功能可将纬度和经度转换为米东距和米北距,这是一种更精确的地面距离测量方法。这些值将用于这项工作的剩余部分。
电子设计自动化(Electronic Design Automation)
为了感受数据的一致性,我们首先按小时对事故进行分组,并绘制了两周的直方图。这表明在一天中似乎有一个双峰分布,大约在上午 8 点和下午 4 点达到峰值,这与正常的高峰时间交通一致。

从周一开始,绘制了两周的直方图,显示每小时的事故数量。
双峰分布对许多人来说是明显的,并且总是在下午发生最多的事故。还有一段时间是凌晨,事故比较少,大概是因为路上车少。然后,我们绘制了整个数据集内每月的事故总数。

数据集期间每月的交通事故总数。
事故数量似乎是季节性的,年初时最低,夏季达到高峰,接近年底时逐渐减少。然而,我们不只是对预测事故何时发生感兴趣。我们感兴趣的是事故将在何时何地发生。为此,我们将按小时和地点汇总事故。如前所述,我们将把它放在 2D 数组中。最初,我们选择将数据离散成 64 x 64 的网格。这返回了一个更加像素化的数据视图,但是城市的许多特征仍然是可辨别的。

所有的事故数据都被收集到一个 64 x 64 的数组中
像这样分组为 64×64 个箱使得每个箱的宽度大约为 712×765 米。虽然这不是绕过事故的最实际距离,但它将允许我们展示我们的模型预测交通事故的能力。为了在合理的时间内进行计算,我们决定查看最近 6 个月的事故数据,该数据集包括 2018 年 12 月 16 日至 2019 年 4 月 16 日的数据。数据被分成 70:20:10 的训练:测试:验证集。使用训练集和测试集改进的模型,验证集是一个保留集,仅应用于训练模型。
模型结构
我们选择用 2D 事故阵列来表示这个模型。这两个维度由规则的距离间隔定义,每个箱中的事故数量相加。这给了我们一个纽约市事故的像素化视图,但它适合看看这种方法是否有效。像这样排列的数据可以被认为是一幅图片,或者当考虑多个时间片时,是一部电影的帧。有鉴于此,我们可以把它当作一个“预测电影的下一帧”的问题。这是一个有趣的问题,因为这是一个时间序列问题,也是一个图像问题。时间序列问题通常受益于长短期记忆(LSTM)模型。这些是递归神经网络,具有存储先前信息以应用于当前帧的能力。这对于时间序列问题非常有用,因为在时间序列中可能会出现规则的模式。因为我们的问题也是一个图像问题,我们可以从卷积神经网络(CNN)中受益。CNN 使用核函数从图像中提取重要特征。我们使用 Keras 进行了深度学习,Keras 是一种运行在 TensorFlow 之上的高级 API。该包包括 convLSTM2D 层,它类似于 LSTM 层,但输入层和变换都是卷积的。

我们在这项工作中使用的深度学习 convLSTM2D 模型的图。
第一次卷积输入后,我们应用了 4 个 convLSTM2D 层,每层之间都进行了 relu 激活和归一化。最终的 conv2D 层使用 sigmoid 激活和二进制交叉熵损失函数将张量折叠回 2D 图像,这提供了我们对事故发生概率的 64 x 64 2D 阵列的预测。
该层所需的数据的输入形状是具有形状(样本、时间、行、列、通道)的 5D 张量,在我们的情况下是(样本、时间,64,64,1)。因为我们有一个 64×64 的矩阵,并且它是一个二进制分类问题,所以 1 个通道。在这种情况下,时间是时间步长,我们必须选择要使用多少时间点来预测感兴趣的小时。对于这项工作,我们选择使用之前的 6 个小时来预测 7 日。我们选择了 64 个具有 5×5 内核的过滤器。
特征工程
由于所涉及的因素极其复杂,直接预测交通事故是一项极其困难的工作。几乎不可能包括导致事故的所有因素,包括但不限于司机分心、天气状况、交通状况和道路状况。我们将预测事故风险,而不是直接预测事故。目前,我们的事故数据是按小时在张量中时空排列的。每个网格代表一天中该时间该网格内发生的事故数量。要将事故转换为事故风险,我们需要找到过去 x 天内每个单元的平均事故数。对于这项工作,我们选择寻找 6 天内事故的平均数。例如,如果最近 6 天上午 8 点的一个格网单元包含以下事故数:3、0、6、2、4、1,则关注日上午 8 点的事故风险为(3 + 0 + 6 + 2 + 4 + 1)/6 = 16/6 = 2.67 事故/小时。这种转换应用于所有数据,因此建模中使用的数据是 64 x 64 的事故率数组,基于同一时间的事故和过去 6 天的网格单元。

一小时内发生的事故。

n 的事故率是用前 6 天每天的同一小时计算的
同一小时的事故率是使用过去 6 天中一天的同一小时计算的(右图)。对于构建我们的模型来说,事故率比单独的事故数字提供了更多的信息。最终模型总共包含 2,876,737 个可训练参数,当在使用 GPU 的 Google Colab 笔记本中运行 50 个时期时,总训练时间约为 6 小时。

该模型似乎在第 50 个纪元时收敛,因此我们没有将训练时间延长到这之后。
评估模型
虽然 Keras 提供了许多内置分析,但为了简单起见,我们计算了自己的混淆矩阵。如前所述,该模型输出一个 64 x 64 的事故风险概率数组。

纽约市事故发生地点概率的基本事实(左)和预测(右)。
因为我们想要二进制分类而不是概率,所以我们需要将结果分层为 0 或 1。我们使用 predict_classes 函数来实现这一点,该函数为我们进行了分层。任何 0.5 或以上的概率变成 1,其余的为 0。

纽约市交通事故地点的地面实况(左)和预测(右)。
每个预测和地面实况 64 x 64 阵列被转换成 pandas 数据帧,该数据帧允许逐元素的乘法和减法。当两者相乘时,因为它们都是二进制矩阵,所以只有正确识别的值或真正值会保留下来。当我们从预测中减去地面真实矩阵时,真正值变成零(1 -1),错误预测的事故或假正值变成 1(1-0),错过的预测或假负值变成-1(0-1)。然后,可以通过从矩阵的大小(64 x 64 = 4096)中减去真正值、假正值和假负值的总和来计算真负值。

用于构建混淆矩阵的基础事实(右上)、预测(右上)、乘积图(左下)和差异图(右下)。
上面是四幅图像,分别显示了事故风险增加的基本事实(左上)、预测矩阵(右上)、显示正确预测像元的乘积矩阵(左下)以及差值图(右下),如果像元被错误预测,则差值图返回-1(白色),如果像元未被预测,但存在于基本事实(右)中,则差值图返回+1(黑色)。由于这两个阶层之间的不平衡(负值远远大于正值,这是一件好事!),报告模型的准确性不是一个合适的指标。相反,我们可以使用马修相关系数,或 phi 系数作为二进制预测质量的衡量标准。
MCC = TP * TN—FP * FN/sqrt((TP+FP)(TP+FN)(TN+FP)*(TN+FN))
其中 TP,TN,FP,FN 分别=真阳性,真阴性,假阳性,假阴性。应用此公式将返回一个介于 1 和-1 之间的值,其中 1 表示基本事实和预测完全一致,0 表示随机机会,而-1 表示模型和基本事实完全不一致。

跨训练集(左)、测试集(中)和验证集(右)计算的迈克尔相关系数。
计算我们三个集合的迈克尔相关系数,我们看到总体上非常好的分数,训练集、测试集和验证集的平均值分别为 0.82、0.82 和 0.83。令人高兴的是,由于这些数据根本不包括在模型训练中,所以坚持组也与训练和测试组得分相似。然而,我们可以从上面图中较低值的重复模式和下面直方图中的负偏斜看出,我们的预测中存在一些异质性。

每个机组的 MCC 值分布。
理想情况下,MCC 直方图将是平均值周围的正态分布。这表明我们的模型在预测一天中特定时间的事故风险方面较弱。如果它在预测某个区域方面很弱,我们不会在 MCC 得分的急剧下降中看到这一点。
首先,我们在混淆数据帧中添加了一个日期时间索引。然后,我们将阈值设置为 0.7,并过滤出发生这些情况的小时的索引值。绘制这些值的直方图,我们看到绝大多数值出现在凌晨 1 点到 3 点之间。

训练组(左)、测试组(中)和验证组(右)的 MCC 值低于 0.7 的时间分布。
如果我们为我们的模型重新计算平均 MCC 值,如果我们排除这些数据点,我们的训练集、测试集和验证集的平均值都略微增加到 0.84。

去除低于 0.7 的值后,MCC 在每组中的分布。
结论
我们生成了一个深度学习模型,用于预测纽约市的交通事故风险。该模型基于 Keras convLSTM2D 架构,该架构接受事故率的 2D 阵列,该阵列是过去 6 天每小时每个网格单元的平均值。
我们证明了该模型对未来未知数据的预测性,其迈克尔相关系数为 0.84。
用原始事故数而不是事故率来训练同一模型的尝试未能收敛,导致对事故数量和位置的严重高估。
要成为真正有用的模型,每个网格的分辨率最好是城市街区的分辨率。将时间分辨率增加到比 1 小时更频繁的周期也是令人感兴趣的。这当然会大大增加计算时间。
编辑
如果你感兴趣,这里有我在这项工作中使用的笔记本的链接。第一个用于数据处理,第二个用于convlsm 2d模型。
模拟疫情的传播!

叶夫根尼·切尔卡斯基在 Unsplash 上拍摄的照片
变更数据
科学家如何模拟流行病?政府是如何制定封锁计划的?我们怎么知道曲线是否已经变平了?
编者按: 走向数据科学 是一份以数据科学和机器学习研究为主的中型刊物。我们不是健康专家或流行病学家,本文的观点不应被解释为专业建议。想了解更多关于疫情冠状病毒的信息,可以点击 这里 。
我们正生活在我们有生之年从未想象过的最前所未有的时代。我们的生活早已偏离了过去的常态,不幸的是,这个世界再也不会像从前一样了。与其强调已经发生的变化,不如接受新常态,继续前进,重新开始,这将是一个明智的想法。
19 世纪见证了永远改变世界的三件大事——工业化、互联网和传染病!
好吧,让我们来解决房间里的大象——源自中国的冠状病毒感染了全球近 2200 万人,并造成 77 万人死亡。这里是截至 2020 年 8 月 17 日的世界现状。

截图,摄于 2020 年 8 月 17 日 https://www.worldometers.info/coronavirus/
这是这篇文章的议程。您可以选择跳到以下您喜欢的任何部分:
本文中使用的所有代码都可以从 GitHub 库下载
新冠肺炎数据可视化
数据可视化#1:按国家列出的案例总数
下面的数据可视化贯穿了从 2020 年 2 月到 2020 年 7 月底的时间线,并显示了报告病例总数排名前 20 位的国家是如何移动位置的。
数据可视化#2:按国家划分的死亡总数
另一个数据可视化显示了前 20 个国家在报告的新冠肺炎死亡总数方面的位次变化。
数据可视化#3:显示第一例报告病例前天数的世界热图
下面的数据可视化显示了它传播到全世界的速度。

距首次报告病例还有几天。互动版点击这里
从上面的交互式数据可视化中,我们可以看出:
- 泰国是第一个受到影响的国家(前 13 天),然后是日本(前 15 天)和韩国(前 20 天)。
- 一个月内,病毒传播到印度、东南亚国家、澳大利亚、美国和加拿大。
- 除了葡萄牙和爱尔兰,大多数西欧国家在头一个月内感染了这种病毒,而东欧国家在大约两个月内都是安全的。
- 俄罗斯在第一个月左右就被感染了。
- 在斯堪的纳维亚国家中,挪威是最后一个被感染的国家,而瑞典和芬兰是在前一个月内被感染的。
- 相对来说,传播到南美和非洲大陆需要更长的时间。
- 朝鲜——没有数据,老规矩,别问我为什么?😛
数据可视化#4:显示前 1000 个案例的天数的世界热图
现在,让我们看看在每个国家达到第一个 1000 个病例需要多长时间:

前 1000 例,互动版,点击此处
从上面的交互式数据可视化中,我们可以看到,要达到第一千例:
- 虽然泰国和日本是第一个被感染的国家,但是他们花了将近两个月的时间才达到第一个千例的里程碑。
- 土耳其、伊朗和塔吉克斯坦只用了两周时间。
- 韩国用了一个多月。
- 大半个欧洲用了近一个月。
- 美国、印度和澳大利亚用了将近 2 个月。
- 巴西和大多数南美国家用了不到一个月的时间。
- 南非用了不到一个月。
尽管这种病毒在一些国家传播相对较慢,但在其他一些国家却传播很快。不同地区的增长率可以用指数增长来表示。
在下一节中,我们将讨论用于病毒建模的不同增长模型,我们还将讨论政府如何提出各种缓解策略。
指数增长,逻辑增长?他们到底在说什么?
在过去的几个月里,我打赌你可能已经在新闻频道上无数次听到这些术语了。他们用这些术语来解释病毒是如何传播的,等等。但是你可能想知道他们到底在说什么?
好吧,为了理解指数增长到底是怎么回事,让我们来处理一个假设的情况。比方说,我让你在我拥有的 Airbnb 网站上住一整月,这是一个惊人的交易——开始的第一天你必须支付 1 美分,但每隔一天,直到一个月,你必须向我支付前一天支付的三倍。这是一笔难以抗拒的交易吗?我看起来像个白痴吗?没关系,你可以有你无声的笑的时刻,但如果你回来报名,我打赌你非常需要一堂数学课。
我用的是所谓的 指数增长 骗你进去。随着指数增长,我的每日利率从第一天的 0.01 美元上升到一个月底的 2 万亿美元以上。如果我们修改我们的交易,所以你每天只付我两倍的钱,你最后一天的最终账单会减少到 1000 万美元,如果我们再次修改交易,所以你每天只增加 50%的钱,你的最终账单会是更合理的 1917.51 美元。
下表总结了这些结果:

指数增长的示例
下面是我们用来计算上表中数字的简单指数方程:

指数增长公式
在上面的公式中,x(t)是你在第 t 天支付给我的,x₀是初始值(你在第一天支付给我的),r 是增加的百分比。
明白了吗?
科学家和流行病学家利用新冠肺炎的这种指数增长来了解病毒是如何传播的。换句话说,他们试图将每天记录的病例数据点拟合到这个模型中,并试图估计增长因子,从而预测未来。更具体地说,我们可以找出病毒在一个国家相对于另一个国家传播的速度。
但是指数增长有个问题!如果你还没有注意到,增长并没有停止。只要你输入一些参数 t,就会有一些输出值。但这并不是以前所有病毒爆发的情况,因为我们知道它们都在某个时候停止了。
我们的下一位嘉宾来了, 物流成长 !
与指数增长不同,逻辑增长不会无限增长。
它遵循三个阶段——在第一阶段非常缓慢地增长,在中间阶段非常迅速,在第三阶段再次开始缓慢增长,最后变平。这正是任何病毒爆发都会遵循的模式。
这是解释逻辑增长的简单公式。

逻辑增长公式
科学家们还研究了一种叫做 冈珀兹增长 的东西,这是一种特殊的逻辑增长,也类似于一个 sigmoid 函数。与在第一阶段和最后阶段完全对称的逻辑增长不同,Gompertz 函数增长缓慢,并缓慢变平。下面的公式表示 Gompertz 增长

Gompertz 增长公式
在 logistic 和 Gompertz 增长公式中,参数 c 代表 logistic 函数的渐近线。换句话说,c 的值是疫情结束前我们将出现的最大病例数。
下面的图表给你一个在所有三种模型中增长率如何变化的感觉。

说明指数、逻辑、Gompertz 的增长率
科学家们使用指数函数和逻辑函数来研究我们目前处于流行病的哪个阶段。流行病的第一阶段可以用指数函数更好地表示,然而,第二阶段可以用逻辑函数更好地表示。
利用约翰·霍普斯金大学编制的新冠肺炎报告数据,我们可以对指数和逻辑斯蒂模型进行曲线拟合。
以下是我们将要做的事情——我们将把除 lat 1 day(2020 年 7 月 29 日)之外的所有数据拟合到模型中,并尝试预测这一天的情况。我们将使用[scipy.optimize.curve_fit](https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.curve_fit.html)一个 Scipy 函数,该函数使用非线性最小二乘回归来拟合一个选定国家的数据。我们可以使用 R2 分数,这是一个测试模型拟合度的常用指标,衡量模型预测曲线新点的能力。值非常接近 1,意味着模型可以很好地预测数据,而值接近 0 甚至负值,意味着模型非常不适合。
下面是相同的代码:
曲线拟合代码
以下是一些选定国家的结果:
中国
从下面的结果来看,中国的指数模型 R2 得分为负,逻辑模型 R2 得分为正。这标志着中国已经越过指数阶段,进入逻辑阶段。R2 分数非常接近 1 表明非常适合逻辑模型。事实上,曲线的平坦化发生在 2020 年 3 月 13 日。
**** Prediction for China-CHN as of 2020-07-29 ****
Initial no. of cases on 2020-01-19 -> 216.0
No. of cases on 2020-07-28 -> 86783.0
~~ Exponential Model ~~
['p=0.035', 'sigma_p=0.001']
MSLE: 45.617, Exp of RMSLE: 9.670, R2 score: -9.730
~~ Logistic Model ~~
['a=10.000', 'b=0.113', 'c=84166.683'] ['sigma_a=1.115', 'sigma_b=0.005', 'sigma_c=390.112']
MSLE: 0.418, Exp of RMSLE: 1.909, R2 score: 0.963
Day of flattening of the infection curve -> 13, Mar 2020
韩国
韩国也是如此。曲线的平坦化发生在 2020 年 5 月 1 日。
**** Prediction for South Korea-KOR as of 2020-07-29 ****
Initial no. of cases on 2020-02-21 -> 155.0
No. of cases on 2020-07-28 -> 14203.0
~~ Exponential Model ~~
['p=0.032', 'sigma_p=0.001']
MSLE: 31.421, Exp of RMSLE: 9.670, R2 score: -11.591
~~ Logistic Model ~~
['a=3.858', 'b=0.074', 'c=12089.302'] ['sigma_a=0.468', 'sigma_b=0.005', 'sigma_c=113.041']
MSLE: 0.132, Exp of RMSLE: 1.439, R2 score: 0.881
Day of flattening of the infection curve -> 01, May 2020
法国和意大利
对法国和意大利来说,逻辑模型显示了拟合优度。这两个国家的曲线在 6 月变平。
**** Prediction for France-FRA as of 2020-07-29 ****
Initial no. of cases on 2020-03-01 -> 100.0
No. of cases on 2020-07-28 -> 183079.0
~~ Exponential Model ~~
['p=0.056', 'sigma_p=0.001']
MSLE: 37.960, Exp of RMSLE: 9.670, R2 score: -3.601
~~ Logistic Model ~~
['a=10.000', 'b=0.055', 'c=166804.289'] ['sigma_a=1.059', 'sigma_b=0.003', 'sigma_c=1840.738']
MSLE: 1.355, Exp of RMSLE: 3.203, R2 score: 0.961
Day of flattening of the infection curve -> 21, Jun 2020
**** Prediction for Italy-ITA as of 2020-07-29 ****
Initial no. of cases on 2020-02-24 -> 132.0
No. of cases on 2020-07-28 -> 246286.0
~~ Exponential Model ~~
['p=0.054', 'sigma_p=0.001']
MSLE: 43.168, Exp of RMSLE: 9.670, R2 score: -3.856
~~ Logistic Model ~~
['a=10.000', 'b=0.056', 'c=243030.882'] ['sigma_a=0.895', 'sigma_b=0.002', 'sigma_c=2068.458']
MSLE: 1.156, Exp of RMSLE: 2.931, R2 score: 0.973
Day of flattening of the infection curve -> 13, Jun 2020
新西兰
新西兰政府的反应被认为是最好的反应之一。对新西兰来说,2020 年 4 月 18 日开始变平
**** Prediction for New Zealand-NZL as of 2020-07-29 ****
Initial no. of cases on 2020-03-23 -> 102.0
No. of cases on 2020-07-28 -> 1207.0
~~ Exponential Model ~~
['p=0.024', 'sigma_p=0.001']
MSLE: 18.770, Exp of RMSLE: 9.670, R2 score: -22.147
~~ Logistic Model ~~
['a=5.567', 'b=0.213', 'c=1159.461'] ['sigma_a=0.323', 'sigma_b=0.006', 'sigma_c=2.736']
MSLE: 0.005, Exp of RMSLE: 1.072, R2 score: 0.985
Day of flattening of the infection curve -> 18, Apr 2020
美国、印度和巴西
当世界其他国家开始看到隧道尽头的一些曙光时,美国、印度和巴西仍然深陷疫情。新病例持续上升。
虽然美国的逻辑模型表明非常适合(R2 接近 1),但印度和巴西的模型有些不太适合,但仍被认为处于逻辑阶段,因为 R2 > 0。下面的预测表明,这三个国家有望在 2020 年 11 月底之前使曲线变平。
**** Prediction for Brazil-BRA as of 2020-07-29 ****
Initial no. of cases on 2020-03-15 -> 121.0
No. of cases on 2020-07-28 -> 2442375.0
~~ Exponential Model ~~
['p=0.080', 'sigma_p=0.000']
MSLE: 32.941, Exp of RMSLE: 9.670, R2 score: -0.713
~~ Logistic Model ~~
['a=9.998', 'b=0.025', 'c=2159468.826'] ['sigma_a=2.364', 'sigma_b=0.006', 'sigma_c=637699.122']
MSLE: 5.820, Exp of RMSLE: 11.161, R2 score: 0.735
Day of flattening of the infection curve -> 19, Nov 2020
**** Prediction for India-IND as of 2020-07-29 ****
Initial no. of cases on 2020-03-17 -> 125.0
No. of cases on 2020-07-28 -> 1483156.0
~~ Exponential Model ~~
['p=0.076', 'sigma_p=0.000']
MSLE: 27.423, Exp of RMSLE: 9.670, R2 score: -0.560
~~ Logistic Model ~~
['a=9.994', 'b=0.024', 'c=1066388.221'] ['sigma_a=3.159', 'sigma_b=0.008', 'sigma_c=441540.841']
MSLE: 6.032, Exp of RMSLE: 11.657, R2 score: 0.650
Day of flattening of the infection curve -> 27, Nov 2020
**** Prediction for United States-USA as of 2020-07-29 ****
Initial no. of cases on 2020-03-03 -> 103.0
No. of cases on 2020-07-28 -> 4290263.0
~~ Exponential Model ~~
['p=0.079', 'sigma_p=0.001']
MSLE: 45.859, Exp of RMSLE: 9.670, R2 score: -1.501
~~ Logistic Model ~~
['a=10.000', 'b=0.024', 'c=4262579.365'] ['sigma_a=1.038', 'sigma_b=0.003', 'sigma_c=503190.213']
MSLE: 5.149, Exp of RMSLE: 9.670, R2 score: 0.903
Day of flattening of the infection curve -> 17, Nov 2020
理解疫情的基本 SIR 模型和构建模块
虽然指数模型和逻辑模型有助于我们了解我们处于流行病的哪个阶段,但是也有一定的局限性:
- 他们不考虑人口规模,除非我们为这些函数创建一些界限。
- 他们只模拟感染,但不考虑康复和死亡。
- 他们没有考虑感染率或恢复率。
- 他们没有考虑改善治疗对更快康复的影响。
- 他们不考虑疫苗接种的影响,如果在一段时间内有疫苗接种的话。
为了分析如此复杂的场景和模拟,科学家和流行病学家使用了另一种模型,称为 SIR(易感-感染-康复)模型。
这个模型由克马克和麦肯德里克于 1927 年首次发表。在这个模型中,一个地区的全部人口被分成三个部分,称为易感(S)、感染(I)、恢复/移除(R)。
易感房室包括易受感染的所有人群。在任一时间点,这是总人口减去感染、康复和死亡的人口。
已感染(R) 区间包括目前已感染病毒且有传播疾病能力的人群。从技术上来说,我们需要至少有一个被感染的个体才能引发流行病。
恢复/移除(R) 区间包括所有不再被感染且不能感染任何人的人。这包括已经康复、产生免疫力或已经死亡的人。因此,这个群体有时被称为删除。
一般来说,当一个人康复后,他们的身体会产生抗体,从而获得对疾病的免疫力,因此他们不会再被感染。除非病毒能够变异,这可能适用于大多数传染病。
接下来,我们需要了解病毒是如何传播的。
了解病毒如何传播的第一个基础是知道病毒的 繁殖数(R0) 。
生殖数量是一个感染者在患病过程中会感染的平均个体数量。
如果 R0=2.0,这意味着平均每个感染者会将疾病传播给另外两个人。对于任何要成为成功疫情的病毒爆发,它需要具有非常高的 R0 值。
R0 始终作为一个范围发布,因为它根据各种因素不断变化。以下是一些常见病毒爆发的 R0,

一些常见疾病爆发的 R0 值。来源:https://SPH . umich . edu/pursuit/2020 posts/how-scientists-quantify-outbreaks . html
从上表可以看出,科学家估计新冠肺炎的 R0 在 1.5-3.5 之间。它不像一些致命的流行病如麻疹、天花等那样糟糕,但规模如此巨大的原因可能是传播的性质。
任何流行病要减缓传播并逐渐消失,都需要维持 R0 ≤1
下一个构件是病毒的 恢复时间 。
恢复时间是个人从病毒中恢复的预期天数。
缩短恢复时间也会减缓病毒的传播。
使用这两个构建模块,我们可以计算以下参数:
- Gamma (γ): 这代表日恢复数。假设一个人需要 14 天恢复,那么γ = 1/14
- β/感染率(β): 这是病毒的每日繁殖数。假设如果 R0=4,那么β=4/14
下面是一个基本的 SIR 模型的图解,带有来自人口的每个状态的转移率。

随着时间的推移,人们在易感到感染到移除之间过渡。每天都会发生以下情况:
- 易感人口因新增感染者数量而减少。
- 感染人口随着新感染人数的增加而增加,随着新恢复人数的减少而减少。
- 恢复/移除人口随着新恢复的数量而增加。
这些变化率可以用下面的微分方程进行数学表达:

SIR 模型微分方程
使用下面的代码,我们可以为 1000 人的小群体模拟一个简单的 SIR 模型,有 10 个初始感染和 0 个恢复。
SIR 模型
这是简单模拟的结果。

SIR 模型的示例模拟。
可以看出,随着时间的推移,会发生以下情况:
- 在流行病开始的时候,人群中易感个体的比例非常高。这将增加感染率,因为更多来自感染者的相互作用将与易感个体发生。此时,恢复的个体数量非常少,因为没有多少被暴露。
- 在疫情中期,易感个体在人群中的比例开始下降。很快新恢复的个体每天变得大于新感染的个体,并且感染**的数量开始减少。****
- 在一场疫情的结束时,在 附近【R】数量开始趋于平稳【N】随着大家都有了病毒,又恢复了 。感染人数** 和 易感人数(S)随着不再有更多的人易感,在 0 附近开始趋于平稳。****
各个政府如何提出不同的策略?
在上一节中,我们已经看到了针对小规模人群的基本 SIR 模型的简单模拟,现在让我们来看看大规模的基本 SIR 模型。
让我们考虑一个假设的情况,一个拥有 800 万人口的大都市被 R0=5 的疫情困住,当地政府面临控制局面的压力。
这是这种疾病的简要介绍:
- 再现数,R0=5.0
- 被感染的正常恢复时间是 14 天。
- 初始感染人数为 1000 人
- 初始恢复人数为 0
政府想看到的第一件事是这件事有多大。该镇最优秀的科学家通过一些快速模拟(见下图)显示,大约 375 万人(该市 47.8%的人口)将在疫情爆发的 37 天内被感染。换句话说,什么都不做的代价是非常非常巨大的!

说明疫情如何在大都市蔓延。
因此,政府对即将发生的事情有所察觉。所以他们接下来想的是我们如何控制局面?
科学家们通过一些额外的模拟来显示 R0 的影响,他们强调 R0 需要降至 1 或更低,以减缓病毒的传播,并最终阻止疫情。
毕竟这不是火箭科学。通过查看下面的图表,政府将会明白,降低 R0 会产生巨大的影响——R0 每降低 1 个单位,感染的峰值就会变小,并推迟几周,因此政府可以争取一些时间来改善医疗设施或实施策略或开发疫苗(如果可行的话)。

R0 对疫情传播的影响
政府不希望经济关闭,所以他们想看看某种洗手或公共行为政策限制是否会有所帮助?
科学家们进行了一些模拟,并做出了以下评论:
- 这种病毒的本质是通过感染者和易感人群之间成功的相互作用来传播。
- 洗手和公共接触政策的改变只是通过限制成功互动的次数来降低病毒传播的可能性。因此,R0 只是减少到某个数值,比如说 2.5。
- 即使在这种情况下,至少有 23%的人口在 88 天后被感染。所以这一次,在感染达到高峰之前有一些延迟。
因此,政府明白这是行不通的,所以拒绝了这个想法。

说明洗手指南的效果。
政府能想到的下一件事是封锁,但他们需要知道以下几点:
- 他们需要多长时间实施锁定—1 周、2 周还是 3 周?
- 他们应该封锁多久——30 天还是 60 天?
- 封锁的严格程度应该是怎样的——适度还是严格?
除了寻找上述问题的答案,政府还需要优化以下内容:
- 最大限度地减少感染高峰——确保总体感染比例非常低。
- 我们需要选择一个最佳的锁定选项,以免对经济造成太大伤害,因为一个真正受到伤害的经济稍后会卷土重来,对我们造成同样严重的打击。
- 尽可能争取一些时间,以便扩大医疗设施,或许也给科学家们一些时间来开发疫苗或其他东西。
同样,科学家通过对各种场景进行模拟来帮助政府,并得出以下结果:

不同锁定选项的图示
你应该如何阅读上面的图表?
- 每一行代表锁定开始的日期,例如,第一行的所有图表对应于在爆发后第 7 天开始的锁定,意味着在 1 周内,第二行是关于在第 2 周开始的锁定,依此类推。
- 在每一行中,有 30 天与 60 天、适度与严格的模拟选项。
- 每个图表显示 R0、感染高峰日的时间以及感染高峰日的感染人口比例。
- 当然,我们希望感染比例最低,但与此同时,我们也需要了解感染高峰期还有多长时间。
我们可以从上面的图表中观察到以下情况:
- 在每个锁定选项中,感染的高峰只是被延迟了,但不会完全消失。
- 与相应的适度锁定选项相比,所有严格锁定选项都为我们赢得了更多时间。例如,如果我们考虑从第 14 天开始的 30 天封锁,严格封锁会在 75 天内导致 47%的感染,而中度封锁会在 60 天内导致 45%的感染(相对更快)——赢得一些时间至关重要。
不管潮水有多高,我们宁愿晚一点被打,因为这样我们可以做一些安全准备或制定逃生路线。
- 在所有中度封锁中,封锁解除后,感染高峰就会上升。
- 等到 35 天后才实施封锁看起来太晚了,因为感染高峰会上升,所以完全排除了这种选择。
通过考虑前面讨论的所有优化因素,政府提出了一个从第 14 天开始实施的 30 天严格封锁计划。
我们现在正处于从第 14 天开始的为期 30 天的严格封锁之中。利用赢得的时间,政府在两周内迅速增加了医疗设施,并从第 28 天开始部署改进的治疗方案。
以下是正常锁定方案与改进处理方案的对比:

改进了 30 天锁定中的恢复时间图示
从上面的图表中我们可以观察到以下情况
- 改进的治疗方案大大减少了 50%的恢复时间,这是非常好的。
- 感染高峰减少了 50%,这意味着现在只有 22%的人被感染,这是一个好消息。
- 感染高峰推迟了,这意味着我们可以再争取几天时间——这太好了。
随着改进的治疗方案的到位,政府想知道如果我们现在将封锁期延长到 60 天,会对感染高峰产生任何影响吗?
下图显示了相同的改进治疗方案,但是是在 60 天严格封锁的情况下。

改进了 60 天锁定中的恢复时间图示
30 天的禁闭与 60 天的禁闭有什么不同?
尽管在一天结束时感染的人口比例相同,但是通过 60 天的封锁,我们赢得了更多的时间(135 天),这对制定下一步措施至关重要。
因此,政府将封锁期延长至 60 天。
政府现在意识到永远推迟这一趋势是没有意义的,因此它与一组科学家合作,为疫苗开发提供一些资金。
如果有疫苗呢?下一个问题是有效控制感染的每日容量应该是多少?
下面显示了 60 天锁定环境中的另一个模拟:

疫苗容量说明
这些数字看起来很有希望。
对于我们正在应对的大都市(800 万人口)的规模,我们似乎需要每天接种 3 万到 5 万次疫苗,以保持较低的感染率。
因此,现在政府可以致力于提供必要的基础设施,希望疫苗能尽快问世。
在本文中,为了简单起见,我们只讨论了基本的 SIR 模型。但是,该模型没有考虑以下情况:
- 生命动力学——每天都有新生儿出生和死亡。
- 移民每天都在发生。
- 大多数病毒都有潜伏期——这可以用 SEIR 模型来模拟,它是 SIR 的变体,引入了另一个称为“暴露”的隔室。
- 有些人是渐近的,不处于感染状态等。
- 住院、重症监护室、呼吸机等。
在真实世界场景中,科学家采用最准确的数据,例如新记录的出生或死亡数量、因其他原因发生的死亡数量、ICU 中的人数等,从而做出准确的预测。
因此,在本文中,我们看到了如何将新冠肺炎数据拟合到指数和逻辑模型中,从而做出预测,并推断出某个国家何时曲线变平,或者如果没有变平,预计何时会变平。
此外,我们还看到科学家如何使用 SIR 模型模拟各种场景,以帮助政府制定不同的病毒预防策略。
本文使用的所有代码都可以从GitHub 资源库下载
模拟疾病的传播
SIR 模型的模拟练习

约翰·西米托普洛斯在 Unsplash 上的照片
这些天,关于新冠肺炎病毒传播的新闻占据了所有报纸的专栏,所以我尝试了一个关于这个主题的简单建模练习。
在继续之前,我需要说两点。第一点很明显:本文仅供参考,不提供任何医疗建议,也不能替代专业医疗。第二个是,作为我的习惯,我选择使用手绘公式,因为我发现在介质上使用乳胶相当烦人。我提前为书法道歉。
在流行病学中,有一个数学技术家族,称为房室模型,用于传染病的建模。顾名思义,在这些模型中,种群被分成不同的组,并假设在相同区间中的个体具有相同的特征。
我们来考虑一下 SIR 型号,这是最简单的一种。在这个模型中,种群被分成三组:易感个体, S,感染个体, I ,以及 R 。应该注意的是,这里的术语“康复的”表示可能已经痊愈并产生免疫力的个体,或者可能已经死亡的个体,但是在任何情况下,他们都不能再被感染。
假设
SIR 模型依赖于几个重要的假设。
- 首先,没有人加入易感群体,因为我们忽略了出生和移民,我们假设人们只能被感染一次。因此,个体离开易感群体的唯一途径就是被感染。同样,受感染的个体最终只能进入康复组。

隔间之间的流动
- 第二,人口中的所有个体都具有相同的患病概率,并且他们的年龄分布均匀地分布在 0 和预期寿命 L 之间(这种假设尤其对发达国家是合理的)。
- 第三,群体的同质混合:个体与群体其余部分的接触也遵循均匀分布,即个体不是更小群体的一部分。由于通常存在的社会结构,这种假设不太合理,但有必要使模型易于处理。
基本定义
在定义模型之前,我们需要引入两个基本的流行病学量:
- 感染率 β ,即单位时间内单个(被感染)个体感染的人数。这个量与单位时间内的接触次数(假定不变)和疾病传播的概率有关;
- 痊愈/死亡率𝛾 ,是单位时间内痊愈的感染者的平均比率。例如,如果一次感染的持续时间【D】为 10 天,那么从统计数据来看,当前感染人群的 1/10 每天都会康复。

恢复速度和疾病持续时间
感染率和恢复率之间的比率给了我们另一个基本的衡量标准,即r₀的基本再生数:

基本复制数
基本生殖数代表疾病的生殖比率,是易感个体群体中一个受感染个体直接产生的预期病例数。

每个被感染的个体预计会感染另外两个个体
如果估计准确的话,这是一个非常重要的数字,因为它区分了自熄性疾病( R₀ < 1 )、地方性疾病( R₀=1 )和流行病( R₀ > 1 )。
模型
为了开始建模,让我们定义一组变量。

模型的变量
给定模型假设,以下等式成立:

平衡方程式
为了推广该模型并简化计算,我们将从现在开始考虑每一组相对于总体的分数:

三类人口的比例
给定上述假设,这些量应该如何随时间变化?
随着人们感染这种疾病,易感者的数量会减少,所以这种变化肯定是负面的。单个(被感染)个体在单位时间内感染 β 【易感】个体,则单位时间内感染者的总分数等于:

单位时间内受感染个体的比例
另一方面,如果一部分𝛾感染的个体在单位时间内康复,则在单位时间内康复的个体的总比例为:

单位时间内恢复个体的分数
现在,使用正确的符号,我们可以写出模型的微分方程:

模型方程
它们反映了模型的假设:没有人会再次变得易感,易感者会被感染,所有被感染的个体最终会康复(或死亡),没有其他选择。
模拟
运行模型的时间到了。迄今为止,我一直在努力寻找新冠肺炎病毒的已知参数。根据[1],恢复的中位时间约为 2 周,因此 β=1/14 。另一个来源【2】估计基本再生数为 R₀=2.28 ,由此我们可以推断出 𝛾=0.16 。最后,尽管有些说法与此相反,但最被接受的假设仍然是,一个人一旦被感染,就会获得对病毒的免疫力。
为了整合模型的方程,我使用了 scipy 的odeint函数。顺便说一句,[3]为编写 SIR 模型提供了一个很好的例子。我假设最初的感染人数等于 0.1%(T21)。**

新冠肺炎的 SIR 模型
根据这个模型,在没有任何干预来控制传播的情况下,病毒将在大约 180 天内被消灭,挽救不到 20%的人口。
SIR 模型当然是一个非常简单的模型,不适合塑造复杂的动态,尤其是那些涉及大量不同人群的动态。尽管如此,它仍然适用于小规模同质人群,在这些人群中,更容易尊重模型背后的假设(例如,疫情发生在游轮钻石公主号上)。
当局下令采取的措施如何遏制病毒的传播?最近在一些欧洲地区实施的隔离和检疫措施显然是为了尽可能地限制感染的可能性。在我们的模型中,这反映在因子 β的突然降低。
假设这些措施:
- 使新的ββ₂=β/3(也许没有这么大的减少),并且
- 在第 50 天施用,即在感染人数达到高峰之前,
会出现以下情况:

减少模型的 β
事实上,通过迅速反应,大幅降低β因子,感染人数立即减少,从而趋近于零。
参考
[1]https://ourworldindata.org/coronavirus
[2]http://www . cid rap . umn . edu/news-perspective/2020/02/study-72000-新冠肺炎-病人-发现-23-死亡率
[3]https://scipython . com/book/chapter-8-scipy/additional-examples/the-sir-epidemic-model/
贝叶斯网络建模
从医学诊断建模导论

我感觉不舒服。发烧。咳嗽。鼻塞。现在是冬天。我得了流感吗?很有可能。另外,我肌肉疼痛。更有可能。
贝叶斯网络非常适合这种类型的推理。我们有变量,有些变量的值是固定的。我们感兴趣的是给定这些固定值的一些自由变量的概率。
在我们的例子中,我们希望知道我们患流感的概率,给出我们观察到的一些症状,以及我们所处的季节。
到目前为止,它看起来像是用条件概率进行推理。还有更多的吗?是的。多得多。让我们放大这个例子,它就会出来。
走向大规模贝叶斯网络
想象一下,我们的网络模拟了每一种可能的症状,每一种可能的疾病,每一种可能的医学测试的结果,以及每一种可能影响某种疾病概率的外部因素。外部因素可以分为行为因素(吸烟、整天躺在沙发上看电视、吃得太多)、生理因素(体重、性别、年龄)和其他因素。为了更好的衡量,让我们也加入治疗。和副作用。
到目前为止,已经有足够的有用的医学知识来捕捉成千上万的变量(至少)及其相互作用。对于任何一组症状,加上一些行为、生理和其他外部因素的值,我们可以估计各种疾病的概率。还有更多。对于给定的疾病,我们可以要求它给出最可能的症状。还有更多。例如我咳嗽、发高烧,但被诊断为流感,还可能有什么其他疾病?对于给定的诊断,我们的特定症状,以及可能的其他因素,如我们的性别和年龄,我们可以要求它推荐治疗方法。
现在我们有所进展了。这些魔法是如何运作的?这就是我们在这里要探讨的。
连通性
第一个问题,网从何而来?来模拟成千上万个变量之间的相互作用。
建模这么多变量之间所有可能的相互作用几乎是不可能的。正是网络给了我们一种机制来穿越这种复杂性。通过让我们指定要建模的交互。目的是寻求一种足够丰富的模式。但不要过于复杂。
说到交互,我们如何决定对哪些进行建模?通常通过领域知识。在我们的案例中,利用了数千年临床实践和研究中获得的医学领域的集体知识。
我们的贝叶斯网会是什么样子?在结构上,一个巨大的有向图,有各种症状、疾病、医学测试、行为因素、生理因素和治疗方案的节点。用适当选择的(或推断的)弧线来模拟它们之间的重要相互作用。例如特定的症状和特定的疾病。
连通性细化
贝叶斯网络在结构上是一个有向图,一个无环图。有向意味着边缘有一个方向,这就是为什么它们被称为弧。非循环表示没有定向循环。这里举一个有向循环的例子:A→B→C→A。
除了无环性约束之外,建模者可以完全控制哪些节点与弧线连接以及如何定向它们。也就是说,在复杂的真实世界用例中,比如我们在这里讨论的(医疗诊断),有一个吸引人的指导原则。
选择弧线来模拟直接原因。将它们导向因果关系的方向。
所以如果 A 是 B 的直接原因,我们就加弧 A → B 。这样的网络被称为因果贝叶斯网络。
因果网络的结构只和它的变量以及因果关系的保真度一样精确。例如,事实可能是 A 导致 B 并且 B 导致 C 。但是我们可能甚至不知道 B 的存在。所以我们能做的最好的事情就是通过弧线 A → C 来建模。
因果建模
好吧,让我们从医学角度来考虑因果关系。这是我们想出来的。
**Variable Type A causes Variable Type B Example**disease causes symptom flu causes you to coughbehavior causes disease smoking causes lung cancerphysiological causes disease aging “causes” various
factor diseasestreatment "causes" disease chemotherapy reduces
cancertreatment causes side-effect chemotherapy causes
hair-loss
在结束这一部分之前,让我们注意一下,我们不应该太担心弄错一些因果弧。(当然,我们宁愿不要。)后果并不严重。事实上,我们很可能会在网络中发现一个新的非因果弧。对因果关系不明确或不存在的相关性进行建模。事实上,网络甚至分不清随意弧和非随意弧。在我们的用例中没有。
举这个例子。说 A 和 B 强相关。假设你认为 A 导致 B ,那么用圆弧 A → B 对此建模。但是你错了。添加这个弧线仍然是一件好事,因为它模拟了相关性。下一节将更详细地讨论非因果弧。
非因果弧
因果关系是网络设计中一个令人信服的指导原则。然而,这还不够。也就是说,添加非因果弧可以进一步改进模型。
考虑变量之间的相关性。例如在一组症状或一组疾病中。集合内的因果关系可能是未知的,甚至是不存在的。不过,我们确实想对相关性进行建模。所以要加上合适的“非因果”弧。
这里有一个简单的例子。说有强有力的证据证明干呕 h 和咽喉痛相关。假设这是网络中仅有的两个变量。在任一方向上用弧线将它们连接起来将捕捉到这种相关性。不考虑弧线会将它们视为独立的。我们不想那样。
网络的主方程
在某些时候,就像一幅画可以展现一个远景一样,数学也可以。我们正处于这一阶段。所以现在开始。
形式上,贝叶斯网络是在 n 个节点上的有向无环图。这些节点,称之为 X 1、 X 2、…、 X n,模拟随机变量。弧线模拟了它们之间的相互作用。
更准确地说,网络的结构将 n 个变量的联合分布分解为
P (X1, X 2,…,Xn)= product _IP(XI |双亲 (Xi))
这里有很多东西需要打开。先说:parents( X i)是有弧线进入 X i 的节点集合,嗯?
让我们用简单的例子慢慢来。都有相同的 5 个节点 A,B,C,D,e。
我们的第一个网络将没有弧线。所以没有一个节点有父节点。因此
P(A,B,C,D,E) = P(A)P(B)P(C)P(D)P(E)
我们的第二个网络将是一个马尔可夫链。在结构上,图是一条单路 A → B → C → D → E。节点 A 没有任何父节点。节点 B 的父节点是 a,节点 C 的父节点是 B,等等。因此
P(A,B,C,D,E) = P(A)P(B|A)P(C|B)P(D|C)P(E|D)
我们的第三个网络是朴素贝叶斯分类器,其中 E 作为类变量,A、B、C 和 D 作为预测变量。它的图形结构是
E → A,E → B,E → C,E → D
e 没有父母。A、B、C 和 D 中的每一个都有一个双亲:e。因此
P(A,B,C,D,E) = P(A|E)P(B|E)P(C|E)P(D|E)P(E
熟悉朴素贝叶斯分类器的读者会认识到这个等式右边的形式。把 A,B,C,D 看作预测值,E 看作类别变量。
现在我们已经准备好了一个临床例子。
临床网络示例:流感及其症状
考虑变量为流感、发烧、咳嗽、鼻塞、季节的网络。为简单起见,假设前四个是布尔型(是/否),第三个是分类型(春、夏、秋、冬)。
因果建模将产生以下弧线:
flu → fever, flu → cough, flu → stuffy nose
让我们给它们加上弧线flu → season。这不是一个因果弧,也就是说,我们可以翻转它的方向。但我们不会。因此它的方向与从流感发出的因果弧线方向一致。这将有助于下一节中的诊断。
有趣的是,这不是巧合,这个网络的结构是朴素贝叶斯分类器。
诊断:从症状到流感
我们想要我们患流感的概率,假设我们有发烧、咳嗽、鼻塞和冬季。让我们把它正式表达为
P(flu = *yes* | fever = *yes*, cough = *yes*, stuffy nose = *yes*, *season* = *winter*)
或者更简明地(更笼统地)表述为
P(flu|fever,cough,stuffy nose, season)
为了推断这一点,我们只需应用贝叶斯法则:
numerator(x) = P(fever|flu=x)*P(cough|flu=x)*P(stuffy nose|flu=x)*P(season | flu=x)*P(flu=x)P(flu=yes|fever, cough, stuffy nose, season) = numerator(yes)/(numerator(yes)+numerator(no))
这就是为什么这个网络被称为贝叶斯网络。从症状到疾病的推断包括贝叶斯推理。
“超越流感”网络
我们已经有一个处方,所以让我们执行。首先,开始添加额外疾病和症状的节点。第二,为行为、生理因素、医学测试等添加节点。第三,按照前面给出的指导,开始添加更多的因果弧。诸如
smoking → lung cancer, aging → disease-1, aging → disease-2, …, aging → disease-*k* chemotherapy → cancer, chemotherapy → hair-loss
接下来,开始添加合适的非因果弧。捕捉症状之间的相关性、疾病之间的相关性等。
这种网络“主干”的宏观结构如下。
behaviors, physiological factors ⇒ diseases
treatments ⇒ diseases
diseases ⇒ symptoms
treatments ⇒ side-effects
测试?
复数形式的术语表示某些类型的节点集。比如疾病。X Y 表示从 X 到 Y 的一组圆弧。该级别不显示特定圆弧的首尾。
我们已经讨论过为什么弧集是这样定向的。我们之所以选择行为和生理因素来共同影响疾病,是因为这两类因素相互作用。例如,某些不良行为选择对某些疾病的不利影响在老年人中往往比在年轻人中更大。
事实上,疾病的宏观父母可以更加详细。诸如
behaviors, physiological factors, treatments ⇒ diseases
这将对所有三种类型的因素、行为、生理因素和疾病治疗的联合相互作用进行建模。也就是说,这种宏观层面的互动通常会产生一个相当复杂的网络。因此,为了传达主干的本质,我们将坚持我们早先的宏观结构。也就是说,例外情况,即影响特定疾病的特定三元组(行为、生理因素、治疗)总是可以添加进来的。宏观结构只是一个大的图景,而不是一个可执行的模式。该模式仅处于精细级别,由网络的弧线指定。
注意我们有一组节点, tests ,它是悬空的。我们将让您思考如何将这台电视机连接到网络的其余部分。我们应该做检查**疾病,还是疾病**检查,还是其他什么?
培训“超越流感”网络
训练意味着从数据、信念或组合中估计模型的各种概率分布P(XI |parents(Xi))。
训练症状分布
让我们从学习任何一个以其父母为条件的症状的概率分布开始。让我们做一个简化的假设,一个症状的母体只能是疾病。例如,症状咳嗽的父母会包括流感和支气管炎。
给定一个症状 S 及其父代 pa ( S ),条件概率表捕捉到P(S|pa(S))在 pa ( S )中的疾病数为指数。这是因为原则上 pa ( S )中 n 疾病的任何子集都可能发生。(我们所说的“发生”是指在特定的就诊中诊断出。)还有 2^n 这样的子集。当 n 较大时,这可能相当大。
三个因素将共同缓解这一问题。一个是,大多数症状不会有大量的父母,即大量的疾病可以导致他们。
第二,在任何情况下,被诊断的疾病将是父母的稀疏子集。诊断实例对应于拍摄显示症状的特定人的疾病状态的快照。在所有可能出现这种症状的潜在疾病中,一个人几乎肯定最多只能被诊断出几种。如果不止一个。这种稀疏性对训练有很大帮助。简单来说,稀疏意味着“没有显著的高阶相互作用”。下面的一个数值例子将说明这一现象。
第三个因素是,我们对我们认为包含在给定症状 S 的父母 pa ( S )集合中的内容有一些控制。如果症状的父集变得特别大,我们可以删除与症状不太相关的疾病。
从数据中发现症状的父母
我们应该将哪些疾病设置为给定症状 S 的父母?以前我们建议,作为一个通用的指导方针,使用领域知识。在我们的特殊情况下,有一个更好的方法。患者记录将揭示哪些症状与哪些疾病相关。因此,这方面的结构也可以从数据中有所收获。患者病历记录了许多专家在不同情况下做出诊断的集体智慧。
从数据中了解症状的父母的好处是巨大的。这避免了网络设计人员必须获取领域知识来完成这项工作——无论是通过与领域专家的讨论、扩展阅读还是一些更复杂的机制。即使这项工作被分配给一个大型的建模师和领域专家团队,这样的手工设计也是费力且容易出错的。症状太多,疾病太多。
也就是说,领域知识仍然可以帮助填补病人记录中可能没有涵盖的情况的空白,或者消除信念和数据之间的不一致。简单来说,领域知识+数据驱动学习一般比单独使用任何一种都要好。
我们将在下一节详细讨论患者就诊记录,因为我们无论如何都需要它们来学习网络的参数,例如在P(S|pa(S))中的概率。不管我们如何得出 pa ( S )的结构。
患者就诊记录
我们假设与医学专家的每一次互动都会生成一个新记录,记录观察到的症状和诊断出的疾病。如果诊断出多种疾病,那么观察到的哪些症状与哪种疾病有关也将被捕获。医学专家认为。诊断可以是确定的,也可以是专家认为合适的推测。我们所关心的是这是由专业人士做的。
让我们看一个病人就诊记录的例子。编造的。不是医学建议!
(*symptoms* = high fever, cough, sore throat, lump in throat; *disease* = flu)
(*symptoms* = lump in throat, chest pain; *disease* = gerd)
在这次访问中,确诊了两种疾病:流感和 GERD 。健康专家暗示两者都有咽喉肿块。
从这样的记录中,我们可以得到以症状为中心的表征,每个表征对应一个观察到的症状。这种表示列出了就诊期间与该症状相关的诊断疾病。这些疾病在就诊记录中也被称为症状的父母。
在我们上面的例子中,记录中喉头肿块的父母是流感和 GERD 。
以症状为中心的表征有助于学习症状分布。
发现症状的父母
从我们可以访问的所有患者就诊记录中收集以症状为中心的表征,我们可以很容易地确定症状的父母。这些都是这个数据所涉及的疾病。如果我们只能从单个病人就诊记录中学习的话,喉咙哽咽的父母应该是流感和 GERD 。
对于某些症状,大量不同的患者就诊记录可能会产生大量的父母。如前所述,我们可以通过删除与症状不太相关的父项来删减如此大的集合。
根据患者就诊记录训练症状分布
我们想知道,对于每一个症状,它的分布取决于它的父母。我们有一个以症状为中心的数据集可用于这项学习。(如前所述,这来源于患者就诊记录。)
考虑这个数据集中的任何一个实例。它列出了一个症状,以及在患者就诊期间与之相关的疾病。它没有列出症状父母中与没有牵连的疾病。正如我们将在下面看到的,我们也需要这些信息。幸运的是,我们可以通过从症状的父母中减去牵连疾病来推断这些疾病。
让我们看一个例子。说咳嗽的父母是流感肺炎哮喘。(在真实的网络中,这个列表将包括更多的疾病。)比如说咳嗽的父母在某个病历里是流感。由此,我们可以推断,在这种情况下咳嗽不是由肺炎或哮喘引起的。虽然在这种情况下,这种推断不是 100%正确的,但重复出现这种相同的推断确实给出了相关条件概率的良好估计。
从这两条信息——症状父母中的哪些疾病与特定患者记录有关,哪些不在特定患者记录中——我们将导出以下形式的训练向量。
cough flu pneumonia asthma
1 1 0 0
这很容易读懂。它说,在这个患者记录中,咳嗽是存在的,并且在咳嗽的父母中,流感被诊断,肺炎未被诊断,哮喘未被诊断。
接下来,考虑一个患者记录,其观察到的症状列表不包括咳嗽。接下来,根据这组父母中的疾病是否在该记录中被诊断,导出该记录中咳嗽的父母的值。
这里有一个例子。假设患者记录导致了诊断
(symptoms = shortness of breath, chest pain, wheezing; diseases = asthma)
由此,我们可以得出记录
cough flu pneumonia asthma
0 0 0 1
有了足够丰富的此类记录,当然随着人们在可预见的未来不断生病,这些记录还会不断增加,我们可以了解到 P ( 咳嗽 | 父母 ( 咳嗽)。更广泛地说,任何症状的分布取决于其父母。
单独来看,这样的训练实例是完美的吗?不。在诊断中没有疾病并不意味着现在或不久就没有。这同样适用于症状。也就是说,在足够多样化的环境下,经过大量的训练,这种噪音应该会被信号淹没。例如,如果诊断流感的记录中只有 30%也显示咳嗽是一种观察到的症状,我们可以很有把握地推断流感产生咳嗽作为观察到的症状的时间不超过一半。
训练行为和生理因素对疾病的影响
这里我们提炼宏观结构
behaviors, physiological factors ⇒ diseases
我们将假设所需的信息也可能来自患者记录。
我们试图为每种疾病 D 估计以其双亲为条件的 D 分布的参数。 D 的父母是行为和生理因素的合适子集。哪些行为,哪些生理因素?这些可以通过领域知识来设置,因为关于哪些行为会影响哪些疾病已经知道了很多。(不利地或有利地。)同样对于生理因素。可选地或附加地,疾病的双亲也可以从数据中推断。
让我们从数据上来说明这样的训练。考虑以下患者记录
smoker, 50 years old, male, diagnosed: lung cancer
首先,从这些记录的集合中,我们可以推断肺癌的父母,即影响其诊断的行为和生理因素。与症状分布一样,我们需要另外两种类型的信息来估计肺癌在其父母已知的情况下的分布。
- 在肺癌的特定诊断中,父母哪一方失踪了?
- 如何估计一个人在部分父母在场的情况下不患肺癌的概率?
对于 1,与症状情况一样,缺失的父母是完整的父母集减去该患者记录中的父母集。对于 2,也是在症状的情况下,我们从患者记录中得到这些,其中一些父母患有肺癌,而患者被诊断为没有肺癌。一个例子是没有肺癌的吸烟者。我们如何决定一个因素是否“关键”?试试领域知识。
训练治疗对疾病的影响
我们这里有个问题。我们的宏观结构模式
behaviors, physiological factors ⇒ diseases
treatments ⇒ diseases
也就是说,任何单一疾病 D 都会有两组亲本,一组涉及行为和生理因素的某些组合,另一组涉及治疗。当然,我们可以将这两组父母合二为一。广泛地这样做有前面讨论过的问题。也就是说,在特定疾病的背景下,行为、生理因素和治疗的特定三元组可能值得包括在内。(如前所述。)
总而言之,我们不想崩溃
behaviors, physiological factors ⇒ diseases
treatments ⇒ diseases
到…里面
behaviors, physiological factors, treatments ⇒ diseases
一般来说。
让两组父母分开
那么,对于一种给定的疾病,我们如何将两组父母分开 D ?一种方法是为 D (我们称之为 DI )引入一个额外的变量,如下。
behaviors, physiological factors ⇒ *DI* treatments, DI ⇒ D
我们可以认为 DI 是对疾病发作的建模,D 是对疾病在一次或多次治疗后的下一个状态的建模。也就是说,这种方案无法模拟疾病对治疗的动态演变。这将要求 D 是 DI 的父代,这将违反贝叶斯网络上的无环性约束。
让我们看一个具体的例子。
diet, age, gender → heart disease-I
heart-disease-I, treatment → heart disease
治疗和副作用
让我们从简单的开始。每个副作用都有一个节点。我们对每种治疗都有一个节点。一个副作用的父母都是有那个副作用的治疗。
让我们看一个例子。
chemotherapy, bone marrow transplantation, …, → fatigue
在我们的网络中包含这样的弧有什么价值?一个是它让我们寻找对特定疾病既有效又有相对轻微副作用的治疗方法。
在这个标度网络中的推论
让我们从重复我们网络的宏观结构开始。这有助于了解网络适合什么类型的推理。
behaviors, physiological factors ⇒ diseases
treatments ⇒ diseases
diseases ⇒ symptoms
treatments ⇒ side-effects
tests ?
现在来看具体的推论。每一个后面都有一个关于它如何工作的解释。在这个解释中,我们关注的是是否以及如何从数据或领域知识中计算出各种概率。目的是深入了解网络结构如何简化各种计算。
在实践中,人们可能将推理算法用作黑盒,它将在幕后做任何事情。
如果我吸烟,我是女性,我 75 岁了,患肺癌的可能性有多大?
我们求 P ( 肺癌 | 抽烟,女, 75 岁)。
好消息是,这个推论所依据的所有观察结果都是肺癌患者的父母。
坏消息是,肺癌可能有额外的父母。这些需要被边缘化。边缘化包括平均这些额外的父母可以采取的各种价值,加权的概率。由于这些价值观的数量与新增父母的数量成指数关系,边缘化是一个缓慢的过程。确实存在复杂的算法来加速它。他们的讨论超出了本文的范围。
节点分布的常用限制可以缓存在节点上。可以认为这是附加到节点 S 上的,不仅是 P ( S | 父节点 ( S ))还有 P ( S | 子集 ( 父节点 ( S ))用于合适的父节点(S)然后可以适当地使用这种缓存的分发,减少即时边缘化的需要。
本人吸烟,女,75 岁。我一直咳嗽。我有肺癌的可能性是多少?
我们求 P ( 肺癌 | 抽烟,女, 75 岁,持续咳嗽)。根据贝叶斯法则,
P(lung cancer | smokes, female, 75 years old, persistent cough) =
P(smokes, female, 75 years old, persistent cough | lung cancer)*
**P(lung cancer)**/
**P(smokes, female, 75 years old, persistent cough)**
(稍后我们将解释粗体字体。)
接下来,我们利用一个重要的属性。
给定其父节点,节点有条件地独立于其非后代节点。
由于这是我们第一次在本帖中看到这个属性,让我们深入研究一下。考虑网络A→B→C。(一个马尔可夫链。)应用前述的条件独立概率,我们得到给定 B 的情况下 C 独立于 A 。即P(C|B, A )等于P(C|B)。或者换句话说,一旦我们观察到了B,A的值不会为预测 C 的值提供额外的信息。
将这种条件独立性应用于我们的情况给出
P(smokes, female, 75 years old, persistent cough | lung cancer) =
**P(smokes,female,75 years old|lung cancer)***
**P(persistent cough|lung cancer)**
好,现在让我们收集所有粗体的术语。这些都是有待估计的。我们在下面复制了它们。
**P(lung cancer)
P(smokes, female, 75 years old, persistent cough)
P(smokes,female,75 years old|lung cancer)
P(persistent cough|lung cancer)**
从一组足够丰富的患者记录中很容易估计出 P。一些有用的估计可能已经存在于公共领域。
P ( 持续咳嗽 | 肺癌)也可以从患者记录中估计,作为诊断为肺癌的记录中具有持续咳嗽作为观察症状的部分。
为了估算 P ( 抽烟,女, 75 岁,持续咳嗽),我们就调用独立性假设。这就给我们留下了 P ( 抽烟)P(年龄)P(持续咳嗽)P(女)。前三个很容易从数据结合知识来估计。最后一个我们可以设置为 0.5。
稍微跑题一下,严格来说,上一段提到的变量并不都是完全独立的。例如,女性比男性长寿,所以年龄和性别至少是轻度相关的。
最后只剩下 P ( 抽烟,女, 75 岁 | 肺癌)。调理(抽烟、女、 75 岁)对肺癌使得前三者有条件依赖。因此,我们应该尽可能避免援引独立性。如果我们不能,那也不是世界末日。由此产生的推论仍然是有意义的解释。具体来说,它作为一个朴素贝叶斯分类器运行,根据作为条件独立结果治疗的吸烟、女性、年龄和持续咳嗽来预测肺癌。
宏课
从上述例子中得到的宏观教训是,当试图从一些观察到的生理因素和一些观察到的症状来诊断疾病时,生理因素可以被合理地假设为与给定疾病的症状无关。当然,老年人可能比年轻人更容易表现出某些症状。然而,当我们以某种疾病作为解释症状的附加条件时,相比之下,变老的额外影响是很小的。
什么癌症疗法副作用最小?
让我们用逻辑和概率的混合来表达这一点。我们寻求这样的治疗方法:P ( 癌症 | T )高,而对于每一种副作用 SE ,P(SE|T)低。这里的关键观察是,在两种概率中,作为条件的变量是我们试图计算其概率分布的变量的父变量。(在前面的句子中,如果“变量”一词引起了混淆,请将其替换为“事件”。)因此,我们可以利用网络的结构来高效地计算我们想要的东西。
延伸阅读
https://www . science direct . com/science/article/pii/s 1532046418302041
【https://www.ncbi.nlm.nih.gov/pmc/articles/PMC5519723/】在本文中,疾病和症状提及也是从护士笔记等非结构化文本中提取的。为此,命名实体识别(NER)技术非常有用。(在这种情况下,命名实体是疾病和症状。)查看https://towards data science . com/named-entity-recognition-in-NLP-be 09139 fa 7b 8了解更多关于 NER 的信息。
http://www . cs . CMU . edu/~guest rin/Class/10701-S05/slides/bns-inference . pdf此处为精辟示例
flu, allergy → sinus, sinus → headache, sinus → nose
请将此理解为“流感或过敏导致鼻窦,鼻窦导致头痛,鼻窦会妨碍鼻子的正常功能”。
用 Python 建模你的股票投资组合表现
用 200 行代码评估你的交易表现

当我刚开始学习 Python 时,我的目标之一是理解如何更好地评估我的股票投资组合的财务表现。我看到了凯文·博勒(Kevin Boller)的一篇很棒的文章(T5 ),他用 python 将投资组合与标准普尔 500 指数进行了比较。我强烈建议花几分钟时间通读它,以便更好地理解基本的金融概念,因为我在这里不会深入探讨。
然而,我想要的是数据的时间序列显示,而这更多的是一个时间点的汇总。最初的代码还假设股票销售永远不会发生,我希望它反映在动态时间框架内买入/卖出头寸的现实。
本文的其余部分将探讨我如何构建代码的过程,在时间加权的基础上准确计算性能的挑战,以及如何显示最终结果。下面解释的文件和代码可以在这里找到并分叉。
构建问题
我们的目标是从某人的投资组合中读取一个包含“买入”和“卖出”的 CSV 文件,然后计算任意指定时间范围内相对于某个指数的回报率。“听起来不可怕!”——在开始编码之前,我脑海中傲慢的声音说道。事实证明,语音是一个绝望的乐观主义者,实际上有很多障碍,包括:
- 对于相对的开始和结束日期,如何计算开始时的“当前”持有量?
- 在给定的时间范围内,你如何计算同一仓位的多次买入和卖出?
- 当同一支股票在不同的成本基础上进行多次购买/销售时,如何准确描述成本基础?
为了更好地说明这些挑战,这里有一张图,试图将一个场景可视化:

显示买卖后正确计算的成本/份额的示例
请看插图,最上面的表格代表投资组合中的一些虚假的买入/卖出数据。下面的部分显示了所选时间段内不同时间点的每日快照。正如你所看到的,在开始日期之前的交易在计算时间段开始时的活跃余额时变得非常重要。仅仅因为我在开始日期之前购买了 100 股苹果股票,并不意味着它应该被排除在外。
你还会注意到,在表格的底部中间,卖出 AAPL 股票时,每股成本变成了 11.53 美元,这是为什么呢?因为 AAPL 股票是在两天后以不同的价格购买的,我们需要合计并平均成本。这意味着你销售的顺序变得很重要!对于本文,我们将假设一切都符合 FIFO 标准,这意味着最早买入的股票是最先卖出的股票。最后,我们看到购买 MSFT 股票将被排除,因为它存在于时间范围之外,所以我们需要在我们的计划中考虑这一点。
解决方案提案
在考虑了上面提到的挑战后,我决定创建一个新的持股和股票价格的“每日”计算,这对于生成准确的分析是必要的。作为一个额外的好处,凯文文章中提到的一切也是可行的,因为几乎所有的计算都是基于拍摄一张快照并将其与后来的快照进行比较。记住这一切,我们将采用以下方法来构建它:
- 读取包含所有买入/卖出交易日期的投资组合文件,然后在指定的结束日期之前提取所有报价机的每日数据(记住我们需要担心开始日期之前的买入/卖出)
- 根据提供的起始日期,计算“活跃”投资组合持有量和调整后的单位持有成本
- 在我的投资组合中创建一个每日“活跃持有”清单,以及股票的每日收盘价
- 基于每天的合并数据创建计算
现在我们已经有了一个我们想要完成的松散结构,让我们开始打字吧!
Doggo 正在做一个蛇 boi 程序
步骤 1 —获取数据
我们需要做的第一件事是获取我们投资组合中股票的每日数据,以及我们评估投资组合的基准数据。股票数据通常不会以开源或免费的形式提供,尽管有很多令人敬畏的服务,如用于核心分析的 Quandl 或 IEXFinance ,它们对我的普锐斯需求来说是一辆法拉利。幸运的是,有一个名为 yfinance 的优秀库,它可以抓取雅虎财经的股票数据,并以结构化的形式返回。因此,让我们从导入我们需要的库和获取数据的前几个函数开始:
我们正在编写的第一个函数叫做create_market_cal,它使用pandas _ market _ calendars库来查找指定时间段内的所有相关交易日。这个库根据市场自动过滤掉非交易日,所以我不需要担心通过使用类似 pandas.date_range 的东西将数据连接到无效日期。由于我的股票都在美国,我将选择NYSE作为我的日历,然后标准化时间戳,使它们便于以后加入。
get_data函数获取一组股票报价器以及开始和结束日期,然后使用上面列出的 yfinance 库获取数据。您会注意到结束日期参数包括一个timedelta转移,这是因为 yfinance 不包括您提供的结束日期。由于我们不想在设置参数时记住这个警告,我们将使用 timedelta 将这里的日期移动+1。
最后,get_benchmark函数只是馈入get_data,然后放下股票代码。现在我们已经有了初始函数,让我们运行下面的程序来给变量赋值:
portfolio_df = pd.read_csv('stock_transactions.csv')
portfolio_df['Open date'] = pd.to_datetime(portfolio_df['Open date'])symbols = portfolio_df.Symbol.unique()
stocks_start = datetime.datetime(2017, 9, 1)
stocks_end = datetime.datetime(2020, 4, 7)daily_adj_close = get_data(symbols, stocks_start, stocks_end)
daily_adj_close = daily_adj_close[['Close']].reset_index()
daily_benchmark = get_benchmark(['SPY'], stocks_start, stocks_end)
daily_benchmark = daily_benchmark[['Date', 'Close']]
market_cal = create_market_cal(stocks_start, stocks_end)
作为参考,我的 CSV 文件包含以下各列,如果您尝试复制,您需要确保您的 CSV 包含相同的列:

事务 CSV 格式
我们现在有四个重要的数据集来继续:
portfolio_df根据我们的购买/销售交易历史daily_adj_close在指定的结束日期之前,每天关闭我们库存中的所有票据daily_benchmark与每日收盘数据进行基准比较market_cal包含我们时间段内市场开放的日期
利用这一点,我们可以前进到下一步,走向辉煌!
第 2 步——找到我们最初的活跃投资组合
现在我们有了这四个数据集,我们需要计算出在指定的起始日期我们积极持有了多少股票。为此,我们将创建两个函数,portfolio_start_balance和position_adjust。
将输出分配给一个变量应该会给你投资组合中的活跃头寸:
active_portfolio = portfolio_start_balance(portfolio_df, stocks_start)
现在我们可以看到代码了,让我们看看内部的工作原理,用通俗的语言展示一下发生了什么。
portfolio_start_balance
首先,我们向portfolio_start_balance函数提供 CSV 数据和开始日期,并创建在开始日期之前发生的所有交易的数据框架。然后,我们将检查在 start _ date 之后是否有未来销售,因为我们最终将重建该数据帧的快照:
positions_before_start = portfolio[portfolio['Open date'] <= start_date]
future_sales = portfolio[(portfolio['Open date'] >= start_date) & (portfolio['Type'] == 'Sell.FIFO')]
然后,我们将创建一个在 start_date 之前发生的销售数据框架。我们需要确保在指定的 start_date,这些都从我们的活动投资组合中剔除:
sales = positions_before_start[positions_before_start['Type'] == 'Sell.FIFO'].groupby(['Symbol'])['Qty'].sum()
sales = sales.reset_index()
接下来,我们将制作在指定时间段内没有任何销售发生的头寸的最终数据框架:
positions_no_change = positions_before_start[~positions_before_start['Symbol'].isin(sales['Symbol'].unique())]
现在,我们将遍历我们的sales数据帧中的每一笔销售,调用我们的position_adjust函数,然后将它的输出追加到我们的空adj_postitions_df中:
adj_positions_df = pd.DataFrame()
for sale in sales.iterrows():
adj_positions = position_adjust(positions_before_start, sale)
adj_positions_df = adj_positions_df.append(adj_positions)
现在让我们看看position_adjust函数是如何工作的,这样我们就可以完全理解这里发生了什么。
position_adjust
首先,我们将创建一个名为stocks_with_sales的空数据框架,稍后我们将在其中添加调整后的头寸,另一个数据框架包含所有标记为“买入”的交易。
请记住,我们已经在portfolio_start_balance函数中过滤了“未来购买”,因此无需在此重复。你还会注意到我们是按“开仓日期”排序的,这很重要,因为我们想用 FIFO 方法减去仓位。通过对日期进行排序,我们知道我们可以迭代地遍历从旧到新的位置列表:
stocks_with_sales = pd.DataFrame()
buys_before_start = daily_positions[daily_positions['Type'] == 'Buy'].sort_values(by='Open date')
现在我们在一个数据框架中有了所有的买入,我们将过滤股票代码与卖出位置的股票代码相匹配的所有买入:
for position in buys_before_start[buys_before_start['Symbol'] == sale[1]['Symbol']].iterrows():
您会注意到,我们使用索引来访问数据中的“符号”列。这是因为使用iterrows()从索引[0]和数据序列[1]创建了一个元组。这与我们在遍历buys_before_start时使用索引的原因相同:
for position in buys_before_start[buys_before_start['Symbol'] == sale[1]['Symbol']].iterrows():
if position[1]['Qty'] <= sale[1]['Qty']:
sale[1]['Qty'] -= position[1]['Qty']
position[1]['Qty'] = 0
else:
position[1]['Qty'] -= sale[1]['Qty']
sale[1]['Qty'] -= sale[1]['Qty']
stocks_with_sales = stocks_with_sales.append(position[1])
所以循环中发生的是在buys_before_start的每一次购买:
- 如果最早的买入数量≤卖出数量(也就是你卖出的数量超过了你最初的买入数量),从卖出数量中减去买入数量,然后将买入数量设置为 0
- 否则(某一天你买入的数量>卖出的数量),从买入位置减去卖出数量,然后从卖出位置减去相同的数量
- 将调整后的位置附加到空的
stock_with_sales数据帧中
一旦循环通过每个销售位置,您的代码现在将执行portfolio_start_balance的最后几行:
adj_positions_df = adj_positions_df.append(positions_no_change)
adj_positions_df = adj_positions_df.append(future_sales)
adj_positions_df = adj_positions_df[adj_positions_df['Qty'] > 0]
因此,我们在adj_positions_df中获取调整后的头寸,添加从未有过销售的回售头寸,添加未来发生的回售头寸,并最终过滤掉position_adjust清零的任何行。你现在应该有一个准确的记录,你的积极持有开始日期!
步骤 3 —创建每日性能快照
现在我们有了在开始日期持仓的准确报表,让我们创建每日业绩数据!我们的策略类似于我们在步骤 2 中所做的,事实上,我们将再次重复使用position_adjust方法,因为我们需要考虑我们的日期范围内的潜在销售额。我们将继续创建两个新函数,time_fill和fifo,我将更详细地解释每个函数的作用:
时间填充
与portfolio_start_balance类似,我们的目标是提供活跃头寸的数据框架,找到卖出头寸,并根据买入头寸将卖出头寸清零。这里的主要区别是,我们将使用有效交易日的market_cal列表进行循环:
sales = portfolio[portfolio['Type'] == 'Sell.FIFO'].groupby(['Symbol','Open date'])['Qty'].sum()
sales = sales.reset_index()per_day_balance = []
for date in market_cal:
if (sales['Open date'] == date).any():
portfolio = fifo(portfolio, sales, date)
这样,我们可以每天查看是否有销售发生,正确调整头寸,然后返回每日数据的正确快照。此外,我们还将过滤之前或当前date发生的头寸,并确保只有买入。然后,我们将在 market_cal 循环中添加一个带有当前date的Date Snapshot列,然后将其追加到我们的per_day_balance列表中:
daily_positions = portfolio[portfolio['Open date'] <= date]
daily_positions = daily_positions[daily_positions['Type'] == 'Buy']
daily_positions['Date Snapshot'] = date
per_day_balance.append(daily_positions)
先入先出
我们的fifo函数获取您的活跃投资组合头寸、在time_fill中创建的销售数据框架以及在market_cal列表中的当前date。然后对sales进行过滤,找出当前date发生的任何情况,并创建不受sales影响的位置的数据帧:
sales = sales[sales['Open date'] == date]
daily_positions = daily_positions[daily_positions['Open date'] <= date]
positions_no_change = daily_positions[~daily_positions['Symbol']. isin(sales['Symbol'].unique())]
然后,我们将使用我们可靠的position_adjust函数来清除任何活跃销售的头寸。如果在特定日期没有销售,我们的函数将简单地将positions_no_change附加到空的adj_positions数据帧上,为您留下准确的每日头寸快照:
adj_positions = pd.DataFrame()
for sale in sales.iterrows():
adj_positions = adj_positions.append(position_adjust( daily_positions, sale))
adj_positions = adj_positions.append(positions_no_change)
adj_positions = adj_positions[adj_positions['Qty'] > 0]
运行这行代码应该会返回指定时间范围内所有交易日的列表,以及每天头寸的准确计数:
positions_per_day = time_fill(active_portfolio, market_cal)
步骤 4 —进行投资组合计算
如果你还跟着我们,我们就在最后阶段了!现在我们已经有了我们的活跃持仓量的准确的每日分类账,我们可以继续创建生成图表所需的最终计算!为此,我们将在代码中添加另外六个函数:
让我们从最后一个函数per_day_portfolio_calcs开始,因为它将使用所有其他函数。
每日投资组合计算
现在我们已经有了步骤 3 中的positions_per_day,我们的目标是将它与daily_benchmark、daily_adj_close和stocks_start一起传递给这个新函数:
combined_df = per_day_portfolio_calcs(positions_per_day, daily_benchmark, daily_adj_close, stocks_start)
然后,我们将使用pd.concat将我们的数据帧列表连接成一个列表:
df = pd.concat(per_day_holdings, sort=True)
现在我们有了一个大数据帧,我们将把它传递给per_day_portfolio_calcs中的其余函数。
修改后的每股成本
如果我们想跟踪每天的表现,我们需要知道我们每天持有股票的理论价值。这需要计算当前持有的证券数量,然后乘以每只证券的每日收盘价。
mcps = modified_cost_per_share(df, daily_adj_close, stocks_start)
为此,我们提供了新的单一 df 以及使用 yfinance 提取的每日数据,以及我们的开始日期。然后,我们将通过将投资组合快照的日期与每日数据的日期相结合,以及在自动收报机上相结合,来将我们的投资组合与每日收盘数据相结合。对于更熟悉 SQL 的人来说,这实际上是一个左连接:
df = pd.merge(portfolio, adj_close, left_on=['Date Snapshot', 'Symbol'],right_on=['Date', 'Ticker'], how='left')
一旦我们合并了df,我们将把每日收盘重新命名为“符号调整收盘”,然后用每日收盘乘以拥有的股票数量。删除额外的列将返回我们需要继续的数据帧:
df.rename(columns={'Close': 'Symbol Adj Close'}, inplace=True)
df['Adj cost daily'] = df['Symbol Adj Close'] * df['Qty']
df = df.drop(['Ticker', 'Date'], axis=1)
基准 _ 投资组合 _ 计算
现在我们有了证券的准确每日成本,我们希望将基准添加到数据集中,以便与我们的投资组合进行比较:
bpc = benchmark_portfolio_calcs(mcps, daily_benchmark)
我们首先使用类似于modified_cost_per_share中的合并将我们的每日基准数据合并到正确的快照中:
portfolio = pd.merge(portfolio, benchmark, left_on=['Date Snapshot'], right_on=['Date'], how='left')
portfolio = portfolio.drop(['Date'], axis=1)
portfolio.rename(columns={'Close': 'Benchmark Close'}, inplace=True)
现在我们已经将基准的每日收盘数据合并到我们的投资组合数据集中,我们将根据其最大和最小日期过滤我们的daily_benchmark数据。使用最大值和最小值来对比你的开始和结束日期是很重要的,因为最大值/最小值会考虑市场开放的日子:
benchmark_max = benchmark[benchmark['Date'] == benchmark['Date'].max()]
portfolio['Benchmark End Date Close'] = portfolio.apply(lambda x: benchmark_max['Close'], axis=1)benchmark_min = benchmark[benchmark['Date'] == benchmark['Date'].min()]
portfolio['Benchmark Start Date Close'] = portfolio.apply(lambda x: benchmark_min['Close'], axis=1)
太好了!因此,现在我们在投资组合数据集中也有了基准的绝对开始和结束收盘,这在计算每日回报时将非常重要。
投资组合 _ 年末 _ 统计
现在我们的基准数据已经添加完毕,让我们进入下一步:
pes = portfolio_end_of_year_stats(bpc, daily_adj_close)
我们这里的目标是获取benchmark_portfolio_calcs的输出,找到投资组合中所有股票的最后一天收盘,然后向我们的投资组合数据集中添加一个Ticker End Date Close列。我们将再次合并到每日股票数据中,过滤最大日期,然后根据股票代码进行连接:
adj_close_end = adj_close_end[adj_close_end['Date'] == adj_close_end['Date'].max()]portfolio_end_data = pd.merge(portfolio, adj_close_end, left_on='Symbol', right_on='Ticker')portfolio_end_data.rename(columns={'Close': 'Ticker End Date Close'}, inplace=True)portfolio_end_data = portfolio_end_data.drop(['Ticker', 'Date'], axis=1)
现在,在我们生成计算结果之前,只需再走一步!
投资组合 _ 年初 _ 统计
这最后一步采用更新的投资组合数据框架,即来自 yfinance 的每日股票数据,并为基准分配年初等价头寸:
pss = portfolio_start_of_year_stats(pes, daily_adj_close)
我们将首先过滤开始日期的每日收盘数据,然后使用股票代码将我们的投资组合数据合并到其中。为了方便起见,我们将这种关闭称为Ticker Start Date Close:
adj_close_start = adj_close_start[adj_close_start['Date'] == adj_close_start['Date'].min()]portfolio_start = pd.merge(portfolio, adj_close_start[['Ticker', 'Close', 'Date']], left_on='Symbol', right_on='Ticker')portfolio_start.rename(columns={'Close': 'Ticker Start Date Close'}, inplace=True)
然后我们需要调整每股成本,但是为什么呢?想象一下,你很久以前以 500 美元/股的价格购买了谷歌,但现在你想计算 2020 年你的头寸的 YTD 回报率。如果你用 500 美元作为你 2020 年初的成本基础,你不会有一个准确的比较,因为成本基础是多年前的。为了解决这个问题,我们将使用 Numpy 的where函数:
portfolio_start['Adj cost per share'] = np.where(portfolio_start['Open date'] <= portfolio_start['Date'],
portfolio_start['Ticker Start Date Close'],
portfolio_start['Adj cost per share'])
简单来说,这就是说‘如果开市日期≤开始日期的日期,那么Adj cost per share等于Ticker Start Date Close’(股票从 yfinance 数据上的 min 日期开始的收盘价)。如果没有,那就用现有的Adj cost per share。
剩余部分根据修改后的每股成本修改调整后的成本,从合并中删除不需要的列,然后根据新计算的调整后成本计算您将拥有的基准股票的等量:
portfolio_start['Adj cost'] = portfolio_start['Adj cost per share'] * portfolio_start['Qty']
portfolio_start = portfolio_start.drop(['Ticker', 'Date'], axis=1)portfolio_start['Equiv Benchmark Shares'] = portfolio_start['Adj cost'] / portfolio_start['Benchmark Start Date Close']portfolio_start['Benchmark Start Date Cost'] = portfolio_start['Equiv Benchmark Shares'] * portfolio_start['Benchmark Start Date Close']
恭喜,我们现在已经有了正确计算回报的所有必要数据!让我们完成最后一部分,然后开始想象!
计算返回
这里的最后一步只是从所有其他函数中提取聚合数据帧,对我们一直在修改的数据应用一系列计算,并返回最终数据帧:
returns = calc_returns(pss)
第一组Benchmark Return和Ticker Return都使用当前收盘价除以其初始成本基础来计算回报:
portfolio['Benchmark Return'] = portfolio['Benchmark Close'] / portfolio['Benchmark Start Date Close'] - 1portfolio['Ticker Return'] = portfolio['Symbol Adj Close'] / portfolio['Adj cost per share'] - 1
每家公司的份额价值以相同的方式计算,使用我们之前计算的修改后的每日数量和等效基准份额:
portfolio['Ticker Share Value'] = portfolio['Qty'] * portfolio['Symbol Adj Close']portfolio['Benchmark Share Value'] = portfolio['Equiv Benchmark Shares'] * portfolio['Benchmark Close']
我们将再次做同样的事情来计算货币收益/损失,从我们在portfolio_start_of_year_stats函数中计算的修改后的调整成本中减去股票价值列:
portfolio['Stock Gain / (Loss)'] = portfolio['Ticker Share Value'] - portfolio['Adj cost']portfolio['Benchmark Gain / (Loss)'] = portfolio['Benchmark Share Value'] - portfolio['Adj cost']
最后,我们将使用之前计算的基准指标来计算绝对回报值:
portfolio['Abs Value Compare'] = portfolio['Ticker Share Value'] - portfolio['Benchmark Start Date Cost']portfolio['Abs Value Return'] = portfolio['Abs Value Compare']/portfolio['Benchmark Start Date Cost']portfolio['Abs. Return Compare'] = portfolio['Ticker Return'] - portfolio['Benchmark Return']
嘣!现在,让我们弄清楚如何绘制我们的新数据,并完成这项工作。
步骤 4 —可视化数据
那么,现在我们已经经历了所有这些来获得我们的日常性能数据,我们应该如何最好地显示它呢?每日数据的最大好处是可以看到你的头寸在一段时间内的表现,所以让我们先来看看我们的汇总数据。
在最近的项目中,我一直在使用 Plotly,所以为了做到这一点,我将选择 simple 并使用 Plotly Express 库。由于我们需要将每天的股票汇总到一个单一的每日指标中,我将把它写成一个函数,它采用您完成的数据框架和两个您想要相互对照的指标:
如您所见,我们将提供 ticker 和基准收益/损失作为指标,然后使用 groupby 将每日业绩汇总到投资组合级别。画出来应该会返回类似这样的东西!

每日累计损益与基准对比
您还可以使用不同的指标(如Abs Value Compare)进行聚合,将其视为一行:

使用绝对值比较度量显示
这很棒,但在我看来,最有用的视图可以通过使用 plotly express 中的facet_col选项来生成每个股票的图表,将基准与每个股票的性能进行比较:
我们还将使用facet_col_wrap参数来限制每行的图表数量。运行这段代码应该会生成类似下面的输出!

每只股票的基准回报比较示例数据
结论
我们在这里讨论了很多内容,希望这对学习更多关于填充和分析财务数据的知识有所帮助!未来还有很多可以探索的领域,包括:
- 考虑分红和股票分割——yfinance 有这个数据,但我想在添加更多功能之前先发布这个数据
- 加入更多奇异的指标,比如波动性比较和滚动平均。在投资组合层面与基准层面进行比较可能会很有意思
- 改善可操作性的信号。例如,你可以利用日间数据,观察短期均线何时穿过长期均线,作为潜在买入/卖出头寸的信号
希望这对任何人都有帮助,并随时联系或在下面评论问题/评论。感谢阅读!
给疫情做模特
复杂系统
使用 Python 和 pandas 构建基于代理的模型

克里斯多夫·伯恩斯在 Unsplash 上拍摄的照片
在我们的日常生活中,我们会遇到许多复杂的系统,在这些系统中,个人会相互影响,例如股票市场或高峰时间的交通。为这些复杂系统找到合适的模型可以让我们更好地理解它们的动力学,并允许我们在变化的条件下模拟其行为。对复杂系统建模的一种方式是使用基于主体的模型,这意味着我们是明确地模拟个体和他们的相互作用,而不是以集合的方式推导系统的动态。
您可以在这里找到一篇关于基于代理的模型的介绍性文章:
基于代理的模型初学者指南:它们如何工作和做什么
medium.com](https://medium.com/swlh/overcoming-complexity-with-agent-based-models-5c4cca37cc61)
在这篇文章中,我们想用 python 开发这样一个基于代理的模型。作为一个例子,我们试图模拟疫情的行为。请注意,我根本不是流行病学家。这篇文章的目标不是建立一个复杂的模型来预测现实生活,而是看看我们如何建立一个简单的基于主体的模型并研究一些由此产生的动力学。让我们从一些基本的考虑因素开始。
我们模型的基础
对于我们的例子,我们假设一种非致命的疾病可能在相互接触的个体之间传播。最基本的方法是考虑三个不同的群体:
- 尚未感染的个体,称为易感组。
- 被感染并可能传播疾病的个体。
- 已经从疾病中康复的个体现在是免疫的。
因为有三个相关的组(Su 可接受, I 感染, R 恢复),这些模型也被称为 SIR 模型。
解析 SIR 模型
我们将从一个数学 SIR 模型开始,它将作为一个基准模型。在基本 SIR 模型中,三组之间的流量为:S -> I -> R。这是一条单行道,在这条单行道上,开始时大多数个体都属于 S 群体,最终通过 I 群体级联成 R 群体。在每个时间步 t 中,一定数量的个体从 S 穿越到 I 以及从 I 穿越到 R ,而个体总数 N = S+I+R 保持不变。我们可以将这些动力学写成一组微分方程,或者,以一种更容易理解的形式,我们可以写下每个组在某个时间步长内的变化量:

基本 SIR 模型
动态由两个变量 β 和 γ 控制。 β 是传染性个体传染他人的速率, γ 是传染性个体恢复的速率。对于固定的 β 和 γ 来说,这些动态特性如下所示:

你可以看到受感染个体的数量增长很快,在第 40 天左右达到高峰,此时易感个体的数量显著下降,感染率下降。这仅仅是因为到那时,相当数量的人已经感染了这种疾病,不能再被感染了。到最后,受感染个体的数量下降到零,根除了疾病。请注意,到那时,大约 20%的人从未被感染。这个所谓的稳态解也可以解析计算,并且取决于参数 β 和 γ。
通过这个简单的 SIR 模型,我们已经可以观察到我们问题的一些基本动力学。然而,我们只是以一种综合的方式来看待我们的群体。我们假设个体是一个同质的、非结构化的集合,被组织成三个定义明确的、完全混合的群体。被建模的相互作用平均起来只有。每个被感染的个体每天感染固定数量的接触,并且每天所有被感染个体中的恒定部分被治愈。在这个模型中,没有办法实现个人之间复杂的社会互动。为了放宽这些假设,我们现在将建立一个基于代理的模型,分别模拟每个个体。
基于主体的模型
我们的第一个目标是再现解析 SIR 模型的结果。作为一种数据结构,我们希望使用熊猫数据帧。让我们从初始化 10,000 个代理开始,用数据帧中的行表示:

目前,数据帧只有一行称为 state ,表示代理的健康状态。我们用 0 编码易感,用 1 编码感染,用 2 编码康复。
现在我们需要一些功能来感染一个代理。我们希望这个函数获取与被感染的代理有过接触的代理的列表。此外,我们希望给出这些接触者实际被感染的概率。为了增加随机性,这里使用了一些蒙特卡罗方法。下面的函数完成所需的工作。
联系人列表允许多次保留同一个代理。我们为联系人列表中的每个唯一代理滚动 0 到 1 之间的随机数,并且如果该滚动低于概率阈值,则将状态从易感染(0)更新为已感染(1)。该函数的最后一行相应地更新 state 列。
同样,我们需要一个函数,以一定的概率恢复被感染的代理。这里,我们在每个时间步中使用一个平坦的恢复机会。
在每个时间步调用感染和恢复函数。为此,我们创建了一个步骤函数。这里,我们生成一个随机接触列表,其长度为受感染代理数量的常数倍。
为了对我们基于代理的模型的结果的变化有一个感觉,我们将运行模拟十次。对于每个实验,我们初始化一组 10,000 个代理,开始时有 5 个被感染的患者,零个患者。然后我们执行 150 个时间步骤。
基线结果
在每个时间步可视化三个组(易感、感染和恢复)的每个组的大小,我们可以看到我们的基于代理的模型的动力学与基本 SIR 模型一致。

基线模型的结果。这里我们使用以下参数:_randomContacts=9,_chanceOfInfection=0.025 和 _chanceOfRecovery=0.1。对于 SIR-模型,参数为 β=0.225 和 γ=0.1。
实线显示了我们 10 次模拟运行的中值,而阴影区域显示了 25%-75%分位数之间的区域。即使在模拟的中心部分存在一些差异,所有模型都达到了非常相似的终点,即等于解析解。
到目前为止,与基本的 SIR 模型相比,我们还没有获得太多,但是我们已经建立了一个基于代理的基线模型,并验证了它的行为类似。有了这个设置,我们现在可以开始添加额外的复杂性。
基于空间主体的模型
直觉上,假设被感染的病原体会与一组完全随机的病原体接触,在现实生活中可能不成立。你更希望有一些社会邻居,一群被感染的代理人定期接触的人。模拟这种效果的一个简单方法是将代理放在一个格子上,让它们与 9 个最近的邻居互动。

空间模型的结果。这里我们使用以下参数:_randomContacts=0,_chanceOfInfection=0.06 和 _chanceOfRecovery=0.1。对于 SIR 模型,参数为 β=0.54 和 γ=0.1。
注意延长的 x 轴。您可以看到,对于基于空间代理的模型来说,动力学现在慢了很多。我甚至不得不显著增加感染的几率,让它继续运行。我们向接触者介绍的结构导致这样一个事实,即受感染的病原体生活在已经有许多病原体被感染或已经康复的环境中,从而导致疾病传播的显著减少。我们可以在下面的动画中直观地看到代理的空间分布:

蓝色:易感,黄色:感染,绿色:康复
添加随机联系人
我们发现,当我们将空间结构引入到个体的社会互动中时,疾病的发展速度明显减慢。除了九个空间邻居之外,我们为每个代理引入一个额外的随机接触会发生什么?

空间模型的结果。这里我们使用以下参数:_randomContacts=1,_chanceOfInfection=0.06 和 _chanceOfRecovery=0.1。对于 SIR-模型,参数为 β=0.6 和 γ=0.1。

蓝色:易感,黄色:感染,绿色:康复
只需一次额外的随机接触,感染的动力学就再次变得更快,迅速破坏我们通过在网格上放置药剂而引入的结构。
日益增加的复杂性
我们有一个工作设置,人们现在可以通过增加复杂性来玩它。人们可以考虑对不同的、相互联系很弱的个体群进行建模,或者为个体引入一个年龄结构,以反映不同年龄组的不同类型的相互作用。此外,人们可以开始采取措施,在某个时间阶段减少感染的机会,或者减少接触的次数。
表演
一句话关于模型的性能。通常,我喜欢使用面向对象的方法来构建基于代理的模型。将代理建模为一个类使得模拟和编码非常直观。然而,在 python 中,模拟可能很快变得相对缓慢。通过将数据存储到 pandas dataframes 中,其中一行代表一个代理,我们失去了一些灵活性,但是我们可以依靠 numpy 函数来完成主要的工作负载,从而使模拟相当快。我的机器以每秒 50 步的速度运行了 100,000 个模拟代理,在几秒钟内产生模拟输出。
结论
我向您展示了如何从头开始建立一个基本的基于代理的模型。我们看了模拟疾病传播的例子。作为第一步,我们对照一个已知的数学模型来验证我们模型的最小版本。然后,我们开始改变参数,以研究系统的动态变化。通过在药物中引入晶格结构,我们观察到疾病的传播明显减慢,但只允许一次随机接触又会导致动力学增加。所提出的实现是一种灵活的设置,允许代理内更复杂的交互、异构性和结构的简单实现。此外,我们能够在一个复杂的大规模模拟中研究个体水平上的代理或代理的子群。
请随意使用这个设置作为开始,并与它一起玩。完整代码可在此处获得:
*[## 比特桶
贮藏室ˌ仓库
bitbucket.org](https://bitbucket.org/chgraf/blog/src/master/Agent Based Pandemic/)*
为药物发现知识图建模生物医学数据

听取阿斯利康的数据科学和人工智能的汇报
本月早些时候,阿斯利康的副首席科学家娜塔莉·库尔巴托娃加入了我们的第一系列轨道。
Natalie 在阿斯利康的数据科学和人工智能部门工作,在那里她专注于数据建模、将数据集成到知识图、预测算法以及其中的主题。
目标:预测新的疾病目标
在阿斯利康,Natalie 的团队专注于构建一个知识图来预测新的疾病目标(基因或蛋白质目标),他们称之为发现图。在构建过程中,Natalie 向我们介绍了两种要考虑的数据源:
结构化数据指的是生物信息学中公开可用的数据集,这些数据集已经在行业中进行了整理和广泛使用。虽然生物医学结构化数据是机器可读的,但通常不容易处理。特别是,很难集成这些数据集,因为它们可以以不同的方式描述类似的概念,例如:不同的 id 彼此不一致。一些最常用的公开可用数据集包括: Ensembl 、 Uniprot 、 ChEMBL 、 PubChem 、 OmniPath 、 Reactome 、GO、CTD、human protein tlas。
非结构化数据指来自文本的数据。为了处理这个,我们需要使用 NLP(自然语言处理)管道,然后处理它们的输出。在这里,困难在于这些数据往往是杂乱和嘈杂的。对于他们的 NLP 引擎,娜塔莉的团队使用了开源库 SciBERT 以及阿斯利康的专有工具。
Natalie 然后向我们介绍了她的团队为他们的发现图构建的模式(幻灯片如下)。

TypeDB Studio 中的可视化模式——关注已定义的实体。经许可重新发布的幻灯片。
娜塔莉的团队主要对研究compounds、diseases和gene/proteins感兴趣——他们亲切地称之为金三角。这些实体之间的连接需要尽可能的稳固和可靠,这意味着将所有可能的、相关的数据源吸收到它们的发现图中。
这个发现图每天都在变大。到今天为止,它已经广泛地包含了这三种实体类型:
gene-protein:19371
disease:11565
compound:14294
实体类型之间还有 656206 个关系。
他们是如何模拟这个“金三角”的?
娜塔莉接着解释了她是如何对发现图的每一部分建模的,并一路举例。
模拟基因和蛋白质
首先,她的团队着眼于如何模拟基因和蛋白质。Natalie 的团队没有将这些分成两个实体,而是决定将它们建模为一个实体,他们称之为gene-protein。这有助于减少噪声和偏差。

在 TypeDB Studio 中可视化的[基因-蛋白质]实体。经许可重新发布的幻灯片。
相关属性gene-name和chromosome,指定相应的基因名称,以及该基因所在位置的染色体名称。gene-id属性被建模为一个抽象,它包含每个gene-protein的唯一 id。
💡 There may have been scenarios where a parent attribute is only defined for other attributes to inherit, and under no circumstance do we expect to have any instances of this parent. To model this logic in the schema, use the abstract keyword.
interaction关系建立了gene-protein之间的交互,其中一个扮演 gene-source的角色,另一个gene-target。这种关系在预测新的疾病靶点中特别重要,因为它连接了基因和蛋白质之间的调节相互作用。
建模化合物
娜塔莉的团队将小分子和抗体放在一起,而不是将它们分开,作为一个实体类型,他们称之为compound。对于这些数据,他们使用了数据源:PubChem 和 ChEMBL。
这些数据库大约 95%相同,但有些化学物质只存在于两个来源中的一个。为了处理这些独特的化学物质,他们决定分配一个chembl-id作为主要 ID,如果它有一个,但是如果它没有那个 ID,那么他们将使用pubchem-id。

在 TypeDB Studio 中可视化的[复合]实体。经许可重新发布的幻灯片。
正如我们在 Natalie 的幻灯片上看到的,compound用更多的属性建模。属性compound-id被用作一个抽象属性,其子属性preferred-compound-id、pubchem-id、chembl-id和drugbank。
为了获得preferred-compound-id属性的值,他们使用两个规则来根据下面的逻辑分配chembl-id或pubchem-id:

用于将适当的(ID)分配给特定[化合物]的规则。经许可重新发布的幻灯片。
第一个规则将chembl-id的值(如果存在的话)附加到preferred-compound-id。第二个规则首先检查compound是否没有chembl-id,如果是,那么它将pubchem-id属性的值附加到preferred-compound-id。这使得在特别查询化合物时,很容易查询该属性。
建模疾病
娜塔莉解释说,要建模的最复杂的概念是疾病。这是因为疾病有多个本体,每个本体都有不同的 id。例如,一种疾病可能有来自不同本体和疾病层次的多个 id。在下面的幻灯片中,娜塔莉向我们展示了三个数据源的层次结构:EFO、蒙多和多德。

疾病数据源的本体论层次。经许可重新发布的幻灯片。
这种异质性的原因是,最初,这些本体是在不同的子域中设计的:例如,医生使用 Orphanet,而医学研究小组可能使用另一个:EFO 或 MONDO。这导致数据互不关联,互不相干。Natalie 和她的团队希望能够建立一个疾病实体模型,在这些本体之间交叉引用。

数据是从哪里来的?经许可重新发布的幻灯片。
因为这个挑战,他们选择了建模两种实体类型:disease和ontology。他们使用一个disease-hierarchy关系——一个三元关系——来连接这两者。

在 TypeDB Studio 中可视化的[疾病]实体。经许可重新发布的幻灯片。
有了这个disease-hierarchy关系,Natalie 和她的团队能够编写有用的查询,例如:
给我所有“慢性肾脏病”患儿的疾病节点使用 EFO 本体。

示例查询和层次结构中的预期路径。经许可重新发布的幻灯片。
这个查询展示了模型的强大功能——使用 TypeDB 的模式的强大功能——因为即使他们只是询问高级疾病,查询也将返回该疾病的所有可能的子类型。
为了对此建模,Natalie 再次利用 TypeDB 的规则引擎:

TypeDB 中的传递性规则示例。经许可重新发布的幻灯片。
上面幻灯片中显示的规则single-ontology-transitivity在disease-hierarchy关系中创建了一个传递性。这个规则的结果是,如果查询一个扮演 superior-disease的疾病,将会推断出扮演 subordinate-disease的所有疾病。此外,这意味着即使您不指定您要查询的本体,您仍然会返回所有摄取的数据源中该特定父疾病的所有从属疾病、。
当特定的本体中没有相应的引用 ID 时,这种类型的规则特别有用。

将疾病实体映射到适当的[疾病 id]。经许可重新发布的幻灯片。
正如 Natalie 向我们展示的,当某个disease-id不存在于某个本体中时,我们可以沿着层级向上,直到我们找到一个确实存在的 ID,然后分配那个 ID。为此,Natalie 使用了另一个传递规则:

当一个(id)不存在时,这个传递性规则从父节点向下拉。经许可重新发布的幻灯片。
第一个规则determine-best-mesh-id-1,如果一个mesh-id已经存在,就给一个disease实体分配一个best-mesh-id属性。然后,第二个规则声明,如果我们不知道mesh-id,我们希望从父疾病中删除mesh-id。娜塔莉强调了这是多么有效,以及在实践中结果是多么积极。
数据整合
一旦领域被建模,我们就可以开始接收数据了。为此,有两种方法可以采用:
- 数据工厂:在加载到知识图之前集成数据
- 数据模式:通过灵活的数据加载和后续推理,数据集成发生在知识图中
Natalie 的研究小组使用数据模式方法,使用知识图本身作为整合数据的工具。这对一些开发者来说可能是反直觉的。
如果您设想添加额外的未知数据源,那么您必须足够灵活地操作数据库中的数据。这使得复制另一个人的工作[论文]来验证假设。
在研究工作的情况下,灵活性是必不可少的,正如 Natalie 在之前的冲突 mesh-id 中向我们展示的那样。
如上所述,解决方案是使用 TypeDB 的推理引擎。
摘要
最后,Natalie 花了几分钟总结了这种方法在数据模式和逻辑推理方面的优势。
其他图形数据库在数据加载方面很灵活,但缺乏验证。虽然灵活性很重要,但是验证对于保持数据的一致性也是必要的。
几个月前,Natalie 的一个初级同事不小心将电影数据加载到了他们的生物医学知识图表中。第二天早上,团队的其他成员惊讶地看到他们的生物医学数据中有电影和演员!
使用 TypeDB,在插入时通过数据模式对数据进行逻辑验证,以确保正确的数据进入知识图。
Natalie 认为这个数据库在正式的模式设计、逻辑推理和预测算法能力之间提供了一个很好的平衡。根据她的经验,在将数据加载到知识图之前,需要先对数据进行建模。和灵活的模式有助于找到这种理想的平衡。
💡 Note that all prediction algorithms depend on what you load in the database — aka: be careful what you put in the knowledge graph. The noise level should be as low as possible.
最后,她谈到了我们是在加载数据之前还是之后集成数据的关键选择,在他们的情况下(如上所述),一旦插入数据库,就在加载之后完成。

特别感谢娜塔莉和她在阿斯利康的团队的热情、奉献和透彻的解释。
你可以在 Vaticle 的 YouTube 频道这里找到完整的演示。
建模分类树
如何编写最流行的机器学习算法之一(Python)

凯利·西克玛在 Unsplash 上的照片
决策树是机器学习中最流行的算法之一:它们易于可视化,高度可解释性,超级灵活,可以应用于分类和回归问题。DTs 通过学习从数据特征中推断出的简单决策规则来预测目标变量的值。
在我的帖子 " 决策树完全指南 s" 中,我详细描述了 DT:它们在现实生活中的应用,不同的 DT 类型和算法,以及它们的优缺点。现在是务实的时候了。你如何建立一个 DT?你如何将它应用到真实数据中?DTs 只不过是算法(或步骤序列),这使得它们非常适合编程语言。让我们看看怎么做。
问题是
世界银行将世界经济分成四个收入组:
- 高的
- 中上
- 中下
- 低的
这一分配基于使用图册方法计算的人均国民总收入(GNI)(以现值美元计算),类别定义截至 2018 年 7 月 1 日。利用数据预处理技术,我创建了一个数据集,其中还包括其他国家的变量,如人口、面积、购买力、GDP 等。你可以在这个链接下下载数据集。
该分类树的目标是根据数据集中包含的变量预测一个国家的收入组。
台阶
您可以通过处理更简单的子步骤来降低构建 DTs 的复杂性:DT 中的每个单独的子例程都将连接到其他子例程以增加复杂性,这种构造将让您获得更健壮的模型,更易于维护和改进。现在,让我们用 Python 建立一个分类树(特殊类型的 DT)。
加载数据并描述数据集
加载数据文件是最简单的部分。问题(也是最耗时的部分)通常是指数据准备过程:设置正确的数据格式、处理缺失值和异常值、消除重复值等。
在加载数据之前,我们将导入必要的库:
import xlrd
import pandas as pd
import numpy as np
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
现在我们加载数据集:
df_c = pd.read_excel(“macrodata_class.xlsx”)
看一下数据:
df_c.head()

我们可以看到有一些缺失的值。让我们检查变量的类型:
df_c.info()

在总共 215 个条目中,几乎所有的列都有缺失值。同样,我们可以看到,除了从变量“国家”和“类”(我们的目标变量)被定义为“对象”,其余的都是数字变量(float64)。
我们可以计算每个变量中有多少个缺失值:
print(df_c.isnull().sum())

让我们在构建 DT 之前去掉那些缺失的值:
df_c.dropna(inplace=True)
如果我们再次描述我们的数据集,我们会看到这些寄存器已被删除:
df_c.info()

您可以在链接上找到探索性数据分析的其他技术。
选择特征和目标变量
您需要将给定的列分成两种类型的变量:因变量(或目标变量)和自变量(或特征变量)。首先,我们定义特性:
X_c = df_c.iloc[:,1:8].copy()
然后是目标变量:
y_c = df_c.iloc[:,8].copy()
分割数据集
要了解模型性能,将数据集分为定型集和测试集是一个好策略。通过将数据集分成两个独立的集合,我们可以使用一个集合进行训练,使用另一个集合进行测试。
- 训练集:这个数据用来建立你的模型。例如使用 CART 算法来创建决策树。
- 测试集:该数据用于查看模型在看不见的数据上的表现,就像在现实世界中一样。在您想要测试您的模型以评估性能之前,这些数据应该是完全不可见的。
X_c_train, X_c_test, y_c_train, y_c_test = train_test_split(X_c, y_c, test_size=0.3, random_state=432, stratify=y_c)
这里有几件事:我们将数据集分为 70%的训练和 30%的测试,并执行分层抽样,以便产生的样本中值的比例与目标变量中提供的值的比例相同。
建立 DT 模型并微调
构建 DT 就像这样简单:
clf = DecisionTreeClassifier(criterion=’gini’,min_samples_leaf=10)
在这种情况下,我们只定义了分裂标准(选择了基尼指数而不是熵),并且只定义了一个超参数(叶子的最小样本量)。定义模型架构的参数被称为超参数,因此,搜索理想模型架构(最大化模型性能的架构)的过程被称为超参数调整。 A 超参数是在学习过程开始前就设定好值的参数,它们不能直接从数据中训练出来。
您可以通过调用模型来查看可以优化的其余超参数:
clf

模型可以有许多超参数,并且有不同的策略来寻找参数的最佳组合。你可以在这个链接上看看其中的一些。
列车 DT 模型
将模型拟合到训练数据代表了建模过程的训练部分。在模型定型后,可以使用预测方法调用来进行预测:
model_c = clf.fit(X_c_train, y_c_train)
测试 DT 模型
测试数据集是独立于训练数据集的数据集。该测试数据集是您的模型的未知数据集,有助于您对其进行概化:
y_c_pred = model_c.predict(X_c_test)
设想
DTs 最大的优势之一是它们的可解释性。可视化 DTs 不仅是理解模型的有效方法,也是传达模型工作原理的有效方法:
from sklearn import tree
import graphviz
dot_data = tree.export_graphviz(model_c, feature_names=list(X_c), class_names=sorted(y_c.unique()), filled=True)
graphviz.Source(dot_data)

“purchasing.power.cap”这个变量似乎对于定义阶层或目标变量非常重要:高收入国家位于右侧,中上收入位于中间,低/中下收入位于左侧。
评估绩效
评估你的机器学习算法是任何项目必不可少的一部分:你如何衡量它的成功,你什么时候知道它不应该再改进了?不同的机器学习算法有不同的评估指标。让我们提一下分类树的一些主要方法:
准确度得分
分类问题的准确性是指模型在各种预测中做出的正确预测的数量。
print ('Accuracy is:',(accuracy_score(y_c_test,y_c_pred)))

我们的准确率是 71.9%。如果我们认为可以通过生成新的特征或调整超参数来改进我们的模型,这还不错。但这是一个全球指标,所以让我们用其他指标来了解更多细节。
混乱矩阵
混淆矩阵是用于发现模型的正确性和准确性的最直观的度量之一。它用于分类问题,其中输出可以是两种或多种类型的类。为了理解它,首先,我们需要定义一些术语:
- 真阳性(TP) :显示一个模型正确预测阳性病例为阳性。被诊断为存在的疾病确实存在。
- 假阳性(FP) :显示一个模型错误地预测阴性病例为阳性。E 例如,被诊断为存在的疾病不存在(I 型错误)。
- 假阴性(FN): 表示一个模型错误地将阳性病例预测为阴性。例如,存在被诊断为不存在的疾病(类型 II 错误)。
- 真阴性(TN): 表明模型正确预测阴性的情况为阴性。被诊断为不存在的疾病是真正不存在的。
cmatrix = confusion_matrix(y_c_test,y_c_pred, labels=y_c_test.unique())
pd.DataFrame(cmatrix, index=y_c_test.unique(), columns=y_c_test.unique())

在像我们这样的多类混淆矩阵的情况下,矩阵将扩展到类的数量(在我们的例子中是 4 x 4)。我们的 DT 正确预测了 19 个“高收入”实例中的 17 个,8 个“低收入”实例中的 6 个,13 个“中低收入”实例中的 8 个,以及 17 个“中高收入”实例中的 10 个。
关于多类混淆矩阵的完整解释,请查看本文。
分类报告
分类报告显示了每个类别的主要分类指标。这给出了对分类器行为超过全局准确性的更深的直觉,这可以掩盖多类问题中的一类中的功能弱点。分类报告集成了不同的指标,例如:
- 精度(TP/(TP+FP): 是正确预测的正观测值与总预测正观测值之比。对于每个类别,它被定义为真阳性与真阳性和假阳性之和的比率。换句话说,当预测正面实例时,分类器有多“精确”?
- Recall (TP/(TP+FN): 是分类器找到所有正例的能力。对于每个类别,它被定义为真阳性与真阳性和假阴性之和的比率。换句话说,“对于所有实际上为阳性的实例,正确分类的百分比是多少?”
- F1-Score (2(精度召回)/(精度+召回)):是精度和召回的加权调和平均值,使得最好的分数是 1.0,最差的是 0.0。一般来说,F1 分数低于准确度,因为它们将准确度和回忆嵌入到计算中。根据经验,F1 的加权平均值应该用于比较分类器模型,而不是全局精度。
- Support: 是指定数据集中该类实际出现的次数。训练数据中的不平衡支持可以指示分类器的报告分数中的结构弱点,并且可以指示分层采样或再平衡的需要。支持在不同的模型之间不会改变,而是对评估过程进行诊断。
report = classification_report(y_c_test, y_c_pred)
print(report)

特征重要性
另一个关键指标包括为预测模型的输入特征分配分数,表明每个特征在进行预测时的相对重要性。特征重要性提供对数据、模型的洞察,并代表降维和特征选择的基础,这可以提高预测模型的性能。越多的属性用于 DT 的关键决策,其相对重要性就越高。
for importance, name in sorted(zip(clf.feature_importances_, X_c_train.columns),reverse=True):
print (name, importance)

变量“purchasing.power.cap”相对于所有其他变量(是模型的主要特征)来说非常重要,如果我们考虑目标变量,这完全有意义。
包裹
虽然我们在建模期间涵盖了几个步骤,但这些概念中的每一个都是独立的学科:探索性数据分析、特征工程或超参数调整都是任何机器学习模型的广泛而复杂的方面。你应该考虑更深入地研究那些学科。
此外,DTs 是称为集成方法的更强大算法的基础。集成方法将几个 DTs 结合起来,产生比单个 DTs 更好的预测性能。系综模型背后的主要原理是一群弱学习者走到一起形成强学习者,显著提高单个 DT 的表现。它们用于减少模型的方差和偏差,并改进预测。既然你已经看到了决策树是如何工作的,我建议你继续使用像打包或推进这样的集合方法。
将新冠肺炎病毒在加拿大的传播建模为指数增长
指数增长及其如何应用于加拿大新冠肺炎传输的简要回顾。

指数函数的一般方程
编者按: 走向数据科学 是一份以数据科学和机器学习研究为主的中型刊物。我们不是健康专家或流行病学家,本文的观点不应被解释为专业建议。想了解更多关于疫情冠状病毒的信息,可以点击 这里 。
介绍
鉴于几天前加拿大的新冠肺炎病例数超过了 1000 例,我认为现在是回顾指数增长的适当时机。在这篇文章中,我将快速演示为什么加拿大的病例总数继续呈指数增长是令人担忧的。声明一下,我不是流行病学家——只是一个对数学建模感兴趣的人。
指数增长引物
如果你有一段时间没上过数学课,我们来简单回顾一下指数增长。如果你 20 多岁,那么我敢打赌,由于指数增长的性质,你已经有几个人建议你开始投资了。对于那些能够留出额外资金的人来说,这是一个好主意。假设你以每年 8%的复利投资 100 美元,在第一年年末你将有 108 美元。如果你把你的 108 美元再投资,第二年后你会有 116.64 美元。如此重复 30 年后,你会有超过 1000 美元。虽然你可能需要几年时间才能开始注意到任何重大收益,但随着时间的推移,你账户的余额会越来越快。这是为什么呢?指数增长的显著特征是它的变化率在增加。同样,它的变化率也在增加。还有,它的变化率,它的变化率是递增的,等等。尽管指数曲线的第一部分可能看起来很平坦,但很难想象这种快速变化仍在发生,只是规模较小。
新冠肺炎以指数增长的方式传播
指数增长的原理可以应用于新冠肺炎的传输。如果每个感染者多感染 2 个人,那么总感染人数就乘以了 3!就像你的投资一样,它开始时很慢,但随着时间的推移会以越来越大的速度增长(这对早期投资者来说是好事,但对传染病来说是坏事)。
图 1 显示了 1 月 25 日至 3 月 21 日加拿大确诊的新冠肺炎病例总数。这个数据是在https://covid 19 tracker . ca找到的,它一直保存着每天病例总数的历史记录[1]。从数据点可以看出,它们遵循指数增长模式。假设指数增长,非线性最小二乘分析被应用于得出指数函数的方程以模拟这种增长。该等式打印在下面的图 1 中。很明显,指数函数很好地拟合了(目前收集的)数据。

图 1: 截至 3 月 21 日,加拿大新冠肺炎病例总数。指数函数被拟合到数据中。t 是自 2020 年 1 月 25 日起的天数。
含义
如果新冠肺炎的传播继续遵循下图所示的指数增长模型,病例数将继续快速上升。图 2 显示了这个模型到 4 月 21 日的推断。如果这个指数函数继续准确地模拟新冠肺炎的总病例数,似乎我们可以在一个月内接近 200 万例。这仅占我国人口的 5%多一点。我们的医疗系统将不堪重负。你可以看到底部的图表在 3 月 21 日之前看起来是平的,考虑到正在发生的增长类型,这是误导性的。如果病毒继续按照这个模型的速度传播,我们没有希望很快“拉平曲线”。只有降低传输速率,才能做到这一点。

图 2: 假设我们拟合的指数模型仍然准确,推测加拿大截至 4 月 21 日(含 4 月 21 日)的新冠肺炎病例总数。
结论
让我们通过尽力练习社交距离来证明这个模型是错误的。通过限制与他人的接触,你正在为“打破”这种模式做出贡献!如果我们成功地减少了传播,也许我会写一篇关于 sigmoid 增长曲线的后续文章(“曲线变平”的结果)。在那之前,享受外面的好天气和与家人在一起的宝贵时间。
参考
小小的。加拿大新冠肺炎跟踪者 (2020),COVID19Tracker.ca
模拟信用卡欺诈
人为平衡的数据总是最佳选择吗?

插图。来源: Pixabay
信用卡诈骗是世界上一个“仍在增长”的问题。据估计,2018 年欺诈损失超过 270 亿美元,预计未来几年仍将大幅增长,如本文文章所示。
随着越来越多的人在日常生活中使用信用卡,也增加了犯罪分子从中赚钱的机会。新技术的发展使得罪犯和信用卡公司都在不断地改进他们的系统和技术。
鉴于这一金额,机器学习对信用卡公司来说肯定不是一个新词,早在它成为一种趋势之前,它们就已经在这方面进行了投资,以创建和优化风险和欺诈管理模型。这个来自 Visa 的快速视频以友好的方式展示了这个价值数百万的复杂系统的冰山一角。
在这本笔记本中,我将使用匿名信用卡交易数据开发一个机器学习模型,以展示一个稍微简单的模型在欺诈检测方面可以实现什么。我也会从实用的角度来讨论一些选型的相关点。
信用卡登记簿被视为个人信息,不能公开共享。所使用的数据集是来自实际卡使用的文件,但是使用称为主成分分析的方法掩盖了变量。这种方法不仅改变变量的名称,还改变变量的数值,用于降维。然后,我们可以在不识别任何个人信息的情况下处理真实数据。
数据集包含欧洲持卡人在 2013 年 9 月进行的交易,并在 Kaggle 中共享。
探索性分析
在我们实际创建机器学习模型之前,对数据有一个大致的了解是很重要的。分布、趋势、异常值,这些都是重要的信息,有助于在模型开发过程中创建和验证可能相关的理论。由于数据集涉及个人和财务信息,变量被 PCA 方法“掩盖”,不具有实际意义。因此,可以从实际分析中提取的信息急剧减少。但是让我们利用现有的资源。首先,我将创建一个计数图,显示数据集中有多少正常和欺诈交易。

为每堂课计算绘图。由作者创作。
甚至很难看到欺诈交易的杠杠。在 284807 笔交易中,只有 492 笔被发现是欺诈。这意味着,正如所料,这是一个非常不平衡的数据集,只有不到 1%的数据被归类为 1。
我还将创建一个按正常(0)和欺诈(1)分类的交易金额(欧元)的箱线图。

每类金额的箱线图。由作者创作。
箱形图显示了分布的偏斜程度,但仍有许多异常值超出了此图中 y 轴的限制。有趣的是,欺诈实际上比真实交易的中位数要低。也许犯罪分子想避开大额交易中常见的障碍和额外要求。但这能保护他们的行动不被发现吗?
模型开发
既然探索性分析已经完成,就可以创建一个预测模型来避免将来的欺诈。我不会关注研究中使用的代码、库或方法,而是关注我为获得结果所遵循的步骤。你可以在我的 Github 里看到完整的分析。
训练测试集分割
第一步是将可用数据分成两组。第一组称为训练集,将用于训练模型,选择最佳模型并调整其参数。第二个称为测试集,用作模型的测试。它就像是新数据一样提供,以便模型预测可以与每个操作的实际分类进行比较。
平衡
当数据集在类的数据之间有很大差异时,它被认为是不平衡的。为了确保模型能够正确地检测两个类的模式,其中一个选项是人工平衡数据集,使所有类的信息数量相似。我使用了随机下划线方法,最终每个类的点数分布均匀(数据集也小得多)。该过程导致整个数据集的 99%以上被排除在模型开发阶段之外。

为每堂课计算绘图。由作者创作。
正常化
一些机器学习模型可能更依赖于变量的数量级。例如,这意味着高阶变量可能被误解为与最终输出更相关。为了避免这种情况,归一化是可以使用的方法之一,因为它将转换所有变量,使它们具有标准的标度。我决定将数据标准化,以确保这些数量级不会影响我的结果。
型号选择
有许多机器学习模型可以用来解决一个问题。对于何时使用每一种都有通用的指导原则,但是强烈建议进行多项测试,以了解哪一种对每种情况都具有最佳的可预测性。这是一个分类问题,目标是根据交易的属性来预测交易属于哪个类别。最常用的分类模型是逻辑回归和分类树,但还有许多其他可用的模型。在这个例子中,我测试并比较了其中的一些,以确定哪一个是使用交叉验证方法的最佳选择。我根据每个模型的召回率做出决定,召回率是模型正确预测的真实类别总数的百分比。

每个型号的召回分数。由作者创作。
不同型号的召回结果非常相似。基于这些结果,我决定继续使用 XGBClassifier 模型。
超参数调谐
机器学习模型具有定义它们将如何学习和预测结果的重要参数。不同的参数导致不同的模型,从而导致不同的结果。由于模型的复杂性,没有分析方法来定义哪种参数组合将提供最佳结果。超参数调整是一种迭代方式,计算机测试不同的参数组合并计算分数以定义最佳组合。
在我的分析中,我测试了模型参数的许多组合,并找到了一个可以进一步改善结果的组合,但在召回率方面没有真正有意义的改善。
模型验证
模型验证的步骤是最等待的一步,因为它显示了新数据的分类是好是坏。让我们看看我的模型在分类报告中的表现。

模型分类报告。由作者创作。
这些都不是很好的结果。召回结果表明,该模型确实能很好地检测欺诈行为。但是,精度显示它也会有许多误报(正常交易被识别为欺诈)。这在混乱矩阵中会更清楚。

预测混乱矩阵。由作者创作。
可以看到,超过 3000 个正常操作被模型错误地分类为欺诈。这将意味着超过 3000 笔交易被阻止,消费者感到恼火,从而发现 82 起欺诈。这是因为该模型正在识别这些操作中的欺诈模式。如果这些模式不明显,这可以通过调整模型阈值来解决。
为了解释什么是阈值,让我们回顾一下分类模型是如何工作的。基于观察到的数据,它估计了它属于研究中的每个类的概率。基于概率,将点分类到一个类中,因此存在一个参考概率,在该参考概率之上点被分类。这个参考概率被称为阈值,并且可以被调整以适合特定分析的需要并改进分类结果。每个阈值的精确度和召回率的结果可以在精确度-召回率曲线中检查,所以我决定画一条曲线,看看是否有更好的阈值。

精确召回曲线。由作者创作。
曲线显示,可供选择的好阈值选项并不多。这意味着模型认为正常交易的模式与欺诈非常相似。似乎平衡数据集大大减少了正常交易的信息,以至于模型无法正确区分它们。所以我决定使用实际的原始数据集,看看不平衡类的结果是否会更糟。

不平衡数据集的模型分类报告。由作者创作。
分类报告在精确度和召回率的结果上显示出更多的均等。让我们看看混淆矩阵。

不平衡数据集的预测混淆矩阵。由作者创作。
现在,被认定为欺诈的正常业务数量大幅下降。因此,该模型无法预测像第一个案例中那样多的欺诈。预测和召回的结果可以在精确召回曲线中再次观察到。

不平衡数据集的精确召回曲线。由作者创作。
在曲线中,可以看到有一个阈值提供了 0.8 的精确度和 0.82 的召回率。在我看来,这是一个有趣的选择,但根据最终目标,有许多组合可以使用。似乎使用整个数据集创建的模型能够更好地识别正常交易与欺诈的模式。因此,当它看到新数据时,预测的欺诈总数从 3000 多起下降到 75 起,这是一种更加自信的方法。
结论
在这篇文章中,我开发了一个机器学习模型来预测信用卡交易中的欺诈行为。该分析涉及不同模型的测试,以基于召回、超参数调整以及使用精确召回曲线的分类阈值调整来选择最佳选项。
通过在分析中使用平衡的和不平衡的数据集,我证明了在某些情况下,使用不平衡的数据集可以获得良好的预测结果,并且在寻找问题的最佳解决方案时,“一刀切”的思维模式是没有帮助的。
使用不同的数据集和阈值,我能够预测数据集中 82%的欺诈,并保持高精度,因此正常交易不会频繁受阻。
当然,没有完美的模型,总会有一个时刻,项目团队必须决定哪种折衷最适合其目标。在本例中,决策点是用平衡或不平衡的数据开发模型。这导致了不同的精确度和召回率。另一个决策是哪个阈值在欺诈检测和误报方面带来最佳平衡。如果公司有非常严格的安全政策,它可以选择尽可能高的召回率,不管精确度是多少。另一方面,一些公司更愿意允许一些欺诈行为,以确保他们的客户在正常交易中不会被卡。
好消息是,无论目标和限制是什么,数据科学中工具和技术的多样性和灵活性允许它应用于许多不同的情况。对此的主要要求是明确定义目标和度量标准。基于此,一个有经验的团队可以在研究过程中运用批判性思维找到最佳解决方案。
谢谢大家!
感谢你阅读这份材料,我希望它能在某些方面对你有用。如果你想了解分析的更多细节,你可以查看我的 GitHub 中的完整研究。在接下来的几周里,我会发布一些非常好的文章,如果你喜欢这篇文章,请查看我的媒体或 LinkedIn 个人资料!感谢您的阅读!
系统模型化
通常简化为仅仅训练一个模型,这实际上是将预测与现实世界联系起来。

图片由皮克斯拜的 Gerd Altmann 提供
最终确定模型
我们已经讨论了使用交叉验证来帮助我们选择适合我们问题的最佳算法。值得注意的是,通过五重交叉验证,我们对一个模型进行了五次训练和测试。每次模型用 80%的数据进行训练,用剩下的 20%进行测试。但是我们最终得到了这些训练有素的模型中的哪一个呢?答案其实是最终的模型是在全训练数据集上重新训练的。毕竟,我们已经通过保留数据来评估模型性能;我们不必再保留一个分区了。为什么要浪费呢?我们可以用这额外的 20%来制作一个模型,这个模型应该更加稳健,因为它已经在更多的数据上进行了训练。
同样,一旦我们完成了所有的交叉验证和模型选择,并使用我们的最终坚持测试集(整个过程之前没有见过)检查了模型性能,我们实际上甚至可以将该数据集纳入我们的训练,以希望产生稍微好一点的结果。我们只需要记住,我们不能对我们的算法设计做任何进一步的调整。这是需要注意的重要一点。我们不能决定让我们的模型变得更复杂一点,只是为了在这个特定的样本上做得更好一点。我们不能决定多一点或少一点正规化是正确的做法。我们可能会感叹,我们已经失去了对算法性能的独立评估,但如果我们乐于接受我们的测试集是好数据,并因此给了我们有效的性能检查,我们应该乐于将这些数据纳入我们的模型对世界的理解。
推理
在前面的步骤中,我们执行预处理和训练,实际上已经完成了许多文本视为建模的工作。对我们来说,建模与其说是一个模型的开发,不如说是一个经过训练的模型的应用,以执行对系统行为建模的推理。毕竟,我们建立预测性机器学习模型的根本原因通常是为了模拟在一组特定环境下会发生什么,这些环境要么尚未发生,要么已经发生,但我们不知道实际结果。
关于这篇文章
这是一个链接系列的第六篇文章,旨在提供数据科学过程入门的简单介绍。你可以在这里找到简介,在这里找到上一篇文章,在这里找到系列文章。
波兰语仇恨言论建模:特定领域嵌入的重要性
机器学习和社交媒体
我们通过使用具有特定领域嵌入的 FastText 与 RoBERTa 和 ULMFiT 竞争

在 Sotrender,我们通过将内部构建的机器学习模型应用于社交媒体,利用人工智能增强数字营销。我们最近的挑战是检测波兰信息中的仇恨言论。下面你可以看到我们是如何解决这个问题的,以及我们所获得的结果。
仇恨言论的问题
在互联网时代,讨论正在各种交流平台上在线进行,而不是面对面。缺少我们信息的直接接收者会给人一种欺骗性的匿名感。这让我们可以更自由地表达自己,但另一方面,忽略了我们的文本会被另一个人阅读的事实。这也被称为在线去抑制效应。此外,人们不会被迫为他们大部分时间在网上发布的言论负责。这些观点导致了网上讨论中明显的仇恨。
由于最近在自然语言处理(NLP)方面的进步,越来越多的有害信息可以被自动检测出来(T3)。然而,主流的自然语言处理研究只集中在一个小的子集,要么是常用语言,要么是人口众多的语言,如英语或汉语(参见[1]对自然语言处理中语言多样性的综合研究)。这在 NLP 模型的质量(以及检测有害内容的能力)之间产生了明显的差异,这取决于它们所针对的语言。虽然最近的一些研究旨在通过开发多语言模型来弥合这一差距,大多数时候,那些针对单一语言的研究呈现出更好的语言理解能力。幸运的是,一些新的可能性正在出现,以支持对代表性不足的语言的研究。
在这篇文章中,我们将关注波兰语言中的仇恨言论检测问题。我们将从描述最近创建的数据集开始,该数据集可用作仇恨言论检测方法的基准。然后,我们将讨论我们基于快速简单的深度学习模型的方法。我们将我们的方法与来自 PolEval 竞赛和 KLEJ 基准的提交文件进行了比较。与 PolEval 的结果相比,所提出的模型实现了显著的改进,并且与 KLEJ 的基于变压器的模型相当,同时在概念上更简单并且需要更少的计算资源。我们方法的优势展示了特定领域文本嵌入的重要性,以及非常适合手头任务的神经模块的使用。
pole val——seme val 的波兰对应物
PolEval 是专门为波兰语设计的 NLP 工具的评估活动。从 2017 年开始,他们每年举办各种语言理解任务的比赛,如机器翻译或命名实体识别。2019 年,其中一项任务是解决从公共论坛上的讨论中收集的信息中检测有害内容的问题。也就是说,分为两部分的"自动网络欺凌检测"任务旨在区分无害和有害信息,或将有害信息进一步分类为网络欺凌或仇恨言论。虽然乍一看,网络欺凌和仇恨言论之间的区别可能并不明显,但这项任务根据特定有害行为的目标将它们区分开来:在网络欺凌中,它是针对个人的,而在仇恨言论中是针对一个群体或实体的。更详细的定义在任务附带的一篇论文中讨论[2]。
数据描述
用于该任务的数据集由从公开的 Twitter 讨论中收集的 tweets 组成。这些推文经过了最低限度的预处理,因此不包含私人身份信息。除此之外,它们的原始格式没有改变。这意味着,它们可以包含表情符号、表情符号、标签、HTTP URLs 和拼写错误等噪音。虽然 URL 通常不包含关于信息危害性的有用信息,但是表情符号、表情符号和标签可以用来加强文本内容或表示讽刺。数据集中的一个示例如下所示:

检测有害信息的另一个挑战来自讨论的主题。数据集中的大量消息与政治相关。一般来说,人们可以怀疑在线平台上发布的关于政治的信息可能包含更多的负面情绪。基于这种数据训练的模型可以学习不期望的偏见,并且认为关于该主题的所有消息更可能是有害的。一般来说,来自数据集的推文指的是政治、体育和新闻(更详细的描述可以在[2]中找到)。
最后,从机器学习(ML)模型的角度来看,数据集可能被认为是具有挑战性的。它包含大约 11k 条 tweet(10k 用于训练集,1k 用于测试),与其他 NLP 任务相比,这是一个相当小的数目。此外,类别分布严重不平衡,因为大多数文本是无害的(见图 1)。

图一。数据集的训练和测试分裂中的类分布。数据集中的大多数消息是无害的。训练拆分仅包含 8.5%的有害消息,而测试包含 13.4%。
我们的方法
大多数最先进的 NLP 模型都是在结构良好的文档上预先训练的,例如来自维基百科的文章,这允许我们建立一个丰富而准确的语言模型。然而,这些预先训练过的模型可能不一定知道如何处理特殊的符号,如新的表情符号、标签或社交媒体传播专用的全新短语,他们在没有接受过这些训练。不同语言风格、缩写、俚语、混合不同语言的单词或简单的打字错误或语法错误的存在将进一步测试模型概括分布外数据的能力。然而,处理词汇表外键的令牌嵌入方法可以部分解决这个问题。
此外,这些型号中的大多数都使用了变压器【3】架构。虽然最近它已经成为大多数 NLP 问题的标准选择,但与 FastText [4,5]等更简单的方法相比,它需要大量的计算资源。在为生产环境开发模型时,必须考虑这一特性。基于 Transformer 的模型的推理时间,即使部署在能够访问 GPU 的实例上,也可能导致过高的延迟。此外,由于高昂的财务成本,用 GPU 部署多个副本可能是不可能的。
牢记这两个方面,我们的方法基于低延迟令牌嵌入模型,专门针对来自社交媒体的数据进行训练,并将其与简单有效的分类头相结合。给定一条消息,我们从简单的预处理步骤开始:
- 将所有字符改为小写,
- 删除空白字符(制表符、换行符等。),
- 丢弃 URL,
- 将表情符号、表情符号和标签编码为特殊符号,这些符号出现在我们的词汇中("😃"-> "[SMILEY_HAPPY]")。
结果是一个干净的消息,然后被分割成令牌,这些令牌可以使用令牌嵌入模型编码为向量。
我们没有使用已经预先训练好的令牌嵌入模型,而是决定专门在我们自己的社交媒体数据集上从头训练一个新的模型。大部分数据来自脸书几年来收集的帖子和评论。此外,它还包含来自其他社交媒体平台的文本,如 Instagram 或 YouTube。我们使用另一种语言检测模型只选择了波兰语文本。总的来说,该数据集包含 10 亿个单词,其中超过 300 万个是唯一的。这些文本经过了最少的预处理,主要是删除 URL,将所有表情符号和表情符号编码为特殊的符号,并将其包含在我们的词汇中。然而,我们没有纠正任何错别字。
虽然最近的工作主要集中在基于 Transformer 的架构上,但是我们选择了 FastText 作为令牌嵌入模型,原因有几个。首先,快速的推理时间和较低的计算要求使得该模型易于部署。其次,FastText 使用字符级信息,这使得该方法非常适合于形态学丰富的语言,如波兰语。
值得指出的是,从头开始创建模型需要大量的时间,不仅是为了训练(我们在一天内完成了训练),也是为了准备数据集。尽管如此,在 Sotrender 中,我们处理的数据主要来自社交媒体,因此我们能够在其他 NLP 任务中使用该模型,如情感分析。此外,训练基于 Transformer 的模型(如 BERT)通常不是您的首选,因为它对硬件的要求很高,甚至比 FastText 花费更多的时间,因此非常昂贵。我们认为,在实践中,最好从简单的解决方案开始,只有在必要时才逐渐增加复杂性。
在这一点上,重要的是观察到,在许多情况下,检测仇恨言论的任务可以通过搜索句子中有害的单词序列(标记)来解决。通常,识别这样的序列就足够了,不需要分析整个信息的上下文。受这一观察的激励,我们在令牌嵌入模型之后直接采用了一维卷积神经网络(CNN)。包含在 CNN 架构中的平移不变性允许我们精确地处理这样的记号序列。此外,通过考虑具有固定宽度(卷积核的大小)的记号列表的所有这样的子阵列,我们能够创建整个句子的表示。类似的方法可以在其他一些作品[6,7]中找到,甚至可以在单词袋(BoW)模型中找到,该模型考虑了 n-grams 的令牌。图 2 展示了如何将 1D 卷积应用于一系列令牌嵌入。

图二。 1D 卷积神经网络通过在标记序列上滑动固定宽度的窗口来处理编码的句子。该窗口能够捕捉连续记号的模式,而与它们在句子中的位置无关。为了捕捉不同的模式,每个 CNN 层由许多这样的窗口组成,由给定层的深度决定。
虽然 CNN 大多是从计算机视觉问题中得知的,我们的方法证明了它们同样适用于包括文本分类在内的序列处理任务,同时比递归模块的计算要求更低。我们还尝试用其他在序列处理方面表现突出的架构来替代 CNN,包括长短期记忆(LSTM)或门控循环单元(GRU),但最好的结果是使用具有池层的单层 CNN。
总之,使用具有池层的浅 1 维 CNN 来变换令牌嵌入,并使用跟随有 softmax 的学习线性变换来投影到类分布中。此外,我们在倒数第二层使用 l2 正则化和丢弃。完整的架构如图 3 所示。

图三。我们完整的文本处理管道,将句子转换成预测的类别分布。
在训练期间,令牌嵌入模型的权重被冻结,并且网络的剩余可学习参数被优化。在大多数实验中,我们使用验证数据集(使用分层抽样从训练分裂中提取)来提前停止和选择超参数,除非另有说明。此外,我们在训练期间应用简单的数据扩充,这将打乱和删除随机选择的令牌。我们计划在未来的文章中对测试过的增强方法进行全面的回顾。
结果
我们分两步对我们的方法进行了评估。首先,我们将其与提交给 PolEval2019 Task 6 的最佳模型进行了比较。该比较包括该任务的二进制(任务 6.1)和多类(任务 6.2)版本。接下来,我们查看了使用 KLEJ 基准[8]中的 CBD 任务评估的大型变压器模型的结果。由于不均衡的类别分布,对于二元分类任务,使用 F1 分数来比较模型,并且在多类别分类的情况下,使用微观 F1 和宏观 F1 来比较模型。
PolEval2019 任务 6
在这两项任务中,我们将我们的模型与五个最佳竞争对手进行了比较。有趣的是,提交的材料包含一些不同的方法。对于任务 6.1,通过以下方法获得了最佳结果(表 1):
- n-waves UML fit[9]——ul fit 架构的扩展,适用于使用子词标记化的形态丰富的语言,
- Przetak [10] —一种识别有毒单词形式并使用对字符 5-grams 单词的逻辑回归来聚集它们的方法,
- ULMFiT+SP+BA — ULMFiT 与句子成分和分支注意相结合,
- 集成 spacy + tpot + BERT [11] —由来自 spacy 和 BERT 的分类器组成的集成,结合使用 tpot 库自动创建的文本处理管道,
- 系综空间+ tpot [11] —同上,除了使用 ULMFiT 代替 BERT。

表 1。将结果与提交给 PolEval2019 任务 6.1 的模型进行比较。我们的模型被命名为软渲染。
任务 6.2(表 2)中的最佳执行方法还包括:
- 模型 1-svm [12] —在表征的 TF-IDF 表示上训练的支持向量机,
- fasttext [13] —基于在“Narodowy Korpus jzyka Polskiego”(NKJP)上预训练的 fasttext 单词嵌入的模型,
- model3-flair [12] —该方法依赖于快速文本单词嵌入与来自 flair 的前向和后向字符级语言模型的组合,其形成双向 GRU 网络的输入,
- SCWAD-CB [14] —基于特征组合的多层感知器,包括激光嵌入、Morfeusz 限定词、基于外部词汇和字符 n-grams 提取的粗俗和攻击性词语。

表二。将结果与提交至 PolEval2019 任务 6.2 的模型进行比较。
正如在两个表中所看到的,我们的方法大大超过了其他报道的结果。重要的是要记住,我们的模型不是基于复杂的功能,如[10]或[14],也不依赖于大架构,如[9]和[11]。这种明显的改进来自于使用领域特定的嵌入,这些嵌入是在类似于模型化的任务/问题的数据上训练的。
KLEJ 基准
为了进一步证明我们模型的优势,我们将它与 KLEJ 基准[8]中评估的基于变压器的方法进行了比较。在许多任务中,它包括网络欺凌检测(CBD),它使用与 PolEval2019 的任务 6.1 相同的数据集。出于公平比较的目的,我们遵循[15]中的设置,并在整个训练分割上训练模型。

表 3。将结果与提交给 KLEJ 基准的模型进行比较【8】。有关各型号的详细信息,请参考 KLEJ 排行榜。
从表中可以看出,虽然我们没有达到排行榜的首位,但我们取得了不相上下的成绩。我们的车型排名第四,超越了基于伯特、罗伯塔或 XLM 的其他车型。
结论
在本文中,我们已经展示了实现复杂的架构或者增加模型参数的数量并不是成功的唯一途径。我们的方法认为,另一个关键因素是使用特定领域的归纳偏见。在我们的例子中,这些偏差主要存在于令牌嵌入模块中,但也存在于 CNN 中,CNN 非常适合于仇恨言论检测的问题。
由于我们方法的简单性,我们能够成功地将我们的模型部署到生产环境中。推理过程可以在没有 GPU 的情况下执行,并且只需要一个 CPU 和不到 3GB 的 RAM。这产生了易于水平扩展的低成本解决方案。如果您对我们如何部署我们的模型感兴趣,请在评论中提出问题,我们也会尽力准备一篇关于它的文章。
从这篇文章中得到的关键观察是,认识到即使在变形金刚的世界里,锤子也可以被螺丝刀代替。
参考文献
[1] P. Joshi 等人,NLP 世界中语言多样性和包容性的状况和命运 (2020),arXiv 预印本 arXiv:2004.09095
[2] M. E. Ptaszynski 和 F. Masui 编辑。,自动网络欺凌检测:新兴研究和机遇:新兴研究和机遇 (2018),IGI 全球 2018
[3] A. Vaswani 等人,注意力是你所需要的全部 (2017),神经信息处理系统进展 2017
[4] P. Bojanowski 等,用子词信息丰富词向量 (2017),计算语言学协会会刊 5(2017):135–146
[5] A. Joulin 等人,高效文本分类的锦囊妙计 (2016),arXiv 预印本 arXiv:1607.01759
[6]b . gamb CK 和 U. K. Sikdar,使用卷积神经网络对仇恨言论进行分类 (2017),第一届在线辱骂性语言研讨会会议录
[7] S. Bai,J. Z. Kolter 和 V. Koltun,用于序列建模的一般卷积和递归网络的经验评估 (2018),arXiv 预印本 arXiv:1803.01271
[8] P. Rybak 等人, KLEJ:波兰语理解综合基准 (2020),arXiv 预印本 arXiv:2005.00630
[9] P. Czapla 等人,波兰仇恨言论检测通用语言模型微调 (2019),PolEval 2019 年研讨会会议录:149
[10] M. Ciura, Przetak:网络上的杂草越来越少 (2019),PolEval 2019 研讨会会议录:127
[11] R. Korzeniowski 等人,利用无监督预训练和自动化特征工程进行波兰语低资源仇恨言论检测 (2019),arXiv 预印本 arXiv:1906.09325
[12] M. Biesek,波兰语自动网络欺凌检测中传统机器学习方法和深度学习模型的比较 (2019),PolEval 2019 研讨会论文集:121
[13] K. Wróbel,对波兰推文进行自动网络欺凌检测 (2019)
[14] K. Krasnowska-Kieras 和 A. Wróblewska,用于检测网络欺凌的简单神经网络 (2019),PolEval 2019 研讨会会议录:161
[15] S. Dadas,m . perekiewicz 和 r . powiata,大规模预训练基于波兰语转换器的语言模型 (2020),arXiv 预印本 arXiv:2006.04229
建模回归树
如何编写这个经典的机器学习算法(Python)

🇨🇭·克劳迪奥·施瓦茨| @purzlbaum 在 Unsplash 上拍摄的照片
决策树可能是最流行的机器学习算法之一。在我的帖子 " 决策树完全指南 s" 中,我详细描述了 DT:它们在现实生活中的应用,不同的 DT 类型和算法,以及它们的优缺点。我已经详细介绍了如何编写分类树,现在轮到回归树了。
回归树使用数字目标变量。与目标变量是定性的分类树不同,回归树用于预测连续输出变量。如果您想要预测诸如医疗成功的概率、金融股票的未来价格或给定人群的工资等事情,您可以使用此算法。让我们看一个用 Python 实现的例子。
问题是
波士顿住房数据集由美国波士顿不同地方的房价组成。除了价格之外,该数据集还提供了犯罪水平、城镇非零售商业区、房屋所有者的年龄以及其他属性等信息。
名为“MEDV”的变量表示房价,是目标变量。其余的变量是我们预测房子价值的预测因素。
台阶
您可以通过处理更简单的子步骤来降低构建 DTs 的复杂性:DT 中的每个单独的子例程都将连接到其他子例程以增加复杂性,这种构造将让您获得更健壮的模型,更易于维护和改进。现在,让我们用 Python 构建一棵回归树(特殊类型的 DT)。
加载数据并描述数据集
加载数据文件是最简单的部分。问题(也是最耗时的部分)通常是指数据准备过程:设置正确的数据格式、处理缺失值和异常值、消除重复值等。
在加载数据之前,我们将导入必要的库:
import pandas as pd
from pandas_datareader import data
import numpy as np
from sklearn.tree import DecisionTreeRegressor
from sklearn.model_selection import train_test_split
from sklearn import metrics
from sklearn.metrics import r2_score
现在,我们加载数据集并将其转换为熊猫数据帧:
boston = datasets.load_boston()
df = pd.DataFrame(boston.data)
并将列命名为:
df.columns = boston.feature_names
df[‘MEDV’] = boston.target
首先理解数据集并描述它:
print(boston.DESCR)df.info()

不错:506 条记录,14 个数字变量,没有缺失值。我们不需要预处理数据,我们已经准备好建模。
选择特征和目标变量
您需要将给定的列分成两种类型的变量:因变量(或目标变量)和自变量(或特征变量)。在我们的例子中,变量“MEDV”(自有住房的中值)是我们试图预测的。
X = df.iloc[:,0:13].copy()
y = df.iloc[:,13].copy()
分割数据集
要了解模型性能,将数据集分为定型集和测试集是一个好策略。通过将数据集分成两个独立的集合,我们可以使用一个集合进行训练,使用另一个集合进行测试。
- 训练集:这些数据用来建立你的模型。例如使用 CART 算法来创建决策树。
- 测试集:该数据用于查看模型在看不见的数据上的表现,就像在现实世界中一样。在您想要测试您的模型以评估性能之前,这些数据应该是完全不可见的。
接下来,我们将数据集分成 70%训练和 30%测试。
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)
建立 DT 模型并微调
构建 DT 就像这样简单:
rt = DecisionTreeRegressor(criterion = ‘mse’, max_depth=5)
在这种情况下,我们只定义了分裂标准(选择均方误差)和一个超参数(树的最大深度)。定义模型架构的参数被称为超参数,因此,搜索理想模型架构(最大化模型性能的架构)的过程被称为超参数调整。 A 超参数是在学习过程开始前就设定好值的参数,它们不能直接从数据中训练出来。
您可以通过调用模型来查看可以优化的其余超参数:
rt

模型可以有许多超参数,并且有不同的策略来寻找参数的最佳组合。你可以在这个链接上看看其中的一些。
列车 DT 模型
将模型拟合到训练数据代表了建模过程的训练部分。在模型定型后,可以使用预测方法调用来进行预测:
model_r = rt.fit(X_train, y_train)
测试 DT 模型
测试数据集是独立于训练数据集的数据集。该测试数据集是您的模型的未知数据集,有助于您对其进行概化:
y_pred = model_r.predict(X_test)
设想
DTs 最大的优势之一是它们的可解释性。可视化 DTs 不仅是理解模型的有效方法,也是传达模型工作原理的有效方法:
from sklearn import tree
import graphviz
dot_data = tree.export_graphviz(rt, feature_names=list(X), class_names=sorted(y.unique()), filled=True)
graphviz.Source(dot_data)

变量“LSTAT”似乎对定义回归树的划分至关重要。我们将在计算要素重要性后检查这一点。
评估绩效
模型的质量与其预测值与实际值的匹配程度有关。评估你的机器学习算法是任何项目必不可少的一部分:你如何衡量它的成功,你什么时候知道它不应该再改进了?不同的机器学习算法有不同的评估指标,所以让我们提到一些回归问题的主要评估指标:
平均绝对误差(MAE)
是测试集中所有实例的单个预测误差绝对值的平均值。它告诉我们平均预期误差有多大。
print(‘Mean Absolute Error:’, metrics.mean_absolute_error(y_test, y_pred))

均方误差
是测试集中所有实例的预测误差平方的平均值。因为 MSE 是平方的,所以它的单位与原始输出的单位不匹配,而且因为我们正在平方差值,所以 MSE 几乎总是大于 MAE:因此我们不能直接比较 MAE 和 MSE。
print(‘Mean Squared Error:’, metrics.mean_squared_error(y_test, y_pred))

MSE 方程中平方项的影响在我们的数据中存在异常值时最为明显:虽然 MAE 中的每个残差按比例贡献给总误差,但 MSE 中的误差以二次方的方式增长。这最终意味着,我们数据中的异常值将导致 MSE 中比 MAE 中更高的总误差,并且该模型将因做出与相应实际值相差很大的预测而受到更多惩罚。
均方根误差(RMSE)
是所有误差的平方的平均值的平方根。通过在计算平均值之前对误差求平方,然后取平均值的平方根,我们得到了一个误差大小的度量,该度量对较大但不常见的误差给予了比平均值更大的权重。我们还可以比较 RMSE 和梅,以确定预测是否包含大量但不常见的误差:RMSE 和梅之间的差异越大,误差大小越不一致。
print(‘Root Mean Squared Error:’, np.sqrt(metrics.mean_squared_error(y_test, y_pred)))

R 平方得分(R2)
用百分比解释由于特征变量的变化而引起的响应变量的变化量。 R 的平方可以取 0 到 1 之间的任何值,尽管它提供了一些关于回归模型的有用见解,但是您不应该只依赖这个度量来评估您的模型。
print(‘R Squared Score is:’, r2_score(y_test, y_pred))

对 R 平方最常见的解释是回归模型与观察数据的拟合程度。就像我们的例子一样,R 的平方为 0.74 表明 74%的数据符合回归模型。虽然较高的 R 平方表示模型更适合,但高度量值并不总是适合回归模型:统计度量值的质量取决于许多因素,例如模型中使用的变量的性质、变量的度量单位以及应用的数据转换。
特征重要性
另一个关键指标包括为预测模型的输入特征分配分数,表明每个特征在进行预测时的相对重要性。特征重要性提供对数据、模型的洞察,并代表降维和特征选择的基础,这可以提高预测模型的性能。越多的属性用于 DT 的关键决策,其相对重要性就越高。
for importance, name in sorted(zip(rt.feature_importances_, X_train.columns),reverse=True):
print (name, importance)

如可视化中突出显示的,变量“LSTAT”相对于其他变量具有更高的重要性(是模型的主要特征)。让我们在图上看一下:

特征“LSTAT”和“RM”占进行预测的重要性的 80%以上。
我们只能将我们模型的误差指标与竞争模型的误差指标进行比较(例如,2 个不同模型的 R 平方得分),尽管这些指标提供了关于模型性能的宝贵见解,但请始终记住:
仅仅因为一个预测在过去是准确的,并不意味着它在未来也是准确的。
最后的想法
我们在建模过程中已经涵盖了几个步骤,其中每一个都是独立的学科:探索性数据分析、特征工程或超参数调整都是任何机器学习模型的广泛而复杂的方面。你应该考虑更深入地研究那些学科。
与其他算法相比,决策树的一个重要方面是它划分数据空间的方式。如果您选择用线性回归来求解波士顿房价预测,您会看到如下图表:

线性回归将搜索目标与其预测值之间的线性关系。在这个例子中,两个变量(“MEDV”和“RM”)似乎是线性相关的,这就是为什么这种方法可能工作得相对较好,但现实往往显示非线性关系。让我们看看回归树如何映射目标和预测值之间的相同关系:

在此示例中,使用 MSE 作为分区标准且 max_depth 为 5 的回归树以完全不同的方式划分数据空间,从而确定线性回归无法拟合的关系。
决策树划分数据空间以优化给定标准的方式不仅取决于标准本身(例如 MSE 或 MAE 作为划分标准),还取决于所有超参数的设置。超参数优化定义了决策树的工作方式,并最终决定了它的性能。一些超参数会严重影响模型的性能,找到它们的正确级别对于达到最佳性能至关重要。在下面的示例中,您可以看到超参数 max_depth 在设置为 0 到 10 之间时如何对回归树的 R 平方得分产生巨大影响,但在 10 以上,您选择的任何级别都不会对其产生影响:

为了克服你试图为你的 DT 找到“完美的”超参数水平而使你的模型过度拟合的事实,你应该考虑探索集合方法。集成方法将几个 DTs 结合起来,产生比单个 DTs 更好的预测性能。系综模型背后的主要原理是一群弱学习者聚集在一起形成强学习者,显著提高单个 DT 的性能。它们用于减少模型的方差和偏差,并改进预测。既然你已经看到了决策树是如何工作的,我建议你继续使用集合方法,比如打包或推进。
用 CatBoost 和 NODE 对表格数据建模
俄罗斯在线搜索公司 Yandex 的 CatBoost 快速易用,但最近该公司的研究人员发布了一个新的基于神经网络的包 NODE,他们声称该包优于 CatBoost 和所有其他梯度提升方法。这可能是真的吗?让我们了解一下如何同时使用 CatBoost 和 NODE!
这篇博文是写给谁的?
虽然我写这篇博文是为了那些对机器学习,尤其是表格数据感兴趣的人,但是如果你熟悉 Python 和 scikit-learn 库,并且想了解代码,那么这篇博文会很有帮助。如果你不是,希望你会发现理论和概念部分很有趣!
CatBoost 简介
CatBoost 是我用来建模表格数据的软件包。它是梯度增强决策树的一种实现,只做了一些调整,与 xgboost 或 LightGBM 略有不同。它适用于分类和回归问题。
CatBoost 的一些优点:
- 它处理猫电气特征(明白吗?)开箱即用,所以不需要担心如何对它们进行编码。
- 它通常只需要很少的参数调整。
- 它避免了其他方法可能遭受的某些微妙类型的数据泄漏。
- 它很快,如果你想让它跑得更快,可以在 GPU 上运行。
对我来说,这些因素使得 CatBoost 成为当我需要分析新的表格数据集时首先要做的事情。
CatBoost 的技术细节
如果只是想用 CatBoost 就跳过这一节!
在更技术性的层面上,关于 CatBoost 是如何实现的,有一些有趣的事情。如果你对细节感兴趣,我强烈推荐论文 Catboost:具有分类特征的无偏增强。我只想强调两件事。
- 在这篇论文中,作者表明,标准梯度推进算法受到模型迭代拟合方式导致的微妙类型的数据泄漏的影响。同样,对分类特征进行数字编码的最有效方法(如目标编码)也容易出现数据泄漏和过拟合。为了避免这种泄漏,CatBoost 引入了一个人工时间线,训练示例根据该时间线到达,以便在计算统计数据时只能使用“以前看到的”示例。
- CatBoost 其实不用常规决策树,而是不经意决策树。在这些树中,在树的每一层,相同的特征和相同的分裂标准被到处使用!这听起来很奇怪,但是有一些很好的特性。我们来看看这是什么意思。


左: 浑然不觉决策树。每个级别都有相同的拆分。 右: 常规决策树。任何特征或分割点都可以出现在每个级别上。
在普通决策树中,要分割的特征和截止值都取决于到目前为止您在树中选择的路径。这是有意义的,因为我们可以使用我们已经拥有的信息来决定下一个最有价值的问题(就像在“20 个问题”游戏中一样)。对于遗忘决策树,历史并不重要;不管怎样,我们都提出同样的问题。这些树被称为“健忘的”,因为它们总是“忘记”以前发生过的事情。
这为什么有用?不经意决策树的一个很好的特性是,一个例子可以很快被分类或评分——它总是提出相同的 N 个二元问题(其中 N 是树的深度)。对于许多示例,这可以很容易地并行完成。这是 CatBoost 速度快的一个原因。另一件要记住的事情是,我们在这里处理的是一个树集合。作为一个独立的算法,遗忘决策树可能不会工作得很好,但树集成的想法是,弱学习者的联盟通常工作得很好,因为错误和偏见被“淘汰”。通常,弱学习者是一个标准的决策树,这里它是一个更弱的东西,即不经意决策树。CatBoost 的作者认为,这种特殊的弱基础学习者很适合泛化。
安装 CatBoost
虽然安装 CatBoost 应该是一件简单的事情
pip install catboost
在 Mac 电脑上,我有时会遇到这方面的问题。在 Linux 系统上,比如我现在输入的 Ubuntu 系统,或者在 Google Colaboratory 上,它应该“正常工作”。如果你在安装时一直有问题,考虑使用 Docker 镜像,例如
docker pull yandex/tutorial-catboost-clickhouse
docker run -it yandex/tutorial-catboost-clickhouse
在数据集上使用 CatBoost
链接到代码为的 Colab 笔记本
让我们看看如何在表格数据集上使用 CatBoost。我们从下载成人/人口普查收入数据集的轻度预处理版本开始,在下文中,假设该数据集位于 datasets/adult.csv 中。我选择该数据集是因为它混合了分类和数字特征,在数万个示例中具有良好的可管理大小,并且没有太多的特征。它经常被用来举例说明算法,例如在谷歌的假设工具和许多其他地方。
成人人口普查数据集在 Colab 上有“年龄”、“工作阶级”、“教育”、“教育人数”、“婚姻状况”、“职业”、“关系”、“种族”、“性别”、“资本收益”、“资本损失”、“每周小时数”、“本国”和“T6”等列,但我将在此复制这些列以供参考。CatBoost 需要知道哪些特征是分类的,然后自动处理它们。在这个代码片段中,我还使用 5 重(分层)交叉验证来估计预测精度。
我们从运行这个(没有超参数优化的 CatBoost)中得到的是 85%到 86%之间的平均准确率。在我最后一次运行中,我得到了大约 85.7%。
如果我们想要尝试优化超参数,我们可以使用 hyperopt(如果您没有它,请使用 pip install hyperopt 安装它)。为了使用它,您需要定义一个 hyperopt 试图最小化的函数。我们将尝试优化这里的精度。也许对日志丢失进行优化会更好,但这是留给读者的练习;)
from catboost import CatBoostClassifier, Pool
from hyperopt import fmin, hp, tpe
import pandas as pd
from sklearn.model_selection import StratifiedKFolddf = pd.read_csv('https://docs.google.com/uc?' +
'id=10eFO2rVlsQBUffn0b7UCAp28n0mkLCy7&' +
'export=download')
labels = df.pop('<=50K')categorical_names = ['workclass', 'education', 'marital-status',
'occupation', 'relationship', 'race',
'sex', 'native-country']
categoricals = [df.columns.get_loc(i) for i in categorical_names]nfolds = 5
skf = StratifiedKFold(n_splits=nfolds, shuffle=True)
acc = []for train_index, test_index in skf.split(df, labels):
X_train, X_test = df.iloc[train_index].copy(), \
df.iloc[test_index].copy()
y_train, y_test = labels.iloc[train_index], \
labels.iloc[test_index]
train_pool = Pool(X_train, y_train, cat_features = categoricals)
test_pool = Pool(X_test, y_test, cat_features = categoricals)
model = CatBoostClassifier(iterations=100,
depth=8,
learning_rate=1,
loss_function='MultiClass')
model.fit(train_pool)
predictions = model.predict(test_pool)
accuracy = sum(predictions.squeeze() == y_test) / len(predictions)
acc.append(accuracy)mean_acc = sum(acc) / nfolds
print(f'Mean accuracy based on {nfolds} folds: {mean_acc:.3f}')
print(acc)
要优化的主要参数可能是迭代次数、学习速率和树深度。还有许多与过拟合相关的其他参数,例如提前停止轮次等。请自行探索!
当我最后一次运行这段代码时,它花了 5 个多小时,但平均准确率为 87.3%,这与我在尝试 Auger.ai AutoML 平台时获得的最好结果相当。
健全性检查:逻辑回归
# Optimize between 10 and 1000 iterations and depth between 2 and 12search_space = {'iterations': hp.quniform('iterations', 10, 1000, 10),
'depth': hp.quniform('depth', 2, 12, 1),
'lr': hp.uniform('lr', 0.01, 1)
}def opt_fn(search_space): nfolds = 5
skf = StratifiedKFold(n_splits=nfolds, shuffle=True)
acc = [] for train_index, test_index in skf.split(df, labels):
X_train, X_test = df.iloc[train_index].copy(), \
df.iloc[test_index].copy()
y_train, y_test = labels.iloc[train_index], \
labels.iloc[test_index]
train_pool = Pool(X_train, y_train, cat_features = categoricals)
test_pool = Pool(X_test, y_test, cat_features = categoricals) model = CatBoostClassifier(iterations=search_space['iterations'],
depth=search_space['depth'],
learning_rate=search_space['lr'],
loss_function='MultiClass',
od_type='Iter') model.fit(train_pool, logging_level='Silent')
predictions = model.predict(test_pool)
accuracy = sum(predictions.squeeze() == y_test) / len(predictions)
acc.append(accuracy) mean_acc = sum(acc) / nfolds
return -1*mean_accbest = fmin(fn=opt_fn,
space=search_space,
algo=tpe.suggest,
max_evals=100)
在这一点上,我们应该问自己是否真的需要这些新奇的方法。一个好的旧逻辑回归在开箱即用和超参数优化后会有怎样的表现?
为了简洁起见,我在这里省略了复制代码,但它在和之前一样的 Colab 笔记本中是可用的。逻辑回归实现的一个细节是,它不像 CatBoost 那样处理开箱即用的分类变量,所以我决定使用目标编码对它们进行编码,特别是留一目标编码,这是 NODE 中采用的方法,与 CatBoost 中发生的情况非常接近,但不完全相同。
长话短说,使用这种编码类型的未调整的逻辑回归产生大约 80%的准确性,在超参数调整后大约 81%(在我最近的运行中是 80.7%)。这里,一个有趣的替代方法是尝试自动化预处理库,如 vtreat 和 Automunge ,但我将把它们留到下一篇博文中!
盘点
在尝试 NODE 之前,到目前为止我们有什么?
未调整的逻辑回归:80.0%
逻辑回归,调整率:80.7%
- CatBoost,未调整:85.7%
- 催化增强,调优:87.2%
- 节点:神经不经意决策集成
- Yandex 研究人员最近的一份手稿描述了一个有趣的神经网络版本的 CatBoost,或者至少是一个神经网络采用了不经意决策树集成(如果你想提醒自己“不经意”在这里是什么意思,请参见上面的技术部分。)这种体系结构称为节点,可用于分类或回归。
摘要中的一项声明写道:“通过在大量表格数据集上与领先的 GBDT 软件包进行广泛的实验比较,我们展示了所提出的节点架构的优势,它在大多数任务上优于竞争对手。”这自然激起了我的兴趣。这个工具会比 CatBoost 更好吗?
NODE 是如何工作的?
你应该去报纸上看完整的故事,但是一些相关的细节是:
entmax 激活函数被用作常规决策树中分裂的软版本。正如论文所说,“entmax 能够产生稀疏的概率分布,其中大多数概率恰好等于 0。在这项工作中,我们认为,在我们的模型中,entmax 也是一个适当的归纳偏差,它允许在内部树节点中进行可微分的分裂决策构建。直观地说,entmax 可以根据数据特征的一个小子集(最多一个,如在经典决策树中)学习拆分决策,避免来自其他人的不良影响。entmax 函数允许神经网络模拟决策树类型的系统,同时保持模型可微分(权重可以基于梯度更新)。
作者提出了一种新类型的层,即“节点层”,可以在神经网络中使用(他们的实现是在 PyTorch 中)。一个节点层代表一个树集合。
- 几个节点层可以堆叠,产生一个层次模型,其中输入通过一个树集合一次馈入。输入表示的连续连接可以用于给出一个模型,该模型让人想起用于图像处理的流行的 DenseNet 模型,只是专门用于表格数据。
- 节点模型的参数包括:
- 学习率(论文中总是 0.001)
节点层数( k
- 每层树的数量( m )
- 每层树的深度( d )
- 节点与树集合有什么关系?
- 为了感受一下这种神经网络架构和决策树集合之间的相似性,这里复制了图 1。
节点层如何与决策树相关联。
应该如何选择参数?

手稿中没有太多的指导;作者建议使用超参数优化。他们确实提到他们在以下领域进行了优化:
层数:{2,4,8}
总树数:{1024,2048}
- 树深度:{6,8}
- 树输出尺寸:{2,3}
- 在我的代码中,我不进行网格搜索,而是让 hyperopt 在特定范围内采样值。我认为(这可能是错误的)每一层都代表一个树集合(比如说 CatBoost 的一个实例)。对于您添加的每一层,您可能会添加一些表示能力,但是您也会使模型变得更难训练,并可能有过度拟合的风险。总树数似乎大致类似于 CatBoost/xgboost/random 森林中的树数,并且具有相同的权衡:如果有许多树,您可以表达更复杂的函数,但是模型将需要更长的时间来训练,并且有过度拟合的风险。树的深度也有同样的权衡。至于输出维度,坦白说,我不太明白为什么是参数。看论文,好像回归应该等于 1,分类应该等于类数。
- 如何使用节点?
作者们已经在 GitHub 上发布了代码。他们不提供命令行界面,而是建议用户在提供的 Jupyter 笔记本上运行他们的模型。这些笔记本中提供了一个分类示例和一个回归示例。
repo README 页面还强烈建议使用 GPU 来训练节点模型。(这是有利于 CatBoost 的一个因素。)
我准备了一个合作笔记本,里面有一些关于如何在节点上运行分类以及如何用 hyperopt 优化超参数的示例代码。
现在请移至 合作笔记本 继续关注!
这里我将只强调代码的一些部分。
改编代码的一般问题
我在改编作者的代码时遇到的问题主要与数据类型有关。重要的是,输入数据集(X_train 和 X_val)是 float32 格式的数组(numpy 或 torch );不是 float64 或 float 和 int 的混合。标签需要编码为长( int64 )用于分类,编码为 float32 用于回归。(您可以在标题为“加载、分割和预处理数据”的单元中看到这一点。)
其他问题与记忆有关。这些模型可能会很快耗尽 GPU 内存,尤其是在作者的示例笔记本中使用的大批量数据。我通过在笔记本电脑上使用最大批量解决了这个问题。
不过,总的来说,让代码工作并不难。文档有点少,但是足够了。
分类变量处理
与 CatBoost 不同,NODE 不支持分类变量,所以您必须自己将它们准备成数字格式。我们对成人人口普查数据集采用与节点作者相同的方式进行处理,使用 category_encoders 库中的 LeaveOneOutEncoder。为了方便起见,这里我们只使用常规训练/测试分割,而不是 5 重 CV,因为训练节点需要很长时间(特别是使用超参数优化)。
现在我们有了一个全数字的数据集。
模型定义和训练循环
from category_encoders import LeaveOneOutEncoder
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_splitdf = pd.read_csv('https://docs.google.com/uc' +
'?id=10eFO2rVlsQBUffn0b7UCAp28n0mkLCy7&' +
'export=download')
labels = df.pop('<=50K')
X_train, X_val, y_train, y_val = train_test_split(df,
labels,
test_size=0.2)class_to_int = {c: i for i, c in enumerate(y_train.unique())}
y_train_int = [class_to_int[v] for v in y_train]
y_val_int = [class_to_int[v] for v in y_val] cat_features = ['workclass', 'education', 'marital-status',
'occupation', 'relationship', 'race', 'sex',
'native-country']
cat_encoder = LeaveOneOutEncoder()
cat_encoder.fit(X_train[cat_features], y_train_int)
X_train[cat_features] = cat_encoder.transform(X_train[cat_features])
X_val[cat_features] = cat_encoder.transform(X_val[cat_features])# Node is going to want to have the values as float32 at some points
X_train = X_train.values.astype('float32')
X_val = X_val.values.astype('float32')
y_train = np.array(y_train_int)
y_val = np.array(y_val_int)
代码的其余部分与作者报告中的基本相同(除了 hyperopt 部分)。他们创建了一个名为 DenseBlock 的 Pytorch 层,实现了节点架构。一个名为 Trainer 的类保存有关实验的信息,并且有一个简单的训练循环来跟踪迄今为止看到的最佳指标,并绘制更新的损失曲线。
结果和结论
通过一些最小的尝试和错误,我能够找到一个验证准确率大约为 86%的模型。用 hyperopt 进行超参数优化后(本该在 Colab 的一个 GPU 上通宵运行,但实际上在大约 40 次迭代后超时),最佳性能为 87.2%。在其他跑步中,我取得了 87.4%的成绩。换句话说,经过 hyperopt 调优后,NODE 的性能确实优于 CatBoost,尽管只是略微优于 CatBoost。
然而,准确性并不是一切。为每个数据集进行昂贵的优化是不方便的。
NODE 与 CatBoost 的优势:
似乎可以得到稍微好一点的结果(基于节点纸和这个测试;我一定会尝试许多其他数据集!)
CatBoost 与 NODE 的优点:
- 快多了
减少对超参数优化的需求
- 没有 GPU 也能正常运行
- 支持分类变量
- 我会在下一个项目中使用哪一个?可能 CatBoost 仍将是我的首选工具,但我会记住 NODE,并可能尝试它以防万一…
- 同样重要的是要认识到,性能是依赖于数据集的,成人人口普查收入数据集并不能代表所有情况。也许更重要的是,分类特征的预处理在 NODE 中可能相当重要。我将在以后的文章中回到预处理的主题!
Which one would I use for my next projects? Probably CatBoost will still be my go-to tool, but I will keep NODE in mind and maybe try it just in case…
It’s also important to realize that performance is dataset-dependent and that the Adult Census Income dataset is not representative of all scenarios. Perhaps more importantly, the preprocessing of categorical features is likely rather important in NODE. I’ll return to the subject of preprocessing in a future post!
用 Google 的 TabNet 对表格数据建模
神经网络能打败经典方法吗?

2019 年发布,谷歌研究的 TabNet 在一篇 预印稿 中声称在表格数据上胜过现有方法。它是如何工作的,怎样才能尝试呢?
如今,表格数据可能构成了大部分业务数据。想想零售交易、点击流数据、工厂中的温度和压力传感器、银行使用的 KYC(了解你的客户)信息,或者制药公司使用的模式生物的基因表达数据。品种简直是无穷无尽。
描述这种方法的论文名为 TabNet:专注的可解释表格学习,它很好地总结了作者们试图做的事情。“网络”部分告诉我们,它是一种神经网络,“注意力”部分意味着它正在使用一种注意力机制,它的目标是可解释的,它用于对表格数据进行机器学习。
它是如何工作的?
TabNet 使用一种软特征选择,只关注对于手头的例子重要的特征。这是通过连续的多步决策机制实现的。也就是说,输入信息是分几个步骤自顶向下处理的。正如手稿所述,“顺序形式的自上而下注意的想法是受其在处理视觉和语言数据中的应用的启发,如视觉问题回答(Hudson & Manning,2018 年)或强化学习(Mott 等人,2019 年),同时在高维输入中搜索相关信息的子集。
执行这种顺序注意的构建块被称为变压器块,即使它们与流行的 NLP 模型中使用的变压器有点不同,如 BERT 。这些变形金刚使用自我关注,并试图模拟句子中不同单词之间的依赖关系。这里使用的转换器类型试图通过使用 sparsemax 函数完成的“软”特征选择,逐步消除那些与当前示例不相关的特征。
论文中的第一张图,复制如下,描绘了信息是如何被聚合以形成预测的。

图片来自https://arxiv.org/abs/1908.07442
TabNet 的一个很好的特性是它不需要特征预处理(与例如节点相反)。另一个原因是,它具有“免费”内置的可解释性,为每个示例选择最相关的功能。这意味着您不必应用外部解释模块,如 shap 或 LIME 。
当阅读这篇文章时,不太容易理解这个架构内部发生了什么,但幸运的是有发布的代码稍微澄清了一些事情,并表明它没有你想象的那么复杂。
怎么用?
2020 年 3 月 9 日新增:
现在 TabNet 有了比下面描述的更好的接口:一个用于 PyTorch ,它有一个类似 scikit-learn 的接口,一个用于FastAI。
原始的 TabNet 代码和我的修改
正如已经提到的,代码是可用的,作者展示了如何将它与森林覆盖类型数据集一起使用。为了方便起见,他们提供了三个特定于数据集的文件:一个文件下载并准备数据(download _ prepare _ cover type . py),另一个文件定义适当的 Tensorflow 特征列和 CSV 阅读器输入函数(data _ helper _ cover type . py),以及包含训练循环的文件(experiment _ cover type . py)。
回购自述文件指出:
要将实验修改为其他表格数据集:
-替换“data/”目录下的 train.csv、val.csv 和 test.csv 文件。
-用新数据集的数字和分类特征修改 data_helper 函数,
-重新优化新数据集的 TabNet 超参数。
在用其他数据集经历了几次这个过程之后,我决定编写自己的包装器代码来简化这个过程。这个代码,我必须强调是一个完全非官方的分叉,在 GitHub 上。
根据上面的自述文件:
- 与其为每个数据集创建新的 train.csv、val.csv 和 test.csv 文件,我更喜欢读取整个数据集并在内存中进行拆分(当然,只要可行),所以我在代码中为 Pandas 编写了一个新的输入函数。
- 修改 data_helper.py 文件可能需要一些工作,至少在开始时,当您不太确定它做什么以及应该如何定义特性列时(我就是这种情况)。还有许多参数需要更改,但这些参数在主训练循环文件中,而不是在数据帮助文件中。鉴于此,我还试图在我的代码中概括和简化这个过程。
- 我添加了一些快速和肮脏的代码来进行超参数优化,但到目前为止只用于分类。
- 还值得一提的是,作者的示例代码只显示了如何进行分类,而不是回归,因此额外的代码也必须由用户编写。我已经添加了回归功能和简单的均方误差损失。
使用命令行界面
执行如下命令:
python train_tabnet.py \
--csv-path data/adult.csv \
--target-name "<=50K" \
--categorical-features workclass,education,marital.status,\
occupation,relationship,race,sex,native.country\
--feature_dim 16 \
--output_dim 16 \
--batch-size 4096 \
--virtual-batch-size 128 \
--batch-momentum 0.98 \
--gamma 1.5 \
--n_steps 5 \
--decay-every 2500 \
--lambda-sparsity 0.0001 \
--max-steps 7700
强制参数是— -csv-path(指向 CSV 文件的位置)、--target-name(带有预测目标的列的名称)和--categorical-featues(应该被视为分类的特征的逗号分隔列表)。其余的输入参数是超参数,需要针对每个特定问题进行优化。不过,上面显示的值直接取自 TabNet 手稿,因此作者已经针对成人人口普查数据集对它们进行了优化。
默认情况下,训练过程会将信息写入您执行脚本的位置的 tflog 子文件夹中。您可以将 tensorboard 指向此文件夹,查看训练和验证统计数据:
tensorboard --logdir tflog
并将您的网络浏览器指向localhost:6006。
如果你没有 GPU…
…你可以试试这款合作笔记本。请注意,如果您想查看 Tensorboard 日志,您最好的选择可能是创建一个 Google 存储桶,并让脚本在那里写入日志。这是通过使用tb-log-location参数完成的。例如,如果您的 bucket 的名称是camembert-skyscrape,您可以将--tb-log-location gs://camembert-skyscraper添加到脚本的调用中。(不过,请注意,您必须正确设置存储桶的权限。这可能有点麻烦。)
然后,您可以从自己的本地计算机将 tensorboard 指向该桶:
tensorboard --logdir gs://camembert-skyscraper
超参数优化
在 repo 中还有一个进行超参数优化的快速而肮脏的脚本( opt_tabnet.py )。同样,在合作笔记本中显示了一个例子。到目前为止,该脚本仅适用于分类,值得注意的是,一些训练参数仍然是硬编码的,尽管它们实际上不应该是硬编码的(例如,早期停止的耐心参数[在最佳验证准确性没有提高的情况下,您继续多少步]。)
在优化脚本中变化的参数是 N_steps、feature_dim、batch-momentum、gamma、lambda-sparsity。(output_dim 被设置为等于 feature_dim,正如下面的优化提示中所建议的。)
本文提供了以下关于超参数优化的提示:
大多数数据集产生 N_steps ∈ [3,10]的最佳结果。通常,更大的数据集和更复杂的任务需要更大的 N_steps。非常高的 N_steps 值可能遭受过度拟合,并且产生较差的泛化能力。
调整 Nd [feature_dim]和 Na [output_dim]的值是权衡性能和复杂度的最有效方法。对于大多数数据集,Nd = Na 是一个合理的选择。非常高的 Nd 和 Na 值可能遭受过拟合,并且产生较差的概括。
γ的最佳选择会对整体性能产生重大影响。通常,较大的 N_steps 值有利于较大的γ。
大批量有利于提高性能-如果内存限制允许,建议使用总训练数据集大小的 1–10%。虚拟批量通常比批量小得多。
最初大的学习速率是重要的,它应该逐渐衰减直到收敛。
结果
我已经通过这个命令行界面对几个数据集尝试了 TabNet,包括我在关于 NODE 的帖子中使用的成人人口普查数据集和 CatBoost ,原因可以在那个帖子中找到。方便的是,这个数据集也曾在 TabNet 手稿中使用过,作者展示了他们在那里找到的最佳参数设置。使用这些设置重复运行,我注意到最佳验证误差(和测试误差)往往在 86%左右,类似于没有超参数调整的 CatBoost】。作者在手稿中报告了 85.7%的测试集性能。当我使用 hyperopt 进行超参数优化时,不出所料,我达到了大约 86%的类似性能,尽管参数设置不同。
对于其他数据集,如扑克手数据集,TabNet 据称以相当大的优势击败了其他方法。我还没有花太多时间在这上面,但是每个人当然都被邀请在各种数据集上尝试超参数优化的 TabNet!
结论
TabNet 是一个有趣的架构,似乎有希望用于表格数据分析。它直接对原始数据进行操作,并使用顺序注意机制来为每个示例执行显式特征选择。这个属性也给了它一种内置的可解释性。
我试图通过编写一些包装代码来使 TabNet 更容易使用。下一步是在大范围的数据集上将它与其他方法进行比较。
如果你有兴趣,请在你自己的数据集上试一试,或者发送拉请求,帮助我改进界面!
用 Python 模拟城市冠状病毒流行
城市为流行病做好准备了吗?
最近在中国爆发的 2019-nCoV 武汉冠状病毒已经震惊了金融市场和整个经济体,并适时引发了全球普通民众的恐慌。2019 年 1 月 30 日,nCoV 甚至被世界卫生组织(世卫组织)指定为全球卫生紧急事件。在撰写本文时,尚未发现任何经医学研究标准验证的特定治疗方法。此外,一些关键的流行病学指标,如基本再生数(一个患病个体感染的平均人数)仍然未知。在我们这个前所未有的全球连通性和流动性的时代,由于小世界网络的影响,这种流行病是全球范围内的主要威胁。人们可以推测,如果 2020 年发生全球灾难性事件(大致定义为> 1 亿人伤亡),最有可能的原因恰恰是某种疫情——不是核灾难,也不是气候灾难,等等。世界范围的快速城市化进一步加剧了这种情况,我们人口密集的动态城市变成了疾病传播网络中的传播节点,从而变得极其脆弱。
在本帖中,我们将讨论当流行病袭击一个城市时会发生什么,应该立即采取什么措施,以及这对城市规划、政策制定和管理有什么影响。我们将把埃里温市作为我们的案例研究,并将在 Python 中对冠状病毒在城市中的传播进行数学建模和模拟,观察城市移动模式如何影响疾病的传播。
城市流动性
有效、高效和可持续的城市交通对于现代城市的运行至关重要。已经证明直接影响城市的宜居性和经济产出。但是,一旦发生疫情,就会火上浇油,放大和传播疾病的蔓延。
因此,让我们先来看看埃里温统一笛卡尔网格上聚集的始发地-目的地( OD )交通网络,以了解该城市交通模式的空间结构:

此外,如果我们观察网格单元的总流入量,我们会看到一个或多或少的单中心空间组织,其中一些具有高日流入量的单元位于中心之外:

现在,想象一场流行病在城市的一个随机地点爆发。会如何传播?可以做些什么来遏制它?
流行病建模
为了回答这些问题,我们将建立一个简单的房室模型来模拟传染病在城市中的传播。随着流行病的爆发,其传播动态会发生显著变化,这取决于最初感染的地理位置及其与城市其他地方的连接性。这是从最近关于城市人口流行病的数据驱动研究中获得的最重要的见解之一。然而,正如我们将在下文进一步看到的,各种结果要求采取类似的措施来遏制疫情,并在规划和管理城市时考虑到这种可能性。
由于运行基于个体的流行病模型是具有挑战性的,并且我们的目标是展示城市中流行病传播的一般原理,而不是建立一个精确校准的流行病模型,我们将遵循自然杂志文章中描述的方法,根据我们的需要修改经典的 SIR 模型。
该模型将人口分为三个部分。对于在时间 t 的每个位置 i ,三个隔间如下:
- Si,t :尚未感染或易感该疾病的个体数。
- Ii,t :感染疾病并能将疾病传播给易感人群的个体数量。
- Ri,t :由于康复或死亡,已经被感染然后从感染组中移除的个体数量。这一群体中的个人没有能力再次感染疾病或将感染传播给他人。
在我们的模拟中,时间将是一个离散变量,因为系统的状态是在每天的基础上建模的。在时间 t 在位置 j 的完全易感人群中,爆发发生的概率为:

其中 βt 为第 t 天的传输速率; mj,k 反映从位置 k 到位置 j 的移动性, xk,t 和 yk,t 分别表示 t 日在位置 k 和位置 j 的感染和易感人群比例,由 xk,t = Ik,t / Nk 给出然后,我们继续模拟一个随机过程,将疾病引入完全易感人群的位置,其中 Ij,t+1 是一个概率为 h(t,j) 的伯努利随机变量。
一旦在随机地点引入感染,疾病在这些地点传播,并由旅行的个人在其他地点携带和传播。这就是由 OD 流量矩阵表征的城市流动模式发挥关键作用的地方。
此外,为了正式确定疾病是如何由感染者传播的,我们需要基本繁殖数、 R0 。定义为 R0 = βt / γ 其中 γ 为恢复率,可以认为是感染个体与易感人群接触后二次感染的预期次数。在撰写本文时,武汉冠状病毒的基本繁殖数估计为 1.4 至 4。我们假设最坏的情况是 4。但是,我们应该注意,它实际上是一个随机变量,报告的数字只是预期的数字。为了让事情变得更有趣一点,我们将在每个位置使用不同的 R0 来运行我们的模拟,从一个良好的候选分布 Gamma 中抽取,平均值为 4:

我们现在可以进入模型动力学:

其中 βk,t 为 t 日 k 处的(随机)传输速率, α 为表示模式份额或城市公共交通与私人小汽车出行方式强度的系数。
上述等式中描述的模型动态非常简单:在第天 t+1 在位置 j 处,我们需要从易感人群 Sj,t 中减去在位置 j 内被感染的人的比例(第一等式中的第二项)以及从城市中其他位置到达的被感染的人的比例,用他们各自的传染率 βk,t 进行加权(第一等式中的第三项由于总人口 Nj = Sj + Ij + Rj ,我们需要将减去的部分移到感染组,同时也将恢复的部分移到 Rj,t+1 (第二个和第三个方程)。
模拟设置
在此分析中,我们将使用从当地拼车公司 gg 提供的 GPS 数据中获得的典型日的聚合 OD 流量矩阵,作为埃里温市移动模式的代表。接下来,我们需要每个 250×250m 格网单元中的人口数,我们通过按比例缩放提取的流量数来近似计算,以便不同位置的总流入量总计约为埃里温 110 万人口的一半。这实际上是一个大胆的假设,但是由于改变这个部分产生了非常相似的结果,我们将坚持它。
减少公共交通?
在我们的第一次模拟中,我们将想象一个以可持续公共交通为主导的未来城市交通,其中 α=0.9 :

我们看到受感染的人口比例迅速攀升,在大约第 8-10 天达到疫情高峰,几乎 70%的人口感染了,而只有一小部分人(~10%)从疾病中康复。接近第 100 天时,当疫情消退时,我们看到康复个体的比例达到惊人的 90% !现在让我们看看将公共交通出行的强度降低到类似于 α = 0.2 的水平对缓解疫情传播是否有任何作用。这既可以解释为采取激烈的措施减少城市流动性(例如,通过发布宵禁)也可以解释为增加私家车出行的比例以减少出行期间感染的机会。

我们看到了疫情的高峰是如何在第 16 天和第 20 天之间到来的,其中的感染群体(约 45%)要小得多,而康复的人数是前者的两倍(约 20%)。在疫情接近尾声时,易感个体的比例也增加了一倍(约 24%对约 12%),这意味着更多的人逃脱了疾病。不出所料,我们看到引入戏剧性的措施来暂时降低城市流动性对疾病传播动态有很大影响。
隔离热门地点?
现在,让我们看看另一个直观的想法,完全切断几个关键的热门地点是否有预期的效果。要做到这一点,让我们选择与高 1%的流动性流量相关的位置,

而完全阻断了所有进出这些地点的人流,有效地在那里建立了隔离制度。从图中我们可以看出,在埃里温,这些位置大多位于市中心,另外两个位置是两个最大的购物中心。选择一个适中的 α = 0.5 ,我们得到:

在疫情高峰期,我们看到的感染者比例甚至更小(~35%),最重要的是,我们看到在疫情接近尾声时,大约一半的人口仍然易感,有效地避免了感染!
这是一个小动画,展示了公共交通高分担率场景的动态变化:

结论
绝不是声称精确的流行病模型(或者甚至任何流行病学的基本知识之外的实质性知识),我们在这篇文章中的目的是获得关于传染病爆发期间网络效应如何在城市环境中发挥作用的第一手见解。随着人口密度、流动性和动态性的不断增加,我们的城市越来越容易受到“黑天鹅”的影响,也越来越脆弱。既然你死了就不能去取咖啡,如果没有有效和高效的危机处理能力和机制,智能和可持续发展的城市将毫无意义。例如,我们看到在关键地点引入隔离制度,或采取严厉措施遏制流动性,在这种健康危机期间可能是有用的。然而,一个更重要的问题是如何实施这些措施,同时尽量减少对城市功能及其经济的损害和损失?
此外,传染病的确切流行传播机制仍然是一个活跃的研究领域,该领域的进展将必须传达给城市规划、政策制定和管理部门,并融入其中,以使我们的城市更加安全和抗脆弱性。
附:点击阅读原文。
上述模拟的代码:
import numpy as np
# initialize the population vector from the origin-destination flow matrix
N_k = np.abs(np.diagonal(OD) + OD.sum(axis=0) - OD.sum(axis=1))
locs_len = len(N_k) # number of locations
SIR = np.zeros(shape=(locs_len, 3)) # make a numpy array with 3 columns for keeping track of the S, I, R groups
SIR[:,0] = N_k # initialize the S group with the respective populations
first_infections = np.where(SIR[:, 0]<=thresh, SIR[:, 0]//20, 0) # for demo purposes, randomly introduce infections
SIR[:, 0] = SIR[:, 0] - first_infections
SIR[:, 1] = SIR[:, 1] + first_infections # move infections to the I group
# row normalize the SIR matrix for keeping track of group proportions
row_sums = SIR.sum(axis=1)
SIR_n = SIR / row_sums[:, np.newaxis]
# initialize parameters
beta = 1.6
gamma = 0.04
public_trans = 0.5 # alpha
R0 = beta/gamma
beta_vec = np.random.gamma(1.6, 2, locs_len)
gamma_vec = np.full(locs_len, gamma)
public_trans_vec = np.full(locs_len, public_trans)
# make copy of the SIR matrices
SIR_sim = SIR.copy()
SIR_nsim = SIR_n.copy()
# run model
print(SIR_sim.sum(axis=0).sum() == N_k.sum())
from tqdm import tqdm_notebook
infected_pop_norm = []
susceptible_pop_norm = []
recovered_pop_norm = []
for time_step in tqdm_notebook(range(100)):
infected_mat = np.array([SIR_nsim[:,1],]*locs_len).transpose()
OD_infected = np.round(OD*infected_mat)
inflow_infected = OD_infected.sum(axis=0)
inflow_infected = np.round(inflow_infected*public_trans_vec)
print('total infected inflow: ', inflow_infected.sum())
new_infect = beta_vec*SIR_sim[:, 0]*inflow_infected/(N_k + OD.sum(axis=0))
new_recovered = gamma_vec*SIR_sim[:, 1]
new_infect = np.where(new_infect>SIR_sim[:, 0], SIR_sim[:, 0], new_infect)
SIR_sim[:, 0] = SIR_sim[:, 0] - new_infect
SIR_sim[:, 1] = SIR_sim[:, 1] + new_infect - new_recovered
SIR_sim[:, 2] = SIR_sim[:, 2] + new_recovered
SIR_sim = np.where(SIR_sim<0,0,SIR_sim)
# recompute the normalized SIR matrix
row_sums = SIR_sim.sum(axis=1)
SIR_nsim = SIR_sim / row_sums[:, np.newaxis]
S = SIR_sim[:,0].sum()/N_k.sum()
I = SIR_sim[:,1].sum()/N_k.sum()
R = SIR_sim[:,2].sum()/N_k.sum()
print(S, I, R, (S+I+R)*N_k.sum(), N_k.sum())
print('\n')
infected_pop_norm.append(I)
susceptible_pop_norm.append(S)
recovered_pop_norm.append(R)
用 LSTM 网络模拟波动时间序列
这是一个长短期记忆网络(LSTM)如何被用来模拟一个不稳定的时间序列的例子。
每年的降雨量数据可能很不稳定。不同于气温,气温通常显示出一个明显的季节趋势,降雨量作为一个时间序列是相当不稳定的。在爱尔兰,夏季和冬季一样多雨并不罕见。

资料来源:pixabay.com
这是爱尔兰纽波特 1959 年 11 月的降雨模式的图解说明:

作为一个序列神经网络,LSTM 模型在解释时间序列的波动性方面可以证明是优越的。
使用 Ljung-Box 检验,小于 0.05 的 p 值表明该时间序列中的残差呈现随机模式,表明波动性显著:
>>> res = sm.tsa.ARMA(tseries, (1,1)).fit(disp=-1)
>>> sm.stats.acorr_ljungbox(res.resid, lags=[10])
(array([78.09028704]), array([1.18734005e-12]))
数据操作和模型配置
正在讨论的数据集包括 722 个月的降雨数据。爱尔兰新港的降雨量数据来源于英国气象局网站。
选择 712 个数据点用于训练和验证目的,即建立 LSTM 模型。然后,将过去 10 个月的数据用作测试数据,与 LSTM 模型的预测进行比较。
以下是数据集的一个片段:

然后形成数据集矩阵,以便根据过去的值回归时间序列:
# Form dataset matrix
def create_dataset(df, previous=1):
dataX, dataY = [], []
for i in range(len(df)-previous-1):
a = df[i:(i+previous), 0]
dataX.append(a)
dataY.append(df[i + previous, 0])
return np.array(dataX), np.array(dataY)
然后用 MinMaxScaler 对数据进行归一化处理:

随着先前的参数被设置为 120,训练和验证数据集被创建。作为参考, previous = 120 表示模型使用从 t — 120 到 t — 1 的过去值来预测时间 t 的降雨量值。
选择之前的参数需要反复试验,但选择了 120 个时间段,以确保捕捉到时间序列显示的波动性或极值。
import tensorflow as tf
from tensorflow.keras import layers
from tensorflow.keras.layers import Dense
from tensorflow.keras.layers import LSTM# Training and Validation data partition
train_size = int(len(df) * 0.8)
val_size = len(df) - train_size
train, val = df[0:train_size,:], df[train_size:len(df),:]# Number of previous
previous = 120
X_train, Y_train = create_dataset(train, previous)
X_val, Y_val = create_dataset(val, previous)
然后输入被整形为样本、时间步长、特征的格式。
# reshape input to be [samples, time steps, features]
X_train = np.reshape(X_train, (X_train.shape[0], 1, X_train.shape[1]))
X_val = np.reshape(X_val, (X_val.shape[0], 1, X_val.shape[1]))
模型训练和预测
该模型跨 100 个时期进行训练,并且指定了 712(等于训练和验证集中的数据点的数量)的批量大小。
# Generate LSTM network
model = tf.keras.Sequential()
model.add(LSTM(4, input_shape=(1, previous)))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam')
history=model.fit(X_train, Y_train, validation_split=0.2, epochs=100, batch_size=448, verbose=2) # list all data in history
print(history.history.keys())
# summarize history for accuracy
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.title('model loss')
plt.ylabel('loss')
plt.xlabel('epoch')
plt.legend(['train', 'val'], loc='upper left')
plt.show()
这是培训与验证损失的关系图:

还生成了预测降雨量与实际降雨量的关系图:
# Plot all predictions
inversetransform, =plt.plot(scaler.inverse_transform(df))
trainpred, =plt.plot(trainpredPlot)
valpred, =plt.plot(valpredPlot)
plt.xlabel('Days')
plt.ylabel('Rainfall')
plt.title("Predicted vs. Actual Rainfall")
plt.show()

将预测结果与基于平均方向精度(MDA)、均方根误差(RMSE)和平均预测误差(MFE)的验证集进行比较。
>>> def mda(actual: np.ndarray, predicted: np.ndarray):
>>> """ Mean Directional Accuracy """
>>> return np.mean((np.sign(actual[1:] - actual[:-1]) == np.sign(predicted[1:] - predicted[:-1])).astype(int))
>>> mda(Y_val, predictions)0.9090909090909091>>> from sklearn.metrics import mean_squared_error
>>> from math import sqrt
>>> mse = mean_squared_error(Y_val, predictions)
>>> rmse = sqrt(mse)
>>> print('RMSE: %f' % rmse)RMSE: 49.99>>> forecast_error = (predictions-Y_val)
>>> forecast_error
>>> mean_forecast_error = np.mean(forecast_error)
>>> mean_forecast_error-1.267682231556286
- 丙二醛: 0.909
- RMSE:49.99 美元
- MFE: -1.26
根据测试数据进行预测
虽然验证集的演示结果相当不错,但只有通过将模型预测与测试(或看不见的)数据进行比较,我们才有理由相信 LSTM 模型具有预测能力。
如前所述,过去 10 个月的降雨量数据用作测试集。然后,使用 LSTM 模型预测未来 10 个月,并将预测值与实际值进行比较。
向下到 t-120 的先前值用于预测时间 t 的值:
# Test (unseen) predictions
Xnew = np.array([tseries.iloc[592:712],tseries.iloc[593:713],tseries.iloc[594:714],tseries.iloc[595:715],tseries.iloc[596:716],tseries.iloc[597:717],tseries.iloc[598:718],tseries.iloc[599:719],tseries.iloc[600:720],tseries.iloc[601:721]])
获得的结果如下:
- 丙二醛: 0.8
- RMSE:49.57 美元
- MFE: -6.94
由于过去 10 个月的平均降雨量为 148.93 毫米,预测精度表现出与验证集相似的性能,相对于测试集计算的平均降雨量,误差较低。
结论
在本例中,您看到了:
- 如何准备用于 LSTM 模型的数据
- LSTM 模型的构建
- 如何检验 LSTM 预测的准确性
- 使用 LSTM 建模波动时间序列的优势
非常感谢您的宝贵时间,这个例子的相关库可以在这里找到。
你也可以在 michael-grogan.com找到更多我的数据科学内容。
免责声明:本文是在“原样”的基础上编写的,没有任何担保。它旨在提供数据科学概念的概述,不应被解释为专业建议。本文中的发现和解释是作者的观点,不以任何方式得到 Metéire ann 的认可或隶属于 Metéire ann。
现代 CI/CD 管道:带有 AWS Lambda 无服务器 Python 函数和 API 网关的 Github 动作
使 web 应用程序开发和部署现代化

云已经存在,越来越多的开发人员正在寻找有效整合云的方法。无论您是一家认识到本地硬件和本地机器局限性的初创企业,还是一家对如何缓慢卸载本地工作负载感到好奇的大型企业,本教程都很有见地。我描述了第 1 阶段的 AWS 架构,包括 Github、API Gateway 和 AWS Lamba python 函数。这是向开发人员展示 AWS 云采用学习曲线的初始教程。
大纲:
- 概念建筑
- 目的和目标
- 如何设置
- 自动气象站 Lambda 的局限性和问题
- 想法和结论
概念架构:

架构图
上面的架构描述了使用 AWS Lambda 部署 python 函数的基本 CI/CD 管道。上面提到的开发人员可以使用 git 将他们的 git 存储库拉入和推送至 github。我们配置了 github actions YAML 文件,一旦一个拉请求被合并到主分支,它就会自动更新 AWS Lambda 函数。API 网关代表一个触发器,它运行 AWS Lambda python 函数并返回结果。通过这种方式,数据科学家(或分析师、前端开发人员或其他开发人员)可以快速简洁地触发和访问结果。
N 注意:AWS Lambda 函数存在于开发人员无法访问的计算资源 AWS Lambda 中。
目的和目标
这个架构代表了团队为应用程序开发和部署构建健壮的 CI/CD 管道的有效方式。尽管这种架构相对于成熟的 web 应用程序来说是“不完整的”,但这种设计可以代表构建 web 应用程序的第一阶段。对于任何对卸载本地计算资源感兴趣的人来说,AWS Lambda 无服务器功能是以经济高效的方式利用云的有效方式(AWS Lambda 功能是 AWS always free 层的一部分)。很多时候,开发团队为应用程序部署(或迁移)设计了一个崇高的基于云的架构,却失败了。进行概念验证并慢慢引入云是更谨慎的做法。
如何设置
主要挑战是理解 YAML 文件并正确格式化在 AWS Lambda 中执行的“main.py”文件。要设置一个新的工作流,开发人员可以在其中为功能部署配置 YAML 文件,请在 github 存储库中单击“actions”。这将提供创建新工作流或从与部署架构一致的现有工作流中进行选择的说明。我创建了一个默认的工作流,然后搜索其他工作流来找到一个部署到 AWS Lambda 的模板(但是可能有一种更简单的方法来利用 AWS Lambda 的预配置工作流)。
注意:第 57 行实际上是将 github repo 压缩到一个名为“test-dep-dev”的 zip 文件中。理想情况下,要组织一个 Lambda 函数部署,我建议在 repo 中创建一个文件夹,并压缩该文件夹以进行部署。例如,您可以创建一个名为“Lambda _ Function _ Scrapping _ Data”的文件夹,其中包含部署您的函数所需的所有依赖项。第 57 行将类似于“zip -r test-dep-dev.zip。/Lambda _ functions _ screwing _ Data "
一旦您配置了 YAML 文件,请检查 AWS Lambda 函数页面,查看该函数是否已经更新。为了排除故障,在 AWS Lambda 函数中创建一个测试用例。我首先面临的问题是 python 返回值的语法不正确(这里有一些文档可能对有帮助)。下面是一个 AWS Lambda Python 函数的简单示例。响应格式是最重要的。
注意:在 AWS Lambda 函数中,处理程序被定义为 file_name.function_name。例如,如果文件名为 main.py,函数为 my_serverless_function,则句柄应定义为 main.my_serverless_function。
这不是重新创建轮子,而是演示如何为 AWS Lambda 函数创建 API 网关的精彩视频:
AWS Lambda 的局限性和问题以及可能的替代方案
在决定使用 AWS Lambda 之前,考虑其局限性是很重要的。AWS Lambda 函数将在 15 分钟后超时,这已经足够了,但是对于更复杂的企业级工作负载,这可能还不够。更重要的是,你打包的函数被限制为 250mb 解压缩和 50mb 压缩,这包括包的大小、编码的函数和其他依赖项。这些只是特别适用于我正在处理的用例的一些限制。你可以在这里找到其他限制。
鉴于 AWS Lambda 本质上是一个运行容器化功能的共享计算实例,为了更加灵活,您可以提供一个 EC2 实例。显然,这导致了更高的成本。如果您担心高可用性,我不确定 SLA 为 AWS Lambda 提供了什么,但通常情况下,AWS 计算实例用户应该假设 90%的可用性。
结论
对 AWS Lambda 和无服务器函数有一点学习曲线,但是考虑到它们在大多数现代 web 应用程序开发中的普遍适用性,我认为这是值得的时间投资。如果你正在做一个项目,并且想要结合一些云,从过程和成本的角度来看,AWS Lambda 是相当值得的。最终用户可以轻松地调用端点并检索数据。似乎有大量的文档和教程可以解决几乎所有的用例。总体来说,我会推荐 AWS Lambda。为了提高计算实例的灵活性,我推荐 Oracle 云计算实例。它们是免费的(不到 1 OCPU),但是文档可能很难找到和理解。
子堆栈:
我最近创建了一个子栈来学习如何用 Python 编程和解决 LeetCode 问题。
现代探索性数据分析
抛弃旧的方式。未来是自动化的。
四个自动化 EDA 库述评

照片由亚历山德罗·埃尔贝塔在 Unsplash 拍摄
EDA 代表探索性数据分析,可能是任何数据科学项目中最重要但最容易被忽视的步骤。
我的目标是与您分享一些工具,这些工具可以在探索数据时将您的工作效率提升 10 倍。
语境
正是在探索阶段,数据科学家在进入数据清理、特征工程和建模之前,与数据集保持密切联系。
然而,我们中有太多的人匆忙完成数据探索,过早地跳到下一步。
过去,你必须为 EDA 编写自己熟悉的函数,在这个领域已经有了很多重新发明轮子和/或复制粘贴的方法。但是随着自动机器学习等先进技术的出现,很明显 EDA 也可以从自动化中受益。
在这篇文章中,我首先展示了一个用老式方法做 EDA 的例子。然后,我回顾了以下致力于使 EDA 尽可能简单高效的软件包:
- 熊猫-剖析(python)
- 摘要工具(R)
- 探索(R)
- 数据女仆(R)
✨要求运行下面的例子
我们将使用的数据集是:
安装 R 包:
install.packages(c("summarytools", "explore" "dataMaid"))
安装 python pandas-profiling包:
pip install pandas-profiling
要使用 Jupyter 笔记本中的软件包,我们还需要从命令行运行以下命令:
jupyter nbextension enable --py widgetsnbextension
如果您更喜欢 Jupyter Lab,命令改为:
jupyterlab: jupyter labextension install @jupyter-widgets/jupyterlab-manager
老办法
在过去,人们要么使用可视化库,要么使用 pandas 内置的可视化功能。让我们看几个例子。
import pandas as pd
import numpy as np data = pd.read_csv('insurance.csv')
👉head功能
这可能是每个数据科学笔记本中的第一个函数调用,用于查看数据:
**data**.head()

👉describe功能
这个函数为我们提供了每个变量的一些汇总统计数据。我喜欢把数字变量和分类变量分成两个独立的调用:
data.describe(include=np.number)

data.describe(include=np.object)

👉和熊猫一起策划
两种最常见的单向分布图是分类变量的条形图和连续变量的直方图。两者都很容易直接从熊猫上使用:
data['region'].value_counts().plot.barh(title='Region')

data['age'].plot.hist(title='Age distribution')

最后,我们有普遍存在的相关矩阵:
data.corr().style.background_gradient(cmap='coolwarm').set_precision(2)

这就结束了我们对老派探索性数据分析的快速回顾。如你所见,我们必须明确地写下每一步。如果列数增加,这个过程要么变得乏味,要么我们必须写下自己的函数来实现自动化。
在这篇文章的其余部分,我回顾了现有的成功的可以为我们执行自动化的包。
📦摘要工具(R)
summarytools可能是获取数据集中每一列单向信息的最简单方法。
library(summarytools)
library(readr) df <- read_csv("insurance.csv") view(dfSummary(df))
输出直接显示在 RStudio 的浏览器中,但是您也可以使用file参数将其保存为 html 文件。看一看:

虽然不全面,但我发现这个输出在很小的空间内打包大量有用的信息时非常有效。这个包还有一些其他的单变量统计和交叉制表的函数,但是在我看来真正的精华是上面的dfSummary函数。
📦探索(R)
explore套餐相当不可思议。只需一行代码,您就可以得到一个完整的闪亮的应用程序,让您交互式地探索您的数据。让我们看一个 titanic 数据集的例子。下面,我展示了两个变量的单向和双向图,一个分类变量(性别)和一个连续变量(年龄)。我们立刻发现女性更有可能存活,年轻人也更有可能存活:
library(explore) df <- read_csv("titanic_train.csv") explore(df)


另一个很好的功能是“解释”选项卡,在这里我们可以直观地看到一个简单的决策树,以建立我们对预测变量的直觉:

📦数据助理(R)
library('dataMaid') makeDataReport(df,
render = FALSE,
file = "output_file.Rmd",
replace = TRUE)
dataMaid可能是目前为止最全面的选项。您可以输出一个 Rmd 文件,然后从 RStudio 将其编织成 HTML 报告,或者您可以使用render=TRUE为您输出一个漂亮的 Word 文档!
报告以一个汇总表开始:

后面是每个变量的一个部分。这是 bmi 的部分,根据上面的汇总表,它可能有问题,结果是潜在异常值的问题:

makeDataReport函数有很多参数来调整输出。总的来说,我对这个包印象非常深刻。
📦熊猫简介(python)
最后但同样重要的是,pandas-profiling可能是 python 世界中自动 EDA 最流行的包。您可以在笔记本中使用它,并且由于奇妙的ipywidgets库,您可以获得一个交互式的非常丰富的可视化来探索您的数据。
from pandas_profiling import ProfileReport profile = ProfileReport(data,
title='Pandas Profiling Report',
html={'style':{'full_width':True}}) profile.to_widgets()
首先,您将获得数据的概述:

以及一些以前的包没有捕捉到的警告:

我们还会得到每个变量的详细报告:


我们甚至可以切换细节进行更深入的探索:

变量之间的相互作用图也可用:

其他选项卡(相关性、缺失值和样本)是您所期望的。我邀请你来玩这个奇妙的包。
✅裁决
我希望我说服你停止使用自制的功能或昂贵的可视化工具,改用开源的专用包。
上面描述的 4 个包是我在真实情况下进行数据探索的常用工具。我从非交互式的开始,首先是summarytools,然后是dataMaid。然后我和explore一起玩耍,最后,我和pandas-profiling在一起的时间最多。
这就是我如何尽可能熟悉我的数据,而不浪费时间编写模板代码。根据我得出的发现,我将使用其他软件包进行异常检测、插补、特征工程等。这些都将是未来文章的主题。感谢阅读!
利用现代数据统一方法实现分析基础架构的现代化
协作式现代数据统一方法如何使企业能够随着数据量的增长而扩展。
传统的数据统一过程围绕着 ETL(提取、转换、加载)。ETL 在 20 世纪 70 年代变得越来越流行,此后一直是构建数据仓库的核心概念。从数据源中提取数据,将其转换成适合数据仓库分析的格式或结构。然而,不断增长的数据量开始影响数据仓库和 ETL 过程的性能,这就是为什么我们现在知道“数据湖用例”。当数据量增加时,转换数据的过程成为构建合适的数据仓库的瓶颈。许多企业开始存储和处理非结构化或原始数据,而不是用 ETL 来转换数据。如果我们将传统 ETL 的优势与从“数据湖用例”中学到的经验结合起来会怎么样?这对数据统一方法意味着什么?
ETL 提供的主要好处
结构化和处理过的数据是 ETL 提供的最大好处。尽管开发和维护的成本很高,但是在大多数情况下,当用作报告的来源时,一个经过深思熟虑的 ETL 过程的输出被认为是可靠的和“高质量的”。大多数 ETL 过程是为业务专业人员量身定制的,基于业务需求,ETL 是为了将正确的数据提供给正确的用户而构建的。安全性可以说是成熟的,您将只能访问您需要的内容,而写时架构处理创建了一个基于 SQL 的结构,便于业务专业人员出于报告目的进行解释。

阿德里安·维森巴赫在 Unsplash 上拍摄的照片
ETL 缺什么?
写模式是 ETL 的标准,但是,当您想要存储和处理非结构化数据时,这将成为一个问题。当从“数据湖”中提取数据时,读取模式通过将数据应用于模式来逆转这一过程,这种方法的用例变得越来越流行。当使用读取模式时,您将数据原样加载到“数据湖”中。通过这样做,您可以集中数据,并开始在所有数据的基础上构建部分业务逻辑,而 ETL 需要创建一个“超级模式”或构建预定义的主题区域来获取数据,这又需要大量的前期建模工作。
为什么不改用数据湖呢?
随着基于 SQL 的数据仓库技术的现代化,大多数数据湖用例都被涵盖了。数据仓库计算能力和存储的存储成本下降,新一代 MPP(大规模并行处理)数据库充分利用了云的可扩展性。因此,存储和处理大型数据集不再需要大量的前期投资,您可以从小规模开始,然后随着增长而扩展。谈到功能,例如在一个雪花数据仓库中,您现在可以以一种变体类型存储半结构化或原始数据,并在稍后阶段应用该模式。这曾经是在数据湖中存储数据的主要原因,现在雪花设法处理大多数类型的非结构化数据,并能够使用 SQL 查询它们,以及所有可用的基于 SQL 的结构化数据。从物理上转换到成熟的数据湖解决方案需要复杂的映射、转换,以及结构化和半结构化数据的基础设施组件之间的集成,因此近年来许多这些昂贵的数据湖迁移项目都失败了。我们现在已经到了现代云分析基础架构可以促进整个“分析工作负载”并服务于大多数数据湖用例的地步。
现代数据统一
建立一个现代化的分析架构始于收集所有数据源并将数据加载到中央数据仓库。当我们使用现代云数据仓库时,我们现在可以利用按需计算能力进行即时转换。当您运行报表时,将在数据仓库上执行带有转换逻辑的 SQL 查询。我们现在可以开始在所有可用数据的基础上定义我们的视图,而不是运行繁重的 ETL 过程,这些过程由于性能影响而被安排在工作时间之外(每天早上都有一个惊喜的元素来查看数据是否被真正加载)。
这是现代数据统一过程中协作的起点。与业务专家一起,现在可以从一组基本的视图中确定业务需求和逻辑。通过使业务专业人员能够在早期阶段看到实际数据,您可以利用他们对数据模型和业务逻辑的隐性知识。他们的参与不仅有助于他们了解“数据驱动型公司的发展”,还将最终让他们明白他们的自助服务工作如何能够增强报告。这与传统的数据统一过程完全不同,传统的数据统一过程要求企业通过功能设计规范来交流他们的数据需求。在这一过程中,商业智能顾问将始终必须翻译和构建基于主题的管道,该管道过滤、聚集和清理数据,同时知道这可能会在数据仓库内创建一个筒仓,在其上有预先确定的不灵活的报告子集。

由 You X Ventures 在 Unsplash 上拍摄的照片
分析基础设施的现代化不仅需要云技术专业知识,还需要方法的改变。让高级业务专家参与基于 SQL 的技术是增强数据文化、为模型开发获取宝贵输入的直接方法,并最终有助于创建协作的数据统一环境。这样做的工具已经存在,但是我们需要在“模式最后”中建立信任。当业务专家从一个集中的、单一的真实来源获得数据时,开发和扩展业务逻辑最终可以与数据量的增长同步进行。
现代化大型机应用程序
从 COBOL 迁移并添加 AI
迈克尔·克鲁彻和蒙特·兹韦本

几十年来,大型机一直是银行、保险、医疗保健、零售以及公共部门等行业的支柱。所有这些行业都需要高负载、低延迟的事务处理。大型机旨在为任务关键型应用程序提供高可用性,在这种情况下,停机时间成本极高或者根本无法接受。
但是,随着企业努力使用分布式计算和公共云进行转型,大型机应用程序正在被抛在后面。在很大程度上,这是因为人们认为现代化大型机应用程序势不可挡,甚至是不可能的。
这种看法是错误的。新技术降低了大型机现代化的障碍,并提供了几个关键优势,包括降低成本、业务敏捷性和释放新的人工智能应用功能。让我们来研究一下这些收获。

成本降低
对大型机驱动的应用程序进行现代化改造,以使用廉价的商用硬件,从而显著降低大型机的前期资本支出以及持续许可和维护成本。迁移到云无疑提供了独特的优势,但许多具有大型机工作负载的客户也碰巧拥有未充分利用的本地集群,这些集群可用于承载现代化的大型机工作负载。导致容器化解决方案的大型机应用程序现代化通常可以提供两方面的最佳效果。例如,Kubernetes 方法可以提供一种途径来抽象出底层基础设施,并将其部署在本地或云中,在两种选择之间充当桥梁,只需很少或不需要修改。

业务敏捷性
将大型机工作负载迁移到云中可以提高业务灵活性。容器化使应用程序更加敏捷,因为它允许应用程序快速部署和调整大小,以适应不断变化的基础设施需求。它既可以在云中利用,也可以在内部利用。容器化内部部署应用程序使客户能够分期偿还其现有基础设施投资,同时提供快速的云之路。云提供了随用随付的定价和对各种计算基础设施的轻松访问,而不需要 IT 团队支持大量潜在的硬件配置。
从大型机中迁移还提供了一条通向更现代的数据库的道路,使应用程序能够横向扩展以容纳更多的数据。它还通过将现有数据和工作负载分布在无限多的商用服务器上,增强了性能和并发性。Splice Machine(我是该公司的联合创始人和首席执行官)就是这种横向扩展 SQL RDBMS 的一个例子。
此外,大型机开发人员和操作员技能也很稀缺。在更现代的编程语言和基础设施上,可以更容易地为任务关键型应用程序找到合格的员工。
容器化、横向扩展数据库和更大的人才库都有助于提高现代化大型机应用程序的灵活性。

新功能
第三,也是将应用程序从大型机中迁移出来的最重要的原因,是由于人工智能,它开启了全新的业务成果。现代环境可以支持这种人工智能。基于大型机的环境不能。
多年来为企业提供良好服务的任务关键型应用程序正在通过数据和新软件功能实现彻底的现代化。例如,保险系统不再简单地管理保单和索赔。他们需要使用新的数据源来支持人工智能和机器学习技术,这些技术可以对风险进行评分,检测欺诈或洗钱,并了解投保人可能感兴趣的其他选项。他们必须在几毫秒内完成这一切。
大型机迁移使老牌企业能够跟上甚至超越其风险投资支持的新贵竞争对手,这些竞争对手自诞生以来就使用数据和人工智能。应用需要高性能和可扩展、一致、灵活的数据访问来完成工作。当公司将他们的大型机基础设施换成一个融合平台(如 Splice Machine)时,他们会立即加速他们的遗留应用程序。它们使他们能够立即处理运营记录系统工作负载、分析报告要求以及基于数据库内机器学习的自动化操作。他们立刻变得聪明,能够根据预测采取行动。
这个数据平台与基础设施无关。它可以直接部署,也可以部署在由 Kubernetes 编排的容器中。它可以在内部或云上的商用集群上进行调配。它具有处理极其广泛的数据库工作负载的独特能力,无需专门的数据模式和数据移动,这使它成为大型机应用程序迁移的理想数据库。最重要的是,它为机器学习提供了一条前进的道路,这是应用程序以前所没有的。
将应用程序从大型机中迁移出来的方法
由于系统的复杂性和多种选择,没有一种单一的迁移途径适用于每一家公司。这导致了非常复杂、冗长的迁移规划和执行过程。
我们建议通过问自己两个问题来简化这个过程:
- 您希望通过大型机现代化实现哪些业务成果?
- 对于迁移过程,您愿意花费多少时间、金钱和风险?
时间和成本有效地衡量迁移所需的工作量。有时,时间和成本可以相互交换。例如,如果迁移需要手动工作,您可以部署更多的人来解决问题,或者您可以通过许可合适的软件来自动化工作。
迁移大型机应用程序的选项

编译器辅助的平台重建和数据提取比重写更有前途
完整的应用程序重写
大型机迁移的一种方法是从头开始重写现有的应用程序。在这种情况下,开发团队使用现有大型机应用程序的功能来定义其最低可行的需求。然后,团队用另一种语言重新编写应用程序。

例如,一家公司可能用 Java 重写了一个有 30 年历史的事务性 COBOL 应用程序,使用微服务设计模式,并使用横向扩展的 NoSQL 文档数据库。这提供了立竿见影的敏捷性、横向扩展能力、更大的人才库以及其他优势。它还在编程语言、设计模式、基础设施和数据管理实现方面为他们提供了完全的灵活性,可以在现代环境中使用。就好像应用程序是全新的一样。
明显的缺点(也是很少部署这种方法的原因)是这种灵活性的代价很大。从头开始的应用程序迁移过程(规范、实现、业务逻辑测试等)需要大量的工作,并且经常容易出错。大型开发团队必须重新编写应用程序,调整数据模式,构建新的 ETL,执行功能和性能测试,等等。

工具辅助的需求收集和重写
加速需求收集和实现的另一种方法是使用商业软件工具,这些工具分析现有的大型机应用程序并生成业务规则和编程语言存根。这种方法的一些例子包括 EvolveWare 、 AveriSource 和 UniqueSoft 。
这些工具有时可以减少迁移的时间和成本,但通常会牺牲灵活性和业务成果。工具可能会假设目标语言和基础设施选项,如果开发团队从零开始使用最佳的软件和基础设施,就不会选择这些选项。
此外,由软件工具自动生成的代码通常需要大量的返工。嵌入在大型机应用程序中的业务规则可能无法完全准确地提取出来,而且微妙的问题可能难以检测,修复起来也很昂贵。

从主机中提取数据
一些开发团队不是从头开始重写大型机应用程序,而是通过将数据从大型机中取出并放入现代数据库中来使他们的大型机应用程序现代化。这种方法的一个主要好处是,通常可以识别和卸载资源密集型大型机任务。例如,大型机通常对业务运营负有报告责任。如果这些报告职责需要大量的总基础设施资源,精明的开发团队可以将报告功能迁移到本地或云中的商用基础设施,从而节省大量资金。
这种方法还提供了用人工智能和机器学习为应用提供动力的可能性。采用这种方法的公司包括 Syncsort 和 T2 Ispirer。
这种技术有一个明显的缺点。尽管这通常是朝着正确方向迈出的一步,但这种方法不会导致从大型机完全迁移出去。结果,该公司仍然不得不承担维护大型机的许可成本、维护时间和机会成本—只是为了更少的业务用途。

编译器辅助的大型机迁移
大型机应用程序迁移的最后一个选项是通过理解大型机应用程序的语言和数据管理实现的编译器来完成大型机应用程序的迁移。编译器生成可以编译成目标语言和数据管理实现的中间语言源。
这种方法通常可以快速实现,同时比完全重写成本低得多,并提供显著的业务成果。实施的成本和时间很低,因为实际的转换是高度(但不是完全)自动化的。编译器软件并不试图理解和提取复杂的业务规则:它在较低的层次上工作,创建应用程序逻辑本身的中间表示。将这种方法作为解决方案一部分的公司包括传家宝计算和微焦点。
大型机应用程序编译的大多数实现将保留现有的 RDBMS(几乎总是 DB2 或 Oracle)。但现在有机会跨越传统数据库,在一个横向扩展的 HTAP 数据库上实现应用程序的现代化,该数据库包含原生人工智能。如果您使用这种编译方法,并将 Splice Machine 作为您的目标 DB,您可以在几个月内轻松地将 ML/AI 添加到您的大型机应用程序中。
Splice Machine 最近与一家领先的保险公司合作,将其全球索赔、客户和保单应用程序从传统的本地 DB2 架构迁移到部署在多个云上的分布式 SQL 平台。已有 35 年历史的 PL-I 应用程序(仍然是公司核心业务流程的支柱)已经转换为 Java,现在能够在融合平台上利用 OLAP 和 OLTP 功能。现在,该公司以前由于 ETL 延迟而无法运行的预测模型可以直接注入到实时索赔工作流中。这是因为 OLTP RDBMS 和 AI 模型共同驻留在 Splice Machine 的聚合架构上。

走向
一般来说,我们推荐“拉出数据”或“编译器辅助的”大型机迁移技术,稍微倾向于后者。
“拉出数据”方法可以快速实施,并显著降低大型机利用率,从而降低未来大型机硬件和许可证成本,同时实现全新的功能。“编译器辅助的应用程序迁移”是一个更全面的解决方案,借助合适的合作伙伴工具是有意义的。
正确的方法取决于许多因素,但我们认为这种解决方案的基本要素是具有 ACID 和 ML 功能的横向扩展 SQL 数据库。这样一个平台不仅使公司能够使他们的大型机应用程序现代化,变得敏捷、数据丰富和智能,而且不需要从头重写。将传统应用程序迁移到统一的平台可以降低数据库许可成本,使应用程序能够大规模添加新数据源,统一分析和,并通过机器学习为它们增压,使它们比以往任何时候都更加智能。
要了解更多关于如何使您的大型机应用现代化的信息,请查看我们的网络研讨会,观看编译器辅助的大型机迁移演示。
摩丁-加速你的熊猫笔记本,脚本和图书馆
使用 Modin 扩展 Pandas 工作流,只需更改一行代码

尼古拉斯·霍伊泽在 Unsplash 上拍摄的照片
熊猫简介:
Pandas 是一个开源库,为 Python 语言提供了高性能、易于使用的数据结构和数据分析工具。Pandas 是用 Python 进行真实世界数据分析的基本高级构建块。它有一个更广泛的目标,那就是成为最强大、最灵活的开源数据分析和操作工具。
熊猫库有 40 多个 API,其中一些最常用的是熊猫数据帧、熊猫系列、熊猫绘图、熊猫输入/输出。熊猫图书馆的重要亮点是:
- 一个快速高效的 DataFrame 对象,用于集成索引的数据操作
- 读写数据的工具
- 智能数据对齐和集成处理缺失数据
- 大数据集的基于智能标签的切片、花式索引和子集
- 通过引擎与强大的组聚合或转换数据。
- 数据集的高性能合并和连接
- 时间序列-功能
熊猫图书馆在学术和商业领域被广泛使用,包括金融、神经科学、经济学、统计学、广告、网络分析等等。
Pandas 是一个非常棒的库,可以让数据科学家/数据分析师与数据进行交互。但是与大数据交互需要时间。熊猫主要使用 1 核 CPU 处理指令并返回结果。这里,摩丁出现了,他使用分布式计算来加速熊猫的工作流程。

(来源),摩丁 logo
如何加快熊猫的工作流程:
Modin 是一个早期访问的多处理数据帧库,具有与 pandas 相同的 API,弥补了数据分析和大数据之间的差距。它提供了一种轻松的方式来加速 pandas 笔记本、脚本和库,并管理数据分区和洗牌,以便数据科学家/数据分析师可以有效地与数据进行交互。
四核笔记本电脑的摩丁速度提升了 4 倍。
Modin 加速了熊猫的查询,只需要用户在笔记本上修改一行代码。熊猫只使用一个 CPU 核心来处理指令,其余的核心没有被利用。然而,Modin 利用系统中所有可用的内核来加速 Pandas 的工作流程。

(来源),比较熊猫与摩丁读取 CSV 文件的时间利用率的图表,(图 1)
引擎盖下的摩丁作品:
如上所述,Modin 通过分布式计算加速了 Pandas 查询。它利用了系统的所有核心。对于 4 核系统,下图比较了 Pandas 和 Modin 库的利用率。

(来源),熊猫和摩丁的 CPU 核心利用率,(图 2)
与标准笔记本电脑相比,在拥有更多内核的大型服务器上,Modin 的性能会呈指数级增长。从(图 1)可以看出,加载 18gb 的 CSV 文件只需几秒钟。
安装:
Modin 是开源的,你可以在这里找到 GitHub 库。从 PyPI 安装 Modin:
**pip install modin**
如果您没有安装 Ray 或 Dask ,您将需要安装带有其中一个目标的摩丁:
# Install Modin dependencies and Ray to run on Ray
**pip install modin[ray]**# Install Modin dependencies and Dask to run on Dask
**pip install modin[dask]**# Install all of the above
**pip install modin[all]**
Modin 将自动检测您安装了哪个引擎,并用于调度计算。
实施:
使用 Modin 来加速你的熊猫查询,只需要一行代码,只需要导入 Modin 库。
**import modin.pandas as pd**
摩丁与熊猫的比较:

(图片由作者提供),左 : Pandas,右 : Modin,在 Pandas 和 Modin 中读取 150 MB CSV 文件的编译时间比较
一些限制:
Pandas 是一个拥有大量 API 的大型库,其中一些著名的 API 是 DataFrame、Series 等。Pandas DataFrame 和系列各有 280 多种功能。摩丁并不支持熊猫的所有功能,但它支持最著名的功能。

(来源),熊猫 API 报道,(图 3)
摩丁的开发者想出了在 Kaggle 比赛中最受欢迎的熊猫方法。图 4 中列出了前 20 名。

(来源),Kaggle 最常用的 20 种熊猫方法,(图 4)
他们开始按照流行程度实现分布式计算的功能。
没有在 Modin 中实现的功能,默认为 pandas。如果您尝试使用未在 Modin 中发布的函数,库将使用 Pandas 中该函数的默认实现,方法是将 Modin 数据框转换为 Pandas 数据框,执行操作,然后再转换为 Modin 数据框。

(来源),默认为熊猫在摩丁不支持的功能,(图 5)
结论:
您可以使用 Modin 代替 pandas,因为它可以并行化实现并减少计算时间,即使某些 pandas API 没有并行化,它仍然默认为 Pandas。
摩丁使用雷或达斯克来尽可能地并行化熊猫 API。它旨在作为熊猫的替代物。从长远来看,Modin 计划成为一个支持流行 API(SQL,pandas 等)的数据框架库。)并在各种计算引擎和后端上运行。
参考资料:
[1]熊猫文件,(2020 年 9 月 8 日):https://pandas.pydata.org/
[2]摩丁文献:https://modin.readthedocs.io/en/latest/
[3] PyData YouTube 视频“利用摩丁-德文·彼得森扩展互动熊猫工作流程”,(2019 年 2 月 1 日)
感谢您的阅读
MODIS 植被指数:GEE 方法
气候数据科学
巴西新古土著公园的案例。
自 1999 年首次发射进入轨道以来,中分辨率成像光谱仪 (MODIS)是地球观测中使用的关键传感器之一。它的成功有几个原因:1 至 2 天的重访时间、全球覆盖、广泛的适用性及其衍生产品。仅举几个例子。
在本教程中,您将重点关注 MOD13Q1 产品,该产品由全球范围内 16 天频率的归一化差异植被指数 (NDVI)和增强植被指数 (EVI)组成。你可能已经从 GEE 的介绍中知道了 NDVI,但是在这里你还将探索 EVI 在环境分析中的用途。

女士们先生们,我们正在从太空探索地球。Brian McGowan 在 Unsplash 上拍摄的照片
通常的嫌疑人(和一个新的)
从 GEE 的介绍中,您已经知道如何注册和使用gee,安装folium进行交互式可视化,以及geehydro以类似于原生 Javascript API 的方式与代码进行交互。如果你对气候建模感兴趣并阅读了 CMIP6 的介绍,那么你一定也知道Proplot。本教程的新颖包是ipygee,其工作方式与geehydro类似,允许使用一些仅在 GEE 代码编辑器中可用的功能。
要安装ipygee,只需使用Pip:
pip install ipygee
然后,您可以导入本教程中需要的所有工具:
import ee
import folium
import geehydro
import numpy as np # yes, numpy!
import pandas as pd # yes, pandas!
import proplot as plot
import matplotlib.pyplot as plt from ipygee import*
from pandas.plotting import register_matplotlib_converters
如你所知,要使用gee,你必须总是触发认证并初始化库,特别是如果你有兴趣运行 Google Colab 中的代码。
# Trigger the authentication flow.
>>> ee.Authenticate()
# Initialize the library.
>>> ee.Initialize()
世界保护区
世界保护区数据库 (WDPA)是最大的海洋和陆地保护区数据库,在 GEE 中很容易找到。它提供全面的地理信息和世界上任何地区的边界。在这里,您将使用它来获得巴西的新古土著公园的形状,该公园被称为世界上第一个也是最大的保护区之一。
使用 EE 片段,您可以加载 WDPA 的面数据库并过滤新谷公园的区域:
wdpa_polygon = ee.FeatureCollection('WCMC/WDPA/current/polygons')
# select the Xingu Indigenous Park shapefile
xingu = wdpa_polygon.filter(ee.Filter.eq('NAME', 'Parque do Xingu'))
正如你已经知道的,Folium和geehydro允许 GEE 数据的可视化。在这种情况下,您可以绘制新谷公园的区域并与之交互:
Map = folium.Map(location=[-12., -52.5], zoom_start=8)
>>> Map.addLayer(xingu)
>>> Map

巴西新古土著公园的 WPDA 多边形。
新谷公园因其丰富的历史和多样性而成为一个特别的地方。在 1961 年划定边界使公园成为官方公园之前, 别墅-博厄斯兄弟 第一次去那里几乎是偶然的,作为龙卡多尔-新古探险的一部分。由于该国大部分地区(以及当时的政府)都认为这个地区已经荒芜,他们很快发现这里住满了数百个不同的土著部落。这些遭遇确定了巴西土著权利历史的开端。
MODIS MOD13Q1 植被指数
要加载 MODIS 产品,只需使用ee.ImageColletion()和您需要的代码片段。为了使分析更容易,您将过滤过去 3 年的数据。
# MODIS MOD13Q1
modis = ee.ImageCollection('MODIS/006/MOD13Q1')
modis = modis.filterDate(ee.DateRange('2016-01-01','2019-12-01'))
MOD13Q1 产品提供 NDVI 和 EVI,因此您可以轻松选择它们并将数据归属于不同的变量:
# select EVI and NDVI
evi = modis.select('EVI')
ndvi = modis.select('NDVI')
MODIS 数据的一个小问题是你需要根据你使用的产品应用一个比例因子来理解它。根据本用户指南 (RTFM),MOD13Q1 产品的比例因子仅为 0.0001 。这意味着您必须将每个数据集乘以这个比例因子,才能正确使用它。
你可能已经意识到你需要写一个小函数来实现它。在 GEE 方言中,这意味着你必须越过一个 T1。请不要在这里使用不必要的 for 循环。入乡随俗。
请在这里插入你的巨蟒笑话。
def scale_factor(image):
# scale factor for the MODIS MOD13Q1 product
return image.multiply(0.0001).copyProperties(image,
['system:time_start'])# mapping function to multiply by the scale factor
scaled_evi = evi.map(scale_factor)
scaled_ndvi = ndvi.map(scale_factor)
适当缩放数据集后,您最终可以在新谷公园的 WDPA 边界内使用它们并开始分析。一个好的开始方式是在互动的Map上画出卑鄙的 NDVI。
# mean NDVI in the Xingu Park
>>> Map.addLayer(scaled_ndvi.mean().clip(xingu),
vis_params={'min': 0,
'max': 1,
'palette': ['red', 'yellow','green']})
>>> Map

2016-2019 年期间的平均 NDVI。
同样的代码也适用于 EVI。多亏了Folium和geehydro,事情就这么简单。虽然交互式的Map很酷,但是任何 python 爱好者都会有兴趣获得一个时间序列,并利用数据科学从中挤出一些很酷的信息。GEE 有一个本地的方法来绘制时间序列,但不幸的是,这只能在代码编辑器中使用。这正是ipygee派上用场的地方,因为它模仿了原生的基于 javascript 的ui.Chart.image.series()。没有它,我们会有麻烦。
它的工作方式与相同,参数与相同,它甚至允许你在绘图时用它制作一个小部件。
# Xingu MOD13Q1 NDVI time series
xingu_ndvi = chart.Image.series(**{'imageCollection': scaled_ndvi,
'region': xingu,
'reducer': ee.Reducer.mean(),
'scale': 1000,
'xProperty': 'system:time_start'})xingu_ndvi.renderWidget(width='50%')

新谷原住民公园的 NDVI 时报系列。
你可能会想,这样会更好。令人惊奇的是,ipygee还允许你像熟悉的任何Dataframe一样处理这个时间序列。这就是Pandas的作用。不信?
>>> type(xingu_ndvi.dataframe)
pandas.core.frame.DataFrame
从此,你可以放心地从 GEE 进入我们通常的 Python 生态系统。想想Pandas、Seaborn或者你知道的任何一个相关的包来研究时间序列。多亏了ipygee,你可以随时从这两点出发。
这意味着你可以用你喜欢的方式来绘制它。用Proplot试试看:
fig, ax = plot.subplots(figsize=(7, 3), tight=True)
ax.plot(xingu_ndvi.dataframe.index, xingu_ndvi.dataframe['NDVI'],
color='forest green', marker='o')plot.show()

一个好看的 NDVI 时间序列图。
同样的事情也可以发生在即将到来的 EVI 时间序列上。通过一点点Pandas,我们可以用.groupby()将这些 16 天的采样时间序列转换成一个月的时间序列。这可能会让他们表现得不那么疯狂。
# monthly averaging
xingu_evi_monthly = xingu_evi.dataframe.groupby(pd.Grouper(freq="M")).mean()xingu_ndvi_monthly = xingu_ndvi.dataframe.groupby(pd.Grouper(freq="M")).mean()
为了比较这两个时间序列,您将返回到标准的Matplotlib,因为在Proplot中有一些关于共享轴的问题。不幸的是,或者不是,在他们解决这个小问题之前,您将不得不坚持使用稍微不那么优雅的解决方案。
# time index
time = xingu_evi_monthly.index# plot
fig, ax1 = plt.subplots(figsize=(7, 3))
ax2 = ax1.twinx()# EVI
ax1.plot(time, xingu_evi_monthly, label='EVI',
color='muddy brown', marker='+')
# NDVI
ax2.plot(time, xingu_ndvi_monthly, label='NDVI',
color='forest green', marker='o')ax1.set_xlabel('Time')
ax1.set_ylabel('EVI')
ax2.set_ylabel('NDVI')ax1.set_yticks(np.arange(0.3, 1.1, 0.1))
ax2.set_yticks(np.arange(0.3, 1.1, 0.1))plt.legend()
plt.tight_layout()
plt.show()

EVI 和 NDVI 在新谷土著公园。
请注意两个时间序列中的幅度差异,但行为相同。很可能更复杂的 EVI 算法使其能够抵抗大气影响,并允许其反映更强的植被变化。新谷公园的植被以塞拉多生物群落为主,比标准的亚马逊雨林密度要低。也许这就是 EVI 比 NDVI 低的原因,但这只是猜测。
遗言
MODIS 相关产品是极好的数据集,允许广泛的环境应用。全球高频率和高分辨率的 MOD13Q1 数据集对于研究植被行为和变化非常有用。使用 Python API,您可以在 GEE 中访问许多这些产品,并且希望您通过本教程学会了如何探索它们。
像往常一样,在我的气候数据科学资源库中可以免费获得上面所有代码的交互式 Jupyter 笔记本。
模块化最大化
贪婪算法

本文基于这四个假设来定义社区
基于随机网络不具有社团结构的假设,提出了局部模块性的概念[1]。它将给定网络的划分与类似的保度随机化进行比较。
考虑具有 N 个节点和 L 条链路的网络,以及具有 nc 个社区的网络分区,每个社区具有 Nc 个节点和 Lc 条链路:

局部模块性比较子图 Cc 的实际网络布线和随机重布线子图之间的链路数量:

pij 对整个网络进行随机重新布线,保持每个节点的预期程度:

如果 Mc = 0,那么子图是随机连线的(因此,没有社区结构)。如果 Mc
操作(2),得到一个简化公式:

将局部模块性推广到整个网络,识别图的最佳划分。这样,网络的模块性就变成了(4)的所有社区的总和:

类似地,模块性越高,网络的划分质量越高。它可以取正值、空值或负值。每当整个网络被定义为一个团体时:

在极端的情况下,每个节点是一个单独的社区, Lc = 0,模块性变成负的。因此,以前的结构都不能归类为社区。
一些算法使用模块化来划分网络。
贪婪算法
贪婪算法在每一步最大化模块性[2]:
1.一开始,每个节点属于一个不同的社区;
2.最大程度地增加模块化的一对节点/社区成为同一个社区的一部分。模块性是针对全网计算的;
3.执行步骤 2,直到剩余一个社区;
4.选择具有较高模块性的网络分区。
在计算复杂度方面,由于模块性变化可以在常数时间内计算,所以步骤 2 需要 O(L) 计算。在合并社区之后,网络的邻接矩阵在最坏情况下被更新 O(N) 。每个合并事件重复 N-1 次。于是,整体复杂度为: O((L+N)N) 或 O(N^2) ,在稀疏图中。
尽管模块性在计算上是合适的,并且是一种准确的社区检测方式,但是应该强调两个缺陷:分辨率限制和模块性最大值。
分辨极限
首先,介绍了一种计算社区 A 和 B 合并时网络模块度变化的方法[2]:

这意味着两个社区应在以下情况下加入:

假设在社区 A 和 B 之间有一个链接:

换句话说,当社区 A 和 B 仅通过一条链路连接时,如果它们的大小低于阈值,它们将被合并。变得不可能检测低于一定规模的社区。
假设,

分辨率限制取决于网络的规模。克服这种限制的一种方法是将较大的社区细分为较小的社区,并对它们进行划分。
模块化最大值
本文开头提出的第四个假设依赖于这样一个假设,即更高的模块化意味着更好的网络划分。尽管在一些图中,显著不同的分区可能具有相似的模块性。当网络中的节点数量增加时,这成为一个相关的问题。越来越难将网络的最佳分区与质量较低的分区区分开来。在优化模块化的算法的情况下,这是一个中心问题,一旦它们迭代直到其变化低于输入阈值。

图 1 同一网络中显著不同的分区可以具有相似的模块性[2]
根据对图 1 中网络的分析,每个集群内的链路数量大约为:

考虑 kA = kB = kC 并将(7)应用于先前的网络,根据节点数量 nc 计算模块性变化:

将先前网络的两个随机集群合并到同一个社区中最多会降低模块化 2/nc^2.在极限情况下,这种变化是检测不到的:

根据经验,最好的分区应该是将每个 5 节点集群分组到不同社区的分区。虽然,每当两个相邻的社区合并时,模块性增加 0.003。此外,如果将随机的 5 节点集群分配给一个社区,即使它们没有直接连接,也会导致在最佳分区检测到的模块性变化接近 0。这符合模块化图中平台的设想,这可能会扭曲图 1 中最佳分区的选择。这个平台解释了为什么大量模块化最大化算法可以快速检测高模块化分区——它们不是唯一的。
模块化优化算法是通过优化质量函数来解决的更大问题集的一部分。
参考
[1] A.-L. Barabási,《网络科学书》,[在线]。可用:http://networksciencebook.com。【2019 年 5 月 15 日获取】
[2] M. E. J .纽曼,“检测网络中社团结构的快速算法”,物理评论 E,69 卷 6 期,2004
MongoDB:从 mLab 迁移到 Atlas
迁移您的 MongoDB 以保持 Heroku 应用程序正常工作

肯里克·米尔斯在 Unsplash 上拍摄的照片
在上一篇文章中,我们讨论了如何从 Heroku 部署中分离 mLab 数据库。我们这样做是为了给我们赢得一些时间,为我们的 MongoDB 应用程序开发一个更持久的解决方案。看看下面作品吧!希望你现在已经超脱了。如果你错过了最后期限,mongo 说你仍然可以重置密码。然而,我们没有这方面的运气,因为链接被打破了。
[## 保持你的 mLab MongoDBs:脱离 Heroku
2020 年 11 月 10 日从 Heroku 分离 mLab 以保存您的数据不被删除的简要指南。
towardsdatascience.com](/keeping-your-mlab-mongodbs-detaching-from-heroku-173b5c97c715)
MongoDB 在 2018 年收购了 mLab,并决定在 2020 年 11 月 10 日结束这项服务。这是一种让用户将数据库迁移到 MongoDB Atlas 的努力,我们将在这里讨论。
Mongo 在他们发给我们的邮件中提供了一个指南;然而,我们不会在本文中遵循它。我们这里的方法将为您提供更多的迁移选择。
我们将使用 Google Cloud shell 将一个数据库从 mLab 迁移到 Atlas。要开始使用本指南,我们需要一个免费的 GCP 帐户。使用云外壳将比在本地完成该过程更容易,但这不是必需的。
迁移指南
在我们开始之前,我们需要访问亚多拉斯的 MongoDB。
在 Mongo.com 上创建帐户以使用他们的免费服务。
福布斯美国首席技术官 Vadim Supitskiy:爱荷华州、俄勒冈州、N. Virginia、南卡罗来纳州、洛杉矶、Salt…
www.mongodb.com](https://www.mongodb.com/cloud/atlas/mongodb-google-cloud)
- 创建帐户后,命名项目并选择语言首选项。

免费集群入门
- 对于这篇文章,我们将使用免费计划。

亚多拉斯集群层
- 选择您使用的云提供商和地区。无论选择什么,这都不会显示在您的项目中。您应该选择低延迟的云和区域。这些选择决定了您的 Atlas DB 将在哪里启动。

云提供商选择
- 最后,部署集群!
现在我们有一个 AtlasDB 实例可以使用了。
2。安装 Mongo 工具
现在,我们将使用 Google Cloud shell 来设置一个 mongo 客户端和一些迁移所需的 mongo 工具。如果你还没有,你可能需要在他们那里建立一个免费账户。
从任何浏览器管理您的基础架构和开发您的应用程序。查看该产品的文档。云…
cloud.google.com](https://cloud.google.com/shell)
我们已经编写了一个 shell 脚本来为您获取工具!请注意,这些都是针对 Debian 10 的版本。当我们需要使用 mongo shell 时,这个 shell 脚本可以很好地重用,因为它会将文件添加到一个不持久的位置。
这将在 Debian 9 和 10 上工作。如果你运行的是另一个版本的 Linux,你可以在Mongo 的安装说明 中找到更多信息。

云壳终端
- 打开终端后,使用以下命令克隆 repo。
git clone https://github.com/edkrueger/download-mongo-shell-tools.git
- 通过运行 shell 脚本安装 MongoDB 工具。
sh install.sh

这个下载获得了 mongo 客户端、mongodump和mongorestore以及其他一些我们不太关心的文件。
3。保存您的 mLab 连接字符串
我们将把我们的连接字符串保存为环境变量,以使我们的生活变得更简单。
- 从 mLab 获取您的连接字符串 URI。这可能会在 Heroku 上作为你的
mongodb_uri出现。 - 运行以下命令,确保用 MongoDB 连接字符串替换
<connection-string>。这将保存该字符串供以后使用。
export MLAB=<connection-string>
- 通过以下方式连接到 mongo:
mongo $MLAB
- 在这里,您应该能够查看和查询您的收藏。
4。将用户添加到 Atlas 数据库
现在我们需要添加一个用户到我们的亚多拉斯云数据库来连接它。
- 回到阿特拉斯。
- 单击数据库访问。

数据库访问面板
- 单击添加新数据库用户。
- 创建用户名和密码,然后单击添加用户。默认设置就可以了。
5。改变网络接入
接下来,我们需要允许连接到 Atlas 数据库。
- 单击左侧的网络访问。
- 点按“添加”和“允许从任何地方访问”,这会将 0.0.0.0/0 添加为访问 IP。这使得我们的来源可以是任何 IP。

网络存取
请记住这里的安全风险,如果使用敏感数据,只添加您信任的 IP。
6。保存你的图集连接字符串
现在我们可以连接了,让我们从 Atlas 中获取连接字符串,并将其保存为环境变量。
- 选择左侧的群集。
- 单击
Cluster0实例下面的连接。 - 点击“连接 Mongo Shell”,你会看到下面的屏幕。

MongoDB 连接字符串
- 复制连接字符串并插入您的
<dbname>。 - 将 Atlas DB 连接字符串保存为环境变量。
export ATLAS=mongodb+srv://cluster0.5g6iu.mongodb.net/<dbname>
- 与
mongo $ATLAS连接。 - 用
db.test.insertOne({"Hello": "World"})测试。
7。使用 mongodump 和 mongorestore 迁移数据
现在我们可以从云 Shell 连接到两个数据库,并将连接字符串作为环境变量,我们已经为迁移做好了准备。我们将使用 shell 脚本中获得的工具来简化这个过程!
- 运行
mongodump --uri $MLAB将旧数据库转储到一个. bson 文件中。 - 将路径复制到。以下命令的 bson 文件。
- 运行
mongorestore --uri $ATLAS dump/heroku_g76fhzjn/posts.bson确保使用您的。bson 路径,不是我们的!您可能需要检查您的收藏名称;我们的默认是heroku_g76fhz。

mongorestore 向 Atlas 添加数据
8。验证迁移
现在我们已经加载了新的数据库,让我们验证一下我们是否已经正确地完成了。
- 运行
mongo $ATLAS连接到新加载的数据库。 - 在 mongo shell 中,运行
show collections,然后运行db.collection-name.find()来查看您的数据,确保使用正确的集合名。同样,如果你的数据库包含大量信息,使用findOne()!
如果您看到您的数据,您就完成了迁移!现在让我们的应用程序运行。
9。重新部署 Heroku 应用程序
我们必须添加dnspython到我们的应用程序正确连接的要求。这是为了适应新的 mongo srv 协议,您可以在我们的连接字符串中看到。
我们通过向 Github 提交一个具有这种依赖关系的新 Pipfile,然后运行pipenv install dnspython来确保它在 Pipfile.lock 中。
在您的环境包含dnspython之后:
- 在 Heroku 上添加 Atlas Connection URI。

显示配置变量以添加新的 URI
- 从所需的分支重新部署应用程序。
如果应用程序出现任何问题,请检查您的构建日志!这是我们被警告缺少依赖项的地方,dnspython。
包扎
这是一个将数据从 mLab 迁移到 Atlas 以保持 Heroku 应用程序运行的指南。
我们在 MongoDB 上做了一个免费的账户来使用 Atlas。然后配置了一个 google 云 shell 来运行 mongo shell 和工具。配置好外壳后,我们将旧的 URI 保存为$MLAB,新的 URI 保存为$ATLAS。完成所有这些设置后,我们可以使用mongodump和mongorestore轻松地将 mLab 数据库中的数据加载到 Atlas 数据库中。
一旦我们的数据成功转移,我们需要重新部署我们的应用程序。在我们的应用程序使用新的连接字符串运行之前,我们需要指示 Heroku 安装dnspython。
我们通过向 Pipfile 添加依赖项,运行 pipenv install,然后从 Heroku 上的主分支重新构建应用程序来实现这一点。
这就是我们通过将数据从 mLab 迁移到 Atlas 来保持 Heroku 应用程序运行的方式。我们的下一个指南将涵盖从 mLab 到 Azure Cosmos 的数据库迁移!
[## MongoDB:从 mLab 迁移到 Azure Cosmos DB
如何将您的 mLab 数据库迁移到 Azure Cosmos DB 以保持您的应用程序运行。
towardsdatascience.com](/mongodb-migrating-from-mlab-to-azure-cosmos-db-88c508f72d24)
我们在这里发布了 mLab 到 Cosmos 的指南!
MongoDB Vs SQL
你应该选择哪个?

MongoDB vs. SQL(图片由作者提供)
MongoDB 和 SQL 数据库是后端世界中截然相反的两个方面。前者处理混乱的非结构化数据,后者处理有组织的结构化数据。两个世界都有各自的优点和缺点,适用于不同类型的用例。在本文中,我们将对 MongoDB 和 SQL 数据库(确切地说是 MySQL 数据库)进行深入的比较,并且还将触及一个重要的话题,即我们如何能够执行 MongoDB 分析,就像在 SQL 数据库上执行分析一样容易。
MongoDB vs MySQL
正如我们所讨论的,我们将比较 MongoDB 和 MySQL,MySQL 是一个众所周知的 SQL 数据库,我们的大多数观众都能够与之相关。但它也可以是任何其他 SQL 数据库,如 Oracle、MS SQL Server、PostgreSQL 等,以便我们进行比较。
历史
MongoDB 属于 NoSQL 数据库家族,用于以 JSON 格式存储非结构化文档。该数据库于 2009 年首次推出,此后成为 NoSQL 空间的领先数据库之一。

MongoDB 总部(图片由作者授权)
MySQL 是一个开源的 SQL 关系数据库,用于以类似表格的格式存储结构化数据。它于 1995 年首次推出,现在由 Oracle 管理。因为它是免费的,所以它已经成为 SQL 数据库需求中非常受欢迎的选择。
SQL vs NoSQL 的范例
SQL 数据库,也称为关系数据库,旨在存储具有结构化模式的数据。模式表示数据应该遵循的数据库的设计。在结构化模式中,数据以被称为表的行列格式保存,并且可以使用结构化查询语言(SQL)格式的查询来检索。
SQL 关系数据库是唯一可行的商业数据存储解决方案,直到 21 世纪初,互联网和 web 2.0 热潮开始产生大量非结构化数据。这种非结构化数据不能正确地映射到类似表的模式,因此需要不同种类的数据库来支持这种非结构化数据。
非结构化数据的积累是迈向大数据时代的一大步,但另一方面,由于存储的数据是非结构化的,因此无法使用 SQL 查询这些数据
这是 NoSQL 数据库开始出现的时候。这些新数据库需要支持这种不同类型的非结构化数据,并且不适合模式;像键值存储、文档、文本、图形和宽列这样的数据。例如,MongoDB 主要支持非结构化文档。
非结构化数据的积累是迈向大数据时代的一大步,但另一方面,由于存储的数据是非结构化的,因此无法使用 SQL 查询这些数据。到目前为止,SQL 一直是查询和分析的标准,并且为开发人员所熟知。我们以后会谈到这一点。
数据是如何存储的
在 MySQL 中,数据存储在表中,其中列表示属性,行表示特定的记录。这些表依次驻留在数据库中。在 MongoDB 中,数据存储在类似于 MySQL 表的集合中。一个集合可以由许多文档组成,文档中的数据以 JSON 格式的键值存储。MongoDB 数据库中可能有数百个这样的集合。
SQL 数据库有一个关系属性,其中不同的表通过外键、主键相互关联。例如, EmployeeID 列将作为 Employee 表的主键,在 Payments 表中作为外键出现,从而用引用属性连接两个表。这种关系确保了不存在其详细信息不在主雇员表中的雇员的付款条目。这就是为什么像 MySQL 这样的 SQL 数据库也被称为关系数据库的原因。
另一方面,在 MongoDB 中,我们不能在集合的非结构化数据之间建立这样的关系。因此,它被认为是非关系数据库。
像 MySQL 这样的 SQL 数据库的架构是由 ACID 属性的原则控制的。
酸代表原子性、一致性、隔离性和持久性。这些属性侧重于数据库中事务的一致性和可靠性。
MongoDB 建立在 CAP 定理的基础上,该定理关注一致性、可用性和分区。与 SQL 数据库的 ACID 属性不同,CAP 定理在 MongoDB 的情况下关注数据的可用性。
总之,SQL 数据库保证了事务的可靠性,而 MongoDB 确保了数据的高可用性。
可量测性
通常,MySQL 数据库或 SQL 数据库只能通过增加服务器的内存大小、磁盘空间或计算能力来进行纵向扩展。对于具有高查询量的大型数据库,垂直扩展的成本可能会很高,而且会快速增长。
对于 MongoDB,不是增加服务器配置来扩展,而是添加一个新的服务器
像 MongoDB 这样的 NoSQL 数据库支持水平伸缩,也称为分片。在这种情况下,不是增加服务器配置,而是添加一个新的服务器来实现可伸缩性。这种方法通常成本较低,因为一组低成本的商用硬件可以一起满足以经济高效的方式支持大量查询的要求。
可靠性和可用性
可靠性和可用性是衡量任何数据库系统健壮性的关键指标。大多数 SQL 数据库最初是为独立服务器设计的。为了降低失败的风险,他们的体系结构转向了分布式数据库,其中数据库在一个节点集群上运行,从而提高了弹性。即使集群中的一个节点关闭,数据库仍会在其他节点上正常运行。
像 MongoDB 这样的 NoSQL 数据库最初设计时就考虑到了弹性。它运行在一个商用硬件集群上,跨节点复制数据以实现高可靠性和可用性。与 SQL 数据库不同,可靠性和可用性是 MongoDB 架构不可或缺的特性,而不是事后才想到的。因此,与 MySQL 和其他 SQL 数据库相比,MongoDB 中的自动故障转移更快、更简单。
(计划或理论的)纲要
与任何其他 SQL 数据库一样,MySQL 数据库有一个数据应该遵循的预定义模式。例如,在创建表时,必须定义表中的列数及其数据类型。保存在表中的任何数据都应该与表结构相匹配,否则将会出错。
MongoDB 模式的动态特性非常有用,因为互联网应用程序和物联网设备生成的大部分数据都是非结构化的
另一方面,在 MongoDB 中,不需要预定义任何模式。集合可以毫无问题地存储不同类型的文档。没有什么可担心的,如果一个新类型的文件到达,它可以很容易地被保存。
MongoDB 模式的动态特性非常有用,因为互联网应用和物联网设备生成的大部分数据都是非结构化的,无法保存在传统的 SQL 数据库中。
此外,许多公司在知道以后如何使用数据之前会存储数据。这在存储日志数据和用户活动的移动应用中很常见。随着该公司的应用程序上市,他们收集数据没有最终目标。
很多公司会先存储数据,然后才知道以后会怎么用。这是因为大数据的好处必须通过自下而上的方法发现,而不是自上而下的方法。
稍后,他们可能会发现这些数据为他们提供了关于需要添加哪些功能的有价值的信息。对于非结构化数据库,更容易进行这种无计划的数据收集,因为不需要提前定义模式。
查询和分析
MySQL 数据库可以借助结构化查询语言或 SQL 进行查询。事实上,MySQL 遵循 ANSI SQL 标准,这是几乎所有关系数据库都采用的通用 SQL 标准,如 Oracle、PostgreSQL、Sybase 等。
SQL 查询对开发人员来说是友好的,并且是成熟的。SQL 还可以用于对数据执行高级分析功能,如过滤、连接、合并和聚合。这使得 SQL 成为执行高级分析的强大选项。
MongoDB 不像 MySQL 那样支持传统的 SQL 查询。然而,MongoDB 确实支持文档查询,但是这个特性还不完善,而且很有限——尤其是与 SQL 相比。一个例子是 MongoDB 查询不支持连接,而连接是从多个数据源获取信息的关键操作。
所以 MongoDB 对于存储非结构化数据是有用的,但是它没有提供一个成熟的查询语言来执行高级分析。这听起来像是许多商业用例的交易破坏者,但幸运的是,有一些选择。
我需要对 MongoDB 数据进行分析:我有哪些选择?
在 MongoDB 中存储大量非结构化文档数据的便利性是许多用例的一个重要因素。这就是为什么业界没有因为 MongoDB 有限的查询支持而忽视它,而是发展出多种解决方案来支持 MongoDB 上的高级分析。这里有几个不错的选择。
选项 1:将 MongoDB 数据导入 SQL 数据仓库
如果我们不能在 MongoDB 上执行分析,我们可以将数据加载到 SQL 数据仓库中,然后在那里运行熟悉的 SQL 查询进行分析。为此,我们可以编写一个定制的批处理 ETL 过程或者使用 Panoply 或 Xplenty 之类的工具。

ETL 到数据仓库管道的图表(图片由作者授权)
这是一个强大的方法,对许多公司都很有效,但是它也有一些限制。尽管它服务于这个目的,但它带来了构建和维护数据仓库的额外开销,更不用说与 ETL 或 ELT 过程相关的成本了。

如果您的一些 MongoDB 数据不适合 SQL 模式,该怎么办?在许多情况下,这些数据会被丢弃(图片由作者提供)
对于大企业来说,数据仓库可能是一个好的解决方案,但是对于小公司或年轻的初创公司来说,您可能不喜欢投资数据仓库。即使数据仓库非常适合您公司的大部分数据,但您的一些数据有更严格的处理要求或受限制性法规的约束,因此可能有必要将其排除在数据仓库之外。另一个可能出现问题的地方是,如果您需要对外部数据集进行快速实验性分析,而您不能或不想将这些数据集移入您的数据仓库。
要点:数据仓库是一个很好的选择,但是会带来一些费用和限制。它们还会通过强制将数据分配到关系模式中来消除使用 NoSQL 数据库的好处。
选项 2: MongoDB BI 连接器
MongoDB 意识到,他们也必须为 MongoDB 分析提供一些选项。所以他们开发了 MongoDB BI 连接器,可以与流行的商业智能工具一起使用,比如 Tableau、Cognos、Qlik 等等。这个连接器充当 BI 工具和 MongoDB 之间的中间接口,它将 SQL 查询转换为 MongoDB 查询,并在将结果传递给 BI 工具时将其转换回 SQL 格式。
与我们讨论的列表中的其他选项相比,MongoDB BI 连接器确实使工作变得简单。使用 MongoDB BI 连接器节省了构建数据仓库或(我们将讨论)为 MongoDB 分析编写定制 Python 应用程序的开销。

使用 MongoDB BI 连接器连接传统商业智能工具的示意图(来源:mongodb.com)
这里仍然存在一个缺点,如果您想在 MongoDB 数据和 MySQL 数据或任何其他 SQL 数据之间进行连接,该怎么办。一种选择是将 MongoDB 数据导入 MySQL 数据库,然后在那里执行分析。但是这本质上将我们带回到我们已经在上面讨论过的数据仓库选项和开销。
要点:如果您想将您现有的 BI 工具与 MongoDB 连接起来,MongoDB BI connector 是一个不错的选择,但是它有一个缺点,即您不能连接来自多个异构数据源的数据。
选项 3:数据虚拟化
数据虚拟化是一个过程,在这个过程中,应用程序可以从多个来源访问数据,并通过抽象底层技术将其呈现给前端用户。这意味着用户将从多个来源获得一致的数据视图和无缝体验。
第三代商业智能平台之一 Knowi 在数据虚拟化上下了大赌注。他们的商业智能平台构建于数据虚拟化之上,用于本地连接到 NoSQL 数据源并构建可视化和仪表板。这使得它成为 MongoDB 分析的一个强大选项。这样做的好处是,用户可以获得在 MongoDB 数据上运行 SQL 查询的简化体验,就像他们在处理 SQL 数据库一样。

数据虚拟化平台如何与包括 MongoDB 在内的各种数据源直接连接的示意图(来源:knowi.com/why-knowi)
此外,它还可以无缝地连接来自多个异构数据源的数据。这意味着,如果我们想将 MongoDB 数据与 MySQL 数据连接起来,只需提供两个数据源和连接字段即可。另一个值得注意的特性是,MongoDB 查询可以在 Knowi 内部运行,也可以用它们的可视化查询构建器构建。它还具有整合基于搜索的分析的优势,这是一种新的数据分析方法,一些 BI 工具正在大力投资。
Denodo 是另一家在数据虚拟化上下大赌注的公司。虽然他们的平台纯粹是为了数据集成,没有 Knowi 提供的分析层。因此,如果你决定使用 Denodo,你仍然需要在此基础上添加一个分析平台。
要点:与列表中的其他选项相比,如果您正在寻找在 MongoDB 数据上执行高级分析的轻量级无缝体验,那么数据虚拟化是最佳选择。如果您有多个数据源,并且需要跨数据库连接,或者如果您只需要在扩展时维护一个灵活的数据基础设施,那么这是一个很好的选择。
选项 4:使用 Python 和 PyMongo 进行定制编码

另一种选择是构建一个定制的 Python 应用程序来连接到 MongoDB,从中获取数据并对其进行分析。PyMango 是 Python 的 MongoDB 驱动程序,可以实现这一点。事实上,使用 PyMongo 我们不仅可以获取 MongoDB 数据,还可以将数据写回 MongoDB。
与数据仓库相比,这可能是一个不错的选择,并且在探索性数据分析方面表现出色,但不一定总是适合商业应用程序。
要点:除非您特别需要一个轻量级的定制解决方案,并且可以轻松地用于探索性数据分析,否则您可能仍然希望考虑数据仓库或 BI 解决方案。
备选方案 5:翻译
最后一个选项是将 SQL 查询转换成 MongoDB 查询。这与 MongoDB 连接器非常相似,但它是作为第三方实现来完成的。Dremio 的团队做了很好的工作,构建了一个翻译引擎,试图解决这个问题。翻译系统让你写出 SQL 查询,解释它们,并把它们重新格式化成 NoSQL 查询。对于一些简单的用例来说,这是一个很好的选择,但是对于需要跨数据库连接的更复杂的应用程序来说,可能会遇到问题。它还会引入延迟,这会给高通量分析带来问题。然而,如果您的用例相当简单,并且您没有预见到它在未来会变得更加复杂,那么翻译可能是一个很好的选择。
要点:如果您需要在单个数据库源上处理简单的分析查询,这个选项很好。
结论
在这篇文章中,我们介绍了 MongoDB 与 SQL 数据库之间的全面比较,并看到了对 MongoDB 数据执行分析的各种选项。下面让我们总结一下我们的讨论。
- SQL 数据库用于存储结构化数据,而像 MongoDB 这样的 NoSQL 数据库用于保存非结构化数据。
- MongoDB 用于以 JSON 格式保存非结构化数据。
- MongoDB 不支持 SQL 数据库支持的高级分析和连接。
- 有许多很好的解决方案可以支持 MongoDB 分析,包括:数据虚拟化、翻译、MongoDB 连接器和带有 ETL 或 ELT 过程的数据仓库。
监控你的依赖!不要再做一个盲目的数据科学家。

多米诺骨牌作为依赖关系的类比。
监控模型依赖关系的原因。
在我以前的文章《班长!停止做一个盲目的数据科学家,我提到了许多用例以及在我们的领域监控&警报的巨大重要性,特别是从数据科学研究者的角度。我查看了用例,并回顾了几家为这个巨大但奇怪的未讨论的问题提供不同解决方案的公司,即缺乏模型监控,这是 atm 超出我们行业的一个问题。
更新:这篇文章现在已经作为第 49 篇文章发表在 奥赖利的书《每个云工程师都应该知道的 97 件事》
用例
在最初的文章中,我列出了数据监控和警报的几个用例,我确信还有其他用例,但是这些用例对数据科学家的影响最大。名单是按时间顺序写的。在本文中,我们将研究包的依赖版本,并探索几个可能直接影响您的客户端的事件。
- 注释者表现
- 注释分布
- 数据完整性,即模式和测试驱动的数据分析。
- 数据分布,即概念漂移
- 依赖版本,即你的包导入。
- 型号版本
- 模型参数
- 模型度量
- 业务产品指标
- 模拟软件和硬件性能
包依赖关系
算法依赖于不同的包,每个包在其生命周期中都有多个版本。以下是依赖关系破坏您的模型或部署的几种情况,当这种情况发生时,您会希望有某种机制来监控您的依赖关系。除了可以通知您问题发生的时间、内容和位置的警报之外。
- 有时,您会有" requirements.txt" 环境之间的差异,例如,当涉及科学包时,您的研究环境总是最新的,更不用说您最近升级了 scikit-learn。但是,您的部署锁定了一个特定的版本。一旦新的代码或序列化的模型被上传到临时或生产环境,当反序列化使用旧的包版本序列化的模型时,诸如 pickle 或 joblib 之类的包将会中断。
- 您可能正在使用一个将表情符号转换为文本的软件包,即,😃->快乐。你的 NLP 算法依赖于表情符号到文本的某种映射,并且是用特定的包版本来测量的。昨天发布了增加表情符号的新版本,现在它已经改变了许多表情符号到单词的映射。对于您的模型,这是一个关键的变化,可能会导致模型打破预期的分类,或者只是影响预测概率。
- 团队之外的某个人调整了一个包,这是他们的责任,它可能发生在上游或下游,最终会改变或破坏模型的预期功能。如果它中断了,就要找出异常来自哪里,我们都知道当这种情况发生时,这是非常耗时的,尤其是当它在您的代码库之外时。另一方面,如果它一直在滴答作响,那么你不会意识到有问题,直到客户投诉。
- 您的管道中最近引入了一个部署流程,但由于某种原因,它中断了,退回到几个月前已被否决的前一个流程,但仍然指向旧代码、旧依赖项和旧序列化模型。您的客户将获得基于陈旧数据的预测。新客户的数据在这些旧模型中是不存在的,你将面临一场小灾难。
在所有这些情况下,通过缩短检测时间、响应时间和解决时间,监控每个环境中的依赖关系将为您节省昂贵的工时,最重要的是,它将缩短您的客户受到这些事件严重影响的时间。

图 1:黑点代表一个依赖项或模型版本检查点,这个图是由 superwise.ai 在一个实际视图的顶部组成的。
我想象一个仪表板,在其中很容易识别这种事件。图 1 是一个建议图,其中模型版本在一个时间线上被跟踪,想象每一条从上到下的曲线,分别在训练、测试、试运行和生产环境中监控某种模型活动。
“如果您不能跟踪您的依赖版本,您就不能相信您的研究、模型性能或部署将是确定的”
结论
为了保持确定性行为,我们必须记录每个环境中的所有依赖版本,如图 1 所示。当环境之间的依赖关系不匹配时,我们需要启用警报。
请记住,单元测试可能会通过,除非您密切监视您的模型性能,与您的客户交谈,或者某些东西完全崩溃,否则您不会发现这些类型的问题。
依赖-版本-历史应该映射到您的预测中,这样您就可以找出哪里出错了,什么时候出错了,哪里出错了,并快速轻松地处理它。
我要感谢谢伊·亚诺夫斯基的富有成效的谈话引发了这个问题。
Ori Cohen 博士拥有计算机科学博士学位,主要研究机器学习。他是 TLV 新遗迹公司的首席数据科学家,从事 AIOps 领域的机器和深度学习研究。
监控机器学习模型

肖恩·奥尔登多夫在 Unsplash 上拍摄的照片
机器学习模型概念漂移量化指南
机器学习模型越来越成为产品或产品功能的核心。因此,数据科学团队现在负责确保他们的模型在产品 3 年多的生命周期内按预期运行。
需要实时监控模型的性能,以检测任何会影响最终用户体验的性能问题。如果没有它,只有当问题已经开始影响底线或者用户开始抱怨时,问题才会被报告。
在本指南中,我们将阐述推动有效监控策略的关键原则:
- 为什么您不能使用准确性指标来跟踪绩效
- 机器学习模型的故障模式
- 使用统计模型或领域分类器量化模型漂移
准确性度量不能用于监控模型
监控机器学习模型的黄金标准是实时跟踪准确性。然而,在大多数情况下,这是不可能的,因为:
- 模型干预
- 预测和实际标签之间的等待时间长
- 人类标签的需求
对于大多数机器学习模型,准确性度量不能实时计算
模型干预
当模型做出预测时,通常会采取行动。例如,如果您预测客户流失,您会采取措施留住您认为可能流失的客户。几个月后,当你衡量“地面真相”标签时,如果一个客户没有翻盘,你不知道这是因为预测错了还是因为你采取了行动。
这可以通过运行模型但不根据预测采取行动的一小部分数据来缓解。虽然这将允许您计算准确性指标,但这是以降低用户体验或损失收入为代价的,并且在某些情况下是不可能的(例如在检测信用卡欺诈时)。
预测和现实之间的延迟
在一些应用中,在做出预测和地面实况标签可用之间存在延迟。例如,信用卡欺诈案件可以在交易完成后 90 天内上报。因此,在做出预测和获得实际情况之间有 3 个月的延迟。
如果准确性是用于跟踪模型性能的唯一指标,那么直到问题开始出现 3 个月后才能被发现。
人类标记
对于计算机视觉应用或自然语言处理模型,除非手动标记样本,否则基本事实标签通常不可用。
正如我们所见,实时测量准确性指标通常是不可能的。因此,我们需要一套不同的指标来跟踪机器学习模型的性能。
模型的故障模式
在我们开始定义一个监控系统之前,考虑一下模型性能随时间而降低的主要方式是很重要的。为了保持这篇文章的重点,我们将不考虑可以通过现有的监控解决方案跟踪的问题(内存、延迟等)。
生产几年后,所有型号都会损坏
潜在的故障模式可以分为两类:
- 突发数据变化:模型依赖于复杂的数据管道,其中一个管道内的微小变化都会导致模型性能的大幅下降。
- 概念漂移:建模数据的底层分布已经改变
通过区分这两种故障模式,我们可以定义一组需要跟踪的指标。
重大数据变更
重大数据变更通常会影响单个变量,并导致其分布发生巨大变化。因此,可以使用单变量指标来检测它们,例如:
- 缺少值的百分比
- 分类数
- 统计分布—最小值、最大值、平均值、分位数等
- 词汇表外单词的百分比
虽然这些单变量指标有些粗糙,但它们可以检测出数据收集过程中可能出现的大多数问题。
增强这种监控策略的有效方法是定义需要更密切监控的关键部分。通过定义关键部分并跟踪这些部分的指标,您可以发现仅影响客户子集的问题,这些问题可能会被您所跟踪的全局指标的噪音所淹没。例如,Slack 为其 10 个最大的客户定义了监控片,因为性能问题会首先影响这些客户[1]。
行为变化
行为变化可能更难检测,尤其是当地面真相标签不可用时。有两组指标可用于跟踪:
- 预测标签的百分比
- 概念漂移
预测标签分布的快速变化通常表明存在问题。如果被检测为欺诈的交易的基准比率是 2%,那么被标记为欺诈的交易的比率突然上升到 10%就需要进行更深入的调查。
虽然概念漂移经常被提及,但很少有资源详细说明如何量化它。
量化概念漂移
概念漂移度量的最终目标是检测多元分布的变化,并量化今天的输入与昨天的输入相比有何不同。
量化概念漂移不是一件容易的事情,仍然是一个非常开放的研究问题。现有技术可以分为两种主要方法:
- 基于距离的度量
- 领域分类器
基于距离的指标往往更易于近实时计算,因为它们不太耗费资源。另一方面,领域分类器需要更多的资源,因此更适合每天或每周测量概念漂移。
基于距离的度量
概念漂移可以从分布的角度来考虑,更具体地说,就是两个不同的分布有多么不同。
一个常用的度量标准是瓦瑟斯坦度量标准,也称为运土车距离或锦葵距离。虽然对于单变量分布计算起来非常有效[2],但是对于多变量分布需要计算近似距离[3]。
在流式环境中,计算累积分布函数可能很棘手。您可以使用的最有效的算法之一是 T-Digest,它允许您计算数据流的分位数,而无需预先定义数据应该如何存储。
基于您可用的资源,您可以通过以下任一方式量化概念漂移:
- 计算每个变量的 Wasserstein 度量并汇总它们。
- 通过将输入变量视为来自单个多元分布来计算单个 Wasserstein 度量。
聚合单变量 Wasserstein 度量: 这是计算效率最高的方法,但代价是遗漏了变量间相关性的变化。

聚合单变量 Wasserstein 度量
虽然这种方法很容易实现,但也不是没有问题。主要的一个是关于如何聚集单变量指标。你取简单平均数还是用加权平均数?如果使用加权平均值,那么如何确定最佳权重?
虽然这些问题的答案将取决于您的用例,但在使用加权平均方法时,一个好的起点是使用特性重要性分数作为起点。
多元 Wasserstein 度量: 计算一个精确的多元 Wasserstein 度量是非常耗费资源的。然而,最近的进展使得近似的计算更加有效。参见[2]了解更多信息。
领域分类器
领域分类器是量化概念漂移的最流行的方法。从概念上讲,这种方法非常简单,如果可以训练一个分类器来准确区分两个数据集,那么概念漂移将会很高。

使用领域分类器量化概念漂移
更正式的说法是:
- 首先定义两个数据集,例如包含今天输入要素的数据集 A 和包含昨天数据的数据集 B
- 组合两个数据集,并创建一个列来标识一行属于哪个数据集
- 将数据集分为训练数据集和测试数据集
- 在定型数据集上定型一个分类器,该分类器将尝试区分一行属于哪个数据集
- 计算该模型在测试数据集上的误差,并使用公式:
concept drift = 2 * (1 — 2 * error)
虽然这种方法允许您检测变量之间相关性的变化,但它也有一些缺点:
- 选择正确的域分类器取决于用例(梯度提升树、支持向量机、双向 LSTM 等)
- 计算成本高
- 用模型监控模型
高水平要求
一旦定义了需要跟踪的指标,我们就可以开始设计您的监控解决方案。第一步是定义和量化需求是什么。考虑以下几点是一个好的开始:
- 延迟:问题发生之间的延迟是多少?
- 持续评估:你会定期标记预测来计算准确性指标吗?
- 概念漂移:您将如何计算概念漂移?
- 日志:为了调试问题,您将如何访问日志?

监控解决方案的高级概述
一旦这些要求到位,您就可以开始构建您的监控系统了。
结论
在部署机器学习模型之前,定义一个监控策略非常重要。第一步是简单地跟踪输入和输出变量的分布,这可以用现有的软件监控框架来完成。
然而,有些情况下这是不够的或不可能的,CV 和 NLP 模型就是一个很好的例子。对于这些情况,领域分类器可以用来量化概念漂移。
参考文献:
[2] 切片 Wasserstein 自动编码器:一个令人尴尬的简单生成模型
原载于 2020 年 5 月 27 日www . sta kion . io。
监控生产中的 ML 模型

在 Unsplash 上由 Carlos Muza 拍摄的照片
传说在 2010 年代早期,数据科学家在他们的 Jupyter 笔记本上掌握熊猫和 Scikit-Learn 就足以在这一领域出类拔萃。如今,人们的期望越来越高,数据科学家通常需要在整个机器学习生命周期中导航。这包括监控生产中的 ML 模型。关于可以用来检查模型健康状况的不同度量标准,已经说了很多,也写了很多。然而,一个基本问题经常被搁置,即何时干预。
过晚修改故障模型会对业务产生灾难性的影响。然而,过早修改它会导致不必要的成本(时间和金钱)和过度拟合。为了在早期和晚期干预之间取得平衡,对于数据科学家来说,拥有一个关于何时介入的合理策略至关重要。
在本帖中,我们将基于时间序列分析的最新研究,探讨如何检测模型性能的实质性变化。
注:所提出的方法受到“回顾性”变更点分析的启发(见[1])。这意味着,我们试图检测历史数据集中的变化(在这种情况下是模型的性能)。如果这种方法用于“在线”监控,可能会导致多次测试,因此需要进行相应的调整。对于在线监测,基于在线变化点检测的方法更适合。
模型质量的下降可能有多种原因。例如,我们可能在新数据进来时更新它,但是进来的数据可能有偏差。或者我们不更新模型,但是底层数据生成过程的分布发生了变化。无论模型恶化的原因是什么,我们都希望检测到它并及时做出反应。
为了简单起见,让我们考虑一个分类器,并以准确度来衡量它的性能。我们可以训练模型,并在维持数据集上达到 90%的准确率。当将模型部署到生产中时,我们可以定义一个硬阈值,比如 80%,并在模型的准确性低于该阈值时做出反应。尽管这看起来是一种方便简单的方法,但它有一些根本性的缺点。
首先,我们不观察模型的真实(或样本外)精度,而是观察样本内精度。这两个概念之间的区别至关重要。样本外精度是模型对总体的(未知)精度。根据输入的数据,我们观察到的是样本内的准确性。样本内精度可以解释为样本外精度加上一些附加噪声。当样本内准确度超过 80%阈值时立即做出反应可能是反应过度。
第二,不清楚哪些数据用于精度评估。我们是基于所有未用于训练的数据来衡量模型的性能,还是应该只使用传入的数据?如果我们只使用输入的数据,我们应该回溯多远?我们应该使用昨天的数据还是考虑更长的时间框架?
我们使用的数据越多,我们发现模型性能变化的时间就越晚。我们使用的数据越少,精确度的方差就越高。特别是,硬阈值对异常值很敏感。
假设我们基于每天传入的数据来度量模型的准确性(如图 1 所示)。80%的硬阈值表明 7 天后已经进行了干预,这似乎是一种过度反应,因为真实(但未知)的准确性仍然高于阈值。

图 1:样本外准确度(线)和测量准确度(点),x 轴:以天为单位的时间,y 轴:以百分比表示的准确度
为了避免偏差和方差之间的权衡以及关于异常值的稳健性问题,采用另一种观点可能是有用的。
比方说,我们希望从第 t=1 天开始,在 T 天内每天监控分类器的性能。更具体地说,设 A(t) 表示分类器对第 t 天的数据的样本内准确度。因此,我们有一个样本内精度的时间序列 A(1),…,A(T) 。然后,我们可以将样本内精度分为确定性部分(样本外精度) a 和均值为 0 的随机部分 ε ,这是由于数据噪声而引入的。特别的,我们有 A(t)= a(t) + ε(t) 与 E[ε(T)**= 0,对于 t=1,…,T 。注意,随机误差 ε 可能是相关的。在这种情况下,我们希望确定与基于来自训练的维持数据集的精度相比,样本外精度的偏差是否很大。换句话说,我们想要检验这些假设

这里 a(0) 表示部署前的模型精度,δ是指定偏差是否相关的阈值。

图 2:测量的准确度(点),准确度的平滑估计值(深蓝色),样本外准确度(浅蓝色),x 轴:以天为单位的时间,y 轴:以百分比为单位的准确度
在我们的示例中, a(0) = 90%,我们可能希望允许 10%的偏差,因此δ= 10%。由于 a(t) 未知,为了检验上述假设,我们必须对其进行估计(见图 2)。
有各种方法来估计 a ,如分位数回归或局部多项式估计。设表示带宽为 h(T) 的 a 的局部线性估计量(见[2]),那么,无论何时我们都可以拒绝零假设

渐进地,随着 T 增长到无穷大,检测到实际变化的概率收敛到 1,错误检测到变化的概率消失。换句话说,我们低概率“过早”介入,大概率在必要时介入。
请注意,上述测试程序可以细化为定义渐近一致水平α测试(意味着非平凡收敛)。然而,在这种情况下,我们将需要估计某些参数并调整超参数,这在应用中是不实际的。
如果零假设被拒绝,我们可以继续下去,估计模型的精度偏离初始精度 90%超过 10%的时间。这可能有助于获得关于为什么模型质量改变的额外见解。一个直接的估计是最早的时间 s 使得|s-90% |>10%。在图 2 的例子中,估计时间为 s =17,接近模型精度下降到 80%以下时的真实时间 t =16。
对于 Python 中监控过程的实现,可以使用 statsmodels 中的内核回归(参见下面的代码)。请注意,样本内精度将来自应用中的外部来源,仅用于展示该方法。第二个代码片段创建了一个类似于图 2 的图(没有未知的样本外精度)。
结论
监控生产中的 ML 模型可能不像从头开始训练新模型那样令人满意,但检测故障过程并确保高质量是非常重要的。
找到正确的行动时机对于防止不必要的成本、模型过度拟合和错误预测带来的灾难性后果至关重要。估计样本外精度可以通过局部线性估计来完成。用 Python 可以很容易地实现一个假设测试来检查相对于初始精度的相关偏差。
随着 ML 生命周期自动化的发展,我们需要确保在这个过程中包括监控部署的模型,以便将 ML 管道的质量提升到下一个水平。
[1] A. Bücher,H. Dette 和 F. Heinrichs,渐变均值中的偏差是否相关?基于超范数估计量的测试方法(2020),arXiv 预印本 arXiv:2002.06143。
[2] H. Dette 和 W. Wu,检测非平稳过程均值的相关变化——一种质量过剩方法(2019),统计年鉴,47(6),3578–3608。
监控 ML 管道
我在这个博客里讲了很多把机器学习代码带入生产的过程。然而,一旦模型投入生产,你还没有完成,你只是刚刚开始。该模型将不得不面对其最大的敌人:现实世界!

图片来源:蒂姆·斯密特
这篇文章关注的是,为了了解你的模型在现实世界中的表现,你可以实施什么样的监控。这既考虑了连续训练,也考虑了已训练模型的使用。它调查了:
- 监控您的基础设施
- 监控数据
- 监控培训
- 监控现实世界中的价值
监控您的基础设施
构建任何系统的工程师通常都会进行一些监控,以确保服务正常运行,基础设施未满负荷。这种监控对您的机器学习组件也很有用,因为它可以帮助您发现:
- 输入数据的频率和数量的变化。
- 我们的数据或处理可能依赖的第三方系统的变化和故障。
- 随着需求的增长,需要升级基础设施。
- 随着数据消耗的增加,需要优化/分发机器学习组件的代码。
- 当达到某些限制时,需要重新评估您选择的解决方案。
其中一些情况也可能对您的模型有某些不太明显的影响。例如,正在接收的数据的数量/频率的增加/减少可能表明现实世界中发生了一些事件(风暴、地震),这可能反过来扭曲您的模型的结果。它也可能是安全漏洞之类的副作用,例如企图进行 DDoS 攻击,这反过来可能会对您的模型结果产生负面影响(例如,在推荐系统中)。
此类监控的一些流行工具包括但不限于:

图像生成使用: imgflip
监控数据
现实世界在变,你的数据也会变。由于这是训练和使用模型的最重要的组成部分,我们需要有一些东西来理解数据何时漂移,以便对其采取行动。
这可以通过跟踪数据的一些统计数据(中值、方差、标准差等)来实现。您可以为用于训练模型的数据计算这些值,并在每次新的训练运行时跟踪这些值。然后,您也可以以一定的频率对模型中用于推理的一批最新数据进行同样的操作。这两个图表将帮助您理解:
- 输入的训练数据是否随时间而改变,以及改变了多少。
- 数据更改的频率,以及您是否需要更频繁地训练模型。
- 选择用于训练的数据是否与用于推理的数据不同。这可能是训练不够频繁的症状,但它也可能是由训练数据的选择过程引起的,这可能是有偏见的。
数据发生变化的场景可能会导致您需要调查变化是什么以及是否需要对其采取行动。这种变化可能表明输入和输出之间的关系已经改变,或者现实世界中的某些事情已经以预期的方式(例如价格变化)或意外的方式(例如自然灾害)影响了数据。也可能是变化对结果没有任何影响,不需要进一步的行动。
除了这些统计度量之外,还可以实现一组验证,当数据与预期不符时,这些验证反过来会发出警报。例如,由某些特征的最小/最大 rages 被超过、丢失数据的可接受阈值被超过或者训练和推理数据的统计数据之间的差异大于阈值而触发的警报。

图片来源: xkcd
监控培训
你很可能是为了跟上世界的变化而以一定的频率训练你的模型。在这个培训过程中,你可以跟踪某些事情,这有助于发现变化和调查问题。其中包括:
- 在您使用的算法允许的情况下,跟踪特性的重要性。其中最重要的特征的变化可能指示数据中可能的漂移,或者输入和输出之间关系的变化。
- 跟踪模型质量度量(F1 分数、精确度等)。与旧版本相比,新型号的性能可能会突然显著下降。这可能表明数据发生了变化,使得当前的方法过时或不适用于正在解决的问题。
- 跟踪有关用于训练模型的输入数据的统计信息。这在前一节中已经提到。
- 跟踪任何其他可能指示产品中模型性能的模型 KPI(例如用例验证)
这些测量值的变化可能表明新输入数据中存在问题,可能是因为数据漂移,也可能是数据清理过程中存在问题。此外,它还可能表明需要一种不同的方法来处理新的数据状态。
监控现实世界中的价值
对于生产中的每个软件系统,我们需要了解用户是如何使用该软件的。我们通过分析来做到这一点,这涉及到收集关于最终用户在应用程序上做什么的数据,以便推断某些功能的价值。与基于规则的系统一样,分析可以帮助您了解机器学习组件对产品和用户的影响。
例如,用户在由机器学习驱动的功能上的行为变化可能表明新的模型版本不再为用户提供正价值。或者,这也可能意味着模型已经过时,因为现实世界已经发生了变化。
这种数据通常显示在一些交互式仪表板上,允许人们监控某些重要的 KPI,并为感兴趣的利益相关者生成报告。在某些系统中,该数据还用于通过基于这些 KPI 的变化提供警报来确定问题。可能受机器学习驱动的功能影响的 KPI 是您可以利用的 KPI。这些可以帮助您理解对这些功能的更改对真实世界中整体用户体验的影响。嗨,我再次检查并删除了不再符合许可证/不清晰的图像。所有其他的要么是我创作的,要么是有许可证允许在此环境下分享的。
如你所见,一旦机器学习组件进入野外,你有许多选择来监控它们。您不必实现所有这些。确定什么对您的团队有意义,您正在解决的问题,您预见的可能问题,您为解决方案选择的方法,以及产品的成熟度。
如果你为你的团队做了一些不同的工作,请在评论中分享🙂
现在去把一些漂亮的图表放在那个废弃的屏幕上!
原载于 2020 年 2 月 13 日【http://intothedepthsofdataengineering.wordpress.com】。
使用 Python 监控公共区域的社交距离

注:Idea 积分登陆 AI
社交距离是减缓传染病传播的重要方式。人们被要求限制彼此之间的互动,减少疾病通过身体接触或密切接触传播的机会。
在过去的十年里,AI/深度学习在几个日常生活问题中显示出了有希望的结果。在人工智能的帮助下,各种日常生活任务已经自动化。在本文中,我们将详细介绍如何使用 python 结合深度学习和计算机视觉来监控社交距离。带有安装说明的完整 python 代码可在 GitHub 上获得
在我们进入细节之前,让我们看看最终的输出。下面的视频是使用人工智能在已经可用的街道摄像头的公共区域监控社交距离的结果。
本文的其余部分将分为三个步骤
- 步骤 1 —安装项目
- 步骤 2—运行项目
- 步骤 3—代码的输入和输出
- 第 4 步——它是如何工作的?
- 步骤 5—建议的改进
步骤 1 —安装项目:
建议为这个项目创建一个新的虚拟环境并安装依赖项。可以采取以下步骤下载并开始使用这些项目
- 克隆存储库:包含代码的存储库可以使用
git clone [https://github.com/aqeelanwar/SocialDistancingAI.git](https://github.com/aqeelanwar/SocialDistancingAI.git)
2.安装需要的包:提供的 requirements.txt 文件可以用来安装所有需要的包。使用以下命令
cd SocialDistancingAI
pip install –r requirements.txt
这将在激活的 python 环境中安装所需的包。
步骤 2 —运行代码:
该代码将视频文件路径作为输入。以下命令可用于运行代码
cd SocialDistancingAI
python main.py --videopath "vid_short.mp4"
步骤 3 —代码的输入和输出:
标记必要的点:
运行 main.py 将打开视频中第一帧的窗口。此时,代码希望用户通过单击框架上的适当位置来标记 6 个点。这些点对于提供视频的用户视角是必要的。
输入:前 4 点:
6 个必需点中的前 4 个用于标记您想要监控的感兴趣区域(ROI)。此外,从上面看,这些点标记的线在现实世界中应该是平行的。例如,这些线可能是路缘。
这 4 个点需要按照预先定义的顺序提供,如下所示。
- 点 1 (bl) :左下角
- 点 2 (br) :右下角
- 点 3 (tl) :左上角
- 点 4 (tr) :右上
输入:最后两点:
最后两个点用于标记感兴趣区域中相距 6 英尺的两个点。例如,这可能是一个人的高度(更容易在框架上标记)
下面的 gif 将有助于更好地理解要点

代码输出
输出:在原始视频中检测到行人
代码的第一个输出是检测到行人的原始视频。行人被定位在紫色方框中。

输出:行人鸟瞰图
代码的第二个输出是用户标记的感兴趣区域中自上而下的局部行人视频(鸟瞰图)。

输出:社交距离参数
基于鸟瞰图和用户标记的 6 英尺距离点,该算法计算并报告以下参数。
1。# 6 英尺违规:行人违反 6 英尺安全距离阈值的次数
2。居家指数:量化了与正常的日常行人交通相比,有多少人待在家中。0%意味着行人流量与平时相比没有变化,50%意味着一半的人呆在家里
3。社交距离指数:量化保持的社交距离。50%意味着一半的互动违反了 6 英尺的安全距离标准。
第 4 步——它是如何工作的?
在没有任何额外信息的情况下,从单目图像中检测行人之间的距离是不可能的。一种方法(虽然不是很准确)是要求用户进行特定的输入,从而估计行人之间的距离。如果用户可以在帧上标记相距 6 英尺的两个点,使用外推法,可以找到帧上不同点之间的距离。如果摄像机与行人行走的平面上的所有点距离相等,这将是真实的。行人离摄像机越近,他们就越大。帧上的两个点(相距相同像素数)离相机越近,它们之间的实际距离就越小。
为了解决这个问题,代码接收用户输入的 4 个点来标记从上面看平行的两条线。由这 4 个点标记的区域被认为是 ROI(可以在上面的 gif 中以黄色看到)。这个多边形 ROI 然后被扭曲成一个矩形,成为鸟瞰图。这个鸟瞰图具有点的属性(这些点相隔相同的像素数),无论它们在哪里都是等距的。它所需要的只是一个乘法器,将像素中的两点之间的距离映射到现实生活中的单位(如英尺或米)中的距离。这是最后两个用户输入点发挥作用的地方。
如上所述,深度学习用于检测和定位行人,然后将其映射到摄像机的鸟瞰投影。一旦我们有了鸟瞰视图中行人的坐标,社交距离参数就变得简单明了。
该算法的完整框图如下所示。

步骤 5 —建议的改进
该代码可以在几个方面进行大量改进。以下是一些建议
- 一种更精确的将摄像机画面映射到鸟瞰图的方法
- 该代码使用现有的多类分类器,该分类器在来自 COCO 数据集的 1000 个类上进行训练。代码可以使用专门为二元类(行人、非行人)训练的重新训练的分类器。
- 可以引入计算社交距离参数的更精确的方法。
总结:
该算法可用于分析公共区域的社交距离,并采取必要的措施来更好地应对疫情。自动化的任务将导致在短时间内采取有效的行动,从而使我们更好地应对这种情况。完整的代码可以在 GitHub 这里获得。可以随意修改完善。
输入行人视频取自 https://www.youtube.com/watch?v=aUdKzb4LGJI
如果这篇文章对你有帮助,欢迎鼓掌、分享和回复。如果你想了解更多关于机器学习和数据科学的知识,请关注我@Aqeel an war或者在LinkedIn上与我联系。
为什么模型停止工作了?

我们能从 16 世纪的学者身上学到什么
视角就是一切。数据的取样位置会对数据的解释产生重大影响,有时还会影响数据的含义。比如小汉斯·霍尔拜因的经典画作。如果你直视它,有一部分看起来很奇怪。很难辨认。也许是个错误?但是,从侧面看,谜底揭开了:这是一个头骨,警告我们可能在我们不注意的地方潜伏着未知的危险。

在数据科学中,我们总是会遇到不同的观点,尽管我们经常意识不到这一点,尤其是在大型项目中。我们正在处理的数据是以某种方式收集的,有时会被恰当地描述。这意味着即使我们可以访问完整的数据集,我们可能也不知道它是如何产生的。尽管如此,我们的工作是分析它并提取洞察力。
一个这样的任务是根本原因分析,这在各行各业都存在,并且由于现代机器学习正在经历复兴。在更紧急的情况下,这句话通常显得不那么平淡无奇: WTF 刚刚发生了?

起初,一切都是顺利和清晰的。使用 t-SNE 的多维数据分析包含明显可分的簇。然而,它不是一个静态的数据集,而是一个随时间演变的数据集。突然你注意到一些事情发生了变化。从一个时刻到下一个时刻,整个整洁的结构崩溃,所有的类都混在一起。

刚刚发生了什么 WTF?
让我们后退一步。要确定根本原因,首先需要描述发生了什么以及情况如何演变。
观察 1:一个明显的普遍变化导致了所有阶级的崩溃
看看星团是如何随时间变化的,很明显坍缩是相当突然的。没有明显的迹象表明是从某一特定来源传播的。事实上,所有的东西都在同一时间倒塌,这表明,不管原因是什么,它影响了所有类别的物体。
观察 2:在崩溃之前,较小的变化是可见的
第一个观察结果很清楚,让我们深入探讨。有即将崩溃的迹象吗?通过仔细观察时间上的分布,我们可以看到,在特定的时间点上,一些星团在 2。这意味着分类子集的识别准确性在崩溃之前开始恶化。

这些是你的金丝雀,➚
这一发现很有趣,因为当与崩溃相关时,这些效应可以作为预警指标。此外,这些类型的观察可以为访问原始数据的特定部分提供基本原理,并确定实际发生了什么。在查看数据之前进行统计分析的作用是识别可能相关的部分,对它们进行优先排序,从而可能更快。
解开谜团:原始数据显示了什么
在关键时刻查看原始数据表明,原因很明显:数据被扭曲了,这种观点的改变影响了所有的阶层。

从左边开始的第一和第二组没有失真,并且形状为学习者提供了足够的信息来清楚地聚集每个数字。虽然最右边的第四个不包含可识别的信息,但是第三个更有趣。人眼很难分辨出那个。顶部的 0 受透视的影响较小,而 4、7 和 9 开始丢失部分。当要求识别这些数字时,准确率低于前两位。
现在,回到根本原因分析,假设这些数据哪里出错了。这种同质转换是数据传感器变化的典型标志。在这种情况下,可能是看数字的摄像机慢慢倾斜,改变了视角。原因可能是天气的机械作用或镜头变形。在任何情况下,这个假设提供了足够的理由来检查和可能更换相机。
使用无监督学习自动排除有监督学习的故障
我们看到的是通过使用无监督学习和可视化来监控深度学习应用程序健康状况的一种方式。但是,我们如何实现自动化呢?观想是一种强大的技术,但需要专业的人眼。
做到这一点的一个方法是建立可视化的统计测量。换句话说,首先你找到适当的观想,然后你找到一个适当的方法来描述想要的状态。然后可以自动监控该测量。在我们的例子中,这样的度量可以是基于剪影方法的距离度量。
这些方法有助于根本原因分析的典型 4 步中的前 2 步:
- 识别并清楚描述问题
- 描述从正常情况到问题发生的时间演变
- 区分根本原因和其他因果因素
- 在根本原因和问题之间建立因果图。
当处理数百万或数十亿个数据点时,即使看似很小的胜利在纠正和预防性维护中也是非常重要的。他们可以决定是否及时发现问题,以及是否能够避免影响许多用户的停机。
利用地球观测卫星监测野火
来自 Sentinel-2 任务的高分辨率光学卫星数据可以使用 SNAP 数据处理工具箱提供大规模野火评估
野火对森林的自然生态系统造成广泛破坏,导致生态、经济和社会退化。在印度,绝大多数(> 90%)野火源于人类活动,如木材采伐、土地转换、刀耕火种农业以及土地使用和财产权方面的社会经济冲突(Roy,2003)。然而,近年来,长期干旱、出于商业目的的过度开发和土地转作他用,导致野火规模、频率和相关环境影响大幅增加。政府当局报告的损失往往被低估。这些数据来自印度(FSI)森林调查局的地面调查研究和实地观察。由于缺乏关于森林火灾规模和总受灾面积的高质量统计数据,森林火灾造成的破坏程度无法准确估计。遥感技术的最新进展使得有可能通过提供关于森林生态系统反应动态的深层次见解,以具有成本效益的方式及时克服这些限制。因此,我们在全球、区域和地方范围内探测、监测、评估和预测火灾的能力有了显著提高。
2019 年 2 月,Bandipur 国家公园爆发了大规模的野火,烧毁了该地区数千英亩的林地(图 1)。2 月 21 日,在公园的昆达凯尔山脉报道了第一次野火,随后由于强风,野火迅速蔓延到附近的山脉。报告显示,持续五天的野火是由居住在国家公园附近的人类引发的。之前的干燥季风期在森林山脉的底层留下了大量易燃物质。虽然野火在 Bandipur 老虎保护区和国家公园并不罕见,但主要由入侵杂草马缨丹组成的灌木丛可能在加剧火势和速度方面发挥了作用。

图 1-Landsat 8 卫星图像显示了 2019 年 2 月 25 日发生的 Bandipur 火灾。
印度空间研究组织报告称,被烧毁的总面积为 10,920 公顷。然而,报道称,官员们错误地报告了估计值,以淡化火灾造成的损失。对于 2019 年 Bandipur 野火的准确损害评估,地球观测卫星可以提供关于火灾范围和严重程度的宝贵见解。本文报告了三个关键方面——a)火灾影响区域的识别,b)烧伤严重程度的分类,以及 c)使用 Sentinel 2 多光谱卫星数据对总火灾影响区域的估计。
背景
位于地球表面的特征所消耗的能量具有独特的特征。这些光谱特征使我们能够研究各种各样的地球过程。光学遥感工具利用位于高分辨率地球观测卫星上的可见光和红外传感器来研究森林火灾。近红外、中红外和热波段易受植被健康变化的影响。它们经常用于准确评估受火灾影响的地区和燃烧的严重程度,以支持森林管理活动。绿色健康的植被反射近红外(NIR)区域的辐射。它吸收电磁波谱中可见光部分的红光。同时,烧伤区域在可见光和短波红外(SWIR)区域消耗更多的能量,同时吸收近红外区域的能量(图 2)。

图 2——健康的植物品种在近红外光谱中反射更多的能量,但在 SWIR 中反射微弱。这种光谱特性有助于探测燃烧区域,如森林地面上的枯死土壤/植物材料。
资料来源:美国林务局。
归一化燃烧比(NBR) 是一项指标,用于通过区分野火事件后光谱特征发生显著变化的区域来衡量燃烧的严重程度。它是利用遥感卫星图像的近红外和 SWIR 波段的能量强度计算出来的。NBR 的公式与归一化差异植被指数(NDVI)相似,后者是基于近红外和红色波段的光强度。NBR 使用近红外波段和 SWIR 波段之间的比率。较高的 NBR 值反映了被健康植被覆盖的区域,而较低的值表示裸露的地面和最近被烧毁的区域。接近零的值表示不受火灾事件影响的区域。
Sentinel 2 数据的 NBR 计算如下:

燃烧严重程度是一个术语,用来表示生态系统受野火事件影响的程度。这是根据卫星图像得出的火灾前和火灾后 NBR 之间的差异估计的。为了识别最近燃烧的区域,并将它们与裸土和其他无植被区域区分开,经常使用火灾前和火灾后 NBR 之间的差异,也称为δ归一化燃烧比(dNBR) 指数。dNBR 值高的区域对应于更高程度的损伤或烧伤。相比之下,较低的 dNBR 值代表未受火灾影响的区域或在野火事件后通过植物物种的再生而反弹的区域。

为了监测火灾,需要同一地区,即感兴趣的区域的两幅卫星图像。在时间上最接近火灾事件采集的图像被称为主图像,最近的一个图像被称为从图像。通过计算主映像和后续从映像的 NBR,可以分析火灾的影响[ 2 ]。基于主 NBR 图像和从 NBR 图像(dNBR)之间的差异,可以确定火灾事件的总燃烧面积,并且可以描绘受火灾严重影响的区域。每个随后的从属图像可用于根据第一张火灾地图(主图像)在不同时间点创建火灾后地图,并通过检查森林植被的反应来监测恢复进度。
卫星数据
Sentinel-2 是一项地球观测卫星任务,广泛应用于研究地球环境。它由两颗卫星组成,配备了最先进的多光谱成像仪(MSI),通过全球范围内的 13 个光谱带提供高分辨率光学图像,重访频率高,使其成为土地利用土地覆盖监测、大气校正和云/雪分离的重要数据源( ESA )。本次分析中使用的哨兵 2 级 2B (L2B)数据是从哥白尼开放存取中心获得的。为了绘制火灾后评估图,使用了 2019 年 2 月 18 日(即主图)和 2019 年 2 月 28 日(即从图)相隔 10 天获得的两幅图像(图 3)。这两张卫星图像已经使用 SNAP 桌面 GUI 进行了预处理,该 GUI 是在 SNAP 引擎之上开发的,并且基于 NetBeans 平台。SNAP 由欧洲航天局(欧空局)开发,是一个通用软件平台,用于科学开发哨兵-1、哨兵-2 和哨兵-3 任务。SNAP 的项目页面和各个工具箱可以在http://step.esa.int找到。

图 3-比较大气穿透 RGB 波段组合中 Bandipur 区域的主(左)和从(右)Sentinel 2 级 2B 卫星图像
数据预处理
大气校正是确定真实地表或大气底部(BOA)反射率值的重要预处理步骤。当分析涉及比较同一地区两个不同时间的卫星数据时,这一点尤其重要。自 2017 年 4 月起上市的所有 Sentinel 2 L2 产品已经应用了大气校正。L2B 产品包含矢量云和卷云遮罩,它们是作为大气校正的产品创建的,但是,将遮罩应用于所有波段和整个场景非常耗时。因此,我们对乘积进行子集化,但是向量乘积由于这种操作而丢失了。因此,在我们的例子中,为了保存信息,必须创建一个包含云掩膜的新波段。目前使用批处理方法无法做到这一点,因此我们需要在使用 GraphBuilder 工具进行预处理之前,分别为每个产品添加云遮罩带。
一个一个地处理数据将非常耗时且不方便。我们可以使用 SNAP 中可用的批处理工具同时处理所有图像。要使用该工具,我们首先需要定义我们想要应用的流程及其所有步骤。我们可以使用 GraphBuilder 工具来完成这项工作。GraphBuilder 中批处理的另一个优点是只有最终产品被物理保存,节省了宝贵的磁盘空间。GraphBuilder GUI 中预处理工作流程(图 4)的简要概述如下。

图 GraphBuilder 中的预处理概述
- 读取:从卫星图像中读取输入数据。这个初始步骤包括将获取的卫星图像加载到 GraphBuilder 工具中。
- 重采样:Sentinel-2 产品中的 13 个波段并不都具有相同的分辨率。许多运营商不支持具有不同大小波段的产品,因此首先我们需要对波段进行重新采样,以获得相同的分辨率。使用双线性重采样算法将所有 13 个波段重采样到 10 米的分辨率。
- 下一步是从卫星图像中选择要研究的区域。这需要使用来自几何图形选项的 WKT 将输入图像子集化为感兴趣区域和选定波段(B3、B8、B12 和 cloud_mask ),以子集化原始图像的一部分,并将坐标复制到地理坐标选项中。为确保选择正确的区域,点击更新。
- 一旦选择了合适的波段和感兴趣的区域,剩下的工作就是计算所选区域的 NBR。这可以使用 BandMaths 操作器来完成。该操作符允许我们通过在表达式选项卡中指定公式来创建 NBR 波段。一个波段合并操作符允许我们将多个波段叠加到一个文件中。然后,Subset 运算符和 BandMaths 运算符都连接到 BandMerge 运算符。
- 最后一步是将波段合并操作器连接到写操作器。该步骤将处理后的 2 级图像输出到指定的目录,然后可进一步用于描绘烧伤区域。
搭配和水/云遮蔽
一旦燃烧前和燃烧后的图像都经过预处理,我们将它们合并以形成包含燃烧前和燃烧后 NBR 带的新产品。使用 SNAP 中的配置工具将两幅空间重叠的图像并置的任务允许我们稍后容易地计算火灾前和火灾后 NBR 值的变化。然而,在检测到变化之前,我们需要从新创建的图像中屏蔽掉水体和云。
NBR 可以是描绘燃烧区域的一种强有力的方法。但是,该指数对水体很敏感,这可能会导致图像像素的错误分类。在某些情况下,湖泊和河流可能在 NBR 值上表现出类似的差异,并导致假阳性情况[ [3](http://• https://www.earthdatascience.org/courses/earth-analytics/multispectral-remote-sensing-modis/normalized-burn-index-dNBR/) ],即像素被分类为已燃烧,而实际上它们并未燃烧。因此,有必要屏蔽掉代表水体的像素,可以使用归一化差异水体指数****【NDWI】进行检测。为此,我们将创建一个单一的组合水和云的面具。NDWI 旨在最大化绿色波段水体的反射率,最小化近红外波段水体的反射率[McFeeters,1996]。
Sentinel 2 数据的 McFeeters NDWI 计算如下:

识别烧伤区域
此时,我们应该有两个 NBR 波段,分别对应于火灾前(2 月 18 日)和火灾后(2 月 28 日)。为了识别最近燃烧的区域,可以通过将火灾后的 NBR 与火灾前的 NBR 进行差分来计算 dNBR。然而,dNBR 是变化的一个绝对量度,这可能会在火灾前植被覆盖低的地区导致问题,在那里差异将是微不足道的。为了避免这样的问题,我们使用【RBR】:

RBR 是通过对火灾前的 NBR 做一个小的调整来计算的,以避免方程达到无穷大而失败(Parks 等人)。, 2014).计算出 RBR 后,我们可以使用 BandMaths 工具通过创建一个新波段来识别最近被烧毁的区域,该波段只包含 RBR 值高于指定阈值的像素。我们可以看到 RBR 值的变化,正如我们已经确定的那样,这是用于烧伤面积评估的测量方法,通过使用类似 QGIS 的 GIS 软件绘制 RBR 带。在这里,我使用 Python 绘制了 RBR 值的地图(图 5 ),使用了合适的色图和基于 RBR 值范围的比例。直方图是评估大多数 RBR 值所在范围的快速方法。对于我们的数据集,RBR 值大多位于-0.25 和 0.6 之间,因此我使用了-0.75 和 0.75 之间的范围来创建变化检测图。

图 5 —变化检测显示了 Bandipur 地区受火灾影响的区域。
烧伤严重程度分类
第二个目标是根据火灾前和火灾后 RBR 值的变化程度对被烧毁的地区进行分类。烧伤严重程度的分类可以作为帮助急救工作和评估火灾后康复发展的重要工具。美国地质调查局(USGS)提出了一个分类表来解释烧伤的严重程度,如下表所示。

烧伤严重程度分类标准表(USGS)
在我们的数据中,最低值为-1.5,表示不属于森林生态系统的像素,对应于其他土地利用类型,如建成区或耕地。森林的某些区域具有与植物再生相对应的 RBR 值。数据集中的大多数像素具有接近 0 的值(平均值为 0.06),这对应于未燃烧区域。显示受火灾影响区域的烧伤疤痕清晰可见(图 6 ),并由 RBR 值高于 0.1 的像素显示。在受影响的区域内,相当多的像素被归类为低严重度烧伤区。这可能是由于两幅图像之间的时间差造成的。一年中的这个时候相对干燥,一些植被可能已经退化,导致 RBR 值的变化幅度类似于轻度烧伤。

图 6-2019 年 Bandipur 野火的烧伤严重程度分类
估计总烧伤面积
在我们的分析中,RBR 波段可以进一步处理,以创建一个新的波段,其中包含与轻度烧伤或更严重烧伤相对应的像素(图 7)。基于该阈值,总烧伤面积可以简单地计算为所有像素占据的面积乘以水平(经度)和垂直(纬度)轴上的分辨率。为了计算我们研究区域的总烧伤面积,我们使用以下公式:


图 7——谷歌地球图片显示了 Bandipur 野火的受灾地区。亮色像素代表比周围深色像素燃烧强度更高的区域。
2019 年 2 月 18 日至 2 月 28 日期间,从哨兵 2 衍生的 RBR 估计的总燃烧面积为 17,501 英亩或约 71 平方英尺。公里。这里估计的面积大大高于 ISRO 报告的面积。它与其他独立权威机构和研究机构做出的估计4、5、6相比相当不错。
结论
野火会对森林生态系统造成严重破坏。卫星为监测大规模火灾提供了一种经济有效的方法。近年来,由于地球观测遥感任务提供的中高空间分辨率光学卫星数据越来越多,研究人员和火灾管理官员得以进行详细的火灾后评估。本文展示了归一化燃烧比(NBR)作为一个有用的指数的效用,用于使用来自哨兵 2 任务的数据描绘火灾影响区域和燃烧区域评估。总之,遥感和地理信息系统技术可以为了解野火对森林生态系统的影响提供重要的信息。这对全球大规模森林火灾的有效监测、规划和潜在预防管理具有重要意义。
承认
本分析中使用的预处理和图像采集技术改编自 RUS 哥白尼(https://rus-copernicus.eu/portal/)培训材料(代码: HAZA02 )。
参考
- Roy,P.S .,2003,《利用卫星遥感和地理信息系统进行森林火灾和退化评估》,卫星遥感和 GIS 在农业气象学中的应用,361–400。
- McFeeters,s . k ,, 1996,“归一化差异水指数(NDWI)在开阔水域特征描述中的应用”,《国际遥感杂志》, 17:7,1425–1432。
- 巴夏礼,美国等。,2014,一种量化烧伤严重程度的新指标:相对烧伤率,遥感,6:3,1827–1844。
使用 Data Studio 监控您的 BigQuery 成本并报告使用情况
简单有效的仪表板。具有实际的报告名称、用户和标签。包含方便的模板

BigQuery 和 Data Studio 成本监控仪表板。(图片作者)
如果您是一个 BigQuery 用户,并且您使用 Data Studio 可视化您的数据,那么您可能想要回答以下问题:
- 昨天每个数据工作室报告的成本是多少?
- 每个查询/报告运行了多少次,由谁运行?
- 查询带有标签 X 的表、数据集(例如生产/暂存/分析)的成本是多少?
- 如果计费字节数突然激增,我能得到通知吗?
标准的谷歌计费仪表板不会回答这些问题。
根据谷歌官方文件,目前你不能为 BigQuery 工作使用标签。
在这里阅读更多:https://cloud.google.com/bigquery/docs/filtering-labels
但是,如果我们想知道到底是谁运行了这个查询,以及它花费了我们多少钱呢?
在本文中,我将向您介绍设置 BigQuery 成本监控仪表板的完整过程。
我使用了谷歌数据工作室的标准谷歌广告模板。我觉得它看起来不错,我根据自己的需要稍微做了些改动。

(图片作者)
如果你愿意,你可以复制 模板 ,包括所有相关的设置和部件。如下面的bq_cost_analysis.sql所示,只需调整每个小工具的数据源(创建自己的数据源)
先决条件:
- 谷歌云平台 (GCP)账号
- BigQuery
- 数据工作室
在本文中,我将使用云审计日志进行 BigQuery。我们将使用这些事件数据,将其导出到 BigQuery 并进行分析。
Google 推荐的最佳实践是使用基于自定义日志的指标。
转到 记录 并创建一个接收器。点击阅读更多关于创建水槽的信息。

(图片作者)
这将把来自云审计日志服务的所有 BigQuery query_job_completed 日志事件输出到您的 BigQuery 表中。
通过这种方式,您可以导出任何日志以供进一步分析,我发现这非常有用。
请注意,只会导出新日志,但是您也可以在日志控制台中过滤和缩小您正在调查的日志:

json 输出。(作者图片)
因此,现在当我有 BigQuery 事件数据流入我的logs数据集时,我将基于我拥有的数据创建一个视图:

(图片作者)
这就是我们需要计算大查询成本(每个查询或作业)的地方:

TotalBilledBytes。(图片作者)
我们可以简单地乘以:cost per TB processed * numbers of TB processed。
数据源
这是一个自定义查询bq_cost_analysis.sql:
现在我们已经将processedBytes与相关的cost per user和per query很好地组合在一起:

BigQuery 结果。(图片作者)
你可能注意到我在bq_cost_analysis.sql中添加了一个查询标签
这是一个小技巧,将有助于成本分组,并让我们使用实际的标签(目前不支持)。
只需调整 SQL 并为您tagged查询添加一列。它可以是任何东西,数据工作室中的一个报告,表名或者一个预定查询:
**substr(**protopayload_auditlog.servicedata_v1_bigquery.jobCompletedEvent.job.jobConfiguration.query.query,0,30**) as report**
或者类似这样的事情可能会更好:
select REGEXP_EXTRACT(‘ --tag: some report — tag ends/’, r’--tag: (.*) - tag ends/’) **as report**

Data Studio / BigQuery 每个用户/报告的成本。(图片作者)
当然,您也可以通过查询来汇总成本。

Data Studio 中每次查询的成本。(图片作者)
如果您将底部的表格设置为每页只显示一条记录 ( 一个查询),这将使查询成本分析变得非常简单。
添加小时和分钟的滑块也非常有用。
另一件有趣的事。缓存。如果您想省钱,您应该在 Data Studio 报告中启用预取缓存,您必须使用owner's credentials并启用缓存选项(数据新鲜度)。在这里阅读更多关于缓存的信息:https://cloud.google.com/bigquery/docs/cached-results

Data Studio 预取缓存。(图片作者)
在这种情况下,如果您与任何其他用户共享对您的报告的访问权限,他们将使用您的凭据来访问数据,您将在您的成本报告中看到的只是您的帐户名称。
如果你想为你的成本报告启用其他用户,你应该使用
Viewer's credentials并授予他们访问底层数据集的权限。
我发现这真的很烦人,但这就是 Data Studio 的工作方式。
在这里阅读更多谷歌官方文档:https://developers . Google . com/data studio/solution/blocks/enforce-viewer-creds。
为各个 Data Studio 用户创建角色
如果您在 Data Studio 中使用查看者凭据,您将需要它。
让我们转到 IAM 角色,并创建名为 Data Studio 用户的角色。为此,我们需要对数据集中的view和list表的权限。您可以简单地添加预定义的 BigQuery 数据查看器角色。
对于来自 Data Studio 的run报告,我们还需要添加 bigquery.jobs.create 权限的 BigQuery 作业用户角色。
在这里阅读更多关于预定义角色的信息:https://cloud.google.com/bigquery/docs/access-control

创建 Data Studio 用户角色。(图片作者)
现在您可以将这个角色分配给任何用户,让他们在 Data Studio 中使用查看者的凭证。只需点击数据集名称,然后点击共享数据集。当只授予最小范围的权限时,这符合 Google 最佳安全实践。

授予权限。(图片作者)
创建计费预警
我还希望在计费字节数突然激增时得到通知。
要创建此Alert,首先进入仪表盘,点击添加图表并选择扫描字节计费:

正在为 BilledBytes 创建 BigQuery 使用率警报。(图片作者)
选择聚合器和对准器到求和。一个聚合器描述了如何在多个时间序列上聚合数据点,以及在分组中使用标签的情况下使用。对齐器描述如何将每个单独时间序列中的数据点放入相等的时间段。阅读更多关于校准器的信息。

设置对准器和协议器。(作者图片)
创建警报策略
现在让我们转到 Google 警报并创建一个策略:

设置警报策略。(图片作者)
我们称之为“过去 15 分钟计费字节数超过 1Gb”,这样,在任何 15 分钟窗口内,每当计费字节总数超过 1Gb 时,我们都会收到警报:

警报策略。(图片作者)
现在,如果我们运行任何查询(包括 Data Studio 报告),并且在过去 15 分钟内总计费字节数的总和超过阈值,系统将会通知我们。
让我们运行一个超过这个阈值的查询,看看是否可行:

测试。(图片作者)
您应该会收到一封电子邮件提醒。成功了:

谷歌快讯。(图片作者)
例如,您也可以以类似的方式为正在使用的插槽数量和查询执行时间设置警报。
在这里阅读更多:https://cloud . Google . com/monitoring/alerts/using-alerting-ui # create-policy
就是这样!
现在,您有了一个漂亮的成本监控仪表板,可以在其中看到与每个 Data Studio 报告相关联的 BigQuery 成本,使您可以在 Google Cloud Billing 中更深入地了解基本数据集级别的报告。
感谢您的阅读,如果您有任何问题,请告诉我。
推荐阅读:
- https://cloud . Google . com/blog/products/data-analytics/taking-a-a-practical-approach-to-big query-cost-monitoring
- https://cloud.google.com/bigquery/docs/access-control?_ga=2.51607977。-1819168026
- https://cloud.google.com/logging/docs/export
- https://cloud.google.com/bigquery/docs/monitoring
- 【https://cloud.google.com/bigquery/docs/controlling-costs
- https://cloud.google.com/bigquery/docs/best-practices-costs
- https://medium . com/Google-cloud/visualize-GCP-billing-using-big query-and-data-studio-d3e 695 f 90 c 08
- https://support.google.com/cloud/answer/7233314?hl=en
- https://cloud . Google . com/billing/docs/how-to/export-data-big query-setup?hl=en
- https://cloud.google.com/bigquery/docs/cached-results
监控你的机器学习模型

作者图片
MLOps
部署您的 ML 模型时需要注意什么
在过去的几年里,机器学习和人工智能已经越来越成为利用数据的组织的主要内容。随着这种成熟,出现了需要克服的新挑战,例如部署和监控机器学习模型。
尽管部署和监控软件一直是一种久经考验的实践,但对于机器学习模型来说,这样做却有很大不同。监控您的模型可以帮助您了解随着时间的推移您的预测有多准确。
通过监控模型来防止错误的预测
从个人经验来看,似乎有许多组织,主要是中小企业,现在正面临着机器学习的这个生产阶段的挑战。
本文将重点关注在生产中监控你的机器学习模型。然而,如果您不熟悉部署您的模型,我建议您看看下面的文章,让您快速上手:
用 FastAPI+uvicon 创建生产就绪的 API
towardsdatascience.com](/how-to-deploy-a-machine-learning-model-dc51200fe8cf)
注意:没有一种正确的方法可以监控你的模型。本文仅作为设计监控框架时的参考。您的里数可能因您正在使用的解决方案而异。
1.为什么要监控你的模型?
所有生产软件都容易出故障,每个软件公司都知道监控其性能以防止问题的发生是很重要的。通常,我们监控 软件 本身的质量,而在机器学习的环境中,人们可能更关注监控 预测 的质量。
并非所有数据都是平等的
您希望监控您的模型有几个原因:
- 模型和输入数据之间的关系会发生变化
- 数据的分布发生了变化,使得模型的代表性降低
- 衡量标准和/或用户群的变化改变了变量的潜在含义
漂移
根据以上原因,通常可以在漂移中找到原因。从本质上来说,漂移是数据的统计属性发生变化的现象,这种现象会导致您的预测随着时间的推移而下降。换句话说,由于数据总是在变化,漂移自然会发生。

以网上商店的顾客数据为例。预测模型可以使用诸如他们的个人信息、购买行为和花在广告上的钱等特征。随着时间的推移,这些特征可能不能代表它们最初被训练时的特征。
漂移通常被称为概念漂移、模型漂移或数据漂移
监控您的模型以确保输入的数据与训练时使用的数据相似是很重要的。
为什么不重新训练你的模特呢?
虽然重新训练一个模型听起来总是一个好主意,但它可能比这更微妙!
如果你没有及时的标签怎么办?如果你的预测是几个星期后的事,那么在实践中验证你的模型将会很困难,因为你需要等待几个星期才能得到事实真相。
或者…如果您自动重新训练您的模型,只是让它运行 10 个小时,然后在第二天重新训练它,会怎么样?这可能会花费组织大量的计算时间,但却有一点积极的影响。
或者…如果您以在线方式重新训练一个模型,但由于只关注新添加的数据而导致其性能下降,该怎么办?
盲目地重新训练一个模型会导致更多的成本,时间的浪费,甚至是一个更差的模型。通过监控你的模型,你可以更精确地决定再培训的最佳方法和时间。
2.监视
监控您部署的模型的方法有很多种。实际上,这主要取决于您的应用程序、模型类型、性能度量和数据分布。但是,在监控模型时,有几件事是您最常看到的。
监控预测

从这里检索。
如果你幸运的话,在你的预测和你的目标预测之间几乎没有时间。换句话说,在做了一个预测之后,你很快就会有真正的标签。在这种情况下,只需监控标准绩效指标,如准确性、F1 评分和 ROC AUC。
但是,当您没有及时的标签时,就不能使用性能测量(例如,疾病预测),您必须寻找其他方法:
- 预测分布*
您可以使用回归和分类任务中预测的分布和频率来跟踪新的预测集是否与训练数据具有相似的分布。显著的偏差可能表明性能下降。 - 预测概率 很多机器学习模型都可以输出分类概率。这些表明一个模型有多“确定”这是正确的预测。如果这些概率相对较低,那么该模型可能会在部署中遇到困难。
*注意:如果你想比较分布,那么我们通常会关注统计测试,比如学生 t 检验或非参数 Kolmogorov Smirnov 。这个可能会帮助你为你的数据选择正确的统计检验。
监控输入

从这里的检索。
如果您有及时的标签或者没有,那么监视您的模型的输入通常是一个好的实践。这有助于您了解通常输入到模型中的内容,并帮助您跟踪预测的恶化情况。
方法包括:
特征分布 与预测一样,您可以使用分布输入数据来跟踪它是否具有与训练数据相似的分布。如果您发现显著差异,这可能表明您的培训数据并不代表您在生产中发现的数据。
异常值检测 根据您的预处理步骤,您通常不允许在预测模型中使用某些值。变量中类别的数量可能会随着时间的推移而增加,这是您的模型所没有预料到的。
人工监控

此处检索到。
在自动化时代,我为什么要建议人工监控呢?尤其是当人类的行为对错误极其敏感的时候?
嗯……虽然人工监控应该是最后的手段,但在某些情况下,让人类看一看模型生成的预测是有益的。如果一个被监控的预测即使有超过 90%的概率也是不准确的,那么如果这些异常能被人看到就好了。
当你在实践中没有预测的基础事实标签时,人类的监控甚至可能是必要的。
3.阴影模式
每当您想要部署一个重新训练的模型时,您可能想要推迟将其部署到生产环境中。相反,以影子模式部署它可能更好。影子模式是一种通过新训练的模型运行生产数据的技术,无需将预测反馈给用户。

由林奈创作
阴影模式允许您同时运行两个模型,同时在生产环境中测试新模型的性能。在实际部署到生产环境之前,您可以存储其预测以监控其行为。
此外,您可以使用此模式来检查模型是否按预期工作,而无需模拟生产环境,因为它在技术上是生产环境的副本。
感谢您的阅读!
如果你和我一样,对人工智能、数据科学或心理学充满热情,请随时在 LinkedIn 上添加我,或者在 Twitter 上关注我。
有关生产中的机器学习的更多信息,请单击以下帖子之一:
用 FastAPI+uvicon 创建生产就绪的 API
towardsdatascience.com](/how-to-deploy-a-machine-learning-model-dc51200fe8cf) [## 借助 Streamlit 快速构建和部署应用
将您的 Streamlit 应用程序部署到 Heroku,展示您的数据解决方案
towardsdatascience.com](/quickly-build-and-deploy-an-application-with-streamlit-988ca08c7e83) [## 数据科学家的单元测试
使用 Pytest 提高管道的稳定性
towardsdatascience.com](/unit-testing-for-data-scientists-dc5e0cd397fb)
面向自动驾驶的单目鸟瞰语义分割
2020 年 BEV 语义分割综述
更新:
- 添加 BEV feat 缝线,2021/01/31
- 添加 PYVA,2021/10/01
- 添加全景 BEV,2021/10/04
- TODO:添加 BEV-Seg , CaDDN , FIERY ,HDPE pnet。
我还写了一篇关于 BEV 物体检测的更新博文,尤其是关于变形金刚。
截至 2021 年末的学术文献和行业实践综述
towardsdatascience.com](/monocular-bev-perception-with-transformers-in-autonomous-driving-c41e4a893944)
自动驾驶需要自我车辆周围环境的准确表示。环境包括静态元素,如道路布局和车道结构,也包括动态元素,如其他汽车、行人和其他类型的道路使用者。静态元素可由包含车道等级信息的高清地图捕捉。
有两种类型的映射方法,离线和在线。离线映射以及深度学习在离线映射中的应用,请参考我之前的帖子。在没有地图支持或无人驾驶汽车从未去过的地方,在线地图会很有用。对于在线绘图,一种传统的方法是 SLAM(同步定位和绘图),它依赖于对一系列图像的几何特征的检测和匹配,或者利用物体的附加概念的变形。
截至 2020 年的文献综述
towardsdatascience.com](/deep-learning-in-mapping-for-autonomous-driving-9e33ee951a44)
这篇文章将关注另一种在线制图的方式——鸟瞰(BEV)语义分割。与 SLAM 相比,SLAM 需要来自同一移动摄像机的一系列图像,BEV 语义分割基于多个摄像机同时观察车辆的不同方向捕获的图像。因此,与 SLAM 相比,它能够从一次性收集的数据中生成更多有用的信息。此外,当 ego 汽车静止或缓慢移动时,BEV 语义分割仍将工作,而 SLAM 将表现不佳或失败。

BEV 语义分段与 SLAM 的输入差异(图片由本文作者提供)
为什么选择 BEV 语义地图?
在一个典型的自动驾驶堆栈中,行为预测和规划通常以这种自上而下的视图(或鸟瞰图,BEV) 完成,因为高度信息不太重要,自动驾驶汽车需要的大部分信息可以方便地用 BEV 表示。这个 BEV 空间可以被不严格地称为 3D 空间。(例如,BEV 空间中的对象检测通常被称为 3D 定位,以区别于成熟的 3D 对象检测。)
因此,标准做法是将高清地图光栅化为 BEV 图像,并在行为预测规划中与动态对象检测相结合。最近探索这一策略的研究包括 IntentNet (优步·ATG,2018 年)司机网 (Waymo,2019 年)交通规则 (Zoox,2019 年) Lyft 预测数据集 (Lyft,2020 年),等等。

最近的研究将高清地图渲染为 BEV 语义地图(图片由本文作者编辑,来源于参考出版物)
诸如对象检测和语义分割的传统计算机视觉任务涉及在与输入图像相同的坐标框架中进行估计。因此,自动驾驶的感知堆栈通常发生在与车载摄像头图像相同的空间——透视视图空间。

感知发生在图像空间(左: SegNet )而规划发生在 BEV 空间(右: NMP ) ( 来源)
感知中使用的表示与预测和规划等下游任务之间的差距通常在传感器融合堆栈中弥合,该堆栈通常在雷达或激光雷达等主动传感器的帮助下,将透视空间中的 2D 观测提升到 3D 或 BEV。也就是说,使用 BEV 表示对跨模态感知是有益的。首先,它是可解释的,并有助于调试每种传感模态的固有故障模式。它也很容易扩展到其他新的形式,并简化了后期融合的任务。此外,如上所述,这种表示中的感知结果可以很容易地被预测和计划堆栈使用。
将透视 RGB 图像提升到 BEV
来自主动传感器(如雷达或激光雷达)的数据有助于 BEV 表示,因为测量在 3D 中是固有的度量。然而,由于全景相机传感器的普遍存在和低成本,具有语义意义的 BEV 图像的生成最近吸引了很多关注。
在这篇文章的标题中,“单目”是指管道的输入是从单目 RGB 相机获得的图像,没有显式的深度信息。自主车辆上捕获的单目 RGB 图像是 3D 空间的透视投影,将 2D 透视观察提升到 3D 的逆问题是固有的不适定问题。
挑战,IPM 及其他
BEV 语义分割的一个明显挑战是视图转换。为了正确地恢复 3D 空间的 BEV 表示,该算法必须利用硬的(但可能有噪声的)几何先验,例如相机的内在和外在先验,以及软的先验,例如道路布局的知识集和常识(车辆在 BEV 中不重叠,等等)。传统上,逆透视映射(IPM) 一直是该任务的常用方法,假设平地假设和固定摄像机外部。但是,当相机外部条件变化时,这项任务不适用于不平坦的表面或崎岖不平的道路。

IPM 会在汽车等 3D 物体周围产生伪像(来源)
另一个挑战在于为这样的任务收集数据和注释。一种方法是让无人机一直跟随自动驾驶汽车(类似于 MobileEye 的 CES 2020 talk ),然后要求人类标注语义分段。这种方法显然不具有实用性和可扩展性。许多研究依赖于合成数据或不成对的地图数据来训练提升算法。
在接下来的会议中,我将回顾该领域的最新进展,并强调其共性。根据使用的监控信号,这些研究可以大致分为两种类型。第一类研究借助模拟进行间接监管,第二类研究直接利用最近发布的多模态数据集进行直接监管。
模拟和语义分割
该领域的开创性研究使用模拟来生成必要的数据和注释,以将透视图像提升到 BEV 中。为了弥合模拟到现实(sim2real)领域的差距,许多人使用语义分割作为中间表示。
VPN(查看解析器网络,RAL 2020)
VPN(用于感知周围环境的跨视角语义分割)是最早探索 BEV 语义分割的作品之一,并将其称为“跨视角语义分割”。视图解析网络(VPN)使用视图转换器模块来模拟从透视图到 BEV 的转换。这个模块被实现为一个多层感知器(MLP ),它将 2D 物理范围扩展到一个 1D 向量,然后在其上执行完全连接的操作。换句话说,它忽略了强几何先验,而是纯粹采用数据驱动的方法来学习透视到 BEV 扭曲。这种扭曲是特定于摄像机的,每个摄像机必须学习一个网络。

视图解析网络的整体管道(来源
VPN 使用合成数据(用 CARLA 生成)和对抗性损失进行训练期间的域适应。此外,它使用语义遮罩作为中间表示,没有照片级的纹理间隙。
视图转换器模块的输入和输出具有相同的大小。该论文提到,这使得它很容易插入到其他架构中。在我看来,这实际上是完全没有必要的,因为透视图和 BEV 本质上是不同的空间,因此不需要强制相同的像素格式,甚至输入和输出之间的纵横比也不需要。代码可在 github 上获得。
渔网(CVPR 2020)
渔网将激光雷达、雷达和相机融合转换成 BEV 空间中的单一统一表示。这种表示使得跨不同模态执行后期融合更加容易。视图转换模块(视觉路径中的紫色块)类似于基于 MLP 的 VPN。视图变换网络的输入是一系列图像,但它们只是在通道维度上连接起来并馈入网络,而不是利用 RNN 结构。

渔网使用 BEV 表示来促进跨传感器模态的后期融合(来源
地面实况生成使用激光雷达中的 3D 注记,主要关注动态对象,如车辆和 VRU(易受影响的道路使用者,如行人和骑自行车的人)。其余的都由一个背景类表示。
BEV 语义网格的分辨率为 10 厘米和 20 厘米/像素。这比离线映射中使用的典型值 4 或 5 厘米/像素要粗糙得多。遵循 VPN 的惯例,图像的尺寸与 192 x 320 的输出分辨率相匹配。CVPR 2020 的演讲可以在 Youtube 上找到。
ICRA 2019 年奥运会
VED(具有卷积变分编码器-解码器网络的单目语义占据网格映射)开发了用于语义占据网格映射预测的变分编码器-解码器(VED)架构。它对驾驶场景的前视视觉信息进行编码,并随后将其解码为 BEV 语义占用网格。

用于 VED 中视图变换的可变编码器-解码器网络(来源
此地面实况是使用城市景观数据集中立体匹配的视差图生成的。这个过程可能会有噪声,这实际上促使使用 VED 和从潜在空间采样,以使模型对不完美的 GT 具有鲁棒性。然而,由于是 VAE,它通常不会产生尖锐的边缘,这可能是由于高斯先验和均方误差。
输入图像和输出图像分别为 256×512 和 64×64。 VED 利用了 vanilla SegNet(传统语义分割的相对强大的基线)的架构,并引入了一个 1 x2 池层,以适应输入和输出的不同纵横比。
学习观察周围的物体(ECCV 2018)
学习环视物体寻找室外场景的俯视图表示在 BEV 中产生遮挡区域的幻觉,并利用模拟和地图数据来帮助。
我个人认为,这是 BEV 语义分割领域的一篇开创性论文,但它似乎没有受到太多关注。也许它需要一个朗朗上口的名字?

学习环视物体的整体流程(来源
通过逐像素深度预测和投影到 BEV 来完成视图变换。这部分克服了 BEV 空间中缺乏训练数据的问题。这也在后面的工作中完成,如下面审查的提升、拍打、射击(ECCV 2020) 。
论文用来学习幻觉(预测遮挡部分)的技巧相当惊人。对于 GT 深度很难找到的动态对象,我们过滤掉损失。随机屏蔽图像块,并要求模型产生幻觉。用亏损作为监督信号。

忽略动态区域,但明确删除静态区域以直接监控幻觉(源)
由于在 BEV 空间中很难获得显式成对的监督,因此本文使用对抗性损失来指导具有模拟和 OpenStreetMap 数据的学习,以确保生成的道路布局看起来像真实的道路布局。这个技巧也用在后面的作品中,如monola layout(WACV 2020)。
它在图像空间中使用一个 CNN 进行深度和语义预测,将预测提升到 3D 空间并在 BEV 中渲染,最后在 BEV 空间中使用另一个 CNN 进行细化。BEV 中的这个细化模块还被用在很多其他作品中,如 Cam2BEV (ITSC 2020)和 Lift,Splat,Shoot (ECCV 2020) 。
Cam2BEV (ITSC 2020)

在 Cam2BEV 中输入语义分割图像和预测的 BEV 图(来源
Cam2BEV (一种 Sim2Real 深度学习方法,用于将来自多个车载摄像头的图像转换为鸟瞰图中语义分割的图像)使用具有 IPM 的空间转换器模块将透视特征转换到 BEV 空间。神经网络架构接收由不同摄像机捕获的四幅图像,并在将它们连接在一起之前对每幅图像应用 IPM 变换。

Cam2BEV 使用确定性 IPM 来转换中间特征图(来源)
Cam2BEV 使用从 VTD (虚拟试驾)模拟环境生成的合成数据。它采用了四个语义分割图像,重点放在提升过程中,避免了处理 sim2real 域的空白。
Cam2BEV 具有相当集中的范围和许多设计选择,这使得它非常实用。首先,它只在语义空间起作用,因此避免了 sim2real domain gap 的问题。它有一个预处理阶段,故意掩盖遮挡区域,以避免对遮挡进行推理,并可以使问题更容易处理。为了简化提升过程,它还将“单应图像”作为输入,该单应图像由语义分割结果的 IPM 生成并连接成 360 deg 图像。因此,Cam2BEV 的主要目标是推理 BEV 中 3D 对象的物理范围,其在单应图像中可能被拉长。

预处理后的单应图像和目标 GT 供神经网络在 Cam2BEV 中预测(来源)
Cam2BEV 的目标是校正 IPM,但在某种意义上,IPM 会扭曲 3D 对象,如不在路面上的汽车。然而,它仍然不能处理不平坦的路面或行驶过程中的倾斜度变化。Cam2BEV 的输入输出都是 256x512 像素。代码可在 github 中获得。它还提供了一个很好的 IPM 基线实现。
你所需要的是(多模态)数据集
最近发布的许多多模态数据集( Lyft 、 Nuscenes 、 Argoverse 等)使得直接监督单目 BEV 语义分割任务成为可能。这些数据集不仅提供 3D 对象检测信息,还提供高清地图以及定位信息,以在高清地图上的每个时间戳精确定位 ego 车辆。
BEV 分割任务有两个部分,即(动态)对象分割任务和(静态)道路布局分割任务。对于对象分割,3D 边界框被光栅化到 BEV 图像中以生成注释。对于静态道路布局,基于所提供的定位结果将地图转换到 ego 车辆框架中,并光栅化为 BEV 注释。
单层布局(WACV 2020)
monola layout:单幅图像的 Amodal 场景布局重点是将单个摄像机提升到语义 BEV 空间。本文的重点是研究造成遮挡区域的模型完成情况。似乎受到了学习观察周围物体的严重影响(ECCV 2018) 。

MonoLayout 在 KITTI(左)和 Argoverse (右)的闭塞区域产生幻觉(来源
视图变换通过编码器-解码器结构来执行,并且潜在特征被称为“共享上下文”。两个解码器用于分别解码静态和动态类。作者还报告了在消融研究中使用组合解码器处理静态和动态对象的负面结果。

MonoLayout 的编解码设计(来源
虽然高清地图 groundtruth 在 Argoverse 数据集中可用,但 MonoLayout 选择仅将其用于评估,而不是用于训练(事后诸葛亮还是有意的设计选择?).对于训练,MonoLayout 使用时间传感器融合过程,通过将整个视频中的 2D 语义分割结果与定位信息聚合来生成弱背景真相。它使用 monodepth2 将 RGB 像素提升为点云。它还会丢弃距离自我车 5 米远的任何东西,因为它们可能会很吵。为了鼓励网络输出可以想象的场景布局,MonoLayout 使用了对抗性特征学习(类似于在 学习环视物体 中使用的)。先验数据分布从 OpenStreetMap 获得。
MonoLayout 的空间分辨率为 30 厘米/像素,因此 128 x 128 的输出相当于 BEV 空间中的 40 米 x 40 米。代码可在 github 中获得。
PyrOccNet (CVPR 2020)
PyrOccNet :使用金字塔占据网络从图像预测语义图表示从单目图像预测 BEV 语义图,并使用贝叶斯过滤将它们融合成连贯视图。

PyrOccNet 在 NuScenes 数据集上的演示结果(来源)
PyrOccNet 中视图转换的核心组件是通过密集转换器模块执行的(注意,该转换器不是基于注意力的!)。它似乎受到了来自同一作者的 OFT (BMVC 2019) 的极大启发。OFT 沿着投射回 3D 空间的光线在像素位置均匀地涂抹特征,这非常类似于计算断层摄影中使用的背投算法。PyrOccNet 中的密集变压器模块通过使用 FC 层沿深度轴扩展而更进一步。实际上,在 BEV 空间中,有多个密集的变压器以不同的比例工作,集中在不同的距离范围。
密集的变压器层的灵感来自于这样的观察:虽然网络需要大量的垂直上下文来将特征映射到鸟瞰图(由于遮挡、缺乏深度信息和未知的地面拓扑),但是在水平方向上,BEV 位置和图像位置之间的关系可以使用简单的相机几何结构来建立。—来自 PyrOccNet paper

密集变压器的 PyrOccNet 架构图(来源
训练数据来自于 Argoverse 数据集和 nuScenes 数据集的多模态数据集,其中既有地图数据,也有 3D 物体检测地面真实
PyrOccNet 使用贝叶斯过滤以连贯的方式融合跨多个摄像机和跨时间的信息。它从二元贝叶斯占用网格的旧思想中汲取灵感,并增强网络输出的可解释性。时间融合结果非常类似于映射过程,并且非常类似于monola layout中用于生成弱 GT 的“时间传感器融合”过程。

仅静态类的 PyrOccNet 的时间融合结果(来源)
PyrOccNet 的空间分辨率为 25 厘米/像素,因此 200 x 200 的输出相当于 BEV 空间中的 50 米 x 50 米。代码将在 github 中提供。
举起,拍打,射击(ECCV 2020)

nuScenes 数据集上的演示(来源)
提升、拍打、拍摄:通过隐式反投影到 3D 对来自任意相机装备的图像进行编码为视图变换执行密集的逐像素深度估计。它首先使用每个摄像机的 CNN 来执行概率性的像素深度预测,以将每个透视图像提升到 3D 点云中,然后使用摄像机 extrinsics 来绘制 BEV。最后,使用 BEV CNN 来改进预测。“拍摄”部分意味着路径规划,将被跳过,因为它超出了本文的范围。

提升和飞溅管道(左)和概率深度预测(右)(来源
通过预测 RGB 图像中像素的深度分布,提出了概率 3D 提升。某种程度上统一了伪激光雷达 (CVPR 2019)的一热提升和 OFT (BMVC 2019)的统一提升。其实这种“软”预测是 可微分渲染 中常用的一招。伪激光雷达 v3 (CVPR 2020)的并发工作也使用了这种软光栅化技巧,使深度提升和投影变得可区分。
训练数据来自于 Lyft 数据集和 nuScenes 数据集的多模态数据集,其中既有地图数据,也有 3D 物体检测地面真实。
Lift-Splat-Shoot 输入分辨率为 128x352,BEV 网格为 200x200,分辨率为 0.5 m/pixel = 100m x 100m。代码可在 github 中找到。
BEV 特征拼接
PyrOccNet 和Lift-Splat-shot都专注于将来自多个相机的同步图像拼接成一个连贯的 360 度视图。 BEV 特征拼接 ( 使用机载单目摄像机理解鸟瞰语义高清地图)将单目视频(具有估计的自我姿态)融合成连贯的前视。

BEV 特征拼接使用 BEV 时间聚合将单目视频转换成 BEV 图
BEV 特征拼接取单目视频(一系列图像)作为模型的输入。为了融合来自多个帧的信息,它引入了 BEV 时间聚合模块。该模块首先投影中间特征图,然后将具有自我姿态(从里程计管道估计)的特征图聚集到连贯和扩展的 BEV 空间中。此外,每个图像帧的中间特征在摄像机空间中用重新投影的 BEV 地面真实来监督。
BEV 特征拼接具有 200x200 像素的 BEV 网格,分辨率为 0.25 m/pixel = 50m x 50m。
PYVA :用心投射你的观点(CVPR 2021)
由于卷积层的局部受限感受野,CNN 很难直接拟合视图投影模型。因此,大多数基于 CNN 的方法明确地将视图变换编码到 CNN 架构中(具有相机外切的特征地图的单应变换等)。
另一方面,由于全局注意机制,变形金刚更适合做这项工作。 PYVA (
在 VPN 之后,PYVA 使用 MLP 将投影图像特征提升到 BEV 中。然后,它使用交叉注意力转换器来进一步细化提升。提升后的 BEV 特征 X’用作查询,图像特征 X(或循环重投影的图像特征 X”)用作键和值。
使用交叉注意力转换器模块背后的思想是直观的:对于查询中的每个像素(BEV 特征),网络应该关注键(图像特征)中的哪个像素?不幸的是,本文没有展示 transformer 模块内部的一些注意力地图的例子,这些例子可以很好地说明这种直觉。

PYVA 使用交叉视图转换器模块将前视图提升到 BEV 中
PYVA 遵循monola layout的思想,利用对抗性训练损失,促使解码器输出更合理的道路布局和车辆位置。PYVA 更进一步,不仅鼓励网络输出合理的车辆位置,还输出道路和车辆之间合理的关系,因为道路布局在预测车辆位置和方向之前提供了有用的信息(例如,汽车更有可能停在路边)。
全景 BEV
全景 BEV ( 使用单目正视图图像的鸟瞰全景分割)正确地指出实例的概念对下游是至关重要的。 FIERY 将语义分割思想扩展到实例分割,全景 BEV 更进一步,在 BEV 空间进行全景分割。
Panoptic BEV 使用带有 BiFPN(如 EfficientDet)颈部的 ResNet 主干。在每个级别(P2-P5),图像特征通过密集转换器模块被投射到 BEV 中(注意,这个转换器不是基于注意力的!)。

全景 BEV 的网络架构(transformer her 不是基于注意力的)
在每个密集变压器中,有一个明显的垂直变压器和一个平面变压器。首先,输入图像被二进制语义分割成垂直掩码和水平掩码。被垂直屏蔽屏蔽的图像被馈送到垂直变换器。垂直转换器使用体积晶格来模拟中间 3D 空间,然后将其展平以生成垂直 BEV 特征。这种立式变压器与升降拍击的变压器非常相似。扁平变压器使用 IPM,后接误差校正模块(ECM ),以产生扁平 BEV 特征。这很大程度上遵循了 PyrOccNet 的思路。

密集变压器提升图像到 BEV,并有一个垂直变压器和平面变压器
通过将 IPM 的先验信息注入到总升降管道中,整个设计似乎是建立在升降拍击和 PyrOccNet 之上。模块设计看起来确实很合理,但它们感觉太手工制作和复杂,一旦模型可以访问足够的数据,可能不会导致最佳性能。
从将视图转换模块命名为“密集转换器”和引用“垂直特征”来看, PyrOccNet 的影响也很明显。从演示视频看,结果很有希望。将在 Github 上发布的代码。
局限性和未来方向
尽管在 BEV 语义分割方面已经取得了很大进展,但是在它被广泛部署到生产系统之前,我认为还存在几个关键的差距。
首先,对于动态参与者来说,还没有实例的概念。这使得在行为预测中很难利用动态对象的先验知识。例如,汽车遵循某种运动模型(如自行车模型),未来轨迹的模式有限,而行人往往具有更多随机运动。许多现有的方法倾向于在语义分割结果中将多个汽车连接成一个连续的区域。
动态语义类不能被重用,并且在很大程度上是“一次性的”信息,而 BEV 图像中的静态语义类(例如道路布局和道路上的标记)可以被视为“在线地图,并且应该被收获和回收。如何聚合多个时间戳上的 BEV 语义分段来估计一个更好的映射是另一个需要回答的关键问题。monola layout和 PyrOccNet 中的时间传感器融合方法可能有用,但需要针对 SLAM 等传统方法进行基准测试。
如何将线上的逐像素语义图转换成轻量级结构化图以备将来重用。为了不浪费宝贵的车载地图周期,在线地图必须转换成某种格式,以便 ego 汽车或其他汽车在未来可以有效地利用。
外卖食品
- 视图变换:现有的许多工作忽略了相机外部的强几何先验信息。应该避免这种情况。 PyrOccNet 和 Lift-Splat-shot 看起来方向正确。
- 数据和监管:2020 年之前的大部分研究都是基于仿真数据,并使用语义分割作为中间表示,以弥合 sim2real 领域的差距。最近的工作利用多模态数据集对任务进行直接监督,取得了非常有希望的结果。
- 我确实觉得 BEV 空间中的感知是感知的未来,尤其是借助可微分渲染,可以将视图变换实现为可微分模块,并插入端到端模型,直接将透视图像提升到 BEV 空间中。
参考
-
VPN :感知周围环境的跨视角语义分割,RAL 2020
-
VED :卷积变分编解码网络的单目语义占据网格映射,ICRA 2019
-
Cam2BEV :一种模拟现实深度学习方法,用于将来自多个车载摄像头的图像转换为鸟瞰图中的语义分割图像,ITSC 2020
-
BEV-Seg :利用几何和语义点云进行鸟瞰语义分割,CVPR 2020 研讨会
-
学习环视物体寻找户外场景的俯视图,ECCV 2018
-
渔网:网格中语义热图的未来推断,CVPR 2020
-
monola layout:单幅图像的模拟场景布局,WACV 2020
-
https://www . Reddit . com/r/self driving cars/comments/gxq56t/recreating _ the _ Tesla _ autopilots _ birds _ eye _ view/和 github 上的代码
-
一千零一个小时:自驾运动预测数据集,Lyft 预测数据集
-
司机网:模仿最好综合最差学车,RSS 2019
-
道路规则:用语义交互卷积模型预测驾驶行为,CVPR 2019
-
IntentNet :学习从原始传感器数据预测意图,CoRL 2018
-
OFT :单目三维物体检测的正交特征变换,BMVC 2019
-
来自视觉深度估计的伪激光雷达:填补自动驾驶 3D 物体检测的空白,CVPR 2019
-
伪激光雷达 v3 :基于图像的三维目标探测端到端伪激光雷达,CVPR 2020
-
BEV-Feat-Stitching:使用机载单目摄像机理解鸟瞰语义高清地图,Arxiv 2021/01
-
PYVA :用心投射你的视角:通过交叉视角变换的单目道路场景布局估计,CVPR 2021
-
:使用单目正视图图像的鸟瞰全景分割,Arxiv 2021/08****
-
CaDDN :单目 3D 物体检测的分类深度分布网络,CVPR 2021 口述
-
火热:从环绕单目摄像机鸟瞰未来实例预测,ICCV 2021
-
BEV-Seg :利用几何和语义点云进行鸟瞰语义分割,CVPR 2020 研讨会
-
高清地图网:在线高清地图构建与评估框架,CVPR 2021 研讨会
自主驾驶中的单目动态物体 SLAM
截至 2020 年的 monoDOS 回顾
更新:
- 2020 年 7 月 11 日,添加来自 S3DOT 作者的 ST3D (CVPR 2020)。
传统的 SLAM(同步定位和绘图)算法通常有一个静态世界假设。即使对于能够在动态环境中运行的实际 SLAM 系统,它们通常也将动态对象视为异常值,并试图在应用常规 SLAM 流水线之前将其过滤掉以获得静态环境。这严重限制了其在自动驾驶中的在线应用,其中动态对象的显式处理至关重要。
单目动态物体 SLAM (MonoDOS) 以两种方式扩展了传统的 SLAM 方法。它是对象感知的,因为它不仅检测和跟踪关键点,还检测和跟踪具有更高级语义含义的对象。它也是动态的,因为它可以处理带有动态对象的场景,并跟踪这些对象的运动。
最好记住,不是所有的对象 SLAM 系统都是动态的,也不是所有的动态 SLAM 系统都是对象感知的。物体 SLAM 的开创性工作是SLAM++(CVPR 2013)但是它仍然需要一个有静态物体的静态场景。一些动态 SLAM 系统改进了基于刚体和恒定速度约束的姿态估计,但是没有对象的明确概念。****
这篇文章回顾了动态对象 SLAM 领域的几篇最新论文。它主要关注单目方法,以及一些可以修改为单目设置的立体方法。这绝不是一个详尽的综述,如果你推荐其他相关的研究,请告诉我。
动态对象 SLAM 的要素
动态对象 SLAM 系统引入了对象的概念,这有几个含义。首先,它需要有一个来自单个帧的对象提议阶段,就像传统 SLAM 系统中的关键点提议阶段(如 ORB-SLAM 中的 ORB)。该阶段将给出 2D 或 3D 物体检测结果。单目 3D 物体检测的最新进展将在这里大放异彩。第二,它有一个更复杂的数据关联。静态 SLAM 只关心关键点,数据关联只是指用特征向量进行跨帧的关键点匹配。现在我们引入了对象的概念,我们还必须在每一帧中的关键点和对象之间以及跨帧的对象之间执行数据关联。第三,作为对传统 SLAM 中的束调整的自然扩展,现在我们必须添加被跟踪的对象(轨迹线)和这些对象上的动态关键点,可选地具有来自假定运动模型的速度约束。
我制作了下面的图表来捕捉动态对象 SLAM 的三个基本元素。绿色块表示数据关联过程,蓝色块表示光束法平差过程,红色方块表示光束法平差的因子图表示中需要优化的因子。

典型动态对象 SLAM 的流水线和元素
为什么对象?
记住这三个基本元素,我们可以问这样一个问题:为什么在 SLAM 中引入动态对象会有效?首先,假设物体是刚体,随着时间的推移,形状和大小固定。还假设对象具有简单且物理上合理的运动模型,该模型鼓励随着时间的推移而平滑地改变姿势。因此,物体的大小和姿态变化都可以用少量的参数来描述。另一方面,对象的引入可以在束平差阶段制定更大数量的约束。在 N 帧中引入 M 个对象很大程度上增加了 O(M)个未知数和 O(MN)个约束。这使得 SLAM 问题更加健壮。
在接下来的每篇论文的回顾环节中,我将对每篇作品中的上述三个基本要素进行梳理。
立方体 SLAM:单目 3D 物体 SLAM (TRO 2019)
这可能是动态对象 SLAM 中最全面的作品。cubeSLAM 的一个主要贡献是它以优雅的方式将长方体的大小和位置集成到因子图优化中。它还使用一个运动模型来约束长方体可能的运动,从而优化物体的速度。在这个公式下,3D 对象检测和 SLAM 可以相互受益。对象为 BA 和深度初始化提供几何和比例约束。它还可以增加泛化能力,让 orb slam 在低纹理环境中工作。mono3D 结果使用 BA 和运动模型的约束进行优化。
目标提议
利用 2D 物体检测和低层图像特征进行三维长方体提案的生成和评分。这个看似天真的方法对椅子和汽车都非常有效。但是我确信基于深度学习的方法会在这个领域产生更好的结果。

长方体提案生成与评分(来源
数据关联
正如概述中提到的,数据关联必须在多个级别上执行,包括点-点、点-对象和对象-对象。
- 点对点匹配:首先,按照标准 orb-SLAM 的方式,基于 ORB 特征进行关键点匹配。对于不满足极线约束的匹配关键点,它们属于动态对象。对于动态关键点,按特征匹配可能会失败,直接用稀疏光流(KLT 光流)跟踪。在跟踪之后,动态关键点的 3D 位置将被三角测量。****
- 点-对象匹配:对于静态关键点,如果它们属于同一个包围盒,那么它与那个对象相关联。这里使用了许多试探法来确保稳健的性能。例如,点必须在 2 帧的同一个 bbox 内,并且距离长方体中心小于 1 米。框之间重叠区域中的特征点被忽略。****
- 物体-物体匹配:物体匹配通过匹配的关键点间接完成。如果连续帧中的两个对象共享最多的特征点(并且超过 10 个),则它们作为同一对象被跟踪。在基于特征的匹配或 KLT 跟踪失败的情况下,在边界框级别上使用视觉对象跟踪方法来完成动态对象跟踪。
目标感知光束法平差
静态关键点与相机姿态联合优化,具有与 ORB-SLAM 相同的相机点误差或重新投影误差。在从每个帧获得最佳 3D 建议后,我们可以将其视为 9DoF 3D“测量”,并公式化光束法平差问题。对于静态对象,我们有以下错误术语。
- 3D 摄像机-物体误差:被跟踪的地标物体具有 6 自由度姿态+3 自由度长方体尺寸,这可以与 9 自由度 3D 测量进行比较,并形成第一误差项。
- 2D 相机-物体误差:从 3D 测量我们可以投射 8 个角到相机图像中。8 个点的最小边界框应该与每帧的 2d 检测 bbox 一致。
请注意,这种 2D-3D 一致性假设并不总是正确的,但对于自动驾驶中车载摄像头的典型视点(水平或稍微向下看)来说,大多数时间都足够接近。
- 物点误差:对于与 bbox 关联的点,根据长方体的中心和大小,它应该位于长方体内。
对于动态对象,cube SLAM 假设动态对象是遵循物理上可行的运动模型的刚体。这引入了两个额外的误差项。
- 运动误差:从 t 帧的一个物体开始,根据分段匀速运动模型,演化状态为 t+1。那么应该和 t+1 时的观测一致。请注意,这涉及到每个动态对象的新状态速度。
- 动态点误差:如果一个点在动态对象上,那么它与动态对象的相对位置是固定的。换句话说,动态点被锚定到相关联的对象。

立方体的因子图。绿色方块是与动态对象相关的因素。为了清楚起见,博客作者添加了绿色/黑色的小文本。(来源)
裁决
CubeSLAM 可以生成一些令人印象深刻的演示,在 KITTI 中使用时间一致的长方体检测和跟踪动态场景中的 3D 对象。然而 SLAM 的结果可能并不总是优于单眼提议。它还可以估计动态对象的速度轮廓,大约在 1 米/秒以内,即使是在分段恒定运动的假设下。请注意,计算里程计(相机姿态)时会考虑对象约束。

SLAM 的 3D 对象检测及其跟踪轨迹、速度估计(来源)
S3DOT :基于立体视觉的语义三维物体和自主驾驶的自我运动跟踪(ECCV 2018)
这项研究是基于立体视频流,但该框架可以扩展到单目 SLAM。本文的主要贡献是展示了使用视频检测和跟踪截断的 3D 对象的能力,否则很难在单个图像上检测到这些对象。

S3DOT 能够预测极端截断汽车的姿态,预测一致的轨迹,并估计被跟踪汽车的速度(来源
目标提议
该管道的灵感来自 Deep3DBox (CVPR 2017,详细配方见我的评论帖)。它使用 2D 包围盒从对象检测和 8 视点分类。它在推断物距之前使用形状尺寸。这使得管道足够通用,可以用在单目设置中。

2D 盒子+视点= 3D 包围盒(来源)
数据关联
- 对象-对象匹配:通过相似性得分投票来匹配跨帧的 2D 包围盒。在补偿相机旋转之后,相似性分数考虑了边界框形状相似性和中心距离。
- 点-点匹配:与匹配的边界框或物体轮廓(2D 图像中 8 个投影角对着的凸包)内的圆形特征进行关键点匹配,用于静态背景。
- 点-对象匹配:没有明确说明,但是当一个点在该对象的对象轮廓内时,该点应该与该对象相关联。这种简单的关联策略是由于高效的 3D 提议导致了多边形 2D 对象轮廓,其非常类似于对象实例遮罩。
目标感知光束法平差
对象轮廓外的关键点被计为静态关键点。静态关键点和相机姿态通过相机点误差以与 ORB-SLAM 相同的方式联合优化。在相机姿态(或自我运动)被求解之后,物体姿态被求解。动态对象 BA 有四个误差项。
- 动态点误差(稀疏特征观察):刚性物体上的特征点在物体坐标系中有固定的坐标。
- 2D 相机-物体误差(语义 3D 物体测量):3D 被跟踪物体的投影应适合 2D 测量。
- 对象尺寸一致性:对象形状在帧之间保持一致。这是 cubeSLAM 中 3D 相机-物体错误的一部分。
- 运动误差:时间预测姿态应与单帧 3D 建议一致。确保一致的方向和运动估计的运动学运动模型。它包括车辆亮度、速度、转向角。
- 动态点云对齐:在最小化上述误差之后,我们获得基于尺寸先验的物体姿态的 MPA 估计。为了纠正这个先验中的任何偏差,S3DOT 执行一个名为的动态点云对齐的步骤,以将 3D 长方体与跟踪的点云对齐。这实质上是 cubeSLAM 中的物点误差。**
裁决
S3DOT 展示了在 KITTI 中检测和跟踪动态场景中的 3D 对象的令人印象深刻的结果。请注意,在计算里程计(相机姿态)时,不考虑对象约束。
S3DOT 的作者在 CVPR 2020 发布了另一个作品, ST3D: 立体 3D 物体跟踪的时空联合优化。这项工作主要利用深度学习进行 3D 检测,然后执行联合时空优化。

ClusterVO :对移动实例进行聚类,并为自己和周围环境估计视觉里程表(CVPR 2020)
ClusterVO 提出了一种更通用的动态 SLAM 方法,通过将对象表示为被跟踪的关键点(或本文中的点界标)的集群。**

ClusterVO 的整体管道(来源)
目标提议
ClusterVO 使用 YOLOv3 作为 2D 对象检测器,为每一帧中的对象提出语义 2D 边界框。它并没有假设物体可以用立方体来描述,比如 cubeSLAM 或者 S3DOT。因此不存在长方体生成阶段。
数据关联
ClusterVO 执行一个非常复杂的数据关联方案。它可以被视为两步过程,将观察到的关键点与被跟踪的界标相关联并将边界框与被跟踪的聚类相关联的多级概率关联,随后是将被跟踪的界标与被跟踪的聚类相关联的异构 CRF。
- 点对点匹配:这属于多级概率关联。最近邻和描述符匹配对于静态关键点可能工作得很好,但是对于动态点则不行。因此,首先预测每个集群的位置及其速度(只有线性,没有旋转)。如果观察值 k 在运动预测标志点 i 的投影附近,则将关键点观察值 k 关联到标志点 i 的概率与描述符相似度成比例。
- 物物匹配:这也属于多级概率关联。如果边界框 m 包含来自一个簇的最多数量的投影点,那么它与该簇 q (对象)相关联。
- 点目标匹配:这是最复杂的部分,使用了一个异质条件随机场(CRF)。它确定界标 i 是否与群集 q 相关联。它有多个能量项。一元能量项包括 2D 能量(如果一个点在与一个簇相关联的边界框内,那么它很有可能与该簇相关联。如果它在多个边界框内,则它可以被分配给多个簇)、一个 3D 能量(如果一个点靠近簇的中心,则它具有与簇相关联的更高概率,由簇的大小来调制)和一个运动能量(地标的投影可以由簇的运动来解释)。成对的标签平滑能量项惩罚邻近的界标,如果它们与不同的聚类相关联的话。****
目标感知光束法平差
在概率数据关联之后,我们可以为静态场景和动态集群制定 BA。它使用滑动窗口和专门设计的双轨帧管理来管理关键帧。
- 相机点误差:对于静态场景,clusterVO 联合优化相机姿态和静态关键点的位置,类似于 ORB-SLAM。当 clusterVO 选择滑动窗口状态估计方法时,它还通过附加的边缘化项来增强。这个边缘化术语捕获了由于滑动窗口的有限宽度而被移除的来自观察的贡献。****
- 运动误差:时间预测的姿态应该与从单个帧推断的 3D 测量一致。采用具有从高斯过程采样的加速度的运动模型。ClusterVO 只考虑平移部分。****
- 动态点误差 : clusterVO 也有这个动态点误差,类似于 cubeSLAM 和 S3DOT。如果一个点在动态对象上,那么它与动态对象的相对位置是固定的。
裁决
ClusterVO 是一种更通用的动态对象 SLAM 方法。从 KITTI 动态场景的结果来看,长方体的质量并不像在地标聚类的后处理步骤中估计的那样好。对于自动驾驶,CubeSLAM 和 S3DOT 似乎更实用。请注意,优化里程计(相机姿态)时会考虑对象约束。
MoMoSLAM: 用于动态环境的多目标单目 SLAM(IV 2020)
“多体单声道 SLAM”的概念似乎来自于“多体 SfM ”,但它本质上与动态对象 SLAM 具有相同的含义。
目标提议
MoMoSLAM 使用了一个相当笨重但精确的 3D 对象提议管道。它使用形状先验和关键点将 2D 检测提升到 3D 形状。它首先检测车辆可区分特征上的 k=36 个有序关键点,以及一系列基本形状的变形系数。然后通过最小化重投影误差将 2D 检测提升到三维,得到 6 自由度的姿态和形状参数。这是作者在以前的出版物(IROS 2018)中使用的,与RoI-10D(CVPR 2019)非常相似。

从 2D 关键点到 3D 形状(来源:地球不是平的,IROS 2018)
数据关联
- 点对点匹配:基于描述符特征的关键点匹配,类似于 ORB-SLAM。
- 对象-对象匹配:这在论文中没有明确提到,但是这是肯定需要的。任何 2D 物体追踪方法都可以。
- 点物匹配:未使用。这是通过检测每一帧中每个对象的语义关键点来隐式地和部分地完成的。
目标感知光束法平差

摄像机-物体姿态图和循环一致性(来源)
MoMoSLAM 使用不同的优化公式。如上所示,MoMoSLAM 不会指定每个错误项并将其最小化,而是会强制在姿势图中创建的每个循环保持一致。但本质上,这应该等同于最小二乘误差的最小化。
- 相机点错误:与 ORB-SLAM 相同。由于单目图像中的比例模糊,这种里程计是按比例的。然后,使用逆透视映射(IPM)将地面区域的语义分割和该区域中的点匹配用于估计 3D 深度。这固定了比例因子,并导致公制比例的里程表。

MoMoSLAM 的公制里程计估算(来源
- 多物体姿态图中的循环一致性:姿态图中的节点是估计,姿态图中的边是测量。 摄像机-摄像机边缘通过公制比例里程计进行约束。摄像机-车辆边缘通过单帧 2D 限制为 3D 提升。车辆-车辆边缘通过两种不同的 3D 深度估计方法进行约束(IPM vs 2D-3D 提升)。注意,没有明确的运动模型。****
我觉得循环一致性有点做作,尤其是车-车边缘。为 IPM 和 2D 到三维提升之间的距离估计的一致性增加一个足够的误差项会更直接。
判决
MoMoSLAM 用于固定单目设置的度量比例的方法非常有用。请注意,在计算里程计(相机姿态)时,不考虑对象约束。
https://www.bilibili.com/video/av90800325/
外卖食品
- 动态对象 SLAM 在 SLAM 的 3D 中加入了对象检测和提升,在后端优化中加入了对象的姿态和大小。
- CubeSLAM 和 ClusterVO 联合优化相机姿态和对象姿态。恒定对象大小和刚体运动的附加约束可用于约束因子图优化。这将有助于在提取的关键点稀疏的挑战性场景中计算相机姿态。相比之下,S3DOT 和 MoMoSLAM 将在 ORB-SLAM 失败时失败,因为它们依赖于 ORB-SLAM 进行相机姿态计算。
- CubeSLAM 和 S3DOT 将物体视为长方体,在自动驾驶中更加实用。虽然 ClusterVO 非常通用,但它没有在自动驾驶中纳入许多有用的先验知识,因此缺乏在 3D 对象检测中实现 SOTA 性能的潜力。
- 当无法获得全球位置信息时,CubeSLAM 似乎是在自动驾驶中执行 VIO 的一个很好的候选框架。
参考
- S3DOT : 基于立体视觉的语义三维物体和自主驾驶的自我运动跟踪,ECCV 2018
- 立方体猛击:单目 3D 物体猛击,TRO 2019
- ClusterVO :对移动实例进行聚类并为自己和周围环境估计视觉里程表,CVPR 2020
- MoMoSLAM : 用于动态环境的多目标单目 SLAM,IV 2020
- SLAM++: 在对象层次上同时定位和绘图,CVPR 2013
- M3D-RPN :用于物体检测的单目 3D 区域提议网络,ICCV 2019
- 利用刚体运动实现动态环境下的 SLAM,ICRA 2018
- ST3D: 立体三维目标跟踪的时空联合优化,CVPR 2020
蒙特卡洛辍学
用一个小技巧免费改善你的神经网络,获得模型不确定性估计作为奖励。
世上没有免费的午餐,至少根据流行的谚语是这样的。嗯,不再是了!也就是说,涉及到神经网络时就不是了。请继续阅读,看看如何用一个简单而巧妙的叫做蒙特卡洛辍学的技巧来提高网络性能。

拒绝传统社会的人
我们将要介绍的魔术只有在你的神经网络有漏层时才有效,所以让我们先简单介绍一下这些。辍学可以归结为在每个训练步骤中简单地关闭一些神经元。在每一步,一组不同的神经元被关闭。从数学上来说,每个神经元都有某种被忽略的概率 p ,称为辍学率。退出率通常设置在 0(无退出)和 0.5(大约 50%的神经元将被关闭)之间。确切的值取决于网络类型、图层大小以及网络过拟合训练数据的程度。

一个完整的网络(左)和在特定训练步骤中有两个神经元退出的同一个网络(右)。
但是为什么要这样做呢?放弃是一种正则化技术,也就是说,它有助于防止过度拟合。在数据很少和/或网络很复杂的情况下,模型可能会记住训练数据,因此,在训练期间看到的数据上效果很好,但在新的、看不见的数据上效果很差。这被称为过度适应,辍学寻求缓解。
怎么会?有两种方法可以理解为什么关闭模型的某些部分可能是有益的。首先,信息在网络中传播得更加均匀。想想网络中某处的单个神经元。有几个其他的神经元为它提供输入。使用 dropout,这些输入源中的每一个都可能在训练期间的任何时候消失。因此,我们的神经元不能只依赖一个或两个输入,它必须分散其权重,并注意所有的输入。因此,它对输入变化变得不太敏感,这导致模型更好地泛化。
从我们蒙特卡罗方法的角度来看,辍学效果的另一个解释甚至更重要。因为在每一次训练迭代中,你随机地对每一层中要被丢弃的神经元进行采样(根据该层的丢弃率),所以每次都有一组不同的神经元被丢弃。因此,每次模型的架构都略有不同,您可以将结果视为许多不同神经网络的平均集合,每个神经网络仅针对一批数据进行训练。
最后一个细节:辍学仅用于培训期间。在推理时,也就是当我们用我们的网络进行预测时,我们通常不会应用任何丢弃——我们希望使用所有训练过的神经元和连接。

蒙特卡洛
现在我们已经解决了辍学问题,蒙特卡洛是什么?如果你正在考虑摩纳哥的一个街区,你是对的!但是还有更多。

摩纳哥蒙特卡洛。Geoff Brooks 在 Unsplash 拍摄的照片
在统计学中,蒙特卡罗指的是一类计算算法,它依靠重复的随机抽样来获得某个数字量的分布。

蒙特卡洛退出:模型准确性
Monte Carlo Dropout 是由 Gal & Ghahramani (2016) 提出的,它巧妙的实现了常规 Dropout 的使用可以被解释为一个众所周知的概率模型:高斯过程的贝叶斯近似。我们可以将许多不同的网络(剔除了不同的神经元)视为来自所有可用模型空间的蒙特卡罗样本。这为推理模型的不确定性提供了数学基础,事实证明,这通常会提高模型的性能。
它是如何工作的?我们只是在测试时应用 dropout,仅此而已!然后,不是一个预测,我们得到许多,每个模型一个。然后我们可以对它们进行平均或分析它们的分布。最好的部分是:它不需要模型架构的任何改变。我们甚至可以在已经训练好的模型上使用这个技巧!为了看到它在实践中的工作,让我们训练一个简单的网络来识别 MNIST 数据集中的数字。
经过 30 个时期的训练,该模型在测试集上取得了 96.7%的准确率。要在预测时打开 dropout,我们只需设置training=True来确保类似训练的行为,即丢弃一些神经元。这样,每个预测都会略有不同,我们可以生成尽可能多的预测。
让我们创建两个有用的函数:predict_proba()生成所需数量的num_samples预测,并对 MNIST 数据集中 10 位数字中的每一位的预测类概率进行平均,而predict_class()只是选择最高的预测概率来挑选最可能的类。这个和下面的一些代码片段是受 Geron (2019)的启发。该书附有一套优秀的 jupyter 笔记本。
现在,让我们在测试集上进行 100 次预测并评估准确性。
这产生了 97.2%的准确度。与之前的结果相比,我们将错误率从 3.3%降低到了 2.8%,降低了 15%,完全没有改变或重新训练模型!

蒙特卡洛退出:预测不确定性
让我们来看看预测不确定性。在分类任务中,从 softmax 输出中获得的类概率经常被错误地解释为模型置信度。然而,Gal&Ghahramani(2016)表明,即使 softmax 输出很高,模型的预测也可能不确定。我们也可以在 MNIST 预测中看到这一点。让我们比较一下 softmax 输出和蒙特卡洛预测的单个测试示例的退出概率。
soft max _ output:[0\. 0\. 1\. 0\. 0\. 0\. 0\. 0\. 0\. 0.]
MC _ pred _ proba:[0\. 0\. 0.989 0.008 0.001 0\. 0\. 0.001 0.001 0\. ]
双方都同意测试示例最有可能来自第三类。然而,soft max 100%确定是这种情况,这应该已经提醒您有些事情不对劲了。0%或 100%的概率估计通常是危险的。蒙特卡洛辍学为我们提供了更多关于预测不确定性的信息:最有可能是第 3 类,但也有小概率是第 4 类,例如,第 5 类虽然不太可能,但仍然比第 1 类更有可能。

蒙特卡洛辍学:回归问题
到目前为止,我们已经讨论了一个分类任务。现在让我们来看一个回归问题,看看蒙特卡洛退出是如何为我们提供预测不确定性的。让我们使用波士顿住房数据集拟合一个回归模型来预测房价。
对于分类任务,我们定义了函数来预测类别概率和最可能的类别。同样,对于回归问题,我们需要函数来获得预测分布和点估计(让我们使用平均值)。
让我们再次对一个测试示例进行 100 次预测,并绘制它们的分布,标记其平均值,这是我们的点估计,或最佳猜测。

来自波士顿住房数据的一个测试示例的预测价格分布。红线表示平均值。
对于这个特定的测试示例,预测分布的平均值为 18,但是我们可以看到,其他值也不是不可能的—该模型对其预测不是很确定。

蒙特卡洛辍学:一个实施细节
最后一点:在本文中,我们一直通过将模型的training模式设置为true来实现蒙特卡洛脱离。这很有效,但是它可能会影响模型的其他部分,这些部分在训练和推断时表现不同,例如批处理规范化。为了确保我们只打开 Dropout 而不影响其他任何东西,我们应该创建一个自定义的 MonteCarloDropout 层,该层从常规 dropout 继承,并在默认情况下将其training参数设置为true(以下代码改编自 Geron (2019))。

结论
- 蒙特卡洛退出归结为用常规退出训练神经网络,并在推理时保持其开启。这样,我们可以为每个实例生成多个不同的预测。
- 对于分类任务,我们可以平均每个类的 softmax 输出。这往往会导致更准确的预测,从而更恰当地表达模型的不确定性。
- 对于回归任务,我们可以分析预测分布,以检查哪些值是可能的,或者使用其平均值或中值对其进行汇总。
- Monte Carlo Dropout 很容易在 TensorFlow 中实现:它只需要在进行预测之前将模型的
training模式设置为true。最安全的方法是编写一个自定义的三行类,继承常规 Dropout。

来源
- Gal Y. & Ghahramani Z .,2016 年,辍学作为贝叶斯近似:表示深度学习中的模型不确定性,第 33 届机器学习国际会议论文集
- Geron A .,2019,第二版,使用 Scikit-Learn 和 TensorFlow 进行机器学习:构建智能系统的概念、工具和技术

感谢阅读!
如果你喜欢这篇文章,为什么不订阅电子邮件更新我的新文章呢?并且通过 成为媒介会员 ,可以支持我的写作,获得其他作者和我自己的所有故事的无限访问权限。
需要咨询?你可以问我任何事情,也可以在这里 为我预约 1:1 。
直观的视觉解释
towardsdatascience.com](/svm-kernels-what-do-they-actually-do-56ce36f4f7b8) [## 校准分类器
你确定你的模型返回概率吗?🎲
towardsdatascience.com](/calibrating-classifiers-559abc30711a) [## 线性回归中收缩法和选择法的比较
详细介绍 7 种流行的收缩和选择方法。
towardsdatascience.com](/a-comparison-of-shrinkage-and-selection-methods-for-linear-regression-ee4dd3a71f16)**
蒙特卡罗积分和抽样方法
积分近似和随机数发生器
积分是解决问题时经常使用的关键计算。对于概率任务,连续随机变量 x 的期望值由以下积分定义,其中 p(x)是 x 的概率密度函数。

然而,用计算机计算这个值并不容易。为了有效地定义这个积分,使用了数值近似方法。在这里,我将介绍一个简单的近似方法,蒙特卡罗积分
蒙特卡罗积分
蒙特卡罗积分是一种数值积分计算方法,使用随机数来近似积分值。考虑下面 f(x)的期望值的计算。这里,p(x)是 x 的概率密度函数。

在这种方法中,我们从概率恒等函数 p(x)中选择 n 个独立同分布的样本{x_i} (i=1,2,…,n)。对于所有样本 x,积分值近似为 f(x)的平均值。

根据大数定律,当 n →∞收敛于 f(x)的期望值时,蒙特卡罗积分的一个极限。

让我们用蒙特卡洛积分法计算一个【π】的近似值。考虑一个边长为 1 的正方形和一个单位圆,如下图所示。

圆的面积是 π。现在假设 f(x,y)是一个函数,当(x,y)位于圆内时输出 1,否则输出 0。设 p(x,y)是[-1,1]上的均匀分布。在这种情况下,单位圆 π 的面积可以写成

用蒙特卡罗积分,这个积分( π )可以用 p(x,y)的 i.i.d 样本来近似。n(in circle)是位于单位圆内的样本数。

下图显示了 n 足够大时蒙特卡罗积分的收敛性。

有了蒙特卡罗积分,我们可以简单地用随机数计算积分值。但是,问题是“如何生成随机数?”当然,如果概率分布是众所周知的,例如均匀分布、高斯分布,我们可以很容易地用一些库实现随机数生成器。如果我们必须从任何库中都没有实现的分布密度函数中生成随机数,那该怎么办?为了解决这个问题,使用了抽样方法。
重要性抽样
一种常用的方法是重要性抽样。在这种方法中,引入了一种代理分布来从任意分布中抽取随机数。在大多数情况下,我们选择众所周知的分布如高斯分布、均匀分布作为代理分布。这个方法的主要概念可以简单地写成下面的形式,其中 q(x)是一个代理分布。

利用重要性采样,我们从代理分布 q(x)中选择 i.i.d .样本{x'_i} (i=1,2,…,n ),而不是从 p(x)中生成随机数,并通过以下计算来近似积分值。这里,p(x)/q(x)被称为抽样的。

现在,让我们以拉普拉斯分布的方差计算为例。
考虑 f(x)=x 和一个概率密度函数 p(x)=1/2 Exp(-|x|)。具有类似 p(x)的密度函数的分布称为拉普拉斯分布。
如果我们选择均匀分布作为代理分布,拉普拉斯分布的方差可以近似计算为
**
有了纸和铅笔,我们可以很容易地计算 Var[x]。这个计算的值是 2。现在,让我们确认重要性抽样方法的结果。

在重要性抽样中,我们使用从代理分布生成的随机数(样本)来近似积分值。为了从具有任何概率密度函数的分布中直接生成样本,我介绍了以下采样方法。
逆变换采样
在逆变换采样法中,我们用一个由 1 维均匀分布生成的随机数 u 来生成任意 1 维概率密度函数 p(x)的随机数 x。在这种情况下,我们使用 p(x)的累积分布函数的反函数。如果 P(x)的累积分布函数是 p(x),那么 u=P(x)的反函数是 x =P^-1 (u)。现在,x = P^-1 (u)有 p(x)作为[0,1]上的概率密度函数。因此,对于[0,1] {u_i} (i=1,2,…,n)上均匀分布的 n 个样本,我们可以通过计算 x_i = P^-1(u_i).来生成 p(x)分布{x_i} (i=1,2,…,n)的 n 个样本
再次,让我们考虑拉普拉斯分布的方差计算作为一个例子。这一次,我们使用逆变换采样方法,从拉普拉斯分布的概率密度函数直接生成随机数(样本)。有了这些随机数,我们将再次重新计算 Var[x]的近似值。

让我们检查这个方法的结果。

下图显示了使用逆变换采样方法的拉普拉斯分布方差 V[x]的近似值。

从这个结果可以直接看出,与代理分布的情况相比,使用从分布中直接生成的随机数提供了更好的近似。然而,利用逆变换采样方法,不可能仅从 2 维或更高维分布直接生成随机数。为此,使用了拒绝抽样方法。
拒绝抽样

拒绝抽样的概念
拒绝抽样的思想是使用代理分布(高斯或均匀分布等。)调用 q(x)生成一个随机数并使用另一个均匀分布来评估生成的样本是否接受它为从 p(x)生成的样本。通过这种方法,我们还可以从更高维度的分布中生成随机数。
作为用这种方法生成随机数的准备,我们需要知道 L 的一个有限值,其中 max[p(x)/q(x)] < L. Here, q(x) is a proxy distribution.
- First, we generate a random number x’ from a proxy distribution q(x). This x’ is called a 建议点 。
- 接下来,从[0,1]上的均匀分布生成随机数 v。这个 v 将用于评估建议点,考虑从 p(x)中生成是否是好的。
- 如果 v ≤ p(x')/q(x '),则 x '被接受为 p(x)生成的随机数,否则,x '被拒绝。
用拒绝抽样产生 n 随机数的算法是

随机数生成和拉普拉斯分布方差计算的结果(代理分布:高斯分布)
**
马尔可夫链蒙特卡罗方法
在拒绝采样方法中,当不知道上边界 L 时,不可能产生随机数。MCMC 方法是解决这一问题的有效方法。MCMC 方法使用随机过程的概念(在这种情况下是马尔可夫链)。在这种情况下,第 I 个样本 x_i 的生成依赖于前一个样本 x_(i-1)。x1,x2,…,xn 用这个概念叫做马尔可夫链。这里,我介绍一种 MCMC 方法,Metropolis-Hastings 方法。
这种方法的过程类似于拒绝抽样。但是在这里,代理分布密度函数由条件概率 q(x|x_i)表示,并且评价指标 v 由[0,1]上的均匀分布生成。
- 首先,我们从代理分布 q(x|x_i)生成一个随机数 x’。这个 x '叫做一个 建议点 。
- 接下来,从【0,1】上的均匀分布生成随机数 v。这个 v 将用于评估建议点,考虑从 p(x)中生成是否是好的。
- 如果 v≤p(x ')q(x _ I | x ')/(p(x _ I)q(x ' | x _ I)),则 x '被接受为 p(x)生成的随机数,否则,x '被拒绝。
用拒绝抽样产生 n 个随机数的算法是

随机数生成和拉普拉斯分布方差计算的结果(代理分布:高斯分布)
**
拉普拉斯分布的方差近似
示例代码
*import random
import numpy as np
from scipy.stats import norm*
用蒙特卡罗积分计算 Pi(模拟参数 n:样本数)
*def f(x,y):
if (x*x+y*y<=1):
return 1
else:
return 0n = 10
N = []
P = []
while n <= 100000:
s = np.random.uniform(-1,1,(n,2))
nn = 0
for x in s:
nn+=f(x[0],x[1])
pi = 4*nn/n
N.append(n)
P.append(pi)
n = n*2*
拉普拉斯分布方差的重要抽样计算(模拟参数 n:样本数)
*def f(x):
return x*x
def p_pdf(x):
return 0.5*np.exp(-abs(x))
def imp_pdf(x):
return norm.pdf(x)n = 10
N = []
P = []
while n <= 100000:
s = np.random.normal(0,1,n)
nn = 0
for x in s:
nn+=f(x)*p_pdf(x)/imp_pdf(x)
p = nn/n
N.append(n)
P.append(p)
n = n*2*
逆变换采样(随机数生成器和积分近似计算)
*def sgn(x):
if x>0:
return 1
elif x==0:
return 0
else:
return -1
def p_laplace(x):
return 0.5*np.exp(-abs(x))#def P_laplace_cum(x):
#return 0.5*((1+sgn(x))(1-np.exp(-abs(x))))def P_laplace_inv(u):
k = 1-2*abs(u-0.5)
return -1*sgn(u-0.5)*np.log(k)# random number generatordef reverse_generator(X):
res = []
for u in X:
res.append(P_laplace_inv(u))
return res n_sample = 50
s = np.random.uniform(0,1,n_sample)
theta = reverse_generator(s)
print("n_sample = ",n_sample)
gen_xx =random.sample(theta,n_sample)
plt.scatter(gen_xx,[0]*n_sample,marker='x')
xx=np.linspace(-15,15,1000)
plt.plot(xx,[p_laplace(i) for i in xx],c='red')
plt.legend(['Laplace Distribution','generated number'])
plt.title("Random Numbers directly generated from p(x)")
plt.show()# Laplace distribution variance calculationN = []
P = []
n= 10
while n <= 100000:
s = np.random.uniform(0,1,n)
theta = reverse_generator(s)
p = 0
for x in theta:
p += x*x
N.append(n)
P.append(p/n)
n = n*2
P_r = [2]*len(N)
plt.plot(N,P)
plt.plot(N,P_r,c='red')
plt.legend(["Inverse Transform Sampling value","true value(2)"])
plt.xlabel("number of samples (n) ")
plt.show()*
拒绝采样(随机数生成器和积分近似计算)
*def p_laplace(x):
return 0.5*np.exp(-abs(x))def p_proxy(x):
return norm.pdf(x)# random number generatordef rejection_generator(n):
xx = np.linspace(0,1,100)
K = max(p_laplace(xx)/p_proxy(xx))+1
random_samples = []
while len(random_samples) < n:
proposal = np.random.randn()
v = np.random.uniform(0,K)
if v <= (p_laplace(proposal)/p_proxy(proposal)):
random_samples.append(proposal)
return random_samplesprint("generating...")
random_samples = rejection_generator(n=50)
print("n_sample = ",len(random_samples))
plt.scatter(random_samples,[0]*len(random_samples),marker='x')
xx=np.linspace(-15,15,1000)
plt.plot(xx,[p_laplace(i) for i in xx],c='red')
plt.legend(['true','gen'])
plt.show()# Laplace distribution variance calculationN = []
P = []
n= 10
while n <= 100000:
theta = rejection_generator(n)
p = 0
for x in theta:
p += x*x
N.append(n)
P.append(p/n)
n = n*2
P_r = [2]*len(N)
plt.plot(N,P)
plt.plot(N,P_r,c='red')
plt.legend(["Rejection Sampling value","true value(2)"])
plt.xlabel("number of samples (n) ")
plt.show()*
MCMC 方法(随机数生成器和积分近似计算)
*def p_laplace(x):
return 0.5*np.exp(-abs(x))def p_proxy(x,theta,sigma):
return norm.pdf(x,theta,sigma)def MCMC_generator(n):
random_samples = []
theta_i = 0
while len(random_samples) < n:
proposal = np.random.normal(loc=theta_i,scale=1)
v = np.random.uniform(0,1)
a = p_laplace(proposal)*p_proxy(theta_i,proposal,1)
b = p_laplace(theta_i)*p_proxy(proposal,theta_i,1)
if v <= (a/b):
random_samples.append(proposal)
theta_i = proposal
return random_samplesn = 50
random_samples = MCMC_generator(n)
print("n_sample = ",n)
plt.scatter(random_samples,[0]*n,marker='x')
xx=np.linspace(-15,15,1000)
plt.plot(xx,[p_laplace(i) for i in xx],c='red')
plt.legend(['true','gen'])
plt.show()N = []
P = []
n= 10
while n <= 10000:
theta = MCMC_generator(n=n)
p = 0
for x in theta:
p += x*x
N.append(n)
P.append(p/n)
n = n*2
P_r = [2]*len(N)
plt.plot(N,P)
plt.plot(N,P_r,c='red')
plt.legend(["Rejection Sampling value","true value(2)"])
plt.xlabel("number of samples (n) ")
plt.show()*
参考
- M.Yamasugi. 统计机器学习-基于生成模型的模式识别-。2019 年,日本奥姆沙
蒙特卡罗积分

Pixabay 上由 Pixapopz 拍摄的照片
很多时候,我们不能解析地求解积分,必须求助于数值方法。其中包括蒙特卡罗积分。你可能还记得,函数的积分可以解释为函数曲线下的面积。蒙特卡罗积分的工作原理是在 a 和 b 之间的不同随机点计算一个函数,将矩形的面积相加,取和的平均值。随着点数的增加,结果越来越接近积分的实际解。

用代数表示的蒙特卡罗积分:

与其他数值方法相比,蒙特卡罗积分特别适合计算奇数形状的面积。

在下一节中,我们将看到当我们知道先验和似然性,但缺少归一化常数时,如何使用蒙特卡罗积分来确定后验概率。
简而言之贝叶斯统计
后验概率指的是贝叶公式中的一个特定项。

Baye 定理可以用来计算一个在特定疾病的筛选测试中测试呈阳性的人实际上患有该疾病的概率。

如果我们已经知道 P(A),P(B)和 P(B|A ),但还想知道 P(A|B ),我们可以使用这个公式。例如,假设我们正在测试一种罕见的疾病,这种疾病会感染 1%的人口。医学专家已经开发了一种高度敏感和特异的测试,但还不十分完善。
- 99%的病人测试呈阳性
- 99%的健康患者测试呈阴性
贝叶斯定理告诉我们:

假设我们有 10,000 人,100 人生病,9,900 人健康。此外,在对所有人进行测试后,我们会让 99 名病人进行测试,但也让 99 名健康人进行测试。因此,我们会以下面的概率结束。
p(生病)= 0.01
p(未患病)= 1–0.01 = 0.99
p(+|有病)= 0.99
p(+|未患病)= 0.01

贝叶斯定理在概率分布中的应用
在前面的例子中,我们假设一个人生病的先验概率是一个精确到. 001 的已知量。然而,在现实世界中,认为. 001 的概率实际上如此精确是不合理的。一个特定的人生病的概率会根据他们的年龄、性别、体重等有很大的不同。一般来说,我们对给定先验概率的了解远非完美,因为它是从以前的样本中获得的(这意味着不同的总体可能给出不同的先验概率估计)。在贝叶斯统计中,我们可以用先验概率的分布来代替这个值 0.001,该分布捕获了我们对其真实值的先验不确定性。包含一个先验概率分布最终产生一个后验概率,它也不再是一个单一的量;相反,后验概率也变成了概率分布。这与传统观点相反,传统观点认为参数是固定的量。
归一化常数
正如我们在关于吉布斯采样和 Metropolis-Hasting 的文章中所看到的,当归一化常数未知时,蒙特卡罗方法可用于计算后验概率分布。
让我们首先探究一下为什么我们需要一个归一化常数。在概率论中,归一化常数是一个函数必须乘以的常数,因此其图形下的面积为 1。还不清楚?让我们看一个例子。
回想一下,正态分布的函数可以写成:

两个圆周率的平方根是归一化常数。
让我们检查一下我们是如何决定它的。我们从以下函数开始(假设均值为 0,方差为 1):

如果我们把它画出来,它会形成一个钟形曲线。

问题在于,如果我们取曲线下的面积,它就不等于 1,这是概率密度函数所要求的。因此,我们将函数除以积分结果(归一化常数)。

回到手头的问题,即如何在没有归一化常数的情况下计算后验概率…事实证明,对于连续的样本空间,归一化常数可以改写为:

在这一点上,你应该考虑蒙特卡罗积分!
Python 代码
让我们看看如何通过在 Python 中执行蒙特卡罗积分来确定后验概率。我们首先导入所需的库,并设置随机种子以确保结果是可重复的。
import os
import sys
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import scipy.stats as stnp.random.seed(42)
然后,我们设置贝塔分布和二项式分布的参数值。
a, b = 10, 10
n = 100
h = 59
thetas = np.linspace(0, 1, 200)
概率密度函数的范围从 0 到 1。因此,我们可以简化方程。

在代码中,前面的等式如下所示:
prior = st.beta(a, b).pdf(thetas)
likelihood = st.binom(n, thetas).pmf(h)
post = prior * likelihood
post /= (post.sum() / len(thetas))
最后,我们可视化的概率密度函数的先验,后验,和可能性。
plt.figure(figsize=(12, 9))
plt.plot(thetas, prior, label='Prior', c='blue')
plt.plot(thetas, n*likelihood, label='Likelihood', c='green')
plt.plot(thetas, post, label='Posterior', c='red')
plt.xlim([0, 1])
plt.xlabel(r'$\theta$', fontsize=14)
plt.ylabel('PDF', fontsize=16)
plt.legend();

结论
蒙特卡罗积分是一种求解积分的数值方法。它的工作原理是在随机点评估一个函数,将这些值相加,然后计算它们的平均值。
Python 中的蒙特卡罗集成
一个著名的赌场启发的数据科学,统计和所有科学的把戏。用 Python 怎么做?

图片来源:维基百科(免费)和作者制作的拼贴画
免责声明:本文灵感来源于 佐治亚理工学院在线分析硕士(OMSA) 项目学习资料。我很自豪能继续这个优秀的在线硕士项目。你也可以在这里查看详情。
什么是蒙特卡罗积分?
蒙特卡洛,实际上是世界著名的赌场的名字,位于摩纳哥城邦(也叫公国)的同名区,在世界著名的法国里维埃拉。
事实证明,赌场启发了著名科学家的思维,设计出一种有趣的数学技术,用于解决统计、数值计算和系统模拟中的复杂问题。

图片来源:维基百科
这项技术最早也是最著名的应用之一是在曼哈顿计划期间,当时高浓缩铀的链式反应动力学向科学家们展示了一个难以想象的复杂理论计算。即使是像约翰·冯·诺依曼、斯坦尼斯劳·乌拉姆、尼古拉斯·大都会这样的天才头脑也无法用传统的方式解决它。因此,他们转向了奇妙的随机数世界,让这些概率量驯服原本难以处理的计算。

三位一体测试:(图片来源:维基百科)
令人惊讶的是,这些随机变量可以解决计算问题,这阻碍了稳健的确定性方法。不确定因素实际上赢了。
就像蒙特卡洛游戏世界中的不确定性和随机性规则一样。这就是这个特殊名字的灵感来源。

图片来源: Pixabay
今天,这是一种广泛应用于各个领域的技术——
尽管它获得了成功和名声,但它的基本思想看似简单,却很容易演示。在本文中,我们用一组简单的 Python 代码来演示它。
这项技术最早也是最著名的应用之一是在曼哈顿计划中
这个想法
复杂的积分
虽然一般的蒙特卡罗模拟技术在范围上要广泛得多,但我们在这里特别关注蒙特卡罗积分技术。
它只不过是一种计算复杂定积分的数值方法,缺乏封闭形式的解析解。
比方说,我们想计算,

要得到这个不定形式积分的封闭解并不容易,或者完全不可能。但是数值逼近总能给我们定积分作为和。
这是函数的曲线。

黎曼和
在 黎曼求和 的大类下有很多这样的技巧。这个想法只是将曲线下的区域分成小的矩形或梯形块,通过简单的几何计算来近似它们,然后将这些分量相加。
为了简单说明,我展示了这样一个只有 5 个等间距间隔的方案。

对于程序员朋友来说,其实 Scipy 包里有现成的函数可以快速准确的做这个计算。
如果我随机选择呢?
如果我告诉你,我不需要如此一致地选择区间,事实上,我可以完全随机地选择 100%的随机区间来计算相同的积分,会怎么样?
疯话?我选择的样本可能看起来像这样…

或者,这个…

我们没有时间或范围来证明其背后的理论,但可以证明的是通过合理的大量随机采样,我们实际上可以以足够高的精度计算积分!
我们只需选择随机数(在两个极限之间),在这些点上评估函数,将它们相加,然后用一个已知的因子缩放。我们完了。
好的。我们还在等什么?让我们用一些简单的 Python 代码来演示这种说法。
尽管它获得了成功和名声,但它的基本思想看似简单,却很容易演示。
Python 代码
用简单的平均法代替复杂的数学运算
如果我们试图计算下面形式的积分——任何积分,

我们只是用下面的平均值代替积分的“估计值”,

其中表示 0 到 1 之间的均匀随机数。请注意,我们如何通过简单地将一堆数字相加并取其平均值来代替复杂的积分过程。
在任何现代计算系统、编程语言,甚至像 Excel 这样的商业软件包中,您都可以使用这个统一随机数生成器。查看我关于这个主题的文章,
我们通过一个简单的伪随机生成器算法,并显示如何使用它来生成重要的随机…
towardsdatascience.com](/how-to-generate-random-variables-from-scratch-no-library-used-4b71eb3c8dc7)
我们只需选择随机数(在两个极限之间),在这些点上评估函数,将它们相加,然后用一个已知的因子缩放。我们完了。
该功能
下面是一个 Python 函数,它接受另一个函数作为第一个参数、两个积分限制和一个可选整数来计算参数函数所表示的定积分。

代码看起来可能与上面的等式略有不同(或者您可能在教科书中看到的另一个版本)。这是因为我通过在 10 个时间间隔内分配随机样本来使计算更加精确。
对于我们的具体例子,自变量函数看起来像,

我们可以通过简单地将它传递给monte_carlo_uniform()函数来计算积分,

在这里,如你所见,我们在积分限值 a = 0 和 b = 4 之间随机抽取了 100 个样本。
计算到底有多好?
这个积分不能用解析方法计算。因此,无论如何,我们需要将蒙特卡罗方法的精度与另一种数值积分技术进行对比。我们为此选择了 Scipy integrate.quad()函数。
现在,您可能也在想— 随着采样密度的变化,精度会发生什么变化。这种选择显然会影响计算速度——如果我们选择降低采样密度,我们需要增加更少的数量。
因此,我们针对一系列采样密度模拟了相同的积分,并将结果绘制在黄金标准之上——下图中以水平线表示的 Scipy 函数。

因此,我们在低样品密度阶段观察到一些小的扰动,但随着样品密度的增加,这些扰动会很好地消除。无论如何,与 Scipy 函数返回的值相比,绝对误差非常小,大约为 0.02%。
蒙特卡洛魔术非常有效!

图片来源: Pixabay
速度呢?
但是它和 Scipy 方法一样快吗?好些了吗?更糟?
我们试图通过运行 100 次循环(总共 10,000 次运行)并获得汇总统计数据来找出答案。

在这个特殊的例子中,蒙特卡罗计算的运行速度是 Scipy 积分方法的两倍!
虽然这种速度优势取决于许多因素,但我们可以肯定的是,在计算效率方面,蒙特卡罗技术并不逊色。
我们在低样品密度阶段观察到一些小的扰动,但随着样品密度的增加,这些扰动会很好地消除
冲洗,重复,冲洗,重复…
对于像蒙特卡罗积分这样的概率技术,不用说,数学家和科学家几乎从来不会只运行一次就停下来,而是多次重复计算并取平均值。
这是一个 10,000 次实验的分布图。

如您所见,该图几乎类似于高斯正态分布,这一事实不仅可以用来获得平均值,还可以围绕该结果构建 置信区间 。
4±2 的区间置信区间是一个我们相当确定自己真正价值所在的数值范围…
www.mathsisfun.com](https://www.mathsisfun.com/data/confidence-interval.html)
特别适用于高维积分
虽然对于我们的简单说明(以及教学目的),我们坚持单变量积分,同样的想法可以很容易地扩展到具有多个变量的高维积分。
与基于黎曼和的方法相比,蒙特卡罗方法在这个更高的维度上表现尤为突出。可以以对蒙特卡罗方法更有利的方式优化样本密度,以使其更快,而不损害精度。
在数学上,该方法的收敛速度与维数无关。用机器学习的话来说,蒙特卡罗方法是你在复杂的积分计算中战胜维数灾难的最好朋友。
阅读这篇文章可以获得很好的介绍,
蒙特卡洛方法在实践中,如果你了解和知道最重要的概率和…
www.scratchapixel.com](https://www.scratchapixel.com/lessons/mathematics-physics-for-computer-graphics/monte-carlo-methods-in-practice/monte-carlo-integration)
与基于黎曼和的方法相比,蒙特卡罗方法在这个更高的维度上表现尤为突出。
摘要
我们介绍了蒙特卡罗积分的概念,并说明了它与传统的数值积分方法的不同。我们还展示了一组简单的 Python 代码来评估一维函数,并评估这些技术的准确性和速度。
更广泛的一类蒙特卡罗模拟技术更令人兴奋,并且在与人工智能、数据科学和统计建模相关的领域中以无处不在的方式使用。
例如,DeepMind 的著名 Alpha Go 程序使用了蒙特卡罗搜索技术,在围棋的高维空间中计算效率高。实践中可以找到许多这样的例子。
[## 蒙特卡洛树搜索简介:deep mind alpha go 背后的游戏规则改变算法
一场五局三胜的系列赛,100 万美元的奖金——一场高赌注的枪战。2016 年 3 月 9 日至 15 日之间…
www.analyticsvidhya.com](https://www.analyticsvidhya.com/blog/2019/01/monte-carlo-tree-search-introduction-algorithm-deepmind-alphago/)
如果你喜欢它…
如果您喜欢这篇文章,您可能也会喜欢我关于类似主题的其他文章,
我们通过一个简单的伪随机生成器算法,并显示如何使用它来生成重要的随机…
towardsdatascience.com](/how-to-generate-random-variables-from-scratch-no-library-used-4b71eb3c8dc7) [## 数学编程——数据科学进步的关键习惯
我们展示了建立数学编程习惯的一小步,这是一个崭露头角的人的关键技能
towardsdatascience.com](/mathematical-programming-a-key-habit-to-built-up-for-advancing-in-data-science-c6d5c29533be) [## 用 Python 实现布朗运动
我们展示了如何模拟布朗运动,在广泛的应用中使用的最著名的随机过程,使用…
towardsdatascience.com](/brownian-motion-with-python-9083ebc46ff0)
喜欢这篇文章吗?成为 中等成员 继续 无限制学习 。如果你使用下面的链接,我会收到你的一部分会员费, 而不需要你额外付费 。
作为一个媒体会员,你的会员费的一部分会给你阅读的作家,你可以完全接触到每一个故事…
medium.com](https://medium.com/@tirthajyoti/membership)*
蒙特卡罗马尔可夫链

希瑟·吉尔在 Unsplash 上拍摄的照片
一个蒙特卡罗马尔可夫链 ( MCMC )是一个描述一系列可能事件的模型,其中每个事件的概率只取决于前一个事件达到的状态。MCMC 有广泛的应用,其中最常见的是概率分布的近似。
让我们看一个蒙特卡罗马尔可夫链的例子。假设我们想确定晴天和雨天的概率。
我们得到了以下条件概率:
- 如果今天下雨,明天有 50%的可能是晴天。
- 如果今天下雨,明天有 50%的可能会下雨。
- 如果今天是晴天,明天有 90%的可能是晴天。
- 如果今天是晴天,明天有 10%的可能会下雨。

假设我们从阳光充足的州开始。然后我们进行蒙特卡罗模拟。也就是说,我们生成一个 0 到 1 之间的随机数,如果刚好在 0.9 以下,明天就是晴天,否则就是阴雨。我们又做了一次蒙特卡洛模拟,这一次,明天会下雨。我们重复该过程进行 n 次迭代。
下面的序列被称为马尔可夫链。

我们数出晴天的天数,除以总天数,来确定天气晴朗的概率。如果马尔可夫链足够长,即使初始状态可能不同,我们也会得到相同的边际概率。这种情况下,天晴的概率是 83.3%,下雨的概率是 16.7%。

让我们看看如何用 Python 实现蒙特卡罗马尔可夫链。
我们从导入以下库开始:
import numpy as np
from matplotlib import pyplot as plt
import seaborn as sns
sns.set()
我们可以使用状态机和相应的矩阵来表达上一个例子中的条件概率。

我们做一些线性代数。
T = np.array([[0.9, 0.1],[0.5, 0.5]])p = np.random.uniform(low=0, high=1, size=2)
p = p/np.sum(p)
q=np.zeros((100,2))for i in np.arange(0,100):
q[i, :] = np.dot(p,np.linalg.matrix_power(T, i))
最后,我们绘制结果。
plt.plot(q)
plt.xlabel('i')
plt.legend(('S', 'R'))

正如我们所见,天气晴朗的概率约为 0.833。同样,多雨的概率也趋向于 0.167。
贝叶斯公式
很多时候,我们想知道某个事件发生的概率,假设另一个事件已经发生。这可以象征性地表示为 p(B|A) 。如果两个事件不是独立的,那么这两个事件发生的概率由下面的公式表示。

例如,假设我们从一副标准的 52 张牌中抽取两张牌。这副牌中一半是红色的,一半是黑色的。这些事件不是独立的,因为第二次抽签的概率取决于第一次。
P(A) = P(第一次抽黑牌)= 25/52 = 0.5
P(B|A) = P(第二次抽黑牌|第一次抽黑牌)= 25/51 = 0.49
利用这些信息,我们可以计算连续抽两张黑牌的概率,如下所示:

现在,让我们假设,我们想要开发一个垃圾邮件过滤器,它将根据某个单词的出现与否来将一封电子邮件分类为垃圾邮件。例如,如果一封电子邮件包含单词 【伟哥】 ,我们将其归类为垃圾邮件。另一方面,如果一封电子邮件包含单词 money,那么它有 80%的可能是垃圾邮件。
根据贝叶斯定理,给定包含给定单词的电子邮件是垃圾邮件的概率为:

我们可能知道一封电子邮件是垃圾邮件的概率,以及一个单词包含在被分类为垃圾邮件的电子邮件中的概率。然而,我们不知道给定的单词在电子邮件中出现的概率。这就是 Metropolis-Hastings 算法发挥作用的地方。
大都会黑斯廷斯算法
Metropolis-Hasting 算法使我们能够在不知道归一化常数的情况下确定后验概率。在高层次上,Metropolis-Hasting 算法的工作方式如下:

验收标准只考虑目标分布的比率,因此分母抵消。

对于视觉学习者来说,让我们用一个例子来说明这个算法是如何工作的。
我们首先为θ选择一个随机的初始值。

然后,我们提出一个新的θ值。

我们计算当前θ值的 PDF 和建议θ值的 PDF 之比。

如果 rho 小于 1,那么我们以概率 p 将 theta 设置为新值。我们通过将 rho 与从均匀分布中抽取的样本进行比较。如果 rho 大于 u 则我们接受建议值,否则,我们拒绝它。

我们尝试不同的θ值。

如果ρ大于 1,它将总是大于或等于从均匀分布中抽取的样本。因此,我们接受θ新值的建议。
**
我们重复这个过程 n 次 次迭代。

因为当目标分布大于当前位置时,我们自动接受提议的移动,所以θ将倾向于在目标分布更密集的地方。然而,如果我们只接受比当前位置大的值,我们会卡在其中一个峰值上。因此,我们偶尔会接受向低密度区域转移。这样,θ将被期望以这样的方式反弹,以接近后验分布的密度。
这些步骤实际上是一个马尔可夫链。

让我们看一下如何用 Python 实现 Metropolis-Hasting 算法,但是首先,这里有一个不同类型的发行版的快速复习。
正态分布
在自然界中,随机现象(如智商、身高)往往遵循正态分布。正态分布有两个参数μ和适马。改变μ会移动钟形曲线,而改变适马会改变钟形曲线的宽度。

https://commons . wikimedia . org/wiki/File:Normal _ Distribution _ pdf . SVG
贝塔分布
像正态分布一样,贝塔分布有两个参数。然而,与正态分布不同,β分布的形状会根据其参数α和β的值而显著变化。

https://commons . wikimedia . org/wiki/File:Beta _ distribution _ pdf . SVG
二项分布
与以高度为定义域的正态分布不同,二项式分布的定义域始终是离散事件的数量。

https://commons . wikimedia . org/wiki/File:Binomial _ distribution _ PMF _ sl . SVG
现在我们已经熟悉了这些概念,我们准备深入研究代码。我们从初始化超参数开始。
*n = 100
h = 59
a = 10
b = 10
sigma = 0.3
theta = 0.1
niters = 10000
thetas = np.linspace(0, 1, 200)
samples = np.zeros(niters+1)
samples[0] = theta*
接下来,我们定义一个函数,对于给定的θ值,该函数将返回似然和先验的乘积。
*def prob(theta):
if theta < 0 or theta > 1:
return 0
else:
prior = st.beta(a, b).pdf(theta)
likelihood = st.binom(n, theta).pmf(h)
return likelihood * prior*
我们逐步执行算法,根据前面描述的条件更新θ值。
*for i in range(niters):
theta_p = theta + st.norm(0, sigma).rvs()
rho = min(1, prob(theta_p) / prob(theta))
u = np.random.uniform()
if u < rho:
# Accept proposal
theta = theta_p
else:
# Reject proposal
pass
samples[i+1] = theta*
我们定义了可能性,以及先验和后验概率分布。
*prior = st.beta(a, b).pdf(thetas)
post = st.beta(h+a, n-h+b).pdf(thetas)
likelihood = st.binom(n, thetas).pmf(h)*
我们将使用 Metropolis-Hastings 算法获得的后验分布可视化。
*plt.figure(figsize=(12, 9))
plt.hist(samples[len(samples)//2:], 40, histtype='step', normed=True, linewidth=1, label='Predicted Posterior');
plt.plot(thetas, n*likelihood, label='Likelihood', c='green')
plt.plot(thetas, prior, label='Prior', c='blue')
plt.plot(thetas, post, c='red', linestyle='--', alpha=0.5, label='True Posterior')
plt.xlim([0,1]);
plt.legend(loc='best');*

正如我们所看到的,Metropolis Hasting 方法在逼近实际后验分布方面做得很好。
结论
蒙特卡洛马尔可夫链是从一组概率分布中提取的一系列事件,这些概率分布可以用来近似另一个分布。Metropolis-Hasting 算法在我们知道似然和先验,但不知道归一化常数的情况下,利用蒙特卡罗马尔可夫链来近似后验分布。
基于 Python 语言伊辛模型的蒙特卡罗方法在 2D 二元合金中的应用

来源:2018 年影响因子,高级科学新闻
使用 Python 中的蒙特卡罗方法,使用 Ising 模型评估材料的属性
诸如一片金属或玻璃样品的材料具有诸如导电性、磁化率、导热性和许多其他的物理性质。对于工业应用,必须了解这些特性,以便选择满足称重传感器要求的最佳材料或合金。例如,汽车的车身必须重量轻、承重大、耐腐蚀。为了满足所有这些需求,使用的三种主要材料是:金属、聚合物和铝合金。但是你如何确定一个物体的属性呢?一个由几十亿几十亿个微观状态组成的物质,怎么可能计算出它的性质?
严格来说,我们需要知道每个微观状态的能量,以便计算一种材料的性质。在本文中,我将重点介绍四个物理量:能量、磁化率、磁化强度和热容量。这些量是相互联系的,并且很好地定义了一个系统,因为它们是基本的物理属性,尽管能量是我们下面定义的所有关系的基础。但是还有一个问题:如何模拟一种材料的行为来计算它的性质?
由于我们受到计算机能力的限制,我们无法模拟硬件的完整行为,因为这将花费我们太多的时间,并导致我们提出一个新的问题:系统的大小应该如何接近现实?
为了回答这些问题,我将考虑 2D 二元合金作为我要研究的材料。对于我感兴趣的所有属性,我将比较不同系统大小的理论值和实验值(由模拟给出的值)。
但首先,我们将看看数学和物理学,它们为我们想要评估的不同属性建立了关系。
发现具有能量 E 的处于特定微观状态的系统的概率由正则分布给出:

𝛃 = 1/kT,k 定义为玻尔兹曼常数,t 定义为温度。z 被称为再分配函数,由下式给出:

有了这两个方程,我们可以测量任何实验参数,但一般来说,求和是不能进行的。幸运的是,发现处于能量 E 的微观状态的系统的概率 P(E)是能量 E 的精确函数。因此,我们可以得到在计算物理性质时要考虑的微观状态数的近似值。在本文中,我将展示系统的大小对物理性质计算的准确性的影响。
我之前说过,我们只对能量,磁化率,磁化强度,热容量感兴趣。有多种方法可以模拟这些性质,如密度泛函理论(最准确的一种)、机器学习或蒙特卡罗方法。
在这篇文章中,我决定建立一个 H=0 的伊辛 2D 模型的蒙特卡罗模拟。使用这个模型,我能够计算 L=4、8、16 和 32 的 L×L 自旋系统的自旋磁化强度绝对值的期望值,作为温度的函数(伊辛模型是自旋在图上的表示)。蒙特卡洛方法基于随机采样的重复(将旋转从-1 改变为 1,反之亦然)来获得新的能量值。如果总能量的值减少,我们接受新的情况,因为这意味着我们越来越接近能量最小的平衡状态。
因为用无量纲单位工作更方便,所以我用 J 单位计算了所有的能量。所有温度均以 J/k 为单位测量。
总系统能量的表达式为:

其中𝛔是自旋,可以等于-1 或 1。此外,我计算了每个温度下每个𝜒站点的磁化率。

这里 M 是系统的总磁化强度,T 是温度。每个位置的磁化强度由下式给出:

最后,每次旋转的热容 C 如下:

用 Tc 定义的临界温度 ln(1 + √2) = (2J)/(k*Tc)。可以看出,热容量在临界温度下呈对数发散。这种发散在有限大小的系统中是不可见的。此外,我没有磁化率的解析解,所以我没有将模拟结果与精确结果进行比较。
现在,数学已经完成,我们可以进入计算部分。
使用 Python,我建立了 H=0 的 2D 伊辛模型的蒙特卡罗模拟,并包含了每个物理属性的不确定性。
**def** init(L):
state = 2 * np.random.randint(2, size=(L,L)) - 1
**return** state
**def** E_dimensionless(config,L):
total_energy = 0
**for** i **in** range(len(config)):
**for** j **in** range(len(config)):
S = config[i,j]
nb = config[(i+1)%L, j] + config[i, (j+1)%L] + config[(i-1)%L, j] + config[i, (j-1)%L]
total_energy += -nb * S
**return** (total_energy/4)
**def** magnetization(config):
Mag = np.sum(config)
**return** Mag
我首先初始化了我的系统,它是 LxL 自旋的平方,可以取值-1 或 1。我创建了一个计算能量的函数和另一个计算磁化强度的函数。
**def** MC_step(config, beta):
*'''Monte Carlo move using Metropolis algorithm '''*
L = len(config)
**for** i **in** range(L):
**for** j **in** range(L):
a = np.random.randint(0, L) *# looping over i & j therefore use a & b*
b = np.random.randint(0, L)
sigma = config[a, b]
neighbors = config[(a+1)%L, b] + config[a, (b+1)%L] + config[(a-1)%L, b] + config[a, (b-1)%L]
del_E = 2*sigma*neighbors
**if** del_E < 0:
sigma *= -1
**elif** rand() < np.exp(-del_E*beta):
sigma *= -1
config[a, b] = sigma
**return** config
然后我用 Metropolis 算法进行蒙特卡罗模拟。基于一个随机的初始配置,我执行了一个随机的自旋变化,并重新计算了能量。如果系统的能量减少,当我们接近平衡时,新的构型被接受。否则,如果新系统具有更高的能量,则配置以 exp()βδE 给出的概率被接受。
**def** calcul_energy_mag_C_X(config, L, eqSteps, err_runs):
*# L is the length of the lattice*
nt = 100 *# number of temperature points*
mcSteps = 1000
T_c = 2/math.log(1 + math.sqrt(2))
*# the number of MC sweeps for equilibrium should be at least equal to the number of MC sweeps for equilibrium*
*# initialization of all variables*
T = np.linspace(1., 7., nt);
E,M,C,X = np.zeros(nt), np.zeros(nt), np.zeros(nt), np.zeros(nt)
C_theoric, M_theoric = np.zeros(nt), np.zeros(nt)
delta_E,delta_M, delta_C, delta_X = np.zeros(nt), np.zeros(nt), np.zeros(nt), np.zeros(nt) n1 = 1.0/(mcSteps*L*L)
n2 = 1.0/(mcSteps*mcSteps*L*L) *# n1 and n2 will be use to compute the mean value and the # by sites*
*# of E and E^2*
Energies = []
Magnetizations = []
SpecificHeats = []
Susceptibilities = []
delEnergies = []
delMagnetizations = []
delSpecificHeats = []
delSusceptibilities = []
**for** t **in** range(nt):
*# initialize total energy and mag*
beta = 1./T[t]
*# evolve the system to equilibrium*
**for** i **in** range(eqSteps):
MC_step(config, beta)
*# list of ten macroscopic properties*
Ez = [], Cz = [], Mz = [], Xz = []
**for** j **in** range(err_runs):
E = E_squared = M = M_squared = 0
**for** i **in** range(mcSteps):
MC_step(config, beta)
energy = E_dimensionless(config,L) *# calculate the energy at time stamp*
mag = abs(magnetization(config)) *# calculate the abs total mag. at time stamp*
*# sum up total energy and mag after each time steps*
E += energy
E_squared += energy**2
M += mag
M_squared += mag**2
*# mean (divide by total time steps)*
E_mean = E/mcSteps
E_squared_mean = E_squared/mcSteps
M_mean = M/mcSteps
M_squared_mean = M_squared/mcSteps
*# calculate macroscopic properties (divide by # sites) and append*
Energy = E_mean/(L**2)
SpecificHeat = beta**2 * (E_squared_mean - E_mean**2)/L**2
Magnetization = M_mean/L**2
Susceptibility = beta * (M_squared_mean - M_mean**2)/(L**2)
Ez.append(Energy); Cz.append(SpecificHeat); Mz.append(Magnetization); Xz.append(Susceptibility);
Energy = np.mean(Ez)
Energies.append(Energy)
delEnergy = np.std(Ez)
delEnergies.append(float(delEnergy))
Magnetization = np.mean(Mz)
Magnetizations.append(Magnetization)
delMagnetization = np.std(Mz)
delMagnetizations.append(delMagnetization)
SpecificHeat = np.mean(Cz)
SpecificHeats.append(SpecificHeat)
delSpecificHeat = np.std(Cz)
delSpecificHeats.append(delSpecificHeat)
Susceptibility = np.mean(Xz)
delSusceptibility = np.std(Xz)
Susceptibilities.append(Susceptibility)
delSusceptibilities.append(delSusceptibility)
**if** T[t] - T_c >= 0:
C_theoric[t] = 0
**else**:
M_theoric[t] = pow(1 - pow(np.sinh(2*beta), -4),1/8)
coeff = math.log(1 + math.sqrt(2))
**if** T[t] - T_c >= 0:
C_theoric[t] = 0
**else**:
C_theoric[t] = (2.0/np.pi) * (coeff**2) * (-math.log(1-T[t]/T_c) + math.log(1.0/coeff) - (1 + np.pi/4))
**return** (T,Energies,Magnetizations,SpecificHeats,Susceptibilities, delEnergies, delMagnetizations,M_theoric,
C_theoric, delSpecificHeats, delSusceptibilities)
对于每个系统尺寸(4x4、8x8、16x16、32x32),我计算了能量、磁化强度、比热和磁化率。每个图形对应一个属性。磁化强度和比热的黑线是精确解,并且对于每种性质,已经考虑了误差估计。
执行上面的脚本后,我得到了以下结果:

评论
模拟说明了有限规模的概念。使用已知的𝑇𝑐和临界指数的精确结果,我们可以检查大系统的模拟结果是否实际上向热力学极限行为收敛。临界现象理论最基本的概念是关联长度,它是一个典型系统长度尺度的量度。相关长度可以根据相关函数来定义,在伊辛模型的情况下,相关函数由下式给出。相关函数在长距离上呈指数下降,由所谓的 Ornstein-Zernicke 形式给出:
𝐶(𝑟⃗)=𝑒xp(−𝑟/𝜉)/r^(d-2/2)
其中𝜉是相关长度。这个表达式只有当𝑟>t13】𝜉时才有效。相关长度𝜉对应于系统中有序畴的典型尺寸。在𝑇𝑐上方的伊辛模型中,自旋向上和自旋向下的有序畴的数量平均相等(材料失去了所有,它们的典型尺寸对应于关联长度)。在 Tc 以下,相关长度对应于相反方向自旋的有序背景中自旋畴的典型尺寸。
当接近临界点(连续相变)时,相关长度在热力学极限内根据幂律发散:【𝜉∼𝑡^(−𝜈】其中 t 是测量离临界点距离的降低温度:𝑡=|𝑇−𝑇𝑐|和𝜈是临界指数的一个例子。另一个重要的临界指数是决定临界点处有序开始的指数,即磁化强度。序参量在𝑇𝑐以上为零,在𝑇𝑐以下出现为: < 𝑚 > =(𝑇𝑐−𝑇)^𝛽 。
对于热容量,关系由下面的公式给出 𝐶=𝑡^(−𝛼).当 t 接近𝑇𝑐.时,热容量出现一个奇点
对于易感性,关系如下: 𝜒=𝑡^(−𝛾).随着 t 接近𝑇𝑐.,𝜒似乎出现了一个奇点因此,当 t 接近𝑇𝑐.时,系统对磁场 h 变得无限敏感
不同系统的𝜈,𝛼,𝛽,𝛾指数集属于普适类。属于同一普适类的系统具有相同的指数。普遍性类别不依赖于与系统成分及其相互作用相关的微观细节(只要相互作用是短程的;长程相互作用可以改变普适类),只是在系统的维数和序参量的性质上。在我们的例子中,系统的大小因此是 l。这就是为什么磁化曲线、热容和温度磁化率随着系统大小的变化而彼此不同。有限尺寸标度的基础是,当相关长度𝜒变得与系统长度 l 相当时,就会出现与无限尺寸临界行为的偏差。这些有限尺寸偏差影响其他量的方式可以通过使用相关长度作为变量来表达它们的温度依赖性来进行研究。由此, 𝑡∼𝜉^(−1/𝜈)
例如,在磁化强度的渐近幂律形式中<𝑚>=𝑇c𝛽𝑡𝛽=𝑇c𝛽𝜉(−𝛽/𝜈).对于给定的系统尺寸 l,磁化强度的最大值应为 < 𝑚 > =𝑇c^𝛽 L^(−𝛽/𝜈).磁化强度向零的收敛取决于系统 l 的维数大小。在 2D 伊辛模型中,我们还具有以下关系 𝛽/𝜈=1/8 。
基于下面的表达式𝜒=𝑡^(−𝛾),在磁化率的渐近幂律形式中,t 的替换给出了 𝜒=𝜉^(𝛾/𝜈).给定系统尺寸 l 的磁化率最大值应为 𝜒=L^(𝛾/𝜈).通过该表达式,我们可以看到𝜒的峰值随 l 快速增长,并解释了不同系统尺寸的磁化率曲线的差异。对于二维伊辛模型,我们有 𝛾/𝜈=7/4
关于热容量,关系非常相似,它是下面的一个 𝐶=𝜉^(𝛼/𝜈) 并且对于给定的系统尺寸 l,热容量的最大值应该是 𝐶=𝜉^(𝛼/𝜈).因此,就磁化率而言,C 的峰值随 L 快速增长,这解释了不同系统尺寸的热容图中剖面的差异。
从更定性的角度来看,我们注意到能量分布的𝑇=𝑇𝑐有一个拐点,但是磁化强度、热容量和磁化率从这个温度下降到零。这意味着系统行为的改变,这对应于临界温度的定义。事实上,临界温度是我们情况下的居里温度,居里温度是材料失去所有永久磁性的温度,这预示着材料行为的变化。永久磁性是由磁矩的排列引起的。因此,在𝑇𝑐上方,平均有相等数量的有序的向上和向下自旋的畴,这可以通过磁矩、比热和磁化率向零的收敛来说明。
因此,结果的准确性和系统的大小之间的关系取决于我们正在检查的相关性。但是系统规模越大,计算成本越高。因此,由于时间成本非常重要,我们需要考虑权衡:时间/成本/精度。这就是为什么我想知道每个属性对系统大小的依赖性。我所理解的是,对于能量,我们可以用最小的系统规模来执行我们的模拟,并且结果将是可用的,因为能量对系统规模的依赖是指数的。对于系统规模越大、结果越精确的其他属性,这一点不再适用。尽管如此,我们仍然可以通过增加 32×32 系统大小达到平衡所需的步骤数来改进我们的结果。
如果你喜欢阅读这样的故事,并想支持我成为一名作家,考虑注册成为一名灵媒成员。每月 5 美元,你可以无限制地阅读媒体上的故事。如果你使用[我的链接](http://If you enjoy reading stories like these and want to support me as a writer consider signing up to become a Medium member. It's $5 a month, giving you unlimited access to stories on Medium. If you sign up using my link, I will earn a small commission and you will still pay $5. Thank you !! https://medium.com/@jonathan_leban/membership)注册,我将赚取一小笔佣金,你仍需支付 5 美元。谢谢大家!!
阅读乔纳森·莱班的每一个故事(以及媒体上成千上万的其他作家)。您的会员费直接支持…
medium.com](https://medium.com/@jonathan_leban/membership)
PS:我现在是柏克莱大学的工程硕士,如果你想讨论这个话题,请随时联系我。 这里的 是我的邮箱。
蒙特卡罗方法
深度强化学习讲解— 13
探索-解释困境

在深度强化学习讲解系列的这篇新帖中,我们将介绍另一种经典的强化学习方法来估计一个策略π的值。最直接的方法是运行几集,收集数百条轨迹,然后计算每个州的平均值。这种估计价值函数的方法叫做蒙特卡罗预测 (MC)。**
在本帖中,我们还将介绍如何评估最优策略和勘探-开采困境。
请访问第 5 页的自由介绍
medium.com](https://medium.com/aprendizaje-por-refuerzo/5-evaluación-de-políticas-con-monte-carlo-a6d70d1db7d4)
蒙特卡洛与动态规划
在本系列的第 1 部分中,我们介绍了一种 MDP 的解决方案,称为动态编程,由理查德·贝尔曼首创。记住,贝尔曼方程允许我们递归地定义值函数,并且可以用值迭代算法求解。总而言之,动态编程为强化学习提供了基础,但我们需要在每次迭代中循环遍历所有状态(它们的大小可以呈指数级增长,状态空间可以非常大,也可以无限大)。动态编程还需要环境的模型,特别是知道状态转移概率p(s′,r|s,a) 。
相比之下,蒙特卡罗方法都是从经验中学习。任何期望值都可以通过样本均值来近似——换句话说,我们需要做的就是播放一堆剧集,收集回报,然后取平均值。蒙特卡罗方法实际上是基本算法的一组替代方案。这些仅适用于偶发任务,当代理遇到终止状态时,交互停止。也就是说,我们假设体验被分成几集,并且不管选择什么动作,所有的集最终都会终止。
重要的是要注意,蒙特卡罗方法只给我们遇到的状态和动作一个值,如果我们从未遇到一个状态,它的值是未知的。
蒙特卡罗方法
这篇文章将提供一个用于强化学习的蒙特卡罗的实用方法。关于这些方法的更正式的解释,我邀请读者阅读理查德·萨顿和安德鲁·巴尔托的教科书 强化学习:简介 的第五章。
回想一下,最优策略****【π∫规定了,对于每个环境状态,代理应该如何选择一个行动来实现其最大化报酬的目标。我们还了解到,代理可以通过首先估计最优行动值函数q∑来构建对最优策略的搜索;那么一旦q∫已知,就很快得到π∫。**
代理开始采取一个基本策略,像等概率随机策略,一个随机策略,代理从每个状态中随机选择一组可用的动作,每个动作以等概率被选择。代理使用当前策略π将与环境交互以收集一些情节,然后合并结果以得出更好的策略。
方法是用一个我们称之为 Q 表的表来估算动作值函数。蒙特卡罗方法中的这个核心表为每个状态提供了一行,为每个动作提供了一列。对应于状态 s 和动作 a 的条目表示为 Q ( s , a )。
我们称之为预测问题:给定一个策略,代理如何估计该策略的价值函数?。我们将预测问题的蒙特卡罗(MC)方法称为 MC 预测方法。
我们将把我们的解释集中于动作值函数 Q,但是 MC 也可以用于估计状态值函数 v。
在用于 MC 预测的算法中,我们通过收集策略的许多片段开始。然后,我们注意到 Q 表中的每个条目对应于一个特定的状态和动作。为了填充 Q-table 的一个条目,我们使用当代理处于那个状态并选择动作时跟随的返回。
我们将一集中状态的每次出现定义为对该状态-动作对的访问。在一集里,一个状态-动作对可能被访问不止一次。这导致我们有两个版本的 MC 预测算法:
- ****每次访问 MC 预测:在所有事件中,对每个状态-动作对的所有访问后的回报进行平均。
- ****初访 MC 预测:每一集,我们只考虑初访状态-动作对。
首次访问方法和每次访问都被认为是保证收敛到真正的行动价值函数。
在本帖中,我们将对 OpenAI 健身房的工作示例进行首次访问:21 点环境。但是实际上,在这个例子中,首次访问和每次访问 MC 返回相同的结果。注意,同一个状态不会在一个情节中重复出现,所以在首次访问和每次访问 MC 方法之间没有区别。首次就诊 MC 预测的伪代码如下:

在这个伪代码中,变量 num _ 剧集表示代理收集的剧集数,有三个相关的表格:
- Q :一个 Q 表,每个状态一行,每个动作一列。
- N :记录我们对每个状态-动作对的第一次访问次数的表格。
- returns _ sum:一个表,记录第一次访问每个状态-动作对后获得的奖励的总和。
在每一集之后, N 和返回 _ sum 表被更新以存储该集包含的信息。在收集了所有剧集之后,完成对*Q(Q 表)的最终估计。***
案例研究:21 点
为了更好地理解蒙特卡罗在实践中是如何工作的,让我们使用 OpenAI 的健身房环境Blackjack-v0对 21 点游戏执行一步一步的编码。
二十一点规则
首先,让我们定义游戏的规则和条件:
- 流行的赌场纸牌游戏 21 点的目标是获得数值总和尽可能大而不超过 21 的纸牌。超过 21 导致破产,而双方都有 21 导致平局。
- 我们考虑一个简化的版本,其中每个玩家独立地与庄家竞争。这意味着我们将只和庄家对打,没有其他玩家参与。
- 数字卡的价值是按面值计算的。牌 J、K 和 Q 的值是 10。ace 的值可以是 1 或 11,取决于玩家的选择。
- 双方,玩家和庄家,都有两张牌。玩家的两张牌面朝上,而庄家的一张牌面朝上。在玩家看到他们的牌和庄家的第一张牌后,玩家可以选择击打或站立,直到他对自己的总和感到满意,之后他将站立。
- 然后,发牌者展示他们的第二张牌——如果总数小于 17,他们将继续抽牌,直到达到 17,之后他们将站着。
开放式健身房:21 点环境
我们将使用 OpenAI 的健身房环境[Blackjack-v0](https://github.com/openai/gym/blob/master/gym/envs/toy_text/blackjack.py)。每个状态是一个三元组:
- 玩家目前的总和∈{0,1,…,31}
- 庄家的面朝上的牌∈{1,…,10}
- 无论玩家是否有可用的 a。
代理有两个潜在的操作:
- 棍(动作
0):不再取牌(也称“立”或“留”)。 - 击(动作
1):从庄家那里再拿一张牌。
21 点的每一局都是一集。赢、输和抽牌的奖励分别为+1、1 和 0。一个游戏内所有奖励都是 0,我们不打折(gamma = 1);因此,这些最终的回报也是回报。
这篇文章的全部代码可以在 GitHub 上找到,而可以通过这个链接作为一个谷歌笔记本来运行。
我们从导入必要的包开始:
**import sys
import gym
import numpy as np
from collections import defaultdictenv = gym.make('Blackjack-v0')
print(env.observation_space)
print(env.action_space)**
我们可以看到,在环境中有 704 种不同的状态,对应于 32 乘以 11 乘以 2,并且有两种可能的动作对应于选择坚持或击打:
**Tuple(Discrete(32), Discrete(11), Discrete(2))
Discrete(2)**
为了在每集的每个时间步骤中查看示例游戏,代理与环境进行交互,我们可以运行以下代码(多次):
**state = env.reset()
while True:
print(state)
action = env.action_space.sample()
state, reward, done, info = env.step(action)
if done:
if reward > 0:
print('Reward: ', reward, '(Player won)\n')
else:
print('Reward: ', reward, '(Player lost)\n')
break**
示例游戏的一个例子是:
**(21, 4, True)
(13, 4, False)
(14, 4, False)
Reward: -1.0**
经纪人输掉比赛的地方。我建议读者运行这段代码几次,看看不同的剧本。
一种简单的 MC 预测方法实现
考虑这样一个策略,玩家只根据她/他当前的分数来决定一个动作。例如,如果我们拥有的卡的总数是 18 或更少,我们认为如果我们要求一辆新的汽车可能是没问题的。我们有 75%的可能性做到这一点。如果卡的总数大于 18,我们认为接受一张新卡太危险了,我们不会以 75%的概率这样做。
总之,如果总和大于 18,我们以 75%的概率选择动作棒;并且,如果总和等于或小于 18,我们选择动作以 75%的概率命中。
请注意,在第一种策略中,我们忽略了状态中的一些信息,例如,庄家的面朝上的牌或我们是否有可用的 a。这是为了简化示例中一段重点代码的解释。以下函数generate_episode使用该策略对情节进行采样:
**def generate_episode(env):
episode = []
state = env.reset()
while True:
probs = [0.75, 0.25] if state[0] > 18 else [0.25, 0.75]
action = np.random.choice(np.arange(2), p=probs)
next_state, reward, done, info = env.step(action)
episode.append((state, action, reward))
state = next_state
if done:
break
return episode**
这段代码返回一个episode(使用策略 π 决定)作为元组的(状态、动作、回报)元组列表。episode[i][0]、episode[i][1]、episode[i][2]分别对应时步𝑖的状态、时步 i 的动作、时步 𝑖+1 的奖励。
为了玩(10 个游戏),我们可以如下执行前面的功能:
**for i in range(10):
print(generate_episode(env))**
输出将是(在我的运行中):
**[((19, 3, True), 1, 0.0), ((19, 3, False), 0, 1.0)]
[((14, 5, False), 1, -1.0)]
[((15, 10, False), 0, -1.0)]
[((15, 10, False), 1, 0.0), ((17, 10, False), 1, -1.0)]
[((13, 2, False), 1, -1.0)]
[((13, 4, False), 1, 0.0), ((14, 4, False), 0, -1.0)]
[((11, 10, False), 1, 0.0), ((21, 10, False), 0, 0.0)]
[((15, 10, False), 1, -1.0)]
[((9, 9, False), 1, 0.0), ((16, 9, False), 0, 1.0)]
[((13, 7, False), 1, 0.0), ((18, 7, False), 1, 0.0), ((21, 7, False), 1, -1.0)]**
奖励只有在游戏结束时才会收到,如果我们赢了就是1.0,如果我们输了就是-1.0。我们看到有时我们赢了,有时我们输了。
让我们开始根据前面的伪代码编写一个代码。首先,我们需要初始化字典N、return_sum和Q:
**N = defaultdict(lambda: np.zeros(env.action_space.n))
returns_sum = defaultdict(lambda: np.zeros(env.action_space.n))
Q = defaultdict(lambda: np.zeros(env.action_space.n))**
接下来,该算法在使用所提供的使用该策略的函数generate_episode生成的剧集上循环。每一集都将是状态、动作和奖励元组的列表。然后,我们使用 zip 命令将状态、动作和奖励分成不同的量:
**episode = generate_episode(env)
states, actions, rewards = zip(*episode)**
让我们回到 de 伪代码,看看我们在时间步长上循环,查看每个时间步长对应的状态动作对。如果这是我们第一次访问该对,我们将表N的相应位置增加 1,并将此时步的返回添加到表return_sum的相应条目中。
请记住,在这个 21 点的例子中,第一次访问和每次访问 MC 预测是等效的,因此我们将对每个时间步长进行更新。然后,一旦我们有了return_sum和N的相应更新值,我们就可以用它们来更新我们的Q估算表。这部分的代码如下:
**discounts = np.array([gamma**i for i in range(len(rewards)+1)])for i, state in enumerate(states):
returns_sum[state][actions[i]] +=
sum(rewards[i:]*discounts[:-(1+i)])
N[state][actions[i]] += 1.0
Q[state][actions[i]] = returns_sum[state][actions[i]]
/ N[state][actions[i]]**
我们的 MC 预测方法的完整代码如下:
**def mc_prediction(env, num_episodes, generate_episode, gamma=1.0):
returns_sum = defaultdict(lambda: np.zeros(env.action_space.n))
N = defaultdict(lambda: np.zeros(env.action_space.n))
Q = defaultdict(lambda: np.zeros(env.action_space.n))for episode in range(1, num_episodes+1):
episode = generate_episode(env)
states, actions, rewards = zip(*episode)discounts = np.array([gamma**i for i in
range(len(rewards)+1)])for i, state in enumerate(states):
returns_sum[state][actions[i]] +=
sum(rewards[i:]*discounts[:-(1+i)])
N[state][actions[i]] += 1.0
Q[state][actions[i]] = returns_sum[state][actions[i]]
/ N[state][actions[i]]
return Q**
该算法将 OpenAI 健身房环境的实例、生成的剧集数量以及折扣率(默认值1)作为参数。该算法返回作为输出的 Q 表(动作值函数的估计),一个字典(一维数组)。
绘制相应的状态值函数,看看哪些状态值更大,会很有意思。我们可以使用这个简单的代码从 Q-table 中完成,它通过根据我们如何定义问题来加权每个动作的值来获得状态的值:
**num_episodes=1000000Q = mc_prediction(env, num_episodes, generate_episode)State_Value_table={}
for state, actions in Q.items():
State_Value_table[state]=
(state[0]>18)*(np.dot([0.75, 0.25],actions)) +
(state[0]<=18)*(np.dot([0.75, 0.25],actions))**
我们可以用从 Udacity 借来的代码来绘制这个图。有两个图对应于我们是否有可用的 a 或者我们没有可用的 a。但是在这两种情况下,我们看到最高的状态值对应于当玩家和是 20 或 21 的时候,这看起来很明显,因为在这种情况下我们最有可能赢得游戏。

(来源:作者)
探索与开发
到目前为止,我们已经了解了一个代理如何采用一个像等概率随机策略这样的策略,使用它来与环境交互,然后使用该经验来填充相应的 Q 表,该 Q 表成为该策略的动作值函数的估计。所以,现在的问题是,我们如何利用这一点来寻找最优策略?
贪婪的政策
为了得到一个更好的政策,不一定是最优的,我们只需要为每个状态选择最大化 Q 表的行动。让我们称这个新政策为【π】。当我们拿一个 Q 表,并使用最大化每一行 的动作来提出策略时,我们说我们正在构建关于 Q 表的贪婪的策略。****
通常将所选动作称为贪婪动作。在有限 MDP 的情况下,作用值函数估计用 Q 表表示。然后,为了得到贪婪动作,对于表中的每一行,我们只需要选择对应于最大化该行的列的动作。
ε-贪婪政策
然而,不是总是构造一个贪婪策略(总是选择贪婪动作),而是构造一个所谓的ε贪婪策略,它最有可能选择贪婪动作,但也有很小但非零的概率选择其他动作。在这种情况下,使用一定在零和一之间的某个小正数εϵ。这是有动机的,正如我们将在后面更详细地解释的那样,由于代理必须找到一种方法来平衡基于他们当前知识的最佳行为的驱动和获取知识以获得更好的未来行为的需要。
蒙特卡洛控制
我们已经了解了代理如何采用策略 π ,使用它与环境进行多次交互,然后使用结果通过 Q 表来估计动作值函数。一旦 q 表非常接近动作值函数,代理就可以构造策略 π ,即ϵ——相对于 q 表是贪婪的,这将产生比原始策略 π 更好的策略。然后我们可以改进这个策略,把它变成ϵ的贪婪策略。
因此,如果代理人在这两个步骤之间反复交替,直到我们得到越来越好的策略,希望我们收敛到最优策略,我们最终会得到最优策略π∫:
- 步骤 1 :使用策略 π 构建 Q 表,以及
- 第二步**:将策略修改为ϵ——贪婪关于 q 表(标注为)。**
只要我们运行足够长的时间,这个被提议的算法是如此接近给我们最优策略。
我们把这种算法称为蒙特卡罗控制方法,用来估计最优策略。通常将步骤 1* 称为策略评估,因为它用于确定策略的动作值函数。同样,由于步骤 2 用于改进策略,我们也将其称为策略改进步骤。示意性地,我们可以将其表示为:***

因此,使用这个新术语,我们可以总结出,我们的蒙特卡罗控制方法在策略评估和策略改进步骤之间交替进行,以找到最优策略π∫:**

其中箭头中的“E”表示完整的政策评估,“I”表示完整的政策改进。在下一篇文章中,我们将展示蒙特卡罗控制算法的实现。
然而,一些问题出现了,在构造 ϵ 贪婪策略时,如何设置 ϵ 的值?在下一节中,我们将看到如何实现。
勘探开发困境
回想一下,我们的代理最初不知道环境的动态,由于目标是最大化回报,代理必须通过交互来了解环境。然后,在每一个时间步,当代理选择一个动作时,它根据过去对环境的经验作出决定。而且,本能可能是选择基于过去经验的的行动,将获得最大回报。正如我们在上一节中所讨论的,这种对动作值函数估计贪婪的策略很容易导致收敛到次优策略。****
平衡开发与勘探
这可能会发生,因为在早期,代理人的知识非常有限,并拒绝考虑就未来回报而言比已知行为更好的非贪婪行为。这意味着一个成功的代理人不可能在每个时间点都贪婪地行动;相反,为了发现最优策略,它必须继续改进所有状态-动作对的估计回报。但同时保持一定程度的贪婪行动,以尽快维持回报最大化的目标。这激发了我们之前提出的ϵ贪婪政策的想法。
我们将平衡这两个竞争需求的需求称为探索-开发困境,其中代理必须找到一种方法来平衡基于其当前知识的最佳行为驱动(开发)和获取知识以获得更好判断的需求(探索)。**
设置ε的值
这种困境的一个潜在解决方案是通过在构建 ϵ 贪婪策略时逐渐修改 ϵ 的值来实现的。对于代理人来说,通过选择探索而不是开发尝试各种策略来最大化回报,开始与环境的互动是有意义的。考虑到这一点,最好的开始策略是等概率随机策略,因为它同样有可能探索每个状态的所有可能行为。设置 ϵ =1 产生一个ϵ-贪婪策略,它等价于等概率随机策略。**
在随后的时间步骤中,促进开发而不是勘探是有意义的,在这种情况下,政策相对于行动价值函数估计逐渐变得更加贪婪。设置 ϵ =0 产生贪婪策略。已经表明,最初倾向于通过开发进行勘探,并逐渐倾向于开发而不是勘探是一种最佳策略。
为了保证 MC 控制收敛到最优策略π∑,我们需要保证满足两个条件:每个状态-动作对被访问无限多次,并且该策略收敛到关于动作-值函数估计 Q 的贪婪策略我们将这些条件称为无限探索极限中的贪婪以确保代理在所有时间步继续探索,并且代理逐渐探索更多和探索更少。
满足这些条件的一种方法是修改 ϵ 的值,当指定 ϵ 贪婪策略时,使其逐渐衰减。然而,在设定 ϵ.的衰减率时必须非常小心确定最佳衰变不是小事,需要一点炼金术,也就是经验。我们将在下一篇文章中看到实现的例子。
下一步是什么?
到目前为止,在我们当前的蒙特卡罗控制算法中,我们收集了大量的片段来构建 Q 表。然后,在 Q 表中的值收敛之后,我们使用该表来提出改进的策略。然而,蒙特卡罗预测方法可以在逐集的基础上逐步实施**。在下一篇文章中,我们将介绍如何按照这个想法构建更好的 MC 控制算法。下期帖子再见!****
深度强化学习讲解系列
一个轻松的介绍性系列以一种实用的方式逐渐向读者介绍这项令人兴奋的技术,它是人工智能领域最新突破性进展的真正推动者。
本系列的内容](https://torres.ai/deep-reinforcement-learning-explained-series/)
关于这个系列
我在五月开始写这个系列,在巴塞罗那的封锁期。老实说,由于封锁,在业余时间写这些帖子帮助了我 #StayAtHome 。感谢您当年阅读这份刊物;它证明了我所做的努力。
免责声明 —这些帖子是在巴塞罗纳封锁期间写的,目的是分散个人注意力和传播科学知识,以防对某人有所帮助,但不是为了成为 DRL 地区的学术参考文献。如果读者需要更严谨的文档,本系列的最后一篇文章提供了大量的学术资源和书籍供读者参考。作者意识到这一系列的帖子可能包含一些错误,如果目的是一个学术文件,则需要对英文文本进行修订以改进它。但是,尽管作者想提高内容的数量和质量,他的职业承诺并没有留给他这样做的自由时间。然而,作者同意提炼所有那些读者可以尽快报告的错误。**
蒙特卡罗方法,变得简单

来源: Unsplash
用混乱来寻找清晰
想象一个坐标网格上的 10 乘 10 的正方形。在那个网格上画了一些形状,但是你不知道它看起来像什么。但是可以查询一个函数 f ( x , y ),其中( x , y )是坐标,输出不是 1(在形状中)就是 0(不在形状中)。你如何找到这个形状的面积?

这种形状的一些可能性。由作者创建
答案很简单。它来自于统计学的一个原理,大数定律,即一个函数被随机抽样的次数越多,它的近似值就越精确。然后,我们的解决方案是简单地在 10×10 网格中随机选择点,计算形状中有多少陆地,然后除以采样点的总数。
虽然这种关于随机抽样的本能想法很简单,但它在许多领域都有应用,从法律到气候预测,也许与本文最相关的是机器学习和统计学。它有一个正式的名字:蒙特卡洛方法。
当遇到由确定性原则组成的问题时,如形状的面积、函数的分布或游戏中玩家下一步应该选择哪一步,蒙特卡罗方法基本上假设它可以通过概率和随机性(随机性)来建模。
蒙特卡罗方法依赖于从分布中重复随机取样来获得数值结果。它是一种方法,而不是一种算法。
对于另一个基于形状的例子,请查看 在不使用任何数学 (使用蒙特卡洛采样和多项式回归)的情况下找到圆面积的公式。随机抽样可以作为一种寻找函数积分(曲线下面积)的廉价方法。众所周知,圆周率是一个重要的常数,圆周率的值也可以通过蒙特卡洛采样来近似计算:

来源:维基媒体。图片免费分享。
一般来说,蒙特卡罗抽样有三类/三种应用:
- 直接取样。在没有先验信息的情况下,天真而直接地从分布中抽样。这就是我们如何逼近一个形状的未知区域。
- 重要性抽样。在分布太昂贵而不能取样的情况下,从更简单的近似函数中取样。这是贝叶斯优化和代理优化的核心组成部分。
- 拒绝采样。在分布未知的情况下,提出新的点,如果它们满足某个标准,就接受它们。
蒙特卡罗抽样通常用于两种情况:
- 优化。自然,找到最佳点需要探索和开发之间的健康平衡。当蒙特卡罗抽样(探索)与另一种控制开采的机制相配合时,它可以成为寻找最优解的有力工具。
- 近似概率&函数。当用另一种方法很难间接评估某些概率或函数(通常是概率函数)时,蒙特卡罗抽样是一种很好的方法。
因为蒙特卡罗方法背后的思想很简单,但可以以相当复杂和创造性的方式使用,所以最好通过几个例子来探索这种思维模式。
首先,考虑马尔可夫链蒙特卡罗(MCMC)方法,它试图在不知道分布是什么的情况下从目标分布中生成随机样本。马尔可夫链——图中每个节点是一个状态,有一定的概率 p 移动到另一个状态——被用来表示这些分布。
考虑一个城市中的天气马尔可夫链(天气恶劣),其中唯一可能的天气状态是风、冰雹/雪、雷暴或雨。每天,第二天的天气可以根据当天天气的概率进行预测。例如,如果今天下雪,有 80%的可能性刮风,20%的可能性明天下雨。

图标: Pixabay 。图示:作者。
为了在马尔可夫链中漫游,我们从一个位置 s 开始,以指定的概率移动到另一个位置 s ' ,。然后, s 成为新的 s 并重复该过程。虽然这个例子的特点是一个小的马尔可夫链,但是具有数千个节点和数十万个连接的巨大图形可以用来模拟错综复杂的概率关系。
如果我们在大量时间(例如 10,000 个模拟日)后运行马尔可夫链,我们开始达到“概率平衡”。这仅仅意味着,我们可以简单地根据我们旅行过的州有多少在下雨来估计下雨的静态概率(基于大数定律)。
例如,如果我们通过运行超过 10,000 个状态的马尔可夫链得到以下(假设的)结果:
- 2754 个州的州风
- 1034 个州有雷暴
- 4301 个州出现冰雹/降雪
- 为 1911 个州下雨
我们将能够产生以下概率:
- p(风)= 0.2754
- p(雷暴)= 0.1034
- p(冰雹/雪)= 0.4031
- p(下雨)= 0.1911
然后,我们可以简单地从分布中进行相应的采样——从马尔可夫链中随机抽取一个状态,而不需要遍历它。通过重复迭代和马尔可夫链的随机遍历(该过程的“蒙特卡罗”部分),系统能够被折叠并表示为概率分布。
可以构建马尔可夫链来模拟无法直接采样的复杂关系,然后将其简化以找到其潜在的概率分布。
有许多成熟的 MCMC 算法,如 Metropolis-Hastings 算法或 Gibbs 抽样。所有人都试图做一些类似的事情——模拟系统的潜在概率。
要从理论迈出一步,进入应用,看看蒙特卡罗树搜索(MCTS),智能游戏强化学习系统的一个关键部分。早期的游戏系统,比如 IBM DeepBlue 在 1997 年击败国际象棋冠军加里·卡斯帕罗夫(Gary Kasparov)的最初胜利,都是基于类似极大极小算法的,这些算法从当前的走法中找出所有可能的游戏,并确定哪一个游戏能带来确定的胜利(或最高的机会)。
随着测试游戏变得越来越复杂——最著名的是围棋游戏,甚至是像 DOTA 这样的高级图形射击游戏——计算上不可行,也很愚蠢,去玩完所有可能的场景。相反,对于一个玩家来说,探索潜在的有利棋步,同时放弃失败概率高的棋步,并利用已知的棋步,是更明智的做法。
假设我们开始构建一个这样的树,其中每个节点代表一些游戏状态。我们可以通过采取某些行动在节点之间转换。从游戏最初的开始状态,我们可以取几个可能的节点。

由作者创建
价值神经网络试图为每一步棋赋予一个价值:给定这一步棋,代理人获胜的概率是多少?注意,刚开始的时候,由于价值网没有经过训练,所以会给出随机的猜测。然而,随着系统玩足够多的游戏,它将更善于智能地分析某些移动的效果。

由作者创建
我们不是简单地选择概率最高的节点,而是执行“加权蒙特卡罗采样”。如果网络认为节点 2 将有 90%的机会获胜,那么它有更大的机会被选中,但也有可能节点 3 和 1 被选中。策略——或如何选择潜在节点——也是可以学习的。
这背后的逻辑是,某一步棋的可预见价值是有限的,是以偶然性为界的(也许对手会出其不意)。此外,只开发已知的移动,而不是冒险探索其他领域并潜在地发现更有利可图的移动,将导致无竞争力和懒惰的模型,这在复杂的强化学习环境中是不可取的。
然后,选择的节点被进一步扩展,并且该过程重复,直到游戏终止。将概率纳入决策中,而不是确定性地选择“最佳”值,有助于强化学习平衡开发/探索权衡。
如果我们从统计学的角度来看这个问题,蒙特卡罗抽样被用来模拟最佳概率分布 p ( v )来选择一个节点,假定它具有一些可预见的值 v 。
蒙特卡洛树搜索是 alpha Go(deep mind 的强化学习系统)击败(并将继续击败)顶级围棋选手的框架。MCTS 在其他地方有各种各样的应用。
模拟退火是蒙特卡罗抽样的另一个应用,在寻找全局最优解的任务中用作非梯度函数优化的有效方法。像应用蒙特卡罗方法的其他问题一样,搜索空间是离散的(例如,我们不是试图优化连续值,像神经网络的参数)。
在一些问题中,在固定的时间内找到一个近似的全局最优值比它的精确值更有价值,模拟退火实际上可能比梯度下降等算法更好。
模拟退火的想法来自冶金学,或金属的操作。在冶金学中,退火是对材料进行有控制的加热或冷却,以增加其尺寸并消除缺陷。类似地,模拟退火控制系统中的能量,这决定了它在探索新的可能性时愿意冒多大的风险。
温度从某个初始量开始,它代表某种计时器。在每个状态 s ,模拟退火根据两个状态的值移动到某个相邻状态s’(移动到s’是收益还是损失?),以及当前温度 T ,以概率 P ( s ,s’, T )。
随着温度随着每个时间步长降低(剩余时间减少),该模型变得更少探索性,而更多利用性。模拟退火允许在时间上从探索到开发的渐进过渡,这对于寻找全局最优非常有益。

随着温度的降低,寻找复杂函数的全局最大值的模拟退火。来源:维基媒体。图片免费分享。
采用蒙特卡罗抽样和贝叶斯方法对概率函数 P ( s ,s’, T )进行建模。事实上,您可能还记得,Metropolis-Hastings 算法通常是一种马尔可夫链蒙特卡罗方法(或以它为模型的方法),用于寻找转换阈值(应该转换的概率)。
这很自然,因为模拟退火将解决方案视为状态,并试图找到最佳转移概率——这是马尔可夫链建模的完美场景。
通常,蒙特卡罗方法——或者类似蒙特卡罗的思维——会出现在我们最意想不到的地方。虽然这是一个简单的机制,但它在无数的应用中有着深刻而复杂的根源。
总结/要点
- 蒙特卡罗方法是基于这样一种思想,即在系统中注入随机性通常可以有效地解决这个问题。
- 一般来说,蒙特卡罗抽样有三类:直接抽样、重要性抽样和拒绝抽样。
- 蒙特卡罗的两个常见应用包括优化和复杂概率和函数的评估。
- 在离散(非连续)和确定性问题中,蒙特卡罗方法利用随机性+概率、大数定律和有效的框架来有效地解决它们。
感谢您的阅读,请在回复中告诉我您的想法!
如果你对最新的文章感兴趣,可以考虑订阅。如果你想支持我的写作,通过我的推荐链接加入 Medium 是一个很好的方式。干杯!
巧妙算法背后的直觉
towardsdatascience.com](/the-beauty-of-bayesian-optimization-explained-in-simple-terms-81f3ee13b10f)
采用美元成本平均法的蒙特卡罗模拟

艾萨克·史密斯在 Unsplash 上拍摄的照片
利用 Python 中的蒙特卡罗模拟分析标准普尔 500 ROI
声明:我不是金融专业人士。此处提供的内容仅用于信息、教育和娱乐目的,不应被解释为专业的财务建议。
从我完成研究生学业开始我在分析领域的新事业已经有几个月了。在我拿到最初的几份工资后,我开始思考如何更明智地使用每月支付费用后剩余的现金,因为把它留在利息为 0.01%的支票账户上似乎有点乏味。经过一番研究后,我决定为什么不把它投入股市,至少可以跟上通货膨胀,这样我就可以将美元成本平均到标准普尔 500 交易所交易基金(ETF)中了。
美元成本平均法(DCA)是一种常见的投资策略,其中固定金额的资本定期投资于某项资产,以减少市场波动的影响。这种策略与 ETF 完美契合了我的需求,因为它是自动多样化的,不需要多少市场知识。
在我把辛苦赚来的钱投入股市之前,我想我应该好好利用我的分析学位所获得的新知识。我决定在 ETF 上用 Python 实现一个简单的蒙特卡罗(MC)模拟,看看 DCA 的表现如何。通过 MC 模拟,我可以在对历史数据进行重复随机采样后,获得预期投资回报(ROI)的分布。
数据
为了做到这一点,我研究了两个最受欢迎的基金,SPDR 标准普尔 500 ETF 信托基金(SPY)和先锋标准普尔 500 ETF (VOO)。我从雅虎上下载了这些数据。财务,从开始日期到当前日期的所有日期。模拟中使用的两个关键变量是交易日和每个交易日结束时的调整收盘价(因为收盘价不包括股息)。没有考虑成交量和日内价格。

间谍数据帧的前五行
通过快速绘制数据,我注意到曲线的形状几乎相同(这是意料之中的,因为它们跟踪的是相同的股票)。间谍存在的时间也是 VOO 的两倍多,在过去的三月里,大概是由于新冠肺炎·疫情的原因,它的大量减少非常明显。

幸运的是,我不必为这个简单的时间序列做任何数据清理,所以我们可以直接进行模拟。
仿真法
构成 MC 模拟的主要组件是过程、常量变量、随机变量和样本大小。在这种情况下:
- 流程: DCA 在 12 个月内每个日历月进行一次间谍和 VOO
- 恒定变量:每月 1000 美元投资
- 随机变量:选择一个连续 12 个月的时间段和每个日历月一次的购买日期
- 样本量: 20,000 次(间谍)和 10,000 次(VOO)迭代
模拟开始时,银行里有 1000 美元作为第一个月的购买力,在股票存在的范围内随机选择 12 个月的时间,间谍从 1993 年到 2020 年,VOO 从 2010 年到 2020 年。在随机过程生成器中,允许 12 个月的周期从一年开始,到下一年结束。所有随机过程发生器都运行在均匀分布上。
对于所选日期范围内的每个月,从历史数据中随机选择该月的一个日期作为购买日期。日期随股价而定,用可获得的购买力购买最大数量的股票。除了每月 1000 美元的新收入外,剩余的购买力会滚动到下个月。
在 12 个月的期间内,每个月都重复该过程的这一阶段,以创建一个样本。整个过程对于 SPY 重复 20,000 次,对于 VOO 重复 10,000 次。
每个数据点都在一个循环函数中计算并存储在 Python 中。请查看我在 GitHub 上的代码了解更多细节。
注意:SPY 中的额外迭代是为了说明其更长的存在时间(需要更多的样本来收敛到总体均值)。
结果
在对间谍和 VOO 进行模拟后,可以计算出每个样本从第 1 个月到第 12 个月的年投资回报率,从而得出样本分布。当我们可视化结果时,我们看到一致的分布,稍微偏向负回报,中心在 6–7%的年回报。SPY 中的分布更加广泛,偏离平均值更多,这可能来自 VOO 没有的 1993 年至 2010 年间的额外数据。

间谍和 12 个月投资回报样本分布

12 个月回报统计
无论某人投资哪只 ETF,都有 75%的机会获得至少 1%的回报,这已经是 0.01%利息支票账户的 100 倍。更妙的是,SPY 的最高回报是 27%(虽然概率很低)!
出于好奇,我还对输入进行了一些调整,并模拟了 60 个月的 ROI,以观察它的表现。间谍变得不那么一致,变得更加多变,而 VOO 没有太大变化。

间谍和 VOO 的 60 个月投资回报样本分布

60 个月退货统计
在 5 年的时间里,投资者可以预期获得 30%左右的平均回报。最好的情况是,一项投资可能会因为间谍而翻倍,或者在最坏的情况下损失 40%。
结论
根据这一分析,每月随机选择一天用 DCA 将钱投入标准普尔 500 ETF,几乎肯定比把钱留在支票账户要好。与任何投资一样,风险越高,回报也越高。正如我们在这里看到的,平均 12 个月的投资回报率为 6–7%也有可能出现负投资回报率。SPY 提供了一个略有不同(也许更好?)由于起步较早,对市场的估计比 VOO 大。周期性将是一个有趣的分析点,可以看出每月分期付款与每季度、每两个月分期付款相比如何。最后,这只是一个有趣的项目,给了我一个练习分析技能的借口,并希望获得一些对我个人财务有用的见解。
如果你有兴趣查看我的代码,请查看我的 Github !
Python 中的蒙特卡罗模拟:分析网页浏览
蒙特卡罗模拟使用随机抽样来生成概率分布,模拟感兴趣变量的潜在结果。
这种方法在金融等领域被广泛使用,以模拟各种风险情景。
然而,该方法在时间序列分析的其他方面也有重要的应用。在这个特定的例子中,让我们看看如何使用蒙特卡罗模拟来模拟网页浏览。

来源:维基媒体工具锻造
以上时间序列来源于 Wikimedia Toolforge ,是 2019 年 1 月—2020 年 7 月维基百科上“医疗保健”一词的网页浏览量时间序列。所有数据都是每日格式。
我们可以看到,时间序列显示了每天的显著波动性,并显示了数据中一些奇怪“峰值”的典型轮廓,或者是搜索该术语的频率特别高的日子。
试图对这样的时间序列进行直接预测通常是徒劳的。这是因为不可能从统计上预测搜索词何时会出现峰值,因为这会受到与过去数据无关的影响,例如,与健康相关的重大新闻事件会导致该词的搜索出现峰值。
然而,人们可能特别感兴趣的是创建一个模拟来分析网页统计的许多潜在场景,并估计在异常场景下该搜索词的页面浏览量多高或多低。
概率分布
当运行蒙特卡罗模拟时,注意正在使用的分布类型是很重要的。
鉴于页面浏览量不可能是负数,我们假设分布是正偏的。
这是数据的直方图:

来源:Jupyter 笔记本
我们可以看到,分布显示出正的偏斜,其中一些异常值使分布尾部向右偏斜。
>>> series = value;
>>> skewness = series.skew();
>>> print("Skewness:");
>>> print(round(skewness,2));Skewness:
0.17
该分布的偏斜计算为 0.17 。
QQ 图表明,除了异常值之外,大多数值的分布呈正态分布。

来源:Jupyter 笔记本
然而,更有可能的是,由于正偏斜,该数据表示对数正态分布-将数据转换为对数格式将导致分布的正态性。
>>> mu=np.mean(logvalue)
>>> sigma=np.std(logvalue)
>>> x = mu + sigma * np.random.lognormal(mu, sigma, 10000)
>>> num_bins = 50
这是对数数据的分布,更能代表正态分布。

来源:Jupyter 笔记本
此外,该分布的偏斜现在为 -0.41 。
>>> logvalue=pd.Series(logvalue)
>>> logseries = logvalue;
>>> skewness = logseries.skew();
>>> print("Skewness:");
>>> print(round(skewness,2));Skewness:
-0.41
这表示轻微负偏,但 QQ 图仍然表示正常。

来源:Jupyter 笔记本
蒙特 卡罗模拟
既然数据已经被适当地转换,蒙特卡罗模拟被生成来分析页面视图统计的潜在结果范围。根据所选择的分布,页面浏览量以对数格式表示。
首先,计算时间序列的平均值和波动性(用标准差衡量)。
>>> mu=np.mean(logvalue)
>>> sigma=np.std(logvalue)
>>> x = mu + sigma * np.random.lognormal(mu, sigma, 10000)
>>> num_bins = 50
然后,相关数组由 x 定义,其中 mu 和 sigma 相加,然后乘以 10,000 个随机数,这些随机数按照定义的平均值和标准偏差遵循对数正态分布。
array([5.21777304, 5.58552424, 5.39748092, ..., 5.27737933, 5.42742056, 5.52693816])
现在,让我们绘制直方图。

来源:Jupyter 笔记本
同样,这些值以对数格式表示。我们看到这个形状代表了正态分布。如上所述,蒙特卡罗模拟的思想不是预测网页浏览本身,而是提供对许多不同模拟的网页浏览的估计,以便识别 1)大多数网页浏览的范围和 2)分布中的极值范围。
结论
在本文中,您已经看到:
- 蒙特卡洛模拟的使用
- 偏斜在定义分布中的作用
- 如何实现模拟来识别获得极值的概率
非常感谢您的参与,您也可以在 michael-grogan.com的找到我更多的数据科学内容。
免责声明:本文是在“原样”的基础上编写的,没有任何担保。本文旨在提供数据科学概念的概述,不应以任何方式解释为专业建议。
蒙特卡罗树搜索:在实时游戏玩家中实现强化学习
第 1 部分—强化学习的基本概念

简介
增强学习是一种机器学习技术,我们可以设计一个代理,它可以通过与环境交互来获得经验,当它犯更多错误时,它会学习如何修改行为以避免惩罚并获得更多奖励。
你将要读到的将是一系列文章的一部分,我们正在经历一个普通游戏玩家在一个棋盘游戏上的实现的起起落落。如果你很急,你可以在这里找到纯 python 代码:
蒙特卡罗树搜索(MCTS)是一种在给定领域通过随机抽样寻找最优决策的方法
github.com](https://github.com/masouduut94/MCTS-agent-python)
还有一个更新的版本,使用 cython 来加速这里的模拟:
蒙特卡罗树搜索(MCTS)是一种在给定领域通过随机抽样寻找最优决策的方法
github.com](https://github.com/masouduut94/MCTS-agent-cythonized)
这是最终产品的演示。看看这个:

图 1:这张 gif 展示了最终软件的简单演示。如你所见,黑棋和白棋玩家在棋盘上互相竞争选择战略棋步。
因为在开始实现之前,我必须用理论概念来温暖你的视野,所以在这篇最开始的文章中,我想把你的注意力吸引到游戏环境(比如象棋)如何被建模为强化学习问题上。通过这种方式,我试图保持内容简单(少一些数学和公式)和更具体。该系列的整体重点是蒙特卡罗树搜索算法,以及如何在一个简单的棋盘游戏上实现它,即作为十六进制。
概要
- 问题描述
- 什么是强化学习
- q 学习
- 结论
问题描述
让我们假设你是一名棋手,把这块棋盘看作游戏的当前状态:

图 1:当前游戏状态。
在这个图像中,有一个属于象棋游戏的环境。游戏由各种情况(状态)组成,在这些情况下,我们必须决定在其他行动中选择最佳行动。我们选择的行动取决于状态,每个行动都会引起环境的变化,而这些变化可能会带来影响未来决策的后果。
例如,在图 1 的当前情况下, G5 中的黑棋兵不能在棋盘上采取任何行动(根据国际象棋规则)。
现在的问题是,在上图中(假设轮到黑棋),我们如何确定某个玩家选择了最好的 棋步(代表黑棋)?

图 2:图 1 中有 3 个突出显示的选项供黑人玩家选择
以下是一些黑人玩家选项:
- 移动 D8 中的黑车,取出 D2 中的白主教。
- 移动 G4 中的黑骑士,取出 E3 中的白皇后。
- 移动 H5 中的黑皇后并将其放置在 H1 、将死 并赢得游戏。
显然最后一步是最好的一步,因为赢得比赛是每个棋手的最终目标。在国际象棋中,除掉对手的棋子可能会增加获胜的机会,但这不是最终目的。即使你的棋子比棋盘上对手的多,你也可能不是赢家(例如,查看下图)。

图 2:游戏状态示例#2
在此图中,黑人玩家是赢家,尽管白人玩家的棋子数量超过了黑人玩家。
换句话说,有一个即时奖励(取出对手棋子)和长期奖励(赢得比赛)而后者是玩家更想要的。
让我们总结一下到目前为止我们所讨论的内容:
游戏是一个由状态组成的环境。在每个状态下,玩家可以选择特定的动作。每个动作都会引起状态转换。这种状态转换可能对玩家有利/不利。
最佳玩家(赢家)是拥有更好的计划 (策略)以最大化长期回报的人。
这听起来不像人类实现目标的方式吗?
拥有更好感知(更好规划)的人类往往牺牲自己的短期回报(比如吃快餐)来最大化长期回报(塑形)。强化学习专门致力于设计能够通过与环境互动来学习最大化长期回报的代理。
好的。现在我们已经对这个主题有了一些背景知识,让我们回顾一下强化学习的基础知识,并学习我们如何利用它来模拟一般的游戏。
什么是强化学习?
R.Sutton 等人[1]很好地描述了 RL:
强化学习是学习做什么——如何将情况映射到行动——以便最大化数字奖励信号。学习者没有被告知要采取哪些行动,而是必须通过尝试来发现哪些行动会产生最大的回报。

强化学习代理在环境中采取行动,观察状态并接受奖励。它根据累积的奖励开始计划。
RL 的关键要素包括:
- 代理人。在这里,它是一个通过反复试验来学习如何玩游戏的程序
- 环境(州)。代理执行动作的世界(这里是国际象棋游戏)。
- 行动。代理进行的移动,将环境状态更改为新状态。
- 奖励。对行动的评估,可以是积极的或消极的,也可以是延迟的。
正如[1]断言的RL 与其他技术的关键区别在于其试错性质和延迟回报。我发现【2】和【3】最能理解 RL 和其他技术的区别。
强化学习环境可以用马尔可夫决策过程(MDP) 来建模。
MDP 提供了一个数学框架,用于模拟在结果部分随机、部分受决策者控制的情况下的顺序决策(就像游戏一样,我们只能控制自己的行动,但无法控制对手的行动)。
在 MDP 环境中,代理人需要找到一个 最优策略 来最大化报酬。
我推荐你看这个视频,它总结了我到目前为止的所有解释,并且最好地解释了数学符号:
问—学习
现在我们需要找出一种方法来确定最优策略。
当人类想要找到游戏状态的最佳可能解决方案时,他的方法是什么?
他/她保持估计(的质量)他当前状态下的行动,同时(在他的想象中)思考对手对他/她的行动的最佳策略是什么。通过这种方式,她/他会选择最有可能获得回报的行动(当然,人类不可能想象所有的场景并记住所有的结果!).
为了将上述陈述与我们的项目相关联,我们需要设计一个代理,该代理能够通过试错法探索环境,并通过理解 其在状态中的动作质量来对其建模。但是这种试错可以发生在它的大脑中,而不是在它与之交互的环境中(就像人类如何在大脑中模拟游戏一样)。
我推荐 Udacity 团队提供的这个短视频,它澄清了由代理建模的环境和代理 与交互的环境的区别:
这就是 q 学习方法的用武之地。Q learning 是一种 RL 算法,它从当前状态(规划)开始,针对任何和所有连续状态,在最大化长期回报的上学习最优策略。如果给无限的探索时间和部分随机策略 4 ,Q-learning 可以学习到最佳策略。查看这篇文章,了解上下文中的更多细节。
结论
好的。我们了解到基于 RL 的智能体是一种通过选择状态中的动作来与环境交互的智能体。通过这样做,它改变了环境状态并转换到其他状态。这种转变可能伴随着作为其行为结果的奖励/惩罚(并且这种结果可能不是直接的)。代理的目标是找到最佳策略来选择最大化回报的行动。最后,我用一些问题来结束这篇文章,这些问题将在下一篇文章中回答。
1 —如果在其他步骤之间只有一个最佳步骤,如果由于时间有限,我们无法逐一处理所有连续的状态,我们如何找到它?
2 —如果我们在计算资源和时间方面受到限制,我们如何将寻找最佳方案转化为长期回报?
为了解决这些问题,我们将在下一篇文章的中挖掘蒙特卡洛树搜索概念及其解决方案。
参考
2—https://medium . com/ai % C2 % B3-理论-实践-商业/强化-学习-第二部分-介绍-马尔可夫-过程-d3586d4003e0
3—https://medium . com/ai % C2 % B3-理论-实践-商业/强化-学习-第三部分-马尔可夫-决策-过程-9f5066e073a2
4—http://www.leemon.com/papers/1995b.pdf
蒙特卡罗树搜索:在实时游戏玩家中实现强化学习|第 2 部分
第 2 部分—蒙特卡罗树搜索基本概念

安妮·尼加德在 Unsplash 拍摄的照片
在上一篇文章中,我们介绍了强化学习的基本概念,并以这两个关键问题结束了这篇文章:
1 —如果由于时间有限,我们无法逐一处理所有连续的状态,我们如何才能找到最佳方案?
2 —如果我们在计算资源和时间方面受到限制,我们如何将寻找最佳行动的任务映射到长期回报?
在本文中,为了回答这些问题,我们通过蒙特卡洛树搜索基础。因为在接下来的文章中,我们将在“HEX”棋盘游戏中实现这个算法,所以我尝试在这个棋盘游戏环境中通过例子来解释这个概念。
如果您对代码更感兴趣,请在此链接中找到它。由于使用了cython,还有一个更优化的版本适用于 linux,你可以在这里找到它。
以下是概要:
1 —概述
2 —勘探与开采的权衡
经典棋盘游戏
4 —算法结构:选择和扩展
5 —算法结构:卷展栏
6 —算法结构:反向传播
7 —优点和缺点
概述
蒙特卡洛方法是 Stanislaw Ulam 在应用统计方法“蒙特卡洛方法”后首次提出的。概念很简单。利用随机性解决原则上可能是确定性的问题。例如,在数学中,当我们不能直接计算积分时,它用于估计积分。同样在这张图中,你可以看到我们如何基于蒙特卡罗模拟来计算圆周率。

图 1:借助蒙特卡罗模拟计算圆周率(来源)。
上图表明,在蒙特卡罗方法中,我们收集的样本越多,对目标值的估计就越准确。
- 但是蒙特卡罗方法是如何在一般游戏中派上用场的呢?
当我们无法处理所有状态时,我们使用蒙特卡罗方法基于模拟随机估计状态 的质量。每个模拟都是一个自我游戏,从当前状态开始遍历游戏树,直到到达叶子状态(游戏结束)。
所以这个算法对我们的问题来说是完美的。
-因为它对未来状态-动作空间进行采样,所以它可以通过保持低计算量来估计当前状态下的接近最优的动作(这解决了第一个问题)。
-此外,它根据长期回报(根据树叶的结果进行奖励)选择最佳行动的事实回答了第二个问题。
这个过程就像当一个人想要估计未来的行动,以在国际象棋比赛中提出最好的行动。他会根据自己的想法模拟各种游戏(从当前状态到未来最后可能的状态),并选择整体效果最好的游戏。
蒙特卡洛树搜索(MCTS) 将蒙特卡洛方法与树搜索结合起来,是一种通过在决策空间中随机取样并根据结果构建搜索树,在给定域中寻找最优决策的方法。
在解释算法结构之前,我们应该先讨论一下探索和开发的权衡。
勘探与开采的权衡
如前所述,在强化学习中,一个智能体总是通过重复使用在该问题中找到的最佳行动来实现最优策略(还记得上一篇文章中的象棋例子)。然而,有可能当前的最佳行动实际上并不是最佳的。因此,它将在学习阶段通过执行替代方案而不是感知的最优方案来继续定期评估替代方案。在 RL 术语中,这被称为勘探开发权衡。RL 中的所有算法(MCTS 也是)都试图平衡探索-开发的权衡。
我认为这个视频最好地解释了探索-开发的概念:
HEX:经典桌游
现在是时候了解一下 Hex 游戏了。它有简单的规则:

图 2:六角板。赢家是白玩家,因为它用链石连接了白双方。
- 黑白相间。
- 每回合一名玩家在任何未被占据的格子上放置一块与其颜色相同的石头。
- 获胜者是将他们的石头形成一串连接他们两个相对的棋盘边的玩家。
- Hex 永远不能以和局结束,可以在任何 n × n 棋盘上玩【1】。
现在让我们来看一下算法结构。
算法结构
1 —选择和扩展
在这一步中,agent 获取游戏的当前状态并在树中选择一个节点(每个节点代表选择一个动作所产生的状态)并遍历该树。每个状态中的每个移动都分配有两个参数,即总展开和每次展开的胜率(它们将在“展开”一节中介绍)。
在其他节点中选择最佳节点的策略非常重要。应用于树的置信上限(UCT)是选择最优节点的最简单而有效的策略。这一战略旨在平衡开采-勘探之间的权衡。这是 UCT 公式:

图 3: UCT 公式。第一项(w/n)表示开采,第二项计算勘探项(c * sqrt(log t / n))
在此公式中,i表示子节点中的第 I 个节点。W是每次推广的获胜次数,n是所有推广的次数。公式的这一部分代表剥削。
C为勘探系数,在[0,1]范围内为常数。此参数指示代理必须在多大程度上支持未探索的节点。
t是父节点中转出的数量。第二个术语代表探索术语。
让我们看一个例子,以便理解提供的所有信息。请看下图:

图 4:选择阶段
考虑深度 2 中的动作 C3(黑色转弯)。总模拟次数(公式中的 n _i )为 2,成功的推广次数(公式中的 w_i )为 1。在公式中,t 将是深度为 1 的父节点的转出数(在这里表示在白色转弯中移动 B2 ),即 4。正如你所看到的,选择阶段在我们有一个未访问节点的深度停止。然后在扩展阶段,当我们在深度 4 中访问 B1 时,我们将它添加到树中。
2 —首次展示(也称为模拟、播出)
在这一步中,基于预定义的策略(比如完全随机选择),我们选择动作,直到到达一个终止状态。在终端状态下,当前玩家的游戏结果要么是 0 (如果输了游戏)要么是 1 (如果赢了游戏)。在十六进制游戏中,终端状态总是可达的,游戏的结果是输或赢(没有平局)。但是在像象棋这样的游戏中,由于象棋分支因子的可扩展性和搜索树的深度,我们可能会陷入无限循环。

图 5:展示了前面步骤(选择和扩展)之后的展示阶段
在上面的图像中,当那个黑人玩家在扩展步骤中选择了 B1 之后,在模拟步骤中,一个卷展栏被启动到游戏的终端状态。
在这里,我们选择随机行动来达到游戏的最终状态。如你所见,在终端状态下,白玩家通过用石头将左右连接起来赢得了游戏。现在是在反向传播部分使用这些信息的时候了。
3 —反向传播
在这一部分中,我们更新了在选择和扩展部分的树中遍历的节点中的统计数据(首次展示数量和每次总首次展示的成功数量)。
在反向传播过程中,我们需要更新节点的首次展示数量和成功/失败统计。我们唯一需要的是找出在 rollout 中赢得游戏的玩家(例如图 4 中的白人玩家)。
对于图 4,由于黑人玩家是赢家(他选择了终止状态中的动作),所有由黑人玩家动作导致的状态都被奖励 1,而由白人玩家动作导致的状态被给予 0 奖励(我们可以通过将它设置为-1 来选择惩罚)。
对于所有状态(通过步骤 1 选择的树节点),如图 6 所示,总展开数增加 1。

图 6:通过树搜索更新节点的赢(对于黑人玩家)、输(对于白人玩家)和转出总数。
这些步骤不断重复,直到一个预定义的条件结束循环(如时间限制)。
优缺点
优点:
1 — MCTS 是一种易于实现的算法。
2-蒙特卡罗树搜索是一种启发式算法。除了规则和结束条件之外,MCTS 可以在没有任何特定领域知识的情况下有效地操作,并可以通过随机播放来找到自己的移动并从中学习。
3-MCTS 可以保存在任何中间状态,并且该状态可以在将来任何需要的时候用于用例。
4-MCTS 支持基于搜索树运行环境的不对称扩展。
缺点:
1-随着树在几次迭代后变得快速增长,它可能需要大量的内存。
2-蒙特卡罗树搜索有一点可靠性问题。在某些情况下,可能会有一个单独的分支或路径,这可能会导致在实施回合制游戏时输给对手。这主要是由于大量的组合,并且每个节点可能没有被访问足够的次数来理解其结果或长期结果。
3-MCTS 算法需要大量的迭代才能有效地决定最有效的路径。所以,这里有一点速度问题。
games 可以随时返回建议的移动,因为关于模拟游戏的统计数据会不断更新。当算法开始时,推荐的移动不是很好,但是它们随着算法的运行而不断改进。
结论
现在,我们弄清楚了 MCTS 算法如何有效地利用随机性对所有可能的场景进行采样,并通过模拟得出最佳行动。MCTS 在每个时期所选择的行动的质量取决于它如何处理环境中的勘探和开发。
好了,到目前为止,我们已经讨论了必要的理论概念,我们可以进入下一个层次,用代码来弄脏我们的手。在下一篇文章中,首先,我们将描述整个框架和需要实现的模块,然后我们将实现 UCT 的基本 MCTS。之后,我们将通过向代码中添加更多功能来改进框架。
如果你对话题感兴趣,有问题,我随时恭候,乐意倾听。在 linkedin 和 StackoverFlow 找到我。
蒙特卡罗树搜索:在实时游戏玩家中实现强化学习|第 3 部分
第 3 部分—MCTS 算法的实现,该算法可以在有限的时间内处理实时游戏“HEX”中的决策。

介绍
在之前的文章中,我们学习了强化学习基础和蒙特卡罗树搜索基础。我们介绍了 MCTS 如何搜索所有的状态动作空间,并根据对搜索空间进行采样后收集的统计数据得出一个好的动作。
对于那些着急的人,这是该项目的完整代码:
蒙特卡罗树搜索(MCTS)是一种在给定领域通过随机抽样寻找最优决策的方法
github.com](https://github.com/masouduut94/MCTS-agent-python) 
图 1:游戏的演示。作者在 Github 上的图片。
这张 gif 展示了最终产品的演示。正如您在 GUI 中点击生成按钮所看到的,MCTS 代理选择了最佳可能的移动。黑人和白人玩家之间的竞争欲望真的很有趣,因为对我们人类来说,游戏棋盘上的移动选择听起来很有战略意义。
我必须感谢肯尼·杨,他出色地实现了 mopyhex,这对我的项目帮助很大。这篇文章主要包括解释他的代码。在接下来的文章中,我们将进一步为这个项目添加一些贡献,以便在状态-动作空间中进行更健壮的搜索,并使用cython工具优化 C 级速度的代码。
模块概述
在接触代码之前,为了给出项目的概述,让我描述一下每个模块的功能。基本上,初始框架需要实现 6 个类:
GameState:这个模块简单的提供了树的每个节点中关于游戏状态的每一个基本信息。UnionFind:该模块是帮助GameState检查两侧是否连接的实用模块。换句话说,在这个模块的帮助下,我们可以在游戏的任何状态下检测出赢家,如果有的话。Node:这个实现了指示每个游戏状态的树的每个节点。每个节点都有一个基于 UCT 算法的评估函数。UctMctsAgent:这个处理蒙特卡罗树搜索算法的树结构。Gui:这个模块使用Tkinter模块实现了一个简单的图形用户界面,使游戏中的人机交互成为可能。MCTSMeta和GameMeta:这些模块只是存储一些常量。
我已经实现了一些算法,这些算法使用了快速动作评估的概念,并且在短期搜索中比 UCT 算法更强。让它变得更加强大。我将在后面讲述这些算法的概念。
游戏状态模块:
在每个游戏状态树中,搜索代理需要知道三件事才能从一个状态转换到另一个状态:
- 轮到哪个玩家了。
- 未被占用的棋盘单元格(可供选择的棋步)。
- 能够为黑人和白人玩家检测双方之间的连接。
后一种实现起来相当棘手。因为要检测连接,我们必须跟踪不相交的细胞群。因此,我们必须利用数据结构,即不相交集(也称为联合查找)。但是 HEX 游戏板单元从 6 个侧面连接到它的邻居的事实,使得它更难实现。看看下面的图片:

图 2:一个黑色单元从 6 个侧面连接到其相邻单元。作者图片
那么我们如何处理这个难题呢?
首先,我使用 NumPy 数组来保存游戏板的值。未被占用的像元值为 0,相应地,白色像元和黑色像元值为 1 和 2。此图以阵列形式展示了一个 8X8 游戏板:

图 3:阵列状单元上的游戏棋盘表示。作者图片
每个单元有 8 个相邻单元。您在这里看到的值是在每个单元格上的坐标,这些单元格在GameMeta中被定义为邻域,以将相邻单元格限制为 6:
NEIGHBOR_PATTERNS = ((-1, 0),(0, -1),(-1, 1),(0, 1),(1, 0),(1, -1))
图 2 中单元的邻域现在可以表示如下:

图 4:新的邻居小区被限制为 6 个(适用于六边形小区)。作者图片
最终在我们的游戏 GUI 中,它变成了类似图 1 的六边形。
UnionFind 类:
现在是时候调整这个游戏的不相交集数据结构了。根据维基百科:
在计算机科学中,不相交集数据结构(也称为 union-find)是一种跟踪被划分成几个不相交(非重叠)子集的一组元素的数据结构。

图 5:白色和黑色不相交的集合。作者图片
上图显示了这些不相交的集合,它们是游戏环境的例子。如果你想知道更多关于不相交集合数据结构的概念,你可能想在这里检查这个链接。这是我想出的unionfind.py的代码:
方法:
join:如果输入板单元格不在同一个组中,则将其加入到单元格组中。find:查找给定单元的代表元素(可能是一个集合)。connected:检查两个元素是否在同一组。该功能用于检查电路板的两侧是否连接,这意味着它有助于我们检测获胜者。看看图 3。set_ignored_elements:在不相交的集合中,必须忽略边缘。get_groups:返回游戏状态下的各组单元格。

图 6:白边连接。这个连接将被 connected 方法检测到。作者图片
现在让我们定义游戏状态模块。这是GameState类的代码:
这就是考虑属性的原因:
size:表示板的尺寸(尺寸 X 尺寸)to_play:表示该轮是黑棋还是白棋。board:2d NumPy 数组,保存板单元值(无论每个单元是黑、白还是空)。White_played和black_played:游戏过程中每个玩家选择的移动次数。(这些值的用法将在本系列的下一集解释)。white_groups和black_groups:为了使用UnionFind类,我们通过给模块分配这些变量来创建模块的实例。
让我们来描述一下这些功能:
play:轮到的玩家拥有作为输入传递的单元格。get_num_played:返回每个玩家出招次数的字典。get_white_groups和 get_black_groups:返回每个玩家组。place_white和place_black:将石头放在棋盘上,然后如果石头是边的邻居,它们通过‘union find . join’函数连接。那么与该小区具有相同值的所有相邻小区也加入该组。turn:这是一个简单返回turn属性的方法。set_turn:要改变转弯,我们使用此功能。winner:这个属性检查两边之间是否有连接,如果有就返回胜者。否则不返回任何内容。neighbors:返回所有邻居单元格。moves:返回当前棋盘状态下所有可能走法的列表。
这是meta.py中的GameMeta:
节点模块:
现在我们已经实现了游戏状态,是时候定义蒙特卡罗树搜索代理了。首先,我们必须为树的节点定义一个好的结构。这是定义Node 模块的代码:
每个节点都具有以下属性:
move:树中的每个节点表示棋盘中的单元格,我们将其定义为每个状态下的潜在动作。parent:指向父节点。N:这是在选择阶段该节点被访问的次数。Q:这是从模拟中的失败次数中减去的成功次数。N_RAVE和Q_RAVE:这些项目属于 RAVE(快速动作评估)算法,将在即将到来的未来文章中解释。children:由此节点产生的下一个状态。- 如果节点是一片叶子,那么结果将显示获胜者。否则没有。
方法
add_children:它得到一个移动字典,并把它们存储在children属性中。value:该属性根据本节点的N和Q属性以及父属性N计算 UCT 公式。UCT 公式中使用的explore常数是 UCT 的探索项,在MCTSMeta.EXPLORATION中有定义。
请注意,当某个节点没有N时,我们必须将该节点的值设置为无穷大,因为必须首先选择该节点。并且下一次基于N和Q,代理可以将其与其兄弟进行比较。
让我们一步一步地编写MCTSAgent代码。首先,让我们开始上课:
如果你熟悉树数据结构的概念,你就知道如果你有了树节点并且遍历到你想去的任何地方,树中的所有节点都是可达的。
所以我们定义root为树根节点,rootstate为树的根状态。然后,当我们遇到每一步棋时,我们在rootstate中进行游戏,然后将状态转换为新的状态(交替玩家回合,检查黑组和白组是否有任何联系,…).
属性run_time、node_count和num_rollouts 仅用于跟踪一些统计数据。
选择和扩展阶段
现在我们定义选择和扩展阶段:
正如代码所暗示的,以及我之前提到的关于树遍历,我们首先复制游戏的root 和root_state (第 6 行和第 7 行)。然后,直到我们没有遇到任何叶子(这意味着游戏没有结束或者代理仍然没有探索树),我们继续选择具有最大 UCT 值的节点(在第 13 行和第 14 行中计算),然后通过进行实际移动来转换状态(第 17 行)。如果我们到达某个N等于0的节点,我们选择它。在我们返回node和state之前,我们必须确定节点是否可扩展。所以在expand方法中,首先我们确定游戏没有赢家,如果没有,我们给展开的节点添加子节点。因此节点及其状态随后被返回。
模拟阶段
对于模拟部分,我们得到了以下代码:
代理简单地获得游戏的状态,它继续玩随机移动,这是默认策略(在下一篇文章中,我将包括更多令人敬畏的策略),并转换state,直到我们到达一个终端状态,在该状态中检测到游戏获胜者。
反向传播
这是该步骤的代码:
在这段代码中,我们有三个输入:
- node:是我们从
select_node方法的输出中得到的节点。 - 转弯:这表示玩家进入了
select_node方法的第二个输出状态。 - 结果:这是模拟阶段的输出,是模拟的赢家
我们在反向传播部分要做的是(如这里讨论的,看一下图 6)从node开始,向后遍历选择的节点(第 23 行)并更新统计(第 21 行,第 22 行),直到我们到达根节点(第 20 行,因为树节点没有父节点)。
我们在输家玩家选择的节点和赢家玩家选择的节点之间交替,所以我们必须给赢家节点+1 奖励,给输家节点 0 奖励(第 24 行)。
另外,请注意,第 18 行表示,如果选择了该移动并到达node状态的玩家获胜,我们必须给予他+1 奖励。如果不是这样,玩家将被罚以0奖励。
现在我们必须定义一个循环,在这个循环中,我们应该完成选择、扩展、模拟和反向传播的整个过程,这里提供了:
在我实现这段代码的时候,python 3.4 是最新的版本。由于在第 7 行中,我使用了clock函数,而在 python 3.8 中clock函数在time库中被弃用,所以我将from time import clock修改为from time import time as clock(很酷的把戏,对吧!?).
在这段代码中,time_budget(顾名思义)是我们可以进行尽可能多的 MCTS 模拟的时间(第 11 行)。选择和扩展在第 12 行完成,变量turn保持当前玩家回合,然后相应地在第 14 行和第 15 行完成卷展和反向传播。
在第 18 行,我们对树中的节点进行计数,并使用方法tree_size将它们存储在变量node_count中,我马上会添加这个方法。
这是整个MCTSAgent 类的代码:
正如您在代码中看到的,有一些实用函数。best_move方法(第 140 行)返回基于N 值的最佳移动(模拟次数最多的节点。模拟越多,其探索的路径越好)。
move方法用于在实际的游戏环境中走棋。这样,我们必须更新树root和rootstate。新的根将是当前根的子根,带有root.children字典的move键。新的rootstate将是游戏中已经使用过的move转换而来的state。
因此,当我们在游戏环境中转换到一个新状态时,我们必须移除到其父状态的连接(第 165 行),我们还更改了rootstate(第 167 行)。
请注意,我插入第 172 行和第 173 行的原因是,在游戏 GUI 中,可以选择不考虑回合的移动(就像黑人玩家连续选择 2 个单元格,而不让白人玩家在其回合中选择移动)。因此,为了在代码中实现这种灵活性,我们必须重置整个树(丢弃当前树)并重新开始 MCTS 过程。
方法set_gamestate给了我们重置游戏状态的能力。所以我们可以改变棋盘的大小,重新开始。方法tree_size在Queue库的帮助下对树节点进行计数。
我们还需要在meta.py中定义这个:
Gui 模块:
最后,我们为框架设计图形用户界面。Gui类是在tkinter 模块的帮助下开发的。这个模块很容易理解,所以我只解释一些重要的部分。这是 500 行代码!
重要方法:
top_left_hexagon:该方法在该函数中返回环境中左上角六边形单元格的坐标。hexagon:这个用tkinter.Canvas.create_polygon画一个六边形并返回。generate_row:在这个方法中,根据它的颜色生成一行六边形:这个函数获取 gameboard 数组,绘制整个游戏板。generate_white_edge和generate_black_edge:这些函数生成板的边缘,在那里单元格都是黑色或白色的。- 这个功能可以让玩家与游戏互动。当在 GUI 中单击一个单元格时,它会用指向 MCTS 代理的
self.agent播放。 set_size:该方法检测 self.game_size 按钮的事件,用新的尺寸重置整个棋盘。它还会重置代理树。set_time:通过此功能调整 MCTS 搜索的时间预算。Set_agent:在我解释了 RAVE 算法之后,这个函数将在 UCT 和 RAVE 代理之间切换。click_to_bot_play:这个设计是让代理玩家根据我们之前讨论的MCTSAgent.best_move方法选择移动。
现在我们必须定义一个循环,在这个循环中,我们应该完成选择、扩展、模拟和反向传播的整个过程,这里提供了:
在我实现这段代码的时候,python 3.4 是最新的版本。由于在第 7 行中,我使用了clock函数,而在 python 3.8 中clock函数在time库中被弃用,所以我将from time import clock修改为from time import time as clock(很酷的把戏,对吧!?).
在这段代码中,time_budget(顾名思义)是我们可以尽可能多地进行 MCTS 模拟的时间(第 11 行)。选择和扩展在第 12 行完成,turn变量保持当前玩家回合,然后相应地在第 14 行和第 15 行完成展示和反向传播。
在第 18 行,我们对树中的节点进行计数,并使用tree_size方法将其保存在node_count变量中,我马上会添加这个方法。
这是整个MCTSAgent 类的代码:
正如您在代码中看到的,有一些实用函数。best_move方法(第 140 行)返回基于N 值的最佳移动(模拟次数最多的节点。模拟越多,其探索的路径越好)。
move方法用于在实际游戏环境中进行移动。这样,我们必须更新树root和rootstate。新的根将是当前根的孩子,带有root.children字典的move键。新的rootstate将会是游戏中已经使用过的move转换后的state。
因此,当我们在游戏环境中转换到一个新状态时,我们必须移除与其父状态的连接(第 165 行),我们还更改了rootstate(第 167 行)。
请注意,我插入第 172 行和第 173 行的原因是,在游戏 GUI 中,可以选择不考虑回合的移动(就像黑人玩家连续选择 2 个单元格,而不让白人玩家在其回合中选择移动)。因此,为了在代码中实现这种灵活性,我们必须重置整个树(丢弃当前树)并重新开始 MCTS 过程。
方法set_gamestate给了我们重置游戏状态的能力。所以我们可以改变棋盘的大小,重新开始。方法tree_size在Queue库的帮助下对树节点进行计数。
我们还需要在meta.py中定义这个:
Gui 模块:
最后,我们为框架设计图形用户界面。Gui类是在tkinter 模块的帮助下开发的。这个模块很容易理解,所以我只解释一些重要的部分。这是 500 行代码!
重要方法:
top_left_hexagon:该方法在该函数中返回环境中左上角六边形单元格的坐标。hexagon:这个用tkinter.Canvas.create_polygon画一个六边形并返回。generate_row:在该方法中,根据颜色生成一行六边形gameboard2hexagons:该函数获取游戏板数组,绘制整个游戏板。generate_white_edge和generate_black_edge:这些函数生成板的边缘,其中的单元格全部为黑色或白色。click2play:这个功能可以实现人与游戏的互动。当在游戏环境中选择一步棋时,它会用指向代理的self.agent来玩。set_size:该方法检测 self.game_size 按钮的事件,用新的尺寸重置整个棋盘。它还会重置代理树。set_time:mcts 搜索的时间预算由该功能调整。Set_agent:在我在下一篇文章中解释了 RAVE 算法之后,你会看到这个函数会在 UCT 和 RAVE 代理之间转换代理。click_to_bot_play:这个设计是让代理玩家根据我们之前讨论的“MCTSAgent.best_move”方法选择移动。
现在是运行代码的时候了:
添加main.py并将这段代码复制粘贴到那里:
结论
在本文中,我们用纯 python 实现了一个蒙特卡罗树搜索算法,包含了尽可能少的外部库。该项目的完整代码可以在这里找到。这个项目是一个简单的 UCT算法的实现。
虽然 UCT 算法是精确的,但是它需要大量的模拟来收敛到可用的最优解。为了提高算法性能,有很多选项,如 RAVE,果断行动,最后一次良好的答复,和 PoolRAVE,…算法。这些算法将在接下来的文章中进一步解释。
顺便说一下,如果你想找一个有经验的研究员加入你的团队,我完全可以。我在强化学习、计算机视觉和数据科学方面有一些经验。你可以在 LinkedIn 上找到我。
Swift 中的蒙特卡罗树搜索(MCTS)人工智能游戏

尼克·卡沃尼斯在 Unsplash 上的照片
再加上在 Jupyter 笔记本里设置 S4TF!
- Swift 不只是苹果的
- 玩游戏的人工智能很棒
- Swift 是不是只为苹果!
在这篇文章中,我将解释我如何在 Jupyter 笔记本中设置 Swift,然后我如何使用 Swift 创建一个有时间限制的蒙特卡罗树搜索算法代理,它可以在 17 秒内玩 2048。整齐!
《2048》之所以是一个测试人工智能代理的有趣游戏,是因为它不是确定性的:它是随机的。因此——不像其他一些用于测试人工智能的传统游戏,如 CartPole 和 noughts & crosses——这不是一个神经网络适合的问题。相反,我们可以使用随机算法,询问“哪一步造成的伤害最小?”一遍又一遍,并能快速浏览场景。
你说速度?是的——使用 Swift 可以让我们创建一个每秒能运行 55 步的代理,这是相当快的速度。
2048 年的游戏
由意大利网络开发商 Gabriele Cirulli 设计,2048 在 4x4 的棋盘上玩,初始化时你有两个随机的瓷砖,上面有“2”或“4”。通过向左、向右、向上或向下滑动,您可以将接触到的匹配值牌相加(例如,“2”和“2”变成“4”),以便通过将所有牌相加得到数字“2048”来获胜。每一回合,新的牌上都会生成“2”或“4 ”,这意味着在您到达目标之前,棋盘很容易填满并被阻挡!
2048 年很复杂,因为你玩得越多,每一步棋对最终棋盘状态的影响就越深远。我们不想让我们的人工智能太容易。
限时蒙特卡罗树搜索(T-B MCTS)
MCTS 是一种与人工智能一起使用的搜索技术,它是概率性的和启发式的,将经典的树搜索与强化学习的机器学习(ML)原则结合在一起。作为一种算法,它于 2006 年推出,并已被广泛应用于许多游戏实例中,包括国际象棋、扑克,甚至是战役。
它利用探索和定期评估,处理当前最佳行动实际上可能不是最佳行动的可能性(这是扩展树的宽度并使用随机化)。这通过利用方法来平衡,通过坚持具有最大估计值的路径来避免低效率(这是扩展树的深度)。
为什么有时间限制?当使用一个详细的算法时,定义一个做出决定的最大时间是很重要的。事实上,你分配给这个过程的时间(资源)越多越好,这是一个权衡。在这个例子中,我给每个决定分配了 0.015 秒。
要想全面详细地了解 MCTS,我推荐看看 GeeksforGeeks 上这篇 有用的文章。
首先,让我们把一切都准备好。
请注意:我将介绍我在课程中使用的设置,其中包括用于 TensorFlow 的 Swift(S4TF ),但它肯定不是创建 MCTS 代理的唯一可能设置。
设置
- 除非你使用 Linux 或 Mac,否则你需要一个 Linux 的 Windows 子系统(WSL)。我使用 Ubuntu 18.04(在微软商店免费获得)
- 为 TensorFlow 安装Swift
Swift Jupyter 内核的可选设置
这花费了我比预期更长的时间,但是如果你想以一种干净和可共享的格式展示你的 Swift 作品(就像使用 Jupyter 一样),这是值得的。)
我找到的最有价值的指南是在 Swift-Jupyter repo。有几个选项可供设置,包括 conda 和 Docker 选项,但我发现使用 Swift for TensorFlow toolchain 和 virtualenv 效果很好(最终)。
提示:我花了这么长时间试图在 LLDB Python3 支持下设计出 Swift 工具链。我最终在 Ubuntu 中使用了 curl:
$ curl -O [https://storage.googleapis.com/swift-tensorfl](https://storage.googleapis.com/swift-tensorfl)ow-artifacts/nightlies/latest/swift-tensorflow-DEVELOPMENT-ubuntu18.04.tar.gz
如果你不打算偏离标准,那么一旦你在 Ubuntu 中有了 Swift 工具链,你需要做的就是运行这个:
$ git clone https://github.com/google/swift-jupyter.git
cd swift-jupyter
python3 -m venv venv
. venv/bin/activate
pip install -r requirements.txt
python register.py --sys-prefix --swift-toolchain ~/swift-jupyter
最后,Jupyter 笔记本可以使用 Swift 内核发布。

漂亮!
玩游戏
使用 Swift(无论是在上述 Jupyter 笔记本中,还是在您偏好的其他环境中),您需要导入提供基础层功能的基础框架:
import Foundation
我们使用一个大的坏代码块来管理 2048 游戏,并确保它快速工作。这是一个位板实现:
太棒了。现在我们需要代理自动游戏,这意味着一个函数,它接受一个游戏状态,返回移动的方向,然后执行随机移动。它还应该返回棋盘的分数。
重要的是,这是我们定义算法做出决定所需要的最大时间量的地方(假设 maxTime = 0.015)。
它还创建了一个保存所选方向和相关分数的字典,返回平均分数最高的方向:
最后,我们有了一个可以工作的 2048 机器人,哇哦!现在我们需要初始化一个新的游戏实例,运行一个 while 循环,重复直到游戏结束。在这个循环中,它将决定最好的走法,玩它,并打印新的棋盘状态:

真快!
正如你所看到的,人工智能仅用了 17 秒就走了 961 步,速度接近每秒 56 步。这还不包括优化,所以你当然可以运行得更快更好!
你可以在我的 Github 这里 找到完整的笔记本和所有代码,并查看我之前为迈向数据科学所做的工作!
是时候主动出击而不是被动应对了
towardsdatascience.com](/chaos-in-the-calm-what-is-chaos-engineering-5311c27b2c7e) [## 我如何使用 Python 代码来提高我的韩语水平
“你好世界!”/ "안녕하세요 세계!"
towardsdatascience.com](/how-i-used-python-code-to-improve-my-korean-2f3ae09a9773)
这是基于 IBM 开发人员技能网络在cognitive class . AI上提供的令人敬畏的“使用 Swift 进行 TensorFlow (S4TF)的人工智能游戏”课程,我强烈建议您查看他们的学习路径。
月度现货价格预测——状态空间法的时间序列分析
SARIMAX 预测 Henry Hub 现货价格
递归特征消除& SARIMAX 状态空间建模方法

作者图片
https://sarit-maitra.medium.com/membership
S ARIMAX 应用于原始序列具有模型参数解释能力等优点。这里的目标是开发一个带有外部变量的季节性自回归综合移动平均(SARIMAX)模型,该模型试图考虑价格影响因素的所有影响,以预测 Henry Hub 的月度现货价格。天然气价格是一个随机变量,遵循具有随机趋势的随机过程。
在像股票市场这样的复杂系统中,预测趋势的能力比点预测更有用。我们可以更有信心和更可靠地预测趋势,而不是这些量的值或水平。
为什么是萨里麦克斯?
这里,我们的时间序列预测模型将使用过去的价格和其他变量,如气候、存储等。来预测天然气未来价值。我们的时间序列受许多外部因素的影响。SARIMAX 能够将季节性和非季节性因素纳入一个乘法模型。因此,当时间序列呈现季节性变化时,通常应用 SARIMAX 模型。
SARIMAX 模型是一个带有外部变量的 SARIMA 模型,称为 SARIMAX (p,D,q) (P,D,Q)S (X),其中 X 是外部变量的向量。

与价格、消费、储备、存储、生产、管道等相关的数据。和气候数据,包括路易斯安那州新奥尔良的冷却/加热度天数、极端最高/最低温度、平均温度和平均最高/最低温度。所有这些数据被组合在一个文件中作为输入数据文件。

我们可以看到,我们的时间序列有一个以上的时间相关变量。每个变量都依赖于它过去的值,并且还依赖于其他变量。我们将在这里使用这种依赖性来预测未来的值。

带有外生回归量的季节性自回归综合移动平均(SARIMAX):
SARIMAX 是一个完全成熟的模型,使用状态空间后端进行估计。SARIMAX 模型由五个迭代步骤组成-
- 模型识别,其中我们识别差分顺序(D)、季节差分顺序(D)、季节长度(S)、非季节自回归顺序(P)、季节自回归顺序(P)、非季节移动平均顺序(Q)和季节移动平均顺序(Q)。
- 参数估计,其中估计来自步骤 1 的已识别模型的参数。
- 模型的适合度是用 l-jung-Box Q 统计量来检验的。如果残差不是正态分布,我们需要进行第 4 步。否则,继续执行步骤 5。
- 纳入外部变量:使用线性回归将相关外部变量纳入 SARIMA 模型。
- 使用样本外验证诊断模型。经验证的模型用于预测未来值。
ACF 和 PACF 图:
plt.figure()
plt.subplot(211)
plot_acf(df['hh_sp'], ax=plt.gca())
plt.subplot(212)
plot_pacf(df['hh_sp'], ax=plt.gca())
plt.xlabel('Lag')
plt.tight_layout()
plt.show()

季节性分解:
df = data.copy()
df.set_index('Date', inplace=True)
print(df.info())
df = df.astype(float)res = sm.tsa.seasonal_decompose(df['hh_sp'],freq=12)
fig = res.plot()
fig.set_figheight(8)
fig.set_figwidth(15)
plt.show()

从趋势图中,我们没有观察到任何明显的趋势,这种趋势遵循某种程度上的随机运动(随机漂移)。

我们在这里可以看到,仅仅求差异无助于数据集的标准化。
df.isnull().sum()
def fill_missing(df):
for row in range(df.shape[0]):
for col in range(df.shape[1]):
if np.isnan(df[row,col]):
df[row,col]= df[row-1, col]
fill_missing(df.values)
np.isnan(df).sum()
fill_missing(df.values)
np.isnan(df).sum()
递归特征消除:
我们使用递归特征消除的特征排序和使用线性回归作为模型的最佳特征数量的交叉验证选择。
特征排序,使得 ranking_[i]对应于第 I 个特征的排序位置。选定的(即,估计最佳的)特征被分配等级 1。
y = df['hh_sp']
X = df.drop(['hh_sp'], axis=1)
names=pd.DataFrame(X.columns)lin_reg = LinearRegression()
mod = RFECV(lin_reg, step=1, cv=20)
mod_fit = mod.fit(X,y)
rankings=pd.DataFrame(mod_fit.ranking_, index=names) #Make it into data frame
rankings.rename(columns ={0: 'Rank'}, inplace=True)
rankings.transpose()

模型中选择了 14 个特征

让我们确定我们的目标;我们将预测未来 5 个月的现货价格。
目标变量:
df['target'] = df['hh_sp'].shift(-5)
df.dropna(inplace=True)
n = df.drop(['target'], axis=1) # storing in a dataframe for future use
dataset = df[['im_pr','ex_pr','prod_mp','prod_liq','prod_dng','imp_tot','imp_pip','ImpLiq', 'mean_max_txt', 'mean_min_txt', 'mean_txt', 'mean_max_lat', 'mean_min_lat','mean_lat', 'target' ]]
dataset.describe()

时间序列交叉验证和分割:
X = np.array(dataset.drop(['target'], 1))
y = np.array(dataset['target'])
tscv = TimeSeriesSplit(max_train_size=None, n_splits=5)
for train_samples, test_samples in tscv.split(X):
#print("TRAIN:", train_samples, "TEST:", test_samples)
X_train, X_test = X[train_samples], X[test_samples]
y_train, y_test = y[train_samples], y[test_samples]
print(X_train.shape, y_train.shape, X_test.shape, y_test.shape)feature_names = ['im_pr','ex_pr','prod_mp','prod_liq','prod_dng','imp_tot','imp_pip','ImpLiq','mean_max_tx', 'mean_min_txt', 'mean_txt', 'mean_max_lat', 'mean_min_lat','mean_lat' ]X_train = pd.DataFrame(data=X_train, columns=feature_names)
X_train.index = dataset[:186].index
y_train = pd.DataFrame(y_train, columns = ['target'])
y_train.index = X_train.index
X_test = pd.DataFrame(data=X_test, columns=feature_names)
X_test.index = dataset[186:].index
y_test = pd.DataFrame(y_test, columns = ['target'])
y_test.index = X_test.index
数据转换:
众所周知,股票价格一般遵循随机游走;第一阶段是为每个变量生成一组变化或差异,因为现货价格可以通过参考所选变量组中的意外变化而不是它们的水平来解释。变量的意外值可以定义为变量的实际值与其期望值之间的差值。从一个时期到下一个时期,变量的整个变化就是意外变化


稳定性检查:

与冲击效应是永久性的非平稳系统相比,平稳系统的优点是,冲击效应将逐渐减弱。
均值回复系列:


是否使用差分部分条件最大似然估计。如果为真,则在估计之前执行差分,这将丢弃前 sD+d 个初始行,但会产生更小的状态空间公式。有关使用此选项时解释结果的重要详细信息,请参见注释部分。如果为 False,则完整的 SARIMAX 模型以状态空间的形式出现,以便所有数据点都可以用于估计。默认值为 False。
确定顺序:
正如我们所知,SARIMAX 将 ARIMA 模型与在季节性水平上执行相同的自动回归、差异和移动平均建模的能力相结合。让我们用最大似然法估计参数。
# Fit auto_arima function
stepwise_fit = auto_arima(y_train_tran, X_train_tran, start_p = 1,
start_q = 1, max_p = 7, max_q = 7, m = 12,
start_P = 0, seasonal = True,d = None,
D = 1, trace = True,
error_action ='ignore',
suppress_warnings = True,
stepwise = True) # To print the summary
stepwise_fit.summary()

模型参数的 p 值表明,季节性和非季节性 AR 和 MA 值在 5%水平上不显著。

模型拟合:
在这里,外生变量是作为平行输入序列给出的,这些序列在与内生序列相同的时间步长上具有观测值。
# fit model
model = sm.tsa.statespace.SARIMAX(
endog = y_train_tran,
exog = X_train_tran, order = (0, 0, 0),
seasonal_order = (2, 1, 1, 12),
enforce_invertibility = False,
enforce_stationarity = False)
model_fit = model.fit(disp = False)
model_fit.summary()

这测试了所有斜率参数共同为零的零假设。附在检验统计量上的 p 值为 0 表明这个零假设应该被拒绝。这里,我们所有的参数都具有 p> 0.05,因此不能拒绝零假设。
使用指示残差不是白噪声的 Ljung-Box Q 统计量来诊断模型。H0 的 Box Ljung 检验的无效假设是,我们的模型不显示缺乏拟合。
诊断图:
诊断图帮助我们直观地确定我们的模型如何拟合数据,以及是否违反了 SARIMAX 模型的任何基本假设。

我们可以看到,潜在的误差分布是具有常数均值和标准差的参数假设的高斯分布。
# Fit a local level model
endog = ytest_tran
local_model = sm.tsa.UnobservedComponents(endog, 'local level')
# Fit the model via maximum likelihood
result = local_model.fit()
# result is an instance of the Unobserved Components Results class
print(result.summary())
# Show a plot of the estimated level and trend component series
fig = result.plot_components()
plt.tight_layout()


测试集数据转换:
X_test_tran = np.log(X_test).diff()
Xtest_tran = X_test_tran.copy()
Xtest_tran = Xtest_tran[~Xtest_tran.isin([np.nan, np.inf, -np.inf]).any(1)]
Xtest_tran.describe()
y_test_log = np.log(y_test['target']).dropna()
y_test_tran = y_test_log.diff().dropna()
ytest_tran = y_test_tran.copy()
#ytest_tran = ytest_tran[~ytest_tran.isin([np.nan, np.inf, -np.inf]).any(1)]
ytest_tran.describe()

模型评估和预测:


逆变换数据:
rebuilt = pred.cumsum().fillna(0)
print('\nCumsum:\n', rebuilt.tail())
rebuilt_further = rebuilt + y_test_log[-1:] # last value of test set
print('\nDe-difference:\n',rebuilt_further.tail())
original = np.exp(rebuilt_further)
print('\nOriginal form:\n',original.tail())

我们已经将目标变量转移到 5 个周期(月);因此,以上应该是未来 5 个月的预测。让我们做一些数据操作来获得未来的日期,如下所示。

可以注意到,按故障设置月频率显示未来所有日期是月末,而不是原始数据集的 15。嗯,这个也可以修复,以同步与原始数据。
可视化:
# Graph
fig, ax = plt.subplots(figsize=(10,5))
ax.set(title='Henry Hub Spot price', xlabel='Date', ylabel='USD / Mbtu')
# Plot data points
y_test.plot(ax=ax, label='Observed')
# Plot predictions
original.plot(ax=ax, style='r--', label='One-step-ahead forecast')
legend = ax.legend(loc='best')

此外,还可以得到预测价格上下水平之间的置信区间,并绘制置信区间图。
从一个简单的模型开始,并测试它是否足够好,这总是一个好主意。应用迭代建模方法,我们可以逐渐增加功能。比较连续模型的结果提供了一种内部验证的形式,因此我们可以捕捉概念、数学和软件错误。通过添加和删除特征,我们可以知道哪些特征对结果的影响最大,哪些特征可以忽略。
关键要点:
时间序列预测的重要假设是当前价格是需求和过去价格的函数。然而,这种关系不仅仅是直接的;需求还取决于气候条件、进出口价格等。通过递归特征消除方法找到一组 14 个变量。发现这些变量对于确定每月的天然气现货价格是重要的。SARIMAX 模型用于价格预测,它在解释异常数据方面有明显的优势。根据预测,我们可以看到 SARIMAX 预测可以平稳地遵循实际数据的模式。
利用贝叶斯定理解决 Monty Hall 问题

介绍
几天前,我决定更新我的概率论知识,所以拉里·瓦色尔曼的书《所有统计学》在这一点上派上了用场。在这本书的前几章中,引人注目的蒙蒂·霍尔问题引起了我的注意。事实上,我之前已经知道这个问题,甚至认为我理解解决方案,但随着时间的推移,不太多的知识片段消失了,蒙蒂霍尔问题的解决方案,不幸的是,没有逃脱这个厄运。幸运的是,上周末的空闲时间和我最近在 Medium 上写博客的热情相结合,我想把它作为我自己的哈利波特传奇的冥想盆,产生了这篇文章。
也就是说,我想用贝叶斯定理详细解释蒙蒂霍尔问题的解决方案。剧透:它更多的是为了理解贝叶斯定理,而不是用简单的术语掌握问题的解决方案。
“所有统计”中的问题陈述
奖品随机放在三扇门中的一扇门之间。你选一扇门。具体来说,你总是选择 1 号门。现在蒙蒂·霍尔选择另外两扇门中的一扇门,打开它,向你展示它是空的。然后他给你机会保留你的门或者换到另一扇没打开的门。应该留下还是换?直觉表明这无关紧要。正确答案是你应该换。证明一下。这将有助于详细说明样本空间和相关事件。这样写

ω₁是奖励所在,ω₂是蒙蒂打开的大门。
此外,我还添加了事件 ω₀ 来描述我们最初选择的门。
贝叶斯定理
让我们先简单地修改一下贝叶斯定理的概念,这是我用来解决问题的主要工具。

在事件 B 发生的情况下,事件 A 的概率等于两个事件的联合概率除以事件 B 的概率。
由于联合概率的对称计算,给定事件 B 的事件 A 的条件概率可以进一步展开:

最后,如果我们还有一个条件事件 C,那么它作为一个条件传播到所有构成概率:

解决办法
现在,我们到底要如何解决这个问题“你应该留下还是离开?”用概率来说?
首先,让我们注意到,如果天魔厅不会打开任何一扇门,这是直截了当的。在我们选择门 1 后,每扇门后的中奖概率是一样的:
- p(1 号门后的奖品|我们选择了 1 号门)=1/3
- p(门 2 后面的奖品|我们选了门 1)=1/3
- p(3 号门后的奖品|我们选择了 1 号门)=1/3
请注意,我使用条件概率来表明我们之前选择了门 1,尽管这一事实无论如何不会改变结果。尽管如此,我发现保留这个条件很重要,因为它有助于以后理解蒙蒂打开另一扇门时概率测度的赋值。
但我们知道蒙蒂·霍尔打开了另一扇门。
它改变了什么?显然,在条件概率中“|”符号的右边,应该有一些更早发生的事件。在我看来,在我们屈服于宇宙的力量之前有两个事件:1)我们选择了一扇门,2)蒙蒂·霍尔打开了另一扇门。“|”符号的左边应该是一个事件,让我们感兴趣。特别是,我们对奖品在我们选择的门后面的事件和奖品在另一扇门后面的事件非常感兴趣,那扇门是关着的。因此,在我的逻辑中,所有相关的条件概率应该是这样的:
- p(1 号门背后的奖品|我们选了 1 号门,蒙蒂厅开了 2 号门)=?
- p(2 号门背后的奖品|我们选了 1 号门,蒙蒂厅开了 2 号门) = 0
- p(3 号门背后的奖品|我们选了 1 号门,蒙蒂厅开了 2 号门)=?
- p(1 号门背后的奖品|我们选了 1 号门,蒙蒂厅开了 3 号门)=?
- p(2 号门背后的奖品|我们选了 1 号门,蒙蒂厅开了 3 号门)=?
- p(门后奖 3 |我们选了门 1,天魔堂开了门 3) = 0
然而,正如我提到的,根据问题陈述,一些概率是已知的,因为有一个约束,蒙蒂大厅只打开空的门。因此,我将概率 2 归零。和 6。,奖品是在同一扇门后,蒙蒂大厅打开。
而且对于我们来说,选择了 1 号门之后,2 号门和 3 号门没有太大的区别,那些天魔会开哪一个。最后,我们只想知道,在同样的情况下,当初选择的成功是否和另一扇门的选择一样。所以在我们选了 1 号门,蒙提·霍尔打开 2 号门后,奖品在 1 号门后的概率,和我们选了 1 号门,蒙提·霍尔打开 3 号门后,奖品在 1 号门后的概率完全一样。也就是概率 1。等于概率 4..同理,概率 3。等于概率 5..所以我假设蒙蒂·霍尔打开了 2 号门,那么我们只剩下两种可能性:
- p(1 号门背后的奖品|我们选了 1 号门,蒙蒂厅开了 2 号门)=?
- p(3 号门背后的奖品|我们选了 1 号门,蒙蒂厅开了 2 号门)=?
用数学术语来说,它会是这样的:

让我们依次讨论每一种可能性。
1.看到门 2 是空的后,奖品在我们的门(门 1)后面的概率
正如我前面提到的,我决定引入我们选择的门 1 的事件,尽管它随后作为一个条件传播到所有考虑的概率。然而,我认为这样我们可以更全面地了解以前的事件。
因此,根据贝叶斯定理,概率展开如下:

在哪里
- 蒙蒂·霍尔打开 2 号门的概率等于 1/2,因为我们选了 1 号门,而奖品也在 1 号门后面。这是因为对他来说,他是打开 2 号门还是 3 号门并不重要,因为我们已经挡住了 1 号门,奖品也在 1 号门后面。

- 假设我们选了 1 号门,蒙蒂·霍尔打开 2 号门的概率等于 1/2。这有点类似于前面的概率,但不知道奖金在哪里。我们封锁了 1 号门,因此他有 50%的几率打开 2 号门(或 3 号门)。

- 假设我们也选了门 1,奖品在门 1 后面的概率等于 1/3。这是合乎逻辑的,因为我们对门的选择不会影响门后(或任何门)奖品的外观。这是一个独立事件。

现在让我们来计算这个概率:

2.看到门 2 是空的后,奖品在门 3 后面的概率
该概率的公式如下所示:

在哪里
- 蒙蒂·霍尔打开 2 号门的概率,假设我们选了 1 号门,奖品在 3 号门后面,注意,等于 1!这是因为他没有别的选择:我们堵住了 1 号门,他不能用奖品开门。

- 假设我们选了 1 号门,蒙蒂·霍尔打开 2 号门的概率等于 1/2。这有点类似于前面的概率,但不知道奖金在哪里。不知道奖品在哪里至关重要——对他来说,2 号门和 3 号门没有区别。

- 假设我们选了 1 号门,奖品在 3 号门后面的概率等于 1/3。如前所述,我们选择 1 号门是独立事件,因为奖品在 3 号门后面。

让我们最后计算一下最终概率是多少:

结论
我们看到,确实更好地改变到门 3,因为在这种情况下,我们利用了蒙蒂霍尔的知识,实际上奖金在哪里。
其实我不了解你,但是即使解决了问题,还是显得有点神秘。对我来说,改变门的选择的机会看起来就像一个将你带到更好的平行宇宙的入口,在那里你有更高的获胜机会。
蒙蒂·霍尔的问题会让你发疯的
用数学、统计学和蒙特卡罗方法解决著名的脑筋急转弯

门由索菲·戴尔创作,山羊由彼得·诺伊曼创作,汽车由Jp·瓦列里在 Unsplash 上创作,并由我剪辑
蒙蒂霍尔问题是统计学中一个著名的概率难题。它是以电视游戏节目《让我们做笔交易》的主持人蒙蒂的名字命名的。脑筋急转弯松散地复制了游戏节目的概念,它是这样的:
有三扇门。你将不得不选择一扇门,你将赢得门后的一切。有一扇门有一辆车。剩下的每扇门都有一只山羊。首先,你被要求选择一扇门。接下来,蒙蒂知道每扇门后有什么,他打开了两扇门中的一扇门,展示了一只山羊。最后,你有机会要么“留”在你原来的选择,要么“切换”到剩下的门。赢车的最佳策略是什么?
我第一次遇到这个问题是在几年前的统计课上。这太难了,我花了一整天的时间来思考这个问题,并且仍然坚持使用两种策略中的1/2。我的解释是:有3门,1门有车。如果主人打开有山羊的一扇门,剩下的两扇门中必须有一扇门有汽车。所以不管“换”还是“留”,概率应该是1/2!
我错了。这个问题的棘手之处在于,蒙蒂知道这些门后面是什么,所以他的决定对结果有影响。花一分钟时间让自己想想这是不是真的。
1)直观回答
假设这三门是:1、2、3、T17 既然probability = Event/Sample空间我们只需要找到这些变量,代入方程。
这里的样本空间是我们选择车门和汽车实际位置的不同方式的总和。下表说明了 6 种可能性:

注意,这一桌,蒙蒂已经淘汰了带山羊的门。
如你所见,“停留”策略的获胜概率为1/3,而“转换”策略的获胜概率为2/3。

蒂姆·埃里克头脑爆炸
2)贝叶斯公式法
这是热爱贝叶斯的人的另一种做法!
让P(A|B)表示事件A的概率,假设事件B已经发生

贝叶斯公式
如果我们知道如何将问题转化为事件,那么一切都变得简单明了。让:
- 第一选择是以车为门的事件。
- b 是蒙蒂消灭一个有山羊的门的事件。因此,我们正在寻找“留下来”的策略。
让我们写下我们知道的事情:
- 因为蒙蒂总是选择正确的门而不管任何给定的条件
P(A)=1/3,有一个1/3打开车门的几率,什么都不知道。
为了理解如何找到P(B),让我们看一下图表:

文氏图

只有2种可能的策略,要么留下,要么换。于是,“切换”概率就是停留的补数,也就是1–1/3=2/3。
如果蒙蒂不知道哪扇门有山羊。这相当于P(B|A)=1 and P(B|¬A)=1/2。将它们插回到等式中,它返回P(B|A)=1/2。这是我感到困惑的部分。
3)更简单的答案
用“留”的策略,一开始选择有车的门才能赢。既然有3门,那么有车选门的概率就是1/3。所以“切换”策略是2/3。这为我们提供了一个对超过3门天魔堂问题的一般观察。
4)如果呢?
如果 Monty Hall 决定在最大数量k为n-2的 n 个门中揭示 k 个门,会不会对策略有丝毫影响?

门乘苏菲·戴尔再乘上我
此例中的“切换”是指不保持原选择
例 A: k=n-2
此例与原问题类似。请注意,您不能切换到您选择的门。因此,“停留”胜率始终是1/n,在这种情况下,转换胜率是(n-1)/n
案例 B: k<n-2
一开始选门带车的概率还是1/n。从而“呆住”= 1/n。
在“转换”策略中,你的最终选择不可能是你的首选。因此,你不会想选择第一个有车的门。不选的概率是(n-1)/n。k-th开关也是当你选择时,还有n-k扇门,但你必须排除你的第一扇门。这样,最后一步中撬开被撬门的概率就是1/(n-k-1)。我们共同拥有:

它表明“转换”策略总是优于“停留”策略。
5)模拟
让我们在 python 上运行它,看看我们公式是否有效。设n是门的数量,而r是蒙蒂想要显示的门的数量

蒙特 卡罗模拟
图表显示,随着迭代次数的增加,统计结果越来越接近我们的实际答案。
所以,你知道下次再玩类似这样的游戏应该怎么做。
关于代码的更多信息,请访问我的 GitHub 库。
蒙蒂·霍尔——实证检验

游戏结果—作者:阿列克萨·米哈伊洛维奇
我记得不久前的老蒙蒂霍尔挑战赛。我试着向我的一个朋友解释,我希望说服他接受这个想法。但这被证明是一项相当具有挑战性的任务..
天魔堂是一个概率游戏,第一次尝试很难完全掌握。这个问题如此简单,却又如此反直觉,以至于我不得不花几分钟来测试它。对于那些不知道这个游戏的人来说,Monty Hall 是一个古老的谜题是这样的:
假设你在参加一个游戏节目,你有三扇门可供选择:一扇门后是一辆汽车;另外两扇门后面是山羊。你选了一扇门,比如 1 号门,主人知道门后有什么,他打开了另一扇门,比如 2 号门,里面有一只山羊。然后他对你说,“你想选 3 号门吗?”改变你的选择对你有利吗?
这里的假设是,你并不想成为一名牧羊人(我并不反对这个光荣的职业),而是希望赢得一辆汽车。
第一次听到这个谜题时,我像大多数人一样回答:“没关系,应该是一样的”。我的推理如下——最初我们有三个门可以选择,但后来主持人展示了一只山羊,并从游戏中淘汰了一个门。我们现在有两扇门,其中一扇门后是一辆车。我认为现在赢得车的机会是 50-50,如果我选择转换或不转换都无所谓。这是不对的。最初赢车的几率是 33%。如果我们不切换(在主人打开藏着山羊的一扇门后),我们基本上忽略了新的信息,我们的获胜机会保持不变,即使游戏中只剩下两扇门。
如果我们同意开关门是一件有利可图的事情,那么现在的胜算有多高?我的第一个想法是——如果我不换车,我赢得车的机会保持在 33%,因此我应该换车,因为现在游戏中还有两扇门,赢得车的机会是 50%。但在现实中,切换将我们的获胜概率提高到惊人的 66%!
让我解释一下为什么——我们最初选择的门,比如 1 号门,有 33%的获胜概率。这意味着有 66%的可能性汽车在剩下的两扇门(2 号门和 3 号门)的后面。例如,如果主人打开 2 号门,发现门后有一只山羊,那么同一扇门藏着一辆汽车的概率现在为零。由于概率必须加到 1,汽车藏在 3 号门后面的概率现在从 33%跳到 66%!从 1 号门转换到 3 号门,我们获胜的机会加倍!
我希望这能澄清一些问题,但是我知道这种玩弄概率的行为可能看起来不太直观。因此,我决定做一个小模拟,根据经验计算汽车和山羊,并计算获胜的概率。我想直接测试 Monty Hall 问题,看看哪种策略(切换或不切换)更好。
显然,我没有打电话给我的山羊和汽车经销商进行这个实验,而是用了一些循环。您可以在这个资源库中找到带有模拟的 Jupyter 笔记本。
所以,我们开始吧!
策略 1——无切换
我玩了 100K 次这个游戏,我没有在任何游戏中开关门。事实上,总的获胜概率是 33%,这意味着我总共赢了大约 33K 次。我打印了前 60 场比赛的结果,我们可以很容易地看到山羊图像是如何主导结果模式的。

策略 1-无切换-模拟结果
策略二——切换
我又玩了 10 万次这个游戏,但是现在我在每场游戏中都换了门。模拟产生 66%的胜率!这意味着我赢了 66K 次左右的车!我们甚至可以在下面看到汽车图像是如何主导结果的。

策略 2-转换-模拟结果
我想这证明了。交换会让我们的胜算翻倍。我猜你已经准备说:“这确实非常有趣,但是我能在哪里应用它呢?”
事实证明,在现实生活中很难遇到纯粹的蒙蒂霍尔式的场景。从技术上讲,这需要发生:
- 我们正在几个不同的选择中做出选择,这些选择并不相同,但从外面看起来是一样的
- 在我们做出选择后,有人神奇地告诉我们,剩下的选择中有一个确实不好,而且肯定不是我们想要的
- 然后,我们应该放弃我们第一次做出的选择,也放弃不好的选择,并从剩余的选择中重新选择
我在努力思考这个问题,但是我真的想象不出任何完全尊重这些条件的情况。我确实在一些帖子中遇到过有人问同样的问题,比如这里的和这里的。如果你真的能想到一个纯粹的现实生活中的蒙蒂大厅的情况,我会很高兴听到它,如果是这样,请给我写一个评论!
我希望你喜欢这个快速阅读,不要忘记切换!
纪念性偏见:马德里为每一个女人树立了四个男人的雕像
自 1600 年以来,这座城市已经竖立了 298 座雕像,其中 49 座是献给女性的。其中,只有一小部分描绘了女性历史人物。
在马德里的主要公共广场太阳门广场,游客们会遇到西班牙首都最受关注的照片之一:熊和草莓树的雕像。这幅关于一只熊在树上吃东西的坦率而受欢迎的描绘代表了这座城市的盾形纹章,也是对新来者的欢迎。

马德里最著名的雕像“熊和草莓树”
尽管马德里在过去几十年里对基础设施进行了现代化改造,但这座城市仍然保留了其历史街区和街道的风貌。证明这一点的是散布在这个大都市的近 300 座雕像。描绘前国王的好战姿态,基督教符号,希腊和罗马的数字,这些雕像大多有一个共同点:他们的性别。
根据马德里市议会发布的一个数据库,虽然这些纪念碑中有 77 %代表男性历史人物,但只有 16 %多一点显示女性历史人物。这意味着这座城市为每一座女性雕像树立了四座男性雕像。然而,这一比例也包括圣母玛利亚、希腊女神和没有名字或姓氏的女性形象。其余的雕像描绘想法和概念(3 %),或动物(3 %)。

*数据来源:马德里市议会| Medium 不支持 Tableau 嵌入,如果要查图:https://tabsoft.co/39VXsfk
“这些数字很可怕,但它们只不过是我们生活的社会的反映,”艺术集体 Mujeres Mirando Mujeres (MMM)的负责人米拉·阿巴迪亚说。
“我们需要重写所有领域的历史,女性历史人物通过艺术的出现是重塑女性角色和她们的作品所代表的社会价值的另一种方式”,她解释道。
结合艺术和女权主义的"妇女看妇女"的工作始于 2015 年,面向专门从事艺术的博客作者和记者,以及策展人、艺术经理、画廊老板和博物馆馆长,就艺术领域的性别问题提出倡议。“我们意识到新技术给我们带来的机遇,于是 MMM 应运而生。我们不能也不可能对正在发生的所有运动视而不见”,Abadía 说。

Saint María de la Cabeza (1722 年),第一座竖立在马德里的女性雕像|摄影:Edurne Morillo
但是她不是唯一一个关注西班牙纪念碑偏见的人。历史学家玛丽亚·伊莎贝尔·加森(María Isabel Gascón)是一个女性历史学家团体的主席,她谴责巴塞罗纳的雕像中缺少女性的存在。她的组织致力于拯救被历史掩盖的西班牙重要女性的传记。
在马德里,如果我们看看通过雕像纪念的是什么样的人,这一点尤其重要。就女性而言,只有三分之一代表真实的历史人物,而其余的要么是宗教代表(28 %),要么是神话生物(12 %),要么是作为职业或抽象人物的女性角色(24 %)。

哥伦布广场附近的女性雕像。背景是 Jaume Plensa 的《Julia》|摄影:Edurne Morillo
就男性而言,7 %的雕像涉及宗教表现,而神话生物和概念分别占 2 %和 4 %。男性最常见的类别是国王(23 %)、作家(13 %)和军事人物(9 %)。其他受欢迎的类别有政治家、医生、画家、植物学家和科学家。
对阿巴迪亚来说,我们“有义务重写历史,给那些被遗忘的伟大女性发言权和一席之地,她们一直被排除在创造性天才模式之外”。她认为,有许多女性人物可以在西班牙的街头占据一席之地,比如费德里卡·蒙塞尼·马涅(Federica Montseny Ma),一位政治家,也是 20 世纪 30 年代西班牙第二共和国时期的一名部长。她是西班牙第一位担任部长职务的女性,也是西欧第一批担任部长职务的女性之一。
其他候选人可能是第一位正式学医的西班牙女性多洛丝·阿留申·里埃拉,或者是作家、艺术评论家、政治家、西班牙主要女权主义者玛格丽塔·内尔肯·曼斯伯格。“我们可以持续几个小时”,艺术专家和策展人争辩道。
年表也没有善待女性历史人物。在第一个男人的雕像竖立起来之后,女人们要等一百多年才能得到一个雕像。一座西班牙腓力三世的雕像于 1616 年竖立起来,但直到 1722 年才建成第一座女性雕像,献给圣玛丽亚·德拉·卡韦萨。

*数据来源:马德里市议会| Medium 不支持 Tableau 嵌入,如果要查图:https://tabsoft.co/2tHjmm3
“大多数艺术手册都把创作者排除在外,即使有宫廷肖像画家、雕塑家或宗教画家。十九世纪的资产阶级习俗将女性置于家庭中。当第一批欧洲博物馆建立时,这种歧视加深了”。
走在马德里的街道上意味着会遇到戈雅和委拉斯开兹的身影,或者像哈辛托·贝纳本特、文森特·阿莱克桑德雷和卡米洛·何塞·塞拉这样的诺贝尔文学奖得主。然而,游客不会发现任何关于最近去世的科学家玛格丽塔·萨拉斯或弗拉门戈舞蹈家卡门·阿马亚的参考资料。
马德里最著名的 10 座雕像的历史
“有必要回顾我们的过去,因为历史不是绝对的真理,它是流传下来的故事,尽管不一定是真实的”,阿巴迪亚总结道。“有许多被历史吞噬的被遗忘和沉默的女性”。
如果你想仔细看看所有的马德里雕像,这张地图显示了它们,按性别分类。
语气和情态与对话情感
言语特征与对话情感的空间性
在本文中,我们将看到动词功能范畴如何在客户对话文本中使用,以及这些范畴如何有助于语义,尤其是文本情感。
句子中的动词短语有时可以承载巨大的语义,有时即使看不到其余的上下文单词,也仅通过其自身暗示情感,因此作为重要特征有助于情感分析模型。例如,从以下客户评论中划掉所有未包含在任何 VP 中的词语:
The product isn't working properly.
I didn't like this product.
I'm not satisfied with the product quality at all.
为了赋予句子意义,许多语言喜欢用动词来表示不同的屈折变化,如时态和人称。此外,大多数时候,我们想表达我们对动词提出的动作是如何发生的感觉和看法:我们确定吗,我们亲眼看到这个动作了吗,我们认为这是可能的还是不可能的?
这更多的是一种语义能力,因此一个人通常需要多于动词的屈折,多于语法结构。动词的功能是一个宽泛的话题,但我会在统计部分之前解释一些基本概念。如果你有这个背景,你可以跳到下一节。
Tense是通过动词屈折在语法上实现时间。英语有两种时态:past和present。Future不是没有屈折标记的时态,而是一个time。Future time由will或tomorrow、8 o'clock或next week/month等副词构成。如你所见,tense是一个语法概念,而time是一个语义概念。
另一个概念是aspect,这是一个语法范畴,反映了动词所给予的动作相对于时间的发生。英语有两个方面:
action complete: perfective has moved, had moved
action in progress: progressive is moving, was moving
人们可以把时态和体概括如下:

动词发生的时间:时态和体
正如我们在高中所学的,英语中的被动语态和主动语态。被动语态有进一步的语义子类,但在这篇文章中,我们将停留在高中水平的语法😉
Mood是表示动词表达事实(指示语气)还是条件性(虚拟语气)的语法范畴。一些例子是:
Sun rises at 6 o'clock here. indicative
It is important the manager be informed of the changes subjunctive
语气是语法上的,与两个语义概念相关联:情态和illocution。Illocution一个句子可以被认为是sentence type:
Go there! imperative
Do you want to go there? interrogative
God save the queen! optative
I will see it. declarative
Modality是一个语义概念,与说话者对事件的可信性、强制性、合意性或现实性的看法和信念有关。英语中的情态可以通过情态动词(will/would、can/could、may/might、shall/should、must)、情态副词(maybe、 perhaps、possibly、probably)、一些从句包括(wish, it’s time,possible, probable, chance, possibility)、一些情态名词(decree、demand、necessity、requirement、request、 ) 或一些情态形容词(advisable、crucial、 imperative、likely、probable来实现
I would love you if things were different irrealis
You may go permission
I may come with you too possibility
I might come with you too possibility
I must go obligation
He must be earning good money necessity
I can ride a bike ability
I can come with you too possibility
It is possible that we might see big changes around us. possibility
It might be the truth doubt
I'm sure they'll come confidence
Lights are on, so he must be in the office evidentiality
从现在开始,我们将看到客户用来与对话代理交互的不同语言特征,以及这些用法如何导致不同的语义。
让我们从我们的 Chris 开始,我们的汽车语音助手,看看汽车对话人工智能中的一些典型用户话语。
克里斯数据集包括许多祈使句:
navigate
navigate home
start navigation
stop navi
play Britney Spears
play music
send a message
read my messages
有时话语只包含一个名词短语:
music
die navigation
new messages
粒子始终是任何语音助手对话数据集的一部分:
yes
no
please
yes please
当然,有些咒骂和侮辱也包括在内,有些是以讽刺的形式:
you suck
you are dumb
you are miserable
a**chloch
you are so intelligent (!)
Chris 是一名司机助理,所以说话简洁明了是很正常的。这不是因为粗鲁或粗鲁,只是因为一个人开车时需要说得简短。对比下面两句话,显然,如果你在开车,第一句更容易:
Hey Chris, drive me home
Hey Chris, shall we drive home together?
祈使句在 SLU 很常见,绝对没有粗鲁的意思,也与说话人的情绪无关。这里真的没什么有趣的,第一组话语有祈使语气,主动语态和无标记体的动词。没有情态动词,没有情态表达或过去式。在这种情况下,语音助手的情感最好从语音信号中计算出来。

克里斯既有礼貌又聪明
到目前为止,口语可能不是很令人兴奋,那么我们可以转向书面语,书面语允许更长的句子,因此有更多的动词形式😄我使用女性电子商务服装评论数据集来探索语言特征的使用。我将使用可爱的空间匹配器(绝对不仅仅因为我是一个贡献者😄)这一节。该数据集包括用户对电子商务网站购物的评论和评级。
在开始之前,让我们记住与动词相关的词性标记,因为我们将主要复习动词。英语动词有五种形式:基底(VB 、VBP)、-s ( VBZ)、-ing ( VBG)、过去(VBD)、过去分词(VBN)。同样,未来时间没有标记。情态动词can、could、might、may、will、would承认标记MD。
声音
先说Voice,被动语态的匹配模式有is/was adverb* past-participle-verb和have/has/had been adverb* past-participle-verb。对应的Matcher图案可以是:
{"TEXT": {"REGEX": "(is|was)"}}, {"POS": "ADV", "OP": "*"}, {"TAG": "VBN"}
and
{"LEMMA": "have"}, {"TEXT":"been"}, {"POS": "ADV", "OP": "*"}, {"TAG": "VBN"}
第一种模式是is/was,后跟任意数量的副词,然后是过去分词动词。 POS用于 UD POS 标签,而TAG用于扩展 POS。第二种模式类似:have、has、had用lemma : have表示。
我将首先导入空间,加载英语模型,然后将这两条规则添加到Matcher对象中:
import spacy
from spacy.matcher import Matchernlp = spacy.load("en_core_web_sm")
matcher = Matcher(nlp.vocab)
pass1 = [{"TEXT": {"REGEX": "(is|was)"}}, {"POS": "ADV", "OP": "*"}, {"TAG": "VBN"}pass2 = [{"LEMMA": "have"}, {"TEXT":"been"}, {"POS": "ADV", "OP": "*"}, {"TAG": "VBN"}]matcher.add("pass1", None, pass1)
matcher.add("pass2", None, pass2)
然后我对数据集运行Matcher,这里有一些被动语态的例子,来自正面和负面评论:
one wash and this was ruined!washed them according to directions and they were ruined.this could not have been returned fasteri kept it anyway because the xs has been sold out, and got it taken in a bit.it is simply stunning and indeed appears to have been designed by an artist.would buy this again in several different colors if they were offeredif these were presented in other colors, i would buy those as well
评论中被动语态动词的数量如何与评论评级相关联?首先,我们来看看点评的评分分布:

查看评级分布。显然许多顾客都很满意
接下来,我们看到评论中被动语态动词计数的分布。许多评论根本不包括被动语态,有些评论只有一个被动动词,很少有评论有一个以上的被动结构。

被动语态动词的数量与评论评分相关吗?从下面来看,确实没有(只看热图就足够了,它指出了根本没有相关性)。



热图、jointplot 和 violin plot 用于评论评级和被动动词计数
毫不奇怪,看看语料库中的句子,被动语态可以是“由著名设计师设计的”或“它们被退回来了”。当提到衣服时,它是如何设计、裁剪和完成的,可以是正面的也可以是负面的;它被归还,毁坏,呈现可以是消极的也可以是积极的。
时态和体
让我们看看动词时态和体的时间如何与评论评分相关联。记住,过去时态和现在时态很容易计算(通过观察动词的词形变化),而将来时不是真正的时态,因为没有词形变化。我们将通过计算will、going to和时间副词来计算未来时间出现的次数。
这次我们可以用Matcher句型再做一遍时态表:

时态和体,这次是用空间匹配模式
我还会数present perfect progressive tense(“一直在做”)和past perfect progressive tense(“一直在做”),它们会对现在时态和过去时态的完成体和进行体计数做出贡献。
以下是评论中使用的时态和体式的一些例子:
I love, love, love this jumpsuit. it's fun, flirty, and fabulous! every time i wear it, i get nothing but great compliments!fits nicely! i'm 5'4, 130lb and pregnant so i bough t medium to grow into.I have been waiting for this sweater coat to ship for weeks and i was so excited for it to arrive. this coat is not true to size and made me look short and squat.I have been searching for the perfect denim jacket and this it!I had been eyeing this coat for a few weeks after it appeared in the email, and i finally decided to purchase it to treat myself.
未来的时间呢?既然没有形态标记,我们可以从will、going to、plan to、in 2/5/10 days、next week/month/summer、the day after tomorrow …
对应的Matcher图案可以是:
future_modal = [{"TEXT": "will", "TAG": "MD"}]future_adv = [{"TEXT": {"REGEX": "(plan(ning) to|(am|is|are) going to)"}}time_expr1 = [{"TEXT": {"REGEX": "((next|oncoming)(week|month|year|summer|winter|autumn|fall|)|the day after tomorrow)"}}]time_expr2 = [{"TEXT": "in"}, {"LIKE_NUM": True}, {"TEXT": {"REGEX":"(day|week|month|year)s"}}]
语料库中的例子有:
sadly will be returning, but i'm sure i will find something to exchange it for!I love this shirt because when i first saw it, i wasn't sure if it was a shirt or dress. since it is see-through if you wear it like a dress you will need a slip or wear it with leggings.Just ordered this in a small for me (5'6", 135, size 4) and medium for my mom (5'3", 130, size 8) and it is gorgeous - beautifully draped, all the weight/warmth i'll need for houston fall and winter, looks polished snapped or unsnapped. age-appropriate for both my mom (60's) and myself (30's). will look amazing with skinny jeans or leggings.This will be perfect for the mild fall weather in texasThere's no extra buttons to replace the old one with and i'm worried more of the coat is going to fall apart.This is going to be my go to all season.i plan to wear it out to dinner for my birthday and to a house party on new years day....i am planning to exchange this and hoping it doesn't happen againit is nice addition to my wardrobe and i am planning to wear it to the multiple occasionthis is one of those rare dresses that looks good on me now and will still look good on me in 6 months when i've got a huge belly.
根据下面的统计,顾客经常使用过去式。现在时也广泛使用,而未来时间在每次复习中使用一到两次。





以下是相应的直方图:





对应直方图
根据下面的热图,现在时和将来时的用法与评分并不真正相关;负面评价和正面评价都包括这两个时态动词。但是,过去式看起来有点负相关;更多地使用过去式意味着更差的评分。完美和进步方面看起来也不太好,它们也有点负相关。

时态和体的热图
下面的脊线图显示了一些信息,更好的评论倾向于使用 0 的过去式;不快乐的顾客倾向于更流畅地使用过去式。一个可能的解释是顾客抱怨很多:“包裹来晚了”、“腰围不合适”、“我拉不上拉链”、“我不喜欢它”;而快乐的顾客则展望未来😄当我们更快乐的时候,我们都会展望未来,不是吗?😉

语气和情态
正如我们所见,情态是一个语义概念,同一个情态可以给出不同的情态。让我们看一个例子中引入的不同语义could:
i love that i could dress it up for a party, or down for work.
possibility
the straps are very pretty and it could easily be nightwear too.
possibilitythis is a light weight bra, could be a little more supportive. pretty color, with nice lines. irrealisI bought this and like other reviews, agree that the quality probably could be better, but i still love it enough to keep.
irrealis
got it on sale, but it still could've been cheaper.
irrealis
Bought a large, could barely pull up over my butt.
ability
could与消极和积极情绪都相关。可能性情绪看起来是积极的,而非现实情绪看起来既积极又消极。
那么couldn't呢?这完全是另外一个故事,下面的例子显示了couldn't为消极和积极情绪提供了多少语义丰富性,尽管几乎所有的例子都只包括一种类型的情态:
so small in fact that i could not zip it up! abilityi was so excited to get his dress for my wedding shower and then i couldn't wear it :( abilityi really tried because the fabric is wonderful and the shirt is light and breezy for summer, i just couldn't make it work abilityi simply couldn't resist! i could not be more pleased and regret not having bought this item earlier, since i would have enjoyed wearing it during the holidays.i could not be happier with the purchase and the keyhole in the back is a beautiful detail.
emphasizing opinioni also thought that this was very heavy for a maxi dress and could not imagine wearing it in 80 degree weather. ability i think it's maybe a little too long (or short, i can't figure it out) but i couldn't pass up this skirt because the pattern is so pretty. ability i just wish it was more of a blue denim blue but nonetheless, i could not walk away from the store without this. ability
would和wouldn't可能感觉不同,但从统计上看,它们与could和couldn't相似:

模式和审查评级相关矩阵
不真实既发生在负面评价中,也发生在正面评价中。考虑:
maybe if i weren't as small chested this wouldn't be an issue for me. i definitely recommend this tee.the neckline wouldn't even stay up on my upper body, it was that loose.
那么毫不奇怪的是,would/wouldn’t/could/couldn’t的出现并没有透露太多的评论情绪。
相应的Matcher模式将是
[{"TEXT": {"REGEX": "(would|could|can|might|may)"}, "TAG": "MD"}]
MD是情态动词标签,我们排除will。
亲爱的读者们,本文到此结束。我们和 spaCy 玩得很开心,不是吗(像往常一样)?😃我们每天都要处理大量的数据,但有时我们会忘记阅读语料库中的内容。语言不仅仅是一堆凑在一起的单词;它有许多方面,包括统计和语言学。今天我们两个都喜欢。下次我们将继续语音助手的统计讨论。直到下一次,你总是可以在https://chris.com拜访克里斯。你也可以随时登陆 https://duygua.github.io 来看我。同时保持快乐、安全和和谐!
参考文献
帕尔默,F. (2001),语气和情态(第二版。,剑桥语言学教科书)。剑桥:剑桥大学出版社。doi:10.1017/CBO 978113916716717
更多微软数据科学面试问题和答案
微软面试中一些数据科学问题的另一个演练

几周前,我发表了一篇文章叫做微软数据科学面试问答!它涵盖了我的朋友在微软一个数据科学家职位的面试中提供给我的十八个问题中的前九个。本周,我将尽我所能回答剩下的九个问题。开始了。****
面试问题

来自微软数据科学面试的面试问题
问:处理贝叶斯问题的简单概率问题
答:我的猜测是,他们可能会问一些关于贝叶斯定理的一般性问题,并要求在给定其他变量的值的情况下,找出一个变量的值。我将通过一个例子简单介绍一下什么是贝叶斯定理。

P(A|B) 是假设事件 B 发生时事件 A 发生的概率
P(B|A) 是假设事件 A 发生时事件 B 发生的概率
P(A) 是事件 A 发生的概率 P(B) 是事件 B 发生的概率
我将用一个例子来说明这一点:
- P(A)等于患者患有肝病的概率。过去有 10%的病人患有肝病。因此 P(A) = 0.1
- P(B)等于病人是酗酒者的概率。过去看过的病人中有 5%是酗酒者。因此 P(B) = 0.05
- 你也知道 7%被诊断为肝病的患者也是酗酒者。因此 P(B|A) = 0.07
- 利用贝叶斯定理,可以得出:
P(A | B)=(0.07 * 0.1)/0.05 = 0.14 因此,如果患者是一个酗酒者,他们有 14%的几率患有肝病。
如果贝叶斯定理仍然让你困惑,3Blue1Brown 以一种视觉和直观的方式做出了令人难以置信的解释——见这里。
问:在应用机器学习算法之前,数据争论和数据清洗的一些步骤是什么?
答:在数据争论和数据清理时,可以采取许多步骤。下面列出了一些最常见的步骤:
- 数据剖析:几乎每个人都是从了解自己的数据集开始的。更具体地说,您可以使用。形状和对数值变量的描述。描述()。
- 数据可视化:有时,用直方图、箱线图和散点图来可视化数据很有用,这样可以更好地理解变量之间的关系,也可以识别潜在的异常值。
- 语法错误:这包括确保没有空格,确保字母大小写一致,检查错别字。您可以使用。unique()或使用条形图。
- 标准化或规范化:根据您正在处理的数据集和您决定使用的机器学习方法,标准化或规范化您的数据可能会很有用,这样不同变量的不同尺度就不会对您的模型性能产生负面影响。
- 处理空值:有多种方法可以处理空值,包括完全删除具有空值的行,用均值/中值/众数替换空值,用新的类别(例如未知)替换空值,预测值,或者使用可以处理空值的机器学习模型。此处阅读更多。******
- ****其他还有:去除无关数据,去除重复,类型转换。
问:如何处理不平衡的二元分类?
答:有许多方法可以处理不平衡的二进制分类(假设您想要识别少数类):
- 首先,您需要重新考虑您用来评估您的模型的指标。你的模型的准确性可能不是最好的衡量标准,因为我将用一个例子来解释为什么。假设 99 次银行取款不是欺诈,1 次取款是欺诈。如果你的模型只是简单地将每个实例归类为“非欺诈”,那么它的准确率将达到 99%!因此,您可能需要考虑使用精度和召回率等指标。****
- 另一个改善不平衡二进制分类的方法是通过增加小众类的误分类成本。通过增加这样的惩罚,该模型应该更准确地对少数民族进行分类。****
- 最后,你可以通过过采样少数类或者欠采样多数类来改善类的平衡。你可以在这里阅读更多关于它的信息。****
问:箱形图和直方图有什么区别?

箱线图与直方图
答:虽然箱线图和直方图是用于显示数据分布的可视化工具,但它们传达信息的方式不同。
直方图是显示数值变量值的频率的条形图,用于估计给定变量的概率分布。它允许您快速了解分布的形状、变化和潜在的异常值。
箱线图表达了数据分布的不同方面。虽然您无法通过箱线图看到分布的形状,但您可以收集其他信息,如四分位数、范围和异常值。当您想要同时比较多个图表时,箱线图尤其有用,因为它们比直方图占用更少的空间。

如何阅读箱线图
问:如何着手解决 L2 正则化回归问题?
A: 不幸的是,我对岭回归的了解还不足以知道 L2 回归问题是什么,但是如果你知道,请评论你的答案,我会在这里大声喊出来!
问:概率基础
答:为此,我打算在这里看一下八大概率法则以及四种不同的计数方法(详见 这里 )。****
概率的八大法则
- 规则#1:对于任何事件 A,0≤P(A)≤1;换句话说,一个事件发生的概率范围可以从 0 到 1。******
- 规则 2:所有可能结果的概率总和总是等于 1。
- 规则#3: P(非 A)= 1—P(A);这个规律解释了一个事件的概率和它的补事件之间的关系。补充事件是指包含 a 中没有的所有可能结果的事件**
- 规则#4:如果 A 和 B 是不相交事件(互斥),那么 P(A 或 B)= P(A)+P(B);这被称为不相交事件的添加规则**
- 规则#5: P(A 或 B) = P(A) + P(B) — P(A 和 B);这就是所谓的一般加法法则。**
- 规则#6:如果 A 和 B 是两个独立事件,那么 P(A 和 B)= P(A) P(B);这叫做独立事件的乘法法则。***
- 规则#7:给定事件 A,事件 B 的条件概率为 P(B|A) = P(A 和 B) / P(A)
- 规则#8:对于任意两个事件 A 和 B, P(A 和 B)= P(A) P(B | A);这叫做一般乘法法则***
计数方法

阶乘公式:n!= n x(n-1)x(n-2)x…x 2 x 1 当项目数等于可用位置数时使用。例如:找出 5 个人可以坐在 5 个空座位上的总数。
= 5×4×3×2×1 = 120
基本计数原理(乘法) 当允许重复且填充空位的方式数不受先前填充的影响时,应使用此方法。有 3 种早餐、4 种午餐和 5 种甜点。组合总数= 5 x 4 x 3 = 60
排列:P(n,r)= n!/(n r)!
一个代码有 4 个特定顺序的数字,数字范围从 0 到 9。如果一个数字只能用一次,有多少种排列?
P(n,r) = 10!/(10–4)!=(10 x 9 x 8 x 7 x 6 x 5 x 4 x 3 x2 x 1)/(6x 5 x 4 x 3 x2 x 1)= 5040
组合公式:C(n,r)=(n!)/[(n r)!r!] 当不允许替换并且项目的排列顺序不重要时使用。要赢得彩票,你必须从 1 到 52 中以任意顺序选出 5 个正确的数字。有多少种可能的组合?
C(n,r) = 52!/ (52–5)!5!= 2598960
问:描述马尔可夫链?

答:Brilliant 提供了一个很棒的马尔可夫链定义(此处):
“马尔可夫链是一个数学系统,它根据某些概率规则经历从一种状态到另一种状态的转换。马尔可夫链的定义特征是,无论过程如何到达其当前状态,可能的未来状态都是固定的。换句话说,转换到任何特定状态的概率只取决于当前状态和经过的时间。”
马尔可夫链背后的实际数学需要线性代数和矩阵的知识,所以我会在下面留下一些链接,以防你想自己进一步探索这个主题。
问:描述不同的正则化方法,如 L1 和 L2 正则化?

答:L1 和 L2 正则化都是用于减少训练数据过拟合的方法。最小二乘法使残差平方和最小,这可能导致低偏差但高方差。
L2 正则化,也称为岭回归,最小化残差平方和加上λ乘以斜率平方。这个附加项被称为岭回归惩罚。这增加了模型的偏差,使得对训练数据的拟合更差,但是也减少了方差。****
如果采用岭回归罚分并用斜率的绝对值替换它,则得到套索回归或 L1 正则化。****
L2 不太稳健,但有一个稳定的解决方案,而且总是一个解决方案。L1 更稳健,但是具有不稳定的解,并且可能具有多个解。
StatQuest 有一个关于套索和山脊回归的惊人视频这里。
问:神经网络基础

答:神经网络是受人脑启发的多层模型。就像我们大脑中的神经元一样,上面的圆圈代表一个节点。蓝色圆圈代表输入层,黑色圆圈代表隐藏层,绿色圆圈代表输出层。隐藏层中的每个节点代表输入经过的功能,最终导致绿色圆圈中的输出。这些功能的正式术语称为s 形激活功能。****
如果你想要一步一步的创建神经网络的例子,请点击这里查看 Victor Zhou 的文章。
如果你是一名视觉/音频学习者,3Blue1Brown 在 YouTube 上有一个关于神经网络和深度学习的惊人系列这里。
感谢你的阅读!
如果你喜欢我的工作并想支持我,请在我的电子邮件列表这里注册,成为第一个听到新的独家内容的人!😃
更多相关文章
微软面试中一些数据科学问题的演练
towardsdatascience.com](/microsoft-data-science-interview-questions-and-answers-69ccac16bd9b) [## 谷歌的数据科学面试脑筋急转弯
作为谷歌数据科学面试的一部分,他们喜欢问一些他们称为“解决问题”的问题…
towardsdatascience.com](/googles-data-science-interview-brain-teasers-7f3c1dc4ea7f) [## 数据科学家的 5 个常见 SQL 面试问题
帮助您发展 SQL 技能,在任何面试中胜出
towardsdatascience.com](/5-common-sql-interview-problems-for-data-scientists-1bfa02d8bae6) [## 数据科学家的 40 个统计面试问题和答案
为你的面试复习统计知识的资源!
towardsdatascience.com](/40-statistics-interview-problems-and-answers-for-data-scientists-6971a02b7eee) [## 亚马逊的数据科学家面试实践问题
一些亚马逊面试问题的演练!
towardsdatascience.com](/amazon-data-scientist-interview-practice-problems-15b9b86e86c6)******
关于自动编码器异常检测的更多信息:用 Keras 测量序列异常
使用 Keras 框架设计一个简单的自动编码器,它可以学习字符串序列的模式并检测错误的模式。

由 Raphael Schaller 在 Unsplash 拍摄的照片
几个月前,我发布了一个的故事,展示了如何使用自动编码器(一种主要用于降维和图像压缩的神经网络的特定类型或架构)来有效地检测一大组序列中的异常或错误的字符串序列。给定一个遵循特定模式或格式的序列列表(例如,“AB121E”、“AB323”、“DN176”),我展示了我们如何设计一个网络来学习这些模式或格式,并能够检测序列格式不正确的情况。在这个故事中,我想展示一个更“高级”的用例,或者我们如何使用相同的方法来解决一个稍微复杂一些的问题。有时,我们想要回答的问题不是某个序列是否异常或异常,而是某个给定序列与其他序列相比如何异常或异常。例如,假设我们的数据流中每小时有一些异常序列是很正常的,但我们需要找到一种方法来评估某个小时与前一个小时相比有多正常或不正常。我们可以计算出我们每小时看到的异常的平均水平,但是我们如何处理这样的事情呢?
我不会深入研究自动编码器的理论以及它们是如何工作的(有相当多的好的和可访问的阅读材料——我在下面提到了一些),但简单地说,基本思想是自动编码器是一种网络形式,它学习其输入数据的 压缩表示,并试图重建它。通过学习如何从压缩表示中重建序列,一个训练有素的自动编码器可以说是学习了控制其格式的“主导规则”(我应该承认,虽然这个类比对一些人有帮助,但不是每个人都觉得学习压缩表示的想法像学习格式规则一样有吸引力)。因此,我们可以使用这样的自动编码器来估计某个序列的格式或模式如何不同于或偏离其他序列。
遵循“代码优先”的方法,我将立即深入研究代码,并按 3 个主要步骤进行:
- 准备深度学习的序列 —标记化、填充和迁移(这取决于手头的问题,但我不认为这个简单的问题需要使用嵌入——无论是学习到的还是预先学习到的)。
- 创建并训练一个自动编码器
- 预测得分
1.序列准备
1.1 标记化
因此,我们有一个相当大的数据集,大约 25 万个序列(这里描述的方法的成功很大程度上取决于有一个大样本)。序列通常如下所示:

顺序
正如你所看到的,大多数序列由 4 个部分(或更多)组成,并且似乎遵循某种模式或格式,尽管我们不知道它。
我们需要做的第一项任务是标记序列,并为序列中的每个成分(或单词)创建数字表示——即,将['AB ',' C1 ',' D5']变成[1,7,6]。为了做到这一点,我使用了 Keras 的 Tokenizer 类,它负责标记化中的大多数低级步骤。
我定义了一个大小为 750 的词汇表——这意味着记号赋予器将只为数据集中最频繁出现的 750 个记号创建表示。
所有序列都存储在数据帧“序列”中,并通过序列[FEAT_FIELD]进行访问
在我们的数据集上安装了 tokenizer()之后,我们可以使用 tokenizer 的属性 word_index 来获取一个 dictionary 对象,该对象可用于检查分配给每个部分的数字表示。
我们通过在序列上调用标记化器的方法 texts_to_sequences() 来完成标记化阶段,以便获得一个数组,其中包含我们集合中所有序列的数字表示。

我们现在有了一组符号化的序列([1,3,31,22])或一组原始集合的数值表示([DF,36,FR,GX567])。
1.2 填充
我们需要采取的下一步是关注我们分析所基于的序列的长度。在最好的情况下,所有序列的长度都是相同的,但有时情况并非如此。这个问题很重要,因为它关系到我们的模型将使用的特性。因为所有的序列都需要相同的长度(您将在后面看到原因),所以短序列通常用零填充,以匹配长序列的长度。因此,如果我们决定我们的模型要学习的数字特征是 6,那么我们需要用零“填充”这个序列[1,3,31,22],并将其转换为这个序列[1,3,31,22,0,0]。
例如,在我们的例子中,绝大多数序列有 4 个组成部分,但有些没有。

我们序列的平均长度确实是 4,但我们看到有些“异常值”有更多。然而,因为大多数序列只有 4 个分量,并且因为不必要的填充可能会影响我们模型的性能,所以我们最好坚持 4 个分量,这样我们所要做的就是填充少于 4 个分量的序列。

我们使用这个一行程序来实现:
MAX_FEAT_LEN = 4
pad_seqs = pad_sequences(seqs, maxlen=MAX_FEAT_LEN, padding='post', truncating='post')
1.3 矢量化
我们最后也是最重要的一步是对每个序列进行矢量化。通过向量化我们的序列,我们像这样变换(或展平)序列:1,4,31,22
变成这样:

您在这里看到的是一个大小为 750(这是我们的词汇量)的向量,它由 746 个 0 和 4 个 1 组成,而每个 1 的位置或索引是其数值的 representד。换句话说,序列 1,4,31,22 将在向量 v 中表示为:
**v**[1] = 1
**v**[4] = 1
**v**[31] = 1
**v**[22] = 1
使用这种方法可以很容易地实现这一点:
弗朗索瓦·乔莱(DLP)
现在我们的序列可以学习了!
2.创建并训练自动编码器
我不会在这里过多地讨论细节,因为我相信如果你对 DL 和 Keras 有所了解,代码是非常简单明了的。然而,简而言之,自动编码器通常是通过连接一个编码层和一个解码层来创建的,前者将输入压缩或挤压到一个较低维度的层中,从而创建一种“瓶颈”,后者学习从输入数据的压缩表示中重建输入。

摘自https://www.jeremyjordan.me/autoencoders/
根据手头的问题,自动编码器显然可以以多种方式设计。我选择使用一个非常简单的例子(主要是由于数据的性质及其相对不复杂),这个例子可以使用 Keras 非常简单地实现,如下所示:

这通常不是必需的,但是如果你想密切跟踪和观察你的模型的性能并保存它(像我一样),那么这可能是有帮助的(否则你可以只调用 autoencoder compile()和 fit()方法。
假设模型表现足够好,我们现在可以加载它,并继续检查和评分我们的序列:
autoencoder = load_model('model_bin.h5')
3.预测和评分
现在我们到了有趣的部分。我们有一个自动编码器,它接收一个字符串序列作为输入(在它被标记化、填充和矢量化之后),并输出同一序列的重构。但是这个输出并不真正有趣——如果我们的自动编码器工作正常,它将简单地输出与我们给它的输入序列非常相似的东西。关于自动编码器的输出,有趣的是输出和输入序列彼此之间有多么不同。假设我们的自动编码器训练有素,那么如果输入序列格式良好,或者如果它遵循自动编码器从训练数据中学习到的相同模式,那么输出序列将是相同或相似的。相应地,如果输入序列不遵循自动编码器已经学习的模式,则输出序列将与输入序列不太相似。
因此,如果我们想要检查某个序列是否或在多大程度上遵循自动编码器从训练数据中学习到的模式,那么我们需要测量自动编码器的预测或其输出的序列所产生的差异(或误差)。但是我们怎么做呢?
让我提出一个可能的方法。在标记化、填充和矢量化之后,每个序列基本上都是一个零向量,在每个数字标记的索引位置都有“1”——因此在 6 个字符的词汇向量中,[1,3]将表示为[0,1,0,1,0,0]。如果我们的自动编码器将接收这个输入,并输出序列[0,1,0,0,1,0],它代表序列[1,4],那么我们有一个错误。我们想要实现的是找到一种方法来测量这或测量两个序列不同的程度。做到这一点的最有效的方法之一是简单地获得每个输出序列的均方误差,通过(1)减去输入和输出以计算误差;(2)平方结果(消除任何负值);(3)将误差的平均值作为输出序列有多“错误”的度量。
这段代码将使这一点更加清楚。

这里我们有 3 个输入序列和 3 个输出序列。第一个输出序列没有任何错误,而第二个输出序列有一个错误,第三个有两个错误。当我们看到减去它们的输出时,这一点就很清楚了:
Result of subtracting the vectors:
[[ 0 0 0 0 0]
[-1 0 0 0 0]
[ 0 0 -1 -1 0]]
当我们计算误差的平方并取每个序列的平均值时,我们就得到一个不完美的度量,或者说它偏离输入序列的程度。
Errors:
[0\. 0.2 0.4]
因此,为了在我们的用例中实现这一点,我们需要做的就是通过对输入序列调用 predict 来从自动编码器获取输出序列,然后对错误数求平方并取平均值。
现在,每个序列都有一个特定的分数,代表它与输入序列的差异程度,我们可以用它来衡量这个序列遵循我们的自动编码器所学习的模式的程度。然而,这个分数本身并没有太大的帮助,也没有告诉我们任何事情(事实上,不管手头的词汇量有多大,考虑这个数字本身就是一个错误)。我们可以利用这一点的一种方法是测量我们数据集的误差平均值 — np.quantile(mse,0.5) ,然后将高于该阈值的每个序列视为比其他序列更不正常;而测量误差高于第 75 百分位的序列为多多异常,依此类推。这将允许我们根据序列与自动编码器学习的模式的偏离程度对序列进行评分。

然而,反其道而行之会更准确、更有效——能够知道某个序列对应的百分位是多少,以便得到它的异常分数。这就是下面的方法所要做的:在准备好字符串之后,它使用 autoencoder 的 predict()方法,获得与输出序列相关联的误差项,并获得误差值的百分位数排名,这可以为我们提供它与学习模式的一致性的更准确的表示。
如下图所示,序列“9 ER 223 ABCDSED”的百分位等级为 73.8,这意味着考虑到自动编码器对其做出的预测错误数量,它是相对不正常的。这可能意味着它与自动编码器学习的其他序列遵循的模式有相当大的差异。
score, rank = get_sequence_anomaly_rank('9 ER 223 ABCDSED')print(f'Anomaly Score:{score}, Percentile Rank:{rank}')==> **Anomaly Score:[0.00346862], Percentile Rank:73.85446487442933**
现在测量某些序列以及序列组的异常程度是很简单的。为了评估某个组的异常程度,我们需要做的就是计算该组成员产生的误差的加权平均值。
我希望这有所帮助!
示例笔记本可以在这里找到
一些关于自动编码器的链接:
[## 自动编码器介绍。
自动编码器是一种无监督的学习技术,其中我们利用神经网络来完成表示的任务…
www.jeremyjordan.me](https://www.jeremyjordan.me/autoencoders/) [## 应用深度学习-第 3 部分:自动编码器
概观
towardsdatascience.com](/applied-deep-learning-part-3-autoencoders-1c083af4d798)
【https://blog.keras.io/building-autoencoders-in-keras.html
不仅仅是漂亮的图表

大数据可视化的重要性
很多时候,局外人认为数据分析师和科学家的工作只是处理数字。我们中那些真正实践过从数据中提取洞见的科学和艺术的人知道,这项工作至少与分析一样具有创造性。数据团队在组织中的角色基本上是用数据讲述故事。那么,为什么如此多的数据团队没有使用他们可用的最强大的通信工具呢?
即使是经验丰富的数据科学家也经常将数据可视化归入数据分析的探索阶段。他们没有在数据分析过程中最重要的阶段使用 dataviz:交流他们的结果。这些数据专家忘记的是,并不是每个人都像他们一样思考。虽然数字或表格对于有丰富理解经验的人来说很有意义,但大多数人更擅长解释数据的可视化表示。
在这篇文章中,我强调了数据可视化对于帮助数据科学家和数据消费者更好地理解数据集至关重要的几个具体例子。这些例子并不全面,但是它们可以帮助你思考和理解数据可视化的价值。最后,我引用了史蒂夫·乔布斯操纵数据的一个具体例子,警告欺骗性或其他不良数据可视化的危险。
模式识别
你是否曾经看着一张表格,却被它弄糊涂了,只是为了可视化数据,并意识到有一个清晰的模式?有效的数据可视化表示允许我们识别当数据用数字表示时不明显的模式。模式可以是任何东西,从认识到增长是指数的(例如,与线性或随机相反),到证明两个变量之间的相关性。
相对值
比较相对价值是人脑比数字更擅长直观理解图像的另一个领域。饼图和圆环图、条形图、直方图和树形图都有助于以易于理解的方式阐明事物的相对大小、价值或数量。
以这个视频为例,它试图给人一种印象,与我们宇宙中的其他物体相比,地球是多么的小。虽然创造者可以简单地列出这些物体的体积(地球是 1 万亿立方千米,而太阳是 1.409 x 1⁰ ⁸立方千米),但他们正确地评估了为了让人们对宇宙的大小有一个真正直观的感觉,直观地显示物体的体积会更好。
地理空间信息
地理空间信息是一种绝对需要可视化的数据。在这个领域,我们认为图形、非表格数据是理所当然的,但重要的是要记住,表格形式的地理空间数据基本上是无用的。

摘自我的分析“HRC 写的是哪里”
左边是一个表示地理空间数据的最基本(也是最有效)方法的例子:地图。具体来说,这是一个 choropleth 地图,这意味着陆地被划分为区域(在这种情况下是国家),然后根据统计数据(在这种情况下是希拉里·克林顿在她的国务院电子邮件中提到特定国家的次数)进行着色。想象一下这个数据以表格的形式出现是什么样子:一列是国家的名称,另一列是那个国家被提及的次数。这个表很长(超过 150 行),很难破译。可视化很快告诉我们哪些国家经常被提及(中国、俄罗斯、英国、伊朗和利比亚),哪些国家很少被提及(蒙古、撒哈拉以南非洲国家)。
能力越大,责任越大
不幸的是,使用数据可视化来欺骗而不是告知太容易了。对于数据分析师工具包中的其他工具也是如此,但是对于数据可视化来说,特别容易陷入欺骗的陷阱。

滥用可视化的一个著名例子;请注意 3D 饼图如何让 19.5%的苹果市场份额看起来比 21.2%的“其他”市场份额大
以史蒂夫·乔布斯在 2008 年 MacWorld 博览会上展示的 3D 饼图为例。一个不经意的观察者可能会认为苹果智能手机的市场份额大于“其他”类别的市场份额,因为绿色部分似乎大于紫色部分。然而,如果你比较切片上的数字,你可以看到“其他”类别实际上有 21.2%的市场份额,而苹果只有 19.5%的市场份额。这是否是史蒂夫·乔布斯的故意混淆视听尚有争议,但效果是显而易见的。
让 dataviz 再次变得伟大
正如我在的另一篇文章中所写的,我最近看到了很多糟糕的数据可视化。作为数据消费者,我们必须对媒体制作者提出更多要求,让他们知道他们不能逃避草率或误导性的数据可视化。相反,作为数据传播者(数据科学家、分析师、记者),我们必须努力使数据可视化,不仅在视觉上引人注目,而且信息丰富,不会误导。数据不仅仅是美丽的。它是一个强大的工具,可以帮助人们在从个人生活到治理的方方面面做出关键决策。让我们以最好的方式使用我们的工具。
死亡率作为流行病爆发的指标
我们可以从冠状病毒死亡率的不可靠估计中推断出什么?

马库斯·斯皮斯克在 Unsplash 上的照片
冠状病毒已经蔓延到世界上许多国家,并在许多地方引起恐慌。一个主要的讨论是关于估计死亡率。众所周知,死亡率的估计是非常不可靠的,原因有很多。举个例子,
死亡率估计值可能过低,因为:
- 重症患者可能会在检测前死亡
- 当地官员可能会隐瞒死亡人数
- 今天的死亡人数是由许多天前的病人造成的(关于这种“滞后”效应的更多细节在我的上一篇文章中讨论过)
- ……
死亡率估计值可能过高,因为:
- 症状较轻的患者可能不会感到不适而寻求医疗帮助
- 患者可能不符合当地政府的检测标准,因此无法得到确认
- 由于人为错误、取样错误、疾病的早期阶段等,受感染的患者可能具有阴性测试结果
- ……
对双方来说,这样的例子不胜枚举。并且有大量的文献根据这些影响来调整估计值。但是,我们能从这个不可靠的估计中了解到别的东西吗?
与其关注死亡率本身的值,我们不如用它来表示流行病的爆发状态。例如,我们可以使用死亡率和确诊病例数来跨越一个二维平面,并添加与新确诊病例成比例的气泡大小来指示最近的传播速度。这些可视化大致将平面分成四个区域:

如果我们假设所有国家的“真实”死亡率大致稳定(一个很大的“如果”,但仍然是一个有用的起点),那么我们可以为每个地区推导出以下公式:
- 区域#1:不确定。该地区确诊数低,死亡率低。没有足够的统计数据来做出可靠的判断。
- 区域#2:检测中。该地区确诊人数低,但死亡率高,这表明可能有许多未被发现的感染患者(因为症状严重的患者通常被较早发现)。
- 区域#3:有效检测。该地区确诊人数高,但死亡率低。这表明已经发现了许多症状轻微的患者。所以检测被感染的病人可能是有效的。
- 区域#4:保健超负荷和/或检测不足。该地区确诊人数多,死亡率高。这表明卫生保健系统可能超负荷,和/或仍有许多病人未被发现。
这些区域的边界不是清晰的,而是逐渐的。对于死亡率,湖北省以外的中国死亡率 0.5%~2%是一个合理的基线。对于已确认的数量,大约 100~500 是开始淹没医疗系统的合理范围。
应用我们上面陈述的逻辑,我们可以使用约翰霍普金斯大学的数据,为一组国家/地区绘制 2020 年 2 月 26 日和一周后 2020 年 3 月 26 日两个不同日期的死亡率与确诊人数的关系图。每个泡沫的大小与新确认计数的 3 天移动平均值的对数成正比。因此,更大的气泡意味着最近确诊了更多病例,这表明(检测到的)病毒正在更快地传播。


从以上情节中,我们可以对各个国家的疫情做出如下判断:
法国
它在 2020 年 2 月 26 日处于检测中区域,尽管其小气泡大小表明当时没有多少新病例。
因此,正如预期的那样,一周后的 2020 年 3 月 2 日,其确诊病例在短短一周内从 18 例激增至 191 例,将死亡率降至更典型的 1.6%。
伊朗
它在 2020 年 2 月 26 日处于检测中区域,其相对较大的气泡尺寸表明该病毒正在快速传播。如果这种蔓延得不到控制,将来可能会淹没医疗资源。
上述说法可以在 2020 年 3 月 2 日的图中得到证实,在那里,伊朗已经进入检测不足和/或医疗保健超载区域,泡沫尺寸甚至更大。因此,我们可以说伊朗可能仍然有大量未被发现的病人,医疗保健系统可能开始超负荷运转。
美国
截至 2020 年 2 月 26 日,它位于非决定性区域。所以相对比较安静。
然而,在 2020 年 3 月 2 日,美国明显位于检测不足区域。这与一周前法国的情况类似,但有更多的已确认数据和更大的泡沫尺寸。这表明,一周前,美国可能比法国有更多未被发现的感染者。这是一个强烈的警告信号。因为如果我们预计美国的死亡率会下降到与法国相似的 0.5~2%的水平,我们会推断出成百上千甚至上千的感染者仍然没有被发现。
另一方面,我们必须意识到,截至 2020 年 3 月 2 日,美国六分之五的死亡病例集中在华盛顿州金县的生命护理中心疗养院。众所周知,老年人群的死亡率更高。然而,King 县只有 14 例确诊病例,因此县内死亡率为 35.7%,比任何年龄组的死亡率都高几倍。因此,仍然有理由相信美国处于未被检测到的地区,有数百名患者未被检测到,尽管疗养院的调整使其没有最初显示的那么严重。
韩国
它位于 2020 年 2 月 26 日和 2020 年 3 月 2 日的有效探测区域。它拥有世界上最高的确诊数量(除了中国),同时保持较低的死亡率。但是它巨大的气泡尺寸表明它的主要问题是遏制传播,而不是检测病毒。
意大利
在这两个日期,它的情况介于伊朗和韩国之间。
德国和西班牙
他们在 2020-02-26 处于非决定性区域,慢慢走向道德为零的有效探测区域。然而,他们相对较大的气泡尺寸表明他们主要关心的是如何遏制病毒。
日本
它一直处于有效检测区域和保健超负荷区域之间。所以他们的病毒检测可能不如德国和西班牙有效。关键是要看他们在不久的将来走向何方。
钻石公主
它在 2020 年 2 月 26 日处于有效探测区域。我们知道,飞船上的病毒检测确实非常严格。
在 2020 年 3 月 2 日,它仍然在有效探测区域,但是气泡尺寸减小了。这表明局势已经降温。此外,其死亡率稳定在 0.9%,非常符合 0.5~2%的基线。
最后的话
我们可以看到,这个图对于快速评估全球每个国家的相对情况非常有帮助。
此外,通过使用我的 GitHub 项目,制作这样一个信息丰富的情节很容易。像往常一样,Google Colab 上的一个功能齐全的 Python 笔记本包含在回购中,供读者把玩。
承认
我要感谢我的朋友 David Tian,他是一名机器学习工程师,对 Google Colab 的设置提供了慷慨的帮助,并对本文提出了宝贵的建议。看看他有趣的自驾* DeepPiCar *博客。
参考:
- Komo news,3/2/2020: 华盛顿州冠状病毒:6 人死亡,12 人感染
- 按年龄划分的新冠肺炎死亡率:https://www . worldometers . info/coronavirus/coronavirus-age-sex-demographics/
使用 Python 的马赛克艺术。

ASCII 艺术图片使用了 Nicola Fioravanti 在 Unsplash 上的照片
用表情符号和 ASCII 字符重建图像。
在我的上一篇文章中,我探索了一种有趣的表示图像的格式,称为床单视图,以开发傅立叶变换背后的一些直觉。


左边是原图,右边是床单图。图中使用了由 Unsplash 上的 Jakob Kac 拍摄的照片
这让我很好奇去探索图像的替代表现,这是当前的主题。在本文中,探索了两种使用 python 构建马赛克“艺术”的有趣方法。第一种方法使用表情符号(比如🏠,🔥,😎…)第二个使用 ASCII 字符。
马赛克传统上是用规则或不规则的小石块或瓷砖制成的图案或图像。马赛克风格的绘画被称为点彩,艺术家使用颜料滴来构建整个图像。文森特·凡高、乔治·修拉和保罗·西涅克是这一流派的几位著名艺术家。


(左)梵高的《星夜》。图片由 Eric Perlin 从 Pixabay 获得。(右)来自土耳其的马赛克艺术。Nick Kwan 在 Unsplash 上拍摄的照片
图像是由像素组成的数字马赛克。因此,像素可以用其他单位代替来表示同一幅图像。比如表情符号。更具体地说,表情符号的图像。



原始图片由 Ahmed Nishaath 在 Unsplash ( 最左边)上拍摄。带有 20x20 个表情符号的表情符号马赛克(中间)。有 100x100 个表情符号的表情符号马赛克(最右边)。
这里,我拿了一个 1500 个表情符号的目录。要用表情符号代替一个像素,必须建立一个公平的比较。因此,每个表情符号通过取组成该单独表情符号的所有像素的平均值而被简化为单个 RGB 元组。类似地,评估目录中所有表情符号的平均值。现在,我们希望转换为马赛克形式的图像被加载,每个像素被替换为具有最接近 RGB 值的表情符号(最近邻居)。结果会如上图所示!
自 60 年代以来,使用 ASCII 字符转换图像的类似想法就已经存在,其中使用可打印字符代替表情符号来编写显示图像的文本文件。这里是一个致力于 ASCII 艺术的网站
它曾经非常流行,因为文本处理起来要快得多。但是,技术已经赶上来了,ASCII 艺术今天对编程爱好者来说仍然只是一个遗迹。在 python 中,可以开发简洁的代码来产生以下结果。



原始图像(右),ASCII 图像(中),图像特写(左)
这里,图像在调整大小后首先被转换成灰度级。ASCII 字符串是按照每个字符大小的递增顺序构造的。它以实心字符如“#”开始,以较小的字符如“.”结束。图像可以用数量少得惊人的字符连贯地再现。每个像素的灰度级被转换成 256(上限)的比例。基于这个比率,每个像素被替换为我们的原始 ASCII 字符串中的一个字符。
我的 GitHub 上也有所有的程序。
有趣的是,超过 1500 个表情符号被用来对原始图像进行连贯的复制。然而,第二种方法使用不超过 24 个 ASCII 字符来提供有效的可视化。这并不完全是一个公平的比较,因为与颜色相关的信息在后来丢失了。
将视频转换成 ASCII 格式也很流行,这提供了一种有趣的视觉效果,就像 90 年代后期在矩阵电影中使用的那样。
像往常一样,联系我,继续讨论其他有趣的图像格式,或者向我提供一些关于内容的反馈。
大多数企业没有以正确的方式使用数据[研究]
没有使用数据的策略,数据就没有价值。GetApp 的研究分享了大多数企业都会犯的数据错误,以及应该怎么做。

资料来源:Teresa Meinen 为 GetApp 撰写
《哈佛商业评论》称数据科学为“21 世纪最性感的工作”LinkedIn 的联合创始人告诉 Knowledge@Wharton,数据科学的工作岗位在过去三年里增长了 15 到 20 倍。由于对数据科学家的需求超过了供应,许多公司将支付六位数的薪酬来吸引顶级人才。
但是闪光的并不都是金子:超过一半的数据科学家说他们计划在 2020 年寻找新的角色。
当被问及不满背后的原因时,44%的人认为是官僚主义,29%的人认为是缺乏领导的支持。这两个答案都超过了对缺乏正确的数据科学工具的抱怨,这赢得了 28%的投票。
这些结果令人难过,但并不令人惊讶。尽管对数据科学家有需求,但大多数企业仍然没有为帮助他们取得成功打下基础。
当今的企业如何使用大数据
去年,GetApp 调查了员工人数不超过 500 人的北美企业的近 500 名员工。我们想了解他们如何利用数据做出商业决策,以及他们在这样做时有多大的信心。我们还想了解拥有专门处理数据的工具和同事是否会增加业务决策的信心。
或许不出所料,结果因行业而异。从事会计工作的受访者最有可能使用 Excel 来收集和分析数据。但是他们也最难从数据中识别观点。这是有道理的,因为会计师也报告说没有足够的数据来发现这种见解;相反,许多人表示,他们要么数据太多,要么数据太少,无法做出有影响力的选择。
与此同时,在医疗保健行业工作的受访者对可用数据的利用率最高。但在接受调查的五个行业中,医疗保健行业在做出基于数据的决策时信心水平最低。他们提到需要某些类型的数据(如客户满意度得分)来改善结果。

其他结果更普遍。在调查的五个行业中,我们发现:
- 人们对其数据决策的自信程度与他们认为数据对其业务的重要性之间存在正相关关系。
- 雇用数据科学家让企业领导者更加自信,他们拥有正确的数据和洞察力来做出业务决策。
- 有全职员工分析数据的公司也更经常使用商业智能(BI)软件。
- 使用 BI 软件的公司认为他们做出的基于数据的决策更有影响力。
从这里去哪里
当我回顾这些结果时,有两件事让我印象深刻。首先,投资于收集和分析业务数据的工具和员工可以改善结果。对数据感觉最舒服的企业也是那些创造专门拥有数据的角色的企业。他们还投资软件和其他数据科学工具供员工使用。
也就是说,对企业来说,分散数据同样重要。这包括给予每个团队领导在他们的领域内使用数据的自主权。无论你管理的是销售团队还是医院,所有业务线的领导者都应该能够利用数据提升团队的业绩。
这就是成为公民数据科学家的意义所在:使用拖放模型等 BI 软件功能来解释数据与您的团队和更大企业的相关性。
与通常从别处高价招聘的数据科学家不同,公民数据科学家已经是你企业的一部分。因此,他们将对您的技术体系、战略和客户痛点的深入了解带到他们创建的每个数据可视化中。
公民数据科学家不仅仅是好的,他们也不打算取代训练有素的数据科学家。他们为您的业务带来独特的价值,并且作为数据科学家同样重要。
这一点值得重复,因为一名员工拥有所有基于数据的决策已经不够了,或者您的企业不再需要投资工具、培训和员工来将您的数据使用提升到一个新的水平。
如果你想在业务中做出更好的决策,你需要应用分析和某种形式的商业智能。如果这是你对 2020 年的承诺,你可以采取以下几个步骤:
1。写一份使命陈述
用一两个书面句子阐明数据如何促进您的业务发展。你越具体越好。光说“我们公司会给所有团队提供他们需要的工具和培训来做出基于数据的决策”是不够的。
相反,可以尝试这样的方法:
“我们的首席技术官将使所有团队领导能够访问[首选 BI/分析软件]中的数据。这将使每个人都能实现我们的共同业务目标,即在 2020 年将客户满意度得分提高 10%。”
不要给自己压力,让这份使命宣言保持不变。随着角色和业务需求的发展,您可以审查和更新它。
关键是让 it 始终专注于期望的业务成果。将数据分析连接回大图,将训练每个人更周到地使用它。
2。为您的数据战略制定路线图
要成功使用数据,您需要将正确类型和数量的数据与您可以用其解决的业务问题联系起来。因此,您应该将您的策略基于支持特定业务目标的数据管理和分析实践。
一旦你写好了你的数据使命陈述,开始构建一个路线图来跟踪你实现它的进程。这个路线图将展示你为实现你的使命宣言所要采取的行动,以及你执行这项工作的计划。它还可以让您将新的数据使用请求与您当前的计划进行比较。
您的数据路线图的主要目标是在业务范围内就以下方面达成共识:
- 如何使用数据。
- 确认您将使用数据实现哪些业务目标,以及实现每个目标所需的数据类型和工具。
- 为您的整个企业提供一个框架,以了解数据现在和未来的使用情况。
像你的使命陈述一样,这个路线图不是静态的:它将随着你的业务需求和数据使用而发展。关键是用它作为北极星,了解你的企业如何根据数据做出决策。如果你和你的同事知道数据的价值,你可以用它来找到更有效的见解。
3。建立数据指导委员会
通过建立数据使用指导委员会,团结组织中的所有领导者。整体业务、技术战略和特定业务线(销售、营销等)的领导者。)都应该在这个委员会有席位。它将设定数据使用的优先级,批准新 BI 工具和计划的资金,解决资源冲突,并寻找在业务中使用数据的新机会。
一个人(比如首席技术官,或者最好是首席数字/数据官)应该担任委员会主席。在这样做的时候,他们需要考虑该委员会相对于企业规模的规模。
一个更小的委员会将能够更快、更频繁地安排会议,同时更快地解决问题。但是,如果你将关键业务部门的领导排除在外,你将面临失去整个组织认同的风险。
为了缓解这种情况,承诺每个业务线都有一名领导在委员会中任职。这包括监督公司系统架构的人,以及负责为分析环境提供源系统的人。
计划每季度至少会面一次。你的目标不是为每个领导创造更多的工作,包括你自己。相反,你要向每个委员会成员展示数据对他们的工作有多么宝贵,以及这个委员会将如何改善他们的所有成果。
如今创建的数据量,再加上BI 和分析软件的增长,意味着旧的分析方法将不再适用。像 Excel 这样的工具,虽然强大而有用,并不是为了处理今天可用的大量数据而构建的。数据与所有业务线的相关性意味着它不再属于一个首席执行官的领域。
问题在于,成为“数据驱动型组织”的道路漫长而曲折。从投资错误的 BI 工具到不知道使用哪些数据点来解决每个问题,使用数据的风险似乎太高了。事实是,你负担不起而不是使用数据——做一些基础工作可以缓解很多痛点。
通过撰写使命陈述、创建路线图和建立跨职能共识,您自己的企业可以避免大多数陷阱,并利用数据保持增长。你甚至可以雇佣一个梦寐以求的数据科学家——防止他们离开。
Lauren Maffeo 在 GetApp 公司领导商业智能研究,该公司为软件购买者提供合适的工具和技术来发展他们的业务。作为一名分析师,Lauren 的兴趣领域包括语音和自然语言工具、数据挖掘技术、预测分析以及为数据科学构建商业案例。她在普林斯顿大学和哥伦比亚大学、Twitter 的旧金山总部和谷歌 DC 开发者大会等机构展示了她对人工智能偏见的研究。她还是计算机协会杰出演讲者项目的成员。
机器学习中最常见的损失函数
现实世界中的 DS
每个机器学习工程师都应该了解机器学习中这些常见的损失函数,以及何时使用它们。
在数学优化和决策理论中,损失函数或成本函数是将事件或一个或多个变量的值映射到实数上的函数,该实数直观地表示与该事件相关的一些“成本”。
——维基百科

作为一个核心元素,损失函数是一种评估机器学习算法的方法,它可以很好地模拟您的特征数据集。它被定义为衡量你的模型在预测预期结果方面有多好。
成本函数 和 损失函数 指同一上下文。成本函数是作为所有损失函数值的平均值计算的函数。然而,损失函数是针对每个样本输出与其实际值的比较来计算的。
损失函数与你已经建立的模型的预测直接相关。因此,如果您的损失函数值较小,您的模型将提供良好的结果。损失函数,或者更确切地说,用于评估模型性能的成本函数,需要最小化以提高其性能。
现在让我们深入研究损失函数。
广泛地说,损失函数可以根据我们在现实世界中遇到的问题类型分为两大类—分类和回归。在分类中,任务是预测问题所处理的所有类别各自的概率。相反,在回归中,任务是预测关于学习算法的一组给定独立特征的连续值。
**Assumptions:**
n/m — Number of training samples.
i — ith training sample in a dataset.
y(i) — Actual value for the ith training sample.
y_hat(i) — Predicted value for the ith training sample.
分类损失
1.二元交叉熵损失/对数损失
这是分类问题中最常用的损失函数。交叉熵损失随着预测概率收敛到实际标签而减少。它测量分类模型的性能,该模型的预测输出是介于 0 和 1 之间的概率值。
当类别数为 2 时,二元分类**

当类别数大于 2 时,多类别分类**
****
交叉熵损失公式是从正则似然函数中推导出来的,但是加入了对数。
2.铰链损耗
用于分类问题的第二常见损失函数和交叉熵损失函数的替代函数是铰链损失,主要用于支持向量机(SVM)模型评估。
****
铰链损失不仅惩罚错误的预测,也惩罚不自信的正确预测。它主要用于类别标签为-1 和 1 的 SVM 分类器。确保将恶性分类标签从 0 更改为-1。

照片由 Jen Theodore 在 Unsplash 上拍摄
回归损失
1.均方误差/二次损耗/ L2 损耗
MSE 损失函数被定义为实际值和预测值之间的平方差的平均值。它是最常用的回归损失函数。
****
对应的代价函数是这些平方误差的均值。MSE 损失函数通过平方它们来惩罚产生大误差的模型,并且该属性使得 MSE 成本函数对异常值不太稳健。因此, 如果数据容易出现很多离群值,就不应该使用。
2.平均绝对误差/ L1 损耗
MSE 损失函数被定义为实际值和预测值之间的绝对差值的平均值。这是第二个最常用的回归损失函数。它测量一组预测中误差的平均大小,不考虑它们的方向。
****
对应的代价函数是这些绝对误差(MAE)** 的均值。与 MSE 损失函数相比,MAE 损失函数对异常值更稳健。因此, 在数据容易出现很多离群值的情况下应该使用。**
3.Huber 损失/平滑平均绝对误差
Huber 损失函数被定义为当𝛿 ~ 0 时 MSE 和当𝛿 ~ ∞(大数)时 MAE 接近时 MSE 和 MAE 损失函数的组合。它是平均绝对误差,当误差很小时,它变成二次误差。使误差为二次型取决于误差有多小,这是由一个可以调节的超参数𝛿(δ)控制的。****
****
增量值的选择至关重要,因为它决定了您愿意将什么视为异常值。因此,取决于超参数值,与 MSE 损失函数相比,Huber 损失函数对异常值不太敏感。因此, 如果数据容易出现异常值,可以使用它,并且我们可能需要训练超参数 delta,这是一个迭代过程。
4.对数损失
对数余弦损失函数被定义为预测误差的双曲余弦的对数。这是回归任务中使用的另一个函数,比 MSE 损失平滑得多。它具有 Huber 损失的所有优点,并且它在任何地方都是两次可微的,不像 Huber 损失,因为像 XGBoost 这样的一些学习算法使用牛顿法来寻找最优值,因此需要二阶导数( Hessian )。
****
*log(cosh(x))*约等于*(x ** 2) / 2*为小*x*,以*abs(x) - log(2)*为大*x*。这意味着“logcosh”的工作方式很大程度上类似于均方误差,但不会受到偶尔出现的严重错误预测的强烈影响。
— Tensorflow 文档
5。分位数损失
分位数是一个值,低于这个值,一个组中的一部分样本就会下降。机器学习模型通过最小化(或最大化)目标函数来工作。顾名思义,分位数回归损失函数用于预测分位数。对于一组预测,损失将是其平均值。
****
分位数损失函数在我们对预测区间而不仅仅是点预测感兴趣时非常有用。
感谢您的阅读!希望这篇帖子有用。我感谢反馈和建设性的批评。如果你想谈论这篇文章或其他相关话题,你可以在这里或我的 LinkedIn 账户上给我发短信。

大多数数据科学职位描述应该不再要求博士学位
我刚刚搜索了“数据科学家”在第一页的 20 份工作中,有 6 份没有提到博士学位,3 份说有博士学位会很好,11 份把博士学位作为一项要求。我没有花几个小时去查看求职公告板,也没有让一个系统自动收集所有信息,但我仍然相信这是一种趋势。
为什么这是一种趋势?
数据科学,机器学习,人工智能,它们都是令人兴奋的新领域。我的理论是,招聘这些职位的人认为这些领域极其复杂,只有拥有博士学位的人才可能理解它们。根据我的经验,虽然数据科学是一个技术领域,但它并不一定比软件中的任何其他职位更具技术性(通常更少)。科技领域的其他职位通常由拥有学士学位的人来担任。以下是我认为停止将博士学位作为一项要求最符合大多数企业利益的主要原因。
1.他们是昂贵的候选人
拥有数据科学/人工智能/数学/统计学/计算机科学等博士学位的人是就业市场上最受欢迎的人之一。公司经常抱怨缺乏人才,正如我在第一段中的例子所表明的,无论如何,企业都对相同的候选人感兴趣。在如此高的需求下,你只能通过提供潜在 20 万美元以上的薪水来竞争。
2.你根本找不到候选人
这些人的供应有限。他们中的大多数人甚至对为财富 500 强公司工作不感兴趣,而是更愿意专门为大学或领先的科技公司(苹果、亚马逊、DeepMind、谷歌、IBM、特斯拉等)工作。不过,这些顶级科技公司也在招聘本科生。如果他们抓住了所有顶尖的本科生,想象一下获得博士学位有多难。你应该问问你自己,你是否认为普通的博士真的会花时间在。我倾向于认为他们没有。我希望人们要么联系他们找工作,要么申请上述公司并被接受。即使一个博士确实花了很多时间,我也会问你自己,你的公司对一个博士来说是否突出。这让我想到了第三点。
3.你的公司可能不太适合博士
正如我们所建立的博士学位有很多工作选择,都是高薪。如果你不提供本质上有趣和/或改变世界的工作,他们可能会很快厌倦。如果工作描述中有“根据需要在 Excel 中进行即席分析”这样的事情,那么这将是一项乏味的工作。一名新员工需要一段时间来适应并开始做出重大贡献,因此不得不替换一名因为不喜欢工作而辞职的数据科学家将是糟糕的。你把钱(和时间)浪费在了一个没有足够长时间产生价值的候选人身上。
4.博士可能不是你想要的
这确实是上述原因的反面。博士是做研究的人,大多数公司不做研究。通过研究,我的意思是创造新的模式。但是你可能会说“如果我们不做研究,我们如何保持竞争力?”大多数公司都没有成功实现基本的机器学习。我建议去找一个对将现有技术应用于您的数据感兴趣的人,为您的数据优化这些技术,找到将它们应用于您的业务的新方法,并让它在您的业务中可操作化。在我的文章“我的本科生团队如何赢得研究生和博士生的数据科学竞赛”中强调了进一步的证据
5.这对合格的候选人来说是一个危险信号
如果我看到你的公司要求一个博士来管理你的 Tableau 仪表盘(或其他平凡的东西),我会立即认为你的公司不知道它在数据科学方面做什么。如果你发出的信号是你的公司不知道自己在做什么,那么你不仅得不到博士学位,还会吓跑那些没有博士学位的合格人才。
你应该怎么做?

听着,我明白你想要一个合格的人,你很难判断谁符合标准,谁不符合标准。我的最佳建议是让你参考 Daniel tunke lang 2013 年关于如何采访数据科学家的演讲。尽管该视频已有近 7 年的历史,但在招聘数据科学家时,它仍然是一个很好的流程。
如果你仍然想在你的团队中获得博士学位,你需要一个不同的策略,然后在求职公告栏上发布申请。我认为你需要一种方法,既能直接与这些人交流,又能让他们相信你有他们感兴趣的工作。
实现分类问题径向基函数神经网络的最有效方法

高斯径向基函数的三维可视化
如何使用 K-均值聚类和线性回归对图像进行分类
介绍
径向基函数神经网络(RBFNN)是一种不同寻常但非常快速、有效和直观的机器学习算法。三层网络可用于解决分类和回归问题。本文描述了对 MNIST 手写数字数据集分类的实现,其中获得了约 94% 的准确率。此外,为了方便来自不同编程语言背景的人,C++和 Python 项目代码都被添加进来[3]。
先决条件
- k-均值聚类算法
- 线性回归
如果您对上述任何一个主题都不熟悉,可以参考文章末尾的参考资料和参考文献[1][2] 部分给出的链接。
径向基函数
首先,让我们从一个简单的例子开始。想象一下,下面 2D 绘制的数据是给你的。你的任务是找到一个最接近集群位置的模式。因此,当引入未知点时,该模型可以预测它属于第一还是第二数据聚类。使用 K-Means 聚类算法可以很容易地解决这个问题。

https://haosutopia.github.io/2018/04/K-Means-01/
然而,RBFNN 使用不同的方法。它使用圆形对数据平面(在 2D)建模。因此,可以通过考虑聚类质心和它们的半径来预测数据所属的聚类。

但是根据上面描述的理论,如果一个点离所有质心半径足够远,则该点有可能不属于任何一个聚类。因此,这导致了数据点类别的模糊性。
为了解决这个理论上的差距,使用径向基函数,这是 RBFNN 的最重要的部分。径向基函数(RBF)的实现使我们能够知道质心和任何数据点之间的接近率,而不管距离的范围。RBF 使用平滑过渡的圆形而不是锐截止圆来模拟数据。此外,RBF 给出了 K-means 聚类算法所不能提供的关于预测置信度的信息。

从上面的图中可以观察到,随着我们远离星团的质心,颜色的强度逐渐降低。为了具有这样的平滑过渡,可以使用距离的负幂的指数函数。通过将距离乘以标量系数,我们可以控制函数衰减的速度。所以更高的贝塔意味着更剧烈的下跌。
**
值得注意的是 Beta 是一个应该微调的超参数。但是,出于测试目的,可以尝试两种选择。
- 使用集群的标准偏差:

2.使用下面的等式:

- K —质心的数量
- Dmax — 任意两个质心之间的最大距离
欧几里得距离 D 可以通过使用勾股定理容易地找到。

从径向基函数获取输出

上面的图表显示了 RBFNN 层是如何组成的。在图中,第一层代表输入数据。第二层也称为隐藏层,是存储所有输入数据的 RBF 的地方。例如,节点 RBF1 是长度为 n 的向量,其中描述了 X ([x1,x2,…,xn]) 和 C1 (第一质心向量)的 RBF。径向基 F1 矢量是对第一质心和数据 X* 之间的距离如何相互关联的度量。*
现在我们有了聚类圆和数据点与聚类质心之间的距离度量。如果我们认为每个数字只有一个聚类,通过找到聚类和给定点之间的最高 RBF,我们可以预测其类别。但是,如果任何一个类有多个集群,会发生什么情况呢?

在下面的场景中,虽然答案是 2,但分类器得出的结果是 3。为了解决这个问题,相同类别的不同聚类以及其他聚类的效果可以线性组合。因此,生成的输出将基于所有 RBF。这里出现的困难是找到最接近 RBF 和输出之间的线性关系的W(【w1,w2,w3】)。

优化:使用最小二乘线性回归寻找权重

径向基函数神经网络的主要优点之一是利用最小二乘线性回归方程,其中获得成本函数的全局最小值相对较快且有保证。另一方面,也可以应用诸如批量梯度下降的其他优化算法来更新权重。
在数字分类问题中:
- x 是径向基函数的二维矩阵
- y 是一个热编码的二维矩阵。
最后,通过使用上面解释的理论,未知点的类别的预测可以如下获得:
- 得到关于所有质心的未知数据点 x 的 RBF 。
- 计算 RBF 和 W 的点积,选择最大值的指标
用 Python 实现理论
- 首先,我们必须定义将在 RBFNN 中使用的所需函数。修改后的“kmeans”函数返回聚类中心以及聚类的标准差。
2.为了使实现更有益,我们可以将 RBFNN 编码为一个类。
3.拟合函数:First lines 执行 k-means 来获得聚类的质心和标准偏差。然后,我们可以通过使用提到的等式,使算法对所有聚类质心使用相同的β。在接下来的几行中,我们获得输入 X 的 RBF,并应用最小二乘优化来获得适当的权重矩阵 W 。此外,为了测量模型的准确性,在最后几行中使用了测试数据。
4.将 MNIST 数据集分成训练和测试两部分,让 RBFNN 完成自己的工作。
结论
总之,RBFNN 是分类和回归任务的强大模型之一。RBF 网络可以学习使用许多 RBF 曲线来逼近底层模式。与 MLP 结构化网络相比,用于优化过程的统计方程的实践使得算法更有益且更快。但是,微调超参数,如 K — 簇数和β需要工作、时间和实践。
资源和参考资料
[1] T. Ahadli,回归简介:用 Python 进行线性回归(2018)
[2] T. Ahadli,K-Means 聚类算法的友好介绍(2020)
[3] T. Ahadli,C++/Python 使用径向基函数神经网络(2020)对 MNIST 数字数据集进行分类的代码
[4]g . Vachkov 教授,使用 RBF 网络模型进行近似和分类的多步建模(2016),智能系统中的创新问题
一个数据科学家需要多少技术技能?
使用 LDA 主题建模分析数据科学家职位列表
随着数据科学家的就业市场不断扩大,许多数据科学家的工作清单实际上是在寻找不同的技术技能。对于 2019 年 Indeed.com 上所有与数据科学相关的职位发布,有 464 种不同的技能被提及。

显然,我们没有办法掌握所有这些技能,所以了解你将寻找的数据科学家工作需要哪些特定的技术技能非常重要。
为了以数据科学的方式解决这个问题,我们对实际上每个数据科学相关工作清单中提到的技术技能进行了 LDA 主题建模,以找出哪些技术技能最有可能在工作清单中一起被提到。我们的数据集来自这个 Kaggle 数据。
# Data Preprocessing on the skill column
df=pd.read_csv('indeed_job_dataset.csv')
text_data=[]
for skills in df.Skill.dropna():
lst=skills[1:-1].split(', ')
words=[]
for item in lst:
words.append(item[1:-1])
text_data.append(words)# Topic Modelling
from gensim import corpora
dictionary = corpora.Dictionary(text_data)
corpus = [dictionary.doc2bow(text) for text in text_data]
import pickle
pickle.dump(corpus, open(‘corpus.pkl’, ‘wb’))
dictionary.save(‘dictionary.gensim’)# Setting the total number of topics to be 3 here
NUM_TOPICS = 3
ldamodel = gensim.models.ldamodel.LdaModel(corpus, num_topics = NUM_TOPICS, id2word=dictionary, passes=15)
ldamodel.save(‘model5.gensim’)
topics = ldamodel.print_topics(num_words=4)
可视化我们的主题建模结果
dictionary = gensim.corpora.Dictionary.load(‘dictionary.gensim’)
corpus = pickle.load(open(‘corpus.pkl’, ‘rb’))
lda = gensim.models.ldamodel.LdaModel.load(‘model5.gensim’)
import pyLDAvis.gensim
lda_display = pyLDAvis.gensim.prepare(lda, corpus, dictionary, sort_topics=False)
pyLDAvis.display(lda_display)
我们可以看到,当我们将主题数量设置为 3 时,第一种工作类型是更传统的数据科学家角色,雇主正在寻找 R、Python、SQL 和机器学习方面的技能。

主题 1:更传统的数据科学家角色
与此同时,第二种工作类型所需的技能略有不同——Spark、Hadoop、AWS、Scala——这显然是一个更侧重于大数据和云的数据科学家角色。

主题 2:大数据和专注于云的数据科学家
最后,对于第三种工作类型,这显然是一个更为的数据分析师角色,专注于使用 SQL、Tableau 和 SQL Server。

主题 3:精通 SQL 和 Tableau 的数据分析师
那么,我们到底需要多少技能才能获得更高的薪水呢?越多越好!嗯,也许除了如果你进入更多的管理角色,这可能是为什么这里的最高工资等级实际上看到所需技能数量的减少。

在执行了主题建模之后,有趣的是,尽管 Python 似乎是这个行业中更占主导地位的语言,但在最近的许多数据科学家职位发布中,仍然非常需要 R。我们怀疑这可能是因为许多清单将 R 和 Python 都放在技能部分,但实际上需要 R/Python。
尽管如此,我们认为在 R 和 Python 之间做一个比较可能会很有趣,看看了解 Python 而不是 R 是否会给你带来任何薪水提升。


我们可以看到,相比于懂 R,懂 Python 确实让你在落地更高工资上更有优势。
希望这可以帮助你的求职过程一路走来更容易一点,愿我们所有的数据梦想成真✨
这是三藩市大学 MSDS 593 的一个小组项目,小组成员是刘品言和刘旭。请随意查看 GitHub repo 中我们的预处理和可视化代码。
参考:
[1]李,苏珊,Python 中的主题建模与潜在狄利克雷分配(LDA)(2018),https://towardsdatascience . com/Topic-Modeling-and-Latent-Dirichlet-Allocation-in-Python-9bf 156893 c24
最重要的 IT 辅助技能,正则表达式
正则表达式是众所周知的 IT 技能,它可以极大地提高你在计算机上做任何事情的效率!

作者图片
当我第一次学习如何使用 Regex 搜索文本并以我以前无法想象的方式处理文本数据时,我感到非常震惊。今天,我们可以在 Python 中利用它的能力,但它在所有其他编程语言、文本编辑器和 ide 中都是一样的。我们学习基础知识的速度会比特朗普发推特还快!
那么什么是 Regex 呢?
Regex 是正则表达式的缩写。含义:它是一个短的字符序列,用于搜索或替换长文本中的单词。它是一种编程语言的次要形式,你可以用它来立即找到你需要的东西。我们将使用一条奇怪的小特朗普推文来学习基础知识。
我们将使用的示例推文
基础
让我们加载 Python,一起浏览一下基础知识。首先,我们必须加载模块 re,它处理 python 内部所有与 regex 相关的事情。让我们把文字删减一点
import re
text='When you see the Drug Companies taking massive television ads against me, forget what they say (which is false), YOU KNOW THAT DRUG'
让我们练习一些魔法吧!我们现在可以要求 Regex 查找所有内容,直到一个单词第一次出现。
regex_pattern = r'.*Drug'
res = re.findall(regex_pattern,text)
print(res)
这将打印“‘当您看到药物时’”。
那么这是怎么回事呢?
首先,你可能会注意到我们是如何用前缀r’定义字符串的,这个 r 向 python 表明我们将要使用 Regex。也称为原始字符串,这不会改变字符串的类型。它主要改变了字符串的解释方式。这并不总是必要的,但它是最佳实践。
接下来的事情就是’。*药物'部分。这包括两个想法。首先,我们有零件。* '这向 Regex 表明,我们需要所有可能的字母或符号…… 直到我们看到药物。
所以基本上。* '就是所谓的通配符通配符。其中' * '表示我们要将星号之前的所有内容重复 0 次或更多次。在哪里?是超级特殊的符号,告诉星星我们想要一切,包括字母、数字和空格。
我们再试一次
regex_pattern = r'W.*ss'
res = re.findall(regex_pattern,text)
print(res)
这将打印“‘当你看到制药公司服用大众’”。从第一次出现的“W”到第一次出现的“ss”
贪婪
regex_pattern = r' .*? '
res = re.findall(regex_pattern,text)
print(res)
会打印“‘你’,‘那个’,‘公司’,‘大规模’…”。正如我们所看到的,这次我们想找到两个空间之间的所有东西。我希望你的头上现在有一个大大的问号。我们把它放在那里是为了确保正则表达式以所谓的贪婪方式匹配。贪婪在这里意味着我们只匹配绝对最小值,直到下一个空格。没有了“?只有使用' *,'我们才能匹配所有内容,包括空格,因此结果将是整个 tweet(没有最后一个和第一个单词)
取代邪恶
regex_pattern = r'drug'
res = re.sub(regex_pattern,'COVID-19',text, flags=re.IGNORECASE)
print(res)
我将刊登“当你看到新冠肺炎公司在电视上做大量广告反对我时,忘记他们说了什么(这是假的),你知道新冠肺炎”。
这是 Regex 最有用的部分之一,你可以用任何东西替换任何东西!呼叫响应(..)采用我们想要替换的模式,并用更合适的单词替换它。我们必须添加' flags=re。“IGNORECASE”以确保我们符合特朗普不区分大小写的推文风格;)
释放北海巨妖
regex_pattern = r'D(.*),'
res = re.sub(regex_pattern,'Kraken',text, flags=re.IGNORECASE)
print(res)
会印上“当你看到北海巨妖时,你就知道那是毒品。”再次说明我们所学到的。
匹配我们所知道的
regex_pattern = r'see( .*? )*?THAT'
res = re.sub(regex_pattern,'\\1',text)
print(res+"S")
上面印着“当你知道毒品”好了,现在我们进入了严肃的正则表达式领域。方括号“()”构成一个匹配组。这是一种引用后来在 replace 语句中发现的内容的方式。我们还嵌套了“?”围绕着它。所以第一个”(。?)"获取单词将" * "放在匹配的单词周围会获取" see "和" THAT "之间的所有单词。我们为什么要这样做?因此,我们可以在以后引用它,正如我们在 replace 语句中看到的那样' \1 '获取第一个单词并将其放入找到的字符串中。这个例子中的第一个词是“你”这让我们“当你知道毒品。”
结论
这些是正则表达式的基本构建块,在你精通正则表达式的过程中,你会发现更多。这些基本技术帮助你理解什么是可能的。我希望你现在能更多地考虑正则表达式。首先,你可能需要投入一些时间。一旦你知道了,一个全新的字符串操作世界将会开启。
如果你喜欢这篇文章,我会很高兴在 Twitter 或 LinkedIn 上联系你。
一定要看看我的 YouTube 频道,我每周都会在那里发布新视频。
数据分析师最需要的技术技能
雇主在数据分析师中寻找哪些技术技能?它们与数据工程师和数据科学家所需的技能相比如何?📊
我分析了 2020 年 1 月以来的在线工作清单,以寻找答案。在本文中,我将分享答案,并为有志成为数据分析师的人提供一条建议的学习路径。🚀

这是我的第三篇文章,着眼于数据密集型岗位的技术技能。参见之前关于数据科学家这里和数据工程师这里的文章。😀
话不多说,以下是截至 2020 年 1 月数据分析师职位列表中的 10 大技术。

数据分析师角色
数据分析师将数据转化为信息。它们通过为决策者提供可操作的数据来发挥重要作用。👍
数据分析师通常获取数据工程师提供的数据,对其进行分析,并提出建议。他们创建可视化效果,在仪表板和演示中显示他们的发现。📈
与数据科学家不同,数据分析师通常不会基于机器学习算法创建预测模型。
方法
我从简单雇佣、事实上和怪物那里搜集信息,看看在美国的工作列表中哪些关键词出现了“数据分析师”。我使用了请求和美汤 Python 库。



我使用了从我对数据科学家和数据工程师工作列表的分析中收集到的相同关键词。LinkedIn 没有被搜索到,因为它之前在抓取了它的网站后将我锁定在我的账户之外。☹️
对于每个求职网站,我计算了每个关键词出现在该网站上的数据分析师职位列表总数的百分比。然后我计算了三个网站上每个关键词的平均百分比。
结果
这是一张展开的图表,展示了 30 种最常见的技术。

这是表格形式的相同数据。

搜索了许多其他技术关键词;这是 30 个最高分。让我们看看最常见的技术。
讨论

SQL 是结构化查询语言的简称。它出现在超过一半的列表中。SQL 用于处理关系数据库。SQL 有多种风格,包括 MySQL、Oracle、SQL Server、PostgreSQL 和 SQLite。每个版本共享大部分相同的核心 API。有高质量的免费产品。

Excel 几乎和 SQL 一样普遍。这是占主导地位的电子表格程序。它是微软 Office 365 软件工具套件的一部分。尽管 Excel 不能像 SQL 数据库那样处理大量数据,但它非常适合快速进行分析。 Google Sheets 是一个拥有免费版本和相似核心功能的竞争对手。

Tableau 出现在大约四分之一的列表中。拖放式商业智能软件使创建可视化和仪表板变得容易。Tableau 的可视化能力远胜于 Excel。Tableau 有一个免费的公开版本,但如果你想让数据保密,你需要支付一些钱。

Python 出现在大约四分之一的列表中。它是一种非常流行的免费开源编程语言,用于处理数据、网站和脚本。它是机器学习的主要语言。🐍

超过 20%的列表中有 R字样。这是一种流行的免费开源统计语言,尤其是在学术界。
与数据工程师和数据科学家的比较
在之前的文章中,我研究了数据科学家和数据工程师最需要的技术技能。数据分析师的结果如何比较?
数据分析师的列表总数为 16,325,数据工程师的列表总数为 12,013,数据科学家的列表总数为 9,396。所以数据分析师的工作相对来说很常见。😀
下图显示了 10 种最常见的数据分析列表技术。数据科学家和数据工程师列表的分数也会针对每个关键字显示出来。

几个亮点:
- SQL 对于这三个工作岗位都很受欢迎。
- Excel 在数据分析师列表中的常见程度是数据科学家和数据工程师列表的四倍多。
- Python 虽然出现在大约四分之一的数据分析师列表中,但在数据科学家和数据工程师工作列表中的受欢迎程度大约是它的三倍。
- r 在数据分析师和数据工程师职位中比在数据科学家职位列表中更不常见。
- PowerPoint 在数据分析师列表中更常见。
就趋势而言,我对数据科学家工作清单的分析显示,从 2018 年到 2019 年,R 和 sa 的受欢迎程度都大幅下降。
值得注意的是,数据工程师职位列表中提到的技术比数据分析师职位列表中提到的多得多。
建议
如果你想成为一名数据分析师,或者让自己更有市场,我建议你学习以下技术,按优先顺序排列。
学习 Excel 。拿起来比 SQL 快。我从学校和工作中知道了 Excel,但我通过学习 Microsoft Excel 认证考试更好地了解了它。如果你是一名自学者,官方认证或 MOOC 课程可能值得你花时间。
学习 SQL 。如果你不知道一种编程语言,这是一个很好的第一次学习。我那本令人难忘的 SQL 书籍在预发布版中有售,请点击。
学习 Tableau 。Tableau 允许你用一个拖放界面快速的制作出很棒的可视化效果。点击这里查看我的文章来学习基础知识。
展示你可以制作一个 PowerPoint 演示。有很多 MOOCs 是教微软 PowerPoint 的。
如果你已经证明你可以使用上述技术来分析和沟通,你应该是许多入门级职位的体面候选人。👍
如果你知道以上技能,又想学习新的技能或者获得更多职位的资格,建议你学习 Python 。如果你想进入数据工程和数据科学领域,Python 是很棒的。😀
我的令人难忘的 Python 书是为初学者设计的。这本书有 Kindle 版本,也有来自亚马逊和亚马逊的印刷版。epub 和。pdf 格式此处。
一旦你知道了基本的 Python,学习 熊猫 。Pandas 是一个处理数据的 Python 库。如果你正在寻找一份需要 Python 的数据工作,你可能也会被期望拥有熊猫技能。

我正在完成一本介绍熊猫的书,所以加入我的数据令人敬畏的时事通讯来确保你不会错过它。🚀
这听起来可能是一个很长的清单,但是一步一步来。🦶认识到这需要时间,而且会有起伏。你能做到——只要坚持!😀
我建议你学习和展示你的技能,做一些你可以放在个人文件夹里的项目。然后你就可以通过做自己喜欢的事情来学习了!你也将有一份工作向潜在雇主展示你的技能。关于建立作品集网站的快速指南,请参见本文。
包装
我希望这篇数据分析师最需要的技术指南对你有用。如果你有,请在你最喜欢的社交媒体上分享,这样其他人也可以找到它。👍
我撰写关于 Python、数据科学和其他技术主题的文章。如果你对此感兴趣,在 Medium 上关注我,在这里阅读更多。📊
快乐分析!📈
数据工程师最需要的技术技能
他们与数据科学家的技术技能相比如何?
根据 2019 年的一项分析,数据工程师是增长最快的职位。数据工程师通过创建和维护用于注入、转换和存储数据的管道和数据库,在组织中扮演着至关重要的角色。
数据工程师最需要哪些技术技能?它们与数据科学家最需要的技术技能相比如何?请继续阅读,寻找答案!😀

我分析了 2020 年 1 月数据工程师的工作清单,以了解哪些技术技能最受欢迎。我将结果与数据科学家的工作列表进行了比较,发现了一些有趣的差异。🔍
事不宜迟,以下是截至 2020 年 1 月数据工程师工作清单中的 10 大技术。

让我们跳进来吧!🚀
数据工程师角色
数据工程师在当今的组织中扮演着重要的角色。他们负责存储数据并使数据可供他人使用。
数据工程师建立管道从许多来源注入流和批处理数据。然后,管道执行提取、转换和加载(ETL)过程,以使数据更加可用。然后,这些数据可供数据科学家和数据分析师进行进一步处理。最终,数据会进入仪表盘、报告和机器学习模型。📊
我搜索数据来确定 2020 年数据工程师最需要哪些技术。
方法
我从简单雇佣、事实上和怪物那里搜集信息,看看在美国的工作列表中哪些关键词出现了“数据工程师”。我使用了请求和美汤 Python 库。



我在分析数据科学家职位列表和阅读数据工程师职位列表时加入了关键词。LinkedIn 没有被搜索到,因为之前在抓取后我被锁定在我的帐户之外。☹️
对于每个求职网站,我计算了每个关键词出现在该网站上的数据工程师职位列表的百分比。然后我计算了三个网站上每个关键词的平均百分比。
结果
以下是工作列表搜索结果中得分最高的 30 个数据工程师技术术语。

以下是表格形式的相同百分比数据。

我们来分析一下。🎸
讨论


SQL 和 Python 都出现在超过三分之二的工作列表中。它们是最值得了解的两项技术。💡
Python 是一种非常流行的处理数据、网站和脚本的编程语言。🐍
SQL 代表结构化查询语言。SQL 是由一系列语言实现的标准,用于从关系数据库中获取数据。它已经存在了很长时间,并显示出它的弹性。

Spark 出现在大约一半的列表中。 Apache Spark 是“一个用于大数据处理的统一分析引擎,内置流、SQL、机器学习和图形处理模块”。它在非常大的数据集上特别受欢迎。

AWS 出现在大约 45%的列表中。AWS 是亚马逊的云计算平台。它拥有所有云平台中最大的市场份额。
接下来是 Java 和 Hadoop,各占 40%多一点。
Java 是一种常用的、久经考验的语言,在 Stack Overflow 的 2019 年开发者调查中排名第十。相比之下,Python 是第二受欢迎的语言。甲骨文控制着 Java,从 2020 年 1 月起,这个网站的主页会告诉你所有你需要了解的东西。😉
就像时光机一样。
Apache Hadoop 使用 MapReduce 编程模型和服务器集群来处理大数据。MapReduce 模型正在失宠。
接下来是 Hive、Scala、Kafka 和 NoSQL,它们都出现在大约四分之一的数据工程师列表中。
Apache Hive 是一个数据仓库软件,它“使用 SQL 来方便读取、写入和管理驻留在分布式存储中的大型数据集”。
Scala 是大数据流行的编程语言。Spark 是用 Scala 构建的。在 Stack Overflow 的 2019 年开发者调查结果中,Scala 是第 11 个最可怕的语言。
Apache Kafka 是一个分布式流媒体平台。它在注入流数据方面非常流行。
NoSQL 数据库站在 SQL 的对立面。NoSQL 数据库是非关系的、非结构化的,并且是水平可伸缩的。NoSQL 非常受欢迎,但是之前关于它将取代 SQL 成为主流存储模式的宣传似乎言过其实了。
与数据科学家术语的比较
以下是 30 大数据科学家工作列表技术术语,通过与数据工程师术语相同的方法得出。

就总列表而言,数据科学家列表比数据工程师列表多约 28%(12,013 对 9,396)。
让我们看看哪些术语在数据工程师列表中比在数据科学家列表中更常见。
对数据工程师来说更常见
下图显示了平均差异大于 10%且小于-10%的关键词。

AWS 的增幅最大,数据工程师的数量比数据科学家多 25%。它出现在大约 45%的数据工程师列表和大约 20%的数据科学家列表中。那是相当不同的!
这是同一数据的另一个视图,并列显示了数据工程师和数据科学家的工作列表结果:

Spark 显示了第二大增长。数据工程师经常和大数据打交道。
卡夫卡也增长了 20%。这大约是数据科学家列表百分比的四倍。注入数据是数据工程师的核心工作。
Java、NoSQL、Redshift、SQL 和 Hadoop 出现在数据工程师列表中的次数增加了 15%。
对数据工程师来说不太常见
现在让我们看看哪些技能在数据工程师工作清单中不太受欢迎。
从数据科学家到数据工程师的下降幅度最大。它出现在大约 17%的列表中,而不是大约 56%。哇哦。r 是一种受学者和统计学家欢迎的编程语言。这是堆栈溢出调查受访者的第八个最可怕的语言。
SAS 在数据工程师列表中也很少出现,大约有 14%的差异。SAS 是统计和数据的专有语言。有趣的是,我最近对数据科学家工作列表的分析显示,SAS 比任何其他技术都下降得多。
对数据工程师和数据科学家都很重要
值得注意的是,十大技术中有八项是在数据科学家和数据工程师职位列表中共享的。SQL、Python、Spark、AWS、Java、Hadoop、Hive 和 Scala 都在前 10 名之列。
以下是 15 个最常见的数据工程师术语,以及它们在数据科学家列表中的流行程度。

建议
如果你想成为一名数据工程师,我建议你学习以下技术,大致按优先顺序排列。
学习 SQL。我建议你学习 PostgreSQL,因为它是开源的,受欢迎的,并且还在增长。我那本令人难忘的 SQL 书向您展示了如何使用 PostgreSQL,在预发布版这里有售。
学习香草 Python。我的令人难忘的 Python 书籍是为 Python 新手设计的。该书有 Kindle 版本,也有来自亚马逊和亚马逊网站的印刷版。epub 和。pdf 格式此处。
一旦你了解了基本的 Python,学习 pandas,一个用于清理和操作数据的 Python 库。如果你正在寻找一份需要 Python 的数据工作,而且大多数人都是这样,你可以期待公司也希望你有熊猫技能。我正在完成一本介绍熊猫的书,所以加入我的数据令人敬畏的时事通讯来确保你不会错过它。👍

熊猫零食
学习 AWS。如果你想成为一名数据工程师,你需要一个云平台,而 AWS 是最受欢迎的。我发现 Linux Academy 在线课程在学习Google Cloud Data Engineering技能时很有帮助,并期待它们对 AWS 有所帮助。
如果你知道所有这些技术,并希望成为更受欢迎的数据工程师,我建议你学习 Apache Spark for big data。尽管我对数据科学家职位列表的研究显示它的受欢迎程度正在下降,但它仍然占据了所有数据工程师职位列表的近一半。

火花
包装
我希望这篇数据工程师最需要的技术指南对你有用。如果你有,请在你最喜欢的社交媒体上分享,这样其他人也可以找到它。👍
如果您想了解这些术语与数据分析师术语相比有何不同,请点击此处查看我的文章。
我为 Python、Docker、数据科学和其他技术主题创建学习资源。如果你对此感兴趣,请关注我,在这里阅读更多。😀
数据工程快乐!🏗
数据专业人员最赚钱的国家
为什么理解标准误差对避免犯影响职业生涯的错误至关重要

简·戈特维斯在 Unsplash 上的照片
数据世界也在以指数速度不断膨胀。每天都有新的工作广告,各行各业都在创造新的角色。随着越来越多的企业熟悉这个领域,对数据专业人员的需求也在不断增加。
在完成我的数据科学学位后(像许多人一样),我问自己下一步该去哪里?我很幸运处在一个全世界都需要工作的领域。在进行职业转移时,定性原因(如工作与生活的平衡)是必不可少的,但有一个定量原因也是决定性因素,即 工资 。这篇文章试图揭示是什么因素导致了更高的薪水,以及哪个国家为数据专业人员支付了更高的薪水。
然而,本文的技术目标是让读者了解 忽略样本量 的危险,以及它如何影响统计变异。
数据专业人员是在数据分析、工程和科学领域工作的员工。本文中使用的数据收集了以下职位的专业人士的薪资信息:
- 数据库管理员
- 数据分析师
- 数据科学家
- 软件工程师/开发人员
- 数据架构师
- 经理
- 系统管理员
在分析了三年来(2017、2018 和 2019 年)在 85 个不同国家超过6500 名数据专业人员的调查结果后,数据中有归因于个人工资的具体特征。
1.教育
“学习不是强制性的;生存也不是”
爱德华兹·戴明博士
拥有学位并不一定意味着高薪。我个人的信念是,学习是你在大学/工作之外学习时发生的事情。下面给出的是从数据中得出的图表,可以说它没有显示出资格和工资之间的关系。

工资与资格的关系。按作者分类的图表
色标表示具有该资格的个人的平均经验年限。如图所示,拥有博士学位的数据专业人员平均薪酬最高,但需要注意的是,他们的经验也最多。在职学习对提高你的技术专长至关重要。因此,没有任何资格证书的人在平均工资方面仍然更好,因为他们倾向于积累更多的工作经验。人们可以得出这样的结论:学士学位可能会带来更高的报酬。然而,重要的是要意识到特征之间的其他几个相互作用(如公司规模、行业、位置等。).
2.经验
“经验是万物之师。”
—朱利叶斯·凯撒
人们几乎很自然地认为工作经验与薪水有着积极的关系。专业人士在工作中花的时间越多,员工就会变得越熟练。随着他们与同事建立工作关系,他们可能会变得更有效率,工作质量也会提高。

工资随经验变化的箱线图。按作者分类的图表
该图进一步支持了先前提出的假设。正如你所观察到的,平均工资随着工作年限的增加而单调增加。
注意:所有工资都以美元给出,由于极端异常值,数据集仅限于 20 万美元以下的工资
3.曝光度(职位)
作为一名数据专业人员,我们期望每个人都能帮助他们的组织使用数据来解决三个关键挑战中的至少一个:
- 统计模型——利用您的专业知识构建/开发和测试算法和数学模型
- 开发管道——作为开发人员/程序员/工程师,你的工作是扩展这些模型并将其部署到生产中。
- 业务事务专家—您的角色是了解来自 it 的数据和信息如何应用于运营。
管道的所有方面就像机器中的齿轮。每个齿轮必须彼此同步工作,系统才能正常工作。理想情况下,这个结构中的每一个成员都应该有相同的薪酬水平,因为一个人并不比另一个人更重要。不幸的是,我们的现实与理想相去甚远,取决于需求;特定的角色有额外的好处。

不同职位的平均工资,偏差以误差线表示。按作者分类的图表
平均工资显示数据架构师和经理是最富有的。跨数据集的分析师之间的差异是最大的。这种显著的偏差可能是因为分析师的职称定义不严格,并且适用于多个角色。色标代表了多年的经验,为我们解读结果提供了额外的视角。毫不奇怪,要成为数据架构师,您需要丰富的经验,上面的数据也表明了这一点。
不久前,数据科学家还被贴上了 21 世纪“最性感”工作的标签。从上面的结果可以明显看出这样做的好处。尽管经验较少,但他们往往比开发人员、工程师和数据库管理员做得更好。
4.位置
最后,分析引出了本文的主要目标— 试图揭示数据专业人员的最佳机会。
这个问题的答案看起来很简单,类似于不同变量(教育、经验、曝光度)的薪资总和;可以对国家进行同样的分析。

按国家显示数据专业人员平均工资的表格
上表总结了收集数据最多的前 10 个国家的信息。美国的样本量最大,为 4509 人,然后是英国的 667 人(很快就会明白为什么这一点很重要)。
从平均工资信息中,我们可以看到美国是数据专业人员薪酬最高的国家,其次是新西兰。对我来说,在澳大利亚为我的跨塔斯曼邻居工作可能会给自己带来伤害。
德莫维尔方程
人口的真实统计数字永远不会为人所知。从人口中的每个人那里获得准确的信息几乎是不可能的(太昂贵了)。直觉上,您收集的人口数据点越多,其结果的可信度就越高。重要的是要认识到样本大小如何影响这个置信区间,以及有时我们是如何危险地无知。

说明人口和样本大小之间的差异
当观察值回归到群体的平均值时,群体的真实值估计得最好。德·莫维尔方程为我们提供了均值的抽样分布的标准差。
简单来说,随着样本量的增加,样本内的变异性开始向真实均值收敛。为了说明这种影响,我随机对数据集进行了抽样,以计算每次观察的平均工资。美国和澳大利亚的结果如下。

通过平均多个实现观察到的漏斗效应(美国)。按作者分类的图表

通过平均多个实现(AUS)观察到的漏斗效应。按作者分类的图表
随着样本量的增加,观察值开始回归平均值。当估计人口的统计数字时,我们必须考虑标准误差。

德莫维尔方程
平均值的标准误差通过将样本的标准偏差除以样本的大小来给出。
对这一等式的无知导致盖茨基金会在选定的学区花费了超过 10 亿美元来提高学术和有效的领导能力,而不是打破教室规模——实际上,这只是增加了差异。
说明平均值的标准误差

考虑标准误差的平均工资。按作者分类的图表

此表按国家/地区显示数据专业人员平均薪金的标准误差
有了标准误差的额外视角,判断就变得不那么明显了。
- 例如,我可以说在美国一名数据专家的平均工资是107,517 美元。尽管如此,我相信人口的真实平均值会在106,665 美元到108,369 美元之间(即在其标准误差范围内)。
- 标准误差与样本的标准偏差成正比,因此德国、新西兰和印度等国家的标准误差非常高。然而,像瑞典和加拿大这样的国家尽管样本量小,但标准误差却较低。
- 尽管瑞典和荷兰的样本量相似,但由于数据的可变性,荷兰的标准误差要高得多。
- 该误差与样本量成反比,因此即使美国和英国在样本中具有高标准偏差,由于样本量大,它们的标准误差相对较低。
人类没有完全理解变异的影响,尤其是差异变异,这并不是一个新的发现。回到这个问题,我问自己在澳大利亚工作比在新西兰工作好。我更愿意根据澳大利亚相对较低的标准误差做出决定,而不是新西兰较高的平均工资。从 2017 年到 2019 年收集的数据可能会显示新西兰的表现更好,但在接下来的几年里却下滑到平均水平。
多少数据就够了
这个问题的答案就像分析学中很多问题的答案;它取决于。你越想确定你的均值,你的样本就需要越大。它完全与你试图解决的问题的背景相关。
通过计算不同样本大小的标准误差,可以生成一个图来建议需要收集多少数据点。

为美国和德国生成的信心图。按作者分类的图表
很明显,对于均值 90%的置信度,30 的样本量对于美国就足够了,然而,由于德国的高度可变性,我们需要将近 90 个数据点。
结论
个人认为,从数据中得出的最终推论并没有改变,“对数据专业人士来说,美国是最赚钱的国家”。然而,应用我对标准误差的理解,我并不是不知道数据集中潜在的可变性。这种见解与当前的分析环境相一致,美国在分析之旅中远远领先于大多数其他国家。它拥有资金最高的大学,大多数科技巨头都位于此。由于市场和需求如此之大,数据专业人员在那里的价值是最高的,这是显而易见的。**
GitHub 链接,谢谢
谢谢你一直读到最后。我希望这篇文章能让你在下一次统计数据呈现给你的时候质疑样本量。对于所有在这个领域寻找工作机会的人,我祝你们一切顺利!
请随时在 LinkedIn上与我联系,一如既往,对此的任何反馈都将不胜感激。
所有的图表和代码都可以在我的 GitHub 资源库中找到,请随意下载并分析您的用例信息。
说明 2019,2018,& 2017 数据专业薪资调查结果汇总数据库管理员有多少…
github.com](https://github.com/wiredtoserve/datascience/tree/master/DataProfessionals)** 
照片由普里西拉·杜·普里兹在 Unsplash 上拍摄
最受欢迎的数据科学和机器学习课程— 2020 年

由 Unsplash 上的 SkillScouter 拍摄的照片
该列表最后一次更新是在 2020 年,并将定期更新以保持其相关性
有很多关于数据科学和机器学习的在线资源和网站,人们可以利用它们来学习新的东西或学习现有的技能。正如他们所说,互联网时代使得在旅途中获取信息变得极其容易。
在科技领域,最难做的事情之一就是颠覆自己
马特·莫楞威格
我在 LinkedIn 上无数次收到关于同一个问题的信息和连接请求。它总是像这样,
"学数据科学和机器学习到底要怎么做……要学哪些课程…..基本上如何开始?”。
这篇文章的目的是回答这个问题,同时给读者一个目前在这个领域最受欢迎的课程列表。
下面您将看到在线课程,这些课程将帮助您加快在数据科学领域的发展。但是要知道,看视频只是给你一个 ML 理事会的席位,如果你明白我的意思,它不会授予你 ML 大师的等级,因为你将不得不处理实际的现实世界的问题,并接触数据。

作者照片来自 SWU
不过我可以说的是,这些课程将带你到那个水平,从那里你将有足够的能力去弄清楚你下一步应该做什么。现在只是开始的问题!
这时你会发现这张表很有帮助。这些课程向您教授从基础到高级主题的所有内容,建议您在开始编写代码之前,不要着急,至少要正确理解基本概念——我相信这是您一开始就想做的事情!
最受欢迎的课程— 2020 年
按照我自己的想法,这些课程按照复杂程度排列。别担心,这不是任何形式的付费广告。我与任何课程提供者都没有关系!
完成以上列表中的每一门课程并不是强制性的,因为列表涵盖了最受欢迎的课程,而不是最必要的课程。我上大多数课程的方式是从一门课程开始,如果我被困在某个话题上,或者如果我想更好地理解某个东西,我会转向另一门课程,或者一些博客帖子,或者 YouTube 视频,甚至图书馆!
这些课程更侧重于获取机器学习、数据科学和 Python 编程方面的知识,而不是一个更全面的画面,该画面更加结构化,还包含软件开发和 SQL 基础知识。
如果您需要完整的数据科学学习路径,我很乐意帮助您——请访问数据科学门户网站,在那里您可以找到最全面的数据科学学习路径。从线性代数开始,到数据预处理,一直到机器学习和人工智能专业主题的核心概念。
让我首先说明为什么有这样一个学习途径是必要的,为什么它将被证明是非常…
thedatascienceportal.com](https://thedatascienceportal.com/tools-and-more/the-most-comprehensive-data-science-learning-path-2020/)
最初发表于T5【https://thedatascienceportal.com】。欲了解更多此类内容,请访问数据科学门户网站。
大家好,欢迎来到数据科学门户。这个空间致力于学习和理解所有令人兴奋的…
thedatascienceportal.com](https://thedatascienceportal.com/)**
作为数据科学家,最常用的词是“洞察力”!
是什么让你的数据富有洞察力?你如何给自己的见解排名?

听到 洞察 这个词,你脑海中浮现的第一个词是什么?作为一名数据科学家,当我从我的数据中收集一些有用的信息时,我会提到 insight 这个词。我相信数据、信息和洞察力是有秩序可循的。
将这三个词放在一起看,脑海中浮现出一个著名的层次结构:

导致洞察力的等级制度
数据是关于特定主题的原始上下文。信息是关注该主题的一种更加结构化的方式,它已经使用数据进行了过滤和聚合。洞察力基本上是在你对数据进行充分分析并得出特定结论后获得的。可操作的见解通常是那些你的团队准备好采取一些行动来帮助公司的透彻的见解。
我密切关注的步骤,以充分利用我的数据—
了解你的数据—
获得可操作洞察的主要步骤是您正在处理的数据的 质量 。它可以是结构化的,也可以是非结构化的。您应该经常密切关注您的目标和您正在处理的数据的一致性。您的数据应该是真实世界场景的良好近似。在开始你的数据探索之前,做一个 清单 列出你期望在你的特定问题表述中看到的所有东西。
例如:寻找不完整的数据、缺失的值、数据的分布、您期望在特征之间看到的明显和直观的相关性、数据的结构等。
了解你的目标—
必须有一个理由来解释为什么你必须处理你的数据。没有明确的目标,就不会得到明确的答案。密切关注你想要的结果,不要因此失去注意力。可能还有许多其他功能对其他任务更重要,但你需要关注的是你的 期望任务 。了解你的冲突池中的核心冲突,并计划解决它。采取额外措施解决冲突。
例如:如果你的主要关注点是基于当前形势和他们如何处理的各种州长的推文,那么更多地关注州长们基于#Covid19、#Coronavirus 的标签,而忽略其他消费者的个人推文,即使他们基于相同的标签追随特定的趋势。
如果你没有目标怎么办?如果你只是被分配了一项任务,并被要求拿出一些重要的东西来讨论呢?
有时你有数据,但你不知道如何使用它。你心中没有特定的目标。你没有想要解决的特定问题。你是如何进行的?
I 通过关联各种特征、使用热图、收集关于各种特征的信息、将它们分解成片段、直观地假设目标变量列表并继续数据探索,特别注意数据中的某些趋势。如果说不通呢?我会用其他的目标变量重复这个过程。
知道哪些洞见已经被处理过了—
当你面前有数百万个数据点时,很容易迷失方向。因此,最好记下您的团队成员已经处理过或正在发现的其他有见地的数据。还有,不要忘记[第二点](#Know your Target Variable)到 避免处理不那么重要的见解 。
了解数据的背景——
如果你熟悉上下文,那么处理想要的结果会变得容易一些。凭借 直觉判断 ,你可能会在数据上占据上风,而且通常情况下,你的结果会与预期结果一致。
例如:如果你根据宏观经济参数预测贷款结果,如果你知道宏观经济参数如何与贷款违约率相关联,这将是有帮助的。
知道如何传达这种洞察力—
如何让你的数据易于解读?大多数时候,你需要向没有数据科学/统计知识的不同团队传达你的洞察力。视觉是解读数据的最佳方式。它遵循透明性、可解释性和简单性。有一个很好的机会,你试图收集的洞察力是基于其他团队提出的问题,以帮助公司赢得客户,实现利润最大化,营销活动等。有时候,我们给出更多的信息,而不是见解。我们需要对我们最关键的观点进行排序,并传达出来。
最后一点,这一切都归结于你对你试图解决的问题的 知识 。它提高了你的工作质量。我想说,即使你从你的数据中发现了一个关键的见解,它也会让你的数据变得有见地。
所有机器学习之母(MoAML)
帮助机器智能成长为与企业高度相关的产品

弗兰·霍根在 Unsplash 上的照片
阿.跋
- 本帖中使用的“ 【分析】 , AI ”与“ 机器智能 ”有重叠。“分析”指的是“大数据”前的分析,与“人工智能”或“人工智能”相比,后者在更一般的意义上使用。“机器智能”被用来代替曾经流行但令人困惑的“ML/AI”缩写。
- 数据科学 ”不能与“人工智能”互换使用,而是作为研究计算来交付新的人工智能方法或用途。
- 本帖表达的所有观点 均为 个人 。
B.背景
如果我们把费希尔 1922 年对回归领域的贡献看作是分析学的一个随意但方便的诞生,我们就站在几乎连续的分析学创新的世纪标记前(参见,例如, 费希尔关于回归)。这一创新极大地改变了企业利用自身数据以及外部市场数据的方式,以尝试并推动改善业务成果和竞争地位。
最近的“大数据”革命通过 3 个相关事件的完美风暴,进一步加快了分析创新的步伐:(1) 智能 quants anywhere 的 web 访问;(2)容易可用性有用有用数量真实数据;(3) 基于云的 存储和计算能力以超* 低成本。***
不幸的是,这场革命并没有让各种类型的“分析”(为了清楚起见,从这里开始称为“人工智能”)均匀受益。它给反射式人工智能和交互式人工智能(定义如下)带来了创新,这主要是由于多产的“数字原生”研究公司的敏锐关注,如苹果、谷歌、脸书、亚马逊、易贝等。
这些聪明、精力充沛、掌握数据的公司在正确的时间和地点迅速推进了这场革命。他们擅长利用其数据环境的特殊性质。他们能够利用从少量自动化和一致流程中获得的数字、专有和集中式数据;对于传统的实体公司来说,这是一种奢侈,也是一个梦想。
这种不平衡革命的结果是,通过扩展人工智能计划的类型,推动企业价值增长的绿地机会仍然存在。企业可以利用人工智能计划的完整补充,通过声称的估计以及对预期增值的热烈讨论,了解每种类型的投资和优先级。
C.企业中的机器智能
有四(4)种主要的人工智能类型,我称之为自反、互动、深思熟虑和战略。前三(3)个是面向事务的和直观的,而战略 人工智能支持完全不同的愿望。它帮助总经理们(从这里开始称为“GM”)平衡增长、盈利能力和客户、满意度,同时与公司目标、理念、风险偏好和其他紧急情况保持一致。
企业人工智能程序过度利用反射式人工智能( RPA +人工智能用于后台办公自动化)和交互式人工智能(聊天机器人+ Q & A 机器 实现自助服务)计划。他们无法从深思熟虑的或战略性的人工智能中收获全部价值,即使这些有时更容易执行。我们不应该指望人工智能在这些解决方案中“冷静”,但它们肯定可以提高决策质量和买入以交付更好的业务成果。
今天,企业战略和政策继续主要由“直觉”和可怕的非正式过程驱动。现有流程松散地执行问责制,并且未能一致地传播关于糟糕决策及其连锁效应的知识。由于战略人工智能对战略流程的广泛影响,与其他类型相比,战略人工智能可能对推动企业价值产生最大的影响。
下面的插图提供了各种人工智能类型预期的相对贡献的概念,每美元投资的(单个方框的大小反映了交付的相对价值,但我承认这可能不适用于所有企业)。**

各种人工智能类型的预期相对贡献(Rajesh Iyer 的原始插图)
在这篇文章的剩余部分,我们将关注战略人工智能。使能技术很容易获得,短期的前期投资足以激活战略性人工智能计划。这一举措将帮助 GMs 研究各种战略方案和相关的财务影响,同时决定的最佳战略策略,以推动利润增长并主导其市场。
D.与战略机器智能竞争
在我最近的文章机器智能和企业中,我写了关于销售关于 ML 驱动的洞察力的价值的 GMs 的挑战。我提到了这个问题的解决方案,它基于将定制的 ML 模型捆绑到一个统一且有说服力的业务(“所有 ML 之母”)视图中。我提议建立一个“通用工作台”,作为一个平台,提供这样一个统一的视角来分析和选择制胜策略。
在这个 GM 工作台中,所有模型在事务级别同时运行,结果汇总为投资组合视图的收入和利润。此外,他们可以跟踪当前和各种提议的场景,以便于比较业务成果。我们现在回到我之前文章中的例子,使这里提出的想法更加具体。
我之前提到过来自营销的 RFM 模型、来自定价的 LTM 和需求模型以及来自运营的留存模型。如果我们想一想,我们会看到每个模型都是由一些独特的变量驱动的,以及与其他模型共享的其他公共变量。
例如,在金融服务领域中,年龄、性别、地点和信用评分、以及其他变量对于 RFM 、 LTV 、需求和保留模型是常见的。这很有用,因为它向我们展示了我们可以从一个地方为所有模型提供输入。在幕后,GM 工作台同时将变量的输入值适当地级联到每个模型。
我们可以稍后深入研究这种方法的分析价值。首先,我们应该停下来,认识到 GM 工作台不太明显但意义深远的好处。这将为 GMs 节省寻找和说服所有者发布其数据和相关模型的工作,以便进行例行分析和洞察,从而为数据驱动的战略和路线修正提供信息。
从分析的角度来看,GM 工作台的输入界面最好被认为反映了特定产品的设计和相关的** 定价。在这个隐喻下,可变输入“激活”特定的产品特征,并提供客户风险属性。这些数据与收入、成本和利润负载数据以及内部计算结合使用,以推动 LTV 和基于需求的定价。这种定价反过来在交易层面推动 RFM 和保留模型的输出。**

模型的可变输入(Rajesh Iyer 的原始插图)
默认情况下,将向 GMs 提供投资组合视图的利润和增长,这是从交易级别上升到投资组合和/或部门级别的ML 驱动结果的汇总。****

拟议战略和行动的效果(Rajesh Iyer 原创插图)
"全球机制工作台"之所以得名,是因为全球机制可以利用它来尝试和比较各种可能的战略和政策情景的财务和非财务影响。更具体地说,工作台允许总经理轻松地对其职权范围内的投资组合执行“What-if、模拟和优化分析。
我们希望 GMs 创建情景,研究改变当前客户组合、细分市场特定增长焦点等的影响。,以达成将实施的“当前正确的”策略。**

在有效边界上选择一个点(Rajesh Iyer 原创插图)
这些类型的练习允许总经理做出正确的选择和策略,以反映他们公司平衡利润与增长的哲学。例如,上图显示总经理优先考虑投资组合的增长而不是盈利。
—
我的团队在实现上述类型的 GM 工作台的愿景方面已经取得了很大的进步,但是我们并不孤单。我要感谢来自 Earnix 和 DataRobot 的金融服务领域和工程专家,前者是一家成熟的优化平台销售商,后者是一家受欢迎的汽车 ML 平台销售商,他们为制定通用工作台概念提供了大量帮助。
在我们的工作中,我们依靠 DataRobot 的 ML 模型构建能力,并利用 Earnix 在一个平台上将各种模型联系在一起,使用我团队的专业知识为金融服务公司制定实施蓝图和 GSI 服务产品。
E.结论
我相信是时候认真投资建立像 GM Workbench 这样的战略人工智能解决方案了。我们与 Earnix 和 DataRobot 的合作表明,随着时间的推移,我们可以构建 GM 工作台并根据个人客户的需求对其进行定制,以便他们可以利用它来主导其市场竞争。
F. Nota Bene
我想以明确阐述这篇文章的动机来结束这篇文章。我对推动人工智能工作感兴趣,而不是专注于任何一种类型。具体来说,我正在寻找方法,说服企业赞助严肃的人工智能投资,这些投资有可能带来巨大的企业价值。
如果承诺的价值,然后一些,可以交付,我们可以期待看到公司充满信心地深化他们在所有类型的人工智能的参与和投资。
使用服务主体和秘密范围在 Azure 数据块中挂载和访问 ADLS Gen2
关于使用 Azure 密钥库支持的秘密范围和服务主体从 Python 中的数据块访问 Azure 数据湖存储 Gen2 的指南。

马库斯·温克勒在 Unsplash 上的照片
Azure 数据湖存储和 Azure 数据块无疑是 Azure 基于云的数据分析系统的支柱。Azure 数据湖存储提供了可扩展且经济高效的存储,而 Azure Databricks 提供了在该存储上构建分析的方法。
分析过程从将存储装载到 Databricks 分布式文件系统(DBFS)开始。有几种方法可以将 Azure 数据湖存储 Gen2 挂载到 Databricks。也许最安全的方法之一是将身份和访问管理任务委托给 Azure AD。
本文着眼于如何将 Azure 数据湖存储挂载到由服务主体和 OAuth 2.0 使用 Azure Key Vault 支持的秘密范围认证的数据块。
警告: Microsoft Azure 是一项付费服务,遵循本文可能会导致您或您的组织承担财务责任。
在撰写本文时,Azure Key Vault 支持的 Secret Scopes 处于“公开预览”状态建议不要在生产或关键系统中使用任何“预览”功能。
在继续阅读本文之前,请阅读我们的使用条款:https://dhyanintech . medium . com/disclaimer-disclosure-disclosure-terms-of-use-fb3 BF BD 1e 0e 5
先决条件
- 有效的 Microsoft Azure 订阅
- Azure 数据湖存储第二代客户
- Azure Databricks 工作区(高级定价层)
- 蓝色钥匙保险库
如果您还没有设置先决条件,请参考我们之前的文章开始:
[## 使用 Azure 将 CSV 文件转换为 Power BI 视觉效果的权威指南
使用 Microsoft Azure 产品将新冠肺炎数据转化为惊人的 Power BI 视觉效果的分步指南。
medium.com](https://medium.com/@dhyanintech/a-definitive-guide-to-turn-csv-files-into-power-bi-visuals-using-azure-4483cf406eab)
要访问由 Azure AD 租户保护的资源(例如,存储帐户),安全主体必须代表需要访问的实体。安全主体为 Azure AD 租户中的用户或应用程序定义访问策略和权限。当应用被允许访问租户中的资源时(例如,在注册时),服务主体对象被自动创建。
关于服务原则的进一步阅读:
本文描述了 Azure Active Directory 中的应用程序注册、应用程序对象和服务主体…
docs.microsoft.com](https://docs.microsoft.com/en-us/azure/active-directory/develop/app-objects-and-service-principals)
让我们首先注册一个 Azure AD 应用程序来创建一个服务主体,并将我们的应用程序身份验证密钥存储在 Azure Key Vault 实例中。
注册 Azure 广告应用程序
在 Azure 门户主页上找到并选择 Azure Active Directory 。选择应用注册,点击 +新注册。

Azure 活动目录:注册一个新的应用程序(图片由作者提供)
在注册应用页面,输入名称ADL access,表示应用的用途,点击注册。

Azure 活动目录:命名和注册应用程序(图片由作者提供)
在ADL access屏幕中,将应用(客户端)ID 和目录(租户)ID 复制到记事本中。应用 ID 是指我们刚刚注册的 app(即 ADL access),我们的 app ADLSAccess 注册到的 Azure AD 租户是目录 ID。
接下来,我们需要生成一个认证密钥(也称为应用程序密码或客户端密码或应用程序密码)来认证 ADLSAccess 应用程序。点击证书和密码,然后点击 +新客户端密码。在上添加一个客户端秘密刀片,输入描述,一年期满,完成后点击添加。

Azure Active Directory:创建客户端机密(图片由作者提供)
当您点击添加时,将出现客户端密码(认证密钥),如下图所示。您只有一次机会将这个键值复制到记事本中。如果您执行另一个操作或离开此刀片,您将无法取回它。

Azure 活动目录:客户端机密(图片由作者提供)
授予服务主体对 ADLS 帐户的访问权限
接下来,我们需要为我们的服务主体分配一个访问角色(回想一下,服务主体是在注册应用程序时自动创建的),以访问我们的存储帐户中的数据。转到 Azure 门户主页,打开您的存储帐户所在的资源组。点击访问控制(IAM) ,在访问控制(IAM) 页面,选择 +添加,点击添加角色分配。在添加角色分配刀片上,将存储 Blob 数据贡献者角色分配给我们的服务主体(即 ADLSAccess),如下所示。

资源组:将角色分配给服务主体(按作者排序的图像)
将应用程序机密添加到 Azure 密钥库
转到 Azure 门户主页并打开您的密钥库。点击秘密添加新的秘密;选择 +生成/导入。在上创造一个秘密刃;给定一个名称,输入客户端机密(即,我们在上一步中复制的 ADLS 访问密钥)作为值和内容类型,以便于以后阅读和识别机密。重复前面复制的应用程序(客户机)ID 和目录(租户)ID 的创建过程。你的金库现在应该有三个秘密了。


Azure Key Vault:添加新秘密(图片由作者提供)
选择属性,将金库 URI 和资源 ID 复制到记事本中;下一步我们将需要它们。

Azure 密钥库:属性(图片由作者提供)
在 Azure Databricks 中创建 Azure 密钥库支持的机密范围
如果你已经阅读了我们的另一篇关于为 Azure SQL Server 凭证创建秘密范围的文章,只要你的密钥库和数据块实例保持不变,你就不必执行这个步骤。
转到https://<DATABRICKS-INSTANCE>#secrets/createScope,用您实际的数据块实例 URL 替换<数据块实例>。创建一个秘密范围,如下所示。
此 URL 区分大小写。

Azure Databricks:创建一个秘密范围(图片由作者提供)
使用秘密范围将 ADLS 安装到数据块
最后,是时候将我们的存储帐户安装到我们的 Databricks 集群上了。回到您的 Databricks 集群,打开我们之前创建的笔记本(或者任何笔记本,如果您没有遵循我们的整个系列)。
我们将定义一些变量来生成我们的连接字符串,并使用 Databricks 实用程序获取秘密。您可以将下面的代码复制粘贴到您的笔记本上,或者自己键入。我们在这个笔记本上使用 Python。使用单元格右上角给出的控件运行代码。不要忘记用你的存储细节和秘密名称替换变量赋值。
关于数据块工具(dbutils)和访问机密的进一步阅读:
[## 数据块工具
Databricks 实用程序(DBUtils)使执行强大的任务组合变得容易。您可以使用这些实用程序来…
docs.databricks.com](https://docs.databricks.com/dev-tools/databricks-utils.html#secrets-utilities) 
Azure Databricks:在 Python 中安装 ADLS Gen2(图片由作者提供)
如何高效使用笔记本的进一步阅读:
[## 使用笔记本
笔记本是可运行单元(命令)的集合。当您使用笔记本电脑时,您主要是在开发和…
docs.databricks.com](https://docs.databricks.com/notebooks/notebooks-use.html)
我们可以通过在单元格的开头指定 language magic 命令来覆盖笔记本的默认语言。支持的魔法命令有%python、%r、%scala和%sql。笔记本也支持一些额外的魔法命令,如%fs、%sh和%md。我们可以使用%fs ls来列出我们挂载的存储的内容。

Azure Databricks:魔法命令(图片由作者提供)
当您不再需要存储时,不要忘记卸载它。
# Unmount only if directory is mounted
if any(mount.mountPoint == mountPoint for mount in dbutils.fs.mounts()):
dbutils.fs.unmount(mountPoint)

Azure Databricks:卸载 Python 中的 ADLS Gen2(图片由作者提供)
恭喜你!您已经成功地将您的存储帐户安装到 Databricks,而没有泄露和存储您的应用程序机密和访问密钥。
结论
我们看了如何注册一个新的 Azure AD 应用程序来创建一个服务主体,为服务主体分配访问角色,并将我们的秘密存储到 Azure Key Vault。我们在 Azure Dataricks 中创建了一个 Azure Key Vault 支持的 Secret 范围,并在 Dataricks 中安全地挂载和列出了存储在我们的 ADLS Gen2 帐户中的文件。
后续步骤
如果你想知道我们是如何在存储中获得这些 CSV 文件的,请阅读我们的另一篇文章,建立一个创新的 Azure 数据工厂管道,通过 HTTP 从 GitHub 存储库中复制文件。
[## 使用 Azure Data Factory 基于 HTTP 上的 URL 模式增量复制文件
一个创新的 Azure 数据工厂管道,通过 HTTP 从第三方网站增量复制多个文件…
我们还有另一篇关于连接和访问来自 Datarbricks 的 Azure Synapse 分析数据仓库的激动人心的文章。看一看:
[## 在 Azure Databricks 中连接和访问 Azure Synapse Analytics 的凭据安全方式
关于如何在 PySpark 中使用秘密作用域设置 SQL Server 防火墙和从数据块连接的指南
喜欢这个帖子?与 Dhyan 联系
让我们做朋友吧!你可以在 LinkedIn 上找到我或者在 Medium 上加入我。
安装 SSH 连接,这样您就可以像处理本地文件一样处理远程文件。
您想在本地编辑远程文件吗?尝试这种简单、安全的方法,用 SSHFS 挂载您的远程驱动器。

这可能是你。只要打破你所有的灯泡,拿出连帽衫。凯文·霍尔瓦特在 Unsplash 上的照片
场景:你在 GPU 服务器上有 Pytorch 或 Tensorflow 机器学习指令,或者你的 Raspberry Pi 上有连接到 GPIO 引脚的硬件项目,或者你的安全气隙网络上有远程 docker 容器,你真的想深入了解一个高级 IDE。
在某些情况下,您可以运行一个远程开发环境,但是如果您连接到一个轻量级系统,或者您没有完全访问权限,您可能会发现自己面临着使用远程系统命令行上可用的东西——通常是 nano 或 vim 。很好,如果你想快速进出的话,但是他们在用户体验方面并不是很好。
SSHFS—安全外壳文件系统
随之而来的是 SSHFS 。发音就像忍者明星飞过你的头时发出的声音(或者如果发音让你觉得奇怪,你可以大声说出这些字母)。SSHFS 是一种安全地挂载远程文件夹的方式,就像它是本地机器上的一个文件夹一样,而且非常简单。
那是什么意思?
这意味着你可以像在本地机器上一样浏览和操作你挂载的远程文件!
这意味着您可以避免来回传输文件,推和拉提交,在小而笨重的命令行编辑器中编辑,这是安全的。还有,我说过容易吗?
macOS:
在您的本地工作站上,从 FUSE 站点或用自制软件安装 FUSE 和 SSHFS:
brew cask install osxfuse
brew install sshfs
安装完成后,确保在您的远程系统上启用了 ssh,并使用您的终端连接到它。
这里有一个例子:
- 假设您有一台本地地址为 192.168.68.55 的远程机器,您连接到端口
9876。你的用户名是ubuntu,你的文件存储在~/Documents/MyDir/。 - 在本地工作站上创建一个 sshfs 将挂载到的文件夹,或者一个挂载点。例如,您可以在您的 local:
~/Documents/projects/remote上创建这个。
3.键入以下内容:
ssh ubuntu@192.168.68.55:/home/ubuntu/Documents/MyDir ~/Documents/projects/remote -p 9876
以下是基本规则:
ssh <user>@<address>:</remote/dir/> </local/mountdir> <options>
如果工作正常,系统会提示您输入密码(或者您可以使用键设置无密码 SSH 登录)。当您现在导航到您的项目文件夹时,您将看到一个挂载的驱动器,可以访问您的本地文件系统。很简单,柠檬榨汁机。
卸载
要卸载本地驱动器上的文件夹,请使用:
umount -f /local/mountpoint
Linux 操作系统
运行和在 macOS 上是一样的,只是安装有点不同。另请参见下面的卸载。
Ubuntu/Debian
sudo apt update
sudo apt install sshfs
红帽,CentOS
sudo yum install sshfs
卸载
fusermount -u /local/mountpoint
Windows 操作系统
有几种适用于 windows 的解决方案:
它们都有稍微不同的方法,所以我建议遵循它们文档中给出的说明,但是基本上,您将使用 Windows Explorer 来映射远程 SSH 机器上的网络驱动器。
利润!
你有它!这种方法不需要在你连接的机器上做任何繁重的工作,而且它真的让你选择的机器上的事情变得更容易。如果你想多走一英里(我知道你想),那么看看如何自动安装你的驱动器,让它一直在那里!
快乐嘘!
基于 MovieLens 的电影推荐系统
利用自然语言处理(NLP)和计算机视觉(CV)
今年夏天,我有幸与 ML 制作的合作,体验了一次有意义的数据科学孵化。我选择了令人敬畏的 MovieLens 数据集,并设法创建了一个电影推荐系统,该系统以某种方式模拟了一些最成功的推荐引擎产品,如抖音、YouTube 和网飞。
这篇文章将解释我是如何在这个项目的整个生命周期中工作的,并提供我对一些技术问题的解决方案。

图片来自 Pixabay 的 Gerd Altmann
主意
乍一看数据集,总共有三个表:
- movies.csv :这是包含电影所有信息的表格,包括标题、标语、描述等。总共有 21 个功能/专栏,所以我们候选人可以只关注其中的一些,或者尝试利用所有的功能/专栏。
- 收视率 _ 小。csv:记录所有用户评分行为的表格,包括他们的评分和他们发布评分时的时间戳。
- links.csv :记录每部电影在 IMDB 和 TMDB 两个电影数据库中唯一 ID 的表格。
有两种常见的推荐过滤技术:协同过滤和内容过滤。协同过滤要求模型学习用户之间的联系/相似性,以便它可以根据用户以前的选择、偏好或品味生成最佳推荐选项。内容过滤需要用户和项目的特征,这样系统就可以根据用户和项目的共同属性来确定推荐。
现在,我将一步一步地尝试这两种方法。
协同过滤
协同过滤只需要我记录用户以前的行为,比如说,他们过去有多喜欢一部电影。幸运的是,我们已经获得了这类信息,因为表 ratings_small.csv 中的数据准确地反映了这个。为了实现这一技术,我应用了奇妙的 Python 库 Surprise 。它提供了一套推荐系统开发中常用的内置算法。我选择了 5 种方法来比较它们的准确性,以 RMSE 作为衡量标准,结果如下:

推荐系统算法的准确性(图片由作者提供)
SVD 优于任何其他同类产品,以下是针对每个用户的最终推荐(当然,配置了 SVD)列表片段,如下所示:

作者图片
协同过滤最明显的优点是易于实现。它不需要关于用户和项目的太详细的信息,理想情况下,它可以用 5 行代码实现。
内容过滤
尽管协同过滤技术有其突出的优点,但它的另一面也很明显:它不能解决“冷启动”问题。这个问题指的是这样一种情况,即一个新的商品或一个新的用户被添加到系统中,而系统无法向消费者推销该商品或向用户建议任何可用的选项。这是因为系统没有跟踪用户和项目的属性。除非用户开始对新项目进行评级,否则它不会被推广;同样,在用户开始评价之前,系统也不知道该推荐什么。

想象一下,如果乔安娜不是一个女人,系统推荐一件比基尼,仅仅是因为 Funmi 也买了一件带比基尼的人字拖……这也是内容过滤可以解决的一个问题。(图片来源:链接)
内容过滤是解决这个问题的方法。当提供用户/项目简档时,它使系统能够理解用户的偏好。例如,如果用户的播放列表包含正义联盟、复仇者联盟、、水行侠、和闪灵,那么他/她很可能更喜欢动作和恐怖类型。如果使用协同过滤,该用户将被推荐一些喜剧,因为其他观看正义联盟、复仇者联盟、、水行侠、和闪灵的观众观看了喜剧。如果某个用户根本不喜欢喜剧,这有时是没有意义的。但是通过内容过滤,可以避免这样的问题,因为系统已经确认了该用户的偏好是什么。
为了实现一个内容过滤推荐系统,我利用 TFIDF 来反映每种类型在任何电影中的重要性(在这个阶段我只考虑类型)。然后,我计算了重要性权重和用户对不同流派的偏好(在用户资料中给出)的乘积。基于和积,我们可以简单地对电影进行排序,并向用户推荐前 N 名候选人。
我是新来的怎么办?
如前面的代码片段所示,我根据现有用户的历史评分记录创建了用户/电影档案。然而,它还没有完全解决冷启动问题,因为系统仍然不知道对新用户或新电影做什么。我将在本文的剩余部分告诉你我如何从电影海报中提取类型信息,现在我将展示系统应该如何响应新用户。
我假设新用户有两种心态:他们要么明白自己想要什么类型的电影,要么什么都不懂。对于第一种类型的客户,我允许他们按照自己的意愿选择任何流派,并简单地让系统根据他们自己提供的偏好返回。
对于那些还不知道该做什么的人,我实现了 Tobias dorsch、Andreas Lommatzsch 和 Christian Rakow 的部分工作。我让系统在新用户没有任何偏好请求的情况下,尽快抓取最受欢迎的专注于电影的 twitter 账户。然后,我将最常提到的命名实体与电影进行了匹配,这些实体是由 spaCy 识别的。匹配的电影被认为是最可能受欢迎的,因为它们与当前时间的人/电影非常相似。

Tobias Dö rsch、Andreas Lommatzsch 和 Christian Rakow 基于事件检测的热门视频点播推荐(来源:http://dl . IC dst . org/pdf/files/1 CD 028 f 7a 702 b 291 a 00984 c 192 f 687 db . pdf)
如何发布新电影?
一个成熟的电影流媒体平台会不断推出新电影。我想模拟这种行为,我的想法是,无论何时有新电影开始播放,他们都可以在内容过滤推荐系统中获得推荐,即使他们的制作公司不提供他们的类型信息。我开发了一个应用 CV 自动生成流派的方法,关于它的细节,请访问这篇文章。

类型预测示例(作者图片)
部署
我总结了我在前面章节中研究的内容,并设法使用 Streamlit 开发了一个 web 应用程序。在https://recommendation-sys.herokuapp.com/尽情享受吧。

推荐系统截图(图片作者提供)
结论
这是我第一次模拟一些最先进的推荐引擎。我利用了我在 NLP 和 CV 方面的知识,尤其是内容/协同过滤推荐和多标签分类。
我应该承认,这个项目还有很大的改进空间,以下是我未来的一些关注点:
- 利用给定数据集的更多信息。你可能还记得我曾经提到过在表 movies.csv 中有 21 列。但是我只是用流派来做一个简单的演示。有了更多的信息输入,相信推荐会更加个性化和有针对性。
- 使用更高级的推荐技术。很多一些大牌开发的推荐引擎越来越精良,背后的逻辑也越来越深入。基于模型的过滤、混合过滤是最近出现的一些技术。
- 消除“过滤气泡”。用户可能只能观看系统推荐的电影,而推荐是基于他/她以前的观看历史。在这种情况下,不符合他们偏好的其他电影对用户来说是不可用的,这使得用户看起来像被困在“气泡”中。尽管如此,一些用户仍然欢迎其他类型的电影,因此推荐系统应该在推荐相似电影和其他电影之间找到一个平衡点。
- 根据近期事件推荐电影。这听起来很像我对新用户所做的,当他们不提供他们的偏好时。不同的是,我所做的只是向他们推荐最近热门的电影(演员、电影制作人等)。)涉及,而我们可以让推荐系统更智能,这意味着理解正在发生的事情,即使它与电影无关,并推荐相关的电影。这将是一个有趣的发现,考虑到数百万人,如果不是数十亿人,这些天被新冠肺炎困在家里,他们可能愿意在电影中体验人类如何最终战胜病毒攻击。
如果您对我的项目感兴趣并愿意为此做出贡献,请随时访问此处:
精彩推荐系统是用 ML 数据科学孵化器 2020 夏季项目做出来的。这是一个推荐…
github.com](https://github.com/MemphisMeng/Brilliant-Recommendation-System)
基于知识图和 Neo4j 的电影推荐
直接从数据库中快速获得高质量的推荐

Lissandrini 等人的插图。艾尔
当你游览网飞时,你会看到几个可供你观看的电影列表。一些新版本,一些受其他用户欢迎,最有趣的是,一些为你挑选的。网飞使用一个强大的推荐系统来生成这份名单。根据你所观看和评价的内容,它会从类型、情节、演员等方面建立你的品味档案,并使用这个档案来推荐符合你品味的电影。
推荐系统或推荐器被大量平台使用,包括亚马逊、网飞、脸书和许多其他电子商务和服务提供平台。他们的目的很简单:推荐特定用户最有可能购买/观看/成为朋友的物品/电影/人物。
在本文中,我们将介绍如何仅使用 Neo4j 来构建有效的推荐系统。
虽然许多推荐系统依赖于几个相互交互的子系统(例如,机器学习集群训练和从中央数据库提取数据),但我们将通过利用知识图的表达能力,实现一个直接在数据库本身上运行的推荐器,并且非常高效。
我们还展示了我们如何使用这项技术来构建 MindReader ,这是一个使用图形技术的推荐系统(将在本文后面解释),允许用户协作构建一个数据集,这不同于个性化推荐研究领域中使用的任何其他数据集。
推荐系统——概述
如果你是一个狂热的恐怖电影观众,网飞会发现这一点,并向你推荐更多的恐怖电影,而不是,例如,喜剧表演和儿童电影。不管一个人的业务性质如何,这都是一个期望的特征。当然,问题在于如何以简单、高效和有效的方式推断用户偏好。
一般来说,我们谈论三种实现方式:通过协作或基于内容的过滤,或者两者的组合(混合)。协同过滤推荐器将使用与你相似的用户的交互来确定你想要什么。
想象一下两个假设的用户,迈克和德鲁,他们都是科幻电影的粉丝,都喜欢星球大战。迈克也喜欢星际,但德鲁没看过。协同过滤推荐器会向德鲁推荐《星际穿越》,因为和德鲁有相同爱好的迈克也喜欢《星际穿越》。

作者插图
协作过滤可以是一种有效的策略,因为两个用户喜欢和不喜欢某组项目的事实可以有效地编码一些相当复杂的偏好,而我们不必担心那些偏好实际上是什么。
另一方面,基于内容的过滤推荐器将查看两部电影的内容,并确定内容的相似性是否值得推荐。也就是说,相似的商品会吸引相似偏好的用户。

作者插图
这也是一种有效的策略,并且比协同过滤更透明,因为我们通过更有形的属性如类型、演员等来理解相似性。注意,在我们的例子中,即使没有人给《星际穿越》评分,我们仍然可以推断出用户的偏好。在协同过滤中,这是不可能的。的确,基于内容的过滤在 项冷启动 项设置中确实能大放异彩。这是指没有用户评级的新项目被引入系统。
我们将构建一个基于内容的推荐器,使用用户信息和知识图(由 Neo4j graph 数据库提供支持)向用户推荐产品。然而,首先值得讨论的是为什么知识图和图数据库首先是必要的。
为什么是图表?
直观地说,为了实现基于内容的推荐器,我们应该能够将所有电影建模为 SQL 数据库中带有一系列属性(例如,类型、演员和主题)的简单对象。事实上,通过适当的关系将几个表连接起来就可以很容易地实现这一点。
一旦我们开始考虑除了Movie→HasProperty→Property之外的连接,图数据库的力量就变得很明显。事实上,我们想要表达一个更加丰富的模型,在这个模型中,我们表示属性之间的相互关系——有效地允许属性拥有属性。这也允许我们明确地对每种关系的性质进行建模。在这种情况下,图形模型的表达变得更加清晰:

作者插图
以上是一个示例知识图 表示电影和书籍以及演员、流派和它们之间的复杂关系。在知识图中,我们不仅知道什么项目与什么属性相关,还知道它们如何相关,并且对什么可以相关以及如何相关没有任何限制。
有了这样的图形结构,我们突然有了很多新的方式来描述我们想要推荐的商品。当我们比较两个项目时,这转化为关于给定用户可能欣赏什么以及为什么欣赏的更复杂的推理。例如,如果用户喜欢《云图》(电影),他们可能会喜欢《如果你能抓住我》,因为汤姆·汉克斯在这两部电影中都有出演。另一方面,他们可能在寻找不同于小说的东西。如果他们想买一本书,他们可能会喜欢“云图”(这本书),如果他们也喜欢“如果你能抓住我”,他们可能会喜欢“我是马拉拉”这本书,因为它也是一本传记,并获得了与云图书类似的奖项。
虽然用标准的 SQL 技术对此建模肯定是可能的,但由于结构复杂,这通常非常困难。相反,在图形数据库中,对这种结构建模更简单。此外,像这样在 SQL 数据库中查询大量关系是而不是 非常高效的 操作。更重要的是,在图形数据库中,我们可以随意扩展数据库图形的结构,并表示一个不断发展的领域。
提出建议
为了向用户建议物品,通常部署非常复杂的机器学习模型。在这里,我们将通过使用 PageRank 算法的变体来为我们的用户提供推荐,从而充分利用图的力量。PageRank 算法是谷歌网页排名算法的核心。它用于根据网页的连接方式对互联网上最相关和最重要的网页进行排名。这意味着它被用来评估一个页面的重要性。
该算法模拟了一个随机的网上冲浪者,通过跟踪各个网页之间的链接来浏览网页。网页以节点的形式呈现,当一个页面包含到另一个页面的链接时,就创建了连接(边)。给定网站(即网络图中的节点)的 PageRank 是由用户在漫无目的地浏览网页时有多大可能最终到达特定网页来给出的。

作者插图
在图中,最重要的网页是 Wikipedia,其次是 Neo4j 和 Dev.to,然后是 Google 和 Reddit,依此类推。
在 PageRank 模型中,我们假设随机上网者可以在任何时间传送到整个网络中的任何页面。这类似于冲浪者只是在浏览器中键入不同的 URL,而不是跟随页面上的链接。在一个名为个性化页面排名的变体中,我们将冲浪者可以传送的目标页面限制为一组特定的图节点(这被称为偏好集或个性化集,因为它们代表了特定用户最喜欢的页面)。例如,如果我们通过只允许冲浪者传送到中等来“个性化”页面排名,我们会得到以下排名:

作者插图
请注意,随机冲浪者模型对图表建模的内容没有要求。最后,我们得到的是根据相关性和重要性对图中的节点进行排序,而不管这些节点代表什么。
所以,我们应该可以用我们的电影图像数据库做类似的事情,对吗?是啊!之前知识图谱的全球 PageRank 给了我们以下排名:

作者插图
这将是我们用来向新访问的用户展示产品的排名,产生前三名(1)“我是马拉拉”,“2”“云图(电影)”,和(3)“如果你能抓住我”。因此,我们建议用户阅读“我是马拉拉”。
让我们想象一下,用户接受了我们的推荐,读到了“我是马拉拉”,并且乐在其中。这给了我们什么信息?此外,建议应该如何根据这些信息进行更改?如果没有什么变化,我们会建议用户接下来观看“云图”电影,但也许他们喜欢“我是马拉拉”的事实可以得到更好的利用。一个想法可以是简单地将页面排序个性化为“我是马拉拉”。这将通过等级向上推动与“我是马拉拉”密切相关的节点。额外的好处是,这允许我们将计算限制在局部受影响的节点。
个性化 PageRank 的另一个显著优势是,我们可以通过分配用户特定的关系权重来进一步个性化排名。例如,如果用户喜欢在不同的电影中看到相同的演员,我们可以为该用户高度权衡Stars和Co-stars关系。
使用“我是马拉拉”作为唯一的源节点,在相同的图上运行个性化的 PageRank,我们得到以下排名:

作者插图
有了这个小小的改变,我们现在会建议用户要么看《如果你能抓住我》,要么读《云图(书)》,而不是看《云图》。
为了进一步证明个性化 PageRank 适应用户偏好的能力,让我们假设我们有一个用户已经阅读并喜欢“云图”这本书。在这种情况下,我们只需将个性化集合更改为仅包含“云图(书籍)”的集合,并获得以下排名:

作者插图
因此,在我们这边没有进一步干预的情况下,我们现在有了这个用户的个性化前三名:(1)“我是马拉拉(书)”、(2)“云图”,(3)“如果你能抓住我”。
个性化页面排名已经被证明是个性化推荐背景下非常有效的排名工具( Shams et。al 2016 ),甚至被 Twitter 用来向用户提供他们可能想要关注的账户(古普塔等人)。al 2013 。不幸的是,在其最基本的形式中,PageRank 不是一个可伸缩的算法,因为它需要在一个潜在的巨大的图上进行多次遍历。幸运的是,盖洛等人。al 2020 提出了一种使用粒子滤波在知识图上非常有效地近似 PageRank 的方法。我们将在后面的实现中使用这种方法。
读心术
如前所述,我们已经使用这种推荐方法在 https://mindreader.tech 上构建了一个推荐系统。在幕后,MindReader 的用户正在合作建立一个数据集,它不同于任何其他数据集,甚至在推荐系统的最新研究中也使用过——你可以看看并在这里下载数据集。如果你今晚需要一些东西来看,想要并帮助研究人员提出更新更好的模型来推荐,试试看 MindReader 是否能猜出你的电影思维!
在下文中,我们将介绍我们是如何构建 MindReader 的。但首先,一些背景:
MindReader 首先是一个用于协作构建数据集的推荐系统。让 MindReader 数据集从研究社区中其他成熟的数据集脱颖而出的是,我们不仅知道用户如何评价,例如,马特·达蒙主演的恐怖和动作电影,我们还特别知道用户对类型和演员的看法。研究人员第一次能够看到在偏好引发过程中做出的假设(例如,“德鲁喜欢科幻和喜剧,因为他喜欢《银河系漫游指南》”)是否实际成立,因为我们现在知道德鲁如何评价这些实体。此外,我们将能够尝试从更广泛的实体(如流派或主题)中正确推断用户的电影偏好——这在冷启动设置中非常有用,因为我们最初对用户一无所知。
该游戏首先从用户那里收集一些评级,范围从对电影、流派、演员和导演的评级:

MindReader 上的反馈页面
请注意,在 Neo4j 中,“相关电影”部分的实现非常简单——只需在图中显示恰好是电影的 1 跳邻居,我们将在后面展示。在传统的 SQL 数据库中设计和要求更复杂的查询不太直观。
在收集了足够多的评分后,我们会给出两个列表:我们认为用户会喜欢和不喜欢。生成这些列表的路径出奇地短——只需运行个性化的 PageRank,将用户喜欢和不喜欢的节点分别作为源节点,按照指定的等级对节点进行排序,并选择前 10 名:

在读心术上生成的建议
查询图形数据库
我们发现将 Neo4j 与我们选择的 API 语言 Python 一起使用非常简单。通过简单地安装 Neo4j 螺栓驱动程序并用数据库凭证初始化它,我们就可以查询数据库了。然而,在直接进入 Python 查询之前,我们大量使用了 Neo4j 浏览器,它允许我们查询图表并可视化结果。这使我们能够试验查询,并更好地理解我们的图形结构和 Cypher 查询语言。
例如,我们可以使用以下查询来可视化与电影《云图》相关的人(示例来自Cypher 基础知识指南):
MATCH (people: Person)-[relatedTo]-(movie: Movie {name: "Cloud
Atlas"})
RETURN people, movie

Neo4j 浏览器客户端中与云图相关的人
我们只使用两个 Cypher 查询:一个用于获取节点进行查询(例如,流派、演员和导演),另一个用于推荐电影。两者都使用 PageRank 分数,如前所述,我们使用粒子过滤,一个 Neo4j 插件,比默认实现更快地逼近(个性化)PageRank。
首先,我们将当前用户喜欢的节点的 URIs 存储在$uris中。这些组成了我们的个性化设置-随机冲浪运动员可以传送到的源节点。我们收集对应于这些 URIs 的节点,并将它们传递给particlefiltering算法:
MATCH (n) WHERE n.uri IN $uris WITH COLLECT(n) AS nLst
CALL particlefiltering(nLst, 0, 100) YIELD nodeId, score
这给了我们节点的标识符nodeId和它们的个性化页面排名分数score。
当然,我们不希望返回用户已经看到的节点。一个简单的解决方法是在$seen变量中有一个用户看到的所有实体 URIs 的列表,我们用命令过滤掉它:
MATCH (n) WHERE id(n) = nodeId AND NOT n.uri IN $seen
WITH DISTINCT id(n) AS id, score, n.name AS name
原则上,我们可以返回这里的所有内容,但是我们注意到,如果没有相关信息,用户很难识别演员或理解主题。因此我们找到所有与实体相关的电影。在我们的图表中,只有有续集或前传的电影是相连的。因此,如果我们简单地使用MATCH关键字,我们将删除所有没有电影边缘的电影。为了解决这个问题,我们执行了一个OPTIONAL MATCH并使用collect()将相关电影限制为具有最高全球页面排名的 5 部电影。
OPTIONAL MATCH (r)<--(m: Movie) WHERE id(r) = id
WITH algo.asNode(id) AS r, m, score
ORDER BY m.pagerank DESC
WITH r, COLLECT(DISTINCT m)[..5] as movies, score
就是这样!我们现在可以返回,提取我们需要的信息:
RETURN r:Director AS director, r:Actor AS actor,
r.imdb AS imdb, r:Subject AS subject,
r:Movie AS movie, r:Company AS company,
r:Decade AS decade, r.uri AS uri,
r.name AS name, r:Genre AS genre,
r:Person AS person, r:Category AS category,
r.image AS image, r.year AS year,
movies, score
因此,使用 Neo4j,我们能够找到相关节点并轻松提取高度相关的数据,而无需实施复杂的推荐系统。
如果我们要用更传统的 SQL 技术来做这件事,我们需要对表中的节点和边进行建模,为每个查询提取节点,包括几个连接,在一个单独的图形工具中构建一个图形,并从那里计算排名。然而,由于图形数据库的强大功能,这一切都直接发生在数据库上。
摘要
在本文中,我们已经描述了如何有效地利用知识图和 图数据库来生成 产品 推荐,而不考虑应用的领域。我们还展示了我们如何使用 Neo4j 构建 MindReader ,我们在此过程中的考虑以及我们选择的数据库管理系统如何为我们带来益处。
我们强烈鼓励读者思考如何用图形建模一个问题,从而提供新的强大工具来非常容易地解决复杂问题。Neo4j 允许我们非常容易地实现一个推荐系统,允许用户协作建立一个不同于任何其他的数据集。在我们的例子中,即使考虑到我们对 SQL 的高度熟悉,用传统的数据库技术实现相同的结果也要复杂得多,并且可能不会执行得那么好。我们现在分享这个新数据集,以推进个性化推荐方面的研究,这将开辟一系列新的研究途径。
如果你今晚需要一些东西看,你应该在我们的网站上试试 MindReader。这样做有助于推进研究,扩展个性化推荐研究社区中最激动人心的数据集。如果你是一名研究人员或数据科学家,完整的 MindReader 数据集可供任何感兴趣的人下载。
(合著 安德斯·兰巴莱 , 泰斯·詹达尔 , 马泰奥·利桑德里尼 ,T43【彼得·多洛格 和 卡佳软管【
电影推荐系统(二)
了解如何找到推荐给用户的最佳商品数量

' k '建议
当在推荐系统中推荐项目时,我们非常感兴趣的是仅向用户推荐前 K 个项目,并且发现使用了最佳的数量召回和精确度。
K 时的召回率和精确度
Precision: 尝试回答“从字面上看,模型的肯定识别的比例是多少?”
真阳性/(真阳性+假阳性)。
回忆:尝试回答“模型正确知道实际阳性的比例是多少?”
真阳性/(真阳性+假阴性)
在某些情况下,我们可能会意识到我们需要关注回忆或精确度。例如,在筛查一个人是否感染了某种疾病时,召回将是可选择的。
查全率和查准率是经典的评估指标,用于评估二进制指标,因此我们必须将我们的评级(从(1–5)转换为二进制问题,其中 0 表示与相关的,1 表示与无关的 相关的项目。
转换为二进制
为了进行转换,我们必须选择一个任意的****值,根据该值,我们可以说上面的任何评级都被认为是相关的。有许多选择该值的方法,但现在,我们将选择 3.5 作为阈值,这意味着任何高于 3.5 的真实评级将被视为相关,低于 3.5 的将被视为不相关。
相关和推荐的定义
****相关:真实等级> = 3.5
****无关:真实评级< 3.5
****推荐项目:预测评分> = 3.5
****不推荐项目:预测等级> = 3.5
在我们想要获得两者混合的情况下,我们可以使用 F1 分数,它基本上是精确度和召回率的调和平均值。
F1 得分= 2 (精度 召回)/(精度+召回)*
下面的函数计算精度和召回和 F1 分数,如上所述。
*#Calculate TP,FP,TN,FN at every threshold level (0.0 - 5.0)*final = []**for** threshold **in** np.arange(0, 5.5, 0.5):
tp=0
fn=0
fp=0
tn=0
temp = [] **for** uid, _, true_r, est, _ **in** predictions:
**if**(true_r>=threshold):
**if**(est>=threshold):
tp = tp+1
**else**:
fn = fn+1
**else**:
**if**(est>=threshold):
fp = fp+1
**else**:
tn = tn+1 **if** tp == 0:
precision = 0
recall = 0
f1 = 0
**else**:
precision = tp / (tp + fp)
recall = tp / (tp + fn)
f1 = 2 * (precision * recall) / (precision + recall) temp = [threshold, tp,fp,tn ,fn, precision, recall, f1]
final.append(temp)results = pd.DataFrame(final)
results.rename(columns={0:'threshold', 1:'tp', 2: 'fp', 3: 'tn', 4:'fn', 5: 'Precision', 6:'Recall', 7:'F1'}, inplace=**True**)

结果表的屏幕截图
根据上述结果,阈值的最佳值为 2.5 。
决定‘K’
在推荐系统中,我们感兴趣的是向用户显示前 N 个项目,因此最好是计算前 N 个值的精确度和召回率,而不是计算所有项目。
下一步是找到最佳 K 值,为了找到它,我们必须首先计算阈值为 2.5 的所有 K 值(2–10)的精度和召回率。
下面是计算精度和召回@ K.** 的函数**
*'''Return precision and recall at k metrics for each user.'''* *# First map the predictions to each user.*
user_est_true = defaultdict(list)
**for** uid, _, true_r, est, _ **in** predictions:
user_est_true[uid].append((est, true_r)) precisions = dict()
recalls = dict()
**for** uid, user_ratings **in** user_est_true.items(): *# Sort user ratings by estimated value*
user_ratings.sort(key=**lambda** x: x[0], reverse=**True**) *# Number of relevant items*
n_rel = sum((true_r >= threshold) **for** (_, true_r) **in** user_ratings) *# Number of recommended items in top k*
n_rec_k = sum((est >= threshold) **for** (est, _) **in** user_ratings[:k]) *# Number of relevant and recommended items in top k*
n_rel_and_rec_k = sum(((true_r >= threshold) **and** (est >= threshold))
**for** (est, true_r) **in** user_ratings[:k]) *# Precision@K: Proportion of recommended items that are relevant*
precisions[uid] = n_rel_and_rec_k / n_rec_k **if** n_rec_k != 0 **else** 1 *# Recall@K: Proportion of relevant items that are recommended*
recalls[uid] = n_rel_and_rec_k / n_rel **if** n_rel != 0 **else** 1 *#tp = n_rel_and_rec_k*
*#fn = n_rel - tp*
*#fp = n_rec_k - tp*
**return** precisions, recalls
现在,让我们调用上面定义的函数,并对所有用户进行平均。
results=[] **for** i **in** range(2, 11):
precisions, recalls = precision_recall_at_k(predictions, k=i, threshold=2.5)*# Precision and recall can then be averaged over all users*
prec = sum(prec **for** prec **in** precisions.values()) / len(precisions)rec = sum(rec **for** rec **in** recalls.values()) / len(recalls)
results.append({'K': i, 'Precision': prec, 'Recall': rec})
results

让我们画出这个:

图 1
如图所示,精度在‘4’,之后显著下降,因此我们将 K 的值视为 4 。
瞧,完成了!!
是时候给用户推荐电影了
现在我们知道了要提供的最佳推荐数量,是时候向用户提供推荐了。为此,我们必须预测用户尚未观看的电影的收视率。
首先,我们将在电影镜头数据集上训练一个 SVD 算法。
*data = Dataset.load_builtin('ml-100k')*
trainset = data.build_full_trainset() *#Build on entire data set*
algo = SVD(n_factors=factors, n_epochs=epochs, lr_all=lr_value, reg_all=reg_value)
algo.fit(trainset)
然后预测不在训练集中的所有对(u,I)的评级。
testset = trainset.build_anti_testset()*#Predicting the ratings for testset*
predictions = algo.test(testset)
现在让我们对所有评级进行排序
**def** get_all_predictions(predictions):
*# First map the predictions to each user.*
top_n = defaultdict(list)
**for** uid, iid, true_r, est, _ **in** predictions:
top_n[uid].append((iid, est)) *# Then sort the predictions for each user*
**for** uid, user_ratings **in** top_n.items():
user_ratings.sort(key=**lambda** x: x[1], reverse=**True**) **return** top_nall_pred = get_all_predictions(predictions)
因为我们有所有预测的评级,所以我们将数据集子集化为每个用户的仅前 K 部电影(K = 4)。
n = 4
**for** uid, user_ratings **in** all_pred.items():
user_ratings.sort(key=**lambda** x: x[1], reverse=**True**)
all_pred[uid] = user_ratings[:n]tmp = pd.DataFrame.from_dict(all_pred)
tmp_transpose = tmp.transpose()
现在我们有了一个数据框,其中包含了推荐给每个用户的前 4 部电影。
让我们创建一个为给定用户返回推荐电影的函数:
**def** get_predictions(user_id):
results = tmp_transpose.loc[user_id]
**return** results
让我们尝试一个例子,并为用户 67 找到建议
user_id=67
results = get_predictions(user_id)
results

对用户的建议 _id-67
上图显示了用户 67 的前“4”个推荐,“电影 id 预测评级”。但是我们只需要电影而不需要收视率,所以让我们从结果中提取电影 id,然后从电影表中获取电影细节
recommended_movie_ids=[]
**for** x **in** range(0, n):
recommended_movie_ids.append(results[x][0])movies = pd.read_csv('data/movies.csv')
recommended_movies = movies[movies['movieId'].isin(recommended_movie_ids)]

推荐电影 id 的电影详情
现在,我们可以将结果与用户历史进行比较,看看这些推荐有多相关。
以下是用户 67 评价最高的电影

用户评分最高的电影
由于用户的历史告诉我们,用户大多喜欢剧情电影,而我们正在推荐所有的剧情电影,这意味着我们正在向用户推荐合适的电影。
看起来你很喜欢这篇文章!
- 你刚刚学会了在 K 计算精度和召回,然后推荐 K 部电影,推荐给用户多少部电影。
- 在这里你可以阅读如何使用 Surprise 库在显式数据上构建模型。
- 完整的代码,你可以在这里找到 Jupyter 笔记本。
编码快乐!
如果你有任何想法或建议,请不吝赐教。你也可以通过 LinkedIn 联系我。
参考资料:
[2]https://towards data science . com/beyond-accuracy-precision-and-recall-3da 06 bea 9 f6c
https://surprise.readthedocs.io/en/stable/
电影推荐系统
基于(1)基于记忆的协同过滤,(2)矩阵分解协同过滤和(3)基于神经的协同过滤的电影推荐系统的比较。

格伦·卡斯滕斯-彼得斯在 Unsplash 上拍摄的照片
互联网的发展给我们带来了大量的在线数据和信息。像推荐系统这样的工具允许我们过滤我们想要或需要的信息。推荐系统可以在许多环境中使用,其中之一是用于视频或音乐服务的播放列表生成器。
在这个项目中,我选择了基于 K-近邻(k-NN)、矩阵分解(MF)以及基于神经网络的方法来构建电影推荐系统。我选择研究的数据是由 GroupLens Research 收集的 MovieLens 数据集。这个数据集有 943 个用户对 1682 部电影给出的 100,000 个评价,每个用户至少评价了 20 部电影。等级是基于从 1 到 5 的范围。
评级构成了来自用户的显式响应,这将用于随后构建基于协作的过滤系统。

查尔斯·德鲁维奥在 Unsplash 上拍摄的照片
该项目分为三个阶段:
- 数据预处理
- 模型结构
- 结果分析和结论
基于 k-NN 和基于 MF 的协同过滤—数据预处理
对于基于 k-NN 和基于 MF 的模型,使用了来自 Surprise Python sci-kit 的内置数据集 ml-100k。惊奇是一个很好的选择,开始了解推荐系统。它适用于构建和分析处理显式评级数据的推荐系统。
基于 k-NN 的协同过滤——建模
数据分为 75%的训练测试样本和 25%的维持样本。GridSearchCV 执行 5 次以上,用于为预测算法寻找相似性度量配置(sim_options)的最佳集合。它使用准确性度量作为基础,通过交叉验证过程找到 sim_options 的各种组合。

作者照片
在 k-NN 模型中,我选择使用余弦相似度作为相似性度量。这将计算所有用户对(或项目)之间的余弦相似性。

作者照片
上图是基于协作的过滤(基于用户)的简单说明。
它显示了三个用户 Maria、Sally 和 Kim,以及他们对电影 A 和 b 的评级。k-NN 模型试图预测 Sally 对电影 C 的评级(Sally 尚未对电影 C 进行评级)。
然而,它首先需要找到一个与 Sally 相似的用户。从电影 A 和 B 的评级来看,基于余弦相似度,玛丽亚与莎莉的相似度大于金与莎莉的相似度。
然后,该模型将根据玛丽亚对电影 C 的评级来预测莎莉对电影 C 的评级。

作者照片
上图是基于协作的过滤(基于项目)的简单说明。
它显示了用户 Maria 和 Kim 对三部电影 A、B 和 C 的评级。当 Sally 已经对电影 A 和 b 进行了分级时,k-NN 模型试图预测 Sally 对电影 C 的分级(尚未分级)
从玛丽亚和金对电影 A、B 和 C 的评级来看,基于余弦相似度,电影 A 与电影 C 的相似度大于电影 B 与电影 C 的相似度。
然后,该模型将根据 Sally 对电影 a 的评价来预测 Sally 对电影 C 的评价。
GridSearchCV 将根据均方根误差(RMSE)找出基于用户还是基于项目的结果精度最高。
这个模型使用的算法是 KNNWithMeans。这是一个基本的协同过滤算法,考虑了每个用户的平均评级。通过这种算法消除他们的偏见,个人用户偏好被考虑在内。
基于 GridSearch CV,RMSE 值为 0.9551。维持样本的 RMSE 值为 0.9402。MSE 和 MAE 值分别为 0.884 和 0.742。
基于 MF 的协同过滤——建模
矩阵分解根据潜在因素将用户项目矩阵压缩成低维表示。这些潜在因素提供了关于用户和项目的隐藏特征。用户与项目的交互被建模为他们潜在向量的乘积。
使用的基于 MF 的算法是奇异向量分解(SVD)。GridSearchCV 用于寻找随机梯度下降过程的迭代次数、学习速率和正则化项的最佳配置。
基于 GridSearch CV,RMSE 值为 0.9530。维持样本的 RMSE 值为 0.9430。MSE 和 MAE 值分别为 0.889 和 0.754。
基于神经的协同过滤—数据预处理
由用户、电影、收视率和时间戳组成的数据文件被读入 pandas 数据帧进行数据预处理。
需要枚举电影和用户来用于建模。创建包含数据中唯一用户和电影总数的变量,然后映射回电影 id 和用户 id。
找到数据中存在的最小和最大额定值。然后将评级标准化,以便于训练模型。
基于神经的协同过滤—建模
嵌入用于在数据中表示每个用户和每部电影。这些嵌入将具有由模型拟合的向量大小 n,以捕捉每个用户/电影的交互。
用户和电影都被嵌入到 50 维(n = 50)数组向量中,用于训练和测试数据。对 75%的数据进行训练,对 25%的数据进行测试。
为了捕捉用户-电影交互,计算用户向量和电影向量之间的点积,以获得预测的评级。
Adam 优化器用于最小化预测值和实际测试值之间的精度损失。

从训练和验证损失图可以看出,基于神经网络的模型具有很好的拟合性。训练损失曲线已经下降到一个稳定点。验证(测试)损失曲线也下降到一个稳定点,与训练损失有一个小的差距。
来自基于神经的模型的 MSE 和 MAE 值是 0.075 和 0.224。
结果和结论

与基于记忆的 k-NN 模型和基于矩阵分解的 SVD 模型相比,基于神经的协同过滤模型显示出最高的准确度。

上面的图像显示了用户 838 在过去评价很高的电影以及基于神经的模型推荐的电影。










浙公网安备 33010602011771号