TowardsDataScience-博客中文翻译-2016-2018-一百五十七-

45 阅读1小时+

TowardsDataScience 博客中文翻译 2016~2018(一百五十七)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

超参数在起作用!第一部分—激活功能

原文:towardsdatascience.com/hyper-param…

Sigmoid activation function in action!

介绍

这是一系列帖子的第一篇,旨在以清晰简洁的方式直观地展示训练神经网络的一些基本活动部分:超参数。

动机

深度学习是关于超参数!也许这是一种夸张,但对不同超参数对训练深度神经网络的影响有一个良好的理解肯定会让你的生活更容易。

在研究深度学习时,你可能会发现大量关于正确设置网络超参数重要性的信息: 激活函数、权重初始化器、优化器、学习速率、小批量 ,以及网络架构本身,如 隐藏层数每层中单元数

所以,你学习所有的最佳实践,你建立你的网络,定义超参数(或者只是使用它的默认值),开始训练并监控你的模型的 损失指标 的进度。

也许实验没有你预期的那么顺利,所以你对它进行迭代调整网络,直到你找到对你的特定问题有用的一组值。

寻找更深层次的理解(无意双关!)

你有没有想过引擎盖下到底发生了什么?我做了,事实证明一些简单的实验可能会对这个问题有所帮助。

激活功能 为例,本帖题目。你我都知道,激活函数的作用是引入一个非线性,否则整个神经网络可以简单地被一个相应的 仿射变换 (即一个线性变换,比如旋转缩放倾斜,后跟一个平移

一个只有 线性 激活 (即不激活!)将很难处理像这样非常简单的分类问题(每条线有 1,000 个点,为在-1.0 和 1.0 之间等距分布的 x 值生成):

Figure 1: in this two-dimensional feature space, the blue line represents the negative cases (y = 0), while the green line represents the positive cases (y= 1).

如果网络唯一能做的事情是执行仿射变换,这可能是它能够提出的解决方案:

Figure 2: linear boundary — doesn’t look so good, right?

显然,这还远远不够!更好的解决方案的一些例子有:

Figure 3: Non-linearities to the rescue!

这是 非线性激活函数 带来的三个很好的例子!你能猜出哪个图像对应于一个 ReLU 吗?

非线性边界(或者它们是?)

这些非线性边界是如何形成的?嗯,非线性的实际作用是扭曲和旋转特征空间,以至于边界变成… 线性

好了,现在事情变得越来越有趣了(至少,我第一次看到这个很棒的克里斯·奥拉的博客帖子时是这样认为的,我从这个博客中获得了写这篇文章的灵感)。所以,我们再深入调查一下吧!

下一步是建立最简单的神经网络来解决这个特殊的分类问题。在我们的特征空间(x1x2****)中有两个维度,网络有一个单独的隐层,有两个单元,所以当涉及到隐层的输出时我们保留维度的数目( z1**

Figure 4: diagram of a simple neural network with a single 2-unit hidden layer

到此为止,我们仍然在仿射变换的领域上……所以,是时候用一个 非线性激活函数 了,用希腊字母 sigma 表示,从而产生了激活值** ( a1a2 )**

这些激活值代表了我在本节第一段提到的扭曲和旋转特征空间**。这是使用【This 形作为 激活功能 时的预览图:**

Figure 5: two-dimensional feature space: twisted and turned!

如承诺的那样,边界是线性的!顺便说一下,上面的图对应于原始特征空间上具有非线性边界的最左边的解决方案(图 3 )。

神经网络的基本数学概述

为了确保你和我在同一页上,我在下面向你展示由神经网络执行的非常基本的矩阵算法的四种表示,直到隐藏层,应用 激活函数 之前(即,只是一个仿射变换xW + b )

Basic matrix arithmetic: 4 ways of representing the same thing in the network

应用 激活功能 的时间,在网络图上用希腊字母 sigma 表示。

Activation function: applied on the results of the affine transformations

瞧!我们从输入到隐藏层的激活值**!**

在 Keras 实施网络

对于这个简单网络的实现,我使用了 Keras 顺序模型 API 。除了不同的 激活功能 之外,每个被训练的模型都使用完全相同的超参数**:**

  • 权重初始值:Glorot(Xavier)法线(隐藏层)和随机法线(输出层);
  • 优化器 :随机梯度下降(SGD);
  • 学习率:0.05;
  • 小批量:16;
  • 隐藏层数:1;
  • 单位数 (在隐藏层):2。

鉴于这是一个二进制分类任务**,输出层有一个 单单元 有一个s 形 激活函数损失二进制交叉熵给出。**

Code: simple neural network with a single 2-unit hidden layer

激活功能正在发挥作用!

现在,对于有趣的部分— 在网络训练时可视化扭曲的特征空间,每次使用不同的 激活函数:sigmoidtanhReLU

除了显示特征空间中的变化,动画还包含:

  • 阴性(蓝线)和阳性病例(绿线)的预测概率直方图,错误分类的病例显示在红条中(使用阈值= 0.5);****
  • ****精度平均损耗的线图;
  • 数据集中每个元素的损失直方图

乙状结肠的

让我们从最传统的 激活函数sigmoid开始,尽管现在,它的使用仅限于分类任务中的输出层。

****

Figure 6: sigmoid activation function and its gradient

从图 6 的中可以看到,一个的 sigmoid 激活函数**将的输入值挤压到的范围(0,1) (同样的范围概率可以取,这也是它在输出层用于分类任务的原因)。此外,请记住,任何给定层的激活值都是下一层的输入,给定 sigmoid 的范围,激活值将是以 0.5** 为中心的**,而不是零(通常是归一化输入的情况)。****

也可以验证其梯度峰值为 0.25(对于 z = 0),并且当| z |达到值 5 时已经接近零。

那么,使用一个 sigmoid 激活函数 n 如何为这个简单的网络工作呢?让我们来看看动画:

Sigmoid in action!

有几点需要注意:

  • 时期 15–40:可以注意到水平轴上发生典型的s 形挤压
  • 历元 40–65:变换后的特征空间停留在一个平台上,在纵轴上有一个“加宽”;
  • epoch 65 :此时,阴性病例(蓝线)全部被正确分类**,尽管其关联概率仍然分布到 0.5;而边缘上的阳性病例仍为误分类**;****
  • 时期 65–100:前述的加宽变得越来越强烈,几乎覆盖了所有的特征空间,而损失稳步下降;
  • 第 103 期:由于扩大,所有阳性病例现在都位于适当的边界内,尽管仍有一些概率勉强超过 0.5 阈值;
  • 纪元 100–150:现在垂直轴上也发生了一些挤压,损失下降到似乎是一个新的平稳状态,除了一些积极的边缘情况,网络对其预测相当有信心**。******

因此,**s 形激活功能 成功分离两条线,但是 损耗 缓慢下降**,同时在停滞处停留相当一部分训练时间。******

我们能否用一个不同的激活功能 做得更好?******

双曲正切

tanh

Figure 7: tanh activation function and its gradient

图 7 中可以看到, tanh *激活功能输入值挤压到范围(-1,1) 。因此,由于以零为中心,激活值已经(在某种程度上)是下一层的归一化输入。*****

关于梯度,它有一个大得多的峰值 1.0(同样,对于 z = 0),但是它的下降甚至更快,接近于零到值 |z |低至 3。这是所谓的消失梯度问题的根本原因,它导致网络的训练越来越慢。****

现在,对于相应的动画,使用 tanh 作为 激活函数 :

Tanh in action!

有几点需要注意:

  • 时段 10–40:横轴上有一个tanhsquashing发生,虽然不太明显,而损耗停留在一个平台上;****
  • 时期 40–55:在 损失 上仍然没有改善,但是在纵轴上有一个变换后的特征空间的加宽**
  • 第 55 期:此时,阴性病例(蓝线)都被正确分类为,尽管其关联概率仍然分布到 0.5;而边缘上的阳性病例仍为误分类**;******
  • 时期 55–65:前述的加宽*很快到达几乎所有特征空间再次被覆盖的点,而损失突然下降;*****
  • 纪元 69 :由于“扩大”,所有阳性病例现在都位于适当的边界内,尽管仍有一些概率勉强高于 0.5 阈值;
  • 时期 65–90:现在垂直轴上也发生了一些挤压,损耗持续下降,直到达到新的平稳状态,网络对所有预测显示出高置信度******
  • 时期 90–150:在这一点上,预测的概率只有很小的提高。

好了,好像好一点了……这个 tanh 激活功能 达到了正确分类对于所有情况更快,随着 损耗 也下降更快(就是下降的时候),但是它也在停滞期花费了很多时间。

如果我们摆脱所有的挤压会怎么样?**

热卢

ReactivatedLlinearUnits,简称 ReLUs ,是目前 激活函数 的常见选择。一个 ReLU 解决了消失渐变的问题,这在它的两个前辈中很常见,同时也是计算渐变最快的

********

Figure 8: ReLU activation function and its gradient

正如你在图 8 中看到的, ReLU 是一个完全不同的怪兽:它不会将值“挤压”到一个范围内——它只是保留正值并将所有的负值变成零

使用 ReLU 的好处是它的渐变要么是 1(正值),要么是 0(负值)——不再有消失渐变!这种模式导致网络的更快收敛

另一方面,这种行为会导致所谓的“死神经元”,也就是说,神经元的输入始终为负,因此,其激活值始终为零。****

最后一个动画的时间,与前两个有很大的不同,由于 ReLU 激活功能 中的挤压缺席:******

ReLU in action!

有几点需要注意:

  • 时段 0–10:亏损从一开始就稳步下降
  • 历元 10 :此时,阴性案例(蓝线)全部被正确分类,尽管其关联概率仍然分布到 0.5;而边缘上的阳性病例仍为误分类
  • 时期 10–60:损失下降直至达到稳定状态,时期 52 以来,所有情况已经被正确分类,并且网络已经对所有预测展现出高置信水平;****
  • 时期 60–150:在这一点上,预测的概率只有很小的提高。

嗯,难怪 ReLUs 是如今激活功能事实上的标准。 损失 使从一开始就稳步下降*,只有稳定在接近零的水平,在中所有情况下达到正确分类的时间约为花费 tanh 的 75%。*****

摊牌

动画很酷(好吧,我有偏见,是我做的!),但是不太方便在特征空间上比较每一个不同的 激活函数总体效果*。所以,为了便于你比较,它们并排在这里:***

Figure 9: linear boundaries on transformed feature space (top row), non-linear boundaries on original feature space (bottom row)

那并排 准确度损耗 曲线呢,这样我也可以对比一下训练速度?当然,我们开始吧:

Figure 10: accuracy and loss curves for each activation function

最后的想法

我用来说明这篇文章的例子是尽可能简单的,动画中描述的模式仅仅是为了给你一个大概的概念关于每一个 激活函数的底层机制。********

况且我用我的 初始化权重 得到了幸运*(也许用 42 做种子是个好兆头?!)并且所有三个网络都学会了在 150 个训练时期内对所有情况进行正确分类。事实证明,训练对初始化非常敏感,但这是以后文章的主题。*

尽管如此,我真心希望这篇文章和它的动画能给你一些见解甚至一些*“啊哈!”了解这个令人着迷的话题的时刻,这个话题就是深度学习**。*

更新(2018 年 5 月 10 日)

现在你可以自己复制剧情和动画了:-)我已经发布了一个包——deep replay——在 GitHub 及其对应的帖子上查看一下。

感谢 Jakukyo 弗列尔,你还可以查看这篇帖子的中文版

修正了图 10 中的错别字:中间的图显示的是 tanh ,而不是 sigmoid。

如有任何想法、意见或问题,请在下方留言或联系我 推特

超参数在起作用!DeepReplay 简介

原文:towardsdatascience.com/hyper-param…

Photo by Immo Wegmann on Unsplash

介绍

在我之前的帖子中,我邀请你去好奇当你训练一个神经网络的时候,在引擎盖下到底发生了什么。然后我研究了 激活函数 的作用,用动画说明了它们对 特征空间 的影响。

现在,我邀请在调查中发挥积极作用!

原来这些情节动画引起了相当多的关注。所以我决定组织我的代码,把它构造成一个合适的 Python 包,这样就可以绘制你自己的深度学习模型

你会问,它们看起来怎么样?好吧,如果你还没有查看最初的帖子,这里有一个快速浏览:

This is what animating with DeepReplay looks like :-)

所以,事不宜迟,我为你呈现… 深度回放

深度回放

这个包被称为 DeepReplay ,因为这正是它允许你做的事情:重放训练你的深度学习模型的过程,绘制动画制作它的几个方面。

这个过程很简单,由五个步骤组成:

  1. 这一切都从创建一个回调的实例开始!
  2. 然后,一切照旧:构建并训练你的模型。
  3. 接下来,将收集的数据加载到重放中。
  4. 最后,创建一个图形,然后将可视化效果附加到它上面。
  5. 剧情和/或动画吧!

让我们逐一完成这些步骤!

1.创建回调的实例

回调应该是 ReplayData 的一个实例。

回调将模型输入( Xy )以及 文件名组名 作为参数,用于存储收集的训练数据。

要记住两件事:

  • 对于玩具数据集,在你的模型拟合中使用相同的 Xy 就可以了。这些是将要绘制的示例-因此,如果使用较大的数据集,您可以选择数据集的随机子集来保持合理的计算时间。
  • 数据存储在一个 HDF5 文件中,你可以多次使用 同一个文件 超过,但是永远不要同一个组 !如果你尝试使用同一个组名运行它两次,你会得到一个错误

2.构建并训练您的模型

就像我说的,一切照常,这里没什么可看的…只是不要忘记在装配时将您的回调实例添加到回调列表中!

3.将收集的数据加载到重放中

所以,给整件事起名字的部分…是时候重播它了!

应该够简单了:创建一个 Replay 的实例,提供 文件名 和您在步骤 1 中选择的 组名

4.创建一个图形并附加可视化效果

实际上,这是事情变得有趣的一步。只需使用 Matplotlib 创建一个图形,就像示例中的图形一样简单,或者像 subplot2grid 允许您创建的图形一样复杂,并开始从您的 Replay 对象向图形附加可视化效果

上面的例子建立了一个 特征空间 基于层的输出命名,暗示性地, 隐藏

但是有五种类型的可视化可用:

  • 特征空间 :表示 扭曲旋转特征空间 的图形,对应一个隐藏层的输出(目前只支持 2 单元隐藏层),包括二维输入的网格线

  • 判定边界 :表示 原始特征空间 的二维网格图,连同 判定边界 (目前仅支持二维输入);

  • 概率直方图 : 两个得到的输入的分类概率的直方图,每个类别一个,对应模型输出(目前只支持二元分类);

  • 损失和度量 :损失和选择的度量的线图,计算所有作为回调参数传递的输入;

  • 损失直方图 :对作为回调参数传递的所有输入计算的损失的直方图(目前仅支持二进制交叉熵损失)。

5.绘制和/或制作动画!

对于这个例子,用一个单个 可视化,就可以直接使用它的 剧情动画 方法。这些方法将分别返回一个图形和一个动画,然后您可以将它们保存到文件中。

如果您决定使用多个同时可视化,有两个辅助方法返回合成的情节和动画,分别是:compose _ plotscompose _ animations

为了说明这些方法,这里有一个要点,它来自我在最初的帖子中使用的“示例。有四个可视化和五个图( 概率直方图两个图,用于阴性和阳性情况)。

本帖开头的动画 GIF 其实就是这个构图动画的结果!

限制

此时,你大概注意到了,两个最酷的可视化, 特征空间 决策边界 ,都被限制在两个维度

我还计划在三维中添加可视化支持,但大多数数据集和模型要么有更多输入要么有更多单元的隐藏层。

所以,这些是你的选择:

  • 2D 输入,2 单元隐藏层: 特征空间 带可选网格(查看激活函数示例);
  • 3D+输入,2 单元隐藏层: 特征空间 ,但无网格;
  • 2D 输入,隐藏层用 3+单位: 决定边界 用可选网格(查看圆圈示例);
  • 没有什么是二维的:嗯…总有一个解决方法,对吗?

围绕多维度工作

我们想要实现什么?因为我们只能做二维图,我们想要二维输出——很简单。

如何获得二维输出?增加一个额外隐藏层两个单位当然!好的,我知道这是次优,因为它实际上是在修改模型(我提到过这是一个变通办法吗?!)。然后我们可以使用这个额外图层的输出进行绘图。

你可以查看 月亮 或者 UCI Spambase 笔记本,查看添加一个额外的隐藏层并绘制它的示例。

继续进行,风险自负:-)

不管怎样,我们对这个模型做了什么?通过添加一个额外的隐藏层,我们可以认为我们的模型有两个组件:一个 编码器 和一个 解码器 。让我们稍微深入一点:

  • 编码器:编码器从输入一直到我们的额外隐藏层。让我们把它的二维输出看作是特征并把它们叫做 f1f2
  • 解码器:在这种情况下,解码器只是一个简单明了的逻辑回归,它接受两个输入,比如说 f1f2 ,并输出一个分类概率。

让我试着用一个网络图来说得更清楚些:

Encoder / Decoder after adding an extra hidden layer

这是什么?一个 9 维输入,一个有 5 个单元的原始隐藏层,一个有两个单元的额外隐藏层,其对应的两个输出(特征)和一个单个单元输出层。

那么,在输入的过程中会发生什么?让我们看看:

  1. 输入( x1x9* )被馈入 编码器 部分的模型。*
  2. 隐**层曲折层投入。隐藏层的输出也可以被认为是特征**(这些将是图中单元h1**到H5的输出),但是这些被假定为 n 维,因此不适合绘图。到目前为止,一切如常。**
  3. 然后是额外的隐藏层。它的权重矩阵具有形状 (n,2) (在图中, n = 5 并且我们可以在*和 e 节点之间计数 10 个箭头)。如果我们假设一个 线性激活函数 ,这一层实际上是在执行一个仿射变换,将点从一个 n 维映射到一个二维特征空间。这些是我们的功能,**【F2】编码器的输出 部分。*
  4. 既然我们假设一个 线性激活函数 用于额外的隐藏层,那么 f1f2 将被直接馈送到 解码器 (输出层),也就是说,馈送到一个具有的单个单元这是一个简单明了的逻辑回归。

这一切意味着什么?意味着我们的模型也在学习一个 潜在空间两个潜在因素 ( f1f2 )现在!很奇特,是吧?!不过,不要被这些术语的花哨所吓倒……这基本上意味着模型学会了将信息最好地压缩为两个特征,给定手头的任务——二进制分类。

这是 自动编码器 的基本原理,主要区别在于自动编码器的任务是重建其输入,而不是以任何方式对其进行分类。

最后的想法

我希望这篇文章能吸引你尝试一下深度回放

如果你为不同的数据集,或者使用不同的网络架构或超参数,想出了好看又酷的可视化,请评论部分分享它。如果有足够的兴趣,我正在考虑开始一个画廊页面。

有关 DeepReplay 包的更多信息,如安装、文档、示例和笔记本(你可以使用 Google Colab 来玩),请访问我的 GitHub 资源库:

**** [## DVD godoy/deep replay

深度回放-深度回放-生成可视化效果,如我的“超参数运行!”系列!

github.com](github.com/dvgodoy/dee…)

祝你的模型动画制作愉快!:-)

如果你有什么想法、评论或问题,请在下方留言或联系我 推特 ****

超参数在起作用!第二部分—权重初始值设定项

原文:towardsdatascience.com/hyper-param…

Photo by Jesper Aggergaard on Unsplash

介绍

这是我关于超参数系列的第二篇文章。在这篇文章中,我将向你展示正确初始化你的深度神经网络的权重重要性。我们将从一个简单的初始化方案开始,解决它的问题,就像消失 / 爆炸渐变,直到我们**(重新)发现**两个流行的初始化方案:Xavier/GlorotHe

我假设你已经熟悉了一些关键概念(Z 值、激活函数及其梯度),我在本系列的第一篇帖子中已经提到过。

说明这篇文章的情节是用我的包 DeepReplay 生成的,你可以在 GitHub 上找到它,并在这篇文章上了解更多。

动机

在我寻求更深入地理解每一个不同的超参数对训练深度神经网络的影响时,是时候研究一下权重初始化器了。

如果您曾经搜索过这个特定的主题,您可能会遇到一些常见的初始化方案:

  • 随机
  • 泽维尔 / 格洛特

如果你挖得更深一点,你可能还会发现,如果激活函数是一个 Tanh ,那么应该使用 Xavier / Glorot 初始化,如果激活函数是一个 ReLU ,那么推荐使用 He 初始化

顺便说一下,澄清一些事情: Xavier GlorotYoshua Bengio 是“ 理解训练深度前馈神经网络 的困难”论文的作者,该论文概述了将作为其第一作者的开头( Xavier 或最后( Glorot )的初始化方案。因此,有时这种方案将被称为 **Xavier 初始化、**和其他一些时候(如在 Keras 中),它将被称为 Glorot 初始化。不要被这个迷惑,因为我是第一次知道这个话题。

弄清楚这一点后,我再问你一次:你有没有想过引擎盖下到底发生了什么?为什么初始化这么重要**?初始化方案之间的差异是什么?我的意思是,不仅是他们对方差应该是什么的不同定义,而且是在训练一个深度神经网络时使用其中一个的总体效果!**

在深入研究之前,我想给它应有的信任:这些情节在很大程度上受到了安德烈·皮卢尼奇关于同一主题的令人敬畏的帖子的启发。

好了,现在让我们开始吧!

设置

确保您使用的是 Keras 2.2.0 或更新版本-旧版本有一个问题,生成的权重集的方差低于预期!

在这篇文章中,我将使用一个具有 5 个隐藏层100 个单位的模型,以及一个典型的二进制分类任务中的单个单位输出层(即使用 sigmoid 作为激活函数,使用二进制交叉熵作为损失)。我将这个模型称为模块模型,因为它有相同大小的连续层。我使用以下代码来构建我的模型:

Model builder function

块状模型

这是块模型的架构,不考虑我将用来构建图的激活函数和/或初始化器。

输入

输入是从 10 维球中抽取的1000 个随机点**(这看起来比实际上的更好,你可以把它想象成一个有 1000 个样本的数据集,每个样本有 10 个特征)这样样本就有了零均值单位标准差。**

在该数据集中,位于球半径一半内的点被标记为阴性情况 (0),而剩余的点被标记为阳性情况* (1)。***

Loading 10-dimensional ball dataset using DeepReplay

朴素初始化方案

开始的时候,有一个 sigmoid 激活函数随机初始化的权重。而且训练是,收敛是,成绩是不好。****

但是,为什么呢?

那时,通常的程序是*从一个正态分布**(零均值,单位标准偏差)中抽取随机值*,然后将它们乘以一个小数字,比如说 0.01 。结果是一组标准偏差约为 0.01 的砝码。这导致了一些问题…**

在进入更深的钻头之前(只是一个钻头,我保证!)进入为什么这是一个坏的初始化方案的数学原因,让我展示在带有 sigmoid 激活功能的模块模型中使用它的结果:

Code to build the plots! Just change the initializer and have fun! :-)

Figure 1. BLOCK model using sigmoid and naive initialization — don’t try this at home!

这个好看吧?你能发现所有正在变坏的东西吗?

  • Z 值(记住,这些是应用激活功能之前的输出)和激活都在窄范围内;**
  • 梯度几乎为零
  • 左栏的奇怪的分布是怎么回事?!

不幸的是,如果我们选择尝试和训练它,这个网络可能不会很快学到很多东西。为什么是?因为它的渐变消失了**!**

我们甚至还没有开始培训过程!这是纪元 0** !那么,到目前为止发生了什么?让我们看看:**

  1. 使用简单方案** ( 右上子情节)初始化**权重****
  2. 1000 个样本被用于通过网络的正向传递,并为所有层(输出层不包括在图中)生成了 Z 值 ( 左上子图)和激活** ( 左下子图)**
  3. 针对通过网络反向传播的和真实标签计算损失*,生成所有层的梯度(右下方子图*****

就是这样!单遍通过网络!

接下来,我们应该相应地更新权重**,然后重复这个过程,对吗?但是,等等…如果梯度几乎为零,那么更新后的权重将几乎与相同,对吗?**

是什么意思?这意味着我们的网络几乎毫无用处,因为它无法在合理的时间内学习任何东西**(即,更新其权重以执行所提议的分类任务)。**

欢迎来到消失渐变的极端案例!

你可能会想:“是的,当然,标准差太低了,不可能像那样工作”。那么,尝试不同的怎么样,比如说, 10x 或者 100x 大

标准偏差大 10 倍= 0.10

Figure 2. BLOCK model using 10x bigger standard deviation

好了,这看起来好一点了… Z 值激活合适的范围内,倒数第二个隐藏层的渐变显示出一些改善,但是仍然朝着初始层消失**。**

也许把变得更大可以修正渐变,让我们看看…

标准偏差大 100 倍= 1.00

Figure 3. BLOCK model using 100x bigger standard deviation

好的,看起来我们在消失渐变问题中有了 som e progress ,因为所有层的范围变得彼此更加相似。耶!但是 …我们毁了Z 值激活**……现在 Z 值显示出太宽的范围,迫使激活几乎变成二进制模式。**

尝试不同的激活功能

如果你读了我关于超参数的第一篇文章,你会记得一个 Sigmoid 激活函数有一个基本的问题,就是以 0.5 为中心的。所以,让我们继续遵循神经网络的进化路径,使用一个 Tanh 激活函数来代替!****

Figure 4. BLOCK model using Tanh and naive initialization

用来自正态分布的小随机值替换 Tanh 激活函数的 Sigmoid ,同时保持初始初始化方案将我们带到了另一种消失渐变的情况(它可能看起来,毕竟,它们沿着所有层都是** 相似的,但是检查一下比例、渐变),伴随着消失的 Z 值消失的激活(只是为了明确,这两个是而不是真正的术语)!肯定,不是该走的路!**

让我们一直使用一个大的标准差,然后看看结果如何(是的,我把最好的留到最后……)。

Figure 5. BLOCK model using Tanh and a BIG standard deviation

在这个设置中,我们可以观察到爆炸渐变的问题。看到渐变值如何随着我们从最后一个隐藏层到第一个隐藏层越来越大了吗?此外,就像使用 Sigmoid 激活函数时发生的一样, Z 值具有太宽的范围激活在大多数情况下都崩溃为零或一个。还是那句话,不好!

并且,如承诺的那样,获胜者是… Tanh 标准差 0.10

Figure 6. BLOCK model looking good!

为什么这个一个是赢家?让我们来看看它的特点:

  • 首先,梯度沿着所有层合理地相似** (并且在适当的比例——大约比权重小 20 倍)**
  • 第二, Z 值合适的范围 (-1,1)内,并且沿着所有层相当相似(尽管一些收缩是明显的)****
  • 第三,激活没有折叠成二进制模式,并且沿着所有层合理地相似(再次,有一些收缩)****

如果你还没有注意到,在所有层上相似是一件大事!简而言之,这意味着我们可以在网络的末端堆叠另一层,并期待类似的分布Z 值激活,当然还有梯度。在我们的网络中,我们绝对做而不是折叠消失爆炸的行为,不,先生!****

但是,所有层的相似是结果,而不是原因……正如你可能已经猜到的,关键是权重标准偏差!****

因此,我们需要一个初始化方案**,它使用最佳可能标准偏差来抽取随机权重!进入泽维尔·格洛特约舒阿·本吉奥 …**

Xavier / Glorot 初始化方案

Glorot 和 Bengio 设计了一个初始化方案**,试图保持所有获胜特征被列出,即渐变Z 值激活沿着所有层相似。另一种说法是:保持所有层的变化相似。**

你可能会问,他们是怎么把这个拉出来的?一会儿我们会看到,我们只需要做一个真正的** 简要的回顾一下方差的一个基本性质。**

非常简要地回顾一下

假设我们有 x 值(来自前一层的输入激活值**)和 W 权重。两个独立变量的乘积的方差由以下公式给出:**

那么,我们假设 xW 都有零均值**。上面的表达式变成了两个方差 xW 的简单乘积。**

这里有两点很重要:

  1. 输入应该有零均值**以保持在第一层,因此总是缩放和居中您的输入!
  2. ****Sigmoid 激活函数对此提出了一个问题,因为激活值的平均值为 0.5,不是 !关于如何补偿的更多细节,请查看这个帖子

鉴于第二点,坚持使用 Tanh 才有意义,对吗?所以,这正是我们要做的!现在,是时候将这些知识应用到一个小例子中了,所以我们到了(希望如此!)得出与 GlorotBengio 相同的结论。

Figure 7. Two hidden layers of a network

微小的例子

这个例子由两个完全连接的隐藏层组成, XY,(我把通常的约定抛到九霄云外,以保持数学符号最少!).

我们只关心连接这两层的权重以及激活渐变差异**。**

对于激活**,我们需要在网络中通过一个转发通道。对于渐变,我们需要反向传播。**

Figure 8. Tanh activation function

并且,为了保持数学简单,我们将假设激活函数在等式中是线性的**(而不是 Tanh ),这意味着激活值是与 Z 值相同的**。****

虽然这看起来有点夸张,但是图 8 向我们展示了在区间[-1,1] 中,Tanh 大致是线性的,所以结果应该成立,至少在这个区间内。

Figure 9. Forward Pass

前进传球

因此,不使用矢量化方法,我们将挑出一个单元、 y1 ,并计算它。

图 9 提供了所涉及零件的清晰图片,即:

  • 三个单元在前一层** ( 扇入)**
  • 重量 ( w11w21w31 )
  • 我们要计算的单位方差为,y1

假设 xW独立同分布**,我们可以对 y1 :方差进行一些简单的数学运算**

还记得关于差异简要回顾吗?是时候好好利用它了!

好了,快到了!记住,我们的目标是保持方差沿着所有层相似。换句话说,我们应该致力于使*x 的方差y 的方差相同。*

对于我们的单个单元, y1 ,这可以通过选择其连接权重方差为:

并且,对于所有隐藏层 XY 之间的连接权重,我们有:

顺便说一下,如果我们从一个正态 分布中抽取随机权重,这就是要使用的方差

如果我们想用一个均匀分布怎么办?我们只需计算(对称的)下限和上限,如下所示:****

完事了吗?!还没有…不要忘了反向传播,我们也想保持的渐变沿着所有的层**(它的方差,更精确)。******

Figure 10. Backward Pass

反向传递(反向传播)

还是那句话,我们挑出一个单位, x1 ,用于后向传递。

图 10 提供了所涉及零件的清晰图片,即:

  • 以下五层 ( 扇出)
  • 重量 ( w11w12w13w14w15 )
  • 我们要计算梯度相对于它的 x1方差的单位

基本上,我们将做出相同的假设,并遵循与正向传递中相同的步骤**。对于梯度 s 相对于 x1方差,我们可以用同样的方法计算出来:******

再次利用我们在简要回顾中学到的知识:

并且,为了保持沿所有层的梯度方差相似,我们发现其连接权重所需的方差为:

好了,我们已经走了很长一段路了!中的“扇出”的逆运算给出了正向传递权重的期望方差,而“扇出的逆运算给出了( )的期望方差!)用于反向传播的权重****

但是…如果“扇入”和“扇出”有非常不同的值呢?

协调向前和向后传球

无法决定选择哪一个,“扇入”或“扇出”,来计算你的网络权重方差**?没问题,就拿平均值吧!**

因此,我们最终得出了权重方差的表达式,如 GlorotBengio 所示,用于正态分布**:**

并且,对于均匀分布**,我们相应地计算极限:**

恭喜!你(重新)发现了 Xavier / Glorot 初始化方案

但是,仍然有一个小的细节,你应该选择一个正态分布来提取权重吗…

截断正态和 Keras 方差标度

当谈到神经网络的权重时,我们希望它们整齐地分布在零附近,甚至更重要的是,我们不希望有任何异常值!所以,我们截断它!****

截断是什么意思?只要去掉任何比两倍标准差远的值!因此,如果您使用标准偏差 0.1 ,截尾正态分布将绝对没有低于-0.2 或高于 0.2** 的值(如图 11** 左侧图**)。******

事情是这样的,一旦你切掉了正态分布的尾部,剩下的值有一个稍微低一点的标准差…准确的说是原始值的 0.87962566103423978。

在 Keras 2 . 2 . 0 之前的版本中,截断正态分布的这种差异在方差缩放初始化器中没有考虑,而这是 Glorot 和 he 初始化器的基础。因此,在更深层次的模型中,基于均匀分布的初始化器可能会比它的正常对应物表现得更好,正常对应物会遭受一层又一层缓慢缩小的方差…

到今天为止,这不再是一个问题,我们可以在图 11 的右侧图中观察到补偿截断的效果,其中方差缩放初始值的分布明显更宽。****

Figure 11. Truncated normal and Keras’ Variance Scaling

求一些剧情!

非常感谢你陪我看完了更多的数学部分。你的耐心将会得到丰厚的回报!

让我们看看 Glorot 初始化器(正如它在 Keras 中被调用的)如何执行,使用正常统一分布。

Figure 12. BLOCK model with Glorot Normal initializer

Figure 13. BLOCK model with Glorot Uniform initializer

看起来我们有两个赢家了!

还记得我们之前的赢家吗,在图 6** 中使用朴素初始化方案标准差 0.1 的 BLOCK 模型?结果非常相似,对吧?**

事实证明,根据 Glorot 初始化方案,当“扇入”和“扇出”等于 100 时,我们使用的 0.1 标准偏差正是正确的值。这是而不是使用截尾正态分布,虽然…

所以,这个初始化方案解决了我们的消失爆发渐变的问题…但是它是否适用于不同于 Tanh 的激活函数**?让我想想…**

整流线性单元(ReLU)激活功能

我们能坚持使用相同的初始化方案,而使用 ReLU 作为激活函数吗?

Figure 14. BLOCK model with ReLU and Glorot Normal initializer — they don’t mix well…

答案是:没有

回到起点…我们需要一个新的改进的初始化方案。进入等人,带着他们的“ 钻研整流器 ”论文…

初始化方案

幸运的是,我们在(重新)发现 Glorot 初始化方案时得到的一切仍然有效。只有一个微调我们需要做… 将权重的方差乘以 2 !真的,这就是全部的代价!

Figure 15. ReLU activation function

很简单,对吧?但是,为什么是

原因也很简单: ReLUZ 值(负一)的一半变成零**,有效地去除了差异的大约一半。因此,我们需要将权重的方差加倍来补偿它。**

既然我们知道 Glorot 初始化方案保留了方差** ( 1 ),那么如何补偿对于 ReLU ( 2 )的方差减半效应?结果( 3 )不出所料,是将方差翻倍。**

因此,与正态分布一起使用的权重方差的表达式为:

并且,对于均匀分布**,我们相应地计算极限:**

恭喜!你(重新)发现了 He 初始化方案

但是 …那么反向传播呢?难道我们不应该再次使用两个“粉丝的平均值吗?实际上,没有也不需要了。等人在他们的论文中指出,对于常见的网络设计,如果初始化方案在正向传递期间缩放激活值,那么对于反向传播 以及也是如此!此外,它以两种方式工作,所以我们甚至可以使用扇出,而不是扇入。****

现在,是更多剧情的时候了!

Figure 15. BLOCK model with ReLU and He Normal initializer

Figure 16. BLOCK model with ReLU and He Uniform initializer

再来两个获奖者**!当谈到 Z 值的分布时,它们看起来非常相似!至于渐变,它们现在看起来比我们用Tanh/Glorotduo 的时候多了一点vanish…这是否意味着 Tanh / GlorotReLU / He 更好?我们知道这是不是真的…**

但是,那么,为什么它的渐变图 16** 上看起来不那么好看呢?嗯,再一次,别忘了看一下刻度!即使梯度方差随着我们通过网络反向传播而减少,其值也无处靠近的消失(如果你记得图 4 ,情况正好相反——沿层的方差*相似,但它).*******

因此,我们不仅需要沿着所有层的相似的变化,还需要一个合适的渐变比例比例非常重要,因为它将与学习率一起定义多快更新权重****更新。如果坡度太小,则学习(即权重更新)将极慢。****

你会问,有多小才算太小?一如既往,它取决于 …重量大小****。所以,过小是不是绝对*度量,而是相对度量。*

如果我们计算梯度的方差和相应权重方差之间的比率(或其标准差,就可以大致比较不同初始化方案学习速度及其底层分布(假设学习速率不变)。****

所以,是时候…

摊牌——普通对制服,格洛特对贺!

说实话,GlorotvsHe其实就是指TanhvsReLU这场比赛的答案我们都知道(剧透预警!): ReLU 胜

普通** vs 制服呢?让我们来看看下面的情节:**

Figure 17. How big are the gradients, after all?

赢家是……制服!很明显,至少对于我们的特定块模型和输入,使用均匀分布比使用正态分布产生相对更大的****梯度。****

此外,正如所料,使用 ReLU 比使用 Tanh 产生相对更大的梯度。对于我们的特定示例,这对于第一层不成立,因为其在中的扇形仅为 10 (输入的尺寸)。如果我们使用 100 维输入,那么 ReLU梯度也会比该层的大**。******

而且,即使看起来一样有点"消失"当使用 ReLU 时,只要看一下图 17* 最右边的紫色小条…我将简单初始化的Sigmoid 激活的网络滑入了情节*****

Ratio: standard deviation of gradients over standard deviation of weights

综上所述,对于一个 ReLU 激活的网络,使用均匀分布He 初始化方案是一个不错的选择*;-)***

有很多很多方法可以分析选择特定初始化方案的影响…我们可以尝试不同的网络架构(如“漏斗”或“沙漏”形状),更深的网络,改变标签的分布(因此,损失)…我尝试了很多组合,他/统一总是优于其他初始化方案,但这篇文章已经太长了!

最后的想法

这是一篇 looong 的帖子,尤其是对于一个如此理所当然的话题,如重量* 初始者!但是我觉得,一个人要真正理解它的重要性,就应该遵循步骤,碰到导致现在使用的方案发展的问题。***

尽管作为一名从业者,你知道用于初始化你的网络的"正确的"组合,我真的希望这篇文章能够给你一些洞察真正发生了什么,最重要的是,为什么 那个特定组合是"正确的"一:-)

如果你有什么想法、评论或者问题,请在下方留言或者联系我 推特

基于 Keras 的超参数优化

原文:towardsdatascience.com/hyperparame…

为深度学习模型找到正确的超参数可能是一个繁琐的过程。不一定要。

TL;速度三角形定位法(dead reckoning)

有了正确的流程,就不难为给定的预测任务找到最先进的超参数配置。在三种方法中——手动、机器辅助和算法——本文将重点讨论机器辅助。本文将介绍我是如何做到这一点的,证明这种方法是可行的,并提供对它为什么可行的理解。主要原则是简单。

关于性能的几句话

关于性能的第一点涉及到作为测量模型性能的方法的准确性(和其他更健壮的度量)的问题。以 f1 成绩为例。如果你有一个 1%肯定的二进制预测任务,那么一个让所有事情都为 0 的模型将接近完美的 f1 分数和准确性。这可以通过对 f1 分数处理极限情况的方式进行一些改变来处理,例如“全零”、“全一”和“无真阳性”但这是一个很大的话题,超出了本文的范围,所以现在我只想说明,这个问题是让系统超参数优化工作的一个非常重要的部分。我们在这个领域有很多研究,但研究更多地集中在算法上,而不是基本面。事实上,你可以拥有世界上最复杂的算法——通常也非常复杂——根据毫无意义的指标做出决策。这对处理“现实生活”中的问题不会有太大的帮助。

不要犯错误;即使我们得到了正确的性能指标(是的,我在叫喊),我们也需要考虑在优化模型的过程中会发生什么。我们有一个训练集,然后我们有一个验证集。一旦我们开始查看验证结果,并开始在此基础上进行更改,我们就开始偏向验证集。现在我们得到了训练结果,这是机器偏差的产物,我们得到了验证结果,这是我们偏差的产物。换句话说,我们得到的模型并不具有一个很好的广义模型的性质。相反,它偏离了一般化。所以记住这一点非常重要。

关于更先进的全自动(无监督)超参数优化方法的关键点在于首先解决这两个问题。一旦这两个问题解决了——是的,有办法做到这一点——最终的指标将需要作为一个单一的分数来实现。然后,该分数成为超参数优化过程被优化的度量。否则,世界上没有任何算法会有所帮助,因为它会优化我们所追求的东西。我们又在找什么?将完成预测任务所阐述的任务的模型。不仅仅是一个模型用于一种情况(这在涉及该主题的论文中经常出现),而是所有类型的模型,用于所有类型的预测任务。这就是像 Keras 这样的解决方案所允许我们做的,任何使用像 Keras 这样的工具来实现过程自动化的尝试都应该接受这个想法。

我用了什么工具?

对于本文中的所有内容,我使用 Keras 作为模型,Talos 是我构建的超参数优化解决方案。好处是它公开了 Keras,而没有引入任何新的语法。它让我可以在几分钟内完成过去需要几天才能完成的事情,而不是痛苦的重复。

你可以自己试试:

pip install talos

或者在这里看代码/文件

但是我想分享的信息,以及我想表达的观点,与一个工具无关,而是与过程有关。你可以按照你喜欢的任何方式遵循同样的程序。

自动化超参数优化和相关工具的一个更突出的问题是,您通常会远离您习惯的工作方式。与所有复杂问题一样,成功的预测任务无关超参数优化的关键在于拥抱人机合作。每一个实验都是一个学习更多关于实践(深度学习)和技术(在这个例子中是 Keras)的机会。这个机会不应该以牺牲过程自动化为代价而被错过。与此同时,我们应该能够去掉这一过程中明显多余的部分。想象一下在 Jupyter 中执行 shift-enter 几百次,并在每次迭代之间等待一两分钟。总之,在这一点上,我们的目标不应该是以全自动的方式找到正确的模型,而是尽量减少给人带来负担的程序冗余。机器自己运转,而不是机械地操作机器。我不是一个一个地分析各种模型配置的结果,而是想以千为单位或者以十万为单位进行分析。一天有 80000 多秒,在那段时间里可以覆盖很多参数空间,而不用我做任何事情。

我们开始扫描吧

为了举例,我将首先提供我在本文所涉及的整个实验中使用的代码。我使用的数据集是 威斯康星乳腺癌 数据集。

一旦定义了 Keras 模型,就该决定初始参数边界了。然后,字典以这样一种方式被输入到处理过程中,即单个排列被挑选一次,然后被忽略。

根据我们希望包含在扫描中的损失、优化器和激活,我们需要首先从 Keras 导入这些函数/类。接下来,模型和参数准备好了,就该开始实验了。

请注意,我不会分享更多的代码,因为我所做的只是更改了参数字典中与以下部分提供的见解相关的参数。为了完整起见,在文章的最后,我将分享一个包含代码的笔记本的链接。

因为在第一轮实验中有许多排列(总共超过 180,000 个),所以我随机选取了总数的 1%,这样我们就剩下 1,800 个排列了。

在这种情况下,我正在用一台 2015 年的 MacBook Air 跑步,看起来我正好有时间见一个朋友,喝杯咖啡(或两杯)。

超参数扫描可视化

对于这篇文章,使用威斯康星州乳腺癌数据集,我已经建立了实验,假设没有关于最佳参数或数据集的知识。我通过删除一列并转换所有其他列来准备数据集,以便每个特征的平均值为 0,标准偏差为 1。

在最初运行 1800 个排列后,是时候看看结果并决定如何限制(或改变)参数空间了。

一个简单的等级顺序关联显示 lr(学习率)对我们的性能度量有最强的影响,在这种情况下是 val_acc(验证准确性)。对于这个数据集,val_acc 是可以的,因为有很多正值。对于假阳性之间存在显著差异的数据集,准确性不是一个好的衡量标准。似乎隐藏层、学习率和辍学率都与 val_acc 显著负相关。一个简单的网络将在这项任务中做得更好。对于正相关,时代的数量是唯一突出的。让我们靠近一点看。在下图中,我们在 x 轴上显示了时期(50、100 和 150),在 y 轴上显示了 val_acc,在列中显示了学习率,在色调中显示了辍学。这种趋势似乎大体上如相关性所暗示的那样;较小的辍学者比较大的辍学者表现更好。

另一种观察漏失的方法是通过核密度估计。这里我们可以看到,在压差为 0 或 0.1 的情况下,val_acc 稍有增加的趋势,而 val_acc 较低的趋势(约为 0.6)。

下一轮扫描的第一个行动项目是完全消除较高的辍学率,并关注 0 到 0.2 之间的值。接下来让我们更仔细地看看学习率。请注意,学习率在优化器之间被标准化为一个范围,其中 1 表示该优化器的 Keras 默认值。

情况相当清楚;较小的学习速率对两种损失函数都很有效,并且这种差异在 logcosh 中尤其明显。但是因为二进制交叉熵在所有学习速率水平上都明显优于其他水平,所以在实验的剩余部分,它将是我们选择的损失。尽管如此,仍然需要进行健全性检查。如果我们看到的不是对训练数据的过度拟合呢?如果 val_loss 到处都是,而我们只是看着画面的一面而忘乎所以,怎么办?简单的回归分析表明事实并非如此。除了一些异常值,所有东西都很好地打包在左下角我们想要的地方。趋势是训练和验证损失都接近于零。

我认为现在我们知道的已经足够了;是时候设置下一轮实验了!作为参考,下一个实验的参数空间如下所示:

除了细化学习速率、放弃和批量大小的界限,我还添加了 kernel_initializer 的 uniform。请记住,在这个阶段,目标是了解预测任务,而不是过于专注于寻找解决方案。这里的关键点是,除了了解具体的预测挑战之外,还要进行实验并了解整个过程。

第 2 轮—增加对结果的关注

最初,我们越少关注结果(更多关注过程),就越有可能获得好的结果。就像下棋一样;如果一开始你太专注于赢得比赛,你就不会专注于开局和中局。竞技棋局赢在残局,基于打好开局和中局。如果一切顺利,超参数优化过程中的第二次迭代就是中间。我们还没有完全专注于赢得比赛,但它有助于着眼于奖励。在我们的例子中,来自第一轮的结果(94.1%的验证准确性)表明,对于给定的数据集和设置的参数边界,存在要进行的预测。

这种情况下,这里的预测任务就是说乳腺癌是良性的还是恶性的。这种类型的预测是一件大事,因为假阳性和假阴性都很重要。预测错误会对这个人的生活产生一些负面影响。如果你感兴趣,有一堆关于这个数据集的论文,以及其他一些相关信息,你都可以在这里找到。

第二轮的结果是 96%的验证准确性。下面的相关性表明,在这一点上唯一突出的是纪元的数量,所以对于第三轮,这是我要改变的一件事。

如果你只看相关性,就有在更大的图景中遗漏某些东西的危险。在超参数优化中,重要的是给定参数中的单个值,以及它们与所有其他值的相互联系。既然我们已经消除了 logcosh 损失函数,并且在参数空间中只有一个损失(binary_crossentropy ),我想了解一下不同的优化器在各个时期的上下文中是如何执行的。

这就像这种相关性所暗示的时代(现在在 x 轴上)。因为 RMSprop 在 100 和 150 中表现不佳,所以让我们在下一轮也放弃它。

在继续之前,让我们非常简要地考虑一个与超参数优化相关的基本问题,作为优化挑战。我们想要达到的目标是什么?答案可以用两个简单的概念来概括:

  • 最佳预测
  • 结果熵

最佳预测是指我们有一个既精确又通用的模型。结果熵是熵尽可能接近零(最小)的地方。结果熵可以理解为一个结果集内所有结果之间相似性的度量(一轮经历 n 次排列)。理想的情况是,预测最佳值为 1,这是 100%的预测性能和 100%的通用性,得到的熵为 0。这意味着无论我们在超参数空间内做什么,每次都只能得到完美的结果。由于几个原因,这是不可行的,但是有助于记住优化超参数优化过程的目标。回答这个问题的另一种方式是通过三个层次的考虑;

  1. 预测任务,目标是找到为该任务提供解决方案的模型
  2. 超参数优化任务,目标是找到预测任务的最佳模型(用最少的努力)
  3. 超参数优化任务优化任务,其目标是找到最佳方法,以最佳方法找到预测任务的最佳模型

你可能会问,这是否会导致我们无限前进,在优化器之上还需要优化器,答案是肯定的。在我看来,超参数优化问题之所以有趣,是因为它引导我们找到了“构建模型的模型”问题的解决方案但是这将使我们远离本文的范围。

考虑到第二个方面,尤其是第三个方面,我们需要考虑该过程的计算效率。我们浪费的计算资源越少,我们就有越多的计算资源来寻找第一和第二方面的最佳结果。从这个角度考虑下面的图表。

从把资源分配到我们需要的地方的意义上来说,第二轮 KDE 看起来要好得多。它们在 x 轴上更接近于 1,并且很少向 0“溢出”。无论扫描的计算资源是什么,它们都在做重要的工作。这里的理想图像是一条 x 值为 1 的直线。

第 3 轮—概括和绩效

让我们开门见山吧。峰值验证准确率现在是 97.1%,看起来我们正朝着正确的方向前进。我犯了一个错误,仅仅增加了 175 个历元作为最大值,并且基于下面的;看来我们必须走得更远。至少在这种配置下。这让我想到…也许在最后一轮,我们应该尝试一些令人惊讶的东西。

正如在前言中所讨论的,考虑一般化也很重要。每当我们看到结果,我们的洞察力就会开始影响实验。最终结果是,我们开始得到不太通用的模型,这些模型可以很好地处理验证数据集,但可能无法很好地处理“真实”数据集。在这种情况下,我们没有很好的方法来测试这种偏差,但至少我们可以采取措施,以我们所拥有的来评估伪泛化的程度。先看训练和验证准确性。

即使这并没有给我们一个肯定的确认,有一个很好的概括的模型,事实上,它离它还差得很远;回归分析结果好不了多少。那我们再来看亏损。

就更好了。事情看起来不错。对于最后一轮,我将增加历元的数量,但我也将尝试另一种方法。到目前为止,我只有非常小的批量,这需要很多时间来处理。在第三轮中,我只包括了批次大小 1 到 4。接下来,我将投入 30 左右,看看会有什么效果。

关于提前停止的几句话。Keras 通过提前停止功能提供了一种非常方便的使用回调的方法。你可能已经注意到了,我没有用那个。一般来说,我会推荐使用它,但它并不像我们到目前为止所做的那样微不足道。以不限制您找到最佳可能结果的能力的方式获得正确的设置并不简单。最重要的方面与度量有关;我希望首先创建一个自定义指标,然后使用它作为我的早期停止模式(而不是使用 val_acc 或 val_loss)。也就是说,早期停止和一般的回调提供了一种非常强大的方式来增加您的超参数优化过程。

第 4 轮—最终结果出来了

在深入研究结果之前,让我们再来看一个上一轮结果的图像。这次是五维的。我想看到其余的参数——内核初始化器、批量大小、隐藏层和时期——都在同一张图片上,与验证准确性和损失进行比较。第一准确性。

基本上是不分上下,但有些事情确实很突出。第一件事是,如果一个隐藏层值(色调)下降,在大多数情况下,它的一个隐藏层。对于批量大小(列)很难说,对于内核初始化器(行)也是如此。接下来让我们看看 y 轴上的验证损失,看看我们是否能从中了解更多。记住,这里我们寻找的是较小的值;我们试图用每个参数排列来最小化损失函数。

统一内核初始化器在保持所有时期、批量大小和隐藏层变化的损失方面做得很好。但是因为结果有点不一致,所以我会保留两个初始化器直到最后。

获胜者是…

获胜的组合是来自最后一刻的想法,尝试更大的批量以节省时间和更少的时期):

小批量的最高结果是验证准确率为 97.7%。使用较大批量的方法,还有一个好处是模型收敛得非常快。在这篇文章的最后,我会提供一个视频,你可以自己看。老实说,一旦我看到更大的批量是如何工作的,我就建立了一个单独的测试。设置它只花了不到一分钟的时间,因为我需要改变的只是批量大小(对于这个较小的时期),扫描在 60 分钟内完成。关于情节,没有什么可看的,因为几乎所有的结果都接近 100%。还有一件事我想分享,因为它从一个不同于我们已经讨论过的角度,与熵的概念有关。熵可以是评估过度拟合的有效方法(因此是泛化的代理)。在这种情况下,我使用 KL 散度分别针对训练损失和准确度来测量 val_loss 和 val_acc 熵。

过程总结

  • 尽可能简单而广泛地开始
  • 试着尽可能多地了解实验和你的假设
  • 对于第一次迭代,尽量不要太关注最终结果
  • 确保您的绩效指标是正确的
  • 请记住,性能是不够的,因为它会使您偏离通用性
  • 每次迭代应该减少参数空间和模型复杂性
  • 不要害怕尝试,这毕竟是一个实验
  • 使用你能理解的方法,例如清晰直观的描述性统计

这里是最后一轮的代码完成笔记本。还有我答应过的视频…

感谢您的宝贵时间!如果你还有几秒钟,请分享。并在寻找最佳参数的过程中享受乐趣!

基于遗传算法的 XGBoost 超参数整定

原文:towardsdatascience.com/hyperparame…

简介

维基百科中定义的遗传算法,其灵感来自于查尔斯·达尔文提出的自然选择过程。在更一般的术语中,我们可以理解自然过程以及它如何与遗传算法相关联,使用下面的描述:

我们从初始群体开始,它将具有某些特征,如图 1 所示。该初始群体将在特定环境中进行测试,以观察该群体中的个体(父母)基于预定义的适合度标准表现如何。在机器学习的情况下,适合度可以是任何性能度量——准确度、精确度、召回率、F1 分数、auc 等等。基于适应值,我们选择表现最好的父母(“适者生存”),作为幸存的群体(图 2)。

(Image by author)

(Image by author)

现在幸存群体中的父母将通过结合两个步骤来交配产生后代:交叉/重组和突变。在杂交的情况下,来自交配父母的基因(参数)将被重组,以产生后代,每个孩子从每个父母那里继承一些基因(参数)(图 3)。

(Image by author)

最后,在突变的情况下,基因(参数)的一些值将被改变以保持遗传多样性(图 4)。这使得自然/遗传算法通常能够得到更好的解决方案。

(Image by author)

图 5 显示了第二代人口,包括幸存的父母和孩子。我们保留幸存的双亲,以便保留最佳适应度参数,以防后代的适应度值不如双亲。

(Image by author)

遗传算法模块为XGBoost:

我们将创建一个为 XGBoost 定制的遗传算法模块。以下是 XGboost 的描述:

XGBoost 是一个优化的分布式梯度增强库,旨在高效**、灵活便携。它在梯度提升框架下实现机器学习算法。**

该模块将具有遵循四个步骤的函数:(I)初始化,(ii)选择,(iii)交叉,和(iv)变异,类似于上面讨论的内容(该代码的一小部分灵感来自帖子这里)。

初始化:

第一步是初始化,参数被随机初始化以创建群体。它类似于图 1 所示的第一代人口。下面的代码显示了初始化过程,其中我们生成了一个包含参数的向量。对于 XGBoost,我们选择了 7 个参数进行优化:learning_rate、n_estimators、max_depth、min_child_weight、subsample、colsample_bytree 和 gamma。这些参数的详细描述可以在这里找到。

def initilialize_poplulation(numberOfParents):
    learningRate = np.empty([numberOfParents, 1])
    nEstimators = np.empty([numberOfParents, 1], dtype = np.uint8)
    maxDepth = np.empty([numberOfParents, 1], dtype = np.uint8)
    minChildWeight = np.empty([numberOfParents, 1])
    gammaValue = np.empty([numberOfParents, 1])
    subSample = np.empty([numberOfParents, 1])
    colSampleByTree =  np.empty([numberOfParents, 1])for i in range(numberOfParents):
        print(i)
        learningRate[i] = round(random.uniform(0.01, 1), 2)
        nEstimators[i] = random.randrange(10, 1500, step = 25)
        maxDepth[i] = int(random.randrange(1, 10, step= 1))
        minChildWeight[i] = round(random.uniform(0.01, 10.0), 2)
        gammaValue[i] = round(random.uniform(0.01, 10.0), 2)
        subSample[i] = round(random.uniform(0.01, 1.0), 2)
        colSampleByTree[i] = round(random.uniform(0.01, 1.0), 2)

    population = np.concatenate((learningRate, nEstimators, maxDepth, minChildWeight, gammaValue, subSample, colSampleByTree), axis= 1)
    return population

参数的限制要么基于 XGBoost 文档中描述的限制,要么基于合理的猜测(如果上限设置为无穷大)。我们首先为每个参数创建一个空数组,然后用随机值填充它。

亲代选择(适者生存)

在第二步中,我们使用初始群体训练我们的模型,并计算适应值。在这种情况下,我们将计算 F1 分数。

def fitness_f1score(y_true, y_pred):
    fitness = round((f1_score(y_true, y_pred, average='weighted')), 4)
    return fitness#train the data annd find fitness score
def train_population(population, dMatrixTrain, dMatrixtest, y_test):
    fScore = []
    for i in range(population.shape[0]):
        param = { 'objective':'binary:logistic',
              'learning_rate': population[i][0],
              'n_estimators': population[i][1], 
              'max_depth': int(population[i][2]), 
              'min_child_weight': population[i][3],
              'gamma': population[i][4], 
              'subsample': population[i][5],
              'colsample_bytree': population[i][6],
              'seed': 24}
        num_round = 100
        xgbT = xgb.train(param, dMatrixTrain, num_round)
        preds = xgbT.predict(dMatrixtest)
        preds = preds>0.5
        fScore.append(fitness_f1score(y_test, preds))
    return fScore

我们将定义我们想要选择多少个父节点,并根据它们的适合度值用所选择的父节点创建一个数组。

#select parents for mating
def new_parents_selection(population, fitness, numParents):
    selectedParents = np.empty((numParents, population.shape[1])) #create an array to store fittest parents

    #find the top best performing parents
    for parentId in range(numParents):
        bestFitnessId = np.where(fitness == np.max(fitness))
        bestFitnessId  = bestFitnessId[0][0]
        selectedParents[parentId, :] = population[bestFitnessId, :]
        fitness[bestFitnessId] = -1 #set this value to negative, in case of F1-score, so this parent is not selected again
    return selectedParents

交叉

遗传算法 ,)的情况下,有多种方法定义交叉,如单点、两点和 k 点交叉,均匀交叉和有序列表交叉。我们将使用均匀交叉,其中孩子的每个参数将基于某种分布从父母中独立选择。在我们的例子中,我们将使用来自 numpy 随机函数的“离散均匀”分布。

'''
Mate these parents to create children having parameters from these parents (we are using uniform crossover method)
'''
def crossover_uniform(parents, childrenSize):

    crossoverPointIndex = np.arange(0, np.uint8(childrenSize[1]), 1, dtype= np.uint8) #get all the index
    crossoverPointIndex1 = np.random.randint(0, np.uint8(childrenSize[1]), np.uint8(childrenSize[1]/2)) # select half  of the indexes randomly
    crossoverPointIndex2 = np.array(list(set(crossoverPointIndex) - set(crossoverPointIndex1))) #select leftover indexes

    children = np.empty(childrenSize)

    '''
    Create child by choosing parameters from two parents selected using new_parent_selection function. The parameter values
    will be picked from the indexes, which were randomly selected above. 
    '''
    for i in range(childrenSize[0]):

        #find parent 1 index 
        parent1_index = i%parents.shape[0]
        #find parent 2 index
        parent2_index = (i+1)%parents.shape[0]
        #insert parameters based on random selected indexes in parent 1
        children[i, crossoverPointIndex1] = parents[parent1_index, crossoverPointIndex1]
        #insert parameters based on random selected indexes in parent 1
        children[i, crossoverPointIndex2] = parents[parent2_index, crossoverPointIndex2]
    return children

突变

最后一步将是通过随机选择一个参数并以一个随机量改变它的值,在孩子中引入多样性。我们还将引入一些限制,以便将改变的值限制在一定的范围内。跳过这些约束可能会导致错误。

def mutation(crossover, numberOfParameters):
    #Define minimum and maximum values allowed for each parameterminMaxValue = np.zeros((numberOfParameters, 2))

    minMaxValue[0:] = [0.01, 1.0] #min/max learning rate
    minMaxValue[1, :] = [10, 2000] #min/max n_estimator
    minMaxValue[2, :] = [1, 15] #min/max depth
    minMaxValue[3, :] = [0, 10.0] #min/max child_weight
    minMaxValue[4, :] = [0.01, 10.0] #min/max gamma
    minMaxValue[5, :] = [0.01, 1.0] #min/maxsubsample
    minMaxValue[6, :] = [0.01, 1.0] #min/maxcolsample_bytree

    # Mutation changes a single gene in each offspring randomly.
    mutationValue = 0
    parameterSelect = np.random.randint(0, 7, 1)
    print(parameterSelect)
    if parameterSelect == 0: #learning_rate
        mutationValue = round(np.random.uniform(-0.5, 0.5), 2)
    if parameterSelect == 1: #n_estimators
        mutationValue = np.random.randint(-200, 200, 1)
    if parameterSelect == 2: #max_depth
        mutationValue = np.random.randint(-5, 5, 1)
    if parameterSelect == 3: #min_child_weight
        mutationValue = round(np.random.uniform(5, 5), 2)
    if parameterSelect == 4: #gamma
        mutationValue = round(np.random.uniform(-2, 2), 2)
    if parameterSelect == 5: #subsample
        mutationValue = round(np.random.uniform(-0.5, 0.5), 2)
    if parameterSelect == 6: #colsample
        mutationValue = round(np.random.uniform(-0.5, 0.5), 2)

    #indtroduce mutation by changing one parameter, and set to max or min if it goes out of range
    for idx in range(crossover.shape[0]):
        crossover[idx, parameterSelect] = crossover[idx, parameterSelect] + mutationValue
        if(crossover[idx, parameterSelect] > minMaxValue[parameterSelect, 1]):
            crossover[idx, parameterSelect] = minMaxValue[parameterSelect, 1]
        if(crossover[idx, parameterSelect] < minMaxValue[parameterSelect, 0]):
            crossover[idx, parameterSelect] = minMaxValue[parameterSelect, 0]    
    return crossover

实施

我们将实现上面讨论的模块,在数据集上进行训练。数据集来自 UCI 机器学习库。它包含一组 102 个分子,其中 39 个被人类识别为具有可用于香水的气味,69 个没有所需的气味。该数据集包含这些分子的 6,590 个低能构象,包含 166 个特征。我们正在做最少的前置处理,作为本教程理解遗传算法的目标。

# Importing the libraries
import numpy as np
import pandas as pd
import geneticXGboost #this is the module we crated above
import xgboost as xgbnp.random.seed(723)# Importing the dataset
dataset = pd.read_csv('clean2.data', header=None)X = dataset.iloc[:, 2:168].values #discard first two coloums as these are molecule's name and conformation's namey = dataset.iloc[:, 168].values #extrtact last coloum as class (1 => desired odor, 0 => undesired odor)# Splitting the dataset into the Training set and Test set
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.20, random_state = 97)# Feature Scaling
from sklearn.preprocessing import StandardScaler
sc = StandardScaler()
X_train = sc.fit_transform(X_train)
X_test = sc.transform(X_test)#XGboost Classifier#model xgboost
#use xgboost API now
xgDMatrix = xgb.DMatrix(X_train, y_train) #create Dmatrix
xgbDMatrixTest = xgb.DMatrix(X_test, y_test)

我们有 8 个父母开始,我们选择 4 个最合适的父母交配。我们将创建 4 代并监控适应度(F1 分数)。下一代中有一半的父母将是从上一代中选出的最适合的父母。这将允许我们保持最好的健康分数至少与上一代相同,以防孩子的健康分数更差。

numberOfParents = 8 #number of parents to start
numberOfParentsMating = 4 #number of parents that will mate
numberOfParameters = 7 #number of parameters that will be optimized
numberOfGenerations = 4 #number of genration that will be created#define the population sizepopulationSize = (numberOfParents, numberOfParameters)#initialize the population with randomly generated parameters
population = geneticXGboost.initilialize_poplulation(numberOfParents)#define an array to store the fitness  hitory
fitnessHistory = np.empty([numberOfGenerations+1, numberOfParents])#define an array to store the value of each parameter for each parent and generation
populationHistory = np.empty([(numberOfGenerations+1)*numberOfParents, numberOfParameters])#insert the value of initial parameters in history
populationHistory[0:numberOfParents, :] = populationfor generation in range(numberOfGenerations):
    print("This is number %s generation" % (generation))

    #train the dataset and obtain fitness
    fitnessValue = geneticXGboost.train_population(population=population, dMatrixTrain=xgDMatrix, dMatrixtest=xgbDMatrixTest, y_test=y_test)
    fitnessHistory[generation, :] = fitnessValue

    #best score in the current iteration
    print('Best F1 score in the this iteration = {}'.format(np.max(fitnessHistory[generation, :])))#survival of the fittest - take the top parents, based on the fitness value and number of parents needed to be selected
    parents = geneticXGboost.new_parents_selection(population=population, fitness=fitnessValue, numParents=numberOfParentsMating)

    #mate these parents to create children having parameters from these parents (we are using uniform crossover)
    children = geneticXGboost.crossover_uniform(parents=parents, childrenSize=(populationSize[0] - parents.shape[0], numberOfParameters))

    #add mutation to create genetic diversity
    children_mutated = geneticXGboost.mutation(children, numberOfParameters)

    '''
    We will create new population, which will contain parents that where selected previously based on the
    fitness score and rest of them  will be children
    '''
    population[0:parents.shape[0], :] = parents #fittest parents
    population[parents.shape[0]:, :] = children_mutated #children

    populationHistory[(generation+1)*numberOfParents : (generation+1)*numberOfParents+ numberOfParents , :] = population #srore parent information

最后,我们得到最佳分数和相关参数:

#Best solution from the final iterationfitness = geneticXGboost.train_population(population=population, dMatrixTrain=xgDMatrix, dMatrixtest=xgbDMatrixTest, y_test=y_test)
fitnessHistory[generation+1, :] = fitness#index of the best solution
bestFitnessIndex = np.where(fitness == np.max(fitness))[0][0]#Best fitness
print("Best fitness is =", fitness[bestFitnessIndex])#Best parameters
print("Best parameters are:")
print('learning_rate', population[bestFitnessIndex][0])
print('n_estimators', population[bestFitnessIndex][1])
print('max_depth', int(population[bestFitnessIndex][2])) 
print('min_child_weight', population[bestFitnessIndex][3])
print('gamma', population[bestFitnessIndex][4])
print('subsample', population[bestFitnessIndex][5])
print('colsample_bytree', population[bestFitnessIndex][6])

现在让我们想象一下每一代人在健康方面的变化(下图)。虽然我们已经从高 F1 分数(~0.98)开始,但在随机产生的初始群体中的两个亲本中,我们能够在最终一代中进一步改进它。初始群体中父母一方的最低 F1 值为 0.9143,最终世代中父母一方的最高 F1 值为 0.9947。这表明我们可以通过遗传算法的简单实现来改进 XGBoost 中的性能指标。最终代码可以在我的 github 账号找到。它还包含允许您观察每一代中各种参数的变化的代码。

(Image by author)

Python 中随机森林的超参数调优

原文:towardsdatascience.com/hyperparame…

改进随机森林第二部分

因此,我们建立了一个随机森林模型来解决我们的机器学习问题(也许通过遵循这个端到端指南),但我们对结果并不太满意。我们有什么选择?正如我们在这个系列的第一部分的中看到的,我们的第一步应该是收集更多的数据并执行特征工程。收集更多的数据和特征工程通常在投入的时间和提高的性能方面有最大的回报,但是当我们用尽所有数据源时,是时候继续进行模型超参数调整了。这篇文章将关注使用 Scikit-Learn 工具优化 Python 中的随机森林模型。虽然这篇文章建立在第一部分的基础上,但它完全独立,我们将涵盖许多广泛适用的机器学习概念。

One Tree in a Random Forest

我在本文中包含了 Python 代码,这是最有启发性的。完整的代码和数据可以在 Github 页面上找到。

超参数调谐的简要说明

思考超参数的最佳方式就像可以调整以优化性能的算法的设置,就像我们可能转动 AM 收音机的旋钮以获得清晰的信号一样(或者你的父母可能有!).虽然模型参数是在训练期间学习的,例如线性回归中的斜率和截距,但是超参数必须由数据科学家在训练之前设置。在随机森林的情况下,超参数包括森林中决策树的数量以及分割节点时每棵树考虑的特征的数量。(随机森林的参数是用于分割在训练期间学习的每个节点的变量和阈值)。Scikit-Learn 为所有模型实现了一组合理的默认超参数,但这些参数并不能保证对某个问题是最优的。最好的超参数通常不可能提前确定,而调整模型是机器学习从科学转向基于试错的工程的地方。

Hyperparameters and Parameters

超参数调整更多地依赖于实验结果而不是理论,因此确定最佳设置的最佳方法是尝试许多不同的组合来评估每个模型的性能。然而,仅在训练集上评估每个模型会导致机器学习中最基本的问题之一:过拟合

如果我们为训练数据优化模型,那么我们的模型将在训练集上得分很高,但是将不能推广到新数据,例如在测试集中。当一个模型在训练集上表现很好,但在测试集上表现很差时,这被称为过度拟合,或者本质上是创建一个非常了解训练集但不能应用于新问题的模型。这就像一个学生背了课本上的简单问题,却不知道如何在杂乱的现实世界中应用概念。

过度拟合模型在训练集上可能看起来令人印象深刻,但在实际应用中却毫无用处。因此,超参数优化的标准程序通过交叉验证解决过拟合问题。

交互效度分析

交叉验证(CV)技术最好用最常用的方法 K 倍 CV 举例说明。当我们处理机器学习问题时,我们确保将我们的数据分成训练集和测试集。在 K-Fold CV 中,我们进一步将我们的训练集分成 K 个子集,称为折叠。然后,我们迭代拟合模型 K 次,每次在第 K-1 个褶皱上训练数据,并在第 K 个褶皱上评估(称为验证数据)。例如,考虑拟合 K = 5 的模型。第一次迭代我们在前四次折叠上训练,在第五次折叠上评估。第二次我们在第一、第二、第三和第五次折叠时进行训练,在第四次折叠时进行评估。我们重复这个过程 3 次以上,每次评估不同的折叠。在训练的最后,我们对每个折叠的性能进行平均,以得出模型的最终验证指标。

5 Fold Cross Validation (Source)

对于超参数调整,我们对整个 K-Fold CV 过程进行多次迭代,每次都使用不同的模型设置。然后,我们比较所有的模型,选择最好的一个,在完整的训练集上训练它,然后在测试集上进行评估。这听起来是一个非常乏味的过程!每当我们想要评估一组不同的超参数时,我们必须将我们的训练数据分成 K 份,并训练和评估 K 次。如果我们有 10 组超参数,并且使用 5 重 CV,则代表 50 个训练循环。幸运的是,与机器学习中的大多数问题一样,有人已经解决了我们的问题,并且可以在 Scikit-Learn 中自动实现 K-Fold CV 的模型调整。

Scikit-Learn 中的随机搜索交叉验证

通常,我们对最佳超参数只有一个模糊的概念,因此缩小搜索范围的最佳方法是评估每个超参数的大范围值。使用 Scikit-Learn 的 RandomizedSearchCV 方法,我们可以定义一个超参数范围网格,并从网格中随机采样,对每个值组合执行 K 倍 CV。

在我们进入模型调整之前,简单回顾一下,我们正在处理一个监督回归机器学习问题。我们试图使用过去的历史天气数据来预测我们城市(华盛顿州西雅图市)明天的温度。我们有 4.5 年的训练数据,1.5 年的测试数据,并使用 6 个不同的特征(变量)来进行预测。(要查看数据准备的完整代码,请参见笔记本)。

让我们快速检查一下特性。

Features for Temperature Prediction

  • temp_1 =前一天的最高温度(华氏度)
  • 平均值=历史平均最高温度
  • ws_1 =前一天的平均风速
  • temp_2 =两天前的最高温度
  • 朋友=来自我们“信任的”朋友的预测
  • 年份=日历年

在以前的帖子中,我们检查了数据以检查异常,我们知道我们的数据是干净的。因此,我们可以跳过数据清理,直接进入超参数调优。

为了查看可用的超参数,我们可以创建一个随机森林并检查默认值。

from sklearn.ensemble import RandomForestRegressorrf = RandomForestRegressor(random_state = 42)from pprint import pprint# Look at parameters used by our current forest
print('Parameters currently in use:\n')
pprint(rf.get_params())**Parameters currently in use:

{'bootstrap': True,
 'criterion': 'mse',
 'max_depth': None,
 'max_features': 'auto',
 'max_leaf_nodes': None,
 'min_impurity_decrease': 0.0,
 'min_impurity_split': None,
 'min_samples_leaf': 1,
 'min_samples_split': 2,
 'min_weight_fraction_leaf': 0.0,
 'n_estimators': 10,
 'n_jobs': 1,
 'oob_score': False,
 'random_state': 42,
 'verbose': 0,
 'warm_start': False}**

哇,这是一个相当压倒性的名单!我们如何知道从哪里开始?一个好地方是 Scikit-Learn 中关于随机森林的文档。这告诉我们最重要的设置是森林中的树的数量(n_estimators)和考虑在每个叶节点进行分裂的特征的数量(max_features)。我们可以去阅读关于随机森林的研究论文,并尝试将最佳超参数理论化,但更有效地利用我们的时间只是尝试各种各样的值,看看什么有效!我们将尝试调整以下一组超参数:

  • n_estimators =前集中的树的数量
  • max_features =分割结点时考虑的最大要素数
  • max_depth =每个决策树中的最大级别数
  • min_samples_split =分割节点前放置在节点中的最小数据点数
  • min_samples_leaf =叶节点中允许的最小数据点数
  • bootstrap =数据点采样方法(有或没有替换)

随机超参数网格

要使用 RandomizedSearchCV,我们首先需要创建一个参数网格,以便在拟合过程中进行采样:

from sklearn.model_selection import RandomizedSearchCV# Number of trees in random forest
n_estimators = [int(x) for x in np.linspace(start = 200, stop = 2000, num = 10)]
# Number of features to consider at every split
max_features = ['auto', 'sqrt']
# Maximum number of levels in tree
max_depth = [int(x) for x in np.linspace(10, 110, num = 11)]
max_depth.append(None)
# Minimum number of samples required to split a node
min_samples_split = [2, 5, 10]
# Minimum number of samples required at each leaf node
min_samples_leaf = [1, 2, 4]
# Method of selecting samples for training each tree
bootstrap = [True, False]# Create the random grid
random_grid = {'n_estimators': n_estimators,
               'max_features': max_features,
               'max_depth': max_depth,
               'min_samples_split': min_samples_split,
               'min_samples_leaf': min_samples_leaf,
               'bootstrap': bootstrap}pprint(random_grid)**{'bootstrap': [True, False],
 'max_depth': [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, None],
 'max_features': ['auto', 'sqrt'],
 'min_samples_leaf': [1, 2, 4],
 'min_samples_split': [2, 5, 10],
 'n_estimators': [200, 400, 600, 800, 1000, 1200, 1400, 1600, 1800, 2000]}**

在每次迭代中,算法将选择特征的不同组合。总共有 2 * 12 * 2 * 3 * 3 * 10 = 4320 个设置!然而,随机搜索的好处是,我们不是在尝试每一种组合,而是随机选择大范围的值进行采样。

随机搜索训练

现在,我们实例化随机搜索,并像任何 Scikit-Learn 模型一样拟合它:

# Use the random grid to search for best hyperparameters
# First create the base model to tune
rf = RandomForestRegressor()
# Random search of parameters, using 3 fold cross validation, 
# search across 100 different combinations, and use all available cores
rf_random = RandomizedSearchCV(estimator = rf, param_distributions = random_grid, n_iter = 100, cv = 3, verbose=2, random_state=42, n_jobs = -1)# Fit the random search model
rf_random.fit(train_features, train_labels)

RandomizedSearchCV 中最重要的参数是 n_iter,它控制要尝试的不同组合的数量,以及 CV,它是用于交叉验证的折叠数(我们分别使用 100 和 3)。更多的迭代将覆盖更宽的搜索空间,更多的 cv 折叠将减少过度拟合的机会,但提高每个将增加运行时间。机器学习是一个需要权衡的领域,性能与时间是最基本的因素之一。

我们可以通过拟合随机搜索来查看最佳参数:

rf_random.best_params_**{'bootstrap': True,
 'max_depth': 70,
 'max_features': 'auto',
 'min_samples_leaf': 4,
 'min_samples_split': 10,
 'n_estimators': 400}**

根据这些结果,我们应该能够缩小每个超参数的取值范围。

评估随机搜索

为了确定随机搜索是否产生了更好的模型,我们将基础模型与最佳随机搜索模型进行比较。

def evaluate(model, test_features, test_labels):
    predictions = model.predict(test_features)
    errors = abs(predictions - test_labels)
    mape = 100 * np.mean(errors / test_labels)
    accuracy = 100 - mape
    print('Model Performance')
    print('Average Error: {:0.4f} degrees.'.format(np.mean(errors)))
    print('Accuracy = {:0.2f}%.'.format(accuracy))

    return accuracybase_model = RandomForestRegressor(n_estimators = 10, random_state = 42)
base_model.fit(train_features, train_labels)
base_accuracy = evaluate(base_model, test_features, test_labels)**Model Performance
Average Error: 3.9199 degrees.
Accuracy = 93.36%.**best_random = rf_random.best_estimator_
random_accuracy = evaluate(best_random, test_features, test_labels)**Model Performance
Average Error: 3.7152 degrees.
Accuracy = 93.73%.**print('Improvement of {:0.2f}%.'.format( 100 * (random_accuracy - base_accuracy) / base_accuracy))**Improvement of 0.40%.**

我们取得了 0.4%的不引人注目的精度改进。不过,根据应用程序的不同,这可能是一个显著的优势。我们可以通过使用网格搜索来集中在随机搜索中找到的最有希望的超参数范围上,从而进一步改进我们的结果。

交叉验证网格搜索

随机搜索允许我们缩小每个超参数的范围。既然我们知道了集中搜索的位置,我们就可以显式地指定要尝试的每种设置组合。我们使用 GridSearchCV 来完成这项工作,这种方法不是从分布中随机抽样,而是评估我们定义的所有组合。要使用网格搜索,我们根据随机搜索提供的最佳值制作另一个网格:

from sklearn.model_selection import GridSearchCV# Create the parameter grid based on the results of random search 
param_grid = {
    'bootstrap': [True],
    'max_depth': [80, 90, 100, 110],
    'max_features': [2, 3],
    'min_samples_leaf': [3, 4, 5],
    'min_samples_split': [8, 10, 12],
    'n_estimators': [100, 200, 300, 1000]
}# Create a based model
rf = RandomForestRegressor()# Instantiate the grid search model
grid_search = GridSearchCV(estimator = rf, param_grid = param_grid, 
                          cv = 3, n_jobs = -1, verbose = 2)

这将尝试 1 * 4 * 2 * 3 * 3 * 4 = 288 种设置组合。我们可以拟合模型,显示最佳超参数,并评估性能:

# Fit the grid search to the data
grid_search.fit(train_features, train_labels)grid_search.best_params_**{'bootstrap': True,
 'max_depth': 80,
 'max_features': 3,
 'min_samples_leaf': 5,
 'min_samples_split': 12,
 'n_estimators': 100}**best_grid = grid_search.best_estimator_
grid_accuracy = evaluate(best_grid, test_features, test_labels)**Model Performance
Average Error: 3.6561 degrees.
Accuracy = 93.83%.**print('Improvement of {:0.2f}%.'.format( 100 * (grid_accuracy - base_accuracy) / base_accuracy))**Improvement of 0.50%.**

看起来我们已经达到了最大的性能,但是我们可以从之前的结果中进一步细化网格,再试一次。代码和以前一样,只是网格不同,所以我只给出结果:

**Model Performance
Average Error: 3.6602 degrees.
Accuracy = 93.82%.****Improvement of 0.49%.**

性能的小幅下降表明我们已经达到了超参数调优的收益递减。我们可以继续,但回报最多也是微乎其微。

比较

我们可以对用于提高性能的不同方法进行一些快速比较,显示每种方法的回报。下表显示了我们所做的所有改进的最终结果(包括第一部分的改进):

Comparison of All Models

Model 是模型的(非常缺乏想象力的)名称,accuracy 是百分比精度,error 是以度为单位的平均绝对误差,n_features 是数据集中的要素数,n_trees 是森林中决策树的数量,time 是以秒为单位的训练和预测时间。

这些模型如下:

  • 平均值:通过预测测试集中每天的历史平均最高温度来计算原始基线
  • 一年:使用一年的数据训练的模型
  • four_years_all:使用 4.5 年的数据和扩展特征训练的模型(详见第一部分)
  • four_years_red:使用 4.5 年的数据和最重要特征的子集训练的模型
  • best_random:交叉验证随机搜索的最佳模型
  • first_grid:交叉验证的第一次网格搜索的最佳模型(被选为最终模型)
  • second_grid:第二次网格搜索的最佳模型

总体而言,收集更多的数据和特征选择将误差降低了 17.69%,而超参数进一步将误差降低了 6.73%。

Model Comparison (see Notebook for code)

就程序员工时而言,收集数据需要大约 6 个小时,而超参数调优需要大约 3 个小时。就像生活中的任何追求一样,在某个点上追求进一步的优化是不值得努力的,知道什么时候停止和能够继续下去一样重要(抱歉,变得如此哲学化)。此外,在任何数据问题中,都有所谓的贝叶斯错误率,这是一个问题中绝对最小的可能错误。贝叶斯误差,也称为可再现误差,是潜在变量、影响我们无法测量的问题的因素以及任何物理过程中的固有噪声的组合。因此,创造一个完美的模型是不可能的。尽管如此,在这个例子中,我们能够通过超参数调整显著改进我们的模型,并且我们涵盖了许多广泛适用的机器学习主题。

训练可视化

为了进一步分析超参数优化的过程,我们可以一次更改一个设置,并查看对模型性能的影响(本质上是进行一个受控实验)。例如,我们可以创建一个包含一定数量的树的网格,执行网格搜索 CV,然后绘制结果。绘制训练和测试误差以及训练时间将允许我们检查改变一个超参数如何影响模型。

首先,我们可以看看改变森林中树木数量的影响。(有关训练和绘图代码,请参见笔记本)

Number of Trees Training Curves

随着树的数量增加,我们的误差减少到一定程度。将树的数量增加到超过 20(我们的最终模型有 100)在准确性方面没有太大的好处,并且训练时间持续增加。

我们还可以检查分割节点的特征数量曲线:

Number of Features Training Curves

随着保留的特征数量的增加,模型的准确性也如预期的那样增加。训练时间也增加了,尽管并不显著。

结合定量统计,这些视觉效果可以让我们很好地了解我们使用不同超参数组合所做的权衡。虽然通常没有办法提前知道什么设置会工作得最好,但这个例子展示了 Python 中的简单工具,这些工具允许我们优化我们的机器学习模型。

一如既往,我欢迎反馈和建设性的批评。可以在 wjk68@case.edu 找到我

调谐超参数(二):火花的随机搜索

原文:towardsdatascience.com/hyperparame…

在本系列的第二部分中,我将继续我的超参数优化策略,这一次我想从 Spark 的角度更仔细地看看。该系列的每个部分都可以单独阅读,请随意查看第一部分

随机搜索和分布式机器学习框架

不管你的算法设计得多好,数学可能有多美,如果客户需要在大量数据上相对较短的训练时间,你最好找到一种方法来提供!

幸运的是,分布式计算是随机搜索果冻的花生酱。让我们提醒一下为什么随机搜索网格搜索效率高?

最大的敌人是维度的诅咒。对于附加的超参数,以及它们各自选择的值,会成倍增加搜索时间。与其固定搜索空间的值,还不如对其进行采样。为了理解这一点,让我们看一下图 1。摘自原文

Figure 1: Grid Search vs Random Search

正如我们所见,在搜索中经常出现的情况,一些超参数比其他的更具决定性。在网格搜索的情况下,尽管采样了 9 次试验,实际上我们只试验了一个重要参数的 3 个不同值。在随机搜索的情况下,9 次试验将测试 9 个不同的决定性参数值。

因为超参数配置的每个样本都是彼此独立绘制的,所以我们可以看到并行化是多么容易!于是,火花来了!

火花

Spark 是一个流行的开源框架,用于在集群上进行分布式计算,提供了一个广泛的库,用于操作数据库、流、分布式图形处理,最重要的是本讨论中的机器学习,即 Spark MLlib。

由于微软 Azure 机器学习团队的工作,Spark MLlib 最近获得了巨大的推动,该团队发布了 MMLSpark 。从实用机器学习的角度来看,MMLSpark 最显著的功能是访问极端梯度增强库 Lighgbm ,这是大多数数据科学概念证明的快速制胜方法。

既然每个数据科学家最喜欢的库都可以在集群上训练,我们只差一个合适的超参数调整框架了。遗憾的是,最初的 Spark MLlib 只有一个网格搜索的实现。MMLSpark 提供了带随机搜索的超调,但遗憾的是采样只有统一**。**

实际上…

均匀采样是一个很好的步骤,但对于许多超参数来说不是最佳的。在 Lightgbm 等极端梯度推进算法的情况下,学习率和正则化超参数浮现在脑海中。这些参数应该在对数尺度上采样,而不是在一个间隔内均匀采样。

那么我们如何黑 Spark MLlib 来满足我们的需求呢?

下面让我们先来看看 Spark 中的关键成分。

正如我们所看到的,超参数值的网格被定义为 ParamMap 类型的数组,来自 ParamGridBuilder 类的一个实例。因此,为了保持与 Spark 的 CrossValidator 兼容,让我们继续并重新定义 build()addGrid 方法。

我们不是在网格中添加一个超参数值列表,而是定义一个分布,稍后我们将从该分布中抽取配置样本。

Breeze是一个流行的用于数值处理的 scala 库,在 breeze.stats.distributions 中有各种各样的分布。 例如,在逻辑回归的情况下,我们可能希望定义以下采样空间:

一方面,我们希望从分布中取样,另一方面,在一组分类选择的情况下,我们应该能够设置一组选择。

我们可以提出以下解决方案,

现在让我们对 LightGBM 进行最后一次测试,

我们走吧!

更多例子的代码可以在 这里 找到。

我写了更多精彩的东西!

@ 调整超参数(第一部分):成功减半

@tensor flow 中的自定义优化器

@XG boost 回归预测区间

参考资料:

  1. J.Bergstra 和 Y. Bengio,超参数优化的随机搜索,2011 年
  2. spark.apache.org/[星火](spark.apache.org/)
  3. github.com/Azure/mmlsp…
  4. 微风,github.com/scalanlp/br…
  5. J.Bergstra,R. Bardenet,Y. Bengio,B. Kégl,超参数优化算法
  6. github:github.com/benoitdesca…

永远不要从假设开始

原文:towardsdatascience.com/hypothesis-…

谎言,该死的谎言,还有 STAT101

设置假设检验是交际舞;它的步骤是动作-动作-世界-世界。有很好的狐步舞节奏。不幸的是,大多数人一开始就搞砸了。以下是如何正确地跳舞。

第一步:写下默认动作

统计学是一门在不确定的情况下改变你想法的科学,所以首要任务是弄清楚你要做什么除非数据说服你放弃。

如果你保持无知,你会做什么?

这就是为什么一切都从一个身体动作/决定开始,如果你没有收集到任何(更多)证据,你就承诺去做。这叫做你的 默认动作

入门是行动,而不是信念。

我问你的是,“如果你走开并且对这些信息一无所知,你实际上会做什么

**“收集数据”不是一个合适的答案。我在敦促你告诉我,如果我现在强迫你选择,你会选择哪个选项。(抱歉我吼了。)

第二步:写下可供选择的行动

你将保持你的决定二元,被框定为做事情不做事情*。哪个不是你默认的就是你的 替代动作 。*

If binary feels too basic, the amazing variety of shapes on your screen speaks volumes of the power of binary options put together. When you need to make a more complex decision, you can compound several hypothesis tests. Let’s start with one at a time.

第一部分不是关于信仰

入门是行动,而不是信念。我不是问你你认为你知道什么,因为作为一个好的常客(又名经典统计学家,大多数 STAT101 课程中教授的哲学的追随者)你在做分析之前不会相信任何事情。

没什么。你什么都不相信。和我一起说。

当谈到这一点时,贝叶斯主义者是不同的,但如果你感到正义的贝叶斯愤怒,因为你在哲学上与这里的逻辑不一致,深呼吸,把这当成了解你的敌人的一课。我们很快就会谈到贝叶斯生活方式。

目前,关于您正在处理哪种统计数据的线索是在行话中流传的。如果你听到“ 置信区间 ”或“ p 值 ”,你好常客。如果你听到“ 可信区间 ”或者“事前”或者“事后”(这不是什么无礼的话,我保证),你好贝叶斯。如果第一种更熟悉,那是因为大多数教育项目在教授贝叶斯思维之前/而不是贝叶斯思维。

处理无信息

对于数字迷来说,选择哪个动作作为你的默认动作并不是一个问题。这是 MBA 的事情,是团队决策者的职权范围。你是在密室里沉思的时候根据商业意识做出这个决定的。

选择默认操作需要商业头脑,这是团队决策者的职责。

我在问你,如果你保持无知*,你更愿意做什么,所以你不需要数据来回答我的问题,尽管你可能会发现之前的分析鼓舞人心*。探索性数据分析 (EDA)是一种引导式冥想,如果你愿意的话。这是帮助决策者通过这一部分的工具。如果你想更深入地了解分析师和决策者是如何合作的,请阅读这篇文章。**

EDA 非常有用…如果你买得起的话。价格是你使用的所有数据,因为在你进入统计部分之前,它必须从轨道被核化。对于没有足够数据的团队来说,从推论中排除任何数据都是非常昂贵的。他们完全受他们的决策者的智力广度和头脑风暴能力的支配。

谨慎行事

想象一个关于推出新产品的决策。决策者中典型的选择是稳扎稳打: 不要启动 it,除非数据给你一个充分的理由按下绿色按钮。如果你没有数据,你会高兴地封存这个项目。也许这是一个错误,但是嘿——你可以心安理得。你选择了默认的方式,使得坚持它是错误中较小的一种。

默认的行动是你在无知下觉得合意的选项。

其他社会认为默认相当明显的例子有无罪-直到被证明有罪(默认=如果没有证据就不定罪),测试新药物(默认=如果没有证据就不批准),以及科学发表(默认=如果没有证据就不发表)。

如果没有默认,就不需要花里胡哨的统计。

虽然真正的冷漠在人类动物中相当罕见,但如果你真的愿意在没有数据的情况下抛硬币,那么你就不需要统计。如果你的想法没定下来,那是无法改变的。往前走,改为读这个。统计推断是针对 不确定性 下的决策。如果你已经有了答案,回家吧。

坦率地说,第一步包括在没有任何信息的情况下框定你的决定,我希望你看到决策者的培训比数学家的培训更相关。

处理全部信息

舞蹈的下一步有点奇怪。STAT101 教你,就像它不是一件小事,但它是一个非常激烈的精神飞跃。你的工作是想象世界上所有可能的状态。是的,你听到了。

这是最艰难的决策任务之一。对于非琐碎的例子(比你在课堂上看到的婴儿例子稍微复杂一点的东西),要做好它确实需要大量的精神训练、创造力、灵活性和专注力。

你的工作是想象世界上所有可能的状态。

一旦你想象了所有可能的平行世界,是时候把它们放入两个桶中的一个了:让我们称桶 1 为“我乐意采取默认行动的世界”,桶 2 为“所有其他的世界”

步骤 3:描述零假设(H0)

如果你不喜欢 Bucket 1 的 10 字名称,它的技术名称是 空假设

统计课教你检验 T21 假设,而不是形成假设。它们往往是在那些考试中为你预先准备好的。

你可能听过对零假设的简略描述,比如“现状”、“无聊的那个”或者“我们不想证明的东西”所有这些都是微妙的不准确,懒惰的事情,一个教授可能会教一个一年级的大学生不值得信赖的心智成熟。但是我相信 能够处理哲学上的古怪,所以现在你知道零假设描述了你乐意选择默认行为的所有宇宙。出于对我们要求决策者处理的心理体操的尊重,让我们默哀片刻。

不是每个人都有缩小视野所需的心理灵活性。明智地选择你的决策者。

让我们快速回顾一下我们的现状。这里的要点是,你已经设置好了事情,所以只要你什么都不知道,你只知道一点点,或者你绝对确定你是一个零假设宇宙的公民,你就致力于做你的默认动作。

假说就像蟑螂。当你看到一个,它永远不会只是一个。附近总有更多的藏身之处。

第四步:描述备选假设(H1)

桶 2 是 的替代假设 你把所有的剩菜都放在那里。当空值为假时,一切都可能为真。这两个假设是数学上的补充,也就是说没有第三个桶。

简而言之,另一个假设就是你对此的答案:

“怎样才能改变你的想法?”

Action (default) -action-worlds-worlds: the dance is complete.

我们已经准备好添加数据,那么接下来要做什么呢?

改变你想法的科学

在它们之间,你的假设涵盖了所有的可能性。它们不重叠。如果我用数据说服你!—你生活在另一个假设世界中……我的天啊,你还在考虑默认动作做什么?停下来。这不是一个快乐的选择。

如果数据让你相信你生活在另一个假设的世界里,那就改变行动。

你最好将默认动作切换到 替代动作 :不做默认。这可能会演变成一系列其他决定,但有一件事是肯定的:你不会碰上违约。数据改变了你的想法!**

主动与被动

这个决策背景的很大一部分是,从一开始,行动对你来说就是 而不是 一样。你像一个常客应该的那样完全开放,但这并不意味着你不认为无知下的某个行为更明智或更道德。这是关键。如果这两个动作对你来说是一样的,读这个代替。

默认行为是你被动接受的行为,而另一种行为是你需要被主动说服去做的事情。

处理部分信息

如果你对你的数据只有部分的了解,你将不得不处理不确定性。这就是花里胡哨的概率计算的用武之地。它们可以归结为一句话,每次都是一样的,我们将在下一章的中看到。

关键是你永远也不会确定哪个世界是你的世界。这就是为什么选择正确反映你价值观的默认行为是很重要的。怎么查?如果你把事情框对了,一个类型 I 错误应该比一个类型 II 错误感觉更糟。换句话说:

错误地离开你舒适的舒适区(默认动作)的想法应该比错误地坚持它的想法更痛苦。

如果这不是真的,那你就没有真正诚实地面对自己。让我们从头再来一遍!

没有魔法能把不确定性变成确定性。

行动最响亮

为了能够建立统计假设,你必须知道你的默认动作是什么。当你从别处开始时,整个事情就会分崩离析。

不幸的是,错误地选择你的默认动作是那些学习数学却没有吸收任何哲学的人的常见错误。这也是一个团队的症状,在这个团队中,决策者在行动中失踪,而数字书呆子们集体出局。

错误地选择默认动作是一个令人痛苦的常见错误。到处都是!

一个让自己注定失败的方法是从假设开始,而不是从行动开始。这是课堂练习结构的遗留问题(因为统计课不会教你决策者的角色,这些事情几乎总是由教授为你做的),但在现实生活中,这相当于出师不利。你将在剩下的时间里投入所有的努力,如果 faceplant 刚出大门,岂不是很可惜?

总是从默认操作开始。

如果你渴望这些例子形式的想法(和外星人!),这里读上。****

如果你更喜欢一个没有统计细微差别的基本例子,读读 这个

Don’t faceplant right out of the gate by starting with the hypotheses, always start with the default action.

感谢阅读!喜欢作者?

如果你渴望阅读更多我的作品,这篇文章中的大部分链接会带你去我的其他思考。不能选择?试试这个:

** [## 我们为什么信任科学家?

现在是时候重新思考我们对事实和虚构的假设了

blog.usejournal.com](blog.usejournal.com/why-do-we-t…)

比起读书更喜欢 YouTube?试试我的 AI 课程!

如果你在这里玩得开心,并且你正在寻找一个为初学者和专家设计的有趣的应用人工智能课程,这里有一个我为你制作的娱乐课程:

Enjoy the entire course playlist here: bit.ly/machinefrie…

喜欢作者?与凯西·科兹尔科夫联系

让我们做朋友吧!你可以在 TwitterYouTubeSubstackLinkedIn 上找到我。有兴趣让我在你的活动上发言吗?用这个表格联系。**

现实生活中的假设检验

原文:towardsdatascience.com/hypothesis-…

用数学解决现实世界的问题

Photo by JESHOOTS.COM on Unsplash

每天你都在测试想法、食谱、新路线,这样你就可以更快或更少的交通到达目的地…

然而,重要的问题是这个想法/配方/路线比你之前的那个好吗?

现在是星期五晚上,你想看电影。有三部电影吸引了你的眼球,但你并不确定它们是好是坏。

在当今这个时代,你是那种仍然依赖家人和朋友推荐的人。所以,你让他们给这些电影评级,并准备好处理数据。

The beauty of dummy data 😀

即使看起来你的朋友对表情电影有点怀疑,你也需要检查每个评分分布,以便更多地了解你朋友投票的主要趋势。

收视率为星际**

评分为的表情符号电影

  • 2 个单位的中位数;
  • 平均值约为 2.2 个单位;
  • 标准偏差约为 0.59 个单位;

《星球大战:最后的绝地武士》的收视率

  • 5 个单位的中位数;
  • 平均值约为 4.5 个单位;
  • 标准偏差约为 0.62 个单位;

这太棒了!但实际上它并没有告诉你比你已经知道的更多的东西:表情符号电影可能没有那么吸引人,星际大战和星球大战之间有明显的竞争…

为了弄清你的朋友对哪部电影评价最高,你决定进行一些 统计测试 并比较三种评价分布。

假设检验

假设检验,或称统计假设检验,是一种用于比较两个数据集或数据集样本的技术。这是一种统计 推断方法所以,在测试的最后,你会得出一个结论——你会推断出一些东西——关于你正在比较的东西的特征。

就你周五晚上的电影选择而言,你想从三种可能中挑选一部最佳电影。

你应该使用哪种测试?

为了回答这个问题,首先你需要知道遵循什么分布*。因为,不同的测试假设数据遵循特定的分布。*

您已经计算了一些统计数据——评级数据——均值、中值和标准差——但是您的数据是什么样的呢?

最著名的分布之一就是所谓的钟形曲线*、正态分布 。在此分布中,数据以平均值为中心,您可以通过钟形曲线的峰值来识别该平均值。在这种情况下,它也对应于中间的值,即中值。*

根据标准偏差,正态分布的数据点分布在平均值/中值周围。

Example of a dataset that follows a Normal Distribution with mean 0 and standard deviation of 1

在这个正态分布的例子中,很容易看到大多数值都以零为中心,即分布的平均值和中值,并且曲线的两侧以 1 个单位的增量从平均值向外移动。

电影收视率服从正态分布吗?

令人欣慰的是,统计学家已经考虑过识别你的数据的形状。他们创造了一个简单的方法来找出答案:分位数-分位数图,也叫 Q-Q 图。

A dataset that follows a Normal Distribution and the Q-Q plot that compares it with the Normal Distribution

Q-Q 图有助于可视化两个概率分布的分位数。分位数只是一种简单的方式来说明你正在把分布分成相等的部分。例如 quart iles,将分配分成四等份,4 等份。

这个 Q-Q 剧情怎么看?

在上面的例子中,我们已经知道数据集遵循正态分布。

Q-Q 图想要直观地表示的是,如果两个数据集遵循相同的分布,它们将大致沿着红色对角线对齐。对应于数据集的蓝点偏离对角线越多,对应于要比较的分布,两个分布之间的差异就越大。

因此,为了弄清楚每个电影评级数据集遵循什么样的分布,您可以使用 Q-Q 图将它们与正态分布进行比较。

Distribution of each movie rating and corresponding Q-Q plot vs Normal Distribution

第一个想到的可能是这看起来一点都不像我期待的 Q-Q 剧情!嗯,算是吧。**

数据点沿着对角线分布,但是,它不完全沿着红线的原因是因为评级是离散值,而不是连续值。这就是为什么我们看到,例如,在《星球大战》的评级中,一些蓝点与值 4 水平对齐,在红线的顶部,然后,再往上,一些点与值 5 对齐。

因此,您可以证明它遵循正态分布,因为,尽管是以离散的、逐步的方式,数据遵循对角线。

既然您已经发现您的评级遵循正态分布,那么是时候进行统计测试了。

还没有。

在考虑使用什么测试之前,你需要

  1. 定义你的假设;
  2. 设置统计测试的显著性水平。

那你挑统计测试就好了!

1.定义你的假设

假设检验通常由以下部分组成

  • 零假设 (H0,读作“H 零”):陈述所有事物保持不变。没有现象被观察到,或者在你比较的事物之间没有关系;
  • 替代假设 (H1,读“H one”):陈述零假设的反面。你所比较的事物之间有一些变化或观察到的关系

对于你周五的电影之夜,你真正想知道的是一部电影是否明显比其他的好。在这种情况下,你可以根据你的朋友对每部电影的平均评价来建立你的假设。

可以解读为零假设* (H0): 电影 A 的均值等于电影 B 的均值替代假设 (H1): 电影 A 的均值不等于电影 B 的均值*

2.设置统计测试的显著性水平

统计测试的目标是试图证明存在一个可观察到的现象。

统计测试的目标是试图拒绝零假设,即没有可观察到的变化或行为

它可能是证明显示患者健康改善的治疗、具有较大人群特征的样本或被认为不同的两个数据集,即它们不可能来自同一人群。所以,在测试的最后,你要有信心拒绝零假设。

这导致定义测试的显著性水平。

它被描述为一种概率,用希腊字母 alpha 表示,它指定了当零假设实际上为真时拒绝零假设的概率,即,您无法观察到问题中的现象或变化。

我认为显著性水平是为你的测试设定质量标准,以便能够得出准确的结论。

在你周五晚上寻找电影的过程中,没有找到一部好电影看的后果很小:一些潜在的浪费时间,和一点挫败感。但是,您可以看到在临床试验等场景中设置适当的显著性水平的重要性,在这些场景中,您正在测试一种新药或治疗方法。

通常使用的显著性水平是 1%和 5%。

对于这个电影夜选,我们可以定在 5%,即α= 0.05。

用什么统计检验?

知道数据遵循正态分布,并且您想要比较您朋友的评分平均值,一个特殊的统计测试浮现在脑海中。

学生的 t-Test

这种统计检验通常用于验证两个数据集之间是否存在显著差异。正如我前面提到的,首先您必须保证两个数据集都具有以下特征

  • 遵循正态分布;
  • 是相互独立的。

让我们假设你的朋友在给每部电影评分时没有偏见,以便给予完全独立的评分。

从我们到目前为止所看到的,你很好地使用学生的 t 测试!

为了验证其中一部电影是否明显优于另一部,你可以进行一个独立双样本 t 检验。这个测试也必须是一个双尾测试*,因为我们试图捕捉一个普遍的显著差异,或者更低或者更高。想想正态分布图的“尾部”。*

准备好运行 t 测试了吗?等等,还没有!

您所有的朋友对不同的电影进行了评级,但是,正如您之前所验证的,每部电影的评级分布都有不同的标准差。这意味着每个分布都有不同的方差。

因为分布的方差是不一样的,你必须使用一个稍微不同的测试,韦尔奇的 t-测试。

韦尔奇 t 检验

这也被称为不等方差 t 检验*。这是对学生 t 检验的一种改进,仍然要求数据呈正态分布。但是,在计算测试时,它会考虑这两个差异。*

分子考虑了两个均值之间的差异,由 X1 和 X2 表示,而分母考虑了方差,由 s 和每个数据集的大小 N 表示。

在周五晚上的电影示例中,两部电影的数据集大小将是相同的,因为你所有的朋友都对这三部电影进行了评级。但是通过 Welch 的 t-test,我们确保在验证评级之间是否存在显著差异时,每个评级分布的方差都被考虑在内。

使用 Welch 的 t-Test,对于每一对分布中的每一个,您计算出测试统计量,它是每个统计软件在您运行测试时生成的。

现在,显著性水平又回到了行动上,因为您已经准备好对数据做出结论了。

除了测试统计数据,您选择的软件还将为您提供 p 值。p 值也表示为概率,是在假设零假设为真的情况下,观察到与检验统计值一样极端的值的概率。

在这个周五电影之夜的场景中,p 值将是平均评分比我们比较的评分高或低的概率。

你现在有了拼图的所有部分!

您运行了测试,获得了测试统计数据和 p 值,现在您可以使用 p 值和显著性水平来确定数据集之间是否存在统计显著性差异。

用你选择的统计软件处理所有数据,你会得到以下结果

星际大战表情符号电影

  • 检验统计~= 15.07
  • p 值= 1.833202972237421e-25

表情符号电影 vs 星球大战:最后的绝地

  • 检验统计~= -18.54
  • p 值= 3.4074007278724943e-32

看看上面测试统计的绝对值,考虑到它们是如此之大,你可以得出结论,这两部电影之间有显著的差异。

将显著性水平与每个 p 值进行比较,您可以安全地拒绝零假设,即这些电影的平均评级之间没有差异。

这适用于星际大战表情符号电影表情符号电影大战星球大战:最后的绝地*,因为在这两种情况下,p 值都远远小于我们在运行测试之前设置的 0.05 的显著性水平。*

你刚刚得出结论,表情符号电影的平均评分(2.2 单位)与《星际穿越》(4.35 单位)和《星球大战》(4.5 单位)相比,实际上有很大差异。

鉴于后面几部电影的平均评分明显更高*,你可以放心地将表情符号电影从你的候选名单中排除。*

现在只剩下两位选手了…

星际大战 vs 星球大战:最后的绝地

  • 检验统计~=-1.35
  • p 值= 0.18046156732197555

从这些结果中,你不能证明这两部电影之间有统计学上的显著差异。如果你还记得,他们的平均评级非常接近——4.35 比 4.5 单位。

尽管说零假设是真的很有诱惑力,而且这两种方法没有区别,但你不能。

你能说的是,你没有足够的经验证据来否定零假设。

如果你想遵守统计学的规则,你需要一个技术上的联系😀🤓

作为决胜局,你可以询问公正的第三方的意见,或者只看平均评分最高的一方。

感谢阅读!

每年的这个时候我都会变得多愁善感

原文:towardsdatascience.com/i-always-ge…

Python 中的 NLP &情感分析!

圣诞快乐,我们来谈谈吝啬鬼。狄更斯对老人关于时空连续体午夜幻觉的描绘,呼应了我们自己一年一度的泪眼朦胧的假日反思。或者…随便吧。也许是对过去假期的回忆——回忆家庭聚会、童年礼物和更简单的时光。也许是当前的气氛——空气中的寒冷(或者在亚特兰大,气温的巨大波动)、拥挤和喧嚣(或者在亚特兰大,交通)。或者也许,只是也许,事实是我渴望有一天定性分析,特别是自然语言处理(NLP),实际上是一种创造有意义见解的有用工具。

😯☃️😤🎄😒🎅🙊☃️

**什么是自然语言处理?**根据维基神的说法,它是“一个与计算机和人类语言之间的交互有关的子领域,特别是如何给计算机编程以处理和分析大量自然语言数据。”干净利落。我为什么关心…?

定性数据是下一场革命

过去情感的幽灵

量化数据是 soooo 2010,老弟。作为分析师和程序员,我们基本上掌握了数字。有无数的科学家、统计学家和商业大师,他们 1)比我聪明得多,2)不知疲倦地努力发明下一个“工具”,让数字运算和数豆子变得更容易、更好、更性感。我想起了第一次打开 Tableau 时,它是如何震撼了我的心灵。我思考 Excel 已经走了多远。我想到了用 Python 和 r 创建的数十亿个库。每天都有新的和旧的出现。显然,我并不是说我们已经完成了定量分析。我只是说,我们现在越来越多地使用非结构化的数据库、图像、声音,当然还有文本。

我们的话语很重要。很多。我花了无数个小时分析美国各地医院的定性数据。这主要是在**情绪分析的保护伞下。**在这些数据中,大部分都存在未开发的改进潜力。是金子,杰瑞。为什么是黄金?调查评论形式的文本通常支持、验证并在许多方面取代我们淹没在其中的定量数据。此外,它还存在于组织的各个层面:领导、中层管理者、一线员工和客户——也就是病人。这些信息不是指南针,而是指导领导者行动计划的 GPS。那里。所以我才在乎。

有没有定性工具可以帮我们做到这一点?简答:有。自动化分析这些信息是可能的,但是它有效吗?我们拿着一个“单词包”,用一个马里奥式的木槌把圆形定性数据打碎成方形定量数据。手动完成这项工作需要花费大量精力,但是当我们利用数据科学工具来加速这一过程时,会发生什么呢?会不会更快达到同样的效果?好点了吗?

Mario BI Analyst vs Qualitative koopa troopa

让我们把计算机比作人类

情感的幽灵出现了

**让我们想想我们需要什么。**就我而言,我指的是对医院调查的情感分析,我们希望了解医院周围的人对安全因素的积极和消极看法。

让我们用 VaderSentimentAnalysis。 这个库是一个很有前途的快速肮脏的‘意见挖掘’库。此外,作者是佐治亚理工学院的毕业生,所以有(去,夹克。蛰他们。bzzz🐝)无论如何,在开始之前,有一些基本的注意事项:

  1. 它的预期目的是挖掘和分析社交媒体。

2.它建立在单词、字符和表情符号(是的,表情符号)的静态库上,随着库的更新而更新。所以之前我写的时候,"😯☃️😤🎄😒🎅🙊☃️,“不仅你确切地知道我的意思,而且显然维达也知道我的意思。好吧,或者反正是编出来的😏←这家伙。

3.该方法的输出包含四个分数:正面、中性、负面和复合。以下是更多相关信息。

出于这种分析的目的,我们为什么不假设调查评论的结构类似于“推文”。我知道他们不一样。这不是一篇研究论文。冷静点。重点是,人们通常会写下他们的感受,所以调查评论包含许多俚语、拼写错误和情绪化的词语……就像……一条推特。

我们来了解一下维达。有了这个假设,让我们来看几个例子和它们相应的分数。这些是直接来自维德 GitHub 页面。注意以下几点:标点符号很重要,大写字母很重要,俚语也很重要。事情很重要。

**[IN]** VADER is smart, handsome, and funny.
**[OUT]** {'pos': 0.746, 'compound': 0.8316, 'neu': 0.254, 'neg': 0.0}**[IN]** VADER is smart, handsome, and funny!
**[OUT]** {'pos': 0.752, 'compound': 0.8439, 'neu': 0.248, 'neg': 0.0}**[IN]** VADER is VERY SMART, handsome, and FUNNY.
**[OUT]** {'pos': 0.754, 'compound': 0.9227, 'neu': 0.246, 'neg': 0.0}**[IN]** VADER is not smart, handsome, nor funny.
**[OUT]** {'pos': 0.0, 'compound': -0.7424, 'neu': 0.354, 'neg': 0.646}**[IN]** Today SUX!
**[OUT]** {'pos': 0.0, 'compound': -0.5461, 'neu': 0.221, 'neg': 0.779}**[IN]** Not bad at all
**[OUT]** {'pos': 0.487, 'compound': 0.431, 'neu': 0.513, 'neg': 0.0}

也许‘pos’,‘neu’和‘neg’是不言自明的(也许不像我们将看到的那样),但是‘compound’需要一些背景:

“复合得分是通过对词典中每个词的化合价得分求和计算出来的,根据规则进行调整,然后归一化到-1(最极端负)和+1(最极端正)之间。如果你想对一个给定的句子进行单一的一维情感测量,这是最有用的度量。称之为‘标准化加权综合得分’是准确的。”—Vader perspection GitHub

所以,如果我理解正确的话,他们的复合分数有点数学魔力。这是对文本块进行分类时最重要的度量。例如,这里有一个例子,从各种新闻句柄抓取推文,并从 7 月份开始计算它们的综合得分。

**让我们来做这件事。**好了,够了。在本地环境中安装库,并根据提示进行升级。

> pip install vaderSentiment
> pip install -- upgrade vaderSentiment

假设您已经有了其他基本的 Python 分析库,这就是我们所需要的。现在,让我们对这个注释进行一些测试:

*“There is a culture where I work that does not appreciate the nurses and does not adequately address concerns brought up by staff regarding potentially dangerous patient situations and potentially dangerous staff situations. Valid concerns are dismissed and disregarded by local and senior management. Employees that raise concerns are made miserable until they quit. Dangerous and problematic employees are kept on and protected.  There is a pervasive culture of bullying among our staff that is not being adequately addressed by management"*

这是一个极好的样本。全国各地的医院都有很多类似的观点。也就是说,如果我看到这个评论,并把它分为积极、中立或消极,你认为这会是什么?是啊。咄。它是阴性的。它还对一些特定的类别持否定态度,但稍后会有更多的内容。现在,让我们看看维德要说什么:

**[IN]:***# Import libraries including the sentiment analyzer* import pandas as pd
import numpy as np
from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer
analyzer = SentimentIntensityAnalyzer()*# Read in the comment into a dataframe*
df = pd.read_csv(‘comments.csv’)*# Analyze the comment*
sample = df.iloc[0,0]
compound = analyzer.polarity_scores(sample)[“compound”]
pos = analyzer.polarity_scores(sample)[“pos”]
neu = analyzer.polarity_scores(sample)[“neu”]
neg = analyzer.polarity_scores(sample)[“neg”]*# Print the results*
print(‘ — — — — — — — — -’)
print(f’compound: {compound}’)
print(f’pos: {pos}’)
print(f’neu: {neu}’)
print(f’neg: {neg}’)**[OUT]:** -----------------
compound: -0.965
pos: 0.03
neu: 0.718
neg: 0.251

这很有意思。我不确定如何理解“积极”、“消极”和“消极”的分数,因为“消极”的分数在 0.71… 时占主导地位。??但是,注意,‘复合’得分是 -0.965。这意味着,“你的文本斑点非常非常消极。”这符合我们的精神分类,所以你说对了,维德。

接下来,有两个任务。

  1. 这在汇总我们所有的调查文本时会起作用吗?
  2. 有没有办法让这个分析更深入,因为那又怎样?

首先,聚集所有的文本(在本例中大约有 400 条评论),并在新的 blob 上再次运行代码。接下来,如果您想要查看每个单独字符串的分数,创建某种循环并将值推入列表中,以便进行切片和切块。这就是我在分析方面要说的全部。这里有一个方法:

compound = []
positive = []
neutral = []
negative = []for x in comments:
    x_comp = analyzer.polarity_scores(x)["compound"]
    x_pos = analyzer.polarity_scores(x)["pos"]
    x_neu = analyzer.polarity_scores(x)["neu"]
    x_neg = analyzer.polarity_scores(x)["neg"]
    compound.append(x_comp)
    positive.append(x_pos)
    neutral.append(x_neu)
    negative.append(x_neg)

print(f'compound: {compound}, length: {len(compound)}')
print(f'positive: {positive}, length: {len(positive)}')
print(f'neutral: {neutral}, length: {len(neutral)}')
print(f'negative: {negative}, length: {len(negative)}')

**让我们看看我们的综合结果。**当我手动分析大约 400 条评论时,这是情绪的分布。一个有医疗保健经验并了解全部数据集的资深分析师大约需要一个小时来执行我们的手动“评论分析”

= 4 hours of manpower

Python 和 Vader perspection 代码在这大约 26,000 个单词上运行的结果是什么?

< 4 seconds of computing power

嗯……这看起来很不一样。然而,还有一线希望。复合得分为 -0.6951 。如果我们粗略地将其与我们的手动分析进行比较,负比率为 0.85 ,我们可能会有所收获。然而,我还不确定在哪里。

感情未来的幽灵

那么,我们学到了什么?

  • PRO :在宏观层面上,Python 情绪分析复合评分认识到了整体数据集的负面性。就像我在维德的介绍中所说的,如果你需要快速和肮脏,这就是你的工具。
  • 反对:然而,即使将我们到目前为止所做的所有假设标准化,总得分中的 15.49% 的差异也是巨大的。这一点尤其正确,因为我们讨论的是一个数据集,它认为 1%的增量是平均值,5%的增量是显著的。如果我告诉一位首席执行官,69.51%的评论是负面的,他或她会不高兴。如果我后来回来告诉那位首席执行官,我仔细检查了,手动梳理了数据,结果,实际上 85%的评论都是负面的,我可能会被扫地出门。理解上的差距还不够小。
  • PROPRO:情感分析工具可以提供快速简单的结果,将文本块分类为正面、中性、负面等等。
  • 反对:无论如何,有了 Vader perspection,就很难理解正、中和负突破值的确切含义。复合分数是有用的,但还需要更多的研究。
  • 我们为自己节省了大量时间。四小时和四秒钟的差别对于大规模雇佣顾问来说是一大笔钱
  • 反对意见:即使客户在省钱,他们也可能忽略了评论中的价值。我关于定量分析的整个策略是,它补充了定量分析,并允许我们超越切片。否则,一个复合分数告诉我们什么?69%的回复是抱怨和抱怨?哦。好的。(再次显示门)。
  • PRO: 将复合分数打成列表对于查看基于单元的、基于角色的或者除了聚合之外的任何其他微观分析可能非常有用。
  • :小心。现在我们进入了基于实践的伦理学。允许的微观分析程度将根据一个组织对其反馈的“老大哥”程度而有所不同。如果员工认为他们会因为说了对组织不利的话而受到惩罚,他们会抑制或缩减他们的语言。如果你打算走这条路,想出一个与领导沟通的计划。

我们还有很长的路要走。展望未来,在处理定性数据时,有一个行动呼吁和一些关键要点。

  • **不要仅仅依赖 NLP 数据科学工具。**自己花时间去理解组织和文本数据。这可能很耗时,但它将提供自动化分析之外的价值
  • 理解你的词典。如果员工使用大量医学术语,那么预先构建的自然语言处理库可能就没有用了。如果不剖析代码库,Vader perspective 的字典可以被视为一个黑盒。作为数据科学家,我们可能必须建立解释这种类型分析的字典
  • 不要忘记上下文和微观分析。文本工具正在变得越来越好,但我们仍然错过了上下文、微妙之处、细微差别、习语、讽刺,以及……嗯,说出任何其他与交流相关的特征。
  • 了解 NLP 的其他应用(但是这里有一篇很棒的文章包含了关于其他 Python NLP 库和应用的信息。我们需要找到合适的工具。)

我留给你两样东西。首先,

**[IN]**
sample = '😯☃️😤🎄😒🎅🙊☃️'
compound = analyzer.polarity_scores(sample)["compound"]
pos = analyzer.polarity_scores(sample)["pos"]
neu = analyzer.polarity_scores(sample)["neu"]
neg = analyzer.polarity_scores(sample)["neg"]
print('-----------------')
print(f'compound: {compound}')
print(f'pos: {pos}')
print(f'neu: {neu}')
print(f'neg: {neg}')**[OUT]**
-----------------
compound: 0.0
pos: 0.0
neu: 1.0
neg: 0.0*# Neutral?!? Vader, please...*

最后,

亲爱的圣诞老人,

请给我一个有用的自然语言处理工具。

SRSLY。分析 TINY TIM 的患者体验反馈表的 BI 团队就指望它了。

真诚地,

杰夫

我分析了 122 小时的假日广播

原文:towardsdatascience.com/i-analyzed-…

你会被迫听到多少次“美妙的圣诞节”?

122 hours, 1,510 tracks. Only 80 original songs. Source: 106.7 LiteFM; 11/30/2018–12/5/2018; Download the data.

早在感恩节前就开始了。准确地说,今年是 11 月 16 日下午 5 点。这时,纽约州的 WLTW 106.7 LiteFM 硬切换到全圣诞音乐节目。它持续到圣诞节。这是一种家庭规则,让汽车收音机在这段时间里一直调到这个电台。

我和我的家人最近开着车听 106.7 的圣诞音乐,似乎每次我打开汽车,我都会被迫听到那些可怕的八个混响合成器音符,这些音符出现在最糟糕的圣诞歌曲保罗·麦卡特尼的“美妙的圣诞节”之前。其他人已经说了很多关于为什么这首歌这么差,所以我继续。

The 8 synthesizer hits at the beginning of the worst Christmas song of all time.

但它确实让我对这个长达 943 小时的圣诞音乐节目感到好奇。我有一些问题。我很好奇新的圣诞歌曲似乎很少。许多艺术家每年都会推出圣诞专辑,但这些通常只是圣诞经典的封面。圣诞音乐有过黄金时代吗?音乐家们已经不再尝试创作原创的圣诞新歌了吗?我决定收集一些数据,并尝试回答其中的一些问题。

我首先从 LiteFM 的网站上下载了“最近播放的”播放列表,这是从 11 月 30 日到 12 月 5 日五天多一点的圣诞音乐。在这段时间里,我播放了 1510 首歌曲。此提要列出了歌曲标题、艺术家、专辑封面和歌曲播放时间。对于每首歌,我都尽可能地搜索最接近最初“出版”日期的日期——歌曲创作或作曲的时间——以及作家和作曲家的名字(如果有的话)。这相当于大约 122 小时的广播时间(其中包括他们在歌曲之间播放的任何广告,我没有跟踪这些广告)。

圣诞音乐有过黄金时代吗?音乐家们已经不再尝试创作原创的圣诞新歌了吗?

考虑到每首歌创作的年份,我的数据集跨越了 484 年的出版音乐。当然,许多老歌被认为是“传统”歌曲,没有明确的作者或作曲家。这种类型的一个明显的特点是它有丰富的翻唱(表演别人歌曲的新版本)。在我检查的这段时间播放的 1,510 首歌曲中,事实证明数据集中只有大约 80 首独特的歌曲。但是从这 80 首歌中,出现了许多翻唱、混音和现场录音。

所以让我们从基础开始。**“圣诞老人进城了”**是我的数据集中播放次数最多的歌曲。这包括了这首歌所有 10 个版本的所有播放次数。《雪橇之旅》紧随其后,共有 89 部戏剧,11 个不同版本。

Source: 106.7 LiteFM; 11/30/2018–12/5/2018; Download the data.

Michael Bublé, the holiday music juggernaut. Photo: Jeanie Mackinder [CC BY 2.0]

现在,如果我们看看特定版本的歌曲(包括所有这些封面),总的来说播放最多的歌曲是宾·克罗斯比的**“白色圣诞节”——绝对是一首经典老歌。但是紧随其后排在第二(和第三)位的是麦可·布雷**。我不得不承认我以前从未听说过他的名字,但是这位 43 岁的加拿大歌手(4 次格莱美奖得主)是假日音乐排行榜上不可忽视的力量。在撰写本文时,Bublé目前有 9 首歌曲登上了 Billboard Holiday 100 排行榜

Source: 106.7 LiteFM; 11/30/2018–12/5/2018; Download the data.

果不其然,当我们按照表演者最多的戏剧来看数据时,布莱在这个包含一些大师的列表中占据主导地位。LiteFM 的听众一定非常喜欢他天鹅绒般光滑的声音,因为在我监测的这段时间里,麦可·布雷的播放量是弗兰克·辛纳特拉的六倍多。

Source: 106.7 LiteFM; 11/30/2018–12/5/2018; Download the data.

Yukon Cornelius sang the words of Johnny Marks.

所以那只是唱和表演这些心爱歌曲的人。幕后真正的词曲作者呢?你可能不认识约翰尼·马克斯这个名字,但你肯定听过他的音乐。约翰尼·马克斯写了**《红鼻子驯鹿鲁道夫》《过一个快乐的冬青树圣诞节】《围着圣诞树摇滚》以及许多其他假日热门歌曲。尽管约翰尼·马克斯创作了一些最经典的圣诞歌曲,但他是犹太人(与此同时还有欧文·柏林和其他许多歌曲作者)。虽然没有出现在这个数据中,但 Marks 还创作了**“银和金”育空·科尼利厄斯在定格鲁道夫漫画中演唱的朗朗上口的曲子。

Source: 106.7 LiteFM; 11/30/2018–12/5/2018; Download the data.

因此,如果我们查看这个收藏中的所有原创歌曲,并回顾它们创作或首次出版的年份,我们可以了解最常播放的音乐是何时产生的。果不其然,有一个十年跳出来,成为该数据集播放的近 30%的歌曲的来源——20 世纪 40 年代。

Source: 106.7 LiteFM; 11/30/2018–12/5/2018; Download the data.

在我监测的五天中,近 30%的歌曲创作于 20 世纪 40 年代。

Martin Luther. Cranking out the hits since 1529. Lucas Cranach the Elder [Public domain]

在我收集数据期间,艾米·格兰特(Amy Grant)的混合曲目**“一个强大的堡垒/我们在高处听到的天使”只播放过一次,但这首曲目拥有收藏中一些最古老音乐的殊荣,因为马丁·路德在 1529 年首次为赞美诗一个强大的堡垒**创作了音乐。

看着这张图表,虽然事情似乎在 20 世纪 70 年代后减少了,但这真的很值得注意。90 年代确实给了我们一个真正的现代圣诞大片的最出色的例子,玛丽亚·凯莉沃尔特·阿法纳西夫的 **“我想要的圣诞礼物是你”**凯里在 1994 年录制。事实上,我很惊讶的是 AIWFCIY 不在这张专辑中播放次数最多的前 20 首歌曲中,因为它目前在 Billboard 的假日 100 首歌曲中排名第一。有趣的事实:这张唱片里没有真正的乐器,因为它都是在电脑上完成的

玛丽亚·凯莉还声称为另一首非常受欢迎的圣诞歌曲《你的圣诞在哪里》创作了歌曲(连同詹姆斯·霍纳威尔·詹宁斯)来自 2000 年的电影**《鬼灵精》**。她最初为电影配乐录制了这首歌,但由于她与汤米·莫托拉的离婚,她被法律禁止演唱这首歌。

那么这些圣诞歌曲中哪首版本最多呢?如果你在我分析的这段时间里不停地听 LiteFM,你会听到 11 个不同版本的**【雪橇之旅】(播放了 89 次)和不少于 10 个版本的【圣诞老人进城】(播放了 90 次),尽管我不明白为什么你需要除了布鲁斯·斯普林斯汀**之外的任何版本

Source: 106.7 LiteFM; 11/30/2018–12/5/2018; Download the data.

最后,我想看看播放所有这些歌曲的模式是什么样的。下面是所有独特歌曲的集合(包括所有版本),按播放次数从多到少排序。每个红框表示每小时播放一次。我从 11 月 30 日星期五下午开始收集数据,到 12 月 5 日星期三晚上结束,*是的,*收集整整一周的歌曲会好得多。

我确实注意到某些歌曲在不同时间播放的一些有趣的模式。我联系了 LiteFM 的项目主管,但没有得到回复。看着电视台的播出时间表,我确实看到一些模式与不同主持人的轮班相匹配。我很想知道主持人(他们现在还被称为 DJ 吗?)还是对播放列表有一定的创意控制。看起来午夜到凌晨 5 点是玩**“祝你小圣诞快乐”**的热门时间。如果我收到电视台的回复,我会更新这个帖子。

Source: 106.7 LiteFM; 11/30/2018–12/5/2018; Download the data.

你可以在这里下载我收集的数据。你可以在jonkeegan@gmail.com向我发送任何问题。感谢阅读!

Jon Keegan 是大纽约地区的视觉记者。他在华尔街日报工作了 18 年,处理数据并开发新闻应用。创作了《华尔街日报》的“* 【蓝馈】、红馈“ *,图文并茂地过滤脸书上的气泡。其他感兴趣的领域包括 3D 打印和扫描、太空、无线电和可视化。你可以在 jonkeegan.com看到更多他的作品。**

* [## 乔恩·基根:视觉记者

18 年来,我为华尔街日报开发新闻应用程序。我设计、写文字、编码和可视化数据。我…

jonkeegan.com](jonkeegan.com/)*

我分析了法戈的犯罪数据,这是我的发现

原文:towardsdatascience.com/i-analyzed-…

TL;博士: 如果你没有耐心,想直接进入有趣的东西,这里有一个链接到我建立的 互动网络仪表板 来补充这个分析。在台式电脑上观看效果最佳。

最近的一个周一下午,我通常会趴在桌子上小心翼翼地敲代码,但我偶然发现了一篇关于我家乡的文章,这让我恍然大悟。法戈暴力犯罪首次超过美国全国平均水平。这篇文章详细描述了坐落在肥沃平坦的古老湖床上的曾经沉睡的农业社区是如何超越全国暴力犯罪率的。

我是一个新爸爸,所以我对这些说法一点也不担心,所以我做了任何数据科学家在这种情况下都会做的事情,我搜寻了一些数据以了解更多信息。幸运的是,法戈警察局公布了他们的调度日志,其中详细记录了所有打给警方的电话。还有一个方便的地图应用程序,可以帮助你在二维空间中可视化“危险区域”的位置。但是,我想知道更多。因此,我制定了一组问题,我将使用 2017 年的派遣数据来回答这些问题。

问题陈述

  • 问题 1:哪个月犯罪最多?
  • 问题 2:哪个工作日犯罪最多?
  • 问题 3:一天中什么时候犯罪最多?
  • 问题 4:犯罪发生在哪里?

但是首先,让我们把假设和方法放在一边。

假设

(1)在调度日志中的犯罪类别中,我们将查看实际上是犯罪的呼叫,例如,我们将忽略停车投诉、胸痛等。

(2)每个电话事件都在街区级别报告,这意味着我们的地图将精确到街区,而不是房子或地址。

(3)为了简洁起见,我将使用“犯罪”一词代替“调度呼叫”,假设调度呼叫通常表示犯罪。

方法

计算语言被用来执行这些分析。完整的源代码可以在这个 Github 库找到。

哪个月犯罪最多?

法戈既有热天气也有冷天气,但在流行文化中,哈姆雷特更因后者而闻名,(谢谢,科恩兄弟/FX!).天哪,当你搜索法戈的图片时,你得到的只是 1996 年电影的海报和一个血迹斑斑的史蒂夫·巴斯米:

北方气候的人们都知道“幽居病”是真实存在的,尽管没有杰克·尼克尔森在《T4》的《闪灵人》中的解释那么极端。我的观点是,当你不得不刮擦挡风玻璃并在助推汽车前预热发动机时,犯罪就会变得更加困难。

在我查看数据之前,我认为在寒冷的几个月里,犯罪率会很低,这可能从 10 月份开始,一直持续到 5 月份。

上面的图表被称为日历热图。它按月份和星期几显示犯罪频率。冷色(紫色)表示低犯罪率,暖色(黄色)表示高犯罪率。正如我所怀疑的,与六月和九月的狂欢相比,十一月到二月相对平淡。

啊,现在这个条形图更清楚了。似乎从一月开始到五月的寒冷月份是堕落的夏季月份的隐喻斜坡,六月是这种放荡的顶峰。

哪个工作日犯罪最多?

我认为条形图也可以解决这个问题。

这很有道理。随着人们在工作周的忙碌,他们比周末有更少的时间变得野蛮。我很好奇,某些犯罪在一周的不同日子里会更常见吗?为此,我将从交互式仪表盘中截取一些截图:

受损司机

人们在周末聚会更多;很明显。

骚扰

看一下调度记录,似乎很多电话都是对电话骚扰的回应,就像骗子一样。显然周一是他们在骗子公司的大日子。

一天中什么时候犯罪最多?

没想到会看到这些结果。显然,犯罪率在午夜左右下降,然后在凌晨两点酒吧打烊时又回升。为简单起见,我将不同的犯罪类型分为三个子类。

犯罪在哪里发生?

总的来说,这个城市到处都是犯罪。然而,市区在 2017 年的犯罪率最高。

互动仪表盘上的热图很有趣,可以用来查看特定犯罪的热点。

有趣的发现

当我摆弄仪表板时,我发现了一些有趣的事情。首先,我注意到窥视者数据相当集中在一个区域:

西英亩西部的法戈地区是主要的偷窥场所吗?我不知道。但是这可能表明只有一两个偷窥者呆在他们自己的社区里。周日的电话数量几乎是一周其他时间的两倍,这是一个明确的模式。

对“过量中毒”犯罪图表的研究显示了一种不同的模式:

这里我们看到两个不同的集群;一个在 Yunker 农场东部,另一个在市中心。如果我放大市中心的集群,我们可以精确定位震中(记住这是块级别的数据):

似乎是一个需要小心的地方。

结论

那么我们学到了什么?当你检查交互式仪表盘时,你总是会得出与我不同的结论,但我的观点是:( 1)市中心=危险区,( 2)寒冷肯定对当地犯罪有“冷却效应”(抱歉),( 3)酒吧关门不是遛狗的好时候。

进一步研究

我曾想过建立一个实时网页来展示法戈犯罪的发生。有一个网页已经做了类似的事情,但它笨拙而缓慢。如果对这个分析有足够的兴趣,我肯定会把这个应用扔在一起(如果你喜欢这个,给我发电子邮件或评论)。

我可以根据合同提供数据咨询。给 bradley.lindblad@gmail.com 发邮件

我分析了漫威的电影剧本,以了解每个复仇者说得最多的是什么。

原文:towardsdatascience.com/i-analyzed-…

处理等待无限战争的一个方法是玩一些复仇者联盟的数据。我拿了最近三部漫威电影宇宙跨界电影(《复仇者联盟》、《复仇者联盟:奥创时代》和《美国队长:内战》)的剧本,找出了每个英雄最具特色的词语。我离开洛基和奥创是因为他们有如此独特的声音。

Matt Winn,数据可视化超级英雄,使用 R 的 ggplot 库制作了这个图(在这里查看他的教程)。这并不容易,我们得到了西雅图数据即 Jam meetup 的大力支持。

我们希望你喜欢!

一些值得注意的数据观察:

  1. 鹰眼和浩克都在说纳特。当然是因为不同的原因。
  2. 洛基有着崇高的想法,比如权力改变。虽然索尔比其他复仇者更多地提到洛基,但洛基并没有回报兄弟般的关怀。他的眼睛盯着他的目标。
  3. 雷神的关键词表明,在复仇者联盟的电影中(不包括雷神标题的电影,如 Ragnarok 和黑暗世界),他比大多数其他角色更注重行动。除了他和洛基的关系,他倾向于关注推动剧情发展的有形艺术品。比如洛基的权杖宇宙魔方和心灵之石。
  4. 蜘蛛侠是如此笨拙和追星族。他的关键词包括“嘿”、“嗯”、“嗯”。
  5. 看看《幻视》和《猩红女巫》有什么相似的词汇——它们都在谈论恐惧。我希望他们在无限战争中保持同步。有趣的是,我也做了一个情绪分析,Vision 有最多的负面情绪。这并不是因为他是一个经常令人沮丧的人,而是因为他认为情况就像他看到的那样,并且有时反思他所爱的人类英雄的徒劳。他看到了额外的,额外的大局,我觉得这让他很不安。
  6. 美国队长经常谈论其他人。事实上,他的五个最有特点的词中有四个是名字(托尼、萨姆、斯塔克、斯特鲁克;另一个词是“西装”,几乎都是指托尼的西装)。
  7. 看到黑豹的话随着他的角色继续发展而变化将会很酷——我们刚刚在内战中见到他,他在他的个人电影中经历了一个惊人的过程。在内战中,他仍然被他在瓦坎丹皇室的地位和他对传统的依赖所定义,这表现在他的用词上。当他被他的个人电影中的事件改变时(这里没有剧透),他必须改变他的世界观并重新定义他与他的遗产的关系。

很想听听大家的评论和见解!

再见了,

我分析了我在脸书的数据,这是一个关于害羞、孤独和变化的故事

原文:towardsdatascience.com/i-analyzed-…

我跟随最新的趋势也下载了我的脸书数据的 zip 存档,但是在分析数据之后我发现的并不是我所期望的。

我想知道脸书对我有什么了解,就像现在其他人一样,但我开始深入挖掘联系数据、广告点击量和我的活动历史之外的信息。第一次,我脑子里所有关于解析我的 facebook 数据的想法看起来都是可行的。页面是干净的,他们只是对我尖叫,“利用我,你知道如何编码我周围的东西”。我开始写一个刮刀来分析所有这些数据,数万条消息,好友添加历史,我的词汇,我对一切都很好奇。整个脸书剑桥分析丑闻应该提醒我们,Facebook 在我们的生活中有多重要,我们每天使用它有多频繁,我们表现出的行为远远超出了简单的喜欢和照片。

我想知道我的模式,并在这些年里做了一些改变。我使用脸书这么多年了,在很长一段时间里,信使也一直是我主要的交流方式。那里必须有有趣的数据,如果我所有的工作变得枯燥乏味,我会非常失望。我在 ruby 中创建了一个脚本来解析我的 zip 存档数据,我与你们所有人分享它,所有你需要的是脸书的 zip 副本存档(它必须是英语)。

这是给你阅读后尝试的,对代码质量抱歉,但这是一个快速的黑客,所以请原谅我:

github.com/Lackoftacti…

这个脚本将生成 excel 文件,其中包含所有分布在工作表中的统计数据:“朋友排名”、“我的消息统计”、“词汇统计”、“联系人列表”、“交朋友”。

我首先感兴趣的是我和谁写得最多,以及我们的转化率看起来如何。

People, groups I wrote the most

到目前为止,一切都很好,我最好的朋友排在第一位,其次是我现在的女朋友,我看到他们在交谈中投入了更多,因为他们写了更多的信息和文字。这是一个非常有趣的统计数据,可能说明我真的更喜欢和同样乐于交谈的人交谈。

到目前为止,这很有趣,我有一个我的朋友的排名,通过一些信息,单词,字符。接下来的部分带来了回忆。是时候坦白了,我是一个非常害羞的人,我独自坐在学校直到高中结束。我不会去参加聚会,喝酒,甚至不会和朋友出去玩。如果提到的任何事情曾经发生过,那也是非常罕见的,几乎是不存在的。我从父母身边搬走后,我知道这是我一生中最好的决定,我独自一人,渴望朋友,但他们无处可寻。我从不约会,很少和任何人去看电影或喝啤酒,我把时间都花在看电视节目上。但是有一年,当我开始出去,经常出去,我是说真的经常出去的时候,我突然明白了一些事情。参加那么多派对并不好,但我需要,接触女孩,去上嘻哈课程,更关心更多的外表。有趣的是,你可以从数据中看到。

这是我每年发送的信息数量,猜猜看,我是从哪里开始变得更加社会化的。

Number of messages sent by year

在 2016 年,当我开始在社交和约会领域迈出第一步时,这一数字增加了近 10 倍,然后与 2015 年相比增加了近 40 倍。我知道这只是一种通信类型的数据,但我以前总是使用 messenger 作为我的主要通信方式。不可否认,我与人们的社会交往和我发送的信息数量之间有很强的相关性。在 2015 年和 2014 年,我每天平均收到 3.6 条消息,这表明我当时有多孤独,电话那头没有任何人让我说话,安排会议,关心我的一天或说晚安。在 2017 年,平均每天有 108 条消息,我有可以交谈的人,有趣的人,我每天都可以和他们交谈和交换这么多消息。我的社交能力处于巅峰状态,在接近女孩时,我觉得自己真的不可战胜。

最繁忙的消息传递日

好吧,即使是这个数据也显示了社交模式的美妙之处。大多数繁忙的消息传递日恰好是从 2017 年 6 月开始的,就在我遇见我的女朋友并给我留下了非常好的印象之前,好到我们在一起了。

Most messages sent by date

我们在这里,我们在一起不是很可爱吗?:)

@p_mroczek picture from instagram

我还分析了我添加新朋友的历史,进展仍然存在,但这个统计数据并不那么可信,因为每次工作变动,旧同事的快速增加很容易造成脸书,使数据混乱。但是你也可以在这里看到进步。

按年交友

我社交最多的一年也是我交朋友最多的一年。但就像我之前说的,这不是我百分百信任的数据,因为任何社会环境的变化都让这个数据不可靠。

我也对我交新朋友感兴趣,是在周末还是在工作日。正如我所预测的,大多数新的友谊来自于周末外出。

你知道,你还能从脸书的数据中得到什么?类型的人,夜猫子对早起的鸟。这并不难,只要分析你的信息模式,按一小时分解。

快速看一看,你就知道,我不是那种早起的人。我一直怀疑我是夜猫子,但现在我有数据支持它。我在晚上更有效率,我最密集的编码会议发生在晚上。即使是这篇文章,我也是在深夜创作的。对我来说更容易进入心流状态,但也很安静。

我们来看看词汇统计。我对我的词汇感兴趣。我总共使用了 43k 个单词和 366k 个单词。那只是冰山一角。我用的最常用的词呢,在我们过了常用词之后,我们得到了有趣的东西。我怀疑,可能作为一个相当敏感的人,这也应该反映在我的写作中。为了更好的理解,我将把这些波兰话翻译成英语。我不想过度分析这一点,但与我的其他朋友相比,我会说这看起来不同,显然我喜欢表达我的情绪和我的感受,人们倾向于隐藏它们,特别是在发短信的时候。也许这也是害羞的一部分,写东西比在别人面前说容易。

Word rank

此外,我真的很喜欢谈论 Taco Hemingway,波兰嘻哈艺术家:)我非常公开地交流我的感受,根据我的经验,这样做的人可能很容易成为欺凌的目标,这导致封闭自己,少说话,更少机会,因为第一次尝试以可怕的方式结束。

所以现在你知道了我的故事(至少有一小部分没有进入沮丧的情绪),用数据表达,一个我几乎忘记的故事,因为现在事情对我来说太不同了。我不记得那个害怕接近女孩、和她们跳舞、和朋友出去的家伙。这就是我们这个时代真正有趣的地方,我们不仅可以通过写日记,拍照片来创造记忆,我们还可以通过创造数据来创造记忆。这些数据我们可以在未来进行分析,可以激发记忆,尽管有时有点悲伤。

想要运行此脚本并了解自己。也许只是为了好玩,也许你也会发现自己的一些有趣之处。

查看我的 github 存储库,了解如何运行:

[## lacoftactics/Facebook _ data _ analyzer

facebook_data_analyzer -分析你的数据的 facebook 副本。从 facebook 下载 zip 文件并获取朋友的信息…

github.com](github.com/Lackoftacti…)

请在产品搜索上投票支持我的存储库,以便更好地展示该库。那会对我有很大的帮助。

www.producthunt.com/posts/faceb…