TowardsDataScience-博客中文翻译-2020-四十七-

TowardsDataScience 博客中文翻译 2020(四十七)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

解释期望最大化

原文:https://towardsdatascience.com/expectation-maximization-explained-c82f5ed438e5?source=collection_archive---------0-----------------------

马文·朗斯多夫在 Unsplash 上的照片

用于聚类、NLP 等的通用算法

期望最大化(EM)是 60 年代和 70 年代发展起来的经典算法,具有多种应用。它可以用作无监督聚类算法,并扩展到 NLP 应用,如潜在狄利克雷分配,用于隐马尔可夫模型的鲍姆–韦尔奇算法,以及医学成像。作为优化过程,它是梯度下降等的替代方案,主要优点在于,在许多情况下,可以分析计算更新。不仅如此,它还是一个思考优化的灵活框架。

在本文中,我们将从一个简单的聚类示例开始,然后讨论该算法的一般性。

无监督聚类

考虑这样一种情况,您有各种数据点,并对它们进行了一些测量。我们希望把他们分到不同的组。

对老忠实喷发数据的期望最大化(维基百科)

在这个例子中,我们有黄石公园标志性老忠实间歇泉喷发的数据。对于每一次喷发,我们都测量了它的长度和自上次喷发以来的时间。我们可以假设有两种“类型”的喷发(图中的红色和黄色),对于每种类型的喷发,结果数据由(多元)正态分布生成。顺便提一下,这被称为高斯混合模型。

k 均值聚类类似,我们从随机猜测这两个分布/聚类开始,然后通过交替两步进行迭代改进:

  1. (期望)概率性地将每个数据点分配给一个聚类。在这种情况下,我们计算它分别来自红色集群和黄色集群的概率。
  2. (最大化)根据聚类中的点(按第一步中分配的概率加权)更新每个聚类的参数(加权平均位置和方差-协方差矩阵)。

注意,与 k-means 聚类不同,我们的模型是生成的:它旨在告诉我们数据生成的过程。反过来,我们可以对模型进行重新采样,以生成更多(虚假)数据。

明白了吗?现在我们要用方程做一个一维的例子。

考虑具有单个测量值 x 的数据点。我们假设这些数据点由两个簇生成,每个簇遵循正态分布 N(μ,σ)。第一个集群生成数据的概率为π。

因此,我们有 5 个参数:混合概率π,以及每个聚类的平均值μ和标准差σ。我将它们统称为θ。

我们模型的 5 个参数,统称为θ

观察到值为 x 的数据点的概率是多少?设正态分布的概率密度函数用ϕ.表示为了让符号不那么混乱,我将使用标准差作为参数,而不是通常的方差。

观察值为 x 的点的概率

观察我们的整个数据集的 n 个点的概率(可能性)是:

观察整个数据集的可能性

我们通常选择取这个的对数,把我们的乘积变成一个更容易管理的和,对数似然。

对数-观察我们数据的可能性

我们的目标是最大化这一点:我们希望我们的参数是最有可能观察到我们观察到的数据的参数(最大似然估计)。

现在的问题是,我们如何优化它?由于对数中的和,直接分析性地这样做将是棘手的。

诀窍是想象有一个潜在变量,我们称之为δ。它是一个二进制(0/1 值)变量,用于确定某个点是位于聚类 1 还是聚类 2 中。如果我们知道每个点的δ,计算参数的最大似然估计就很容易了。为了方便匹配第二个聚类的δ为 1 的选择,我们将π转换为点在第二个聚类中的概率。

请注意,总和现在不在对数之内。此外,我们获得一个额外的总和,以说明观察到每个δ的可能性。

反过来假设我们确实观察到了δ,最大似然估计很容易形成。对于μ,取每个聚类内的样本均值;对于σ,标准差也是如此(总体公式,即最大似然估计)。对于π,第二个聚类中点的样本比例。这些是每个参数的最大似然估计量。

当然,我们没有观测到δ。对此的解决方案是期望最大化算法的核心。我们的计划是:

  1. 从参数的任意初始选择开始。
  2. (期望值)形成δ的估计值。
  3. (最大化)计算最大似然估计量来更新我们的参数估计。
  4. 重复步骤 2 和 3 以收敛。

同样,您可能会发现考虑 k-means 聚类很有帮助,我们也是这样做的。在 k-means 聚类中,我们将每个点分配到最近的质心(期望步长)。本质上,这是对δ的硬估计。很难,因为其中一个集群的值为 1,其他所有集群的值为 0。然后,我们将质心更新为聚类中点的平均值(最大化步骤)。这是μ的最大似然估计。在 k 均值聚类中,数据的“模型”没有标准偏差。(“模型”在吓人的引号中,因为它不是可生成的)。

在我们的例子中,我们将改为对δ进行赋值。我们有时称之为责任(每个集群对每个观察的责任有多大)。我们将把责任标为ɣ.

每个集群对于数据点 I 的责任ɣ

现在我们可以写下这个例子的完整算法。但在此之前,我们将快速回顾一下我们定义的符号表(有很多)。

符号表

算法是这样的:

我们例子中的期望最大化算法

注意,对聚类 1 的μ和σ的估计是类似的,但是使用 1–ɣ作为权重。

现在我们已经给出了一个算法的例子,希望你已经有了感觉。我们将继续讨论一般的算法。这基本上相当于用稍微复杂一点的变量来修饰我们所做的一切。这将使我们能够解释为什么它会起作用。

一般期望最大化

让我们转到一般设置。设置如下:

  1. 我们有某种形式的数据 X。
  2. 我们假设还有未被观察到的(潜在的)数据δ,不管是什么形式。
  3. 我们有一个参数为θ的模型。
  4. 我们有能力计算对数似然ℓ(θ;x,δ)。具体来说,观察我们的数据的概率日志指定了给定参数的潜在变量的赋值。
  5. 在给定一组参数的情况下,我们还能够使用该模型来计算条件分布δ| X。我们将把这个 P(δ| X;θ).
  6. 因此,我们可以计算对数似然ℓ(θ;x)。这是在给定参数的情况下观察到我们的数据的概率的对数(没有假设潜在变量的赋值)。

使用 P 来表示概率,我们现在可以使用链式法则来写:

概率的链式法则

这里的符号可能很微妙。这三项都以参数θ为给定值。

  1. 左边的第一项是观察数据的概率和指定的潜在变量赋值。
  2. 右手边的第一项是给定观察数据的潜在变量的指定赋值的概率。
  3. 最后一项是观察数据的概率。

我们可以取对数并重新排列术语。然后在第二行我们将做一个符号的改变(而且是一个令人困惑的改变)。别怪我,不是我发明的):

对于前两个术语,有必要回顾一下它们在我们上一个示例的上下文中是什么。第一个,ℓ(θ;x),是我们要优化的。第二个,ℓ(θ;x,δ)是分析上容易处理的。

高斯混合模型示例中的似然公式

还记得我说过,给定参数θ,我们可以计算条件分布δ| X 吗?这就是事情变得疯狂的地方。

我们将引入第组第二组相同的参数,称之为θʹ.我有时也会用一顶帽子(扬抑符)来表示它,就像这个“ê”所戴的帽子一样。把这组参数想象成我们的电流估计值。我们将对公式中的θ进行优化,以提高我们的估计值。

现在,我们将获得条件分布δ| x,θʹ的对数似然性的期望,即给定数据和当前参数估计的潜在变量的分布。

左手边的项不变,因为它不知道/不关心δ(它是一个常数)。同样,期望值超过了δ的可能值。如果你跟随我们的例子,术语ℓ(θ;x,δ)在我们取期望值后改变,所以δ被ɣ.代替

高斯混合模型例子中似然的期望。

现在,很快地,为了改善我们正在进行的记数噩梦,让我们介绍一下右手边的两个期望的简写记数法

预期可能性的速记符号

该算法变成:

一般期望最大化算法

为什么有效

证明这一点的最重要的工作是考虑函数 R(θ,θʹ).这种说法是,当θ=θʹ.代替一个完整的证明,让我们想想 R 计算什么。去除对数据 X 的依赖(在我们期望的分布和似然函数之间共享),R 示意性地变成

函数 R 的示意形式

换句话说,我们有两种概率分布。我们使用一个(由θʹ参数化)来生成数据δ,我们使用另一个(由θ参数化)来计算我们所看到的概率。如果δ仅代表一个数字,并且分布具有概率密度函数,我们可以写(再次,示意性地)

特殊情况下函数 R 的示意性形式

我以类似于 Kullback-Leibler (KL)散度的形式写下了这一点,这(几乎)是两个概率分布之间距离的度量。如果我们从一个常数 R(p||p)中减去 R(q||p ),我们将得到 KL 散度,它在 0 以下有界,并且当 q=p 时只有 0。换句话说,当 q=p 时,R 最大。这是关于 KL 散度的标准结果,可以用詹森不等式证明。⁴

现在唯一要做的事情是考虑更新步骤前后的可能性之间的差异:

更新步骤后可能性的提高

我们选择新的参数来最大化 Q,所以第一项肯定是正的。根据上面的论证,R 通过将旧参数作为其第一个参数而最大化,因此第二项必须是负的。正减去负就是正。因此,这种可能性在每次更新时都会增加。每一步都保证让事情变得更好。

还要注意,我们不需要优化 q。我们所要做的就是找到一些方法让它变得更好,我们的更新仍然保证让事情变得更好。

结论

希望你现在对这个算法有一个好的感觉。从数学的角度来说,关键方程就是下面的可能性。在那之后,我们只需要对旧参数进行期望(期望步骤),并表明我们可以优化右边的第一项。正如我们以高斯混合模型为例,第二项通常更容易优化。第三个学期我们不用担心,不会搞砸什么的。

后退一点,我想强调 EM 算法的强大和有用性。首先,它代表了我们可以通过交替处理潜变量(取参数为固定已知)和处理参数(取潜变量为固定已知)来引入潜变量然后计算的思想。这是一个强有力的想法,你会在各种环境中看到。

第二,该算法天生快速,因为它不依赖于计算梯度。任何时候你可以解析地解决一个模型(比如使用线性回归),它会更快。这让我们能够分析棘手的问题,并通过分析解决部分问题,将这种能力扩展到迭代环境中。

最后,我想指出,关于 EM 算法还有很多要说的。它推广到进行最大化步骤的其他形式和变分贝叶斯技术,并且可以以不同的方式理解(例如作为最大化-最大化或者作为在统计流形上的相互对偶仿射连接(e-和 m-连接)下到子流形的交替投影)。以后会有更多的报道!

感谢

这种讨论很大程度上遵循了统计学习中的要素,尽管速度更慢一些。特别感谢芳芳李告知我这个奇妙的算法。

笔记

[1]潜在的狄利克雷分配通常适合于变分贝叶斯方法,一种期望最大化的扩展。例如,参见 sklearn 实现。

[2]在这个平台上不可能键入超过θ的帽子。请给我们一些乳胶。

[3]我没有将距离放在引号中,因为我不是指 Kullback-Leibler 散度(这不是一个度量标准),而是指 Fisher 信息度量标准(这是一个度量标准)。这两者有很深的联系;对我们来说,我们可以说当实际距离为 0 时,KL 散度为 0。

[4]对这一点的标准证明似乎是对詹森不等式的笨拙的求助。我的版本也是手工的,但是通过 KL-divergence 合并了 Jensen 的版本。

解释了 GMM 的期望最大化

原文:https://towardsdatascience.com/expectation-maximization-for-gmms-explained-5636161577ca?source=collection_archive---------10-----------------------

内部 AI

直观、实用的数学解释

在这篇文章中,我们将以我能想到的最清晰的方式,回顾用 EM 训练高斯混合模型的过程。到本文结束时,您应该对 GMM、EM 做什么以及所有这些的应用有了更广泛的理解。

我们将讨论以下几点:

  1. 什么是 GMM?我们为什么使用 GMM?
  2. 训练 GMM
  3. 用 EM 训练 GMM
  4. 硬/维特比 EM
  5. EM 在 GMM 的应用

顺便提一下,生成这些图形并将其放入交互式 web 应用程序的所有代码都在专用的 Github 存储库中:

[## 梅尔法比恩/EM _ GMM _ 嗯

说明 GMM 和 hmm 的 EM。在 GitHub 上创建一个帐户,为 maelfabien/EM_GMM_HMM 的发展做出贡献。

github.com](https://github.com/maelfabien/EM_GMM_HMM)

一.什么是 GMM?我们为什么使用 GMM?

如果你对 EM 感兴趣,你可能已经知道高斯混合模型。在这一节中,我提供了一些关于我们为什么需要 GMM 的思考,以及对于某些任务,它与其他算法相比如何,所以阅读它可能仍然是有趣的。

1.什么是高斯混合模型?

GMM 是 M 个高斯密度分量的加权和。

它是一个概率模型,假设数据点是由混合高斯成分生成的。GMM 的概率分布函数可以写成:

GMM 的 PDF

其中参数记为λ,X 是观测值,我们给每个高斯密度分配一个权重,使得权重总和为 1。GMM 的参数是:

  • 每个分量的平均向量
  • 每个分量的协方差矩阵
  • 以及与每个组件相关联的权重

我们可以在 Python 中用随机参数从高斯混合模型中生成数据(您会在 Github 资源库中找到附带的代码),并更改组件的数量。

2.为什么我们使用高斯混合模型?

假设您有一个包含两个要素的数据集。绘制数据集时,它可能如下所示:

具有两个聚类的数据集

很明显,这里有两个集群。我们通常希望解决两个主要任务:

  • 聚类这些数据点,传统上用一种聚类算法
  • 建模基础分布并识别生成模型的参数,其可以例如用作语音处理中的输入,以评估观察值由该 GMM 生成的可能性有多大

现在让我们讨论解决这些任务的两种常见方法,并将它们与高斯混合模型进行比较:

k-Means 聚类与 GMM 聚类

k-Means 是聚类的常用选择。我们将在本文中看到,当用特殊类型的 EM 算法求解时,k-Means 实际上是 GMMs 的一个特例。但是我们会回来的。

好了,你可能知道 k-Means 迭代地识别每个聚类的质心坐标。因此,它仅依赖于 1 个分量,即每个聚类的平均值。当两个聚类的均值重叠,但协方差矩阵不同时,可能会出现问题。我们可以说明在这个任务中,与 k-Means 相比,GMM 工作得有多好。

聚类的 K-Means 与 GMMs

下面总结了 K-Means 和 GMM 之间的主要区别。你可能会开始看到,如果我们考虑一个身份协方差矩阵,并在一个额外的假设下,k-均值和 GMMs 将最终成为相同的解决方案。

请注意,对于 k-Means 和 GMM,您需要指定所需的聚类数,这可能是一项艰巨的任务。我们会回来的。

使用高斯和 GMM 对分布进行建模

如果我们现在讨论第二个任务,即建模分布,高斯分布通常是一个自然的选择。然而,在我们的数据具有两个聚类的情况下,选择单个高斯模型来模拟该数据是一个问题。事实上,如果我们计算数据的平均值,我们可能会在一个绝对没有数据点的区域结束,这与平均值的最大似然估计应该给我们的结果正好相反。

用一个高斯模型模拟这些数据

相反,使用 2 个高斯函数会更有趣,并且允许我们:

  • 第一高斯分布的均值和协方差
  • 第二高斯分布的均值和协方差
  • 每个高斯分量的权重

二。训练 GMM

最终,您将需要训练一个 GMM,这意味着识别正确的参数集来描述您的 GMM。

GMM 参数

但是我们如何解决 GMM 和估计这些参数呢?让我们首先解决一个高斯作为一个有用的提醒。

1.求单高斯的参数

为了识别单高斯的参数,我们应用了最大似然估计 (MLE):

单高斯模型的最大似然估计

L 是给定参数的观测值序列的似然性。我们的目标是最大化这种可能性,以确定最佳参数集,使我们的观察最“可能”。为了方便起见,为了进行求和,我们通常最大化对数似然,因为:

对数似然

我们只需要将偏导数设为 0,就可以获得参数的最大似然估计:

单高斯参数的极大似然估计

2.求高斯混合模型的参数

当我们考虑 GMM 时,我们有几个高斯分量和与每个分量相关的权重因子。类似地,我们可以将可能性改写为:

GMM 的可能性

我们可以再次采用对数似然法:

GMM 的对数可能性

看起来更难解决。如果我们把对第 k 个均值的导数设为 0 呢?

将平均导数设置为 0

这就是单高斯方法的极限。这个表达式解析无解!这就是为什么我们需要期望最大化(EM)来克服这种无法解决的表达。这就是我们将在下一节中讨论的内容,下一节将重点讨论用 EM 训练 GMM。

三。用 EM 训练 GMM

1.EM 简介

EM 背后的主要思想如下:

  • 我们知道如何求解单高斯的参数
  • 但是我们不知道如何求解高斯混合
  • 那么如果我们知道每个数据点属于哪个高斯分量呢?
  • 它可能会帮助我们确定每个高斯的参数!
  • 如果我们首先猜测哪个点属于哪个分量,我们可以迭代地改进这个猜测,并使这个猜测收敛到最优解。

视觉上,我们首先假设我们知道每个观察值 Xi 属于哪个分量 Zi = k:

潜在变量图解

当我们假设我们知道每个观察值属于哪个分量时,我们说有一个潜变量 Z.

  • 现在说 X 是不完全数据,而完全数据是:(X,Z)
  • 节理密度为:P(X,Z |θ)= P(Z | X,θ)P(X |θ)
  • 现在说可能性 L(θ| X)是不完全的
  • 完全可能性现在变成:L(θ| X,Z) = P(X,Z |θ)

EM 算法由几个步骤组成:

  • 我们随机初始化组件的参数,或者通过 k-Means 找到哪些值
  • 期望步骤,在该步骤中,给定 X 和θ,我们估计 Z 的分布,表示为γ
  • 最大化步骤,其中我们最大化 Z 和 X 的联合分布,以导出参数θ的最优值
  • 我们迭代 E 步和 M 步,直到满足收敛标准。

在最后一步,我们已经获得了 GMM 的最佳参数(事实上,收敛性也取决于初始化)。

2.电子步骤

在 E 步骤中,如上所述,我们需要估计给定观测值 Xi 属于给定分量 Zk 的概率。这实际上就是我们所说的伪后验,表示为:

伪后验估计

从视觉上看,它可以简单地表示为估计下列量:

估计伪后验概率

然后将该值插入到所谓的辅助功能中。辅助功能定义为:

辅助功能

其中,θt 是旧参数值,θ是新参数值。

这个辅助函数的表达式可能看起来很奇怪,但它实际上可以被证明是我们通过更新参数值获得的可能性增益的下限。不感兴趣的话可以不看这个,但这是一种说明参数更新的似然增益 L(θ)-L(θt)有辅助函数作为下界的方式。

回想一下,我们不使用任何其他方法,因为它在分析上是不可解的。好吧,但是我们在哪里插入伪后验概率的估计呢?让我们扩展一下这个辅助功能:

具有伪后验估计的辅助函数

这是我们插入这个值的地方。这就是 E 步的结尾。我们现在可以跳到 M 步骤,在估计辅助函数值后,我们将其最大化以确定参数的最佳值!

3.M 步

在最大步长(M-step)中,我们最大化 Q 值以找到最佳参数值:

m 步

而且这个表达式可以解析求解!我们可以将权重、均值和协方差的导数设置为 0,并确定最佳值:

将导数设置为 0 以确定最佳参数值

好吧,在那之前有一点数学,但如果你还在阅读,这是它变得更直观的地方。平均参数的最佳值为:

由 M 步定义的最佳平均参数值

平均值定义为数据的加权平均值,其权重显示了每个观测值属于 GMM 某个分量的可能性。视觉上,你可以这样表示:

属于每个组件的概率加权平均值

直观上,越靠近第一分量的点越有可能属于这个第一分量。但不代表远离这个分量的点没有概率属于它。因此,我们计算这个加权平均值。协方差的值可以以类似的方式看到,并表示为:

协方差更新值

最后,权重是所有点属于第一聚类的概率的总和除以点的总数:

重量更新

4.反复的过程

我们使用这些新的参数值,并将它们再次注入到 E-step 中:

再次 e 步

参数值影响辅助函数的方式是通过伪后验估计:

参数更新

我们计算辅助函数,并在 M 步中再次最大化它。整个 EM 算法可以示意性地表示如下:

EM 迭代过程

我们可以这样描绘 GMM 的视觉训练周期:

迭代 EM 训练周期

同样,您可以在 Github 资源库中找到使用 Plotly 的动画代码。

EM 保证增加迭代次数的可能性:

EM 可能性增加

5.电磁极限

说吧,EM 不猥琐,但是很好看。但是,它有几个限制,我想谈谈:

  • 这取决于我们在启动 E-step 之前进行的初始化
  • 它只收敛到局部最优
  • 运行两次不会得到相同的结果
  • 高度相关的特征可能会阻止 EM 收敛
  • 因为在我们的推导中,辅助函数依赖于 Jensen 不等式,它假设凸函数,因此 EM 不适用于所有的基础分布(例如,也适用于多项式)
  • 我们需要确定 GMM 中组件的正确数量,这并不总是微不足道的

让我们回到最后这一点。为了选择正确的元件数量,我们必须定义一个优化标准,在元件数量上迭代,看看哪个优化了标准。有两个标准可以做到这一点:

  • 艾卡克信息标准(AIC)
  • 贝叶斯信息准则(BIC)

我不会涉及这方面的细节,但简单地记住,BIC 往往比 AIC 在模型复杂性(即组件数量)方面惩罚更多。这种方法通常需要很大的计算能力,但是您最终会得到这样的结果:

AIC 和 BIC 准则

AIC 和 BIC 需要最小化。在上面的示例中,5 是 GMM 组件的最佳数量。

四。硬/维特比 EM

我还想提一个特例。这是硬 EM 或维特比 EM 的情况。这与我们之前看到的有一个主要区别,顺便说一下,这被称为全 EM 或软 EM。

在艰难的 EM 中,我们做出艰难的选择。我们不考虑所有可能的 Z 的概率权重,而是简单地选择最有可能的 Z,然后继续前进。

维特比 EM

我们使用硬/维特比 EM 有几个原因:

  • 硬 EM 更容易实现
  • 但是它没有考虑 Z 的多种可能性,如果我们对 Z 的了解有限,这就成了一个问题
  • k-Means 实际上是硬 EM 的一个特例,有一个恒等协方差矩阵

我们在上面看到的表达式中唯一会改变的是,我们必须用一个最大值代替所有分量的总和。在这篇文章中,我们已经看到了几种表达的意思。比较它们会很有趣,可以看到它们都被这个伪后验定义联系在一起:

平均值更新公式

动词 (verb 的缩写)EM 在 GMM 的应用

我认为以 GMM 在哪里被使用的实际概述来结束这篇(长)文章会很好。这只是一个概述,我并不认为我对 GMM 的使用有多广泛有所了解。

1.演讲中的 GMM

GMM 广泛用于语音中,例如性别检测,其中每种性别的一个 GMM 可以被拟合在 MFCCs 上,并且我们将样本归属于具有最高可能性的 GMM。

这是语音处理的众多应用之一。

2.计算机视觉中的 GMMs

GMM 也用于计算机视觉中的背景减法,例如,其中背景是给定的聚类,而要保留的运动物体是另一个聚类。

将 k-Means 视为 GMMs 的特例,k-Means 用于矢量量化。例如,这是一种用于图像的压缩方法,它防止存储每个像素的值,而只是存储聚类和由 k-Means 识别的值。更具体地说,k-Means 是可以用来执行 VQ 的方法之一。

矢量量化

3.使聚集

更广泛地说,GMM 用于聚类,并且非常有效。GMM 满足密度属性的通用近似(参见https://math . stack exchange . com/questions/3122532/Gaussian-mixture-model-what-a-universal-approximator-of-densities)。

结论

在这篇文章中,我们看到:

  • 什么是 GMM,它们与 k-Means 相比如何
  • 训练 GMM 意味着什么
  • 为什么直接解决它在分析上是不可行的,而我们需要它们
  • EM 是什么
  • EM 的性质和限制
  • 什么是硬/维特比 EM
  • GMM 的应用

我希望这篇文章对你有用。请在您的反馈中留下评论,说明哪些内容是清楚的,哪些需要更好的解释/说明。我花了一些时间将所有这些放在一起,我想参考一些我结合的优秀参考资料。

这篇文章以幻灯片的形式发表在我的个人博客上(有近 200 篇其他文章),就在这里:【https://maelfabien.github.io/machinelearning/GMM/#

参考

期望和差异关系

原文:https://towardsdatascience.com/expectations-variance-relations-4d9fe224f8a0?source=collection_archive---------80-----------------------

基本性质及其证明

这篇文章将会简短而甜蜜。当涉及到一般的技术知识时,我非常支持对一个人正在使用的方法有坚实的基础和扎实的理解。一般来说,我不喜欢记忆任何东西,并尽可能避免这样做。相反,我专注于发展概念的坚实基础,然后我可以用数学方法推导出我可能需要的任何东西。

从数学上理解期望、方差、它们之间的关系,以及如何对它们进行分析操作,是任何人学习概率论和/或数理统计基础知识的第一步。对 Medium 的快速搜索显示,这些证据和关系(据我所知)还没有被简单地组织在一篇文章中。这些是绝对值得了解的概念。

所以让我们开始吧:

定义:

请注意:

  1. 在下面的几个证明中,我提供了离散情况下的推导。连续情况的证明如下。
  2. 大写的变量是随机变量,小写的变量是常量。

关系和证据:

让我们深入研究一下关系和证明!

应用示例:

让我们看一个非常简单的应用示例:

最后的想法

希望以上有见地。正如我在以前的一些文章中提到的,我认为没有足够的人花时间去做这些类型的练习。对我来说,这种基于理论的洞察力让我在实践中更容易使用方法。我个人的目标是鼓励该领域的其他人采取类似的方法。我打算在未来写一些基础作品,所以请随时在【LinkedIn】上与我联系,并在 Medium 上关注我的更新!

随机变量的期望值—简单解释

原文:https://towardsdatascience.com/expected-value-of-random-variables-explained-simply-a0b02eebd9af?source=collection_archive---------3-----------------------

并通过例子进行了说明

随机变量的期望值是该变量所有可能值的加权平均值。这里的权重是指随机变量取特定值的概率。

胡萝卜长度的期望值是多少?这里的随机变量是胡萝卜的长度。在这篇文章中,我将解释回答这个问题的方法。

查尔斯·德鲁维奥在 Unsplash 上拍摄的照片

在详细讲述之前,我们应该区分一下离散型连续型随机变量。

  • 离散随机变量取有限多个或可数无穷多个值。一年中的下雨天数是一个离散的随机变量。
  • 连续随机变量取不可数的无穷多个值。例如,从你家到办公室的时间是一个连续的随机变量。取决于您如何测量它(分钟、秒、纳秒等等),它需要无数个值。

离散随机变量的期望值

让我们从一个非常简单的离散随机变量 X 开始,它只取值 1 和 2,概率分别为 0.4 和 0.6。

注意:概率总和必须为 1,因为我们考虑了这个随机变量可以取的所有值。

这个随机变量的期望值,用 E[X]表示,

如果 1 和 2 的概率相同,那么期望值就是 1.5。离散随机变量的期望值公式为:

你可能认为这个变量只取值 1 和 2,期望值怎么可能是别的值呢?考虑更广的范围。假设我们从这个随机变量中选择 10 个值。总期望值将是 16 (6 乘以 2,4 乘以 1)。

让我们做一个稍微复杂一点的例子。假设你参加了一个有 4 道选择题的测试。每道题 10 分,有 4 个选项。

你甚至没有看问题就随意选择了一个选项。你在这次测试中所得到的分数的期望值是多少?这个问题不需要任何复杂的计算就可以回答。由于有 4 个选项,所以选择正确答案的概率为 0.25。共有 4 个问题,因此您可能会正确回答 1 个问题(1 x 0.25),这相当于 10 分。

让我们也用公式求出期望值,看看是否得到同样的结果。我们可以正确回答 0、1、2、3 或 4 个问题。因此,我们有一个取值为 0、10、20、30 和 40 的离散随机变量。我计算了下面每种情况的概率,并写下了每种情况的得分。

作者图片

作者图片

期望值是通过将点(xi)和得到该点的概率(p(xi))相乘并将它们相加来计算的。如果你真的继续计算,你会看到结果是 10。

连续随机变量的期望值

连续随机变量的期望值是用相同的逻辑但不同的方法计算的。由于连续随机变量可以取不可数的无穷多个值,我们不能谈论一个变量取一个特定值。我们更关注价值范围。

为了计算值范围的概率,使用概率密度函数(PDF)。PDF 是指定随机变量在特定范围内取值的概率的函数。

这里是均匀分布在 5 到 10 之间的连续随机变量的 PDF。x 轴包含所有可能的值,y 轴显示值的概率。

5 到 10 之间均匀分布的连续随机变量(图片由作者提供)

由于变量具有均匀分布,因此所有值的概率都是相同的。整个 PDF 下的面积必须等于 1。对于上面的 PDF,面积是 0.2 x(10–5),等于 1。不是每个 PDF 都是直线。一般来说,面积是通过对 PDF 进行积分来计算的。

这个随机变量的期望值是 7.5,这在图上很容易看到。然而,最好学习公式,因为不是每个 PDF 都像上面的那样简单。

连续变量期望值的公式为:

根据该公式,期望值计算如下。

作者图片

让我们做一个稍微复杂一点的例子。考虑连续随机变量 x 的如下 PDF。

作者图片

我们将尝试从不同的角度接近期望值。变量取值为 0 的概率为 0。概率随着值的增加而不断增加,最终在值 8 时达到最高概率。

如果这是一个均匀随机变量,期望值将是 4。由于概率随着值的增加而增加,因此期望值将高于 4。

如果你把这个 PDF 想象成一个三角形的均匀金属片或者其他任何材料,期望值就是质心的 x 坐标。

这条线代表的 PDF 函数是 f(x) = 0.03125x。如果你计算一下,预期值是 5.33。

作者图片

期望值是统计学和概率论中一个简单却非常基本的概念。为了固化你的理解,我建议自己做几个例子。那你就可以走了!

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

所有图片均由作者创作,除非另有说明。

免费体验 Google autoML 表格

原文:https://towardsdatascience.com/experience-google-automl-tables-for-free-d5648ae3d0e5?source=collection_archive---------38-----------------------

autoML 工具用户体验回顾系列之二

图片来自 PixabayPavlofox

欢迎阅读我的 autoML 工具用户体验评论系列的第二篇文章。我的目标是比较几种 autoML 工具对关键信息的使用和访问。今天,我主要关注 Google autoML 工具中的一个,叫做 Tables。autoML Tables 旨在处理半结构化表格数据(典型的。csv 文件)。谷歌宣布于 2019 年 4 月 10 日发布 autoML 表格的测试版。仍处于测试阶段,功能仍在推出,我想看看谷歌的产品。

为什么使用 Google autoML 表格?

我读过一些关于谷歌机器学习体验的好东西。我以前使用过谷歌云进行一些咨询工作,发现体验非常简单。我很期待看到他们的机器学习服务。我选择表格是因为它与我在本系列中使用的 Kaggle 文件类型相匹配。

正如我之前提到的,Tables 处于测试模式。因此,我查看了发布和问题页面,以确保没有任何障碍。没有屏蔽程序,尽管问题页面上的这个通知让我忍俊不禁:

【Microsoft Edge 和 Microsoft Internet Explorer 浏览器的用户体验可能不是最佳的。”

设置

与 AWS AutoPilot 相比,为 autoML 表做准备的设置更加手动。开始之前的文档非常好,但是似乎在你开始每个项目之前都需要。

  • 创建新项目
  • 确保您的账单已启用
  • 在 Google 云平台中注册您的云存储、Cloud AutoML API、Google 云存储 JSON API、BigQuery API 应用程序
  • 安装 gcloud 命令行工具
  • 安装谷歌云 SDK
  • 创建服务帐户
  • 设置环境变量
  • 更新 IAM 角色

不要担心,一旦你完成了先决条件,体验会大大改善。

数据

至于 AWS SageMaker 自动驾驶仪,我用的是 Kaggle 竞赛数据集。

矛盾,我亲爱的华生。使用 TPUs 检测多语言文本中的矛盾和蕴涵。在这个入门竞赛中,我们将句子对(由一个前提和一个假设组成)分为三类——蕴涵、矛盾或中性。

6 列 x 13k+行—斯坦福 NLP 文档

  • 身份证明(identification)
  • 前提
  • 假设
  • 朗 abv
  • 语言
  • 标签

模型培训成本

模特培训费用每小时 19.32 美元。由于您不需要任何许可证,并且只需为培训计算资源付费,因此这是非常合理的。对于你的第一个模型,有一个免费试用。你可以得到 6 个免费的节点小时用于训练和批量预测。这个免费试用不包括部署模型,然后在线调用它们。六个小时足够试一试了。我的例子用了 1.2 小时。

开始培训

导航到谷歌人工智能。在那里你会发现一个菜单项“用 autoML 训练”

作者的菜单截图

单击表的尝试自动下拉列表。

作者下拉截图

加载数据

导入训练数据集很简单。也可以在此屏幕中直接创建输出数据集的存储桶。

作者数据导入截图

数据导入后,您可以访问一些基本的数据分析。

作者截图

细节有点粗糙。馅饼和油炸圈饼是用来吃的,不是用来吃的。

甜甜圈是用来吃的作者截图

训练您的模型

为了培训预算,我投入了 6 个小时的空闲时间。在这个屏幕上,您可以访问和调整的其他参数并不多。

作者训练截图

等待魔法

虽然 UI 确实提供了一些关于基础设施启动和崩溃状态的更新,但没有多少迹象表明您已经进行了多长时间。我等了大约半个小时,然后决定相信我的预算设置。我做了其他事情,直到我被通知完成。电子邮件是一个很好的功能。

基础设施建设截图由作者提供

等待中…..作者截图

评估培训结果

电子邮件到达后,“评估”选项卡即被填充。有一个图表显示了一些标准指标,如 AUC、精度、召回和日志丢失。我喜欢混乱矩阵是可用的。

作者训练成绩截图

除了它是一个多类分类器之外,我找不到关于模型本身的任何细节。为了了解更多信息,我导出了模型。模型的细节是什么?超参数有哪些?我喜欢的信息远远不够。

得分

在批处理中运行时,输出必须转到 BigQuery。

作者打分截图

这是用户体验的终点。输出文件加载到 BigQuery。我喜欢数据可用于查询或直接在 Data Studio 可视化中使用。我更喜欢在 autoML 控制台中随时可用的一些分析。

结论

我非常喜欢所提供的文档和技巧。它们很容易理解。电子邮件更新也很棒。

体验的不足之处在于识别最终模型本身的细节的容易程度。你最多可以下载一个 Tensorflow 模型包,在 Google Cloud 之外进行评估。

视觉效果令人失望。几乎没有分布图比一些不能用的饼图要好。由于他们仍处于测试阶段,我希望他们正在收集用户对他们需要什么功能的反馈。

总的来说,缺乏透明度可能会阻止我定期使用这个工具。不过,这是测试版…请稍后再来查看!

体验细分:通过客户旅程地图和人工智能增强将洞察力转化为行动

原文:https://towardsdatascience.com/experience-segmentation-turning-insight-into-action-with-customer-journey-maps-and-ai-augmentation-8be2633e21f4?source=collection_archive---------39-----------------------

我们如何使用 CX 方法和数据科学为我们的客户设计和实施移情和数据驱动系统。

我坚信,要确保我的团队做出的每一个决定或产出都是通过为我们的客户创造切实的、可衡量的结果来管理的。事实上,很多时候,当我与当前或潜在的客户交谈时,他们强调咨询或概念工作的定位对他们来说是多么重要,这样有形的执行自然就会到来。

在最近的一次会议上,我们讨论了创建清晰的客户旅程图(、【CJM】、)的战略意义,与会的高管感叹道,CX 的咨询工作虽然在产生见解和创造焦点方面很有价值,但“却最终落在了我办公桌底部的抽屉里。这凸显了我的高管级客户在实施他们购买的任何 CX 咨询服务时所遇到的挫折。

诚然,许多 CX 调查工作的结果可能会产生建议或想法,这些建议或想法需要组织采取高度变革性的行动(即高度努力),而这些行动在短期或中期内根本无法得到支持。因此,最近一个致力于改善客户体验的团队慢慢地将他们的注意力和努力转移到了其他地方,结果是一事无成。

这些调查工作的核心产品通常是闪亮且设计精美的 CJM,它能清楚地识别您客户的需求、动机、痛点以及与这些东西相关的所有接触点。这项活动本身就非常有价值,因为它迫使组织对他们的客户体验采取同理心的方法,因为他们被迫从客户的角度创造 CJM。此外,它允许组织了解传统上孤立的部门如何影响这种客户体验,并创造机会来协调它们并实现价值最大化。最后,也许是在测量方面最重要的,它允许组织了解他们的客户的“理想”或未来状态可能是什么。

虽然这些结果在各种方面都很有价值,但它们本身不会推动行动,并且通常会留下一个 CJM,而只是团队聚集在一起并针对客户的痛点和动机进行协调的人工制品。虽然有许多方法可以解决 CJM 壁画艺术降级的问题,但组织可以通过简单地创建一个测量框架并将相当简单的数据分析试点纳入业务,以少量的努力最大限度地提高利用这一核心产出的成功机会。

本文将概述一种这样的分析方法,作为这种操作化的工作示例。

我将向您展示如何通过就关键指标和这些指标的成功达成内部共识、构建您的数据和运行简单的深度学习机制来开始衡量客户旅程图。

关于数据收集的说明

出于本文的目的,我们将假设您有合适的基础设施来度量您的客户交互。有了大量可用的数字工具,这些反馈回路可以无缝集成到您的数字平台中。如果有必要在物理环境或两者结合的环境中收集旅行数据,投资智能设备或流程来促进这一点。

请注意,如果您没有高质量的数据,本文的整个前提就会崩溃。人们可以用市场上强大的工具轻松地收集数据,但这并不意味着流程和策略很容易设计。如果没有强大的数据检索方法,卫生和数量,这篇文章中的思想在聚类分析方法方面将变得毫无用处。但是,可以使用其他更简单的方法来确保您的 CJM 处于运行状态并不断改进。

区分优先顺序并理解您的数据收集

正如我们之前提到的,CJM 活动是一个很好的机会,可以让可能彼此孤立的各个部门的成员团结起来。这样做有很多好处,例如整个企业更大的#协同作用,更大的内部同理心,并有望出现关于客户旅程如何受到公司内部和外部流程影响的强有力的见解。利用这一调整和期望兴奋的时刻,抓住那些可以三倍提升客户体验的时刻。这是一个关键时刻,以确保您的团队在客户类型及其旅程、哪些指标对这些客户普遍重要以及我们如何衡量这些指标的绩效方面保持一致。

我们可以使用多种技术来识别这些航段及其特定旅程,例如:

  • 客户/用户调查;
  • 客户/用户访谈;
  • 用户组/可用性研究;
  • 会话重放;
  • 网络分析;
  • 嵌入式传感器

假设我们作为一个团队走到了一起,我们已经制定了客户细分的类型及其相应的旅程。让我们以一家大型在线零售商为例,展示我们可以经历的流程,以了解我们正在收集的数据类型,以及哪些数据需要与客户细分和相应的旅程相一致。

在我们的示例中,我们将介绍这些细分和旅程:

  1. 浏览器:查看他们喜欢的品牌,以了解即将推出的产品类型。
  2. 橱窗购物者:在他们买得起的东西之外寻找品牌,喜欢购买渴望的东西。
  3. 里里外外:确切地知道自己想要什么,得到它,购买它,抛弃它。
  4. 退货者:已购买,想退货。

在这一点上,我们已经就以下方面达成一致并达成共识:客户的类型及其相应的旅程,这是一种成功。但至关重要的是,我们需要在另外两个方面达成共识。

就指标达成共识

首先:我们可以用来衡量这些旅程的指标。在本例中,我们可以依靠通用诊断工具,该工具将在零售商的旅程中普遍使用,这些指标是:

  1. 客户努力:客户为实现目标付出了多少努力?
  2. 难点:给客户带来最大痛苦或情绪低落的步骤(我们可以使用 NPS 或调查反馈来计算)。
  3. 问题:问题何时以及为什么会出现,我们是否向他们提供了这些信息,客户的问题是否容易得到回答?
  4. 等待时间:我们的客户必须等待多长时间才能完成某些任务,这些任务对客户来说是最长、最令人沮丧的?
  5. 错误:我们最常失败的地方是哪里,我们如何减轻这种失败?

就指标的绩效达成共识

作为一个团队,我们已经就这些指标达成共识,认为它们是我们诊断每个客户旅程的关键,以及它们如何与客户在旅程中的步骤(即他们的体验)相关联。这将引导我们进入第二步,也是更精细的一步,即围绕如何衡量这些指标的绩效形成公司共识。一个简单(注意:不容易)的方法是就团队的某个指标达成一致。

例如,让我们以“客户努力”为例:我们可以基于交易发生的总时间来索引该性能,如下所示:

在 1-5 的基础上(1 为最佳):

  1. 0–60 秒
  2. 61-120 秒
  3. 121-180 秒
  4. 181-240 秒
  5. 241-300 秒

或者对于“问题”分析,我们可以编制索引,例如:

  1. 0 个问题
  2. 1 个问题
  3. 2 个问题
  4. 3 个问题
  5. 4 个问题

我们可以让索引变得更复杂。例如,我们可以使用高级分析来测量不确定时刻和必要澄清之间的差值;然而,这个例子的目的是简单地表达一个指数是一个明确的和统一的测量某一指标的性能。至关重要的是,团队需要在这个指标上达成共识

我们已经创建了适用于我们的客户群及其旅程的统一指标,那又如何?

现在,我们有了一种在统一基础上持续衡量我们的细分和旅程的方法。人们已经这样做了很长时间——听说过客户洞察吗?也就是说,至少我们可以开始使用我们的 CJM,因为我们现在知道客户旅程在当前状态下是什么样子,并且我们可以决定我们希望客户旅程在未来状态下是什么样子。

我们可以寻找立竿见影的效果、重大创新,或者介于两者之间。

最重要的是,我们可以开始跟踪、衡量、调整和使用我们的 CJM 作为焦点,以我们知道团队可以产生有凝聚力和影响力的努力的方式来衡量我们的内部计划。作为一个内部团队,我们在何时何地选择我们的工作可以以一种战略性的方式完成,并且有数据支持我们,我们可以以统一的方式完成这项工作,同时确保我们创建反馈循环,以迭代和维护我们的 cjm 的相关性。

祝贺您,您刚刚向我解释了我的客户洞察团队是做什么的…

好吧,你没有完全错。但是请记住,这个思考练习的重点是把我们的 CJM 变成不仅仅是一个提醒。我们想让它做点什么。

然而,如果你仍然不为所动,为什么我们不推出这个现在的宝库美丽的索引数据,我们可以匹配我们的每个客户群,让我们开始创建体验细分。

没错,我们正在深入每一个可靠的客户旅程,并试图根据他们的实际体验对他们进行细分。

我们需要什么数据来做这件事?我们已经有了——这是我们的 CJM 绩效指数数据。

好吧,但是我怎么才能解决经验分割的问题呢?每个客户和事件都有很大的不同,很难以有意义的方式将它们配对。我们将使用机器的力量,即将成为无所不知的人工智能来争论我们的数据,以一种不可思议的复杂方式,在一个快得可笑的时间内,把它分割成小块,并让它根据我们客户的经验对他们进行聚类

这是一个我们如何使用聚类分析来开始理解和预测经验分割的例子

数据

让我们使用 iris 数据集,但假设我们不是处理物种而是根据他们与公司产品或服务的互动,处理不同的客户体验组。

from sklearn import datasets
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from sklearn.cluster import KMeans
from sklearn import metrics, neighbors, model_selection
import seaborn as sns# data
iris = datasets.load_iris()# convert to data frame
dat = pd.DataFrame(iris.data)

转换

让我们将最初的花卉测量值转换为 4 个可量化指标的 1-5 分(1 dp ),这些指标都与客户对产品/服务的体验有关。

# scale
scaler = MinMaxScaler(feature_range=(1,5))# fit scaler
scaler.fit(dat)# scale and round
X = pd.DataFrame(scaler.transform(dat)).round(1)# these are our metrics
X.columns = [‘Effort’, ‘Questions’, ‘Wait Times’, ‘Errors’]# view
X.head()

下面是结果:我们有统一的标准来衡量基于上述活动的结果!

从上表中我们可以看出,客户我们的大多数客户在所有指标上都有良好的体验,除了一些问题,这些问题需要全面解决。

探索性数据分析

我们可以运行一些简单的(如果你像我一样是个黑客的话,很容易)数据——即看看是否有任何分数的自然聚类。

# viz
sns.pairplot(X)

查看数据,我们可以看到两种清晰的客户“体验”,简单来说就是“好”和“差”

聚类分析

从这些数据中,能识别出这些截然不同的经历吗?

让我们开始吧。运行流行的“k-means”模型的无监督学习模块。

通过快速浏览上面的图,我们可以看到有两个不同的组,所以我们可以告诉我们的计算机寻找 2 个集群(' _clusters=2 ')。

然后让我们来区分这是一个好的还是一个坏的体验(集群 1 v 集群 2)。

# k-means algo, lets look for 2 experiences
kmeans = KMeans(n_clusters=2, random_state=0).fit(X)# save the result
result = X.copy()# label the clusters we found as experiences
result.loc[:,'experiences'] = (kmeans.labels_) + 1# view the result
result

以下是我们的结果样本:

我们可以看到,聚类分析很好地识别和分离了我们的聚类。从一个快速的 squiz 中,我可以看出 2 是#不好的体验,1 是#好的体验(提示:看看指标)。

让我们想象一下!

如你所见,有明显的群体。机器在数据中发现了“复杂”的模式,由于我们上面的数据工程,这些模式代表了好的体验或不好的体验。

这意味着它实际上可以开始预测客户的体验是好是坏。

“经验分割”一词由此而来。

别拐弯抹角了,让我们做些模型吧!

现在我们已经确定了我们的'体验细分市场,我们可以对它们训练一个#机器学习模型,这样我们就可以根据新客户与我们的产品/服务的互动对他们进行分类(最好是在他们使用产品时实时分类!!).

关键的想法是,如果我们能做到这一点,我们可能能够实时采取行动,防止客户有“坏”的体验,同时让那些将有“好”体验的客户离开。

目标和功能

让我们设置功能(我们的体验表)和我们的目标是什么(1)。

# target
y = result.experiences# features
X = result.drop("experiences", axis=1)

训练/测试分割

现在它只是你的标准 TTS 工作通过。我们只需要训练我们的模型来预测什么是好的,什么是坏的,然后根据我们的数据集进行测试。

# creating training / testing datasets
X_train, X_test, y_train, y_test = model_selection.train_test_split(X, y, test_size = 0.25, random_state = 42)

先训练模型。让我们再次找到我们的聚类分析模型,然后根据我们上面制作的两个数据集进行拟合。

# specifying the classifier
clr = neighbors.KNeighborsClassifier(n_neighbors = 3, weights = 'uniform')# fit the classifier
clr.fit(X_train, y_train)

现在我们来测试一下。

# predicted
y_pred = clr.predict(X_test)# accuracy
metrics.accuracy_score(y_pred, y_test)

我们可以通过让我们的集群用 be 预测 X 的结果(对“X_test”)来确定分数的准确性,然后通过比较“y_pred”和“y_test”的性能来实际计算准确性

这意味着,我们现在有了一个模型,可以分析我们的数据并预测客户的体验。

如果我们可以在生产中部署并自动化这一点,我们就可以触发功能来增加良好体验或挽救糟糕体验的可能性。

(注意:你可以有更丰富的数据来帮助你更进一步,但是为了简单起见,我们使用了上面的数据集)。

好吧,酷——那又怎样?

上述(简化的)执行使我们能够在数据中找到适用于不同客户群的抽象和复杂模式实时旅程,,我们可以将其描述为体验细分。我们已经创建了一个模型,可以找到这些模式并将“相似”的体验组合在一起,以描述我们通常忽略的一些事情——客户(在特定细分市场中)在特定时间实际经历的事情。设计师会称之为移情,营销人员会称之为个性化。我称之为机会。

例如,假设我们是一家在线零售商。

想象一下,我们已经对【浏览器】的行为进行了建模——某人上网,查看他们最喜欢的品牌,拥有高度的消费者知识,但最终对他们在网站上的活动量的转化率相对较低。我们可以假设客户的“等待时间”和情感体验(“痛点”)保持相当稳定。毕竟,我们可以假设他们发现他们悠闲的浏览是一种宣泄,而不是过度紧张。

他们喜欢花时间去了解他们最喜欢的品牌或某些衣服,并自己做非正式的研究,以构建他们想要的那件衣服。他们在脑海中构建“完美”的物品,并确切地知道他们在追求什么——愿意花费大量的高价物品。

作为一家公司,我们一直在监控这类行为。我们了解客户群。我们从宏观层面监控他们的行为,并能轻松挖掘出某一类客户在个人层面的行为(即个性化)。在这里,他们想买一件剪裁合适、颜色合适的羊绒混纺冬季大衣。我们知道,作为客户 细分市场,他们是特定的长尾买家,有很多购买想法,但不容易转变。然而,当他们改变信仰时,他们会购买高价商品。获得这类顾客的注意力并不需要花费太多成本——他们是知道自己喜欢什么的老练买家——但是不管最优 UX 如何,都很难突破购买的心理障碍。

我们的聚类分析是实时运行的,具有内置的自动化功能,一直在测试和训练这个特定的细分市场。我们的模型知道它是一个'浏览器',它也知道指标的分组,在正确的时机将导致好的或坏的体验。实时地,集群可以设法将客户体验保持在“好区域”(转换),而远离“坏区域”(关闭以便改天浏览)。购买的利润很薄,但作为一家企业,在我们的 CJM 和体验细分研究和开发的推动下,我们推出了一个聊天机器人,提醒我们的客户我们的退货政策和相应的视频,显示我们的“浏览器”如此钦佩的适合和平衡。

我们在 UX 的努力与体验直接对应,不张扬也不咄咄逼人,但清晰而直接;因此,消除购买“浏览器”的障碍是常有的事。浏览器被迫完成购买,因为我们的模型已经管理并触发了关键功能或特性,这极大地提高了正确的指标。浏览器转换了,这件外套是我们以 CJM 为骨干精心管理客户体验细分的结果,并以一种允许我们执行强大的机器学习应用程序来管理客户的体验的方式操作它。

这种以数据为导向的 CX 方法(或任何其他设计导向过程)允许我们以一种可以有效监控、测量和增强的方式实施初始设计分析。允许内部团队或客户做出决策,验证业务案例,并(有希望地)进一步推动业务案例,以实现真正的创新。最重要的是,它让我们能够做出决策——在这个例子中是在实时——积极确保我们的客户拥有良好的体验,或者在每次与我们互动时避免糟糕的体验。

这篇文章的核心要点是:

  1. 我们可以使用 cjm 来创建严格的框架,以实际监控我们当前和未来的客户状态/旅程。
  2. 我们可以使用数据科学来了解细分市场之外的客户,并允许我们基于实际的体验来监控他们。
  3. 我们可以利用经验细分来产生深刻的移情和数据驱动的见解,这些见解实际上整合到我们的反馈循环中,为我们的决策以及我们与客户的互动方式提供信息。

体验遗传算法的威力

原文:https://towardsdatascience.com/experience-the-power-of-the-genetic-algorithm-4030adf0383f?source=collection_archive---------34-----------------------

基于遗传算法的顶点覆盖

PixaBay 上的图片

遗传算法是一种基于遗传学自然选择概念的进化计算技术。它主要用于在确定性多项式解决方案不可行的情况下,为许多优化和更棘手的问题找到一个接近最优的解决方案。

遗传算法也可以用于搜索空间,以找到正确的特征和模型参数来预测目标变量。遗传算法的另一个重要优点是除了标准优化问题之外,当目标函数是不可微的、不连续的、随机的或非线性的时,它们也是有用的。

我将以关于遗传算法顶点覆盖问题的简介开始这篇博文。然后我将展示我们如何使用遗传算法来获得顶点覆盖问题的近似最优解,这远优于近似算法。通过这个博客,我也想让读者体验遗传算法的力量。该解决方案的完整源代码可以在这里找到。

遗传算法

遗传算法有 5 个阶段:

  1. 初始化:初始解(第一代)随机初始化
  2. 健康评分:这里的主要思想是,可能很难找到最佳解决方案,但是一旦我们有了一些解决方案,很容易给它附加一个良好或健康评分。
  3. 选择:种群中最适合的成员(解)存活下来,并继续进入下一代。
  4. 交叉:群体中最适合的成员(解)成对组合,产生新的成员(可能的解)。
  5. 突变:新成员(可能的新方案)少量改变自己,变得更好。

一旦初始种群生成,选择、交叉和变异的循环将持续许多代,直到算法收敛或达到最大迭代。这些概念在这个博客里解释得很清楚。

[## 遗传算法简介—包括示例代码

遗传算法是一种受查尔斯·达尔文的自然进化理论启发的搜索启发式算法。这个…

towardsdatascience.com](/introduction-to-genetic-algorithms-including-example-code-e396e98d8bf3)

顶点覆盖问题

顶点覆盖中顶点的子集,使得对于图的每个边(u,v ),或者‘u’或者‘v’在顶点覆盖中。这是一个典型的 NP-hard 优化问题,有一个近似算法,但没有确定的多项式解。

图片来源维基百科

图-1 的顶点覆盖为 3,与 3-red 节点一样,所有的边都被覆盖,因此对于图的每条边(u,v ),或者“u”或者“v”都在顶点覆盖中。类似地,图-2 的顶点覆盖为 4。

图片来源维基百科

图-3 的顶点覆盖为 2,图-4 的顶点覆盖为 3。

顶点覆盖是一个典型的 NP-Hard 问题,没有确定的多项式时间解。我们有一个可用的近似算法,它在多项式时间内运行并给出一个次优解。近似算法的细节可以在下面的链接中找到。

[## 顶点覆盖问题|集合 1(简介和近似算法)- GeeksforGeeks

无向图的顶点覆盖是其顶点的子集,使得对于图的每条边(u,v ),或者…

www.geeksforgeeks.org](https://www.geeksforgeeks.org/vertex-cover-problem-set-1-introduction-approximate-algorithm-2/)

还有证据表明,近似解永远不会超过最优解 2 倍。详情可以在这里找到。

[## 顶点覆盖

定义:无向图 G=( V,E)的顶点覆盖是 V 的子集,使得如果边(u,V)是…

www.personal.kent.edu](http://www.personal.kent.edu/~rmuhamma/Algorithms/MyAlgorithms/AproxAlgor/vertexCover.htm)

生成图形数据

我生成了一个 250 节点的图表。用于生成该图的主要思想是在[0,1]范围内绘制随机数,并且仅当概率小于阈值时,才在节点“u”和“v”之间创建边。

#Graph Library
import networkx as nxedge_probability = .0085
adjacency_matrix = np.zeros((nodes,nodes),dtype = np.int)
edges = []
edges_cnt = 0
for i in range(nodes):
    for j in range(i):
        prob = random.random()
        if prob < edge_probability:
            adjacency_matrix[i,j] = 1
            edges.append((i,j))
            edges_cnt += 1G=nx.Graph()
G.add_nodes_from(list(range(0,nodes)))
G.add_edges_from(edges)
nx.draw(G,node_color='r', node_size=18, alpha=0.8)

生成了具有 250 个节点的图“G”

这个图有 256 条边。

求图“G”顶点覆盖的近似算法

顶点覆盖的近似算法是一种贪婪算法,它可能不会给出最优解。近似算法的工作可以解释为:

1) Initialize the solution-set as  {}
2) Loop through all the E (Edges).
3) For an arbitrary edge (u, v) from set of E (Edges).
   a) Add 'u' and 'v'  to solution-set if none of the vertices 'u' or 'v' present in the set already
   b) If any of the vertex 'u' or 'v' present already in the solution-set, skip the edge and move to the next one.
3) Return solution-set.

该解决方案的更多细节可以在这个博客中找到。

[## 顶点覆盖问题|集合 1(简介和近似算法)- GeeksforGeeks

无向图的顶点覆盖是其顶点的子集,使得对于图的每条边(u,v ),或者…

www.geeksforgeeks.org](https://www.geeksforgeeks.org/vertex-cover-problem-set-1-introduction-approximate-algorithm-2/)

在具有 250 个节点和 256 条边的图“G”上运行近似算法,我们得到 178 个节点的顶点覆盖。

#Vertex Cover Greedy Algorithm
visited = np.zeros(nodes)
cnt = 0
for e in edges:
    (u,v) = e
#     print(u,v)
    if ((visited[u]==0) & (visited[v]==0)):
        visited[u] = 1
        visited[v] = 1
        cnt+=2
print("Vertex cover consists of {} nodes".format(cnt))

输出:顶点覆盖由 178 个节点组成。

用遗传算法寻找图 G 的顶点覆盖

图片由肯尼罗Unsplash 上拍摄

解决方法

使用遗传算法可能有相当多的解决顶点覆盖的方法。

一种方法是将适应度分数定义为覆盖边和所用顶点数量的函数。当一个解覆盖了所有的边时,罚分是 0,当一些边丢失时,罚分被包括。此外,实现这一目标所需的节点数量也会带来成本。这将需要仔细定义健身核心。

我不想惹上麻烦,因为适应值是覆盖边和顶点数的函数。我希望它是其中一个的功能。因此,我固定了顶点的数量,并将适应度分数定义为仅覆盖边的函数。这是如何做到的:

1\. Is it possible to cover all the edges with exactly 'k' number of vertices. Genetic Algorithm tries to find such solution, if possible.2\. Binary Search for the minimum value for 'k'.

初始化

我们需要发现,如果使用一些“k”个顶点,是否有可能覆盖所有的边。因此,我们用一些“k”个顶点随机初始化种群,作为顶点覆盖的一部分。

n = 250
#----------Hyperparameters can be tuned--------------------
pop_total = 1500   # max population allowed in the environment
pop_init = 1000    # Initial Population
max_iterate = 500  # Max Iterations or Generations allowed
#----------------------------------------------------------def chromosomes_gen(n,k,pop_init):
    lst = []
    for i in range(pop_init):
        chromosome = np.zeros(n,dtype= int)
        samples = random.sample(range(0,n), k = k)
        for j in range(k):
            chromosome[samples[j]] = 1
        lst.append(chromosome)
    return lst

健身得分

在这里,我通过使用精确的“k”个顶点的给定解决方案来检查,有多少条边丢失了。我为它增加一个惩罚/障碍计数。

def cost(soln,n,edges):
    obstacles = 0
    for e in edges:
        (u,v) = e
        if ((soln[u]==0) & (soln[v]==0)):
            obstacles += 1
    return obstacles

选择

有各种可能的选择方法。在这个问题中,我只是简单地将得分最高的解决方案传递给下一代,并杀死其余的。变量‘pop _ total’定义了允许传递给下一代的最大群体。

def selection(lst,pop_total,n,edges):
    score = []
    output_lst = []
    len_lst = len(lst)
    for i in range(len_lst):
        score.append(cost(lst[i],n,edges))
    sorted_index = np.argsort(score)
    cnt = 0
    for i in range(len_lst):
        output_lst.append(lst[sorted_index[i]])
        if((i+1) == pop_total):
            break
    lst = output_lst 
    # Return truncated-population and best score of this Generation
    return lst,score[sorted_index[0]]

交叉

在交叉中,我们将两种解决方案结合起来,希望得到更好的解决方案。在这个问题中,我采用两个解决方案,找到第一个解决方案中不存在于第二个解决方案中的顶点,类似地,采用第二个解决方案中不存在于第一个解决方案中的顶点,并交换其中的 50%。

#Crossover
cross_over_prob = 0.50
for i in range(len_lst):

      # First solution
      tmp = lst[i].copy()
      # Second solution
      mate_with = lst[int(random.uniform(0,len_lst))]

      tmp_unique = []
      mate_with_unique = []

      # Store all vertices of Solution-1 and Solution-2
      for j in range(n):
          if(tmp[j]==1):
              tmp_unique.append(j)
          if(mate_with[j]==1):
              mate_with_unique.append(j)#Filter vertices from Solution-1 which is in Solution-2 and shuffle
      tmp_unique = np.setdiff1d(tmp,mate_with)
      random.shuffle(tmp_unique)#Filter vertices from Solution-2 which is in Solution-1 and shuffle
      mate_with_unique = np.setdiff1d(mate_with,tmp)
      random.shuffle(mate_with_unique)#Swap 50% unique vertices from Solution2 into Solution1 ~ New Soln
      swap = math.ceil(cross_over_prob * min(len(tmp_unique),len(mate_with_unique)))
      for j in range(swap):
          tmp[mate_with_unique[j]] = 1
          tmp[tmp_unique[j]] = 0
      new_solution = tmp

变化

在这一步中,来自交叉状态的新解会稍微改变自己,或者变异,希望变得更好。在这个问题中,我执行了两种类型的突变,改变了 5%的解。我掷硬币,如果正面朝上,我进行变异 1,否则进行变异 2,两者都不同地改变 5%的解。

在变异-1 中,我从解中随机取出 5%的顶点,并用之前不在解中的其他随机 5%的顶点替换它。

在变异-2 中,我从解决方案中随机抽取 5%的顶点,并用其他 5%的顶点替换,这些顶点可以覆盖当前解决方案未能覆盖的一些边。

# Mutation 
mutat_prob = 0.05
zeroes = []
ones = []
for j in range(n):
    if soln[j]==1:
        ones.append(j)
    else:
        zeroes.append(j)random.shuffle(ones)
random.shuffle(zeroes)coin_toss = random.random()
if(coin_toss <= 0.5):
    # Mutation-1
    swaps = min(len(ones),len(zeroes))
    for j in range(swaps):
        coin_toss2 = random.random()
        if(coin_toss2 < mutat_prob):
            soln[ones[j]] = 0
            soln[zeroes[j]] = 1
            #Swapping logic
            dummy = ones[j]
            ones[j] = zeroes[j]
            zeroes[j] = dummy
else:    
    # Mutation-2
    mutate_lst = []
    for e in edges:
        (u,v) = e
        if((soln[u] == 0) & (soln[v] == 0)):
            coin_toss2 = random.random()
            if(coin_toss2 < mutat_prob):
                coin_toss3 = random.random()
                if(coin_toss3 <= 0.5):
                    if(u not in mutate_lst):
                        mutate_lst.append(u)
                else:
                    if(v not in mutate_lst):
                        mutate_lst.append(v) random.shuffle(mutate_lst)
    sz = min(len(ones),len(mutate_lst))

    for j in range(sz):
        soln[ones[j]] = 0
        soln[mutate_lst[j]] = 1
        #Swapping logic
        dummy = ones[j]
        ones[j] = mutate_lst[j]
        mutate_lst[j] = dummy

soln_lst.append(soln)

遗传算法的性能主要取决于交叉和变异函数的构造。这些功能需要仔细设计和调整/修改,以获得最佳性能。

二分搜索法找到最小的 k

我执行了一个二分搜索法来寻找‘k’的最小值,这样‘k’个顶点的顶点覆盖覆盖了所有的图边。

# Environment to perform Initialisation-Selection-Crossover-Mutation
def environ(n,k,mut_prob,pop_init,pop_totl,max_iterate,edges)
    soln = chromosomes_gen(n,k,pop_init)
    for it in range(max_iterate):
        soln = cross_over_mutate(soln,n,k,mut_prob,pop_total,edges)
        soln,cost_value = selection(soln,pop_total,n,edges)
        if cost_value==0:
            break
    return cost_value,result# Binary Search function
def mfind(n,mut_prob,pop_init,pop_total,max_iterate,edges,start,end)
    result_dict = {}
    l = start
    h = end
    ans = 0
    while(l<=h):
        m = int((l+h)/2.0)
        cv,res = environ(n,m,mut_prob,pop_init,pop_total,max_iterate,edges)
        if(cv==0):
            result_dict[m] = res
            h = m-1
        else:
            l = m + 1
    return result_dict# Main-function Call
res = mfind(n,mutat_prob,pop_init,pop_total,max_iterate,edges,1,n)

结果

使用遗传算法,具有 250 个节点和 256 条边的图“G”的顶点覆盖是 104 个节点,这比近似算法的 178 个节点的解决方案小得多且更好。解决方案的完整源代码可以在这里找到。

结论

在这篇博文中,我们理解了遗传算法的概念及其用例和属性。然后我们用遗传算法来解决顶点覆盖问题,得到了一个比近似算法更好的解。

如果你有任何疑问,请联系我。我很有兴趣知道你是否有一些有趣的问题陈述或想法,遗传算法可以用于它。

我的 Youtube 频道更多内容:

[## 阿布舍克·蒙戈利

嗨,伙计们,欢迎来到频道。该频道旨在涵盖各种主题,从机器学习,数据科学…

www.youtube.com](https://www.youtube.com/channel/UCg0PxC9ThQrbD9nM_FU1vWA)

关于作者-:

Abhishek Mungoli 是一位经验丰富的数据科学家,拥有 ML 领域的经验和计算机科学背景,跨越多个领域并具有解决问题的思维方式。擅长各种机器学习和零售业特有的优化问题。热衷于大规模实现机器学习模型,并通过博客、讲座、聚会和论文等方式分享知识。

我的动机总是把最困难的事情简化成最简单的版本。我喜欢解决问题、数据科学、产品开发和扩展解决方案。我喜欢在闲暇时间探索新的地方和健身。关注我的 Linkedininsta gram并查看我的往期帖子。我欢迎反馈和建设性的批评。我的一些博客-********

拔靴带

原文:https://towardsdatascience.com/experimental-design-bootstrapping-5b56fc7a10f1?source=collection_archive---------73-----------------------

因为“观察到你的结果的概率或者一个更极端的假设是真的”是令人困惑的。

介绍

实验是公司决策的基础,p 值是决定选择哪个实验假设的主要方法。

  • 这个新的登录页面布局比现有的登录页面更好吗?
  • 这种新的推荐策略是否优于现有的推荐策略?
  • 我们应该在这个新的渠道上发起新的营销活动吗?

在任何一种情况下,我们都可以通过实验来做出决定。运行实验时,步骤如下:

  1. 确定您感兴趣的指标。
  2. 陈述无效和替代假设。
  3. 收集数据。
  4. 计算置信区间或 p 值。
  5. 使用区间或 p 值做出与假设相关的决策。

当使用 p 值时,利益相关者盲目地将该值与某个阈值(大多数情况下为 0.05)进行比较来做出决策。这种比较是带着一些模糊的认识进行的,当 p 值较小时,我们选择替代假设。然而,即使这种说法也从根本上反对与 p 值计算方式相关的理论。

例子

图 1:示例实验数据

假设我们在第一个场景中,我们想知道两个登录页面中哪一个“更好”。针对该示例的实验步骤概述如下:

  1. 我们希望监控与每个页面相关的平均收入。
  2. 零(H₁)和替代(H₂)假设可以表述为
    H₁:新页面的平均收入小于或等于现有页面。
    H₂:新页面的平均收入高于现有页面。
  3. 我们可能会收集与每个登录页面相关的数据,这些数据包含以下信息。提供这些数据的脚本可以在 Github 链接这里获得,你可以在 图 1 中看到这个部分顶部的数据示例。
  4. 从这些数据中,我们需要计算出假设零假设为真的情况下,观察到的平均收入差异的概率。无效假设是真实的平均差异为零。

在一门介绍性的静力学课程中,你将学习使用某种双样本 t 检验,在这种情况下你需要确定你的组的方差是相等还是不同。然后计算标准误差,您可以用它来确定 p 值。

如果您是 t-test 和 python 中搜索的新手,您可能会遇到这样的问题:您是否会使用相关 t-test、配对 t-test、独立 t-test。那么,你如何选择?你为什么选择一个特定的测试?你怎么知道你选择了正确的测试?

这就是自举发挥作用来拯救世界的地方!但是如果您从所有可能的选择中选择了正确的测试,并且使用了 Scipy 中的内置测试,那么对应的 p 值就是 0.00008。这个解决方案的代码非常简单。困难的是知道你做了正确的选择。

科学 t 检验

5.同样,在统计介绍课程中,您现在可以将 p 值与 0.05 进行比较,并选择版本 1 和版本 2 的平均收入不同的替代方案。

然而,为了充分理解在这种情况下得出的结论,理解 p 值、I 型错误率以及这两者的组合如何推动第 5 部分中的结论是很重要的。

虽然这个解决方案的代码很简单,但是知道选择哪个解决方案以及如何解释结果可能会很混乱,特别是对于那些不经常进行假设检验的人。

拔靴带

为了使最后两步更容易理解,我更喜欢使用 bootstrapping 来模拟均值差异的分布。这与传统的 p 值和置信区间的思想有很好的联系。

自举的定义是有替换的抽样。通过对原始数据集进行替换采样,我们可以多次计算每个引导样本的平均收入差异,以了解平均差异如何从一个引导样本变化到下一个引导样本。

自助抽样——个人来自 freepik.com

我们可以使用下面显示的函数为这个数据集执行引导。在这种情况下,每个引导样本都是一个新的DataFrame,包含原始行的“替换样本”版本,直到我们创建了一个具有相同行数的新的DataFrame

使用 10,000 个引导样本的默认值提供了三个列表,其中每个引导样本都有一个值:

  • 版本 1 和版本 2 之间的均值差异列表。
  • 版本 1 收入的方法列表。
  • 第 2 版收入方式列表。

从引导结果中得出结论

这种从一个样本到另一个样本存在多少差异的想法使我们能够看到我们可以从包含这些值的总体中得到什么类型的样本估计。这些估计值的范围可以帮助我们理解哪些值是可能的,以及这些值是否符合零假设或替代假设。

上述函数的结果可以用下面的代码绘制出来。

这些图看起来是这样的:

绘制的自举结果

从这些图中,我们可以看到,在所有引导样本中,版本 2 的平均收入几乎总是高于版本 1 的平均收入。

此外,第一个图显示了所有 bootstrap 样本的平均差异,以及中间 95%的差异所在的界限(蓝色虚线)。因为这两个界限之间不包含零差,所以 bootstrap 区间提供了均值差不为零的重要证据。

使用来自 Scipy 的 p 值确实使我们得出了与 bootstrap 方法相同的结论。然而,从这些图中得出结论(使用 bootstrap 方法)比使用 p 值的传统方法(在我看来)更直观。

深度学习中超参数调整的实验——遵循的规则

原文:https://towardsdatascience.com/experiments-on-hyperparameter-tuning-in-deep-learning-rules-to-follow-efe6a5bb60af?source=collection_archive---------27-----------------------

鸣谢—https://image . free pik . com/free-photo/gear-cutting-machine _ 137573-2479 . jpg

任何深度学习模型都有一组参数和超参数。参数是模型的权重。这些是在每个反向传播步骤使用优化算法(如梯度下降)更新的。超参数是我们设定的。他们决定模型的结构和学习策略。例如,批量大小、学习速率、权重衰减系数(L2 正则化)、隐藏层的数量和宽度等等。由于深度学习在模型创建中提供的灵活性,人们必须仔细挑选这些超参数以实现最佳性能。

在这篇博客中,我们讨论

1.调整这些超参数时要遵循的一般规则。
2。在数据集上的实验结果验证了这些规则。

实验细节

数据— 实验在以下数据集上进行。它包含 6 类图像——建筑物、森林、冰川、山脉、海洋、街道。每门课大约有 2300 个例子。测试集由每个类的大约 500 个例子组成。训练集和测试集都相当平衡。

资料组

code—
https://github . com/DhruvilKarani/hyperparameter tuning/blob/master/readme . MD

硬件—
NVIDIA 1060 6GB GPU。

使用的型号— 1。学习率实验— ResNet18
2。对于批量大小、内核宽度、重量衰减的实验—自定义架构(见代码)。

观测记录— 1。每个时期的训练和测试损失
2。每个历元的测试精度
3。每个时期的平均时间(测试集上的训练和推断)

超参数及其对模型训练的影响

我们以卷积神经网络(CNN)为例,将模型行为和超参数值联系起来。在这篇文章中,我们将讨论以下内容——学习速度、批量大小、内核宽度、权重衰减系数。但是在我们讨论这些通用规则之前,让我们回顾一下任何学习算法的目标。我们的目标是减少训练误差以及训练误差和测试/验证误差之间的差距。我们通过调整超参数来实现这一点

训练和验证误差通用图。原图。

让我们看看深度学习文献如何描述改变超参数值的预期效果

学习率

深度学习书上说—

如果你有时间只调整一个超参数,调整学习率

在我的实验中,这当然成立。在尝试了三种学习速率后,我发现过低或过高的值都会严重降低算法的性能。对于学习率 10^-5,我们实现了 92%的测试准确性,对于其余两个率,我们几乎没有超过 70%,所有其他超参数保持不变。

在 ResNet 上测试准确性

如果你看看误差图,会发现一些东西。例如在训练错误中—

ResNet 上的训练错误

不出所料,三者都有所下降。但是在最低的学习速率下(粉色),第 10 个时期的损失大于第一个时期的红色曲线的损失。由于学习率极低,你的模型学得非常慢。此外,在高学习速率下,我们期望模型学习得更快。但是如你所见,红色曲线上的最低训练误差仍然大于橙色曲线上的误差(中等学习率)。现在让我们看看测试误差—

ResNet 上的测试错误

一些观察。对于最低比率,测试损失稳步下降,似乎还没有达到最低点。这意味着模型有欠拟合,可能需要更多的训练。

图片鸣谢—https://www . Jeremy Jordan . me/content/images/2018/02/Screen-Shot-2018-02-24-at-11 . 47 . 09-am . png

红色曲线显示异常行为。有人可能会怀疑,由于高学习率,优化器无法收敛到全局最小值,并一直在误差范围内跳动。

对于中等曲线(橙色),测试误差在第一个时期后开始缓慢增加。这是过拟合的经典例子。

批量

如果你熟悉深度学习,你一定听说过随机梯度下降(SGD)和批量梯度下降。为了重新访问,SGD 对每个数据点执行权重更新步骤。batch 在对整个训练集中数据点的梯度进行平均后执行更新。根据 Yann LeCun

SGD 的优势—
1。学起来快多了
2。往往能获得更好的解决方案
3。用于跟踪更改。

批量学习的优势—
1。收敛的条件是众所周知的。
2。许多加速学习技术,如共轭梯度,在批量学习中很好理解。
3。重量动力学和收敛速度的理论分析要简单得多

使用介于两者之间的方法,小批量梯度下降法很受欢迎。不是使用整个训练集来平均梯度,而是使用一小部分数据点的梯度平均。该批次的大小是一个超参数。

举例来说,考虑下图所示的损失情况

损失景观示例。原图

上面有数字的对角线是损失等值线。x 轴和 y 轴是两个参数(比如说 w1w2 )。沿着等高线,损耗是恒定的。例如,对于任何位于损耗为 4.500 的线上的 w1w2 对,损耗为 4.500。蓝色之字形线是 SGD 的行为方式。橙色线是您预期的小批量梯度下降的工作方式。红点代表参数的最佳值,此时损耗最小。

《深度学习》一书提供了一个很好的类比来理解为什么太大的批量效率不高。从 n 个样本估计的标准误差为 σ/√ n. 考虑两种情况——一种有 100 个样本,另一种有 10000 个样本。后者需要 100 倍以上的计算。但是标准误差的预期降低只有 10 倍

在我的实验中,我改变了批量大小。我获得的测试准确度看起来像—

内核宽度

CNN 中的卷积操作包括从特征图中提取特征的核。内核由学习到的参数组成。在二维卷积中,内核是一个 N*N 的网格,其中 N 是内核宽度。

增加或减少内核宽度各有利弊。增加核宽度会增加模型中的参数,这是增加模型容量的明显方法。很难对内存消耗进行评论,因为参数数量的增加会增加内存使用量,但特征图的输出维度会变小,从而减少内存使用量。

重量衰减

权重衰减是 L2 正则化的优势。它实质上惩罚了模型中较大的权重值。设置正确的强度可以提高模型的泛化能力并减少过度拟合。但是过高的值会导致严重的欠拟合。例如,我尝试了正常和极高的重量衰减值。如你所见,当这个系数设置不好时,学习能力几乎为零。

结论

这些实验完全证实了我们的大多数假设。像 ResNet18 这样调优不佳的复杂模型很容易比调优良好的简单架构表现更差。损耗曲线是研究超参数影响的良好起点。

为你的模型找到最好的超参数是困难的。尤其是手动操作的时候。我建议看看 HyperOpt 和 Optuna 这样的超参数优化库。这篇由 neptune.ai(一家为你的 ML 实验管理需求提供解决方案的公司)撰写的文章很好地介绍了这些包。

如果你和我一样是 ML 爱好者,那就来连线一下 LinkedIn Twitter。我非常乐意收到对这篇文章的任何评论。

专家系统 2.0

原文:https://towardsdatascience.com/expert-systems-2-0-c8c552f6b2d8?source=collection_archive---------32-----------------------

神经网络如何使程序知识大众化

Unsplash 上由 Franck V. 拍摄的照片

神经网络和深度学习是最终打开人工智能之路的钥匙吗,还是我们正处于另一个 AI Winter 的边缘?

如果你是企业家,那么答案是:没关系。你的资助并不依赖于资助者对你的研究议程——制造一个会说话的机器人——感到兴奋。这取决于为真实的人提供真实的价值。

事实是,尽管人工智能的发展到目前为止还没有制造出可以与我们自己的智能相媲美的机器,但沿着这条道路发展起来的技术都非常有用,并且充满了潜在价值。虽然看起来神经网络本身不足以构建最智能的系统,但它们丰富我们生活的全部潜力尚未得到充分挖掘。

今天,我想提醒我们所有人一项来自人工智能前一个时代的有用且相关的技术:专家系统。虽然不再是一个迷人的研究海报的孩子,这项技术已经对社会产生了持久的影响。即使没有强化学习、培训课程和其他前沿创新,神经网络也准备做同样的事情。

正如我们将看到的,正如专家系统基于描述性知识自动进行推理一样,神经网络也可以对程序性知识做同样的事情。与商业专家系统不同,商业专家系统在很大程度上掌握在企业手中,我们随时准备让所有人都可以使用神经网络系统。

专家系统:综述

专家系统至少需要两个组件才能运行:

  1. 知识库
  2. 推理机

知识库存储命题——这些命题是经过编码的信息——而推理机使用推理规则来推导新的命题。

显然,建立专家系统的一个主要挑战是建立知识库所需的知识获取。领域专家通常需求量很大,而他们自己通常缺乏整理自己知识的能力。这意味着构建知识库是程序员和专家之间耗时的协作。

然而,这种专业知识的缺乏也是专家系统有价值的原因。一旦编码,知识库很容易转移,并且不会过期。它可以被无休止地复制,并在许多应用程序中使用。专家系统将困难的技术推理和知识查找自动化,以换取前期投入的时间和精力。

毫不奇怪,专家系统通常出现在医学、法律和其他需要对事实和程序有明确的专业知识的领域。

由于它们是一项成熟的技术,与其学习技术细节,不如对它们的目的需求相关性有一个整体的了解,这会更好地为您服务。当讨论神经网络时,我们将回到这三个因素,所以请记住它们。

我们已经介绍了目的和要求…

目的:专家系统利用明确的事实和推理规则自动推理。

需求:构建或更新专家系统需要领域专家的时间和知识,以及用代码表示这些知识的能力。

那么,专家系统还有意义吗?当然可以。

虽然现在很少能找到明确称为“专家系统”的软件,但知识表示和推理的思想和实现仍在大量使用。例如, ROSS ,一个由 IBM Watson 支持的法律系统,结合了一个巨大的知识库和更现代的从自然语言处理领域过滤相关信息的方法。

此外,许多语音接口,将声音输入映射到意图,但有一个固定的意图和响应规则库,类似于旧的专家系统。这些新系统不是自动化需要专业知识的高度专业化的任务,而是自动化更日常的事情,如检查账户余额,同时被打包在一个被认为更智能的接口中(但通常也是硬编码的)。

总之…

相关性:虽然专家系统背后的思想很少作为独立产品出现,但它们对软件产生了持久的影响,并且一直沿用至今。

描述性知识与程序性知识

在继续讨论神经网络之前,我想快速了解一下我们人类可以拥有的知识类型的区别:描述性知识和程序性知识之间的区别。

描述性(也称显性陈述性)知识,采取事实和规则的形式。它通常可以用文字或符号来表示,并使用逻辑或其他演算来操作。

程序性(也叫隐性表现性)知识,更多的是关于知道如何去做事。它通常与直觉、肌肉记忆和实践等概念联系在一起。

想想骑自行车需要什么。当然,你知道你坐下来,把你的脚放在踏板上,这个动作会保持自行车直立。但是更重要的是知道如何骑自行车,这需要练习。这样,骑自行车就有了更多的程序成分。

另一方面,考虑找出三角形的缺角,给定一些长度和角度。在这里,得到解的过程要清楚得多,可能涉及到三角恒等式之类的东西。如果需要,每一步都可以写在纸上。这个问题需要更多的描述性知识。

当然,许多复杂的任务需要知识类型的混合,这种区分比它必然地指示一个深层的机械真理更方便。然而,我们会看到,在考虑神经网络的潜在应用时,记住这一区别是有用的。

神经网络和软件 2.0

事实证明,现实世界中的大部分问题都有这样一个特性,即收集数据(或者更一般地说,确定一个理想的行为)比显式地编写程序要容易得多安德烈·卡帕西,软件 2.0

你可能已经注意到上面的描述性知识是构成专家系统的知识库推理规则的那种东西。

如何为程序性知识建立一个知识库?嗯,这将是非常困难的,尤其是因为即使是这些领域的专家也很难准确地表达他们自己的专业知识。

也就是说,使用传统的编程方法是非常困难的。

机器学习模型将范式从程序员必须提供规则和输入以获得结果的范式转变为程序员可以提供输入和结果以导出规则的范式。那就是:

软件 1.0: 输入+规则→结果

软件 2.0: 输入+结果→规则

软件 1.0 的成分非常类似于专家系统所需的知识库和推理机。软件 2.0 的承诺是,任何人使用该软件都可以将学到的规则应用到许多新的输入中,而不需要他们自己拥有得出结果所需的专业知识。

我们可以看到这是如何暗示由神经网络和深度学习驱动的新一代专家系统的,其目的、要求和相关性如下。

目的:专家系统 2.0 将通常需要专业培训和实践的决策和感知自动化,在该领域,专业知识更多的是程序性的,而不是描述性的。

需求:要建立这样的系统,需要建立一个数据集,让专家运用他们的知识做出判断(例如,作者给创造性写作打分)。更新这样的系统只需要更新数据集。

相关性:专家系统 2.0 已经准备好将过程专家民主化,几乎不需要进一步的技术创新。

正如旧的专家系统一样,知识获取仍然需要领域专家的时间和注意力。然而,建立一个机器学习数据集仅仅需要他们练习他们的手艺,而不是解构它。此外,对于工程师来说,在数据集中表示这些知识比想出编码和操作符号知识的方法要容易得多。最后,随着数据集随着时间的推移而增长和发展,更新和改进模型可以更顺利地完成。

识别专家系统 2.0 的机会

如果一项任务需要程序多样性的专业知识,那么使用深度学习实现自动化的时机已经成熟。许多创造性学科符合这一描述——音乐、舞蹈、艺术等。

2018 年,谷歌艺术与文化实验室(Google Arts & Culture Lab)制作了 Living Archive ,这是一个经过训练可以预测韦恩·麦格雷戈(Wayne McGregor)舞蹈动作的模型,因此可以用来建议舞蹈编排。

在技术方面,评估的表演和作品要比合成新的表演和作品容易得多。像 SmartMusic 这样的软件能够对音频录音的语调和节奏等方面进行评分。然而,这些事情可以使用硬编码的规则来检测和分析。在音乐等领域,真正的价值来自于模特为诸如 dynamics 和 rubato 等更微妙、更具解释性的元素配乐。

如果人类专家可以在几秒钟内做出决定,然后有时间解释他们到底是如何做出决定的,那么捕捉数百万个这样的决定将使我们走向一台可以做同样事情的机器。

正如人工智能技术的理想一样,专家系统 2.0 将扩大和民主化人类智能,而不是使其过时。人们仍然应该每周去上小提琴或舞蹈课,但他们也可以即时获得有效的反馈,即使他们的老师不在他们面前。缩短反馈的距离将大大加快学习。

现在,为什么我说基于深度学习的系统将民主化专业知识,而这对于旧的专家系统来说并不是真的?这里有几个因素在起作用:

  • 旧的专家系统通常被设计在错误代价很高的领域——法律、药物设计、医学诊断——因此民主化的价值较低。
  • 自从有了互联网,我们的文化已经转向重视信息的公开获取。
  • 与上述相关,获得技术的机会比以往任何时候都多。

神经网络和深度学习可能只是通往人工智能的垫脚石,但这并不意味着它们没有直接的价值。

通过以专家系统自动化描述知识的方式自动化和民主化程序知识,神经网络可以改善我们对专业知识的获取,并永远增强我们的学习方式。他们的价值才刚刚开始被意识到。

专家与算法:一个观点

原文:https://towardsdatascience.com/experts-vs-algorithms-a-view-ea0951211f8f?source=collection_archive---------20-----------------------

解决问题:“专家的决定比算法的预测更好吗?”

大脑:我们用来思考的器官― 比尔斯,A·

“我很高兴能够及时回答,我也这么做了。我说我不知道。”― 马克·吐温

“你的假设是你观察世界的窗口。每隔一段时间就把它们擦掉,不然光线就照不进来了。”― 艾萨克·阿西莫夫

带骨龄计算的手部 x 光照片——迈克尔·海格斯特伦(来源:维基共享)

算法(人工智能,机器学习)的传播引发了一个有效的问题:专家的决策比算法做出的预测更好吗?这是一个预示着人类知识和行动存在危机的问题,因为它经常被问到。

例如,The Verge 最近的一篇报道题为:“为什么发现癌症的人工智能需要小心处理”(詹姆斯·文森特,2020 年 1 月 27 日)。来自该报告:“但对于医疗保健领域的许多人来说,这些研究展示的不仅仅是人工智能的前景,还有它的潜在威胁。他们说,尽管算法处理数据的能力显而易见,但护士和医生微妙的、基于判断的技能却不那么容易数字化。

但是这种观点忽略了表明其他情况的证据。有两个关键因素― (1)认知偏差(2)认知超载―在起作用。

要回答这个问题(专家 vs .算法),我们先来看看 认知偏差 所起到的作用。

心理学家和行为经济学先驱丹尼尔·卡内曼因“将心理学研究的见解融入经济科学,尤其是关于人类在不确定情况下的判断和决策”而获得 2002 年诺贝尔经济学奖他与经济学家阿莫斯·特沃斯基的合作是众所周知的――这种合作关系导致了行为经济学的许多基础性成果,尤其是 20 世纪 70 年代“前景理论”的发展。

前景理论是对作为现代经济理论基础的经典效用理论的重要更新,它是对人类估计中损失和收益不对称的行为框架,最初由丹尼尔·伯努利于 1738 年提出,作为对、【圣彼得堡悖论】的著名解决方案,并于 20 世纪 40 年代由约翰·冯·诺依曼奥斯卡·莫根施特恩运用理性选择的公理编织成其现代形式预期效用理论。(虽然冯·诺依曼和摩根斯坦使用理性选择的公理构建了现代效用理论,但伯努利在 1738 年建立了效用构造作为“道德期望”与数学期望相对,强调了他的效用观点是一种主观构造而非数学构造。这是他的论点的关键点:一个穷人的道德期望不同于一个富人的道德期望――这使得丹尼尔·伯努利成为真正的行为经济学先驱。)

要不是卡尼曼在 1996 年英年早逝,特沃斯基几乎肯定会与他分享诺贝尔奖(卡尼曼与经济学家弗农·史密斯(Vernon Smith)分享了诺贝尔奖,后者因其在实验经济学中的开创性作用而闻名)。

在研究生院,我第一次看到他们的经典论文“不确定性下的判断:启发式和偏见”(特沃斯基和卡尼曼,1974) ,并立即被不确定性下的判断、启发式在决策中的作用以及由此产生的系统性偏见吸引住了。在这篇经典论文中,Tversky 和 Kahneman 将这些偏见归纳为三个关键的启发法:代表性(一个对象代表特定类型的可能程度);可用性(基于脑海中出现的实例或事件对事件发生概率的主观评估);以及来自锚点的调整(从初始值开始做出的主观估计,并被调整以产生最终答案,这些调整通常被证明是不充分的)。

在他的精彩著作《思考,快与慢》(2011) 中,丹尼尔·卡内曼解释了扎根于心理学的观念,即人类的思维是两个心理系统的混合:一个快速、直觉、自动、几乎无意识的过程(系统 1);一个缓慢、审慎、有意识的过程,需要专注和努力(系统 2) 。这两个系统的相互作用使得我们的思想和行动。(注意书名中突出快和慢的思考后的逗号。)**

卡尼曼在“直觉与公式”一节中探讨了专家与算法的问题。他强调了临床心理学家 Paul Meehl 关于专家直觉和使用公式指导的有趣发现。米尔的发现表明,简单的算法(统计预测)在几乎所有测试领域都胜过或追平了专家(临床判断)。正如卡尼曼指出的那样,对于算法来说,实现的容易程度甚至可以让一场平局成为一场胜利。这些结果(“米尔模式”)震惊了当时的临床机构,这是可以理解的,因为人工智能的进步现在仍在继续。

卡尼曼强调的著名的阿普加评分清楚地表明了这一点。1953 年,医学博士维珍尼亚·阿普伽发表了她对新生儿评估新方法的建议。本文的公开目的是建立一个简单明了的新生儿分类,用于比较产科实践的结果、产妇疼痛缓解的类型和复苏的结果。在考虑了几个与婴儿出生时的状况有关的客观体征后,她选择了五个可以毫无困难地进行评估并教给产房人员的体征。这些迹象是心率、呼吸努力、反射性易怒、肌肉紧张度和颜色。婴儿完全出生后 60 秒,根据是否存在,每个迹象被给予 0、1 或 2 的评级。”(资料来源:“阿普加评分经受住了时间的考验”,芬斯特博士&伍德博士,2005)

简而言之,产房工作人员在出生后 1 分钟获得的这个简单分数(0-10)提供(并且仍然继续提供)新生儿的预后(如果 8-10,则为优秀;如果 3-7,则为一般;如果 0-2,则为差)。重要的是,阿普加博士选择了五个“可以毫无困难地评估和教授给产房人员”的客观体征――也就是说,它可以被非专家容易地理解。阿普加评分对降低婴儿死亡率产生了巨大的影响。根据纽约长老会/哥伦比亚大学医学中心的理查德·斯迈利博士的说法:“这个分数要求医生和护士用一种有组织的方法来观察新生儿,它帮助防止了无数婴儿的死亡。一旦医生和护士不得不分配一个分数,它就产生了一个必须采取行动来提高分数。这实质上是临床新生儿学的诞生。”(资料来源:纽约长老会健康问题:“发生在这里:阿普加评分”)

先驱维珍尼亚·阿普伽博士是哥伦比亚大学内外科医学院第一位拥有正教授职位的女性医生。

卡尼曼对直觉与公式问题的研究表明,“每当我们可以用公式代替人类的判断时,我们至少应该考虑它。”偏见(傲慢、过度自信等)阻碍了客观性。不相关的上下文或显著性偏差影响一致性。在可预测性差的情况下,算法有可能提供客观和一致的判断。使用卡尼曼提供的框架,很容易看出,在需要系统 2 思维(缓慢思考)的领域使用系统 1 思维(快速直觉)会产生这些情况。认识到这些模式可以帮助不同领域的规划者制定策略(就像阿普加博士所做的那样),以保持客观和一致的评估,因为情况(例如在出生后一分钟内快速评估新生儿)似乎需要直觉。

现在让我们通过检查 认知超载 所扮演的角色来看看专家与算法的问题。认知超载是复杂性增长和人类极限现实的结果。

阿图尔·加万德博士**在《纽约客》杂志上发表的关于医疗保健的文章声名鹊起。他的第一本书《并发症:一个外科医生对不完善科学的笔记》(2002) ,一本他在布莱根妇女医院住院期间写的散文集,为他赢得了国家图书奖。“包含并发症的文章(其中许多最初出现在《纽约客》上)分为三个部分――易错性、神秘和不确定性――每一部分都涉及医疗实践中不同种类的缺陷或困难。因此,Gawande 探索了医学可能达不到预期的许多方式。尽管尽了最大努力,人类还是会犯错。科学知识仍然是不完整的,留下了许多没有答案和无法回答的问题。有些情况就是模棱两可,需要在面对令人抓狂的不完整信息时做出判断,而且往往没有好的解决方案。”(资料来源:“像外科医生一样”,史蒂文·卢贝特,2003 年 5 月,康奈尔法律评论)**

人类易犯的错误、我们不知道的事情以及在信息不完整的情况下做出判断都是任何情况的一部分。当这些在时机很重要的情况下(例如,新生儿出生后 1 分钟或心脏病发作到达后 90 分钟)结合在一起时,复杂性总是存在,求助于算法(和公式)可能是必要的。**

在 2009 年,Gawande 出版了《清单宣言:如何把事情做好》*,在那里他着眼于如何利用“受过良好训练、拥有高技能和勤奋的人”积累的来之不易但难以管理的知识,并通过提出一个“克服失败,一个建立在经验基础上并利用人们拥有的知识,但也以某种方式弥补我们不可避免的人类不足”的策略来消除可以避免的常见和持续的失败——一个清单!***

与阿普加的见解相呼应,加万德认为:“清单似乎可以防止这种失败。它们提醒我们最起码的必要步骤,并使它们明确。它们不仅提供了验证的可能性,还灌输了一种更高性能的纪律。”作为一个例子,Gawande 讨论了由约翰·霍普金斯医院的重症监护专家 Peter Provonost 设计的用于解决 ICU 中心静脉导管感染问题的清单如何在两年的时间内显示出此类感染的显著下降(几乎降至零)。另一个例子是一名奥地利儿童在滑入冰冷的池塘后奇迹般地康复,这表明现代医学的快速和协调的复杂性是如何创造这样的奇迹的。清单宣言有很多这样的例子,读起来很有意思。

清单或公式或算法可以提供一个客观和一致的途径,通过关注在时间敏感的情况下的最小必要,来消除可避免的故障,在这种情况下,不断增长的复杂性和人的易错性(以及人的极限)可能会不利地结合在一起。这种算法方法可能在某些领域不太适用(至少目前如此),但重要的是要认识到这种方法适用的所有领域。

这种对算法的观点违背了专家和学者当前的智慧。但理论和经验为这一观点提供了充分的支持,表明为什么至少考虑算法在规划复杂操作和为复杂项目汇集专业知识中的作用是重要的。

在这种情况下,为什么简单的算法能如此成功?从周围的噪音中提取信号。

  • 认知偏差(详见 Tversky-Kahneman 论文)将焦点转移到噪声上,特别是当问题表现出高信噪比和时间敏感性时。
  • 根据“思考,快和慢”中解释的系统 1/系统 2 模型,尽管经常遇到这种情况(婴儿每天都出生),但系统 1 思维(快速、联想、直觉)可能会主导系统 2 思维(缓慢、审慎、逻辑)。发生这种情况的原因有很多。结合了速度和相关公式的算法在这种情况下似乎很方便。
  • 相关公式:现代统计学和群体遗传学的先驱罗纳德·费雪(和他那个时代的许多杰出统计学家一样,他也是一个狂热的优生学者),在面对统计模型的未知参数时,强调了充分统计的概念。跳过技术解释,充分统计背后的直觉是为现象的统计模型找到一个具有充分解释力的代理。例如,样本均值和方差作为总体真实均值和方差的代理。基本上,这意味着确定一组简单的相关参数或一个简单的代表性函数(即使是一个粗略的函数),以提供足够的预测能力。虽然围绕阿普加分数的神话层出不穷,但很明显,阿普加在一段时间内的观察和她在综合方面的天赋帮助她确定了出生后立即观察的相关迹象,以预测婴儿的状况和所需的护理。
  • 正如 Gawande 的书中充分说明的那样,认知超载只是反映了人类由于复杂性的增长和解决日常问题所需的专业知识的协调而带来的负担。Gawande 展示了三个因素――人的易错性、未知性(我们不知道的)和不确定性(部分或不完善的信息)―是如何导致判断错误的。为了避免错误,积累经验并掌握诀窍,Gawande 建议用清单作为灌输所需纪律的方式。**
  • 失足跌入冰冷池塘的儿童的康复案例说明了在现代医学发展到极限的情况下,生存概率非常低的情况是如何得到扭转的。这看起来不可思议,因为期望每件事都非常及时地非常好地工作,并且准确无误地到位,没有任何误差,这是不合理的。清单在两个方面有所帮助:(1)将问题快速归类为非常规问题(识别未知问题),以及(2)为采用非标准技术的专业干预铺平道路(使用合理的判断,利用专业知识)。
  • 在许多领域(如建筑设计、飞机等复杂机械系统的设计)中,将系统设计到极限(根据经验建立合理的概率模型并构建适当的安全系数)是非常标准的做法,但这通常不适用于分布式过程的设计,尤其是跨越学科边界的过程。失败的影响只有在事后才能确定。只有专业项目(核项目、太空项目等)出于必要才采用这种端到端视图。算法可以廉价地探索多种可能性。即使模拟低概率事件,也能帮助规划者理解面对最坏情况时如何应对。

认知偏差视为专家低效或不利地过滤信息,将认知超载视为人类在面对大量信息时的无能或限制,算法可以克服这些障碍,使决策变得客观和一致。**

本文仅考察了在典型的不确定性和复杂性条件下,专家相对于算法的相对优势,回避了引入算法所带来的道德、社会和经济影响这一更大的问题。工业革命的教训和卡尔·马克思在其名著《资本论】中阐述的关于自动化轨迹的见解与这一讨论相关。但是我会把它留到下一天。**

劳动的工具,当它采取机器的形式时,立即成为工人自己的竞争者。― 卡尔·马克思,《资本论》第一卷

期待您的问题、评论和反馈。

Suresh Babu |年 2 月 6 日|旧金山湾区

我对人工智能前沿的兴趣:

  • 作为一个作家,研究人工智能的含义、伦理、社会影响和经济学(作为一个作家)
  • 作为从业者,为企业和政府创造 AI 战略和应用
  • 作为顾问,帮助非营利组织和非政府组织评估人工智能的全球影响和效用

向一个六岁的孩子解释大数据

原文:https://towardsdatascience.com/explain-big-data-to-a-six-year-old-71e341e5da45?source=collection_archive---------37-----------------------

爸爸,什么是大数据?

照片由 Remy_LozUnsplash 上拍摄

作为一名期待中的父母,我有一种难以言喻的恐惧,担心有一天我的孩子会说出他的第一个严重关切:“爸爸,什么是大数据?”。

这可能不会成为现实,但当我的孩子对他们父亲的职业感到好奇时,我该如何向他们解释大数据的概念?

快进到我宝宝的六岁生日。我最大的恐惧终究会成为现实。天真的头脑会开始好奇爸爸在工作中做什么。经过漫长的一天回到家,我最终将不得不告诉我的孩子我典型的一天是什么样子的。当我骄傲地给我的孩子一份亲手挑选的礼物时,最后的时刻就要到来了。

我小时候没有乐高玩具,所以我保证会给我的孩子补上。当我把孩子最喜欢的乐高积木拆成碎片时,我说 “我的孩子,听着,仔细听着,我将用这些乐高积木一劳永逸地解释大数据”

想象你有一堆乐高积木,上面有 100 块不同颜色和形状的积木,爸爸让你收集所有红色的积木。作为一个六岁的孩子,你可以在几分钟内完成。最简单的方法就是检查每一块,把红色的拿出来。迟早,你会把它们都握在你的小手里。

Fran Jacquier 在 Unsplash 上拍摄的照片

然而,爸爸想挑战一下。当你在数原来的那一堆的时候,爸爸会增加 200300 ,甚至 1000 更多的砖块。一个六岁的孩子可能不知道 1000 块乐高积木有多大,但是随着你一遍又一遍地数,它永远也数不完。

凯利·西克玛Unsplash 上拍摄的照片

奥马尔·弗洛雷斯在 Unsplash 上拍摄的照片

当爸爸在工作的时候,人们也让我拿出乐高积木。但是他们有比我们大得多的乐高积木。这些碎片可以堆积到的大小,一栋房子,一栋建筑,甚至一座山。

如果爸爸一直像你这样数数,他会累坏的,下班后就没有时间和你玩了。但是爸爸很聪明,他有一些秘密武器。他有两个朋友帮助他: RangOStackO

兰戈所做的是他整理东西。当你有一个小小的乐高积木堆,每个人都在你的视线范围内时,选择一种特定的颜色似乎很简单。但是当事情变得越来越大的时候,爸爸不能独自处理所有的搜索和挑选。他让 RangO 按颜色组织乐高积木。爸爸可以有几个颜色相同的积木,而不是一堆五颜六色的。你看到爸爸现在捡红砖有多容易了吗?最酷的是 RangO 可以根据形状、颜色或者爸爸能想到的任何东西来准备食物。

StackO 怎么样?当爸爸不得不在兰戈准备的红色积木堆中捡起一个房子大小的红色乐高积木时,爸爸无法用手将它们捆起来。StackO 帮助爸爸将所有的部件连接在一起,形成一个单独的乐高玩具。StackO 最适合将许多小片段放入一个表单中。StackO 还可以连接不同种类的零件。爸爸可以有一个建筑大小的红色、蓝色和绿色的乐高积木。

在 RangO 和 StackO 的帮助下,爸爸不再害怕挑选乐高积木,这就是为什么爸爸总是很高兴回到你身边。

LEGO 集合表示具有数百个信息片段的大量数据。毫无疑问,这种搜索和挑选工作只在很小的体积上进行,但是它不能扩展。数据呈指数级增长,而运营仅呈线性发展RangOStackO 不过是数据准备、数据清理和数据存储的技术工具。

现实生活中的大数据并不局限于这些简单的流程,但为了让好奇的访问者了解大数据,有必要展示其中一个核心概念。我们害怕我们无法理解的东西。大数据不是复杂的操作,而是大规模的简单操作。

大数据不是复杂的操作,而是大规模的简单操作

照片由凯勒·伍兹Unsplash 拍摄

我叫 Nam Nguyen,我写关于大数据的文章。享受你的阅读?请在 Medium 和 Twitter 上关注我,了解更多更新。

[## 大数据工程师的一天

为巴黎的一家广告技术巨头工作

towardsdatascience.com](/a-day-in-the-life-of-a-big-data-engineer-a286e4a5ae29) [## 阮怀南

阮怀南的最新推文(@namnguyen168)。热爱机器学习的技术极客。法国巴黎

twitter.com](https://twitter.com/namnguyen168)

向你奶奶解释深度学习神经网络

原文:https://towardsdatascience.com/explain-deep-learning-neural-networks-to-your-grandma-80b726bf6124?source=collection_archive---------22-----------------------

“除非你能向你的祖母解释,否则你不会真正理解某事”

不知道这句话最初是从哪里来的,它有时被部分归因于阿尔伯特·爱因斯坦。

无论如何,这篇文章是我的尝试(向我自己和其他人)解释神经网络(NN)算法如何以一种简单、新手、直接和高级的方式工作,没有任何公式、方程或代码。我完全知道下面的文字可能有些不准确,但是为了避免复杂的解释和保持简单,这是很自然的,否则你的奶奶不会明白…

在我们开始之前,我想感谢由杰瑞米·霍华德和雷切尔·托马斯创立的 fast.ai ,这是一个专注于深度学习和人工智能的非营利研究小组。我已经从他们的免费在线课程中学到了很多,而且我还在继续学习。

哦..我在最后写了这篇文章的摘要,以防你想直接看。

好吧,我假设如果你读了这篇文章,你至少对 NN 有所了解,所以我就不介绍 NN 是如何在最近几年变得流行起来的,以及它们如何帮助你解决问题了。

那么神经网络算法是如何工作的呢?

一个字:数学。

两个字:矩阵乘法。

如果你的祖母不理解这个解释,那么你可以告诉她我们从输入层开始,这是神经网络模型的输入数据。

输入图层形状为( 1,n) ,意为 1 行和 n 列。我们也可以称之为张量,把张量想象成一个可以容纳 N 维数据的容器。向量是 1D 张量,矩阵是 2D 张量。

一定要注意 NN 中张量的形状

如果我们的数据是表格形式的,那么这个输入层代表我们表格中的一行,这意味着一个具有 n 个特征的观测值。假设我们想用 NN 来预测房子的价格,那么 Xs 可以是房间数量、位置、是私人住宅还是大楼中的公寓、楼层数等。如果我们的数据是一个图像,那么输入层代表一个像素,其中x是该像素的波段/通道,例如,红色、贪婪和蓝色。

你猜怎么着让我们把它变得更具体,我会在示意图上加一个数字。所以现在我们有了这个:

正如你所看到的,在下面的例子中 n =6。顺便说一下,我刚刚做了这些数字,它们不属于特定的数据集。

接下来,我们将输入层乘以一个矩阵,这个矩阵可以称为权重矩阵。

为了使乘法有效,我们需要确保输入层中的列数等于权重矩阵中的行数。这里就是这样,输入层形状是 (1,n)(1,6) ,权重矩阵形状是 (n,m)(6,4) 。虽然我们不能控制权重矩阵中的行数(它必须等于输入层中的列数),但是我们可以将权重矩阵的列数设置为我们喜欢的任何数字。这里,为了简单起见,我将权重矩阵的列数设置为 4。权重矩阵中的数字是完全随机的(NN 就是这样开始的)。示意图中的字母 P 也是随机选择的,因为我需要示意性地表示数字,而 X 已经被输入层取了。

乘法运算结果是:

结果是一个形状为 (1,m)(1,4) 的张量,矩阵乘法就是这样进行的……第一个张量的列数必须与第二个张量的行数相同,结果是一个张量的行数等于第一个张量的行数,列数等于第二个张量的列数。

换句话说: (n,m) @ (m,k) →(n,k)

现在我们把这个结果传递给一个叫做激活函数的函数。有各种各样的激活功能,但这里我们将使用一个常见的称为 ReLU。名字真的不重要。这个函数获取我们的结果(蓝色张量)中的数字,并根据一个非常简单的规则更改里面的数字:如果值大于 0,则保留该值,如果值小于 0,则将其更改为 0。数学上是这样写的: max(0,X)。

请注意,形状没有改变。我们刚刚做的叫做隐藏层。你想加多少都可以。例如,如果我们想添加另一个隐藏层,我们只需重复我们之前所做的。意思是,取(最右边的)蓝色张量,乘以一个 4 行和任意列数的权重矩阵(用随机数),然后在结果张量中将小于 0 的值改为 0,就这样。希望不要混淆,但是我在示意图中选择的蓝色张量中的字母值,只是为了让你知道数字是不同的。

一旦您添加了所有您想要的隐藏层,就该收敛到最终输出了,这意味着模型的预测或估计。为此,我们需要知道我们想要的输出是什么样的。如果是预测房价这样的回归问题,那么我们只需要一个数——shape(1,1) 。由此,我们知道我们要乘以的张量的形状应该是什么。如果是分类问题,我们需要一个形状为 (m,how _ many _ categories _ we _ has)的张量,在我们的例子中, m = 4

为了简单起见,假设我们有一个回归问题,那么:

再次注意形状,为了得到我们想要的形状 (1,1) 的预测,我们必须将我们的蓝色张量 (1,4) 乘以另一个形状为 (m,1) 的张量,这里的意思是 (4,1) 。现在我们得到了模型的预测,我们使用一个叫做损失函数的函数来比较预测值与真实值或实际值。这种比较将使我们了解我们的模型有多好。在我们对我们的模型精度进行第一次估计(通过损失函数)之后,我们(我指的是 NN 算法)改变权重矩阵中的数字(黄色的),以便模型精度会更好。我们不断重复这个过程,直到我们有我们想要的精度。

神经网络是如何工作的——摘要

1)我们获取输入数据

2)我们将它乘以一个带有随机数的矩阵

3)我们改变所有值< 0 to 0, and do not change when value> 0

4)我们根据需要重复步骤 2 和 3 多次

5)我们将结果乘以最终张量以获得模型预测

6)我们使用一个函数(称为损失函数)将模型预测值与真实值进行比较,这将给出模型准确度的值

7)我们改变第 2 步和第 5 步中矩阵中的所有数字,这样精确度会好一点

8)我们重复所有步骤,直到我们对精确度满意为止

嗯,就是这样。我希望这足够清楚和简单。如果你明白了,或者至少你认为你明白了,只有一种方法可以证明。试着向你的奶奶解释,或者随便找一个奶奶(但是要征得她的完全同意😊)

向六岁小孩解释 Hadoop MapReduce

原文:https://towardsdatascience.com/explain-hadoop-mapreduce-to-a-six-year-old-d338937c7f4b?source=collection_archive---------62-----------------------

好奇的头脑永远不知道自己的极限

张家瑜Unsplash 上拍照

在向我六岁的孩子解释大数据时,我履行了一个父亲的职责。尽管如此,我还是感觉到了对答案明显的不满。

我不确定原因。要么是我让我的孩子在拆他最喜欢的乐高玩具时心烦意乱,要么是我的解释不够有教育意义。无论如何,我必须想出另一个计划。

[## 向一个六岁的孩子解释大数据

爸爸,什么是大数据?

towardsdatascience.com](/explain-big-data-to-a-six-year-old-71e341e5da45)

毫无疑问,我的孩子已经掌握了一两件关于大数据的事情,但大数据不仅仅是这些。我回忆了我成为大数据工程师的历程。我想到了我最初是如何理解它的每个核心概念的。MapReduce 这个词突然出现在我的脑海里。

在我职业道路的开端,Hadoop MapReduce 是我遇到的第一个关键想法。它帮助我获得了如何利用常见操作在大数据上执行大规模计算的概念。

我想这对于我六岁的孩子来说是另一个合适的概念。相比之下,我变得更加沮丧。

我不知道如何向我的孩子解释 MapReduce。

它没有任何关于计算机如何工作的线索。天真的头脑怎么可能理解 Hadoop MapReduce 的原理呢?

当我们进行这种对话时,一切都变了

  • 爸爸,你最喜欢的食物是什么?
  • 嗯,我喜欢披萨,你呢?
  • 我喜欢冰淇淋。我太爱他们了,我长大后想娶一个冰淇淋。

就在那一刻,我的内心有了火花。我的孩子对这种喜爱的食物的热情可能是我的 MapReduce 去神秘化的关键。

那是一个炎热的夏日。我和我的宝宝站在一辆冰淇淋车前。卖冰淇淋的人友好地问我们想要什么样的冰淇淋。我不喜欢任何一个,所以我让我的孩子决定。

  • “爸爸,我想吃三色冰淇淋——它在我耳边轻声说道
  • 那就两筒三色冰淇淋吧——我向忙碌的冰淇淋小贩下了订单

卖冰淇淋的人做了三勺巧克力、香草和草莓口味的冰淇淋。然后,他把这些放在一个褐色的华夫饼干筒上。

照片由弗洛伦西亚·维亚达纳Unsplash 拍摄

我的宝宝对它的新鲜冰淇淋很满意,毫不犹豫地开始享用。突然,一个 MapReduce 的图像闪过我的脑海。我都想明白了。我让我的宝宝停下来吃第一口。我说:“我的宝贝,我用你的三色冰淇淋给你解释 Hadoop MapReduce”。

你看到那个带着巨大冰柜的卖冰淇淋的人了吗?那是成吨的冰淇淋。每天他都要处理顾客的冰淇淋需求。想象一下,每个人都和你一样喜欢三色冰淇淋。卖冰淇淋的人必须提供那种独特的冰淇淋。

但是制作合适的三色冰淇淋需要时间。卖冰淇淋的人必须先从冰淇淋容器里拿勺子。在那里,他必须把它们放在华夫饼干筒的正确位置上,然后把它们送到顾客手中。爸爸打赌他可以这样做一整天。

想象一下那些冰淇淋容器变成了一大堆冰淇淋。成堆的冰淇淋可以让卖冰淇淋的人站在它们的树荫下。冰淇淋车现在每小时吸引成千上万的顾客。不管卖冰淇淋的人有多熟练,他永远也赶不上节奏。

作为一名大数据工程师,爸爸伸出手来帮助卖冰淇淋的人。爸爸不知道如何制作一个漂亮的冰淇淋勺,但他知道如何快速制作。像往常一样,爸爸有一些朋友帮助他。这一次他将他们分成两组:映射器减速器

照片由莎拉·瓜尔蒂埃里Unsplash 拍摄

映射器是做什么的?他变了。一位制图师牺牲自己的生命来制作完美的冰淇淋勺。卖冰激淋的人的琐碎任务成了地图绘制者的生活目标。每一个都有三种口味的巧克力、香草和草莓。出色的制图者不能在组内竞争。每个制图者在不同的时间完成他的任务。它们都有助于还原剂使用的公共池。

手上拿着华夫饼干的减速器,正在等待制图者完成他们的任务。地图绘制者已经准备好了冰淇淋勺。减速器只需将它们从公共池中取出。每个减速器每分钟能够输送数千个三色冰淇淋。这种效率使得卖冰淇淋的人能够取悦他的顾客。

卖冰淇淋的人很满足,因为他想招待多少顾客就可以招待多少顾客。那些绘制者和缩减者总是关注他们唯一的可能性。他们从未让他失望。“我的孩子,你现在可以继续享用你最喜欢的三色冰淇淋了。请记住,一项简单的工作可能会因为大量的投入而变得不可能。在测绘员和减速器的帮助下,我们可以克服障碍”。

Hadoop MapReduce 的强大之处在于其组件定义明确的职责。它使我们能够将大量的工作分成单个工人就能完成的小块。

理解 Hadoop MapReduce 是每个大数据工程师的职责。它为其他大数据计算算法奠定了基础。这完全是利用平庸的计算机来获得计算能力。它激发了使用大数据的想法。

我叫 Nam Nguyen,我写关于大数据的文章。享受你的阅读?请在 Medium 和 Twitter 上关注我,了解更多更新。

[## 如何构建可扩展的大数据分析管道

大规模建立端到端系统

towardsdatascience.com](/how-to-build-a-scalable-big-data-analytics-pipeline-7de18f8be848)

用手工计算解释线性回归

原文:https://towardsdatascience.com/explain-linear-regression-with-manual-calculation-1622affdce6b?source=collection_archive---------15-----------------------

这可能很疯狂,但这将帮助你从线性回归中了解更多。

在我看来,线性回归是数据科学中最简单的话题之一。使用线性回归,您可以获得两组变量(自变量和因变量)之间的相关性。我确信许多人已经熟悉了在不同的软件或编程工具中使用线性回归。比如在 Excel 中,你可以通过数据分析求解线性回归;在 Python 中,可以使用 statmodels 或 scikit-learn 模块。但在本文中,我将通过手动计算进行多元线性回归,并解释一些常见变量背后的意义。

我们开始吧。

(PS:在本文中,线性回归的所有基本假设都是有效的,例如,同方差,关系是线性的,没有多重共线性)

以下是本次演示的数据集:

线性回归的目的是创建一个模型,以方程的线性形式显示因变量(Y)与自变量(X)之间的关系。如果只有一个独立变量,这将被称为简单线性回归。如果超过一个,那么这将被称为多元线性回归。自变量和因变量之间的关系反映在系数上。系数的数学定义是因变量相对于自变量的偏导数。通俗地说,系数就是告诉你一个自变量增加 1,而其他自变量保持不变时,因变量的变化。除了这些变量,还有一个常数叫做截距,方程中还有一个误差。截距将不受独立变量的值的影响,而误差团队捕获模型无法解释的随机误差。

下面是线性回归的一般方程:

yᵢ代表第 I 次观察的因变量;xᵢₖ代表第 I 次观测的自变量,β₀代表截距,βₙ代表 xᵢₖ的系数;最后,εᵢ代表误差团队进行第 I 次观察。在我们的例子中,只有一个独立变量。因此,等式变成了

对于ε有一些假设。第一个假设是ε的均值为 0。第二个假设是ε有一个恒定的方差,并且是不相关的。更常见的是,ε也被假设为独立同分布(也称为 iid)。

为了得到β₀和β₁的值,最主要的两种估计方法是最小二乘估计和最大似然估计。今天我只解释最小二乘估计。

最小平方估计

定义:

  1. b₀和 b₁代表β₀和β₁的估计值
  2. ŷ代表 y 的估计值;ŷᵢ = b₀ + b₁ xᵢ
  3. eᵢ代表实际 yᵢ和估计ŷᵢ之间的残差

最小二乘估计的目标是估计β₀和β₁,使得残差平方和(SSE)最小。在数学表述中,SSE =σ(yᵢ-ŷ)=σ(yᵢ-(b₀+b₁xᵢ)。从等式中,你会明白为什么这种方法被称为最小二乘估计。

为了估计 b₀和 b₁的值,需要偏导数,我不会详细说明。b₀和 b₁的最终方程式如下:

所以在例子中,x̄=(1+3+5+8)/4 = 4.25;ȳ = (5+9+18+25)/4 = 14.25.sₓᵧ=(1–4.25)(5–14.25)+(3–4.25)(9–14.25)+(5–4.25)(18–14.25)+(8–4.25)(25–14.25)= 79.75;sₓₓ=(1–4.25)+(3–4.25)+(5–4.25)+(8–4.25)= 26.75。因此,b₁ = 79.75/26.75 = 2.98,而 b₀= 14.25–2.98 * 4.25 = 1.58。

稀有

评估线性回归性能的一个常用参数是 R 平方(R)。但在解释 R 之前,有必要先先解释两个多余的项,总平方和(SST),回归平方和(SSR)。SST、SSR 和 SSE 都显示了不同度量的变化。

SST 显示了观察到的因变量的变化;SSR 显示了回归解释的变异;SSE 显示了回归线周围的变化。通过简单的计算,可以发现 SST = SSR + SSE,aka 观察到的因变量的总变异是回归模型解释的变异和未解释的变异之和。

在示例中,SST =(5–14.25)+(9–14.25)+(18–14.25)+(25–14.25)= 242.75。SSE =(5-(1.58+1 * 2.98))+(9-(1.58+3 * 2.98))+(18-(1.58+5 * 2.98))+(25-(1.58+8 * 2.98))= 4.99。SSR = 237.76。

r 等于 SSR/SST 或 1-SSE/SST。因此,当 R 较高时,它表示回归可以捕捉观察到的因变量的大部分变化。这就是为什么我们可以说当 R 很高时回归模型表现很好。在示例中,R = 237.76/242.75 = 0.98

一旦我们计算了 R,我们就可以立即计算相关性(R)。相关值只是 r 的平方根,符号与 b₁.相同因此,本例中的相关性为 0.99。

在面试中,更常见的是被问及一个特定模型的不同变量的原理和解释。应用并不困难,因为已经有许多工具来执行计算。但是为了完全理解如何解释所获得的结果,理解背后的原理也是重要的。

我希望这篇文章能帮助你更多地了解线性回归,以及你应该如何解释回归中的一些常见变量。希望你喜欢。下次见。

我的其他文章

如果您是 Python 新手(尤其是自学 Python 的话),请将此加入书签

我在德国亚马逊公司为期六个月的实习中做了什么,学到了什么

如何使用 SQL 进行数据分析(房产销售时间序列)

为什么 SQL 中的窗口函数如此重要,你应该马上学习它

用数学细节解释支持向量机

原文:https://towardsdatascience.com/explain-support-vector-machines-in-mathematic-details-c7cc1be9f3b9?source=collection_archive---------37-----------------------

数学中的超平面、最大边缘、硬边缘、软边缘

支持向量机(SVM)是一种监督机器学习算法,通常用于解决二分类问题。它还可以应用于多类分类问题和回归问题。这篇文章描述了二进制线性支持向量机背后的数学原理。理解数学有助于在实践中实现和调整模型。此外,您可以从头开始构建自己的支持向量机模型,并与 Scikit-Learn 中的模型进行比较。详细内容,你可以和我的另一篇文章一起阅读这篇文章。

具体来说,本报告解释了线性支持向量机的关键概念,包括硬边界和软边界情况下的原始形式及其对偶形式;支持向量的概念、最大间隔和泛化过程。

SVM 的核心概念

假设我们有 n 个训练点,每个观测值 I 有 p 个特征(即 x_i 有 p 维),并且在两个类 y_i=-1 或 y_i = 1 中。假设我们有两类线性可分的观测值。这意味着我们可以通过我们的特征空间画一个超平面,使得一个类的所有实例都在超平面的一边,而另一个类的所有实例都在另一边。(p 维的超平面是 p-1 维的子空间。在下面的二维例子中,超平面只是一条线。)我们将超平面定义为:

其中w是 p 向量,b 是实数。为了方便起见,我们要求\u w = 1,所以量x* \u w+\u b 是从点 x 到超平面的距离。

图片来自维基百科

因此,我们可以用 y = +1/-1 标记我们的类,并且超平面划分类的要求变成:

应该如何选择最佳超平面?

回答这个问题的方法是选择在两个类之间产生最大间隔 M 的平面,这被称为最大间隔分类器。

图片来自维基百科

从前面的图表中,我们可以看到 H1 并没有把这两个阶级分开;对于 H2 和 H3,我们将选择 H3,因为 H3 的利润率更高。数学上,我们选择\u b 和\u w来最大化 M,给定约束条件:

定义w = w/Mb = b/M,我们可以将其改写为:

支持向量

支持向量是距离分离超平面最近的数据点。它们是最难分类的数据点。此外,支持向量是训练集的元素,如果被移除,其将改变划分超平面的位置。生成权重的优化算法以这样的方式进行,即只有支持向量确定权重,从而确定边界。数学上支持向量被定义为:

硬利润 SVM

硬边界 SVM 对穿过超平面的支持向量非常严格。它不允许任何支持向量被分类到错误的类别中。为了最大化超平面的间隔,硬间隔支持向量机面临优化问题:

软边际 SVM 和超参数C

一般来说,类不是线性可分的。这可能是因为阶级边界不是线性的,但往往没有明确的边界。为了处理这种情况,支持向量机添加了一组“松弛变量”,这些变量允许几个点偏移到甚至跨越边距,如下图所示:

作者图片

我们希望在最大化利润宽度的同时最小化总松弛量,这被称为软利润支持向量机。这被更广泛地使用,并且目标函数变成:

对于某些常数 C 。这个优化问题被称为原始问题。常数 C 代表时差的“成本”。当 C 小时,允许更多的点进入余量以获得更大的余量是有效的。更大的 C 将产生具有更少支持向量的边界。通过增加支持向量的数量,SVM 减少了它的方差,因为它更少地依赖于任何单独的观察。减少方差使模型更一般化。因此,减少 C 将增加支持向量的数量并减少过拟合。

使用拉格朗日乘数:

两个限制

我们可以将约束优化问题重写为原始拉格朗日函数:

我们可以根据之前为 wb 获得的关系,最大化乘数,而不是根据约束最小化 wb 。这被称为对偶拉格朗日公式:

这现在是一个相当简单的二次规划问题,用序列最小化优化来解决。有很多编程工具可以用来解决优化问题。你可以使用 Matlab 中的 CVX 工具来解决这个问题。或者熟悉 python 的话,可以用 CVXOPT 包来解决。我有在 Medium 的另一篇文章讨论 CVXOPT 包的使用,以及如何应用它来解决对偶公式中的 SVM。一旦我们解决了这个问题,我们就可以很容易地计算出系数:

浏览支持向量机算法背后的数学知识肯定有助于理解模型的实现。它为正确的问题选择了正确的模型,并为超参数选择了正确的值。

希望这有所帮助。感谢大家的阅读!这是我所有博客帖子的列表。如果你感兴趣的话,可以去看看!

[## 我的博客文章库

我快乐的地方

zzhu17.medium.com](https://zzhu17.medium.com/my-blog-posts-gallery-ac6e01fe5cc3) [## 阅读朱(以及媒体上成千上万的其他作家)的每一个故事

作为一个媒体会员,你的会员费的一部分会给你阅读的作家,你可以完全接触到每一个故事…

zzhu17.medium.com](https://zzhu17.medium.com/membership)

自己解释!利用语言模型进行常识推理

原文:https://towardsdatascience.com/explain-yourself-leveraging-language-models-for-common-sense-reasoning-1c988b8b24cd?source=collection_archive---------41-----------------------

活动讲座

Nazneen Rajani | TMLS2019

https://torontomachinelearning.com/

关于演讲者

Nazneen Fatema Rajani 是 Salesforce Research 的高级研究科学家。领英

关于谈话

深度学习模型在需要常识推理的任务上表现不佳,这通常需要某种形式的世界知识或对输入中不立即存在的信息进行推理。我们以自然语言序列的形式收集人类对常识推理的解释,并在一个名为常识解释(CoS-E)的新数据集中突出标注。

我们使用 CoS-E 来训练语言模型,以自动生成解释,这些解释可以在新颖的常识自动生成解释(CAGE)框架中的训练和推理期间使用。凯奇在具有挑战性的常识问答任务中提高了 10%的技术水平。我们进一步研究 DNNs 中的常识推理,使用人类和自动生成的解释,包括转移到域外任务。实证结果表明,我们可以有效地利用语言模型进行常识推理。

自己解释!利用语言模型进行常识推理

通过深度学习对新冠肺炎 X 射线分类器的可解释性和可见性

原文:https://towardsdatascience.com/explainability-and-visibility-in-covid-19-x-ray-classifiers-with-deep-learning-c12c3247f905?source=collection_archive---------48-----------------------

关键词:深度学习,Grad-CAM,X 射线,新冠肺炎

编者按: 走向数据科学 是一份以数据科学和机器学习研究为主的中型刊物。我们不是健康专家或流行病学家,本文的观点不应被解释为专业建议。想了解更多关于疫情冠状病毒的信息,可以点击 这里

目的

在复活节的周末,我接触了一些用于新冠肺炎肺的深度学习分类器。演示结果看起来很好,似乎与当时关于这个主题的一些学术研究出版物相匹配。但是真的“没事”吗?

最近我碰巧听了一个关于“机器学习中的可解释性”的在线午餐网络研讨会,演讲者在最后谈到了这个分类结果:

上图也在这篇研究论文中呈现:“我为什么要相信你?”解释任何分类器的预测。我们可以看到,分类器实际上被训练为将背景像素(例如,雪等野生环境)作为主要输入来分类它是宠物狗还是野狼。

这唤起了我以前的兴趣,现在肯定激起了一点好奇心:

  • 我们如何“研究”这些通常以“黑盒”形式出现的新冠肺炎分类器,以了解哪些像素实际上促成了“新冠肺炎肺”的结果?
  • 在这种情况下,我们可以利用的最简单的方式或最简单的工具是什么?

这是旅途中另一个 10 分钟的笔记。

范围

幸运的是,在过去的几年里,出现了各种 CNN 衍生分类器的便利工具:

我们将使用 Grad-CAM 对我们之前的新冠肺炎肺分类器做一个快速演示。

“tensor flow 2 . 2 . 0 RC+Jupyter”Docker 用于搭载英伟达 T4 GPU 的 AWS Ubuntu 16.04 服务器上。Tensorflow 2 提供了一个简单的“梯度带”实现。

下面是我在 Ubuntu 服务器上启动它的快速提示:

docker run -itd — runtime=nvidia -v /zhong/tf/:/tf -p 8896 :8888 -p 6026 :6006 — name tf-gpu2 tensorflow/tensorflow: 2.2.0rc2-gpu-py3-jupyter

方法

你可以放心地忽略上面 Grad-CAM 研究出版物中引用的一点数学知识。

这里引用它只是为了我们对最初的提议(在第 4 页& 5) 与后来使用的 Python 代码进行持续的交叉检查,希望结果也有更好的透明度。

(1):为了获得任何类别 c 的宽度 u 和高度 v 的类别区别定位图,我们首先计算类别 c 的分数 yc(在 softmax 之前)相对于卷积层的特征图 Ak 的梯度。这些流回的梯度被全局平均汇集,以获得目标类的神经元重要性权重 ak。(2):在为目标类 c 计算 ak 之后,我们执行激活图的加权组合,并在其后跟随 ReLU。这会产生一个与卷积特征图大小相同的粗略热图

试验

现在让我们尝试一下目前为止我们能找到的最简单的编码:

1。导入包

import tensorflow as tf;
print(tf.__version__)

2.2.0-rc2

import tensorflow as tf
import tensorflow.keras.backend as K
from tensorflow.keras.applications.inception_v3 import InceptionV3
from tensorflow.keras.preprocessing import image
from tensorflow.keras.applications.inception_v3 import preprocess_input, decode_predictions
import numpy as np
import os
import imutils
import matplotlib.pyplot as plt
import cv2

2。加载我们之前训练并保存的 模型

new_model = tf.keras.models.load_model('saved_model/inceptionV3')
new_model.summary()

我们可以看到,在最终的全球平均池之前,我们模型中 4D 的最后一个 CNN 层被称为“ mixed10 ”。

3。计算 Grad-CAM 热图

下面是一个简单的热图,实现了上面的 Grad-CAM 方程(1)和(2)。在本帖中有解释

with tf.GradientTape() as tape:
 last_conv_layer = model.get_layer('mixed10') 
 iterate = tf.keras.models.Model([model.inputs], [model.output, last_conv_layer.output])
 model_out, last_conv_layer = iterate(testX)
 class_out = model_out[:, np.argmax(model_out[0])]
 grads = tape.gradient(class_out, last_conv_layer)
 pooled_grads = K.mean(grads, axis=(0, 1, 2))
 heatmap = tf.reduce_mean(tf.multiply(pooled_grads, last_conv_layer), axis=-1) 

在我们的例子中,它将生成一个(27,6,6)的 heatmap numpy 数组。然后,我们可以将它调整到原始 X 射线图像的大小,并将其覆盖在 X 射线图像的顶部——就这样。

然而,在这种情况下,我们将使用一个稍微更详细的版本,这个版本在本文中也有很好的解释。它使用 Grad-CAM 热图编写了一个函数,该热图的大小已经调整为原始 X 射线的大小:

# import the necessary packages
from tensorflow.keras.models import Model
import tensorflow as tf
import numpy as np
import cv2class GradCAM:
    def __init__(self, model, classIdx, layerName=None):
        self.model = model
        self.classIdx = classIdx
        self.layerName = layerName
        if self.layerName is None:
            self.layerName = self.find_target_layer()def find_target_layer(self):
        for layer in reversed(self.model.layers):
            # check to see if the layer has a 4D output
            if len(layer.output_shape) == 4:
                return layer.name
        raise ValueError("Could not find 4D layer. Cannot apply GradCAM.")def compute_heatmap(self, image, eps=1e-8):
        gradModel = Model(
            inputs=[self.model.inputs],
            outputs=[self.model.get_layer(self.layerName).output,
                self.model.output])
        # record operations for automatic differentiation
 **with tf.GradientTape() as tape:
            inputs = tf.cast(image, tf.float32)
            (convOutputs, predictions) = gradModel(inputs)
            loss = predictions[:, self.classIdx]**
 **# use automatic differentiation to compute the gradients
        grads = tape.gradient(loss, convOutputs)**
        # compute the guided gradients
        castConvOutputs = tf.cast(convOutputs > 0, "float32")
        castGrads = tf.cast(grads > 0, "float32")
        guidedGrads = castConvOutputs * castGrads * grads
        convOutputs = convOutputs[0]
        guidedGrads = guidedGrads[0]
        weights = tf.reduce_mean(guidedGrads, axis=(0, 1))
        cam = tf.reduce_sum(tf.multiply(weights, convOutputs), axis=-1)# resize the heatmap to oringnal X-Ray image size
        (w, h) = (image.shape[2], image.shape[1])
        heatmap = cv2.resize(cam.numpy(), (w, h))# normalize the heatmap
        numer = heatmap - np.min(heatmap)
        denom = (heatmap.max() - heatmap.min()) + eps
        heatmap = numer / denom
        heatmap = (heatmap * 255).astype("uint8")# return the resulting heatmap to the calling function
        return heatmap

4。装一张新冠肺炎肺部 x 光片

现在,我们加载一个从未在模型训练和验证过程中使用过的测试 X 射线。(也上传到了之前的帖子里)

filename = ‘./test/nejmoa2001191_f1-PA.jpeg’
orignal = cv2.imread(filename)
plt.imshow(orignal)
plt.show()

然后调整大小为 256 x 256,并将其规范化为像素值在 0.0 和 1.0 之间的 numpy 数组“dataXG”。

orig = cv2.cvtColor(orignal, cv2.COLOR_BGR2RGB)
resized = cv2.resize(orig, (256, 256))
dataXG = np.array(resized) / 255.0
dataXG = np.expand_dims(dataXG, axis=0)

5。进行快速分类

现在我们可以调用上面新加载的模型来进行快速预测

preds = new_model.predict(dataXG)
i = np.argmax(preds[0])
print(i, preds)

0 [[0.9171522 0.06534185 0.01750595]]

所以被归类为 0 型——新冠肺炎肺,概率为 0.9171522。

6。计算 Grad-CAM 热图

# Compute the heatmap based on step 3
cam = GradCAM(model=new_model, classIdx=i, layerName='mixed10') # find the last 4d shape "mixed10" in this case
heatmap = cam.compute_heatmap(dataXG)#show the calculated heatmap
plt.imshow(heatmap)
plt.show()

7。在原始 x 光片上显示热图

# Old fashioned way to overlay a transparent heatmap onto original image, the same as above
heatmapY = cv2.resize(heatmap, (orig.shape[1], orig.shape[0]))
heatmapY = cv2.applyColorMap(heatmapY, cv2.COLORMAP_HOT)  # COLORMAP_JET, COLORMAP_VIRIDIS, COLORMAP_HOT
imageY = cv2.addWeighted(heatmapY, 0.5, orignal, 1.0, 0)
print(heatmapY.shape, orig.shape)# draw the orignal x-ray, the heatmap, and the overlay together
output = np.hstack([orig, heatmapY, imageY])
fig, ax = plt.subplots(figsize=(20, 18))
ax.imshow(np.random.rand(1, 99), interpolation='nearest')
plt.imshow(output)
plt.show()

(842, 1090, 3) (842, 1090, 3)

这似乎表明我们的新冠肺炎演示分类器“相信”患者在“右气管旁条纹”周围有一点“不透明”问题?我真的不知道,除非我和真正的放射科医生核实一下。

好了,让我们再尝试一些从真实案例提交到 GitHub 存储库中的测试图片:

filename = ‘./test/1-s2.0-S0929664620300449-gr2_lrg-b.jpg’

[9.9799889 e-01 3.8319459 e-04 1.6178709 e-03]]

这似乎也是一个合理的新冠肺炎解释,表明问题更多发生在左心线区域?

让我们试试另一种随机测试 x 光:

filename = ‘../Covid_M/all/test/covid/radiol.2020200490.fig3.jpeg’

0 [[0.9317619 0.0169084 0.05132957]]

令人惊讶的是,这看起来并不完全正确,但再看看它似乎也不太离谱,对不对?它显示了两个问题区域——主要问题在左侧,一些问题在右侧,与人类放射科医师的标记有些一致?(同时希望它不是在人类标记上训练——这是另一个层次的可解释性问题)。

好了,我就说到这里,因为我不确定会有多少人对阅读这份 10 分钟的简短记录感兴趣。

好了,我就说到这里,因为我不确定是否有太多的人会对阅读 X 射线感兴趣。

为什么?

那么,我们为什么要为此烦恼呢?为什么我们要触及这个话题并记下来以备后用?

我个人深深体会到“可解释性”和“可解释性”的重要性,以及实现它们的任何技术方法。任何进入这个维度的微小尝试都是值得努力的,不管它们有多微小。最终,“数据公平”、“数据公正”和“数据信任”将建立在数字经济的过程透明性之上。此外,它现在开始变得可用。现在我们有越来越多的研究和工具,就在今天人工智能开发者的指尖。

最后但同样重要的是,具体到这个演示,我对这种工具的一个欣赏是,它甚至不需要像素级标记,它试图自动为您生成肺部病变区域,有点半自动标记。在真实作品中是有意义的。我确实记得去年我的一个放射科朋友帮助我为 U-Net 训练一次又一次地为一些骨折数据生成一些像素级标签——这种练习确实伤害了我们的眼睛。

然后

我现在有点走神了。得益于过去 10 多年来深度学习的快速发展,医学成像是人工智能领域中相对成熟的方向。它值得一些好时光。然而,接下来我希望我们可以在 NLP 方面做更多的尝试,如果我们有一点时间的话。

确认

所有的资料来源都已在需要的地方插入上述文本。如果需要,我会放入更多的参考资料。

免责声明:

同样,上面应该是一个快速笔记,以防如果我现在不记录它,几周后它就消失了。都是作为一个“开发者”的个人看法。内容和文本可以根据需要随时修改。以上更多的是展示想法和方法,而不是临床解释,这将需要专业的放射科医生在良好的数据数量和质量上建立黄金法则。

前贴:10 分钟内新冠肺炎肺部 X 线分类和 CT 检测演示

下一篇文章:将新冠肺炎模型部署到一个统一的人工智能演示平台上

https://community.intersystems.com】最初发表于

可解释的人工智能和人工智能的可解释性

原文:https://towardsdatascience.com/explainable-ai-and-ai-interpretability-54a3d2b22052?source=collection_archive---------46-----------------------

苹果 | 谷歌 | SPOTIFY | 其他

Bahador Khaleghi 在 TDS 播客

编者按:迈向数据科学播客的“攀登数据科学阶梯”系列由 Jeremie Harris 主持。Jeremie 帮助运营一家名为sharpes minds的数据科学导师初创公司。可以听下面的播客:

如果我让你解释为什么你会读这篇博客,你可以用很多不同的方式来回答。

例如,你可以告诉我“这是因为我喜欢它”,或者“因为我的神经元以特定的方式激活,导致我点击了广告给我的链接”。或者你可以更进一步,把你的答案和量子物理的基本定律联系起来。

关键是,为了有效,解释需要针对一定的抽象层次。

在生活中确实如此,但在机器学习中也是如此,可解释的人工智能作为一种确保模型正常工作的方式,以对我们有意义的方式,受到了越来越多的关注。理解可解释性以及如何利用它变得越来越重要,这就是为什么我想与 H20.ai 的数据科学家 Bahador Khaleghi 交谈,他的技术重点是机器学习中的可解释性和可解释性。

我们的谈话涵盖了很多领域,但这里有一些我最喜欢的要点:

  • 明确如何分解数据科学生命周期非常重要。尽管在数据探索和预处理之间,或者在特征工程和建模之间并不总是有明确的界限,但是将分析数据的过程分成定义明确的块会使您的工作更加模块化,并使协作更加容易。
  • 最近,无论是行业还是监管机构,对可解释人工智能的兴趣都有了巨大的提升。这主要是因为我们使用的模型越来越复杂,因此其行为越来越难以解释。鉴于我们现在依赖机器学习来实现许多任务关键型应用,如无人驾驶汽车、贷款审批甚至医疗程序,可解释性将变得更加重要,作为确保模型不会表现出危险或意外行为的一种方式。
  • 不太可能被输入中的微小变化所迷惑的模型被称为“稳健的”,事实证明,可解释的模型往往也更稳健。在某种程度上,这有点直观:对你的预测有一个连贯的解释意味着你的推理是合理的,而合理的推理不太可能被一点点噪音打乱。
  • autoML 的出现使得可解释性变得更加重要,因为 autoML 本质上把整个数据管道变成了一个黑盒(而不仅仅是机器学习模型)。当您使用 autoML 时,您甚至没有明确地决定如何设计或选择您的功能,您将不会对您的模型的决策过程有太多的可见性,这就为问题和病态敞开了大门。

你可以在 Twitter 上关注巴哈多,在 Medium 上阅读他的一些帖子这里这里

你可以在推特上关注我。

订阅《走向数据科学》的月刊,直接在你的邮箱✨中接收我们最好的文章、视频和播客

可解释的人工智能:shapely 值在营销分析中的应用

原文:https://towardsdatascience.com/explainable-ai-application-of-shapely-values-in-marketing-analytics-57b716fc9d1f?source=collection_archive---------8-----------------------

直觉的介绍性指南,以及用 Python 实现营销分析的 SHAP

照片由 Artyom KimUnsplash 上拍摄

XAI:可解释的 AI 。来源:作者图片

最近,我偶然发现了一份白皮书,其中谈到了人工智能在营销分析中的最新应用。具体讲了 XAI(可解释 AI)在营销组合建模【白皮书】中的应用。这引起了我的注意,我开始探索三件事:XAI,营销分析的现状,以及 XAI 在营销分析中的潜在应用。在浏览了所有可用资源后,我意识到 XAI 在重塑营销分析方面有着巨大的潜力。

在本文中,首先我们将讨论与营销分析现状相关的具体挑战及其解决方案。其次,我们将尝试开发一个关于 XAI 的直觉,最后,我们将在一些公开可用的营销数据集上实现 XAI。

与营销分析现状相关的挑战及其可能的解决方案:

存在许多挑战,但与营销分析现状相关的三个重大挑战与所用模型(GLMs:广义线性模型)的准确性、渠道属性以及市场反应中固有的非线性相关。鉴于这些挑战,我们将讨论 XAI 如何解决与 GLMs、渠道归属相关的问题,以及与非线性相关的其他问题。作为介绍,我们将在本节非常简要地讨论一些挑战及其解决方案;然而,随着本文的深入,我们将详细讨论每一件事。

  1. GLMs

现有挑战:广义线性模型(GLMs)被广泛用于整个行业的营销组合建模。GLM 的附加性质有助于我们很容易地确定营销渠道对销售收入的贡献。与 GLMs 相比,现代算法要精确得多,但这些算法更像是黑盒模型,缺乏可解释性。因此,工业上广泛使用 GLM 的唯一原因是因为它易于解释。

可能的解决方案: XAI 为我们提供了一种解释任何黑箱模型的极好方法,因此它为我们使用高度精确的集合模型代替 GLMs 打开了大门。因此,现在在高精度集合模型的帮助下,我们不仅在精度上而且在可解释性上胜过 GLMs。

2。频道归属

现有挑战:这是营销人员最大的痛点之一。由于渠道之间存在相互作用,因此几乎不可能公平地将收益分配给不同的渠道。

可能的解决方案:来自合作博弈理论的匀称的价值观来拯救这里。Shapley 值是一种将总增量收益公平分配给游戏中合作玩家的方法。在我们的案例中,营销渠道是玩家相互合作以增加收入等指标;我们的目标是将增加的收入公平地分配给营销渠道。谷歌分析也在其数据驱动的归因方法中使用 shapely 值。【来源】

3。不同营销渠道的互动

现有挑战:有一些渠道,作为独立渠道,并不是重要的贡献者;然而,与其他渠道相结合可以发挥重要作用。因此,对于营销人员来说,了解互动渠道的不同组合是很重要的。随着通道数量的增加,相互作用的数量显著增加,并且在 GLMs 中包括所有这样的相互作用项变得非常麻烦。

可能的解决方案:为了应对这一挑战,我们将再次使用 shapely 值,但方式不同。我们将使用 SHAP 算法来研究大规模渠道的相互作用。【 SHAP 算法是机器学习中 shapely 值的实现,用来解释和诠释任何黑盒 ML 模型。因为,在我们的例子中,我们将用高度精确的基于树的集合模型代替 GLMs,因此我们将使用 SHAP 来解释和说明我们的模型中的通道相互作用。

4。正面影响阈值

现有挑战:例如,在电视广告方面,较低的支出可能不会产生任何收入;然而,在一定的最低消费后,电视广告开始显示其积极的影响。另一方面,数字渠道的市场反应函数通常是陡峭的,这意味着您在数字渠道上增加的支出越多,在达到饱和点之前,您看到的对您收入的影响就越大。由于市场固有的动态非线性性质,使用线性模型获得正面影响阈值变得非常具有挑战性。

可能的解决方案:我们可以使用非线性的、基于树的集成模型以及解释器模型,而不是使用线性模型。从 explainer 模型中,我们将获得 shapely 值(每个渠道单独的贡献),然后我们可以绘制这些 shapely 值,以获得每个渠道的积极影响阈值。下图显示了电视正面影响阈值的一个例子。

显示电视广告正面影响阈值的图表:Y 轴是电视广告带来的增量收入(Shapely Value)。x 轴显示电视广告支出。来源:作者图片

5。每个渠道的最佳支出

现有挑战:最佳支出取决于不同渠道的营销响应函数,通常这些营销响应函数本质上是非线性的。例如,如果我们不断增加在某个渠道上的支出,那么在某次支出后,增加的积极影响开始减少。增量影响开始饱和然后最终下降的点,就是我们在该渠道上实现最佳支出的点。

可能的解决方案:同样,解决方案与第 3 点相同,基于 shapely 值。然而,唯一的区别是,这里我们必须得到一个点,从这个点开始,我们的图变得渐近,或者在某些情况下开始下降。

鉴于所有这些挑战和解决方案,你可能很想知道与匀称的价值观相关的 5 个问题的答案:

  1. 为什么我们一直在使用 GLMs?
  2. 什么是匀称的价值观?
  3. shapely 值如何用于归因?
  4. 对于复杂的机器学习模型是如何实现的?
  5. 如何解释它,以获得可操作的见解?

接下来,本文将尝试回答上述问题。

目录

  1. 为什么选择营销分析中的 GLMs?
  2. 得体的价值观
  3. 匀称的价值观:直觉与实例
  4. 使用 Shapely 值的机器学习可解释性
  5. SHAP:营销分析中的一个用例
  6. 结束注释
  7. 更多阅读和参考资料

为什么选择营销分析中的 GLMs?

为了理解这一点,让我们将机器学习模型分为两类:一类是简单模型,另一类是复杂模型。简单模型是那些非常容易解释/说明并且准确性低的模型;然而,复杂的模型本质上是黑箱,几乎不可能解释,而且精确度非常高。

简单模型的例子是线性回归模型和决策树。线性模型很容易解释,例如:借助模型方程中特征权重的大小和符号,我们可以了解特征对输出的影响。在决策树的情况下,我们可以通过分裂(基于基尼系数、信息增益或熵)从根节点向下移动到叶节点,并解析树以容易地理解/解释模型。虽然这些模型是高度可解释的,但是这些模型有几个问题:线性模型不准确,因为它们不能解释特征的非线性行为,而决策树也不准确,并且有过拟合的问题。

随着模型复杂性和准确性的增加,模型的可解释性降低。来源:作者图片

与决策树相比,随机森林具有高准确性和非常低的方差/过拟合,但是随机森林具有非常大量的树,这些树不能以如此大的数量来解释。因此,这种模型的可解释性是一个大问题。

如果我有精确的模型,那我为什么要用不太精确的呢?

首先,我们应该理解正确解释模型输出的能力是非常重要的。它有助于建立利益相关者的信任。我们只是不能对我们的利益相关者说,我们应该实施某个模型,因为它非常准确,具有良好的 AUC/ ROC 数,或者具有非常低的均方误差。利益相关者总是对知道一个模型如何做决策感兴趣。

其次,模型中每个特征的可解释性在营销分析中起着非常重要的作用。我们应该知道所有重要特性影响模型结果的方向和程度。这可以通过 GLMs 容易地实现;然而,在复杂模型的情况下,我们确实获得了特征的相对重要性值,但是这些值不是很有帮助,因为它没有告诉我们关于对模型输出的影响的大小和方向的任何事情。

因此,在营销分析中,线性模型因其易于解释而受到青睐,即使不如复杂模型准确。

为了解决复杂模型的可解释性,有一些技术(LIME、Shapely 等)可以用来创建解释模型,这是一个简单的线性模型,可以非常容易地解释复杂模型。我们将在本文中讨论的方法使用合作博弈论中的 shapely 值来创建解释模型。

这里要注意的有趣的事情是,当这些复杂模型与解释模型相结合时,它们不仅在准确性上(很明显)而且在可解释性/可解释性上都优于 GLMs。

接下来,我们将试图通过它的定义和一个例子来发展一种关于匀称的价值观的直觉。然后,我们将继续使用一个非常简单的数据集来实现它的机器学习。

得体的价值观

沙普利值是由诺贝尔经济学奖获得者罗伊德·S·沙普利开发的,作为一种在团队成员中公平分配团队产出的方法。

我们将讨论 shapely value 的定义,但为了更好地理解这个概念,我建议你先跳到示例部分(Shapely Values:直觉和示例),然后再回到定义部分。

定义

形式上,联盟博弈被定义为:给定一个联盟博弈 G(N,ν) ,其中 N 是博弈中玩家的集合, ν 是一个函数(一个特征函数),当这个特征函数作为 ν(S)应用于每个子集 S 时,它给每个子集 S 分配一个实数。我们也称ν(S)为联盟 的价值。

如果有 2 个玩家(A,B),那么将有 4 个子集{Null,A,B,AB}。因此,对于子集 AB,我们有ν(AB)作为 A 和 b 的联合值。

ν(S)描述了 S 的成员通过合作可以获得的总期望收益。

Shapley 值是将总收益分配给参与者的一种方式,假设他们都合作。根据 Shapley 值,给定一个联盟博弈 G(N,ν),玩家I得到的金额为

这里, n 是 N 个玩家的总数,‘N \ { I }’表示不包括玩家‘I’的玩家集合。因此,这里的“S”包括可以从“N \ { 1 }”中的玩家中得到的所有子集。对于每个 S,' |S|' 表示每个子集中玩家的数量。

对于 S 中的每个子集,' |S|!'表示排列的数目,可以从 S 中的玩家中创建。 (n-|S|-1) 表示除玩家 I 之外的剩余玩家的数目。 (n-|S|-1)!’代表除了玩家 I 之外的剩余玩家可以形成的排列总数

对于 S 中的每个子集, |S|的乘积!(n-|S|-1)!代表 S 和 S 之外的玩家可以形成的排列总数,使用这个乘法是因为对于这些(|S|!*(n-|S|-1)!)组合数参与人‘I’的边际贡献保持不变。

注意:公式考虑了排列,因为玩家的顺序在这里起着重要的作用。这里的事件不是独立的;因此,shapely 值计算考虑了所有可能的排列,然后最终取平均值来计算每个玩家的贡献。

喏, n!显示游戏中所有玩家的所有组合所能形成的排列总数,它用于计算可能形成联盟的不同排列的平均贡献。

这个公式乍一看似乎有点令人生畏,但是一旦我们借助一个例子来理解它,它就会变得非常容易理解。在下一节中,我们将通过一个示例计算来培养直觉。

注意:特性函数应满足某些最低属性:

  1. 单调性:如果联盟中玩家数量增加,收益应该不会减少。
  2. 超可加性:ν(S ∪ T) ≥ ν(S)+ν(T),若ν(s∪t)=∅;这意味着大联盟的收益最高。

匀称的价值观:直觉与实例

为了进一步简化和更好地理解 shapely 价值观,我们举一个非常简单的例子,一个虚构的新零售店,它只通过电视广告、平面广告和广播广告进行营销。这些营销媒介的结合有助于零售店获得顾客并产生收入。

零售店的老板观察到这些营销媒体的组合为商店带来了更高的收入,但是问题在于通过每种营销媒体获得的收入份额的归属。这些信息非常重要,因为拥有者可以利用这些信息优化营销预算。

这个问题(或游戏)可以借助 shapely 价值观来解决。为了推进这个过程,让我们看看“表 A”的内容,并在上面的 shapely 值的定义和游戏的实际数据之间画出一些平行关系。

“表 A”中的第一列显示了联盟( S,如上面定义中所表示的),其可以使用游戏的参与者(参与者是电视、广播、印刷品)来形成,包括空集(其中没有参与者)。第二列显示对应于第一列中每个联盟的符号。第三列( ν(S)或定义中的“联盟价值”)显示了与第一列中给出的每个子集/联盟相对应的收入。

换句话说,当只显示电视广告时,商店获得 3000 美元的收入,当只显示广播广告时,收入为 1000 美元,类似地,当只显示印刷广告时,收入为 1000 美元。然而,当广播或印刷与电视结合时,收入是其个人贡献总和的两倍,当三者结合时,收入为 10,000 美元。

表 A:显示了联盟和联盟的价值。来源:作者图片

我们将使用“表 A”中给出的信息,并使用这些值得出每台电视、收音机和印刷品的 shapely 值(平均边际贡献)。

下表 B 显示了电视、广播和印刷品的 shapely 值的计算。

表 B:显示每种营销媒体的平均边际贡献或匀称价值的计算。来源:作者图片

让我们看看“表 B”中给出的电视 shapely 值的计算方法。第一栏显示不包括电视的联盟。所以有 2 这样的组合包括一个空集 {} 。第二列显示没有电视的收入,即 v(S) 或没有电视的联盟对子集的价值。

第三列示出了包括电视和在表格中表示为'S ∨{ I }'的联盟。第四列显示包括电视在内的联盟收入或价值。第五列显示添加电视后的收入变化或电视的边际贡献,表示为“v(S ∨{ I })-v(S)”。第六列显示玩家总数( n )。

第七列显示了每个子集 S 中的玩家数量。第八列是对于 S 中子集的边际贡献的出现的可能排列总数以及进一步除以 n!对该贡献进行平均。最后一栏是电视机的平均边际贡献总和(6000 美元)。****

类似地,也计算了印刷品(2000 美元)和广播(2000 美元)的平均边际贡献或合理价值。我希望表 A 和表 B 以及它们的计算有助于发展对匀称的价值观的基本理解和直觉。

对 shapely 值的基本理解和直觉很重要,因为本文的下一部分是关于使用 shapely 值的机器学习可解释性。我们将讨论并了解这些匀称的值如何被用于机器学习,特别是在媒体混合建模中。

注 1: 定义联盟的特征功能或“价值”是一个关键步骤,应极其谨慎。在现实世界中,它的定义严重依赖于数据收集的方法。如果您有兴趣阅读在线广告活动归因模型中如何定义特征函数,请参考本文

注 2: Shapely 值保证解中的唯一性。Shapley 值是唯一满足属性 效率对称性哑元 的归属方法,这些属性合起来可以认为是一个公平支付的定义。 【出处】****

效率:所有玩家的 Shapley 值之和等于大联盟的值,这样所有的收益都在玩家之间分配。

对称:如果两个玩家对所有可能的联盟贡献相等,那么他们的贡献应该是相同的。

哑元:如果玩家 I 没有给任何联盟增加价值,那么玩家 I 将获得零的有形价值,或者换句话说,获得零奖励。

可加性:设‘a’是来自游戏‘x’的 shapely 值,‘b’是来自游戏‘y’的 shapely 值。那么游戏‘x+y’的 shapely 值就是‘a+b’。

关于公理的更多细节, 参见本书的这一节。

使用 Shapely 值的机器学习可解释性

现在,既然我们对 shapely 值有了基本的了解,那么接下来我们应该讨论如何在机器学习解释中使用 shapely 值,然后我们将讨论它在营销分析中的效用。

让我们看看合作/联盟博弈论中的 shapely 值计算和它在机器学习中的使用之间的并行性。

在机器学习模型的情况下,用作生成机器学习模型的数据的特征值的每个实例充当团队成员,并且机器学习模型的预测充当输出或回报。

为了更直观地理解它,让我们假设我们已经训练了一个机器学习模型,以根据在不同营销渠道上的花费来预测收入。在数据集中,我们有一个数据实例,其中电视等营销渠道的支出为 2000 美元,广播支出为 500 美元,印刷支出为 800 美元。该数据实例的收入预测值为 10 万美元;然而,来自所有数据实例的收入的平均预测值是$80k(基础值)。鉴于这种情况,我们想了解并解释电视、广播和印刷品是如何造成基础价值(8 万美元)和预测价值(10 万美元)之间的差异的。因此,在本例中,电视、广播和印刷品的实例值(支出)充当玩家,任何实例的预测收入充当产出或回报。在这种情况下,每个频道的归属收入可能类似于电视的归属收入为 1 万美元,而广播的归属收入为 6000 美元,印刷品的归属收入为 4000 美元。因此,每个营销渠道的这些归属值有助于我们了解不同营销渠道的支出如何影响我们的增量收入。

在上面的例子中,有两件事情是并行发生的:首先,我们使用了一些机器学习算法进行预测,其次,我们使用 shapely 值来理解数据行/实例的特征行为。因此,对于最准确的预测,我们可以使用任何黑盒复杂模型,同时我们可以使用非常简单的可解释/可解释的 shapely 值来解释特征行为。这种结合解决了复杂模型的可解释性/可解释性的主要问题。

现在,我们对 shapely 值如何适应复杂模型的可解释性有了更高层次的理解。因此,现在是时候更深入地研究和理解 shapely 值的算法实现了。

得体的附加解释(SHAP)

SHAP(SHapley Additive explaints)是一种解释任何机器学习模型输出的博弈论方法。它将最优信用分配与使用博弈论及其相关扩展的经典 Shapley 值的本地解释联系起来。【来源】

SHAP 是一个解释者模型,它有优化的功能,可以借助 shapely 值来解释任何黑盒模型。为了理解 SHAP,我们首先需要知道解释者模型的概念,然后我们将看到这个解释者模型如何适应任何黑盒模型的可解释性。

解释者模型

对于复杂的黑盒模型(如集成模型或深度学习模型),我们使用不同的模型来解释复杂的模型。这样的模型被称为解释者模型,它是原始模型的一个可解释的近似。解释者模型是一个简单的线性加性归因模型,作用于二元变量。

讲解器型号

这里,g(z′)是一个解释函数。∅o 是基础值,它代表所有输入关闭时的模型输出。∅i 是要素 I 的 shapely 值。z’要么为零(要素不存在时),要么为一(要素存在时)。m 是简化输入特征的数量。

我们还应该注意到,g(z′)的值与原始模型 f(x)的输出相匹配,它也被称为局部精度。局部精度也是一个方法获得唯一解所需的三个属性之一。另外两个属性是缺失和一致性。这三个属性保证了博弈论中 Shapley 值的唯一性,因为它们适用于机器学习模型预测的局部解释。要了解更多关于这些属性的信息,请阅读这篇文章。

为简单起见,让我们假设我们有一个数据集,其中有 3 个特征电视、广播、印刷作为预测变量,收入作为目标变量。我们有 100 行/实例的数据,对于每一行/实例,我们将计算 g(z′)。

对于特征变量(电视、收音机、印刷品)的任何数据实例“j”:

g(z′)等于特征值实例的模型预测值。对于电视、广播、印刷品,z’的值是 1,因为它们有一些实例值。如果有这样一个例子,电视花费了\(x_tv,广播花费了\)x_radio,但是印刷品没有花费,那么在这种情况下我们的 g(z′)将如下。

由于解释器模型在实例或行级别的特征数据上工作,因此它也被称为预测的局部解释。这个解释模型以 shapely 值的形式直接测量局部特征重要性(也称为 SHAP 值 用于局部特征归属)。当我们组合每个预测的局部解释时,我们得到了全局解释的全局模型结构。

我们将讨论局部和全局解释,当我们以 python 为例讨论 SHAP 库的用法时,这将变得更加清晰。

这个解释器模型如何与黑盒模型一起工作?

****流程图显示解释器模型将数据和预测值作为输入,并基于 shapely 值创建解释。资料来源——瑞典 PyCon:用拉维·辛格的 SHAP 解释 ML 模型

这个流程图让我们很好地了解了解释器模型如何与黑盒模型协同工作。

利用数据,我们训练黑盒模型,然后将训练好模型与数据一起传递给解释器模型。

该黑盒模型然后被用作预测函数,并且连同特征值一起,该函数被用于计算每个特征的特征重要性(SHAP 值)。SHAP 值是使用下述公式计算的,该公式与 shapely 值计算公式非常相似。

****计算特征 I 的 SHAP 值的公式,例如 x。来源:ML 解释能力的 SHAP 值- Adi Watzman

我们现在正在计算∅i,它是特征 I 在预测中的贡献,它分三步计算,如下所示。

  1. 首先,我们创建没有特征 i (表示为 S )的所有特征的子集,然后针对 S 中的每个子集,计算具有( f(S U {i}) )和没有( f(S) )特征 I 的预测值
  2. 其次,我们减去两个预测( f(S U {i})-f(S) ),以获得该特征对于每个子集的边际贡献。
  3. 最后,我们取上一步计算的所有边际贡献的平均值来得到特征 I 对预测的贡献。

上述 SHAP 值的计算与合作游戏的 shapely 值的计算非常相似。这里唯一的区别是特征函数被黑盒模型代替,这里我们计算的是特征 I 对预测的贡献,而不是对游戏的贡献。

一旦我们获得了所有特征实例的所有 SHAP 值,那么这些 SHAP 值将用于解释模型的局部和全局。

要获得关于 SHAP 价值观的详细信息,请阅读《SHAP 价值观》作者斯科特·M·伦德伯格的论文。

在下一节中,我们将看到如何局部和全局地解释模型,以获得可操作的见解。

SHAP 价值观:营销分析中的一个用例

了解了 SHAP 值后,现在让我们看看如何将它应用到一些营销数据集中。

我们的数据集包括电视广告支出、广播广告支出、报纸广告支出等特征,我们的目标变量是每周产生的销售收入。

我们可以假设这些数据是从营销组合的时间序列数据中创建的横截面数据。尽管这些数据与真实世界的营销组合数据集相差甚远,但即使如此,它也将为我们提供一个很好的想法,即当我们使用它进行营销组合建模时,SHAP 值如何帮助我们获得可操作的见解。

导入库并读取数据集。

这个数据集可以在 Kaggle 上找到。我对该数据集所做的唯一更改是将“销售”列乘以 100。以便它看起来更接近产生销售收入。

我没有关于这个数据集的太多细节,我只是用它来展示 SHAP 值在营销数据集中的应用。

创建训练测试分割,拟合随机森林回归方程,预测并检查误差。

创建解释器模型,然后从 shapely 值创建一个数据框架

有几种方法可以让我们使用 python 中的 shap 库创建解释器模型。我们有形状。 KernelExplainer ,它是模型不可知的,可以用来解释任何机器学习模型。我们也有 shap。 TreeExplainer ,针对基于树的机器学习模型进行了优化。在我们的例子中,我们使用随机森林算法,这是一个基于树的模型,所以我们将使用 TreeExplainer。

让我们仔细看看 SHAP 价值数据框架。

我们可以从上面的数据帧中清楚地看到,对应于测试或训练数据帧中的每个特征值,我们在测试和训练数据集的 shap 数据帧(df_shap_train,df_shap_test)中有一个 shap 值。因此,我们可以看到,对于每个要素的每个数据实例,我们都有一个 SHAP 值。

检查基础值

来源:作者图片

当我们在上面讨论解释者模型时,我们已经讨论了基本值。这个∅o 是基础值。

注意:TreeExplainer 的基值是训练数据集上模型输出的平均值。 [【源 ](http://24. https://github.com/slundberg/shap/issues/352)**

基本值

方法“base”为我们提供了每个数据实例的数组列表。每个数组的前 3 个元素包含要素的 SHAP 值,第 4 个值是基值。在我们的例子中,基础值是 1429.31875。

基础值+ SHAP 值=预测值

让我们比较(基础值+ SHAP 值)和预测值的值。

从该数据框中,我们可以很容易地看到,每个实例的要素的基础值+ SHAP 值等于预测值。

我们在讨论解释者模型的时候讨论过这个关系。因此,对于数据帧的每一行,我们可以看到这种关系是如何成立的。

模型解释

汇总图

汇总图旨在从全局层面解释模型。汇总图有两种类型:第一种显示所有要素的所有 SHAP 值,第二种显示所有要素的聚合值(绝对 SHAP 值的平均值)。我们会看到这些情节,然后讨论如何解读。

训练数据的汇总图:

显示特征平均绝对 SHAP 值的汇总图

上图显示了对应于每个要素的 SHAP 值的汇总视图。它显示了特性的重要性。上面的情节是不言自明的,我不认为它需要任何详细的解释;但是,下面的情节解读起来很有趣。

显示每个特征的所有 SHAP 值的汇总图

显示每个特征的所有 SHAP 值的汇总图

显示所有 SHAP 值的汇总图的组成部分:Y 轴上我们可以看到电视、广播、报纸。 X 轴显示 SHAP 值的范围。每个特征对应的个体点就是训练数据集中所有实例的 SHAP 值。右侧的垂直色阶显示特征值是偏高还是偏低。此外,要注意的是,特征以特征的聚合平均绝对 SHAP 值的降序排列。

释义:

哪个功能的影响最大?由于 TV 具有最高的平均绝对 SHAP 值,因此它对模型输出的影响最大。在我们的例子中,模型输出是销售收入。因此,电视对销售收入的影响最大。同样,我们也可以说收音机和报纸。因为收音机一直排在第二位,所以它对销售收入的影响第二大。

****营销渠道的支出对销售收入有何影响?要回答这个问题,看一下分配给每个特征实例的 SHAP 值的颜色。对于电视来说,剧情右手边(正面)的大部分点都是红色的。这意味着更高的电视广告支出会带来更高的 SHAP 价值或增量销售收入。曲线图还显示,对于较低的电视广告支出(蓝色),我们有负的 SHAP 值。负 SHAP 值表明我们在电视广告上的低支出甚至不能产生与其在该渠道上的支出相等的销售收入。同样,我们可以解释其他渠道的影响。报纸对销售收入的影响不显著,这一点从汇总图中可以明显看出。

依赖图(无交互作用):

****我的电视广告和广播广告支出的市场反应如何?要回答这个问题,我们可以画出单个特征与其 SHAP 值之间的关系图。

广告支出和 SHAP 价值之间的关系是市场对广告支出反应的一个很好的指标。从上面的图中可以明显看出,随着我们增加电视广告支出,我们的 SHAP 价值增加,在特定的电视广告支出后,它开始变得停滞不前。

同样,我们可以为广播广告绘制图表,观察到随着广播广告支出的增加,SHAP 值也在增加。因为我们的计划中没有任何停滞点,所以我们需要继续增加广播广告的支出,直到我们到达任何停滞点。

****电视广告的正面影响阈值是多少?为了回答这个问题,我们将使用电视广告和 SHAP 值之间相同的相关性图,并查看 Y 轴上 SHAP 值为零的地方。对应于该点,我们将在 X 轴上得到一个点,该点是电视广告支出的阈值,从该点开始,电视广告开始对销售收入产生积极影响。

****电视广告的正面影响阈值。来源:作者图片

类似地,我们可以计算收音机的阈值。

****广播广告的正面影响阈值。来源:作者图片

交互概要图:

这个图非常重要,它在一个视图中给出了不同频道之间的交互。

****互动总结。来源:作者图片

上面的图显示了交互总结。我们可以看到对角线外的相互作用。我已经用红圈标出了可能的相互作用。我们在这里能看到的唯一重要的互动是电视广告和广播广告之间的互动。

既然我们现在知道电视广告和广播广告之间的互动是显著的,那么另一个问题应该是了解它们互动的本质。

依赖图(有交互作用):

****电视和广播之间互动的本质是什么?为了回答这个问题,我们将再次绘制一个依赖图,但这一次是通过交互作用。这是最有趣的情节之一,也有点难以理解。我们先把这个画出来,然后一步一步地去解读它。

每个点代表对应于特定电视广告支出实例的 SHAP 值。y 轴是 SHAP 价值观,X 轴是电视广告支出。颜色基于每个实例的广播广告支出。

****对于低于 150 英镑和高于 150 英镑的电视广告支出,电视和广播的互动性质正好相反。来源:作者图片

为了理解这个情节,让我们看看 150 到 300 之间的电视广告花费窗口。在电视广告花费窗口中,SHAP 值范围从较高到较低的 SHAP 值(0 到 600)。在该窗口中,我们假设电视广告支出约为 220 美元(显示为带黑边的矩形框),对于该特定支出,我们有不同的 SHAP 值,但较高的 SHAP 值也与较高的广播支出相关联。这意味着该窗口的广播广告支出和电视广告支出之间存在积极的互动关系(电视广告支出为 150-300 英镑)。

另一方面,在 0 到 150 的电视广告花费之间的电视广告花费窗口中存在负交互作用。

到目前为止,我们已经完成了对 SHAP 价值观的全球解读,但现在我们将看到对 SHAP 价值观的一些本地解读。可以使用力图进行局部解释。

局部解释力图:

局部解释是指实例级别的解释。我们的实例级别是周的粒度。因此,如果我们想解释营销渠道对特定一周的影响,那么我们应该使用这个图。

为数据实例强制绘图

在上图中,我们可以看到蓝色条和红色条显示了电视、广播和报纸的 SHAP 值。条形的长度代表 SHAP 值的大小。条形的颜色显示 SHAP 值的方向(正或负)。正的 SHAP 值为红色,负的为蓝色。预测值以黑色粗体显示。基准值也已经在线图上标出。每个渠道的实例价值或广告花费价值也会显示出来。我们可以将这些值与上一节中计算的值进行比较,如下所示。

TV SHAP 值是负的,并且条的长度在负方向上最长,显示-563.6 值。广播和报纸是积极的,但非常小,震级分别为 164.7 和 25.96。由于电视、广播和报纸的 SHAP 值之和为负,所以一旦将其加到基值上,我们就得到了预测值,在这种情况下,预测值低于基值。总之,本周业绩不佳的主要原因是电视广告支出低。

因此,到目前为止,我们已经看到了如何在 SHAP 的帮助下,有效地解释任何黑盒模型。还有许多其他变量,如价格、地区、季节性、趋势、广告库存、市场饱和度等;这可以通过一些特征工程包含在营销数据中。即使有这些额外的变量和更多,我们现在可以使用最准确的模型和 SHAP 来整体地解释它们,并获得大规模的可操作的见解。

结束注释

在这篇文章中,我们主要关注属性和媒体混合建模;然而,XAI 在营销和客户分析的其他领域也有巨大的应用,如流失预测、客户保持和决策支持。例如,在客户流失预测的情况下,XAI(本文中讨论的本地解释)可以帮助我们解释特定客户流失的原因或特定客户流失概率高的原因。根据这一解释,我们可以为特定客户量身定制保留策略。此外,XAI 还通过比较业务领域知识和模型行为来帮助我们调试模型。因此,总而言之,XAI 有可能成为分析行业的游戏规则改变者。

最后,我希望在读完这篇文章后,你对合作博弈理论中的价值以及它在营销分析中的高层次应用有一点了解。

请随时发表评论,提出修改建议,并在评论区分享您的观点。

进一步阅读和参考

  1. https://www.nature.com/articles/s42256-019-0138-9.epdf
  2. https://papers . nips . cc/paper/7062-a-unified-approach-to-interpretation-model-predictions . pdf
  3. 归因模型和合作博弈理论
  4. https://github.com/slundberg/shap
  5. https://www . H2O . ai/blog/the-benefits-of-budget-allocation-with-ai-driven-marketing-mix-models/
  6. 用于科学和医学的可解释人工智能
  7. https://edden-gerber.github.io/shapley-part-1/
  8. https://christophm . github . io/interpretable-ml-book/Shapley . html
  9. https://shap . readthe docs . io/en/latest/examples . html # tree-explainer
  10. *https://github.com/slundberg/shap/issues/352 ***
  11. PyData Tel Aviv Meetup:ML 可解释性的 SHAP 值— Adi Watzman
  12. 瑞典 PyCon:理解 ML 黑箱:使用 SHAP 解释 ML 模型
  13. https://medium . com/@ Gabriel Tseng/interpreting-complex-models-with-shap-values-1c 187 db 6 EC 83
  14. https://towards data science . com/explain-your-model-with-the-shap-values-BC 36 AAC 4 de 3d

可解释的人工智能:机器学习模型的可解释性

原文:https://towardsdatascience.com/explainable-ai-interpretability-of-machine-learning-models-412840d58f40?source=collection_archive---------28-----------------------

使用石灰的模型可解释性

你能信任你的机器学习模型吗?

安迪·凯利在 Unsplash 上的照片

W 我们为什么要盲目信任机器学习模型?如果我们能够更好地了解模型预测并改进我们的决策,这不是很好吗?随着莱姆和 SHAP 等可解释的人工智能技术的出现,这不再是一个挑战。如今,机器学习模型无处不在,比以往任何时候都更成为我们生活的一部分。这些模型本质上通常是一个黑箱,我们很难评估模型的行为。从内置对话代理的智能扬声器到个性化推荐系统,我们每天都在使用它们,但我们了解它们为什么以某种方式运行吗?鉴于他们能够影响我们的决定,我们应该能够信任他们,这是至关重要的。可解释的人工智能系统帮助我们理解这些模型的内部工作原理。

那么,什么是可解释的人工智能呢?

可解释的人工智能可以总结为理解 ML 模型预测的过程。中心思想是使模型尽可能具有可解释性,这在本质上有助于测试其可靠性和特征的因果关系。概括地说,可解释性有两个方面:

  1. 可解释性(为什么会这样?)
  2. 透明度(它是如何工作的?)

通常,可解释的人工智能系统提供对模型输入特征的评估,并识别作为模型驱动力的特征。它给了我们一种控制感,因为我们可以决定是否可以依赖这些模型的预测。例如,如果流感识别模型考虑了体温和咳嗽等比其他症状更重要的特征,我们可能会更信任它。

既然你有了可解释系统的概念,我们如何解释模型预测呢?

做那件事有不同的方法。酸橙就是其中之一。让我们挤压它。

LIME 代表:
L ocal:在被解释的预测的邻域内局部近似,
Iinterpretable:产生的解释是人类可读的,
M 模型不可知的:适用于任何模型,如 SVM、神经网络等Ex 解释:提供模型预测的解释。(模型行为的局部线性解释)

梁杰森Unsplash 上的照片

Lime 可用于获得对模型预测的更多见解,如解释模型为何针对单个观察做出特定决策。在不同型号之间进行选择时,它也非常有用。Lime 背后的中心思想是,它通过干扰不同的特征在被解释的实例附近进行局部解释,而不是在整个模型级别产生解释。这是通过在局部分散的、由噪声引起的数据集上拟合稀疏模型来实现的。这有助于将非线性问题转化为线性问题。然后将模型中系数最大的指标变量作为得分的驱动因素返回。

装置

你可以简单地 pip 安装它或者克隆 Github repo :

pip install limepip install . (Git version)

履行

我们将使用 Lime 来解释 sci-kit learn 中内置的糖尿病数据集上的随机森林回归模型的预测。这篇文章假设你已经掌握了一些 Python 和机器学习的知识。为了简单起见,我们不会涵盖我们通常在模型构建管道中遵循的所有步骤,如可视化和预处理。对于模型构建位,你可以在这里克隆回购

所以,让我们切入正题,看看如何用石灰来解释某个实例。

理解 Lime 预测中的模型行为主要包括两个步骤:

  • 初始化一个解释器
  • 调用解释 _ 实例

解释模型预测的第一步是创建一个解释器。我们可以使用 Lime 表格解释器,它是用于表格数据的主要解释器。Lime 使用局部性缩放和生成新数据,并计算统计数据,如数字数据的平均值和分类数据的频率,因此我们需要将训练数据作为参数传递。

石灰解释器

在第二步中,我们只需要为需要解释的实例调用 explain_instance。如果您希望了解不同的实例,可以使用不同的' i'

最后,我们可以使用 explainer 来显示 Jupyter 笔记本中特定预测的解释。

解释实例的 Lime 输出(图片由作者提供)

当我们使模型变得复杂时,它的可解释性会降低,反之亦然。一个建议是注意模型复杂性和可解释性之间的权衡。

您可以选择将您的解释保存为 HTML 文件,这样更易于共享。

exp.save_to_file(“explanation.html”)

可供选择的事物

  • Eli 5——模型可解释性的另一个库。我用它来处理文本数据,效果很好。你可以在这里阅读更多关于 Eli5 的内容。
  • SHAP——Shapley Additive Explanations 顾名思义,告诉您它是如何以相加的方式获得实例的分数的。SHAP 不仅有适用于任何模型的通用解释器,还有适用于基于树的模型的树解释器。理论上保证一致性,比石灰慢。此外,探索所有可能的特征组合的计算需求在 SHAP 呈指数增长。

结论

Lime 提供了人类可读的解释,并且是分析每个特征的贡献的快速方法,因此有助于更好地了解机器学习模型行为。一旦我们理解了模型以某种方式预测的原因,我们就可以与模型建立信任,这对于与机器学习的交互来说是至关重要的。在这篇文章中,我们使用了一个随机森林回归模型来解释它对一个特定实例的预测。

有趣的是,Lime 还支持图像、文本数据和分类问题的解释器。您可以在更复杂的模型(如 Xgboost & LightGBM)中进一步探索 Lime 解释,并比较预测。点击这里阅读更多关于酸橙的内容。此外,这里有一个有趣的阅读关于人工智能透明度和可解释性的不同工具。

我很想在下面的评论中听到你对石灰、机器学习和可解释的人工智能的想法。如果你觉得这很有用,并且知道任何你认为会从中受益的人,请随时发送给他们。

可解释的人工智能需要更多的人

原文:https://towardsdatascience.com/explainable-ai-needs-more-humans-64e4c239e412?source=collection_archive---------34-----------------------

资料来源:Petr Kratochvil

在 LIME、Shap 等技术术语中,你可能会忘记目标是向一个人解释一些事情。

对于许多人来说,可解释或可解释的 AI 或 ML 意味着在一套旧算法的基础上叠加一套新算法,以更好地理解旧算法的输出。因此,围绕可解释 ML 的讨论有时会围绕着 LIME 或 Shapley 值,而不会涉及到任何可能会使用模型输出的人。

在创建新算法的热潮中,模型用户想知道算法如何做出决策的问题被遗忘了。我们甚至没有开始讨论最终用户是否是一种人,或者实际上是一群不同类型的人,他们可能有不同的需求。也就是说,对理解一组新算法的关注分散了对模型用户需求的理解。

这可能是有问题的,因为如果用户不理解问题域内潜在变量的意义或重要性,简单地知道哪些变量在特定方向上导致 x 单位效应直观上不是有用的解释。

回到不同类型用户的问题上,这就引出了一个问题:我们是否应该讨论用一种解释来制作一个模型,或者,我们是否真的需要多个模型来处理同一件事情,因为不同的用户群需要不同类型的解释?例如,当思考医疗 AI 时,对医疗专业人员的解释有可能被患者理解吗?或者,一个被病人理解的解释可能对医学专业人员有用吗?例如,它会有足够的细节吗?

Leo Breiman 的罗生门集(一组不同的模型,有不同的解释)表明,在某些时候,制作这种情景模型至少是可行的。用于患者解释和医生解释的模型可能不会给出完全相同的结果,但是如果它们给出大致相同的结果,则可以提供所需的两个层次的解释。

当然,并不是简单的教育水平决定了什么样的模型解释是合适的。冒着过度延伸医学类比的风险,与试图优化慢性健康疾病治疗的医生相比,急诊室医生可能需要更快理解的解释。

一个数据科学家不可能知道并应用所有关于 UI/ UX 设计的知识,但是从这个领域考虑一个有用的概念是“用户角色”。这基本上是对将要使用你的产品的人的一个虚构的描述。以这种方式理解产品的用户应该是构建可解释模型的首要任务之一,因为要解释你的模型的人是一个特定的人,如果他们要理解你的模型及其输出,就需要理解他们的能力、约束和愿望。

罗伯特·德格拉夫的书《管理你的数据科学项目》》已经通过出版社出版。

在 Twitter 上关注罗伯特

可解释人工智能:健康数据用例

原文:https://towardsdatascience.com/explainable-ai-use-case-1-bc2abd7b197a?source=collection_archive---------54-----------------------

图片来源:Research @ Infosys Labs (Infosys 博客)

免责声明:为了更好地理解这篇文章的背景,阅读我之前关于可解释人工智能的文章会很有帮助。如果您以前用 Python 编写过逻辑回归代码,这也会很有帮助,因为我在代码中提到过它。

在我之前关于可解释人工智能的文章中,我提到过高级监督学习算法,如逻辑回归和神经网络(及其变体,如卷积神经网络和递归神经网络),很难解释。特别是神经网络,深度学习的一部分,可以产生惊人的准确性,但是程序员和用户对算法如何产生分类知之甚少。部分原因是神经网络和逻辑回归努力创建与其预测的直接关系,但这是有代价的:可解释性

我之前提到过,理解分类问题的另一种方式是使用贝叶斯定理来推断我们的两个假设——H1(类别标签 1)和 H2(类别标签 0)。作为参考,下面是等式:

两个假设的贝叶斯推断

在今天的用例中,我将把这个推理框架应用于加州大学欧文分校的机器学习知识库中的肝细胞癌数据集。该数据集包括在马德里大学接受治疗的 165 名患者,以及每个患者的近 50 个个体特征/测量值。类别标签表明患者是否从癌症中存活(1)或是否不幸不能存活(0)。

我们开始吧!首先,我们需要导入相关的包。随着代码的进展,我们将导入更多的库。由于数据集和代码较长,这是我的 GitHub repo,完整代码。我将在这里张贴片段。

*# First, we read in the file and import packages
import numpy as np
import pandas as pd
from sklearn.linear_model import LogisticRegression
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.metrics import confusion_matrix as cmatrix hcc_data = pd.read_csv('/Users/rabeya/Carcinoma_Classification_data/hcc.csv')*

加载到 HCC 数据集中。

接下来,在我们加载数据后,我们使用 Miriam Santos 在 HCC 数据集上的原始论文中的算法,利用“HEOM-distance”填充所有标有“?”的缺失值。这种“HEOM-distance”算法采用每个患者记录,并根据与其“最接近”的患者记录来填充缺失值;正如你所猜测的,“近”的概念是基于超远距离的。因为这个算法有很多行,所以我把你引向我的 Github repo。填充的数据集被命名为 new_df

一旦我填充完缺失的值,我需要平衡标签。标签不平衡是机器学习中的一个大问题,由于收集中的偏差或缺失值,标签 1 与标签 2 的记录比率可能不是 50%:50%。事实上,在 HCC 数据集中,患者生命记录与患者死亡记录的比率是 62%:38%,因此我们需要平衡这些记录。一种方法被称为“人工过采样”,我们为更小的类人工创建新的数据记录,这些记录与原始记录接近。这种过采样方法叫做“SMOTE”,代码如下。

***from** **imblearn.over_sampling** **import** SMOTE
sme = SMOTE(random_state=10)

*#features*
features = new_df.columns
*# transform the dataset*
X_matrix = new_df[features[:-1]].values
y_matrix = new_df[features[-1]].values
X_trans, y_trans = sme.fit_resample(X_matrix, y_matrix)transformed_df = pd.DataFrame(X_trans, columns=features[:-1])
transformed_df['Class'] = y_trans*

现在我们已经转换了数据集并平衡了类(有 102 个病人-生命和病人-死亡样本),我们准备好构建我们的生成分类器了!(我们的平衡 HCC 数据集在接下来的部分中被称为转换 _df* 。)*

在创建任何模型之前,我们减少了数据集中的要素数量。最初的一个有 49 个单独的特征/对于每个病人,那是太多要考虑的了!因此,相反,我们使用统计学中所谓的 互信息 将它削减到“最佳”10 个特征。

首先,我将逻辑回归作为测试模型案例应用于训练和测试数据。我将训练集和测试集分成:65%给训练集,35%给测试集。这不是标准的,但是我这样做了,因为我们的整个集合只有 200 条记录,所以必须节约使用数据。

***from** **sklearn.feature_selection** **import** SelectKBest
**from** **sklearn.feature_selection** **import** mutual_info_classifK = 10
selector = SelectKBest(mutual_info_classif, k=K)
X = transformed_df.iloc[:, :-1]
y = transformed_df.iloc[:, -1]
X_reduced = selector.fit_transform(X,y)*# use logistic regression as a test case model*
logreg = LogisticRegression(C=1.5, max_iter=1000, solver='lbfgs')
logreg.fit(X_train, y_train)*

对看不见的数据使用逻辑回归,预测准确度是:真阳性-36%;真阴性——30%;假阳性——22%;假阴性——12%。逻辑回归在预测如此小的数据集方面做得不太好,22%的 FPR 用于未来预测是危险的,因为五分之一的未来新 HCC 患者将被预测存活,而他们的实际存活机会将很低。这种糟糕的预测能力并不令人惊讶,因为我们只给出了大约 130 条记录供模型训练。

这里有许多解决方案:1)收集更多的数据并将其转储,但 HCC 的数据来之不易。2)使用更多的参数/权重,例如用于小数据的神经网络模型。但是正如我们之前讨论的,神经网络很难解释。我采取的解决方案是完全改变预测的视角,并使用生成模型来代替。正如我提到的,生成模型可以帮助我们计算可能的数据在属于不同类别的假设下如何指向,这种概率视角对于小数据尤其有用。

密度估计模型。图片来源:machinelearningmastery.com

最后,我们可以创建我们的生成模型。第一种类型称为 高斯混合模型 (GMM),其中一堆单独的高斯模型(称为“组件”)被捆绑在一起,以创建一个大的生成模型。我们根据患者是死于癌症还是幸存来分离我们的训练数据,然后拟合模型。为了适应模型,我们不能只选择一个#的组件。相反,我训练了 64 个模型(计算机可以比你想象的更快地执行这个操作),并使用期望最大化* (EM)算法优化了这些模型。EM 算法已有几十年的历史,但经常用于寻找高斯混合。*

高斯混合(EM)在工作。图片来源:维基百科

然后,我根据一种叫做 阿凯克信息标准 (AIC) 的统计方法,挑选出了最佳数字 22 和 21。它基本上比较了密度模型的精确程度和复杂程度,并试图在模型过拟合和欠拟合之间寻找平衡。

***from** **sklearn.mixture** **import** GaussianMixture **as** GMM
M = 64
*# so M models to train*
n_components = np.arange(2, M, 1)

life_models = [GMM(n, covariance_type='full', random_state=5) **for** n **in** n_components]death_models = [GMM(n, covariance_type='full', random_state=7) **for** n **in** n_components]life_aics = [model.fit(X_train_life).aic(X_train_life) **for** model **in** life_models]death_aics = [model.fit(X_train_death).aic(X_train_death) **for** model **in** death_models]gmm_life = GMM(22, covariance_type='tied',tol=1e-4, random_state=5)
gmm_life.fit(X_train_life)gmm_death = GMM(21, covariance_type='tied',tol=1e-4, random_state=7)
gmm_death.fit(X_train_death)*

对看不见的数据使用高斯混合模型的准确度是:35%真阴性、12%假阳性、15%假阴性和 37%真阳性。与逻辑回归相比,假阳性率有了多大的提高!我们实际上可以通过使用类固醇上的高斯混合模型的变体在假阳性率上做得更好: 核密度估计 (KDE) 。这听起来肯定像是人工智能科幻小说中的东西,但它的工作原理是一样的!

***from** **sklearn.neighbors** **import** KernelDensitykde_life = KernelDensity(bandwidth=0.1).fit(X_train_life)
kde_death = KernelDensity(bandwidth=0.1).fit(X_train_death)

log_density_life = kde_life.score_samples(X_test)
log_density_death = kde_death.score_samples(X_test)*

当我们对看不见的数据使用 KDE 方法时,我们的预测精度是:44%真阴性,8%假阳性,2%假阴性和 46%真阳性。只在 130 张唱片上接受过训练的 KDE 在预测能力上做得非常出色。(顺便说一句,GMM 和 KDE 需要记住的是,让密度模型过度适应训练数据。我将在以后的文章中探讨这个问题。)

好的,这一切都很好,但是我们如何解释密度模型呢?可解释性很重要。好吧,如果来自测试集的一个新的数据点病人 Y 进来,机器可以这样解释它的预测:“因为类别标签(1 =生存,0 =死亡)是平衡的,我的先验比率等于 1。因此,我的决定只基于我根据之前的 130 条训练记录计算出的密度似然模型。根据我的生存可能性模型,如果我假设 Y 属于 1 类,它的对数可能性是 p1。根据我的死亡可能性模型,如果我假设 Y 属于 0 类,它的对数可能性是 p2。因为我计算出,比如说,(p1-p2) > 0,这意味着病人 Y 的数据记录被生成的概率——假设它属于类 1——大于类 0。因此,我根据他们的 10 个‘最佳’计算特征对癌症存活患者 Y 进行分类。”这是代码的实际样子:

*delta = np.subtract(log_density_life, log_density_death)
# this is the difference in likelihoods under the hypothesis  assumptions (H1: label=1, H2: label=0)kde_predictions = np.asarray([1 **if** x > 0 **else** 0 **for** x **in** delta])*

当然,机器不能和我们说话,但我相信这就是我们如何理解机器学习模型的决策。看看这比使用神经网络或逻辑回归更有解释力?

但不仅仅如此。假设我们有一个更大的像这样的不平衡的数据集。我们可以在贝叶斯推理公式的先验比中考虑生存和死亡标签的比例。如果我们计算 sam 似然密度模型,并发现对新测试点的预测是错误的,我们可以潜在地* 修改模型。要么我们可以修改我们的类标签的先验概率,要么我们可以修改似然模型。怎么会?通过在*可能性模型本身上使用贝叶斯框架。例如,这可以用概率编程语言如 PYMC3 来动态完成。**

我希望这些用于医疗保健数据的结果提供了一种途径,不仅可以将密度估计用于聚类客户模式,还可以像我们在这个项目中所做的那样,将它们用于预测能力。我将指出,实验者没有考虑的一个特征是种族/民族。我可以理解,因为数据集是在西班牙收集的,所以种族会相当单一。但是在数据收集中忽略种族,不批判性地思考其他因素,如社会经济阶层和获得医疗保健的机会,是一件非常冒险和危险的事情。但是希望我能找到更多的 HCC 数据集(或类似的),这些数据集也考虑了这些因素。

一会儿见!😃

可解释的人工智能

原文:https://towardsdatascience.com/explainable-artificial-intelligence-14944563cc79?source=collection_archive---------8-----------------------

发现机器学习和人工智能的最大趋势之一

图片来自 Unsplash

可讲解 AI 是机器学习领域最热门的话题之一。机器学习模型通常被认为是不可能解释的黑盒。最终,这些模型被需要信任它们、理解它们所犯的错误以及它们的预测背后的推理的人类所使用。在这篇文章中,我们将探索什么是可解释的人工智能,为什么它很重要,并看看我们如何使用真实数据实现它的一些例子。

在我们开始之前,这里有一些额外的资源可以让你的机器学习生涯突飞猛进

*Awesome Machine Learning Resources:**- For* ***learning resources*** *go to* [***How to Learn Machine Learning***](https://howtolearnmachinelearning.com/books/machine-learning-books/)*! 
- For* ***professional******resources*** *(jobs, events, skill tests) go to* [***AIgents.co — A career community for Data Scientists & Machine Learning Engineers***](https://aigents.co/)***.***

[## 订阅我的专属列表!

订阅我的专属列表!获得所有你喜欢的新鲜文章<3! By signing up, you will create a Medium…

z-ai.medium.com](https://z-ai.medium.com/subscribe)

好了,让我们开始吧😋

那么,什么是可解释的人工智能?

机器学习工作流的当前结构,从培训到在生产环境中的部署,大致如下:

图 1: ML 工作流程。(自制图片。来自平面图标的图标)

这个图像的解释如下:我们通过特定的学习过程,使用一些数据来训练一个模型。这个学习过程会产生一个已学习的函数,然后可以输入该函数,并在特定的界面中输出预测,这是最终用户看到的并与之交互的内容。

在前面的例子中,我们使用住房数据来训练一个模型,它最终是一个人工神经网络,但也可以是任何其他东西:从决策树,到 SVM 或 boosting 模型。

在学习了这个函数之后,我们可以给它输入新的输入,在我们的例子中是新房子,它返回关于输入房子价格的预测。最后,一个用户,在这个例子中是一个真实的公司的所有者,看到输出并做出决定或开始某些行动。

正如我们从用户的疑虑中看到的,这里的问题是预测没有正当理由

在部署任何机器学习模型之前,我们或多或少会意识到它的错误(因为这是我们的学习过程试图减少的),但这只能给我们损失规模中的某个数字,或者量化数据中的标签和部署之前预测的标签之间的某个数字距离。我们能相信这个错误高于一切吗?

我们的模型可以有很低的误差,仍然有一定的偏差,或者时不时做出耐人寻味的预测。从商业角度来看,理解这些奇怪的预测可能是一个好主意,因为它们可能会告诉我们一些我们不知道的事情。它们也可能意味着模型有问题,需要更换。

即使预测没有实际问题,我们也希望理解为什么模型会说出它所说的话。你可以在测试数据上得到很好的结果,并得到相当准确的预测,但有时这还不够。

所有这些之前的问题是让可解释的人工智能如此受欢迎的原因。我们将通过查看它在前面的例子中的位置来了解它是什么。

图 2:可解释的人工智能融入到工作流程中。(自制图片。来自平面图标的图标

让我们看看这里发生了什么。这一次,我们使用了一个新的不同的学习过程来学习一个与可解释模型相关的函数。这意味着这个函数不仅能给我们一个预测,还能解释为什么它会做出这个预测。新的解释界面显示了额外的信息,可以让我们的用户对为什么做出这样的预测有所了解。在本文的后面,我们将通过一些真实的例子来看看这是如何实现的。

为什么可解释的人工智能如此重要?

当我们用我们应用变换的大量参数训练 ai 时,我们最终将预处理和模型构建的整个过程变成了非常难以解释的黑盒模型。也许构建模型的数据科学家或机器学习工程师知道数据经历的转换和操作,但对于最终用户来说,这些完全是神奇的。

图 3:被视为黑盒的机器学习模型。(自制图片。来自平面图标的图标)

他只是给模型一个输入,然后得到一个他应该相信的预测。在不知道引擎盖下发生了什么,也没有任何进一步解释的情况下,这可能吗?

自从人工智能的主流采用之路开始以来,用黑盒方法训练机器学习模型是没问题的——只要它们给我们带来好的结果,如果我们无法解释它们也没关系。现在,焦点正慢慢转向这些模型的透明性和可解释性。我们想知道他们为什么会得出他们输出的预测。这种转变的到来有多种原因:

  1. 理解当机器学习模型做出预测时会发生什么可以帮助加速这些系统的广泛采用。新技术总是需要时间变得成熟,但如果它们被理解了,肯定会有所帮助。
  2. 它让用户变得越来越适应这项技术,并去除了似乎包围着人工智能的神奇面纱。让用户信任他们正在使用的系统是最重要的。
  3. 对于保险或银行等行业,有时会有公司层面甚至的法律限制,这使得这些公司使用的模型必须是可解释的。
  4. 在其他一些关键领域比如医学,人工智能可以产生如此巨大的影响,并令人惊讶地改善我们的生活质量,最基本的是,所使用的模型可以毫无疑问地被信任。拥有一个有时会输出奇怪预测的网飞推荐系统可能不会产生很大的影响,但在医疗诊断的情况下,不寻常的预测可能是致命的。提供比预测本身更多的信息允许用户决定他们是否信任预测。
  5. 可解释的模型可以帮助他们的用户更好地利用这种模型给出的输出,使他们在商业/研究或决策中有更大的影响力。我们应该永远记住,像任何其他技术一样,人工智能的目标是提高我们的生活质量,因此我们从中获得的好处越多越好。

原因肯定不止这 5 个;可解释的人工智能的主题是广泛而多样的,但在看到为什么拥有可解释的机器学习模型是如此令人向往之后,让我们看看通常会发生什么。

一般来说,正常的心态是上面提到的那种——我们想要结果,所以我们的机器学习模型必须有很好的性能。一些机器学习模型通常比其他模型表现更好,同时也具有不同的可解释性。

随机森林模型通常优于决策树,但前者比后者更难解释。下图显示了这种趋势:随着绩效的提高,可解释性趋于降低。我们想要的是从 X 到 O,也就是说,在不太损害模型性能的情况下增加可解释性。

图 4:不同机器学习模型的可解释性与性能。实际和期望的情况。(自制图片)

最后,让我们来看一些例子,看看我们如何构建一个可解释的人工智能系统,以及我们将从中获得的信息。

可解释人工智能的例子

如前所述,机器学习模型的可解释性是其固有的。虽然人工神经网络很难解释,但决策树允许简单的解释。然而,神经网络要强大得多。

如果我们想要实现可解释的人工智能,我们可以做几件事:

  1. 使用一个可解释的模型,比如决策树。
  2. 在一个更难解释的模型上增加一层可解释性,就像一个随机森林。

让我们看一个使用住房数据集的例子,继续上面例子的主题。数据集将如下:
房价:来自 Kaggle 的高级回归技术数据集。

其描述如下:

有 79 个解释变量描述了(几乎)爱荷华州埃姆斯住宅的每个方面,这个比赛挑战你预测每个家庭的最终价格。

由于这篇文章的目标是详细说明如何应用可解释的人工智能,这些数据已经大大简化了,我们只保留了数字变量,进行了非常简单的预处理步骤,模型使用了默认的超参数,没有进一步优化。

1)使用决策树

正如我们之前提到的,决策树是最容易解释机器学习模型的一种。实现这一点的方法非常简单,如果我们知道决策树是如何构建的,那就更好理解了:通过递归将数据分割成越来越小的组,这些组最终到达我们的 leave 节点。

如果你不确定决策树是如何工作的,你可以在这里了解它们

当使用决策树进行预测时,从根节点开始,直到到达离开节点,(并经过必要的中间节点),在每个节点评估数据的某个变量,我们的数据样本根据它为该变量提供的值向左或向右移动。

下图描述了一个这样的例子,在一些住房数据上构建的树只使用了两个变量。首先,它查看变量 LSTAT 的值,然后查看 RM,的值,以在最终的 leave 节点中结束。

图 5:输入数据样本如何通过只有两个特征的决策树的例子。(自制数字)

注: 这是为另一个项目构建的随机树,不是为我们的实际数据。

我们如何用树来解释一个预测?简单,我们只需跟随样本在树中的路径,这使得样本最终到达叶子节点。

对于我们的实际数据集,我们构建了深度为 8 的决策树,并为其提供了一个预测。它通过树的路径,直到它到达其对应的离开节点(在那里进行预测)是下面的:

图 6:随机输入测试数据的决策树路径

类似这样的事情是数据科学家可以从决策树模型中得到的,以及预测本身,在本例中是 212542$

现在,如果我们知道每个变量的含义,并且我们与一个领域专家合作,我们就可以创造出更强大的东西,因为从这些信息中可能可以提取更深刻的见解。

从前面的路径中,我们可以看到这是一个高质量的房子(在总质量变量的 6.5 和 7.5 之间),地下室、地面和第一层的尺寸限制(从地下室- TotalBsmtSF ,第一层- 1stFlrSF ,地面面积-GrLivArea)并且它还有一个大车库,它在 1978 年后被改造(从

有了所有这些知识,领域专家可以为每个变量构建一个文本,并将它们连接在一起,就像这样:

**“高品质建筑,地面居住面积小于 1941 平方英尺,地下室小于 1469 平方英尺,车库大于 407 平方英尺,一楼大于 767 平方英尺。1978 年后,它也被改造过。因此,其预测价格为 212542 美元。

从广义上讲,这将是支持价格数字预测的原因。关于进一步的信息,这些变量的实际值以及其他值可以包括在文本中。

有了所有这些信息,我们可以给用户两件事:首先,使用路径,我们可以给他一个模型给出的预测背后的一般推理。此外,可以精心制作一个比上面更复杂的解释,因为在上面的例子中,我们只是列出了从变量及其值中获得的原始信息,然而,可以构建一个涉及业务推理的连贯和更精细的文本。

想象一个例子,我们有前面的路径,还有像邮政编码这样的变量。我们可以编写一个类似于的文本:

"高质量的改建房屋,位于一个好社区,有车库和三层楼,这些房屋的售价略高于正常市场价格。因此,预测价格为 212542 美元"**

使用这种方法,我们还可以包括不在决策树路径中但我们认为在单独文本中相关的其他变量的值,例如供暖类型、空调可用性和电气系统。

酷吧?最后,让我们来看一个更复杂的解释方法。

2)增加了一层可解释性:Shapley 值

然而,决策树有一个关键的弱点:它们在可解释性上获得的东西在预测能力上失去了。它们是简单的模型,通常不会得到最好的结果。

如果我们使用相同的数据训练一个随机森林模型,并比较误差(表示绝对概率误差或 MAPE),我们会看到以下结果:

图 7:两种模型的 MAPE 比较。(自制图像,图标来自 Flaticon )

决策树的误差几乎是随机森林模型的两倍。通过比较两个模型的预测价格和房屋的实际价格,我们也可以看到这一点:决策树预测价格为 212542\(* ,我们的随机森林预测价格为 *206049\) ,房屋的实际价格为 208500$ 。随机森林比孤独的树做得更好。

那么,我们如何解释一个更复杂的模型,比如随机森林?我们所做的是使用我们的复杂模型进行预测,然后在其上添加一层可解释性,如下图所示:

图 8:在黑盒模型上添加一个可解释性层(自制的图,来自 Flaticon 的图标)

从相同的输入中,我们使用一个复杂的模型生成一个准确的预测,并使用可解释层或可解释层生成一个好的解释。这一层是什么?这可能是各种各样的事情:我们可以使用决策树,就像前面的例子一样来生成解释,然而,这将是最简单的事情,我们正在寻找更强大和准确的东西。

在这个例子中,我们将使用 Shapley 值。 Shapley 值来自博弈论,我不会过多讨论它们是如何计算的。基本上,他们所做的是反映每个参与者(在我们的机器学习案例中的特征)在特定合作游戏中的贡献的重要性(在我们的机器学习案例中进行预测)。

我们可以为特征集中的所有变量计算这些值,并为每个预测获取与该特定预测最相关的特征。

在 Python 中, SHAP允许我们轻松计算特定输入的更复杂版本的 Shapley 值。为此,我们只需使用我们用来训练模型的相同训练数据来训练 Shapley 解释器,并指定我们使用哪种模型,在我们的情况下是随机森林。让我们看看解释器对我们用于决策树的相同观察的输出:

图 Shapley 值的输出

我们如何解读这个数字?非常容易。这些变量与具有特定大小和两种颜色之一的条形相关联:粉色或蓝色。在这两种颜色相遇的地方,我们可以通过随机森林模型看到房子的预测价格( 206049$ 如前所述)。最接近粉色和蓝色交汇处的变量是对特定预测影响最大的变量。

粉红色的变量及其相应的值有助于提高房价,而蓝色的变量及其相应的值有助于降低房价。正如我们在这里看到的,对房价上涨贡献最大的变量/值串联是值为 7 的总体相等变量。

在这种情况下,房子的质量和建造年份(2003 年)是房子最相关的积极特征。地下室和一楼面积的小值是最相关的负面特征。所有这些变量及其值共同证明了预测的正确性。

可以以纯文本的形式提取这些信息,并且可以采取一些方法来进行清晰的解释,就像在决策树示例中一样,但是这一次使用了一个更准确和复杂的模型,因为树中的解释受到我们获得的叶子节点和它们中结束的路径的限制。

注: 在这两种情况下(决策树和 Shapley 值),解释都是针对数据的一个单独的寄存器。我们没有使用像模型的整体特征重要性这样的东西,这也为我们的模型如何表现提供了非常有价值的见解,但这是模型特定的,并没有下降到个体预测水平。

还有其他增加可解释性层的技术,如 排列重要性或石灰, ,但它们不会在这篇文章中讨论,因为我认为我们的目标是强调可解释人工智能的重要性,详细说明它是什么,并展示一些例子,这已经完成了。

* [## 订阅我的专属列表!

订阅我的专属列表!获取你喜欢的所有新鲜文章<3! By signing up, you will create a Medium…

z-ai.medium.com](https://z-ai.medium.com/subscribe)

Conclusion

我们已经看到了什么是可解释的人工智能,为什么它如此重要,并且看到了我们可以实现更接近我们正在寻找的东西的两种方法。

如果你想要这篇文章的任何部分的代码,请随时留下评论,我会发给你。

就这些,我希望你喜欢这个帖子。请随时在 Twitter 上关注我。还有,你可以在这里 看看我关于数据科学、统计学、机器学习的帖子。好好读!

有关机器学习和数据科学的更多资源,请查看以下资源库: 如何学习机器学习 !有关职业资源(工作、事件、技能测试),请访问 AIgents.co——一个数据科学家&机器学习工程师 的职业社区。

干杯!*

可解释的、数据高效的文本分类

原文:https://towardsdatascience.com/explainable-data-efficient-text-classification-888cc7a1af05?source=collection_archive---------29-----------------------

以正确的方式改善 ULMFiT

在这篇文章中你可以找到:

  • 介绍迁移学习的核心思想、历史和应用,
  • 对自然语言处理的近期发展的回顾,包括 ULMFiT 算法(利用基于递归神经网络的预训练语言模型),
  • 一种新颖的网络架构——对 ULMFiT 的修改——能够实现更准确的文本分类,尤其是在小数据集上训练的情况下;在几个场景中评估其性能;它在自动网络欺凌检测比赛中获得第三名(pole val 2019 Task 6–1)
  • 一种训练极轻量级组的方法(<100KB) classifiers meant to process documents from similar domains, as well as a way to deploy them efficiently,
  • 注意力可视化的交互式演示,不仅显示文本的哪些部分对分类器重要,还显示它如何理解它们。

行动中的注意力可视化——阅读下面的工作原理, 自己试试

目录:

实用深度学习关键——迁移学习

实用深度学习的关键——迁移学习

在计算机视觉中

将深度神经网络应用于非结构化数据,最常见的是图像,可以产生令人印象深刻的结果。撇开学术论文和原型不谈,大众汽车通过分析视频反馈实时了解周围环境。在某些地区,汽车完全自主驾驶。在这些情况下,以及许多其他情况下,深度学习被用于将图像的单个像素的原始数据转换为对场景的某种理解。

图 1:实时对象检测的开源示例:神经网络返回所有检测到的对象的边界框和类。来源: YOLO v3 物体探测

虽然理解图像是人类的天性,但对计算机来说却绝非易事。教一台机器如何看待世界需要大量的计算和大量的数据,有点类似于人类看待世界的方式。实际上,这也需要花费大量的时间和金钱。上面的例子?它使用超过 1400 万张人工注释的图像进行训练( ImageNet ,然后是 COCO )。然而,在各种各样的领域都有许多实际的应用——无数的工业领域,许多被非政府组织使用——例如通过照片来识别鲸鱼个体或者使用卫星图像来探测森林砍伐。

在没有庞大数据集和预算的情况下使用深度学习的关键?重新规划已经训练好的模型及其包含的知识;更正式的说法:迁移学习。计算机视觉经历了一场革命,在 2010 年代中期,使用 ImageNet 上预先训练好的模型用于各种应用成为了标准做法

预训练任务很简单:对于每幅图像,通过选择(20,000+)预定义类别中的一个来决定其中的主要对象。为了解决这个问题,神经网络学习如何从原始像素数据中提取有意义的图像表示。首先,卷积层学会检测简单的特征,例如边缘和拐角。下一层使用这些信息来识别更复杂的形状——当观察鸟类图像时,可能是翅膀和喙。最终层可以识别特定类型的对象,例如各种类型的鸟、汽车或飞机。

我们可以使用预先训练的网络来计算图像的表示,并训练我们的模型使用这些表示。或者,我们可以删除预训练网络的最后几层,用新的、随机初始化的层来替换它们,并训练整个网络来执行手头的任务。这样就有可能使用只有几千或几百张标记图像的数据集快速构建原型系统。例如,400 个训练样本足以在视网膜的 OCT 图像中出现的 4 类疾病分类中实现 85%的准确性——这是一种与原始网络训练的动物和车辆图片非常不同的图像。通过一些额外的努力,例如对于分类任务,可以进一步降低数据要求。更极端地说,在看不到图像的任何例子的情况下将图像分类也是可能的(零镜头学习,例如设计)。

在自然语言处理中

自然语言的自动处理是一个具有挑战性的问题。在这个领域中执行各种任务的系统已经存在了几十年,但是直到最近它们还主要是基于规则的。这些系统可以很好地执行(例如:以 97.2%的准确率将问题分为 50 类),并且它们的行为可以很容易地被理解和调试。不幸的是,因为它们是基于人工开发的规则和知识库系统,所以它们的开发是劳动密集型的。随着预期功能的增加,它们的复杂性会大大增加,因此它们通常只适用于定义明确、范围狭窄的任务。此外,打字错误或语法错误可能会干扰它们的运行。

开发统计的、数据驱动的 NLP 系统的动机是明确的。在许多情况下,收集与手头任务相关的数据集比开发一套庞大的规则更容易。这意味着更低的成本和更短的系统开发时间。此外,随着时间的推移,随着收集的数据越来越多,它为系统提供了更好的性能。此外,许多文本数据可以免费获得——以书籍、在线出版物等形式。——可以说包含了人类的大部分知识。直接利用它作为知识库,可以构建新的、非常强大的系统。

建立一个能够完全理解自然语言的系统是一个极其困难的、尚未解决的问题。解决语言歧义通常需要语境、习语知识、发现讽刺,甚至常识和人类“常识”(见威诺格拉图式挑战)。尽管如此,这个问题还是很值得研究的——因为它有直接的应用价值,也有可能更接近理解一般智力。

NLP 中的第一个统计方法开始时要简单得多。他们将文本文档表示为它们所包含的单词数(这种表示称为单词包)。这种方法的改进建议使用频率而不是计数,并且频率通常与给定单词的常见程度相关( TF-IDF )。这些方法完全不考虑词序,所以只使用一小部分可用信息。使用 n-grams 个单词的序列)代替单个单词,进行类似的进一步处理,是一种合并词序信息的可行方法。不幸的是,使用高 n 值是不可行的,因为可能的单词组合的数量随着 n 呈指数增长。

上述基于计数的表示在很长一段时间内一直是最先进的统计解决方案的基础。由于其简单性和计算效率,对于某些应用来说,它们仍然是重要的基线。例如,对于主题分类和情感分析,尝试基于词袋或双字母特征的朴素贝叶斯或 SVM 分类器,可能是一个好主意(在下面描述的完整 IMDB 任务的情况下,它实现了 90–91%的准确率,相比之下,ULMFiT 的准确率为 95%,但速度要快几个数量级)。

第一波变化发生在 2013 年,随着 Word2Vec 的出现。这一类别的模型为其词汇表中的每个单词生成一个数字表示(称为“单词向量”,或“嵌入”)。它们是作为浅层神经网络构建的,根据来自大量免费文本的短序列进行训练。每个单词的表示都基于其通常的上下文,并且已经捕获了大量的语言语义。众所周知,词语之间的类比对应于其向量之间的算术关系,例如king — man + woman ~= queen

图 2:单词嵌入空间中的语义关系。标有“女王”、“女人”、“男人”和“国王”的点的坐标是单词向量/嵌入。来源:走向理解线性词语类比博文

词汇表中的每个单词都被表示为一个固定长度的向量(例如 300 个数字)——嵌入空间中一个点的坐标集。由于词汇表的大小(唯一单词的数量)通常高几个数量级,所以表示是分布式的——单词和概念由特定的坐标组合表示,而不是向量的单个元素。

作为一个完整系统的一部分,单词嵌入将用于对文档中的每个单词进行编码,产生一个可变长度的向量序列(长度对应于单词的数量)。开发了许多使用这些表示的算法——从平均所有的单词向量并根据结果训练 SVM 分类器,到通过递归或卷积网络传递序列。

单词嵌入有一个中心问题。当用于对文档中的单词进行编码时,它们会单独对每个单词进行编码——忽略上下文。代表的向量在下式中是相同的:

  • 一棵树的根
  • 二的平方根号

对于 NLP 的大多数实际应用来说,具有一个以上可能含义的单词是一个问题。对于某些应用,例如情感分析,也必须解决句法歧义。这可以从简单的问题(一个短语被否定了吗?)变得非常复杂,有时甚至是不可能的(这个短语是讽刺性的吗?).

让我们考虑一个实际的情况。我们希望建立一个系统,通过将关于品牌的推文分类为正面或负面,来评估公众对品牌的情绪,然后计算正面的分数。我们需要训练数据——我们可能会下载一些关于各种品牌的相关推文,然后手动给它们贴上标签(或者众包这项任务)。

如果我们使用单词嵌入并将编码文本输入神经网络,我们将能够训练它执行分类任务——区分积极信息和消极信息。但是因为单词嵌入并不捕捉上下文,只是单个的单词,我们的网络必须同时学习语言的整个结构,甚至更高层次的概念,如讽刺或挖苦!所有这些都是基于我们珍贵的,人工标注的数据。这显然是低效的。

语境化的词汇表征

为了帮助神经网络“理解”语言的结构,研究人员开发了几种网络架构和训练算法,它们计算单词的上下文化表示。与基于单词嵌入的方法相比,关键的区别是:这种对上下文的理解是在来自给定语言(或多种语言)的未标记的、可自由获得的文本上训练的。数据通常来自维基百科、T2、公共领域的书籍,或者只是从互联网上搜集。

在 2018-2019 年,他们开始了一场类似于几年前计算机视觉的“革命”。深度神经网络通常用于计算丰富的、上下文化的文本表示——与上下文无关的单词向量相反。这些表示然后可以被另一个神经网络使用(例如 ELMO )。更常见的是,预训练网络的最后一层被一组不同的层取代,这些层是为手头的任务设计的(通常称为下游任务)。新层的权重被随机初始化,并使用标记的数据进行训练以执行下游任务。这个过程要容易得多,因为最难的部分——语言理解——已经大部分完成了。

最初的训练,旨在训练网络对文本的“理解”,是基于未标记的数据。标签——模型要预测的东西——是从数据本身自动生成的。使用这种标签的训练通常被称为无监督的预训练。

目前的技术水平

最常见的无监督预培训方法有:

  • 屏蔽语言建模 —从剩余的上下文中预测一些删除的单词(例如 BERT
  • 语言建模——给定前一个单词预测下一个单词(例如 GPT-2乌尔姆菲特
  • 替换标记检测 —最近的,但非常有前景的策略,其中一些单词被由单独的辅助语言模型生成的单词替换,预训练任务是识别它们(例如 ELECTRA

图 3:两个最受欢迎的培训前目标

标记化——将文本分割成基本的块——的方法也各不相同。最简单但次优的策略是在每次遇到空格字符时将它们分开。在某些情况下使用基于单词的标记化,但通常包括特定于每种语言的规则(例如,将“不”分为“做”和“不”)。子词标记化(例如 SentencePiece )可能是最常见的——它是基于特定字符序列的频率从数据中导出的,通常将空白视为另一个字符。同样值得注意的是,字符级语言模型有时也会在实践中使用(例如 FLAIR )。

两组网络架构主导着这个领域:

  1. 变形金刚网络**——基于自我关注机制
    变形金刚网络最著名的例子包括谷歌的伯特 (+其众多变种)和 OpenAI 的 GPT-2 。他们帮助在许多(如果不是大多数)NLP 问题中实现了最先进的结果。训练非常容易并行化,当解释一个令牌时,他们可以在被解释的令牌之前和之后使用这两个令牌。
    注意机制有许多变种,这里概括了;它们通常将来自输入的不同部分的信息聚合为这些部分的加权平均值,由各种可训练模块计算权重。

    要找到许多架构的实现、预训练模型、教程等等,请参见拥抱脸变形金刚。**

  2. ****递归网络**(以及递归+其他架构的混合体)。
    乌尔姆菲特或许是这个群体中最受欢迎的,其他值得一提的还有多菲特沙-RNN 以及最近开源的莫格里菲耶 LSTM
    虽然训练更难并行化,但正是因为这个原因,模型往往更小,训练更有效——没有办法通过增加问题的计算来加速训练。上面提到的所有递归网络都可以在具有单个 GPU 的台式计算机上从头开始训练。他们应该相对快速地为下游任务进行微调。如果标记数据很少,它们也往往表现得更好(参见 MultiFiT )。与基于 Transformer 的模型相比,另一个优势是:它们可以处理任意长的输入,而 Transformer 只能处理它们被设计和训练的长度的数据块(有一些技巧可以用来一部分一部分地处理更长的文档并汇总结果)。

    Fast.ai 包含一个 ULMFiT 的实现,以及一个预先训练好的英语语言模型。多功能库包含其他几种语言的预训练模型,如果需要另一种语言,还有预训练脚本。**

提议的架构

本文描述了一种用于处理文本文档的分类模型的新型网络架构——对 ULMFiT 的修改。使用该论文的命名法,提出了不同的分类头

ul fit—概述

从头开始训练一个 ULMFiT 型号包括 3 个步骤:

  1. 语言模型预训练 —在大型通用文本语料库(例如维基百科)上训练语言模型
  2. 语言模型微调 —对来自分类任务领域的文本继续训练语言模型(想要对推文进行分类?在同一种语言的大量推文上训练)
  3. 分类器微调 —语言模型的最后一层被替换为分类器头(如下所述)。剩下的预训练部分称为编码器。
    首先,只训练分类器头的随机初始化参数。之后,编码器的各层逐渐开始优化,从最后到第一。**

ULMFiT 论文中使用的网络架构如下所示。它包含:

  • 可训练嵌入层,
  • 3 个递归层( LSTM 增加了许多正则化方法: AWD-LSTM ),
  • 分类器头(将可变长度序列聚合成固定长度的表示,并计算分类决策)

图 4: ULMFiT 分类——处理一个示例输入序列

分类器头**执行串联池,然后将其结果通过一个完全连接的层和输出层。

****串联轮询是 3 个元素的简单串联:

  • average-pooling——沿序列维度对元素进行平均(对代表每个单词的向量进行平均),
  • max-pooling —沿序列维度的元素最大值,
  • 编码器的最后一个输出向量。

转移注意力——建议的分类器头

所描述的体系结构旨在直接解决使用平均池和/或最大池来聚集由递归语言模型生成的文本表示的两个主要问题。

  1. 按位置统计(平均池和最大池)对序列中的所有标记进行同等加权,即使通常只有一部分与分类任务相关。在电影评论中进行情感分析的情况下——评论者可能会写一部伟大电影中讲述的悲伤、悲惨的故事。虽然大部分文本可能有负面情绪,但我们只想专注于描述电影的部分,而不是其情节的内容。
  2. 只有当感兴趣的文本特征与表示空间的坐标系的轴对齐时,平均池和最大池才有语义意义。对于情感分析,我们希望看到一个神经元(表示空间的一维)准确表达文本中的积极或消极情感,而不是其他。虽然对于像情感这样的简单概念来说,它可能近似为真,但对于更复杂的特征来说,它不太可能成立,甚至是近似成立。如果条件不成立,对长序列(数百或数千个令牌)求平均值很可能会破坏存储的信息。

因此,分类器头包含两个“分支”,每个分支回答一个问题:

  • ATT 分公司:“哪些部分是相关的”,我们应该注意什么?****
  • AGG branch:"我们想要 agg regate 的特性是什么?"

两者都被实现为简单的、完全连接的神经网络。层的数量和大小是额外的超参数,其选择在“结果”一节中讨论。网络独立地应用于每个序列元素。ATT 分支返回的值(每个序列元素的标量(eⱼ))然后通过 Softmax 函数传递,以获得加权平均值的正确权重(aⱼ)。AGG 返回的向量的加权平均值( bⱼ )成为序列 (C) 的最终表示。

分支注意力结构方程。非线性函数 f 和 g 由神经网络 ATT 和 AGG 实现。粗体向量。

“分支注意力”分类器头部示意图:

图 5:分支注意力分类器头

注意:如果 AGG 分支被跳过,并且 ATT 只有输出层,那么整个聚合减少到点积关注,只有一个可训练的查询。它不会产生特别好的结果,这将在下一节中讨论。

实验和结果

大多数实验都是使用流行的情感分类数据集 IMDB T1 进行的。它包含 5 万条影评,由 imdb.com 网站的用户撰写。如果用户对电影的评分为 7 分或以上,它们将被标记为正面,4 分或以下,它们将被标记为负面。等级是平衡的,每部电影正面和负面的评论数量相等。每部电影评论不超过 30 条。文件长度差异很大——许多相对较短,但有 2%超过 1000 字。同样值得注意的是,这些标签有些“嘈杂”,例如,有些正面评论被标记为负面。

IMDB 示例

为了模拟小数据集的情况,同时在体系结构之间进行具有统计意义的比较,实验以如下方式进行:在 IMDB 训练数据集的 1000 个元素样本上训练模型,并在整个测试集上进行评估。对于每个架构和超参数集,这重复了 20 次。与训练相关的超参数和单个漏失乘数(由乌尔姆菲特作者推荐)分别针对分支注意力和串联池头部进行了调整。

下面,将所提出的架构(分支注意力)的结果与基线(串联池)进行比较。作为最小消融研究,提供了两种额外的变体:

  • 移除了 AGG 分支(并且用来自 ATT 分支的权重对编码器输出进行平均),
  • ATT 分支被移除,并且 AGG 提取的特征被统一加权。

表 1:不同分类器头的分类性能,平均来自 20 个 1000 元素数据集的训练。优化整个网络的参数(逐步解冻)

虽然这绝不是突破性的,但上述结果似乎令人鼓舞。在不改变编码器(该模型最重要的部分)的情况下,错误分类的样本数量减少了 10%以上。所提出的架构似乎更好地利用了编码器的文本表示。

****在上述最佳配置中,分支注意力头的参数比原始 ULMFiT 架构少 30%。在其他配置中,性能稍差,参数数量最多减少 85%。

值得注意的是,移除分支注意力头的任何一个分支都会导致表现的显著下降——甚至低于基线水平。虽然为了简洁起见没有在这里显示,但是减少任一分支的深度(到一个层)也会导致性能小幅但持续下降。结果支持使用具有两个分支的完整网络架构,如前所述。

单个实验运行的准确度分数的分布如下所示,为箱线图。它旨在可视化每个配置的结果差异,这些差异是由不同的训练数据集采样和不同的随机初始化产生的。

值得注意的是:所有四个体系结构中存在的单个负异常值(表示为点)来自于对训练数据集的相同样本的训练——可能质量较差。很可能,如果每次都用相同的数据集重复训练(只改变随机种子),每个配置的精确度的方差将会低得多。然而,使用不同的数据集样本是为了确保任何结论都更具普遍性,而不是特定于特定的数据集。总体来说,均值精度的提高不能合理解释为随机噪声。****

仅头部训练

使用带有可训练集合的分类器头,而不是固定的,我们还能得到什么好处?我们可能根本不需要修改编码器的参数。根据 ULMFiT 的说法,我们应该首先优化分类器头部,然后逐渐将编码器的层添加到优化器的范围内——这种方法在上一节中有所描述。然而,专门训练分类器的头部具有重要的实际好处,这将在下一节中讨论。下表总结了通过这种方式获得的结果——仅优化了分级机机头的参数。

表 2:不同分类器头的分类性能,平均来自 20 个 1000 元素数据集的训练。仅优化分类器头部的参数

有点令人惊讶的是,对于完全分支注意力的头部,结果一点也没有恶化。以这种方式训练分类器可能是一种可行、实用的方法,尤其是对于相对较小的数据集正如预期的那样,提议的架构和 Concat 池之间的差距已经增大。

没有 AGG 分支的变体的性能高于预期,优于整个网络优化时的性能(上一节)。这可能表明优化整个网络时的训练过程可以改进,但这种尝试并不成功。

纯头部训练的好处

为什么当只训练分类器头时,所提出的架构比基线执行得更好是重要的?以这种方式构建系统有几个实际优势。训练速度更快,需要的内存更少。一个不太明显的好处在于,被训练的模型所特有的参数数量很少。

新的用例出现在许多分类器对同一领域的文本进行操作的场景中,例如给定语言的“tweets”或“news articles”。培训、存储和运行新模型变得非常便宜:

  • 训练不到一分钟(在现代 GPU 上)
  • 存储的分类器头占用约 100kB 的磁盘空间(大概数字,某些配置会更少)
  • 在推理时,所有模型的文档可以一起批处理,并在同一个 GPU/TPU 上有效编码;特定型号的推理部分很小,可以在 CPU 上快速运行。

能在哪里有用?有许多可能的情况,例如:

  • AutoML 平台 —一个不熟悉 ML 的用户提供一个带标签的数据集,并期望一个 API 给一个执行分类的模型。
  • ****推荐系统冷启动问题的“产品”一半。例如,当一篇新文章发布在 Medium 上时,没有人与之互动。传统的(协同过滤)算法基于具有相似品味的人的意见来推荐东西。这种方法不适用于新内容。我们可以根据一组活跃用户过去的活动,为他们训练“鼓掌预测”(评级预测)模型。然后,我们可以在任何人有机会阅读新文章之前,对它们进行人工评级。
  • ****个性化,有足够的数据可用。基于内容推荐社交媒体帖子,而不是其他人与它的互动,标签等。以 Twitter 为例,虽然根据所有用户的偏好模型运行所有新推文是不可行的,但可以使用这样的偏好模型作为当前系统之上的最终过滤层。

注意力可视化

通过分析神经网络中的注意力权重,我们可以更好地理解它——我们可以看到输入的哪些部分与手头的任务相关。有趣的可视化技术已经在机器翻译图像字幕生成中得到演示。

有了分支注意力,在某些情况下,我们可以更进一步。如果分类问题是二进制的,我们可以通过将 AGG 分支的最后(或唯一)维度设置为 1 来获得相当好的结果。实际上,对于每个输入令牌,我们将获得单个标量值,最终的决策将是这些值的加权平均值(经过简单的转换)。在情感分析的情况下,我们可以在处理每个标记之后显示“本地”情感,以及文档特定部分的重要性分数

请看下面的例子,并查看 互动演示 在演示中,可以同时显示权重和情绪(如示例所示),也可以只显示权重或情绪。也可以在不同的文本上测试模型。

图 6:示例电影回顾——注意力可视化

****每个标记后面颜色的不透明度表示与其相关的注意力权重。色调表示为该标记计算的特征(情感)的值,考虑其左上下文,以红到绿的比例。

一些观察结果:

  • ****该模型在大多数情况下正确识别情感,并“关注”相关部分(尽管“故事中开始出现漏洞”未被识别为相关)。
  • ****否定,即使放在离其对象几个字以外的地方,也被正确解读(“我不认为这是一部好电影”)。
  • 大的权重分配给句号字符。正如“什么不起作用”一节中所述,对这一事实的几种可能的解释进行了探讨。剩下的,工作假设:当遇到句号时,模型已经处理了一个完整的语句,后续的单词不太可能改变它的含义。模型在处理完句号后,通过编码器的状态学习访问整个句子的内容。****

完整的 IMDB,其他数据集

为了验证所提出的架构是在总体上表现良好,还是仅针对特定的数据集类型和大小表现良好,进行了几个其他实验。

完整的 IMDB 数据集

表 3:完整 IMDB 数据集上的分类性能

当在完整的 IMDB 数据集上训练时,在其默认的训练/测试分割中,结果大约等于基于 Concat 池的分类器的结果(尽管分支注意力似乎对训练相关的超参数的选择不太敏感)。直观上,大型训练数据集可能包含足够的信息来修改编码器,其输出将通过平均池和最大池进行有意义的聚合。

完整的 IMDB 数据集,仅头部训练

表 4:完整 IMDB 数据集上的分类性能,仅优化分类器头

当在完整的 IMDB 数据集上进行训练,但仅优化分类器头部时,分支注意力的表现明显好于基线。正如所料,它的性能比整个网络优化时更差。尽管如此,这种方法在某些情况下还是有用的,正如“纯头部训练的好处”一节中所讨论的。

自动网络欺凌检测— PolEval 2019 任务 6

使用一个旧版本的代码,一个基于分支注意力的系统进入了 PolEval 2019 竞赛——任务 6。它的目标是准备一个系统来评估哪些用波兰语写的推文可能包含网络欺凌内容。提供了大约 10,000 条示例推文的人群标签训练集。这是非常不平衡的——绝大多数例子不包含网络欺凌。

该系统使用了一种子词标记化机制——sentence piece——来处理 Twitter 上常见的拼写错误。这种标记化通常对波兰语有利,因为它是一种融合语言。它丰富的前置和后置修复大大增加了独特单词的数量,这是传统的基于单词的标记化方法的一个问题。该语言模型在波兰语维基百科上进行训练,然后在一个大型的未标记的波兰语推特数据集上进行微调。

在竞赛中,该系统获得了第三名 ( proceedings ,第 106 页),落后于一个ulm fit——类似于一个更大的、改进的语言模型的系统(类似于并由 MultiFiT 的合著者)。然而,它的排名仅在基于 BERT 的系统之前。

分支注意力和串联池的性能随后以与“IMDB 样本”实验相同的方式进行了比较,并在 IMDB 数据集上调整了超参数。两个模型都被训练了 20 次,每次都在训练数据集的不同子样本上进行,并且具有不同的随机初始化。为了直接比较模型的性能并避免设置检测阈值的问题,选择了平均精度度量。

表 5:网络欺凌检测任务的平均精度,20 次重复,1000 个元素的训练数据集样本

在完全不同的环境中——不同的语言、标记化策略、不同的(更复杂的)任务、没有进一步的超参数优化,分支注意力提供了适度但明显的性能改善。可以认为这是对所提议的体系结构的成功测试。

问题分类— SemEval-2019 任务 8A

使用事实核查问题数据集对该方法的结果进行了另一次验证。该数据集摘自“卡塔尔生活”——一个社区问答论坛。每个文档(主题中的第一篇文章)由类别、主题行和文章内容组成。这些字段用分隔符连接在一起。任务:给每个问题分配一个类别:“事实”、“观点/建议”或“社会”。

在与上述相同的实验设置中,使用相同的超参数,获得的结果为:****

表 5:问题分类任务的准确性,20 次重复,1000 个元素的训练数据集样本

****再一次,使用分支注意力的网络表现明显更好——尽管两个网络共享相同的预训练编码器。

什么没有起作用,可能的改进

为提高网络性能,进行了几次尝试:

  1. ****双向编码器。编码器的输出与第二个编码器的输出连接在一起,第二个编码器被训练来反向处理文本。它为文档提供了更丰富的表示,每一点都以文档的整体内容为条件。GPU 内存需求增加了大约 2 倍,但性能并没有明显提高。然而,由于内存限制,其他参数必须更改,这可能会降低性能。利用具有更多内存(理想情况下为 16GB 以上)的 GPU 的进一步实验可能会改变这一结论。前向和后向语言模型的联合训练可以改善结果,并且节省一些内存——通过在嵌入/softmax 层中的权重共享。
    ul mfit 的作者报道了类似的结果。然而,值得注意的是,分支注意力聚合应该能够更好地利用附加信息——对于每个序列元素,计算相关特征,然后对它们进行平均。总的来说,虽然最初的结果是负面的,但这个想法似乎仍然值得进一步研究。
  2. ****使用较早的编码器层。ELMO 之后,编码器前几层的输出被用作分类器头的附加输入(图 4 中的 LSTM-1 和 LSTM-2)。它没有改善结果,但是——根据最初的作者——最佳层或层的组合可能会因任务而异。
  3. 使用最后一个令牌的表示。其中一个假设,即模型为什么学习给句号字符分配高权重,是:它寻找文档的结尾。最后一个标记的表示取决于整个文档,所以假设它包含所有相关信息。然而,以各种方式直接结合最后一个记号的表示并没有改善分类结果。
  4. ****运用 LSTM 的“细胞”价值观。对于输入序列的每个元素,LSTM 层产生两个向量:“单元”状态值 c 和输出/隐藏值 h 。虽然 c 值旨在更长时间地存储信息,但输出值是作为 c 的门控版本产生的,并且输出门由本地上下文参数化。正常情况下,只有 h 值被传递到下一层(参见图 4 )。
    另一个为什么句号字符的表示可能被赋予更大权重的想法是:在语言模型设置中,它们被用来预测新句子的第一个单词。要做到这一点,他们必须访问更多的文档上下文,这可能会导致输出门更加开放。
    这一假设激发了在分类器头部直接使用非门控 c 值的想法。然而,
    分类结果并没有改善(计算效率降低,因为优化的 cuDNN 实现无法使用)。
    在对几个示例中的相应的 ch 向量进行更仔细的检查后, h 值不像 c 的门控版本(在某种意义上近似于硬二进制门控)。似乎选通操作只是提供了一个额外的、可训练的非线性变换,有点类似于大小为 1 的卷积。进一步分析 LSTM 网络的输出门发生了什么,以及它如何与正在处理的文本的语法相对应,这本身似乎是一个有趣的话题,但超出了本文的范围。

结论

  • 在几项任务中,提出的解决方案优于原始的 ULMFiT 架构,或者表现同样出色。
  • 基于“分支注意力”的分类器头在相对小的数据集的情况下具有优势,并且如果只是分类器头被优化。
  • 如果标记的训练数据稀少和/或需要许多分类器在相似的文档上操作,则在预训练的语言模型之上仅优化分类器头可能是可行的策略。在后一种情况下,如果只是分类器头的参数不同,则可能实现更高效的部署,并且需要更少的磁盘空间。
  • 分支注意力架构的所有变体都为分类器增加了某种程度的可解释性。我们不仅可以发现哪些部分被认为是重要的,而且在某种程度上还可以发现它们是如何被理解的(见演示)。
  • 在其推荐的配置中,与基线相比,建议的架构具有更少的参数,并且需要更少的计算。由于其性能似乎大多优于或等于基线,因此可将其视为使用原始 ULMFiT 配置的分类器的替代产品。

提供了重现本文结果所需的代码和微调后的语言模型。

乳腺癌预测中的可解释深度学习

原文:https://towardsdatascience.com/explainable-deep-learning-in-breast-cancer-prediction-ae36c638d2a4?source=collection_archive---------16-----------------------

了解医疗保健中的卷积神经网络预测结果

高级机器学习模型(例如随机森林、深度学习模型等。)通常被认为是不可解释的[1][2]。如[1][2][3][4]中所述,这些模型在很大程度上仍然是黑盒,如果医生计划根据预测结果采取措施治疗疾病(如癌症),那么理解其医疗保健预测结果背后的原因对于评估信任度非常重要。在[2]中,我使用了威斯康星州乳腺癌诊断(WBCD)表格数据集来介绍如何使用局部可解释模型不可知解释(LIME)方法来解释随机森林模型在乳腺癌诊断中的预测结果。

在本文中,我使用 Kaggle 乳腺癌组织学图像(BCHI)数据集[5]来演示如何使用 LIME 来解释用于浸润性导管癌(IDC)乳腺癌诊断的 2D 卷积神经网络(ConvNet) 的图像预测结果。

1.准备乳腺癌组织学图像数据集

BCHI 数据集[5]可以从 Kaggle 下载。如[5]中所述,数据集由 5,547 个 50x50 像素的 H & E 染色乳腺组织病理学样本的 RGB 数字图像组成。这些图像被标记为 IDC 或非 IDC。有 2,788 个 IDC 图像和 2,759 个非 IDC 图像。这些图像已经被转换成 Numpy 数组,并存储在文件 X.npy 中。类似地,相应的标签以 Numpy 数组格式存储在文件 Y.npy 中。

1.1 加载数据

X.npyY.npy 文件下载到本地计算机后,它们可以作为 Numpy 数组加载到内存中,如下所示:

X = np.load('./data/X.npy') # images
Y = np.load('./data/Y.npy') # labels (0 = Non IDC, 1 = IDC)

以下是其中两个数据样本,左边的图像标记为 0(非 IDC),右边的图像标记为 1 (IDC)。

图一。两个样本:左边的标为 0(非 IDC),右边的标为 1 (IDC)。

1.2 混洗数据

在原始数据集文件中,所有标记为 0(非 IDC)的数据样本都放在标记为 1 (IDC)的数据样本之前。为了避免人为的数据模式,数据集被随机打乱如下:

indices = np.arange(Y.shape[0])
np.random.shuffle(indices)
indices = list(indices)
X = X[indices]
Y = Y[indices]

1.3 转换数据集

IDC 图像中的像素值在[0,255]的范围内,而当输入数据的值在[0,1]或[-1,1]的范围内时,典型的深度学习模型工作得最好。下面的类 Scale 是将 IDC 图像的像素值转换到[0,1]的范围内。

class Scale(BaseEstimator, TransformerMixin):
    def __init__(self):
        pass 

    def fit(self, X, y):
        return self

    def transform(self, X): 
        X1 = X.copy()
        X1 = X1 / 255.0
        return X1

1.4 划分数据集用于模型训练和测试

数据集分为三部分,80%用于模型训练和验证(1,000 用于验证,其余 80%用于训练),20%用于模型测试。

X_train_raw, X_test_raw, y_train_raw, y_test_raw = train_test_split(X, Y, test_size=0.2)X_train = X_train_raw.copy()
X_val   = X_train[:1000]
X_train = X_train[1000:]
X_test  = X_test_raw.copy()y_train = y_train_raw.copy()
y_val   = y_train[:1000]
y_train = y_train[1000:]
y_test  = y_test_raw.copy()

2.训练 2D ConvNet 模型

BCHI 数据集[5]由图像组成,因此选择 2D ConvNet 模型进行 IDC 预测。

2.1 创建 2D 通信网

与[5]类似,下面的函数 getKerasCNNModel ()为 IDC 图像分类创建 2D ConvNet。

def getKerasCNNModel():
    batch_size = BATCH_SIZE
    epochs = EPOCH_SIZE  
    img_rows, img_cols = X_train.shape[1], X_train.shape[2]
    input_shape = (img_rows, img_cols, 3) 
    model = Sequential()
    model.add(Conv2D(16, kernel_size=(3,3), activation='relu', input_shape=input_shape))
    model.add(MaxPooling2D(pool_size=(2, 2)))  
    model.add(Dropout(0.25)) 
    model.add(Conv2D(32, (3,3),  activation='relu')) 
    model.add(MaxPooling2D(pool_size=(2, 2)))  
    model.add(Dropout(0.25)) 
    model.add(Flatten())
    model.add(Dense(128, activation='relu'))  
    model.add(Dropout(0.5)) 
    model.add(Dense(1, activation='sigmoid'))

    model.compile(loss= keras.losses.binary_crossentropy, 
                  optimizer=keras.optimizers.rmsprop(), 
                  metrics=['accuracy'])

    return model

2.2 创建管道组件

KerasCNN 是将 2D ConvNet 模型包装成一个 sklearn 管道组件,这样它就可以与其他数据预处理组件如 Scale 组合成一个管道。

class KerasCNN(BaseEstimator, TransformerMixin):
    def __init__(self, X_val=None, y_val=None):
        self._model      = getKerasCNNModel()
        self._batch_size = BATCH_SIZE
        self._epochs     = EPOCH_SIZE
        self._X_val      = X_val / 255.0
        self._y_val      = y_val

    def fit(self, X, y):  
        self.history = self._model.fit(X, y,
                        batch_size=self._batch_size,
                        verbose=1,
                        epochs=self._epochs,
                        validation_data=(self._X_val, self._y_val))
        return self

    def transform(self, X): 
        return X def predict_proba(self, X):
        y_pred = self._model.predict(X) 
        return y_pred  

    def evaluate(self, X, y):
        return self._model.evaluate(X,y)

3.解释模型预测结果

如前所述,我在本文中使用 LIME 来解释 ConvNet 模型预测结果。

3.1 设置管道

与[1][2]类似,我制作了一个管道来包装 ConvNet 模型,以便与 LIME API 集成。

from sklearn.pipeline import Pipelinesimple_cnn_pipeline = Pipeline([
    ('scale', Scale()),
    ('CNN', KerasCNN(X_val=X_val, y_val=y_val))
    ])

3.2 训练 ConvNet 模型

ConvNet 模型的训练如下,以便它可以被 LIME 调用,用于以后的模型预测。

simple_cnn_pipeline.fit(X_train, y_train)

3.3 选择石灰解释器

如[1][2]所述,LIME 方法支持不同类型的机器学习模型解释器,用于不同类型的数据集,如图像、文本、表格数据等。本文选择了 LIME image 解释器,因为数据集由图像组成。

2D 图像分割算法quick shift用于生成石灰超像素(即片段)[1]。

*from lime import lime_image
from lime.wrappers.scikit_image import SegmentationAlgorithmexplainer = lime_image.LimeImageExplainer() segmenter = SegmentationAlgorithm(‘quickshift’, kernel_size=1, max_dist=200, ratio=0.2)*

3.4 解释模型预测

一旦训练了 ConvNet 模型,给定一个新的 IDC 图像,就可以调用 LIME 图像解释器的 explain_instance ()方法来生成模型预测的解释。

图像预测的说明由模板图像和相应的掩模图像组成。这些图像可用于以不同方式解释 ConvNet 模型预测结果。

图二。将解释上述两个样本的预测。ConvNet 模型预测左图像为负(IDC: 0),右图像为正(IDC: 0)。

说明 1:正 IDC 的预测(IDC: 1)

图 3 显示了用于解释通过石灰的模型预测的正 IDC 图像。

图 3。IDC_1_sample:待解释的正 IDC 样本的预测

下面的代码是为图 3 中的图像 IDC_1_sample (IDC: 1)生成模型预测的解释对象explain _ 1。**

在本说明中,白色用于指示支持模型预测的图像部分( IDC: 1)

*explanation_1 = explainer.explain_instance(IDC_1_sample, 
                classifier_fn = simple_cnn_pipeline.predict_proba, 
                top_labels=2, 
                hide_color=0, 
                num_samples=10000,
                segmentation_fn=segmenter)*

一旦得到模型预测的解释,就可以调用它的方法 get_image_and_mask ()来得到模板图像和对应的掩膜图像(超像素):

*from skimage.segmentation import mark_boundariestemp, mask = explanation_1.get_image_and_mask(explanation_1.top_labels[0], 
                                            positive_only=True, 
                                            num_features=20, 
                                            hide_rest=True)
plt.imshow(mark_boundaries(temp, mask))*

图 4 以灰色显示了给定 IDC 图像的隐藏部分。图像的白色部分表示给定 IDC 图像中支持正 IDC 模型预测的区域。

图 4:通过隐藏原始图像细节来解释图 3 中的正 IDC 的模型预测。白色表示支持模型预测的区域。灰色部分不支持或与模型预测无关。

下面的代码以黄色显示 IDC 图像区域的边界,支持正 IDC 的模型预测(见图 5)。

*temp, mask = explanation_1.get_image_and_mask(explanation_1.top_labels[0], 
                                            positive_only=True, 
                                            num_features=20, 
                                            hide_rest=False)
plt.imshow(mark_boundaries(temp, mask))*

图 5:用原始图像细节解释图 3 中的正 IDC 的模型预测。黄色表示图 4 中支持模型预测的白色区域的边界。灰色区域要么不支持,要么与预测无关。

说明 2:非 IDC (IDC: 0)的预测

图 6 示出了用于解释经由石灰的模型预测的非 IDC 图像。

图六。IDC_0_sample:要解释的负 IDC 样本的预测

下面的代码是为图 6 中的图像 IDC_0_sample 生成模型预测的解释对象explain _ 2在本说明中,白色用于表示支持非 IDC 的模型预测的图像部分。

*explanation_2 = explainer.explain_instance(IDC_0_sample, 
                                         classifier_fn = simple_cnn_pipeline.predict_proba, 
                                         top_labels=2, 
                                         hide_color=0, 
                                         num_samples=10000,
                                         segmentation_fn=segmenter
                                        )*

一旦获得了模型预测的解释,就可以调用其方法 get_image_and_mask ()来获得模板图像和对应的掩膜图像(超像素):

*temp, mask = explanation_2.get_image_and_mask(explanation_2.top_labels[0], 
                                            positive_only=True, 
                                            num_features=20, 
                                            hide_rest=True)
plt.imshow(mark_boundaries(temp, mask))*

图 7 以灰色显示了非 IDC 图像的隐藏区域。图像的白色部分表示给定非 IDC 图像中支持非 IDC 模型预测的区域。

图 7:通过隐藏原始图像细节对图 6 的负 IDC (IDC: 0)的模型预测的解释。白色表示支持模型预测的区域。灰色部分不支持或与模型预测无关。

下面的代码以黄色显示 IDC 图像区域的边界,支持非 IDC 的模型预测(参见图 8)。

*temp, mask = explanation_2.get_image_and_mask(explanation_2.top_labels[0], 
                                            positive_only=True, 
                                            num_features=20, 
                                            hide_rest=False)
plt.imshow(mark_boundaries(temp, mask))*

图 8。用原始图像细节解释图 6 的非 IDC(IDC:0)的模型预测。黄色表示图 7 中支持模型预测的白色区域的边界。灰色区域要么不支持,要么与预测无关。

结论

在本文中,我使用 Kaggle BCHI 数据集[5]来展示如何使用石灰图像解释器[3]来解释 IDC 乳腺癌诊断中 2D 康文内特模型的 IDC 图像预测结果。通过将超像素/特征的数量(即方法 get_image_and_mask ())中的 num_features 参数)设置为 20,提供了对 IDC 和非 IDC 的模型预测的解释。

我观察到解释结果对超级像素/特征的数量的选择很敏感。需要领域知识来调整该参数,以实现适当的模型预测解释。输入数据(在这种情况下是图像)的质量对于合理的结果也非常重要。增加更多的样本可以提高精确度。

Github [6]中提供了一个 Jupyter 笔记本,其中包含了本文中使用的所有源代码。

参考

[1] M. T. Ribeiro,S. Singh,C. Guestrin,“我为什么要相信你?”解释任何分类器的预测

[2] Y. Huang,面向医疗保健的可解释机器学习

[3] 关于图像分类的石灰教程

[4] 可解释的机器学习,使黑盒模型可解释的指南

[5] 预测乳腺癌组织学图像中的 IDC

[6] Y .黄,朱庇特笔记本

披露声明:2020 年。本文中表达的观点仅代表作者的观点,不代表阿贡国家实验室的观点。

知识图中可解释、高效和准确的节点分类

原文:https://towardsdatascience.com/explainable-efficient-and-accurate-node-classification-in-knowledge-graphs-a5d3ba02c245?source=collection_archive---------24-----------------------

用 MINDWALC 挖掘区别性行走

1.用(知识)图表示更丰富的数据

图形是一种数据结构,可用于表示无处不在的现象,如社交网络、化学分子和推荐系统。它们的优势之一在于,它们显式地对单个单元(即节点)之间的关系(即边)进行建模,这为数据增加了额外的维度。我们可以用 Cora 引用网络来说明这种数据的丰富。这是一个数据集,包含了几百篇论文的单词包表示以及这些论文之间的引用关系。如果我们应用降维(t-SNE)来创建单词袋表示的 2D 图,我们可以看到(相似研究主题的)聚类出现,但它们重叠。如果我们生成一个嵌入图网络,考虑到引用信息,我们可以看到聚类被更好地分离。

左图:每篇论文的单词袋表示的 t-SNE 嵌入。右:图网络产生的嵌入,考虑了论文之间的引用。来源:Velickovic 等人的“Deep Graph Infomax”。

知识图是一种特殊类型的图。它们是多关系的(即不同类型的关系有不同的边)和有向的(即关系有主体和客体)。幸运的是,我们可以将知识图转换为正则有向图,这便于进一步的分析。

将多关系有向知识图转换为正则有向图。

2.(知识)图中的节点分类

图形越来越多地被用于各种机器学习任务。例如,可以在社交网络中向用户推荐新朋友,预测一个人在协作网络中的角色,或者在生物交互图中对蛋白质的角色进行分类。在这篇文章中,我们将重点关注节点分类的任务:我们在一个图中获得了一组标记节点,需要创建一个模型来预测未标记节点的类别。

在本帖中,我们将使用一个运行示例,目标是将研究人员分类到正确的机构中。下面我描绘了 5 名研究人员。他们的颜色与他们的机构相对应。我们可以检索这些节点的邻居来扩展我们的图。可以看出,图表的大小增长很快。经过 2 次迭代的扩展,我们不再能直观地看到我们的图形。

通过检索节点的邻居来扩展我们的图。经过 2 次迭代后,很难将图形可视化。

3.遍历和通配符

我们的目标是挖掘对某一类非常有区别的行走(或图中的路径)。我们可以按如下方式记录一次行走:

一个 n 跳行走的例子。

如果它有向“v0”的方向,则可以在“根”的邻域中找到这个行走。从那里,“v0”和“v1”之间必须存在有向边,如此类推,直到我们到达“vn”。如果突然我们不能再穿过一条边到达下一跳,我们就说找不到了。****

此外,我们在遍历中引入了通配符(" * ")。这个通配符的语义是任何顶点都可以在那个位置匹配:

在我们的行走中引入通配符。

现在,如果它具有到节点的有向边,该节点具有到 v1 的有向边,则可以在根的邻域中找到该行走。中间节点上的标签无关紧要。

4.MINDWALC:有效挖掘歧视性行走

MINDWALC(MiningInexpertable,Dis criminativeWalks forCclassification of Nodes In a Knowledge Graph)是一种允许高效地挖掘特定格式的遍历的技术。一次 l 跳的遍历必须由 l-1 个通配符组成,最后跟一个顶点:

如果 x 可以在 l 次跳跃中到达,则可以在根的邻域中找到这种行走。由于这种走查的格式,我们可以引入一个数据结构,该数据结构将允许测试是否可以在恒定时间内在一个邻域中找到这样的走查。数据结构是一个集合列表。列表中索引 I 上的每个集合包含可以在恰好 I 跳中到达的节点。下面显示了如何构建该数据结构的代码片段:

构建一个数据结构,允许在恒定时间内测试行走的存在

MINDWALC 的目标是找到能最大限度获取信息的行走方式。信息增益可以被定义为由数据划分导致的熵的减少。熵是对不确定性的一种度量,如下所示:

熵是不确定性的度量。不确定性越多,熵就越高。

由于我们可以在恒定时间内测试这些特定行走的存在,我们可以对所有可能的(深度,顶点)候选进行强力搜索。深度定义了我们遍历中的跳数(因此我们将使用深度为 1 的通配符),顶点是我们最后一跳的标签。

MINDWALC 允许快速计算所有可能行走的信息增益。

5.将挖掘算法与分类技术相结合

我们的挖掘算法可以与几种分类技术相结合。首先,我们可以递归地应用该算法,以便归纳出一个决策树。最终的模型是完全可解释的,因为我们可以很容易地将整个树可视化,或者在我们的决策树中突出显示所采用的路径以形成预测。在这篇文章中,我们将通过检查和讨论一个归纳决策树来进一步证明这一点。第二,我们可以归纳出多个决策树,每个决策树都有训练实体和可能的子结构的子样本,以便形成一个森林。这种集成技术通常导致更好的预测性能,但代价是较低的可解释性和较长的训练时间。最后,我们可以通过对数据执行一次遍历来分离建模和挖掘,以挖掘信息丰富的行走集合。然后,这些遍历可以用于创建高维二进制特征向量(特征变换),这些向量可以被传递给任何分类算法。

6.可解释的和最先进的分类结果

我们使用四个基准数据集将我们的技术与两个黑盒替代方案进行了比较。黑盒替代品是 RDF2VecR-GCN 。下表列出了四个基准数据集的属性:

我们研究中使用的四个基准数据集的属性。AIFB 数据集对应于本文中使用的运行示例。

对于每个数据集,我们运行了 10 次。在测试集上获得的平均准确度分数及其相应的标准偏差总结如下:

两种黑盒方案和我们的算法的分类精度。

这些结果清楚地表明,对于 KGs 中的节点分类,所有三种提出的技术至少与当前最先进的技术相竞争。最后,我们检查了在 AIFB 数据集上归纳的决策树,这是我们对不同研究人员进行分类的运行示例:

在 AIFB 数据集上诱导的决策树。

在根节点中,我们找到了行走root->->->->->->viewProjektOWL/id68 instance。当这个遍历可以在一个实例的附近找到时,它就不再属于研究机构 id4instance ,因为这个叶子没有出现在右边的子树中。此外,通过在我们的遍历中使用通配符,这种类型的遍历已经展示了具有固定深度的附加值。事实上,从一个实例到一个类型为项目的实体,只需要两次跳跃(例如root->->viewProjektOWL/id68 instance)就可以结束,但是这导致的信息增益比需要六次跳跃时少得多。当检查原始 KG 时,似乎只有两个人直接参与了项目**id68 实例,或者换句话说,只有两跳的路径可以匹配。另一方面,这两个人似乎与他们所属的其他研究人员一起写了相当多的论文。因此,首先从某个人(根)跳转到他或她的一篇论文,并通过作者谓词从那里转到前面提到的两个人中的一个,可以找到来自从属关系ID3 实例的 45 个人、来自id2 实例的 3 个人和来自id1 实例的 2 个人。从根开始的右边子树中的剩余节点信息较少,因为这些节点试图将来自两个附属关系 id2instanceid1instance 的 5 个人与其他 45 个人分开。

7.结论

这篇关于 MINDWALC 的博文到此结束。MINDWALC 是一种算法,它允许在知识图的节点分类的上下文中挖掘特定类型的行走,这些行走对于某些类(组)是有信息的。此外,我们表明,这种算法是一个很好的预测模型的基础,当结合使用在这项工作中提出的三种不同技术之一。在四个 KG 基准数据集上的实验表明,我们提出的方法优于当前最先进的技术,同时,与这些技术相比,是完全可解释的。这对于关键领域的应用非常重要。

参考

  1. Velič ković,Petar 等人,《深度图 infomax》 arXiv 预印本 arXiv:1809.10341 (2018)。
  2. 用图卷积网络建模关系数据。欧洲语义网大会。施普林格,查姆,2018。
  3. 《RDF2Vec: RDF 图嵌入及其应用》语义网10.4(2019):721–752。
  4. 引导一棵有区别路径的决策树来分类知识图中的实体。 SEPDA2019,第四届语义驱动的数据挖掘与分析国际研讨会。2019.
  5. https://github.com/IBCNServices/MINDWALC

可解释的 MNIST 分类:对一个网络的剖析

原文:https://towardsdatascience.com/explainable-mnist-classification-dissection-of-a-convnet-f32910d52842?source=collection_archive---------45-----------------------

解释在 MNIST 图像分类中一个 ConvNet 的决定。

神经网络怎么把图像分类的这么好?尽管在准确性方面取得了很大进展,但我们还无法解释神经网络的内部工作方式。

卷积神经网络(ConvNet)只是简单数学函数的组合——构建块(BB)。这些论坛本身更容易理解,我们可以通过仔细查看它们的参数(权重)和观察它们如何转换输入来了解它们在做什么。

由于每个 BB(大部分)都可以自己解释,而 ConvNet 只是这些 BB 的组合,我们应该能够组合 BBs 的所有小解释,并建立对 ConvNet 如何工作的整个机制的理解,对吗?

这是我想探究的问题。举个简单的例子,我想了解在 MNIST 训练过的 ConvNet 如何对数字图像进行分类,并受训练过的 ConvNet 的启发,基于更简单的 BBs 手工构建一个 ConvNet(没有训练),它将模仿原始 ConvNet 的 BBs,但它们会更受约束,因此有望更容易解释。

我从剖析一个在 MNIST 上训练的 ConvNet 开始(我称之为 SimpleConvNet ),我发现其中一个卷积层可以完全省略,因为它只是复制了前一个卷积层的输出。我还发现了 BBs 的双重性,即 BBs 既被用来检测模式,又被用来检测那些模式的位置。在理解了 SimpleConvNet 的 BBs 之后,我开始用手构建一个 ConvNet(我将称之为 HandNet)包含更多约束(因此比使用 backprop 更容易调整)的 BBs。我发现手工制作的 ConvNet 性能要差得多,但经过微调后,它的精度与 SimpleConvNet 不相上下。HandNet 相对于 SimpleConvNet 的优势在于,理解和解释其内部工作方式要容易得多。

在本文中,我将详细解释 SimpleConvNet 的剖析。在第 2 部分中,我将展示我如何使用更多的受限 BBs 来构建 HandNet。

如果您想跟随代码,调整代码,或者对您的网络进行类似的剖析,本文有一个 Colab 笔记本

这是文章其余部分的大纲

术语

首先,我将在本文的其余部分使用一些术语:

我用 PyTorch 符号表示卷积层:输入是形状的( NC in、 HW )。输出的形状为( NC out、 H out、 W out)。卷积将被说成有 Cin 内层 通道和 Cout 外层 通道。

我将在整篇文章中使用基于 1 的索引,因为它使图表更容易理解。

剖析网络

培养

我首先用一个相当简单的架构训练了一个 ConvNet,如下所示。

(con v1+relu+max pool)→(con v2+relu+max pool)→(con v3+relu+max pool)→扁平化→ fc4 → fc5 → log_softmax。使用此工具生成的图像。

我主要对剖析卷积层感兴趣,因为全连接层非常简单,因为它们只是实现了线性组合。以卷积为例,我们看到 conv2 权重(形状为[64,32,5,5])将由 64x32=2048 个大小为 5x5 的网格组成。手动标注的网格数量相当大,所以我决定缩小网络的规模。

我反复修剪了 conv 的外通道(见术语部分关于外通道的解释)。基于下述标准的层:

  • 那些活化变异小的。
  • 看起来像噪声的那些(与看起来像模式检测器(如 Gabor 滤波器)的那些相对)。

conv1 中看起来像噪声的网格示例(左)与模式检测器示例(右)

简单超级刺激(左)与具有清晰模式的超级刺激(右)的示例

  • 外部通道不会选择性地响应来自 MNIST 的输入或策划的合成输入。

选择性反应的例子。左图:MNIST 数字——conv。选择性地响应数字 9 的对角线部分。右图:策划合成输入——conv。选择性地响应垂直线。

***我修剪了整个外部通道,而不仅仅是一些内部通道,因为在给定的外部通道中只移除一些内部通道会使卷积运算不规则并且有点难看😬。

在每一次剪枝迭代后,ConvNet 的精度略有下降,但重新训练网络很快恢复到剪枝前的精度,这表明许多权重是无用的。修剪去除了无用的权重,这些权重本质上是噪声,并且它使分类的本质更接近表面。希望只有少量的路径用于分类,其余的都是噪音。

在每一次修剪迭代后重新训练 ConvNet 会恢复修剪前的精度

最终架构(7 次修剪迭代后)如下所示。正如你所看到的,每一层的深度都明显减少了。

修剪后的最终架构。使用此工具生成的图像。

我们有 3 个 conv 层,接着是扁平化,然后是 2 个全连接(fc)层。(con v1+relu+max pool)→(con v2+relu+max pool)→(con v3+relu+max pool)→扁平化→ fc4 → fc5 → log_softmax

解剖

Conv1

让我们先来看看形状为[8,1,5,5] (8 个外通道,1 个内通道,5×5 网格)的 conv1 的权重:

以及它们对 MNIST 随机输入的激活:

以及它们在合成输入上的激活(在图中,我用一条线代表每个方向:水平、垂直和 2 条对角线):

上面的 3 幅图像强烈表明,conv1 中的第一个滤波器检测斜率= 45°的线(从权重可以看出,该模式类似于斜率= 45°的线)。用于角度的坐标框架如下所示:

类似地,第二和第三滤波器检测垂直线。不过两者的区别在于第二个滤镜检测的是垂直线的左侧,第三个滤镜检测的是右侧。如果我们对所有过滤器都做这个练习,我们可以从过滤器的索引(从 1 开始)到它检测的模式得到以下映射:

conv1 滤波器说明—从滤波器索引到其检测模式的映射

我们可以看到一些模式是重复的:索引=3 和 6 的过滤器检测相同的“垂直线的右侧”,索引= 4 和 7 的过滤器检测相同的“水平线的下侧”。还要注意,这些过滤器作为一个集合似乎跨越了线方向的整个空间,尽管是一个非常离散的方向(增量为 45 度)。(由以下角度组成的空间:[0,45,90,135])

请记住,conv2 不会直接看到 conv1 的输出。中间有 ReLU 和 MaxPool。因此,当 conv2 发现时,负值和大部分低正值将从 conv1 的输出中消失。

Conv2

Conv2 形状为[16,8,5,5] (16 个外通道,8 个内通道,5x5 网格)。现在,我们不能只看 conv2 的权重,因为这需要可视化 168=128 个大小为 5x5 的滤波器,这有点太多了。此外,conv2 外部通道(有关外部通道的定义,请参见“术语”部分)不仅仅是滤波器与图像的卷积(conv1 就是这种情况),而是这种卷积的线性组合(因为与 conv1 不同,conv2 内部通道不等于 1),这使得仅通过查看 128 个滤波器很难想象 conv2 的机制。因此,现在,我们将跳过直接可视化权重。相反,让我们开始为 conv2 的每个外部通道(共有 16 个)可视化超级模拟(参见特征可视化了解超级模拟*的定义)。

conv2 外部通道的超级模拟

superstimuli 可以提示 conv2 的每个外部通道试图检测的内容。例如,它表示索引=3 的外部通道(我们称之为 conv2_3)正在检测斜率=135 的对角模式。

conv2_3 的超刺激

来自 MNIST 的随机输入激活 conv2 外部通道;

让我们看看 conv2_3 的激活情况。我们已经从它的超刺激中看到,它试图检测斜率= 135°的对角线模式。上面的激活似乎证实了这个假设——只有斜率=135 的数字 0 的对角线部分被这个外部通道突出显示:

让我们更仔细地看看 conv2_3 是如何实现的:

conv2_3 的切割激活

让我解释一下这个庞大的图表。

  • 第一列:conv2 的总输入(即 max pool(relu(con v1 的输出))。我们将用 x_j. 来表示该列中的行 j
  • 第二列:我们在上一节中发现的 conv1 的每个外部通道的描述。这个专栏应该描述第一个专栏(尽管它并不完美)。
  • 第三列:conv2_3 内部通道的索引(从 1 开始)。
  • 第四列:conv2_2 的每个内部通道的权重。我们将用 w_j. 来表示该列中的 j
  • 第五列:该列第行 j 是 x_j 与 w_j 的卷积,即a _ j= x _ j∫w _ j。( a 为激活)
  • 第六列:该列第行 j 是所有 a_j 的累加,即c _ j=σa _ k 从 k=1 到 k=j* (c 为累加)。而最后一行是c _ 8 = Bias+σa _ k 从 k=1 到 k=8 (bias 从第四列最后一行开始)。*
  • 第七列是 conv2_3 的输出。只是复制了第六列的最后一行。我们将把它表示为 y 。本质上,conv2_3 只是对其内部通道的各个卷积求和(x _ j∫w _ j)**

***为了便于比较,每列内的色标都进行了重新标准化。(因此,第五列和第六列的第一行实际上具有相同的值,但它们看起来不同,因为对它们应用了不同的颜色标准化)。

让我们开始分析每一行。

  • **a _ 1 = x _ 1∫w _ 1没有传达任何意义,通过查看conv2_3 的总输出,即 y ,,看起来 a_1y 没有任何影响。这很有可能是由于 a_1 被其他 a _j 的淹没,所以我们忽略这一行。
  • w_2 好像检测到斜率=135 的线。通过查看a2我们看到这是真的,但是a2非常弱,因此我们看不到a2*对总产量 y 的贡献。我们也将忽略这一行。*
  • 通过查看 a_3 ,似乎索引=3 的行正好否定其输入 x_3 ,即“垂直线的右侧”。它通过在其权重网格(第 4 列,第 3 行)的中心附近有一条强负垂直线来实现这种否定。所以我们将 a_3 的描述表示为 neg(“垂直线右侧”)。
  • a_4 是对 y 的第一次重大贡献。你可以看到从C4开始,它下面的所有东西都有两个亮点,它们出现在 y (斜率=135 的数字 0 的部分)。w_4 只寻找斜率为 135 的线,这与其输入 x_4,重叠,因此斜率为 135 的线在 a_4 中高亮显示。所以我们只是在 a_4 中看到更多的“坡度为 135 度的线”。

**** ( x_4 的描述是水平线的下侧”但是在这个特定的图像中,conv1_4 似乎检测对角线——你永远无法用神经网络_(ツ)_/ 100%确定。实际上,如果您仔细观察 conv1_4 的权重(如下所示),它有一个非常模糊的提示,即它正在检测斜率为 135 的线(下图中突出显示)。我把这些类型的模式称为 conv 的次要功能。过滤器*。我将主要省略这些,只关注主要的,因为它们通常比次要的更强。但我们必须时刻牢记,除了 conv,可能还有第二种,甚至更多的模式。过滤器检测)。**

conv1_4 的辅助功能

我们已经完成了 conv2_3 的一半。我将复制 conv2_3 解剖图,以便您可以少滚动一点:)

conv2_3 的切割激活

  • 第 5 行: w_5 通过在 w_5 网格中有一条与 x_5 (即“水平线的上侧”)匹配的强负水平线来否定 x_5 。我们将 a_5 的描述称为 neg(“水平线的上侧”)。
  • w_6 看起来像是检测竖线,但是不明确。a6看起来也不是很强,因此它对 y 没有显著贡献,因此我们也将忽略这一行。
  • w_7 有一条水平亮线,因此很可能检测到再次与其输入 x_7 (“水平线的下侧”)匹配的水平线。净贡献 a_7 是复制的 x_7 ,但与其他*a _ j相比,此贡献较弱,这可以从第 5 列中其褪色的颜色看出。因此,我们将该行标记为弱行(“水平线的下侧”)。*
  • 最后一行是迄今为止最重要的贡献,您可以从剖析的激活列中看到。它对净输出 y 的影响最大。A8的大小比其他A8的大得多,因为w8的大小(和对比度)比其他w8的大得多。从w8的网格中可以看到(如下所示),它在负值和正值之间具有非常高的对比度(周围有非常亮和非常暗的斑点),因此它有非常强的趋势更重要的是, w_8 试图检测的模式与其输入匹配(再次!) x_8 即“坡度为 135 度的线”。这只是加强了由 x_8 检测到的模式,并且我们在 a_8 中看到对斜率为 135 的线的非常强的选择性。

w_8。正值用红色圈出,负值用蓝色圈出。正负数值的排列形成一条斜率=135 的线。因此,该滤波器强烈响应斜率=135 的线

我们已经分析完了每一行。现在让我们把它们放在一起。

  • 我们忽略了第 1、2 和 6 行
  • 第 3 行是 neg("垂直线右侧")
  • 第 4 行是“斜率为 135 度线”
  • 第 5 行是负数(“水平线的上边”)
  • 第 7 行弱(“水平线的下侧”)
  • 而第 8 排是(“坡度为 135 度的线”)

y 变成 y = 0 + 0 + neg("垂直线右侧")+ "坡度 135 度的线"+ neg("水平线上侧")+ 0 +弱("水平线下侧")+ ("坡度 135 度的线")+偏

其中 0 表示行 1、2 和 6 的贡献可以忽略。让我们简化一下:

y = neg("垂直线的右边")+ neg("水平线的上边")+ weak("水平线的下边")+ strong ("斜率为 135 度的线")

其中来自第 4 行的贡献被归入第 8 行的贡献,我们忽略偏差,因为它只是将所有值移动了某个常数。如果我们看一下 y

左:y,右:A8

我们看到每行都有一些贡献,但它们的大小都不同。到目前为止,A8的相对大小是最强的,所以它最终支配了对 y 的其他贡献。并且 y 最终看起来很像 a_8 。ReLU 和 MaxPool 应用于 conv2 后, ya_8 会更加相似。所有负值在 ReLU 之后都会消失,所以我们不会看到 y 中的黑点。

从剖析 conv2_3 中我们发现了什么?两件事:

  1. 许多内部通道试图复制它们的输入,即 w_j 的模式通常与 x_j 的模式相似。当 w_j 检测到的模式与 x_j 的描述非常匹配时,我们在第 4、5、7 和 8 行看到了这种情况。
  2. 对总产量的强贡献只有一个。基本上所有其他行都是噪音,我们可以只处理最后一行。

conv2_3 只是一个例子。我们可以分析 conv2 在不同输入上的其他外部通道,您可以在 Colab 笔记本中完成。在解剖了其中一些之后,我发现这些 2 的发现在其他 conv2 外部通道中也基本成立。例如,参见 conv2_6 和 conv2_9 的 Colab 中检测水平线的解剖;以及检测垂直线的 conv2_13。

关于 conv2,我们一般能得出什么结论?基于这两个发现,只有一个来自内部通道的强贡献,并且该贡献模仿 conv1 的输出之一(因为内部通道复制其输入)。这意味着 conv2 的外部通道大部分时间的功能只是放大 conv1 的一个输出,conv2 的总输出最终只是一串放大的 conv1 输出。

我们可以完全省略 conv2 吗?我认为是的,因为 conv2 并没有在 conv1 的基础上增加任何新的东西。当然,有一个额外的冗余层可能是有益的,也许 conv2 层可以检测到我们无法从上面的分析中推断出的某些高频模式,但我认为对于像 MNIST 分类这样简单的任务来说,没有必要。

Conv3

现在,我们将剖析最终的卷积层。让我们比较一下 conv3 和 conv2 的权重分布。下图左侧是 conv2 权重的随机样本,右侧是 conv3 权重的随机样本。

左:conv2 砝码,右:conv3 砝码

如果你仔细观察权重,你可以看到 conv2 有很多权重(网格),形成类似 Gabor 滤波器的模式。其中一些如下所示

然而,conv3 的这类滤波器相对较少。相反,conv3 的大多数网格都有“像素爆发”,在灰色网格值中有一两个非常亮或非常暗的像素。这些过滤器类似于椒盐噪声。下面是一些例子

这种像素突发滤波器的目的是什么?理论上,当卷积的输入在相同位置具有类似突发时,在网格中的特定位置具有小突发的滤波器应该最大程度地激活。这可能意味着 conv3 像素突发滤波器在网格内的特定位置寻找来自 conv2 的激活。

爆发的位置可以用[ α,d] 来描述,其中 α 是角度(下方时钟指针的方向) d 是距中心的距离。此外,我们可以使用+或-符号来表示脉冲是正的还是负的。例如,上面的第二个过滤器查找 7:30 的激活。使用上面描述的符号,上面的过滤器寻找+[7:30,2√2],这意味着它在角度=7:30 点和距离 2√2 处寻找正激活。

conv3 输入(7x7)几乎与 conv3 权重滤波器(5x5)大小相同(conv3 输出大小为 3x3),这一事实支持了我们关于突发用于选择激活位置的假设。让我来解释一下。

在最常见的卷积情况下,卷积运算的输入维数比滤波器的维数大得多。例如,输入可能是 28x28,过滤器可能是 5x5。在这种情况下,滤波器只看到输入的一小部分,并且滤波器充当等变模式检测器。然而,对于我们的 conv3,输入大小(7x7)与滤波器大小(5x5)几乎相同。因此,滤波器具有几乎 100%的感受野,当您将滤波器覆盖在输入上,并考虑滤波器将对该输入做什么时,滤波器会在滤波器具有高值的位置寻找激活,这有点道理。

因此,当输入尺寸近似等于滤波器尺寸时,滤波器的功能从模式检测变为位置检测。这就是我们 SimpleConvNet 中卷积滤波器的双重性()——卷积用于检测模式及其位置。

*此外,请注意 conv3 之后有 ReLU 和 MaxPool (3x3 ),之后 MaxPool (1x1)的输出被传递到完全连接的层。因此,conv3 输出的重要之处在于 3x3 网格内的最大值。最大值和其他负值的位置被忽略。虽然在执行加法c _ j =σx _ k∫w _ k的中间步骤中,最大值和负值的位置很重要。

为了对 conv3 滤波器的位置选择性而非模式选择性进行某种确认,我们来看看几个合成输入的激活情况。

各种合成输入上的 conv3_21 输出

左边是合成输入,右边是 conv3 的第 21 个外部通道的输出。相同的图案(水平线)呈现在相同外部通道的不同位置。外部通道对水平线的响应不一致。这个外部通道的激活取决于图案的位置。当水平线在图像中间时,激活度最高。另一方面,conv3 的第 6 个外部通道检测图像顶部的水平线:

各种合成输入上的 conv3_6 输出

我们现在将重点关注 conv3_6。一些支持证据表明 conv3_6 在图像顶部寻找水平线:

  • 下图显示了 MNIST 最大程度激活 conv3_6(应用 ReLU 和 MaxPool 后)的前 20 个输入。

最大程度激活 conv3_6 的前 20 个输入

  • 下面也是来自 MNIST 的随机输入和它们相应的 conv3_6 激活。数字 9、6 和 5 的激活很强。但是对数字 7 的激活不是很强。我们将在后面看到,conv3_6 不仅在顶部寻找水平线,而且 conv3_6 还是其他检测器的组合。其中,有一些抑制其激活的“反模式”。数字 7 的对角线部分可能是抑制因素之一。当我们分析 conv3_6 如何处理数字 7 时,我们将对此进行验证。

  • conv3_6 的超级模拟如下所示。这不是 100%的结论,但至少有一个轻微的暗示,它正在寻找图片顶部的水平线

conv3_6 超级刺激

现在我们已经看到了一些证据,表明 conv3_6 在图像顶部寻找水平线,让我们分析一下外部通道是如何实现这种位置选择性的。我们将首先剖析数字 5,因为这是 MNIST 数据集中最大程度激活 conv3_6 的数字。下面是输入和分解的 conv3_6。

**

我们将查看每一行的权重(使用索引作为标识符),并使用前面定义的时钟符号

  • 索引 1 是不明确的,所以我们将跳过它。
  • 指数 2 寻找+[10,2√2]和-[8,√5]的模式。它的输入看起来像是数字 5 的垂直部分,所以这个内部通道喜欢 10 点钟方向的垂直线,但不喜欢 8 点钟方向的垂直线。
  • 索引 3 的输入有点模糊,所以我们将跳过这一个。
  • 指数 4 是对总产出的最大贡献之一。它有+[1,√5],+[9,2]和-[3,1]。它的输入是水平线检测器的输出,所以索引 4 喜欢 1 点和 9 点位置的水平线,不喜欢 3 点位置的水平线。我认为这解释了 conv3_6 如何检测顶部的水平线:1 点钟位置的水平线激励这个内部通道,9 点钟和 3 点钟位置的水平线分别激励和抑制激活,从而相互抵消。请注意,索引 7 与索引 4 有些相似之处。
  • 对于其他行,您可以遵循这个分析,因此我将跳过无聊的行,专注于最强的贡献。
  • 指数 9 在 12 点钟方向有一个巨大而明亮的斑点。它的输入也是水平线,所以我们看到顶部水平线的大贡献。
  • 指数 12 在中心附近有一个巨大的黑色斑点。它的输入也是水平线,因此我们看到来自中间水平线的**大贡献**。

总的来说,顶部的水平线有很大的贡献,中间的水平线有很大的不喜欢,不同位置的其他图案有一些小的贡献。

让我们快速地对数字 7 做同样的剖析。

**

上图中的亮点:

  • 索引 4 和 9 通过检测顶部水平位置再次起作用。
  • 指数 12 不喜欢靠近中心的水平部分,但在这个特定的图像中,这是一个错误的不喜欢,因为更靠近顶部的数字 7 的水平部分最终与权重中的负斑点重叠。
  • 索引 13 对数字 7 的对角线部分有一点贡献——它喜欢中心的对角线,不喜欢 1 点和 9 点位置的对角线。

我希望上面的 2 个剖析至少在某种程度上让您相信 conv3_6 更喜欢图像顶部的水平线,它通过在检测水平线的输入顶部叠加“像素突发”样式的过滤器(突发靠近网格顶部)来实现这种选择性。

**conv3 结论:解剖了很多 conv3 外通道,我学到了什么?一个 conv3 外部滤波器不仅仅代表某个位置的一种模式,而是不同位置选择的组合,不一定是同一模式。例如,conv3_i 可以在+[12,2],-[6,2]处寻找水平方向的组合;以及在中心具有 45°斜率的线和在+[3,2]处的垂直线。

我们在上面对 conv3_6 的两次剖析中也看到了这一点——除了顶部水平线的主要贡献之外,还有其他贡献,如 10 点钟方向的垂直线。conv3_6 总结了所有这些贡献。所以 conv3_6 是对各种模式的各种位置选择性的混合。

我们已经学完了卷积层,现在我们来看看全连接(fc)层。

完全连接的层

我们的 SimpleConvNet 在 conv3 之后有两个完全连接的层:fc4 和 fc5。

我们看到每个 conv3 神经元在不同位置检测不同模式的组合。我们希望 fc4 和 fc5 层以某种方式使用这些信息对图像进行分类。他们可能会组合这些特性,或者将每个组合分解成更有用的特性。

FC 层非常简单,只是一个矩阵乘法,但是很难理解。本质上,一个 FC 层只接受一个向量并产生另一个向量,其中输出向量中的条目是输入向量中条目的线性组合。此操作的二次方复杂性正好压倒了理解一个 FC 层的内部工作的任何尝试。

鉴于 FC 层包含太多参数,我没有尝试进一步分析它们。我的直觉是,conv3 层输出已经很好地将高维空间中的图像划分为不同的类别。FC 层只是扩大了类别之间的界限,这样就更容易对图像进行分类。

考虑 FC 层的另一种方式是,它们只是从 conv3 转换位置选择性的基础。也就是说,它们将位置选择性的交织组合转换为“标准”基础,其中有用的要素位于正交轴上。

如果我们在 FC 和 conv3 层上使用 PCA 和 t-SNE 等可视化技术,您可以看到 conv3 层确实已经很好地将数据集分成了不同的类,FC 层进一步改善了这种分离。以下是 PCA 和 t-SNE (3D)投影的屏幕截图:

  • 原始 28x28 输入图像。

原始投入预测

  • 1x1x24 conv3 输出。

conv3 预测

  • 1x1x16 fc4 输出

fc4 预测

  • 以及 1x1x10 fc5 输出,这是网络的最后一层(在 softmax 之前)。

fc5 预测

你可以在 Colab 笔记本中找到投影的互动 3D 版本。

解剖结论

我们看到 conv1 只是实现了一个非常简单的模式(线)检测器。它只检测不同角度的线条。Conv2 试图放大 conv1 的输出,因此它实际上没有增加任何新内容,我们可以忽略它。Conv3 为 conv1 检测到的模式选择位置。这是通过使用权重过滤器的双重性相当优雅地实现的。在 conv3 之后,我们能够在图像的不同位置检测不同角度的线条。但是这些检测并没有分开,也就是说,我们没有一个神经元(来自 conv3)只检测图像顶部的水平线,相反,它们是交织在一起的——con v3 的每个神经元都检测这种位置选择性的混合。

全连接(FC)层的工作是解开和/或组合这些混合物,以便将纠缠的混合物转换成用于分类的有用特征(如顶部、中间和底部的水平+10 点和 4 点的垂直组成数字 5)。FC 层可能使用某些组合/纠缠之间的相关性来对数字进行分类。

我相信我们 SimpleConvNet 的黑盒性质(无法解释决策)来自于 conv3 中位置选择的纠缠。如果我们能够解开位置选择性(就像 FC 层那样,但它们本身并不太容易理解),我们将会得到一个完全可以解释的网络。我在第二部分的目标是从零开始建立这样一个完全可解释的网络

从某种意义上来说,conv3 在向量不佳的地方选择了错误的基(它们是交织在一起的),我们的工作是通过选择我们感兴趣的方向变得正交的基向量来使 conv3 变得可解释,以便位置选择性变得完全交织在一起——孤立而不是位置选择性的混合。有没有一个矩阵可以将 conv3 原始输出转换成一个很好的基?这是一个我还没有探索过的问题。

手动构建网络

我们将在第二部分中建立手动检测 MNIST 数字的网络。在这里,我将简要概述第 2 部分的内容。

第 2 部分的一点小味道。

我们制作了一个手工分类器。我们可以省略 conv2,因此我们只需在手工分类器中用 conv3 构成 conv1。为了使这里的讨论与解剖讨论保持一致,我将继续使用相同的卷积数。也就是说,conv3 会直接跟在 conv1 后面(我不把 conv3 重命名为 conv2)。

更受约束的积木。我们用非常有限的版本模拟 conv1 和 conv3,这将使解释它们的决策变得容易。它们实际上可以自动化*——不需要人工检查!受约束的版本将是权重的参数化栅格,即在栅格上绘制的参数化曲线。*

例如,下图显示了具有各种斜率和西格玛参数的参数线。我们使用这些网格作为 conv1 层的权重。当我们训练 conv1 时,我们只允许调整 slope 和 sigma 等参数,而不是在通常的训练中对整个网格进行自由形式的调整。这样我们就可以使用参数自动解释con v1 的决策。

用作 conv1 权重的参数线。左:斜率=1.5,西格玛=0.3。右图:斜率=-0.5,西格玛=0.6

我们使用“参数突发”作为 conv3 的权重,con v3 也用几个参数进行参数化。训练时只允许调整参数。

参数突发用作 conv3 的权重。左:mean_x=0.6,mean=-0.7,sigma_x=0.7,sigma_y=0.7。右:mean_x=-0.4,mean_y-0.7,sigma_x=0.3,sigma_y=0.7。

使用这些权重的约束版本,我们首先构建一组 conv1 检测器,用于检测我选择的各种线。然后,我们在 conv1 之上构建 conv3 层,用于检测 conv1 模式的位置。然后,我们将 conv3 输出组合成一个数字。例如,要检测数字 5:

  • 使用参数线为水平线和垂直线构建 conv1 检测器。
  • 使用参数突发为图像顶部、图像中部、图像底部、图像 10 点钟位置和图像 4 点钟位置构建 conv3 检测器。
  • 将 conv1 检测器输入 conv3 检测器,并按如下方式合并结果

数字 5 =顶部(横线)+中部(横线)+底部(横线)+ 10 点(竖线)+ 4 点(竖线)。

这只是第 2 部分的一个预览。我正在撰写第 2 部分,敬请关注。如果您想在第 2 部分准备就绪时得到通知,您可以提供您的电子邮件

试试吧!

如果你想重现文章中的人物,这里有一个笔记本。您还可以调整代码或对您的网络进行类似的剖析。

可解释的监控:停止盲目飞行,监控你的 AI

原文:https://towardsdatascience.com/explainable-monitoring-stop-flying-blind-and-monitor-your-ai-4e27898f1b9?source=collection_archive---------47-----------------------

数据科学团队发现可解释的监控对于管理他们的人工智能至关重要

照片由布鲁斯·沃林顿

我们生活在一个前所未有的时代,短短几周时间,全球许多人和企业的生活发生了翻天覆地的变化。随着新冠肺炎在全球展开翅膀,夺走生命,我们看到失业率和小企业破产率创下新高。

那么,这种低迷如何影响一个 正在使用 AI 的企业

如今,AI 越来越多地被各行各业的公司应用,但 AI 并不是最容易操作的技术。大多数生产人工智能系统是随着时间的推移有机积累的专有、开源和基于云的技术的拼凑物。然而,在过去的几年里,出现了基于 GUI 的人工智能工具和开源库,以帮助不太倾向于内部构建、成功训练和部署人工智能模型的企业。

随着这些工具的出现,公司已经意识到培训和部署人工智能只是第一步- 然后他们必须监控和管理他们部署的模型,以确保无风险和可靠的业务成果。随着更高性能的黑盒模型的兴起,治理这些模型的需求变得更加必要,也更具挑战性。越来越多的公司认识到:

“训练和部署 ML 模型是相对快速和廉价的,但是随着时间的推移维护、监控和治理它们是困难和昂贵的。”

事实上,由于部署后输入数据的变化,模型的性能会随着时间的推移而降低,因此模型需要持续监控,以确保它们在生产中的保真度。虽然许多现有的监控技术提供了实时问题可见性,但它们通常不足以识别复杂人工智能系统中问题的根本原因。

缺乏反馈回路

大多数组织在发现生产 ML 系统的问题时已经太晚了,损害已经造成。在某些情况下,生产问题可能持续存在而未被发现,直到由 ML 系统驱动的最终业务指标下降。

今天的大部分人工智能是不受监控的(图片由作者提供)

企业可以通过监控领先指标(包括预测和特征漂移以及输入数据错误)来提前发现潜在问题,而不是依赖下游业务指标作为上游模型性能问题的指标。跟踪这些领先指标并能够识别意外变化,使 ML Ops 团队能够实时进行调查,而不是在事后进行调查。但是仅仅跟踪正确的度量标准只能解决一半的问题。一旦检测到转移,应尽快进行调查或根本原因分析。为了确保快速准确的根本原因分析,人工智能可解释性可用于帮助确定问题的根本原因以及应采取的行动过程(例如,根据新数据重新训练模型,修复数据管道)。

结合起来,跟踪模型性能问题的领先指标,利用 AI 可解释性帮助从业者理解问题背后的“为什么”,构成了一种新的范式,称为可解释的 ML 监控。

一个可解释的监控系统的架构(图片由作者提供)

传统监控解决方案中的差距

今天,有两种主要的方法来监控生产软件:

  • DevOps 使用服务或基础架构监控来获得广泛的运营可见性和服务运行状况。
  • 业务所有者通过遥测技术监控业务指标,以跟踪业务运行状况。

不幸的是,这些方法不适合 ML 系统,ML 系统的性能与传统软件系统的性能不同,具有不确定性,取决于各种因素,如季节性、新用户行为趋势以及通常非常高维的上游数据系统。例如,当新的假日季节到来时,一个功能完善的广告模型可能需要更新。类似地,一个在美国被训练用来显示内容推荐的模型,对于国际注册用户来说可能不太好。

AI 的出现带来了对模型性能监控的需求(图片由作者提供)

模型监控的独特挑战

1。模型衰减。与其他软件不同,ML 模型的性能会随着时间而衰减。对正确模型结果的监控,当可用时,提供即时的业务影响变更通知。能够监控模型衰退将帮助我们知道是否是时候刷新模型了。

模型性能随时间漂移(图片由作者提供)

2。数据漂移。尽管 ML 模型是用特定数据(如年龄 20-60 岁)训练的,但它们在生产中可能会遇到不同的数据(如年龄 60-80 岁),因此会做出次优预测。

漂移的类型(图片由作者提供)

3。数据完整性。业务数据是动态的,其构成也在不断变化。这可能会对 ML 模型产生不利的性能影响,尤其是对于自动化数据管道。在已部署的人工智能系统中,数据不一致常常会被忽视。

特征分布截图(图片由作者提供)

4。离群值。部署的 ML 模型可能会遇到远离训练分布的数据。这些异常值会导致难以全局调试的孤立的性能问题。实时查明这些问题有助于立即解决问题。检测异常值是一个具有挑战性的问题,因为有多种技术可以应用,并且多年来已经得到了很好的研究。它在 ML 模型性能的环境中变得更具挑战性,因为我们需要将异常值作为跨大量变量的多变量分析问题进行研究,并查看其对模型行为的影响,即确定它是否会导致模型行为异常。

模型监控截图(图片由作者提供)

5。****。即使在监测数据变化后,尽管进行了模型验证,但其对保护组的真实影响可能会发生变化,即 ML 模型在部署后可能会出现偏差。第一道防线可能是在训练过程中丢弃受保护的属性(例如,种族、性别等),但是由于与受保护的属性高度相关的其他特征,模型也可能表现出偏差。我们需要的是围绕公平性的模型的连续跟踪,其中这些度量是动态和实时计算的。偏见的定义(机会均等、结果均等等)可能因组织和问题的不同而不同,因为公平没有统一的定义。因此,我们应该能够支持一个可插拔的策略,并持续执行它来检测任何潜在的偏见问题。如果检测到偏差,深入分析原因以确定模型是否需要更换或是否存在数据管道问题非常重要。

车型评测截图(图片由作者提供)

什么是可解释的监控?

强大的人工智能监控系统需要与服务基础设施的模型集成,以防范上述 5 种运营挑战。它允许用户轻松查看实时监控的输出,以发现 KPI 和其他问题,或者对警报采取行动。调查已标记的操作性 ML 问题通常需要付出大量努力。ML 模型的黑盒性质使得 ML 开发人员很难理解和调试它们。

一个可解释的 ML 监控系统扩展了传统监控,提供了具有可操作步骤的深度模型洞察。通过监控,用户可以了解问题驱动因素、根本原因,并分析模型以防止问题重复出现。这有助于节省大量时间。

我们认为这样的系统应该展示 3 个关键特性:

****综合。一个可解释的 ML 监控系统应该覆盖模型性能和性能指标本身的所有主要指标。除了统计上的全面,一个理想的可解释的 ML 监控系统为技术(模型开发者,ML Ops)和非技术(分析师,企业所有者)利益相关者提供直观的用户界面。

****可插拔。团队应该能够将监控系统与现有数据和人工智能基础设施以及最常见的开源 ML 框架(Scikit-Learn、PyTorch、Tensorflow、Spark 等)相集成,以快速看到可操作的结果。

****可操作的。用户应该能够获得生产问题背后的可操作的见解。带有更深入分析的实时解释对于快速揭示模型行为的“为什么”和“如何”至关重要。获得大量警报会产生噪音,因此系统允许用户进行必要的控制,以便只为需要采取行动的班次配置警报,这一点非常重要。

与人工智能相关的金融风险是巨大的。信任是赌注的一部分,失去比得到更容易。我们已经看到像新冠肺炎这样的黑天鹅事件会给企业带来什么。如果你的人工智能产品不受监控,你可能会向你的客户传达错误的决策。可见性,因此,将是极其重要的!

这就是为什么我们决定开始提琴手。AI 并提供了我们认为可以帮助弥合差距的系统。欢迎发送电子邮件至 info@fiddler.ai ,了解更多信息。

原载于 2020 年 4 月 13 日https://blog . fiddler . ai**

可解释的建议——为什么打开黑匣子很重要

原文:https://towardsdatascience.com/explainable-recommendations-why-opening-black-boxes-matters-bd5754af63a2?source=collection_archive---------29-----------------------

可解释推荐系统介绍

无论是线上还是线下的市场,都充满了你可以购买的商品。扎里亚·赖特在 Unsplash 上的照片

这篇文章是我关于可解释建议系列文章的第一部分,基于我的 BSc 论文 第二部分 展示了一个可解释的电影推荐系统的实现,而 第三部分 讨论了事后可解释性在数据科学中的应用。

推荐系统帮助用户发现新的项目,并且它们已经在包括视频流、电子商务和社交媒体的各种应用中得到了越来越多的使用。推荐系统可以使用多种技术来构建,但是在这里我将集中讨论使用潜在因素模型(LFM)的协同过滤。协同过滤方法基于用户先前的评级产生推荐,其理论是,如果用户 AB 过去喜欢相似的项目,那么 A 很可能也会喜欢被B【1】【2】高度评级的其他项目。解释 LFM 如何工作超出了这篇博文的范围,但简单地说,它们将已知评分的稀疏矩阵分解为一组具有维度 d 的常见用户和项目因素。然后,这些因素可以被视为具有 d 维的向量,允许我们通过计算它们的向量的点积来预测用户 u 将给予项目 i 的评分。

LFM 可以由各种矩阵分解模型产生,例如 SVD。由于这些因素是从数字用户评级中学习的,而不参考项目本身,它们不直接映射到任何可解释的因素或类别——因此 LFM 被称为不可解释的黑盒模型。无法解释的模型是一个问题,原因有很多,也许最重要的原因是缺乏信任。如果系统不能告诉它的用户为什么做出一个特定的决定,为什么用户要相信这个决定呢?除了信任之外,表 1 显示了之前为可解释性确定的七个解释标准[4]。在医疗行业等领域,证明模型所做决策的合理性尤为重要,因为不正确的决策可能会带来灾难性的后果。虽然推荐系统通常用于不太严肃的应用,如在线商店和社交网络,但让推荐具有可解释性仍然很重要,因为已经证明用户更喜欢他们认为透明的推荐[5]。

表 1: 评价推荐系统可解释性的七个标准。改编自表 15.1,Tintarev,n .,& Masthoff,J. (2011)。设计和评估推荐系统的解释。《推荐系统手册》(第 479-510 页)。马萨诸塞州波士顿斯普林格。

对可解释建议的研究主要集中在两种方法上,嵌入式和事后解释[6]。在嵌入式方法中,解释生成与推荐模型本身集成在一起,而在事后方法中,不可解释的推荐在由原始模型通过白盒解释生成器生成后变得可解释。嵌入式方法的一个例子是显式矩阵分解(EMF ),其中 LMF 中的因素不是自动学习的,而是从文本用户评论中收集的。该系统为用户对每个项目特征的关心程度以及该特征对每个项目的描述程度构建矩阵。然后可以使用这些矩阵构建 LFM。通过简单地检查哪些因素项目和用户得分最高,该系统容易被解释和说明,同时在预测准确性方面保持良好的性能。[7]

事后解释器可以通过从推荐模型的输入和输出中提取逻辑关联规则来构建。提取的规则具有{X => Y}的形式,其中 X 是用户体验偏好的项目,Y 是推荐。问题中的关联规则可以用文字表达为“因为你喜欢 X,所以我们推荐 Y”。通过将两个模型产生的推荐标记为“可解释的推荐”,关联规则与黑盒模型相结合[8]。

嵌入式解释自然倾向于具有良好的可解释性,因为解释和推荐模型是不分离的。与事后方法不同,嵌入式解释不是模型不可知的,需要特定类型的模型(如 LFM)或领域(如学术研究论文或文本评论)才能发挥作用,因此灵活性较低。

可以用定量和定性的方法对这些解释进行评估。定量方法评估系统的客观的、可测量的特征,例如生成解释所花费的时间、可解释的建议的准确性以及可解释的建议的份额。最后一个指标被称为模型保真度,它被认为是某些事后解释的关键指标之一[8]。还应该对这些解释进行定性评估。这通常是通过使用一个或多个解释标准(表 1)作为度量标准[4]的用户研究或 A/B 测试来完成的。例如,如果解释的目标是增加用户对系统的信任,用户研究可以比较一组看到解释的用户是否比只看到建议的控制组对系统更信任。

亚马逊在他们的系统中使用了多种解释。首先,一些推荐被标上一个句子,描述它是如何被发现的,例如“购买了这个商品的顾客也购买了…”。亚马逊还为用户引入了一项功能,可以影响一项购买对他们推荐的影响程度,或者是否应该完全过滤掉。这种解释提高了系统的可检查性,并且可以用于过滤掉作为礼物购买的物品。用户还可以看到他们之前的哪些购买或评级影响了推荐。[9]

Amazon.com 解释了为什么向作者推荐类别“咖啡、茶和饮料”,包含作者在过去购买的物品。该小部件还允许排除一个项目(例如,如果它是一个礼物或者用户不喜欢它),从而提高系统的可检查性。

脸书有一个解释功能,告诉用户为什么他们会看到一个帖子,以及为什么帖子会以原来的方式排序。用户还可以看到为什么向他们显示广告的详细信息,包括广告制作者如何与他们的个人数据进行交互的时间表。通过发布这一功能,脸书希望增加其产品的透明度,并帮助用户控制自己的新闻,即提高产品的可审核性。[10]此外,中国电子商务平台京东使用特征-观点对的词云作为解释,作为显式矩阵分解研究的一部分,这是一种基于内容的解释。研究人员发现,看到新解释的用户的点击率(CTR)明显高于那些收到一般“人们也看过……”解释或根本没有解释的用户。[7]

虽然可解释的建议在过去几年的研究中受到越来越多的关注,但很少有解决方案被部署到工业生产中。本系列的下一部分将介绍我使用两个事后解释器实现的一个可解释的电影推荐系统,包括离线评估和通过用户研究的评估,以及如何将解释部署到实际系统中的讨论。

以下参考资料与这篇文章的内容直接相关。我的论文中列出了整个项目的完整参考书目。

[1]:y .科伦和 r .贝尔(2015)。协同过滤的进展。在推荐系统手册(第 77–118 页)中。马萨诸塞州波士顿斯普林格。

[2]:里奇,f .,罗卡奇,l .,&沙皮拉,B. (2011)。推荐系统介绍手册。在推荐系统手册(第 1–35 页)。马萨诸塞州波士顿斯普林格。

[3]:y .科伦、r .贝尔和 c .沃林斯基(2009 年)。推荐系统中的矩阵分解技术。电脑,(8),30–37。

[4]:廷塔列夫,n .,&马斯托夫,J. (2011)。设计和评估推荐系统的解释。在推荐系统手册(第 479–510 页)中。马萨诸塞州波士顿斯普林格。

[5]: Sinha,r .,Swearingen,k .:透明度在推荐系统中的作用。摘自:计算系统中人的因素会议,第 830–831 页(2002 年)

[6]:王,x,陈,y,杨,j,吴,l,吴,z .,&谢,x(2018,11 月)。用于可解释推荐的强化学习框架。在 2018 IEEE 数据挖掘国际会议(ICDM) (第 587–596 页)。IEEE。

[7]:张,杨,赖,高,张,米,张,杨,刘,杨,马等(2014 年 7 月)。基于短语级情感分析的可解释推荐的显式因素模型。《第 37 届国际 ACM SIGIR 信息检索研究与发展会议论文集》(第 83-92 页)。ACM。

[8]:皮克,g .,&王,J. (2018 年 7 月)。推荐系统潜在因素模型的事后可解释性。《第 24 届 ACM SIGKDD 知识发现和数据挖掘国际会议论文集》(第 2060–2069 页)。ACM。

[9]:Amazon.com。(2010).完善你的推荐。检索 2019 年 10 月 2 日,来自https://www . Amazon . com/gp/help/customer/display . html/ref = HP _ 16465201 _ FAQ _ recommendations?nodeId=13316081

[10]: Sethuraman,R. (2019 年 3 月 31 日)。为什么我会看到这个?我们有一个答案给你。检索于 2019 年 10 月 2 日,发自 https://newsroom.fb.com/news/2019/03/why-am-i-seeing-this/。

用 SHAP 解释 CNN 生成的土壤地图

原文:https://towardsdatascience.com/explaining-a-cnn-generated-soil-map-with-shap-f1ec08a041eb?source=collection_archive---------28-----------------------

这是我致力于深度学习在土壤科学中的应用的系列文章的第四篇。这是一个正在进行的系列,到目前为止还包括:

[## 深度学习和土壤科学-第 1 部分

预测土壤性质的土壤光谱。根据光谱图预测多种土壤特性的多任务卷积神经网络。

towardsdatascience.com](/deep-learning-and-soil-science-part-1-8c0669b18097) [## 深度学习和土壤科学—第二部分

使用上下文空间信息的数字土壤制图。从点信息生成土壤图的多任务 CNN。

towardsdatascience.com](/deep-learning-and-soil-science-part-2-129e0cb4be94) [## 深度学习和土壤科学—第 3 部分

土壤光谱学与迁移学习。将大陆土壤光谱模式“本地化”到国家范围。

towardsdatascience.com](/deep-learning-and-soil-science-part-3-c793407e4997)

在我之前的一篇文章中,我解释了如何使用空间模型来生成地图。我还介绍了使用多任务卷积神经网络(CNN)来更好地表示土壤形成过程的复杂性。CNN 使用点状土壤观测周围的像素窗口作为输入,而不是截取其位置的单个像素,以结合空间背景。此外,由于其多任务设计,该模型能够同时预测多种土壤属性,并考虑到它们之间的相互作用。与更传统的 ML 模型(立体树模型)相比,这两个新特征的引入产生了大约 30%的误差减少。

你可能知道深度学习如何在许多领域表现出优异的性能,但与此同时,它们也引起了人们对其可解释性和潜在偏见的担忧,特别是因为这些类型的算法开始被应用于在具有高度人类影响的领域中辅助决策。

为了解决可解释性问题,在这篇文章中,我展示了我最新研究的一些结果,在这些研究中,我应用 SHAP 解释了一个用于数字土壤制图的多重谈话 CNN。主要目的是证实模型捕捉了目标土壤特性和用于训练模型的协变量之间的合理关系。

博弈论与 SHAP 价值观

从博弈论的角度来看,建模可以被视为一个合作游戏,其中预测者(代理或参与者)战略性地相互作用,以实现预测输出的目标。因此,每个代理都会收到与其贡献成比例的支付。

SHAP 是一种逼近每个预测因子的边际贡献的方法。关于如何估计这些值的详细信息,您可以阅读我的出版物 Lundberg 和 Lee (2017)的原始论文,或这篇文章中的直观解释塞缪尔·马赞蒂

为了进行分析,我使用了 Scott Lundberg 的 shap python 库,他是该方法的创造者之一。

模型描述

讨论中的模式是一个多任务 CNN,被训练来同时预测智利五个深度间隔(0-5、5-15、15-30、30-60 和 60-100 厘米)的土壤有机碳(SOC)含量。为了训练 CNN,我使用了形状为(29,29,5)的 3D 阵列,代表每个观察点周围的 29x29 像素窗口,用于解释 SOC 的空间分布的五个协变量。这个简单模型中包含的五个协变量是:海拔、坡度、地形湿度指数(TWI)、长期年平均温度(MAT)和年总降水量(TAP)。

图 1 。多任务网络的结构。“共享层”表示所有深度范围共享的层。每个分支(每个深度范围一个)首先将信息展平为 1D 阵列,随后是一系列 2 个全连接层和大小=1 的全连接层,这对应于最终预测。

结果

本地和全球解释

如果你熟悉 SHAP,你可能见过下面的一些图。首先,我们可以通过每个协变量的贡献获得局部解释(单个预测)。在下图中,我们可以看到两种不同观测的力图:顶部面板对应于海拔和坡度具有负作用(它们促进侵蚀)的山区。底部面板对应于相反情况的山谷。两个观测都位于智利南部,与中部和最北部地区相比,那里更冷更湿的条件促进了有机碳的积累,因此温度和降水的贡献是积极的。

图二。两个不同样本的力图。图中显示了正(绿色)和负(紫色)的总贡献,它们使 SOC 含量偏离了预期的基本 SOC 值(整个数据集的预测平均值)。

考虑到协变量对所有样本的贡献,使用 SHAP 也可以获得模型的全局解释。下图显示了每个协变量的相对重要性,其中气候对地形相关协变量的影响更大(SHAP 值范围更广)。这是一个国家 SOC 模型,智利有很强的南北气候梯度,所以 TAP 和 MAT 的影响最大也就不足为奇了。在更局部的层面上,地形开始发挥重要作用。下图显示的另一件事是,有机碳与降水量呈正相关,与温度呈负相关,这也是一种预期行为。

图 3。CNN 模型的每个协变量和土壤深度区间的 SHAP 值。TAP:年总降水量;MAT:年平均温度;地形湿度指数。

相互作用

还可以进一步检查特定协变量的影响,并评估模型是否捕捉到了它们之间的相互作用。例如,温度的积极贡献在 4 至 8℃之间达到峰值,而负面贡献在 12℃以上。鉴于该国的气候条件,MAT 贡献的减少与降水量的减少相关,这在高温地区(图 4a)更为突出,那里的碳输入量低。降水量的贡献(图 4b)显示了一个确定的趋势,在⁻大约 1000mm 年时,基本上是恒定的负响应,在⁻大约 1400mm 年时开始增加,变成正响应。该模型还捕捉到了有机碳含量和海拔之间的反比关系(图 4c ),鉴于该国的地形,较高的值通常与较陡的地形相关。一般来说,这种影响在 TAP 高于 400 毫米/年⁻的地区会加剧。

对于温度和降水,其贡献变为正值的阈值(分别约为 12°c 和 1400 毫米/年⁻)与该国境内的一个重要区域(南纬 38°左右)相一致,在该区域,砖红壤(“火山”土壤)变得更加普遍,土壤水分状况从干旱变为湿润(变得更加湿润),与有机碳含量的急剧增加相关。就似乎改变了海拔(大约 400 毫米/年⁻)贡献行为的 TAP 阈值而言,它大致对应于该国干旱和半干旱地区之间的过渡(变得更加湿润),在这种情况下,水的侵蚀过程开始变得更加重要。

图 4。SHAP 值和所选协变量之间的相关性图。a)SHAP 值和年平均温度(MAT)之间,显示与年总降水量(TAP 色标);b)SHAP 值和年总降水量之间的关系,显示与年平均温度的相互作用(色标);以及 c)SHAP 值和海拔之间的关系,显示了与年总降水量(色标)的相互作用。

空间解释

因为我们讨论的是一个包含每个样本周围环境的空间模型,所以我们也可以从空间上评估贡献。下图显示了与图 2 相同的两个位置,但这次分解了每个像素的贡献。解释是一样的,但理论上,我们应该能够识别更有影响力的景观的具体特征。

图五。两个不同样本(来自图 2 的相同样本)中每个协变量的 SHAP 值的空间分布。

也许对我来说最有趣的输出(我主要使用地图)是可以聚集本地空间贡献来显示更一般的空间解释并进一步证实之前的解释。下图显示了 SHAP 值的地图,其中可以区分靠近海岸的山谷,与周围区域相比,该山谷具有较低的坡度和坡度以及较高的 MAT。与预期的平均输出相比,这些条件对模型输出有负面影响。当然,即使这些趋势在有机碳过程方面有意义,它们也可能与人类活动(农业)的影响相互作用,这在该模型中没有考虑。

图六。CNN 模型中每个协变量的 SHAP 值的空间分布。每个像素的值是该像素被用作上下文的所有实例的平均值(对于 29×29 的上下文窗口高达 841 次)。TAP:年总降水量;MAT:年平均温度;地形湿度指数。

最后的话

这项研究的结果表明,有可能在数字土壤制图中建立可解释的深度学习模型,通常被视为黑盒的复杂模型可以使用 SHAP 值进行检查。这不仅对于证实模型被适当地训练以及它捕获目标属性和预测器之间的合理关系是必要的,而且它也是导致知识发现的过程的基本部分。

引用

关于这项工作的更多细节可以在相应的论文中找到。

帕达里安,j .麦克布拉特尼,A. B .和米纳斯尼,B. 2020。数字土壤制图卷积神经网络的博弈论解读,土壤讨论,https://doi.org/10.5194/soil-2020-17.

注 04/04/2020: 该文件尚未被接受,正在接受公众审查和讨论。欢迎您参与这一过程。

参考

  • Lundberg,S.M .和 Lee,S.I .,2017 年。解释模型预测的统一方法。在:神经信息处理系统进展 30 第 4765-4774 页。

向孩子们解释人工智能

原文:https://towardsdatascience.com/explaining-ai-to-children-c9b2ecab1ffc?source=collection_archive---------36-----------------------

如何让我们的年轻一代为明天的挑战做好准备!

伯纳德·赫曼特在 Unsplash 上的照片

向儿童解释人工智能音频版

人工智能(AI)可能会让许多人望而生畏,更不用说孩子了。正因为如此,用他们能理解的具体例子来解释它是很重要的。这可以通过三个简单的步骤轻松实现;给人工智能下一个定义,提供例子并给他们指出可以探索的有趣资源。

首先,我们从定义开始。人工智能可以被定义为一个计算机程序,它能够执行一项需要智能的任务。这项任务通常是人类或智能动物能够完成的,例如学习、计划、解决问题等。

第二,展示 AI 最明显的例子是在游戏中。孩子们是玩游戏的专家,这可能是任何年龄的孩子最喜欢的活动。

照片由基里尔·沙尔科夫斯基Unsplash 拍摄

一个有趣且无需介绍的经典游戏无疑是吃豆人。游戏发生在一个迷宫里。玩家控制吃豆人,吃豆人的任务是吃掉迷宫中所有的点,同时避开四个彩色的幽灵。有意思的是,四个鬼都有自己的人格,由中央 AI 系统控制。Blinky(红色)是领导者,并始终遵循吃豆人。Pinky (pink)是一个缓慢的幽灵,它试图通过预测 Pac-Man 可能去的地方来伏击他。Inky(青色)是一个害羞的幽灵,倾向于跟随 Blinky。最后,克莱德(橙色)是一个胆小鬼幽灵,它在吃豆人接近时逃跑了。吃豆人是一个简单的游戏,但人工智能的行动可以在实践中看到,很容易理解。在大多数游戏中都可以找到类似的 AI 机制。在足球游戏中,如国际足联,对方球队是由人工智能控制的。在模拟游戏如“模拟人生”中,大多数其他角色都由人工智能处理。几乎所有的游戏都使用人工智能!

最后,在理解了它是什么并意识到我们都经常使用它之后,也是时候享受它的乐趣了。网上有大量资源可供教育者和孩子们使用。以下是一些最有趣的不完全列表:

与机器聊天

  • 伊莱扎是第一批被创造出来的聊天机器人之一(1964)。它有些受限;不过,和它聊天还是挺好玩的。可以在这里访问https://tinyurl.com/AIEx-Eliza
  • Mitsuku 是目前存在的最先进的聊天机器人之一。它获得了各种奖项,可以谈论大多数话题。它可以位于 https://tinyurl.com/AIEx-Mitsuku这里

处理语言的机器

  • 情感分析器将一个典型的句子作为输入。然后它决定了这个句子是正面的、负面的还是中性的意思。该计划可以在这里找到https://tinyurl.com/AIEx-Sentiment
  • Thing Translator 给一个物体拍照,用自然语言(如西班牙语、意大利语等)给我们描述。)我们的选择。这个演示可以在https://tinyurl.com/AIEx-Translator找到

参见的机器

  • 句子生成器获取一个图像作为输入,并为该图像创建一个标题。它通过理解组成图像的对象并给它们一个标签来做到这一点。演示可以在这里访问https://tinyurl.com/AIEx-See
  • 表情符号寻宝游戏使用人工智能通过移动设备的摄像头来识别现实世界中的表情符号。游戏可以在这里玩https://tinyurl.com/AIEx-Emoji

玩机器

  • 快,画!是一种游戏,用户必须画一些东西,人工智能必须猜它是什么。游戏可以在这里玩https://tinyurl.com/AIEx-Draw
  • 这张智能纸展示了一个程序如何玩井字游戏,并通过遵循简单的规则而获胜。游戏的说明可以在https://tinyurl.com/AIEx-TicTacToe找到

科林·阿姆斯特朗在 Unsplash 上拍摄的照片

人工智能非常有趣,教授人工智能的资源在网上随处可见。此外,在过去几个月里,马耳他大学信息通信技术学院人工智能系发布了一本题为“教育中的人工智能——教师和年轻人实用指南”的书。在教育部的支持下,这本书免费分发给所有学校。它包含如何向任何年龄的儿童教授人工智能概念的进一步例子和有趣的练习。马耳他教师联盟也组织了一个关于"教室中的人工智能"的短期课程,旨在帮助教育工作者理解人工智能,并随后在课堂上使用它。有很多资源可以让你在课堂上开始使用人工智能。

人工智能很可能是人类发明的最强大的工具。它被许多人戏称为新的电力。正因为如此,设计未来的唯一途径就是从事 AI 工作。这就是为什么我们必须教导年轻一代接受它,并把它作为一种工具。只有这样,我们才有希望让我们的年轻一代为明天的挑战做好准备!

原载马耳他教师联盟。请在下面留下你的想法。如果你喜欢这篇文章,请跟我来🐦推特,🔗领英📷 Instagram 或者😊脸书。****

** [## 140 个网站帮助您的孩子学习几乎任何东西!

优质教育可以是免费的,网络上充满了免费资源。

medium.com](https://medium.com/@alexieidingli/140-websites-to-help-your-child-learn-almost-anything-55474d54a420) [## 冠状病毒封锁下的儿童有趣的人工智能活动

冠状病毒正使整个地球陷入停顿,因为孩子们将有充足的时间在家与他们的…

towardsdatascience.com](/fun-artificial-intelligence-activities-for-kids-under-coronavirus-lockdown-7177969a2ba1) [## 使用人工智能提升我们的教育系统和劳动力

在任何国家,最重要的资源是人力资本。熟练的劳动力和运转良好的教育系统…

towardsdatascience.com](/boosting-our-educational-system-and-our-workforce-using-ai-a676afacb9cd)

阿列克谢·丁力教授 是马耳他大学的 AI 教授。二十多年来,他一直在人工智能领域进行研究和工作,协助不同的公司实施人工智能解决方案。他的工作被国际专家评为世界级,并赢得了几个当地和国际奖项(如欧洲航天局、世界知识产权组织和联合国等)。他出版了几本同行评审的出版物,并且是马耳他的一部分。由马耳他政府成立的人工智能工作组,旨在使马耳他成为世界上人工智能水平最高的国家之一。**

向管理层解释人工智能用例——我的四个关键信息

原文:https://towardsdatascience.com/explaining-an-ai-use-case-to-management-my-four-key-messages-e6f4583a0d82?source=collection_archive---------77-----------------------

你在你的组织中负责启动人工智能项目吗?你需要向管理层解释你的人工智能用例吗?

在之前的帖子中,我已经写了我如何一步一步地进行填鸭式管理。一旦他们理解了人工智能的要点,而我有一个人工智能用例要在内部追求。是时候召开第一次会议来介绍我的用例了。在这次会议中,我保持简单,我用四个关键信息来解释用例。

Austin DistelUnsplash 上拍摄

信息 1:一句话的解决方案

我用一句话写下解决方法。我的解决方案到底能做什么?一句一句写下来的过程有助于我找到正确的解释,让他们更容易理解。我确保把重点放在事情的商业方面。

一个很好的例子:“自动接受/拒绝信贷申请”

一个不好的例子:“预测还贷的逻辑回归”

想想你的经理会怎么想。在第一个例子中,他会想“太好了。我可以节省成本”。在第二个例子中,他会认为“嗯,有趣”。

信息 2:这个人工智能项目的好处

第二步,我讲这个项目的好处。收益可以是硬性因素,如节省时间、提高投资回报率、节约成本或降低某种风险。有时候利益是软性的,这也没关系。为了让我的管理层对一个用例真正感兴趣,我一定要提到他们与奖金挂钩的 KPI。

信息 3:这个项目需要的数据

人工智能项目需要数据,但我的管理层可能没有完全理解这一点,或者没有想到那么远。在这一步,我告诉他们我将需要什么数据,数据来自哪里,以及获取这些数据的难易程度。这真的让我的案子生动起来。一旦他们理解了什么将进入模型,它也开始对管理层变得有形。我发现这种方法引发了具体的讨论,并帮助他们想出其他我可能能够纳入的想法。

信息 4:关键成功因素

我们都经历过,管理层对我们期望过高。在大多数人工智能项目中,都会遇到障碍。我会思考前面的这些障碍,并将它们转化为关键的成功因素。项目的第一阶段是我为管理层设定正确期望的时候。当出现我事先预料到的问题时,这将极大地帮助我。

当我第一次与管理层一起启动一个人工智能项目时,这是我得到的四个关键信息。大多数时候,一次会面是不够的,我会经历一系列的互动,直到信息落地。当消息到达后,就该进行下一步了。

耐心点,这可能比你预期的要长。

关于我:我是一名分析顾问,也是当地一所商学院“人工智能管理”研究的主任。我的使命是帮助组织利用人工智能创造商业价值,并创造一个数据科学家可以茁壮成长的环境。 在这里报名我的简讯。

解释“黑箱”ML 模型——SHAP 的实际应用

原文:https://towardsdatascience.com/explaining-blackbox-ml-models-practical-application-of-shap-f05f986863cf?source=collection_archive---------44-----------------------

在真实数据集上训练“黑盒”GBM 模型,并使其可以用 SHAP 解释。

动机

GBM 模型已经作为强大的模型经受了战斗的考验,但是由于缺乏可解释性而被玷污了。通常,数据科学家会查看可变重要性图,但这不足以解释模型是如何工作的。为了最大化模型用户的采用,使用 SHAP 值来回答常见的可解释性问题,并建立对模型的信任。

在这篇文章中,我们将在一个简单的数据集上训练一个 GBM 模型,你将学习如何解释这个模型是如何工作的。这里的目标不是解释数学是如何工作的,而是向非技术用户解释输入变量如何与输出变量相关,以及如何进行预测。

我们正在使用的数据集是由 ISLR 提供的广告数据集,你可以获得在 d6t GitHub 上使用的代码。

首先,构建高效的数据科学工作流

正如在数据科学家犯的 10 大编码错误中所解释的,为了创建高效的数据科学工作流,我们将使用文件管理器 d6tpipe 和工作流管理器 d6tflow

api = d6tpipe.APIClient()
pipe = d6tpipe.Pipe(api, 'intro-stat-learning')
pipe.pull()

预处理工作流:

**class** **TaskProcessRawData**(d6tflow.tasks.TaskPqPandas): **def** run(self):
        df = pd.read_csv(pipe.dirpath/'Advertising.csv', usecols=[1,2,3,4])
        self.save(df)@d6tflow.requires(TaskProcessRawData)
**class** **TaskFeatures**(d6tflow.tasks.TaskPqPandas):

    **def** run(self):
        df = self.inputLoad()
        df['target']=df['Sales']
        df['radio']=-df['radio'] *# force negative relationship*
        df['tv_radio']=df['TV']*df['radio'] *# interaction effect*
        self.save(df) 

第二,不要在对模型应该做什么没有直觉的情况下建立模型

正如在数据科学家犯下的十大统计错误中所解释的,你想要对模型应该如何工作形成一种经济直觉。

广告数据集显示销售是电视、广播和报纸广告支出的函数。通过观察散点图,我们可以看出电视和广播是有效的营销渠道,因为销售与这些渠道的支出密切相关。但是注意,广播有负面影响,而电视有正面影响(NB 我们强行与广播建立了负面关系)。报纸似乎只有很小的影响。

sns.lmplot(x="value", y="target", col="variable", data=dfp)

现在训练一个“黑盒”ML 模型

GBM 模型已经作为强大的模型经受了战斗的考验,但由于缺乏可解释性而受到了污染。让我们训练模型,看看我们如何解释它。我们将使用 LightGBM

m_lgbm = lightgbm.LGBMRegressor()
m_lgbm.fit(df_trainX,df_trainY)

为什么可变重要性图不起作用

顾名思义,变量重要性图告诉您输入和输出变量之间的关系强度。但是要信任一个模型,用户通常想知道更多:影响是积极的/消极的,线性的/非线性的?是否有成功/失败的时候?他们想看看这个模型是否符合他们的经济直觉。

在我们的例子中,重要性图并没有显示报纸消费与我们确定的负相关。这将使模型用户感到困惑,他们不会相信仅基于该图的模型。

lightgbm.plot_importance(m_lgbm)

与 SHAP 一起解释模型

SHAP 让你可以看到每个输入对输出变量的方向性影响,这给了模型用户关于模型如何工作的重要直觉。

如您所见,该模型确实显示了电视支出越高,销售额越高;广播支出越高,销售额越低。这符合我们的直觉,让模型用户相信你的模型做了正确的事情。

explainer = shap.TreeExplainer(m_lgbm, df_trainX)
shap_values = explainer.shap_values(df_trainX)shap.summary_plot(shap_values, df_trainX)

您还可以研究散点图中的“拟合”值,以更详细地显示输入和输出变量之间的关系。这对于更复杂的关系和理解交互效应是有用的。

在这种情况下,关系是线性的,电视和广播之间存在交互作用(您可以通过运行 OLS 并包含tv_radio变量来确认)。

**for** col **in** cfg_col_X:
    shap.dependence_plot(col, shap_values, df_trainX)

部分依赖情节呢?

那些也有用。优点是它们的输出与实际的目标变量具有相同的规模。这与 Shapley 值不同,Shapley 值显示了相对于平均预测的边际影响。

plot_partial_dependence(m_lgbm, df_trainX)

解释最新的预测

这是实践中经常遇到的另一个问题:您已经训练了模型,并使用最新数据进行了预测。模型用户不仅想知道预测值,还想知道为什么模型会做出这样的预测。SHAP 可以解释单个预测,也可以解释每个输入变量如何影响整体预测。

这里我们可以看到最新的预测值是【13.18】。电视提高了这一预测,而广播则降低了这一预测。报纸几乎没有影响。这符合我们的预期。

shap.force_plot(explainer.expected_value, shap_values[-1,:], df_trainX.iloc[-1,:])

从哪里了解更多信息?

了解 SHAP

构建高效的数据科学工作流

利用 Databolt 加速数据科学

如何向朋友和家人解释数据科学

原文:https://towardsdatascience.com/explaining-data-science-to-friends-and-family-15af317ec3ed?source=collection_archive---------33-----------------------

当数据科学家谈论他们的职业时

车头拍摄

Y 你在谈话中提到了你作为数据科学家的工作;几乎是瞬间,一种尴尬的感觉开始从你的胃里升起。当你提到你的职业时,你要为几乎总是被问到的问题做好准备。尽管之前已经回答过这个问题很多次了,你仍然觉得你的听众离开时困惑多于知情。当你站在那里考虑这一次不同的回答方式时,这个人提出了一个问题:

那么,数据科学家是做什么的呢?

现在怎么办?你仍然希望保持他们的兴趣,不要用技术细节来烦他们。你也不想给人一种“我认为我比你聪明”的印象,给人一种居高临下或居高临下的感觉。那怎么办呢?怎么简单解释什么是数据科学家?我们退一步,先解释一下什么是数据科学。

面向所有人的数据科学

如果你需要向普通人解释数据科学家的职业,你可能首先需要解释什么是数据科学。数据科学可以简单地描述为一种科学地测试数据以获得洞察力和结论的方法。用于测试这些数据的方法和技术是大量的,但对于主题的简要描述来说可能不是必需的。

解释数据科学

数据科学大量涉及编码,通常使用 Python 或 T9 的编程语言,但最近主要是 Python。它与统计分析和建模有关。有了这两个统计概念,就可以用正确的数据做出结论和应用。

数据科学中的“数据”

什么是正确的数据?这个数据是如何实现的?大多数数据来源来自公司或个人管理的大型数据库。如今,许多企业一直在利用数据科学来分析他们的大量数据,以便做出战略决策或获得对自己业务的洞察力。

其他数据记录可以从一个名为“ web-scraping 的过程中收集,该过程涉及使用一种编程语言,在这个特定情况下是 Python,来导航到不同的网站。在每个网站上,可以从网页上的各个部分获取数据。

当我们拥有所有需要的数据时,我们就可以继续使用数据科学来分析这些数据并从中得出结论。

让数据科学变得有趣

好了,现在你知道了数据科学基础的解释,让我们试着让它听起来更有趣。你可以将人们的注意力引向人工智能AI 。作为一名数据科学家,你主要关心的问题之一是与人工智能合作。大多数人听说过人工智能,并对这个想法超级着迷,这要感谢科幻媒体,如终结者系列。人工智能是当今的一个热门词汇,仅仅提到你处理它的应用肯定会引起一些人的惊讶。

随着人工智能的应用,你可以更深入地研究数据科学剧本,并提出机器学习。机器学习是另一个将迅速抓住人们注意力的时髦词,因为他们中的许多人也听说过这个概念。

人工智能和机器学习的这些流行语可能能够抓住人们的注意力,但你的大部分观众可能甚至不知道它实际上是什么。许多人听说过这些术语,但是你又一次陷入了试图在简短的对话中解释一个复杂主题的困境。

你也许可以简化人工智能和机器学习,只要说它是计算机自动化和推进现代技术的应用。总而言之,这些概念应该能够独立成为引人注目的话题,足以吸引所有从事数据科学家职业的人。

数据科学家如何帮助一家公司?

另一个可能出现的问题是,你如何为公司的底线做出贡献。我们简要地讨论了数据科学家如何处理公司的大型数据库。你可以提出,作为一名数据科学家,你在公司的主要角色是如何对公司多年来收集的数据提出可行的见解。

这家公司收集的数据范围广泛,从客户对服装的偏好到他们是否在特定节目中点击“下一集”。这些数据包含大量需要处理的信息,这正是您作为数据科学家的职责所在。

您能够将这些数据处理成可展示的东西。你将会以这样一种方式展示它,在这种方式下,你和公司可能会在数据中发现以前可能没有注意到的新关系。根据这些关系,公司可以做出一些战略决策来增强或减弱数据中的这些联系。另一件你可以为公司提供价值的事情是通过开发从检测欺诈交易到预测销售价格的应用程序。

最终,数据科学家在公司的未来决策中扮演着重要的角色。大多数大公司近年来收集了如此多的数据,以至于他们需要数据科学家来帮助组织和分析这些数据。随着越来越多的数据被创建和收集,对数据科学家的需求也将持续增长。

结束语

如您所见,有许多方法可以消除数据科学复杂主题的复杂性。这可能听起来令人生畏和困惑,但如果你把它分解成更小的部分,它似乎更容易消化。机器学习和人工智能等数据科学中更具传统吸引力的部分肯定会吸引一大批人。每个人每天都在使用技术,都会对这两个术语有所了解。

只要你不表现出居高临下的姿态,你应该能够毫不费力地向普通人解释数据科学和你的职业。希望现在你不会纠结于描述你对家人和朋友做了什么。也许你甚至可以激励他们中的一些人成为数据科学家!

解释 DBSCAN 集群

原文:https://towardsdatascience.com/explaining-dbscan-clustering-18eaf5c83b31?source=collection_archive---------2-----------------------

使用 DBSCAN 识别员工子组

图片由 Ishan @seefromtheskyUnsplash 上拍摄

带噪声应用的基于密度的空间聚类(DBSCAN) 是一种无监督聚类 ML 算法。无监督的,因为它不使用预先标记的目标来聚类数据点。聚类是指试图将相似的数据点分组到人工组或聚类中。它是 KMeans 和层次聚类等流行聚类算法的替代方法。

在我们的示例中,我们将检查由 15,000 名员工组成的人力资源数据集。该数据集包含员工工作特征,如工作满意度、绩效得分、工作量、任职年限、事故、晋升次数。

在我的上一篇文章中,我们研究了 KMeans 聚类算法,我强烈建议您阅读(链接)。您将能够了解 KMeans 如何对数据进行聚类。

KMeans vs DBSCAN

KMeans 特别容易受到离群值的影响。当算法迭代通过质心时,在达到稳定和收敛之前,离群值对质心的移动方式有显著影响。此外,KMeans 在准确聚类数据方面存在问题,因为聚类的大小和密度不同。K-Means 只能应用球形聚类,如果数据不是球形的,其准确性会受到影响。最后但同样重要的是,KMeans 要求我们首先选择我们希望找到的集群的数量。下面是 KMeans 和 DBSCAN 如何分别对同一个数据集进行聚类的示例。

另一方面,DBSCAN 不需要我们指定集群的数量,避免了离群值,并且可以很好地处理任意形状和大小的集群。它没有质心,聚类是通过将相邻点连接在一起的过程形成的。

DBSCAN 是如何施展魔法的?

首先,让我们定义ε最小点,应用 DBSCAN 算法时的两个必需参数,以及一些附加术语。

  1. ε(ɛ):邻居的最大半径。如果数据点的相互距离小于或等于指定的ε,则它们将是有效的邻居。换句话说,它是 DBSCAN 用来确定两个点是否相似并属于同一个点的距离。较大的ε将产生较宽的聚类(包含更多的数据点),而较小的ε将构建较小的聚类。一般来说,我们更喜欢较小的值,因为我们只希望数据点之间的距离在ε范围内。太小,你将开始把有效的集群分成越来越小的集群。太大的话,您会将有效的集群聚合成 2 到 3 个大规模集群,几乎没有业务洞察力。
  2. 最小点(minPts): 一个邻域半径内的最小个数据点(即ε),以将该邻域视为一个聚类。请记住,初始点包含在 minPts 中。较低的 minPts 有助于该算法构建更多具有更多噪声或离群值的聚类。较高的 minPts 将确保更健壮的集群,但是如果它太大,较小的集群将被并入较大的集群。

如果“最小点”= 4,在ε距离内的任何 4 个或更多的点将被认为是一个群集。

附加术语

核心点:核心数据点在其ε距离内至少有 minPts 个数的数据点。

我一直认为 DBSCAN 需要第三个名为“core_min”的参数,它将决定一个邻域点簇被认为是有效簇之前核心点的最小数量。

边界点:边界数据点在郊区,因为它们在附近(即 w/in 核心点的ε距离)但小于所需的 minPts。

离群点:这些点不是邻域的一部分(即大于ε距离)并且不是边界点。这些点位于低密度区域。

首先,选择在其ε半径内至少有 minPts 的随机点。然后,评估核心点邻域内的每个点,以确定其附近是否有ε距离内的 min pts(min pts 包括点本身)。如果该点满足 minPts 标准,则它成为另一个核心点,并且集群扩展。如果一个点不满足 minPts 标准,它就成为边界点。随着过程的继续,一个链开始发展,因为核心点“a”是“b”的邻居,而“b”是“c”的邻居,等等。聚类是完整的,因为它被边界点包围,因为在ε距离内没有更多的点。选择新的随机点,并且重复该过程以识别下一个聚类。

如何确定最佳ε值

用于估计最佳ε值的一种方法是使用最近邻距离。如果您还记得,最近邻是一种受监督的 ML 聚类算法,它根据新数据点与其他“已知”数据点的距离对其进行聚类。我们在标记的训练数据上训练 KNN 模型,以确定哪些数据点属于哪个聚类。然后,当我们将该模型应用于新数据时,该算法会根据到已训练聚类的距离来确定新数据点属于哪个聚类。我们必须先验地确定“k”参数,该参数指定在将新数据点分配给聚类之前,模型将考虑多少个最近或最近的相邻点。

为了确定最佳ε值,我们计算每个点与其最近邻点之间的平均距离。然后,我们绘制一个 k 距离图,并选择图表“肘部”处的ε值。在 y 轴上,我们绘制了数据集中所有数据点的平均距离,在 x 轴上绘制了这些数据点。

如果ε选得太小,大部分数据将不会被聚类,而高ε值的聚类将合并,并且大多数数据点将在同一聚类中。一般来说,ε值越小越好,根据经验,只有一小部分点应该在彼此的距离之内。

如何确定最优 minPts

通常,我们应该将 minPts 设置为大于或等于数据集的维数。也就是说,我们经常看到人们将特性的数量乘以 2 来确定它们的 minPts 值。

很像用于确定最佳ε值的“肘方法”, minPts 试探法并非 100%正确。

DBSCAN 集群评估

剪影法:该技术衡量聚类之间的可分性。首先,找出每个点和一个聚类中所有其他点之间的平均距离。然后它测量每个点与其他簇中每个点之间的距离。我们减去两个平均值,然后除以较大的平均值。

我们最终想要一个高(即。最接近 1)的分数,其将指示存在小的群内平均距离(紧密的群)和大的群间平均距离(充分分离的群)。

视觉聚类解释:一旦你获得了你的聚类,解释每个聚类是非常重要的。这通常是通过将原始数据集与聚类合并并可视化每个聚类来完成的。每个聚类越清晰和明显越好。我们将在下面回顾这个过程。

DBSCAN 的优点

  • 不需要我们预先确定像 KMeans 这样的集群的数量
  • 像冠军一样处理异常值
  • 可以将高密度数据分成小簇
  • 可以聚集非线性关系(查找任意形状)

DBSCAN 的缺点

  • 努力在不同密度的数据中识别聚类
  • 会受到高维数据的困扰
  • 对ε和最小点参数非常敏感

我们群集吧!

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import plotly.offline as pyo
pyo.init_notebook_mode()
import plotly.graph_objs as go
from plotly import tools
from plotly.subplots import make_subplots
import plotly.offline as py
import plotly.express as px
from sklearn.cluster import DBSCAN
from sklearn.neighbors import NearestNeighbors
from sklearn.metrics import silhouette_score
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCAplt.style.use('fivethirtyeight')
from warnings import filterwarnings
filterwarnings('ignore') with open('HR_data.csv') as f:
    df =  pd.read_csv(f, usecols=['satisfaction_level', 'last_evaluation', 'number_project',
       'average_montly_hours', 'time_spend_company', 'Work_accident',
       'promotion_last_5years'])
f.close()

1.标准化

由于数据集中的要素比例不同,我们需要对整个数据集进行标准化。换句话说,数据集中的每个要素都有其数据的唯一量值和范围。满意度增加一分并不等于上次评估增加一分,反之亦然。由于 DBSCAN 利用点之间的距离(欧几里德距离)来确定相似性,因此未缩放的数据会产生问题。如果一个要素的数据具有较高的可变性,则距离计算将更容易受到该要素的影响。通过缩放我们的特征,我们将所有的特征对齐到平均值为 0,标准偏差为 1。

scaler = StandardScaler()
scaler.fit(df)
X_scale = scaler.transform(df)df_scale = pd.DataFrame(X_scale, columns=df.columns)
df_scale.head()

2.特征约简

一些算法(如 KMeans)发现,如果数据集具有太多的特征(即高维度)。高维不一定意味着成百上千的特征。即使 10 个特征也可能产生精度问题。

特征或维度减少背后的理论是将原始特征集转换成较少的人工导出的特征,这些特征仍然保持原始特征中包含的大部分信息。

最流行的特征减少技术之一是主成分分析或 PCA。主成分分析将原始数据集缩减为特定数量的特征,称为主成分。我们必须选择我们希望看到的主成分的数量。我们在我关于 KMeans 集群的文章中讨论了特性缩减,我强烈建议您看一下( LINK )。

首先,我们需要确定适当数量的主成分。似乎 3 个主成分解释了大约 75%的方差。

pca = PCA(n_components=7)
pca.fit(df_scale)
variance = pca.explained_variance_ratio_ var=np.cumsum(np.round(variance, 3)*100)
plt.figure(figsize=(12,6))
plt.ylabel('% Variance Explained')
plt.xlabel('# of Features')
plt.title('PCA Analysis')
plt.ylim(0,100.5)plt.plot(var)

现在我们知道了保持特定方差百分比所需的主成分数,让我们对原始数据集应用一个三成分 PCA。请注意,第一个主成分占原始数据集方差的 26%。在本文的剩余部分,我们将使用“pca_df”数据帧。

pca = PCA(n_components=3)
pca.fit(df_scale)
pca_scale = pca.transform(df_scale)pca_df = pd.DataFrame(pca_scale, columns=['pc1', 'pc2', 'pc3'])
print(pca.explained_variance_ratio_)

在 3D 空间中绘制我们的数据,我们可以看到 DBSCAN 的一些潜在问题。如果您还记得 DBSCAN 的一个主要缺点,那就是它无法准确地对不同密度的数据进行聚类,从下图中,我们可以看到两个密度非常不同的独立聚类。在应用 DBSCAN 算法时,我们可能能够在数据点的较低聚类中找到聚类,但是在较高聚类中的许多数据点可能被归类为异常值/噪声。这当然完全取决于我们对ε和最小点值的选择。

Scene = dict(xaxis = dict(title  = 'PC1'),yaxis = dict(title  = 'PC2'),zaxis = dict(title  = 'PC3'))trace = go.Scatter3d(x=pca_df.iloc[:,0], y=pca_df.iloc[:,1], z=pca_df.iloc[:,2], mode='markers',marker=dict(colorscale='Greys', opacity=0.3, size = 10, ))
layout = go.Layout(margin=dict(l=0,r=0),scene = Scene, height = 1000,width = 1000)
data = [trace]
fig = go.Figure(data = data, layout = layout)
fig.show()

3.DBSCAN 聚类

方法 1

在我们应用聚类算法之前,我们必须使用上面讨论的“肘方法”来确定适当的 epsilon 级别。看起来最佳的ε值大约是 0.2。最后,由于我们的数据有 3 个主要成分,我们将把最低分标准设为 6。

plt.figure(figsize=(10,5))
nn = NearestNeighbors(n_neighbors=5).fit(pca_df)
distances, idx = nn.kneighbors(pca_df)
distances = np.sort(distances, axis=0)
distances = distances[:,1]
plt.plot(distances)
plt.show()

将ε设置为 0.2 并将 min_samples 设置为 6 导致了 53 个聚类,轮廓得分为-0.521,以及超过 1500 个数据点被认为是异常值/噪声。在一些研究领域中,53 个聚类可能被认为是信息丰富的,但我们有一个 15,000 名员工的数据集。从业务的角度来看,我们需要一个可管理的集群数量(即 3-5 个),可以用来更好地了解工作场所。此外,轮廓得分为-0.521 表示数据点的聚类不正确。

查看下面的 3D 图,我们可以看到一个包含大部分数据点的聚类。出现了一个较小但重要的集群,但剩下 52 个更小的集群。从业务角度来看,这些集群并不能提供很多信息,因为大多数员工只属于两个集群。组织希望看到几个大的集群,以便确定它们的有效性,但是也能够针对集群的员工参与一些组织活动(即培训增加、薪酬变动等。).

db = DBSCAN(eps=0.2, min_samples=6).fit(pca_df)
labels = db.labels_# Number of clusters in labels, ignoring noise if present.
n_clusters_ = len(set(labels)) - (1 if -1 in labels else 0)
n_noise_ = list(labels).count(-1)print('Estimated number of clusters: %d' % n_clusters_)
print('Estimated number of noise points: %d' % n_noise_)
print("Silhouette Coefficient: %0.3f" % metrics.silhouette_score(pca_df, labels))

Scene = dict(xaxis = dict(title  = 'PC1'),yaxis = dict(title  = 'PC2'),zaxis = dict(title  = 'PC3'))labels = db.labels_trace = go.Scatter3d(x=pca_df.iloc[:,0], y=pca_df.iloc[:,1], z=pca_df.iloc[:,2], mode='markers',marker=dict(color = labels, colorscale='Viridis', size = 10, line = dict(color = 'gray',width = 5)))
layout = go.Layout(scene = Scene, height = 1000,width = 1000)
data = [trace]
fig = go.Figure(data = data, layout = layout)fig.update_layout(title='DBSCAN clusters (53) Derived from PCA', font=dict(size=12,))
fig.show()

接近 2 号

我们不使用“肘方法”和最小值试探法,而是采用迭代方法来微调我们的 DBSCAN 模型。当我们将 DBSCAN 算法应用于数据时,我们将遍历一系列ε和最小点值。

在我们的示例中,我们将以 0.1 的间隔迭代范围为 0.5 至 1.5 的ε值,以及范围为 2 至 7 的最小点值。for 循环将使用这组值运行 DBSCAN 算法,并为每次迭代生成聚类数和轮廓分数。请记住,您需要根据您的数据调整您的参数。您可能会在一组参数上运行此代码,并发现产生的最佳轮廓分数为 0.30。您可能希望增加ε值,以便将更多的点包含到一个簇中。

pca_eps_values = np.arange(0.2,1.5,0.1) 
pca_min_samples = np.arange(2,5) 
pca_dbscan_params = list(product(pca_eps_values, pca_min_samples))pca_no_of_clusters = []
pca_sil_score = []
pca_epsvalues = []
pca_min_samp = []for p in pca_dbscan_params:
    pca_dbscan_cluster = DBSCAN(eps=p[0], min_samples=p[1]).fit(pca_df)
    pca_epsvalues.append(p[0])
    pca_min_samp.append(p[1])pca_no_of_clusters.append(
len(np.unique(pca_dbscan_cluster.labels_)))
    pca_sil_score.append(silhouette_score(pca_df, pca_dbscan_cluster.labels_))pca_eps_min = list(zip(pca_no_of_clusters, pca_sil_score, pca_epsvalues, pca_min_samp))pca_eps_min_df = pd.DataFrame(pca_eps_min, columns=['no_of_clusters', 'silhouette_score', 'epsilon_values', 'minimum_points'])pca_ep_min_df

我们可以看到,通过迭代我们的ε和最小值,我们已经获得了大量的聚类和轮廓分数。ε得分在 0.9 和 1.1 之间开始产生可管理数量的聚类。将 epsilon 增加到 1.2 及以上会创建太少的集群,没有太多的商业意义。此外,这些簇中的一些可能只是噪声(即-1)我们一会儿就会谈到。

同样重要的是要理解,增加ε会减少聚类的数量,但是每个聚类也会开始包含更多的异常值/噪声数据点。存在一定程度的收益递减。

为了简单起见,让我们选择 7 个集群,并检查集群分布情况。(ε:1.0 & min pts:4)。

指出运行这一串代码时肯定会遇到的一个常见错误也很重要。有时当你设置好你的参数(即 eps_values 和 min_samples)太宽,for 循环最终将得到仅产生一个聚类的 eps_values 和 min_samples 的组合。但是,Silhouette_score 函数要求至少定义两个分类。您需要限制您的参数来避免这个问题。

在上面的例子中,如果我们将 epsilon 参数范围设置为 0.2 到 2.5,则最有可能产生一个聚类并最终导致错误。

您可能会问自己“我们不是应该获得 7 个集群吗?”。答案是“是”,如果我们查看唯一的标签/聚类,我们会看到每个数据点有 7 个标签。根据 Sklearn 文档,标签“-1”相当于一个“嘈杂”的数据点,它没有被聚类到 6 个高密度集群中的任何一个。我们自然不希望将“-1”的任何标签视为一个集群,因此,它们被从计算中移除。

db = DBSCAN(eps=1.0, min_samples=4).fit(pca_df)
labels = db.labels_# Number of clusters in labels, ignoring noise if present.
n_clusters_ = len(set(labels)) - (1 if -1 in labels else 0)
n_noise_ = list(labels).count(-1)print('Estimated number of clusters: %d' % n_clusters_)
print('Estimated number of noise points: %d' % n_noise_)
print("Silhouette Coefficient: %0.3f" % silhouette_score(pca_df, labels))

set(labels)

查看六个 DBSCAN 衍生的集群的 3D 图,看起来接近图顶部的密度较低的集群尽管密度较低,但并没有对 DBSCAN 造成太大的问题。如果您还记得,DBSCAN 很难正确地对各种密度的数据进行聚类。顶部星团和大得多的底部星团之间的距离很可能大于我们的ε值 1.0。

也就是说,数据集包含额外的高密度聚类,但我们的ε和最小值太大。底部聚类包含至少两个高密度聚类,然而,由于底部聚类的强密度,降低ε和最小点值将简单地创建许多更小的聚类。这也是 DBSCAN 的主要缺点。我一直认为 DBSCAN 需要第三个参数“min_core”,它将决定一个簇被认为是有效簇之前的最小核心点数。

Scene = dict(xaxis = dict(title  = 'PC1'),yaxis = dict(title  = 'PC2'),zaxis = dict(title  = 'PC3'))# model.labels_ is nothing but the predicted clusters i.e y_clusters
labels = db.labels_trace = go.Scatter3d(x=pca_df.iloc[:,0], y=pca_df.iloc[:,1], z=pca_df.iloc[:,2], mode='markers',marker=dict(color = labels, colorscale='Viridis', size = 10, line = dict(color = 'gray',width = 5)))
layout = go.Layout(scene = Scene, height = 1000,width = 1000)
data = [trace]
fig = go.Figure(data = data, layout = layout)fig.update_layout(title="'DBSCAN Clusters (6) Derived from PCA'", font=dict(size=12,))
fig.show()

在我们开始之前,让我们快速统计一下每个集群中的员工数量。看起来第 0 类包含了大部分的数据点,这些数据点提供的信息并不多。事实上,如果我们使用ε值为 0.5 且最小点数为 5 来运行该算法,将会产生 63 个分类,分类 0 仍将包含 99%的雇员人口。

np.unique(labels, return_counts=True)

摘要

DBSCAN 是一种密度聚类算法,常用于非线性或非球形数据集。ε和最小点是两个必需的参数。ε是需要被认为足够“相似”以开始聚类的附近数据点内的半径。最后,最小点是需要在半径内的最小数量的数据点(即ε)才能被认为是一个集群。

在我们的示例中,我们试图根据 15,000 名员工的工作特征对他们的数据集进行聚类。我们首先对数据集进行标准化,以缩放特征。接下来,我们应用主成分分析,以便将维度/特征的数量减少到 3 个主成分。使用“肘法”,我们估计ε值为 0.2,最小点值为 6。使用这些参数,我们能够获得 53 个聚类、1,500 个异常值和-0.52 的轮廓得分。不用说,结果并不乐观。

接下来,我们尝试了一种迭代方法来微调ε和最小点参数。我们决定ε值为 1.0,最小点数值为 4。该算法返回 6 个有效聚类(一对一聚类),只有 7 个异常值,以及 0.46 的可观轮廓分数。但是,在绘制派生的分类时,发现第一个分类包含 99%的雇员。再次从业务的角度来看,我们希望我们的集群分布更加均匀,以便为我们提供关于员工的良好见解。

似乎 DBSCAN 不是这个特定数据集的最佳聚类算法。

这个相同的数据集使用 KMeans 进行聚类,并产生更加平等和明确的聚类(链接)。

人工智能的可解释性(以及它对教育人工智能的意义)

原文:https://towardsdatascience.com/explaining-educational-ai-c5f175850f0b?source=collection_archive---------39-----------------------

可解释性”——自从宣布人工智能以来,这个词已经出现在许多与技术相关的新闻报道中,对普通用户来说更有意义。我会回到为什么这很重要。

前几天我读了一篇关于我们如何信任他人的研究论文,这给我留下了一个有趣的想法。这篇论文(你可以在这里找到)的结论是,人们更信任不考虑后果就合作的人。

本质上,这意味着当人们盲目信任他人时,我们更容易信任他们。这是有道理的,谁会希望他们的朋友在伸出援手之前权衡利弊,或者一个商业伙伴考虑把你排除在外的得失呢?没有人。

但现在想想这如何转化为人工智能。

人工智能正是这么做的,它计算。它接收信息,并根据这些信息做出最佳决策。这项研究表明,当一个人这样做的时候,我们不会那么信任他们……那么我们为什么要信任完全这样做的人工智能呢?

简短的回答是:我们没有。

这项研究本质上告诉我们,我们自己被编程为不相信任何人或任何事来衡量它的选择,而不是本能地站在我们一边。但是 AI 没有本能。它拥有不能做任何事情的算法 但是 权衡它的选择。

所以我们就不能相信 AI?

我们可以。人工智能有巨大的潜力,如果我们正确使用它,它可以让我们在很多事情上做得更好。在过去,我谈过一点信息技术在教育中的潜在用途;在课堂上或去高校协助讲师。然而,这两个建议都需要对所使用的人工智能或技术有一定程度的信任。

所以,我相信你们中有些人会想"为什么这是一个问题?我们已经在生活中使用人工智能,即使它不是完全可信的,“还有一些人在想”我不信任人工智能,你在说什么?“这两个观点都很有道理,但都需要一点视角的转变。

Andrei Elekes 好心提供的 Gif

当然,我们可能已经在使用人工智能,就像可口可乐公司使用人工智能来规划其自动售货机的最佳位置,但这是不同的。我们可能会相信人工智能会做出这样的决定,或者我们可能会接受我们不需要相信它,因为它只是一台自动售货机。这和允许机器决定你孩子的教育没有任何相似之处

我最近看到的另一个有趣的研究是专门针对完全初学者的编程教学/学习。它专注于在不同的代码片段中寻找所谓的“信标”。当我们谈论计算机科学教育时,信标指的是可以帮助读者轻松识别其功能的特定代码行。

这篇研究是关于在向新学生介绍编程时突出正确方向的重要性。

用于确定信标的热图(摘自上面链接的论文)

这真的很有趣,因为将这项研究与人工智能相结合可以为学生定制一些课程。如果人工智能可以确定哪些线应该作为信标加以强调,它可能会彻底改变计算机科学的教学方式,并使该领域可以面向一大批学习风格不同的学生,这些学生可能与计算机科学中使用的传统教学方法不兼容。

听起来很神奇,对吧?

也许吧。但是,如果人工智能开始向学生展示错误的信标,会发生什么?如果结果证明它对一些学生强调什么做出了错误的决定呢?然后,学生们努力学习,并强调他们课程中错误的部分来练习。

所以…我们到底该不该相信 AI?

这不是一个容易回答的问题。我能给出的最好答案是肯定的,但有一些警告。至少这只是我的看法,但请听我说完。

我们可以用人工智能做令人惊奇的事情,但我们需要小心使用它。我们应该专注于确保我们理解人工智能的决策过程,然后才允许它负责像教育这样的大事情。所以这又回到了可解释性。

如果我们想要信任一个 AI,我们首先应该了解它是如何思考的。如果我们可以信任它如何做决定,那么也许我们可以信任它所做的决定,即使我们无法跟随它在特定决定上的“思维过程”。

我们能做些什么来实现这一点?

在个人层面上,我们都应该努力学习人工智能是如何工作的。这不仅仅局限于在 IT 行业工作或者觉得自己擅长数学或计算机的人;这适用于每个人。有一个很棒的(免费!赫尔辛基大学关于人工智能的在线课程,我强烈推荐给任何从零开始或者有一点经验的人。

你永远不知道,当你在学习可以做什么的时候,你可能会得到一点灵感。如果你这样做了,试试甲骨文的云试验吧。

人工智能无处不在,未来几年它只会变得更大。我们应该拥抱变化,但我们永远不应该忘记关注它。这是新的一年,新的十年;为什么不把这当成你了解这个迷人领域的机会呢?你永远不知道,它可能会在几年后教你的孩子!

解释 K 均值聚类

原文:https://towardsdatascience.com/explaining-k-means-clustering-5298dc47bad6?source=collection_archive---------1-----------------------

比较主成分分析和 t-SNE 降维技术在聚类识别雇员子群时的作用

埃里克·穆尔在 Unsplash 上拍摄的照片

今天的数据有各种形状和大小。NLP 数据包含书面文字,时序数据跟踪随时间推移的顺序数据移动(即股票),允许计算机通过例子学习的结构化数据,以及允许计算机应用结构的未分类数据。无论你拥有哪一个数据集,你都可以确定有一个算法可以破解它的秘密。在本文中,我们想要介绍一个名为 KMeans 的聚类算法,它试图发现隐藏在数据集中的隐藏子群。此外,我们将检查降维对从 KMeans 获得的聚类质量有什么影响。

在我们的示例中,我们将检查由 15,000 名员工组成的人力资源数据集。该数据集包含员工工作特征,如工作满意度、绩效得分、工作量、任职年限、事故、晋升次数。我们将应用知识手段来发现相似的员工群体。

聚类与分类有何不同?

分类问题会有我们试图预测的目标标签。著名的数据集如 Titanic 和 Iris 都是分类的首选,因为它们都有我们试图预测的目标(即幸存下来的还有物种)。此外,分类任务要求我们将数据分为训练和测试,其中分类器根据训练数据进行训练,然后通过测试数据集来衡量其性能。

在处理聚类问题时,我们希望使用算法来发现数据中有意义的组。也许我们正试图发现客户群或识别数据中的异常。无论哪种方式,该算法在很少人为干预的情况下发现了这些群体,因为我们没有目标标签可以预测。

也就是说,我们可以通过首先识别组/聚类,然后构建监督模型来预测聚类成员,从而将非监督聚类算法与监督算法结合起来。

K-Means 是怎么施展魔法的?

在其核心,KMeans 试图将数据组织到指定数量的集群中。 不幸的是,由我们来决定我们希望找到的集群的数量 ,但恐怕我们没有工具来协助这个过程。KMeans 的目标是识别 相似的 数据点 并将它们聚类在一起,同时尝试 尽可能远离每个聚类 。它的“相似性”计算是通过两点之间的欧几里德距离或普通直线来确定的( Wiki )。欧几里德距离越短,点越相似。

首先,用户(即。您或我)确定 KMeans 需要查找的聚类数。聚类的数量不能超过数据集中要素的数量。接下来,KMeans 将为每个质心选择一个随机点。质心的数量等于所选簇的数量。质心是围绕其构建每个聚类的点。

第二,计算每个点和每个质心之间的欧几里德距离。每个点最初将根据欧几里德距离被分配给最近的质心/聚类。每个数据点可以属于一个聚类或质心。然后,该算法对每个聚类的欧几里德距离(每个点和质心之间)进行平均,并且该点成为新的质心。这个平均聚类内欧几里得距离并分配新质心的过程重复进行,直到聚类质心不再移动。下面的动画显示了这个过程,如果需要,刷新页面。

http://shabal.in/visuals/kmeans/6.html

选择初始质心

我们需要知道 KMeans 如何选择初始质心,以及这会产生什么问题。如果没有我们的干预,KMeans 将随机选择初始质心,这最终会在相同的数据上产生不同的聚类。从下面的图中,我们可以看到在两种不同的情况下运行 KMeans 算法会产生不同的初始质心。在第一次和第二次运行 KMeans 之间,相同的数据点被分配给不同的聚类。

第一次初始化

第二次初始化

不要害怕,Sklearn 是我们的后盾!Sklearn KMeans 库有一些参数,比如“n_int”和“max_iter ”,可以缓解这个问题。“n_int”参数决定了 KMeans 随机选择不同质心的次数。“Max_iter”决定了将运行多少次迭代。迭代是寻找距离、取平均距离和移动质心的过程。

如果我们将参数设置为 n_int=25,max_iter=200,则 Means 将随机选择 25 个初始质心,并运行每个质心多达 200 次迭代。这 25 个质心中最好的将成为最终的聚类。

Sklearn 还有一个“int”参数,它将随机选择第一个质心,并定位所有离第一个质心最远的数据点。然后,第二个质心被分配在那些远点附近,因为它们不太可能属于第一个质心。Sklearn 默认选择“int=kmeans++”,这应用了上述逻辑。

如何确定聚类数?

“您提到了需要选择集群的数量……。?我们如何做到这一点?”

领域知识:通常我们在收集数据集的领域拥有一定水平的知识和经验。这种专业知识可以让我们设定我们认为存在于一般人群中的聚类数。

假设检验:设定一个特定的聚类数也可以作为我们可能有的某个假设的检验。例如,在分析营销数据时,我们有一种预感,有 3 个客户群非常可能、可能和不可能购买我们的产品。

数据是预先标记的:有时候我们分析的数据带有预先标记的目标。这些数据集通常用于有监督的 ML 问题,但这并不意味着我们不能对数据进行聚类。预先标记的数据是唯一的,因为您需要从初始分析中移除目标,然后使用它们来验证模型对数据的聚类效果。

肘方法:这是一种非常流行的迭代统计技术,通过对一系列聚类值实际运行 K-Means 算法来确定最佳聚类数。对于 KMeans 的每次迭代,肘形法计算每个点到其指定质心的距离平方和。每次迭代都要经过不同数量的集群。结果是一个折线图,显示每个聚类的平方距离之和。我们希望选择折线图肘部的聚类数或最小距离平方和(即。惯性)。距离平方和越小,意味着每个分类中的数据分组越紧密。

缩放:标准化

KMeans 对比例非常敏感,要求所有要素的比例相同。KMeans 会将更多的权重或重点放在方差较大的特征上,这些特征会对最终的聚类形状产生更大的影响。例如,让我们考虑一个汽车信息数据集,如重量(lbs)和马力(hp)。如果所有汽车之间的重量差异较大,平均欧几里得距离将会受到重量的更大影响。最终,重量比马力更能影响每个集群的成员。

高维度

诸如 KMeans 之类的聚类算法很难准确地聚类高维数据(即功能太多)。我们的数据集不一定是高维的,因为它包含 7 个要素,但即使是这个数量也会给 KMeans 带来问题。我建议你探索一下维度的诅咒以获得更多细节。正如我们前面看到的,许多聚类算法使用距离公式(即欧几里德距离)来确定聚类成员资格。当我们的聚类算法有太多的维度时,成对的点将开始有非常相似的距离,我们将不能获得有意义的聚类。

在这个例子中,我们将在运行 K-Means 聚类算法之前比较 PCA 和 t-SNE 数据简化技术。让我们花几分钟来解释 PCA 和 t-SNE。

主成分分析

主成分分析(PCA)是一种经典的方法,我们可以用它将高维数据降低到低维空间。换句话说,我们根本无法精确地可视化高维数据集,因为我们无法可视化 3 个要素以上的任何内容(1 个要素=1D,2 个要素= 2D,3 个要素=3D 绘图)。PCA 背后的主要目的是将具有 3 个以上特征(高维)的数据集转换成典型的 2D 或 3D 图,供我们这些虚弱的人使用。这就是低维空间的含义。PCA 背后的美妙之处在于,尽管减少到低维空间,我们仍然保留了原始高维数据集的大部分(+90%)方差或信息。来自我们原始特征的信息或差异被“挤压”到 PCA 所称的主成分(PC)中。第一台 PC 将包含原始特征的大部分信息。第二台电脑将包含第二大信息量,第三台电脑包含第三大信息量,依此类推。PC 是不相关的(即正交的),这意味着它们都包含独特的信息片段。我们通常可以“挤压”大多数(即 80–90 %)的信息或包含在原始特征中的变化分解成几个主要成分。我们在分析中使用这些主要成分,而不是使用原始特征。这样,我们可以仅用 2 或 3 个主成分而不是 50 个特征进行分析,同时仍然保持原始特征的 80–90%的信息。

让我们来看看 PCA 是如何变魔术的一些细节。为了使解释变得简单一点,让我们看看如何减少具有两个特征的数据集(即 2D)在一个主成分(1D)。也就是说,将 50 个特征缩减为 3 或 4 个主成分使用了相同的方法。

我们有一张体重和身高的 2D 图。换句话说,我们有一个 7 人的数据集,并绘制了他们的身高与体重的关系。

首先,PCA 需要通过测量每个点到 y 轴(身高)和 x 轴(体重)的距离来确定数据的中心。然后计算两个轴的平均距离(即身高和体重)并使用这些平均值将数据居中。

然后 PCA 将绘制第一主成分(PC1)或最佳拟合线,其最大化体重和身高之间的方差或信息量。 它通过最大化投影到最佳拟合线上的点到原点的距离来确定最佳拟合线(即下面蓝光)。 它对每个绿点都这样做,然后它对每个距离进行平方,去掉负值,并对所有值求和。最佳拟合线或 PC1 将具有从原点到所有点的投影点的最大距离平方和。

现在,我们只需将轴旋转到 PC1 现在是 x 轴的位置,我们就完成了。

如果我们想要将 3 个特征减少到两个主要成分,我们将简单地在 2D 空间中放置一条与我们的最佳拟合线垂直的线(y 轴)。为什么是垂直线?因为每个主分量与所有其他特征正交或不相关。如果我们想要找到第三个主分量,我们将简单地找到另一条到 PC1 和 PC2 的正交线,但是这次是在 3D 空间中。

从下面的柱状图可以看出,我们最初是从一个包含 5 个要素的数据集开始的。记住主成分的数量总是等于特征的数量。然而,我们可以看到,前 2 个主成分占原始 5 个特征中包含的方差或信息的 90%。这就是我们如何确定主成分的最佳数量。重要的是要记住 PCA 经常用于可视化非常高维的数据(即数以千计的特征),因此,您将最常看到 2 或 3 个主成分的 PCA。

t 分布随机邻居嵌入(t-SNE)

就像 PCA 一样,t-SNE 提取高维数据,并将其简化为低维图形(通常为二维)。这也是一个很好的降维技术。与主成分分析不同,t-SNE 可以降低非线性关系的维数。换句话说,如果我们的数据具有这种“瑞士滚”非线性分布,其中 X 或 Y 的变化与其他变量的恒定变化不一致。PCA 不能够准确地将这些数据提取到主成分中。这是因为 PCA 试图通过分布绘制最佳拟合线。在这种情况下,T-SNE 将是一个更好的解决方案,因为它根据点之间的距离计算相似性度量,而不是试图最大化方差。

让我们来看看 t-SNE 是如何将高维数据空间转换成低维空间的。它通过观察距离(想想欧几里德距离)来看局部或附近点之间的相似性。彼此相邻的点被认为是相似的。然后,t-SNE 将每对点的相似性距离转换成每对点的概率。如果在高维空间中两个点彼此靠近,它们将具有高概率值,反之亦然。这样,选择一组点的概率与它们的相似性成比例。

假设的高维空间

然后将每个点随机地投影到低维空间中。对于这个例子,我们在 1 维空间中绘图,但是我们也可以在 2 维或 3 维空间中绘图。为什么是 2d 还是 3d?因为那些是我们(人类)唯一能想象的维度。记住 t-SNE 首先是一个可视化工具,其次是一个降维工具。

随机投影到一维空间

最后,t-SNE 在低维空间中计算相似性概率得分,以便将这些点聚集在一起。结果是我们下面看到的一维图。

关于 t-SNE,我们需要讨论的最后一件事是“困惑”,这是运行算法时需要的参数。“困惑”决定了一个空间有多宽或多紧 t-SNE 捕捉到了点与点之间的相似之处。如果你的困惑是低的(也许 2),t-SNE 将只使用两个相似的点,并产生一个有许多分散集群的情节。然而,当我们将困惑度增加到 10 时,SNE 霸王龙会将 10 个相邻点视为相似,并将它们聚集在一起,从而形成更大的点群。有一个收益递减点,在这个点上,困惑会变得太大,我们会得到一个有一两个分散集群的地块。在这一点上,t-SNE 错误地认为不一定相关的点属于一个集群。根据最初发表的论文(链接),我们通常将困惑度设置在 5 到 50 之间。

困惑太低

困惑太高

t-SNE 的主要限制之一是它的高计算成本。如果你有一个非常大的特征集,最好先用主成分分析将特征数量减少到几个主成分,然后用 t-SNE 进一步将数据减少到 2 或 3 个聚类。

L 让我们回到克迈斯……..

k 均值聚类评估

当使用无监督的 ML 算法时,我们经常不能将我们的结果与已知的真实标签进行比较。换句话说,我们没有测试集来衡量我们模型的性能。也就是说,我们仍然需要了解 K-Means 如何成功地对我们的数据进行聚类。通过查看肘形图和我们选择的聚类数,我们已经知道数据在我们的聚类中包含的紧密程度。

剪影法:该技术衡量聚类之间的可分性。首先,找出每个点和一个聚类中所有其他点之间的平均距离。然后它测量每个点与其他簇中每个点之间的距离。我们减去两个平均值,然后除以较大的平均值。

我们最终想要一个高(即。最接近 1)的分数,其将指示存在小的群内平均距离(紧密的群)和大的群间平均距离(充分分离的群)。

视觉聚类解释:一旦你获得了你的聚类,解释每个聚类是非常重要的。这通常是通过将原始数据集与聚类合并并可视化每个聚类来完成的。每个聚类越清晰和明显越好。我们将在下面回顾这个过程。

我们群集吧!

下面是分析计划:

  1. 使数据标准化
  2. 对原始数据集应用 KMeans
  3. 通过主成分分析的特征约简
  4. 将 KMeans 应用于 PCA 主成分分析
  5. 基于 t-SNE 的特征约简
  6. 将 KMeans 应用于 t-SNE 聚类
  7. 比较主成分分析和 t-SNE 均值聚类
*import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import seaborn as sns
import plotly.offline as pyo
pyo.init_notebook_mode()
import plotly.graph_objs as go
from plotly import tools
from plotly.subplots import make_subplots
import plotly.offline as py
import plotly.express as pxfrom sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.manifold import TSNE
from sklearn.metrics import silhouette_score%matplotlib inline
from warnings import filterwarnings
filterwarnings('ignore')with open('HR_data.csv') as f:
    df =  pd.read_csv(f, usecols=['satisfaction_level', 'last_evaluation', 'number_project',
       'average_montly_hours', 'time_spend_company', 'Work_accident','promotion_last_5years'])
f.close()df.head()*

这是一个相对干净的数据集,没有任何缺失值或异常值。我们看不到任何需要编码的混合型特征、奇数值或罕见标签。要素也具有较低的多重共线性。让我们继续扩展我们的数据集。

1.标准化

如上所述,数据的标准化最终将使所有特征达到相同的尺度,并使平均值为零,标准偏差为 1。

*scaler = StandardScaler()
scaler.fit(df)
X_scale = scaler.transform(df)df_scale = pd.DataFrame(X_scale, columns=df.columns)
df_scale.head()*

2.原始数据集上的 KMeans

让我们利用肘方法来确定 KMeans 应该获得的最佳集群数。看起来 4 或 5 个集群是最好的,为了简单起见,我们选择 4 个。

*sse = []
k_list = range(1, 15)for k in k_list:
    km = KMeans(n_clusters=k)
    km.fit(df_scale)
    sse.append([k, km.inertia_])

oca_results_scale = pd.DataFrame({'Cluster': range(1,15), 'SSE': sse})
plt.figure(figsize=(12,6))
plt.plot(pd.DataFrame(sse)[0], pd.DataFrame(sse)[1], marker='o')
plt.title('Optimal Number of Clusters using Elbow Method (Scaled Data)')
plt.xlabel('Number of clusters')
plt.ylabel('Inertia')*

让我们对请求 4 个集群的原始数据集应用 KMeans。我们取得了 0.25 的剪影分数,这是低端。

*df_scale2 = df_scale.copy()kmeans_scale = KMeans(n_clusters=4, n_init=100, max_iter=400, init='k-means++', random_state=42).fit(df_scale2)print('KMeans Scaled Silhouette Score: {}'.format(silhouette_score(df_scale2, kmeans_scale.labels_, metric='euclidean')))labels_scale = kmeans_scale.labels_
clusters_scale = pd.concat([df_scale2, pd.DataFrame({'cluster_scaled':labels_scale})], axis=1)*

使用 PCA 将数据集减少为 3 个主要成分,我们可以将 KMeans 衍生的聚类绘制成 2D 和 3D 图像。PCA 可视化倾向于围绕一个中心点聚集聚类,这使得解释困难,但是我们可以看到聚类 1 和 3 与聚类 0 和 2 相比具有一些不同的结构。然而,当我们将集群绘制到 3D 空间中时,我们可以清楚地区分所有 4 个集群。

*pca2 = PCA(n_components=3).fit(df_scale2)
pca2d = pca2.transform(df_scale2)plt.figure(figsize = (10,10))
sns.scatterplot(pca2d[:,0], pca2d[:,1], 
                hue=labels_scale, 
                palette='Set1',
                s=100, alpha=0.2).set_title('KMeans Clusters (4) Derived from Original Dataset', fontsize=15)plt.legend()
plt.ylabel('PC2')
plt.xlabel('PC1')
plt.show()*

*Scene = dict(xaxis = dict(title  = 'PC1'),yaxis = dict(title  = 'PC2'),zaxis = dict(title  = 'PC3'))labels = labels_scaletrace = go.Scatter3d(x=pca2d[:,0], y=pca2d[:,1], z=pca2d[:,2], mode='markers',marker=dict(color = labels, colorscale='Viridis', size = 10, line = dict(color = 'gray',width = 5)))layout = go.Layout(margin=dict(l=0,r=0),scene = Scene, height = 1000,width = 1000)data = [trace]fig = go.Figure(data = data, layout = layout)
fig.show()*

3.通过主成分分析的特征约简

首先,让我们确定我们需要的主成分的最佳数量是多少。通过检查每个主成分包含的方差,我们可以看到前 3 个主成分解释了大约 70%的方差。最后,我们再次应用主成分分析,将我们的数据集减少到 3 个主成分。

*#n_components=7 because we have 7 features in the dataset
pca = PCA(n_components=7)
pca.fit(df_scale)
variance = pca.explained_variance_ratio_var = np.cumsum(np.round(variance, 3)*100)
plt.figure(figsize=(12,6))
plt.ylabel('% Variance Explained')
plt.xlabel('# of Features')
plt.title('PCA Analysis')
plt.ylim(0,100.5)plt.plot(var)*

*pca = PCA(n_components=3)
pca_scale = pca.fit_transform(df_scale)pca_df_scale = pd.DataFrame(pca_scale, columns=['pc1','pc2','pc3'])print(pca.explained_variance_ratio_)*

每台电脑解释的差异百分比

4.将 k 均值应用于 PCA 主成分分析

现在,我们已经将 7 个要素的原始数据集减少到只有 3 个主成分,让我们应用 KMeans 算法。我们再次需要确定集群的最佳数量,而 4 似乎是正确的选择。重要的是要记住,我们现在使用 3 个主成分而不是最初的 7 个特征来确定最佳聚类数。

*sse = []
k_list = range(1, 15)for k in k_list:
    km = KMeans(n_clusters=k)
    km.fit(pca_df_scale)
    sse.append([k, km.inertia_])

pca_results_scale = pd.DataFrame({'Cluster': range(1,15), 'SSE': sse})
plt.figure(figsize=(12,6))
plt.plot(pd.DataFrame(sse)[0], pd.DataFrame(sse)[1], marker='o')
plt.title('Optimal Number of Clusters using Elbow Method (PCA_Scaled Data)')
plt.xlabel('Number of clusters')
plt.ylabel('Inertia')*

现在我们准备将 KMeans 应用于 PCA 主成分。我们可以看到,通过传递 KMeans 低维数据集,我们能够将轮廓得分从 0.25 提高到 0.36。查看 2D 和 3D 散点图,我们可以看到聚类之间的区别有了显著改善。

*kmeans_pca_scale = KMeans(n_clusters=4, n_init=100, max_iter=400, init='k-means++', random_state=42).fit(pca_df_scale)
print('KMeans PCA Scaled Silhouette Score: {}'.format(silhouette_score(pca_df_scale, kmeans_pca_scale.labels_, metric='euclidean')))labels_pca_scale = kmeans_pca_scale.labels_
clusters_pca_scale = pd.concat([pca_df_scale, pd.DataFrame({'pca_clusters':labels_pca_scale})], axis=1)*

*plt.figure(figsize = (10,10))sns.scatterplot(clusters_pca_scale.iloc[:,0],clusters_pca_scale.iloc[:,1], hue=labels_pca_scale, palette='Set1', s=100, alpha=0.2).set_title('KMeans Clusters (4) Derived from PCA', fontsize=15)plt.legend()
plt.show()*

**

5.基于 t-SNE 的特征约简

当我们将维数减少到 3 个主成分时,我们可以看到 KMeans 对数据进行聚类的能力有了明显的提高。在本节中,我们将再次使用 t-SNE 缩减我们的数据,并将 KMeans 的结果与 PCA KMeans 的结果进行比较。我们将减少到 3 个 t-SNE 组件。请记住,t-SNE 是一个计算量很大的算法。使用' n_iter '参数可以减少计算时间。此外,您在下面看到的代码是“困惑”参数多次迭代的结果。任何超过 80 的困惑倾向于将我们的数据聚集成一个大的分散的集群。

*tsne = TSNE(n_components=3, verbose=1, perplexity=80, n_iter=5000, learning_rate=200)
tsne_scale_results = tsne.fit_transform(df_scale)tsne_df_scale = pd.DataFrame(tsne_scale_results, columns=['tsne1', 'tsne2', 'tsne3'])plt.figure(figsize = (10,10))
plt.scatter(tsne_df_scale.iloc[:,0],tsne_df_scale.iloc[:,1],alpha=0.25, facecolor='lightslategray')
plt.xlabel('tsne1')
plt.ylabel('tsne2')
plt.show()*

下面是将我们的原始数据集缩减为绘制在 2D 空间中的 3 个 t-SNE 分量的结果。

6.将 KMeans 应用于 t-SNE 聚类

对于我们的 KMeans 分析来说,4 似乎又是一个神奇的聚类数。

*sse = []
k_list = range(1, 15)for k in k_list:
    km = KMeans(n_clusters=k)
    km.fit(tsne_df_scale)
    sse.append([k, km.inertia_])

tsne_results_scale = pd.DataFrame({'Cluster': range(1,15), 'SSE': sse})
plt.figure(figsize=(12,6))
plt.plot(pd.DataFrame(sse)[0], pd.DataFrame(sse)[1], marker='o')
plt.title('Optimal Number of Clusters using Elbow Method (tSNE_Scaled Data)')
plt.xlabel('Number of clusters')
plt.ylabel('Inertia')*

将 KMeans 应用于我们的 3 t-SNE 衍生组件,我们能够获得 0.39 的轮廓得分。如果您还记得从 KMeans 获得的 PCA 的 3 个主成分的轮廓分数是 0.36。一个相对较小的改进,但仍然是一个进步。

*kmeans_tsne_scale = KMeans(n_clusters=4, n_init=100, max_iter=400, init='k-means++', random_state=42).fit(tsne_df_scale)print('KMeans tSNE Scaled Silhouette Score: {}'.format(silhouette_score(tsne_df_scale, kmeans_tsne_scale.labels_, metric='euclidean')))labels_tsne_scale = kmeans_tsne_scale.labels_
clusters_tsne_scale = pd.concat([tsne_df_scale, pd.DataFrame({'tsne_clusters':labels_tsne_scale})], axis=1)*

对 SNE 霸王龙的解释可能有点违反直觉,因为 SNE 霸王龙星团的密度(即低维空间)与原始(高维空间)数据集中的数据关系不成比例。换句话说,我们可以从 k 均值聚类中获得高质量的密集聚类,但 t-SNE 可能会将它们显示为非常宽的聚类,甚至显示为多个聚类,尤其是当困惑度太低时。在阅读 t-SNE 图时,密度、聚类大小、聚类数量(在同一个 k 均值聚类下)和形状真的没有什么意义。对于同一个 KMeans 聚类,我们可以有非常广泛、密集甚至多个聚类(尤其是当困惑度太低时),但这与聚类的质量无关。相反,SNE 霸王龙的主要优势是每个克曼星团的距离和位置。彼此靠近的集群将彼此更加相关。然而,这并不意味着彼此远离的集群在比例上是不同的。最后,我们希望看到使用 t-SNE 显示的 k 均值聚类之间的某种程度的分离。

*plt.figure(figsize = (15,15))
sns.scatterplot(clusters_tsne_scale.iloc[:,0],clusters_tsne_scale.iloc[:,1],hue=labels_tsne_scale, palette='Set1', s=100, alpha=0.6).set_title('Cluster Vis tSNE Scaled Data', fontsize=15)plt.legend()
plt.show()*

*Scene = dict(xaxis = dict(title  = 'tsne1'),yaxis = dict(title  = 'tsne2'),zaxis = dict(title  = 'tsne3'))labels = labels_tsne_scaletrace = go.Scatter3d(x=clusters_tsne_scale.iloc[:,0], y=clusters_tsne_scale.iloc[:,1], z=clusters_tsne_scale.iloc[:,2], mode='markers',marker=dict(color = labels, colorscale='Viridis', size = 10, line = dict(color = 'yellow',width = 5)))layout = go.Layout(margin=dict(l=0,r=0),scene = Scene, height = 1000,width = 1000)data = [trace]fig = go.Figure(data = data, layout = layout)
fig.show()*

7.比较 PCA 和 t-SNE 衍生的 k 均值聚类

盯着 PCA/t-SNE 图并比较评估指标(如轮廓得分)将为我们提供从 KMeans 导出的聚类的技术视角。如果我们想从业务的角度理解集群,我们需要对照原始特性检查集群。换句话说,什么类型的雇员组成了我们从 KMeans 获得的集群。这种分析不仅有助于指导业务和潜在的持续分析,还能让我们深入了解集群的质量。

让我们首先将 KMeans 集群与原始的未缩放特征合并。我们将创建两个独立的数据框。一个用于 PCA 导出 k 均值聚类,一个用于 t-SNE k 均值聚类。

*cluster_tsne_profile = pd.merge(df, clusters_tsne_scale['tsne_clusters'], left_index=True, right_index=True )cluster_pca_profile = pd.merge(df, clusters_pca_scale['pca_clusters'], left_index=True, right_index=True )*

让我们通过基于每个单独的特征来比较聚类,从聚类的单变量回顾开始。

*for c in cluster_pca_profile:
    grid = sns.FacetGrid(cluster_pca_profile, col='pca_clusters')
    grid.map(plt.hist, c)*

*for c in cluster_tsne_profile:
    grid = sns.FacetGrid(cluster_tsne_profile, col='tsne_clusters')
    grid.map(plt.hist, c)*

满意度 X 上次评估

当我们从双变量的角度来研究集群时,真正的乐趣就开始了。当我们检查员工满意度和最后的评估分数时,我们可以看到从我们的主成分得到的 KMeans 聚类更加明确。我们将把我们对满意度和绩效之间的二元关系的检验集中在从 PCA 得到的聚类上。

聚类 0 代表最大数量的员工(7488),他们平均对工作相对满意,表现得分范围很广。因为这个集群代表了将近 50%的人口,所以我们看到如此大范围的满意度和绩效分数也就不足为奇了。还有一个规模较小但很明显的非常满意的高绩效员工群体。

集群 1 代表了劳动力的关键部分,因为这些是非常高绩效的员工,但不幸的是,员工满意度极低。事实上,集群 1 代表了 20%的劳动力,这只会增加情况的严重性。不满意的员工面临更大的自愿离职风险。他们的高分不仅表明了他们的熟练程度,也表明了他们潜在的机构知识。失去这些员工会导致组织知识和绩效的显著下降。看到组织级别(即经理、董事、执行董事)。失去大部分高级管理人员是一个重大问题。

第 2 组似乎代表了满意度较低的低绩效员工。同样,不满意的员工面临更高的自愿离职风险,由于他们的低绩效评估,我们可能会将这些员工视为“建设性离职”。换句话说,这些员工的自愿离职实际上可能对组织是一件好事。我们必须从两个不同的角度来看待这些结果。首先,这些雇员占总人口的 25%以上。如果这些员工中有很大一部分辞职,组织可能很难拥有足够的员工来成功履行其职能。其次,如此大比例的人不满意和表现不佳充分说明了组织的招聘、管理和培训功能。这些员工可能是组织计划不完善的结果。如果公司能更深入地探究是什么让这些员工不满意和表现不佳,那将会给自己带来巨大的好处。

最后,聚类 3 仅包含 2%的人口,并且没有可辨别的分布。我们需要一个更大的数据集来充分发掘这些员工。

*plt.figure(figsize=(15,10))
fig , (ax1, ax2) = plt.subplots(1,2, figsize=(20,15))
sns.scatterplot(data=cluster_pca_profile, x='last_evaluation', y='satisfaction_level', 
                hue='pca_clusters', s=85, alpha=0.4, palette='bright', ax=ax1).set_title(
    '(PCA) Clusters by satisfaction level and last_evaluation',fontsize=18)sns.scatterplot(data=cluster_tsne_profile, x='last_evaluation', y='satisfaction_level', 
                hue='tsne_clusters', s=85, alpha=0.4, palette='bright', ax=ax2).set_title('(tSNE) Clusters by satisfaction level and last evaluation', fontsize=18)*

满意度 X 平均每月小时数

非常有趣的是,满意度和绩效得分之间的二元关系几乎等同于满意度和每月平均工作时间。同样,从 PCA 得到的 k 均值聚类明显更加明显,让我们将注意力集中在 PCA 特征上。

看起来,PCA 集群 0 中的员工不仅表现出色,而且平均每月工作时间也很长。总的来说,这些结果是有希望的,因为大多数员工总体上是快乐的,表现令人钦佩,并且工作时间很长。

集群 1 员工再次不满意,平均每月工作时间最长。如果你还记得的话,这些人也是非常优秀的员工。这些长时间的工作很可能会对他们的整体工作满意度产生影响。请记住,第 1 类员工中有一个人数较少但正在崛起的群体,他们总体上感到满意,表现非常好。在我们的数据集中可能不止有 4 个聚类。

集群 2 的员工不仅表现不佳,而且平均月工作时间也最低。请记住,这个数据集可能有点失真,因为一个月工作 160 小时被视为全职。

最后,聚类 3 再次没有出现在图中。

*plt.figure(figsize=(15,10))
fig , (ax1, ax2) = plt.subplots(1,2, figsize=(20,15))
sns.scatterplot(data=cluster_pca_profile, x='average_montly_hours', y='satisfaction_level', 
                hue='pca_clusters',s=85, alpha=0.4, palette='bright', ax=ax1).set_title(
    '(PCA) Clusters by Satisfaction Level and Average Monthly Hours',fontsize=18)sns.scatterplot(data=cluster_tsne_profile, x='average_montly_hours', y='satisfaction_level', 
                hue='tsne_clusters', s=85, alpha=0.4, palette='bright', ax=ax2).set_title(
    '(tSNE) Clusters by Satisfaction Level and Average Monthly Hours', fontsize=18)*

满意度 X 花在公司的时间(任期)

当我们继续我们的二元图时,我们比较了满意度和在公司或任期内花费的时间。不足为奇的是,PCA 主成分的 k 均值聚类要明显得多。t-SNE 图与 PCA 图的形状相似,但其聚类更加分散。通过查看 PCA 图,我们发现了一个关于 0 类或绝大多数(50%)员工的重要发现。群组 0 中的员工主要在公司工作了 2 到 4 年。这是一个相当常见的统计数据,因为随着任期的增加,延长任期(即 5 年以上)的员工数量将会减少。此外,他们的总体高满意度表明平均任期可能会增加,因为满意的员工不太可能辞职。

分类 1 有点难以描述,因为它的数据点分散在两个不同的分类中。总的来说,他们的任期在 4 到 6 年之间变化不大,然而,他们的满意度却相反。有一个集群是高工作满意度和一个更大的集群与非常低的工作满意度。看起来集群 1 可能包含两个不同的雇员组。那些对工作很开心又很不满意的人。也就是说,尽管他们的满意度不同,但他们的工作表现、平均每月工作时间和任期都更高。看起来第一组要么快乐要么不快乐,都是非常努力和忠诚的员工。

聚类 2 遵循与上述图相同的模式。他们不仅对工作不满意,而且平均任期也是最低的。然而,重要的是要记住这些员工占组织的 26%。失去这些员工中的很大一部分会产生问题。最后,不满意的员工不太可能参与组织公民行为,如利他主义、尽责、乐于助人,而更有可能参与反生产的工作行为,这可能会产生有毒的文化。从任期、公司级别、离职率、地点和工作类型等方面剖析这些员工将非常有助于诊断问题和制定行动计划。

最后,第三组仍然过于分散,无法为我们提供任何有用的信息。根本没有足够的员工来识别任何有用的模式。

*plt.figure(figsize=(15,10))
fig = plt.subplots(1,2, figsize=(20,15))
ax1 = sns.catplot(data=cluster_pca_profile, x='time_spend_company', y='satisfaction_level', 
                hue='pca_clusters', jitter=0.47, s=7, alpha=0.5, height=8, aspect=1.5, palette='bright')
ax2 = sns.catplot(data=cluster_tsne_profile, x='time_spend_company', y='satisfaction_level', 
                hue='tsne_clusters', jitter=0.47, s=7, alpha=0.5, height=8, aspect=1.5, palette='bright')
ax1.fig.suptitle('(PCA) Clusters by satisfaction level and time_spend_company', fontsize=16)
ax2.fig.suptitle('(t-SNE) Clusters by satisfaction level and time_spend_company', fontsize=16)
plt.close(1)
plt.close(2)*

**

满意度 X 项目数量

然而,五氯苯甲醚衍生的聚类再次优于 SNE 霸王龙,只是略微领先。集群 0 继续遵循类似的趋势,总体上快乐的员工有平均数量的项目。没什么可过分兴奋或担心的。这可能为组织的员工指出理想的项目数量。

集群 1 的员工也在跟随他们的趋势,我们在之前的图表中已经看到了这一点。看到项目数量时,非常不满意正在极其努力地工作的员工。如果你还记得的话,这个小组在这个月也工作了很长时间。我们再一次看到一个较小但明显的满意的员工团队在平均数量的项目上工作。这与我们在满意度 X 任期、平均每月工作时间和上次评估分数中看到的趋势相同。肯定还有另一群非常满意和努力工作的员工。在招聘方面更深入地了解这些员工(即来源、招聘人员)、管理实践、薪酬,可能会导致对成功的招聘和雇用实践的洞察。这些见解也可能有助于组织了解如何转化表现不佳的员工。

集群 2 也在跟随它的趋势。不太满意的员工在很少的项目上工作。这种趋势在所有满意的双变量关系中都可以看到。

第三组太小,无法看出任何明显的趋势。

*plt.figure(figsize=(15,10))
fig = plt.subplots(1,2, figsize=(20,15))
ax1 = sns.catplot(data=cluster_pca_profile, x='number_project', y='satisfaction_level', 
                hue='pca_clusters', jitter=0.47, s=7, alpha=0.5, height=8, aspect=1.5, palette='bright')
ax2 = sns.catplot(data=cluster_tsne_profile, x='number_project', y='satisfaction_level', 
                hue='tsne_clusters', jitter=0.47, s=7,alpha=0.5, height=8, aspect=1.5, palette='bright')
ax1.fig.suptitle('(PCA) Clusters by Satisfaction Level and Number of Projects', fontsize=16)
ax2.fig.suptitle('(t-SNE) Clusters by Satisfaction Level and Number of Projects', fontsize=16)
plt.close(1)
plt.close(2)*

**

8.摘要

我们当然可以继续检查每个聚类和我们的特征之间的二元关系,但我们看到了一个明确的趋势。以下是我们敦促您检查的其余关系图。

本文的目的是检验不同降维技术(即主成分分析和 t-SNE)将对 KMeans 产生的聚类质量产生影响。我们检查了一个相对干净的人力资源数据集,包括超过 15,000 名员工及其工作特征。从最初的 7 个特征,我们设法将数据集减少到 3 个主要成分和 3 个 t-SNE 成分。我们的轮廓分数范围从 0.25(未缩减数据集)到 0.36(主成分分析)和 0.39 (t-SNE)。通过目测,PCA 和 t-SNE 衍生的 KMeans 聚类与未简化的原始 7 个特征相比,在数据聚类方面做得更好。直到我们开始比较主成分分析和 t-SNE 均值聚类在员工工作特征方面的差异,我们才发现了显著的差异。PCA 方法提取的聚类似乎产生更清晰和明确的聚类。

从商业的角度来看,如果没有对 PCA KMeans 聚类进行总结,我们会对读者造成伤害。

聚类 0: 该聚类包含数据集中的大多数员工(50%)。这个集群主要包含普通员工。他们的工作满意度保持在一个平均水平,他们的月工作时间有一个很大的范围,但没有延伸到极限。他们的表现也是如此,因为他们大多保持着令人满意的成绩。这些员工参与的项目数量平均也在 3 到 5 个之间。我们看到的这些员工的唯一异常值是他们在公司相对年轻的任期(2-4 年)。这些结果并不罕见,因为即使是拥有足够员工的组织也会在许多因素上遵循高斯分布。随着样本量的增加,我们在统计上将有更高的概率选择位于分布中间的员工,离群值对分布的拉动将越来越小(即回归到平均值)。很容易理解为什么 KMeans 创建了这个员工集群。

集群 1: 有时,这群员工有一定的双重性或并列性。一方面,我们看到了一群非常不满意的员工,他们取得了绝对惊人的绩效分数,工作时间非常长,管理着大量的项目,任期超过平均水平,并且零事故发生。换句话说,对组织不满的非常努力和有价值的员工。如果这些员工开始流动,公司将在生产力和知识方面遭受重大损失。

另一方面,我们看到了一个较小但明显的非常满意的员工群体,他们有着惊人的绩效分数、高于平均水平的项目数量、每月工作时间和任期。无论如何,公司有幸拥有模范员工。满意度无疑是分裂因素,因为当我们将团队的表现与任期甚至项目数量进行比较时,这两个独特的团队往往会合并。

看起来还有其他员工没有被 KMeans 识别出来。对于 KMeans 算法来说,也许 5 个甚至 6 个聚类可能是更好的标准。

聚类 2: 虽然这个聚类没有像聚类 0 那样被很好地定义,但是我们能够在数据中看到一个明确的趋势。平均而言,这些员工对公司并不十分满意。他们的表现通常处于较低水平。他们在最少的项目上每月工作最少的时间。他们的任期平均在 3 年左右。

集群 3: 最后,集群 3 的员工约占数据集的 2%,这使得几乎不可能在数据中识别出任何可辨别的趋势。

*plt.figure(figsize=(15,10))
fig = plt.subplots(1,2, figsize=(20,15))
ax1 = sns.catplot(data=cluster_pca_profile, x='Work_accident', y='satisfaction_level', 
                hue='pca_clusters', jitter=0.47, s=7, alpha=0.3, height=8, aspect=1.5, palette='bright')
ax2 = sns.catplot(data=cluster_tsne_profile, x='Work_accident', y='satisfaction_level', 
                hue='tsne_clusters', jitter=0.47, s=7, alpha=0.3, height=8, aspect=1.5, palette='bright')
ax1.fig.suptitle('(PCA) Clusters by Satisfaction Level and Work Accident', fontsize=16)
ax2.fig.suptitle('(t-SNE) Clusters by Satisfaction Level and Work Accident', fontsize=16)
plt.close(1)
plt.close(2)*

**

*plt.figure(figsize=(15,10))
fig = plt.subplots(1,2, figsize=(20,15))
ax1 = sns.catplot(data=cluster_pca_profile, x='number_project', y='last_evaluation', 
                hue='pca_clusters', jitter=0.47, s=7, alpha=0.4, height=8, aspect=1.5, palette='bright')
ax2 = sns.catplot(data=cluster_tsne_profile, x='number_project', y='last_evaluation', 
                hue='tsne_clusters', jitter=0.47, s=7, alpha=0.4, height=8, aspect=1.5, palette='bright')
ax1.fig.suptitle('(PCA) Clusters by Last Evaluation and Number Projects', fontsize=16)
ax2.fig.suptitle('(t-SNE) Clusters by Last Evaluation and Number Projects', fontsize=16)
plt.close(1)
plt.close(2)*

**

*plt.figure(figsize=(15,10))
fig , (ax1, ax2) = plt.subplots(1,2, figsize=(20,15))
sns.scatterplot(data=cluster_pca_profile, x='average_montly_hours', y='last_evaluation', 
                hue='pca_clusters', s=85, alpha=0.3, palette='bright', ax=ax1).set_title(
    '(PCA) Clusters by Last Evaluation and Average Montly Hours',fontsize=18)sns.scatterplot(data=cluster_tsne_profile, x='average_montly_hours', y='last_evaluation', 
                hue='tsne_clusters', s=85, alpha=0.3, palette='bright', ax=ax2).set_title(
    '(tSNE) Clusters by Last Evaluation and Average Montly Hours', fontsize=18)*

*plt.figure(figsize=(15,10))
fig = plt.subplots(1,2, figsize=(20,15))
ax1 = sns.catplot(data=cluster_pca_profile, x='time_spend_company', y='last_evaluation', 
                hue='pca_clusters', jitter=0.47, s=7, alpha=0.5, height=8, aspect=1.5, palette='bright')
ax2 = sns.catplot(data=cluster_tsne_profile, x='time_spend_company', y='last_evaluation', 
                hue='tsne_clusters', jitter=0.47, s=7, alpha=0.5, height=8, aspect=1.5, palette='bright')
ax1.fig.suptitle('(PCA) Clusters by Last Evaluation and Time Spend Company', fontsize=16)
ax2.fig.suptitle('(t-SNE) Clusters by  Last Evaluation and Time Spend Company', fontsize=16)
plt.close(1)
plt.close(2)*

**

*plt.figure(figsize=(15,10))
fig = plt.subplots(1,2, figsize=(20,15))
ax1 = sns.catplot(data=cluster_pca_profile, x='Work_accident', y='last_evaluation', 
                hue='pca_clusters', jitter=0.47, s=7, alpha=0.4, height=8, aspect=1.5, palette='bright')
ax2 = sns.catplot(data=cluster_tsne_profile, x='Work_accident', y='last_evaluation', 
                hue='tsne_clusters', jitter=0.47, s=7, alpha=0.4, height=8, aspect=1.5, palette='bright')
ax1.fig.suptitle('(PCA) Clusters by Last Evaluation and Work Accident', fontsize=16)
ax2.fig.suptitle('(t-SNE) Clusters by Last Evaluation and Work Accident', fontsize=16)
plt.close(1)
plt.close(2)*

**

*plt.figure(figsize=(15,10))
fig = plt.subplots(1,2, figsize=(20,15))
ax1 = sns.catplot(data=cluster_pca_profile, x='number_project', y='average_montly_hours', 
                hue='pca_clusters', jitter=0.47, s=7, alpha=0.5, height=8, aspect=1.5, palette='bright')
ax2 = sns.catplot(data=cluster_tsne_profile, x='number_project', y='average_montly_hours', 
                hue='tsne_clusters', jitter=0.47, s=7,  alpha=0.5, height=8, aspect=1.5, palette='bright')
ax1.fig.suptitle('(PCA) Clusters by Number Project and Average Montly Hours', fontsize=16)
ax2.fig.suptitle('(t-SNE) Clusters by Number Project and Average Montly Hours', fontsize=16)
plt.close(1)
plt.close(2)*

**

*plt.figure(figsize=(15,10))
fig = plt.subplots(1,2, figsize=(20,15))
ax1 = sns.catplot(data=cluster_pca_profile, x='number_project', y='time_spend_company',
                hue='pca_clusters', jitter=0.4, s=30, alpha=0.2, height=8, aspect=1.5, palette='bright')
ax2 = sns.catplot(data=cluster_tsne_profile, x='number_project', y='time_spend_company', 
                hue='tsne_clusters', jitter=0.4, s=30, alpha=0.2, height=8, aspect=1.5, palette='bright')
ax1.fig.suptitle('(PCA) Clusters by Number Project and Time Spend Company', fontsize=16)
ax2.fig.suptitle('(t-SNE) Clusters by Number Project and Time Spend Company', fontsize=16)
plt.close(1)
plt.close(2)*

**

*plt.figure(figsize=(15,10))
fig = plt.subplots(1,2, figsize=(20,15))
ax1 = sns.catplot(data=cluster_pca_profile, x='Work_accident', y='number_project',
                hue='pca_clusters', jitter=0.45, s=30, alpha=0.2, height=8, aspect=1.5, palette='bright')
ax2 = sns.catplot(data=cluster_tsne_profile, x='Work_accident', y='number_project', 
                hue='tsne_clusters', jitter=0.45, s=30, alpha=0.2, height=8, aspect=1.5, palette='bright')
ax1.fig.suptitle('(PCA) Clusters by Number Project and Work Accident', fontsize=16)
ax2.fig.suptitle('(t-SNE) Clusters by Number Project and Work Accident', fontsize=16)
plt.close(1)
plt.close(2)*

**

*plt.figure(figsize=(15,10))
fig = plt.subplots(1,2, figsize=(20,15))
ax1 = sns.catplot(data=cluster_pca_profile, x='time_spend_company', y='average_montly_hours', 
                hue='pca_clusters', jitter=0.47, s=7, alpha=0.4, height=8, aspect=1.5, palette='bright')
ax2 = sns.catplot(data=cluster_tsne_profile, x='time_spend_company', y='average_montly_hours', 
                hue='tsne_clusters', jitter=0.47, s=7, alpha=0.4, height=8, aspect=1.5, palette='bright')
ax1.fig.suptitle('(PCA) Clusters by Time Spend Company and Average Monthly Hours', fontsize=16)
ax2.fig.suptitle('(t-SNE) Clusters by  Time Spend Company and Average Monthly Hours', fontsize=16)
plt.close(1)
plt.close(2)*

**

*plt.figure(figsize=(15,10))
fig = plt.subplots(1,2, figsize=(20,15))
ax1 = sns.catplot(data=cluster_pca_profile, x='Work_accident', y='average_montly_hours', 
                hue='pca_clusters', jitter=0.47, s=7, alpha=0.4, height=8, aspect=1.5, palette='bright')
ax2 = sns.catplot(data=cluster_tsne_profile, x='Work_accident', y='average_montly_hours', 
                hue='tsne_clusters', jitter=0.47, s=7, alpha=0.4, height=8, aspect=1.5, palette='bright')
ax1.fig.suptitle('(PCA) Clusters by Work Accident and Average Monthly Hours', fontsize=16)
ax2.fig.suptitle('(t-SNE) Clusters by Work Accident and Average Monthly Hours', fontsize=16)
plt.close(1)
plt.close(2)*

**

*plt.figure(figsize=(15,10))
fig = plt.subplots(1,2, figsize=(20,15))
ax1 = sns.catplot(data=cluster_pca_profile, x='Work_accident', y='time_spend_company', 
                hue='pca_clusters', jitter=0.45, s=40, alpha=0.2, height=8, aspect=1.5, palette='bright')
ax2 = sns.catplot(data=cluster_tsne_profile, x='Work_accident', y='time_spend_company', 
                hue='tsne_clusters', jitter=0.45, s=40, alpha=0.2, height=8, aspect=1.5, palette='bright')
ax1.fig.suptitle('(PCA) Clusters by Work Accident and Time Spend Company', fontsize=16)
ax2.fig.suptitle('(t-SNE) Clusters by Work Accident and Time Spend Company', fontsize=16)
plt.close(1)
plt.close(2)*

**

向迈克尔·斯科特解释线性回归

原文:https://towardsdatascience.com/explaining-linear-regression-to-michael-scott-973ed050493c?source=collection_archive---------53-----------------------

如果我能在办公室的一个场景中总结出统计学对我的意义,那就是这个:

来源: Wiffle Gif

公式、希腊符号、复杂名称之间(均方根对数误差,有人吗?!),我发现核心概念变得难以理解或学习起来令人生畏。这篇文章的目标是让你直观地了解线性回归如何工作如何解释一个回归公式、r 用来表示什么。我的目标是不要像迈克尔那样,而要像奥斯卡那样,在这集有趣又有启发性的节目中,他教迈克尔什么是预算盈余。

来源: Tumblr

什么是线性回归,我们如何计算回归公式?

以奥斯卡为例,我将提出一个场景,我们正在经营一个柠檬水摊位,并希望找到最完美的配方来最大限度地提高销售额。假设我们要研究的第一个变量是甜度(每罐方糖),我们想了解它是否是售出杯子数量的良好预测指标。

我们收集了一罐柠檬水中的方糖数量以及从该罐中售出的玻璃杯数量的观察数据。来源:作者。

乍一看,甜度和售出的玻璃杯之间似乎存在正线性关系,越甜的柠檬水通常售出的玻璃杯越多。然而,我们无法评估甜度对销售量的预测效果,也无法预测加入超过 6 块方糖后售出的玻璃杯数量。这就是线性回归(有时称为普通最小二乘回归)的用武之地。

线性回归通过可用的数据点绘制最佳拟合直线,最佳拟合定义为最小化每个点和直线之间的距离总和。然后,我们可以使用这条线,根据添加的方糖数量来预测未来的销售,并了解销售数量的变化有多少可以归因于甜味。

线性回归的通用公式是 y = mx + b,其中 y 是 y 轴上的高度,m 是直线的斜率,x 是 x 轴上的位置,b 是直线与 y 轴相交的点。回归线的一个好的起始坐标是(x 平均值,y 平均值)。

趋势线的起始坐标是(3,18)-平均值 x 和平均值 y。该线从这里开始,因为该线的其余部分是通过绘制远离平均值的标准偏差来创建的。来源:作者。

接下来,我们找到穿过平均值的直线的斜率。这可以通过将 y 的标准偏差除以 x 的标准偏差 (std y / std x) 来实现,在我们的示例中,这给出了 5.18。

观测数据的汇总统计。来源:作者。

标准差告诉我们所有点和样本平均值之间的平均距离,所以用 y 标准值除以 x 标准值,就可以知道 x 轴上每变化一个单位,y 轴上平均可以变化多少个单位。此时,我们有一个工作版本的斜率(5.18),但如果我们将它乘以相关系数(5.18 x 0.88 = 4.55),它会更好地拟合这些点。这是因为 5.18 的斜率假设甜度和销售量之间存在完美的线性关系,但我们已经可以通过散点图看出这种关系并不遵循完美的直线。将斜率乘以关系的强度将使斜率更接近数据点。

我用蓝色的原始斜率(假设完全线性关系)和绿色的乘以相关系数的斜率绘制了回归线。两条趋势线都相当不错,但是你可以看到绿线更接近黄色的观察数据点。来源:作者。

希望你没有放弃这个柠檬水摊,决定只在 Instagram 上卖 Fit Tea,因为我保证我们快完成了。当我们第一次开始计算斜率时,你会记得我们的第一个点是(3,18)红色的。但是,我们创建的回归线都不经过这条线。那是因为我们还需要计算直线(b)的截距,这样会移动整条直线。

我们可以通过将已知的坐标(3,18)代入回归公式,求解 b。

18.29 = 4.55(3) + b

18.29 = 13.67 + b

b = 4.62

来源:作者。

这就是我们得到的——柠檬水甜度和售出杯数之间的线性回归,手工计算!(好吧——Google sheets,但在一个有 R 和 scikit-learn 的世界里,这还不如手工操作。)

解释回归公式

现在我们已经计算了公式,我们可以继续这个公式对我们的柠檬水摊位的影响。斜率(m)可以解释为x 每变化 1 个单位,y 就有 m 个变化。在我们的例子中,这意味着一罐柠檬水中多加一块方糖会多卖出 4.55 杯,多加两块方糖会多卖出 9.1 杯,依此类推。对于一块方糖来说,这是一个不错的投资回报!

y 截距(b)可以解释为当 x 等于 0 时 y 等于什么。在我们的例子中,这意味着即使柠檬水中没有糖,我们也能卖出 4.62 杯柠檬水(也许有人在烤鲑鱼,在紧要关头需要柠檬汁?)

从我们的柠檬水摊例子中,你可能已经知道解释回归公式有一些限制。即使一个公式在数学上行得通,你也需要考虑你试图预测的真实世界的背景。糖可能与柠檬水的销量成正相关,直到它达到某一点,之后它会迅速下降,因为柠檬水变得太甜了。

这张 GIF 有些令人不安,但这就是人们对你的柠檬水的感觉,如果你只根据线性回归来决定放多少糖# truthhacks # LizzosLinearRegression Source:Giphy

还有一点需要考虑的是,你可以把负的 x 值代入公式,得到对 y 的预测,但是在现实世界中,没有负方糖这种东西。

用 r 评估模型性能

在这一点上,基于散点图可视化,我们对模型的表现有了一个大致的概念。但是,如果我们想要量化性能并与其他模型进行比较,我们可以使用 r 值。r 有时被称为决定系数。

r 测量 y 中方差的百分比,可以通过 x 上的回归来解释,并且可以通过平方皮尔逊相关系数来获得。在我们的例子中,0.88 = 0.77,这意味着售出的柠檬水的 77%的差异可以单独归因于甜味!

在我们的例子中,我们只模拟了一个因变量(柠檬水甜度)的影响。然而,在具有多个变量的更复杂的模型中,一次添加一个变量并测量 r 的变化是理解每个变量在预测因变量中的重要性的好方法。

结论和附加资源

我希望你觉得这篇文章有趣又有启发性——至少,你喜欢我的 gif!我知道网上有很多线性回归教程,但我注意到其中很多都专注于在 Python / R 中实现回归,或者如何简单地应用公式。我觉得缺乏对这个概念的直观理解,想写一篇文章让你更好地理解为什么这个公式有效。

如果你还在寻找额外的材料来更好地理解这个概念,我建议看看汗学院关于二元数值数据的整个系列。它比我的文章更深入,甚至有练习题供您浏览。

快乐学习!

奥斯卡出局。来源:期限

用石灰解释机器学习分类器

原文:https://towardsdatascience.com/explaining-machine-learning-classifiers-with-lime-def5adaacfea?source=collection_archive---------39-----------------------

石灰在实践中的效果如何?

机器学习算法可以在分类、预测、异常检测和许多其他难题中产生令人印象深刻的结果。理解结果的基础通常是复杂的,因为许多算法是黑盒,很难看到它们的内部工作。可解释的 AI 是一个术语,指的是为 ML 算法输出提供人类可理解的解释的技术。

可解释的人工智能很有趣因为 许多 原因包括能够推理所使用的算法,我们用来训练它们的数据,以及更好地理解如何使用这些算法测试系统。

LIME局部可解释的模型不可知解释是最近似乎在该领域受到关注的一种技术。LIME 的想法是给它一个单独的数据点,以及要使用的 ML 算法,它将尝试为该特定数据点的 ML 算法的输出建立可理解的解释。如“因为发现此人打喷嚏咳嗽(datapoint 特征),所以很大概率是得了流感(ML 输出)”。

有大量介绍性的 文章 围绕石灰但我觉得我需要一些更具体的东西。所以我在几个分类器和数据集/数据点上尝试了一下。本文讨论了这些结果。

对于不耐烦的人,我可以总结石灰似乎很有趣,并在正确的方向上前进,但我仍然发现解释细节令人困惑。这并没有让我对解释很有信心。对于易于理解和高可信度的解释,似乎还有很多路要走。

如果故事很枯燥,给自己拿一杯酸橙饮料,继续读下去。图片来自 PixabaySteve buiss NNE

实验设置

概观

本文的实验分为三个部分。首先,我尝试使用 LIME 来解释专门为表格数据设计的三种不同 ML 算法的输出。第二,我尝试解释一般神经网络架构的输出。第三,我尝试了一个与前两个问题相反的回归问题,前两个问题检查了一个分类问题。这三个部分都使用 LIME 来解释一些数据点,每个数据点都来自不同的数据集。

反转值

作为一个小实验,对于我实验中的每个 ML 算法,我选取了一个被 LIME 评为对数据点的解释有较高贡献的单一特征,并反转(或改变)了它们的值。然后,我在相同的数据点上重新运行了 ML 算法和 LIME,改变了单个值,并比较了解释。

在大多数情况下,反转特征是二元分类特征(在一种情况下,分类具有 4 个可能的值),使得反转过程变得明显(例如,将性别从男性变为女性或者相反)。这样做的目的只是为了查看更改石灰权重较高的要素的值是否会导致 ML 算法输出和相关石灰权重变量重要性的较大变化。

数据集和要素

不同部分使用的数据集:

  • 《泰坦尼克号》:一个特定的人有哪些特征可以被归类为幸存者?
  • 心脏病 UCI :什么特征导致一个特定的人被归类为有心脏病风险?
  • 艾姆斯房屋数据集:哪些特征对预测房价有积极影响,哪些有消极影响?

树提升分类器

应用的算法:

  • 泰坦尼克号:来自 LGBM,CatBoost,XGBoost 的分类器
  • 心脏病 UCI: Keras 多层感知器神经网络架构
  • Ames 住房数据集:来自 XGBoost 的回归量

我在表格数据中看到的一些最流行的分类器是基于梯度增强决策树的分类器; LGBMCatboostXGBoost 。还有很多其他的方法,我有时也会用到,比如朴素贝叶斯、随机森林和逻辑回归。然而,LGBM、Catboost 和 XGBoost 是我最近经常首先尝试的表格数据。因此,在这一节中,我尝试使用 LIME 来解释这些 ML 算法的一些数据点。我希望对其他最大似然算法的类似评估应该遵循一个非常相似的过程。

对于这一部分,我使用的是 Titanic 数据集。这个数据集的目标是预测谁能在海难中幸存,谁不能。其特点:

  1. 存活 : 0 =否,1 =是
  2. pclass :车票等级(1 =第一,2 =第二,3 =第三)
  3. 年龄:以年为单位的年龄
  4. sibsp :泰坦尼克号上的兄弟姐妹/配偶数量
  5. 泰坦尼克号上父母/孩子的数量
  6. :票号
  7. 票价:客运票价
  8. 舱室:舱室编号
  9. 已装船:装船港(C =瑟堡,Q =皇后镇,S =南安普顿)

实际的笔记本代码在我的 GithubKaggle 笔记本上都有。

三个提升模型(LGBM、Catboost、XGBoost)中的每一个都以特征权重的形式提供对其内部统计数据的访问。详情查看部分文章文档。与 LIME 试图解释的单一数据点相反,这些类型的模型特征权重在所有数据点上提供了模型工作的更全面的视图。在下文中,我将展示这些特征权重,以便进行比较。

然而,也有一些非常好的批评使用这些类型的分类器内部统计进行特征重要性,注意到与其他技术如排列重要性和 drop-column 重要性进行比较可能也是有意义的。因此,我还计算了这里三个助推器中每一个的排列重要性,以及随后的 Keras NN 分类器。

LGBM

来自分类器/排列的特征权重

下图说明了当我通过分类器feature_importances_属性在 Titanic 数据集上训练模型时,模型本身给出的权重。

LGBM 报告了特征重要性。图片作者。

而下图所示的是 SKLearn 的置换重要性函数对同一分类器给出的。

排列重要性算法报告的特征重要性。图片作者。

比较上面的两种——基于模型统计的权重和基于排列的权重,它们的排名有很大的不同。接下来要记住一些有趣的事情。

数据点 1

下图说明了我在 Titanic 数据的测试集中选择的第一个数据点的 LIME 解释(图来自 LIME 本身):

用原始值(左)和一个反转值(右)解释单个数据点的分类。图片作者。

该图显示了同一数据点的两个版本。左边的是数据集的原始数据。右边的那个把性别属性改成了异性。这是我之前提到的高排名石灰功能的反转。如图所示,LIME 将其列为该数据点的最高等级特性。

现在,将这两个数据点变量的这些时间可视化/解释与上面的全局特征重要性(来自模型内部统计和排列得分)进行比较。由 LIME 呈现的顶级特征与由作为顶级特征的全局置换重要性给出的那些特征非常匹配。事实上,这几乎是一个精确的匹配。

除此之外,图的左边说明了我对石灰的一个主要困惑。该数据点的分类器预测为:

  • 未能幸存:71%的可能性
  • 幸存几率:29%

我希望石灰特征权重显示出最高的贡献,然后是未幸存的分类。但是它显示出幸存的权重更高。到目前为止,“性别=男性”似乎是 LIME 给出的任何变量中最重的权重,它显示为指向幸存的。类似地,左图中的总体石灰特征权重为

  • 未存活:0.17+0.09+0.03+0.00=0.29
  • 幸存值:0.31+0.15+0.07+0.03+0.02+0.01 = 0.59

有趣的是未幸存的权重合计出了幸存的准确预测值。我可能认为我看问题的方式是错误的,但是我尝试用其他数据点做进一步的解释,似乎表明情况并非如此。从上图的右边开始。

上图的右侧,性别颠倒,也显示了作为最高 contibutor 的性别属性。但现在,这个头衔已经升得很高了。所以或许正说明了一个女主有更高的生存变化?我不知道,但可以肯定的是分类器的预测变成了:

  • 未存活:43%
  • 幸存率:57%

类似地,乘客等级( Pclass )值已经从生存加权跃升为非生存加权。在反向情况下,石灰要素权重的总和总体上看起来没有太大的不同,但是预测发生了相当大的变化。我本来希望有简单易懂的解释,但它似乎很复杂。

数据点 2

测试集中第二个数据点的时间解释:

用原始值(左)和一个反转值(右)解释单个数据点的分类。图片作者。

对于这一点,左侧原始数据点的 ML 预测似乎更强烈地表明预测的存活机会很低(未存活为 81%),但是石灰特征权重甚至更强烈地指向相反的方向(石灰权重几乎所有特征值都有助于存活)。

另一方面,该图的右侧,性别值反转,提供了石灰重量与实际预测的相当好的匹配。石灰算法和最大似然算法的权重存活率都较高,比率相对接近。但与这里的原始数据点(性别未倒置,左侧)相比,无论如何都不一致。对于原始数据点,石灰权重与预测值不匹配,而对于改变后的数据点,预测值接近。

当然,右边的图说明了我的改变有多愚蠢(只颠倒了性别)。我不会期望的结合发生在真实数据中。但是,不管某些值组合是否合理,我希望解释能同样好地反映预测。毕竟,LIME 旨在用给定的特征来解释给定的预测,不管这些特征有多疯狂。

有趣的一点是,在这两种情况下,性别似乎总是对生存有很大影响。也许这是由于其他特征值的组合学,但是考虑到时间权重与预测似乎在数据点之间有所不同,我不太确定。如果这个值在两种情况下都表示高存活率,为什么它会有很高的解释力呢?假设所有(两个)值都表示相同的结果(而其他值没有变化)?

Catboost

来自分类器/排列的特征权重

基于模型内部的模型特征权重:

Catboost 报告了功能重要性。图片作者。

基于排列:

排列重要性算法报告的特征重要性。图片作者。

有趣的是,烤干显示负贡献。

数据点 1

使用 Catboost 的第一个数据点:

用原始值(左)和一个反转值(右)解释单个数据点的分类。图片作者。

在这种情况下,LIME 似乎将雄性列为未存活,而雌性列为存活,两者的权重都非常高。当然,这是针对单个数据点的,因此也取决于其他变量的具体值。在这种情况下,存活的对未存活的的总权重对于石灰对 ML 分级机来说并不太远。然而,性别变化对毫升产量的影响很小,而对石灰产量的影响很大。同样,似乎不太一致。

与上面的 LGBM 情况/部分相反,在这种情况下(对于 Catboost ),顶级石灰特征实际上似乎几乎完全遵循来自模型内部统计的全局特征权重。对于 LGBM 来说,情况正好相反,他们不遵循内部权重,而是遵循排列权重。令人困惑,尽管一个是特定于数据点的,另一个是全局的。

数据点 2

使用 Catboost 的第二个数据点:

用原始值(左)和一个反转值(右)解释单个数据点的分类。图片作者。

在这种情况下,LIME 为幸存于的方的变量赋予了非常高的权重,而实际的分类器几乎完全预测了非幸存。并没有让我感到非常自信。

XGBoost

来自分类器/排列的特征权重

基于模型内部统计的模型特征权重:

XGBoost 报告了功能重要性。图片作者。

基于排列:

排列重要性算法报告的特征重要性。图片作者。

数据点 1

为 XGBoost 解释的第一个数据点:

用原始值(左)和一个反转值(右)解释单个数据点的分类。图片作者。

在这种情况下,左边的一个似乎表明未能幸存的权重相当大,但实际预测在幸存未能幸存上相当平均。在右侧,权重与预测更符合石灰要素权重,似乎与预测相匹配。

至于来自模型内部和排列的时间权重和全局权重,在这种情况下,它们似乎是混合的。一些顶级特征与模型内部的顶级全局特征权重共享,一些与排列共享。

与前面的部分相比,石灰权重与模型和排列权重似乎无处不在。在内部特征权重的情况下,这可能与不同 ML 算法的一些属性有关,以及 LIME 如何考虑单个数据点与全局整个模型。然而,我希望 LIME 在排列权重方面更加一致,因为该算法在分类器之间是相同的。

数据点 2

第二个数据点:

用原始值(左)和一个反转值(右)解释单个数据点的分类。图片作者。

在这里,左边的石灰数字似乎表明更多的生存对重量,和非生存在实际的 XGBoost 预测。在右边,权重和预测似乎又更加一致了。不是很一致。

解释 Keras NN 分类器

本节使用不同的数据集克利夫兰心脏病风险。在这种情况下,反转变量不是性别,而是 cp 变量,因为在我查看的数据点上,它似乎是石灰得分最高的分类变量。这个 cp 变量也有 4 个值,而不是 2 个,但是无论如何,我希望改变一个高分变量来显示一些影响。

特点:

  1. 年龄:以年为单位的年龄
  2. 性别 : (1 =男;0 =女性)
  3. cp :胸痛类型(4 个值)
  4. trestbps :入院时的静息血压,单位为毫米汞柱
  5. 胆固醇:血清胆固醇,单位为毫克/分升
  6. 空腹血糖:空腹血糖> 120 毫克/分升
  7. 静息心电图:静息心电图结果(数值 0,1,2)
  8. *达到最大心率
  9. *运动诱发心绞痛:(1 =是;0 =否)
  10. oldpeak :运动相对于休息诱发的 ST 段压低
  11. 斜率:运动 ST 段峰值的斜率
  12. 荧光镜染色的主要血管数量(0-3)
  13. thal : 3 =正常;6 =修复缺陷;7 =可逆转缺陷

排列的特征权重

作为一个通用的神经网络框架,Keras 不提供基于模型内部统计的特征权重,这与上面的 boosters 等特定算法相反。但是基于置换的特征加权总是一个选项:

排列重要性算法报告的特征重要性。图片作者。

训练曲线

训练曲线总是很好看,所以开始吧:

模型精度与训练时期的损失。图片作者。

数据点 1

LIME 为 Keras 解释的第一个数据点:

用原始值(左)和一个改变的值(右)解释单个数据点的分类。图片作者。

这一个几乎完全预测了两个数据点都没有风险。然而,石灰重量似乎完全表明了患心脏病的风险。 cp (胸痛)值从 0 到 1 的变化已经将变量从顶级特性列表中完全删除。然而,石灰重量仍然严重显示在心脏风险侧,而 ML 算法预测几乎完全在无风险侧。非常不一致。

与全局排列权重相比,石灰权重共享相同的前 1-2 个特征,在较低等级的特征中有一些变化。

数据点 2

LIME 为 Keras 解释的第二个数据点:

用原始值(左)和一个改变的值(右)解释单个数据点的分类。图片作者。

在这种情况下,两边的预测和石灰重量更为混杂。右侧似乎在无风险的一侧比左侧有更多的权重,然而 ML 算法预测在相反的方向上有更多的偏移,朝向心脏风险的一侧。

在这种情况下,这些特征与第一个数据点完全不同,也与排列重要性给出的全局权重完全不同。这可能不是一个问题,因为 LIME 旨在解释单个数据点,而不是全局模型。然而,我确实看到了一个问题,那就是不能以任何合理的方式将石灰重量映射到预测。至少不是一贯如此。有时石灰重量与预测一致,通常不一致,有时则完全相反。

解释 XGBoost 回归变量

本节使用的艾姆斯住宅数据集中的特征:

  • 销售价格——以美元为单位的房产销售价格。这是你试图预测的目标变量。
  • 实用程序:可用的实用程序类型
  • 总体质量:整体材料和表面质量
  • GrLivArea:地面以上居住面积平方英尺
  • 外部质量:外部材料质量
  • 功能:家庭功能评级
  • 厨房质量
  • 壁炉质量
  • 车库汽车:车库在汽车容量中的大小
  • YearRemodAdd:改造日期
  • 车库面积:车库的面积,以平方英尺为单位

数据点 1

解释单个数据点回归预测值的时间。图片作者。

正如一篇 Python 数据文章所讨论的,LIME 结果对于分类的推理似乎比回归更直观。对于回归,它应该显示特征值如何影响预测回归值的一些相对值。在这种情况下,这将是预测特定特征值如何影响房价的方式。

但是,这其中的含义有点不清楚。例如,什么东西的权重为正意味着什么?还是消极?关于什么?什么是基线,或者什么是衡量标准?对解释回归模型进行更彻底的评估将是有趣的,但至少可以应用石灰。就像分类一样,结果可能会更直观一些。

数据分布

出于兴趣,这里描述了上面显示的特性的数据分布。

不同变量的数据分布。图片作者。

也许可以对特征值分布如何与这些变量的石灰权重相关进行一些分析,并使用这些分析作为进一步分析与预测价格相关的石灰结果的手段。也许有一天有人会..🙂

结论

与模型内部统计给出的所有全局特征权重以及基于排列的权重相比,我在本文中展示的结果通常共享一些顶级特征。并且使用相同的算法比较不同数据点的解释,看起来有一些变化,其中特征时间在每个数据点中排名最高。总的来说,考虑到石灰应该是什么,这一切都是有意义的。解释单个数据点,其中全局重要特征可能经常(并且平均来说应该)排名靠前,但是单个数据点可能有所不同。

一般来说,时间可视化似乎是可视化数据点的特性重要性的好方法。我喜欢这些特性在一个方向相对于另一个方向的权重。尝试接近一个点的值来得出一个解释的想法似乎也有道理。然而,我在实验中看到的许多结果似乎不太合理。呈现的石灰重量似乎经常与 ML 算法的实际预测相反。

我试着在网上寻找这方面的见解,这本书的第章对石灰的局限性进行了很好的讨论,也许它解释了一些问题。这本书的章节最后说在使用石灰时要非常小心,以及石灰参数如何影响结果和给出的解释。这似乎与我在上面看到的一致。

我发现的许多文章(有些在开头有链接)提供了一个例子,或者一些来自 LIME papers 的图表,并且简单地掩盖了对结果的解释,很少提供对它如何在更大规模上表现的见解。对我来说,找到一些符合假设的例子并使技术看起来很好似乎很简单。这在学术界是很常见的,你想展示你研究的好结果。然而,如果我真的尝试使用它,这并不一定符合我的期望。在我的实验中,我得到了我在这里展示的结果,在这里,我无法在整个数据集中自信地将 LIME 结果与分类器输出进行匹配。

更有用的可能是理解其局限性,而不是期望 LIME(或任何其他技术)在每种情况下都能完美工作。当结果看起来与展示(广告)材料不同时,我有一种感觉,也许零件展示是精心挑选的。我自己也从事过研究,石灰是研究的成果,这当然是它的工作方式。你不会因为没有展示出好的结果,或者没有展示出你的方法中的问题而被接受论文或者获得资助和任期。但是不讨论它们会使我们更难找到实用的工作技术。

总的来说,有了这些结果,除了一些实验,我不会真的使用石灰。主要是因为我看不出自己有多信任这些类型的结果,不管销售论点如何。但总的来说,这似乎是一项有趣的工作,我喜欢其中隐含的想法。也许这将有助于产生对我更好的其他新的可解释的人工智能技术。沿着这些思路,也很高兴看到这些类型的方法被集成为 ML 平台产品和服务的一部分。

类似的方法还有其他有趣的方法。SHAP 是一个似乎很受欢迎的,Eli5 是另一个。有人甚至说 LIME 是 SHAP 的一个子集,它应该比 LIME 采用的采样方法更完整。也许有一天值得努力做一个比较..

如果你对我错过了什么有什么想法,或者可以做得更好,很高兴听到:)。

这次到此为止。干杯。

原载于 2020 年 6 月 10 日 http://swenotes.wordpress.com**的

解释公平的衡量标准

原文:https://towardsdatascience.com/explaining-measures-of-fairness-f0e419d4e0d7?source=collection_archive---------27-----------------------

Scott Lundberg/Corgarashu-Adobe Stock

公平和偏见

通过将现代可解释的人工智能方法应用于公平测量,避免机器学习中公平度量的黑盒使用。

这篇实践文章将可解释的人工智能与公平性度量联系起来,并展示了现代可解释性方法如何增强量化公平性度量的有用性。通过使用 SHAP (一个流行的可解释的人工智能工具),我们可以分解公平的衡量标准,并为模型的每个输入特征之间的任何观察到的差异分配责任。解释这些量化的公平度量可以减少依赖它们作为不透明的公平标准的趋势,相反,促进它们作为理解模型行为如何在群体之间不同的工具的明智使用。

量化的公平度量标准试图给机器学习中的公平定义带来数学上的精确性【1】。然而,公平的定义深深植根于人类的伦理原则,等等价值判断,这些价值判断通常关键取决于使用机器学习模型的背景。这种对价值判断的实际依赖在量化公平测量的数学中表现为有时相互不相容的公平定义之间的一组权衡【2】。由于公平性依赖于上下文相关的价值判断,将量化的公平性指标视为不透明的公平性黑盒度量是危险的[ 3 ],因为这样做可能会模糊这些重要的价值判断选择。

如何用 SHAP 来解释模型公平性的度量

本文不是关于如何选择模型公平性的“正确”度量,而是关于解释您正在使用的任何度量。哪个公平指标最合适取决于你的具体情况,例如适用什么法律,机器学习模型的输出如何影响人们,以及你对各种结果和权衡的价值。在这里,我们将使用经典的人口均等指标,因为它很简单,并且与不同影响的法律概念密切相关。人口统计奇偶校验表明,机器学习模型的输出在两个或更多个群体之间应该是相等的。人口统计学上的奇偶差异是两组样本之间模型结果差异的度量。

由于 SHAP 将模型输出分解为与原始模型输出具有相同单位的要素属性,因此我们可以首先使用 SHAP 将模型输出分解为每个输入要素,然后使用每个输入要素的 SHAP 值分别计算该要素的人口统计奇偶差异(或任何其他公平性度量)。因为 SHAP 值的总和等于模型的输出,所以 SHAP 值的人口统计奇偶差异的总和也等于整个模型的人口统计奇偶差异的总和。

在各种模拟场景中,SHAP 公平的解释是什么样的

为了帮助我们探索解释量化公平指标的潜在效用,我们考虑一个基于信用核保的简单模拟场景。在我们的模拟中,有四个驱动贷款违约风险的潜在因素:收入稳定性、收入金额、支出约束和一致性。这些潜在的因素没有被观察到,但它们不同程度地影响着四个不同的可观察到的特征:工作经历、报告的收入、信用查询和逾期付款。使用这种模拟,我们生成随机样本,然后训练一个非线性 XGBoost 分类器来预测违约概率(查看本文的笔记本版本获取相关的 Python 代码)。同样的过程也适用于 SHAP 支持的任何其他模型类型,只要记住对更复杂的必然模型的解释隐藏了更多的模型细节。

通过在完全指定的模拟中引入性别特定的报告错误,我们可以观察到这些错误引起的偏差如何导致男女之间的人口统计均等差异。在我们的模拟案例中,真实的标签(如果有人将拖欠贷款)在统计上与性别无关。因此,发现男性和女性之间的任何差异意味着一个或两个群体由于特征测量误差、标签误差或模型误差而被错误地建模。如果您预测的真实标签(可能不同于您可以访问的训练标签)在统计上不独立于您正在考虑的敏感特征,那么即使是没有错误的完美模型也无法通过人口统计奇偶校验。在这些情况下,公平性解释可以帮助您确定哪些人口统计差异来源是有效的,因此应该保留在模型中,哪些来源是无效的,应该移除。

场景 A:无报告错误

我们的第一个实验是一个简单的基线检查,我们避免引入任何性别特定的报告错误。虽然我们可以使用任何模型输出来衡量人口统计的均等性,但我们使用来自二元 XGBoost 分类器的连续对数优势分数。正如预期的那样,这一基线实验的结果是男性和女性的信用评分之间没有显著的人口统计学差异。我们可以通过将女性和男性的平均信用评分之间的差异绘制成条形图,并注意到零接近误差范围(注意,负值意味着女性的平均预测风险低于男性,正值意味着女性的平均预测风险高于男性):

现在,我们可以使用 SHAP 在模型的每个输入要素中分解模型输出,然后计算归因于每个要素的组件的人口统计奇偶差异。如上所述,由于 SHAP 值总和等于模型的输出,因此每个要素的 SHAP 值的人口统计奇偶差异总和等于整个模型的人口统计奇偶差异。这意味着下面的条形之和等于上面的条形(我们的基线情景模型的人口奇偶差异)。

情景 B:对妇女收入的低报偏见

在我们的基线场景中,我们设计了一个模拟,其中性别对模型使用的任何特征或标签都没有影响。在方案 B 中,我们在模拟中引入了女性收入的低报偏差。这里的重点不是真实世界中女性收入被低报有多现实,而是我们如何识别性别偏见的引入并理解其来源。通过绘制女性和男性之间平均模型输出(违约风险)的差异,我们可以看到,收入少报偏差已经产生了显著的人口统计均等差异,女性现在比男性有更高的违约风险:

如果这是一个真实的应用程序,这种人口统计奇偶差异可能会触发对模型的深入分析,以确定是什么导致了差异。虽然这一调查具有挑战性,仅给出一个单一的人口统计学差异值,但给出基于 SHAP 的每个要素的人口统计学差异分解就容易多了。使用 SHAP,我们可以看到有一个来自报告的收入特征的重大偏见,这增加了女性比男性不成比例的风险。这使我们能够快速识别哪个特征具有导致我们的模型违反人口统计均等性的报告偏差:

在这一点上,重要的是要注意我们的假设是如何影响 SHAP 公平解释的。在我们的模拟场景中,我们知道女性实际上与男性具有相同的收入特征,因此当我们看到报告的收入特征对女性的偏差低于男性时,我们知道这是来自报告的收入特征中的测量误差偏差。解决这个问题的最佳方法是找出如何消除该特征的测量误差。这样做可以创建一个更准确的模型,而且人口差异也更小。然而,如果我们假设女性实际上比男性挣得少(因此这不仅仅是一个报告错误),那么我们就不能“修正”报告的收入特征。相反,我们必须仔细考虑如何最好地解释两个受保护群体之间违约风险的实际差异。仅仅使用 SHAP 公平解释是不可能确定这两种情况中哪一种正在发生的,因为在这两种情况下,报告的收入特征将导致男女预测风险之间观察到的差异。

情景 C:对妇女逾期付款的低报偏见

为了验证 SHAP 人口统计均等解释能够正确地检测差异,无论效果的方向或来源特征如何,我们重复了之前的实验,但是我们引入了妇女迟付率的低报偏差,而不是收入的低报偏差。这导致了模型输出的显著人口统计学平价差异,现在女性的平均违约风险低于男性:

正如我们所希望的那样,SHAP 的解释正确地强调了延迟支付的特征是该模型的人口均等差异的原因,以及影响的方向:

情景 D:女性违约率的低报偏差

上面的实验集中在为特定的输入特征引入报告错误。接下来,我们考虑当我们通过对女性违约率的低报偏差在培训标签上引入报告错误时会发生什么(这意味着女性的违约率比男性低)。有趣的是,对于我们的模拟场景,这在模型的输出中没有导致显著的人口统计奇偶差异:

在 SHAP 的解释中,我们也看不到任何人口均等差异的证据:

场景 E:女性违约率的低报偏差,取 2

起初,当我们引入女性违约率的低报偏差时,可能会令人惊讶地发现,人口统计学上的均等差异并没有产生。但这是因为我们的模拟中的四个特征都没有与性别显著相关,所以它们都不能有效地用于模拟我们引入训练标签的偏见。如果我们现在为模型提供一个与性别相关的新特征(品牌 X 购买分数),那么我们会看到人口统计奇偶差异出现,因为该特征被模型用来捕获训练标签中的性别特定偏差:

当我们解释与 SHAP 的人口统计平价差异时,我们看到,正如预期的那样,品牌 X 购买分数特征驱动了差异。在这种情况下,这不是因为我们在如何测量品牌 X 购买分数特征方面存在偏差,而是因为我们在训练标签中存在偏差,该偏差被任何与性别充分相关的输入特征捕获(因此可以作为性别的代理):

场景 F:梳理多重漏报偏见

当报告偏差的原因只有一个时,那么模型输出的经典人口统计奇偶检验和人口统计奇偶检验的 SHAP 解释都捕捉到了相同的偏差效应(尽管 SHAP 解释通常具有更大的统计意义,因为它隔离了导致偏差的特征)。但是,当一个数据集中出现偏差的原因有多种时,会发生什么呢?在这个实验中,我们引入了两个这样的偏见,一个是对女性违约率的低报,另一个是对女性工作经历的低报。这些偏差往往会在全球平均值中相互抵消,因此对模型输出的人口统计均等测试显示没有可测量的差异:

然而,如果我们看看 SHAP 对人口均等差异的解释,我们会清楚地看到两种(抵消)偏见:

确定多种潜在的抵消偏差影响可能很重要,因为虽然平均而言,对男性或女性没有不同的影响,但对个人有不同的影响。例如,在这个模拟中,没有在品牌 X 购物的女性将得到比她们应该得到的更低的信用分数,因为在工作历史报告中存在偏见。

引入受保护的特征如何有助于区分标签偏差和特征偏差

在情景 F 中,我们能够区分出两种不同形式的偏差,一种来自工作经历的少报,另一种来自违约率的少报。然而,来自违约率低报的偏差并不归因于违约率标签,而是归因于碰巧与性别相关的品牌 X 购买分数特征。这仍然给我们留下了一些关于人口统计奇偶差异的真实来源的不确定性,因为归因于输入要素的任何差异都可能是由于该要素的问题,或者是由于训练标注的问题。

事实证明,在这种情况下,我们可以通过将性别作为一个变量直接引入模型来帮助区分标签偏见和特征偏见。引入性别作为输入特征的目的是使标注偏差完全落在性别特征上,而不影响特征偏差。因此,我们可以通过比较上面的场景 F 和下面的新场景 G 来区分标签偏差和特征偏差。当然,这造成了比以前更大的人口统计均等差异,但这没什么,因为我们的目标不是减轻偏见,而是理解偏见:

SHAP 对方案 G 的解释表明,方案 F 中曾经与品牌 X 购买分数特征相关联的所有人口统计均等差异现在已经移动到性别特征,而方案 F 中与工作经历特征相关联的任何人口统计均等差异都没有移动。这可以解释为情景 F 中归因于品牌 X 购买分数的所有差异都是由于标签偏见,而情景 F 中归因于工作经历的所有差异都是由于特征偏见。

注意,将受保护特征引入模型以将标签偏差与特征偏差分开的技巧依赖于将几乎所有标签偏差置于受保护特征上的模型训练过程。这适用于默认的 XGBoost 模型,但如果您在 XGBoost 中打开按列子采样,则不会发生这种情况,因为这将强制在与受保护要素相关的要素之间共享偏差(对于岭回归补偿线性模型也会发生类似的信用扩散效应)。

结论

公平是一个复杂的话题,干净的数学答案几乎总是带有警告,并取决于假设或价值判断。这意味着,尤其重要的是,不要仅仅将公平指标作为黑盒使用,而是要寻求理解这些指标是如何计算的,以及您的模型和训练数据的哪些方面正在影响您观察到的任何差异。使用 SHAP 分解量化的公平性度量可以减少它们的不透明性。我希望这里展示的公平性解释能够帮助您更好地处理公平性评估中固有的潜在问题,从而帮助您在现实环境中使用公平性度量时减少意外后果的风险。

精确和召回:你应该使用哪一个?

原文:https://towardsdatascience.com/explaining-precision-vs-recall-to-everyone-295d4848edaf?source=collection_archive---------12-----------------------

数据科学概念

精确度、召回率、准确度和 F1 分数之间的差异

Unsplashengin akyurt 拍摄的照片

随着你在数据科学的不同方面取得进展,你会遇到各种用于评估机器学习模型的评估指标。为了确定机器学习模型的有效性,必须对其进行评估。不评估机器学习模型,就无法运行它。可用于验证模型的评估指标有:

  • 精确
  • 回忆
  • F1 分数
  • 准确(性)

每个指标都有自己的优点和缺点。确定使用哪一个是数据科学过程中的重要一步。

在这里注册一个中级会员,可以无限制地访问和支持像我这样的内容!在你的支持下,我赚了一小部分会费。谢谢!

评估指标

我们将解释上面列出的评估指标之间的差异。但是首先,为了理解这些指标,你需要知道什么是 假阳性和假阴性 以及两者之间的区别。请参阅下面的文章,了解两者之间的区别:

[## 假阳性还是假阴性:哪个更糟?

解释第一类和第二类错误

towardsdatascience.com](/false-positives-vs-false-negatives-4184c2ff941a)

为了计算不同的评估指标,您需要知道假阳性(FP)、假阴性(FN)、真阳性(TP)和真阴性(TN) 的数量。了解它们之间的区别也是有帮助的。

准确(性)

让我们从四个评估指标中最简单的一个开始——准确性。准确性是对我们的模型在观察总数中正确预测的观察数的简单衡量:

准确度= (TP + TN) / (TP + TN + FP + FN)

照片由 Igal NessUnsplash 上拍摄

例如,假设我们有一台机器可以对水果进行分类,看它是不是苹果。在数百个苹果和桔子的样本中,机器的精确度将是它正确分类为苹果的苹果数量和它分类为非苹果的桔子数量除以苹果和桔子的总数。只要苹果和橘子的数量相同,这就是一个简单有效的测量方法。否则,我们可能不得不使用不同的评估标准。

精确

精度是对我们的模型正确预测的观察值与正确和不正确预测值之比的度量。这里的观察意味着我们试图预测的事情。

李仁港Unsplash 上拍照

精度= TP / (TP + FP)

使用我们的苹果和橙子的例子,precision 将测量正确分类的苹果的数量除以正确标记为苹果的苹果和错误标记为苹果的橙子的数量。换句话说,精度衡量了我们分类的苹果中有多少实际上是橙子。**

回忆

召回是对我们的模型在观察总量中正确预测的观察数量的度量。在这里,观察也意味着我们试图预测的事情。

Benjamin Wong 在 Unsplash 上拍摄的照片

召回= TP / (TP + FN)

在我们的苹果和橘子的例子中,召回测量正确标记的苹果数量除以存在的苹果总量。换句话说,回忆衡量的是在整个水果样本中我们可能漏掉了多少个苹果。

F1 分数

如果我们把注意力放在一个分数上,我们可能会忽略另一个分数。为了解决这个问题,我们可以使用 F1 分数,它在精确度和召回分数之间取得了平衡。要计算 F1 分数,您需要知道精确度和召回分数,并将它们输入到以下公式中:

F1 得分= 2 (精确度召回率)/(精确度+召回率)

使用我们的苹果和橘子的例子,F1 分数将计算精度和召回之间的平衡。它将测量被错误分类为苹果的桔子的数量(假阳性)和没有被正确分类为苹果的苹果的数量(假阴性)。

使用哪个指标?

在构建和评估机器学习模型时,您必须能够确定要使用的最有效的评估指标。最合适的度量标准完全取决于问题。

不是准确性

大部分时间你会处理 不平衡 数据集。使用我们的苹果和橘子的例子,在现实世界中,你很可能会有一个不平衡的样本,可能是 1 比 3 的苹果和橘子。在这种情况下,使用准确性作为评估标准是不合适的,因为准确性会歪曲我们模型的有效性。

如果数据集是真正平衡的,那么准确性可能是最合适的度量标准。然而,大多数时候情况并非如此。

不平衡数据的度量

由于您最有可能处理不平衡的数据,因此精确度和召回率(以及 F1 分数)将是您的首选评估指标。使用哪一个将再次完全取决于您的模型的目标。

有毒的苹果

照片由玛利亚·特内娃Unsplash 上拍摄

假设我们试图检测一个苹果是否有毒。在这种情况下,我们希望减少假阴性的数量,因为我们希望不遗漏该批次中的任何毒苹果。回忆将是这里使用的最佳评估指标,因为它衡量了我们可能遗漏了多少毒苹果。我们并不太在意给苹果贴上有毒的标签,因为我们宁愿安全也不愿后悔。

股票投资

杰米街Unsplash 上拍摄的照片

抛开苹果的例子,假设我们开发了一个机器学习模型,来确定一只股票是否是一个好的投资。如果我们要把我们的全部净值都投入到这只股票中,我们最好希望我们的模型是正确的。精度将是这里使用的最佳度量,因为它决定了我们模型的正确性。只要我们的钱流向了由我们的模型正确预测的升值股票,我们就可以承受错过一些有利可图的股票投资。

如果您想了解使用精选评估指标预测股票投资未来的真实机器学习模型,请查看以下文章:

[## 教机器像沃伦·巴菲特一样交易股票,第二部分

利用机器学习进行股票基本面分析

medium.com](https://medium.com/better-programming/teaching-a-machine-to-trade-stocks-like-warren-buffett-part-ii-5d06427b13f7)

F1 成绩?

有时候,最大化模型的精确度和召回分数是最好的。我们什么时候想最大化这两个分数?如果我们希望我们的模型是正确的,不会错过任何正确的预测。基于这一事实,你可能会得出这样的结论:F1 分数是最值得使用的分数。虽然 F1 分数在精确度和召回率之间取得了平衡,但它并没有优先考虑其中一个。有时,根据我们面临的问题,优先考虑一个可能是最好的。

结论

正如您在本文中所了解到的,有几种不同的评估指标可用于您的机器学习模型。每种度量标准都有其特定的优点和缺点。由您决定使用哪一个来评估您的模型。在本文结束时,我们希望您了解了使用哪种评估指标以及何时应用它们。

在 Twitter 上关注我:@Marco_Santos

基于 AWS DeepRacer 为初学者讲解强化学习

原文:https://towardsdatascience.com/explaining-reinforcement-learning-for-beginners-based-on-aws-deepracer-efcefff65a9b?source=collection_archive---------38-----------------------

高级别解释强化学习如何在自主赛车中与神经网络一起工作

雅罗米尔·卡万Unsplash 上拍摄

在本文中,我们将讨论以下主题:

1.什么是强化学习?

2.什么是 DeepRacer?

3.强化学习在 DeepRacer 中的应用

4.为什么我们需要强化学习?

5.强化学习算法的一部分

6.解释学习过程

1.什么是强化学习?

强化学习的基础(作者图片)

强化学习是一种通过经验学习的机器学习算法。这个算法告诉代理在一个封闭的环境中应该采取哪一组动作来完成一个任务。之后,代理人会因为这些行为获得可量化的奖励。此奖励是一个数字分数,用于衡量这些行动相对于总体目标的调整程度。粗略地说,代理人在算法的指导下,反复尝试不同的动作集,试图使收到的总回报最大化。在每组试验之后,该算法“学习”哪些动作比其他动作更有效,并根据这些发现调整其下一次试验。这个过程持续分配的时间或试验次数。因此,该算法将能够告诉代理采取哪些行动,以获得更高的奖励。

如果这听起来像一个游戏,你没有错;强化学习遵循同样的原则!这就是为什么它被成功地用于玩游戏,如围棋和国际象棋,或视频游戏,如超级马里奥,或星际争霸,击败职业选手。其他应用包括算法交易、机器人或动态定价。

例如,想象我们决定使用强化学习来玩超级马里奥。代理(马里奥图标)观察环境(玩家可以在屏幕上看到的关卡的一部分)并执行动作(前进、后退或跳跃)。每次试炼后,它会收到一份奖励,奖励是根据它在关卡中的进度而定。

2.什么是 DeepRacer?

DeepRacer 赛车参加 2020 年 5 月资格赛(图片由作者提供)

让我们看看 AWS DeepRacer 中是如何引入强化学习原理的。亚马逊网络服务(AWS) DeepRacer 是一辆自动驾驶的赛车,它在云中的数字模拟器中接受强化学习训练。强化学习“教”汽车(也就是代理)如何在赛车道上驾驶。代理的目标是在尽可能快的时间内完成多圈。这个简短的背景将是后面几章的基础。如果您需要进一步的信息,您可以访问这里

3.强化学习在 DeepRacer 中的应用

凯特琳·威尔森Unsplash 上拍摄

DeepRacer 使用两个神经网络应用了一种特定的强化学习方法。这个算法帮助代理(汽车)在每次试验中选择最佳行动。在强化学习中,每次试验都被称为一个情节。从形式上来说,一个情节是特定状态下的动作组合。把动作想象成汽车做出的决定。状态是赛车在赛道内的位置。一旦赛车离开划定的赛道或跑完一圈,每集就结束了。

在 DeepRacer 电路中,它的(x,y)坐标描述了数百万种可能的汽车状态。每个动作都是将代理从一种状态移动到另一种状态的原因。在我们的例子中,一个动作是转向角和油门的组合。

强化学习算法执行一些计算(我们将在后面解释),并告诉汽车采取特定的行动(例如,以 0.5 米/秒的速度行驶,向左转向 30 度)。汽车执行这个动作几毫秒,然后到达另一个状态。在这个新状态中,代理执行另一个计算并决定一个动作。这个过程反复发生,直到汽车完成这一集。

该算法基于奖励函数决定采取哪些行动。这个奖励函数对代理在每个动作中的表现进行评级,就像学生在测试中被评级一样。我们必须设置奖励函数,它引导代理人实现期望的目标。在我们的例子中,我们希望赛车完成赛道(目标#1)并快速完成(目标#2)。

奖励函数使用参数来反映这些目标,这些参数是每个状态中可测量的因素。一个简单的奖励函数可以包括进度(轨迹完成的百分比)和速度参数。这样,汽车离终点线越近(目标 1),越快(目标 2),奖励越高。在 DeepRacer 中,算法从虚拟环境中接收这些和其他参数(例如,转向或距离赛道中心的距离)。该算法在告诉汽车采取何种行动之前测量这些参数,反映了与环境的互动。然后,这些参数的值将被有选择地放置在一个奖励函数中,该函数将返回一个数字,对代理的行为进行评级。根据我们的目标,我们必须选择将哪些参数添加到奖励函数中,以及它们将对奖励的大小产生什么影响。

4.为什么我们需要强化学习?

学习过最优化问题的人应该对最大化奖励函数很熟悉。对于一些优化问题,你可以得到一组全局的行动,在给定的一组约束条件下,最大化代理人可以得到的奖励。但是在 DeepRacer 的情况下,我们没有标准的优化问题,因为有几乎无限数量的动作和状态的组合。这就是为什么不可能通过执行所有可能的组合并查看哪一个是最好的来强行得出最佳答案。

在某种程度上,强化学习是关于教代理如何执行最佳的“猜测”应该采取什么行动,这可能会给出很高的回报。通常,我们从强化学习算法中所能获得的最多的是局部奖励最大值。由于我们无法有效地尝试所有组合,因此我们无法得出这是代理可以获得的全球最高奖励的结论。

5.强化学习算法的一部分

强化学习算法的部分内容(图片由作者提供)

当我们谈到强化学习时,有几种不同的算法。在本文中,我们将重点讨论强化学习的神经网络应用。该算法结合了两个神经网络来学习采取哪些行动:T2 政策网络 T3 和 T4 价值网络 T5。如果你不明白什么是神经网络,那么我强烈推荐观看来自 3Blue1Brown 的视频,尽管它们对于理解接下来的内容并不必要。

价值网络的目标是基于之前的试验产生“猜测”。在这种情况下,一个“猜测”是在一个特定的行动后得到的预期奖励。想象一下,汽车可以采取三种可能的行动。然后,这个网络根据过去的经验,估计采取这些行动的预期回报。理解价值网络如何做到这一点超出了本文的范围。为简单起见,我们将动作空间定义为汽车在给定状态下可能采取的所有动作的组合。例如,假设汽车处于状态(S39),动作空间为 3。那么,价值网络的一个输出可以是:(A1,A2,A3) = (10,100,5)。

政策网络将价值网络产生的“猜测”转化为行动。它将使用采取每项行动的预期回报,并应用一种叫做政策梯度的方法,将它们转化为概率。这些概率是给定状态下动作的分布。采取行动的预期回报越高,可能性就越大。我们将这些概率称为策略。回到我们之前在 S39 中的例子,策略可以是:(A1,A2,A3) = (0.2,0.7,0。1). A2 的概率更高,因为采取 A2 的预期回报比其余行动更高。

6.解释学习过程

安妮·斯普拉特在 Unsplash 上拍摄的照片

学习的过程包括两个阶段:探索阶段调整阶段,依次重复几次。

探索阶段,策略网络将使用价值网络提供的“猜测”来评估给定状态下的策略。使用这些相同的猜测,它将运行 x 量的试验(例如,10.000 次),进行不同的动作组合,并在每集之后接收奖励。在每个状态中,代理可能执行一个动作或另一个动作,遵循由策略设置的概率分布。

再回到我们以前的例子:(A1,A2,A3) = (0.2,0.7,0。1) 中的 S39 在所有情节中,代理到达 S39,其中 20%做出 A1 决定,70% A2 决定,10% A3 决定。注意,策略网络将永远不会输出某些动作(例如(A1,A2,A3) = (0,1,0)),而是输出一个概率,以允许代理探索回路并尝试每一个动作,即使模型预测低的预期回报。这是因为这些预期回报只不过是基于经验的“猜测”,因此可能是错误的。

请注意,在第一个探索阶段,价值网络将没有经验,并将对所有行动给予相同的预期回报。然后政策网络会把这些预期回报转化为等概率。在每个状态中,汽车将以相等的概率执行任何动作,并移动到另一个状态。因为这是第一次探索,没有积累经验,大多数剧集都以脱离轨道和相对较低的回报而告终。

在这个探索阶段结束后,将开始调整阶段。价值网络将汇编和汇总从这些试验中获得的所有回报,并调整在给定状态下执行每个特定行动的预期回报。这些新的“猜测”将反馈给政策网络,并改变采取特定行动的可能性。这将增加给予较高回报的行动的概率,同时减少导致较差回报的行动的概率。然后,代理将开始探索和调整阶段的第二次迭代。这个过程可以重复几次。

技术提示:概率增加/减少的大小将取决于:(a)在那些经历中预期的回报和获得的回报之间的差异,以及(b)政策梯度。策略梯度是强化学习的基础部分。策略网络创建一个代理函数,它近似依赖于策略变化的奖励函数的行为。简而言之,它估计了政策变化对预期回报的影响。然后算法计算策略,使期望回报最大化。为了做到这一点,我们计算这个替代函数的梯度,它将表明在调整阶段旧政策变化的方向和大小。在 DeepRacer 的情况下,策略梯度应用了一种叫做近似策略优化(PPO) 的方法。这种方法与其他方法的不同之处在于它的简单性和易于实现。此外,PPO 设置了一个上限,以避免基于几个事件的重大政策调整。关于 PPO 如何详细工作的更多信息,你可以阅读 Jonathan Hui 的这篇文章。

主要思想是,一旦我们这样做几次,我们将增加导致更高回报的行动的概率。然后,随着探索和调整阶段的每一次迭代,该算法通过采取导致更好回报的行动来不断提高其回报。因此,经过几个小时的训练,我们的赛车完成了一个快速圈速!

如果你想了解更多关于自动驾驶赛车和 DeepRacer 的内容,你可以看看这个高级指南,我的团队成员,丹尼尔建造。他收集了我们从参加 AWS-Formula 1 赛事中获得的所有见解,实现了前 1%的排名。

最后,我要感谢娜塔莉亚·科查金娜丹尼尔·冈萨雷斯帮助我创作了这篇文章,还要感谢我们的教授乔迪·宁阿尔贝托·卢比奥校对了这篇文章。最后,感谢 ESADE 商学院允许像这样的内容创作成为我们商业分析硕士课程的一部分。

向你的隔壁邻居解释强化学习

原文:https://towardsdatascience.com/explaining-reinforcement-learning-to-your-next-door-neighbor-256d2e279f8a?source=collection_archive---------69-----------------------

强化学习的直观介绍

强化学习是机器学习的一个非常有趣的子领域。而其他 ML 技术依赖于静态输入输出对来学习隐藏的规则,然后将这些规则应用于看不见的数据以获得可能的结果。强化学习算法往往会随着时间的推移自动学习最佳决策。

RL 技术广泛用于解决难题和开发能够在数百种不同的游戏中击败人类的智能代理。除此之外,RL 还有多种实际应用,例如

  1. 机器人技术:工业自动化
  2. 开发学生培训系统
  3. RL 基础 神经架构搜索 (NAS)

让我们了解一下 RL 是如何工作的—

= >为了帮助您了解内容:-

  1. 如果我们有其他 ML 技术,为什么还要使用 RL?
  2. 什么是强化学习?
  3. 强化学习的并发症。
  4. 结论

1.如果我们有其他 ML 技术,为什么要使用 RL?

机器学习技术,如监督学习、非监督学习,从给定的潜在历史数据中学习,然后被部署来产生关于看不见的(未来)数据的结果。这种模型的好坏取决于给定的训练数据的质量。当/如果一些新的看不见的(训练集中不存在的新的各种数据)例子出现时,这些模型会突然失效。

基于强化学习的算法能够解决这样的问题。RL 模型以这样的方式设计,即它们学习数据随时间的变化并保持高性能。让我们了解更多关于 RL 的知识—

2.什么是强化学习?

基于 RL 的算法学习随着时间自动做出最佳决策。它从过去的错误中学习,并试图在未来的每个时间点做出最佳决策。这种从经验中学习的方法与人类学习和成长的方式非常相似。这个想法让 RL 更接近人工智能的目的。让我们深入了解 RL 的更多细节——

希望大家能回忆起以前的喂蛇游戏,如果没有,那么下面的视频一定会提醒你——

https://gfycat.com/wildunevenhackee

现在让我们写下五个神奇的词,整个 RL 将围绕这五个词展开—

代理

b. 环境

c 。动作

d. 观察

e. 奖励

让我们来理解这五个与喂蛇游戏有关的术语。

代理和环境

每一个 RL 问题都可以分解成两个主要的模块— 1 .代理 2 .环境。代理是可以做一些事情(一组明确定义的事情)的东西,我们的 RL 算法的目标是教会这个代理以某种方式做那些事情,以实现特定的目标(由求解器定义)。除了代理,其他的都叫环境。代理在环境中执行所有的活动,并在每个步骤中不断改变环境的状态。

例子:关于蛇的游戏

在这里——蛇是媒介,整个绿色游乐场和诱饵/食物是环境。这条蛇可以做事情,它可以走直线,左转,右转。求解者可以定义一个目标,比如“吃尽可能多的诱饵”或“吃 100 个诱饵”。现在,我们训练有素的工作 RL 算法应该指导这条蛇采取适当的行动,以便实现求解器的目标。

https://it next . io/reinforcement-learning-with-q-tables-5f 11168862 c8

行动、观察和奖励

正如所讨论的,RL 代理做某些事情,并且可以在每一步改变环境的状态。这些东西的一个明确定义的集合被称为给定代理的动作空间。在每一步,代理从动作空间中选择一个动作(随机地——如果没有实现 RL 算法)并执行它。

这种行为可能以某种方式改变环境,而这种由行为引起的环境变化被称为观察。每一个行动步骤都与一些奖励(即一个标量值——2、5、100……任何东西)和观察。奖励是由问题解决者定义的,这样更多的奖励会使代理更接近目标。

总结:-在每一步,代理将执行一个动作并获得一些奖励,并记录观察结果。这里——我们的 RL 算法的主要目标是帮助代理在每一步选择最佳行动,以便每次都能获得良好的回报,并最终完成目标。

(从过去的行动中)收集的观察和奖励有助于 RL 算法理解环境并为代理决定下一个最佳行动。大多数情况下,使用某种监督学习算法来决定最佳移动。通常,来自游戏的观察是环境的屏幕截图,因此深度学习算法(卷积神经网络)被非常频繁地使用。

例子:关于蛇的游戏

在我们的喂蛇游戏中,RL 算法应该在每一步给蛇指令(直走,左转,右转)。这些命令应该帮助蛇吃足够的诱饵,这样我们的目标就完成了。RL 模型需要记住一点——蛇在完成目标前不能死,否则——游戏 overr .一旦我们的游戏结束, 插曲 完成,我们需要重新开始。

插曲

只是 RL 词汇表中用来定义任务结束的另一个术语。如果你的插曲在完成目标之前结束,那么你的 RL 算法需要更多的调整/训练/增强。

强化学习太酷了!

为什么我们不在所有的 ML 问题中应用 RL?

我想现在每个人都很清楚 RL 是怎么运作的了。这个概念看起来非常简单和直观,但是在实现这样的算法时,我们遇到的障碍确实很少。让我们更多地了解它们——

3.RL 的并发症

下面列出了一些使基于 RL 的模型开发变得复杂的事情——

假设我们的代理不断犯错,却没有获得任何奖励。现在,对这种错误的观察是没有结果的,可能不会向代理人显示如何进一步赢得奖励。代理可能会在这种情况下遭受损失,而 RL 可能无法解决这种情况。

二。假设你正在编写一个 RL 算法来下 棋并获胜。现在,国际象棋是一种不同的挑战性游戏,你的棋步在开始时可能没有意义,但深入下去可能会有很大的不同。在这样的问题中,你无法决定每一步的回报。这里不能写奖励函数。奖励只有一个——赢得游戏,只有在游戏结束时你才能得到它。这使得 RL 代理很难在每一步选择最佳行动。

在进入第三个复杂问题之前,让我先介绍两个更重要的术语——

探索/开发的故事

想象一下,你最近搬到了一个新的城市,你的房子周围有数以千计的晚餐地点。每天晚上,你都有一个选择,比如——我今天应该探索一个新的地方吗?还是吃弗雷迪好吃的鸡翅?现在你已经知道弗雷迪餐厅很不错,再去那里吃一次也未尝不可。但是如果你不去探索新的餐馆,你将永远不会发现更有趣的地方,甚至是最好的地方。这也是有代价的——在找到一个好地方之前,你可能需要去很多不好的地方,而且你可能真的找不到一个好地方。这种情况在现实生活中可能会非常频繁地出现。比如——换工作,换智能手机品牌……等等。

三。强化学习中的探索/利用困境

RL 代理也面临这样的情况,探索是必要的,因为一个好的奖励可能在未探索的地方等着,并且利用已经研究过的行为(通过观察)是必要的,否则你的代理就像随机一样好。求解器总是需要在这两者之间找到一个平衡,以便设计一个高效的 RL 代理。

4.结论

是的,这些并发症一直存在,现在仍然存在。但由于研究人员的固执,基于 RL 的算法已经随着时间的推移取得了巨大的进步。强化学习作为一个研究领域正变得越来越有趣和活跃。是时候找到一些相关的业务问题,并开始使用基于 RL 的算法有效地解决它们了。

参考文献:

  1. 了解自:拉潘,M. (2018)。深度强化学习实践。英国伯明翰:Packt 出版公司。
  2. 【GIF 来自:https://gfycat.com/wildunevenhackee
  3. 图片来自:https://it next . io/reinforcement-learning-with-q-tables-5f 11168862 c8

感谢阅读!请分享您的反馈/意见。

解释熊猫中带有复制警告的设置

原文:https://towardsdatascience.com/explaining-the-settingwithcopywarning-in-pandas-ebc19d799d25?source=collection_archive---------7-----------------------

照片由 NeONBRANDUnsplash 上拍摄

如果你想知道是什么导致了SettingwithCopyWarning,这就是你要找的地方!

不管你和pandas共事多久,是一天还是一年,迟早你都有可能遇到臭名昭著的SettingWithCopyWarning。在本文中,我将解释是什么导致了这个问题,以及如何正确地解决这个问题。

警告不是错误

在我深入研究技术细节之前,我想强调一下SettingWithCopyWarning是——顾名思义——一个警告,而不是一个错误。所以我们正在执行的代码很可能不会中断并产生最终结果。然而,最终的结果可能不是我们真正想要的。

我想强调这种区别的原因是,当我们看到代码实际上成功返回结果时,我们可能会忽略警告。事实上,结果可能是正确的!最佳实践是格外小心,并真正理解基本原理。通过这种方式,我们通常可以节省大量时间来识别一个不明显的 bug,这是我们一开始就可以避免的。

视图与副本

SettingWithCopyWarning相关的关键概念是视图和副本。pandas中的一些操作(和numpy一样)将返回原始数据的视图,而其他的副本。

简单地说,视图是链接到原始源的原始对象(DataFrameSeries)的子集,而副本是一个全新的对象。一般来说,当我们对副本进行的操作完成后,副本就会被丢弃。这种区别的结果是,当我们修改视图时,我们也修改了原始对象。副本不会发生这种情况,因为它们没有连接到原始对象。

描述了区别之后,SettingWithCopyWarning实际上是让我们知道我们写的代码可能做了一件事,而实际上我们想做的是另一件事。我将用一个真实的例子来说明这一点。想象一下有一个大的DataFrame。对于某些分析,您过滤(切分)了DataFrame,使其只包含完整数据的子集,例如,来自某个国家的用户。然后,您可能想要修改提取的DataFrame中的一些值,假设一个特性的最大值为 100。这是您可能遇到臭名昭著的警告的典型情况——您只想修改提取的帧,而最终却修改了源数据。你可以很容易地想象这不是你想做的事情,并可能导致以后的潜在问题。

注意:要了解一个框架是否是副本的视图,可以使用一个pandas.DataFrame的内部_is_view_is_copy方法。第一个函数返回一个布尔值,而第二个函数要么是原来的DataFrameweakref,要么是None

警告的常见事件

在这一节中,我回顾了实践中发生SettingWithCopyWarning时最常见的情况。我将使用一个小的定制DataFrame来举例说明这些情况,因为这对于理解逻辑来说已经足够了。

为了准备数据,我运行以下代码:

运行代码打印出小的DataFrame:

为了清楚地理解正在发生的事情,对于下面的每一种情况,我们将从头开始—运行get_data函数的结果。

作为将来的参考,本文是使用pandas版本 1.0.3 编写的。

1.链式分配

为了解释链式赋值的概念,我们将依次讨论构造块。赋值操作(也称为设置操作)简单地设置一个对象的值。我们可以通过创建一个列表来说明这一点:

x_list = [1, 2, 3, 4, 5]

尽管第一个例子是基于列表的,但是同样的原则也适用于数组、SeriesDataFrames(我们马上就会看到)。第二种类型的操作称为 get 操作,用于访问和返回对象的值。索引是一种 get 操作,我们可以通过运行返回的x_list[0:3]来索引列表

[1, 2, 3]

最后一个构建块叫做链接,本质上是指链接多个索引操作,比如x_list[0:3][1],返回 2。

已经描述了所有的单个部分,通过链接赋值我们指的是链接和赋值的组合。是时候提及我们的玩具DataFrame了。首先,我们对DataFrame进行切片,以显示B特性值大于 12 的观察值。

X = get_data()
X[X['B'] > 12]

只有 2 行符合该标准。让我们用 999 替换C特性的值。

X[X['B'] > 12]['C'] = 999
X[X['B'] > 12]['C']

运行上面的行会导致臭名昭著的警告:

SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

在生成的输出中,我们看到这些值没有被替换!

我们看到警告是因为我们链接了两个索引操作。这些连锁操作在后台独立执行。第一部分是 get 操作,它返回一个包含所有B值大于 12 的行的DataFrame。第二部分是设置的操作,并且是对新建的DataFrame进行由获取的操作。所以我们不是在修改原来的DataFrame

当我们在一行中使用两个方括号时,这是非常明显的,然而,通过使用lociloc或访问列的点方法也会发生同样的情况。例如,运行X.loc[X[‘B’] > 12][‘C’] = 999会给出同样不正确的结果。

为了正确替换DataFrame中的值,我们需要以如下方式使用loc:

X.loc[X['B'] > 12, 'C'] = 999
X[X['B'] > 12]['C']

我们可以看到原来的DataFrame中的值被成功替换。

2.隐藏链接

隐藏链接可能是一个很难调试的问题,因为问题到底出在哪里通常不是很明显。我们将复习一个例子。首先,让我们加载数据,并使用前面案例中的知识,创建一个DataFrame,它是原始数据的子集。我们过滤掉所有具有大于 101 的C特性值的行。

经常发生的是,我们探索并进一步处理新的DataFrame。让我们想象运行几行代码来进一步检查temp对象,比如pandas.DataFrameshapedescribeplot方法。

我们实际上没有在这里打印输出,因为这不是重要的部分。现在,再运行几行代码后,让我们用 999 替换第一行tempC特性的值:

temp.loc[2, 'C'] = 999

在这样做的时候,我们遇到了我们的老朋友:

SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

让我们检查原始的和提取的DataFramesC的值:

print(f"New DataFrame: {temp.loc[2, 'C']}")
print(f"Original DataFrame: {X.loc[2, 'C']}")# New DataFrame: 999
# Original DataFrame: 102

所以我们真的发生了?警告的原因在于,链式索引可能出现在两行中,而不仅仅出现在一行中。当我们创建新的DataFrame时,我们使用了 get 操作的输出。这可能是原件DataFrame的副本,也可能不是。在我们检查之前无法知道。引用关于链式索引的pandas文档:

除了简单的情况之外,很难预测它[链式索引]将返回一个视图还是一个副本(这取决于数组的内存布局,对此 pandas 不能保证),…

所以当我们索引temp来分配新值时,我们实际上使用了链式索引。因此,我们可能在修改temp的同时也修改了X

棘手的部分是,在实际的代码库中,负责隐藏链式分配的两行代码可能被几十行代码分开,这使得识别潜在问题相当困难。

为了解决这个问题,我们可以通过使用copy方法直接指示pandas创建原始DataFrame的副本。下面,我们使用这种方法来避免隐藏链接:

我们看到运行代码生成了正确的输出。

3.一个假阴性的例子

最后,我们回顾一下[3]中提到的假阴性(pandas没有通知我们SettingWithCopyWarning,而它实际上应该通知我们)。当我们在对DataFrame的多个列进行切片时使用链式索引时,就会发生这种情况。我们来看两个简单的案例。

X = get_data()
X.loc[X['A'] > 2, ['A', 'B']]['A'] = 999
X

X = get_data()
X[['A', 'B', 'C']]['A'] = 999
X

两者都产生了下面的结果,但没有真正显示出SettingWithCopyWarning

从上面的图片中我们可以看到,这些值并没有按照我们想要的那样被修改。由于在索引操作中包含多个列,因此没有显示警告。我们可以很容易地验证在单个列的情况下不会发生这种情况——运行X[[‘C’]][‘C’] = 999
会产生警告并且不会修改X

关于警告来源的更深层次的背景

我们可以说pandasnumpy继承了视图和副本的概念。在引擎盖下,pandas使用numpy进行高效的数据存储和操作。在numpy中,视图和副本遵循一组特定的规则,并以可预测的方式返回(更多信息见【5】)。那么为什么pandas不是这样呢?问题在于numpy数组被限制为单一数据类型。正如我们所知,pandas的情况并非如此。

实际上,在多数据类型DataFrame上的索引( get 操作)将总是返回一个帧的副本。对单一类型框架的相同操作几乎总是返回基于单一numpy数组的视图,这是解决问题的最有效方式。然而,正如我们在文档的引用中已经看到的,返回视图依赖于对象的内存布局,不幸的是不能保证。

综上所述,pandas尽最大努力将其通用的索引方法(由于这种方法,它非常流行,并且基本上是用 Python 进行数据科学研究的先决条件)和使用底层numpy数组的效率结合起来。这导致了一些小麻烦,然而,这种权衡绝对是值得的,并且通过正确理解pandas在引擎盖下如何工作,这些问题可以被克服。

结论

在这篇文章中,我解释了 pandas 中副本和视图之间的区别,以及它们与臭名昭著的SettingWithCopyWarning之间的关系。主要的想法缩小到知道什么是链式索引以及如何成功地避免它。一般规则是:

  • 如果要改变原来的DataFrame,使用单赋值。
  • 如果您想要制作一个DataFrame的副本,请使用copy方法显式地这样做。

遵循这两条规则可以节省您调试一些奇怪情况的大量时间,尤其是在冗长的代码库中。

同样值得一提的是,SettingWithCopyWarning仅在我们使用 set 操作(赋值)时发生。然而,最好也避免对 get 操作进行链式索引。这是因为链式操作通常比较慢,如果您后来决定将 set 操作添加到代码中,可能会导致问题。

您可以在我的 GitHub 上找到本文使用的代码。一如既往,我们欢迎任何建设性的反馈。你可以在推特上或者评论里联系我。

参考

[1]https://pandas . pydata . org/pandas-docs/stable/user _ guide/indexing . html

[2]https://pandas . pydata . org/pandas-docs/stable/user _ guide/indexing . html # return-a-view-vs-a-copy

https://github.com/pandas-dev/pandas/issues/9767

[4]https://www . practical data science . org/html/views _ and _ copies _ in _ pandas . html

[5]https://scipy-cookbook . readthedocs . io/items/viewsvscopies . html

有影响力的解释

原文:https://towardsdatascience.com/explaining-with-impact-38935eccc7fe?source=collection_archive---------58-----------------------

活动讲座

谢尔登·费尔南德斯和迈克尔·圣·朱尔斯| TMLS2019c

在多伦多机器学习峰会上的演讲

关于扬声器:

DarwinAI 首席执行官 Sheldon Fernandez 是一位经验丰富的高管,也是技术和企业社区中备受尊敬的思想领袖。在他的职业生涯中,他将人工智能等新兴技术应用于企业客户的实际场景中。谢尔登也是一位有成就的作家和演说家。他在许多场合的许多会议上发表过演讲,包括湾区著名的智库 Singularity University,并撰写了许多主题的技术书籍和文章,包括人工智能和计算创造力。

— Michael St. Jules 是 DarwinAI 的高级研究开发人员,自 2018 年初以来一直在该公司工作。他于 2014 年和 2016 年分别获得卡尔顿大学和渥太华大学数学专业的 BMath 和 M.Sc 学位,主要研究数学分析、逻辑和计算机科学,硕士论文和出版物为量子密码学。然后他在滑铁卢大学的计算数学 MMath 中重点研究了机器学习和深度学习,于 2017 年毕业,并在加入 DarwinAI 之前担任 Alexander Wong 博士的研究助理。

关于演讲:

围绕人工智能的普遍进展引发了人们对可解释的人工智能(XAI)的浓厚兴趣,其目标是产生由机器学习算法做出的可解释的决策。鉴于深度神经网络的复杂性和“黑箱”性质,特别感兴趣的是对深度神经网络如何做出决策的解释。

鉴于该领域的起步阶段,对可解释方法的性能评估的探索有限,大多数评估集中在对当前方法的主观和视觉解释上。在本次演讲中,演讲者介绍了两种量化性能指标,用于通过一种新颖的决策影响分析来量化深度神经网络上可解释性方法的性能:

  1. 影响得分,评估具有强烈信心降低影响或决策改变影响的关键因素的百分比;和
  2. 影响范围,评估输入中受到不利影响的因素的百分比范围。我们进一步考虑使用这种方法与众多最新的可解释性方法进行综合分析。

带着冲击力讲解

订阅我们的月刊,直接在你的邮箱✉️中接收我们最好的文章、视频和播客

用 SHAP 和莱姆解释你的机器学习模型!

原文:https://towardsdatascience.com/explaining-your-machine-learning-models-with-shap-and-lime-9da525748e28?source=collection_archive---------36-----------------------

帮助您揭开一些人可能认为您的机器学习模型是“黑箱”的神秘面纱

大家好!欢迎再次回到另一个数据科学快速技巧。这篇特别的文章对我来说是最有趣的,不仅因为这是我们迄今为止处理的最复杂的主题,而且也是我刚刚花了几个小时自学的一个主题。当然,还有什么比想出如何把它教给大众更好的学习方法呢?

在开始之前,我已经把这篇文章中展示的所有作品上传到了一个奇异的 Jupyter 笔记本上。如果你想更深入地了解,你可以在我的个人 GitHub 找到它。

因此,尽管这是一个幕后的非常复杂的话题,但我还是打算尽可能地为尽可能多的观众淡化这个话题。尽管这最终是一篇为数据科学从业者设计的帖子,但我认为对于任何业务人员来说,理解他们为什么应该关注这个话题也同样重要。

在进入如何计算/可视化这些值之前,让我们先对为什么我们会关心这个话题建立一些直觉。

为什么你应该关心 ML 的可解释性

如果你对机器学习有所了解,你会知道你将一些数据放入一个预测模型,它会在另一端产生一个输出预测。但是让我们诚实地说:你知道在那个模型里面到底发生了什么吗?请记住,我们正在谈论的是在引擎盖下进行大量复杂数学运算的算法。实际上,这可能就是你的 ML 模型现在的样子:

你真的不知道那个黑盒里发生了什么。数学完全掩盖了这些特性在模型中的重要性。那么,如果特性 1 和 2 承担了所有的重量,而特性 3 和 4 没有给模型增加任何价值呢?您更希望看到的是类似这样的东西:

好的,在我们的例子中,我们显然使用了外行人的术语。但这一点很重要,因为如果你的模型依赖一两个特征来做所有的预测,这可能表明有问题。这也可能表明你正在使用某种可能被认为是不道德的功能。例如,今天许多公司会避免在他们的模型中使用性别或种族之类的东西,因为它们往往会在模型中产生许多不希望的偏见。

通过一个具体的例子来理解这些概念总是更有意义,所以我们将回到我们在最近几篇文章中使用的数据集来具体演示 SHAP 和莱姆:泰坦尼克号数据集。

用泰坦尼克号数据集建模

好吧,所以在我们使用泰坦尼克号数据集的最后几个帖子中,我们创建了非常差的模型,因为我们的重点更多的是学习新技能。为了有效地演示 SHAP 和莱姆,我们真的需要一个模型来显示某种程度的“尝试”(?)这样我们就可以看到 ML 的可解释性是如何发挥作用的。现在,我不打算在这篇文章中花时间向您展示我的数据清理和建模部分的所有代码(您肯定可以在我的 GitHub 中找到这里,但是我将快速总结产生最终模型的特性。

  • PClass :记录这个人上船的船票等级。(1 档、2 档或 3 档。)
  • 年龄箱:根据人的年龄,他们被分成一般人群。这些群体包括儿童、青少年、年轻人、成年人、老年人和未知人群。
  • 性别(Gender) :注明该人是男是女。
  • 船舱段:如果这个人有船舱,我就拉出他们可能在哪个舱。这些可以是 A 舱、B 舱、C 舱,也可以是无舱。
  • 登船:记下此人最初出发的三个地点之一。
  • SibSp(兄弟姐妹/配偶):记录此人在船上有多少兄弟姐妹和/或配偶。
  • Parch (Parent / Child) :记录这个人在船上有多少父母/孩子。

对于实际的模型本身,我选择使用来自 Scikit-Learn 的一个简单的随机森林分类器。将数据分为训练集和验证集,我得到了验证集的以下指标:

这些指标并不是很好,但是对我们的学习来说很好。至少我们将在下一节看到这些特性有多重要。让我们继续前进!

用 SHAP 和石灰解释

在这一点上,我们目前还不知道我们的随机森林模型是如何使用这些特征进行预测的。我猜性别和年龄是影响因素,但我们还不太清楚。

这是聪明人基于博弈论开发复杂算法来生成这些东西的地方,以更好地解释我们当前的黑匣子。当然,我指的是 SHAP 和酸橙。现在,我要 100%诚实:我不太熟悉这些算法是如何在引擎盖下工作的,所以我甚至不打算在这篇文章中尝试这样做。相反,我会给你介绍这篇关于 SHAP 的文章另一篇关于石灰的文章

就像 Scikit-Learn 抽象出我们的随机森林分类器的底层算法一样,我们将使用一些简洁的 Python 库来抽象出 SHAP 和莱姆的内部工作原理。要使用它们,您所需要做的就是对两者进行简单的 pip 安装:

pip install shappip install lime

在高层次上,这两种工作方式都是将您的训练数据和模型交给“解释者”,然后您可以将任何观察结果传递给“解释者”,它会告诉您该模型的特性重要性。这听起来像是天书,但是我们会用泰坦尼克号的例子来说明这一点。

首先,我们需要从我们的验证集中选出两个人,我们知道他们分别没有幸存和幸存。这是因为我们将通过 SHAP 和莱姆的解释来了解这些人的哪些特征对我们的模型的生存能力有最大的影响。(还是那句话,你可以在我的笔记本里看到这个作品。)

通过 SHAP 解释

让我们从 SHAP 开始。这里的语法非常简单。我们将首先实例化 SHAP 解释器对象,使我们的随机森林分类器(rfc)适合该对象,并插入每个人以生成他们可解释的 SHAP 值。下面的代码向您展示了如何为第一个人做这件事。要对人员 2 进行同样的操作,只需交换适当的值。

# Importing the SHAP library
import shap# Instantiating the SHAP explainer using our trained RFC model
shap_explainer = shap.TreeExplainer(rfc)
shap.initjs()# Getting SHAP values for person 1
person_1_shap_values = shap_explainer.shap_values(person_1)# Visualizing plot of expected survivability of person 1
shap.force_plot(shap_explainer.expected_value[1], person_1_shap_values[1], person_1)# Visualizing impact of each feature for person 1
shap.summary_plot(person_1_shap_values, person_1)

这真的就是全部了!让我们在下面的截图中看看我们刚才所做的实际输出。首先,一号人物。

根据上图,我们的模型预测第一个人有 94%的机会幸存,这是正确的。阅读起来有点困难,但看看第二个视觉,看起来最有影响的因素包括这个人的性别,这个人是否有三等票,以及这个人是否是个孩子。在这种情况下,第一个人是一个女孩,所以这两个因素促使我们的模型说是的,这个人可能活了下来。

现在让我们看看第二个人。

我们的模型预测这个人可能没有活下来,不幸的是这是正确的。再看看这里的影响因素,看起来第二个人是一个没有小屋的成年男性。我不是历史爱好者,但我猜想妇女和儿童首先被救出船外,所以对我来说,第一个人(一个女孩)幸存而第二个人(一个成年男子)没有生还是很有意义的。

好吧,让我们继续我们的石灰解释!

通过石灰的可解释性

使用 LIME 的语法有点不同,但是从概念上讲,SHAP 和 LIME 做了很多相似的事情。

# Importing LIME
import lime.lime_tabular# Defining our LIME explainer
lime_explainer = lime.lime_tabular.LimeTabularExplainer(X_train.values, mode = 'classification', feature_names = X_train.columns, class_names = ['Did Not Survive', 'Survived'])# Defining a quick function that can be used to explain the instance passed
predict_rfc_prob = lambda x: rfc.predict_proba(x).astype(float)# Viewing LIME explainability for person 1
person_1_lime = lime_explainer.explain_instance(person_1.iloc[0].values, predict_rfc_prob, num_features = 10)
person_1_lime.show_in_notebook()

让我们来看看 LIME 为每个人展示了什么。

我在这里有点偏见,但我认为这些石灰值的用户界面更容易阅读 SHAP。我们看到每个人是否幸存的预测概率是一样的。在中间,我们看到了 10 大特征的影响力。与 SHAP 相比,莱姆在解释能力上有一点点不同,但它们大体上是相同的。我们再次看到,性别是一个巨大的影响因素,无论这个人是否是个孩子。在右边,它们很好地显示了这个特定观察的精确值。

那么 SHAP 和莱姆哪个更好呢?我会说都不是。虽然两者都可以很好地相互补充,但我认为向您的业务用户展示这两者是很重要的,这样可以让他们更好地了解哪些功能最容易解释。我认为没有人能肯定地说 SHAP 和莱姆哪个更准确,所以两者都要展示出来。无论如何,语法很容易理解!

这就结束了另一个帖子!希望你们都喜欢这个。我要说这是我迄今为止最喜欢学习和写的,我期待着在我自己的工作中应用这一点。在下一篇文章中与大家见面!

解释性建模和统计因果推理教程

原文:https://towardsdatascience.com/explanatory-modeling-f1f890d11ac2?source=collection_archive---------27-----------------------

通过对新冠肺炎死亡危险因素的案例研究

来源:马库斯·斯皮斯克

介绍

也许最近的疫情和相关的封锁的一个积极的副作用是,我们可以实现被搁置的项目。有一段时间,我想写一篇关于统计模型解释的文章,我需要的只是时间、动机和数据。这个 Kaggle 数据集由 2019 年 12 月至 2020 年 3 月期间在武汉市采样的 1085 例新冠肺炎病例组成,它是首批发布的包含患者级别记录的数据集之一。这篇 TDS 文章很好地概述了数据及其字段。拥有患者级别的记录意味着有足够的粒度来得出关于一个人的个人属性如何与暴露于新冠肺炎病毒的死亡风险相关的医学见解。

本解释性建模指南要求对以下主题有中级理解:

死亡率风险分析需要一个带有死亡事件记录的响应变量。因此,我们将尝试将这种反应表达为预测变量的函数,这些变量与新冠肺炎对人类健康的影响直接或间接相关。解释模型的目的是解释而不是预测死亡的结果,其中解释的目的是应用统计推断以便:

  • 识别对死亡事件有统计显著影响的预测变量
  • 估计重要预测因素的影响程度
  • 量化我们估计的不确定性

换句话说,我们感兴趣的是一个用于推理而不是预测的模型,或者更准确地说,一个更倾向于因果推理而不是预测推理的模型。理解两个之间的差异至关重要,因为它决定了我们选择建模方法的基本原理。解释性分析需要一个不太灵活(高偏差)但更加可解释的模型,它使用概率推理来近似一个假设的数据生成过程。在执行了模型选择过程之后,得到的“最佳”模型可以包括对响应具有显著影响的预测器,尽管不一定提高预测准确度。

从表面上看,一个不错的模型选择是二项式 logit 模型,通常称为二元逻辑回归。考虑到模型选择后缺乏用于推理的工具以及我们验证因果假设的意图,我们将使用领域知识来精选假设对死亡事件有直接或间接影响的独立变量;然后,我们将通过将我们的数据拟合到我们的模型假设的数据生成过程中来量化它们的影响。本文的目的是实现一个解释性建模的管道,它展示了如何使用一个小而稀疏的数据集来获得关于新冠肺炎的医学见解。为此,我有选择地将理论概念与 R 编程语言中的代码片段结合起来。为了达到理想的结果,采取以下步骤:

  • 广义线性模型的定义
  • 数据插补与数据删除
  • 渐近准则下的模型选择
  • 基于 bootstrap 推理的模型验证
  • 模型解释

本作品中使用的代码库可以在这里找到。

让我们从加载必要的库和数据开始。

*# Load required libraries:*
library(mice)
library(VIM)
library(jtools)
library(ggplot2)
library(gridExtra)
options(repr.plot.width = 14, repr.plot.height = 8)*# Load COVID-19 data:*
dat0 = read.csv('./data/COVID19_line_list_data.csv', 
                header = T, sep = ',')
print(names(dat0))

在预测建模场景中,我们可以在这一点上选择我们想要的任何数量的变量,然后让模型选择算法来决定我们应该考虑哪些变量。然而,在解释性建模场景中,算法模型选择之后的推断并不是一个可行的选项。一个激烈的变量选择过程预计会偏向系数 p 值,并剥夺我们使用模型的渐近性质。有各种方法来克服这种现象,但是,为了解释因果关系,我们必须依靠领域知识来隔离我们认为有影响的变量。例如,一个显而易见的变量选择是与患者的年龄、性别、原籍国、症状开始日期、住院日期以及他/她是否是武汉本地人相关的变量。诸如“症状”、“康复”、“医院 _ 就诊 _ 日期”和“暴露 _ 开始”等变量看似相关,但实在太稀疏,没有任何用处。

dat1 = dat0[c("location", "country", "gender", "age", "reporting.date", "symptom_onset", "hosp_visit_date", "visiting.Wuhan", "from.Wuhan", "death", "recovered", "symptom")]
print(head(dat1, 5))

隔离最相关的预测值后,我们的新数据集应该如下所示:

*# Binarize response:*
dat1$y = 0
dat1$y[dat1$death==1] = 1   
dat1$y = as.factor(dat1$y)

我们有一个代表死亡事件的二元响应变量,我们准备更深入地研究建模过程。

模型假设

在不打算进入太多细节的情况下,我认为为了理解模型可解释性是如何获得的,经历一些基本步骤是很重要的。

回想一下,在二项式 logit 模型中,响应变量是遵循二项式或伯努利分布的随机向量:

其中P(Y =1)= P,对于所有观测值 i= 1 ,…,N.

这是与线性回归的一个重要区别,在线性回归中,随机因素来自协变量,而不是响应。我们的模型做出了其他几个假设,分析的第一步应该是确保我们的场景满足这些假设。

关于响应分布的第一个假设已经确定,但是可以扩展为假设其期望值E[Y =1】= NP,对于所有的 i 、可以与协变量的线性组合相关联:

这相当于:

对于观测值 i =1、…、 N ,解释变量 j =1、…、 M ,链接函数 g 。其次,假设解释变量 X 相互独立,不相关。第三,观测值也被假定为相互独立和不相关的。一些进一步的假设与最大似然估计对数据大小的限制有关。众所周知,当参数的数量增加超过一定限度时,最大似然估计往往会过度拟合,但幸运的是,我们的数据并没有接近那个危险区域。

一个更普遍的假设是,我们的数据是随机抽样的结果。这在预测的情况下尤其重要,因为响应变量(样本类别平衡)的分布对截距项的估计有直接影响,因此也对模型预测有直接影响。好的一面是,斜率系数不受影响。换句话说,逻辑回归中的非代表性类别平衡对预测有直接影响,但对解释没有影响。因为我们的目标是后者,所以我们可以不做平衡样本假设。

定义 GLM

我们现在可以将我们的模型定义为二项式 GLM。回想一下,任何 GLM 都由三个部分定义:

  • 随机成分
  • 系统的组成部分
  • 链接功能

假设随机变量 Y 将新冠肺炎案例的二元结果映射为伯努利试验,使得:

其中 p 是观察到单个患者死亡结果 P ( Y =1)的概率, N 是具有 Y 观察值的患者数量, n 是每个患者的试验次数(在我们的例子中, n =1,对于所有 n )。这是我们的随机成分。

ηYE[Y]=η的期望值,假设存在一个函数 g (。)使得 g ( η )是协变量的线性组合:

对于 X = x ,对于所有的 j ,上式的 RHS 就是我们的系统分量。

我们选择 g (。)= logit (。)作为反函数的环节,逻辑(。),可以把我们模型的输出挤在[0,1]区间内,这也恰好是E[Y]=η= p,for n =1 的期望范围。因此,通过将η=NP=p插入我们的链接函数 g (。),我们得到:

从中我们得出模型的最终结构:

为了简单起见,我们省略了索引:

随机分量现在可以改写为:

注意:最后一个表达式解释了为什么逻辑回归是字面上的回归和而不是分类。

数据准备

这一步包括检查数据集中的方差和稀疏性,以及生成新的预测值。定义了 GLM 并检查了其假设后,我们可以继续检查数据中的类别平衡:

table(dat1$y)

该数据集与我们对新冠肺炎死亡率的先验知识一致,这使我们的分析进入了罕见事件检测的领域。利用新冠肺炎的领域知识,我们可以立即考虑将'年龄'和'性别'作为相关的预测因子,但我们也可以尝试创建新的预测因子。从第一次症状出现到患者住院的时间可能会提供一些信息。我们可以很容易地通过计算住院日期和症状发作日期之间的天数来创建这个预测值。

*# Cast 'Date' types:*
dat1$hosp_visit_date = as.character(dat1$hosp_visit_date)
dat1$symptom_onset = as.character(dat1$symptom_onset)
dat1$hosp_visit_date = as.Date(dat1$hosp_visit_date, format="%m/%d/%y")
dat1$symptom_onset = as.Date(dat1$symptom_onset, format="%m/%d/%y")
dat1$from.Wuhan = as.factor(dat1$from.Wuhan)*# Create 'days before hospitalization' variable:*
dat1$days_before_hosp = as.numeric(as.Date(dat1$hosp_visit_date, format="%m/%d/%y") - as.Date(dat1$symptom_onset, format="%m/%d/%y"))
dat1$days_before_hosp[dat1$days_before_hosp < 0] = NA

因此,在稍后的模型选择过程中,可以将计算症状发作和住院之间天数的变量添加到我们的模型中。数据的当前状态似乎包括相当多的缺失值(显示为' NA '),因此可视化稀疏性前景将是有见地的。

# Plot missing values:
aggr(dat1, col = c('green','red'), numbers = TRUE, sortVars = TRUE, 
     labels = names(dat1), cex.axis = .5, gap = 2, 
     ylab = c("Proportion in variable","Proportion in dataset"))

每列缺失值的比例

上表显示了每个变量中缺失值的比例。下面的柱状图展示了同样的观点(左图)。

缺失的数据环境

右侧的网格显示了单个预测的缺失值占数据集中所有值的比例。例如,矩阵的最后一行,全绿色,告诉我们 39%的数据集案例在所有变量中都没有缺失值。前一行告诉我们,前三个变量(出现的顺序)中缺少 20%的事例,前一行显示前五个变量中缺少 15%的值,依此类推。

很明显,有很多缺失的数据,而且它们分散在各处,所以在剔除和插补之间的选择并不明显。在试图回答这个问题之前,我们应该尝试使用少量但大量的预测因子来拟合基线模型。我们希望在接近原始数据集的数据集上拟合一个模型,但我们将执行缺失值移除,而不是插补。具有稀疏性或低方差的变量(例如'国家'、'医院 _ 访问 _ 日期'、'症状 _ 发作')应该被排除,并且为了最小化数据移除,包含在基线模型中的变量应该具有尽可能少的缺失值。

模型 0(基线模型)

模型选择过程将包括拟合几个候选模型,直到我们找到最接近“真实”模型的一个。在每次比较时,将根据特定的渐近标准(待解释)对候选模型进行评估。

我们将从一个非常简单的模型开始,该模型根据变量'年龄和'性别'回归死亡率。

将死亡率风险表示为年龄和性别的函数,这使得我们可以将等式[1]中的 GLM 改写为:

# Loading custom functions used in the analysis:
source('../input/glmlib/myglm-lib.r') #modify path as required

我们使用 R 的标准' glm '函数拟合模型,并借助' jtools '软件包和一些自定义函数检查拟合优度。我们使用包' jtools '中的' summ '函数来获得模型诊断的良好摘要。应该注意的是,R 的内置' glm '函数默认情况下会删除所有带有' NA' 值的行,所以我们在摘要中被告知,在我们总共 1082 个观察值中,只有 825 个用于训练。我们还看到关于拟合优度统计和标准的报告,如 AIC、BIC 、偏差,它们可用于特定条件下的模型选择。

#---------------------------------------------
# Model 0: gender + age
#---------------------------------------------# Train model:
gfit0 = glm(y~gender+age, data = dat1, binomial)# Summarize and inspect:
summ(gfit0, confint = TRUE, pvals = TRUE, digits = 3)

让我们为这个输出提供一些背景。

回想一下,偏差是普通最小二乘回归中残差平方和的推广,因此它将当前模型与饱和 模型的距离表示为它们各自对数似然 l (。).因此,当前模型的(缩放)偏差由下式获得:

其遵循渐近分布:

自然,偏差总是正的,与拟合优度成反比,用 D = 0 表示完美拟合。零偏差 D0 是仅截距(零)模型的偏差——最差的可能模型:

—通过设置获得:

代入上面的公式。 Ds 是饱和模型的偏差——可能的最佳模型——对于 n 样本量具有 n 个参数的模型,其似然等于 1,偏差等于 0。

偏差可以用来评估任何两个模型是否相等。这可以通过测试较大模型的所有额外系数都等于零的零假设来实现。对于任何给定的嵌套模型对 m1m2 ,可以通过统计进行比较:

换句话说,对于两个相等的嵌套模型,它们相应偏差的差应该遵循一个 χ 分布,其中 k 自由度等于它们相应自由度的差。因此,这意味着我们也可以测试任意模型在统计上是否等于零模型(非常差的拟合)或饱和模型(非常好的拟合)。这些比较的代码如下所示:

# Comparing current model to the null model (LTR):
pval_m0 = 1 - pchisq(gfit0$null.deviance - gfit0$deviance, gfit0$df.null - gfit0$df.residual)
round(pval_m0, 3)

上述比较检验了零假设:

即当前模型是否等于仅拦截模型。自然,我们希望这个空值被拒绝,以使当前模型不被认为是无用的,因此我们期望 p 值低于可接受的决策阈值(通常为 0.05)。

# Comparing current model to the saturated model (Deviance test):
pval_ms = 1 - pchisq(gfit0$deviance, df = gfit0$df.residual)
round(pval_ms, 3)

这里,我们测试空值:

即当前模型在统计上是否等于饱和模型。未能拒绝零是期望的结果,尽管它不能作为零接受的证据。

我们可以通过观察它们的 p 值来评估模型系数的统计显著性。这些天有很多关于 p 值可靠性的讨论,所以我认为应该解释一下这些 p 值是如何获得的,它们真正的含义是什么。回想一下,GLM 的推论是渐近的,拟合系数的真实分布是未知的。MLE 的渐近性质为我们提供了模型系数的高斯分布:

其中 I ( β )表示费希尔信息矩阵。求解 RHS 中的常态 (0,1),我们导出 j =0,…, M 的 Wald 统计量:

我们用它来计算置信区间和应用假设检验(就像我们在线性回归中使用 t 统计一样)。因此,我们可以利用下式计算系数的 100(1α)%置信区间:

自然,我们也可以使用系数的渐近分布,通过检验零假设来进行关于其显著性的正式假设检验:

该方法使用 Wald 统计量,通常被称为 Wald 检验。

麦克法登的伪 R 是对解释变量的度量,定义为:

其中,当前模型的对数似然比 1 减去仅截距模型的对数似然比。该等式改编自线性回归 1SSE/SST中的 R 公式,但是,对伪 R 的解释是模型与最佳拟合的接近度,而不是模型解释的方差百分比。这里的一个经验法则是,0.2-0.4 之间的值通常表示“好”适合(范围从好到非常好)。

在经历了拟合优度的一些方面之后,我们意识到我们的基线模型客观上并没有那么差。我们得到了总体拟合的显著 p 值以及我们的一个系数(使用 Fisherian 95%置信水平),并且我们得到了 0.20 的相当不错的伪R。此外,我们可以使用软件包' jtools '中的函数' plot_summs '来绘制模型系数的渐近分布,并通过注意β2(性别的系数)的尾部如何包含零值且不拒绝空值来可视化 Wald 测试的结果:

# Asymptotic distribution of model coefficients (model 0)
plot_summs(gfit0, scale = TRUE, plot.distributions = TRUE)

基线模型系数的渐近分布

上面的密度图可以帮助我们理解渐近推断是如何工作的。渐近置信区间和 p 值是通过估计该理论密度图上的积分来计算的,并且在适当的条件下,渐近推断的输出应该与任何客观非渐近方法的输出相匹配,例如(frequentist)蒙特卡罗、bootstrap 或平坦先验贝叶斯估计。

使用该模型的预测能力来可视化年龄对男性和女性死亡风险的影响将是有趣的。死亡率风险和年龄之间函数关系的重建如下所示:

# Mortality risk as a function of Age: 
x1 = data.frame(gender="male", age=1:90)
plot(predict(gfit0, x1, type = "response"), 
    type = 'l', xlab = "Age", ylab = "Mortality risk")
x2 = data.frame(gender="female", age=1:90)
lines(predict(gfit0, x2, type = "response"), col="red")

作为年龄函数的预测死亡风险(黑色:男性,红色:女性)

这个模型可以作为基线,所以我们可以从这里开始构建。显而易见的下一步是在模型中加入更多的预测因子,看看拟合度是否会提高。如前所述,该数据集的瓶颈在于我们有太多的缺失值分散在我们的预测器中。如果我们继续添加变量并删除缺少值的行,我们的数据集注定会危险地减少。例如,我们在基线中使用的变量'年龄和'性别',已经花费了我们 260 次观察。变量' days_before_hosp '直观上是一个有趣的预测因子,但如果我们将它添加到基线模型中并应用逐行移除,我们数据集中的观察总数将减少到其原始大小的一半。从变量'也是如此。武汉’,表示被抽样的个体是否是武汉本地人(稍后将详细介绍该预测因子的含义)。因此,如果我们排除数据集中至少有一个缺失值的所有行,我们将得到一个更小但完整的数据集。另一方面,如果相信插补算法来“猜测”我们所有的缺失值,我们最终会发现我们的大部分数据是模拟观察的结果。鉴于目前的事态,如果我们要用更多的解释变量来丰富基线模型,我们面临两种选择:

  • 数据删除:如果我们假设我们的数据是“随机缺失”(MAR)或“完全随机缺失”(MCAR),那么应该考虑删除案例。然而,如果这个假设是错误的,那么我们就有可能在最终的精简数据集里引入偏差。
  • 数据插补:假设数据“非随机缺失”(MNAR),有多种插补方法可供选择。

这两种方法都不一定是错误的,我们也没有办法事先知道哪种方法最适合我们的情况,除非我们清楚地了解为什么数据会丢失。由于我们在这种情况下没有这种先验知识,明智的做法是使用两种方法,并生成简化数据集和估算数据集;然后将安装在前者上的模型与安装在后者上的模型进行比较。这种比较不是直截了当的,因为使用不同的数据集(就行输入而言)会导致模型无法通过基于可能性的标准进行比较,如 AIC/BIC、偏差和麦克法登的伪 R 。更好方法的选择将取决于“随机缺失”假设的验证。如果数据删除的结果与数据插补的结果相似,我们可以有把握地假设缺失的数据实际上是“随机缺失的”,在这种情况下,我们将倾向于插补的数据而不是减少的数据。

模型 1(估算数据集)

如前所述,可以添加到基线模式的两个有趣的变量是:

  • 'days_before_hosp ':从首次出现新冠肺炎症状到患者随后住院(之前创建)之间的天数
  • ‘从。武汉':表示患者是否来自武汉的因素。同样,后一个变量对死亡率有显著影响有点违背直觉,但在这个过程中会有一个合理的解释。

应该注意的是,我们有意避免自动化这个选择过程,以尽量减少模型比较。这种做法的目的是避免陷入逐步选择的领域,逐步选择是一种广泛使用的技术,如今被明智地视为一种统计学罪过

我们的下一个候选模型将具有以下嵌套形式:

此时,我们必须创建估算数据集。根据数据中缺失值的性质、数量和分布,有大量的插补方法可供选择。在温和的“随机缺失”假设下,我们决定我们场景的最佳方法是通过链式方程的多重插补,通常称为 MICE 算法。使用“mice”库,我们可以模拟所有缺失数据的值,如下所示:

# Create imputed dataset using 'mice':
dat1_obj <- mice(dat1[c("gender", "age", "days_before_hosp", "from.Wuhan", "country")], m=5, maxit = 50, method = 'pmm', seed = 400)
dat1_imp = complete(dat1_obj)
dat1_imp$y = dat1$y

现在我们在' dat1_imp '中有了估算数据集。接下来,我们将在新估算的数据集上重新调整基线模型和两个新的候选模型,并总结它们的诊断结果。我们正在重新调整我们的基线模型,因为多重插补还填充了之前由' glm '函数自动删除的关于性别年龄的缺失数据。我们并不期望这会对基线模型的诊断造成巨大的变化,但为了使所有模型相互之间具有可比性,有必要将其重新调整到与新的候选模型完全相同的数据集。

# Train models on imputed data:
gfit1a = glm(y~gender+age, data = dat1_imp, binomial)
gfit1b = glm(y~gender+age+days_before_hosp, data = dat1_imp, binomial)
gfit1c = glm(y~gender+age+days_before_hosp+from.Wuhan, data = dat1_imp, binomial)# Compare fits:
export_summs(gfit1a, gfit1b, gfit1c, scale = F, error_format = "[{conf.low}, {conf.high}]", digits = 3, model.names = c("model 1a", "model 1b", "model 1c"))
plot_summs(gfit1a, gfit1b, gfit1c, scale = TRUE, plot.distributions = F, inner_ci_level = .95, model.names = c("model 1a", "model 1b", "model 1c"))

比较数据插补后模型系数的渐近分布(基线,住院前天数,来自武汉)

因此,现在我们有三个嵌套模型适用于相同的输入情况,我们可以使用基于可能性的标准直接进行比较。正如预期的那样,在所有方面都有明显的改善,因为我们看到 AIC 和 BIC 值都显著下降,而伪 R 在第三个模型中几乎翻了一番。可以有把握地得出结论,带有两个新变量的模型是我们应该关注的。

95%置信区间图可以被认为是系数渐近分布的“全景”视图,它允许我们评估估计的一致性。两个置信区间是否重叠让我们知道两个模型之间相同系数的估计值是否显著不同。这有助于我们检测多重共线性的存在,这可能是由于向模型中添加了新的变量而导致的。

在这一点上,有人可能想知道,使用基于可能性的度量来比较模型的最佳方式是什么。根据经验,Akaike 信息标准(AIC) 已被描述为留一交叉验证的渐近等价物,因此它是用于预测目的的更好指标。另一方面,贝叶斯信息标准(BIC)已经被在之前引用作为在一组候选模型中找到模型的更好标准,这是我们为了解释的目的所需要的。请注意,“真实模型”是具有正确的函数形式和正确的回归变量集的模型,这些回归变量可以最好地解释响应,但它不一定是进行预测的最佳模型(解释响应所需的一些变量可能会影响估计的精度,而不会增加预测值)。考虑到这一点,我们将把 BIC 作为选择车型的标准。

我们继续将相同的候选模型拟合到已经经历了数据移除的数据集。

模型 2(精简数据集)

缩减的数据集将通过列表式删除来创建,即移除具有一个或多个缺失值的输入案例(观测值)。我们使用以下代码创建以下内容:

# Create reduced dataset for model 2:
# (filter out rows with NA values)
not_na_mask = !is.na(dat1$y) & !is.na(dat1$days_before_hosp) & !is.na(dat1$age) & !is.na(dat1$gender) & dat1$days_before_hosp >= 0
dat2 = dat1[not_na_mask, ]
table(dat2$y)

我们可以看到观察总数减少了 40%。现在让我们看看拟合模型的诊断结果会是什么样子。

#------------------------------------------------------------------
# Model 2: gender + age + time to hospitalization (on reduced 
# dataset)
#------------------------------------------------------------------# Train model:
gfit2a = glm(y~gender+age, data = dat2, binomial)
gfit2b = glm(y~gender+age+days_before_hosp, data = dat2, binomial)
gfit2c = glm(y~gender+age+days_before_hosp+from.Wuhan, data = dat2, binomial)# Merge model summaries:
export_summs(gfit2a, gfit2b, gfit2c, scale = F, error_format = "[{conf.low}, {conf.high}]", digits = 3,  model.names = c("model 2a", "model 2b", "model 2c"))# Compare asymptotic distributions of coefficients:
plot_summs(gfit2a, gfit2b, gfit2c, scale = TRUE, plot.distributions = F, inner_ci_level = .95, model.names = c("model 2a", "model 2b", "model 2c"))

比较数据删除后模型系数的渐近分布(基线,住院前天数,来自武汉)

考虑到我们如何将数据集缩减到原来的一半,这个模型的拟合度出奇的好。渐近分布的比较表明,在缩减数据集上拟合的三个模型的诊断与在估算数据集上拟合的诊断一样稳定。这是一个积极的结果,表明缺失数据实际上是如假设的那样随机缺失的,并且插补算法在填充缺失值方面做得很好。在下一节中,我们将进一步加强这一假设,比较两个数据集的最佳候选模型(就 BIC 而言)。

比较估算数据集和缩减数据集

# Merge summaries of two models (imputed vs. reduced)
export_summs(gfit1c, gfit2c, scale = TRUE, error_format = "[{conf.low}, {conf.high}]", digits = 3, model.names = c("model 1c", "model 2c"))# Compare asymptotic distributions of coefficients:
p1 = plot_summs(gfit1c, gfit2c, scale = TRUE, plot.distributions = T, model.names = c("model 1c", "model 2c"))
p2 = plot_summs(gfit1c, gfit2c, scale = TRUE, plot.distributions = F, inner_ci_level = .95, model.names = c("model 1c", "model 2c"))
grid.arrange(p1, p2, ncol=2)

比较模型系数的渐近分布(缩减数据集模型与估算数据集模型)

正如已经提到的,基于可能性的分数在不同数据集上拟合的模型之间是不可直接比较的,尽管如此,根据我们的经验法则,可以有把握地说 0.56 的麦克法登分数本身就表明非常好的拟合。将估算数据集模型系数的渐近分布绘制在简化模型系数的渐近分布旁边是一种比较标准误差并确认两种方法之间没有根本变化的好方法。所有预测因子的估计系数在两种方法中都被发现是显著的,所以我们很高兴地观察到他们的推断保持一致。性别系数的点估计值在拟合简化数据的模型中较高,但其置信区间与拟合估算模型的置信区间重叠,因此两个估计值之间的推断没有显著差异。

到目前为止,我们有足够的证据相信我们的缺失数据实际上是“随机缺失”(MAR),即一个值缺失的概率只取决于观察值,而不取决于未观察值。如果 MAR 假设成立,那么 MICE 在产生正确表示“真实”缺失数据的模拟观察方面做得很好。因此,MICE 估算数据集上的模型与缩减(列表式删除)数据集上的模型一致,因此,可以安全地假设两个数据集都是人口的代表性随机子样本。理论上,我们可以使用两个数据集中的任何一个进行下一步的分析,但是出于信息增益的原因,我们将使用估算数据集。

解决了估算数据集后,我们可以进入模型选择的最后阶段,这需要创建和添加新的预测值。我们注意到,变量‘T2’的加入是从。武汉对模型的拟合通过减少 BIC 而显著提高,伪 R 增加约. 20。在这一点上,我们不清楚为什么一个表明一个人是否出生在武汉的变量会与死亡率有任何关系,更不用说有如此大的影响了。让我们通过测试武汉本地人与非本地人的人均死亡人数来进一步了解这一因素。

武汉本地人死亡率

武汉人和非武汉人的死亡率似乎有显著差异。如果我们相信我们的数据,这种反直觉的洞察力就不应该被忽视,但是这种差异背后的原因是什么呢?一个不幸但现实的假设是,来武汉出差或旅游的人平均收入高于本地人。我们可以通过比较武汉本地人和非本地人的死亡率来挑战这一假设。这可以通过简单的双样本𝑧-比例测试来实现,并可视化为柱状图。

#-------------------------------------------------------------------
#Inspecting proportion of mortality rate in Wuhan natives:
#-------------------------------------------------------------------
# Create dataframe:
tmp = data.frame(rbind(
  table(dat1_imp$y[dat1_imp$from.Wuhan==1]),
  table(dat1_imp$y[dat1_imp$from.Wuhan==0])
))
names(tmp) = c("total", "deaths")
tmp$death_rate = round(tmp$deaths/tmp$total, 3)
tmp$from_wuhan = as.factor(c(1,0))# Compare proportions (deaths per cases between groups):
se1 = sqrt(tmp$death_rate[1]*(1-tmp$death_rate[1])/tmp$total[1])  
# Standard errors of proportions:
se2 = sqrt(tmp$death_rate[2]*(1-tmp$death_rate[2])/tmp$total[2])
tmp$prop_se = round(c(se1, se2), 4)
print(tmp)
print(prop.test(x = tmp$deaths, n = tmp$total, alternative = "greater"))# Barplot of proportions:
ggplot(tmp, aes( y=death_rate, x=from_wuhan)) + 
 geom_bar(position="dodge", stat="identity", width=0.4, 
 color="black", fill="cyan", alpha=.2) + 
 geom_errorbar(aes(ymin=death_rate - prop_se, ymax=death_rate +
 prop_se), width=.1, position=position_dodge(.9))

显然,武汉本地人和非本地人之间的平均死亡率有显著差异。让我们探索不同协变量的相同测试,例如两组之间住院前的平均天数:

# Compare average number of days gone by before hospitalization 
# between both groups:
d1 = dat1_imp$days_before_hosp[dat1_imp$from.Wuhan==1]
d2 = dat1_imp$days_before_hosp[dat1_imp$from.Wuhan==0]
sem1 = t.test(d1)$stderr
sem2 = t.test(d1)$stderr
tmp$avg_days = c(mean(d1), mean(d2))
tmp$mean_se = c(sem1, sem2)
print(tmp)
t.test(d1, d2, alternative = "greater")# Barplot:
b1 = ggplot(tmp, aes( y=avg_days, x=from_wuhan, fill=from_wuhan)) + 
  geom_bar(position="dodge", stat="identity", width = .4, alpha=1) + 
  geom_errorbar(aes(ymin=avg_days - mean_se, ymax=avg_days + mean_se), width=.1, position=position_dodge(.9)) # Boxplot:
df = data.frame(days = c(d1, d2), from_wuhan = as.factor(c(numeric(length(d1))+1, numeric(length(d2)) )) )
b2 = ggplot(df, aes( y=days, x=from_wuhan, fill=from_wuhan)) + 
  geom_boxplot(outlier.colour="black", outlier.shape=16, 
  outlier.size=2, notch=T) +
  stat_summary(fun=mean, geom="point", shape=23, size=4) 
grid.arrange(b1, b2, ncol=2)

武汉市本地人与非本地人平均住院天数的比较

正如所怀疑的,在这个测试中也有一个显著的不同。他们的平均值之间的距离可能很小(3.70 对 2.68),但他们的差异在统计上是显著的,这进一步加强了我们关于因子''与'之间潜在联系的假设。武汉’和个人的社会经济地位。至此我们可以推测,变量‘T4’来自何处。武汉作为另一个潜在变量的混杂因素。换句话说,可能有另一个携带所有社会经济信息的预测器。武汉百变。一个合乎逻辑的假设是,这个潜在的预测因素是“国家”,理由是某人的种族与其社会经济地位以及是否是武汉本地人直接相关。我们已经确定不能将' country '添加到模型中,因为它的方差很低,但是我们可以' from。假设武汉是其代理人。必须有另一个比' from 更细粒度的潜在变量。‘武汉’与当时的‘国家’相关联。**

我们将引入一个国家层面的宏观经济变量,如 GDP (PPP),它表示一个国家的人均国内生产总值(按购买力平价计算)。如果各组(来自武汉,而不是来自武汉)之间的每个国家的平均 GDP 值存在显著差异,那么我们关于患者的财富与死亡率相关联的最初假设将进一步加强。最终,这个变量也会提高我们模型的拟合度。

让我们为 GDP 创建一个数据向量,其中元素的顺序对应于国家名称的顺序(按' levels(dat$country) '列出):

# Adding GDP (PPP):
dat = dat1_imp
dat$gdp = 0

gdp_per_country = c(2182, 16091, 54799, 55171, 51991, 50904, 5004, 
                    52144, 20984, 29207, 14800, 49548, 48640, 55306, 
                    66527, 9027, 17832, 40337, 41582, 46827, 67891, 
                    15599, 34567, 3550, 10094, 30820, 105689, 46452, 
                    43007, 14509, 55989, 67558, 57214, 21361, 70441, 
                    48169, 67426, 8677)
for(i in 1:length(gdp_per_country)){
  country = levels(dat$country)[i]
  country_inds = dat$country == country
  dat$gdp[country_inds] = gdp_per_country[i]
}

名为“ gdp 的新列现在是估算数据的一部分。让我们看看当我们把这个变量引入模型时会发生什么。

模型 3(包含 GDP 的模型)

因此,GDP 模型将具有以下形式:

#----------------------------------------------------------------
# Model 4: Adding GDP (PPP)
#----------------------------------------------------------------# Fit model with GDP:
gfit1d = glm(y~gender+age+days_before_hosp+from.Wuhan+gdp, data = dat, binomial)# Compare models with GDP with and without GDP:# Merge model summaries:
export_summs(gfit1c, gfit1d, scale = F, error_format = "[{conf.low}, {conf.high}]", model.names = c("without GDP", "with GDP"))# Compare asymtotic distributions:
f1 = plot_summs(gfit1c, gfit1d, scale = TRUE, plot.distributions = TRUE, model.names = c("without GDP", "with GDP"))
f2 = plot_summs(gfit1c, gfit2c, scale = TRUE, plot.distributions = F, inner_ci_level = .95, model.names = c("without GDP", "with GDP"))
grid.arrange(f1, f2, ncol=2)# Final model summary:
summ(gfit1d, scale = F, plot.distributions = TRUE, inner_ci_level = .9, digits = 3)
gfit = gfit1d   #rename final model

比较模型系数的渐近分布(有国内生产总值与无国内生产总值)

正如我们推测的那样,将 GDP 加入到我们的模型中进一步提高了拟合度,如 BIC 的显著降低和伪 T2 R 的增加所示。它的系数可能很小(可能是由于‘T4’from 的存在)。武汉【我们模型中的 )但它的影响仍然是显著的。我们可以进一步比较每个群体的平均 GDP(武汉本地人与非本地人),以评估他们的差异是否显著:

# Mean GDP per group (from Wuhan):
d3 = dat$gdp[dat$from.Wuhan==1]
d4 = dat$gdp[dat$from.Wuhan==0]
t.test(d3, d4)
sem3 = t.test(d3)$stderr
sem4 = t.test(d4)$stderr
tmp$avg_gdp = c(mean(d3), mean(d4))
tmp$mean_se_gdp = c(sem3, sem4)# Barplot:
f3 = ggplot(tmp, aes( y=avg_gdp, x=from_wuhan, fill=from_wuhan)) + geom_bar(position="dodge", stat="identity", width = .5) + geom_errorbar(aes(ymin=avg_gdp-mean_se_gdp, ymax=avg_gdp+mean_se_gdp), width=.1, position=position_dodge(.9)) # Boxplot:
df = data.frame(days = c(d1, d2), gdp = c(d3, d4), from_wuhan = as.factor(c(numeric(length(d1))+1, numeric(length(d2)) )) )
f4 = ggplot(df, aes( y=gdp, x=from_wuhan, fill=from_wuhan)) + 
  geom_boxplot(outlier.colour="black", outlier.shape=16, 
  outlier.size=2, notch=FALSE) + 
  stat_summary(fun=mean, geom="point", shape=23, size=4)
grid.arrange(f3, f4, ncol=2)

武汉本地人与非本地人人均 GDP /国家比较

我们的假设检验证实,来自武汉的死亡患者的平均 GDP (PPP)与来自非武汉的死亡患者的平均 GDP (PPP)显著不同。这进一步证实了我们模型的系数 p 值。

到目前为止,我们已经确认估算数据集是“最佳”数据集,并且具有预测值'年龄'、性别'、天 _ 之前 _ 住院日'、的模型来自。武汉’,GDP’,是最接近“真实”模型的一个。作为建模过程的最后一步,我们将使用计算推断来评估系数稳定性和样本的同质性。

自助系数稳定性

到目前为止,我们已经经历了一个保守的模型选择过程,这导致了一个我们认为接近真实模型的模型。验证过程的最后一步是通过自举程序测试模型系数的稳定性。如果我们的系数被发现是“稳定的”,那么我们的样本可以被认为是同质的,我们的估计是可靠的。这里使用两个标准来评估系数的稳定性:

  • 计算/袋装系数估计的统计显著性(即当经验分布包含零时评估)
  • 计算/袋装估计值与渐近估计值的接近程度(即当渐近和计算平均值之间的差异分布包含零时进行评估)

请注意,在这种情况下,引导聚合(打包)和重采样的目的与随机森林的目的并不完全相同。我们将使用带替换的随机子采样来训练多个模型,但我们的目标是为模型系数而不是模型预测创建袋装估计和自举置信区间。Bagging 将用于产生模型参数(袋装系数)的点估计,而经验自助分布和那些袋装估计的相关自助置信区间将允许我们评估估计的模型稳定性。在其最简单的形式中,这种评估包括检验袋装估计等于渐近估计的假设,这归结为比较同一参数的渐近分布和 bootstrap 分布。实现这一点的一种方法是测试袋装估计和渐近估计之差等于零的零值。如果我们可以证明渐近估计可能与计算(bootstrap)估计没有不同,我们可以有把握地假设我们已经收敛到“真实”系数。

我们使用自定义函数' bagged_glm '来获得模型参数的 bootstrap 分布。' alpha 参数用于模型汇总统计,' class_balancing 标志以 50%对欠采样类进行重新采样(未使用),' bag_pct '参数确定训练包的大小,标志' ocv '激活出坏交叉验证(相当于随机森林的出包错误)。

*#-------------------------------------------------*
*# Coefficient stability (final model):*
*#-------------------------------------------------*
bagged_results = bagged_glm(dat = dat, gfit = gfit, alpha = 0.05, class_balancing = F, bag_pct = 0.80, ocv = T)
coef_bags_df = bagged_results$coef_bags_df

让我们来看看计算推理的结果:

*# Bootstrap means of transformed coefficients:*
cat(
    "\n Bootstrap means of back-transformed coefficients:\n",
    "Intercept: ", mean(coef_bags_df$`(Intercept)`), 
    "Gender: ", mean(coef_bags_df$gendermale),    
    "Age: ", mean(coef_bags_df$age),     
    "Days before hospitalization: ", 
     mean(coef_bags_df$days_before_hosp),
    "From Wuhan: ", mean(coef_bags_df$from.Wuhan1),
    "\n"
)
*# Compare with asymptotic values: (merge with table above)*
coefficients(gfit)
confint(gfit)

*# Test null distribution directly: H0: m2-m1 = 0* 
par(mfcol=c(3,2))
for(i in 1:length(bagged_results$coef_bags_df)){
  d = bagged_results$coef_bags_df[[i]] - coefficients(gfit)[i]
  qtt = quantile(d, c(.975, .025))  
  att = t.test(d)
  hist(d, col="lightblue", main = names(coefficients(gfit)[i]), 
  breaks = 30)
  abline(v = 0, col="blue", lwd=3, lty=2)
  abline(v = qtt[1], col="red", lwd=3, lty=2)
  abline(v = qtt[2], col="red", lwd=3, lty=2)
}
dev.off()

检验模型系数的渐近分布和计算分布之间的差异

正如我们在上面的直方图中看到的,袋装估计值 μ 2 - μ 1 之间的差值 d 的分布在自举置信区间(红线)内包含零(蓝线)。因此,零假设:

在 95%的显著性水平上未被拒绝。尽管未能拒绝零假设并不意味着接受零假设,但这一发现与我们之前的所有发现相结合,应该足以假设我们的估计是可靠的,并允许我们继续进行模型解释。

模型解释

作为分析中期待已久的步骤,我们终于可以继续解释我们模型的系数了。回想一下,任何二项式 logit 模型(等式[1])都可以表示为:

求解 β0 产量:

求解 βj 产量:

最后:

表达式[2]中的 β0 的值可以被解释为当所有连续回归变量为 0 且分类回归变量处于其基础水平时,响应的概率为 1。在我们的场景中,我们可以说“住院前天数、患者年龄和 GDP 为零时的平均死亡概率,并且患者不是武汉本地人”。显然,患者年龄和 GDP 等于 0 是一种无意义的解释,但是,如前所述,我们对该分析中的 β0 的直接解释不感兴趣(否则我们可以使用截距校正技术)。我们对表达式[3]中的 βj 的解释很感兴趣,如果 x 连续,它可以被一般地解释为回归变量 j 每增加一个单位,或者如果 x 是绝对的,它可以被解释为从基础水平到当前水平的转变;其他条件不变。为了使这种解释有意义,我们必须使它适应我们的场景,并简单地把 βj 看作:

死亡率概率的百分比变化

对于与回归量 j 相对应的任何变化,其他都是相同的。我们可以将这种解释应用于最终模型的系数:

因此,让我们总结一下原始形式和转换形式的模型系数:

glm.transform(gfit, alpha = .10, logodds_str = "exp(beta)%", ci = T, stdout = F)

glm.plot(gfit, alpha = 0.10, logodds_str = "exp(beta)%", stdout = F)

对死亡风险的可变影响的大小

我们可以将转换后的系数作为一个因子,并将 Y 的几率解释为比 X 的几率高几倍,或者我们可以使用百分比变化形式,并说 Y 的概率按照所解释的 X、的每一次变化而变化一定的比例。就个人而言,我认为后一种解释更直观,下面我们可以看到它在 90%置信水平下对我们模型的每个系数的应用:

  • []—exp(β1):
    年龄 每增加一岁,新冠肺炎患者的死亡风险大约增加 11%。(CI: 8.36%-14.97%)
    表达这一观点的一种更直观的方式是将转换后的系数乘以因子 10,并得出年龄增加 10 岁的几率增加 114%。因此,我们可以说:
    年龄每增加 10 岁,新冠肺炎的死亡风险几乎翻倍。
  • []—exp(β2):
    新冠肺炎死亡风险男性比女性高 120%。(置信区间:8.67%-367%)**
  • [days before hosp]—exp(β3):
    新冠肺炎患者在他/她出现第一个症状 的第一天之后,患者不住院的每一天,死亡风险都会增加 12%。(置信区间:2.7%-20.9%)**
  • [ 来自武汉]—exp(β4):
    武汉本地人的新冠肺炎死亡风险大约高 6 倍 。(置信区间:231%-1577%)
  • [GDP]—exp(β5):
    新冠肺炎的死亡风险间接受到患者原籍国 GDP (PPP)的影响。虽然这种影响太弱,无法量化,但结合武汉的'',它 很可能暗示了患者的生存机会与其个人财富 之间的依赖关系。**

利用模型训练和预测之间的差异。

原文:https://towardsdatascience.com/exploiting-the-differences-between-model-training-and-prediction-40f087e52923?source=collection_archive---------39-----------------------

减少内存占用,提高部署模型的速度和可移植性。

这篇文章的代码可以在这里找到

最近几个月,我们已经帮助许多公司在各种环境中部署他们的 AI / ML 模型。我们为医疗保健领域的模型部署做出了贡献,在过去的几个月里,我们已经帮助几家公司将经过训练的模型迁移到不同类型的物联网设备上。特别是在后一种情况下,要求通常很严格:计算周期的数量和可用内存通常都是有限的。

在这篇文章中,我澄清了我们如何确保使用标准 ML 库训练的模型,如 PyTorchScikit-learnTensorflow 可以在各种边缘设备上有效地部署。为了使事情变得具体,我们将检查一个简单的逻辑回归模型的训练和部署。然而,我们在这里讨论的大部分直接转移到更复杂的模型。

模特培训

为了说明模型训练和部署之间的区别,让我们从模拟一些数据开始。下面的代码根据下面的简单模型生成 1000 个观察值:

(一个简单的逻辑回归模型用作本例的 DGP)

*import numpy as np
np.random.seed(66)  # Set seed for replication# Simulate Data Generating Process
n = 1000  # 1000 observations
x1 = np.random.uniform(-2,2,n)  # x_1 & x_2 between -2 and 2
x2 = np.random.uniform(-2,2,n)
p = 1 / (1 + np.exp( -1*(.75 + 1.5*x1 - .5*x2) ))  # Implement DGPy = np.random.binomial(1, p, n)  # Draw outcomes# Create dataset and print first few lines:
data = np.column_stack((x1,x2,y))
print(data[:10])*

生成数据后,我们可以专注于拟合模型。我们简单地使用sklearnLogisticRegression()函数来实现:

*from sklearn.linear_model import LogisticRegression
mod = LogisticRegression().fit(data[:,[0,1]], np.ravel(data[:,[2]]))*

近距离观察

在这一点上,有必要暂停一下,简要地考虑一下引擎盖下正在发生的事情。逻辑回归模型,就像许多其他有趣的 ML 模型一样,被反复训练。为了训练这个模型,sklearn(或者任何其他提供类似功能的包)必须实现几个功能:**

  1. 某种分数函数表示模型的适合度。这可能是一个误差函数,或最大似然函数。
  2. 从一次迭代到下一次迭代更新拟合模型参数的函数。

训练过程将有效地重复使用这两个函数:最初,模型的参数是随机实例化的。接下来,检查模型的分数。如果分数被认为是不够的(通常是因为它与前一次迭代相比有所改进),则更新模型参数并重复该过程。

即使对于这个简单的模型,sklearn也需要多次遍历数据集。下面的代码给出了迭代次数(对于这个种子选择是 7 次):

**# Print the number of iterations
print(f'The number of iterations is: {mod.n_iter_}.')**

因此,为了训练一个模型,我们需要访问数据,几个效用函数,并且我们需要多次迭代/遍历数据集。一般来说,这种训练过程在计算上要求很高,这解释了为什么对于复杂的模型,我们求助于并行计算和 GPU 或 NPU 加速来在合理的时间内完成它。然而,幸运的是,当训练模型时,我们使用的各种 ML 库抽象掉了这样做所需的相当复杂的逻辑。

生成预测

将这与从已经拟合的模型中生成预测进行比较(通常称为推论,但我发现后一个术语令人困惑,因为它在统计学中的用法不同,所以我坚持使用预测)。在拟合模型时,在这种情况下,实际上我们需要生成预测的只是逻辑回归函数(与我们在上面的示例中用于生成数据的数学函数相同)和拟合模型的三个参数。这些很容易检索:

**b = np.concatenate((mod.intercept_, mod.coef_.flatten()))
print(b)**

并且参数最终与我们用于数据生成的值相对接近:[0.84576563 1.39541631 -0.47393112]

此外,在大多数部署情况下,我们通常只使用一个输入来评估模型:在本例中,是一个长度为 2 的数字向量。所以真的,如果我们想要部署一个模型,我们不需要拟合函数,我们不需要数据,我们不需要迭代。为了生成预测,我们只需要简单有效地实现相关的数学函数。

利用训练和预测之间的差异进行(边缘)部署

“那又怎么样?”你可能会问。当现代模型训练工具抽象掉所有这些细节时,为什么还要关心训练和预测中涉及的本质细节呢?嗯,因为当你希望你的模型被有效地部署的时候,例如当你需要它们在小型设备上快速运行的时候,你最好利用上面描述的差异。

为了便于讨论,请对比以下两种模型部署方法(即,将训练好的模型投入生产,以便您可以使用其预测):

  1. ****sklearn 作为 REST 服务的 docker 容器部署:这种方法简单且常用:我们启动一个 Docker 映像,其中包含 python 堆栈和用于训练的工具:在上面的示例逻辑回归模型中,sklearn。接下来,我们创建一个 REST 端点,它使用拟合模型的mod.predict()函数来生成结果。
  2. ****可伸缩 WebAssembly 部署:最近,但一点也不困难的是将 fitted 模型转换为 WebAssembly(使用类似于可伸缩提供的服务),并部署。WASM 二进制只包含在最小 WebAssembly 运行时预测所需的逻辑。(自动生成的)二进制文件将只包含必要的逻辑函数(在这种情况下)和估计的参数。二进制文件可能部署在服务器上(因此类似地通过 REST 调用使用),但是,使用各种可用的运行时,它也可以运行在几乎任何边缘设备上。

显然,第一个部署过程接近数据科学家的“我们所知道的”。直接使用我们熟悉的工具工作是很好的。在许多方面,它是可行的:我们可以通过调用 REST 端点来生成预测。第二种解决方案与我们的标准实践相去甚远,并且对于模型训练毫无用处(也就是说,没有“用于训练模型的 WebAssembly 包……”,如果那句话有任何意义的话)。但是,我们仍然认为应该优先选择它:第二种设置利用了训练和预测之间的差异,在几个方面使模型部署更好:

  • 内存占用:**上面两个选项中的第一个将需要至少 75Mb 的容器(让容器变得那么小需要大量的工程设计,更常见的是容器的大小接近 1Gb)。在这种情况下,存储的模型本身很小(~2Kb),因此容器包含了部署的最大内存块(注意,对于大型神经网络来说,这可能不是真的)。相反,WebAssembly 运行时可以减少到小于 64 Kb 。无可否认,WebAssembly 二进制文件本身比存储的sklearn模型大(~50kb),但是它现在包含了生成预测所需的全部内容。因此,第一个部署选项至少需要 75Mb,而第二个部署选项需要不到. 1Mb。**
  • ****速度:与高效的 WebAssembly 部署相比,消耗运行在 Docker 容器中的 REST 端点(它启动了训练所需的所有东西)在执行时间方面并不占优势。这里是各种模型的一些速度比较,但是,不用说,利用训练和预测之间的差异,仅仅将预测的基本需求投入生产,会将这些预测生成的速度提高一个数量级。

因此,内存占用更少,执行速度更快。这很好,有几个原因:一个原因是,我们可能希望为巴黎协定做出贡献,从而有效地部署模型,而不会在每次生成预测时浪费能量。但是,小的占用空间和快速的执行也很有吸引力,因为这正是我们在将模型投入生产时所需要的:祝您在 ESP32 MCU 板上部署 Docker 容器好运。有了 WebAssembly,这简直是小菜一碟。

放弃

值得注意的是我自己的参与:我是 Jheronimus 数据科学院 的数据科学教授,也是Scailable的联合创始人之一。因此,毫无疑问,我对 Scailable 有既得利益;我有兴趣让它成长,这样我们就可以最终将人工智能投入生产并兑现它的承诺。这里表达的观点是我自己的。**

开放式电子健康档案的探索

原文:https://towardsdatascience.com/exploration-into-open-electronic-health-records-d581c5dfb265?source=collection_archive---------24-----------------------

路易斯·梅伦德斯在 Unsplash 上的照片

放弃

这篇文章的内容是面向学术读者的,我不作任何保证,也不承担任何责任。作为读者,您在探索或以其他方式使用此处包含的材料时,风险完全由您自己承担。您有责任遵守您所在地区、州、省、国家/地区和/或司法管辖区的所有适用隐私法,尤其是关于本文中提及或提及的任何及所有材料。你被警告了。

介绍

在这篇文章中,我将讨论一个公开的电子健康记录(EHR)数据集。作为一个乐观主义者,我希望有一天去身份化的 EHR 能被广泛使用。掌握这些信息的研究人员应该能够更好地测试他们的医疗保健应用。潜在的应用包括训练非线性编程模型,以帮助预测给定某些治疗和程序的患者结果。其他包括改善医疗机构内部和之间的互操作性和信息交换。第三个应用是各种后端的基准测试。最后但同样重要的是,大数据的使用应该有助于为运营和管理改进提供信息,包括更好的护理和服务交付模式。结果是:越来越多的人得到了最佳的医疗保健!

直截了当地说,在写这篇文章的时候,公开的电子病历还很少。我是说,真的很少。许多网站初看起来很有帮助,提供了大量的报告和各种其他研究工具。不幸的是,用于生成这些产品的大部分基础数据是高度模糊的。堪比那么多冰山的尖端!除非您是合作机构或政府机构的成员,否则获取源数据几乎是不可能的。

尽管最初遇到了一些挫折,但我并没有放弃寻找电子病历公共数据集的旅程。我的收集标准包括以下内容:

  • 隐私。必须对数据进行适当的去识别,以保护患者及其家属的隐私。除了什么都不做这一显而易见的选择之外,模拟数据是帮助缓解隐私担忧的一个不错的选择。参见此链接了解模拟 EHR。不幸的是,根据研究人员的预期应用,模拟数据可能无法提供足够的真实世界保真度、相关性、噪声或其他标准的分数。第三种选择包括去除身份的患者记录,医疗机构或团体同意在他们通过隐私“嗅探测试”后发布记录。在这些情况下,对于研究人员来说,有这样的决定伴随他们的工作是谨慎的。这可以包括经批准的讨论记录、参考号或其他形式的可审计证据,从隐私角度证明数据的去标识性和可发布性。见以上免责声明。你最好小心行事,因为在你的国家,侵犯病人健康记录的隐私会受到严厉的惩罚。
  • 容易接近,如没有任何附加条件的接近。任何提供“合作伙伴计划”和“免费试用”的网站都被排除在我的搜索之外。我的排除列表中的其他网站包括要求人们提供信息请求的网站,由底层组织决定请求是否有效。这些网站通常会在提供数据之前宣传几周到几个月的延迟,如果有的话。
  • 这项研究工作的第三个标准涉及互操作性和通过一种或多种已知且受支持的格式集成到数据科学应用程序的容易程度。用更通俗的话来说:如果我能快速、轻松地将其融入 Python 生态系统,那就万事大吉了。这部分是我喜欢称之为“mungieness”的部分。
  • 最后,我对包含代表性样本的数据集感兴趣。如果可能的话,它应该基于跨越数年的大量人口。它还应该具有与企业级应用程序生成的文件相当的大小,例如,中型到大型医疗记录系统(MRS)的大小。

我重申一下:这篇文章的目的是什么?以获得包含去识别的 EHR 的代表性数据集。有了这样一个数据集,我打算通过制定和测试几个以医疗保健研究主题为基础的假设来解决问题。我觉得有趣的一些主题包括:

  • 一个机构能在多大程度上为患者提供及时有效的服务?
  • 管理人员和其他员工是否可以使用任何指标来梳理效率,从而帮助优化护理的提供?

剧透:我不幸发现的数据集并没有帮助我回答这些问题。根据一篇关于 ORBDA:一个用于电子健康记录服务器性能评估的 openEHR 基准数据集的论文的作者,他们研究中的数据旨在用于 OpenEHR 系统的基准测试:“我们不鼓励在基准评估以外的其他情况下使用 ORBDA。”[1]尽管如此,庞大的信息量(以 GB 为单位)意味着我至少可以使用我认为非常现实的医疗保健信息技术副产品来测试我的一些分析工具集。请查看此链接以访问数据集。

程序

这些数据可以在几个文件中下载,并有附带的 SQL 脚本来帮助研究人员将其加载到 PostgreSQL 数据库中。我的测试是在运行 Ubuntu 20.04 的笔记本电脑上进行的。

首先,您需要安装 PostgreSQL:

sudo apt-get install postgresql

我建议打开两个终端窗口,一个运行 shell 命令,另一个在登录到数据库后从 postgresql 提示符运行命令。

从这个网站下载 ORBDA 数据文件和脚本,并将它们放在一个合适的文件夹中。

接下来,解压缩脚本和数据文件。

为了让 PostgreSQL 在您的本地机器上启动并运行,可以在这里找到一个有用的教程。使用默认用户名登录。这一步需要您的 Ubuntu 帐户的管理员密码:

sudo su postgres

在第二个终端窗口中执行上述步骤。登录后,运行:

psql

提示符应该变成类似如下的内容:

postgres=#

使用这个新提示,您现在可以输入 PostgreSQL 命令来与您的数据库进行交互。

在创建数据库之前,打开名为“orbdaCreateDB.sql”的脚本,并根据您所在的地区更改排序规则,否则会出现错误。对于我所在的地区,我必须将其从:

 LC_COLLATE = ‘en_US.UTF-8’
    LC_CTYPE = ‘en_US.UTF-8’

收件人:

 LC_COLLATE = ‘en_CA.UTF-8’
    LC_CTYPE = ‘en_CA.UTF-8’

更改后,保存脚本。切换回第一个终端窗口,运行以下脚本。

psql -U postgres -f orbdaCreateDB.sql

如果它成功完成,您现在应该有一个名为“orbda”的数据库。在 PostgreSQL 提示符的第二个终端窗口中,键入:

\c orbda

您应该会收到一条消息,说明:

You are now connected to database “orbda” as user “postgres”.
orbda=#

您在该提示符下运行的后续命令应该在 orbda 数据库上执行。

切换回第一个终端并运行表创建脚本:

psql -U postgres -d orbda -f orbdaCreateTables.sql

如果一切顺利,您可以切换到第二个终端来查看新创建的表的列表。在 postgresql 提示符下键入' \dt '以查看表列表:

orbda=# \dt
 List of relations
 Schema | Name | Type | Owner 
 — — — — + — — — — — — — — -+ — — — -+ — — — — — 
 public | bariatrics | table | postgres
 public | chemotherapy | table | postgres
 public | hospitalisation | table | postgres
 public | medication | table | postgres
 public | miscellaneous | table | postgres
 public | nephrology | table | postgres
 public | radiotherapy | table | postgres
(7 rows)

如果你已经达到了这篇文章的这一点,你就可以开始玩一些有趣的游戏了,叫做等待游戏。在此之前,请打开“importingFiles.sql”脚本,将目录更改为您在上述步骤中放置文件的位置。打开“importingFiles.sql ”,并根据需要将路径修改为文件的绝对路径。如果不这样做,在尝试导入数据时会出现错误。

更新并保存脚本后,导航到第一个终端并运行:

psql -U postgres -d orbda -f importingFiles.sql

导入脚本运行后,您应该会看到连续写入数据库的条目数量。将鼠标移到第二个终端上,在导入数据时查看表的详细信息。它们的大小应该增加。在短暂停顿之间,在 PostgreSQL 提示符下执行以下命令:

\dt+

一旦导入开始,您可能需要等待几个小时才能完成。您的里程可能会有所不同。

当我发现其中一个文件夹位置命名错误时,一个令人高兴的小后果发生了,导致我在纠正错误后不得不重新运行“导入文件”脚本。我注意到,不仅我试图追加的表变大了,许多其他表也变大了。显然,这些表的一些记录在上次导入时丢失了。第三次运行该脚本导致数据库中没有新条目。

如果一切顺利,您应该有一个 PostgreSQL 数据库,其中有几十亿字节的去标识 EHR。在 postgresql 提示符下使用以下命令检查其中一个表:

SELECT * FROM nephrology LIMIT 1;

结论

这是一个快速入门教程,可以通过链接获得一组公开的电子健康记录。我将重申我上面的友好提醒,这些 EHR 中包含的数据不应用于研究或其他目的,因为研究人员的预期范围是关于 OpenEHR 系统的基准测试。关于将信息从 PostgreSQL 的关系数据库格式转换成 OpenEHR 的更多细节包含在他们的网站上。如果您希望继续转换,建议您遵循附加步骤。

本文到此结束,祝您在医疗保健信息技术领域的数据科学应用一切顺利!

参考

[1] ORBDA: An open EHR 电子健康记录服务器性能评估基准数据集,Douglas Teodoro,Erik Sundvall,Mario joo Junior,Patrick Ruch,Sergio Miranda Freire,2018。

意大利新冠肺炎数据的探索性数据分析

原文:https://towardsdatascience.com/explorative-data-analysis-of-covid-19-data-in-italy-d5665cb62c5a?source=collection_archive---------46-----------------------

UnsplashCaleb Stokes 拍摄的照片

不幸的是,意大利是受疫情病影响最严重的国家之一,所以我试图在现有的数据中找到一些见解。

我使用的数据来自每天更新的官方 git 库“Protezione Civile”,可在这里获得。

用于执行该分析的代码可以在这里找到。

如果你想直接运行笔记本只需点击这个链接:https://my binder . org/v2/GL/acalax % 2f covid-19-EDA-Italy/master?filepath=eda_italy.ipynb

笔记本会下载数据,所以你可以随时更新图表!

免责声明:我没有试图预测或模拟任何事情,只是看着数据,并相应地建立一些图表。

分析覆盖了整个国家,但也可以很容易地扩展到地区和省份,只需修改代码。

我们开始吧。

数据

关于数据的所有信息都可以在存储库的 README.md 中找到,但是可以帮助澄清一些特性是相关的

有些是其他值的和,几乎所有的值都是日复一日累积的。

在代码中,我使用了意大利语的特性名称,而没有翻译它们

我们来详细看看:

被发现呈阳性的人可能处于以下情况之一:

  • ricoverati _ con _ sintomi(有症状住院患者)
  • terapia_intensiva (重症监护)

他们的总数是住院病人总数

  • isolamento_domiciliare (居家隔离)

总和为 totale_positivi(当前阳性病例总数)

结果可能是:

  • 地美西 _ 瓜里提(已恢复)
  • deceduti (死亡)

所有阳性和结果的总和是 total_casi(阳性病例总数)

测试的计数器

  • 棉塞(已进行测试)

昨天增加了一个新的字段“nuovi_positivi ”,每天都有新的案例,但我直接通过移动和减去数据得出所有的差异

让我们从显示所有案例总体情况的两个图表开始。

这些图像与 4 月 1 日更新的数据有关。

总阳性病例的总累积计数

并且结果尊重正面的案例

阳性、死亡和恢复的总累积计数

这就是疫情的样子,阳性病例和相关结果大幅增加,幸运的是死亡人数低于痊愈人数。

现在,让我们来看一下阳性病例的累计计数

阳性条件的累积计数明细

3 月 20 日以后,家庭分娩超过住院分娩,而重症监护增加。

让我们看看测试和阳性是如何联系的

测试和阳性的总累积计数

测试和阳性反应越来越多,希望阳性反应会“稳定下来”

现在让我们推导出几个新的特征,死亡与总病例数之间的比率(死亡/总阳性)以及恢复与总阳性之间的比率(恢复/总阳性)

死亡/总阳性比率和康复/总阳性比率

这是最“奇怪”的趋势之一,因为死亡和康复非常接近,而在其他国家,差距更大。

现在,我们来分析每天的增量,即一天与前一天的差值。这让我们更好地了解发生了什么和当前的趋势。

每天新增阳性病例

但愿下降趋势正在发生

案件总数的细目

增量阳性分解

家庭分娩在 3 月 20 日达到高峰,住院分娩在同一天达到最低点

这是 delta outcomes 的细分

Delta 结果分解

回收的价值很高,趋势似乎不错。

让我们看看测试和住院的趋势

重症监护是很小的一部分,让我们来看更多细节

趋势似乎不错,希望是因为没那么必要了。

最后,对所有相关增量进行统计分析。

三角洲的箱线图分析

这是对所发生事情的最好描述。

添加测试缩小了其他数据,但给出了所执行的努力的概念,在一天内执行了超过 35K 个测试,作为最大数量(26/03)

三角洲的箱线图分析

结论

这些只是可能的报道的简单例子,希望可以帮助更容易理解正在发生的事情的规模。

就我个人而言,我没有一个清晰的见解,即使是因为数据的收集方式可能会影响结果,也因为在这个数据集中,目前有一些缺失的功能可能会有所帮助,如关于阳性、康复和死亡的年龄间隔的统计数据。

但是我也在想,使用可用的东西总比什么都没有好,尤其是当我们只是使用“原样”而不猜测任何东西的时候。

最后一个想法:数字可能是不可思议的无菌,但我们不能忘记它们涉及到生命,医院里遭受痛苦的人,哀悼亲人的家庭,处于严格封锁中等待知道自己是否生病的人。

然后还有其他人,包括我自己,没有被任何数据捕获,但仍然经历着这一切。我们真的是同舟共济,齐心协力才能克服这种局面。

注意安全!

编者按: 走向数据科学 是一份以数据科学和机器学习研究为主的中型刊物。我们不是健康专家或流行病学家,本文的观点不应被解释为专业建议。想了解更多关于疫情冠状病毒的信息,可以点击 这里

成分数据的探索性分析:农业土壤的案例研究

原文:https://towardsdatascience.com/exploratory-analysis-of-compositional-data-case-study-from-agricultural-soils-a1fc5f076ddc?source=collection_archive---------25-----------------------

组成数据是向量,其中所有的组成部分都是正数,并且受到闭包形式的约束,这意味着它们的总和是一个常数。在本文中,我使用土壤矿物部分作为一个案例研究的探索性分析这样的数据。

图片由 Zbysiu Rodak 提供

介绍

背景

农业行业的数据驱动决策在满足不断增长的食品、饲料和原材料需求方面发挥着关键作用,同时确保自然资源和环境的可持续利用。例如,精准农业涵盖了广泛的监测技术,包括数据科学、地质统计学和预测分析等跨学科领域,为农民、农学家、土壤科学家和所有其他利益相关者提供了大量信息,这些信息是我们从未见过的,可用于准确监测土壤和作物状况并分析处理方案。

图片由若昂·马塞洛·马克斯拍摄

在这方面,在目前市场上可用的许多监测技术中,使用台式或手持设备的激光诱导击穿光谱(LIBS)越来越受欢迎,主要是因为其速度快、样品制备最少且成本相对较低。像在农业和食品工业中广泛使用的近红外光谱一样,LIBS 提供了对土壤化学成分的估计,可用于评估其肥力——即土壤中可用的矿物养分,这是为大田作物推荐肥料的基础。

然而,无论是在实验室还是在野外,测量准确度和精度对被分析的土壤类型非常敏感:细质地、中等质地和粗质地土壤。为了克服这些缺点,可以将土壤筛分成细粒或中等粒级,这样可以消除大颗粒对分析的任何影响。另一个选择是把大颗粒打碎。第三种选择是根据土壤质地调整设备参数并进行单独分析。后者是美国宇航局的 LIBS 仪器所采用的,名为 ChemCam ,它有一个机载相机来通知纹理,并配备了一个直径为 300-550 微米的聚焦激光束,可以对大颗粒和非常细颗粒大小的火星岩石和土壤进行元素分析。

美国宇航局火星科学实验室任务的好奇号火星车,使用位于 LIBS 的 ChemCam 仪器分析火星岩石表面的化学成分。图片来源:美国宇航局/JPL 加州理工学院。

在地球上,仍然悬而未决的问题是这些选择将如何影响分析——例如,植物养分的可用性受到包括土壤颗粒大小在内的许多因素的影响——或者它们将如何影响 LIBS 吸引人的特征,如快速响应和低入侵性。在很大程度上,本文将搁置这些问题的细节。相反,主要主题是提供探索性分析的概述,显示土壤颗粒大小如何影响 LIBS 测量或任何基于激光的光谱测量,以便确定未来的挑战,并帮助确定最有效的解决方案。

土壤粗密度

对于任何从事农业的人来说,无论规模大小,了解土壤的质地都是非常重要的,因为它会极大地影响土壤养分和持水能力。土壤质地通常由沉降法确定——使用移液管或比重计——提供土壤矿物颗粒的相对比例,称为土壤颗粒大小分离:沙子(2.0-0.05 毫米)、淤泥(0.05-0.002 毫米)和粘土(<0.002 mm). Sandy soils are often referred to as coarse-textured soils whilst clayey soils are referred to as fine-textured soils. The term “coarse” is used because sandy soils are mostly made up of larger or coarse particles. Clayey soils are mainly composed of fine flaky-shape particles. The term “medium” is used for silty and loamy soils. The latter represents soil that does not have a dominant particle size fraction.

Image by 保罗·莫康

成分数据

众所周知,土壤颗粒大小部分构成了所谓的成分数据,这对其统计分析有很大的影响。事实上,自 20 世纪 80 年代初,在约翰·艾奇逊的工作—**—成分数据的统计分析之后,成分数据被地质学家和统计学家以及其他人熟知为向量,其中分量是作为整体的一部分的严格正数,这意味着每个分量仅携带相对部分的信息。这些数据通常被称为封闭数据,因为它们的总和是一个常数,例如 1%或 100%。从数学上讲,这意味着一个 D 部分的成分数据占据了一个有限的空间,在几何上用( D -1)单纯形表示,例如,成分只能从 0%变化到 100%,就像土壤矿物部分的情况一样。

因此, D 部分组成数据集𝐗表示为:

其中组件xjj∈{ 1,2,…, D }受到以下约束:

例如,对于 D = 2 或 1-单形,数据可以表示为一条线段,对于 D = 3 或 2-单形,数据可以表示为一个三角形(所谓的三元图,用于将土壤结构等级表示为沙子、淤泥和粘土百分比的函数)。

探索性分析

在接下来的几节中,我将采取几个步骤来更好地了解数据集。这些步骤包括:(1)收集有关分析样本性质的信息,(2)计算土壤颗粒大小部分的描述性统计,(3)分析土壤质地对 LIBS 信号的影响,以及(4)可视化 PCA 双标图以发现模式。

其余软件包:

*library(data.table)
library(tidyverse)
library(soiltexture)
library(ggtern)
library(compositions)
library(robCompositions)
library(FactoMineR)
library(factoextra)*

土壤样本

我们的数据集包含 LIBS 分析的 172 个土壤样本,其中包含土壤质地信息,即沙子、淤泥和粘土的百分比。

*> soil.data <- fread(file="data.csv") %>% 
   unique() %>% 
   as_tibble() %>%
   str()

Classes ‘tbl_df’,‘tbl’ and 'data.frame': 172 obs. of 7156 variables:
 $ SampleID   : Factor w/172 levels "S18-0001",..: 1 2 3 4 5 ...
 $ Clay       : num  41 35.1 81 69.1 39.1 ...
 $ Silt       : num  30 41.9 12 23.9 41.9 ...
 $ Sand       : num  29 23 7 7 19 19 26.9 ...
 $ 199.3771616: num  659 659 664 663 661 ...
 $ 199.4644141: num  664 668 660 657 662 ...
 $ 199.5516666: num  674 669 660 661 659 ...
 $ 199.6389192: num  663 671 658 667 661 ...
 [list output truncated]*

我们的第一步是收集有关分析样本的信息,以了解数据集的性质和分布。下图显示了一个三元图(或土壤结构三角形),它告诉我们分配给每个样品的结构类别以及它们在不同类别中的分布情况。

三元图用于报告土壤颗粒大小分离的相对比例。

然后,我们可以根据土壤样品的名称来确定它们的不同结构类别。根据美国农业部的分类系统,共有 12 种土壤质地类别,土壤质地类别符号见下表。

*> TT.classes.tbl(class.sys="USDA-NCSS.TT") abbr   name              points                          
 [1,] "C"    "clay"            "24, 1, 5, 6, 2"                
 [2,] "SIC"  "silty clay"      "2, 6, 7"                       
 [3,] "SC"   "sandy clay"      "1, 3, 4, 5"                    
 [4,] "CL"   "clay loam"       "5, 4, 10, 11, 12, 6"           
 [5,] "SICL" "silty clay loam" "6, 12, 13, 7"                  
 [6,] "SCL"  "sandy clay loam" "3, 8, 9, 10, 4"                
 [7,] "L"    "loam"            "10, 9, 16, 17, 11"             
 [8,] "SIL"  "silty loam"      "11, 17, 22, 23, 18, 19, 13, 12"
 [9,] "SL"   "sandy loam"      "8, 14, 21, 22, 17, 16, 9"      
[10,] "SI"   "silt"            "18, 23, 26, 19"                
[11,] "LS"   "loamy sand"      "14, 15, 20, 21"                
[12,] "S"    "sand"            "15, 25, 20"*

我们还可以检查分配给土壤样品的不同结构类别的数量。结果是我们有 11 种土壤质地——我们没有砂质粘土。如下图所示,最丰富的是粘壤土、壤土和砂壤土,其次是粉质粘壤土、粉质壤土和粘土。

*soil.texture %>%
 ggplot() +
 geom_bar(aes(x=class, fill=class, weight=num.sample)) +
 geom_text(aes(x=class, y=num.sample, label=num.sample), nudge_x=-0.1, nudge_y=1) +
 scale_fill_hue() +
 labs(x=" ", y="Total number of soil samples") +
 theme_grey(base_size=14) +
 theme(legend.position="none")*

分配给每个质地类别的土壤样本总数。

每一类可以根据它们的纹理进行分组,例如粗糙、中等或精细,这对于主成分分析是有用的。

分为粗、中和细质地的土壤质地类别的数量。

此外,我们还可以从下面的三元图中看到,我们的大多数土壤样品具有大致相等比例的沙子、淤泥和粘土大小的颗粒,这解释了不同壤土类型结构在结构类别中的优势。

*soil.data %>%
 ggtern(aes(x=Sand, y=Clay, z=Silt)) + 
    stat_density_tern(aes(fill = ..level.., alpha = ..level..), geom = "polygon") +
    scale_fill_gradient2(low="red", high="blue") +
    theme_bw(base_size=14) +
    guides(color="none", fill="none", alpha="none")*

密度等值线的可视化。

土壤颗粒级分

由于土壤颗粒的成分性质,土壤颗粒数据存在固有的依赖性。应用标准统计方法——例如算术平均值、标准偏差等——无法解释数据的这种受限属性。这就是为什么像探索性数据分析(EDA)一样,开发了一套称为成分数据分析(CoDA)的程序来分析这种数据。在 CoDA 中,对数比变换应用于成分数据,因此数据不受限制,可以在真实空间中取任何值,并且可以使用任何标准统计方法进行分析。不同类型的对数比变换包括:

加法对数比变换(alr)
居中对数比变换(clr)
等距对数比变换(ilr)

对于 D = 3 部分组成数据,加法、居中和等距对数比转换由下式给出

因此,下文通过箱线图矩阵总结了土壤颗粒大小部分的描述性统计数据,其中每个箱线图都是成对的对数比。

成对对数比的箱线图矩阵。

或者,我们可以计算一些描述性的统计数据。

*> comp.data %>% mean()      # Compositional (geometric) mean
Clay      Silt      Sand 
0.2413    0.3579    0.4008> comp.data %>% variation() # Variation matrix
     Clay      Silt      Sand
Clay 0.0000    0.4174    1.2623
Silt 0.4174    0.0000    0.9059
Sand 1.2623    0.9059    0.0000> comp.data %>% var()       # Variance matrix of the clr-transform      
      Clay      Silt     Sand
Clay  0.3074    0.0927  -0.4001
Silt  0.0927    0.2371  -0.3298
Sand -0.4001   -0.3298   0.7298*

土壤质地对 LIBS 光谱的影响

在查看了我们的土壤样本及其结构数据后,我现在将考虑 LIBS 光谱数据。这里,要考虑的两个最重要的参数是光谱的整体强度及其基于重复测量的相对标准偏差——我们对每个分析样品进行了 24 次重复测量。

下图说明了从不同土壤质地类别获得的平均强度,并根据土壤质地类型进行分组。

*spec.class %>%
  arrange(norm.int) %>%
  mutate(class=factor(class, levels=class)) %>%
  ggplot(aes(x=class, y=norm.int, group=texture, color=texture)) +
  geom_point() +
  geom_line() +
  labs(x=" ", y="Normalized averaged intensity") +
  theme_bw(base_size=14)*

作为土壤质地函数的 LIBS 光谱的标准化平均强度。

类似地,比较信号的可变性。

作为土壤质地函数的 LIBS 信号的相对标准偏差。

主成分分析

对光谱数据进行 PCA,其清楚地显示了样品按结构类型聚类,尽管按结构类别聚类不太明显。

*set.seed(1234)
pca.model <- soil.data[ ,-1] %>%
  select(Clay, Silt, Sand, colnames(spec.diff[selec.wave])) %>%
  PCA(scale.unit=TRUE, graph=FALSE)fviz_pca_biplot(pca.model,
                axes        = c(1,3),
                geom.ind    = "point",
                geom.var    = c("arrow", "text"),
                pointshape  = 21,
                pointsize   = 4,
                label       = "var",
                select.var  = list(name=c("Clay", "Silt", "Sand")),
                fill.ind    = pca.data$texture,
                col.ind     = "black",
                col         = "darkred",
                palette     = "jco",
                legend.title= "Texture",
                repel       = TRUE) +
  labs(x="t1 (69.3%)", y="t3 (5.8%)") +
  theme_bw(base_size=14)*

按土壤质地类型分组的光谱数据的 PCA 双标图。

按土壤结构类别分组的光谱数据的 PCA 双标图。

摘要

我提供了一个快速概览,介绍了使用 LIBS 仪器对土壤样品进行元素成分分析的准确性如何受到不同类型土壤质地的影响。我们以前认为,特别注意土壤颗粒大小的不均匀性会产生更好的测量精度。LIBS 受欢迎的原因之一是其速度和多功能性,因此将样本接地是一个最佳的选择,但可能更具理论性。

**更新:R 代码可从以下 GitHub 链接访问,https://GitHub . com/ChristianGoueguel/EDA-of-composition-Variables

对 1959 年至 2016 年美国宇航局宇航员的探索性分析

原文:https://towardsdatascience.com/exploratory-analysis-of-nasa-astronauts-from-1959-2016-778aba6775a8?source=collection_archive---------30-----------------------

Unsplash 上用高清拍摄的历史照片

当我们还是孩子的时候,这个世界似乎充满了无限、无尽和奇妙的可能性,我们被问及长大后想做什么。虽然有许多不同的反应,如医生,消防队员,警察,或美国总统,其他人想成为宇航员,为美国航天局工作。虽然成为一名宇航员不是一件容易的事,但这篇文章将探索那些已经成为宇航员的人,并希望鼓励那些正在努力实现他们太空雄心的人。

我们会看母校的,军衔,军兵种,军种分布,性别分布,最致命的任务,最多的太空飞行,在太空呆的时间最多。

宇航员的性别分布是怎样的?

作者图片

大多数是男性,但女性不要气馁!

哪些学校培养了最多的宇航员?

作者图片

我们可以看到美国海军学院&美国空军学院培养了最多的宇航员。普渡大学麻省理工学院紧随其后。

宇航员的本科学位是什么?

作者图片

许多宇航员拥有不止一个本科学位。

他们的研究生学位呢?

作者图片

许多宇航员也有不止一个研究生学位。

宇航员中有百分之多少是军人?

作者图片

大多数是军人,但也有许多不是军人。

哪些军种?

作者图片

似乎美国空军和美国海军生产的宇航员最多。

军衔?

作者图片

他们中的许多人过去/现在都是军队中的高级成员。

五次或更多次太空飞行的宇航员。

作者图片

富兰克林·r·张-迪亚兹 & 杰里·l·罗斯并列第一。

太空飞行时间最多的前 20 名宇航员。

作者图片

杰弗里·n·威廉姆斯赢得最多太空飞行小时。

最后,哪个任务是最致命的?

作者图片

1986 年 1 月 28 日,STS-51L 上的挑战者号及其七名宇航员失踪。他们的名字分别是格雷戈里·b·贾维斯s·克丽斯塔·麦考利夫罗纳德·e·麦克奈尔埃里森·s·奥尼祖卡朱迪思·a·雷斯尼克弗朗西斯·r·斯科比迈克尔·j·史密斯。愿所有在任务中丧生的宇航员安息。

总之,

根据我的发现,如果你正在考虑成为一名宇航员,这可能会增加你的机会,加入军队并真正将自己融入高等教育是明智的。虽然工程、数学和科学主要是在宇航员中发现的,但我相信 NASA 可以找到许多对他们的任务有用的不同领域。

这个数据集来源于Kaggle.com美国宇航局宇航员年鉴

探索性数据分析

原文:https://towardsdatascience.com/exploratory-data-analysis-86eb12060eac?source=collection_archive---------28-----------------------

视觉化!视觉化!视觉化!哦,不要浪费墨水!

图片由皮克斯拜的 Gerd Altmann 提供

数据质量

我们以前写过关于数据质量的问题。当然,许多问题可以在一开始就迅速而清晰地被发现,例如那些缺失的河流水位值(即使您不能立即确定如何处理它们)。但是,许多问题只有在你稍微深入挖掘数据,并开始考虑变量内部和变量之间的分布和关系时才会浮出水面。例如,一个人的身高可能非常合理地介于 50 厘米(学步儿童)和 1.80 米之间。他们的体重也可能非常合理地介于 10 千克和 100 千克之间。但是只有把两者放在一起考虑,你才能意识到有一个 100 公斤重的“蹒跚学步的孩子”或者一个 15 公斤重的高个子成年人。你手上突然有了一些离群值!因此,探索性数据分析(EDA)是数据质量问题仍处于探索阶段的一个步骤。

复杂的关系

我们刚刚举了一个例子,在这个例子中,你需要一致地比较两个变量,以便发现异常值。言下之意,这很容易;你只需在 x 轴上画出一个变量,在 y 轴上画出另一个变量,然后看看“突出”的是什么。如果你也有年龄呢?有一些绘图工具可以让你定义一个 z 轴,甚至可以动态地旋转 z 轴来显示不同方向的数据。如果你也有薪水呢?现在变得棘手了,不是吗?你可以在多张图上画出变量的不同组合,比如 x 和 y。但即使这样也只是考虑了成对的关系。

如果你的数据有 10 个变量呢?20?1000?幸运的是,有许多技术可以减少数据的维数并可视化模式。一种常见的技术是主成分分析(PCA),尽管还有许多其他技术。

形象化

谈到 EDA,一项我们怎么强调都不为过的技能是数据可视化。这并不总是一个数字!如果它是合理的少量汇总统计数据,那么表格通常是最有效的交流方式。现在我们不太倾向于打印这么多,但是想象一下,打印一个中等大小的条形图,仅仅传达三个数据值,要用多少墨水。也许这将是一个很好的候选,在一个简单的表中提出。

现在软件包中有越来越多的绘图类型,提供了许多不同的方式来表示您的数据。但是仅仅因为它们的存在,并不意味着你需要使用它们。不要像 datavis 那样,使用他能找到的每一个动画和声音效果进行 powerpoint 演示,因为它们就在那里!仔细考虑你想在你的形象中引出什么信息,并据此精心制作。让您入门的一些基本类型:

  • 散点图—非常适合显示两个变量之间的关系。可以有效地利用点的颜色、大小甚至形状。考虑将哪些功能映射到哪种美学。除非你确信你的读者有非常敏锐的眼光,否则不要把一个有 20 个不同值的特征映射到形状上!你真的想用 20 种不同的值来表示吗?或许只给前五个类别涂上颜色,其余的归入“其他”类别。不同的颜色可以提供离散(分类)特征的信息。当心有太多!色调(单一颜色,例如从黑色到越来越浅的灰色)可以为你的身材增加一个额外的连续变量。许多图形库会根据数据类型选择颜色或色调。注意你的绘图函数认为一个特征是什么数据类型!1/0 代表真/假是一个经典的例子,它可以表现为一种色调,而当它作为不同的颜色会更清晰。最重要的是,探索不同的选择,有效地展现你的信息。如果你有大量密集的数据点,就试试 alpha 值,问问自己是否真的需要将每个数据点都放在页面上——你能通过在每一千个数据点中绘制一个来显示关于数据分布的相同信息吗?
  • 条形图——显示一组离散值的常用工具,例如类别计数。如果 x 轴也是一个类别,不要因为它产生了一道美丽的彩虹就分别给每个类别上色!例如,假设 x 轴是月,y 值是蝴蝶数量。不要按月份给酒吧涂颜色。但是也许应该考虑按季节上色。这可能会增加一些东西。或者,根据一个月是特别潮湿还是特别干燥,它可能与颜色有关。这将是对色彩的有效利用。如果你有太多的类别(在 x 轴上)以至于标签难以辨认,问问你自己是否真的需要显示所有的类别。也许仅仅一个显示前 20 个类别的数字就能表达你的观点?在这里,我们来到了“浪费墨水”点。如果你有两个类别,比如男性和女性,那么也许只需要把它们放在一个表格里。
  • 直方图——非常适合汇总分布。一定要探索不同的容器宽度,以便在数据中提取出适量的有意义的结构。不,我不能告诉你那是什么。
  • 折线图-非常适合绘制连续的数值序列。如果中间的 x 值没有意义,例如在处理类别时,请避免使用它们。眼睛自然会认为墨水连接点的轨迹是有效的插值。如果你没有很多 x 值,重新考虑它们的用途。包含三个顶点的线图信息不多。一个个人的 bugbear 是由许多值(点)组成的线,但它们形成了非常非常接近直线的东西。该线图是否仅包含两点,两端各一点,在这种情况下,这是一个很差的图,给人一种高相关性的错觉,还是有 1000 个点,这是(非常可疑的)相关性的更强有力的证据。散点图在这里会更好吗?或者至少是有点有线的剧情!用不同类别着色的多条线组成的单个图可以有效地突出不同类别之间的不同趋势。

这是一个令人难以置信的情节类型的节略选择,但它们是很好的中坚力量,通过一些仔细的思考,你可以一次又一次地有效利用。最重要的是,这一点怎么强调都不为过,想想你在你的情节中试图传达的是什么,并试图有效地传达它,只有它,用最少的笔墨和无关的大惊小怪和麻烦。

关于这篇文章

这是一个链接系列的第四篇文章,旨在简单介绍如何开始数据科学过程。这里可以找到介绍,这里可以找到上一篇,这里可以找到系列的下一篇

探索性数据分析:DataPrep.eda 与 Pandas-Profiling

原文:https://towardsdatascience.com/exploratory-data-analysis-dataprep-eda-vs-pandas-profiling-7137683fe47f?source=collection_archive---------14-----------------------

使用正确的工具进行探索性数据分析(EDA)

探索性数据分析(EDA)是每个数据科学项目的重要组成部分。EDA 的目的是实现对数据的理解,并获得关于数据所代表的现象的见解。

Pandas-profiling (2016)被誉为进行 EDA 的典范工具[1,2,3]。然而,pandas-profiling 的一个显著缺点是它给出了数据集的轮廓!EDA 是一个 迭代 过程,数据科学家将在其中质疑、理解、处理、转换数据,并重复[4、5、6]。pandas-profiling 的僵化结构与当前的 EDA 最佳实践相悖。

DataPrep.eda (2020)是由 SFU 的数据科学研究小组制作的用于做 eda 的 Python 库。DataPrep.eda 支持迭代和以任务为中心的分析——这是 eda 应该做的。(关于 DataPrep.eda 的全面介绍,请参见本文

在这篇文章中,我们将分析 DataPrep.eda 比 pandas-profiling 更适合做 eda 的四个原因:

  1. 更好的 API 设计
  2. 快 100 倍
  3. 智能可视化
  4. 处理大数据

“‘探索性数据分析’是一种态度,一种灵活的状态,一种寻找我们认为不存在的东西以及我们认为存在的东西的意愿。”

—《探索性数据分析》的作者约翰·图基

1.更好的 API 设计

我们将使用 DataPrep.eda 中的plot()函数。为了理解如何使用该函数有效地执行 eda,下面给出了数据科学家意图的函数调用的语法:

  • plot(df):“我想要数据集的概览”
  • plot(df, “col_1”):“我想了解栏目col_1
  • plot(df, “col_1”, “col_2”):“我想了解col_1col_2列的关系。”

为了了解这一点,我们将使用一个包含南韩新冠肺炎患者记录的数据集。让我们从数据集的概述开始:

from dataprep.eda import plot
import pandas as pddf = pd.read_csv("PatientInfo.csv")
df["confirmed_date"] = pd.to_datetime(df["confirmed_date"])
plot(df)

每列的频率分布

请注意列birth_year似乎具有双峰分布,让我们了解一下该列的更多信息:

# transform birth_year to age in 2020 for simplicity
df["age"] = 2020 - df["birth_year"]
plot(df, "age", bins=26)

以多种方式绘制的一列分布图

使用工具提示,我们可以检查模式的边界,并使用各种图来全面了解这种分布。

接下来,让我们调查一下新冠肺炎男性和女性的年龄分布。为此,我们只需将列sex添加到前面的函数调用中:

plot(df, "age", "sex", bins=26)

按性别比较年龄分布的图表

我们看到在感染新冠肺炎病毒的男性和女性中,年龄分布有很大的差异——所有的差异都来自一行简单的代码!

这说明了 EDA 应该如何进行——提问、想象、理解、重复。用不到 0.5 秒的时间完成上述每个命令,DataPrep.eda 是一个高效的 eda 工具。

pandas-profiling 能支持这个简单的 EDA 任务吗?

我们运行以下代码,其中 pandas-profiling 花费了 50 秒来生成报告:

from pandas_profiling import ProfileReport
ProfileReport(df).to_widgets()

为了分析单变量分布,pandas-profiling 具有以下输出:

熊猫 Jupyter 笔记本中的剖析 API

用户需要切换每一列来查看它的信息。虽然我们可以发现birth_year的双峰分布,但是 pandas-profilin g 不支持对这一见解的进一步研究,也不支持分析agesex之间的关系。

对于这个简单的 EDA 场景,DataPrep.eda 能够发现重要的见解,但是 pandas-profiling 是不够的。

交互式功能—工具提示

用户与数据的交互对于有效理解数据是必不可少的[7]。DataPrep.eda 使用交互式可视化库 Bokeh 创建所有图,并包含工具提示,可准确读取可视化的每个组件。然而,熊猫烧香不支持工具提示。

2.速度快 100 倍

DataPrep.eda 比 pandas 快 100 倍——针对 eda 的分析

回想一下,在第 1 部分的中,完成每项任务需要不到 0.5 秒的时间,而 pandas-profiling 需要 50 多秒才能生成一份报告。

DataPrep.eda 比 pandas 更快,原因有二:

  1. DataPrep.eda 使用 Dask ,一个并行计算库,用于所有的数据处理。然而,熊猫简介使用了熊猫
  2. DataPrep.eda 通过创建与当前 eda 任务相关的可视化来避免不必要的计算,而 pandas-profiling 只分析整个数据集。

DataPrep.eda 比 pandas 快 5 倍以上——即使对于数据分析也是如此

DataPrep.eda 可用于生成 pandas-profiling 报告的组件,从而实现直接的性能比较。下图显示了针对 DataPrep.eda 的三个组件运行 pandas-profiling 的ProfileReport的结果:单变量分析(plot(df))、相关矩阵(plot_correlation(df))和缺失值图(plot_missing(df))。

DataPrep.eda 与 pandas-profiling 的等待时间(使用汽车数据集,通过复制放大)

使用 Dask 代替 Pandas 是 DataPrep.eda 比 pandas-profiling 更快的主要原因。影响性能的更具体的因素是

  1. DataPrep.eda 并行处理单变量分析,而 pandas-profiling 顺序计算单变量统计。
  2. 使用 Dask 的 DataPrep.eda 支持逐块计算,而 Pandas-profiling 在整个数据集上执行计算(对于大型数据集很重要)。

3.智能可视化

DataPrep.eda 的一些智能特性包括

  • 选择正确的图来可视化每个 EDA 任务的数据;
  • 列类型推断(数字、分类和日期时间);
  • 为每个图找到合适的时间单位(用户也可以指定);
  • 输出具有最高计数的分类值,以便视觉清晰(用户也可以指定)。

为了看到这些功能的作用,让我们理解人们是如何随着时间的推移收缩新冠肺炎的,即列confirmed_dateinfection_case之间的关系。我们跑

plot(df, "confirmed_date", "infection_case")

描绘日期时间和分类列之间关系的图

我们可以很容易地看到哪些收缩方法是重要的,在哪些时期!

我们可以用熊猫轮廓来完成这项任务吗?

不要!Pandas-profiling 只支持相关矩阵形式的交互(也受 DataPrep.eda 支持)和用于两个连续变量的双变量分析的热图。在数据集分析框架中,有效的双变量分析代价太高,因为必须对每一对列进行计算,即使用户可能只对一小部分关系感兴趣。

4.处理大量数据

使用 Dask 的 DataPrep.eda 适用于大于内存的数据集。Dask 支持核外并行处理,因此可以高效地评估大型数据集上的计算。

使用 Pandas 的 Pandas-profiling 只有用于内存分析的数据结构;在大型数据集上,pandas-profiling 的性能会显著下降。

结论

探索性数据分析是一个迭代循环,其步骤包括

  1. 质疑数据
  2. 通过处理和可视化数据来回答问题
  3. 在获得新的理解后提炼之前的问题,或者提出新的问题

没有适合全面 EDA 的通用数据配置文件。

与 pandas-profiling 相比,DataPrep.eda 是更好的 eda 工具,原因有四:

  1. 更好的 API 设计
    DataPrep.eda 的 API 是为 eda 设计的,而不是为数据剖析设计的
  2. 快 100 倍
    DataPrep.eda 并行执行计算
  3. 智能可视化
    DataPrep.eda 将自动选择合适的图来可视化数据
  4. 处理大数据
    DataPrep.eda 支持核外处理

是时候从生成数据概要文件开始,以 DataPrep.eda 的方式执行 EDA 了。

一个笔记本的代码从这篇文章中可以找到 这里 。要安装 DataPrep.eda,并了解有关参与该项目的信息,请访问 此处 。一个 DataPrep.eda 教程视频可以在 这里 。别忘了给 GitHub 上的 项目 上星★。

参考

[1] M. Deep,快速探索性数据分析:熊猫简介 (2020),中等

[2] L. Frei,使用 Pandas-Profiling (2019)加速您的探索性数据分析,走向数据科学

[3] R. Rei, EDA 使用 Panda 的剖析 (2020),走向数据科学

[4] D. Bourke,探索性数据分析的温和介绍 (2019),走向数据科学

[5] J. Wei,探索性数据分析:结构化数据实用指南和模板 (2019),走向数据科学

[6] G. Grolemund 和 H. Wickham 著,R for Data Science(2016 年 12 月),在线图书

[7] W. Koehrsen,Python 中数据可视化的下一个级别 (2019),走向数据科学

使用 EarthPy 对卫星图像进行探索性数据分析

原文:https://towardsdatascience.com/exploratory-data-analysis-eda-on-satellite-imagery-using-earthpy-c0e186fe4293?source=collection_archive---------16-----------------------

中间导轨

在本文中,我们将使用 EarthPy 来处理卫星图像,并有效地进行探索性数据分析(EDA)

杰西·艾伦和罗伯特·西蒙利用美国地质调查局地球探测器的数据拍摄的陆地卫星图像。

目录

  • 卫星图像介绍
  • 安装
  • 如何下载卫星图片
  • 卫星图像上的探索性数据分析(EDA)
  • 最终想法
  • 参考文献

让我们开始吧,✨

卫星图像介绍

卫星图像有着广泛的应用,它融入了人类生活的方方面面。特别是遥感经过多年的发展,已经解决了不同领域的很多问题。在遥感中,高光谱遥感器以其高光谱分辨率被广泛用于监测地球表面。

高光谱图像(HSI) 数据通常包含同一空间区域内的数百个光谱带,这些光谱带为识别各种材料提供了有价值的信息。在 HSI 中,每个像素可以被视为一个高维向量,其条目对应于从可见光到红外的光谱反射率。

遥感的一些最佳应用是矿物勘探、国防研究、生物化学组成、森林健康状况、农业等。使用下面的研究论文可以更直观地了解高光谱遥感的应用。

[## 超光谱遥感应用简介

植被指数叶面积指数高光谱数据悬浮泥沙含量土地类这些…

link.springer.com](https://link.springer.com/chapter/10.1007/978-3-662-47456-3_9)

使用下面的文章获得使用 python 进行高光谱影像分析的实践经验。

[## 超光谱图像分析—入门

使用 Python 进行高光谱图像分析的演练。

towardsdatascience.com](/hyperspectral-image-analysis-getting-started-74758c12f2e9)

装置

让我们看看 EarthPy ,它是一个开源 python 包,使用开源工具可以更容易地绘制和处理空间栅格和矢量数据。Earthpy 依赖于 geopandas,后者侧重于矢量数据,而 rasterio 则有助于栅格数据文件的输入和输出。它还需要 matplotlib 进行绘图操作。使用以下命令安装 EarthPy。

如何下载卫星图像

EarthpPy 包中有 14 个可用的数据集,让我们看看可供下载的数据集。

数据集-按作者分类的图像

让我们看看如何下载可用的数据集。“get_data”方法用于使用数据集的名称下载数据。

输出将是:

图片 bt 作者

卫星图像上的 EDA

在本文中,我们使用了' vignette Landsat' 数据集。该数据集包含 2017 年 2 月 21 日的 Landsat 8 数据,用于科罗拉多州尼德兰附近冷泉火边界周围的区域。它还包含 GeoMAC 提供的冷泉火边界。Landsat 波段已被裁剪为覆盖冷泉火边界,并且不包括原始图像东南边缘的数据值以及云层。

让我们来看看如何读取数据集:

使用 EarthPy 的空间模块中的“堆叠”方法选择并堆叠条带。上面的代码读取数据并打印元数据。输出如下所示。

Landsat8 数据集的元数据-作者提供的图像

数据集的形状为(2158,1941),有 7 个波段,包含 41,88,678 个像素。

绘图区带

正如我们所讨论的,Landsat8 数据集有 7 个波段。让我们使用 earthpy 包中的内置方法' plot_bands '来绘制波段。plot_bands 方法采用带和图的堆栈以及自定义标题,这可以通过使用title=参数将每个图像的唯一标题作为标题列表传递来完成。

让我们看看绘制 Landsat8 数据集波段的代码。

结果图如下所示:

landsat8 数据集中所有波段的图-作者提供的图像

RGB 合成图像

这些超光谱图像有多个波段,包含从可见光到红外的数据。所以对人类来说很难将数据可视化。因此,创建 RGB 合成图像可以更容易有效地理解数据。为了绘制 RGB 合成影像,您将绘制红色、绿色和蓝色波段,分别是我们根据 Landsat8 数据创建的影像堆栈中的波段 4、3 和 2。Python 使用从零开始的索引系统,所以你需要从每个索引中减去值 1。因此,红色波段的指数为 3,绿色为 2,蓝色为 1。让我们看看绘制 RGB 合成图像的代码。

生成的 RGB 合成图像如下所示。

作者图片

拉伸合成图像

如果像素亮度值偏向零值,我们创建的合成图像有时会很暗。这种类型的问题可以通过拉伸图像中的像素亮度值来解决,使用自变量stretch=True将这些值扩展到潜在值的整个 0-255 范围,以增加图像的视觉对比度。此外,str_clip参数允许您指定想要剪掉多少数据尾部。数字越大,数据将被拉伸或变亮的越多。

让我们看看如何起诉 earthpy。

应用拉伸后的 RGB 合成图像如下所示:

应用 Strech 后的 Landsat8 数据集 RGB 合成图像-作者提供的图像

绘制光谱

让我们看看如何绘制光谱,这有助于我们理解像素的性质。以下代码用于绘制 Landsast8 数据集第一行的 7 个光谱。

输出如下所示:

光谱图—图片由作者提供

直方图

可视化高光谱图像数据集的波段有助于我们理解波段值的分布。“eathpy.plot”中的hist方法通过为我们之前创建的数据集/堆栈的波段绘制直方图来完成工作。我们还可以修改单个直方图的列大小,标题,颜色。让我们看看绘制直方图的代码。

产生的图是

波段直方图-按作者分类的图像

归一化差异植被指数(NDVI)

为了确定一块土地上的绿色密度,研究人员必须观察植物反射的可见光(VIS)和近红外(NIR)阳光的不同颜色(波长)。归一化差异植被指数(NDVI)通过测量植被强烈反射的近红外和植被吸收的红光之间的差异来量化植被。NDVI 的范围总是从-1 到+1。

NDVI =(近红外-可见光)/(近红外+可见光)

例如,当你有负值时,很可能是水。另一方面,如果 NDVI 值接近+1,则很有可能是浓密的绿叶。但是当 NDVI 接近零度时,就没有绿叶,甚至可能成为城市化地区。

以上代码用于计算归一化差异植被指数(NDVI)并显示生成的图像。

NDVI 陆地卫星 8 号—作者提供的图像

NDVI 的分类

基于高光谱图像数据,归一化差异植被指数(NDVI)结果被分类为有用的类别。0 以下的值将一起归类为无植被。将为裸露区域中等植被区域创建附加类别。让我们看看代码:

上述代码对 NDVI 进行分类,并绘制结果数据。分类后的结果图像如下所示。

归一化差异植被指数(NDVI)类别-图片由作者提供

不是但不是最不重要的,我在博客中写的代码可以从下面的 GitHub 链接或使用 google collaboratory 笔记本访问。

[## syamkakarla 98/超光谱 _ 图像 _ 分析 _ 简化

github.com](https://github.com/syamkakarla98/Hyperspectral_Image_Analysis_Simplified/blob/master/Articles/Exploratory_Data_Analysis(EDA)_on_Satellite_Imagery_Using EarthPy.ipynb) [## 使用 EarthPy 对卫星图像进行探索性数据分析

colab.research.google.com](https://colab.research.google.com/drive/1kpG7Vp_gg5uXUEP0BO3GwN1hMDpPIJKR?usp=sharing)

最后的想法

本文涵盖了使用 EarthPy 分析卫星/高光谱图像的不同方法,但还有更多方法,如降维(DR)分类等。使用下面的文章进行详细解释并使用 python 进行实际操作。

[## 利用 Python 实现高光谱图像的降维

高光谱图像的降维技术。

towardsdatascience.com](/dimensionality-reduction-in-hyperspectral-images-using-python-611b40b6accc) [## 超光谱图像分析分类

使用 python 对高光谱图像(HSI)进行分类的演练。

towardsdatascience.com](/hyperspectral-image-analysis-classification-c41f69ac447f)

参考

[## 测量植被(NDVI 和 EVI)

为了监测植被的主要波动并了解它们如何影响环境,科学家们使用…

earthobservatory.nasa.gov](https://earthobservatory.nasa.gov/features/MeasuringVegetation/measuring_vegetation_2.php) [## 地球引擎|地球引擎数据目录中的 Landsat 8 数据集

来自 Landsat 8 OLI/TIRS 传感器数据集的大气校正地表反射率可用性:2013 年 4 月…

developers.google.com](https://developers.google.com/earth-engine/datasets/catalog/landsat-8)

探索性数据分析(EDA): Python

原文:https://towardsdatascience.com/exploratory-data-analysis-eda-python-87178e35b14?source=collection_archive---------0-----------------------

学习使用 Python 和 Numpy、Matplotlib 和 Pandas 进行探索性数据分析的基础知识。

照片由UXUnsplash 上拍摄

什么是探索性数据分析(EDA)?

如果我们想用简单的术语解释 EDA,这意味着试图更好地理解给定的数据,以便我们可以从中获得一些意义。

我们可以在 维基 中找到更正式的定义。

在统计学中,探索性数据分析是一种分析数据集以总结其主要特征的方法,通常采用可视化方法。可以使用或不使用统计模型,但 EDA 主要是为了查看数据可以告诉我们什么,而不仅仅是正式的建模或假设测试任务。

Python 中的 EDA 使用数据可视化来绘制有意义的模式和见解。它还包括通过消除数据中的不规则性来准备用于分析的数据集。

基于 EDA 的结果,公司也做出商业决策,这些决策会在以后产生影响。

  • 如果 EDA 没有正确完成,那么它会妨碍机器学习模型构建过程中的进一步步骤。
  • 如果做得好,可能会提高我们接下来做的一切事情的功效。

在本文中,我们将了解以下主题:

  1. 数据来源
  2. 数据清理
  3. 单变量分析
  4. 双变量分析
  5. 多变量分析

1.数据来源

数据源是查找数据并将其加载到我们的系统中的过程。概括地说,我们有两种方法可以找到数据。

  1. 私人数据
  2. 公共数据

私人数据

顾名思义,私人数据是由私人机构给出的。它有一些安全和隐私问题。这种类型的数据主要用于组织内部分析。

公开数据

每个人都可以获得这种类型的数据。我们可以在政府网站和公共组织等找到这一点。任何人都可以访问这些数据,我们不需要任何特殊的许可或批准。

我们可以从以下网站获取公共数据。

EDA 的第一步是数据源,我们已经了解了如何访问数据并加载到系统中。现在,下一步是如何清理数据。

2.数据清理

完成数据源后,EDA 过程的下一步是数据清理。在将数据输入我们的系统后,去除不规则性并清理数据是非常重要的。

不规则性是不同类型的数据。

  • 缺少值
  • 格式不正确
  • 不正确的标题
  • 异常/异常值

为了执行数据清理,我们使用一个样本数据集,它可以在 这里 找到。

我们正在使用 Jupyter 笔记本进行分析。

首先,让我们导入必要的库并将数据存储在我们的系统中以供分析。

现在,数据集看起来像这样,

营销分析数据集

如果我们观察上面的数据集,前 2 行的列标题有一些差异。正确的数据来自索引号 1。因此,我们必须修复前两行。

这被称为固定行和列。让我们忽略前两行,再次加载数据。

现在,数据集看起来像这样,它更有意义。

修复行和列后的数据集

以下是在固定行和列时要采取的步骤:

  1. 删除数据集中的汇总行和汇总列。
  2. 删除每页上的页眉和页脚行。
  3. 删除多余的行,如空行、页码等。
  4. 如果有助于更好地理解数据,我们可以合并不同的列
  5. 同样,我们也可以根据我们的需求或理解,将一个列拆分成多个列。
  6. 添加列名,将列名添加到数据集中非常重要。

现在,如果我们观察上面的数据集,customerid列对我们的分析不重要,而且jobedu列同时包含了jobeducation的信息。

因此,我们要做的是,我们将删除customerid列,并将jobedu列拆分为另外两列jobeducation,之后,我们也将删除jobedu列。

现在,数据集看起来像这样,

删除Customerid 和 jobedu 列,添加 job 和 edu 列

缺失值

如果在进行任何统计分析之前数据集中有缺失值,我们需要处理这些缺失值。

缺失值主要有三种类型。

  1. MCAR(完全随机缺失):这些值不依赖于任何其他特征。
  2. MAR(随机缺失):这些值可能依赖于其他一些特征。
  3. MNAR(非随机缺失):这些缺失值有一些缺失的原因。

让我们看看数据集中哪些列缺少值。

# Checking the missing values
data.isnull().sum()

输出将是,

数据集中的空值

正如我们所看到的,有三列包含缺失值。让我们看看如何处理丢失的值。我们可以通过删除丢失的记录或输入值来处理丢失的值。

删除丢失的值

让我们处理age列中缺失的值。

现在让我们检查数据集中缺失的值。

处理年龄列后缺少值

让我们对 month 列的缺失值进行估算。

因为 month 列是一个对象类型,所以让我们计算该列的模式,并将这些值归入缺失值。

现在输出是,

# Mode of month is
**'may, 2017'**# Null values in month column after imputing with mode
**0**

处理响应列中的缺失值。因为我们的目标列是 Response 列,如果我们将值归入这个列,就会影响我们的分析。因此,最好从响应列中删除缺失的值。

#drop the records with response missing in data.
**data = data[~data.response.isnull()].copy()**# Calculate the missing values in each column of data frame
**data.isnull().sum()**

让我们检查数据集中缺失的值是否已被处理,

所有丢失的值都已被处理

我们还可以将缺失值填充为‘NaN’,这样在进行任何统计分析时,都不会影响结果。

处理异常值

我们已经看到了如何修复缺失值,现在让我们看看如何处理数据集中的异常值。

离群值是指远离下一个最近的数据点的值。

有两种异常值:

  1. 单变量异常值:单变量异常值是基于一个变量,其值位于期望值范围之外的数据点。
  2. 多元异常值:在绘制数据时,一个变量的某些值可能不会超出预期范围,但当您绘制其他变量的数据时,这些值可能会远离预期值。

因此,在理解了这些异常值的原因之后,如果更有意义的话,我们可以通过丢弃那些记录或者用值进行估算或者让它们保持原样来处理它们。

标准化值

要对一组值执行数据分析,我们必须确保同一列中的值应该在相同的范围内。例如,如果数据包含不同公司汽车的最高速度值,那么整列应该以米/秒或英里/秒为单位。

现在,我们已经清楚了如何获取和清理数据,让我们看看如何分析数据。

3.单变量分析

如果我们对数据集中的单个变量/列进行分析,这就是所谓的单变量分析。

分类无序单变量分析:

无序变量是没有定义顺序的分类变量。以我们的数据为例,数据集中的职位列被分成许多子类别,如技术人员、蓝领、服务、管理等。“作业列中的任何值都没有权重或度量。

现在,让我们通过使用图来分析工作类别。由于 Job 是一个类别,我们将绘制条形图。

输出看起来像这样,

通过上面的柱状图,我们可以推断,与其他类别相比,数据集包含更多的蓝领工人。

分类有序单变量分析:

有序变量是那些具有自然排序的变量。我们数据集中分类有序变量的一些示例如下:

  • 月份:一月,二月,三月……
  • 教育:小学、中学……

现在,让我们从数据集中分析教育变量。既然我们已经看到了条形图,让我们看看饼图是什么样子的。

输出将是,

通过以上分析,我们可以推断出,该数据集有大量属于中等教育之后的那一级和下一级小学。此外,其中很小一部分不为人知。

这就是我们分析单变量分类分析的方法。如果列或变量是数值型的,那么我们将通过计算它的平均值、中值、标准差等来进行分析。我们可以通过使用 describe 函数获得这些值。

data.salary.describe()

输出将是,

4.双变量分析

如果我们从数据集中考虑两个变量/列来分析数据,这就是所谓的双变量分析。

a)数值-数值分析:

分析数据集中的两个数值变量称为数值-数值分析。我们可以从三个不同的角度来分析。

  • 散点图
  • 配对图
  • 相关矩阵

散点图

让我们从我们的数据集中选取三列“余额”、“年龄”和“薪水”,看看我们可以通过绘制salary balanceage balance之间的散点图来推断什么

现在,散点图看起来像,

散点图

配对图

现在,让我们为我们在绘制散点图时使用的三列绘制成对图。我们将使用 seaborn 库来绘制 Pair 图。

结对图看起来像这样,

年龄、平衡、工资的配对图

相关矩阵

因为在散点图和配对图中,我们不能使用两个以上的变量作为 x 轴和 y 轴,所以很难在单个图形中看到三个数值变量之间的关系。在这些情况下,我们将使用相关矩阵。

首先,我们使用年龄、工资和余额创建了一个矩阵。之后,我们使用矩阵的 seaborn 库绘制热图。

b)数值分类分析

分析数据集中的一个数值变量和一个分类变量称为数值分类分析。我们主要使用均值图、中值图和箱线图来分析它们。

让我们从数据集中取出salaryresponse列。

首先使用groupby检查平均值

#groupby the response to find the mean of the salary with response no & yes separately.**data.groupby('response')['salary'].mean()**

输出将是,

使用平均值的响应和薪金

根据薪水的不同,回答“是”和“否”没有太大区别。

让我们计算一下中位数,

#groupby the response to find the median of the salary with response no & yes separately.**data.groupby('response')['salary'].median()**

输出将是,

无论是平均值还是中间值,我们都可以说,不管一个人的工资多少,回答“是”和“否”的答案都是一样的。但是,它真的是那样表现的吗,让我们画出它们的方框图,并检查它的表现。

#plot the box plot of salary for yes & no responses.**sns.boxplot(data.response, data.salary)
plt.show()**

箱形图看起来像这样,

正如我们所看到的,当我们绘制箱线图时,它描绘了一个与平均值和中值非常不同的画面。给予肯定回答的顾客的 IQR 偏高。

这就是我们分析数字分类变量的方法,我们使用均值、中值和箱线图来得出某种结论。

c)分类—分类分析

由于我们的目标变量/列是回复率,我们将看到不同的类别如教育、婚姻状况等。,与响应列相关联。因此,我们将把它们转换成“1”和“0”,而不是“是”和“否”,这样我们就可以得到“回应率”。

输出看起来像这样,

让我们来看看婚姻状况中不同类别的回复率是如何变化的。

图表看起来像这样,

通过上图,我们可以推断出,对于数据集中的单一状态成员,正面响应更多。类似地,我们可以绘制贷款对响应率、住房贷款对响应率等图表。

5.多变量分析

如果我们从一个数据集中考虑两个以上的变量/列来分析数据,这就是所谓的多元分析。

让我们看看“教育程度”、“婚姻状况”和“回应率”之间的差异。

首先,我们将创建一个包含三列的数据透视表,之后,我们将创建一个热图。

数据透视表和热图如下所示,

根据热图,我们可以推断,受过小学教育的已婚人士不太可能对调查做出积极回应,而受过高等教育的单身人士最有可能对调查做出积极回应。

类似地,我们可以绘制工作对婚姻对反应、教育对收入对反应等图表。

结论

这就是我们如何进行探索性数据分析。探索性数据分析(EDA)帮助我们透过数据看问题。我们对数据探索得越多,从中得出的见解就越多。作为一名数据分析师,我们几乎 80%的时间都会花在理解数据和通过 EDA 解决各种业务问题上。

感谢您阅读快乐编码!!!

在这里查看我以前关于 Python 的文章

参考

使用 Pandas 进行探索性数据分析(EDA)可视化

原文:https://towardsdatascience.com/exploratory-data-analysis-eda-visualization-using-pandas-ca5a04271607?source=collection_archive---------1-----------------------

照片由艾萨克·史密斯Unsplash 上拍摄

原来你可以直接从你的数据帧中画出许多有用的图,而不需要调用plt.show()

Pandas 无疑是用 Python 执行数据分析的最受欢迎的包,这要归功于它丰富直观的功能,允许我们轻松地对数据执行无数次操作。难怪该软件包已经成为许多数据科学家/分析师处理日常任务的不可或缺的工具。

关于探索性数据分析(EDA),Pandas 通常与另一个绘图包一起使用,如 Matplotlib、Seaborn、Plotly 等。在通过 Pandas 对所有数据进行预处理后,就可以使用手边选择的绘图包对其进行可视化。

但是,如果我告诉你 Pandas 也能够可视化你的数据,这意味着你可以直接从你使用的数据框架中生成 EDA 所需的(大部分)图,那会怎么样呢?不会很整齐吗?

这正是我想在这里与你分享的话题。

快速总结

以下是本文所涉及的内容:

  1. 熊猫的plot方法一瞥
  2. 如何使用熊猫的plot方法画一些基本的图,包括箱线图、散点图、饼图等等
  3. 如何使用 Pandas 绘制相关矩阵(这是由plot方法生成的而不是,但是它在任何 EDA 中都是必不可少的,所以我也包括它)
  4. 使用以下 pandas 功能绘制数据准备,以创建上述第 2 点中的一些图。
  • 分组依据聚合(pandas.DataFrame.groupby())
  • 分位数切宁滨(pandas.DataFrame.qcut())
  • 应用方法转换(pandas.DataFrame.apply())

数据

本文中使用的数据是一个电子商务数据集,可通过这个链接在 Kaggle 获得。该数据集是一个交易数据,包含 2011 年英国注册的无店铺在线零售的交易。这里要提到的是,数据已经过预处理,为本文的主要目标做好了准备,即可视化数据。

前五行数据

如我们所见,该数据有如下八列:

  1. InvoiceNo:发票号(表键)
  2. InvoiceDate:发票日期
  3. StockCode:物品编码
  4. Description:物品描述
  5. Quantity:物品售出数量
  6. UnitPrice:物品单价(英镑)
  7. UnitDiscount:单位商品折扣
  8. UnitWeight:物品单位重量(磅)

接下来,我们显示下面数字列的一些统计汇总,这是data.describe() 方法的输出。

数字列的统计摘要

熊猫可视化方法一瞥

Pandas 以plot方法的名义提供了可视化其系列和数据帧的功能。该功能是对 matplotlib 包的 plot方法的简单包装,具有更高级别的实现。通过使用该方法,我们可以直接从我们的数据帧/序列中生成一些有用的(基本)图,而无需调用plt.show(),因为我们已经在笔记本的开头导入了包,即import matplotlib.pyplot as plt

从数据帧df中提取*type of plot*的一般语法如下。

df.plot.*type of plot*(*relevant-necessary parameters*)

在下文中,我们将生成一些由 handy 方法支持的图,底线是在我们加载的数据帧data上执行 EDA。

箱线图

从上面的data.describe()生成的统计汇总中,我们知道Quantity列的数据范围比其他数值列大得多,所以这里我们只使用下面的一行代码在一个图中绘制UnitPriceUnitDiscountUnitWeight三个数值列的箱线图。我们用的方法是plot.box

标准箱线图

如果我们想让盒状图变成水平的,而不是上面的垂直的,该怎么办?此外,我们希望有一个额外的绘图网格来帮助我们更精确地看到绘图的重要部分,如中间值、异常值边界等。没问题,我们可以通过设置参数vert = Falsegrid = True来实现。

带网格的水平箱线图

根据上面的箱线图,我们知道虽然UnitWeight列不包含任何异常值,但其他两列确实包含许多大的异常值,对于UnitPriceUnitDiscount,阈值分别为 7.5 和 2.1 左右。

直方图

我们为Quantity列绘制一个直方图。通过plot.hist,代码很简单,有一个必需的参数来表示我们想要在直方图中包含的仓的数量(这里我们选择 20)。

数量列的直方图

注意,我们使用了自己命名的参数(title)在直方图的顶部生成标题。

相关矩阵

正如本文开头的总结部分所述,这个不是由plot方法生成的。相反,它是由数据帧的corr对象的子功能style.background_gradient产生的。

数字列的相关矩阵

在上面的代码中,我们设置了发散颜色贴图主题参数cmap=’coolwarm’(其他好的替代方案包括‘RdBu_r’‘BrBG’)。去给他们一个尝试吧!

散点图

根据上面的相关矩阵,我们知道UnitPriceUnitDiscount的相关性相当强(0.72)。因此,我们现在使用plot.scatter将它们绘制成散点图。注意,我们使用自己命名的参数color将颜色设置为sandybrown

单价和UnitDiscount之间的散点图

我们可以清楚地看到正相关。此外,UnitDiscount对于每个UnitPrice是不同的。为了说服自己UnitDiscount永远不会超过UnitPrice,我们在散点图的顶部叠加了一个 y = x(即,单价=折扣)的线图。为此,我们利用ax参数。

带附加线图的散点图

条形图

在下一张图中,我们将确定数据中销量最高的 10 种产品。因为我们还想知道产品描述,而不仅仅是产品代码,所以我们需要为产品描述和产品代码创建一个参考表。

为了获得整个数据中每种产品的销售总量,我们需要执行分组聚合。也就是说,我们按照产品代码(StockCode)对数据进行分组,然后对每组的Quantity求和。在熊猫身上,我们可以用groupby方法完成这类任务。一般形式如下。

df.groupby([*columns to be grouping base*]).*aggregating function*()

在我们的情况下,我们有

请注意,在上面的最后一行代码中,我们已经将聚合数量与其对应的产品描述进行了合并。最终结果(前五行)是如下所示的数据帧。

df_quant_sold.head()

现在我们已经准备好绘制条形图了!使用的方法是plot.bar,要设置的参数是xy

十大最畅销产品

线条图

在这里,我们想知道交易总收入的月度分布情况。为此,我们首先必须构建一个包含两列的 data frame:InvoiceMonthTransactionRevenue。与生成上面的条形图类似,我们执行 sum 聚合,按InvoiceMonth列分组。

df _ 月度 _ 收入()

此外,我们为总折扣创建了一个类似的数据框架,这样我们就可以理解这两个变量在每月期间的进展情况。

我们通过在plot.line中指定参数xy 绘制两个线图,在一个图中使用之前的ax参数。

月收入和折扣的折线图

我们看到,直到 11 月,这两个变量的趋势都是积极的。这种趋势在 11 月达到顶峰,收入和折扣的价值分别接近 90 万英镑和 25 万英镑。然而,在 12 月的最后一个月突然下降。

饼状图

我们可以深入研究的另一个观点是季度对全年收入的贡献。饼图非常适合这个用例。为了实现这一点,我们需要在创建上面的折线图时对交易收入(df_monthly_revenue)再执行一步分组聚合。这是三个月(一个季度)每月交易总额。

df _ 季度 _ 收入

从 plot 方法制作饼图相当简单。我们只需要将参数y设置为我们想要在plot.pie中绘制其值的列。

季度收入的标准饼图

我们发现传奇挡住了 Q1 的标签,这并不好。保持冷静,我们可以通过调整startangle参数(旋转饼图)来解决。此外,我们还可以利用autopct参数,通过提供每个饼图部分的百分比来增强细节。注意,我们也通过设置figsize 参数来放大下图中的图形尺寸。

调整了饼图。好多了!

我们看到,尽管 12 月份大幅下滑,但第四季度仍贡献了 2011 年全年近三分之一的收入。

堆积条形图

这个是我最喜欢的。我们将创建一个堆积条形图,以分析不同时段的交易收入金额的构成,形成每个季度的交易总数。通过拥有一个,我们可能能够了解低收入桶的交易数量是否随着时间的推移而增长,因此我们可以相应地调整我们的策略(例如,我们可能希望重新标记网站,使其看起来更具排他性,以瞄准更多优质买家)。

为了画出我们的,我们必须做几个准备步骤。第一个是将TransactionRevenue列成 bucketize(宁滨)列。我们将创建三个存储桶:低(低于 25 个百分点)、中(介于 25 和 75 个百分点之间)和高(高于 75 个百分点)。我发现pd.qcut是完成这项任务的便利工具。

接下来,我们创建三个(代表存储桶的数量)counter(二进制值)列,这将帮助我们执行下一步的分组聚合,以计算每个存储桶中的事务数量。

我们通过使用apply方法来做到这一点,这基本上是一种允许我们传递函数并应用于熊猫系列的每个值的功能。这里,我们想要应用的函数是一个简单的条件(if-else)赋值函数。

最后一个准备步骤包括分组聚合,随后是值规范化(使每个条形总和达到 100%),如下所示。

下面是我们准备绘制的数据帧的最终结果。

df _ percent(df _ quarterly _ revenue _ bucket 的规范化值)

最后,生成图的代码如下。通过在plot.bar内设置参数stacked = True实现条形图的堆叠形式。

每个季度的交易时段细分

除其他外,上图告诉我们,高价值交易的比例趋向于积极的趋势(即,在各个季度中增加),除了最后一个第四季度,每个季度中高价值交易的比例都有所增加。

结束语

在这篇文章中,我们已经能够使用 Pandas plot方法直接从我们的数据框架中生成各种各样的图,这些图对于进行典型的探索性数据分析(EDA)肯定是有用的。关于这一点,我需要说明的是,我们在这里生成的图的种类并不详尽代表了该方法支持的所有图(关于更完整的特性,请参考该方法的官方文档这里)。

话虽如此,但这并不意味着熊猫完全可以进行可视化。我们确实需要借助 Matplotlib 或其他绘图软件包来制作更精细的图,如蜂群图、小提琴图等。,以及调整我们在这里创建的情节中的高级细节(通过丰富的plt方法)。

但我相信,如果我们需要的大多数图表可以直接从我们的数据框架中产生,这仍然是很好的,这意味着我们可以减少执行数据分析和可视化的摩擦!

最后,感谢您的阅读,让我们在 https://www.linkedin.com/in/pararawendy-indarjo-5b87bab7通过 LinkedIn 与我联系。

使用 PySpark 在数据块上进行探索性数据分析(EDA)

原文:https://towardsdatascience.com/exploratory-data-analysis-eda-with-pyspark-on-databricks-e8d6529626b1?source=collection_archive---------9-----------------------

再见,熊猫们…

Unsplash 上的 chuttersnap 拍摄

EDA 加 spark 的意思是和熊猫说拜拜。由于数据规模大,每次计算都必须并行化,而不是 熊猫pyspark . SQL . functions才是你可以使用的合适工具。毫无疑问,它正在努力改变你争论数据的旧习惯。我希望这篇文章可以给你一个用 Spark 执行 EDA 的跳跃性开始。

有两种变量,连续变量和分类变量。每种技术都有不同的 EDA 要求:

连续变量 EDA 列表:

  • 缺少值
  • 统计值:平均值、最小值、最大值、标准差、分位数
  • 宁滨和分销
  • 相互关系

分类变量 EDA 列表:

  • 缺少值
  • 频率表

我还将展示如何在没有任何绘图库(如 seaborn 或 matplotlib)的情况下在 Databricks 上生成图表。

首先,让我们加载数据。我使用的数据来自一个 Kaggle 竞赛,桑坦德客户交易预测。

# It's always best to manually write the Schema, I am lazy heredf = (spark
  .read                                              
  .option("inferSchema","true")                 
  .option("header","true")                           
  .csv("/FileStore/tables/train.csv"))

连续变量的 EDA

内置函数 describe()非常有用。它计算选定变量的计数、平均值、标准偏差、最小值和最大值。例如:

df.select('var_0').describe().show()

但是,当您计算多个变量的统计值时,显示的数据框可能不便于检查,如下所示:

记得我们之前说过不要用熊猫来做计算。但是,我们仍然可以用它来显示结果。这里,spark 数据框架中内置的 describe()函数已经完成了统计值的计算。计算出的汇总表大小不大。所以我们可以用熊猫来展示它。

df.select('var_0','var_1','var_2','var_3','var_4','var_5','var_6','var_7','var_8','var_9','var_10','var_11','var_12','var_13','var_14').describe().toPandas()

获取分位数:

quantile = df.approxQuantile(['var_0'], [0.25, 0.5, 0.75], 0)
quantile_25 = quantile[0][0]
quantile_50 = quantile[0][1]
quantile_75 = quantile[0][2]
print('quantile_25: '+str(quantile_25))
print('quantile_50: '+str(quantile_50))
print('quantile_75: '+str(quantile_75))'''
quantile_25: 8.4537 
quantile_50: 10.5247 
quantile_75: 12.7582
'''

检查缺失:

引入两个函数来做过滤

# where
df.where(col("var_0").isNull()).count()
# filter
df.filter(col("var_0").isNull()).count()

这两个是一样的。根据 spark 文档,“where”是“filter”的别名。

对于连续变量,有时我们希望对它们进行分类,并检查这些分类的分布。例如,在金融相关数据中,我们可以将 FICO 分数(通常范围为 650 到 850)归入不同的类别。每个桶有 25 的间隔。比如 650–675,675–700,700–725,…然后检查每个桶里有多少人。

现在让我们用“var_0”来举一个宁滨的例子。根据以前的统计值,我们知道“var_0”的范围是从 0.41 到 20.31。所以我们创建一个 0 到 21 的列表,间隔为 0.5。

关联:

分类变量的 EDA

检查缺失值,和连续变量一样。

要查看频率表:

*freq_table = df.select(col("target").cast("string")).groupBy("target").count().toPandas()*

数据块上的可视化

Databricks 实际上提供了一个“类似 Tableau”的可视化解决方案。display()函数为您提供了一个友好的用户界面来生成您喜欢的任何图形。

例如:

选择所需的图表类型。

您也可以创建包含多个变量的图表。点击“绘图选项”按钮。您可以根据需要修改图:

如果喜欢讨论更多,在 LinkedIn 上找我。

探索性数据分析指南

原文:https://towardsdatascience.com/exploratory-data-analysis-guide-4f9367ab05e5?source=collection_archive---------25-----------------------

入门

关于如何组织和加速数据科学项目 EDA 的提示(Python)

探索性数据分析 (EDA)是熟悉数据,更好地理解数据的重要步骤。在这篇文章中,我将展示一种构建 EDA 工作流的方法,并在处理表格数据(只是从这里开始的数据)时,以一种更有组织、更高效的方式进行。

作者图片

尽管 EDA 会因项目和数据的不同而不同,但对于大多数项目来说,我们必须做一些基本的探索。考虑到这一点,我们可以将 EDA 分为两步:

步骤 1:基本探索:大多数数据通用

第二步:定制探索:特定于手边的数据

通过使用模板简化第一步,我们可以加快整个过程。

0.数据📦

如果你想跟随你电脑上的代码,确保你已经安装了pandaspandas _ profilingmatplotlibseaborn。helpers 模块中的函数定义可以在本文末尾的附录中找到。

让我们导入必要的包并更新默认设置:

# Import packages
import pandas as pd
from pandas_profiling import ProfileReport
import matplotlib.pyplot as plt
import seaborn as sns
import helpers # custom module# Update default settings
pd.options.display.float_format = '{:.4f}'.format
sns.set(style='darkgrid', context='talk', palette='Set1')

我们将使用从 titanic 数据集中选择的列作为示例:

# Import data
exclude = ['pclass', 'embarked', 'who', 'adult_male', 'alive', 
           'alone']
df = sns.load_dataset('titanic').drop(columns=exclude)# Inspect shape of the data and top rows
print(f"{df.shape[0]} rows, {df.shape[1]} columns")
df.head()

让我们定义目标和特性列。在代码的开头将 target 声明为变量,这样当目标名称从一个数据变为另一个数据时,更新代码会更容易。也就是说,您只需要在下面的一个地方更改代码,而不是在代码中的多个地方。

target = 'survived'
features = df.drop(columns=target).columns

在我们开始探索之前,让我们确保留出一些数据用于测试:

# Split data into train & test
X_train, X_test, y_train, y_test = helpers.partition(df, target)# Append target back using indices
train = pd.concat([X_train, y_train], axis=1)
test = pd.concat([X_test, y_test], axis=1)# Inspect training data
print(f"Training data ({train.shape[0]} rows): Target distribution")
display(helpers.find_frequency(y_train))print(f"Test data ({test.shape[0]} rows): Target distribution")
display(helpers.find_frequency(y_test))

我们已经准备好开始探索训练数据集了!🔎

第一步:基本探索🐇

对于基本探索,您可能还记得,我们指的是对大多数数据通用的任何探索。为了简化这一步骤,我们将利用自动化工具和定制功能来准备一个模板。因此,让我们将基本的探索步骤分成两个部分:

  • 第 1 部分:利用自动化工具
  • 第 2 部分:使用定制函数进行剩余的基本探索

模板背后的想法是,当我们有新的数据时,我们只需更新模板中的几个部分,就可以不费吹灰之力获得基本的探索。一旦基本探索完成,仔细分析所有图表和汇总统计数据,并创建关键发现的摘要是很重要的。这种分析将有助于形成探索的第二步。

第一部分。利用自动化工具

我们将使用熊猫概况包来自动化部分的基本探索。我们可以在中创建熊猫概况报告(从此处开始报告)。像这样的 html 文件:

# Create profile report
profile_train = ProfileReport(train)# Export it to html file
profile_train.to_file("training_data_report.html")

只需 2 行代码,我们就可以得到一个非常棒的报告,其中包含数据概述、描述性统计、单变量和一些双变量分布、相关性和缺失值汇总。输出报告将类似于这个。您会注意到,您可以使用右上角的选项卡跳转到各个部分,并通过点击切换按钮查看更多详细信息。

你可以用title改变出现在左上角的标题,用dark_mode 参数打开黑暗模式。例如,当创建一个报告时,将第一行扩展到下面将得到一个标题为“训练数据报告”的深色模式的报告(我觉得很好):

profile_train = ProfileReport(train, title="Training Data Report", 
                              dark_mode=True)

虽然我认为创建 html 报告是使用报告的最佳方式,但可以使用以下选项之一在 Jupyter Notebook 中访问它:

# Option 1
train.profile_report()# Option 2
profile = ProfileReport(train)
profile.to_widgets()# Option 3
profile = ProfileReport(train)
profile.to_notebook_iframe()

我建议尝试运行这些选项来找到您偏好。如果你想了解更多关于这个包的信息,请看他们的文档页面。

第二部分。使用自定义函数进行剩余的基本探索

像 Pandas Profiling 这样的自动化工具可能无法涵盖基本探索的所有部分。如果您发现自己在不同的项目中反复做相同的探索,并且它们没有包含在报告中,那么创建自定义函数或使用 pandas 内置函数或方法来迎合这种差距。

保持模板尽可能干净的一个关键技巧是将某些功能抽象成自定义函数,并保存在一个单独的模块中。这篇文章中的例子使用了自定义模块“帮助者”中的函数。每个函数的代码可以在这篇文章的末尾找到。

在这一节中,我们将看几个可以用来补充报告的示例探索。让我们从总结所有变量开始:

helpers.summarise(train)

虽然这个输出看起来像是报告显示内容的副本,但是并排查看所有变量的信息可能会很有用。从这个输出中,我们可以看到大约五分之四的记录中缺少 deck,大约五分之一的记录中缺少 age。让我们将特性分成两组:

# Define feature groups
continuous = train[features].select_dtypes(['float']).columns
discrete = train[features].columns.difference(continuous)# Convert to category dtype
train[discrete] = train[discrete].astype('category')

检查连续变量的汇总统计也很有用。以下函数扩展了 pandas DataFrame 的.describe()方法:

helpers.describe_more(train, continuous)

在这里,我们可以看到描述性统计和异常值的汇总。这里的异常值是用约翰·图基的规则定义的。12%的票价值是异常值。

可视化每个目标类的连续特征的分布可以让我们了解它们在区分目标类中的有用性:

for feature in continuous:
    helpers.plot_continuous(train, feature, target)

可以对离散值进行类似的汇总和可视化:

train.describe(exclude=['number'])

我们可以在前两行中看到非缺失值和唯一值的数量。在最后两行中,我们可以看到最频繁的值及其频率计数。

现在,让我们来想象一下离散的特征。通过使用“缺失”进行输入,我们可以看到,相对于要素中的其他类别,那些具有缺失值的记录更有可能或更少可能保留下来:

# Fill missing
for feature in discrete:
    if train[feature].isnull().sum()>0:
        train[feature] = train[feature].cat\
                                       .add_categories('missing')
        train[feature] = train[feature].fillna('missing')# Visualise
for feature in discrete:
    helpers.plot_discrete(train, feature, target)

在本节中,我们并排查看了特征的汇总统计数据,并可视化了特征与目标的关系,以获得关于它们在区分目标类别中的有用性的一些见解。我希望你将使用提到的一些或所有想法,并辅以你自己最喜欢的探索数据的方式,来创建你自己的基本探索模板。

一旦基本探索完成,仔细分析输出并创建关键发现的摘要是很重要的。这里有几个例子:

  • 三等舱的乘客要多得多,他们的存活率要低得多。
  • 男性乘客多得多,他们的存活率也低得多。

理解你试图用数据解决的问题的背景是很有价值的,可以帮助你感觉检查数据或解释发现。阅读问题或与相关的利益相关者谈论问题是获得理解的好方法。在泰坦尼克号的例子中,我们可以通过谷歌搜索信息来更好地理解这场灾难。读了一点书后,我们很快就会发现,在紧急情况下,有一个‘妇女和儿童优先’协议,它在决定谁更有可能幸存下来方面发挥了作用。以这种方式将数据置于背景中有助于理解数据探索的结果,并更好地理解问题。

第二步。习俗探索🐢

在完成基本探索并分析其结果后,下一步明智的做法是检查你的假设,并使用数据回答具体问题。在这里,您可以发挥创造力,对您的数据进行独特的探索。由于这一步的探索因数据而异,我们不会看很多例子。这里有一个例子,我们可以用数据来回答问题:

在每个班级中,女性和男性的存活率如何比较?

fig, ax = plt.subplots(1, 2, figsize=(11,3))
sns.heatmap(pd.crosstab(train['sex'], train['class']), 
            cmap='Greys', annot=True, fmt='g', ax=ax[0])
ax[0].set_title("Counts")
ax[0].set_yticklabels(ax[0].get_yticklabels(), rotation=0)sns.heatmap(train.pivot_table(target, 'sex', 'class'), 
            cmap='Greys', annot=True, fmt='.1f', ax=ax[1])
ax[1].set_title("Mean target")
ax[1].set_yticklabels(ax[1].get_yticklabels(), rotation=0)plt.tight_layout();

正如我们从热图中看到的,在每个班级内部和之间,女性比男性更有可能幸存下来。

虽然我们已经看了一个分类的例子,但是这个想法也可以适用于回归或者无监督学习的问题。

杰瑞米·托马斯Unsplash 上拍照

您想访问更多这样的内容吗?媒体会员可以无限制地访问媒体上的任何文章。如果您使用 我的推荐链接成为会员,您的一部分会费将直接用于支持我。

谢谢你看我的帖子。我希望这些例子将有助于创建您自己的模板,以加快和组织您的数据科学项目的探索性数据分析。

如果你感兴趣, 以下是我的一些帖子的链接:
◼️ 用这些技巧整理你的 Jupyter 笔记本
◼️ 用 Python 实现简单的数据可视化,你会发现有用的
◼️ 6 个简单的技巧让 Seaborn (Python)中的情节更漂亮、更定制化
◼️ 用 Python 进行探索性文本分析
◼️️ 给熊猫用户的 5 个技巧
◼️️ 关于熊猫中数据聚合的 5 个技巧

再见🏃💨

附录:helpers.py

图像分类的探索性数据分析思路

原文:https://towardsdatascience.com/exploratory-data-analysis-ideas-for-image-classification-d3fc6bbfb2d2?source=collection_archive---------14-----------------------

图像数据的可视化模式

疾控中心Unsplash 拍摄的照片

探索性数据分析包括描述数据集的简要分析,以指导建模过程并回答初步问题。对于分类问题,这可能包括查看变量的分布或检查不同类别中任何有意义的预测模式。图像数据的分类也存在同样的问题。我们打算寻找简单操作能给我们的有意义的信息。在这里,我概述了一些方法,我们可以使用胸部 x 光数据来实现这个目标[ 来源 ]。该数据集由肺炎患者和健康对照者的 X 射线图像组成。

原始比较

首先,我们可以从简单地查看一些随机采样的图像开始。

这一步将从每个子文件夹中随机抽取图像并显示出来。

图像作为矩阵

在接下来的几个步骤中,我们将直接处理每个图像的像素值,以便对它们进行操作。我们可以通过将图像转换成 Numpy 数组来实现这一点。

这个函数将遍历每个文件,并将它们转换成一个( n,m )矩阵,其中 n 是观察值的数量, m 是像素数。

平均图像

现在让我们看看每个类的平均图像是什么样的。为了计算平均图像,我们可以取所有观测中每个像素的平均值。

我们可以从平均图像中看到,肺炎 X 射线往往显示胸部周围有较高的阻塞。

平均图像之间的对比度

使用平均图像,我们也可以计算差异。

变化性

类似地,我们也可以通过计算方差或标准差而不是平均值来查看哪一个区域在这两类中变化最大。这里较亮的区域表示较高的可变性。我们可以再次看到,在肺炎 X 射线中,肺部有更多的可变性。

特征图像

最后,我们可以使用一种降维技术,如主成分分析(PCA ),来可视化描述每个类别的最佳成分。本质上是我们的图像矩阵的 PCA 的特征向量(分量)的特征图像可以被整形为矩阵并被绘制。它也被称为特征脸,因为这种方法最初用于面部识别研究。在这里,我们将把描述每一类 70%可变性的主要成分形象化。

您可以看到,与肺炎类相比,健康 X 射线图像的特征图像显示了更多关于胸腔和器官的边缘定义。

今天,我简要地展示了一些在简单的图像数据集中寻找模式的快速简单的方法。显然,这些方法在处理具有规则构图的图像时非常有用。除了上述方法之外,我们还可以查看鱼表面和像素间的相关矩阵,以便对图像数据进行探索性分析。

数据源

[## 标记的光学相干断层扫描(OCT)和胸部 X 射线图像的大型数据集

确保下载该数据集的最新版本,以保持准确性。该数据集包含数千个…

data.mendeley.com](https://data.mendeley.com/datasets/rscbjbr9sj/3)

用于数据建模的探索性数据分析

原文:https://towardsdatascience.com/exploratory-data-analysis-intro-for-data-modeling-8ff019362371?source=collection_archive---------45-----------------------

如何了解数据集、定义因变量和自变量、计算目标变量和预测变量之间的相关系数、数据转换

探索性数据分析(EDA)是一个广泛的话题。很难在一篇文章中涵盖这一点。探索性数据分析可用于了解数据以及数据集不同特征之间的关系。也可以通过了解要素来选择重要的要素并准备数据集,以便统计模型能够适合数据集。不管原因是什么,了解这些特性以及特性之间的关系是非常重要的。在本文中,我将重点介绍用于数据建模的探索性数据分析(EDA)。虽然我们不打算在这里做预测。我们将只关注 EDA 部分。

资料组

我使用的是 scikit-learn 库中已经存在的波士顿数据集。它包含有关波士顿房价的信息。首先,导入必要的包和数据集。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
from sklearn.datasets import load_boston
boston_data = load_boston()

作为参考,波士顿数据集分为两部分。一个是包含特性的数据部分,另一个包含房屋的价格。我们将价格作为要素包含在同一个数据集中。

df = pd.DataFrame(data=boston_data.data, columns=boston_data.feature_names)df["prices"] = boston_data.target

找出有多少特征和多少观察值:

df.shape#Output:
(506, 14)

数据集的形状表示,数据集中有 14 个要素和 506 个观测值。现在,列出数据集中的所有列或要素:

df.columns#Output:
Index(['CRIM', 'ZN', 'INDUS', 'CHAS', 'NOX', 'RM', 'AGE', 'DIS', 'RAD', 'TAX', 'PTRATIO', 'B', 'LSTAT', 'prices'], dtype='object')

这些功能的名称可能看起来晦涩难懂。以下是对这些功能的解释:

各城镇的人均犯罪率

ZN: 面积超过 25,000 平方英尺的住宅用地比例。脚

梧桐:每个城镇非零售商业亩数比例

CHAS: 查尔斯河虚拟变量(= 1,如果区域边界为河流;否则为 0)

NOX: 一氧化氮浓度,以每千万份为单位

RM: 平均房间数

楼龄:1940 年前建成的自住单位比例

DIS: 到五个波士顿就业中心的加权距离

RAD: 放射状公路可达性指标

税:每万美元的财产税税率

各城镇学生与教师的比例

B: 1000(Bk — 0.63)2,其中 Bk 是各城镇非裔美国人的比例

LSTAT: 处于较低地位的人口所占的百分比

价格:千美元住宅的价格

现在,我们对数据集有了更清楚的了解。在开始任何数据探索之前,检查缺失的数据是很重要的:

df.isnull().sum()

幸运的是,这个数据集中没有缺失值。所以,我们可以进一步分析。

因变量和自变量

在这个数据集中,我们需要找出因变量和自变量。如果你注意到数据集中,人们可能会有兴趣根据其他特征来预测房价。因为他们不想支付高于公平市场价值的价格。根据经验,我们可以预计,根据数据集中的其他要素,房价可能会有所不同。所以,在这个数据集中,房价是因变量

选择自变量或预测变量可能很棘手。它主要取决于变量与因变量的关系。我们可以从本能开始,然后测试它。我的直觉告诉我,这个数据集的预测变量可以是 RM、CRIM、DIS、Age 和 PTRATIO。如果我们想到细节:

RM(平均房间数)会影响平方英尺,影响房子的价格。与住房的关系预计将是积极的。这意味着如果房间数量越多,价格也越高。

犯罪率也可能影响价格。如果犯罪率更高,很可能房价更低。到五个波士顿就业中心(DIS)的加权距离也可能与房价负相关。师生比也可能与房价成负相关,因为孩子的教育对父母来说很重要。现在,检查一下我们的直觉是否正确。

探索性分析

从目标变量或因变量的分布开始:

sns.set(rc={'figure.figsize': (12, 8)})
sns.distplot(df["prices"], bins=25)
plt.show()

如上图所示,价格的分布几乎是正态的。在上分位数中有一些异常值。

一个非常重要的步骤是了解因变量和自变量之间的关系。预测变量之间的相关性对于选择正确的预测变量也很重要。以下是在数据集或部分数据集的所有要素之间绘制相关矩阵的方法。因为这个数据集不太大,所以我取整个数据集并绘制相关矩阵:

correlation_matrix = df.corr().round(2)sns.set(rc={'figure.figsize':(11, 8)})
sns.heatmap(data=correlation_matrix, annot=True)
plt.show()

来源:作者,相关矩阵

提醒你一下,相关系数的范围是-1 到 1。-1 表示有很强的负相关性,1 表示有很强的正相关性。如果相关系数为零,则没有相关性。

现在,从上面的相关矩阵中检查目标变量“价格”和我们选择的预测变量(RM、CRIM、DIS、AGE 和 PTRATIO)之间的相关性。

如图所示,价格与 RM 的相关系数为-0.7,为强正相关。价格和犯罪之间的相关系数是-0.39,这是一个显著的负相关。价格与 DIS、年龄和 PTRATIO 之间的相关系数分别为 0.25、-0.38 和-0.51,表明相关性非常显著。你可能认为除了 CHAS,所有的特征都与我们的因变量有很好的相关性,这是正确的。如果我用这个数据集写一个预测算法,我将只排除 CHAS,把其余的特征作为预测变量或自变量。

让我们进一步研究因变量和自变量之间的关系。这次我想看看个人关系。在本文中,我不打算检查所有预测变量与目标变量之间的关系。我根据 correlation_matrix 中的相关性选择两个预测变量。在这里,我采用了与“价格”有良好负相关关系的年龄和有良好正相关关系的 DIS。

要查看 RM 和 DIS 与目标变量之间的关系,散点图是最有帮助的。

plt.figure(figsize=(20, 5))features = ['RM', 'DIS']
target = df['prices']for i, col in enumerate(features):
    plt.subplot(1, len(features) , i+1)
    x = df[col]
    y = target
    plt.scatter(x, y, marker='o')
    plt.title(col)
    plt.xlabel(col)
    plt.ylabel('prices')

从上面的散点图中,我们可以看到年龄的分布是左偏的,DIS 的分布是右偏的。在年龄图中,上分位数有一个聚类,在 DIS 图中,下分位数有一个聚类。这种关系呈线性趋势。但如果我们想到的是线性模型,就要做一些数据操作,让关系成为更强的线性关系。当数据向左倾斜时,转换数据的一种方法是获取数据的立方体。当它向左倾斜时,用圆木会有帮助。有几种方法可以转换数据。我将在以后的文章中详细讨论这一点。

plt.figure(figsize=(20, 5))df['AgeN'] = df['AGE']**3
df['DisN'] = np.log(df['DIS'])features = ['AgeN', 'DisN']
target = df['prices']for i, col in enumerate(features):
    plt.subplot(1, len(features) , i+1)
    x = df[col]
    y = target
    plt.scatter(x, y, marker='o')
    plt.title(col)
    plt.xlabel(col)
    plt.ylabel('prices')

如果你注意到,集群消失了,线性更强了。这两个预测变量已准备好进行建模。请用其他预测变量进行练习。

从本文中,我们学习了如何检查空值、定义预测值和相关值、制作相关矩阵、转换数据以提高数据建模的质量。

更多阅读推荐:

如何在 Python 中呈现多个变量之间的关系

Python 中的基本线性回归算法,适合初学者

假设检验、特征和计算

掌握熊猫的分组情况,进行高效的数据汇总和分析

用 Python 中的单变量和多变量图表和绘图理解数据

用 Python 从零开始构建一个完整的神经网络

探索性数据分析是数据科学的重要组成部分

原文:https://towardsdatascience.com/exploratory-data-analysis-is-a-significant-part-of-data-science-7f3b173c04d2?source=collection_archive---------31-----------------------

数据科学,数据可视化

你将发现探索性数据分析 (EDA),你可以使用的技术和策略,以及为什么你应该在你的下一个问题中执行 EDA。

卢克·切瑟Unsplash 上拍摄的照片

D数据科学无所不在,以先进的统计机器学习方法。无论有多长时间的数据需要分析,调查的需求都是显而易见的。

然而,任何数据科学任务的一个重要关键部分是探索性数据分析,这一点往往被低估

“探索性数据分析是一种态度,一种灵活的状态,一种寻找我们认为不存在的东西以及我们认为存在的东西的意愿。”约翰·w·图基

构建与数据的关系

照片由 Dương HữuUnsplash 上拍摄

在你可以建立数据模型和测试你的假设之前,你必须建立与数据的关系。

您可以通过投入时间总结、绘制和调查来自该领域的真实数据来建立这种关系。这种建模前调查的方法论被称为探索性数据分析

在预先投入时间处理数据的过程中,您可以利用数据格式、值和关系来构建直觉,这有助于稍后阐明观察结果和建模结果。

它被称为探索性数据分析,因为你正在调查你对数据的理解,收集创造数据的潜在过程如何工作的本能,并激发你可以用作建模理由的问题和想法。

该过程可用于检查数据,识别异常值,并提出处理异常值的具体策略。在数据上投入时间,您可以发现值中的损坏,这可能标志着数据记录过程中的缺陷。

探索性数据分析的开始

西蒙·米加吉在 Unsplash 上的照片

E 探索性数据分析是贝尔实验室的约翰·图基发明的,作为一种在数据假设产生之前有效利用洞察工具解决问题的方法。

目标是理解问题以创建可测试的假设。考虑到所有因素,像图表和汇总统计数据这样的结果只是为了提高您的理解能力,而不是为了向普通观众展示数据中的关系。

探索性数据分析策略

照片由费利佩·费塔多Unsplash 拍摄

探索性数据分析通常使用代表性的数据样本进行。您不必利用所有的数据访问。

在原始数据上投入时间。

从目测数字表开始是明智的。浏览表可以快速显示每个数据属性的类型、明显的反常情况以及值中的大异常值,并开始提出候选关系来调查属性之间的关系。

可以使用简单的单变量多变量方法来透视数据。

例如,我认为必须具备的方法有:

  • 汇总(平均值、中值、最小值、最大值、q1、q3)
  • 直方图
  • 折线图
  • 盒须图
  • **散点图 **

除了摘要,另外,看看数据的转换和数据的重新缩放。

比如,问很多关于数据的问题

迈特·沃尔什Unsplash 上拍照

  • 你看到了什么价值观?
  • 你看到哪些发行版?
  • 你看到了哪些关系?
  • 你有什么样的数据,如何对待不同类型的数据?
  • 数据中缺失了什么,你会如何管理?
  • 离群值在哪里,出于什么原因,考虑它们对你来说是个好主意?
  • 您将如何添加、更改或删除功能以充分利用您的数据?

专心理解

米米·蒂安Unsplash 上拍照

你不是在写报告,而是在试图理解问题。

结果最终会被丢弃,留给你的应该是对数据更突出的解释和直觉,以及建模时要研究的一长串假设。

代码不应该是美丽的,但他们应该是正确的。利用可复制的脚本和标准包。

你不必跳入复杂的统计方法或图表。保持简单,花时间研究数据。

SQL 这样的查询接口可以帮助你用一个数据样本快速地处理很多假设场景。

模型可能与问题和你对数据和问题的理解不相上下。

管理缺失值的一些常用技术包括

  • 如果有过多的空值,我们可以设置一个阈值来删除整列。
  • 删除缺少值的实例。
  • 删除缺少值的属性。
  • 使用平均值、中值、所有缺失值的模式输入属性。
  • 用线性回归的方法输入属性缺失值。

回忆齐夫定律可以辅助离群值。不定期出现的接近尾部终点的值可能是异常。一种选择是尝试转型。

平方根和对数转换都需要大量的数字。如果异常值是因变量,这可以使假设更好地工作,如果异常值是自变量,这可以减少单个点的影响。

结论

凯利·西克玛在 Unsplash 上的照片

在任何数据分析中,数据挖掘都是发现数据模式的关键步骤。它为您的整体分析过程奠定了坚实的基础。图基经常将 EDA 比作侦探工作。

数据分析师或数据科学家的角色是从尽可能多的角度关注数据,直到一个可以想象的数据故事变得显而易见,而不管这样的描述是否会在下面的示例中得到证实。

现在,把你的想法放在TwitterLinkedin,以及Github!!**

同意 还是 不同意 与 Saurav Singla 的观点和例子?想告诉我们你的故事吗?

他乐于接受建设性的反馈——如果您对此分析有后续想法,请在下面的 评论 或联系我们!!

推文@ SauravSingla _ 08Saurav _ Singla**,还有明星SauravSingla马上!**

IPL 比赛的探索性数据分析-第一部分

原文:https://towardsdatascience.com/exploratory-data-analysis-of-ipl-matches-part-1-c3555b15edbb?source=collection_archive---------5-----------------------

数据科学

对印度超级联赛(2008–2019)的有趣见解

约根德拉·辛格在 Unsplash 上的照片

T he 数据集由 2008 年至 2019 年的 IPL 比赛数据组成。IPL 是由印度板球管理委员会(BCCI)于 2008 年创立的职业板球联赛。该联盟有 8 支球队,代表 8 个不同的印度城市或邦。它享有巨大的知名度,2019 年 IPL 的品牌价值估计为 10 亿₹475(67 亿美元)。所以我们通过统计来分析一下 IPL。

目标:

  • 找出在一个赛季中赢得最多比赛的球队。
  • 找出一个赛季中输掉最多比赛的球队。
  • 掷硬币赢了会增加获胜的机会吗?
  • 找出比赛中获奖最多的球员。
  • 找到举办最多 IPL 比赛的城市。
  • 找出每一季获胜最多的队伍。
  • 找出 IPL 比赛次数最多的场上裁判。
  • 寻找 IPL 中最大的胜利,同时捍卫总,同时追逐总。

要对探索性数据分析(EDA)有一个详细的了解,我推荐你访问我的博客:

[## 哈伯曼生存数据集探索性数据分析综合指南

一个详尽和广泛的方法,处理探索性数据分析的每个方面。

towardsdatascience.com](/comprehensive-guide-to-exploratory-data-analysis-of-habermans-survival-data-set-b33f0373c83a)

数据准备和清理

让我们从将 csv 文件读入 Pandas DataFrame 开始。

import pandas as pd
ipl_matches_df = pd.read_csv('matches.csv')

所以有 756 行和 18 列。756 行意味着 2008 年至 2019 年期间举行了 756 场 IPL 比赛。

观察结果:

从 describe()方法可以得出以下推论:

  • 的。csv 文件包含从 2008 赛季到 2019 赛季的 IPL 比赛数据。
  • 第一棒球队(win_by_runs)的最大胜率是 146 分。
  • 第二棒球队(win_by_wickets)的最大胜场是 10 个 wickets
  • 75%的 bat 第一次获胜的球队以 19 分的优势获胜
  • 第二棒的获胜队中有 75%以 6 个门的优势获胜。
  • 从 2008 年到 2019 年举办了 756 场 IPL 比赛。

让我们查看每一列的唯一值,以帮助我们更好地理解数据集。

数据集有 18 列。让我们熟悉一下柱子。

  • id:IPL 比赛 id。
  • 赛季:IPL 赛季
  • 城市:IPL 比赛举办城市。
  • 日期:比赛举行的日期。
  • team 1:IPL 比赛的队伍之一
  • team 2:IPL 比赛的另一支队伍
  • 掷币 _ 赢家:掷币获胜的队伍
  • 掷硬币决定:赢得掷“球棒”或“场地”的队所做的决定
  • 结果:比赛结果(“正常”、“平局”、“无结果”)。
  • dl_applied : (1 或 0)表示是否应用了杜克沃斯-路易斯规则。
  • 获胜者:这场比赛的获胜者。
  • win_by_runs :提供第一个击球的队获胜的分数
  • win_by_runs :提供第二棒球队获胜的三柱门数量。
  • 这场比赛的杰出球员。
  • 场地:比赛举办的场地。
  • 裁判员:主持比赛的两名场上裁判员之一。
  • 裁判员:主持比赛的两名场上裁判员之一。
  • 裁判员:主持比赛的场外裁判

只要浏览一下数据帧的各个列,就可以看出“umpire3”列中存在 NaN 值。让我们看看列中 NaN 值的计数。

在我们继续下一步之前,必须知道每一列的 NaN 值的计数。

列“umpire3”有大量 NaN 值。由于场外裁判参数无关紧要,我们可以删除此列。其他具有 Nan 值的列属于 object 类型(Pandas 相当于 Python String 数据类型),数量很少(< =7)。

ipl_matches_df = ipl_matches_df.drop(columns=['umpire3'], axis=1)

探索性分析和可视化

既然我们的数据集已经清理完毕,是时候进行深入的分析和可视化了。

让我们从导入matplotlib.pyplotseaborn开始。

import seaborn as sns
import matplotlib
import matplotlib.pyplot as plt
%matplotlib inlinesns.color_palette("Paired")
matplotlib.rcParams['font.size'] = 14
matplotlib.rcParams['figure.figsize'] = (12, 8)
matplotlib.rcParams['figure.facecolor'] = '#00000000'

要了解更多关于 matplotlib.rcParams 的信息,请访问我的博客:

[## 让《朱庇特笔记本》的情节变得更美丽、更有意义

自定义 matplotlib.pyplot 以提高绘图质量

towardsdatascience.com](/making-plots-in-jupyter-notebook-beautiful-more-meaningful-23c8a35c0d5d)

每个赛季获胜次数最多的球队。

每个 IPL 赛季都有相当多的兴奋和奉承。除了钦奈超级国王队(2010 年和 2011 年),没有一支球队能够连续赢得 IPL 奖杯。这证明了 IPL 的不可预测性。

我们来分析一下每个赛季赢球最多的球队。

获胜次数是一个离散值。因此,我们将绘制一个柱状图(Seaborn 的柱状图)。

从柱状图中,我们可以很容易地推断出某个特定团队在哪一年赢得了最多的胜利(以及胜利的次数)。

观察结果:

  • 孟买印度人在四个赛季(2010 年、2013 年、2017 年和 2019 年)中获得了最多的胜利。

举办最多比赛的场馆

观察结果:

  • 伊甸园花园举办了最多的 IPL 比赛,其次是万克赫德体育场和 M Chinnaswamy 体育场
  • 到 2019 年,40 个场馆举办了 IPL 比赛

最成功的 IPL 团队

在体育比赛中,每个队都为胜利而竞争。因此,获得最多胜利的队伍是最成功的。

观察结果:

  • 孟买印度人队是最成功的球队(因为他们赢得了最多的 IPL 比赛——109 场),其次是钦奈超级国王队和加尔各答骑士队

最有价值球员

在竞争激烈的联赛中,获胜是最重要的。如果一名球员对他的球队的胜利做出了最大的贡献,那么他将被选为这场比赛的最佳球员。在大多数情况下赢得最佳球员称号的球员是最有价值的球员。

观察结果:

  • 克里斯·盖尔是赢得比赛奖项最多的球员,因此也是最有价值球员
  • 六名印度选手进入了前十名 IPL 选手名单。

掷硬币次数最多的队获胜

观察结果:

  • 孟买印度人赢得了 IPL 历史上最多的投掷(直到 2019 年)
  • 所有 IPL 的顶级球队都成功地赢得了比赛。

提问和回答问题

让我们试着找到那些萦绕在 IPL 球迷脑海中的问题的答案。在第一部分,我们正在处理一些非常基本的问题。在第二部分中,我们将深入探讨。

Q1:球队中最有价值球员的出现确保了 IPL 奖杯吗?

我们用“最佳球员”的头衔作为衡量标准,选出了 IPL 的前十名球员。有趣的是,前两名球员克里斯·盖尔和 AB de Villers 从未赢得过 IPL。前十名选手中,有 6 名选手(RG Sharma、MS Dhoni、DA Warner、SR Watson、SK 刘冰和 G Gambhir)获得了 IPL 。这说明了球队中最有价值球员的重要性。

Q2:哪位裁判执法过最多的 IPL 比赛?

S. Ravi(Sundaram Ravi)执法了最多的 IPL 比赛,其次是前斯里兰卡国际板球运动员 HDPK Dharmasena。

Q3:哪支队伍是 IPL 中最成功的队伍?

没有比成功更成功的了。在板球比赛中,获胜就是一切。我们已经缩小了每个赛季获胜次数最多的球队名单。数据帧 win_per_season_df 给出了所需的信息。孟买印度人在四个赛季(2010 年、2013 年、2017 年和 2019 年)中获得了最多的胜利。孟买印度人在 2013 年、2015 年、2017 年和 2019 年赢得了 IPL 奖杯。

孟买印度人在 IPL 中获得了最多的胜利(109),其次是钦奈超级国王队。

在截至 2019 年的所有 IPL 比赛中,孟买印度人在最多的比赛中获胜。他们在四个赛季中赢得了最多的胜利,迄今为止已经赢得了四个 IPL 奖杯。因此孟买印度队是 IPL 中最成功的队伍。

哪个城市举办了最多的 IPL 比赛?

孟买举办了最多的 IPL 比赛

Q5:掷硬币赢了有什么好处吗?

该队在掷硬币时获胜的概率是 52% 。所以掷硬币赢了会比对手有一点优势。然而,如果说掷硬币赢了是更大的优势,那就太天真了,因为有 363 次掷硬币输了的队赢得了比赛。

问 IPL 的主要胜利是什么?IPL 中防守好还是追防好?

  • 在 2008 年至 2019 年的 756 场 IPL 比赛中, 419 场比赛是在追逐目标中获胜的。因此,追逐总成绩(击球秒)的队伍比捍卫总成绩的队伍取得了更多的胜利。
  • 卫冕总的时候,最大的胜利是靠 146 分。让我们找出那个特定的匹配。

2017 年 5 月 6 日,在德里 Feroz Shah Kotla 体育场,孟买印度人以 146 分的成绩击败了德里夜魔侠,这是 IPL 在捍卫总比分方面取得的最大胜利。

  • 在 2008 年至 2019 年的 756 场 IPL 比赛中,赢得了 350 场比赛,捍卫了总数
  • 当追逐一个目标时,最大的胜利是靠 10 个小门(没有损失任何一个小门)。有 11 个这样的实例,我们可以通过下面的代码片段找到这些匹配的详细信息:

推论和结论

让我们总结一下我们在探索性数据分析过程中得出的重要观察结果:

  • 孟买印度人是 IPL 最成功的球队
  • 孟买的印度人赢得了最多数量的投掷
  • 追总(419 场)赢的比赛比防守(350 场)多
  • 卫冕总时,最大的胜利是 146 分(2017 年 5 月 6 日在德里 Feroz Shah Kotla 体育场,孟买印度人以 146 分击败德里夜魔侠)
  • 追逐一个目标时,最大的胜利是通过 10 个小门(没有损失任何一个小门),这样的例子有 11 个
  • 孟买是举办过最多 IPL 比赛的城市。
  • 克里斯·格利获得了比赛冠军的最大玩家数量。
  • 掷硬币赢给对手一点优势(52%的获胜概率)
  • 五名印度选手进入了前十名 IPL 选手名单。
  • s·拉维(Sundaram Ravi)执法了最多的 IPL 比赛
  • 伊甸园花园举办了最多的 IPL 比赛
  • 截至 2019 年,40 个场馆举办了 756 场 IPL 比赛

Kaggle 数据集的探索性数据分析。

原文:https://towardsdatascience.com/exploratory-data-analysis-of-kaggle-datasets-9a293886f644?source=collection_archive---------20-----------------------

弗兰基·查马基在 Unsplash 上拍摄的照片

简介:

探索性数据分析或 EDA 指的是更多地了解手头数据并为建模做准备的过程。坦率地说,EDA 和特征工程是一门艺术,你可以在预测过程之前摆弄数据并试图从中获得洞察力。大多数人认为机器学习只是关于模型和算法。但要让这些模型以高精度良好运行,EDA 是必不可少的。EDA 提供了很多非常容易被忽略的重要信息,这些信息从长远来看有助于分析。

背景:

关于如何执行 EDA,没有硬性规定。处理数据的每个人都必须找到自己执行 EDA 的方式,并相应地理解数据。在这篇文章中,我将解释我在 Kaggle 的房价数据集上做 EDA 的一步一步的方法。涉及的基本步骤是:

  1. 导入数据集。
  2. 获得基本的洞察力。
  3. 分析不同的特征并将它们分成数字和类别。
  4. 处理缺失值。
  5. 处理相关特征。

最后,我将使用名为“Sweetviz”的库实现一种现代的 EDA 方法,我认为这可能会帮助我们节省大量时间和精力。

相反,我不会在文章中显示代码,我在这里给出了整个代码 的 链接。请浏览一遍以跟上解释。

正在加载数据集:

如上所述,我将使用 Kaggle 的房价数据集,其链接在这里给出

我们将把训练和测试数据集分别加载到 Pandas 数据帧中。

初步分析:

包含训练和测试数据的数据帧将会。我们将尝试获得关于整个数据的一些基本见解。

图一。列车数据帧

图二。测试数据帧

数据集附带了对这些功能的详细描述。获得简要信息。

<class 'pandas.core.frame.DataFrame'>
Int64Index: 1460 entries, 1 to 1460
Data columns (total 80 columns):
 #   Column         Non-Null Count  Dtype  
---  ------         --------------  -----  
 0   MSSubClass     1460 non-null   int64  
 1   MSZoning       1460 non-null   object 
 2   LotFrontage    1201 non-null   float64
 3   LotArea        1460 non-null   int64  
 4   Street         1460 non-null   object 
 5   Alley          91 non-null     object 
 6   LotShape       1460 non-null   object 
 7   LandContour    1460 non-null   object 
 8   Utilities      1460 non-null   object 
 9   LotConfig      1460 non-null   object 
 10  LandSlope      1460 non-null   object 
 11  Neighborhood   1460 non-null   object 
 12  Condition1     1460 non-null   object 
 13  Condition2     1460 non-null   object 
 14  BldgType       1460 non-null   object 
 15  HouseStyle     1460 non-null   object 
 16  OverallQual    1460 non-null   int64  
 17  OverallCond    1460 non-null   int64  
 18  YearBuilt      1460 non-null   int64  
 19  YearRemodAdd   1460 non-null   int64  
 20  RoofStyle      1460 non-null   object 
 21  RoofMatl       1460 non-null   object 
 22  Exterior1st    1460 non-null   object 
 23  Exterior2nd    1460 non-null   object 
 24  MasVnrType     1452 non-null   object 
 25  MasVnrArea     1452 non-null   float64
 26  ExterQual      1460 non-null   object 
 27  ExterCond      1460 non-null   object 
 28  Foundation     1460 non-null   object 
 29  BsmtQual       1423 non-null   object 
 30  BsmtCond       1423 non-null   object 
 31  BsmtExposure   1422 non-null   object 
 32  BsmtFinType1   1423 non-null   object 
 33  BsmtFinSF1     1460 non-null   int64  
 34  BsmtFinType2   1422 non-null   object 
 35  BsmtFinSF2     1460 non-null   int64  
 36  BsmtUnfSF      1460 non-null   int64  
 37  TotalBsmtSF    1460 non-null   int64  
 38  Heating        1460 non-null   object 
 39  HeatingQC      1460 non-null   object 
 40  CentralAir     1460 non-null   object 
 41  Electrical     1459 non-null   object 
 42  1stFlrSF       1460 non-null   int64  
 43  2ndFlrSF       1460 non-null   int64  
 44  LowQualFinSF   1460 non-null   int64  
 45  GrLivArea      1460 non-null   int64  
 46  BsmtFullBath   1460 non-null   int64  
 47  BsmtHalfBath   1460 non-null   int64  
 48  FullBath       1460 non-null   int64  
 49  HalfBath       1460 non-null   int64  
 50  BedroomAbvGr   1460 non-null   int64  
 51  KitchenAbvGr   1460 non-null   int64  
 52  KitchenQual    1460 non-null   object 
 53  TotRmsAbvGrd   1460 non-null   int64  
 54  Functional     1460 non-null   object 
 55  Fireplaces     1460 non-null   int64  
 56  FireplaceQu    770 non-null    object 
 57  GarageType     1379 non-null   object 
 58  GarageYrBlt    1379 non-null   float64
 59  GarageFinish   1379 non-null   object 
 60  GarageCars     1460 non-null   int64  
 61  GarageArea     1460 non-null   int64  
 62  GarageQual     1379 non-null   object 
 63  GarageCond     1379 non-null   object 
 64  PavedDrive     1460 non-null   object 
 65  WoodDeckSF     1460 non-null   int64  
 66  OpenPorchSF    1460 non-null   int64  
 67  EnclosedPorch  1460 non-null   int64  
 68  3SsnPorch      1460 non-null   int64  
 69  ScreenPorch    1460 non-null   int64  
 70  PoolArea       1460 non-null   int64  
 71  PoolQC         7 non-null      object 
 72  Fence          281 non-null    object 
 73  MiscFeature    54 non-null     object 
 74  MiscVal        1460 non-null   int64  
 75  MoSold         1460 non-null   int64  
 76  YrSold         1460 non-null   int64  
 77  SaleType       1460 non-null   object 
 78  SaleCondition  1460 non-null   object 
 79  SalePrice      1460 non-null   int64  
dtypes: float64(3), int64(34), object(43)
memory usage: 923.9+ KB<class 'pandas.core.frame.DataFrame'>
Int64Index: 1459 entries, 1461 to 2919
Data columns (total 79 columns):
 #   Column         Non-Null Count  Dtype  
---  ------         --------------  -----  
 0   MSSubClass     1459 non-null   int64  
 1   MSZoning       1455 non-null   object 
 2   LotFrontage    1232 non-null   float64
 3   LotArea        1459 non-null   int64  
 4   Street         1459 non-null   object 
 5   Alley          107 non-null    object 
 6   LotShape       1459 non-null   object 
 7   LandContour    1459 non-null   object 
 8   Utilities      1457 non-null   object 
 9   LotConfig      1459 non-null   object 
 10  LandSlope      1459 non-null   object 
 11  Neighborhood   1459 non-null   object 
 12  Condition1     1459 non-null   object 
 13  Condition2     1459 non-null   object 
 14  BldgType       1459 non-null   object 
 15  HouseStyle     1459 non-null   object 
 16  OverallQual    1459 non-null   int64  
 17  OverallCond    1459 non-null   int64  
 18  YearBuilt      1459 non-null   int64  
 19  YearRemodAdd   1459 non-null   int64  
 20  RoofStyle      1459 non-null   object 
 21  RoofMatl       1459 non-null   object 
 22  Exterior1st    1458 non-null   object 
 23  Exterior2nd    1458 non-null   object 
 24  MasVnrType     1443 non-null   object 
 25  MasVnrArea     1444 non-null   float64
 26  ExterQual      1459 non-null   object 
 27  ExterCond      1459 non-null   object 
 28  Foundation     1459 non-null   object 
 29  BsmtQual       1415 non-null   object 
 30  BsmtCond       1414 non-null   object 
 31  BsmtExposure   1415 non-null   object 
 32  BsmtFinType1   1417 non-null   object 
 33  BsmtFinSF1     1458 non-null   float64
 34  BsmtFinType2   1417 non-null   object 
 35  BsmtFinSF2     1458 non-null   float64
 36  BsmtUnfSF      1458 non-null   float64
 37  TotalBsmtSF    1458 non-null   float64
 38  Heating        1459 non-null   object 
 39  HeatingQC      1459 non-null   object 
 40  CentralAir     1459 non-null   object 
 41  Electrical     1459 non-null   object 
 42  1stFlrSF       1459 non-null   int64  
 43  2ndFlrSF       1459 non-null   int64  
 44  LowQualFinSF   1459 non-null   int64  
 45  GrLivArea      1459 non-null   int64  
 46  BsmtFullBath   1457 non-null   float64
 47  BsmtHalfBath   1457 non-null   float64
 48  FullBath       1459 non-null   int64  
 49  HalfBath       1459 non-null   int64  
 50  BedroomAbvGr   1459 non-null   int64  
 51  KitchenAbvGr   1459 non-null   int64  
 52  KitchenQual    1458 non-null   object 
 53  TotRmsAbvGrd   1459 non-null   int64  
 54  Functional     1457 non-null   object 
 55  Fireplaces     1459 non-null   int64  
 56  FireplaceQu    729 non-null    object 
 57  GarageType     1383 non-null   object 
 58  GarageYrBlt    1381 non-null   float64
 59  GarageFinish   1381 non-null   object 
 60  GarageCars     1458 non-null   float64
 61  GarageArea     1458 non-null   float64
 62  GarageQual     1381 non-null   object 
 63  GarageCond     1381 non-null   object 
 64  PavedDrive     1459 non-null   object 
 65  WoodDeckSF     1459 non-null   int64  
 66  OpenPorchSF    1459 non-null   int64  
 67  EnclosedPorch  1459 non-null   int64  
 68  3SsnPorch      1459 non-null   int64  
 69  ScreenPorch    1459 non-null   int64  
 70  PoolArea       1459 non-null   int64  
 71  PoolQC         3 non-null      object 
 72  Fence          290 non-null    object 
 73  MiscFeature    51 non-null     object 
 74  MiscVal        1459 non-null   int64  
 75  MoSold         1459 non-null   int64  
 76  YrSold         1459 non-null   int64  
 77  SaleType       1458 non-null   object 
 78  SaleCondition  1459 non-null   object 
dtypes: float64(11), int64(25), object(43)
memory usage: 911.9+ KB(1460, 80)(1459, 79)

我们可以看到,训练数据集总共由 80 个特征组成,包括目标变量 SalePrice 和 1460 个训练示例。测试或预测数据集由 79 个特征(要预测的销售价格)和 1459 个数据点组成。

检查缺少的值:

任何数据集都会在其特征中包含某些缺失值,无论是数值特征还是分类特征。发生这种情况的原因有很多,如数据不可用、数据输入错误等。我们需要检查训练数据集中缺失的值。下面的可视化可以帮助我们做到这一点。

图三。缺少值

我们可以看到“PoolQC”和其他一些特性丢失了大约 90%的数据。我们稍后会处理它们。

数据可视化:

特征类型:

主要有两种类型的列,数字列和分类列。让我们将 train_df 中的特性分成每一个。

数字特征是:

Index(['MSSubClass', 'LotFrontage', 'LotArea', 'OverallQual', 'OverallCond',
       'YearBuilt', 'YearRemodAdd', 'MasVnrArea', 'BsmtFinSF1', 'BsmtFinSF2',
       'BsmtUnfSF', 'TotalBsmtSF', '1stFlrSF', '2ndFlrSF', 'LowQualFinSF',
       'GrLivArea', 'BsmtFullBath', 'BsmtHalfBath', 'FullBath', 'HalfBath',
       'BedroomAbvGr', 'KitchenAbvGr', 'TotRmsAbvGrd', 'Fireplaces',
       'GarageYrBlt', 'GarageCars', 'GarageArea', 'WoodDeckSF', 'OpenPorchSF',
       'EnclosedPorch', '3SsnPorch', 'ScreenPorch', 'PoolArea', 'MiscVal',
       'MoSold', 'YrSold'],
      dtype='object')

分类特征是:

Index(['MSZoning', 'Street', 'Alley', 'LotShape', 'LandContour', 'Utilities',
       'LotConfig', 'LandSlope', 'Neighborhood', 'Condition1', 'Condition2',
       'BldgType', 'HouseStyle', 'RoofStyle', 'RoofMatl', 'Exterior1st',
       'Exterior2nd', 'MasVnrType', 'ExterQual', 'ExterCond', 'Foundation',
       'BsmtQual', 'BsmtCond', 'BsmtExposure', 'BsmtFinType1', 'BsmtFinType2',
       'Heating', 'HeatingQC', 'CentralAir', 'Electrical', 'KitchenQual',
       'Functional', 'FireplaceQu', 'GarageType', 'GarageFinish', 'GarageQual',
       'GarageCond', 'PavedDrive', 'PoolQC', 'Fence', 'MiscFeature',
       'SaleType', 'SaleCondition'],
      dtype='object')

特征分布:

让我们看看数字特征中的数据是如何分布的。distplot 给出了每个变量的单变量分布图,如下所示。这些图还有助于我们了解特征的偏斜度和异常值(如果有)。

图 4。数字特征分布图

单变量分析:

我们不能只根据分布图来决定异常值。箱线图有助于我们了解更多关于特征中异常值的信息。蓝框外的点描述了作为异常值的数据点。

图五。数字特征的单变量分析

双变量分析:

我们还可以绘制特征和目标变量,以进行双变量分析。散点图可以达到目的。

图六。数字特征的二元分析

所有这些图可用于检测异常值,并更好地了解特征的分布及其与目标变量的关系。

数据处理:

移除异常值:

通过仔细观察上述图,可以得出以下特征存在异常值的结论:

  1. 停车场正面
  2. LotArea
  3. 马斯夫纳雷亚
  4. BsmtFinSF1
  5. BsmtFinSF2
  6. 总计 BsmtSF
  7. 1stFlrSF
  8. 低质量 FinSF
  9. 格里瓦雷亚
  10. WoodDeckSF
  11. 封闭的港口
  12. 3SsnPorch
  13. 纱窗门廊
  14. 游泳池区
  15. 误算

让我们通过绘制回归图来进一步了解这些特征。

图七。具有异常值的要素的回归图

我们现在可以确认这些要素存在异常值。下一步将是删除它们。我们可以获得从箱线图和回归图中移除它们(从各自的特征中)的极限。

移除不起作用的特征:

数据集中可能存在对目标变量贡献不大的特征。我们需要移除这些以获得更好的准确性。彼此高度相关的多个特征可能导致过度拟合。我们来找出高度相关的特征(相关性> 0.8)。这可以通过包含特征相关值的热图来完成。

图八。具有特征相关值的热图

从图中,我们可以看到以下特征彼此高度相关:

  1. 1stFlrSF & TotalBsmtSF
  2. TotRmsAbvGrd & GrdLivArea
  3. GarageYrBlt & YearBuilt
  4. 车库区域和车库汽车

删除这四个集合中的任何一个特征就足够了。我们可以通过观察它对目标变量 SalePrice 的贡献来决定删除哪一个。这可以通过检查其与 SalePrice 的相关性来实现。

SalePrice        1.000000
OverallQual      0.801493
GrLivArea        0.718996
GarageCars       0.647613
TotalBsmtSF      0.644541
GarageArea       0.634446
1stFlrSF         0.622300
FullBath         0.562614
YearBuilt        0.552223
YearRemodAdd     0.528484
TotRmsAbvGrd     0.524650
GarageYrBlt      0.508125
MasVnrArea       0.493222
Fireplaces       0.464076
BsmtFinSF1       0.392553
LotFrontage      0.360988
LotArea          0.332449
WoodDeckSF       0.329587
OpenPorchSF      0.322831
2ndFlrSF         0.296424
HalfBath         0.289755
BsmtUnfSF        0.230026
BsmtFullBath     0.220147
BedroomAbvGr     0.161515
ScreenPorch      0.069728
MoSold           0.051067
3SsnPorch        0.038210
BsmtHalfBath    -0.014339
YrSold          -0.023857
BsmtFinSF2      -0.038292
MiscVal         -0.048533
LowQualFinSF    -0.064357
OverallCond     -0.081193
MSSubClass      -0.085814
EnclosedPorch   -0.134095
KitchenAbvGr    -0.139927
PoolArea              NaN
Name: SalePrice, dtype: float64

我们将删除与目标变量相关性较低的特征(在上面显示的对中)。

我们之前已经检查并可视化了特征中缺失值的数量。我们需要删除缺失值超过 90%的特征。

PoolQC         1.000000
MiscFeature    0.966387
Alley          0.936975
Fence          0.809524
FireplaceQu    0.479692
dtype: float64

正如我们在 PoolQC、MiscFeature、Alley 中所看到的,缺失值超过 90%。所以我们应该去掉这些特征。

我们可以再次绘制散点图,看看是否有更多无用的特征。

图 9。去除异常值后的散点图

正如我们所看到的,PoolArea 特性并不重要,因为它将所有培训示例的 pool area 报告为零,因此我们也可以放弃它。

填充数字缺失值:

现在,我们必须处理具有缺失值的数字特征。

LotFrontage    0.175070
MasVnrArea     0.005602
SalePrice      0.000000
YrSold         0.000000
LotArea        0.000000
dtype: float64

我们可以看到正面和背面的特征缺少特征。我们可以使用 sklearn 内置的信誉函数用它们的平均值替换它们。

更换后,我们可以再次检查数字特征中的任何缺失值:

SalePrice        0.0
YrSold           0.0
LotFrontage      0.0
LotArea          0.0
OverallQual      0.0
OverallCond      0.0
YearBuilt        0.0
YearRemodAdd     0.0
MasVnrArea       0.0
BsmtFinSF1       0.0
BsmtFinSF2       0.0
BsmtUnfSF        0.0
TotalBsmtSF      0.0
2ndFlrSF         0.0
LowQualFinSF     0.0
GrLivArea        0.0
BsmtFullBath     0.0
BsmtHalfBath     0.0
FullBath         0.0
HalfBath         0.0
BedroomAbvGr     0.0
KitchenAbvGr     0.0
Fireplaces       0.0
GarageCars       0.0
WoodDeckSF       0.0
OpenPorchSF      0.0
EnclosedPorch    0.0
3SsnPorch        0.0
ScreenPorch      0.0
MiscVal          0.0
MoSold           0.0
MSSubClass       0.0
dtype: float64

正如我们所看到的,我们已经处理了训练数据中数字特征的所有缺失值。检查测试数据

LotFrontage      0.155586
MasVnrArea       0.010281
BsmtFullBath     0.001371
BsmtHalfBath     0.001371
GarageCars       0.000685
BsmtFinSF1       0.000685
BsmtFinSF2       0.000685
BsmtUnfSF        0.000685
TotalBsmtSF      0.000685
LowQualFinSF     0.000000
LotArea          0.000000
OverallQual      0.000000
OverallCond      0.000000
YearBuilt        0.000000
YearRemodAdd     0.000000
2ndFlrSF         0.000000
YrSold           0.000000
GrLivArea        0.000000
MoSold           0.000000
FullBath         0.000000
HalfBath         0.000000
BedroomAbvGr     0.000000
KitchenAbvGr     0.000000
Fireplaces       0.000000
WoodDeckSF       0.000000
OpenPorchSF      0.000000
EnclosedPorch    0.000000
3SsnPorch        0.000000
ScreenPorch      0.000000
MiscVal          0.000000
MSSubClass       0.000000
dtype: float64

大约有九个要素缺少值。我们可以使用训练集中使用的相同方法,用它们各自的平均值替换它们。

更换后再次检查是否有任何缺失值:

YrSold           0.0
GrLivArea        0.0
LotFrontage      0.0
LotArea          0.0
OverallQual      0.0
OverallCond      0.0
YearBuilt        0.0
YearRemodAdd     0.0
MasVnrArea       0.0
BsmtFinSF1       0.0
BsmtFinSF2       0.0
BsmtUnfSF        0.0
TotalBsmtSF      0.0
2ndFlrSF         0.0
LowQualFinSF     0.0
BsmtFullBath     0.0
MoSold           0.0
BsmtHalfBath     0.0
FullBath         0.0
HalfBath         0.0
BedroomAbvGr     0.0
KitchenAbvGr     0.0
Fireplaces       0.0
GarageCars       0.0
WoodDeckSF       0.0
OpenPorchSF      0.0
EnclosedPorch    0.0
3SsnPorch        0.0
ScreenPorch      0.0
MiscVal          0.0
MSSubClass       0.0
dtype: float64

因此,我们已经处理完了训练和测试数据集中数字特征的所有缺失值。

填充分类缺失值:

现在让我们看看分类特征的分布。

图 10。分类变量的分布

我们可以看到一些特征是完全扭曲的。我们可以通过检查特征的最高出现类别的频率来验证。

Utilities        1427
Street           1423
Condition2       1415
RoofMatl         1407
Heating          1397
LandSlope        1356
CentralAir       1333
Functional       1329
PavedDrive       1309
Electrical       1302
GarageCond       1295
LandContour      1289
GarageQual       1282
BsmtCond         1279
ExterCond        1256
SaleType         1240
Condition1       1238
BsmtFinType2     1233
BldgType         1190
SaleCondition    1174
RoofStyle        1126
MSZoning         1121
LotConfig        1035
BsmtExposure      940
LotShape          916
ExterQual         893
MasVnrType        849
GarageType        846
KitchenQual       726
HeatingQC         721
HouseStyle        713
BsmtQual          638
Foundation        634
GarageFinish      599
Exterior1st       508
Exterior2nd       497
BsmtFinType1      426
FireplaceQu       366
Neighborhood      221
Fence             153
Name: freq, dtype: object

如上所示,功能“公用事业”、“街道”、“条件 2”、“屋顶材料”、“供暖”是高度倾斜的(因为在 1428 个例子中,它们有大约 1400 次出现在一个条目中)。我们最好删除这些功能。

现在,我们将填充分类变量中缺失的值。我们将为分类特征中缺失的值填写一个新的类别“无”。

替换后再次检查列车数据集中的缺失值。

SaleCondition    0.0
SaleType         0.0
Foundation       0.0
ExterCond        0.0
ExterQual        0.0
MasVnrType       0.0
Exterior2nd      0.0
Exterior1st      0.0
RoofStyle        0.0
HouseStyle       0.0
BldgType         0.0
Condition1       0.0
Neighborhood     0.0
LandSlope        0.0
LotConfig        0.0
LandContour      0.0
LotShape         0.0
BsmtQual         0.0
BsmtCond         0.0
BsmtExposure     0.0
FireplaceQu      0.0
Fence            0.0
PavedDrive       0.0
GarageCond       0.0
GarageQual       0.0
GarageFinish     0.0
GarageType       0.0
Functional       0.0
BsmtFinType1     0.0
KitchenQual      0.0
Electrical       0.0
CentralAir       0.0
HeatingQC        0.0
Heating          0.0
BsmtFinType2     0.0
MSZoning         0.0
dtype: float64

现在检查测试数据集的分类特征是否缺少值。

Fence            0.801234
FireplaceQu      0.500343
GarageCond       0.053461
GarageQual       0.053461
GarageFinish     0.053461
GarageType       0.052090
BsmtCond         0.030843
BsmtQual         0.030158
BsmtExposure     0.030158
BsmtFinType2     0.028787
BsmtFinType1     0.028787
MasVnrType       0.010966
MSZoning         0.002742
Functional       0.001371
Exterior2nd      0.000685
KitchenQual      0.000685
SaleType         0.000685
Exterior1st      0.000685
HouseStyle       0.000000
LotShape         0.000000
LandContour      0.000000
LotConfig        0.000000
LandSlope        0.000000
Neighborhood     0.000000
Condition1       0.000000
BldgType         0.000000
HeatingQC        0.000000
RoofStyle        0.000000
Heating          0.000000
PavedDrive       0.000000
ExterQual        0.000000
ExterCond        0.000000
Foundation       0.000000
Electrical       0.000000
CentralAir       0.000000
SaleCondition    0.000000
dtype: float64

我们将再次用新的类别“无”替换所有缺失的值,就像在训练数据集中所做的那样。

现在在替换后再次检查 test_df 中的缺失值。

SaleCondition    0.0
SaleType         0.0
Foundation       0.0
ExterCond        0.0
ExterQual        0.0
MasVnrType       0.0
Exterior2nd      0.0
Exterior1st      0.0
RoofStyle        0.0
HouseStyle       0.0
BldgType         0.0
Condition1       0.0
Neighborhood     0.0
LandSlope        0.0
LotConfig        0.0
LandContour      0.0
LotShape         0.0
BsmtQual         0.0
BsmtCond         0.0
BsmtExposure     0.0
FireplaceQu      0.0
Fence            0.0
PavedDrive       0.0
GarageCond       0.0
GarageQual       0.0
GarageFinish     0.0
GarageType       0.0
Functional       0.0
BsmtFinType1     0.0
KitchenQual      0.0
Electrical       0.0
CentralAir       0.0
HeatingQC        0.0
Heating          0.0
BsmtFinType2     0.0
MSZoning         0.0
dtype: float64

我们已经替换了训练和测试数据集中所有要素的所有缺失值。我们的电子设计自动化和数据清理到此结束。

使用 Sweetviz 库进行电子设计自动化:

正如我们在上述章节中看到的,执行 EDA 是一项非常累人的任务,因为它需要花费大量的时间和精力来可视化数据、执行不同类型的分析并得出有关数据的结论。所有这些工作都可以通过使用一个新的非常有用的库来简化,这个库叫做 Sweetviz 。它获取一个熊猫数据帧并创建一个自包含的 HTML 报告。而这一切只需 2 行代码就可以完成!

图 11。Sweetviz 报告

这里有一个 链接 到包含训练和测试数据集报告的 HTML 文件。我们可以得到所有类型的可视化,如分布图和相关图。我们还提供了数字细节,如缺失值的数量、频繁出现的条目、偏斜度、峰度等。这个库是熊猫轮廓和更酷的可视化的更好版本。

结论:

在本文中,我们已经看到了 EDA 过程中涉及的不同步骤。我们看到了如何在各种图中可视化数据,以执行不同类型的分析。我们学习了如何使用这些图来检测异常值,以及如何消除它们。我们通过使用适合特征类型的方法来处理缺失值。然后我们看到了如何使用 Sweetviz 在几行代码中完成所有的分析部分。

推论:

** [## Kaggle Learn 用户的房价竞争

将你在 Kaggle Learn 上的机器学习课程中学到的东西与课程中的其他人一起应用。

www.kaggle.com](https://www.kaggle.com/c/home-data-for-ml-course/notebooks)

Kaggle 竞赛中的创作者提交的公共笔记本包含了许多关于处理 EDA 过程中遇到的不同类型问题的信息。如果你有时间,我会推荐你浏览多个笔记本,并在你自己的笔记本上实现这些方法。我从 Kaggle 的公共笔记本上了解了很多关于 EDA 和房价数据集建模的知识。

[## 使用 Sweetviz,仅两行代码即可实现强大的 EDA(探索性数据分析)

使用这个新的 Python 库,可以更快地了解您的数据

towardsdatascience.com](/powerful-eda-exploratory-data-analysis-in-just-two-lines-of-code-using-sweetviz-6c943d32f34)

TDS 中的这篇文章引导我将 Sweetviz 用于 EDA。向 Francois Bertrand 和其他贡献者致敬!即使可视化和推论是不言自明的,请参考文章的细节。**

美国警方造成的死亡

原文:https://towardsdatascience.com/exploratory-data-analysis-of-u-s-police-caused-fatalities-fce47a2b7198?source=collection_archive---------54-----------------------

R 中的探索性数据分析

美国警方造成的死亡

乔治·弗洛伊德。埃里克·加纳。塔米尔·赖斯。

在成千上万的其他名字中,这些名字是美国致命警察暴力的象征。像野火一样在美国蔓延的抗议运动使警察暴行成为每个人最关心的问题。

随着新闻站和社交媒体信息的泛滥,我不禁想知道,这些数据说明了什么?这些是新趋势还是长期存在的现实?警察对美国黑人的暴行有多不相称?心理健康起到什么作用?为了引发变革,我们需要对手头的问题有很强的经验性理解。查看数据使这成为可能。

在这篇文章中,我将分析 20 年来美国警察杀人的数据。我将使用各种探索和建模技术来回答以下问题:—随着时间的推移,有多少人被警察杀害?—这些人的种族/年龄/性别是什么?—事件发生时,一般人群的人口统计学特征是什么?心理健康状况如何影响警察杀人的可能性?

警察造成的死亡数据

在本次分析中,我将使用一些数据集。不同的数据集包括 2000 年至 2020 年被警察杀害的个人。除此之外,我还有一些人口普查、事件和主题级别的数据。这些数据来自 2000 年至 2020 年,这使得它成为过去 20 年由警察导致的死亡/杀戮的极其庞大的数据集。

在将五个不同的数据集连接和绑定在一起之后,我们得到了上面看到的数据集,它将驱动本文的大部分分析。我们可以看到,我们正在处理多行数据,这些数据显示了死于警察之手的个人、他们的一些人口统计信息、他们来自的城市/州,以及关于谋杀的信息。值得指出的是,人口统计数据来自 2014 年人口普查局的数据,这些数据只能作为一个城市/州的实际人口统计数据的估计,主要是因为该数据集中的一些杀人事件早在 2000 年就发生了。

现在我们知道了我们正在处理的数据,让我们开始进一步探索它。

在过去 20 年中,警察造成的死亡有什么趋势?正如我们在下面看到的,警察造成的死亡人数在过去 20 年中显著增长,在 2015 年达到高峰。

一段时间内警察造成的死亡

这是令人担忧的,尽管从 2015 年的峰值开始总体下降,但这表明警方处理这些情况的方式发生了变化。这令人担忧,尽管自 2015 年以来总体有所下降,但表明警方处理这些情况的方式发生了变化。2020 年的低数字可能是由于当年的数据不完整,以及由于新冠肺炎,警察造成的杀戮较少。是什么导致了这种上升和随后的下降呢?我不是专家,很想听听你的想法。以下是我的猜测:

  1. 2014 年至 2015 年展示了一些最严重的警察暴力案件(塔米尔·赖斯、弗格森骚乱等。).抗议警察暴行的人可能在抗议时被杀,这似乎是有道理的。随着更多的关注,警察在 2015 年后可能会更加小心。
  2. 在这些事件发生后,更实质性的政策可能已经到位(警察使用武力政策、旁观者干预政策、逃生训练等)。).显然这还不够,但可能归因于 2015 年以来的一些下降。
  3. 数据收集!看起来《华盛顿邮报》的收集方法在 2015 年发生了变化,这可能会对我们看到的趋势产生影响。关于这方面的更多信息包含在我的原始帖子的底部。

按种族划分的警察造成的死亡

有趣的是,根据数据集,被警察杀害的美国白人比美国黑人多。这让我很惊讶,尤其是最近新闻报道的所有事情。为了帮助解释这一点,我加入了美国境内每个种族的比例。由此,我们可以清楚地看到,美国黑人是第二大被警察盯上的群体,但只占美国人口的 12。另一方面,被警察杀害的美国白人略多,但却占美国人口的 60%。

从另一个角度来看,在美国,每八个人中就有一个是非西班牙裔美国黑人。每八个人中就有五个是白人。在被警察杀害的美国人中,每 8 人中有 2.3 人是黑人,每 8 人中有 3.7 人是白人。只看表格,美国黑人是唯一一个因警察造成死亡的比例比他们在人口中所占比例有显著增加的种族。最重要的是,如果你把黑人和西班牙裔美国人结合起来,他们占了美国白人人口的一半;然而,他们构成了更多的警察死亡。

让我们通过每个种族在人口中所占的比例进行标准化,以更公平的方式来展示这一点。

现在我们可以清楚地看到,在我们的数据集中,几乎每一年,美国黑人的死亡率都高于其他任何种族。我在这里注意到的一件事是 2015 年黑人和白人被杀的人数激增。到 2016 年,这一峰值急剧下降,但远不如其他比赛那么显著。

按年龄分列的警察造成的死亡

这看起来像是一个非常正常的年龄分布,数据中可能存在右偏。大多数被杀害的人都比较年轻,我们数据中的中位年龄是 33 岁。

各州警方造成的死亡人数

这是没有帮助的,因为人口是如此不同。例如,我们一些人口最多的州,像加利福尼亚和德克萨斯,都出现在这里。我有一种预感,如果我们使用 2014 年的一些人口普查数据,按州人口进行标准化,我们会对死亡人数有更好的认识。

超级有趣!我们现在在可视化中有许多新的州,表明仅仅因为一个州,如德克萨斯州,有许多警察造成的死亡,并不意味着它与其他州的人口比例一样高。值得在未来的迭代中探索这些状态的共同点。

高中毕业率分析

我想分析完成高中学业的城市平均人口的枪击数量。我在这里的假设是,高中毕业率低的城市会发生更多枪击案。

要做的第一件事是多填一点我们的数据。有许多地理数据没有在更早的时候引入。为了填写这些数据,我计划通过找到每个州的平均比率并使用它来估算。之后,让我们看一个高中毕业率的箱线图,以更好地了解数据分布。

箱线图显示了数据分布;也就是说,在上面的方框中,我们可以看到中间 50%的数据(第 25 百分位到第 75 百分位)。看起来大多数地区都有相当高的高中毕业率,中位数为 85.6068493 ,平均数为 86.2

同样,这没有考虑到按人口标准化我们的数据。让我们看看这是如何改变事情的。

在这两个图表之间,我看不出数据中有任何关联。令人欣慰的是,我最初的假设是错误的,即在高中毕业率较低的地区,警察导致的死亡人数会大幅增加。为了给我们的分析增加更多的统计严谨性,让我们快速看看高中毕业率和警察造成的死亡人数之间的相关性。

这是什么意思?当两个变量高度相关时,我们期望上表中的值接近+/-1。另一方面,当两个变量没有明显的关系(没有相关性)时,我们会看到一个非常接近 0 的值。由此我们可以看出,人口和死亡之间的相关性最高(. 88),这是有道理的——我们之前看到,人口越多,死亡就越多。但是我们在这里关心的相关性,高中毕业率和人口标准化死亡率之间的相关性,只有 0 . 1233,相当低。总的来说,我认为死亡人数和高中毕业率之间几乎没有关联。

贫困率分析

我想根据城市平均贫困率来分析枪击案的数量。我在这里的假设是,贫困率高的城市会有更多的枪击事件,虽然我早先的假设被揭穿了,所以我们拭目以待!

首先,让我们看看贫困率和警察导致的死亡数量之间是否有关联。

这里有一点相关性(-.1845),比我们看到的高中毕业率略高。有趣的是,这种相关性是负的。这意味着,随着贫困率的普遍上升,每 100 万人口中由警察造成的死亡人数会下降,这与我最初的想法正好相反。同样,负相关性相当弱,所以我不会过多解读。

让我们快速浏览一下散点图,看看这种负相关关系。我将叠加一条回归线来帮助显示这种关系。

同样,虽然有轻微的负面关系,但这看起来不是一个强劲的趋势。

心理健康分析

有一件事我经常听不到媒体报道,那就是警察造成死亡的受害者中存在精神健康问题。我们来看看数据显示了什么。

在 15348 名受害者中,大约有五分之一的人有心理健康问题。我们的数据可以追溯到 2000 年,坦率地说,当时围绕精神健康的讨论远没有现在这样进步。因此,我预计有心理健康问题的受害者的比例会随着时间的推移而下降。我们来看看这个。

让我们放大这张图的紫色部分。

有趣的是,在警察造成的死亡中,当时正在与精神健康作斗争的受害者的比例似乎略有上升趋势。然而,从 2016 年开始,警察杀害有精神健康问题的个人的事件似乎急剧减少。这似乎表明,警察开始对那些有心理健康问题的人做出不同的反应,这可能是因为围绕心理健康的全国性讨论增加了。

虽然相对较小,但我们可以看到约会和有心理健康问题的受害者比例之间的强烈趋势。

结论

感谢阅读!我希望你能够了解更多关于美国警察杀人背后的数据。如果你觉得这篇文章很有教育意义,很有趣,或者你只是想支持我,请随时在任何社交媒体平台上关注我(所有这些都在主页上以图标形式列出),并关注下一篇文章。如果你有关于公共政策/政治话题的想法,可以使用一些数据专业知识,请给我发送你的建议——想法越多越好!

关于数据的注释

数据可信吗?大部分情况下,是的。由于华盛顿邮报的大量工作,本分析中使用的数据已经收集完毕。尽管他们尽了最大努力,但数据(以及所有与警方导致的杀戮相关的数据)仍存在一些潜在的问题。数据收集 |华盛顿邮报通过分析“当地新闻报道、执法网站和社交媒体”收集这些数据,并试图通过向每个警察部门提交请求来提高数据质量。虽然他们的工作范围很广,但这个过程并不完美。
2。政府跟踪数据 |虽然联邦调查局和疾病预防控制中心记录了警察的致命枪击事件,但这些数据是不完整的。联邦调查局目前正在彻底检查他们收集这些数据的方式。
3。数据漏报 |已经很好地记录了警察暴行的行政记录中存在的偏见。这会让我们认为我文章中的数据低估了问题的严重性。

原载于 2020 年 6 月 14 日https://data cracy . netlify . app

动漫数据的探索性数据分析

原文:https://towardsdatascience.com/exploratory-data-analysis-on-anime-data-468cc15e13b8?source=collection_archive---------18-----------------------

得把它们都画出来

这篇文章的重点是让你了解我们如何使用图表来探索数据,并从中获得有价值的见解。我将使用 kaggle 上公开提供的动漫推荐数据库数据集来进行分析。帖子中涉及的主题的快速目录如下。

您可以点击上面的任何主题,导航到相应的部分。

新海诚动漫艺术来自 Animatrix Network

介绍

动漫太牛逼了!虽然我不是看着动漫长大的,但我对动漫的热爱最近在看了日喀则 wa kimi no uso 之后开花了,然后我就被深深地吸引进了动漫的世界。就我个人而言,我喜欢通过动漫来解释许多想法和概念的方式——可能是像《海丘》、《黑子野巴介》和《羽扇豆》这样的体育动漫中,年轻的 Kaneki 在一个人类与食尸鬼共存的世界中为和平而战时面临的困境,或者是《Clannad & Clannad:After Story》中表达的你必须经历的充满挑战的生活方式……

我可以就此滔滔不绝,但让我不要离题,紧扣主题。因为有很多很棒的动漫内容,如果有人可以做一些数据探索(因为关于动漫的数据是公开的,可以通过查看类型、其他人的意见和观察等来识别他们喜欢的动漫或他们的选择,那将是非常棒的。

人类有很强的视觉感知能力。当数据被可视化地表达,而不是写成一个脚本,这让我们更容易理解。

这就是为什么在数据科学的世界里,我们求助于使用各种图形、图表、绘图等。从数据中获得洞察力或探索数据。这也被称为探索性数据分析,它只是通过视觉或非视觉手段来了解你的数据,通常是机器学习或深度学习管道的第一步。

Python 提供了一个名为 seaborn 的伟大包,它可以与名为 matplotlib 的包的绘图功能结合使用,以便可视化我们的数据。我们将浏览数据科学中常见的几种不同的图,并使用它们来研究我们的数据集。在此之前,我们还会做一些描述性分析。所以,事不宜迟,让我们开始吧!

描述性分析

在转到图之前,我们将读入数据并进行一些描述性分析,以找出与数据相关的重要统计数据。我们还将处理数据,以便为绘图做好准备。

图片由 Vinayak 提供

我们可以看到,类型中有 0.5%的缺失字段,类型中有 0.2%的缺失数据,剧集中有 3%的缺失数据,收视率中有大约 2%的缺失数据。许多数据集中通常都是这种情况。丢失数据可能是由多种原因造成的:其中一些原因可能包括用户提供的数据不完整、从数据源抓取数据时数据丢失等。

当缺失数据的百分比很小而数据本身很大时,即至少超过一千条记录左右,在许多领域中,处理这种情况的两种最常用的方法是丢弃具有缺失值的行或者用相应列的平均值/模式对它们进行估算。现在,让我们选择删除缺少值的记录。

看看记录的描述性统计。

图片由 Vinayak 提供

我们可以看到,平均每部动漫有 12 集,所有动漫的平均评级约为 6.5。此外,MyAnimeList 平台上谈论该动漫的平均成员数约为 18.5k,可以说这是相当可观的。对于分类变量,mean、std、quartiles 等度量没有意义,因此这些位置使用上面的 NaN 填充;对于连续变量,unique、top 和 frequency 等度量没有太大意义,因此这些位置也使用 NaN 值填充。

需要注意的一点是,就剧集和成员而言,差异相当大;这表明,与大量评级集中在 6.5 左右的评级不同,在剧集和成员中,分布相当平坦,没有在平均值附近达到峰值。我们将在下面的直方图部分对此进行更详细的探讨。

此外,在分类栏中,我们可以看到大部分的动画都是电视动画,其中最流行的类型是《非常》,有 785 个条目。这是一个误导性的统计,我将在直方图和计数图部分解释原因。

图片由 Vinayak 提供

排名前 5 的动漫都是电影和一部电视动漫。我在这里试着保持客观,但我真的希望你看 Kimi no Na wa,不要忘记和你一起连续看《风化》,看看多喜和光羽目前在他们的生活中做些什么!

现在,回到手头的主题,Taka no Tsume,Mogura no Motoro 和 Kahei no 海是成员非常非常小的动漫。这意味着有一些人看了他们,他们都非常喜欢他们。由于这里的成员数量很少,即使他们的评分很高,我也有点不愿意看(除非我看到成员对这 3 部电影的描述)。

图片由 Vinayak 提供

伙计们,这有什么意义吗?每个宅男如果没有看过这些动漫,至少也听过。此外,它们中的大多数已经存在了大约十年或更长时间。随着时间的推移,随着一个系列的名气越来越大,越来越多不同的人观看它,其中一些人会不喜欢它,这使得收视率与前一个例子中只有 13 名成员的 Taka no Tsume 的高收视率相比大幅下降。

图片由 Vinayak 提供

只是为了好玩,看看有很多集的动漫,毫无疑问哆啦 a 梦榜上有名。每个千年都会记得在电视、海报或商品上至少看过一次哆啦 a 梦。但是在这个有限的集合中,我们可以看到,更多的剧集并不一定意味着更高的收视率,反之亦然。

视觉分析

一幅画最大的价值是当它迫使我们注意到我们从来没想到会看到的东西

约翰·图基

现在我们已经对数据集的描述性统计有了一个概念,让我们尝试可视化地分析数据集。我们将理解并使用几种不同类型的图来可视化使用 seaborn 的数据。

在此之前,我们需要知道数据集的两种主要特征。

  1. 分类变量—取离散值的变量称为分类变量。它们可以是数字,甚至可以是非数字的,例如考试中的成绩(A、B、C 等。)、排名(第一、第二、第三)、交通信号上的标志(红色、黄色、绿色)等。在 A 和 b 之间没有 1.5 级或紫色信号或等级,这些变量可以取的值是预先定义的集合中的一个。也就是说,类别中可能存在等级(A > B > C ),也可能没有等级(如红色、黄色、绿色)。前者被称为序数变量,后者被称为名义变量。不管是名义变量还是基数变量,它们都是范畴变量。
  2. 数字变量——采用连续值的变量,如一个人的体重/身高、每平方英尺的财产数量、黄金价格等。被称为数值变量。

根据变量是数值型还是分类型,我们可以借助不同种类的图来直观地表达它们之间的关系。

下面是我们将在下一节介绍的不同类型的图表。

  • 直方图/计数图
  • 条形图
  • 饼图
  • 盒子情节和小提琴情节
  • 散点图
  • 热图

直方图和计数图

直方图

只看均值和标准差你只能得到一个变量分布的大概概念;直方图有助于实际可视化变量的分布。

当我们有一个连续变量时,我们创建一个直方图,当我们有一个离散变量时,我们创建一个计数图。因此,我们将使用 matplotlib 和 seaborn 分别创建一个流派计数图和一个评级直方图,以查看这些变量的分布。

Utils.histogram(data, "rating")
Utils.histogram(data, "members", 50)

成员直方图

评分直方图

观察这两个图,我们实际上可以看到,平均值是总体评级的一个很好的估计值,因为大多数评级都包含在非常接近平均值的区域内。覆盖在二进制顶部的连续线被称为核密度估计,它是一种适合于近似该特定变量范围内所有连续值的观察频率的估计量。

在会员中,我们可以看到某些动漫有很多会员,而其中很多动漫的会员非常少。这种行为可以说是非常自然的,因为在大量的动漫中,有些非常受欢迎,这些动漫的成员/粉丝非常多,但鲜为人知的却没有多少人谈论它们…

这就是为什么我们在描述表中观察到,成员的标准偏差与其平均值相比是巨大的,而评级的标准偏差与其平均值相比是巨大的。

计数图

计数图与直方图非常相似,唯一的区别是它们用于离散变量。顾名思义,它计算特定类别的条目数,并简单地将它们绘制成条形。我们试着形象化一下流派的计数,看看所有动漫中哪个流派最受欢迎。

我们需要为流派执行一些预处理步骤。由于一部动画有多种类型,我们首先必须为类型的计数创建一个单独的表,并创建一个相同的计数图。我们开始吧!

Utils.genre_countplot(data, 'genre')

类型情节的计数

现在看看这个,我们可以看到,实际上来说,喜剧是最常见的类型,不像 hentai,在我们做预处理和策划之前,它应该是最常见的。

条形图

当我们有一个分类变量和一个数字变量时,我们可以使用柱状图。根据分类列是在 x 轴上还是在 y 轴上,图表的同义词分别是垂直条形图或水平条形图。

让我们就类型(分类变量)来研究评级和情节(数字变量)。从条形图的角度来看,这是一个很好的用例。

Utils.barplot(data, 'type', 'rating')
Utils.barplot(data, 'type', 'episodes')

作为类型函数的情节

作为类型函数的评级

我们可以看到,平均而言,任何类型的动漫的评分都在 5 到 6 之间。

然而,对于电视来说,集数似乎偏高,平均每部动漫约 35 集。对于电影,特别节目和 OVA,它大约是 1 或 2 集,而对于 ONA,它大约是每部动漫 5 集。

饼图

为了直观显示分类变量的计数分布,我们可以使用饼状图作为计数图的替代方法,以百分比的方式更好地理解相对比例。让我们看看类型和流派的馅饼。

from collections import Counter
typedata = Counter(data.type)all_genres = []
for item in data.genre:
    item = item.strip().split(', ')
    all_genres.extend(item)genredata = Counter(all_genres)Utils.pieplot(typedata, "type")
Utils.pieplot(genredata, "genre")

图片由 Vinayak 提供

图片由 Vinayak 提供

查看上面的图表,我们可以看到,当一个类别中的级别数量很高时,饼图并不是可视化数据的好方法,因为数据不能很好地进行美学和语义分析。那么选择传统的计数图是个好主意。

然而,对于相对较少的级别,它在传达百分比分割方面非常有效,通俗地说,这接近于许多人的理解。我们可以看到,电视,OVA 和电影在动漫产业中占主导地位,而特别节目,ONA 和音乐动漫的数量相对较少。

箱线图和紫线图

箱线图

直方图是研究数据分布的一种好方法,但是它有时是无关紧要的,直方图的大部分信息可以用另一种称为盒图或盒须图的图来更简洁地表达。这是一个直方图的压缩版本,你可以从中了解中位数和四分位数,也可以用它来标记潜在的异常值。关于如何使用方框图的详细解释可以在这篇文章中找到。

基本上,我们感兴趣的是找出四分位数,并确认大部分数据位于 IQR 和晶须内。位于这些范围之外的记录是潜在的异常值,需要对它们进行调查,如果发现正常,将在未来的分析中考虑它们,否则这些记录将被阻止。

箱线图的解释

那么,让我们来看一下评级的箱线图,看看它是如何随类型变量而变化的。

图片由 Vinayak 提供

我们可以看到有大量的异常值,特别是在电视、OVA 和特别节目的低端。这就需要注意那些远远超出正常评级范围的点。为什么他们会被这样评价?这些评级是伪造的吗,是否有一些负面的宣传影响了这些动漫,它们是否被足够多的人评级,或者评级仅仅是基于对这些动漫的 1 或 2 个评论而获得的?

因此,该图有助于我们识别可能异常的记录,我们需要进一步挖掘以确保这些异常是真实的,并决定是否保留这些记录以供我们分析。

还有一点需要注意的是,有时 y 变量的范围可以呈指数变化。在这种情况下,在绘制箱线图之前进行对数变换确实有助于使事情更容易理解。

fig, ax = plt.subplots(1, 2, figsize = (15, 6))Utils.boxplot(data, "type", "members", ax[0], log = False)
Utils.boxplot(data, "type", "members", ax[1], log = True)plt.savefig('./img/boxplot_members_type')
plt.close()fig, ax = plt.subplots(1, 2, figsize = (15, 6))Utils.boxplot(data, "type", "episodes", ax[0], log = False)
Utils.boxplot(data, "type", "episodes", ax[1], log = True)plt.savefig('./img/boxplot_episodes_type')
plt.close()

图片由 Vinayak 提供

图片由 Vinayak 提供

我们可以看到,对于平面图,很难解释箱线图,因为大部分数据都集中在这么短的时间间隔内,而且数据的分布范围很大。为了减轻这种情况,我们可以进行对数变换,然后研究分布。

我们可以看到,就成员而言,OVA 和音乐中有一些潜在的异常值,当谈到剧集时,大多数类别都有几个问题。

一部电影必须只有一集,再多就有嫌疑了。图表显示有一些这样的数据点。也许它应该是电视,但却被贴错了电影的标签,反之亦然。让我们找出答案。

图片由 Vinayak 提供

虽然这些我都没有亲自看过,但是我看过 Byousoku 5 厘米。这是一部有三个小故事的电影。这可能是为什么在这种情况下,集数是 3,在我看来是相当可观的。

然而,戈曼-希基和卡米·天兔·罗珀分别以 100 集和 14 集的电影首映集引起了争议。它们可能是电视连续剧,但由于它们有一个潜在的故事,而且都是很短的插曲,所以它们可以被统称为电影。

现在,数据科学家可以自行决定是否将这些记录视为电影或连续剧,或者完全放弃。没有方法是错误的,这只是个人的思维过程和他的推理方式,以证明排除/包含/更改记录在这里是重要的。

紫罗兰花

想象一下直方图和箱线图的最佳组合——这就是紫线图。我们在直方图中看到的内核密度估计覆盖在盒状和须状图的顶部。这不仅为我们提供了中位数、IQR 和晶须,而且还在一个单独的图中概述了分布的性质。

虽然它是如此的信息密集,但美学和缺乏简单性限制了它们在现实世界中的使用;然而,为了向了解这个情节及其代表意义的人展示信息,我个人想不出比小提琴更好的选择了。

让我们为评级分类画一张小提琴图。

图片由 Vinayak 提供

我们可以清楚地观察到传播和潜在的异常值。虽然电影的评级最分散,但它们没有很多异常值,而在 OVA 中,我们可以看到很大一部分数据超出了晶须,这表明存在几个异常值。

fig, ax = plt.subplots(1, 2, figsize = (15, 6))Utils.violinplot(data, "type", "members", ax[0], log = False)
Utils.violinplot(data, "type", "members", ax[1], log = True)plt.savefig('./img/violinplot_members_type')
plt.close()fig, ax = plt.subplots(1, 2, figsize = (15, 6))Utils.violinplot(data, "type", "episodes", ax[0], log = False)
Utils.violinplot(data, "type", "episodes", ax[1], log = True)plt.savefig('./img/violinplot_episodes_type')
plt.close()

图片由 Vinayak 提供

图片由 Vinayak 提供

从 violinplot 的成员中,我们可以看到,除了大部分数据包含在 IQR 之内,数据的集中程度不仅在平均值附近,而且在整个范围内相当分散。

另一方面,当我们比较剧集 wrt 类型的小提琴情节时,大多数类型都有一些记录,这些记录对于特定类型的动漫来说剧集数量多得离谱(正如我们从上面的盒子情节中看到的)。

散点图

当我们需要研究两个连续变量之间的变化时,我们求助于散点图。它们帮助我们理解这两个变量之间的相关性,即如果一个变量增加,另一个变量会增加、减少还是保持不变。

它们提供了一个很好的视觉检查来衡量线性回归模型的性能,或者更确切地说,在实际构建一个回归模型之前设置对其性能的预期。Seaborn 提供了一个很好的功能,在散点图的顶部覆盖一条回归线来解决这一点。

让我们来看看评分的相关性作为成员的函数,大概我期望他们应该是成正比的,因为高评分的动漫会有更多的成员关注该动漫,反之亦然。

Utils.scatterplot(data, "members", "rating")

图片由 Vinayak 提供

正如我们所看到的,集中在 0 到 400000 个成员中的大量数据点显示出正相关关系,即一个成员的增加会导致另一个成员的增加。

请记住,y 轴实际上被限制在 0 到 10 之间。不能有大于 10 的分数,这就是为什么虽然很多人喜欢一些动漫,但他们将被迫给出不超过 10 的评分,即使他们非常喜欢它。如果没有这种限制,我预测 100000 个成员中的线和点会更加接近。

再来一个,看看一部剧的集数和收视率的关系。

Utils.scatterplot(data, "episodes", "rating")

图片由 Vinayak 提供

看起来剧集和收视率之间确实存在正相关,但并不像在成员和收视率之间观察到的那样强。此外,该模型的 95%置信区间更宽,这意味着回归线不如成员和评级之间的回归线拟合得好。

这就是我们如何利用散点图来理解两个量是如何相互变化的。

热图

另一个有用的视觉效果是热图。当我们有两个分类变量和一个与这两个分类变量相关联的数值度量时,热图是一个非常好的可视化工具。我们可以看到有多少动漫属于某个特定的流派和类型,并建立一个相同数量的热图或相同评级的热图,并以色标的形式显示出来。

让我们先来看看制作一个动画类型和流派的热图,感受一下热图。

# Get a list of all the unique types
types = data.type.unique()# Create a mapping between genre and type and initialize it to all zeros
mapping = pd.DataFrame(np.zeros((len(genredata.keys()), len(types)), dtype = np.int), index = list(genredata.keys()), columns=types)
rating_mapping = pd.DataFrame(np.zeros((len(genredata.keys()), len(types)), dtype = np.float), index = list(genredata.keys()), columns=types)# Each time an item is encountered for genre and type, increment the count by 14
# Each time an item is encountered for genre and type, increment the rating by the respective rating
for item in genredata.keys():
    for row in data.itertuples():
        if item in row[3]:
            mapping.loc[item, row[4]] += 1
            rating_mapping.loc[item, row[4]] += row[6]# Divide the rating by the counts to get average rating for the pair
rating_mapping = rating_mapping / mappingUtils.heatmap(mapping, "Type", "Genre")
Utils.heatmap(rating_mapping, "Type", "Genre", quantity = "Rating")

图片由 Vinayak 提供

我们可以从类型类型热图中直观地看到,有一些组合制作了很多动画,而其他一些组合则没有。

例如,我们没有任何年轻的音乐剧、亨泰电视或 ONA 动漫,另一方面,我们有许多电视喜剧、科幻电影、生活片段电视等等。最常见的组合是电视喜剧,Hentai 不出现在电视上,这是有道理的,因为电视是一个家庭的每个人都在看的,儿童不应该在年幼时接触敏感内容。

图片由 Vinayak 提供

如果我们看上面的热图,大多数组合的平均评分在 5-6 分左右,一些组合的评分为零(白色的组合)。收视率最高的节目是朝鲜电影,其次是电视惊悚片,然后是警察电影等(总之,包括悬疑、阴谋和青少年爱情/青少年漫画在内的短片平均受到观众群体的欢迎)。

来自九州的日向翔阳——图片来源:音云

这就是这篇文章的全部内容!希望你觉得有用。这篇文章附带的代码和其他东西可以在我的 github repo 这里找到。EDA,尤其是绘图,是一些很棒的工具,可以帮助你很好地理解和交流这种理解。我希望这里提到的情节成为你解决数据科学问题的工具箱中的无价工具:)。

参考文献和更多阅读

  1. Kaggle 上的动漫推荐数据库
  2. 了解箱线图
  3. 使用 matplotlib 进行数据可视化
  4. Seaborn 官方文档
  5. Matplotlib 官方文档
  6. 代码实现的 Github repo

心脏病 UCI 数据集的探索性数据分析**

原文:https://towardsdatascience.com/exploratory-data-analysis-on-heart-disease-uci-data-set-ae129e47b323?source=collection_archive---------5-----------------------

|一个完整的逐步探索性数据分析,并附有简单的解释。

Jair Lázaro 在 Unsplash 上的照片

动机

  • 探索性数据分析(EDA)是理解数据的预处理步骤。执行 EDA 有许多方法和步骤,但是,大多数方法和步骤都是特定的,集中在可视化或分布上,并且是不完整的。因此,在这里,我将一步一步地了解、探索和提取数据中的信息,以回答问题或假设。没有结构化的步骤或方法可以遵循,但是,这个项目将为你和我未来的自己提供一个关于 EDA 的洞察力。

简介

心血管疾病或心脏病是全球头号死亡原因,每年有 1790 万例死亡。心血管疾病是由高血压、糖尿病、超重和不健康的生活方式共同造成的。你可以阅读更多关于心脏病统计数据和病因的自我了解。这个项目包括手动探索性数据分析和使用谷歌 Colab 上的 Jupyter 笔记本中的熊猫概况。本项目使用的数据集为 UCI 心脏病数据集,本项目的数据代码均可在我的 GitHub 库中获得。

数据集解释

最初,数据集包含来自 303 名患者的 76 个特征或属性;然而,发表的研究只选择了 14 个与预测心脏病相关的特征。因此,这里我们将使用由 303 名患者组成的数据集,具有 14 个特征集。

EDA 的概要如下:

  1. 导入并了解数据
  2. 数据清理

a)检查数据类型

b)检查数据字符错误

c)检查缺失值并替换它们

d)检查重复行

e)统计摘要

f)异常值以及如何消除它们

3。分布和关系

a)分类变量分布

b)连续变量分布

c)分类变量和连续变量之间的关系

4。使用 pandas 评测报告的自动化 EDA

变量或特性解释 :

  1. 年龄(以年为单位的年龄)
  2. 性别:(1 =男性,0 =女性)
  3. cp(胸痛型):[ 0:无症状,1:不典型心绞痛,2:非心绞痛性疼痛,3:典型心绞痛]
  4. trestbps(静息血压,单位为毫米/毫米汞柱)
  5. 血清胆固醇(毫克/分升)
  6. fps(空腹血糖> 120 mg/dl): [0 =否,1 =是]
  7. restecg(静息心电图):[0:根据 Estes 标准显示可能或明确的左心室肥大,1:正常,2:ST-T 波异常]
  8. thalach(达到的最大心率)
  9. exang(运动诱发的心绞痛):[1 =是,0 =否]
  10. oldpeak(相对于静息运动诱发的 ST 段压低)
  11. 斜率(最大运动 ST 段的斜率):【0:下坡;1:平;2:上坡]
  12. ca[主要血管数量(0-3)
  13. thal : [1 =正常,2 =固定缺陷,3 =可逆缺陷]
  14. 目标:[0 =疾病,1 =无疾病]

此处提供了数据集描述

让我们开始吧…!!

1。导入并了解数据

作者的图像快照

这里我们有 303 行,14 个变量。

2.数据清理

a)检查数据类型

变量类型有

  • 绝对的
  • 连续的:年龄,trestbps,胆固醇,塔拉克,旧峰

python 对变量的类型分类正确吗?我们来了解一下数据类型。

作者的图像快照

请注意,python 将二进制和分类变量分类为不同的整数类型。我们需要将它们更改为“对象”类型。

b .检查数据字符错误

  1. 特征‘ca’的范围是 0-3,然而**df.nunique()**列出的是 0-4。所以让我们找到 4,把它们改成 NaN。

作者的图像快照

2.特征‘thal’范围为 1-3,然而**df.nunique()**列出了 0-3。“0”有两个值。所以让我们把它们改成 NaN。

作者的图像快照

c)检查缺失的数值并替换它们

作者的图像快照

并且使用 Missingno 库可视化丢失的值。缺少的值用水平线表示。这个库提供了一种信息丰富的方式来可视化位于每一列中的缺失值,并查看不同列的缺失值之间是否有任何关联。这里要为一篇关于 Missingno 的伟大文章欢呼。

msno.matrix(df)

作者的图像快照

用中位数代替 NaN。

df = df.fillna(df.median())
df.isnull().sum()

d)检查重复行

作者的图像快照

e)统计汇总

作者的图像快照

基本上,使用df.describe(),我们应该检查分类变量的最小值和最大值(min-max)。性别(0–1),CP(0–3),FBS(0–1),rest ECG(0–2),exang(0–1),斜率(0–2),ca(0–3),thal(0–3)。我们还应该观察连续变量的平均值、标准差、25%和 75%。

在我们绘制离群值之前,让我们更改标签,以便更好地可视化和解释。

f)异常值以及如何移除它们

作者的图像快照

还有其他几种绘制箱线图的方法。

fig = px.box(df, x=”target”, y=”chol”)
fig.show()

作者的图像快照

或者使用 seaborn

sns.boxplot(x=’target’, y=’oldpeak’, data=df)

作者的图像快照

现在,让我们定义并列出离群值..!!

这是列出的异常值。

作者的图像快照

让我们去掉离群值。

作者的图像快照

3。分布和关系。

a)目标变量分布

作者的图像快照

患病者比健康者多。

b)年龄变量分布

# print(df.age.value_counts())
df[‘age’].hist().plot(kind=’bar’)
plt.title(‘Age Distribution’)

作者的图像快照

年龄呈正态分布。

# Analyze distribution in age in range 10
print(df.age.value_counts()[:10])
sns.barplot(x=df.age.value_counts()[:10].index,
y=df.age.value_counts()[:10].values,
palette=’Set2')
plt.xlabel(‘Age’)
plt.ylabel(‘Age distribution’)

作者的图像快照

大多数病人年龄在 50 岁到 60 岁之间。让我们快速浏览一下基本数据。平均年龄约为 54 岁,std 为 9.08,最小的为 29 岁,最大的为 77 岁。

作者的图像快照

c)根据目标变量的性别分布

作者的图像快照

从柱状图中,我们可以观察到在疾病患者中,男性高于女性。

d)根据目标变量得出的胸痛分布

作者的图像快照

胸痛(cp)或心绞痛是由流向心脏的血液减少引起的一种不适,引发手臂、肩膀、颈部等不适。

然而,看上面的柱状图,它提出了具有典型心绞痛的健康受试者的更高数量的问题。或者换句话说,大多数健康的受试者都有胸痛,这也是这里讨论的。胸痛可能是由于压力、身体活动和许多其他因素造成的主观症状,并因性别而异。女性和老年患者通常有非典型症状和病史。这篇文章提供了一项临床试验中典型心绞痛患者与非典型心绞痛患者的对比分析。

e)根据目标变量的空腹血糖分布

作者的图像快照

空腹血糖或 fbs 是糖尿病指标,fbs >120 mg/d 被认为是糖尿病(真级)。在这里,我们观察到,与类 false 相比,类 true 的数量较低。然而,如果我们仔细观察,就会发现没有糖尿病的心脏病患者人数更多。这表明 fbs 可能不是区分心脏病患者和非疾病患者强有力的特征。

f)根据目标变量的斜率分布

作者的图像快照

g)连续变量分布图。

作者的图像快照

  • 正态分布为:年龄,trestbps 和几乎为胆固醇
  • 旧峰向左倾斜
  • thalac 是右倾的

h) Sns pairplot 可视化分布。

作者的图像快照

  • 在疾病和非疾病之间具有线性分离关系的旧峰。
  • thalach 在疾病和非疾病之间具有温和的分离关系。
  • 其他特征没有形成任何明显的分离

i)相关性

作者的图像快照

  • cp ',' thalach ',' slope '与目标呈良好正相关
  • “老峰”、“额”、“钙”、“身高”、“性别”、“年龄”与目标呈良好的负相关
  • fbs' 'chol ',' trestbps ',' restecg '与我们目标的相关性较低

4。使用 Jupyter 笔记本电脑上的 pandas profiling 报告进行自动化 EDA,Google Colab

  1. !皮普安装熊猫档案

! pip install [https://github.com/pandas-profiling/pandas-profiling/archive/master.zip](https://github.com/pandas-profiling/pandas-profiling/archive/master.zip)

这是自动化 EDA 的快照

  • 所以你去,一个完整的 UCI 心脏病走一遍 EDA。
  • 我希望你觉得这个指南有用,我将继续使用另一种类型的数据集探索 EDA。
  • 请随意评论/纠正/提问。
  • 探索愉快!!❤

你可以在 Jupyter Google Colab 我的文章下面查看应用熊猫概况报告的步骤。

[## 如何使用谷歌 Colab 上的熊猫简介

使用 Google Colab 上 Jupyter 中的 Pandas Profiling 进行自动探索性数据分析。

medium.com](https://medium.com/python-in-plain-english/how-to-use-pandas-profiling-on-google-colab-e34f34ff1c9f)

移动应用行为数据的探索性数据分析

原文:https://towardsdatascience.com/exploratory-data-analysis-on-mobile-app-behavior-data-2777fc937973?source=collection_archive---------60-----------------------

使用可视化和相关性分析,深入了解具有大量原始数据的 EDA,以提高您的动手技能

来自 Unsplash 的 Img 通过链接

在之前的文章中,我们介绍了如何在小型 app 行为数据集上执行 EDA。希望你在那里学到了很多。这篇文章旨在用更复杂的数据集提高你的 EDA 技能,并介绍新的技巧。它被分成 6 部分。

1.数据审查

2.数据清理

3.数值变量分布

4.二元变量分布

5.相关分析

6.摘要

现在,让我们开始旅程🏃‍♂️🏃‍♀️.

  1. 数据审核

快速查看下面视频中显示的数据,您会发现有 31 列 27,000 行。有了这么多特性,最好为每一列的解释创建一个视图,如图 1 所示,以增强我们的理解。

视频 1 原始数据的简要视图

图 1 表格视图中的变量说明

2.数据清理

原始数据通常包含缺失值。所以我们需要知道每一列中是否有 NaN 。具体来说,

dataset.columns[dataset.isna().any()].tolist()

我们得到了列 ['年龄','信用分数','奖励收入']。但是这些列中有多少条记录是 NaN ?所以,

dataset.isna().sum()

如图 2 所示,在' credit_score' 栏中有 8000 多名 NaN ,在' rewards_earned' 栏中有 3227 名 NaN我们将删除这两列,并删除年龄为的 4 条记录。

图 2 列中的 NaN 记录

具体来说,

dataset = dataset[pd.notnull(dataset.age)]
dataset = dataset.drop(columns = [‘credit_score’, ‘rewards_earned’])

3.数值变量分布

为了更好地理解数据分布,可视化是最好的方法。让我们尝试一个直方图。具体来说,

dataset2 = dataset.drop(columns = [‘user’, ‘churn’])
for i in range(1, dataset2.shape[1] + 1):
    plt.subplot(6, 5, i)
    f = plt.gca()
    vals = np.size(dataset2.iloc[:, i — 1].unique()
    plt.hist(dataset2.iloc[:, i — 1], bins=vals, color=’#3F5D7D’)

如图 3 所示,许多变量是正偏的。一些二元变量均匀分布,而另一些则高度集中在一边。对于高度集中的变量,审查因变量是否不平衡非常重要。例如,对于列' waiting_4_loan ',小于 10%为 1。如果大多数等待贷款的人退订了该产品,该模型很可能会在这个功能上适得其反。

图 3 数值变量直方图

4.二元变量分布

如上所述,让我们使用一个饼状图来关注二元变量的分布。具体来说,

dataset2 = dataset[[‘housing’, ‘is_referred’, ‘app_downloaded’, ‘web_user’, ‘app_web_user’, ‘ios_user’, ‘android_user’, ‘registered_phones’, ‘payment_type’, ‘waiting_4_loan’, ‘cancelled_loan’, ‘received_loan’, ‘rejected_loan’, ‘left_for_two_month_plus’, ‘left_for_one_month’, ‘is_referred’]]for i in range(1, dataset2.shape[1] + 1):
    f = plt.gca()
    values = dataset2.iloc[:, i — 1].value_counts(normalize = True).values
    index = dataset2.iloc[:, i — 1].value_counts(normalize = True).index
    plt.pie(values, labels = index, autopct=’%1.1f%%’)

如图 4 所示,有 5 列需要进一步探索,因为它们的分布高度集中: 'waiting_4_loan '、' cancelled _ loan '、' received_loan '、' rejected_loan '、' left_for_one_month'

图 4 二元变量饼图

对于这 5 列,让我们回顾一下少数类别中的因变量分布。具体来说,

图 5 集中变量的因变量分布

图 5 告诉我们,在少数民族类别中,因变量并不严重失衡。太好了。没什么好担心的。

总之,可视化的全部目的就是了解每个变量的分布是如何均匀的,以及每个二元变量中因变量的分布是如何均匀的。因此,我们可以识别需要过采样或下采样的变量。

5.相关性分析

5.1 自变量和因变量之间

这一步是为了了解哪些特征或变量可能对因变量有很大影响。这里我们只分析数值变量

具体来说,

dataset.drop(columns = [‘user’, ‘churn’, ‘housing’, ‘payment_type’, ‘registered_phones’,‘zodiac_sign’]).corrwith(dataset.churn).plot.bar(figsize=(20,10), title = ‘Correlation with Response variable’,fontsize = 15, rot = 45,grid = True)

图 6 显示了一些有趣的发现。例如,对于变量' cc_taken' ,客户获得的信用越多,他们流失的可能性就越大。这可能表明顾客对信用卡不满意。

图 6 自变量和因变量之间的相关性

5.2 自变量之间

理想情况下,我们只使用“独立”变量作为输入。相关矩阵表明变量是否相互独立。具体来说,

corr = dataset.drop(columns = [‘user’, ‘churn’]).corr()

如图 7 所示,' android_user '和' ios_user '之间有很强的负相关关系。另一列是“ app_web_user ”,表示同时使用 app 和 web 的用户。当' app_downloaded '为 1 且' web_user '为 1 时,只能为 1。所以' app_web_user '不是一个需要去掉的自变量。具体来说,

dataset = dataset.drop(columns = [‘app_web_user’])
dataset.to_csv(‘new_churn_data.csv’, index = False)

图 7 独立变量间的相关矩阵

6.总结

在许多情况下,您将面临比我们在这里处理的更多的脏数据。因此,您需要首先清理数据,检查数据分布,并了解是否出现了不平衡。此外,使用相关性分析来消除对要素的任何依赖性。幸运的是,不管你有多少数据,思维过程或多或少都是一样的。

太好了!这就是旅程的全部!如果需要源代码,可以随时访问我的 Github 页面🤞🤞。下一篇文章将介绍数据处理、模型构建和优化

类固醇探索性数据分析

原文:https://towardsdatascience.com/exploratory-data-analysis-on-steroids-e1488324fbaa?source=collection_archive---------21-----------------------

Python 中 EDA 的一种表达方法

机器学习的讨论通常围绕算法及其性能展开:如何提高模型精度或降低其错误率,擅长特征工程或微调超参数。但是有一个概念比任何事情都重要:探索性数据分析,或 EDA。

EDA 就是在我们忙于任何模型之前,从数据中找出意义。这完全说得通,对吗?如果我们还不知道我们的数据,我们将使用哪个模型?

在有机器学习模型之前,有 EDA

这是数据科学的一个核心方面,但有时会被忽视。你做任何事情的第一步都应该是了解你的数据:理解它,熟悉它。你想从这些数据中得到什么答案?你使用了哪些变量,它们的含义是什么?从统计学的角度看如何?数据格式是否正确?你有缺失值吗?复制的吗?离群值呢?

随着数据量的增加,这一概念变得更加重要:想象一下,试图解析成千上万个寄存器,并从中找出意义。接下来,我想分享我的 Python 方法,以最有效的方式回答其中的一些问题。

描述数据集

在这篇文章中,我使用了世界银行的经济数据集,描述了一些世界范围内的关键因素,如 GDP、人口水平、地表等。你可以在这里找到数据集和完整代码

首先,我们需要导入一些库:

import pandas as pd
import numpy as np
import seaborn as sns
import xlrd

熊猫、 NumpySeaborn 是任何 EDA 练习中的关键。“xlrd”包仅在我们的示例中需要,因为我们使用 Excel 文件作为数据源。

现在,让我们导入数据集:

df = pd.read_excel(“wbdata.xlsx”)

看一下数据:

df.head()

df.describe()

数据集中数值变量的一些基本统计。

df.shape

查看数据集结构的快速方法:215 行和 10 列。

df.dtypes

检查变量类型:它们都是浮动的,除了国家名称是一个字符串。在这里找到如何改变变量类型的教程。

df.info()

在这里,我们看到一些变量具有空值(例如,变量“人口”有一行缺少值,“表面”2,等等)。我们将在下面看到如何处理它们。

缺少值

缺失值可能由不同的原因造成,如数据输入错误或记录不完整。这是非常常见的,并且会对从数据中得出的结论产生重大影响。

我们在上面已经看到,本例中的数据集有几个缺失值,但是让我们看看如何测试任何数据集。你可能要问你的第一个问题是:有没有缺失的值?

print(df.isnull().values.any())

接下来,您可能想要检查它们的数量:

print(df.isnull().sum().sum())

现在,让我们回顾一下这些缺失值的总结

print(df.isnull().sum())

你看到什么熟悉的东西了吗?这是世界的另一面。info()函数。处理缺失值有不同的策略,但没有通用的方法。对于本例,我们将放弃它们,因为进行插补没有意义:

df1 = df.copy()df1.dropna(inplace=True)

我们回顾新的数据集:

df1.info()

剩下 188 条记录,没有空值。现在我们准备向前迈进。

您可以在这里找到处理缺失值的其他方法。

设想

让我们用 Seaborn 可视化新数据集:

sns.pairplot(df1)

这样,您可以快速识别异常值、聚类和变量之间的明显相关性。

让我们结合变量“gdp”和“人口”:

sns.scatterplot(x='gdp.cap', y='population', data=df1, hue='population')

你在右上角看到两个明显的异常值了吗?2 个国家的人口水平与数据世界中的其他国家相比处于极端水平。您可以通过单独分析“人口”变量来验证观察结果:

sns.kdeplot(df1[‘population’], shade=True, color=’orangered’)

检测异常值的另一种方法是绘制一些箱线图:

df1.plot(kind=’box’, subplots=True, layout=(3,3), sharex=False, sharey=False, figsize=(20, 20), color=’deeppink’)

您还可以显示这些变量的密度图,并分析它们的偏斜度:

df1.plot(kind=’density’, subplots=True, layout=(3,3), sharex=False, figsize=(20, 20))

查看这个链接获得更多的 Seaborn 造型技巧。

在这个例子中,我故意没有处理异常值,但是有多种方法可以做到这一点。你可以在这里找到一些异常值识别和处理的例子。

相互关系

关联变量将为您节省大量的分析时间,这是对数据进行任何假设之前的必要步骤。相关性仅针对数值变量进行计算,因此了解数据集中的变量类型非常重要。

你可以在这里找到非数字变量的其他系数。

mask = np.tril(df1.corr())
sns.heatmap(df1.corr(), fmt=’.1g’, annot = True, cmap= ‘cool’, mask=mask)

我已经屏蔽了左下方的值,以避免重复,并给出一个更清晰的视图。右边的价值尺度也提供了一个极端值的快速参考指南:您可以很容易地发现变量之间的高相关性和低相关性(例如,“国民收入”与“购买力”高度正相关)

在此处找到定制关联矩阵的其他方法。

最后的想法

EDA 对于理解任何数据集都至关重要。这是你可以提供见解和发现的地方。这里是你运用知识的地方。

但是 EDA 需要大量的准备工作,因为现实世界中的数据很少是干净的和同质的。人们常说数据科学家 80%的宝贵时间都花在了简单地查找、清理和组织数据上,只留下 20%的时间来实际执行分析。

同时,完美是好的敌人,你需要在有限的时间框架内驱动你的洞察力。为分析准备数据是不可避免的,而你这样做的方式将决定你的 EDA 的质量。

对这些话题感兴趣?在Linkedin Twitter 上关注我

探索性数据分析——熊猫的护照号码

原文:https://towardsdatascience.com/exploratory-data-analysis-passport-numbers-in-pandas-4ccb567115b6?source=collection_archive---------37-----------------------

探索前导零和尾随零、字母和数字的分布、常见前缀、正则表达式和数据集的随机化。

照片由 mana5280Unsplash 上拍摄

根据国际民航组织的标准,护照号码最长应为 9 个字符,可以包含数字和字母。在你作为一名分析师的工作中,你会遇到一个包含护照的数据集,你会被要求去研究它。

我最近处理过一个这样的数据集,我想与您分享这种分析的步骤,包括:

你可以和我一起完成这些步骤。从 github 获取(随机数据)。它还包含了木星笔记本的所有步骤。

基本数据集探索

首先,让我们加载数据。因为数据集只包含一列,所以非常简单。

# import the packages which will be used
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as snsdf = pd.read_csv(r"path\data.csv")
df.info()

的。info()命令将告诉我们,我们在数据集中有 10902 本护照,并且全部作为“object”导入,这意味着格式是string

复制

任何分析的第一步都应该是检查是否有重复的值。在我们的例子中,有,所以我们将使用 pandas 的.drop_duplicates()方法删除它们。

print(len(df["PassportNumber"].unique()))# if lower than 10902 there are duplicatesdf.drop_duplicates(inplace=True) # or df = df.drop_duplicates()

护照长度

通常,你继续检查最长和最短的护照。

[In]: df["PassportNumber"].agg(["min","max"])
[Out]: 
   min 000000050
   max ZXD244549
   Name: PassportNumber, dtype: object

你可能会高兴,所有的护照都是 9 个字符长,但你会被误导。数据具有字符串格式,因此最小的“字符串”值是从最大数量的零开始的值,最大的值是在开始时具有最多 zed 的值。

# ordering of the strings is not the same as order of numbers
0 > 0001 > 001 > 1 > 123 > AB > Z > Z123 > ZZ123

为了看到护照的长度,让我们看看他们的长度。

[In]: df["PassportNumber"].apply(len).agg(["min","max"])
[Out]: 
   min 3
   max 17
   Name: PassportNumber, dtype: object

根据我们最初的想法,在合同中,最短的护照只有 3 个字符,而最长的护照有 17 个字符(远远超过预期的最大长度 9 个字符)。

让我们用'len'列扩展我们的数据框,这样我们可以看看例子:

# Add the 'len' column
df['len'] = df["PassportNumber"].apply(len)# look on the examples having the maximum lenght
[In]: df[df["len"]==df['len'].max()]
[Out]: 
   PassportNumber    len
   25109300000000000 17
   27006100000000000 17# look on the examples having the minimum lenght
[In]: df[df["len"]==df['len'].min()]
[Out]: 
   PassportNumber    len
   179               3
   917               3
   237               3

3 位数的护照号码看起来可疑,但它们符合国际民航组织的标准,但最长的一个显然太长了,然而,它们包含相当多的尾随零。也许有人只是为了满足某些数据存储要求而添加了零。

让我们来看看数据样本的总体长度分布。

# calculate count of appearance of various lengths
counts_by_value = df["len"].value_counts().reset_index()
separator = pd.Series(["|"]*df["len"].value_counts().shape[0])
separator.name = "|"
counts_by_index = df["len"].value_counts().sort_index().reset_index()lenght_distribution_df = pd.concat([counts_by_value, separator, counts_by_index], axis=1)# draw the chart
ax = df["len"].value_counts().sort_index().plot(kind="bar")
ax.set_xlabel("position")
ax.set_ylabel("number of records")
for p in ax.patches:
    ax.annotate(str(p.get_height()), (p.get_x() * 1.005, p.get_height() * 1.05))

数据样本中护照长度的分布

我们看到,在我们的样本中,大多数护照号码的长度是 7、8 或 9 个字符。然而,相当多的字符长度为 10 或 12 个字符,这是意料之外的。

前导零和尾随零

也许长护照有前导零或尾随零,就像我们有 17 个字符的例子。

为了探究这些补零,让我们在数据集上再添加两列——“前导零”和“尾随零”,以包含前导零和尾随零的数量。

# number of leading zeros can be calculated by subtracting the length of the string l-stripped off the leading zeros from the total length of the stringdf["**leading_zeros**"] = df["PassportNumber"].apply(lambda x: len(x) - len(x.**lstrip**("0")))# similarly the number of the trailing zeros can be calculated by subtracting the length of the string r-stripped off the leading zeros from the total length of the stringdf["**trailing_zeros**"] = df["PassportNumber"].apply(lambda x: len(x) - len(x.**rstrip**("0")))

然后,我们可以轻松地显示超过 9 个字符的 passport,以检查是否有任何前导或尾随零:

[In]: df[df["len"]>9]
[Out]:
   PassportNumber  len  leading_zeros trailing_zeros
   73846290957     11   0             0
   N614226700      10   0             2
   WC76717593      10   0             0
   ...

零点在特定位置的分布

集合中的大多数护照没有任何零,但它们仍然长于 9 个字符。只是为了显示它,让我们来看看零在护照号码的每个位置上的分布。

我们知道最短的护照有 3 个位置,最长的有 17 个。让我们遍历所有的护照号码,然后遍历所有的字符,看看哪里是零。

passports_list = []
# for each passport number
for passport_number in df["PassportNumber"]:
    # let's create a dictionary with the passport number
    pos = {"PassportNumber": passport_number}
    # and let's add to each position if it's a zero or not
    for i, c in enumerate(passport_number):      
        # and for each position check if it's 0 -> True or something else --> False
        pos[i+1] = True if c == "0" else False
    passports_list.append(pos)# let's turn the dictionary into pandas dataframe
zeros_distribution = pd.DataFrame(**passports_list**)
zeros_distribution["len"] = zeros_distribution["PassportNumber"].apply(len)

该操作的输出是新的数据帧'zeros_distribution',其在护照号码中有零的每个位置包含

零在其出现的位置被标记为真

注意,我用黄色背景突出显示了True。熊猫的造型可以用.style的方法来做,但是你一定要小心。样式将绘制整个数据集,这可能很耗时,所以指定您想要显示多少行—例如通过sample(5)。您可以仅使用subset参数将样式应用于某些列。

# styling functiondef highlight_true(val):
    return 'background-color: yellow' if val == 1 else ''# apply the style to each value, using applymap
# subset for position columns 1-17
zeros_distribution.sample(5).style.applymap(highlight_true, subset=list(range(1,18)))

通过每个位置的零求和,我们可以看到那里出现了多少个零。通过统计该位置的所有值,我们将看到,该位置在数据集中被填充了多少次(例如位置 17 很少被填充),因为 count(N/A)为 0。我们仅通过range(1,18)在第 1–17 列上运行这些聚合

# summing the zeros at each position.
zero_count = zeros_distribution[range(1,18)].sum()
zero_count.name = "is_zero"# count how many times this position is filled in
position_filled_count = zeros_distribution[range(1,18)].count()
position_filled_count.name = "is_filled"

以表格和图表形式显示 10 位或以上位置的零分布

我们看到,除了位置 16 和 17,这些值都不是尾随零。很明显,这些值可能是前导零和尾随零的组合,但这种可能性不大。我们之前也已经看到过超过 9 个字符的护照号码的例子。

我们可以简单地.strip("0")删除前导零和尾随零,我们会看到仍然有超过 9 个字符的无效护照号码:

df[“PassportNumber”].str.strip(“0”).apply(len).value_counts().sort_index().plot(kind=”bar”)

即使我们去掉了两端的零,一些护照号码仍超过 9 个字符

用正则表达式分析字符串

要回顾字符串模式,使用正则表达式是很有帮助的。尽管它们的语法有点麻烦,但是一旦正确使用,它们会非常快速有效。

首先,让我们添加一列,如果护照号码以字母开头,则该列为真,否则为假。

df["starts_with_letter"] = df["PassportNumber"].apply(lambda x: True if re.match("**^[a-zA-Z]+.***", x) else False)

^[a-zA-Z]+.*"这个正则表达式的意思是

  • ^开始时
  • [a-zA-Z]是小写还是大写字母
  • 一次或多次
  • .后接任意字符
  • *零次或多次

我喜欢显示结果,不只是作为一个系列,而是作为一个数据帧,因为我通常想显示更多的信息,例如,计数百分比,就像以字母开头的护照一样。

start_with_letter_count = df["starts_with_letter"].value_counts()
pd.DataFrame({"count": start_with_letter_count,
             "percentage": start_with_letter_count/df.shape[0]}).style.format("{:.2%}", subset=["percentage"])

以字母开头的护照数量和百分比

人们可能还会对护照号码的开头有字母或者中间也有字母(至少在一个号码之后)感兴趣。这也可以通过正则表达式轻松解决:

  • ^开始时
  • .是某物(字母、数字或字符)
  • *零次或多次
  • \d然后是一个数字
  • +一次或多次
  • [a-zA-Z]小写或大写字母
  • +一次或多次
df["letter_after_number"] = df["PassportNumber"].apply(lambda x: "Leter after number" if re.match("^.*\d+[a-zA-Z]+", x) else "trailing numbers only"

如果我们使用与上面相同的代码显示计数和百分比,我们会看到大多数护照中间没有任何字母。

在我们的数据集中,不到 10%的护照号码中间有一个字母

另一个问题是,当我们为我们的系统设计护照模式时,字符串的开头可以出现多少个字母。为了找出答案,让我们使用 regex 设计一个简单的函数。

def **lenght_of_start_letter_sequence**(string):
    # re.match returns None in case no match is found, and applying of .group(0) would lead to an error
    if re.match("**^[a-zA-Z]+**", string) is not None:
        return len(re.match("^[a-zA-Z]+", string).group(0))
    else:
        return 0

^[a-zA-z]+表示开头有一个或多个字母。re.match.group(0)方法返回第一个匹配这个正则表达式的组。所以在 T3 的情况下,它将返回 T4,我们将简单地计算它的长度。唯一的问题是,如果不符合模式,re.match返回Nonegroup()失败,所以我们必须在护照只包含数字的情况下处理它。

一旦我们有了自己的函数,就只需要apply将它添加到 passportNumber 列:

df["lenght_of_start_letters"] = df["PassportNumber"].apply(**lenght_of_start_letter_sequence**)

在这种情况下,我们不显示统计数据,而是根据开头字母序列的长度列出值:

df.sort_values(by="lenght_of_start_letters", ascending=False).loc[df["lenght_of_start_letters"]>0,["PassportNumber","lenght_of_start_letters"]]

开头最长的字母序列是 6 个字母,有些护照有 4 个

有时会要求您提供示例值,这很容易通过sample()方法完成。

# 5 examples of the passports which start with 3 letters
[In]: df[df["lenght_of_start_letters"]==3]["PassportNumber"].sample(5).to_list()[Out]: ['DZO795085', 'SNJ370118', 'UJR13307234', 'DSG229101', 'VAA353972']

常见前缀

有可能是数据供应商为这些值添加了一个前缀,而这个前缀最初并不包含在护照号码中。在大多数情况下,这样的前缀必须删除。但是怎么找呢?

让我们首先假设,我们的前缀有 3 个字符。然后,我们可以轻松地slice前 3 个字符和value_counts来查看哪些是最常见的:

[In]: df["PassportNumber"].str.slice(stop=3).value_counts()
[Out]: 
   000 41
   009 37
   005 33

我们可以看到,值的出现频率比 3 个字母前缀的平均出现频率高得多(应用于上面代码的.mean()是 2.3)。009 出现了 37 次,比 2.3 次多得多。

您可以展开您的分析,并检查前缀是否仅出现在特定长度。我们可以假设有 12 个字符的护照至少有 3 个字母前缀。以下代码将掩饰前缀932在 12 个字符长的护照中比通常更常见。

c = df[["PassportNumber", "len"]]
c["prefix"] = c["PassportNumber"].str.slice(stop=3)# group by both prefix and the full length of the passport
prefix_by_length_df = c.**groupby(["prefix", "len"]).size()**.reset_index()prefix_by_length_df[prefix_by_length_df["len"]==12].sort_values(by=0, ascending=False)

932出现了 27 次,而 12 个字符长的平均值仅为 1.3。您可以重新运行这些简单的大小计数,以更短或更长的前缀或限制任何其他特征。

随机化护照

如果您有敏感数据,但需要与数据科学家共享,大多数客户会同意共享匿名数据或随机样本。在我们的例子中,让我们尝试简单的随机化,它:

  • 保留前导零和尾随零
  • 将任意数字更改为随机数
  • 将任意字母更改为随机字母
import random
import string def **passport_randomizer**(list_of_passports):
    processed = {} # dictionaly which will keep the {"old": "new value"} # loop through all the values
    for p in list_of_passports:
        leading_zeros_up_to = len(p) - len(p.lstrip("0"))
        trailing_zeros_up_to = len(p) - (len(p) - len(p.rstrip("0")))
        out = []
        for i, c in enumerate(p):
            # keep the leading and trailing zeros intact
            if i < leading_zeros_up_to or i >= trailing_zeros_up_to:
                out.append(c)
            # then change any number to a random number
            elif c.isnumeric():
                out.append(str(random.randint(0,9)))
            # finally the rest for a random letter
            else:
                out.append(random.choice(string.ascii_letters).upper())
        processed[p] = "".join(out)
    return processed

这样的函数一个字符一个字符地把值改成新的随机值。结果是一本字典{"old value": "new value"}

{'0012300': '0050100',
 'ABC': 'LNZ',
 '00EFG': '00AQT',
 'IJK00': 'KVP00',
 '012DF340': '032DT030'}

这种方法为随机化数据的分析保留前导/尾随零,但是,它会重置任何重复的前缀,这样的方法会更复杂,您可以在家里尝试。

有了旧值到新值的映射,我们可以简单地将它.map()到护照列表,以获得一个新的随机列表,它保留了我们原始集合的大部分特征。

df["PassportNumber"].map(passport_randomizer(df["PassportNumber"].unique())).to_csv(r"new_data.csv", index=False, header=True)

该功能应用于.unique()护照,以避免相同的复制护照变成两个不同的新随机值。两本不同的护照仍然有可能变得一样。为了避免这种风险,在创建每个新字符串的过程中,您必须检查新的随机值是否还没有在现有的映射中使用。

结论

在本文中,我们回顾了如何分析字母数字字符串列表来揭示它们的模式。我们使用的技术是每个数据分析数据清理程序中最常见的要求,可以调整以涵盖大量类似的任务,如:

  • 计算绳子的长度
  • 检查前导零和尾随零
  • 检查特定字符(零)在特定位置的出现
  • 查看有多少值以字母开头
  • 复习有多少个字母在中间
  • 分析是否有共同的前缀
  • 随机化数据集

您可以随意使用这些数据:

[## 阅读熊猫 CSV 时处理多余的空格

为什么我们关心空白?内置熊猫功能,自定义处理。创建 1M 测试数据和…

towardsdatascience.com](/dealing-with-extra-white-spaces-while-reading-csv-in-pandas-67b0c2b71e6a)

使用 Dora 进行探索性数据分析

原文:https://towardsdatascience.com/exploratory-data-analysis-using-dora-ac596e5a32a6?source=collection_archive---------28-----------------------

自动化探索性数据分析和数据建模

威廉·艾文在 Unsplash 上的照片

探索性数据分析是最关键的部分,无论我们何时处理数据集,都要从它开始。它允许我们分析数据,并从数据中获得初步发现。EDA 是一种方法,在这种方法中,我们使用不同的方法主要是可视化来总结数据的主要特征。在建模之前,应该对数据进行分析、清理和清楚地理解,以使建模成功。

EDA 花费了我们大部分时间,因为我们必须清楚地分析数据,以便我们可以清理数据,并在建模前对数据进行可视化预处理。这就是为什么 EDA 是最重要的,但我们可以通过自动化所有的 EDA 工作来节省时间,并可以在建模中使用节省的时间。

Dora 是一个开源的 python 库,用于自动化探索性数据分析的痛苦部分。它只是清理你的数据,让你可视化,不仅如此,它还让你准备你的数据建模的目的。

在本文中,我们将探索 dora,看看它为我们提供了哪些不同的功能。

安装:

我们可以通过在 anaconda 命令提示符下运行下面给出的命令来轻松安装 dora。

pip install dora

实施:

答:导入所需的库

我们将从导入所需的库开始。我们将导入 pandas 以加载数据集,并导入 Dora 用于 EDA 目的。

import pandas as pd
from Dora import Dora
dora = Dora()  #Intialize dora

b .加载数据集

我们将在这里使用的数据集是一个跨国公司的广告数据,它包含不同的属性,如“销售”、“电视”等。其中“销售”是因变量,其他属性是特征变量。加载数据集后,我们将把它传递给 dora,以创建一个 dora 数据集并定义目标变量。

df = pd.read_csv('Advertising.csv')
dora.configure(output = 'Sales', data = df)
dora.data

广告数据集

c.数据预处理

Dora 允许我们用该列的平均值估算空数据,也允许我们缩放数据。这里我们将估算空数据,如果有的话。

dora.impute_missing_values()

在此之后,我们将可视化数据,看看数据试图告诉我们什么。对于可视化,我们可以一次完成整个数据集,也可以选择想要可视化的列。

dora.plot_feature('TV') #Visualizing Single Feature

特征和目标变量图

dora.explore()  #Visualizing all feature variable

所有特征变量与目标变量的关系图

接下来,我们将看看 dora 用于建模的数据拆分功能。默认情况下,dora 将数据分割成 80:20 的比例。

d .数据建模

这里我们将拆分数据,使用 sklearn 创建机器学习模型。

dora.set_training_and_validation() #Splitting the data
#Linear Regression Model
from sklearn.linear_model import LinearRegression
X = dora.training_data[dora.input_columns()]
y = dora.training_data[dora.output]

上面给出的命令将让我们为正在使用的数据集创建线性回归模型,现在让我们拟合数据并打印模型的分数以显示其准确性。

reg = LinearRegression().fit(X, y)
reg.score(X, y)

模型精度

同样,我们也可以创建其他模型,如下面我创建的岭回归模型。

from sklearn.linear_model import Ridge
clf = Ridge(alpha=1.0)
a = clf.fit(X, y)
a.score(X,y)

模型精度

结论:

在本文中,我们首先加载数据,并通过将目标变量传递为“Sales”来将数据传递给 Dora,然后我们对数据执行操作,以对其进行分析和可视化。最后,我们对数据进行预处理,创建不同的机器学习模型。

[## 使用 TA-Lib 对股票进行技术分析

技术分析 python 库

towardsdatascience.com](/technical-analysis-of-stocks-using-ta-lib-305614165051) [## 自动化机器学习

使用 H2O 汽车公司自动化机器学习

towardsdatascience.com](/automating-machine-learning-a486d365e423)

在你走之前

感谢 的阅读!如果你想与我取得联系,请随时通过 hmix13@gmail.com 联系我或我的 LinkedIn 个人资料 您还可以查看我的Git hub关于数据科学不同项目的简介。

使用 PandasGUI 进行探索性数据分析

原文:https://towardsdatascience.com/exploratory-data-analysis-using-pandasgui-8c119c3447ea?source=collection_archive---------32-----------------------

用于分析熊猫数据帧的图形用户界面

威廉·艾文在 Unsplash 上的照片

探索性数据分析是最关键的部分,无论我们何时处理数据集,都要从它开始。它允许我们分析数据,并让我们探索数据的初始发现,如有多少行和列,不同的列是什么,等等。EDA 是一种方法,在这种方法中,我们使用不同的方法主要是可视化来总结数据的主要特征。

如果您正在处理数据,EDA 是一个重要且最关键的步骤。探索数据并找出它的全部内容几乎占据了项目总时间的 30%。EDA 允许我们并告诉我们如何在建模前预处理数据。这就是为什么 EDA 是最重要的,但我们可以通过自动化所有时间采取 EDA 工作来节省时间,并可以在建模中使用节省的时间。

Pandasgui 是一个开源 python 模块/包,它创建了一个 gui 界面,我们可以在其中分析 pandas 数据帧并使用不同的功能来可视化和分析数据并执行探索性数据分析。

在本文中,我们将探索 Pandasgui,并了解如何使用它来自动化探索性数据分析过程,并节省我们的时间和精力。

安装 Pandasgui

像任何其他库一样,我们可以使用 pip 安装 pandasgui。

pip install pandasgui

正在加载数据集

pandasgui 中预定义了大量数据集。我们将使用 pandasgui 加载一个名为“IRIS”的数据集,这是一个非常著名的数据集,并将使用 pandasgui 的 gui 界面来探索它。我们还将导入“show”函数,它将数据集加载到 GUI 中。

from pandasgui.datasets import iris
#importing the show function
from pandasgui import show

创建界面

现在,我们只需要通过将数据集名称作为参数传递来调用 show 函数,它将启动一个 GUI,我们可以在其中浏览它的不同部分,并尝试浏览数据集的不同属性。

show(iris)

PandasGUI 的主屏幕(来源:作者)

在这里,您可以看到 show 功能启动了 GUI,我们可以清楚地看到包含不同功能的不同选项卡。

让我们分析一下这个界面的不同部分。

1。数据帧

数据框架(来源:作者)

在这一节中,我们可以清楚地分析不同的属性是什么,它包含哪些值。我们可以清楚地分析所有的值和属性。在左侧,我们还可以看到数据帧的形状。

2.过滤

过滤器(来源:作者)

在这一部分,我们可以应用不同的过滤器来分析数据。我们可以简单地输入我们想要运行的查询并应用过滤器。你可以查看这个链接,了解熊猫中的查询。

3.统计数字

统计数据(来源:作者)

该部分类似于熊猫数据帧的描述功能。它帮助我们分析数据集的统计属性。

4.Grapher

塑造者(来源:作者)

这是最重要的部分,在这里我们可以清楚地看到不同类型的可视化,我们可以使用界面创建,并节省我们为每个可视化编写代码的努力。在上图中,我已经为萼片长度和萼片宽度创建了散点图。类似地,您可以通过拖放 x、y 和其他参数中的列名来创建不同的可视化。

5.整形器

整形者(来源:作者)

在本节中,我们可以通过应用不同的函数和更改数据集的形状来分析数据集。提供的两种形状格式是“枢轴”和“熔化”。我们可以在不同的函数中拖放列,并相应地分析数据集的不同形状。

这是 PandasGUI 提供的 5 个部分,通过它们我们可以分析 pandas 数据帧并在任何给定的数据集上执行 EDA。PandasGUI 是一个有用的工具,因为它减少了反复编写代码的工作量,还节省了时间。

同样,您可以使用不同的数据集探索 PandasGUI。试试吧,在这篇文章的回复中让我知道你的经历。

在你走之前

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

用一行 Python 代码进行探索性数据分析

原文:https://towardsdatascience.com/exploratory-data-analysis-with-1-line-of-python-code-5fe25387c84b?source=collection_archive---------20-----------------------

熊猫概述-特征库

图片来自 Pixabay佩吉和马可·拉赫曼-安克

探索性数据分析(EDA)是一种分析数据并总结其主要特征的方法。一个人花很多时间做 EDA 来了解数据。

EDA 涉及许多步骤,包括一些统计测试、使用不同类型的图来可视化数据等等。EDA 的一些步骤讨论如下:

  • 数据质量检查:可以使用describe()info()dtypes()等熊猫库函数来完成。它用于查找多个特征、其数据类型、重复值、缺失值等。
  • 统计测试:进行一些统计测试,如皮尔逊相关、斯皮尔曼相关、肯德尔测试等,以获得特征之间的相关性。它可以使用 stats 库在 python 中实现。
  • 定量检验:一些定量检验用于发现数字特征的扩散,计数分类特征。它可以使用 pandas 库的函数在 python 中实现。
  • 可视化:特征可视化对于理解数据非常重要。条形图、饼图等图形技术用于了解分类特征,而散点图、直方图则用于数字特征。

为了执行上述任务,我们需要输入几行代码。这里 pandas-profiling 开源库发挥了作用,它可以使用一行代码执行所有这些任务。使用 pandas-profiling 的 EDA 结果可以显示在 jupyter 笔记本中,也可以转换为 HTML 页面。

安装:

安装 Pandas-profiling 库的方法:

  • 直接从 GitHub repo 安装 pandas-profiling 库到 jupyter 笔记本;
! pip install [https://github.com/pandas-profiling/pandas-profiling/archive/master.zip](https://github.com/pandas-profiling/pandas-profiling/archive/master.zip)
  • 使用 pip 安装 pandas-profiling 库:
pip install pandas-profiling
  • 使用 conda forge 命令安装 pandas-profiling 库:
conda install -c conda-forge pandas-profiling

导入包:

要将 pandas-profiling 库用于 EDA,我们需要导入所需的必要库:

import pandas as pd
import numpy as np
from pandas_profiling import ProfileReport

用一行 Python 代码实现 EDA:

profile = ProfileReport(pd.read_csv('titanic.csv'),explorative=True)

是的,就是这样,你完成了探索性的数据分析。结果可以在 jupyter notebook 或 google colab 中观察到,或者文件可以保存为 HTML 格式并在网络浏览器中使用。

#to view result in jupyter notebook or google colab
profile.to_widgets()# to save results of pandas-profiling to a HTML file
profile.to_file("EDA.html")

Titanic 数据集的 EDA 的一些结果:

使用 pandas-profiling 库进行探索性数据分析的数据集是从 Kaggle 下载的

(作者代码)

(作者 GIF)

结论:

我更喜欢使用几个 python 库,通过自定义函数来完成我的 EDA。

对于初学者来说,在尝试这个库之前,使用熊猫库开始做 EDA 并编写 python 代码是很好的,因为更重要的是具备基础知识和编程实践。

你可以在这里获得熊猫档案库的文档和用法。 sweetvizpandas-profiling 的一个备选库,给出了类似的结果。

感谢您的阅读

电影的探索性数据分析

原文:https://towardsdatascience.com/exploratory-data-analysis-with-movies-3f32a4c3f2f3?source=collection_archive---------25-----------------------

对制作大片和获奖电影的标准的调查

GR Stocks 在 Unsplash 上的照片

作为熨斗学校训练营要求的一部分,我们需要在每个学习模块结束时完成一个项目,展示我们应用所学知识的能力。

第一个项目的提示如下:

微软想进入电影行业,但是他们之前没有这个行业的知识,他们需要帮助,这样他们的电影工作室才能成功。

执行电影行业探索性数据分析(EDA)所需的主要技能包括:在 pandas 数据帧中进行网络搜集、存储和清理数据,以及使用 seaborn 和 matplotlib 进行数据可视化。我将描述一些我用于网络抓取和清理的方法,我将回顾一些我们为了成为一个成功的电影工作室而提出的建议。

网络抓取

在训练营开始之前,我对网络抓取并不熟悉,但我可以毫无疑问地说,这是我在过去几周里学到的最有用、最有趣的技能之一。Web 抓取本质上是查看网页的 HTML 并解构该 HTML 以便您可以提取相关信息进行分析的过程。通过使用请求和漂亮的汤库,我们可以很容易地把所有的 html 放进一个 Jupyter 笔记本中,并开始把它们分开。我们用来开发推荐的一些网站是 moviefone.com 的、imdb.com 的boxofficemojo.com 的。例如,这个页面有 2019 年上映的电影的电影上映日期,所以我最终编写了这样的代码:

movies_= requests.get("https://www.moviefone.com/movies/2019/?     page=1")
soup = BeautifulSoup(movie_dates_page.content,'lxml')
movie_title = soup.find_all("a", class_="hub-movie-title")

然后,我简单地使用movie_title变量中每个元素的.text方法,就可以将网页上的每个电影标题放到一个列表中。我使用与上面所示类似的方法将所有发布日期放入一个列表中。然后,可以将这两个列表放入一个数据帧中,并使用 datetime 库来操作日期列,这样我们就可以计算某个月或某一天上映的电影数量。数据帧的构造如下所示:

movie_dict = {'movies':movie_list, 'release_date':dates_list}
dates_df = pd.DataFrame(data=movie_dict)#movie_list and dates_list are previously constructed lists from #webscraping

对于这个特定的项目,最简单的方法是确定网页的哪些元素对 EDA 最有用,然后定义一个函数来抓取这些元素并构建数据帧。抓取网页时,一个很好的经验法则是在抓取每个页面之间使用睡眠计时器。重复呼叫网页可能会面临被网站禁止的风险,因为这些重复呼叫会导致大量流量。

数据清理

从不同的网页抓取各种数据并将数据编译成数据帧后,下一步是清理数据。幸运的是,许多网站以一种使清理相对简单的方式来组织他们的电影数据。使用类似于.replace()的字符串方法被用来从预算和利润中删除逗号和美元符号,这样.astype() pandas 方法可以用来将数字从字符串转换成整数。

使用上面描述的电影日期数据帧的示例,使用日期时间库创建的新列如下所示:

import datetime as dtdates_df['release_date'] = pd.to_datetime(movie_releases_df['release_date'], format='%B **%d**, %Y')dates_df['release_month'] = dates_df['release_date'].map(**lambda** z: z.strftime('%B'))

dates_df['release_day'] = dates_df['release_date'].map(**lambda** z: z.strftime('%A'))dates_df['release_year'] = dates_df['release_date'].map(**lambda** z: z.strftime('%Y'))

dates_df['release_year'] = dates_df['release_year'].astype(int)

最难清理的是在维基百科页面上清理一个表格,其中包含电影、奥斯卡提名以及随后获奖的数据。虽然提名和奖项的数量被列在各自独立的栏目中,但也有特定条目有脚注的情况,被美丽的汤认为是文本。只有 11 部电影出现了脚注,所以在数据帧中手动纠正并不是很大的负担。然而,值得注意的是,您应该留意杂乱的数据,以便您可以开发适当的方法来清理这些数据。如果有数百或数千行,那么就需要一个更健壮的解决方案,这样您就不必手动逐行清理数据。

成功电影制片厂的推荐

我们决定为这个项目解决几个问题,我会在下面给那些想看完整个项目的人留一个我的 GitHub repo 的链接。我将讨论这个博客的两个问题/建议。

问题 1:拍一部成功的电影应该花多少钱?

为了回答这个问题,我们选择只查看利润大于零的电影数据。所有预算、收入和利润都使用 3.22%的平均通货膨胀率进行了调整。利用 seaborn,我创建了一个散点图,看看我是否可以确定任何趋势。

作者图片

从上面的图中我们可以看到,趋势线是正的,这让我们相信,如果我们花的钱比我们能赚的钱多。但是,仅凭这个情节,不足以做出认定。下图显示了盈利电影的预算和利润率。在这里,散点图显示了一个消极的趋势线,警告不要花太多的钱,因为你有降低利润率的风险。

调整后的预算与盈利电影的利润率(图片由作者提供)

那么,我们是如何决定一个合适的搬迁预算的呢?我们决定查看有史以来最赚钱的 25 部电影的利润率,并将利润率的中位数作为成功的目标。我们选择使用中位数是因为存在极端的异常值,这将使平均值作为集中趋势的衡量标准不太可靠(《泰坦尼克号》、《阿凡达》和《复仇者联盟 4:终局之战》对于首次涉足电影业的公司来说是不切实际的目标)。

作者图片

我们发现利润率的中位数是 0.84,我们选择推荐在一部电影上花费 82,500,000 美元,因为这与利润率大约为 0.8 相关。82,500,000 美元的预算远远低于前 25 部最赚钱电影的预算(这些预算约为 2 亿美元)。因此,我们认为,在制作一部利润率可以与一些最成功的电影相媲美的电影时,花费少得多是可能的。

问题二:哪些演员和导演给一部电影带来的价值最大?

如果我们知道我们应该在一部电影上花多少钱,那么显而易见,我们也应该知道我们应该雇佣谁来出演和导演这部电影,这样我们就可以实现利润最大化。为了确定谁给一部电影带来了最大的价值,我们创建了自己的统计数据,称为价值高于替代(VAR)。对于棒球迷来说,这是我们自己淡化的战争统计数据。VAR 背后的数学很简单:如果所有电影的平均净利润是 100 美元,而《演员:X》的平均净利润是 200 美元,他/她就会有 2 的 VAR。这个数字是平均值的 X 倍。我们对演员使用了 10 部电影的最小值,对导演使用了 5 部电影的最小值。

用于计算 VAR 的电影数据来自 imdb.com,我们用于计算 VAR 的代码如下:

actor_counts = actors_df['value'].value_counts()
actor_list = actor_counts[actor_counts >= 10].index.tolist()
actors_df = actors_df[actors_df['value'].isin(actor_list)]actor_total = actors_df.groupby(['value'],  as_index=False)['Net Profit'].mean().sort_values(by='Net Profit', ascending=False)actor_total['VAR'] = (actor_total['Net Profit']/actor_total['Net Profit'].mean())

作者图片

作者图片

我们看到,与演员相比,导演往往有更高的 var,这无疑有助于确定您应该如何为您的人员编制预算。

我们还探讨了其他主题:

  1. 拍一部奥斯卡获奖电影应该花多少钱?
  2. 你应该在一年中的什么时候发行电影?
  3. 哪些流派最赚钱?
  4. 我们应该效仿哪些工作室的最佳实践?

后续步骤

考虑到我们在这个项目中只使用了探索性的数据分析,我们可以采取更多的步骤来尝试并做出更准确的建议。我们收集的数据量当然可以让我们尝试并创建一些线性回归模型,根据一个或多个输入来尝试和预测利润。我们可以使用简单的线性回归来预测基于预算的利润,或者使用多元线性回归并选择几个输入,如预算、发行月份、演员、导演和获奖。像发行月份、演员和导演这样的变量是分类变量,而预算是连续变量。显然,通过查看上面的散点图,我们首先需要确保我们的数据在试图建立模型之前满足某些假设。研究和收集有关流媒体服务的数据,看看流媒体与传统票房相比是否有更大的回报,也将是有益的。大流行后的票房数据肯定会被证明是有价值的,因为电影公司正试图将自己与未来的经济衰退隔离开来,从而做出恢复或调整的决定。

对于理解数据科学过程的开端和练习各种编码技能来说,这是一个很好的项目。一旦我掌握了更多的数据科学技能,我一定会重新查看这些数据,这样我就可以在之前的建议基础上进行改进和完善!

GitHub:https://GitHub . com/Jeremy-lee 93/DSC-mod-1-project-v2-1-on l01-dtsc-pt-052620

Youtube 演示:【https://youtu.be/C9YgIYwHaIQ

探索性数据分析与熊猫概况

原文:https://towardsdatascience.com/exploratory-data-analysis-with-pandas-profiling-de3aae2ddff3?source=collection_archive---------2-----------------------

Pandas profiling 是一个开源的 Python 模块,使用它我们只需几行代码就可以快速进行探索性的数据分析。此外,如果这还不足以说服我们使用这个工具,它还可以生成 web 格式的交互式报告,可以呈现给任何人,即使他们不懂编程。

简而言之,熊猫概况分析所做的是为我们省去可视化和理解每个变量分布的所有工作。它会生成一份报告,其中包含所有易于获取的信息。

一幅画胜过千言万语

为了向您清楚地展示它是如何工作的,这是一个由 pandas profiling 生成的报告示例:

点击此链接,您还可以查看互动报道

生成的报告的优点之一是在开始时出现警告。它告诉我们包含 NaN 值的变量,有许多零的变量,有高基数的分类变量,等等。

告诉我们警告的部分

如何使用熊猫概况

第一步是用这个命令安装它:

pip install pandas-profiling

然后,我们使用以下命令生成报告:

from pandas_profiling import ProfileReport
prof = ProfileReport(df)
prof.to_file(output_file='output.html')

我们到了,就这么简单。我们可以看到在 output.html 文件中生成的报告。

熊猫剖析劣势

pandas profiling 的主要缺点是使用大型数据集。随着数据量的增加,生成报告的时间也增加了很多。

解决这个问题的一个方法是仅从我们拥有的所有数据的一部分生成报告。确保选择用于生成报告的数据代表我们拥有的所有数据是很重要的,例如,前 X 行数据可能只包含一个类别的数据。在本例中,我们希望随机排列数据,并选择一个有代表性的样本。

代码示例:

from pandas_profiling import ProfileReport
#We only use the first 10000 data points
prof = ProfileReport(df.sample(n=10000)) 
prof.to_file(output_file='output.html')

另一种选择是使用最小模式,这是在熊猫档案的 2.4 版本中引入的。您可以使用以下命令检查您安装了哪个版本:

pandas_profiling.version.__version__

在最小模式下,生成的简化报告包含的信息比完整报告少,但对于大型数据集,生成该报告的速度相对较快。这是要使用的代码:

profile = ProfileReport(df, minimal=True)
profile.to_file(output_file="output_min.html")

利用测井数据进行探索性数据分析

原文:https://towardsdatascience.com/exploratory-data-analysis-with-well-log-data-98ad084c4e7?source=collection_archive---------18-----------------------

卢卡斯·布拉塞克在 Unsplash 上的照片

一旦数据经过整理和分类,数据科学过程的下一步就是进行探索性数据分析(EDA)。这一步使我们能够识别数据中的模式,了解特征(测井记录)之间的关系,并识别数据集中可能存在的异常值。在这一阶段,我们了解数据,并检查是否需要进一步处理或是否需要清理。

作为岩石物理学家/地球科学家,我们通常使用测井图、直方图和交会图(散点图)来分析和探索测井数据。Python 提供了一个很好的工具集,可以快速简单地从不同的角度可视化数据。

在本文中,我们将使用由 XeekFORCE 发布的数据集子集,作为从测井曲线预测相的竞赛的一部分。我们将混合使用 matplotlibseabornmissingno 数据可视化库来可视化数据。

我们将涵盖的观想将允许我们:

  • 确定我们哪里有或没有数据
  • 了解数据分布
  • 可视化受恶劣井眼条件影响的数据

这篇文章的笔记本可以在我的 Python 和 Petrophysics Github 系列中找到,可以通过下面的链接访问:

https://github.com/andymcdgeo/Petrophysics-Python-Series

加载数据和库

任何项目的第一步都是加载所需的库和数据。

对于这项工作,我们将调用一些绘图库:seaborn、matplotlib 和 missingno 以及 math、pandas 和 numpy。

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import math
import missingno as msno
import numpy as np

我们正在使用的数据集是 Xeek 和 FORCE(https://xeek.ai/challenges/force-well-logs/overview)举办的机器学习竞赛的一部分。竞赛的目的是从由 98 口训练井组成的数据集中预测岩性,每口井的测井完整性程度不同。目的是根据测井测量预测岩相。

为了使本文中的图和数据易于管理,我使用了来自训练数据的 12 口井的子集。数据已经整理成一个单一的 csv 文件,无需担心曲线记忆。为了加载数据子集,我们可以调用pd.read_csv.

data = pd.read_csv('Data/xeek_train_subset.csv')

加载数据后,我们可以使用以下命令确认井的数量和名称:

# Counts the number of unique values in the WELL column
data['WELL'].nunique()

它将返回预期的 12。

# Gets the unique names from the WELL column
data['WELL'].unique()

这将返回一个带有井名的数组对象。

array(['15/9-13', '15/9-15', '15/9-17', '16/1-2', '16/1-6 A', '16/10-1', '16/10-2', '16/10-3', '16/10-5', '16/11-1 ST3', '16/2-11 A', '16/2-16'], dtype=object)

找出差距

测井中的数据缺失可能是由多种原因造成的,包括:

  • 工具故障和问题
  • 选择遗漏(即由于预算限制,工具没有运行)
  • 人为误差
  • 复古数据集
  • 钻孔环境产生的问题

我们可以使用多种方法来确定哪里有数据,哪里没有。我们将看两种方法来可视化丢失的数据。

缺少图书馆

我们要看的第一种方法是使用 missingno 库,它提供了一个很好的小工具箱,由 Aleksy Bilgour 创建,作为一种可视化和理解数据完整性的方法。更多关于图书馆的细节可以在 https://github.com/ResidentMario/missingno找到。如果您没有这个库,您可以使用pip install missingno将它快速安装到您的终端中。

missingno 工具箱包含许多不同的可视化,但是对于本文,我们将重点关注矩阵图和条形图。矩阵图可通过以下方式调用:

msno.matrix(data)

将 matplotlib.pyplot 作为 plt 导入

这向我们展示了所有特征的数据密度(对数曲线)。在图的右边是迷你图,它显示了数据中最大或最小的零值。

从可视化我们可以看到,只有少数几个列是完整的井,深度 _MD,GR,组,岩相 _ 岩性和岩相 _ 置信度。

其他国家有接近完整的数据值,如 DTC、CALI 和 ROP,而一些国家的数据覆盖范围很小,如 RMIC、SGR 和 ROPA。

我们还可以调用msno.bar(data)来生成一个条形图,显示每一列中非空数据值的数量(对数曲线)。沿着图表的顶部,我们得到了非空数据值的总数。

msno.bar(数据)中缺少数值计数。

快速浏览一下矩阵图和条形图,就可以知道数据集中缺少什么数据,尤其是在数据集中有大量列的情况下。

使用 Matplotlib 可视化缺失数据

在我的上一篇文章:使用 Matplotlib 可视化油井数据覆盖范围中,我介绍了一种逐井查看数据覆盖范围的方法。这使您可以看到关键曲线中的缺口。

如果我们想为每口井的所有曲线生成一个图,我们可以将每条曲线保存为一个文件,或者将数据显示在一个单独的列中。在这个例子中,我只选择了岩石物理学家可能在解释中使用的普通曲线。

data_nan = data[['WELL','DEPTH_MD','CALI', 'BS', 'GR', 'NPHI', 'RHOB', 'PEF', 'RDEP', 'RMED', 'DTC', 'DTS']].copy()
for num, col in enumerate(data_nan.columns[2:]):
    data_nan[col] = data_nan[col].notnull() * (num + 1)
    data_nan[col].replace(0, num, inplace=True)
    print(col, num) #Print out the col name and number to verify it works
grouped = data_nan.groupby('WELL')#Setup the labels we want to display on the x-axis
labels = ['CALI', 'BS', 'GR', 'NPHI', 'RHOB', 'PEF', 'RDEP', 'RMED', 'DTC', 'DTS']#Setup the figure and the subplots
fig, axs = plt.subplots(4, 3, figsize=(20,20))#Loop through each well and column in the grouped dataframe
for (name, df), ax in zip(grouped, axs.flat):
    ax.set_xlim(0,9)

    #Setup the depth range
    ax.set_ylim(5000, 0)

    #Create multiple fill betweens for each curve# This is between
    # the number representing null values and the number representing
    # actual values

    ax.fill_betweenx(df.DEPTH_MD, 0, df.CALI, facecolor='grey')
    ax.fill_betweenx(df.DEPTH_MD, 1, df.BS, facecolor='lightgrey')
    ax.fill_betweenx(df.DEPTH_MD, 2, df.GR, facecolor='mediumseagreen')
    ax.fill_betweenx(df.DEPTH_MD, 3, df.NPHI, facecolor='lightblue')
    ax.fill_betweenx(df.DEPTH_MD, 4, df.RHOB, facecolor='lightcoral')
    ax.fill_betweenx(df.DEPTH_MD, 5, df.PEF, facecolor='violet')
    ax.fill_betweenx(df.DEPTH_MD, 6, df.RDEP, facecolor='darksalmon')
    ax.fill_betweenx(df.DEPTH_MD, 7, df.RMED, facecolor='wheat')
    ax.fill_betweenx(df.DEPTH_MD, 8, df.DTC, facecolor='thistle')
    ax.fill_betweenx(df.DEPTH_MD, 9, df.DTS, facecolor='tan')

    #Setup the grid, axis labels and ticks
    ax.grid(axis='x', alpha=0.5, color='black')
    ax.set_ylabel('DEPTH (m)', fontsize=14, fontweight='bold')

    #Position vertical lines at the boundaries between the bars
    ax.set_xticks([1,2,3,4,5,6,7,8,9,10], minor=False)

    #Position the curve names in the centre of each column
    ax.set_xticks([0.5, 1.5 ,2.5 ,3.5 ,4.5 ,5.5 ,6.5 , 7.5, 8.5, 9.5], minor=True)

    #Setup the x-axis tick labels
    ax.set_xticklabels(labels,  rotation='vertical', minor=True, verticalalignment='bottom')
    ax.set_xticklabels('', minor=False)
    ax.tick_params(axis='x', which='minor', pad=-10)

    #Assign the well name as the title to each subplot
    ax.set_title(name, fontsize=16, fontweight='bold')plt.savefig('missingdata.png')
plt.tight_layout()
plt.subplots_adjust(hspace=0.15, wspace=0.25)
plt.show()

多孔图,显示数据集中每孔的数据覆盖率。

从生成的图像中,我们可以确定主曲线中的缺口在哪里。从这个图中,我们可以确定哪些井对机器学习建模或进一步研究有用。

熟悉数据

在本节中,我们将研究一些可视化技术,这些技术可用于深入了解我们的数据,以及这些数据与岩相和地质地层学的关系。

Python 世界有多个绘图和数据可视化库。其中之一是 Seaborn ,这是一个建立在 matplotlib 之上的数据可视化库,并与 pandas 密切合作。它提供了一种简单且更直观的方式来显示和浏览您的数据。

以下示例说明了一些不同的绘图,可用于混合使用 FacetGrid 和 matplotlib 来快速了解数据。

密度——岩性的中子分布

与 matplotlib 相比,FacetGrid 提供了一种使用少量代码在网格中绘制多个子情节的更简单、更流畅的方法。

在使用我们的数据之前,我们首先必须将数字岩性数据转换成实际的描述性标签。这可以通过使用一个简单的字典来实现:

lithology_numbers = {30000: 'Sandstone',
                 65030: 'Sandstone/Shale',
                 65000: 'Shale',
                 80000: 'Marl',
                 74000: 'Dolomite',
                 70000: 'Limestone',
                 70032: 'Chalk',
                 88000: 'Halite',
                 86000: 'Anhydrite',
                 99000: 'Tuff',
                 90000: 'Coal',
                 93000: 'Basement'}

在我们的数据框架中,我们创建了一个名为 LITH 的新列,并使用 map 函数快速获得描述性标签,如下所示:

data['LITH'] = data['FORCE_2020_LITHOFACIES_LITHOLOGY'].map(lithology_numbers)

对于第一个 FacetGrid,我们将按地层查看密度-中子数据。当我们这样做时,我们只需要指定几行代码,而不是多行或使用 for 循环,就像上面的例子中看到的丢失数据一样。我们可以用密度中子数据初始化 FacetGrid,并将 LITH 列传入col参数。此外,我们可以使用col_wrap参数来指定网格中需要多少列。

最后,由于我们熟悉使用特定比例在交会图上处理密度-中子数据,我们需要相应地设置xlimylim,否则它将自动缩放。

g = sns.FacetGrid(data, col='LITH', col_wrap=4)
g.map(sns.scatterplot, 'NPHI', 'RHOB', alpha=0.5)
g.set(xlim=(-0.15, 1))
g.set(ylim=(3, 1))

根据岩性划分密度-中子数据面网格。

这将生成一系列按岩性划分的密度-中子数据的微型散点图/交会图。你可以从上面的代码中看到,我们必须指定的只是绘图的类型、轴和设置轴限制,这样我们就有了一个可以立即使用的好绘图。

密度——岩性和井的中子分布

通过查看多口井的岩性分布,我们可以进一步增强密度中子数据。使用转换后的岩性数据列,我们可以通过向hue参数提供岩性来为不同的岩性类型创建阴影。然后,我们可以将数据框中的井列输入到col参数中。

可以通过向标记和大小参数提供值来更改标记。这将有助于清理绘图,以便我们可以看到更多的数据。

最后,由于我们使用的是色调,我们可以使用add_legend().自动生成图例

g = sns.FacetGrid(data, col='WELL', hue='LITH', col_wrap=4)
g.map(sns.scatterplot, 'NPHI', 'RHOB', linewidth=1, size=0.1, marker='+')
g.set(xlim=(-0.15, 1))
g.set(ylim=(3, 1))
g.add_legend()

FacetGrid 密度-中子数据按井划分,并按岩性着色。

密度——按岩性和地质组的中子分布

我们可以很容易地将地质组列的分组选项换成按组查看岩性变化。

g = sns.FacetGrid(data, col='GROUP', hue='LITH', col_wrap=4)
g.map(sns.scatterplot, 'NPHI', 'RHOB', linewidth=1, size=0.1, marker='+')
g.set(xlim=(-0.15, 1))
g.set(ylim=(3, 1))
g.add_legend()

按地质组划分密度-中子数据网格,并按岩性着色

从该图中,我们可以很快看出 Rotliegendes Gp 主要是砂岩,而 Nordaland Gp 是页岩和砂岩/页岩岩性的混合物。

Seaborn 配对图

Seaborn 库还有一个非常强大的可视化功能,称为 pairplot,可以通过一行代码调用。这允许我们在一个网格上比较多个列/测量值。matplotlib 中的等效代码会扩展几行。

在显示配对图之前,我们需要修改我们正在使用的数据。首先,我们将使用由一些常用测井记录组成的数据子集。如果我们使用本文示例中的所有列,我们将无法查看详细信息。其次,我们需要删除任何丢失的数据值,否则在绘图时会出现问题。

要创建我们想要的曲线列表:

key_logs = ['CALI', 'GR', 'NPHI', 'RHOB', 'PEF', 'RDEP', 'RMED', 'DTC', 'DTS']

然后,我们可以提取数据的一个子集:

subset = data[key_logs]
subset = subset.dropna()

一旦我们有了关键曲线的列表,我们可以将它传递给 pair 图中的vars 参数。

sns.pairplot(subset, vars=key_logs, diag_kind='hist', plot_kws={'alpha':0.6, 'edgecolor':'k'})

我们的油井数据对绘图。直方图沿着每条对数曲线分布的对角线。

这将生成多个散点图,每条曲线沿对角线有一个直方图。如您所见,这使我们能够快速了解不同测井曲线之间的数据分布和关联。很厉害的剧情!

识别不良井眼数据

在本节中,我们将简要介绍一种方法,该方法可以直观地显示因井眼扩大而产生的井眼数据。井眼壁的劣化可能因多种原因而发生,包括欠压实岩石和作用在这些岩石上的应力变化,如泥浆比重的变化。

如缺失数据部分所述,我们只有 10 口井有钻头尺寸曲线。因此,将不对 2 口井进行绘图。我们可以使用 bitsize 曲线和 caliper 曲线来创建一个不同的 caliper。负数表示井眼尺寸缩小(例如页岩膨胀),正数表示井眼尺寸增大。

data['DIF_CAL'] = data['CALI'] - data['BS']

由于我们将使用 matplotlib 绘制这些图,我们首先需要按井对数据帧进行分组:

grouped = data.groupby('WELL')

然后计算我们绘图所需的行数:

nrows = int(math.ceil(len(grouped)/3.))

最后,我们可以使用一个小的 for 循环来遍历每个井,并在我们的图中添加一个密度与中子的子图。df.plot中的c参数允许我们在数据帧中指定一列来给数据着色。在这种情况下,我们将使用我们计算的 DIF _ 卡尔数据。

fig, axs = plt.subplots(nrows, 3, figsize=(20,20))
for (name, df), ax in zip(grouped, axs.flat):
    df.plot(kind='scatter', x='NPHI', y='RHOB', ax=ax, c='DIF_CAL', cmap='jet', vmin=-1, vmax=10)
    ax.set_xlim(-0.15,1)
    ax.set_ylim(3,1)
    ax.set_title(name)
plt.tight_layout()

密度-中子测井数据的多井图,用计算的差分井径曲线着色。

快速浏览一下,我们可以看到我们可能对两口井的数据有问题:15/9–15 和 16/1–2。这些应该进一步调查。

我们也可以把同样的想法应用到我们的岩性上。如果我们发现某个岩性受到了不良井眼条件的严重影响,那么在尝试使用机器学习算法预测该岩性时,可能会出现潜在的后果。

为此,我们通过 LITH 对数据进行分组,并使用与上面相同的绘图逻辑。

# Determine number of rows
nrows = int(math.ceil(len(grouped)/3.))# Group our data
grouped_lith = data.groupby('LITH')# Plot our data
fig, axs = plt.subplots(nrows, 3, figsize=(20,20))
for (name, df), ax in zip(grouped_lith, axs.flat):
    df.plot(kind='scatter', x='NPHI', y='RHOB', c='DIF_CAL', cmap='jet', ax=ax, vmin=-1, vmax=10)
    ax.set_xlim(-0.05,1)
    ax.set_ylim(3,0)
    ax.set_title(name)
plt.tight_layout()

密度-中子测井数据的多井图,按岩性划分,用计算的井径差曲线着色。

从图中我们可以看出,所有岩性都有很大程度的“好”数据。

结论

在本文中,我们介绍了使用常见 Python 库探索和可视化测井数据的三种方法。我们已经看到了如何使用 Seaborn 在单个图形上快速绘制多个数据集,并按井和岩性分割数据。我们还看到了如何快速识别缺失数据和受井眼条件影响的数据。

探索数据是熟悉和理解数据的一个很好的方式,尤其是在进行机器学习或进一步解释之前。

参考

鲍曼,奥桑德,迪里布,迪辛顿,曼拉尔,2020。2020 原力机器学习大赛。https://github . com/bolgebrygg/Force-2020-机器学习-竞赛

力:机器预测的岩性:【https://xeek.ai/challenges/force-well-logs/overview

漫威宇宙的探索性网络分析

原文:https://towardsdatascience.com/exploratory-network-analysis-of-marvel-universe-c557f4959048?source=collection_archive---------26-----------------------

动手教程,漫威网络分析

在图形数据科学库中引入新的 k-最近邻算法

一位智者曾经说过,2020-30 年将是图形数据科学的十年。事实上,这就发生在几天前的 2020 节点会议上,这位智者就是在 2020 节点的主题演讲上发言的 Emil Eifrem。如果你错过了会议,所有的演讲都可以在网上找到

几天前发布了 1.4 版本的 Neo4j 图形数据科学库,这正好符合 Emil 的说法。这是 GDS 图书馆的一个重要里程碑。这个版本中添加了许多新功能。如果有兴趣了解更多,可以查阅发布声明。在这篇博文中,我们将看看新的 k 近邻算法。但在此之前,我们将进行适当的图形探索性分析。

我写了这么多博客文章,要找到一些我还没有探索过的优秀数据集需要付出努力。这次我在 Kaggle 上看到了一个很酷的知识库,里面有关于漫威宇宙的数据。可惜后来才知道,只有漫画和人物文件才有匹配的 id。作为一名图形分析师,我们想要连接所有相关的数据,而没有匹配的 id,这有点困难。然后我意识到那些匹配的 id 是从漫威 API 中刮出来的。我从 API 中获取了一些额外的数据来丰富我们的图表。关于角色的信息不能通过 API 获得,但是可以在他们的网站上获得。这让我穿上蜘蛛侠服,测试我的网络爬行能力。我很自豪地说,我学会了使用硒,并有效地从漫威网站上搜集了有关人物的信息。

图形导入

你可以通过运行这个要点中的 cypher 语句来轻松导入这个漫威宇宙图。它包含十个简单的LOAD CSV cypher 语句和一个apoc.schema.assert过程来定义唯一的约束和索引。如果您使用 Neo4j 浏览器,请确保您已经启用了多语句查询编辑器。

这样,您可以复制整个要点的内容,而不必担心单独执行每个语句。

图表模式

既然我们已经导入了图表,我们可以通过以下过程来检查它的模式:

CALL db.schema.visualization()

如果您在 Neo4j 浏览器中运行这个过程,您将获得这个图形模式的可视化效果。

在图形的中心,有一些字符,也称为英雄。他们可以出现在多个漫画中,是一个事件的一部分,也可以属于一个团体。对于一些角色,我们也知道他们的数据,比如速度和战斗技能。最后,我们有代表亲戚、盟友或敌人关系的角色之间的社会关系。

为了感受一下图形的大小,我们可以运行以下 APOC 过程:

CALL apoc.meta.stats() YIELD labels
return labels

结果

在 38875 部漫画中出现过的角色有 1105 个。
我们有 470 个角色的数据。图中还存储了 92 个组和 74 个事件。

探索性图形分析

为了了解我们的图表,我们将从一个基本的图表数据探索过程开始。首先,我们来看看漫画中出现频率最高的角色。

MATCH (c:Character)
RETURN c.name as character, 
       size((c)-[:APPEARED_IN]->()) as comics
ORDER BY comics DESC
LIMIT 5

结果

你可能想知道我是从哪里得到这个条形图的。 Apache Zeppelin 笔记本有一个内置功能,可以让你创建各种可视化。幸运的是, Andrea Santurbano 为 Apache Zeppelin 开发了 Neo4j 连接器,使得编写 cypher 语句来读取或写入数据到 Neo4j 数据库变得很容易。

前五个最常见的字符不足为奇。蜘蛛侠是最常见或最受欢迎的角色。鉴于蜘蛛侠的受欢迎程度,难怪他们最近创造了一个年轻版的蜘蛛侠。托尼·斯塔克,又名钢铁侠,位居第二。似乎洛根,也被称为金刚狼,在历史上很受欢迎,但我认为他的知名度在最近一段时间慢慢消失了。史蒂夫·罗杰斯,更受欢迎的名字是美国队长,也很有名。似乎最近的漫威电影展示了漫画中更受欢迎的角色。

您可能想知道我们图表中的事件是什么,让我们来看看。我们将检查参与英雄的最高计数的事件。

MATCH (e:Event)
RETURN e.title as event, 
       size((e)<-[:PART_OF_EVENT]-()) as count_of_heroes,
       e.start as start,
       e.end as end,
       e.description as description 
ORDER BY count_of_heroes DESC 
LIMIT 5

结果

我不知道这些事件代表了什么,但有趣的是看到许多角色参与其中。大多数事件的时间跨度不到一年,而复仇行为的时间跨度超过二十年。在被 Michael Piper 告知这一数据很可能不正确后,我与其他来源进行了交叉引用,看起来这确实是不正确的数据。复仇行动事件从 1989 年 12 月持续到 1992 年 2 月。我也已经通知漫威在他们的 API 中更正这个信息,作为对传播错误信息的道歉,其他人不会遇到同样的问题。从描述来看,洛基和 92 号也有关系!其他角色。不幸的是,我们没有存储在图表中的漫画和事件之间的联系来进行进一步的分析。如果有人愿意收集漫威 API,我会很乐意把它添加到数据集中。

让我们也来看看最大的人物群体。

MATCH (g:Group)
RETURN g.name as group, 
       size((g)<-[:PART_OF_GROUP]-()) as members
ORDER BY members DESC LIMIT 5

结果

《x 战警》中有 41 个角色,这很有意义,因为他们有一个完整的学院。你可能会对复仇者联盟的 31 名成员感到惊讶,但在漫画中,有许多复仇者联盟的成员,尽管大多数是前成员。

正因为可以,我们来考察一下,是不是同一个群体的某些成员也是敌人。

MATCH (c1:Character)-[:PART_OF_GROUP]->(g:Group)<-[:PART_OF_GROUP]-(c2:Character)
WHERE (c1)-[:ENEMY]-(c2) and id(c1) < id(c2)
RETURN c1.name as character1, c2.name as character2, g.name as group

结果

看起来洛根和其他 x 战警相处的并不好。对于一些角色,我们也有他们的出生地和教育背景,所以让我们快速看一下。在刮的过程中,我注意到一个来自南斯拉夫的英雄,所以我想知道是否有更多来自南斯拉夫的角色。

MATCH (c:Character)
WHERE c.place_of_origin contains "Yugoslavia"
RETURN c.name as character, 
       c.place_of_origin as place_of_origin,
       c.aliases as aliases

结果

两个人物起源于今天的克罗地亚,离我住的地方不到两个小时车程。让我们看看所有完成博士学位的角色。

MATCH (c:Character)
WHERE c.education contains "Ph.D"
RETURN c.name as character, c.education as education
LIMIT 10

结果

看起来这些英雄中的很多人都很适合工作。只有龙葵似乎有点危险。这感觉就像是人们在搜索博士简介时会放在 LinkedIn 个人资料上以引起注意的东西。顺便问一下,你知道 X 教授有四个博士学位,同时也是精神病学的医学博士吗?受过良好教育的人。

分析盟友和亲戚的社区

我们已经研究了基本的图统计,现在我们将更多地关注网络分析。我们将调查人物之间的社会关系。
首先,我们将计算角色之间每种关系类型的等级值,并显示总体等级最高的英雄。

MATCH (c:Character)
RETURN c.name as name,
       size((c)-[:ALLY]->()) as allies,
       size((c)-[:ENEMY]->()) as enemies,
       size((c)-[:RELATIVE]->()) as relative
ORDER BY allies + enemies + relative DESC 
LIMIT 5

结果

猩红女巫和雷神似乎有最直接的敌人。金刚狼有最多的盟友,但也有很多敌人。看起来 Triton 是一个有 17 个直系亲属关系的大家庭。我们可以使用apoc.path.subgraphAll程序来检查 Triton 的亲属社区。查看文档了解更多信息。

MATCH p=(c:Character{name:"Triton"})
CALL apoc.path.subgraphAll(id(c), {relationshipFilter:"RELATIVE"})
YIELD nodes, relationships
RETURN nodes, relationships

结果

我从来不知道有些漫威英雄有一个幸福的大家庭。如果没有败家子在场就不准确了。马克西姆斯在这里看起来像是家族中的害群之马,因为他在家族中有四个敌人。你可能会奇怪,当我们只遍历相对关系时,为什么会显示盟友和敌人的关系。Neo4j 浏览器有一个功能,在屏幕上显示节点之间的所有连接。

如果你有一个密集的图表,你可能应该取消这个设置,否则在 Neo4j 浏览器中会显示很多连接。

弱连通分量算法

弱连通分量是几乎所有图形分析工作流的一部分。它用于查找网络中断开的组件或孤岛。

WCC 算法的示例结果

在本例中,图表由两部分组成。迈克尔、马克和道格属于第一部分,而布里奇特、爱丽丝和查尔斯属于第二部分。我们将应用弱连通分量算法来寻找关联字符的最大分量。由于我们不打算在这个网络上运行任何其他算法,我们将使用匿名图投影

CALL gds.wcc.stream({
  nodeProjection:'Character',
  relationshipProjection:'ALLY'})
YIELD nodeId, componentId
WITH componentId, count(*) as members
WHERE members > 1
RETURN componentId, members
ORDER BY members DESC
LIMIT 5

结果

同盟的最大组成部分有 195 个成员。然后我们有几个只有几个成员的小联盟岛。如果我们在 Neo4j 浏览器中可视化 alliances 的最大组件,并选择 connect results nodes 选项,我们将得到以下可视化结果。

虽然我们已经找到了最大的盟友组件,但我们可以观察到组件中的许多角色实际上是敌人(红色关系)。为了更好地理解为什么会出现这种情况,让我们看下面的例子。

在这个例子中,灭霸是加莫拉、法师和惊奇队长的敌人,但仍然是同一个盟国的成员。它们属于同一个组件,因为人们可以仅使用联盟关系(如果我们将它们视为无向图)从任何成员到所有其他组成员遍历该图。

定制 ally 组件算法

假设我们想要找到在给定组件中没有敌人的盟友社区。算法实现相对简单,例如,您可以使用 Neo4j 自定义过程。尽管如此,如果你像我一样不会说 Java,你总是可以求助于你最喜欢的脚本语言。我用 Python 开发了自定义 Ally 组件算法。首先,我们定义了一些助手函数来获取单个节点的盟友和敌人。

我的实现相对简单。算法的输入是最大联合组件中所有节点 id 的列表。从单个节点开始,将它的敌人加载到敌人列表中,将它的盟友加载到一个队列中,稍后进行处理。然后我们迭代盟军队列。如果某个节点不是组件中任何现有节点的敌人,则将它们添加到团体列表,并将它们的敌人添加到团体的敌人列表。我添加了一些小的性能调整,比如如果我们已经遍历了 allies 队列中的节点,我们可以从起始节点的全局列表中删除该节点。

在这段代码中,该算法只返回属于最大联盟组件的节点的 id,其中没有敌人。在 Neo4j 中标记这些节点应该不成问题,因为您可以通过它们的 id 来匹配它们。盟友中最大的组成部分,也就是内部没有敌人的部分,有 142 个成员。如果我们在 Neo4j 浏览器中将其可视化,我们可以看到没有敌人关系可见。

分析角色的状态

在我们分析的最后一部分,我们将检查角色的属性。我们有总共 470 个英雄的统计数据。这条信息是从漫威的网站上搜集来的。这里有一个例子,展示了漫威网站上单个角色的统计数据。

你能猜出它们属于谁吗?它们是托尼·斯塔克(钢铁侠)的数据。统计范围从 0 到 7,钢铁侠没有一个 7。可能不是最强的英雄,尽管他是最受欢迎的英雄之一。现在我们将探索平均属性最高的角色。每当我的 cypher 查询需要帮助时,我都会求助于 Neo4j Slack。幸运的是,Andrew Bowman 总是在优化和美化我们的 cypher 查询方面提供很好的建议。这次他给我看了apoc.map.values程序。它可用于获取单个节点的所有属性,而无需显式写入属性键。

MATCH (c:Character)-[:HAS_STATS]->(stats)
RETURN c.name as character, 
       apoc.coll.avg(apoc.map.values(stats, keys(stats))) as average_stats
ORDER BY average_stats DESC
LIMIT 10

结果

看起来很多角色的属性都已经达到极限了。我不确定这个数据收集过程是如何进行的,但我发现了一个名叫松鼠女孩的迷人女主角,她可能会用一只手踢钢铁侠的屁股,同时用另一只手做酸奶面包。或者擦亮她的指甲,不确定她是什么类型的女孩。唯一确定的是她是个坏蛋。

k-最近邻算法

k-最近邻是一种更标准的图形算法,之前已经在图形数据科学库中以余弦欧几里德皮尔森相似度算法的形式实现。这些是基本实现,其中算法比较网络中所有节点对的给定向量。因为比较所有节点对并不能很好地伸缩,所以向库中添加了 kNN 算法的另一个实现。它基于通用相似性度量的高效 k-最近邻图构造文章。该算法不是比较每个节点对,而是基于节点的邻居很可能已经是最近的邻居的假设来选择可能的邻居。该算法相对于节点计数是准线性的,而不是二次的。该实现使用余弦相似性来比较两个向量。

首先,我们需要创建一个向量(数字数组),在英雄对之间进行比较。我们将使用角色的属性以及他们飞行的能力来填充向量。因为所有的统计在 0 到 7 之间有相同的范围,所以不需要标准化。我们只需要对飞行特征进行编码,使其跨度也在 0 到 7 之间。那些会飞的角色会有飞行特性七的值,那些不会飞的角色会有零的值。

MATCH (c:Character)-[:HAS_STATS]->(s)
WITH c, [s.durability, s.energy, s.fighting_skills, 
         s.intelligence, s.speed, s.strength,
         CASE WHEN c.flight = 'true' THEN 7 ELSE 0 END] as stats_vector
SET c.stats_vector = stats_vector

我们还将使用第二个标签标记拥有统计向量的角色。这样,我们可以很容易地在命名图的本地投影中用统计向量过滤英雄。

MATCH (c:Character)
WHERE exists (c.stats_vector)
SET c:CharacterStats

现在一切都准备好了,我们可以开始加载我们的命名图了。我们将把所有带有 CharacterStats 标签的节点和它们的 stats_vector 属性投影到一个命名图中。如果你需要快速复习或了解 GDS 图书馆是如何工作的,我建议你参加的图算法入门课程

CALL gds.graph.create('marvel', 'CharacterStats',
  '*', {nodeProperties:'stats_vector'})

现在,我们可以继续使用新的 kNN 算法来推断相似性网络。我们将使用算法的变异模式。 mutate 模式将结果存储回投影图,而不是 Neo4j 存储图。这样,我们可以使用 kNN 算法结果作为工作流中稍后的社区检测算法的输入。kNN 算法有一些我们可以用来微调结果的参数:

  • topK :每个节点要查找的邻居数量。返回 K 个最近邻。
  • sampleRate :限制每个节点比较次数的采样率。
  • deltaThreshold :确定何时提前停止的百分比值。如果发生的更新少于配置值,则算法停止。
  • randomJoins :在每一次迭代之间,基于随机选择进行了多少次连接新节点邻居的尝试。

我们将定义 topK 值为 15,采样率为 0.8,其他参数保留默认值。

CALL gds.beta.knn.mutate('marvel', {nodeWeightProperty:'stats_vector', sampleRate:0.8, topK:15, mutateProperty:'score', mutateRelationshipType:'SIMILAR'})

鲁汶模块化算法

相似性网络被推断并存储在命名图中。我们可以用 Louvain 模块性算法来检查这个新的相似性网络的社区结构。由于关系的相似性分数作为它们的属性是可用的,我们将使用 Louvain 模块化算法的加权变体。使用relationship weight property参数,我们让算法知道在计算网络的社区结构时应该考虑关系的权重。这次我们将使用算法的 write 模式将结果存储回 Neo4j 存储图。

CALL gds.louvain.write('marvel',
  {relationshipTypes:['SIMILAR'],  
   relationshipWeightProperty:'score', 
   writeProperty:'louvain'});

我们可以使用下面的 cypher 查询来检查社区结构结果。

MATCH (c:Character)-[:HAS_STATS]->(stats)
RETURN c.louvain as community, count(*) as members, 
       avg(stats.fighting_skills) as fighting_skills,
       avg(stats.durability) as durability,
       avg(stats.energy) as energy,
       avg(stats.intelligence) as intelligence,
       avg(stats.speed) as speed,
       avg(stats.strength) as strength,
       avg(CASE WHEN c.flight = 'true' THEN 7.0 ELSE 0.0 END) as flight

结果

为每个统计数据添加标准偏差是有意义的,但是对于一篇博客文章来说这是不合适的。id 68 的社区拥有最强大的成员。大多数统计的平均值是 6.5,这意味着他们几乎完全被透支了。飞行的平均值为 2 表明大约 30% (2/7)的成员可以飞行。最大的社区有 106 名成员,他们的平均统计数据在 2 到 3 之间,这表明他们可能是能力较低的支持角色。能力较强的角色通常是主角。

我们还可以用 Neo4j Bloom 来可视化推断的相似性网络的社区结构。我们根据社区成员关系给节点着色。首先,我们必须用GDS . graph . write relationship过程将变异的关系存储回 Neo4j,以便用 Neo4j Bloom 可视化它们。

粉色社区包含了最强的英雄。我们可以观察到它们在右上方有自己的集群,也向中间延伸了一点。

标签传播算法

标签传播算法也可以用来确定一个网络的社区结构。我们将它应用于推断的相似性网络,并将结果与 Louvain 模块性算法结果进行比较。

CALL gds.labelPropagation.write('marvel',
  {relationshipTypes:['SIMILAR'],
   relationshipWeightProperty:'score', 
   writeProperty:'labelPropagation'})

我们研究了标签传播算法的结果。

MATCH (c:Character)-[:HAS_STATS]->(stats)
RETURN c.labelPropagation as community, count(*) as members, 
       avg(stats.fighting_skills) as fighting_skills,
       avg(stats.durability) as durability,
       avg(stats.energy) as energy,
       avg(stats.intelligence) as intelligence,
       avg(stats.speed) as speed,
       avg(stats.strength) as strength,
       avg(CASE WHEN c.flight = 'true' THEN 7.0 ELSE 0.0 END) as flight

结果

我们可以注意到,标签传播算法找到的社区是鲁汶模块化算法的两倍。其中一些相对较小。例如,id 为 693 的社区只有三个成员,他们的平均统计值都是 1.0。他们是被称为 Maggott,死亡之鸟和 Slayback 的英雄。时髦的名字。最强大的社区有 137 个 id,只有 23 个成员。记住,鲁文模块化算法发现的最强大的社区有 46 个成员,平均统计值略低。

我们可以再次用 Neo4j Bloom 可视化标签传播社区结构的结果。

再说一遍,粉色社区的成员是最强大的。我们可以注意到,这一次右上角的集群根本没有延伸到中心。我们可以放大这个社区来观察它的成员。

似乎所有的事情都在平衡中,因为松鼠女孩就在最强大的社区的中心。

结论

我希望您已经在 APOC 和 GDS 图书馆的帮助下学会了在 Neo4j 中执行网络分析的一些技巧。我们仍然可以用这个图表做很多事情,所以期待一个新的帖子。在那之前,试用 Neo4j 并试用 GDS 库 1.4 版本的预发布版。如果你需要帮助,我推荐你去看看 Neo4j 图形学院

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

编辑:由于数据收集过程中的缺陷,我已经删除了关于人物的错误历史数据。

Python 中的探索性文本分析

原文:https://towardsdatascience.com/exploratory-text-analysis-in-python-8cf42b758d9e?source=collection_archive---------22-----------------------

建立情感分类器的一步

为什么我们在建立模型之前要做探索性的数据分析?我会说“为了更好地理解数据,以便我们以合适的方式预处理数据,并选择合适的建模技术”。这种理解数据的必要性在处理文本数据时仍然适用。这篇文章是构建情感分类器的三篇系列文章中的第一篇。在这篇文章中,我们将看看一种对文本进行探索性数据分析的方法,或者为了简洁起见,将探索性文本分析

照片由安德鲁·尼尔Unsplash 上拍摄

在我们深入研究之前,让我们先退一步,看看更大的图景。CRISP-DM 方法概述了成功的数据科学项目的流程。下图显示了数据科学项目的第 2-4 阶段。在数据理解阶段,探索性数据分析是关键任务之一。

CRISP-DM 工艺流程摘录

在从事数据科学项目时,在不同阶段之间来回切换而不是线性前进并不罕见。这是因为想法和问题会在随后的阶段出现,你想回到一两个阶段去尝试这个想法或找到问题的答案。官方的 CRISP-DM 中没有粉色箭头,但是,我认为这些经常是必要的。事实上,为了探索性的文本分析,我们将在这篇文章中做一些数据准备。对于那些有兴趣了解 CRISP-DM 更多信息的人来说,这个是一个很好的简短介绍,这个资源提供了更详细的解释。

0.Python 设置🔧

这篇文章假设读者(👀是的,你!)可以访问并熟悉 Python,包括安装包、定义函数和其他基本任务。如果你是 Python 的新手,这个是一个很好的起点。

我在 Jupyter 笔记本里测试过 Python 3.7.1 的脚本。

让我们在开始之前确保您已经安装了以下库:
◼️ 数据操作/分析: numpy,pandas ◼️ 数据分区: sklearn ◼️ 文本预处理/分析: nltk◼️ 可视化: matplotlib,seaborn

一旦你安装了 nltk ,请确保你已经从 nltk 下载了【punkt】**【停用词】【wordnet】,脚本如下:

import nltk
nltk.download('punkt') # for sent_tokenize
nltk.download('stopwords') 
nltk.download('wordnet') # for WordNetLemmatizer

如果你已经下载了,运行这个会通知你。

现在,我们准备导入所有的包:

# Setting random seed
seed = 123# Data manipulation/analysis
import numpy as np
import pandas as pd# Data partitioning
from sklearn.model_selection import train_test_split# Text preprocessing/analysis
import re
from nltk import word_tokenize, sent_tokenize, FreqDist
from nltk.util import ngrams
from nltk.corpus import stopwords
from nltk.stem import WordNetLemmatizer
from nltk.tokenize import RegexpTokenizer# Visualisation
import matplotlib.pyplot as plt
import seaborn as sns
sns.set(style="whitegrid", context='talk', 
        palette=['#D44D5C', '#43AA8B'])

外卖或笔记将附带🍀探索时

1.数据📦

我们将使用 IMDB 电影评论数据集。您可以在这里下载数据集,并将其保存在您的工作目录中。保存后,让我们将其导入 Python:

sample = pd.read_csv('IMDB Dataset.csv')
print(f"{sample.shape[0]} rows and {sample.shape[1]} columns")
sample.head()

从查看数据的头部,我们可以立即看到第二条记录中有 html 标记。

🍀进一步检查 html 标签,看看它们有多普遍。

让我们来看看情绪之间的分歧:

sample['sentiment'].value_counts()

在样本数据中,情感是平均分配的。在开始探索性文本分析之前,我们先把数据分成两组:训练测试。我们将留出 5000 箱进行测试:

# Split data into train & test
X_train, X_test, y_train, y_test = train_test_split(sample['review'], sample['sentiment'], test_size=5000, random_state=seed, 
                                                    stratify=sample['sentiment'])# Append sentiment back using indices
train = pd.concat([X_train, y_train], axis=1)
test = pd.concat([X_test, y_test], axis=1)# Check dimensions
print(f"Train: {train.shape[0]} rows and {train.shape[1]} columns")
print(f"{train['sentiment'].value_counts()}\n")print(f"Test: {test.shape[0]} rows and {test.shape[1]} columns")
print(test['sentiment'].value_counts())

通过指定stratify参数,我们确保了情感在两组中平均分配。

在本帖中,我们将使用训练进行探索性文本分析。一旦我们探索了训练数据集,单独检查测试集的关键特征可能是有用的。理想情况下,这两组都应该代表潜在人群。让我们检查一下训练数据集的头部:

train.head()

好了,我们都准备好去探索了!✨

2。探索性文本分析🔎

引导探索性数据分析的一个方法就是写下你感兴趣的问题,用数据来回答。找到问题的答案通常会引出你可能想探究的其他问题。以下是我们可以回答的一些问题示例:

📋 2.1。热身: *一共有几根弦?
*最常见的字符串有哪些?
*最短的字符串是什么样子的?
*最长的字符串是什么样子的?

📋 2.2。代币:

💡 令牌是一个字符序列。 令牌通常被笼统地称为文字。
💡 记号化是将文档拆分成记号的过程,有时还会丢弃某些字符,如标点符号。示例:标记化将“这部电影棒极了”变成 4 个标记:[“这部电影”、“电影”、“曾经是”、“棒极了”]

*有多少代币?
*有多少个唯一令牌?
*每个令牌的平均字符数是多少?

📋 2.3。停字:

💡 停用词是对文本意义几乎没有价值的常用词。示例:和。

*最常见的停用词是什么?
*还有哪些词经常出现,可以添加到停用词中?

📋 2.4。常见单词 n-grams:

💡 单词 n-grams 是文本数据中相邻 n 个单词的所有组合。例如:“这部电影棒极了”中的二元结构是[“这部电影”、“电影曾经是”、“曾经棒极了”]

*最常见的令牌是什么?最常见的二元模型是什么?最常见的三元模型是什么?最常见的四字格是什么?

📋 2.5。文件:

💡 文档是文本记录。例如:每个电影评论都是一个文档。
💡语料库是文档的集合。简单来说,文本数据就是一个语料库。示例:我们可以将训练数据称为训练语料库。

*每个文档的平均句子数量是多少?
*每个文档的平均令牌数是多少?
*每个文档的平均字符数是多少?
*每个文档的平均停用词数量是多少?
*答案如何因情绪而异?

现在,是时候找到这些问题的答案了!😃

📋 2.1.W 手臂抬起

让我们将所有的评论合并成一个字符串,然后在空格处将其拆分成子字符串(以下简称为字符串)。这确保了对于这种预热探索,语料库被最小程度地改变(例如,保持标点不变):

# Prepare training corpus into one giant string
train_string = " ".join(X_train.values)
print(f"***** Extract of train_string ***** \n{train_string[:101]}", "\n")# Split train_corpus by white space
splits = train_string.split()  
print(f"***** Extract of splits ***** \n{splits[:18]}\n")

✏️ 2.1.1。有多少根弦?

print(f"Number of strings: {len(splits)}")
print(f"Number of unique strings: {len(set(splits))}")

在训练语料库中有超过 1000 万个字符串,其中有大约 41 万个独特的字符串。这给了我们初步的大概数字。在我们正确标记后,我们将看到这些数字是如何寻找标记的。

✏️。最常见的字符串有哪些?

让我们为每个字符串准备频率分布来回答这个问题:

freq_splits = FreqDist(splits)
print(f"***** 10 most common strings ***** \n{freq_splits.most_common(10)}", "\n")

看到最常见的字符串是停用词并不奇怪。我们将在第 2.3 节中进一步探讨停用词。停止言语

🍀在查看普通令牌和 n 元语法之前,删除停用词。

✏️。最短的字符串是什么样子的?

让我们将短字符串定义为长度小于 4 个字符的字符串,并检查它们的频率:

short = set(s for s in splits if len(s)<4)
short = [(s, freq_splits[s]) for s in short]
short.sort(key=lambda x:x[1], reverse=True)
short

摘录自 ,未显示所有输出

许多短字符串似乎是停用词,但也有数字和其他短词。

🍀有不同形式的数字:3,2,70,90% —我们需要决定是放弃还是保留它们。

🍀有大小写变化:“the”、“The”、“The”-这些需要规范化。

因为我们还没有标记化,一些字符串目前包含附加在单词上的标点符号。因此,在其他情况下,相同的单词被认为是不同的,如下例所示:

摘自 ,并非所有输出都显示

🍀舍弃标点符号将有助于进一步规范文字。

✏️ 2.1.4。最长的字符串是什么样子的?

让我们将长字符串定义为 16+字符长,并重复这个过程。

long = set(s for s in splits if len(s)>15)
long = [(s, freq_splits[s]) for s in long]
long.sort(key=lambda x:x[1], reverse=True)
long

摘录自 ,并非所有输出都显示

长弦的频率看起来比短弦低得多,这并不奇怪。长字符串看起来很有趣,有几个要点:

🍀同一个词有美国和英国的拼法:“charactering”和“charactering”。使用下面的脚本快速检查后,这个单词的美式拼写看起来更占优势。量化这两种拼写在整个训练语料库中的流行程度有点棘手。

print(f"characterisation: {sum([c for s, c in long if re.match(r'characterisation*', s.lower())])} strings")
print(f"characterization: {sum([c for s, c in long if re.match(r'characterization*', s.lower())])} strings")

🍀有带连字符的词:“发人深省”、“后启示录”、“不被欣赏”和“电影特别”(这个有双连字符)。如果我们在空格或标点符号上做记号,这些字符串将被分割成单独的单词。在大多数情况下,这将保留句子的要点。如果我们保留用连字符连接的单词,它们就不会像生僻字一样常见,因此会被删除。

🍀有文字结合其他标点符号(有些由于篇幅不够) : ' 演员/女演员',《碟中谍:不可能',《演员(哈!哈!哈!)…他们是“,‘不同:其实,布洛克’。在标记时,最好将这些情况分成单独的单词。因此,基于空格或标点符号的标记可能是一个好主意。

🍀有网址和邮箱:'/>www。ResidentHazard.com ','**http://www.PetitionOnline.com/gh1215/petition.html', ' iamaseal 2 @ YAHOO。COM“”。但是,好像并不多。**

🍀有把同一个字重复两次以上的不法词语:“Booooring……不要,不要!),'.如果你知道这些拉长的单词的正确术语,我很想知道。在此之前,我们将把它们称为“非法词汇”。这些违法案件似乎很少出现。**

还有其他有趣的发现,我们可以添加到这些,但这些是一个很好的开端。

理解我们刚刚探索的这些情况是否普遍到足以证明额外的预处理步骤(即更长的运行时间)是正确的,这一点很重要。进行实验以了解添加额外的预处理步骤是否会提高模型性能是很有用的。我们将在第三篇文章中做一些介绍。

✏️ 2.1.5。到目前为止出现的后续问题

我们已经回答了所有 4 个热身问题!在寻找答案的同时,我们收集了更多的问题。在我们跳到下一组预定义的问题之前,让我们快速跟进一些移动中出现的问题。

◼️html 标签出现的频率如何?
这个问题是我们在查看
第 1 节的样本数据头时出现的。数据。当我们根据空白分割数据时,示例 html 标记:'< br / > < br / >'将被分割成三个字符串:'< br ','/ > < br '和'/ >'。顺便说一下,
标签
似乎是用来换行的。让我们粗略估计一下 html 标签有多普遍。

所有跟进问题都是相似的,因为我们要评估特定类型字符串的出现频率。为了避免重复我们自己,我们来做一个函数。

*def summarise(pattern, strings, freq):
    """Summarise strings matching a pattern."""
    # Find matches
    compiled_pattern = re.compile(pattern)
    matches = [s for s in strings if compiled_pattern.search(s)]

    # Print volume and proportion of matches
    print("{} strings, that is {:.2%} of total".format(len(matches), len(matches)/ len(strings)))

    # Create list of tuples containing matches and their frequency
    output = [(s, freq[s]) for s in set(matches)]
    output.sort(key=lambda x:x[1], reverse=True)

    return output# Find strings possibly containing html tag
summarise(r"/?>?w*<|/>", splits, freq_splits)*

没有显示所有输出

如果我们滚动输出,除了 < br > 标签和少数 < i > 标签之外,没有多少 html 标签。

🍀如果我们在分词时去掉标点符号,那么“/ > < br”和“< br”将变成“br ”,我们可以添加“br”来停止单词。**

◼ ️How 经常出现的数字是多少? 我们从 2.1.3 节的中找到了一些数字的实例。短弦。让我们通过下面的脚本来看看它们出现的频率:

*summarise(r"\d", splits, freq_splits)*

没有显示所有输出

包含数字的字符串很少出现。在电影评论的背景下,很难直观地理解数字是如何有用的。10/10 可能是积极情绪的标志,但我们能从 4、80 和 20 这样的数字中推断出什么呢?我们将在标记时丢弃数字。

根据项目的时间表,你可能没有足够的时间去尝试所有有趣的想法。在这种情况下,保留一份可以尝试的项目清单 是很方便的,你可以在有时间的时候尝试一下。我们将在该列表中添加以下任务:
1)保存数字并将其转换为文本
2)创建一个特征来指示评论是否包含数字

◼ ️How 常用的是连字符的单词吗?
我们在
第 2.1.4 节中检查长字符串时看到了连字符。让我们看看它们出现的频率:

*summarise(r"\w+-+\w+", splits, freq_splits)*

没有显示所有输出

大约不到 1%的字符串包含带连字符的单词。浏览用连字符连接的单词,将它们分开以保持数据简单更有意义。例如:我们应该将“camera-work”标记为 2 个标记:['camera ',' work']而不是 1 个标记:['camera-work']。我们可以在列表中添加“保持连字符单词的原样”。**

◼ ️How 常用的词是由其他标点符号组合而成的吗? 很像上一个问题,我们在长串探索中看到了这些案例。让我们看看它们出现的频率:

*summarise(r"\w+[_!&/)(<\|}{\[\]]\w+", splits, freq_splits)*

没有显示所有输出

不要太频繁,这些肯定是要分开的。

◼ ️How 频繁出现的都是绿林好汉的话吗?我们看到了像 NOOOOOOIIIISE!),‘早先。让我们看看它们有多普遍:

*def find_outlaw(word):
    """Find words that contain a same character 3+ times in a row."""
    is_outlaw = False
    for i, letter in enumerate(word):
        if i > 1:
            if word[i] == word[i-1] == word[i-2] and word[i].isalpha():
                is_outlaw = True
                break
    return is_outlawoutlaws = [s for s in splits if find_outlaw(s)]
print("{} strings, that is {:.2%} of total".format(len(outlaws), len(outlaws)/ len(splits)))
outlaw_freq = [(s, freq_splits[s]) for s in set(outlaws)]
outlaw_freq.sort(key=lambda x:x[1], reverse=True)
outlaw_freq*

没有显示所有输出

这些不值得纠正,因为案例太少。

这是最后一个跟进问题!我们已经了解了一些数据。希望你感觉暖和了。💦你可能已经注意到,我们可以很容易地不断扩展我们的问题,不断探索?出于时间的考虑,我们将在这里停止这一部分,并尽量使接下来的部分尽可能简洁。否则,这篇文章会超过几个小时。💤

📋 2.2.代币

让我们一口气回答这两个问题:

✏️2 . 2 . 1。有多少代币?
✏️ 2.2.2。有多少独特的代币?

我们必须先将数据符号化。回想一下之前的探索,似乎最好在标记时去掉标点和数字。记住这一点,让我们将文本标记为字母标记:

*tokeniser = RegexpTokenizer("[A-Za-z]+")
tokens = tokeniser.tokenize(train_string)
print(tokens[:20], "\n")*

现在我们已经标记化了,我们可以回答前两个问题:

*print(f"Number of tokens: {len(tokens)}")
print(f"Number of unique tokens: {len(set(tokens))}")*

训练数据中有超过 1000 万个令牌,其中大约有 12.2 万个唯一令牌。目前,“手表”、“手表”和“观看”被视为不同的令牌。嗯,如果我们能把它们正常化为“手表”,并把它们算作一个唯一的令牌,那不是很好吗?如果我们进行标准化,唯一令牌的数量将会减少。让我们快速地做两件事:将所有的记号转换成小写,并对它们进行 lemmatise:

*lemmatiser = WordNetLemmatizer()
tokens_norm = [lemmatiser.lemmatize(t.lower(), "v") for t in tokens]
print(f"Number of unique tokens: {len(set(tokens_norm))}")*

太好了,独特令牌的数量下降了约 30%。

📌练习:如果你感兴趣并且有时间,不要像上面那样把两个步骤结合起来,试着分离出来,看看每个步骤中独特代币的数量是如何变化的。例如,您可以首先将记号转换成小写,并检查数字,然后使用 lemmatise 并再次检查数字。如果改变这两个操作的顺序,唯一令牌的最终数量是否不同于 82562?为什么会这样?

👂嘶,我会在* 下一篇 为模型预处理文本时,展示 lemmatise 的另一种方式。*

✏️ 2.2.3。每个令牌的平均字符数是多少?

让我们找出平均令牌长度并检查其分布情况:

*# Create list of token lengths for each token
token_length = [len(t) for t in tokens]# Average number of characters per token
print(f"Average number of characters per token: {round(np.mean(token_length),4)}")# Plot distribution
plt.figure(figsize=(12, 12))
sns.countplot(y=token_length)
plt.title("Counts of token length", size=20);*

有几个令牌很长,但也很罕见。让我们来看看超过 10 个字符的确切数量:

*pd.DataFrame(data=token_length, columns=['length']).query("length>10").value_counts()*

超过 17 个字符的长单词并不常见。让我们来看看其中的一些:

*[t for t in tokens if len(t)>=20]*

没有显示所有输出

有趣的是,有些是有效的长单词,而有些长是因为它们缺少空格或非法单词(即拉长)。

🍀在预处理时,我们应该确保像这样非常罕见的记号被丢弃,这样它们就不会在将记号矢量化成矩阵时创建单独的列。**

📋 2.3.停止言语

✏️2 . 3 . 1。最常用的停用词是什么?

让我们首先检查所有停用词:

*stop_words = stopwords.words("english")
print(f"There are {len(stop_words)} stopwords.\n")
print(stop_words)*

在写这篇文章的时候,有 179 个停用词。停用词的清单将来还会增加。看起来我们可以扩展停用词来包含更多的停用词。事实上,我已经在 Github 上提议将下面的通用停用词添加到 nltk 的英文停用词列表中。我们还要确保在列表中添加一个自定义停用词“br”😗*

**stop_words.extend(["cannot", "could", "done", "let", "may" "mayn",  "might", "must", "need", "ought", "oughtn", "shall", "would", "br"])
print(f"There are {len(stop_words)} stopwords.\n")**

现在,让我们来看看最常见的停用词是什么:

**freq_stopwords = [(sw, tokens_norm.count(sw)) for sw in stop_words]
freq_stopwords.sort(key=lambda x: x[1], reverse=True)
freq_stopwords[:10]**

频率真的很高(咄,我的意思是它们是停用词,当然会频繁😈),特别是对于‘be’和‘the’。找出停用词在标记中所占的比例不是很有趣吗?让我们快速检查一下:

**n_stopwords = len([t for t in tokens_norm if t in stop_words])
print(f"{n_stopwords} tokens are stop words.")
print(f"That is {round(100*n_stopwords/len(tokens_norm),2)}%.")**

大约一半的标记是停用词。💭

✏️。还有哪些词经常出现,可以添加到停用词中?

我们很快会在查看常见令牌时回答这个问题。

📋 2.4.常见 n 元语法

是时候找出常见的 n-gram 了。让我们一起回答这四个问题:

✏️2 . 4 . 1–4。什么是最常见的令牌,二元,三元和四元?****

首先,让我们删除停用词:

**tokens_clean = [t for t in tokens_norm if t not in stop_words]
print(f"Number of tokens: {len(tokens_clean)}")**

这是剩下的 49%的代币。现在,我们可以检查常见的记号(即,一元词)、二元词、三元词和四元词:

**def preprocess_text(text):
    """Preprocess text into normalised tokens."""
    # Tokenise words into alphabetic tokens
    tokeniser = RegexpTokenizer(r'[A-Za-z]{2,}')
    tokens = tokeniser.tokenize(text)

    # Lowercase and lemmatise 
    lemmatiser = WordNetLemmatizer()
    lemmas = [lemmatiser.lemmatize(token.lower(), pos='v') for token in tokens]

    # Remove stopwords
    keywords= [lemma for lemma in lemmas if lemma not in stop_words]
    return keywordsdef get_frequent_ngram(corpus, ngram, n=20):
    """Find most common n n-grams tokens."""
    # Preprocess each document
    documents = [preprocess_text(document) for document in corpus]

    # Find ngrams per document
    n_grams = [list(ngrams(document, ngram)) for document in documents]

    # Find frequency of ngrams
    n_grams_flattened = [item for sublist in n_grams for item in sublist]
    freq_dist = FreqDist(n_grams_flattened)
    top_freq = freq_dist.most_common(n)
    return pd.DataFrame(top_freq, columns=["ngram", "count"])# Get frequent ngrams for all 4
for i in range(1,5):
    mapping = {1:"uni", 2:"bi", 3:"tri", 4:"four"}
    plt.figure(figsize=(12,10))
    sns.barplot(x="count", y="ngram", data=get_frequent_ngram(train['review'], i))
    plt.title(f"Most common {mapping[i]}grams");**

****

与其他常用词相比,单词“film”和“movie”看起来相当频繁。问题 2.3.2 的答案。就是潜在的加上‘电影’,和‘电影’来停止用词。有趣的是经常看到二元、三元和四元。随着 n 的增加,频率如预期的那样下降。二元模型可能是潜在有用的,但是三元模型和四元模型相对于标记频率来说不够频繁。

📋 2.5.文档

让我们一起来回答这些问题:

✏️2 . 5 . 1。每个文档的平均句子数是多少?
✏️ 2.5.2。每个文档的平均令牌数是多少?
✏️。每个文档的平均字符数是多少?
✏️。每个文档的平均停用词数量是多少?
✏️。这些问题的答案如何因情绪而异?

首先,我们必须准备数据:

**# tokeniser = RegexpTokenizer("[A-Za-z]+")
train["n_sentences"] = train["review"].apply(sent_tokenize).apply(len)
train["tokens"] = train["review"].apply(tokeniser.tokenize)
train["n_tokens"] = train["tokens"].apply(len)
train["n_characters"] = train["review"].apply(len)
train["n_stopwords"] = train["tokens"].apply(lambda tokens: len([t for t in tokens if t in stop_words]))
train["p_stopwords"] = train["n_stopwords"]/train["n_tokens"]# Inspect head
columns = ['sentiment', 'n_sentences', 'n_tokens', 'n_characters', 'n_stopwords', 'p_stopwords']
train[columns].head()**

让我们检查感兴趣的变量的描述性统计数据:

**train.describe()**

在这个表格中,我们有前四个问题的答案。现在,让我们看看它是否因情绪而不同。如果它们显著不同,我们可以使用变量作为模型的特征:

**num_vars = train.select_dtypes(np.number).columns
train.groupby("sentiment")[num_vars].agg(["mean", "median"])**

从集中趋势来看,情绪似乎没有实质性的不同。为了确保万无一失,我们来看看分布情况:

**def plot_distribution(df, var, hue):
    """Plot overlayed histogram and density plot per sentiment."""
    fig, ax = plt.subplots(nrows=1, ncols=2, figsize=[16,4])

    # Histogram
    sns.histplot(data=df, x=var, hue=hue, bins=30, kde=False, ax=ax[0])
    ax[0].set_title(f"Histogram for {var}")

    # Density plot
    sns.kdeplot(data=df, x=var, hue=hue, shade=True, ax=ax[1])
    ax[1].set_title(f"Density plot for {var}");

# Plot for all numerical variables
for var in num_vars:
    plot_distribution(train, var, 'sentiment')**

情感之间变量的分布似乎非常相似。它们不太可能作为有用的特性,但是我们总是可以尝试。也许我们可以把它添加到一个值得尝试的项目列表中

在我们结束之前,让我们看看最后一件事——常用词是否因情感不同而不同。让我们为每种情绪准备数据:

**pos_documents = [preprocess_text(document) for document in train.loc[train['sentiment']=='positive', 'review']]
pos_tokens = [item for sublist in pos_documents for item in sublist]
pos_freq = FreqDist(pos_tokens)
pos_common = [word for word, frequency in pos_freq.most_common(20)]
print(f"***** 20 frequent tokens in positive reviews: *****\n{pos_common}\n")neg_documents = [preprocess_text(document) for document in train.loc[train['sentiment']=='negative', 'review']]
neg_tokens = [item for sublist in neg_documents for item in sublist]
neg_freq = FreqDist(neg_tokens)
neg_common = [word for word, frequency in neg_freq.most_common(20)]
print(f"***** 20 frequent tokens in negative reviews: *****\n{neg_common}\n")common = set(neg_common).union(pos_common)
print(f"***** Their union: *****\n{common}\n")**

这两种情绪中最常见的三个符号是“电影”、“电影”和“一”。让我们看看它们的频率:

**# Create a dataframe containing the common tokens and their frequency
common_freq = pd.DataFrame(index=common, columns=["neg", "pos"])
for token in common:
    common_freq.loc[token, "pos"] = pos_freq[token]
    common_freq.loc[token, "neg"] = neg_freq[token]
common_freq.sort_values(by="pos", inplace=True)# Add ranks and rank difference
common_freq['pos_rank'] = common_freq['pos'].rank()
common_freq['neg_rank'] = common_freq['neg'].rank()
common_freq['rank_diff'] = common_freq['neg_rank'] - common_freq['pos_rank']
common_freq.sort_values(by='rank_diff', inplace=True)
common_freq.head()**

现在,是时候想象了:

**fig, ax =plt.subplots(1, 2, figsize=(16, 10))
sns.barplot(x="pos", y=common_freq.index, data = common_freq, ax=ax[0])
sns.barplot(x="neg", y=common_freq.index, data = common_freq, ax=ax[1])
fig.suptitle('Top tokens and their frequency by sentiment');**

嗯,有趣的是,在正面评论中,“film”比“movie”出现得更频繁。在负面评论中,它被翻转了。也许它们不应该被添加到停用词中,尽管它们出现的频率很高。我们再来看一下图表,但是排除这两个常用词:

**rest = common_freq.index.drop(['film', 'movie'])
fig, ax =plt.subplots(1, 2, figsize=(16, 10))
sns.barplot(x="pos", y=rest, data = common_freq.loc[rest], ax=ax[0])
sns.barplot(x="neg", y=rest, data = common_freq.loc[rest], ax=ax[1])
fig.suptitle('Top tokens and their frequency by sentiment');**

很直观地看到,单词“棒”、“好”和“爱”在正面评价中更频繁出现,而“甚至”和“不好”在负面评价中更频繁出现。

还有很多东西要探索,但是是时候总结了!🕛

3.结束语💭

干得好,你走了这么远!😎让我们总结一下要点:
◼️在标记时删除标点和数字
◼️规范化文本(小写、字母等)
◼️用“br”和其他缺失的辅助动词丰富停用词
◼️删除罕见词

一个不错的尝试列表:
◼️将英式拼写转换为美式拼写(反之亦然)
◼️保留数字并将其转换为单词
◼️在标记时保留连字符
◼️包含二元模型
◼️添加数字特征,如句子、标记、字符和停用词的数量

图片由 Andreas ChuUnsplash 上拍摄

您想要访问更多这样的内容吗?媒体会员可以无限制地访问媒体上的任何文章。如果您使用 我的推荐链接成为会员,您的一部分会费将直接用于支持我。**

谢谢你看我的帖子。探索性数据分析是一项开放式的主观任务。您可能已经注意到,在探索和预处理时,我们不得不做出许多小的选择。我希望这篇文章能让你体会到如何构建分析,并展示一些你可以在这个过程中思考的问题。做了一些探索性分析后,我们离构建模型更近了一步。在下一篇文章中,我们将为模型准备数据。以下是该系列另外两篇帖子的链接:◼️ 用 Python 预处理文本
◼️ 用 Python 进行情感分类

以下是我的其他 NLP 相关帖子的链接:
◼️Python 中的简单 word cloud
(下面列出了一系列关于 NLP 介绍的帖子)
◼️ 第一部分:Python 中的预处理文本
◼️ 第二部分:词条满足和词干的区别
◼️ 第三部分:TF-IDF 解释
◼️ 第四部分:python 中的监督文本分类模型

再见🏃💨

探索最受欢迎的“佛罗里达人”标题的数据库

原文:https://towardsdatascience.com/explore-a-database-of-the-most-popular-florida-man-headlines-cec6f62c8360?source=collection_archive---------45-----------------------

这个网络应用程序使用了有史以来排名最高的佛罗里达人 subreddit 帖子的解析标题。

近十年来,“佛罗里达人”一直是互联网文化中的主流反英雄。像“佛罗里达人太胖进不了监狱”和“佛罗里达人偷恐龙骨头”这样的标题很容易成为迷因化的素材。2013 年初,“佛罗里达人”在 Twitter 上被封为圣人,有 @_FloridaMan ,在 Reddit 上被封为圣人,有 r/FloridaMan subreddit 。经过七年的转发和投票,我们可以收集最受欢迎的标题,看看是什么让“佛罗里达人”标题成功。

下面是这些标题背后的数字的简要概述,这里是一个网络应用的链接,在那里你可以自己浏览文章。

快速提醒一下:“佛罗里达人”的标题经常包含成人内容。

数据分析

按性别

“佛罗里达男人”标题的数量是“佛罗里达女人”标题的四倍多,尽管考虑到数据来自“佛罗里达男人”子网站,这并不令人惊讶。

通过出版物

《坦帕湾时报》(以下显示为 tampabay.com)遥遥领先,是佛罗里达州男人材料的最大生产商,至少这些材料通常在网上排名很好,但在收集的数据中,他们的故事有一些得分最高和最低的网址。

在前 10 名中,福克斯新闻频道是唯一一家全国性的媒体(abcactionnews.com是坦帕湾地区特有的),他们主要在他们的“美国”办公桌下提交文章。

按出版物的 Reddit 评分(剔除异常值)

正如上面的方框图所示,redditors 对新闻机构的评价没有太大的差异。

按内容

佛罗里达州的“阳光法律”允许公众接触政府材料,包括逮捕记录。当看下面的动词分布时,这些定律对“佛罗里达人”现象的影响就很明显了。像“逮捕”、“偷窃”和“开枪”这样的动词很常见。

用红色突出显示的许多顶级动词显示了佛罗里达人的标题如何在很大程度上与警方记录中可能找到的项目相关。佛罗里达州的“阳光法律”允许公众查阅警方记录。

WEB 应用程序

这里有一个链接指向我的标题浏览器。这个平台可以让你轻松地筛选数据库。您可以按字母顺序或频率排序。

方法学

Reddit API

数据是用 Reddit 的 API 提取的。使用 Reddit 的praw Python 库相对简单,很容易收集 URL。以下几行代码是必需的:

完整的代码可以在这个笔记本里找到。API 提供的每个页面对象包含 100 篇文章,不幸的是,reddit 只提供前 1000 篇 Reddit 提交的文章,所以我们的数据库无法扩展到整个 subreddit。(欢迎对如何获取更多数据提出建议!)

为什么是 Reddit?

与 Twitter 相比,Reddit 的数据更容易解析。Twitter 上主要的“佛罗里达人”包括图片和评论,而 Reddit 上的帖子通常只是一个带有标题的 URL。此外,“佛罗里达人”subreddit 有 64 万名粉丝,Twitter 账户有 41.2 万名粉丝,因此它们为一篇文章的受欢迎程度提供了类似的指标。

用 NLTK 解析标题

我使用 NLTK(自然语言工具包)解析标题,这是一个 python 库,可以解析句子并给出词性。为了使分析和 web 应用程序的动词标准化,我们还可以使用WordNetLemmatizer函数将所有动词转换为一种时态(例如,“give”、“give”、“giving”都将被压缩为“give”)。

下面的函数帮助我正确地整理了标题中的动词,尽管需要一些手工输入和清理。

清理数据

Reddit 特定的帖子被删除,一些帖子(如漫画或大头照)不利于网络应用的格式,并被删除。基本的清理和动词解析都记录在这本笔记本里。虽然许多标题都经过了人工复查,但肯定仍然存在重复和错误。

数据和笔记本

如果您对查看脚本或数据感兴趣,可以在此链接获得,完整的清理数据集可在此处获得。

疑问?更正?联系我或者在 我的网站 上查看更多项目。

探索和清理:任何数据项目的第一步。

原文:https://towardsdatascience.com/explore-and-clean-first-steps-of-any-data-project-976a1d80d7aa?source=collection_archive---------24-----------------------

在您开始使用“有趣”和“令人兴奋”的算法和方法之前,这里是在您的数据项目中取得成功的第一步。

人们很容易如此热衷于从数据中获得一些东西,以至于直接跳到编写预测算法或其他形式的机器学习/人工智能,以进行尝试。可能和大多数人一样,我也掉进了那个陷阱,对自己发育不良的进步感到失望。

在本文中,我想强调在开始“有趣”的东西之前了解您的数据的价值,并用一些具体的例子来说明如何使用一个简单的数据集来做到这一点。我是一个 Python 用户,所以我的代码示例会提到这一点,但是方法的理论将适用于跨语言。

浏览和清理您的数据

当您考虑您的数据时,在开始清理之前,探索您的数据,找出那里有什么以及哪些部分需要注意似乎是合乎逻辑的。毕竟,如果你不知道乱七八糟的是什么,你怎么能收拾干净呢?我有点同意这一点,但我认为这是一个迭代过程。对于格式错误的数据,您可能无法探索和找到您需要的信息。所以,我倾向于在探索和清理之间做一些来回。

我坚信“一张图胜过千言万语”这句话背后的含义,在数据世界中,这意味着将你拥有的数据可视化。在某些情况下,您可能无法可视化数据,因为它可能是错误的格式(您的数字是字符串类型而不是浮点数),或者您的时间被收集并分布在 4 列(小时、分钟、秒、毫秒),这对于数字数据尤其重要。

出于这个原因,我喜欢探索我的数据,然后解决我看到的问题。

在这一点上我认为值得考虑的问题是:

干净的数据意味着什么?我相信什么是干净的数据在很大程度上取决于你手头的任务,包括你的数据项目的应用和你想用这些数据做什么。但是,一般来说,它需要符合逻辑:数字应该是数字,日期应该是日期等等。下面我会详细解释我的意思。

将数据导入数据框后,我建议的第一件事就是检查您拥有的数据是否符合您的预期。为此,我使用df.head检查前五行(其中 df =您的数据帧)。

示例数据帧

在这里,您可以看到有 5 列,其中一些是单词或字符串,一个日期字段和一个整数。所有这些看起来和我对列名的期望差不多。

然而,我们看到的可能不是计算机看到的,所以检查当前的类型很重要:df.dtypes将为您完成这项工作。对于上述数据帧,我们得到以下输出:

显示数据帧中存在的类型的输出函数

正如您在这里看到的,输出告诉我不同的列名,以及其中包含的数据类型。这是单独打印数据帧所不能做到的。从表中,没有办法知道这些值是字符串、浮点数还是其他。这是很重要的信息,甚至对于最初创建简单的图来说也是如此——你不能用字符串创建散点图!—但是当您进一步深入时,您可能会使用需要数字数据而不是字符串的随机森林回归。

您可能想知道为什么所有的列都被分配了“对象”类型。这就是文档派上用场的地方。字符串被赋予对象类型,因此 Name 列和 Sex 列都是这样。然而,这不足以解释出生日期或死亡年龄栏。Pandas 还会将对象类型分配给包含混合数据的列,因此对于提到的两列,我们不知道其中的部分或全部数据是字符串还是其他数据。出生日期列具有字符串或混合数据类型的暗示是日期没有以统一的方式显示。

一般来说,如果我们在数据帧中看到人类认为是数字或日期的项目,那也是我们希望计算机看到的。在这里,我建议使用以下代码将出生日期列转换为日期,将死亡年龄列转换为整数(其中 pd 是 pandas 库):

df['Age at Death'] = df['Age at Death'].astype(int)
df['Date of Birth'] = pd.to_datetime(df['Date of Birth'])

当我再次展示数据帧时,它看起来像这样:

列重新格式化后的数据帧

所有列看起来都一样,除了出生日期列,该列已被重新格式化,因此日期是统一的。这可能不是你想要的格式,所以你可以看看文档来了解更多关于改变的细节。

但是,当我们查看每列中的数据类型时,您会看到我们转换的两种类型如下所示:

重新格式化后数据帧中存在的类型

如果你有一个简单的数据集,探索可能看起来有点费力,但我仍然认为这是一个重要的经历;这不仅是因为当你拥有复杂的数据集时,它会帮你做好准备,还因为花时间去探索可以让你看到很多你在原始数据甚至表格中看不到的东西。

让我们再举一个例子:有时当你获取数据时,你可能会得到一个文件,里面有很多零,表示单元格中没有任何内容。从这个角度来看,只要你的团队有这样的理解,那就很好。然而,你的计算机无法理解:对计算机来说,零是一个数字,因此在单元格中存在某种东西。我们需要告诉计算机这个单元格是空的。让我们向我们一直使用的数据帧添加几行:

具有两个附加行的数据帧

在上面的示例中,我们可以看到数据帧中缺少欧内斯特·卢瑟福的出生日期,这一点在 NaT 或 Not a Time(时间的空值示例)中很明显。然而,如果不仔细看死亡年龄一栏,你可能会漏掉玛丽·斯科多瓦斯卡·居里的年龄为零。对于一个大的数据帧,这两个例子都可能会被遗漏,所以简明扼要地获得数据帧的数字方面的信息是很重要的,这样你就能看到,为此我使用了df.describe():

描述函数的输出

因为我们仅有的数字列是“死亡年龄”列,所以我们预计会在输出中看到一列,如果您有一个包含多个数字列的数据帧,每个数字列的显示方式都是相似的。我们可以清楚地看到最小值是零,我们可以用我们人类的智慧意识到这里有问题。在这种情况下,可能会出现这样的情况,无论您的数据来自何处,都用零替换该值,以表明没有我们的数据。然而,在 Python 中,我们需要将它显示为 NaN 或 null,所以单元格中实际上什么也没有。这里您可以使用df.replace(0, np.NaN)(其中 np 是 numpy 库)给出以下内容:

正如您所看到的,当您单独看数据帧时,数据表示的变化似乎很小,但是深入研究类型,您会看到很大的差异。

要检查数据帧中的空值,您也可以使用df.isna().count(),它给出以下输出:

每列中出现的 Na 值的数量

上述方法是一种快速而清晰的方法,可以确定数据帧中丢失了多少数据。同样,对于这个例子来说,这似乎有点多余,但是当您开始处理数百列和数千行时,您会喜欢这种方法(甚至可能在这之前!)

一般来说,当我有数字数据时,我喜欢在完成初始清理后绘制它。我这样做是我探索的一部分:我能看到一个明显的趋势吗?是否有可能扭曲我的数据的明显异常或错误?我发现它帮助我看到实际存在的东西,而不是我在数据帧的片段中看到的东西。

我喜欢更清楚地看到我的数据,例如如下所示:

fig, ax = plt.subplots()x = df['Date of Birth']
y = df['Age at Death']
plt.plot(x, y, 'bo')
fig.suptitle('Year of Birth vs Age at Death', fontsize=20)
xlabel = 'Year of Birth'
ylabel = 'Age at Death'
ax.set_xlabel(xlabel, fontsize=10)
ax.set_ylabel(ylabel, fontsize='medium')

图解可能的可视化选项

您可能会注意到,虽然数据帧中有七行数据,但图上只有六个数据点。玛丽·斯科多瓦斯卡·居里的数值没有标绘出来,因为她死时的年龄是 15 岁。这是一件好事,如果我们用零值标绘它并继续分析,它可能导致误导的结果。

然而,Python 有点困惑,因为它显示了大约 1680 年欧内斯特·卢瑟福的数据点。似乎是由于 NaT 的出现而不是他的出生日期,Python 选择了最早的可能日期并绘制了死亡年龄数据点。这是需要注意的事情!通过一点点进一步的操作,您可以确保它也被排除,方法是从数据帧中没有空值的行中选择要绘制的图形的 x 值和 y 值,只需添加少量的df.dropna():

fig, ax = plt.subplots()df2 = df.dropna()
x = df2['Date of Birth']
y = df2['Age at Death']
plt.plot(x, y, 'bo')
fig.suptitle('Year of Birth vs Age at Death', fontsize=20)
xlabel = 'Year of Birth'
ylabel = 'Age at Death'
ax.set_xlabel(xlabel, fontsize=10)
ax.set_ylabel(ylabel, fontsize='medium')

从绘图中移除空值后绘制图表

如果我试图在清理之前绘制数据,Python 会因为不正确的数据类型而抛出一个错误。大多数绘图函数需要整数、浮点数或日期,除非它们是正确的类型,否则看不到我们看到的数字。如果没有人工检查和删除空值行的操作,我们会得到一个误导性的图形,尽管没有抛出任何错误。

遗言

我还可以对数字数据做更多的处理,如果我想把它变成整数格式的话,也许我想得到男性和女性的平均死亡年龄,这样我就可以比较了?我可能想将性别列改为二进制类型,因为这可以为分析提供更多的机会。

自然,随着数据集越来越复杂,可能性变得越来越令人兴奋。

这是我教给任何新程序员的一个过程,作为他们使用数据的第一步。我自己在我的博士学位中使用的每一个新的数据集上使用它,当我只是在新的数据集上玩的时候。当然,这些数据集比我在本文中展示的例子更复杂,但原理是相同的。

使用定制的交互式 web 应用程序探索任何数据:体育数据科学

原文:https://towardsdatascience.com/explore-any-data-with-a-custom-interactive-web-app-data-science-with-sports-410644ac742?source=collection_archive---------14-----------------------

了解如何使用领先的数据可视化工具 Plotly 和 Streamlit(包括数据和代码)构建一个交互式、可重用的 web 应用程序来进行探索性数据分析

丹尼斯·简斯在 Unsplash 拍摄的背景照片

大多数好的数据项目都是从分析师做一些事情来感受他们正在处理的数据开始的。

他们可能会拼凑一个 Jupyter 笔记本来查看数据摘要、前几行数据和 matplotlib 图表。有些人可能会把数据当作 Excel 表格来浏览,摆弄数据透视表。真正拥有数据的人可能更喜欢直接盯着原始数据表。

这些都不是理想的解决方案。其中一些解决方案可能只适合我们当中的受虐狂。那么一个人要做什么呢?

对我来说,我更喜欢建立一个数据探索的 web 应用程序。

有一些关于切片,分组,过滤的能力,最重要的是——数据,这有助于我理解它,并帮助我制定问题和假设,我希望在回答。

它让我能够直观地与数据互动。

这些天来,我首选的工具包是 PlotlyStreamlit 。在过去的一段时间里,我已经写了足够多关于 Plotly 的东西——我认为它是 Python 最好的数据可视化包。但是 Streamlit 确实改变了我的工作方式。因为它非常简洁,当我修补时,几乎不需要额外的努力就可以将我在 python 脚本中的情节和评论转换成具有交互性的 web 应用程序。(仅供参考—我在这里写了 Dash 和 Streamlit 的对比

我更喜欢构建一个用于数据探索的 web 应用程序

因此,在本文中,我想分享一个简单的例子,用这些工具构建一个数据探索应用程序。

现在,对于一个数据项目,我们需要数据,这里我将使用 NBA 的数据。学习编程可能是枯燥的,所以使用一些相关的东西,如体育数据,有助于我保持专注;希望对你也一样。

(如果你不关注 NBA 也没关系,因为重点是数据科学和编程!)

在开始之前

接下来,安装几个包— plotlystreamlitpandas。用一个简单的pip install [PACKAGE_NAME]安装每一个(在您的虚拟环境中)。

这篇文章的代码在我的 GitHub repo here 上,所以你可以下载/复制/叉走你喜欢的内容。

这个脚本叫做data_explorer_app.py——所以你可以在 shell 中运行它:

streamlit run data_explorer_app.py

哦,这是我计划写的关于使用 NBA 数据的一系列数据科学/数据分析文章中的第一篇。所有的钱都会去回购,所以你要睁大眼睛!

如果您正在跟进,请使用以下内容导入密钥库:

import pandas as pd
import plotly.express as px
import streamlit as st

我们准备好出发了。

数据深潜

流线化

我们在这里使用 Streamlit,因为它旨在帮助我们快速构建数据应用程序。因此,我们要建立的是一个 Streamlit 应用程序,然后将在本地运行。(要了解更多信息,您可以点击这里查看我的 Dash v Streamlit 文章。)

如果您从未使用过 Streamlit,这就是您构建基本应用程序所需的全部内容:

import streamlit as st
st.write("Hello, world!")

将其保存为app.py,然后用 shell 命令streamlit run app.py执行:

看,妈妈,这是一个网络应用程序!

你就有了一个正常运行的 web 应用程序!构建一个 streamlit 应用程序就是这么简单。然而,更令人惊讶的是,构建一个有用的应用程序并不难。

哦,顺便说一句,你不需要每次改变脚本时都停止并重启服务器。每当更新底层脚本文件时,您都会看到右上角弹出一个按钮,如下所示:

寻找此提示以刷新应用程序

只要保持脚本运行,并在每次想要查看最新版本时点击这里的重新运行。

准备好了吗?好了,我们走吧!

原始数据探索

我最初想做的是查看整个原始数据集。第一步,我们从 CSV 文件加载数据:

df = pd.read_csv("data/player_per_game.csv", index_col=0).reset_index(drop=True)

一旦加载了数据,只需输入st.write(df)就可以创建整个数据框架的动态交互式表格。

以交互式表格的形式浏览整个数据集

并且可以用st.write(df.describe())类似地绘制列的各种统计数据。

两行代码中的两个动态表

我知道你可以在 Jupyter 笔记本上绘制一个表格,但是区别在于交互性。首先,用 Streamlit 呈现的表可以按列排序。正如您稍后将看到的,您可以将过滤器和其他动态元素整合到笔记本电脑中,这是真正的力量所在。

现在我们准备开始添加一些图表到我们的应用程序。

分布可视化

单个变量的统计可视化非常有用,在某种程度上,我认为它是一个不可或缺的工具,不仅仅是查看原始数据。

我们将从一个变量的可视化数据开始分析,使用交互式直方图。直方图可以用 Plotly 来构造,如下所示:

hist_fig = px.histogram(df, x=hist_x, nbins=hist_bins)

传统上,我们必须手动调整xnbins变量,看看会发生什么,或者从这些变量的各种排列中创建一个巨大的直方图墙。相反,让我们看看如何将它们作为输入来交互式地研究数据。

直方图将分析熊猫数据帧中一列的数据。让我们通过调用st.selectbox()模块将其呈现为下拉框。我们可以获取一个列的列表作为df.columns,另外我们提供了一个默认的选择,使用df.columns.get_loc()方法得到列号。综合起来,我们得到:

hist_x = st.selectbox("Histogram variable", options=df.columns, index=df.columns.get_loc("mp_per_g"))

然后,可以使用st.slider()模块调用滑块,让用户选择直方图中的条块数量。该模块可以定制一个最小/最大/默认和增量参数,如下所示。

hist_bins = st.slider(label="Histogram bins", min_value=5, max_value=50, value=25, step=1)

然后,可以将这些参数组合在一起,得出下图:

hist_fig = px.histogram(df, x=hist_x, nbins=hist_bins, title="Histogram of " + hist_x,
                        template="plotly_white")
st.write(hist_fig)

将它与一个小标题st.header(“Histogram”)放在一起,我们得到:

应用程序的直方图部分

我建议在这里花点时间研究一下这些数据。例如,看看不同的数据,比如场均篮板数:

场均篮板直方图

或职位:

位置直方图

交互性使得对数据的更容易、动态、主动的探索成为可能。

您可能已经注意到,在最后这张图中,直方图类别没有任何合理的顺序。这是因为这是一个分类变量。因此,在没有提供顺序的情况下,Plotly(我认为)正在根据它第一次遇到每个类别的顺序绘制这些类别。因此,让我们对进行最后一次更改来解决这个问题。

因为 Plotly 允许一个category_orders参数,我们可以传递一个排序的位置顺序。但这与其他任何参数都无关。相反,我们可以做的是根据选择的输入值隔离列,并通过按字母顺序排序来传递它们,如下所示:

df[hist_x].sort_values().unique()

总之,我们得到:

hist_cats = df[hist_x].sort_values().values
hist_fig = px.histogram(df, x=hist_x, nbins=hist_bins, title="Histogram of " + hist_x,
                        template="plotly_white", category_orders={hist_x: hist_cats})

位置直方图—按字母顺序排序

这样,任何分类(或顺序)变量都将按顺序显示

现在我们可以更进一步,用箱线图对我们的数据进行分类。箱线图做的工作与直方图相似,因为它们显示分布,但它们真正最擅长的是显示这些分布如何根据另一个变量而变化。

因此,我们的应用程序的箱线图部分将包括两个下拉菜单,如下所示。

box_x = st.selectbox("Boxplot variable", options=df.columns, index=df.columns.get_loc("pts_per_g"))
box_cat = st.selectbox("Categorical variable", ["pos_simple", "age", "season"], 0)

只需将这两个输入传递给 Plotly 来构建一个图形:

box_fig = px.box(df, x=box_cat, y=box_x, title="Box plot of " + box_x, template="plotly_white", category_orders={"pos_simple": ["PG", "SG", "SF", "PF", "C"]})
st.write(box_fig)

然后…瞧!你有一个交互式方框图!

交互式箱线图

您会注意到,我手动为我的简化头寸栏传递了一个订单。原因是这个顺序是比较随意的,篮球特有的顺序(从 PG 到 C),不是字母顺序。尽管我希望所有的东西都是参数化的,但有时你不得不求助于手工规范!

相关性和过滤器

数据可视化或探索性数据分析中的另一件大事是理解相关性。

例如,它可以方便地用于数据科学中的一些手动特征工程,并且它实际上可能会将您引向一个您可能从未考虑过的研究方向。

现在让我们只关注散点图中的三维空间。

不,不是在 x,y,z 方向。我不是怪物。下面我举了一个例子——你能理解这是怎么回事吗?

我不是三维散点图的忠实粉丝

我不要,谢谢。

颜色将是表示数据的第三维度。我将所有列都保留为前两列,并且只保留了有限的颜色选择——但是您真的可以做任何您想做的事情。

corr_x = st.selectbox("Correlation - X variable", options=df.columns, index=df.columns.get_loc("fg3a_per_g"))
corr_y = st.selectbox("Correlation - Y variable", options=df.columns, index=df.columns.get_loc("efg_pct"))
corr_col = st.radio("Correlation - color variable", options=["age", "season", "pos_simple"], index=1)

关联开!

并且该图表可以被构造如下:

fig = px.scatter(df, x=corr_x, y=corr_y, template="plotly_white", color=corr_col, hover_data=['name', 'pos', 'age', 'season'], color_continuous_scale=px.colors.sequential.OrRd)

那么告诉我——它们有关联吗?

但是这个图表并不理想。首先是因为数据被异常值所支配。看到左上方孤独的点了吗?那些有效 FG%为 1.5 的乡亲们不是什么篮球之神,但这是极小样本量的副作用。

那么我们能做什么呢?让我们对数据进行过滤。

我将在这里放入两个交互部分,一个用于选择过滤器参数,另一个用于输入值。由于我不知道这里的参数是什么,我将简单地取一个空文本框,它将接受数字作为输入。

corr_filt = st.selectbox("Filter variable", options=df.columns, index=df.columns.get_loc("fg3a_per_g"))
min_filt = st.number_input("Minimum value", value=6, min_value=0)

使用这些值,我可以像这样过滤数据帧:

tmp_df = df[df[corr_filt] > min_filt]

然后将临时数据帧tmp_df而不是原始数据帧传入图中,我们得到:

效率和射击次数之间的相关性(为高容量射手过滤)

该图表可用于查看各种统计数据之间的相关性。例如,看看伟大的 3 分射手通常也是伟大的罚球手:

罚球准确性与三分球准确性

或者说,伟大的篮板手往往也是盖帽手。有趣的是,这项运动已经发生了变化,现代球员平均每场比赛都不会有很多盖帽。

场均篮板 vs 场均盖帽

绘制篮板和助攻图,它们显示出某种逆相关性,并且根据位置很好地分层。

场均助攻 vs 场均篮板

我们已经可以从我们的应用程序中看到相当多的趋势和相关性。最后,让我们创建一些热图来查看各组数据列之间的总体相关性。

与热图的广义相关性

散点图对于查看单个数据点很有用,但有时只可视化数据集也很好,这样我们可以立即看到哪些列可能很相关、不相关或反向相关。

热图对于这项工作来说是完美的,因为它将所谓的关联矩阵可视化。

由于热图最擅长可视化输入类别集之间的相关性,所以让我们使用一个包含多个类别的输入。因此,st.multiselect()是这里选择的模块,而df.corr()是我们创建相关矩阵所需要的。

组合代码是:

hmap_params = st.multiselect("Select parameters to include on heatmap", options=list(df.columns), default=[p for p in df.columns if "fg" in p])
hmap_fig = px.imshow(df[hmap_params].corr())
st.write(hmap_fig)

我们得到了:

场均助攻 vs 场均篮板

这些列中哪些正相关,哪些不相关,一目了然。我还建议玩不同的色标/色板来获得额外的乐趣!

今天就到这里——我希望这很有趣。在我看来,在探索方面,很难有比这样的交互式应用更好的了,Plotly 和 Streamlit 的强大功能使得为我的目的构建这些定制应用变得如此容易。

请记住,我在这里提出的只是一些基本的建议,我相信您可以根据自己的目的和喜好构建更有用的东西。我期待看到他们所有人!

但是在你离开之前——如果你喜欢这个,在 twitte r 上打个招呼/关注,或者关注这里的更新。ICYMI:我也写了这些文章,你可能会觉得有用:

[## 这些数据科学产品组合将让您惊叹不已并深受启发(2020 年中期版)

使用这些来改进您自己的数据科学产品组合,学习新技能或发现新的有趣项目。

towardsdatascience.com](/these-data-science-portfolios-will-awe-and-inspire-you-mid-2020-edition-728e1021f60) [## Plotly Dash 与 Streamlit——哪个是构建数据仪表板 web 应用程序的最佳库?

用于共享数据科学/可视化项目的两个顶级 Python 数据仪表板库的比较——

towardsdatascience.com](/plotly-dash-vs-streamlit-which-is-the-best-library-for-building-data-dashboard-web-apps-97d7c98b938c) [## 使用 Python 在几分钟内构建一个 web 数据仪表板

通过将您的数据可视化转换为基于 web 的仪表板,以指数方式提高功能和可访问性…

towardsdatascience.com](/build-a-web-data-dashboard-in-just-minutes-with-python-d722076aee2b)

回头见!保持安全:)

探索新冠肺炎信息技术

原文:https://towardsdatascience.com/explore-covid-19-infodemic-2d1ceaae2306?source=collection_archive---------35-----------------------

图片来源:Unsplash

自然语言处理,可视化

得知半数加拿大人被新冠肺炎阴谋论愚弄,令人心碎。

根据世卫组织的说法,新冠肺炎的相关信息和病毒本身一样危险。同样,阴谋论、神话和夸大的事实可能会产生超出公共卫生范畴的后果。

感谢像 Lead StoriesPoynterFactCheck.orgSnopeseuvsdisifo这样的项目,它们监视、识别并核实散布在世界各地的虚假信息。

为了探究新冠肺炎假新闻的内容,我使用了关于真假新闻的严格定义。具体来说,真正的新闻文章是那些被认为是真实的,并且来自可靠的新闻来源的文章。假新闻故事是已知是虚假的故事,来自众所周知的假新闻网站,故意试图传播错误信息。

牢记上述定义,我从各种新来源收集了超过 1100 篇关于新冠肺炎的新闻文章和社交网络帖子,然后给它们贴上标签。数据集可以在这里找到

数据

流程 _ 数据. py

经过一番清理,可以看到我们有 586 篇真文,578 篇假文。

df.loc[df['label'] == 'TRUE'].source.value_counts()

图 1

真实文章大部分收集自哈佛健康出版社、《纽约时报》约翰霍普金斯大学彭博公共卫生学院世卫组织疾控中心等等。

df.loc[df['label'] == 'FAKE'].source.value_counts()

图 2

这些假药是从极右翼网站“自然新闻”和另类医学网站“orthomolecular.org”的脸书邮报上收集的。一些文章或帖子被从互联网或社交网络上删除,然而,它们能够进入互联网档案馆等。

使用下面的函数,我们将能够阅读任何给定的新闻文章,以便我们能够确定如何清理它们:

print_plot.py

print_plot(1000)

这些文章非常干净,我们可以去掉标点符号,改成小写。

df['text'] = df['text'].str.replace('[^\w\s]','')
df['text'] = df['text'].str.lower()

文章的长度

在接下来的步骤中,

  • 获取每篇文章的情绪得分,极性位于[-1,1]的范围内,其中 1 表示积极情绪,-1 表示消极情绪。
  • 获取每篇文章的长度(字数)。

polarity_length.py

图 3

数据中的大部分文章包含不到 1000 个单词。尽管如此,很少有文章超过 4000 字。

当我们用标签来区分时,就文章的长度而言,真消息和假消息之间没有显著的区别。虽然大部分真的文章看起来比假的短一点,但是在数据上。

text_len_hist.py

图 4

为了显示不同值的文本长度的概率密度,我们可以使用 violin plot:

text_len_violin.py

图 5

脸书对哈佛

平均而言,脸书的文章比哈佛的健康文章短得多:

facebook _ 哈佛 _textlen_hist.py

图 6

我们也可以用小提琴的情节来呈现:

facebook _ 哈佛 _textlen_violin.py

图 7

也许我们都很熟悉,脸书的假帖子在内容上往往更短。张贴它们的人试图通过启发而不是论据来说服读者。

情感极性

label_polarity.py

图 8

就情感而言,真消息和假消息之间没有明显的区别。这可以用下面的小提琴情节来证实:

极性 _violin.py

图 9

当我们比较这四个来源之间的情绪极性时,我们可以看到纽约时报和自然新闻的情绪分布比哈佛健康新闻和脸书的情绪分布窄得多。

极性 _ 源. py

图 10

这意味着《纽约时报》的新闻文章和数据中的自然新闻听起来不那么情绪化。

这可以用下面的小提琴情节来证实:

来源 _violin.py

图 11

情绪 vs 文章长度 vs 真实性

我注意到我收集的新闻文章和帖子,既不是很强的正面,也不是很强的负面。大部分都在适度正的范围内,而且大部分长度不到 1000 字。

len_polarity.py

图 12

情绪和文章长度没有明显的关系。而且一般来说,一篇文章的情绪或篇幅并不能反映其真实性。假新闻和真新闻之间的区别可能是相当武断的。

polarity _ scatter.py

图 13

df.groupby(['source']).mean().sort_values('polarity', ascending=False)

图 14

我注意到鲁迪·朱利安尼的帖子拥有最高的情感评分,很想知道它是关于什么的:

df.loc[df['source'] == 'RudyGiuliani']['text'][880]

当然是关于羟氯喹

真假新闻文章的内容

现在,我们将了解我的数据中包含了哪些主题。

true_bigram.py

fake_bigram.py

  • 促进治愈:这包括使用高剂量静脉注射维生素 c
  • 关于起源的猜测:这个主题包括声称冠状病毒是在用于生物武器的实验室中创造的,或者 5G 技术导致了疾病。
  • 关于有影响力的人的谣言:比如冠状病毒是比尔盖茨和福奇博士代表制药公司策划的。
  • 利用人们的恐惧:比如梅琳达·盖茨基金会和约翰·霍普金斯大学已经在三个月前通过 Event 201 预测到了冠状病毒。

从我们的数据来看,真假新闻内容的一个明显区别是,假新闻似乎更经常使用人名,这表明假新闻可能更私人化。

naturalnews.com 对 orthomolecular.org

上述两个新闻来源都在宣扬阴谋论,然而,它们确实侧重于不同的主题。

自然 _bigram.py

naturalnews.com 方面一直在传播虚假信息,比如冠状病毒是中国一家实验室设计的生物武器,以及/或者它被传播是为了掩盖与接触 5G 无线技术有关的所谓有害健康影响。

ortho_bigram.py

orthomolecular.org 一直在推广使用高剂量静脉注射维生素 C 作为治疗方法,但这是没有根据的。

你可以随意查看其他来源。

摘要

首先,我们不知道在收集数据时是否有任何选择偏差。第二,虽然我们可以说这些是用户参与度很高的新闻故事,但我们不能说这些故事产生了什么实际流量。尽管有这些限制,这个数据集提供了合理的事实标签,我们知道所有的故事都被广泛阅读和分享。

Jupyter 笔记本可以在 Github 上找到。很快再聊。

免费探索零编码技能的数据分析

原文:https://towardsdatascience.com/explore-data-analytics-with-zero-coding-skills-for-free-f2c982d1e2d6?source=collection_archive---------58-----------------------

你所需要的就是在网上探索这些开源工具。

斯蒂芬·道森在 Unsplash 上拍摄的照片

你最近是否经常听到新的行业术语——数据分析(更早是 AI-ML)?这听起来很复杂,但又足够简单吗?理解模型背后的逻辑却不知道如何编码?害怕在赶时髦之前花太多时间学习编码?

不要担心,有一些非常棒的工具可以免费提供给非编码人员,可以帮助他们立刻开发复杂的模型。这些工具完全免费供个人使用,非常简单和直观,可以帮助一个人练习,而不用学习如何编码。

我是一个业余的程序员,但也是一个机器学习的狂热爱好者。我可以编码,但我尽可能避免它(感谢 Excel 中的录制宏选项),直到我无法避免它。

我正致力于开发一个预测道路交通的模型,当我开始寻找非编码资源并发现这些宝石时,我不得不尝试很多东西。我正在讨论我找到的最好的三个。同样,这些软件对个人用户开放源代码,但有商业用途的定价版本。

这些工具不能做什么

请注意,虽然这些工具消除了对编码的需求,但是您对模型、数据准备基础和统计的理解应该高于最低要求。原因是当您编码时,您确切地知道正在做什么以及如何做,而在大多数这些工具中,默认参数是预先加载的,并且有时代码对用户是不可见的。因此,如果用户没有进行彻底的 QA,模型错误很容易被忽略。

除此之外,这些工具不会告诉您使用哪种数据清理技术、构建哪种模型或比较哪种统计数据。相反,这些工具将让您轻松完成上述所有任务,并让您有更多时间思考和分析数据。

既然您已经阅读了所有警告,让我们直接开始吧。

1.Knime 分析

这是目前为止开源领域中最好的工具。

Knime 是一个非常直观的平台,可以在工作流环境中使用拖放节点来创建模型。它建立在 python 之上,拥有用于数据输入、数据清理、建模(回归、聚类、分类、神经网络等)、统计和常用表示的小部件。

它有一个桌面版本(我喜欢它)和一个服务器版本,供想要在网络上开发和部署这些模型工作流的人使用。在您的机器上安装 Knime 相当容易,使用它就更容易了。下面是一个神经网络模型的例子。

构建神经网络所需的每个动作都有节点。导入数据,对数据进行划分,将一部分数据提供给学习者、预测者(测试集),然后是用于检查模型准确性的评分者。可以在使用连接器相互连接的节点中设置参数,并且可以按顺序执行参数。

学分—我桌面上的 Knime 工作区

2.柑橘

Orange 是一款开源的机器学习、数据可视化和分析工具。Orange 还处理以工作流模式排列的窗口小部件,并为特定任务(时间序列、生物信息学等)提供了一些专门的库。

Orange 的用户界面更加流畅,但是它的节点列表没有 Knime 详尽。它有许多可视化选项,可以产生像样的数据分析。它建立在 python 之上,可以帮助创建和评估回归、分类、神经网络、聚类、时间序列等模型。

演职员表— 桔子网站

3.蓝天统计

Bluesky 是一个基于 R 的工具,可用于数据建模和可视化。它是开源的,可用于桌面。它有一个丰富的图形用户界面,它可以帮助 R 新手缓解学习曲线,因为每个函数的 R 代码都是可见的。

BlueSky 缺乏工作流风格的架构和节点功能。相反,它在类似于 MS Office 功能区选项卡的选项卡下列出了一些功能。BlueSky 的美妙之处在于它是建立在 R 之上的,R 是一种非常强大的统计数据分析语言。它有命令编辑器,由于代码对用户是完全可见的,用户可以非常容易地根据自己的喜好修改代码。它确保 R 的普通用户可以节省大量使用这个应用程序的时间。

积分-蓝天统计用户手册

市场上有许多数据分析工具,但大多数都不是开源的。这给仍处于数据科学探索阶段的个人用户造成了困难。

这三个工具是我处理小型数据分析问题的最爱。它们可以为新手节省大量的时间,这些新手可能对学习编码的想法望而生畏。

该列表基于 2019 年末可用的工具。如果我发现更多类似的工具,我会更新这个。我希望这个故事对您开始数据分析之旅有所帮助!

通过动态的人物社交网络探索哈利·波特

原文:https://towardsdatascience.com/explore-harry-potter-via-a-dynamic-social-network-of-characters-f5bed9a39f01?source=collection_archive---------15-----------------------

小说通常提供复杂的叙述,读者可能很难理解整本书的内容。因此,提出有助于获得更好理解的工具是一条有趣的道路,这是一条尚未探索的道路。事实上,尽管最近开发了像 GPT-3 这样的 NLP 算法,在各种各样的任务(翻译、问答、文本生成……)中表现出色,但真正理解这本书甚至是一个好的摘要仍然遥不可及。

然而,从小说中获得有益的洞见还有其他方法。在这篇文章中,我们转向一个处于自然语言处理(NLP)和网络科学(NS)十字路口的概念——应用图论的一个新兴分支,它汇集了许多学科的传统,包括数学、社会学、经济学和计算机科学[3]。更准确地说,我们从书的文本内容中创建了一个相关的动态异质人物社交网络,然后利用这个图表中的信息来提高人们对小说的理解。特别是,我们关注五种不同的应用:人物的重要性、叙事结构变化、社区检测、总结和书籍比较。对他们的调查给了我们关于这本书的情节、作者的风格或人物的重要性、关系和角色的宝贵线索。

我们将我们的分析应用于 J.K .罗琳的名著:“《哈利波特与魔法石》(HP1)——尽管这种分析绝对可以应用于任何一本书。

这时你可能还在疑惑“ 但是 w 这是一个动态的异质人物社交网络 ?”回答你的问题,一个人物网络无非是一个,意思是一组节点 V 和边 E,其中节点代表书中的人物,边代表他们之间的互动。虽然文献中的绝大多数方法都集中在静态方法上,根据定义,静态方法在时间上是固定的——整本书有一个图表,但我们遵循的是动态方法——一个不断随时间演变的图表,能够保留叙事的时间信息。为了创建这个动态方面,我们额外使图异构,也就是说,具有几个边类型和节点类型。换句话说,不是所有的节点都表示字符,也不是所有的边都表示它们之间的交互。但是我们稍后将回到这一点。

下面是对将要涵盖的内容的更准确的概述。

  • 文本处理
    (1)从网上检索书籍并按章节拆分
    (2)提取出现在文本中的人物名称
    (3)将每个人物出现与对应的实体匹配
  • 图形创建
    (1)构建完整的动态异构图形
    (2)从中导出多个感兴趣的图形(动态实体图形、静态实体图形……)
    (3)使用名为 gephi 的特殊软件进行可视化
  • 图形分析with networkx
    (1)人物重要性
    (2)叙事中的结构变化
    (3)社区检测
    (4)书籍的写作风格比较
    (5)通过图形的 k-core
    总结一本书(6)其他应用:关系预测、体裁/作者/风格分类……

关于预处理图形创建部分的更多细节,可以参考原 论文 ,其中代码可在Github上获得。但是如果图形分析部分是你唯一感兴趣的,你可以跳过它们。

预处理

为了创建一种动态字符图,我们首先需要对原始文本进行一些自然语言处理。预处理模块的目的是捕获书中所有角色的所有出现,在它们不同的形式下;并存储以下信息:

character_name: [str]角色的名字(例如:'哈利')
pos: [int]开始以来的令牌数(例如:30490)
chapter: [int]章节的索引(例如:2)
entity : [str]对应的实体(例如:'哈利波特')

  1. 我们从网上检索这本书。txt 格式,使用项目古腾堡库并按章拆分。为什么是?因为章节是剧情演变的兴趣单元。
  2. 我们在整本书中一章一章地运行 伯特 NER——预先训练好的谷歌伯特为实体识别任务进行了微调——来检测所有的字符出现。我们只保存‘PER’实体(人物),我们将它们与它们在正文中的位置和相应的章节索引一起存储。
  3. 我们将提到每个角色与一个独特的实体 (=主角)进行匹配。社交网络将实体联系起来,因此将所有的共同参照对象组合在一起是至关重要的一步。例如,关于哈利·波特,我们希望哈利、波特、波特先生、哈利·波特被链接到一个单一的实体。在这种情况下,哈利波特。我们遵循 M.Ardanuy 和 C.Sporleder 在[1]中为这个字符分辨任务开发的策略。这分 4 步完成:

  • 人名解析:使用 NameParser 框架将人物姓名解析成通用结构。例如,哈利·波特先生将被解析为:
  • 性别分配。如果可能的话,确定角色的类型。在这里,我们使用数据库,简单地从标题和名字对其进行评估。例如,如果标题为“先生”,则体裁为“男性”。
  • 匹配算法:将每个出现映射到一个实体,将共同参照对象分组在一起。不需要深入研究细节,假设我们首先考虑显示标题、名和姓的所有事件——具有最完整形式的事件。如果现有的实体没有出现的名字,姓氏和最终的流派,我们创建一个新的。对于显示相同结构的所有提及,我们都这样处理,然后将重点放在只显示名字和姓氏的其余提及上。同样,我们重复相同的过程,如果名字和姓氏相同,将新的提及与现有的实体进行匹配。接下来是出现的标题和名字,标题和姓氏,最后是名字或姓氏。结果,由于实体哈利·波特在一开始就被构想出来(由哈利·波特先生),所以哈利或波特先生直接与这个实体相关联。
  • 细化匹配流程:考虑 NER 模型的昵称、首字母和小错误。事实上,在目前的框架中,哈利·波特和达迪是不同的实体,并没有像我们希望的那样映射到哈利·波特和达力·德思礼。我们通过仔细指定有针对性的规则来处理首字母和使用现有的昵称数据库来解决这个问题。

图形创建

主动态图

我们现在处理我们方法的一个关键点:我们如何创建一个图表来保存关于叙事动态的时间信息?我们选择使用上面存储的信息构建一个图表,并呈现以下结构。

我们的完整图形由 3 个节点类型组成:

  • 出现节点:元组(字符名称,出现位置)
  • 章节节点:章节的索引
  • 实体节点:表示实体的字符串

还有 4 种不同的边缘类型:

  • 归属边:如果事件位于章节节点 idx 中,则将事件节点连接到章节节点。
  • is-entity edge :如果出现的字符名称已经匹配到对应的实体,则将出现节点连接到实体节点。
  • interact-with edge :连接两个发生节点,如果它们一起交互并且不连接到同一个实体节点。我们在对应于不同实体的两个出现节点之间创建一个交互边,如果它们在小说中的位置相距不超过 20 个令牌。这正是使用同现滑动窗口的想法,只是在我们的情况下,计算起来更简单。请注意,两个角色之间的边缘并不一定意味着他们是朋友——它只是意味着他们相互影响,谈论彼此,或者一起被提及。
  • 时间边缘:在小说中,连接两个连接到同一实体的出现节点,如果不存在另一个出现节点连接到位于这两个出现节点之间的这个实体。

这个完整图的主要目的是我们在这个过程中不会丢失任何信息:事实上,我们只是将事件列表转换成一个更容易操作的图结构。

[数]子图

对于某些应用,采用这种全动态图的一些修改版本是有用的。

特别是实体图,对于书籍的一些全局分析非常有用。它是完整图的静态折叠版本,仅包含一种类型的节点:实体-节点和一种类型的边:交互对象,其中由两个实体之间的交互次数加权。按章剪切以嵌入时态信息通常很有趣。因此,我们将动态图折叠成实体图的序列,每章一个。

我们可以进一步扩展这个观点,生成一个细粒度的动态实体交互图,其中时间轴就是文本中迄今为止的标记数,也就是:“时间”在小说中从 0 到标记数。

图形分析

字符重要性

分析的第一步,也可能是最具启发性的一步,是找出书中最重要的人物。为了做到这一点,我们为每一个节点计算了书籍实体图(静态或动态)中的中心性度量,代表人物在叙事中的重要性。

有几种度量方法,如度中心性、中间中心性或网页排名中心性。它们能够捕捉不同的重要性变量。事实上,一个人可以以多种方式发挥核心作用。她可能有良好的关系,位于中心,或处于独特的位置来帮助传播信息或影响他人。有关这些指标的更多信息,请参考该资源。简而言之:

  • 程度中心性:你有很多关系吗?
  • 加权度中心性:你们有很多互动吗?
  • 特征向量中心性:你和重要的人有很多联系吗?
  • PageRank Centrality :你和重要的人有很多互动吗?
  • 中间中心性:你帮助连接网络的不同部分吗?

在这里,我们倾向于 Pagerank centrality,对于这个故事来说,它被 Google 用来对推荐给你的网页进行排序。我们得到了以下结果:

这本书的 10 个最有影响力的人物的 Pagerank 中心性的演变

结果描述:在计算完全静态实体图上最重要的 10 个字符后,我们遵循跨章节的中心性度量演化,意思是在动态实体图上。观察到的结果与我们对这本书的了解非常一致,我相信你会同意。显然,哈利一直是最核心的人物。它在网络中定位独特,似乎与每个人都有联系。尽管乍一看令人惊讶,伏地魔和奇洛教授被排除在前十名之外,但事实上仍然被认为是重要的(排名第 11 和第 13)。在第一本书中,哈利在霍格沃茨的日常生活占据了很大的篇幅,这是有道理的,因为在那里伏地魔并不经常被提及。更令人吃惊的是第五章中大多数角色的网页排名突然飙升,这与哈利到达霍格沃茨的时间相吻合。有些角色,比如罗恩,很快就变得非常重要,而另一些角色,比如赫敏,则逐渐变得重要起来。请注意,这段话也与达力·德思礼等一些有影响力的人物的消失相吻合。

在下一小节中,我们检查叙述中的这种结构变化是否被其他图形属性的演变所证实。这将使我们能够概括出一本书主要情节发展的能力。

结构变化

我们选择了几个属性,静态的和动态的,可以帮助我们更好地理解剧情的发展。我们解释其中的一些:

节点/边缘/现有人物的数量 给出了关于故事结构、书的类型和作者写作风格的宝贵线索。另一方面, 消失/被引入的人物数量 给出了更多关于剧情发展的动态洞察。当主角哈利到达一个新的地方时,很容易被注意到。观察下面的情节,互动和主角介绍的激增证实了前一部分的推断,因为它与哈利到达霍格沃茨相对应。将这张图表与其他书籍进行比较,可以让我们区分两种类型,两个作者…

在每一章中寻找最大集团的例子,可以看出哪些角色相互作用,以及全局情节是关于什么的。是关于杜德利一家、师生互动还是哈利-伏地魔-邓布利多?调查派系的余弦相似性也可能是有趣的,看看动作是否经常围绕相同的角色(如在惠普),或者是否经常改变(如在 LOTR 的 GOT)。此外,我们可以查看最强的边,或者将集团与图 k-core 进行比较,以查看集团在章节中占据了多少空间。最后,使用我们的动态图,我们甚至可以在更小的范围内,在章节内进行这种分析。

前两章的简化最大集团+主要优势

其他章节的最大派系+主要互动

看每章重要人物的比例 或许可以帮助我们找到故事情节中哪些章节是重要的。对于哈利波特来说,这并不是最重要的特征,但是对于像《LOTR》或《GOT》这样的书来说,它会非常有帮助,因为许多故事情节同时发生。

主要人物和次要人物重要性的区别 给出了所写的书的类型信息,是一个主要人物的书还是几个主要人物的书。

作为旁注,请记住,我们在这里使用的是手工制作的功能,而不是网络表征学习领域的最新方法。这些方法通常是图形神经网络家族的一部分,它们隐式地提取图形属性,因此具有自己学习特征的特殊性(使用图形结构和节点特征)。它们用于获得每个节点的向量表示,该向量表示稍后用于下游 ML 任务,例如节点分类或链路预测。日常生活中的例子多种多样:推荐电影,推荐朋友,预测分子的作用,使用存储为知识图的大数据库回答你的问题,预测病毒的传播…

社区检测

尽管 HP1 并不是这类应用最引人注目的例子,与 GOTLOTR 相反,我们网络的复杂结构仍然反映了故事情节的交织。值得注意的是,我们观察到许多现实世界网络中的两个特征。首先,这个网络包含多个更密集的子网,由一个更稀疏的全球边缘网络连接在一起。其次,它是围绕着一部分极具影响力的人组织起来的,无论是在本地还是全球。

为了使用网络科学的分析工具来量化这些观察,我们继续进行静态实体图上的社区检测(或者如果需要的话,更多动态版本)。更准确地说,我们将格文纽曼算法 应用于通过删除交互太少的节点(在我们的例子中少于 5 个)而获得的完整实体图的子图。除了使图形更具可读性之外,它还排除了 BERT 发现的一些虚假实体,只保留了真实的实体。

描述:在《哈利·波特》中,我们获得了四个不同的社区。一个涉及哈利的家庭,包括他的亲生父母和杜德利一家,以及他们的随行人员。第二个是由邓布利多的朋友组成的,这有点超出了本书的主要情节范围,在一章中提到了。第三部的成员与魁地奇有关:格里芬多的队伍、演讲者李·乔丹、斯莱特林和马库斯·弗林特…最后,最后一部包括了剧情的所有主要人物——当然有哈利/罗恩/赫敏,但也有伏地魔/奇洛/斯内普/邓布利多,还有哈利的老师和同学。

虽然很难评估这个社区有多好,而且其他几个不错的选项是一致的,但我相信这个算法做得相当好。

这本书的概要

我们现在想看看是否有可能得到一个图表来总结人物之间的主要互动,从而以这种方式提供一种对这本书的总结。

我们的直觉需要使用静态实体图的 k-core 作为这本书的摘要。为了评估这种方法的相关性,我们可以很容易地找到一个好的书籍摘要与之进行比较。其背后的想法是,我们希望完整图形的 k-core 分解类似于本书摘要的图形。

实际上,这两个图几乎具有相同的节点数、相同的连通性和相似的特征关系。涉及的实体仅略有不同,iou 分数为 0.5 (两组字符的交集/并集)。主要的区别在于包含了德思礼和韦斯莱。因此,总之,我们从整本书的 k-core 中获得的图表可以成功地用作这本书的交互总结。

与其他书籍的比较

与《指环王》的比较。我们对比一下哈利波特与魔法石魔戒:魔戒相交的文笔。更准确地说,我们对这两本书进行完全相同的分析,寻找它们之间的相似和不同之处。例如,尽管《LOTR》要长得多,但在两本书中存在的实体和交互的数量是相似的。然而,与哈利波特不同,LOTR 是围绕几个人物和几个情节展开的。

更进一步,我们可以计算全动态图的嵌入(就像网络表示学习领域中的图分类一样— 资源)并计算两个图的嵌入之间的距离。它可以让每个人看到哪些书非常相似。请注意,只有在处理了两本书以上的情况下(这里不是这种情况),它才是相关的,以便对度量的数量级有一个概念。

结论

在这篇文章中,我们构建并分析了一个小说的社会角色网络的动态版本。我们首先处理这本书的文本内容——使用伯特·NER 提取所有出现的字符,并特别强调共同引用以获得尽可能准确的图表。然后,我们创建了一个动态异构图框架,将时间维度嵌入到我们的网络中,并专注于五个不同的任务:角色重要性、结构变化、社区检测、图总结和书籍比较。指导他们给了我们关于这本书的情节、作者的风格、与其他书的写作模式的不同、人物(重要性、关系、角色、主要互动等)的宝贵信息。).

我们的网络分析证实了一些预期,并为这本想象丰富的书《哈利·波特与魔法石》提供了新的见解。请注意,我们已经考虑了网络科学的一个奇特应用,以展示其诱人的能力。其他相关任务包括小说聚类、预测流派、作者甚至新角色互动。更一般地说,更严肃的应用比比皆是,网络科学有望在理解我们现代网络生活中发挥不可估量的作用。

参考

基于结构的小说聚类 、Mariona Coll Ardanuy 和 Caroline Sporleder。2014.

*【2】*用图形漫画讲述动态网络的故事 。本杰明·巴赫等人 2016。

*【3】*权力网 。安德鲁·贝弗里奇和洁珊。2016.

*【4】*从文学小说中提取社交网络 。David Elson 等人,2010 年。

*【5】*虚构人物网络的提取与分析:一个综述。 文森特·拉巴图和泽维尔·博斯特。2019.

如何用网络讲故事:用《伊利亚特》探索图表的叙事启示。 托马索·文图林等 2017。

使用 Google BigQuery 和 DataStudio 探索公共数据集

原文:https://towardsdatascience.com/explore-public-dataset-with-google-bigquery-and-datastudio-30f9279b8d42?source=collection_archive---------25-----------------------

数据科学基本指南

在您的 Web 浏览器中探索和报告海量数据集—以新冠肺炎数据集为例

卢克·切瑟在 Unsplash 上的照片

M 任何时候,作为数据科学家,我们都会在数据存储、导入、管理和清理过程中浪费大量时间。在这篇短文中,我将向您介绍如何使用谷歌云服务(BigQuery + DataStudio 免费计划)来探索开源数据集,并以谷歌云公共数据集计划中的新冠肺炎数据集为例。

来自 Google Big Query 的公共数据集样本(作者截图来自 Google BigQuery )

好消息,免费了!

与谷歌云公共数据集计划中的所有数据一样,谷歌为该计划中数据集的存储付费。BigQuery 还提供对某些 COVID 相关数据集的免费查询,以支持对新冠肺炎的响应。对 COVID 数据集的查询将不计入 BigQuery 沙盒空闲层。与免费层一样,您每个月最多可以免费查询 1 TB,每个月最多可以查询 1TB,完全免费。[1]嗯,你可以探索的数据还真不少!

开始前你需要什么?

是的,就是它——你可以走了!

我们开始吧

步骤 1:探索 Google Cloud BigQuery 中的公共数据

首先,只需前往你的谷歌云控制台,然后进入谷歌大查询网络界面。然后,你会在 web 界面的左下窗口找到可用的大查询公共数据。您可以通过单击来检查每个数据集的元数据。

例如,下图显示了来自 JHU CSSE 的新冠肺炎数据集,该数据集由谷歌托管并每天更新。

BigQuery Web 界面(作者)

第二步:查询数据!

在选择了您想要的数据存储桶之后,您可以像在数据库系统中一样,使用标准的 SQL 语言简单地查询您需要的数据。

例如,让我们研究来自 CSSE JHU 的新冠肺炎数据集,其中的数据日期为“2020–10–12 ”,您可以使用以下 SQL 脚本来完成:

**SELECT
  *
FROM
  `bigquery-public-data.covid19_jhu_csse.summary` 
WHERE
  date = '2020-10-12'**

然后,单击 Run 按钮开始查询。

例如,使用 BigQuery 查询新冠肺炎(JHU-CSSE)数据集。(作者)

你可以做更复杂的查询;例如,确认、死亡和康复的新冠肺炎病例的汇总摘要;按国家级别分组;按确诊病例数排序;可以使用以下 SQL 脚本进行查询:

**SELECT
  country_region, SUM(confirmed) as confirmed_sum, SUM(deaths) as deaths_sum, SUM(recovered) as recovered_sum
FROM
  `bigquery-public-data.covid19_jhu_csse.summary` 
WHERE
  date = '2020-10-12'
GROUP BY country_region
ORDER BY confirmed_sum desc;**

例如,使用 BigQuery 查询聚合的新冠肺炎(JHU-CSSE)数据集。(作者)

步骤 3:使用 DataStudio 浏览数据

当您对查询结果满意后,您可以使用 Google 的 DataStudio 服务进行可视化和探索。您可以在 BigQuery web UI 的查询结果上找到这个选项。

使用 Data Studio 探索数据。(作者)

在 DataStudio 中,有一系列数据可视化工具,如图表、地图等;您可以用它来研究 BigQuery 中的查询数据。

Data Studio 可视化工具。(作者)

将数据微件添加到 Data Studio 后,它将自动显示查询的数据集中的数据。这些工具非常好用,可以灵活地调整颜色或主题(亮或暗)。

data studio 中新冠肺炎数据的图表和地图可视化示例(浅色主题)。(作者)

data studio 中新冠肺炎数据的图表和地图可视化示例(深色主题)。(作者)

此外,您可以很容易地邀请任何人在这个数据报告上进行协作。或者,获取一份 PDF 报告。或者甚至将嵌入式报告放到您网站上。

共享数据工作室报告。(作者)

结论:

本文以查询来自 JHU CSSE 的新冠肺炎数据集为例,展示了如何使用 Google Cloud BigQuery 来探索公共数据集。之后,它还展示了如何使用 Data Studio 以一种简单的方式从查询结果创建报告仪表板。我希望你喜欢这篇文章,并发现它对你的日常工作或项目有用。如果您有任何问题或意见,请随时给我留言。

关于我&查看我所有的博客内容:链接

安全健康健康!💪

感谢您的阅读。📚

参考

[1] Chad W. Jennings,新冠肺炎公共数据集计划:让数据可自由访问以获得更好的公共结果(2020 年 10 月 13 日),谷歌云数据分析

高级 DAX 教程:篮子分析 2.0

原文:https://towardsdatascience.com/explore-the-potential-of-products-through-customers-purchase-behaviour-in-power-bi-basket-a1f77e8a2bf6?source=collection_archive---------21-----------------------

通过 Power BI 中的客户购买行为发掘产品的潜力

本文旨在使用 DAX 分析 Power BI 中的客户购买行为,并洞察产品潜力。

马尔科·鲁索 阿尔贝托·法拉利 几年前曾发表过一篇名为《 购物篮分析 》的博客,这篇有趣的文章详细描述了如何使用 DAX 来计算任何产品组合下的订单数和客户数等非常有用的指标。这篇文章可以看作是“购物篮分析”的扩展,它考虑了客户购买不同产品的时间顺序。

与“篮子分析”相比

假设 A 和 B 代表两种不同的产品,那么“篮子分析”计算的是 P(AB),而本文计算的是 P(A|B)和 P(B|A),因为你可以比较下图所示的两个数字:

作者图片

上图是“购物篮分析”中“两种产品都有客户”的衡量标准,显示有 72 个客户同时有“瓶瓶罐罐”和“自行车架”的购买记录。但是,下图中显示的数据考虑了客户购买产品的时间顺序。你可以发现,先买自行车架,后买瓶子和笼子的顾客有 8 个,先买瓶子和笼子,后买自行车架的顾客有 14 个。(注:我们暂时忽略同时买 A 和 B 的情况)

作者图片

为什么这个分析有意义?

客户的订单记录反映了一些非常有用的事实,为产品之间的关联提供了方向。换句话说,“购物篮分析”在分析超市数据时非常有用,因为顾客往往在购物时选择多种产品,然后去收银台一起下单。在这种情况下,所有产品都被视为同时订单。但实际上,你无法追溯顾客在超市购物过程中选择不同商品的记录。但是如果是在其他场景,比如客户在电商平台或者官网上下单,如果你作为店长,你可能想知道 A 和 B 是最畅销的型号,哪一个能带来更多的回头客,哪一个更容易流失客户。所以我们需要知道每个产品的回购百分比。比如所有先购买产品 A 的客户,未来有多少人会再回来购买产品,进一步分析,在这些人中,购买的仍然是产品 A 还是其他产品?各占多大比例,这是一个值得研究的问题。

计算过程。

按照计算过程,我们将最终实现下图所示的计算结果(注:我使用的数据集与“篮子分析”相同):

作者图片

如前所述,它显示了哪些客户首先购买了产品 A 并有后续购买记录,其中有多少客户购买了产品 B 或产品 C 等。

因此,为了达到这个计算结果,这里有五个步骤:

1.首先,对销售表的所有订单进行分类,在客户的所有订单中,订单日期最早的一个或多个订单被分类为第一个订单,其余为“非第一个”:

IsFirstOrder = 
VAR
E_Date = 'Sales'[OrderDateKey]
VAR
CUST = 'Sales'[CustomerKey]
RETURN
IF(
    SUMX(
        FILTER('Sales',
        CUST = 'Sales'[CustomerKey]&&
        E_Date > 'Sales'[OrderDateKey]),
        COUNTROWS('Sales'))>0,FALSE,TRUE)

2.过滤销售中所有产品 A 的订单数据,然后进一步过滤哪些订单被标记为客户的第一个订单,我们在这个过滤后的表中提取客户列表,并在其中添加一个名为“ROWS”的虚拟列,如下面的代码所示—虚拟表“VT1”。

3.使用 Sales 作为主表,并使用 NATURALLEFTOUTERJOIN()与虚拟表“VT1”相关联,然后使用 filter()排除那些[ROWS]值不等于 1 的行,以便剩余的数据(VT2)是“VT1”返回的所有客户的所有订单。最后,对除“一阶”以外的所有订单进一步筛选数据,结果命名为“CustDistinctValue”:

CustDistinctValue = 
VAR
FIRSTORDERPROD = 
IF(HASONEVALUE('Product'[Subcategory]),
    VALUES('Product'[Subcategory]),0)
VAR
VT1 = 
SUMMARIZE(
    FILTER(Sales,
        AND(related('Product'[Subcategory]) = FIRSTORDERPROD,
            'Sales'[IsFirstOrder]=TRUE)),
        'Sales'[CustomerKey],
        "ROWS",
        DISTINCTCOUNT(Sales[CustomerKey]))
VAR
VT2 = 
FILTER(
    NATURALLEFTOUTERJOIN(ALL(Sales),VT1),
    [ROWS] = 1)
RETURN
CALCULATE(
    DISTINCTCOUNT('Sales'[CustomerKey]),
    FILTER(VT2,'Sales'[IsFirstOrder] = FALSE)
)

4.之后,我们需要确保这些数据可以被产品过滤(在这种情况下,我们只使用子类别)。这里和 Macro 的计算方法基本相同,使用产品表的副本(过滤产品)和主表建立非活动关系,然后创建一个度量,使其上下文忽略产品表的所有字段,接受来自其副本(过滤产品)的上下文。

CustPurchaseOthersSubcategoryAfter = 
VAR CustPurchaseOthersSubcategoryAfter = 
CALCULATE (
    'Sales'[CustDistinctValue],
    CALCULATETABLE (
        SUMMARIZE ( Sales, Sales[CustomerKey] ),
        'Sales'[IsFirstOrder] = FALSE,
        ALLSELECTED ('Product'),
        USERELATIONSHIP ( Sales[ProductCode],
             'Filter Product'[Filter ProductCode] )
    )
)
RETURN
IF(NOT([SameSubCategorySelection]),
    CustPurchaseOthersSubcategoryAfter)

注:“SameSubCategorySelection”用于排除选择相同子类别的数据。这个公式也使用宏的方法来完成:

SameSubCategorySelection = 
IF (
    HASONEVALUE ( 'Product'[Subcategory] )
        && HASONEVALUE ( 'Filter Product'[Filter Subcategory] ),
    IF (
        VALUES ( 'Product'[Subcategory])
            = VALUES ( 'Filter Product'[Filter Subcategory] ),
        TRUE
    )
)

5.现在,我们已经计算出购买产品 A 的客户中有多少人首先购买了其他产品,现在我们需要计算这些客户占首先购买产品 A 然后有购买记录的客户总数的比例。下面是计算这个比例的分母的代码。

AsFirstOrderCust = 
VAR
FIRSTORDERPROD = 
IF(
    HASONEVALUE('Product'[Subcategory]),
    VALUES('Product'[Subcategory]),0)
VAR
VT1 = 
SUMMARIZE(
    FILTER(Sales,
        AND(
            RELATED('Product'[Subcategory]) = FIRSTORDERPROD,
                'Sales'[IsFirstOrder]=TRUE)),
            'Sales'[CustomerKey]
)
return
CALCULATE(
    DISTINCTCOUNT('Sales'[CustomerKey]),
    VT1)-------------------------------------------------------------------------------
IsLastOrder = 
VAR
E_Date = 'Sales'[OrderDateKey]
VAR
CUST = 'Sales'[CustomerKey]
RETURN
IF(
    SUMX(
        FILTER('Sales',
        CUST = 'Sales'[CustomerKey]&&
        E_Date < 'Sales'[OrderDateKey]),
        COUNTROWS('Sales'))>0,"F","T")-------------------------------------------------------------------------------
AsFirstOrderCustRepurchase = 
CALCULATE(
    'Sales'[AsFirstOrderCust],
    'Sales'[IsLastOrder] = "F")

现在我们得到最终的结果:custpruchaseotherssubcategoryaafter %,这个度量的名字很长,因为它的逻辑很复杂,就像上面的计算过程一样。

CustPurchaseOthersSubCategoryAfter *% =* 
DIVIDE ( 'Sales'[CustPurchaseOthersSubcategoryAfter],
    'Sales'[AsFirstOrderCustRepurchase])

最后的结果。

最后,我们将成功地得到如下的最终结果,并选择使用一个名为“CHORD”的自定义视觉效果来可视化它。

作者图片

作者图片

正如你所看到的,首先购买公路车的顾客中,1853 人后来购买了山地车,而有趣的是,只有 200 名顾客在购买山地车后购买了公路车。

非常感谢 格哈德 之前的指点,这次我在文章中附上了 PBIX 文件,有兴趣的可以在这里下载。

结束~

用 R 探索你在 Google 上的活动:如何分析和可视化你的搜索历史

原文:https://towardsdatascience.com/explore-your-activity-on-google-with-r-how-to-analyze-and-visualize-your-search-history-1fb74e5fb2b6?source=collection_archive---------21-----------------------

使用一份你的个人资料,找出你是如何使用这个世界上最流行的搜索引擎的,以及使用了多少。

仪表板,以查看您的谷歌搜索活动历史。文末的链接。

你能想象一个没有世界上最流行的搜索引擎的世界吗?亲爱的读者,我敢打赌,如果你年龄在 18 至 25 岁之间,接触字典或百科全书对你来说不是日常生活的一部分,就像我们许多人在年轻时那样。

如今,任何人都可以点击鼠标找到答案、知识、信息(和错误信息)。这就是为什么我觉得能够看看我们的个人消费习惯是如何变化的很有趣,特别是如果你有很长的历史,因为你打开了谷歌的大门,找到了关于你的一切。

我在哪里可以获得我的数据的副本?

多亏了谷歌外卖,谷歌提供了一个工具来查阅你使用过的任何产品中存储的数据,你可以查阅你的搜索历史。你必须输入 https://takeout.google.com/settings/takeout的网址才能登录你的个人账户。

截图:谷歌外卖网站

您可以要求您的数据或副产品的完整副本,或者只选择单个产品的某些特征。对于我们来说,为了本文的目的,选择“我的活动”就足够了。

截图:我在谷歌外卖的活动选择

我还建议您确保取消选中它包含的其余选项,只保留“Search”选项,以减少它将生成的文件的重量和交付时间。

截图:搜索选择,在我在谷歌外卖的活动中

完成后,您必须选择“导出一次”并选择。zip 交付格式。

截图:生成。压缩谷歌外卖

瞧,现在你只需要耐心等待你选择的内容的副本生成。

截图:确认谷歌外卖生成的文件

文件生成后,您将会收到一封通知电子邮件,邀请您下载该文件,出于安全原因,该文件的截止日期会在指定日期之前。

截图:谷歌外卖的数据下载通知邮件

当你打开。在您下载的 zip 文件中,您会发现一个名为“my activity . html”的文件。

截图:解压后的文件夹结构。活力

这个 HTML 文件包含您在 Google 中搜索历史的完整数据,从您第一次使用搜索引擎开始,直到您使用 Google 外卖生成这个数据副本。

截图:谷歌搜索历史 HTML 文件

读取导出的数据

现在,您可以继续处理在新的 R 脚本中获得的信息。首先,您必须包含您将使用的所有软件包,并建立对您的搜索历史记录的读取,这些记录包含在“我的活动”文件夹中的“搜索”文件夹内的文件“My Activity . html”中。

# REQUIRED LIBRARIES
library(wordcloud)
library(lubridate)
library(rvest)
library(tm)
library(tidyverse)# READ DATA
fileHTML <- "Takeout/My Activity/Search/MyActivity.html"
mySearchFile <- read_html(fileHTML, encoding = "UTF-8")

您将提取您感兴趣的数据,并使用“rvest”包对 HTML 进行 web 清理。这样,使用正则表达式,您可以提取搜索的日期和时间、搜索的文本以及您在 Google 上进行的搜索类型,从而创建包含所有这些信息的数据框。

# SCRAPPING SEARCH DATE AND TIME
dateSearch <- mySearchFile %>% 
  html_nodes(xpath = '//div[[@class](http://twitter.com/class)="mdl-grid"]/div/div') %>% 
  str_extract(pattern = "(?<=<br>)(.*)(?<=PM|AM)") %>%
  mdy_hms()
dateSearch[1:5]# SCRAPING SEARCH TEXT
textSearch <- mySearchFile %>% 
  html_nodes(xpath = '//div[[@class](http://twitter.com/class)="mdl-grid"]/div/div') %>%
  str_extract(pattern = '(?<=<a)(.*)(?=</a>)') %>% 
  str_extract(pattern = '(?<=\">)(.*)')
textSearch[1:5]# SCRAPING SEARCH TYPE
searchType <- mySearchFile %>% 
  html_nodes(xpath = '//div[[@class](http://twitter.com/class)="mdl-grid"]/div/div') %>% 
  str_extract(pattern = "(?<=mdl-typography--body-1\">)(.*)(?=<a)") %>% 
  str_extract(pattern = "(\\w+)(?=\\s)")
searchType[1:5]# CREATE DATA FRAME USING SCRAPED DATA
searchedData <- tibble(timestamp = dateSearch,
                      date = as_date(dateSearch),
                      year = year(dateSearch),
                      month = month(dateSearch, label = TRUE),
                      day = weekdays(dateSearch),
                      hour = hour(dateSearch),
                      type = searchType,
                      search = textSearch)searchedData$day <- factor(searchedData$day, levels = c("Sunday", "Monday", "Tuesday", "Wednesday","Thursday", "Friday", "Saturday"))searchedData <- na.omit(searchedData)
head(searchedData)

因此,您将获得一个包含 8 个变量的新数据框。

屏幕截图:从 HTML 创建的数据框的控制台预览

随着时间的推移,你在谷歌上搜索的频率发生了多大的变化?

这可能是你可以回答的第一个问题。根据获得的信息,您可以查看与您的历史年份相关的搜索次数。

# PLOT SEARCHED BY YEAR
searchByYear <- ggplot(searchedData, aes(year, fill=..count..)) +
  scale_fill_gradient(low = "yellow", high = "red")+
  geom_bar(width=0.7)+
  labs(x= "Year", y= "Count") + 
  ggtitle("How much your search frequency has changed over time", "Search activity by year")
searchByYear
ggplotly()

以我为例,我的历史从 2007 年延续到 2020 年的今天。也就是说,13 年的搜索量表示如下。与 2014 年之前的几年相比,有非常显著的增长,在 2014 年之前,我主要继续使用雅虎搜索引擎,而且在我看来,必须考虑到越来越快的互联网连接也促进了向谷歌进行越来越多查询的可能性。

如何分析和可视化您的个人数据搜索历史——每年搜索次数的图表

随着时间的推移,你在谷歌上搜索的频率发生了多大的变化?(按月详细查看)

你可以对之前获得的结果进行更深入的挖掘,现在可视化你在谷歌上搜索的频率发生了多大的变化,按你的历史记录中的月份进行详细描述。

# PLOT SEARCH BY MONTH
searchByMonth <- searchedData[(searchedData$year > 2007 & searchedData$year< 2021), ]
ggplot(searchByMonth, aes(year, fill=..count..)) + 
  scale_fill_gradient(low = "yellow", high = "red")+
  geom_bar(aes(x = month, group = year)) +
  theme(axis.text.x = element_text(angle=90)) +
  facet_grid(.~year, scales="free") + 
  labs(x= "Year / Month", y= "Count") + 
  ggtitle("How much your search frequency has changed over time", "Month activity on detail")

亲爱的读者,我几乎可以肯定,像我一样,如果你仔细观察,很多事情都与搜索次数的增加有关,这些搜索是关于贵国疫情新冠肺炎迫使你更多地呆在家里的最糟糕的几个月,因此也与你的手机、平板电脑和可以上网的电脑等设备有关。例如,就我而言,我认为 2020 年的最高纪录与我居住的国家(墨西哥)的红灯(关闭所有非必要的设施和在家远程工作)重合并非巧合。

如何分析和可视化您的个人数据搜索历史——每年/每月搜索次数的图表

你一天中什么时候最常谷歌?

您也可以获得这些信息,因为您有使用流行的 Google 引擎进行搜索的准确时间的记录。

# PLOT SEARCH BY HOUR
searchByHour <- ggplot(searchedData, aes(hour, fill=..count..)) +
  scale_fill_gradient(low = "yellow", high = "red") +
  geom_bar() + 
  labs(x= "Hour", y= "Count") + 
  ggtitle("What time of day do you have the highest frequency of searches?", "Hour activity on detail")
searchByHour

结果你会得到下面的图,例如,在我的例子中,值得注意的是,我在 dawn 基本上不是谷歌用户。

如何分析和可视化你的个人数据搜索历史——你最常搜索的时间图

一周中的哪一天你最常谷歌?

按照前面的示例,您还可以直观地看到一周中哪一天您执行的搜索次数最多。

# PLOT SEARCH BY WEEKDAY
seearchByWeekD <- ggplot(searchedData, aes(day, fill=..count..)) + 
  scale_fill_gradient(low = "yellow", high = "red") +
  geom_bar() +
  labs(x= "Day", y= "Count") + 
  ggtitle("What day of the week do you have the highest frequency of searches?", "Weekday activity on detail")
seearchByWeekD

可以预料,如果你像我一样,利用周末稍微远离上网,比如去看朋友,那么搜索量的下降将会非常明显。

如何分析和可视化你的个人数据搜索历史——一周中你在谷歌上搜索最多的日子

一周中的某一天和你经常搜索的时间之间关系的数据可视化

在最后两个图中,您可以看到一周中您在 Google 中注册最多搜索的时间和日期,您可以创建一个新的图来详细查看这两者之间的关系。

# PLOT SEARCH BY WEEKDAY AND TIME 
searchWdayTime <- ggplot(searchedData) + 
  scale_fill_gradient(low = "yellow", high = "red")+
  geom_bar(aes(x = hour, group = day, fill=..count..) ) +
  labs(x= "Hour / Day", y= "Count") + 
  ggtitle("Relationship between day / time you have a higher frequency of searches", "Weekday/Time activity on detail") +
  facet_grid(.~day, scales = "free")
searchWdayTime

这样,你就会得到如下这样的情节。您还可以将数据与您已经使用的其他变量(如月和年)进行交叉。

如何分析和可视化您的个人数据搜索历史——一周中的日期和时间之间的关系图,您通常在其中进行搜索

在过去的两年里,谷歌搜索次数最多的术语是什么?

这是另一个有趣的问题,你可以通过使用“word cloud”包查看数据来回答。首先,您必须提取术语,以便以后清理它们,从而能够创建一个文本语料库。你也应该删除不提供相关信息的词,如冠词和代词。

# CLEAN AND EXTRACT TEXT TO CREATE A TEXT CORPUS
lastTwoYears <- searchedData[(searchedData$year > 2007 & searchedData$year< 2010), ]search <- tolower(lastTwoYears$search)
search <- gsub('(http|https)\\S+\\s*|(#|@)\\S+\\s*|\\n|\\"', " ", search)
search <- gsub("(.*.)\\.com(.*.)\\S+\\s|[^[:alnum:]]", " ", search)
search <- trimws(search)textCorpus <-  Corpus(VectorSource(search))
textCorpus <- tm_map(textCorpus, content_transformer(removePunctuation))
textCorpus <- tm_map(textCorpus, content_transformer(removeNumbers))
stopwords <- c(stopwords("english"), "que", "com", "cómo", "como", "para", "con", "qué", "las", "los", "del", "can")
textCorpus <- tm_map(textCorpus, removeWords, stopwords)searchTDM <- TermDocumentMatrix(textCorpus)
searchMatrix <- as.matrix(searchTDM)

最后,您可以创建一个新的数据框,以便能够可视化最近两年在 Google 中搜索次数最多的术语。

# CREATE DATA FRAME WITH WORDS
arrange <- sort(rowSums(searchMatrix), decreasing = TRUE)
twNames <- names(arrange)
dataCloud <- data.frame(word = twNames, freq = arrange)wordcloud(dataCloud$word, dataCloud$freq, min.freq = 40, scale = c(2 , 0.5), max.words = 100, colors=brewer.pal(9, "Paired"))

然后你应该得到你搜索了至少 40 次或更多的词。这是我从 2018 年到 2020 年搜索次数最多的词条云。不要评价我搜索了这么多“洪流”请呵呵。

如何分析和可视化你的个人数据搜索历史——过去两年谷歌搜索次数最多的词

作为奖励,你可以重复这个练习,找出在你允许谷歌进入你的生活以了解你的一切的头两年里,搜索次数最多的术语是什么。你只需要修改 lastTwoYears 变量中包含的年份范围。以我为例,从 2008 年到 2010 年(这几年有很多 Flash 和论坛,在那里可以找到关于 web 开发的答案),这是我的 wordcloud。显然,与之前生成的 wordcloud 相比,搜索量要低得多。

如何分析和可视化你的个人数据搜索历史——谷歌搜索历史前两年的最热门词汇

非常感谢您的善意阅读。和我的大多数文章一样,我在一个 flexdashboard 中分享了用 plotly 生成的情节,我把它们放在一起更美观一些:https://rpubs . com/cosmoduende/Google-search-history-analysis

在这里可以找到完整的代码:https://github . com/cosmoduende/r-Google-search-history-analysis

感谢你坚持到最后,祝你分析非常愉快,可以把一切都付诸实践,对结果感到惊讶,和我一样开心!

用 R 探索你在网飞的活动:如何分析和可视化你的观看历史

原文:https://towardsdatascience.com/explore-your-activity-on-netflix-with-r-how-to-analyze-and-visualize-your-viewing-history-e85792410706?source=collection_archive---------26-----------------------

你花多少时间看电视连续剧,以及更多

用于查看文章末尾网飞链接上的活动数据的仪表板

视频流媒体服务的到来彻底改变了娱乐业,我们的许多习惯也随之改变。无论你花几个小时还是几分钟看电影或电视剧,网飞已经成为现场的最爱。

像今天的许多应用程序一样,网飞允许用户自由查看他们的观看活动历史。对于本文,我们将使用我的历史来分析和可视化一些有趣的数据。

如何获取数据历史和准备

作为一名活跃的网飞用户,您必须输入网址【https://www.netflix.com/viewingactivity】,在这里您将找到您最近在平台上看过的图书列表。

您在网飞的电视剧和电影观看活动

在页面的末尾,有一个链接写着“下载全部”,点击它将邀请你下载一个包含你的完整历史的 CSV 文件。默认情况下,下载的这个文件的名称是“netflixviewingshistory . CSV”,这是我们在 r 上工作所需要的。

下载您在网飞观看活动的 CSV 文件

好了,创建一个新的 R 脚本,您可以导入我们将要使用的包和下载的 CSV 数据来开始分析它们,用一个更可读的日期格式。

# LIBRARIES
library(dplyr)
library(tidyr)
library(lubridate)
library(zoo)
library(ggplot2)# READING DATA FROM CSV DOWNLOADED FROM NETFLIX ACCOUNT
minetflix <- read.csv("NetflixViewingHistory.csv") 
str(minetflix)
minetflix$Date <- dmy(minetflix$Date)

如你所见,CSV 文件只有两列:日期(确切日期)和标题(你看过的电影或电视剧的插曲和季节的标题)。

使用 R-导入的 CSV 文件进行网飞分析

狂看网飞,多少钱?

我们中的许多人沉迷于看一部或多部电视剧,突然你花了几个小时处于一种近乎催眠的状态,在这种状态下,其他的事情根本就不重要。没有定义“狂看”是多少小时或多少集,但我们都熟悉这个概念。做第一个分析,考虑到“狂看”是从 6 集到更多,我们来看看看的最多的电视剧有哪些。

让我们将标题列从数据中分离出来,利用网飞按电视剧标题、电视剧季节和电视剧集命名的格式中的常量。我们也来清理一下数据,把没有这个格式的东西(也就是电影)都去掉,专注电视剧。我们还需要确定每天观看的集的记录,属于单个电视连续剧。

# SEPARATE TITLE COLUMN IN TITLE OF TV SERIES, SEASON AND EPISODE TITLE
minetflix_serie <- minetflix %>%
  separate(col = Title, into = c("title", "temporada", "titulo_episodio"), sep = ': ')# REMOVE OCCURRENCES WHERE SEASON AND EPISODE ARE EMPTY (BECAUSE THEY ARE NOT TV SERIES)
minetflix_serie <- minetflix_serie[!is.na(minetflix_serie$temporada),]
minetflix_serie <- minetflix_serie[!is.na(minetflix_serie$titulo_episodio),]# REGISTRO DE NÚMERO DE EPISODIOS VISTOS POR DÍA, POR SERIE
maratones_minetflix <- minetflix_serie %>%
  count(title, Date)# LET'S CONSIDER "BINGE-WATCHING" 6 OR MORE EPISODES PER DAY AND SORT BY DATE
maratones_minetflix <- maratones_minetflix[maratones_minetflix$n >= 6,]
maratones_minetflix
maratones_minetflix <- maratones_minetflix[order(maratones_minetflix$Date),]
maratones_minetflix

在那些条件下,在我的例子中,我得到了 35 个观察值和 3 个变量。

网飞分析 R——每天“狂看”最多的电视连续剧

想象网飞十大狂欢

现在,我们已经准备好观看收视率最高的 10 部电视连续剧,你已经为它们奉献了“疯狂观看”。我们将根据电视剧的标题对数据进行分组,并根据观看的剧集数量对数据进行排序。

# GROUPING DATA BY TV SERIES TITLE AND SORTING BY NUMBER OF EPISODES VIEWED
maratones_minetflix_todas <- maratones_minetflix %>% 
  group_by(title) %>% 
  summarise(n = sum(n)) %>%
  arrange(desc(n))# PLOTTING TOP 10 OF BINGE-WATCHING TV SERIES
maratones_minetflix_top <- maratones_minetflix_todas %>% 
  top_n(10) %>%
  ggplot(aes(x = reorder(title, n), y = n)) +
  geom_col(fill = "#0097d6") +
  coord_flip() +
  ggtitle("Top 10 de series más vistas en maratón en mi Netflix", "4 o más episodios por día") +
  labs(x = "Serie en Netflix", y = "Episodios vistos en total") +
  theme_minimal()
maratones_minetflix_top

然后我们将得到下面的图。请不要因为我花了那么多时间看《贝蒂,拉费》(这是一部受欢迎的哥伦比亚“肥皂剧”)而对我评头论足。

网飞分析与 R-10 大电视连续剧

你每天在电视剧上消耗多少网飞?

看看随着时间的推移,你的消费习惯发生了多大的变化,这将是一件有趣的事情。例如,最大峰值是否与新冠肺炎的锁定相匹配?或者也许它与你再次单身以来的所有时间相吻合?

您可以在您的活动历史中对每天的发作次数进行统计。

# EPISODES PER DAY
netflix_episodios_dia <- minetflix %>%
  count(Date) %>%
  arrange(desc(n))# PLOTTING EPISODES PER DAY
netflix_episodios_dia_plot <- ggplot(aes(x = Date, y = n, color = n), data = netflix_episodios_dia) +
  geom_col(color = c("#f16727")) +
  theme_minimal() +
  ggtitle("Episodios vistos en mi Netflix por día", "Historial de 2016 a 2020") +
  labs(x = "Fecha", y = "Episodios vistos") 
netflix_episodios_dia_plot

就我而言,在下面的图中,我的网飞账户中的消费一直在增加,这非常值得注意。

每天观看 R 集的网飞分析

每天,每月,每年观看电视剧集完整时间表

你可以更深入。让我们以另一种方式来看这个活动,用一个按天、周、月、年分布的热图,我们可以更好地指定每天消费或多或少电视剧集的时间。

# CALENDAR WITH NUMBER OF EPISODES SEEN PER DAY IN HEATMAP
netflix_episodios_dia <- netflix_episodios_dia[order(netflix_episodios_dia$Date),]
netflix_episodios_dia$diasemana <- wday(netflix_episodios_dia$Date)
netflix_episodios_dia$diasemanaF <- weekdays(netflix_episodios_dia$Date, abbreviate = T)
netflix_episodios_dia$mesF <- months(netflix_episodios_dia$Date, abbreviate = T)# YOU DON'T NEED TO RENAME NECESSARILY IN SPANISH DAYS OF THE WEEK AND MONTHS
netflix_episodios_dia$diasemanaF <-factor(netflix_episodios_dia$diasemana, levels = rev(1:7), labels = rev(c("Lun","Mar","Mier","Jue","Vier","Sáb","Dom")),ordered = TRUE)netflix_episodios_dia$mesF <- factor(month(netflix_episodios_dia$Date),levels = as.character(1:12), labels = c("Enero","Febrero","Marzo","Abril","Mayo","Junio","Julio","Agosto","Septiembre","Octubre","Noviembre","Diciembre"),ordered = TRUE)netflix_episodios_dia$añomes <- factor(as.yearmon(netflix_episodios_dia$Date)) 
netflix_episodios_dia$semana <- as.numeric(format(netflix_episodios_dia$Date,"%W"))
netflix_episodios_dia$semanames <- ceiling(day(netflix_episodios_dia$Date) / 7)netflix_episodios_dia_calendario <- ggplot(netflix_episodios_dia, aes(semanames, diasemanaF, fill = netflix_episodios_dia$n)) + 
  geom_tile(colour = "white") + 
  facet_grid(year(netflix_episodios_dia$Date) ~ mesF) + 
  scale_fill_gradient(low = "#FFD000", high = "#FF1919") + 
  ggtitle("Episodios vistos por día en mi Netflix", "Heatmap por día de la semana, mes y año") +
  labs(x = "Número de semana", y = "Día de la semana") +
  labs(fill = "No.Episodios")
netflix_episodios_dia_calendario

2017 年 6 月和 7 月发生了什么,在平台上的利益损失在情节上是可见的?和亚马逊 Prime Video 给我的试用月说服我有关吗?也许吧。

网飞分析-按日、月和年观看的 R 热图剧集

一周中的哪几天有更多关于网飞的电视连续剧?

我们还可以非常直观和详细地看到一周中的哪几天有或多或少的观看电视剧集的活动。

# FREQUENCY OF ACTIVITY IN MY NETFLIX ACCOUNT PER DAY
vista_dia <- netflix_episodios_dia %>%
  count(diasemanaF)
vista_diavista_dia_plot <- vista_dia %>% 
  ggplot(aes(diasemanaF, n)) +
  geom_col(fill = "#5b59d6") +
  coord_polar()  +
  theme_minimal() +
  theme(axis.title.x = element_blank(),
        axis.title.y = element_blank(),
        axis.text.y = element_blank(),
        axis.text.x = element_text(face = "bold"),
        plot.title = element_text(size = 16, face = "bold")) +
  ggtitle("Frecuencia de episodios vistos", "Actividad por día de la semana en mi Netflix")
vista_dia_plot

如图所示,有一个明显的趋势,那就是周一是花时间在网飞上的繁忙日子。这也会是你的案子吗?

带 R 的网飞分析—按一周中的某一天进行活动

网飞哪几个月的电视连续剧最活跃?

像前面的例子一样,我们可以重复操作,但现在基于月份,这是我们选择给网飞宝贵时间的最喜欢的月份。

# FREQUENCY OF ACTIVITY IN MY NETFLIX ACCOUNT PER MONTH
vista_mes <- netflix_episodios_dia %>%
  count(mesF)
vista_mesvista_mes_plot <- vista_mes %>% 
  ggplot(aes(mesF, n)) +
  geom_col(fill = "#808000") +
  coord_polar()  +
  theme_minimal() +
  theme(axis.title.x = element_blank(),
        axis.title.y = element_blank(),
        axis.text.y = element_blank(),
        axis.text.x = element_text(face = "bold"),
        plot.title = element_text(size = 18, face = "bold")) +
  ggtitle("Frecuencia de episodios vistos", "Actividad por mes en mi Netflix") 
vista_mes_plot

你会得到如下图。在我和网飞的关系中,六月、七月和九月并不是最好的几个月。

网飞分析与 R-每月活动

最后看一下你在网飞看电视剧的活动,按月份和年份

最后,让我们以与前两个示例相同的格式再次回顾一下您的活动,但现在是按每年的月份来划分的。

# FREQUENCY OF ACTIVITY IN MY NETFLIX ACCOUNT PER YEAR
vista_años <- netflix_episodios_dia %>%
  count(añomes)
vista_añosvista_años_plot <- vista_años %>% 
  ggplot(aes(añomes, n)) +
  geom_col(fill = "#1a954d") +
  coord_polar()  +
  theme_minimal() +
  theme(axis.title.x = element_blank(),
        axis.title.y = element_blank(),
        axis.text.y = element_blank(),
        axis.text.x = element_text(face = "bold"),
        plot.title = element_text(size = 18, face = "bold")) +
  ggtitle("Frecuencia de episodios vistos", "Actividad por mes del año en mi Netflix")
vista_años_plot

你可以在获得的图中看到,在我的情况下,2019 年 10 月和 11 月是网飞收集我的偏好和习惯数据的好时机。

网飞分析与 R-按一年中的月份进行的活动

不得不说,我和女朋友共用账号。有时候平台做的推荐,并不完全是最适合我或者她,lol。

感谢您的友好阅读。你可以在我整理的 flexdashboard 中看到为这篇文章生成的图,用 plotly 稍微有点花哨:https://rpubs.com/cosmoduende/netflix-data-analysis-r

我也分享完整的代码,以防你对分析你在 https://github.com/cosmoduende/r-netflix-data-analysis 网飞的观看活动感到好奇

有一个快乐的分析,你可以练习它,甚至提高它,玩,用有趣的结果给自己惊喜。

这篇文章是根据我之前发表的西班牙语文章翻译成英语的,是应那些不会说西班牙语的人的要求,他们在小组和论坛上问我是否愿意用英语发表这篇文章。

谢谢你,再见。

用 R 探索你在 Youtube 上的活动:如何分析和可视化你的个人数据历史

原文:https://towardsdatascience.com/explore-your-activity-on-youtube-with-r-how-to-analyze-and-visualize-your-personal-data-history-b171aca632bc?source=collection_archive---------18-----------------------

了解您如何使用您的个人数据副本使用 Youtube

YouTube 上数据的分析和可视化-基于 YouTube 类别的绘图

让我们面对现实吧,亲爱的读者,我们经常访问并成为我们生活一部分的网站之一,无论是为了娱乐还是寻找答案,在那里我们可以花上几个小时,那就是 YouTube。就像我们花费大量时间在其他事情上一样,我很好奇我们的消费习惯到底发生了怎样的变化。有了谷歌外卖和 YouTube API,分析你的个人 YouTube 历史就很容易了。

如何获得我的数据的副本?

你可以通过谷歌外卖获得你在 YouTube 上的历史记录,这是谷歌提供的一个工具,可以查询你使用过的任何产品的历史记录和存储数据。你必须输入 https://takeout.google.com/settings/takeout 的网址并用你的个人账户登录。

截图:谷歌外卖网站

在那里,你会发现有可能要求一个完整的副本,或副产品,甚至只选择产品的一些特征。我们目前只对 YouTube 感兴趣。即使您链接了多个帐户,您也可以通过点击右上角的个人资料图片在它们之间切换。如果您上传了视频,我建议您确保取消选中它将生成的文件的视频选项,这将减少其重量和交付时间。

屏幕截图:生成 YouTube 帐户数据的副本

文件生成后,您将会收到一封通知电子邮件,您可以从这里下载该文件。出于安全考虑,该文件有一个截止日期,因此您必须在电子邮件中指明的日期之前完成。

截图:谷歌外卖的数据复制下载通知邮件

当你下载这个文件时,你会得到一个. zip 文件,解压后会创建一个文件夹和文件的结构,这取决于你最初从 Google 外卖中请求了什么。

截图:解压后的文件夹结构。活力

目前,与您在 YouTube 上的搜索和观看历史相对应的生成文件仅导出为 HTML。可以使用“rvest”包解析生成的 HTML。是的,我知道,不要评价我,《el canaca》和《dios eolo》永远是经典(墨西哥的病毒模因)。

截图:YouTube 搜索历史

处理导出的数据

现在您可以继续创建一个 R 脚本了。首先,您必须包含所有必需的包并设置搜索历史读数,它包含在“history”文件夹中的“search-history . html”文件中。

# REQUIRED PACKAGES
library(stringr)
library(rvest) 
library(tidyverse) 
library(jsonlite) 
library(tidytext)
library(lubridate) 
library(wordcloud)
library(httr)
library(ggplot2)
library(wordcloud2)
library(RCurl)
library(curl)
library(pbapply)
library(ggthemes)# READ SEARCH HISTORY
youtubeSearchHistory <- read_html("Takeout/YouTube and YouTube Music/history/search-history.html")

要使用 "rvest" 解析 HTML 中的文本,您可以指定对应于特定部分的 CSS 类。比如说,这条线。标题-单元格+。content-cell > a 在内容中查找超链接,对应于您所做的搜索。

# SCRAPING SEARCH HISTORY
youtubeSearch <- youtubeSearchHistory %>%
  html_nodes(".header-cell + .content-cell > a") %>%
  html_text()

截图:YouTube 搜索历史 HTML 元素检查器

当然,您可以通过同样的方式获得您需要的信息,例如时间戳。

# SCRAPING TIMESTAMP
youtubeSearchContent <- youtubeSearchHistory %>%
  html_nodes(".header-cell + .content-cell")
youtubeSearchTimeStr <- str_match(youtubeSearchContent, "<br>(.*?)</div>")[,2]
youtubeSearchTime <- mdy_hms(youtubeSearchTimeStr)

现在您已经有了搜索和时间戳数据,您可以用它们创建一个数据框。

# CREATING DATA FRAME SEARCH + TIMESTAMP
youtubeSearchDataFrame <- data.frame(search = youtubeSearch, 
                                time = youtubeSearchTime,
                                stringsAsFactors = FALSE)

观看历史

您将在另一个名为“watch-history . HTML”的 HTML 中找到的观看历史也包含在“历史”文件夹中。首先,您必须读取文件,并像以前一样通过 web 抓取获取每个条目。你必须用正则表达式来分析信息。

# READ WATCH HISTORY 
watchHistory <- read_html("Takeout/YouTube and YouTube Music/history/watch-history.html")watchedVideoContent <-  watchHistory %>%
  html_nodes(".header-cell + .content-cell")# POSSIBLE TIME CHARACTERS
watchVideoTimes <- str_match(watchedVideoContent, 
                             "<br>([A-Z].*)</div>")[,2]# POSSIBLE ID VALUES 
watchedVideoIDs <- str_match(watchedVideoContent, 
                             "watch\\?v=([a-zA-Z0-9-_]*)")[,2]# VIDEO TITLE
watchedVideoTitles <- str_match(watchedVideoContent, 
                                "watch\\?v=[a-zA-Z0-9-_]*\">(.*?)</a>")[,2]

现在,您可以将所有内容放回数据框中。

# DATA FRAME WATCH HISTORY
watchedVideosDataFrame <- data.frame(id = watchedVideoIDs, 
                                     scrapedTitle = watchedVideoTitles, 
                                     scrapedTime = watchVideoTimes, 
                                     stringsAsFactors = FALSE)watchedVideosDataFrame$time <- mdy_hms(watchedVideosDataFrame$scrapedTime)

使用 YouTube API 获取更多视频数据

我几乎可以肯定你会说到这一点…好吧萨乌尔,这没什么大不了的,还有什么!当您与 YouTube API 集成以获取更多数据时,这变得更加有趣。你可以获得更多的信息,而不是仅仅看到标题和时间戳这样的基本数据。通过这种方式,您可以查看您的视频浏览量的受欢迎程度、描述、类别等。

如果这是你第一次获得使用 YouTube API 的凭证,请遵循 YouTube 官方文档提供给你的简单步骤:https://developers.google.com/youtube/v3/getting-started

一旦你建立了一个谷歌项目,你将需要生成一个 API 键并启用 YouTube 数据 API v3 ,在侧边栏菜单中创建你的凭证。

截图:Google API 控制台,设置 YouTube API

截图:Google API 控制台,生成一个新的 API 密钥

一旦完成,现在就可以通过将 API 键赋给一个新变量来使用它了。您还必须将到 youtube API 的连接赋给一个新变量。

# ESTABLISH API KEY AND CONNECTION
youtubeAPIKey <- "HERE_YOUR_API_KEY"
connectionURL <- '[https://www.googleapis.com/youtube/v3/videos'](https://www.googleapis.com/youtube/v3/videos')

你可以做一个测试,比如我从我的 YouTube 频道里取一个 ID 为“SG2pDkdu5kE”的视频,给你一个概述。

# TRYIING QUERY RESPONSE
videoID <- "SG2pDkdu5kE"
queryParams <- list()
queryResponse <- GET(connectionURL,
                     query = list(
                       key = youtubeAPIKey,
                       id = videoID,
                       fields = "items(id,snippet(channelId,title,categoryId))",
                       part = "snippet"
                     ))
parsedData <- content(queryResponse, "parsed")
str(parsedData)

你会发现两个重要的参数是【字段】【零件】。您在查询时必须小心,因为您可能会超出请求配额,或者对请求的响应可能会变得非常慢。您可以在官方文档中找到关于这些参数的更多信息:https://developers.google.com/youtube/v3/docs/videos

屏幕截图:测试查询响应

获取视频类别:准备请求

要获得更多像视频类别这样的元数据,您可以使用从文件中获得的视频 id,并向 YouTube 请求每个视频的更多信息。您将向 YouTube API 发出几千个请求,因此您必须为这些请求做额外的准备。

发出 web 请求最流行的库是“httr”(一次只支持一个请求)。还有【卷毛】****【卷毛】。为了确保尽可能快地获得数据,请尝试这三种方法,根据已建立的查询,速度可能会有所不同。

# REQUESTS OPTIONS
testConnection <- "[https://www.google.com/](https://www.google.com/)"
testCount <- 100# HTTR TEST
system.time(for(i in 1:testCount){ 
  result <- GET(testConnection)
})# RCURL Test
uris = rep(testConnection, testCount)
system.time(txt <-  getURIAsynchronous(uris))# CURL TEST
pool <- new_pool()
for(i in 1:testCount){curl_fetch_multi(testConnection)}
system.time(out <- multi_run(pool = pool))

至少在我的例子中,使用“curl”的速度要快得多,不像其他两个选项。

获取视频类别:格式化请求

您需要为每个请求创建一个连接字符串,并消除重复以减少请求的数量。您还需要一个函数来解析请求-响应数据。

# CREATE REQUEST AND REMOVE DUPLICATES
createRequest  <- function(id){
  paste0(connectionURL,
         "?key=",youtubeAPIKey,
         "&id=",id,
         "&fields=","items(id,snippet(channelId,title,description,categoryId))",
         "&part=","snippet")
}
uniqueWatchedVideoIDs <- unique(watchedVideosDataFrame$id)
requests <- pblapply(uniqueWatchedVideoIDs, createRequest )# PARSE OUT RESPONSE
getMetadataDataFrame <- function(response){
  rawchar <- rawToChar(response$content)
  parsedData <- fromJSON(rawchar)
  data.frame <- cbind(id = parsedData$items$id, parsedData$items$snippet)
  return(data.frame)
}

您可以配置请求成功或失败时要做的事情。

videoMetadataDataFrame <- data.frame(id = c(), 
                                       channelId = c(), 
                                       title = c(), 
                                       description = c(), 
                                       categoryId = c()
                                       )# SUCCESS
addToMetadataDataFrame <- function(response){
  .GlobalEnv$videoMetadataDataFrame <- rbind(.GlobalEnv$videoMetadataDataFrame,getMetadataDataFrame(response))
}# FAIL
failFunction <- function(request){
  print("fail")
}

一种方法是从内存中获取数据,并配置我们的多个请求,这种方法虽然较慢,但更可靠。

# GRAB REQUEST RESPONSE FROM MEMORY
fetchMetadataFromMemory <- function(request){
  return(getMetadataDataFrame(curl_fetch_memory(request)))
}system.time(out <- multi_run(pool = pool)) 
saveRDS(videoMetadataDataFrame, file = "videoMetadataDataframeAsync1.rds")length(requests)
nrow(videoMetadataDataFrame)listMetadata <- pblapply(requests, fetchMetadataFromMemory)

一旦完成,根据你的互联网连接速度和数据帧的大小,你可以准备一杯咖啡,一杯茶,或开一瓶啤酒,只要指标达到 100%就可以等待。

截图:列表元数据控制台预览

您还将使用 "bind_rows" 函数将列表组合成一个有序的数据帧。

# COMBINE LIST INTO A DATA FRAME
videoMetadataDataFrame <- bind_rows(listMetadata)
saveRDS(videoMetadataDataFrame, file = "videoMetadataDataFrame_memory.rds")

获取视频类别:格式化类别

每个类别都有一个唯一的 ID。您需要发出另一个请求来获取它们,并将数据添加到新的列中。

# CATEGORY ID REQUEST
categoryListURL <- "[https://www.googleapis.com/youtube/v3/videoCategories](https://www.googleapis.com/youtube/v3/videoCategories)"categoryResponse <- GET(url = categoryListURL,
                         query = list(
                           key = youtubeAPIKey,
                           regionCode = "us",
                           part = "snippet"
                         ))
parsedCategoryResponse <- content(categoryResponse, "parsed")categoryDataFrame <- data.frame(categoryId=c(), category=c())
for(item in parsedCategoryResponse$items){
  categoryDataFrame <<-rbind(categoryDataFrame, 
                             data.frame(categoryId = item$id, category=item$snippet$title))
}categoryDataFrame
videoMetadata <- merge(x = videoMetadataDataFrame, y = categoryDataFrame, by = "categoryId")
head(videoMetadata)

您可以将新的数据框与观看历史相结合,以获取视频元数据及其播放时间。

# COMBINE WITH WATCH HISTORY
watchedVideos <- merge(watchedVideosDataFrame , videoMetadata, by="id")
str(watchedVideos)

随着时间的推移,你对在 YouTube 上观看的内容的品味有所改变吗?

有了搜索历史、观看历史和类别元数据,你现在可以回答这个问题了。你可以看到你玩得最多的视频类别,以及这些类别是如何随着时间的推移而变化的。

# VISUALIZE VIDEO CATEGORIES WATCHED 
watchedVideos %>% 
  group_by(category) %>% 
  summarise(count = n()) %>% 
  arrange(desc(count))watchedVideos %>% 
  ggplot(aes(x = time, fill = category)) + 
  labs(x= "Year", y= "Count") + 
  ggtitle("How much have your genre tastes changed over time?", "Most played categories")+
  geom_area(stat = "bin") + 
  theme_economist_white()

例如,在我的例子中,肯定会有很多人喜欢的东西,比如音乐类的视频。当然,电影和动画类别的存在是有意义的,我通过许多渠道了解即将上映的电影、预告片和评论。

对 YouTube 上的数据进行分析和可视化——根据类别及其随时间变化的行为绘制图表

你一天中什么时候花最多时间在 YouTube 上看视频?

把你宝贵的时间贡献给 YouTube,看看你最活跃的时候会很有趣。您可以通过创建一个自定义函数来查看它,该函数允许您将它作为一个时钟来查看。

# VISUALIZE CLOCK WATCHES PER HOUR
clockPlot <- function (x, col = heat.colors(n), ...) {
  if( min(x)<0 ) x <- x - min(x)
  if( max(x)>1 ) x <- x/max(x)
  n <- length(x)
  if(is.null(names(x))) names(x) <- 0:(n-1)
  m <- 1.05
  plot(0, type = 'n', xlim = c(-m,m), ylim = c(-m,m), 
       axes = F, xlab = '', ylab = '', ...)
  fig <- pi/2 - 2*pi/200*0:200
  polygon( cos(fig), sin(fig) )
  f2 <- .02
  fig <- pi/2 - 2*pi/n*0:n
  segments( (1+f2)*cos(fig), (1+f2)*sin(fig), (1-f2)*cos(fig), (1-f2)*sin(fig) )
  segments( cos(fig), sin(fig),0, 0, col = 'light grey', lty = 3)
  f1 <- -2*pi/n*(0:50)/50
  for (i in 1:n) {
    fig <- pi/2 - 2*pi/n*(i-1)
    b <- pi/2 - 2*pi/n*i
    polygon( c(0, x[i]*cos(fig+f1), 0), c(0, x[i]*sin(fig+f1), 0), col=col[i] )
    f2 <- .1
    text((1+f2)*cos(fig), (1+f2)*sin(fig), names(x)[i])
  }
}clockDataFrame <- watchedVideos %>% 
  mutate(hour = hour(time)) %>% 
  group_by(hour) %>% 
  summarise(count = n()) %>% 
  arrange(hour)clockPlot(clockDataFrame$count, main = "What hours do you spend the most time watching YouTube?")

举起手来,这里有多少人像我一样,能够克服失眠有时在黎明时分花几个小时看 YouTube?在我的情况下,很明显,从凌晨 2 点到 3 点,我把我宝贵的时间都给了平台。

YouTube 上数据的分析和可视化——YouTube 上最活跃的时间图

有哪些视频是你反复看得最多的?

你可以获得的另一个有趣的信息是你反复播放次数最多的前 10 个视频。您可以用一个简单的表格来形象化这一点,用包“kable extra”并按照复制次数降序排列。

# TABLE MOST RE-WATCHED VIDEOS
w1 <- watchedVideos %>%
  mutate(year = year(time)) %>% 
  group_by(year, title) %>% 
  summarise(count = n()) %>% 
  arrange(year, desc(count)) %>% 
  top_n(5)mostReWatched <- knitr::kable(x = head(arrange(w1, desc(count)) %>%
                                      select(year, title, ,count), 10),
                                      col.names = c('Year', 'Video Title', 'Count'))
kable_styling(mostReWatched, "striped", position = "left", font_size = 12)

您将得到如下所示的表格。就我而言,我很好奇从 2016 年到 2019 年,一些视频没有出现在我的前 10 名中。我想原因是 Spotify 进入了我的生活,在此之前我主要使用 YouTube 来播放音乐。顺便说一下,是的,我是“Vicentico”和“Los Fabulosos Cadillacs”的粉丝(西班牙有史以来最具标志性的摇滚/ska 乐队的前歌手和乐队之一)。

分析并可视化您在 YouTube 上的数据——反复观看的 10 大热门视频

YouTube 上搜索次数最多的词语是什么?

你可以用“word cloud”软件包更好地想象这一点。当然,这取决于你的历史有多长。例如,你可以建立至少重复 25 次的单词。

# WORDCLOUD MOST SEARCHED WORDS
myWords <- youtubeSearchDataFrame %>%
  unnest_tokens(word, search) %>%
  anti_join(stop_words) %>%
  count(word, sort = TRUE)myWordcloud <- myWords %>%
  group_by(word) %>%
  summarize(count = sum(n)) %>%
  anti_join(stop_words)wordcloud(words = myWordcloud$word, freq = myWordcloud$count, min.freq = 25, 
          max.words = 100, random.order =FALSE, rot.per =.35,
          colors=brewer.pal(9, "Set1"))

结果你会得到一个如下的图,在那里你会发现与你的口味相吻合。如果你想知道,答案是肯定的,我相信 UFO 现象(西班牙语 OVNI)。

对你在 YouTube 上的数据进行分析和可视化——Wordcloud 搜索次数最多的词

在你看过的视频描述中,出现频率最高的术语是什么?

要回答这个问题,您可以像上一个示例一样进行操作,但是这次使用视频描述,利用我们的数据框中有这些信息这一事实。通过制作广告,很多视频在其描述中包含 URL,这是非常常见的,除了不能给我们相关信息的词,如文章、代词等,我们将过滤这些(或尽可能多的过滤)。这次我们将用【单词云 2】和至少重复 250 次以上的单词来做。

# WORDCLOUD MOST FREQUENT WORDS IN VIDEO DESCRIPTIONS
descriptionsWordcloud <- watchedVideos %>%
  unnest_tokens(word, description) %>%
  anti_join(stop_words) %>%
  count(word, sort = TRUE) %>%
  filter(! word %in% c("www.instagram.com", "gmail.com", "www.twitter.com", "youtu.be", "como", "instagram", "instagram.com", "tú", "watch", "aquí", "pero", "su", "http", "al","se","si","goo.gl","smarturl.it","facebook","video","más", "twitter", "te","lo","este","tu", "para", "por", "con", "es", "del", "las", "una", "mi", "de", "en", "la", "el", "los", "https", "bit.ly" , "â", "www.youtube.com")) %>%
  filter(n > 250)wordcloud2(descriptionsWordcloud)

正如你所看到的,正如你所料,许多词指的是订阅他们的频道或关注他们的社交网络

对你在 YouTube 上的数据进行分析和可视化——视频描述中最常见的词

感谢您的友好阅读。在这里可以找到完整的代码:https://github . com/cosmoduende/r-YouTube-personal-history-analysis

我感谢你走了这么远,我希望你有一个快乐的分析,你可以把它付诸实践,并像我一样对结果感到惊讶和有趣!

顺畅地探索您的数据

原文:https://towardsdatascience.com/explore-your-data-frictionless-adddb3759bb?source=collection_archive---------60-----------------------

引入新的数据探索框架

照片由阿克谢·纳纳瓦蒂Unsplash 上拍摄

问题

作为一名数据科学家,我一直认为代码不是探索数据集的完美解决方案。

在我看来,数据探索应该是一个平稳的过程,而不是一波三折。

我不喜欢使用代码进行数据探索的另一点是项目之间的高度代码冗余。

如果你不是新手,很可能你已经开发了一个函数来识别和绘制数据集的每个变量。但是,您可能会发现这种方法不允许进行深入探索。您仍然需要调整您的代码以超越整个发行版。

几个月前,我厌倦了这个。

我决定编写 Smoof 代码,这是我第一次尝试创建一个无摩擦数据探索软件。

解决办法

Smoof 不是所有解决方案的一站式解决方案,而是一个用于数据探索的生产力工具。

你可以试试 Smoof 的一些演示数据集这里。你也可以通过下载代码 (python)在你的机器上运行来测试它。

如何使用它

导航至您的工作目录并选择一个 csv 文件

输出数据集关键信息

输出变量关键信息

离群值的数量是用四分位法估计的。这当然不是识别异常值的最佳方法,但它是省时的。这里的目的是提供关于异常值的潜在存在的预先信息。

分类变量的所有描述性统计都是根据每个标签的计数计算的。

可视化变量分布

交叉过滤

向下钻

降一降一过滤

关于变量类型及其蕴涵的注记

在 Smoof 中,变量被识别为以下类型之一:

  • 日期
  • 数字的
  • 类别短(少于 15 个唯一标签)
  • 长类别(超过 15 个唯一标签)

为类别创建两个子类型的原因是,超过 15 个不同标签的条形图不容易分析。这就是为什么类别 Long 被表示为每个标签的值的分布。

电流限制

平滑处理高达 10 MB 的数据集,对于更大的数据集,它仍然运行,但加载和交叉过滤操作可能会变慢。

实际版本缺少一些关键功能,如 2D 和 3D 绘图以及空值探索。这些特性将在下一个版本中添加!

成为共同创造者

这是一个开源项目,我很高兴你能和我一起开发未来的版本。有兴趣请联系

只需 3 次点击,即可在线浏览您的数据

原文:https://towardsdatascience.com/explore-your-data-online-in-3-clicks-8fe356a36c3c?source=collection_archive---------60-----------------------

使用这款 Google Colab 笔记本,无需任何编程即可获得免费的数据探索性分析报告

活动发起人Unsplash 上的照片

毫无疑问,我们正处于数据时代,数据就是力量。事实上,许多人将的数据称为新石油,这是有充分理由的:

  • 分析正确的数据可以提供有用的见解,从而更好地理解和改进业务、流程和任务。
  • 机器学习已经证明数据可以打败更好的算法(这句名言出自谷歌研究总监。)
  • 数据为你提供了事实,你可以根据这些事实在需要时评估和纠正事情(正如爱德华兹·戴明所说的“我们相信上帝,所有其他人都必须带来数据”)。)

然而,数据本身通常是不够的。您还需要分析和利用这些数据的技能,这通常涉及(至少)一些统计和编程知识。不幸的是,这些技能并不常见,在数据科学专业人员的需求和供应之间造成了巨大的差距。

轻松浏览您的数据

好消息是,近年来已经开发了许多工具来促进对数据分析和数据科学的访问。最新的一个是 pandas-profiling ,这个库使我们能够对文件中的数据进行快速而轻松的探索性分析。

在 Medium 上已经有很多关于这个主题的文章,所以我不会用更多关于熊猫的信息来打扰你。相反,我写这篇文章是希望让非程序员能够使用这个强大的库轻松地对他们的数据进行快速分析。

为此,我已经创建了一个 Google Colab Notebook ,它已经整合了上传数据、创建 HTML 格式的探索性报告和下载报告所需的所有工具和代码,所有这些只需点击 3 次。

你需要做的就是使用谷歌 Chrome 访问下面的链接(其他浏览器在谷歌 Colab 上上传和下载文件的表现不佳),然后按下下图所示的左边的播放按钮(或者你也可以按 CTRL + ENTER。)这将运行您在那里看到的代码,它将提示您选择一个或多个包含您的数据的 CSV 文件。只需按下“选择文件”按钮,并选择您想要分析的文件。

[## 探索分析

探索 csv 文件与熊猫-剖析

colab.research.google.com](https://colab.research.google.com/drive/1UV1tgLOFmZdz0H5S8NOUHJ8mxvtZhezp?usp=sharing)

然后,等待您看到上传文件和生成报告的进度。请耐心等待,因为有时根据文件的大小,做这两件事可能需要一段时间。在此之后,您可以下载与您上传的数据同名的报告。

重要的是,你上传的数据不会与任何人共享,如谷歌实验室常见问题所述。我还将代码输出设置为在每次运行后不保存,这样除了您之外没有人能看到您上传的文件的名称,此外,代码会在使用 pandas-profiling 处理每个上传的文件后立即删除它。为了您的安全,您可以查看笔记本中的所有代码,也可以随意重复使用。

附注:请注意,我已经用文件< 15MB, if your files are bigger, I recommend you to download the notebook and running it locally. For this, you will need 测试了这个来安装 Jupyter

查看报告

我已经用 Google Colab 笔记本为 Ames 住房数据集创建了一份报告,这是我从房价卡格尔竞赛下载的。该数据集包含爱荷华州埃姆斯地区出售的房屋信息,包括出售房屋的几个特征及其销售价格。这是从机器学习开始的最受欢迎的数据集之一,目标是能够根据其特征预测房屋销售价格。

该报告是一个 HTML 文件,您可以用您喜欢的任何浏览器打开它。它由不同的部分组成,您可以导航并与之交互:

  • 概述:数据集统计数据的快速汇总,如变量数、观测值数、缺失单元格、变量类型分为 CAT (category)、NUM (numeric)和 BOOL。在这里,您还可以看到来自 pandas 的“警告”——包括缺失值、值为 0 的单元格以及其他一些可能构成报告完整性的情况。

我从埃姆斯房产数据集中创建的一份报告的概述,该数据集是从房价卡格尔竞赛中下载的。

  • 变量:数据中包含的每个变量(即列)的详细分析,包括不同值、零、缺失值和其他基本统计数据的数量。您也可以使用“切换细节”按钮(如下所示)查看每个变量的更多细节。

来自 Ames Housing 数据集的变量 MSSubClass 的详细信息。

  • 相互作用:在这一部分,我们可以看到两个变量是如何相互作用的。下面,我选择了 SalePrice 和 GrLivArea 这两个高度相关的变量(我是怎么知道这个的?只需等到下一节;) ).在这里,我们可以看到,地面居住面积越高,售价越高。您可以通过在左侧列表中选择任意一对变量来检查这一点。

来自 Ames 住房数据集的变量 GrLivArea 和 SalePrice 之间的相互作用。

  • 相关性:这是报告中最有趣的部分之一。我们可以看到 4 个不同的相关矩阵,每个矩阵都使用不同的相关类型:Pearson(最常用的,如下图所示)、Spearman、Kendall 和 Phik。根据相关值,每两个变量之间的相关性显示为具有不同颜色的正方形:高度着色的是高度相关的,而白色或几乎白色的正方形显示低相关性。如果我们有两个高度相关的变量 X 和 Y,我们可以用 X 来推断 Y,反之亦然。事实上,这让我们快速分析了每个变量对于建立机器学习模型的重要性。例如,在 Ames Housing 数据集中,我们希望在给定房屋特征(如地块面积、车库面积、楼层数、房屋建造年份等)的情况下预测其价格。在下图中,我们可以在矩阵的最后一行看到销售价格和所有这些特征之间的相关性,这表明每个特征对决定房屋价格的重要性。正如你所看到的,地段面积与房子的价格高度相关,这是我们从经验中知道的。但我们也可以看到,其他不太明显的特征与房价高度相关,如车库可以容纳的汽车数量和一楼的面积。

艾姆斯住宅数据集的相关矩阵。我们看到了一个关于熊猫的小缺陷——描述:情节标签有点拥挤。

  • 缺失值:这个部分向我们展示了一个图表,从中我们可以快速观察到每个特性有多少缺失值。它以一种有点令人困惑的方式显示出来:蓝色条越高,现值就越多。所以在下图中,我们可以看到 PoolQC(池条件)是一个有很多缺失值的特性,而 HeatingQC(加热条件)没有任何缺失值。

埃姆斯住宅数据集的缺失值。与上面相同的小缺陷:图标签有点拥挤。

  • Samples :在这里您可以看到数据集的第一行和最后一行,以便更好地了解数据的样子。

来自 Ames Housing 数据集的样本数据,请注意,此图中仅显示了前几列,但您可以在报告中水平滚动表格。

结论

Unsplash 上由Duan Smetana拍摄的照片

Pandas-profiling 为我们提供了用几行代码就能生成几乎即时的数据报告的能力,使我们能够轻松地对其进行详细分析。通过使用我在这里展示的 Google Colab 笔记本,任何人都可以利用这个工具,无需编码,只需简单地点击三次。

尽管这个工具并不完美:

  • 大文件需要很长时间来处理,甚至有时我们会得到许多错误。特别是,当使用 Google Colab 笔记本时,大文件可能需要很长时间才能上传并进行分析(在这种情况下,您可以下载笔记本并在本地运行;) ).
  • 有一些小的装饰错误,比如上面显示的那些。
  • 可能有必要进行更专门的分析,为此,我们需要自己实施分析。

然而,pandas-profiling 是将数据科学带给每个人的一大步,我相信它将继续改进,在不久的将来提供更好的报告。

感谢阅读,希望这篇文章对你有所帮助!

探索两个月的纽约自行车事故

原文:https://towardsdatascience.com/exploring-2-months-of-bike-accidents-in-nyc-897d280951d5?source=collection_archive---------25-----------------------

数据分析

在纽约骑自行车可能很危险,因为咄咄逼人的驾驶和大量的汽车。但是我们很快就会看到,它们不是唯一的…

我骑花旗自行车已经一年了,把手放在自行车把上是我在纽约最喜欢的出行方式。我觉得在街上转来转去,享受这个城市令人惊叹的氛围是如此的方便和有趣。我感谢过多的自行车码头和安全的自行车道,让我有这些经历。

相反,在纽约骑自行车可能是危险的,因为咄咄逼人的驾驶和压倒性的汽车数量。在这篇博文中,我调查了 2020 年 1 月和 2 月曼哈顿自行车碰撞的纽约公共数据集。此外,我会更仔细地查看细节,以了解为什么会发生这些事故以及涉及哪些车辆。我们很快就会看到,可能不仅仅是车手和赛车有问题。

你可以在我的笔记本上阅读我的代码。

纽约机动车碰撞数据集

原始数据来源为 NYC 公共安全机动车碰撞事故。这些数据显示了事故的确切位置、发生的原因、涉及的车辆、受伤的骑车人数量、撞车的时间和日期。我过滤了 2020 年 1 月和 2 月在曼哈顿发生的事故,并使用 python 熊猫只保留了受伤或死亡的骑自行车者的行(在我的数据中,没有死亡,幸运的是!)数据帧包含 65 个事故。下面是前 5 行的截图,分为两部分。让我们看看数据显示了什么。

  1. Vehicle_type_code_1 (事故第一方,即车辆 1)是指具体涉及的车辆。
  2. 促成因素 _ 车辆 _1 是车辆 1 促成事故的原因。它包括转向不当、驾驶员注意力不集中/分心和倒车不安全等原因。
  3. Vehicle_type_code_2contribution _ factor _ Vehicle _ 2的意思与车辆 1 相同,但涉及第二方。

让我们分析我们的数据。

事故是由什么引起的?

该数据集以车辆 1(驾驶员或骑车人)作为事故的原因来组织,而偶尔车辆 2(另一个驾驶员或骑车人)也受到责备,例如两个驾驶员都分心了。

56/65 或 86%的事故是由车辆 1 驾驶员造成的,因为:

**Driver Inattention/Distraction                           19
Unspecified                                              11
Passing or Lane Usage Improper                            6**
**Passenger Distraction                                     4** Pedestrian/Bicyclist/Other Pedestrian Error/Confusion     3
Failure to Yield Right-of-Way                             3
Turning Improperly                                        2
Following Too Closely                                     2
Backing Unsafely                                          2
Passing Too Closely                                       1
Traffic Control Disregarded                               1
Unsafe Lane Changing                                      1
Driver Inexperience                                       1

首要原因是驾驶员注意力不集中(19/56 或 34%),其次是“原因不明”。后面我们会稍微讲一下未指明的。其他原因包括超车、车道使用不当和乘客分心。

通读这些理由是很有意思的,让我更加意识到这些可能性,这样我骑自行车的时候会更加安全。由于 4/56 或 7%的事故是由分心的乘客造成的,我只能想象这是如何导致事故的。放音乐声音太大?和司机说太多话?乘客对摩托车手的愤怒?

埃里克·奥迪恩在 Unsplash 上的照片

不仅仅是司机

车辆 1 不仅包括司机,也包括骑自行车的人。1 类车辆中有 9/65 或 14%由骑车人构成,事故原因如下:

Driver Inattention/Distraction    6
Aggressive Driving/Road Rage      1
Passing Too Closely               1
Unspecified                       1

最主要的原因是骑车人注意力不集中/分心,占 67%。第二和第三个原因是好斗骑自行车/暴怒和靠得太近。 经验教训:注意,保持冷静的头脑,获得空间!

至于骑车人的“未指明的”原因,该原因也是未指明的或缺少第二方车辆 2 的原因。我认为可能有很多原因,比如无法确定确切的原因。了解导致两个未知原因的情况会很有趣。

另一方面,这些是造成事故的一些原因双方:

大多数原因是两个司机都分心或没有注意。其他组合是汽车倒车不安全,而自行车太近,或者司机或骑车人的某种错误或计算失误导致事故。其他一些事故是由行人/骑自行车的人/其他行人的错误造成的。我想知道这可能是什么,但当我和一个朋友谈到这些事故时,她告诉了我一个个人故事。有一次她过马路发短信,没有注意到一个骑自行车的人朝她走来。骑自行车的人试图避开她,结果撞车了!

照片由 Daria NepriakhinaUnsplash 拍摄

谁对事故负最大责任?

让我们看看整体事故中涉及哪些车辆。

Taxi                                   20
Station Wagon/Sport Utility Vehicle    16
Bike                                   14
Sedan                                  13
Pick-up Truck                           2
Box Truck                               2
Van                                     1
Armored Truck                           1
Bus                                     1

大约 30%的事故是由出租车引起的,24%是由旅行车或 SUV 引起的,20%的事故是由骑自行车的人引起的!让我们仔细看看出租车是如何造成最多事故的。

**Driver Inattention/Distraction                           6
Passing or Lane Usage Improper                           4** Unspecified                                              3
Following Too Closely                                    2
Passenger Distraction                                    2
Pedestrian/Bicyclist/Other Pedestrian Error/Confusion    2
Traffic Control Disregarded                              1

同样,司机注意力不集中和分心,以及不正确的车道通过。就我自己而言,我应该小心,不要假设车辆能看到我,并记住要小心超车。纽约的出租车司机因不打信号就插队和变道离其他车辆太近而臭名昭著。出租车司机或骑车人的一个错误举动可能会造成严重伤害!

DilUnsplash 上拍摄的照片

小心!

最后,我也有兴趣看看是否有任何自行车碰撞。在 65 起事故中,只有一起是一个骑自行车的人跟得太近。只有一名骑车人受伤。也许一个骑车人以为有足够的空间超过一个骑得慢的骑车人,但最终靠得太近,导致了撞车。我会保留我的空间。

后续步骤

我喜欢我们可以看着这些数据,用我们的想象力来看看事故是如何发生的。两个月的数据已经让我对今天纽约市的自行车安全有了一点了解。随着更多月份的分析,我们可以开始看到更多的趋势。我对这些事件的分析有助于我理解发生事故的原因。上面的原因只是一小部分原因,但它也帮助我知道当我骑自行车时还需要注意什么。在未来的帖子中,我将进一步深入这个数据集,并绘制出我在花旗自行车旅行中的撞车事故,以了解我与事故的接近程度。感谢阅读。

来看看我的 jupyter 笔记本,看看我是如何用 Python 编写我的分析的

使用 NLP 探索 4.5 年的日志条目

原文:https://towardsdatascience.com/exploring-4-5-years-of-journal-entries-with-nlp-589de6130c2d?source=collection_archive---------55-----------------------

诺德伍德在 Unsplash 上拍摄的照片

使用 Python 和 fast.ai 写作 4 年多的经验解读

介绍

自 2016 年 1 月 1 日起,我每天都在谷歌文档中记录当天的事件和我的想法。我开始写日记是因为我对自己的现状不满意,希望能够回顾过去看到进步。尽管有这个目的,这些日记还是变得很长很快,我从来没有机会以任何有意义的方式回顾它们。

随着我的日志接近五周年纪念日,我认为最终回顾一下会很有趣——不是通过阅读它,而是通过用我新学到的数据科学和自然语言处理(NLP)技能来分析它。

数据收集

幸运的是,我是一个习惯动物。在过去的 53 个月里,每篇日志看起来都完全一样——每月一篇谷歌文档,格式如下:

一月

1 月 1 日
此处录入

1 月 2 日
此处录入

诸如此类。

创建我的语料库就像下载每一个谷歌文档作为文本文件一样简单,快速阅读它以寻找零散的段落中断,并使用几行代码(准确地说是 21 行)将其全部放入 pandas DataFrame。在数据帧中,我包括了年、月、日、日期(日期时间)和日志条目。

作者图片

然后,我使用正则表达式(re)模块清理数据,使用 scikit-learn 的 CountVectorizer 函数创建文档术语矩阵,并按年份对其进行分组,然后将其转置用于探索性数据分析。

作者图片

探索性数据分析

为了更好地理解我的日记,我从三个角度来看它们:常用词、感悟和长度。

  • 常用词:我使用词云来确定和可视化最常用的词,以收集重要的主题。
  • 情绪:我用 TextBlob 库进行了一次情绪分析,看看我的极性是如何随时间变化的。
  • 长度:我用熊猫分离出各种长度指标,并推断出为什么我在特定时间点写得多或少。

最常见的单词

首先,我想看看最常见的单词,我希望这些单词能让我了解每年对我来说最重要的事情。我删除了五年中至少三年前 30 个最常用的单词,因为它们可能对区分不是很有见地。

我喜欢尽可能直观地表示信息,所以我使用文档术语矩阵来创建每年的单词云。

作者图片

我的观察:

  • 2016: 我谈了很多我的另一半、麦迪森** ,还有我的(大概指的是我如何挣扎着从中走出来,或者因为背痛在里面待了多长时间)。其他让我印象深刻的词还有英语生物作业,指的是学校,打*,指的是各种游戏和运动。
  • 2017: 麦迪逊依旧突出但不那么突出;类似的学校主题也存在,工作指的是我在 Sonic 开始工作,而我将重要性显著增加——我想我开始更多地谈论我将来要做什么。
  • 2018: Madison 随着我们关系的结束而再次凸显,随着变大,我开始更多地谈论其他人。我也和汉娜成了好朋友,后来还和她约会。
  • 2019: 汉娜变得很重要,其他 — 2019 包括我高三第二学期和大学第一学期,所以很有意义。今年的乐趣也增加了,这是另一个不足为奇的变化。
  • 2020: 工作指的是春季班增加的工作量——比如MIS——以及我在学期中的实习;依然突出,数据阅读指的是我在 2020 年捡来的爱好/兴趣(我正在用其中的一个写这篇文章)。

*匿名化名

情感分析

接下来,我想看看我的写作有多积极/消极,是否符合我那些年的感受。TextBlob 使得对语料库进行情感分析变得非常容易,所以我分析了语料库,并按月/年查看了极性。

作者图片

年度结果令人惊讶:2020 年几乎是其他年份的两倍。我肯定会认为 2020 年是我最快乐的一年,但我没想到情绪分析会如此强烈地支持我的直觉。

这里我们看到极性随着时间的推移以每月和每年的频率显现出来:

作者图片

长度分析

我想探究的最后一件事是长度:我写了多少,随着时间的推移,我的冗长是如何变化的?

为此,我创建了一个数据框架,其中包含关于日志条目长度的各种指标:

作者图片

我在 2019 年写得最多,在 2020 年写得最少,每天写得更多的年份词汇量更大。2020 年是迄今为止每日单词量最低的一年,这是有道理的(WPD);这是我最快乐也最忙碌的一年。

看到我在 2020 年写得少了多少,不禁让人产生疑问:“快乐”(极性)和我的条目长度之间有联系吗?我用不同的方法绘制了每天平均字数与情绪的关系图,看看是否有什么规律。

作者图片

随着时间的推移,每天绘制情绪和单词显示了一些关系;除了 2019 年的字数大幅上升和 2020 年的字数快速下降之外,每月的情节都很好。

年度图表给出了一个更全面的见解,如果不是相互冲突的话:除了 2018-2019 年,情绪和字数都有所上升,字数和情绪相互反映。当情绪上升时,字数下降,反之亦然。这符合我的直觉,如果我更快乐、更积极,我会写更少的日志,而是花更多的时间关注那些积极的事情。

作者图片

去除时间成分,散点图变得不那么有意义;我看到的唯一趋势是,当绘制每日图表时,情绪非常高或非常低的条目没有高字数,而那些极性更中值(介于-0.5 和 0.15 之间)的条目有更高的字数。

这很有意思;如图所示,我不会期望在非常积极的日子里写很多,但在非常消极的日子里,我会期望写很多关于为什么这一天不好,为什么我感到沮丧/不安,等等。有可能 TextBlob 情绪分析并没有很好地将我的糟糕日子捕捉为低极性,而将我的积极日子捕捉为高极性。

深度学习

由于这是一个无监督的学习项目(没有标签的数据),我的大部分见解来自探索性的数据分析。我尝试过主题建模,但结果无法理解。这是有意义的,因为主题建模最擅长识别高度不同的主题(如体育和政治),而我的日志主要集中在谈论我的一天。

我的分类器计划也没有成功,因为我需要手动对所有 1601 日志条目进行分类——这不是对我时间的一个特别好的利用。

然而,我能够使用深度学习来完成我最感兴趣的任务:文本生成!

训练语言模型

我从一个语言模型开始,这个语言模型是在 Wikitext103 数据集上预先训练的,这个数据集是从维基百科中经过验证的文章中提取的超过 1 亿个标记的集合。

这样做让我可以利用迁移学习,这意味着我的模型从对英语的良好理解开始,而不是从零开始。然后,我对所有 1601 条日志条目进行了训练,经过四次微调后,准确率接近 33%。

作者图片

我对它的表现感到惊讶。在语言模型的上下文中,准确性基本上指的是它能够根据之前的词汇正确预测下一个单词的频率。鉴于我的词汇表中有成千上万的单词,当时⅓的正确预测是相当不可思议的。我想我是一个可预测的作家。

文本生成

对于文本生成,我给了模型各种各样的起始单词/短语,类似于我经常用来开始我的日志的单词/短语,并在获得满意的结果之前测试了一些不同的长度:

200 字,起始文字:“好的”

作者图片

这些条目波动很大是可以理解的,这里有一个不太容易理解的条目,其中有相同的参数:

作者图片

这里是我让它谈论大学里的事情的最佳尝试——起始文本是“我的宿舍”。考虑到我只有一年的大学经历,而高中有四年,我对这个模型很少提到与大学相关的话题并不感到惊讶。

作者图片

肯定有很多重复和一些无意义的部分,但老实说,这听起来很像我写日记的方式。我对结果很满意。这种语言模式确实一针见血地描述了我大多数时候是如何开始写日记的:起床,谈论学校。

Fast.ai 的 learner.predict 方法当然不是最好的文本生成方法,但在这个项目的范围内,我无法使用另外两种文本预测方法 【光束搜索】nucleus predict 获得更好的结果。

语法上

我注意到的最后一件事是,生成的文本中有一些基本的语法错误,如缺少标点符号、笨拙的空格和大写错误。我本来可以安装大量的语法检查 API,但是我在学校使用 Grammarly,希望快速得到结果,所以我手动将生成的条目放入其中,不加区别地接受所有的修改建议。结果如下:

200 字,起始文字:“好的”

作者图片

200 字,起始文字:“我的宿舍”

作者图片

结论

从事这个项目不仅令人怀念,而且是一次很好的学习经历和许多乐趣。我不知道接下来会发生什么,但我发现了大量有趣的结果,并从一个新的数据驱动的角度审视了我的个人经历。

用我在过去三个月里学到的技能来解决这个新奇的问题真的很令人兴奋,我想继续使用指导我完成这个项目的过程来获得有意义的见解。

这是我写的第一篇文章,也是我完成的最大的数据科学项目,所以肯定还有改进的空间。如果你有任何建议,请让我知道。

作为投资者探索零售数据集

原文:https://towardsdatascience.com/exploring-a-retail-data-set-as-an-investor-1b08e5c7b255?source=collection_archive---------47-----------------------

基于美国人口普查年度零售贸易调查数据集,应用数据分析寻找相关见解

图片由 Unsplash 上的克拉克街商业拍摄

介绍

年度零售贸易调查涵盖了 50 个州和哥伦比亚特区的零售贸易企业。这项贸易调查显示了国家的销售额、销售税、费用、库存等等。

这里的挑战是只探索这些数据,并发现我们可以找到什么样的相关见解,以及一些答案,以向投资者展示哪个细分市场可能是最佳选择。

当然,这可能会产生新的疑问和疑虑,但以下问题将在一开始引导我们:

  • 作为投资人进入哪个细分市场更感兴趣?成本和费用如何影响细分市场?
  • 哪一个有更多的钱花在存货上?
  • 库存如何影响商业利润?
  • 预测下一年(2015 年)的销售额

数据

为了找出这些答案,将使用由数据提供的美国人口普查年度零售贸易调查数据集。世界。通过这个数据集,我们可以获得从 2004 年到 2014 年在美国收集的销售、销售税、库存、毛利、费用、采购和应收账款总额等信息。

假设

美国人口普查年度零售贸易调查数据集得出了按主要细分市场组织的数据,其中一些细分市场。经过数据清理过程后,2004 年至 2014 年间,选择简化并仅处理主要类别。这些段和各自的代码是:

我们应该检查许多指标,以便尽可能准确地了解感兴趣的细分市场。然而,该数据集提供了一般信息,允许进行唯一的定量分析。这里的想法是从这个数据集中提取最相关的信息。

另一个重要的问题是,这个分析是一个研究案例,针对一个“假设的投资者”,他没有会计领域的知识,只是通过在该领域的一些研究。此处的目的是,即使数据有限,也要展示数据分析如何帮助识别相关信息,从而帮助做出决策。

说完了,我们开始吧!

克里斯蒂安·柯普克在 Unsplash 上拍摄的照片

销售

当有人考虑在某个领域投资时,销售额可能是第一个想到的指标。我们可以说这是合乎逻辑的,毕竟销售越多,钱就越多。因此,我们需要扫一眼,挑选出销售额较高的细分市场。

实际上,事情没那么简单。很明显,如果你不卖,你就退出了市场,但我们需要记住,销售不同于盈利能力。销售是必不可少的,可以为我们带来有价值的数据,但除了销售,我们还需要检查许多其他重要的指标。无论如何,这是一个很好的起点。

总销售额增长| 2004 年至 2014 年

上图向我们展示了逐年的销售增长,从 2004 年到 2007 年,我们可以实现接近平均 5%的持续增长。由于大衰退,我们可以看到从 2008 年开始下降,到 2009 年下降到-7.68%。从 2010 年开始,美国零售业开始恢复性增长,平均增长 4.2%。根据这一初步分析,我们可以预计 2015 年的增长率约为 3.5%。

在下面的图表中,我们可以看到一个有趣的按细分市场划分的年度总销售额视图。汽车和零部件经销商的总销售额最高,历年达到 94.4 亿美元。如果我们与销售额第二高的地方百货店比较,会发现销售额几乎少了 31%,而与销售额最低的地方体育用品、业余爱好、书籍和音乐商店相比,差距达到 90%以上。

细分市场总销售额| 2004 年至 2014 年

下面的下一张图显示了各细分市场的同比增长,除了各细分市场的情况与之前一样之外,还可以了解哪些领域对大衰退带来的经济问题更加敏感。汽车及零部件经销商板块是第二大受影响的板块,2009 年 -14.53% ,仅次于加油站,同年-22.28%

另一方面,受影响最小的部门是百货商店,其次是食品和饮料商店,分别为 -1.03%-0.14%

无店铺零售商多年来的增长最为稳定和强劲,在 2014 年达到了 8.48%

细分市场同比增长| 2004–2014 年

当我们按细分市场分析平均增长率时(如下所示),无店铺零售商在分析期内的比率最高,为 6.85%

相比之下,汽车及零部件经销商,这一历年来总销售额最高的行业,平均增长仅为1.87%

各细分市场的平均增长率| 2004–2014

从以前的数据分析到现在,我们可以得到一些结论。尽管是对经济危机最敏感的行业之一,汽车和零部件经销商的复苏最为强劲,从 2010 年开始计算,平均增长率为 8.75%

非商店零售商有类似的平均增长( 8.59%) ,但如果我们考虑总销售额,机动车和零部件经销商销售额为 94.4 亿美元。相比之下,非商店零售商的销售额略高于 37.4 亿美元,两者相差 57 亿美元。

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

费用和成本

之前的分析向我们展示了汽车和零部件经销商似乎是最有前景的细分市场,但正如之前所说,销售利润各不相同。

从现在起,我们的任务将是寻找能够证实或否定这一先前结论的相关信息。基于我们的数据集信息,我们可以开始寻找支出、成本以及它们如何影响收入。

让我们从一些用来探索公司财务稳定性和整体健康状况的最重要的利润率开始:毛利率净利润率。****

首先,我们将分析毛利率。这个指标表明公司在成本之外获得了多少利润。这个结果越高,公司就越能有效地从每一美元的成本中获取利润。我们可以通过公式计算

毛利率=(收入—销货成本)/收入 x 100

我们可以使用总销售额作为收入,但首先我们需要找出商品销售成本(COGS ),它指的是生产公司销售的商品的直接成本。成本越高,利润就越低。为此,将使用下面的公式

销货成本=期初库存+购货—期末库存

商品成本 x 总收入

按部门划分的货物费率成本

上面的图表让我们大致了解了商品成本对零售利润的影响。总销售额较高(超过 50 亿美元)的细分市场也有较高的商品成本,在机动车和零部件经销商中超过 74 % ,在加油站中几乎达到 82%

另一方面,销售额较低的第二个区域,家具和** 家居用品商店的价值较低,含 Cogs 的费用不到 49% 。**

现在,我们可以访问毛利率并发现每一美元的成本可以产生多少利润。

按部门划分的平均毛利

同样,我们可以看到一个与之前相似的标准。销售额较高的细分市场每一美元的成本回报都较低。加油站机动车和零部件经销商处于基线,每一美元产生 1318 美分的毛利。排在第一位的是家具和** 家居用品店,每一美元涉及 46 美分的费用。**

毛利率提供了一个公司盈利能力的大致线索,但不是一个精确的衡量标准。为了有一个更准确的数字,我们需要找到净利润率。为此,我们还需要将运营成本、税收、利息等其他费用纳入计算,以得出净收入,然后。****

净利润率=净收入/收入

该数据集仅带来费用文件,因此,将假设额外成本被收集到该文件中,以供进一步计算。

按部门划分的净利润率

上面的图表揭示了关于利润的更精确的数字。按照用于分析毛利率的相同标准,这些数字表达的是公司从销售退货中获得的每一美元中有多少是利润。

****加油站、电子电器商店机动车和零部件经销商的数字较低,将利润转化为低于 3.5% 。换句话说,每卖出一美元,就有不到 0.035 美分的利润。无店铺零售商杂货铺零售商的回报率最高,分别接近 15%12%

下面我们对每个细分市场的净利润率销售额进行了比较,结果显示,销售额最高的地区通常利润较低。

净利润率 x 销售额

更高的利润率是目标,因为这意味着公司从销售中获得更多。然而,来自领先位置细分市场的强劲销售并不一定转化为高利润率。我们需要找出这个百分比对应的金额。

****汽车和零部件经销商仅转化为利润 3.30% — 对应于平均销售额2830 万美元——而位于销售图表中间的无店铺零售商获得 14.55%,对应4950 万美元。非商店零售商胜出。

如果我们将机动车辆和零件经销商与净利润率第二高的杂货铺零售商进行比较,我们的、2830 万美元(3.30%)** 、1260 万美元、 (11.54%) 来自平均销售额。在这种情况下,机动车和零部件经销商转向最佳选择。**

到目前为止,无店铺零售商似乎是最好的选择,尽管它不是销售额最高的部门,但却有着最好的净利润率。

图片来自 Pixabay延斯·p·拉克

库存

库存是零售业中必须调查的另一个具有重大影响的关键领域。这意味着这是公司创收和盈利的来源之一。简单来说,就是指可供销售的商品和用于制造供销售的产品的原材料。

基本上,长期保持较高库存量对公司来说是不利的,除了过时之外,还会带来储存和损耗成本。然而,小的库存也会带来一些问题,比如由于库存不足而导致的潜在销售损失。

也就是说,我们需要使用以下公式,通过存货周转率来衡量一家公司在特定时期内销售和置换存货的次数

库存周转率=销售额/平均库存

按部门划分的库存周转率

一些消息来源称,零售的理想库存周转率在 4 到 6 之间,但是正如我们在上面看到的,这些数字会根据业务领域的不同而有所不同。事实上,这些数字只是指导搜索最佳结果的参考。

从上图可以看出,加油站的库存周转率较高,销售库存天数较少,每年替换库存 50.35 次,抛售库存 7.32 天。在这种情况下,我们可以说高销售需求和有限的库存空间可能是原因。然而,很少会在加油站停下来,面对由于燃料短缺而关闭的加油站。

考虑到参考值(4 和 6),我们可以说体育用品、业余爱好、书籍和音乐商店服装和配件商店机动车辆和零件经销商显示出最佳库存比率,正好在期望比率 4 和 6 之间。

最后,我们将分析毛利润投资回报(GMROI)** ,这是一个零售指标,用于确定一家公司(在本例中,是一个细分市场)是否从购买的产品中获得了足够的毛利润。**

由此,我们可以使用下面的公式计算出需要多少库存投资才能产生理想的毛利润。

GMROI =毛利/平均库存成本

投资毛利率回报

上面的图表带来了有趣的数字。我们可以说,GMROI 越高,投资于库存的每一美元就能赚更多的钱。从机动车辆和零件经销商部门开始,显示最低的 GMROI 为 1.04,这意味着该部门的收入为 104%,仅略高于成本。

分析加油站,GMROI 结果为 6.59 的细分市场返回了 656%的收入,因此销售商品的价格高于其购买成本。无店铺零售商紧随其后,销售额为 5.07 英镑,收益为 507%。基于这些结果,加油站非商店零售商对投资者来说似乎是不错的选择。

Adeolu EletuUnsplash 上拍摄的照片

结论

我们现在将总结一下到目前为止发现的关键见解。

- 由于大衰退(2008 年至 2009 年),美国零售贸易地区在 2004 年至 2014 年期间的年平均增长率约为 2.5%。在此之前,该部门的增长率接近 5%。自 2010 年开始复苏后,预计 2015 年该细分市场的增长率约为 3.5%。

- 汽车及零部件经销商部门的总销售额较高,同期为 94.4 亿美元。比第二高的卖家多 31%左右的销量。

- 汽车及零部件经销商深受危机影响,在危机最严重的时候下降了 14.53%,但也是复苏最强劲的,2010 年至 2014 年平均下降了 8.75%。非商店零售商也有类似的复苏(8.59%),但与汽车和零部件经销商相比,销售额减少了近 40%。****

-家具和 家居饰品店的销售成本(COGS)最低,不到费用的 49%。加油站机动车和零部件经销商的 COGS 最高,分别为 82%和 74%。

- 加油站、电子和家电商店以及汽车和零部件经销商的净利润率较低,为 3.5%,每销售一美元利润仅获得 0.035 美分的回报。非商店零售商和杂货铺零售商显示了最好的数字,分别转换了近 15%和 12%。

- 无店铺零售商的净利润率更高,即使销售额排名第五。该部分的回报率为 14.55%,相当于 4950 万美元。最高卖家机动车及零部件经销商,回报仅为 3.30%,回应为 2830 万美元。

-尽管拥有可观的库存周转率,加油站无店铺零售商都拥有最好的毛利率投资回报率(GMROI)达到 6.59 和 5.07,这意味着其收入分别为成本的 659%和 507%。

基于本文收集的所有信息,我们可以得出结论,无商店零售商细分市场是投资者的最佳选择。尽管不是销量最高的细分市场,但它在受大衰退影响较小的细分市场中排名第四,仅下降了-2.43%,在复苏中排名第二,平均增长 8.59%。

无店铺零售商也有最好的净利润率,回报率为 14.55%,或者说每卖出一美元赚 0.15 美分。此外,它的 GMROI 排名第二,收入是成本的 507%。

希望你喜欢这篇文章!谢谢大家!

这里 可以访问 用于分析的代码

探索悉尼的区域和场所

原文:https://towardsdatascience.com/exploring-areas-and-venues-of-sydney-nsw-australia-88c0cf4f3da2?source=collection_archive---------63-----------------------

使用 Foursquare API 和 K-means 对悉尼地区进行聚类。

乔希·威瑟斯在 Unsplash 上的照片

这篇文章对我的项目进行了概述。要查看用于完成分析的代码,请查看我的笔记本

1.介绍

新南威尔士州(NSW)是澳大利亚的主要州之一,拥有超过 810 万居民,是人口最多的州。新南威尔士州内有许多城镇,其首府悉尼是新南威尔士州近三分之二人口的家园。

该项目的目的是探索悉尼的不同地区,并根据每个区的顶级公共场所为新居民找到最佳位置。这篇文章将针对有意成为澳大利亚悉尼新南威尔士州居民的人,以及购房者、长期居留者和其他类似情况的人。其他有兴趣了解悉尼不同行政区的人也可以从中受益。

随着澳大利亚成为一个多元文化的国家,它的景观不断演变,看到不同的种族社区和场所的增加。将讨论悉尼区,以便利益相关者可以根据他们的兴趣选择最佳居住地点。

2.数据

将使用以下数据:

  1. 二级行政区划,澳大利亚,2015
  • 包含澳大利亚的二级行政和政治区划。这包括土著理事会、自治市、城市、政府和区理事会、市政当局、农村城市、郡和地区。导出的 JSON 文件中的相关信息包括州/地区、区/议会和每个分区的多个坐标。

2.four square API

  • 用于获取悉尼每个区的公共场所,如餐馆、商店和公园。

数据集(1)由 11 个州或地区和 1395 个行政区组成,随后被提取以仅获得与新南威尔士州相关的数据;这包括 199 个行政区。在探索新南威尔士州的数据时,没有快速的方法来查找悉尼地区新南威尔士州的行政区。相反,不在悉尼附近的行被手动删除,悉尼被指定为在悉尼的 80 公里半径内。

在发现每个位置的几何都是多点类型后,我使用了我的数据框的第一个坐标,后来发现它是不准确的。相反,我手动清理了数据框,删除了重复的行政区名称(如利物浦东、利物浦西),因为它们与其他行政区重叠,并使用谷歌地图/搜索获得了每个分区的正确坐标。最终的数据框现在由指定的悉尼地区的 38 个行政区组成。

3.方法学

在检索了我们指定的悉尼地区的行政区以及每个行政区的经度和纬度之后,我们现在将使用 Foursquare API 进行探索性分析,以找到我们附近的场馆。然后,我们将使用 K-means 聚类来创建具有相似特征的邻域聚类。

首先,使用地理库树叶库我们可以创建一个叠加了 38 个区的悉尼地图。

现在,我们使用 Foursquare API 从给定的坐标中发现附近的场地。我将半径定义为 1 公里,并提取了场馆的信息,如名称、类别和坐标。然后,我们可以将其合并到一个新的数据框中,其中包含社区和场馆信息。例如,在阿什菲尔德我们发现:

不幸的是,一些位置没有返回任何地点(Botany Bay,Warringah ),这可能是由于坐标的放置,因此它们被从分析中删除。总的来说,我们的行政区共有 143 个独特的类别和 890 个场馆。下图显示了返回的前 10 个场馆类别;然而,有些是不准确的,因为他们没有捕捉到至少 10 个不同的场馆。

在使用一个热点编码并对每个场馆类别的频率取平均值后,我们可以使用 K-means 聚类,这是一种无监督学习算法,用于根据特征相似性创建数据点的 K 聚类。观察肘方法和剪影评分,我们发现要使用的最佳聚类数是 K = 4

在运行 K-means 聚类之后,生成的聚类标签被添加到一个新的数据框中,该数据框是从我们的原始数据框及其对应的前 10 个场馆合并而来的。

想象一下每个集群中每个社区的第一个最常见的场地也是很棒的。这样,我们可以为我们的集群形成不同的标签。我创建了第一个最常见场馆的堆积条形图:

挺有意思的。通过分析条形图(以及排名前 10 的场馆),我们可以归纳出每个集群,并将其标记如下:

受塞尔詹·耶尔德兹项目的启发,我为每个区添加了前 2 个场馆,这样我们可以在创建新地图后查看该地区的更多信息。

我们可以合并新列,形成包含所有信息的最终数据框。

4.结果和讨论

最后,我们最终创建了显示集群信息的地图。

总共 36 个邻域的 K-均值聚类的结果描绘了 2 个主要聚类和 2 个单邻域聚类。通过检查集群,我们发现:

  • 集群 0(红色)-在这些位置可以找到大量的咖啡馆,其中有两个独特的位置(曼利和萨瑟兰郡,在这种情况下,克罗努拉)拥有海滩
  • 第 1 组(紫色)——各种社交区域、购物商店和餐馆/咖啡馆
  • 集群 2(青色)——被自然保护区包围(库-环-盖)
  • 聚类 3(浅黄色)-由公园和旧货店组成(Holroyd)

通过包含更多的社区/议会区域,这些集群可以更加独特,具有更多的特征。每个邻域坐标周围 1 公里的半径可能不足以获取该区域的信息。例如,在第 2 组和第 3 组的地点周围只发现了两个场馆。这可以通过选择更好的坐标来改善,这样更多的场馆将被包围。增加圆圈的半径也将捕获更多关于该区域内其他场馆的信息。

建议想去悉尼新南威尔士州旅游或居住的人多研究一下附近地区,看看这些地区是否适合他们的生活方式或文化。红点(聚类 0)大多更接近悉尼的中央商务区,该地区的特点是咖啡馆场地。紫色圆点(聚类 1)远离悉尼中央商务区,该区域以其各种美食场所为特色。通过更仔细的观察,一些地区可以通过特定菜系的餐馆数量来识别。例如,我们的分析涵盖了 Leichhardt 的意大利餐厅(7 家)、Marrickville 的越南餐厅(8 家)和 Strathfield 的韩国餐厅(15 家)。

运行笔来访问地图的交互式版本

通过分析不同社区的顶级场所,这导致探索许多以美食和咖啡馆为特色的区域。仅基于这一因素选择居住区域不是最佳选择,但可以帮助特定人群寻找社交聚会和美食的不同区域。

5.结论

希望成为居民和其他利益相关者的人可以根据他们的兴趣选择悉尼新南威尔士州的最佳位置,如餐厅的菜肴、咖啡馆的数量或靠近海滩和公园。然而,不建议这样做,因为在寻找一个可以参观或居住的地方时,会有很多因素在起作用。顶级场所的聚类对于那些希望在菜肴与该地区其他餐馆有所不同的地区开设餐馆的人来说可能是有用的。在这个项目中进行的分析展示了每个集群的一些独特的场地。

总的来说,为新居民寻找最佳地点的最终决定将由个人自己做出。除了发现社区是如何通过场馆数量来表征的,他们还应该考虑其他因素,如交通、房价和不同必需品的获取。

感谢阅读!

这是我的 IBM 数据科学专业证书的顶点项目的一部分。您可以在这里找到该分析中使用的代码。下一次,我想探索悉尼不同的美食,所以请保持警惕!

欢迎在 LinkedIn 上联系我,或者在 Medium 上关注我。我刚刚开始在这个网站上发帖,我将期待发布更多关于数据科学、统计和我的经历的内容。

使用 PipelineProfiler 探索 Auto-Sklearn 模型

原文:https://towardsdatascience.com/exploring-auto-sklearn-models-with-pipelineprofiler-5b2c54136044?source=collection_archive---------34-----------------------

如何打开 AutoML 的黑匣子

图片来源:负空格

构建机器学习管道通常是一项困难、耗时且反复试验的任务。AutoML 通过自动选择计算步骤、调整超参数和训练解决机器学习问题的端到端模型,使这一过程变得更容易。Auto-Sklearn 是最流行的开源 AutoML 系统之一。给定一个数据集、一个问题类型(分类或回归)和一个度量分数,Auto-Sklearn 能够产生集成管道来优化所选的度量并产生良好的结果。

数字数据集为例,我们希望在这里对数字图像进行分类。我们可以自动创建一个管道,用几行代码解决这个问题:

import sklearn.datasets
import autosklearn.classification
X, y = sklearn.datasets.load_digits(return_X_y=True)
automl = autosklearn.classification.AutoSklearnClassifier()
automl.fit(X, y, dataset_name='digits')

虽然这极大地方便了模型构建过程,但也使结果更加模糊:数据科学家不知道采取了什么步骤来产生管道,探索了搜索空间的什么部分,以及调整了什么超参数。

在这篇博文中,我们展示了如何使用 PipelineProfiler 来调试和探索 Auto-Sklearn 生成的模型。 PipelineProfiler 是一个新的库,旨在生成端到端机器学习管道的详细可视化。它与 Python 笔记本(Jupyter 和 Google Colab)集成在一起,可以在数据科学工作流中使用。

管道剖面仪工具

给定由 Auto-Sklearn 生成的训练模型,我们可以使用具有三行代码的 PipelineProfiler:

import PipelineProfiler
profiler_data = PipelineProfiler.import_autosklearn(automl)
PipelineProfiler.plot_pipeline_matrix(profiler_data)

图 1 PipelineProfiler 应用于 Digits 数据集。

图 1 显示了应用于 Digits 数据集的 PipelineProfiler 。该系统分为三个部分。1)原始贡献,显示原始使用与管道分数的相关性。2)流水线矩阵,呈现流水线、所使用的原语和超参数的概要。3)管道比较视图,突出显示所选管道之间的差异。

管道分析

图一。显示顶部两条管线使用 Nystroem 采样器LDA变压器,但是在级平衡采样器超参数的使用上有所不同。原始贡献向我们展示了 Nystroem 采样器和 LDA 与高准确度分数相关。反之,特征集聚与低分相关。

管道比较视图显示了所选管道的节点链接表示(按住键并点击可以选择多条管道)。图一。显示了三大管道之间的差异。为每个选定的管线指定一种颜色,并用颜色对图形节点进行编码,以表示原始管线。

PipelineProfiler 还显示被评估管道的性能指标。在图 1 中。,我们会看到所有管道的准确度分数。但是,我们可以将此视图切换到其他管道指标。例如,Auto-Sklearn 生成的集合由所有评估的管道的加权平均值组成。我们可以切换度量分数来显示总体权重。我们还可以可视化每个管道的训练时间。图二。显示了可以显示的多个指标。我们从这个图像中移除了原始贡献视图,因为对于每个选择的度量,贡献都是不同的。

图 2 PipelineProfiler 可用于检查 Auto-Sklearn 提供的指标,例如准确度、总体重量和时间。

我们注意到管线#9 和#19 不属于系综,因为它们的权重被设置为零。此外,我们看到管道#7 训练时间最长,管道#9 和#5 训练时间最快。我们还注意到,流水线#1 具有最好的精度,但是不具有最高的总体权重。这表明 Auto-Sklearn 使用其他标准将权重分配给集合中的管道。

如果我们对哪些原语导致了长训练时间感兴趣,我们可以研究原语贡献视图:

图三。基于时间度量计算的原始贡献。梯度提升是与流水线训练时间最相关的原语。

这里,我们看到梯度增强多项式是与高训练时间相关的两个原语,因此它们可能是罪魁祸首。直观地说,原始的多项式会导致更长的训练时间,因为它会从原始数据中生成大量的多项式特征。

最后的想法

这篇文章简要介绍了 PipelineProfiler ,这是一个 Python 库,可以用来研究 AutoML 模型。我们已经在 Jupyter Notebook 中看到了如何从 Auto-Sklearn 导入和可视化管道。

PipelineProfiler 是一款新工具,我们正在积极寻求关于我们应该支持哪些功能的反馈。如果您有任何问题或想要合作,请告诉我们!

安装和演示

该系统的现场演示可在 Google Colab 上获得。

该代码是在 BSD 3 许可下开源的。github 库是:https://github.com/VIDA-NYU/PipelineVis

PipelineProfiler 可从 PyPI 库中获得。它可以通过以下命令安装:

pip install pipelineprofiler

欲了解更多信息,请参见我们的论文并观看我们的视频演示:

[1]福雷尔,马蒂亚斯等.高效健壮的自动化机器学习 神经信息处理系统进展 2015。https://automl.github.io/auto-sklearn

[2] Ono,Jorge 等人,“PipelineProfiler:用于探测 AutoML 管道的可视化分析工具”。2020.https://arxiv.org/abs/2005.00160

探索 Azure Cosmos DB 中的内置 Jupyter 笔记本

原文:https://towardsdatascience.com/exploring-built-in-jupyter-notebooks-in-azure-cosmos-db-c9ffdb0273d5?source=collection_archive---------34-----------------------

快速介绍如何使用内置的 Jupyter 笔记本探索 Azure Cosmos DB 数据

Azure Cosmos DB Logo 来源Jupter 标志来源

【2019 年 5 月,Cosmos DB 团队公布了在 Cosmos DB 账户内运行 Jupyter 笔记本的预览。该功能于 2019 年 9 月公开发布,适用于所有的 Cosmos DB API。有了这个功能,我们可以使用 Jupyter 笔记本来运行交互式查询,探索我们的数据和可视化我们的数据。我们甚至可以在 Cosmos DB 中的数据上建立机器学习模型!

我能从中得到什么?

在我们的 Cosmos DB 帐户中拥有 Jupyter 笔记本电脑会给我们带来很多好处。其中包括:

  • 我们可以在我们的 Cosmos DB 数据上生成很酷的数据可视化。
  • 在我们的笔记本中更轻松地共享代码。有没有尝试过在 GitHub 中分享 Jupyter 笔记本代码中的代码?这是可行的,但是通过在 Cosmos DB 中使用笔记本,它更具交互性,我们可以在 Azure 门户中显示结果。
  • 我们的代码更具交互性,我们可以在笔记本中嵌入用户控件
  • 我们可以在一个文档中组合不同类型的文档、文本、图像、动画等。
  • 我们可以在笔记本上执行神奇的宇宙命令!稍后会详细介绍。

听起来很酷,怎么才能得到这个功能?

创建一个新的支持笔记本的 Azure Cosmos DB 帐户非常简单:

在 Azure 门户中,点击创建资源并选择 Azure Cosmos DB。创建 Azure Cosmos DB 帐户中,确保您选择了笔记本。点击审核+创建,然后点击创建。

创建帐户后,您将在数据浏览器窗格中找到您的笔记本工作区。

但是我已经有一个 Cosmos DB 帐户了!要使用笔记本电脑,我必须销毁现有的笔记本吗?

一点也不!您可以通过执行以下操作来启用笔记本功能:

进入你的 Cosmos DB 账户的数据浏览器面板,选择启用笔记本。点击完成设置,您的账户将可以使用笔记本!

好了,我都准备好了!给我演示一下!

现在我们的 Cosmos DB 帐户中已经有了笔记本,让我们开始使用它吧!我已经在我的 Cosmos DB 帐户中创建了一个容器,并按照这个演示使用数据填充它。(这个演示是由 Cosmos DB 工程团队提供的一系列演示,如果您想深入了解 Cosmos DB 的所有内容,我强烈推荐它!).

我们可以在我们的 Jupyter 笔记本上安装新的软件包,就像其他笔记本一样。让我们通过输入以下命令来安装 Pandas:

import pandas as pd

点击运行,很快,我们就可以在笔记本上使用熊猫了。现在让我们使用这些宇宙命令中的一个来创建一个我们可以使用的熊猫数据框架。我有一个名为 CustomCollection 的集合,它是由 /type 划分的。我想创建一个 DataFrame 来处理我的所有类型为 PurchaseFoodOrBeverage 的项目。我们可以通过执行以下命令来做到这一点:

%%sql --database EntertainmentDatabase --container CustomCollection --output df_foodOrBeverage
SELECT c.id, c.unitPrice, c.totalPrice, c.quantity, c.type FROM c WHERE c.type = "PurchaseFoodOrBeverage"

在这个查询中,我只选择了我们的 POCO 属性。SELECT *还将包括 Cosmos DB 系统生成的属性,所以我现在将它们排除在外。我们现在可以通过执行以下命令来查看我们的数据帧:

df_foodOrBeverage.head(10)

我们现在应该看到以下结果:

让我们通过可视化来结束这个快速演示。键入以下命令:

pd.options.display.html.table_schema = True
pd.options.display.max_rows = Nonedf_foodOrBeverage.groupby("quantity").size()

这将产生一个 nteract 数据浏览器。这使我们能够过滤和可视化我们的数据框架。我们只需将 table_schema 设置为 True,将 max_rows 设置为我们想要的值,或者设置为 None 以显示所有结果。

我们可以用笔记本做很多事情,比如使用内置的 Python SDK for Cosmos DB,并将 JSON 文件上传到特定的 Cosmos DB 容器。查看文档以了解我们可以在 Jupyter 笔记本上为 Cosmos DB 做的所有事情!

结论

希望你现在对在 Cosmos DB Jupyter 笔记本上可以做什么有了一个基本的概念。如果你有什么问题,欢迎在评论中提问!

使用 Python Scikit-learn-Iris 数据集探索分类器

原文:https://towardsdatascience.com/exploring-classifiers-with-python-scikit-learn-iris-dataset-2bcb490d2e1b?source=collection_archive---------3-----------------------

如何在 Python 中构建第一个分类器的分步指南。

凯文·卡斯特尔在 Unsplash 上的照片

一瞬间,想象你不是花卉专家(如果你是专家,那对你有好处!).你能区分三种不同的鸢尾吗——刚毛鸢尾、杂色鸢尾和海滨鸢尾?

鸣谢:基尚·马拉德卡(链接

我知道我不能…

但是,如果我们有一个包含这些物种实例的数据集,并对它们的萼片和花瓣进行测量,会怎么样呢?

换句话说,我们能从这个数据集中学到什么来帮助我们区分这三个物种吗?

古玩目录

  1. 我们为什么选择这个数据集?
  2. 我们试图回答什么问题?
  3. 我们能在这个数据集中找到什么?
  4. 我们正在构建哪些分类器?
  5. 接下来我们能做什么?

资料组

在这篇博文中,我将探索来自 UCI 机器学习知识库的虹膜数据集。摘自其网站,据说是“也许是 模式识别文献【1】中最知名的数据库 。此外,创建了机器学习大师社区的杰森·布朗利(Jason Brownlee)称之为机器学习的“Hello World”[2]。

我会把这个数据集推荐给任何一个初学数据科学并渴望构建他们的第一个 ML 模型的人。以下是该数据集的一些良好特征:

  1. 150 个样本,4 个属性(相同单位,全是数字)
  2. 均衡的类别分布(每个类别 50 个样本)
  3. 没有丢失数据

正如您所看到的,这些特征有助于最大限度地减少您在数据准备过程中需要花费的时间,以便您可以专注于构建 ML 模型。并不是说准备阶段不重要。相反,这个过程非常重要,对于一些初学者来说,它可能太费时间了,以至于他们可能在进入模型开发阶段之前就不知所措了。

例如,来自 Kaggle 的流行数据集House Prices:Advanced Regression Techniques有大约 80 个特征,其中超过 20%包含某种程度的缺失数据。在这种情况下,您可能需要花一些时间来理解属性并输入缺失的值。

现在希望你的信心水平(没有统计双关语)相对较高。这里有一些关于数据争论的资源,你可以在处理更复杂的数据集和任务时通读一下:降维不平衡分类特征工程插补

目标

探索该数据集后,我们希望能够回答两个问题,这两个问题在大多数分类问题中非常典型:

  1. 预测 —给定新的数据点,模型预测其类别(物种)的准确度如何?
  2. 推断 —哪个(些)预测器能有效地帮助预测?

简单说说分类

分类是一种类型的 监督机器学习 问题,其中目标(响应)变量是分类的。给定包含已知标签的训练数据,分类器逼近从输入变量(X)到输出变量(Y)的映射函数(f)。有关分类的更多资料,请参见统计学习介绍吴恩达的机器学习课程(第 3 周)和 Simplilearn 的分类教程中的第 3 章。

Florian OlivoUnsplash 上拍摄的照片

现在是时候写一些代码了!见我的 Github 页面获取我的完整 Python 代码(写在 Jupyter 笔记本上)。

导入库并加载数据集

首先需要导入一些库: pandas (加载数据集) numpy (矩阵操作) matplotlibseaborn (可视化) sklearn (构建分类器)。确保在导入它们之前已经安装了它们(安装包指南此处为)。

import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from pandas.plotting import parallel_coordinates
from sklearn.tree import DecisionTreeClassifier, plot_tree
from sklearn import metrics
from sklearn.naive_bayes import GaussianNB
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis, QuadraticDiscriminantAnalysis
from sklearn.neighbors import KNeighborsClassifier
from sklearn.svm import SVC
from sklearn.linear_model import LogisticRegression

要加载数据集,我们可以使用 pandas 的 read_csv 函数(我的代码还包括通过 url 加载的选项)。

data = pd.read_csv('data.csv')

加载完数据后,我们可以通过 head 函数查看前几行:

data.head(5)

注意:所有四个测量单位都是厘米。

数字汇总

首先,让我们通过描述:来看看每个属性的数字摘要

data.describe()

我们还可以使用 groupbysize 检查等级分布:

data.groupby('species').size()

我们可以看到每个类都有相同数量的实例。

列车测试分离

现在,我们可以将数据集分为训练集和测试集。一般来说,我们还应该有一个验证集,用于评估每个分类器的性能,并微调模型参数,以便确定最佳模型。测试集主要用于报告目的。然而,由于这个数据集很小,我们可以通过使用测试集来服务于验证集的目的,从而简化这个过程。

此外,我使用分层的拒绝方法来估计模型的准确性。另一种方法是做交叉验证来减少偏差和方差。

train, test = train_test_split(data, test_size = 0.4, stratify = data[‘species’], random_state = 42)

注意:一般的经验法则是将数据集的 20–30%作为测试集。由于这个数据集很小,我选择了 40%来确保有足够的数据点来测试模型性能。

探索性数据分析

分割数据集后,我们可以继续研究训练数据。无论是 matplotlib 还是 seaborn 都有很棒的绘图工具,我们可以用来进行可视化。

让我们首先通过每个特征的直方图创建一些单变量图:

n_bins = 10
fig, axs = plt.subplots(2, 2)
axs[0,0].hist(train['sepal_length'], bins = n_bins);
axs[0,0].set_title('Sepal Length');
axs[0,1].hist(train['sepal_width'], bins = n_bins);
axs[0,1].set_title('Sepal Width');
axs[1,0].hist(train['petal_length'], bins = n_bins);
axs[1,0].set_title('Petal Length');
axs[1,1].hist(train['petal_width'], bins = n_bins);
axs[1,1].set_title('Petal Width');# add some spacing between subplots
fig.tight_layout(pad=1.0);

四个特征的直方图

请注意,对于花瓣长度和花瓣宽度,似乎有一组数据点的值比其他数据点的值小,这表明该数据中可能有不同的组。

接下来,让我们尝试一些并排的方框图:

fig, axs = plt.subplots(2, 2)
fn = ["sepal_length", "sepal_width", "petal_length", "petal_width"]
cn = ['setosa', 'versicolor', 'virginica']
sns.boxplot(x = 'species', y = 'sepal_length', data = train, order = cn, ax = axs[0,0]);
sns.boxplot(x = 'species', y = 'sepal_width', data = train, order = cn, ax = axs[0,1]);
sns.boxplot(x = 'species', y = 'petal_length', data = train, order = cn, ax = axs[1,0]);
sns.boxplot(x = 'species', y = 'petal_width', data = train,  order = cn, ax = axs[1,1]);
# add some spacing between subplots
fig.tight_layout(pad=1.0);

并排箱形图

底部的两个图表明我们之前看到的那组数据点是setas。它们的花瓣尺寸比其他两种更小,也更不展开。比较其他两个物种,云芝的平均价值低于海滨锦鸡儿。

Violin plot 是另一种类型的可视化,它结合了直方图和箱线图的优点:

sns.violinplot(x="species", y="petal_length", data=train, size=5, order = cn, palette = 'colorblind');

花瓣长度的小提琴图

现在,我们可以通过使用 seaborn 的 pairplot 函数来制作所有成对属性的散点图:

sns.pairplot(train, hue="species", height = 2, palette = 'colorblind');

全配对属性散点图

注意,一些变量似乎是高度相关的,例如,花瓣长度和花瓣宽度。此外,花瓣的测量方法比萼片的测量方法更能区分不同的物种。

接下来,我们制作一个相关矩阵来定量考察变量之间的关系:

corrmat = train.corr()
sns.heatmap(corrmat, annot = True, square = True);

相关矩阵

主要的收获是花瓣测量值具有高度正相关,而萼片测量值不相关。注意花瓣特征与萼片长度也有相对较高的相关性,但与萼片宽度没有相关性。

另一个很酷的可视化工具是平行坐标图,它将每个样本表示为一条线。

parallel_coordinates(train, "species", color = ['blue', 'red', 'green']);

平行坐标图

正如我们之前看到的,花瓣测量比萼片测量更能区分物种。

构建分类器

现在我们准备建立一些分类器(呜-呼!)

为了使我们的生活更容易,让我们先把类标签和特性分离出来:

X_train = train[['sepal_length','sepal_width','petal_length','petal_width']]
y_train = train.species
X_test = test[['sepal_length','sepal_width','petal_length','petal_width']]
y_test = test.species

分类树

我想到的第一个分类器是一个被称为分类树的区别性分类模型(在这里阅读更多)。原因是我们可以看到分类规则,并且很容易解释。

让我们使用 sklearn ( 文档)构建一个,最大深度为 3,我们可以在测试数据上检查它的准确性:

mod_dt = DecisionTreeClassifier(max_depth = 3, random_state = 1)
mod_dt.fit(X_train,y_train)
prediction=mod_dt.predict(X_test)
print(‘The accuracy of the Decision Tree is’,”{:.3f}”.format(metrics.accuracy_score(prediction,y_test)))--------------------------------------------------------------------
The accuracy of the Decision Tree is 0.983.

该决策树正确预测了 98.3%的测试数据。该模型的一个优点是,您可以通过其 feature_importances_ 属性看到每个预测值的重要性:

mod_dt.feature_importances_--------------------------------------------------------------------
array([0\.        , 0\.        , 0.42430866, 0.57569134])

从输出和基于四个特征的索引,我们知道前两个特征(萼片测量)不重要,只有花瓣特征用于构建该树。

决策树的另一个好处是我们可以通过 plot_tree 可视化分类规则:

plt.figure(figsize = (10,8))
plot_tree(mod_dt, feature_names = fn, class_names = cn, filled = True);

此树中的分类规则(对于每个拆分,左->是,右->否)

除了每个规则之外(例如,第一个标准是花瓣宽度≤ 0.7),我们还可以看到在每个分割、指定类别等处的基尼指数(杂质测量)。注意,除了底部的两个“浅紫色”方框外,所有的终端节点都是纯的。对于这两类情况,我们可以不那么自信。

为了演示对新数据点进行分类是多么容易,假设一个新实例的花瓣长度为 4.5 厘米,花瓣宽度为 1.5 厘米,那么我们可以根据规则预测它是杂色的。

由于只使用了花瓣特征,我们可以可视化决策边界并绘制 2D 的测试数据:

在 60 个数据点中,59 个数据点被正确分类。显示预测结果的另一种方式是通过混淆矩阵:

disp = metrics.plot_confusion_matrix(mod_dt, X_test, y_test,
                                 display_labels=cn,
                                 cmap=plt.cm.Blues,
                                 normalize=None)
disp.ax_.set_title('Decision Tree Confusion matrix, without normalization');

通过这个矩阵,我们看到有一个我们预测是弗吉尼亚的云芝。

一个缺点是建造一个单独的树是它的 不稳定 ,这可以通过集合技术来改善,如随机森林,助推等。现在,让我们继续下一个模型。

高斯朴素贝叶斯分类器

最流行的分类模型之一是朴素贝叶斯。它包含单词“Naive ”,因为它有一个关键的假设类-条件独立性,这意味着给定类,每个特性的值被假设为独立于任何其他特性的值(在这里阅读更多)。

我们知道,显然不是这种情况,花瓣特征之间的高度相关性证明了这一点。让我们用这个模型来检验测试的准确性,看看这个假设是否可靠:

The accuracy of the Guassian Naive Bayes Classifier on test data is 0.933

如果我们只使用花瓣特征,结果会怎样:

The accuracy of the Guassian Naive Bayes Classifier with 2 predictors on test data is 0.950

有趣的是,仅使用两个特征会导致更正确的分类点,这表明在使用所有特征时可能会过度拟合。看来我们的朴素贝叶斯分类器做得不错。

线性判别分析

如果我们使用多变量高斯分布来计算类别条件密度,而不是采用单变量高斯分布的乘积(在朴素贝叶斯中使用),那么我们将得到 LDA 模型(在此阅读更多)。LDA 的关键假设是类之间的协方差相等。我们可以使用所有特征并且仅使用花瓣特征来检查测试准确性:

The accuracy of the LDA Classifier on test data is 0.983
The accuracy of the LDA Classifier with two predictors on test data is 0.933

使用所有特征提高了我们的 LDA 模型的测试精度。

为了可视化 2D 的决策边界,我们可以使用只有花瓣的 LDA 模型,并绘制测试数据:

四个测试点被错误分类——三个 virginica 和一个 versicolor。

现在假设我们想用这个模型对新的数据点进行分类,我们可以在这个图上画出这个点,并根据它所属的彩色区域进行预测。

二次判别分析(QDA)

LDA 和 QDA 的区别在于,QDA 并不假设协方差在所有类中都是相等的,它被称为“二次的”,因为决策边界是二次函数。

The accuracy of the QDA Classifier is 0.983
The accuracy of the QDA Classifier with two predictors is 0.967

在所有特征的情况下,它与 LDA 具有相同的准确性,在仅使用花瓣时,它的性能略好。

同样,让我们为 QDA(只有花瓣的模型)绘制决策边界:

K 个最近邻居(K-NN)

现在,让我们稍微转换一下话题,看看一个名为 KNN 的非参数生成模型(点击阅读更多)。这是一个流行的模型,因为它相对简单且易于实现。然而,当特征数量变大时,我们需要注意维度的诅咒

让我们画出不同 K 选择的测试精度图:

我们可以看到,当 K 为 3,或者介于 7 和 10 之间时,精度最高(约为 0.965)。与之前的模型相比,对新数据点进行分类不太直接,因为我们需要查看四维空间中它的 K 个最近邻居。

其他型号

我还探索了其他模型,如逻辑回归,支持向量机分类器等。详见我在 Github 上的代码。

请注意, SVC(带线性内核)实现了 100%的测试准确率!

我们现在应该很有信心,因为我们的大多数模型的准确率都超过了 95%。

接下来的步骤

以下是对未来研究的一些想法:

  1. 创建一个验证集并运行交叉验证,以获得准确的估计值,并比较它们之间的差值和平均准确度。
  2. 找到包括其他鸢尾属物种及其萼片/花瓣测量值的其他数据源(如果可能,也包括其他属性),并检查新的分类准确性。
  3. 制作一个交互式网络应用程序,根据用户输入的测量值预测物种(查看我的简单网络演示,Heroku 部署此处

摘要

让我们回顾一下。

我们研究了虹膜数据集,然后使用 sklearn 构建了一些流行的分类器。我们看到花瓣的测量比萼片的测量更有助于实例的分类。此外,大多数模型的测试准确率都达到了 95%以上。

我希望你喜欢这篇博文,并请分享你的任何想法:)

查看我关于探索 Yelp 数据集的另一篇文章:

[## 发现你下一个最喜欢的餐馆 Yelp 数据集上的探索和可视化

你用 Yelp 找好餐馆吗?这篇文章揭示了流行的 Yelp 数据集中的见解和模式。

towardsdatascience.com](/discover-your-next-favorite-restaurant-exploration-and-visualization-on-yelps-dataset-157d9799123c)

参考

[1]https://archive.ics.uci.edu/ml/datasets/iris
【2】https://machinelingmastery . com/machine-learning-in-python-step-by-step/

探索张量板上混淆矩阵的演化

原文:https://towardsdatascience.com/exploring-confusion-matrix-evolution-on-tensorboard-e66b39f4ac12?source=collection_archive---------15-----------------------

训练卷积神经网络对来自数据集的图像进行分类,并使用 TensorBoard 来探索其混淆矩阵如何演变。

张量板上的混淆矩阵演化

Tensorboard 是在训练和验证神经网络时可视化许多指标的最佳工具。在大多数情况下,我们需要寻找更多的细节,比如模型如何处理验证数据。有时训练和验证损失和准确性是不够的,我们需要弄清楚验证数据的性能。一种方法是使用混淆矩阵来可视化。

混淆矩阵

机器学习领域,特别是统计分类问题中,混淆矩阵,也被称为误差矩阵,是一种特定的表格布局,允许算法性能的可视化,通常是监督学习算法(在非监督学习中,它通常被称为匹配矩阵)。矩阵的每一行代表预测类中的实例,而每一列代表实际类中的实例(反之亦然)。该名称源于这样一个事实,即它可以很容易地看出系统是否混淆了两个类(即通常将一个类误标为另一个类)。

让我们建立一个混淆矩阵

我不会深入研究编码,我将只强调重要的代码部分,展示如何用 python 在 tensorboard 中设置和实现自定义回调。如果你想查看完整的代码,你可以查看我的知识库,我在这个故事的底部添加了一个链接。

我假设您已经构建并编译了一个 Keras 序列模型。

定义绘制 cm 的函数

def plot_confusion_matrix(cm, class_names):
    """
    Returns a matplotlib figure containing the plotted confusion matrix.

    Args:
       cm (array, shape = [n, n]): a confusion matrix of integer classes
       class_names (array, shape = [n]): String names of the integer classes
    """

    figure = plt.figure(figsize=(8, 8))
    plt.imshow(cm, interpolation='nearest', cmap=plt.cm.Blues)
    plt.title("Confusion matrix")
    plt.colorbar()
    tick_marks = np.arange(len(class_names))
    plt.xticks(tick_marks, class_names, rotation=45)
    plt.yticks(tick_marks, class_names)

    # Normalize the confusion matrix.
    cm = np.around(cm.astype('float') / cm.sum(axis=1)[:, np.newaxis], decimals=2)

    # Use white text if squares are dark; otherwise black.
    threshold = cm.max() / 2.

    for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
        color = "white" if cm[i, j] > threshold else "black"
        plt.text(j, i, cm[i, j], horizontalalignment="center", color=color)

    plt.tight_layout()
    plt.ylabel('True label')
    plt.xlabel('Predicted label')
    return figure

设置 tensorboard 回调

我们现在准备训练 CNN,并在此过程中定期记录混淆矩阵。使用下面的代码,您将创建一个 Keras TensorBoard 回调来记录基本指标。

logdir = "logs/image/" + datetime.now().strftime("%Y%m%d-%H%M%S")tensorboard_callback = keras.callbacks.TensorBoard(log_dir = logdir, histogram_freq = 1)file_writer_cm = tf.summary.create_file_writer(logdir + '/cm')

将 Matplotlib 图形转换为 PNG

遗憾的是,Matplotlib 文件格式不能作为图像记录,但 PNG 文件格式可以记录。因此,我们将创建一个 helper 函数,它接受一个 Matplotlib 图形,并将其转换为 PNG 格式,以便可以编写。

def plot_to_image(figure):
    """
    Converts the matplotlib plot specified by 'figure' to a PNG image and
    returns it. The supplied figure is closed and inaccessible after this call.
    """

    buf = io.BytesIO()

    # Use plt.savefig to save the plot to a PNG in memory.
    plt.savefig(buf, format='png')

    # Closing the figure prevents it from being displayed directly inside
    # the notebook.
    plt.close(figure)
    buf.seek(0)

    # Use tf.image.decode_png to convert the PNG buffer
    # to a TF image. Make sure you use 4 channels.
    image = tf.image.decode_png(buf.getvalue(), channels=4)

    # Use tf.expand_dims to add the batch dimension
    image = tf.expand_dims(image, 0)

    return image

计算混淆矩阵

我们将定义一个计算混淆矩阵的函数。

def log_confusion_matrix(epoch, logs):

    # Use the model to predict the values from the test_images.
    test_pred_raw = model.predict(test_images)

    test_pred = np.argmax(test_pred_raw, axis=1)

    # Calculate the confusion matrix using sklearn.metrics
    cm = sklearn.metrics.confusion_matrix(test_labels, test_pred)

    figure = plot_confusion_matrix(cm, class_names=class_names)
    cm_image = plot_to_image(figure)

    # Log the confusion matrix as an image summary.
    with file_writer_cm.as_default():
        tf.summary.image("Confusion Matrix", cm_image, step=epoch)

设置张量板以记录混淆矩阵

我们将在纪元结束时设置 tensorboard 回调来记录混淆矩阵

cm_callback = keras.callbacks.LambdaCallback(on_epoch_end=log_confusion_matrix)

开始冲浪板和训练

我们需要将值指定为 model.fit 中回调参数的列表,以指定 Keras 在训练时使用我们的自定义回调函数。

# Start TensorBoard.
%tensorboard --logdir logs/image# Train the classifier.
model.fit(train_images,
          train_labels,
          epochs=5,
          verbose=0, # Suppress chatty output
          callbacks=[tensorboard_callback, cm_callback],
          validation_data=(test_images, test_labels))

默认情况下,tensorboard 服务器运行在端口 6006 上,如果您想要指定任何其他端口,您需要将该端口指定为 tensorboard 命令的参数。
Jupyter 用户只需在第一个单元格中键入%load_ext tensorboard并在导入库之前运行,这将在 Jupyter 笔记本中加载 tensorboard。

让我们看看我们的困惑矩阵

想试试自己吗

[## novas ush/tensor board-时尚 MNIST

训练一个卷积神经网络来分类时尚 MNIST 数据集的图像,并使用 TensorBoard 来探索…

github.com](https://github.com/novasush/Tensorboard-with-Fashion-MNIST)

参考我的 Github 回购链接。我在时尚 Mnist 数据集上训练了一个 CNN 分类器,并设置了一个混淆矩阵。以上输出来自我的 tensorboard 服务器。

额外资源

  1. 深度潜入张量板:教程附实例https://neptune.ai/blog/tensorboard-tutorial

探索冠状病毒研究出版物

原文:https://towardsdatascience.com/exploring-covid-19-research-publications-407f8c2aa842?source=collection_archive---------25-----------------------

执行探索性数据分析

来源

在冠状病毒爆发后,许多数据来源已向公众开放,以鼓励该领域的研究。最近,白宫和一群领先的研究人员公布了新冠肺炎开放研究数据集(CORD-19),,该数据集可在 Kaggle 上获得。我们的目标是让全球研究界应用机器学习和自然语言处理的最新进展,以获得对传染病的深入了解。

与大多数文本挖掘工作一样,生成汇总统计数据并直观地表示文本信息的内容是探索性数据分析的一个非常重要的部分。

在本帖中,我们将对新冠肺炎开放研究数据集(CORD-19)进行探索性数据分析。我们将使用 python 库,如‘seaborn’,‘matplotlib’和 Pandas 来探索和可视化数据集中提供的分类和文本信息。我们将使用的特定文件名为“all _ sources _ metadata _ 2020–03–13 . CSV”。

我们开始吧!

读取数据

首先,让我们将数据读入 Pandas 数据框:

import pandas pd 
df = pd.read_csv(“all_sources_metadata_2020–03–13.csv”)

接下来,让我们打印前五行数据。让我们放松对显示的列数的限制(这里输出被抑制):

pd.set_option('display.max_columns', None)
print(df.head())

我们可以从很多地方开始分析。最简单的第一步是查看列的列表及其长度:

print("List of columns:")
print(list(df.columns))
print("Length of columns:", len(df.columns))

我们还可以查看数据集中的行数:

print("Number of rows: len(df))

接下来,我们可以查看所有出版物在期刊中的出现频率。我们可以使用 collections 模块中的 Counter 方法来实现这一点(这里输出也被抑制):

from collections import Counter
print(Counter(df['journal']))

我们可以看到,这是相当期刊名称和频率的列表。我们可以通过删除缺失值(' nan ')并将输出限制为最常见的 10 个日志来缩小范围:

df['journal'].dropna(inplace = True)
print(Counter(df['journal']).most_common(10))

然后,我们可以使用“matplotlib”和“seaborn”来可视化最常见的期刊出版物。让我们对五种最常见的出版物也这样做:

import matplotlib.pyplot as plt
import seaborn as snssns.set()
bar_plot = dict(Counter(df['journal'].values).most_common(5))
plt.bar(*zip(*bar_plot.items()))
plt.show()

同样值得注意的是,如果我们看一下期刊的总数:

print(len(set(df['journal'])))

该数据集中有 1732 个日志。

出于好奇,我查找了这些期刊的影响因子。对于那些不知道的人来说,期刊影响因子是一个衡量期刊被引用频率的指标。越有声望或越难在期刊上发表的文章通常有更高的影响因子。最常见的五种期刊在 2019 年有以下影响因素:

  1. PLoS one: 2.8
  2. 急诊传染病:7.4
  3. 科学报告:4.1
  4. 公共科学图书馆 Pathog: 6.2
  5. 病毒:3.8

由于新兴传染病 (Emerg Infect Dis)的影响因子最高,发表数量第二高,因此分析该期刊发表的论文的文本内容可能会很有意思。

另一个有趣的项目是找出每种期刊的影响因子,并用影响因子信息扩充现有数据。用这些信息扩充我们的数据将允许我们过滤和搜索高影响力的新冠肺炎研究出版物。为了找到具有最高影响因子和相对出版物数量的期刊,我们需要将 1732 种期刊映射到影响因子值。

现在,让我们假设新兴传染病是一个有大量与新冠肺炎相关的出版物的好杂志。加强了这个假设,,一个期刊排名网站,把新兴传染病排在他们传染病期刊名单的第八位,这是相当不错的。

过滤数据

接下来,让我们过滤我们的数据,仅包括来自新发传染病的数据,并打印数据帧长度以供验证:

df = df[df.journal == 'Emerg Infect Dis']
print(len(df))

我们看到数据帧长度为 941。

现在我们来看一些摘要。让我们先来看看前 5 篇摘要,以便对文中的内容有所了解:

print(list(df['abstract'].values)[0])
print('-'*170)
print(list(df['abstract'].values)[1])
print('-'*170)
print(list(df['abstract'].values)[2])
print('-'*170)
print(list(df['abstract'].values)[3])
print('-'*170)
print(list(df['abstract'].values)[4])
print('-'*170)

情感分析

接下来我们要看的是摘要的情感分数。也许我们可以给每个摘要分配情感分数,看看分数是否与积极的结果相关联。

为了获得情感分数,我们需要导入一个名为 textblob 的 python 包。textblob 的文档可以在这里找到。要安装 textblob,请打开命令行并键入:

pip install textblob

下次导入 textblob:

from textblob import TextBlob

我们将使用极性得分作为积极或消极情绪的衡量标准。极性得分是一个从-1 到+1 的浮点数。

例如,让我们考虑一下 1991 年关于癌症药物紫杉醇的出版物, 紫杉醇:一种新的有效的抗癌药物 。摘要中有一句肯定的情感句子:

临床试验表明,紫杉醇对治疗难治性卵巢癌、乳腺癌、恶性黑色素瘤和其他可能的实体瘤有效。

如果我们定义一个 textblob 对象并传入上面的句子,我们有:

abstract_sentence = "Clinical trials indicate that taxol is effective in the treatment of patients with refractory ovarian cancer, breast cancer, malignant melanoma and probably other solid tumors."sentiment_score = TextBlob(abstract_sentence).sentiment.polarity
print("Sentiment Polarity Score:", sentiment_score)

如果我们把句子中的有效改为无效:

“临床试验表明,紫杉醇在治疗难治性卵巢癌、乳腺癌、恶性黑色素瘤以及其他可能的实体瘤患者时无效。”

并将其传递给我们的 textblob 对象:

abstract_sentence = "Clinical trials indicate that taxol is ineffective in the treatment of patients with refractory ovarian cancer, breast cancer, malignant melanoma and probably other solid tumors."sentiment_score = TextBlob(abstract_sentence).sentiment.polarity
print("Sentiment Polarity Score:", sentiment_score)

我们看到 textblob 能够捕捉科学陈述中的消极和积极情绪。让我们将此应用于新冠肺炎数据的摘要:

df['abstract'] = df['abstract'].astype(str)
df['sentiment'] = df['abstract'].apply(lambda abstract: TextBlob(abstract).sentiment.polarity)

让我们限制数据框中的列,使其包括“抽象”和“情感”,并打印前五行:

df = df[['abstract', 'sentiment']]
print(df.head())

我们也可以计算积极和消极情绪的数量:

df_pos = df[df['sentiment'] > 0.0]
df_neg = df[df['sentiment'] < 0.0]
print("Number of Positive Results", len(df_pos))
print("Number of Negative Result", len(df_neg))

为了安全起见,让我们将积极情绪的极性阈值提高到> 0.5,并打印一些积极情绪摘要:

df_pos = df[df['sentiment'] > 0.5]
print(list(df_pos['abstract'].values)[0])
print('-'*170)
print(list(df_pos['abstract'].values)[1])
print('-'*170)
print(list(df_pos['abstract'].values)[2])
print('-'*170)
print(list(df_pos['abstract'].values)[3])
print('-'*170)
print(list(df_pos['abstract'].values)[4])
print('-'*170)

我们看到 textblob 在给摘要分配积极的分数方面做得很好。让我们对负面情绪摘要做同样的事情,我们将把负面情绪阈值降低到-0.1:

df_neg = df[df['sentiment'] < -0.1]
print(list(df_neg['abstract'].values)[0])
print('-'*170)
print(list(df_neg['abstract'].values)[1])
print('-'*170)
print(list(df_neg['abstract'].values)[2])
print('-'*170)
print(list(df_neg['abstract'].values)[3])
print('-'*170)
print(list(df_neg['abstract'].values)[4])
print('-'*170)

这些结果不太确定。理想情况下,我们希望带有负面情绪的摘要包含如下陈述:

“没有证据表明会横向传染给其他猫,因为只有两只猫产生了抗 H5N1 病毒的抗体。”

将这种情感评分方法应用于所有期刊并比较期刊之间的消极和积极情感将是有趣的。

我就说到这里,但请放心地继续研究这些数据。可能值得研究其他文本分类方法,如 BERT,它可用于对报告负面和正面结果的期刊进行分类。

结论

总之,在本文中,我们对新冠肺炎开放研究数据集(CORD-19) 进行了简单的探索性分析。我们生成期刊数量的频率计数,可视化最常见的期刊,过滤我们的数据以将我们的分析限制在具有大量出版物的高影响力期刊,并对期刊摘要执行情感分析。我希望你觉得这篇文章有用/有趣。这篇文章中的代码可以在 GitHub 上找到。感谢您的阅读!

探索 DenseNets:从纸到 Keras

原文:https://towardsdatascience.com/exploring-densenets-from-paper-to-keras-dcc01725488b?source=collection_archive---------17-----------------------

拆开 DenseNets 看看里面有什么“密”!

照片由阿丽娜·格鲁布尼亚Unsplash 上拍摄

DenseNets 广泛用于图像分类任务、分割、图像重建等。如果你是一个经验丰富的 TF 开发者,你可能会在tf.keras.applications模块中看到它们。什么是 DenseNets?它们里面真的有的东西吗?来吧,让我们一起探索!

您可以在本笔记本中查看 TF 的实现,

[## 谷歌联合实验室

编辑描述

colab.research.google.com](https://colab.research.google.com/drive/1v2p228o-_PRtecU0vYUXuGlG_VierqcP#scrollTo=wbkXMkrTgXiF&forceEdit=true&sandboxMode=true)

致密网应该是致密的。对吗?

绝对的,DenseNets 是密集连接的卷积神经网络。我们将通过比较普通的 CNN(你会在大多数在线博客中看到)和密集块(DenseNet 的构建块)来理解这一点。).

矩形代表卷积层,红线表示连接或信息流。

常规 CNN(左)和密集区块(右)。

注意上面两个网络中的卷积层 3。在第一网络中,第三层接收来自前一层即第二层的信息。这似乎很正常。

看一看第二网络。这就是密集块的作用。第二网络中的第三卷积层接收来自所有先前层的信息。它接收 3 个输入,

  • 红色箭头来自图像。这是第一层的输入。
  • 另一个红色箭头来自前一层(第二层)。
  • 来自第一层的黑色箭头。这是第一层的输出。

看,我们得到了所有先前层(第 1 层和第 2 层)的输出以及输入。这就是我们普通 CNN 和密块的区别。密集块中的每一层都与该块中的每一个后续层相连。一个只是一组具有密集连接的层的别称。

除了密集块,我们还有所谓的过渡层。它们基本上对特征地图进行下采样。这些层执行 1 × 1 卷积和 2 × 2 平均合并。

每一层都从所有前面的层获得输入。听起来是不是很疯狂?

这个主意被证明是一个绝妙的诡计。在我们传统的 CNN 中,每一层只知道从前一层接收的特征地图。较差的卷积层对网络的输入一无所知。它只学习以前的输入。

另一方面,密集块中的每一层都从所有层接收输入或信息。简而言之,在全球范围内,整个网络都可以获得这些信息。这一点甚至在 DenseNets 的惊人论文中也有提及,

“除了更好的参数效率,DenseNets 的一大优势是改善了整个网络的信息流和梯度,这使得它们易于训练。”

而且,

“为了进一步改善各层之间的信息流,我们提出了一种不同的连接模式:我们引入了从任何一层到所有后续层的直接连接。”

我们也可能包括瓶颈层来提高计算效率。由于许多输入的串联,我们有大量的特征图,这可能会减慢训练。因此,我们对输入执行 1 × 1 卷积来解决这个问题。

解决上述问题的另一种方法是使用压缩率。它从区间 ( 0,1】中选择,并决定过渡层将产生的特征图的数量。如果θ是压缩因子并且 m 是输入特征图的数量,则过渡层产生由下式给出的 p 特征图,

数学

我们确实可以用一些数学来理解稠密连通性的概念!别担心,这很容易理解。考虑一个输入图像 x₀ 给我们的密集块。

H 是一个复合函数,由三个运算组成:批量归一化→ ReLU →卷积

我们将 H()定义为三个连续操作的复合函数:批量归一化(BN),然后是校正线性单元(ReLU)和 3×3 卷积(Conv)。

第一层(x₁)的输出将由,

1.第一层的输出。

一般对于常规 CNN(非 DenseNets),l层的输出将由第l-1层的输入产生。

(1)的一般表达式。

对于一个 DenseNet ,正如我们之前讨论的,一个层从前面的层接收输入。它看起来会像,

方括号表示 x₀,x₁,…,xₗ₋₁的连接

这是 DenseBlock 的数学表达式。

在 TensorFlow 里看起来怎么样?(请用 Keras!)

我们都在急切地等待这个,对吗?我会坚持让你跟着 Colab 笔记本去看看 DenseNets 的实现。首先,我们实现前面讨论过的 H 函数。

然后是过渡层

现在,实现了密集块。我们将调用H()方法,将之前输出的conv_outputsinputs连接起来。这将会循环发生,

我们现在将所有的密集块组装在一起,并用过渡层将它们连接起来,

最后,我们做了一个漂亮的tf.keras.models.Model()

培训和评估包含在 Colab 笔记本中。恭喜你,你已经从零开始实现了一个 DenseNet!

更多关于 ML 的资源和博客

就这样

DenseNets 的想法令人印象深刻。请确保您也阅读了 ResNets。浏览报纸,你会了解更多关于训练的细节。感谢阅读。

探索 Python 中的设计模式

原文:https://towardsdatascience.com/exploring-design-patterns-in-python-be55fbcf8b34?source=collection_archive---------8-----------------------

乔治·帕甘三世在 Unsplash 上的照片

如何在您的编程体验中实现可重用模型

设计模式用于帮助程序员理解概念、教学、学习,以及构建其他伟大的工作理念和概念。所以,当你思考设计模式的时候,要想到解决问题。设计模式是帮助构建和解决简单到复杂问题的模型。许多程序员实际上已经在他们自己的代码中实现了它们,而没有意识到这一点。这是因为它是一个如此抽象的概念,以至于你甚至不用真正思考它是什么就可以使用它。通过了解什么是设计模式以及如何使用它们,你可以克服那些看起来势不可挡的障碍。你还可以带来另一个层次的意识,而不是意外地实现一个笨拙的设计模式,你可以有意识地实现一个伟大的设计模式。设计模式在软件社区中被越来越多地使用,了解它们是一大优势。重要的是你至少知道面向对象编程的基础。更具体地说,你知道继承多态性

设计模式通常被称为设计模板,因为它们提供了如何处理常见的重复出现的问题的模板。您可以使用现有的设计模式,甚至创建自己的模式。设计模式有许多种类,下面是一些流行的设计模式:

  • 创意
  • 结构性
  • 行为

设计模式有一些独特的优点:

  1. 它们适用于任何面向对象编程语言。
  2. 它们是灵活的。不断变化、发展和更新。
  3. 经常因为创造力而没有完成。

模式结构

模式名称

模式的简要描述。

问题还是意图

你试图解决的潜在问题是什么?

解决办法

指定模式适用的位置。定义模式的结构和行为。

参与者

模式中涉及的类和对象。

结果

使用该模式可能产生的影响。

Python 设计模式的常见类型:

创造型的

工厂

抽象工厂

一个

建设者

原型

对象池

结构的

装饰者

代理人

转接器,适配器;改编者

复合材料

外表

轻量级

行为的

观察者

访问者

迭代程序

战略

命令

调解人

纪念品

状态

责任链

用 Python 实现基本设计模式

图案名称:工厂

图案类型:创意

在 Python 中实现设计模式时,能够为正确的用途选择正确的模式是很重要的。了解您的设计模式类别将有助于这个决策过程。

问题:你不确定你需要什么类型的对象或者你将使用什么类。

因此,在这个假设的场景中,假设您拥有一家计算机商店,并且只销售一种类型的显示器。你已经在卖的显示器是小显示器。您现在想在库存中添加大型显示器进行销售。

解决方案:这种模式适合创建新对象。它在许多涉及接口或类实例化的编程问题领域也有广泛的适用性。这对于大规模扩展您的类实例化并保持代码快速、简单和有组织非常有用。

# First Class for Small Monitor
class SmallMonitor():
    def __init__(self, brand):
        self._brand = brand def details(self):
        details = "24in x 24in 1080p | $87.00"
        return details# Second Class for Large Monitor
class LargeMonitor():
    def __init__(self, brand):
        self._brand = brand def details(self):
        details = "32in x 32in 1080p | $115.00"
        return details# Basic Creative Design Pattern Implementation
def get_monitor(monitor = 'small_monitor'):
    """factory method"""
    monitors = dict(small_monitor = SmallMonitor("ASUS"),
                    large_monitor = LargeMonitor("HP"))
    ruturn monitors[monitor]small_monitor = get_monitor("small_monitor").details()
large_monitor = get_monitor("large_monitor").details()print(small_monitor)
print(large_monitor)[out]
24in x 24in 1080p | $87.00
32in x 32in 1080p | $115.00

参与者:参与者是SmallMonitor()LargeMonitor()类。另外,small_monitorlarge_monitor对象也将包含在内。

结果:当应用于大规模的类实例化时,该模式具有积极的结果,但有时可能会令人困惑,或者对于更简单的实例化来说没有必要。在某些情况下,它会限制可用的代码。这也是 Python 中不推荐的许多应用程序。

结论

设计模式支持社区之间以及个人之间一致的编码。它们有助于维护一个健康的编程生态系统,该系统对常见问题有众所周知的解决方案。使用设计模式有时会使事情变得更糟,应该使用逻辑、推理和适当的实现将其集成到您的编码方案中。有时你手头的问题有错误的设计模式,有时没有或创建自己的设计模式是最好的主意。不管怎样,设计模式的知识只会让你成为更好的程序员。在许多情况下,它们将显著提高您的代码和效率。我希望这能帮助任何想了解更多 Python 设计模式基础知识的人。谢谢大家,编码快乐!

探索贝叶斯领域的实验

原文:https://towardsdatascience.com/exploring-experimentation-in-bayesian-territory-8e2c40df77bd?source=collection_archive---------23-----------------------

通过贝叶斯方法将 A/B 实验提升一个档次

资料来源:datascientistinsights.com

这篇文章的目的是鼓励人们探索实验设计的贝叶斯框架。

这篇文章包括:

  1. 什么是 A/B 测试,大多数人一般是怎么做的?
  2. 贝叶斯框架相对于频率主义方法的优势
  3. 这两种方法的根本区别在于
  4. 接下来去哪里找?

你也可以在这个 Jupyter 笔记本里找到这个题目的详细版本和解释(带代码)。

1.什么是 A/B 测试,人们一般是怎么做的?

极限实验是开启公司成功之门的钥匙之一。我们试验得越多,学到的东西就越多,就越有可能发现顾客的真正需求。

A/B 测试(有时也称为分割测试)是指同时向不同部分的访问者显示同一网页的两种变体,并比较哪种变体能带来更多的转换。

资料来源:Optimizely.com

历史上,公司一直使用频率主义/经典方法来测试不同的假设(例如,从两个不同的网站中找出一个胜出的版本)。基于这种方法,正在做出建立成功的客户-公司关系的决策。

那么大多数人/公司是怎么做的呢?

设计实验的整个过程由多个步骤组成,如决定测试组和对照组(即哪个用户将看到页面的哪个变体),希望实验运行的天数,以及最后测量结果。规划和测量阶段通常需要统计计算,如先验功效分析、效应大小和样本大小计算。最后,为了评估或测量结果,我们通常对比例进行 t 检验、卡方检验和 z 检验,以获得 p 值。这被称为频率主义/古典框架。

资料来源:reddit.com

上面我提到了相当多的统计关键词。如果您不知道这些术语,请不要担心。这些我已经在这里详细解释过了。

现在让我们来看看为什么我们应该谈论这个奇特的贝叶斯理论!

资料来源:xkcd.com

2.贝叶斯框架相对于频率主义方法的优势

实验/假设的成功也取决于决策者如何解释这些结果。实验者面临的常见挑战是统计结果的商业翻译,如“由于 p 值小于 0.05,我们可以拒绝零假设。我们相信,如果零假设是真的,我们看到的证据不太可能在 95%的情况下出现。”

他们实际上想听到的是“变量 B 优于 A 的概率是 x%。”

幸运的是,有一种方法可以降低实验成本,使我们做出决策的速度比平时快大约 50%,并且分析师可以大胆地大声说出下面的话:

“根据数据,从‘B’转换高于 A 的概率为 80%(举例)”。

不仅如此,我们可以在贝叶斯框架中不断地测量我们的结果,提供更多的透明度,而不是固定的样本大小设计。

贝叶斯框架为我们提供了这种非凡的方式。我们可以使用贝叶斯估计和序贯贝叶斯因子方法来设计我们的实验和测量我们想要的结果。

以下是贝叶斯方法相对于频率主义方法的明显优势:

一、达成决策所需的样本量约为频率主义方法的 50%(图 1) ( 白皮书 )

二。可解释性:简单直观地解释结果

观察结果的例子会是这样的——

频率主义者:“当我们观察到显著的 p 值为 0.02 时,我们可以拒绝零假设。”

Bayesian:“有 95%的可能性‘A’版本比‘B’版本更好,转化率的预期提升是 1.2 个百分点。”

三。我们可以对两个变量的转换率的实际概率进行评论

四。贝叶斯框架实现了一个连续的测量,相比之下,frequentist 的方法,窥视往往导致 p 值黑客攻击,这是一个有问题的做法

3.这两种方法的根本区别在于

借助于一个例子,也许可以更好地理解基本的区别(本节假设一些关于假设检验的先验知识)。

假设我们有两组,一组是治疗组,另一组是对照组。在频率主义者的方法中,我们从陈述我们的零假设开始。

零假设:经过某种处理后两组表现相似(假设这里考虑的度量是均值);即治疗组的平均值等于对照组的平均值。

这个假设固定了μ1 — μ2 =0 的信念(参数)。修正这个无效假设,我们继续看数据如何以 p 值的形式证明这个信念。

在贝叶斯方法中,我们说我们的信念有一定的不确定性,我们试图找到我们信念/参数的概率分布。所以,在贝叶斯的世界里,我们试图估计这个信念的概率分布(参数;即μ1 — μ2 ),在合并了来自我们先前的信念的信息(我们认为参数可能是什么,如果我们对先前的信念没有意见也没关系!)和观测数据。简单来说,我们不看单点估计,我们看假设的概率分布,给定数据。这叫做后验概率。

p 值和后验概率之间的根本区别在于,p 值是关于被观察数据的概率的陈述(假设我们的零假设为真),而后验概率是关于特定参数的置信程度(概率分布)的陈述。

4.接下来去哪里找?

我阅读了大量的博客和白皮书来理解贝叶斯方法。当我开始的时候,我对贝叶斯推理的看法是一种“神奇的修正”。我需要一个代码,在那里我可以输入数据,并可以得到类似“A 比 B 好的概率是 x%”的输出。一旦我完成了阅读和研究,我就把我所有的理解浓缩在一个笔记本里

组织应该从频繁的 A/B 测试方法转向贝叶斯框架

但是在我能够使用、理解和应用这个神奇的修复方法之前,我遇到了一些令人痛苦的术语,如知情和不知情的先验、后验、可能性、边际可能性、贝叶斯因子、共轭先验、马尔可夫链蒙特卡罗模拟、假阳性率(当零假设实际上为真时拒绝它)和假阴性率(当零假设实际上为假时未能拒绝它)。

来源:布拉尼·韦达科维奇的博客

有了这本 Jupyter 笔记本,我希望能让任何想要理解贝叶斯框架及其与频率主义方法的比较的人变得更容易。

非常感谢建设性的反馈!

探索 Pytorch 库中几个有用的张量函数

原文:https://towardsdatascience.com/exploring-few-useful-functions-in-the-pytorch-library-on-tensors-d23ce14d142?source=collection_archive---------54-----------------------

通过探索 Pytorch 的库开始使用 py torch

马库斯·斯皮斯克在 Unsplash 上的照片

D 由脸书人工智能研究实验室开发的 PyTorch 如今被广泛用作深度学习框架,原因很多,从小规模的机器学习原型到生产层面的应用。

“Pytorch 是一个开源的机器学习框架,它加速了从研究原型到生产部署的道路”,官方网站上的描述说。

这篇博客故事是由 Jovian.mlfreecodecamp 合作提供的关于 Pytorch 深度学习的 6 周课程的一部分。#零托甘人

在本教程中,我们将深入探讨 Pytorch 库中关于张量的 5 个有用函数。让我们开始吧。

首先:进口 Pytorch

import torch

torch.rand():

该函数返回一个张量,其中填充了区间[0,1]上均匀分布的随机数。它的一些参数如下所示:

size(int)-定义输出张量形状的整数序列。可以是可变数量的参数或集合,如列表或元组。

dtype (torch.dtype,可选)-返回张量的所需数据类型。默认值:如果没有,则使用全局默认值(请参见 torch.set_default_tensor_type())。

requires_grad (bool,可选)-如果自动签名应该记录对返回张量的操作。默认值:False。

让我们看看这个相当简单的函数的几个例子。

工作示例-1

我们可以观察到,我们得到了区间[0,1]中 9 个元素的张量向量。

工作示例-2

这里,我们得到一个形状为 3x4 的张量,其元素在区间[0,1]中

何时使用该功能?

当我们希望以随机值的张量作为[0,1]范围内的元素时,可以使用这个函数。

torch.mean():

torch.mean 函数返回张量的平均值。下面列出了它的一些参数。

输入(张量)——输入张量。

dim (int 或 python 的 tuple:ints)-要减少的一个或多个维度。

keep dim(bool)——输出张量是否保留 dim。

out (张量,可选)—输出张量。

现在,让我们看几个使用这个非常方便的函数的例子。

在单元格中,通过对张量向量应用 torch.mean(),我们可以观察到张量向量中的所有数字相加并除以元素的个数。

让我们用这个函数来解决一些复杂的问题。

有时我们可能不想在整个张量上计算平均值,而是在行或列上计算。

使用 torch.mean()的“dim”参数

在上述单元格中,torch.mean()应用于(3,5)张量,并带有一个名为“dim”的附加参数。此参数指定应该对行(如果 dim=0,则为每一列)或列(如果 dim=1,则为每一行)取平均值

有人曾经说过,“当你知道如何打破一个东西,那么你就真正知道如何用好它。”现在,让我们试着打破这个函数,我的意思是让我们理解这个函数什么时候不能像预期的那样工作。

打破火炬

在单元格中,我们得到的运行时误差”只能计算浮点类型的平均值。却得到了布尔。”这意味着 torch.mean()只能应用于 floating 类型,其他所有情况下都会失败。

不知道这是 bug 还是特性。我会把它留给你去想象。

何时使用该功能?

当有人想要计算张量的平均值时,例如当我们想要使用均方根误差(RMSE)作为损失函数时,可以使用该函数。

torch.view(形状):

PyTorch 有趣的一点是,它允许一个张量成为一个现有张量的视图。视图张量与其基础张量共享相同的底层数据。视图避免了显式的数据复制,因此允许我们进行快速且节省内存的整形、切片和基于元素的操作。

该函数返回一个新的张量,其数据与自张量相同,但形状不同。

正如我们可以观察到的,我们得到了给定张量的一个不同大小的副本,我们可以对它进行运算。

这是使用 view 函数的另一个例子。这里的-1 是从其他维度推断出来的。

看看这个功能什么时候不行。

这里,我们得到一个错误,因为形状应该匹配输入张量的大小。

torch.view 的灵感来源于 numpy . ndarray . shape()或 numpy.reshape()。它创建了张量的新视图,只要新形状与原始张量的形状兼容。

torch.reshape()函数将返回一个视图,与使用 torch 完全相同。Tensor.view()只要新的形状与原始张量的形状兼容。否则,它将返回一个副本。

然而,torch.reshape()的注释警告说:

连续输入和具有兼容步幅的输入可以在不复制的情况下被重新整形,但是不应该依赖于复制和查看行为。

torch.linspace():

个人感觉这个功能以后会很好用。

该函数返回“开始”和“结束”参数之间等距点的一维张量向量。点数是“步数”参数。它的一些重要参数是:

start(float)-点集的起始值

结束(浮点)-点集的结束值

steps(int)-开始和结束之间的采样点数。默认值:100。

out(张量,可选)-输出张量。

dtype (torch.dtype,可选)-返回张量的所需数据类型。默认值:如果没有,则使用全局默认值(请参见 torch.set_default_tensor_type())。

requires_grad (bool,可选)—如果亲笔签名的应该记录对返回张量的操作。默认值:False。

在单元格中,开始=10,结束=4,步数=25。因此,我们得到 25 个等间距的点,在 10 和 4 之间(包括 10 和 4)。默认情况下, PyTorch 给我们的元素是浮点。

这类似于上面的例子,但是这里我们得到的是类型为 int32 的元素。

现在我们来试着破函数。

torch.linspace()的“out”参数,它指定要由 torch.linspace 函数返回的值替换的所需张量。运行时错误“dtype Int 不匹配 dtype of out 参数(Float)”告诉我们,要使用“out”参数,两种类型的张量应该相同。

何时使用该功能?

当我们知道真实数据位于特定区间时,可以使用 torch.linspace 创建张量形式的数据。

torch.trace():

该函数返回输入二维矩阵的主对角线(从左上到右下)的元素之和。它唯一需要的参数是一个输入 2D 张量。

正如我们所见,该函数接受输入的 2D 张量,并计算对角线上元素的总和。

正如我们所见,该函数接受输入的 2D 张量,并计算对角线上元素的总和。

这个函数只需要一个 2D 矩阵,给它任何东西都会破坏它。这里,我们给出了一个 1D 数组。

虽然现在还不可用,但是这个函数的一个简单的新特性是允许我们得到任意对角线元素的和。但那不会被称为矩阵的“迹”。

何时使用该功能?

单独来看,跟踪操作并不有趣,但是它提供了一种更简单的符号,并且它被用作其他关键矩阵操作中的一个元素。

结论:

这里,我们展示了一些与 PyTorch 张量的一些核心概念相关的基本函数,如:

  • 如何生成元素在[0,1]范围内的随机张量?
  • 如何计算张量及其不同变量的平均值?
  • 如何为给定的张量创建不同的视图
  • 如何创建一个张量,其元素在点与点之间等距。
  • 如何计算 2D 张量的轨迹?

我希望你喜欢这个博客。感谢您的阅读,我希望这对您有所帮助。

参考资料:

用 Spark 探索金融消费者投诉

原文:https://towardsdatascience.com/exploring-financial-consumer-complaints-with-spark-48a253f9c830?source=collection_archive---------29-----------------------

PySpark 数据框架入门

在仔细阅读“美国政府公开数据的所在地”Data.gov 时,我偶然发现了美国联邦金融服务消费者投诉数据库。我想到了一些初步的问题,包括:这要追溯到多久以前?这是最新的吗?人们会提出多少投诉?哪些公司产生的投诉最多?

为了回答这些问题,我决定求助于 Spark。

当你有资格向消费者金融保护局投诉时,你可能需要的宁静形象。米尔科维Unsplash 上的照片

如何在你的电脑上设置 Spark

互联网上有很多博客/帖子/地方可以找到在你的电脑上安装 Spark 的方法(见莫塔达·梅希亚尔的帖子)。Spark 曾经以在你的电脑上启动和运行是一个挑战而闻名,但我在 1.6 GHz 英特尔酷睿 i5 和 8 GB 内存上使用运行 macOS Mojave 的 MacBook Air 时并没有太多的挣扎。对于不同的设置或以前版本的 Spark,这可能更具挑战性。

在这个阶段,你可能也需要一点额外的平静。照片由 A. Shuau (Obofili)Unsplash 上拍摄

开始使用 Spark 之前需要了解的一些事情

  • 关于为什么你可能想使用它的一点点(无耻地塞给我的文章,或者 HackerNoon 的, Toptal 的)
  • 使用 Spark 时,“你可以编写代码来描述你希望如何处理数据,而不是你希望如何执行,然后 Spark 代表你‘做正确的事情’来尽可能高效地运行它”(3)
  • Spark 使用“懒惰评估”,在你询问答案之前什么都不做。然后,它仍然只进行获得答案所需的计算,从而最小化工作量。你应该避免强迫它在不必要的中间步骤进行评估。

现在,我们已经准备好使用 SQL/pandas 风格的工作来研究一些真实的数据。今天,我们将调查金融服务消费者投诉数据库。我从 Data.gov这里下载的。坦白地说,我很难使用 Spark 的 CSV 加载器正确加载 CSV,所以我使用 pandas 将 CSV 转换为 parquet 文件。我很喜欢你有任何提示来解决我的问题,其中一些文本列被切成两半(可能在逗号?),导致表的行数和列数大约是原来的两倍。

注意:您需要安装 pyarrow 或 fastparquet 才能运行。下面镶木地板。

import pandas as pdpd_df = pd.read_csv('../data/Consumer_Complaints.csv')
pd_df.columns = [col.lower().replace(' ', '_') for col in 
                 pd_df.columns]
pd_df.to_parquet('../data/consumer_complaints.parquet')

在决定进行切换后,Spark 中 parquet 文件的加载速度比 CSV 文件快得多,这给我留下了深刻的印象。唯一的不便是,似乎 Spark 和/或 Parquet 文件不能很好地处理带空格的列名。在转换到拼花文件之前,我选择在 pandas 中删除它们。总的来说,还有另一个理由来为你的数据库/仓库/湖泊起一个合理的、没有空格的名字。

现在你知道该怎么做了:根据你所处的环境,你可能有很多机会利用照片来恢复宁静。梁朝伟Unsplash 上拍照

让火花在您的环境中运行

如果您使用的是基本的 Spark 特性和方法,那么您应该从初始化 SparkContext 开始,通过连接到特定的执行环境来设置 Spark 应用程序。如果我想在本地机器的所有内核上运行,我会使用:

import pysparksc = pyspark.SparkContext('local[*]')

您可以随时使用sc.stop()停止 SparkContext。

对于我们的 DataFrame 工作,我们将让 SparkSession 为我们初始化 SparkContext:

import pysparkspark = pyspark.sql.SparkSession \
     .builder \
     .master('local[*]') \
     .appName('Python Spark Consumer Complaints example') \
     .getOrCreate()

在这里,我们使用 SparkSession 构建器来“获取或创建”(如果尚不存在,则创建)一个 SparkSession,它具有指定的执行环境(我的本地计算机上的所有内核),并命名该应用程序以便在在线用户界面上进行识别。

作为检查,我运行了spark.sparkContext.defaultParallelism来确保 Spark 使用了预期数量的内核。

将数据导入数据框架

df = spark.read.load('../data/consumer_complaints.parquet',
     inferSchema='true', header='true')

仅此而已。我做了一个快速的df.count()来确认这次 Spark 读取了正确的行数。

使用 PySpark 数据框架

现在我们有了数据框,让我们继续使用它来回答一些问题:

我们在看什么时间框架?

这里我们需要转换成日期时间类型。我“选择”(像 SQL 一样)接收的日期转换成日期格式的日期类型,并给它一个别名。然后我按日期排序,取 1。Take 产生的结果与 limit 相同,但我不确定 Spark 各版本的性能差异。Take(n)是一个基本的 spark 经典,返回前 n 个结果,而 limit()对于面向 SQL 的人来说更直观。

from pyspark.sql.functions import to_datefirst_date = df.select(to_date(df.date_received,
                              'MM/dd/yyyy').alias('date')) \
     .orderBy('date').take(1)
first_date Out: [Row(date=datetime.date(2011, 12, 1))]last_date = df.select(to_date(df.date_received, 
                             'MM/dd/yyyy').alias('date')) \
     .orderBy('date', ascending=False).take(1)
last_date Out: [Row(date=datetime.date(2020, 1, 12))]

通过几行代码,我们看到数据集的时间跨度从 2011 年 12 月 1 日到 2020 年 1 月 12 日。

我们总共有多少投诉?每天?

对于投诉总数,快速df.count()返回约 150 万。

为了获得每天的投诉,我们将使用上面的 datetime 对象:

days = last_date[0]['date']-first_date[0]['date']
days Out: datetime.timedelta(days=2964)

每天平均投诉数量:

df.count()/days.days Out: 497.49257759784075

我通过将 PySpark 数据帧转换成熊猫数据帧并使用matplotlib来可视化每天的抱怨:

dates = df.select(to_date(df.date_received, 
                  'MM/dd/yyyy').alias('date'))
complaints_per_day_pd =dates.groupBy('date').count() \
      .sort('date').toPandas()

我注意到了这些峰值,并决定进行调查。你可以像在pandas中使用.loc一样使用 filter(有时与 where 和 when 结合使用)。

daily_complaint_counts = dates.groupBy('date').count()
daily_complaint_counts.filter(
     daily_complaint_counts['count'] > 1500) \
     .orderBy('date').show()

返回的格式为 SQL 样式:

+— — — — — + — — -+
| date|count|
+ — — — — — + — — -+
|2017–01–19| 2070|
|2017–01–20| 1633|
|2017–09–08| 3553|
|2017–09–09| 2709|
|2017–09–13| 1600|
+ — — — — — + — — -+

2017 年 9 月是 Equifax 安全漏洞。我不确定 2017 年 1 月发生了什么,除了特朗普总统 20 日的就职典礼。

哪些公司被投诉最多?

这里我想再次使用matplotlib可视化结果,所以我将结果转换成熊猫。

company_counts = df.groupBy('company').count()
company_counts_for_graph = company_counts.filter(
     company_counts['count'] >1000) \
     .sort('count', ascending=False).toPandas()

然后我使用matplotlib来可视化结果:

对于信用报告机构来说并不可爱…但是我们看到 Spark 的这个模块用相当 SQL-y 的代码很快就得到答案。如果您知道如何在 SQL 或 pandas 中实现,PySpark 的方式可能是相似的,或者至少在 PySpark 中可能有相似的方式。像往常一样,有许多方法可以得到相同的结果。

一些最后的问题

投诉是怎么接到的?主要是通过网络。

df.groupBy('submitted_via').count().sort('count', 
     ascending=False).show()Out:
+ — — — — — — -+ — — — -+
| submitted_via| count|
+ — — — — — — -+ — — — -+
|          Web|1106079|
|     Referral| 187007|
|        Phone|  88292|
|  Postal mail|  72485|
|          Fax|  20297|
|        Email|    408|
+ — — — — — — -+ — — — -+

回应是否及时?通常情况下。

df.groupBy('timely_response?').count().sort('count', 
     ascending=False).show()Out:
+ — — — — — — — — + — — — -+
| timely_response?|  count|
+ — — — — — — — — + — — — -+
|              Yes|1439485|
|               No|  35083|
+ — — — — — — — — + — — — -+

他们有争议吗?有时候。这取决于 null 在这一列中的含义。

df.groupBy('timely_response?').count().sort('count', 
     ascending=False).show()Out:
+ — — — — — — — — — + — — — +
|consumer_disputed?| count|
+ — — — — — — — — — + — — — +
|              null|706088|
|                No|620102|
|               Yes|148378|
+ — — — — — — — — — + — — — +

这篇文章的最后一个提示:你可以用*SparkSession*.stop()关闭你的 SparkSession。

我希望这有助于您开始使用 Spark 查看数据!你可以在这里找到回购

了解 Spark 工作原理的其他资源

  1. https://mapr . com/blog/datasets-data frames-and-spark-SQL-for-processing-of-tabular-data/
  2. https://mapr.com/blog/how-spark-runs-your-applications/
  3. https://mapr.com/blog/5 分钟-指南-理解-意义-apache-spark/
  4. https://realpython.com/pyspark-intro/
posted @ 2024-10-15 13:45  绝不原创的飞龙  阅读(18)  评论(0编辑  收藏  举报