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

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

原文:TowardsDataScience

协议:CC BY-NC-SA 4.0

梯度下降的摄像机径向失真补偿

原文:https://towardsdatascience.com/camera-radial-distortion-compensation-with-gradient-descent-22728487acb1

如何基于简单模型来表征相机-镜头对的径向畸变

Charl FolscherUnsplash 上拍摄的照片

消费者级的相机和镜头既便宜又随处可见。不幸的是,与工业上的同类产品不同,它们并不是用来作为计算机视觉应用中精确测量的工具。

在各种失真类型中,影响低档相机和镜头最明显的是径向失真。径向失真是场景中对象的视角与图像中该对象出现的像素之间的非线性。在光学中心附*,几乎感觉不到这种效应,但随着我们径向远离光学中心,失真会变得更加明显。通常,远离光学中心的像素看起来会比应该的更靠*中心。图像的角似乎被拉向了中心。这种现象被称为桶形失真,因为从摄像机垂直方向看,一个矩形物体会变成一个圆形的“桶”(见图 1)。

图 1:显示径向失真的*面棋盘图像。作者的形象。

失真补偿

这个故事的目的是根据一个简单的模型来描述相机镜头对的径向畸变。一旦我们知道了失真参数,我们就能够补偿物体的像素位置,得到未失真的像素位置。

未失真?!那是一个词吗?

我不确定。但为了简明起见,我将使用[可能是假的]动词“不失真”来表示“补偿径向失真”。

您可以在这个库中克隆代码和示例图像。

图 1 的棋盘图像将为我们提供共线特征点。在没有径向失真的情况下,场景中共线点的像素位置应该是共线。由于它们在视觉上不共线,我们将构建一个参数可调的模型,将扭曲的像素点映射到未扭曲的点上。我们的目标函数将是棋盘上属于一条线的未失真像素点的共线度。

特征点

第一步是提取图 1 中的特征点。

# Find the checkerboard intersections, which will be our feature points that belong to a plane
    checkerboard_intersections = checkerboard.CheckerboardIntersections(
        adaptive_threshold_block_side=adaptive_threshold_block_side,
        adaptive_threshold_bias=adaptive_threshold_bias,
        correlation_threshold=correlation_threshold,
        debug_directory=output_directory
    )
    intersections_list = checkerboard_intersections.FindIntersections(checkerboard_img)

类型为棋盘交叉点的对象将彩色图像转换为灰度图像,然后应用自适应阈值。结果是一个二进制图像,其中正方形交叉点非常清晰。正方形的内部对于其余的处理没有任何价值。

图 2:阈值模式。图片由作者提供。

图 2 的阈值图像与设计用来强调两种交叉的合成图案图像相关联。

图 3:两种合成模式。图片由作者提供。

对两幅相关图像进行阈值处理,以获得最高峰值(见图 4)。

图 4:阈值相关图像之一。图片由作者提供。

检测阈值化的相关图像中的斑点,并为每个斑点计算质心,产生交叉点列表。

图 5:彩色小圆圈显示了找到的特征点。图片由作者提供。

径向畸变模型

我们将考虑一个基本的径向失真模型——非常简单:一个二次校正因子,它是距光学中心距离的函数。未失真半径将是失真半径和校正因子的乘积。

图 6:作为径向距离函数的修正系数。图片由作者提供。

这种失真模型只有三个参数:

  • 光学中心(cx,cy)。不一定与图像中心重合(w/2,h/2)。
  • 二次系数α。当α > 0 时,出现桶形失真(见图 1)。当α < 0, we have pincushion distortion (the image corners appear stretched outwards). When α = 0, there is no radial distortion.

Model optimization

We are facing a nonlinear optimization problem: finding the optimal parameters (cx, cy) and α that will project the intersection points that we found (see Figure 5) in such a way that they form straight lines. To do this, we’ll create a PyTorch 模型存储我们的三个失真参数时。

class DistortionParametersOptimizer(torch.nn.Module):
    def __init__(self, center, alpha, image_sizeHW, zero_threshold=1e-12):
        super(DistortionParametersOptimizer, self).__init__()
        self.center = torch.nn.Parameter(torch.tensor([center[0]/image_sizeHW[1], center[1]/image_sizeHW[0]]).float())
        self.alpha = torch.nn.Parameter(torch.tensor(alpha).float())
        self.image_sizeHW = image_sizeHW
        self.zero_threshold = zero_threshold

这个类还需要知道图像的大小,因为为了数值的稳定性,像素坐标将被规范化为(-1,1)。distortionparametersoptimizer . forward()方法返回一批均方误差,每一批均方误差对应一行特征点投影到对应最佳拟合线上的残差。在理想情况下,如果径向失真得到完美补偿, forward() 方法将返回一批零。

我们直接与失真补偿类 RadialDistortion 交互。当我们调用它的 Optimize() 方法时,它将在内部实例化一个类型为DistortionParametersOptimizer的对象,并运行确定数量的优化时期。

# Create a RadialDistortion object, that will optimize its parameters
radial_distortion = radial_dist.RadialDistortion((checkerboard_img.shape[0], checkerboard_img.shape[1]))
# Start the optimization
epoch_loss_center_alpha_list = radial_distortion.Optimize(intersections_list, grid_shapeHW)
Plot([epoch for epoch, _, _, _ in epoch_loss_center_alpha_list],
      [[loss for _, loss, _, _ in epoch_loss_center_alpha_list]], ["loss"])

在 100 个历元内,均方误差下降 100 倍:

图 MSE 损失与时间的函数关系。图片由作者提供。

哇!那很容易!

找到的参数是(cx,cy) = (334.5,187.2)(即图像中心(320,240)的北-北-东)和α = 0.119,对应于桶形失真的校正因子,和预期的一样。

不扭曲点

既然我们已经描述了径向失真的特征,我们可以将校正因子应用于我们的特征点。

图 8:补偿后的特征点。图片由作者提供。

图 8 显示了经过径向失真补偿后的蓝色特征点。中心点大部分保持不变,而外围点被推得离中心更远。我们可以观察到,属于棋盘上直线的场景点在图像中排列得更好。

虽然我们可能不希望在实时应用中这样做,但我们可以通过将原始图像中的每个像素投影到其相应的未失真位置来不失真整个图像。

图 9:未失真的图像。图片由作者提供。

由于我们逐渐将像素径向推离光学中心,因此我们经常会遇到投影空间中没有被原始图像中的像素映射的像素,从而产生图 9 中令人讨厌的黑色痕迹。我们可以通过将黑色像素替换为其邻域中的中值颜色来消除这些影响(参见图 10,右图)。

图 10:左图:原始图像。右图:补偿径向失真后的相应图像。图片由作者提供。

结论

我们考虑了影响大多数消费级相机和镜头的径向失真问题。我们假设一个简单的失真模型,它根据二次定律径向推动或拉动像素。通过梯度下降,我们使用棋盘的特征点优化了一组参数。这些参数允许我们补偿径向失真。

认识到畸变参数是相机镜头对固有的是很重要的。一旦知道了它们,我们就可以补偿任何图像的径向失真,只要相机-镜头对是固定的。

我邀请你一起玩代码,让我知道你的想法!

像往常一样,我只向您展示最终结果,而不是超参数的半随机调整,这感觉就像是迷宫中的盲鼠。

AI 能画出情感吗?

原文:https://towardsdatascience.com/can-ai-draw-emotions-d13329f9fcaa

测试 Dall-E 处理抽象主题的能力

“一幅代表情感的画”。图片由作者提供,使用 DALL-E 创建。

文本到图像的生成是人工智能领域最热门的话题之一。它最*取得了突破性的进展,打破了仅在几年前被认为是可能的事情。 DALL-E 2ImagenParti 最*在生成高质量图像方面表现出令人难以置信的能力,这些图像对输入文本提示具有良好的保真度。由于担心可能的滥用和偏见,谷歌尚未向公众发布其模型(Imaged 和 Parti),OpenAI 邀请了大约一百万人从等待名单中尝试 DALL-E 2 封闭测试版。

自从 Dall-E 2 paper 发布以来,我已经看到了几十个惊人的图像,从名画的重现和风格转移到奇特的宇航员在外太空骑马。然而,我没有见过许多代表抽象概念的图像。因此,我很好奇,想看看达尔-勒如何完成这项任务。我决定限制询问几代人的情绪,因为代表这些情绪的图像应该足够常见,可以出现在训练集中,最重要的是,应该可以被没有任何特定背景知识的人解释。通过这种方式,我可以自己判断代,而不用担心遗漏对我不了解的概念的引用。经过一些快速测试后,我决定在提示中坚持使用两种风格:“油画棒”和“绘画”。事实上,在某些情况下,要求一种特定的情感会产生几代人,包括现实的(但不知何故不自然扭曲的)人脸。因为我想测试模型的抽象能力,我相信一幅画会更有洞察力,而不是将情感的图像局限于人们的面部表情。

这篇文章中的所有图片(包括封面图片)都是使用 DALL-E 从给定的提示中选择第一代提供的所有(或一个,作为封面)图片生成的。对于所有图像,标题中都提供了用于生成的提示。

说的够多了,让我们给图片留点空间。

“油画棒”风格

悲哀

“一幅代表悲伤的油画棒画”。图片由作者提供,使用 DALL-E 创建。

愤怒

“一幅代表愤怒的油画棒画”。图片由作者提供,使用 DALL-E 创建。

快乐

"描绘幸福的蜡笔画"。图片由作者提供,使用 DALL-E 创建。

"一幅描绘爱情的油画棒画"。图片由作者提供,使用 DALL-E 创建。

“绘画”风格

悲哀

“一幅代表悲伤的画”。图片由作者提供,使用 DALL-E 创建。

“一幅代表爱情的画”。图片由作者提供,使用 DALL-E 创建。

愤怒

“一幅代表愤怒的画”。图片由作者提供,使用 DALL-E 创建。

快乐

“一幅代表幸福的画”。图片由作者提供,使用 DALL-E 创建。

其他抽象概念:正义与非正义

虽然不是情感,但我也为正义和非正义的概念生成了绘画风格的图像。

公正

“一幅代表正义的画”。图片由作者提供,使用 DALL-E 创建。

不公正

“一幅代表不公正的画”。图片由作者提供,使用 DALL-E 创建。

结论

在这些例子中,油画棒画似乎主要代表消极情绪的人脸,而主要代表积极情绪的绘画和作品。同样的趋势也适用于我测试的少数其他案例(抑郁、快乐)。颜色的使用似乎反映了我对特定情绪的预期:忧郁代表悲伤,强烈的红色代表愤怒,明亮的颜色代表积极的情绪。我们可以在爱的图画中看到一些心,在代表幸福的图画中看到彩虹的尝试。

这种绘画风格展示了更多抽象和复杂的插图,尽管并不总是能清晰地识别特定的情感(比如愤怒)。

代表正义的画作大多把它描绘成一个手持天*的希腊女神,而我不太确定如何解读代表非正义的画作。

总的来说,我们观察到,结果强烈地依赖于所选择的风格,并且在大多数情况下,模型在图画上书写情感的名称,尽管拼写很差。

总之,DALL-E 似乎对被测试的情绪表现出一定程度的理解,正确地将它们与面部表情以及通常与之相关的颜色或符号配对。这几代人的素质相当好,我觉得他们很有感染力,尤其是那些具有绘画风格的人。进一步调查不同风格之间相同情绪表现的差异,并检查在积极和消极情绪之间观察到的偏见是否在其他例子中仍然成立,这将是有趣的。

上下文分析能帮助提取有意义的见解吗?

原文:https://towardsdatascience.com/can-context-analysis-help-extract-meaningful-insights-178a21a88e9f

数据可视化

通过上下文分析从数据中提取有意义见解的三种策略

艾伦·琼斯在 Unsplash拍摄的照片

我最*正在读一本非常有趣的书,作者是詹姆斯·盖茨,书名是用数据讲故事:以最快的方式实现你的商业目标的新的可视化数据指南。在这本书里,我发现了一个非常有趣的章节,它描述了从数据中提取见解时上下文的重要性。

我已经详细阐述了作者的想法,在这里我描述了我所学到的,关于数据背景的重要性。我用一个实际的例子来说明我学到的概念。

上下文分析涉及数据集周围所有世界的分析。数据集周围的世界可能包括不同的方面。例如,如果您正在测量一段时间内海面的温度,环境可能包括天气状况、一些船只的存在等等。

定义上下文分析有三个要素:

  • 事件
  • 环境
  • 时间

我分别考虑这三个方面,并用一个实际的例子来说明。

1 场景的设置

例如,我考虑 1970 年至 2020 年意大利航空运输的乘客数量。使用的数据集由世界数据库在 CC-BY 4.0 许可下发布。

本节的目的是将原始数据集转换成一个时间序列,其中包含意大利的考虑指标。

首先,我将数据集加载为熊猫数据帧:

import **pandas** as pddf = pd.read_csv('API_ITA_DS2_en_csv_v2_3472313.csv')

作者图片

原始数据集包含不同的指标,因此我只选择我感兴趣的指标:

indicator = 'Air transport, passengers carried'
df_ind = df[df['Indicator Name'] == indicator]

然后,我删除未使用的列:

df_ind.drop(['Country Name', 'Country Code', 'Indicator Name', 'Indicator Code','Unnamed: 65'], axis=1, inplace = True)

我将索引重命名为value:

df_ind.rename(index={511 : 'value'}, inplace=True)

现在,我建立时间序列:

df_ind = df_ind.transpose()
ts = df_ind['value']
ts.dropna(inplace=True)
ts.index = ts.index.astype(int)

最后,我绘制了时间序列:

import matplotlib.pyplot as plt
import numpy as npxmin = np.min(ts.index)
xmax = np.max(ts.index)
plt.figure(figsize=(15,6))
plt.plot(ts)
plt.title(indicator)
plt.gca().yaxis.set_major_formatter(plt.matplotlib.ticker.StrMethodFormatter('{x:,.0f}'))
plt.xticks(np.arange(xmin, xmax+1, 1),rotation=45)
plt.xlim(xmin,xmax)
plt.grid()
plt.savefig('air_transport_basics.png',bbox_inches="tight")
plt.show()

作者图片

现在,我准备分析时间序列的背景。

2 项活动

事件是发生的事情并以某种方式影响数据集的趋势或行为。

在我们的数据集中,我们可以清楚地识别出至少三个产生负峰值的事件:

  • 双子塔在 2001 年倒塌,负峰在 2002 年,
  • 2008 年始于意大利的经济危机,
  • 2020 年新冠肺炎大流行的开始。

下图显示了意大利航空运输公司载客人数的负峰值:

作者图片

3 环境

环境包括影响数据集中趋势的所有特定外部或内部约束。例如,一个非常严格的教授可以给学生 28/30 的最高分,而另一个教授也可以分配 30/30 的最高值。因此,除非对数据进行了正确的标准化处理,否则不可能对两位不同教授评估的学生成绩进行比较。

在我们关于意大利航空运输公司运送的乘客人数的示例中,我们可能被迫将意大利的数据与德国的数据进行比较,如下图所示:

作者图片

然而,我们不能在意大利和德国之间下结论,因为它们的情况不同。虽然意大利有 44 个现役机场,德国只有 36 个,但德国机场比意大利机场更重要。这也证明了不同的流量。

4 次

时间是指中按时间顺序排列的数据。我们通常倾向于将现在的数据与过去的数据进行比较。但即使在这种情况下,我们也必须非常小心。

例如,如果我们有某个国家的游客流量的数据,我们就不能把当月的数据与上月的数据进行比较。事实上,这类数据显示了一定的季节性。相反,我们应该将当前的数据与上一个赛季的数据进行比较,以更准确地了解情况。

在我们的乘客运载场景中,我们无法将 1970 年与 2010 年进行比较,因为这两年的情况不同。相反,我们可以将一年与前一年进行比较。

我们可以计算出去年相对于前一年的增长百分比:

ts_diff = ts.diff()
ts_diff = ts_diff.iloc[1:-1]ts_diff_perc = (ts_diff-ts_diff.min())/(ts_diff.max()-ts_diff.min())*100
xmin = np.min(ts_ita.index)
xmax = np.max(ts_ita.index)

然后我们可以画出来:

作者图片

5 如何表示上下文?

用上下文来丰富图表,给读者阅读它们的可能性,而不提取错误的信息,这是非常重要的。

存在许多表示上下文的技术,包括(但不限于):

  • 标记 —添加注释以突出显示事件
  • 着色 —使用不同的颜色来突出环境
  • 图表类型 —选择适当的图表以突出显示时间。

摘要

恭喜你!您刚刚学习了上下文对于从数据中提取有意义的见解并表示它们的重要性。

三个因素共同决定了环境:时间、事件和环境。您可以用不同的技术在图表中表示它们,包括标签、颜色和图表类型。

如果你已经走了这么远来阅读,对我来说今天已经很多了。谢谢!你可以在这篇文章中读到更多关于我的信息。

相关文章

https://pub.towardsai.net/are-data-journalism-and-data-science-the-same-thing-77ba7ec794d4 https://alod83.medium.com/how-to-design-a-data-journalism-story-b2e421673b6e

有上下文和无上下文的示例

可观察量

当监督数据不足以用于 LLM 时,混合 ML 方法能有所帮助吗?

原文:https://towardsdatascience.com/can-hybrid-ml-approaches-help-when-supervised-data-isnt-enough-for-llms-fa2b587677a0

具有符号向量的精益架构可以产生更好的结果

塞缪尔·阿尔盖里尼 雷奥纳多·里古蒂尼

大卫和歌利亚。提香 1542 -1544 年。来源:维基媒体

大型语言模型时代

在过去的十年里,我们观察到了自然语言处理领域的一个重要的范式转变。随着深度学习技术的出现,端到端方法已经逐渐取代了基于分析和选择特征的严格阶段的原始机器学习方法,它们(尤其是变形金刚)目前是 NLP 和相关领域的 SOTA。通常,这些方法由非常大的人工神经网络模型(数十亿个参数)组成,这些模型被训练成用基于统计的方法来模拟语言。当使用神经网络时,训练阶段通常需要大量的数据和更多的计算资源。因此,由于对数据、功率和成本的更高需求,它们通常使用通用任务在非常大的数据集上进行预训练,然后发布以供使用或最终集成到专有工具中。你可以在这篇帖子中找到关于这些巨头的有用讨论。

图 1 — 过去几年大型语言模型(LLM)
规模趋势。图片由作者受 启发微软研究院博客

集成阶段通常需要一个微调阶段,旨在将模型裁剪为您想要重现的数据和任务,并响应特定的业务需求。值得注意的是,这个过程需要大量的标记数据(受监督的),反映要复制的任务的特征和要求。然而,在现实世界的场景中,这种数量的标记数据通常是不可用的,并且它的产生是耗时且相当昂贵的(此处您可以找到一篇关于类似问题的有趣文章)。因此,将机器学习技术应用于商业或行业特定用例的两个最大挑战是数据的稀缺和计算资源的缺乏。

围绕“少量学习” 11 技术的研究主要集中在研究和比较从少量监督数据和大量未标记数据中学习的方法。在这个领域,我们已经看到越来越多的新混合方法,其中由通用大型语言模型(LLM)返回的密集表示与知识的符号表示相结合(通常富含语言和语义信息),从而即使在处理较小的数据集时也能提供显著的提升。

但是 AI 不仅仅是深度学习

Gartner 将“复合人工智能”定义为不同人工智能技术的组合,以实现更好的结果。没错,人工智能不再仅仅是机器学习或深度学习。例如,基于规则的系统是人工智能领域的经典,它们是解决特定任务的不同但有效的方法。本文中讨论的混合方法由一个 ML 算法组成,该算法由文本的符号表示提供支持。

这种文本的符号表示利用了来自 NLP 的前一步骤的丰富的语言信息列表(形态句法和语义数据),包括许多最常见的 NLP 任务,如词汇化、依存和选区解析、词性标注、形态分析、关系检测等。此外,这些技术拥有丰富的知识图,这是一个巨大的知识树,由代表概念和它们之间关系的节点和连接组成。由 NLP 分析的这一步骤产生的不同类型的信息被表示在分离的向量空间中,这些向量空间被连接并输入到机器学习算法中。

基本上,在这种混合方法中,使用大型语言模型(LLM)产生的基于密集向量(即嵌入)的最常见表示技术被替换为文本的符号表示,其中向量的每个维度编码文本的清晰和明确的语言特征。

图 2——所用混合方法的方案。作者图片

挑战

当监督数据稀缺时,将符号人工智能与机器学习结合起来可能会改变游戏规则。事实上,当数据对于 LLM 来说根本不够用时,微调任务是极其复杂和无效的。

在这个实验中,我们比较了几种主流机器学习技术与 expert.ai *台中使用的混合 ML 在数据稀缺条件下的性能——训练集(监督数据)的大小从每类几个样本到每类大约一百个样本不等。比较的重点是:

  • 基于变压器架构的模型:BERT【1,2】及其衍生DistilBERT【3,4】和RoBERTa【5,6】;
  • Spacy 提供的车型:BoW、CNN、Ensemble
  • 关于 BoW 表示的标准sk learn【8,9】ML 算法(SVM、NB、RF、LR)
  • expert.ai 的混动车型

为了进行比较,我们选择了消费者投诉数据集 (CCD):在 Kaggle 上公开的关于金融产品和服务的投诉集合。该数据集包括从现有公司收集的真实投诉,并侧重于金融产品和服务,其中每个投诉都已按产品正确标记,同时创建了一个有 9 个目标类别(标签)的监督文本分类任务。

对于我们的实验,较长和较短的文本被删除,最终得到一个由 80,523 个监督文档组成的最终数据集:其中 10%用作测试集(8052),而其余的用作训练数据。为了测量每个模型在少量学习场景中的分类能力,我们通过随机二次抽样建立了 4 个维度递增的训练集:

  • T90:每类 10 个文档(总大小为 90);
  • T450:每类 50 个文档(总大小为 450);
  • T810:每类 90 个文档(总大小为 810);
  • TFull:没有子采样(总大小为 72471)。

我们使用增量过程来构建训练数据集,以便特定类别的监督文档始终包含在较大的数据集中:

图 3-为少数镜头比较建立的 4 个训练集。作者图片

获胜者是……

表 1 显示了随着训练集大小的增加,所有模型的分类性能。对于 expert.ai 混合模型,这些值代表通过比较 4 种不同算法(SVM、朴素贝叶斯、随机森林和逻辑回归)获得的最佳结果,同时利用文本的符号表示。

表 1:模型的性能随着训练集大小的增加而增加。作者图片

使用较小的训练集(T90、T450 和 T810),混合方法实现了其最佳性能,特别是与基于变压器的模型(即 BERT)相比,以及更一般地与深度神经网络(如 SpaCy)相比,具有显著的提升。

结果并不出人意料,因为人工神经网络,尤其是深度神经网络,通常需要大量的监督数据来进行有效的微调。在缺乏必要数据的情况下,绩效可能会很差。

实验结果带来的一个有趣的发现是,与主流算法相比,来自符号人工智能的丰富文本表示通常也提供了更好的结果。当调查来自 SkLearn 模型的结果时,这一点很明显,这些模型毫无例外地低于 expert.ai 的混合方法。

显然,当增加训练集的规模时,方法之间的差距和差异会减小——当使用整个训练集时,它们实际上消失了(TFull)。在这种情况下,表现最好的模型是 RoBERTa-Large,尽管所有其他模型,包括 expert.ai 的混合 ML 方法,都以非常小的偏差紧随其后。

结论

这些实验中出现的概念证实了一个假设,即大多数深度神经网络方法在监督数据稀缺的典型现实世界场景中可能不是非常有效。

在这些情况下,使用利用文本符号表示的混合方法似乎更有效,并产生最佳结果。这并不意外,因为深度模型通常也需要大量的监督数据来进行微调,而在相反的情况下,性能往往会下降。

当使用混合方法时,利用丰富的文本符号表示弥补了监督数据的不足,甚至优于基于 BoW 表示的经典方法。

参考文献:

  1. Devlin,Jacob,et al .〈伯特:语言理解的深度双向转换器的预训练〉arXiv 预印本arXiv:1810.04805,(2018)。
  2. 抱脸的 BERT-Large:https://huggingface.co/bert-large-uncased
  3. SANH,Victor 等人《蒸馏伯特,伯特的蒸馏版:更小、更快、更便宜、更轻》。arXiv 预印本arXiv:1910.01108,(2019)。
  4. 香椿面:https://huggingface.co/distilbert-base-uncased
  5. 刘,,等,“RoBERTa:一种稳健优化的 BERT 预训练方法”。arXiv 预印本 arXiv:1907.11692,(2019)。
  6. RoBERTa-Large on hugging face:https://huggingface.co/roberta-large
  7. SpaCy:https://SpaCy . io
  8. PEDREGOSA,Fabian 等人,“sci kit-learn:Python 中的机器学习。《机器学习研究杂志》,(2011),12:2825–2830。
  9. sk learnhttps://scikit-learn.org/stable/index.html
  10. expert . ai:T22https://www . expert . ai
  11. 王,亚青,等,“从几个例子中概括:关于少投学习的调查”美国计算机学会计算调查(csur)53.3(2020):1–34。https://arxiv.org/pdf/1904.05046.pdf

我能相信我的模型的概率吗?深入探讨概率校准

原文:https://towardsdatascience.com/can-i-trust-my-models-probabilities-a-deep-dive-into-probability-calibration-fc3886cfc677

数据科学统计学

概率校准实用指南

Edge2Edge 媒体Unsplash 上拍摄

假设你有一个二元分类器和两个观察值;模型将它们分别评分为0.60.99。具有0.99分数的样本属于阳性类的可能性更大吗?对某些模型来说,这是真的,但对其他模型来说可能不是。

这篇博文将深入探讨概率校准——这是每个数据科学家和机器学习工程师的必备工具。概率校准允许我们确保来自我们的模型的较高分数更可能属于正类。

这篇文章将提供可复制的开源软件代码示例,这样你就可以用你的数据运行它了!我们将使用 sklearn-evaluation 进行绘图,使用 Ploomber 并行执行我们的实验。

嗨!我叫爱德华多,我喜欢写关于数据科学的所有东西。如果您想了解我的最新内容。在媒体推特上关注我。感谢阅读!

什么是概率校准?

当训练一个二元分类器时,我们感兴趣的是发现一个特定的观察值是否属于正类。正类的意思取决于上下文。例如,如果处理电子邮件过滤器,这可能意味着某个特定的邮件是垃圾邮件。如果致力于内容审核,这可能意味着有害帖子

使用一个实数值范围内的数字比是/否答案提供了更多的信息。幸运的是,大多数二元分类器可以输出分数(注意,这里我使用的是单词分数,而不是概率,因为后者有严格的定义)。

让我们看一个逻辑回归的例子:

predict_proba函数允许我们输出分数(对于逻辑回归的情况,这是独立的概率):

控制台输出(1/1):

输出中的每一行代表属于类别0(第一列)或类别1(第二列)的概率。不出所料,行加起来是1

直觉上,我们期望模型在对特定预测更有信心时给出更高的概率。例如,如果属于类别1的概率是0.6,我们可以假设该模型不像一个概率估计为0.99的例子那样有信心。这是校准良好的模型所表现出的特性。

这个属性是有利的,因为它允许我们对干预进行优先级排序。例如,如果致力于内容审核,我们可能有一个模型将内容分类为无害有害;一旦我们获得了预测,我们可能会决定只要求审查团队检查那些被标记为有害的,而忽略其余的。但是团队能力有限,最好只关注危害概率大的帖子。为了做到这一点,我们可以对所有的新帖子进行评分,取分数最高的前N,然后将这些帖子交给评审团队。

然而,模型并不总是表现出这种特性,因此,如果我们想要根据输出概率对预测进行优先排序,我们必须确保我们的模型是校准良好的。

让我们看看我们的逻辑回归是否被校准。

控制台输出(1/1):

现在让我们按概率箱分组,并检查该箱内属于正类的样本的比例:

控制台输出(1/1):

我们可以看到该模型得到了合理的校准。对于0.00.1之间的输出,没有样本属于阳性类别。对于其余部分,实际正类样本的比例接*值边界。比如0.30.4之间的,29%属于正类。逻辑回归由于其损失函数而返回精确校准的概率。

很难评估表格中的数字;这就是校准曲线的用武之地,它允许我们直观地评估校准。

什么是校准曲线?

校准曲线是模型校准的图形表示。它允许我们将我们的模型与一个目标进行比较:一个完美校准的模型。

一个完全校准的模型将在 10%确信该模型属于正类时输出得分0.1,在 20%确信时输出得分0.2,以此类推。所以如果我们画这个,我们会有一条直线:

完美校准的模型。图片作者。

此外,校准曲线允许我们比较几个模型。例如,如果我们想要将一个校准良好的模型部署到生产中,我们可能会训练几个模型,然后部署一个校准更好的模型。

恋恋笔记本

我们将使用笔记本来运行我们的实验并更改模型类型(例如,逻辑回归、随机森林等)。)和数据集大小。这里可以看到源代码

笔记本很简单:它生成样本数据,拟合模型,对样本外预测进行评分,并保存它们。运行所有实验后,我们将下载模型的预测,并使用它们绘制校准曲线和其他曲线。

运行实验

为了加速我们的实验,我们将使用 Ploomber Cloud ,它允许我们参数化并并行运行笔记本。

注意:本节中的命令是 bash 命令。在终端中运行它们,或者添加 *%%sh* 魔法,如果你在 Jupyter 中执行它们的话。

让我们下载笔记本:

控制台输出(1/1):

现在,让我们运行我们的参数化笔记本。这将触发我们所有的*行实验:

控制台输出(1/1):

大约一分钟后,我们将看到所有 28 个实验都已执行完毕:

控制台输出(1/1):

让我们下载概率估计:

控制台输出(1/1):

加载输出

每个实验都将模型的预测存储在一个.parquet文件中。让我们加载数据来生成一个数据框,其中包含模型类型、样本大小和模型概率的路径(由predict_proba方法生成)。

控制台输出(1/1):

name是型号名称。n_samples是样本大小,path是每个实验生成的输出数据的路径。

逻辑回归:一个校准良好的模型

逻辑回归是一个特例,因为它的目标函数是最小化对数损失函数,所以通过设计得到了很好的校准。

让我们看看它的校准曲线:

控制台输出(1/1):

逻辑回归校准曲线。图片作者。

您可以看到概率曲线非常类似于一个完美校准的模型。

样本大小的影响

在上一节中,我们展示了逻辑回归是用来产生校准概率的。但是要注意样本大小。如果没有足够大的训练集,模型可能没有足够的信息来校准概率。下图显示了随着样本量的增加,逻辑回归模型的校准曲线:

控制台输出(1/1):

不同样本量的逻辑回归校准曲线。图片作者。

可以看到,对于 1,000 个样本,校准效果很差。然而,一旦你通过了 10,000 个样本,更多的数据不会显著改善校准。请注意,这种影响取决于您的数据的动态性;在您的用例中,您可能需要更多或更少的数据。

非校准估计量

虽然逻辑回归被设计为产生校准的概率,但是其他模型不显示这种属性。让我们看看 AdaBoost 分类器的校准图:

控制台输出(1/1):

不同样本量下的 AdaBoost 校准曲线。图片作者。

可以看到校准曲线看起来高度失真:阳性分数(y 轴)与其对应的*均预测值(x 轴)相差甚远;此外,该模型甚至不产生沿整个0.01.0轴的值。

即使样本量为 1000,000,曲线也可以更好。在接下来的章节中,我们将看到如何解决这个问题,但是现在,记住这一点:不是所有的模型都会默认产生校准的概率。特别是,最大间隔方法,如 boosting (AdaBoost 是其中之一)、支持向量机和朴素贝叶斯产生未校准的概率(Niculescu-Mizil 和 Caruana,2005)。

AdaBoost(不同于逻辑回归)有一个不同的优化目标,不产生校准概率。然而,这并不意味着模型不准确,因为在创建二元响应时,分类器是通过其准确性来评估的。我们来对比一下两款机型的性能。

现在,我们绘制并比较分类指标。AdaBoost 的指标显示在每个方块的上半部分,而逻辑回归的指标显示在下半部分。我们将看到两种型号具有相似的性能:

控制台输出(1/1):

AdaBoost 和逻辑回归度量比较。图片作者。

概率分布的重要性

到目前为止,我们仅使用校准曲线来判断分类器是否经过校准。然而,另一个需要考虑的关键因素是模型预测的分布。也就是分值有多常见或者多罕见。

让我们看看随机森林校准曲线:

控制台输出(1/1):

随机森林与逻辑回归校准曲线。图片作者。

随机森林遵循与逻辑回归相似的模式:样本量越大,校准越好。众所周知,随机森林能够提供精确的概率(Niculescu-Mizil 和 Caruana,2005 年)。

然而,这只是一部分情况。首先,让我们看看输出概率的分布:

控制台输出(1/1):

随机森林与概率的逻辑回归分布。图片作者。

我们可以看到,随机森林将概率推向0.01.0,而来自逻辑回归的概率则不那么偏斜。当随机森林被校准时,在0.20.8区域没有很多观察值。另一方面,逻辑回归在0.01.0区域一直有支撑。

一个更极端的例子是当使用一棵树时:我们会看到一个更加偏斜的概率分布。

控制台输出(1/1):

概率的决策树分布。图片作者。

让我们看看概率曲线:

控制台输出(1/1):

不同样本量的决策树概率曲线。图片作者。

你可以看到我们的两个点(0.01.0)被校准了(它们相当接*虚线)。但是,由于模型没有输出具有其他值的概率,因此不再存在更多数据。

校准分类器

培训/校准/测试分割。图片作者。

有几种技术可以校准分类器。它们通过使用您的模型的未校准预测作为输入来训练第二个模型,该模型将未校准分数映射到校准概率。我们必须使用一组新的观察数据来拟合第二个模型。否则,我们将在模型中引入偏差。

有两种广泛使用的方法:普拉特的方法和保序回归。当数据较少时,建议使用 Platt 的方法。相反,当我们有足够的数据来防止过度拟合时,保序回归更好(Niculescu-Mizil 和 Caruana,2005)。

考虑到校准不会自动产生校准良好的模型。可以更好地校准预测的模型是提升树、随机森林、支持向量机、袋装树和神经网络(Niculescu-Mizil 和 Caruana,2005)。

请记住,校准分类器会增加开发和部署过程的复杂性。因此,在尝试校准模型之前,确保没有更直接的方法来进行更好的数据清理或使用逻辑回归。

让我们看看如何使用 Platt 的方法训练、校准和测试分割来校准分类器:

控制台输出(1/1):

未校准与校准模型。图片作者。

或者,您可以使用交叉验证和测试折叠来评估和校准模型。让我们看一个使用交叉验证和保序回归的例子:

使用交叉验证进行校准。图片作者。

控制台输出(1/1):

未校准与校准模型(使用交叉验证)。图片作者。

校准多类别模型

在上一节中,我们讨论了用于校准分类器的方法(普拉特方法和保序回归),这些方法仅支持二元分类。

然而,校准方法可以通过遵循一对一 策略扩展到支持多个类别,如下例所示:

控制台输出(1/1):

未校准与校准的多级模型。图片作者。

结束语

在这篇博文中,我们深入探讨了概率校准,这是一个实用的工具,可以帮助你开发更好的预测模型。我们还讨论了为什么有些模型无需额外步骤就能显示校准预测,而其他模型则需要第二个模型来校准其预测。通过一些模拟,我们还演示了样本大小的影响,并比较了几个模型的校准曲线。

为了并行运行我们的实验,我们使用了 Ploomber Cloud ,为了生成我们的评估图,我们使用了 sklearn-evaluation 。Ploomber Cloud 有一个免费层,sklearn-evaluation 是开源的,所以你可以从这里获取这篇笔记本格式的文章,获得 API 密钥,然后用你的数据运行代码。

如果您有任何问题,欢迎加入我们的社区

参考

使用的包

以下是我们用于代码示例的版本:

控制台输出(1/1):

最初发表于ploomber . io

机器翻译能成为基于知识图的多语言问答系统的合理选择吗?

原文:https://towardsdatascience.com/can-machine-translation-be-a-reasonable-alternative-for-multilingual-question-answering-systems-242c4674a29b

剧透警告:是的,它可以!

TLDR

提供获取信息的途径是网络最主要也是最重要的目的。尽管有易于使用的工具(例如,搜索引擎、问题回答),但可访问性通常受限于使用英语的能力。

在这项工作中,我们评估知识图问答(KGQA)系统,旨在提供自然语言访问存储在知识图(KG)中的数据。这项工作的特别之处在于,我们用多种语言来看待问题。主要地,我们比较本地支持质量值和集成机器翻译(MT)工具时获得的值之间的结果。评估结果表明,使用机器翻译工具,单语 KGQA 系统可以有效地移植到大多数被考虑的语言。

问题是

根据最*的统计,只有 25.9%的在线用户说英语。与此同时, 61.2%的网络内容以英语发布。因此,不能说或读英语的用户只能有限地访问网络上提供的信息。因此,尽管统计数据存在争议,但网络上的信息可访问性存在明显差距。这种差距被称为数字语言鸿沟

如今,每当用户查询搜索引擎时,他们都希望得到一个直接的答案。直接回答功能基于问答(QA)方法,由知识图驱动。2012 年,谷歌知识图谱问世,为谷歌的直接答案提供了动力。例如,当有人问谷歌“谁写了哈利波特?”,预期会出现以下结构化结果(见下图)。

查询“谁写了哈利波特?”的谷歌搜索结果(作者截图)

这些直接的回答更具可读性,并且立刻满足了用户的信息需求,例如,他们不需要打开由搜索引擎给出的每个“相关”网页(像以前那样)并且在打开的网页上手动搜索所请求的信息。但是如果我们用一种低资源的语言问同样一个非常简单的查询——bash kir(拥有 140 万说话者的突厥语)——结果并不真正令人满意(见下图)。

查询“谁写了哈利波特?”的谷歌搜索结果巴什基尔语(作者截图)

因此,对于那些不会说任何“流行”语言的人来说,网络上的知识可访问性有一个明显的限制。因此,在本文中,我们在问题“自动机器翻译工具是否可用于增加非英语使用者对网络上知识的可访问性?”之后,在知识图问答(KGQA)的背景下解决这个问题

方法

为了回答上述研究问题,我们进行了一项大型评估研究。在我们的实验中,我们遵循面向组件的方法,通过重用现成的 QA 组件来获得对已定义主题的见解。由于我们的目标是通过机器翻译(MT)工具评估适应不支持语言的 KGQA 系统,因此我们需要:

  1. 一套支持特定语言的多语言 KGQA 系统。

2.一组由母语为不同语言的人编写的高质量问题(其中需要两组:一组是现有 KGQA 系统支持的,一组是现有 KGQA 系统不支持的)。

3.一套机器翻译工具,能够把不支持的语言中的问题翻译成支持的语言。

对于第一点,我们使用了以下知名系统: QAnswerDeepPavlov KBQA鸭嘴兽。对于第二点,我们使用了我们的 QALD-9-Plus 数据集,该数据集包含英语、德语、俄语、法语、乌克兰语、白俄罗斯语、立陶宛语、巴什基尔语和亚美尼亚语的高质量问题,相应的答案表示为对 DBpediaWikidata 知识图的 SPARQL 查询。对于第三点,我们使用了 Yandex Translate (商业 MT 工具)和 Helsinki NLP (开源 MT 工具)的 Opus MT 模型。使用沙鼠*台进行评估。因此,建立了以下实验设置(见下图)。

实验装置概述(作者提供的图片)

结果

实验结果中,我们清楚地观察到英语作为目标翻译语言的强大优势。在大多数实验中,将源语言翻译成英语会产生最佳的 QA 质量结果(例如,德语→英语、乌克兰语→英语)。

德语和乌克兰语作为源的实验值。本地问题用粗体文本突出显示。星号(*)对应最高质量的目标语言。关于系统和度量的最佳值用绿色进行了颜色编码(图由作者提供)。

在第一种情况下,英语是源语言,原始(原生)问题的质量最好,因为额外使用机器翻译会降低质量(见下图)。

英语作为资源的实验价值。本地问题用粗体文本突出显示。星号(*)对应最高质量的目标语言。关于系统和度量的最佳值用绿色进行了颜色编码(图由作者提供)。

只有在立陶宛语是源语言的情况下,关于 QA 质量的最佳目标语言才是德语(即立陶宛语→德语),而英语也表现出合理的质量(即立陶宛语→英语)。虽然实验是精心设计的,但我们认为这种情况是异常的。然而,在提高问答系统的回答质量的同时,这种异常值可能具有显著的影响。

立陶宛语作为源的实验值。本地问题用粗体文本突出显示。星号(*)对应最高质量的目标语言。关于系统和度量的最佳值用绿色进行了颜色编码(图由作者提供)。

摘要

我们的主要结论是,机器翻译可以有效地用于为大多数语言建立多语言 KGQA 系统。原来使用机器翻译工具的最佳方式是只翻译源语言(如德语、俄语等)。)翻译成英语 —这将产生最高质量的问题回答过程。因此,即使源语言和目标语言来自同一个组(例如,乌克兰语→俄语—斯拉夫语组)从质量的角度来看,最好将它们翻译成英语

尽管我们的结果和结论建议通过机器翻译组件来扩展问答系统,但我们希望向研究社区指出许多可能影响答案质量的开放问题。因此,我们计划用更多的语言来扩展我们的实验。我们欢迎任何帮助我们扩展这项研究的意见。

如果你想看到更详细的结果,请参阅我们最*的论文:https://dl.acm.org/doi/10.1145/3485447.3511940

视频演示可在此处获得:

ACM SIGWEB 频道的视频

感谢

我要感谢这部作品的合著者,即: Andreas BothDennis DiefenbachAxel-Cyrille Ngonga Ngomo

本文件的作者要感谢参与翻译数据集的所有贡献者,特别是:康斯坦丁·斯米尔诺夫、米哈伊尔·奥尔真诺夫斯基、安德烈·奥古尔佐夫、纳雷克·马洛扬、阿尔特姆·埃罗钦、米哈伊洛·内多代、阿利亚克塞·叶兹雷佐、安东·扎波洛斯基、阿图尔·佩什科夫、维塔利·利亚林、阿尔特姆·利亚利科夫、格列布·斯基巴、弗拉季斯拉夫·多尔迪伊、波琳娜·福明尼克、蒂姆·施拉德、苏珊娜·博恩斯和安娜·施拉德。此外,作者要感谢开放数据科学社区将全世界的数据科学爱好者联系在一起。

ML 能预测我的猫现在在哪吗?

原文:https://towardsdatascience.com/can-ml-predict-where-my-cat-is-now-part-2-7efaec267339

利用过去行为和天气观测的猫预测模型

温贝托·阿雷利亚诺在 Unsplash 上拍摄的照片

通过捕获几个月的历史位置和温度数据,这篇博客介绍了如何训练机器学习(ML)模型来预测我的猫一天中会去哪里。

对于不耐烦的人,你可以直接跳到预测网站这里

猫过去的行为能预测未来的睡觉地点吗?

利用一些廉价的硬件(以及一只对数据隐私问题矛盾的猫),我想看看我是否可以训练一个机器学习(ML)模型来预测这只猫 Snowy 一天中会去哪里。

基于家庭的位置和温度跟踪允许我建立一个她最喜欢的睡觉地点的房间的广泛历史。我有一个理论,通过从她的过去收集足够的数据,我能够训练一个 ML 模型来预测她未来可能去哪里。

使用 Streamlit web 应用程序预测猫的位置(图片由作者提供)

用于房间级 cat 跟踪的硬件

第一项任务是收集大量关于 Snowy 历史上在哪里度过的数据,以及温度和降雨量等环境因素。我设定了一个任意的目标,收集三个月来在房子里和房子周围活动的每小时更新。

用房间信标定位猫(图片由作者提供)

猫不擅长数据输入;所以我需要一种自动的方法来收集她的位置。我让白雪戴上一个小的电池供电的蓝牙追踪器。她衣领上的这个装置只是发射一个常规的独特的蓝牙低能量( BLE )信号。然后我用八个固定的接收器来监听这个 BLE 信号。这些接收器节点是基于 ESP32 的存在检测节点,放置在房屋内和周围指定的房间中(6 个在室内,2 个在室外)。

每个接收节点不断寻找唯一的 BLE 信号,并测量接收信号强度指标( RSSI )。信号越强,白雪离那个信标就越*(要么是这样,要么是她在摆弄电池)。例如,如果我在研究传感器旁边收到几秒钟的强信号,我可以假设白雪可能非常接*那个房间。

最终我收集了三个月的位置观测,超过1200 万的位置、温度、湿度和降雨量观测(我可能已经超过了数据收集的上限)。

我一直试图回答的问题是,我能利用这些历史观察建立一个她可能去哪里的预测模型吗?我能有多大信心用一台机器来预测一只猫可能在哪里,来预测白雪的藏身之处?

ML 训练营

监督学习是基于示例输入-输出对创建将输入映射到输出的函数的 ML 任务。在我的例子中,我想对猫的位置、温度、一天中的时间等进行历史观察。,作为输入和查找模式…预测未来猫位置的函数(推理)。

温度、时间和日期——它能映射到位置吗?(图片由作者提供)

我的假设是这个问题可以从这些数据中归纳出来;例如,未来的数据将遵循过去猫行为的一些常见模式(对于猫来说,这种假设可能是有问题的)。

猫咪位置预测(图片由作者提供)

该训练使用过去的信息来建立一个模型,该模型是可部署的人工制品。一旦训练了候选模型,就可以测试它的预测准确性并最终部署它。在我的例子中,我希望创建一个 web 应用程序来预测 Snowy 可能在哪里打盹。

同样重要的是,该模型不必明确输出绝对位置,但可以根据置信度给出答案。如果它输出 P(位置:研究)接* 1.0,则它是有信心的,但是接* 0.5 的值表示对预测 Snowy 的位置的信心“不确定”。

使用 dbt 汇总数据

我的数据*台家庭助手将每个传感器更新存储在状态表中。这真的是细粒度的,每隔几秒就有来自所有传感器的更新(在我的例子中,每天大约有 18,000 个传感器更新)。为了简化模型训练,我想把数据总结成每小时更新的数据——基本上是一个单一的(最普遍的)位置,以及温度和湿度读数。

将大量数据汇总成每小时的摘要(图片由作者提供)

最初,我用一堆 SQL 语句(如 this )手动运行数据处理来处理数据。然而,我发现这相当麻烦,因为我想在新的位置和环境条件下重新训练模型。我决定使用可靠的数据工程工具 dbt 来简化数据库中 SQL 转换的创建,以使再培训更加有效。

显示数据转换的 dbt 谱系图(图片由作者提供)

dbt 负责将这些 my select 语句转换成表和视图,在我的 postgres 数据仓库中执行数据转换。

模型训练和评估

一旦我清理了我的数据,我就使用 Scikit-learn 随机森林决策树分类作为我的预测模型。随机森林根据训练数据创建决策树。对于 Snowy 来说,这意味着位置预测是从投票的多次随机迭代中得出的分类——选择投票最多的结果位置。

如果你看一下 python 笔记本,你可以看到为输入分配一个类标签所采取的步骤,这些步骤基于许多例子,这些例子是从过去对一天中的时间、温度和位置的数千次观察中训练出来的。

用于可视化要素重要性的 Python 代码段(图片由作者提供)

关于 Scikit-learn 决策树模型的一个真正酷的事情是可视化正在发生的事情是多么容易。通过可视化模型特征(如上),我可以看到“一天中的一小时”是模型中最重要的特征。

直觉上这是有道理的——一天中的时间可能对下雪的地方有最大的影响。预测白雪位置的第二个最重要的特征是室外气温。这也是有道理的——如果她想出去,太热或太冷都有可能改变。我发现令人惊讶的是最不重要的特性是正在训练特性。一种可能的解释是,该功能仅在白天有意义,当白雪晚上在室内睡觉时,下雨不会对模型产生影响。

还可以使用 Scikit-Learn 从 Python 中的随机森林中可视化出一棵决策树。

显示小时和日期决策点的可视化决策树(图片由作者提供)

在我的显示树中,我可以看到一天中的某个小时是预测中的初始决策点,上午 7:00 是算法中有趣的部分。这是我们家闹钟响起的时间——猫被激励起来寻找食物。该树的另一个有趣部分是“一周中的某一天≤ 5.5”。这相当于一周中的某一天是周一到周五——同样,算法的这一部分是有意义的,因为我们(和猫)通常在周末会起得晚一点

Streamlit 中的 cat predictor 网络应用程序

有了创建的模型,我现在想要构建一个 web 应用程序来基于一系列输入预测 Snowy 的位置。 Streamlit 是一个开源的 Python 库,它使得创建 web 应用程序变得很容易(无需我学习一堆前端框架)。我为特征值添加了滑块和选择框,比如日期和温度。

Web 应用程序—输入为滑块控件(图片由作者提供)

瞧——我用更多的 python 代码创建了一个猫预测应用程序;预测雪猫可能位置的网络应用。我找到了一些优秀的指令来将我的 Streamlit 应用程序部署到 Heroku。

那么,ML 能预测我的猫现在在哪里吗?没错——而且惊人的准确。白雪在花园里愉快地睡着了,正如预测应用程序所预测的那样。

代码链接

希望你发现这个博客和代码对你所有的宠物位置预测需求有所帮助

量子机器学习能帮助我们解决气候变化吗?

原文:https://towardsdatascience.com/can-quantum-machine-learning-help-us-solve-climate-change-3500741d583b

量子衍生技术的历史和潜在应用

埃利斯·陈嘉炜在 Unsplash 上拍摄的照片

介绍

在过去的几年里,由于组件结构、效率、速度和质量改进方面的指数级技术进步,通用计算机硬件的小型化速度受到了限制。这些变化过去和现在都主要受到通信、数字休闲、逃避现实生活及其问题以及由于疫情特殊情况而远程工作等社会需求的推动。

因此,一旦必然性把科学和技术推到了边缘,我们就开始遇到一个不可逾越的物理极限——原子。此外,无法进一步减小经典处理设备的尺寸已经传播了这样一种观点,即支持 摩尔定律 的趋势将在未来几十年变得*坦,因为当建造这样的微小机制时,工程师的工作将变得更加复杂。为了正确理解这意味着什么以及为什么会发生这种情况,经典计算最基本的构建模块晶体管的最小尺寸约为 2 纳米。这意味着地球上发现的最小的病毒比嵌入 CPU 的最小的 晶体管 大十倍。知道了这些,就方便注意到原子的直径范围在 0.1 到 0.5 纳米之间,这是我们在生产高端技术时正在接*的一个疯狂的*距离。

布莱恩·科斯蒂克在 Unsplash 上拍摄的照片

尺寸限制不仅阻止了 CPU 拥有更多的晶体管,从而获得更多的计算能力,而且由于其异常的复杂性,也使它们无法廉价或直接地制造和维修。此外,即使我们可以设计出比今天强大一百万倍的 CPU,如果它需要一百万倍的能量来运行,它也没有什么用处,这就引入了新的问题需要考虑。于是,许多世界范围内的计算机科学、物理、电子和数学专家提出了一个解决方案,那就是建造一台所谓的 “量子计算机”。 然而,这种将经典规则量子化的想法并不像看起来那么当代。第一个介绍它的人是保罗·贝尼奥夫,一个臭名昭著的美国物理学家,被广泛认为是量子计算之父。在他 1981 年的论文 【作为物理系统的计算机】 中,他提出了“量子图灵机”的想法,这种机器将使用量子力学的原理来执行计算。尽管如此,直到 1995 年,https://en.wikipedia.org/wiki/National_Institute_of_Standards_and_Technology*研究人员 克里斯托弗·门罗大卫·温兰 才实验性地实现了第一个量子逻辑门,即受控非 (C 非) 门,从而导致了第一个工作的双量子位核磁共振 (原子核)*

在花费了二十多年的时间开发量子逻辑门实验实现的新方法并在实验室证明其可行性后,世界各地的科学团队已经在各种量子*台上成功构建了量子处理器,从捕获的离子到超导电路。此外,它的快速发展也使量子计算成为解决人类面临的一些最具挑战性问题的可行选择。然而,这个领域存在的时间太短,这对它不利。许多梦想升级这个世界的硬件开发人员、研究人员和物理学家面临着大量的阻碍和挫折;它们中的大部分源自理解处理物理元件的不同方式的工作原理所需的特定知识,以产生可应用于工业的功能硬件。因此,非专家和量子技术之间的关系很可能是前者向后者寻求问题的潜在解决方案,理解实施这些想法可能有很大的困难。

因此,这些快速的进步导致了公众的明显无知,有时会被 20 世纪或更早的古代技术的明显简单性所淹没。谈到技术教育,除了对目前被落在后面的某些群体的长期不利影响之外,还有越来越多的问题与它对地球、环境以及我们所有人的影响有关。发布与我们在市场上拥有的产品一样先进的产品,不仅在电子或计算领域,而且在所有领域,意味着大量资源的消耗和废物的产生,这可能对我们的身体健康产生严重后果。由于我们无法在短时间内完全解决这个问题,我们需要利用今天的想法、项目、创新和人们的意愿来实现可持续的存在方式。

目标

本文旨在尽可能简单地解释量子力学、量子计算及其与机器学习的融合的基本原理,提供这些研究领域的总体视图,以解决标题“ 中提出的问题:量子机器学习能否帮助我们解决气候变化?” 。通过这样做,我们可以增加社会对这些遥远的破坏性工具的了解和信任,假设这是一个学习、贡献和提高对其可能使用案例的认识的绝佳机会,主要是在环境保护领域。此外,将语言和数学中难以理解的高级概念简化为更熟悉的东西,也为进一步的研究和实验提供了一个途径,这对于保持密集的技术进步率而不导致社会其他方面的弊端是必不可少的。

由于在处理量子力学这样一个必要的研究领域时,可能性是无限的,掌握基础知识迫使我们适应我们构思知识的思维框架,并让我们对我们生活的宇宙有更深的理解。

基本概念

历史:

在我们直接进入量子计算概念之前,我们需要理解支持其硬件的物理原理。首先,让我们来看看它是如何开始被研究的历史。20 世纪初,物理学由三大精选组成, 牛顿 力学, 麦克斯韦电动力学,以及 克劳修斯-玻尔兹曼 热力学。这些知识的每一个分支共同构成了对已知现象的完整解释,由于其确定性,这些已知现象被认为是 【经典物理学】 。然而,当涉及到为物质的最基本方面建模时,如原子光谱,或接*光速的情况,它引发了激烈的矛盾,这后来导致了爱因斯坦的 相对论 。尽管如此,科学家们通过无数次实验,一直在寻找能够一致描述所有自然现象的统一物理理论。其中一些不可避免地把我们带到了今天我们知道的量子力学。**

图片来自物理-堆栈-交换。

作为一个例子,强调像 紫外线灾难 这样的先验问题并不是多余的,在这些问题中,人们试图找到一种定律来模拟理想 黑体 发出的辐射量。这种物理物体吸收与其波长相关的各种辐射。正如你在上面的图表中看到的,这个函数代表了一个物体发出的【光】的波长,对应于其开尔文度的温度。例如,如果物体不够热,它会发出红外光,如果没有合适的检测设备,人类就看不到红外光。相反,温度在图表的“可见”条纹上的物体会产生我们可以感知为光的辐射。例如,红热的铁与中子星的颜色不同。当科学家试图用经典力学建立这个函数时,提出了两种主要方法; 瑞利-杰恩斯 定律和 维恩 定律,都未能准确预测相关性。其中一个在接*低波长值时趋于无穷大,另一个分别在波长接*较高值时趋于无穷大。

由于传统的物理学解决方案没有成功, 马普决定通过建立一个能够精确匹配大部分函数值的分布,然后解读其物理意义,从而在数学上解决问题。被认为是量子力学之父的普朗克得出了能量实际上是量子化的结论,这一物理学新分支因此而得名。简而言之,这意味着光(以及能量)被投射并吸收在被称为 量子 的微小【包】中,而不是像人们认为的那样连续不断。

能量等于普朗克常数 h 乘以原子振荡器的频率,再乘以整数 n

最后,在量子力学诞生后,由于像光电效应双缝实验这样的额外实验,自然开始被更好地理解。此外,它还鼓励理论发展,如 玻尔的 假设或 德布罗意的 假设。

基本原则

如果你想了解更多关于量子力学的起源,可以去参考资料部分。但现在,我们将探索量子计算实用部分背后的理论。

大量的知识是基于 电子 的行为,从速度到 自旋。 对这些参数进行建模并准确了解其属性对于在离原子如此*的距离进行操作至关重要,在这种情况下,人类的感知和偏差在解释实验和理论证明有时奇怪的结果方面发挥了有意义的作用。为了介绍这个已知的“奇怪”工作,让我们回答这个问题; 什么是电子?。 自从电子为人所知以来,它就被定义为与质子或中子一起构成原子的亚原子粒子。了解它们最令人兴奋的特征是它们的负电荷,与质子的正电荷相反,它让位于静电相互作用和亚原子力,如电磁力,负责将电子保持在原子核周围。

然而,当我们试图知道电子的位置、速度或角动量时,电子变得很奇怪,这也与流行的 自旋 或内在角动量有关,在这种情况下可以是向上或向下的。所以,如果我们问自己关于电子在空间中的位置或速度,我们将不得不处理 海森堡的 不确定性 原理,形式化为:

位置 x 和动量 p 的增量代表这些变量的不确定性。

因为动量是质量乘以速度

我们可以用两边的增量重写它

****

并在不确定性表达中进行替代。

上述公式将普朗克常数与位置和速度 v 和联系起来,告诉我们不能同时测量这两个变量,因为它们是 共轭(并由* 连接)在这种情况下,你会知道它的位置,但不知道它的速度,因为你无法确定它下一步会移动到哪里,所以你对速度有不确定性。相反,如果你增加相机的曝光时间,你将能够确定地知道它的速度,但这个时间位置将无法确定。*****

这种现象发生与 所有 可能的 共轭变量 我们可以从电子中提取,不仅仅是前面提到的那些;位置和动量,能量和时间,也就是说,这种无法知道像位置这样简单参数的想法似乎违反直觉,但我们必须记住,我们正在处理所有物质的最基本组成部分。宏观世界也受量子定律支配,但正如亚里士多德所说:

整体大于各部分的总和

以基本力学的解释来结束,重要的是要有这样的想法,即物质并不总是表现为粒子,而是表现为波。DeBroglie 在 1924 年提出了这个假设,其核心在于特定波长与粒子的关联取决于其动量(速度)。

波长取决于动量 p

这一个,相反,取决于速度 v

这个假设是通过像双缝实验这样的实验而成为可能的,在这个实验中,量子尺度的测量有着深远的影响。要知道为什么会发生这种情况,我们必须注意到,从本质上讲,电子处于叠加态,通过叠加态,它们可以同时位于属于空间特定区域的所有位置。只要我们不去测量,这个叠加态总是守恒的。如果我们试着观察它的实际位置,状态会坍缩,电子会将自己置于受其 波函数 影响的空间中的一点。

作者图片

正如你在上面看到的,波函数有一个具体的形状,它封装了在空间中的一个点上找到电子的概率(根据 玻恩法则 ) ,当找到电子的概率越大,找到电子的概率就越高,反之则越低。然后,在测量一个精确的位置并打破叠加态后,波函数坍缩成另一个函数,其峰值在检测到电子的点上。这个数学过程有几种物理解释,如 哥本哈根解释多世界 解释。

特定时间位置测量的概率密度函数是波函数的*方模数(玻恩法则)****

如波函数ψ**定义在复*面上,其*方模等于该函数乘以其 复共轭 。**

在空间中的两点 ab 之间找到电子的概率

知道电子的位置对于及时了解它的行为是必要的。但在此之前,我们需要找到它的波函数ψ(x,y,z,t)** ,而负责让那成为可能的形式模型是 薛定谔方程 。基本上,它是一个线性微分方程,用来得到一个量子系统的适当波函数,这个量子系统可以由单个亚原子粒子或整个复杂分子组成。这些系统中的每一个都会有不同的薛定谔方程的表达式,造成这种可变性的原因是 哈密顿算符 ,这个方程的内核:**

哈密顿算符 H 作用于波函数ψ等于虚数单位 i 乘以普朗克约化常数 h 乘以波函数对时间的偏导数。

哈密顿算符是一个物理算符,它将 机械能守恒 原理引入到方程中,工作方式类似于经典物理。

例如,如果我们有一个 原子作为一个量子系统,我们想知道其中一个电子的运动,则哈密顿量将是:

****

求解氢原子量子系统的结式薛定谔方程

相同的表达式,但扩展了 纳布拉运算符

由于这个表达式可以根据量子系统变得更大和更复杂,有几种方法可以得出波函数的合适解;请访问以下资源,详细了解物理学家如何解决这个问题:

经典计算

我们已经看到,经典计算正在逼*原子极限,带来了几个后果,例如, 量子隧道效应 。为了理解它的含义,让我们首先以最简单的形式定义什么是计算机。

作者图片

又称为通用 【图灵机】 ,计算机由一列包含有 离散 值的单元组成,正式表示为 01、 称为主 内存。 在这个存储器之外,一个头可以通过存储器切片,在一组指令(程序) 的指导下读取和修改单元的值,这些指令以二进制表示编码并存储在存储器中。

作者图片

但实际上,计算机的形式表示是由 冯诺依曼架构 带入现实的。的确,单元列表是由 RAMSSD/HDD 磁盘驱动器构成的。头部是一个处理单元 CPU (有时结合一个GPU进行可并行处理)双稳态组合 电路组成,能够执行简单的操作,如求和、比较数量、计数或寄存。同时,在文档中可以看到,这些电路包含了逻辑门, 布尔代数 的积木。 虽然,如果我们看得更接*原子尺度,我们会看到 晶体管 的组合代表逻辑门的工作。

作者图片

由于晶体管充当允许或阻止电流(电子)流动的屏障,有时电子行为的粒子波二象性导致电流泄漏,这一过程被表示为 【量子隧穿】 ,其中电子的叠加态允许它“同时”处于屏障的两侧,尽管一侧的概率值比另一侧低,如上所述。这种效果暗示了执行问题和数据损坏,大多用纠错算法解决,突出了计算机科学中的 汉明码作为这些算法的起点。**

量子计算

由于其硬件必须保持的 极端条件 ,量子计算中对强大纠错算法的需求明显大于其他计算机科学系统。

这些类型的计算之间的主要差别在于包含离散信息的每个存储单元的性质。虽然量子计算机使用相同的二进制数据表示,但它们主要利用 叠加纠缠 量子现象来扩展对处理NP复杂问题至关重要的信息处理能力,比经典计算机高效得多。

作者图片

简而言之,虽然经典位(存储单元)可以处于两种基本状态之一,但量子位(量子位)具有标记为 叠加 的第三种状态,其中它可以采用基本状态的任何线性组合,具有不确定的值,直到测量(打破叠加状态的****

作者图片

形式上,量子位的状态用 狄拉克符号 来描绘。 由此, 量子位|ψ⟩的状态由两个复系数α和β分别乘以基本 states|0⟩ and|1⟩的组合构成。因此,如果我们在一个称为 布洛赫球体 的球体上绘制这些信息,我们可以更好地了解量子位实际上是什么。

作者图片

可以推断,量子位离|0⟩越*,对应的系数越大,|1⟩.也一样因此,我们可以观察到,当打破叠加态时,这些数包含了获得特定基态的概率。

所有复系数的*方模的归一化概率

到目前为止,这种存储数据的方式似乎过于复杂。尽管如此,量子计算机在解决 NP-Hard 问题(如分解一个大数)时如此快速的关键是在一个量子系统中有多个量子位。尽管硬件问题的增加和对更鲁棒的纠错算法的需求,多量子位系统通过同时线性组合所有系统量子位的所有可能的基本状态值来利用它们的。所以有了 n 量子比特,你就有了 2n 系数(等价经典比特),它们都是确定量子系统叠加态所必需的。

量子态的张量表示

在下一节,量子算法将帮助你最好地理解叠加效用。

量子算法

几年前就已经知道,量子计算机能够用 肖尔算法 破解 RSA 加密协议,并且足够强大的计算机。但是, 为什么 在解决此类任务时是叠加键?。答案很简单;量子计算机可以通过叠加多个量子位来产生一个问题的所有可能的答案,并通过破坏性干扰叠加来丢弃【错误】的解决方案,就好像它们是 【波】

为了构建量子算法,类似于经典计算,我们需要通过在电路中顺序布置逻辑门,将初始状态的量子位转换为最终状态。

作者图片

上面,你可以看到大多数算法中的前导门。比如 X 门 就是逻辑否定的等价;同时, 哈达玛//C-Not门分别引入叠加和纠缠。你可以在 Qiskit 的 官方指南中了解更多关于它的工作原理和与古典门的关系的完整解释。然而,有必要强调的是,所有量子门都是可逆酉矩阵,使得量子计算过程 可逆 。此外,量子计算机是唯一可以用如此简单的电路生成真正随机数的设备:**

Hadamard 门产生叠加,得到 0 或 1 的概率为 50-50

扩展随机数生成器

作为超越经典计算的效率提升的一个例子,假设你有一个布尔函数 f(x) {0,1}→{0,1},你想知道它是 常数 还是*衡的。使用传统的计算机,你可以计算 0 和 1 中的函数,并比较结果,如果这个函数计算量不大的话。如果是,你可能需要使用 Deutsch 的算法 ,通过避免对函数的二次求值,来成倍提高你的程序的时间性能。

德语 问题量子电路。

形式上, 证明了 如果 f(x)不变,过程结束时上量子位的状态将是|0⟩,否则是|1⟩。但是,为了清楚地理解算法在做什么,我们可以把这个过程分成四个部分。首先,所有量子位都被初始化为|a⟩,并与 阿达玛 门|b⟩.设置成叠加态然后,充当一个 C-Not 门来遍历输入值的所有可能的组合,将该函数作为主成分添加到叠加处理|c⟩.中最后|d⟩,另一个 哈达玛 打破第一个量子比特的叠加,测量完成任务。**

量子机器学习

这一部分值得特别注意,因为它仍在开发中,并且根据问题类型有不同的方法。因此,这里我们将只关注神经网络的基础知识,这是不久的将来最突出的技术。

首先,我们需要知道机器学习和量子计算领域之间的交集根据数据和算法的类型产生了四种工作方法。

作者图片

根据图表,我们可以拥有 混合系统 ,其中经典和量子计算机共同工作,对数据进行编码,并优化机器学习模型。当然,量化一个或两个过程有它自己的优点。例如,如果您的经典模型太深并且在训练时遍历成本太高,您可能希望使用量子模型的 叠加 来提高执行时间,将其他任务(如参数调整)留给经典设备。

由于数据对于维持这一工作流程至关重要,因此大量的统计流程旨在从数据中提取模式。但是神经网络值得强调,因为它们在定理 通用逼*定理 中证明了优异的泛化能力。 简而言之,它们是可微分的数学模型,其中使用训练过程来修改它们的参数,使得模型符合输入数据集的模式。

作者图片

从上面这篇*的文章中可以看出,神经网络试图通过串联多层包含可变参数的 感知器【神经元】来模仿大脑的功能。然后,为了测试模型在拟合特定输入数据集方面的有效性,在每次训练迭代中计算并最小化成本函数,以实现一个“智能”模型,该模型稍后在训练数据之外执行推断。***

作者图片

在量子神经网络的情况下,主要的区别是为每个训练迭代计算模型,通过依靠量子 叠加 和纠缠来加速该过程及其泛化能力。在这里,你必须将经典数据输入到一个量子电路中,用来自|0⟩的【x】门块初始化量子位对数据进行编码。接下来,正确格式化的数据以叠加状态被馈送到可微分模型,并被经典地测量以获得用于最小化成本函数和调整模型参数 (θ)的输出。**

这是量子机器学习的 本质 的一个简化例子。不过既然是这么广阔的领域,你可以在这里继续了解

应用程序

从根本上说,人类几乎从诞生之日起就一直试图在经典计算机上模拟像原子这样的物理元素。然而,当处理具有许多分子的系统时,从计算的观点来看,这个任务变得指数地昂贵。由于这个原因,量子计算机已经开始用于各种研究项目,旨在忠实地模拟物质之间的相互作用,以获得新的知识,并开发在市场上具有优异性能的产品和新的通信方式,这将对我们的生活产生臭名昭著的影响。

关于气候变化和 环境保护 ,通过成功纳入量子物理定律来模拟一个物理系统意味着创造新材料,如石墨烯或任何其他其特性允许其完全回收的材料。例如,塑料是地球上产生残留物的主要材料,可以被另一种材料替代,这种材料的特性有助于其适当的再利用/分解,或者经过修改使其更具可持续性。此外,由于地球已经受到某种程度的污染,依赖经典机器学习模型对 废物进行分类 的回收厂将受益于使用 量子 模型。他们将提高效率、工作效率和准确性,避免废物堆积问题。

材料可能会对自然产生影响,但二氧化碳排放肯定是 温室效应 和气候变化的主要加速器成分之一。在这方面,通过解决【TSP】、** 等问题,量子计算机非常适合进行路线优化,从而减少车辆排放。然而,它们提供的自动化优势和高耐用性电池的可能发展是几乎所有行业最感兴趣的应用,因为它们将结束全球有害排放。所有这一切都假设少数产业 因那些改进【石油公司,以及许多其他公司】而受到伤害** 不会为了纯粹的经济目的而强行阻止技术的适当发展。****

结合其他应用,如推出新药、改进保健技术、治愈癌症、实现意识建模,以及更好地了解人类大脑和 DNA,像join us 4 the planet这样的倡议愿意使用颠覆性技术来提高对可持续消费的认识,并为旨在保护我们环境的技术发展做出贡献。**

结论

回到题目的问题,我们可以说量子计算可以对我们生活的几乎任何领域做出贡献,包括应对气候变化。此外,我们必须注意到,这是一种需要经典框架才能在解决复杂任务时获得有益结果的范式。因此,考虑到它是对仅适用于繁重进程的经典计算的补充,我们可以得出结论,仍然有许多工作要做,我们将不得不一起做,以便量子计算成为一个全面但安全、可理解和改变游戏规则的工具。

美国和中国能在人工智能安全方面合作吗?

原文:https://towardsdatascience.com/can-the-u-s-and-china-collaborate-on-ai-safety-f066731975d1

播客

Ryan Fedasiuk 谈及中国人工智能政策的可能性艺术

苹果 | 谷歌 | SPOTIFY | 其他

编者按:TDS 播客由 Jeremie Harris 主持,他是 Gladstone AI 的联合创始人。每周,Jeremie 都会与该领域前沿的研究人员和商业领袖聊天,以解开围绕数据科学、机器学习和人工智能的最紧迫问题。

众所周知,美国和中国是地缘政治对手。这种竞争延伸到人工智能领域也不是什么秘密,这是两国都认为具有战略重要性的领域。

但是,在每隔几周就有潜在变革性的人工智能能力被释放出来的背景下,其中许多都有助于具有巨大不稳定潜力的军事应用,你可能希望美国和中国能够达成强有力的协议,以应对由人工智能驱动的武器失灵引发的失控冲突升级等问题。即使在冷战最激烈的时候,美国和俄罗斯也有强大的沟通渠道来缓和潜在的核冲突,所以美国和中国现在肯定有一些至少一样好的东西……对吗?

好吧,他们不这样做,为了了解原因以及我们应该做些什么,我将采访乔治城大学安全和新兴技术中心的研究分析师兼新美国安全中心的兼职研究员 Ryan Fedashuk。瑞安最*为《外交政策》杂志写了一篇精彩的文章,概述了美中在人工智能安全方面合作的挑战和重要性。他和我一起谈论了美国和中国在建设安全人工智能方面的共同利益,如何看待对方,以及在这一集的 TDs 播客中,现实的中国人工智能政策是什么样的。

以下是我在对话中最喜欢的一些观点:

  • 鉴于人工智能霸权竞争中的竞争压力,美国和中国之间需要高度信任,以便双方就人工智能安全开发的规范和标准达成一致。这种信任目前并不存在。
  • 美国已经采取了几项单边措施来定义自己的人工智能安全和鲁棒性标准。这些包括国防部第 3000.09 号指令,该指令为自主和半自动武器的开发和使用制定了严格的规则。不幸的是,中国将这些措施解释为故意试图在联合国等论坛上抢先实施关于自主武器的国际法
  • 与此同时,中国有一些关于安全人工智能开发的规则,但不清楚这些规则是适用于中国军方,还是仅适用于私营部门。在无法确定谁应该遵守这些规则的情况下,并且鉴于中国不愿意澄清这一点,建立信任是困难的。
  • 瑞安提出了一个关于自主控制的明确红线:在 hist 看来,人工智能驱动的核武器发射应该被排除在外。瑞安指出,像中将·杰克·沙纳汉这样的著名美国军事人物已经明确表示自主核武器不在考虑范围内。但是没有一个中国政治或军事人物愿意公开做出同样的保证。
  • 目前还不清楚谁真正有能力影响中国的自主武器政策。个别研究人员似乎意识到了使用能力越来越强的人工智能系统来自动化越来越多的国防应用程序所带来的一些风险,但中国军方领导层没有意识到。
  • 中国似乎比其他国家做了更多的“安全清洗”。例如,中国军方将承诺不建造自主武器系统,而只建造符合自主定义的系统,这些定义非常狭窄,不适用于美国或中国甚至正在考虑部署的任何系统。(瑞安引用了中国保证不会制造缺乏召回能力的自主无人机的想法——这在很大程度上毫无意义,但却被高度宣传的让步,对解决中美两国实际上正在考虑的各种系统没有任何帮助。)

章节:

  • 零点介绍+免责声明
  • 2:15 中国作为核心焦点
  • 4:30 中国人工智能战略
  • 10:00 竞争即风险
  • 17:20 进行建设性对话
  • 22:20 了解中国政策
  • 27:15 对人工智能调整的共同兴趣
  • 32:45 在国际层面监管人工智能的问题
  • 40:15 协作是一件好事吗?
  • 44:15 高比例变压器模型趋势的影响
  • 47:15 总结

我们能用名字预测原产国吗?

原文:https://towardsdatascience.com/can-we-predict-country-of-origin-just-using-name-fb5126e2c128

多分类(63 类),自然语言处理,数据生成,拥抱脸,XLNet,论文与代码

克里斯汀·罗伊在 Unsplash 上的照片

动机:

银行和金融业正处于一个矛盾的阶段,一方面越来越受到金融法规的约束,另一方面又要适应区块链世界的快速发展。反洗钱正变得越来越复杂,至少可以说,人工智能正在*行发展以解决这些问题。成功的反洗钱模型依赖于人类专业知识和某种人工智能的结合,包括机器学习。来源国是更好地对这些 AML 模型进行风险评分的主要特征之一,需要从 swift 报文、交易详情等中提取这些信息。

注:

你可以在下面的最后找到本文中使用的所有 colab 笔记本。

数据准备:

这是找到真实数据集的关键部分,我们可以从中找到代表整个国家的名称。在我短暂的研究中,没有与此相关的数据集。下一步是从维基百科的个人通用名列表中收集数据,如这些。最*我从这个视频中偶然发现了数据准备工具的重要性。我将简要概述一下我在数据准备过程中遇到的困难,这可能会有所帮助。

使用 faker 生成名称:

Faker library 用于创建虚假数据,这些数据是从公开可用的数据中收集的,当涉及到名字时,它们是从维基百科的常用名和其他名字来源中收集的。下面的代码用于生成来自 63 个不同国家的大约 50 万个名字。

  • The first hurdle is that the names that are generated are in their own languages, we need to convert the names to English (or common language) for generating tokens for model training (ex: 黒澤明 -> Akira Kurosawa). The library that can be used for this is googletrans which uses googletranslate API.
  • 第二个障碍是,如果我们翻译每个名字,API 太慢了(大约 15 小时)。为了解决这个问题,我将所有相同的国家名称转换为一个列表字符串,并附加到数据框中,这样可以大大减少时间(63 个列表不到 2 分钟)。
  • 接下来是第三个障碍,对于每个 API 调用可以输入的列表字符串的长度有一个阈值,必须少于 5000 个字符。为了解决这个问题,我们需要使用下面的代码拆分列表字符串并追加,这需要大约 10 分钟的运行时间。

你可以在这个链接找到我上传到 Kaggle datasets 的数据集。

预训模特选择:

为了解决这个 NLP 分类问题,首先,我们需要从几个可用的预训练语言模型的巨大生态系统中选择用于分类的预训练模型的类型。对于模型的准确性、训练速度(硬件依赖性)、模型的训练方式、架构(自回归或自动编码或两者都有)、模型的大小(部署原因),总是需要进行折衷。

我选择 XLNet 作为预训练语言模型,因为它利用了我们问题的预训练目标,获得了自回归(AR)和自动编码(AE)语言建模的最佳效果。在我们的案例中,硬件依赖性不是问题,因为我们将使用 colab TPUv2 (8 个 TPU 内核,每个内核 8GB)。模型的大小不是问题,但有很好的替代方案,如 google ELECTRA,它的大小非常小,具有很好的胶合分数,它使用生成器和鉴别器架构来进行训练。选择 XLNet 的另一个原因是它在分类问题上的表现,我参考了代码文本分类的论文,得出结论选择 XLNet。

模型搭建:

我将使用 TensorFlow XLNet 模型通过 huggingface 进行训练。首先,使用 XLNetTokenizer 对名称进行标记化,并通过预先训练的模型运行结果标记,以优化相对于标记的权重。可以使用以下内容定义模型架构:

模特培训:

首先需要初始化 TPU,然后为了有效地使用 TPU,并使用由 TPU 实验室提供的所有工人和核心,我们需要通过使用以下代码初始化 TPU 系统,以初始化在模型构建中使用的 TPU 策略对象。

结果:

在 30 个时期和大约 110 分钟的训练时间之后,我们已经获得了 90.73 的训练准确度、74.02 的验证准确度和 74.67 的测试准确度,这些数据是关于大约 50 万个名字和 63 个类别的整体数据的未见过的数据。本文的管道可以在下面的 colab 链接中找到:

https://colab.research.google.com/drive/1yloybl3-h3JDTyZKdqDBGzHUb7MqcCDw?usp=sharing

你可以在不到两分钟的时间内从下面的 colab 笔记本中输入你选择的全名来测试模型。

https://colab.research.google.com/drive/1G0dvq1VeGnMSF9uGsIzB04RViZwqMiip?usp=sharing

名称国家预测示例

参考资料:

我们能把文字转换成地理信息吗?

原文:https://towardsdatascience.com/can-we-transform-words-into-geographies-51259ae04b2a

以非常规方式在地图上放置文本

来自维基百科的欧洲边界文本。图片作者。

MAP 一般到处都有标签。这些有助于我们识别代表的是什么。告诉我们事物是什么的文本片段,从餐馆或街道的名称到国家的名称。然而,一个标签并没有嵌入到地图中,严格来说,它只是链接到一个“地理位置”,并给我们关于它的信息(例如,意大利边界内部写的“意大利”一词)。但是如果我们改变了呢?

如果你想一想,当你输入一个字符时,你的计算机会用几条(贝塞尔)曲线连接一些点,并把它们画在像素网格上。然后这个网格就会呈现在你的屏幕上。定义每个字形的点只是保持它们之间的相对距离,这让我们能够理解正在书写的字符。从技术上讲,我们可以给这些点分配一些坐标,然后把它们想象成可以在地图上可视化的“地理”。

来源:wikimedia.org(根据 CC 许可)

从想法到实施

这比一开始看起来要简单得多。 freetype 库允许我们从字体文件中提取每个字形的轮廓。我们迭代文本中的字符,遍历每个字符的轮廓,形成 Linestring 对象( shapely 库)。然后我们将它们合并成一个多线串。最后,我们将所有的点投影到地图上,将文本集中在期望的坐标上。这里需要技巧的是将每个字符放在前一个字符旁边,并考虑字符之间的间距。

下面是一小段代码,它根据给定的坐标、比例尺和倾斜度在地图上绘制文本。依赖项可以安装在:

pip install cartoframes shapely freetype

如果您运行这段代码,您将得到下面的交互式地图。

按作者分类的地图

进入下一个阶段

好吧,但是这样做有什么意义呢?常规标签还不够好吗?是的,它们对于 99.9%的用例来说已经足够好了。但是请原谅我。让我告诉你如何扭曲文本,并使其遵循自定义路径。

按作者分类的地图

我们在这里做什么?基本上,我们首先生成一条点的路径(左边是一条螺旋,右边是一条希尔伯特曲线),然后我们将文本分成块,然后调用一个新的函数来拟合两点之间的每个块。例如,如果我们有一条路径,包含点 A、B 和 C 以及文本“Hello World ”,我们首先在 A 和 B 之间放上单词“Hello ”,然后在 B 和 C 之间放上“World”。我认为这非常简洁,因为您可以选择任何一个。tff 字体文件,并使用它来快速生成地理图(请注意,不仅底图和颜色不同,第二张地图的字体也不同)。

为了更进一步,我们可以取一个普通的 LineString,分解它并使用它作为插入文本的路径。为了得到一个好的结果,对线串进行插值是很重要的,这样每一对点(A,B)的间距是均匀的。

下面你可以看到第一次环球航行幸存者的名字,这次航行是由十六世纪的费迪南德·麦哲伦领导的。在 1519 年 8 月开始探险的 256 人中,只有 18 在三年多后活着回到🫢(剧透一下,麦哲伦不在其中)

按作者分类的地图

最后,我与您分享另一张地图(点击!)我把欧洲国家的边界和它们各自的维基百科页面。你可以把每个国家的边界看作是对自己的描述🤯。

这里的是我为这篇文章写的代码,如果你想看的话。

图片作者。点击它可以看到实时地图!

弱标记能代替人类标记的数据吗?

原文:https://towardsdatascience.com/can-weak-labeling-replace-human-labeled-data-bfd06766a816

弱监管与全监管的逐步比较

斯科特·格雷厄姆Unsplash 上拍照

*年来,由于深度学习模型的出现,自然语言处理(NLP)取得了重大进展。从智能聊天机器人到从非结构化文档中自动提取数据,使用 NLP 的现实应用越来越普遍,并为许多公司带来了真正的商业价值。然而,这些模型仍然需要手动标记的训练数据来微调它们以适应特定的业务用例。收集这些数据可能需要几个月的时间,标记这些数据甚至需要更长的时间,尤其是在需要领域专家并且文本中有多个类需要识别的情况下。可以想象,这可能成为许多企业的真正采用障碍,因为主题专家很难找到,而且价格昂贵。

为了解决这个问题,研究人员采用了弱形式的监督,例如使用启发式生成的标签函数和外部知识库来以编程方式标记数据。虽然这种方法很有希望,但与完全监督相比,它对模型性能的影响仍不清楚。

在本教程中,我们将从职位描述中生成两个训练数据集:一个通过弱标记生成,另一个通过使用ubai手工标记生成。然后,我们将比较模型在 NER 任务中的表现,该任务旨在从职位描述中提取技能、经验、文凭和文凭专业。数据和笔记本都在我的 github repo 里。

监管不力

在弱监督的情况下,用户定义一组函数和规则,将有噪声的标签(即可能不正确的标签)分配给未标记的数据。标记功能可以是模式的形式,例如正则表达式、字典、本体、预先训练的机器学习模型或群体注释。

弱监督流水线具有三个组件:(1)用户定义的标签函数和启发式函数,(2)统计模型,其将来自函数的标签作为输入,并输出概率标签,以及(3)机器学习模型,其在来自统计模型的概率训练标签上被训练。

作者图片

Skweak

在本教程中,我们将使用 Skweak 库执行弱标记。根据库创建者[1]:

Skweak 是一个基于 Python 的软件工具包,它使用弱监督为这个问题提供了一个具体的解决方案。 [skweak](https://github.com/NorskRegnesentral/skweak) 是围绕一个非常简单的想法构建的:我们定义了一组标注函数来自动标注我们的文档,然后聚集它们的结果来获得我们的语料库的标注版本,而不是手工标注文本。

要了解更多关于 skweak 库的信息,请阅读原文“ skweak:弱监管使 NLP 变得容易”。

标签功能

为了执行弱标注,我们将编写一组函数,对与我们想要标注的语料库相关的字典、模式、知识库和规则进行编码。在本教程中,我们将添加一些功能,这些功能将从职位描述中自动标记实体技能、经验、文凭和文凭 _ 专业。将这些函数应用于未标记的数据后,将使用 skweak 库提供的统计模型将结果聚合到单个概率注记图层中。

首先,我们将创建一个 skills Skills_Data.json 字典,并将其添加到我们的函数 lf3 中来注释技能实体。该词典是从公开可用的数据集中获得的。

*#Create_Skills_Function_from_dictionary*
**import** spacy**,** re
**from** skweak **import** heuristics, gazetteers, generative, utils
tries**=**gazetteers**.**extract_json_data('data/Skills_Data.json')
nlp**=**spacy**.**load('en_core_web_md' , disable**=**['ner'])
gazetteer **=** gazetteers**.**GazetteerAnnotator("SKILLS", tries)
lf3**=** gazetteers**.**GazetteerAnnotator("SKILLS", tries)

对于经验实体,我们使用正则表达式模式来获取经验的年数:

*#Create_Function_Foe_Experience_Detection(Use Regex)*
**def** experience_detector (doc):
    expression**=**r'[0-9][+] years'
    **for** match **in** re**.**finditer(expression, doc**.**text):
        start, end **=** match**.**span()
        span **=** doc**.**char_span(start, end)
        **if**(span):
            **yield** span**.**start , span**.**end ,  "EXPERIENCE"
lf1 **=** heuristics**.**FunctionAnnotator("experience", experience_detector)

对于实体 DIPLOMA 和 DPLOMA_MAJOR,我们使用来自 Kaggle 和 regex:

**with** open('Diploma_Dic.json' , 'r' , encoding**=**'UTF-8') **as** f :
    DIPLOMA**=**json**.**load(f)

print(len(DIPLOMA))
**with** open ('Diploma_Major_Dic.json' ,encoding**=**'UTF-8') **as** f :
    DIPLOMA_MAJOR**=**json**.**load(f)*#Create Diploma_Function*
**def** Diploma_fun(doc):
    **for** key **in** DIPLOMA:
                **for** match **in** re**.**finditer(key , doc**.**text , re**.**IGNORECASE):
                    start, end **=** match**.**span()
                    span **=** doc**.**char_span(start, end)
                    **if**(span):
                        **yield** (span**.**start , span**.**end ,  "DIPLOMA")

lf4 **=** heuristics**.**FunctionAnnotator("Diploma", Diploma_fun)*#Create_Diploma_Major_Function*
**def** Diploma_major_fun(doc):  
    **for** key **in** DIPLOMA_MAJOR:
                **for** match **in** re**.**finditer(key , doc**.**text , re**.**IGNORECASE):
                    start, end **=** match**.**span()
                    span **=** doc**.**char_span(start, end)
                    **if**(span):
                        **yield** (span**.**start , span**.**end ,  "DIPLOMA_MAJOR")

lf2 **=** heuristics**.**FunctionAnnotator("Diploma_major", Diploma_major_fun)*#Create_Function_For_diploma_major(Use Regex)*
**def** diploma_major_detector (doc):
    expression**=**re**.**compile(r"(^.*(Ph.D|MS|Master|BA|Bachelor|BS)\S*) in (\S*)")
    **for** match **in** re**.**finditer(expression, doc**.**text):
        start, end **=** match**.**span(3)
        span **=** doc**.**char_span(start, end)
        **if**(span):
            **yield** span**.**start , span**.**end ,  "DIPLOMA_MAJOR"

lf5 **=** heuristics**.**FunctionAnnotator("Diploma_major", diploma_major_detector)

我们将所有函数聚合在一起,并使用 Skweak 的统计模型来找到自动标记数据的最佳协议。

*#create_corpus_annotated_to_train_the_model*
docs**=**[]
**with** open('Corpus.txt' , 'r') **as** f :
    data**=**f**.**readlines()
    **for** text **in** data:
        **if** (len(text) **!=**1):
            newtext**=**str(text)
            doc**=**nlp(newtext)
            doc**=**lf1(lf2(lf3(lf4(lf5(doc)))))
            print(doc**.**spans)
            docs**.**append(doc)**from** skweak **import** aggregation
model **=** aggregation**.**HMM("hmm", ["DIPLOMA", "DIPLOMA_MAJOR" , "EXPERIENCE" , "SKILLS"])
docs **=** model**.**fit_and_aggregate(docs)

我们终于准备好训练模型了!我们选择训练 spaCy 模型,因为它很容易与 skweak 库集成,但我们当然可以使用任何其他模型,例如变压器。github repo 中提供了带注释的数据集。

**for** doc **in** docs:
    doc**.**ents **=** doc**.**spans["hmm"]
utils**.**docbin_writer(docs, "train.spacy")**!**python -m spacy train config.cfg --output ./output --paths.train train.spacy --paths.dev train.spacy

现在,我们已经准备好使用两个数据集来运行训练,完全手工标记和弱标记,具有相同数量的文档:

手工标注数据集模型性能:

**================================== Results ==================================**

TOK     100.00
NER P   74.27 
NER R   80.10 
NER F   77.08 
SPEED   4506  

 **=============================== NER (per type) ===============================**

                    P       R       F
DIPLOMA         85.71   66.67   75.00
DIPLOMA_MAJOR   33.33   16.67   22.22
EXPERIENCE      81.82   81.82   81.82
SKILLS          74.05   83.03   78.29

弱标签数据集模型性能:

**================================== Results ==================================**

TOK     100.00
NER P   31.78 
NER R   17.80 
NER F   22.82 
SPEED   2711  

 **=============================== NER (per type) ===============================**

                     P       R       F
DIPLOMA          33.33   22.22   26.67
DIPLOMA_MAJOR    14.29   50.00   22.22
EXPERIENCE      100.00   27.27   42.86
SKILLS           33.77   15.76   21.49

有趣的是,人工标记数据集的模型性能明显优于弱标记数据集,有监督数据集的模型性能为 0.77,弱监督数据集的模型性能为 0.22。如果我们深入挖掘,会发现性能差距在实体层面依然成立(体验实体除外)。

通过添加更多的标注功能,如人群标注、模型标注、规则、字典,我们当然可以预期模型性能的改善,但还不清楚性能是否会与主题专家标注的数据相匹配。第二,找出正确的自动标记功能是一个迭代和特别的过程。当处理高度技术性的数据集(如医疗记录、法律文档或科学文章)时,这个问题变得更加严重,在某些情况下,它无法正确捕获用户想要编码的领域知识。

结论

在本教程中,我们演示了由弱标记数据和手动标记数据训练的模型性能之间的逐步比较。我们已经表明,在这个特定的用例中,弱标记数据集的模型性能明显低于完全监督方法的性能。这并不一定意味着弱标记没有用。我们可以使用弱标注对数据集进行预标注,从而引导我们的标注项目,但我们不能依赖它来执行完全无监督的标注。

在 Twitter 上关注我们 @UBIAI5订阅这里

参考文献:

  1. https://github.com/NorskRegnesentral/skweak

你能只用高中数学编写一个神经网络吗?

原文:https://towardsdatascience.com/can-you-code-a-neural-network-using-only-high-school-mathematics-ac9ad80f52f7

回到基础——仅使用 NumPy 编码神经网络

最*,我在向母亲解释神经网络的概念;我无法超越通常的输入、隐藏层、输出等比喻。我不认为我在这方面做得很好,这让我思考——我自己理解他们吗,还是我只是假装理解他们?

我拿了一张纸,画了几张相连网络的图表,在一个 jupyter 笔记本上用 Keras 层对整个网络进行了编码。我用了不到 10 行代码编写了一个多层网络,并且运行良好。

虽然我不满意,但感觉我只是在假装我理解这个概念,如果我继续深入下去,我肯定会犹豫。我已经无数次地阅读了神经网络的工作原理,并且可以肤浅地解释它们是如何工作的,还可以快速得出一些数学公式,但是我真的理解它们以便我可以清晰地向任何人解释吗?这个问题在那天晚上成了众矢之的。我决定回到基本原则,只用高中数学来编码整个自然网络。没有瞎忙活,没有使用 PyTorch、TensorFlow 或 Keras 的任何花哨框架。

这应该是可行的!

埃利斯·陈嘉炜在 Unsplash 上拍摄的照片

潜在的想法对我来说很清楚,但我不清楚我将如何从基础开始编码。

所以,首先,让我们在一张纸上涂鸦,首先是想法,然后把想法转化成数学方程,然后尽可能简洁地把数学编码成程序。

好吧,我已经想出了一个粗略的计划来解决这个问题。

基本想法

我相信你们都曾经见过类似的事情。

作者图片

在最简单的神经网络中,有一个输入层、一个隐藏层和一个输出层

如果没有隐藏层,这种排列将被称为感知器,类似于弗兰克·罗森布拉特在 1943 年设计的 Mark 1。

用一句话来解释,神经网络背后的思想是,给定一个输入 X,我们能否做一些数学计算来正确识别 X 或将 X 分类到预定义的类别之一?就像我们的大脑一样。

简单看一下算法

让我们以一个单层网络为例,输入 X1,X2;隐层神经元 H1,H2;和输出 Y1、Y2。

图 1(作者图片)

您估计权重 w1、w2、w3 等,计算每个节点的输出,并最终根据 Y 的真实值测量最终输出 Y。Y 和 Y之间的差异是您用来重新调整权重和计算新 Y 的误差;继续这样做,直到你得到一个接* Y 或者令你满意的 Y值。

这是所发生事情的淡化版本;实际上,还有更多的步骤。一个更复杂的算法:

  1. 获取输入 X
  2. 估计重量——这是一个完全不知情的过程,人们可以随机初始化重量。
  3. 估计偏差项的值。
  4. 前向传递(前馈)—在初始化权重的帮助下,估计每个节点的输出。
  5. 计算最终输出(y 值)。
  6. 找出误差-网络的估计值减去您想要观察的实际值。
  7. 反向传递(反向传播)—借助计算出的误差,重新调整权重。
  8. 不断重复步骤 3 至 6,直到达到所需的值。

从线性到非线性

这是一个回归方程,是输入和不同权重(betas)的线性组合。

对于图 1,可以看到 H1 的输出是:

H2 的输出是:x1w3 + x2w4 + b1 等等。

在任何时候,它都是权重和输入的线性组合。如果我们只在神经网络的不同节点获取输出,它将只不过是一组复杂的线性回归。为了避免这种情况,我们需要引入非线性,这就是激活函数的作用。有许多流行的函数,如 sigmoid、tanh、softmax、ReLu 等。

在神经网络中引入激活函数有助于:

  1. 使模型非线性化。
  2. 模仿大脑中的神经元;当满足某些阈值条件时,大脑中的神经元就会激活;激活函数给神经网络的功能带来了类似的阈值。
  3. 在 0 和 1 之间映射输出。

一旦我们应用激活函数,例如 sigmoid,节点处的输出将是:

一旦计算了所有节点的所有这些值,网络将估计最终输出,并计算期望值和观察值之间的误差。

数据

数据集:MNIST 手写数字。

来源:http://yann.lecun.com/exdb/mnist/

http://www.pymvpa.org/datadb/mnist.html MNIST 手写数据集的许可——知识共享许可

正向传播

有 42,000 张图像,每张图像的尺寸为 28 x 28 = 784 像素,需要将它们分为 10 类——0、1、2、3、……9。

作者图片

各层的大小很重要,需要预先计算。

转化为数学

方程组 1(图片由作者提供)

网络的第一层是输入层 X,向其添加权重和偏差的线性组合,以获得线性输出 z。

在激活函数的帮助下,第一层的输出 Z 是非线性的,这里使用 ReLU。

ReLU 的非线性输出被馈送到网络的第二隐藏层,在第二隐藏层中添加了 softmax 非线性,并且最终产生输出 A2。

Softmax 会将输出向量转换为概率向量。

一个旁注,在【https://latexeditor.lagrida.com/】上写乳胶很容易

反向传播

到目前为止,我们一直在前进。

一旦我们得到了输出,我们将测量误差并进行反向传播。

为什么要反向传播?

最初随机选择的权重在反向传播期间被调整。权重决定了特征的重要性:权重的数值越高,特征就越重要。

最初,我们并不真正知道分配什么权重,所以我们从随机权重开始,但反向传播作为一种校准机制,有助于更新权重,并使网络和整个系统达到*衡。

计算以下偏导数,它们有助于确定 Z、b 或 W 等特征的微小变化如何影响误差函数。

作者图片

以上是为了计算权重和偏差的误差,从而更新它们。

方程组 3(图片由作者提供)

这看起来像是一大堆数学。简而言之,我们将使用以下等式更新权重和偏差:

新权重=旧权重—学习率总误差相对于该权重的偏导数*

例如,在图 1 中,如果我们想要更新权重 5,那么

方程组 4(图片由作者提供)

我们知道总误差,我们只需要对误差函数进行 w5 的部分微分,但是因为误差函数中没有任何 w5 项,我们将使用导数链规则来找到各个偏导数,并将它们相乘以获得相关值(正如我们在上面的方程组 4 中所做的)

观看 3B1B 视频更多了解反向传播微积分如何工作。

数学够了,我们来编码吧!

从回购中读取数据后,让我们将其分为训练集和测试集

一个非常重要的步骤是使训练和测试集正常化,否则训练将会很慢或者根本无效。

X_train = X_train/255.
X_test = X_test/255.

根据上面的方程组 1,让我们初始化参数并为正向传递定义一个函数

至此,已经定义了正向传递的一次迭代,让我们定义反向传播的方法,并根据方程组 2 和 3 更新参数。

让我们定义一个梯度下降方法,该方法将调用其中的向前传递和向后传递方法,并最终更新参数。

第一次运行达到了 88%的准确率

各种迭代的准确性

在测试集上使用训练过的学习者显示了一些错误的分类,但大部分是正确的。

输出

在 PyTorch、Keras 或 TensorFlow 的帮助下,通过一些调整可以达到 95%的精度,而在这里,通过基本的数学知识,我们可以达到 88%。没那么糟糕!

更重要的是,我们能够计算出正向传播和反向传播数学,正确地将它们编码,并将其应用于真实世界的示例数据集。

代码库在我的 GitHub repo 上。

如果你知道一些更简单的反向传播方法(或更直接的解释),那么请留言或联系。

你能超越坏数据吗?

原文:https://towardsdatascience.com/can-you-out-architect-bad-data-d1d3d8cf0106

即使是设计最精良的数据*台,系统也会崩溃。不接受风险,你就是在玩火。

图片由 Unsplash 上的丹尼尔·赫雷斯提供。

跟我说:破数据在所难免。

它不关心您在编写 dbt 测试时有多主动,您的数据建模有多完美,或者您的架构有多健壮。重大数据事件的可能性(空值?错误的模式更改?失败的模型?)回荡在整个公司的声音总是潜伏在角落里。

这并不是说像数据测试、验证、 数据契约 、领域驱动的数据所有权和数据区分等东西在减少数据事件方面没有起到作用。确实如此。事实上,我已经写了很多关于新兴的数据质量最佳实践的文章,比如数据 SLA断路器不可变/语义仓库模式变更管理数据资产认证,以及向数据网格发展。

图片由作者提供;经约翰·斯坦梅茨许可,重新发布。

正如任何数据从业者都会告诉你的,将技术投入到问题中并不是保证数据质量的灵丹妙药,但是它们可以被用作防范一些不太明显的问题的护栏,否则这些问题将无法通过区分或测试来发现。

用我们现场可靠性工程前辈的睿智的话来说:“拥抱风险”

原因如下。

我们能战胜不可避免的事情吗?

在 dbt Labs 首席执行官 Tristan Handy 的一篇很棒的文章中,他提供了几个有用的建议,用于解决当软件工程师推送影响紧密耦合系统中数据输出的更新时出现的上游数据问题。

这是对我们之前关于数据契约的一篇帖子的有价值的回应,这些类型的见解也是为什么我们很高兴他能在我们即将举行的影响力大会上发表。虽然我同意他试图尽可能多地防止数据事件击中 prod 的总体论点,但我会对他的一个论点提出异议:

或许,这些期望之一应该是:不要做出破坏下游内容的改变。

对你来说,这和对我来说一样明显吗?与其建立检测和警告系统崩溃的系统,不如建立不会崩溃的系统。”

我并不是要挑出特里斯坦(他定期写信与社区分享他的专业知识,并将加入我们在 IMPACT 的创始人小组!)因为这是潜伏在相当一部分数据从业者潜意识里的思想。“也许我可以做得很好,让坏数据永远不会进入我的管道,我的系统永远不会崩溃。”

这是一个乐观且诱人的提议。毕竟,数据工程师,以及更广泛的数据分析专业人员,需要有某种“排除万难完成任务”的心态,才能决定首先进入这个行业。

我们每天必须处理的临时请求的数量和多样性对我们不利;我们必须管理的桌子越来越大,规模也越来越大,这对我们不利;在了解我们公司日益增长的数据需求的复杂性方面,我们处于不利地位。

但是最好的数据工程师和领导者明白两件事:1)他们无法预测数据事件发生的所有方式,2)这些事件的后果变得更加严重

这篇文章将列举一些为什么你不能在架构上超越坏数据的原因,然后看看数据专业人士可以从网络安全、物流和软件工程等其他行业如何解决这个问题中收集到什么(提示:这不是通过构建完美的系统或拥有完美的代码)。

为什么您不能超越坏数据

尽管即使是最完美的体系结构也会受到坏数据的影响,原因有很多,但我将使用现实生活中的例子列出一些最常见的原因。

人为误差

没有一个架构是完美的,因为这些系统涉及到人,而人是不完美的。犯错是人之常情。这些类型的事件可能是由手动数据输入过程中的一个简单错误导致的,也可能是将坏代码转移到生产环境中。

我知道贵公司没有人使用 Excel,这些数据不可能进入您的完美系统,也从未有过过于匆忙的问答导致带有错误代码的批准的拉取请求,但请相信我,这在现实世界中是会发生的。

例如,一家在线零售公司在产品中发布了一个用户无法结账的 bug,幸运的是这个 bug 在 45 分钟内被发现并解决了。另一家营销技术公司增加了可迭代测试,其中包括休眠用户,并看到他们在客户数据*台上的消费(和产生的成本)一夜之间增加了*两倍。一个在线语言学习*台向他们的 ETL 管道添加了一个有问题的过滤器,阻止了他们的表以正确的速度增长。

所有这些问题,如果没有被某种形式的自动化所捕获,将会产生负面的后果,并且没有一个是最良好架构的*台所能避免的。

第三方依赖关系

向约翰·多恩道歉,没有一个系统是孤立的。您的数据生态系统可能包含许多不受您控制的数据源。这可以是不太成熟的第三方合作伙伴,他们没有点击发送他们的每周报告,甚至是您最大的合作伙伴中最复杂的自动化系统。

例如,一家媒体公司首先意识到他们的在线视频合作伙伴(一家服务于数百万用户的技术巨头)的数据馈送发送文件比*时晚了四个小时,这导致 ETL 作业无法正确更新。

例如,一家游戏公司注意到他们的新用户获取数据存在偏差。他们做广告的社交媒体*台改变了他们的数据时间表,所以他们每 12 小时而不是 24 小时发送一次数据。该公司的 ETL 被设置为每天只获取一次数据,因此这意味着突然有一半发送给他们的活动数据没有得到处理或传递到下游,使他们的新用户指标偏离“付费”而转向“有机”

因此,即使你已经设计了完美的流程来防止自己系统中的上游数据问题(这是一个相当大的壮举,因为正如我们在上面所展示的,即使是最大的科技公司也会有问题),你也需要你所有的合作伙伴都是完美的。祝你好运。

新举措

正如我们的一位同事喜欢说的,“数据就像时尚,永远不会结束,它总是在不断发展。”即使在一个假设的世界中,您已经完成了您的架构杰作的最后一笔,也会有来自业务的新需求和用例需要变更。

即使有了最好的变更管理流程,当你开辟新的领域时,也会有尝试和错误。挑战极限(伟大的数据团队就是这样做的)会降低安全边际。

例如,一家在线零售公司正在努力降低其分析数据集中的数据延迟,从 30 分钟减少到 5 分钟。他们有许多可移动的部分,包括复制 mySQL 数据和 Kafka 流。在该计划中,有一个问题导致了一次全面的中断,影响了填充在管理层控制面板中的 100 多个表。

另一个例子是金融公司采用了一种涉及加密货币的新金融工具,这种工具要求数据类型是浮点型而不是整数型,这与他们所有其他更传统的工具不同。

测试只能到此为止

我已经几次提到,不可能预测坏数据进入管道的所有方式。说得好听点,这就是为什么测试(或者说数据断路器实际上是在测试类固醇)将只捕捉到大约 19%的数据问题

图片由作者提供。

例如,一家物流公司有一条通往其最重要的战略客户的重要管道。他们用流水线上的 90 多条独立规则一丝不苟地监控质量。然而,一个问题仍然设法绕过他们所有的测试,导致 50 万行丢失,一堆空值像杂草一样冒出来。

其他行业的可靠性经验

那么如果不良数据的挑战不可避免,解决方案是什么?简短的回答是更多,更智能的自动化。

对于更长的答案和解释,让我们看看我们在网络、物流和软件工程领域的同事是如何处理类似问题的。

网络安全专家认为安全漏洞是不可避免的

你不会发现任何可信的网络安全供应商承诺完美的安全性,或者他们会消除所有风险。就连他们的营销人员也知道没有这回事。

挑战是一样的。人是会犯错的,防御必须完美,而攻击者(在我们的例子中是坏数据)只需成功一次,就可能造成负面后果。你的系统的强度取决于你最薄弱的环节。

那么他们会怎么做呢?他们尽可能构建最好的分层防御,然后将资源投入到事件解决和缓解中。他们制定了计划来了解不可避免的违规行为的影响,并将这种影响降至最低。

在数据方面,我们需要确保我们不仅投资于预防,还投资于检测、警报和解决方案。例如,就像网络安全专业人员了解违规如何蔓延到邻*系统至关重要一样,数据工程师需要了解一个数据集中的坏数据如何影响下游的其他资产,包括 BI 仪表板和使用它们的消费者。

物流最佳实践关注整个价值链

制造业和物流业是最早将质量保证提升到今天水*的行业之一。

在这些行业中,每 100 万个事件中有 3.4 个缺陷被认为是有效的(我们的数据显示在他们的环境中,*均组织每年每 1,000 个表有 70 个数据事件,因此按照这一标准,我们仍然有办法继续我们的质量之旅)。

精益六适马起源于 20 世纪 80 年代,是一种在提高质量的同时消除浪费和缺陷的方法,至今仍广受欢迎。一个常见的 LSS 实践是“价值链分析,它涉及到监控为产品增值的核心业务活动。

看到数据工程的等价物并不需要丰富的想象力:

  • 入库物流=提取
  • 操作=转换
  • 出库物流=装货
  • 营销和销售=采用计划、数据素养、建立数据信任
  • 服务= 数据自助服务,临时分析/报告

价值链分析的关键是不仅要理解和监控每个阶段的主要和次要活动,而且要理解它们之间的联系,这被认为是该过程中最困难的部分。这表明数据工程师需要对他们的系统(不仅仅是数据仓库)进行端到端的自动监控,并确保他们拥有更新的数据血统,以了解跨系统的联系。

软件工程师投资自动化监控

软件工程是数据工程,尤其是数据可靠性工程发展的*乎完美的类比和路线图。

有三个主要趋势导致软件应用程序用“5 个 9”(99.999%的正常运行时间)来衡量其可靠性。

一是更好的合作。一旦他们的应用程序开始运行,开发人员就被蒙在鼓里,导致同样的错误重复发生,因为开发人员缺乏对应用程序性能的了解,并且不知道在出现问题时从哪里开始调试他们的代码。

解决方案是广泛采用的 DevOps 概念,这是一种新的方法,要求在软件部署和开发过程中开发人员(Dev)和运营(Ops)团队之间进行协作和持续迭代。

DataOps 是一个将数据工程和数据科学团队合并起来以支持组织的数据需求的学科,与 DevOps 帮助扩展软件工程的方式类似。与 DevOps 如何将 CI/CD 应用于软件开发和运营类似,DataOps 需要一种类似 CI/CD 的自动化优先方法来构建和扩展数据产品。

第二个趋势是专业化。网站可靠性工程师和 谷歌的 SRE 手册 的兴起,通过改进专业知识、最佳实践和问责制来提高可靠性。随着数据可靠性工程师的崛起,我们看到这种专业化开始出现。

第三个趋势是增加对应用程序性能管理和可观察性的投资。DataDog 和 New Relic 等可观测性供应商的爆炸性增长充分说明了有价值的软件工程师如何继续寻找这些解决方案来实时监控异常并发出警报。随着时间的推移,这种投资只会增加(即使他们的应用程序比我们的数据产品可靠得多),当一个团队没有在可观察性上投资时,这是很奇怪的。

我们的软件同事会告诉你,提高可靠性不仅仅是采用新的解决方案。也不在于建立了完美的系统。它是人(SREs)、过程(DevOps)和技术(可观察性)的结合。

没有必要免费索罗

作者站在巴塔哥尼亚臭名昭著的塞罗托雷山前。(没有她没有爬上去)。图片由巴尔·摩西提供。

组织在您的数据团队中所做的所有投资都是为了在数据的驱动下做出更好的决策。您的数据需要高度可靠,以建立信任、加速采用和扩展数据优先文化,否则这些投资将成为开销,而不是收入来源。

这是一座难以攀登的高山,但没有必要“自由单飞”。这就是监控、数据可观察性、沿袭和其他覆盖数据质量基础的自动化方法可以派上用场的地方。

放自己一马也无妨。相信我:你的股东会感谢你的。

想法、感觉、情绪?伸手到领英上 巴尔摩西

癌症预测与电信流失建模

原文:https://towardsdatascience.com/cancer-prediction-vs-telecom-churn-modelling-9e4ec49095f6

应用机器学习

癌症预测和流失建模之间的相似性

图片来自 Shutterstock 作者 tadamichi

介绍

在过去的一年里,我一直在一家电信公司工作,担任数据科学家。电信部门有许多机器学习的应用,其中一个主要应用是客户流失预测。准确预测客户流失有助于提高客户保持率并增加企业收入。虽然流失预测看起来与癌症预测无关,但这两个问题实际上可以非常相似地解决。在本文中,我将介绍为这两种应用程序设计功能和训练机器学习模型的最佳方法。

那么什么是流失呢?

顾客是指停止使用/支付服务的人。我们的目标是预测未来的客户流失,这样我们就可以针对我们预测会流失的用户开展有针对性的营销活动。减少客户流失和提高客户保持率将增加企业中普通客户的终身价值,从而增加公司的收入。

理想情况下,我们希望尽可能预测未来的客户流失。我们对未来的预测越多,营销团队就越容易做出反应和组织营销活动。然而,我们预测的未来越远,我们的预测就越不准确。我们需要在模型的性能和结果的有用性之间取得*衡。

癌症预测的类比

为了解释客户流失建模,我将从不久前收到的一个案例面试问题开始:

比如有一种非常致命的癌症,如果治疗得足够早,是可以治愈的。然而,当癌症在常规医疗检查中被诊断出来时,已经太晚了。如果在通常通过常规诊断发现之前 1-3 个月发现,那么它很容易治愈。

您可以访问一个医疗保健数据集,其中包含全国许多患者的医疗记录。该数据集还包含每个患者每月的血液检查,以及他们是否被诊断患有这种癌症。

你将如何着手建立一个模型来及时预测这种癌症?

按作者分类的表格(虚构数据,按作者分类的图像)

这可能是一个很好的时机,让你停下来,想想如何在这里建立一个模型。这是一个在数据科学面试中出现的问题,在我的数据科学职位中也出现了非常类似的情况。

构建数据集

最终目标是建立一个分类器,该分类器接收关于患者及其血液测试结果的信息,并预测他们是否患有未诊断的癌症(将在接下来的 1-3 个月内通过每月体检诊断的癌症)。

数据集非常大,以千万计。数据是关系型的,因此像 SQL 这样的语言是最合适的。数据集可以通过左连接通过“患者 ID”列进行连接。这些查询可以通过云服务来执行,比如 Google 的 Big Query。

为了训练分类器,我们需要建立训练数据集。每个数据点将显示每个患者的每月血液测试结果。数据点必须标记为正或负。在接下来的 1-3 个月内将被诊断为癌症的患者的结果为阳性。阴性是指没有患癌症的人。

图 1:患者每月检查数据(作者图片)

在上面的图片中,我展示了两个病人的数据。患者 1 在 5 月被诊断出患有癌症,患者 2 从未被诊断出患有癌症。我们需要一个模型,将患者 1 在 2 月、3 月和 4 月的血液测试分类为阳性,并将患者 2 的所有血液测试结果分类为阴性:

图 2:图 1 中所示的训练数据点被分成负的、正的和未使用的(图片由作者提供)

在训练数据中不使用患者 1 被诊断患有癌症(May)的血液测试,因为它已经太晚而无法治愈。我们希望在这些数据点被标记为阳性之前 1-3 个月预测癌症。请注意,患者 1 在 1 月份的血液检测被标记为阴性,因为他在 4 个月后才检测为阳性。

请注意,我们是如何将每位患者的每次血液测试作为不同的训练数据点。我们不能简单地使用最*一个月诊断出的癌症数据。这些数据是不充分的,因为只有极少数患者会被诊断患有癌症。训练这些模型已经是一项艰巨的挑战,因为产生的数据集将非常不*衡。我们需要在训练数据中尽可能多的癌症阳性,以提高模型性能,因此我们需要尽可能远地收集数据。

我们限制预测未来的原因很简单,因为我们越往前看,预测者就越弱。我们希望建立一个强大而稳健的模型,如果它预测某人患有癌症,我们几乎可以肯定该预测是正确的。

特征工程

如果我们只看最*的血液测试数据,我们可能会错过过去几个月结果变化的模式。例如,白细胞计数等一些变量的稳定下降可能是一个预测因素。在训练数据中捕获这一点极其重要,并将极大地影响模型的性能。

特征工程变量可能包括:

  • 重要变量的变化率
  • 过去几个月重要变量的*均值
  • 过去几个月变量的方差
  • 二元变量,如:在过去几个月中,钠水*是否曾< 145 mmol/L
  • 分类变量的一种热编码
  • 标度连续变量

包括什么特性在很大程度上取决于用例以及数据集。数据科学家需要利用数据中的模式,了解潜在的问题,并找到最佳特征来支持机器学习模型。

电信客户流失建模

希望通过阅读上面描述的癌症预测模型,你可以想象它是如何应用于客户流失模型的。

我们试图预测客户在未来 1-3 个月内是否会流失。我们需要时间来针对他们进行营销,并防止他们出现混乱。我们收集他们与出售给他们的服务的互动,而不是每月的血液测试。这可能包括他们使用该服务的次数、用途等。

流失建模和癌症预测之间的主要区别在于流失建模是非常季节性的。模特每个月都需要重新培训,否则,她们会表现不佳。

下一个巨大的区别是,在电信领域,这些特征通常以数据流的形式出现,而不是像血液测试那样一个月一个数据点。这些数据流仍然需要转换成唯一的数据点。这些数据点通常对应于一个月中的某一天,在这一天,模型将被定期重新训练和执行。

训练和评估预测模型

这两个问题产生的数据集将会非常不*衡。这是因为大部分患者不会得癌症,大部分用户不会流失。处理不*衡分类的方法包括例如加权分类和欠采样。还有其他的,但是从我的经验来看,这些效果最好。

根据所得数据集的大小,不同的监督学习分类器可能工作得最好。对于较小的数据集(数万行或有时数十万行),由于这是表格数据,集成方法将工作得最好。集成方法包括决策树、boosting 方法等。

遵循标准的机器学习最佳实践技术非常重要,例如:

  • 规范化和标准化/最小最大缩放连续变量
  • 一个热编码离散变量
  • 列车测试拆分
  • 对于变动,保存最*的变动(一个月左右)以供验证

为了改进模型,您应该调整模型以增加测试数据的结果。这可能涉及进一步的特征工程、超参数调整等。完成后,您需要评估验证数据集上的模型,以便很好地了解您在生产中可以预期的性能。

就客户流失建模而言,最有用的指标是 ROC AUC 得分。接收器工作特性(ROC)是真阳性率对假阳性率的曲线图。它与我在本文的中详细解释的精确度和召回率密切相关。

图 3 精确召回曲线与 ROC AUC 得分(作者提供的图片)

在上面的图片中,我回收了以前文章中的一个模型的结果。在左边你可以看到两个模型的精确召回曲线。在右边你可以看到大鹏鸟。通过改变阈值,我们能够改变每一类的精确度和召回率。

同样,通过改变分类器的阈值,我们可以获得不同的真阳性率和假阳性率。ROC 对所有可能阈值(从 0 到 1)的积分给出了曲线下面积(AUC)。完美的模型将具有 1 的积分,即使在最低的假阳性率下也具有完美的真阳性率。对于纯随机模型,ROC 曲线将是一条对角线,AUC 为 0.5(1 乘 1 *方的一半面积为 0.5)。良好的 ROC AUC 分数将取决于应用和数据,但是越接* 1,分数越好。

该度量很好地比较了机器学习模型的总体泛化能力。该度量对于任何种类的不*衡分类问题都非常有用,因此它对于癌症预测模型和流失模型都是合适的。

结论

早期癌症检测和流失预测看似不相关,但实际上这两个问题几乎可以同时解决。在本文中,我将讨论如何为这些挑战构建一个机器学习模型。我还讨论了如何构建数据集,这可能是问题中最难的部分。它需要大量的特征工程和预处理。机器学习只能做这么多,但最终,模型的质量将在很大程度上取决于你提供给它的数据和功能的质量。最后,我将讨论评估这些分类器的最佳方式,并推荐 ROC AUC 评分。希望这篇文章能启发你训练自己的模型,并通过使用数据提供有意义的见解!

支持我

希望这对你有所帮助,如果你喜欢,你可以跟我来

您也可以使用我的推荐链接成为 中级会员 ,并访问我的所有文章及更多:https://diegounzuetaruedas.medium.com/membership

你可能喜欢的其他文章

支持向量机

精准 vs 召回

典范分解——一种被遗忘的时间序列协整方法及其他

原文:https://towardsdatascience.com/canonical-decomposition-a-forgotten-method-for-time-series-cointegration-and-beyond-4d1213396da1

将多个时间序列分解为静态和趋势关系。从头开始完整的 Python 代码。

作者图片

在处理多个非*稳时间序列时,协整是最重要的概念之一。对于那些不熟悉协整的人来说,简单来说,两个或多个时间序列的协整意味着它们之间存在一个*稳的线性组合。

非*稳时间序列的问题是我们不能直接应用大多数统计工具,因为这些工具是为*稳数据设计的。然而,当存在协整时,我们拥有所有的统计机器来制作有意义的模型,并从我们的数据中获得进一步的见解。

两个著名的协整方法是 Engle-Granger 方法和 Johansen 方法。在这两种方法中,实践中最常用的是 Johansen 方法。原因是恩格尔-格兰杰方法往往会产生虚假回归,但这超出了目前的故事。可以说 Johansen 方法是大多数统计软件包中最常用的协整方法。

然而,在 Johansen 的第一篇关于协整的开创性论文(1988)之前 11 年,Box 和 Tiao 提出了一种称为规范分解的方法,我们将简称为 BTCD。这种方法很有趣,因为它不仅能发现协整关系,还能发现时间序列之间的趋势关系。事实上,有研究表明 BTCD 比约翰森方法更稳定,然而 BTCD 似乎已经被遗忘了。

在这个故事中,我将解释 BTCD 背后的基本数学,并用 Python(从头开始)编写一个库,供你在下一个时间系列项目中使用。希望我们能让 BTCD 在协整方法中恢复其应有的地位。

问题设置

我们要找到一个权重向量 w 来形成多个时间序列的线性组合,得到的时间序列 S 应该是*稳的。设 p 为一个 n 维向量,表示一个 n 维时间序列(聚合多个 1D 时间序列),形式上:

基础数学

BTCD 的基础是 Box 和 Tiao 定义的可预测性的概念。为了说明这个概念,让我们考虑一维时间序列 p 的情况。考虑一个模型 q 在时间 t: 预测 p

其中,ε是均值为零的 i.i.d。模型 q 具有来自 k 先前滞后、 t -1、 t -2、…、 t - k 的信息,即

在最简单的情况下,模型 q 是使用 AR(1)规范构建的,这是一个线性模型,其中 k =1。但是保留 q 作为 p 的抽象模型更方便。

对第一个等式的两边求*方,并取我们得到的期望值:

Box 和 Tiao 将可预测性定义为:

我们可以看到,当可预测性较大时 p 优于ε,这意味着模型 qp 之间的残余噪声较小,因此 p 是可预测的。当可预测性较小时,ε支配 p,意味着 p 几乎是具有*稳均值和方差的纯噪声。

现在我们转向 n 维情况,其中 pq 和ε现在是 n 维向量。类似于一维情况:

其中 T 上标表示转置,注意以上等式中的所有项都是矩阵。

为了获得分解关系,我们使用权重向量 w 将可预测性表示为:

使用矩阵:

我们现在的问题是找到 w 使得可预测性λ最小/最大。

为了解决优化问题,我们引入了一个巧妙的变量变化:

(最后一个等式来自于 B 的*方根是对称的这一事实)并且

在此变量变化下,我们的权重向量为:

我们可以将可预测性表示为标准瑞利商:

这很好,因为优化问题现在是矩阵 D 的特征值问题。对应于最小/最大特征值的特征向量最小化/最大化λ。

事实上,如果我们将所有特征值从最小到最大排序,它们相应的特征向量(乘以“V”的*方根倒数)将生成从最不可预测(*稳)到最可预测(趋势)的时间序列。这就是为什么 Box 和 Tiao 把这种方法命名为“规范分解”。

编码规范分解

我们需要解决的第一件事是如何计算二元乘积的期望值,因为矩阵 AB 将需要这个。设 v 为一个 n 维向量(1D 数组)如果有 vt 个观测值,这个数据可以表示为一个矩阵 V (2D 数组),其中每行代表一个观测值,即形状为( tn )

那么二元乘积的期望值可以从样本中估计出来,如下所示:

对此进行编码:

现在我们可以求解 B 的*方根的倒数,其中“p_mat”是向量 p 的观测值矩阵。

然后我们需要计算出 A ,为此我们需要建立模型的方式q .【Box】和 Tiao 使用普通最小二乘(OLS)回归。然而,我们在这里允许一些灵活性,并且只为这样的模型建立一个接口。

如果你仔细看看这个接口,它正是我们都喜欢的 Scikit-learn 的接口,由 Scikit-learn 和许多其他库的所有回归模型实现。如果您想使用具有不同接口的不同库,您可以用 fit 和 predict 方法将它包装在一个类中,效果也一样。

接口而非具体实现背后的想法是能够以灵活的方式切换回归模型,而无需对 BTCD 的实际代码进行任何更改。Box 和 Tiao 使用了 OLS,但也可以尝试一些不同的东西,比如弹性网,甚至是非线性模型?这取决于你的实验。

在下面的代码中,为了可读性,参数 k 将被重命名为“max_lag”。

为了得到模型 q ,我们需要使用所有时间序列的滞后版本(直到“max_lag”)来预测每个时间序列的下一步。换句话说,对于每个时间序列(“p_mat”的每一列),我们需要形成一个包含滞后时间序列的特征向量“X”,然后形成由 X 的 T-max _ 滞后观测值组成的矩阵 X,X 的形状为(T-max _ 滞后,最大 _ 滞后 n)。回归 y 的目标只是去掉了第一个“最大滞后”值的时间序列,所以它的形状是(T-max 滞后)。*

现在求特征值问题的解

现在,将实现我们刚刚编码的所有内容的函数,即,将被调用来进行实际分解的函数,以及用于规范分解结果的数据类:

现在让我们为 BTCD 把它放在一个干净的图书馆里。

合成时间序列示例

让我们生成 n 个离散采样的相关布朗运动,以生成我们的“p_mat”用于演示目的。要生成此类数据:

现在我们生成数据:

作者图片

给定“p_mat ”,其中每一列是一个时间序列,我们的权重向量 w ,结果时间序列 S 可以用以下公式计算:

最后是使用 Sklearn OLS 实现“RegressionModel”接口的规范分解的例子,没有截距,max_lag=1

作者图片

为了说明使用另一种类型的回归是多么容易,现在让我们使用 Sklearn 的 elastic net 并将 max_lag 设置为 9。

作者图片

最后的话

我们已经成功地为 BTCD 编码了一个库,在我看来,它填补了大多数时间序列统计库中的一个空白。现在你除了 Engle-Granger 和 Johansen,又多了一个协整分析的工具。

为回归模型创建接口而不是直接对其进行编码的主要目的是让您使用和微调最适合您的数据的回归模型。然而,如果不想修补这一点,那么使用 OLS 作为框和条原本打算。

协整方法和 BTCD 的主要问题是它们容易过度拟合。这就是为什么对数据使用合适的回归模型很重要。如果你没有注意到我们使用相关随机行走(离散布朗运动)作为例子,事实上,这种数据的分解是过度拟合的证明。任何布朗运动的非*凡线性组合(即使它们是相关的)都是一个重新标度的布朗运动,它既不是*稳的,也不是趋势性的,因此这种组合不存在协整。这是非常重要的,尤其是当协整被用来做预测时,比如在统计套利交易应用中。遗憾的是,这些方法中的大多数会过度拟合数据,但是希望你能找到最适合你的回归模型。

可预测性值λ依赖于回归模型,因此在用不同回归模型计算的λ之间没有有意义的比较,因此它不是一个绝对的度量,仅在使用相同回归模型时对比较有意义。

参考

[1] G. E. P. Box 和 G. C. Tiao,对多个时间序列的规范分析 (1977 年),Biometrika 64,2,第 355-65 页

[2] R .,D. Orden,M. Yang 和 L. Fisher,【VEC 协整向量的 Box-Tiao 和 Johansen 规范估计量的比较(1)模型 (1994),计量经济学杂志 64,3–27

我希望这个故事对你有用。如果我错过了什么,请让我知道。如果你想知道更多这样的故事,请关注我的媒体

*https://medium.com/subscribe/@diego-barba

喜欢这个故事吗?通过我的推荐链接成为媒体会员,可以无限制地访问我的故事和许多其他内容。

https://medium.com/@diego-barba/membership *

在 TensorFlow 中捕获训练状态

原文:https://towardsdatascience.com/capturing-a-training-state-in-tensorflow-7d643e3fb20b

如何调试培训项目中出现的 NaN 损失

艾伦·埃梅里在 Unsplash 上的照片

之前的帖子中,我们试图为 TensorFlow 中的调试任务提供一些支持,这项任务通常很困难,有时不可能,而且总是令人抓狂。这个博客包括了一个描述,我认为这是现代机器学习开发者潜在痛苦的终极例子——NaN 损失的突然出现。在我们所说的被诅咒的场景中,在一段时间(可能是延长的)成功的模型收敛之后,模型损失突然被报告为NaN——“不是一个数字”,并且模型状态(权重)变得灾难性的和不可修复的被摧毁。我们提出了一种创新的方法,用于在权重被破坏之前捕捉模型训练的精确状态。在假设训练步骤不包括随机性(例如,没有丢失层)的情况下,该方法允许以可靠和有效的方式再现和调查导致 NaN 的问题。(我们将在下面的训练步骤中回到随机性的可能性。)

从那时起,我们开发了一种额外的技术来捕获模型的训练状态,这种技术可以扩展到原始方法没有解决的场景。这篇文章的目的是分享这种技术,并与它的前辈进行比较。

我们将从简要回顾问题和以前的解决方案开始。为了简洁起见,我将不再详述调试 NaN 损失的独特挑战和我们提出的原始解决方案,而是建议您阅读以前的帖子。接下来,我们将描述原始解决方案的一些局限性,并介绍一种解决这些局限性的新技术。最后,我们将简要演示一个使用这种技术的示例调试流程(基于一个真实的故事)。

这篇文章将包括几个代码块,演示如何对我们讨论的解决方案进行编程。代码基于 TensorFlow 版。和往常一样,你应该对照你读到这篇文章时的最新版本来验证代码和我们提出的一般论点。

调试 NaN 损失可能很困难

虽然调试通常很难,但有许多原因使得调试 TensorFlow 中出现的 NaN 损失尤其困难。

符号计算图的使用

TensorFlow 包括两种执行模式,急切执行图形执行。虽然在急切执行中训练时更容易调试,但是图模式有许多的好处,使得训练明显更有效。图形模式的缺点是很难调试。具体来说,在图形模式中,您预先构建模型,并将其发送到训练设备(例如 GPU)。在训练过程中,您不能随意访问和测量计算图上的张量。显而易见的结论是,我们应该总是在图模式下训练,在急切执行模式下调试。然而,这种策略的先决条件是能够容易地捕获精确的训练状态以用于再现和调试。

重现训练状态的挑战

为了完全重现一个训练步骤,我们需要知道三件事:模型的状态、精确的数据输入批次,以及控制训练步骤中包含的任何随机性的种子。

捕获模型状态 : TensorFlow 提供了将其模型权重和优化器变量的值保存到文件中的工具(例如TF . keras . model . save _ weights)。在培训期间定期捕获这些内容是一个很好的做法(例如使用 tf.keras ModelCheckpoint 回调)。
理想情况下,我们希望能够捕获导致 NaN 损失的精确模型状态。这将简化和缩短复制。不幸的是,在典型的场景中,一旦报告了 NaN 的损失,模型状态就已经被破坏了。在我们之前的帖子中,我们展示了一种在权重被不可逆转地混淆之前发现 NaN 损失的方法。在这篇文章中,我们也将采用类似的技术。

捕获数据输入:由于tensor flow TF . data . datasets的顺序性质,一旦训练批次通过训练步骤,我们就不再能够访问它。在我们的上一篇文章中,我们演示了一种捕获训练批次以进行复制的机制。

捕捉控制随机性的种子:随机性是机器学习训练算法的固有属性,往往是它们成功的关键。与此同时,随机性可能是复制 bug 的噩梦。克服这个挑战的一个方法是显式地设置和捕获决定随机性的特定种子(例如TF . random . generator . from _ seed)。

我们先前提案的局限性

在我们之前的文章中,我们提出了一种方法,包括定制 tf.keras.model 类的 train_stepmake_train_function 例程,以便:

  1. 将输入批处理保存为一个渴望的(可访问的)张量,
  2. 在将梯度应用于模型权重之前,测试 NaN 值的梯度,
  3. 终止训练并保存上次输入批次和当前权重。

这种方法有两个明显的局限性:

在分布式训练中捕获状态:在其当前形式下,该方法不支持使用 tf.distribute 分布策略捕获在多个内核上运行的训练会话的状态。

捕捉 TPUs 上的状态:该方法要求部分训练步骤在 tf.function 范围之外运行,以捕捉输入数据。TPU 不支持此方案。

在下一节中,我们将描述一种克服这些限制的新技术。

捕获训练状态的新建议

我们的新提案包括四个部分:

1.一辆 tf.keras.models.Model 带定制 train_step

2.一个定制的 TensorFlow 输入数据捕获层,我们将其附加到输入数据管道的末端。

3.将数据集预取值设置为 0(以确保最后一批数据不会被覆盖)。

4.标准的TF . keras . callbacks . termin ate onnan回调。

自定义训练步骤

我们定制训练步骤来测试 NaN 梯度,然后将它们应用于模型权重。如果发现了一个 NaN 梯度,我们明确地将损失度量设置为 NaN 并且而不是更新权重。 NaN 损失将由TF . keras . callbacks . termin ate onnan回调识别,培训将停止。与我们之前的解决方案相反,定制训练步骤完全在 tf.function 范围内。下面的代码块演示了自定义训练步骤。突出显示了默认训练步骤的主要变化。

import tensorflow as tf
from tensorflow.python.keras.engine import data_adapterclass DebugModel(tf.keras.models.Model):
  def train_step(self, data):
    data=data_adapter.expand_1d(data)
    x, y, sample_weight=data_adapter.unpack_x_y_sample_weight(data)
    # Run forward pass.
    with tf.GradientTape() as tape:
      y_pred=self(x, training=True)
      loss=self.compiled_loss(
         y, y_pred, 
         sample_weight, regularization_losses=self.losses)
    grads_and_vars=self.optimizer._compute_gradients(
              loss, var_list=self.trainable_variables, tape=tape)
    grads_and_vars=self.optimizer._aggregate_gradients(
              grads_and_vars)
    self.compiled_metrics.update_state(y, y_pred, sample_weight)
    # Collect metrics to return
    return_metrics = {}
    for metric in self.metrics:
      result = metric.result()
      if isinstance(result, dict):
        return_metrics.update(result)
      else:
        return_metrics[metric.name] = result
 **def t_fn():
      # if any of the gradients are non, set loss metric to NaN       
      return tf.constant(float('NaN'))
    def f_fn():
      # if all gradients are valid apply them
      self.optimizer.apply_gradients(
               grads_and_vars,
               experimental_aggregate_gradients=False)
      return return_metrics['loss']
    grad_nan=[tf.reduce_any(tf.math.is_nan(g)) for g,_ in  
              grads_and_vars if g is not None]
    grad_nan=tf.reduce_any(grad_nan)
    return_metrics['loss'] = tf.cond(grad_nan,
                                     t_fn,
                                     f_fn)**    return return_metrics

数据输入捕获层

之前的帖子中,我们介绍了一种独特的方法,使用自定义的 tf.keras.layers.Layer 来捕捉图张量的值,该方法将张量值分配给可访问的渴望张量。我们将这种张量捕捉层应用于数据输入管道的末端(在预取之前),以便在输入数据批次进入训练步骤之前捕捉它们。

class TensorCaptureLayer(tf.keras.layers.Layer):
  def __init__(self, shape, dtype, **kwargs):
    super(TensorCaptureLayer, self).__init__(dtype=dtype, **kwargs)
    self.record_tensor_var = tf.Variable(shape=shape,    
             initial_value=tf.zeros(shape=shape, dtype=dtype),
             validate_shape=False,
             dtype=dtype,
             trainable=False)
  def call(self, inputs, **kwargs):
     self.record_tensor_var.assign(inputs)
     return inputs# Here we demonstrate a case where the input consists of a 
# single batch of 32 frames and a single batch of 32 labels
class DataInputCaptureLayer(tf.keras.layers.Layer):
  def __init__(self, **kwargs):
    super(DataInputCaptureLayer, self).__init__(**kwargs)
    self.frame_input = TensorCaptureLayer([32,512,512,3],tf.float32)
    self.label_input = TensorCaptureLayer([32],tf.int32)
  def call(self, x, y, **kwargs):
    self.frame_input(x)
    self.label_input(y)
    return x,y

将数据集预取设置为零

tf.data.Dataset.prefetch 例程是一种常用的技术,用于简化训练流程和最大化资源利用率(参见此处)。但是,如果我们将预取设置为一个大于零的值,我们将冒着用即将到来的批处理而不是当前批处理填充 DataInputCaptureLayer 的风险,破坏我们捕获导致 NaN 丢失的输入批处理的能力。

使用终端启动回调

我们使用标准的 TerminateOnNaN 回调来监控损失,如果是 NaN 则终止训练。

下面的代码块演示了我们是如何把它们放在一起的。

dataset=...
capture_layer=DataInputCaptureLayer()
dataset=dataset.map(lambda x,y:capture_layer(x,y))
dataset=dataset.prefetch(0)strategy=... 
with strategy.scope():
  model = DebugModel(inputs=..., outputs=...)
model.compile(loss=..., optimizer=...)
model.fit(dataset, epochs=..., steps_per_epoch=...,
          callbacks=[tf.keras.callbacks.TerminateOnNaN()])if model.stop_training: # set by callback in case of Nan
  # capture the model state
  model.save_weights('model_weights.ckpt')
  # capture the last data batch
  np.save('frm.npz',capture_layer.frame_input.record_tensor.numpy())
  np.save('lbl.npz',capture_layer.label_input.record_tensor.numpy())

再现的失落

有了现在的训练状态,我们就可以开始复制 NaN 的失败。使用TF . keras . model . load _ weights例程可轻松恢复模型状态,并使用自定义层将输入数据注入管道,如下所示:

class InputInjectionLayer(Layer):
  def __init__(self, **kwargs):
    super(InputInjectionLayer, self).__init__(**kwargs)
    self.frames=tf.convert_to_tensor(np.load(frm.npz))
    self.labels=tf.convert_to_tensor(np.load(lbl.npz))
  def call(self, x, y, **kwargs):
    return self.frames,self.labelsdataset=...
inject_layer=InputInjectionLayer()
dataset=dataset.map(lambda x,y:inject_layer(x,y))
dataset=dataset.prefetch(0)strategy=... 
with strategy.scope():
  model = DebugModel(inputs=..., outputs=...)
model.compile(loss=..., optimizer=...)
model.fit(dataset, epochs=1, steps_per_epoch=1)

与以前解决方案的比较

与我们之前提出的解决方案相反,该解决方案可用于调试在分布式培训设置中遇到的 NaN 丢失,也可用于 TPU。

值得指出的另一个区别是两种解决方案所需的资源。在第一种解决方案中,数据批次作为训练步骤的一部分存储在训练工作者的存储器中(例如 GPU)。在第二种解决方案中,数据批次被存储为在主机 CPU 上运行的数据输入管道的一部分。根据您的工作负载,数据批处理可能需要大量内存,存储数据的资源可能会影响您构建模型的方式(例如,最大批处理大小)。

最后,不同的解决方案可能会对您的训练步骤时间产生不同的影响。每个解决方案的性能损失在很大程度上取决于模型的属性。例如,如果您有一个 CPU 瓶颈,那么添加数据输入捕获层可能会比我们原来的解决方案有更高的代价。另一方面,如果您的 CPU 没有得到充分利用,损失可能会更低。对于我们的模型(通常相对较大),我们没有注意到两种解决方案的惩罚有显著差异,两者都很低(<10%).

Solution Extensions

A number of enhancements can be made to this solution to extract more debug information and/or extend its coverage to additional use cases. Here we will note a few of them.

使用 tf.print 提取更多信息当检测到 NaN 坡度时,我们上面实现的定制训练步骤只不过是将损失设置为 NaN 。但是,我们可以使用 tf.print 实用程序来增强收集和打印附加诊断信息的功能。例如,我们可以打印梯度张量的完整列表,以及它们是否包含 NaN 值。这可能会为问题的根源提供一些线索。值得注意的是(在撰写本文时), TPU 还不支持 tf.print,因此尽管捕捉更多信息仍然是可能的,但它需要更多的创造力(例如,使用自定义指标)。

支持备选数据加载方案 在我们分享的示例代码中,我们假设数据分发由 tf 分发策略管理,并且使用默认的数据加载机制。但是,该解决方案可以很容易地扩展到其他数据加载方案。例如,如果 Horovod 用于数据分发,那么每个训练核心(例如 GPU)都有其自己的单独数据集,所需要的就是让每个 Horovod 进程将其输入数据保存(并注入)到唯一的文件路径。如果 tf 分配策略distribute _ datasets _ from _ function一起使用,则单个数据集管道可以为多个训练核提供数据。在这种情况下,可以使用队列机制来增强 InputDataCaptureLayer,以捕获多个数据批次(根据单个数据集管道提供的训练核心的数量)。

支持在进样期间修改批次大小 有时,您可能希望尝试在可用内存少于训练设备的环境中进行重现和调试。为此,您可能需要使用较小的输入数据批量运行。我们上面描述的注入层可以很容易地修改,以支持将捕获的训练批次大小的数据分成更小的批次。当然,总有可能问题是由整批引起的,不会在任何较小的批次上重现。如果您采用这种技术,请确保使用原始模型重量测试每一批次,即不要在批次之间更新重量。

再现随机操作 一些模型包括由随机变量控制的层。一个经典的例子是漏失层,一种正则化技术,它将一部分输入单元随机设置为零。在某些情况下,你可能会怀疑你的损失是由随机变量的不幸组合造成的。在当前形式下,我们描述的解决方案不能解决这种情况。当然,您可以多次运行 train_step,并希望问题能够重现,但这并不是成功的秘诀,尤其是在您有大量随机变量的情况下。这里有两种方法可以解决这个问题。首先是捕捉每个随机层的随机变量。例如,对于下降图层,您将捕获设置为零的单位。第二种选择是为每个随机层和每个训练步骤明确设置 RNG 种子(例如,作为迭代的函数)。这些选项在理论上都很简单,但在 TensorFlow 中实现需要一点掌握和创造力,这超出了本文的范围。

调试流程示例

为了让您更好地了解如何使用这个解决方案,我将描述一个基于真实用例的典型调试流程。

我们最*的任务是在 TPUs 上训练一个相当大的计算机视觉模型。(该模型没有包括随机层,如 dropout。)训练没有按计划进行。早期,我们发现几乎一半的训练实验都以 NaN 的失败告终。此时,我们开始在上面描述的捕获模式下运行我们的实验,使用定制的 DebugModel 代替默认的 keras 模型,并使用 DataInputCaptureLayer。幸运的是,这使我们能够很容易地捕捉到导致 NaN 失败的训练状态。捕获数据的批量大小为 1024。因为我们对在 CPU 环境中调试感兴趣,所以我们致力于识别一个大小为 1 的子批次,该子批次再现了 NaN 损失。在 CPU 上,我们使用 tf.gradients 显式计算特定变量的梯度,使用 tf.print 打印诊断结果。通常这是在定制层内完成的。(有时我们会扩展一个标准层并覆盖它的调用函数,以便包含这些操作。)我们还利用急切执行模式,遍历图表并分析张量值。通过这种方式,我们能够识别出导致 NaN 梯度的精确操作。我们已经实现了一个定制的损失函数,其中包括对 tf.norm 的调用。结果是,在特定的数据样本和捕获的模型状态上,范数运算的值为零。由于范数运算在零处不可微,我们非常恰当地得到了 NaN s。(解决这个问题的一种方法是分解范数 op,并在执行*方根运算之前添加一个小ε。)

在另一种情况下,我们使用相同的技术来调查我们在 GPU 上使用混合精度时的 NaN 损失。我们发现 tf.float16s 的使用导致了损失计算的溢出。我们迅速将损失函数设置为使用 tf.float32,问题得到了解决。

这种方法无论如何也不能保证成功。在上面的轶事中,我们可能会在几个步骤中运气不佳。我们可能无法在较小的批量上重现该问题,我们可能会发现该问题仅在 TPU 上重现(例如,由于使用了 bfloat16)或仅在图形模式下重现,或者该模型可能依赖于随机层。在任何一种情况下,我们都需要从头开始,看看如何改进我们的方法来应对我们面临的挑战。

摘要

这篇文章讨论了在 TensorFlow 中训练的更困难的挑战之一,即 NaN loss 的出现。我们提出了一种重现和调试这一问题的新技术。正如我们所展示的,这项技术可以很容易地实现。虽然它不能解决所有可能的情况,但当所有其他方法都失败时,还是值得一试。你不觉得吗?

利用决策树分类器进行汽车评估分析

原文:https://towardsdatascience.com/car-evaluation-analysis-using-decision-tree-classifier-61a8ff12bf6f

Python Sklearn 中决策树分类器的顺序编码

概述

购买前评估汽车状况在决策中起着至关重要的作用。手动地将状况良好或可接受的车辆与状况不可接受的车辆进行分类是费时费力的。我们可以利用机器学习技术来开发汽车评估的自动系统,因为 ML 在解决分类相关问题方面已经显示出有希望的结果。在这个项目中,我们将分析汽车的不同物理条件,并随后根据汽车的属性帮助/推荐用户进行决策。

汽车评估数据集从 UCI 机器学习库收集,数据源(创建者)是 Marko Bohanec【1】。它包含具有 7 个属性的 1728 个汽车样本信息,包括一个告诉汽车是否处于可接受状态的类别特征。下面列出了功能名称及其描述:

  • buying_price: 客户的购买水*或能力(非常高:vhigh,高:High,低:Low,Medium: med)
  • 维护成本:维护水*(非常高:v 高,高:高,低:低,中:中)
  • 门:轿厢内的门数(2、3、4、5 或更多)
  • person_capacity: 运载人员的容量(2 人、4 人及以上)
  • lug_boot: 行李箱的尺寸(小、中、大)
  • 安全:轿厢的安全等级(高、中、低)
  • 等级:不可接受,可接受,非常好,良好

尽管原始数据集有四个类,但出于教程的目的,我通过将可接受、非常好和良好放在一个类(可接受的类)中,将该问题转换为二元分类问题(可接受与不可接受)。

在动手实验部分,我们将实际应用决策树分类器模型进行汽车评估分类,包括探索性数据分析(EDA)、数据预处理、模型构建、实验结果和后分析。

动手实验练习

复制并粘贴以下链接以打开 google colab 或单击以下 Google Colaboratory 选项卡。

https://colab.research.google.com/notebooks/welcome.ipynb

进口包装

为数据预处理、模型开发和验证导入必要的包/库:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics import confusion_matrix
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
from mlxtend.plotting import plot_confusion_matrix
from sklearn import metrics%matplotlib inline

读取数据集:

data = pd.read_csv(‘car_evaluation.csv’)
#We can check the first five samples of the data:
data.head(5)

数据的维度

我们可以检查数据的维度——找出观察值的数量和特征的数量

data.shape

注释:数据集包含 7 个特征的 1727 个观察值

功能名称

我们可以显示数据集的特征/列名

data.columns

最后一个变量——“类别”是目标变量,表示样品的标签——样品车是处于可接受的状态还是不可接受的状态。

缺失值

我们必须检查数据集是否包含任何缺失值。如果数据包含缺失值,那么我们应该估算缺失值或丢弃包含缺失值的观测值。

data.isnull().sum()

注释:数据集不包含任何缺失值。

数据集的分布

我们可以计算每一类数据集中的样本数,也可以绘制分布图以直观显示。

data[‘class’].value_counts()

备注:有 1209 个不可接受的汽车样本,而可接受的汽车数量为 518 个

绘制类变量

data[‘class’].value_counts().plot(kind = ‘bar’)
plt.show()

作者图片

独立变量的分布

我们可以通过检查自变量的分布来更好地理解数据。我在这里提供了一个例子,但是,您应该尝试使用其他变量。

data[‘safety’].value_counts().plot(kind = ‘bar’)
plt.show()

作者图片

注释:数据集包含数量大致相同的低、中和高安全条件汽车。

计数图

我们可以对照相关类变量检查任何自变量的分布,以获得对数据更深入的理解。例如,我绘制了变量购买价格和类变量在不同类别中的分布。您还可以绘制并检查其他变量。

import seaborn as sns
sns.countplot(data[‘buying_price’], hue = data[‘class’])
plt.show()

作者图片

数据类型

我们可以检查每个变量的数据类型(如分类变量、数值变量)。这个过程很重要,因为它告诉我们一个变量是否需要从分类到数字的转换。

data.info()

注释:从数据类型 -所有变量都是分类格式。因此,在将数据输入模型之前,需要一个转换过程来准备数据。

数据预处理

因变量和自变量

我们必须将自变量(X)和目标变量(y)分开,以便进一步分析。我们将目标变量“class”放到表单 X 中,并将 class 定义为目标变量。

X = data.drop([‘class’], axis = 1)y = data[‘class’]

序数编码

从 EDA 部分,我们发现所有的变量都是分类格式。尽管决策树分类器可以处理分类变量和数字格式变量,但是我们将在本教程中使用的 scikit-learn 包不能直接处理分类变量。因此,我们必须执行一个转换过程。然而,首先,我们需要检查数据集中分类变量的类型。从实验前部分,我们看到每个变量的所有类别都与等级或顺序相关联,表明变量是有序分类变量。因此,我们对变量进行顺序编码,以保持变量的顺序。例如,行李箱的尺寸有三个类别:小号、中号和大号,它们是有顺序的。

首先,我们按升序定义每个变量的类别。我们将使用一个 sklearn 包来完成这个任务——ordinal encoder 使用一个给定的类别列表。例如,转换后,buying_price_category 的值将从['low ',' med ',' high ',' vhigh']转换为[0,1,2,3],对于可变门:转换类别后将为:['2 ',' 3 ',' 4 ',' 5more'] → [0,1,2,3]

from sklearn.preprocessing import OrdinalEncoder
buying_price_category = [‘low’, ‘med’, ‘high’, ‘vhigh’]
maint_cost_category = [‘low’, ‘med’, ‘high’, ‘vhigh’]
doors_category = [‘2’, ‘3’, ‘4’, ‘5more’]
person_capacity_category = [‘2’, ‘4’, ‘more’]
lug_boot_category = [‘small’, ‘med’, ‘big’]
safety_category = [‘low’, ‘med’, ‘high’]
all_categories = [buying_price_category, maint_cost_category,doors_category,person_capacity_category,lug_boot_category,safety_category]oe = OrdinalEncoder(categories= all_categories)
X = oe.fit_transform( data[[‘buying_price’, ‘maint_cost’, ‘doors’, ‘person_capacity’, ‘lug_boot’, ‘safety’]])

列车测试数据分割

将数据集划分为训练数据和测试数据,其中训练数据将用于训练模型,而测试数据将用于评估模型。这里,我们将数据集分为 70% : 30%,分别用于训练和测试数据。

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.3, random_state=2)

模型开发

训练决策树分类器

我们用 X_train 和 y_train 数据在 sklearn 中训练决策树分类器。在开始训练过程之前,我们可以设置不同超参数的值,例如 criterion、max_depth(树的最大深度)。min_samples_split(拆分内部节点所需的最小样本数)。等等..

DT_classifier = DecisionTreeClassifier( criterion= ‘gini’, max_depth= 3, min_samples_split= 10)DT_classifier.fit(X_train, y_train)

实验结果

预测

我们根据测试数据预测:

y_pred = DT_classifier.predict(X_test)

结果

我们使用混淆矩阵来寻找模型的性能:

混淆矩阵(y 测试,y 预测)

作者图片

从混淆矩阵中:

真阳性的数量:103

误报数量:59

假阴性数量:15

真阴性数:342

我们可以使用其他评估指标来了解模型的性能,并分别显示每个类的结果。

print(metrics . classification _ report(y _ test,y_pred))

上述报告显示了可接受和不可接受类别的精确度、召回率和 f1 分数。精度、召回、f1 分数分别为 87%、64%和 74%,其中可接受的条件汽车样本(支持)的数量为 162。另一方面,不可接受类别的精度、召回、f1 分数分别是 85%、96%和 90%,其中不可接受汽车样本的数量是(支持)357。从这个报告中,我们可以看到,对于不可接受的汽车样本,该模型比可接受的汽车表现得更好,这是一个有趣的发现。

总体准确率为 86%。

岗位分析

我们可以将树可视化,了解模型如何预测一辆车是否可以接受。下面是绘制树的代码。从树中:根节点是安全变量,以 0.5 分割安全变量,这意味着,它将创建两个节点-一个具有{低}安全性,而另一个将包含{中,高}安全性汽车样本。

from sklearn import tree
fig = plt.figure(figsize=(15,12))
_ = tree.plot_tree(DT_classifier,
feature_names=data.columns[:-1],
class_names= DT_classifier.classes_, filled=True)

作者图片

相关文章:

挑战机器学习算法中的再现性

中心极限定理中样本量的影响

数据泄露是机器学习管道中的一个重要因素;可以阅读— 如何避免数据泄露

参考

[1]Dua d .和 Graff c .(2019 年)。UCI 机器学习知识库[http://archive . ics . UCI . edu/ml]。加州欧文:加州大学信息与计算机科学学院。

基数—您的权力 BI 陛下

原文:https://towardsdatascience.com/cardinality-your-power-bi-majesty-3add4137753e

你知道最优数据模型大小的最大“敌人”是谁吗?了解基数,并学习如何应用一些简单的技术来提高基数水*,从而提高报告性能

Ricardo Resende 在 Unsplash 上拍摄的照片

正如您可能已经从的前几篇文章中了解到的,创建一个星型模式并不意味着您的数据建模任务已经完成。还有很多方面需要注意——虽然你可以偷偷摸摸地不去微调每一个细节——但其中一些“细节”当然更重要,需要更好地理解。仅仅是因为它的影响可能是重大的!

在 Power BI 中,( im)适当的解决方案最明显的含义之一就是性能!性能,性能,性能…我们一直在努力实现它(或者至少我们应该努力),但在调整 Power BI 报告时,有许多事情需要记住。优化过程的第一步是确保您的数据模型大小处于最佳状态——这意味着,尽可能减小数据模型大小!这将使 VertiPaq 的存储引擎在为您的报告检索数据时以更高效的方式工作。

当您致力于优化数据模型大小时,您知道谁是您的头号“敌人”吗?!列数据类型,对吗?文本列将比数值类型的列消耗更多的内存。那只说对了一半!

最大的对手叫基数!

在我们解释什么是基数,以及它为什么对减少数据模型大小如此重要之前,让我们首先讨论 VertiPaq 存储数据的方式,一旦您将表的存储模式设置为 Import ( DirectQuery 存储模式超出了本文的范围,因此从现在开始,本文中所有关于基数的内容都专门指导入存储模式)。

一旦将存储模式设置为 Import,VertiPaq 将从列中扫描样本行,并基于特定列中的数据(不要忘记,VertiPaq 是一个列数据库,这意味着每一列都有自己的结构,并且在物理上与其他列分离),它将对数据应用某种压缩算法。

有三种不同的编码类型:

  • 值编码 —仅适用于整数数据类型
  • 哈希编码 —适用于所有非整数数据类型,在某些情况下也适用于整数数据类型
  • 【RLE(游程长度编码) —在 哈希编码后出现 ,作为一个附加的压缩步骤,在这些场景中,当列中的数据以 VertiPaq“认为”的方式排序时,可以获得比仅使用哈希算法更好的压缩率

同样,在我们解释基数的概念在整个故事中的位置之前,让我们说明一下哈希算法在后台是如何工作的:

作者图片

正如您所看到的,VertiPaq 将为该列的不同值创建一个 字典 ,为每个值分配一个位图索引,然后存储这个索引值而不是“真实”值——简单地说,它将把数字 1 存储为指向“Book”值的指针,将数字 2 存储为“Shirt”值,依此类推。就像在后台创建一个虚拟维度表一样!

现在,假设该列中没有两个不同的值,而是如下所示:

作者图片

我们的“dimension”现在将有 10 行,因此您可能会认为这个字典表的大小将比只有两个不同值的情况大得多。

现在,你可能会问自己:好吧,这很好,但是这个故事和基数有什么共同之处呢?

基数表示列中唯一值的数量。

在我们的第一个例子中,基数为 2,而在第二个例子中,基数等于 10。

基数是影响列大小的首要因素。别忘了,列的大小不仅受其中数据大小的影响。你应该考虑字典的大小,和层次结构的大小一样。对于具有高基数(大量不同的值)并且不是整数数据类型的列,字典的大小明显大于数据本身的大小。

让我给你看一个例子。我将使用 DAX Studio 来分析数据模型大小背后的不同指标。一旦我在 DAX Studio 中打开“高级”选项卡并选择“查看指标”,就会出现一系列不同的数字,以了解我的数据模型的特定部分有多大:

作者图片

让我们快速重申一下上图中的关键观点。在 Chats 表中,datetmStartUTC 列是日期/时间数据类型,精度达到第二级,它有将* 900 万个不同的值!它的大小约为。455 MBs —这个数字不仅包括数据大小(26 MBs),还包括字典大小和层次结构大小。您可以看到 VertiPaq 应用了散列算法来压缩该列中的数据,但是最大的内存占用是字典大小(几乎 358 MBs)。

显然,这远远不是我们的数据模型的最佳条件。然而,我有好消息要告诉你…

有多种技术可以提高基数水*!

以前的一篇文章中,我解释了如何通过应用一些更高级的方法来降低基数,比如使用除法和模运算将一个高基数的数字列拆分成两个低基数的列,每行节省一些位。我还向您展示了如何将日期/时间列分成两个单独的列——一个只包含日期部分,而另一个包含时间数据。

但是,这些技术需要在报表端做额外的工作,因为所有的度量都需要重写以反映数据模型结构的变化并返回正确的结果。因此,这些高级方法不是您在常规数据模型优化中需要应用的东西,它们更多的是一种“边缘”用例,当没有其他方法来减少数据模型的大小时,简单地说,当您处理非常大的数据集时!

但是,这并不意味着您不应该努力提高基数水*,即使对于更小更简单的数据模型也是如此。相反,这应该是 Power BI 开发过程中的一个常规部分。在本文中,我将向您展示两种简单的方法,它们可以显著降低列的基数,从而降低整个数据模型的大小。

通过更改数据类型来提高基数级别

老实说,您的用户多久需要分析一次二级数据?比如,我们在 09:35:36 或 11:22:48 有多少销售?我会说,毫无意义。在 98%的情况下,业务需求是每天都有可用的数据。也许在某些情况下,用户需要了解一天中的哪一段时间最“高效”:上午、下午或晚上……但是,我们仍然要关注大多数情况,在这些情况下,数据应该在每天的级别上进行细化。

作者图片

我已经将列的数据类型从日期/时间更改为日期,因为时间部分与报告目的无关。让我们在 DAX Studio 中刷新我们的指标:

作者图片

哦,哇哦!基数不是大约 900 万,我们现在只有 1356(这是列中不同天数的数目)。但是,更重要的是,列的大小从 455 MB 降到了 7 MB!那是 huuuuge!看看字典的大小:VertiPaq 现在只处理 1356 个不同的值,而不是必须为 900 万个值构建一个字典,字典大小从 358 MBs 下降到 85 KBs!

另一个你可以应用的技巧是当你处理十进制数值的时候。将数据“按原样”导入 Power BI 的情况并不少见,而且,我不止一次看到有人导入小数点后精确到 5 位的十进制数字!我的意思是,真的有必要知道你的销售总额是 27.586.398,56891,还是显示 27.586.398,57 就可以了?

需要明确的是,在 Power BI 中格式化值以显示 2 个小数位不会影响数据模型的大小,这只是一个可视化的格式化选项,在后台,数据以小数位后 5 位数存储。

作者图片

现在,让我们在 DAX Studio 中检查这个表的指标。这是一个微不足道的数据模型大小,其中只有 151 个不同的值,但是您只能想象在数百万行的表上的差异:

作者图片

现在,我将转到超级查询编辑器,并将该列的类型更改为固定十进制数:

作者图片

我们现在已经将数值四舍五入到小数点后两位,所以让我们切换回 DAX Studio 并再次检查数字:

作者图片

即使在这个极小的数据集上,差别也是显而易见的!

如果您现在想知道:好吧,如果我对单笔交易值进行舍入,这没问题,但这会如何影响我的计算呢?好吧,让我们研究一个非常常见的场景,计算表中每行占总数的百分比:

作者图片

现在,让我们检查一下,一旦我们将列的基数和值四舍五入到两位小数,这个计算是如何工作的:

作者图片

现在,公*的问题应该是:业务决策者会关心单个行的份额是 0.66%还是 0.66225%……如果第二位小数之后的这些值对业务至关重要,那么——见鬼,是的,谁会关心基数:)……但是,我要说,在 99.99%的场景中,没有人会关心 0.66 vs 0.66225。在任何情况下,即使有人坚持这样的精度,也要试着向他们解释更高的基数带来的所有缺点(内存消耗、更慢的计算等等),尤其是在大型事实表上。

使用汇总和分组提高基数水*

我想向您展示的另一项技术是如何利用汇总和分组的概念来提高基数级别,并使您的数据模型更具性能。

当您创建报表时,用户可能需要在比单个事务更高的粒度级别上理解不同的指标,例如,在特定日期售出了多少产品,在特定日期有多少客户注册,等等。这意味着您的大多数分析查询不需要针对单个事务,因为汇总的数据完全没问题。

如果我们总结聊天表的数据,让我们检查一下内存占用的差异。您可能还记得本文前一部分中的原始表,它占用 555 MBs:

作者图片

现在,如果我预先汇总数据,并按日期和/或产品对其进行分组,则编写以下 T-SQL:

SELECT CONVERT(DATE,datetmStartUTC) AS datetm
,productID
,COUNT(chatID) AS totalChats
FROM Chats
GROUP BY CONVERT(DATE,datetmStartUTC)
,productID

如果最常见的业务请求是分析每个日期和/或产品的聊天次数,这个查询将成功满足这些请求。

让我们检查一下这个总结表与原始表相比的大小:

作者图片

虽然原始表(即使 datetmStartUTC 列的基数级别有所提高)需要 105 MBs,但是聚合表只需要 217 KB!而且这张表可以回答大部分的经营分析问题。即使您需要包含额外的属性,例如客户数据,这仍然是检索数据的最佳方法。

还有更多!

即使您不能在数据源端创建汇总数据,您仍然可以通过利用 Power BI 中的聚合特性获得显著的性能提升。这是 Power BI 中最强大的特性之一,值得单独撰写一篇文章,甚至是一系列文章,比如 Phil Seamark 的 this,当我需要更深入地了解聚合及其在表格模型中的工作方式时,我总是会参考它。

结论

构建一个最优的数据模型不是一件容易的事情!有许多潜在的警告,有时很难避免通往“完美”模型的道路上的所有陷阱。然而,了解减少整体数据模型大小的重要性是调整 Power BI 解决方案的关键要求之一。

记住这一点,为了能够实现最佳的数据模型大小,您需要吸收基数的概念,作为决定列大小的主要因素。通过以正确的方式理解基数,以及 VertiPaq 存储和压缩数据的方式,您应该能够应用我们刚刚介绍的一些技术来提高列的基数级别,从而提高报告的整体性能!

感谢阅读!

成为会员,阅读媒体上的每一个故事!

用 PyTorch 预测汽车效率

原文:https://towardsdatascience.com/cars-efficiency-predictions-with-pytorch-dd7060477c22

Unsplash 上的Rock star拍摄

了解如何在 PyTorch 中构建完整的深度学习管道

介绍

众所周知,在过去的几个月里,汽油价格飞涨。由于成本因素和与环境相关的原因,人们正在用汽油加满最低必要量。但是,你有没有注意到,当你在互联网上查找你的汽车从 A 点到 B 点应该花多少汽油时,这些数字几乎从来都不符合现实?

在这篇文章中,我想开发一个简单的模型,可以预测一辆汽车的效率(或消耗),以每加仑汽油行驶的英里数来衡量。

目标是解决管道中的所有步骤,如数据处理、特征工程、培训和评估。

所有这些都将使用 PyTorch 在 Python 中完成。特别是,我将依赖于 Colab 笔记本,我总是发现它对这些小项目非常方便!😄

数据集

我们将在这个项目中使用的数据集现在是一个里程碑, 来自 UCI 库 的自动 MPG 数据集。它由 9 个特征组成,包含 398 条记录。具体来说,变量名及其类型如下:

1。mpg :连续
2。气缸:多值离散
3。位移:连续
4。马力:连续
5。重量:连续
6。加速:连续
7。车型年:多值离散
8。原点:多值离散
9。汽车名称:字符串(每个实例唯一)

我们来编码吧!

首先,我们加载数据集并适当地重命名列。na_values 属性用于使 pandas 识别类型为“?”的数据应被视为 null。

现在使用 df.head() 来显示数据集。

df.head()

使用 df.describe() 函数,我们可以显示数据集的一些基本统计数据,以开始了解我们将找到哪些值。

df.describe()

否则,我们可以使用 df.info() 来查看是否有空值,并找出我们的变量的类型。

df.inof()(图片由作者提供)

我们首先看到马力特性包含空值,因此我们可以开始从数据集中删除对应于这些值的记录,并重置 dataframe 索引,如下所示。

现在如果我们去打印 len(df) → 392 ,因为我们已经剔除了行。

接下来要做的是将数据集分成训练集和测试集。我们使用一个非常有用的 sklearn 函数来完成这个任务。让我们接着保存 df_train.describe()。transpose() table,因为我们将需要一些统计人员对一些特性进行预处理。

训练统计:

数字特征

我们现在要处理一些特征。我们经常把数字变量和分类变量区别对待。首先,我们从定义我们要归一化的数值变量开始。

要归一化一个特征,我们需要做的就是减去该特征的*均值,然后除以标准偏差,为此我们需要之前提取的统计数据。

如果我们现在根据原始数据集中的要素绘制归一化要素,您会注意到这些值由于标准化而发生了怎样的变化。那么它们将具有 0 *均值和 1 的标准偏差。

标准化(图片由作者提供)

标准化(图片由作者提供)

现在,关于 Model_Year 特性,我们对特定汽车型号是哪一年制造的并不感兴趣。但也许我们对区间或区间更感兴趣。例如,如果该型号是在 73 '和 76 '之间制造的,则该汽车是类型 1。这些范围有点随意,您可以尝试更多的范围,看看哪个效果最好。

垃圾箱范围(图片由作者提供)

分类特征

就范畴特征而言,我们基本上有两种主要方法。第一种是使用 one-hot vectors 将类别(字符串)转换成只包含一个 1 的二进制向量。例如,零类别将被编码为[1,0,0,0],类别 1 将被编码为[0,1,0,0],等等。

否则,我们可以使用一个嵌入层,将每个类别映射到一个可以训练的‘随机’向量,这样我们就可以得到一个类别的向量表示,它可以管理大量的信息。

当类别数量很大时,使用有限大小的嵌入会有很大的优势。

在这种情况下,我们使用独热编码。

让我们也提取我们需要预测的标签。

PyTorch 数据集和数据加载器

现在我们的数据已经准备好了,我们创建一个数据集以便在训练期间更好地管理我们的批次。

模型创建

我们构建了一个小网络,它有两个隐藏层,一个是 8 个神经元,一个是 4 个神经元。

模型(图片由作者提供)

培养

现在我们定义损失函数,我们将使用 MSE随机梯度下降作为优化器。

为了预测新的数据点,我们可以将测试数据提供给模型。

最后的想法

在这篇短文中,我们看到了如何使用 PyTorch 解决现实生活中的问题。我们从做一些 EDA 开始,以了解我们手上有什么样的数据集。然后,我向您展示了在预处理阶段如何区别对待数值变量和分类变量。将列值拆分成多个仓的技术被广泛使用。然后我们看到 PyTorch 如何允许我们用很少的步骤创建一个自定义数据集,我们可以逐批迭代。我们创建的模型是一个非常简单的模型,只有几层,但是使用正确的损失函数和适当的优化器可以让我们快速地训练我们的网络。我希望这篇文章对发现(或回顾)PyTorch 的一些特性有所帮助。

结束了

马赛洛·波利蒂

LinkedinTwitterCV

因果 SHAP 值:SHAP 值的一种可能的改进

原文:https://towardsdatascience.com/casual-shap-values-a-possible-improvement-of-shap-values-4d4d62925b71

介绍和案例研究

图片由埃文·丹尼斯拍摄

正如我在之前的帖子中解释的那样,广泛用于机器学习可解释性的 SHAP 价值观框架不幸未能反映其结果中的因果结构。研究人员一直在提出可能的解决方案来消除这种限制。在这篇文章中,我将回顾其中一个被提议的选择,因果 SHAP 值(CSV),并给出一个简单的例子和详细的计算来说明 CSV 和“传统的”SHAP 值之间的区别。

传统 SHAP 价值观及其局限性

让我们首先回顾一下 SHAP 值的定义,这是一种基于合作博弈论的方法,旨在解释一种机器学习模型,将特征重要性归因于特征对最终输出所做贡献的回报。对于给定的模型 f,考虑 j∈{1,2,…n}:=N 的特征 x_j 的 SHAP 值由下式给出

其中π是选择为均匀分布的 N 和符号

We can notice immediately that the above computation is based on a marginal expectation, which, explains why SHAP values cannot reflect the causal relation of the model.

CSVs: a possible improvement of SHAP

Definition of CSVs

Consider a very simple case where feature 1 is the direct cause of feature 2 and we would like to argue that we cannot give the two features the same weight when evaluating their contributions to the output of the model. By intuition, the cause should be considered more important than the effect when attributing feature importance. Such intuition leads to the causal SHAP values (CSVs) framework, proposed by Heskes 等人的排列,旨在修改当前的 SHAP 值框架,而不破坏其期望的属性,即效率、对称性、虚拟性和可加性。

CSV 具有以下定义:

我们立即注意到与前一个定义的不同(根据作者的偏好,达到一个常数):边际期望被由 Pearl 的 do 演算实现的介入期望所取代。对于那些不熟悉这个概念的人,只需注意 do(。)是一个数学运算符,允许我们在一个随意的模型中进行干预,通常由有向无环图(DAG)表示,这个“do”可以从字面上理解为“do it”。基于 CSV 的定义,让我们回头看看上面的例子:做特征 1,特征 2 的值不再是自由的,并且因果将被介入期望考虑。

与 SHAP 的区别

省略计算的细节,我们想提到的是,特征 x_i 的 CSV 可以分解成两部分:直接贡献和间接贡献。当特征变量 X_i 取特征 x_i 的值时,直接贡献是预测的变化,而间接贡献是由于干预 do(X_i=x_i)引起的变化。从这个意义上说,CSV 与传统 CSV 的区别在于这种间接贡献的额外信息。

下图给出了自行车租赁模型中的 SHAP 值和 CSV:以一年中的时间和温度的 cos 值作为输入,模型预测自行车租赁计数。我们可以看到,在考虑一年中的时间对温度的影响时,CSV 更重视 cos_year 值。

图片由赫斯克斯等人拍摄:自行车租赁模式的 SHAP 价值观

为了结束这一部分,让我们继续,通过用介入期望代替模型的边际期望,使得特征的间接贡献与其直接贡献一起被考虑,偶然 SHAP 值可以被视为当前 SHAP 值的概括。

CSV 的具体示例

让我们通过考虑以下只有三个变量的简单模型来说明 CSV 是如何工作的:

让我们假设变量遵循由下面的 DAG 给出的偶然模型,其中我们假设 x_1 是 x_2 的父,x_3 独立于其他两个。

作者图片:休闲模特

此外,我们假设:

首先,很容易得到所有特征的 SHAP 值:

让我们现在转向 CSV。以第一个特征为例。根据定义,CSVs 被给定为{1,2,3}的所有排列的介入期望的总和。作为例子,我们将计算φ_ 1(1,2,3)。将线性函数代入φ的定义,我们得到:

根据偶然模型,我们有 E[X_2|do(x_1)]=α_1x_1,而 E[X_3|do(x_1)]=E[X_3]这导致:

类似的计算给出了所有三个特征的 CSV:

我们从结果中观察到,当模型具有一定的因果结构时,CSV 最终具有不同的特征属性。由于在上面所示的 DAG 图中,X_1 是 X_2 的父节点,所以当我们从 x_2 的 SHAP 值中移除一项时,由于它是由 x_1 而不是 x_2 本身引起的,所以在 x_1 的值中增加了一项作为其间接影响。

CSV 的局限性

我们最后想说,如果用 CSV 取代 SHAP 还为时过早。尽管设计巧妙,但 CSV 的局限性也很明显。总的来说,我们数据科学家对 SHAP 解释任何黑盒模型的能力感到兴奋,这些黑盒模型的随意结构在实践中很难获得,如果没有这些信息,我们将永远无法对 do-calculus 进行很好的估计。然而,即使只有一部分偶然的信息可用,它仍然有助于更好地理解模型。

CatBoost 与 LightGBM 与 XGBoost

原文:https://towardsdatascience.com/catboost-vs-lightgbm-vs-xgboost-c80f40662924

哪个算法最好?

廷杰伤害律师事务所Unsplash 上的照片

【CatBoost】(类别提升) LightGBM (光梯度提升机) XGBoost (极限梯度提升)都是梯度提升算法。在深入研究它们在特征和性能方面的相似性和差异之前,我们必须了解术语集成学习以及它与梯度增强的关系。

目录

  1. 集成学习
  2. Catboost vs . light GBM vs . XGBoost 特性
  3. 提高精度、速度和控制过拟合
  4. 性能对比

集成学习

集成学习是一种结合来自多个模型的预测以获得更稳定和更好概括的预测的技术。这个想法是*均不同模型的个别错误,以减少过度拟合的风险,同时保持强大的预测性能。

在回归中,总体预测通常是单个树预测的*均值,而在分类中,总体预测基于加权投票,概率是所有树的*均值,概率最高的类是最终预测的类。

有两种主要的集成学习方法,即 bagging 和 boosting,尽管 ML(机器学习)算法可以是两者的组合,但有一定的变化。

  • Bagging 方法使用数据的随机子集(替换取样)并行构建模型,并汇总所有模型的预测
  • Boosting 方法利用全部数据依次建立模型,每个模型在前一个模型的误差上进行改进

CatBoost、LightGBM 和 XGBoost 都是梯度增强算法的变体。现在,您已经理解了打包和提升之间的区别,我们可以继续讨论算法如何实现梯度提升的区别。

Catboost 与 LightGBM 和 XGBoost 特性的关系

下表总结了这三种算法之间的差异,请继续阅读,以了解详细的特征。

表 CatBoost、LightGBM 和 XGBoost 的特征——按作者分类的图像

树对称

在 CatBoost 中,对称树或*衡树指的是在树的同一深度上所有节点的分裂条件是一致的。另一方面,LightGBM 和 XGBoost 会产生不对称的树,这意味着相同深度的每个节点的分割条件可能不同。

图 1:不对称树与对称树——作者图片

对于对称树,这意味着分裂条件必须导致相同深度的所有节点的最低损失。*衡树架构的好处包括更快的计算和评估以及控制过拟合。

尽管 LightGBM 和 XGBoost 都是非对称树,但是 LightGBM 是按叶生长的,而 XGBoost 是按层生长的。简而言之,我们可以认为 LightGBM 是选择性地生长树,从而产生比 XGBoost 更小更快的模型。

图 2: LightGBM(左)与 XGBoost(右)——作者图片

分裂法

拆分方式是指如何确定拆分条件。

在 CatBoost 中,使用贪婪方法,从而将特征分裂对的可能候选列表分配给叶子作为分裂,并且选择导致最小惩罚的分裂。

在 LightGBM 中,基于梯度的单侧采样(GOSS)保留所有具有大梯度的数据实例,并对具有小梯度的数据实例执行随机采样。梯度是指损失函数切线的斜率。具有较大梯度的数据点具有较高的误差,并且对于找到最佳分裂点是重要的,而具有较小梯度的数据点具有较小的误差,并且对于保持学习决策树的准确性是重要的。这种采样技术导致训练模型的数据实例更少,因此训练时间更快。

在 XGBoost 中,预排序算法考虑所有特征,并根据特征值对它们进行排序。之后,进行线性扫描,以确定导致最大信息增益的特征和特征值的最佳分割。基于直方图的算法以相同的方式工作,但是它不是考虑所有特征值,而是将特征值分组到离散的箱中,而是基于离散的箱找到分裂点,这比预先排序的算法更有效,尽管仍然比 GOSS 慢。

增压类型

选择数据进行训练的方式有所不同。有序推进指的是每个模型训练一个数据子集并评估另一个数据子集的情况。有序提升的好处包括增强对未知数据的稳健性。

分类列

不同算法的分类列的参数如下:

  • CatBoost: cat_featuresone_hot_max_size
  • LightGBM: categorical_feature
  • XGBoost: NA

提高精度、速度和控制过拟合

在集成学习中,对不同模型的预测进行*均有助于过拟合。然而,与任何基于树的算法一样,仍然存在过度拟合的可能性。可以在将数据集分成训练集、验证集和测试集的过程中处理过拟合,从而实现交叉验证、提前停止或树修剪。为了比较不同的算法,我们将集中于使用模型参数控制过拟合。

注意,为了控制模型的复杂性,XGBoost 使用参数max_depth(因为它是逐层增长的),而 LightGBM 使用参数num_leaves(因为它是逐叶增长的)。

表 2:用于调整精度、速度和过度拟合的参数—图片由作者提供

性能对比

在不同的数据集上,有各种关于准确性和速度的基准测试。我发现在几个数据集上概括算法性能是草率的,特别是如果过度拟合和数字/分类变量没有得到适当的考虑。

然而,一般来说,从文献来看,XGBoost 和 LightGBM 产生的性能相似,CatBoost 和 LightGBM 的性能比 XGBoost 快得多,尤其是对于较大的数据集。

希望您对三种最流行的 ML boosting 算法有更好的理解——CatBoost、LightGBM 和 XGBoost,它们主要在结构上有所不同。在实践中,数据科学家通常会对他们的数据尝试不同类型的 ML 算法——所以现在还不要排除任何算法!除了在不同算法之间进行选择时的可理解性、性能和时序考虑之外,通过超参数调整来微调模型以及通过流水线架构或超参数来控制过拟合也是至关重要的。

相关链接

CatBoost

LightGBM

XGBoost

在人工智能中捕捉边缘案例

原文:https://towardsdatascience.com/catching-edge-cases-in-ai-b9860589ece4

播客

亚龙·辛格关于构建更强大、容错的人工智能系统

苹果 | 谷歌 | SPOTIFY | 其他

编者按:TDS 播客由杰雷米·哈里斯主持,他是人工智能安全初创公司墨丘利的联合创始人。每周,Jeremie 都会与该领域前沿的研究人员和商业领袖聊天,以解开围绕数据科学、机器学习和人工智能的最紧迫问题。

众所周知,人工智能系统正被用于越来越多的高风险应用中。随着人工智能吞噬世界,确保人工智能系统行为稳健变得至关重要——它们不会被不寻常的输入抛弃,并开始吐出有害的预测或建议危险的行动路线。如果我们要让人工智能开车带我们去工作,或者决定谁能获得银行贷款,谁不能,我们最好有信心,我们的人工智能系统不会因为一场反常的暴风雪或因为某个实习生错过了一个负号而失败。

我们现在已经过了这样一个阶段,公司可以将人工智能开发视为一场荣耀的 Kaggle 竞赛,其中唯一重要的事情是模型在测试集上的表现如何。人工智能驱动的失误并不总是生死攸关的问题,但它们可能会伤害真正的用户,并对没有预料到它们的公司造成品牌损害。

幸运的是,人工智能风险最*开始受到更多关注,新公司——如 Robust Intelligence——正在加紧开发预测人工智能故障并减轻其影响的策略。和我一起参加本期播客的是 Yaron Singer,他是前谷歌员工,哈佛大学计算机科学和应用数学教授,现在是 Robust Intelligence 的首席执行官和联合创始人。Yaron 拥有理解什么是人工智能风险所需的理论和工程专业知识的罕见组合,以及知道如何将这种理解整合到可以帮助开发者和公司处理人工智能风险的解决方案中的产品直觉。

以下是我在对话中最喜欢的一些观点:

  • 与传统软件系统不同,AI 系统中的错误往往是无声的。糟糕的预测或错误的数据输入通常不会导致崩溃,甚至不会发出错误警告。这给人工智能开发人员带来了挑战,他们必须找到聪明的方法来预测和检测比软件工程师可能遇到的更微妙的错误。
  • 软件工程最佳实践通常涉及测试驱动开发(TDD),在这一过程中,开发人员在编写代码之前,首先编写他们的代码必须通过的测试。Yaron 认为,类似的方法在人工智能领域变得越来越必要:就像 21 世纪初的软件工程师一样,今天的人工智能开发人员浪费大量时间跟踪(通常是无声的)错误并调试他们的模型——当开发人员使用强大的测试协议时,这两者都变得不那么必要了。
  • Yaron 指出了人工智能风险和模型失败的许多来源。例如,当模型在野外遇到与其训练或测试时不同的输入时,就会出现分布外采样,这通常会导致错误。但数据输入错误,甚至利用敌对人工智能的公然恶意攻击,也是人工智能失败风险的重要来源。
  • 当涉及到人工智能风险时,数据预处理通常是数据生命周期中最脆弱的阶段。明显有问题的数据(例如错误的数据类型)可以很容易地检测和标记出来,但更具挑战性的模型故障会发生,因为数据预处理缺乏关于什么输入是合理的或预期的上下文。例如,用户可能输入他们的出生年份而不是他们的年龄,并抛出一个预处理方案,该方案在设计时没有识别“1985”位于年龄值的适当范围之外所需的上下文意识。处理这些失败类可能涉及到设计良好的测试,甚至开发异常检测模型来捕捉和标记异常输入值。
  • 亚龙从网络安全世界借用的一个概念是模糊化。fuzzer 是一种生成(通常是随机的)输入的工具,这些输入可以输入到程序中,希望使其崩溃,从而揭示它可能包含的漏洞。从某种意义上来说,Robust Intelligence 正在对其客户的机器学习模型做同样的事情:他们从事构建模糊器的业务,这些模糊器可以使人工智能系统生成无意义的输出。

章节:

  • 0:00 介绍
  • 2:30 人工智能风险之旅
  • 人工智能系统的 5:20 保证
  • 11:00 测试作为解决方案
  • 15:20 通用性和软件与定制工作
  • 不同型号之间 18:55 的一致性
  • 24:40 不同型号的故障
  • 30:25 责任级别
  • 35:00 总结

使用 CatBoost 的分类嵌入

原文:https://towardsdatascience.com/categorical-embeddings-with-catboost-9f87ceda76a2

研究使用来自 fast.ai 神经网络的分类嵌入是否可以提高 CatBoost 在分类任务中的性能

泰勒·伊斯顿在 Unsplash 上的照片

CatBoost 是 Yandex 的开源机器学习算法。它的名字来源于单词CategoryBoosting。正如您所料,该库的主要优势之一是以比其他决策树算法更直观的方式处理分类数据。与其他决策树算法相比,它的性能也很好。我将 fast.ai 分类嵌入传递给随机森林和 XGBoost 模型,代替一次性编码的分类变量,取得了很好的结果。本文旨在回答 CatBoost 是否也能从使用它们中获益的问题。我们将使用 Kaggle 的pet finder . my Adoption Prediction数据集对此进行调查。 PetFinder.my 是一个帮助防止虐待马来西亚流浪动物并在那里为它们寻找新家的资源。如果这是你感兴趣的话题,他们确实有一个志愿者人工智能任务组。该组织好心地允许我在这篇文章中使用他们的数据集。

我的 Kaggle 笔记本,包括下面显示的代码,可以在这里找到

在本文中,我们将了解:

  1. 什么是范畴嵌入
  2. 如何制作和检查一个 fast.ai 表格学习器
  3. 如何构建 CatBoost 模型
  4. 传递分类嵌入来代替或补充离散分类值是否可以提高性能

什么是范畴嵌入?

类别要素是不包含连续数据的要素,具有来自一组固定类(或类别)的离散值。这些类别可以是数字,尽管它们通常是字符串值。这些值需要以某种方式转换成数字,然后才能输入到大多数机器学习模型中,因为大多数模型只接受数字输入。CatBoost 是不常见的,因为它接受没有这种转换的分类特征。但是即使是 CatBoost 也以类似的方式转换这些功能

一种典型的方法是一键编码。为每个固定类别建立一个单独的二进制变量,当样本属于给定类别时,该变量的值为 1,否则为 0。如果我们查看 PetFinder.my 数据集中的 Breed1 特性,我们会看到文档中描述了 307 个狗和猫的品种,其中 176 个出现在训练数据集中。对该列进行一次性编码意味着要管理额外的 176 列!此外,我们会失去任何内在的关系或品种之间可能存在的相似性。

范畴嵌入(有时称为实体嵌入)克服了这两个缺点。嵌入是分类数据的向量表示。例如,在嵌入之前,Breed1 数据点由单个分类变量表示,用每个品种的数字 ID 填充(品种名称可以在附带的 BreedLabels.csv 文件中看到)。

df_train.Breed1.value_counts()

嵌入后,Breed1 数据如下所示。

我们可以看到,单列已被 29 列浮点值所取代。我们将在后面看到 29 这个数字是如何确定的。但是现在,知道离散数据已经被一些连续的数据点所取代就足够了,这些数据点可以直接用作我们模型的特征输入。这个数字几乎比一次性编码数据所需的数字小一个数量级。

值得注意的是,这些输入既连续又有意义,正如论文“分类变量的实体嵌入”(郭城和费利克斯·贝克汉)中所展示的。作者表明,连续值描述了潜在类别的内在属性。这意味着我们可以计算和比较不同类别(在我们的例子中,猫和狗品种)之间的嵌入距离,并发现我们认为在现实生活中相似的类别之间的距离比我们认为相距较远的类别之间的距离更短。

例如,在宠物收养的情况下,我们可能会认为两种家庭犬种,拉布拉多犬和梗犬,彼此之间的距离比罗威纳犬更*,罗威纳犬更多地被认为是一种护卫犬。并且,计算这些品种的嵌入之间的简单欧几里德距离,我们看到这确实是事实。

breed1_embeddings = [x for x in df_train_embeddings.columns if x.startswith('Breed1_')]
terrier=df_train_embeddings[df_train_embeddings.Breed1=='218'][breed1_embeddings].head(1)
labrador=df_train_embeddings[df_train_embeddings.Breed1=='141'][breed1_embeddings].head(1)
rottweiler=df_train_embeddings[df_train_embeddings.Breed1=='189'][breed1_embeddings].head(1)
print('Terrier -> Labrador', round(np.linalg.norm(np.array(terrier)-np.array(labrador)),3))
print('Rottweiler -> Terrier', round(np.linalg.norm(np.array(rottweiler)-np.array(terrier)),3))
print('Rottweiler -> Labrador', round(np.linalg.norm(np.array(rottweiler)-np.array(labrador)),3))

安装

好了,现在我们知道了一点什么是嵌入,以及它们如何帮助我们,让我们通过代码来使用 fast.ai 库生成它们。如果你不熟悉 fast.ai,是一家由杰瑞米·霍华德和瑞秋·托马斯创立的研究机构,致力于让深度学习变得更容易实现。

首先,我们导入我们的库。

import catboost as cb
from fastai.tabular.all import *
import numpy as np
import os
import pandas as pd
import scipy as sp
import seaborn as sns
from sklearn.model_selection import train_test_split
from typing import Dict, Tuple

然后,我们加载提供的训练、测试和样本提交文件。

input_folder = '../input/petfinder-adoption-prediction'
df_train = pd.read_csv(f'{input_folder}/train/train.csv')
df_test = pd.read_csv(f'{input_folder}/test/test.csv')
df_sample_submission = pd.read_csv(f'{input_folder}/test/sample_submission.csv')
df_sample_submission.drop('AdoptionSpeed', axis=1, inplace=True)

预处理

我们对数据做一些非常基本的预处理。品种栏似乎很混乱,用各种组合来表示一只狗或猫是混种。所以我们设计了一个简单的二进制标志。

def get_mixed_breed(breed1: int, breed2: int) -> int:    
    # mixed breed with both breeds specified
    if breed1 != 307 and breed2 != 307 and breed1 != 0 and breed2 != 0 and breed1 != breed2:
        return 1
    # mixed breed with only one breed specified
    elif (breed1 != 307 and breed1 != 0 and breed2 == 307) or (breed2 != 307 and breed2 != 0 and breed1 == 307):
        return 2
    # mixed breed with no breed specified
    elif (breed1 == 307 and breed2 == 307) or (breed1 == 307 and breed2 == 0):
        return 3

    # breed is not mixed
    return 0df_train['mixed_breed'] = df_train.apply(lambda row: get_mixed_breed(row.Breed1, row.Breed2), axis=1)
df_test['mixed_breed'] = df_test.apply(lambda row: get_mixed_breed(row.Breed1, row.Breed2), axis=1)

每个宠物档案的描述都通过谷歌的自然语言 API 运行,这意味着我们可以提取描述长度、语言、情感程度和分数的特征。

def get_sentiment(filename: str) -> pd.Series:    
    try:
        with open(filename) as file:
            data = json.load(file)
        return pd.Series((data['documentSentiment']['magnitude'], data['documentSentiment']['score'], data['language']))
    except FileNotFoundError:
        return pd.Series((np.nan, np.nan))

df_train[['description_sentiment_magnitude', 'description_sentiment_score', 'description_language']] = df_train.PetID.apply(lambda pet_id: get_sentiment(f'{input_folder}/train_sentiment/{pet_id}.json'))
df_test[['description_sentiment_magnitude', 'description_sentiment_score', 'description_language']] = df_test.PetID.apply(lambda pet_id: get_sentiment(f'{input_folder}/test_sentiment/{pet_id}.json'))
df_train['description_length'] = df_train.Description.str.count(' ')
df_test['description_length'] = df_test.Description.str.count(' ')

从阅读这 3 个样本的描述中,我们可以看到年龄被错误地输入了月份而不是年份。

df_train.loc[df_train.PetID=='e3b589e13', 'Age']=2
df_train.loc[df_train.PetID=='e77f9e778', 'Age']=3
df_train.loc[df_train.PetID=='53923463d', 'Age']=3

我们定义了相关(目标)变量以及分类和连续特征的列表,并根据 CatBoost 的要求创建了类型为str的分类变量。

reproducible_results = True
random_state = 42 if reproducible_results else None
dependent_var = 'AdoptionSpeed'
categorical = ['Breed1', 'Breed2', 'Gender', 'Color1', 'Color2', 'Color3', 'State', 'Vaccinated', 'Dewormed', 'Sterilized', 'mixed_breed', 'Type', 'MaturitySize', 'FurLength', 'Health', 'description_language']
continuous = ['Age', 'Quantity', 'Fee', 'VideoAmt', 'PhotoAmt', 'description_length', 'description_sentiment_score', 'description_sentiment_magnitude']df_train[categorical] = df_train[categorical].astype('str')
df_test[categorical] = df_test[categorical].astype('str')

如果我们想要可再现的结果,特别是从 fast.ai 表格学习器中,我们可以重新播种一切

def seed_everything(seed): 
    random.seed(seed) 
    os.environ['PYTHONHASHSEED'] = str(seed) 
    np.random.seed(seed) 
    torch.manual_seed(seed) 
    torch.cuda.manual_seed_all(seed) 
    torch.cuda.manual_seed(seed) 
    torch.backends.cudnn.deterministic = True

if reproducible_results:
    seed_everything(random_state)

现在我们使用 sklearn 的 train_test_split实用程序将训练数据分成 80/20 分割的训练集和验证集。我们设置分层参数以确保训练和验证数据的 AdoptionSpeed 类的划分是相同的。

train_xs, valid_xs, _, _ = train_test_split(df_train, df_train[dependent_var], test_size=0.2, shuffle=True, stratify=df_train[dependent_var], random_state=random_state)
splits = (train_xs.index.tolist(), valid_xs.index.tolist())

表格熊猫

斯通王Unsplash 上的照片

fast.ai [TabularPandas](https://docs.fast.ai/tabular.core.html#TabularPandas)对象是熊猫数据帧的有用包装器。我们选择进行分类,填充缺失值(默认行为是使用中值),而我们的数据进行标准化。splits参数是一个元组,包含训练集和验证集的行索引列表。我们将使用CategoryBlock来完成这个分类任务。

procs = [Categorify, FillMissing, Normalize]
tabdata = TabularPandas(df=df_train, procs=procs, cat_names=categorical.copy(), cont_names=continuous.copy(), splits=splits, y_names=dependent_var, y_block=CategoryBlock())

嵌入尺寸

现在,我们第一次可以看到. ai 计划以多快的速度构建分类嵌入。这个方法给了我们要使用的默认嵌入大小。

embedding_sizes = get_emb_sz(tabdata)
embedding_sizes

该方法返回一个元组列表,每个分类变量一个,按照我们在cat_features参数中添加它们的顺序。我们来看第一个元组。列表中的第一个分类变量是 Breed1。正如我们已经看到的,在训练数据集中有 176 个不同的值。

df_train['Breed1'].nunique()

第一个元组是(177,29)。177 值是具有一个额外类别的 176 个唯一值,该类别将用于任何验证、测试或未来样本,该样本具有迄今为止未在训练数据集中表示的未知值。

29 是 fast 的连续值的数目。ai 已经确定它将用来表示 Breed1。换句话说,我们认为在收养宠物的情况下,主要品种之间的关系可以用 29 个连续的值来充分表达。让我们看看这个数字是如何得出的。

emb_sz_rule??

我们可以看到 fast.ai 用来确定嵌入矩阵大小的经验法则——它是 600 的最小值或类别数的 0.56 次方乘以 1.6。杰瑞米·霍华德提到,这条规则是通过运行许多模型并调整尺寸以找出最佳公式得出的。他特别提到这只是一个起点,应该像任何其他超参数一样进行优化(他甚至开玩笑地称之为他的“可怕的嵌入黑客”)。我们将在后面看到如何覆盖嵌入的默认大小。

我在上面说过,get_emb_sz方法返回一个元组列表,每个元组对应一个分类变量。然而,仔细观察,我们看到有 19 个元组(或嵌入),但我们只有 16 个分类变量!为了理解这是怎么回事,我们可以看看表格熊猫对象的属性cat_names

[col for col in tabdata.cat_names]

我们已经获得了 3 个新的分类变量,最后三个带有_na后缀。这种意外的行为是由于我们在创建表格熊猫对象时传递给它的FillMissing proc 参数。默认情况下,该方法在替换那些na值之前,为每个具有na值的连续变量添加一列。这样做是为了确保模型保留对给定样本缺少列值这一事实的预测能力。(请注意,如果需要,您可以使用FillMissing(add_col=False)来覆盖此行为。)

制作表格学习器

考虑嵌入大小的另一种方式是注意每个嵌入都是表格学习模型的输入层,因此每个嵌入的大小实际上是该嵌入层的输出数量。为了更好地理解这一点,我们可以继续创建表格学习器,看看整体结构。

def get_default_nn_layers(num_embeddings: int, num_continuous: int, num_outputs: int, num_layers: int=2) -> List[int]:
    num_input_nodes = num_embeddings + num_continuous  
    first_layer = 2**(num_layers-1) * round((((2 / 3) * num_input_nodes) + num_outputs) / 2**(num_layers-1))

    return [first_layer] + [int(first_layer / 2**n) for n in range(1, num_layers)]num_embeddings = sum(n for _, n in get_emb_sz(tabdata))
num_classes = df_train[dependent_var].nunique()
layers = get_default_nn_layers(num_embeddings, num_continuous=len(continuous), num_outputs=num_classes)
batchsize = 16
train_dl = TabDataLoader(tabdata.train, bs=batchsize, shuffle=True, drop_last=False)
valid_dl = TabDataLoader(tabdata.valid, bs=batchsize, shuffle=False, drop_last=False)
dls = DataLoaders(train_dl, valid_dl)
config = tabular_config(ps=[0.001, 0.01], embed_p=0.04)
nn_model = tabular_learner(dls=dls, layers=layers, config=config, loss_func=CrossEntropyLossFlat(), metrics=accuracy, n_out=num_classes)

这里有很多,让我们来分解一下。首先,我们使用layers参数指定隐藏层的数量和每层中神经元的数量,以覆盖[tabular_learner](https://docs.fast.ai/tabular.learner.html#tabular_learner)的默认值[200,100]。我发现一个有用的经验法则是从两层开始,第一层包含的神经元数量等于输入节点数量加上输出(在此上下文中为类)数量的 2/3 倍,第二层包含这个数量的一半。我的get_default_nn_layers方法简单地获取嵌入的总数、连续变量的总数(每个变量一个输入)、输出类的数量和所需的层数,以返回层的建议起始值(在我们的例子中是[88,44])。这是一个应该像其他参数一样优化的超参数。如果神经元数量过多,网络可能会过拟合,如果神经元数量过少,网络可能会过拟合。

接下来,我们生成表格数据加载器对象,一个用于训练集,一个用于验证集。我们选择 16 个小批量(因为这是一个小数据集,训练很快)。我们选择在每次迭代数据加载器时打乱训练集。

最后,我们创建模型本身。我们传入一个[tabular_config](https://docs.fast.ai/tabular.model.html#tabular_config)参数,这样我们就可以指定退出率。 Geoffrey Hinton 和其他人介绍了一种正则化技术,在训练时随机比例的激活被设置为零,试图减少过度拟合的可能性。ps参数提供两个隐藏层的退出率,而embed_p参数提供嵌入层的退出率。我们选择[CrossEntropyLossFlat](https://docs.fast.ai/losses.html#CrossEntropyLossFlat)方法作为损失函数,并指示模型使用[accuracy](https://docs.fast.ai/metrics.html#accuracy)度量。

检查模型

现在我们创建了模型,让我们看看底层结构。

nn_model.model

我们看到 16 个分类变量的 19 个嵌入层,加上由库生成的另外 3 个嵌入层,后面是嵌入删除层。

8 个连续变量还有一个额外的输入层。这被称为 BatchNorm1d,因为表格学习器应用批量标准化,即标准化每批每个输入的激活的过程(回想一下,这里的批次包含 16 个样本)。这有助于规范网络并加快收敛速度。

接下来,我们有两个隐藏层的第一个。我们看到 126 个输入特征(来自分类嵌入层和 8 个连续层的总共 118 个输出节点),馈入 ReLU 激活层,在应用第一轮非嵌入退出之前,对 88 个输出应用批量归一化。第二个隐藏层有 88 个输入和 44 个输出,在应用最终漏失之前再次进行批量归一化。最后,输出层有 44 个输入和 5 个输出,每个 AdoptionSpeed 类一个。

在我们调整网络和生成嵌入之前,最后一点需要注意。我在上面提到嵌入数组的建议大小仅仅是建议。如果我们想要覆盖任何给定列的默认嵌入大小,我们可以将包含列名和值的字典传递给调用中的emb_szs参数,以构造表格学习器。

embedding_sizes_override={'Breed1': 30, 'Breed2': 24, 'State': 8}
nn_model = tabular_learner(dls=dls, layers=layers, config=config, emb_szs=embedding_sizes_override, loss_func=CrossEntropyLossFlat(), metrics=accuracy, n_out=num_classes)

训练模型

现在让我们训练网络。

valley = nn_model.lr_find()
plt.show()
num_epochs = 5
nn_model.fit_one_cycle(n_epoch=num_epochs, lr_max=valley, wd=0.01)
nn_model.recorder.plot_loss()
plt.show()

我们使用 fit_one_cycle 方法实现单周期回调来提高收敛速度。我们采用来自 fast.ai 学习率查找器的建议学习率,以及权重衰减的默认参数。同样,这里没有列出的这些和其他超参数都可以并且应该进行调整和优化。

检查嵌入物

既然我们已经训练了网络,表格学习者模型将已经学习了分类嵌入。让我们来看看它们。

def embed_features(learner: tabular_learner, xs: TabularPandas) -> pd.DataFrame:
    xs = xs[learner.dls.cat_names]
    for i, col in enumerate(xs.columns):
        embeddings = learner.model.embeds[i]
        embedding_data = embeddings(tensor(xs[col], dtype=torch.int64))
        embedding_names = [f'{col}_{j}' for j in range(embedding_data.shape[1])]

        df_local = pd.DataFrame(data=embedding_data, index=xs.index, columns=embedding_names)
        xs = xs.drop(col, axis=1)
        xs = xs.join(df_local)

    return xsdf_train_embeddings = embed_features(learner=nn_model, xs=tabdata.train.xs)
df_valid_embeddings = embed_features(learner=nn_model, xs=tabdata.valid.xs)
embedded = df_train_embeddings.columns.tolist() 
df_train_embeddings = df_train_embeddings.merge(right=df_train, left_index=True, right_index=True)
df_valid_embeddings = df_valid_embeddings.merge(right=df_train, left_index=True, right_index=True)

embed_features方法采用一个表格学习器模型和一个表格熊猫来返回一个只包含新的嵌入列和值的 dataframe 的副本。上面的代码(重新)为定型和验证数据集生成嵌入值,并将它们与原始列联接,准备将它们输入到决策树模型(或任何其他类型的模型)中。

state_embeddings = [x for x in df_train_embeddings.columns if x.startswith('State_')]
df_train_embeddings[['State']+state_embeddings].head(10)

在这里,我们看到州特征的原始值和这 10 个样本中表示的三个值的相关 7 个分类嵌入(三个唯一的州值是 41326、41327 和 41401,分别对应于柔佛、槟榔屿和吉隆坡)。

我们使用表格熊猫process方法准备我们的测试数据。

tabdata_test = tabdata.new(df_test)
tabdata_test.process()
df_test_embeddings = embed_features(learner=nn_model, xs=tabdata_test)
df_test_embeddings = df_test_embeddings.merge(right=df_test, left_index=True, right_index=True)

构建 CatBoost 模型

照片由克里斯服从Unsplash

我采用的是单标签、有序、多类、不*衡的分类任务。那是相当多的。我们来分解一下。

  • 单一标签意味着一个给定的样本只能分配到一个采用速度类别
  • 序数意味着类之间有一个固有的顺序。它们的范围从 0 到 4,0 表示宠物在上市当天被收养,4 表示宠物在上市 100 天后没有被收养
  • 多类别意味着给定样品可以被分配到三个或更多类别(与二进制分类任务相反,在二进制分类任务中,样品将被分配到两个类别中的一个)
  • 不*衡意味着在训练数据集中类的频率是偏斜的(并且,可能在测试数据集中);例如,制作类的直方图显示,采用速度为 4 的样本数是采用速度为 0 的样本数的 10 倍

所有这一切意味着,我们可以通过几种不同的方式来实现使用 CatBoost 预测采用速度的目标。我们可以使用带有多类损失函数的 CatBoost 分类器来分配给多个类。我们可以使用 WKappa (二次加权 Kappa)评估指标来利用*凡性来改进结果。我们还可以确定类权重,将其传递给模型,以尝试解决训练数据集的不*衡性质。

然而,竞赛中最成功的团队倾向于利用回归技术,结合使用 Nelder-Meads 算法和二次加权 kappa 度量的优化舍入器来帮助确定最佳点,以设置类别之间的阈值。让我们一步一步来。

CatBoost 回归器

首先,让我们定义方法来获取我们的 CatBoost 回归器和相关的数据池

def get_catboost_regressor(iterations: int=1000, loss_function: str='RMSE', eval_metric: str='RMSE', ignored_features: List[str]=['PetID'], depth: int=6) -> cb.CatBoostRegressor:
    return cb.CatBoostRegressor(iterations=iterations, loss_function=loss_function, eval_metric=eval_metric, ignored_features=ignored_features, depth=depth, random_seed=random_state)def get_catboost_pool(df: pd.DataFrame, use_categorical: bool, use_embedded: bool, has_label: bool=True) -> cb.Pool:
    columns = continuous + (categorical if use_categorical else []) + (embedded if use_embedded else []) + ['PetID']
    cat_features = ['PetID'] + (categorical if use_categorical else [])    
    label = df[dependent_var] if has_label else None

    return cb.Pool(data=df[columns], label=label, cat_features=cat_features)

如果我们不覆盖默认参数,返回一个使用默认参数的回归量。我们告诉 CatBoost 忽略 PetID 列,因为尽管我们希望以后使用它,但我们不希望模型试图从 PetID 值中学习。

get_catboost_pool使用带有附加布尔值的数据帧来定义我们希望模型使用的列组。

顺便提一下,熟悉 CatBoost 池文档的读者可能会注意到我们在这里没有使用的参数[embedding_features](https://catboost.ai/en/docs/concepts/python-reference_pool#embedding_features)。尽管有这个名字,这个参数并不涉及分类嵌入。它与用于文本分析的特定类型的嵌入相关(在这里讨论)。

我们将进行三次试验进行比较。

  1. 仅使用连续特征和离散分类特征
  2. 使用由表格学习器生成的连续特征和分类嵌入
  3. 使用由表格学习器生成的连续特征、离散分类特征和分类嵌入

第 1 轮-仅连续特征和离散分类特征

# switch to indicate whether or not to feed the original categorical values into the CatBoost regressor
use_categorical = True
# switch to indicate whether or not to feed categorical embeddings into the CatBoost regressor
use_embedded = Falsetrain_pool = get_catboost_pool(df=df_train_embeddings, use_categorical=use_categorical, use_embedded=use_embedded)
valid_pool = get_catboost_pool(df=df_valid_embeddings, use_categorical=use_categorical, use_embedded=use_embedded)
test_pool = get_catboost_pool(df=df_test_embeddings, use_categorical=use_categorical, use_embedded=use_embedded, has_label=False)model = get_catboost_regressor(iterations=10000)

现在,我们将模型拟合 10,000 次迭代,仅显示每 1000 轮的输出。CatBoost 根据验证集自动评估模型的每次迭代,并恢复到得分最低的迭代。

model.fit(X=train_pool, eval_set=valid_pool, verbose=1000)

在连续特征和离散分类特征上训练的最佳模型的 RMSE 是 1.0760086,在 6,104 次训练迭代后达到。

现在我们拟合了我们的模型,让我们看看预测是什么样子的。

predictions = model.predict(train_pool)
predictions

预测是以一个浮动数组的形式出现的。我们需要以某种方式将它们中的每一个都转换成 0 到 4 之间的整数。我们可以简单地通过舍入来实现,使用 0.5、1.5、2.5 和 3.5 之间的阈值。但是更好的技术可能是使用算法来确定要使用的最佳阈值。

优化舍入器

我们将使用许多领先的竞赛笔记本中引用的优秀的OptimizedRounder类和相关方法。我用的是 Kaggle 里第八名笔记本的版本。定义很长,所以我不会在这里包括它们,但是你可以在我的笔记本版本中看到它们,在这篇文章的顶部。

optR = OptimizedRounder()
optR.fit(predictions, df_train_embeddings[dependent_var].values)
coefficients = optR.coefficients()
coefficients

二次加权 Kappa

优化舍入器使用二次加权 kappa 度量来确定为训练集返回最小误差的上述 4 个边界。二次加权 kappa 对在序数尺度上相距较远的错误分类给予更大的权重。在不使用二次加权 kappa 度量的情况下,被误分类为类别 1 或类别 4 的类别 0 的样本将具有相同的误差。对于二次加权 kappa,错误分类到类别 4 的惩罚比错误分类到类别 1 的惩罚严厉 4 = 16 倍。

做预测

最后,我们可以根据测试数据集进行预测,并将结果提交给 Kaggle。PetFinder.my 的采用是一个纯内核的竞赛,要求所有提交都通过内核输出来完成。(如果你自己尝试这样做,你需要在 Kaggle 内核右下角的笔记本设置中关闭互联网接入。)

test_predictions = model.predict(test_pool)
test_predictions = optR.predict(test_predictions, coefficients)
df_predictions = pd.concat([df_test[['PetID']], pd.DataFrame(test_predictions, columns=[dependent_var])], axis=1)
df_submission = pd.merge(df_sample_submission, df_predictions, on='PetID')
df_submission.to_csv('submission.csv', index=False)

Kaggle 使用连续和离散分类特征对该提交进行评分,得分为 0.35155。

第 2 轮—连续特征和分类嵌入

use_categorical = False
use_embedded = True

在连续特征和分类嵌入上训练的最佳模型的 RMSE 是 1.0775805,在 1,869 次训练迭代后达到。这比第一轮的 0.15%略有下降。

Kaggle 对本次提交的评分为 0.34823,比我们的第一次 Kaggle 评分下降了 0.94%。

第 3 轮—连续特征、离散分类特征和分类嵌入

use_categorical = True
use_embedded = True

在连续特征、离散分类特征和分类嵌入上训练的最佳模型的 RMSE 值是 1.0787290。这比第一轮的最佳 RMSE 下降了 0.24%。

然而,当我们将这个模型提交给 Kaggle 时,我们得到了最好的分数,0.35787,比第一轮提交的 1.80%有所提高。

结论

我们进行了三次训练并提交给 Kaggle,有和没有离散的分类特征和分类嵌入。我们只做了最少量的预处理和特征工程。这些是结果。

我们发现,针对验证数据集的最低 RMSE 是由仅使用离散分类特征的模型实现的(所有模型都使用归一化的连续特征)。然而,在 RMSE,三种模式之间的差异很小。

Kaggle 分数是最重要的指标。它决定了参赛者在整体竞争中的位置。它还提供了模型针对全新数据集做出概括预测的能力的最佳指示。

我们可以看到,在这一指标上得分最高的模型是我们使用原始连续特征、离散分类特征和分类嵌入所尝试的最终模型。该模型的得分为 0.35787,比我们第二好的提交结果提高了 1.8%。这可能不是一个巨大的进步,但在每一分都很重要的 Kaggle 比赛中意义重大。

这也许并不奇怪。尽管分类嵌入是从离散的分类特征中提取的,但其中包含的信息不仅仅是离散值。这是因为表格学习器已经推断出离散值之间的关系,并将这些关系封装在分类嵌入中。

我选择了 PetFinder.my 领养数据集,因为它很小,很容易理解。具有更多分类变量的较大数据集,或者具有更高基数(更多离散值)的分类变量的数据集,可能会从分类嵌入的使用中获益更多。此外,我没有对表格学习器的超参数进行优化,例如调整神经元和层的数量、学习速率、权重衰减、退出、嵌入大小等。所有这些可能使范畴嵌入变得更有意义。

机器学习算法的分类变量

原文:https://towardsdatascience.com/categorical-variables-for-machine-learning-algorithms-d2768d587ab6

Python 和数据仓库中的一键编码

照片由上的爆裂未爆裂

虽然大多数机器学习算法只处理数值,但许多重要的现实世界特征不是数值而是分类的。作为分类特征,它们具有层次或价值。这些可以表示为各种类别,例如年龄、州或客户类型。或者,这些可以通过宁滨潜在的数字特征来创建,例如通过年龄范围(例如,0-10、11-18、19-30、30-50 等)来识别个体。).最后,这些可以是数值标识符,其中值之间的关系没有意义。邮政编码就是一个常见的例子。数字上接*的两个邮政编码可能比数字上远离的另一个邮政编码相距更远。

由于这些分类特征不能直接用于大多数机器学习算法,分类特征需要转换成数字特征。虽然有许多技术可以转换这些特性,但最常用的技术是一键编码。

在一次性编码中,分类变量被转换成一组二进制指示符(整个数据集中的每个类别一个指示符)。因此,在包含晴朗、部分多云、雨、风、雪、多云、雾的级别的类别中,将创建包含 10 的七个新变量。然后,对于每个观察,与类别匹配的变量将被设置为 1 ,所有其他变量被设置为 0。

使用 scikit 编码-学习

使用 scikit-learn 预处理库可以很容易地执行一键编码。在这段代码中,我们将从 dataframe df 对存储在变量 column 中的列进行一次性编码。

from sklearn.preprocessing import OneHotEncoder
encoder = OneHotEncoder()
onehotarray = encoder.fit_transform(df[[column]]).toarray()
items = [f'{column}_{item}' for item in encoder.categories_[0]]
df[items] = onehotarray

这将由一位热码编码器创建的列添加回原始数据帧中,并通过原始列名以及类别级别来命名每个添加的列。

数据库中的一次性编码

假设数据已经存在于数据仓库中。开源 Python 包 RasgoQL 可以直接在仓库上执行这种编码,而完全不需要移动数据,而不是将数据提取到系统上的 pandas 并转换数据以将数据推回仓库。这种方法节省了移动数据的时间和费用,可以处理太大而不适合单台机器内存的数据集,并在新数据到达仓库时自动生成编码变量。这意味着编码不仅适用于现有的数据和建模管道,还自动适用于生产管道。

要使用 RasgoQL 执行一键编码,可以使用下面的代码。

onehotset = dataset.one_hot_encode(column=column)

同样,要编码的列存储在 Python 变量列中。

可以通过运行preview将这些数据下载到 python 环境中,作为数据帧中十行的样本:

onehot_df = onehotset.preview()

或者可以下载完整数据:

onehot_df = onehotset.to_df()

为了让每个人都可以在数据仓库上看到这些数据,可以使用save发布这些数据:

onehotset.save(table_name='<One hot Tablename>',
               table_type='view')

或者,将此保存为表格,将 table_type‘view’更改为‘table’。如果您想检查 RasgoQL 用来创建这个表或视图的 SQL,运行sql:

print(onehotset.sql())

最后,如果您在生产中使用 dbt,并且希望将这项工作导出为 dbt 模型供您的数据工程团队使用,请调用to_dbt:

onehotset.to_dbt(project_directory='<path to dbt project>'

一键编码是许多机器学习管道中应用的最常见的特征工程技术之一。使用 scikit-learn 很容易应用,但是,除非生产系统运行 Python,否则将需要重新编写以将该步骤迁移到生产系统。在现代数据堆栈中,这种迁移通常涉及将代码重写为 SQL。

为了在 SQL 中执行一次性编码,首先要识别分类变量中的所有级别,然后生成 case 语句来填充每个级别的二进制指示符变量。这可能非常乏味。使用 RasgoQL,SQL 代码会自动生成并保存到数据仓库中。这意味着尽管仍在使用 Python 和 pandas,数据科学家已经生成了可以直接用于生产的代码。

将建模过程中开发的代码转换为生产就绪代码的过程可能是整个数据科学生命周期中最耗时的步骤之一。使用一种工具,可以很容易地将建模代码转移到产品中,这可以节省大量的时间。此外,通过将数据准备任务移动到数据仓库,数据科学家可以处理存储在数据仓库中的全部数据,并且不再受到将数据传输到他们的计算环境所花费的时间的限制,也不再受到环境存储器的大小限制。

如果你想查看 RasgoQL,可以在这里找到文档,在这里找到存储库

分类短文本描述:机器学习与否?

原文:https://towardsdatascience.com/categorising-short-text-descriptions-machine-learning-or-not-d3ec8de8c40

我如何自动跟踪我的开支

费用明细示例(来源:作者)

记录零花钱

当我年轻的时候,我喜欢记录我是如何花掉我的零花钱的。我过去常常保留收据,在电子表格上登记交易的金额和类别,并且偶尔我可以生成一个饼图(见下图)来查看我的零花钱的明细,并查看如果我需要为更大的采购节省更多的钱,我应该在哪个费用类别上花费更少。

每月支出明细样本

当我从保留收据转移到获取包括每笔交易的金额和描述等信息的数字报表(例如 csv)时,事情变得更容易了。手动对交易进行分类很无聊,所以我编写了一个 python 脚本来自动化这个过程,它完成了以下工作:

  • 获取所有已分配类别的事务(训练数据)
  • 使用训练数据训练机器学习(ML)模型(由一个单词包 + 随机森林组成),以类别为目标进行预测
  • 将模型应用于新的事务,以获得预测的类别并保存数据

然后我会去分析那批交易。但是,我没有处理所有的交易,而是主要关注交易金额最高或出现频率最高的交易,确保它们的分配类别是正确的(修改不正确的类别),并将它们添加到训练数据堆中,以提高对下一批新交易的预测。这样一来,虽然我没有把所有的交易正确分类,但每一类的大致情况都足以让我清楚地知道我想知道我的支出。

所有这些都是几年前完成的,在金融机构开始提供账户交易和消费习惯分析之前。所以我不再经常使用我的脚本了。也就是说,我总是喜欢在事后想到如何以不同的方式做事情,这有助于我不断学习和改进。

非 ML 溶液?

有很多方法可以解决这个挑战,这是一个针对短文本描述的一般多类分类问题。如果你把这当成一个 ML 问题,你可以把所有的数据科学工具都扔给它,用更好的方法处理和表示数据,而不是用单词袋模型,或者用不同的算法对随机森林进行更好的超参数优化

但是退一步说,ML 是最好的也是唯一的方法吗(对于数据科学家来说,回答这个问题可能不容易)?在玩了 elasticsearch 之后,我一直在思考这个问题,我偶然发现了它的模糊查询函数,并认为这个函数本身就可以完成这个任务。

elasticsearch 中的模糊匹配功能使用 Levenshtein 距离(测量两个字符串序列之间的差异)来获得最接*的匹配条目。例如,给定一个新条目“HONEST BURGER ”,它需要更少的步骤来改变文本以匹配像“BURGER KING”这样的“EAT OUT”条目,而不是像“GATWICK EXPRESS”这样的“PUBLIC TRANSPORT”条目。因此,我们期望这种方法应该具有某种程度的可预测性,可能与 ML 方法的性能相当。

我写了一个简单的 elasticsearch python 客户端(这里是 github repo )来看看这个非 ML 解决方案有多可行。策略是:

试车

为了测试这个非 ML 解决方案,我做了一些粗略的分析,将它与一些 ML 方法进行了比较,如神经网络XGBoost随机森林,只是挑选几个。我在 github 上的示例 jupyter 笔记本包含使用示例数据集(我的银行交易描述的匿名示例,带有注释类别)生成如下所示结果的探索代码。

在我继续讨论结果之前,这里有一些值得一提的假设和警告:

  • 该数据集很小,只有大约 450 个条目,并被分配到 10 多个类别中(见下图)。因此,预计在每个模型的性能评估中会有相当大的波动,即使有交叉验证
  • 总会有改进模型的空间(非 ML 解决方案也是如此)。我们的目的是对不同的技术进行粗略的比较,而不是去竞争和寻找表现最好的技术!因此,所有模型都使用基本默认参数进行训练(即不考虑超参数优化)
  • 对于像这样的多类分类问题,有许多不同的度量可供选择。关于哪一个是最好的,有很多争论,但在这种情况下,我只是坚持准确性和*衡准确性(试图说明类的不*衡),因为这些是简单的度量标准,允许我知道我从所有数据中得到多少正确的预测

训练和测试集中的类别出现分布(随机分割)(来源:作者)

结果

下图显示了应用于训练集和测试集时,经过训练的模型(使用 80%的数据)的性能。以下是值得强调的几点:

  • 此图基于仅将数据分割为一次训练和测试(即,未交叉验证),因此我们需要小心不要过度解读精确的数字(预计这些数字会在小数据集的不同分割中波动)。也就是说,我们仍然可以获得一些高层次的见解
  • 由于加载了所有训练数据,因此保证了通过非 ML 方法(elasticsearch)对训练数据的分类是 100%准确的。
  • 弹性搜索方法的测试性能与其他 ML 技术相当,尽管它在训练数据上完全“过度拟合”

ML 模型和非 ML (elasticsearch)性能的快照比较(来源:作者)

为了更公*地比较预测,应用了k 折叠交叉验证 (CV) (n = 10),准确度和*衡准确度的结果如下图所示。我们可以看到的是:

  • 同样,使用 elasticsearch 预测训练数据的预测是 100%准确的,没有任何扩展,因此在图表上不可见(1.0 处的一条橙色线)
  • 给定一个小的数据集(带有 CV ),测试集上的性能分布是可以预料的
  • 虽然我们不应该过度解读确切的数字,但这个图表表明,elasticsearch 方法的测试性能实际上与其他 ML 方法相当

对训练数据的预测性能(来源:作者)

测试数据的预测性能(来源:作者)

思想

虽然人们可能会争论如何通过更复杂的特征生成、超参数优化或更多层的神经网络来使 ML 方法更加优越(一个相反的论点是,你也可以微调模糊匹配!),我喜欢的非 ML 弹性搜索方法是:

  • 它的简单性(至少后台的数据索引已经由 elasticsearch 为您处理了,这很棒!).
  • 不需要担心重塑 ML 模型要消费的数据
  • 不需要关心微调任何模型或担心“过度拟合”
  • 预测能力相当好(即使有“过拟合”)
  • 虽然其他 ML 方法不能保证预测训练数据的正确类别,但这种非 ML 方法可以保证 100%,因为它只是“记录”训练数据,而不需要“学习”和概括

虽然 ML 方法很神奇,可以解决很多问题,但有时很容易忽略更简单的解决方案,它们也可以优雅地解决手头的问题。此外,如果你卖的产品包含“机器学习”或“人工智能”的流行语,它肯定会比“查找与你的记录最匹配的”卖得更好!

我真的只是触及了弹性搜索的皮毛,并且已经爱上了它。 Elasticsearch 还有 ML 功能,我会在以后进一步探索!

链接:

注意:

  • 文章最初发布在blog.chilledgeek.com
  • 免责声明:本文表达的内容和观点完全是我自己的,不代表我现在或过去的雇主的观点或意见

分类客户支持请求

原文:https://towardsdatascience.com/categorizing-customer-support-requests-7c285a9cf974

以及如何通过众包来实现

图片由at ree23来自 Pixabay

简介

如果你创造了一个成功的应用或产品,恭喜你!没几个人能走那么远。

现在是时候支持您的客户了。他们对你的产品有什么看法?他们是否报告了任何问题或错误?

你需要阅读每一篇评论来了解客户的需求吗?你是怎么看完一堆的呢?

在本文中,您将通过了解世界上最大的浏览器之一是如何做的,来学习如何使用众包来对客户支持请求进行分类。

查看分类

想象一下,一个月收到 15,000 条评论,并试图全部看完。最显而易见的解决方案是雇佣一个内部支持团队来阅读所有的邮件并尝试回复。但是这绝对不是最具成本效益的方法,也可能不是对支持团队时间的最佳利用。

作者照片

事实上,网上遇到的大约一半的评论并不需要你的团队的全部注意力,因为他们没有报告错误或者要求新的特性。

这意味着你可以将你的评论分为两类:非特定的和特定的。

非特异性评论通常表达情绪,但不会告诉你情绪背后的原因。一个例子是“我不喜欢你的应用”。

另一方面,具体的评论可能包含情绪,但最重要的是,它们给出了为什么会有这种感觉的具体原因。例如“我不喜欢你的应用,它没有夜间模式”。这些类型的评论值得转发给你的支持团队。

初始解决方案

那么,如何有效地过滤这两种类型的评论呢?你可以让另一个低层次的内部支持团队手动完成这项工作,或者用 ML 算法对评论进行自动分类。

在这种情况下,浏览器团队对他们的产品评论进行了实验,发现这两种解决方案仍然非常昂贵。第一个需要维持另一个内部团队,第二个需要大量数据来训练模型。

该团队最终决定尝试众包和 Toloka 的现成解决方案。这是获得高质量标签数据的最简单方法,无需设计和维护实际的数据标签管道。这个过程包括上传数据,给出简单的指令,定义类,并为它们提供例子。

最初的方法是将审核分为六个不同的类别,其中只有一个类别涉及需要 rso 或支持团队进一步关注的特定审核。

其他五个类别旨在捕捉不同类型的非特定评论,例如

  • 没有细节的正面评论
  • 没有细节的负面评论
  • “应用程序不工作”评论
  • “应用程序有问题”评论
  • 垃圾邮件

以这种方式分类的非特定评论可以收到专门为此评论类型设计的自动响应。

您可以在下图中看到该管道的示例。

第一条管道,作者照片

与以前的解决方案相比,最初的设计节省了资金,但没有产生预期的结果。标签的准确率只有 78%,而且分类模糊,容易混淆。

第二条管道

由于第一个管道的结果不令人满意,团队决定增加两个新类别:广告和仇恨言论。这是生成的管道的样子:

第二条管道,作者照片

添加两个新的类别将准确率从 78%提高到 88%,并且类别更容易区分。尽管第二条管道的结果令人满意,但该公司决定进一步改善结果。

第三条管道

第三条管道设计有两个等级。在第一阶段,群体注释者将评论标记为正面、负面、垃圾或特定的。具体的评论被直接传递给支持团队,所有其他评论被传递给下一个分类级别的人群。

如下图所示,每个类别在第二层都有自己的子类别。这导致整个管道中有四个不同的标签项目:一个项目在第一层,三个项目在第二层。

第三条管道,作者照片

第三种流水线设计导致了较慢的审查速度,但准确率显著提高,达到 93%。在这一阶段,解决方案是可以接受的,这是项目目前使用的最终管道。

你的项目

如果你想复制这个项目,以满足自己的需求,这是很容易做到的。

注释管道,作者照片

如前所述,您可以使用 Toloka 中的现成解决方案来设置您的项目。然后你可以通过 API 上传数据。结果将以 JSON 文件的形式返回给您。

这种数据格式很容易用自动脚本来分析。例如,您可以使用脚本收集特定类别的所有评论,并向所有评论发送自动回复。

总结

在本文中,我们已经了解了如何使用众包来对产品评论进行分类。您已经看到了一些在浏览器评论分类环境中用于实验的管道。希望对你设计自己的类似项目的管道有所帮助。

如果你想了解这个项目的更多细节,请观看 Natasha 的这个演示。如果你想了解更多关于数据标注管道的信息,请加入这个数据赋能社区

PS:我正在 Medium 和aboutdatablog.com上撰写深入浅出地解释基本数据科学概念的文章。你可以订阅我的 邮件列表 以便在我每次写新文章时得到通知。如果你还不是中等会员,你可以在这里加入https://medium.com/@konkiewicz.m/membership

下面还有一些你可能喜欢的帖子

* *

因果人工智能——支持数据驱动的决策

原文:https://towardsdatascience.com/causal-ai-enabling-data-driven-decisions-d162f2a2f15e

理解因果人工智能框架和算法如何支持决策任务,如估计干预的影响,反事实推理和重新利用以前在其他领域获得的知识。

来源:https://pix abay . com/illustrations/ai-人工智能-sci-fi-7111802/

1.介绍

人工智能和机器学习解决方案在过去十年中取得了长足的进步,它们越来越依赖于根据历史数据生成预测。然而,在需要理解结果背后的实际原因、量化不同干预对最终结果的影响并做出政策决定、对尚未发生的情况进行假设分析和推理等任务上,它们未达到预期。

让我们考虑一个实际场景,以了解企业面临的决策挑战,以及当前的人工智能解决方案如何帮助解决这些挑战:

  • 假设一家公司投资建立了一个员工流失模型,预测员工的离职倾向。在其他因素中,定期的业务培训、不断增加的沟通、员工认可被模型确定为影响最终结果的因素。

虽然生成模型预测和解释影响结果的关键特征是有帮助的,但它不允许做出决策。

为了便于决定减少流失所需的正确干预措施,我们需要回答以下问题:

  • 如果公司决定进行干预并为员工组织定期的季度培训,对最终结果有什么影响?
  • 我们如何比较不同的竞争性干预措施的影响,比如组织季度培训和安排定期的高层领导联系?

在这种情况下,后知后觉地理解不同行为的后果也会有很大的帮助。

  • “如果在过去 1 年中提供季度培训,离职倾向会是什么样的?

以上是一个反事实问题的例子,并且比估计干预更困难,因为要回答的数据没有被观察和记录。

更复杂决策的下一个层次需要理解系统将如何对外部刺激做出反应,以及为什么会发生变化。

来源:图片来自作者

企业如何获得上述问题的答案,从而做出真正的决策?

重要的是要认识到决策会对下游产生深远的影响,因此不仅需要了解系统为什么做出决策,还需要了解决策的影响,以及如何改进决策以实现更理想的结果。

2.通过现有方法进行决策

让我们探索一下,我们是否能够从监督机器学习和其他传统方法中获得上述问题的答案

2.1 有监督的机器学习有帮助吗?

我们的第一直觉可能是采用机器学习方法,从一组特征中预测结果,包括过去接受的培训、参加的会议等。。然而,这里有一个陷阱!

我们需要考虑到干预会改变观测数据中感兴趣变量的统计分布,并使建立模型所依据的基本假设无效。因此,由此产生的预测是不可靠的。

回答反事实问题可能比使用传统的 ML 回答干预性问题更加困难。这里的基本问题是模型从训练数据中学习,在这种情况下,回答反事实问题的数据没有被观察和测量。

2.2 随机对照试验呢?

你能想到的另一种方法是进行随机对照试验(也称为 A/B 测试)。长期以来,这些一直是衡量干预影响的黄金标准。

然而,在使用它们时会遇到一些实际挑战:

  • 确保对照组和治疗组之间的随机分配并不总是可行的,并且组之间的系统差异会使结果产生偏差。
  • 随机对照试验既昂贵又耗时
  • 在许多情况下进行随机试验会产生伦理问题——让人们接受潜在有害的治疗是否道德?

2.3 因果 AI 如何帮助解决挑战

上述问题都是因果问题,与许多传统的机器学习任务不同,不能仅使用被动观察的数据和传统的机器学习算法来回答。基于因果推理原理的机器学习新方法为我们提供了一条充满希望的前进道路。

因果推断弥合了预测和决策之间的差距,并允许研究人员和程序设计人员模拟干预,并根据现有数据推断因果关系。

下图强调了机器学习和因果推理之间的主要区别。

来源:图片来自作者

为什么因果艾

与统计和传统的监督学习方法相比,因果人工智能具有明显的优势。

一些主要优势概述如下:

来源:图片来自作者

  • 当基础数据发生变化时,因果模型仍然保持稳健,因此可以帮助将解决方案推广到看不见的数据
  • 因果人工智能有助于衡量干预的影响,并且是非常有用的决策工具
  • 因果模型还允许我们对我们以前没有见过的情况做出反应,并使解决方案能够计划不可预见的反事实情况
  • 因果模型还允许人类将以前获得的知识推广到看不见的和不同的挑战

3.因果人工智能—快速概述

3.1 基本概念

因果推断能够在保持所有其他变量不变的情况下,从现实世界的观察数据中估计干预对某些结果的因果影响。

回到我们最初的场景,公司决定衡量干预的影响,比如投资员工的季度培训。雇员基础被分成两个治疗组;第 1 组是那些接受过培训的人,第 2 组由没有接受过任何培训的员工组成。

现在,在这种情况下,因果效应估计需要计算员工参加培训和不参加培训时的结果差异,保持其他变量不变。我们需要意识到只有一种结果是可能的,即员工是否参加培训。未被观察到的结果是反事实的。因果推断方法采用各种假设,让我们估计不可观察的反事实结果

使因果推断问题具有挑战性的一些因素讨论如下:

  • 混杂因素:混杂因素是一个影响治疗和结果的变量。从根本上说,如果治疗组和对照组之间除了治疗之外的其他方面有所不同,我们不能肯定地说在结果中观察到的任何差异仅仅是由于治疗。
  • 选择偏差:当被观察群体的分布不代表该群体时发生,我们对计算因果效应感兴趣。如果我们在不处理选择偏差的情况下直接训练数据上的因果模型,则训练的模型在估计另一组中的单元的结果时效果不佳。
  • 测量治疗效果:可以在人群(*均治疗效果)、治疗组、亚组(条件*均治疗效果(CATE))和个体水*(个体治疗效果(ITE))测量治疗效果。

来源:图片来自作者

3.2 因果推理的方法

因果推理有两种基本方法:

I .潜在结果框架:将已接受培训的个人的结果与未接受培训的情景进行比较。因此,对于每个接受过培训的员工,因果人工智能算法将在数据集中找到一个没有接受过培训但在其他重要方面(如年龄、经验、层级、部门、教育等)相同的个人。这种方法的局限性在于,它一次只能测试一种干预措施的影响

二。因果图模型:这种方法帮助我们将不同的因果路径映射到感兴趣的结果,并显示不同的变量如何相互关联。一种广泛使用的方法是结构方程模型,其中我们指定了可能相互作用的变量,它们可能如何相互作用,然后模型分析数据以揭示它们是否确实相互作用。这种模型的局限性在于它只测试特定变量之间的联系。另一种因果图方法是因果贝叶斯网络,它估计数据集中所有变量之间的关系。

这两种方法都使得用真实世界的数据来测试潜在干预的效果成为可能。使它们成为人工智能的是用于揭示大型数据集中因果模式的强大底层算法。

3.3 因果推理算法

因果推断由一系列方法组成,其核心是两种类型的估计方法:

  • 无混杂情况下的估计方法:这里做的假设是我们在测量观察数据中的所有混杂变量。如果不是这样,那么结果会有一些偏差。匹配法和重新加权法是两种常用的估计方法。实施的关键主题是创建一个“伪群体”来解决由于治疗组和对照组的不同分布而带来的挑战。这是通过使用距离度量(匹配方法)找到最接*的匹配,对样本进行加权(反向倾向加权)来实现的。除此之外,其他估计方法包括元学习器、基于森林的估计器(非线性模型)等。
  • 准实验的估计方法:在准实验中,预先存在的组已经接受了不同的处理,并且在组的创建中缺乏随机性。常用的方法包括简单的自然实验、工具变量(IV)和回归-不连续模型。

4。实际实施

4.1 开源 Python 库进行因果推断

因果人工智能正在获得很大的吸引力,下面是我发现在进行因果分析时非常有帮助的两个 Python 库:

来源:图片来自作者

  • DoWhy — 是来自微软的 Python l 库,旨在激发因果思考和分析。它为因果推断提供了一个原则性的四步接口,重点是显式建模因果假设并尽可能地验证它们。更多关于图书馆的细节可以在 https://github.com/microsoft/dowhy找到
  • Causallib—IBM 的库在统一的 scikit-learn 启发的 API 下提供了一套因果方法。它实现了元算法,允许插入任意复杂的机器学习模型。这种模块化方法支持高度灵活的因果建模。更多关于图书馆的细节可以在 https://github.com/IBM/causallib/找到

4.2 解决岗位培训干预挑战

在研究论文用实验数据评估培训项目的计量经济学评估中,讨论了测量工作培训对收入增长的因果影响的相关挑战。

5.前方道路

  • 因果学习与机器学习的集成: 缺乏因果理解使得传统的机器学习解决方案很难对看不见的数据或当对环境进行干预时进行归纳。当从更少的数据中学习时,因果学习是有效的,对环境的任何变化都更鲁棒,并对没有观察到的数据的反事实场景做出响应。目前正在进行大量结合机器学习机制和结构因果模型的研究工作。
  • 促进伦理和社会责任的 AI: 因果学习是其中一个关键的方法,研究人员正在利用它来开发社会责任的 AI。它在指定社会类别干预的性质和时机、理解明确的因果假设、因果发现、中介分析和公*评估技术的含义方面发挥着关键作用。
  • 下一代人工智能解决方案的开发: 因果模型正被用于重新利用以前获得的知识,以产生对新领域的洞察力。一些前沿工作包括学习实时策略和应用学到的知识来概括不同的领域。因果模型可以在加强防御敌对攻击方面发挥至关重要的作用因果学习技术也正被用来帮助强化学习的培训更有效,让他们从培训开始就做出明智的决定,而不是采取随机和不合理的行动。

6.参考

免责声明:本文分享的观点是我个人的观点,不一定代表富达国际或任何关联方的观点。

因果发现:小公鸡的啼叫会导致太阳升起吗?

原文:https://towardsdatascience.com/causal-discovery-does-the-cockerel-crowing-cause-the-sun-to-rise-f4308453ecfa

10 行 Python 代码来自动发现因果关系,你一定要看看

照片由叶戈尔·迈兹尼克Unsplash 上拍摄

介绍

我最*的研究重点是因果推理,这是由我收到的越来越多的客户要求我超越机器学习预测,回答“如果?”键入问题以推动影响和结果。

最初引起我兴趣的事情之一是——“因果图是如何构建的?”。在许多网上的例子中,它们似乎是在没有解释节点和链接是如何被识别的情况下完全形成的。

这让我读了朱迪亚·珀尔和达纳·麦肯齐的《为什么之书》(读了好几遍!)并且表达的一种观点是,你不能仅从数据逆向工程因果图,也称为有向无环图(DAG)。

数据可以揭示相关性,但不能揭示因果关系,那么怎么可能“发现”因果关系呢?

注意:本文中的所有数据集都获得了公开使用的许可,有关所有来源和参考资料,请参见文章末尾的参考资料部分。

在我们深入因果发现之前,请考虑…

通过我的推荐链接加入 Medium(如果你使用此链接注册,我将收取一定比例的费用)。

每当我发表新故事时,订阅一封免费电子邮件。

快速浏览我之前的文章

下载我的免费战略数据驱动决策框架

访问我的数据科学网站— 数据博客

从数据中可以建立因果关系吗?

考虑以下二进制数据集中的值计数…

注:日出/小公鸡数据集是作者创作的合成数据,完整来源和参考详情请见文末参考资料部分。

作者图片

我们可以看到,在 90.25%的观察中,公鸡在太阳升起时啼叫,但仅凭数据,我们如何知道是太阳升起导致公鸡啼叫,还是公鸡啼叫导致太阳升起?

当太阳没升起时小公鸡啼叫的次数和当太阳升起时小公鸡保持沉默的次数实际上是相同的(50,000 次对 47,500 次),所以通过比较相对数量的数据无法找到因果答案。

一种方法可以是着眼于时间方面。如果太阳总是在小公鸡打鸣之前升起,这将是一个很好的因果关系指示器,但是如果我们的小公鸡是一个早起的人呢?

答案是咨询领域专家。如果我们能召集一个由占星家、物理学家和家禽饲养者组成的团队,他们会得出结论:是太阳导致了公鸡啼叫,而不是相反!

费德里科·雷斯皮尼在 Unsplash 上的照片

那么图书馆能做些什么呢?

我没有被太阳升起和公鸡啼叫所吓倒,而是出发去寻找某种已经存在的东西,这种东西也许能够在数据的基础上发挥因果发现的魔力。

有一个叫“NOTEARS”的算法,确实号称可以实现因果发现,那我们就来试一试,看看它能做什么……

注:天花数据集是作者创建的合成数据,完整来源和参考资料详情请见文末参考资料部分。

作者图片

这个数据集正在模拟天花疫苗接种中的因果关系。让我们对它运行 NOTEARS 算法,看看它能做什么…

[('Reaction?', 'Vaccination?'),
 ('Smallpox?', 'Reaction?'),
 ('Death?', 'Reaction?'),
 ('Death?', 'Smallpox?')]

如果我们设想由 NOTEARS 产生的结果,它看起来会是这样的…

作者图片

看起来不太好,是吗?根据诺埃尔的说法,死亡会导致天花。时间相位告诉我们天花是最先出现的,所以它不可能是由死亡引起的。天花也不能引起反应(疫苗引起反应),反应当然不能引起疫苗接种。

利用领域专家的知识,我们可以很容易地确定疫苗接种对患者是否有反应以及他们是否继续发展疾病有因果关系,并且反应和天花都与死亡有因果联系…

作者图片

因此,我们已经确定,即使在非常简单的因果模型中,NOTEARS 因果发现算法也不会产生正确的结果。此外,我以前的一篇文章的读者指出,NOTEARS 在 Python 3.9 中不工作,当我准备升级时,这是另一个大问题。

如果你想了解更多关于笔记不适合因果关系的内容,这里有一篇优秀的学术论文——https://arxiv.org/pdf/2104.05441.pdf(Marcus Kaiser)和马克西姆·希波什(Andrew Smith)。

在我们放弃全自动因果发现之前,让我们来看看 NOTEARS 的一个替代方案,即“套索”算法

注:人口普查收入/毕业生收入数据集被授权公开使用,请参见文章末尾的参考资料部分,了解完整的来源和参考资料详情。

作者图片

作者图片

哦,亲爱的,套索是一场灾难!它已经预测到一切事物会引发其他事物,节点也会引发它们自己!

我最后的尝试是尝试 GES,GIES 和 LINGAM 算法,但这些都需要 R 库。我不使用 R,即使我成功地进行了正确的配置,我也永远无法创建其他数据科学家可以使用的可移植代码。

可用的因果发现库和算法不起作用,这强化了“为什么之书”中表达的观点,即因果关系不能仅从数据进行逆向工程。

这个结论促使我开发了自己的方法…

照片由阿曼达·琼斯Unsplash 拍摄

坦白说,我还没有写因果发现算法。更确切地说,我的算法实现了一个扭曲的相关发现(没有双关语的意思!).

当我放弃因果发现库时,我仍然需要一种方法来可视化因果关系,作为与领域专家对话的起点。

我推断我可以很容易地计算特征/节点之间的相关性,这至少是一个好的起点。

我的想法如下——在因果发现模型中,我们通常识别“效果”,即我们感兴趣的数据特征(就像机器学习预测中的“目标”)。在天花的例子中,这是“死亡?”,在毕业生收入示例中是收入“大于 5 万”。

因此,如果任何特征和“效果”之间存在相关性,因果方向必须是从其他特征到效果,因为它在“线的末端”。

我的下一步是开发一个递归算法,可以如下所示…

作者图片

必须强制排除重复和双向链接。我还希望能够明确地包含或排除连接(边)以及明确地排除特征(节点)。

这是我实现因果发现的 10 行递归 Python 代码

行动中的解决方案

为了展示这个解决方案是如何工作的,我选择了一些关于酒店预订取消的数据。

让我们从读取数据开始,看一看相关性…


作者图片

发现算法的第一次运行涉及将迭代次数保持在 1,因此我们只是查看与“效果”的相关性(应该是因果关系),即酒店预订是否被取消…

作者图片

好了,这是一个不错的开始,让我们将迭代/递归/层数增加到 3,稍微调整一下相关阈值,看看我们会得到什么…

作者图片

好的,也不太坏,但是有点“忙”,所以下一步是排除一些我们怀疑可能会引起一些噪音的节点(注意:在真实的业务案例中,我们将在这个阶段与我们的领域专家交谈)。

作者图片

这开始变得更好看了。请记住,我的算法确信“被取消”的链接是因果的,因为它是“结果”,因此没有什么在它之后。

虽然树的其他层只是相关的,但箭头的方向是简单地根据算法发现它们的顺序添加的。

与领域专家合作(或者我自己在酒店预订方面的经验!)我注意到以下几点——

  • 从“不同的预订”到“提前期”的链接方向是错误的,因为较长的预订提前期增加了客房预订发生变化的可能性,而不是相反。
  • “登记变更”和“不同的未分配”之间的相关性低于阈值,但它可能是一个重要的因果关系,因此需要包括在内。

下一次尝试指示算法进行这些修正-

作者图片

最后一次迭代:考虑“未观察到的混杂因素”

“未观察到的混杂因素”是我们认为通过提供一些节点之间的联系而影响我们的因果模型的一个因素,但尚未测量,因此未包括在图表中。

我去酒店的经历和我对酒店数据集的了解使我怀疑有一个“未观察到的混杂因素”影响着酒店数据。

数据中有两家酒店——一家“城市”酒店和一家“度假”酒店。这让我假设这个因果模型中未被观察到的混杂因素是“住酒店的原因”

我的假设是,度假者主要住在度假酒店,商务人士和城市游客主要住在城市酒店。

此外,我假设这两组有不同的行为,这是“未观察到的混杂因素”(未观察到,因为数据没有捕捉到“停留的原因”)。

NOTEARS 和其他类似的算法不能解释“未观察到的混杂因素”,但是我开发的算法可以将它们考虑在内,方法是将它们明确地包括在内,如下所示…

作者图片

最终迭代的结果是酒店数据中的因果关系的一个可信的、可读的和可理解的可视化,我有信心与领域专家一起探索和提炼。这些都是重点…

  • 有一个“未被观察到的混杂因素”是停留的主要原因(度假与商务/城市休息)。
  • “国家”对未被观察到的混杂因素有因果影响——那些从一些国家旅行的人更有可能是在度假。
  • 这同样适用于“全部来宾”。大型聚会更有可能是度假,单身人士更有可能是商务旅行,单身人士更有可能是城市度假。
  • 未观察到的“停留原因”对“交付周期”、“酒店类型”和“存款类型”有因果影响。
  • “交付时间”、“不同的订单”、“酒店类型”、“存款类型”和“停留原因”(U)都对“取消”有因果关系。

结论

因果发现是超越相关链接发现因果链接的过程(即箭头属于相关线的哪一端?).用像 NOTEARS 这样的算法来实现自动化是不可能的,或者至少是极其困难的,因为答案不仅仅存在于数据中。

然而,仍然迫切需要将因果关系可视化,以便领域专家可以对其进行探索,从而构建因果图(也称为有向无环图或 DAG)。

在这篇文章中,提出了一种基于增强关联发现的算法,该算法仅使用了 10 行 Python 代码,领域专家可以使用该代码来重复地改进敏感度(阈值)以及校正、添加或移除链接(箭头)并排除不相关的节点。

在未来的文章中,这些因果图将用于建立一个成熟的因果机器学习模型,可以回答复杂的“如果会怎样?”键入客户和企业领导的问题。

如果你喜欢这篇文章,请考虑…

通过我的推荐链接加入媒体(如果你使用这个链接注册,我将收取一定比例的费用)。

https://grahamharrison-86487.medium.com/membership

每当我发表一个新故事时,订阅一封免费电子邮件

快速浏览我以前的文章

下载我的免费战略数据驱动决策框架

访问我的数据科学网站— 数据博客

参考

日出/公鸡打鸣数据

天花数据

毕业生收入/人口普查收入数据

酒店预订数据

因果效应

原文:https://towardsdatascience.com/causal-effects-f30f962ffff3

什么是治疗效果,如何计算?

本文是关于因果关系的三部曲系列 的延续。之前的帖子主要关注因果推断因果发现。这篇文章基于这些想法,描述了三种不同的因果关系。在以后的文章中,我们将讨论从观察数据计算因果效应,以及使用 do 运算符评估因果效应。

关键点:

  • 因果效应通过比较不同治疗值的结果来量化治疗的影响

为什么?

因果的概念在我们(即人类)如何理解我们周围的世界中起着核心作用。例如,“人行道是湿的,因为昨晚下雨了”,使我们能够在新的情况下做出推论,例如为什么天井家具是湿的?

这就归结到一个根本问题,为什么?为什么会这样?这是什么原因造成的?或者说,这将走向何方?会有什么效果?

在许多实际环境中(例如商业、医疗保健、教育),不仅知道什么导致什么是有利的,而且知道多少是有利的?换句话说,庭院家具的潮湿程度是由昨晚的雨水和今早的洒水器造成的?回答这个问题多少钱?就是的因果关系。

背景

在深入探讨之前,我们如何才能回答这个“有多大的问题?“我们需要定义一些东西。熟悉这些概念的人可以随意跳到下一节。

结果、治疗和协变量

我们必须区分研究中的变量,即:结果、治疗和协变量。

一个结果是我们最终感兴趣的变量。对企业来说,可能是利润,对临床医生来说,可能是心脏病的发病率,等等。

治疗是我们为了影响结果变量而想要改变的变量。例如,企业可能希望改变广告支出来影响利润,或者临床医生可能改变剂量来影响心脏病的发病率。

然后是反变,基本都是别的。对于临床的例子,这可能是:年龄,体重,身高,他们是吸烟者吗?、运动水*等。在整个系列中,结果将由 Y 表示,治疗由 X 表示,协变量由 Z 表示。

潜在成果框架

因果关系中的一个重要概念是潜在结果框架,这是一种估计因果影响的方法。这个框架建立在一个核心问题上。考虑一个随机对照试验(RCT) 的受试者,如果受试者接受一种治疗( X =x_1)代替另一种治疗( X =x_0),结果变量( Y )的值会是多少。

这是一个反事实问题的例子,即如果 A 而不是 B 发生了什么 ?这个问题的根本挑战是每个受试者只能接受一次治疗。例如,如果我吃了一片泰诺,我的头痛消失了,没有办法观察如果我不吃药会发生什么。

虽然我们永远无法真正观察到反事实(即,如果我接受治疗 A 而不是 B 会发生什么),但这一理论概念是讨论因果关系的良好起点。

3 种因果关系

在这里,我描述了 3 种不同类型的因果关系。虽然所提供的表达式考虑了布尔治疗(例如,服用或不服用避孕药),但是这些表达式可以推广到任何数量的治疗级别。

1)个体治疗效果(ITE)

单个治疗效果的视觉分解(ITE)。图片作者。

一个个体治疗效果(ITE) 量化了治疗对特定个体的影响。它通过比较不同治疗水*的结果来做到这一点。这就像之前的泰诺例子一样。

在一种情况下,我吃了一片泰诺,头痛就消失了。然后假设,在反事实的情况下(即,如果我不吃药),我的头痛不会消失,但我感觉好一点了。为了评估这种情况下的 ITE,我们将比较服用避孕药的结果和不服用避孕药的结果。

下图给出了一种表达方式。

表达个体治疗效果(ITEs)的一种方式。图片作者。

其中, Y(1) 表示服药方案的结果值(即 X =1),而 Y(0) 表示未服药方案的结果(即 X =0)。我将再次强调,在现实中,我们只能观察到 ITE 方程中的两种情况之一。因此,ITEs(充其量)是我们估计的东西,但从不直接测量,即使这样也是有挑战性的。挑战在于 ite 要求很多,即在特定的时间、特定的情境下,对特定的个体有什么治疗效果?

另一种方法是扩大我们的人口。然后,我们确实可以评估在特定时间和背景下的治疗效果,但不是针对特定的个人。我们将获得群体水*的治疗效果,这就引出了第二种因果效应。

2)*均治疗效果(ATE)

*均治疗效果(ATE)的视觉分解。图片作者。

如前所述,ite 的一个问题是它们永远无法真正被测量。但是,如果我们把目标转向人口水*的影响呢?这正是*均治疗效果(ATE) 所量化的。换句话说,ATE 评估治疗对人群的预期影响。就像 ITE 一样,它通过比较不同治疗水*的结果来工作。然而,它不是考虑一个特定的个体,而是评估整个群体。

这可以表示如下:

表示*均治疗效果的一种方法。图片作者。

其中 E{V} 代表某个变量 V 的期望值(即*均值)。 Y_i(1) 表示第 I 个受试者服用药丸方案的结果值。并且, Y_i(0) 表示第 I 个受试者在无药丸情况下的结果值。使 ATE 更容易估计的是期望值。也就是说,我们感兴趣的是*均值,而不是点估计值。

这是量化治疗效果的最重要的方法之一。它通常出现在随机对照试验(RCTs) 中,在这种情况下,整个人群被随机分成 2 个相等的组(例如,服药组和不服药组)。非常重要的一点是,在随机和相同的组中进行分割,以便对于足够大的群体规模,这两个组是直接可比的。这就把前面的 ATE 方程简化成非常容易计算的东西(见下面的表达式)。

随机对照试验(RCT)中的*均治疗效果(ATE)[1。图片作者。

其中 j 代表治疗组,k 代表对照组。换句话说,ATE 可以通过比较两个亚群的*均结果值来直接计算。

RCT 中计算 ATE 的视觉故障。图片作者。

虽然 RCT 使得 ate 的计算非常容易,但是它们是昂贵的(不仅仅是美元,而是时间和精力)。此外,它们可能在所有情况下都不实际、不道德或不可行。在本系列的下一篇博客中,我将讨论一种更便宜且更容易实现的替代计算 ate 的方法。

3)治疗组(或对照组)的*均治疗效果

与 ATE 相关的一个量是被治疗者(ATT) 的*均治疗效果。这与 ATE 相同,它估计治疗对人群的预期影响,但有一个关键的区别。我们没有考虑整个人群(即治疗组和对照组的受试者),我们只考虑实际接受治疗的受试者

这可以表示如下:

表示被治疗者(ATT)*均治疗效果的一种方式[ 1 ]。图片作者。

换句话说,是观察到治疗(X= 1)时的预期治疗效果。看这个等式,我们只能观察到第一项( Y_i( 1 ) ,而第二项是反事实。虽然这再次提出了计算这个理论量的挑战,但它有实际的重要性。****

回到泰诺的例子,ATT 将量化服用泰诺的人对头痛状况的影响,与任何头痛的人形成对比。ATE 和 ATT 之间的关键实际差异是,在 RCT 的背景之外,有一些典型的共同因素会使 ATE 和 ATT 不同(例如,年龄、获得药物的途径、耐受性等)。这可能看起来像是,ATT 吃了,因为那些服用药丸的人往往比那些没有服用的人更频繁地服用泰诺,因此对其效果产生了耐受性。

我们同样可以计算对照组(ATC) 的*均治疗效果,其中我们评估 ate,但针对对照组人群。****

一种表示对照组(ATC)*均治疗效果的方法。图片作者。

在这里,我们可能观察到相反的情况,因为那些没有服用避孕药的人在头痛时往往不会服用泰诺,因此更容易受到它的影响。[ 2 ]中提供了关于 att 和 ATC 的详细讨论。

注:另一种量化效果的方法

在前面的讨论中,我已经将因果关系表示为结果的差异,例如ATE = E { Y _ I(1)—Y _ I(0)}。虽然这种配方在许多应用中工作良好,但它不是我们唯一的选择。计算因果关系的另一种方法是通过风险比。这个公式类似于前面的公式,但是用除法代替了减法,如下图所示。

ATE 的另一种表达方式。我们用一个比率来代替结果的差异。图片作者。

do 运算符的因果关系

到目前为止,我们已经讨论了 3 种类型的因果关系,并给出了每一种的方程式。在许多情况下,这些都是表达因果关系的有效方式,然而,可以说还有一种更深层次的方式来思考它们。

这通过使用 do 操作符来完成,该操作符是干预** [ 3 的数学表示。在之前关于因果推理的帖子中,do 运算符被表述为允许我们回答反事实问题并最终计算因果效应的东西。然而,我们通过一个关于因果关系的完整的讨论(假定)做到了这一点,并且没有一个 do 运算符被使用。**

原因是 do 运算符涉及的数学可能不是每个人都能立即理解的(即 do-calculus)。我写这篇博客的目的是为以后使用 do 运算符发表因果效应的文章提供一个容易访问的起点。

**

实际问题

虽然这种因果关系的讨论可能已经给了我们一个坚实的理论基础,但仍然有一些挥之不去的实际问题:

  • 我该如何处理这些反事实的术语?
  • 实践中我们是否仅限于随机对照试验?观察数据呢?
  • 有什么软件工具可以处理这些东西?

为了解决这些问题,在下一篇文章中,我将介绍一套技术,我们可以用这些技术从观测数据中计算因果关系。

这些方法都使用一种叫做 的倾向得分 的东西,这是基于其他特征的某人接收治疗值的估计概率。这提供了一种处理研究亚人群中可能存在的偏倚的方法。

资源

更多关于因果关系 : 因果关系概述 | 因果关系:简介 | 因果推断 | 因果发现

连接 : 我的网站 | 预定电话 | 邮件我

社交:YouTube|LinkedIn|Twitter

支持 : 给我买杯咖啡 ☕️ | 成为会员 ⭐️

https://shawhin.medium.com/membership

[1] 减少观察性研究中混杂效应的倾向评分方法简介作者 Peter C. Austin

反事实和因果推理:社会研究的方法和原则

【3】朱迪亚·珀尔著因果推理导论**

通过 Dag 的因果效应

原文:https://towardsdatascience.com/causal-effects-via-dags-801df31da794

打破后门和前门标准

这是关于因果关系系列的第四篇文章。在本系列的上篇中,我们探讨了可识别性的问题。换句话说,是否可以从给定的数据中评估因果关系?在这里,我们看到了一个系统的三步过程,在一个观察到所有变量的因果模型中,表达任何因果效应。然而,当我们有了不可测量的混杂因素时,问题就变得有趣多了。在这篇文章中,我讨论了两个快速简单的评估因果关系的图形标准。

可识别性

可识别性是因果分析中的一个中心问题,即能否从给定的数据中评估因果效应?之前的博客 中,我们看到了一个系统的三步过程来回答这个所谓马尔可夫因果模型的问题。这些因果模型满足两个条件:1)没有循环,2)没有同时引起两个或更多变量的不可测量的噪声项。这类模型可以用一个有向循环图DAG 来表示。马尔可夫和非马尔可夫 Dag 的例子如下所示。

非马尔可夫和马尔可夫 Dag 的简单例子。图片作者。

马尔可夫条件很重要,因为它保证了可识别性。换句话说,如果我们的因果模型是马尔可夫的,那么因果总是可识别的 [ 12 。虽然这是一个强大的洞察力,但它也有局限性,因为我们可能对非马尔可夫的因果模型感兴趣。

例如,如果存在不可测量的混杂因素,马尔可夫模型可能变成非马尔可夫模型。我们在之前的博客中看到了这样的例子。下图显示了相同的示例。

马尔可夫模型如何变成非马尔可夫模型的例子?图片作者。

我们从最左边的马尔可夫模型开始。然后假设吉米忘记打开 Z2 传感器,所以我们没有变量 Z2 的观测值。中间的模型描绘了这种情况。这里我们有一个未观察到的变量(Z2)和两个孩子(Z3 和 Y)。

我们可以通过从 DAG 中移除未观察到的变量并用双向边[ 1 ]连接它的两个子节点来等效地表示这种情况。直观上,双向边表示 Z3 和 Y 之间经由 Z2 的统计相关性,但是如果没有 Z2 的观察,这将在我们的数据中表现为两者之间的虚假关联。

注意,最右边的两个因果模型不是马尔可夫的。中间的一个有一个不可测量的噪声项,同时引起两个变量,最右边的一个有一个周期。虽然这些模型不是马尔可夫模型,但是 X 对 Y 的因果关系确实是可以识别的(稍后会详细介绍)。

微积分 3 法则

在一般情况下,可识别性问题总是可以用 Do-Calculus 的规则 [ 34 来回答。这些是我们可以用来操纵介入分布的 3 条规则——换句话说,用不包含 do 运算符的概率来表达包含 do 运算符的概率。

关键的一点是这套规则是完整的,也就是说如果不能用这 3 条规则建立可识别性,那么因果关系是不可识别的

Do 演算的规则 —给定 X、Y、Z 和 W 是因果模型 G 中任意不相交的变量集,Do 演算的规则如下所示。

Do-Calculus 的 3 个规则[ 4 ]。图片作者。

虽然这些规则简洁而完整,但这里有很多值得推敲的地方。例如,看规则 1,只有当 Y 和 Z 在通过删除所有到 x 中的节点的输入箭头而获得的图中条件独立时,我们才能忽略变量 W 的集合。

如果没有对这些规则和相关概念的强烈直觉,将它们应用到问题中可能会很慢并且很有挑战性。这就是两个快速简单的可识别性测试有所帮助的地方。

2 快速简单的图形标准

虽然 Do-Calculus 的规则为我们提供了一套完整的评估因果效应的操作,但对于复杂的 Dag 来说,在实践中使用它们可能很困难。为了帮助解决这个困难,我们可以求助于两个评估可识别性的实用图形标准:后门标准(BDC)和前门标准(FDC)。

与 do-calculus 的规则不同,这些标准并不完整。这意味着即使他们不满意,因果关系仍然可以识别。然而,它们的关键用途是,在诉诸微积分规则之前,它们可以作为我们可以用来回答因果问题的实用测试。

1)后门准则

后门标准(BDC)是一种相对快速和简单的测试,用于评估一组节点是否足以回答可识别性问题。换句话说,它告诉我们需要测量哪些变量来计算特定的因果效应。

在定义 BDC 之前,我们首先必须用两个关键概念武装自己:一个后门路径阻塞。

两个节点(比如 X 和 Y)之间的后门路径是从指向 X 的箭头开始并在 Y3处终止的任何路径。例如,下图中的这些都是后门路径。

  • X-Z1->Z3->Y
  • X-Z1->Z3<-Z2->Y
  • X-Z3->Y
  • X-Z3<-Z2->Y

评估后门标准的 DAG 示例。图片作者。

注意,我们在构造后门路径时忽略了箭头(当然,指向 X 的除外)。后门路径背后的直觉是它们在两个变量[ 2 ]之间进行虚假关联。

这里的第二个关键概念是阻塞 [ 3 ]。当且仅当路径 p 被一组节点{Z_i}阻塞时,

  1. p 包含一个链 A -> B -> C 或者一个叉 A C,使得 B 是{Z_i} — 中的一个元素这就是我们可能直观地认为是阻塞的
  2. p 包含一个对撞机(即一个倒叉)A-> B—{ Z _ I }中没有一个节点产生虚假的统计关联(伯克森悖论)

我们现在可以结合这些概念来定义后门标准[ 3 ]。

后门准则— 一组节点{Z_i}满足相对于(X,Y)的 BDC 如果

  1. 没有节点是 X 的后代(即{Z_i}专门位于后门路径中)
  2. {Z_i}阻塞 X 和 Y 之间的所有后门路径

将 BDC 应用于上图,我们可以看到三组节点满足这个标准[ 2 ]。

注意集合 {Z3}不满足 BDC ,因为它是一个碰撞器,因此不满足我们之前对阻塞的定义。

集合{Z1,Z3}、{Z2,Z3}和{Z1,Z2,Z3}称为充分集合(也是容许集合)。它们告诉我们测量哪些变量来计算 X 和 y 之间的无偏因果关系。只要满足 BDC,我们就可以使用以下等式来表示干预分布。

当满足后门标准时,用观察分布表示的介入分布。图片作者。

倾向评分方法说明

我们在早期博客中看到的将后门标准与倾向得分(PS)方法联系起来的一个关键点是当后门标准不满足 [ 2 时,倾向得分方法会失败。换句话说,如果用于得出倾向得分的变量不能构成一个足够的集合,它们可能会在因果效应估计中引入偏差。

虽然有人可能(天真地)认为 PS 模型中使用的变量越多越好,但这可能会适得其反。在某些情况下,如上述 DAG 中的变量 Z3,包含特定变量可能会增加倾向得分匹配偏差[ 2 ]。

2)前门标准

我们可以用来评估可识别性的另一个快速简单的测试是前门标准****(FDC)3。一组节点{Z_i}满足相对于(X,Y)的 FDC,如果

  1. {Z_i}截取从 X 到 Y 的所有有向路径
  2. 从 X 到{Z_i}的所有后门路径都被空集阻塞
  3. 从{Z_i}到 Y 的所有后门路径都被 X 阻塞

让我们看另一个例子。考虑下面的 DAG。

评估前门标准的 DAG 示例。图片作者。

我们可以先求一组满足条件 1 的变量。为此,我们列举了从 X 和 y 开始的所有有向路径。

  • X -> Z1 -> Y
  • X -> Z2 -> Y
  • X -> Z2 -> Y

由此,我们可以看到{Z1,Z2}满足条件 1。但是我们还没有完成。我们需要通过对照条件 2 和 3 来检查这个集合是否满足 FDC。

为了检查条件 2,我们需要查看从 X 到{Z1,Z2}中每个节点的所有后门路径。

X 和 Z1

  • X Y
  • X Z3 -> Y

X 和 Z2

  • xY
  • X Z3-> Y
  • X Z3-> Y

We can see that all 这些路径被阻塞因为它们包含一个碰撞器 Y。

最后,我们通过查看{Z1,Z2}和 y 中每个变量之间的所有后门路径来检查条件 3

Z1 和 Y

  • Z1 Y
  • Z1 Z3 -> Y
  • Z1 Z2 -> Y
  • Z1 Z2 Y

Z2 和 Y

  • Z2 Y
  • Z2 Z3 -> Y
  • Z2 Z1 -> Y

事实上,我们看到所有这些路径都被 X 阻挡。因此,我们得出结论,为了估计 X 和 Y 的因果关系,我们只需要测量变量 X、Y、Z1 和 Z2。我们可以使用下面的等式来做到这一点,该等式根据观察分布(包括 X、Y、Z1 和 Z2)来表达介入分布 P(Y|do(x))

当满足前门标准时,用观察分布表示的介入分布。图片作者。

注意,对于{Z_i}中的每个变量,我们对变量 x 的所有可能值求和。

上面给出的 FDC 示例 DAG 的介入分布。图片作者。

关键点:

  • 可识别性与回答以下问题有关:能否从给定数据中评估因果关系?
  • Do-Calculus 的 3 规则给了我们一套完整的运算来评估可识别性。
  • 我们还可以通过两个快速简单的图形测试来评估可识别性:后门标准和前门标准。

下一步是什么?

https://shawhin.medium.com/membership

资源

更多关于因果关系 : 因果关系概述 | 因果关系:介绍 | 因果推断 | 因果发现

连接 : 我的网站 | 预定电话 | 邮件我

社交:YouTube|LinkedIn|Twitter

支持 : 给我买杯咖啡 ☕️ | 成为会员 ⭐️

田,明珠(2002)。因果关系的一般识别条件。第十八届全国人工智能会议论文集www.aaai.org

珀尔,J. (2010)。国际生物统计学杂志:因果推理导论。《国际生物统计学杂志》6 (2),第 7 条。https://doi.org/10.2202/1557-4679.1203

田,j .,&施皮瑟,I .(未注明)。关于识别因果关系

珍珠杂志(2012)。重新审视微积分。第二十八届人工智能不确定性会议论文集8 月,4–11 日。http://arxiv.org/abs/1210.4852

珀尔,J. (2009 年)。因果分析中的神话、困惑和科学。

通过 Do 运算符的因果关系

原文:https://towardsdatascience.com/causal-effects-via-the-do-operator-5415aefc834a

将观察转化为干预

这是关于因果关系系列的第三篇文章。在的上一篇文章中,我们回顾了一套通过倾向得分评估效果的实用方法。然而,这些方法的缺点是它们没有考虑到不可测量的混杂因素。在本文的结尾,我们将看到如何克服这个缺点。但是首先,我们需要后退一步,重新评估我们如何看待因果关系。

要点:

  • do 运算符是干预的数学表示
  • 干预是对数据生成过程的有意操纵
  • 使用珀尔的结构因果框架,我们可以使用观察数据来估计干预的效果

到目前为止,我们已经(在很大程度上)采用了一种经典的统计方法来计算因果关系。换句话说,我们将数据分成两个群体(如治疗组和对照组)并比较它们的*均值。然而,有一种更稳健的方式来思考问题。

因果关系大师朱迪亚·珀尔用他所谓的因果关系的结构方法来框定事物。这种方法的核心部分是所谓的 do 运算符。

我们首先在关于因果推理的文章中看到了 do-operator 。在那里,操作符被引入作为一个干预的数学表示,使我们能够计算因果效应。在这里,我们将更详细地探讨这个概念。

Do-Operator 的*均治疗效果

在本系列的第一篇中,我们将随机对照试验的*均治疗效果 (ATE)定义为两个治疗水*之间预期结果的差异。换句话说,我们根据治疗状态(例如是否服药)比较结果变量(例如是否头痛)的期望值。这是最广泛使用的量化因果关系的方法之一。

公式 1:随机对照试验的*均治疗效果[ 2 ]。图片作者。

或者,Pearl 公式中的ATE被定义为两个干预水* [ 1 之间预期结果的差异。这就是 do 运算符的用武之地。

公式 2:根据 do 运算符[ 1 ]的*均治疗效果。图片作者。

观察分布与干预分布

P(Y | X=x))和 P(Y | do(X=x)) 之间的关键区别在于,第一个具体说明了给定 X=x 的被动 观察时 Y 的概率,而后者表示给定 X 的干预时 Y 的概率。

这里,我将包含 do 算子的分布称为介入分布(例如 P(Y | do(X = 1));而不包含 do 算子的分布称为观察分布注:这类似于 上一篇博客 中对观察性研究和介入性研究的区分。

观察分布与干预分布的比较。图片作者。

公式 1 对公式 2

在随机对照试验(RCT)中,配方 1 和配方 2 是等效的。因为治疗任务是由实验者有意地(小心地)操纵的。然而,一旦我们将移到 RCT (或类似的东西)之外,公式 1 不再有效,但公式 2 有效。

这样,我们可以把公式 2 看成是公式 1 的推广。这个新的公式为我们提供了一个更清晰的因果关系图。它有助于我们从特定环境下的特定等式转移到一个更通用(更强大)的框架。在下一节中,我们将看到这一公式在实践中的威力。

可识别性

分三步连接观察和干预分布

虽然理论和抽象可能会有所帮助,但在某些时候,我们需要将它们与现实(即我们的数据)联系起来。在因果关系的背景下,这提出了可识别性的问题。换句话说, 能否从给定的数据中得出介入分布?1

正如我前面提到的,如果我们有来自介入性研究(例如 RCT)的数据,我们确实有介入性分布(这是我们精心测量的)。但是如果我们只有观测数据呢?**

幸运的是,这个问题已经被 Pearl 和他的同事解决了。该解决方案可以分解为 3 个步骤

步骤 1——写下一个因果模型

因果模型在之前关于因果关系的系列文章中介绍过。一个简单的因果模型可以用一个叫做有向无环图(DAG) 的东西来表示,其中通过点和箭头来描绘变量之间的因果联系,其中 A → B 表示 A 导致 B

我们在本系列的最后一篇文章中看到了类似下图的 DAG 示例。注意,我们没有(或不需要)连接的细节(即变量之间的函数关系),只有什么导致什么。

代表年龄、毕业学校和收入之间因果关系的 DAG 示例。图片作者。

步骤 2——评估马尔可夫条件

一旦我们有了 DAG,我们就可以评估保证可识别性的条件。这被称为马尔可夫条件,如果满足,因果关系是可以识别的。这意味着我们可以从观测数据中计算公式 2(不需要 RCT!).

马尔可夫条件有两部分一个,图一定是无环的,所有 Dag 都是如此(那就是“A”代表的意思)。和两个,所有噪声项共同独立。这第二点的意思是没有从 DAG 中排除的变量同时引起任何两个变量。下面给出了满足/违反这一要求的一些例子。

满足和违反马尔可夫条件的简单例子。图片作者。

步骤 3——将观察结果转化为干预措施**

如果我们确认我们的 DAG 是马尔可夫的(即满足马尔可夫条件),我们可以使用下面给出的截断因子分解公式通过观察值来表达任何介入分布。

截断因式分解公式[ 1 ]。图片作者。

为了隔离 LHS 分布中的治疗和结果变量,我们可以对协变量(即所有其他变量)求和。例如,在来自最后一篇博客的简单 DAG 中,过程如下。

通过截断因子分解公式根据观察数据表达介入分布 P(Y|do(x0))的简单示例。图片作者。

从上面的例子中我们可以看到,对于给定的 DAG,我们必须针对协变量 z 进行调整。这是我们在因果推断的帖子中(直观地)所做的。然而,这里我们从截断因式分解公式中推导出这个结果,而不是依靠直觉。

这三个步骤为我们提供了一个从观察数据中计算因果关系的系统方法。使我们能够做到这一点的关键是一个满足马尔可夫条件的因果模型。我们可以把这个过程更进一步,用它来处理不可测量的混杂因素的问题。

应对不可测量的混杂因素

数据收集的一个问题是变量有时很难甚至不可能测量。这就提出了不可测量的混杂因素的问题,即变量使我们对因果效应的估计产生偏差,而我们没有的数据。再次幸运的是,这个结构性的因果框架对这个问题有一个解决方案。

我们通过珀尔1引言中的一个例子来探讨不可测量混杂因素的问题。假设我们有下面的马尔可夫因果模型。

更复杂的马尔可夫因果模型来自[ 1 ]。图片作者。

然后,正如我们之前所做的,我们可以通过截断因式分解公式和协变量求和来写下介入分布 P(Y | do(X=x0))。下图给出了代数。

经由截断因子分解公式[ 1 ]根据观察数据表达介入分布 P(Y|do(x0))的更复杂的例子。图片作者。

但是现在假设 Z_2 很难(或者不可能)测量我们如何用我们的数据计算上面的估计值?这确实是可能的,结果如下。

应对不可测混杂因素 Z_2 [ 1 ]。图片作者。

虽然这最后一步看起来很神奇,但它来自下面的等式。

仅针对 X [ 1 ]的直接原因进行调整的介入分布的表达式。图片作者。

这里的关键点是我们只需要测量 X 的双亲来估计它的因果效应。使该表达式有效的是马尔可夫条件,该条件要求任何变量(当以其父变量为条件时)独立于其非后代变量。

换句话说,通过对 Z_1 和 Z_3 进行调节,我们阻断了 X 和 Z_2 之间的统计相关性。在本系列的下一篇博客 中,我们将探索截断因式分解公式的其他可能的简化。

*

如何使用倾向得分

回到本系列的上一篇文章,我们可以利用我们在这里学到的知识通过倾向得分来改进我们对因果效应的估计。为了生成倾向评分,我们采用一组受试者特征(即协变量),并使用它们通过例如逻辑回归[ 2 ]来预测治疗状态。然而,选择正确的协变量不是一个微不足道的步骤。

我们可以克服这个挑战的一个方法是在倾向得分模型中只包括 X 的父母。因此,如果我们花时间为我们的问题写下一个因果模型,我们就能以一种直接的方式挑选出协变量。例如,在上面的 DAG 中,我们将只在我们的倾向得分模型中包括 Z_1 和 Z_3,即使 Z_2 被测量。

替代协变量选择

虽然只需要解释 X 的直接原因是一个强大的洞察力,如果 X 的父母是不可测量的呢?

这促使人们寻找替代的协变量来表达我们的介入分布。本系列的下一篇文章将会这样做,并通过 DAGs 讨论因果关系。

资源

更多关于因果关系 : 因果关系概述 | 因果关系:简介 | 因果推断 | 因果发现

连接 : 我的网站 | 预定电话 | 邮件我

社交:YouTube|LinkedIn|Twitter

支持 : 给我买杯咖啡 ☕️ | 成为会员 ⭐️

https://shawhin.medium.com/membership

【1】朱迪亚·珀尔因果推理导论

[2] 减少观察性研究中混杂效应的倾向评分方法简介作者 Peter C. Austin*

因果推理:综述

原文:https://towardsdatascience.com/causal-inference-an-overview-736efdfe01c4

劳拉·奥克尔在 Unsplash 上的照片

找出相关性实际上意味着因果关系的时候

因果关系比相关性更能提供信息

首先,我们为什么要费心研究因果关系?相关性不足以进行预测吗?难道机器学习没有让因果关系的研究变得毫无意义吗?不完全是。

很多时候,我们对这种关系感兴趣:“如果我做 X ,那么 Y 就会发生”,这和“如果 X 发生,那么 Y 就会发生”是不一样的。区别在于,在第一种情况下,我们实际上是在强迫 X 发生,而在第二种情况下,X 是自发发生的。这可能看起来很无聊,但却很重要。当我们想研究第一种关系时,因果关系是最重要的。

例如,在市场营销中,这将帮助我们隔离特定活动对目标客户的影响,消除任何潜在的选择偏差。

随机对照试验是衡量因果关系的黄金标准

推断因果关系的最佳方法是通过随机对照试验 (RCTs)。在我们的营销活动示例中,这可以通过随机将我们的人口分成两组来实现:一组将接受活动(A 组),另一组不接受(B 组)。这或多或少是新药测试的方式。

在一个理想的世界中,我们将总是使用 RCT,并从这些研究中获得许多有价值的见解。实际上,这并不总是可能的,原因有很多:RCT 可能太昂贵,太难执行,花费太多时间,甚至不道德。

以吸烟为例。你认为运行 RCT 来测试它是否真的导致肺癌有多容易?要做到这一点,你必须让一群人吸烟很多年。尽管至少从 1912 年就已经观察到了吸烟和肺癌之间的相关性,但直到 20 世纪 60 年代才确立了因果关系。烟草业当时花了很大力气来否认这种关系,声称可能正好相反(肺癌会让你渴望吸烟)或者可能有某种遗传因素使人们更容易患肺癌和吸烟。

证明他们是错的特别困难,因为 RCT 是不可能的,所以科学家们不得不使用其他的变通方法,而且我们今天所知道的因果推理在当时并没有得到很好的研究。

这就是为什么我们经常进行观察性研究:它们通常更容易进行,尽管以后更难利用。因果推断是一个最*的研究领域,还有很多正在开发中,但如果我们愿意做出一些假设,已经有足够的技术允许我们从那些观察性研究中推断因果关系。

在我们研究这些技术之前,让我们先了解一些有用的基本概念和定义。

有用的概念:为因果推理奠定基础

有向无环图

DAG 是一组节点,由方向(箭头)链接,不形成循环

让我们看几个 Dag 的例子(以及一些不是 Dag 的图):

作者图片

上图都是 Dag 的例子,箭头表示因果关系。在图表 1 中,我们说吸烟导致肺癌。在图表 2 中,我们说肺癌导致吸烟。在图 3 中,我们说遗传导致了吸烟和肺癌

现在,让我们来看一些不是 Dag 的图形:

作者图片

图 4 不是 DAG,因为它不是指向的:我们无法判断是什么导致了什么。图 5 不是 DAG,因为它是循环的:3 个箭头在 3 个节点之间构成了一个循环的 T21。

这是因果推断需要掌握的最基本的概念,因为它将被用来直观地表示任何因果假设。

调解人

知道因果关系的存在只是成功的一半。真正重要的是确定为什么一件事会引起另一件事。

如果你能找出某种疾病是由某种东西引起的,那将使预防和找到治疗方法变得容易得多。

一个中介是一个变量,告诉我们为什么一个因素会导致一个特定的结果。

作者图片

上面的例子说明了这一点。不是说吸烟直接导致癌症,我们现在知道吸烟导致焦油在肺部积累,然后导致癌症。

混杂因素

混杂因素是既影响我们视为原因的变量,又影响我们视为结果的变量。

例如,图 3 中的基因会影响吸烟和肺癌。

控制混杂因素的一种方法是引入随机化,但正如我们前面看到的,随机化并不总是可行的。另一种方法是只比较各组中基因相似的人(吸烟者和非吸烟者)。

后门小路

后门路径是从可能的因果来源到结果的非因果路径。在下图中,假设是遗传导致了吸烟和肺癌。

作者图片

如果我们对吸烟对肺癌的因果影响感兴趣,我们只对它们之间的因果路径感兴趣。路径“吸烟”←“遗传”→“肺癌”是一条后门路径,在这个意义上,除了它们的因果关系之外,它还会在吸烟和肺癌之间产生相关性。通过控制遗传因素(我们将在后面看到如何控制),这条路径可以被因果推理“阻断”。

对撞机

如果我们对遗传和吸烟之间的因果关系感兴趣,图 7 中的肺癌就是一个对撞机的例子:它是两者的原因,所以我们有两个来自两者的箭头。在这种情况下,该路径已经被“阻断”,因此,如果我们控制了肺癌,我们将“疏通”它。这就是为什么理解因果模型很重要,而不是仅仅控制每一个可能的变量。

向前切进

可以想象,前门路径是两个变量之间的因果路径。在图 7 中,这将是吸烟和肺癌之间的箭头。在图 6 中,是它们之间的完整路径,包括“Tar”。

工具变量

一个影响治疗的变量,但不通过治疗以外的任何其他方式影响结果。它可用于“两阶段最小二乘法”(见下文)。

总是接受者、顺从者、反抗者和从不接受者

如果我们将工具变量视为治疗的鼓励或阻碍,那么我们可以定义 4 类人:总是接受者将总是接受治疗,不管鼓励与否;编者会一直遵从鼓励(每次我们鼓励他们接受治疗,他们就接受);挑衅者总是做与我们告诉他们的相反的事情;从不接受治疗的人永远不会接受治疗,不管我们是否鼓励他们。

RCT 的替代品

d-分离

到目前为止,我们一直在看简单的因果模型,只有几个变量。然而在现实中,事情通常会更复杂。将有多条路径连接 2 个变量,经过多个变量。以下面的例子为例(这个例子仍然很简单):

作者图片

我们看到,生活在大城市会让你更容易吸烟,但也会让你暴露在空气污染中,这可能会导致肺癌。由于我们对吸烟对肺癌的因果关系感兴趣,“生活在大城市”在这里是一个混杂因素。在这种情况下我们能做什么?我们可以“阻断”吸烟和肺癌之间通过“生活在大城市”的后门路径。

如果我们控制路径上的非碰撞器或者不控制碰撞器,则路径被阻塞。正如我们之前看到的,如果我们控制一个处于后门路径的对撞机,我们最终会“解锁”这条路径。

相称的

从观察数据中,我们通过找到最相似的匹配,将接受治疗的观察结果与未接受治疗的观察结果进行匹配。这将产生两组治疗组对照组。根据您的匹配方式,这些组可以有不同的大小。

您可以评估匹配前后两组之间每个协变量的“标准化均值差异”(SMD),以确保它们是相似的。你可以通过计算每组的每个独立变量的*均值,然后计算两组之间的差值,然后用该差值除以变量的标准偏差。这会给你显示两组有多大不同的比例数字。如果它们都低于 0.1 或 0.2,则匹配充分*衡。

一种匹配方式是使用倾向得分匹配:

  1. 计算倾向得分(给定协变量,使用逻辑回归等模型,计算治疗的概率)
  2. 通过一起绘制两组的两个评分分布,检查治疗组和未治疗组之间的倾向评分重叠。理想情况下,应该有很多重叠
  3. 基于该分数匹配相似的观察结果。我们假设有相似治疗倾向的两个观察结果是相似的
  4. 使用 SMD 检查协变量不*衡

一旦你有了两个匹配的组,你就可以对它们进行测试,例如使用 McNemar 测试。

几个能做到这一点的 R 包有: matchit (它做贪婪匹配,比较快,但不是最优的) optmatchrc balance (最优,但慢)。

治疗加权的逆概率(IPTW)

给定我们想要控制的一组变量,通过获得他们实际获得的治疗(治疗或控制)的逆概率对个体进行加权。如何做:

  1. 估计倾向得分(如上所示)
  2. 创建权重:治疗受试者的 1 /倾向得分,未治疗受试者的1/(1-倾向得分)
  3. 指定感兴趣的边际结构模型(MSM )(人口水*模型,独立于协变量 X)
  4. 使用软件拟合广义线性模型
  5. 使用渐*(三明治)方差估计器(或 bootstrapping)来说明伪总体可能大于样本量的事实(大多数软件会自动这样做)

两阶段最小二乘法

  1. 在 Z(工具变量)和协变量 X 上回归 A(处理)
  2. 在β(步骤 1 的结果)上回归 Y(结果)和协变量 X

在步骤 2 中获得的β的回归系数是估计的因果效应。

ivreg ,来自 ivpack (在 R 中),在一个函数中完成这两个步骤。

敏感性分析可以在这里针对两个假设进行:

  • 排除限制:如果 Z 直接影响 Y 一个量 p ,我的结论会改变吗?变化 p
  • 单调性:如果违抗者的比例是 i ,我的结论会改变吗?

有关如何进行这些敏感性分析的更多细节,您可以查看生物统计学中的本教程。

更进一步

如果你真的对因果推断很认真,我推荐以下资源:

朱迪亚·珀尔(Judea Pearl)和达纳·麦肯齐(Dana Mackenzie)写的《为什么》这本书可能是一个很好的开始,它会给你一个关于 Dag 以及如何操纵它们进行因果推理的强烈直觉。

https://www.amazon.fr/Book-Why-Science-Cause-Effect/dp/046509760X

如果你对阅读这本书没有把握,我根据它写了一篇关于媒介的文章,那会让你对它的内容有个概念:

https://medium.com/@arthurmello_/the-science-of-why-a-brief-introduction-ed92060e6f0d

读完那本书后,还有一本更短的书,也是朱迪亚·珀尔写的,会给出更多的数学细节:

https://www.amazon.fr/Causality-Primer-Judea-Pearl/dp/1119186846/ref=pd_lpo_3?pd_rd_i=1119186846&psc=1

最后,我推荐 Coursera 上的这个 MOOC:

https://www.coursera.org/learn/crash-course-in-causality

不会太长,会让你很好的把理论和实践结合起来(用 R)。如果我要从这份清单中只推荐一样东西给你,由于它的实际内容,它可能会是这门课程。

如果你喜欢这篇文章,你可能也会喜欢这些:

如果你想进一步讨论,请随时通过 LinkedIn联系我,这将是我的荣幸(老实说)。

因果推断:计量经济模型与 A/B 检验

原文:https://towardsdatascience.com/causal-inference-econometric-models-vs-a-b-testing-190781fe82c5

观察研究、实验研究、回归模型、工具变量、差异中的差异模型、参数检验和非参数检验

班农·莫里西在 Unsplash 拍摄的照片

作为数据科学从业者,我们经常被问到“X 驱动 Y 吗?“Y 是我们在意的结果。x 可能是一项新功能、产品或药物。例如,一个网站所有者想问一个新的网页设计是否会带来更高的点击率或销售额。临床研究人员会问一种新药是否会促进健康。

不幸的是,单单是 X 和 Y 之间的* 而不是 *足以帮助我们建立起的因果关系。这里的复杂因素是一组影响 X 和 y 的其他特征,称为混杂变量,例如,游客地理位置、性别、年龄和兴趣等因素会影响新特征的使用和销售收入的结果。因此,我们需要在控制这些混淆变量的同时,隔离新网页设计(X)对销售收入(Y)的影响。**

第一部分:观察研究和实验研究

有两大类设计用于调查研究中两个或更多变量之间的关系:观察实验

  • 观察性研究中,我们观察并收集研究中相关的实际数据(如 X 和 Y ),而不会随意对某个群体进行任何处理或限制。
  • 实验研究中,我们随机对一组进行治疗,而另一组不接受治疗,这样我们可以调查治疗和结果变量之间的因果关系。随机化设计和干预使实验研究不同于观察研究

问题:这项研究是观察性的还是实验性的?

研究 1:一项研究将学生随机分为两组:

  • 一组被要求遵循严格的锻炼时间表。
  • 一组被禁止做任何运动。

研究人员观察了哪一组在学期结束时倾向于获得更高的 GPA。

研究 2:另一项研究随机抽取学生样本,调查他们的锻炼习惯。每个人被分为轻度、中度或重度锻炼者。研究人员观察了哪些小组倾向于在学期结束时获得更高的 GPA。

研究 1 是一项实验研究,而研究 2 是一项观察研究。因为研究 1 需要研究人员的干预,而研究 2 不需要。

观察研究与实验研究的区别:

  • ****实验研究通常更昂贵,因为它需要更多的资源来建立研究实验,而观察研究可以通过调查和数据收集来完成。研究人员不需要任何干预。
  • ****实验研究通常比观察研究更短,因为研究人员的干预使得为研究收集相关数据更加有效,而观察研究可能需要几年才能完成数据收集。
  • 实验研究提供的证据被认为比观察研究更有力。观察性研究中变量之间的关系不是必然的因果关系,而在实验性研究中,随机化将确保其他协变量(如年龄、性别、地理位置、消费习惯)在治疗组和对照组中均匀分布,并且我们的样本代表我们的总体,以便我们可以更精确地调查治疗对我们关心的结果的因果影响。

听起来实验研究比观察研究更有优势。然而,在一些研究中,实验研究并不是一个可行的选择。例如,

  • ****您根据历史数据进行研究,这些数据在过去没有随机分配治疗。
  • ****治疗只能观察,不能强加。一些治疗效果(例如,性别、家庭收入水*、种族等)不能由研究人员操纵。
  • 将治疗强加给一个群体被认为是不道德的。例如,我们想调查产品价格的变化将如何影响其销售收入。如果我们进行一项实验研究,在相同的地点和相同的时间范围内,向一组顾客收取比另一组更高的价格,这将是不道德的,也是一场公关灾难。在另一个例子中,我们想调查获得大学学位是否会影响收入。还是那句话,不能强制某些人“有大学学历”。这样做不仅是不可能的,也是不道德的。

第二部分:计量经济学模型的观察研究

****回归模型

计量经济学模型(也称为受控回归)是一种流行的观察性研究方法,用于估计预测变量(如治疗 X 和其他协变量)的变化与反应变量(Y)的变化之间的关系。重要的是,使用受控回归模型,我们可以隔离一个变量(如治疗 X)的影响,同时保持所有其他预测变量不变。

受控回归不仅能够控制协变量(影响反应变量),还能够控制混杂变量,影响治疗 X 和反应变量 y

例如,一个电子商务网站想要调查他们网站上的虚拟试衣间功能是否会增加他们的销售收入。如果我们只是回归销售收入的网站功能,我们可以很容易地看到虚拟试衣间功能和销售收入之间的正相关关系。如果我们就此打住,特征的影响会有偏差(也称为省略变量偏差),因为一个重要的混杂变量,年龄,没有包括在模型中。基于额外的数据分析,我们可以发现,年轻顾客倾向于更频繁地使用虚拟试衣间,年轻顾客倾向于在这个电子商务网站上花费更多。如果我们在受控回归中不包括年龄变量,该特征的效果可能会有偏差

作者图片

**Background: Age is positively related to both Virtual_Fitting_Room and Sale_Revenue.Controlled Model 1: β1 here is biased.
Sale_Revenue = β1 * Virtual_Fitting_Room + CControlled Model 2: β1 here is estimated more precisely and R-square is higher.
Sale_Revenue = β1 * Virtual_Fitting_Room + β2 * Age + C**

检查受控模型中是否有遗漏变量偏差的提示

  • 如果在回归模型中增加一个预测变量,将有意义地增加 R *方,治疗效果也会发生显著变化。很可能之前的模型存在遗漏变量偏差。
  • 如果在模型中包括额外的变量不会对治疗效果产生有意义的影响,那么我们更有信心估计的治疗效果是治疗和反应变量之间的真正因果关系。

使用工具变量解决遗漏变量偏差

观察性研究的另一个问题是混淆变量在概念上存在,但无法测量或观察,因此治疗效果会受到遗漏变量偏倚的影响。在模型中包括工具变量** (IV)是解决这个问题的一种流行方法。**

让我们定义这个工具变量 Z:

  • z 与模型中的任何其他协变量(包括误差项)都不相关
  • z 与处理 X 有意义且强相关,因此通过 X 间接影响 Y

在实践中,工具变量可以分两步实现:

  • 第一步:我们回归 x 上的工具变量。请记住,我们需要在 IV 和 x 之间有很强的相关性。否则,我们可能仍然对治疗效果有偏见
  • 第二步:我们根据第一步预测的 X 和其他协变量回归 Y。所以我们更准确地估计治疗效果。

工具变量的应用

****案例研究:一个社交媒体网站想要调查在同一个社交媒体网站上有更多的朋友是否会使用户更有可能回到该网站。

首先,一项实验研究是不可能的,因为我们不能随机分配人们比其他人拥有更多的朋友。其次,包括所有混杂变量是不可行的。使用工具变量似乎是一条更简单的途径。一个社交媒体网站应该有一个现有的策略来邀请更多的朋友来这个网站。例如,使用用户的朋友联系人向朋友发送邀请的功能。

作者图片

**Background: This feature of Send_Invitations_to_friends is related to number_of_friends, but not related to any other covariates for Return.Step 1 of IV Model: β1 should suggest strong positive correlation between Send_Invitations_to_friends and Number_of_friends
Number_of_friends = β1 * Send_Invitations_to_friends + CStep 2 of IV Model: β2 can estimate effect of number of friends on Return more precisely.
Return_Flag = β2 * Predicted_Number_of_friends + C**

差异中的差异模型

当一个工具变量不可行时,我们需要一个替代方法来控制未观察到的混杂变量的影响。差异中的差异** (DiD)模式可能是一个可行的选择。它比较了治疗组和对照组在治疗前和治疗期间的结果。DiD 模型所需的关键假设是:**

  • 如果不进行治疗,治疗组和对照组的结果变量将遵循*行趋势
  • 任何协变量(包括省略的变量)都会以同样的方式影响治疗组和对照组的结果变量。

作者图片

差异中的差异模型的应用

****案例分析:一家零售店想要调查一种产品的涨价是否会带来更多的销售收入。

首先,实验研究是不可能的,因为在同一地点随机对不同价格的产品收费被认为是不道德的。其次,构建混杂变量和工具变量本身就是一项艰巨的任务。在这种情况下,差异中的差异模型是更好的选择。

我们可以从不同的城市挑选两家零售店,这两个城市在人口、收入水*和产品需求方面也具有可比性(*行趋势假设)。

城市 1 的商店(治疗组)在治疗期间向顾客收取的价格(治疗效果)高于对照期间,而城市 2 的商店(对照组)在两个期间向顾客收取的价格相同。

我们可以使用 DiD 模型比较两个城市在价格上涨后的收入变化。

**Background: Trends of sale revenue from both cities are **parallel** in both the pre-treatment periodandtreatment period (if the price change is not imposed.)**DiD Model**: "Treatment_period" is an indicator of the timing that a price increase is imposed (treatment period: 1; Pre-treatment period: 0). "Treatment_Group" is an indicator of the treatment and control groups (City_1: 1; City_2: 0). The effect of the Interaction term between "Treatment_period" and "Treatment_group"(β3) estimates the pure effect of price increase on sale revenue.Sale_Revenue = β1 * Treatment_period + β2 * Treatment_group + β3 * Treatment_period * Treatment_Group + C**
  • β1:估计治疗期间除我们的治疗以外的因素对反应变量的影响。
  • β2:估计治疗组和对照组之间的*均差异
  • β3:估计对反应变量的纯治疗效果。

作者图片

第三部分:A/B 测试的实验研究

在这一节,我们来谈谈因果推断的另一个有力工具, A/B 测试

A/B 测试(又名随机对照试验)也许是调查因果关系最准确的工具。通过不断确定转化率和参与度指标方面的新目标,并测试新功能,网站可以提高绩效,应用可以吸引和留住更多用户。因此,A/B 测试*年来在技术行业中普遍使用。

A/B 检验通常通过一些必要的步骤来实现:形成假设样本容量计算、随机化设计、后验分析。****

假设

形成假设是每个 A/B 测试的第一步。假设描述你想要调查的因果关系的陈述。让我们举一个假设的例子。

**Null hypothesis (H0): ABC e-commerce site visitors who receive email coupons will **NOT** have higher purchase conversion rate compared to visitors who don’t receive email coupons. Alternative hypothesis (H1): ABC e-commerce site visitors who receive email coupons will have higher purchase conversion rate compared to visitors who don’t receive email coupons.**

每个假设都由关键部分组成:人群治疗评价指标无效&替代假设****

人口 : 我们需要定义哪些受试者有资格参加实验(例如,所有用户,或来自某个位置的用户),以及如何确定单个受试者(又名转移单位)。在上面的例子中,受欢迎的是来自 ABC 电子商务网站的所有访问者,分流的单位是用户 ID。

处理(干预):一个处理可以是一个新特性或者新设计。在上面的例子中,处理将是接收电子邮件优惠券。请记住,治疗通常只能进行一次干预。我们不能在一个组中强加多种变化。例如,如果我们为实验同时发送电子邮件优惠券和邮件优惠券,我们无法区分两种干预的效果。

治疗和对照组:通过治疗施加的任何受试者将属于治疗组。任何未受治疗影响的受试者都属于对照组。

评估指标(结果变量):评估指标是我们关心的结果,将被调查。在上面的例子中,评估指标是购买转化率,它被定义为在实验中进行购买的访问者数量与总访问者数量之间的比率。

有不同类型的评估指标。举个例子,

  • 计数:参与度指标,如日活跃用户(DAU)、周活跃用户(WAU)、月活跃用户(毛)和用户粘性(DAU/毛)是常见的评估指标
  • 分布(例如,*均值、百分位数):评估度量可以是分布。例如,网站的*均会话时间或转换前的*均点击次数。
  • 概率和比率:评估指标也可以是一个比率。例如,转化率,其由在实验中采取期望的行动(例如,点击底部,进行购买)的受试者的数量与受试者总数之比来定义。留存率,衡量用户在一段时间内返回你的网站或应用的百分比。通过跟踪转化率和保留率,您可以监控网站的表现,并确定需要改进的地方。

无效和替代假设:无效假设表示治疗组和对照组之间的结果变量没有差异。换句话说,治疗不会影响结果。另一个假设是两组之间的结果有统计学差异。

样本量计算

下一步是计算实验的样本量。在计算之前,我们需要确定几件事。

  • 效果大小:这是治疗组和对照组之间结果变量(如转化率的变化)的差异。请记住,有了足够的样本量,即使是实验中微小的变化也会被发现具有统计学意义。所以你需要考虑这些变化的商业影响以及的实际重要性。你需要问“为了值得发动干预,对结果的最小影响是什么?”同时考虑开发和机会成本。另外效果尺寸越小,需要的数据就越多,测试持续的时间就越长
  • 统计显著性水*和功效:通常显著性水*设置为 0.05,功效设置为 0.8。显著性水*(又称为 I 型误差)是当不存在影响时错误检测影响的可接受可能性。因此显著性水*越小,测试越好,需要的数据越多功效是指当一个效应存在时,测试检测到该效应的可能性。因此功率越高,测试越好,需要的数据也越多
  • 标准差:这是结果变量的方差。当很难获得时,我们可以依靠历史数据或领域专家的知识来估计。标准差越高,我们需要的数据就越多
  • 样本量计算:一旦你有了上面提到的信息,下面的公式可以计算样本量。Z 值是关于显著性水*和功效值的标准分数。σ值是标准差。c- t 是效应大小。

作者图片

随机选择

一旦我们有了一个假设和样本量,我们就可以将受试者随机分配到治疗组和对照组。随机化是无偏** A/B 测试成功的关键。它需要满足以下要求:**

  • 需要确保测试中的样本是总体的代表。以便从样本中得出的结论可以应用于总体。
  • 需要确保协变量在治疗组和对照组之间均匀分布。任何可能影响结果变量的因素(如性别、收入水*、位置、设备类型)都需要均匀分布。这样我们就可以在保持其他协变量可比性的同时,分离出对治疗的影响。

测试后分析

在我们分析治疗如何影响实验中的结果变量之前。我们需要在实验中进行健全性检查。我们用于健全性检查的度量被称为不变度量(例如,cookies 的数量),它不应该受到实验的影响。因此,对照组和治疗组之间的不变指标不应该有变化。否则,实验装置就有缺陷。

一旦通过健全性检查,我们就可以分析我们关心的实际数据。我们可以使用许多方法来研究对照组和治疗组之间的结果变量是否不同。

****参数检验:当结果变量相对正态分布时,参数检验工作良好。以下是一些流行的参数测试:

  • 学生的 t 检验:我们假设控制组和治疗组之间结果变量的方差相同。
  • 韦尔奇的 t 检验:当样本大小或方差不可比时,韦尔奇的 t 检验会优于学生的 t 检验
  • 方差分析检验:有时有多个治疗组。在我们运行多个 t 检验之前,我们可以首先运行 ANOVA 检验,它使用 f 检验来确定三个或更多组的均值是否不同。如果 f 检验的 p 值很小,我们知道至少有一组与其他组不同。然后我们可以花时间进行成对 t 检验,找出哪一组是不同的。请记住,当运行多重测试时,我们需要使用 Bonferroni 校正错误发现率 (FDR)来校正 P 值

****非参数检验:非参数检验不对底层数据的分布做出假设。当连续结果变量不是正态分布或者有两个或更多分类结果时,它们是可行的选择。以下是一些流行的非参数测试:

  • 卡方检验:这是一个独立性检验,允许您检验治疗和结果变量之间是否有统计学上的显著关联。它可以处理具有两个或更多结果值的分类数据,而 t 检验只能处理具有两个结果值的分类数据。
  • 费雪精确检验:卡方检验只有在样本量相对较大(即 n>1000)时才可靠。如果没有达到这个阈值,可以使用费希尔的精确测试来代替。
  • 曼惠特尼 U 检验(Wilcoxon 秩和检验):这种检验将使用等级而不是实际值。当比较非正态分布的连续变量或样本量很小时,Wilcoxon 秩和检验将是一个很好的选择。

A/B 测试中的问题及解决方案

  • ****过早停止 A/B 测试:在我们计算样本量之后,我们将通过除以*均日流量来知道运行测试的天数。如果数量少于一周,我们应该继续运行测试至少 2 周。如果可能,1-2 个商业周期会更好。人们每天都有不同的行为(例如工作日与周末),并受到外部事件的影响(例如节假日、纳税季、夏季与冬季)。通过扩展测试,我们可以得到更稳健的结果。
  • 网络效应**:在社交媒体*台中,用户的行为很可能受到其社交圈中的人的影响,因此,关于用户的独立性假设不成立。当将每个用户随机分配到对照组和治疗组时,测试的治疗效果通常被低估,因为治疗效果可能会通过治疗组的社交圈溢出到对照组。为了解决这个问题,我们可以使用集群随机化,将用户放在同一个社交圈的同一个组中。
  • 新奇效应&首因效应:人们会对产品的新变化/新特点做出不同的反应。有些人可能会对任何新的变化感到兴奋,并愿意为了尝试新事物而进行实验。这种行为被称为新奇效应。另一方面,有些人可能会抵制对产品的任何改变。这被称为首因效应变化厌恶。如果你观察到一个或大或小的初始效应,这可能是由于新奇感或首因效应。为了解决这个问题,我们可以延长测试的持续时间,因为这些影响最终会消失。或者,我们可以对新用户进行 A/B 测试,因为新用户会有一个全新的视角,不应该受到这些影响。
  • ****矛盾的结果:有时,我们会看到来自多个评估指标的矛盾结果(例如,转化率上升,但留存率下降)。为了解决这个问题,我们可以提出一个 OEC ( 总体评估标准),它同时考虑了短期和长期目标,以及不同指标之间的权衡。然而,你应该能够量化积极和消极影响,并确保消极影响是可以接受的。

最终注释

如果你对线性回归因果推断感兴趣,这里有一些相关的帖子可以浏览。

感谢您的阅读!!!

如果你喜欢这篇文章,并且想请我喝杯咖啡,点击这里

您可以注册一个 会员 来解锁我的文章的全部访问权限,并且可以无限制地访问介质上的所有内容。如果你想在我发表新文章时收到电子邮件通知,请订阅。

使用综合控制的因果推理

原文:https://towardsdatascience.com/causal-inference-using-synthetic-control-4377b457c6bb

探索一种最新的准实验技术

埃文·丹尼斯在 Unsplash 上的照片

人们普遍认为 A/B 测试是因果推断的黄金标准。也称为随机对照试验(RCT),这些试验包括将受试者随机分为治疗组和对照组。这确保了单位之间的任何差异是由于所应用的处理。A/B 测试已被企业广泛采用,以测试新产品、功能和营销策略。这有助于他们捕捉客户反应、产品问题等。在产品或战略周期的早期。然而,在许多情况下,将受试者随机分为治疗组和对照组可能不是最佳解决方案。社交媒体测试就是一个例子,其中网络效应可能导致测试和控制之间的污染。同样,在某些情况下,它可能会引起伦理问题(例如,在医学试验中),或者可能过于昂贵,甚至由于技术限制而变得不可行。正是在这些情况下,我们使用准实验技术,如差异分析中的(DID)、回归等。本文重点研究的综合控制方法就是这样一种技术。这篇文章探讨了:

1)综合控制方法的细节,

2)其优点和缺点,以及

3)技术的数据要求

综合控制方法(SCM):

什么是合成控制法?

合成控制方法最初是在 Abadie 和 Gardeazabal (2003)中提出的,目的是估计综合干预对某些感兴趣的综合结果的影响[1]。在这里,综合干预是指在综合水*上实施并影响少数大单位(如城市、地区或国家)的干预。它基于这样的思想,当在集合实体级别进行观察时,未受影响单元的组合可以提供比任何单个未受影响单元更合适的比较。简而言之,它将治疗组与对照组的加权组合进行比较。与传统的准实验技术相比,SCM 有两大优势:

1)它可以解释混杂因素随时间变化的影响,而 DID 的*行趋势假设意味着,如果没有干预,治疗组和对照组的结果将随时间推移遵循*行轨迹[1]。

2)这种方法使用数据驱动的程序将比较单位的选择正式化[2]。

型号详情:

让我们假设有 J 个单位,j=1 为处理单位,j=2,…,j=J 为对照单位。设 y 为结果变量,Y_(1t)^N 为在没有干预的情况下在时间 t 观察到的治疗单位的结果变量的值。设 T_0 为干预时间,Y_(1t)为干预后的结果变量的值,Y_(jt)为控制单位 j 在时间 t 的结果变量的值。

综合控制方程;作者图片

如果是干预对治疗的影响,那么

冲击方程;作者图片

这里,Y_(1t)可以通过观察干预后的 y 来获得,而 Y_(1t)^N 可以从等式(1)获得。(1).问题仍然是:我们如何获得上述等式的权重?Abadie、Diamond 和 Hainmueller (2010 年)建议以类似于以下的方式计算权重

综合控制的权重;作者图片

这里 W 是权重 w_j 的(J-1)x1 矩阵,X_(t,pre)是暴露区域的干预前特征的向量,X_(c,pre)是对照的相同干预前特征的向量。

干预前特征,也称为协变量,可以是适当代表治疗的任何变量。例如,在 Abadie、Diamond 和 Hainmueller (2010)中,在估计 99 号提案对加州的影响时,使用的协变量是干预前时期香烟的*均零售价格、干预前时期人均国家个人收入(记录)的*均值、干预前时期 15-24 岁人口的百分比以及干预前时期人均啤酒消费量的*均值。这些变量因三年的滞后吸烟消费而增加(这也是结果变量)。可以使用任意年的滞后数据来模拟处理单元。4

计算模型权重的公式虽然与线性回归非常相似,但也有细微的差别。该模型使用以下约束条件,这使其不同于经典的线性回归模型:

按作者排列的约束图像

最后两个约束保护该方法不被外推。因为综合控制是可用控制单元的加权*均,所以这种方法清楚地表明:(1)每个控制单元对感兴趣的反事实的相对贡献;以及(2)就干预前结果和干预后结果的其他预测因素而言,受感兴趣的事件或干预影响的单元与合成对照之间的相似性(或缺乏相似性)。相对于传统的回归方法,透明性和防止外推是该方法的两个吸引人的特征4

实现示例:

在这个练习中,我使用了公开可用的数据,其细节在[6]中描述。文章中的代码理解综合控制方法已用于本例。

在本例中,我们将尝试估计99 号提案对州一级年人均卷烟消费的影响,该消费在我们的数据集中以人均卷烟销售额来衡量。因此,在这个例子中,我们感兴趣的结果变量是“年人均卷烟销售量”。我们示例的样本期从 1970 年开始,到 2000 年结束。加州在 1989 年提出了 99 号提案。让我们先来看看这个方法的上下文需求[1]:

  1. 用这种方法很难测量高波动性的小影响。
  2. 对照组的可用性,即并非所有单位都采用类似于治疗组的干预措施。在这个例子中,在我们的研究期间引入正式的全州烟草控制计划或增加卷烟税超过 50 美分的州被排除在外
  3. 对控制单元没有溢出效应,即实施治疗干预不会影响控制单元中感兴趣的结果变量。这个例子假设在处理单元和控制单元之间没有溢出效应。
  4. 干预前,综合控制和受影响单元的特征差异很小,即

作者图片

鉴于我们已经考虑了上下文需求,让我们来看看数据:

作者的数据图像

正如我们在上面的图像中看到的,我们的数据有三列:州、年和卷烟销售,这是我们感兴趣的结果变量,也称为“年人均卷烟销售”。因为我们将使用滞后数据作为我们练习的协变量;我们需要将其转换为面板数据,即每行代表年份,每列代表州。

作者透视数据图像

接下来是选择治疗州,在我们的例子中是加利福尼亚,治疗年是 1989 年,这是 99 号提案在加利福尼亚推出的第一年。在为我们的研究构建一个综合的加州之前,让我们比较一下加州的香烟销售额与本研究中其他控制州的香烟销售额的*均值。

作者图片

从上面的数字来看,1989 年以后,加州的 cig 销量确实比其他控制州下降得更快。下一步是建造人造加州。我们将首先使用以下约束条件定义我们的回归函数:

作者图片

预测合成加州的功能:

现在让我们构建我们的合成加州,看看控制状态的权重:

按作者列出的合成加州的系数/权重

从这个练习中获得的系数/权重几乎与4中提到的相似。细微的差异是由于4和本例中使用的协变量不同。现在是时候将合成的加利福尼亚与观察到的加利福尼亚进行比较了。

作者观察到的与合成的加州图像

看上面的情节,很像 99 号命题对 cig 的作用。销售是负面的,但是让我们想象一下观察到的和合成的加利福尼亚之间的区别:

作者合成的和观察到的加利福尼亚的黑白差异

差异图能够显示合成的和观察到的加利福尼亚之间的负差异,特别是在 1988 年之后,这是我们的治疗年。虽然我们现在可以观察到 Propsition 99 对加州 cig 销售的负面影响,但仍然存在的重要问题是我们如何确定这种影响在统计上是显著的。这就把我们带到了文章的最后一部分,即推理。

推论:

现在,我们已经构建了我们的合成加州,并确定了合成加州和观察加州之间的差异,我们如何估计我们的研究的统计意义?简而言之,我们如何确定加州观测到的影响不是偶然发生的。我们将使用排列测试,在【4】中也被描述为安慰剂研究。我们将把综合控制方法应用于在我们研究期间没有实施 99 号提案的州。这个想法是,通过将合成控制应用到其他州,如果我们没有观察到像加利福尼亚一样大的影响,那么 99 号提案对加利福尼亚的影响是显著的。

  1. 为所有状态创建综合控制,并绘制综合状态和观察状态之间的差异:

作者提供的安慰剂研究图片

我们可以在上图中观察到,对于某些状态,我们没有得到很好的拟合。在4中,建议我们去掉 MSE > 2*MSE 的处理态。

2.让我们去除治疗状态的 MSE > 2*MSE 的状态,看看综合控制和状态之间的估计差异。

作者提供的安慰剂研究图片

在排除了极端的州之后,看起来 99 号提案对加州的影响很小。在4中,MSE Pre 和 MSE Post 之间的比率用于排列测试。

3.让我们计算比率并估计测试 p 值:

作者的 p 值图像

我们测试的 p 值为 0.0256,即如果在该数据中随机分配干预,获得与加利福尼亚一样大的 MSE post/ MSE pre 的概率约为 0.026。

参考文献:

[1]阿巴迪,A. (2021)。使用综合控制:可行性、数据要求和方法方面。经济文献杂志59 (2),第 391–425 页。

[2]a .阿巴迪、a .戴蒙德和 j .海恩米勒(2015 年)。比较政治学与综合控制方法。美国政治科学杂志59 (2),495–510。

[3]a .阿巴迪和 j .加德扎巴尔(2003 年)。冲突的经济成本:巴斯克地区的案例研究。美国经济评论93 (1),113–132 页。

4阿巴迪、戴蒙德和海恩米勒律师事务所(2010 年)。比较案例研究的综合控制方法:评估加州烟草控制计划的效果。美国统计协会杂志105 (490),493–505。

5n . Doudchenko 和 g . w . Imbens(2016 年)。*衡、回归、差异中的差异和综合控制方法:一种综合(编号 w22791)。美国国家经济研究局。

[6]数据来源— 人均卷烟消费量(包)。资料来源:Orzechowski 和 Walker (2005)获取&使用信息——公开

连续治疗的因果推断

原文:https://towardsdatascience.com/causal-inference-with-continuous-treatments-5ff691869a65

非分类治疗的广义逆概率权重

从分类变量到连续变量(作者)。

因果推断,从非随机观察数据中估计因果效应的科学,通常使用二元处理来表示;我们要么治疗,要么不治疗;我们给药物 A 或药物 b,这是有原因的,因为因果关系已经很复杂了。然而,并不是所有的干预都是二元的(或离散的)。

有时候,我们关心的干预是连续的。例如,“服用一种药物”是相当模糊的——药物有活性成分,这些活性成分可以有不同的剂量。太少,药物可能看起来无效,太多,药物可能有害。因此,我们可能对同一种药物不同剂量的效果感兴趣。这通常被称为剂量反应模型。

持续的曝光在我们周围随处可见。从药物剂量到每天吸烟的数量或空气污染水*,从跳过广告前你看广告的时间到时事通讯上“退订”按钮的红色,从中央银行提高利率到彩票中奖金额。我们不能仅仅因为入门书没有涵盖其他内容,就把自己局限于研究二元曝光。

在这篇文章中,我将介绍连续治疗的广义逆概率加权。我将展示不同的估计方法,并讨论所需的假设及其局限性。我假设你熟悉因果推理和二元处理的 IPW,但是如果你不熟悉——我会在这个 IPW 解释器中介绍你。

从二元治疗到连续治疗

回想一下,在二元治疗设置中,估计因果关系的一种常见方法是使用逆概率加权(有时称为逆倾向加权,但我将只使用 IPW)。给定个体 l ,处理赋值 aₗ ,特征 xₗ ,其逆倾向权重定义为:wₗ=1/pr[a=aₗ|x=xₗ】。即,在给定他们的特征的情况下,分配给他们的治疗的 l 的逆概率。

然而,当治疗(或任何这方面的随机变量)是连续的,那么概率质量的概念就失效了,我们需要用概率 密度 来说话。这是因为单个点的概率,比如说 aₗ ,基本上是 0,尽管它可能仍然具有与之相关的密度,因为密度被定义为累积概率函数的导数。这是一个基本的理论差异,但我们可以在一个小的符号变化中捕捉到它,我们将使用ƒ(aₗ|xₗ而不是pr[a=aₗ|x=xₗ

用连续的高斯分布逐步逼*离散的二项式分布(图片由作者提供)

系统模型化

回想一下,估计 IPW 的治疗效果包括两个主要步骤。首先,对处理进行建模并获得 IP 权重。其次,用这些权重来模拟结果。在二元情况下,一旦我们有了权重,估计潜在结果的最简单方法就是简单地取治疗和未治疗的加权*均值(通常称为 Horvitz-Thompson 估计量)。然而,一个等效的方法是使用一个简单的单变量回归:根据 IP 权重加权的治疗(和截距)回归结果。然后,*均治疗效果简单地由对应于治疗变量的系数来定义。在流行病学文献中,这通常被称为边缘结构模型。

请注意,在连续治疗的情况下,第一个选项不适用。通常会有许多独特的处理值,对于所有的处理值,很少会有足够的样本具有完全相同的连续处理值。宁滨他们会解决它,但我们在这里进行连续处理建模。因此,我们将需要使用后一种选择,并在结果和治疗之间创建一个额外的(参数)模型。这将是我们的剂量反应函数。

让我们更详细地检查这两个步骤。

步骤 1:模拟治疗

对于分类治疗,我们需要对接受治疗的概率进行建模。我们可以通过回归协变量的治疗分配来做到这一点,基本上使用任何“分类器”输出 0-1 区间的预测,然后我们可以解释为概率。例如,逻辑回归是一种广义线性模型,由二项式分布(一种离散概率函数)定义。然而,随着治疗的持续,我们将需要一个回归模型来代替。例如,在广义线性模型中,线性回归模型由高斯分布定义。正如上面的动画所示,二项式分布的类别越多,就越接*正态分布。

一旦我们拟合了一个模型,我们就可以得到条件期望 E[A|X] 。但与二项式情况不同,在连续情况下,这不足以产生密度。为简单起见,让我们假设常见的高斯分布,它由均值和方差参数化。分布条件均值将是估计的条件期望(预测);方差将是恒定的,并且将被设置为治疗和预测之间的残差的方差。一旦我们定义了分布,我们就可以根据这个分布得到观察到的处理值的密度。广义 IP 权重是这些密度的倒数。

总结步骤 1:

  1. 拟合函数 g(x) ,对协变量 X 进行回归处理 A
  2. 定义条件分布 Dₗ=Normal(g(xₗ)、Var(aₗ-g(xₗ)))
    a .每个样本的条件均值就是它的预测值。
    b .方差是固定的,是预测残差的方差。
  3. 将密度 dₗ 定义为来自 Dₗaₗ 的值。
  4. 定义重量 wₗ 为密度的倒数: 1/dₗ

第一步的统计概要,模拟治疗

步骤 2:模拟结果

一旦我们获得了*衡权重 w ,我们就可以使用观察到的结果和处理来模拟反事实结果。为了做到这一点,我们用从步骤 1 中获得的 IP 权重对治疗结果进行回归。然而,与二元治疗情况不同,连续治疗的功能形式应该足够灵活,以避免由于错误设定而产生的偏差。例如,我们将添加治疗的二次项或使用样条等对其建模。

当我们对主要治疗变量进行非线性变换时,我们不能再将治疗效果解释为治疗协变量的系数。相反,为了进行反事实的结果预测,我们将设置一些治疗值,并通过我们的模型运行它以获得预测的结果,并在各个单元之间进行*均,以获得每个人都被分配了特定治疗值的*均结果。

我们可以对两个不同的处理值重复这个过程。那么因果效应将是这两个潜在结果预测之间的差异(或比率)。或者,我们可以对我们关心的范围内的每一个治疗值重复这个过程,并获得一个剂量-反应曲线——看看反事实的结果预测如何作为分配不同剂量的函数而变化。

边际结构模型——回归由广义 IP 权重加权的治疗结果。正如罗宾斯、埃尔南和布伦巴克提出的那样。

密码

下面是演示上述估算过程的 Python 代码。

从上述代码中截取的剂量反应曲线。

扩展ˌ扩张

以上描述的是简单的评估。然而,它可以扩展为多个部分。下面是一些这样的扩展。如果你够了,请随意跳过。

稳定重量

在二元处理的 IPW 中,我们通常将概率的权重计算为 1。这导致伪总体的大小是我们样本的两倍,因为每个治疗组的加权结果是我们原始样本的大小。

稳定权重是一个版本,其中分子不是 1,而是治疗流行率(二元治疗的*均值)。这缩小了权重,使得总体伪总体大小是原始样本的大小,而不是两倍的大小。

这种稳定性也适用于连续治疗设置。代替将分子设置为 1,我们可以将分子取为*均处理值下的处理值的密度(或者,更一般地,仅截距模型的预测)。在这个公式下,我们也可以稳定效果修改器,但这是另一篇文章)。上面的代码显示了一个更值得推荐的稳定版本。

用巧妙的协变量代替加权回归

在第二步中,当基于治疗的结果建模时,我们将广义倾向分数作为权重合并到加权回归中。这通常被称为边际结构模型,由 Robins、Hernan 和 Brumback 描述。然而,类似于不同口味的 TMLE ,我们也可以将广义倾向得分作为额外的协变量纳入第二步结果回归,而不是作为权重。事实上,这正是*野和伊本斯所建议的

在这个版本中,我们添加了密度(不是它们的倒数)作为一个额外的特征。但是,由于它是另一个连续的度量,容易出现错误设定,我们将灵活地添加它。通常还通过添加*方项和与治疗变量(或样条)的相互作用。

以广义 IP 权重作为预测因子的结果模型。如*野和 Imbens 所提议。

需要注意的一个小但重要的细节是,在预测过程中,当我们为所有个体设置感兴趣的治疗值时,我们现在首先需要计算该特定值的密度,然后将这些密度作为预测值插入我们应用的结果模型。

异方差密度和其他分布

在第一步的第(2)项中,我们用一个固定的方差来估计所有个体的密度。这种被称为同伦假设的假设是合理的(可以通过检验残差进行经验检验),但可以放宽。类似于密度函数的均值如何取决于协变量(即预测),方差也可以是随协变量变化的函数。或者以其他方式,如密度加权回归。

此外,我们可以使用其他分布参数化密度函数,如 t 分布、截尾正态分布等。或者,可以通过使用核密度估计进一步去参数化,但天下没有免费的午餐,这将需要更密集的数据来进行可靠的估计。

持续治疗下的阳性假设

到目前为止,我们已经讨论了如何获得治疗和结果之间的统计关联。然而,要将它们转换成因果关系,我们需要做额外的假设。无论统计估计有多复杂,这些假设都是必要的。我们需要在此基础上应用额外的逻辑来证明这些联想差异确实是因果关系。

因果关系=联想+逻辑

回想一下,我们有三个主要假设:一致性、可交换性和积极性。一致性是对治疗机制的一种假设,因此与分类治疗设置相同。可交换性假设不存在不可测量的混杂因素,并且在给定协变量的情况下,每个潜在结果独立于观察到的治疗分配(无偏倚)——这也是相同的。积极是需要一些调整的假设。

回想一下,在分类情况下,阳性假设每个单元都有一些机会(正概率)被分配到每个治疗中。这意味着治疗组共享共同的支持,并且它们的特征重叠。对于整个空间协变空间 X 上的所有处理值 a ,它被正式定义为 Pr[A=a|X] > 0

但是在连续的情况下,我们需要用密度来代替概率。然而,其余的保持不变。对于整个协变量空间 X 的所有处理值 a ,我们将需要\u( A = A | X)>0。也就是说,我们需要所有可用的治疗组合和协变量水*的正密度。幸运的是,这可以通过在我们获得的密度模型(其*均值是我们的回归预测)下检查不同处理值的密度来进行经验测试(像常规阳性一样),特别是那些我们最感兴趣的。

绘制吸烟实例的条件密度直方图以评估重叠。这里测试两个治疗值:0 =吸烟强度没有变化,1 =每天增加一支烟。这相当于在二元治疗情况下绘制概率图,以评估各组之间的重叠。

或者更广泛地说——检查所有 可能剂量增加的 的条件密度分布的脊状图。我们看到剂量之间的距离越远,重叠越小。因此,我们可能更相信局部变化的影响(比如说,增加 5 对没有变化),而不是全局变化的影响(比如说,增加 25 对没有变化)。

限制

增加治疗值的数量不是没有代价的。在模拟连续治疗时,我们应该注意几个限制。

首先,理论假设更难符合。更难实现可交换性,因为我们现在有更多的治疗值,我们希望实现无基础。出于同样的原因,也因为条件密度由于其连续的性质而可能更稀疏的事实,实现积极性也更难。

其次,连续变量更难建模。他们更倾向于错误的设定。我们可以通过使用灵活的估计器(如加法树)或灵活的数据转换(如 GAMs 中的样条)来部分解决这个问题,但这是有代价的——需要更多的数据或由于偏差-方差权衡而引入一些偏差。此外,众所周知,密度很难估计,我们的广义 IP 权重可能对密度估计器的不同选择很敏感。

第三,通常一些连续的测量实际上是有序的。在顺序尺度上的处理可以*似为连续的,特别是当类别的数量及其范围增加时。但是顺序变量的连续逼*也可能由于错误设定而引入一些偏差。IPW 可以推广到序数尺度,这需要序数回归(类似于我们需要线性回归,而常规 IPW 需要逻辑回归),但这些超出了本文的范围。只是想让你知道。

最后,以一个更好的方式结束,在这篇文章中,我想到了一个持续治疗和持续结果的例子。然而,这也适用于其他结果。即,第二步结果模型可以对应于任意类型的结果。最常见的是,如果我们有二元结果,我们可以应用逻辑回归(或任何其他“分类器”)。

结论

在这篇文章中,我介绍了连续治疗的因果推理。我介绍了它们的重要性,描述了如何对它们建模,如何调整所需的因果假设,以及它们的局限性。希望你觉得有用。

线性回归因果推断:内生性

原文:https://towardsdatascience.com/causal-inference-with-linear-regression-endogeneity-9d9492663bac

关于外生变量、外生变量、遗漏变量、测量误差和同时偏差的讨论

作者图片

在我的上一篇文章中,我们讨论了设计线性回归时的一些常见问题——省略重要变量包括无关变量。在本文中,我们将讨论线性回归模型中的内生性,尤其是在因果推断的背景下。

线性回归模型是一种常用工具,用于绘制反应变量(Y)和治疗变量(即 T)之间的因果关系,同时控制其他协变量(如 X),如下所示。治疗效果(即α)的偏差(准确度)和方差(精确度)是此类研究的重点。

什么是内生性?

内生性是指线性回归模型中的预测因子(如治疗变量)与误差项相关的情况。

你称这样的预测器为内生变量。内生变量的系数估计不再蓝(最佳线性无偏估计量)因为内生性违背了线性回归的经典假设之一——所有的自变量都与误差项不相关。

另一方面,如果一个变量没有被模型中的其他变量(例如,响应变量、其他解释变量和误差项)解释,则该变量被称为外生变量。外生变量由模型之外的因素决定。

内生性的来源有哪些?

内生性有多种来源。内生性的常见来源可分为:省略变量、同时性和测量误差。

来源 1:省略变量

如果变量 Z 与响应变量和预测变量都相关,我们称这样的变量为混杂变量。

图 1(作者图片)

如果一个混杂变量 Z 在线性回归模型中被省略,那么受影响的预测因子(如治疗变量)将成为内生的,因为在这种情况下,“无法解释的”变量 Z 漏入误差项,那么受影响的预测因子将是 与误差项相关的

如果由于省略变量而存在内生性,对受影响变量(如治疗变量)的估计会变得有偏差(即省略变量偏差)。见证明此处。这意味着我们有一个不准确的因果效应。

如果混杂变量 Z 在线性回归模型中添加了,那么受影响的预测因子(如治疗变量)将不再是内源性的。因此,对治疗效果的系数估计将不再有偏差。

来源 2: S 不一致性

同时性是内生性的另一个常见原因。当一个或多个预测因子(如治疗变量)由反应变量(Y)决定时,同时性出现。简单来说,X 导致 Y,Y 导致 X,例如,我们可以用教育水*来解释家庭收入,因为受过高等教育的人往往挣得更多。同时,我们知道收入较高的人更容易负担得起高等教育。

通常这种关系可以通过联立方程(也称为结构方程)来解释。

图 2(作者图片)

通过求解上面的两个方程,我们得到了模型的简化形式

图 3(作者图片)

在因果推断的背景下,如果治疗效果 X 由响应变量决定,那么很容易看出治疗效果与图 2 中的误差项 u 相关联。

图 4(作者图片)

因此,如果我们应用图 2 中的 OLS,治疗效果和反应变量都是内生变量。这会导致对治疗效果的估计有偏差(即同时偏差)。所以治疗效果永远不可能是真正的效果。

图 5(作者图片)

来源 3:测量误差

在线性回归模型中,假设观测值被正确测量,没有任何误差。在许多情况下,这种假设是违反的。一些变量(例如,人们锻炼的能力和意愿)可能无法衡量,那么我们使用代理变量(例如,人们的智商得分和在健身房的小时数)来衡量效果。有时候,很难做出正确的观察。例如,年龄变量通常以整数记录,而月和日通常被忽略。在这些情况下,变量的真实值不包括在模型中。变量的观测值与真值之差称为 测量误差

情景一:当测量误差在因变量 Y 中时,它不会引起内生性,因为在这种情况下,无法解释的测量误差是外生变量,与包含的解释变量无关。因此,即使无法解释的测量误差泄漏到误差项,解释变量也不会与误差项相关联。

情景二:相比之下,当测量误差在解释变量中时,内生性的问题就产生了。

假设 X* 是观察到的解释变量,而 X 是变量的真实值。X*和 X 之间的关系可以解释如下,

图 6(作者图片)

我们照常建立线性回归,不包括测量误差项 v,因为它是不可测量的。

图 7(作者图片)

那么我们实际估算的模型是

图 8(作者图片)

通过一些数学,我们可以发现 X*与实际误差项 u 相关,然后内生性发生。

图 9(作者图片)

在图 9 中,Cov(X,v)为 0,因为测量误差与自变量 X 无关,Cov(X,ε)和 Cov(v,ε)都为 0,因为ε与 X 无关,而ε与测量误差不太相关。

那么在有测量误差的线性回归中,OLS 估计量β_hat 不再是无偏的。此外,估计器将总是被低估(例如衰减偏差)。

图 10(作者图片)

内生性的补救方法是什么

在线性回归模型中处理内生性的一种流行方法是通过两阶段最小二乘法(2SLS) 引入一个或多个工具变量

让我们定义这个工具变量 Z:

  • z 与模型中的任何其他协变量(包括误差项)都不相关
  • z 与受影响的预测因子(如治疗变量)有意义且强相关,因此通过 X 间接影响 Y

图 11(作者图片)

图 12(作者图片)

在实践中,工具变量(IV)模型可以分两步实施(2sl):

  • 步骤 1:我们回归受影响的预测因子 x 的工具变量。请记住,我们需要 IV 和 x 之间有很强的相关性。否则,我们可能仍然对受影响的预测因子有偏差。
  • 第二步:我们在第一步拟合的 X 和其他协变量上回归 Y。我们从步骤 2 中得到的估计值将比图 11 中的估计值更加准确和一致。

图 13(作者图片)

最终注释

当使用线性回归模型进行因果推断时,内生性是我们需要解决的问题,否则,我们会由于遗漏变量、同时性或测量误差而得到有偏差的处理效果。

如果你对线性回归因果推断感兴趣,这里有一些相关的帖子可以浏览。

感谢您的阅读!!!

如果你喜欢这篇文章,并且想请我喝杯咖啡,请点击这里

您可以注册一个 会员 来解锁我的文章的全部访问权限,并且可以无限制地访问介质上的所有内容。如果你想在我发表新文章时收到电子邮件通知,请 订阅

使用 Python 和 SparseSC 的综合控制的因果推理

原文:https://towardsdatascience.com/causal-inference-with-synthetic-control-using-python-and-sparsesc-9f1c58d906e6

了解合成控制并使用微软的 SparceSC 包在大型数据集上运行合成控制

什么是合成控制法?

我将尽量缩短这一部分,更多地关注为什么数据科学家应该关注这些方法,以及如何根据使用 SparseSC 包的实践经验在更大的数据集上使用它们。

综合控制(SC)方法是一种统计方法,用于估计二元处理对观察面板(纵向)数据的因果影响。这种方法被称为“过去几年政策评估文献中最重要的创新”得到了广泛的报道,并在华盛顿邮报上发表了一篇文章——说真的,这里有一个惊人的数学技巧来学习不为人知的东西。“SC 是一种通过对未处理单位进行加权*均来创建人工对照组的技术,其方法是再现干预(处理)前处理单位的特征。SC 作为一个治疗单位的反事实,治疗效果的估计是治疗后观察结果和 SC 结果之间的差异。”[1]

“一种看待 SC 的方式是将其视为对差分(DiD)估计的改进。典型的 DiD 将处理单位与对照单位的*均值进行比较。但通常处理过的单位看起来不像典型的对照(例如,它可能具有不同的生长率),在这种情况下,DiD 的“*行趋势”假设无效。SC 通过选择一个更智能的线性组合而不是简单的*均来补救这一点,以对更相似的单元进行更重的加权。SC 的假设是,如果有影响治疗和未来结果的内源性因素,那么你应该能够通过匹配过去的结果来控制它们。因此,SC 提供的匹配可以处理 DiD 无法处理的一些估计问题。”[1]

这是因果推理书的链接,我发现这本书对理解 Matheus Facure 的《勇敢者和真理的因果推理》第 15 章背后的数学非常有用。

为什么数据科学家要关心这种方法?

作为一名数据科学家,您经常会遇到以下情况,运行 A/B 测试是不可行的,因为-

  1. 缺乏基础设施
  2. 缺少运行 A/B 测试的类似组(在评估州政策的情况下,因为没有与其他州等效的州)
  3. 给一个群体提供不必要的优势。有时进行 A/B 测试会带来不公*的优势,并导致您进入反垄断领域。例如,如果亚马逊试图对不同的客户收取不同的价格,或者对相同的产品对其卖家适用不同的利润,该怎么办?

作为一名数据科学家,利益相关者可能仍然会要求您评估某些变化/处理的影响,在这种情况下,合成控制可以提供帮助。出于这个原因,它是一个有价值的工具,可以放在你的算法工具箱里。

问题概述

我们将使用 Proposition 99 data 来解释这种方法的用例,以及如何使用 SparceSC 库及其关键特性。1988 年,加州通过了一项著名的烟草税和健康保护法案,即众所周知的 99 号提案。它的主要作用是对加州境内的烟草销售征收每包 25%的州消费税,对其他商业烟草产品,如雪茄和嚼烟的零售征收大致相同的消费税。对烟草销售的额外限制包括禁止在青少年可以进入的公共场所出售香烟自动售货机,以及禁止单支香烟的单独销售。该法案产生的收入被指定用于各种环境和卫生保健项目,以及反烟草广告。为了评估其效果,我们可以从多个州收集多年的香烟销售数据。在我们的案例中,我们从 39 个州获得了从 1970 年到 2000 年的数据。”[2]

**Note —** *To render the blog with both code and content correctly, you can read the same on my quarto powered blog —* [*Link*](https://aayushmnit.github.io/posts/2022-09-19-SyntheticControl/2022-09-19_SyntheticControl.html)*, if you are interested in replicating the results, here is a kaggle notebook you can run to follow along —* [*Kaggle Link*](https://www.kaggle.com/code/aayushmnit/causal-inference-with-synthetic-control-using-pyth)

让我们看看数据

图 1—数据快照

我们有以state为处理单位的数据,以及每年(year列)人均香烟包装销售额(cigsale列)和香烟零售价格(retprice列)。我们将旋转这些数据,以便每行是一个治疗单位(state),列代表每年的cigsale值。

图 2—转换数据的快照

让我们来观察一下加州和其他州的人均卷烟销售趋势。

图 3-加州和其他州的卷烟销售对比

从上面的图表中我们可以看出,20 世纪 80 年代以后,卷烟销售普遍下降,随着 99 号提案的出台,加利福尼亚州的下降趋势加快了。我们不能肯定地说这种情况是否有任何统计学意义,这只是我们通过检查上面的图表观察到的一些情况。

为了回答 99 号提案是否影响了卷烟消费的问题,我们将利用干预前时期(1970-1988 年)建立一个模拟加州卷烟销售趋势的合成对照组。然后,我们将看到这个合成控制在干预后的表现。

用 SparseSC 软件包拟合综合控制

在高层次上,SparseSC包提供了两个用于拟合综合控制的功能,即fit()方法和fit_fast()方法。在高水*上-

  • fit() -该方法尝试联合计算权重,并得出“最优”的 sc。这是我发现的大多数代码/库中最常用的方法,但是计算量很大,需要很长时间才能运行。因此,无法扩展到更大的数据集。
  • fit_fast() -该方法试图通过执行一些非匹配分析来分别计算权重。这种解决方案要快得多,并且通常是处理较大数据集的唯一可行方法。这个软件包的作者推荐从fit_fast方法开始,并且只在需要的时候才转向fit方法。

[SparseSC.fit_fast()](https://sparsesc.readthedocs.io/en/latest/api_ref.html#fit-a-synthetic-controls-model-fast-separate)方法需要至少三个参数-

  1. 特征 —这是 I/p 变量的 NumPy 矩阵,其中每一行代表一个治疗/控制单元(在我们的例子中是状态),每一列是从治疗前(1970-1988)的时期,矩阵中的值是感兴趣的度量(在这种情况下是cigsale值)
  2. 目标 —这是 I/p 变量的 NumPy 矩阵,其中每一行代表一个处理/控制单元(在我们的例子中为状态),每一列是从处理后的时期(1999-2000),矩阵中的值是感兴趣的度量(在这种情况下是cigsale值)
  3. treatment_units —这是包含已处理单元的行索引值的整数列表

注:治疗单元可以是多个治疗指标的列表。想想同样的处理方法应用于多个组的情况,例如,如果 99 号提案在加利福尼亚州和明尼苏达州都滚动了,在这种情况下,treatment_units 将得到[2,15],这是这些州各自的索引。

现在我们已经拟合了模型,让我们通过使用predict()函数来获得综合控制输出。

图 4 —样本结果

现在我们有了合成控制,我们可以用加利福尼亚州的结果变量来绘制它。

图 5——使用综合控制对加利福尼亚州 99 号提案的评估

注:在干预前期,合成对照并不精确地再现处理,而是紧密地遵循曲线。这是一个好迹象,因为它表明我们没有过度适应。此外,请注意,在 1988 年之后的干预(引入 99 号提案)之后,我们确实看到了分歧。

有了合成对照组,我们可以用治疗结果和合成对照组结果之间的差距来估计治疗效果。

图 6——加州 w.r.t .合成控制人均卷烟销售的差距

看上面的图表,我们可以观察到,到 2000 年,99 号提案已经减少了大约 29 包香烟的销售。现在我们将弄清楚这是否有统计学意义。

做出推论

在合成对照中,为了发现我们观察到的效果是否显著,我们进行了安慰剂试验。安慰剂试验是随机选取一个未经处理的单元,假装所有其他单元都在控制中,并在这个随机选择的未经处理的单元上拟合合成控制,以估计效果。一旦我们多次重复这种安慰剂试验,我们就可以估计这种随机观察到的效应的分布,并观察观察到的效应是否与安慰剂观察到的效应显著不同。在SparceSC包中,我们可以使用estimate_effects方法为我们自动完成这项工作。估计效果方法最少需要两个参数-

  1. 结果 —这是 I/p 变量的 NumPy 矩阵,其中每一行代表一个治疗/控制单元(在我们的情况下是状态),每一列是治疗前和治疗后的时间段,矩阵中的值是感兴趣的度量(在这种情况下是cigsale值)
  2. unit _ treatment _ periods—每个单元的治疗周期的向量,(如果一个单元从未被治疗,则使用 np。NaN(如果向量通过数字索引引用周期)

图 7—评估效果的输出

estimate_effects方法返回一个对象,该对象将打印每个治疗后年份的治疗效果,并估计观察到的差异的显著性。打印的信息也可以在返回对象的pl_res_pre函数中找到。由于介词 99,加利福尼亚州 2000 年销售额的估计影响为-30.0,p 值为 0.05。

结论

以下是一些关键要点-

  1. 综合控制允许我们将多个控制单元组合起来,使它们类似于被处理的单元。有了合成控制,我们可以估计在没有治疗的情况下我们的治疗单位会发生什么。
  2. 微软 SparseSC 库提供了一个快速易用的 API 来运行合成对照组,并允许我们运行安慰剂测试来估计观察到的效果的显著性。

参考

  1. Sparce SC 文档
  2. 马修·法库《勇敢和真实的因果推理》——第 15 章
  3. 数据来源— 人均卷烟消费量(包)。资料来源:Orzechowski 和 Walker (2005)获取&使用信息——公开

我希望你喜欢阅读,并随时使用我在 Kaggle 上的代码来为你的目的进行测试。此外,如果对代码或博客文章有任何反馈,请随时联系 LinkedIn 或给我发电子邮件,地址是 aayushmnit@gmail.com。您也可以在 Medium 和 Github 上关注我,了解我将来可能会写的博客文章和探索项目代码。

因果 Python——3 个简单的技术,今天就开始你的因果推理之旅

原文:https://towardsdatascience.com/causal-kung-fu-in-python-3-basic-techniques-to-jump-start-your-causal-inference-journey-tonight-ae09181704f7

学习 3 种识别因果关系的技术,并在 Python 中实现它们,而不会损失几个月、几周或几天的研究时间

图片由 pexels.com 的维尔罗伊·费尔南德斯拍摄

如果你正在读这篇文章,你可能已经在数据科学领域工作了 2-5 年。你以前很可能听说过因果关系,甚至可能读过一两本关于这个主题的书,但是如果你不自信或者你不清楚如何抓住这些概念并让它们在实践中为你工作而不损失几周甚至几个月的研究时间,这篇文章是给你的。

我也去过类似的地方!在某个时候,我读了差不多 1000 多页关于因果关系的书,包括书籍和研究论文,但我仍然不清楚如何在实践中应用这些概念,除非花几周时间去实现!

这篇博文旨在帮助你今晚开始你的因果推理之旅

开始吧!

【链接到笔记本康达环境文件下面】

因果推理 101

在这篇文章中,我们关注因果推理。出于本文的目的,我们将因果推理理解为从观察数据中估计一个变量对另一个变量的因果影响的过程。

因果效应估计旨在当我们将治疗值修改一个单位时,捕捉结果变量中(预期)变化的强度。

在实践中,几乎任何机器学习算法都可以用于此目的,然而在大多数情况下,我们需要以不同于经典机器学习流程的方式来使用这些算法。

图片由castor ly Stock@ pexels.com 提供

混淆公司。

从观测数据中估计因果关系的主要挑战(但不是唯一的挑战)来自于混淆混杂因素是感兴趣系统中的一个变量,它在治疗和结果之间产生一种虚假关系。虚假的关系是一种错觉。有趣的是,你不仅可以在记录的数据中观察到虚假的关系,在现实世界中也是如此。它们为许多刻板印象和认知偏见奠定了坚实的基础(但那是另一篇文章)。

去混杂

为了获得因果效应的无偏估计,我们需要排除混杂因素。同时,我们需要小心 不要引入混淆自己的 !这通常归结为在您的分析中控制变量的正确子集。不要太小,不要太大。选择正确控件的艺术和科学本身就是一个话题。

很好地理解它可能会受到这样一个事实的阻碍,即关于这个主题有许多令人困惑和不准确的信息来自各种来源,甚至包括高影响力的同行评审期刊(原文如此!)。如果这听起来让你感兴趣,我将在我即将出版的关于 Python 中因果推理和发现的书中更深入地探讨这个主题。

对 Python 中的因果关系和因果机器学习感兴趣?

订阅邮件列表获取关于因果关系的独家免费内容和我的因果书更新:https://causal python . io

三骑士

好消息是,在某些情况下,我们可以自动选择正确的变量作为控制。在本帖中,我们将演示如何使用三种不同的方法来识别图表中的因果关系:

  1. 后门标准
  2. 前门标准
  3. 工具变量(四)

前两种方法可以从由 Judea Pearl 及其同事(例如 Pearl,2009 )制定的一套更通用的识别规则中导出,该规则称为 do -calculus

后者(IV)是计量经济学中非常流行的一系列方法。根据 Scott Cunningham(Cunningham,2021 )工具变量估计器早在 1928 年就由 Philip Wright (Wright,1928)首次提出。如果你想知道挑选什么资源来学习更多关于 IVs 和其他标准的知识,请查看这篇文章

图片来自卡罗琳娜·格拉博斯卡@ pexels.com

假设

澳大利亚推理方法要求满足特定的假设。我们在这里没有足够的空间来讨论所有的问题,但是我们将简要回顾一下最重要的问题。

因果图

我们在这里理解的因果推理的第一个一般假设是,我们有一个因果图来编码变量之间的关系。变量被编码为节点,它们之间的关系被编码为有向边。边的方向表示因果影响的方向。

一些方法要求测量所有相关变量,一些方法允许各种配置中的不可测量变量。

如何找到一个因果图不知道的时候呢?

后门标准

后门标准要求在我们的数据中没有 隐藏的混杂因素 ,即没有影响治疗和结果并且同时未观察到的变量。这种假设在一些现实世界的场景中可能很难满足,因为可能很难确定没有我们不知道的隐藏变量影响治疗和结果。对于非常复杂的系统(例如,社会、生物或医学环境),尤其如此。

前门标准

前门标准允许治疗和结果之间的隐藏混杂,但要求治疗对结果的影响完全由另一个不受隐藏混杂影响的变量介导。那是满嘴的!请随意先睹为快,看看下面的图 1(B) ,获得一些视觉直觉。

工具变量

工具变量也允许在治疗和结果之间隐藏混杂,然而它们要求有另一个变量与治疗相关,但与结果无关(既不是直接相关,也不是通过共同原因;埃尔南&罗宾斯,2020)。

让我们看一下图 1 来为我们上面讨论的假设提供一些视觉直觉。

图一。可使用后门(A)前门 (B)和工具变量 (C)方法识别的图表示例。真实的你的形象。虚线表示未观察变量

DoWhy 的力量

DoWhy(Sharma&ki ciman,2020)是一个因果推理库,它最初是作为微软的开源项目开始的,最*被转移到一个名为 py-why 的新项目中。它由一个经验丰富的研究人员和开发人员团队开发,提供了所有 Python 因果库中最全面的功能集。

DoWhy 有几个不同的 API,包括主要的面向对象的CausalModel API、高级的 Pandas API 和新的实验 GCM API。在这篇文章中,我们将使用第一个。

DoWhy 的承诺是,它将自动识别图中的因果关系(假设使用我们上面讨论的三种方法之一可以识别它们)。这意味着您不必查看因果图并自己决定应该使用哪种方法(对于较大的图,这可能会变得更加困难)。

寻找推荐关于因果关系的书籍阅读?

**https://aleksander-molak.medium.com/yes-six-causality-books-that-will-get-you-from-zero-to-advanced-2023-f4d08718a2dd **

让我们得到数据

为了这篇文章,我们将生成三个简单的合成数据集。为什么是合成数据?让你非常清楚发生了什么。

在每个阶段,你都可以回到数据生成过程,亲眼看看数据集的真正结构是什么。

我们开始吧!

****代码块 1。生成数据。

首先,我们导入numpypandas。DoWhy 对 Pandas 数据框进行本地操作,这很方便,因为在构建模型和分析结果时,它可以很容易地识别您的变量。

接下来,我们生成三个不同的数据集。请注意,在结构上,这些数据集遵循图 1 中的对应数据集。一个需要注意的重要事情**是,在数据集 2 和 3 的情况下(它们在图 1 中的对应部分是 B 和 C ),我们没有将变量包含在数据帧中,尽管它被用于数据生成过程。这是故意的,因为我们在模拟未被观察到的场景。****

因果推理的 4 个步骤

oWhy 的主 API 提供了一种伟大而直观的方法来执行因果推理。

它包括四个步骤:

  1. 对问题建模
  2. 找到需求
  3. 估计效果
  4. 反驳模型

在本文中,我们将重点关注前三个步骤,并针对三个数据集分别运行所有步骤。

因果,我们来了!

让我们从必要的导入开始,然后跳到第一个数据集。

代码块 2。导入 DoWhy 组件。

数据集 1 —后门标准

让我们从数据集 1 开始。

第一步:对问题建模

步骤 1 有两个子步骤:

  • 创建一个 DoWhy 可以阅读的格式的图表
  • 实例化CausalModel对象

在我们的例子中,我们将使用名为【GML】的图形语言来定义数据集的图形结构。开始吧!**

****代码块 3。数据集 1 的 GML 图定义。

GML 语法相当简单(至少对于简单的图形是这样)。如果你想更多地了解 GML,你可以在这里做。

现在,让我们实例化我们的模型对象。

****代码块 4。实例化数据集 1 的“CausalModel”对象。

我们将数据集和图表以及治疗和结果变量名传递给构造函数。这就是我们所需要的!

让我们将模型可视化,以检查图表是否如我们所期望的那样。用model_1.view_model()就可以了。

****图二。通过调用 model_1.view_model()得到的模型 1 的图形

图表看起来不错!在图 2 中,我们有与图 1A* 中完全相同的一组节点和边。*****

第二步:找到需求

****估算需求表示我们想要估算的数量,并且 不一样。你可以把因果估计看作是调整后的公式,它只包含我们想要控制的变量,以便获得无偏的因果估计。Estimand 既不是一个值,也不是一种估计方法,它是一个要估计的量。

代码块 5。获得模型 1 的估计值。

请注意,DoWhy 只为数据集 1 找到了一个 estimand 后门。这是有意义的,因为我们在数据中没有任何隐藏的混杂因素,治疗的效果也没有被调节。

让我们估计一下因果效应,并检查它是否是我们所期望的。

第三步:评估效果

代码块 6。估算模型 1 的因果效应。

我们在模型对象上调用了.estimate_effect()方法,并将上一步中的 estimand 命令作为参数传递给我们想要使用的 estimator 方法名称。DoWhy 提供了许多不同的估值器。对于我们的目的,线性回归已经足够好了。

结果如何?这个估计与真实情况非常接*。在代码块 1 中可以看到, X 的真实系数为 0.87 ,模型返回 ~0.88

第四步:反驳测试

反驳测试是实现卡尔·波普尔的 可证伪性逻辑 (波普尔 1935;1959)中的因果推理过程。这一步超出了本文的范围。如果你想很快看到它的作用,你可以查看这个笔记本,在【为什么查看这个页面中可以找到最新的反驳测试列表。

数据集 2 —前门标准

让我们用数据集 2 重复整个过程。

第一步:对问题建模

手动创建 GML 图可能会令人望而生畏。让我们把它变得更简单。我们将定义一个节点列表和一个边列表,并让两个 for 循环为我们完成这项工作。

代码块 7。使用 for 循环生成数据集 2 的图形。

太美了。让我们实例化模型。

代码块 8。实例化数据集 2 的“CausalModel”对象。

我鼓励你使用model_2.view_model()来检查你的图表是否符合预期。您可以在本文附带的笔记本中看到它的实际应用(下面的链接)。

第二步:找到需求

我们现在已经准备好寻找目标了。

代码块 9。获取模型 2 的估计需求。

DoWhy 发现的唯一需求是前门。这是我们提供的图表所预期的。让我们检查一下估计的效果。

第三步:评估效果

这个过程实际上和我们之前看到的一样。请注意,我们已经将method_name改为frontdoor.two_stage_regression

代码块 10。获取模型 2 的估计值。

我们看到估计的效果是 ~0.66 。预期的真实效果是怎样的?我们能计算出它多亏了休厄尔·赖特,菲利普·赖特的儿子(原文如此!)我们之前在工具变量的上下文中提到过。根据 Judea Pearl (Pearl,2019),Sewall Wright 是第一个引入 路径分析——一种评估线性系统中因果效应强度的方法(Wright,1920)。为了得到一个真正的因果系数,我们需要将治疗和结果之间的所有系数相乘。**

在我们这里,因果影响是这样的: X → Z → Y 。我们取公式中的系数 X公式中的系数 Z 并相乘。所以真正的因果关系是 0.78 x 0.87 = 0.6786 。道为什么做得很好!****

数据集 3 —工具变量

快到说再见的时候了。在我们这样做之前,让我们最后一次经历整个 4 步过程,看看工具变量技术的作用!

第一步:对问题建模

这次我们还将使用 for-loops 来自动创建 GML 图定义。

代码块 11。为模型 3 生成 GML 图。

让我们实例化CausalModel对象。

代码块 12。实例化模型 3 的“CausalModel”对象。

我再次鼓励你使用model_3.view_model()。有时在你的图表中很容易出错,其结果通常会对你的估计不利。

第二步:找到需求

代号布洛克 13。获得模型 3 的估计值。

DoWhy 正确地认识到数据集 3 的因果关系可以使用 工具变量 技术来识别。我们来估计一下效果吧!

第三步:评估效果

估计效果是 ~0.90 。真实是 0.87 。挺*的!

如果你想了解工具变量是如何工作的,哪些因素会影响估计误差,你可以查看 Lousdal (2018),Cunningham (2021),或者在下面的评论中告诉我,如果你想让我就这个主题写一篇文章。

结束了!

在这篇文章中,我们介绍了因果关系识别的三种方法:后门判据前门判据工具变量 技术,并使用 DoWhy 演示了如何在实践中使用它们。虽然我们在例子中只使用了非常简单的图形,但是我们学到的所有东西都可以推广到更大和/或更复杂的图形。

如果你是一个对因果关系感兴趣的 Python 人,在这里注册我的电子邮件列表来增长你的知识:https://causal Python . io

你将在收件箱里获得免费独家内容和我即将出版的因果推理&发现书的更新!

代码和环境

本文的笔记本环境文件在这里:

****https://github.com/AlxndrMlk/blogs-code/tree/main/Causal Python 3 - Simple Techniques to Jump-Start Your Causal Inference Journey Today

脚注

请注意,我们没有冻结随机种子,所以您的数字可能会略有不同。

我们的数据生成过程非常嘈杂。结果有点偏差这个事实是意料之中的。估计的不确定性应该随着样本量的增加而下降。有关不确定性估计以及哪些因素有助于减少不确定性的更多详情,请查看此系列

参考

坎宁安,S. (2021)。因果推理:混音带。耶鲁大学出版社。

埃尔南文学硕士,罗宾斯法学硕士(2020)。因果推断:如果会怎样。博卡拉顿:查普曼&霍尔/CRC。

法学硕士卢斯达尔(2018)。工具变量假设、验证和估计导论。流行病学的新兴主题 15 (1)。

珀尔,J. (2009 年)。因果关系 。剑桥大学出版社。

珀尔,J. (2019)。原因之书企鹅。

波普,K. (1935)。逻辑推理。施普林格。

波普,K. (1959)。科学发现的逻辑。基础书籍。

Sharma,a .,& Kiciman,E. (2020 年)。DoWhy:一个用于因果推理的端到端库。 arXiv 预印本。ArXiv:2011.04216。

赖特,P. (1928)。动植物油关税。麦克米利安。

赖特(1920 年)。遗传和环境在决定豚鼠花斑类型中的相对重要性。美国国家科学院院刊,6,320–333。


❤️ 对获取更多这样的内容感兴趣吗?使用此链接加入:

https://aleksander-molak.medium.com/membership

这篇文章中的一些书籍链接是亚马逊会员链接,通过使用这些链接购买,你将支持作者(或他们的家庭)和我的写作(我将从你的每一笔购买中获得一小笔费用)。谢谢大家!****

因果关系和人工智能想象

原文:https://towardsdatascience.com/causality-counterfactuals-and-ai-imagination-7a0c10cec6ae

因果关系如何帮助推进机器“想象”的状态?

在过去的几个月里,有大量的媒体报道了文本到图像建模的惊人进展。这些模型可以基于用户输入的文本提示生成人类级别的艺术品,并且是新一波艺术图像生成算法的一部分。

图 1:由稳定扩散模型生成的样本图像。在 CreativeML Open RAIL-M 下许可的稳定扩散模型输出。

很难不对机器从简单的句子中变出如此细节的能力印象深刻——如图 1 所示。

我们可以推测图像生成模型的工作方式如下。我们有一组数据(在稳定扩散~ 50 亿张图像的情况下),根据这些数据,我们学习一组规则——一个数学函数——它们精确地描述了底层数据。在图 2 中可以看到一个非常简单的例子。

图 2:二维(X 和 Y)的简单曲线拟合。作者创建的图像。

为了“想象”新的实例,我们简单地使用我们的函数来预测函数最初没有显示的数据点,例如,图 2 中没有显示 X 值 5,但是使用曲线,我们可以将相应的 Y 估计为 20。

这就是所有机器学习模型如何根据看不见的数据生成预测。文本到图像模型,如稳定扩散,以及它的表兄弟 DALLE 和 Imagen,也不例外。该算法将从数十亿个参数*的学习维度空间中进行采样,解释文本提示以指导在空间中的何处进行搜索,然后返回从未见过的图像。魔法。

为什么因果关系很重要?

挑战在于,这些生成式人工智能模型与我们对世界的理解不同。他们非常擅长重新创造普通场景的新视图,但不理解工作中的因果关系。

图 3:稳定扩散生成的图像,展示了当模型缺乏对因果关系的理解时,简单的场景是如何失败的。由作者使用稳定扩散生成的图像。

如果不理解世界内部相互作用的因果关系,这些模型就无法“想象”我们认为微不足道的情景。

那么,因果关系在哪里呢?

因果模型采取了不同的方法。上面的文本到图像模型“想象”来自新文本提示的看不见的数据实例,而因果模型允许我们探索两种非常人性化的行为:

  • 策划 (干预):给定某个场景,我该怎么做?
  • 反思 (反事实):基于所发生的事情,我本可以做些什么不同的事情?

你会注意到,这些类型的“想象”与文本对图像模型的创造能力是非常不同的。事实上,因果关系的世界区分了这些不同的类型,如下面图 4 所示的朱迪亚·珀尔的因果关系阶梯。

图 4:朱迪亚·珀尔的因果阶梯。作者创建的图像。

稳定扩散等深度学习模型执行的任务位于梯子上的“看见”级别。这些模型能够回答诸如“如果我在文本提示中看到‘猫’这个词怎么办?”。

因果模型能让我们爬得更高。他们让我们接触到干预,帮助回答计划类型的问题,以及反事实,让我们反思之前的见解。

干预和反事实为我们提供了回答机器学习模型之前无法解决的问题的超能力。

让我们用一个例子来说明这些在现实世界中是如何应用的。假设我们有一大堆关于音乐家的数据。我们希望根据各种不同的功能来预测他们在 6 个月内的销售记录。因此,我们确定了一个因果图,它显示了这些特征之间的关系,以及这些特征对唱片销售的影响。请看图 5,这是一个虚构的例子**。

图 5:如何估计给定艺术家的音乐销量的因果图示例。作者创建的图像。

虽然创建的图表过于简化,但它确实展示了使用因果技术的好处。因果图明确地告诉我们已经编码到我们的模型中的假设,使它比其他方法更容易解释:

  • 从“唱片公司营销支出”指向“6 个月创纪录销售额”的箭头表明,我们认为这两者之间存在某种因果关系。在这种情况下,营销支出会带来更大的销售额。

介于中间的

一旦我们有了因果图,我们就可以进行干预来想象一个新的场景。我们可以问“一个艺人获得 10 个小时的电台播放时间会有什么影响?”并在 6 个月内看到对销售的后续影响。这种干预将新值强加到“广播播放”特征中,将其设置为 10 小时。

事实上,这是一种干预,意味着我们不会简单地寻找其他有 10 小时广播剧的艺术家,并比较相似之处。干预意味着将广播剧的价值限制在 10 小时,防止抖音的营销支出和使用影响广播剧。

图 6:应用将广播播放时间设置为 10 小时的干预,并显示它如何改变原始因果图。作者创建的图像。

通过隔离广播剧的影响,我们可以计算***,只要我们有足够的数据,如果一个艺术家得到 10 个小时的播放时间,6 个月内唱片销售的合理无偏估计。

反事实

传统的机器学习模型学习训练时输入给它们的数据的表示。

然后,应用于模型的巧妙技术允许用户塑造和塑造数据表示。在稳定扩散的情况下,这些是模型学习与数据表示的不同区域相关联的文本提示,即,如果文本提示包含单词“cat”,则模型被限制为从数据表示的“cat 部分”中挑选。

因果模型也允许这些行为,但也可以改变底层的数据表示,而不是简单地塑造它。这个允许因果模型用于回答真正的反事实问题**** 。

反事实是关于替代现实的问题,作为人类,我们一直在使用它们,它们是以下方面的基础:

  • 现代医学:“是扑热息痛让我退烧的吗?”
  • 警察工作:“如果受害者早一个小时离开,他们会活下来吗?”
  • 你的网飞狂欢:“如果我没有看 8 小时的《欧扎克》,我会感到如此内疚吗?”

反事实为我们提供了一种“想象”不同结果的新技术——这是其他机器学习工具无法做到的。让我们考虑一个例子:“如果泰勒·斯威夫特的电台播放时间减半至 1000 小时,她的销量会是多少?”。

图 7:泰勒·斯威夫特改变电台播放时间的影响的反事实例子。作者创建的图像。

请注意,反事实还包括干预(将泰勒的广播剧设置为 1000 小时),但尽管前面的干预问题对任何艺术家都是通用的,但反事实是特定于个人的,泰勒在抖音的广告支出和使用仍是他们的原始价值。

结论

虽然最新和最伟大的生成式人工智能模型可以创建令人惊叹的图像,但在人工智能中实现类似人类的想象力还有很长的路要走。

在我们寻求机器想象力的过程中,因果关系可以提供许多强大的工具,为我们提供提前计划的方法,同时反思以前的事件。

附录

*一个很酷的网站是https://losslandscape.com/,在那里你可以查看这些深度学习模型探索以获得可行解决方案的维度空间的真实世界示例。我相信你也可以把它们当作非功能性食品来卖。

  • *我们不会在这篇文章中讨论如何构建因果图。现在假设这超越了天堂,进入了我们的集体意识。或者,请随意阅读该主题,这篇关于数据科学的文章给出了合理的介绍。而这篇论文更有深度。

    • *可以使用随机对照试验来计算干预,其中概率干预确定您是否接受治疗或安慰剂,或者通过以巧妙的方式分割数据集来消除其他特征的影响。如果你有兴趣了解更多这方面的知识,请留下你的评论,我会在上面写一篇博客!
      • *对于那些目前尖叫,你可以从传统的基于 ML 的模型计算反事实;这是真的。然而,我区分了这些反事实,以及那些通过使用“真正的反事实”可能存在因果关系的反事实。基于相关性的反事实估计假设一个因果图结构,其中每个数据点对目标都有因果影响,称为星形图

这样做的结果是,如果数据生成过程的真正因果结构偏离了这一点(即,您的要素交互并不完全相互独立),您将无法计算代表性的反事实。这就是误导性的反例。

因果关系,而非相关性——因果推理的本质

原文:https://towardsdatascience.com/causation-not-correlation-the-nature-of-causal-inference-49dd1a2e5188

结构因果模型的因果建模方法

来源

1。因果推断

1.1。因果关系

检测真正因果关系的能力对于在不同类型的领域(如健康、金融、银行等)产生有效的警告是至关重要的。当解释一个系统的元素之间的关系时,因果性这个术语本身不同于相关性,因为它有数学和统计学的基础[1]。

作为文字描述,一个特征 f1 可以作为一个例外导致另一个特征 f2 ,在所有混杂因素都适应的情况下, f1 的中断返回 f2 的移位;然而, f2 中的干扰可能不会输出 f1 中的变化。这种情况可以被认为是相关性性质的矛盾事件,当 f1f2 关联, f2 与 f1 关联,而如果 f1 导致 f2f2 可能不会导致 f1 时,这种相关性在本质上是成比例的。

f1 引起 f2 引起 Z;但是,f2 不会导致 f1(归作者所有)

在没有随机对照的初步研究的情况下,完全依靠观察信息来完成因果联系通常是很重要的。在这种情况下,一段关系不包含因果关系这一可证实的事实就变成了现实。随后,至关重要的是要认识到有特定结果的场合和有关联的场合。

对各种因素之间关系的一个可能的解释是,存在一个令人困惑的变量,它既影响目标,也影响目标背后的主旨,而两者互不因果。看不见的混杂因素代表了从注意到的信息中收集因果关系的真正危险[2]。

考区已做出重要承诺,要改进这类调查的策略和方法。惰性结果设计、倾向分数协调和主要因果模型可能是最著名的观察因果推测系统。

1.2.结构因果模型(SCMs)

这种类型的因果模型利用图形模型来处理因果条件,图形模型通过将因素处理为图中的枢纽并将因素之间的连接处理为图中的边来给出直觉感知。SCMs 填补了一个完整的系统,汇集了图形模型,潜在的条件,反事实和干预的基本原理[3]。

每个结构模型可以作为 DAG 来寻址,其中因子作为因子之间的连接来寻址,因子作为边来寻址。推测的因果关系之间的结果和说明性因素是由强大的螺栓解决因果关系。

带有单个链接的因果图形表示示例(归作者所有)

图形模型作为一种组织和想象世界信息的语言,可以融合信息驱动和人类数据源。

反事实赋予了渴望了解的事物解释的能力,而基本条件有效地整合了这两者。SCMs 极大地影响了许多学科,以图解和数学结构授权当前信息的编码,从而利用信息来衡量干预和反事实调查的解决方案。

每个结构模型都可以与一个图形模型相关联,这个图形模型也可以定义为有向无环图(DAG) 4。每条边都与下面的因果假设相比较。

在特征 B 是特征 A 的后代的事件中,我们说 B 是由 A 带来的,或者 A 是 B 的直接原因。

如果变量 B 是特征 A 的亲戚,我们说 B 可能是由 A 引起的,或者 A 可能是 B 的原因。

SCM 的图形表示(归作者所有)

从这个描述中,我们可以消除供应链管理模型的许多微妙之处:

  • a 和 B 没有接*边,所以它们是外生变量
  • c 有两个接*的边,所以它是一个内生变量
  • C 有 An 和 B 两个直接原因,或者它倾向于被阐明为 C 的价值依赖于 An 和 B 的上侧并且 fc=f(A,B)

SCM 图形版本的制定(归作者所有)

1.2.1.何时应用结构因果模型

SCM 是两种情况下的最佳决策:

  • 互动场合发生在总的层面,如地区、州、领地。
  • 单个治疗病例和几个对照病例。

1.3.结构因果模型的好处

使用供应链管理有以下三个好处。

  • 它在 0 和 1 之间分配重要性权重,从而防止外推。外推法意味着我们没有将载荷限制在 0 到 1 的范围内,然而假设重量保持在 100%之外似乎是不合理的。
  • 它铺开了选择规则,明确了每个给予者的普遍意义。
  • 工程控制案例看起来与处理案例非常相似,实际上相当。

结论

因果图,SCMs 基于图表的伙伴对专家也有帮助;他们利用感知以及图表假设的使用来进行因果推断。

每当一个任意的指定在审查中没有被使用,单元可能被有意地分配给条件。总的来说,在两次聚会之间直接检查正常分数可能无法公正地衡量治疗效果。

非常感谢您的提问和评论!

参考

  1. 理解和误解随机对照试验
  2. 相关性并不意味着因果关系
  3. 因果推理导论
  4. 有向无环图
  5. 时间的方向,赖欣巴哈

CFA 论文综述:基于耦合超球的特征适应用于面向目标的异常定位

原文:https://towardsdatascience.com/cfa-paper-review-coupled-hypersphere-based-feature-adaptation-for-target-oriented-anomaly-c3b46316266b

一种新颖的异常定位方法,产生适应于目标数据集的特征并采用迁移学习

妮可·阿瓦里亚诺在 Unsplash 上的照片

本文是故事【T4 论文综述:通过修复进行视觉异常检测的重建和 GANomaly 论文综述:通过对抗训练进行半监督异常检测的延续。在之前的文章中,我介绍了基于重建的方法来识别图像中的异常。诸如自动编码器和生成对抗网络的模型可以用于检测图像中的异常。它们是如何工作的?

它们在训练期间只编码和重建正常图像。在评估中,假设这些模型不应该很好地重建异常图像,因为它们在训练期间没有被馈送,然后,缺陷图像应该比无缺陷图像具有更高的异常分数。然而,这项任务对于复杂的数据集来说是具有挑战性的,并且有时这些类型的方法可以对异常图像产生良好的重建结果,从而导致无法区分异常图像和正常图像。

出于这个原因,我研究了其他类型的异常检测方法,并发现了一种新的方法,称为基于耦合超球的特征适应 (CFA)。这种方法背后有两个主要思想:

  • 它利用预先训练好的 CNN 来提取斑块的特征
  • 它采用迁移学习来使特征适应目标数据集,因此,异常特征可以与正常特征清楚地区分。

在这篇文章中,我将回顾介绍这种新颖的异常检测模型的文章。

概述

  1. 要求
  2. CFA 概述
  3. 实验设置
  4. 量化结果
  5. 定性结果

1.要求

当你阅读一篇论文时,总有一些概念是想当然的,是深入理解作品所必需的。如果您不知道这些术语中的某一个,我建议您看看这一部分:

  • 铲子
  • 转移学习
  • 视觉异常检测

黑桃的例子。在评估过程中,SPADE 提取测试图像在每个像素位置的特征,通过 K 个最*邻找到测试图像与正常图像之间的差异,并返回预测的掩模。

语义金字塔异常检测(SPADE)是一种异常检测方法,它使用预训练的 CNN,如 ResNet-18 和 Wide ResNet-50,来提取有意义的特征[2]。与 CFA 不同,这种方法利用 ImageNet 上的预训练 CNN,而无需学习目标数据集,目标数据集可以具有与馈送给预训练 CNN 的数据集完全不同的分布。

SPADE 包含 3 个不同的阶段来解决异常检测任务:

  1. 预训练的 CNN 从目标数据集中提取特征。
  2. 第二阶段使用 KNN从每个测试图像的训练集中检索 K 个最*的正常图像。使用从训练数据集中提取的表示正态性的特征和测试图像的提取特征之间的欧几里德度量来计算距离。
  3. 第三阶段找到目标和正常图像之间密集的像素级对应关系。如果目标图像区域不存在与第二阶段中检索的正常图像的*似匹配,则标记为异常。

迁移学习

迁移学习是一个深度学习研究领域,专注于应用以前在一个领域获得的知识来解决一个不同但相关的任务。例如,您可以使用预先训练的 CNN,如之前在 ImageNet 上训练的 ResNet,将图像分类到猫和狗的类别中。

视觉异常检测

视觉异常检测是结合了计算机视觉和异常检测的机器学习领域中的一个重要问题[3]。它可以进一步分为两个不同的类别:

  • 图像级异常检测仅尝试了解整幅图像是异常还是正常。
  • 像素级异常检测定位图像内的异常区域。由于这个原因,它通常被称为异常定位。

2。CFA 概述

基于耦合超球的特征自适应(CFA)是一种结合特征提取器和迁移学习的异常定位方法。事实上,它利用迁移学习的原理来创建更健壮和概括的特征,这些特征允许确定目标数据集的输入图像是否异常。

以前的工作只使用预训练的 CNN 而不使用迁移学习,如 SPADE,Padim 和 PatchCore,使用大型数据集,如 ImageNet,取得了非常好的性能。然而,当目标数据集与 ImageNet 完全不同,并且中间层中生成的要素因此有偏差时,这可能是一个挑战。这种方法还有另外两个主要贡献:

  • 基于软边界回归,提出了一种新的损失函数,它搜索具有最小半径的超球来聚类正常特征。因此,它允许片描述符提取区别特征,然后,异常特征可以与正常特征清楚地区分。
  • 可扩展 存储体的压缩与目标数据集的大小无关。它提供了三个好处:减轻了异常特征被高估的正态性的风险,实现了空间复杂性的效率。

基于耦合超球的特征自适应

通过结合基于超球的损失函数和记忆库,解决了预训练 CNN 的偏差问题。它尝试使用 K-means 算法提取聚类特征 φ(pt ),因为正常特征对于区分异常特征很重要。

Att Loss [1]。

其中 K 是与面向目标的特征匹配的最*邻的数量,D 是距离度量。因此,CFA 允许通过优化面向目标的特征φ(pt)的参数来进行特征自适应,以通过迁移学习来最小化损失 L_{att}。

为了避免高估异常特征的正态性,定义了附加损失。为了解决这个问题,硬否定特征被用于执行对比监督,导致更具判别力的φ(pt)。硬负特征被定义为 pt 的第 K+j 个最*邻 ct^j。损失 L_{rep}对比监督φ(),使得以 ct^j 为中心创建的超球面排斥 pt。

代表损失[1]。

其中 J 是用于对比监督的硬负面特征的总数,α是控制这两个损失 L_{att}和 L_{rep}之间*衡的超参数。因此,这两个损失合并成一个独特的损失:

将前面两个损失合并成一个唯一的损失[1]。

存储体压缩

目标是构建一个有效的记忆库。首先,通过将 K-means 聚类应用于从训练集 x 的第一个正常样本 x0 获得的所有特征来构建初始记忆库 C0。

  • 推断第个正常样本并从先前的存储体 C_{i-1}中搜索最*的面片特征集合
  • 通过 Ci^{NN}和 C_{i-1}的指数移动*均(EMA)来计算下一状态 Ci 的第个存储体

通过对训练集的所有正常样本重复上述过程|X|次,获得最终的记忆库 C。

算法与内存条的压缩过程[1]。

我们可以注意到,与其他基于特征提取器而没有迁移学习的方法相比,空间复杂度降低了。特别是,它不依赖于目标数据集|X|的大小。

记忆库建模和记忆库大小的空间复杂度估计[1]。

评分功能

使用面向目标的特征φ(pt)和记忆的特征之间的最小距离来定义异常分数。

异常分数的定义[1]。

然而,正常特征的聚类之间的边界并不清楚,并且很难用朴素异常分数精确地区分异常特征。为此,提出了一种新的评分函数来考虑φ(pt)的确定性。φ(pt)匹配得越多,与其他记忆特征相比,到特定记忆特征的距离就越短。Softmin 用于衡量最*的 c 与另一个 c 相比有多接*。

新颖的评分函数[1]。

在评估新方法 CFA 的过程中,我们可以获得异常得分图,该异常得分图构成了异常定位的最终输出。

3.实验设置

有两个数据集被认为是评估新方法的基准:MVTec AD 和 RD-MVTec 数据集。虽然 MVTec AD 是一个新颖而全面的工业数据集,包含 5354 幅高分辨率图像,分为 15 个类别,但 RD-MVTec 只是 MVTec AD 的一个副本,样本未经比对。RD-MVTec 的图像在+/-10 度范围内随机旋转。转换后,样本大小调整为 256x256,并随机裁剪为 224x224。

使用受试者操作曲线下面积(AU-ROC)作为度量来评估性能。图像级 AUROC 用于评估模型异常检测的性能,而像素级 AUCROC 用于评估异常定位的性能。

使用 ImageNet 上所有预训练的 CNN 执行实验,其中从每个预训练的 CNN 的中间层{C2、C3、C4}提取特征图。1x1 CoordConv 被认为是补丁描述符,它被训练 30 个时期。

4.定量结果

表 1 和表 2 分别显示了不同异常定位方法在 MVTec AD 数据集和 RD-MVTec AD 数据集上的性能。

  • 当考虑 MVTec AD 数据集上的所有类时,CFA++呈现比 CFLOW 稍低的像素级 AUROC 分数。但是应该注意,它利用具有较小空间复杂度的存储体获得了良好的性能。
  • 在 RD-MVTec AD 数据集上,异常定位方法的性能低于 MVTec AD 数据集的性能。特别是,SPADE 似乎对图像的旋转更敏感,这大大降低了它的 AUROC 分数。

表 MVTec AD 数据集上不同异常定位方法的图像级 AUROC 和像素级 au pro[1]。

表 2:RD-mv tec AD 数据集上不同异常定位方法的图像级 AUROC 和像素级 au pro[1]。

在表 3 中,通过在 MVTec AD 数据集上可视化每个类别的图像级 ROCAUC 得分,CFA++的性能更加出色。值得注意的是,由于特性适应目标数据集的影响,CFA++在类级别上的性能优于所有其他方法,而在一次处理一个类时,CFLOW 的性能低于 CFA++。

表 MVTec AD 数据集上每个类的图像级 AUROC。

表 MVTec AD 数据集上不同预训练 CNN 的图像级/像素级 ROCAUC 得分[1]。

表 4 允许将异常检测/定位的性能与预训练的 CNN 进行比较。当使用 eff net-B5 和 ResNet18 作为特征提取器时,CFA++获得了最高的性能。

5.定性结果

定性结果对于解释由 CFA 产生的特征是否能够区分正常图像和异常图像是重要的。下图显示了两个示例的补片特征的异常分数,一个是容易识别缺陷的瓶子,另一个是具有更具挑战性异常的电缆。

在异常分值中,红色代表异常分值。该可视化想要强调在提取的特征上应用或不应用迁移学习时的差异:

  • 当在没有迁移学习的情况下获得特征时,正常特征的正态性被低估,并且具有与异常特征相似的分数。然后,很难识别两个特征之间的差异,因为基于异常分数的边界是模糊的,不是很清楚(第二列—有偏差)
  • 当在之后使用迁移学习获得面向目标的特征时,它们被很好地聚类,如图 1 的第三列所示。然而,单独的聚类不足以精确地对疑难病例的不确定异常特征进行评分。本文提出的评分函数通过考虑确定性来计算异常得分。通过这种方式,我们能够在困难的情况下将异常特征与正常特征区分开来。

图 1:每个斑块特征的异常分数的 2D 可视化,其中三角形表示记忆的特征,虚线圆圈表示异常特征的区域[1]。

下面是异常定位结果,显示由 CFA 识别的异常区域。

图 2:原始图像 VS 地面实况 VS 预测热图 VS 分割结果[1]。

外卖食品

我希望你喜欢 CFA 的这篇评论。正如我不厌其烦地重复的那样,异常检测是一个需要解决的具有挑战性的问题,需要对这些方法进行概述,以了解哪种方法最适合特定的环境。

在上一篇文章中,我建议你看一下解释 Skip-GANomalyAnoGAN 的论文。在这篇文章中,我建议你阅读关于https://arxiv.org/pdf/2005.02357v3.pdfPaDiMcf flowFastFlow 的论文。所有这些方法的共同点在于,它们利用预先训练的 CNN 来检测和定位异常。如果你有其他关于阅读的建议,请告诉我,分享知识是提高的最好方法。感谢阅读。祝您愉快!

参考资料:

[1] CFA:基于耦合超球的特征适应用于面向目标的异常定位,S. Lee,S. Lee 和 B. Cheol Song,(2022)

[2] 具有深金字塔对应关系的子图像异常检测,N. Cohen 和 Y. Hoshen,(2021)

[3] 图像的视觉异常检测:一个系统的综述,杨军,徐,齐,石,(2022)

GitHub 知识库

**https://github.com/sungwool/CFA_for_anomaly_localization **

你喜欢我的文章吗? 成为会员 每天无限获取数据科学新帖!这是一种间接的支持我的方式,不会给你带来任何额外的费用。如果您已经是会员, 订阅 每当我发布新的数据科学和 python 指南时,您都会收到电子邮件!

cGAN:条件生成对抗网络——如何获得对 GAN 输出的控制

原文:https://towardsdatascience.com/cgan-conditional-generative-adversarial-network-how-to-gain-control-over-gan-outputs-b30620bd0cc8

神经网络

用一个详细的 Python 例子解释 cGAN 架构

条件生成对抗网络。图片由作者提供。

介绍

你尝试过生成性对抗网络(GANs)吗?如果是这样,你可能遇到过这样的情况,你想让你的 GAN 产生一个特定类型的数据,但是对 GAN 的输出没有足够的控制。

例如,假设你使用了一系列花卉图像来训练一个能够制作花卉假像的 GAN。虽然您可以使用您的模型生成一个随机的花的图像,但您不能指示它创建一个图像,比如说,一朵郁金香或一朵向日葵。

有条件的 GAN (cGAN)允许我们用类标签等附加信息来调节网络。这意味着在训练过程中,我们将图像和它们的实际标签(玫瑰、郁金香、向日葵等)一起传递给网络。)来了解它们之间的区别。这样,我们就能够要求我们的模型生成特定花朵的图像。

内容

在本文中,我将带您了解以下内容:

  • 条件 GAN (cGAN)在机器学习算法领域中的地位
  • cGAN 和 cDCGAN 架构及其组件概述
  • Python 示例向您展示了如何使用 Keras / Tensorflow 从头开始构建条件 DCGAN

机器学习算法宇宙内的条件甘(cGAN)

虽然大多数类型的神经网络都是受监督的,但有些(如自动编码器)是自我监督的。由于这一点以及它们对机器学习的独特方法,我在我的 ML 宇宙图中给神经网络赋予了它们自己的类别。

由于条件性 GAN 是 GAN 的一种类型,您可以在生成性对抗性网络子类别下找到它。点击👇在下面的交互式图表中,找到 cGAN 并揭示隐藏在 ML 每个分支下的其他算法。

机器学习算法分类。由作者创建的互动图表。

如果你喜欢数据科学和机器学习 ,请 订阅 获取我的新文章邮件。如果你不是中等会员,可以在这里加入https://bit.ly/36Mozgu

cGAN 架构及其组件概述

我们如何调节 GAN?

让我们首先提醒自己一个基本的生成对抗网络架构。

基本 GAN 模型架构。图片由作者提供。

如您所见,我们有两个主要组件:

  • 生成器模型 —生成与问题域相似的新数据(即假数据)。
  • 鉴别器模型 —尝试识别所提供的示例是假的(来自生成器)还是真的(来自实际的数据域)。

在条件性 GAN 的情况下,我们希望对发生器和鉴别器都进行条件化,以便它们知道自己处理的是哪种类型。

假设我们使用 GAN 创建包含伦敦和马德里房价的合成数据。为了使它有条件,我们需要告诉生成器每次生成哪个城市的数据。我们还需要通知鉴别器传递给它的例子是伦敦的还是马德里的。

因此,有条件的 GAN 模型架构如下所示:

条件 GAN (cGAN)模型架构。图片由作者提供。

请注意,我们可以根据多种类型的输入来调节 gan。例如,我们还可以在其他图像上调节网络,以便为图像到图像转换创建 GAN(例如,将白天的图像转换为夜间的图像)。

条件深度卷积 GAN (cDCGAN)

与之前的 flower 示例一样,我们可能希望对深度卷积 GAN 进行调整,以便我们可以要求模型生成特定类型的图像。

下面是一个条件 DCGAN 的模型架构图。请注意,除了生成器和鉴别器包含额外的层,如卷积转置卷积之外,高级架构与前面的示例基本相同。

条件深度卷积生成对抗网络。图片来自作者

**

Python 示例

在本例中,我将向您展示如何构建上图所示的 cDCGAN。它将使我们能够生成类似于 MNIST 数据集中的“假”手写数字。

由于我们正在构建一个有条件的 GAN,我们将能够指定希望生成器每次生成哪个数字(0–9)。

设置

我们需要获得以下数据和库:

让我们导入库:

上面的代码打印了本例中使用的包版本:

*Tensorflow/Keras: 2.7.0
numpy: 1.21.4
matplotlib: 3.5.1
graphviz: 0.19.1*

接下来,我们加载 MNIST 数字数据,这在 Keras 数据集中是可用的。

上面的代码显示了前十位数字及其标签。

MNIST 训练数据集中的前十个数字。图片由作者提供。

创建有条件的 DCGAN 模型

完成数据准备后,让我们定义和组装我们的模型。请注意,我们将使用 Keras Functional API ,这比顺序 API 给了我们更多的灵活性,允许我们创建复杂的网络架构。

我们将从发电机开始:

发电机模型图。图片来自作者

我们有两个输入到一个发电机模型。第一个是 100 节点的潜在向量,它是我们模型的种子,第二个是标签(0–9)。

潜在向量和标签在通过网络的其余部分之前被整形和连接,转置卷积层将数据放大到所需的大小(28 x 28 像素)。

接下来,让我们定义一个鉴别器模型:

鉴别器模型图。图片由作者提供。

鉴频器也有两个独立的输入。一个是标签,另一个是图像——要么是来自 MNSIT 数据集的真实图像,要么是由生成器模型创建的虚假图像。

输入被组合并通过网络传递。卷积层和最大池层在输出层进行预测(真实/虚假)之前提取要素并减小大小。

让我们结合生成器和鉴别器来创建一个条件深度卷积对抗网络(cDCGAN)。下面代码中的一个关键细节是我们使鉴别器模型不可训练。我们这样做是因为我们希望使用真实和虚假(生成)数据的组合来分别训练鉴别器。稍后您将看到我们是如何做到这一点的。

cDCGAN 模型图。图片由作者提供。

注意生成器和鉴别器如何使用相同的标签作为输入。

为发生器和鉴别器准备输入

我们将创建三个简单的函数来帮助我们为这两个模型采样/生成数据。

  • 第一个函数从训练数据中采样真实图像和标签;
  • 第二个函数从潜在空间中提取随机向量,以及随机标签作为生成器的输入;
  • 最后,第三个函数将潜在变量和标签传递到生成器模型中,以生成假的示例。

模型培训和评估

最后两个函数将帮助我们训练模型并显示中间结果(以指定的时间间隔),因此我们可以观察生成器如何随着时间的推移而改进。

让我们先创建一个函数来显示中期结果:

最后,让我们定义训练函数:

现在我们可以调用我们的训练函数,喝点茶,让计算机完成剩下的工作😃

*train(gen_model, dis_model, gan_model, data, y_train, latent_dim)*

结果

当生成器和鉴别器相互竞争,我们可以跟踪他们的进展。

以下是生成器创建手写数字的一些早期尝试:

生成器创建手写数字的早期尝试。图片由作者提供。

时代 5 的一些进展:

第 5 纪元对生成器结果的改进。图片由作者提供。

通过 Epoch 10 的假图像,生成器在整个训练过程中继续变得更好,如下所示:

通过 Epoch 10 改进了生成器结果。图片由作者提供。

模型训练完成后,我们可以保存发电机部分以备将来使用。

下面是一个示例,说明我们如何加载模型并让它生成带有特定标签的图像:

用我们的 cDCGAN 模型生成的“假”手写数字。图片由作者提供。

正如您所看到的,结果并不完美,但我们可以通过更长时间地训练模型来进一步改进它们。

结束语

我希望我的解释和例子足够清楚。无论哪种方式,如果您有任何问题或建议,请不要犹豫留下评论。上面 Python 代码的完整 Jupyter 笔记本可以在我的 GitHub 库 找到。

此外,请随时查看我在 Medium/TDS 上的其他 GAN 和神经网络文章:

如果你想收到我即将发表的关于机器学习和神经网络的文章,请用你的电子邮件订阅,一旦我发表了这些文章,它们就会马上到达你的收件箱。

干杯!🤓
索尔·多比拉斯

我加入媒体的个性化链接:

*https://bit.ly/3J6StZI *

数据科学家每天面临的挑战

原文:https://towardsdatascience.com/challenges-data-scientists-face-everyday-344bb18fba84

Unsplash 上由 Boitumelo Phetla 拍摄的照片

数据科学机器学习现在是互联网上的热门词汇,而且这种趋势还在增长。随着各种格式的大量数据,公司越来越依赖于数据科学家、机器学习工程师和软件开发人员来自动化各种日常任务的流程,并提高短期和长期运营的生产率效率。此外,数据科学家和 ML 工程师的工资也随着良好的薪酬和股票福利进一步增加。

Markus SpiskeUnsplash 上拍摄的照片

然而,也应该考虑到数据科学家在工作中经常面临许多挑战,从数据提取到大规模部署最佳超参数调整模型。因此,意识到这些挑战并学习如何应对它们会对轻松完成工作产生重大影响。下面重点介绍了数据科学家在工作中面临的一些挑战,以及应对这些挑战的一些技巧和策略。

数据在任何地方都可以以各种格式获得,例如以文本、视频、音频、图片和网站的形式。根据 seedscientific.com 提供的估计,在 2020 年的黎明,世界上可用的数据量是惊人的 440 亿字节。今年的数字甚至更高,而且在未来也会有增长的趋势。有了这些庞大的信息,通过分析趋势和了解预测来充分利用这些信息对公司来说是很方便的,这样他们就可以采取适当的措施来确保他们朝着正确的方向前进并获得利润。

了解了下面详细介绍的挑战后,数据科学家可以收集应对挑战所需的所有工具和资源,并为公司做出有益的贡献。

寻找正确的数据

安德烈·德·森蒂斯峰在 Unsplash 上拍摄的照片

拥有大量数据的挑战是找到团队可以使用的正确数据,以便他们从中产生有价值的模式和见解。重要的是要问一些问题,如谁应该得到什么数据,是否应该有持续的数据流用于分析,或者数据是否是固定的。问这些有趣的问题可以使数据科学工作流的任务变得轻松,同时使系统设计变得不那么繁琐和易于遵循。

可能存在包含大量影响机器学习模型性能的异常值缺失值不准确信息的数据。因此,对数据进行预处理也很重要,这样模型就能以最佳方式高效运行,同时性能也有很大提高。

数据准备

Towfiqu barbhuiyaUnsplash 上拍摄的照片

数据科学家必须考虑的挑战之一是准备大量的数据,并使其可供团队的其他成员访问和解释,同时提供他们自己有用的见解和模式。预处理数据也有助于增加其可读性,以便团队中的其他成员可以查看数据中的特征。有些情况下,数据中的各种特征可能存在异常值,必须将其视为异常值,因为并非所有的机器学习模型都对这些异常值具有鲁棒性。除此之外,还可能存在包含缺失值或不正确值的特征,这些特征必须被识别,以便它们不会降低准备在生产中部署的 ML 模型的性能。所有这些事情都可以在探索性数据分析 (EDA)的帮助下确定,这通常是处理大量数据时机器学习的第一步。因此,最初必须遵循这一步骤,以确保我们分别从我们的模型中获得最佳结果。

选择正确的绩效指标

安德烈·德·森蒂斯峰在 Unsplash 上拍摄的照片

由于机器学习中有大量可用的指标,因此可能会陷入循环,而无法决定可用于评估的最佳工具或指标。对于分类问题,我们有流行的度量标准,如准确度精确度召回f1-分数以及其他。

对于回归任务,我们必须考虑其他指标,例如均方误差*均绝对误差。在时间序列问题的情况下,这也主要是一个回归任务,我们采取其他指标,如*均绝对百分比误差 (MAPE)或均方根误差。因此,选择正确的指标可能是数据科学家或机器学习工程师必须应对的挑战,以提高工作效率,并确保公司通过这种分析获得最佳结果。

部署

照片由戴恩·托普金Unsplash 上拍摄

在获取数据并对其进行预处理并确保它在交叉验证数据上表现良好之后,现在是时候部署它并将其投入生产了。毕竟,如果模型只是给出正确的预测,而没有显示测试数据或以前没有见过的数据的结果,这将是没有用的。因此,还应该考虑在生产中部署模型。

有时,在尝试实时部署模型时,还应该考虑用于运行这些模型的基础设施。如果我们想要一个在互联网应用中广泛使用的低延迟系统,选择能够快速给出结果的 ML 模型可能是一个值得考虑的好事情。还有其他系统对延迟的要求可能没有这么严格。一些应用涉及电影的网飞推荐系统。在这个系统中,并不总是需要在很短的时间内给出建议。该模型可能需要一两天的时间从感兴趣的特定用户以及其他用户那里收集更多的见解,然后提出可靠的推荐。因此,在部署之前考虑手头的业务问题是必要的。

性能监控

照片由 Ahmed ZayanUnsplash 上拍摄

作为一名机器学习工程师,监控模型在生产中的性能非常重要。就项目的延迟、效率和范围而言,总有改进的余地。还可能出现这样的情况:模型变得不正常,或者基于新数据产生扭曲的结果。因此,对模型的不断监控和再训练可能是机器学习工程师必须处理的挑战之一。

减少数据的维度也可以是监控系统性能的好步骤,并且根据 ML 问题是分类问题还是回归问题来查看准确性或均方误差是否有大的降低。

结论

总而言之,我们已经看到了如何使用机器学习以及与机器学习工作流相关的挑战。看一看这些挑战,数据科学家可以确保他们拥有正确的工具和资源来应对这些挑战,并为公司提供有价值的见解。

如果你想进一步了解我的工作,下面是我们可以联系的细节,你也可以查看我的工作。谢了。

GitHub:https://github.com/suhasmaddali

领英:https://www.linkedin.com/in/suhas-maddali/

https://www.facebook.com/suhas.maddali脸书

如何更改 Matplotlib 轴上的刻度频率

原文:https://towardsdatascience.com/change-tick-frequency-matplotlib-axis-6af2c6bce1ea

在 Python 中更改 Matplotlib 图的 x 轴和 y 轴的刻度频率值

照片由艾萨克·史密斯Unsplash 上拍摄

在我看来,数据可视化是讲述故事最重要的方面。数据团队通常会投入大量的精力来构建可扩展的数据管道,执行复杂的分析并构建机器学习模型,但最终这一切都归结于交流和讲述故事。换句话说,你如何让非技术人员获得和理解这些发现?

Matplotlib 是 Python 中用于可视化数据点的事实上的库。在今天的简短教程中,我们将演示几种不同的方法,帮助您调整图中 X 轴和/或 Y 轴上的刻度频率。这个频率通常会控制数据点在图表中的显示方式,因此正确设置它非常重要。

首先,让我们用一些虚拟数据点用matplotlib和 Python 创建一个示例图:

**import** matplotlib.pyplot **as** pltx = [20, 50, 150, 202, 358, 500, 879]
y = [10, 20, 30, 40, 50, 60, 70]plt.plot(x, y)
plt.ylabel('y-axis')
plt.xlabel('x-axis')
plt.title('Example plot')
plt.show()

上述代码片段将生成如下所示的图:

示例情节—来源:作者

调整 matplotlib 图中轴的刻度频率

我们可以通过调用接受特定 x 轴刻度位置列表的[matplotlib.pyplot.xticks](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.xticks.html)方法来明确指定 x 轴上的刻度频率。

因此,我们首先需要准备一个列表,指出 x 轴上记号可见的具体位置。为此,我们可以使用[numpy.arange](https://numpy.org/doc/stable/reference/generated/numpy.arange.html)方法,该方法可用于返回给定区间内均匀分布的值。

在我们的示例中,假设我们希望看到 x 轴上从值0开始到值900的刻度,步长为150。这将被翻译成

plt.xticks(np.arange(0, 900, 150))

完整的代码将变成:

import matplotlib.pyplot as plt
import numpy as npx = [20, 50, 150, 202, 358, 500, 879]
y = [10, 20, 30, 40, 50, 60, 70]plt.plot(x, y)
plt.ylabel('y-axis')
plt.xlabel('x-axis')
plt.title('Example plot')
plt.xticks(np.arange(0, 900, 150))
plt.show()

由此产生的图分享如下:

用 x 轴上更新的分笔成交点频率绘图—来源:作者

同样,我们可以调用plt.yticks()方法来相应地调整 y 轴的节拍频率。假设我们希望看到从 y 值的最小值到最大值的分笔成交点,步进频率为5。下面的表达式可以解决这个问题:

plt.yticks(np.arange(min(y), max(y) + 5, 5))

截取的完整代码将变成

import matplotlib.pyplot as plt
import numpy as npx = [20, 50, 150, 202, 358, 500, 879]
y = [10, 20, 30, 40, 50, 60, 70]plt.plot(x, y)
plt.ylabel('y-axis')
plt.xlabel('x-axis')
plt.title('Example plot')
plt.xticks(np.arange(0, 900, 150))
plt.yticks(np.arange(min(y), max(y) + 5, 5))
plt.show()

由此产生的图分享如下:

更新了图中 y 轴上的刻度频率—来源:作者

最后的想法

最终,一切都归结于数据可视化和故事讲述。你可以建立最好的数据管道,应用最佳实践,进行出色的分析——但如果你不能可视化并恰当地传达发现,那么这些都不重要。

因此,作为这一过程的一部分,重要的是要确保你能够正确地将数据可视化,并将其转化为有意义的图表,供想要了解任何见解的读者使用。

在今天的简短教程中,我们演示了几种不同的方法,它们将帮助您在使用 Python 创建的 matplotlib 图和绘图上调整 X 轴和 Y 轴上的刻度频率。正如我们在一些实际例子中看到的,这种配置可以帮助你根据你想要可视化的内容来放大或缩小你的图形。

成为会员 阅读介质上的每一个故事。你的会员费直接支持我和你看的其他作家。你也可以在媒体上看到所有的故事。

https://gmyrianthous.medium.com/membership

相关文章你可能也喜欢

单词嵌入的特征

原文:https://towardsdatascience.com/characteristics-of-word-embeddings-59d8978b5c02

单词嵌入入门

反义词的问题

照片由精心设计Unsplash 上拍摄

本文是 6ᵗʰ系列文章中关于单词嵌入的入门: 1。word 2 vec 后面有什么 | 2。单词成向量 |
3。统计学习理论 | 4。word 2 vec 分类器 |
5。word 2 vec 超参数 | 6。单词嵌入的特征

在上一篇文章Word2vec 超参数 中,我们通过了解 word 2 vec 算法的文本预处理建议、重新加权技术和优化设置,完成了对它的研究。

在本文中,我们将评估典型单词嵌入的优缺点,特别是来自 Word2vec 的那些。

词义和词义消歧

Manning 和 Schütze 的名为自然语言处理统计基础的书专门用了 36 页的章节来讨论词义消歧(Manning 和 Schütze,1999)。这是因为多义词,即具有多个含义的词的处理长期以来被认为是 NLP 中的一个重要问题。

1998 年,斯特蒂娜和高娜通过 WordNet 使用语义标记计算了当今美国英语布朗大学标准语料库(布朗语料库)中每个单词的*均词义数量,从而量化了这个问题的程度。他们发现每个单词*均有 5.8 个意思。他们还量化了语料库中具有多种含义的单词的百分比,如下所示:

虽然词典中的大多数单词都是单义的,但在演讲和文章中出现频率最高的却是多义词。例如,WordNet 中超过 80%的单词是单义的,但是在测试的语料库中几乎 78%的实词有不止一个义项。(斯特蒂娜和高娜,1998)

然而,由 Word2vec 产生的单词嵌入即使对于多义词也能很好地运行。正如 Neelakantan 等人(2014 年)所解释的,原因是“在适度高维空间中,一个向量可以一次相对‘接*’多个区域。”然而,由和 Mooney 在 2010 年以及黄等人在 2012 年发表的早期论文证明了当在单个词嵌入上使用聚类技术开发多个向量原型时对词向量表示的改进。

根据 Ruas 等人在 2019 年发表的一篇论文,该论文对多义词嵌入的历史发展进行了广泛的回顾,关于 Word2vec 的多协议类型嵌入的论文数量很少。事实上,只有两篇论文使用了 Skip-gram,这两篇论文都发表于 2014 年,第一篇由 Tian 等人发表,第二篇由 Neelakantan 等人发表。Tian 等人(2014 年)设计了一种使用 Skip-gram 和分层 softmax 的期望最大化算法,Neelakantan 等人(2014 年)提出了一种 Skip-gram 的扩展,该扩展使用上下文词*均在 SGNS 训练期间区分多个义项。两种方法都显示出有希望的结果。

Ruas 等人还讨论了其他研究人员的工作,这些工作整合了结构化信息,如来自词汇数据库的词性,如 WordNet 以帮助每个词向量的意义表示。Ruas 等人(2019)将其他研究人员的这些发现与他们自己使用词汇数据库为每个词义创建单独向量的方法进行了对比。

2015 年,李和朱拉夫斯基断言,“为一个歧义词的每一个意义获得一个不同的表示可以导致更强大和更细粒度的向量空间表示模型。”然而,在他们自己的关于何时证明多义词嵌入合理的模型的结论中,他们指出:“我们的结果表明,简单地增加基线跳格嵌入的维度有时足以实现与使用多义嵌入相同的性能优势”(李和 Jurafsky,2015)。尽管如此,Gladkova 和 Drozd 在他们 2016 年关于评估单词嵌入的论文中,假设一词多义如此重要,以至于它是“房间里的大象”(Gladkova 和 Drozd,2016)。

事实上,对多协议类型嵌入需求的一些模糊性来自于对如何衡量成功缺乏共识。Dubossarsky 等人在 2018 年反对一般意义上的特定向量,因为它们的好处通常是使用单词相似性任务来衡量的,这可能导致假阳性结论(Dubossarsky 等人,2018)。

单词嵌入向量的结构

Word2vec 的训练算法涉及一个对数线性函数,我们在本系列的第四篇文章 中介绍过 Word2vec 分类器 。虽然目标函数的最小值是非凸的,因为目标和上下文单词的表示 tc 是共同学习的(Goldberg 和 Levy,2014),但是在嵌入中观察到线性关系。这些线性关系是 Arora 等人 2018 年论文的主题,该论文考虑了词嵌入中词义的线性代数结构。Arora 等人提供了理论和实验结果,表明一个多义词的向量本质上是独立意义向量的加权*均值(Arora 等人,2018)。这一发现可能与本系列第一篇文章中介绍的向量数学有关,并以国王男人+女人≈王后为例进行了说明。

单词嵌入的线性关系也支持从向量中归纳单词含义的能力(Arora 等人,2018;穆等,2016)。它们还可能有助于尝试找到一种统计测试,以确定何时需要多义词嵌入的临界值(Oomoto 等人,2017 年)。

多义词向量的另一个特征是它们的大小。在本系列的第二篇文章中, 单词成向量 ,当描述为什么同相似度优于其他距离度量时,我们注意到更频繁的单词往往具有更大幅度的向量,或 L 范数。这种趋势在向量空间模型中通常是真实的,但是当应用有限的上下文窗口(如 Word2vec 所使用的)时,还有其他因素。

Schakel 和 Wilson 在 2015 年的论文中证明,对于固定的上下文窗口,向量大小实际上随着频率而增加,但对于出现在多个上下文中的单词,向量大小往往会缩小,因为多义性将向量值拉向多个方向。基于这一观察,Schakel 和 Wilson 讨论了一个单词的“重要性”,他们使用向量幅度与单词频率的比率来衡量与其他以相同频率出现的单词的关系。一般来说,多义词往往有较小的向量幅度,给定其整体词频(Schakel 和 Wilson,2015)。

反义词和单词嵌入

即使单词嵌入相当好地完成了单词相似性任务和类似于国王男人+女人≈王后的类比,它们也有缺点。对于同义词,单词嵌入通常执行正确,但是对于多义词,执行不太好。

此外,单词嵌入不区分反义词。他们对待反义词更像同义词,因为反义词往往出现在相同的语境中,因为他们的对立面(雷辛格和穆尼,2010;Turney 和 Pantel,2010 年)。例如,以下对立短语通常在附*文本的内容中差别很小:

确认 预约。
取消了 的预订。

大多数区分反义词的方法依赖于词汇数据库。然而,一些研究者,最著名的是 Samenko 等人,已经找到了在标准单词嵌入本身中寻找反义词信息痕迹的方法。

克服单词嵌入中的反义词的挑战的技术在继续发展。例如,2021 年由 Qalaxia 发布的下图显示了一个扩展的更大的类比测试集( BATS )反义词测试集的令人印象深刻的结果:

反义词在扩展蝙蝠反义词测试集
上的表现(作者卡拉夏,2021,经许可转载)

Samenko 等人对反义词这一主题进行了综述(Samenko 等人,2020)。

超越 Word2vec

自 2013 年 Word2vec 发布以来,研究人员积极地继续开发其他算法来改善单词嵌入。像 Word2vec 一样“肤浅”且值得更详细讨论的两个重要算法是 GloVe 和 fastText。

手套是由斯坦福大学的一组研究人员在 2014 年发明的。GloVe 代表“全局向量”,它基于全局单词-单词共现统计,使用类似于本系列第二篇文章中讨论的降维技术的技术, 单词到向量 ,其中数据被收集,然后降维。GloVe 的技术产生的单词嵌入具有与 Word2vec 的嵌入类似的属性,并且在测试时,它产生的结果略好于 word 2 vec(Pennington 等人,2014)。

继 GloVe 之后, fastText 于 2016 年在 Word2vec 的创作者 Mikolov 和脸书的 AI 研究团队(Bojanowski 等人,2016;Joulin 等人,2016)。fastText 不仅将 Word2vec 的训练扩展到单词,还扩展到字母组(称为字符 n-grams ),这允许对训练词汇中不存在的单词进行更准确的建模。

Word2vec、GloVe 和 fastText 是用于创建单词嵌入的三种主要的浅层神经网络算法。

在 GloVe 和 fastText 的开发之后,使用上下文化单词表示的深度学习系统的研究取得了进展。目前,AllenNLP ELMo 、OpenAI 的 GPT 和 Google 的 BERT 是三个使用上下文嵌入的流行深度学习系统(Ethayarajh,2019;Ethayarajh,2020)。在这些系统中,单词嵌入是基于上下文动态更新的,例如周围的句子和其他上下文线索。为了完成他们的 NLP 任务,这三个系统使用深度学习技术,如一种称为长短期记忆(LSTM)的递归神经网络(RNN)和变形金刚( Ghati,2020 )。

随着时间的推移,埃尔莫,GPT 和伯特已经变得非常准确,并导致了重大的自然语言处理应用程序的改进。然而,因为它们是深度神经网络,所以它们是计算密集型的。

摘要

在本文中,我们回顾了由浅层神经网络算法(如 Word2vec、GloVe 和 fastText)生成的单词嵌入的一些特征。这些算法通常工作良好,但受到多义词和反义词的挑战,改进浅层神经网络的研究正在进行中。我们还了解了三个更受欢迎的使用上下文嵌入的深度学习系统,以及它们的优点和缺点。

在这个系列文章中,关于单词嵌入的初级读本,我们研究了单词嵌入是如何包含单词在实际使用时的关系信息的,以及这些关系是如何用数学方法表达的。我们揭示了单词嵌入如何发挥作用并为计算应用程序提供价值,例如搜索引擎和消息应用程序的自动完成功能中的单词预测。

我们了解到,创建单词嵌入所涉及的数据收集和处理深深植根于统计理论。虽然 NLP 中使用的数据不一定是随机的或参数分布的,并且使用了重新加权和压缩技术来将数据转换为实用的信息,但概率和统计理论是 NLP 不可或缺的一部分。

我们还了解了浅层神经网络在创建单词嵌入方面的能力。通过详细研究 Word2vec 学习算法,我们发现并不仅仅是 Word2vec 的新颖算法导致了它的显著成功。Word2vec 特殊的数据处理技术对其有效性和广泛采用至关重要。

最后,我们了解了单词反义词和具有多重含义的单词给 NLP 带来的挑战,以及 NLP 如何通过利用深度学习技术做出响应。

单词嵌入的应用非常广泛:搜索引擎、情感分析、语言翻译、推荐引擎、文本预测、聊天机器人等等。随着如此多的人类交流在网上发生,通常以数字或图像的形式,但更多的是以文本和语音的形式,改进和更好地利用单词嵌入的探索将会继续。我想我们会对未来几年的发展印象深刻。

具有可操作价值的信息(知识)通常以文字的形式在人们之间交流,而书面文本通常是我们尽力总结这些信息的方式— 走向数据科学就是这样一个知识库!

虽然在实践层面上,单词嵌入是统计导出的值,以表示应用中的单词,但在更深的层面上,它们使用语言中单词的关系来阐明意义。

这篇文章是 6ᵗʰ系列文章中关于单词嵌入的初级读本: 1。word 2 vec 背后有什么 | 2。单词成向量 |
3。统计学习理论 | 4。word 2 vec 分类器 |
5。word 2 vec 超参数 | 6。单词嵌入的特征

关于这个主题的更多信息:我推荐的一个了解更多关于单词嵌入属性的资源是一篇在 ICML 2019 年获得最佳论文荣誉奖的论文:Allen,c .和 Hospedales,T. (2019)。类比解释:理解单词嵌入。在机器学习国际会议上。可从 arXiv:1901.09813 获得。

如果你想了解更多关于语境嵌入,Ghati 的文章《伯特、GPT-2、埃尔莫之间的比较》(中)是一个很好的介绍。

如果您想开始使用 Word2vec 算法,Chia 的“使用 NumPy 和 Google Sheets 的 Word2Vec 实现指南”包括一个可下载的 Python 脚本,它很容易设置,并且有一些图表有助于描述向量和矩阵数学,尽管实现非常基础。或者, PyTorch 版本的 Word2vec 可以在上找到,走向数据科学,TensorFlow 都有教程。

最后,如果您想要从更大的语料库中创建可用的单词嵌入,或者使用一组预训练的嵌入启动应用程序, Gensim 具有 Word2vec 和 fastText 的完整实现,并使用 Python 接口在 C 中进行了优化。您还可以从 Word2vec、fastText 和 GloVe online 中找到标准的预训练嵌入。

参考

Arora,s .,Li,y .,Liang,y .,Ma,t .,和 Risteski,A. (2018 年)。词义的线性代数结构及其在多义词中的应用。计算语言学协会汇刊,6:483–495。可在 arXiv:1601.03764v6 获得。

Bojanowski,p .,Grave,e .,Joulin,a .,和 Mikolov,T. (2016 年)。用子词信息丰富词向量。可在 arXiv:1607.04606 获得。

Chia,D. (2018)。使用 NumPy 和 Google Sheets 的 Word2Vec 实现指南。走向数据科学。

Dubossarsky、e . Grossman 和 d . wein shall(2018 年)。多义词研究中的控制和评价集。自然语言处理经验方法会议论文集,第 1732-1740 页。计算语言学协会。可在 doi10.18653/v1/D18–1200处获得。

Ethayarajh,K. (2019)。语境化的单词表征有多语境化?比较伯特、埃尔莫和 GPT-2 嵌入的几何学。2019 自然语言处理经验方法会议暨第九届国际自然语言处理联合会议论文集,第 55–65 页。 PDF

Ethayarajh,K. (2020 年)。伯特、埃尔莫和 GPT-2:语境化的词汇表征有多语境化? Github

Ghati,G. (2020)。伯特、GPT-2 和埃尔莫的比较。中等

Gladkova,a .和 Drozd,A. (2016)。单词嵌入的内在评估:我们能做得更好吗?评估自然语言处理矢量空间表示的第一次研讨会会议记录,第 36–42 页。计算语言学协会。可在 doi10.18653/v1/W16–2507处获得。

Goldberg 和 o . Levy(2014 年)。word2vec 解释:推导 Mikolov 等人的负采样单词嵌入法。可从 arXiv:1402.3722v1 获得。

黄(e .)、索赫尔(r .)、曼宁(c .)和吴(a .)(2012 年)。通过全局上下文和多个单词原型改进单词表示。计算语言学协会第 50 届年会会议记录,第 873-882 页。 PDF

Joulin,a .,Grave,e .,Bojanowski,p .,和 Mikolov,T. (2016 年)。高效文本分类的窍门。可在 arXiv:1607.01759 获得。

李和朱拉夫斯基博士(2015 年)。多义嵌入能提高自然语言理解吗?2015 年自然语言处理经验方法会议论文集,第 1722–1732 页。可在 doi10.18653/v1/D15–1200处获得。

曼宁和舒策(1999 年)。统计自然语言处理基础。麻省剑桥:麻省理工学院出版社。

Mu,j .,Bhat,s .,和 Viswanath,P. (2016 年)。多义的几何学。可从 arXiv:1610.07569v1 获得。

Neelakantan,a .,Shankar,j .,Passos,a .,和 McCallum,A. (2014 年)。向量空间中每个词多重嵌入的有效非参数估计。2014 年自然语言处理经验方法会议论文集,第 1059–1069 页。计算语言学协会。可在 doi10.3115/v1/D14–113处获得。

Oomoto,k .,Oikawa,h .,Yamamoto,e .,Yoshida,m .,Okabe,m .,Umemura,K. (2017)。词义分布式表征中的多义词检测。可从 arXiv:1709.08858v1 获得。

Pennington、r . Socher 和 c . Manning(2014 年)。GloVe:单词表示的全局向量。2014 年自然语言处理经验方法会议论文集,第 1532-1543 页。 PDF

雷辛格和穆尼(2010 年)。词义的多原型向量空间模型。人类语言技术会议录:计算语言学协会北美分会会议,第 109-117 页。 PDF

Ruas、w . gro sky 和 a . Aizawa(2019 年)。通过词义消歧过程实现的多义嵌入。专家系统与应用,136:288–303。在 doi10.1016/j . eswa . 2019 . 06 . 026有售。

Samenko,I .,Tikhonov,a .,Yamshchikov,I. (2020)。同义词和反义词:嵌入冲突。可从 arXiv:2004.12835v1 获得。

a . m . j . schakel 和 b . j . Wilson(2015 年)。使用单词的分布式表示来测量单词的重要性。可从 arXiv:1508.02297v1 获得。

斯蒂纳和高娜(1998 年)。基于完整句子上下文的通用词义消歧方法。**自然语言处理杂志,5 卷 2 期:47–74 页。

特尼博士和潘特尔博士(2010 年)。从频率到意义:语义学的向量空间模型。人工智能研究杂志,37:141–188。 PDF

*除非另有说明,数字和图像均由作者提供。

用虚线绘制图表

原文:https://towardsdatascience.com/charting-with-dash-583b65a22ace

3:使用破折号选择图表以更好地讲述故事

戴红帽的女孩Unsplash 上的照片

简介

数据可视化中最常见的问题之一是试图根据消息的性质建立最合适的图当可视化的目标不仅仅是显示给定时间段内各种定量变量的趋势或变化模式,而是将其与成分分析(组成整体的各个部分是如何相关的)联系起来时,这个问题就更加重要了。最常用的可视化工具是多折线图、堆积条形图和堆积面积图。

当对应于显示的水*轴的时间段具有许多值时,可能难以确定垂直轴上指示的每个数值变量的精确值。然后可以方便地添加第二个图表来显示这些确切的值,以提高消息的质量。在这种情况下,最常用的可视化工具是饼状图、树形图和旭日图。

问题是:对于上述情况,哪种组合可以实现最佳的讲故事效果?

我们将尝试通过使用 Python 框架 Dash 的应用程序来回答这个问题,使用的数据集对应于 2007-2020 年期间的世界生物乙醇产量。

破折号

Dash 是一个开源的免费库,由加拿大蒙特利尔的 IT 公司 Plotly 创建。它是一个 python 框架,用于创建名为 Dash Apps 的 dataviz 应用和仪表盘。Dash 主要基于 ReactJS(前端)、Flask(后端)、Plotly(数据可视化工具)。

Dash Apps 有几个优点:(1)你不仅可以用 python 设计应用,还可以用 Julia,。NET,或 R;(2)您不需要成为 Java、CSS 或 HTML 方面的专家来构建分析型 web 应用程序;(3)应用程序呈现在浏览器中,您可以通过 URL 与几个人共享您的仪表板;(4)它是多*台的,应用程序的设计可以调整到任何你想使用它的屏幕。

仪表盘应用

为了更好地理解本文中开发的一些概念,我推荐阅读我以前在这个中型*台上写的关于 Dash 的文章(用 Plotly Dash 1 制作图表:损耗模式用 Dash 2 制作图表:用于转换的单选项目)。

本质上,Dash 应用由两部分组成: (1)描述应用外观的布局;(2)允许应用程序被动响应的回调

我们总是通过布局开始构建应用程序的过程。这是我们放置应用程序组件的地方。为此,我们使用了两个库:(1)dash _ html _ components(html)它提供了 HTML 组件(标题、段落、换行符等的 HTML 标签。);(2)dash _ core _ components(DCC)提供了滑块、图形、下拉等组件。布局清楚地定义了我们的应用程序的视觉外观。

每个 Dash 应用的反应性(交互性)都是通过回调来实现的。这是一个有两个部分的函数:(1)装饰器,在这里你指出输入和输出组件。用户可以修改输入组件。输出组件显示应用对输入组件变化的反应。装饰器必须用 @app.callback() 初始化;(2)函数定义,根据某个输入组件的变化,对要显示的活动进行编码。函数定义必须用 def 初始化。****

我们的 Dash App

我们的布局包括以下内容:

  • 标题在最上面;
  • 三个选项卡,每个选项卡都有一个相关的回调函数( dcc。tab);
  • 一个滑块,用户可以在那里拖动来改变年份( dcc。滑块);
  • 两个图表:左边的图表显示了特定年份的生物乙醇产量;右边的图表显示了世界生物乙醇的产量。图表

我们的回拨包括以下内容:

  • 两个输入:一个用于选项卡组件,另一个用于滑块[ Input() ]
  • 两个输出:一个用于屏幕左侧的图表,另一个用于屏幕右侧的图表 (Output())
  • 函数 build_graph,我们在其中对将显示在屏幕上的六个图表进行编码( def build_graph() )

我们的代码

我们使用了一个公共领域数据集:按国家或地区划分的全球乙醇产量(百万加仑)。数据来源:可再生燃料协会(RFA) 。最后更新 2021 年 6 月[1]。

像往常一样,我们通过导入熊猫库作为 pd ,Plotly Express 作为 px ,plotly.graph_objects 作为 go ,并将我们的 csv 文件转换成 dataframe 来开始代码:

import pandas as pd
import plotly.express as px
import plotly.graph_objects as go# Step 1: world bioethanol production (units in Mgal)
df_bioet = pd.read_csv(path + 'world_bioethanol_2007_2020.csv',
               index_col = False, sep = ';', engine = 'python',
               header = 1)

首先,我们必须执行某些替换,并将 pandas 对象转换为指定的数据类型:

df_bioet['2007']  = df_bioet['2007'].str.replace(',','')
df_bioet['2008']  = df_bioet['2008'].str.replace(',','')
………………………………………………………………………………………………………………………………………………….
df_bioet['2019']  = df_bioet['2019'].str.replace(',','')
df_bioet['2020']  = df_bioet['2020'].str.replace(',','')df_bioet = df_bioet.astype(
           {'2007': int, '2008': int, '2009': int, '2010': int,
            '2011': int, '2012': int, '2013': int, '2014': int,
            '2015': int, '2016': int, '2017': int, '2018': int,
            '2019': int, '2020': int},
             errors = 'ignore')df_bioet['Country']= df_bioet['Country'].apply(str)
tipo1 = df_bioet.dtypes

我们必须对原始数据帧进行一定数量的修改,以将其转换为适合绘图的数据帧:

df_bioet_f1 = df_bioet.iloc[-1:]
df_bioet_f1 = df_bioet_f1.transpose()df_bioet_f1.columns = df_bioet_f1.iloc[0]
df_bioet_f1 = df_bioet_f1[1:]df_bioet_f1.index.names = ['Year']
df_bioet_f1.reset_index(level = 0, inplace = True)df_bioet_f2 = df_bioet.iloc[:-1]

下面的屏幕截图显示了名为 df_bioet_f2 的数据帧的记录:

作者图片

现在我们准备好第一张图表:一张特定年份世界生物乙醇产量的 标准饼状图

h1 = 600
w1 = 600
year = '2007'
text_title = "World Bioethanol Production "
titlefont  = dict(family = 'Arial', color='white', size= 30)col_discr_map = {'USA':'orange', 'Brazil':'darkgreen',
                 'European Union':'blue', 'CANADA': 'yellow',
                 'CHINA' : 'red','Rest of World' : 'darkred'}# First Figure: pie for a specific yearfig1 = px.pie(data_frame = df_bioet_f2,
              values = '2007', names = 'Country',
              color = 'Country',
              color_discrete_map= col_discr_map)fig1.update_layout(template = 'plotly_dark',
            title = dict(text = text_title + str(year),  x = 0.5),
            title_font= titlefont,
            width  = w1, height = h1)

我们的第二张图是生物乙醇生产区域的多线图:

xyaxesfont = dict(family='Verdana', color='white', size= 15)
xytickfont = dict(family='Calibri', color='white', size= 15)# Second Figure: line chart for productive regionsdf_bioet_f4 = df_bioet_f2.transpose()
df_bioet_f4.columns = df_bioet_f4.iloc[0]
df_bioet_f4 = df_bioet_f4[1:]df_bioet_f4.rename(columns={'Country': 'Year'})
df_bioet_f4.index.names = ['Year']
df_bioet_f4.reset_index(level = 0, inplace = True)fig2 = px.line(df_bioet_f4, x = 'Year', y = ['USA', 'Brazil', 
               'European Union','Canada',
               'China', 'Rest of World'],
                color_discrete_map= col_discr_map)fig2.update_layout(template = 'plotly_dark',
                   hovermode = 'y',
                   legend_title="Region",
                   width  = w1 +200, height = h1)fig2.update_xaxes(title_text = 'Year',
                  title_font = xyaxesfont,
                  tickfont   = xytickfont)fig2.update_yaxes(title_text = "Production (Mgal)",
                  title_font = xyaxesfont,
                  tickfont   = xytickfont)

现在是时候开始开发我们的应用程序了。

在本文中,我们假设您已经安装了库 Dash 如果没有,请遵循这些说明

首先我们需要导入以下库:破折号,主库;dash _ html _ components(html)用于构建布局;dash _ core _ components(DCC)渲染一组选项卡、一个滑块和 plotly 图形;输出,输入来自模块 dash.dependencies 的函数用于回调装饰器。

import dash
from dash import html
from dash import dcc
from dash.dependencies import Output, Input

现在,是时候定义一些 css 属性来对布局中包含的组件(尤其是选项卡)进行样式化了:

external_stylesheets=['https://codepen.io/chriddyp/pen/bWLwgP.css']app = dash.Dash(__name__, external_stylesheets=external_stylesheets)tab_style = {"background": "darkred",'color': 'white',
             'text-transform': 'uppercase',
             'justify-content': 'center',
             'border': 'grey','border-radius': '10px',
             'font-size': '12px','font-weight': 600,
             'align-items': 'center','padding':'12px'}tab_selected_style = {"background": "darkblue",'color': 'white',
                      'text-transform': 'uppercase',
                      'justify-content': 'center',
                      'border-radius': '10px',
                      'font-weight': 600,'font-size': '12px',
                      'align-items': 'center','padding':'12px'}

我们的布局( app.layout) 包括一个标题,三个标签( dcc。选项卡),滑块( dcc。滑块),和两个图表( dcc。图表)。都是用 html 包装的。div([])容器。

app.layout= html.Div(children =[html.Div([
             html.H1(children = 'World Bioethanol Production',
               style = {'textAlign' : 'center', 'color':'darkblue',
                        'fontSize'  : '30px', 'fontWeight': 'bold'}
                     ),
               html.Br(),
             html.Div([
               html.Br(),
               dcc.Tabs(id="Chart_Tabs",
                        value='Chart_P',
                        children=[
                        dcc.Tab(label='PieChart/LineChart',
                                value='Chart_P',
                                style=tab_style,
                                selected_style=tab_selected_style),
                        dcc.Tab(label='Treemap/StackedBars',
                                value='Chart_T',
                                style=tab_style,
                                selected_style=tab_selected_style),
                        dcc.Tab(label='Sunburst/StackedAreas',
                                value='Chart_S',
                                style=tab_style,
                                selected_style=tab_selected_style),
                                    ]),
               html.Div(id='Tabs_Content'),
               html.Br(),
            html.Div([
               html.Br(),
               html.Div([html.H6('Drag to change the year')],
                                 style={'text-align': 'Left'}),
               dcc.Slider(id = "Year_Slider", updatemode = 'drag',
                          min=2007, max=2020,step=1,value=2007,
                          tooltip={"placement":"bottom",
                          "always_visible": True}),
                         ]),
            html.Div([
               html.Br(),
               dcc.Graph(id = 'A-reactive-graph', figure = fig1,
                         style={'width': '80vh',
                         'display': 'inline-block'}),
                      ], className = 'five columns'),
            html.Div([
               html.Br(),
               dcc.Graph(id = 'A-static-graph', figure = fig2,
                         style={'width': '80vh',
                         'display': 'inline-block'}),
                      ], className = 'six columns'),
                   ]),
                 ]),
             ])

如前所述,我们的回调装饰器由两个输入(一个用于选项卡组件,另一个用于滑块)和两个输出(一个用于屏幕左侧的图表,另一个用于屏幕右侧的图表):

@app.callback(Output('A-reactive-graph',  'figure'),              
              Output('A-static-graph',    'figure'),            [Input(component_id='Chart_Tabs',component_property='value'),       Input(component_id='Year_Slider',component_property='value')])

我们在回调函数 build_graph: 中编写了六个图表

  1. 图 1,一个饼状图;
  2. 图 2,多线图表;
  3. 图 3,一个树形图
  4. 图 4,堆积条形图
  5. 图 7,一张旭日图
  6. 图六,堆叠区域
def build_graph(value_T, value_S):
    if value_T ==   'Chart_P':
       fig1 = px.pie(data_frame = df_bioet_f2,
                     values = str(value_S), names = 'Country',
                     color  = 'Country',
                     color_discrete_map= col_discr_map)
       fig1.update_layout(template = 'plotly_dark',
                                     + str(value_S),  x = 0.5),
                          title_font= titlefont,
                          width  = w1, height = h1) fig2 = px.line(df_bioet_f4, x = 'Year', y = ['USA', 'Brazil',
              'European Union','Canada', 'China', 'Rest of World'],
               color_discrete_map= col_discr_map) fig2.update_layout(template = 'plotly_dark',
                          title = dict(text = "World Production ",
                                      x = 0.5),
                          title_font= titlefont,
                          hovermode = False,
                          legend_title="Region",
                          width  = w1 +200, height = h1) fig2.update_xaxes(title_text = 'Year',
                         title_font = xyaxesfont,
                         tickfont =   xytickfont) fig2.update_yaxes(title_text = "Production (Mgal)",
                         title_font = xyaxesfont,
                         tickfont =   xytickfont) return fig1, fig2, elif value_T == 'Chart_T':
        fig3 = px.treemap(df_bioet_f2, path = ['Country'],
                          values = str(value_S),
                          color  = 'Country',
                          color_discrete_map= col_discr_map) fig3.update_layout(template = 'plotly_dark',
                           title = dict(text = "Production Year "
                                        + str(value_S),  x = 0.5),
                           title_font=  titlefont,
                           hovermode = 'y',
                           width  = w1, height = h1) fig3.data[0].textinfo = 'label + value'
       fig3.data[0].hovertemplate = '%{label}<br>%{value}' fig4 = go.Figure(data=[
                 go.Bar(name='USA',x=df_bioet_f4['Year'],  
                                   y=df_bioet_f4['USA'],
                        marker_color = 'orange'), 
                 go.Bar(name='Brazil',x=f_bioet_f4['Year'],
                                      y=df_bioet_f4['Brazil'],
                        marker_color = 'darkgreen'), go.Bar(name='Eur.Union',x=df_bioet_f4['Year'],
                             y=df_bioet_f4['European 
                              Union'],
                        marker_color = 'blue'),
                 go.Bar(name='Canada',x=df_bioet_f4['Year'],    
                                     y=df_bioet_f4['Canada'],       
                       marker_color = 'yellow'),
                 go.Bar(name='China',x=df_bioet_f4['Year'],     
                                    y=df_bioet_f4['China'],
                        marker_color = 'red'),
                 go.Bar(name='Rest of World',x=df_bioet_f4['Year'],   
                                   y=df_bioet_f4['Rest of World'],
                        marker_color = 'darkred'),] fig4.update_layout(barmode='stack')
       fig4.update_layout(template = 'plotly_dark',
                          title = dict(text = "World Production ",   
                                       x = 0.5),
                          title_font= titlefont,
                          hovermode = False,
                          width  =  w1 + 200, height = h1) fig4.update_xaxes(title_text = 'Year',
                         title_font = xyaxesfont,
                         tickfont =   xytickfont) fig4.update_yaxes(title_text = "Production (Mgal)",
                         title_font = xyaxesfont,
                         tickfont =   xytickfont)
       return fig3, fig4,
    elif value_T == 'Chart_S':
       fig7 = px.sunburst(df_bioet_f2, path = ['Country'],
                            values = str( value_S),
                            color  = 'Country',
                            color_discrete_map= col_discr_map) fig7.update_layout(template = 'plotly_dark',
                            title = dict(text = "Production Year "
                                         + str(value_S),  x = 0.5),
                            title_font=  titlefont,
                            hovermode = 'y',
                            width  = w1, height = h1) fig7.data[0].textinfo = 'label + value'
       fig7.data[0].hovertemplate = '%{label}<br>%{value}' 

       fig6 = go.Figure()
       fig6.add_trace(go.Scatter(x = df_bioet_f4['Year'], 
                                 y = df_bioet_f4['USA'],
                                 name = 'USA',
                                 mode = 'lines', 
                             line= dict(width=0.5, color='orange'),
                                 stackgroup = 'one'))
       fig6.add_trace(go.Scatter(x = df_bioet_f4['Year'],
                                 y = df_bioet_f4['Brazil'],
                                 name = 'BRAZIL',
                                 mode = 'lines', 
                            line= dict(width=0.5,color='darkgreen'),
                                stackgroup = 'one'))
       fig6.add_trace(go.Scatter(x=df_bioet_f4['Year'],
                                 y=df_bioet_f4['European Union'],
                                 name = 'EUROPEAN UNION',
                                 mode = 'lines',          
                             line=dict(width=0.5, color='blue'),
                                stackgroup = 'one'))\
       fig6.add_trace(go.Scatter(x = df_bioet_f4['Year'], 
                                 y = df_bioet_f4['Canada'],
                                 name = 'CANADA',
                                 mode = 'lines', 
                             line= dict(width=0.5,color='yellow'),
                                 stackgroup = 'one'))
       fig6.add_trace(go.Scatter(x = df_bioet_f4['Year'],
                                 y = df_bioet_f4['China'],
                                 name = 'CHINA',
                                 mode = 'lines',  
                             line=dict(width=0.5, color='red'),
                                stackgroup = 'one'))
       fig6.add_trace(go.Scatter(x = df_bioet_f4['Year'],
                                 y = df_bioet_f4['Rest of World'],
                                 name = 'REST OF WORLD',
                                 mode = 'lines',       
                             line=dict(width=0.5, color='darkred'),
                                stackgroup = 'one')) fig6.update_layout(template = 'plotly_dark',
                            title = dict(text = "World Production ",                       
                                         x = 0.5, y = 0.95),             
                            title_font= titlefont,
                            hovermode = False,
                            width  =  w1 + 200, height = h1)
       fig6.update_xaxes(title_text = 'Year',
                           title_font = xyaxesfont,
                           tickfont =   xytickfont)     
       fig6.update_yaxes(title_text = "Production (Mgal))",
                           title_font = xyaxesfont,
                           tickfont =   xytickfont)
       return fig7, fig6,

我们的申请

运行服务器需要下面两行代码:

if __name__ == '__main__':
     app.run_server(debug = False, port = 8080)

您应该会在 Python 开发环境中看到一条类似于以下屏幕截图所示的消息:

图片作者。

在您的浏览器中复制 url ,您将看到我们应用程序的第一个屏幕,其中有饼状图和多条线图。

图 1:应用程序的第一个屏幕。图片由作者用破折号。

单击带有文本树形图/堆积条形图的选项卡,您将看到我们应用程序的第二个屏幕,带有树形图和堆积条形图:

图 2:应用程序的第二个屏幕。图片由作者用破折号。

单击带有文本旭日图/堆积面积的选项卡,您将看到我们的应用程序的第三个屏幕,上面有旭日图和堆积面积图:

图 3:应用程序的第三个屏幕。图片由作者用破折号。

拖动任一屏幕上的滑块,您会看到左侧图表发生相应变化,而右侧图表保持不变:

图 4:拖动后在第二个屏幕上产生的变化。图片由作者用破折号。

分析

第一屏:饼状图之所以吸引人,是因为它们简单且广为人知。但它们不太适合进行精确的比较,尤其是当类别之间的差异很小时。我们的多线图表也有它的缺点,因为四条较低的线是交叉重叠的。

第二个屏幕:树形图适用于在非层级扁*结构中表示相对比例,在相对较小的空间中比较嵌套矩形方案中组成整体的部分。堆积条形图广泛用于动态构成分析以显示类别构成如何随时间变化。

第三个屏幕:旭日图类似于树状图,但是类别之间的比较非常困难,因为我们用切片和角度而不是矩形来表示它们。堆积面积图存在一些困难,因为每条线都跟随下面的线的基线,随着时间的推移,每个特定的趋势都类似于下面的趋势。

结论

对于前面描述的情况(随时间变化的趋势+成分分析),最好的叙述方式是将树形图与堆积条形图结合起来。

如果你对这篇文章感兴趣,请阅读我以前的(https://medium.com/@dar.wtz)

[1]年度乙醇产量,RFA,可再生燃料协会,https://ethanolrfa . org/markets-and-statistics/annual-Ethanol-Production

面板应用中的 ChatGPT 和 DALL E 2

原文:https://towardsdatascience.com/chatgpt-and-dall-e-2-in-a-panel-app-1c921d7d9021

让我们用 Python 编写一个聊天机器人吧!

你有兴趣建立一个使用最新人工智能模型的聊天机器人吗?使用 Panel 只需几行代码,我们就可以轻松构建一个包含 ChatGPT 和 DALL E 2 的聊天机器人。ChatGPT 和 DALL E 2 由 OpenAI 开发,根据文本提示从头开始生成文本和图像。在这篇博文中,我们将探索 OpenAI API,并了解如何在一个 Python 面板应用程序中使用 ChatGPT 和 DALL E 2 的创造力。想象一下,一个聊天机器人不仅可以回答问题和制作故事,还可以通过视觉艺术帮助你设想你的故事,那岂不是很酷?

OpenAI API

要使用 OpenAI API,我们首先需要在 openai.com 上创建一个帐户,并创建一个 API 密钥。记得复制密钥并保存在某个地方以备后用。

在 openai.com 创建一个新的 API 键

然后我们可以通过 pip: pip install openai安装 OpenAI API 的 Python 绑定。

然后我们就可以开始用 Python 编码了!你可以在 Jupyter Notebook 或者 VSCode 或者其他你喜欢的编辑器中编码。

在导入所需的包并定义 API 键之后,我们可以运行openai.Model.list()来列出 OpenAI 上所有可用的模型。他们有 66 个人!你可以在 beta.openai.com/docs/models.上阅读主要车型的描述

这些 OpenAI 模型分为五类:文本完成、代码完成、图像生成、微调和嵌入。为了探索 ChatGPT 和 DALL E 2,我们将使用文本完成和图像生成 API 以及openai.Completionopenai.Image

我将这两个 API 请求方法封装到两个函数中,在这两个函数中,我们给出一个提示并接收一个文本和图像 URL。这两个函数中有很多参数可以使用。例如,openai.Completion让你从一组 GPT-3 模型中选择,这里我选择了 text-davinci-003 ,OpenAI 发布的最有能力的 GPT-3 模型。它还允许您定义完成时生成的最大令牌数和温度,这是模型将承担的风险,0 表示定义良好的答案,1 表示更有创意的答案。同样,openai.Image允许您定义返回的图像数量和图像大小,您可以从“256x256”、“512x512”和“1024x1024”中选择。

def openai_completion(prompt):
    response = openai.Completion.create(
      model="text-davinci-003",
      prompt=prompt,
      max_tokens=150,
      temperature=0.5
    )
    return response['choices'][0]['text']

def openai_image(prompt):
    response = openai.Image.create(
      prompt=prompt,
      n=1,
      size="512x512"
    )
    image_url = response['data'][0]['url']
    return image_url 

让我们试着给他们提示,看看会发生什么。openai_completion 函数能够正确地告诉我奥斯汀的当前温度,openai_image 函数返回“独角兽在云上徒步旅行”的图像。成功!

面板仪表板

现在我们已经学习了 OpenAI 文本完成 API 和图像生成 API,我们可以在 Python 面板仪表板中将它们结合在一起吗?

什么是面板?Panel 是开源 HoloViz 生态系统中的仪表板库,提供高级工具来简化 Python 中的可视化,由 Philipp Rudiger、Jim Bednar 和一个贡献者社区开发。查看 panel.holoviz.org了解更多信息。

为了构建一个面板仪表板,让我们首先导入panel和相关的模块:

import panel as pn
pn.extension(loading_spinner='dots', loading_color='#00aa41')

然后让我们制作三个小部件。inp是我们输入文本提示与聊天机器人聊天的地方,button_conversation是“聊天!”按钮,它将向 OpenAI 文本完成 API 发送提示,而button_image是“图像!”将提示发送到 OpenAI 图像生成 API 的按钮。

inp = pn.widgets.TextInput(value="", placeholder='Enter text here…')
button_conversation = pn.widgets.Button(name="Chat!")
button_image = pn.widgets.Button(name="Image!")

get_conversations函数在一个面板对象中返回我们与聊天机器人的所有对话。这里有几点需要注意:

  • convos_text将所有对话存储在列表中。当我们给 OpenAI 文本完成一个提示时,我们包括了历史中的所有聊天。
  • 面板支持降价。我将所有的提示和 OpenAI 的响应添加到 Markdown 格式中。
  • 每当有新的聊天时,就会创建一个新的面板对象来反映这个新的聊天,并将其添加到convos列表中。

get_image函数要简单得多,我们只需在一个面板对象中显示来自图像 URL 的图像。面板仪表板中显示的所有内容都必须是面板对象。

convos_text = [] # store all texts in a list
convos = [] # store all panel objects in a list

def get_conversations(_):
    prompt = inp.value
    inp.value = ''
    if prompt != "":
        convos_text.append(prompt)
        openai_answer = openai_completion('\n'.join(convos_text)) # prompt includes all history
        convos_text.append(openai_answer)
        convos.append(
            pn.Row('\U0001F60A', pn.pane.Markdown(prompt, width=600))
        )
        convos.append(
            pn.Row('\U0001F916', pn.pane.Markdown(openai_answer, width=600, style={'background-color': '#F6F6F6'}))
        )
    if len(convos_text) == 0:
        convos.append(pn.Row('\U0001F916', pn.pane.Markdown("Give me something!", width=600, style={'background-color': '#F6F6F6'})))

    return pn.Column(*convos)

def get_image(_):
    if len(convos_text)>0:
        image_prompt = convos_text[-1]
        image_url = openai_image(image_prompt)
        return pn.pane.PNG(image_url, width=600)

然后最后一步是将get_conversations函数与button_conversation按钮绑定,将get_image函数与button_image按钮绑定,也就是说只有当我们点击按钮时,函数才会执行。然后,我们将所有面板部件和交互组件组织在一个仪表板中。

interactive_conversation = pn.bind(get_conversations, button_conversation)
interactive_image = pn.bind(get_image, button_image)

dashboard = pn.Column(
    inp,
    pn.Row(button_conversation,button_image),
       pn.panel(interactive_conversation, loading_indicator=True, height=500),
    pn.panel(interactive_image, loading_indicator=True, height=500),

)

dashboard.servable()

完全码

您可以在我的 GitHub repo 中找到完整的代码:

https://github.com/sophiamyang/panel_openai/

请随意尝试一下。运行panel serve panel_openai.pypanel serve panel_openai.ipynb查看文章开头所示的交互式仪表盘!

同样,在这个应用程序中,你可以输入提示,点击“聊天!”开始与聊天机器人聊天,点击“图片!”根据最*的对话生成图像。

在哪里可以了解更多关于 Panel 的信息?

结论

在本文中,我们演示了如何在 Panel 中构建一个简单的聊天机器人。使用两个 OpenAI APIs 并将 ChatGPT 和 DALL E 2 结合在一个应用程序中,我们的聊天机器人具有文本生成和图像生成功能。如果您还没有尝试过,请尝试一下!

致谢:

感谢 Jim Bednar、Simon Hansen、Maxime Liquet 和 Philipp Rudiger 的指导和反馈!

参考资料:

. . .

索菲亚杨于 2022 年 12 月 22 日。

Sophia Yang 是 Anaconda 的高级数据科学家。在 LinkedInTwitterYouTube 上与我联系,并加入 ds/ml❤️读书俱乐部

备忘单:决策树术语

原文:https://towardsdatascience.com/cheat-sheet-decision-trees-terminology-c37ee6cb2d2e

在术语的随机森林中找到自己的路

备忘单:决策树[图片由作者提供]

B 聚集、推进、集成方法和随机森林 —最初,围绕决策树的术语和概念可能会非常混乱和吓人,尤其是在机器学习领域起步时。

上周的期中,我们从头开始讲述了决策树的实现,解释了算法的内部工作原理。然而,仍然有一些术语和主题没有涉及。

在接下来的章节中,我们试图发现其中的一些主题,创建一个备忘单,让我们快速、全面地了解决策树领域中的概念。

免责声明:备忘单的性质只允许对某些主题进行概述,不可能涵盖所有细节。然而,人们可以利用这个概述开始更深入地研究感兴趣的领域。

基础知识

在我们进入更高级的、被认为是新奇的东西之前,我们从基础开始——通过学习决策树的主要组成部分来建立基础。

根节点

决策树也可以解释为一系列节点,一个从单个节点开始的有向图。这个起始节点称为根节点,它代表整个样本空间。

从根节点开始,然后可以通过根据各种特征和阈值划分或分割样本空间来生长决策树。

剧烈的

描述将一个节点划分为两个或更多子节点的过程。有几种方法来分割决策树,涉及不同的度量(例如,信息增益、基尼系数杂质)。

决策节点

由分割的结果定义。当一个子节点分裂成更多的子节点时,我们称这些节点为决策节点或内部节点。

决策树的基本组成部分[图片由作者提供]

叶节点

如果一个子节点不能再分,我们称之为叶节点。叶节点表示用于预测的响应值(例如,最常见的类标签)。

树枝

分支描述了决策树的节点或子部分之间的连接。

父/子节点

描述节点之间关系的相对术语。任何属于另一个节点的节点都是子节点。这些子节点之前的任何节点都称为父节点。

种植一棵树

现在,我们知道了决策树的基本构建模块,我们需要知道如何构建一个决策树。

递归二进制分裂

创建决策树描述了将输入空间划分成几个不同的、不重叠的子空间的过程。为了划分输入空间,我们必须测试所有特征和阈值,以找到最小化我们的成本函数的最佳分割。

一旦我们获得了最佳分裂,我们可以继续递归增长我们的树。

该过程被称为递归,因为每个子空间可以被分裂无限次,直到达到停止标准(例如,最大深度)。

贪婪分裂

寻找最佳分割包括评估所有可能的特征和分割点。由于我们只关注当前的增长阶段,而没有向前看,我们正以贪婪的方式选择最佳分割。

修剪

通过删除非关键部分来减小决策树的大小和复杂性。这允许更好的泛化、更少的过拟合和更好的预测准确性。修剪可以用不同的方式完成,因此有几种方法。

例如,减少错误修剪用最常见的类标签替换节点。如果精度不受影响,更改将被保留。

另一方面,成本复杂性修剪试图移除影响精确度最小但继承最大复杂性的完整子树。

韵律学

仅仅知道生成决策树的过程是不够的。为了评估所有可能的拆分,并能够合理地选择最佳拆分,我们需要一种方法来衡量拆分的质量或有用性。

因此,存在几个度量标准。

在信息论中,衡量一组观察值中信息或不确定性的*均水*。更正式地说,它可以定义为以下等式:

熵为 1 表示最不确定,而熵为 0 表示完全确定。

信息增益

信息增益提供了一种方法来描述一个特征提供了多少信息——或者换句话说,移除了多少熵。

可以通过从父熵中减去子的加权熵来计算分裂的信息增益。因此,使它特别有助于评估一个可能的分裂候选,让我们找到和选择最佳分裂。

信息增益 1 可能是最佳值,而值 0 意味着没有不确定性或熵被移除。

基尼杂质

衡量随机选择的元素被错误标记的频率。它提供了一种测量值为 0 的节点纯度的方法,一方面定义了完全的纯度,另一方面描述了最大的杂质。

基尼系数也可用于评估可能的分裂候选值,其计算公式如下:

扩展ˌ扩张

虽然决策树很容易解释,因为它们模拟了人类决策的过程,但它们有时可能缺乏准确性或不够健壮。

为了克服这些缺点,可以应用许多技术和扩展。

集成方法

是一种机器学习技术,它将几个“弱学习者”或构建块组合成一个更强大的预测。在我们的例子中,构建块可以是简单的回归或分类决策树。

制袋材料

bootstrap aggregating 的缩写,是减少统计学习方法的方差的一般过程。这里的主要思想是,组合和*均观察组减少了方差。

在实践中,我们通过从单个训练集中随机抽取替换样本并构建决策树来进行引导。重复这个过程将产生多个可以聚合的决策树。

例如,应用于回归树,我们通过取预测值的*均值来聚合多个树。将 bagging 应用于分类树,包括对预测的类别标签进行多数投票,以便进行汇总。

随机森林

通过决策树的去相关,在 bagging 思想的基础上进行构建和改进。每次考虑分割时,只选择随机样本 m 个特征。这意味着每次分割算法甚至不允许考虑大多数可用特征。

虽然一开始感觉违反直觉,但主旨还是有道理的。

例如,当构建袋装树时,算法可能只考虑一个特定的强特征。

因此,大多数树看起来大致相同。由于对高度相关的变量进行*均不会显著降低方差,因此对看起来非常相似的树进行*均不会提高准确性。

助推

到目前为止,我们讨论的扩展都涉及到生长多个独立的树。Boosting 通过构建多个依赖于先前树的树,稍微修改了这个想法。

在实践中,我们生长了几棵更小的树,其中每棵树都与前一棵决策树的残差相匹配。因此,在模型表现不好的地方慢慢改进模型。

结论

在本文中,我们试图通过创建一个备忘单,在术语的随机森林中找到自己的路,作为围绕决策树学习方法的主题的快速参考和一般概述。

由于我们只能提供一个快速的概述,一些概念值得更多的解释,应该被更深入的发现。然而,概述可能有助于确定感兴趣的领域。

请随意下载并分享我的 GitHub 的备忘单。

Marvin Lanhenke

马文·兰亨克

# 30 日

View list30 stories

喜欢这篇文章吗?成为 中等会员 继续无限学习。如果您使用以下链接,我将收取您一部分会员费,无需您支付额外费用

https://medium.com/@marvinlanhenke/membership

参考资料/更多资料:

  • 加雷斯·詹姆斯,丹妮拉·威滕,特雷弗·哈斯蒂,罗伯特·蒂布拉尼。《统计学习导论:在 r .纽约的应用》: Springer,2013 年。

重要 Docker 命令的备忘单

原文:https://towardsdatascience.com/cheat-sheet-of-important-docker-commands-6468678ab8ea

UnsplashSai Kiran Anagani 拍摄的照片

在马上进入命令之前,如果你不知道 docker,下面的代码片段是为你准备的。

Docker 是什么?

Docker 是一个开发、发布和运行应用程序的开放*台。Docker 使您能够将应用程序从基础设施中分离出来,这样您就可以快速交付软件。使用 Docker,您可以像管理应用程序一样管理基础设施。通过利用 Docker 快速交付、测试和部署代码的方法,您可以显著减少编写代码和在生产中运行代码之间的延迟。

如果你还有任何疑问,那么你可以访问他们的官方文档。现在,让我们深入研究一下这些命令。

我试着列出了大部分命令,使用 docker 的时候你会发现这些命令非常有用。这些命令遵循提取映像、运行容器、删除的逻辑顺序。任何可能被遗漏的核心命令都欢迎评论!

1.docker 版本:

首先也是最基本的命令,一旦你下载并安装了 docker,你就可以使用这个命令检查 docker 是否安装正确。

docker --version

2.码头工人拉动:

现在,安装 docker 后,您将开始使用 Docker hub 或私有存储库上的各种图像。提取图像的命令是:

docker pull <image_name>

从 DockerHub 中提取 python 图像的示例。

拉具体版本的 docker 图片:

如果您希望获取图像的任何特定版本,可以通过如下方式指定其版本来轻松实现:

docker pull <image_name>:<version>

例如:提取特定版本的 python 图像

docker pull python:3.10-buster

3.docker ps:

要查看计算机中正在运行的容器,请运行命令

docker ps

docker ps 示例

从输出数据来看,容器 id名称总是唯一的。您可以使用这个 id/name 来启动、停止和执行容器上的各种其他选项。

端口部分,0 . 0 . 0:6001是本地机器的端口号, 6379/tcp 是容器的端口。

见先前停靠码头的集装箱:

要查看之前停止的容器,使用选项-a "

docker ps -a

4.码头运行:

拉出图像后,下一步是从图像创建一个容器(一个实例)。

docker run <image_name>

创建 redis 图像容器的示例

对于这个命令,它首先检查给定的图像是否存在于本地,如果不存在,它首先下载图像,即执行" docker pull "然后运行它的容器。

docker run 一起使用的有用选项 :

a .拆下的端子:

docker run -d <image_name>

要在分离的终端中运行容器,并让我们在当前终端中重新获得控制权,请使用 -d 选项和 run 命令。

b .为集装箱指定特定港口:

假设你拉了不同版本的 redis,说“最新”和“ 7 ”。当你做“docker run redis”和“docker run redis:4”。然后,两个容器将在每个容器的同一个端口启动,通常是 6379。现在,您需要将它们绑定到不同主机端口(您的本地主机端口)。

docker run -p<host_port_number>:<container_port> <image_name>

例如:将 redis 的端口 6379 绑定到本地主机上的不同端口

docker run -p6000:6379 redis
#This will bind your host’s 6000 port to container's 6379 port.

将 redis:7 绑定到另一个端口

docker run -p6001:1463 redis:7
#This will bind your host’s 6001 port to container's 6379 port.

c .为集装箱命名:

为了给容器命名,使用选项“名称”。

例如:将 redis_new 命名为最新的 redis 映像,将 redis_old 命名为旧映像

docker run -d --name redis_new redis && docker run -d --name redis_old redis:7

d .在 docker 运行命令中给出卷:

为了使数据持久化并利用机器的存储,您需要将卷与容器绑定在一起。下面是我们使用命名卷并将其绑定到容器中的" /usr/lib/data" 的示例。

docker run -v my_personal_volume:/usr/lib/data mongo

e .将容器连接到特定网络:

要使容器使用任何特定网络,请使用以下命令:

docker run -p27017:27017 -d --net mongo-network mongo

在这里,我们将容器连接到“mongo-network”。

5.docker 图像:

要查看机器上的所有 docker 图像,请使用以下命令:

docker images

6.停靠站:

在运行 docker 容器之后,您希望在某个时候停下来取下容器。要停止容器,请运行下面给出的命令:

docker stop <container_id/name>

您可以使用 "docker ps" 命令获取集装箱的 id,然后停止所需的集装箱。

注意:或者,您也可以使用容器名称来停止容器。

7.docker 开始:

如果您想再次启动任何停止的容器,那么使用“docker PS-a”获取容器 id 或名称。并运行下面给出的命令:

docker start <container_id/name>

8.docker 日志:

要查看容器的日志,您需要使用以下命令:

docker logs <container_id/name>

docker 日志的有用选项:

a. —尾部

docker logs <container_id/name> --tail <n/all>

为了只获得日志的结尾部分,使用 tail 选项,后跟您希望看到的行数。比如 10 等,甚至你可以输入“全部”来查看整个日志。

b. -f:

为了实时传输日志,请使用-f 命令。

docker logs <container_id/name> -f

9。码头工人执行:

如果您想在容器的终端内部运行任何命令,那么您需要使用下面给出的 "docker exec" 命令:

docker exec -d ubuntu_bash touch /tmp/execWorks

这将在后台运行的容器ubuntu_bash中创建一个新文件/tmp/execWorks

获取容器的交互终端:

接下来,在容器上执行一个交互式的bash shell。

docker exec -it <container_id/name> /bin/bash

这将在容器ubuntu_bash中创建一个新的 Bash 会话。

注意:有时,您可能会得到错误消息,指出/bin/bash 命令未找到,在这种情况下,您需要 shell 使用命令:

docker exec -it <container_id/name> /bin/sh

10。docker 网:

查看 docker 当前正在使用的网络。使用命令:

docker network ls

创建码头工人网络:

docker network create mongo-network

这将创建新的“mongo-network ”,然后可以与容器一起使用。

11。docker rm:

使用完容器后,您希望删除它,那么在这种情况下使用“docker rm”命令:

docker rm <container_id/name>

注意:移除容器前,应先停止,否则会出现错误

12。码头工人 rmi:

如果您希望删除任何图像,请使用以下命令:

docker rmi <image_name>

注意:为了删除图像,它不应该有任何基于此图像的运行容器。

就这样结束了!我试图在这篇文章中涵盖大多数有用的命令。您可以随时在注释中添加任何遗漏的命令。那是我的落款:)

使用 Python 亲自检查数据声明

原文:https://towardsdatascience.com/check-data-claims-yourself-with-python-an-example-8b8a92c0ae56

公共媒体越来越多地使用数据可视化。使用您的 python 技能为自己验证声明。

艾通过作者创造艺术。 见上图 NFT 受埃伯哈德 的启发

介绍

公共媒体越来越多地使用数据可视化。

特别是在疫情时代,各种图表和数据解释被用来支持论点。

作为一名商业法毕业生和数据科学家,我总是对公共媒体和网络上描绘的一些数字和可视化表示好奇和怀疑。

在本文中,我向您展示了对奥地利 covid 死亡的快速分析,以探索我的一些好奇心。我不敢相信我在公共电视上看到的一些数字,我想亲自核实一下。

我鼓励你也这样做。使用分析工具自己检查某些声明从未像现在这样简单。

在我的分析中,我使用了

  • 奥地利原始数据的公共数据集
  • 用于编程的 python
  • jupyter 笔记本,用于文档和探索
  • 用于数据操作的 pandas / numpy 库
  • plotly 用于数据可视化
  • 用于代码部署和静态渲染的 Github
  • 简化为交互式 web 应用程序

在这篇文章中,我将挑选一些我认为值得详细阐述的东西。如果你需要更多的细节,请随意评论。

多项式回归的动画切线:作者 gif

结果链接

目录

数据准备

对于数据准备,重要的是要理解,

  • 数据质量非常重要
  • 总是需要某种预处理

在这种情况下,我的原始数据来自政府官方。这意味着,在政府主管部门看来,这些数据应该是可信的,但对于数据的来源和收集方式,可能没有任何适当的解释。

在这个分析中,没有特别的准备步骤。我对格式进行了一些操作,比如更改为 DateTime 格式,并创建新的特定列以供将来使用。在最后一步中,我合并了两个使用的数据集,并通过确定分位数阈值来去除异常值。

Plotly 和回归线

我想展示如何轻松地将回归线(来自 sklearn)集成到您的 plotly 可视化中。因为除了良好的可视化本身之外,将不同的数据点组合在一个图表中也很重要。一个完美的例子是回归线。此外,增加回归线的斜率。

绘制数据和多项式回归

这可以通过以下方式轻松实现

计算线的新点

然后在现有图表中添加线条

因此,仅仅添加add_traces就能做得很好。

然而,现在我们想做一些更复杂的事情,即添加回归线某一点的斜率。

添加回归线某一点的斜率

这实现起来并不容易,因为你首先需要理解多项式回归是如何工作的,然后你还需要画一条好看的切线。事实上,到目前为止还没有任何实现,这也是为什么我在 Stackoverflow 上创建了自己的答案。

最后,看起来是这样的

作者截图

代码看起来像这样:

实现的核心是这些行

fitted_params = np.polyfit(np.arange(0, len(y)), y, poly_degree )polynomials = np.poly1d(fitted_params)derivatives = np.polyder(polynomials)y_value_at_point = polynomials(x).flatten()slope_at_point = np.polyval(derivatives, np.arange(0, len(y)))

这些漂亮的 NumPy 函数允许你计算用来画切线的数字。

Plotly 动画

最后一步是 plotly 中的动画。显示切线运动的动画。

大概是这样的:

作者 gif

它的代码如下

这里需要理解的是

  • 首先,您需要将数据点和回归线作为普通图形对象添加到 plotly (traces)中。
  • 切线需要通过时间线计算并存储为字典
  • 然后,您需要将帧添加到数据轨迹和 plotly 布局中
  • plotly 布局还包括滑块和按钮的配置
  • 请注意,您需要计算类似于时间轴中每个步骤内的帧的滑块步骤

多项式回归的动画切线:作者 gif

作为 streamlit 应用程序进行分析;作者 gif

结论

最后,我很容易明白疫情是如何影响澳大利亚的死亡率的。

我通过使用开源数据和开源工具实现了这一点。

我鼓励你也这样做,检查你在媒体上看到的数据及其可视化的质量。

解释为视频

有时通过视频解释项目更容易。这也是我制作一个快速视频的原因

作者的解释视频

为了看到建造一切的全过程,我还制作了一个延时录像

作者拍摄的延时视频

你可能喜欢的其他文章

放弃

我与本文中使用的任何服务都没有关联。

我不认为自己是专家。除了做其他事情,我只是记录事情。因此,内容并不代表我的任何专业工作的质量,也不完全反映我对事物的看法。如果你觉得我错过了重要的步骤或者忽略了什么,可以考虑在评论区指出来或者联系我。

这写于 2022 年 7 月 9 日。我无法监控我的所有文章。当你阅读这篇文章时,提示很可能已经过时,过程已经改变。

我总是乐于听取建设性的意见以及如何改进。

关于

丹尼尔是一名艺术家、企业家、软件开发人员和商业法毕业生。他的知识和兴趣目前围绕着编程机器学习应用程序及其所有相关方面。从本质上说,他认为自己是复杂环境的问题解决者,这在他的各种项目中都有所体现。

作者图片

连接到:

直接:

艺术相关:

检查你的偏见

原文:https://towardsdatascience.com/check-your-biases-ba2685a7f799

符号引擎和意想不到的结果——个人编码体验

摘要

【2016 年 6 月,就在英国退出欧盟投票的前几天,在 K-Drive 项目刚刚结束后,我正乘坐英国航空公司(British Airways)的飞机从苏格兰阿伯丁飞往意大利。我随身带着一台笔记本电脑,里面有我在过去几周开发的一个小型 NLP 模型,用来预测英国退出欧盟的结果。除了模型,我还有详细描述预测本身的信息图表,这与主流的预测有很大不同。

照片由 Rohan MakhechaUnsplash 上拍摄

玛丽居里行动(Marie Curie actions)旗下的 K-Drive 项目(K-Drive Project)是一个由欧洲资助的项目,位于阿伯丁大学,旨在将工业界和学术界聚集在一起,在语义技术领域进行相互交流和共同努力。我于 2016 年 1 月作为 MER(更有经验的研究员)加入,当时主要论文已经发表,所以我被分配了培训和公开演讲任务,以展示所涉及的 NLP 技术。

在项目结束前的最后一个月,我们决定采用我们最初为苏格兰独立公投开发的模型,以监控社交媒体上关于英国退出欧盟的意见,寻找预测结果的见解和趋势。

经过一些磋商,我们决定使用 Twitter 作为一个来源,基于以前的经验和专业知识:推文简短、相关且切中要点,由各种各样的用户撰写,通常带有可靠的标签,而脸书的帖子则往往琐碎、过长且用蹩脚的英语撰写。此外,Reddit 的帖子有时很难理解,而且可能充满了钓鱼信息,而 Twitter 上的评论与原始推文截然不同,在分析中很容易被忽略。

首先,一名软件工程师开发了一个蜘蛛来下载在苏格兰、英格兰、北爱尔兰和威尔士发布的推文,这样它们就可以被分别处理,而我作为一名知识工程师,负责调整和进一步开发语言引擎。该蜘蛛被设计为只下载包含与英国退出欧盟公投相关的标签的推文,这是通过 Twitter API 允许的,属于我准备的一个微调列表。

在对大约 400 条随机推文的训练语料库进行初步分析后,我决定开发一个完全符号化的引擎,主要依靠知识图来掌握推文中包含的概念,以便理解观点以及相关的情绪。我试图涵盖整个语义领域,也包括苏格兰俚语。

因此,每天我们都会爬上 Twitter,获取推文,转换它们,并将其提供给语言引擎。不涉及机器学习。引擎在整个过程中保持不变,因此我们可以根据地理区域记录在线表达的趋势。

但当第一批结果开始出现时,有些尴尬,因为产出显示出坚实的、无可争议的休假优势。事实上,每一天都以明确的脱欧结果结束,这一趋势还在继续,而整个世界都一致认为,留欧一直以几个百分点的优势胜出。

当然,有人问我为什么我的结果会走调,所以我做了一个(自我)令人信服的演讲,讲述了公投活动人士总是比沉默的大多数更有发言权,因此我的结果将被视为对社会科学的见解,而不是对政治决策的实际预测。显然,我避免提及害羞的保守党因素。

但是,当我完全被自己的编码所吸引时,我不禁注意到,我被困在了主流媒体的叙述和我每天实际会见大量离职者的轶事经历之间。我的女房东,同行的爬山者,同行的观鸟者,酒吧里的石油和天然气公司的人,路那头那个地方的女收银员。他们很年轻,来自各种地方,有各种背景,有些人有博士学位,他们会问问题。

我就是这样,一个(南部)欧洲公民,在一个欧洲资助的项目中,舒适地坐在古老的梅斯顿大楼里,愉快地编码我的工作日,或者走在泥泞的小路上,寻找绒鸭、海雀和海雀,偶尔在周末还会看到狐猴。

我是一个有争议的人物,所以有人问我问题。

“我在出差”通常是不被允许的。“我在阿伯丁大学工作,从事一个欧洲资助的项目”遇到了一些紧张的尴尬。“我在 IT 部门工作”听起来很可疑,因为所有在阿伯丁的外国人都在石油&天然气部门工作。

离开者真的很大声。他们会告诉我被困在欧洲官僚机构中的感觉,并希望能够加入更多大型项目。或者他们会谈论英国不想缴纳欧洲税,或者无法承受不断增加的移民流。

我当然不会告诉他们哦,别担心!我刚刚发明了一个神奇的电脑,它会说“离开”会赢!

人工智能炒作还没有开始,所以他们不会相信我。

无论如何,新闻稿已经写好了,我们的结果也展示出来了。公投结果正式宣布的那天早上,我在意大利的家里。我正在吃早餐,我对自己说,我不想成为英国的外国人,但是如果我们是对的,而其他人都是错的,那不是很酷吗?

事实证明我们是对的,其他人都错了。

显然,明智地选择资源(在这里是 Twitter)和精心开发符号引擎是我们成功的关键。

但是后来主流分析出来了,我完全吃了一惊。给我的英国英国退出欧盟的总体印象是,只有没有上过学的农村老人投票离开,而城市里充满活力的年轻专业人士全都投票留下,与我的直接经历相比,这感觉完全超现实。显然,我不得不打消主流媒体只是根据政治议程框定事件的怀疑。也许他们一直都知道强烈的脱欧投票,但没有说出来,因为害怕导致更多的人投票脱欧,现在他们正在制造一个受广播剧阿彻启发的英国刻板印象,让脱欧者重新考虑。但这可能是阴谋论,所以我们可以排除它。

所以慢慢地怀疑开始蔓延。如果我的代码一开始就有偏差,我错得到了正确的结果怎么办。但是怎么做呢?不涉及机器学习,所以引擎不可能意外地从不*衡的训练集中学习。这是一个完全象征性的引擎——因此这种偏见肯定是无意的。

关键是符号引擎总是不受理论约束的。根据科学方法,你应该基于观察形成一个假设,然后你做出一个预测,你测试它,你重复测试直到你有足够一致的数据来得出结论。符号引擎不是这样工作的。它从不根据理论发展,没有观察、假设或预测。我想可能有一些假设,但是它们应该被认为是这样的,而不是编码到引擎中。符号引擎包含一组复杂的概括的、可解释的条件,这些条件是基于已知的需求以及对相关训练集的分析而开发的。概括需要直觉,这将是引擎中人类的火花,这将允许它自主工作。开发完成后,将处理新的数据集,并根据哪些条件为真来执行代码,从而返回反映初始需求的输出,并概述可能是或可能不是预期的信息和相关性。

最后,我们相信计算而不是假设,就像伽利略一样。

我也重新考虑 Twitter 作为一个有效的来源。会不会本身就有偏见?推特用户代表了所有英国选民吗,或者也许是左派代表过多了?在社会科学研究中,我仍然找不到任何令人信服的证据来支持这样一个理论,即人们在 Twitter 上比在现实生活中更容易请假。

突然,启示来了。如果与休假人员的日常接触让我开发出更好地理解休假词汇、概念、措辞和措辞的代码,会怎么样?或者可能是让人们更加直言不讳,在现实生活中以及在社交媒体上,他们最终教我他们的语言,我将这些知识传递给引擎。也许我不能很好地从呆着的人身上学到东西,因为他们要安静得多。毕竟,在我半心半意地为自己的工作辩护时,我实际上是在描述我的模型成功背后的非常社会语言学的过程。

所以现在我担心我的代码会无意中出现偏差,也就是说,患上了人工智能世界中最危险的疾病。话说回来,可能还有另一个因素。当前人工智能叙述中对偏见的炒作如此强烈,以至于炒作本身可能会影响我的判断。

总之,在处理符号引擎的发展时,我认为在我们的分析中融入社会语言学的元素是有用的。了解我们的源代码如何工作,人们如何在其中交流,我们如何能够解释他们的信息,可以帮助我们开发一个更*衡的代码。

当然,这一经历让我更多地尝试机器学习,不是作为符号引擎的生硬替代,而是为了混合解决方案。在我们用于符号开发的相同集合上训练 ML 模型可以提供有用的比较,以突出和理解我们结果中的偏差。

我越来越相信,多学科方法在这个领域是有益的,可以提高我们从大型数据集获得自动化、可靠见解的能力。

加入对话

你的 ML 模型稳定吗?用 PSI 和 CSI 检查模型稳定性和总体漂移

原文:https://towardsdatascience.com/checking-model-stability-and-population-shift-with-psi-and-csi-6d12af008783

如何确保你的机器学习模型仍然像训练的那样运行

由 Unsplash 上的 Aziz achar ki 原创

我们建立的机器学习模型基于一个简单的前提:我们将在推理时间使用的数据应该与训练时使用的数据具有相同的分布。

这个前提很简单,但相当有力,因为它意味着我们可以根据以前已知的观察结果来推断新的观察结果。

然而,有时一些现实生活中的影响会导致观察结果发生变化。一个例子是新冠肺炎疫情。在病毒传播之前训练出来的金融模型,在疫情之后可能不太适用。家庭收入可能会减少,甚至那些没有减少的家庭在花钱前也可能会三思。

这些影响可能是无声的,为了避免它们,我们需要评估模型的稳定性,或者换句话说,在现实生活场景发生变化后,模型是否仍然有效。

在本文中,我将向您展示两个可以做到这一点的指标,群体稳定性指数(PSI)和特征稳定性指数(CSI)。

模型稳定性

在动态系统分析中,我们可以把一个稳定的系统定义为在存在扰动的情况下保持不变(或只有微小变化)的系统。简单地说,一个稳定的系统对外部变化是稳健的。

测量模型稳定性的一种方法是检查总体或数据漂移,评估总体或特征在模型环境中的变化情况。

人口流动有几个可能的来源。一些例子可以包括

  • 社会经济关系的变化,如通货膨胀、疾病或政治变化;
  • 无法解释的事件,比如假期、世界杯,甚至是自然灾害;
  • 新的竞争对手进入市场,和/或客户转移;
  • 所提供产品或营销活动的变化。

数据和人口漂移的一个较少被评论的来源是模型本身的使用。如果您开发了一个模型来解决一个业务问题,并且这个解决方案是有效的,那么环境就变了,这个模型可能不会有同样的性能!

在下面的章节中,我将展示如何在机器学习模型的稳定性的背景下计算 PSI 和 CSI。所有代码和示例都可以在我的 GitHub 资源库中找到:

https://github.com/vinyluis/Articles/tree/main/Model Stability

人口稳定指数

人口稳定指数(PSI),顾名思义,是衡量人口在两个不同的时刻发生了多大的变化。

我们的目标是根据模型的预测(独立)变量来看人口分布如何变化。如果是回归模型,可以直接用预测值,但是如果是二元分类模型,就需要用概率(scikit-learn 上有.predict_proba())。

首先,我们通过将预测变量的范围分割成相同大小的部分来创建测试数据的箱。箱的数量是任意的,但是通常使用 10 个。

然后,我们将每个箱内的人口百分比与“新”生产数据进行比较。绘制这些箱,我们可以直观地比较它们:

作者图片

很明显,新增人口已经漂移。最初的分布更窄,*均值更接*于 4,因为新的分布*均值接*于 6,分布更广。

现在,我们使用下面的公式计算每个箱的 PSI:

结果如下表所示:

如果我们愿意,我们可以将*均 PSI 作为整个模型的单一指标。在这种情况下,模型的 PSI 为 0.1435。

通常,我们认为:

  • PSI < 0.1 = The population hasn’t changed, and we can keep the model
  • 0.1 ≤ PS1 < 0.2 = The population has slightly changed, and it is advisable to evaluate the impacts of these changes
  • PSI ≥ 0.2 = The changes in population are significant, and the model should be retrained or even redesigned.

In our example, the change was significant, but not so much that needs immediate action. We still should dive deeper into the causes and we can do it with CSI.

But first, remember that we created bins of our data with fixed-size ranges? Another way to calculate PSI would be by using quantile bins. In this scenario, by using 10 bins we assure that each bin will have 10% of the initial population, and we compare with the new population:

Image by author

The process here is pretty much the same, but the results can be slightly different:

The average PSI in this scenario is 0.1087, which is still in the “warning” zone, but much closer to the 0.1000 value of the “safe” zone.

If you have the time and resources, it is advisable to calculate PSI both ways.

If you are looking for the Python code to calculate PSI, please check out my GitHub ,功能如下:

特征稳定性指数

在前面的例子中,我们模型的 PSI 在 0.1 到 0.2 之间的“警告”区域。我们现在需要了解哪些特征可能导致了漂移。输入 CSI。

特征稳定性指数(CSI)用于评估每个特征的稳定性或漂移,以便我们可以找到有问题的特征。由于 PSI 关注的是人口漂移对模型预测的影响,因此 CSI 关注的是了解要素分布是如何变化的。

使用它非常简单:我们只需应用与 PSI 相同的公式,但不是通过使用预测变量来宁滨数据,而是使用每个特征来创建条块:

# Fixed
print("CSI - Fixed-size bins")
for col in sample_initial.columns:
    csi_values = psi(sample_initial[col].values, sample_new[col].values, mode = 'fixed')
    csi = np.mean(csi_values)
    print(f'{col} -> {csi=:.4f}')# Quantile
print("\\nCSI - Quantile bins")
for col in sample_initial.columns:
    csi_values = psi(sample_initial[col].values, sample_new[col].values, mode = 'quantile')
    csi = np.mean(csi_values)
    print(f'{col} -> {csi=:.4f}')

在本例中,结果是:

所以 x1 几乎没有变化,x2 略有变化但仍在“安全区”上,而 x3 变化显著,很可能是造成种群漂移的特征。

结论

数据科学课程通常不涉及模型稳定性,但在处理生产模型时,模型稳定性确实非常重要。我希望这篇文章能帮助你理解它,并应用到你的管道中。

另外,请参考我的 GitHub 存储库,查看一个回归和一个分类示例:

https://github.com/vinyluis/Articles/tree/main/Model Stability

如果你喜欢这个帖子…

支持我一杯咖啡!

给我买杯咖啡!

看看这个很棒的帖子

参考

[1] Yurdakul,b .种群稳定性指数的统计性质。https://scholarworks.wmich.edu/cgi/viewcontent.cgi?article = 4249&context =学位论文

[2]伯克,m .人口稳定指数。【https://mwburke.github.io/data 科学/2018/04/29/人口-稳定-指数. html

清单 NLP 模型的行为测试

原文:https://towardsdatascience.com/checklist-behavioral-testing-of-nlp-models-491cf11f0238

确保你的 NLP 模型如预期的那样工作

动机

在新数据上测试时,您的 NLP 模型不会抛出任何错误。是否意味着可以用于生产?

没必要。ML 系统可以运行完成而不抛出任何错误,但是仍然可以产生不正确的输出。因此,测试您的模型的行为以确保它如您所期望的那样工作是很重要的。

作者图片

但是,为您的 NLP 模型进行测试可能会很困难。一些困难来自于需要:

  • 创建数据来测试您的模型
  • 知道测试什么以及如何创建测试

如果您可以快速创建测试数据集并使用这些数据集测试您的模型,这不是很好吗?这就是清单派上用场的时候。

什么是检查表?

Checklist 是一个开源的 Python 库,使得测试 NLP 模型变得容易。本库中的方法在文章超越准确性:带清单的 NLP 模型的行为测试中有描述。

清单特别关注行为测试。行为测试关注的是通过验证输出来测试系统的不同能力,而不需要任何内部结构的知识。

要了解 Checklist 的工作原理,请从安装库开始:

pip install checklist

将清单扩展添加到 Jupyter:

jupyter nbextension install --py --sys-prefix checklist.viewer
jupyter nbextension enable --py --sys-prefix checklist.viewer

我们还将下载一个空间模型用于本文:

python -m spacy download en_core_web_sm

最低功能测试(MFT)

最低功能测试(MFT)使用简单的例子来确保模型能够很好地执行特定的任务。例如,我们可能想要测试情绪模型在处理否定时的表现。

作者图片

让我们看看 TextBlob 的情感模型如何处理否定。

pip install textblob
0.3499999999999999

情感得分是[-1.0,1.0]范围内的浮动值。由于上述文本的得分为正,因此该文本的情感被归类为正。

看起来这个模型在这些例子中工作得很好。然而,我们需要创建更多的数据来测试该模型是否也适用于其他否定。幸运的是,Checklist 允许我们轻松地创建类似的测试数据。

创建数据

我们将使用 Checklist 的编辑器,使用一个模板和一个项目列表来创建多个示例。

['Staying at home is not a great option.',
 'Staying at home is not a good option.']

我们也可以使用{mask}让 Checklist 填入适合句子的单词:

['sleep is not a good option.', 'Rest is not a great option.']

现在我们知道了 Checklist 的编辑器是如何工作的,让我们使用它来创建一些示例:

相当酷!我们刚刚用几行代码创建了 200 个样本。

创建预测情绪的函数

接下来,我们将创建一个预测情绪的函数。请注意,该函数将返回句子情感是消极还是积极的概率,而不是分数。

array([[0.15, 0.85]])

从上面的输出来看,模型表明文本有 15%的几率是负面的,而文本有 85%的几率是正面的。

由于清单的测试需要一个返回元组(predictions, confidences)的函数,我们将使用一个包装器在predict_proba返回的置信度旁边添加预测。

(array([1]), array([[0.15, 0.85]]))

创建测试

既然我们已经创建了测试数据和预测功能,我们可以使用MFT(最小功能测试)来创建测试。

Predicting 200 examples

总结测试结果:

Test cases:      200
Fails (rate):    115 (57.5%)

Example fails:
0.0 Shipping is not an awful option.
----
0.5 Pinterest is not a time-consuming option.
----
0.5 Training is not an unfeasible option.
----

哇哦。200 次测试有 57.5%失败。我们不知道 TextBlob 的情绪在没有测试的情况下有如此高的否定失败率。

摘要还给出了一些失败测试的例子。要查看所有失败的测试,使用text.visual_summary()

作者 GIF

不变性测试

除了测试模型的功能,我们可能还想测试当输入的小部分稍微改变时,模型预测是否保持不变。这些测试被称为不变性测试(IV)。

作者图片

让我们为输入添加一些琐碎的元素,如标点符号、错别字、姓名和位置,看看情绪是否会发生变化。

从创建玩具数据集开始:

把数据变成空间的Doc:

创建一个向数据集添加扰动的函数,然后创建不变性测试:

添加标点符号

理想情况下,如果在句末加上标点符号,句子的情绪应该不会改变。让我们使用Perturb.punctuation测试 Textblob 是否对标点符号具有鲁棒性。

First sample before and after pertubation:
The cake is great.
The cake is great

Summary:
Predicting 10 examples
Test cases:      5
Fails (rate):    0 (0.0%)

酷!5 项测试全部通过。

添加错别字

如果我们给一个句子加上错别字,情感会保持不变吗?让我们使用Perturb.add_typos来找出答案。

First sample before and after pertubation:
The cake is great.
The cake isg reat.

Summary:
Predicting 10 examples
Test cases:      5
Fails (rate):    1 (20.0%)

Example fails:
0.9 The cake is great.
0.5 The cake isg reat.

添加错别字会导致 20%的失败率。既然单词great变成了reat,那么句子的情绪变得既不积极也不消极也是有道理的。

更改名称

Michael改成Jackson应该不会改变一句话的情调。让我们用Perturb.change_names来测试一下。

First sample before and after pertubation:
Michael had fun traveling to Mexico
Jackson had fun traveling to Mexico
Jose had fun traveling to Mexico
Shawn had fun traveling to Mexico
Steven had fun traveling to Mexico
Jackson had fun traveling to Mexico
Noah had fun traveling to Mexico
Chad had fun traveling to Mexico
Bryan had fun traveling to Mexico
Stephen had fun traveling to Mexico
Derek had fun traveling to Mexico

Summary:
Predicting 22 examples
Test cases:      2
Fails (rate):    0 (0.0%)

酷毙了。该模型对于名称的改变是鲁棒的。

改变位置

First sample before and after pertubation:
Michael had fun traveling to Mexico
Michael had fun traveling to Afghanistan
Michael had fun traveling to Kenya
Michael had fun traveling to Peru
Michael had fun traveling to Morocco
Michael had fun traveling to Mexico
Michael had fun traveling to Canada
Michael had fun traveling to Germany
Michael had fun traveling to Algeria
Michael had fun traveling to Indonesia

Summary:
Predicting 11 examples
Test cases:      1
Fails (rate):    0 (0.0%)

不错!当我们改变句子中的位置时,所有的测试也通过了。

改为相关名词

我们也可以创建自定义的扰动函数。让我们创建一个函数,将每个句子的第一个名词变成一个相关名词(例如,将“椅子”换成“桌子”)。

然后将该函数添加到我们的IV测试中:

First sample before and after pertubation:
The cake is great.
The game is great.
The movie is great.
The book is great.
The food is great.
The story is great.

Summary:
Predicting 20 examples
Test cases:      4
Fails (rate):    0 (0.0%)

不错!改变每个句子的第一个名词并不能改变他们的情绪。

方向性期望测验

在不变性测试中,我们期望扰动后的输出是相同的。然而,有时我们可能期望扰动后的输出变为变化。这就是方向性预期测试派上用场的时候了。

作者图片

期待变化

要了解方向性期望测试是如何工作的,首先要创建一个期望。在这里,我们期望原来的预测和它的否定是不一样的。

接下来,向数据集添加求反:

[['The cake is great.', 'The cake is not great.'],
 ['Michael had fun traveling to Mexico',
  "Michael didn't have fun traveling to Mexico"]]

使用DIR测试我们的期望:

Predicting 6 examples
Test cases:      3
Fails (rate):    2 (66.7%)

Example fails:
0.5 Anna hates party.
0.1 Anna doesn't hate party.

----
0.7 Michael had fun traveling to Mexico
0.7 Michael didn't have fun traveling to Mexico

啊哈。测试失败是因为Michael had fun traveling to Mexico和它的否定有相同的情感。

期望单调递减

如果你不仅期望预测是不同的,而且期望正的预测概率不上升(单调下降),那该怎么办?这时候Expect.monotonic就派上用场了。

让我们创建一个包含所有肯定句的新数据集:

由于所有的句子都是肯定的,我们期望在加上否定之后肯定的预测概率会降低:

tolerange=0.1表示如果预测概率上升小于 0.1,我们不认为测试失败

现在,让我们向数据集添加否定,并测试是否所有样本的情绪都降低了:

First sample before and after pertubation:
The cheesecake is great.
The cheesecake is not great.

Summary:
Predicting 8 examples
Test cases:      4
After filtering: 2 (50.0%)
Fails (rate):    1 (50.0%)

Example fails:
0.5 Anna loves party.
0.8 Anna doesn't love party.

有意思。一个测试失败了,因为Anna doesn’t love partyAnna loves party更有可能是阳性的。

结论

恭喜你!您刚刚学习了如何使用检查表测试您的 NLP 模型。我希望这篇文章能给你动力去测试你自己的 NLP 模型,以确保它如你所愿的那样工作。

随意发挥,并在这里叉这篇文章的源代码:

https://github.com/khuyentran1401/Data-science/blob/master/nlp/checklist/checklist_examples.ipynb

我喜欢写一些基本的数据科学概念,并尝试不同的数据科学工具。你可以在 LinkedIn 和 Twitter 上与我联系。

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

Cherry Pickup 自底向上动态编程解释

原文:https://towardsdatascience.com/cherry-pickup-bottom-up-explanation-ecc14487db05

发展您在动态编程中的抽象技能

问题

这个动态编程问题 Cherry Pickup 在 solution 选项卡中有一个自底向上的解决方案,对于像我这样的初学者来说解释得不够充分,所以本文旨在阐明解决方案中每个元素的物理意义。

《樱桃》由布鲁诺创作,《荆棘》由 CC BY-SA 授权的未知作者创作

目标是仅使用单下右移动从左上移动到右下,并沿路收集樱桃,然后仅使用单上左移动返回到左上,并沿路收集樱桃,最后在这两条路径后收集最多的樱桃。

对这个问题感兴趣的人应该首先尝试使用自上而下的方法解决 leetcode 问题,也许可以考虑自下而上的解决方案。这篇文章是为那些已经放弃,盯着自下而上的解决方案,仍然不知道发生了什么。

自下而上的解决方案(稍作编辑,使用 python 3 的范围,而不是 python 2 的 xrange)

自下而上的解决方案

我对这个解决方案的疑问

  1. 为什么range(1, 2*N - 1)要进行图层迭代?
  2. I,j 通过range(max(0, t-(N-1)), min(N-1, t) + 1)的迭代在做什么?为什么有这些界限?
  3. 指数grid[i][t-i]grid[j][t-j]是什么意思?
  4. val是如何计算的,i != j是什么意思?
  5. 为什么使用嵌套循环列表理解来完成dp2更新?
  6. 为什么会有dpdp2两个互相依赖的 2D 阵列?

照片由杰米街Unsplash 上拍摄

基本上,我对解决方案一无所知。在将**Debug: Inline Values**设置为 True 并对其他输入进行测试的情况下,在 vscode 中调试完调试器后,事情变得更加清晰了。

准备工作

对我来说,理解解决方案最难的部分是记住dpdp2指的是抽象的概念,而不是网格上的物理位置。dp[1][1]不代表两个人都在grid[1][1]。当跟踪算法时,读者必须不断地在心理上在dp数组和grid数组之间切换,这是令人困惑的,因为它们都具有相同的正方形形状和大小。

溶液流动

作者图片:动态编程的层次

使用 dp 变量逐层构建 dp2 数组,保存上一层的 dp2。每个 dp2 都需要填充其相关的单元格。dp2中的每个相关单元格是两部分的总和

  • 来自dp的 4 个先前状态中的最佳状态
  • 这两个人在网格中所处的当前状态的值(处理了重复计算)。

所有层做好之后,在 dp 中从右下角单元格读取整体答案。

并非每个dp2层中的所有单元都会覆盖它们的-inf,原因有二:

  • 网格中的-1 使一些路径不可能(稍后解释)
  • 人们必须每次移动一步,而不是静止不动的约束
    -如果 t = 1,任何人都无法走到第 2 行或第 2 列
    -如果 t = 3,任何人都无法停留在 N=3 网格中的第 0 行或第 0 列

1。为什么 **range(1, 2*N - 1)** 要进行图层迭代?

为了理解这一点,我们可以模拟几个 N 值,看看我们得到了什么。

N = 2 → t =范围(1,2 * 2–1)= 1,2

N = 3 → t =范围(1,2 * 3–1)= 1,2,3,4

N = 4 → t =范围(1,2 * 4–1)= 1,2,3,4,5,6

在任何时刻,t都代表着两个人到目前为止已经走了多少步。

请注意,这两个人不可能在任何时候采取不同数量的步骤,因为根据算法设计,我们强迫两个人从一个子问题前进到下一个子问题。

作者图片:重复计算是如何发生的

这样的设计有助于处理两个人可能摘同一个樱桃的情况,并避免了在一些其他回溯解决方案尝试中标记/取消标记网格的需要。

t中的最后一个值显示了从网格的左上单元走到右下单元所需的步数。对于大小为 3 的网格,需要 4 步,因此最大的t值是 4。不管你怎么走,任何{下,右}的顺序都需要 4 步才能完成 3 格。

为什么自下而上不像自上而下的 O(n)空间?

我的一个外围问题是,既然自顶向下的解决方案中有 3 个变量在变化(r1, c1, c2),为什么自底向上的解决方案不是具有 N 空间复杂度的三维解决方案。这个想法是不需要存储整个t(步骤)维度,因为它可以被压缩成一个dpdp2的交互。

作者图片:将 t 维压缩成多个 dp 和 dp2

每个转移到下一个子问题都需要两个人的 1 步,所以下一个子问题只依赖于前一层。这类似于使用 2 个变量来跟踪 O(1)空间解决方案的 2 个步骤的历史,而不是使用 O(n)数组来跟踪算法开始以来的整个历史,来求解斐波那契。共同点是,如果子问题的依赖行为足够简单,可以硬编码成几个变量,则可以节省一维空间。

为什么压缩 t 维而不是其他 2 维?

除了 t 维,您可以选择将这种“只跟踪必要的历史”的想法应用到其他 2 维中的任何一个,只是简化 t 维是最容易解释和调试的。想象一个三维长方体,有三个不同的轴,你可以沿着这些轴将这个长方体切割成许多 2D 切片。虽然你只能将一维压缩成变量,但尝试在二维上进行,并且没有足够的历史记录来完成自下而上的解决方案。不知道这种“只允许一维压缩”的直觉是否适用于所有问题,或者只是樱桃皮卡,希望有经验的人可以在这里附和。

2。I,j 通过**range(max(0, t-(N-1)), min(N-1, t) + 1)**T12 的迭代在做什么?为什么有这些界限?

这里有几个移动的部分,N 是固定的,t变化,但也有一些最小值/最大值,所以让我们用例子来理解 N = 3 的情况。

t = 1 → max(0,1-(3–1))= max(0,-1) → 0,min(3–1,1) = min(2,1) = 1

t = 2 → max(0,2-(3–1))= max(0,0) → 0,min(3–1,2) = min(2,2) = 2

t = 3 → max(0,3-(3–1))= max(0,1) → 1,min(3–1,3) = min(2,3) = 2

t = 4 → max(0,4-(3–1))= max(0,2) → 2,min(3–1,4) = min(2,4) = 2

我们停在 4,因为 N = 3 最多有 4 层。

作者图片:I,j 对于每个 t 的迭代范围

概括地说,用于填充每层的dp2ij迭代值(包括端点,因为在上述计算中,范围 I 的端点处有一个+1 ):

t = 1 → 0,1

t = 2 → 0,1,2

t = 3 → 1,2

t = 4 → 2

为什么会有这些界限?

在解释这一点之前,我们必须暂时跳到问题 3 来理解这些指数的物理意义。

3。指数 **grid[i][t-i]** **grid[j][t-j]** 是什么意思?

来自樱桃皮卡的官方解决方案:

*t* 时刻,让 *dp[c1][c2]* 成为两个人从 *(0, 0)* *(r1, c1)* *(0, 0)* *(r2, c2)* ,其中 *r1 = t-c1, r2 = t-c2*能够摘到的最多的樱桃

我怀疑上面的语句中存在行和列交换的错误。因为ij被用作 grid 中的第 1 个索引,所以我相信它们应该指的是行。

我认为应该这样理解:

*t* 时刻,让 *dp[r1][r2]* 成为两个人从 *(0, 0)* *(r1, c1)* *(0, 0)* *(r2, c2)* 能摘到的最多的樱桃,其中 *c1 = t-r1, c2 = t-r2*

这意味着dpdp2中的第一个值是第一个人当前在网格中的哪一行,而dpdp2中的第二个值是第二个人当前在网格中的哪一行。因为t是已知的,这意味着通过做t-r1t-r2我们也知道两个人在哪个列,所以dp[r1][r2]完全描述了两个人的坐标。

重要警告

图片作者:注意你在哪个图层的 dp2

很重要的一点要注意,跨层时,t会发生变化。因此,t = 1 的dp[0][0]与 t = 2 的同一个dp[0][0]相比,在网格上具有完全不同的物理意义。

当 t = 1 时,两个人都走了 1 步。因为dp[0][0]意味着两者在 1 步后仍然在第 0 行,所以它们必须都移动到右 1 列并且都在grid[0][1]

当 t = 2 时,两个人都走了 2 步。因为dp[0][0]意味着两者在两步后仍在第 0 行,所以它们必须都移动到右边的 2 列并且都在grid[0][2]

回到这个问题,grid[i][t-i]grid[j][t-j]则分别表示网格上第一个人和第二个人的行列坐标。它们有两个用途:

  • 检查这两个人中是否有人走到网格上的-1 单元格上(这种状态是不可能的,因此不需要用任何有用的值更新dp2中的相应单元格)
  • 访问网格,将当前州的樱桃(可能是 0 或 1)添加到来自dp的前 4 个州的最佳答案之上

回到为什么这些界限

t = 1 → 0,1

t = 2 → 0,1,2

t = 3 → 1,2

t = 4 → 2

作者的图像:可视化相关的 dp2 区域以填充每个层

正如前面提到的为什么dp2中的一些单元格不会被更新的“解流”部分,有一个约束条件,人们必须每次移动一步,而不是静止不动。

当 t = 1 时,只有dp2左上方的 2x2 方块需要更新,因为没有人可以只用一个时间步长来踩第 2 行或第 2 列。

当 t = 2 时,整个dp2都是可能状态

当 t = 3 时,只有dp2右下方的 2x2 方块需要更新,因为在 3 个时间步长后,没有人能停留在行 0 或列 0。

当 t = 4 时,两个人肯定都在网格的右下角

如果我们只是遍历整个 NxN 并检查非法方块呢?

我考虑过这是否会使循环边界更容易编写,但后来意识到要检查的条件已经嵌入到优化的循环中了!
例如,如果我们简单地做了for i in range(N)for j in range(N),并为continue在当前的 2 个条件中添加了更多的条件,则这些条件将类似于以下内容(因为它们需要对要排除的内容取反,而不是包含的内容):

  • r1 < N, r1≤ t (same 2 conditions for r2) to prevent any person stepping further than the accumulated time step allows him to go
  • r1 ≥ t-(N-1) (same for r2) to prevent any person (impossibly) staying at row 0 when he has used up all the columns in the grid after a certain number of time steps (so is forced to step down the rows).

Rather than adding more conditions in 【 which makes the code harder to read and longer to run, they are already elegantly expressed in 【 and 【 . N for upper bound was changed to N-1 to compensate for/allow factoring out a +1 at the end of 【 .
我更喜欢把上界写成min(N,t+1)来去掉结尾奇怪的+1,而不是强制 N-1。

4。 **val** 是如何计算的 **i != j** 是什么意思?

作者图片:计算 val 的两种情况

val是算法的两个部分之一,它决定什么进入每层的 dp2 中的每个单元。它查看 person 1 在网格中的位置以获取其值(可能有樱桃,可能没有,但求和 0 不会有什么影响,所以只求和)。i != j是有条件的,因此只有当第二个人与第一个人不在同一个格子中时,才计算第二个人的樱桃(如果他在格子中有 1)。这种检查是足够的,因为当两个人在网格的相同位置时,他们将具有相同的行索引,所以i = j。它们的列索引也将是相同的,因为在任何t,对于相同的t,两个人的列索引t - it - j将是相同的列。

案例 1: i = j

上面的绿色箭头dp2[1][1]演示了这一点。val为 0,由于两人在网格中处于同一位置,所以只有一份被加到dp2[1][1]

案例二:我!= j

dp2[0][1]的蓝色箭头表示网格中 2 个不同位置的 2 个 val 值被加到dp2[0][1]中。

这两者只对答案的第一部分有贡献。第 2 部分在问题 5 中解释。

5。为什么 **dp2** 更新是使用嵌套循环列表理解完成的?

除了val之外,决定dp2中每个单元格的内容的第二部分是dp2中当前位置左上方的dp中的 2x2 方块单元格。该算法按照红色、蓝色、橙色、绿色的顺序进行检查。

if pi >= 0 and pj >= 0处理红色、蓝色、橙色支票超出dp边界的次数,以防止 IndexError(也因为那里没有物理意义上的信息)。

图片由作者提供:查看 dp 中的四个 2x2 方块,以更新 t=2 时的 dp2

为什么检查前一层 dp 中的 4 个单元

4 个单元,因为当前dp2中的每个单元可以从dp中的 4 个状态到达。

再次注意到dp中的这 4 个状态并不对应于物理网格上的 2x2 正方形,而是前一层中两个人的行位置的更抽象的概念。

关注 t = 2 的绿色单元格dp2[1][1],它来自于在 t = 1 时从dp2创建的dp中的 2x2 单元格。物理意义是人 1 在网格上的第 1 行(也意味着第 1 列,因为 t = 2,c1 = t - r1),人 2 也在网格上的相同位置。

  • 左上(1):
    这是当两个人都在第 0 行第 1 列时(因为 t = 1),这意味着两个人都从他们在 t = 1 时的位置向下移动 1 行到达 t = 2 时的当前状态。
  • 右上(2):
    这是当人 1 在第 0 行第 1 列,人 2 在第 1 行第 0 列时,意味着当前状态是人 1 向下移动 1 行,人 2 向右移动 1 列。
  • 左下(2): 这是当人 1 在第 1 行第 0 列,人 2 在第 0 行第 1 列时,意味着人 1 向右移动 1 列,人 2 向下移动 1 行就到达了当前状态。
  • 右下(1): 这是当两个人都在第 1 行第 0 列时,意味着两个人都向右移动 1 列达到当前状态。

我们可以使用上面的理解来跟踪 t = 1 时从带有单个 0 的初始化 dp 到dp2的值,看看它是如何工作的。记住val也影响当前dp2单元格的值。

6。为什么会有 **dp** **dp2** 两个互相依赖的 2D 数组?

这两个 2d 数组是为了节省空间而创建的,因此我们不使用 3D 数组,而只使用两个相互依赖的 2D 数组。上面问题 1 的解决方案流程和解释应该已经清楚了这一点。

如果输入为-1 怎么办

敏锐的读者会发现我的输入网格将两个-1 值替换为 0。这样做是为了更好地展示在最简单的情况下dp2会是什么样子,并展示range(max(0, t-(N-1)), min(N-1, t) + 1)界限是如何工作的。

作者的图像:输入包含-1 的情况

如果我们使用 leetcode 上给出的原始网格

  • grid[0][2]处的-1 将导致 t = 2 中 2 条红线下的单元格全部为-inf
  • grid[1][2]处的-1 将导致 t = 3 中 2 条红线下的单元格全部为-inf。

不过这并不影响最终的答案。

为什么网格中的-1 会导致红线保持在-inf 处

以 t = 2 为例。

网格[0][2]处的 A -1 将使dp2[0][x] (x 表示任何列)处的所有结果都不可能,因为行索引为 0 意味着人员 1 将 2 列向右移动了 2 步。人 1 遇到了那条不可能的路径,所以无论人 2 在哪里,这条路径都不能保存在dp2中。只需将 1 号人物和 2 号人物换成dp2[x][0]的理由。

为什么 dp2 看起来是对称的?

任何层的dp2都将是对称的。我们重点来看一下 t = 1 的dp2的 2x2 *方(1,2,2,1)。

图片作者:看到对称了吗?

  • 右上角红色状态,带 2 in dp2[0][1]。表示第 0 行第 1 列的人员 1(红色圆圈),第 1 行第 0 列的人员 2(红色三角形)。
  • dp2[1][0]左下角蓝色状态 2 表示第 1 行第 0 列的人 1(蓝色圆圈),第 0 行第 1 列的人 2(蓝色三角形)。

我们看到的是,尽管樱桃数量出现在相反的对角线上,但它们覆盖了网格中的相同路径,只是人 1 和人 2 被交换了。这种模式只在dp2中出现在非对角线上,因为总是有 2 种方式来安排人员 1 和 2,同时共同覆盖网格中相同的 2 个非对角线正方形,但是只有 1 种方式让他们覆盖网格中的对角线正方形,因此在dp2中也只有 1 个对角线值。

回到最初的问题描述,这整个人 1,人 2 的事情只是一个虚构的概念,以帮助制定动态编程中的状态变量。无论您沿着路径 A 到达任何(可能是中间)状态并返回路径 B,还是沿着路径 B 到达相同的状态并返回路径 A,都不应该改变在该状态下采摘的樱桃数量。

在层之间移动,我们从对称的dp开始,然后对dp2中的每个单元应用相同的 2x2 左上检查,这意味着这种对称通过层传播。

这意味着你实际上可以填充dp2的上/下三角形,仍然得到相同的最终答案。然而,为了易于实现循环索引,并且由于空间复杂度没有提高一个数量级,填充整个dp2方块看起来不错。

是什么让这个问题变难了?

  1. 将返回路径逆转为第二条前进路径的创造力
  2. 知道迫使每个人每次移动 1 步会使他们在相同步数后到达相同的单元,并且这允许方便地处理重复计数,而不需要标记/取消标记网格
  3. 建立状态的抽象表示,而不是直接跟踪grid,并使用dp2t自下而上地这样做,而不是 r1、c1、c2
  4. 正确生成ij的索引,并将每个dp2单元正确链接到前 4 个状态

是什么让自下而上的解决方案比自上而下的更难?

  1. 使用t步长变量提取两个人的坐标,并使用t作为最外层循环逐步构建解决方案
  2. 知道任何时候只需要 2 层,而不是全部的t历史,所以只需要 O(n)个空间,而不是自顶向下的 O(n)

照片由梅尔特·卡赫维奇Unsplash 拍摄

引人深思的事

  1. 迭代方向在自底向上中什么时候重要,或者说重要吗?
  2. 自下而上的求解路径是否正好与自上而下的路径相反?总是吗?
  3. “相反”到底是什么意思?当我们超越 1 维时是否存在对立面?
  4. 这个问题不是通过两个人同时行走,而是只有一个人,并更新网格状态来解决的吗?为什么或为什么不?是可解性问题还是效率问题?
  5. 如果这个问题被调整为需要 3 次行走(在网格的右下方结束),那么需要 3 个人,如何建模?
  6. 如果超过 3 个人,自下而上比自上而下节省的空间会随着人数的增加而增加还是保持不变?
  7. 对于任何一个 DP 问题,最多 1 维可以空间优化吗(比如这个例子中的 t)?那一定是在最外面的循环吗?
  8. 为什么自上而下用 r1,c1,c2 表示状态,而自下而上用 t,r1,r2?通过使用彼此的表示,这两种方法还能工作吗?既然 t 几乎是 2N 而不是 N,那么用 t 表示是不是浪费空间/时间?

我对问题 8“依赖性魔方的诅咒”的部分尝试:

如果我们将 r1 c1 c2 用于自下而上,则无法将 3D 更新关系压缩到 2D 中,也无法像使用 t、r1、r2 那样将 1 维分解到 for 循环中。
为什么?查看 leetcode 上给出的自顶向下解决方案的以下状态转换:

  • 人 1 下,人 2 下:dp[r1+1][c1][c2]
  • 人 1 右人 2 下:dp[r1][c1+1][c2]
  • 人 1 下人 2 右:dp[r1+1][c1][c2+1]
  • 人 1 右和人 2 右:dp[r1][c1+1][c2+1]

无论您选择 r1、c1 还是 c2,它们中的任何一个都将使 4 个移动中的 2 个保持相同的值,其他 2 个取决于+1(如果您从左上角的网格[0][0]作为基础情况到网格的右下角构建底部,则必须翻转到-1,否则如果您从相反方向的网格[-1][-1]底部向上,则可以是+1)。

无论 4 个中有多少个,我的重点是阻止在*面块中构建历史的依赖性的不均匀性。使用 t,r1,r2 表示允许对 t-1 的均匀依赖,这是这种自底向上解决方案的精髓。

伸展你解决问题的肌肉

  1. 初学者:接触其他动态编程问题,配以自上而下的樱桃捡拾方法图解:https://towards data science . com/mastering-Dynamic-Programming-a 627 dbdf 0229
  2. 中级:简明背包利弊来自最喜欢的答案 by【删除】:https://www . Reddit . com/r/cscareerquestions/comments/c6t7a 0/dynamic _ programming _ problems _ are _ they _ important/
  3. 高级:动态编程中的优化机会(古老而珍贵):https://codeforces.com/blog/entry/8219

最终外卖

在这个练习之前,我一直认为自底向上的状态跟踪空间中的每个单元格都必须填满,但这个问题证明了相反的情况。
然而,这种怪癖只是特定于这个问题的约束和表示,其中人必须移动,并且在每一步 t 只移动 1。

因此,考虑用于表示 DP 状态的每个维度的可能值的范围很重要。这些可能的值在各个维度上可能并不独立。在这个问题中,每个 r1、r2 的范围依赖于 t。(在上面的部分可视化,回到为什么这些边界部分)。

我希望这篇文章给你留下一个框架来分解其他自下而上的解决方案,并寻找机会做空间优化。我的 6 个问题是按照代码阅读顺序问的,但是你可能会找到一个更有效的提问和理解算法的顺序。

卡方分布简单解释

原文:https://towardsdatascience.com/chi-square-distribution-simply-explained-87f707ba631a

卡方分布及其起源的简单解释

micha Parzuchowski 在 Unsplash 上的照片

介绍

在我之前的文章中,我们推导并讨论了伽玛分布,你可以在这里查看:

在这篇文章中,我们将解释伽玛分布的一个特例,卡方分布。这种分布在统计学中无处不在,甚至有它自己的检验,在数据科学中经常使用,卡方检验。

我们将通过推导其概率密度函数(PDF) 来了解分布的起源,并展示其与伽马分布的关系。

起源

卡方分布, 𝜒2 ,是来自标准正态分布:v 随机自变量相加的结果

作者在 LaTeX 中生成的方程。

我们来分解一下这个表达式。

X 是从标准正态分布中抽取的随机变量。这是一个正态分布,均值为零,方差为一N(0,1):

作者用 Python 生成的图像。

vhttps://en.wikipedia.org/wiki/Degrees_of_freedom_(statistics)自由度,是我们从分布中取样的变量的数量。所以,如果我们采样一个变量, v = 1 ,从标准正态分布:****

作者在 LaTeX 中生成的方程。

卡方分布如下所示:

作者用 Python 生成的图像。

这有道理吗?当我们对这些值求*方时,它们将会显著减少或增加并变为正值。此外,由于对-1 和 1 之间的变量进行采样的概率相对较高,因此将它们*方意味着它们将变得更小。因此,我们观察到大部分密度在左手边。

当我们有三个自由度, v = 3 😗* 时呢**

作者在 LaTeX 中生成的方程。

绘制分布图:

作者用 Python 生成的图像。

我们看到,这些值*均要大得多,而且更加左倾。这是有意义的,因为当我们有更多的自由度时,我们更有可能采样高数值。求和也自然导致更高的值。

所以这就是全部了!卡方是从标准正态分布中选取的值的*方。

导出概率密度函数****

现在我们要推导一个一自由度卡方分布的https://en.wikipedia.org/wiki/Probability_density_functionv = 1

让我们从标准正态分布的 PDF 开始:

作者在 LaTeX 中生成的方程。

作者在 LaTeX 中生成的方程。

为了计算 PDF,我们需要找到 累积密度函数(CDF) ,然后简单地对其进行微分以返回 PDF。综合发展框架的定义是:

作者在 LaTeX 中生成的方程。

将它应用到卡方公式中,我们想要找到 𝑋 的 PDF:

作者在 LaTeX 中生成的方程。

这个推导需要很多积分和替换,所以我省略了全部证明,因为我不想这篇文章变成一篇研究论文!不过,这里有一个很棒的帖子和这里的网页把整个证明过的很彻底很干净。

然而,通过进行微分,最终的 PDF 等于:

作者在 LaTeX 中生成的方程。

事实上,这只是一个自由度的 PDF。 v 自由度的 PDF 为:

作者在 LaTeX 中生成的方程。

其中γγ函数。对于半正整数值,函数的形式为:

作者在 LaTeX 中生成的方程。

通过设置 v = 1, 可以看出,我们获得了上面推导的一个自由度的 PDF。

与伽马分布的关系

我们知道伽马分布由两个值参数化:

  • λ,事件发生率
  • n ,你所等待的事件的数量

而它的概率密度函数是:

作者在 LaTeX 中生成的图像。

其中 x 是感兴趣的随机变量。

为了充分理解这个表达的来源,我建议阅读我之前关于伽马分布的文章。

通过设置 n = v/2λ = 1/2:

作者在 LaTeX 中生成的图像。

我们证明了卡方分布只是伽玛分布的一个特例!

结论

在这篇文章中,我们展示了如何从第一原理和伽玛分布中推导卡方分布。在我的下一篇文章中,我们将使用分布来解释卡方检验!

和我联系!

(所有表情符号都是由 OpenMoji 设计的——开源的表情符号和图标项目。许可证: CC BY-SA 4.0

卡方拟合优度检验

原文:https://towardsdatascience.com/chi-square-goodness-of-fit-test-7774d3410896

数据科学中卡方拟合优度统计检验的简明解释

Riho Kroll 在 Unsplash 上拍摄的照片

介绍

我们最*探索并推导出了卡方分布,您可以在此查看:

如果您对卡方分布不熟悉,我强烈推荐您阅读这篇文章,否则这篇文章对您来说毫无意义!

然而,在本帖中,我们将讨论卡方检验中的一种,拟合优度检验。

该测试用于验证我们的样本数据分布是否与该数据的某些预期分布一致。换句话说,它决定了样本和预期分布之间的差异是随机产生的,还是具有统计学意义。

在本文中,我们将深入探讨拟合优度测试背后的数学原理,并通过一个例题来获得我们的直觉!

测试的假设

  • 有一个分类变量
  • 观察是独立的
  • 每个变量中每个类别的频数应该大于 5
  • 每组数据中的频数必须互斥****
  • 数据被随机采样

卡方检验统计量

和每一个假设检验一样,我们需要找到一些检验统计量。对于卡方检验,它是:

作者在 LaTeX 中生成的方程。

  • v 是自由度
  • O 是来自样本的观测值
  • E 是总体的期望值
  • n 是变量中的类别数

当我们看一个例子时,这个公式会更有意义。

注意卡方分布来自分子的*方

假设检验步骤

  • 定义空、 H_0、 和候补、 H_1 ,假设。
  • 决定你的显著性水* ,这是未能拒绝或拒绝零假设的概率阈值。通常选择值 5% ,其将对应于依赖于分布的某个 临界值
  • 计算 测试统计量 ,在我们的例子中,它将是上面给出的卡方统计量。
  • 将测试统计值与临界值进行比较。如果它更大,那么我们拒绝零假设,否则我们无法拒绝零假设(这是一个右尾检验)。

为了更深入地理解假设检验和临界值,我建议阅读我的关于置信区间和 Z 检验的帖子,它将上述步骤进一步分解:

**

也有许多 youtube 视频网站也做了大量假设检验步骤。

工作示例

让我们看一个非常简单的例子。

一家糖果店声称,每个巧克力球袋含有 70%的牛奶巧克力球和 30%的白巧克力球。

我们挑选一个巧克力球袋,里面有 50 个球。在这个包里,30 个是牛奶巧克力,20 个是白巧克力。这是 60%牛奶巧克力和 40%白巧克力的混合物。

这个观察结果符合糖果店的说法吗?

假设

  • H_0 : 糖果店的说法是 正确的 为每个巧克力球袋中的白巧克力*分 70-30%的牛奶
  • H_1 : 糖果店的说法是 不正确的 为每个巧克力球袋中 70-30%牛奶兑白巧克力的比例

在本例中,我们将使用 5%的显著性水*

相依表

我们从小样本中计算观察到的和预期的巧克力类型,并将其显示在列联表中:

由作者生成的图像。

检验统计量

现在,我们使用上面显示的公式计算卡方检验统计量:

作者在 LaTeX 中生成的方程。

自由度,df ,公式为:

作者在 LaTeX 中生成的方程。

其中 n 是我们之前说过的变量中的类别数。因此,在我们的例子中,它就是简单的 df = 1。

临界值

使用卡方表,1 自由度的 5%显著性水*对应的临界值3.84

因此,由于我们的统计值低于临界值,我们无法拒绝零假设。

注意:卡方检验通常是单尾检验。这个证明的范围超出了本文的范围,但是在 StackExchange 上有一个很好的答案可以解释为什么会这样。

结论

在这篇文章中,我们已经走过如何进行卡方拟合优度测试。该测试确定单个分类变量的样本分布是否与预期分布一致。

在我的下一篇文章中,我们将讨论另一个卡方检验,独立性检验。这经常用于数据科学中的特征选择

和我联系!

(所有表情符号都是由 OpenMoji 设计的——开源的表情符号和图标项目。执照: CC BY-SA 4.0**

详细相关性检验的卡方检验:手动和 Python 实现

原文:https://towardsdatascience.com/chi-square-test-for-correlation-test-in-details-manual-and-python-implementation-472ae5c4b15f

侯赛因·巴德沙阿在 Unsplash 上拍摄的照片

检查两个变量是否相互独立

相关性测试在统计学和机器学习中非常常见。在统计学中,相关性检验对于理解不同变量对总体的影响很重要。例如,假设我们为七年级学生开发了一门新的数学课程。在我们的数据集中,我们有两组学生。一个群体的父母教育水*是大学水*。另一组学生的父母没有大学学历。我们可能想知道父母是否受过大学教育对学生新的数学课程的成功有任何影响。

在机器学习中,相关性测试可以用于特征选择。在输出变量是分类变量且输入变量也是分类变量的分类问题中,可以使用卡方检验来了解输入变量是否与输出变量相关。

本文将集中讨论卡方检验,以确定两个变量之间是否存在相关性。

我们将通过一个例子一步步地学习这个过程。

首先,我将展示手动完成整个过程的方法,稍后我们将使用 Python。

以下是我们将用来演示如何执行卡方检验的数据:

上表中显示的两个变量来自一个大型数据集。请随意从以下链接下载数据集:

https://www.kaggle.com/aljarah/xAPI-Edu-Data

使用这个数据集的权限就是这里所说的。

有两个栏目:学生的成绩和家长是否回答了调查问题。我们可能想知道回答调查问题的父母是否与成绩有关。让我们看看这两个变量是否相关。

这是列联表:

你可以在这个列联表的每一栏中看到,在“是”和“否”组中,每个年级的数字都有所不同。在这种情况下,卡方检验的目的是了解这些差异是偶然的还是有统计学意义的。

零假设:两个变量(ParentAnsweringSurvey 和 GradeID)不相关。他们是独立的。

要开始这个测试,我们需要从计算期望值开始。期望值是多少?

期望值是如果零假设为真时所期望的值。

这里我展示了两个期望值的计算,例如:

当“家长回答调查”为“否”时,G-02 的期望值为 74:

e02(否)= (210*147)/480 = 64.31

这里,480 是如上表所示的总计数。

这是另一个例子。当“家长回答调查”为“是”时,G-08 的期望值为 77:

e08(是)= (270*116)/480 = 65.25

这样我们可以计算整个表的期望值。下表显示了每个单元格的预期值:

现在,使用列联表中的原始值和期望值,我们可以计算卡方统计。我们需要计算每个单元格的卡方值,并将它们相加。这里我展示了第一个单元格的 chi2 检验统计量计算:

CH2 _ 02 _ no =(74–64.31)/64.31 = 1.459

同样,您也可以计算其余的单元格。以下是完整的表格:

从上表可以看出,总数是 16.24。因此,我们的 chi2 检验统计值是 16.24。

我使用 excel 表来计算期望值和卡方检验统计。

在我们得出结论之前还有几个步骤。

我选择了 0.05 的显著性水*。

自由度是:

(行数-1)*(列数-1)

在列联表中,行数为 2,列数为 10。

所以,自由度是:

(2–1)*(10–1) = 9

使用下面的 chi2 分布表,对于 0.05 的显著性水*和 9 的自由度,我们得到 chi2 临界值 16.92。

因此,我们计算的 chi2 检验统计量(16.24)小于我们从分布中得到的 chi2 临界值。所以,我们没有足够的证据来否定零假设。

基于 chi2 检验,两个变量 (ParentAnsweringSurvey 和 GradeID) 不相关。

但与此同时,chi2 检验统计量与分布中的 chi2 之间的差异并不大。如果我们选择显著性水* 0.01 或 0.025,结果会有所不同。我们将能够拒绝无效假设。所以,这是一次侥幸脱险。

Python 实现

这里我用 Python 做了同样的卡方测试。使用编程语言制作列联表和所有计算要容易得多。

首先,我在这里导入数据集。这与我之前提供的链接中的 Kaggle 数据集相同。

import pandas as pd
pd.set_option('display.max_columns', 100)edu = pd.read_csv("xAPI-Edu-Data.csv")

这些是该数据集中的列:

edu.columns

输出:

Index(['gender', 'NationalITy', 'PlaceofBirth', 'StageID', 'GradeID', 'SectionID', 'Topic', 'Semester', 'Relation', 'raisedhands', 'VisITedResources', 'AnnouncementsView', 'Discussion','ParentAnsweringSurvey', 'ParentschoolSatisfaction', 'StudentAbsenceDays', 'Class'], dtype='object')

和前面的例子一样,我将使用同样的两个变量,这样你就可以比较过程和结果。所以,零假设也将和以前一样:

零假设:两个变量(ParentAnsweringSurvey 和 GradeID)是独立的。没有关联。

像往常一样,我们需要从制作一个列联表开始:

contingency = pd.crosstab(edu['ParentAnsweringSurvey'], edu["GradeID"])

这将为您提供完全相同的列联表:

这些是观察到的值。为了计算方便,我想把它们转换成一个数组。如果我只从 DataFrame 中获取值,它会给我一个数组:

observed_values = contingency.values
observed_values

输出:

array([[74, 25,  3, 16, 40, 39,  1,  1,  5,  6],
       [73, 23,  0, 16, 61, 77,  4,  3,  8,  5]], dtype=int64)

这些是数组形式的观察值。我们现在需要计算期望值。也就是说,如果零假设为真,这个列联表中的期望值是多少。

这次我不会手动计算期望值。我将使用 Python 的“scipy”库:

import scipy.stats as stats
val = stats.chi2_contingency(contingency)
val

输出:

(16.24174256088471,
 0.06200179555553843,
 9,
 array([[64.3125, 21\.    ,  1.3125, 14\.    , 44.1875, 50.75  ,  2.1875, 1.75  ,  5.6875,  4.8125],
[82.6875, 27\.    ,  1.6875, 18\.    , 56.8125, 65.25  ,  2.8125, 2.25  ,  7.3125,  6.1875]]))

输出是一个元组,其中第一个值是卡方统计 16.24。这也正是我们手动计算得到的结果。看,这里我们不需要自己计算卡方统计量。我们已经拿到了!

第二个值 0.062 是 p 值。如果我们认为显著性水*为 0.05,则 p 值大于显著性水*α。因此,我们不能拒绝两个变量( ParentAnsweringSurvey 和 GradeID )是独立的零假设。

输出 9 的第三个值是自由度。在前面的部分中,我们也是手动计算的。我们将使用它来找到 chi2 临界值。

我们还可以使用与前面相同的方法,将卡方统计量与卡方分布中的 chi2 临界值进行比较。我们也可以使用 stat 库从分布中找到卡方临界值。

alpha=0.05
dof = 9 #degree of freedomcritical_value = chi2.ppf(q = 1-alpha, df = dof)
critical_value

输出:

16.918977604620448

它是 16.92,大于卡方统计。所以,我们不能拒绝零假设。

我们之前在查找预期值时收到的 p 值默认考虑了 0.0.5 的显著性水*。如果考虑不同的显著性水*,可以使用卡方临界值来找出 p 值,如下所示:

n_rows = 2
n_cols = 10
dof = (n_rows-1)*(n_cols-1)p_value = 1-chi2.cdf(x=val[0], df = dof)
p_value

输出:

0.06200179555553864

我们得到了相同的 p 值,因为我们使用了显著性水*为 0.05 的卡方值。如果使用不同的显著性水*,p 值将会不同。请你自己试试。

结论

在本文中,我试图使用手工计算和 python 来详细解释独立性检验的卡方检验过程。希望对你有帮助!

请随时在推特脸书页面上关注我,并查看我的新 YouTube 频道

更多阅读

https://pub.towardsai.net/data-analysis-91a38207c92b </30-very-useful-pandas-functions-for-everyday-data-analysis-tasks-f1eae16409af>

卡方检验——如何使用公式和 Python 实现计算卡方

原文:https://towardsdatascience.com/chi-square-test-how-to-calculate-chi-square-using-formula-python-implementation-6da203f96569

凯利·西克玛在 Unsplash 上的照片

1.测试类型

卡方检验是非参数检验,也就是说,它不需要对抽样人群进行正态分布或方差假设。

2.目的

卡方检验的一般目的是比较离散的分类数据(计数数据)。例如,产品可以分为两类,如有缺陷/无缺陷,或者分为两类以上,如优秀、良好、一般和差。卡方检验非常适合于比较的两个变量都是分类变量的数据集。卡方检验将观察值与理论期望值进行比较。

3.范围

像卡方检验这样的非参数检验不如参数检验有效,也就是说,它们不太可能拒绝零假设,尤其是当它为假时。一些应用领域包括:

  1. 用于检查拟合优度的卡方检验用于检查观察值(实验值)和预期值(理论值)之间是否存在差异。它确定与过去相比,数据的分布是否保持相似
  2. 两个属性独立性的卡方检验用于检查两个特征是否独立。它用于确定分类结果变量(Y)是否与另一个分类预测变量(X)相关联

4.假设

  1. 样本是从人群中随机抽取的。这是将结果推广到全部人口所必需的
  2. 数据将以原始频率报告(计数,而非百分比)
  3. 观察是独立的
  4. 变量是互斥的(个人不能被分配到一个以上的类别)和穷举的(包括所有可能的上下文或类别)
  5. 在 2 x 2 表中,观察到的频率不算太小(n 必须相对较大);如果 n 小于 20 或任何单个细胞计数小于 5,则不应使用卡方检验

5.方法学

几种类型的卡方检验取决于所需的数据收集和检验。我们来看一个最简单的例子,它包括一个 2 x 2 的列联表。假设你对一组多发性骨髓瘤患者进行了一项药物试验,并假设接受药物治疗的患者与未接受药物治疗的患者相比,缓解率有所提高。让我们假设收集了以下数据。

图一。使用假设数据的列联表。图像—由作者使用 Excel 制作。

H0(零假设):各种情况下观察值的分布是相等的(观察到的频率不偏离预测的频率),即药物治疗不影响进入缓解的患者比例。

HA(替代假设):进入缓解期的患者比例与药物治疗有关,这表明观察结果并不是均匀分布在各个环境中的(观察到的频率确实与预测的频率不同)。

图一。检验统计量的公式。图像—由作者使用 Powerpoint 准备。

其中 O观测频率,而 E期望频率

6.计算卡方统计

6.1 计算预期频率

下表的卡方统计计算如下:

  1. 计算每个单元中的预期频率。每个单元格的预期频率(E)由(行总计 x 列总计)/总计给出
  2. 行总计表示给定上下文的观察总数(例如,有多少患者用药物 A 治疗?— 50 名患者)
  3. total 列表示给定因变量的观察总数(例如,有多少患者处于缓解期?— 66 名患者)
  4. 总计代表观察的总数(例如,参与研究的患者总数-105 名患者)

图 3。使用假设数据的列联表和期望值。图像—由作者使用 Excel 制作。

用药物 A 治疗的预期值×缓解计算为(50×66)/105 = 31.43。2.观察到的频率(O)是收集的数据,反映了实际数字,即对于用药物 A×缓解治疗,它是 36。

6.2 计算测试统计

使用公式χ2=∑[(O-E)2/E]计算统计值。
χ2 =(36–31.42)/31.42+(14–18..57) /18.57 + (30–34.57) /34.57 + (25–20.43) /20.43 = 3.418

6.3 计算自由度

自由度= (# columns (or)因变量—1)*(# rows(or)categories—1)=(2–1)(2–1)= 1

6.4 优势比

这是 2 × 2 列联表关联的另一种度量。在分类数据的最重要模型中,它作为一个参数出现。对于成功概率π,成功几率定义为;赔率=π/(1π)。

几率是非负的,当成功的可能性大于失败的可能性时,其值大于 1.0。例如,如果π = 0.75,那么成功的几率等于 0.75/0.25 = 3。当赔率= 4.0 时,成功的可能性是失败的 4 倍。

图 4。参考下面的比值比计算。图像—由作者使用 Excel 制作。

所以,比值比=(A 类成功几率)/(B 类成功几率)。在本例中,接受治疗的患者的缓解几率= 36/14 = 2.57,未接受治疗的患者的缓解几率= 30/25 =1.2。比值比由这两个比值给出,即 2.57/1.2 = 2.14。这一比率意味着接受治疗的患者比未接受治疗的患者更有可能获得缓解 2.14 倍。

计算比值比的捷径:
让我们考虑上面的例子,其中我们必须计算治疗类别中缓解相对于未治疗类别的比值比。在上表中,每个单元格的值都用一个字母表示。
那么,赔率会是(AD)/(BC)。对于我们的例子,优势比= (3625)/(1430) = 2.14。

6.5 解释

将计算的χ2 统计值与临界χ2 值进行比较,以确定是否拒绝零假设:

  1. 如果计算的χ2 >临界χ2,p≤0.05-表明有重要的统计证据支持拒绝零假设。我们偶然获得这一结果的概率小于或等于 5%,这是实验可接受的误差水*
  2. 如果计算的χ2 ≤临界χ2,p> 0.05 表示没有显著的统计证据支持拒绝零假设。我们偶然得到这个结果的概率大于 5%,这超过了任何实验可接受的误差水*

在我们的例子中,我们现在有了卡方统计(χ2 = 3.418)、预先确定的 alpha 显著性水*(0.05)和自由度(df = 1)。进入具有 1 个自由度的卡方分布表并沿行读取,我们发现我们的χ2 (3.418)值位于 2.706 和 3.841 之间。相应的概率是 0.07。因此,p 值大于可接受的显著性水* 0.05,具有 95%的置信区间(CI)(即 p > 0.05);我们没有足够的证据来否定无效假设。更简单地说,两个治疗组的缓解患者比例没有显著差异。可以使用下面的链接查找 p 值。

7.用 Python 实现

从任何数据集中选择两个分类列,然后创建一个交叉表。cross 选项卡的输出然后与 chi2_contingency()一起使用,以获得拒绝或接受零假设的 p 值。

#----------step 1: Extract required columns, ideally it should be two columns at a timedata_interim=df[['column1', 'column2']]#----------step 2: Create a cross tab which is a contigency tabledata_cont=pd.crosstab(data_interim['column1'], data_interim['column2'])data_contfrom scipy.stats import chi2_contingencystat, p, dof, expected = chi2_contingency(data_cont)#-----------interpret p-value
alpha = 0.05print("p value is " + str(p))if p <= alpha:

    print('Dependent (reject H0)')

else:
    print('Independent (H0 holds true)')

8.结论

在制药领域,尤其是在处理患者数据、医生数据或销售人员数据时,需要了解不同变量之间的相关性,尤其是分类变量。制药行业的分类属性包括目标细分、患者数量、性别、人口统计、通话结果、通话质量、销售代表拜访的讨论主题等等。当我们检查更少的类别时,卡方检验可以让我们建立这些变量之间的关系,这反过来可以用来深入研究变量或选择的驱动因素。

关于作者:高级分析专家和管理顾问,帮助公司通过对组织数据的商业、技术和数学的组合找到各种问题的解决方案。一个数据科学爱好者,在这里分享、学习、贡献;你可以和我在 上联系 上推特

独立性卡方检验

原文:https://towardsdatascience.com/chi-square-test-of-independence-154b49b7498f

独立性卡方检验的简明解释

照片由 Tra NguyenUnsplash 上拍摄

介绍

到目前为止,我们已经介绍了卡方分布,并用它来解释卡方拟合优度测试。你可以在这里查阅这两篇文章:

我强烈推荐在这篇文章之前阅读那些文章!

在本帖中,我们将介绍另一个著名的卡方检验独立性检验。该测试确定两个分类变量是否以某种方式相关,例如,它们是独立的还是相关的。你可以把这看作是两个连续变量之间相关性的分类版本。

在本文中,我们将介绍独立性测试的执行过程,并以一个示例问题结束,展示如何在实践中实施它!

假设

  • 两个变量都是分类的
  • 观察是独立的
  • 每个类别的计数大于 5
  • 类别中的每个计数都互斥****
  • 数据是随机选择的

假设检验步骤

在这里,我们将规划几乎每个假设检验中涉及的基本步骤:

这些是任何假设检验的基本表层步骤。我没有详细解释每个主题,因为那样会使这篇文章非常详尽!然而,对于不熟悉的读者,我为每个步骤都提供了链接站点,这样你就可以更深入地了解这些想法。

我还有其他一些帖子,以更详细的形式介绍了假设检验的概念,你可以在这里查看:

****

卡方检验统计和自由度

对于卡方检验,我们需要计算的检验统计量为:

作者在 LaTeX 中生成的方程。

  • v 是自由度
  • O 是观测采样值
  • E 是计算出的期望值
  • n 是变量中的类别数

注意:卡方分布来自于分子的*方,这也确保了我们只有正数“添加”到统计数据中。

自由度 v 计算如下:

作者在 LaTeX 中生成的方程。

  • r列联表中的行数(变量 1 中的类别数)
  • c列联表中的列数(变量 2 中的类别数)

当我们接下来看一个例题时,这两个公式会更有意义。

示例问题

我们想看看年龄对你投哪个政党的票有没有影响。

数据

我们收集了 135 人的随机样本,并将其显示在以下按年龄和政党细分的权变表中:

由作者创建的表。

注:这纯粹是我自己编造的合成数据,与任何真正的政党无关。

假设

让我们从陈述我们的假设开始:

  • H_0 :年龄对你投的政党没有影响。这两个变量是独立的。
  • H_1 :年龄确实对政党有影响。这两个变量是相关的。

显著性水*和临界值

在本例中,我们将使用 5%的显著性水*。因为我们有 2 个自由度(使用上面的公式):

作者在 LaTeX 中生成的方程。

使用显著性水*、自由度和卡方概率表,我们发现我们的临界值为 5.991。这意味着我们的卡方统计需要大于 5.991 ,以便我们拒绝零假设和变量不独立。

计算预期计数

我们现在需要确定列联表中每个单元的预期计数频率。如果零假设为真,这些是预期值,使用以下公式计算:

作者在 LaTeX 中生成的方程。

其中 n_rn_c 是特定类别的行和列的总数, n_T 是总计数。

例如,18-30 岁投票给自由党的预期人数是:

作者在 LaTeX 中生成的方程。

然后,我们可以用这些期望值(在括号中)填充列联表:

作者制作的表格。

卡方统计

现在是使用上述公式计算卡方统计的时候了:

作者在 LaTeX 中生成的方程。

这等于 37.2!

因此,我们的统计量远大于临界值,所以我们可以拒绝零假设!

结论

在本文中,我们描述并展示了一个独立性卡方检验的例子。该测试测量两个分类变量是否相互依赖。这在数据科学中用于特征选择,我们只需要对对目标有影响的特征建模。

和我联系!

(所有表情符号由 OpenMoji 设计——开源表情符号和图标项目。许可证: CC BY-SA 4.0****

选择合适的正态性检验

原文:https://towardsdatascience.com/choose-the-appropriate-normality-test-d53146ca1f1c

夏皮罗-维尔克检验、科尔莫戈罗夫-斯米尔诺夫检验和达戈斯蒂诺-皮尔逊 K 检验

照片由卢卡斯像素上拍摄

正态分布,也称为高斯分布,是用两个参数描述的概率分布:*均值和标准差。对于正态分布,68%的观察值在*均值的一个标准差内,95%在两个标准差内,99.7%在三个标准差内。

对于许多最流行的统计检验来说,检验正态性是满足假设所必需的。否则,我们可能会得出不准确的结论,开发出不正确的模型。

但是,要得出数据分布的结论,统计学家不应该只依赖直方图或分布图等图形方法。也有必要包括正态性测试的结果。

然而,文献中有几个测试可以用来评估这种正态性,很难确定哪一个是最准确的。例如,文章[1]提到了 27 种不同的正态性检验。

因此,本文的目的是描述和比较三种主要的正态性检验:

  1. 夏皮罗-维尔克试验
  2. 科尔莫戈罗夫-斯米尔诺夫试验
  3. 达戈斯蒂诺-皮尔逊 K 检验

本文展开如下。首先,对每项测试进行描述(参见第 1 节)。第二,试验之间的比较和关于哪种方法是最佳使用方法的结论(参见第 2 节)。最后,用 Python 实现这些算法(参见第 3 节)。

1.描述

夏皮罗-维尔克和达戈斯蒂诺-皮尔逊 K 检验的假设是:

  • 零假设 (H0):数据服从正态分布。
  • 备选假设 (H1):数据不符合正态分布。

相比之下,Kolmogorov-Smirnov 检验的假设为:

  • 零假设 (H0):数据来自指定的分布。
  • 替代假设 (H1):至少有一个值与指定的分布不匹配。

如果我们认为指定的分布是正态分布,那么我们将评估正态性。

如果 p 值小于选择的α水*,那么零假设被拒绝,并且有证据表明测试的数据不是正态分布的。另一方面,如果 p 值大于所选的α水*,则不能拒绝零假设。

以下是计算每个测试的 p 值的逐步方法。

夏皮罗-维尔克试验

夏皮罗-维尔克(SW)正态性检验的基本方法如下[2]。

首先按升序排列数据,使x1≤……≤xn。其次,计算*方和,如下所示:

然后,计算 b 如下:

夏皮罗-维尔克表中的表 1(基于 n 的值)中取 ai 权重。注意,如果 n 为奇数,则在 b 的计算中不使用中间数据值。如果 n 是偶数,设 m = n /2,如果 n 是奇数,设m=(n–1)/2。

最后,计算测试统计量:

找出夏皮罗-维尔克表的表 2 中最接* W 的值(对于 n 的给定值),必要时进行插值。这是测试的 p 值。

这种方法仅限于 3 到 50 个元素之间的样本。

科尔莫戈罗夫-斯米尔诺夫试验

Kolmogorov-Smirnov 测试将您的数据与指定的分布进行比较,如果它们具有相同的分布,则输出结果。尽管该检验是非参数的(它不假设任何特定的基础分布),但它通常被用作正态性检验,以查看您的数据是否正态分布[3]。

运行测试的一般步骤是:

  • 为你的样本数据创建一个 EDF(步骤见 经验分布函数 )。经验分布函数是对产生样本点的累积分布函数的估计。
  • 指定一个父发行版(即您要与 EDF 进行比较的发行版)
  • 将这两个分布绘制在一起。
  • 测量两个图形之间的最大垂直距离,并计算检验统计量,其中 m 和 n 是样本大小。

图一。双样本 Kolmogorov-Smirnov 检验的一个例子。 Ref : Bscan

  • KS 表中找到临界值。
  • 与临界值进行比较。

达戈斯蒂诺-皮尔逊 K 检验

D'Agostino-Pearson 的 K 检验计算两个统计参数,即峰度和偏斜度,以确定数据分布是否偏离正态分布:

  • 偏斜是分布被向左或向右推的量化,是分布不对称的度量。
  • 峰度量化分布有多少在尾部。这是一个简单且常用的正态性统计检验。

该测试首先计算偏斜度和峰度,以量化分布在不对称度和形状方面与高斯分布有多远。然后,它会计算这些值与高斯分布的预期值之间的差异,并根据这些差异的总和计算单个 p 值4

2.测试选择

为了回答这个问题,下面是从文章阿斯加尔·哈塞米等人(2012) 5中得出的结论,这篇文章引用了 4000 多次:

根据现有文献,使用参数统计检验时,应考虑评估正态性假设。似乎最流行的正态性检验,即 Kolmogorov-Smirnov 检验,由于其功效较低,不应再使用。最好通过目测和正态性检验来评估正态性,其中强烈推荐夏皮罗-维尔克检验。

3.履行

为了评估测试,我们创建了四个样本,它们的样本大小和分布各不相同:

  • 第一个样本 : 20 个数据,正态分布。
  • 第二个样本 : 200 个数据,正态分布。
  • 第三个样本 : 20 个数据,β= 1,β = 5 的 beta 分布。
  • 第四个样本 : 200 个数据,β= 1,β = 5 的 beta 分布。

图二。样本分布。参考:图片由作者提供。

原则上,查看这些图,正态性检验应该拒绝第四个样本的零假设,而不应该拒绝第二个样本的零假设。另一方面,第一个和第三个样品的结果很难仅通过观察图来判断。

夏皮罗-维尔克试验

下面是夏皮罗-维尔克测试的实现:

Sample 1: ShapiroResult(statistic=0.869, pvalue=0.011)
Sample 2: ShapiroResult(statistic=0.996, pvalue=0.923)
Sample 3: ShapiroResult(statistic=0.926, pvalue=0.134)
Sample 4: ShapiroResult(statistic=0.878, pvalue=1.28e-11)

结果表明,我们可以拒绝样本 1 和 4 的零假设(样本不遵循正态分布)。

科尔莫戈罗夫-斯米尔诺夫试验

以下是 Kolmogorov-Smirnov 测试的实现:

Sample 1: KstestResult(statistic=0.274, pvalue=0.081)
Sample 2: KstestResult(statistic=0.091, pvalue=0.070)
Sample 3: KstestResult(statistic=0.507, pvalue=2.78e-05)
Sample 4: KstestResult(statistic=0.502, pvalue=4.51e-47)

结果表明,我们可以拒绝所有样本的零假设(样本不遵循正态分布)。

由于这种类型的检验也适用于任何类型的分布,我们可以比较它们之间的样本。

Samples 1 and 2: KstestResult(statistic=0.32, pvalue=0.039)
Samples 3 and 2: KstestResult(statistic=0.44, pvalue=0.001)
Samples 4 and 2: KstestResult(statistic=0.44, pvalue=2.23e-17)

达戈斯蒂诺-皮尔逊 K 检验

下面是达戈斯蒂诺-皮尔逊 K 检验的实现:

Sample 1: NormaltestResult(statistic=10.13, pvalue=0.006)
Sample 2: NormaltestResult(statistic=0.280, pvalue=0.869)
Sample 3: NormaltestResult(statistic=2.082, pvalue=0.353)
Sample 4: NormaltestResult(statistic=46.62, pvalue=7.52e-11)

与夏皮罗-维尔克检验相似,结果表明我们可以拒绝样本 1 和 4 的零假设(样本不遵循正态分布)。

结论

测试样本的正态性对于满足许多最流行的统计测试的假设是必要的。然而,文献中有许多正态性检验,很难确定哪一个是最合适的正态性。

因此,本文描述了三个主要的正态性检验((1)夏皮罗-维尔克,(2)科尔莫戈罗夫-斯米尔诺夫,以及(3)达戈斯蒂诺-皮尔逊的 K)并且在四个不同的样本上实施了它们。

目前的文献和实验结果都表明,一般来说,推荐使用的测试是夏皮罗-维尔克5

如果你喜欢这个帖子,请考虑 订阅 。你将获得我所有的内容+所有其他来自牛逼创作者的文章!

参考

[1]研究门,二十七个正态性检验的比较

[2]真实统计,夏皮罗-维尔克原始检验

[3]统计学如何, Kolmogorov-Smirnov 拟合优度检验

4真实统计,达戈斯蒂诺-皮尔逊检验

5美国国家医学图书馆,统计分析的正态性检验:非统计人员指南

为您的神经网络选择正确的优化算法

原文:https://towardsdatascience.com/choose-the-right-optimization-algorithm-for-your-neural-network-cb86c15d7328

由于神经网络开发过程的本质是迭代的,我们需要利用每一种可能的方法来最小化训练时间。

来源:unslpash.com

任何机器学习模型的开发都是一个高度迭代和经验主义的过程,遵循想法-实验-评估的循环。

图片由作者提供。

在达到令人满意的性能之前,上述循环通常要重复多次。“实验”阶段包括机器学习模型的编码时间和训练时间。随着模型复杂性的增加和更多数据的处理,训练时间膨胀,结果是训练一个非常大的深度神经网络可能会非常慢[1]。

有不同的方法可以加速训练过程,例如:

  • 使用迁移学习,就像我在这个项目中所做的那样。
  • 应用适当的权重初始化,如 GlorotHe 初始化[2],[3]。
  • 对训练数据使用批处理规范化。
  • 选择一个可靠的激活函数。
  • 使用快速优化器。

虽然上述所有技术都是基本的,但本文将详细介绍最后一点。我提出了几种优化神经网络参数的方法,并指出每种方法的局限性和优点。

在文章的最后一部分,我展示了一个比较所有优化器的可视化图表。实际比较的代码可以在这个 GitHub 存储库中找到:

https://github.com/andreoniriccardo/articles/tree/main/art07-NN-optimizer

批量梯度下降

批次梯度下降传统上被认为是优化器方法的默认选择。

在神经网络输出对整个训练集 X 的预测之后,我们将网络的预测与每个训练点的实际标签进行比较。这样做是为了计算成本函数 J(W,b ),它是模型产生准确预测的能力的标量表示。梯度下降优化算法使用成本函数作为寻路器来调整网络的每个参数。直到代价函数接*于零,或者不能再降低,梯度下降在某个方向调整网络的每一个权值和偏差。所选择的方向是降低成本函数最多的方向。

图片由作者提供。

拿上图做参考。梯度下降从最右边的蓝点开始。通过分析成本函数在起始点周围的梯度,它选择向右调整参数(x 轴值)。这个过程重复多次,直到算法以最佳值的非常好的*似值结束。

如果成本函数有两个输入参数,则该函数不是一条线,而是由一个三维表面组成。考虑到表面的水*曲线,可以显示批量梯度下降步骤:

来源:wikipedia.org4

神经网络由成千上万影响成本函数的参数组成。由于无法显示百万维的图形表示,数学方程有助于理解梯度下降过程。

首先,我们计算成本函数 J(W,b ),它是网络的权重 W 和偏差 b 的函数。然后,由于反向传播算法,我们计算成本函数相对于网络每层的每个权重和偏差的导数:

知道了调整参数的方向,我们就更新它们。每个参数的更新幅度由梯度本身和学习速率α来调节。α是优化算法的超参数,其值通常保持不变。

批量梯度下降的优点是:

  • 这是一种简单易懂的方法。
  • 它只有一个要调整的超参数(学习率)。
  • 对于凸代价函数,只要我们为学习速率提供一个合理的值,它总是达到全局最优。

梯度下降的缺点是:

  • 由于它会在评估所有训练示例后更新参数,因此可能会很慢,尤其是对于大型训练集。
  • 可能会卡在局部最优或鞍点。
  • 容易出现渐变消失或渐变爆炸的问题。
  • 它需要将所有训练数据存储在 CPU/GPU 内存中。

小批量梯度下降

批量梯度下降要求遍历所有训练示例,以便采取单个改进步骤。该步骤将是最准确的,因为它考虑了所有可用的信息。然而,在大多数实际情况下,这种方法慢得不可思议,更快的解决方案是存在的。

最常见的解决方案称为小批量梯度下降,它只使用训练集的一小部分来更新权重值。

考虑整个训练集 X 和相关联的标签 Y:

其中 m 是训练样本的数量。

我们只将训练集的一小部分提供给优化算法,而不是将整批数据提供给优化算法。假设子集 X^t 和 Y^t 包含 512 个训练示例:

假设整个训练集包含 5,120,000 个训练点。我们将训练集分成 10,000 个小批量,每个小批量包含 512 个示例。

对于每个小批量,我们执行经典的梯度下降操作:

  • 计算相对于小批量 t,J^t(W,b).的成本
  • 执行反向传播以计算 J^t(W,b 相对于每个权重和偏差的梯度。
  • 更新参数。

下图中的绿线显示了小批量梯度下降的典型优化路径。批量梯度下降采用更直接的路径到达最佳点,而小批量梯度下降似乎需要更多不必要的步骤。

来源:wikipedia.org4

其原因是小批量梯度下降,在任何时候 t 只有一小部分训练集作为其决策的基础。由于可用信息的限制,很明显,所遵循的路径不是最直接的。

然而,小批量梯度下降的巨大优势在于,由于算法需要评估一小部分数据而不是整个训练集,因此每一步的计算速度都非常快。在我们的例子中,每一步只需要评估 512 个数据点,而不是 500 万。这也是为什么几乎没有真正需要大量数据的应用使用批量梯度下降的原因。

如图所示,使用小批量梯度下降,不能保证迭代 t+1 的成本低于迭代 t 的成本,但是,如果问题设置得很好,算法会非常快地到达最优点周围的区域。

小批量大小的选择是网络训练过程的附加超参数。如果小批量正好是 m,我们将执行批量梯度下降。相反,如果 m=1,我们正在执行随机梯度下降,其中每个训练示例都是一个小批量。

如果训练集非常小,批梯度下降可能是一个有效的选项,否则,通常会考虑大小为 32、64、128、256 和 512 的批。出于某种原因,批量大小等于 2 的幂似乎表现得更好5。随机梯度下降的问题是,它可能会非常嘈杂,并且可能会远离最小值。此外,如果我们考虑每个例子的计算时间,随机梯度下降是非常低效的,因为它不能利用矢量化的好处。

动量梯度下降

回想一下,批量和小批量梯度下降中的参数更新由以下公式定义:

因此,优化的每个步骤的大小由学习率、固定量和梯度来定义,该梯度是在成本函数的特定点计算的。

如果在成本函数的接**坦的部分计算梯度,它将非常小,因此梯度下降的步长将成比例地小。考虑下图中 A 点和 B 点的梯度差异。

图片由作者提供。

动量梯度下降解决了这个问题。我们可以把动量梯度下降想象成一个滚下山坡的保龄球,其形状是由代价函数定义的。如果球从斜坡的陡峭部分开始下降,它将开始缓慢,但它将迅速获得速度和动量。由于它的动量,球将保持很高的速度,即使通过一个*乎*坦的斜坡。

这是动量梯度下降的关键思想:算法将考虑先前的梯度,而不仅仅是迭代 t 计算的梯度。再次遵循保龄球类比,迭代 t 计算的梯度用于定义加速度而不是速度。

我们计算重量和偏差的速度。在每个迭代步骤中,它是根据先前的速度值和当前迭代的梯度计算的:

称为动量的参数β规定了新的速度值在多大程度上由当前斜率或过去的速度值确定。

最后,我们更新参数:

在大多数应用中,动量梯度下降已被证明优于小批量梯度下降。相对于标准梯度下降,动量梯度下降的主要缺点是它需要额外的参数来调节。然而,实践表明β= 0.9 的值是如何有效工作的。

RMS Prop

考虑一个成本函数,其形状类似于一个细长的碗,其中最小点是最窄的部分。下图中的高程曲线描述了函数的形状。

图片由作者提供。

如果起点远离最小值,梯度下降也随着动量变化,开始沿着最陡的坡度下降,在上图中,这不是通向最小值的最佳路径。RMS Prop 优化算法的思想是尽早校正方向,并且更迅速地瞄准全局最小值。

至于动量梯度下降,RMS Prop 也需要一个额外的超参数来调整,称为衰减率。实践表明,衰减率设置为 0.9 几乎适用于所有问题。

圣经》和《古兰经》传统中)亚当(人类第一人的名字

Adam 及其变体可能是神经网络最常用的优化算法。Adam 代表自适应矩估计,它将动量梯度下降和 RMS Prop 结合在一起。

作为两种优化方法的组合,Adam 需要两个额外的超参数来调整(除了学习速率α之外)。我们称它们为β_ 1 和β_ 2,它们是分别用于动量和均方根 Prop 的超参数。幸运的是,beta_1 和 beta_2 有有效的默认选择:beta_1 通常设置为 0.9,beta_2 设置为 0.999。在 adam 中,还有一个用作*滑项的参数ε,它几乎总是被设置为小值,如 e-7 或 e-8。

Adam 优化算法在大多数情况下优于上述所有方法。唯一的例外是非常简单的问题,简单的方法工作得更快。Adam 效率的唯一代价是有两个额外的参数需要调整。

优化算法比较

为了实际测试和可视化每个算法的性能,我用上面的每一个优化器训练了一个简单的深度神经网络。该网络的任务是从 28×28 的图像中对时尚物品进行分类。该数据集名为时尚 MNIST (麻省理工学院许可),由 7 万幅服装小灰度图像组成。

测试不同优化器的算法在下面的要点中给出,更完整的内容在下面链接的 GitHub 库中。

https://github.com/andreoniriccardo/articles/tree/main/art07-NN-optimizer

结果图如下所示:

图片由作者提供。

我们可以立即看到动量梯度下降(黄线)比标准梯度下降(蓝线)快得多。相反,RMS Prop(绿线)似乎获得了与动量梯度下降相当的结果。这可能是由于几个原因造成的,例如超参数调整不完美或神经网络过于简单。最后,Adam optimizer(红线)看起来优于所有其他方法。

学习率衰减

我想在本文中包括学习率衰减,因为尽管它不是一种优化算法,但它是一种加速学习算法的强大技术。

学习率衰减包括在各时期降低学习率超参数α。这样做的原因是,在优化的开始阶段,我们可以采取更大的步骤,但随着算法接*最小值,我们更喜欢采取更小的步骤,以便它将在更接*最小值的区域反弹。

有几种方法可以降低学习率。其中一个由以下公式描述:

其中衰减率是要调整的附加参数。

其他可能性有:

第一个称为指数学习率衰减,在第二个中,k 是一个常数。

最后,其他可能性包括应用离散学习率衰减,例如在 t 次迭代后将其减半,或者手动降低它。

图片由作者提供。

结论

对于每个数据科学从业者来说,时间是宝贵的,但也是有限的资源。由于这个原因,掌握加快学习算法训练的工具可以有所作为。

在本文中,我们已经看到了标准的梯度下降优化器是一个过时的工具,并且有几种替代方法可以在更短的时间内提供更好的解决方案。

为给定的机器学习应用选择正确的优化器并不总是容易的。这取决于任务,对于哪一个是最好的还没有明确的共识。然而,正如我们在上面看到的,使用建议的超参数值的 Adam optimizer 在大多数情况下是一个有效的选择,了解最流行的超参数的工作原理是一个很好的起点。

参考

[1]: 使用 Scikit-Learn、Keras 和 TensorFlow 进行机器实践学习,第二版——aurélien géRon

[2]: 了解训练深度前馈神经网络的难度— Xavier Glorot,Yoshua Bengio

[3]:【深挖整流器:在 ImageNet 分类上超越人类水*——何、、任、

为 PCA 降维选择一个好的值

原文:https://towardsdatascience.com/choosing-a-good-value-for-pca-dimensionality-reduction-a763f2edc8d

降低数据维数的快速代码

米赫利·克勒斯Unsplash 拍摄的照片

介绍

如果你曾经面对一个数据集,比如说,有 100 个特征,你可能会考虑减少它的维数。

首先,因为我们真的很难创造出有那么多属性的引人注目的可视化效果。很难知道什么对情节真正重要。

其次,因为有一个被称为 维度诅咒 的概念,它告诉我们,包含太多变量的数据集往往会变得稀疏(太多零),这使得机器学习模型更难估计可靠的结果。随着变量数量的增加,对观测数据的需求呈指数增长。

因此,数据科学家经常使用主成分分析[PCA]来减少数据集的特征数量。

主成分分析

PCA 是一种数学变换,它将数据点从其当前维度投影到称为分量的点向量。每个组成部分的目标都是理解真实数据的最大差异,尽可能减少信息损失。

运行 PCA

让我们快速创建一个数据集并运行 PCA。以下是包含 100 个变量的数据集的代码。

from sklearn.datasets import make_regression
from sklearn.decomposition import PCA
import pandas as pd
import matplotlib.pyplot as plt# Dataset
X, y = make_regression(n_features=100, n_informative=40, random_state=12)

高维数据集。图片由作者提供。

因为它是用 Scikit Learn 的make_regression创建的,所以它已经处于类似的规模,所以我们在本文中不使用缩放技术。

现在,如果我们想要绘制最*创建的数据,这将是一项艰巨的任务。我们需要知道哪些是最重要的变量,然而,比较太多的维度会给我们带来很多麻烦。因此,解决这个问题的一个方法是将数据减少到仅仅 2 维,这样我们就可以用一个普通的 2D 图形来绘制它们。就这么办吧。

#Reduce Dimensionality
pca = PCA(n_components=2)# Fit
pca_2d = pca.fit_transform(X)
df_2d = pd.DataFrame(pca_2d, columns=['PC1', 'PC2'])

绘制 2D 图形。

# Plot 2D PCA
plt.figure(figsize=(10,6))
plt.scatter(x= df_2d.PC1, y = df_2d.PC2)
plt.title('Principal Components 2D', size=15);

PCA 减少后的 2D 图形。图片由作者提供。

这是我们缩减到二维的数据。

选择维度的数量

为了选择维度的数量,您有几个选项。当然,第一种方法是像刚才那样输入一个整数。我们简化到二维。但是,这是最好的决定吗?

对于可视化来说,可能是的,因为它使我们能够绘制数据。但是,让我们看看我们通过这种减少获得了多少差异。

# Variance comprehended in 2D
pca.explained_variance_ratio_**[OUT]:**
array([0.03874607, 0.03712152])

我们只获得了总方差的 7.58%。不是我们数据的一个很好的代表,对吗?仅用这种方法将数据拟合到模型中并不是一个好的选择。

这就是为什么PCA()类可以选择从数据中插入您想要的百分比来创建组件。换句话说,例如,您可以询问获取数据集中 95%方差的维度数量。

这是我们如何做到这一点。

#Reduce Dimensionality
pca95 = PCA(n_components=0.95)# Fit
transformed = pca95.fit_transform(X)

正如在前面的代码片段中看到的,如果我们使用属性explained_variance_ratio_,就有可能看到每个主成分捕获的数量。利用这些数据,我们可以绘制一个柱状图。

# Plot
plt.figure(figsize=(15,6))
plt.bar(x= [str(i) for i in range(1,pca95.n_components_+1)], height= pca95.explained_variance_ratio_,)
plt.title('Principal Components with 95% data variance', size=15);

60 个主成分将获得 95%的方差。图片由作者提供。

在这里,我们可以看到方差在许多变量中分布良好。不存在方差超过 4%的组件,因此需要 60 台电脑来弥补我们数据集中高达 95%的方差。

在你走之前

我以前写过关于 PCA 的文章,重温这个概念和这个漂亮的变压器总是好的。

我们可以使用 PCA 来帮助我们解决复杂的问题,因此知道如何选择正确的组件数量也同样重要。

如果你喜欢这个内容,关注我的博客或者在 LinkedIn 找到我。

http://gustavorsantos.medium.com/

Git Hub 代码:https://github . com/gure zende/studining/blob/master/Python/sk learn/PCA _ Python . ipynb

参考

https://scikit-learn.org/stable/modules/generated/sklearn.decomposition.PCA.html https://medium.com/@monishatemp20/principal-component-analysis-pca-1c285cce3c26

Aurélien Géron,2019。 用 Scikit-Learn 动手机器学习,Keras & TensorFlow 。第二版,奥赖利。

为自然语言处理选择神经网络而不是 N-Gram 模型

原文:https://towardsdatascience.com/choosing-neural-networks-over-n-gram-models-for-natural-language-processing-156ea3a57fc

今天,我们将通过一个利用 LSTMs 分析金融科技数据的例子,来看看在 N 元模型上使用递归神经网络、门控递归单元和 LSTMS 的优势

乔希·里默尔在 Unsplash 上的照片

为什么是神经网络?传统的学习模式需要大量的空间和内存!!!

传统的学习模型将文本从一种语言转换成另一种语言。为了准确翻译,您将使用一个 N-Gram 语言模型来计算一个句子的概率。一个限制是捕捉相距较远的单词之间的差异。这需要一个大的语料库,意味着更多的空间和内存。与 N- Grams 相比,递归神经网络(RNNs)和门控递归单元(GRUs)对于机器翻译更有效,因为它们可以使用过去记忆的概念和结合来预测单词是什么,或者文本语料库的情感。虽然神经网络可能更适合您的分析,但传统的学习模型仍然有效,至少会为您的工作提供一个起点。

普通递归神经网络(RNN)

rnn 有能力捕捉传统语言模型没有捕捉到的依赖关系。RNN 将信息从句子的开头一直传播到结尾。

基本的 RNN 结构(图片来自作者)

如上所示,与旧信息相比,最新信息在 RNN 的给定步骤中具有更高的影响。通过训练 RNN 更新的参数有 WxWhW

优势

  • 在序列之间传播信息
  • 计算共享相同的参数

数学

RNN 的第一个计算是隐藏状态,它通过激活函数做矩阵乘法。

隐藏状态计算(图片来自作者)

RNN 中的计算顺序(图片来自作者)

在计算出下一个时间步的隐藏状态后,状态乘以另一个参数矩阵 Wyh 并通过一个激活函数,得到该时间步的预测。

预测计算(图片来自作者)

价值函数

RNNs 可以利用交叉熵损失进行训练。

交叉熵损失(图片来自作者)

RNN 的交叉熵损失的计算略有不同。

RNN 交叉熵损失(图片来自作者)

  • K →类别数
  • T →时间步长 T
  • 查看实际值和预测值之间的差异。

门控循环单位

gru 不同于 rnn,因为它们现在将学习随着时间的推移保存相关信息(可能是词性)。如果与正在解决的问题相关,长期记忆就不会丢失。这很重要,因为在一个普通的 RNN 中,长期信息将开始随着时间消失,导致消失梯度。基本上,RNN 架构增加了一个额外的输入。GRU 的架构中有一个相关性更新门。更新门确定应该保留或更新多少以前的信息。gru 更加数学化,因此,使用它们的一个缺点是更大的内存和计算成本。

数学

如上所述,gru 比普通的 rnn 更加数学密集。

门控循环单元(图片来自作者)

两个 sigmoid 激活单元是关联门和更新门。

相关性和更新方程式(图片来自作者)

这些门的权重参数将随时间变化,以确定应该保留、更新和从该单元传递什么信息。

隐藏状态候选人(图片来自作者)

隐藏状态候选从关联门和隐藏状态中获取信息进行计算,并最终通过 tanh 激活函数。

隐藏状态(图片来自作者)

基于由更新门传递的信息来更新 GRU 的隐藏状态。

预测(图片来自作者)

通过最后激活函数的最后一次传递是由 GRU 做出的预测。

双向 RNNs

想象未来流向现在,这基本上是双向 RNN 所做的。它将有两个方向的信息流,其中每个方向都独立于另一个方向。双向 rnn 是非循环图,这意味着一个方向的计算独立于另一个方向的计算。

LSTM 细胞(图片来自作者)

双向 RNN 的一个例子是长短期模型(LSTM)。LSTM 单元最大的变化是,它现在把状态分成两个向量。在两个新的状态向量中,一个数组表示长期记忆,另一个表示短期记忆。如上面的单元格所示,有许多输入和变量计算与 LSTM 单元格相关联。是细胞的短期信息 c(t) 是细胞的长期信息。

对于长期信息,它将首先通过一个遗忘门来丢弃一些记忆信息,然后通过加法门添加新信息。在添加新的记忆并通过 tanh 激活功能后,短时记忆、**、、【t】、被创建。

LSTM 方程(图片来自作者)

上面显示的是 LSTM 细胞在训练过程中进行的不同计算。主要的要点是,LSTM 将更新不同的权重矩阵以识别给定问题和数据的相关长期和短期记忆。**

例子

我想使用 Kaggle 的金融情绪分析数据集提供一个快速编码示例。我训练了一个 LSTM 网络来识别金融问题的情绪是积极的还是消极的。今天的代码将帮助您重新创建和训练模型,但请注意,由于模型计算的复杂性和组合性,训练 LSTM 模型可能需要很长时间。虽然这不同于

首先,让我们导入库和数据。

**from keras.layers import Input,GlobalMaxPooling1D, Dense, Dropout, LSTM, SpatialDropout1D
from keras.models import Model, Sequential
from keras.preprocessing.text import Tokenizer
from keras.optimizers import Adam
from keras.losses import BinaryCrossentropy
from keras.utils import pad_sequences
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
import re df = pd.read_csv('finance_data.csv')
df.head()**

数据集示例(图片来自作者)

如您所见,数据集是讨论金融部门不同领域的条目的集合。在“情绪”栏中,我们有“积极”、“消极”和“中性”情绪,我们将它们映射为 0 或 1。此外,我们希望预处理我们的句子,并将它们转换成 NumPy 数组。

**df['Sentence'] = df['Sentence'].apply(lambda x: x.lower())
df['Sentence'] = df['Sentence'].apply((lambda x: re.sub('[^a-zA-z0-9\s]','',x)))mapping = {'negative' :0,
            'neutral' : 1,
            'positive': 1}
df['Sentiment'] = df['Sentiment'].map(mapping)X = df['Sentence'].to_numpy() #turn the sentences into numpy arrays
y = df['Sentiment'].values #Targert sentimen values**

接下来将使用 80/20 分割创建训练集和测试集。

**X_train, X_test, y_train, y_test = train_test_split(sentences, y, test_size=0.2)**

接下来,我们要标记我们的句子。标记化是将每个句子分解成单个单词的操作,其中每个单词都是一个标记。

**vocab_size = 10000
tokenizer = Tokenizer(num_words=vocab_size, oov_token="<OOV>")
tokenizer.fit_on_texts(X_train) #Fit the tokenizer on the training data#We will then fit onto each of the sequences
X_train = tokenizer.texts_to_sequences(X_train)print(X_trai[0])[2, 100, 3, 2, 138, 12, 326, 9, 259, 29]**

在我们转换了我们的序列后,我们将填充它们,使它们都是相同的形状。

**seq_length = 100
X_train = pad_sequences(X_train, maxlen=seq_length, padding='post', truncating='post')#Now do the same for the test data 
X_test = tokenizer.texts_to_sequences(X_test)
X_test = pad_sequences(X_test, maxlen=seq_length, padding='post', truncating='post')**

让我们创建我们的分类模型。该模型将首先使用嵌入层,该嵌入层将是序列向量的嵌入大小(在这种情况下是 FinTech 概要)。为简单起见,我使用了 1 个 LSTM 层,但两个或更多层也可以(这里建议使用进一步的超参数调整以及 k- 折叠交叉验证)。最后,sigmoid 激活函数将用于预测输出为 0 或 1。

**embed_dim = 100
batch_size = 32model = Sequential()
model.add(Embedding(vocab_size, embed_dim, input_length=sequence_length))
model.add(LSTM(units = 100))
model.add(Dense(1,activation='sigmoid'))
model.compile(loss = 'binary_crossentropy', optimizer='adam',metrics = ['accuracy'])
print(model.summary())**

我们的最后一步是训练和执行模型。

**history = model.fit(X_train, y_train, epochs=100, 
                    validation_data=(test_padded, y_test))**

训练这个模型需要很长时间,但是训练之后,你会有一个模型,可以预测任何 FinTeh 相关的摘要/评论/语料库的情绪。

结论

虽然 N-gram 模型是预测句子中下一个单词的伟大工具,但神经网络是一个更强大的工具,因为它们可以保留长期信息,在进行 NLP 分析时不应被忽略。神经网络不仅可以用于 n-gram 预测,而且今天的例子还显示了用 Python 创建 LSTM 网络并在您的数据上实现它是多么简单!

如果你喜欢今天的阅读,请关注我,让我知道你是否还有其他想让我探讨的话题(这对我的帮助超乎你的想象)!另外,在LinkedIn上加我,或者随时联系!感谢阅读!****

来源

-盖伦,奥雷连恩。使用 Scikit-Learn、Keras 和 TensorFlow 进行动手机器学习:构建智能系统的概念、工具和技术。第二版。,奥莱利,2019。

**https://www.kaggle.com/datasets/sbhatti/financial-sentiment-analysis

-该数据集属于首席运营官公共领域,允许公众使用。**

为您的数据选择最佳 ML 时间序列模型

原文:https://towardsdatascience.com/choosing-the-best-ml-time-series-model-for-your-data-664a7062f418

随着每年的新发展,决定合适的模型变得越来越具有挑战性

克里斯·利维拉尼在 Unsplash 上的照片

ARIMA、先知、LSTMs、CNN、GPVAR、季节分解、DeepAR 等等。当谈到时间序列模型时,有太多的方法,这意味着在提交模型之前考虑您的选择是很重要的。

关于开始改进模型,需要做出两个关键决定:是局部的还是全局的,以及预测是单变量的还是多变量的。为了解释这些,让我们介绍一个来自 Kaggle 的经过修改的样本天气数据集:

天气数据( CC0:公共域 )

一个局部模型(有时也称为迭代或传统模型)只使用一个单一数据列的先验值来预测未来值。由于局部模型仅依赖于单个数据列,因此它们也必须是单变量的,或者我们预测的是单个变量随时间的变化。在本例中,一个局部单变量模型将使用第 1-20 天的最高温度来预测第 21 天的最高温度。

相比之下,一个全局模型使用许多数据列来预测未来值,通常是与时间无关的变量。有两种类型的全局模型:全局单变量模型,其中我们使用许多值来预测单个值;全局多变量模型,其中我们使用许多值来预测许多值。全局单变量模型将使用最高温度、蒸发量和湿度来预测未来的最高温度值。全局多变量模型将利用最高温度、蒸发量和湿度来预测最高温度、蒸发量和湿度的未来值。

局部单变量模型

局部单变量模型最适合这些用例:

  1. 你的数据微不足道

如果您的数据简单、单变量且易于预测,那么时间序列预测的经典方法可能是最好的。为什么?它可以立即被训练,需要很少的计算资源,并且更复杂的模型可能是多余的,并且使你的数据过拟合。这方面的一个例子可能是预测某个商品在商店中的销售量。

2.你需要准确的预测,但只有一个变量

如果你只有 1 个可用的变量,迭代预测/集成局部模型已经一次又一次地证明,由于它们能够消除任何一个特定模型的缺点和弱点,它们可以比许多 ML 模型更准确地预测数据,因此,尽管训练可能相对昂贵,但它仍然是时间序列预测最流行的方法之一。一个流行的例子是利用过去的数据预测股票市场。(如果你有兴趣这么做,这里的是你可以获取数据的地方)

3.可预测季节性的模型

如果您知道您的数据遵循可预测的季节模式,当您对自己的“季节”有信心时,许多时间序列(如 SARIMA(季节性自回归移动*均))就可以用来处理数据。这方面的一个例子可能是 web 流量,在这种情况下,您知道数据每天都遵循一种规律。在这种情况下,您可以定义一个具有每日季节性的模型。

局部单变量模型示例(从最简单到最复杂——带有简要解释和更深入阅读的链接)

移动*均——最简单的方法,可以用一行熊猫计算

指数*滑/霍尔特-温特斯 —指数*滑使用所有先前值的加权*均值来预测值,其中最*值的权重较高。在 Holt-Winters 中,季节性和趋势作为参数被纳入方程

ARIMA/萨里玛/自动 ARIMA —在它的基础上,它是一个移动*均加上一个自回归项的导数(使用带有噪声的过去值),以便预测未来值。SARIMA 考虑到了季节性,而自动 ARIMA 将进行搜索,尝试找到最佳的模型参数

Prophet —由脸书开发,自开源以来,Prophet 是一个回归模型,它结合了线性或逻辑增长趋势、季节性组件和变点检测。关于 Prophet 的一个很酷的事情是它能够分离出这些趋势并为你绘制出来!

https://research . Facebook . com/blog/2017/2/prophet-forecasting-at-scale/

迭代预测 —迭代预测是简单地结合使用许多上述模型来创建预测!

全局单变量模型

全局单变量模型最适合这些用例:

  1. 你有许多补充变量,并希望预测未来的一个奇异值。这方面的一个例子可能是温度预测,就像我们的玩具例子一样,使用湿度、风速、季节等变量来预测每天的温度。
  2. 你不知道你的模型的季节性或趋势——ML 模型的一个好处是,从设计上讲,它们能够检测数据中观察者不能立即看到的模式。这方面的一个例子可能是预测硬件故障。虽然人类可能很难诊断哪些变量可能会将硬件置于风险之中,但这些全球时间序列模型要成功得多
  3. 你需要在单个模型中训练许多时间序列——对于许多深度学习实现,模型可以同时学习许多时间序列模型。回到我们的温度示例,如果我们有来自多个区域的数据,我们可以训练单个 ML 模型来预测任何单个区域,甚至学习区域之间的模式!

全球单变量模型示例(从最简单到最复杂——带有简要解释和更深入阅读的链接)

SARIMAX — SARMAX 就是 SARIMA(前面讨论过的),它考虑了外部变量,使时间序列能够更快地适应不断变化的变量

基于树的方法——几乎每个问题都可以用树来解决,而且时间序列也没有太大的不同。如果数据是稀疏的、准确的,并且与深度方法(下面讨论)相比训练起来相对较快,那么它们往往是有利的。当前最流行的实现是 lightgbmxgboost

基于 MLP 的方法——使用经典的全连接神经网络进行预测产生了一些非常奇妙的结果,甚至赢得了国际比赛。当前最流行的实现是 N-BEATSGP 预测器,它混合了 MLP 和高斯嵌入,或基于密度的分布

基于 CNN 的方法— 卷积神经网络类似于 MLPs 除了它们不是完全连接的。CNN 被广泛使用,因为它们往往更小,更少浪费,更容易训练。不幸的是,实现很少,但是有许多学术文章详细介绍了它们是如何工作的

基于 RNN/LSTM 的方法——目前研究人员的“最先进”方法,RNN 是相互“循环”的神经网络。这种方法如此有效的原因是,它允许后续数据点“记住”之前几个点已经处理过的内容,从而允许更动态的预测,因为时间序列自然依赖于之前的值。lstm是一种更具体和更流行的 RNN,代表“长期短期记忆”。这种方法的一个缺点是,因为它非常依赖以前的数据点,所以用 RNNs 进行的长期预测往往不如其他一些方法可靠。RNN 方法非常流行,一些更先进的实现是 DeepARDeepStateDeepFactor

全局多元模型

全局多元模型最适合这些用例:

  1. 你有许多补充变量,并希望预测未来的许多或所有值。回到我们的温度示例,我们将使用温度、湿度、风速、季节等变量,并为这些变量中的许多或全部生成未来预测。
  2. 如上所述,如果您不知道模型的季节性或趋势,全局多元模型也很好
  3. 您需要为许多不同的变量训练许多时间序列,所有这些都有效地包装到单个模型中—例如,回到我们的温度示例,如果我们有来自多个区域的数据,我们可以训练单个 ML 模型,该模型可以预测来自任何区域的任何变量!

全局多元模型的例子

基于 RNN/LSTM 的方法——几乎每一个全球多变量实施都是 RNN/LSTM 模型的某种变体,彼此之间有微小的差异,其中一些甚至是从其单变量版本改编而来,以预测任何和所有变量。流行的实现有DeepVAR(DeepAR 的一个变种) GPVAR ,将高斯过程融入 RNNs,以及 LSTNet ,一个 LSTM 变种。

结论

上面列出的每个模型都有其优点、优点和缺点。我希望这篇文章为您开始时间序列之旅提供了一个有用的指南,因为选项可能会非常多。

如果你觉得指南有用,如果你认为你需要回来参考,一定要保存/书签的故事!如果你喜欢这篇文章,请随意关注我,阅读我写的更多内容,或者将我作为推荐人,这样我就可以继续制作我喜欢的内容。我在数据科学/ML 空间写了很多!

数据集许可为 CC0 1.0 通用公共领域

为您的 NLP 用例选择正确的语言模型

原文:https://towardsdatascience.com/choosing-the-right-language-model-for-your-nlp-use-case-1288ef3c4929

理解、选择和部署大型语言模型的指南

大型语言模型(LLM)是经过训练以产生文本的深度学习模型。凭借这种令人印象深刻的能力,LLM 已经成为现代自然语言处理(NLP)的骨干。传统上,他们由学术机构和 OpenAI、微软和英伟达等大型科技公司进行预培训。其中大部分随后可供公众使用。这种即插即用的方法是迈向大规模人工智能采用的重要一步——企业现在可以专注于针对特定用例微调现有的 LLM,而不是花费大量资源来训练具有一般语言知识的模型。

然而,为您的应用程序选择正确的模型可能很棘手。用户和其他利益相关者必须在语言模型和相关创新的活跃环境中前进。这些改进解决了语言模型的不同组成部分,包括它的训练数据、预训练目标、架构和微调方法——你可以就这些方面中的每一个写一本书。在所有这些研究的基础上,围绕巨大语言模型的人工通用智能的营销热潮和迷人光环使事情更加模糊不清。

在本文中,我解释了 LLM 背后的主要概念和原则。目标是为非技术利益相关者提供直观的理解,以及与开发人员和人工智能专家有效交互的语言。对于更广泛的覆盖面,文章包括了大量 NLP 相关出版物中的分析。虽然我们不会深入语言模型的数学细节,但是这些可以很容易地从参考资料中检索到。

文章的结构如下:首先,我将语言模型置于不断发展的 NLP 环境中。第二部分解释了 LLM 是如何构建和预训练的。最后,我描述了微调过程,并提供了一些模型选择的指导。

语言模型的世界

弥合人机差距

语言是人类思维的一种迷人技能——它是交流我们丰富的世界知识的通用协议,也是更主观的方面,如意图、观点和情感。在人工智能的历史上,有过多次用数学手段*似(“建模”)人类语言的研究浪潮。在深度学习时代之前,表示基于简单的代数和概率概念,如单词的一键表示、序列概率模型和递归结构。随着过去几年深度学习的发展,语言表示在精确度、复杂性和表现力方面都有所增加。

2018 年,在新变压器架构的基础上推出了 BERT 作为第一个 LLM。从那以后,基于变压器的 LLM 获得了强劲的发展势头。语言建模因其普遍的有用性而特别吸引人。虽然许多现实世界的 NLP 任务(如情感分析、信息检索和信息提取)不需要生成语言,但假设生成语言的模型也具有解决各种更专业的语言挑战的技能。

尺寸很重要

学习是基于参数进行的,这些参数是在训练过程中为实现最佳预测质量而优化的变量。随着参数数量的增加,模型能够获得更多的粒度知识并改进其预测。自 2017-2018 年推出第一批 LLMs 以来,我们看到参数大小呈指数级爆炸——突破性的伯特用 3.4 亿个参数训练,威震天-图灵 NLG,2022 年发布的模型,用 530 亿个参数训练——增加了一千多倍。

图 1:语言模型的参数大小随时间呈指数增长【11】

因此,主流不断用越来越多的参数让公众惊叹。然而,有批评的声音指出,模型的性能并没有随着模型大小的增加而增加。另一方面,模型预训练可能会留下相当大的碳足迹。缩小规模的努力对抗了蛮力方法,使语言建模的进展更可持续。

语言模型的生命

LLM 领域竞争激烈,创新短暂。下图显示了 2018 年至 2022 年期间最受欢迎的前 15 名 LLM,以及他们在一段时间内的话语权份额:

图 2:前 15 个最受欢迎的语言模型的提及和声音份额【12】

我们可以看到,大多数模型在相对较短的时间后会逐渐淡出人们的视线。为了保持领先,用户应该监控当前的创新,并评估升级是否值得。

大多数 LLM 遵循相似的生命周期:首先,在“上游”,模型被预先训练。由于对数据大小和计算的严格要求,这主要是大型科技公司和大学的特权。最*,也有一些合作努力(例如大科学研讨会)来共同推进 LLM 领域。Cohere 和 AI21 Labs 等少数资金雄厚的初创公司也提供预培训的法律硕士。

发布之后,该模型被专注于应用程序的开发人员和企业在“下游”采用和部署。在这个阶段,大多数模型都需要对特定的领域和任务进行额外的微调。其他的,如 GPT-3,更方便,因为他们可以在预测期间直接学习各种语言任务(零或少射预测)。

最后,时间敲门,一个更好的模型即将出现——要么有更多的参数,更有效地利用硬件,要么对人类语言的建模进行更根本的改进。带来实质性创新的模型可以产生完整的模型家族。比如 BERT 住在 BERT-QA,DistilBERT,RoBERTa,都是基于原来的架构。

在接下来的部分中,我们将关注这个生命周期中的前两个阶段——预培训和部署的微调。

前期培训:法律硕士是如何诞生的

大多数团队和 NLP 实践者不会参与 LLM 的预培训,而是参与它们的微调和部署。然而,要成功地选择和使用一个模型,重要的是要理解“幕后”发生了什么。在本节中,我们将了解 LLM 的基本要素:

  • 培训用数据
  • 输入表示
  • 培训前目标
  • 模型架构(编码器-解码器)

这些不仅会影响选择,还会影响 LLM 的微调和部署。

培训用数据

用于 LLM 训练的数据大多是涵盖不同风格的文本数据,如文献、用户生成的内容和新闻数据。在看到各种不同的文本类型后,生成的模型开始意识到语言的细节。除了文本数据之外,代码经常被用作输入,教导模型生成有效的程序和代码片段。

不出所料,训练数据的质量对模型性能有直接影响,也对模型所需的大小有直接影响。如果您在准备训练数据方面很聪明,您可以在减少模型大小的同时提高模型质量。一个例子是 T0 模型,它比 GPT-3 小 16 倍,但在一系列基准任务上优于它。诀窍在于:它不仅仅使用任何文本作为训练数据,而是直接使用任务公式,从而使其学习信号更加集中。图 3 展示了一些培训示例。

图 3: T0 接受了针对各种语言任务的明确任务公式的训练

关于训练数据的最后一点说明:我们经常听说语言模型是以无监督的方式训练的。尽管这让它们很有吸引力,但从技术上来说这是错误的。相反,格式良好的文本已经提供了必要的学习信号,省去了手动标注数据的繁琐过程。要预测的标签对应于句子中的过去和/或未来单词。因此,注释是自动和大规模发生的,使得该领域的相对快速的进展成为可能。

输入表示

一旦收集了训练数据,我们需要将它打包成模型能够消化的形式。神经网络以代数结构(向量和矩阵)为基础,语言的最佳代数表示是一个持续的追求——从简单的单词集到包含高度分化的上下文信息的表示。每一个新的步骤都让研究人员面临自然语言无尽的复杂性,暴露了当前表示法的局限性。

语言的基本单位是单词。在 NLP 的初期,这产生了天真的单词包表示法,将文本中的所有单词放在一起,不管它们的顺序如何。考虑这两个例子:

在单词袋的世界里,这些句子会得到完全相同的表示,因为它们由相同的单词组成。显然,它只包含了它们的一小部分含义。

顺序表征包含了关于词序的信息。在深度学习中,序列的处理最初是在顺序感知的递归神经网络 (RNN)中实现的。[2]然而,更进一步说,语言的基本结构不是纯粹的顺序结构,而是层次结构。换句话说,我们讨论的不是列表,而是树。相距较远的单词实际上比相邻的单词具有更强的句法和语义联系。考虑下面的例子:

在这里,她的指的是那个的女孩。当 RNN 到达句子的结尾,最后看到她的时,它对句子开头的记忆可能已经褪色,因此不允许它恢复这种关系。

为了解决这些长距离依赖性,提出了更复杂的神经结构来建立对上下文的更有区别的记忆。这个想法是将与未来预测相关的单词保留在记忆中,而忘记其他单词。这是长短期记忆(LSTM)[3]细胞和门控循环单位(GRUs)4的贡献。然而,这些模型并不针对要预测的特定位置进行优化,而是针对一般的未来环境。此外,由于其复杂的结构,它们甚至比传统的 rnn 训练更慢。

最后,人们摒弃了循环,提出了注意力机制,并将其整合到转换器架构中。5注意力允许模型在预测过程中在不同的单词之间来回聚焦。根据每个单词与要预测的特定位置的相关性对其进行加权。对于上面的句子,一旦模特到达的位置,她的女孩在处的权重会比高,尽管事实上它在线性顺序中要远得多。

迄今为止,注意力机制最接*人类大脑在信息处理过程中的生物工作。研究表明,注意力学习层次句法结构,包括一系列复杂的句法现象(参见BERTology 初级读本及其中引用的论文)。它还允许并行计算,因此,更快和更有效的训练。

培训前目标

有了合适的训练数据表示,我们的模型就可以开始学习了。有三个用于预训练语言模型的通用目标:序列到序列的转导、自回归和自动编码。所有这些都要求模型掌握广泛的语言学知识。

编码器-解码器架构以及变换器模型处理的原始任务是序列到序列的转换:将序列转换成不同表示框架中的序列。经典的序列到序列的任务是机器翻译,但其他任务,如总结,经常以这种方式制定。请注意,目标序列不一定是文本,它也可以是其他非结构化数据(如图像)以及结构化数据(如编程语言)。序列间 LLM 的一个例子是 BART 系列。

第二个任务是自回归,这也是最初的语言建模目标。在自回归中,模型学习根据以前的标记预测下一个输出(标记)。学习信号受到企业单向性的限制——模型只能使用来自预测令牌右侧或左侧的信息。这是一个主要的限制,因为单词可以依赖于过去以及未来的位置。例如,考虑动词书写如何在两个方向上影响下面的句子:

在这里,纸张的位置被限制为可写的东西,而学生的位置被限制为人类,或者,无论如何,另一个能够书写的智能实体。

许多今日头条的 LLM 都是自回归的,包括 GPT 家族、PaLM 和 BLOOM。

第三项任务—自动编码—解决了单向性问题。自动编码非常类似于经典单词嵌入的学习。[6]首先,我们通过在输入中隐藏一定比例的标记(通常为 10-20%)来破坏训练数据。然后,该模型学习基于周围环境重建正确的输入,同时考虑前面和后面的标记。自动编码器的典型例子是 BERT 系列,其中 BERT 代表变压器的双向编码器表示。

模型架构(编码器-解码器)

语言模型的基本构件是编码器和解码器。编码器将原始输入转换为高维代数表示,也称为“隐藏”向量。等一下——藏起来了?事实上,在这一点上没有什么大秘密。当然你可以看看这个表示,但是一个冗长的数字向量对人类来说没有任何意义。需要我们模型的数学智能来处理它。解码器以可理解的形式再现隐藏的表示,例如另一种语言、编程代码、图像等。

图 4:编码器-解码器架构的基本模式(英德翻译示例)

编码器-解码器架构最初是为递归神经网络引入的。自从引入基于注意力的变压器模型以来,传统的递归已经失去了它的流行性,而编码器-解码器的思想仍然存在。大多数自然语言理解(NLU)任务依赖于编码器,而自然语言生成(NLG)任务需要解码器,序列到序列的转换需要这两个组件。

在这里,我们不会深入讨论 Transformer 架构和注意机制的细节。对于那些想掌握细节的人来说,要准备好花大量的时间来思考这个问题。在原始论文之外,[7]和[8]提供了极好的解释。对于轻量级的介绍,我推荐吴恩达序列模型课程中的相应章节。

在现实世界中使用语言模型

微调

语言建模是一项强大的上游任务——如果你有一个成功生成语言的模型,那么恭喜你——这是一个智能模型。然而,拥有一个充满随机文本的模型的商业价值是有限的。相反,NLP 主要用于更有针对性的下游任务,如情感分析、问题回答和信息提取。是时候应用迁移学习并重用现有的语言知识来应对更具体的挑战了。在微调过程中,模型的一部分被“冻结”,其余部分用特定领域或特定任务的数据进一步训练。

显式的微调增加了 LLM 部署的复杂性。它还可能导致模型爆炸,每个业务任务都需要自己的微调模型,升级为不可维护的各种模型。因此,人们已经努力使用少量或零次学习来消除微调步骤(例如,在 GPT-3 [9])。这种学习发生在预测过程中:模型被输入“提示”——任务描述和潜在的几个训练示例——以指导其对未来示例的预测。

虽然实现起来要快得多,但零次或少次学习的便利因素被其较低的预测质量抵消了。此外,许多这些模型需要通过云 API 来访问。在开发的开始阶段,这可能是一个受欢迎的机会——然而,在更高级的阶段,它可能会变成另一个不想要的外部依赖。

为您的下游任务选择正确的模型

看着人工智能市场上新语言模型的持续供应,为特定的下游任务选择正确的模型并与最先进的技术保持同步可能是棘手的。

研究论文通常根据特定的下游任务和数据集对每个模型进行基准测试。标准化的任务套件如强力胶大工作台允许对大量 NLP 任务进行统一的基准测试,并提供比较的基础。尽管如此,我们应该记住,这些测试是在高度控制的环境下准备的。截至目前,语言模型的泛化能力相当有限,因此,转移到现实生活的数据集可能会显著影响模型的性能。评估和选择合适的模型应包括对尽可能接*生产数据的数据进行实验。

根据经验,预训练目标提供了一个重要的提示:自回归模型在文本生成任务上表现良好,如对话式人工智能、问题回答和文本摘要,而自动编码器擅长“理解”和结构化语言,例如用于情感分析和各种信息提取任务。旨在用于零射击学习的模型理论上可以执行各种任务,只要它们接收到适当的提示——然而,它们的准确性通常低于微调模型。

为了使事情更具体,下面的图表显示了流行的 NLP 任务如何与 NLP 文献中突出的语言模型相关联。基于多个相似性和聚集度量来计算关联,包括嵌入相似性和距离加权共现。评分较高的模型-任务对,如 BART /文本摘要和 LaMDA /对话式 AI,表明基于历史数据的良好匹配。

图 5:语言模型和下游任务之间的关联强度【12】

关键要点

在本文中,我们已经讨论了 LLM 的基本概念和创新发生的主要方面。下表总结了最流行的 LLM 的主要特性:

表 1:最流行的大型语言模型的特性总结

让我们总结一些选择和部署 LLM 的一般准则:

1.在评估潜在模型时,要清楚自己在人工智能旅程中的位置:

  • 一开始,尝试通过云 API 部署 LLM 可能是个好主意。
  • 一旦您找到了适合产品市场的产品,就可以考虑在您这边托管和维护您的模型,以便对您的应用程序进行更多的控制并进一步提高模型性能。

2.为了与您的下游任务保持一致,您的 AI 团队应该基于以下标准创建一个模型的短列表:

  • 学术文献中的基准结果,重点是你的下游任务
  • 预训练目标和下游任务之间的一致性:考虑 NLU 的自动编码和 NLG 的自回归
  • 先前报告的这种模型-任务组合的经验(参见图 5)

4.然后,应该对照您的真实任务和数据集来测试入围的模型,以获得对性能的第一感觉。
5。在大多数情况下,通过专门的微调,您可能会获得更好的质量。然而,如果你没有内部技术技能或预算进行微调,或者如果你需要完成大量的任务,可以考虑少量/零尝试学习。
6。LLM 的创新和趋势是短暂的。当使用语言模型时,关注它们的生命周期和 LLM 环境中的整体活动,并寻找机会来提高你的水*。

最后,要意识到 LLM 的局限性。虽然它们拥有惊人的、类似人类的语言表达能力,但它们的整体认知能力与我们人类相去甚远。这些模型的世界知识和推理能力严格限于它们在语言表层找到的信息。他们也不能及时了解事实,可能会毫不犹豫地向你提供过时的信息。如果您正在构建一个依赖于生成最新甚至原始知识的应用程序,请考虑将您的 LLM 与其他多模态、结构化或动态知识源相结合。

参考

[1]维克多·桑等人 2021。多任务提示训练使零射击任务泛化。更正,abs/2110.08207。
[2]约舒阿·本吉奥等译 1994。学习具有梯度下降的长期依赖性是困难的IEEE 神经网络汇刊,5(2):157–166。
[3]塞普·霍克雷特和于尔根·施密德胡贝尔。1997.长短期记忆神经计算,9(8):1735–1780。
4 Kyunghyun Cho 等 2014。关于神经机器翻译的性质:编码器-解码器方法。第八届 SSST 会议记录,第八届统计翻译句法、语义和结构研讨会,第 103-111 页,卡塔尔多哈。
5阿希什·瓦斯瓦尼等 2017。你需要的只是关注。载于神经信息处理系统进展,第 30 卷。[6]托马斯·米科洛夫等人,2013 年。词和短语的分布式表示及其组合性。更正,abs/1310.4546。
【7】杰伊·贾拉马尔。2018.图示变压器
[8]亚历山大·拉什等 2018。带注释的变压器
[9]汤姆·布朗等著 2020。语言模型是一次性学习者。第 34 届国际神经信息处理系统会议论文集,NIPS'20,美国纽约红钩。Curran Associates Inc .
[10]Jacob Devlin 等人 2019。 BERT:用于语言理解的深度双向转换器的预训练。在计算语言学协会北美分会 2019 年会议记录:人类语言技术,第 1 卷(长和短论文),第 4171-4186 页,明尼苏达州明尼阿波利斯。
[11]朱利安·西蒙 2021。大型语言模型:新摩尔定律?
【12】基础数据集:领先的人工智能智库在专业人工智能资源、技术博客和出版物中发表的 2018-2022 年超过 32 万篇关于人工智能和自然语言处理的文章。

除非另有说明,所有图片均为作者所有。

Choropleth 地图与熊猫和繁荣

原文:https://towardsdatascience.com/choropleth-maps-with-pandas-and-flourish-aea2ad9ec5e3

使用 Python、pandas 和 fluore 制作美国脊髓灰质炎疫苗接种率地图的教程

图片由作者提供,使用 Flourish.studio

背景

在之前的一篇文章中,我简要介绍了如何制作与废水追踪相关的美国县数据的 choropleth 地图。当然,这里使用的方法可以应用于其他数据,并且有许多映射选项没有在那些指令中显示。

本教程包含更多关于使用 fluore 制作地图的详细信息——使用美国各州脊髓灰质炎疫苗接种率的示例数据,并创建如上所示的地图

首先,几个定义…

  • choropleth map 是一种地理地图(如县或州),通过对地理区域进行颜色编码来显示数据值。
  • fruitfruit . studio的简写,这是一家总部位于伦敦的软件公司,提供非编程数据可视化。地理地图是一种数据可视化。

美国儿童脊髓灰质炎疫苗接种的标准时间表是总共四剂,其中三剂在儿童三岁生日之前给药。在本教程中,我们做了一个简化的假设,一个在三岁生日前接种了全部三针的孩子被称为“接种疫苗”,一个没有接种的孩子被称为“未接种疫苗”。

准备数据

关于美国各种疾病和年龄组的儿童早期疫苗接种率的数据包含在 CDC 的单一数据集中。简单的 Python/pandas 数据准备将原始数据集翻译成地图输入文件

import pandas as pd 
from sodapy import SocrataPOLIO_MAP_DATA = "polio_coverage_map.tsv"
VAX_DATASET = "fhky-rtsk"   # CDC code for this dataset# Get the data, which covers many vaccines and age groups.cdc_client = Socrata("data.cdc.gov", None)
VaxDF = pd.DataFrame.from_records(cdc_client.get(VAX_DATASET, limit=1000000))# Select rows about 3 doses of polio vax.PolioDF = VaxDF.query("vaccine == 'Polio'")
PolioDF = PolioDF.query("dose == '≥3 Doses'")# Select rows for the 50 US states.PolioDF = PolioDF.query("geography_type == 'States/Local Areas'")
PolioDF = PolioDF.query("geography != 'Puerto Rico'") # Use the latest year in the dataset.PolioDF = PolioDF.query("year_season == '2018'") # Get rows for almost-3-year olds.PolioDF = PolioDF.query("dimension_type == 'Age'")
PolioDF = PolioDF.query("dimension == '35 Months'")# Select the columns we need for mapping.PolioDF = PolioDF[["geography", "coverage_estimate", "population_sample_size"]]# Make the data file that becomes the input to the map.print ("\nWriting map data to " + POLIO_MAP_DATA)
PolioDF.to_csv(POLIO_MAP_DATA, encoding='utf-8', sep='\t', index=False)

生成的地图文件如下所示:

开始繁荣

个人使用免费,商业和企业用户有付费选项。我所有的作品都是免费版的。

创建一个繁荣的帐户,并点击新的可视化。你用“繁荣”制作的一切都是“可视化”,包括条形图,折线图,散点图,层次图,2D 和三维地图。

选择并清理地图模板

内置模板是启动任何项目的最佳方式,但是在使用自己的数据和设计之前,您需要清除示例数据和设置。

向下滚动到投影地图/美国各州,并选择它作为您的起点。在左上角给你的地图命名。

此示例将仅使用区域,而不使用地图点,因此移除点数据:

  • 转到预览窗格,并设置点层=禁用。
  • 确保不使用任何点数据。转到数据窗格/数据点,删除你在最右边看到的 A,B,C,D。
  • 删除所有的点数据。点按 A 列的标题,然后按住 SHIFT 键点按 D 列的标题,以便选择所有列。下拉 D 列标题中的小部件,并选择 Remove Columns。

清除您将替换的区域数据:

  • 转到“数据/区域”并删除最右侧列出的任何列,除了“几何图形= A”和“名称= B”
  • 完全移除 C、D 和 E 列。

转到预览窗格。你应该会看到一张空白的美国地图。当您将鼠标悬停在一个州上时,它的轮廓会高亮显示,其名称会弹出。您的地图现在已准备好接收有关脊髓灰质炎疫苗接种的数据。

导入和合并数据

转到数据/区域。更改右侧的上传选项,上传数据并合并。按下那个按钮。

当前数据(flower 模板中的地图地理)与传入数据(Python 中的脊髓灰质炎疫苗文件)正确对齐非常重要。fruit 善于猜测哪些列应该相互连接。在这种情况下,模板中的名称列应该连接到传入数据中的地理位置列。您通常可以保留其他选项的默认设置。因此,上传/合并操作如下所示:

作者图片

继续进行合并,然后选择 Next。

设置 Choropleth 值和颜色

choropleth 映射的目标是将数据值(数字或关键字)显示为颜色。对于如何给地图上色,您有许多选择,因此您应该尝试各种方案,看看哪种方案最能表达您想要讲述的数据故事。有内置的配色方案,你可以定义自己的。颜色可以是分级的(一组固定的颜色,每个颜色代表一个值范围)或连续的(根据数值字段改变阴影)。任何配色方案都可以轻松反转,因此低/高颜色可以互换。

制作同样的脊髓灰质炎疫苗接种图如本文顶部所示:

  • 转到数据/区域。
  • 设置值= C。(该值决定哪个变量控制着色。)
  • 转到预览/区域/填充。
  • 设置刻度类型=数字、顺序、线性。
  • 设置调色板=绿色,不反转。

您应该看到这个:

图片由作者提供,使用 Flourish.studio

以下是具有相同数据的完全相同的配色方案,但颜色相反:

图片由作者提供,使用 Flourish.studio

以下是使用等离子调色板的相同数据:

图片由作者提供,使用 Flourish.studio

标度类型=数值型、发散型、分仓型怎么样;调色板=红色/蓝色?顺序/发散指的是颜色轴。连续通常是同一颜色的阴影,而发散是两种不同的颜色。

图片由作者提供,使用 Flourish.studio

哪个地图比较好?哪个更清楚的说出了你想要传达的故事?这些都是主观问题,是你做的每一张地图的选择。

此外,还有如何处理缺失值区域的选项,为准 3D 效果添加阴影,以及许多其他可能性。请注意,你可能会把自己逼疯,试图找到最好的总体结果。

设置区域弹出窗口

choropleth 地图上的颜色编码有助于突出显示您希望查看者关注的地理区域。但是查看感兴趣区域的更多详细信息也很有帮助。为此,您需要弹出窗口:

  • 转到弹出窗口的数据/区域/元数据(在右侧控件的底部)。
  • 将弹出菜单设置为 C,d。
  • 转到预览。当您将鼠标悬停在每个区域上时,您应该会看到该区域的疫苗接种百分比和数据样本量。

设置页眉、页脚和致谢名单

由于大多数地图都需要标题和数据配额:

  • 转到预览/页眉/标题(和副标题)。想怎么设置就怎么设置。
  • 转到预览/页脚/源名称(和 URL)。根据需要设置。我建议保持名称简短,因为几个学分很快就会填满地图页脚。您可以添加多个信号源。
  • 您输入的任何要注意的内容都会出现在源演职员表之后。

发布地图

要使您的地图对其他人可见:

  • 单击导出和发布/发布以共享和嵌入/发布。
  • 将向您显示公共 URL 和嵌入地图的代码。

如果您稍后对地图进行了更改,您可以在相同的 URL 上重新发布,以便读者可以立即看到更新。

了解更多信息

en.wikipedia.org/wiki/Choropleth_map(关于 choropleth 地图的一般信息)

help . friedy . studio(兴旺帮话题)

【www.cdc.gov/vaccines/ (更多疾控中心疫苗数据)

使用 GitHub 动作进行机器学习模型训练的 CI/CD

原文:https://towardsdatascience.com/ci-cd-for-machine-learning-model-training-with-github-actions-24b6a28cab96

使用 EC2 实例作为训练机器学习模型的服务器的综合指南

图片由this engineering RAEngUnsplash 上拍摄

介绍

可以使用多个开源工具来执行机器学习流水线的适当编排。Github actions 是众所周知的。它是一个内置的 Github 工具,主要用于自动化软件的开发、测试和部署过程。

如今,机器学习实践者已经在使用它来自动化他们项目的整个工作流程。所有这些工作流都与可以使用 Github actions 服务器或您自己的服务器执行的特定作业相关。

在这个概念 blob 的结尾,你会明白:

  • 招待自己的跑步者的好处。
  • 如何创建 EC2 实例并为手头的任务配置它。
  • 使用您的跑步者通过 GitHub 动作实现机器学习工作流程。
  • 使用 DVC 存储模型元数据。
  • MLFlow 来跟踪您的模型的性能。

为什么要使用自托管运行程序?

托管自己的运行程序意味着您可以在定制的硬件环境中执行作业,并拥有所需的处理能力和内存存储。

这样做有以下好处:

  • 用户可以轻松地增加或减少跑步者的数量,这在并行训练模型时是有益的。
  • 在操作系统方面没有限制。Linux、Windows 和 macOS 都受支持。
  • 当使用 AWS、GCP 或 Azure 等云服务时,跑步者可以受益于所有服务,具体取决于订阅级别。

如何配置您的 AWS EC2 实例?

为了成功地执行这一部分,我们需要执行两个主要任务。首先,获取 AWS 和 Github 凭证,然后设置加密的秘密来同步 EC2 和 Github。

获取您的 AWS 和 Github 证书

在开始之前,你需要首先创建一个 EC2 实例,这可以从这篇文章中完成。需要三个主要的证书,下面详细介绍了获取它们的过程。

→从您的 Github 帐户获取您的[PERSONAL_ACCESS_TOKEN](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token)。这被用作密码的替代,需要与 Github API 交互。

→完成以下 5 个步骤后,可以取回ACCESS_KEY_IDSECRET_ACCESS_KEY:

  • 点击右上角附*的用户名。
  • 选择安全凭据选项卡。
  • 选择访问密钥(访问密钥 ID 和秘密访问密钥)。
  • 创建新的访问密钥。
  • 单击显示访问密钥查看您的访问密钥 ID 和秘密访问密钥。

获取 AWS 访问密钥 ID 和秘密访问密钥的 5 个步骤的图示(图片由作者提供)

为同步设置加密的机密

这一步是从 Github 上的项目存储库中执行的。Khuyen Tran 在下一篇文章的的加密秘密部分描述了所有步骤。

最后你应该会有和我差不多的东西,像这样。

添加到 Github secrets 的环境机密(图片由作者提供)

使用 GitHub actions 和您的自托管 EC2 runner 实现机器学习工作流。

本节涵盖的机器学习任务是使用 BERT 模型实现 3 类分类。这是使用以下工作流程执行的:

数据采集→数据处理→模型训练&评估→模型&元数据序列化。

我们将首先解释每个步骤中执行的底层任务及其源代码,然后解释如何使用我们的自托管 runner 运行模型训练。

数据采集:负责从 DVC 存储器采集数据。

get_data.py

数据处理:为简单起见,仅限于去除特殊字符。

准备 _ 数据. py

模型训练&评测:创建一个 BERT 变压器模型。

模型训练包括两个主要部分:(1)训练和评估模型性能,以及(2)使用 MLFlow 跟踪度量。

培训生成两个主要文件:

  • model/finetuned_BERT_epoch{x}.model:对应于第 n 个x纪元后生成的微调后的 BERT 模型。
  • metrics/metrics.json:包含先前生成的模型的精度、召回率和 F1 值。

对于模型跟踪,我们需要从您的 DagsHub 项目存储库的右上角获取如下凭证。

从 DagsHub 项目获取 MLFlow 凭据的步骤(图片由作者提供)

下面的脚本只显示了 MLFlow 的模型跟踪部分,因为显示所有的源代码会太长。然而,完整的代码可以在文章的末尾找到。

培训文件中的 MLFlow 部分。

Model &元数据序列化:负责将以下元数据保存到 DVC 存储: metrics.json,fine tuned _ BERT _ epoch { x } . Model .

使用 EC2 自托管跑步者训练您的模型。

这一节主要关注在提供自托管运行器之后启动模型的训练,该运行器将用于训练模型,而不是使用默认的 GitHub actions 运行器。

培训工作流程在.github/workflows/training.yaml中实施

您可以随意命名training.yaml文件。但是,它必须位于.github/workflows文件夹中。yaml 扩展。这样它将被 Github 认为是一个工作流文件。

以下是培训工作流程的一般格式:

training.yaml

  • name:工作流程的名称。
  • on : push 和 pull_request 是我们案例中负责触发整个工作流的主要事件。
  • jobs:包含我们工作流程中的一组作业,它们是:(EC2 转轮的部署,以及(2)模型的训练。

每项工作的步骤是什么?

在深入研究源代码之前,让我们理解工作流的底层可视化说明。

博客范围的一般工作流程(图片由作者提供)

  1. 工作流由开发人员/机器学习工程师的推或拉请求启动。
  2. 该训练是在供应的 EC2 实例中触发的。
  3. 元数据(metrics.json 和 model)存储在 DVC 上,度量值由 DagsHub 上的 MLFlow 跟踪。

自托管 runner (training.yaml)中培训流程的完整工作流程

→第一个作业 : **deploy-runner**

我们从使用 连续机器学习库开始,通过迭代 来自动化服务器供应和模型训练。然后,我们获得运行资源调配所需的所有凭据。

正在使用的 EC2 实例是:

  • 自由层t2.micro实例。
  • 位于us-east-1a区域。
  • cml-runner标签标记,该标签将用于在训练模型时识别实例。

→第二个作业 : **train-model**

通过使用先前提供的运行器,我们能够使用 CML 库来执行训练。

将元数据保存在 DVC 存储中。

从训练步骤生成的所有文件都自动存储在本地机器中。但是我们可能希望跟踪这些数据的所有变化。这就是我们在本节中使用[dvc](https://dvc.org/)的原因。

总共有两个主要步骤:(1)获取您的凭证,以及(2)实现推送数据的逻辑。

→获取您的 DVC 证书。

获取您的 DVC 证书遵循相同的流程,类似于 MLFlow:

从 DagsHub 项目获得 DVC 证书的步骤(图片由作者提供)

执行逻辑

首先,我们在save_metadata.py文件中实现 DVC 配置和数据保存逻辑。

save _ 元数据. py

然后,这个文件被工作流中的save_metadata作业调用。

jobs: ... # deploy-runner & train-model jobs save_metadata:
   steps:
     - name: Save metadata into DVC
       run: python save_metadata.py 

现在,我可以提交更改并在 Github 上推送代码,以便能够执行拉请求。

git add . 
git commit -m “Pushing the code for pull request experimentation”
git push -u origin main

在将 epochs 的数量更改为 2 之后,我们得到了触发 pull 请求的新版本的训练脚本。下面是一个插图。

拉取请求

下图显示了 pull 请求之前和之后的模型度量,分别针对时段 1 和时段 2。

结论

当使用 Github 操作实现完整的机器学习管道时,使用自托管服务器在许多方面都有好处,如本文开头所示。

在这篇概念博客中,我们探讨了如何提供 EC2 实例,从 push 和 pull 请求中触发模型序列,然后将元数据保存到 DVC 存储中,并使用 MLFlow 跟踪模型性能。

掌握这些技能将帮助您提供一项宝贵的技能,将您组织的整个机器学习管道提升到下一个级别。

此外,如果你喜欢阅读我的故事,并希望支持我的写作,考虑成为一个媒体成员解锁无限制访问媒体上的故事。

欢迎在媒体推特YouTube 上关注我,或者在 LinkedIn 上问好。讨论人工智能、人工智能、数据科学、自然语言处理和人工智能是一种乐趣!

额外资源

项目源代码

关于自托管跑步者

在自托管 AWS EC2 runner 上使用 CML 训练和保存模型。

带顶点和人工智能*台的 TFX 管道 CI/CD

原文:https://towardsdatascience.com/ci-cd-for-tfx-pipelines-with-vertex-and-ai-platform-c562452fba1

使用顶点管道部署 TFX 管道,在人工智能*台上服务您的模型,监控您的请求,并构建 CI/CD 管道来扩展您的系统

里卡多·戈麦斯·安吉尔在 Unsplash 上的照片

1。简介

本文是两部分的第二部分,第一部分可在TFX 管道情绪分析—本地部署获得。在第一部分中,我们讨论了如何在本地开发管道,此外还展示了构建情感分析模型的过程。

在这一部分中,我们将介绍如何在云环境中运行我们的管道,如何开发 CI/CD 管道,以及如何监控我们模型的服务请求。

完成本文后,您将能够:

  • 创建情感分析模型
  • 创建一个与顶点 AI 集成的 TFX 管道
  • 通过 REST API 在云环境中服务您的模型
  • 分析您的管道元数据
  • 创建 CI/CD 管道
  • 使用 BigQuery 分析推论

整个应用程序的代码可以在这个 GitHub 存储库中找到

2。顶点 AI + TFX 管线

在第一部分,我们解释了什么是 TFX 管道,它的组成部分是什么,以及我们的模型是如何工作的。现在,让我们来关注如何在云环境中使用这个管道,以及如何构建 CI/CD 管道来扩展我们的系统。

当部署一个模型时,我们需要考虑如何让最终用户可以使用它。最常用的方法之一是通过 REST API 中的请求。

在本教程中,我们将使用谷歌云*台 (GCP)的服务部署我们的管道,一般来说,我们将使用两个主要服务:

2.1 顶点 AI

Vertex AI 是谷歌新的 AI *台,联合所有谷歌云服务构建 ML。我们将使用顶点管道来运行我们的训练管道,主要有两个原因:

  • AI *台管道 便宜——在我们这里。顶点管道具有每次运行的固定执行率,也与通过计算引擎训练模型的成本相关联。无需激活和管理集群,使基础架构更精简、更便宜。
  • 与人工智能*台中使用的谷歌 Kubernetes 引擎(GKE)不同,我们不需要管理服务器和集群的基础设施/健康状况,因为 Vertex 是一个自我管理的*台。

2.2 人工智能*台

虽然我们的管道使用顶点管道运行,但我们的模型将在 AI *台上训练和服务,主要原因有一个:

到目前为止,无法通过顶点 AI 中的函数 [预测](https://cloud.google.com/ vertex-ai/docs/reference/rest/v1/projects.locations.endpoints/predict) 来通知saved modeltensor flow 的订阅。

因此,我们的管道的工作原理如下:

图一。 TFX 管道上的云。这张图片是从谷歌创作和分享的作品中复制的,并根据知识共享 4.0 归属许可中描述的条款使用。来源:https://www.tensorflow.org/tfx/guide

3。云上 TFX 管道

现在,让我们了解如何将我们的管道迁移到云环境。

3.1 配置项目

重要:这个项目将有成本。谷歌云的新用户可能有资格获得免费试用。

首先,您需要在 Google Cloud Console 中创建一个项目,并启用以下 API:

  • 顶点人工智能
  • 人工智能*台训练和预测
  • 云存储
  • 集装箱登记处
  • BigQuery
  • 云构建
  • 笔记本 API

3.2 管道设置

现在让我们打开 configs.py 文件并分析一些变量:

  • 【GCP _ 人工智能 _ *台 _ 训练 _ARGS —传递给人工智能*台训练我们模型的参数。
  • 【GCP _ AI _ *台 _ 服务 _ ARGS——传递给 AI *台服务我们模型的参数。

添加这些参数是为了在 kubeflow_v2_runner 中创建管道。py 文件

现在,让我们来看看相对于路径的变量。请注意,在代码中,所有变量都指向 Google 云存储中的存储桶,这是因为当我们要构建 CI/CD 管道时,这些文件将在云环境中找到并可用。

  • TRANSFORM _ MODULE _ FILE—包含变换组件的变换的文件。
  • TRAIN _ MODULE _ FILE—包含训练器组件说明的文件。
  • TUNER _ MODULE _ PATH—包含一个best _ hyperparameters . txt文件的目录,该文件包含模型的超参数。
  • 数据路径 —包含我们数据的目录。
  • LABEL _ ENCODER _ FILE—包含我们的 LabelEncoder 来转换我们的目标变量的文件。

3.3 为云构建创建 Docker 映像

我们需要创建一个 Docker 映像,供云构建使用。我们创建此映像是因为默认 Python 映像提供的 Python 版本是 3.9,但最新的 TFX 版本仅支持 Python 3.8 或更低版本。

因此,我们必须访问 Vertex AI -> Workbench 并创建一个笔记本来定义我们的图像。这个步骤也可以在本地完成或者在云壳中完成。

打开终端并克隆 repo:

git clone [https://github.com/arturlunardi/sentiment-analysis-tfx](https://github.com/arturlunardi/sentiment-analysis-tfx)

在存储库内部,打开create _ first _ container . ipynb记事本,运行代码行在指定的路径中创建容器。

运行后,您可以在容器注册表中检查图像。

3.4 创建存储桶

为了让我们能够将文件从我们的存储库复制到 Google 云存储的中,我们需要创建文件夹来复制它们。

访问项目中的云存储,并使用以下目录结构创建一个存储桶:

└───{{ GOOGLE_CLOUD_PROJECT }} + '-vertex-default'
    └───{{ PIPELINE_NAME }}
        ├───best_hyperparameters
        ├───data
        └───modules

3.5 创建服务账户

为了让 GitHub Actions 能够在 Cloud Build 中执行任务,我们需要创建一个用于身份验证的服务帐户。

创建服务帐户并添加以下权限:

  • AI *台管理员
  • 顶点人工智能服务代理
  • 服务代理顶点人工智能自定义代码
  • 云构建服务帐户
  • 电视观众

然后在 keys -> Add Key -> JSON 中创建一个 Key。

最后一步是必要的。由于我们要通过 Vertex AI 服务从 Google 云存储中读取文件,所以我们需要给 Vertex AI 服务帐户授予读取权限。

然后进入 IAM 和 admin ->勾选右侧包含 Google 提供的角色授予->找到名为:AI *台自定义代码服务代理->添加查看者角色的账户。

3.6 在 Git Repo 中设置秘密

我们的 CI/CD 管道将使用一些秘密的变量,所以我们需要添加它们。

转到您的存储库->设置->机密->操作,并创建以下机密:

  • GCP 项目标识:你的项目标识
  • GCP _ 凭证:复制并粘贴上面创建的服务帐户的 JSON 密钥的全部内容

4。CI/CD 管道

根据 Park,c;在软件工程中,持续集成(CI)和持续交付(CD)是两个非常重要的概念。CI 是集成变更(新特性、批准的代码提交等。)进入你的系统。CD 是您可靠地、持续地部署这些变更的时候。CI 和 CD 既可以单独执行,也可以耦合执行。

为了使我们的系统可伸缩,我们需要使用 CI/CD 实践。这将允许我们更快、更可靠地应用实验、集成和部署。让我们分析几个场景:

  • 有了新数据,我如何定期重新培训我的模型?
  • 添加新功能时,我如何确保我的系统不会崩溃?
  • 在部署我的模型之后,我如何通过 API 测试预测服务以确保输入和预测被正确解释?

在生产中实现 ML 不仅仅意味着通过 API 提供模型,还意味着实现一个可扩展的、适应变化的管道,允许快速实验并提供准确的结果。如果你想了解更多关于 CI/CD 管道及其重要性的知识,我推荐这篇谷歌文章, MLOps:机器学习中的连续交付和自动化管道

在这个项目中,我们的 CI/CD 渠道将按如下方式工作:

  1. 我们创建了一个 Github 库,包含了将在 Vertex AI 上运行的 TFX 管道的所有代码。
  2. 我们在 GitHub Actions 上设置了一个流程,它将触发对主分支的每次推送,并将检查我们的存储库的特定目录中的更改。
  3. 如果有变化,对每种类型的变化采取特定的行动。

YAML 文件后面的定义了一个 GitHub 动作,由对主分支的推送激活。

让我们理解重要的一点:GitHub Actions 的 dorny/paths-filter 允许我们检测使用 push 时某些路径是否发生了变化。

在这种情况下,我们看到两条路径: tfx-pipeline模块:

  • 如果 tfx-pipeline 路径有变化,使用Build/full-pipeline-deployment . YAML文件中提供的设置,通过 —替换使用环境变量,启动云构建过程。
  • 如果模块路径有变化,而 tfx_pipeline 没有变化(为了避免重复运行),则使用Build/partial-pipeline-deployment . YAML文件中提供的设置启动云构建过程,通过 —替换使用环境变量。

现在让我们仔细分析这两个文件。

4.1 全管道部署. yaml

图二。整个 CI/CD 管道。本图片转载自 Park,c .创作并分享的作品;并根据 Apache 2.0 许可证中描述的条款使用。来源:https://cloud . Google . com/blog/topics/developers-从业者/模型-培训-cicd-system-part-i

如果在包含与管道设置相关的文件的目录中有变化,流程将启动云构建过程,该过程将克隆整个储存库,仅将变化的模块复制到 Google 云存储桶中的模块目录,基于代码变化构建新的 Docker 映像,将新映像上传到 Google 容器注册表,并在 Vertex AI 中提交 TFX 管道。

第一次阅读可能很难,但如果我们一步一步地阅读,我们就会明白发生了什么:

  1. 我们克隆整个存储库
  2. 我们将这个存储库中的一些文件复制到指定的桶中
  3. 我们编译管道来检查是否一切正常
  4. 我们用 tfx 管道创建创建管道
  5. 我们用 tfx run create 创建顶点 AI 中的游程

该文件中有三个要点:

  • tfx pipeline create 中的 — build-image 标志用于创建我们环境的新 Docker 映像
  • 云构建使用的 Docker 镜像是gcr.io/$_PROJECT/cb-tfx:latest,也就是我们在步骤 3.3 中创建的镜像
  • 我们将 tfx 运行创建引擎设置为顶点,因为我们正在向顶点 AI 提交管道。

4.2 局部-管道-部署. yaml

如果目录中包含模型代码、预处理器代码、训练数据、超参数或标签编码器的变更

  1. 该流程将启动一个云构建过程,该过程将克隆整个存储库
  2. 仅将更改的模块复制到 Google 云存储桶中的模块目录
  3. 在 Vertex AI 中提交 TFX 管道,无需建立新的 Docker 映像

图 3。部分 CI/CD 管道。本图片转载自 Park,c .创作并分享的作品;并根据 Apache 2.0 许可证中描述的条款使用。来源:https://cloud . Google . com/blog/topics/developers-从业者/模型-培训-cicd-system-part-i

该文件实际上与full-pipeline-deployment . YAML相同,不同之处在于:

  • tfx 管道创建中的 —构建映像标志已被移除,因为我们不需要创建新的 Docker 映像
  • 因为没有改变管道设置,我们不需要编译它,移除 tfx 管道编译

5。监控

当将一个模型部署到产品中时,我们需要不断地监控它。我们的模型已经在某个数据集上进行了训练,因此为了保持性能,它将暴露的数据必须具有相同的分布。

由于模型不是在静态环境中运行的,由于训练数据和预测数据之间的偏差,它们的性能可能会随着时间而降低。偏差的一些例子可能是:

  • 新词汇(新语言)的引入
  • 测量单位的变化(米到公里)
  • 消费者行为因某些外部事件而改变(疫情)

由于模型没有受到这些变化的影响,因此性能可能会受到影响。所以我们需要监控:

  • 数据如何随时间变化
  • 当需要重新训练模型以捕捉新模式时
  • 正在摄取的数据是否与我的模型所接触到的数据相同

在这个例子中,我们将记录模型中所有的请求/响应日志。我们的模型将在 AI *台上可用,记录将以 raw 格式(JSON)保存在一个big query中。

下图显示了我们将要创建的架构:

图 4。监控架构。这张图片是从谷歌创作和分享的作品中复制的,并根据知识共享 4.0 归属许可中描述的条款使用。来源:https://cloud . Google . com/architecture/ml-建模-监控-分析-ai-*台-预测-大查询

5.1 为 BigQuery 启用请求-响应日志记录

默认情况下,AI *台预测服务不提供关于请求的日志信息,因为日志会产生成本。模型可以接收非常多的请求,导致相当大的成本,所以我们需要显式地启用注册。代码在activate _ logging _ requests . ipynb 文件中。

在启用日志之前,我们需要创建一个 BigQuery 表来保存日志。

每个表都需要在一个 BigQuery 数据集中,所以我们定义数据集和表名并创建数据集。

然后,我们定义表所期望的数据模式。

我们创建表格。注意,我们没有为表格设置任何到期时间,如果您想了解更多信息,请前往 文档

现在我们可以为我们的模型启用日志记录了。重要的一点是我们需要定义我们想要激活记录的模型的版本。对于模型的每个版本,我们需要显式地启用日志记录。

我们在模型中搜索最新版本。

最后,我们启用日志记录。 samplingPercentage 参数定义了我们想要记录的请求的百分比,在我们的例子中,我们设置 1.0,也就是说,我们记录 100%的记录。

在我们的表中会有我们模型的所有版本的原始记录。

5.2 可视化我们的数据

正如我们在下面看到的,我们的数据是以 JSON 格式记录的。

图五。 Raw 格式数据(JSON)(图片由作者提供)。

为了改进我们的视图,我们可以为我们的数据创建一个特定的视图。让我们来理解 BigQuery 表模式。

在下表中,我们分别显示了存储在 BigQuery 表的 raw_dataraw_prediction 列中的数据示例。

现在,在文件 create_view.py 中我们定义了视图的创建。首先,我们创建一些辅助函数。

之后,我们定义将创建视图的函数,接收所有参数。让我们把它分成四个部分:

1.我们定义特征和目标变量。

2.我们定义两个变量 json_features_extractionJSON _ prediction _ extraction。这些变量包含可以在 SQL 语句中输入的格式的属性和预测值。

3.我们创建了 sql_script 变量,它包含了 CREATE 或 REPLACE VIEW 语句。该语句包含几个标记,这些标记在字符串中使用@作为前缀进行标记。

4.最后,我们用前面定义的变量替换 SQL 语句中的标记,并创建视图。

现在我们可以访问 BigQuery 控制台并访问我们的数据。

输出和这个差不多。

图 6。个性化 BigQuery 视图(图片由作者提供)。

我们也可以使用 Google Looker 来创建交互式仪表盘和我们数据的可视化。

要清理该项目中使用的所有 Google Cloud 资源,您可以删除您在教程中使用的 Google Cloud 项目

6。结论

在云环境中开发管道可以让我们的系统变得可伸缩,并且不容易出错。部署模型只是整个过程的一部分,为了保持我们预测的性能,我们需要不断地监控推论并分析哪些数据被发送到我们的模型。

CI/CD 管道为我们的系统提供了响应能力,允许更快的测试周期、模型部署中更高的可靠性、改进的代码质量和更短的反馈循环。

7。参考文献

[1] MLOps:机器学习中的连续交付和自动化管道(2020 年 1 月 07 日),谷歌。

[2]帕克,c;保罗,S. 模型训练作为一个 CI/CD 系统:第一部分(2021 年 10 月 6 日),谷歌。

[3] ML 模型监控:利用 AI *台预测记录服务请求(2021 年 3 月 12 日),Google。

4 分析 AI *台预测日志在 big query(2021 年 3 月 12 日),Google。

5迪凡特,A. L. 我如何部署我的第一个机器学习模型(2021 年 12 月 15 日),中。

[6] 云 AI *台管道上的 TFX(2021 年 11 月 05 日),TensorFlow。

【简单 TFX】管道为顶点管道(2021 年 12 月 08 日),TensorFlow。

最小生成树算法的冲突

原文:https://towardsdatascience.com/clash-of-the-minimum-spanning-tree-algorithms-12568d21ca15

在 Go 中实现 Boruvka、Kruskal 和 Prim 算法

假设你正在建设一个连接一个小城镇的电网。你所在的城镇有许多建筑——住宅、办公室、商店、学校、图书馆和其他需要电力的建筑,因此它们都需要成为电网的一部分。像许多城镇一样,这些建筑也不是一个挨着一个,事实上它们四处蔓延,有些稀疏,有些密集。连接这些建筑的最佳方式是什么,以减少能源损失并最具成本效益?

布尔季哈穆德的街道(图片来自 Pixabay

许多配电公司使用 最小生成树 来模拟他们的电网。生成树是一个加权的、连通的和无向图的子集,它包括该图的所有节点。图中通常有不止一棵生成树。

将生成树的所有边的权重相加,就得到生成树的权重或成本。最小生成树 (MST)是成本最低的生成树。

基本思想是在可能的电力线连接图中,将建筑物建模为节点,将电力线建模为边。边缘权重可以是铺设线路的成本或电力传输损耗,也可以是我们想要表示用电力线连接两栋建筑的成本的任何值。它甚至可以是通过包含多个参数的公式计算出的值。然而,最终我们把它们归结为一个数字。

使用最小生成树算法,我们从该图中创建一个 MST。这将为我们提供最具成本效益的电网来为城市服务。

在这篇文章中,我将讨论 3 种经典的 MST 算法——Boruvka、Kruskal 和 Prim 算法。我还将使用 Go 实现它们,并对它们进行基准测试。然而,你应该明白,虽然它们在今天作为软件算法被实现和使用,但是它们是在计算机还没有发明或者几乎没有的时候被创造出来的。

CSIRAC 照片由维多利亚博物馆 Unsplash 上拍摄

第一个真正在计算机上运行的计算机程序是在 1948 年,而第一台商用计算机仅在 1951 年售出。这里的算法根本不是软件算法,它们基本上是数学算法,它们的创造者/发现者都是数学家。

最小生成树算法

最小生成树是一个很好解决的问题。最早提到最小生成树算法的是捷克数学家 Otakar Boruvka,他在 1926 年发表了一篇论文,题目是“T2 对电力线网络经济建设问题解决方案的贡献”。

捷克共和国南摩拉维亚的米库洛夫镇

这篇论文是西南摩拉维亚(现在捷克共和国的一部分)电气化问题相关工作的一部分。如果这还不清楚的话,这是大约 100 年前,那时计算机还不存在。

博鲁夫卡算法

博鲁夫卡的算法优雅而简单。给定一个带节点和边的加权无向图 G:

  1. 从 G 中所有节点的图 M 开始,但是没有边
  2. 找出每个节点连接到另一个节点的最小权重边,并在两个节点之间创建一条边。这导致了节点的集群,我们称之为子图
  3. 找出连接到另一个子图的每个子图的最小权重边,并在它们之间添加一条边。这将导致两个子图合并成一个子图。
    (记住,子图的边本质上是连接一个子图中的节点和另一个子图中的另一个节点的边。将一个子图中的节点连接到同一子图中的另一个节点的边不是子图的边)。
  4. 重复步骤 3,直到只剩下 1 个子图

在算法的最后,M 将是最小生成树。

克鲁斯卡尔算法

约瑟夫·克鲁斯卡尔是美国数学家和计算机科学家。他来自一个拥有惊人的数学家和科学家的家庭——他的两个兄弟都是著名的数学家,其中一个是统计学家(威廉·克鲁斯卡尔),他是克鲁斯卡尔-沃利斯单向方差分析的共同制定者,另一个(马丁·克鲁斯卡尔)是物理学家和天文学家,他发明了孤立子理论以及其他一些东西。马丁的儿子,约瑟夫的侄子是克莱德·克鲁斯卡尔,他是美国计算机科学家,是并行和分布式计算中读-修改-写概念的发明者之一。

约瑟夫·克鲁斯卡尔本人相当多产。他最有影响力的工作是他对多维尺度(MDS) 公式的贡献,它允许我们将一个数据集转换为另一个更低维度的数据集。这有助于我们将高维数据集可视化为 2D 数据集。其他以他名字命名的著作还有克鲁斯卡尔树定理克鲁斯卡尔-考托瑙定理克鲁斯卡尔秩(或 k 秩)。

但是让我们回到克鲁斯卡尔的算法。1956 年,当他 28 岁的时候(你 28 岁的时候在做什么?).

算法很简单。给定一个带节点和边的加权无向图 G:

  1. 从 G 中所有节点的图 M 开始,但是没有边
  2. 在列表 L 中,根据权重对所有边进行排序
  3. 从 L 中取最小重量的边,加到 m 上。
  4. 检查这是否会创建一个循环图。如果有,去掉边缘,否则不要管它
  5. 重复步骤(3)和(4),直到 L 为空

普里姆算法

Prim 的算法有一个有趣的起源故事。该算法最早是由著名的捷克数学家(是的,另一个) Vojtech Jarnik 在 1930 年开发的,他也阅读了 Boruvka 的论文,并在同一份期刊上用他的算法写了一篇回应。不幸的是,Jarnik 的回应不如 Boruvka 的论文那样知名或广为人知(Kruskal 和 Prim 后来都引用了 Boruvka 的论文,但没有 Jarnik 的)。

1957 年,美国数学家和计算机科学家 Robert Prim 独立提出了类似的算法。巧合的是,普里姆和克鲁斯卡尔都是贝尔实验室的同事,他们都为基本相同的问题提出了不同的算法。

霍姆德尔的老贝尔实验室大楼

甚至更晚些时候,Edsger Dijkstra(Dijkstra 的算法名声,我在另一篇文章中详细描述过)在 1959 年再次独立提出了它。

因此,这种算法现在有很多名字,包括普里姆算法、贾尼克算法、普里姆-贾尼克算法、普里姆-迪克斯特拉算法甚至 DJP 算法。在这篇文章中,我将使用 Prim 算法这个名字。

尽管系谱复杂,算法本身相当简单,这也是 Dijkstra 想出它的原因。这是因为这些步骤与 Dijkstra 算法非常相似。

给定一个带节点和边的加权无向图 G,步骤如下:

  1. 从一个空图 M 开始,图 G 中的一个任意节点作为当前节点
  2. 找到图 G 中当前节点的所有边,并将其添加到最小堆中
  3. 弹出最小堆以获得最小权重的边。将边添加到图 m。(在后面的迭代中,该边可能不是连接到当前节点的边)
  4. 使边节点成为当前节点,并将其添加到图 M 中
  5. 重复步骤(2)到(4),直到图 G 中的所有节点也在图 M 中

你可以看到为什么 Dijkstra 也想出了同样的算法。Dijkstra 和 Prim 的算法都是从单个节点开始,使用最小堆添加最小权重的边。不同之处在于,Dijkstra 的算法寻找从一个节点到另一个节点的最短路径,而 Prim 的算法寻找以最小的边总权重连接所有节点。

在围棋中实现算法

既然我们已经研究了这 3 种算法,让我们依次看看它们,看看它们是如何实现的。在所有 3 个算法中,我们将重复使用我之前关于 Dijkstra 算法的文章中的相同图形结构。

这还不够,但我们以后会添加更多的功能。我们也将使用这个简单的图作为最小生成树的基础。

亚历山大·德里切尔,CC BY-SA 3.0<https://creativecommons.org/licenses/by-sa/3.0>,转自维基共享

一天结束时,最小生成树看起来像这样(突出显示的图表)。

在我们开始任何算法之前,让我们看看这个图是如何创建的。

这就是我们将如何可视化图表。

当我们运行这个程序时,这就是将要显示的内容。

GRAPH
-----
A -> [B(7) D(4)]
B -> [A(7) C(11) D(9) E(10)]
C -> [B(11) E(5)]
D -> [A(4) B(9) E(15) F(6)]
E -> [B(10) C(5) D(15) F(12) G(8)]
F -> [D(6) E(12) G(13)]
G -> [E(8) F(13)]

既然已经解决了,我们就从博鲁夫卡算法开始。

围棋中的博鲁夫卡算法

让我们再来看看算法:

  1. 从 G 中所有节点的图 M 开始,但是没有边
  2. 找出每个节点连接到另一个节点的最小权重边,并在两个节点之间创建一条边。这导致了节点的集群,我们称之为子图
  3. 找出连接到另一个子图的每个子图的最小权重边,并在它们之间添加一条边。这将导致两个子图合并成一个子图。
    (记住子图的边本质上是连接一个子图中的节点和另一个子图中的另一个节点的边。将一个子图中的节点连接到同一子图中的另一个节点的边不是子图的边)。
  4. 重复步骤 3,直到只剩下 1 个子图

这是代码。

首先,我们创建mst图,并从graph复制节点。然后我们创建一部分Subgraph结构。Subgraph结构有一片Node结构和一片NodePair结构。记住,子图是节点和连接这些节点的边的集群。子图本身就是一棵最小生成树。在这个实现中,我们使用这个结构作为工具来帮助我们构建最终的 MST。我们没有使用子图作为实际的 MST 本身。

我们知道什么是Node结构,一个NodePair结构仅仅是一个有NodeEdge的结构。这代表一对节点,有一条边连接两个节点(记住Edge本身有一个节点和一个权重)。

我们为什么要这样做?在一个图中,我们用一个 map 来表示边,这个 map 将节点名作为键,将一部分Edge structs 作为值,但是我们不能在图之外表示它,所以我们需要使用其他的东西。

我们初始化子图切片,MST 中的每个节点也表示为一个子图。每个子图都有一些连接它和其他子图的边,这由一个NodePair表示。

一旦设置好了,我们就取第一个子图,并对它的所有节点对进行排序,以得到最小权重边,该边从这个子图的一个节点连接到另一个子图中的另一个节点。

我们将这条边添加到mst图中,并组合这两个子图。

combine函数只是将两个子图结合在一起。首先,我们需要找到这两个子图。接下来,我们将第二子图的节点和节点对添加到第一个子图中。哪个子图被添加到哪个子图并不重要,因为在一天结束的时候,所有子图都被添加到了一起,成为一个子图。

接下来是剔除从新的组合子图中的一个节点连接到同一子图中的另一个节点的边。我们使用in函数来解决这个问题。

一旦节点对被清理干净,我们就移除第二子图。

我们重复这样做,直到只剩下 1 个子图。在这个时间点上,最后剩下的子图是 MST,但是同时我们也已经建立了 MST in out mst图。

我们再次运行 main 函数,这次可视化 mst 图。

这是你应该看到的。

GRAPH
-----
A -> [B(7) D(4)]
B -> [A(7) C(11) D(9) E(10)]
C -> [B(11) E(5)]
D -> [A(4) B(9) E(15) F(6)]
E -> [B(10) C(5) D(15) F(12) G(8)]
F -> [D(6) E(12) G(13)]
G -> [E(8) F(13)]BORUVSKA MINIMUM SPANNING TREE
-----------------------------
A -> [D(4) B(7)]
B -> [A(7) E(10)]
C -> [E(5)]
D -> [A(4) F(6)]
E -> [B(10) C(5) G(8)]
F -> [D(6)]
G -> [E(8)]

试着把这个和上面的图匹配一下,看看是否符合!

接下来我们来看看克鲁斯卡尔的算法。

围棋中的克鲁斯卡尔算法

这就是算法。给定一个带节点和边的加权无向图 G:

  1. 从 G 中所有节点的图 M 开始,但是没有边
  2. 在列表 L 中,根据权重对所有边进行排序
  3. 从 L 中取最小重量的边,加到 m 上。
  4. 检查这是否会创建一个循环图。如果有,去掉边缘,否则不要管它
  5. 重复步骤(3)和(4),直到 L 为空

这是代码。

该算法使用了我们之前在 Boruvka 算法中看到的相同的NodePair。我们还需要创建一个 min 堆,并向Graph结构添加一些方法。让我们从 min 堆开始。

Go 标准库有一个打包的container/heap,带有一个名为Interface的接口,您可以用它来实现自己的堆。你需要做的是实现sort包中的Interface,这意味着你需要实现LenLessSwap,此外还有两个名为PushPop的方法。

这些方法实现起来非常简单。方法LenLessSwap非常简单,就像你如何实现对任何结构片的排序一样。Less方法确定它是最小堆还是最大堆。

Push方法只是简单地添加到堆中,而Pop从堆中移除。您可能会注意到PushPop方法分别接受和返回any。这是最*发布的 Go 泛型 1.8 中的新特性。这在Pop中不太重要,但是对于Push来说,变化只是在追加参数之前将参数声明为NodePair。这与 1.7 和之前的版本没有什么不同,因为之前它是一个空接口(interface{}),你也需要做类型断言。

使用这个堆实现的一个常见错误是忘记使用heap.Init初始化堆。您不能在创建堆后立即使用它,您需要在开始使用它之前运行它:

h := &Heap{}
heap.Init(h)

除了 min 堆,我们还需要增加一些方法:

  • HasEdge —检查图形是否有特定的边
  • RemoveEdge —从图形中删除给定的边
  • IsCyclic —检查图形是否循环

HasEdge方法很简单。给定节点名称,我们遍历所有的边,如果边节点的名称和边权重检查通过,我们返回 true。

移除边有点棘手,因为这是一个无向图,我们需要从节点的两端来看。然而,步骤是相同的,我们获得给定节点的所有边,并删除边节点名称匹配的边,只是我们需要在两边都这样做。

检查一个图是否是循环的需要我们对图进行深度优先遍历。有几种方法可以实现这一点,但在这种实现中,我们需要一个堆栈。堆栈的元素是一个指向Node的指针的 2 元素数组。

第一个元素是父节点,第二个是子节点。堆栈实现现在应该很熟悉了——推入和弹出堆栈只是简单地添加一个元素或从元素切片中移除一个元素。

我们从将一个元素推到父节点和子节点相同的堆栈开始遍历。在每次迭代中,我们弹出堆栈,如果子节点以前没有被访问过,我们将其标记为已访问,然后遍历子节点的边。如果边缘节点没有被访问过,我们将一个新元素推送到堆栈上,其中父节点是当前节点,子节点是边缘节点。如果边节点已经被访问过,这意味着图是循环的。

如果我们遍历整个图而没有遇到边节点,这意味着该图不是循环的。

现在让我们运行这个算法,看看我们得到了什么。

GRAPH
-----
A -> [B(7) D(4)]
B -> [A(7) C(11) D(9) E(10)]
C -> [B(11) E(5)]
D -> [A(4) B(9) E(15) F(6)]
E -> [B(10) C(5) D(15) F(12) G(8)]
F -> [D(6) E(12) G(13)]
G -> [E(8) F(13)]BORUVSKA MINIMUM SPANNING TREE
-----------------------------
A -> [D(4) B(7)]
B -> [A(7) E(10)]
C -> [E(5)]
D -> [A(4) F(6)]
E -> [B(10) C(5) G(8)]
F -> [D(6)]
G -> [E(8)]KRUSKAL MINIMUM SPANNING TREE
-----------------------------
A -> [D(4) B(7)]
B -> [A(7) E(10)]
C -> [E(5)]
D -> [A(4) F(6)]
E -> [C(5) G(8) B(10)]
F -> [D(6)]
G -> [E(8)]

你可能会觉得这个算法实现起来有点长,但是没有一个部分是困难的,事实上它们是非常常见的图形算法和数据结构。

这个三重奏的最后一个算法是 Prim 的算法。

围棋中的 Prim 算法

和以前一样,我们再看一下算法。给定一个带节点和边的加权无向图 G:

  1. 从一个空图 M 开始,图 G 中的一个任意节点作为当前节点
  2. 找到图 G 中当前节点的所有边,并将其添加到最小堆中
  3. 弹出最小堆以获得最小权重的边。将边添加到图 m。(在后面的迭代中,该边可能不是连接到当前节点的边)
  4. 使边节点成为当前节点,并将其添加到图 M 中
  5. 重复步骤(2)到(4),直到图 G 中的所有节点也在图 M 中

这是算法的代码。

您会注意到函数签名有点不同。不是只接受一个参数,图形,prim函数接受图形和节点名。这是因为 Prim 需要从图中的一个节点开始。它可以是任何节点,所以我可以随机选择一个,但我选择了一个起始节点。

该算法还使用了像 Kruskal 算法中的最小节点对堆。我们从一个mst图开始,然后添加开始节点。然后,我们获取开始节点的所有边,并使用它创建节点对,将每个节点对推入堆中。

然后,当堆不为空时,我们将弹出节点对,并检查 MST 是否已经有了边缘节点。如果没有,我们将边节点和边添加到mst中。

我们还将从边缘节点中取出所有的边缘,并在下一个周期将它们作为节点对推入堆中。这样,我们将遍历图中的所有节点和边..

检查图形是否有特定的节点需要我们为图形创建一个新的方法。这个方法很简单,不需要太多解释。我们简单地遍历图中的所有节点,如果找到节点,则返回 true。

Prim 的算法看起来是 3 个算法中最简单的,但那是因为我们已经实现了之前需要的函数和数据结构。

下面是运行 main 时的结果,包括 Prim 的算法。

GRAPH
-----
A -> [B(7) D(4)]
B -> [A(7) C(11) D(9) E(10)]
C -> [B(11) E(5)]
D -> [A(4) B(9) E(15) F(6)]
E -> [B(10) C(5) D(15) F(12) G(8)]
F -> [D(6) E(12) G(13)]
G -> [E(8) F(13)]BORUVSKA MINIMUM SPANNING TREE
-----------------------------
A -> [D(4) B(7)]
B -> [A(7) E(10)]
C -> [E(5)]
D -> [A(4) F(6)]
E -> [B(10) C(5) G(8)]
F -> [D(6)]
G -> [E(8)]KRUSKAL MINIMUM SPANNING TREE
-----------------------------
A -> [D(4) B(7)]
B -> [A(7) E(10)]
C -> [E(5)]
D -> [A(4) F(6)]
E -> [C(5) G(8) B(10)]
F -> [D(6)]
G -> [E(8)]PRIM MINIMUM SPANNING TREE
-------------------------
A -> [D(4) B(7)]
B -> [A(7) E(10)]
C -> [E(5)]
D -> [A(4) F(6)]
E -> [B(10) C(5) G(8)]
F -> [D(6)]
G -> [E(8)]

对 3 种算法进行基准测试

现在我们已经有了所有的 3 个,接下来要做的当然是对它们进行基准测试。

这是基准测试结果。

% go test -bench=. -benchmem
goos: darwin
goarch: amd64
pkg: github.com/sausheong/gocookbook/graph
cpu: VirtualApple @ 2.50GHz
BenchmarkBoruvka-10       112311      10238 ns/op     4488 B/op      183 allocs/op
BenchmarkKruskal-10        72430      16436 ns/op     4623 B/op      147 allocs/op
BenchmarkPrim-10          220465       5326 ns/op     2120 B/op       81 allocs/op
PASS
ok   github.com/sausheong/gocookbook/graph 4.109s

可以看到,Prim 的算法在 5.3ms 左右性能最好,比 Kruskal 的算法在 16.4ms 左右快 3 倍左右,Boruvka 的算法在 10.2ms,当然这些都是未优化的实现。

密码

这里的所有代码都可以在这个库中找到。

https://github.com/sausheong/mst

机器学习问题中的类不*衡:实践指南

原文:https://towardsdatascience.com/class-imbalance-in-machine-learning-problems-a-practical-guide-4fb81eee0041

来自应用数据科学战壕的五个教训

照片由 Unsplash 上的 Piret Ilver 拍摄

C 类不*衡是数据科学文献中最普遍的话题之一,其中一个类比另一个类丰富得多。仅在 Medium 上搜索“阶级失衡”就会发现许多标题如下的文章:

  • “处理用于分类的类不*衡数据集”
  • “如何用 Python 和 SMOTE 毫不费力地处理类不*衡”
  • “停止使用 SMOTE 处理阶级失衡”
  • “一种适用于类别不*衡数据的损失函数:焦点损失”
  • “阶层失衡:一个令人头疼的分类问题”

这的确令人头痛。你应该重新*衡吗?使用 SMOTE?不是用 SMOTE?用焦损怎么样?感觉就像你可以花几个月的时间做实验,但仍然会有你没有尝试过的事情。这是数据科学家的西西弗斯山。

我写这篇文章的目的是打破常规,解释五个我认为对理解类不*衡最重要的概念,这是为业务领域的数据科学家写的,他们的实验时间预算有限。我将以一套简单的“经验法则”作为结束,你可以在下一个不*衡的 ML 问题中用作实践指南。

让我们开始吧。

1.阶级失衡是常态,而不是例外

在典型的 ML 应用中,类别不*衡是正常的和预期的。例如:

  • 在信用卡欺诈检测中,大多数交易是合法的,只有一小部分是欺诈性的。
  • 在垃圾邮件检测中,情况正好相反:今天全球发送的大多数电子邮件都是垃圾邮件。
  • 在广告转化预测中,大多数用户会忽略该广告,只有一小部分会点击它。
  • 在用户流失预测中,大部分用户会留在*台上,只有一小部分会‘流失’(即离开*台)。

失衡只是我们生活现实的一部分。事实上,相反的情况很少见:在现实世界中,你很少会遇到分类问题,因为所有的类出现的频率都是一样的。换句话说,许多现实世界的 ML 问题都是“大海捞针”。

2.阶级不*衡本身不是问题

尽管有些文章中提到了这一点,但失衡本身实际上并不是问题所在。相反,当处理一个不*衡的 ML 问题时,有 3 件事可能出错:

选择了错误的指标。准确性是一个不好的度量,用来量化 ML 模型在不*衡问题上的性能:如果阳性率仅为 1%,那么一个将所有东西都标记为阴性的朴素分类器根据定义具有 99%的准确性。这在理论上听起来不错,但在实践中却很糟糕。这个问题当然可以通过选择更合适的指标来避免,例如固定召回率下的精确度、固定召回率下的精确度、PR-AUC 或 ROC-AUC。

训练/发球歪斜。这是指当用于训练模型的数据与推理时使用的数据不同时出现的问题,例如,因为训练数据已被手动重新*衡。训练/服务不对称的问题在于,训练数据上的性能并不能很好地代表服务时的性能,模型最终可能会比预期的更差。例如,如果在训练时,我们以 10 倍的因子对底片进行下采样,那么,在最坏的情况下,生产中的精度可能比预期的差 10 倍。

数据稀缺。在不*衡问题中,可能很难收集足够大量的标记阳性样本来训练具有合理性能的 ML 模型。例如,如果您只有 10 到 100 个正样本,模型可能很容易记住这些样本,导致过度拟合模型,泛化能力很差。问题越不*衡,可用于训练模型的阳性样本就越少。

请注意,在这三种情况下,不*衡本身都不是问题所在。例如,在 100 万张底片和 100 万张 10K 底片上训练一个模型是完全可以的,只要你避免使用精确度作为衡量标准。

3.向上采样少数民族类可能不是一个好主意

对少数类进行上采样,即将负样本的副本添加到训练数据中,通常被推荐为解决不*衡问题的首要尝试之一(例如这里的)。动机是实现更*衡的正/负比率,并因此在训练模型之前“修复”类不*衡。然而,上采样实际上会损害模型性能有两个原因。

首先,上采样引入了训练/服务偏斜,即训练数据不再代表服务数据。当您在训练数据上选择一个操作点时,该操作点在现实世界中可能是次优的。萨缪尔·马赞蒂在合成数据上证明了这种效应:在对训练数据中的少数类进行上采样后,看不见的数据的对数损失从 1.28 增加到 2.3,这是一个显著的下降。他的结论是:

你的数据集不*衡?什么都不做!

其次,上采样的另一个潜在问题是数据泄漏:如果你首先对数据进行上采样,然后将数据分成训练和验证部分,你的模型可以简单地记住训练数据中的积极因素,并在验证数据上实现人为的强劲表现,导致你认为模型比实际情况好得多。如果必须进行上采样,请始终在将数据拆分为训练和验证折叠后进行,而不是在此之前。

4.谨慎对多数类进行缩减采样

对多数类进行下采样指的是在训练数据中随机删除多数类的某一部分。例如,您可能决定只保留原始多数类的 10%、1%或更小的比例。在两种情况下,您会考虑这样做:

  • 当训练数据不适合内存时(而您的 ML 训练管道要求它在内存中),或者
  • 当模型训练花费不合理的长时间(几天到几周),导致太长的迭代周期,并且阻止你快速迭代。

在这些情况下,对多数类进行下采样是合理的。但是,记住这样做肯定会损害模型性能,这一点很重要。为什么?

随机下采样假设所有样本的信息量相同,因此我们可以简单地保留它们的随机子集。但是很明显,这种假设是错误的:一些样本比其他样本信息丰富得多,即那些更接*分类问题的决策边界的样本。当您随机缩减取样时,您可能会从数据中丢弃一些最具信息性的样本,从而导致模型性能比您没有丢弃任何样本时更差。

红色和绿色两类分类问题的卡通。靠*决策边界(黑线)的点比远离决策边界的点更能提供信息。(图片由作者提供)

因此,一个更好的想法可能是域过滤器,而不是随机下采样:一个简单的启发式规则,减少大部分多数类,同时保留几乎所有的少数类。例如,如果一个规则可以保留 99%的肯定信息,但只有 1%的否定信息,这将是一个很好的域过滤器。然后,在您的 ML 模型之前,在训练时间和推理时间应用该规则。以下是一些好的人口过滤器示例:

  • 在信用卡欺诈预测中,过滤新信用卡,即没有购买历史的信用卡。
  • 在垃圾邮件检测中,过滤来自以前从未见过的地址的电子邮件。
  • 在电子商务产品分类中,过滤包含特定关键字或关键字组合的产品。
  • 在广告转化预测中,针对用户群体的特定人口统计细分进行过滤。

想出一个好的领域过滤器需要领域知识,所以一个好的技巧是从项目涉众那里获得想法,他们最了解问题领域,然后在数据上验证这些想法。

5.超参数应该是最后一个需要试验的东西

某些 ML 模型 API 公开了超参数,这些超参数声称可以使模型更好地处理不*衡数据。例如,XGBoost 和 LightGBM 都有一个名为scale_pos_weight的参数,它在每次增强迭代计算梯度时增加正样本的权重。

然而,在实践中,调整这种超参数的影响可能没有那么大。例如, Jason Brownlee 显示,通过网格搜索调整scale_pos_weight参数,ROC-AUC 从 0.9572 提高到 0.9599,仅提高了 0.0027。

因此,我的建议是,超参数,尤其是那些特定于阶级不*衡的超参数,应该是最后才试验的。相反,如果模型性能是可接受的,那么尽快将模型部署到生产中,这样您就可以确认建模管道实际上正在工作,并且性能是预期的。如果模型性能不可接受,与其调整超参数,不如投资于数据质量:收集更多的训练数据,构建更好的特征,并确保标注正确。

结论:不*衡问题的“经验法则”

让我用一些简单的“经验法则”来总结不*衡分类问题:

  • 如果你至少有 1K-10K 阳性,训练集适合内存,并且模型可以在合理的时间(小时)内被训练,那么你是好的。这是一个很好的训练数据集。只要确保不使用准确性作为性能指标。
  • 如果您至少有 1K 10K 正样本,但训练集不适合内存,或者模型训练时间太长(几天到几周),请考虑缩减采样负样本,以便能够更快地迭代。更好的是,尝试设计一个好的群体过滤器,这样您可以在不牺牲模型性能的情况下获得迭代速度。
  • 如果你有<1K positives, your training set may be too small, especially so if the model has a large number of degrees of freedom (such as neural nets or tree ensembles). The model may overfit to such a small number of positives. Try to collect more training data, in particular positives.

Lastly, always remember the principle of 杠杆:作为一名数据科学家,总有无限多的“闪亮新事物”可以尝试,但你应该不断问自己,哪些东西有望为你的时间投资带来最大回报。有许多解决类不*衡的技术,我没有在这里介绍,但是在实践中,您可以做的最有效的事情是坚持使用最简单的方法,并尽快部署您的模型。

在你走之前…

喜欢这个内容吗?在我的 个人资料页面 找到更多我的文章。在 Medium 上关注/订阅,这样你就不会错过我以后写的新帖。 成为中等会员 这样可以无限阅读文章。请务必在LinkedIn和/或Twitter上关注我!

放大图形神经网络的经典策略

原文:https://towardsdatascience.com/classical-strategies-for-scaling-up-graph-neural-networks-69dd10cd21a

总结了使网络运行速度更快、占用内存更少的方法

纳斯蒂亚·杜尔希尔在 Unsplash 上的照片

我之前的帖子中,我谈到了图形神经网络(GNNs)如何成为一个热门的研究主题,因为在复杂网络(图形)的深度学习相关任务上取得了进展。

由于这是一个如此重要的主题,许多研究都集中在改进 GNNs 上。有两个主要目标:使它们更强大(就预测性能而言)并使它们运行得更快,消耗的内存更少。

在本帖中,我们将看到,今天,使用消息传递框架的传统 gnn 训练起来非常慢,并且需要大量的内存。我假装指导你一些已经到位的策略,尝试将 gnn 扩展到非常大的图。

分析消息传递框架的复杂性

消息传递框架包括聚集来自节点邻居的信息以及节点本身的信息,以生成节点表示。

我们已经在这篇文章中看到了一点这是如何工作的,以及它与这篇文章中的 WL 测试的关系。现在,让我们评估一下拥有一个大图的深度网络可能有多困难。

让我们记住,每增加一层 GNN 就意味着网络上多了一跳。

现在,如果我们看看 GraphSAGE [1]架构,它为每层上的每个节点选择固定数量的邻居,我们可以看到内存消耗是如何开始失控的。

设 F 为网络的 X 数据集上的要素数,L 为图层数。首先,你将有一个 O(LF)的内存消耗来保存网络的权重矩阵。这本身问题不大。

现在,让我们来看看内存消耗,它来自于在内存中保存节点的嵌入,这是为了在训练期间计算每个目标节点的嵌入而需要的。

假设 b 是批量大小,r 是使用 GraphSAGE 采样的节点数。让我们假设每层采样相同数量的节点。在这种情况下,您需要的内存量将是 O(bFr^L).关于这种计算的更多信息可以在[2]中找到

问题在于网络层数之间的指数关系。这说明了为什么现在存在的一些架构不能超越 3 或 4 层。

(这里需要注意的是,一些研究指出,深入研究 GNNs 可能不会带来性能提升,然而,这一讨论超出了本文的范围)。

现在,让我们回顾一下研究人员为了改善 GNNs 的可扩展性问题而采用的一些策略。

取样方法

采样方法试图通过对计算图进行采样来减少我们在任何给定时间点需要处理的节点数量。根据取样的方式和地点,我们可以有三种不同的分类。

节点采样

这是我们刚刚看到的 GraphSAGE 实现的方法。其思想是,在训练期间,我们可能不需要考虑整个节点邻域,而是可以选择一些节点。

正如我们所看到的,这仍然带来了复杂性问题。此外,随机采样可能不是一个好策略,因为没有选择这些节点的标准,这可能会对性能产生负面影响。

子图采样

子图采样背后的想法是,可能有一种方法可以将一个非常大的复杂网络分成更小的组件,然后在训练期间批量处理这些组件。这种方法有两个主要的例子:ClusterGCN [2]和 GraphSAINT [3]。

ClusterGCN 在训练之前对图应用聚类算法(或社区检测算法)。因此,通过生成几个聚类(子图),就有了一批随时可用的子图。

另一方面,GraphSAINT 有一些采样方法,考虑到采样的重要性和偏差,定义哪些节点和哪些边应该添加到采样的子图中。

这两种方法都成功地改善了模型训练的时间和记忆复杂性。然而,两者都包括生成子图的潜在缓慢的预处理步骤。此外,他们有一些假设,现在可以推广到每一个网络。例如,如果图上没有社区结构会发生什么?ClusterGCN 在这些情况下可能不太好用。

分层取样

层采样策略包括对每个层上要考虑的节点进行采样。请注意,这不同于节点采样。在节点采样中,每个节点都经过每一层,只是计算邻居的变化。在图层采样中,一个图层可能根本没有一个节点。

这种策略的主要例子是 FastGCN 4。该架构假设节点之间独立,然后应用一些蒙特卡罗*似来生成采样。

注意,这里节点之间的独立性是一个非常强的假设。我们正在处理一个图,根据定义,节点之间有某种关系(一条边)。

通过预计算简化架构

另一类扩展 GNNs 的策略是简化架构。这种策略背后的想法是打破经典 GCN 论文5中定义的消息传递框架,支持另一种类型的架构,通过预计算更好地扩展。我们将探索这种架构的两个例子。

简化图形卷积神经网络(SGC)

该架构[6]始于这样一个假设,即产生 GNNs 良好性能的是邻居的聚合,而不是 GNN 各层之间的非线性(ReLU 等)。

如果你去除非线性,你会看到整个 GNN 开始看起来像一个矩阵乘法序列。以三层网络为例:

其中 S 是归一化邻接矩阵,X 是特征向量,P 是网络层的权重矩阵。

可以清楚地看到,这些乘法运算的整个左侧不依赖于网络的权重。这意味着我们可以在训练网络之前将其相乘。

该体系结构的作者表明,这种方法与传统的 GNNs 相比是有竞争力的,表明人们可以消除网络的非线性,并预先计算大量以前在训练期间会发生的信息。

可扩展初始图神经网络(SIGN)

这个架构[7]是基于计算机视觉领域的初始网络。这个网络背后的基本思想是,人们可以预先计算多个没有可训练权重的聚合,然后在一个模型上连接它们以生成嵌入。

这与 SGC 的想法非常相似。在 SIGN 上,该架构的作者预计算网络的每种类型的跳(通过对邻接矩阵求幂),然后将其乘以特征矩阵,并且只有在那时他们才使用下游模型。

来自这两个架构的想法表明,预计算是一个非常突出的研究领域,可以为 GNNs 产生非常强大的结果

结论

这绝不是对图形神经网络的缩放方法的广泛回顾。然而,我希望这能够向您展示一些最著名的方法,以及它们如何在扩展 gnn 的大背景下相互配合。

[1]汉密尔顿,威廉&英,雷克斯&莱斯科维奇,朱尔。(2017).大型图上的归纳表示学习。

[2]蒋、刘维林、宣庆、李、杨、谢、曹瑞。(2019).聚类 GCN:一种训练深度和大型图卷积网络的有效算法。

[3]曾、韩青&周、洪宽&斯里瓦斯塔瓦、阿吉特什&坎南、拉杰戈帕尔&普拉桑纳、维克托。(2020).GraphSAINT:基于图抽样的归纳学习方法。

4陈、马杰、曹腾飞、萧。(2018).FastGCN:通过重要性抽样快速学习图形卷积网络。ICLR。

5基普夫,托马斯&韦林,马克斯。(2016).基于图卷积网络的半监督分类。

[6]吴,费里克斯&张,天一&苏扎,Jr &五十,克里斯托弗&余,陶&温伯格,基连。(2019).简化图形卷积网络。

[7]罗西,伊曼纽&弗拉斯卡,法布里齐奥&张伯伦,本&埃纳德,大卫&布朗斯坦,迈克尔&蒙蒂,费德里科。(2020).SIGN:可扩展初始图神经网络。

神经网络超参数的分类

原文:https://towardsdatascience.com/classification-of-neural-network-hyperparameters-c7991b6937c3

受网络结构、学习和优化以及正则化效应的影响

约翰-马克·史密斯在 Unsplash 上的照片

使用 DL 算法时的一个主要挑战是设置和控制超参数值。这在技术上叫做 超参数调优 或者 超参数优化

超参数控制 DL 算法的许多方面。

  • 他们可以决定运行算法的时间和计算成本。
  • 他们可以定义神经网络模型的结构
  • 它们会影响模型的预测准确性和泛化能力。

换句话说,超参数控制神经网络模型的行为和结构。因此,了解每个超参数在正确分类的神经网络中的作用非常重要(见图表)。

关于超参数的重要事实

在介绍和分类神经网络超参数之前,我想列出以下关于超参数的重要事实。要结合实例详细了解参数和超参数的区别,请阅读我的参数 Vs 超参数:有什么区别?条。**

  • 不要混淆 超参数参数 。两者都是存在于 ML 和 DL 算法中的变量。但是,它们之间有明显的区别。
  • 参数是变量,其值从数据中学习并在训练期间更新。
  • 在神经网络中,权重和偏差是参数。它们在反向传播期间被优化(更新)以最小化成本函数。
  • 一旦找到参数的最佳值,我们就停止训练过程。
  • 超参数是变量,其值由 ML 工程师或任何其他人在训练模型之前设置。这些值不是从数据中自动学习的。因此,我们需要手动调整它们来构建更好的模型。
  • 通过改变超参数的值,我们可以建立不同类型的模型。
  • 在 ML 和 DL 中,寻找超参数的最佳值是一项具有挑战性的任务。
  • 超参数的最佳值还取决于数据集的大小和性质以及我们想要解决的问题。

神经网络超参数分类标准

可以通过考虑以下标准对神经网络中的超参数进行分类。

基于上述标准,神经网络超参数可以分类如下。

(作者图表,用 draw.io 制作)

定义神经网络结构的超参数

根据该标准分类的超参数直接影响神经网络的结构。

隐藏层数

这也被称为网络的深度。深度学习中的术语【深度】是指一个神经网络的隐含层数(深度)。

在设计 MLPCNNAE 等神经网络时,隐含层数决定了网络的学习能力。为了学习数据中所有重要的非线性模式,神经网络中应该有足够数量的隐藏层。

当数据集的大小和复杂性增加时,神经网络需要更多的学习能力。因此,大型复杂数据集需要太多的隐藏层。

非常少量的隐藏层会生成较小的网络,可能会使训练数据不足。这种类型的网络不会学习训练数据中的复杂模式,也不会对看不见的数据进行很好的预测。

过多的隐藏层会生成一个较大的网络,可能会使训练数据过拟合。这种类型的网络试图记忆训练数据,而不是学习数据中的模式。因此,这种类型的网络不能很好地概括新的看不见的数据。

过拟合没有欠拟合有害,因为过拟合可以通过适当的调整方法减少或消除。

每层中的节点数(神经元/单元)

这也就是所谓的 宽度 的网络。

隐藏层中的节点通常称为 隐藏单元

隐藏单元的数量是影响网络学习能力的另一个因素。

太多的隐藏单元会创建非常大的网络,这可能会使训练数据过拟合,而非常少的隐藏单元会创建较小的网络,这可能会使训练数据过拟合。

MLP 输入层中的结点数量取决于输入数据中的维度(要素数量)。

MLP 输出图层中的结点数量取决于我们想要解决的问题类型。

  • ****二元分类:使用输出层的一个节点。
  • ****多标签分类:如果有 n 个相互包含的类,则在输出层使用 n 个节点。
  • ****多类分类:如果有 n 个互斥类, n 个节点用于输出层。
  • ****回归:使用输出层的一个节点。

新手总是问一个神经网络层应该包含多少个隐层或者多少个节点。

要回答这个问题,你可以利用上述事实以及以下两点。

  • 当隐藏层和隐藏单元的数量增加时,网络变得非常大,参数的数量显著增加。训练这样的大型网络,需要大量的计算资源。因此,大型神经网络在计算资源方面非常昂贵。
  • 我们可以通过添加或删除隐藏层和隐藏单元来试验不同的网络结构,然后通过绘制训练误差和测试(验证)误差相对于训练期间的时期数来查看模型的性能。

激活功能的类型

这是定义网络结构的最后一个超参数。

我们在神经网络层中使用激活函数。输入层不需要任何激活功能。我们必须在隐藏层中使用激活函数来给网络引入非线性。输出层中使用的激活类型由我们想要解决的问题类型决定。

  • ****回归:同一个节点的身份激活函数
  • ****二元分类:具有一个节点的 Sigmoid 激活函数
  • ****多类分类:每类一个节点的 Softmax 激活函数
  • ****多标签分类:每类一个节点的 Sigmoid 激活函数

要通过图形表示详细了解不同类型的激活函数,请阅读我的如何为神经网络选择正确的激活函数一文。**

要阅读激活功能的使用指南,请点击此处的。

要了解激活函数的好处,请阅读我的神经网络中激活函数的 3 个惊人好处文章。**

要了解如果在神经网络的隐藏层中不使用任何激活函数会发生什么,请阅读本文。

决定如何训练网络的超参数

根据该标准分类的超参数直接控制网络的训练过程。

优化器的类型

优化器也叫 优化算法 。优化器的任务是通过更新网络参数来最小化损失函数。

梯度下降是最流行的优化算法之一。它有三种变体。

  • 批量梯度下降
  • 随机梯度下降
  • 小批量梯度下降

所有这些变量的不同之处在于我们用来计算损失函数梯度的批量(稍后将详细介绍)。

为解决梯度下降算法的缺点而开发的其他类型的优化器有:

  • 动量梯度下降
  • 圣经》和《古兰经》传统中)亚当(人类第一人的名字
  • 阿达格拉德
  • 阿达德尔塔
  • 阿达马克斯
  • 那达慕
  • Ftrl
  • RMSProp (Keras 默认值)

学习率- α

这个超参数可以在任何优化算法中找到。

在优化过程中,优化器采取微小的步骤来降低误差曲线。学习率指的是步长的大小。它决定了优化器下降误差曲线的快慢。步长的方向由梯度(导数)决定。

这是神经网络训练中最重要的超参数之一。

较大的学习速率值可以用来更快地训练网络。过大的值会导致损失函数在最小值附*振荡,永远不下降。那样的话,模型就永远训练不出来了!

学习率的值太小会导致模型训练甚至几个月。在这种情况下,收敛发生得非常慢。该网络将需要许多纪元(稍后将详细介绍)才能以非常小的学习速率收敛。

我们应该避免使用太大和太小的学习率值。最好从一个小的学习率开始,例如 0.001(大多数优化器中的默认值),然后如果网络需要太多时间收敛,则系统地增加学习率。

损失函数的类型

应该有一种方法在训练期间测量神经网络的性能。损失函数用于计算预测值和实际值之间的损失分数(误差)。我们的目标是通过使用优化器来最小化损失函数。这就是我们在训练中取得的成绩。

训练中使用的损失函数的类型取决于我们所面临的问题的类型。

  • 均方误差(MSE) —这是用来衡量回归问题的表现。
  • *均绝对误差(MAE) —这是用来衡量回归问题的表现。
  • *均绝对百分比误差 —用于衡量回归问题的表现。
  • Huber Loss —用于衡量回归问题的表现。
  • 二元交叉熵(对数损失) —这用于衡量二元(两类)分类问题的性能。
  • 多类交叉熵/分类交叉熵 —用于衡量多类(两类以上)分类问题的性能。
  • 稀疏分类交叉熵 —在多类分类问题中,自动将标量值标签转换为 一个热点向量 。点击了解更多关于此的信息。

模型评估指标的类型

就像我们在训练期间使用损失函数来测量神经网络的性能一样,我们在测试期间使用评估度量来测量模型的性能。

对于分类任务,将使用“准确性”、“精确度”、“召回率”、“auc”指标。对于回归任务,将使用“均方误差”、“*均绝对误差”。

批量

批量 是在model.fit()方法中发现的另一个重要的超参数。

批次大小指的是批次中训练实例的数量—来源:关于批次大小、时期和神经网络中的训练步骤,您需要了解的全部信息

换句话说,它是每次渐变更新(迭代)使用的实例数。

批量大小的典型值为 16、32 (Keras 默认值)、64、128 和 256、512 和 1024。

较大的批量通常需要每个时段大量的计算资源,但是需要较少的时段来收敛。

较小的批量不需要每个时期大量的计算资源,但是需要许多时期来收敛。

纪元

时期 是在model.fit()方法中发现的另一个重要的超参数。

时期指的是模型看到整个数据集的次数——来源:您需要知道的关于批量大小、时期和神经网络中的训练步骤的所有信息

当,

  • 网络是用很小的学习率训练出来的。
  • 批量太小。

有时,网络会倾向于用大量的历元来过度拟合训练数据。也就是说,在收敛之后,验证误差在某个点开始增加,而训练误差进一步减小。当这种情况发生时,该模型在训练数据上表现良好,但在新的未知数据上表现不佳。此时,我们应该停止训练过程。这就叫 早停

****提前停止进程(图片由作者提供,用 matplotlib 和 draw.io 制作)

每个时期的训练步骤(迭代)

一个训练步骤(迭代)是一个梯度更新—来源:关于神经网络中的批量大小、时期和训练步骤,您需要知道的一切

我们不需要为此超参数设置一个值,因为算法会按如下方式自动计算它。

****Steps per epoch = (Size of the entire dataset / batch size) + 1****

我们加 1 来补偿任何小数部分。比如我们得到 18.75,我们忽略 0.75,18 加 1。总数是 19。

在 Keras model.fit()方法中,这个超参数由 steps_per_epoch 参数指定。它的默认值是None,这意味着算法自动使用使用上述等式计算的值。

无论如何,如果我们为这个参数指定一个值,将会覆盖默认值。

注:**要通过例子了解更多关于批量大小、时期和训练步骤之间的联系,请阅读我的所有你需要知道的关于神经网络中批量大小、时期和训练步骤的文章**

应用正则化效果的超参数

根据该标准分类的超参数直接控制神经网络中的过拟合。

我不打算像以前在我的其他文章中所做的那样,在这里详细讨论每个超参数。链接到以前发表的文章将被包括在内。

L1 和 L2 正则化中的λ-λ

λ是控制 L1 和 L2 正则化级别的正则化参数(因子)。λ的特殊值为:

  • lambda=0:不适用正规化
  • lambda=1:应用完全正则化
  • lambda=0.01 : Keras 默认

λ可以取 0 到 1 之间的任何值(包括 0 和 1)。

要详细了解这个超参数以及李和的正则化技术,请阅读我的文章【如何将和的正则化技术应用于 Keras 模型****

辍学调整中的辍学率

该超参数定义了丢失正则化中的丢失概率(要从网络中移除的节点比例)。两个特殊值是:

  • rate=0:不应用辍学调整
  • rate=1:从网络中删除所有节点(不实用)

辍学率可以取 0 到 1 之间的任何值。

要了解关于这个超参数以及辍学正则化的更多信息,请阅读我的辍学正则化如何减轻神经网络中的过度拟合文章。****

摘要

术语 超参数 在 ML 和 DL 中是一个非常重要的概念。对于 DL 中的给定任务,神经网络架构的类型也是超参数。例如,我们可以使用 MLP 或 CNN 架构来分类手写数字。这里,在 MLP 和 CNN 之间选择是一种设置超参数的类型!

对于给定的神经网络架构,存在上述超参数。注意,正则化超参数是可选的。

在 Keras 中,一些超参数可以通过函数中的相关参数作为层或字符串标识符添加。

例如,可以通过以下方式之一将 ReLU 激活功能添加到层中。

***# As a string identifier
model.add(Dense(100, activation='relu'))***
***# As a layer
from tensorflow.keras.layers import ReLU
model.add(Dense(100))
model.add(ReLU())***

这两个选项给出了相同的结果。当我们想要在激活功能之前添加任何其他层(例如批量归一化层)时,第二个选项是理想的选择。

另一个例子是,优化器可以被定义为字符串标识符或函数本身。

***# As a string identifier
model.compile(optimizer="rmsprop",...)***

当我们将 RMSProp 优化器定义为字符串标识符时,默认的学习率 0.001 将应用于该算法。没有办法改变。但是,我们经常需要在训练过程中改变学习率的值来获得最佳结果。为此,我们可以使用以下选项。

***# As a function itself
from tensorflow.keras.optimizers import RMSprop
model.compile(optimizer=RMSprop(learning_rate=0.005),...)***

这里,优化器被定义为 RMSprop()函数。期望的学习速率可以被定义为该函数中的一个自变量。

这些都是新手经常混淆的东西。希望你今天拿到了这些分!

今天的帖子到此结束。

如果你有任何问题或反馈,请告诉我。

阅读下一篇(推荐)

  • 参数 Vs 超参数:有什么区别?

***https://rukshanpramoditha.medium.com/parameters-vs-hyperparameters-what-is-the-difference-5f40e16e2e82

  • 所有你需要知道的关于批量大小、时期和神经网络中的训练步骤

https://rukshanpramoditha.medium.com/all-you-need-to-know-about-batch-size-epochs-and-training-steps-in-a-neural-network-f592e12cdb0a

  • 如何为神经网络选择正确的激活函数

**

(作者截图)**

****

(作者截图)****

支持我当作家

我希望你喜欢阅读这篇文章。如果你愿意支持我成为一名作家,请考虑 注册成为会员 以获得无限制的媒体访问权限。它只需要每月 5 美元,我会收到你的会员费的一部分。

****https://rukshanpramoditha.medium.com/membership

非常感谢你一直以来的支持!下一篇文章再见。祝大家学习愉快!

鲁克山普拉莫迪塔
2022–09–16*******

LeNet-5 CNN 在交通标志分类中的应用

原文:https://towardsdatascience.com/classification-of-traffic-signs-with-lenet-5-cnn-cb861289bd62

使用 Keras 库构建和利用一个简单的 CNN

丽贝卡·扎尔从派克斯拍摄的照片

这个项目的目的是训练和测试一个分类任务的 LeNet-5 卷积神经网络的实现。该模型将用于一个应用程序中,用户可以上传一张交通标志的照片,并获得其类别的预测。

1。数据集

该数据集取自德国交通标志识别基准(gt SRB)【1】的 ,并在 2011 年【2】国际神经网络联合会议 的单幅图像分类挑战赛上首次亮相。它是根据白天在德国不同道路上行驶时录制的大约 10 个小时的视频制作的。该数据由大约 40,000 幅真实的彩色交通标志照片组成。这些图像有一个。ppm* 扩展名,它们的大小从 15x15 到 250x250 像素不等。笔记本的话,我更喜欢用 Google Colab。我将数据集保存在我的 Google Drive 上,只需使用 drive.mount 命令即可访问它:*

***from** google.colab **import** drive
drive**.**mount('/content/gdrive', force_remount**=True**)*

2.图书馆

对于我们的项目,我们需要以下库:一些标准库,如 NumPy、OS 和 Matplotlib cv2 ,为解决计算机视觉任务而开发的强大库;sk learn . model _ selection . train _ test _ split用于将数据集拆分成训练和测试子集;用于构建模型的TF . keras . modelsTF . keras . layers中的一些组件。

***import** numpy **as** np
**import** random
**import** os
**import** cv2 **as** cv
**import** matplotlib.pyplot **as** plt
**from** sklearn.model_selection **import** train_test_split
**from** keras.models **import** Sequential, load_model
**from** keras.layers **import** Conv2D, Dense, Flatten, Rescaling, AveragePooling2D, Dropout*

3.读取和预处理图像文件

我们从读取数据集中的图像开始。这些图像分布在代表 43 个班级的 43 个文件夹中。我们遍历文件夹和图像,打开它们,调整到 32x32 像素,从 RGB 转换为灰色,并保存为 np.arrays。

*images **=** []
labels **=** []
classes **=** 43

current_path **=** '/content/gdrive/My Drive/GTSRB/Images/'

**for** i **in** range(classes):
    path **=** os**.**path**.**join(current_path, str(str(i)**.**zfill(5)))
    img_folder **=** os**.**listdir(path)
    **for** j **in** img_folder:
        **try**:
            image **=** cv**.**imread(str(path**+**'/'**+**j))
            image **=** cv**.**resize(image, (32, 32))
            image **=** cv**.**cvtColor(image, cv**.**COLOR_BGR2GRAY)
            image **=** np**.**array(image)
            images**.**append(image)
            label **=** np**.**zeros(classes)
            label[i] **=** 1.0
            labels**.**append(label)
        **except**:
            **pass***

我们现在将数据标准化:我们将图像除以 255,得到 0.0 到 1.0 之间的像素值。最后,我们将总共 39.209 幅图像分配给 43 个类别。

*images **=** np**.**array(images)
images **=** images**/**255
labels **=** np**.**array(labels)Images shape: (39209, 32, 32)
Labels shape: (39209, 43)*

当处理图像时,查看一些样本总是值得的。让我们从数据集中随机抽取 25 幅图像,并显示它们的标签。

25 张预处理图片样本。图片来自作者的笔记本

在这组图片中,我们可以看到一些可能的错误分类问题。例如,在图像 7 上很难识别数字,图像 13 和 14 太暗。图 8(“交通信号”)可能被误归类为“一般注意事项”。

4.构建模型

在创建模型之前,我们必须将数据集分成训练和测试子集。对于测试子集,我们取出标准的 20%的数据集。另外,一定要确保我们的数据有 np float32 格式。

*X **=** images**.**astype(np**.**float32)
y **=** labels**.**astype(np**.**float32)

X_train, X_test, y_train, y_test **=** train_test_split(X, y,                 
test_size**=**0.2, random_state**=**123)X_train shape: (31367, 32, 32)
y_train shape: (31367, 43)
X_test shape: (7842, 32, 32)
y_test shape: (7842, 43)*

对于我们的分类任务,我们将使用 LeNet-5 卷积神经网络的实现。LeNet-5 是 Yann LeCun 等人[3]在 1998 年设计的,是最早的卷积神经网络之一。它的架构非常简单,但非常高效。

有三个基于 5×5 滤波器的卷积层,然后是具有 2×2 补丁的*均池。我们使用 ReLU 功能进行激活,因为它可以加快训练速度。然后我们添加系数为 0.2 的 Dropout 层来处理过拟合。这意味着 20%的输入将被随机取消,以防止层间的强依赖性。我们最终得到了扁*化和两个致密层。在最后一个密集层中,我们必须分配与类别数量相等的神经元数量,并使用 Softmax 激活函数来获得 0.0 到 1.0 之间的概率。该网络中的最终参数数量为 70,415。

 *Model: "sequential_6"
_________________________________________________________________
 Layer (type)                  Output Shape              Param #   
=================================================================
 rescaling_7 (Rescaling)       (None, 32, 32, 1)         0         
 conv2d_19 (Conv2D)            (None, 28, 28, 6)         156       
 average_pooling2d_12          (None, 14, 14, 6)         0         
      (AveragePooling2D)                                                    
 conv2d_20 (Conv2D)            (None, 10, 10, 16)        2416      
 average_pooling2d_13          (None, 5, 5, 16)          0         
      (AveragePooling2D)                                                    
 conv2d_21 (Conv2D)            (None, 1, 1, 120)         48120     
 dropout_6 (Dropout)           (None, 1, 1, 120)         0         
 flatten_6 (Flatten)           (None, 120)               0         
 dense_12 (Dense)              (None, 120)               14520     
 dense_13 (Dense)              (None, 43)                5203      
=================================================================
 Total params: 70,415
 Trainable params: 70,415
 Non-trainable params: 0*

现在有了型号* 编译的方法,我们配置模型。Adam 学习率优化算法是随机梯度下降的扩展,在训练速度方面是一个很好的选择。分类交叉熵损失函数适合这里,因为它为我们的分类问题提供了多类概率分布。对于模型的性能评估,我们将采用准确性指标。*

*model**.**compile(optimizer**=**'adam',
              loss**=**'categorical_crossentropy',
              metrics**=**['accuracy'])*

5.训练模型

现在是训练模型的时候了。我们将输入数据( X_train )和目标数据( y_train )传递给 model.fit 方法,定义了 50 个训练时期,还添加了验证数据 X_testy_test ,用于在每个时期结束时评估损失和其他模型指标。

*history **=** model**.**fit(X_train, y_train, epochs**=**50,
                    validation_data**=**(X_test, y_test))*

6.培训结果评估

我们如何知道我们的模型是好是坏?来看看学习曲线吧!训练* 精度验证精度曲线最终收敛,经过 50 个历元我们得到了 98.9%的精度,相当不错。验证损失曲线上下跳动一点。这意味着如果有更多的验证数据就好了。大约 25 个时期后验证损失超过列车损失,这意味着我们这里有一点过度拟合。但是曲线不会随着时间的推移而上升,并且验证列车损失之间的差异并不大,因此这是可以接受的。我们就此打住。*

学习曲线。作者图片

让我们来看看一些样本,找出错误的分类图片。我们用预测地面实况类来标记图像。如果预测等于地面真相,我们分配一个绿色的标签。否则,我们把它变成红色:

25 个随机样本的预测和增长的真实性。图片来自作者的笔记本

对于编号为 5955 的图片,我们可以看到“限速(50 公里/小时)”标志被误归类为“限速(80 公里/小时)”。显然,这里标牌上的文字很难辨认。

7.保存模型。在应用程序中使用模型

最后,我们使用模型* 将模型保存到 Google Drive 上的一个单独的文件夹中。 保存方法。该文件夹包含模型的图形定义和权重,并将用于交通标志识别应用程序中的进一步预测。*

很明显,客户或任何数据科学之外的人永远不会深入你的笔记本,他不会对图表、模型、准确性和所有这些机器学习的东西感兴趣。他需要一个应用程序,他可以上传图像,并得到他的结果。让我们开始吧。

对于我们的 Streamlit 应用程序,我们必须准备一个 GitHub 存储库:我们将 keras_model 文件夹、 streamlit_app.pyrequirements.txt 放在这里。在 Python 文件中,我们制作一个应用程序本身:定义它的外观、降价、按钮,我们加载模型并进行预测。

***def** sign_predict(image):
    model = load_model('./keras_model/')    
    image = np.array(image, dtype=np.float32)    
    image = image/255    
    image = np.reshape(image, (1, 32, 32))    
    x = image.astype(np.float32)    
    prediction = model.predict(x)    
    prediction_max = np.argmax(prediction)    
    prediction_label = labels_dict[prediction_max]    
    confidence = np.max(prediction)    
    return prediction_label, confidence*

我们让用户上传他的照片,并得到他的交通标志预测。在开始预测之前,我们要对用户的图像进行预处理:使其灰度化,32x32 像素,另存为 np 浮子 32 式。我认为这将是很好的显示上传的图像,以及其预处理的 32x32 灰色版本。此外,我们将显示代表模型中 43 个可用类的扩展列表。上传一张图片后,得到的是预测和置信度。 这里你可以自己试试 app

在 Streamlit 上的应用。作者图片

参考

[1]德国交通标志识别基准(GTSRB):https://benchmark.ini.rub.de/gtsrb_dataset.html

[2] J. Stallkamp,M. Schlipsing,J. Salmen,C. Igel,Man vs. computer:交通标志识别的基准机器学习算法:https://www . science direct . com/science/article/pii/s 0893608012000457

[3] Lecun,y;博图湖;纽约州本吉奥;哈夫纳,P. (1998 年)。基于梯度的学习应用于文档识别:http://vision . Stanford . edu/cs 598 _ spring 07/papers/le Cun 98 . pdf

4 Suhyun Kim,卷积神经网络(CNN)初学者指南:https://towardsdatascience . com/A-初学者-卷积神经网络指南-cnns-14649dbddce8

*5 Keras,Python 深度学习 API:【https://keras.io/ *

*[6] OpenCV,一个计算机视觉库:【https://opencv.org/ *

使用 LightGBM 对音乐流派进行分类

原文:https://towardsdatascience.com/classifying-music-genres-with-lightgbm-17662035cf4e

Optuna 超参数优化和降维

图片由马体·米罗什尼琴科派克斯拍摄

他的文章概述了使用 tuned LightGBM 模型根据歌曲的音频和歌词特征将歌曲分类的过程。它从一个 Kaggle 音乐数据集中提取公开的音乐数据(通过 Spotify API 和一个授权开发者账户提取)。LightGBM 是一个基于决策树算法的梯度推进框架,被认为是当今可用的最佳开箱即用模型之一。它带有大量的可调参数,我们将尝试使用超参数优化框架 Optuna 对其进行优化。

文章大纲如下:

  • 数据概述和预处理
  • 探索性分析
  • 歌词特征的探索性降维
  • 建模和超参数优化
  • 结果
  • 带回家的点数

数据概述和预处理

Kaggle 数据集包含超过 18,000 首歌曲的信息,以及关于其音频特征的信息(例如,它有多活跃或充满语音,或者它以什么速度或基调播放等。),以及歌曲的歌词。在地图上可以看到更详细的可用列概览。

在最初的形式下,数据有点难以处理,所以在使用之前需要一些过滤和整理。可以在 GitHub repo 中查看所采取的预处理步骤的完整概要。prep_kaggle_data.py脚本可用于处理来自 Kaggle 的数据,或者,处理后的数据也可作为 repo 中的压缩 CSV 文件。

预处理步骤是这个项目中最不有趣的部分,所以我们将在总结中忽略它们:

  1. 过滤数据,只包括英语歌曲,并删除“拉丁”风格的歌曲(因为这些歌曲几乎全部是西班牙语,所以会造成严重的类别不*衡)。
  2. 整理歌词,使它们小写,删除标点符号和停用词。统计剩余单词在歌曲歌词中出现的次数,然后过滤掉所有歌曲中出现频率最低的单词(杂乱数据/噪音)。
  3. 转换剩余的单词计数,使每首歌曲都包含计算某个单词在该歌曲的歌词中出现的次数的列。

探索性分析

本节和下一节的所有代码都可以在 repo 中的 eda.ipynb 笔记本中找到。

让我们检查一下标签(流派)的等级*衡

作者图片

似乎有一点的类不*衡,所以交叉验证和训练测试拆分大概应该是 分层

音频和歌词特征的 稀疏性 呢?下面的数据框架显示了它们各自的百分比稀疏度。相比之下, 的抒情特色却极其稀疏 。这是有意义的,因为歌词中的大多数非停用词不会在不同的歌曲中一致出现。

作者图片

歌词特征的稀疏性可以很好地表明它们非常适合于降维。

歌词特征的探索性降维

如果许多机器学习算法处理具有大量特征(维度)的数据,它们的性能会更差。如果这些特征中的许多非常稀疏,情况尤其如此。这就是降维有用的地方。

其思想是将高维数据投影到低维子空间中,同时尽可能多地保留数据中存在的差异。

我们将首先使用两种方法(主成分分析和 t-SNE)来探索对我们的歌词数据使用降维是否合适,以及获得降维的良好范围的早期指示。建模过程中使用的实际降维将略有不同(用户选择截断 SVD、PCA 和 Keras autoencoder),我们将在后面看到。

主成分分析

一种常用的降维方法是主成分分析或 PCA(关于它的很好的入门可以在这里找到)。我们可以用它来看看,相对于我们减少的维数,我们可以解释歌词列中的多少变化。例如,我们可以看到,通过 将歌词缩减到大约 400 维 (在这种情况下是主分量)我们仍然 保留了歌词 中 60%的方差。大约 800 个维度,我们可以覆盖 80%的差异。减少维度的另一个好处是,它消除了歌词特征的稀疏性,使它们更容易建模。

作者图片

上面的图和 PCA 的代码可以在下面找到。

作者要点

SNE 视觉化

我们还可以更进一步,想象我们的数据在一系列维度缩减中是如何分离的。t-SNE 算法可以用于进一步将我们的歌词主成分减少到二维,即人脑可以感知的图形。想了解更多关于 t-SNE 的信息,这里有一篇好文章。本质上,该图向我们展示了当我们使用例如所有 1806 个特征,或者将其缩减为 1000、500、100 个主分量等时,如果将其投影到 2-D 空间,我们的歌词数据将会是什么样子。

作者图片

理想情况下,我们希望看到在某个特定的缩减维数(例如截止值= 1000)时,类型变得更加可分。

然而,根据 t-SNE 图的结果,似乎任何特定数量的维度/主成分都不会产生更容易分离的数据。所有的体裁在抒情特征上似乎都相当混杂。因此,对所有类型进行准确分类可能很困难

t-SNE 图的代码如下:

作者要点

建模和超参数优化

我们可以使用下面的代码得到最终形式的数据。我们这样做,以便我们可以很容易地改变我们想要在歌词特征上使用的降维方法,以及要降维的维数。我们将在一系列输出维度上试验 PCA、截断 SVD 和 Keras 欠完整自动编码器。

为了简洁起见,省略了自动编码器的代码,但是可以在 repocustom_functions.py文件中的autoencode函数中找到。

作者要点

现在 我们已经按照我们想要的方式对数据进行了标准化、转换和简化 ,我们可以开始用它建模了。

构建模型

首先,让我们 定义一个评估指标 来评估我们的模型的性能并进行优化。由于我们数据中的流派/类别略有不*衡,宏 F1 分数可能是一个很好的选择,因为它*等地评估了类别的贡献。下面定义了它,以及一些我们将应用于所有 LightGBM 模型的固定参数。**

作者要点

接下来,我们必须 为 Optuna 定义一个目标函数 进行优化。这是一个返回性能指标的函数,在我们的例子中,这将是一个分层的 5 重交叉验证,超级法拉利(jk jk,但它肯定是一个拗口的),宏观 F1 分数。

LightGBM 带有大量可调参数,所以这段代码中有很多内容。如果你想了解更多关于他们的信息,那么文档很好,还有这篇这篇文章。

然而,关键的一点是,在param论证中,我们定义了一个可能的超参数的搜索空间(最初是一个相当广泛的搜索空间),Optuna 将用它来测试我们的模型。

作者要点

超参数优化

现在我们有了目标函数,我们可以使用 Optuna 来调整模型的超参数。

我们 创建一个“研究”,用不同的超参数值运行我们的目标函数 ,模型的每次运行被称为“试验”该研究记录了特定试验中使用的超参数值/组合,以及该试验中模型的表现(根据 5 倍分层 CV 宏 F1)。

我们还可以 为研究 分配一个修剪程序,以减少训练次数。如果很早就清楚当前选择的超参数将导致较差的性能,则HyperbandPruner将提前结束或“修剪”试验。

作者要点

然后,我们可以使用 Optuna 的可视化模块可视化不同超参数组合 的性能。例如,我们可以使用plot_param_importances(study)来查看哪些超参数对模型性能最重要/对优化影响最大。

按作者分类的图表

我们还可以使用plot_parallel_coordinate(study)来查看尝试了哪些超参数组合/范围,从而产生了较高的目标值(良好的性能)。

作者图片

然后,我们可以使用plot_optimization_history来查看最佳目标值/最强模型性能与运行的试验数量之间的关系。

按作者分类的图表

最后,我们可以这样选择

  • 使用研究确定的最佳超参数运行我们的最终模型。最佳超参数存储在study.best_params属性中。最终模型中的params参数需要更新为params = {**fixed_params, **study.best_params},如下面的代码所示。
  • 或者,运行更多轮次的超参数调整/研究,缩小搜索空间/超参数范围,以更接*之前确定的每一轮次的最佳超参数值。然后使用study.best_params运行你的最终模型。

作者要点

结果

好了,现在我们有了最终的模型,让我们来评估它!我们将考察 列车,交叉验证并测试 F1 成绩。 我们还可以将我们的结果与使用的降维方法、使用的降维数量以及我们进行研究的试验数量一起存储在一个数据框架中。

作者要点

通过将上述所有代码的结果保存到一个 CSV 中,我们可以比较一系列缩减方法、试验和缩减的维度,以查看哪一个给出了总体上最好的模型。在所试验的值中, 具有 400 个缩减维度和 1000 次试验 的 PCA 似乎已经产生了最佳模型- ,实现了 66.48% 的测试宏 F1 分数。

作者图片

可以使用更小尺寸的更多值和更大量的试验,但是这很快在计算上变得昂贵(运行数小时)。

带回家的点数

  1. 预处理音乐数据,将歌词整理成列,统计歌曲中非停用词的出现次数。
  2. 探索性数据分析:考虑类别*衡和特征稀疏性。其他 EDA 也可以在 [**eda.ipynb**](https://github.com/louismagowan/lgbm-music_classifier/blob/master/eda.ipynb) 笔记本中找到。
  3. 探索性地对高度稀疏的歌词特征进行降维:使用主成分分析,然后使用 t-SNE,在一系列降维选项/截止点上将音乐流派可视化(投影到 2-D 空间)。
  4. 将数据分成测试集和训练集,然后使用您选择的降维方法(截断 SVD、PCA 或 Keras 欠完整编码器)对其进行处理。
  5. 定义您的目标函数/构建 LightGBM 模型。使用 Optuna 研究为其尝试一系列超参数值(搜索空间)。
  6. *** * 可选:重复 5,但缩小超参数值的范围,使其更接*第一轮超参数调整研究确定的最佳值。*
  7. 使用 Optuna 研究发现的最佳超参数值运行您的最终模型。
  8. 在测试集上评估模型。

链接

参考

清理 Pandas 中格式混乱的日期列

原文:https://towardsdatascience.com/clean-a-messy-date-column-with-mixed-formats-in-pandas-1a88808edbf7

处理 Pandas Dataframe 中混乱的日期字符串列的提示和技巧

图片由 Pixabay 提供

如果您的datetime列是一个格式混乱的字符串,那么在 Pandas 中使用datetime有时会很棘手。您将需要做一些预处理,并将混合格式转换为标准的datetime类型,以便执行任何后续的数据分析。

让我们看看下面的例子。在这个数据框架中,我们有一个名为‘joining _ date’的列,它看起来是一个datetime列,但实际上是一个字符串列。此外,日期都是以不同的格式排列的。

例如,对于 ID a0001,日期字符串排列在 DD 中。首先写入日期的 MM.YYYY 格式。对于 ID a0005,日期字符串以 DD-MM-YY 格式显示,其中也首先写入日期。对于 ID a0002,日期字符串以 MM/DD/YYYY 格式排列,首先写入月份,这在美国很常见。

作者图片

那么,我们能做些什么来简单有效地清理这个混杂格式的日期字符串列呢?

让我们首先试试 Pandas 的to_datetime()方法,它可以很容易地将任何有效的日期字符串解析为datetime。如果我们在没有任何附加参数的情况下简单地执行以下操作,我们会得到如下所示的结果:

df['joining_date'] = pd.to_datetime(df['joining_date'])

作者图片

我们可以看到,使用pd.to_datetime()方法,我们能够将混合格式的日期字符串解析为标准格式的datetime(默认为 YYYY-MM-DD)。由于pd.to_datetime()默认解析月首(MM/DD、MM DD 或 MM-DD)格式的字符串,它将日首格式的日期字符串的日和月混淆了(例如,DD。MM.YYYY 表示带有红色叉号的日期)。

要解决这个问题,我们可以简单地在下面的代码中添加参数dayfirst=True:

df['joining_date'] = pd.to_datetime(df['joining_date'], dayfirst=True)

作者图片

好的。因此,这解决了之前的问题,但产生了新的问题。对于格式为 MM/DD/YYYY(第 7 行)的日期字符串,由于我们将参数dayfirst=设置为True,因此它将 2016 年 5 月 3 日转换为 2016–03–05,将 5 视为日,将 3 视为月。

为了解决这个新问题,我们可以应用np.where()条件,只为首先写入日期的日期字符串设置参数dayfirst=True

df['joining_date_clean'] = np.where(df['joining_date'].str.contains('/'), pd.to_datetime(df['joining_date']), pd.to_datetime(df['joining_date'], dayfirst=True))

作者图片

有用!现在我们有了一个更加简洁的日期列。请注意,通过使用dt.strftime()方法,您还可以指定默认格式之外的输出日期格式。例如,您可以通过指定dt.strftime(‘%m/%d/%Y’)来选择将输出日期显示为 MM/DD/YYYY。

df['joining_date_clean'] = np.where(df['joining_date'].str.contains('/'), pd.to_datetime(df['joining_date']).dt.strftime('%m/%d/%Y'), pd.to_datetime(df['joining_date'], dayfirst=True).dt.strftime('%m/%d/%Y'))

作者图片

这就对了。不需要做任何复杂的字符串操作,我们就可以将原始数据集中杂乱的日期字符串转换成标准的datetime列。我希望您学到了一些在 Pandas 中处理混乱的日期字符串列的技巧。感谢阅读,希望你喜欢这个简短的教程。

数据源:

本文中使用的样本数据集是作者出于演示目的创建的。

你可以通过这个推荐链接注册成为 Medium 会员(每月 5 美元)来获得我的作品和 Medium 的其他内容。通过这个链接注册,我将收到你的会员费的一部分,不需要你额外付费。谢谢大家!

清理熊猫数据框中的“数字”ID 列

原文:https://towardsdatascience.com/clean-a-numeric-id-column-in-pandas-dataframe-fbe03c11e330

熊猫初学者的一段方便的代码

图片由 Pixabay 提供

作为一名数据科学家,在您的数据科学之旅中,您一定至少遇到过一次这样的问题:您将数据导入到 Pandas dataframe 中,ID 列显示为一个数字(整数或浮点)变量。原始 ID 列中可能有前导零,但由于数据类型为数字,它们已被删除。

这是一个常见的问题,几乎总是需要在进行数据管理和分析任务之前进行处理。如果您计划以后将 ID 字段用作与其他表连接的键,这一点尤其重要。那么如何在 Python 中修复呢?

让我们看一个简单的例子。我们有一个样本数据框架,显示了 2020 年 10 月每个县的房屋销售价格中值。在此数据框架中,我们有一个 ID 字段— FIPS,它是美国每个县的唯一 5 位地理标识符

作者图片

FIPS 字段应该是一个五位数的代码,应该作为字符串导入;相反,它显示为没有意义的浮点类型。我们需要将它改为字符串类型,并添加回前导零。我们可以通过两种方式做到这一点:

方法 1:

使用这种方法,我们首先将 FIPS 字段从float类型更改为integer类型,然后将其更改为string。我们可以使用 Pandas 的DataFrame.astype()方法来改变数据类型。

df['FIPS'] = df['FIPS'].fillna(0).astype(int).astype(str)

作者图片

在 FIPS 字段更改为string类型后,我们可以添加前导零,使其成为一个五位数代码。我们可以用熊猫的方法来做。

df['FIPS'] = df['FIPS'].str.zfill(5)

作者图片

方法二:

清理 FIPS 字段的另一种方法是首先将其数据类型更改为string,然后使用 Python 中的regex(正则表达式)来搜索和替换字符串中的某些模式,以便删除小数位。以下代码使用regex从 FIPS 列的字符串中删除“. 0”部分。

df['FIPS'] = df['FIPS'].astype(str).replace('\.0', '', regex=True)

作者图片

然后,我们使用zfill()将前导零添加回 FIPS 字段,使其成为一个五位数代码。

df['FIPS'] = df['FIPS'].str.zfill(5)

作者图片

您还可以选择使用以下代码将所有“00nan”替换为“00000”或其他值,但这是可选的。

df['FIPS'] = df['FIPS'].replace('00nan', '00000')

我想指出的一点是,在方法 1 中,请确保在使用astype(int)更改数据类型之前,使用fillna(0)将 FIPS 列中所有缺失的值替换为 0。这是因为如果您的 ID 列(即 FIPS)有缺失值,那么astype(int)将无法工作。

例如,如果您使用下面的代码试图将 FIPS 从float直接更改为integer,您将得到如下所示的回溯错误:

df['FIPS'] = df['FIPS'].astype(int)

作者图片

总之,通过使用以下两种方法之一,可以很容易地清除带有前导零的“数字”ID 列。这是一个非常常见并且看起来很容易解决的问题,但是对于 python 初学者来说却很难解决。因此,我希望这篇简短的教程和代码对您有所帮助,尤其是对于那些刚刚开始数据科学和 python 之旅的人。感谢阅读!

方法一:

df['FIPS'] = df['FIPS'].fillna(0).astype(int).astype(str)
df['FIPS'] = df['FIPS'].str.zfill(5)

方法二:

df['FIPS'] = df['FIPS'].astype(str).replace('\.0', '', regex=True)
df['FIPS'] = df['FIPS'].str.zfill(5)

数据源:

Redfin 数据中心 : Redfin 月度房市数据——县级。这是由全国房地产经纪公司 Redfin 提供的一个开放数据集,你可以免费下载,并注明出处供你自己使用。

你可以通过这个推荐链接注册 Medium 会员(每月 5 美元)来获得我的作品和 Medium 的其他内容。通过这个链接注册,我将收到你的一部分会员费,不需要你额外付费。谢谢大家!

清除 AutoML 中的“脏”数据

原文:https://towardsdatascience.com/clean-automl-for-dirty-data-how-and-why-to-automate-preprocessing-of-tables-in-machine-learning-d79ac87780d3

如何以及为什么在机器学习中自动预处理表格

预览图像(按作者)

在本帖中,我们将讨论一个众所周知且被广泛描述的主题,即数据科学中的表格数据预处理。你可能会问,“我们为什么需要它?没什么新鲜的可说!”事实上,还有什么比机器学习模型的表格数据处理更琐碎的呢?但是我们会尝试收集尽可能多的信息到一个最终指南中,并通过自动机器学习(AutoML)的角度给出它。

免责声明:我们下面描述的所有方法都不是唯一的方法。我们在开源 AutoML 框架 FEDOT 的开发过程中使用过它们。这个项目在架构和开发设计方法上都有自己的特点。我们实现了一个相当大的预处理模块,与框架的主要部分正交。然而,它仍然抓住了图书馆核心的灵魂。言归正传!

引言(“我感觉不到我的特征。——他们不在那里!”)

首先,有必要澄清(以防万一)数据科学中“预处理表格数据”的含义。它是一组转换数据的操作,以便机器学习模型能够正确拟合。广义地说,预处理包括初始数据清理(删除不可读的单元格和列等)。)、消除缺口(插补)、分类特征编码、数据归一化和异常值去除。在这篇文章的下面,我们将只把前三个块称为“预处理”如果您想了解更多关于预处理表格数据的内容,请从数据预处理:概念开始,然后继续阅读数据预处理综合指南

因此,在我们的团队中,在我们的 AutoML 框架中实现高级预处理的需求(最终变得相当大)并没有立刻出现。起初,我们对最小的功能感到满意:填补空白、编码分类特征、标准化并传递给模型输入。然而,在过去的几个月里,我们的任务是在来自 OpenML、Kaggle 和其他来源(例如,来自资源“Departamento de science ia de computadores”)的大量数据集上运行我们的框架。直到那时,我们才意识到我们之前为框架创造了多么舒适的条件——我们只在干净的数据集上运行框架。因此,该算法无法在超过一半的数据集上启动 AutoML 过程。我们运行了 46 个表格数据集,其中大部分是针对分类任务的。这是我们旅程的起点。

图一。我们通常不得不玩右边的(http://memegenerator.net/)

关于数据集的一些信息:表的大小从很小(对于“输血服务中心”,每 5 列 748 行)到很大(对于“KDDCup99_full”,每 42 列 490 万行)。元素的总数(行数乘以列数)从几千到几亿不等。我们需要所有这些来测试算法在不同数据大小上的性能(图 2)。

图 2:我们为实验选择的表格数据的维度(图片由作者提供)

除了间隙和大量分类属性的问题之外,这些数据还有一个令人不快的特性,即多种数据类型(如 float 和 string)可以混合在一列中。我们将在下面写下这种邻域导致了什么样的麻烦,但现在,只是总结一下——数据集有一些不允许对数据运行机器学习模型的功能,因为它们没有经过认真的预处理。

实验中使用的数据集的链接:

Research("顺便问一下,船上有人知道如何启动 AutoML 吗?")

在我们开始修改预处理系统之前,我们决定看看其他团队的同事是如何解决类似问题的。诚然,我们要考虑数据中最关键的变化;如果有必要,任何大规模 AutoML 库都会隐式考虑将一维目标数组转换为向量列格式。

让我们从 H2O 框架开始,它遵循这个结构。预处理先于 AutoML 算法的启动。预处理包括经典的绅士工具包:自动类型检测、使用 OneHotEncoding 的分类编码、插补方法以及必要时的归一化。也有可能为某些模型执行可选的预处理—例如,决策树和随机森林模型等不需要标准化。一些有趣的事实:3.28.1.2 的 H2O 版本在 46 个数据集的 24 个上成功运行。

与 H2O 不同,流行的学术 TPOT 框架并不是一个完全“端到端”的解决方案。因此,该库将其大部分预处理留给了用户,而是专注于管道优化。然而,TPOT 结构有输入数据同化的方法。特别是,预处理器检查数据集是否存在缺口,并对其进行估算。有各种标准化和规范化。分类特征和浮点特征的分离是基于列中唯一值的数量来完成的。与 H2O 相反,没有为 TPOT 提供可选的预处理—预处理在模型搜索的进化算法开始之前充当单个块。尽管预处理不是这个框架的强项,但它是广为人知的,而且相当可靠。但是,建议在提交数据之前清理原始数据集。例如,框架不消化数组中存在的字符串数据类型。

LightAutoML 框架也有标准的预处理:填补空白、处理分类特征和不同类型。就框架而言,类型被称为“角色”,其中每个“角色”(数字或分类类型)都有自己的预处理方式。数字特征经历缩放过程。在启动模型之前有几个处理步骤:选择合适的特征和生成新的特征。第一块主要包括预处理方法,如缩放、编码和插补。预处理对模型来说是必须的,但也可能是多种多样的。

自动引导框架实现了两级预处理:独立于模型和特定于模型。这种划分避免了不必要的计算。它还允许保持方法的灵活性。除了标准化、间隙填充和类别编码等经典操作之外,该框架还有一个用于将日期时间索引转换为数字特征的模块。在我们的实验中,自转被证明是最可靠的。

根据我们的简要回顾,我们可以得出结论,最常见的预处理方案是“灵活的”版本。基于模型确定输入数据的变换集。

此处的简要概述并不涉及技术实现的细节。此外,目前的审查可能会失去相关性,因为框架正在被修改。如果不是开发人员,只是在做评审,也很容易遗漏一些细节。因此,如果您发现任何不匹配的地方,并建议对预处理方法的描述进行补充,我们将非常高兴。

实现(“像瑞士表一样可靠”)

故障排除是迭代的;我们一桌一桌地尝试管理图书馆。如果出了问题,我们会改进。决定在将数据输入管道之前,在单独的块中实现预处理。

让我们以下表为例来考虑预处理。假设我们解决了二进制分类的问题,并且表存储在 csv 文件中:

表 1。包含用于解决二元分类任务的要素的表示例(图片由作者提供)

此表中可以突出显示哪些功能:

  • 在特征一中,有无穷大的值;
  • 功能 2 中缺少太多值;
  • 特征 3 中有一个遗漏。还要注意,该特性只取两个可能的值:1 和 10;
  • 特征 4 中有三个类别,但是由于行中的缩进,类别数可以定义为 5;
  • 特征 5 是二元分类的,并且有遗漏;
  • 特性 6 包含数据类型 float 和 string
  • 目标变量包含间隙。同时以 raw 标签的形式表示,也就是字符串。

我们开始从左向右移动。首先,用 NaN 替换特征 1 中所有具有无穷大值的单元格。然后,我们移除特征二,因为它包含太多缺失值(我们在框架中的阈值被设置为 90%的对象)。

表二。将无效值(正无穷大和负无穷大)转换为 NaN,删除丢失值比例较大的列(图片由作者提供)

之后,我们从训练样本中移除目标未知的对象— id 2。

表 3。删除目标列中具有未知值的行(作者图片)

然后应用 LabelEncoder 将标签' > 10 '和' < = 10 '分别翻译为 0 和 1。让我们删除特性 4 中的缩进。

表 4。目标变量编码和前导及尾随空格删除(图片由作者提供)

让我们稍微反思一下。sklearn 中的 OneHotEncoding 实现(我们在 AutoML 中使用)支持处理二进制分类特征和预处理操作在训练样本中没有看到的新类别。第一个意思是,如果表中有一个二进制分类特征,它不会通过扩展到多个列而被扩大。第二个意思是,如果训练样本具有诸如“中等”和“小”的类别,而测试样本具有“大”的类别,那么算法将能够继续而不会产生错误。唯一的问题是这两个选项不能同时工作:必须选择其中之一。我们认为,在处理新类别时,框架不崩溃更重要。

因此,二元分类特征(唯一类别的数量不超过两个)应该单独处理。我们正在用特性 5 做这件事:把值转换成“1”和“0”。

表 5。二进制分类特征的编码(图片由作者提供)

最后是功能 6。来自类型处理系统的输出也是作为预处理的一部分实现的,这是至关重要的。它对所有列都是一样的。但是首先,让我们定义这个问题:一个具有浮点值的特性有一个“?”sign,当加载这样的数据帧时,发现熊猫已经小心翼翼地将所有数字转换成字符串。但是,如果列中唯一值的数量很大,并且使用一种热编码进行编码,那么您会对结果表的大小感到惊讶。

使用 map 函数,我们查看列中的每个元素,并确定该列包含什么类型的数据。在本列的例子中,唯一值的数量比分类特征中应有的数量要多得多(可能在上面的特定片段中没有,但是如果表稍微大一点,您会马上看到)。然后,算法会尝试将列转换为 float 类型,同时将所有无法转换为此类型的值标记为 nan(请参见“?”)在表 6 中签名)。

我们以这样的方式通过每一列,每一个单元格。因此,当预处理结束时,我们就有了表中每一列的汇总信息,这些关于类型的知识会进一步用于各种数据处理模型和算法。

表 6:处理混合数据类型的列。替换“?”带有缺失值的签名(图片由作者提供)

尽管已经做了很多工作,但数据中仍然存在差距和分类特征。是的,这就是目标。现在我们继续第二步,额外的预处理步骤,允许对特征进行编码并消除间隙。

又一个实现(“你不能处理差距!”)

一旦数据经过必要的预处理,AutoML 的核心——进化算法——就启动了。正是这个“引擎”产生了多个管道。他们中的每一个都被给定了原始的预处理数据。请记住,数据只是经过了强力清理,仍然包含间隙和字符串数据类型。因此,对每个生成的模型应用不同的预处理步骤。

预处理表的主要目的是防止模型因错误而崩溃。让我们看看填补空白的例子。如果算法“理解”如果间隙没有被填充,则管道不能被拟合,则应用默认的间隙填充策略。如果管道结构中的模型可以拟合有间隙的数据,则该表将进一步未经处理。那么问题来了,“那么为什么不立即预处理数据,填补空白,并应用编码器呢?比较靠谱。”事实是,有相当多的方法来填补空白,并且许多不止两种编码方式。将搜索空间限制为只有一种预处理方式不是一种灵活的方法;— AutoML 算法将计算出如何最好地转换数据,以获得最准确的模型。

模型识别期间的专家推理是相似的——转换由要解决的任务的细节和数据集的特征决定:例如,用中值、*均值填充间隙,或者创建指标变量的。如果有必要,专家可以拒绝在模型中包含不合适的预处理方法,而只使用默认的转换。

管道结构分析基于从每个主节点(左边的那些)到根节点的图形的顺序遍历。根节点总是唯一的,位于右边的图中(图 3)。在这种情况下,操作由标签分为:

  • 可以处理数据并解决问题(例如,是否存在差距);
  • 可以跳过数据,但不消除误差源;
  • 当对数据执行操作导致错误时。

图 3。分析复合管道结构,对表格数据中出现的间隙示例进行额外预处理(图片由作者提供)

图中显示了不同的管道配置。如果复合管道在其结构中包含同化具有间隙和分类特征的数据的操作,则不执行预处理。否则,它将应用默认策略。

上述方法允许解决方案的灵活性。自动识别复合模型结构的算法可以找到最佳的预处理方法和模型配置组合。在流水线结构搜索过程中,预处理操作可以用类似的操作来代替。如果该模型足以产生令人满意的预测误差来产生默认预处理,则将仅基于这些模型来构建复合模型。

假设管道中没有预处理操作,默认情况下,算法需要填充间隙并对分类值进行编码。首先,让我们处理一下差距:它们存在于特性 1、3、5 和 6 中。对于第 1 列和第 6 列,通常的策略是用*均值来填补空白。第 3 列和第 5 列更有趣:这些列包含数值,但是这些列中的指标可以接受的唯一值的数量不超过两个。你知道有多少数字特征只包含两个值?—让我们假设—一个特征最有可能表示一个对象中某种东西的存在或不存在。例如,物体要么有尾巴,要么没有。这里不需要输入半色调。所以在这样的列中,让我们用多数类来填充值。如果类别的数量相等(这种情况很少发生),那么无论如何都会用二元特征的*均值来填充间隙。

表 7。默认情况下填充表格中的间隙(恢复的值以绿色显示)(图片由作者提供)

使用分类值 I 更简单—默认的编码策略是 OneHotEncoding(表 8)。

表 8。转换分类特征(作者图片)

决赛(“坦白说,亲爱的,我一点也不在乎”)

让我们复制我们在 AutoML 框架中开发预处理块时实现的所有调整。

因此,预处理分为两个层次:强制的和可选的。第一个是永远永远适用于所有数据。第二种方法仅在管道结构中没有模型来吸收数据时进行,以便在尝试训练管道时不会出现错误。

必修模块包括:

  • 用 nan 替换特征和目标列中的值 infinity。
  • 移除缺失值数量超过 90%的要素
  • 从表中移除目标变量值未知的要素(目标中有 NaN)
  • 将目标列中的标签从字符串格式转换为整数格式
  • 消除特征中字符串对象的缩进
  • 不同数据类型的列同化包括检测混合类型的列,并将它们转换为多数类型、任何可能的类型,或者从表中删除一列。

可选模块:

  • 间隙填充作业的管道结构分析——必要时进行间隙填充
  • 分类编码操作的管道分析-如果需要,应用编码

上面的修改允许框架在提到的数据集上运行(甚至在最可怕的数据集上),完全符合章节标题中的短语。

我想停下来想一想。以上描述的都是复杂的技术任务(不是开玩笑)。但是它对研究人员或商业客户有吸引力吗?—可能不会。所有这些对数据的大惊小怪通常希望尽快被跳过,并转移到模型开发,因为这是最令人兴奋的部分。想象一下,以这种方式预处理数据需要编写多少代码。所以把所有的预处理留给 AutoML 算法不是更好吗?

请注意,有时您希望自己构建模型,而不是将最激动人心的部分交给 AutoML 算法。然后,您可以通过为 FEDOT 编写以下代码行来分配所有预处理:

import numpy as np
from fedot.core.data.data import InputData
from fedot.core.pipelines.node import PrimaryNode
from fedot.core.pipelines.pipeline import Pipeline
from fedot.core.repository.dataset_types import DataTypesEnum
from fedot.core.repository.tasks import Task, TaskTypesEnumtrain_input = InputData(
idx=np.arange(0, len(features)),                
features=features, target=target, task=Task(TaskTypesEnum.classification), data_type=DataTypesEnum.table
)pipeline = Pipeline(PrimaryNode('scaling'))
pipeline.fit(train_input)
preprocessed_output = pipeline.predict(train_input)
transformed_data = preprocessed_output.predict

Pipeline 会自动启动数据处理器,输出会经过预处理和标准化,可以作为实验的基础。默认情况下会进行预处理。考虑到数据可能是什么,这可以在模型开发过程中节省大量时间。

结语。实验(“数据集就像一盒巧克力。你永远不知道你会得到什么。”)

因为在所有的表上运行这个框架需要很多时间,所以我们在汽车碰撞测试库中准备了一个小的玩具数据集。用于解决分类任务的表相对较小,但它是我们在所有 46 个数据集上运行期间遇到的所有糟糕事情的一般化表示。

如果你想在这些数据上运行你喜欢的框架,欢迎你。只有通过正确地预处理至少一些列、丢弃所有不必要的列并保留必要的列,才能为此表的测试样本键入 ROC AUC 1.0。通过这样的碰撞测试并不像看起来那么容易。而且拿到一个好成绩(ROC AUC 1.0)是很棒的成绩。

下面是对这些数据运行 FEDOT 框架(版本 0.5.2)的示例。运行该脚本的结果非常好——没有出现任何错误,并且我们可以获得最终的解决方案。存储库中还有运行它的所有代码和依赖项。

train_features, train_target = get_train_data()
test_features, test_target = get_test_data()# Task selection, initialisation of the framework
fedot_model = Fedot(problem='classification', timeout=timeout)# Fit model
obtained_pipeline = fedot_model.fit(features=train_features, target=train_target) 
obtained_pipeline.show()# Evaluate the prediction with test data
predict = fedot_model.predict(test_features)
predict_probs = fedot_model.predict_proba(test_features)

测试部分的最终指标是 1.0 ROC AUC。

结论(“一个男孩最好的朋友是他的汽车。”)

综上所述,AutoML 中实现的上述所有转换都不是最先进的技术。然而,它们的成功结合使得运行机器学习模型成为可能,即使在表格数据是“垃圾”的情况下。

值得记住的是,所有实现的算法都是在修改 AutoML 框架时开发的。一个好的 AutoML 框架必须在初级数据科学家不能快速可靠地工作的地方可靠地工作(或者可以,但不是非常可靠或非常快速)。如果这种预处理适用于我们的模型,那么它也适用于您的模型。

有用的链接:

在一次带有预处理表格数据指南的表演中,参与的有:米哈伊尔·萨拉法诺夫NSS 实验室团队

使用 AWS Glue DataBrew 清理和规范化数据

原文:https://towardsdatascience.com/cleaning-and-normalizing-data-using-aws-glue-databrew-b31fb1075302

使用 AWS DataBrew 自动清理数据,无需编写任何代码

斯蒂芬·道森在 Unsplash 上拍摄的照片

任何数据管道的主要部分都是数据的清理。根据项目的不同,清理数据可能意味着很多事情。但是在大多数情况下,这意味着将数据规范化,并将数据转换成项目中可以接受的格式。例如,可以从时间戳列中提取日期和时间成分到多个列中,转换字符串列的大小写,或者标签编码

我们为此编写代码,然后使用 Apache Airflow 等工具设计一个自动化的管道。我们已经这样做了很多年了。但是,随着我们将越来越多的处理和管道转移到云中,我们可以通过使用云基础设施提供商提供的工具来实现这些数据清理结果。通常,这样的工具不需要任何编码。作为一名开发人员,我可以看到这是如何威胁到我的工作的。但我不想卷入这场争论,因为我认为这是没有实际意义的。

无论如何,在这篇文章中,我们将看看 AWS Glue DataBrew,以及我们如何使用它来清理数据集。对于这个 POC ,我使用 S3 来存储输入和处理后的输出(主要是因为这是我看到的最简单的选项)并使用 NYC OpenData 数据集。更具体地说,我使用的是机动车碰撞——碰撞数据集。

什么是 AWS Glue DataBrew?

我们已经知道 AWS Glue 是一个用于设计提取、转换和加载(ETL)管道的工具。它为创建和运行 ETL 作业提供了很多特性。DataBrew 领先一步,提供了清理和转换数据的功能,为进一步处理或输入机器学习模型做好准备。

DataBrew 提供了 250 多种转换。这些包括过滤数据、转换格式或将数据转换为标准格式、修复数据问题、使用 regex 从列中提取数据等等。最棒的是,这些转换在 DataBrew 中已经可用,这意味着我们不需要编写任何这些转换的代码。

这样做的好处是,数据分析师可以自己设计转换管道,而不必等待开发人员对转换进行编码。您可以在管道的任何阶段应用这些转换,尤其是如果您已经有一个 Glue ETL 作业的话。

这里的另一个优势是所有这些都是无服务器的。这意味着您只需为您使用的东西付费,当管道不运行时,您不需要为任何东西付费。此外,它可以根据传入的数据量自动扩展。

现在,让我们从 DataBrew 开始吧。我们首先要创建一个新项目和一个配方。然后我们将下载样本数据集并上传到 DataBrew。然后,我们将应用一些转换,并创建一个作业来对整个数据集运行转换。最后,我们将看到转换后的数据是什么样子。

AWS Glue DataBrew 入门

创建项目

首先,前往 AWS Glue DataBrew 控制台并创建一个新项目。接下来,提供一个项目名和一个配方名,如下面的截图所示。

作者图片

对于此概念验证,我们可以将所有配置保留为默认值。为项目和配方命名后,向下滚动到选择数据集部分,上传样本 CSV 数据集。如果你还没有下载样本数据集,你可以从纽约市开放数据网站这里获得。

选择数据集部分,选择屏幕右侧的新数据集选项。为数据集提供一个名称。然后在连接到新数据集选项中选择文件上传选项。你可以在下面的截图中看到这个配置。

作者图片

输入 S3 目的地选项中,选择用于存放源数据和转换数据的 S3 存储桶。如果您没有为此创建的存储桶,您可以从这里自己创建。

最后,向下滚动到页面底部,单击创建项目按钮创建项目。这将需要一些时间,因为文件必须首先上传到 S3 桶,然后必须提供一堆资源,并且还必须预览文件。在此期间,您会看到一个进度指示器,如下所示:

作者图片

应用变换

准备好数据后,您将看到数据以表格形式呈现,在表格顶部有一堆选项。在这里,我们可以选择单个行,选择转换,预览结果,然后应用转换。此外,我们还可以操作列,如复制列、拆分列等。这些确实算作转换,但我只是想明确地把它们叫出来,因为它们是非常普遍应用的转换。

我们将从第一列开始应用转换,即崩溃日期在大多数分析用例中,我们会希望在几年内汇总一些数据。我们通常有时间戳列,从中我们可以提取查询中的年份部分。但是如果我们有一个专用于 year 值的列,查询会更容易和高效。所以我们现在将从日期列中提取年份,并将其保存为一个名为崩溃年份的新列。

为此,选择该列,单击该列右上角的三个点,选择提取菜单,然后选择日期时间值选项。这将在页面右侧打开一个新的配置窗格,如下面的屏幕截图所示:

作者图片

这里,创建列选项源列提取选项部分应该已经被填充。在提取的日期时间单位部分,从下拉菜单中选择年份,输入车祸年份作为目的列字段值。这将创建一个名为撞车年份的新列,其中年份部分是从日期列中提取的。点击底部的预览按钮预览更改。这应该会向数据集添加一个新列,如下面的屏幕截图所示。

作者图片

点击应用按钮应用转换。

接下来,我们将转换崩溃时间列,将其格式化为 HH:MM 格式。它已经是标准格式了。但是为了实验起见,我们还是要这样做。所以我们再重复一遍同样的过程。点击栏右上角的三个点,选择格式菜单,然后点击日期时间格式菜单。然后向下滚动到子菜单的末尾,选择更多选项。这将打开一个类似的配置窗格,我们在前面提取 year 组件时看到了这个窗格。使用下面的屏幕截图作为配置此转换的参考。

作者图片

单击预览更改选项,查看应用变换后的差异。应该和下面的截图差不多。最后,点击应用按钮来应用转换。值得注意的是,这些更改不会在点击应用按钮后立即应用。apply 按钮将只保存转换,以便我们可以随时返回并更改它。只有在我们运行作业之后,转换才会更改数据。

作者图片

现在,我们将最后的转换应用到另一列。对于机器学习用例,我们需要尽可能多地消除文本列。在我们处理分类数据的情况下,我们可以将它们映射到数字上,以确保机器学习模型不会产生任何偏见。在我们的数据集中,我们有一个这样的列— 促成因素车辆 1。将文本、分类数据转换成数字的过程称为分类映射。还有另外一种方法,叫做一个热编码

Glue DataBrew 提供了这两种选项。单击列右上角的三个点,打开上下文菜单并滚动到末尾,您将看到分类映射一键编码列选项。对于这个例子,我们将使用分类映射。所以选择菜单打开配置面板。匹配如下图所示的配置。

作者图片

点击预览更改按钮预览更改,应该如下所示:

作者图片

再次点击应用按钮,应用该转换。我们应该会看到添加了映射值的新列。现在,对于一个实际的项目,这种转换对机器学习没有太大帮助,因为数值会让模型认为这是某种排序而不是分类数据。我们需要使用一次性编码。但是在这个例子中,我们将坚持这样做。

之后,点击工作台右上角的制作方法按钮。现在,您应该可以看到我们应用于数据集的所有转换。它应该看起来类似于下面的截图。

作者图片

从这里,我们应该能够轻松地编辑或删除这些食谱。我们甚至可以从这个面板中添加一个新的转换。当我们想在一个地方检查应用到数据集的所有转换时,这非常方便。

运行作业

我们现在终于准备好运行作业,将所有这些转换应用到整个数据集。您应该会在页面右上角看到运行作业按钮。点击那个按钮,现在休息一下。这需要几分钟才能完成。

与此同时,我们可以查看数据谱系来可视化和理解完整的管道。点击运行任务按钮右侧的沿袭按钮。在这里,您将看到来自 S3 时段的数据流,通过转换配方,然后返回到 S3 时段。它应该看起来类似于下面的截图所示。

作者图片

您可以看到,我们有一个数据集是从 S3 存储区中的源文件创建的。从该数据集中,我们有一个包含六个配方的项目。这六种配方目前正通过作业应用于数据集,最后,转换后的数据将保存回 S3 存储桶。

好了

差不多就是这样。我重复一遍,这不是典型的生产流水线。通常,数据源通过 Kinesis DataStream 等工具流入系统。通过微批处理即时应用转换,并存储在数据仓库中。但是如果用例是用于批处理,S3 可能是源。

尽管如此,这个练习应该可以帮助您开始使用 DataBrew,并探索该工具的高级转换功能。如果这个工具没有提供你需要的转换,或者如果你需要添加一个定制的转换,你总是可以使用 Lambda 函数

AWS 有很好的工具来创建带有转换的数据管道、数据湖和仓库、机器学习模型等等。您可以点击这里查看更多资源、入门指南和 AWS 服务的概念验证。如果你想让我为你探索一个特定的工具或服务,请在评论中告诉我。

如果你喜欢你在这里看到的,或者在我的个人博客上看到的。要写博客,并希望在未来看到更多这样有用的技术帖子,请考虑在 Github 上关注我。

原载于 2022 年 1 月 17 日 https://blog.contactsunny.comhttps://blog.contactsunny.com/data-science/cleaning-and-normalizing-data-using-aws-glue-databrew

[1] NYC OpenData —纽约警察局https://data . cityofnewyork . us/Public-Safety/Motor-Vehicle-Collisions-Crashes/h9gi-nx95

蒙特卡洛强化学习的悬崖行走

原文:https://towardsdatascience.com/cliff-walking-with-monte-carlo-reinforcement-learning-587e9d3bc4e7

TD(0)和 TD(1)学习的比较,包括完整的 Python 实现

玛丽安·布兰德在 Unsplash 上的照片

新的一年,新的悬崖行走算法!这一次,蒙特卡洛强化学习将被部署。可以说,这是强化学习最简单、最直观的形式。本文将该算法与时间差分方法(如 Q 学习和 SARSA)进行了对比。此外,还提供了完整的 Python 实现,并显示了一些数值结果。

悬崖漫步问题

悬崖行走问题是一个教科书问题(萨顿和巴尔托,2018 年),其中一个代理试图从左下瓦片移动到右下瓦片,旨在最大限度地减少步骤数,同时避免悬崖。一集在走进悬崖(大负奖励)或在目标瓦片上(正奖励)时结束。

悬崖漫步世界[图片由作者提供]

蒙特卡洛强化学习

蒙特卡洛 RL 还有其他几个名称(技术上有一些细微的区别,但我们可以忽略不计),包括:

  • 双重传递:首先向前传递直到剧集结束,同时收集奖励,然后向后传递以更新所有遇到的状态的值函数。
  • 向后传递:这个术语强调向后机制,在情节中遇到的所有状态上循环返回。
  • 时间差 1 或 TD(1) : TD(0)指的是在每个时间步长之后发生的更新,TD(1)在整个轨迹之后更新。

我并不偏爱这两个名字,但是在本文的剩余部分,我将坚持使用蒙特卡罗 RL (MC-RL)。

无论如何,核心概念是首先完成完整体验轨迹——在这种情况下,这些步骤一直走到达到目标或跌入悬崖——然后才继续更新查找表。

MC-RL 和时间差分方法如 Q-learning 和 SARSA 的根本区别在于,后者在每个时间步长后更新查找表,使用估计值 Q(s_t+1,a)更新Q(s_t,a_t)。这个过程也称为引导;一个估计用于更新另一个。作为复习,以下是更新功能:

q-学习更新公式(非政策)

SARSA 更新公式(根据政策)

相比之下,MC-RL 仅利用轨迹的实际观测值来更新查找表。关键的好处是使程序无偏;为了直观起见,假设观察值是从相应的概率分布中随机抽取的。特别是当 Q 值由*似函数表示时——当问题变大时,这通常是不可避免的——偏差会严重影响性能。然而,MC-RL 奖励轨迹表现出更大的方差,因此通常学习(慢很多)。

价值函数V(s_t)的更新函数非常简单,简单地计算观察到的回报轨迹G_t和价值函数V(s_t)之间的误差,用权重α进行更新:

蒙特卡罗强化学习的更新函数

其中累积奖励轨迹G_t(下游奖励除以γ)由下式给出:

累积奖励函数

机制非常全面和直观,但让我们考虑一些并发症。看看下面两个轨迹示例:

成功轨迹。在目标状态下产生的积极回报会反向传播到轨迹中的所有状态。[图片由作者提供]

失败的轨迹。在悬崖状态产生的负回报会反向传播到轨迹中的所有状态。[图片由作者提供]

尽管轨迹几乎相同,但一个轨迹会导致所有遇到的状态的正奖励更新,而另一个轨迹会导致负奖励更新。如前所述,蒙特卡罗方法的方差往往非常高,因为累积奖励可能会有很大差异。上述情况对 TD(0)学习的影响要小得多。

此外,以次优解决方案告终的风险也在增加。对于这个特定的问题,代理可能只坚持一条路径,仅仅因为它曾经发现过这条路径。由于这些图块具有较高的值,代理可能会一遍又一遍地遵循相同的路径。

数值实验

这里介绍的蒙特卡罗方法是政策上的,这意味着它使用实际观察到的奖励来更新估计值。因此,一个公*的基准是 SARSA,它也是根据政策原则运作的。像往常一样,Python 源代码可以在我的 GitHub 库中找到。

注意到的第一件事是,使用标准探索率ϵ=0.05,MC-RL 通常不会在 10,000 集内收敛到令人满意的解决方案。我把它提高到ϵ=0.10,为了公*起见,对 SARSA 也做同样的事情。

事不宜迟,该出结果了。显然,蒙特卡罗 RL 比 SARSA 表现差得多,需要更长的时间来收敛,并且经常坚持更差的路径。急剧下降意味着代理继续遵循第一条成功的路径,之后几乎没有改善。

SARSA 和蒙特卡罗 RL 的比较。SARSA 通常收敛得更快,并且经常找到更好的解决方案。[图片由作者提供]

相应的解决方案也往往表现出一些奇特的模式。上了政策,自然要绕道悬崖(由于正面ϵ)。然而,也有一些模式显然没有理性基础。

使用 MC-RL 学习路径的示例[图片由作者提供]

使用 MC-RL 学习路径的示例[图片由作者提供]

尽管直觉很吸引人,但方差问题确实阻碍了 MC-RL 快速识别好的解决方案。如果有的话,这些结果说明了为什么时间差异学习方法已经成为基于价值的强化学习的标准。

外卖食品

  • 蒙特卡洛强化学习(或 TD(1),双通)基于观察到的完全回报轨迹更新价值函数。
  • 与时间差异学习方法(如 Q-learning 和 SARSA)相比,MC-RL 是无偏的、,即,值更新不受值函数的不正确先验估计的影响。
  • 由于奖励轨迹的高方差,MC-RL 通常比时间差异学习花费更长的时间来收敛。

蒙特卡洛强化学习算法的完整代码可以在我的 GitHub 资源库 上找到。

也来看看我这个系列的其他文章吧!

-Q-learning:

离散政策梯度 :

深度政策梯度 :

深度 Q-学习 :

参考

鲍威尔(2007 年)。*似动态规划:解决维数灾难(第 703 卷)。约翰·威利的儿子们。

萨顿和巴尔托(2018 年)。强化学习:简介。麻省理工出版社。

基于空间的临床命名实体识别

原文:https://towardsdatascience.com/clinical-named-entity-recognition-using-spacy-5ae9c002e86f

一种基于空间的医疗保健领域 NER 方法

作者照片

正如我在以前的文章[1]中所描述的,自然语言处理( NLP )成为了 AI 研究和应用的热门话题,因为文本(例如,英语句子)是自然语言数据的主要类型。医疗领域文本数据的人工智能方法有很多,如临床文本分类、临床命名实体识别()、……等。

在[1]中,我使用了一个开源的临床文本数据集[2][3]来介绍一些常见的用于临床文本分类的机器学习和深度学习方法。

在本文中,我使用相同的数据集来演示如何使用 spaCy 4实现特定于医疗保健领域的命名实体识别( NER )方法。

新 NER 方法包括以下步骤:

  • 预处理数据集
  • 定义特定于领域的实体和类型
  • 生成带注释的数据集
  • 创建和培训 NER 模型
  • 评估 NER 模型
  • 扩展 NER 模型
  • 预测和可视化命名实体

1.预处理数据集

从 Kaggle [3]下载数据集 mtsamples.csv 文件后,数据集可以作为 Pandas 数据帧加载到内存中,如下所示:

raw_df = pd.read_csv('./medical-nlp/data/mtsamples.csv', index_col=0)
raw_df.head()

数据集中有 40 个独特的医学专业类别。如[1]中所述,通过以各种方式过滤数据集,医学专业类别的数量可以从 40 个减少到以下 9 个类别:

为了更好地演示,通过将本文中的转录列改为小写,对这个过滤后的数据集进行进一步预处理。

2.定义特定于领域的实体和类型

空间中的 NER 模型是有监督的深度学习模型。因此,数据集中的每个文档都需要标记的实体来进行模型训练和测试。

典型的文本注释工具如 prodigy 用于注释文档中的实体类型。本文利用另一种方法来自动生成带注释的文本数据集。为此,需要指定实体(不是它们在文档中的位置)和类型。

出于演示的目的,假设我们对以下实体(疾病或医学)和类型(医学专业)感兴趣:

每种实体类型的实体在实现中存储为一个集合。作为一个例子,下面是手术的设置:

surgery = set(['acute cholangitis', 'appendectomy', 
               'appendicitis', 'cholecystectomy', 
               'laser capsulotomy', 'surgisis xenograft', 
               'sclerotomies', 'tonsillectomy' 
               ])

3.生成带注释的数据集

如前所述,为了训练和测试空间 NER 模型,每个包含实体的文本文档都需要使用以下模板中的文本注释进行标记:

('文档文本',{ '实体':[(开始,结束,类型),…,(开始,结束,类型)]))

一旦确定了实体和类型(参见上一节),就可以自动生成一个带注释的文本数据集,如下所示:

  • 使用识别的实体和类型创建空间实体标尺模型
  • 使用实体标尺模型从给定文档中查找每个实体的位置和类型
  • 使用确定的位置和类型以及上面的模板来标记给定的文档

可以通过三个步骤创建空间实体标尺模型(参见下面的 RulerModel 类中的 __ init __ 方法):

  • 为给定语言(如英语)创建空模型
  • 将一个 entity_ruler 管道组件添加到模型中
  • 创建实体规则,并将其添加到 entity_ruler 管道组件中
import spacy
from spacy.lang.en import English
from spacy.pipeline import EntityRulerclass RulerModel():
    def __init__(self, surgery, internalMedicine, medication, obstetricsGynecology):
        self.ruler_model = spacy.blank('en')
        self.entity_ruler =      self.ruler_model.add_pipe('entity_ruler')

        total_patterns = [] patterns = self.create_patterns(surgery, 'surgery')
        total_patterns.extend(patterns) patterns = self.create_patterns(internalMedicine, 'internalMedicine')
        total_patterns.extend(patterns) patterns = self.create_patterns(medication, 'medication')
        total_patterns.extend(patterns)

        patterns = self.create_patterns(obstetricsGynecology, 'obstetricsGynecology')
        total_patterns.extend(patterns)

        self.add_patterns_into_ruler(total_patterns)

        self.save_ruler_model()

    def create_patterns(self, entity_type_set, entity_type):
        patterns = []
        for item in entity_type_set:
            pattern = {'label': entity_type, 'pattern': item}
            patterns.append(pattern) return patterns

    def add_patterns_into_ruler(self, total_patterns):
        self.entity_ruler.add_patterns(total_patterns)

创建实体标尺模型后,可将其保存到文件中供以后使用,如下所示:

ruler_model.to_disk('./model/ruler_model')

下面的类generated dataset中的assign _ labels _ to _ documents()方法使用上述实体标尺模型查找实体的位置和类型,并使用它们生成带注释的数据集:

*class GenerateDataset(object):

    def __init__(self, ruler_model):
        self.ruler_model = ruler_model

    def find_entitytypes(self, text):
        ents = [] 
        doc = self.ruler_model(str(text))
        for ent in doc.ents:
            ents.append((ent.start_char, ent.end_char, ent.label_)) return ents     

    def assign_labels_to_documents(self, df):
        dataset = []
        text_list = df['transcription'].values.tolist() for text in text_list:
            ents = self.find_entitytypes(text)
            if len(ents) > 0:
                dataset.append((text, {'entities': ents}))
            else:
                continue        return dataset*

一旦生成了带注释的文本数据集,就可以将其分成子集,用于模型训练、验证和测试。

4.创建和培训 NER 模型

与实体标尺模型类似,空间 NER 模型可以分两步创建(参见下面的 NERModel 类的 init 方法):

  • 为给定语言(如英语)创建空模型
  • 将一个 ner 管道组件添加到模型中

一旦创建了 NER 模型,就可以调用类 NERModel 的方法 fit ()来使用模型训练数据集训练该模型。

模型训练遵循典型的深度学习模型训练过程:

  • 设置纪元的数量(在本文中默认为 10)
  • 混洗训练数据并将其分成每个时期的小批
  • 设置模型超参数,如丢弃率
  • 获取空间优化器,并使用它来管理目标函数损失的计算和通过反向传播更新权重
  • 显示模型训练进度(通过使用本文中的 tqdm 库)
*import spacy
from spacy.util import minibatch
from spacy.scorer import Scorer
from tqdm import tqdm
import randomclass NERModel():
    def __init__(self, iterations=10):
        self.n_iter = iterations 
        self.ner_model = spacy.blank('en') 
        self.ner = self.ner_model.add_pipe('ner', last=True)

    def fit(self, train_data):
        for text, annotations in train_data:
            for ent_tuple in annotations.get('entities'):
                self.ner.add_label(ent_tuple[2])
        other_pipes = [pipe for pipe in self.ner_model.pipe_names 
                       if pipe != 'ner']

        self.loss_history = []

        train_examples = []
        for text, annotations in train_data:
            train_examples.append(Example.from_dict(
               self.ner_model.make_doc(text), annotations))

        with self.ner_model.disable_pipes(*other_pipes): 
            optimizer = self.ner_model.begin_training()
            for iteration in range(self.n_iter):
                print(f'---- NER model training iteration {iteration + 1} / {self.n_iter} ... ----')
                random.shuffle(train_examples)
                train_losses = {}
                batches = minibatch(train_examples, 
                  size=spacy.util.compounding(4.0, 32.0, 1.001))
                batches_list = [(idx, batch) for idx, batch in 
                  enumerate(batches)]
                for idx, batch in tqdm(batches_list):
                     self.ner_model.update(
                         batch,
                         drop=0.5,
                         losses=train_losses,
                         sgd=optimizer,
                     )

                self.loss_history.append(train_losses)
                print(train_losses) def accuracy_score(self, test_data):
        examples = []
        scorer = Scorer()
        for text, annotations in test_data:
            pred_doc = self.ner_model(text)
            try:
                example = Example.from_dict(pred_doc, annotations)
            except:
                print(f'Error: failed to process document: \n{text}, 
                      \n\n annotations: {annotations}')
                continue

            examples.append(example)

        accuracy = scorer.score(examples)

        return accuracy*

以下是斯帕西·NER 模型训练输出的屏幕截图:

在模型训练期间,损失历史已经保存在 NERModel 类的 fit ()方法中。

*from matplotlib import pyplot as pltloss_history = [loss['ner'] for loss in ner_model.loss_history]
plt.title("Model Training Loss History")
plt.xlabel("Iterations")
plt.ylabel("Loss")
plt.plot(loss_history)*

上述代码使用保存的损失历史数据绘制了以下损失历史图。

一旦 NER 模型被训练,它可以保存到文件中以备后用,如下所示:

*ner_model.to_disk('./model/ner_model')*

5.评估 NER 模型

一旦一个空间 NER 模型被训练,那么 NERModel 类的 accuracy_score ()方法就可以用模型测试数据集来调用,以获得模型精度性能结果。

原始精度结果是一个 Python 字典,其中包含令牌、实体和每种实体类型的精度、召回率和 f1 值。这样的字典可以被格式化为熊猫数据帧,如下所示:

6.扩展 NER 模型

如前所述,本文创建了一个实体标尺模型,用于自动生成带注释的文本数据集。

以下代码显示了如何通过将相同的实体标尺模型和 NER 模型合并为一个来扩展已训练的 NER 模型。这很有用,因为实体标尺模型识别的实体可能由于训练数据和/或时间的限制而被 NER 模型遗漏。

*from spacy.language import Languagedef extend_model(surgery, internalMedicine, 
                 medication, obstetricsGynecology):
    ruler_model = spacy.load('./model/ruler_model') 
    base_ner_model = spacy.load('./model/ner_model')     @Language.component("my_entity_ruler")
    def ruler_component(doc):
        doc = ruler_model(doc)
        return doc

    for entity_type_set in [surgery, internalMedicine, 
                            medication, obstetricsGynecology]:
        for item in entity_type_set:
            base_ner_model.vocab.strings.add(item) if 'my_entity_ruler' in base_ner_model.pipe_names:
        base_ner_model.remove_pipe('my_entity_ruler') base_ner_model.add_pipe("my_entity_ruler", before='ner') 

    return base_ner_model*

7.识别和可视化命名实体

一旦空间 NER 模型被训练和/或扩展,那么我们可以使用它从给定的文本文档中识别命名实体,如下所示:

*doc = *extended_ner_model*(text)*

可以通过文档访问已识别的实体。

下面的功能是在 Jupyter 记事本中使用颜色编码显示已识别的命名实体:

*def display_doc(doc): 
    colors = { "surgery": "pink",
               "internalMedicine": "orange",
               "medication": "lightblue",
               "obstetricsGynecology": "lightgreen", 
             }
    options = {"ents": ["surgery", 
                        "internalMedicine", 
                        "medication",
                        "obstetricsGynecology",
                       ], 
                "colors": colors
              } displacy.render(doc, style='ent', 
                     options=options, jupyter=True)display_doc(doc)*

以下是两个示例文档中带注释的实体的显示:

**

8.结论

在本文中,我使用了与[1]中描述的相同的数据集[2][3],来展示如何使用 spaCy 4实现特定于医疗保健领域的命名实体识别( NER )方法。在该方法中,首先识别一组医疗实体和类型,然后创建空间实体标尺模型并用于自动生成用于模型训练和测试的带注释的文本数据集,之后创建并训练空间 NER 模型,最后使用相同的实体标尺模型来扩展训练的 NER 模型的能力。结果表明,这种新的 NER 方法在识别临床领域特定命名实体方面是有效的。

参考

[1] Y. Huang,用于临床文本分类的常用机器学习和深度学习方法

[2]MTS 示例:https://www.mtsamples.com

[3] T .波义耳,https://www.kaggle.com/tboyle10/medicaltranscriptions

4空间:https://allenai.github.io/scispacy/

鸣谢:我要感谢 MTSamples 和 Kaggle 提供的数据集。

剪辑和粘贴:使用人工智能从文本提示创建照片拼贴

原文:https://towardsdatascience.com/clip-and-paste-using-ai-to-create-modern-collages-from-text-prompts-38de46652827

如何使用 ML 模型从照片中提取物体并重新排列以创建现代艺术

来自剪辑和粘贴的样本照片拼贴画,图片由作者提供

在我之前的许多帖子中,我展示了如何使用人工智能模型从文本提示中创建新的艺术作品。对于这些项目,我至少使用了两个人工智能模型,一个是生成式对抗网络(GAN)等生成器来创建图像,另一个是多模态语言/图像模型,如 OpenAI 的对比语言-图像预训练(CLIP) [1]来指导生成器。

对于这个项目,我想看看我是否可以让 CLIP 从文本提示中组装现代照片拼贴画,而不使用人工智能模型作为生成器。我构建了一个自定义的图像生成器,它组装图像的各个部分,并使用 CLIP 反复分析和调整拼贴中的各个部分。我称这个系统为“剪辑和粘贴”,其中第二个缩写代表通过编辑在语义上组合的图片。

下图显示了剪切和粘贴的重要组成部分。我将简要讨论这些元素,然后在接下来的部分中描述细节。

剪贴组件图,作者图

这个过程从一个文本提示开始,就像“企鹅从雪山上滑下”系统使用 Python 关键短语提取库[2]来提取关键词。然后,它从 Wikimedia Commons [3]和 OpenImages 4中搜索与提示中的关键字匹配的图像。我使用维基媒体搜索 API 来寻找可能匹配的图片。对于 OpenImages,我使用剪辑文本编码器,通过比较关键字和描述的嵌入来找到最佳文本匹配。接下来,该系统从 Wikimedia Commons 和 OpenImages 下载前 100 张图像,并通过剪辑图像编码器运行它们,再次将它们与关键字进行比较。该系统通过 GRoIE 5图像分割模型运行顶部图像,以从照片中创建剪切部分。剪切部分通过剪辑重新运行,以找到与关键字最匹配的图像部分。我构建了一个定制的图像合成器,它可以将图片随机组合成多个照片拼贴。布局通过剪辑运行,以找到最好的一个。该系统通过使用 Pytorch 中的 Adam 优化器[6]迭代地分析和合成拼贴来调整图像部分的位置。经过 100 次迭代后,显示最终图像。

你可以在下面的章节中了解这个系统的细节。请务必查看附录中更多生成的照片拼贴。你也可以使用 Google Colab 在这里创建自己的。

背景

制作照片拼贴的艺术,也称为照片蒙太奇,可以追溯到 100 多年前。

拼贴通常被认为是一种典型的现代艺术手法。这个词来自法语动词 coller ,意思是“粘着”,最早用于描述巴勃罗·毕加索和乔治·布拉克的立体派创新,他们于 1912 年开始将剪报和其他材料粘在画布上。故事是这样的,从那时起,艺术家们就开始用剪贴的方式将我们周围的世界在画布上进行意想不到的、变革性的组合。——塞缪尔·赖利,《经济学家》

这里有一些早期照片拼贴的样本,从左上按时间顺序顺时针展示。

从左上顺时针方向,带广告的静物,1913 年,巴勃罗·毕加索,玻璃杯和瓶子(皮草),乔治·布拉克,1914 年,用菜刀切开德国最后一个魏玛啤酒肚文化时代,1919 年,汉娜·霍奇,女孩、运动员和小丑拼贴画,1925 年,亚历山大·罗德钦科,艺术评论,1920 年,拉乌尔·豪斯曼

你可以看到艺术形式是如何发展的,从用颜料和铅笔画将剪报粘贴在画布上,到仅仅通过安排照片的剪切来制作作品。

系统详细信息

以下是对“剪切和粘贴”中使用的组件和过程的描述。

关键短语提取

与过去的项目不同,我使用 KeyBERT [7]从文本中提取关键词和短语,我改用一个叫做 Python 关键短语提取(PKE)的包。在对这两个软件包做了一点试验后,我发现 PKE 似乎做得更好。例如,PKE 允许我指定关键字的词性(名词、形容词和动词。)

下面是两个系统中从“企鹅从雪山上滑下”中提取的关键短语和分数的比较。

**KeyBERT** penguins skiing snowy, 0.9217
penguins skiing, 0.8879
skiing snowy, 0.6593
skiing snowy mountain, 0.643
penguins, 0.6079**PKE**
penguins skiing, 0.5
snowy mountain, 0.5

尽管 KeyBERT 想出了更多的候选关键词,但其中一些,如“企鹅在雪地里滑雪”是无意义的。然而,PKE 系统似乎用两个同等权重的关键词“企鹅滑雪”和“雪山”抓住了提示的要点

你可以在这里看到我用于关键短语提取的 Python 代码。

查找源图像

正如我在概述中提到的,我从 Wikimedia Commons 和 OpenImages 数据集收集拼贴图像。这两个来源都在知识共享署名共享开源许可下发布它们的图像。

为了在维基共享资源中搜索图片,我在 https://commons.wikimedia.org/w/api.php 使用了 API。

下面是设置搜索参数的 Python 代码。

s = requests.Session()
url = "https://commons.wikimedia.org/w/api.php"
params = {
"action": "query",
"generator": "images",
"prop": "imageinfo",
"gimlimit": 500,
"titles": keyphrases,
"iiprop": "url|dimensions",
"format": "json"
}
results = s.get(url=url, params=params)

当结果出来时,我根据它们的尺寸过滤图像。我保留尺寸为 512x512 或更大的图像。

为了在 OpenImages 数据集中搜索图片,我使用剪辑文本编码器来比较关键短语的嵌入和数据集中描述的嵌入。如果你想了解更多关于这个过程的信息,你可以在我的文章中阅读关于搜索设计专利的细节这里

一旦我获得了图像集合,我就通过 CLIP Image Encoder 将它们与整个提示的嵌入进行比较。以下是来自维基共享和 OpenImages 的顶级照片。

来源图片来自维基共享和开放图片

图像分割

我使用了一个称为 GRoIE 的人工智能模型进行图像分割,GRoIE 代表通用感兴趣区域提取器5。该系统是由意大利帕尔马大学的莱昂纳多·罗西等人开发的。给定输入图像,GRoIE 将提取并标记任何可辨别的前景对象。对于找到的每个对象,它会创建一个定义对象形状的遮罩图像。

注意,GRoIE 模型通常在每个输入图像中找到并提取不止一个对象。例如,在这张照片中,它发现并隔离了两只企鹅。

使用 GRoIE 进行多对象提取,图片由作者提供

在我提取了所有的对象之后,我再次运行 CLIP 来比较图像嵌入和来自提示的嵌入。原始图像在上面,提取的图像在下面。这些是从排名前五的图片中剪下的物品。

使用 GRoIE 进行图像分割,图像由作者提供

创建背景颜色渐变

我可以用纯色,比如白色或黑色,来合成照片拼贴的剪影。但是为了增加背景的趣味性,我写了一点 Python 代码来创建带有五个控制点的垂直颜色渐变。然后,我使用 CLIP 分析结果图像,并使用 Pytorch 中的 Adam optimizer [6]迭代调整颜色,以找到文本提示的最佳匹配。

我们提出 Adam,这是一种有效的随机优化方法,只需要一阶梯度和很少的内存需求。该方法根据梯度的一阶和二阶矩的估计来计算不同参数的个体自适应学习率;Adam 这个名字来源于自适应矩估计。迪德里克·金马和吉米·巴

如果你不会说数学,Adam 方法会稍微调整参数,并注意每次迭代的结果是否会改善以及改善了多少。它根据每个参数对改善结果的贡献大小,以不同的速率改变参数。

这是在对 25 个参数(红、绿和蓝的 5 个控制点)使用 CLIP 和 Adam 100 次迭代后,得到的“企鹅在雪山上滑雪”的渐变。)

使用剪辑创建背景渐变图片,图片作者

左边是参数值的图表,右边是合成的梯度图像。我不知道为什么它在中下区域显示紫色,但它看起来棒极了!

这里有一些使用这种方法的附加背景渐变。

背景渐变为(左)城市上空的夕阳(中)海边的晴天(右)秋叶,图片作者

正如你所看到的,创建一个只有 15 个参数的图像,在 CLIP 的指导下,是相当有表现力的。从文本生成颜色渐变的代码是这里是

寻找最佳拼贴布局

一旦系统有了背景图像和剪贴画,下一步就是有意义地排列对象。为了做到这一点,我创建了 100 个随机对象放置变量,然后通过 CLIP 运行它们,以查看哪个布局最匹配文本提示。我按大小对剪出的图片进行分类,将较大的图片放在背景,较小的放在前景。这样做可以减少遮挡较小图片的机会。

以下是三种随机布局的示例。

随机对象布局,作者图片

通过 CLIP 运行所有 100 个布局后,右边的一个与“企鹅在雪山上滑雪”最匹配这很好,但它可以使用一点微调。

优化布局

类似于创建背景渐变的方法,我再次使用 Adam 优化器来调整对象位置,以更好地匹配文本提示。这有效地颠倒了函数,允许优化器找到最佳解决方案。我使用了 Edgar Riba 等人编写的名为 Kornia [8]的软件包,它在 Pytorch 中执行数学上可微分的图像处理例程。

系统对渐变中的对象和颜色的位置进行微小的调整,然后使用 CLIP 分析结果图像。然后,它会检查自己是朝着更好的解决方案前进,还是远离它。这个循环重复 100 次,以创建最终的图像。

以下是“企鹅在雪山上滑雪”的第一、第五十和第一百次迭代的布局

布局优化,作者图片

你可以看到系统如何将企鹅的身体滑过人类滑雪者的头部和胸部,组织背景中的雪的图片,并调整颜色梯度,将紫色移动到图像的顶部。

这是“企鹅在雪山上滑雪”的最终拼贴画

企鹅从雪山上滑下,图片作者

好吧,这很酷(哈,双关语)。因为拼贴是使用参数组装的,所以可以以各种尺寸渲染,包括高分辨率。

请务必在附录中查看更多的剪贴照片。

讨论和未来方向

剪贴系统似乎工作得相当好。请注意,它高度依赖于从维基共享和 OpenImages 中找到合适的图像。如果它找不到与提示中的文字相匹配的图片,它会继续搜索找到的图片。

未来的改进将增加图像缩放和旋转。如果有更多的参数需要控制,优化器可能会找到更好的布局。

源代码和 Colabs

这个项目的所有源代码都可以在 GitHub 上获得。我在 CC BY-SA 许可下发布了源代码。

知识共享署名共享

感谢

我要感谢詹尼弗·林对这个项目的帮助。

参考

[1] 剪辑由 a .拉德福德等著,从自然语言监督中学习可转移的视觉模型 (2021)

[2] F. Boudin,:一个开源的基于 Python 的关键短语抽取工具包 (2016),COLING 2016 论文集,第 26 届计算语言学国际会议:系统演示

[3] 维基共享资源 (2004 年至今)

[4]open images(2020)

5 格罗伊由 l .罗西、A .卡利米和 A .普拉蒂提出,一种新颖的感兴趣区域提取层实例分割 (2020)

[6] D. P .金玛和 j .巴雷,亚当** : 随机优化的一种方法 (2015),学习表征国际会议2015**

[7] M. Grootendorst, KeyBERT :用 BERT 进行最小关键词提取(2020)

[8] E. Riba、D. Mishkin、D. Ponsa、E. Rublee 和 G. Bradski,Kornia:py torch 的开源可区分计算机视觉库 (2020),计算机视觉应用冬季会议

附录

这里有一些使用剪辑和粘贴来创建照片拼贴的例子。

波士顿天际线上的烟火

****波士顿天际线上的烟花,作者图片

一只哈巴狗在用萨克斯管演奏的铜管乐队里

****一只哈巴狗在铜管乐队中吹奏萨克斯管,图片由作者提供

与蝴蝶一起奔跑的抽象人

这一个只用了两个图像剪切。

****与蝴蝶一起奔跑的抽象男子,作者图片

为了无限制地访问 Medium 上的所有文章,成为的会员,每月 5 美元。非会员每月只能看三个锁定的故事。

CLIP:open AI 最有影响力的人工智能模型——以及如何使用它

原文:https://towardsdatascience.com/clip-the-most-influential-ai-model-from-openai-and-how-to-use-it-f8ee408958b1

了解该模型如何工作——包括编码示例

照片由 MaximalfocusUnsplash 上拍摄

最*的 AI 突破,【DALLE】【1】*稳定扩散*【2】有什么共同点?

它们都使用了 CLIP 的【3】架构的组件。因此,如果你想掌握这些模型是如何工作的,理解夹子是一个先决条件。

此外, 已用于索引上的照片。

但是 CLIP 是做什么的,为什么它是 AI 社区的一个里程碑?

让我们开始吧!

剪辑—概述

剪辑代表ConstastiveL语言-I法师 P 再训练:

CLIP 是一个开源、多模态、零镜头的模型。给定图像和文本描述,该模型可以预测与该图像最相关的文本描述,而无需针对特定任务进行优化。

让我们来分解一下这个描述:

  • 开源:模型由 OpenAI 创建并开源。我们稍后将看到如何使用它的编程教程。
  • 多模态: 多模态架构利用多个领域来学习特定的任务。剪辑结合了自然语言处理计算机视觉
  • 零距离: 零距离学习是一种对看不见的标签进行归纳的方法,没有经过专门的分类训练。例如,所有的 ImageNet 模型都被训练识别 1000 个特定的类。不受此限制。
  • const astic Language:通过这种技术, CLIP 被训练理解相似的表征应该靠*潜在空间,而不相似的表征应该远离潜在空间。这将在后面的例子中变得更加清楚。

关于 CLIP 的有趣事实:

  • CLIP 使用数量惊人的 4 亿个图文对进行训练。作为比较,ImageNet 数据集包含 120 万张图像。
  • 最终调好的剪辑模型在 256 V100 GPU 上训练了两周。对于一个关于 AWS Sagemaker 的按需培训,这至少要花费 20 万美元!
  • 该模型使用 32,768 幅图像的小批量进行训练。

夹住动作

让我们直观地演示一下夹子的作用。我们稍后将更详细地展示一个编码示例。

首先,我们从 Unsplash 中选择一个自由图像:

照片由андрейкурганUnsplash 上拍摄

接下来,我们为 CLIP 提供以下提示:

  • “一个戴着无沿帽的女孩”。
  • 一个戴着帽子的女孩。
  • 一个戴着无沿帽的男孩。
  • 一个骑自行车的女孩。
  • ‘一条狗’。

显然,第一个描述更好地描述了图像。

CLIP 通过分配一个标准化概率,自动找到哪个文本提示最恰当地描述了图像。我们得到:

模型成功定位最合适的图像描述。

还有,CLIP 可以准确的识别出它从未见过的类和对象。

如果您有一个大的图像数据集,并且您想要将这些图像标记到特定的类/类别/描述中, CLIP 会自动为您完成这项工作!

接下来,我们将展示夹子是如何工作的。

剪辑架构

CLIP 是一个深度学习模型,它使用了来自其他成功架构的新颖想法,并引入了自己的一些想法。

让我们从第一部分开始,对比预训练:

对比预训练

图 1 显示了对比预培训流程的概述。

假设我们有一批N图像与它们各自的描述配对,例如<image1, text1><image2, text2><imageN, textN>

对比预训练的目的是联合训练一个图像和一个文本编码器,产生图像嵌入[ I1I2……IN和文本嵌入[ T1T2……TN],其方式是:

  • 正确的嵌入对<I1,T1><I2,T2>(其中i=j)的余弦相似度被最大化。
  • 以对比的方式,相异对<I1,T2><I1,T3><Ii,Tj>(其中i≠j)的余弦相似度被最小化。

图 1:夹** ( 来源)的**对比预训练步骤

让我们一步一步来看看会发生什么:

  1. 模型接收一批N <图文>对。
  2. 文本编码器是标准的变压器型号,带有 GPT2 样式的修改【4】。图像编码器可以是 ResNet视觉转换器【5】
  3. 对于批次中的每幅图像,图像编码器 会计算一个图像矢量。第一幅图像对应于I1矢量,第二幅图像对应于I2,依此类推。每个向量的大小为de,其中de是潜在维度的大小。因此,该步骤的输出是N X de矩阵。
  4. 类似地,文本描述被压缩成文本嵌入[ T1T2TN ],产生一个N X de矩阵。
  5. 最后,我们将这些矩阵相乘,并计算每个图像和文本描述之间的成对余弦相似度。这产生了一个N X N矩阵,如图图 1 所示。
  6. 目标是最大化对角线上的余弦相似性——这些是正确的对。以对比的方式,非对角线元素的相似性应该最小化(例如,I1图像由T1描述,而不是由T2T2T3等描述)。

一些额外的备注:

  • 该模型使用对称交叉熵损失作为其优化目标。这种类型的损失最小化了图像到文本的方向以及文本到图像的方向(记住,我们的对比损失矩阵保持了<I1,T2><I2,T1>余弦的相似性)。
  • 对比预培训并不是全新的。在之前的车型中引入,并由CLIP【6】进行适配。

零射击分类

我们现在已经预训练了我们的图像文本编码器,我们已经为零镜头分类做好了准备。

基线 首先,我们来提供一些背景。前变形金刚时代是如何实现少镜头分类的?

很简单【7】:

  • 下载一个高性能预训练的 CNN 比如 ResNet ,用它进行特征提取,得到图像特征。
  • 然后,使用这些特征作为标准分类器的输入(例如逻辑回归)。分类器以监督方式训练,图像标签作为目标变量(图 2 )。
  • 如果你选择K-shot学习, 你在分类阶段的训练集应该只包含每个类的 K 实例。
  • **K**<10时,该任务被称为少镜头分类学习。相应地,对于**K**=1,我们有一次性分类学习。如果我们使用所有可用的数据,这是一个完全监督的模型(老式的方法)。

图 2: 具有特征提取的图像分类( I 作者的图像)

注意上面的关键字‘受监督的’——分类器应该事先知道类别标签。使用与分类器配对的图像提取器也被称为线性探头评估。

CLIP 的竞争优势
T13
CLIP
如何进行零次分类的过程如图图 3:****

图三:零拍分类使用剪辑* ( 来源)*

同样,这个过程很简单:

首先,我们提供一组文本描述,如a photo of a doga cat eating an ice-cream(我们认为最能描述一个或多个图像的任何内容)。这些文本描述被编码到文本嵌入中。

然后,我们对图像做同样的事情——图像被编码成图像嵌入。

最后, CLIP 计算图像和文本嵌入之间的成对余弦相似度。具有最高相似度的文本提示被选为预测。

当然,我们可以输入多个图像。 CLIP 巧妙的缓存了输入的文本嵌入,这样就不需要为其余的输入图像重新计算了。

就是这样!我们现在已经总结了夹子是如何端到端工作的。

寻找数据的问题

CLIP 使用 30 个公共数据集进行预训练。用大量数据拟合大型语言模型是很重要的。

然而,很难找到具有成对图像-文本描述的健壮数据集。大多数公共数据集,比如 CIFAR ,都是只有一个单词标签的图像——这些标签就是目标类。但是剪辑被创建来使用完整的文本描述。

为了克服这种差异,作者没有排除这些数据集。相反,他们进行了一些特征工程:单个单词标签,如birdcar被转换成句子:a photo of a doga photo of bird。在牛津-IIIT Pets* 数据集上,作者使用了提示:A photo of a {label}, a type of pet。*

有关预训练技术的更多信息,请查看原始论文[3]。

人工智能中剪辑的影响

最初,我们声称剪辑是人工智能社区的一个里程碑。

让我们看看为什么:

1.作为零触发分级机的卓越性能

CLIP 是一个零镜头分类器,所以首先针对少量镜头学习模型测试 CLIP 是有意义的。

因此,作者针对由高质量预训练模型之上的线性分类器组成的模型测试了剪辑,例如 ResNet

结果如图 4 所示:

图 4: 根据少数镜头分类,CLIP 相对于其他型号的性能(来源)

CLIP 明显优于其他分类器。

另外, CLIP 能够匹配 16-shot 线性分类器 BiT-M 的性能。换句话说, BiT-M 的分类器必须在每类至少 16 个样本的数据集上进行训练,以匹配剪辑的分数,而剪辑无需微调即可获得相同的分数。

有趣的是,作者将 CLIP 评估为线性探针:他们仅使用 CLIP 的 图像编码器来获取图像特征,并将其输入线性分类器——就像其他模型一样。即使在这种设置下, CLIP 的少数镜头学习功能也是非常出色的。

2.对分布变化的无与伦比的鲁棒性

分配移位是一件大事,尤其是对于生产中的机器学习系统。

注:你可能知道分配移位概念漂移,虽然从技术上讲它们是不一样的。**

分布偏移是当模型的训练数据随时间变化时发生的现象。因此,久而久之,模型的效率下降,预测变得不那么准确。

事实上,分配转移并不是意料之外的事情——它将会发生。问题是,如何及早发现这种现象,以及需要采取什么措施来“重新校准”您的模型?这个不好解决,取决于很多因素。

幸运的是,关于人工智能的新研究正朝着创造能适应分布变化的模型的方向发展。

这就是作者测试剪辑的鲁棒性的原因。结果显示在图 5:

图 5: 根据分布偏移,CLIP 对 ResNet 的性能(来源)

关于夹子有两件事非常重要:

  1. CLIP 在 ImageNet 上实现了与 SOTA ResNet 模型相同的精度,尽管 CLIP 是零镜头模型。
  2. 除了最初的 ImageNet 之外,我们还有类似的数据集作为分布转移基准。似乎 ResNet正在与这些数据集作斗争。然而, CLIP 可以很好地处理未知图像——事实上,该模型在 ImageNet 的所有变化中保持了相同的精确度!**

3.计算效率

GPT-2 之前,计算效率是理所当然的(某种程度上)。

如今,在一个模型需要用数百个 8k 美元的 GPU 训练数周的时代,计算效率问题得到了更认真的解决。

CLIP 是一个更加计算友好的架构。这一成功的部分原因是因为 CLIP 使用了一个视觉转换器作为默认的图像编码器组件。结果如图 6 所示:

图 6: 各种模型每幅图像的浮点运算次数(来源)

显然, CLIP 与其他型号相比,能够更好地利用硬件资源。这也意味着在培训云服务(如 AWS Sagemaker )时可以节省额外的资金。此外,图 6 显示与其他型号相比,夹子硬件操作精度分数方面提供了更好的可扩展性。

还有数据效率的问题。作者表明剪辑在零镜头设置中比类似模型更具数据效率。但是,它们没有解决片段在预训练阶段的数据效率问题。然而,在这方面可能没有太多事情可做,因为 CLIP 使用两种类型的变压器——变压器本质上是数据密集型模型。

4.研究兴趣增加

CLIP 的成功引发了对文本到图像模型的兴趣,并推广了对比预训练方法。

除了 DALLE稳定扩散,我们可以用 CLIP 作为 GANs 中的 鉴别器

此外, CLIP 的发布激发了类似的基于 CLIP 的出版物,这些出版物扩展了模型的功能,例如dense CLIP【8】CoCoOp【9】。****

此外,微软发布了X-CLIP【10】用于视频语言理解的 CLIP 的最小扩展。

****奖励信息:一款类似于猜字谜的 app,名为 paint.wtf ,使用 CLIP 对你的画进行排名。试试吧——超级好玩!

如何使用剪辑编码示例

接下来,我们将展示如何使用 HugginFaces 库来使用剪辑。****

首先,让我们从 Unsplash 中选择 3 张图片。我们之前使用了第一个:

照片由андрейкурганUnsplash 上拍摄

理查德·布鲁约在 Unsplash 上拍摄的照片

奥斯卡·萨顿在 Unsplash 上的照片

我们将使用以下库:

接下来,我们加载剪辑模型的权重,tokenizer 图像处理器:

此外,我们用 Python 加载了上面的 Unsplash 图像:

最后,我们为片段提供一些文本提示。

目标是让 CLIP 将 3 个不清晰的图像分类成特定的文本描述。请注意,其中一个是误导性的,让我们看看是否可以混淆模型:

该模型成功地将所有 3 幅图像分类!

注意两件事:

  1. CLIP 可以理解每个图像中的多个实体及其动作。
  2. CLIP 为每幅图像分配最具体的描述。例如,我们可以将第二幅图像描述为‘a dog’‘a dog at the beach’。然而,该模型正确地判定‘a dog’短语更好地描述了第二幅图像,因为没有海滩。

请随意使用这个例子。完整的例子是这里的
使用带有文字描述的图片,了解
剪辑的工作原理。

局限性和未来工作

虽然夹子是革命性的型号,但仍有改进的空间。作者指出了有进一步发展潜力的领域。

  • 准确度得分:CLIP 是一个最先进的零射击分类器,直接挑战特定任务的训练模型。事实上剪辑ImageNet 上完全监督的 ResNet101 的精确度相当惊人。然而,仍然有监督模型获得更高的分数。作者强调, CLIP 鉴于其惊人的可扩展性,可能会获得更高的分数,但这将需要天文数字的计算机资源。
  • ****多义性:作者陈述片段患有多义性。有时,由于缺乏上下文,模型无法区分某些单词的含义。请记住,我们之前提到过,一些图像只标记了类别标签,而没有全文提示。作者提供了一个例子:在牛津-IIIT 宠物数据集中,‘boxer’这个词指的是一个狗品种,但是其他图片认为‘boxer’是一个运动员。这里,罪魁祸首是数据的质量,而不是模型本身。
  • 特定任务学习:虽然 CLIP 可以区分复杂的图像模式,但该模型在一些琐碎的任务中会失败。例如,模型与手写数字识别任务进行斗争(图 7 )。作者将这种错误分类归因于训练数据集中缺少手写数字。

图 7: 数字分类(来源)

结束语

毫无疑问,CLIP 是 AI 社区的一个重要模型。

本质上, CLIP 为革新人工智能研究的新一代文本到图像模型铺*了道路。当然,不要忘记这个模型是开源的。

最后但同样重要的是,还有很大的改进空间。在整篇论文中,作者暗示许多剪辑的限制是由于低质量的训练数据。

感谢您的阅读!

我每个月写一篇关于 AI 的有影响力的论文的深度分析。
保持连接!

参考

  1. Aditya Ramesh 等人 分层文本条件图像生成与剪辑潜伏(2022 年 4 月)
  2. 罗宾·龙巴赫等人 用潜在扩散模型合成高分辨率图像(2022 年 4 月)
  3. 亚历克·拉德福德等 从自然语言监督中学习可转移的视觉模型(2021 年 2 月)
  4. 亚历克·拉德福德等人 语言模型是无监督的多任务学习器 ( 2019 )
  5. Dosovitskiy 等人 一幅图像抵得上 16x16 个字:变形金刚在尺度上的图像识别 (2020)
  6. 张宇豪等https://arxiv.org/pdf/2010.00747.pdf【2020】从成对的图像和文本中对比学习医学视觉表征
  7. 田,杨等 反思少镜头图像分类:一个好的嵌入就够了?【2020】**
  8. 饶永明等DenseCLIP:带上下文感知提示的语言引导密集预测
  9. 周开阳等视觉语言模型的条件提示学习(2022 年 3 月)
  10. 倪博林等 扩展通用视频识别的语言-图像预处理模型(2022 年 8 月)

线性回归的封闭解

原文:https://towardsdatascience.com/closed-form-solution-to-linear-regression-e1fe14c1cbef

一步一步的指导理论背后的美丽封闭形式的解决方案

如果你想进入机器学习领域,线性回归是理解机器学习基础的一个很好的起点。线性回归是一种非常简单但通用的机器学习工具。它广泛应用于工业和研究领域,所以如果你想在专业或学术环境中应用你的机器学习知识和技能,掌握它是关键。

线性回归是少数几个可以有封闭形式解决方案的机器学习应用程序之一。

如果可以的话,应该总是使用封闭形式的解来代替迭代算法,因为这是找到最优解的最直接的方法。

线性回归

什么是封闭解?

闭式解是一个可以用函数和数学运算来求解的方程。Wolfram MathWorld 对它有一个非常广泛和理论化的定义,但是它很复杂。

一种简单的思考方式是,是否有可能用一些老派的微积分而不是完整的机器学习算法来解决你的优化问题。而不是让你的算法迭代地改进优化函数,就像你使用梯度下降遗传算法等。,您对损失最小化函数进行求导,将其设置为零,并找到权重和偏差的最佳值(也称为您的 y = mx + b 等式的 mb )。

这不可能在所有情况下都做到,但我将在稍后概述何时可以使用它。如果可能的话,简单的数学解决方案将会直接引导你找到最优的解决方案。让我们更深入地了解一下封闭形式的解决方案是如何工作的。

照片由米卡·鲍梅斯特Unsplash 上拍摄

封闭式解释

如果优化问题相对于权重 w 是可微的并且导数可以被求解,则该优化问题是封闭形式可解的,但是这仅在最小/最大优化问题的情况下才成立。

为了弄清楚是否是这种情况,你采用最优化方程,根据权重 w 对其进行推导,将其设置为零,并求解 w

线性回归封闭解的推导

失业率对 GDP 的线性回归

在机器学习中,我们经常使用 2D 可视化来更好地理解我们可怜的小人类的眼睛和大脑。然而,我们几乎从来没有 2D 的数据。虽然整篇文章中使用的图形是 2D,但是这个解决方案适用于多维输入和输出的问题。

给定一组维度为 DN 输入向量(在可视化中,D=1,因此我们有一维输入数据)和一组维度为 S 的输出向量(在可视化中,S=1),我们正在寻找从输入到目标的映射函数。

观察结果:

目标:

映射功能:

因为我们使用线性回归,函数 f 是线性的。任何线性函数的形式都是 y = mx + b。像 y = mx + b 这样的一维映射意味着单个 x 值输出单个 y 值,就像如果 y = 2x+ 3,当 x = 2,y = 7。在这种情况下,我们的函数看起来像 f: 1 - > 1。然而,在现实世界中,我们有多维数据,所以公式有点不同。我们希望将多维输入映射到多维输出。

让我们一步一步来,首先从多维输入映射到一维输出,所以 f: D ->1。在这种情况下,输入 x 成为一个向量,我们需要权重因子 m 成为一个权重向量而不是一个标量值。我们的映射函数演变为:f: D - > 1:

如果我们更进一步,从多维输入映射到多维输出,我们需要找到一个映射 f: D ->S,其中 D 是我们输入的维度, S 是输出的维度。思考这个问题的一种方法是为输出的每个维度找到权重向量 m 和偏差 b 。这就好像我们在堆积一堆这样的功能:

如果我们正式地将这些映射堆叠在一起,偏差 b 就是堆叠的偏差 bi,并且权重矩阵 W 可以通过堆叠所有的 m 向量 so 来公式化

我们很少在一个数据集中只有一个样本。我们通常,如上所述,有一组 N 个数据样本。我们可以将所有这些数据样本添加到两个矩阵中,一个用于输入和输出。

我们希望同时通过函数传递所有这些信息,这意味着我们需要扩展函数以获得表单

这一步的功能是:

令人惊讶的是,得出这个公式相当简单。我们只需要吸收偏见。我们将改变矩阵 X ,在末尾添加一列 1,创建:

而矩阵 W 通过添加偏置向量作为最后一行,所以 W 变成

我们的新维度是:

我们成功地吸收了偏差,XW 接* y。

现在我们需要制定优化。因为我们想让 XW 尽可能接* Y,所以优化是最小化 f(X) = XW 和 Y 之间的距离,或者:

这本质上是最小二乘损失函数,其经典形式如下:

但是我们已经把它改编成矩阵。它只是为了让我们在求导时更容易些。我们使用最小二乘法策略,因为当分析线性回归时,如果 y = 3,f(x) = 1 或 f(x) = 5 并不重要,因为它们都会产生相同的误差。

正如我在开始时提到的,为了找到一个封闭形式的解决方案,我们现在需要进行映射并导出它。

万岁!我们已经成功地推导出了相对于权重 W 的映射函数。现在我们要做的就是将它设置为零,并求解 W 以找到最优解。

就是这样! W* 代表最佳权重。

对于那些真正热爱他们的理论线性代数的人来说,请随意挖掘令人眼花缭乱的伪逆世界。伪逆是:

也有一个正式的证明来说明为什么我们可以颠倒第一项,因为这是不可假设的。首先,我们假设 X 是满秩的。x 是一个维数为 N x D 的矩阵。满秩意味着有 D 或 N 个线性独立的列和行,以较小者为准。n 是我们的样本大小,D 是输入数据的特征数量。为了使其工作,样本数 N 必须大于特征数 d,我们只需要证明列是线性独立的。我们可以这样假设,因为如果不是这样,我们的数据集中的要素不会添加任何信息,例如,如果我们将摄氏和华氏温度都作为每个数据点的要素。这两个值是线性相关的,所以我们可以只去掉其中的一列。如果 X 是满秩,根据秩的定义,

这也是满秩二次的。所以,可以倒过来!

何时使用封闭解

应尽可能使用封闭形式的解决方案,因为它提供了最佳解决方案。在机器学习中,我们通常寻求最小化误差函数或等价问题,如果被优化的函数是可微分的,并且微分可以针对我们正在优化的变量求解,则问题有封闭形式的解决方案。虽然机器学习让很多人难以接受,但有时一点基本的线性代数是更干净的解决方案。

这种方法有几个缺点,其中最明显的是我们只能解决线性问题。这严重限制了使用这种方法可以解决的一系列问题。

但是,您可以使用基函数将封闭形式的解决方案应用于非线性数据。基函数是一种没有任何可学习参数的函数,但允许您将数据转换到不同的空间。

闭合形式的解决方案是一个简单而优雅的方式来找到一个线性回归问题的最佳解决方案。在大多数情况下,找到封闭形式的解决方案比使用迭代优化算法(如梯度下降)进行优化要快得多。

公开招聘自由职业技术写作和代笔工作:【margo.hatcher.writes@gmail.com】T2。我专注于数据科学和前端编程。

使用 DagsHub 注释和 Label Studio 结束数据循环

原文:https://towardsdatascience.com/closing-the-data-loop-with-dagshub-annotations-and-label-studio-8bd328d3785a

DagsHub x Label 工作室,作者图片

不需要在*台之间移动数据、遭受同步问题、破坏项目结构或执行繁琐的任务。

关于带有准确一致标签的干净数据的重要性,已经说了很多。整个以数据为中心的范例在很大程度上依赖于使数据标签更加一致来提高模型性能。那么,为什么数据科学界没有迅速采用这种方法呢?可能有许多原因,但一个反复出现的说法是标记是一个太乏味的任务,一个很难迭代、管理和扩展的任务。

阿拜石许可

DagsHub 集成了 Label Studio

标签是一项如此重要的任务,但它比想象中的要复杂。知道了这一点,我们决定 DagsHub 应该帮助消除这个障碍。对于这种集成,我们有许多强有力的候选人,但脱颖而出的是 Label Studio——一个强大的开源工具,它通过一个强大而活跃的社区支持许多非结构化和结构化数据类型的标记。

支持的数据类型:

  • 计算机视觉—图像和视频
  • 音频和语音应用
  • NLP,文档,聊天机器人,抄本
  • 时间序列
  • 结构化数据—表格、HTML、自由格式
  • 多领域应用

来自官方标签工作室网站

DagsHub 上的每个存储库都带有一个完全配置好的 Label Studio workspace 。此工作区允许您对数据进行注释,并可以访问所有项目文件。通过直接从 DagsHub 存储中获取数据,因此您不再需要将数据移动、复制或拉至第三方*台。这大大减轻了与标记相关的负担,即管理和同步数据和标签。

数据标注的 Git 流程

标注工作流相当于开发一个新功能。它应该在一个隔离的环境中完成,能够比较、分析和合并更改,或者回滚并恢复以前的版本。知道标签通常是外包的,这些能力变得更加重要,以确保其成功。

基于这些需求和贴标机面临的挑战,DagsHub 为 Label Studio 添加了一些配料,并创建了其独特的开源版本。它提供 Git 体验,遵循行业最佳实践,确保标签和数据的完全可再现性、可伸缩性和有效的版本控制

按作者标记非结构化数据、图像的 Git 流程

DagsHub 和 Label Studio 的工作流程

在 DagsHub 上创建新的标签项目时,您可以将其与活动分支的尖端相关联。它标记了项目的起点,并将使 DagsHub 存储上托管的所有文件在选定的提交下可用于标记。一旦获得有价值的结果,您就可以使用 Git 直接将注释版本化并提交到远程分支。一旦任务完成,您就可以在 DagsHub 上创建一个 pull 请求,在这里审阅者可以看到并评论每个注释。

如何用 DagsHub 对 Label Studio 项目进行版本化?

为了对任何工件进行版本控制,它需要有一个单一的真实来源。为了提供注释的真实来源,我们创建了.labelstudio目录,以开源格式保存每个任务的注释。当创建一个新的标记项目时,DagsHub 解析这个目录的选定提交,并将现有的注释加载到它们相关的任务中。这样,我们只需点击一个按钮就可以回滚到以前的版本。

Label Studio 和 DagsHub 入门

在这一节中,我将逐步指导您如何在遵循推荐的 Git 流程的同时使用 Label Studio 和 DagsHub 注释。主要目标是帮助您获得实践经验,同时受益于我的领导。为此,我将使用我的“埃隆在哪里”项目,在那里我注释埃隆·马斯克的图像。我假设您已经在 DagsHub 上有了一个项目,并且版本化数据已经准备好进行注释。

作者图片

步骤 1:创建 Label Studio 工作区。

导航到 DagsHub 存储库中的 Annotations 选项卡,并创建一个新的工作区。这个过程可能需要 2-3 分钟,因为 DagsHub 会在幕后启动 Label Studio 机器。

在 DagsHub 上创建一个 Label Studio 工作空间,图片由作者提供

步骤 2:创建标签工作室项目

在“新建注释项目”菜单中,选择要与项目关联的远程分支的尖端。它标记了项目的起点,并将使 DagsHub 存储上托管的所有文件在选定的提交下可用于标记。为了在隔离的环境中工作,我们将为标签项目创建一个新的分支。默认的项目名称基于创建它的注释者;但是,你可以随心所欲地改变它。

在 DagsHub 上创建一个 Label Studio 项目,Image by author

步骤 3:选择要注释的文件

第一次启动项目时,您需要选择要注释的文件(也称为任务)。您可以通过选中文件名旁边的框来选择特定文件或整个目录。

注意:你可以给 Git 和 DVC 远程主机上的文件添加注释。作为一个经验之谈的角色:“如果你能看到文件,你就可以对它进行注释。”

选择要在 DagsHub 上批注的文件,按作者排序的图像

步骤 4:配置 Label Studio

你可以配置 Label Studio 的标签界面,使用其中一个很棒的模板。如果你需要一个定制模板,你可以使用基本的 HTML 创建它。

注意:如果您选择使用模板,您需要手动设置项目的标签。

在 DagsHub 上配置 Label Studio,图片由作者提供

步骤 5:注释数据

就这么简单,您可以开始用注释您的数据。不需要将数据移动到不同的*台,改变其结构或同步任何东西。您可以开始处理任务,并将注释保存到 DagsHub 的数据库中。

注释 DagsHub 上的数据,图片由作者提供

步骤 6:提交对 Git 的更改

在任何时候,您都可以使用 Git 将项目状态版本化,并将变更提交回您在步骤 2 中选择的分支,或者创建一个新的分支并提交给它。提交将包括特殊的“. labelstudio”目录。您可以添加一个常用格式的注释文件(JSON、COCO、CSV、TSV 等)。)提交。

将注释提交给 DagsHub 上的 Git,按作者排序的图像

利用 Git 的功能,您现在可以无缝地迭代第 5 步和第 6 步,比较不同的版本,合并结果,或者回滚更改。

步骤 7:创建一个拉取请求

当您对标签感到满意时,意味着它们是准确和一致的,您可以将它们合并到主分支。使用 DagsHub,通过标签进行通信是拉取请求的一部分,而无需转移到第三方*台。审查者可以在每个标签上留下他的评论,并记录整个过程,便于管理。一旦完成任务,将它合并到项目的主分支中只需点击一下按钮。

摘要

标注非结构化数据伴随着各种挑战,其中许多是工作流的副产品,与标注任务本身无关。DagsHub Annotations 和 Label Studio 集成旨在帮助克服这些挑战并创建顺畅的标签工作流程。它执行 DevOps 繁重的工作,并为您提供管理和扩展贴标流程所需的工具。

如果您对整合有任何问题、想法或想法,我们很乐意在我们的 Discord channel 上听到!我们迫不及待地想看到你的惊人的项目变得更大,准确和一致的标签。

缩小机器学习和人类学习之间的差距

原文:https://towardsdatascience.com/closing-the-gap-between-machine-learning-and-human-learning-536720af00cd

大型语言建模的进展

LukasUnsplash 拍摄的照片

人类拥有强大的推理能力。他们能理解他人提出的问题,并给出最恰当的答案。人脑可以通过快速的数学运算来回答一个微不足道的问题,比如“如果我有 10 个球,买了两个罐头,每个罐头有 5 个球,我会有多少个球?”人类可以进行常识性的推理,比如“如果一个司机在交叉路口看到一个行人,他会怎么做?”人类有智能理解某人是否在开玩笑,并可能更深刻地理解说话者真正想说什么?

问题是,我们能训练机器获得我们人类拥有的这种智能吗?*年来,科学家们在这一领域进行了大量的研究。随着深度神经网络(DNN)和大型语言模型(LLM)的发明,我们在实现这一目标方面取得了良好的进展。在本文中,我将向您介绍通过使用 LLM 和 Google 最新的 PaLM[ ] (Pathways 语言模型)所取得的成就。

首先,让我们考虑一下我们正在努力完成的任务。

NLU 任务

在大型神经网络在自然语言处理(NLP)中取得巨大成功之后,研究人员将注意力集中在语言理解(NLU)和生成上,而不是简单的文本处理任务。那么,我们试图用这些巨大的网络来解决什么问题呢?我在下面给出了我们寻求解决的 NLU 任务的简短列表。尽管下面的列表并不详尽,但它会让您对我们的目标有所了解。

  • 语言翻译
  • 聊天机器人(问答)
  • 文本摘要
  • 语言生成
  • 论证

语言翻译

从英语翻译到德语或反之亦然,更确切地说,从任何语言翻译到任何语言,一直是我们的需要。今天,有几个 ML 模型甚至移动应用程序使用这样的预训练模型来以非常高的准确度完成这项任务。

聊天机器人(问答)

用自动化系统取代庞大的客户服务代表队伍一直是企业的梦想。这一点现在可以通过*乎完美的聊天机器人来实现。聊天机器人需要自然语言理解和问答能力。尽管特定领域的问答系统已经非常完善,但是开发一个面向开放领域的问答系统仍然是一个挑战。人类很快理解问题的上下文(领域)来回答问题。这就需要我们所知的 LLM 的少量学习[ ]。GPT-3[ ]是第一个应用少镜头学习的。最*的法学硕士如 GLaM[⁴],LaMDA[⁵],Gopher[⁶]和威震天-图灵·nlg[⁷]都采用了少击学习。

文本摘要

很多时候,我们需要创建一个长文档的摘要。虽然这是一项 NLP 任务,但语言理解在创建有意义的摘要时也起着有效的作用。具有注意力的编码器-解码器架构和基于转换器的架构在创建抽象和提取摘要方面都表现出了突出的成功。

语言生成

像莎士比亚那样写作是许多人的梦想。从 RNN(循环神经网络)、LSTM(长短期记忆)和最新的 Transformer 开始的神经网络架构允许创作可以模仿阿加莎·克里斯蒂和许多著名作家作品的小说。有许多工具可用,如 Arria NLG PLC[⁸],AX Semantics[⁹],Yseop[ ⁰],Wordsmith[ ],SimpleNLG[ ],NaturalOWL[ ]和其他自然语言生成(NLG)[ ⁴].

论证

人类有很强的常识推理能力、概念理解能力、玩琐事、同义词游戏的能力,以及根据反事实做出反应的能力。

这些仅仅是 NLP/NLU 研究进展强劲的几个领域。上述目标可以通过创建大型语言模型来实现。

创建 LLM

创建 LLM 的主要挑战是训练一个具有数百万和数十亿参数的超大型深度神经网络。像 GLaM 和 LaMDA 这样的模型是在一个单独的 TPU v3 吊舱上训练的。威震天-图灵 NLG 使用流水线并行在 GPU 集群中扩展到 2240 - A100 个 GPU。使用多个 TPU v3 pod 的 Gopher 实现了 4096 个 TPU v3 芯片的规模。他们观察到,具有更多训练参数的更大模型改善了 NLG 结果。PaLM 是这一类别中最新的一个,它使用谷歌的路径系统将训练扩展到 6144 个芯片,并创建了 5400 亿参数语言模型。它实现了 57.8%的硬件 FLOPs 利用率的训练效率,这是迄今为止 LLM 实现的最高效率。Google 重新设计了 Transformer 模块,允许并行计算注意力前馈层。这有助于为训练网络创建更好的并行策略。

https://medium.com/@profsarang/membership

手掌训练

他们在英语和多语言数据集的组合上训练 PaLM。其中包括维基百科文章、书籍、网络文档、对话甚至 GitHub 代码。注 PaLM 也能写计算机代码,那大概是因为它在 GitHub 上的训练。在代码生成中,空格很重要。因此,培训师创造了一个保留空白的“无损”词汇。他们还负责处理词汇表之外的 Unicode 字符,并将大数字拆分成单个数字。所有这些都有助于有效的代码生成。

现在,我将讨论 PaLM 的一些成就。

棕榈成就

研究人员在 29 个广泛使用的 NLP 任务上评估了 PaLM。在这 29 个任务中的 28 个上,它超过了先前语言模型的少数表现。它还在多语言 NLP 基准测试中显示了强大的性能,包括翻译。BIG是最新的基准,包含了 150 多个新的语言建模任务。与地鼠、龙猫和人类相比,PaLM 在 58/150 任务的子集上表现更好。它有效地区分了原因和结果。对于特定的上下文,它会为您的问题提供合适的答案。它可以玩同义词游戏。它可以从一篇文章中推导出相反的事实。

在我之前提出的问题中——“如果我有 10 个球,买了两个罐头,每个罐头有 5 个球,我会有多少个球?”—单纯的答案可能不容易以其准确性说服读者。你需要给出多步算术,推理出结论性的答案是如何推导出来的。使用思维链提示,PaLM 将通过为所有中间步骤生成文本来推理出答案。Google 博客[1]提供了一些例子来展示这些能力。

PaLM 可以生成一个明确的解释,甚至是一个笑话。生成这样的解释需要多步逻辑推理、世界知识和深刻的语言理解。博客[ ]中提供了一个很好的例子来说明这一点。

除了自然语言文本生成,代码生成是我们期望 LLMs 执行的另一个重要任务。代码生成可能意味着文本到代码,从一种编程语言翻译到另一种,甚至修复编译错误(代码到代码)。PaLM 训练数据集包括编码示例,尽管只有 5%。它已经显示出与诸如 Codex 12B[ ⁶].我将再次向您推荐 Google 博客[ ]中的优秀示例。

结论

在查看了 LLM 的最新发展,尤其是 PaLM 之后,人们可以观察到机器在学习自然语言和我们人类之间的差距正在迅速缩小。最*,Meta AI 也向研究社区提供了它的 OPT-175[ ⁷]十亿参数模型。我们可以希望看到机器学习和人类能力之间的差距很快缩小。

参考资料:

  1. 通路语言模型(PaLM)
  2. 少投学习
  3. GPT-3
  4. 多面手语言模型(GLaM)
  5. LaMDA
  6. 地鼠
  7. 威震天-图灵 NLG
  8. 阿里亚 NLG 公司
  9. AX 语义
  10. Yseop
  11. 语言大师
  12. 简单明了
  13. 自然猫头鹰
  14. 自然语言生成(NLG)
  15. 大板凳
  16. 抄本 12B
  17. OPT-175

闭包在起作用:闭包函数作为类型的巨大威力

原文:https://towardsdatascience.com/closures-in-action-the-dramatic-power-of-closure-functions-as-types-bf52be2b632e

在 Julia 中,将函数视为正常类型的各种用法产生了一些相当有趣的结果。

(图片由 Pixabay 上的 roketpik 提供)

介绍

Julia 编程语言及其功能的一个非常强大的地方是对闭包的强大支持。也就是说,在将函数视为数据方面,Julia 是一种出色的语言。使用 dispatch 函数是完全可能的,因此会产生一些非常棒的语法。当涉及到使类型和函数协同工作以实现给定目标时,这种语法也有许多应用。如果您不熟悉闭包函数,这可能是本文信息的一个先决条件。幸运的是,我正好有一篇文章更详细地介绍了这些是什么,你可以在这里阅读:

另一篇文章提供了更多相关信息,介绍了这类事情在 Julia 中通常是如何通过匿名函数定义来实现的,在接下来的文章中也会有更详细的讨论:

如果代码是你正在寻找的,我在这个项目中使用的代码可以在 Github 的这个资源库中以笔记本格式获得。

快速复习

在 Julia 中,闭包函数定义处理得非常漂亮。通常情况下,函数和类型之间的差异是无法衡量的,除非我们想通过提供()将该函数用作函数。它总是含蓄的,这是一件伟大的事情。同样,函数也很容易定义,任何东西都可以等同于函数,例如我们可以这样定义:

n = function hello(x, y)
   println("hi") 
end

然后我们可以像调用 hello()的定义一样调用 n。

n(5, 1)

我们当然也可以内联定义函数,使得函数和传统构造的数据结构之间的区别更加抽象——这是一件很棒的事情。特别重要的是,这种实现也不会妨碍其他事情,而这类事情经常会发生。这是闭包赋予的基本能力,仅仅利用这种思想,就可以实现一些非常激进和强大的解决方案。

原始闭包实现

使用函数作为参数的第一个实现实际上将是一个非常酷的用例。在这个例子中,我们需要为我们的服务器制作一个路由器,因为默认的 HTTP。HTTP 包中的路由器类型只能在非异步的服务器上工作,至少据我所知是这样。无论哪种方式,这都可以被认为是一种更加手动的方式,实际上,人们可以围绕这个实现制作他们自己的 mimes 所以这非常酷。为了清楚起见,mime 是 Julia 中的 display()函数到常规 HTT 协议之间的转换层。这只是一种奇特的方式来表达我们如何把这个 Julia 翻译成 HTML。所有这些都可以通过闭包有效地完成。想想这可能会有多强大是有点意思的。让我们看看这在 Julia 代码中是什么样子的。

using HTTP

我从定义一些构造函数开始编写代码,使一些类型实际上保存我们可能希望在服务器上保存的大量信息。这些信息包括不同的部分,比如路径、页面的组成部分,以及我们希望通过 HTTP 在前端显示的所有信息。我们将从路由构造器开始:

mutable struct Route
    path::String
    page::Any
    function Route(path::String = "", page::Any = "")
        new(path, page)
    end
end

这是我们自己的独特类型,最著名的构造函数是 Route(::String,:Any)。也就是说,每当我们要创建一条路线时,我们都要注意这一点。接下来,我将为我们的服务器创建一个外部构造函数。我将使用闭包方法定义,并将它们保存在一个常规闭包实现之上的结构中,使用一个函数来控制传入的路由。

mutable struct OurServer
    ip::String
    port::Integer
    routes::AbstractVector
    remove::Function
    add::Function
    start::Function

考虑到这一点,我们需要首先查看为我们创建这些方法的支持函数,

function funcdefs(routes::AbstractVector, ip::String, port::Integer)
    add(r::Route) = push!(routes, r)
    remove(i::Int64) = deleteat!(routes, i)
    start() = _start(routes, ip, port)
    return(add, remove, start)
end

这些新功能只是添加和删除路由以及启动服务器。记住,start 只是开始为 HTTP 服务器提供服务。

function _start(routes::AbstractVector, ip::String, port::Integer)
    server = Sockets.listen(Sockets.InetAddr(parse(IPAddr, ip), port))
    println("Starting server on port ", string(port))
    routefunc = generate_router(routes, server)
    [@async](http://twitter.com/async) HTTP.listen(routefunc, ip, port; server = server)
    println("Successfully started server on port ", port, "\n")
    println("You may visit it now at http://" * string(ip) * ":" * string(port))
    return(server)
end

虽然这个基本实现中没有日志记录,但这可能是处理整个事情的更好方法,因为现在我将只使用 println()。我们的 start 函数只是启动一个服务器,然后使用一个自动生成的 router 函数异步地为它服务。

function generate_router(routes::AbstractVector, server)
    route_paths = Dict([route.path => route.page for route in routes])
    routeserver = function serve(http)
     HTTP.setheader(http, "Content-Type" => "text/html")
        fullpath = http.message.target
    if contains(http.message.target, '?')
         fullpath = split(http.message.target, '?')
         args = ""
    end
     if length(fullpath) > 1
         args = fullpath[2]
     end
     if fullpath in keys(route_paths)
        write(http, route_paths[fullpath])
     else
         write(http, route_paths["404"])
     end
 end # serve()
    return(routeserver)
end
function stop!(x::Any)
    close(x)
endfunction stop!(x::Any)
    close(x)
end

还有一站!()函数杀死服务器。最终,这个 generate_router 函数就是神奇之处,它定义了一个到其内容的页面路由,然后使用 write()将正确的内容写出流。需要注意的重要部分是 routeserver 定义所在的位置。这使得该功能在关于路由的所有情况下都是可再现的,并且在没有找到路由的情况下,该路由请求 404 路由。现在我们完成了我们的内部构造函数,它依赖于我们在它下面创建的这个新的类型系统。

mutable struct OurServer
    ip::String
    port::Integer
    routes::AbstractVector
    remove::Function
    add::Function
    start::Function
    function OurServer(ip::String, port::Int64)
        routes = []
        add, remove, start = funcdefs(routes, ip, port)
        new(ip, port, routes, remove, add, start)
    endfunction OurServer()
        port = 8001
        ip = "127.0.0.1"
        OurServer(ip, port)
    end
end

使用这种技术,我们现在已经构建了一个服务器,它将根据请求本身的内容来引导或限制流量请求。非常棒,这个结果非常好合作。首先,我们创建路线:

home = Route("/", "<h1>HELLO WORLD</h1>")
four04 = Route("404", "<h1> Directory not found </h1>")

然后,我们创建一个服务器,并在其中添加两条新的 HTTP 路由。感谢我们的内部构造器,这绝对是轻而易举的事:

server = OurServer()
server.add(home)
server.add(four04)

现在我们可以像这样启动一个新的异步服务器:

serving = server.start()Starting server on port 8001
Successfully started server on port 8001

You may visit it now at [http://127.0.0.1:8001](http://127.0.0.1:8001/)Sockets.TCPServer(RawFD(45) active)

访问该链接会产生以下结果:

(图片由作者提供)

现在我们可以关闭服务器,转到另一个更酷的闭包实现。

stop!(serving)

作为参数的函数

虽然使用这些闭包函数的非常规酷的方式可能是将它们用作 HTTP 服务器的路由函数,但更有可能的情况是,人们最终会看到这些函数被用作参数。这通常是在处理复杂数据结构的基本 Julia 方法中完成的,所以这绝对是你想了解 Julia base 及其生态系统的东西。像这样的函数的一个例子就是过滤器!()函数,它采用一个函数作为筛选依据。这通常是通过匿名函数完成的。

x = [5, 10, 15, 20, 25]

假设我们想从这个数组中得到的只是 15 以上的值。分离这些值的典型方法是制作一个掩码。掩码是一个 bitarray,意思是一个布尔值数组,它决定每个值是否满足某个条件。我们真正需要做的是设计一个函数,然后应用于每个单独的值,以创建这个掩码。我们使用按位逻辑右操作符->创建这些函数。下面是我们如何单独创建该函数的示例:

x -> x > 15
#11 (generic function with 1 method)

或者,我们可以在过滤器的直接调用中定义它!()方法:

filter!(x -> x > 15, x)

虽然我们可以简单地通过名称将函数作为参数传递,但是还有另一个非常好的语法您可能想知道。这种语法称为 do/end 语法,它允许您在给定调用后将函数定义为代码块,然后将该函数作为参数进行处理。作为一个例子,我引用了我的代数数组的实现。这里有两个链接,一个是我详细介绍这些阵列如何工作的文章,另一个是这个项目的笔记本资源:

https://github.com/emmettgb/Emmetts-DS-NoteBooks/blob/master/Julia/Algebraic Arrays.ipynb

代数数组基本上是可以用某种规范形式表示的数据,所以计算机只计算它需要处理的部分。这个想法是将所有这些内存消耗和事物排除在环境之外,通过选择何时与什么数据进行交互,使科学在更大范围内变得更加可行。Julia 实际上使这个实现变得非常简单,这要归功于完成这类事情所需要的非常简单的多重调度绑定。无论如何,这里是看看什么类型,然后是使用 do/end 语法计算函数。

mutable struct AlgebraicArray
    f::Function
    n::Int64
    calls::Vector{Pair{Function, Tuple}}
    function AlgebraicArray(f::Function, n::Int64)
        new(f, n, [])
    end
end

在这之上是生成层,它允许我们为一个调用生成单独的索引。这存在于几种不同的调度形式中,如 range、bitarray 和没有附加参数的代数数组。

function generate(aa::AlgebraicArray, range::UnitRange)
    if range[2] > aa.n
        throw(BoundsError(string("Invalid algebraic index, ", string(range[2], 
                        " on algebraic expression of length ", string(aa.n)))))
    end
    [aa.f(n) for n in range]
end

最后一个前端是计算()。Compute 是通常用来从类似这样的代数运算中获取值的方法。使用 do 语法,我们可以将它作为一个流参数来使用,为了设置它,我们应该像这样构造我们的方法:

function compute(f::Function, aa::AlgebraicArray) # compute(aa)
    gen = generate(aa)
    for call in aa.calls
        gen = [call[1](gen, call[2]...)]
    end
    return(f(gen))
end

这一努力的最终结果变成了这样的语法:

mask = compute(z) do aa
    [if val > 12 true else false end for val in aa[1]]
end
20-element Vector{Bool}:
 0
 0
 0
 0
 0
 1
 1
 1
 1
 1
 1
 1
 1
 1
 1
 1
 1
 1
 1
 1

如果您想深入了解这背后的实际代码,并且想了解更多关于它的一般信息,那么我肯定会建议您阅读上面的文章,因为它可能会给出关于这些数组实际如何工作的更多解释。

结论

关闭的力量被大大低估了。该技术提供了许多好处和解决困难或复杂问题的新方法,这无疑使它有别于许多其他编程技术和方法。此外,Julian 实现确实允许以一些非常酷的方式使用函数。虽然在其他编程语言中肯定有这种元素,Python/Java 的 lambda 和 Javascript 的函数定义浮现在脑海中,但我必须说我真的更喜欢 Julia 处理这些事情的方式。

感谢您阅读我的文章,我希望对闭包功能的强调似乎是有道理的。也许在你的下一个项目中,你会用它来解决一些问题,因为它们经常会派上用场!

云基础:自动扩展服务器

原文:https://towardsdatascience.com/cloud-basics-auto-scaling-servers-71aa9945178a

AWS 中基于负载自动扩展服务器的教程

Jorge Ramirez 在 Unsplash 上的照片

云的优势之一是不必提前为生产中的工作负载准确预测和调配资源。在内部模型中,您需要通过纵向扩展(增加现有服务器的 cpu、内存等)来过度配置硬件资源。)或水*(添加更多同等资源的服务器)来尝试满足预期的峰值工作负载。这导致大部分时间服务器资源利用不足和浪费。

云允许我们根据各种参数自动扩展和缩减资源(服务器)。

图 1:云中的自动扩展选项

在本教程中,我们将根据上面列出的选项#1 和#2 测试自动缩放,步骤如下:

  1. 在负载*衡器后面的自动扩展组中启动一组 2 个 EC2 web 服务器。
  2. 基于测试工作负载的自动扩展
    -手动触发 CPU 峰值超过 90%持续 2.5 分钟
    -观察额外的服务器被自动创建
    -观察服务器在 CPU 低于 70%的 2 分钟后被移除
  3. 测试基于可用性的自动伸缩
    -手动删除 1 台服务器&观察新服务器的创建

先决条件

从下面链接的帖子中完成以下部分。

  1. AWS 设置
  2. GitHub 帐户设置和安装

1.启动自动扩展堆栈

目标:使用 CloudFormation 模板在负载*衡器后面的自动扩展组中启动一组 web 服务器。

图 2:虚拟私有云中负载*衡器后面的自动扩展服务器组的架构

  1. 转到https://github.com/shrestha-ajay/cloud-basics2.git
  2. 遵循"自述文件中的步骤。MD 文件中的回购或遵循以下步骤
  3. 通过点击 GitHub UI 中的“Fork”来分叉这个库
  4. 通过运行以下命令,将此存储库克隆到您的本地计算机上:
$ git clone [git@github.com:YOUR-USERNAME/YOUR-REPOSITORY.git](http://git@github.com:YOUR-USERNAME/YOUR-REPOSITORY.git)
$ cd YOUR-Repository-Folder

6.确保按照先决条件在您的计算机中配置了 AWS CLI

7.运行以下命令创建自动扩展解决方案堆栈

aws cloudformation deploy \
  --stack-name my-cloudbasic-autoscaling \
  --template-file AutoScalingMultiAZWithNotifications.template \
  --parameter-overrides KeyName="EC2Keypair" OperatorEMail="Email"

注:用您的值替换ec2 key pair&Email

8.转到 AWS 控制台> CloudFormation 和 AWS 控制台> EC2 以确认资源已创建。

9.从云信息堆栈的输出选项卡中打开 URL

10.堆栈启动后,检查您的电子邮件并确认订阅通知

图 3:从 CloudFormation 堆栈创建的负载*衡器的 URL

2.基于工作负载的自动扩展

目标:连接堆栈中的两台服务器,在 2.5 分钟内将服务器的 CPU 提高到 90%以上。这将触发组中新服务器的创建。这可能需要 5-7 分钟。当服务器上的负载在 2.5 分钟内降至 70%以下时,第三台服务器将自动终止。

代码 1:如果 CPU 负载在 2 分钟内> 90%,则自动添加新服务器的 CloudFormation 代码片段

步骤

  1. 从 AWS 管理控制台> EC2 列出当前作为自动伸缩组的一部分运行的 2 个服务器实例

图 4:自动缩放组的 EC2 实例列表

2.SSH 到服务器的公共 IP,并运行以下命令

$ **sudo ssh -i "yourEC2key.pem" ec2-user@54.146.219.38**[ec2-user@ip-10-0-1-116 ~]$ **yes > /dev/null &**[1] 12777[ec2-user@ip-10-0-1-116 ~]$ **top**[ec2-user@ip-10-0-1-116 ~]$ **kill -9 12777**[ec2-user@ip-10-0-1-116 ~]$ **top**

注意:
a. ssh 命令:Replace "yourec 2 key . PEM"和 IP 来匹配你的值 b. top 命令 : 显示 CPU 和资源利用率
c.
kill 命令:只有在第 3 台服务器启动后才运行 kill 。用你的进程 ID 替换 12777 警告 :请勿在您的电脑中运行“yes>/dev/null&”。这可能会导致您的计算机过热。

图 5:显示执行命令序列的屏幕截图

图 6:增加 CPU 后运行 top 命令的输出

图 7:显示创建第三个 EC2 实例的屏幕截图

图 8:显示服务器高 CPU 的屏幕截图

图 9:显示低 CPU 和移除(自动终止)第三个服务器的屏幕截图

3.基于可用性的自动扩展

目标:终止两个正在运行的服务器中的一个,并看到一个新的服务器被自动创建,因为 CloudFormation 模板表明我们需要至少两个服务器一直运行。

代码 2:定义自动扩展组中最小和最大服务器数量的云信息资源

步骤:

  1. 从 AWS 管理控制台> EC2 终止服务器
  2. 等待几分钟,让新服务器自动配置。

图 10:终止正在运行的实例,并看到自动创建的新实例

图 11:自动创建的实例的初始化

摘要

在本教程中,我们演示了自动伸缩如何适用于基于负载和可用性的场景。请注意,在整个实验中,负载*衡器的 URL 应该是打开的,而服务器根据负载和最低可用性进行伸缩。自动伸缩实现了 web 服务的高可用性和可靠性。有关实现的更多信息,请查看 GitHub 资源库中的 CloudFormation 基础设施代码。

图 12:从 CloudFormation 堆栈创建的负载*衡器的 URL

清除

温和的提醒删除云形成堆栈,以避免在你的实验后被收费。删除云形成堆栈后,应终止 EC2 服务器。

图 13:显示堆栈终止的屏幕截图

资源

  1. https://aws.amazon.com/autoscaling/
  2. https://aws.amazon.com/cloudformation/
  3. https://github.com/shrestha-ajay/cloud-basics2
  4. https://en . Wikipedia . org/wiki/Yes _(Unix)

云基础知识:与 AWS 交互

原文:https://towardsdatascience.com/cloud-basics-interacting-with-aws-da179d3f5829

关于使用各种接口创建云资源的教程

Unsplashengin akyurt 拍摄的照片

有基于编程和 GUI/web 的方式与云提供商进行交互,以创建和管理云中的资源。在本实验教程中,我们将通过以下四种方式与 Amazon Web Services(AWS)API 进行交互,以创建 AWS EC2 实例(虚拟化服务器)和 S3(云存储)。

  1. AWS 管理控制台(浏览器)
  2. AWS CLI(命令行界面)
  3. 交互式开发环境
  4. 自动气象站云形成

通过控制台进行更改对于实验来说很好,但除此之外并不可取。它也不支持自动化和版本控制。CloudFormation 之类的基础设施代码解决方案应该用于实验之外的任何事情。

表 1:比较不同的云 API 交互方法

先决条件

AWS 设置

  1. AWS 设置
    -设置自由层 AWS 帐户( 链接)
  2. 使用 root 用户登录 AWS 控制台,并创建 IAM 用户。
    -按照链接中的创建 IAM 用户(控制台)下的步骤操作。为两个程序性 & AWS 管理控制台访问
    创建凭证——在第 6 步(权限,选择“直接附加现有策略”&添加“管理员访问”,并完成其余步骤
  3. 为 EC2
    创建密钥对——确保您位于 AWS 控制台
    中的 us-east-1 (北弗吉尼亚)地区——使用链接中的“创建密钥对”下的说明创建 ec2 密钥对
  4. 安装 AWS CLI — 链接
    —使用“ aws configure ”命令配置 aws-cli。在这里使用上面创建的 IAM 凭据。参见链接寻求帮助。下面的例子:
$ aws configure
AWS Access Key ID [None]: AKIAIOSFODNN7EXAMPLE
AWS Secret Access Key [None]: wJalrXUtnFEMMPLEKEY
Default region name [None]: us-east-1
Default output format [None]: json

5.这将存储在 ~/中。AWS/凭证文件为:

[default]
aws_access_key_id = AKIAIOSFODNN7EXAMPLE
aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
region = us-east-1
format = json

6.使用以下命令测试 AWS CLI 凭据和配置。

aws sts get-caller-identity 

如果对您的 AWS 帐户的身份验证成功,不管您的授权级别是什么,即 IAM 权限,它都应该返回以下输出。

{
    "Account": "123456", 
    "Arn": "arn:aws:sts::123456:assumed-role/role-name/role-session-name",
    "UserId": "AR#####:#####"
} 

GitHub 账户设置&安装

  1. 创建免费 GitHub.com 账户— 加入 GitHub
  2. 这里下载并安装 git 到你的电脑上
  3. 在您的终端/shell 中使用以下命令配置您的用户名和电子邮件。
$ git config — global user.name "Your name here" 
$ git config — global user.email "your_email@example.com"

4.在您的终端/shell 中使用以下命令在您的计算机上创建 SSH 密钥。只需接受默认值并一直按 enter 键,直到生成了 2 个文件(私钥和公钥)(如果您愿意的话)。

$ ssh-keygen -t rsa -C "your_email@example.com"

5.上面的步骤将在/中创建 2 个文件作为 ~/。ssh/id_rsa & ~/。ssh/id_rsa.pub

6.将您的公共 ssh 公钥添加到您的 github 帐户。
-转到您的 github 帐户设置
-单击“ SSH 密钥
-单击“添加 SSH 密钥
-给出一个标签(例如,“我的笔记本电脑”)并将公钥粘贴到文本框
-通过键入以下内容来测试与您的 github.com 帐户的连接:

$ ssh -T git@github.com

它应该是这样的:

Hi <your username>! You've successfully authenticated, but GitHub does not provide shell access.

Vistual Studio 代码和 AWS 工具包

  1. 安装 IDE(如 Visual Studio 代码)https://code.visualstudio.com/
  2. 为 VS 代码安装 AWS 工具包—https://aws.amazon.com/visualstudiocode/

AWS 管理控制台

这是 AWS 的 web 界面。

  1. 转到https://aws.amazon.com/console/
  2. 在 AWS 控制台中,确保您位于 us-east-1(北弗吉尼亚)地区
  3. 从控制台启动 Windows 实例
  4. 遵循https://docs . AWS . Amazon . com/AWS C2/latest/windows guide/EC2 _ get started . html中的步骤
  5. 使用实例的公共 IP 连接(RDP)到您刚刚创建的 Windows 实例。
  6. 确保终止 EC2 实例,以避免充电

图 1:显示运行 ec2 实例细节的屏幕截图

AWS CLI

  1. 使用以下命令从命令行界面启动 EC2 Linux 实例。用您的 ec2 密钥对名称替换
aws ec2 run-instances --image-id ami-08e4e35cccc6189f4 --count 1 --instance-type t2.micro --key-name <Key-Pair-Name> --security-groups default

暂时向所有 IP 开放实例的安全组上的 SSH 端口(请注意,这不是一个好的安全做法)。请参见https://docs . AWS . Amazon . com/AWS C2/latest/user guide/authoring-access-to-an-instance . html的“向 Linux 实例添加入站 SSH 流量规则”一节

2.从控制台使用“ EC2 实例连接连接到实例。

3.确保终止 EC2 实例,以避免充电

图 2:显示创建 ec2 实例的 CLI 命令后终端输出的屏幕截图

交互式开发环境(IDE)

AWS Toolkit for Visual Studio 包括 AWS SDK。既然您在 VS 代码上安装了工具包,我们将在 VS 代码中使用 AWS 扩展来创建 AWS S3。

  1. 打开 Visual Studio 代码
  2. 打开 AWS 浏览器
  3. 右键单击 S3 并创建一个桶
  4. 从控制台验证铲斗

图 3:显示 VS 代码 UI 中 S3 创建链接的屏幕截图

自动气象站云形成

CloudFormation 是 AWS 的 IaC(基础设施即代码)服务,它允许我们建模、创建和管理 AWS 和第三方资源。

  1. 从云形成模板启动 EC2
  2. https://github.com/shrestha-ajay/cloud-basics1.git
  3. 遵循"自述文件中的步骤。MD 文件中的回购或遵循以下步骤
  4. 通过点击 GitHub UI 中的“Fork”来分叉这个库
  5. 通过运行以下命令,将此存储库克隆到您的本地计算机上:
$ git clone git@github.com:YOUR-USERNAME/YOUR-REPOSITORY.git
$ cd YOUR-REPOSITORY-Folder

6.在本地计算机中配置 AWS CLI

7.运行以下命令,在您的 AWS 帐户中创建 ec2 实例和安全组

aws cloudformation deploy \
  --stack-name my-cloudbasic-ec2 \
  --template-file ec2_securitygroup.template \
  --parameter-overrides KeyName=General-key-1

注意:用您的 EC2 密钥对名称替换“General-key-1”密钥名称

8.确保终止 EC2 实例,以避免充电

图 4:显示带有 EC2 实例的 CloudFormation 堆栈的屏幕截图

总结

在本实验中,我们使用四种方式与 AWS 交互来创建云资源(ec2 和 s3):控制台、CLI、IDE 和 CloudFormation。存在一种方法可能优于另一种方法的用例,所以熟悉每种方法是有好处的。

打扫

温和地提醒终止资源,以避免被收取费用。

资源

  1. https://aws.amazon.com/free
  2. https://docs . AWS . Amazon . com/IAM/latest/user guide/id _ users _ create . html
  3. https://docs . AWS . Amazon . com/AWS C2/latest/windows guide/ec2-key-pairs . html
  4. https://AWS . Amazon . com/CLI/
  5. https://docs . AWS . Amazon . com/CLI/latest/user guide/CLI-configure-files . html
  6. http://github.com/signup
  7. https://git-SCM . com/downloads
  8. https://AWS . Amazon . com/SDK-for-python/
  9. https://code . visualstudio . com/
  10. https://AWS . Amazon . com/visualstudio code/
  11. https://AWS . Amazon . com/console/
  12. https://docs . AWS . Amazon . com/awsec 2/latest/windows guide/ec2 _ getstarted . html
  13. https://github . com/sh restha-ajay/cloud-basics 1 . git

云 ML 性能清单

原文:https://towardsdatascience.com/cloud-ml-performance-checklist-caa51e798002

优化基于云的培训的指南

杰克·威瑞克在 Unsplash 上的照片

在云中培训有很多好处。其中包括对多种培训实例类型的可访问性,将培训无限制地扩展到多个实例和多个并行实验的能力,以及丰富的生态系统功能和服务促进了 ML 工作负载。然而,如果管理不当,cloud ML 会产生相当高的成本。虽然主要职责是制定和实施治理政策、监控云服务的使用和成本、与云服务提供商(CSP)协商定价策略等。,可能会落到您组织的云支持团队头上,算法开发人员也有责任尽自己的一份力量来提高效率,减少浪费。下面嵌入推文中的迷因说明了一切。

本文档描述了一个项目清单,我们发现该清单有助于指导算法开发人员提高训练效率,进而降低训练成本。

性能测定

我们的主要性能指标是 每美元的样本数 ,即每花费一美元,训练循环遍历的样本数。 样本每美元 的公式为:

每美元公式示例(按作者)

其中每秒样本数=批量每秒批次数*。培训实例费用可在您的 CSP 网站上找到。

虽然您的目标应该是最大化每美元 的 样本,但有时您需要考虑其他因素。例如,您可能决定将训练分布在多个 GPU 上以加速训练,即使这可能会减少每美元个样本。另一个例子是,最大化每美元的样本会损害训练工作的收敛速度(例如,对批量大小的增加敏感的模型)。

性能优化是一个迭代过程,如下图所示:

性能优化流程(按作者)

这个迭代过程应该伴随项目的整个生命周期。

性能分析工具

有许多分析性能的工具和方法。一些基本指标包括云服务提供商(CSP)报告的资源利用率指标和 TensorBoard profiler 插件

这张图片显示了一个由亚马逊云观察监控工具报告的低 GPU 利用率的例子:

亚马逊 CloudWatch 中的 GPU 利用不足(作者)

TensorBoard profiler 插件是一个强大的工具,支持许多流行的机器学习框架的分析(包括 TensorFlowPyTorchJax )。一开始,这个分析器看起来有点吓人,但是它非常容易设置,并且是识别性能瓶颈的一个非常有用的工具。更多详情参见分析器文档

下图来自 TensorBoard profiler trace viewer,显示了长时间的 GPU 空闲时间,表明训练步骤中存在重大瓶颈。

TensorBoard Profiler 插件显示的性能瓶颈(作者)

性能分析清单

本节包括衡量培训工作当前绩效的基本指南。

  1. 您的培训计划是否包括定期报告每秒培训样本的*均数量
  2. 您目前衡量每美元样品的标准是多少?
  3. 在整个培训过程中,每美元样品测量值是否相对稳定?
  4. 您的培训脚本是否支持启用概要分析的选项,例如通过 TensorFlow 中的 TensorBoard 回调
  5. CSP 的云指标报告的当前*均 GPU 内存利用率是多少?一般来说(但不总是),通过增加训练批量来最大化 GPU 内存的使用是一个好主意。这通常会增加每秒的样本数,因为它降低了固定操作(如内核加载和梯度共享)的成本。在某些情况下,批量大小的选择会对 GPU 内存对齐产生影响,这也会影响性能。
  6. CSP 的云指标报告的当前*均 GPU 利用率是多少?你的目标应该是尽可能地增加这一点。95%的利用率是一个很好的目标。任何低于 80%的数据都意味着你的训练出现了瓶颈,应该会让你夜不能寐。
  7. 其他系统指标,包括 CPU 和网络利用率,可能会对潜在的性能问题提供一些提示,但通常很难从中获得有用的信息。这些指标中是否有任何一项表明可能存在瓶颈?
  8. 评估训练管道性能的一个有用方法是对缓存的输入运行训练循环。通过这样做,您可以有效地隔离出所有的输入流水线计算,并在这种情况下计算每美元 的 个样本。这个数字表示当数据输入管道上没有瓶颈时可以达到的性能。如果有您正在努力解决的瓶颈,这个数字应该被视为您的目标性能。您的脚本支持在缓存的输入上计算每美元样本的选项吗?在 Tensorflow 中,缓存输入可以通过应用: dataset = dataset.take(1)轻松完成。缓存()。在数据集创建结束时重复()(预取之前)。请注意,通过在数据集的其他阶段应用缓存,可以获得对 CPU 争用的潜在来源的更多见解,并且应该在分析输入管道上的瓶颈时使用。
  9. TensorBoard profiler 是一个非常有用的性能分析工具。让它成为你发展周期的一个常规部分是很重要的。对其功能的全面概述超出了本文档的范围,但是强烈建议(并没有您想象的那么困难)学习如何使用它。您运行(并分析)分析器了吗?
  10. 查看探查器摘要报告。它是否报告了输入管道瓶颈?(请注意,如果在“内核加载”上花费了大量时间,这可能表明 CPU 争用率很高,并且可能表明存在 CPU 瓶颈。)
  11. 打开跟踪查看器。有没有什么麻烦的模式比如 GPU 长时间闲置或者频繁的 CPU 到 GPU 的拷贝?
  12. 查看十大 TensorFlow GPU 操作。它们是你所期望的吗?这些有时可以暗示对模型的潜在优化。

实例选择

根据您选择的培训实例,每美元的样本量可能会有很大差异。选择错误的训练实例会对成本产生可怕的后果。一般来说,新一代的实例(比如 Amazon EC2 G5 和 p4 实例类型)包含了提高性能的硬件增强。另一个考虑因素是 CPU 与 GPU 计算能力的比率。较低的比率可能会增加对 CPU 资源的争用,并导致 CPU 瓶颈。

  1. 您目前的培训情况如何?你考虑过其他选择吗?如果你使用的是老一代 GPU,比如 Amazon EC2 p2 或 p3,你应该有一个很好的理由。
  2. 替代(非 GPU)加速器,如亚马逊 EC2 dl1 (基于哈瓦那高迪)和谷歌云 TPU (在 GCP)通常(但不总是)提供比 GPU 更高的性价比。然而,它们有时需要对这些加速器进行一些调整。你有没有尝试过在一个专门的 DNN 培训加速器上运行你的模型?使用这些替代品有什么障碍?

培训优化

下图显示了典型训练步骤的各个阶段。每一步都是性能瓶颈的潜在来源:

典型训练步骤的步骤(作者)

存储到 CPU

训练步骤的第一阶段是将原始训练样本加载到 CPU 上。存储数据的方式需要特别注意,这样这个阶段才不会成为瓶颈。如果您的数据存储在远程(云)对象存储中,比如亚马逊 S3,这一点尤其正确。实例类型网络入站容量、S3 出站容量或 CPU 争用的限制可能会导致数据匮乏。训练速度可能会受到您选择的数据格式(例如顺序与列格式)、数据文件的大小、用于流式传输数据的方法(管道模式、FFM、直接下载等)的影响。)、数据压缩的使用等。

分析数据流速度的一种方法是直接迭代数据集,测量每美元的 样本 ,而不运行训练步骤。理想情况下,结果将明显高于使用训练步骤跑步时的结果。

  1. 您的文件大小是否在 100 MB 左右?
  2. 如果你在使用 Amazon SageMaker,你会利用 FFM 或 PipeMode 来传输你的数据吗?
  3. 在没有训练的情况下迭代数据集时, 每美元样本数 是多少?
  4. 在输入数据管道的开始应用数据缓存时,每美元的 样本 是什么?如果它高于基准值,这可能表明有问题。
  5. 使用数据压缩可以减少网络带宽需求,也可以降低存储成本。另一方面,数据解压缩可能会增加 CPU 争用。你是否研究过数据压缩对你训练速度的潜在影响?

CPU 瓶颈

GPU 利用率不足的最常见原因之一是 CPU 上的瓶颈。如果由于 CPU 争用而出现瓶颈,可以考虑以下选项:

  1. 您是否使用了基本的并行化技术,比如批处理预取num_parallel_calls
  2. 有没有可以转移到数据准备阶段的计算?
  3. 有没有可以移到 GPU 上的计算层?请注意,这将增加整体 GPU 计算,但可能会导致步骤时间缩短。
  4. 将一些输入数据预处理卸载到辅助 CPU 会有帮助吗?请注意,需要考虑很多因素,例如总体成本、对网络数据流量的影响等。
  5. 您是否探索过优化 CPU 操作的选项(例如使用 XLA )?

提高 GPU 利用率

即使您的 GPU 得到了充分利用,您也可以采取一些措施来提高其性能:

  1. 使用混合精密浮子。TensorFlow 和 PyTorch 的当前版本支持 16 位精度浮点。当使用 TensorFlow 混合精度时,模型参数存储在全精度浮点表示中,而算术运算用低精度浮点执行。使用混合精度可以大幅降低 GPU 内存的使用(从而增加批量大小)并增加每美元的样本数。有两种类型的 16 位浮点表示:float16 和 bfloat16。Bfloat16 是首选,因为它的动态范围与 float32 相似。Bfloat16 由现代 GPU(例如 A100 和 A10)、云 TPU 和 Habana Gaudi 支持。如果您的实例支持 bfloat16,那么您可以启用混合精度,而不会对您的模型收敛产生任何影响。对于 float16,事情会变得更棘手,因为你可能需要使用渐变缩放技术来确保收敛。你的实例支持 bfloat16 吗?你在使用混合精度吗?
  2. XLA 是一个编译器,它通过融合单个操作来优化 GPU 计算。在许多情况下,启用 XLA 将导致每美元的样本增加。你在用 XLA 吗?对性能有什么影响?
  3. 有时,您的计算图可能包含加速器不支持的操作。在这种情况下,操作可能会卸载到您的 CPU。这会大大降低你的训练速度,应该避免。在 TensorFlow 中,可以使用TF . debugging . set _ log _ device _ placement来识别这种情况。这种情况有时也可以在 TensorBoard profiler trace viewer 中发现,在 GPU 计算过程中,您会看到主机和设备之间频繁的内存复制。你确保所有的运算都在 GPU 上运行了吗?
  4. DNN 专用加速器,如云 TPU 和 HPU (Habana Gaudi)可以增加每美元的样本数,但可能需要一些改造才能获得最佳性能。例如,TPU 和 HPU 在具有动态形状(例如 boolean_mask)的张量上表现不佳。您的模型是否针对您正在训练的加速器进行了优化?
  5. 您是否正在为您的任何模型层使用 tf.py_function ?如果是这样,您可能需要考虑其他性能替代方案。
  6. 记忆格式会影响训练效果。例如,参见这篇文章关于将你的内存格式编程到底层加速器的重要性。你用的是什么内存格式?你的选择对你的培训资源来说是最优的吗?

多 GPU 训练

多 GPU 训练是一种用于提高整体开发速度的常用技术。使用多个 GPU 的一种常见方法是执行数据分布式训练,在这种训练中,我们将全局批量大小划分到多个 GPU 上,每个 GPU 维护一个相同的模型副本。通过在每个训练步骤结束时共享梯度来保持同一性。理想情况下,您的培训绩效将呈线性增长,即每美元的样本数不会减少。然而,通常情况下,梯度共享,尤其是它所暗示的增加的网络通信流量,会降低每 美元样本。但是,有多种方法可以缓解这种情况:

  1. 你的多 GPU 训练的每美元的样本是多少?与单 GPU 每美元采样相比如何?
  2. 一般来说,比起多个实例,您应该总是更喜欢具有多个 GPU 的单个实例。是这样吗?
  3. CSP 通常包括专门的网络接口,可以提高节点间网络通信的性能(例如亚马逊 EFA谷歌 FastSocket )。你在多种情况下训练吗?您选择的实例支持加速的节点间网络通信吗?
  4. 你有没有探索替代的梯度分享策略?共享渐变的常见/默认策略是环全减。你可能会发现你的模型受益于替代策略,比如鲱鱼。
  5. 现代梯度共享策略将把梯度分块,并把它们并行分布到后退步骤。如果 TensorBoard profiler 跟踪查看器显示所有的设备到设备通信在该步骤结束时聚集在一起,这可能表明存在问题。
  6. 梯度共享 API 将包括一个压缩梯度的选项,以减少网络流量。你考虑过这个选择吗?
  7. 由于多 gpu 旨在加速培训,并且您可能不太关心评估时间,因此您可能希望考虑将评估转移到单独的单个 GPU 实例。

项目优化

上一节重点介绍了对单个培训作业的优化。这里,我们建议在项目级别进行优化:

  1. 你的项目包括自动提前停止失败实验的回调吗?
  2. 您是否使用自动化超参数调整技术?
  3. 您的开发流程包括删除未使用的存储吗?
  4. 你在用的工具(TensorBoard,comet.ai,clearML,Neptune 等。)来管理你的实验?高效的实验管理可以增加订单并减少重复。

摘要

本页分享的列表绝非包罗万象。自然要适应自己项目的具体需求。请随时提出问题/评论/更正,特别是您认为应该包括的其他项目。

聚类—对数据科学家来说是一项艰巨的任务

原文:https://towardsdatascience.com/clustering-a-daunting-task-for-a-data-scientist-5c7f362ccfb8

设计群集模型的准则

照片由 Unsplash 上的尼克·费因斯拍摄

考虑这样一种情况,一个政党邀请您(一名数据科学家)分析人口数据,以帮助他们赢得即将到来的选举。你的任务是找出全国人口中支持该党的群体。有了这些信息,党就可以在不同的地区策划他们的运动。这是在大规模数据集上聚类(形成组)的经典例子之一。还有许多其他情况下数据集很小,主要是因为数据隐私和收集数据本身的方式。

无论您拥有的是小数据还是大数据,即使对于经验丰富的数据科学家来说,聚类也始终是一项挑战。这主要是因为“集群”本身的概念没有精确的定义。一个数据科学家可能在一个数据集中找到三个聚类,而另一个数据科学家可能将同一个数据集聚类成四个区域。通常,数据科学家之间可能不会就此达成共识;然而,最终的决定通常是由数据集的利益相关者做出的。正是他们对数据的知识和理解最终满足了他们的需求。

那么,数据科学家如何满足客户的要求呢?

数据科学家的方法

对于数据科学家来说,解决聚类问题的第一个也是最重要的方面是检查数据集的大小。根据大小,数据科学家选择合适的聚类模型和算法。一段时间以来,研究人员开发了许多满足各种数据大小的特定需求的聚类算法。对于小到中等大小的数据集,我们使用简单的基于连通性的聚类算法;而对于具有多元分布的非常大的数据集,必须使用基于非常先进的统计技术的算法。选择聚类算法是数据科学家的知识和技能。在本文中,我将描述数据科学家解决这个重要问题的方法。为了理解这种方法,让我们简要地考虑一下聚类类别和每一类别中的各种算法。

聚类类别

聚类算法大致分类如下:

  • 基于质心的
  • 基于连通性
  • 基于分布的
  • 基于密度
  • 聚类巨大的数据集
  • 八卦式聚类
  • 基于网格的

我现在将向您介绍这些类别,并简要描述每个类别中的算法。

基于质心的

k-meansk-medoids 算法就属于这一类。这些可能是最容易理解的,并且通常是学习集群的起点。在这里,您需要在运行算法之前指定想要形成的聚类数。有一些可用的技术,如*均轮廓间隙统计,用于选择集群形成的最佳数量。这种情况下的优化问题是 NP 难的,因此它通常只提供*似解。该模型非常适合中小型数据集。

基于连通性

凝聚分裂聚类算法就属于这一类。这些算法基于这样的概念,即根据对象之间的距离来连接对象。他们为整个数据集创建一个树状图或层次结构。这种可视化表示有助于您识别集群。聚类主要取决于距离函数的类型和连锁准则的选择。我们在专门的应用中使用这种类型的聚类,如确定动物进化的系统进化树、通过绘制系统进化树来跟踪病毒、通过分析文本相似性来分类文档等等。如果您希望在确定聚类时获得数据集的分层表示,可以使用这些算法。

基于分布的

高斯混合模型 (GMM)就是属于这一类的一种算法。这里,我们假设利益相关者对其数据集的统计分布有所了解。由于在数据分布中存在强假设,如果假设出错,算法将产生不利的聚类。因此,仅当您确定数据集的统计分布时,才使用此类算法,在这种情况下,该算法将产生出色的聚类结果。

基于密度

DBSCAN 算法就属于这一类。某个区域中数据点的高密度定义了群集。密度的下降标志着星团的边界。对于像高斯混合这样的分布,这种算法非常有效。这种类型的聚类的最重要的方面是它可以创建任意形状的聚类,而前面讨论的算法大多产生圆形聚类。第二,算法复杂度相当低,并且输出是确定的——这意味着它们在每次运行中发现相同的结果。

光学是属于同一类别的另一种算法。它推广了 DBSCAN。该算法还产生链接可视化,这有助于数据科学家创建他选择的集群。

Mean-Shift 算法是这一类别中的另一种算法,其中我们基于核密度估计(KDE)来形成聚类。该算法可以检测任意形状的簇,但比 DBSCAN 慢。此外,它不能在多维数据集上产生良好的聚类。

当数据集很大时,数据科学家使用基于密度的聚类。

庞大的数据集

你如何聚集巨大规模的数据集?你使用了一个众所周知的策略——分而治之。T4 桦树 T5 算法就属于这一类。我们将数据集分成更小的单元,在每次分割中尽可能多地保留原始数据集的信息。然后,我们使用其他已知的聚类技术对每个分割单元进行聚类。这种技术的优势在于,它消除了将整个数据集加载到内存中的需要。

CLARANS 是这一类别中的另一种算法,它在对数据集进行聚类时使用随机搜索。然后,它对每个单元应用 PAM(围绕 Medoids 划分)算法来确定其聚类质量。如果不好,它重复整个聚类过程。因此,它保持了计算成本和数据随机采样之间的*衡。它允许多边形物体和可以使用任何任意距离测量功能。它提供了高质量的聚类。

八卦式聚类

也被称为相似性传播聚类,这里我们通过对等体之间的闲聊来形成聚类。这就像社交网络。我们根据成员和他们的领导之间的某种亲密关系来组建团体。同伴消息传递和一个人成为小组领导的意愿决定了亲和力度量。你不需要预先估计集群的数量。该算法肯定会产生非常好的结果,尤其是当您不知道数据集可能具有的聚类数时。该算法计算量很大。

基于网格的

到目前为止,我们考虑的所有上述算法都是基于查询的。如果您决定尝试另一个查询,您将需要再次扫描整个数据集。因此,所有这些聚类技术的计算量都很大。

STING 算法就属于这一类。最初,我们将整个数据集分割成矩形单元,层次结构中的每个较高级别的单元包含较低级别的单元集合的摘要。由于该结构是独立于查询的,因此我们可以并行化整个集群过程,从而节省集群时间。不仅如此,由于其性质,它可以处理增量更新,这是动态数据集中的一个要求。另一个优点是该算法的低复杂度。

CLIQUE 是这一类的另一个算法。它结合了基于密度和网格的聚类技术。该算法从一维子空间开始,不断融合计算高维子空间。它使用一种先验技术来寻找可聚类的子空间,非常适合聚类高维数据集。

结束语

到目前为止,我已经讨论了许多聚类类别,这些类别可以帮助您根据数据集大小和客户对聚类的期望来决定使用哪种算法。我在这篇小文章中介绍的概述可以帮助您选择一种算法来对小型、中型、大型甚至空间数据集进行聚类。

聚类通常是非*凡的,因为它的无监督的性质和事实上可能没有对最终结果的共识。作为一名数据科学家,您应该对这些不同的聚类算法、构建它们的目的以及它们所解决的聚类问题有一个很好的概念性概述。几个库提供了这些算法的有效实现,作为一名数据科学家,你不必担心它们背后的数学。只要了解每一类算法的用途,你就能解决任何聚类问题。

要了解更多信息,你可能想参考我即将出版的书,思考数据科学(应用机器学习中的斯普林格系列)。它有一大部分是关于聚类的,有关于所有算法的实际例子。

https://medium.com/@profsarang/membership

聚类算法基础及其 Python 实现

原文:https://towardsdatascience.com/clustering-algorithm-fundamentals-and-an-implementation-in-python-31a482592b04

创建包含相似元素的数据组的无监督过程

伊恩·杜利在 Unsplash 上拍摄的照片

什么是集群?

聚类是一种可以通过创建有意义的组或簇来帮助机器学习工程师理解未标记数据的方法。这通常会揭示数据中的模式,这可能是机器学习中有用的第一步。由于您正在处理的数据是未标记的,聚类是一项无监督的机器学习任务。

通过一个称为相似性度量的度量标准,数据根据彼此的相似性被分类成组,其中用于找出数据集中对象的相似程度。为了计算这种相似性度量,使用数据集中对象的特征数据。为每个集群提供一个集群 ID** ,这是一个强大的集群应用。这样可以简化大型数据集,还可以将对象的整个特征集压缩到其分类 ID 中。**

这一原则的一个简单现实例子是收集关于家庭规模和家庭收入的数据,以创建用户群,如小家庭高消费群、小家庭低消费群、大家庭高消费群和大家庭低消费群。

聚类的用途

如今,集群被广泛应用于行业中的各种用例。其中包括搜索结果分组、社交网络分析和市场细分。聚类也用于图像分割、异常检测和医学成像。

扩展上面提到的集群 id 的优点,集群可以用于根据不同的特征对对象进行分组。例如,星星可以根据亮度分组,音乐可以根据流派分组。

在像 Google 这样的组织中,集群用于:

  • 概化:当集群中的对象缺少要素数据时,可以从集群中的其他对象推断出它们。
  • 数据压缩:特征数据可以完全由集群 ID 代替。这节省了存储空间并简化了特征空间。这有助于使 ML 模型训练更简单、更快速。
  • 保护隐私:将用户分组并将他们的数据与集群 id 相关联可以防止将用户数据与特定用户相关联,从而确保用户隐私。

聚类分析的结果。来源:hellisp,公共领域,via Wikimedia Commons

聚类算法

现在我们已经了解了聚类的概念,让我们看看一些常见的聚类算法。关于详尽的清单,你可以参考这篇论文

分层聚类

这种方法适用于分层数据,它创建了一个聚类树。对于大多数数据集来说,标准算法太慢,因为它的时间复杂度为 O(n ),内存需求为ω(n)。然而,运行时间的减少是以内存需求为代价的,尽管内存开销在大多数情况下很难实际使用。

Orange 数据挖掘套件中的层次聚类和交互式树状图可视化。blazupan(橙色数据挖掘)CC BY-SA 4.0 ,通过维基共享

基于分布的聚类

在这些算法中,假设数据属于不同的分布。然后,集群被定义为那些包含相似分布的对象的集群。一个缺点是基于分布的聚类容易过度拟合。因此,必须对模型的复杂性加以限制。

下图显示了一个示例,其中数据被聚类为三个高斯分布。较暗的颜色更接*分布的中心,条带显示数据属于某个分类的概率强度。随着到中心的距离增加,数据属于该聚类的可能性将降低。

如果您没有关于数据集分布类型的信息,此算法可能不是最好的。

基于高斯分布的聚类。由 Chire-Own 工作, CC BY-SA 3.0 ,通过维基共享

基于密度的聚类

这些算法通过连接包含高密度对象的区域来创建聚类。它要求密集区域是可连接的,并且根据设计,离群值不会被分配给聚类。一个缺点是基于密度的聚类算法在处理更高维度以及具有不同密度的数据时面临困难。

使用 DBSCAN 算法对基于密度的数据集进行聚类分析。 ChireCC BY-SA 3.0 ,通过维基共享

基于质心的聚类

这种形式的聚类将数据分组到非分层分区中。虽然这些类型的算法是有效的,但它们对初始条件和异常值很敏感。最常用的基于质心的算法称为 k-means,其中 k 是定义聚类数量的超参数。

K-means 提供了一些优势,例如扩展到大型数据集的能力、易于实现以及适应新数据。另一方面, k 值必须费些力气手动找到,并且质心会被离群值拖动。考虑在聚类之前移除离群值是有益的。

给定一组 n 个数据点,k-means 算法的目标是将它们分成 k 个组,其中每个组包含相似的数据点。为了做到这一点,我们首先需要选择一个数字 k,然后我们开始随机分配每个点到它最*的聚类中心。接下来,计算每个数据点与其指定中心之间的距离。然后,我们重复上述步骤,直到没有进一步的变化发生。一旦我们完成了距离和中心的计算,我们返回到步骤 1 并重新计算聚类。这种情况一直持续到集群没有变化。此时,我们知道我们的集群是稳定的。

k 均值算法的实现

现在,让我们实现上面讨论的算法之一,并可视化产生的集群。为此,我们将使用 k-means 算法和 scikit-learn。该代码受 scikit-learn examples 提供的手写数字数据 K-Means 聚类演示的启发,并包含其中的代码。

实现 k 均值。包含来自 scikit-learn 示例 ( BSD 许可证)的代码

输出如下图所示。

结果图是从上面的代码中产生的。

除了 k-means 算法之外,scikit-learn 库还提供了其他几种算法,可以根据您所拥有的数据来使用。这些算法包括:

  • 亲和传播
  • 凝聚聚类
  • 桦树
  • 基于密度的噪声应用空间聚类
  • k 均值
  • 小批量 K 均值
  • 光学
  • 谱聚类
  • 高斯混合

请记住,没有固定的算法可以提供最好的结果。您必须运行受控实验,以确定最适合您正在处理的数据集的算法。

如果您想更深入地研究所提供的算法,scikit-learn 集群 API 是一个很好的起点。

结论

在本文中,我们研究了集群、其用途以及一些常用的集群算法类型。我们还研究了它们的优缺点,以及一些算法与其他算法相比的闪光点。最后,我们看了一个如何进行 k 均值聚类的编码示例。我希望这些信息对你有用。请在下面的评论区告诉我你的想法和问题。

基于聚类的运行风力发电机数据预处理

原文:https://towardsdatascience.com/clustering-based-data-preprocessing-for-operational-wind-turbines-268e231d90a

创建资产组的有效数据科学方法

左:涡轮集群(图片由作者提供)。右图:照片由菲利普·梅(Philip May)通过维基媒体提供

介绍

运行风力涡轮机产生数据流,同时产生清洁和可再生的电力供我们日常使用。数据是环境、机械和生产变量的时间序列,使用监控和数据采集(SCADA)系统获得。

风能分析通常需要对 SCADA 数据进行预处理,包括识别哪些涡轮机可被视为“邻居”。其中邻居的概念取决于感兴趣的变量,例如涡轮机位置、风速、风向和功率输出。

例如,在地理上,如果两个或更多涡轮机的纬度和经度与其余涡轮机相比彼此更*,则两个或更多涡轮机可以被认为是邻居。从技术上讲,如果两个涡轮机在调查期间经历相似的风速,也可以基于风速将它们分组为邻居。

风力涡轮机集群的应用

对风电场中的涡轮机进行分组是一个有用的数据预处理步骤,需要相对频繁地执行,并且对于非地理变量,涡轮机之间的关系可能会随着时间而改变。风力涡轮机分组的一些有用应用包括:

  • 处理缺失和虚假数据:识别代表给定涡轮机的一组涡轮机提供了用邻居传感器数据的*均值回填缺失或虚假数据的有效方式。这对于风速等变量尤其有用,因为风速计的可靠性相对较低。
  • 并排分析:在该分析中,控制涡轮机通常根据产生的功率被选为试验涡轮机的代表。在涡轮机升级结束时,有必要通过将其生产与相邻涡轮机的生产进行比较来测量试验涡轮机的性能改进。在这种情况下,使用基于聚类的方法需要进一步探索,并与现有方法进行比较。
  • 组功率预测:为了降低计算成本,功率预测模型可以针对风电场中的涡轮机组而不是针对单个涡轮机来构建。此外,与为整个农场建立单一模型相比,这种方法有望提供更准确的结果。
  • 组偏航控制:通过对一组涡轮机实施最佳偏航控制策略,可以提高风力发电场的发电量,这些涡轮机在相对于风向的位置上是相邻的,而不是单独的。

虽然聚类技术已经用于风能分析的不同领域,如风电场功率预测和偏航控制,但本文建议将这种方法扩展到其他应用,如并排分析和处理缺失或虚假的 SCADA 数据。

基于聚类的 SCADA 数据分析

涡轮机分组的一种方法包括计算一台涡轮机和农场中其他涡轮机的测量值之间的*方差之和(SSD)。其中选择具有最小 SSD 的涡轮机作为邻居。这种方法在计算上非常昂贵,尤其是如果由非编程专家编写脚本的话。

对风电场中的涡轮机进行分组的另一种方法是基于感兴趣的变量使用不同涡轮机之间的相关系数。这种方法很简单,计算量也不大,但是对于处理缺失值这样的应用可能没什么用。

基于聚类的方法采用现有的公开可用的最新数据科学技术和工具。这种方法中最流行的是 K 均值聚类。该方法确保一个组中的涡轮机不仅具有最小的组内*方和,而且与其他组中的涡轮机具有最大的组间*方和。

虽然 K-Means 聚类类似于 SSD 方法,但它也针对聚类间的不相似性进行了优化,这有望提高其健壮性。此外,该方法适用于比相关系数法更广泛的风力涡轮机分析,并且可以优雅地处理多个变量。因此,这种方法对 SCADA 数据分析更有效。

此外,聚类技术在数据科学中得到了很好的研究,可以很容易地在几行代码中实现。可以探索的其他聚类方法包括分级、谱和基于密度的聚类方法。

案例研究:

使用聚类统计识别涡轮机组并处理缺失的风速数据。

在本例中,我们展示了一种高效而简单的数据科学方法,使用来自 Sklearn 库的 K-Means 聚类技术在风力发电场中创建涡轮机组。我们还比较了使用获得的聚类预测缺失风速数据的两种方法。

首先,让我们导入相关的库

# Import relevant libraries
import os
os.environ['OMP_NUM_THREADS'] = "1"import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as snsfrom sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import mean_absolute_error, mean_absolute_percentage_errorfrom scada_data_analysis.modules.power_curve_preprocessing import PowerCurveFiltering

数据

数据集在 Kaggle 上公开,并在必要的引用下使用。它由 134 个运行中的涡轮机组成,运行时间超过 245 天,分辨率为 10 分钟。还提供了每个单位的位置数据。数据加载如下:

# Load data
df_scada = pd.read_csv('wtbdata_245days.csv.zip')
df_loc = pd.read_csv('sdwpf_baidukddcup2022_turb_location.csv')

数据探索

让我们通过检查数据集的头部来确保数据被正确读取。

作者图片

作者图片

原始 SCADA 数据包括 470 万行和 13 列。

数据清理

让我们提取相关列,即涡轮机唯一标识(混浊)、天(日)、时间戳(Tmstamp)、风速(Wspd)、风向(Wdir)、机舱方向(Ndir)和功率输出(Patv)。

# Extract desired features
dff_scada = df_scada[['TurbID',  'Day', 'Tmstamp', 'Wspd', 'Wdir', 'Ndir', 'Patv']]

现在,让我们检查丢失的值并删除受影响的行。在移除缺失值之前,数据质量如下所示:

作者图片

由于缺少值,总共只有 1.05%的行被删除,新的数据质量显示如下:

作者图片

接下来,我们创建一个唯一的日期时间字符串,用于根据需要创建变量的时间序列。

dff_scada['date_time'] = dff_scada[['Day', 'Tmstamp']].apply(lambda x: str(x[0]) + 'T' +str(x[1]), axis=1)

数据过滤

原始 SCADA 数据可能相当混乱,需要过滤以提取每个涡轮机的典型操作行为。我们将使用开源的 scada 数据分析库,它有一个功率曲线过滤工具用于这一步。这个库的 GitHub 库可以在这里找到。

作者图片

用于数据过滤的代码和由此产生的干净的 scada 数据如下所示。首先,我们基于领域知识移除虚假数据。

# set parameters
cut_in_Speed = 2.85# Initial filtering of obviously spurious data
ab_ind = dff_scada[(dff_scada['Wspd'] < cut_in_speed) & (dff_scada['Patv'] > 100)].indexnorm_ind = list(set(dff_scada.index).difference(set(ab_ind)))assert len(dff_scada) == len(norm_ind) + len(ab_ind)scada_data = dff_scada.loc[norm_ind, :]

然后,我们使用功率曲线滤波器来去除异常运行数据。

# Instantiate the Power Curve Filter
pc_filter = PowerCurveFiltering(turbine_label='TurbID',                                     
windspeed_label='Wspd', power_label='Patv', data=scada_data, cut_in_speed=cut_in_speed, bin_interval=0.5, z_coeff=1.5, filter_cycle=15, return_fig=False)# Run the data filtering module
cleaned_scada_df, _ = pc_filter.process()

作者图片

清理后的数据有 190 万行,更能代表运行中的风力涡轮机的风速和功率输出之间的预期关系。

现在,我们创建测试数据,用于评估预测缺失值时聚类方法的性能。测试数据是从清理后的数据中随机抽取的,并且具有与原始数据集中相似的流行率(1.05 %)。

作者图片

数据转换

在这一步中,我们为每个期望的变量转换过滤的数据,这使它为聚类分析做好准备。风速的转换数据如下所示:

作者图片

聚类建模

我们希望根据风速、风向、机舱方向和功率输出对涡轮机进行分类。其思想是识别哪组涡轮机可以被认为是相邻的,并用于给定涡轮机的统计表示。

在这个例子中,我们使用 KMeans 算法。最佳聚类数的选择是建立模型的关键,为此采用了流行的肘方法。所有情况下的弯管图如下所示:

作者图片

尽管使用 4 或 5 个聚类也给出了合理的结果,但是基于单个和组合特征选择的聚类的最佳数量是 3。

我们使用 Sklearn 预处理模块中的标准缩放工具来缩放所有特征的输入数据,因为它们具有不同的数量级。

结果

我们创建了一个基于每个变量和所有变量组合的聚类模型,并确定了这些情况下的涡轮机组。结果如下所示:

作者图片

在上面的结果中,风速和风向涡轮机集群显示出相似的模式,其中第一组中的涡轮机位于公园的边缘,这是有意义的,因为两个位置的障碍物水*都降低了,特别是如果主导风向是沿着 X 轴。此外,第 2 组和第 3 组位于公园的中间,横跨柱子(沿 X 轴)。

在功率输出聚类结果中,第 1 组涡轮机位于 X 轴的右侧,边界清晰。第 2 组涡轮机位于公园的中间,沿着 X 轴,而第 3 组涡轮机是最大的一组,主要占据公园的边缘。

机舱方向不同于其他变量,因为它很大程度上取决于应用于涡轮机的偏航逻辑,并且可能在整个现场发生变化。因此,集群没有明确的边界。然而,当结合生产数据时,这种分析可能有助于排除与偏航不对准相关的性能不佳。

使用所有特征组合的聚类结果类似于风速聚类,并显示在文章标题图片中。

基于聚类的缺失值插补方法

这里,我们将探讨两种基于聚类的方法来处理基于风速数据的缺失值,即朴素聚类(NC)和列敏感聚类(CSC)。这两种方法都是作者直观命名的。

朴素聚类

在这种方法中,给定涡轮机的缺失值被涡轮机在期望的时间戳所属的组的*均值(或中值)代替。我们将使用*均聚类值进行分析。

对列敏感的聚类

该方法通过仅取相同组和列中的涡轮机的*均值,扩展了朴素聚类方法。这考虑了涡轮机的地理位置的影响,并且对于诸如风速之类的变量可能尤其更准确。

聚类模型评估

测试数据由 19,940 行组成,包含使用聚类方法预测的缺失风速数据的地面实况。

基于训练聚类数据中的可用数据,简单聚类方法可以填充 99.7%的缺失值,而当聚类被进一步分箱成列时,由于较少的训练数据,列敏感方法只能填充 93.7%。

这两种方法都使用*均绝对误差(MAE)指标进行评估,该指标基于它们可以预测的缺失值。此外,*均绝对百分比误差(MAPE)度量用于评估非零预测。对于这两个指标,模型性能越小越好。

SCS 方法比基于 MAE 和 MAPE 的 NC 方法分别提高了 2%和 8%。但是,它会填充更少的缺失值。因此,两种方法的互补使用将提供更大的好处。

为了对结果进行直观的比较,我们从测试数据中随机抽取了 100 个点,如下所示:

作者图片

接下来,我们设想两种方法的缺失值插补误差。插补误差是地面真实风速值和预测值之间的差值。

作者图片

两种方法都有良好的插补误差,插补误差关于零误差位置是对称的。

结论

在本文中,我们使用不同的单独变量和组合变量执行了基于聚类的 SCADA 数据预处理。此外,我们使用简单的和列敏感的聚类方法预测缺失的风速数据。最后,我们从分析中推断,这两种方法可以互补以获得更大的利益。

我希望你喜欢阅读这篇文章,直到下次。干杯!

什么更有趣?你可以通过我下面的推荐链接订阅 Medium 来获得更多我和其他作者的启发性文章,这也支持我的写作。

https://aolaoye.medium.com/membership

不要忘了查看在可再生能源领域应用最新数据科学原理的其他故事。

参考文献

周军,陆,徐,肖,苏,吕军军,马,窦,(2022)。SD wpf:2022 年 KDD 杯空间动态风力预测挑战数据集。 arXivhttps://doi.org/10.48550/arXiv.2208.04360

风能分析工具箱:迭代功率曲线滤波器

聚类备忘单

原文:https://towardsdatascience.com/clustering-cheat-sheet-dcf72259abb6

在 11 分钟内你应该知道的关于聚类的所有事情

聚类。作者图片

在本文中,您将找到一个完整的集群备忘单。在 11 分钟内,你将能够知道它是什么,并刷新你对主要算法的记忆。

聚类(也称为聚类分析)是将相似的实例分组到聚类中的任务。更正式地说,聚类是将未标记数据点的群体分组为聚类的任务,其方式是同一聚类中的数据点彼此之间比其他聚类中的数据点更相似。

聚类任务可能是无监督学习中最重要的,因为它有许多应用,例如:

  • 数据分析:一个庞大的数据集往往包含几个大的聚类,分别对其进行分析,可以得出有趣的见解;
  • 异常检测:如前所述,位于低密度区域的数据点可视为异常;
  • 半监督学习:聚类方法通常可以帮助你为分类任务自动标记部分标记的数据;
  • 间接聚类任务(聚类有助于获得良好结果的任务):推荐系统、搜索引擎等。,以及
  • 直接聚类任务:客户细分、图像细分等。

Scikit Learn 的聚类算法。图像来源

所有的聚类算法都需要数据预处理和标准化。大多数聚类算法在大量特征的情况下表现较差,因此有时建议在聚类之前使用维度缩减的方法。

k 均值

K-Means 算法基于质心概念。质心是聚类的几何中心(所有聚类点的坐标的*均值)。首先,随机初始化质心(这是基本选项,但还有其他初始化技术)。之后,当质心移动时,我们迭代执行以下两个步骤:

  • 更新聚类——为每个数据点分配一个质心最*的聚类数,以及
  • 更新聚类的质心 —计算聚类元素的新*均值以移动质心。

K-均值算法的收敛性。公共领域

算法的优点和缺点是直观的。它简单而快速,但需要关于聚类数的初始知识。它也不能很好地检测复杂形状的聚类,并且可能导致局部解决方案。为了选择好的聚类数目,我们可以使用从数据点到聚类质心的距离*方和作为度量,并选择该度量停止快速下降时的数目( 肘方法 )。为了找到全局最优解,可以多次运行算法,选择最佳结果( sklearn 中的n_init参数)。

这个算法的一个加速版本是小批量 K-Means 。在这种情况下,我们使用随机子样本而不是整个数据集进行计算。还有很多其他的修改,很多都是在 sklearn 中实现的。

优点:

  • 简单直观;
  • 扩展到大型数据集;
  • 因此,我们也有质心,可以用来作为标准的集群代表。

缺点:

  • 关于群集数量的知识是必要的,并且必须作为参数来指定;
  • 不能很好地处理大量的特征;
  • 仅很好地分离凸的和均匀的簇;
  • 可能导致较差的本地解决方案,因此需要运行几次。

分层聚类

分层聚类(也称为分层聚类分析(HCA)凝聚聚类)是一系列聚类算法,在分析过程中构建聚类的层次结构。

它被表示为一个树状图(一个树形图)。树的(通常是上部或左侧元素)是一个包含所有数据点的大型集群。(底部或右侧元素)是微小的簇,每个簇只包含一个数据点。根据生成的树状图,你可以选择所需的分离成任意数量的聚类。

iris 数据集上的层次聚类树状图示例。改编自公共领域

这类算法需要计算聚类之间的距离。为此使用了不同的度量标准(简单关联、完全关联、*均关联、质心关联等)。),但其中最有效也最受欢迎的是沃德距离或沃德联动。

要了解更多关于测量聚类之间距离的不同方法,请阅读本文:

https://dataaspirant.com/hierarchical-clustering-algorithm/#:~:text=creates meaningful clusters.-,Difference ways to measure the distance between two clusters,-There are several

优点:

  • 简单直观;
  • 当数据具有层次结构时效果很好。
  • 不需要知道集群的数量。

缺点:

  • 需要额外的分析来选择聚类的结果数量;
  • 仅很好地分离凸的和均匀的簇;
  • 贪婪算法可能会导致较差的局部解。

谱聚类

谱聚类方法基于图论和线性代数。该算法使用相似度矩阵(包含每对数据点的相似度)的(一组特征值)进行降维。然后它使用这个低维空间中的一些聚类算法(sklearn.cluster.SpectralClustering类使用 K-Means)。

由于维数降低,该算法可以检测复杂的聚类结构和形状。它还可以用于在图形中搜索聚类。然而,由于其计算复杂性,它不能很好地处理大型数据集。

优点:

  • 可以检测复杂的团簇结构和形状;
  • 可用于在图形中搜索聚类。

缺点:

  • 关于群集数量的知识是必要的,并且必须作为参数来指定;
  • 不能很好地处理大量的实例;
  • 当簇的大小非常不同时不能很好地处理。

基于密度的噪声应用空间聚类

DBSCAN 缩写代表带噪声应用的基于密度的空间聚类

根据该算法,聚类是由低密度区域(数据点彼此远离)分隔的高密度区域(数据点彼此靠*)。

DBSCAN 算法的中心概念是一个核心样本的想法,这意味着样本位于一个高密度的区域。如果至少有min_samples个其他实例(通常包括 A )位于距 Aeps距离内,则数据点 A 被视为岩心样本。

DBSCAN 核心样本示例。min_samples=4,核心样本标记为红色。公共领域

因此,聚类是一组彼此靠*的核心样本和一些靠*核心样本的非核心样本。其他样本被定义为异常值(或异常值)并且不属于任何聚类。这种方法被称为基于密度的聚类。它允许您不将簇的数量指定为参数,而是查找复杂形状的簇。

DBSCAN 聚类。公共领域

DBSCAN 算法的扩展或推广是光学算法(排序点以识别聚类结构)。

优点:

  • 关于聚类数量的知识不是必需的;
  • 还解决了异常检测任务。

缺点:

  • 需要选择和调整密度参数(eps);
  • 不能很好地处理稀疏数据。

亲和传播

相似性传播算法也不需要关于聚类数量的知识。但是与 DBSCAN 不同,DBS can 是一种基于密度的聚类算法,相似性传播基于在数据点之间传递消息的思想。基于某个距离函数(即欧几里德距离)计算成对相似性,然后该算法收敛于某个数量的标准代表。然后使用这少量的标准代表来描述数据集,这些代表被识别为特定集群上最具代表性的实例。

Scikit Learn 的仿射传播聚类。图像来源

这种算法的结果常常不尽如人意,但它有许多强大的优势。然而,它的主要缺点是计算复杂性(由于需要计算所有可能的数据点对的距离)不允许它用于大型数据集。

优点:

  • 关于聚类数量的知识不是必需的;
  • 因此,我们也有集群的标准代表
    与 K 均值质心不同,这些实例不仅仅是*均值,而是来自数据集的真实对象。

缺点:

  • 由于计算复杂性,比其他算法慢得多;
  • 不能很好地处理大量的实例;
  • 仅很好地分离凸簇和同质簇。

均值漂移

均值漂移算法首先在每个数据点的中心放置一个一定大小的圆(圆的半径是一个叫做bandwidth的参数)。之后,它迭代计算每个圆的*均 T2(圆内各点的*均坐标)并移动 T4。执行这些均值偏移步骤,直到算法收敛并且圆圈停止移动。

您可以在这里看到均值漂移算法的可视化效果:

均值漂移收敛到密度最大的局部区域。然后所有彼此足够接*的圆形成一个集群。因此,同时解决了密度估计任务,并计算出聚类质心。

与 DBSCAN 一样,该算法代表了一种基于密度的方法,因此在处理稀疏数据时效果不佳。

优点:

  • 关于聚类数量的知识不是必需的;
  • 只有一个超参数:圆的半径
  • 求解密度估计任务并计算聚类质心;
  • 找不到实际不存在的簇结构。

缺点:

  • 不能很好地处理稀疏数据和大量特征;
  • 不能很好地处理大量的实例;
  • 不能很好地处理复杂形状的簇:倾向于将这些簇切成碎片。

桦树

桦树代表使用层次结构的*衡迭代减少和聚类

这种分层聚类算法是专门为大型数据集设计的。在大多数情况下,它的计算复杂度为 O(n) ,因此只需要对数据集进行一次扫描。

在训练期间,它会创建一个包含足够信息的树状图,以便将每个新数据实例快速分配给某个集群,而不必将所有实例的信息存储在内存中。与其他聚类算法相比,这些原则允许为给定的一组内存和时间资源获得最佳质量。它们还允许执行在线学习的增量集群输入数据实例。

优点:

  • 是专门为非常大的数据集设计的;
  • 显示给定的一组内存和时间资源的最佳质量;
  • 允许实现联机群集。

缺点:

  • 不能很好地处理大量的功能。

高斯混合模型

高斯混合模型( GMM )是一种概率算法,可以解决多达三个无监督学习任务:聚类密度估计异常检测

该方法基于期望最大化算法,并假设数据点是从一组(混合)高斯分布中生成的。这种算法会导致较差的局部解,因此需要运行几次,只保留最佳解( sklearn 中的n_init参数)。

众所周知,在一般情况下,高斯分布有两个参数:*均值的向量和方差的矩阵。然后,如果已知数据可以分成M维空间中的N个簇,那么算法的任务就是选择N个μ向量(带有M个元素)和N个σ矩阵(带有MxM个元素)。

在一维空间的情况下,μ和σ都是标量(单个数)。

一维空间中不同参数的高斯(正态)分布。公共领域

在下图中,你可以看到二维空间中的两种分布。每个分布都有以下参数:

  • *均向量的两个值(x 和 y 坐标);
  • 方差矩阵的四个值(主对角线的方差和其他元素的协方差)。

GMM 的例子。图像来源

为了选择一个好的集群数量,你可以使用 BICAIC (贝叶斯/阿凯克信息标准)作为度量,并选择具有最小值的模型。另一方面,你可以使用贝叶斯 GMM 算法。该模型只需要一个大于可能的聚类数的值,就可以自己检测出最佳的聚类数。

此外,高斯混合模型是一个生成模型,这意味着您可以从中采样新的实例。还可以在任何给定的位置估计模型的密度。

优点:

  • 完美地处理由不同形状和大小的高斯分布混合生成的数据集;
  • 同时解决了密度估计异常检测任务;
  • 是一个创成式模型,所以可以生成新的实例。

缺点:

  • 关于聚类数量的知识是必要的,并且必须被指定为一个参数(不是在贝叶斯 GMM 的情况下);
  • 期望最大化算法会导致局部解不佳,所以需要多次运行;
  • 无法很好地扩展大量功能;
  • 假设数据实例是由混合高斯分布生成的,那么就很难处理其他形状的数据。

如何选择一种聚类算法?

正如您所看到的,聚类任务非常困难,并且有各种各样的应用程序,因此几乎不可能建立一些通用的规则来选择聚类算法—它们都有优点和缺点。

当你对你的数据有一些假设 时,事情会变得更好,所以数据分析可以帮助你。集群的数量大概是多少?它们彼此相距很远还是相交?它们的形状和密度相似吗?所有这些信息可以帮助你更好地解决你的任务。

如果集群的数量未知,一个好的初始*似值是对象数量的*方根。您也可以首先运行一个不需要将集群数量作为参数的算法( DBSCANAffinity Propagation ),并使用结果值作为起点。

另一个重要的问题仍然是质量的评估——你可以尝试所有的算法,但如何决定哪一个是最好的?这方面有很多衡量标准——从同质性完整性轮廓——它们在不同的任务中表现出不同。理解这些指标以及如何成功地使用它们需要经验,这超出了本文的范围。

尽管如此,我还是设法列出并回顾了所有主要的聚类算法和方法。希望这能帮助你并激励你更深入地进行聚类分析。

本文是以下内容的一部分:

您可能还对以下内容感兴趣:

感谢您的阅读!

  • 我希望这些材料对你有用。在媒体上关注我以获得更多类似的文章。
  • 如果您有任何问题或意见,我将很高兴得到任何反馈。在评论里问我,或者通过 LinkedIn 或者 Twitter 联系。
  • 为了支持我作为一名作家,并获得数以千计的其他媒体文章,使用我的推荐链接获得媒体会员资格(不收取额外费用)。

用于主题建模的聚类上下文嵌入

原文:https://towardsdatascience.com/clustering-contextual-embeddings-for-topic-model-1fb15c45b1bd

通过聚类上下文句子嵌入提取主题

Unsplash 上由 Luisa Denu 拍摄的照片

TL;速度三角形定位法(dead reckoning)

我们进行了大量的实验来比较基于聚类的主题模型和传统的神经主题模型(NTMs ),展示了一种从文档中提取主题的替代方法。

查看我们的T5 NAACL 2022论文📄" 神经话题建模比聚类好吗?关于主题和官方 Github 的语境嵌入聚类的实证研究。

范例 1:传统的主题建模

opic 建模是一种无监督的方法,用于提取文档中的语义主题。从传统的潜在狄利克雷分配(LDA)模型到神经网络增强的神经主题模型(NTMs ),主题建模取得了显著的进步。然而,这些主题模型通常采用词袋(BoW)作为文档表示,这限制了模型的性能。

后来,由各种预训练语言模型(如 BERT)产生的上下文化单词和句子嵌入出现,并在多种自然语言处理(NLP)任务中显示出最先进的结果。最*的作品如 CombinedTMZeroShotTM ⁴将这些情境化的嵌入整合到 NTMs 中,显示出比传统 NTMs 更好的建模性能。

尽管结果很有希望,但是这种 NTM 遭受计算开销,并且当前将预训练的嵌入集成到 NTM 架构中是幼稚的。

有了高质量的上下文化文档表示,我们真的需要复杂的 ntm 来获得连贯和可解释的主题吗?

范例 2:基于聚类的主题建模

相反,我们探索了另一种从文档中建模主题的方法。我们使用一个简单的集群框架和上下文嵌入来进行主题建模,如下所示。

我们方法的架构。图片作者。

我们首先通过预先训练的语言模型对预处理的文档进行编码,以获得情境化的嵌入。之后,在应用聚类方法(例如,K-Means)来分组相似的文档之前,我们降低嵌入(例如,UMAP)的维度。每个群组将被视为一个主题。最后,我们采用加权的方法选择代表词作为主题。降低嵌入维度是可选的,但可以节省运行时间(更多细节见下文)。

这种基于聚类的主题建模范例并不新鲜。 Top2vec ⁵联合提取单词和文档向量,将每个密集区域的质心作为主题向量,n 个最*的单词向量作为主题词; BERTopic ⁶采用类似的方法,但是它使用提出的 c-TF-IDF 来选择每个聚类内的主题词; Sia 等人(2020) ⁷聚类词汇级的单词嵌入,并使用加权和重新排序从每个聚类中获得顶部单词。

然而,上述方法忽略了最*提出的有前途的 NTMs。基于聚类的主题模型的性能还没有与传统的主题模型进行比较。

我们提议的工作

在这里,我们介绍一下我们的 NAACL 2022 论文📄" 神经话题建模比聚类好吗?主题和官方 Github 的语境嵌入聚类实证研究。据我们所知,我们是第一个使用各种基于 transformer 的模型产生的上下文嵌入来与 ntm 进行比较的。此外,我们提出了新的单词选择方法,该方法将全局单词重要性与每个聚类中的本地词频率相结合。**

我们以[Health News in Twitter](https://archive.ics.uci.edu/ml/datasets/Health+News+in+Twitter) ⁰数据集(从 UCI 机器学习库中检索)为例,说明 范式 2:基于聚类的主题建模 也能抽取高质量的主题。

1.数据准备

我们首先使用 OCTIS 加载预处理过的数据集:

文档被标记化,给出了 3929 个预处理的句子:

**length of documents: 3929
preprocessed documents: [
  'breast cancer risk test devised',
  'workload harming care bma poll',
  'short people heart risk greater',
  'new approach hiv promising',
  'coalition undermined nhs doctors',
  'review case nhs manager',
  'day empty going',
  'overhaul needed end life care',
  'care dying needs overhaul',
  'nhs labour tory key policies',
  ...
]**

2.把...嵌入

其次,我们需要将这些文档转换成矢量表示。我们可以使用任何嵌入,这里,我们使用来自 SimCSE 的预训练princeton-nlp/unsup-simcse-bert-base-uncased嵌入。

3.减少和集群嵌入

现在,我们有了文档的嵌入表示,我们可以通过应用聚类方法将相似的文档分组在一起。减小嵌入大小可以有效地节省运行时间。

4.从聚类中选择主题词

最后,我们可以应用加权方法从每个聚类中选择主题词:

推特中健康新闻设置 5 个话题时的评价是:

**num_topics: 5 td: 1.0 npmi: 0.106 cv: 0.812**

示例主题包括:

**Topic:
0: ['nhs', 'amp', 'care', 'hospital', 'health', 'mental']
1: ['ebola', 'vaccine', 'flu', 'liberia', 'leone', 'virus']
2: ['dementia', 'obesity', 'alzheimer', 'diabetes', 'brain', 'disabled']
3: ['cancer', 'heart', 'breast', 'surgery', 'transplant', 'lung']
4: ['cigarette', 'cigarettes', 'pollution', 'smoking', 'sugar', 'drug']**

我们可以看到 范式 2:基于聚类的主题建模 发现的主题也可以是高度连贯和多样的,虽然文档的长度相对较短。尽管所有话题都是关于健康的,但我们可以区分话题 1 是关于传染性病毒和疾病的,话题 2 是关于疾病症状的,等等。

为了进行比较,我们还使用 CombinedTM ,一个 范例 1:基于常规主题建模 的模型来提取 Twitter 数据集中相同的健康新闻。我们使用相同的princeton-nlp/unsup-simcse-bert-base-uncased嵌入。

对推特中健康新闻的评价在设置 5 个话题时分别是:

**num_topics: 5 td: 1.0 npmi: -0.267 cv: 0.401**

示例主题包括:

**Topic:
0: ['cancer', 'amp', 'drug', 'death', 'audio', 'test']
1: ['food', 'obesity', 'cigarette', 'smokers', 'link', 'smoking']
2: ['ebola', 'mers', 'liberia', 'vaccine', 'malaria', 'virus']
3: ['babies', 'brain', 'sperm', 'man', 'human', 'cell']
4: ['nhs', 'health', 'mental', 'care', 'staff', 'hospital']**

我们可以看到,一些主题是不连贯的,例如,主题 0 和 3。

我们总结了基于聚类的主题建模的发现。

直接聚类高质量的嵌入可以产生好的主题。

实验表明,高质量的嵌入对于基于聚类的主题建模至关重要。我们实验了不同的嵌入,包括伯特、罗伯塔 ⁸、辛姆塞 ⁹等。,基于三个不同长度的数据集。对 RoBERTa 进行聚类得到的结果与上下文化的 ntm 相似或更差,这表明嵌入质量很重要。

最*的 DiffCSE 在某些数据集上可以达到略高的性能!

前 10 个词的主题连贯性(NPMI 和 CV)和主题多样性(TU)的不同嵌入。图片由

选词方法至关重要。

一旦我们有了一组聚类的文档,选择有代表性的主题词对于识别主题的语义是至关重要的。与以前提出的方法不同,我们捕获每个聚类中的全局词重要性和本地词频率,并比较 4 种不同的方法:

图片作者。

图片作者。

图片作者。

图片作者。

我们发现,在所有方法中,TFTDF × IDFᵢ取得了明显更好的结果。这表明 TFIDF 标记出整个语料库中每个文档的重要单词,而 IDFᵢ惩罚多个簇中的常见单词。相反,其他三种方法忽略了一个聚类中的频繁词也可能在其他聚类中流行,因此选择这样的词导致低主题多样性。

不同主题词选择方法的比较。图片由论文

嵌入维度对主题质量的影响可以忽略不计。

在聚类之前,我们应用 UMAP 来降低句子嵌入的维数。我们发现,在聚类之前降低维数对性能的影响可以忽略不计,但可以节省运行时间。

用法示例

Github 中所述,你可以从[tfidf_idfi, tfidf_tfi, tfidfi, tfi]中选择一种选词方法。如果您不想使用 UMAP 减少嵌入维数,只需设置dim_size=-1。您可以训练模型,并获得评估结果和主题:

预期的输出应该类似于:

结论

在这篇文章中,我们介绍了一种基于聚类的方法,只要使用高质量的上下文嵌入,就可以产生值得称赞的主题,以及一种合适的主题词选择方法。与神经主题模型相比,
基于聚类的模型更加简单、高效,并且对各种文档长度和主题数量更加鲁棒,这可以作为一种替代方案
应用于某些情况。

[1]:大卫·M·布雷,安德鲁·吴和迈克尔一世·乔丹。2003.潜在狄利克雷分配。机器学习研究杂志,3:993–1022。

[2]: Devlin,j .,Chang,M.W .,Lee,k .和 Toutanova,k .,2018。Bert:用于语言理解的深度双向转换器的预训练。 arXiv 预印本 arXiv:1810.04805

[3]:比安奇,女,特拉尼,s 和霍维,d,2020。预训练是一个热门话题:语境化的文档嵌入提高了主题的连贯性。arXiv 预印本 arXiv:2004.03974 。

4:比安奇,f .,特拉尼,s .,霍维,d .,诺扎,d .,费尔西尼,e .,2020。零镜头学习的跨语言语境化主题模型。arXiv 预印本 arXiv:2004.07737 。

5:安杰洛夫特区,2020 年。Top2vec:主题的分布式表示。 arXiv 预印本 arXiv:2008.09470

[6]:格罗腾多斯特,m,2022。BERTopic:使用基于类的 TF-IDF 过程的神经主题建模。 arXiv 预印本 arXiv:2203.05794

[7]: Sia,s .,Dalmia,a .和 Mielke,S.J .,2020。厌倦了话题模型?预先训练的单词嵌入集群也是快速和良好的主题!。arXiv 预印本 arXiv:2004.14914 。

[8]:刘,y .,奥特,m .,戈亚尔,n .,杜,j .,乔希,m .,陈,d .,列维,o .,刘易斯,m .,泽特勒莫耶,l .,斯托扬诺夫,v .,2019。Roberta:稳健优化的 bert 预训练方法。 arXiv 预印本 arXiv:1907.11692

[9]:高,汤,姚,陈,2021。Simcse:句子嵌入的简单对比学习。 arXiv 预印本 arXiv:2104.08821

[10]:卡拉米,a .,甘戈帕迪亚,a .,周,b .,&哈拉齐,H. (2017)。健康和医学语料库中的模糊方法主题发现。国际模糊系统杂志,1–12。

[11]: Dua,d .和 Graff,C. (2019)。UCI 机器学习知识库[http://archive . ics . UCI . edu/ml]。加州欧文:加州大学信息与计算机科学学院。

[12]:张,z,方,m,陈,l .,&纳马齐-拉德,M. R. (2022)。神经主题建模比聚类好吗?基于主题上下文嵌入的聚类实证研究。 arXiv 预印本 arXiv:2204.09874

混合数据类型上的聚类

原文:https://towardsdatascience.com/clustering-on-mixed-data-types-5fe226f9d9ca

利用高尔相和 HDBSCAN

资料来源:Unsplash。

聚类是一种无监督的机器学习技术,旨在将相似的数据点分组到不同的子组中。通常,用于这种分组的距离度量对于数值数据是欧几里德距离,对于分类数据是雅克卡距离。大多数聚类算法也是为数字或分类数据显式设计的,但不是同时为两者设计的。在本文中,我将概述一种通过利用一种称为高尔相异度的距离度量来对混合数据进行聚类的方法。虽然各种聚类算法可以将预先计算的距离度量作为输入,但我将使用 HDBSCAN,因为它对异常值具有鲁棒性,并且能够识别有噪声的数据点。

数据集和预处理

为了说明,我使用公开可用的泰坦尼克号测试数据集。高基数要素(主要是每个乘客独有的要素,如 PassengerIdNameCabin )以及任何包含 nan 的行和包含超过 50% nan 的列都已从数据集中移除。为了简单和更好的可视化,只使用了 30%数据的随机部分。

预处理的 Titanic 测试数据集的随机样本。

高尔不同

高尔相异度( GD ) 是表示两个样本差异程度的度量。度量范围从 0 到 1,0 表示无差异,1 表示最大差异。它是基于任意两个样本的部分相似性来计算的。部分相似性( ps )根据数据类型是数值型还是分类型进行不同的计算。对于分类数据,如果值相同,ps = 1,如果值不同,ps = 0。对于数值数据, ps 计算如下:

首先,确定特征列的范围。

第二, ps 是对数据中任意两个样本计算的。

第三,高尔相似度( GS )是通过取所有部分相似度的算术*均值来计算的。

最后,通过从 1 中减去相似性度量( GS )来将其转换成距离度量( GD )。

视觉插图

为了更好地说明,让我们再次查看预处理数据的前五行:

预处理的 Titanic 测试数据集的随机样本。

查看第 0 行和第 3 行,人们会立即意识到这两行非常相似,只有在年龄费用上略有不同。因此,我们预计 GD 会非常低(接*于零)。其实准确的 GD 是 0.0092(见下图)。更不相似的样本,如第 0 行和第 2 行,具有更大的距离值,在本例中为 0.24。

上面概述的样本的相应高尔距离矩阵。

我们整个预处理数据集的 GD 矩阵如下所示:

整个预处理测试数据集的高尔距离矩阵。

使聚集

作为聚类算法,我选择了 HDBSCAN (针对有噪声的应用的基于层次密度的空间聚类)。HDBSCAN 擅长识别高密度集群,计算效率高,对异常值具有鲁棒性。

设置了两个参数来运行 HDBSCAN。影响聚类结果的主要参数是最小聚类大小。这是指被视为一个组或簇的最小数量的数据点。可以设置一个附加参数 min_samples 来控制分类为噪声的数据。该值越低,归类为噪声的数据点就越少。在该示例中,最小聚类大小被设置为 6,而最小样本数被设置为 1。

这些参数可以使用集群质量度量进一步调整,例如基于密度的集群验证(DBCV) 分数。然而,为了简洁起见,本文省略了这一点。

形象化

为了可视化结果,应用了 2D t 分布随机邻居嵌入 (t-SNE)投影。

HDBSCAN 输出 6 个不同的簇和一个被视为噪声的簇(暗红色)。

让我们通过可视化其中一些集群的值来看看这些分组的质量。

紫色集群

组成紫色集群的数据点样本。

黄色集群

组成黄色聚类的数据点样本。

绿色集群

组成绿色聚类的数据点样本。

噪声集群

正如所料,归类为噪声的样本彼此非常不同:

组成噪声群的数据点的样本。

结论

在本文中,我演示了如何对混合类型的数据进行聚类,方法是首先计算高尔距离矩阵,然后将其输入 HDBSCAN。结果表明,对于所使用的数据,该方法在将相似的数据点分组在一起方面表现得相当好。然而,这不是一个适用于所有混合数据类型的通用方法,最终使用的方法将取决于手头的数据。例如,HDBSCAN 要求集群内的密度一致,集群之间的密度下降。如果没有,就需要考虑其他方法。

如何利用机器学习自动检测文本中的模式

原文:https://towardsdatascience.com/clustering-text-with-k-means-c2953c8a9772

用 k-Means 聚类葡萄酒评论

Maksym Kaharlytskyi 在 Unsplash 上的照片

什么文本聚类?

在上一篇文章中,我们谈到了主题建模或者从文档语料库中识别几个主题的方法。这里使用的方法是潜在的狄利克雷分配或 LDA。在本文中,我们将执行类似的任务,但通过无监督的机器学习方法聚类。虽然方法不同,但结果是几组(或主题)彼此相关的单词。

对于这个例子,我们将使用来自 Kaggle 的葡萄酒评论数据集。它包含了超过 100,000 种不同的全球葡萄酒评论。作为品尝笔记的葡萄酒描述是基于文本的变量,我们将使用它来聚类和解释结果。

从导入所需的库开始。

:这里不打算展示文本的预处理。你可以在 GitHub 上看到完整的代码。还有一篇关于文字清理的完整文章可以参考。

from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score

import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline

文本矢量化

向量化文本是将文本文档转换成数字表示的过程。这有不同的版本,例如单词袋** (BoW),以及术语频率-逆文档频率 (TF-IDF),我们将在这里使用。**

  • BoW 或 TF :表示每个文档中每个单词的计数。在这种情况下,文档记录了我们所针对的列的数据集中的观察结果。
  • TF-IDF :它不是只计算单词的数量,而是反过来,给那些出现频率较低的单词更高的权重。常见单词具有较低的权重,而可能更特定于领域且出现较少的单词将具有较高的权重。

创建 TF-IDF ,创建矢量器的一个实例,然后fit-transform数据框的列是非常容易的。

vectorizer = TfidfVectorizer()
X = vectorizer.fit_transform(df['description_clean'])

确定最佳聚类数

我上面提到聚类是一种无监督的机器学习方法。无监督意味着我们的数据集中没有告诉我们正确答案的信息;这通常被称为标记数据。在我们的例子中,我们不知道文本中有多少不同类型的酒或不同的主题。然而,仅仅因为我们不知道这些信息,并不意味着我们不能找到正确的集群数。

有了聚类,我们需要初始化几个聚类中心。这个数字被输入到模型中,然后在结果输出后,具有数据知识的人可以解释这些结果。然而,有一些方法可以评估哪一个是正确的聚类中心数量,我将介绍两种常用的方法。

肘法

第一种称为肘法。该名称源自运行此分析后的图形外观。理想情况下,我们在寻找曲线开始变*的点。这种方法使用inertia来确定簇的数量。惯性是从每个点到聚类中心的距离的*方的总和。我们可以对一系列不同的聚类值进行计算,绘制它们,并寻找弯头

Sum_of_squared_distances = []
K = range(1,10)
for k in K:
    km = KMeans(init="k-means++", n_clusters=k)
    km = km.fit(X)
    Sum_of_squared_distances.append(km.inertia_)

ax = sns.lineplot(x=K, y=Sum_of_squared_distances)
ax.lines[0].set_linestyle("--")

# Add a vertical line to show the optimum number of clusters
plt.axvline(2, color='#F26457', linestyle=':')

plt.xlabel('k')
plt.ylabel('Sum of Squared Distances')
plt.title('Elbow Method For Optimal k')
plt.show()

在绘制数据后,肘形并不明显,但最佳*似是在 2 个集群处,我们在曲线中看到一个轻微扭结。我画了一条垂直线来识别它。

作者图片

剪影分数

另一种计算最佳聚类中心的方法是轮廓系数。使用每个样本的*均聚类内距离和*均最*聚类距离来计算轮廓系数。换句话说,样本和样本不属于的最*聚类之间的距离。

最好的值是1,最差的是-10附*的值表示重叠的簇。负值通常表示样本被分配到了错误的分类,因为不同的分类与其被分配的分类更相似。让我们也为不同的聚类中心值计算这些分数。

def get_silhouette_score(X, k):
    for n_clusters in range(2, k):
        clusterer = KMeans(init="k-means++", n_clusters=n_clusters, random_state=42)
        y = clusterer.fit_predict(X)

        message = "For n_clusters = {} The average score is: {}"
        print(message.format(n_clusters, silhouette_score(X, y)))

get_silhouette_score(X, 10)
For n_clusters = 2 The average score is: 0.00821919113279018
For n_clusters = 3 The average score is: 0.006522933295313797
For n_clusters = 4 The average score is: 0.006237960319271207
For n_clusters = 5 The average score is: 0.006266850309331783
For n_clusters = 6 The average score is: 0.006381665959703946
For n_clusters = 7 The average score is: 0.005549433908077499
For n_clusters = 8 The average score is: 0.005962146586290015
For n_clusters = 9 The average score is: 0.00632540099660495

根据定义,最接*1的数字是最好的,在我们的例子中是2簇。但是,这些值接*于零,这意味着我们的聚类有很高的重叠。

虽然这两种方法都不能让我们理想地了解集群的数量,但是我们应该使用,两者都指向2作为最佳值。

k-均值聚类

好吧!我们准备好构建模型并检查结果。这个过程非常简单,我们将重复上面所做的大部分工作,但是只对我们选择的集群数量进行一次运行。

对于这个例子,我们将使用 k-Means。k-Means 是最常见的聚类算法之一,如果不是最常见的话。通常,k-Means 会随机初始化聚类中心,然后迭代,直到找到理想的位置。指定init="k-means++"意味着我们将使用 k-means++算法来初始化集群中心,这是 2007 年提出的一种减少随机初始化问题的方法。首先,我建议使用这个工具,并稍微阅读一下它是如何工作的。

# Set the number of clusters
k = 2
# Vectorize the text
vectorizer = TfidfVectorizer()
X = vectorizer.fit_transform(df['description_clean'])
# Fit our Model
model = KMeans(init="k-means++", n_clusters=k, max_iter=25, n_init=1)
model.fit(X)

您可以将聚类分配保存为数据框中的新列,其中包含聚类编号,以供将来参考。

# Get the cluster labels
clust_labels = model.predict(X)
cent = model.cluster_centers_

kmeans_labels = pd.DataFrame(clust_labels)
df.insert((df.shape[1]),'clusters',kmeans_labels)

最后,让我们构建一个快速数据框,显示两个集群中的前15个单词,看看我们得到了什么。

order_centroids = model.cluster_centers_.argsort()[:, ::-1]
terms = vectorizer.get_feature_names_out()

results_dict = {}

for i in range(k):
    terms_list = []

    for ind in order_centroids[i, :15]:  
        terms_list.append(terms[ind])

    results_dict[f'Cluster {i}'] = terms_list

df_clusters = pd.DataFrame.from_dict(results_dict)
df_clusters
Cluster 0   Cluster 1
0   pineapple      cherry
1      flavor      flavor
2     acidity         dry
3     vanilla  blackberry
4       fruit      tannin
5         oak        cola
6       crisp   raspberry
7        pear     currant
8       apple        good
9       peach        rich
10       lime        soft
11     butter       spice
12      toast        show
13      sweet         oak
14      lemon       sweet

看看集群0,我们看到的单词通常与酒相关,集群1酒相关。

预测新文档

接下来,我们可以看到该模型如何对过去没有的新葡萄酒评论进行聚类。我用白葡萄酒和红葡萄酒可能与之相关的词造了几个句子。

new_docs = ['Rich deep color of oak and chocolate.',
            'Light and crisp with a hint of vanilla.',
            'Hints of citrus and melon.',
            'Dark raspberry and black cherry flavors.']

pred = model.predict(vectorizer.transform(new_docs))
print(pred)
[1 0 0 1]

正如我们所料,红酒被归类为1,白酒被归类为0!你可以试试其他的弦,看看它的表现如何。

结论

第一次在这个数据集上执行聚类时,我真的被震惊了。显而易见,通过2聚类,算法可以识别和聚类红葡萄酒和白葡萄酒。这感觉有点像魔术,但最终,这只是数学!享受这个过程。它功能强大,可以帮助你识别文本中相关的主题组!

如果你喜欢阅读这样的故事,并想支持我成为一名作家,可以考虑报名成为一名媒体成员。一个月 5 美元,让你可以无限制地访问成千上万篇文章。如果你使用我的链接注册,我会赚一小笔佣金,不需要你额外付费。

CLVTools:评估客户的强大 R 包

原文:https://towardsdatascience.com/clvtools-a-powerful-r-package-to-evaluate-your-customers-4fd1781811d

客户的终生价值对企业来说是一种无价的战略工具

包装使用说明。来源:作者。

在这篇与 Patrick Bachmann 共同撰写的文章中,我们浏览了广泛的 R 包“CLVTools”,其中包括一些最知名的模型的便捷实现,以估计客户终身价值(CLV 或 LTV,这里以为例介绍)。这篇文章是基于在找到的这个包的相当详细的预排。还有一个(更老的)youtube 视频提供了进一步的解释。

我们将主要关注 ParetoNBD 模型及其扩展,在关于 LTV 的文献中,它被视为一种黄金标准。该模型假设交易以所谓的“泊松过程”发生,并且客户是(I)异质的,(ii)可以在任何时间购买,以及(iii)可以在任何时间结束他们与公司的关系。情况(iii)被认为是永久性的,一旦你失去了一个客户,他们就不会回来了。因此,除了交易或泊松过程之外,一个单独的过程被建模来表示客户的“生命周期”。这是这类模型中的一个典型主题,因此人们通常将事务过程和生命周期或损耗过程区分开来。

因为这个模型有点复杂,它的表亲 BG-NBD 模型经常被考虑,例如在这个帖子中。不同之处在于,BG-NBD 模型在本质上更加离散——特别是,客户只能在交易后直接变得不活跃(而在帕雷顿 BD 模型中,他们可以在任何时候变得不活跃)。这在某些情况下可能是可取的,但在(许多)其他情况下就不那么可取了。

CLVTools 中包含的模型概述。来源:经过作者许可,此处的演练。

关于模型本身已经说得够多了,我们如何将它们与 CLVTools 结合使用呢?使用软件包进行成功的分析通常需要遵循 3 个步骤:

  1. 创建“clv.data”对象,其中包含数据集和所需的元信息(使用的日期格式、列名等)。初始化之后,可以添加更多关于潜在协变量的信息。
  2. 符合模型。
  3. 使用估计的模型参数进行分析,即绘制、预测客户行为等。

上图也说明了这一点。我们现在用一个示例数据集来完成这些步骤。

第一步:初始化

library(CLVTools)
data("apparelTrans")
data("apparelDynCov")

我们使用包装中的“apparelTrans”数据,如下所示。这个截屏很重要,因为它显示了该包所需的数据格式:每个客户应该有一个唯一的 Id 号、交易日期(以某种给定的格式,如 YYYY-MM-DD )和交易价格。

交易数据。来源:作者。

此外,我们假设客户的协变量(即属性)存储在“apparelDyncov”中。特别是公司知道客户的性别和他们的获取渠道(“线下”=0 或“线上”=1)。这些是静态协变量,这意味着它们可能因人而异,但随着时间的推移是恒定的。然而,也有“营销”,这表明有多少直接营销尝试在一个单一的客户。这是一个时变协变量,这意味着它不仅会因客户而异,还会随时间而变。软件包中只有一个(非常新的)模型目前能够处理这种时变结构,扩展的 ParetoNBD 模型在这里开发,我们将充分利用这一点。

所有客户的附加协变量。来源:作者。

现在,我们采取必要的第一步,用“clvdata”命令将这些数据引入到包中,这将产生一个对象,第二步和第三步的所有函数都可以轻松地处理该对象。在这个命令中,我们需要指定数据集、我们使用的日期格式(如在“lubridate”R 包中)、时间单位以及何时(或是否)应该将数据划分为估计和验证周期。最后,我们需要指定 id、日期和价格的列名。

clv.apparel <- clvdata(apparelTrans,
                       date.format = "ymd",
                       time.unit= "week",
                       estimation.split=40,
                       name.id="Id",
                       name.date="Date",
                       name.price="Price")

这里可以看到摘要输出:

clv.apparel 对象的摘要输出。来源:作者。

第二步:评估

我们现在可以估计我们的上述对象的模型。有了默认选择,没有协变量,这就简单了:

## Estimate the PNBD model
est.pnbd<- pnbd(clv.data=clv.apparel)
## Estimate the BGNBD model
est.bgnbd <- bgnbd(clv.data=clv.apparel)

另一方面,对于(时变)协变量,我们需要指定更多:特别是,我们需要指定生存期或损耗过程(data.cov.life)和事务过程(data.cov.trans)的协变量数据。此外,我们需要在 names.cov.life 和 names.cov.trans 中给出这两个进程的名称。

clv.dyn <- SetDynamicCovariates(clv.data=clv.apparel,
                                data.cov.life = apparelDynCov,
                                data.cov.trans = apparelDynCov,
                                names.cov.life = c("Marketing", "Gender", "Channel"),
                                names.cov.trans = c("Marketing", Gender", "Channel"),
                                name.id = "Id",
                                name.date = "Cov.Date")
# Estimate the PNBD with Covariates (This takes a while (!))
est.pnbd.dyn <- pnbd(clv.dyn)

然后,我们可以首先检查这些模型的汇总输出。该输出类似于简单线性回归:给出了标准误差以及参数的显著性值,此外,还提供了关于优化收敛的信息,如最终似然值。请注意“est.pnbd.dyn”比 est.pnbd 多了 6 个参数。这对应于交易和流失流程的 3 个协变量(“性别”、“渠道”、“营销”)中每一个的一个协变量值。我们也可以利用这一点,限制协变量只影响两个过程中的一个,或者用相同的参数影响两个过程。

ParetoNBD 模型的摘要输出。来源:作者。

BG-NBD 模型的概要输出。来源:作者。

有协变量的 ParetoNBD 模型的汇总输出。来源:作者。

有趣的是,该模型暗示了直接营销努力的不同效果(假设因果解释是可能的):它似乎增加了交易频率(trans。营销是正面的),但对一个客户的*均寿命(寿命)有负面影响。营销是负面的)。这可能意味着直接营销可能会在短期内促进销售,但会增加客户提前离开公司的机会。像往常一样,人们必须小心这种因果关系的陈述,但该模型至少提供了对影响的暗示(上面链接的原始论文详细解释了如何使用工具变量(iv)等恢复因果关系)。).

但是干输出已经足够了,让我们做一些绘图。检查样本内拟合的一个有趣的图是“跟踪图”,通常在这种情况下完成。

ParetoNBD 中所有客户的重复交易次数的预测值与实际值。来源:作者。

BG-NBD 预测的与实际的所有客户的重复交易数量。来源:作者。

对于有协变量的 ParetoNBD,所有客户的重复交易的预测数与实际数。来源:作者。

BG-NBD 和 ParetoNBD 图看起来非常相似:它们很好地穿越了不稳定的交易,似乎也很好地捕捉到了均值。对于时变协变量,这变得更加有趣,我们突然能够匹配一些起初看起来随机的模式。这就是设计良好的时变协变量所能带来的优势。事实上,加上单独的,但时间不变的协变量,单独的预测有时可以大大改善,超出了这个聚合图中可见的。除了大量经典模型和极快且优雅的实现,这才是 CLVTools 真正与众不同的地方。

但最终我们感兴趣的是预测而不是良好的样本内拟合。让我们开始吧:

第三步:预测

从代码的角度来看,这也是非常简单的(如果我们使用默认值):

predict(est.pnbd)
predict(est.bgnbd)
predict(est.pnbd.dyn)

不幸的是,这里没有什么华而不实的东西可以展示,只有方便的表格给出了不同模型的所有预测(我们在这里省略了 est.bgnbd):

PartoNBD 模型的预测表:来源:作者。

有协变量的 PartoNBD 模型预测表:来源:作者。

这里要提到一件奇怪的事情:虽然 ParetoNBD 和 BG-NBD 可以预测交易的预期数量(“CET”)和客户仍然留在公司的概率(“PAlive”),但他们无法独自模拟支出过程。为此,模型的输出通常与用于支出的伽马-伽马(GG)模型相结合。这模拟了消费过程,当我们要求预测时,它会自动拟合,这就是红色部分。这不仅会导致预测的*均支出,还会导致我们期望的预测客户终身价值。每个 ID 在表格的最后一列给出。

这些预测可以用于更明智的商业决策。例如,关注 Id 为 100 和 1000 的客户可能是明智的,因为他们的 LTV 估计相当高。然而,给定参数估计,在这个例子中不清楚直接营销是否是可行的方法:至少有一个暗示,直接营销可以减少顾客在公司停留的*均时间。

结论

本文简要介绍了用于客户价值建模的 CLVTools 包。该软件包具有广泛的功能和优势,我们不可能在一篇文章中涵盖,但我们希望它能帮助感兴趣的读者开始使用该软件包并使用它做有用的事情。

美国二氧化碳排放数据集:使用 Python 的统计分析

原文:https://towardsdatascience.com/co2-emissions-dataset-in-usa-a-statistical-analysis-using-python-e8f2ab8eb78f

从二氧化碳排放数据集中提取信息

Marek Piwnicki 在 Unsplash 上的照片

免责声明:这个笔记本不是由气候科学家写的!一切都由数据科学家的观点独家分析。所有的统计分析都是用来作为任何一种时间序列分析的工具。

让我们从陈述显而易见的事实开始:

数据科学家的工作是提取洞察力。

你正在使用的工具的复杂性并不真正相关。更重要的是,无论你使用什么,对你想要研究的分析都是有用的。

1.介绍

在我们的例子中,我们有一个时间序列。这一时间序列当然在 x 轴(特别是记录)上有时间,在 y 轴上有 上有美国燃煤发电产生的二氧化碳排放量。

这个数据集是公开的,没有任何版权限制( CC0:公共领域)。可以免费下载,在 Kaggle 上,这里

让我们来看看。首先,让我们导入库:

然后,让我们绘制时间序列(最后 7 个条目是 2016 年的,但这是一个截断的年份,所以我们将其截断);

第一个数据点是 1973 年的第一个月,最后一个数据点是 2015 年的最后一年。没有缺失值,因此数据集的长度正好是 12x(2015–1973)。

现在,什么样的分析与本案相关?嗯,我认为提出三个问题是很自然的:

a . CO2 排放量是否普遍增加?大趋势是什么?

B .这个 CO2 时间序列有没有什么重现的模式?

C .时间序列是“稳定的”还是我们可以期待不愉快的惊喜?

让我们调查一下,一步一步来:

2.一般趋势

我们感兴趣的是描绘这个时间序列的总体趋势。这并不是一种真正的回归技术,因为我们使用的是整个时间序列,这也不是一项真正的预测任务,因为……因为我们根本不是在预测的😃**

这只是趋势的一个侧面,以一种通用的方式对时间序列建模是有用的。如果你真的想拥有预测能力,使用更复杂的回归技术和/或将数据集分成训练集和测试集,可以很容易地改变代码,但这不是我们现在的情况。

我们对模型的预期是,从起点到峰值会有一个巨大的增长(x3 ),然后回到更合理的值,这个值仍然在初始值的 x2 附*。

使用至少三阶模型(它看起来不像抛物线,并且它肯定不是线:D)可以获得合理的模型(不太复杂,不太有损耗)。在我看来,degree = 4 更好,因为它捕捉到了更多的细节,但实际上并没有太大的区别。(同样,如果您想对此更加稳健,请对数据集的较小部分使用以下方法,并使用验证集来挑选误差最低的模型)。

所以我们来拟合一下四度模型。两步走:

A .拟合:

B .预测+绘图:

这看起来和我们预期的一模一样:

  1. 单调增加,直到 2002 年左右
  2. 从 2002 年到 2015 年单调(快得多)下降**

我们没有更多关于具体数据来源的信息,但我们可以说,在长时间的增加后,排放量有所减少。增加看起来比减少更渐进(甚至可能更自然,与工厂的节奏有关),也更慢。一种猜测可能是,在达到排放的危险值后,有人故意开始减少排放(可能有某种规定)(我不太清楚,只是一种似是而非的猜测)。

3.循环模式

我们在前一章所做的考虑很有趣,但是不应该妨碍我们接下来要做的事情:让我们去趋势我们的时间序列。

现在看起来是这样的:

现在,正如你们所看到的,《泰晤士报》非常活跃。让我们用一个非常强大的工具来分析这种起伏:神奇的傅立叶变换。

我❤傅里叶变换,我已经用了很多次了。特别是,我在几篇博客文章前在这里谈到过它。在那里,你可以找到傅立叶变换的概念,为什么它是有用的,希望你会像我一样喜欢它:)

让我们准备数据:

让我们画出傅立叶变换(振幅对周期,以月为单位)

为了清楚起见,让我转贴这张图片:

作者图片

这个其实很有意思。为了简洁起见,让我们考虑一下, y 轴上的每一次选择都是一个循环模式,在正好 M 个月之后被周期性地验证,**其中 M 是相对于该选择的 x 轴值。**

一些重要的趋势得到了证实:

  1. 大约 2.5 个月后
  2. 三个月后
  3. 4 个月后
  4. 半年后
  5. 整整一年后

同样,我们也不知道,但看起来二氧化碳排放确实与雇主的休假时间有关:通常他们在 6 个月或 12 年后休假,这是最显著的峰值。另一个提示是 2.5/3/4 个月的高峰期,因为不是所有的雇主都在准确的时间休息。

现在看起来很明显,等你意识到才明显:)

4.稳定性

为了理解时间序列的稳定性,我们可以做如下(合理的)假设:

时间序列是被一些高斯和白噪声“扰乱”的物理过程的结果

我是一个物理学家,这是我们的黄油和面包。这就是我们处理每一个问题的方式,并且有大量的关于误差“高斯性”的统计考虑。现在,让我们接受它,继续前进,就像你应该分手一样:)

预测物理过程产生的*均值并找到数据集的不确定性的一种非常有效的方法是使用著名的高斯过程回归器(GPR)。这是另一个我非常喜欢的工具,因为它是可解释的、足够通用的并且易于使用。我也在多个博客中谈论它(比如这里的和这里的)。简而言之,GPR 模型给你一个*均值和一个方差(+1.96 方差意味着在该区域找到点的概率为 95.7%)。这听起来可能令人困惑,让我们慢一点。****

让我们考虑 60%的数据点作为训练集。

让我们在训练集上拟合 GPR 模型,并在整个数据集上应用拟合的模型。通过这最后一步,我们将有一个预测的时间序列,根据“*均”值及其相对不确定性。让我展示给你看:

它看起来有点乱,但实际上做得很好:

现在,为了看看它是否“稳定”,我们可以调查有多少点落在预测的边界之外。

结果如下:

再一次,为了清晰起见,让我给你看一下情节:

作者图片

这意味着:

  1. 从 1973 年到 1983 年只有两个点在边界之外
  2. ****1983 年至 1993 年 7 点越界
  3. 从 1993 年到 2003 年有 8 个点超出界限
  4. ****7 分2003-2013 年越界

这意味着,从 1983 年开始,时间序列的行为变得更加不规则。但是还有!时间序列应该适应行为的变化,不确定性应该增加。这意味着,即使不确定性增加,仍然有很多点超出边界:时间序列的不稳定性不断增加。它实际上是首先通过查看时间序列就可以看到的东西。

5.结论

一些要点:

  1. 经过几十年的增长,二氧化碳的排放量似乎已经开始下降了
  2. 二氧化碳排放可能与雇主的休假时间有关,或者至少与公司的生产节奏有关
  3. 时间序列越来越不稳定,在第一个十年的数据后,不稳定性发生了很大的变化

如果你喜欢这篇文章,你想知道更多关于机器学习的知识,或者你只是想问我一些你可以问的问题:

A.在 Linkedin 上关注我,我在那里发布我所有的故事
B .订阅我的 简讯 。这会让你了解新的故事,并给你机会发短信给我,让我收到你所有的更正或疑问。
C .成为 推荐会员 ,这样你就不会有任何“本月最大故事数量”,你可以阅读我(以及数千名其他机器学习和数据科学顶级作家)写的任何关于现有最新技术的文章。

Python 中的二氧化碳排放信息图

原文:https://towardsdatascience.com/co2-emissions-infographics-in-python-369dd968eb84

如何用 Python 和 Matplotlib 构建数据信息图

从国际空间站看到的地球 NASA 提供的公开图片https://www . NASA . gov/mission _ pages/station/images/index . html

信息图这个词被用来描述各种可爱的图片,这些图片上有卡通人物和指向各处的箭头。

但是这里我们谈论的是传达数据信息的图形,这些数据可能会在报告或报纸文章中使用。从本质上来说,它们是一种有吸引力的数据呈现方式,带有支持性的图表和文本。

所以,让我们想象一下,我们要写一篇关于我们的大气状况和导致气候变化的二氧化碳水*的报告或文章。

我们将通过创建两个信息图表来告诉我们的读者大气中二氧化碳的水*,在过去和现在。

第一个将从 NASA 的这张图片中获得灵感:

大气中二氧化碳的历史水*——公共领域图片由美国宇航局提供https://climate . NASA . gov/climate _ resources/24/graphic-the-renely-rise-of-carbon-CO2/

这张图表记录了几十万年来大气中的二氧化碳水*。它本质上是一个带注释的图,我们将使用 Pandas 和 Matplotlib 从原始数据构建我们的版本。

第二张图将包含两个图表,一个在上面,另一个显示各大洲的二氧化碳排放量。我们将把这些和一些支持文字一起放在一个背景图片上。

我会给你看我们进行的代码,但整个代码,数据和图像将可从我的网页。

我们将使用的数据全部由优秀的我们的数据世界 (OWID)网站和 Github 页面提供。在 CC-BY 许可下,他们所有的可视化、数据和文章都是免费和开源的。

我们的方法

我们将使用 Matplotlib 的功能,它允许我们注释图表并在图表中定位图像。

首先,我们将数据读入 Pandas dataframes,然后我们将根据这些数据创建一个基本图表。一旦我们有了这个,我们就像你在上面的 NASA 例子中看到的那样注释图表,并把它保存为图像。

为了构建信息图,我们创建了一个新的 Matplotlib 图,并将我们保存的图表图像与背景图像(您在本文顶部看到的图像)一起放入其中。

然后,我们重复这个过程来创建另一个信息图,但这次我们有两个图表和一些说明性文本。

结果将是这样的:

我们将创建的两个信息图——作者图片

代码

我将一步一步地浏览代码,并在每一节的末尾包含该部分完整代码的要点。所有的代码也可以通过我的网站上的链接获得。

我们需要做的第一件事是导入我们将使用的 Python 库。

**import** pandas **as** pd
**import** matplotlib.pyplot **as** plt

我们将从 OWID 数据创建 Pandas 数据帧,并使用 Pandas 和 Matplotlib 创建图表和其他视觉效果。

获取数据

我们将使用的第一个数据集包括许多国家和地区(实体)的各种气候指标。我们将使用全球的二氧化碳浓度数据,因此我们将相应地过滤数据。

首先,我们将数据读入数据帧co2,当然,这将包含所有的数据。

接下来,我们过滤数据,只考虑其中的实体名称为“World ”,并将其存储在数据帧co2world.

*# original source:* # [*https://ourworldindata.org/co2-and-other-greenhouse-gas-emissions*](https://ourworldindata.org/co2-and-other-greenhouse-gas-emissions)co2 **=** pd**.**read_csv('data/climate-change.csv')co2world **=** co2[co2['Entity']**==**'World']
co2world

历史二氧化碳浓度—作者图片

数据中还有其他几列,但我们只对 CO2 浓度数据感兴趣。

绘制基本图表

我们可以这样绘制这些数据:

co2world**.**plot(y **=** "CO2 concentrations", 
              x **=** 'Year', 
              grid **=** **True**, 
              figsize **=** (20,10))

历史二氧化碳浓度—作者图片

结果是一个相当令人震惊的图表,它显示,在过去的 80 万年里,地球大气中的二氧化碳浓度低于 300 ppm(百万分之一),但在过去的几十年里,它飙升至 400 ppm 以上。

然而,除非你熟悉数据,否则解读图表并不容易。所以,让我们帮读者一个忙,让我们看到的东西更明显一点。

x 轴代表时间,但大多数年份是负值。这些值是相对于共同时代的开始,因此,例如,-800,000 意味着 800,000 BCE(在共同时代之前),0 是我们在当前系统中开始计算世纪的位置,最后一年是 2022 年,即今年。

我们将设置记号和标签,使其更具可读性,因为标签很长,我们将旋转它们,使它们不会重叠。

plt**.**xticks([**-**800000, **-**600000, **-**400000, **-**200000, 0], 
           ['800,000 BCE', '600,000 BCE', '400,000 BCE',
            '200,000 BCE','0'],
             rotation**=**20)

我们还定义了 x 轴和 y 轴标签,移除图例(这是多余的)并添加标题。

plt**.**ylabel("carbon dioxide level (parts per million)")plt**.**xlabel("years before today (0 = 1950)")plt**.**legend("")plt**.**title("CO2 Concentrations")

然后,我们将生成的图形保存为图像。

plt**.**savefig('images/co2world.png')

以下是完整的代码:

这是结果图。

历史二氧化碳浓度,可读性更强的版本——图片由作者提供

我们这里有一个可读的图表,但它并不包含上面 NASA 图表中的所有信息。

给图表加注释

我们需要添加标题“几千年来,大气中的碳从未超过这条线”,并绘制标题所指的线。

caption = "For millennia, atmospheric carbon has never been above this line"plt**.**text(**-**790000,320, caption,
        verticalalignment**=**'top', horizontalalignment**=**'left',
        fontsize**=**fontsize, color **=** color)

plt**.**plot([**-**800000,0],[305,305], linestyle **=** 'dashed', 
          color**=**'red', linewidth **=** 2)

在上面的代码中,我们将标题定义为一个字符串,然后将其添加到图表中的 x/y 位置-790000,320,然后在 CO2 浓度水*为 305ppm 时从-800000,0 绘制一条水*虚线。

接下来,我们添加注释——指向 1950 年和 2022 年的标签和箭头。

plt**.**annotate("1950",xy **=** (0,312), xytext **=** (70000,306),
              arrowprops**=**dict(facecolor**=** color, shrink**=**0.1), 
              fontsize**=**fontsize, color **=** color)
plt**.**annotate("2022",xy **=** (0,417), xytext **=** (70000,411),
              arrowprops**=**dict(facecolor**=** color, shrink**=**0.1),
              fontsize **=** fontsize, color **=** color)

最后要做的事情是去掉脊骨:

ax**.**spines['top']**.**set_visible(**False**)
ax**.**spines['right']**.**set_visible(**False**)
ax**.**spines['bottom']**.**set_visible(**False**)
ax**.**spines['left']**.**set_visible(**False**)

然后将结果保存为图像文件。

plt**.**savefig('images/'co2.png', transparent **=** **True**)

结果如下:

完整的二氧化碳浓度图表—图片由作者提供

这是这个阶段的完整代码。你会注意到我已经改变了默认的颜色和字体大小,并在开始时将它们声明为变量,这样我们就可以很容易地改变它们。

创建信息图

该图现在看起来像美国宇航局的图像,但我们想把它放在一个背景图像。这不仅仅是向当前图表添加图像的问题。如果我们这样做,轴标签将在图像之外,我们希望将整个图表放置在背景图像内。

解决方案是创建一个新的 Matplotlib 图形,如下所示:

ax**.**plot([0,100],[0,100], alpha **=** 0)

这给了我们一个 100 乘 100 的图表,我们可以使用它作为画布来放置我们的图表图像。请注意,我已经将 alpha 值设置为零,这意味着图表将不可见。

我们现在可以将图表图像添加到这个新图形中:

img1 **=** plt**.**imread('images/co2.png')
ax**.**imshow(img1, aspect**=**'auto', alpha**=**1, extent**=**(1,99,1,99))

请注意,通过使用范围参数,我使图像比图形稍微小一点,但是在它的中心。

背景图像以类似的方式添加:

bground**=**plt**.**imread('images/ISS_earth.en.jpg')
ax**.**imshow(bground, aspect**=**'auto', alpha**=**0.3, extent**=**(0,100,0,100))

但是这一次,图像的范围是图形的全部。

我已经将两张图像的 alpha 值设置为图表图像为 1(这是默认值),背景为 0.3。这意味着图表将完全不透明,但背景图像将部分透明。

完整的信息图-作者提供的图片

这是第一个信息图的代码。

更复杂的信息图

信息图通常包含不止一个图表和一些说明性文字,所以这是我们接下来要尝试的。

我们将使用相同的技术创建一个空白图形,并在该图形上放置图像和(这次)文本。我们将使用已经为其中一个图表和背景创建的图像,并添加另一个图表和一些文本。

从另一个 OWID 数据集,我们可以创建一个图表,表示世界各地二氧化碳排放量的增加。这是:

各大洲二氧化碳排放量—数据来源,数据中的我们的世界,图片由作者提供

创建它的代码如下。首先,我们读取数据,然后根据我们指定的大洲列表过滤结果表。然后直接遍历这些大陆的名称,并在图表中添加一个新的绘图。

我们将结果保存为图像。

现在我们有了我们需要的图像,但是我们需要指定三个字符串中的文本。

title **=** """
CO2 Emissions
"""text1 **=** """
CO2 emissions have risen 
significantly in all parts 
of the World in modern times,
particularly in Asia.
"""text2 **=** """
For hundreds of millennia,
CO2 levels never rose above 
around 300ppm. In 1950 it 
was just over 300 and now it 
is over 400ppm
"""

我不会重复放置图像,因为这几乎是我们之前所做的重复,但是使用范围参数来将图像放置在图的右上角和左下角。

然而,文本的定位略有不同。这里有一个例子。

ax**.**text(5,80, text1,
        verticalalignment**=**'top', horizontalalignment**=**'left',
        color**=**'black', fontsize**=**18)

在这个对 ax.text 的函数调用中,前两个参数是文本的位置,第三个是文本本身,其他的不言自明。

我们现在拥有了完成信息图的所有组件和技术。这是结果,代码如下。

完整的信息图-作者提供的图片

大概就是这样。感谢阅读,我希望这是有用的,并会启发你产生自己的惊人的图形。

像往常一样,在这篇文章发表后不久,完整的代码将通过我的网站上的链接提供——你也可以在那里看到我的其他作品。

如果你想看更多的文章,请在媒体上关注我或者订阅我偶尔发的子栈简讯

https://medium.com/membership/@alan-jones

笔记

通过下面的链接,你可以在“数据世界”和美国宇航局的图片中找到原始数据。

https://ourworldindata.org/co2-and-other-greenhouse-gas-emissions https://github.com/owid/co2-data https://climate.nasa.gov/climate_resources/24/graphic-the-relentless-rise-of-carbon-dioxide/ https://www.nasa.gov/mission_pages/station/images/index.html

Apache Spark 中的合并与动态合并

原文:https://towardsdatascience.com/coalescing-vs-dynamic-coalescing-in-apache-spark-1d7824c8dbcb

参考:Pixabay

随着 Spark 3.0 中默认启用“动态合并”,Spark 应用程序中合并的使用将会增加。现在,您不再需要手动调整分区来进行洗牌,也不会感觉受到“spark.sql.shuffle.partitions”值的限制。阅读这个故事来了解更多..

分区的重要性:对于 Spark 应用程序的最佳执行来说,正确的分区集就像一个圣杯。当 Spark 应用程序的每个组成阶段都以最佳方式执行时,它将实现最佳效率。这反过来意味着每个阶段都应该在最佳数量的分区上运行,这些分区的数量可能因阶段而异。最佳分区数量的差异是因为输入数据量和计算性质通常随阶段不同而不同。

太多的小分区会增加簿记和调度的开销,而大分区会导致所需并行性的损失。类似地,繁重和内存密集型计算更喜欢小尺寸的分区,而大尺寸的分区自然适合于轻型计算。如果你想了解更多关于火花分割方面的内容,可以参考《火花分割指南》

洗牌产生阶段:Spark 作业中的大多数阶段是由于 Spark 执行引擎插入了洗牌交换操作符而产生的。此外,每个混洗交换操作符使用配置参数"spark . SQL . shuffle . partitions"来决定混洗分区的数量。因此,所有混洗产生的阶段(进行混洗读取的阶段)在相似数量的分区上运行。但是,类似数量的已配置混洗分区对于所有混洗产生的级来说可能不是最优的,导致受影响的级非最优地运行。这反过来降低了 Spark 作业的整体效率。(你可以参考揭示阿帕奇火花洗牌魔术来了解更多关于火花洗牌的过程)

下面是一个代码片段,用于模拟当未启用动态合并时,所有 shuffle born 阶段都以分区号等于"spark . SQL . shuffle . partitions"的方式运行。

图 1:模拟多个洗牌诞生阶段的代码片段

以下是在 Spark 集群上执行图 1 中所示逻辑时的阶段执行快照:

图 5 :图 1 的逻辑产生的分级快照,无动态合并

图 2 中可以看出,阶段 3、5 和 6 都是混洗产生的阶段,并且每个阶段都运行在默认值“spark . SQL . shuffle . partitions上,该值设置为 200。

为了克服“spark . SQL . shuffle . partitions带来的限制,Spark 开发人员通常使用“re partition”或“ coalesce ”转换来手动和间接调整 shuffle 分区。调整是围绕需要数据混洗的转换进行的,例如“分组”和“连接

重新分区和合并 : ' 重新分区'与数据集中的原始分区数量相比,转换允许用户将数据集重新分区为更多或更少的分区。这通常是通过指定从记录的一个或多个属性派生的重新分区键来完成的。如果在 groupby 转换前使用“re partition】,则重新分区键与分组键相同;如果在 Join 转换前使用“re partition】,则重新分区键与 Join 键相同。

当开发人员想要手动增加 shuffle 分区的数量超过“spark.sql.shuffle.partitions”指定的数量时,他们通常会在 shuffle 导致转换之前求助于' r epartition'

Coalesce’转换允许用户将数据集重新划分为与数据集中的原始分区数量相比数量更少的分区。在这里,没有必要指定任何键。

与“spark.sql.shuffle.partitions”指定的分区相比,当开发人员想要手动减少洗牌分区的数量时,他们通常会在洗牌后求助于' Coalesce' '。

虽然可以通过'重新分区或'合并'手动调整混洗的阶段分区,但这是一个反复而繁琐的过程,需要对 Spark 的执行引擎有很好的理解。为了解决这个问题,Spark 支持将'动态合并'作为一个更大的运行时优化模块的特性之一,Spark 1.6 中首次引入了'高级查询执行(AQE) '。

除了‘动态合并’,AQE的其他特性包括’将连接策略切换到广播连接’和‘优化偏斜连接’。在 Spark 3.0 之前,AQE 被默认禁用,因为它仍在不断成熟,但在 Spark 3.0 中,AQE 被默认启用,以在运行时自动优化 Spark 作业。

要使用“动态合并”,必须将以下配置设置为“”。

图 3。用于启用动态合并的配置参数。

了解“动态合并”:众所周知,洗牌由两个连续的阶段组成。第一个是写入混洗块的映射阶段(对应于配置的混洗分区号)。映射阶段之后是归约阶段,该归约阶段读取相关混洗块,根据它们的混洗分区号来组合它们,然后对每个组合的数据块运行归约逻辑。

“动态聚结”在洗牌映射和简化阶段之间起作用。实际上,在映射阶段之后,在混洗完成之前(在写入所有混洗数据块之后),它向 spark 执行引擎报告大量的统计数据,例如记录的数量和每个混洗分区的大小,关于产生的混洗分区(如配置"Spark . SQL . shuffle . partitions")的规定。

当混洗分区被合并到较低的数目时,这些报告的统计信息提示执行引擎咨询“动态合并”模块来检查潜在的优化机会。

“动态合并”参考由洗牌的映射端产生的统计数据和一些其他可配置的参数(以提供行为的灵活性),以便计算合并分区的最佳目标大小。基于所计算的目标大小,对合并的混洗分区的数量进行估计。如果估计的数量低于由“spark . SQL . shuffle . partitions”指示的数量,则在运行时,“动态合并”模块动态地插入后续的合并转换,该转换具有作为合并的混洗分区的估计数量的输入参数。

图 4:动态合并的图示

图 4 提供了“动态合并”的图示。如图所示,'spark . SQL . shuffle . partitions'设置为 4。因此,混洗的映射阶段中的两个映射任务(对应于 2 个分区)写入对应于所配置的混洗分区的 4 个混洗块。然而,在火花执行引擎已经使用了'动态合并'之后,第二和第三混洗分区被合并,因此混洗分区的总数在混洗之后的缩减器阶段(混洗产生阶段)变为 3 而不是 4。

配置“动态合并”:影响“动态合并的决策过程的各种可配置参数如下:

在 Spark 3.0 之前: 在 Spark 3.0 之前,这样的参数只有一个:

"spark . SQL . adaptive . shuffle . targetpostshuffleinputsize":控制合并后的目标大小。合并后的分区大小将接*但不会大于该目标大小。(默认值:64MB)

Spark 3.0 及以上:Spark 3.0 引入了多个参数来微调“动态合并的行为,以保持所需并行度和合并洗牌分区的最大大小之间的*衡。所有这些参数解释如下:

"spark . SQL . adaptive . advorypartitionsizeinbytes":控制合并后的目标大小。合并后的分区大小将接*但不会大于该目标大小。(默认值:64MB)

"spark . SQL . adaptive . coalesce partitions . minpartitionnum":控制合并后的最小分区数。如果未设置,则默认为等于'spark . default . parallelism'。

"Spark . SQL . adaptive . coalesce partitions . minpartitionsize":这是 Spark 3.2 中新引入的参数。它控制合并后分区的最小大小。合并的分区大小不会小于该大小。(默认值:1MB)

因此可以看出,在 Spark 3.2 之前,只使用“Spark . SQL . adaptive . shuffle . targetpostshuffleinputsize”为合并分区大小指定了一个固定的上限。但是,在 Spark 3.2 中,可以分别基于“Spark . SQL . adaptive . shuffle . targetpostshuffleinputsize”和“Spark . SQL . adaptive . coalesce partitions . minpartitionsize”的值为合并的分区大小指定固定的上限和下限。

以下是再次执行图 1 中的逻辑时 Spark 产生的阶段快照,但“动态合并”已打开:**

图 5 :图 1 逻辑产生的带有动态合并的阶段快照

图 5* 中可以看出,在启用了动态合并的情况下,在图 1 中的 46 分别对应的 514 中,洗牌分区的数量减少为 1,而对应于阶段的 9 的分区数量保持为 200(注意,启用 AQE 后,由于引入了多个跳过的阶段*,阶段编号有所改变)**

我希望上面的故事已经让你对 Coalesce 操作有了一个很好的了解,以及“动态合并”特性背后的原因。在这种背景下,我鼓励大家通过使用手动合并操作或启用动态合并功能来探索现有 Spark 作业的优化机会。

关于使用“动态合并”的指导原则,我将在下一部分介绍,在此之前,如果有任何疑问、反馈或建议,你可以通过 LinkedIn 联系我@https://www.linkedin.com/in/ajaywlan/

代码验证和测试实践

原文:https://towardsdatascience.com/code-validation-testing-practices-86a304fd3ca

组合各种断言、if 语句、pytest 等

来自像素的免费使用照片

介绍

我们编写的代码必须按照我们预期的方式运行。验证这一点的过程被称为“代码测试”或“代码验证”。包括 Git 在内的版本控制和协作工具促进了这一过程。Git 不在本文讨论的范围之内,所以我将在其他时间讨论它。但是我想向您介绍一些关键的代码验证实践,它们可以帮助您保持您编写的代码和您处理的数据的良好质量。

定义错误情况

我个人认为定义潜在的错误来源和场景对代码验证至关重要。因此,记录整个生产流程中出现的错误并定期更新该列表非常重要。

If 语句和 Assert 语句

一旦这个错误列表被充实,创建一个单独的测试/验证脚本(例如 testing.py)。检查每个错误的 If-Else 语句在这种脚本中非常有用。

断言语句是涉及使用“断言”的语句,顾名思义,断言某个逻辑为真。如果该逻辑结果为假,它将停止正在运行的脚本或程序,并返回 AssertionError。这使得调试和验证代码变得非常方便。

看看下面这个简单的例子。

number = 2
assert number > 0

number = -2
assert number > 0
Traceback (most recent call last):
    ...
AssertionError

上面的代码验证名为“number”的变量是否是大于 0 的值。如果不是,则返回 AssertionError。这种断言验证称为“比较断言”。从身份断言到成员断言,还有其他种类的断言可用(例如,检查某个元素是否是一个更大的组的一部分,比如 list)。下面的代码是成员断言的一个示例。

numbers = [1, 2, 3, 4, 5]
assert 1 in numbers
assert 2 in numbersassert 3 in numbers

assert 6in numbers
Traceback (most recent call last):
    ...
AssertionError

使用 debug

使用调试模式是 Python 中的内置常量。它默认为 True。它是一个“常量”,因为一旦 Python 解释器开始运行,它的值就不能改变。[1]

我们如何利用这个变量呢?请看看下面这个例子。

if __debug__:
    if not <some expression>:
        raise AssertionError(assertion_message)## Source: [https://realpython.com/python-assert-statement/](https://realpython.com/python-assert-statement/)

上面的代码等效于下面使用 assert 的代码。

assert <some expression>, assertion_message

我们如何使用这个 debug 变量类似于我们如何使用 assert 语句,但是一个主要的区别是 debug 为我们提供了在调试或非调试模式下运行脚本的灵活性。Python 的 python -o 命令将 debug 变量设置为 false,允许用户

Pytest

Pytest 是一个用于代码测试的 Python 库。它是一个框架,允许你使用上面的所有东西和许多其他东西来系统地测试你写的各种功能或代码。我将向您介绍一些有用的案例和 Pytest 应用程序示例。

装置

pip install -U pytest

示例 1:验证一个函数

假设我们想使用一些测试用例来验证您编写的名为“func”的函数是否按照您想要的方式工作。这里,测试用例将是 func(3) == 5。

**def** func(x): **return** x + 1**def** test_answer( ): **assert** func(3) == 5

我们将该脚本命名为 test.py,并在命令 shell 中运行以下内容:

pytest test.py

这是结果的样子。

>>>> $ pytest**=========================== test session starts ============================**platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.ycachedir: $PYTHON_PREFIX/.pytest_cacherootdir: $REGENDOC_TMPDIRcollected 1 itemtest_sample.py F                                                     [100%]================================= FAILURES =================================**_______________________________ test_answer ________________________________**def test_answer():>       assert func(3) == 5**E       assert 4 == 5****E        +  where 4 = func(3)****test.py**:6: AssertionError========================= short test summary info ==========================FAILED test_sample.py::test_answer - assert 4 == 5============================ **1 failed** in 0.12s =============================

引发 AssertionError 是因为 func(3)返回 4,但我们断言它是 5。这很好,因为我们知道 func 正在以我们想要的方式工作。

示例 2:验证执行更复杂任务的函数

Pytest 还可以用来验证执行更复杂任务的函数或部分脚本,比如从文本中提取情感。你只需要定义一个函数的预期行为和非预期行为。看看这个来自教程的例子,使用 Textblob,一个流行的用于情感提取的 NLP 库。

**## sentiment.py
### From** [https://towardsdatascience.com/pytest-for-data-scientists-### 2990319e55e6](/pytest-for-data-scientists-2990319e55e6)**from** textblob **import** TextBlob**def** extract_sentiment(text: str): '''Extract sentiment using textblob. Polarity is within range [-1, 1]''' text = TextBlob(text) return text.sentiment.polarity **def** test_extract_sentiment( ): text = “I think today will be a great day” sentiment = extract_sentiment(text) assert sentiment > 0

在测试用例中,我们将extract_sentiment函数应用于一个示例文本:“我认为今天将是伟大的一天”。我们认为该语句包含积极情绪,因此我们将 assert 语句设计为assert sentiment > 0

奔跑

pytest sentiment.py

我们得到了

>> ========================================= test session starts ==========================================

platform linux -- Python 3.8.3, pytest-5.4.2, py-1.8.1, pluggy-0.13.1collected 1 itemprocess.py .                                                                                     [100%]========================================== 1 passed in 0.68s ===========================================

测试通过了,没有出现任何错误!

将多个测试分组到一个类中

您还可以使用类在 Pytest 中执行多个测试。您可以在一个类中捆绑多个测试,如下所示:

**class** **TestClass**: **def** first_test(self): [ some test ] **def** second_test(self): [ some test ]

如果其中一个测试未能通过,您将在类似于以下内容的输出消息中得到通知:

.....................................
========================= short test summary info ==========================FAILED test_class.py::TestClass::second_test- AssertionError: assert False**1 failed**, 1 passed in 0.56s

JSON 模式验证

JSON 代表JavaSscriptOobjectNrotation。它是一种基于文本的标准格式,使用 JavaScript 对象语法表示结构化数据,因此得名于 JavaScript。该结构看起来类似于 Python 中的嵌套字典。

JSON 文件中最常见的错误之一是模式或格式问题不一致。jsonschema 库允许我们轻松地验证 JSON 文件模式。

验证 JSON 模式可以简单到两行,如下所示。

**from** jsonschema **import** validatevalidate(instance=[some json], schema=[define schema])

让我们看一个更具体的例子。

**from** jsonschema **import** validate**# Define Schema**
schema = {
"type" : "object",
"properties" :{
"name" : {"type": "string"}
"height" : {"type" : "number"},
"gender" : {"type" : "string"}, 
"ethnicity": {"type" : "string"},
"age": {"type": "number"},
}

如上定义模式后,您可以在某个测试实例上运行 validate 函数。

validate(instance={"name" : "Josh", "gender" : "Male"}, schema=schema)

如果实例与模式定义一致,那么 validate 函数将运行,不会出现任何问题或错误消息。

对以下实例运行 validate 确实会返回一条错误消息,因为 age 字段被定义为包含一个数值,但该实例却包含一个字符串。

validate(
instance={"name" : "Josh", "gender" : "Male", "ethnicity": "Asian", "age": "idk"}, schema=schema,
)>>> 
Traceback (most recent call last):
 …
ValidationError: ‘idk’ is not of type ‘number’

关于作者

数据科学家。在密歇根大学刑事司法行政记录系统(CJARS)经济学实验室担任副研究员。Spotify 前数据科学实习生。Inc .(纽约市)。即将入学的信息学博士生。他喜欢运动,健身,烹饪美味的亚洲食物,看 kdramas 和制作/表演音乐,最重要的是崇拜耶稣基督。结账他的 网站

参考

[1] Python-Assert-Statements,真正的 Python,https://realpython.com/python-assert-statement/

[2] K. Tran,面向数据科学的数据科学家 Pytest,https://towardsdatascience . com/Pytest-for-Data-Scientists-2990319 e55e 6

[3] S. Biham,How to Validate Your JSON Using JSON Schema,Towards Data Science,https://Towards Data Science . com/How-to-Validate-Your-JSON-Using-JSON-Schema-f55 F4 b 162 DCE

使用 Keras 顺序 API 编码卷积神经网络(CNN)

原文:https://towardsdatascience.com/coding-a-convolutional-neural-network-cnn-using-keras-sequential-api-ec5211126875

神经网络和深度学习课程:第 24 部分

由作者编辑的来自 PixabayGerd Altmann 的原始图像

先决条件: 用简单的图表用简单的英语解释卷积神经网络(CNN)架构(强烈推荐)

在第 23 部分中,我详细讨论了卷积神经网络(CNN)中的层和操作。如果你读过那本书,现在你就明白 CNN 是如何在幕后工作的了。

今天,我们将讨论如何使用 Keras 顺序 API 构建 CNN。我们将详细讨论如何使用Sequential()类实例化顺序模型,如何使用add()方法添加卷积、池化和密集层,如何使用Conv2D()MaxPooling2D()Dense()类构建卷积、池化和密集层。

最后,我们将使用summary()方法得到整个 CNN 架构的概要,并统计网络中总参数的数量。该信息可用于在“参数效率”方面比较 CNN 和等效 MLP。

Keras 顺序 API

Keras 中有两种类型的 API:顺序功能。今天,我们将使用顺序 API 来构建一个 CNN。在顺序 API 中,我们将层一层一层地添加到模型中(因此得名 顺序 )。使用顺序 API 很容易。然而,顺序 API 对于分支层并不太灵活,并且它不允许网络中有多个输入和输出。

顺序模型

在 Keras 中,可以通过使用Sequential()类来构建顺序模型。在这里,我们使用add()方法给模型添加层。根据 Keras 文件,

一个Sequential模型适用于一个简单的层叠,其中每一层都有一个输入张量和一个输出张量——来源:https://keras.io/guides/sequential_model/

CNN 可以被实例化为一个序列模型,因为每一层只有一个输入和输出,并且堆叠在一起形成整个网络。

from tensorflow.keras.models import Sequential**CNN = Sequential()**

在第 16 部分中,我们使用相同的Sequential()类创建了一个多层感知器(MLP)。MLP 中的每一层都只有一个输入和输出,并且堆叠在一起形成整个网络。因此,MLP 可以被实例化为序列模型。

CNN 中的层排列

在我们讨论 CNN 层之前,总结一下 CNN 中的层排列是很有用的。更多详情可在这里找到。

典型地,CNN 中的层被堆叠为,

卷积池对→卷积池对→一个*坦层→多个密集层

CNN 中的图层排列(图片由作者提供,用 draw.io 制作)

Keras Conv2D 类

CNN 中的每个卷积层都是使用简单地在二维空间中执行卷积运算的Conv2D()类创建的。换句话说,核(过滤器)的移动发生在输入图像上,跨越二维空间。

在 Keras 中,卷积层被称为 Conv2D 层。

from tensorflow.keras.layers import Conv2D**Conv2D(****filters, kernel_size,
       strides, padding,
       activation,** **input_shape)**

Conv2D 中的重要参数

  • 滤镜:滤镜数量(内核)。这也被称为特征图的深度。接受整数。我们通常将每个卷积层中的滤波器数量增加为 16、32、64、128 等等。
  • kernel_size: 指定内核(卷积)窗口的高度和宽度。这需要一个整数或两个整数的元组,如(3,3)。在大多数情况下,窗口是一个高度和宽度相同的正方形。方形窗口的大小可以指定为整数,例如,3 表示(3,3)窗口。
  • 步数:我们在输入图像上移动滤镜的步数(像素)。这需要沿着高度和宽度的步幅的元组。如果高度和宽度相同,我们可以使用整数。默认值设置为(1,1)。
  • 填充:有两种选择:"valid""same"。“有效”意味着没有填充。“相同”导致用零填充,使得当strides=1时,特征图的大小与输入的大小相同。
  • 激活:卷积层中使用的激活函数类型。默认情况下,没有激活等同于线性或身份激活。我们通常在每个卷积层使用'relu'激活函数。
  • input_shape: 将输入的高度、宽度和深度指定为一个整数元组。换句话说,这是输入图像的大小。如果第一个卷积层是模型中紧接在输入层之后的第一层,则必须在第一个卷积层中指定此参数。其他中间卷积层不包含这个参数。

input_shape被传递到第一个卷积层时,Keras 为场景后面的模型添加一个输入层,我们不需要明确指定输入层。

Keras MaxPooling2D 类

创建卷积层后,下一步是创建池层。卷积层和池层成对使用。共有两种拼版操作:最大拼版*均拼版。这里,我们使用最大池。

CNN 中的每个池层都是使用MaxPooling2D()类创建的,该类只是在二维空间中执行最大池操作。

在 Keras 中,最大池层被称为 MaxPooling2D 层。

from tensorflow.keras.layers import MaxPooling2D**MaxPooling2D(****pool_size, strides, padding****)**

MaxPooling2D 中的重要参数

  • pool_size: 池窗口的大小。默认值为(2,2)。这需要一个整数或一个元组。如果我们使用整数,相同的窗口长度将用于两个维度。
  • 步数:我们在特征图上为每个合并步骤移动合并窗口的步数(像素)。这需要沿着高度和宽度的步幅的元组。如果高度和宽度相同,我们可以使用整数。默认设置为None,采用pool_size的值。
  • 填充:有两种选择:"valid""same"。“有效”意味着没有填充。“相同”会导致以零填充,使得合并的要素地图的大小与输入的大小相同。

Keras 稠密类

CNN 中的最后几层是完全(密集)连接的层。在 Keras 中,这些层是使用Dense()类创建的。

CNN 中的多层感知器(MLP)部分是使用多个完全连接的层创建的。

在 Keras 中,完全连接的层称为密集层。

from tensorflow.keras.layers import Dense**Dense(****units, activation,** **input_shape)**

密集中的重要参数

  • 单元:层中节点(单元)的数量。这是一个必需的参数,接受一个正整数。
  • 激活:在层中使用的激活函数的类型。默认值为None,表示无激活(即线性或身份激活)。
  • 我们不需要包括这个参数,因为任何密集层都不是 CNN 中的第一层。

Keras 展*类

在 CNN 中,在最终汇集层和第一密集层之间有一个*坦层。展*层是一个单列,用于保存 CNN 中 MLP 部分的输入数据。

在 Keras 中,展*过程是通过使用flatten()类来完成的。

设计 CNN 架构

我们将在下面的场景中使用上述类型的层来构建 CNN。

假设我们有 MNIST 数据集,该数据集包含 10 个类别(0 到 9)下的大量手写数字灰度图像。我们需要创建一个 CNN,它应该能够准确地对这些图像进行分类。每个灰度(单通道)图像的大小为 28 x 28。所以,输入的形状是(28,28,1)。

我们将 CNN 的架构定义如下。

  • 卷积层数:两层,第一层 16 个滤波器,第二层 32 个滤波器,每层 ReLU 激活
  • 池层数:两层,使用最大池
  • 展*层:在最终汇集层和第一致密层之间
  • 密集层数:三层,第一层 64 单位,第二层 32 单位,最后一层 10 单位,前两层 ReLU 激活,最后一层 Softmax 激活

下面的代码块可以用来在 Keras 中定义上面的 CNN 架构。

(作者代码)

CNN 输出摘要(图片由作者提供)

读取输出

Sequential()类的summary()方法给出了输出摘要,其中包含了关于神经网络架构的非常有用的信息。

在上面的输出中,图层信息按从上到下的顺序列在左侧。第一层在顶部,最后一层在底部。请注意,这里没有显示输入层。

在中间的列中,您可以看到每个层的输出形状。例如,第一 Conv2D 层具有(无,14,14,16) 的输出,其指示在执行第一卷积运算之后特征图的维度。特征图的大小是 14×14,深度是 16,因为使用了 16 个过滤器。元组中的第一个值是 None ,它表示训练实例的数量(批量大小)。一旦输入数据输入到模型中,就可以确定这一点的确切值。就目前而言,它注定

右栏包括每层中涉及的参数数量。请注意,池化层和展*层没有参数!

展*层的形状为(无,128)。这意味着 MLP 部分的输入层形状是(128,)。

输出的底部显示了网络中所有参数的数量。

比较模型

  • 在第 16 部分中,我们创建了一个多层感知器(MLP)模型来分类 MNIST 手写数字。在那里,我们得到了 269,322 个参数,这对于那种小型分类任务来说是一个巨大的数字。当处理图像数据时,MLP 不是参数有效的。
  • 作为该问题的解决方案,在第 17 部分中,我们将主成分分析(PCA)应用于图像数据,并构建了相同的 MLP 模型。在那里,我们只有 8874 个参数。我们能够将参数数量减少 30 倍,同时还能获得更好的性能。
  • 今天,我们使用了不同的神经网络架构(CNN)而不是 MLP。我们只有 15466 个参数!因此,CNN 是参数有效的。

你喜欢哪种型号?

MLP 用 PCA 模型还是 CNN 模型?我的答案是 CNN。当处理图像数据时,CNN 是比 MLP 更好的选择。这是因为,

  • 它可以减少参数的数量,同时保持图像的空间信息(相邻像素之间的关系)。需要空间信息来保持图像中的某些模式。
  • 如果对 RGB 图像使用 MLP,您将获得大量参数。此外,将 PCA 应用于 RGB 图像是复杂的,并且不太实用。

今天的帖子到此结束。

如果您有任何问题或反馈,请告诉我。

我希望你喜欢阅读这篇文章。如果你愿意支持我成为一名作家,请考虑 注册会员 以获得无限制的媒体访问权限。它只需要每月 5 美元,我会收到你的会员费的一部分。

https://rukshanpramoditha.medium.com/membership

非常感谢你一直以来的支持!下一篇文章再见。祝大家学习愉快!

阅读下一篇(推荐)

阅读我的 神经网络与深度学习课程 中的所有其他文章。

点击此图片进入我的神经网络和深度学习课程(作者截图)

鲁克山普拉莫迪塔
2022–06–27

用 Python 编写家庭入侵系统/运动探测器

原文:https://towardsdatascience.com/coding-a-home-intruder-system-motion-detector-with-python-22f5ba8bcca0

通过编写您自己的安全摄像机,了解如何检测运动、处理和分析图像

(图片由阿尔伯托·罗德里格斯桑塔纳未拉车

在许多情况下,分析和挖掘图像和视频流中的数据非常有用。在这篇文章中,我们将关注一个非常基本的技术:检测流中的运动,以便我们可以隔离运动的东西,并应用进一步的分析和/或检测。

的目标学习运动检测如何工作,并学习如何应用所需的技术,这样你就可以在自己的项目中使用它们;想想检测运动的图像处理和在视频流中显示运动的方法。本文的最终结果将是一些代码,例如,可以应用于家庭入侵系统或野生动物摄像机。我们来编码吧!

在开始之前

当然,在处理视频流时,可能会涉及到数据。始终考虑处理清晰可见人物图像的潜在风险。查看以下文章,了解如何降低处理此类数据的潜在风险:

https://mikehuls.medium.com/image-analysis-for-beginners-detect-and-blur-faces-with-a-simple-function-60ba60753487

结果呢

让我们先看看我们的目标是什么:

最终结果(作者提供的 gif)

如您所见,我们能够检测视频中是否检测到运动,甚至隔离运动(绿色矩形)。请注意,像改变灯光或摇摆草地这样非常小的动作会被忽略。在接下来的部分中,我们将探索这是如何实现的。

战略

我们将每 10 秒创建一个截图,并将每一帧与该截图进行比较。如果有任何差异,我们将通过在检测到移动的区域周围画一个大的绿色矩形来显示它们。此外,我们将添加一个红色的大文本,上面写着“检测到移动!”。

该过程可以用以下步骤来描述:

  1. 获取摄像机记录的画面
  2. 设置比较框;我们将把后面的每一帧与这一帧进行比较,这样我们就可以检测到变化(即运动)
  3. 分析每一帧:获得与比较帧的差异
  4. 过滤差异:只考虑足够大的区域
  5. 在差异周围画绿色矩形
  6. 搞定了。

第一步。阅读视频

我们将使用 Python 和一个名为 OpenCV 和 PIL (Python Image Lib)的包。像这样安装它们:

pip install opencv-python pillow

现在让我们设置一些允许我们读取帧的代码。因为我想创建一个好看的例子,所以我选择录制我的屏幕的一部分,显示一个 Youtube 视频,显示入侵者如何进入某人的后院。如果你想使用你的网络摄像头或其他摄像头,我会给你介绍下面的文章。

因为我只录制我的屏幕的一部分,所以我需要屏幕顶部,屏幕机器人等。参数,以便我可以很好地“抓取”(截图)它们:

我们将跳到 X 行,在那里我们读取帧。前面的代码将在接下来的部分中变得清晰。我们启动一个无限 while 循环,并使用 ImageGrab 创建一个屏幕截图,我们将把它转换成一个 Numpy-array。这是一个保存截图中每个像素的 RGB 值的数组。

第 2 部分:预处理

在这一部分中,我们将准备用于检测运动的图像。

OpenCV 默认支持 BGR(查看上面的文章),所以我们需要将 BGR 转换成 RGB。我们稍后将需要img_rgb,因为我们想要显示它。接下来的两行涉及到将 RGB 图像转换成黑白图像。这是因为像素阵列现在只需要“保持”一个值(黑度),而不是三个值(红、绿和蓝)。最后,我们将图像模糊一点,这样非常小的差异就不那么明显了。

第三部分。比较图像

首先我们必须设置比较框架,因为我们需要一些东西来比较我们的框架。我们每隔 10 秒钟就会这样做(这就是第 1 部分中的strt_time的用途。

接下来我们使用一个名为absdiff的 cv2 函数。这将从比较帧中减去当前帧的像素值(“黑度”)。结果看起来像这样:

我们的视频与对比框架有何不同(作者提供的 gif)

与比较框架不同的值变成白色;剩下的是黑色。

第四部分。过滤

也许有时候树枝会动,云会在太阳前面移动,或者一片垃圾会吹过街道。我们不希望每一个微小的差异都会触发我们的警报系统。

在这个代码中,确定了每个像素的色差必须至少为 20(255 分)。满足该阈值的每个像素将变成 100%白色,其余的将变成 100%黑色。

第五部分。视觉反馈

这是最精彩的部分。我们已经分析了我们的图像,现在是时候得出的结论了(双关语)。

首先,我们将找到所有的轮廓,这些是可以组合在一起的阈值框架的区域。将轮廓想象成 MS paint 中的填充桶。

接下来,我们将遍历每个轮廓,如果面积足够大,我们将计算轮廓周围的外接矩形。我们将使用它在轮廓周围绘制绿色矩形。如果至少有一个轮廓足够大,我们也插入一个文本。注意,我们在之前保存的img_rgb帧上绘制了矩形和文本。

最后一部分是展示结果。

(作者 gif)

结论

通过这篇文章,我希望已经展示了一些准备、处理和分析图像的方法,以便您从图像中提取和分析数据。你可以在这里找到这篇文章 的完整代码

我希望这篇文章像我希望的那样清楚,但如果不是这样,请让我知道我能做些什么来进一步澄清。同时,请查看我的关于各种编程相关主题的其他文章:

编码快乐!

—迈克

喜欢我正在做的事情吗? 跟我来!

https://mikehuls.medium.com/membership

在 NumPy 中从头开始编写神经网络

原文:https://towardsdatascience.com/coding-a-neural-network-from-scratch-in-numpy-31f04e4d605

通过从零开始实现人工智能来加深您对人工智能的理解

附身摄影Unsplash 上拍照

介绍

在本文中,我将使用 NumPy 从头开始介绍人工神经网络的开发。这个模型的结构是所有人工神经网络中最基本的——一个简单的前馈网络。我还将展示这个模型的 Keras 等价物,因为我试图让我的实现‘Keras-esque’。虽然与变压器等其他神经网络相比,前馈体系结构是基本的,但这些核心概念可以推广到构建更复杂的神经网络。这些话题本质上是技术性的。关于人工智能更概念性的文章,请查看我的另一篇文章,揭开人工智能的神秘面纱

目录

  • 架构概述
    —正向传递
    —反向传递
  • NumPy 实现 —数据
    —构建层
    —构建网络
    网络 : 正向传递
    : 正向传递
    —执行正向传递/完整性检查
    网络 : 反向传递
  • 结论

术语词汇表

  • X =输入
  • y =标签
  • W =重量
  • b =偏差
  • Z =和 W 加上 b 的点积
  • A =激活(Z)
  • k =类别数
  • 小写字母表示向量,大写字母表示矩阵

体系结构

前进传球

点积

首先,我们计算输入和权重的点积,并添加一个偏差项。

点积+偏差。图片作者。

第二,我们将第一步中获得的加权和通过一个激活函数。

这两种操作都是基于元素的,并且简单明了。所以,我就不深入了。更多关于点积和激活函数的信息,请点击点积激活函数。这些计算发生在每个隐藏层的每个神经元中。

神经元计算概述。图片作者。

函数符号—模型预测。图片作者。

激活功能

在我的实现中,我们在隐藏层使用 ReLU 激活,因为这很容易区分,而在输出层使用 Softmax 激活(下面将详细介绍)。在未来的版本中,我将把它构建得更加健壮,并启用这些激活功能中的任何一个。

常用激活功能:

乙状结肠激活功能。图片作者。

Tanh 激活函数。图片作者。

ReLU 激活功能。图片作者。

偶数道次

损失函数

我们首先计算损耗,也称为误差。这是衡量模型有多不正确的标准。

损失是一个微分目标函数,我们将训练模型使其最小化。根据您尝试执行的任务,您可以选择不同的损失函数。在我的实现中,我们使用分类交叉熵损失,因为这是一个多分类任务,如下所示。对于二元分类任务,可以使用二元交叉熵损失,对于回归任务,可以使用均方误差。

分类交叉熵损失。图片作者。

这给我造成了一些困惑,所以我想详细说明一下这里发生了什么。上面的公式暗示标签是一键编码。Keras 希望标签是一次性的,但是我的实现不希望如此。这里有一个计算交叉熵损失的例子,以及为什么没有必要对标签进行一次性编码的例子。

给定来自单个样本的以下数据,独热编码标签(y)和我们的模型预测(yhat),我们计算交叉熵损失。

y = [1,0,0]

ŷ = [3.01929735e-07,7.83961013e-09,9.9999690 e-01]

计算带有独热编码标签的交叉熵损失。图片作者。

正如您所看到的,这个示例中正确的类是零,由 y 数组的零索引中的 1 表示。我们将输出概率的负对数乘以该类别的相应标签,然后对所有类别求和。

你可能已经注意到了,除了零索引,我们得到零,因为任何乘以零的东西都是零。这归结起来就是我们在正确类别的相应索引处的概率的负对数。这里正确的分类是零,所以我们取零索引处概率的负对数。****

零索引处概率的负对数。图片作者。

总损耗是所有样本的*均值,在等式中用 m 表示。为了得到这个数字,我们对每个样本重复上述计算,计算总和并除以样本总数。

随机梯度下降

现在我们已经计算出了损失,是时候把它降到最低了。我们首先计算相对于输入参数的输出概率的梯度,并将梯度反向传播到每一层的参数。

在每一层,我们执行与正向传递类似的计算,除了不是只对 Z 和 A 进行计算,我们必须对每个参数(dZ,dW,db,d A)执行计算,如下所示。

隐藏层

隐藏层 dZ。图片作者。

当前层的权重渐变。图片作者。

当前层的偏置梯度-样本之和。图片作者。

激活梯度—这是下一层的 dA[L]。图片作者。

在输出层有一个 dZ 的特例,因为我们使用的是 softmax 激活。本文稍后将对此进行深入解释。

NumPy 实现

数据

我将为这个模型使用简单的虹膜数据集。

from sklearn.preprocessing import LabelEncoderdef get_data(path):
    data = pd.read_csv(path, index_col=0) cols = list(data.columns)
    target = cols.pop() X = data[cols].copy()
    y = data[target].copy() y = LabelEncoder().fit_transform(y) return np.array(X), np.array(y)X, y = get_data("<path_to_iris_csv>")

初始化图层

密集层类

初始化网络

ANN 类

网络——向前传递

体系结构

让我们从动态初始化网络架构开始。这意味着我们可以为任意数量的层和神经元初始化我们的网络架构。

人工神经网络架构

我们首先创建一个矩阵,将我们的维度(特征数量)映射到输入层的神经元数量。从这一点来看,它非常简单——新层的输入维度是前一层中神经元的数量,输出维度是当前层中神经元的数量。

model = Network()
model.add(DenseLayer(6))
model.add(DenseLayer(8))
model.add(DenseLayer(10))
model.add(DenseLayer(3))model._compile(X)print(model.architecture)**Out -->**[{'input_dim': 4, 'output_dim': 6, 'activation': 'relu'},
 {'input_dim': 6, 'output_dim': 8, 'activation': 'relu'},
 {'input_dim': 8, 'output_dim': 10, 'activation': 'relu'},
 {'input_dim': 10, 'output_dim': 3, 'activation': 'softmax'}]

因素

现在我们已经创建了一个网络,我们需要再次动态初始化我们的可训练参数(W,b ),用于任意数量的层/神经元。

初始化人工神经网络参数

如您所见,我们在每一层都创建了一个权重矩阵。

该矩阵包含每个神经元的向量和每个输入特征的维度。

一层中的每个神经元都有一个具有维度的偏置向量。

还要注意,我们设置了一个 np.random.seed(),以便每次都能获得一致的结果。试着注释掉这行代码,看看它对你的结果有什么影响。

model = Network()
model.add(DenseLayer(6))
model.add(DenseLayer(8))
model.add(DenseLayer(10))
model.add(DenseLayer(3))model._init_weights(X)
print(model.params[0]['W'].shape, model.params[0]['b'].shape)
print(model.params[1]['W'].shape, model.params[1]['b'].shape)
print(model.params[2]['W'].shape, model.params[2]['b'].shape)
print(model.params[3]['W'].shape, model.params[3]['b'].shape)**Out -->**(6, 4) (1, 6)
(8, 6) (1, 8)
(10, 8) (1, 10)
(3, 10) (1, 3)

正向传播

在网络中执行一次全转发的功能。

网络正向传播

我们将前一层的输出作为输入传递给下一层,用 A_prev 表示。

我们将输入和加权和存储在模型存储器中。这是执行向后传递所需要的。

层—向前传递

激活功能

正向传递层方法

记住这些是基于元素的函数。

热卢

用于隐藏层。概述部分提到了函数和图形。下面是我们调用 np.maximum()时发生的情况。

if input > 0:
 return input
else:
 return 0

Softmax

用于最后一层。该函数获取 k 个实数值的输入向量,并将其转换为 k 个概率的向量,其总和为 1。

函数符号。图片作者。

其中:

获取非标准化值。图片作者。

对每个元素进行归一化,以获得概率分布。图片作者。

单样本示例:

输入向量= [ 8.97399717,-4.76946857,-5.33537056]

Softmax 计算如下公式。图片作者。

单层正向传播

层向前传播

其中:

  • 输入= A_prev
  • 权重=当前层的权重矩阵
  • 偏置=当前层的偏置向量
  • 激活=当前层的激活功能

我们在网络的 _forwardprop 方法中调用这个函数,并将网络的参数作为输入传递。

执行向前传球

model = Network()
model.add(DenseLayer(6))
model.add(DenseLayer(8))
model.add(DenseLayer(10))
model.add(DenseLayer(3))model._init_weights(X)
out = model._forwardprop(X)print('SHAPE:', out.shape)
print('Probabilties at idx 0:', out[0])
print('SUM:', sum(out[0]))**Out -->**
SHAPE: (150, 3)Probabilties at idx 0: [9.99998315e-01, 1.07470169e-06, 6.10266912e-07]SUM: 1.0

完美。一切都在一起了!我们有 150 个实例映射到我们的 3 个类,每个实例的概率分布总和为 1。

网络——后向通道

反向传播

在网络中执行一次完整的反向传递的功能。

网络反向传播

我们从计算分数的梯度开始。用 dscores 表示。这是概述部分提到的 dZ 的特例。

根据 CS231n :

我们现在希望了解 z 内部的计算值应该如何改变,以减少本示例对完全目标有贡献的损耗 Li。换句话说,我们想得到梯度∂Li/∂zk.

损耗 Li 是从 p 计算的,而 p 又取决于 z。对于读者来说,使用链式法则来推导梯度是一个有趣的练习,但在很多东西相互抵消之后,最终证明它非常简单且易于理解:“

根据 Stanfords CS231n 推导 dscores。来源:斯坦福 CS231n

其中:

引入术语 Li 来表示损失。在概述部分对此进行了深入探讨。来源:斯坦福 CS231n

引入术语 pk 来表示归一化概率(softmax 输出)。来源:斯坦福 CS231n

单样本示例:

对于每个样本,我们找到正确类别的索引并减去 1。相当简单!这是上面代码块中的第 9 行。由于 dscores 是一个矩阵,我们可以使用样本和相应的类标签进行双重索引。

输入向量= [9.99998315e-01,1.07470169e-06,6.10266912e-07]

输出向量= [-1.68496861e-06,1.07470169e-06,6.10266912e-07]

这里正确的索引是零,所以我们从零索引中减去 1。

注意,我们从输出层开始,然后转移到输入层。

层—向后传递

活化导数

ReLU 导数函数

ReLU 导数的函数符号。图片作者。

单层反向传播

该函数将梯度反向传播到层中的每个参数。

层后退法

再次展示这些,因为它们非常重要。这些是反向传递计算。

dZ-soft max 层的特例。图片作者。

隐藏层 dZ。图片作者。

dW —在所有层中保持一致。图片作者。

db —在所有层中保持一致。图片作者。

dA[L-1] —在所有层中保持一致。图片作者。

大家可以看到,除了 dZ 的计算,每一层的步骤都是一样的。

执行向后传递

model = Network()
model.add(DenseLayer(6))
model.add(DenseLayer(8))
model.add(DenseLayer(10))
model.add(DenseLayer(3))model._init_weights(X)
out = model._forwardprop(X)
model._backprop(predicted=out, actual=y)print(model.gradients[0]['dW'].shape, model.params[3]['W'].shape)
print(model.gradients[1]['dW'].shape, model.params[2]['W'].shape)
print(model.gradients[2]['dW'].shape, model.params[1]['W'].shape)
print(model.gradients[3]['dW'].shape, model.params[0]['W'].shape)**Out -->** (10, 3) (3, 10)
(8, 10) (10, 8)
(6, 8) (8, 6)
(4, 6) (6, 4)

哇,太美了。请记住,渐变是从输出图层开始计算的,向后移动到输入图层。

火车模型

准确性/损失方法

要理解这里发生了什么,请重新访问概述部分,在那里我深入讨论了损失的计算并给出了一个例子。

现在是在每次迭代(历元)之后执行参数更新的时候了。让我们实现 _update 方法。

更新参数

终于到了把所有的东西放在一起并训练模型的时候了!

训练模型方法

数字模型

model = Network()
model.add(DenseLayer(6))
model.add(DenseLayer(8))
model.add(DenseLayer(10))
model.add(DenseLayer(3))model.train(X, y, 200)**Out -->** EPOCH: 0, ACCURACY: 0.3333333333333333, LOSS: 8.40744716505373
EPOCH: 20, ACCURACY: 0.4, LOSS: 0.9217739285797661
EPOCH: 40, ACCURACY: 0.43333333333333335, LOSS: 0.7513140371257646
EPOCH: 60, ACCURACY: 0.42, LOSS: 0.6686109548451099
EPOCH: 80, ACCURACY: 0.41333333333333333, LOSS: 0.6527102403575207
EPOCH: 100, ACCURACY: 0.6666666666666666, LOSS: 0.5264810434939678
EPOCH: 120, ACCURACY: 0.6666666666666666, LOSS: 0.4708499275871513
EPOCH: 140, ACCURACY: 0.6666666666666666, LOSS: 0.5035542867669844
EPOCH: 160, ACCURACY: 0.47333333333333333, LOSS: 1.0115020349485782
EPOCH: 180, ACCURACY: 0.82, LOSS: 0.49134888468425214

检查结果

******

图片作者。**

尝试不同的架构

model = Network()
model.add(DenseLayer(6))
model.add(DenseLayer(8))
# model.add(DenseLayer(10))
model.add(DenseLayer(3))model.train(X, y, 200)

******

图片作者。**

喀拉斯当量

from keras.models import Sequential
from keras.layers import Dense
import tensorflow as tf
from tensorflow.keras.optimizers import SGDohy = tf.keras.utils.to_categorical(y, num_classes=3)model2 = Sequential()
model2.add(Dense(6, activation='relu'))
model2.add(Dense(10, activation='relu'))
model2.add(Dense(8, activation='relu'))
model2.add(Dense(3, activation='softmax'))model2.compile(SGD(learning_rate=0.01), loss='categorical_crossentropy', metrics=['accuracy'])model2.fit(x=X, y=ohy, epochs=30)

检查结果

******

图片作者。**

Keras 不设置 random.seed(),所以每次运行之间会得到不同的结果。如果从 _init_weights 方法中删除这一行,NumPy 网络的行为是一样的。

结论

如果你已经走到这一步,那么恭喜你。这些都是令人费解的话题——这也是为什么我必须把这篇文章放在一起的原因。来验证我自己对神经网络的理解,并希望将这些知识传递给其他开发者!

完整代码在此: NumPy-NN/GitHub

我的 LinkedIn: 约瑟夫·萨森| LinkedIn

我的邮箱:sassonjoe66@gmail.com

请不要犹豫,取得联系,并呼吁任何错误/错误,你可能会遇到的代码或数学!

感谢阅读。

编写智能战舰代理程序

原文:https://towardsdatascience.com/coding-an-intelligent-battleship-agent-bf0064a4b319

发展一种从随机猜测到超人表现的策略

使用概率搜索的战列舰游戏示例。图片作者。

战舰似乎是一个非常流行的测试各种人工智能策略的格式。在网上你可以找到各种各样的方法,从强化学习到遗传算法到蒙特卡罗方法。关于这个话题的最受关注的媒体之一是由 Vsauce2 频道制作的 Youtube 视频。在视频中,他们描述了一种基于概率的算法,这种算法导致的策略远比人类玩家合理使用的策略复杂。这个视频引导我到了数据遗传学的尼克·贝里的博客文章,他非常详细地描述了这个算法。他的文章以一个相对较弱的算法开始,但他将其构建成一个强大的解决方案,可以显著优于许多其他方法。他对算法的解释非常精彩,但是我找不到他实现的任何源代码。我决定重新创建他的策略,并把源代码公开发布给任何想摆弄或修改它的人。对于那些想要的人来说,一切都可以在我的 Github 上找到。

战舰规则

我假设任何阅读这篇文章的人都熟悉战舰的基本规则,但我将快速回顾一些对 AI 实现特别重要的规则。首先,没有船只可以重叠:虽然船只可以与其他船只相邻,但它们不能堆叠在一起。第二,一旦对手的船的所有方格都被击中,对手必须宣布他们的船被击沉。这是至关重要的信息,因为它表明我们不再需要在这个特定的地区继续射击。最后,棋盘上总共有五艘船,长度分别为五、四、三、三和二。

战列舰甲板的例子。红色 X 表示命中,黑色 X 表示未命中。图片作者。

初始策略

一个显而易见的策略是尝试完全随机地猜测,尽管这很糟糕。正如您所料,这会导致非常差的性能。如果没有别的,这个策略将会给我们一个性能的下限,我们可以用它来作为其他策略的基准。

使用随机猜测的示例游戏。图片作者。

我用这种策略模拟了 100 万次游戏,以生成一个游戏时长的分布。对于随机策略,一场游戏的中值长度是 97,这意味着在大多数情况下,当它击沉最后一艘船时,它非常接*于覆盖所有 100 个方格。下图显示了完成一场游戏所需击球次数的分布。

作者图片

改进我们策略的第一步是将点击量考虑在内。当算法击中一个里面有船的方块时,它不应该只是移动到下一个随机的方块。它应该向被击中的方格附*的方格开火,这样它就能击沉船只。借用尼克·贝里的术语,我们称之为狩猎/目标战略。当算法处于搜索模式时,它会像以前一样随机触发。然而,当它击中一个有船在里面的方块时,策略转换到目标模式。我们将维护一个“目标方块”列表,算法应该优先于所有其他方块。当击中时,与击中方块相邻的所有方块都被添加到目标列表中。在随后的回合中,该算法将从列表中一次弹出一个方块,并向它们开火。当列表为空时,算法切换回搜索模式。

这是一个简单的算法,但是相对于随机猜测策略已经有了很大的改进。正如你在下面看到的,一旦算法击中一艘船,它就会在相邻的方格中布满子弹。这更让人想起一个(坏的)人类对手会怎么玩。

狩猎/目标策略示例游戏。图片作者。

狩猎/目标策略的*均游戏时间是 65 步。不太好,因为我们在搜索所有敌舰的时候仍然覆盖了超过一半的区域。然而,该算法有时确实很幸运。在 1,000,000 次模拟中,最短的游戏时间是 24 分钟。

作者图片

接下来,我们将寻求改进算法搜索船只的方式。我们可以想象只有一艘船在甲板上。让我们假设我们想要做的只是追捕游戏中最长的船,即航母(长度为 5)。我们不需要在每个方格上开火来找到它,相反我们可以意识到当载体在棋盘上时,载体必须接触某些方格。看看下面的图片。要将载体安装到板上而不接触其中一个标有 x 的方块是不可能的。

搜索五号航母的射击模式。图片作者。

当算法搜索载波时,它应该从上面的模式中随机选择镜头。创建这种模式很简单。纸板是零索引的,这意味着行和列的范围从 0 到 9。我们将行和列的索引相加,并检查它们是否能被 5 整除。如果是的话,这个正方形就在我们的射击模式中。我们可以把这个想法推广到任何长度的船。

注意,船的长度越小,这种方法消除的方块就越少。然而,即使对于巡逻艇(长度为 2),这种新的射击模式也消除了棋盘上一半的方格。

狩猎长度为 2 的巡逻艇的射击模式。图片作者。

该算法将需要使用仍然在板上的最小的船的模式。在游戏开始时,射击模式将是巡逻艇的射击模式。然而,当巡逻船被消灭时,射击模式将切换到下一个最小的船的模式。为了让这种策略发挥最大效果,我们需要运气,希望算法首先击中较小的船只。

作者图片

在原始和改进的搜索/目标策略之间有一些边际收益。值得注意的是,原始策略的最坏情况比改进后的策略要糟糕得多。在最坏的情况下,最初的策略击中了所有 100 个方块,而改进的版本从未超过 88 个方块。

概率策略

为了进一步改进算法的目标策略,我们转向一种新的方法。我们可以使用概率来告知我们的决定,而不是在目标阶段选择随机方块来猜测。如果我们假设船只是随机放置的,我们更有可能在棋盘的中间看到一艘船,而不是在边缘或角落。这仅仅是因为有更多的方法可以把船放在棋盘的中心而不是外面。考虑一个像第四行第四列那样的中央广场。载体可以以 10 种不同的方式水*放置,也可以以 10 种不同的方式垂直放置。这使得正方形的权重为 20。相比之下,第 0 行第 0 列的角方块只有两种放置载体的方式,权重为 2。通过对棋盘上的每个方格进行这些计算,我们可以生成一张“热图”,显示哪些方格上最有可能有载体。我们可以对每艘船都这样做,并结合概率得到船上所有船的概化热图。为了找到下一个要开火的方块,我们选择热图中具有最高值的方块。

航母的初始概率热度图(左)与所有舰船的综合热度图(右)。图片作者。

一旦计算机开了一枪,它就会收到关于这一枪是命中还是未命中的信息。我们可以使用这些信息来重新计算和更新热图。在失误之后,船只被放置在失误附*的方格中的可能性减少了,所以失误周围的方格降低了概率。

当一艘船被击中时,我们人为地对被击中的方格进行加权,这样算法就会找出剩余的方格并击沉它。在船被击沉后,我们移除这些人为的概率权重,因为它们不再有用。这将阻止算法向已经被摧毁的船只附*的空间开火。

两种潜在的现实:未命中(左)和命中(右)。当出现失误时,失误周围的方块权重较低。当有击中时,击中周围的方块被赋予更高的权重。图片作者。

对击中后算法选择方块的方式可以进行最后的改进。如果算法在水*方向上击中了一艘船,那么它应该优先考虑水*方向上相邻的方块。垂直定向的船只也是如此。

所有策略的博弈长度分布(左),所有策略的累积分布函数(右)。图片作者。

我们看到这一新策略的效率大幅提高。一场游戏的中值长度是 43,100 万次模拟中最短的游戏是 18 步。下面是一个完整的概率策略游戏示例。

一个用概率算法玩的示例游戏。作者 GIF。

对于我上面的 Pygame 可视化,我不得不从头开始编写热图着色系统。如果任何读者对如何改进着色以使其更接* Matplotlib 产生的情节有想法或建议,我绝对愿意听取他们的意见。

潜在的改进

我想指出的是,这种算法是为了在随机性存在的情况下实现最佳性能而设计的。然而,大多数对手不会随意将他们的船放在棋盘上。有些人可能更喜欢把他们的船放在棋盘的角落或边缘。这种算法在这种策略下表现不佳,因为它假设大多数船只将位于中心位置。为了说明这一点,我们可以考虑人为地加重棋盘外侧的方块。

我也认为如果人工智能在许多游戏后修改它的策略会很有趣。如果对手倾向于在一局接一局的船只布局游戏中采用相似的策略,那么我们可以对对手喜欢放置船只的棋盘区域进行加权。

最后的想法

我想感谢 Nick Berry 在战舰上的精彩帖子,希望我能够用自己的实现来公正地处理它。如果你没看过他的解释,我强烈推荐。

如果你觉得这篇文章有趣,你可能想看看我的源代码,它在 Github 上公开。感谢阅读!

编码面试:下一次置换

原文:https://towardsdatascience.com/coding-interview-next-permutation-1cd07dcb20db

一个在编码面试中经常出现的排列问题的教程。

图一。下一个排列|图标由 JungSa

排列(和组合)问题在代码面试中经常出现,并且由于有效解决问题所需的基础知识而具有挑战性。

在这篇博文中,我们将看到一些解决下一个排列问题的方法和技术,从基于蛮力的方法开始,以找到更有效的解决方案。

我们开始吧!

问题陈述

让我们来看看问题的定义:

整数数组的下一个排列是其整数的下一个更大的排列。更正式的说法是,如果数组的所有排列都在一个容器中按照它们的字典顺序排序,那么该数组的下一个排列就是排序容器中它后面的排列。[ 1 ]

例如:

**Case 1**
**Input**: nums = [1,2,3]
**Output**: [1,3,2]**Case 2**
**Input:** nums = [3,2,1]
**Output:** [1,2,3]**Case 3
Input**: nums = [2,0,2,2]
**Output**: [2,2,0,2]

简而言之,该问题需要以下排列作为输入,给定从给定排列开始的升序。如果没有更大的排列,则返回具有最小字典序值的排列。在图 2 中,我们看到了从按字典顺序排列的[1,2,3]生成的排列的表示。

图二。从[1,2,3]开始的所有字典序排列的表示。作者用红色|图像表示输入的下一个排列。

一旦明确了问题的定义和范围,让我们继续解决方案,从基于蛮力的方法开始,然后应用一种技术来获得更有效的解决方案。让我们去吧!

方法 1:暴力

强力方法的关键思想是生成所有排列,按字典顺序排序,并提取由input确定的下一个排列。在代码片段 1 中,我们可以看到暴力方法的实现。

代码片段 1。下一个排列问题的强力解决方案

给定一组n元素的排列数为n!,那么运行时复杂度为 O(N!)和 O(N)表示内存使用情况。值得一提的是,在代码片段 1 中展示的基于暴力的方法中,主要的运行时复杂度是 O(N!).尽管实现最小堆的复杂度为 O(N Log N ),并且寻找下一个排列的复杂度为 O(N ),但是最终的主要复杂度为 O(N!).

从这个问题衍生出的一个对代码面试非常有用的重要话题是使用特定的递归技术(比如backtracking)生成排列,对此我将写一整篇博客。与此同时,您可以看看第 10 行的代码片段 1,其中 función backtrack()是以一种高效而优雅的方式为置换生成而定义的。

继续问题,不生成所有的排列,怎么找到下一个排列?好吧,让我们在下一节详细看看。

方法 2:诀窍

下一个排列问题,乍一看,似乎有很高的复杂程度。然而,它比看起来要简单。事实上,在最简单的形式中,问题可以一次解决。“诀窍”是知道、推断或以这种方式找出解决问题的步骤。

步骤描述如下:
1。从后向前遍历数组nums,直到找到序列中的第一个非递增元素,比如说nums[i-1]
2。如果索引i-1不为零,则找到并交换大于元素nums[i-1]的第一个元素。如果索引i-1是零,这意味着该序列是按降序排列的,因此它是可以形成的最大序列,因此,该过程通过返回相反的序列而结束。
3。从索引i开始的元素被反转。

在图 3 中,我们可以看到解决下一个排列问题时两种情况的可视化表示。

图 3。下一个排列示例。|图片作者

在代码片段 2 中,我们可以看到有效解决下一个置换问题的函数的定义。

代码片段 2。下一个排列,最佳方法。

由于nums数组最多被遍历两次,运行时复杂度是线性的 O(N ),内存使用保持恒定 O(1 ),因为不需要额外的内存来解决问题。

太好了,如果你在代码面试中遇到下一个排列问题,现在你有了一个解决它的选择。

结论

排列和组合问题需要特定的技术来有效地解决它们(例如回溯)。然而,这个特殊的问题可以用另一种方法解决。解决这个问题变得有争议,因为它可以被视为“知道诀窍与否”,这是可以理解的,但这不是我们(那些在面试中解决这类问题的人)可以做出的决定。控制。

参考

[1]https://leetcode.com/problems/next-permutation/

编码面试:两个总和

原文:https://towardsdatascience.com/coding-interview-two-sum-bbc615c0e952

一个博客教程来理解二和问题,它的变种,以及一些有效解决它的方法

图一。两笔加总|作者图片|图标

在技术面试中,最常出现的问题之一就是Two-Sum。在这篇博客教程中,我们将看到如何解决Two-Sum问题,它的变体以及用Big(O)符号表示的时间和空间复杂性。

我们开始吧!

问题陈述

让我们来看看这个问题的基本定义:

给定一个整数数组nums和一个target,如果有任何一对数字的总和为target,则返回true,否则返回false

例如:

**Case 1**
**Input**: nums = [3,1,5,2,9] target = 7
**Output**: True**Case 2**
**Input**: nums = [3,1,5,2,9] target = 1
**Output**: False

值得一提的是,结果的类型可以是boolean(如上例所示),或者可能需要返回总计为target的一对数字的索引。

在开始处理和解决问题的一些方法之前,强烈建议澄清疑问并确定问题的范围。你可以问面试官一些问题来帮助你定义问题的范围:
-数组排序了吗?
-内存使用有什么限制吗?
-运行时是否有任何约束?

出于实际目的,我们假设现在数组nums将是未排序的,并且没有内存和运行时约束。

那么,我们怎么解决呢?一步一步来,首先,我们会看到如何以最不优化的方式解决它,然后我们可以如何改善它,让我们去做吧!

方法 1:暴力

有时候,为了更接*最优解,建议先用一种蛮力的方法,这种方法显然不会是最优的,但会为理解问题提供基础,为优化让路。

第一种方法是比较从nums可能形成的所有可能的对,如果我们找到总计为target的对,我们完成该过程。该函数如代码片段 1 所示。

代码片段 1。二和:暴力解决方案

尽管这种方法找到了解决方案,但是由于为了得到解决方案而必须进行的比较的数量,它不是最佳的解决方案,也就是说,对于numsn-1中的每个n元素,进行比较,这产生了二次 O(N)执行时间。存储器使用保持恒定 O(1 ),因为不需要额外的存储器来执行比较。

优化这种暴力方法的一种方法是使用额外的内存,这将导致运行时和内存中的 O(N)复杂度,让我们看看如何做到这一点。

方法 2:使用 Set()进行优化

这种方法的目的是通过利用额外的内存来避免二次比较的次数,在这种情况下,通过利用 Python 中的 set 或set()

使用set()允许我们在常数时间 O(1)内插入或查询,唯一要考虑的方面是set()将需要 O(N)顺序的内存,如果是问题范围内的约束,就必须考虑。

这种方法的思想是将nums的所有元素放入set()。然后遍历nums的每个元素,检查set()中是否存在target — num,如果存在,返回true。代码片段 2 显示了函数定义。

代码片段 2。两和:使用 set()的解决方案

将 N 个元素插入到n需要 O(N) x O(1 ),结果是 O(N)。循环遍历nums中的每个num以验证set()中是否存在补码target — num也需要 O(N),结果是 O(N) + O(N)最终在运行时接* O(N),在内存中接* O(N)。

值得一提的是,使用set()的方法只有在返回值为boolean时才有效,也就是说,不需要返回额外的信息。如果我们需要返回总计为target的数字的索引,我们可以使用hashmap来代替。我们来看看怎么做。

方法 3:使用 HashMap()进行优化

这种方法解决了这样一种变化,即需要返回相加到target的一对数字的索引,而不仅仅是返回truefalse

例如:

**Case 1**
**Input**: nums = [3,1,5,2,9] target = 7
**Output**: [2, 3]**Case 2**
**Input**: nums = [3,1,5,2,9] target = 1
**Output**: []

对于这种变化,hashmap()帮助我们跟踪指数。接下来,我们只需要检查target — nums[i]的补码是否存在于hashmap()中,如果存在,返回currentcomplement索引。该函数如代码片段 3 所示。

代码片段 3。Two-Sum:使用 hashmap()的解决方案

在一个hashmap()中插入和查询键需要恒定的 O(1)时间,所以用n元素填充hashmap()需要恒定的时间 O(N),这也导致线性顺序内存使用 O(N)。遍历nums的每个元素以检查target — nums[i]是否存在于hashmap()中需要 O(N)个常数时间,结果是执行时间和内存使用的总时间为 O(N)。

虽然利用hashmap()有助于我们验证target — nums[i]是否作为键存在,但是我们仍然可以更有效地使用它。请注意,在第 8 行,我们完全循环通过nums来填充hashmap(),然后循环通过第 12 行的nums来验证补数,这导致遍历 2 次nums。让我们看看如何通过仅循环一次nums来使这种方法更有效。

方法 4:使用 HashMap() II 进行优化

这种方法的思想是只遍历nums一次,以验证target — nums[i]是否作为键存在于hashmap()中。这将使执行时间更有效,尽管它仍然是线性的,而不是在nums上迭代两次,现在我们将只做一次。代码片段 4 显示了函数的定义。

代码片段 4。Two-Sum:使用 hashmap() II 的解决方案

由于我们在这种情况下循环通过nums,执行时间也是线性的 O(N),并且由于我们使用了hashmap(),内存使用也是线性的 O(N)。通过避免在nums上迭代两次并且只做一次来进行优化。

回到原来的问题,我们只需要返回truefalse,如果nums已经排序了,我们将如何处理这个问题?我们来看看怎么做。

方法 5:排序数组的优化

假设不是nums不排序,而是发现nums按非降序排序。

例如:

**Case 1**
**Input**: nums = [1,2,3,5,9] target = 7
**Output**: True**Case 2**
**Input**: nums = [1,2,3,5,9] target = 1
**Output**: False

在这种情况下,问题条件是合适的,因此不再需要使用额外的内存来跟踪已经访问过的号码。在这种情况下,通过实现两个指针的技术,我们可以确定是否有一对总计为target。代码片段 5 展示了该函数的实现。

代码片段 5。Two-Sum(排序数组):使用双指针技术的解决方案

这种方法的执行时间是线性的 O(N ),因为在最坏的情况下,我们只遍历每个 nums 元素一次。空间使用是恒定的,因为不需要额外的内存使用。

结论

两和问题可以有不同的变化和条件,总是建议尽可能澄清,以避免误解,并选择正确的路径。在这篇博客中,我们看到了一些变化,以及如何处理它们,从基于蛮力的方法到寻找更优化的方法。

如果你有具体的变化或任何其他问题,我们可以在博客中分解,请留下你的评论。

参考

[1]https://leetcode.com/problems/two-sum/

https://leetcode.com/ferneutron/

编写 Smart Wordle Solver Python 版本的代码(第 2 部分)

原文:https://towardsdatascience.com/coding-the-smart-wordle-solver-python-version-part-2-a0ed4d127225

智能单词解算器的 Java 和 Python 实现之间的广泛比较

照片由耶鲁安穴獭Unsplash

介绍

继续上一篇文章 用 Java 构建一个智能 Wordle 解算器,这篇文章(第二部分)将用两种语言中可用的相同工具介绍它的 Python 实现,以及对两种替代方案的性能、可读性和总体优势的分析。

作为快速回顾,前一篇文章描述了解决构建 wordle 解算器问题的最佳方法,并定义了解决方案的算法,最终用 Java 编码。此外,它还对依赖机器学习或启发式过程等多种技术的某些替代解决方案的可行性进行了深入解释。确切地说,它揭示了使用像这样简单的策略的缺点,这导致了在整个游戏中不必要的尝试,以及 人工智能 提供准确解决方案的潜力。然而,尽管在算法中引入 机器学习 过程带来了对早期基本解决方案的改进,但它并没有作为最终版本的正确方法。最后,通过使用 香农 熵公式,我们得出了一个过程,这个过程被证明对于给定的问题是最优的,优于其他任何方法,然后用 Java 编码,并用合适的语言工具进一步优化。

因此,在部署了功能性 Java 代码之后,我们可以使用字典或输入单词长度等自定义参数来测试它的性能,当将它转换为应用程序、产品或程序时,可以方便地考虑其他可能性,以便在生产中达到最佳版本。因此,我们通常执行连续的细化,以检测内存泄漏、不准确或任何其他干扰其正确操作的错误。然而,在这种情况下,我们将使用另一种语言 (Python) 对其进行编码,以探索可能出现的问题以及我们可能利用的改进机会。此外,除了增强我们提出的解决方案,将【翻译】代码翻译成不同的语言,这些语言的属性与我们将要使用的不同,这种练习开发了我们的计算思维,并增加了我们对用于创建软件的资源的内部工作方式的了解。

为什么是 Python?

选择 Python 作为重写程序的第二语言并没有单一的原因。事实上,Java 甚至没有一个被选为首选的理由。在所有现有技术中,Java 被认为是在学习如何编码方面最好的技术之一,也就是说,它与众不同的特性,例如具有 强类型多参数 ,促进了软件开发领域新人的学习过程,因为语法相对简单,作为一种【编译】语言,它的学习曲线是合理的,并且它在其程序中提供了某些可移植性/兼容性选项,使其独一无二。

起初,Python 是一种语法非常简单的语言,是全球市场上最需要的工具之一,在前面提到的 【人工智能】 、数据科学甚至云计算等领域非常有用。为了了解它与自然语言有多接*,在某些情况下,我们只需要看看它已经包含的用于执行基本操作的替代方法,而在其他语言中,这需要几十行甚至几百行代码。例如,要遍历某个范围的值并检查某个特定属性(或一系列属性)*中 范围内的每个值 ,我们可以根据我们正在处理的数据结构,在 列表理解字典理解 中压缩操作,然后结束***

*>>> print([i for i in range(0, 9) if i%2==0])
[0, 2, 4, 6, 8]
>>> print({i:i%2==0 for i in range(0, 9)})
{0: True, 1: False, 2: True, 3: False, 4: True, 5: False, 6: True, 7: False, 8: True}*

你可以观察到,在 Python 自己的操作符中,我们可以清楚地注意到其语法与英文的相似性,比如在中的 主要用于检查一个数据结构中某个元素的包含性,遍历生成器对象。此外,Python 在代码中处理布尔条件的方式表现出了显著的直观性,以与英语完全相同的方式表示基本逻辑门 NOT、

*>>> print(not True and False)
False
>>> print(not (True and False))
True*

然后,如果我们想在任何编译语言中构建与上面所示代码等价的代码,比如 JavaC++ ,我们就不会有相同的工具来增加算法的可读性。结果,它将花费更多的代码行,虽然它不应该是我们编写的代码质量的唯一或明确的指标,但在阅读其他人的代码或处理大型软件项目时,这一点尤其有影响。

因此,到目前为止,我们可以得出结论,Python 是一个明智的选择,因为它的语法。然而,不仅有更多的 Python 特性使它以自己的方式变得有用,而且理解起来非常简单的优势也产生了其他值得考虑的问题。第一个也是最决定性的一个是执行时间的减慢。因为如前所述,这种语言是解释型的,这赋予了它简单的能力,但同时,由于本文后面将解释的原因,执行起来非常昂贵。

最后,关于在这个项目中使用 Python,值得强调的最后一个重要特性是它对库和模块的支持。basic 语言版本本身不能做很多事情,它需要一系列具有预编程功能的模块来扩展 Python 在尽可能多的领域中的潜在应用。简而言之,模块是在 Python 程序中组织和重用代码的强大工具。它们允许开发人员在一个自包含的单元中定义函数、类和其他代码结构,然后可以将其导入并用于程序的其他部分。这种模块化的代码组织方法有许多优点,包括代码重用、可维护性、改进的名称空间管理、可读性和组织,允许开发人员以逻辑的、分层的结构组织代码,这使得查找和理解程序的特定部分变得更加容易。

总的来说,使用 Python 模块可以大大提高 Python 程序的效率和可维护性。这是一个关键的语言特性,有助于它成为开发人员的热门选择。

语言属性

在这一节中,我们将学习更多关于这两种语言(尤其是 Python)* 的知识,以充分利用我们所拥有的工具来高质量地实现我们的算法。*

Python 和 Java 是世界上最流行的两种编程语言,有许多相似之处和不同之处。两者都是面向对象的语言,这意味着它们被设计成将代码组织成代表现实世界实体的“对象”。这使得构建复杂的程序更加容易,并有助于确保代码的可重用性和可维护性。Python 和 Java 的另一个区别是它们的执行方式。就性能而言,Java 通常比 Python 快,但这取决于具体的程序和运行它的硬件。Python 还以拥有庞大而活跃的用户群体而闻名,这意味着有大量的资源和库可用来帮助开发人员构建应用程序。虽然 Java 也很受欢迎,但是它的社区要小一些,但是它仍然受到许多组织和公司的支持。

程序执行

关于执行过程,有必要对计算机如何处理用于具体软件项目的语言有一个坚实的理解,即,对编码指令执行的完整过程有一个广阔的视野不仅仅是一个选项,而是节省开发和生产成本的先决条件。

一方面,我们有 Java 的例子,之前我们称之为编译语言。简单来说,一个 编译语言 就是翻译成可以被计算机的处理器 (CPU) 直接执行的机器代码的一类语言。编译过程包括把用高级编程语言编写的源代码翻译成 CPU 能够理解的低级语言。这是由编译器完成的,编译器是一个读取源代码并产生其机器可读版本的程序。**

具体来说,如果我们处理 Java,我们不能肯定地将其归类为完全编译的语言,因为当编写 Java 程序时,它必须首先被编译成 字节码 ,这是一种独立于*台的机器代码形式,可以在任何具有 Java 虚拟机(JVM) 的设备上运行。JVM 是一个解释字节码并在设备处理器上执行指令的程序。

你可以在这里找到更多关于 Java 执行 的信息。

作者图片

相比之下,Python 是一种解释型语言。因此,当 Python 程序运行时,解释器读取源代码并逐行执行指令。这意味着代码在运行之前不需要编译成机器码,这使得用 Python 开发和测试程序更加容易,因为可以立即进行更改和测试。解释语言的一个缺点是它们通常比编译语言执行得慢,因为代码必须在运行时解释,这给执行过程增加了一个额外的步骤。然而,它们可能更容易使用,因为它们通常具有更简单的语法。

支持的范例

另一个要考虑的关键方面是每种语言支持的编程范式,即,如果我们要创建一个视频游戏,使用支持 事件驱动范式 的语言将是合乎逻辑的,例如,如果我们要构建一个机器人的软件,我们将需要 命令性范式 。在这个 Wordle Solver 项目中,Java 和 Python 都是多范式的,所以我们不用太担心它们是否支持任何一个特定的。

尽管如此,其中一些可能对我们算法的实现特别有用,比如,它主要强调使用函数来解决问题(例如λ函数)。函数式编程在 Python 中很有帮助,原因有很多:

  1. 它促进了代码 的复用性: 函数可以一次定义多次调用,减少了重复代码的需要。
  2. 促进 推理: 通过将一个问题分解成更小的、独立的函数,就更容易理解代码是如何运行的。
  3. 它支持 并行性: 因为函数是独立的,不依赖于程序的状态,所以可以很容易地并行运行,使得函数式编程非常适合 并行计算

以范例结束,提到面向对象的范例是必要的,因为它是正确理解高级编程的关键。基于“对象”的概念,它们是数据和行为的自包含单元,在 OOP 中,你定义了代表真实世界对象的类,以及它们所具有的属性和行为。然后,您可以创建这些类的实例,并在代码中操作对象。

作者图片

此外,这种范式有一套指导原则,可以帮助开发人员以一种有组织和有效的方式设计和构造他们的代码。这些原则包括封装、抽象、继承或多态。

作者图片

你可以在这里看到更多关于 OOP 的信息。

强/弱打字

在计算机科学中,术语“类型化”指的是编程语言处理数据类型的方式。一种语言 (Java) 如果要求显式声明变量并在编译时检查类型错误,则被视为“强类型”。这意味着如果一个变量应该是一个整数,当你试图给它赋值一个字符串时,编译器会产生一个错误。强类型有助于防止与类型相关的错误,并且更容易在早期发现错误。

***import java.util.HashMap;

HashMap<String, Integer> mapVariable = new HashMap<String, Integer>();***

另一方面,一种语言 (Python) 被认为是 【弱类型】 ,如果它允许变量在数据类型之间隐式转换,并且直到运行时才检查类型错误。这意味着如果一个变量应该是一个整数,如果你给它赋值一个字符串,程序不会产生错误。相反,程序会尝试将字符串转换为整数,如果无法转换,可能会产生意外的结果。弱类型会使捕捉与类型相关的错误变得更加困难,并且会导致不可预知的行为。

**mapVariable = {}**

总的来说,Python 和 Java 都是功能强大的编程语言,各有优缺点。Python 是快速原型化和开发的好选择,而 Java 更适合更大、更复杂的项目和需要高性能的应用程序。最终,Python 和 Java 之间的选择将取决于项目的特定需求和开发人员的偏好。

Python 实现

之前,我们已经在上一篇文章中用 Java 编写了 Wordle Solver,甚至 Python 也被用来快速生成某些数据,比如字典单词或输入组合。因此,在这一章中,我们将回顾一个完整的 Python 版本的代码及其相应的更新、改进、奇点和缺陷。

Main 方法和 main

在 Java 中,我们创建的每个程序都需要有一个 main 方法,因为它是代码的启动块(由JVM)调用,我们的其余代码稍后将从这里被访问。打个比方,它类似于游戏开发中的 Start 方法,在一个程序生命周期的开始,共享几乎相同的独特执行特性。

相反,在 Python 中工作时,我们没有主方法,除非我们想定义一个。相反,我们可以直接将指令写入我们的脚本,这是可行的,但这不是用 Python 编写任何东西的正确方式,因为其他脚本可能会导入 "main" 指令,并且会有一些指令不必要地执行,甚至会导致错误。因此,为了解决这个问题,总是建议添加一个 if 条件,检查我们脚本中的指令是要在 "main" 方法上执行,还是属于模块/脚本本身用于外部计算。

具体来说,通过使用包含相应模块标识符的内置变量 name ,我们可以检测代码是否从“main”方法运行,只需将名称与 Python 提供给我们从终端首先运行的脚本的默认值 main 进行比较。这样,每一个其他的脚本(模块)都有自己的名字,而不是一个单数的_ _ main _ _****

乍一看,由于这种语言处理列表很容易,我们可以看到在这个版本中,包含单词的字典是如何像在 Java 中一样从 URL 加载的。但是,它支持借助于NumPy库,动态地选择其中的一定数量用于程序的其余执行。此外,' s’变量声明下的注释行为 NumPy 伪随机数生成器设置了一个新种子,根据它所采用的输入整数参数产生一个确定的行为。然后,在整个代码中不再使用 NumPy,尽管它提供了函数和数据结构来有效地运行大型数值数据数组和矩阵的数学和统计运算。

如上所述,我们不需要明确指定每个变量的类型,因为它可以在运行时改变(尽管大多数变量的类型保持不变)。因此,在复杂的数据结构中,如字典或多维列表,我们避免了冗长的变量声明,实现了更简洁的代码。但是,需要注意的是,如果数据处理不当,这种灵活性也会导致潜在的问题。在像 Python 这样的弱类型语言中,由程序员来确保数据以有意义的方式使用,并处理任何可能发生的潜在类型错误,这需要仔细规划和调试,这种情况借鉴了上一个 Java 版本。

另一个需要考虑的有益组件是 Python 标准包含的大量内置 功能。根据上面的代码,第一个也是最臭名昭著的是列表和字典数据结构管理。除了它的底层实现,我们不需要像在 Java 中那样导入任何库或特殊模块来使用这些关键结构,例如 ArrayListLinkedHashMap、 需要在相应的。java 文件。此外,python 通过其语法非常直观地解决了列表和映射上的一些特定操作。例如,只使用 [::] 操作符,就可以访问、分割列表中的元素,甚至只使用一个操作符和适当的数字索引就可以反转整个结构。

最后,第二个 内置 Python 组件是它所包含的多用途函数的集合。例如,在 Java 中,我们不得不使用Java . lang . math库来执行 max/min 比较,同时,在 Python 中,我们可以直接使用 max()min() 来获得相同的结果。在其他情况下,我们可能要用各自的参数或 Java 中的【length()函数来计算数据结构、字符串或类似的长度。尽管如此,Python 已经实现了 len() 函数来简化这种计算(只能用定义的 len name 构造)。此外,Python 的标准库也在不断更新和改进,为该语言增加了更多的功能。**

注意,我们在 for 循环内部添加了一个 break 语句,只是为了让赢/输逻辑暂时工作,这不是一个好的编程实践,将在单独的 GitHub 版本中进行纠正。

正则表达式模式生成

继续从“main”方法调用函数,我们进入输入/输出管理部分,在这里程序处理用户在每次迭代中插入的 0、1 和 2 的组合。为此,我们在数据处理方面使用了 Java 代码中使用的相同技术。因此,一方面,我们将使用 正则表达式 来生成匹配模式,这将帮助我们验证 字典 中的特定单词是否可以由游戏期间获得的单词和条目的序列形成,并且还验证玩家输入其数据的一致性,即,他没有作弊或试图破坏算法的执行流程。另一方面,我们稍后将需要多重处理来根据各自的 Regex 模式有效地更新字典单词分数。

就像我们在 Java 版本中看到的那样,模式的创建被封装在一个函数generate pattern()中,该函数将用户组合及其对应迭代的单词作为输入,并提供正则表达式作为输出,该表达式包含有关某个字符是在单词的特定位置、肯定位于某个位置还是在整个单词中找不到的信息。

在前面介绍了正则表达式的基础和实用程序之后,这里我们将只关注自 Java 版本以来的变化。通过简单的概述,我们看到 IntStreams 已经被内置的 list comprehensions 所取代,使得代码不容易在库中(甚至在外部模块中)出现错误,并且增加了可维护性。通过这样做,我们的主要兴趣是在遍历列表结构时有一个快速和安全的方法来执行“if conditions”,在这种情况下,它将是函数的输入参数。另一个值得注意的变化是使用 Python 的f-strings将 regex 模式编码在字符串中的方式,这也是内置的,使用起来比 Java 的string . format()函数舒服得多。

同样,利用简单的 Python 语法,我们将深入研究验证用户输入的函数【validarEntrada()

首先,注意 Python 不像 Java 那样有 switch 语句,所以我们必须将条件编码在一个 if-elif 结构中,假设这是使用这种语言的一个轻微缺点。通过上面的函数,除了 regex 模式(这一次是玩家之前尝试形成的全局模式)之外,使用与generate pattern()几乎相同的输入参数,我们得到一个布尔值,帮助我们检测输入中的不一致。例如,正如您在函数的 if 条件中看到的,如果用户在先前标记为 2 (肯定在所选位置)的字符中输入了 0,函数将返回值 False、 ,并且将增加一次尝试,因为不可能确切知道玩家是否作弊或只是犯了一个错误。

这样,考虑到所有可能发生错误的情况,我们也避免了一个只有在用正则表达式实现 Wordle 解算器时才会出现的错误。为了清楚地理解它,让我们想象在一次尝试中,程序启动单词【ARCAS】,用户将字符标记为【01210】。在这种特殊情况下,第一次出现的 A 被标记为 0,第二次出现的 A 被标记为 1,根据 Wordle 规则(否则就是正确的),这是不允许的。然而,除了破坏游戏规则之外,在与单词相关的模式中还会有矛盾的正则表达式,导致严重的异常并使程序崩溃。**

**>>>generatePattern([0,1,2,1,0], "ARCAS") #Previous case pattern
^(?=[^A]*$)(?!.{1}R)(?=.*R)(?=.{2}C)(?=[^S]*$).*$**

字典更新过程

随后,我们还没有提到的其余函数处理每个游戏尝试的字典更新过程。 Regex 模式匹配和新字典的每个元素的熵值计算都是计算密集型操作,因此我们必须提供尽可能高质量的解决方案。换句话说,除了编写干净的、结构良好的、有序的代码之外,我们还必须关心我们的实现的时间效率和内存消耗,这样它就不需要整个服务器来运行而不会出现任何问题。但在解释这是如何用 Python 编码的之前,我们必须了解什么是并行 多处理****

根据官方定义,并行 是同时执行多个任务的能力。不过,通过下面的类比,你可以对这个概念有更清晰的理解;如果你想在地板上挖一条沟,你告诉 32 个人同时工作(并行)这项工作比只告诉 16 个人完成要快得多,因为单位时间内完成的总工作量与工人数量成正比。

假设每个人都不干涉另一个人的工作,也不超过一个人试图在同一个地方挖掘,这在计算机中会转化为处理错误和中断程序的异常。

所以,在计算机科学中我们的工作人员是 CPU/GPU/TPU 内核,工作就是完成 进程 执行。进程只是一个程序的实例,它带有一组由 CPU 执行的特定指令。它有自己的内存空间,独立于其他进程运行。这意味着每个进程都有自己的变量和数据结构,除非明确允许,否则不能访问其他进程的内存。在大多数操作系统中,进程是在程序执行时创建的,并在程序结束运行时终止。一个程序可以产生多个进程,通过创建多个进程,可以在一台计算机上同时运行多个程序。

进程可以通过各种方式相互通信和共享数据,例如共享内存、消息传递或 管道。 虽然,在这种情况下,它是不需要的,因为我们将为每个进程预先分配它整个执行所需的数据。

现在,我们已经准备好理解上述 updateDict() 函数的内部工作原理,它类似于 Java 代码,使用 RegexParallelism 来丢弃在未来游戏迭代中不能使用的单词。

首先,它将输入的正则表达式模式与每个条目进行匹配,剩余的映射被分成与 CPU 内核一样多的块,然后在熵值计算之后进行连接,这是最昂贵的步骤。

最后,Java 和 Python 实现之间唯一显著的区别是在 并行中使用的助手结构。 而在 Java 中,我们有一个继承自 Callable 接口的类,这里我们只需要一个函数负责调用 scoreWord() 并返回相应的字典块和所有计算出的熵值。

进一步预计算的可能性

尽管使用了 并行 ,但拥有与 Java 版本中使用的一样多的单词的字典只能在高端 CPU 上工作,这不是一个合适的最终解决方案。关注于时间复杂度,我们的算法(每次迭代)现在必须遍历整个字典来计算熵,向总复杂度添加一个 O(n) 项。然后,它必须按照输入组合的次数对每个元素 再次遍历整个数据结构 ,在这种情况下,它是一个常数 243 值,从而创建另一个项 O(243 n)。 因此,函数中时间复杂度的决定性度量将是 O(243nxn)。

作者图片

作者图片

由于我们不能在所有设备上运行这个版本,所以我们必须为最终版本提供一个最佳解决方案。

在尝试了各种各样的替代方案后,我们可以得出结论,降低复杂图增长率的唯一简单方法是 预先计算 数据。

正如你在上面的代码中看到的,我们用来优化这个过程的技术是建立在预先计算所有可能的字典的基础上的,这些字典来自游戏的第一次迭代,由于有大量的初始单词,所以计算起来是最昂贵的。为了避免在游戏执行过程中加载大量数据,我们只计算长度大于 100 的字典。其余的可以很容易地在本地机器上在合理的时间内生成。

作者图片

最后,在计算了各自的字典文件并使用一个 树形数据结构 来组织它们之后,就像上面显示的(有一些修剪过的分支,因为它们从来没有到达过),其中每一层都代表一种可能的尝试,我们必须在我们认为最终的版本中加载它们。

最后,通过所有这些改进,我们在最昂贵的计算中实现了 O(1) 恒定的时间复杂度,假设验证和模式生成函数的计算成本可以忽略,假定单词只有 5 个字符长,尽管也应该考虑 n 长度 单词的一般化。现在,我们的算法已经可以部署在几乎所有运行 Python 的设备上。

你可以在下面的 资源 中看到整个 Google Colab 笔记本:

*https://github.com/cardstdani/practica-java *

用 Python 编写你自己的 GIF Creator 项目

原文:https://towardsdatascience.com/coding-your-own-gif-creator-project-with-python-3c3c1348dfb0

使用 Python 从图像开发自己的 GIF 文件

照片由 Unsplash 上的 Dariusz Sankowski 拍摄

图像构成了我们生活的一个重要部分,无论是在互联网上,还是在书籍、杂志上。它们有助于更快地掌握相关信息,并更有效地向观察者传达特定的信息。处理这些图像也是编程的一个重要部分,尤其是在计算机视觉和图像处理领域。

观看单个图像很有趣,但将它们组合在一起创建一个小的动画文件,为图像添加更多的意义和价值,让观察者获得更多的信息,这是非常有趣的。虽然视频或电影是由一堆配对在一起的图片组成的,但它们通常很长。

然而,1987 年开发的 图形交换格式 (GIF)最高支持每像素 8 位,可以包含 256 种索引颜色。它们可以用来显示静止图像或小的动画文件,通常持续几秒钟。这些 GIF 文件就像动画书一样,有助于交流独特的想法、幽默和情感,或者传达有意义的表达。

在本文中,我们将在图像的帮助下进行 GIF 创作项目,当这些图像按顺序配对在一起时,有助于产生特定的意义或动作。我们将利用两个基本的 Python 编程库,并用一些简单的代码开发我们的项目。我还将介绍一些额外的改进,以增强读者可以尝试的项目功能。

在开始这个项目之前,我建议阅读我以前的一篇文章,其中涵盖了七个基本的编程技巧,可以遵循这些技巧来提高 Python 编程语言的生产率。下面提供了到以下文章的链接,包括如何修复一些常见的低效编程实践的简要指南。

</7-python-programming-tips-to-improve-your-productivity-a57802f225b6>

用 Python 开发 GIF creator 项目:

在这一节中,我们将通过利用一组描述特定动作的图像来开发一个 GIF 文件。通过编辑相似图像的列表,或者说明特定活动的某些图像,我们可以相应地计算 GIF 文件。

我在这个 gif 文件中使用了以下三张免费图片——来自 Unsplash 的图片 1(日出)、图片 2(日落)和图片 3(夜晚)。读者可以自由地为他们各自的 GIF 文件实现他们自己的图像。

照片由塞巴斯蒂安·加布里埃尔雷切尔·库克罗西奥·吉伦Unsplash 拍摄

一旦开发人员选择了他们想要的图像,我们就可以通过导入我们将用于这个项目开发的基本库来开始这个项目。

导入基本库:

我们将在这个项目中使用的两个基本库是 Pillow (PIL)库和 glob 模块。我们利用这些库来处理图像和存储图像的目录。pillow library 允许我们加载相应的图像并对它们执行操作。这些操作包括相应地调整大小和保存信息。glob 模块允许我们直接访问特定目录中特定格式的所有图像,从而加快这个项目的计算速度。

# Importing the essential libraries
from PIL import Image
from glob import glob

我们可以利用的另一个库是内置的 os Python 模块,它允许我们在操作系统上执行操作。使用这个库,一旦 GIF 文件被创建,我们就可以通过我们的程序直接打开它。

初始化所需的参数:

我们将为这个 GIF creator 项目创建的主要参数是图像列表,其中包含每个帧的相应图像,另一个参数用于存储指定目录中的图像列表。我们将考虑的其他变量是图像尺寸的宽度和高度,以及用于存储创建的 GIF 文件的名称。下面提到了各个参数和变量的代码块。

# Mention the Resizing Dimensions and save file name
w = 1024
h = 1024
image_name = "Day_Cycle_1024.gif"

# Creating a list of images
image_list = []
_images = glob("images/*.jpg")

一旦我们定义了这个代码块所需的基本变量和参数,我们就可以继续开发循环周期,用存储的图像创建所需的 GIF 文件。

开发循环周期:

在下一步中,我们将遍历包含存储图像的图像目录(在。jpg 格式)并检索每个单独的图像。使用 pillow 库,我们将打开存储在目录中的每个图像,并将它们调整到所需的宽度和高度。请注意,调整大小的步骤对于保持整个 GIF 文件的纵横比不变至关重要。一旦调整了每张图片的大小,我们可以将它们存储在一个列表文件中,通过这个列表文件可以创建 GIF 文件。下面提到了以下步骤的代码块。

# Creating the for loop with the respective images
for _image in _images:
    # Opening each individual image
    file = Image.open(_image)
    # Resizing the images to a single matching dimension
    file = file.resize((w, h))
    # Creating a list of all the images
    image_list.append(file)

创建 GIF 文件:

创建 GIF 文件的最后一步是以 GIF 文件格式存储列表中创建的所需图像。我们可以利用 pillow 库中的 save 命令来创建所需的 GIF 文件。创建以下内容所需的主要属性是图像名称(我们之前定义的)、保存文件的格式、要附加到原始图像的图像列表、保存状态、持续时间和循环。一旦 Python 代码成功运行,我们最终可以打印出 GIF 文件已创建。下面提到了创建 GIF 文件的代码块。

# Create the GIF File
image_list[0].save(image_name, format='GIF',
                    append_images=image_list[1:],
                    save_all=True,
                    duration=500, loop=0)

print("The GIF file is successfully created!")

创建保存文件后,您可以直接从 Python 代码的相应工作目录中访问它。

完整的代码和其他提示:

创建的 GIF 图像

如果读者已经准确地遵循了所有的步骤,现在就可以访问创建的 GIF 文件了。一旦 GIF 文件被创建并从工作目录中被访问,上面显示的 GIF 图像可以在 Python 文件夹中查看,代码就是在这个文件夹中运行的。

该项目的完整代码在下面提供给读者,以便于阅读。

# Importing the essential libraries
from PIL import Image
from glob import glob

# Mention the Resizing Dimensions and save file name
w = 1024
h = 1024
image_name = "Day_Cycle_1024.gif"

# Creating a list of images
image_list = []
_images = glob("images/*.jpg")

# Creating the for loop with the respective images
for _image in _images:
    # Opening each individual image
    file = Image.open(_image)
    # Resizing the images to a single matching dimension
    file = file.resize((w, h))
    # Creating a list of all the images
    image_list.append(file)

# Create the GIF File
image_list[0].save(image_name, format='GIF',
                    append_images=image_list[1:],
                    save_all=True,
                    duration=500, loop=0)

print("The GIF file is successfully created!")

我建议观众检查并自己尝试进一步改善项目的其他生活质量改进如下:

  1. 为各种图像格式创建一个列表/循环,例如。png,。jpeg 和。tif 文件,这样所有的图像,不管是什么格式,都可以包含在正在创建的 GIF 文件中。在当前项目中,只有。jpg 文件被认为来自各自的图像文件夹。
  2. 一旦程序被执行,利用操作系统库模块或其它类似的库直接显示 GIF 文件。执行这一步骤将允许开发者跳过每次程序运行时必须从工作目录访问创建的 GIF 文件。

结论:

赫克托·j·里瓦斯在 Unsplash 上拍摄的照片

“什么时候使用迭代开发?您应该只在您希望成功的项目上使用迭代开发。”——马丁福勒

图像是生活的重要组成部分。在这个美丽的世界上有太多值得崇拜的东西,而图像是感知我们生活的世界的最美妙的方式之一。从图像向前推进一步的是 GIF 文件,它由许多图像编辑在一起构成,以创建有意义或有趣的内容。

在本文中,我们探索了一个简单的 Python 项目,借助两个基础库 Pillow 和 glob,我们可以轻松地开发这个项目。我们利用一些基本的变量和参数来创建一个基本的循环代码块,通过它我们可以迭代图像来创建所需的 GIF 文件。可以对建议的进行进一步的改进,以增强其实用性。

另一方面,我为我的观众缺乏连续的内容感到抱歉,因为我在过去的两个月里非常忙。然而,在接下来的几个月里,我会更加自由,能够提供更多的内容。敬请关注更多酷炫项目!

如果你想在我的文章发表后第一时间得到通知,请点击下面的链接订阅邮件推荐。如果你希望支持其他作者和我,请订阅下面的链接。

https://bharath-k1297.medium.com/membership

如果你对这篇文章中提到的各点有任何疑问,请在下面的评论中告诉我。我会尽快给你回复。

看看我的一些与本文主题相关的文章,你可能也会喜欢阅读!

</7-best-research-papers-to-read-to-get-started-with-deep-learning-projects-59e11f7b9c32>

谢谢你们坚持到最后。我希望你们都喜欢这篇文章。祝大家有美好的一天!

烘焙过程中的咖啡密度和重量损失

原文:https://towardsdatascience.com/coffee-density-and-weight-loss-in-roasting-28376ae8ca03

咖啡数据科学

偶然数据探索

我收集烤统计数据已经有几年了,但是还没有用在任何事情上。直到现在我才觉得我有足够的数据来绘制它。所以我有了烘焙生咖啡的密度和重量损失的初步数据。所有这些数据都应该被认为是探索性的数据,我希望它们能启发你和我更好地收集关于烘焙的数据。

所有图片由作者提供

我的大部分烘焙是两种咖啡豆的混合,所以目前不值得通过烘焙来分割数据。

所有的烘烤都是在一个热炉上用相同的曲线完成的。我唯一改变的是结束时间。我通常在第一次破裂后的 1 到 2 分钟内保持烘烤。

尺寸

我收集了一些关于时间和环境温度的测量数据,但密度和重量损失似乎比其他数据点更重要。

此外,我有*均 Q-分数的基础上,从甜玛丽亚的分数

对于密度,我使用了一个简单的 80 毫升量筒。我把它装满,摇匀,如果需要的话再装满。然后我加权了一下(密度=质量/体积)。这种方法本来可以做得更准确,但我接受了这个错误,只要我在所有的烘烤中使用相同的技术。

为了减肥,我用精确到 0.1 克的秤测量了烘烤前后的重量,每次烘烤的重量从 200 克到 350 克不等。

数据

我绘制了常规烘焙、酵母和罗布斯塔的数据。普通的意思是阿拉比卡咖啡。然后我有任何使用酵母加工咖啡豆和罗布斯塔。

酵母似乎是百搭牌,robusta 没有足够的数据。所以让我们只关注普通的阿拉比卡咖啡豆:

有一个相当不错的线性拟合,在测量中肯定会有一些误差。

我还回顾了使用金属杯收集的数据,这项技术似乎很适合自己,但它相对于黄色管有一个误差。

回头看看测量用的金属杯,我有一些 9%左右的罗布斯塔烘烤。所以我绘制了这些混合烘焙,它们与趋势相当吻合,可能是因为它们大多是阿拉比卡咖啡。

使用金属杯(70 毫升)

准确(性)

为了检查测量的准确性,我进行了一次烘烤,并对密度进行了十次单独测量。重量的标准偏差(STD)比预期的要大,大约为 4 到 5 个豆子,但这对密度没有太大影响,因为试管足够大。

还有其他方法可以提供更好的测量。就我的目的而言,只要我在不同的漫游中保持相同的技术,从数据的角度来看就足够有用了。

这个长期的实验让我很感兴趣。我希望继续,我希望有一天这些信息在杯赛中对我更有用。

未来的实验应包括:

  1. 绿豆密度
  2. 生坯和烘烤硬度
  3. 湿度计读数
  4. 校准的颜色读数

我也对任何人拥有的关于自己烘焙的数据感兴趣,尤其是如果他们有大量每天烘焙咖啡的数据。

如果你愿意,可以在推特、 YouTubeInstagram 上关注我,我会在那里发布不同机器上的浓缩咖啡照片和浓缩咖啡相关的视频。你也可以在 LinkedIn 上找到我。也可以关注我在订阅

我的进一步阅读:

我未来的书

我的链接

浓缩咖啡系列文章

工作和学校故事集

基于模式识别的小生境和 Ode 研磨机的咖啡颗粒相似性

原文:https://towardsdatascience.com/coffee-particle-similarity-between-the-niche-and-ode-grinders-using-pattern-recognition-714c4a92ded0

咖啡数据科学

使用模式识别分析形状以更好地理解基本粒子形状

为了更好地理解锥形和扁*毛刺之间的区别,我想更好地理解它们是如何切割咖啡豆的。我的目标是在粒子上使用模式识别,看看它们的形状有什么不同。

之前的努力在显示小生和 Rok 的较粗颗粒之间的差异方面很有用,所以我拿出了一些小生和 Ode 的图像数据,然后我就去工作了。主要的警告是,这是在单一研磨环境下从单一咖啡豆中取样的。两台研磨机都被调至产生相似的颗粒分布。对于其他 beans 和设置,结果可能会有所不同;更多的数据和研究是必要的。

设备/技术

意式咖啡机 : 像样的意式咖啡机

咖啡研磨机 : 小生零 SPP 毛刺的同杆针

原始数据

首先,让我们画出分布图。我查看了颂歌上的两个设置,以找到最接*利基的设置。这些分布略有不同,但对于 300 微米以下的颗粒直径,它们有许多共同之处。

所有图片由作者提供

分析

然后我使用线性二元模式 (LBP)对每个粒子进行分类,然后我使用 K-means 对所有粒子进行聚类,以查看它们如何对齐。

首先,我们可以看看有百分之多少的粒子被分组到哪个簇中。集群对齐有一点点移动,但不多。

然后,我们可以使用簇排列作为模式向量来计算不同大小的粒子与其他粒子的*均差异。这将一个组中所有粒子的聚类与另一个组中所有粒子的聚类进行比较。

大小相似的粒子更相似并不意外,但要梳理出其中的差异却有点棘手。让我们仔细看看:

小生境的颗粒尺寸比 Ode 的颗粒尺寸更不相似。当比较这两者时,同样大小的粒子非常相似。

我们可以更仔细地看看相同的颗粒大小与不同的分数。分数越低,两者越相似。分数一直稳定到 400um。

我们还可以将当前箱与下一个箱的颗粒尺寸进行比较,以将小生境与小生境进行比较,并理解这些差异如何与 Ode 进行比较。同样,在 650um 之前,利基和颂歌之间没有太大的区别。

随机集合

让我们将每种颗粒尺寸的颗粒分成两组,这样我们可以更好地比较每台研磨机的颗粒箱。在比较中,每个颗粒尺寸箱被分成两组,x 轴使用一组,y 轴使用另一组。

当我们看这条对角线时,主要的差别在 550 微米左右。

在检查和比较粒子后,我没有找到足够的证据证明这两种毛刺集产生了根本不同的粒子形状。因此,对于这种咖啡和研磨设置,毛刺设置对味道的主要影响来自颗粒分布,而不是颗粒形状。

如果你愿意,可以在推特、 YouTubeInstagram 上关注我,我会在那里发布不同机器上的浓缩咖啡照片和浓缩咖啡相关的视频。你也可以在 LinkedIn 上找到我。也可以关注我在订阅

我的进一步阅读:

我未来的书

我的链接

浓缩咖啡系列文章

工作和学校故事集

浓缩咖啡有六种不同的加工方法

原文:https://towardsdatascience.com/coffee-processed-six-ways-for-espresso-49399319ba9f

咖啡数据科学

天然,蜂蜜,湿,碳浸渍,酵母和厌氧

我一直很好奇咖啡果加工如何影响烘焙咖啡的味道。然而,我认为不与咖啡农交朋友是不可能进行实验的。幸运的是,我在彩色咖啡和 Hiver van Geenhoven 成为朋友,我们开始聊天。

Hiver 已经在一个农场工作了很多年,生产 Parainema 咖啡。他碰巧有同一种咖啡果的多种加工变体,他给了我一些样品。一开始,他给我天然的,蜂蜜的,水洗的,还有碳酸浸渍的。我把剩下的青豆混合在一起,在酸面团中发酵。后来他给了我一个厌氧加工豆子的样品。

左(绿色),右(烤)。在每张图片中,左上(天然),右上(蜂蜜),左下(水洗),右下(碳浸渍)。所有图片由作者提供。

都烤的差不多,我也是在差不多的发育时间掉的。烘焙后,它们的密度范围为 0.365 克/毫升至 0.387 克/毫升,因此就密度而言,它们具有大致相同的密度。

天然、蜂蜜、水洗、碳浸渍

设备/技术

意式咖啡机 : 像样的意式咖啡机

咖啡研磨机 : 小生零 SPP 毛刺的同杆针

咖啡:家庭烘焙咖啡,中杯(第一口+ 1 分钟)

镜头准备:断奏夯实断奏

预输注:长,约 25 秒

输液:压力脉动

过滤篮 : 20g VST

其他设备: Atago TDS 计Acaia Pyxis 秤

绩效指标

我使用两个指标来评估技术之间的差异:最终得分和咖啡萃取。

最终得分 是评分卡上 7 个指标(辛辣、浓郁、糖浆、甜味、酸味、苦味和回味)的*均值。当然,这些分数是主观的,但它们符合我的口味,帮助我提高了我的拍摄水*。分数有一些变化。我的目标是保持每个指标的一致性,但有时粒度很难确定。

用折射仪测量总溶解固体量(TDS),这个数字结合咖啡的输出重量和输入重量用来确定提取到杯中的咖啡的百分比,称为提取率(EY)** 。**

表演

我惊讶地发现前四种(天然、蜂蜜、水洗和碳浸渍)的表现相似。就 TDS/EY 而言,没有突出的,好的或坏的。有一个自然处理的镜头,我磨得太粗糙了,我认为这是一个离群值。

我按照拍摄方法和口味评分对不同时间拍摄的照片进行了分类。EY 没有这种趋势。厌氧和酵母注射较少。

在口味上,除了厌氧是明显的赢家之外,也没有其他趋势。

我用另一种方式来削减这些分数,即对一个类别中所有镜头的个人品味指标进行*均。这显示了更多的细微差别,特别是,被加工的酵母在甜味、酸味和苦味方面得分更高。我怀疑如果我有更多关于酵母的数据,它会比其他的更容易区分。

此外,蜂蜜和水洗咖啡在苦味方面比天然咖啡和碳酸浸渍咖啡稍好。

我确信我会看到不同技术之间更大的差异,也许在受过训练的品酒师的拔罐评分中会有细微的差别。我可以肯定地说,厌氧是这些镜头中最好的处理技术。

我也很惊讶,当我确定我更喜欢天然的时候,我喜欢洗干净的咖啡豆。因为果味,我的目标是更多的天然豆子,但在这些豆子中,这似乎并不重要。

如果你愿意,可以在推特、 YouTubeInstagram 上关注我,我会在那里发布不同机器上的浓缩咖啡照片和浓缩咖啡相关的视频。你也可以在 LinkedIn 上找到我。也可以关注我在订阅

我的进一步阅读:

我未来的书

我的链接

浓缩咖啡系列文章

工作和学校故事集

咖啡折光仪的精确度:R2 与 VST 的比较

原文:https://towardsdatascience.com/coffee-refractometer-accuracy-vst-vs-difluid-r2-304036bc55f6

咖啡数据科学

单设备性能试验

作者:罗伯特、乔和杰里米

DiFluid 最初在今年早些时候推出了一款小型、价格合理的折光仪。少数研究的准确性表明,该设备可能对某些咖啡表现得足够好,但随着样本(即过滤强度咖啡)中信号强度的下降,担忧会增加。更麻烦的是不同设备的制造差异。DiFluid 继续迭代,他们在今年秋天推出了 R2。

所有图片由作者提供

因此,用 R2 做了更多的实验,将其与 VST 和阿塔戈折射仪进行比较。R2 的价格为 200 美元,比 VST 的 700 美元便宜得多,但是 R2 在准确性和精确度方面付出了代价吗?

数据是在苏格拉底咖啡收集的,我们反复进行了一些实验,以更好地了解 R2 的性能。

数据

使用 5 台 DiFluid R2 设备、1 台 VST、1 台 Atago Coffee 和 1 台 Atago RX-5000i 收集数据。使用了几种解决方案,每种都提供了不同的见解:

  1. 蔗糖溶液(白利糖度测量的基础;公认的规范性数据;硬件的“干净”评估)
  2. 浓度为浓缩咖啡的速溶咖啡(高咖啡可溶物浓度,不可溶物干扰最小;蔗糖增加了难度,需要软件将折光率读数转换为咖啡可溶物)
  3. 过滤器浓度的速溶咖啡(低咖啡可溶物浓度,不可溶物干扰最小;与速溶咖啡相比,信号强度降低,但与现实世界的解决方案相比,噪音相对较低,因为速溶咖啡几乎完全是咖啡可溶物——99.9%
  4. 浓缩咖啡(高咖啡可溶物浓度的实际应用;噪声增加但信号强的困难测试解决方案)
  5. 过滤咖啡(低咖啡可溶物浓度的实际应用;最困难的测试解决方案,信号减少,噪声增加,测试硬件和软件的鲁棒性)

每次实验前,所有折光仪都归零。所有数据都是在室温下收集的。还使用了精确到 0.001 克的精密标尺。

在第一轮实验中,我们主要关注单个器件的性能。在实验开始时,有一个设备被选为 R2 的官方设备。这是在收集任何数据之前随机选择的。关于多器件性能的更多数据将在稍后讨论,以使讨论更加简洁。

浓缩咖啡浓度

对于典型的咖啡师来说,DiFluid 以前在浓缩咖啡范围内表现得足够好。对于蔗糖,所有三种折光率仪具有相似的性能(图 1)。

图 1:蔗糖溶液

对于浓缩咖啡来说也是如此,但事实真相并不为人所知(图 2)。

图 2:浓缩咖啡的浓度

过滤浓缩速溶咖啡

回想一下我们之前的结果,过滤强度样本显示 VST 和 Atago 略低于地面真实值(图 3)。我们可以将图表分离到 VST 和阿塔哥。这是几个已知基本事实的解决方案之间的差异。

图 3:左:原味速溶咖啡;右图:放大 VST 和阿塔戈,来自这篇文章

我们可以用 DiFluid R2 官员重复这个实验。如图 5 所示,相对于 groundtruth,R2 装置似乎比原来的双流体装置具有更小的可变性。有趣的是,它似乎比 VST 和 Atago 更接*于事实。

图 5: VST,阿塔哥,R2 过滤强度下的速溶咖啡

过滤咖啡

对于过滤强度,我们可以将 Atago 和 R2 与 VST 进行比较,假设 VST 是最准确的,尽管根据之前的图,这一假设受到质疑。

该测试是在 4 种浓度稍有不同的酿造品上进行的,并且从每种酿造品中取出一些测试样品。所有四次酿造都以相同的浓度开始(15g 进,220g 出),最后两次酿造分别在酿造后加入 12g 和 32g 水。

图 6:过滤咖啡中的 R2/阿塔哥 vs VST

我们可以对这些样本三胞胎进行排序,看看它们的表现如何,R2 和 VST 的表现相当不错。

图 7:R2/阿塔哥 vs VST 在过滤咖啡,排序对

该数据没有解决从之前的数据中观察到的问题之一——制造可变性。这个话题将在不久的将来讨论。

这些发现为一些结论提供了证据:

  1. 二流体 R2 在多种测试条件下表现良好。
  2. 对于地面真相的数据,特别是过滤强度的速溶咖啡,R2 在地面真相方面的表现优于 VST。

如果你愿意,可以在推特、 YouTubeInstagram 上关注我,我会在那里发布不同机器上的浓缩咖啡照片和浓缩咖啡相关的视频。你也可以在 LinkedIn 上找到我。也可以关注我在订阅

我的进一步阅读:

我未来的书

我的链接

浓缩咖啡系列文章

工作和学校故事集

CoffeeJack 和 Wafo vs VST、Flair 和 Kompresso 浓缩咖啡过滤篮

原文:https://towardsdatascience.com/coffeejack-and-wafo-vs-vst-flair-and-kompresso-espresso-filter-basket-2991314b224e

咖啡数据科学

过滤篮的图像分析

几年前我开始研究过滤篮,我观察随机的极大或极小的孔是如何在空间上排列的。我想知道这是否可以用来解释为什么有些篮子比其他篮子表现得更好。

所以当我得到一个咖啡插座时,我采取了同样的方法。我将它与 Kompresso 相比较,因为 Kompresso 实际上已经成为旅行机器三年了。我很惊讶,对于同样的研磨、水和预浸泡,CoffeeJack 在味道和提取方面优于 Kompresso(结果在另一个讨论中)。我认为它与过滤篮的关系比其他因素更大,因为 Kompresso 是最简单的便携式咖啡机。它相当于一个便携式杠杆机器,而 CoffeeJack 是一个泵。

所有图片由作者提供。上面是 Kompresso,左边是 CoffeeJack,右边是 Flair Signature。

所以,我收集了一些数据。我还收集了 Flair Signature 上的图片,因为这是我的另一个旅行机器,也是一个 VST 篮子。有趣的外卡是 Wafo 篮子,他们发给我一个很好的图像来做这个分析。

坦白说,CoffeeJack 的 Ashley 给了我一个单元,因为他需要技术反馈。所以我们已经谈过了,但是在这篇文章发表之前,他是不会看到的。我的一个批评很受欢迎,那就是中间的咖啡豆标志。这个标志改变了淋浴帘和过滤篮的水流。他的回答很简单,他们喜欢它的外观,但这并不意味着它不会在未来的迭代中改变。

数据

让我们从孔数和过滤器打开的百分比开始。CoffeeJack 的球洞数量是竞争对手的两倍。Wafo 处于不同的联盟,我认为它抢了我在 CoffeeJack 中试图讨论的话题的风头。就过滤器打开的百分比而言,Flair、CoffeeJack 和 Wafo 是最好的。

但是等等,也许不是。就天赋而言,球洞不会一直延伸到边缘。如果在计算中还考虑了边缘(以 1.8 的因子增加过滤器面积),Flair 具有与 VST 相同的过滤器打开百分比,如下所示:

Wafo 离大规模销售还有几个月的时间,CoffeeJack 似乎是目前可用的篮子中最大的一个。在与 Ashley 的交谈中,团队反复讨论了基于保持泵背压以维持流量的设计。他们的目标不是推出市场上最开放的过滤篮之一。愉快的意外?

在孔径方面,CoffeeJack 在孔径*均值和标准偏差方面与 VST、Kompresso 和 Flair 一致。咖啡插孔略小,但更多。Wafo 有大得多的洞,但我的算法试图计算假设圆形洞,Wafo 有矩形,所以我不确定影响。

我们可以看到球洞的分布,CoffeeJack 和 VST 和 Flair 的 Kompresso 都发生了变化。

空间分布

这些图像显示了相对孔径。所以黄色越多代表洞越大,深蓝色代表洞越小。希望这些洞的大小不会一成不变。不幸的是,他们有,这种模式在提取过程中被看到。我已经能够根据咖啡的流动方式和它们的孔洞模式来区分我的两个 Flair 签名篮子。即使是 VST 也没有这种理想的分布,虽然比以前的篮子要好。

左:VST,右:天赋

Kompresso 和 CoffeeJack 都有自己的。然而,CoffeeJack 不是无底洞,所以我不能说这种模式发挥了多大作用。在咖啡杰克的情况下,可能会有一个轻微的偏离角度的相机,因为大孔在顶部,小孔在底部。Kompresso 热点在拍摄过程中非常明显。

左:Kompresso,右:CoffeeJack

从 CoffeeJack 用过的冰球底部,我没有看到一个特定的模式,表明除了中心的深蓝色区域外,由于徽标而导致的非常低的流量。

Wafo 就是好玩!这可能在相机中有类似的角度问题。我没有拍摄图像,所以我只是展示我所拥有的。我为他们的过滤篮感到非常兴奋。

由于成像问题,Wafo 有几个孔

CoffeeJack 恰好出现在未来咖啡篮的浪潮中。几年前我对精确篮子的批评和现在一样:它们在屏幕上的分布不够精确,因为有热点和冷点。

我希望对有更多孔的过滤篮进行更多的研究,因为我怀疑它们会允许更精细的研磨,我相信这是获得更高萃取率和更好咖啡的关键。

如果你愿意,可以在推特、 YouTubeInstagram 上关注我,我会在那里发布不同机器上的浓缩咖啡照片和浓缩咖啡相关的视频。你也可以在 LinkedIn 上找到我。也可以关注我在订阅

我的进一步阅读:

我未来的书

我的链接

浓缩咖啡系列文章

工作和学校故事集

一个月后的浓缩咖啡:回顾

原文:https://towardsdatascience.com/coffeejack-for-espresso-after-a-month-cf6253ed77b6

咖啡数据科学

原始数据

收到 CoffeeJack 后,我开始将它与其他机器进行比较,特别是 Kompresso,因为我的目标是将其与我的标准旅行浓缩咖啡机进行比较。我对 CoffeeJack 的表现感到惊喜。因此,我想详细介绍一下这款设备的一些机制,以及使用它时获得的一些数据。

披露:coffee jack 的 Ashley 送了我一个设备,因为他对我做技术评论的专长感兴趣。在我发表之前,CoffeeJack 的人不会看到这篇评论,但我在这里分享了我对 CoffeeJack 的评论以及我的经历。Ashley 对设计讨论非常积极和开放,所以我对 CoffeeJack 公司充满希望。

这是我拍摄的一段视频,这是我使用这台机器的典型方式:

作者提供的所有图片和视频

快速总结:我喜欢 CoffeeJack 足以取代 Kompresso 作为我的旅行机器,尽管有一些设计缺陷,但由于创新的设计,它的性能得到了克服。

机器

CoffeeJack 应该是一台便携式浓缩咖啡机,这样任何人都可以在各种环境下制作浓缩咖啡。在收集进一步数据以寻找任何缺陷之前,我首先评估了设备的子单元。

压力:coffee jack 的目标是能够为浓缩咖啡或一些人认为合适的浓缩咖啡建立 9 巴的压力。我不像数据显示的那样在高压下工作压力越低,提取效果越好。所以咖啡杰克对我来说很有能力。因此,我没有做工作来验证压力能力,我只按了两次机械压力限制器。

温度:我用开水做了实验,发现放入小室后温度会降到 84C 到 88C 之间。然而,即使用蒸汽预热设备,一旦加入水,水几乎不超过 88℃。我通过更长时间的预输注进行补偿,通常,我在我的杠杆机器上使用 100C 或更高的温度。

淋浴帘:水泵直接进入淋浴帘,使水通过淋浴帘的中心,直到室内压力增大。这是我最关心的,我很惊讶没有更多的中心通灵。我怀疑在水泵和淋浴屏之间安装一个分散屏会有所帮助。

滤篮 :第一个瑕疵是豆标正中央。这中断了流动。但是过滤篮的主要优点是它的孔数是普通浓缩咖啡篮的两倍。淋浴屏也是如此。我认为这种设计选择可以让 CoffeeJack 在性能方面克服其他问题。

分析表明过滤网在孔的数量和分布上是有趣的。

移动式过滤器:它有一个阀门,但是你可以把阀门拿掉。所以你可以让它不受压。然而,它不是无底洞。这使得从屏幕底部进行某些类型的故障分析成为不可能。然而,他们可以为人们设计一个无底的篮子,而不必改变机器本身。我解决了这个问题,对于大多数人来说,我认为这不是问题。

设备/技术

浓缩咖啡机:咖啡插孔

咖啡研磨机 : 小生零 & VSSL

咖啡:家庭烘焙咖啡,中杯(第一口+ 1 分钟)

镜头准备:常规镜头和断奏夯实

预灌注:长,约 60 秒

注入:低压

其他设备: Atago TDS 计Acaia Pyxis 秤

绩效指标

我使用了几组指标来评估技术之间的差异:

最终得分 是评分卡上 7 个指标(辛辣、浓郁、糖浆、甜味、酸味、苦味和回味)的*均值。当然,这些分数是主观的,但它们符合我的口味,帮助我提高了我的拍摄水*。分数有一些变化。我的目标是保持每个指标的一致性,但有时粒度很难确定。

强度半径定义为 TDS vs EY 的控制图上从原点开始的半径,所以 IR = sqrt( TDS + EY)。这一指标有助于标准化产量或酿造比的击球性能。

在浓缩咖啡过程中,较高的萃取率导致较低的 gTDS 读数。

单圆盘分析

我从一个镜头开始,取了一些核心样本。我用这些来确定咖啡的哪些部分没有被提取。在这种情况下,中心没有被很好地提取,但与其他数据相比,这是一个异常值。

********

多张照片

我通过多次拍摄收集了这些数据,侧面比中心提取得更多。这台机器仍然像其他咖啡机一样有侧沟。

随着时间的推移,我看着我的 CoffeeJack espresso 镜头,我能够从最初的几个镜头中提取出很好的内容。这需要 1 分钟的预注入,这也是 Kompresso 或 Flair 的典型做法。

********

与其他人相比

我的目标不是做配对拍摄,相比三个机器,我只有 8 对拍摄。这是一个探索性的数据,表明 CoffeeJack 与其他浓缩咖啡机处于同一领域。就 TDS 而言,其他机器略有优势。

然而,就口味而言,CoffeeJack 略胜一筹。我怀疑这是由于在预灌注阶段水温冷却导致拍摄结束时不那么刺耳。

********

冷酿造

这种机器最大的优点之一就是你可以制作冷煮浓缩咖啡。目的是使用室温或更冷的水,你可以用水填充冰球。然后静置一到五分钟或更长时间,慢慢提取咖啡。

我用 CoffeeJack 做过这个,结果和 Kompresso 非常相似。当没有热水时,这是一个有用的工具。

我喜欢探索 CoffeeJack 上的数据,我的目标是继续优化性能,以利用我所拥有的数据。

如果你愿意,可以在推特、 YouTubeInstagram 上关注我,我会在那里发布不同机器上的浓缩咖啡照片和浓缩咖啡相关的视频。你也可以在 LinkedIn 上找到我。也可以关注我在订阅

我的进一步阅读:

我的书

我的链接

浓缩咖啡系列文章

工作和学校故事集

图像生成和大规模语言建模

原文:https://towardsdatascience.com/cogview-image-generation-and-language-modelling-at-scale-8d358a0686d2

播客

丁明谈建设中国 DALL-E 的挑战

编者按:TDS 播客由杰雷米·哈里斯主持,他是人工智能安全初创公司墨丘利的联合创始人。每周,Jeremie 都会与该领域前沿的研究人员和商业领袖聊天,以解开围绕数据科学、机器学习和人工智能的最紧迫问题。

注意:由于录制问题,这一集的播客只有文字稿。你可以在下面找到这一集的摘要,然后是完整的书面采访。

2021 年初,OpenAI 发布了 DALL-E,这是一个强大的模型,可以从任意文本提示中生成图像。但就在 DALL-E 在加利福尼亚建造的同时,一个非常相似的模型正在 10000 公里外的中国开发。

该模型被称为 CogView,参与其开发的主要研究人员之一是清华大学的研究员丁明。

众所周知,CogView 是一个巨大的成就:一个 40 亿参数的模型,需要大量的计算预算和大量的软件工程和硬件来组合。但它也为国际人工智能研究的世界提供了一个迷人的窗口,比较跨语言生成模型的难度,以及英语和汉语之间的差异如何影响语言模型学习它们的速度。

在这一集的 TDS 播客中,Ming 和我一起谈论了构建 CogView 的挑战,它与 DALL-E 的比较,以及更多关于人工智能研究的国际方面。

以下是我在对话中最喜欢的一些观点:

  • CogView 基于 VQ-VAE(矢量量化变分自动编码器)架构。像典型的变分自动编码器(VAEs)一样,VQ-VAE 学会接受输入并将其压缩成潜在的表示,该表示可用于重建相同的输入。然而,VAEs 将输入映射到一个连续的潜在空间,而 VQ-VAEs 将输入映射到一个离散的潜在空间,有效地将它们归类到一个大但有限的类别集中。CogView 的 VQ-VAE 首先被训练重建图像,然后一个单独的语言模型被用来将用户输入的文本映射到 VQ-VAE 的潜在空间,图像在那里生成。
  • 尽管它们的功能非常相似,但 Ming 指出,很难将 CogView 与 DALL-E 进行比较,原因有二。首先,CogView 是在中文文本/图像对上训练的,而 DALL-E 是在英文上训练的,这意味着每个模型的提示不能在苹果到苹果的基础上进行比较。但是第二,OpenAI 还没有发布 DALL-E 模型,所以 CogView 的团队只能比较有限的一组公开的 DALL-E 文本提示及其相应的图像输出。
  • Ming 解释说,像 CogView 这样的图像生成系统的指标仍然不完善。例如,在某些情况下,它们的趋势与人类的判断相反,根据某些度量标准被评为高等级的图像对人类观察者来说一点也不吸引人。Ming 特别强调,到目前为止,对生成图像质量的定量测量更多地关注纹理,而对人类感知更重要的其他特征(如物体的形状)关注不够。
  • Ming 认为有前途的一种方法是评估生成图像的质量,不是通过直接测量这些图像的属性,而是通过使用图像字幕模型将图像转换回字幕,然后可以与输入到 CogView 的原始文本提示进行比较。无论这一策略最终能否奏效,它都强调了为生成性学习任务定义良好的衡量标准需要多少创造力。
  • OpenAI 决定不发布他们的 DALL-E 模型,而 Ming 和他的同事选择开源发布 CogView。Ming 认为这一决定是合理的,因为 CogView 的功能还没有发展到足以允许恶意使用的程度。然而,他确实认为这种情况在未来可能会改变:在未来两年左右的时间里,Ming 预计真实感图像的生成是可能的。他预计,随之而来的将是更大的风险,并可能需要政府干预和企业审计。

文字记录:

Jeremie :大家好,欢迎回到迈向数据科学播客。在今天的节目中,我将与清华大学的研究员丁明进行对话,他是 NeurIPS 论文的第一作者,该论文介绍了 CogView:一种由明和他的同事在今年早些时候开发并开源的生成式文本到图像模型。

据说,CogView 是一项巨大的成就。这是一个有 40 亿个参数的模型,需要大量的计算预算和大量的软件工程和硬件。

但它也为国际人工智能研究的世界提供了一个迷人的窗口,以及比较不同语言之间的生成模型的困难——以及英语和汉语之间的差异如何影响语言模型学习它们的速度。在本期“走向数据科学”播客中,Ming 和我一起讨论了构建 CogView 的挑战,它与同期开发的 DALL E 的比较,人工智能研究的国际方面,以及更多更多内容。

杰雷米:我真的很兴奋能有这次谈话,因为 CogView 是迅速崛起的中国人工智能生态系统中如此重要的一部分。这是中国人尝试大型语言和视觉模型的一个很好的例子。是什么让你决定参与这个项目?

:我其实很早的时候就开始这个方向了,可能比去年这个时候还要早。在此之前,没有人成功地使用文本在一般领域生成图像。我的意思是,在有限的领域有很多成功的案例,例如,人脸,动物或类似的东西,但没有人成功地将文本输入转化为图像。

我以前在阿里巴巴实习的时候做过一些图像生成的工作。在那里,我主要专注于生成与时尚相关的图像,以及一些通用领域的图像。然后突然之间,OpenAI 真正开始了他们的文本到图像的工作。我们发现他们的想法与我们极其相似,所以我们迅速要求更多的资源,使用更大的数据集和更多的参数来训练一个大模型。这原来是 CogView,我们终于在 NeurIPS 上发表了。

杰里米:这也是一个快速的转变,对吗?如果我没记错的话,CogView 的最大版本——40 亿参数版本——是在 OpenAI 的 DALL-E 模型之后四五个月推出的,对吗?

:实际上,在 OpenAI 发布他们的之前,我们已经完成了所有的代码和小型模型变体

杰瑞米:我没意识到已经这么*了。这也是一个非常有趣的项目——我的意思是,通用的文本到图像转换在两年前还只是科幻小说。你能告诉我一些让这一切发生的架构吗?我记得,它是基于 VQ-VAE 模型,所以如果你能从解释 VAE 部分开始,这可能对任何不熟悉变分自动编码器的人有帮助。

: VQ 和原来的很不一样。VAE 试图将输入映射到一组连续的潜在变量。虽然 VQ-VAE 的第一阶段是相似的,但中间层使用字典将连续的表示转化为离散的表示。所以你把初始层后得到的向量量化成从学习过的字典中提取的记号。VQ-VAE 的第二步是通过自回归模型对离散潜在特征的分布进行建模,因此不需要像普通 VAE 那样对潜在变量的 KL 散度和先验进行建模。

Jeremie :你有一个离散的潜在空间,而不是试图在一个连续的潜在空间中编码一些东西。因此,本质上,您是将输入映射到不同标签或不同类的有限集合。我假设那些离散的嵌入比连续的嵌入更容易让模型学习?

:对。事实上,采用像 VQ-VAE 这样的离散嵌入策略的一大优势是,它允许我们处理像语言这样内在离散的数据(从某种意义上说,单词是意义的离散包,你不能连续地从一个单词移动到另一个单词)。因此,VQ-VAE 以一种自然的方式打开了语言建模的大门——特别是像 GPT 这样的自回归语言模型。

语言数据的分布实际上很难参数化,因为它们太复杂了。如果我们处理的分布更简单,我们可以使用高斯混合模型,或者类似的模型。但是,如果您正在建模一个非常复杂的分布,比如文本数据中的下一个单词概率,或者一般情况下图像中的像素亮度,那么您需要一个更健壮的方法。

目前,最佳实践是使用像 GPT 这样的转换器,它只是通过字典学习分布。每次你想预测下一个单词的时候,你可以看看这个分布,找出一个在预测的分布下概率最高的单词。

这是一个非常灵活的方法,但是它确实有一个问题。语言词典中可能有成千上万的单词,并且迫使下游图像生成模型学习每个单词的语义表示是难以管理的。连续的单词嵌入太复杂了。如果您想将这些单词作为输入提供给另一个模型,例如图像生成模型,您需要一种方法来降低数据的复杂性,将其简化为更易于管理的形式。VQ-VAE 让我们做到了这一点,通过将复杂和连续的单词嵌入转化为少量简单、离散的表示,这些表示不会淹没下游的模型。

杰雷米:你认为自回归模型会在更广泛的图像生成方面开始统治 GANs 吗?看起来事情正朝着那个方向发展。

:是啊,肯定是。如果你问我,有三个原因让我们看到 GANs 直到最*一直占据主导地位。首先,甘斯很早就表现出色,激发了研究人员的想象力。我应该知道——以前我是一名研究甘斯的研究员。

但第二个问题是,生成性自回归模型实际上非常慢。原因不是因为车型大;这是因为自回归模型逐令牌生成其输出。例如,生成一幅 65000 像素的图像需要 65000 个推理步骤。如果没有生成前一个标记,就不能生成一个标记或一个像素。这意味着您不能像使用 GAN 那样在 GPU 上并行化图像生成任务。这个速度和并行瓶颈是我们用 CogView2 解决的。因此,也许在未来我们将开始看到非常快速的自回归模型,与基于扩散或基于 GAN 的模型相比,它具有更好的性能。

最后,还有一个问题是,当前的自回归模型总是基于变压器架构,其中包括自关注机制,这会导致速度变慢,特别是在以非常高的分辨率工作时。因此,我们还需要像局部注意力这样的特殊技术来降低这种复杂性——这也是我们一直在用 CogView2 做的工作的一部分。

但我的感觉是,自回归模型将在未来几年主导计算机视觉。

Jeremie :对于变形金刚来说,似乎有更多容易改进的地方。例如,我们看到 DeepMind 的 Gopher 模型的效率提高了 25 倍,这仅仅来自于架构和培训流程的优化。有趣的是,您的工作还显示,只需稍加调整,您就可以解决许多并行化问题,并获得更高效的输出生成。

你认为持续的架构调整会在未来几年推动变形金刚取得更多进展吗,或者你认为从现在开始的大部分进展会来自于或多或少地扩大我们今天拥有的相同技术?

:这其实是一个很有意思的话题。一般来说,哪些技术能够成功加速变形金刚和人工智能相关的计算,很大程度上取决于你的数据。如果你正在训练一个 NLP 模型,只是为了句子,那么你不能用其他技术来代替注意力,因为注意力在 NLP 中非常重要。但是我们发现在计算机视觉中,注意力实际上并不重要。例如,在基于视觉的分类问题的背景下,其他作品也发现了同样的事情。例如,元形成者发现,*均池和其他一些操作可以在不降低性能的情况下取代变形者的注意力。所以答案从根本上取决于数据和任务。

因此,当涉及到生成任务时,我认为注意力也可以被更轻量级的操作所取代。自那以后,有其他论文表明,在某些情况下,转移注意力可以提高效率——因此,架构调整能让我们在图像生成领域走多远还有待观察。

但是,回到文本到图像生成的例子。我们不仅想塑造形象;我们还需要理解文本。这需要一个更强大的架构,减少针对特定任务的优化。所以这迫使我们不得不回到最初的注意力机制。因此,我的预测是,在几个月后,对于像图像分类或分割这样的任务,我们可能会发现自我注意力被取代了,但对于像图像生成或图像字幕这样的跨模态任务,我们会发现原来的注意力机制保留了下来。

用定制组件取代自我关注的想法是一个有趣的反潮流,尤其是考虑到我们最*看到的围绕变形金刚的整合。我真的很好奇这种情况是否会持续下去。

因此,特别关注一下 CogView 模型,我很想知道它在性能方面与 DALL-E 相比如何。我知道这个模型有点小——我想 DALL-E 大约有 120 亿个参数,而 CogView 有 40 亿个参数。但是当我们比较两个不同的模型时,我们需要考虑的不仅仅是参数的数量。

:很有意思,NeurIPS 的评测人员也问过我们类似的问题,但是很难将 CogView 与 DALL-E 进行比较,因为 DALL-E 不是一个开源项目。因此,我们只能看到 OpenAI 随 DALL-E 论文一起提供的例子,但很难评估它们有多大的代表性,或者它们与 DALL-E 训练数据有多相似。

虽然很难进行公*的比较,但我们至少可以通过查看 OpenAI 与 DALL-E 一起发布的一些指标来比较 CogView 和 DALL-E,并查看它们与 CogView 的对比情况。虽然我们发现,根据其中一些指标,我们实际上获得了更好的性能,但很难知道如何进行比较。一个关键问题是,众所周知,图像生成的指标很差,最终只有人工评估才是图像质量的可靠指标。

杰里米:这些指标是什么?你如何看待它们与人类评价不一致?

明:一个流行的度量标准是 FID(弗雷歇初始距离)。为了计算 FID,您需要将生成的图像输入到一个视觉模型中,比如 Inception V3。然后,你观察一个更深层次网络中神经元的活动,并与你在模型中输入真实图像时看到的活动进行比较。直觉告诉我们,一个生成的狗的图像和一个真实的狗的图像都应该引起网络深层的“狗”神经元放电。因此,由真实图像和生成的图像引起的点火模式越相似,生成的图像的质量就越高。

这有一个固有的限制:它适用于有真实图像可以比较的情况。但是生成的图像可能被设计成根本不对应于真实对象,这使得 FID 对于一般领域的图像质量评价是不利的。

但也许更令人担忧的是,我们经常发现 FID 和人类的判断在某些情况下是完全不相关的。

杰雷米:为什么会这样?

: FID 和盗梦空间配乐是为了捕捉生成图像的“真实感”。但 InceptionNet 倾向于锁定对人类不太重要的功能。特别是,人类专注于形状和颜色来推导图像的含义,而 InceptionNet 往往更关注纹理。所以你经常会发现,那些纹理生动、逼真但形状完全不正确的场景会有非常高的盗梦空间分数。

这就是为什么我们在我们的 CogView 论文中设计了一个不同的指标。我们获取一个文本提示,用它以通常的方式生成一个图像,然后使用这个图像,我们尝试重建原始的文本提示。这里的想法是图像越好,即时重建应该越好。

Jeremie :这就像你在设计一个超级笨重的自动编码器,你将文本映射到图像,图像映射到文本,然后查看你的重建误差。在某种程度上,这就像你过去可以去谷歌翻译,来回翻译一段文本,直到它变得乱码,但随着算法变得更好,它变得更加强大。

:正是。我认为重建误差通常有助于更好的度量。

杰瑞米:这很有道理。你在论文中还提到了微调——你使用 CogView 并针对特定的下游任务进行微调,比如风格学习和超分辨率。那些管道看起来像什么?

:风格学习和超分辨率其实差别很大。基于样式的微调与预训练非常相似,因为它只是使用特定样式中的一些图像,并在文本提示中添加该样式的单词。所以我们只是增加标题,用精选的图片进行训练。以这种方式,模型可以学习用于一种风格的单词之间的相关性,以及如何在图像中表达该风格。

超分辨率是一个不同的故事。假设我们给 CogView 一个类似“Big Ben”的提示。我们将得到一个大本钟的图像,天空和其他建筑物围绕着它。但是现在假设我们把提示改成类似于“大本钟的特写镜头”。在第二种情况下,我们希望看到更多的细节,实际上是更高的分辨率。因此,该模型清楚地知道一个细节层次,它并不总是在提示时显示。有挤出更多的潜力。

但生成图像的质量受到模型分辨率的限制,而模型分辨率又取决于它生成的标记数量。我们可以通过训练模型来专门将较短的令牌序列映射到较长的令牌序列来克服这一点,比如说,从 16 x 16 分辨率的图像到 32 x 32 分辨率的图像。然后,我们可以使用滑动窗口将生成的图像的每个部分放大到更高的细节水*。

微调过程足够短,我们并没有真正教给模型任何新东西——我们更多的是让它揭示它已经拥有的知识。总之,我们只用了大约 20 个小时的训练时间就完成了这项任务。从这个意义上说,这很像即时工程——一个快速、低计算量的过程,它精炼系统的输出,以确保它揭示了它已经包含的所有知识。

杰里米:这和我想问的另一个问题有关。一个 40 亿个参数的模型是相当大的,我想实现它会有很多技术上的挑战。为了让你的基础设施或模型发挥作用,最困难的事情是什么?

:最大的问题其实是不稳定。这是所有大型模型的问题,但在我们的 cae 中更明显,因为我们特定任务所需的数据是非常异构的:它包括文本和图像。我们论文中的一个发现是,训练过程的不稳定性水*实际上取决于数据,而异构数据通常会导致更大的不稳定性。我们最终使用基于算法的方法解决了这个问题。我们通过开发一种新的结构改变了转换器的体系结构,我们称之为三明治层范式,并使用了另一种操作,我们称之为精确瓶颈松弛,这两种操作在本文中都有讨论。

但是 OpenAI 和谷歌可以用其他不需要这些变通办法的方法来解决不稳定问题。他们可以获得更多的计算能力,特别是谷歌可以使用优化的硬件,如 TPU 和 BF16 格式的数据,这更适合机器学习。由于某些图层中的值可能会变得非常大,因此提高浮点精度非常重要。

我们现在使用的这种老式 GPU,可以追溯到 A100 之前,不支持 BF16 格式。在 OpenAI,他们对这个问题的解决方案只是使用一些工程技术将某些图层的数据映射回单精度,以及各种其他技巧。他们在论文中讨论了其中的一些技巧,但不是非常详细——更重要的是,在我们进行项目时,OpenAI 的论文还没有发表,所以我们必须找到另一种解决方案。

我们决定改变变压器的架构。这种方法非常成功,也在最*的一些论文中被“发现”——比如脸书人工智能研究所的 NormFormer。我已经和他们的作者谈过了,他们的手法其实和我们很像。

Jeremie :有趣的是,人工智能训练现在比过去更像是一个硬件问题。

我想把工作重心转移到与语言更相关的方面。当我们看英语这样的语言时,我们的字典有一定的维度——每个单词背后都有一定的含义。英语语言转换器通常使用 500 维,word2vec 以前使用 200 到 300 维,但粗略地说,我们谈论的是 300 到 500 维来表示英语单词。汉语也是如此吗?或者说,两种语言中每个标记的信息量有所不同吗?

:这个话题很有意思。首先,我应该注意到 word2vec 的维数并不总是最优的:容量更大的模型可能会发现更大的维数更优越。当然,还有用于嵌入的抽象级别的问题,以及我们是在处理单词级、音节级还是字符级嵌入。在一般情况下,你会发现通过使用更高的维度,你会做得更好。

在标记化部分,不同的语言有更多的不同。目前很多方法都是看音节或者字符层面,或者其他的子词方法。这很有用,因为英语单词有词根和时态,以及传达特定含义的其他成分。

但在中国,一切都不一样。中文书写包含单个字符,通常具有明确的含义。有时它们在普通短语中聚集在一起,但单个字符是语义承载元素,而英语单词中的单个音节不是。汉语缺少时态;在英语中,你可以在动词后面加上“ed”来表示动作发生在过去,而在汉语中,时态是从上下文中推断出来的。

因此,目前中文建模比英文建模容易。当一个语言模型第一次学习英语时,它必须学会所有这些在中文中没有的变化的细微差别。这需要大量的时间和计算,否则这些时间和计算将被花费在更抽象的推理上。

这让我回想起我在高中和大学学习中文的日子。很明显,它是一种比英语更有逻辑性的语言,因此更容易学习(或者至少,口头形式是,字符更难学!).所以在某种程度上,也许我不应该对机器也更容易学习感到惊讶。

当我们谈到语言的话题时,现在已经有一段时间了,人工智能的研究大部分是用英语进行的。但是这些天,第一次有一个真正大规模的令人印象深刻的人体工程在中国进行。

中国发表的大部分研究是倾向于英文还是中文?你认为语言障碍对跨境合作有什么影响?

:是的,即使在中国,对英语语言数据的研究仍然很多。这背后的一个主要驱动力是需要将我们的模型的性能与在英语世界中构建的模型的性能进行对比。然而,这不仅仅适用于中国的研究人员:如果中国、俄罗斯和法国的研究人员想要衡量他们的模型的有效性,并将其与社区中其他人发布的内容进行比较,他们都必须用英语训练模型。

这在很大程度上是为什么我们的语言建模至今如此关注英语。但我们也在研究一些方法,让我们能够更专注于中文工作,同时不牺牲我们衡量自己相对于他人工作进展的能力。这很重要,因为最终,如果我们希望中国消费者从语言建模中受益,我们需要能够用中文执行语言生成。从这个角度来看,我们不得不如此关注英语语言生成,这是一个真正的劣势:大模型很昂贵,如果它们只对基准测量有用,它们就不会带来经济价值。这项工作的大部分集中在提出通用标记器,使我们的模型能够同时学习英语和汉语,有效地创建双语模型,在中国创造经济价值,同时也可与国外开发的英语模型相媲美。

Jeremie :这很有意思,我从未想过语言障碍会成为非英语研究的负担。

我想问的 CogView 项目的另一个方面是是否向公众发布这个模型的问题。显然,CogView 现在是开源的,所以您已经决定发布它。相比之下,OpenAI 以安全考虑和对恶意使用的担忧为由决定不发布 DALL-E,这一想法是人们可能会用它来伪造证据,或产生虚假信息等等。当你决定发布 CogView 时,你考虑过这种可能性吗?

:我们决定把这个模型公布出来,让更多的研究人员能够接触到它,让更多的人意识到这种模型应用到图像上的力量。我不认为滥用目前是一个非常大的问题。我们的系统目前还不存在——也许两年后,新模型将能够以一种潜在的强烈负面社会影响的方式生成非常逼真的图像,我可以看到发布这些模型会有多大的风险。

杰瑞米:我们如何能够提前猜测一个模特在产生真正的负面社会影响之前要有多好?

似乎不可能知道。当 GPT-3 API 发布测试版时,甚至 OpenAI 也没有意识到它的全部功能——其中一些功能被发现被恶意使用(以网络钓鱼、虚假信息生成和传播等形式)。当然,我们应该考虑到类似的令人讨厌的意外的可能性,即使模型乍一看似乎没有令人担忧的能力水*?

:对。我的意思是,我的观点是,因为技术本身实际上是整个行业努力的产物,没有一个实验室不发布模型的决定会产生很大影响。将需要其他方法来有效管理这些系统的社会影响——包括新的法律和法规。不发布一个模型实际上并不是事情计划中的一个重要因素。

杰雷米:这是一场非常有趣的辩论,我完全同意政府必须在其中发挥作用,按照事情发展的速度,这可能是迟早的事。

政府已经开始考虑的一个领域是人工智能安全,这让我想到了我想问你的最后一件事。中国人工智能社区如何看待人工智能安全和人工智能比对等问题?

我知道你不是比对研究人员,这完全没问题,但在西方,特别是在美国,有一个相当小且快速增长的研究人员社区,他们担心人工智能的长期未来风险。这是中国正在讨论的事情吗?

:我认为人工智能的社会影响问题在美国和中国都没有得到充分的讨论。

还有相关的问题和挑战。例如,考虑“远程监督”关系提取的情况,在这种情况下,我们训练语言模型从从网络上爬取的文本中单词(或音节等)的共现中学习。随着像 GPT-3 这样的模型的出现和广泛使用,越来越多的基于网络的内容本身将是人工智能生成的——所以我们冒险在前几代人工智能的输出上训练人工智能系统。因此,这些较旧的系统正在引入一种非常具有挑战性的噪声,这种噪声有可能使基于网络的爬行在未来无法用作数据集生成的手段。

例如,在 BAAI(北京人工智能研究院)和其他机构,他们可能会就人工智能模型的安全性进行一些内部讨论,但他们从根本上缺乏控制人工智能安全进程的权力,因为人工智能模型正在世界各地开发。这里需要全球协调。

杰雷米:如此复杂的问题——我完全同意。我的意思是,这需要某种全球协作。这是不可避免的。无论如何,中国和美国将不得不讨论这个问题,其他国家也是如此。

非常感谢你,明。迷人的聊天。

:非常感谢。

posted @ 2024-10-18 09:32  绝不原创的飞龙  阅读(337)  评论(0)    收藏  举报