使用开源 Plotly“Dash”库的简短 Python 教程(第一部分)
最近,我已经成为 Plotly 的“Dash”Python 库的主要粉丝,该库用于数据可视化项目。使用 Dash,可以创建漂亮的图形和数字,然后轻松地将它们作为轻量级 web 应用程序进行分发,而无需编写任何 HTML 或 Javascript 代码。我不认为说在未来 18 个月内,Dash 库将成为最重要的 Python 数据可视化库之一是夸张的——而且它都是开源的(麻省理工学院许可的)。
在最近的一个项目中,我一直在使用 Dash 的一些中级功能,并希望创建一些教程来分享我学到的一些东西。今天,我想重点学习如何在 Dash 中创建动态数据表(这里的是基于原始 Plotly 代码的)。在后续教程中,我们将了解更多高级功能,例如使用 Dash 回调函数来启用/禁用基础应用程序的某些组件。
出于本教程的目的,我们将使用 2011 年美国每个州的农业出口数据和第二个美国每个州的 2014 年人口数据集,这两个数据集都可以在 Github 上获得。
2011 US Agricultural Export Data
2014 Population by US State
假设我们想找出一种可视化每个州的牛肉、猪肉和家禽出口的方法(在后面的教程中,我们将查看出口数据与该州人口的交叉引用)。我们将一步一步地介绍应用程序的每个部分——如果你很着急,可以跳到最终产品的最后代码嵌入部分。
首先,我们需要导入必要的 Python 模块,然后加载数据。
重要提示:‘dash _ table _ experiments’尚未完全稳定,因此在安装 Dash 库时,默认情况下不包括它。要使用 pip 安装,只需在您激活的环境中键入:’ pip install dash-table-experiments '。
接下来,我们需要定义应用程序的布局。我最近特别喜欢动态 Dash“数据表”,所以让我们使用下面的代码在布局中定义一个这样的表。
记下我们上面创建的组件的 id,因为接下来我们需要编写两个应用回调来启用表的动态属性。第一个回调等待用户点击子情节中的一个条,然后高亮显示表格中的那一行。相反,第二个回调基于用户在表中选择的行创建和更新条形图。在回调之后,我们将调用附加到外部 CSS 文件中以进行样式化,然后最终运行应用程序。
The finished app created using the Plotly “Dash” Python library
现在,如果我想找出最大的牛肉出口州,我可以很容易地对表进行排序,图表将动态更新。这并不令人惊讶,但我们可以很快看到德克萨斯州、内布拉斯加州和堪萨斯州是美国前三大牛肉生产地。
在下一个教程中,我们将看看如何使用更高级的回调来交叉引用农业数据和州人口数据。在此之前,这里是这个应用程序的最终代码!
使用开源 Plotly“Dash”库的简短 Python 教程(第二部分)
在上一篇教程中,我们使用 Python 的开源 Plotly“Dash”库创建了一个相当简单的数据仪表板。具体来说,我们研究了如何创建一个动态表来可视化美国各州的农产品出口。在数据科学项目中,我经常发现自己想要从多个数据库和用户输入标准中查询数据。我可以简单地加载我需要的所有数据,作为 Dash 应用程序的初始化步骤。然而,这是低效的,尤其是在涉及大量数据的情况下。我们如何修改现有的应用程序来做到这一点?请继续阅读…
为了举例,假设我们想要交叉引用美国各州的农业出口与该州的人口。显然,该州的人口数据集很小,但是想象一下这样一种情况:想要将数据交叉引用到该州发生的每个农业交易的时间序列。在这种情况下,让用户只在需要时运行查询会更有效。我们可以通过在应用程序中包含一个“运行查询”按钮来实现这一点,如果(且仅如果)从表中选择了一个州,则启用该按钮。
Mock-up of updated app with cross-referencing query function
首先,我们用 query 按钮更新我们的应用程序布局,并添加一个文本区域,在运行查询后,将在该区域中输出该州的人口。
很好,现在我们有了一个按钮和文本输出区域——但是我们如何合并逻辑呢?为此,我们需要为我们的应用程序创建两个新的回调。
我们的查询按钮现在可以在应用程序中通过选择表中的单个行来启用。单击“运行查询”后,该州的人口数将返回到文本框中!
App updated with query to cross-reference state population
完整应用程序的最终代码如下所示。敬请关注未来更多教程!
模糊时间序列的简短教程
介绍
时间序列分析和预测方法在工程、医学、经济、气象等许多领域都是不可缺少的。
有几种分析和预测的方法,从传统的和神圣的统计工具(ARMA,ARIMA,萨里玛,霍尔特-温特斯等),到新的计算智能工具(递归神经网络,LSTM,GRU 等)。没有完美的方法,也没有我在这里介绍的方法。但是模糊时间序列的一些关键特征使它成为一个吸引人的选择:
- 可读性
- 易处理
- 简单
- 可量测性
此后,我将假设你没有机器学习(侧重于模糊系统)和时间序列背景,我将提出这些领域的关键概念。然后将借助 pyFTS 库介绍模糊时间序列方法。走吧。
什么是模糊集?
如果你已经知道模糊逻辑和模糊集,你可以继续下一节。这里介绍的只是非常入门的概念!
逻辑理论和经典数学将集合定义为二分法:每个元素都在集合之内或之外。没有中间点!元素的隶属度是一个布尔值,即集合{0,1}上的一个值,它给每个集合强加了严格且不可改变的边界。
这种二分法的思维方式对人类来说是不舒服的,因为无数的现实并非如此。当我们试图用严格的界限将人分类时会有困难,例如:体重= {瘦,苗条,胖},年龄= {儿童,青少年,青年,成人,老年人},身高= {低,中,高}。如果我们要描述一个至少在其中一个方面使用了这些概念的人,我们会发现这个人可能处于这两个值之间的中间类别。但是经典/刚性集合没有给我们这种灵活性。
扎德(1965)提出的模糊逻辑提出了一个对偶来代替这种二分法:某个元素在某些层次上可能属于也可能不属于同一个集合,这样隶属度就是区间[0,1]中的一个值。模糊集没有严格的界限,它们通常是重叠的,因此,使用前面的例子,我可以是中高和中等,或者 90%中等和 10%高。
给定 X ,一个数值变量,使得 x∈ℝ——例如一个高度度量——它的论域,缩写为 U ,是这个变量可以取值的范围,例如u =【min(x),max(x)】。
A 语言变量 A 是数值变量 X 的值到一组单词/语言术语的转换(我们称之为模糊化)。每个单词/语言术语是一个模糊集∈,每个模糊集关联到一个函数 μ (mu 希腊文字母),这样μ:X→【0,1】(这意味着μ从 X 接收一个输入值,并在区间[0,1]返回一个输出值)。**
让我们回到数字变量高度,以厘米为单位。我们将 U = [20,220] 和语言变量γ定义为:
*= {*“非常小”、“小”、“矮”、“中”、“高”、“非常高” }
你明白我们在做什么吗?我们想停止处理 X 的数字数据,开始处理用γ表示的词汇,为此我们需要为每个集合映射 X 的值。
我们将把 U 分成 6 个重叠区间,每组度∈度一个区间。对于每个区间,我们关联一个μ°( X)函数。隶属函数有几种类型,但为了简化起见,我们将使用三角形隶属函数。三角形隶属函数可以定义为:
*def triangular(x, a, b, c):
return max( min( (x-a)/(b-a), (c-x)/(c-b) ), 0 )*
其中 a、b 和 c 以 a 在左边, b 在上边, c 为右点绘制三角形。当给定的输入值 x 等于 b 时,我们说它 100%在模糊集内(换句话说:它的隶属度等于 1)。另一方面,隶属度在区间【a】**【b】上从 0 线性增加到 1,在区间【b,c】上从 1 线性减少到 0。如果 x 小于 a ,或者大于 c ,我们说 x 完全在模糊集之外(int 换句话说:其隶属度等于 0)。
那么,对于语言变量来说,我们的集合会是什么样的呢?
假设一个人身高 163cm。对于变量°,中位数为 0.27,高度为 0.73,即:
μ_median(163) =三角形(163,90,130,175) = 0.2666666…
μ_high(163) =三角形(163,130,175,220) = 0.7333…
查看这里的代码!
ut 仅仅是一个巨大知识领域的开始。如果你想知道更多关于模糊逻辑和模糊系统的知识…
但是我们在这里讨论时间序列,对吗?所以说时间吧!
什么是时间序列?
如果你已经知道时间序列,你可以去下一节,这一节是非常入门的!
T ime 系列是代表一个(或多个)随机变量随时间变化的行为的数据集,其主要特点是该变量的连续记录不是相互独立的,对它们的分析必须考虑到收集的顺序。
根据 Ehlers (2009),“相邻的观察值是相关的,我们有兴趣对这种相关性进行分析和建模”。那是什么意思?为了预测时间序列的未来值,我使用同一序列的过去/滞后值。
一个简单的例子是自回归 AR§模型,其中 p 表示用于预测的滞后变量的数量。给定一个时间序列 X(t) ,其中 t 表示一个时刻,如果我们在预测中仅使用滞后,那么我们有一个 AR(1)模型,这将类似于 X(t) = α X(t-1) + ε ,其中 X(t-1) 是滞后值,α是通过统计方法调整的系数,ε表示噪声或随机误差如果我们想在预测中使用最后两个滞后,我们将有一个 AR(2)模型,类似于X(t)=α0X(t-1)+α1X(t-2)+ε。**
什么是滞后量,应该使用什么样的滞后?为了做到这一点,有必要研究时间序列的组成部分并分析它们的特性,使用诸如 ACF 和 PACF 的图表。通常时间序列在高层次上被建模(使用加法模型)为:
X(t) =C(t) + T(t) + S(t) + R(t)
其中:
- t 是时间指数;
- X(t)是时间 t 时序列的点估计;
- C(t)是周期性成分,可预见的短期/非常短期波动;
- T(t)是趋势分量,表示序列的长期行为。一般趋势是增长/上升或减少/下降,否则就说数列没有趋势;
- S(t)是季节性成分,是中长期的周期性波动。季节性因素的一个很好的例子是…猜猜看?一年四季!这些 4 个月的间隔有其自身的特点,并且每年重复一次——这使得这种季节性的任何变量都更容易预测。
- R(t)是噪声分量,一个均值和方差恒定的随机值。这种随机噪声是不可预测的!
不是所有的时间序列都有趋势或季节性。一些数列被认为是平稳的,这意味着它们的平均值(或多或少)是恒定的。在非平稳序列中,平均值随时间变化。在同方差序列中,方差是常数,而在异方差序列中,方差是随时间变化的。总结:平稳和同方差时间序列“表现良好”且更容易预测,而非平稳和异方差时间序列要复杂得多。在后一种情况下,我们可以应用数学变换(如微分、Box-Cox 等)。)使级数平稳且齐次。
但是…模糊集在这个故事中起了什么作用?
什么是模糊时间序列?
使用模糊集对时间序列进行建模和预测几乎是直觉地出现的,首先是基于模糊模型逼近函数的能力,但也基于使用语言变量的规则的可读性,这使得它们更易于专家和非专家分析。
宋(Song)和齐松(Chisson)在模糊时间序列方面做了开拓性工作,但我们在这里介绍陈(Chen)在 1996 年发表的《进化》。这个想法是将时间序列中的论域划分为区间/分区(模糊集),并学习每个区域如何表现(通过时间序列模式提取规则)。这些模型的规则告诉我们,随着时间的推移,当值从一个地方跳到另一个地方时,分区如何与自身相关联。换句话说:让我们创建一个语言变量来表示数字时间序列,这些区域将是我们变量的语言术语。
当我们创建一个语言变量来表示话语的范围时,我们创建了一个“词汇”,然后模糊化的系列由该词汇中的单词组成。这些单词——句子或短语——的顺序是我们需要学习的模式。
为了方便起见,我将模糊时间序列的方法论分为两个步骤:训练和预测。
培训程序
在本教程中,我们将使用模糊时间序列起源中的一个众所周知的时间序列:阿拉巴马大学的入学人数。你可以看到下面的数据:
1。话语论域 U 的定义
首先我们需要从训练数据中知道话语论域 U,比如U =【min(X),max(X)】。通常我们将上下界外推 20%,作为安全余量。
2。创建语言变量γ(论域划分)
现在我们需要在几个重叠区间(又名划分)上划分 U,并为每个区间创建一个模糊集。区间数是模糊时间序列中最重要的参数之一,它将直接影响模型的精度。
除了分区的数量,我们分割 U 的方式也对准确性有很大的影响。此后,我们采用最简单的分区方法,网格分区,其中所有分区都具有相同的长度和格式。要了解其他分区方法,请点击此处。对于我们的示例数据,我们将使用 10 个分区的方案,这样语言变量就是= { A0,a 1,…,A9 }。**
3。模糊化
现在我们可以将 X(t) 的数值转化为语言变量γ的模糊值,产生模糊时间序列 F(t) 。提醒一下和上的模糊集是重叠的总是好的,因此对于每个 x ∈ X(t) 来说,它可能属于不止一个模糊集Ai∈16。在 Chen 的方法中,事情稍微简单一点:只选择最大隶属度模糊集。然而,在其他 FTS 方法中,所有的模糊值都被考虑。**
使用陈氏方法,我们的测试数据的模糊值将是 F(t) = { A1,A2,A2,A3,A4,A4,A4,A5,A5,A4,A4,A4,A4,A4,A4,A5,A7,A8,A8,A7 }。
4。创建时间模式
时间模式表示在模糊时间序列 F(t) 上顺序出现的两个模糊集,并且具有格式先例→后果*,其中先例表示在时间 t 上出现的模糊集,而后果表示在时间 t+1 后不久出现的模糊集。*
对于前面的例子,生成的时间模式将是:
A1→A2,A2→A2,A2→A3,A3→A4,A4→A4,…,A8→A8,A8→A7
5。创建规则
我们的模型规则也有格式先例→结果*。鉴于先前生成的时间模式,我们将根据其先例对它们进行分组。我们的模型将为每个发现的独特先例包含一个规则,每个规则的结果将是具有相同先例的每个时间模式的所有结果的联合。*
对于前面的例子,生成的规则将是:
A1 →A2
A2 →A2,A3
A3 →A4
A4 →A4,A5
A5 →A4,A5,A7
A7 →A8
A8 →A7,A8
Rule visualization
事实上,这套规则不符合 FTS 模式。它们描述了我们的时间序列如何表现,如果它足够稳定(表现良好),我们可以使用这个模型来预测时间的下一个值。
这样一个简单易读的模型还有另一个优点:
a)非常容易并行化/分布,这使得它对大数据非常有吸引力;
b)它非常容易更新,这使得它对频繁变化的数据非常有吸引力。
但是我们如何将这些规则用于预测呢?
预测程序
既然我们知道时间 t 、 x(t) ∈ X(t) 的数值,我们现在要预测下一个瞬间, x(t + 1) 。
1。输入值模糊化 输入值 x(t) 将被转换成语言变量的模糊值,生成值 f(t) 。因为在训练过程中,只选择最相关的集合。**
对于示例数据,对于 t = 1992 ,该值为 x(t) = 18876 。模糊化 x(t) 最相关的集合是 A7,所以 f(t) = A7 。
2。寻找相容规则
寻找其先例等于 f(t) 的规则。规则的结果将是对 t + 1 的模糊预测,即 f(t + 1) 。
对于 f(t) = A7 ,我们有规则 A7 → A8 。那么 f(t + 1) = A8 。
3。现在你需要将 f(t + 1) 转换成一个数值。为此,我们使用质心方法,其中数值等于模糊集合的中心的平均值 f(t + 1) ,即 x(t+1) = n⁻ ∑ Ai ,对于 i = 0…n-1 和 n 等于 f(t+1) 中的组数。
由于 f(t + 1) 只有一组,那么 x(t + 1) = 19366.46。
他展示的模型非常简单,而且我必须说,是过时的(它来自 1996 年!!!).但它是理解模糊时间序列如何工作的一个很好的指南。现在最精确的方法使用更多的滞后(这种方法仅使用 t-1 滞后)、规则中的权重(来自 Yu (2005)的工作)、优化器来寻找最佳的集合数、滞后等。在接下来的教程中,我们将深入研究更高级的模型。
上面解释的所有程序都可以在这台谷歌 Colab 笔记本上重现。
现在是时候去玩一会儿,把我们的手弄脏了!!!!😃
pyFTS 图书馆
pyFTS:Fuzzy Time Series for Python库是在巴西米纳斯吉拉斯联邦大学(UFMG)的 MINDS —机器智能和数据科学上开发的,面向学生、研究人员、数据科学家或希望利用模糊时间序列方法的人。PyFTS 是一个持续发展的项目,欢迎所有的贡献!
让我们了解一下这个图书馆的一些主要特点:
在 pyFTS.data 包中有几个常见时间序列的数据集,如 TAIEX、纳斯达克、标准普尔 500、乘客等。每个数据集都有自己的特征,有些是单变量的,有些是多变量的,等等,但是所有的数据集基本上都有两个功能:
- get_data() :返回单变量时间序列
- get_dataset() :返回多元时间序列
**from pyFTS.data import Enrollmentstrain = Enrollments.get_data()**
在 pyFTS.common.Transformations 包中,可以对预处理和/或后处理数据使用几种数据转换,这直接影响了论域的划分。
**from pyFTS.common import Transformations
tdiff = Transformations.Differential(1)**
在 pyFTS.partitioners 包中是话语划分器的领域。每个划分器都具有表示语言变量、创建话语论域及其模糊集的划分的功能。在所有这些函数中,至少需要两个构造函数参数:
- 数据:(必填!)的列车数据;
- npart :(强制!)要建立的模糊集的最小数量;
- mf :将用于模糊集合的隶属函数,默认为三角函数(trimf)。各种隶属函数可以在 pyFTS.common.Membership 中找到;
- 变换:如果系列中使用了任何变换,应在此处报告。
**from pyFTS.partitioners import Grid, Entropy, Util as pUtilfs = Grid.GridPartitioner(data=train, npart=20)print(fs)**
我们可以探索几种话语划分器宇宙的几种替代方案,每一种都有自己的特点和性能。
各种方法可以在包 pyFTS.models 中找到。所有方法都继承自类 common.fts.FTS 。对于最终用户来说,有两种主要的方法需要了解:
- FTS.fit(data,partitioner=fs):根据关于参数数据的训练数据和已经由 fs 分割器构造的语言变量来训练模型。建议研究该功能的帮助,因为这里已经可以使用集群显示进行分布式训练。与 pySpark 兼容的版本也会尽快发布。
- FTS.predict(data,type='point ‘,steps_ahead=1):它使用已经训练好的模型根据数据中包含的滞后进行预测。有三种可能的预测类型,由“类型”参数指示:“点”(默认)、“间隔”和“分布”。选择方法时应该小心,因为不是所有的方法都适用于所有这些类型。最后,’ steps_ahead '参数表示预测范围,或者您希望预测向前多少步。
**from pyFTS.models import chenmodel = chen.ConventionalFTS(partitioner=fs)
model.fit(train)print(model)forecasts = model.predict(test)**
向前看——第二次会更好
在本教程的第二部分,我将讨论加权方法、高阶模型、多元模型和多步预测。我还应用这些模型来预测光伏能源。
一定要看完!
参考
陈希明。基于模糊时间序列的招生预测。模糊集与系统,第 81 卷,第 3 号,第 311-319 页,1996 年。网址:https://doi . org/10.1016/0165–0114(95)00220–0。Acess 于 2018 年 7 月 25 日。
Ehlers,r . s .anáLise de séries temporais。巴拉那联邦大学统计局。网址:【http://conteudo.icmc.usp.br/pessoas/ehlers/stemp/stemp.pdf 。Acess 于 2018 年 7 月 25 日。
莫勒廷,托罗伊,C.M.C. 时间的分析。布吕歇尔,2004 年
Silva,P. C. L .等人pyFTS:Python 的模糊时间序列,4.0 版**。网址:https://doi.org/10.5281/zenodo.597359。Acess 于 2018 年 7 月 25 日。**
宋、羌;模糊时间序列及其模型。模糊集与系统,第 54 卷,第 3 号,第 269–277 页,1993 年。网址:https://doi . org/10.1016/0165-0114(93)90372-O。Acess 于 2018 年 7 月 25 日。
俞慧光,加权模糊时间序列模型在预测中的应用。物理 A:统计力学及其应用,第 349 卷,第 3 期,第 609–624 页,2005 年。网址:https://doi.org/10.1016/j.physa.2004.11.006。Acess 于 2018 年 7 月 25 日。
洛杉矶扎德模糊集**。信息与控制 8(3)338–353,1965 年。网址:https://doi . org/10.1016/s 0019-9958(65)90241-X。Acess 于 2018 年 7 月 25 日。**
模糊时间序列的简短教程—第二部分
通过对太阳能的案例研究
在本教程的第一部分中,我简要解释了时间序列分析、模糊集以及什么是模糊时间序列 pyFTS,并简要介绍了 pyFTS 库。太棒了!我从世界各地的人们那里得到了反馈,我听到了他们提出的非常好的想法。但是,正如在每个介绍性研究中发生的那样,我们使用了非常简单的方法和数据,目的是促进对这种方法的总体理解。
但是现在,假设读者已经知道了所需的背景,我们可以稍微前进一点,玩一些更有用的东西。首先,我们将看到对偏差/方差概念的直观介绍,然后我们将了解一些最重要的 FTS 超参数及其对模型准确性的影响。最后,我们将采用一些 FTS 模型来模拟和预测太阳辐射时间序列,这对光伏能源预测很有用。
本教程的所有例子都可以在谷歌实验室,http://bit.ly/short_tutorial_colab2获得。随时联系,给 ou 反馈,提问。现在,让我们开始吧!
信号、噪声、偏差和方差
T 机器学习模型的训练是将信号从噪声中分离出来、将一般行为从局部特殊性和随机误差中分离出来的冲突。在每个估计器的训练中,偏差和方差之间,欠拟合和过拟合之间存在冲突。
给定一个数字时间序列 Y ,它的单个值 y(t)∈Y ,以及 y 的一个估计量,一个函数 ŷ(t+1) = f( y(t) ) 。 f 的目的是预测 Y 的最佳可能方式,使实际值 y(t) 与预测值 ŷ(t) 之间的差值 ε(t) 趋于零,或者换句话说对于*ε(t)= y(t)*t→∞来说
直观地说,有偏估计量是指ε (t)的期望值不为零— E [ε (t)] ≠ 0 的估计量。偏差是正确值的系统“偏差”,请记住,估计值通常会与真实值有偏差,这是意料之中的,但平均而言,这些偏差应该趋于零。这种偏差是典型的欠拟合模型,当模型不能学习信号,我们感兴趣的时间序列的组成部分。
另一方面,方差与模型的泛化能力有关,尤其是在训练阶段没有提供的数据。当模型开始学习不能很好地概括测试数据的训练样本的特异性时,高方差与过拟合有关。简而言之:模型学习了数据的噪声。
众所周知,完全消除偏差和方差的副作用是不可能的,最佳拟合是通过它们之间的平衡来实现的——这是估计模型的挑战。
模糊时间序列参数
几个参数决定了 FTS 模型的最佳拟合,但主要的参数是分区和顺序。这两个参数占模型准确率的 90%(经验值)。
1.分割
分区由三个参数组成,这里根据它们的重要性列出:
1a)分区(或模糊集)的数量
这是对模型精度影响最大的参数。模糊集越多,对时间序列特征的捕捉就越精确。这里有一个陷阱:
- 由于信号过度简化,太少的模糊集产生欠拟合;
- 过多的模糊集会产生过拟合,使得模型开始学习数据上的噪声;
Several numbers of partition for the sine function
Accuracy of several partitionings for sine function
组数是一个必须进行基准测试的参数。在已经被区分的时间序列中,10 个分区是一个好的开始数字。在其他情况下,35 个分区是一个很好的数字。
1b)分区类型
有许多类型的分区,从网格分区(GridPartitioner)到分区器,网格分区中所有集合均匀分布并具有相同的格式,分区器中集合具有不同的大小,如基于熵的分区器和基于簇的分区器。这里我不会深入讨论这个问题,但是出于好奇在 PYFTS/notebooks 存储库中有几个分区类型的例子。
总是从网格划分开始,如果是这样的话,探索其他类型。
1c)隶属函数
这是一个对模型的准确性几乎没有实际影响的参数,但是根据具体情况,您可能有很好的理由使用高斯或梯形函数来代替默认的三角函数。
理由之一可能是参数的数量(高斯型使用 2,三角形使用 3,梯形使用 4),模型的易读性,甚至与过程和数据的性质相关的其他问题。
同样,我不会在这里深入讨论这个问题,看看 PYFTS/notebooks 存储库,了解更多细节。
2.命令
模型的阶是 FTS 的第二个最重要的参数,因为它们是自回归模型(使用滞后值来预测下一个模型)。序参数是模型的内存大小,或者说需要多少过去的信息来描述未来的事件。
为了确定这个参数,熟悉自相关函数的概念——ACF 是很重要的。ACF 不仅能够指示最重要滞后的顺序和索引。
2a)滞后(或订单)数量
顺序是模型使用的滞后量(过去的值)。这真的非常重要,并且取决于被建模的时间序列的类型。这里的问题是:我需要多少滞后来让模型学习时间模式、周期、季节性等等?看看 ACF,看看有多少是显著的滞后。
Model accuracy by order
然而,这里有一个陷阱:当模型使用更多的滞后时(特别是当分区数量很大的时候!)并且模型越大,学习和推理变得越慢。
根据我的经验,描述一个时间序列行为不超过 3 个滞后。但是当然一切都取决于数据。
2b)滞后指数
默认情况下,模型会按顺序选择最近的滞后。但是根据时间序列的季节性,这些可能不是最好的滞后。所以看看 ACF,看看哪个滞后指数是最重要的。
3.方法的类型
关于 FTS 方法的文献非常多样化,但有两个特征极其重要:
3a)有重量与无重量
权重增加了模型的准确性,平衡了模型规则中的哪些设置对预测更有影响。它们稍微降低了模型的可读性,但没什么大不了的。如果你必须选择,总是喜欢加权模型!
在下面的例子中,我们可以比较 HOFTS(无权重)、WHOFTS(规则的结果权重)和 PWFTS(规则的结果权重和先例权重):
3b)单变量与多变量
大多数时候,我们只有一个变量的时间序列——内生变量。其他时候,我们可以利用其他信息(外生变量)来辅助这个变量。
例如,通常与时间序列测量相关的日期,对于季节性数据(如社会、环境等)来说是非常有价值的信息。数据。
如果你有多变量数据,那么首先要知道变量之间是否存在相关性,所以使用相关矩阵来检查它。相关系数指向简单的线性关系,因此它不应该是您应该使用的唯一工具,交叉熵是一个很好的替代方法。
另一个提示:如果您有一个单变量时间序列,您可以通过创建一个多变量序列来丰富您的模型,其中其他变量是内生变量的转换。例如,您可以拥有一个包含原始(内生)变量和微分内生变量的多元序列,从而提供有关值的最近波动的额外信息。
案例研究:太阳辐射
是时候找点乐子了!请记住:所有代码和结果都可以在 http://bit.ly/short_tutorial_colab2 的 T2 获得。我将使用来自 SONDA——巴西国家环境数据组织系统的太阳辐射数据,特别是来自巴西利亚/DF 站的数据。辐射数据在“glo_avg”变量中,每天 24 小时每分钟采样,从 2013 年到 2014 年用于训练集,从 2015 年用于测试。
正如预期的那样,数据有点嘈杂,不太需要每分钟采样。我清理了数据,删除了其他变量(它们与我们的主变量相关性不是很大),只留下了日期和内生变量。下一步是减少数据量和清除噪音,我通过对系列的每小时平均值进行采样来完成。预处理后的数据可从地址https://data . world/petroniocandido/sonda-BSB-average-hourly获得。
直观上,太阳辐射有两个主要周期,日周期(太阳自转运动)和年周期(太阳平移运动)。影响这种预测的最大不确定性是天气和天空中是否有云。
Sample of the yearly cycle
Sample of the daily cycle
Autocorrelation function for the first 24 lags, correspondig for the previous 24 hours
适合各种口味的方法
正如一样,您已经知道 pyFTS 库中的方法在模型包中。然后我们将分析几个 FTS 模型在这个时间序列建模中的表现。在现实世界中,我们应该通过测试上述所有参数的众多组合来优化模型的超参数。在 pyFTS 中,我们可以使用超参数包,您可以在其中运行分布式网格搜索,这将在每个超参数的所有可能值之间生成笛卡尔乘积,并测试每个值。你也可以使用遗传算法(超参数。进化的或使用远视库。但让我们简单点,好吗?
我们将使用具有 35 个分区的网格方案(GridPartitioner)划分我们的内生变量,分为 5 个亚组,VL-非常低,L-低,M-中,H-高和 VH-非常高,每个亚组有 7 个水平。这种命名法将通过生成以下规则使模型更易于解释:
L6 L5→L6 M0 M1
这条规则可以理解为:
如果 y(t-1) 为低(子级 5) 而 y(t) 为低(子级 6) 那么*【t+1】*将为低(子级 6) 或中(子级 0) 或
根据所选择的规则(由于规则的先例的成员与输入值相关),去模糊化将把结果转换成数值(一个简单的方法是集合的加权平均)。
Grid partitioning of solar radiation time series
单变量方法
我们已经有了分区方案,所以让我们来研究一下方法。我们选择了带权重和不带权重的高阶方法(阶数> 1 ),所有方法都用 1 到 3 的阶数进行了测试。下面列出了生成规则的方法和示例:
- 霍夫茨。HighOrderFTS :高阶失重法
L4,VL0 → VL0,VL1
L5,VL0 → VL0,VL1
- hofts。weightedhigorderfts:高阶加权法,权重只在每个规则的后件上。
L4,VL0 → VL0 (0.5), VL1 (0.5)
L5,VL0 → VL0 (0.5), VL1 (0.5)
- pwfts。probabilistic weighted FTS:高阶加权方法,其中概率权重基于每个规则的前件和后件。
(0.003) L0,VL1 → (0.876)VL0, (0.103)VL1, (0.015)VL2, (0.006)VL3, (0.001)VL4(0.003) L0,VL2 → (0.003)L0, (0.003)L1, (0.003)L2, (0.0)L3, (0.0)L4, (0.787)VL0, (0.164)VL1, (0.03)VL2, (0.002)VL3, (0.002)VL4, (0.005)VL5, (0.001)VL6
Performance sample of univariate models
多元方法
在pyft 上,多变量模型使用 Pandas 数据帧作为数据输入(单变量模型使用列表或 Numpy 数组),方法在 models.multivariate 包中。每个变量都有自己的分区方案和其他参数。您必须首先创建一个变量类型的对象,说明变量的名称、数据帧中的数据列和分割器。然后使用追加变量函数将外生和内生变量合并到模型中。内生变量必须在目标变量属性中设置。
from pyFTS.models.multivariate import common, variable, mvfts
from pyFTS.models.seasonal import partitioner as seasonal
from pyFTS.models.seasonal.common import DateTimesp = {'seasonality': DateTime.day_of_year , 'names': ['Jan','Feb','Mar','Apr','May','Jun','Jul', 'Aug','Sep','Oct','Nov','Dec']}month = variable.Variable("Month", data_label="data", partitioner=seasonal.TimeGridPartitioner, npart=12,data=train_mv, partitioner_specific=sp)radiation = variable.Variable("Radiation", data_label="glo_avg", alias='rad',partitioner=Grid.GridPartitioner, npart=35, data=train_mv)model = mvfts.MultivariateFTS()
model.append_variable(month)
model.append_variable(radiation)
model.target_variable = radiation
model.fit(train_mv)
除了我们的内生变量“glo_avg ”,我们将只使用“date”列,并从中提取两个外生变量:月份和小时。变量的划分如下所示:
Partitioning scheme by variable
你可能会问自己:*为什么时间和月份会重叠?毕竟这是非常准确的信息!*回答:那是模糊逻辑的家伙!我们假设相邻的事物相互影响并具有相似的行为,因此特定时间的规则也会受到相邻时间和月份的影响。
现在让我们来看看多元方法,并看看它们生成的规则示例:
- mvft。MultivariateFTS :失重一阶法(order = 1);
Jan,8hs,VL0 → VL1,VL2,VL3,VL4,VL5
Jan,8hs,VL1 → VL1,VL2,VL3,VL4,VL5,L0,L1,L3,L4
- wmvfts。Weighted multivariatedfts:加权一阶法;
Jan,8hs,VL0 → VL2 (0.353), VL1 (0.253), VL4 (0.147), VL3 (0.207), VL5 (0.04)Jan,8hs,VL1 → VL2 (0.276), VL3 (0.172), VL1 (0.198), VL5 (0.083), VL4 (0.151), VL6 (0.021), L0 (0.036), L4 (0.005), L1 (0.036), L2 (0.021)
- 颗粒状。GranularWMVFTS :加权高阶法;
Jan11hsV3,Jan12hsL1 → Jan13hsVL6 (1.0)
Jan12hsL1,Jan13hsVL6 → Jan15hsVL3 (1.0)
对模型进行基准测试
显然,评估预测模型的主要标准是其准确性。我们将使用均方根误差(RMSE ),该值越低越好。但是另一个重要的标准是简约:简单的模型总是比复杂的模型更受欢迎。所以我们的目标是最小化 RMSE 和模型尺寸。让我们看看五个表现最好的模型:
预计多变量模型比单变量模型表现更好,毕竟,它们有更多的信息可以利用。但是请注意,最节省的模型是 PWFTS!
可能性不止于此。我们现在可以决定集合最好的模型(models . ensemble),或者使用非平稳模糊集(models . non stability)。但这是为了另一个机会!
扩大预测范围
当我们采用预测模型时,我们不仅对预测下一个值感兴趣,而且对预测这些值的序列感兴趣。这真的很酷,因为只要有一些滞后的信息,我们就可以对未来的许多步骤做出估计!对于太阳辐射预测的具体情况,我们将我们的预测范围设置为 48 小时。
对于 pyFTS 上的单变量模型,这非常简单,只需使用预测函数的提前步骤参数。该参数将在下一次 steps_ahead 迭代中反馈输入中的输出值。
forecasts = model.predict(input_data, steps_ahead=48)
对于多元模型,这有点棘手:我们不仅生成内生变量的值,还生成外生变量的值。由于这个值的生成取决于每个变量的性质,除了 steps_ahead 参数之外,生成器参数也是必需的,它是一个字典,必须包含每个 Pandas DataFrame 列的一个键,其值是 lambda 函数,接收变量的当前值并返回下一个值。在我们的示例中,这非常简单,因为外生变量的值是日期时间,要计算下一个值,我们只需在输入值上加一个小时:
generator = lambda x : pd.to_datetime(x) + pd.to_timedelta(1, unit='h')forecasts = model.predict(input_data, steps_ahead=48,
generators={'date': generator})
Forecasts for the next 48 hours, using the two best evaluated models
结论
现在,您已经对 FTS 和 pyFTS 库有了足够的了解,可以在您的个人项目中使用它们。当然,还有很多东西要谈!在下一个教程中,我们将讨论区间和概率预测、数据转换、非平稳和增量模型等。
如果您需要帮助、提出问题或给出反馈,请随时联系我们。回头见,伙计们!
用于 MNIST 数字识别的简单 2D CNN
卷积神经网络(CNN)是当前用于图像分类任务的最新架构。无论是面部识别、自动驾驶汽车还是物体检测,CNN 都在到处使用。在这篇文章中,我们使用 keras 和 tensorflow backend 为著名的 MNIST 数字识别任务设计了一个简单的二维卷积神经网络(CNN)模型。整个工作流程可以是:
- 准备数据
- 模型的建立和编译
- 训练和评估模型
- 将模型保存到磁盘以供重用
深度学习——深度学习技术在自然语言处理、计算机视觉等领域的实现。
github.com](https://github.com/sambit9238/Deep-Learning/blob/master/cnn_mnist.ipynb)
准备数据
这里使用的数据集是上面提到的 MNIST 数据集。 MNIST 数据库(修改后的国家标准技术研究院数据库)是一个手写数字(0 到 9)的大型数据库。该数据库包含 60,000 个训练图像和 10,000 个测试图像,每个图像的大小为 28×28。第一步是加载数据集,这可以通过 keras api 轻松完成。
import keras
from keras.datasets import mnist
#load mnist dataset
(X_train, y_train), (X_test, y_test) = mnist.load_data() #everytime loading data won't be so easy :)
这里,X_train 包含 60,000 个训练图像数据,每个数据的大小为 28×28,y_train 包含它们相应的标签。类似地,X_test 包含 10,000 个测试图像的数据,每个维度为 28x28,y_test 包含它们相应的标签。让我们可视化一些来自训练的数据,以更好地了解深度学习模型的目的。
import matplotlib.pyplot as plt
fig = plt.figure()
for i in range(9):
plt.subplot(3,3,i+1)
plt.tight_layout()
plt.imshow(X_train[i], cmap='gray', interpolation='none')
plt.title("Digit: {}".format(y_train[i]))
plt.xticks([])
plt.yticks([])
fig
如这里可以看到的,在左上角,“5”的图像存储为 X _ train[0 ], y _ train[0]包含标签“5”。我们的深度学习模型应该能够只获取手写图像,并预测实际书写的数字。
现在,为了准备数据,我们需要对图像进行一些处理,如调整图像大小、归一化像素值等。
#reshaping
#this assumes our data format
#For 3D data, "channels_last" assumes (conv_dim1, conv_dim2, conv_dim3, channels) while
#"channels_first" assumes (channels, conv_dim1, conv_dim2, conv_dim3).
if k.image_data_format() == 'channels_first':
X_train = X_train.reshape(X_train.shape[0], 1, img_rows, img_cols)
X_test = X_test.reshape(X_test.shape[0], 1, img_rows, img_cols)
input_shape = (1, img_rows, img_cols)
else:
X_train = X_train.reshape(X_train.shape[0], img_rows, img_cols, 1)
X_test = X_test.reshape(X_test.shape[0], img_rows, img_cols, 1)
input_shape = (img_rows, img_cols, 1)
#more reshaping
X_train = X_train.astype('float32')
X_test = X_test.astype('float32')
X_train /= 255
X_test /= 255
print('X_train shape:', X_train.shape) #X_train shape: (60000, 28, 28, 1)
在对图像信息进行必要的处理后,标签数据即 y_train 和 y_test 需要转换成分类格式,如标签**‘3’应转换成向量【0,0,0,1,0,0,0,0,0】**用于建模。
import keras
#set number of categories
num_category = 10
# convert class vectors to binary class matrices
y_train = keras.utils.to_categorical(y_train, num_category)
y_test = keras.utils.to_categorical(y_test, num_category)
模型的建立和编译
数据准备好输入模型后,我们需要定义模型的架构,并使用必要的优化函数、损失函数和性能指标对其进行编译。
此处遵循的架构是 2 个卷积层,然后分别是池层、全连接层和 softmax 层。对于不同类型的特征提取,在每个卷积层使用多个滤波器。一个直观的解释是,如果第一个过滤器有助于检测图像中的直线,第二个过滤器将有助于检测圆等等。对每一层的技术执行的解释将是即将到来的文章的一部分。为了更好地理解每一层,可以参考
在最大池和全连接层之后,在我们的模型中引入了丢弃作为正则化,以减少过拟合问题。
*##model building
model = Sequential()
#convolutional layer with rectified linear unit activation
model.add(Conv2D(32, kernel_size=(3, 3),
activation='relu',
input_shape=input_shape))
#32 convolution filters used each of size 3x3
#again
model.add(Conv2D(64, (3, 3), activation='relu'))
#64 convolution filters used each of size 3x3
#choose the best features via pooling
model.add(MaxPooling2D(pool_size=(2, 2)))
#randomly turn neurons on and off to improve convergence
model.add(Dropout(0.25))
#flatten since too many dimensions, we only want a classification output
model.add(Flatten())
#fully connected to get all relevant data
model.add(Dense(128, activation='relu'))
#one more dropout for convergence' sake :)
model.add(Dropout(0.5))
#output a softmax to squash the matrix into output probabilities
model.add(Dense(num_category, activation='softmax'))*
定义了模型的架构之后,就需要编译模型了。这里,我们使用分类交叉熵损失函数,因为它是一个多类分类问题。由于所有标签都具有相似的重量,因此我们更倾向于将准确性作为性能指标。称为 AdaDelta 的流行梯度下降技术用于优化模型参数。
*#Adaptive learning rate (adaDelta) is a popular form of gradient descent rivaled only by adam and adagrad
#categorical ce since we have multiple classes (10)
model.compile(loss=keras.losses.categorical_crossentropy,
optimizer=keras.optimizers.Adadelta(),
metrics=['accuracy'])*
训练和评估模型
在模型架构被定义和编译之后,模型需要用训练数据来训练,以便能够识别手写数字。因此,我们将用 X_train 和 y_train 来拟合模型。
*batch_size = 128
num_epoch = 10
#model training
model_log = model.fit(X_train, y_train,
batch_size=batch_size,
epochs=num_epoch,
verbose=1,
validation_data=(X_test, y_test))*
这里,一个时期意味着所有训练样本的一次向前和一次向后传递。批量意味着一次向前/向后传递中训练样本的数量。培训输出是:
现在需要根据性能来评估训练好的模型。
*score = model.evaluate(X_test, y_test, verbose=0)
print('Test loss:', score[0]) #Test loss: 0.0296396646054
print('Test accuracy:', score[1]) #Test accuracy: 0.9904*
测试准确度 99%以上意味着模型在预测方面训练有素。如果我们可视化整个训练日志,那么随着更多的历元数,训练和测试数据的模型的损失和准确性收敛,从而使模型稳定。
*import os
# plotting the metrics
fig = plt.figure()
plt.subplot(2,1,1)
plt.plot(model_log.history['acc'])
plt.plot(model_log.history['val_acc'])
plt.title('model accuracy')
plt.ylabel('accuracy')
plt.xlabel('epoch')
plt.legend(['train', 'test'], loc='lower right')plt.subplot(2,1,2)
plt.plot(model_log.history['loss'])
plt.plot(model_log.history['val_loss'])
plt.title('model loss')
plt.ylabel('loss')
plt.xlabel('epoch')
plt.legend(['train', 'test'], loc='upper right')plt.tight_layout()fig*
将模型保存到磁盘以便重复使用
现在,训练好的模型需要序列化。模型的架构或结构将存储在 json 文件中,权重将以 hdf5 文件格式存储。
*#Save the model
# serialize model to JSON
model_digit_json = model.to_json()
with open("model_digit.json", "w") as json_file:
json_file.write(model_digit_json)
# serialize weights to HDF5
model.save_weights("model_digit.h5")
print("Saved model to disk")*
因此,保存的模型可以在以后重用,或者很容易移植到其他环境中。在接下来的文章中,我们将看到如何在生产中部署这个经过训练的模型。
享受深度学习!
参考资料:
Keras 顺序模型入门
keras.io](https://keras.io/getting-started/sequential-model-guide/) [## 用于视觉识别的 CS231n 卷积神经网络
斯坦福 CS231n 课程材料和笔记:视觉识别的卷积神经网络。
cs231n.github.io](http://cs231n.github.io/convolutional-networks/)*
对辛顿胶囊网络的简单而直观的解释
想获得灵感?快来加入我的 超级行情快讯 。😎
深度学习的复兴
早在 2012 年,机器学习领域发表的一篇论文将引发人工智能领域的复兴。该论文首次发表了使用深度卷积神经网络进行图像识别的研究。它打破了图像识别算法的所有记录。
从那时起,每个人都跳上了深度学习的列车,原因很简单:它一直在工作。事实上,在计算机视觉或自然语言处理中,还没有一个领域应用了深度学习,没有改善结果。研究人员也一直在尝试各种疯狂的配置,并将这些技术应用到许多新的应用中,这对人工智能来说确实是一个令人兴奋的时刻。
下图显示了其中的一些技术。像堆叠许多层、多个子网络、改变卷积大小和跳过连接都在提高精确度和推进研究方面发挥了作用。
A few of the many techniques developed for deep learning in computer vision
…那么有什么问题呢?
深度学习不会在任何地方都有效。
很明显,在过去的几年里,它在很多事情上都做得很好,但是当你使用一段时间后,它的一些缺陷和弱点就会显现出来。
深度学习的失败
卷积神经网络(CNN)的主要构件是卷积运算。在深度网络中,卷积(及其权重)的基本工作基本上是“检测”关键特征。当我们训练一个深度网络时,我们有效地调整了 CNN 卷积运算的权重,以检测图像中的某些特征或被其“激活”。
如果我们在人脸检测数据集上训练一个网络,那么网络中的一些卷积可能由眼睛触发,而其他卷积可能由耳朵触发。如果我们拥有组成一张脸的所有组成部分(或至少一定数量),如眼睛、耳朵、鼻子和嘴巴,那么我们的 CNN 会告诉我们它已经检测到一张脸,万岁!!
但不幸的是,CNN 的影响力仅限于此。让我们看看下面的例图,看看为什么。左边的人脸图像包含了上面提到的所有元素。CNN 很好地处理了这种情况:它看到了所有的组件,即卷积在所有正确的特征上激活。所以我们的网络说是的,那是一张脸!
An example of where a CNN would fail. Source
棘手的是,对于 CNN 来说,右边的图像也是一张脸。你说为什么?对一个网络来说,它拥有一张脸的所有特征。当应用卷积运算时,它们将在所有这些特征上被激活!
需要理解的一件重要事情是,较高级别的特征将较低级别的特征组合为加权和:在被传递到激活非线性之前,前一层的激活乘以后一层神经元的权重并相加。在这个信息流中,没有任何地方考虑到特征之间的关系。
因此,我们可以说CNN 的主要失败在于它们没有携带任何关于特征之间相对关系的信息。这只是 CNN 核心设计中的一个缺陷,因为它们是基于应用于标量值的基本卷积运算。
胶囊有什么帮助
Hinton 认为,为了正确地进行图像识别,保持图像特征之间的层次姿势关系是很重要的。胶囊引入了一种新的构建模块,可用于深度学习,以更好地对网络内部的这些关系进行建模。当这些关系建立到网络中时,它很容易理解它看到的东西只是它以前看到的东西的另一个视图,因为它不仅仅依赖于独立的特征;它现在把这些特征放在一起,形成一个更完整的“知识”表示。
这种更丰富的特征表现的关键是使用了矢量而不是缩放器。让我们回顾一下 CNN 中卷积的基本操作:矩阵乘法(即标量等待),全部相加,标量到标量映射(即激活函数)。以下是步骤:
- 标量输入标量的加权
- 加权输入的总和标量
- 标量到- 标量非线性
通过使用向量代替标量,可以简单地分解胶囊网络的变化:
- 输入矢量的矩阵乘法
- 输入矢量的标量加权
- 加权输入矢量的总和矢量
- 矢量 -to- 矢量非线性
Capsules vs traditional neurons
向量有帮助,因为它帮助我们编码更多的信息,而不仅仅是任何一种信息,相关和相对信息。想象一下,不仅仅是特征的标量激活,我们认为它的向量包含类似于包含【可能性,方向,大小】的东西。最初的标量版本可能像左边的图一样工作;即使眼睛和嘴唇相对于面部来说很大(95%的可能性),它也能检测到面部!另一方面,胶囊由于其更丰富的矢量信息,看到特征的大小是不同的,因此输出检测面部的较低可能性!请特别注意图表中的分数,以便完全理解。
Left: How a regular CNN would detect facial features. Right: How a capsule network would detect features.
胶囊还有一个好处,就是能够用少得多的训练数据达到很好的准确度。等方差是对可以相互转化的物体的检测。直观上,胶囊检测到脸部向右旋转 20(或向左旋转 20),而不是意识到脸部匹配向右旋转 20 的变体。因此,由于胶囊具有诸如方向、大小等矢量信息,所以它可以在被训练时使用该信息来学习更丰富的特征表示。它不需要 50 个相同的旋转狗的例子;它只需要一个可以很容易转换的向量表示。通过迫使模型学习胶囊中的特征变体,我们可以用更少的训练数据更有效地推断可能的变体。
结论
凭借胶囊提供的丰富信息,它们显示出巨大的潜力,成为我们未来深度网络的主要驱动力。这种丰富性确实带来了新的挑战:更多的数字意味着更多的计算和更多的复杂性。无论如何,人工智能领域一个激动人心的新机遇已经开启。
喜欢学习?
在推特上关注我,我会在那里发布所有最新最棒的人工智能、技术和科学!也在 LinkedIn 上与我联系!
简单且可扩展的分析管道
Source: https://www.flickr.com/photos/bilfinger/14074154115
收集关于应用程序使用和用户行为的数据,例如玩家在游戏中的进度,对于产品团队来说是无价的。通常,整个团队都致力于构建和维护数据管道,以收集和存储应用程序的跟踪数据。然而,随着许多新的无服务器工具的出现,构建用于收集应用程序使用数据的分析管道的障碍已经大大减少。Google 的 PubSub、DataFlow 和 BigQuery 等托管工具使小型团队能够建立可扩展到大量事件的分析管道,同时只需最低的运营开销。这篇文章描述了如何在谷歌云平台(GCP)上建立一个轻量级的分析管道,这个管道是完全管理的(无服务器的)并且可以自动扩展以满足需求。
我受到了谷歌用于手机游戏分析的参考架构的启发。这篇文章的目标是展示一个小团队可以构建和维护一个数据管道,它可以扩展到大型事件量,为数据科学任务提供一个数据湖,为分析团队提供一个查询环境,并具有对其他组件的可扩展性,如应用程序的实验框架。
我用来实现这条数据管道的核心技术是谷歌的数据流,它现在与 Apache Beam 库集成在一起。数据流任务定义了要在事件集合上执行的操作图,这些事件可以是流数据源。这篇文章展示了一个用 Java 实现的数据流任务,它将跟踪事件从 PubSub 主题传输到数据湖和 BigQuery。关于数据流及其概念的介绍可以在谷歌的文档中找到。虽然数据流任务是可移植的,但由于它们现在是基于 Apache Beam 的,这篇文章主要讨论如何将数据流与 GCP 上的其他托管服务结合使用,以构建一个简单、无服务器、可伸缩的数据管道。
My lightweight implementation of the GCP Reference Architecture for Analytics.
执行所有这些功能的数据管道相对简单。管道从 PubSub 读取消息,然后转换事件以实现持久性:管道的 BigQuery 部分将消息转换为 TableRow 对象并直接传输到 BigQuery,而管道的 AVRO 部分将事件批处理到离散的窗口中,然后将事件保存到 Google 存储中。操作图如下图所示。
The streaming pipeline deployed to Google Cloud
设置环境
构建数据管道的第一步是设置编译和部署项目所需的依赖项。我使用了以下 maven 依赖项来为向管道发送事件的跟踪 API 和处理事件的数据管道设置环境。
<!-- *Dependencies for the Tracking API* ->
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-pubsub</artifactId>
<version>0.32.0-beta</version>
</dependency>
</dependencies><!-- *Dependencies for the data pipeline* ->
<dependency>
<groupId>com.google.cloud.dataflow</groupId>
<artifactId>google-cloud-dataflow-java-sdk-all</artifactId>
<version>2.2.0</version>
</dependency>
我使用 Eclipse 来编写和编译本教程的代码,因为它是开源的。然而,其他 ide 如 IntelliJ 为部署和监控数据流任务提供了额外的特性。在将作业部署到 Google Cloud 之前,您需要为 PubSub 和 DataFlow 设置一个服务帐户。设置这些凭证超出了本文的范围,更多细节可以在 Google 文档中找到。
运行这个数据管道的另一个先决条件是在 GCP 上设置一个 PubSub 主题。我定义了一个 raw-events 主题,用于发布和消费数据管道的消息。关于创建 PubSub 主题的更多细节可在这里获得。
要部署这个数据管道,您需要用上面列出的 maven 依赖项设置一个 java 环境,设置一个 Google Cloud 项目并启用计费,在存储和 BigQuery 服务上启用计费,并创建一个用于发送和接收消息的 PubSub 主题。所有这些托管服务都要花钱,但有一个免费层可用于构建数据管道原型。
Sending events from a server to a PubSub topic
发布事件
为了构建可用的数据管道,构建封装发送事件数据细节的 API 是很有用的。跟踪 API 类提供了这一功能,并可用于将生成的事件数据发送到数据管道。下面的代码显示了发送事件的方法签名,并显示了如何生成示例数据。
/** Event Signature for the Tracking API
public void sendEvent(String eventType, String eventVersion, HashMap<String, String> attributes);
*/// send a batch of events
for (int i=0; i<10000; i++) { // generate event names
String eventType = Math.random() < 0.5 ?
"Session" : (Math.random() < 0.5 ? "Login" : "MatchStart"); // create attributes to send
HashMap<String, String> attributes = new HashMap<String,String>();
attributes.put("userID", "" + (int)(Math.random()*10000));
attributes.put("deviceType", Math.random() < 0.5 ?
"Android" : (Math.random() < 0.5 ? "iOS" : "Web")); // send the event
tracking.sendEvent(eventType, "V1", attributes);
}
跟踪 API 建立一个到 PubSub 主题的连接,以 JSON 格式传递事件,并实现一个回调来通知传递失败。下面提供了用于发送事件的代码,它基于 Google 的 PubSub 示例,在这里提供。
// Setup a PubSub connection
TopicName topicName = TopicName.of(projectID, topicID);
Publisher publisher = Publisher.newBuilder(topicName).build();// Specify an event to send
String event = {\"eventType\":\"session\",\"eventVersion\":\"1\"}";// Convert the event to bytes
ByteString data = ByteString.copyFromUtf8(event.toString());//schedule a message to be published
PubsubMessage pubsubMessage =
PubsubMessage.newBuilder().setData(data).build();// publish the message, and add this class as a callback listener
ApiFuture<String> future = publisher.publish(pubsubMessage); ApiFutures.addCallback(future, this);
上面的代码使应用程序能够将事件发送到 PubSub 主题。下一步是在完全托管的环境中处理这些事件,该环境可以根据需要进行扩展以满足需求。
存储事件
数据管道的一个关键功能是让数据科学和分析团队可以使用测量的事件进行分析。用作端点的数据源应该具有低延迟,并且能够扩展到大量事件。本教程中定义的数据管道展示了如何将事件输出到 BigQuery 和数据湖,后者可用于支持大量分析业务用户。
Streaming event data from PubSub to DataFlow
这个数据管道的第一步是从 PubSub 主题中读取事件,并将获取的消息传递给数据流流程。DataFlow 提供了一个 PubSub 连接器,它支持将 PubSub 消息流式传输到其他 DataFlow 组件。下面的代码显示了如何实例化数据管道、指定流模式以及使用来自特定 PubSub 主题的消息。这个过程的输出是一组 PubSub 消息,可以存储起来供以后分析。
// set up pipeline options
Options options = PipelineOptionsFactory.fromArgs(args)
.withValidation().as(Options.class);
options.setStreaming(true);
Pipeline pipeline = Pipeline.create(options);// read game events from PubSub
PCollection<PubsubMessage> events = pipeline
.apply(PubsubIO.readMessages().fromTopic(topic));
我们希望存储事件的第一种方式是采用列格式,这种格式可用于构建数据湖。虽然这篇文章没有展示如何在下游 ETL 中利用这些文件,但是拥有一个数据湖是在需要修改数据库时维护数据集副本的好方法。数据湖提供了一种在由于模式变化或数据接收问题而需要时重新加载数据的方法。分配给该进程的数据管道部分如下所示。
Batching events to AVRO format and saving to Google Storage
对于 AVRO,我们不能使用直接分流的方法。在保存到平面文件之前,我们需要将事件分组到批处理中。在数据流中实现这一点的方法是应用窗口函数,将事件分组为固定的批次。下面的代码应用转换,将 PubSub 消息转换为 String 对象,以 5 分钟为间隔将消息分组,并将结果批量输出到 Google Storage 上的 AVRO 文件。
// AVRO output portion of the pipeline
events
.apply("To String", ParDo.of(new DoFn<PubsubMessage, String>() {
@ProcessElement
public void processElement(ProcessContext c) throws Exception {
String message = new String(c.element().getPayload());
c.output(message);
}
}))// Batch events into 5 minute windows
.apply("Batch Events", Window.<String>into(
FixedWindows.of(Duration.standardMinutes(5)))
.triggering(AfterWatermark.pastEndOfWindow())
.discardingFiredPanes()
.withAllowedLateness(Duration.standardMinutes(5))) // Save the events in ARVO format
.apply("To AVRO", AvroIO.write(String.class)
.to("gs://your_gs_bucket/avro/raw-events.avro")
.withWindowedWrites()
.withNumShards(8)
.withSuffix(".avro"));
总而言之,上面的代码将事件分批放入 5 分钟的窗口中,然后将事件导出到 Google Storage 上的 AVRO 文件中。
这部分数据管道的结果是谷歌存储上的 AVRO 文件的集合,可以用来建立一个数据湖。每隔 5 分钟就会生成一个新的 AVRO 输出,下游 ETL 可以将原始事件解析为经过处理的特定于事件的表模式。下图显示了 AVRO 文件的输出示例。
AVRO files saved to Google Storage
除了创建数据湖之外,我们还希望在查询环境中可以立即访问事件。DataFlow 提供了一个 BigQuery 连接器来实现这一功能,传输到这个端点的数据在短时间内就可以用于分析。下图显示了数据管道的这一部分。
Streaming events from DataFlow to BigQuery
数据管道将 PubSub 消息转换为 TableRow 对象,这些对象可以直接插入到 BigQuery 中。下面的代码由两个应用方法组成:数据转换和 IO 写入程序。转换步骤从 PubSub 读取消息有效负载,将消息解析为 JSON 对象,提取 eventType 和 eventVersion 属性,并使用这些属性以及时间戳和消息有效负载创建 TableRow 对象。第二个 apply 方法告诉管道将记录写入 BigQuery,并将事件追加到现有表中。
// parse the PubSub events and create rows to insert into BigQuery events.apply("To Table Rows", new
PTransform<PCollection<PubsubMessage>, PCollection<TableRow>>() {
public PCollection<TableRow> expand(
PCollection<PubsubMessage> input) {
return input.apply("To Predictions", ParDo.of(new
DoFn<PubsubMessage, TableRow>() {
@ProcessElement
public void processElement(ProcessContext c) throws Exception {
String message = new String(c.element().getPayload());
// parse the json message for attributes
JsonObject jsonObject =
new JsonParser().parse(message).getAsJsonObject();
String eventType = jsonObject.get("eventType").getAsString();
String eventVersion = jsonObject.
get("eventVersion").getAsString();
String serverTime = dateFormat.format(new Date());
// create and output the table row
TableRow record = new TableRow();
record.set("eventType", eventType);
record.set("eventVersion", eventVersion);
record.set("serverTime", serverTime);
record.set("message", message);
c.output(record);
}}));
}})
//stream the events to Big Query
.apply("To BigQuery",BigQueryIO.writeTableRows()
.to(table)
.withSchema(schema)
.withCreateDisposition(CreateDisposition.CREATE_IF_NEEDED)
.withWriteDisposition(WriteDisposition.WRITE_APPEND));
总结一下上面的代码,从 PubSub 消费的每个消息都转换为带有时间戳的 TableRow 对象,然后流式传输到 BigQuery 进行存储。
数据管道的这一部分的结果是,事件将被流式传输到 BigQuery,并可在由 DataFlow 任务指定的输出表中进行分析。为了有效地将这些事件用于查询,您需要构建额外的 ETL 来创建具有模式化记录的已处理事件表,但现在已经有了用于存储跟踪事件的数据收集机制。
Game event records queried from the raw-events table in BigQuery
部署和自动扩展
使用 DataFlow,您可以在本地测试数据管道,或者部署到云中。如果在未指定其他属性的情况下运行代码示例,则数据管道将在本地计算机上执行。为了部署到云并利用此数据管道的自动缩放功能,您需要指定一个新的 runner 类作为运行时参数的一部分。为了运行数据管道,我使用了以下运行时参数:
--runner=org.apache.beam.runners.dataflow.DataflowRunner
--jobName=game-analytics
--project=your_project_id
--tempLocation=gs://temp-bucket
部署作业后,您应该会看到一条消息,说明作业已提交。然后您可以点击 DataFlow 控制台查看任务:
The steaming data pipeline running on Google Cloud
上面指定的运行时配置将不会默认为自动缩放配置。为了部署根据需求扩大规模的作业,您需要指定其他属性,例如:
--autoscalingAlgorithm=THROUGHPUT_BASED
--maxNumWorkers=30
有关设置数据流任务以适应繁重工作负载条件的更多详细信息,请参见 Spotify 的这篇 Google 文章中的和这篇文章。下图显示了 DataFlow 如何根据需要进行扩展以满足需求。
An example of Dataflow auto scaling. The pipeline will scale up and down as necessary to match demand.
结论
现在有各种各样的工具可以用最少的努力为游戏或网络应用建立一个分析管道。使用托管资源使小型团队能够利用无服务器和自动扩展的基础架构,以最少的基础架构管理扩展到大量事件。您可以记录应用程序的所有相关数据,而不是使用数据供应商现成的数据收集解决方案。
这篇文章的目标是展示如何使用 GCP 堆栈建立数据湖和查询环境。虽然这里介绍的方法不能直接移植到其他云,但是用于实现该数据管道核心功能的 Apache Beam 库是可移植的,并且可以利用类似的工具在其他云提供商上构建可伸缩的数据管道。
该架构是对分析和数据科学团队有用的事件收集系统的最小实现。为了满足大多数分析团队的需求,需要将原始事件转换为经过处理的熟事件,以满足业务需求。这个讨论超出了本文的范围,但是分析基础现在应该已经到位,可以构建一个高效的数据平台。
Github 上提供了该示例管道的完整源代码:
游戏分析——游戏分析的全面管理管道
github.com](https://github.com/bgweber/GameAnalytics)
本·韦伯是意外收获数据的首席数据科学家,我们的任务是建立最准确和全面的净值模型。意外收获团队正在壮大,并正在招聘工程师和数据科学家。
用 Scikit-learn 实现机器学习中流水线的一个简单例子
Akagiyama: (Image Source: Author)
今天的帖子将简短明了,我将带你看一个在 python 机器学习中使用管道的例子。我将使用其他一些重要的工具,如GridSearchCV
等。,演示管道的实现,最后解释为什么管道在某些情况下确实是必要的。我们开始吧
根据 scikit-learn 对pipeline
类的定义是
顺序应用变换列表和最终估计器。流水线的中间步骤必须实现 fit 和 transform 方法,而最终的估计器只需要实现 fit。
一旦我们开始在一个简单的数据集上实现 pipeline,上面的语句就更有意义了。这里我使用的是红酒数据集,,其中的“标签”是葡萄酒的质量,范围从 0 到 10。就数据预处理而言,这是一个相当简单的数据集,因为它没有缺失值。
import pandas as pd winedf = pd.read_csv('winequality-red.csv',sep=';')
# print winedf.isnull().sum() # check for missing dataprint winedf.head(3)>>> fixed ac. volat. ac. citric ac. res. sugar chlorides \
0 7.4 0.70 0.00 1.9 0.076
1 7.8 0.88 0.00 2.6 0.098
2 7.8 0.76 0.04 2.3 0.092free sulfur diox. tot. sulfur diox. dens. pH sulphates \
0 11.0 34.0 0.9978 3.51 0.56
1 25.0 67.0 0.9968 3.20 0.68
2 15.0 54.0 0.9970 3.26 0.65 alcohol quality
0 9.4 5
1 9.8 5
2 9.8 5
我们总是可以用seaborn
来检查相关图,或者我们可以用散点图来绘制一些特征,下面是两个这样的图…
Correlation between pH and Acidity (Image: Author)
正如预期的那样,与残糖和酸度相比,酸度和 pH 具有很高的负相关性。一旦我们熟悉并充分利用了数据集,让我们来讨论和实现管道。
顾名思义,*pipeline*
类允许将多个进程粘贴到一个 scikit-learn 估计器中。 *pipeline*
类有拟合、预测和评分方法,就像任何其他估计器一样(例如。LinearRegression
)。
为了实现 pipeline,通常我们首先从数据集分离特征和标签。
X=winedf.drop(['quality'],axis=1)
Y=winedf['quality']
如果你仔细观察了pd.head(3)
的输出,你会发现数据集的特性变化很大。正如我之前解释的,就像主成分分析一样,一些拟合算法需要缩放,这里我将使用一个这样的算法,称为 SVM(支持向量机)。关于 SVM 的更多理论,你可以查看我的其他帖子。
from sklearn.svm import SVC
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split, GridSearchCV
这里我们使用StandardScaler
,它从每个特征中减去平均值,然后缩放到单位方差。
现在,我们准备通过提供步骤列表来创建管道对象。我们的步骤是——标准标量和支持向量机。*这些步骤是由转换器或估算器的名称和实例组成的元组列表。*让我们来看看下面这段代码,以便澄清 -
steps = [('scaler', StandardScaler()), ('SVM', SVC())]from sklearn.pipeline import Pipeline
pipeline = Pipeline(steps) # define the pipeline object.
字符串(’ scaler ‘,’ SVM ')可以是任何东西,因为这些只是为了清楚地识别转换器或估计器的名称。我们可以用make_pipeline
代替流水线,避免命名估计器或变换器。最后一步必须是元组列表中的估计器。
我们用一个random_state=30
将数据集分为训练集和测试集。
X_train, X_test, y_train, y_test = train_test_split(X,Y,test_size=0.2, random_state=30, stratify=Y)
有必要使用 *stratify*
*正如我之前提到的,标签是不平衡的,因为大部分葡萄酒质量属于 5、6 级。*您可以使用 pandas value_counts()
进行检查,它返回包含唯一值计数的对象。
print winedf['quality'].value_counts() >>> 5 681
6 638
7 199
4 53
8 18
3 10
SVM 通常使用两个参数gamma,C
进行优化。我已经在另一篇文章的中讨论了这些参数的影响,但是现在,让我们定义一个我们将在GridSearchCV
中使用的参数网格。
parameteres = {'SVM__C':[0.001,0.1,10,100,10e5], 'SVM__gamma':[0.1,0.01]}
现在我们用管道实例化GridSearchCV
对象,用 5 重交叉验证实例化参数空间。
grid = GridSearchCV(pipeline, param_grid=parameteres, cv=5)
我们可以用它来拟合训练数据集,并在测试数据集上测试算法。我们还可以找到最适合 SVM 的参数,如下所示
grid.fit(X_train, y_train)print "score = %3.2f" %(grid.score(X_test,y_test))print grid.best_params_>>> score = 0.60
{'SVM__C': 100, 'SVM__gamma': 0.1}
由此,我们看到了一个有效地使用网格搜索流水线来测试支持向量机算法的例子。
在另一篇文章中,我已经详细讨论了如何应用 pipeline 和 GridSearchCV 以及如何为 SVM 绘制决策函数。你可以使用任何其他算法,如逻辑回归,而不是 SVM,来测试哪种学习算法最适合红酒数据集。为了在更真实的数据集上应用包括 GridSearchCV 在内的管道中的决策树算法,您可以查看 这篇文章 。
为什么选择管道:
我将用一个简单直观的解释来结束这篇文章,解释为什么管道有时是必要的。它有助于执行所需的应用步骤顺序,创建一个方便的工作流程,确保工作的可重复性。但是,管道中还有更多东西,因为我们使用了网格搜索交叉验证,我们可以更好地理解它。
上例中的管道对象是用StandardScaler
和SVM
创建的。如果单独使用管道,那么对于StandardScaler
,可以如下进行
scale = StandardScaler().fit(X_train)
X_train_scaled = scale.transform(X_train)
grid = GridSearchCV(SVC(), param_grid=parameteres, cv=5)
grid.fit(X_train_scaled, y_train)
这里我们看到了单独应用变压器和估计器的固有问题,其中估计器(SVM)的参数是使用GridSearchCV
确定的。用于交叉验证的缩放特征被分成测试和训练文件夹,但是网格搜索内的测试文件夹已经包含关于训练集的信息,因为整个训练集(X_train)被用于标准化。更简单地说,当SVC.fit()
使用交叉验证完成时,特性已经包括来自测试文件夹的信息,因为StandardScaler.fit()
是在整个训练集上完成的。
人们可以通过使用管道绕过这种过分简化。使用管道,我们将StandardScaler()
和SVC()
粘合在一起,这确保了在交叉验证期间StandardScaler
仅适用于训练折叠,与用于SVC.fit()
的折叠完全相似。安德烈亚斯·穆勒的书给出了上述描述的一个奇妙的图示。
[1]安德烈亚斯·穆勒、萨拉·圭多;用 Python 进行机器学习的介绍;PP-305–320;第一版;莱利·奥出版公司;亚马逊链接
你可以在 G itHub 中找到完整的代码。
干杯!保持坚强!!
如果你对更深入的基础机器学习概念感兴趣,可以考虑加盟 Medium 使用 我的链接 。你不用额外付钱,但我会得到一点佣金。感谢大家!!
初始网络版本的简单指南
初始网络是 CNN 分类器发展的一个重要里程碑。在其出现之前(双关语),大多数流行的 CNN 只是将卷积层越叠越深,希望获得更好的性能。
Designing CNNs in a nutshell. Fun fact, this meme was referenced in the first inception net paper.
另一方面,初始网络是复杂的(大量工程)。它使用了很多技巧来提升性能;无论是速度还是准确度。它的不断演变导致了几个版本的网络的产生。流行的版本如下:
每个版本都是对前一个版本的迭代改进。了解这些升级可以帮助我们构建在速度和准确性上都得到优化的定制分类器。a̶l̶s̶o̶,̶̶d̶e̶p̶e̶n̶d̶i̶n̶g̶̶o̶n̶̶y̶o̶u̶r̶̶d̶a̶t̶a̶,̶̶a̶̶l̶o̶w̶e̶r̶̶v̶e̶r̶s̶i̶o̶n̶̶m̶a̶y̶̶a̶c̶t̶u̶a̶l̶l̶y̶̶w̶o̶r̶k̶̶b̶e̶t̶t̶e̶r̶.̶*(编辑:删除了这句话,因为它相当投机;请忽略相同)*。
这篇博文旨在阐明盗梦空间网络的演变。
盗梦空间 v1
这是一切开始的地方。让我们分析一下它打算解决什么问题,以及它是如何解决的。(论文)
前提是:
- 图像中的显著部分在尺寸上可以有极其大的变化。例如,一幅带有狗的图像可以是以下任意一种,如下所示。在每幅图像中,狗占据的区域是不同的。
From left: A dog occupying most of the image, a dog occupying a part of it, and a dog occupying very little space (Images obtained from Unsplash).
- 由于信息位置的巨大变化,为卷积运算选择正确的内核大小变得困难。一个较大的内核优选用于更多全局分布的信息,一个较小的内核优选用于更多局部分布的信息。
- 非常深的网络容易过拟合。也很难通过整个网络传递梯度更新。
- 天真地叠加大型卷积运算计算量大。
解决方案是:
为什么不让多尺寸的过滤器在同一水平面上运行?这个网络本质上会变得更宽一点,而不是更深一点。作者设计了 inception 模块来反映这一点。
下图是“幼稚”的初始模块。它对一个输入执行卷积,使用 3 个不同大小的滤波器 (1x1,3x3,5x5)。此外,最大汇集也被执行。输出被连接并发送到下一个初始模块。
The naive inception module. (Source: Inception v1)
如前所述,深度神经网络计算量很大。为了让它更便宜,作者通过在 3x3 和 5x5 卷积之前添加额外的 1x1 卷积来限制输入通道的数量。虽然增加一个额外的运算看起来可能违反直觉,但 1x1 卷积比 5x5 卷积便宜得多,而且输入通道数量的减少也有所帮助。但是,请注意,1x1 卷积是在 max pooling 层之后引入的,而不是之前。
Inception module with dimension reduction. (Source: Inception v1)
利用降维初始模块,构建了神经网络结构。这就是广为人知的 GoogLeNet (Inception v1)。该架构如下所示:
GoogLeNet. The orange box is the stem, which has some preliminary convolutions. The purple boxes are auxiliary classifiers. The wide parts are the inception modules. (Source: Inception v1)
GoogLeNet 有 9 个这样的线性堆叠的初始模块。它有 22 层深(27 层,包括池层)。它在最后一个 inception 模块的末尾使用全局平均池。
不用说,是一个挺的深度分类器。如同任何非常深的网络一样,它受到消失梯度问题的影响。
为了防止网络的中间部分被淘汰,作者引入了两个辅助分类器(图中的紫色方框)。他们实际上将 softmax 应用于两个初始模块的输出,并计算了相同标签上的辅助损失。总损失函数是辅助损失和实际损失的加权和**。本文中使用的权重值为每个辅助损失 0.3。**
# The total loss used by the inception net during training.
**total_loss = real_loss + 0.3 * aux_loss_1 + 0.3 * aux_loss_2**
辅助损失不用多说,纯粹用于训练目的,推理时忽略。
盗梦空间 v2
盗梦 v2 和盗梦 v3 在同 论文 中呈现。作者提出了许多提高精确度和降低计算复杂性的改进措施。Inception v2 探索了以下内容:
前提是:
- 减少代表性瓶颈。直觉是,当卷积没有显著改变输入的维度时,神经网络表现得更好。过多地减少维度可能会导致信息丢失,这就是所谓的“表示瓶颈”
- 使用智能因子分解方法,卷积可以在计算复杂性方面变得更有效。
解决方案是:
- 将 5x5 卷积因式分解为两个 3x3 卷积运算,提高计算速度。虽然这看起来可能违反直觉,但 5x5 卷积比 3x3 卷积贵 2.78 倍。因此,堆叠两个 3×3 卷积实际上会导致性能的提升。下图对此进行了说明。
The left-most 5x5 convolution of the old inception module, is now represented as two 3x3 convolutions. (Source: Incpetion v2)
- 此外,它们将滤波器尺寸 nxn 的卷积分解为 1xn 和 nx1 卷积的组合。例如,3×3 卷积相当于首先执行 1×3 卷积,然后对其输出执行 3×1 卷积。他们发现这种方法比单一的 3x3 卷积便宜 33%。下图对此进行了说明。
Here, put n=3 to obtain the equivalent of the previous image. The left-most 5x5 convolution can be represented as two 3x3 convolutions, which inturn are represented as 1x3 and 3x1 in series. (Source: Incpetion v2)
- 模块中的滤波器组被扩展(变得更宽而不是更深)以消除代表性瓶颈。如果模块做得更深,尺寸会过度减小,从而丢失信息。下图对此进行了说明。
Making the inception module wider. This type is equivalent to the module shown above. (Source: Incpetion v2)
- 以上三个原则被用来构建三种不同类型的初始模块(让我们按照它们被介绍的顺序称它们为模块 A、B 和 C )。这些名称是为了清楚起见而介绍的,而不是正式名称)。该架构如下所示:
Here, “figure 5” is module A, “figure 6” is module B and “figure 7” is module C. (Source: Incpetion v2)
盗梦空间 v3
前提
- 作者注意到辅助分类器直到训练过程接近尾声时,准确率接近饱和,才发挥了很大作用。他们争辩说,他们的职能是调整,特别是如果他们有 BatchNorm 或 Dropout 操作。
- 将调查在不大幅改变模块的情况下改进 Inception v2 的可能性。
解决方案
- Inception Net v3 整合了上述针对 Inception v2 的所有升级,此外还使用了以下内容:
- RMSProp 优化器。
- 因子分解的 7x7 卷积。
- 辅助分类器中的 BatchNorm。
- 标注平滑(一种添加到损失公式中的正则化组件,用于防止网络对某个类过于自信。防止过度配合)。
初始版本 4
盗梦空间 v4 和盗梦空间-ResNet 在同 论文 中有介绍。为了清楚起见,让我们在单独的部分中讨论它们。
前提
- 使模块更加一致。作者还注意到一些模块比必要的模块更加复杂。这可以让我们通过添加更多这样的统一模块来提升性能。
解决方案
- 盗梦空间 v4 的词干被修改。这里的词干指的是在引入先启块之前执行的初始操作集。
The top image is the stem of Inception-ResNet v1. The bottom image is the stem of Inception v4 and Inception-ResNet v2. (Source: Inception v4)
- 他们有三个主要的 inception 模块,命名为 A、B 和 C(与 Inception v2 不同,这些模块实际上命名为 A、B 和 C)。它们看起来非常类似于它们的 Inception v2(或者 v3)的对应物。
(From left) Inception modules A,B,C used in Inception v4. Note how similar they are to the Inception v2 (or v3) modules. (Source: Inception v4)
- Inception v4 引入了专门的缩减块,用于改变网格的宽度和高度。早期版本没有明确的 reduction 块,但是功能已经实现。
(From Left) Reduction Block A (35x35 to 17x17 size reduction) and Reduction Block B (17x17 to 8x8 size reduction). Refer to the paper for the exact hyper-parameter setting (V,l,k). (Source: Inception v4)
Inception-ResNet v1 和 v2
受 ResNet 性能的启发,提出了一个混合初始模块。Inception ResNet 有两个子版本,即 v1 和 v2。在我们检查显著的特性之前,让我们看看这两个子版本之间的细微差别。
- Inception-ResNet v1 的计算成本与 Inception v3 相似。
- Inception-ResNet v2 的计算成本与 Inception v4 相似。
- 它们有不同的词干,如 Inception v4 部分所示。
- 两个子版本的模块 A、B、C 和减速块的结构相同。唯一的差异是超参数设置。在这一节中,我们将只关注结构。请参考论文以获得准确的超参数设置(图片来自 Inception-Resnet v1)。
前提
- 引入剩余连接,将初始模块卷积运算的输出添加到输入中。
解决方案
- 对于剩余加法来说,卷积后的输入和输出必须具有相同的维数。因此,我们在原始卷积后使用 1x1 卷积,以匹配深度大小(卷积后深度增加)。
(From left) Inception modules A,B,C in an Inception ResNet. Note how the pooling layer was replaced by the residual connection, and also the additional 1x1 convolution before addition. (Source: Inception v4)
- 主要初始模块中的池化操作被替换为支持剩余的连接。但是,您仍然可以在归约块中找到这些工序。缩减块 A 与初始 v4 的缩减块相同。
(From Left) Reduction Block A (35x35 to 17x17 size reduction) and Reduction Block B (17x17 to 8x8 size reduction). Refer to the paper for the exact hyper-parameter setting (V,l,k). (Source: Inception v4)
- 如果过滤器的数量超过 1000,在体系结构中具有较深残余单元的网络会导致网络“死亡”。因此,为了增加稳定性,作者将剩余激活值调整为大约 0.1 到 0.3。
Activations are scaled by a constant to prevent the network from dying. (Source: Inception v4)
- 原论文没有使用求和后的 BatchNorm 在单个 GPU 上训练模型(在单个 GPU 上拟合整个模型)。
- 据发现,Inception-ResNet 模型能够在较低的时期达到较高的精确度。
- Inception v4 和 Inception-ResNet 的最终网络布局如下:
The top image is the layout of Inception v4. The bottom image is the layout of Inception-ResNet. (Source: Inception v4)
感谢您阅读本文!希望它能让你对盗梦空间网有所了解。如果是的话,请按拍手按钮!如果您有任何问题,可以通过社交媒体或电子邮件(bharathrajn98@gmail.com)联系我。
数据结构的简单介绍:第一部分——链表
编程的世界总是在变化。而且变化很快。我们一直在寻找更好的方法来做我们正在做的事情。这是一件伟大的事情。迭代是一个非常强大的概念。
然而,在计算机科学世界中,有一些想法和结构是不变的。数据结构及其应用就是其中的一部分。
你可能会想,在所有事情中,这是每个程序员或软件工程师都会理解的,对吗?那你就错了。
我可以告诉你,大学毕业后,我肯定不知道何时以及为什么要使用一种数据结构而不是另一种,我发现,现在许多通过实践学习编程的程序员也不知道(代码训练营,让你动手的在线课程,从地下室构建软件)。
老实说,我记得我认为它们真的没有那么重要。我认为它们只在特殊情况下需要,可能是为公共框架或库编写的代码。
伙计,我错了。
了解如何有效地使用数据结构可以很容易地将优秀的开发人员与糟糕的开发人员区分开来。然而,对于一些很难理解这些抽象概念的人来说,真正掌握它们是很困难的。试着从头到尾阅读关于这个主题的事实上的书(“算法简介”——旁注:我知道它说的是“算法”,但它实际上涵盖了数据结构是如何构建的,以适应特定的算法)。
在那本书和许多其他类似的书中,你会发现许多数学证明和想法看起来非常理论化,并且它的抽象本质使得在实际开发软件时很难理解实际的用例。
那么一个程序员如果没有数学学位毕业该怎么办呢?
当许多开发人员第一次意识到数据结构的重要性时(在试图编写一个在几秒钟内处理数百万条记录的系统之后),他们经常会看到为斯坦福大学计算机科学学位的人编写的书籍或文章。
我希望这篇文章能够填补这个空白,并以更实用的方式解释数据结构。我希望人们从这篇文章中学到的是理解为什么我们有不同的数据结构,它们是什么,以及何时使用一个特定的数据结构。
这将是一个简单的介绍,所以我将介绍您 95%的时间会用到的数据结构,剩下的 5%留给您自己去发现。
让我们开始吧!
首先,我们需要定义什么是数据结构。好吧,一群聪明人已经抛出了许多复杂的听起来不错的定义,但是描述数据结构的最简单也是最准确的方式是说数据结构是在计算机中组织数据的一种特殊方式,以便它可以被有效地使用。仅此而已。这只是一种组织数据的方式,就像人类组织书架的方式一样。你要以一种方式来组织它们,使它们容易得到你想要的东西。
例如,继续使用书架类比,如果我想要能够快速地挑选出我拥有的以字母“T”或“B”或任何其他字母开头的所有书籍(假设有数百本),那么我会想要以一种使任务快速且容易执行的方式来组织这些书籍。在这个例子中,这意味着按字母顺序组织书籍。很简单。
然而,如果我使用书架的方式不同(比如我想找到所有与物理学科相关的书籍),那么我们很快就会发现这种书籍的组织方式是有问题的,会导致我在寻找我想要的书籍时效率非常低。
这里的解决方案是根据我们在最常见的场景中如何检索它们,以不同的方式组织书籍。在第二个场景中,我们可能已经决定根据主题来组织书架。这同样适用于数据结构以及你通常如何与它们交互。
让我们开始讨论组织数据的不同方式…又名公共数据结构的类型。为了开始这个有趣的话题,我们将从我最喜欢的数据结构之一开始…
链表:更复杂数据结构的构建块
链表是最简单和最常见的数据结构之一。它们也是一个很好的起点,因为链表是一种数据结构,许多其他数据结构在它们自己的结构中内部使用它。
理解链表将有助于你理解“指针”(一个值——通常以十六进制书写——代表另一个值的内存地址或一个值序列的开始)以及系统如何存储和访问内存。
为了使事情变得简单,我在概念上包含了一个链表的可视化表示。
让我们浏览一下上面的图片,然后我们将解释为什么以这种方式存储数据在某些情况下可能是个好主意,而在其他情况下可能是个坏主意。
首先,你可以看到图像上写着“开始”的地方。这就是所谓的链表的“头”。它只是起点,表示第一个“节点”在内存中的位置(本例中为地址 1000)。“1000”是指向下一个节点位置的指针。
我知道你在想什么。“什么是节点??"。至少这是我第一次读到链表时的想法。简单来说,“节点”就是一个有两个字段的对象。一个字段是“信息”字段(您可以在其中存储一些您关心的数据),另一个是指针字段。指针字段保存的只是下一个节点位置的地址位置。就是这样!这实际上简单得可笑,以至于很多人都想多了。
正如你在上面的图片中看到的,在我们到达内存地址 1000 之后,我们遇到了一个节点。在这个节点中,我们可以看到两个字段。在我们的例子中,保存“信息”的第一个字段是存储字符“A”。第二个字段(指针字段)将内存中的位置存储到下一个节点(内存位置 2000)。
接下来,我们跟随指针到内存位置 2000。一旦我们到达那里,我们会遇到第二个节点。如您所见,第二个节点的信息为“B ”,指针值为“3000”。这里没什么新鲜的!
最后,我们跟随指针值到内存位置(3000 ),来到另一个节点。这个节点和前面两个差不多。它的信息字段有一个值“C ”,但是正如您所看到的,它的指针字段什么也没有(或者为空)。这仅仅意味着我们已经到了列表的末尾。没有更多的指针可循!这个节点(作为最后一个节点)被称为“尾节点”。
那么是什么使得链表作为一种数据结构有价值,我们什么时候以及为什么会选择使用它呢?让我们接下来讨论这个问题。
链表的结构允许它拥有许多属性,这使得它不同于其他集合结构(比如我将在后面的文章中介绍的数组)。由于使用指针而不是连续的内存块,链表非常适合以下情况:
当您需要从列表中进行固定时间的插入/删除时(例如在实时计算中,时间的可预测性是绝对重要的)
当您不知道列表中会有多少项时。对于数组,如果数组变得太大,您可能需要重新声明和复制内存(同样,我将在以后的文章中更详细地介绍数组。)
当您不需要随机访问任何元素时
当您希望能够在列表中间插入项目时(例如优先级队列——我将在后面介绍的另一种数据结构)
然而,有时使用链表会非常低效,使用另一种集合数据结构(如数组)会更好。
在以下情况下不要使用链表:
当您需要对元素进行索引/随机访问时
当您提前知道数组中元素的数量以便可以为数组分配正确的内存量时
当你需要速度的时候。您可以对数组使用指针数学来访问每个元素,而您需要根据链表中每个元素的指针来查找节点,这可能会导致页面错误,从而影响性能。
当记忆成为问题时。填充数组比链表占用更少的内存。数组中的每个元素都只是数据。每个链表节点都需要数据以及一个(或多个)指向链表中其他元素的指针。
正如你所看到的,取决于你想做什么,链表可能是一个很好的数据结构,也可能是一个非常低效的数据结构。这一切都要追溯到理解每种数据结构的优缺点,并为工作选择正确的“工具”。
希望这是一个快速简单的介绍,说明了为什么学习数据结构是重要的,并阐明了链表何时以及为什么是数据结构的重要起点。
接下来,我将讨论数组、堆栈和队列。敬请期待!
如果你能想到任何更好的方法来解释链表,或者为什么理解数据结构很重要,请在评论中留下它们!
如果你喜欢这篇文章,请与他人分享!这是我能得到的最大的赞美。另外,如果你是技术爱好者,请订阅我的博客(jasonroell.com)。祝您愉快,继续学习!
一个简单的机器学习介绍
当我开始学习机器学习时,我一直发现自己对自己在做什么感到困惑。在我看来,很清楚我需要编码什么,但我不明白这种编程方式与我以前开发的有什么不同。我困惑的原因是我对 ML(机器学习)的主要主题和它的基础缺乏了解。所以今天我要解释什么是机器学习的基本概念?它是用来做什么的?它的优点是什么?它有哪些应用?
机器学习是人工智能最受欢迎的分支之一。简单地说,ML 是计算机科学的一个研究领域,它研究一种特殊类型的算法,这种算法可以根据经验自动改进。换句话说,ML 算法与其他算法的不同之处在于,它们能够从我们提供给它们的数据中学习,这是 ML 如此有用的主要原因之一。在这项技术之前,软件工程师需要创建一长串规则,才能开发出能够响应我们需求的程序,但现在这种情况已经发生了变化,因为这些算法不再需要编写成千上万行代码,而是自己创建规则,以获得预期的输出。这将焦点从编码规则转移到了输入数据,使得复杂的问题相对更容易解决。
Photo by Franki Chamaki on Unsplash
这项新技术开启了一个充满可能性和优势的世界,但它的三个主要优势是:
1)它减少了编程时间。
既然这些新算法都是要自己学习规则的,开发者就不再需要对每条规则进行显式编码,现在我们只是让算法自己学习。这方面的一个简单例子是所有提出搜索、音乐或视频建议的算法。你可以花很长时间写下每个案例,如果有人喜欢某种音乐,那么就给另一个,或者你可以制定一个算法,学习每个用户的口味,然后根据以前的数据给出建议。
2)使产品更具可定制性和可扩展性。
回到音乐建议的例子,想象你花了几个月的时间为你当前的市场美国创造了这个算法,但是然后你想把自己扩展到另一个国家,在那里音乐品味不同,建议不再相关。然后,你需要为这个新位置创建一个新的算法,这可能需要很长时间,但如果你使用机器学习,你可以只添加来自这个国家的数据,软件将学习如何自己提出建议,这不仅更快,而且需要更少的努力。
ML 让我们有可能解决人类无法解决的复杂问题。
有很多问题人类根本找不到确切的一套规则来使其运转。这些问题中的大多数都包含数千个可能导致结果偏差的变量,这使得对所有可能的场景进行编码变得极其困难。机器学习算法在生物科学中非常有用,这项技术使我们能够更多地了解来自身体的信号,并能够像在机器人假体中一样绘制它们。
Photo by Franck V. on Unsplash
在机器学习中,有不同类型的算法,这些算法分为三大类:监督学习、非监督学习和强化学习。
监督学习是 ML 的一个分支,其中数据被标记,换句话说,你提供给模型的信息带有答案。在这种类型的算法中,软件通过对输出进行预测,然后将其与实际答案进行比较来学习。在这种比较之后,模型的参数被调整,这个过程被称为反向传播。(我将在下一篇文章中更深入地解释这个话题)重复这个过程,直到模型能够以可接受的精度进行预测。
在无监督学习中,方法不同。在这种类型的算法中,数据是没有标记的,模型的目标是从数据的海洋中创建一些结构。这些类型的程序通常用于发现数据中的模式,这在商业智能中特别有用,它们利用这种模式发现能力来分析客户行为并利用这些模式。
在我看来强化学习是机器学习的一个非常特殊的分支,因为它非常接近人类通过试错来学习的方式。在这种类型的算法中,会创建一个性能函数来告诉模型它所做的是让它更接近目标还是让它走上另一条路。基于这个反馈,模型学习,然后做出另一个猜测,这继续发生,每个新的猜测都更好。这种类型的算法在游戏机器人中被大量使用,这些机器人一开始总是输,但渐渐地他们学会了如何在游戏中取得进展,直到他们可以比人类表现得更好。
如前所述,机器学习已经彻底改变了计算机软件的开发方式。这种算法的两个主要应用是自然语言处理(NLP)和计算机视觉。
自然语言处理是机器学习的一个分支,专注于让人机交互更加自然。在这里,模型的目标是在不需要某些命令的情况下理解人类想要表达的意思。这是最常用的语言翻译和聊天机器人。
另一边的计算机视觉正试图理解这个世界,就像我们用自己的眼睛一样。这个领域的重点是使计算机成为图像识别专家的算法。它可以从简单的算法如数字识别器到复杂的算法,如实时物体识别。
正如你所看到的,机器学习有很多应用,其中一些专注于科学,另一些专注于商业,还有一些只是娱乐,但所有这些都是有用的。这只是对这个主题的一个简单介绍,因为正如我们将在后面的文章中看到的,这个研究领域非常广泛,并且每天都在增长。
这篇文章是一系列文章的一部分,目的是为没有机器学习经验的人提供一条简单的道路。我将尽力以最简单的方式解释每个主题,同时保留该领域所有必要的主题。
使用虚拟足球数据预测球员表现的简单方法
(注:根据意大利语版本,clicca qui )
这篇文章的目标是分析过去几年意甲梦幻足球(又名 Fantacalcio )的相关数据,并使用结果来预测下一个足球赛季的最佳球员。
大部分的文字将探索数据和可视化的球员的分数有见地的信息。最后一部分将展示一个简单的公式,可用于预测未来的表现。
数据
输入数据是从 Fantagazzetta 下载的,对应于为每个球员计算的季节性统计数据。它涵盖了过去 4 个赛季,从 2013 年至 2014 年至 2016 年至 2017 年。最相关的数据字段是每年的**平均芬达分数:**它表示玩家在所有游戏中获得的平均分数,并根据与特定事件(如进球、黄牌、红牌、助攻等)相关的奖金和惩罚进行调整。
在深入分析之前,我确保检查并清理了数据集。在这种情况下,我决定剔除上个赛季参加比赛少于 10 场或者芬达平均分数等于 0 的球员。前者的原因是为了尽量保持数据的“一致性”:比赛次数越高,他的平均得分就越准确。至于零分,它们似乎与没有参加运动的运动员有关,例如,因为他们被转移到不同的国家联盟。保罗·博格巴就是一个例子,他以创世界纪录的价格从尤文图斯转会到英超联赛的曼联。数据集中保存的球员总数是 265 人,每队大约 13 人,这听起来是一个相当合理的数字(一名首发 XI 加上两名替补)。
哪些玩家的趋势最好和最差?
让我们从数据集开始学习一些有趣的东西。有没有每年都在不断变好的选手?而又是谁反其道而行之,不断获得比上赛季更低的分数?
结果表明,芬达平均得分不断增加的运动员人数为 58 人,这相当于总人数 265 人中的大约 1/5(或每队 2 名运动员)。不出所料,绝对分数最高的都是进攻角色。
以下是关于这些结果的一些注释。
- 上赛季的指挥官 埃丁代科在罗马的第二个赛季取得了令人印象深刻的年平均得分飙升,这可能是这位前曼城前锋在第一个赛季表现不佳后所没有预料到的。
- 安德烈亚“伊尔盖洛”贝洛蒂已经成为夏季转会市场的热门人物:在 3 个赛季中得分从 6.4 跃升至 8.5 后,他的€100 米逃脱条款似乎并不那么疯狂。
- Alejandro“El Papu”Gomez 是亚特兰大 B.C .向意甲积分榜 anEuropa 联赛惊人进军的球员之一。
- “莫莫”萨拉赫自从第一次来到意大利后,他的速度给所有人留下了深刻的印象。他的得分不断上升的趋势表明,尤尔根·克洛普为他的球队的动态比赛增添了一名优秀的人才。
- 有趣的是注意到一些守门员在这一套。Szczesny 的国际比赛经验有助于解释他的积极表现。多纳鲁马可能是目前最想要的门将,AC 米兰暂时能够留住他的神童。但最引人注目的名字是费德里科·马尔凯蒂和他的积极趋势,跨越了 4 年,而不是像提到的同事那样只有 2 年。
另一方面,得分不断下降的意甲足球运动员人数为 46 人。这些名字中的大多数都扮演着防守的角色,与之前展示的正好相反。
- 排名倒数第三的意甲球员包括守门员比萨里、波萨维奇和拉曼纳。其中两人随队降级到乙级,这可能解释了糟糕的分数。
- 卡洛斯·巴卡和马里奥·曼祖基奇是这个小组中绝对得分最高的球员。他们的得分下降几乎肯定与进球数量减少有关,因为哥伦比亚人的得分表现较差,克罗地亚人的角色也发生了变化。
- 安东尼奥·坎德雷瓦的数据也不差,尽管这位意大利国民的平均得分在 4 年后首次低于 7.0。
意甲第一年和上一年有哪些球员的差距最好/最差?
这个问题与上一个问题类似,但在这种情况下,我想包括那些年度绩效趋势可能发生变化(即不持续上升/下降)的球员。这个想法是为了看看哪些球员在开始他们的意甲经历后变得更好(或更差)。
- 前 10 名球员中有 3 名效力于 S.S. Napoli: Mertens、Insigne 和 Zieliński。
- 倒数 3 名球员同样被列入负面趋势名单,即比扎里、波萨维奇和拉曼纳。
- 一些负差异最大的人被认为是有才华的潜在客户,他们没有达到预期,例如迪斯特罗、克里斯坦特和伊图贝。
Top-10 players with best last-vs-first year score difference
Bottom-10 players with worst last-vs-first year score difference
哪些球员的职业生涯得分最好/最差?
一个球员职业生涯得分的一个很好的指标是他的年度芬达得分的平均值。不出所料,前 10 名和后 10 名分别包括前锋和门将。
Top-10 players with best career mean score
Top-10 players with worst career mean score
那么,哪些球员有可能成为 2017-2018 赛季意甲联赛的最佳球员呢?
这篇文章的最后一部分展示了一个简单的方法,利用前面几节收集的信息来预测球员下赛季的表现。
考虑到以下参数:
- 第一季和最后一季的得分差异
- 职业得分
- 存在持续的正/负趋势
- 在意甲踢球的年数
- 上一季芬达的平均得分
一个简单的公式可以帮助估计每个球员在下一个赛季的表现。它只是计算以下加权和:
通过选择不同的权重,有可能给足球运动员职业生涯的特定方面或多或少的关联。
定义公式后,为每个球员计算 2017/2018 赛季的加权得分。然后,这些值按降序排列,以获得所需的排名。
所以最后,按角色划分的未来最佳球员名单(包括转投其他联盟的)如下。
守门员
- 马尔凯蒂
- 贝里萨
- 拉斐尔
- 布丰
- 佩林
守军
- 甘贝里尼
- 安泰
- 康蒂
- 卡纳瓦罗
- 里斯波利
- 尖酸刻薄
- 费莉佩
- 库利巴利
- 马西埃罗 a。
- 博努奇
中场球员
- 杰琳斯基
- 奈英戈兰
- 巴塞尔利
- 贝纳西
- 戈麦斯 a。
- 哈姆西克
- 马格纳内利
- 费利佩·安德森
- 佩罗蒂
- 贝尔纳代斯基
向前
- 贝洛蒂
- 梅尔滕斯
- 泽科
- Insigne
- 穆里尔(女子名)
- 伊瓜因
- 伊卡尔迪
- 埃尔沙拉文
- =salat[变体]
- 博列洛
结论
这篇文章提供了一些关于意甲球员过去表现和相关预测的见解。
第一部分显示了一些有趣参数的存在,例如持续增加/减少的趋势以及上一个赛季和第一个赛季的得分差异。然后,这些参数被用来创建一个能够给出关于玩家预测分数的指示的公式。提供了一份按角色划分的下赛季最佳球员名单。
有关更多信息…
一个简单的神经网络
虽然我最近完成了 Udacity 的深度学习纳米学位项目,但我对这个学科还相当陌生。为了进一步深造,我目前正在读一本关于这个主题的优秀书籍,安德鲁·特拉斯克的 探索深度学习 *。*这篇博文的灵感直接来源于这本神奇的书。
那么,什么是神经网络?
神经网络是极其强大的参数模型,它使用矩阵和可微分函数的组合将输入数据转换为输出数据。—安德鲁·特拉斯克
换句话说,神经网络是对输入数据进行建模的一种方式,以便对该数据执行的数学函数产生有意义的结果。让我们深入研究并构建一个来了解更多信息。
def neural_network(input, weight):
prediction = input * weight
return prediction
这是最简单的神经网络。我们接受一个输入(真实世界的数据)和一个权重乘以输入,然后返回结果。例如,让我们说丹麦足球队在最后一场比赛中进了 3 个球。给定 0.2 的权重,我们可以用我们的神经网络预测丹麦有 60%的机会赢得他们的下一场比赛。
$ neural_network(3, 0.2)
$ => 0.6
但是我们如何找到我们的体重呢?
通过有监督的深度学习,我们可以用输入数据和实际结果来训练我们的模型,以确定权重。例如,如果我们有以下丹麦足球队的历史数据。
GOALS | WIN/LOSS
3 | 1
1 | 0
4 | 1
然后,我们可以告诉我们的网络从随机权重开始,计算获胜的可能性。将计算的预测值与实际的赢/输数进行比较,我们可以一遍又一遍地修改我们的权重,直到我们得到一个数字,当乘以我们的输入值(得分)时,得到一个最接近实际赢/输结果的数字。
所以,如果随机数一开始是 0.5,我们的预测将是
3 * 0.5 = 1.5
1 * 0.5 = 0.5
4 * 0.5 = 2
以此为起点,我们的网络对第一场比赛超估 50%,第二场超估 50%,最后一场超估 100%。现在有了这些信息,我们可以回过头来修改我们的权重,以减少我们预测结果的错误率。这就是线性回归的本质,本文不详细讨论。但这应该让你知道我们如何通过监督学习来确定权重,通过减少我们每次修改权重的错误率,并将我们的预测与我们的结果进行比较。
多端输入
在我让你为上述代码如何可能被用于驾驶自动驾驶汽车、翻译语言或赢得围棋比赛而挠头之前,让我们深入了解一个多输入神经网络。
如果我们有更多的数据来预测丹麦足球队是否会赢得下一场比赛,而不仅仅是进球数,会怎么样?假设我们知道目标的数量,他们当前的赢/输比率,以及他们拥有的粉丝数量。
Goals Scored | Win/Loss Ratio | No. Fans
3 | 0.6 | 30,000
有了更多的数据,我们可以组合多个输入来计算预测的加权和。
def neural_network(inputs, weights):
output = 0
for i in range(len(inputs)):
output += (inputs[i] * weights[i])
return output
用下面的权重取我们的 3 点输入数据,我们可以确定丹麦有 78%的可能赢得他们的下一场比赛。
weights = [0.2, 0.3, 0]
inputs = [ 3, 0.6, 30000]$ neural_network(inputs, weights)
$ => 0.78
注意,我们对应的粉丝数量权重为零。你可以想象,给定一组历史游戏数据,球迷的数量可能被证明对实际游戏没有什么影响,因此计算出的权重将接近于零。
积木
恭喜你!你刚刚学习了深度学习神经网络的基本构建模块之一。我们接收数据并对数据执行数学函数(在我们的例子中是乘法)来确定输出。有了更大的数据集,我们可以训练我们的网络来计算更准确的权重。在确定输出之前,我们还可以使用隐藏层来执行更多的功能。
想了解更多信息,我强烈推荐安德鲁·特拉斯克的 摸索深度学习 和观看西拉杰·拉瓦尔的 YouTube 频道。
构建神经网络的简单入门指南
Photo by Alina Grubnyak on Unsplash
从今天开始,你将能够通过 PyTorch 编程并构建一个香草 前馈神经网络 (FNN)。以下是 https://github.com/yhuag/neural-network-lab FNN 的 python jupyter 代码库:
本指南作为基本的实践工作,引导您从头开始构建神经网络。大部分的数学概念和科学决策都被遗漏了。你可以在这方面做更多的研究。
入门指南
- 请确保您的计算机中安装了 Python 和 PyTorch:
2.通过控制台上的命令检查 Python 安装的正确性:
python -V
输出应该是 Python 3.6.3 或更高版本
3.打开一个储存库(文件夹)并创建您的第一个神经网络文件:
mkdir fnn-tuto
cd fnn-tuto
touch fnn.py
开始写代码
以下所有代码都应写入 fnn.py 文件
进口 PyTorch
它会将 PyTorch 载入代码。太好了!良好的开端是成功的一半。
初始化超参数
超参数是预先设置的强有力的参数,不会随着神经网络的训练而更新。
下载 MNIST 数据集
MNIST 是一个巨大的数据库,有大量的手写数字(即 0 到 9)用于图像处理。
加载数据集
下载 MNIST 数据集后,我们将它们加载到我们的代码中。
注: 我们对 train_dataset 的加载过程进行了洗牌,使学习过程独立于数据顺序,但 test_loader 的顺序仍然是为了考察我们是否能处理输入的未指定偏序。
建立前馈神经网络
现在我们已经准备好了数据集。我们将开始构建神经网络。概念图如下所示:
FNN image retrieved from http://web.utk.edu/
前馈神经网络模型结构
FNN 包括两个完全连接的层(即 fc1 和 fc2)以及其间的一个非线性 ReLU 层。通常我们称这种结构为1-隐藏层 FNN ,不包括输出层(fc2)。
通过运行正向传递,输入图像(x)可以通过神经网络,并生成输出(out ),展示它属于 10 个类别中的每一个的受欢迎程度。例如,一个猫的形象对狗类可以有 0.8 的喜欢度,对飞机类可以有 0.3 的喜欢度。
实例化 FNN
我们现在根据我们的结构创建一个真正的 FNN。
net = Net(input_size, hidden_size, num_classes)
启用 GPU
注意: 你可以启用该行运行 GPU 上的代码
# net.cuda() # You can comment out this line to disable GPU
选择损失函数和优化器
损失函数(标准)决定了输出如何与一个类进行比较,从而决定了神经网络的表现好坏。并且优化器选择一种方式来更新权重,以便收敛到该神经网络中的最佳权重。
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(net.parameters(), lr=learning_rate)
培训 FNN 模型
根据您的机器,此过程可能需要大约 3 到 5 分钟。详细的解释在下面的代码中作为注释(#)列出。
测试 FNN 模型
类似于训练神经网络,我们还需要加载一批测试图像并收集输出。不同之处在于:
- 无损失和重量计算
- 没有重量更新
- 具有正确的预测计算
保存训练好的 FNN 模型以备将来使用
我们将训练好的模型保存为一个 pickle 供以后加载和使用。
torch.save(net.state_dict(), ‘fnn_model.pkl’)
恭喜你。您已经完成了第一个前馈神经网络的构建!
下一步是什么
保存并关闭文件。开始在控制台运行文件:
python fnn.py
您将看到培训过程如下所示:
感谢你的时间,希望你喜欢这个教程。所有代码都可以在这里找到!
功劳:代码都是基于 yunjey 的 伟大的code base编写的。❤
一个简单的词义消歧应用程序
词义消歧已经成为自然语言处理和机器学习领域的一个研究热点。词义消歧从根本上解决了因不同语境中词义不同而产生的歧义。
例如,考虑这两个句子。
" 银行 周六不接受现金. "
谓河水漫过堤岸。 ”
第一句中的 银行 一词是指商业(金融)银行,第二句是指河岸。由此产生的模糊性对于机器来说很难检测和解决。检测歧义是第一个问题,解决它并显示正确的输出是第二个问题。这里,给出的代码是为了解决第二个问题。请随意投稿。
给我看看代码……
我们将使用的导入是,
自然语言工具包 (NLTK)库,是由普林斯顿大学开发的免费开源工具。这个库为我们的应用程序执行核心功能。它提供了训练数据集、Wordnet 语料库、各种标记器、词条分类器、词干分析器和标签器。
函数 simpleFilter 将给定的查询/句子作为输入,并返回一个经过词汇化的标记列表。词条化是指推导出词形正确的词根。在 词干化 和 词干之间出现了一点混淆。然而,这两个词的味道不同。词干化通常指一种粗糙的启发式过程,即砍掉单词的词尾,希望在大多数时候都能正确实现这一目标,通常还包括去除派生词缀。词汇化通常是指通过使用词汇和词形分析来正确地做事情,通常旨在只去除屈折词尾,并返回单词的基本形式或词典形式,这就是所谓的 引理 。
停用词,是一种语言中的高频词,对句子的主题没有多大作用。在英语中,这样的词包括,“一个”,“一个”,“of”,“to”等…我们去掉这些词,专注于我们的主题,以解决歧义。该函数应用于训练数据集以及用户输入。
接下来,我们执行相似性检查,function:similarity check,用于过滤第一个函数返回的句子标记。检查给定查询/句子标记和训练数据集标记之间的相似性。为此,从 wordnet 语料库中为每个单词加载同义词集。一个单词的深度和接近度是按照 0-1 的范围计算和返回的。这是解决模糊性的主要数据。你提供的数据越多,它就越准确。存储句子之间的标准化相似度。
synonymsCreator 是一个简单的函数,用于存储给定输入单词的同义词。这将用于存储给定数据集的同义词和查询标记。在对句子执行相似性检查时,还会考虑同义词。
一旦存储了相似性,我们就应用下一级过滤器,function:filtered sentence,对词干标记应用词条化,并再次删除停用词。在过滤后的句子列表中,我们现在存储单词及其同义词,以便进行更精确的匹配/相似性检查。接下来,我们把所有这些放在一起。
这里所做的是,应用程序被提供了两个数据集文件,第一个是cricketbat . txt,其中包含几个涉及板球运动中使用的蝙蝠的句子,第二个是vampire bat . txt,其中包含几个涉及哺乳动物鸟蝙蝠的句子。 sent1 存储 vampirebat.txt 文件中降低的案例字符串数据,sent2 存储 cricketbat.txt,sent3 存储用户查询。接下来,使用上述函数过滤句子并检查相似性。这种比较是标准化的,无论查询是指 板球球棒 还是 哺乳动物球棒,都会相应地给出输出。
这个程序给出非常准确的答案。它唯一不能处理的是否定句,比如“哪只蝙蝠是 而不是 用来打板球的”,“哪只蝙蝠是*而不是 *飞”等等。在这里随意贡献模块给它。
数据集和代码已经包含在库中。
神经网络导论
在过去的几年里,神经网络已经成为机器学习的同义词。最近,我们已经能够制造神经网络,它可以产生栩栩如生的面孔,转移动态艺术风格,甚至可以按年“老化”一个人的照片。如果你想知道神经网络背后的直觉是如何工作的,但不知道从哪里开始,你来对地方了!
在本教程结束时,您应该能够理解神经网络工作的方式和原因。您还将仅使用 NumPy 从头开始创建一个非常简单的神经网络。线性代数/多元微积分和 Python 编程的经验是本教程的先决条件。
向前传球
任何一个神经网络的项目都是基于某个输入生成一个预测。这听起来非常模糊,但这是因为每个神经网络的任务略有不同,所以这个定义是一刀切的。输入的一些例子可以是三维张量形式的图像、属性的输入特征向量或字典嵌入的向量;输出的一些示例可能是预测的分类标签(分类)或预测的非离散值(回归)。
给我数据
我们举个简单的例子,让这个概念不那么模糊。我们有这个 16 个数据点的玩具数据集,每个数据点有四个特征和两个可能的类别标签 (0 或 1):
这是数据集的代码:
**import** numpy **as** np
X = np.array([[1, 1, 0, 1], [0, 1, 0, 1], [0, 1, 0, 1], [1, 0, 1, 0], [0, 1, 1, 0], [1, 0, 1, 1], [0, 0, 0, 0], [1, 1, 1, 0], [0, 0, 1, 1], [1, 1, 0, 1], [0, 0, 1, 0], [1, 0, 0, 0], [1, 1, 1, 1], [0, 1, 1, 1], [1, 0, 0, 1], [1, 0, 0, 1]])y = np.array([[0], [0], [0], [1], [1], [1], [0], [1], [1], [0], [1], [0], [1], [1], [0], [0]])
如您所见,类标签与第三个特性的值完全相同。我们希望网络能够准确地将要素转换为预测的类别标注概率,也就是说,输出应该具有两个可能类别的概率。如果我们的网络是准确的,我们将对正确的类有高的预测概率,对不正确的类有低的预测概率。
在我们继续之前,有一点很重要:我们不能用所有的 X 来训练我们的网络。这是因为我们期望我们的网络在它看到的数据上表现良好;我们真的很有兴趣看看它在还没有看到的数据上表现如何。我们想要两种类型的看不见的数据:一个是数据集,我们可以基于它定期评估我们的网络,称为验证集,另一个是数据集,它模拟“真实世界的数据”,我们只评估一次,称为测试集。其余用于训练的数据被称为训练集。下面是执行该分割的代码:
X_train = X[:8, :]
X_val = X[8:12, :]
X_test = X[12:16, :]
y_train = y[:8]
y_val = y[8:12]
y_test = y[12:16]
网络层
单词“ layers ”在机器学习的上下文中经常出现。说实话,它只不过是一种数学运算*,经常涉及乘法权重矩阵和加法偏差向量*。
让我们只取其中一个数据点,它是一组四个特征值。我们希望将输入数据点(1×4 维的矩阵)转换为标签概率向量(1×2 维的向量)。为此,我们只需将输入乘以一个 4 乘 2 的权重矩阵。就是这样!不完全是这样,我们还在乘积中增加了一个 1 乘 2 的偏置向量,只是为了增加一个额外的学习参数。
代码如下:
W = np.random.randn(4, 2)
b = np.zeros(2)
linear_cache = {}
**def** linear(input):
output = np.matmul(input, W) + b
linear_cache[**"input"**] = input
**return** output
(暂时不用担心缓存;这将在下一节中解释)
这种操作方式的一个非常酷的结果是,我们不需要只给图层一个数据点作为输入!如果我们取四个数据点,输入是 4 乘 4,输出是 4 乘 2(四组分类概率)。在训练时间使用的数据集子集被称为批次。
概率最高的类是网络的猜测。太好了!除了网络会严重出错——毕竟所有的权重都是随机的!言简意赅地说:现在我们已经有了网络猜测*,我们需要让它正确地猜测*。**
向后传球
为了使网络的猜测更好,我们需要找到权重和偏差的“好”值,以便正确猜测的数量最大化。我们通过找到一个代表我们的猜测有多“错误”的标量值(称为损失值)并使用多元微积分将其最小化来做到这一点。
损失函数(或“取 L”)
在计算标量损失之前,我们希望将任意值的“概率”转换成适当的概率分布。我们通过对以下值计算 softmax 函数来实现这一点:
对于线性输出中的每个 f_j 值。为了得到标量损失值,计算正确类的交叉熵:
正确的类值 f_i。下面是代码:
**softmax_cache = {}
**def** softmax_cross_entropy(input, y):
batch_size = input.shape[1]
indeces = np.arange(batch_size)
exp = np.exp(input)
norm = (exp.T / np.sum(exp, axis=1)).T
softmax_cache[**"norm"**], softmax_cache[**"y"**], softmax_cache[**"indeces"**] = norm, y, indeces
losses = -np.log(norm[indeces, y])
**return** np.sum(losses)/batch_size**
反向传播
我们现在的工作是最小化损失值,我们需要改变权重来做到这一点。我们利用链式法则的美丽来实现这一点。
我们举一个超级简单的例子来论证这个概念。我们来定义一个函数:L(x) = 3x + 2。下面是一个函数图:
我们想求 L 相对于 x 的导数。我们可以通过处理子函数处的每个图节点,求每个子函数处的偏导数,然后乘以引入的导数来实现:
我们来分析一下。L 对 L 的导数正好是 1。n + 2 的导数为 1;乘以 1(引入渐变)等于 1。导数 3n 是 3;乘以 1 等于 3。最后,n 的导数是 2n;乘以 3 是 6n。的确,我们知道 3x +2 的导数是 6x。通过递归使用链式法则求关于参数的导数称为反向传播**。我们可以对复杂的神经网络做同样的事情,首先将网络绘制成图形:**
现在,我们可以通过网络反向传播,找到关于权重和偏差的导数(当变量是非标量时,称为梯度**)😗*
通过该图的反向传播具有与上图相同的基本规则。然而,这里仍然有很多事情要做,所以让我们再来分析一下。第二个梯度是整个soft max/交叉熵函数的梯度;它基本上说明了导数与前向传递的 softmax 的输出相同,只是我们从正确的类中减去 1。梯度的推导不在本文的讨论范围内,但是你可以在这里阅读更多。此外,b 和 q 的维数不同,所以我们需要对一个维数求和,以确保维数匹配。最后,x^T 是输入矩阵 x 的转置。希望现在清楚了为什么我们需要缓存某些变量:在正向传递中使用/计算的一些值需要在反向传递中计算梯度。以下是向后传球的代码:
****def** softmax_cross_entropy_backward():
norm, y, indeces = softmax_cache[**"norm"**], softmax_cache[**"y"**], softmax_cache[**"indeces"**]
dloss = norm
dloss[indeces, y] -= 1
**return** dloss
**def** linear_backward(dout):
input = linear_cache[**"input"**]
dW = np.matmul(input.T, dout)
db = np.sum(dout, axis=0)
**return** dW, db**
更新规则
相对于参数的反向传播给出了最陡的变化方向。所以,如果我们向与相反的*方向移动,那么我们将减少函数值。向最陡下降方向移动的最简单算法称为梯度下降** — 将梯度乘以某个值α并从参数中减去它:***
乘性值α非常重要,因为如果它太大,我们可能会超过最小值,但如果它太小,我们可能永远不会收敛。我们在权重更新中采取的步长被称为学习率。学习率是一个超参数,一个我们可以改变的值,以在我们训练的网络中产生不同的结果。
请注意,在我们的训练制度的代码中,注释了“参数更新”一节:
***def** eval_accuracy(output, target):
pred = np.argmax(output, axis=1)
target = np.reshape(target, (target.shape[0]))
correct = np.sum(pred == target)
accuracy = correct / pred.shape[0] * 100
**return** accuracy
*# Training regime* **for** i **in** range(4000):
indeces = np.random.choice(X_train.shape[0], 4)
batch = X_train[indeces, :]
target = y_train[indeces]
*# Forward Pass* linear_output = linear(batch)
loss = softmax_cross_entropy(linear_output, target)
*# Backward Pass* dloss = softmax_cross_entropy_backward()
dW, db = linear_backward(dloss)
*# Weight updates* W -= 1e-2 * dW
b -= 1e-2 * db
*# Evaluation* **if** (i+1) % 100 == 0:
accuracy = eval_accuracy(linear_output, target)
print (**"Training Accuracy: %f"** % accuracy)
**if** (i+1) % 500 == 0:
accuracy = eval_accuracy(linear(X_val), y_val)
print(**"Validation Accuracy: %f"** % accuracy)
*# Test evaluation* accuracy = eval_accuracy(linear(X_test), y_test)
print(**"Test Accuracy: %f"** % accuracy)*
以下是全部代码的完整要点:
也可以在这里找到:https://gist . github . com/ShubhangDesai/72023174 E0 d 54 f 8 FB 60 ed 87 a 3 a 58 EC 7 c
就是这样!我们有一个非常简单的,一个隐藏层的神经网络,可以训练它在我们的玩具数据集上产生 100%的验证和测试准确性。
后续步骤
我们使用的层类型被称为线性或全连接层。今年夏天,我将写更多关于其他类型的层和网络架构的文章,以及关于比玩具数据集更酷的应用程序的文章。小心那些东西!
有一些很棒的关于机器学习的在线课程是免费的。Coursera ML 课程当然是经典,但我也推荐斯坦福 CS 231n 的课程材料。这是一门硕士水平的课程,我有幸在上个季度修过;这是令人难以置信的教学和密集。
我还会推荐查看一些关于 TensorFlow 和 PyTorch 的初学者教程。它们是最受欢迎的开源深度学习库,理应如此。教程深入浅出,易于理解。
现在你已经有了神经网络的基础,你已经正式进入了一个令人兴奋的快速变化的领域。去,探索领域!我真的希望机器学习能激发你和我一样的敬畏和惊奇。
如果你喜欢这篇文章,请一定给我一个掌声,并关注我,在你的 feed 中看到我未来的文章!另外,看看我的 个人博客 ,关注我的 推特 了解更多我的想法。
佐治亚州不活跃选民的“激增”
用 R 分析佐治亚州选民档案中不活跃的和被清除的选民
介绍
最近的中期选举引发了关于如何、何时以及为什么应该从选民登记文件中清除选民的讨论和辩论。被清除意味着一个选民已经从一个州的选民登记中被删除。乔治亚州当选州长、前州务卿布莱恩·肯普(Brian Kemp)在今年的中期选举中受到密切关注,他被指控在过去的几年里转移了数十万倾向于有色人种的选民。分析人士不仅发现从选民档案中除名的人数不成比例,而且这些选民中的许多人完全有资格,并且不知道他们被除名了。为了更好地了解删除选民的过程,我查看了 2017 年 9 月和 11 月的佐治亚州选民登记文件,以了解谁被删除,为什么被删除,何时被删除,以及这些选民是谁的种族。我对种族感兴趣,因为它经常是佐治亚州和全国关于选举权和选民压制的争论焦点。
工具和数据
对于这个项目,我使用的是 2017 年 9 月和 11 月的佐治亚州选民文件。为了分析数据,我将 R 与 R Studio 和一些有用的库一起使用。
library(tidyverse)
library(RColorBrewer)
library(lubridate)
library(ggpubr)
library(scales)
Tidyverse 是整理数据最有用的库。它让我能够轻松地对数据进行分类。例如,它提供了根据特定值的属性进行过滤或根据特定列进行分组的能力。这在我创建数据子集时非常有用。大多数其他库只是为了图形的美观。
为了整理数据,我创建了两个转换变量类型的函数——将字符类型转换为日期或数字类型——以及一个向量来帮助消除数据中不需要的和无用的列。
# We need to change our dates to Date types
setAs("character","myDate", function(from) ymd(from))# We are only given birth year, so we need to convert that to their # age
setAs("character", "age", function(from) as.integer(2017 - as.numeric(from)))# We need to skip some columns that are not useful to us
# as well as read in the correct type for the column
readcolumns <- c(rep("character", 3), rep("NULL", 9), rep("character", 2), "age", "myDate", rep("character", 2), rep("NULL",2), rep("character", 2), rep("NULL",25), rep("myDate", 4), "NULL", "character", "myDate", rep("NULL", 9))# Sept 2017 GA Voter File
working.dir <- "C:/Users/Bradley/Desktop/Election Research/Georgia_Daily_VoterBase/"ga_voter.file <- paste(working.dir, "Georgia_Daily_VoterBase.txt", sep="")ga_voter.sept17 <- as.tibble(read.csv(ga_voter.file, header = TRUE, sep = "|", quote = "", dec = ".", colClasses = readcolumns, fill = TRUE, stringsAsFactors = FALSE))colnames(ga_voter.sept17)[6] <- "AGE"
为了使日期更容易处理,我创建了一个包装函数,它使用了来自 lubridate 库的 ymd()函数(year,month day)。此函数将日期列转换为日期类型。
佐治亚州的选民档案不包括年龄,而是出生年份。我创建了一个将出生年份转换成年龄的函数。我还把栏目名改成了“年龄”。我最终没有在这个项目中使用这个属性,但是它可能在将来的分析中有用。
我在 2017 年 11 月的佐治亚州选民文件上使用了所有相同的函数和“readcolumns”向量。
你可以在我的 github 上找到这个项目的所有代码。
分析
为了开始我的分析,我查看了 2017 年 9 月佐治亚州选民文件中的非活跃选民和他们的“日期改变”状态。在乔治亚州的选民档案中,“更改日期”类别是选举官员最后一次更新登记的日期。我决定查看非活跃选民的日期更改频率,因为如果大规模选民清洗开始,我预计会看到大量选民的状态在大群体中被更改为非活跃。这样做,我发现了三大“尖峰”:2015 年 8 月 8 日,2015 年 10 月 5 日,2017 年 8 月 9 日。
# All Inactive voters from the Sept 2017 voter file
inactive.sept17 <- ga_voter.sept17 %>% filter(VOTER_STATUS == "I")
Figure 1: Frequency graph of dates changed among inactive voters.
为什么这么多注册在这些日期被改变?
这些峰值让我们深入了解佐治亚州的选民清洗过程。注册人数的大幅增加是因为佐治亚州在重大选举(中期选举和总统选举)结束的年份清理选民档案,这就是为什么日期相隔一年,但在一年中的同一时间。我发现这些尖峰仍然很有趣。在三个主要峰值中,我决定关注 2017 年 8 月 9 日,主要有两个原因:
- 我有 2017 年的其他选民文件,所以我有更多那一年的数据。
- 2017 年 8 月 8 日,司法部决定俄亥俄州大规模清洗选民的方法是可以接受的。佐治亚州使用与俄亥俄州相似的方法来清洗选民。
在整个分析中,我将 2017 年 8 月 9 日称为“尖峰”。
谁在秒杀?
首先,我想看看 2017 年 9 月选民文件中的选民比例。接下来,我想看看,在 2017 年 9 月的数据集中,所有不活跃的选民中,有多少人处于峰值。
# Inactive voters in the spike
inactive.spike <- inactive.sept17 %>%
filter(DATE_CHANGED == “2017–08–09”)# voters in spike / all inactive voters
spike_by_inactive <- nrow(inactive.spike) / nrow(inactive.sept17) * 100# voters in spike / all voters
spike_by_total <- nrow(inactive.spike) / nrow(ga_voter.sept17) * 100spike <- tibble(Voters = c('Of Total', 'Of Inactive'),
Percent = c(spike_by_total, spike_by_inactive))
Figure 2: Bar chart showing percent of voters in spike among total voters and total inactive voters.
该峰值包含 2017 年 9 月佐治亚州选民文件中所有选民的 2.1%和所有非活跃选民的 22.6%。因此,峰值包含数据集中的大部分非活跃投票者。
接下来,我查看了所有选民、非活跃选民和峰值内选民的种族分类。为了检查尖峰中任何潜在的成员比例失调,我在数据集中创建并可视化了三个群体子集:
- 穗中的选民
- 不活跃的选民
- 整个 2017 年 9 月选民档案的选民总数
利用这些子集,我按种族将他们分组。通过比较高峰和非活跃选民总数中的百分比与选民百分比,我可以检查一个群体在高峰或非活跃中的存在是否与他们在数据集中的总体代表性不相称。例如,如果一个群体在尖峰信号中所占的比例大于总比例,那么它就是不相称的。
# Total percent by race
total.race <- ga_voter.sept17 %>%
group_by(RACE) %>%
summarise(Total = n()/nrow(ga_voter.sept17) * 100) %>%
arrange(desc(Total))
total.race# Spike percent by race
spike.race <- inactive.spike %>%
group_by(RACE) %>%
summarise(Spike = n()/nrow(inactive.spike) * 100) %>%
arrange(desc(Spike))
spike.race# Inactive percent by race
inactive.race <- inactive.sept17 %>%
group_by(RACE) %>%
summarise(Inactive = n()/nrow(inactive.sept17) * 100) %>%
arrange(desc(Inactive))
inactive.race# Make a table for all results to compare proportions
total_inactive <- merge(x = total.race, y = inactive.race, by = "RACE")
total_inactive_spike.1 <- merge(x = total_inactive, y = spike.race, by = "RACE") %>%
arrange(desc(Total))
format(total_inactive_spike.1, digits=1, nsmall=2)# Using gather, we can make the data more friendlier to work with in a graph
total_inactive_spike.2 <- total_inactive_spike.1 %>%
gather(Total, Inactive, Spike, key="Voters", value="Percent") %>%
arrange(RACE)
format(total_inactive_spike.2, digits=1, nsmall=2)
Figure 3: Bar graph representing the racial breakdown of voters in the spike, total inactive voters, and total voters.
各组中白人占多数(斯派克占 54.7%;占非活跃选民总数的 52.5%;占总选民的 54.9%)。因为白人在总选民中占大多数,他们在高峰和非活跃选民总数中的代表性并不令人关注。对所有群体来说,这些百分比表明,在高峰和非活跃选民总数中的代表性是成比例的。为了扩展这个项目,我们可能会使用某种类型的分布测试来统计比较这些比例。
既然我们已经对选民档案有了一个全面的了解,我决定看看高峰时期的活动。对于这部分分析,我决定对三个不同的组重复上面的分析:
- 参加 2016 年大选投票的人
- 在高峰和 2017 年 11 月之间被清洗的人
- 在 2016 年大选和中投票的人在高峰和 2017 年 11 月之间被移除
对于每个群体(如上所述),我检查了我之前创建的三个子群体中的每一个——(1)处于高峰的选民,(2)不活跃的选民,以及(3)整个 2017 年 9 月选民文件中的总选民。
第一组:参加 2016 年大选投票的人
为了查看在高峰期间投票的每个人,我采用了我最初的非活跃选民子集,并根据他们更改日期的高峰日期和他们上次投票日期的 2016 年选举日进行了过滤。我只选择了选举日,没有包括提前投票的选民,因为我找不到任何提前投票的人。
# Spike voted
spike.voted <- inactive.sept17 %>%
filter(DATE_CHANGED == "2017-08-09" & DATE_LAST_VOTED == "2016-11-08")voted_by_spike <- nrow(spike.voted) / nrow(inactive.spike) * 100voted_by_inactive <- nrow(spike.voted) / nrow(inactive.sept17) * 100voted_by_total <- nrow(spike.voted) / nrow(ga_voter.sept17) * 100voted <- tibble(Voted = c('Total', 'Inactive', 'In Spike'),
Percent = c(voted_by_total, voted_by_inactive, voted_by_spike))
Figure 4: Bar graph of 2016 General Election voters who are in the spike among all spike, inactive, and total voters.
我很惊讶(也有点怀疑)约 35%的 spike 在 2016 年选举日投了票。一个值得问的问题是,为什么 47931 名选举日选民在不到一年后变得不活跃?我们还应该记住,在总统选举一年后的 2017 年 9 月选民文件中,这个峰值占所有非活跃选民的 22.6%。
查看每个人口的种族分类,我重复了按种族分组的过程,创建了每个种族按其受尊重的人口所占百分比的表格,并合并了结果。
# Voted by race / all voted
voted_overall.race <- all.voted %>%
group_by(RACE) %>%
summarise(Total = n()/nrow(all.voted) * 100) %>%
arrange(desc(Total))
voted_overall.race# Voted in spike by race / all voted in spike
voted_in_spike.race <- spike.voted %>%
group_by(RACE) %>%
summarise(Spike = n() / nrow(spike.voted) * 100) %>%
arrange(desc(Spike))
voted_in_spike.race# Voted inactive by race / all voted inactive
voted_by_inactive.race <- inactive.voted %>%
group_by(RACE) %>%
summarise(Inactive = n() / nrow(inactive.voted) * 100) %>%
arrange(desc(Inactive))
voted_by_inactive.race# Make a table for all results to compare proportions
overall_inactive <- merge(x = voted_overall.race, y = voted_by_inactive.race, by = "RACE")
overall_inactive_spike.1 <- merge(x = overall_inactive, y = voted_in_spike.race, by = "RACE") %>%
arrange(desc(Total))
format(overall_inactive_spike.1, digits=1, nsmall=2)# Using gather, we can make the data more friendlier to work with in a graph
overall_inactive_spike.2 <- overall_inactive_spike.1 %>%
gather(Total, Inactive, Spike, key="Voters", value="Percent") %>%
arrange(RACE)
format(overall_inactive_spike.2, digits=1, nsmall=2)
Figure 5: Bar graph representing the racial breakdown of 2016 General Election day voters in the spike, total inactive voters, and total voters.
看起来,在总选民中,不活跃选民和活跃选民明显不成比例。我们可以看到这一点,因为当白人的比例从总数下降到不活跃和/或支持选民的比例时,黑人的比例却上升了。这一结果表明,白人在高峰和非活跃选民群体中的代表性低于他们的总体代表性,而黑人的情况正好相反——他们的代表性似乎不成比例。
第二组:在斯派克和 2017 年 11 月之间被清除的人
在看了投票的人之后,我的下一个问题是,一旦人们变得不活跃,他们会在多长时间内被移除?
我使用了 2017 年 11 月佐治亚州的选民文件,并在 2017 年 11 月的选民文件中寻找处于峰值但不的人。这意味着在他们变得不活跃和 2017 年 11 月选民文件发布之间的某个时候,选民已经被清除。
# How many were purged from the entire voter file?
purged.all <- ga_voter.sept17 %>% filter(!(ga_voter.sept17$REGISTRATION_NUMBER %in% ga_voter.nov17$REGISTRATION_NUMBER))# How many were purged from the spike?
purged.spike <- inactive.spike %>% filter(!(inactive.spike$REGISTRATION_NUMBER %in% ga_voter.nov17$REGISTRATION_NUMBER))# How many were purged that were inactive?
purged.inactive <- inactive.sept17 %>% filter(!(inactive.sept17$REGISTRATION_NUMBER %in% ga_voter.nov17$REGISTRATION_NUMBER))purged_by_spike <- nrow(purged.spike) / nrow(inactive.spike) * 100purged_by_inactive <- nrow(purged.spike) / nrow(inactive.sept17) * 100purged_by_total <- nrow(purged.spike) / nrow(ga_voter.sept17) * 100purged <- tibble(Voters = c('Total', 'Inactive', 'In Spike'),
Percent = c(purged_by_total, purged_by_inactive, purged_by_spike))
Figure 6: Bar graph of spike voters who were removed between September and November 2017 in the spike, total inactive voters, and total voters.
不到 1%的尖峰被清除出选民档案。虽然这只是一个很小的百分比,但仍有 900 多人。
接下来,我研究了这个群体的种族划分——从峰值中清除了选民。
# Purged by race / all purged
purged_total.race <- purged.all %>%
group_by(RACE) %>%
summarise(Total = n() / nrow(purged.all) * 100) %>%
arrange(desc(Total))
purged_total.race# Purged by race in spike / purged in spike
purged_by_spike.race <- purged.spike %>%
group_by(RACE) %>%
summarise(Spike = n() / nrow(purged.spike) * 100) %>%
arrange(desc(Spike))
purged_by_spike.race# Purged by race inactive / all inactive
purged_by_inactive.race <- purged.inactive %>%
group_by(RACE) %>%
summarise(Inactive = n() / nrow(purged.inactive) * 100) %>%
arrange(desc(Inactive))
purged_by_inactive.race# Make a table for all results to compare proportions
purged_inactive <- merge(x = purged_total.race, y = purged_by_inactive.race, by = "RACE")
purged_inactive_spike.1 <- merge(x = purged_inactive, y = purged_by_spike.race, by = "RACE") %>%
arrange(desc(Total))
format(purged_inactive_spike.1, digits=1, nsmall=2)# Using gather, we can make the data more friendlier to work with in a graph
purged_inactive_spike.2 <- purged_inactive_spike.1 %>%
gather(Total, Inactive, Spike, key="Voters", value="Percent") %>%
arrange(RACE)
format(purged_inactive_spike.2, digits=1, nsmall=2)
Figure 7: Bar graph representation of racial breakdown of purged voters between September and November 2017 in the spike, total inactive voters, and total voters.
似乎在这些人当中,白人是最不成比例的。西班牙裔略不成比例。这似乎与大多数调查结果相矛盾,大多数调查结果认为,在佐治亚州的选民清洗中,有色人种比白人比例更高。为什么我在这里找不到这个?我将在我们下一次分析后讨论这一点。
第三组:在 2016 年大选和中投票的人在高峰和 2017 年 11 月之间被移除
为了将所有事情联系起来,我的最后一个问题是,有多少被清除的选民在 2016 年选举日投票?我使用了我之前清除的选民子集,并根据上次投票日期(等于 2016 年 11 月 8 日)对其进行了过滤。
purged_all.voted <- purged.all %>%
filter(DATE_LAST_VOTED == "2016-11-08")purged_inactive.voted <- purged.inactive %>%
filter(DATE_LAST_VOTED == "2016-11-08")purged_spike.voted <- purged.spike %>%
filter(DATE_LAST_VOTED == "2016-11-08")voted_purged_by_spike <- nrow(purged_spike.voted) / nrow(inactive.spike) * 100voted_purged_by_inactive <- nrow(purged_spike.voted) / nrow(inactive.sept17) * 100voted_purged_by_total <- nrow(purged_spike.voted) / nrow(ga_voter.sept17) * 100voted_and_purged <- tibble(Voters = c('Total', 'Inactive', 'In Spike'), Percent = c(voted_purged_by_total, voted_purged_by_inactive, voted_purged_by_spike))
Figure 8: Bar graph of spike voters who were purged and voted on 2016 election day in the spike, total inactive voters, and total voters.
我们已经减少了相当多的数据。如果 295 个选举日投票者是不活跃的(这包括尖峰中的 243 个选举日投票者),那么在选举中投票并被移除的剩余 8134 个投票者是谁?这些选民的身份是活跃的。8134 名选民是搬走了,还是死了,还是两者兼而有之?看看 2016 年 8,134 名被清洗的选举选民将是一件有趣的事情,可以从这个项目中抽出来做进一步的分析。现在,让我们来看看种族分类。
voted_purged_all.race <- purged_all.voted %>%
group_by(RACE) %>%
summarise(Total = n() / nrow(purged_all.voted) * 100) %>%
arrange(desc(Total))
voted_purged_all.racevoted_purged_inactive.race <- purged_inactive.voted %>%
group_by(RACE) %>%
summarise(Inactive = n() / nrow(purged_inactive.voted) * 100) %>%
arrange(desc(Inactive))
voted_purged_inactive.racevoted_purged_spike.race <- purged_spike.voted %>%
group_by(RACE) %>%
summarise(Spike = n() / nrow(purged_spike.voted) * 100) %>%
arrange(desc(Spike))
voted_purged_spike.race# Make a table for all results to compare proportions
voted_purged_inactive <- merge(x = voted_purged_all.race, y = voted_purged_inactive.race, by = "RACE")
voted_purged_inactive.1 <- merge(x = voted_purged_inactive, y = voted_purged_spike.race, by = "RACE") %>%
arrange(desc(Total))
format(voted_purged_inactive.1, digits=2, nsmall=2)# Using gather, we can make the data more friendlier to work with in a graph
voted_purged_inactive.2 <- voted_purged_inactive.1 %>%
gather(Total, Inactive, Spike, key="Voters", value="Percent") %>%
arrange(RACE)
format(voted_purged_inactive.2, digits=2, nsmall=2)
Figure 9: Bar graph representing the racial breakdown of purged 2016 election day voters between September and November 2017 in the spike, total inactive voters, and total voters.
这种比例失调似乎仍然对白人影响最大。它也影响未知种族的人,似乎也轻微影响亚洲人或太平洋岛民。
仍然突出的问题是,为什么白人在清洗和最近投票清洗的选民中所占比例过大,而不是像许多其他分析显示的那样在有色人种中所占比例过大?我的第一个假设是我没有足够的数据。我不应该只看两份不同的选民档案。另一个因素可能是我如何检查数据。我关注的是两个选民文件之间的子集,而不是一整年或几个月的选民文件。也许如果我只看几个月来被清洗的选民和选民档案,我可能会发现比例失调。
总结一下,我决定创建一个水平条形图,显示我们观察到的群体中的所有总人口— (1)选民文件中的每个人,(2)在 2016 年大选中投票的人,(3)在高峰和 2017 年 11 月之间被清洗的人,以及(4)在高峰和 2017 年 11 月之间参加 2016 年大选中投票的人和。
# Compare total populations of each group and overall
total_populations.1 <- tibble(Race = c("WH", "U", "OT", "HP", "BH", "AP", "AI"), Overall = total_inactive_spike.1$Total, Voted = total_inactive_spike.voted.1$Total, Purged = purged_inactive_spike.1$Total, Voted_And_Purged = voted_purged_inactive.1$Total)total_populations.1total_populations.2 <- total_populations.1 %>%
gather(Overall, Voted, Purged, Voted_And_Purged, key = "Total_Type", value = "Percent")
total_populations.2
Figure 10: Horizontal bar graph of total proportions of each subset — Everyone in the voter file, people who voted in on the 2016 General Election Day, people who were purged between September 2017 and November 2017, and people who were purged and voted.
Figure 11: Table representation of horizontal bar graph above.
挑战
在这个项目中,我最大的挑战是不要迷失在数据中。我相信我大大低估了分析数据时“下兔子洞”的容易程度。几乎在每一个转折点,似乎都有一个新的问题要问。在深入研究数据之前,我必须学会如何组织我的想法,并制定一个游戏计划。因此,简而言之,我花了很多时间编写最终没有出现在项目中的东西。
另一个挑战是试图弄清楚我想如何提问。因为我做了很多子集的子集的子集,有时计算我到底想看什么,我需要做什么才能看到它,以及如何可视化它变得令人不知所措。在深入研究数据并开始编码之前,这也是关键的一步。我最终在 Word 上创建了一个详细的大纲,将我的项目分成几个部分,并列出我需要计算的内容。幸运的是,分析是重复的,所以一旦我有了一些初始计算,我只需要正确地创建我的子集并应用关于子集的计算。
结论
在很大程度上,我不认为有什么突出的或致命的发现,但我确实相信这为进一步调查选民清洗以及他们如何在佐治亚州运作奠定了基础。这个项目的下一步将是使用更多的数据,并寻找一年中的整体清除。我相信这样做很可能会导致其他人发现的不相称的结果。我想采取的另一个步骤是对这个项目进行统计分析。我需要做更多的研究,了解需要使用哪些测试,以及如何正确地可视化结果。总而言之,对我个人来说,这是一次很棒的学习经历,学习 R 和它所有的功能非常令人兴奋。我计划继续对这个项目和其他相关项目做进一步的分析,为争取一个公平的民主做出贡献。